diff --git a/distrib/idea/IDEAPlugin-3.6.3.zip b/distrib/idea/IDEAPlugin-3.6.0.zip similarity index 88% rename from distrib/idea/IDEAPlugin-3.6.3.zip rename to distrib/idea/IDEAPlugin-3.6.0.zip index ba009886..b3637805 100644 Binary files a/distrib/idea/IDEAPlugin-3.6.3.zip and b/distrib/idea/IDEAPlugin-3.6.0.zip differ diff --git a/public/metamodel/dochub/root.yaml b/public/metamodel/dochub/root.yaml index 6ca9ef54..016d8766 100644 --- a/public/metamodel/dochub/root.yaml +++ b/public/metamodel/dochub/root.yaml @@ -1,3 +1,14 @@ +$package: + dochub: # Идентификатор пакета + # Краткое название пакета + name: DocHub метамодель + # Поставщик + vendor: R.Piontik + # Описание + description: Облегченная метамодель для описания прикладного слоя + # Версия + version: 3.6.0 + # Базовая метамодель DocHub. Включает в себя сущности # components - Архитектурные компоненты. # aspects - Архитектурные аспекты. Позволяют выделить и отобразить на diff --git a/public/metamodel/docs/welcome.md b/public/metamodel/docs/welcome.md new file mode 100644 index 00000000..13e1621d --- /dev/null +++ b/public/metamodel/docs/welcome.md @@ -0,0 +1,28 @@ +# Добро пожаловать в DocHub! + +Для начала работы вызови меню (слева вверху). + +Подробная документация [https://dochub.info/](https://dochub.info/). + +Сообщество где тебе всегда помогут - [t.me/archascode](t.me/archascode). +Если ты найдешь баги, оставляй информацию о них в репе [https://github.com/RabotaRu/DocHub](https://github.com/RabotaRu/DocHub). +А еще лучше pull request с исправлением ;) + +Чтобы заменить стартовую страницу, создай собственный документ с идентификатором +dochub.welcome. Пример: + +```yaml + ... + docs: + dochub.welcome: + title: Моя стартовая страница + type: markdown # Тип документа + source: welcome.md # Файл с документом + ... +``` + +Подробнее о создании документов и их видах тут [https://dochub.info/entities/docs/blank?dh-doc-id=dochub.docs](https://dochub.info/entities/docs/blank?dh-doc-id=dochub.docs). + +Успехов! + +Команда DocHub \ No newline at end of file diff --git a/public/metamodel/root.yaml b/public/metamodel/root.yaml index 5838b7db..1fe59ed9 100644 --- a/public/metamodel/root.yaml +++ b/public/metamodel/root.yaml @@ -1,13 +1,8 @@ -$package: - dochub: # Идентификатор пакета - # Краткое название пакета - name: DocHub метамодель - # Поставщик - vendor: R.Piontik - # Описание - description: Облегченная метамодель для описания прикладного слоя - # Версия - version: 3.6.0 +# Стартовая страница для плагина +docs: + dochub.welcome: + type: markdown + source: docs/welcome.md imports: - dochub/root.yaml diff --git a/src/frontend/components/Docs/DocTable.vue b/src/frontend/components/Docs/DocTable.vue index 5312cfb8..e08dc00b 100644 --- a/src/frontend/components/Docs/DocTable.vue +++ b/src/frontend/components/Docs/DocTable.vue @@ -13,7 +13,7 @@ v-bind:headers="headers" v-bind:items="source.dataset || []" v-bind:search="search" - v-bind:items-per-page="15" + v-bind:items-per-page="itemsPerPage" v-bind:multi-sort="true" v-bind:hide-default-footer="isPrintVersion" v-bind:disable-pagination="isPrintVersion" @@ -82,6 +82,12 @@ }, isTemplate() { return true; + }, + itemsPerPage() { + return Math.max( + Math.round( + (Math.max(window.document.body?.scrollHeight || 0, window.document.body?.offsetHeight || 0) - 240) + / 48), 5); } }, methods: { diff --git a/src/frontend/components/Schema/DHSchema/DHSchemaTrack.vue b/src/frontend/components/Schema/DHSchema/DHSchemaTrack.vue index 07063795..8527f9b7 100644 --- a/src/frontend/components/Schema/DHSchema/DHSchemaTrack.vue +++ b/src/frontend/components/Schema/DHSchema/DHSchemaTrack.vue @@ -142,6 +142,7 @@ arrows() { let result = ''; const track = this.track.path; + if (track.length < 2) return ''; // Расставляем стрелки завершения пути [ { curr : track[track.length - 1], prev: track[track.length - 2], after: true, arrow: this.endArrow}, diff --git a/src/frontend/helpers/uri.js b/src/frontend/helpers/uri.js index 95a8973a..bc94c8eb 100644 --- a/src/frontend/helpers/uri.js +++ b/src/frontend/helpers/uri.js @@ -13,7 +13,7 @@ export default Object.assign({ // eslint-disable-next-line no-console console.warn(`Не найден путь к свойству [${path}]`); } - return (paths || [])[0]; + return (paths || []).slice(-1)[0]; } } }, new uriTool(config)); diff --git a/src/frontend/storage/gitlab.js b/src/frontend/storage/gitlab.js index acad38fe..fe7d738a 100644 --- a/src/frontend/storage/gitlab.js +++ b/src/frontend/storage/gitlab.js @@ -42,7 +42,7 @@ export default { // Выявленные Проблемы problems: [], // Источники данных манифеста - sources: [], + sources: {}, // Доступные проекты GitLab available_projects: {}, // Проекты diff --git a/src/global/manifest/parser.mjs b/src/global/manifest/parser.mjs index e7b7a8bb..055dd85d 100644 --- a/src/global/manifest/parser.mjs +++ b/src/global/manifest/parser.mjs @@ -3,11 +3,11 @@ import cache from './services/cache.mjs'; import prototype from './prototype.mjs'; class PackageError extends Error { - constructor(uri, message) { - super(message); - this.name = 'Package'; - this.uri = uri; - } + constructor(uri, message) { + super(message); + this.name = 'Package'; + this.uri = uri; + } } // Определяет глубину лога источника для секции @@ -32,8 +32,8 @@ const parser = { onStartReload: null, // События по ошибкам (ошибки запросов) onError: null, - // Сервис управления кэшем - cache, + // Сервис управления кэшем + cache, // Очистка clean() { this.loaded = {}; @@ -55,10 +55,10 @@ const parser = { manifest: {}, // Лог загруженных файлов loaded: {}, - // Загруженные пакеты - packages: {}, - // Ожидающие пакеты - awaitedPackages: {}, + // Загруженные пакеты + packages: {}, + // Ожидающие пакеты + awaitedPackages: {}, // Возвращает тип значения fieldValueType(value) { const type = typeof value; @@ -106,16 +106,16 @@ const parser = { // eslint-disable-next-line no-console console.error(e, `Ошибка запроса [${errorPath}:${uri}]`, e); this.pushToMergeMap(errorPath, null, uri); - if(typeof e === 'string') e = JSON.parse(e); + if (typeof e === 'string') e = JSON.parse(e); let errorType = (() => { switch (e.name) { case 'YAMLSyntaxError': case 'YAMLSemanticError': return 'syntax'; - case 'EntryIsADirectory (FileSystemError)': - return 'file-system'; - case 'Package': - return 'package'; + case 'EntryIsADirectory (FileSystemError)': + return 'file-system'; + case 'Package': + return 'package'; default: return 'net'; } @@ -128,12 +128,13 @@ const parser = { }, // Сохраняет в карте склеивания данные pushToMergeMap(path, source, location) { - const structPath = (path || '').split('/'); - if (structPath.length - 1 > sectionDeepLog[structPath[1] || '$default$']) return; - const storePath = path || '/'; + const structPath = (path || '/').split('/'); + const storePath = structPath.slice(0, sectionDeepLog[structPath[1] || '$default$'] + 1).join('/'); + + let locations = this.mergeMap[storePath]; - !this.mergeMap[storePath] && (this.mergeMap[storePath] = []); - this.mergeMap[storePath].push(location); + !locations && (this.mergeMap[storePath] = locations = []); + locations.indexOf(location) < 0 && locations.push(location); if (typeof source === 'object') { for (const key in source) { @@ -161,7 +162,7 @@ const parser = { !temp[index] && (temp[index] = JSON.stringify(srcItem)); return distContent === temp[index]; })) { - result.push(distItem); + result.push(distItem); } }); result = source.concat(result); @@ -237,129 +238,129 @@ const parser = { } }, - async parseManifest(manifest, uri) { - this.manifest = this.merge(this.manifest, manifest, uri); + async parseManifest(manifest, uri) { + this.manifest = this.merge(this.manifest, manifest, uri); - for (const section in manifest) { - const node = manifest[section]; - switch (section) { - case 'forms': - case 'namespaces': - case 'aspects': - case 'docs': - case 'contexts': - case 'components': - case 'rules': - case 'datasets': - case '$package': - await this.parseEntity(node, `/${section}`, uri); - break; - case 'imports': - for (const key in node) { - const url = parser.cache.makeURIByBaseURI(node[key], uri); - if (this.loaded[url]) { - // eslint-disable-next-line no-console - console.warn(`Manifest [${url}] already loaded.`); - } else { - this.loaded[url] = true; - await this.import(url, true); - } - } - break; - } - } - }, + for (const section in manifest) { + const node = manifest[section]; + switch (section) { + case 'forms': + case 'namespaces': + case 'aspects': + case 'docs': + case 'contexts': + case 'components': + case 'rules': + case 'datasets': + case '$package': + await this.parseEntity(node, `/${section}`, uri); + break; + case 'imports': + for (const key in node) { + const url = parser.cache.makeURIByBaseURI(node[key], uri); + if (this.loaded[url]) { + // eslint-disable-next-line no-console + console.warn(`Manifest [${url}] already loaded.`); + } else { + this.loaded[url] = true; + await this.import(url, true); + } + } + break; + } + } + }, - async checkAwaitedPackages() { - // пройтись по ожидающим пакетам и проверить зарезолвелнены ли их зависимости. - // Если да - то распарсить их и убрать из ждунов - const resolved = {}; - const awaitedTuples = Object.entries(this.awaitedPackages); - if(!awaitedTuples?.length) return; + async checkAwaitedPackages() { + // пройтись по ожидающим пакетам и проверить зарезолвелнены ли их зависимости. + // Если да - то распарсить их и убрать из ждунов + const resolved = {}; + const awaitedTuples = Object.entries(this.awaitedPackages); + if (!awaitedTuples?.length) return; - this.awaitedPackages = Object.fromEntries( - awaitedTuples.filter(([url, pkg]) => { - if(this.isDepsResolved(url, pkg.$package)) { - resolved[url] = pkg; - return false; - } else return true; - }) - ); - - const parsingPackages = Object.entries(resolved) - .map(([uri, pkg]) => - this.parseManifest(pkg, uri) - ) - - await Promise.all(parsingPackages); + this.awaitedPackages = Object.fromEntries( + awaitedTuples.filter(([url, pkg]) => { + if (this.isDepsResolved(url, pkg.$package)) { + resolved[url] = pkg; + return false; + } else return true; + }) + ); - Object.values(resolved).forEach(({$package}) => { - const [id, pkg] = Object.entries($package)[0]; - this.packages[id] = pkg; - }); + const parsingPackages = Object.entries(resolved) + .map(([uri, pkg]) => + this.parseManifest(pkg, uri) + ); + + await Promise.all(parsingPackages); - return resolved; - }, + Object.values(resolved).forEach(({ $package }) => { + const [id, pkg] = Object.entries($package)[0]; + this.packages[id] = pkg; + }); + + return resolved; + }, - isDepsResolved(uri, $pkg) { - const [_, pkg] = Object.entries($pkg)[0]; + isDepsResolved(uri, $pkg) { + const [_, pkg] = Object.entries($pkg)[0]; - // если у пакета нет зависимостей то и нечего решать - if(!pkg?.dependencies) return true; + // если у пакета нет зависимостей то и нечего решать + if (!pkg?.dependencies) return true; - // если нет установленых пакетов то зависимости не решены - const packageTuples = Object.entries(this.packages); - if(!packageTuples?.length) return false; + // если нет установленых пакетов то зависимости не решены + const packageTuples = Object.entries(this.packages); + if (!packageTuples?.length) return false; - // проверяем все ли зависимости установлены - return Object.entries(pkg.dependencies).every(([id, version]) => { - // Зависимость установлена (есть в packages)? - return packageTuples.find(( [i, v] ) => { - if(id !== i) return false; + // проверяем все ли зависимости установлены + return Object.entries(pkg.dependencies).every(([id, version]) => { + // Зависимость установлена (есть в packages)? + return packageTuples.find(([i, v]) => { + if (id !== i) return false; - if(!semver.satisfies(v.version, version)) { - throw new PackageError( - uri, - `Не подходящая версия пакета "${id}". Требуется "${version}" но найдена "${v.version}"` - ); - } - return (id === i && semver.satisfies(v.version, version)); - }); + if (!semver.satisfies(v.version, version)) { + throw new PackageError( + uri, + `Не подходящая версия пакета "${id}". Требуется "${version}" но найдена "${v.version}"` + ); + } + return (id === i && semver.satisfies(v.version, version)); + }); - }); - }, + }); + }, - checkCycleDeps($package) { - Object.entries(this.awaitedPackages).forEach(([uri, pkg]) => { - const [aID, aPkg] = Object.entries(pkg.$package)[0]; - const [bID, bPkg] = Object.entries($package)[0]; - const aDeps = Object.keys(aPkg.dependencies); - const bDeps = Object.keys(bPkg.dependencies); - const aDepID = aDeps.find(id => bID === id); - const bDepID = bDeps.find(id => aID === id); - if(aDepID && bDepID) { - throw new PackageError( - uri, - `Циклическая зависимость между пакетами ${aDepID} и ${bDepID}` - ); - } - }); - }, + checkCycleDeps($package) { + Object.entries(this.awaitedPackages).forEach(([uri, pkg]) => { + const [aID, aPkg] = Object.entries(pkg.$package)[0]; + const [bID, bPkg] = Object.entries($package)[0]; + const aDeps = Object.keys(aPkg.dependencies); + const bDeps = Object.keys(bPkg.dependencies); + const aDepID = aDeps.find(id => bID === id); + const bDepID = bDeps.find(id => aID === id); + if (aDepID && bDepID) { + throw new PackageError( + uri, + `Циклическая зависимость между пакетами ${aDepID} и ${bDepID}` + ); + } + }); + }, - checkLoaded() { - const awaited = Object.entries(this?.awaitedPackages); - if(awaited.length) { - awaited.forEach(([uri, pkg]) => { - const [id, $pkg] = Object.entries(pkg.$package)[0]; - const unresolved = Object.entries($pkg.dependencies).filter(([id, _]) => - !this.packages[id] - ) - .map(([id, ver]) => `${id} (${ver})`) - .join(', '); - this.registerError(new PackageError(uri, `У пакета ${id} не разрешены зависимости ${unresolved}`), uri) - }) - } - }, + checkLoaded() { + const awaited = Object.entries(this?.awaitedPackages); + if (awaited.length) { + awaited.forEach(([uri, pkg]) => { + const [id, $pkg] = Object.entries(pkg.$package)[0]; + const unresolved = Object.entries($pkg.dependencies).filter(([id, _]) => + !this.packages[id] + ) + .map(([id, ver]) => `${id} (${ver})`) + .join(', '); + this.registerError(new PackageError(uri, `У пакета ${id} не разрешены зависимости ${unresolved}`), uri); + }); + } + }, // Подключение манифеста async import(uri) { @@ -369,26 +370,26 @@ const parser = { ? response.data : JSON.parse(response.data)); - // если манифест - пакет - if(manifest?.$package) { - const $package = manifest.$package; - - // если у пакета решены его зависимости - // - парсим и складываем версию в установленные пакеты - if(parser.isDepsResolved(uri, $package)) { - await this.parseManifest(manifest, uri); - // TODO если пакет уже установлен с другой версией то что? - const [id, pkg] = Object.entries($package)[0]; - this.packages[id] = pkg; - await this.checkAwaitedPackages(); - } - // иначе складываем пакет в ждуны - else { - this.checkCycleDeps(manifest.$package); - this.awaitedPackages[uri] = manifest; - return; - } - } else await this.parseManifest(manifest, uri); + // если манифест - пакет + if (manifest?.$package) { + const $package = manifest.$package; + + // если у пакета решены его зависимости + // - парсим и складываем версию в установленные пакеты + if (parser.isDepsResolved(uri, $package)) { + await this.parseManifest(manifest, uri); + // TODO если пакет уже установлен с другой версией то что? + const [id, pkg] = Object.entries($package)[0]; + this.packages[id] = pkg; + await this.checkAwaitedPackages(); + } + // иначе складываем пакет в ждуны + else { + this.checkCycleDeps(manifest.$package); + this.awaitedPackages[uri] = manifest; + return; + } + } else await this.parseManifest(manifest, uri); } catch (e) { this.registerError(e, e.uri || uri);