@likec4/language-server 0.40.0 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/contrib/likec4.monarch.ts +48 -0
  2. package/contrib/likec4.tmLanguage.json +73 -0
  3. package/dist/Rpc.d.ts +1 -0
  4. package/dist/ast.d.ts +117 -0
  5. package/dist/ast.js +46 -2
  6. package/dist/elementRef.d.ts +2 -1
  7. package/dist/generated/ast.d.ts +45 -3
  8. package/dist/generated/ast.js +36 -2
  9. package/dist/generated/grammar.d.ts +2 -0
  10. package/dist/generated/grammar.js +1 -1
  11. package/dist/generated/module.d.ts +6 -1
  12. package/dist/index.d.ts +1 -0
  13. package/dist/logger.d.ts +1 -0
  14. package/dist/lsp/CodeLensProvider.d.ts +1 -0
  15. package/dist/lsp/DocumentLinkProvider.d.ts +1 -0
  16. package/dist/lsp/DocumentSymbolProvider.d.ts +1 -0
  17. package/dist/lsp/HoverProvider.d.ts +1 -0
  18. package/dist/lsp/SemanticTokenProvider.d.ts +1 -0
  19. package/dist/lsp/SemanticTokenProvider.js +21 -14
  20. package/dist/lsp/index.d.ts +1 -0
  21. package/dist/model/fqn-computation.d.ts +1 -0
  22. package/dist/model/fqn-index.d.ts +1 -0
  23. package/dist/model/fqn-index.js +25 -19
  24. package/dist/model/index.d.ts +1 -0
  25. package/dist/model/model-builder.d.ts +2 -1
  26. package/dist/model/model-builder.js +21 -15
  27. package/dist/model/model-locator.d.ts +1 -0
  28. package/dist/model/model-parser.d.ts +1 -0
  29. package/dist/model/model-parser.js +21 -32
  30. package/dist/module.d.ts +1 -0
  31. package/dist/protocol.d.ts +1 -0
  32. package/dist/references/index.d.ts +1 -0
  33. package/dist/references/scope-computation.d.ts +3 -2
  34. package/dist/references/scope-computation.js +5 -0
  35. package/dist/references/scope-provider.d.ts +2 -1
  36. package/dist/shared/WorkspaceManager.d.ts +2 -1
  37. package/dist/shared/index.d.ts +1 -0
  38. package/dist/test/index.d.ts +1 -0
  39. package/dist/test/testServices.d.ts +7 -6
  40. package/dist/test/testServices.js +12 -3
  41. package/dist/utils.d.ts +1 -0
  42. package/dist/validation/element.d.ts +3 -0
  43. package/dist/validation/element.js +17 -13
  44. package/dist/validation/index.d.ts +1 -0
  45. package/dist/validation/index.js +3 -2
  46. package/dist/validation/relation.d.ts +2 -0
  47. package/dist/validation/relation.js +8 -4
  48. package/dist/validation/specification.d.ts +3 -0
  49. package/dist/validation/specification.js +12 -0
  50. package/dist/validation/view.d.ts +2 -0
  51. package/dist/view-utils/assignNavigateTo.d.ts +3 -0
  52. package/dist/view-utils/assignNavigateTo.js +20 -0
  53. package/dist/view-utils/index.d.ts +4 -0
  54. package/dist/view-utils/index.js +3 -0
  55. package/dist/view-utils/resolve-extended-views.d.ts +7 -0
  56. package/dist/view-utils/resolve-extended-views.js +41 -0
  57. package/dist/view-utils/resolve-relative-paths.d.ts +3 -0
  58. package/dist/view-utils/resolve-relative-paths.js +76 -0
  59. package/package.json +22 -14
@@ -1,13 +1,8 @@
1
1
  import {
2
- ModelIndex,
3
- assignNavigateTo,
4
2
  compareByFqnHierarchically,
5
- computeView,
6
3
  invariant,
7
4
  isStrictElementView,
8
- parentFqn,
9
- resolveRulesExtendedViews,
10
- resolveRelativePaths
5
+ parentFqn
11
6
  } from "@likec4/core";
12
7
  import {
13
8
  DocumentState,
@@ -19,17 +14,19 @@ import { isValidLikeC4LangiumDocument } from "../ast.js";
19
14
  import { logError, logWarnError, logger } from "../logger.js";
20
15
  import { LikeC4WorkspaceManager } from "../shared/index.js";
21
16
  import { printDocs, queueMicrotask } from "../utils.js";
17
+ import { assignNavigateTo, resolveRelativePaths, resolveRulesExtendedViews } from "../view-utils/index.js";
18
+ import { LikeC4ModelGraph, computeView } from "@likec4/graph";
22
19
  function isRelativeLink(link) {
23
20
  return link.startsWith(".") || link.startsWith("/");
24
21
  }
25
22
  function buildModel(docs) {
26
23
  const c4Specification = {
27
- kinds: {}
24
+ kinds: {},
25
+ relationships: {}
28
26
  };
29
- R.forEach(
30
- R.map(docs, R.prop("c4Specification")),
31
- (spec) => Object.assign(c4Specification.kinds, spec.kinds)
32
- );
27
+ R.forEach(R.map(docs, R.prop("c4Specification")), (spec) => {
28
+ Object.assign(c4Specification.kinds, spec.kinds), Object.assign(c4Specification.relationships, spec.relationships);
29
+ });
33
30
  const resolveLinks = (doc, links) => {
34
31
  const base = new URL(doc.uri.toString());
35
32
  return links.map(
@@ -84,6 +81,15 @@ function buildModel(docs) {
84
81
  ...model
85
82
  }) => {
86
83
  if (source in elements && target in elements) {
84
+ if (model.kind) {
85
+ const kind = c4Specification.relationships[model.kind];
86
+ return {
87
+ source,
88
+ target,
89
+ ...kind,
90
+ ...model
91
+ };
92
+ }
87
93
  return {
88
94
  source,
89
95
  target,
@@ -134,8 +140,7 @@ function buildModel(docs) {
134
140
  links: null,
135
141
  rules: [
136
142
  {
137
- isInclude: true,
138
- exprs: [
143
+ include: [
139
144
  {
140
145
  wildcard: true
141
146
  }
@@ -199,7 +204,7 @@ ${printDocs(docs)}`);
199
204
  if (!model) {
200
205
  return null;
201
206
  }
202
- const index = ModelIndex.from(model);
207
+ const index = new LikeC4ModelGraph(model);
203
208
  const views = R.pipe(
204
209
  R.values(model.views),
205
210
  R.map((view) => computeView(view, index).view),
@@ -220,7 +225,8 @@ ${printDocs(docs)}`);
220
225
  logger.warn(`[ModelBuilder] Cannot find view ${viewId}`);
221
226
  return null;
222
227
  }
223
- const result = computeView(view, ModelIndex.from(model));
228
+ const index = new LikeC4ModelGraph(model);
229
+ const result = computeView(view, index);
224
230
  if (!result.isSuccess) {
225
231
  logError(result.error);
226
232
  return null;
@@ -14,3 +14,4 @@ export declare class LikeC4ModelLocator {
14
14
  locateRelation(relationId: c4.RelationID): Location | null;
15
15
  locateView(viewId: c4.ViewID): Location | null;
16
16
  }
17
+ //# sourceMappingURL=model-locator.d.ts.map
@@ -20,3 +20,4 @@ export declare class LikeC4ModelParser {
20
20
  private getAstNodePath;
21
21
  private convertTags;
22
22
  }
23
+ //# sourceMappingURL=model-parser.d.ts.map
@@ -11,7 +11,8 @@ import {
11
11
  streamModel,
12
12
  toAutoLayout,
13
13
  toElementStyle,
14
- toElementStyleExcludeDefaults
14
+ toElementStyleExcludeDefaults,
15
+ toRelationshipStyleExcludeDefaults
15
16
  } from "../ast.js";
16
17
  import { elementRef, fqnElementRef } from "../elementRef.js";
17
18
  import { logError, logWarnError, logger } from "../logger.js";
@@ -22,33 +23,6 @@ export class LikeC4ModelParser {
22
23
  this.fqnIndex = services.likec4.FqnIndex;
23
24
  }
24
25
  fqnIndex;
25
- // public onParsed(callback: ModelParsedListener): Disposable {
26
- // this.listeners.push(callback)
27
- // return Disposable.create(() => {
28
- // const index = this.listeners.indexOf(callback)
29
- // if (index >= 0) {
30
- // this.listeners.splice(index, 1)
31
- // }
32
- // })
33
- // }
34
- // protected async onValidated(docs: LangiumDocument[], cancelToken: CancellationToken): Promise<void> {
35
- // let countOfChangedDocs = 0
36
- // logger.debug(`[ModelParser] onValidated (${docs.length} docs)\n${printDocs(docs)}`)
37
- // for (const doc of docs) {
38
- // if (!isLikeC4LangiumDocument(doc)) {
39
- // continue
40
- // }
41
- // countOfChangedDocs++
42
- // try {
43
- // await this.parseDocument(doc, cancelToken)
44
- // } catch (cause) {
45
- // logError(new InvalidModelError(`Error parsing document ${doc.uri.toString()}`, { cause }))
46
- // }
47
- // }
48
- // if (countOfChangedDocs > 0) {
49
- // this.notifyListeners()
50
- // }
51
- // }
52
26
  parse(doc) {
53
27
  const docs = Array.isArray(doc) ? doc : [doc];
54
28
  logger.debug(`[ModelParser] onValidated (${docs.length} docs)
@@ -82,6 +56,22 @@ ${printDocs(docs)}`);
82
56
  }
83
57
  }
84
58
  }
59
+ const relations_specs = doc.parseResult.value.specification?.relationships;
60
+ if (relations_specs) {
61
+ for (const { kind, props } of relations_specs) {
62
+ if (kind.name in specification.relationships) {
63
+ logger.warn(`Duplicate specification for kind ${kind.name}`);
64
+ continue;
65
+ }
66
+ try {
67
+ specification.relationships[kind.name] = toRelationshipStyleExcludeDefaults(
68
+ props
69
+ );
70
+ } catch (e) {
71
+ logWarnError(e);
72
+ }
73
+ }
74
+ }
85
75
  for (const el of streamModel(doc)) {
86
76
  if (ast.isElement(el)) {
87
77
  try {
@@ -146,6 +136,7 @@ ${printDocs(docs)}`);
146
136
  const coupling = resolveRelationPoints(astNode);
147
137
  const target = this.resolveFqn(coupling.target);
148
138
  const source = this.resolveFqn(coupling.source);
139
+ const kind = astNode.kind?.ref?.name;
149
140
  const hashdata = {
150
141
  astPath: this.getAstNodePath(astNode),
151
142
  source,
@@ -156,6 +147,7 @@ ${printDocs(docs)}`);
156
147
  return {
157
148
  id,
158
149
  ...hashdata,
150
+ ...kind && { kind },
159
151
  title
160
152
  };
161
153
  }
@@ -225,10 +217,7 @@ ${printDocs(docs)}`);
225
217
  parseViewRule(astRule) {
226
218
  if (ast.isViewRuleExpression(astRule)) {
227
219
  const exprs = astRule.expressions.map((n) => this.parseExpression(n));
228
- return {
229
- isInclude: astRule.isInclude,
230
- exprs
231
- };
220
+ return astRule.isInclude ? { include: exprs } : { exclude: exprs };
232
221
  }
233
222
  if (ast.isViewRuleStyle(astRule)) {
234
223
  const styleProps = toElementStyle(astRule.props);
package/dist/module.d.ts CHANGED
@@ -26,3 +26,4 @@ export declare function createLanguageServices(context?: LanguageServicesContext
26
26
  likec4: LikeC4Services;
27
27
  };
28
28
  export {};
29
+ //# sourceMappingURL=module.d.ts.map
@@ -30,3 +30,4 @@ export type LocateParams = {
30
30
  };
31
31
  export declare const locate: RequestType<LocateParams, Location | null, void>;
32
32
  export {};
33
+ //# sourceMappingURL=protocol.d.ts.map
@@ -1,2 +1,3 @@
1
1
  export * from './scope-computation';
2
2
  export * from './scope-provider';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -1,10 +1,11 @@
1
- import { DefaultScopeComputation, type AstNodeDescription, type PrecomputedScopes } from 'langium';
1
+ import { DefaultScopeComputation, MultiMap, type AstNodeDescription, type PrecomputedScopes } from 'langium';
2
2
  import type { CancellationToken } from 'vscode-languageserver';
3
3
  import { ast, type LikeC4LangiumDocument } from '../ast';
4
4
  type ElementsContainer = ast.Model | ast.ElementBody | ast.ExtendElementBody;
5
5
  export declare class LikeC4ScopeComputation extends DefaultScopeComputation {
6
6
  computeExports(document: LikeC4LangiumDocument, _cancelToken: CancellationToken): Promise<AstNodeDescription[]>;
7
7
  computeLocalScopes(document: LikeC4LangiumDocument, _cancelToken: CancellationToken): Promise<PrecomputedScopes>;
8
- protected processContainer(container: ElementsContainer, scopes: PrecomputedScopes, document: LikeC4LangiumDocument): any;
8
+ protected processContainer(container: ElementsContainer, scopes: PrecomputedScopes, document: LikeC4LangiumDocument): MultiMap<string, AstNodeDescription>;
9
9
  }
10
10
  export {};
11
+ //# sourceMappingURL=scope-computation.d.ts.map
@@ -20,6 +20,11 @@ export class LikeC4ScopeComputation extends DefaultScopeComputation {
20
20
  docExports.push(this.descriptions.createDescription(spec.tag, "#" + spec.tag.name, document));
21
21
  }
22
22
  }
23
+ for (const spec of specification.relationships) {
24
+ if (spec.kind && !isEmpty(spec.kind.name)) {
25
+ docExports.push(this.descriptions.createDescription(spec.kind, spec.kind.name, document));
26
+ }
27
+ }
23
28
  }
24
29
  if (model && model.elements.length > 0) {
25
30
  for (const elAst of model.elements) {
@@ -10,9 +10,10 @@ export declare class LikeC4ScopeProvider extends DefaultScopeProvider {
10
10
  private scopeExtendElement;
11
11
  private scopeElementView;
12
12
  getScope(context: ReferenceInfo): Scope;
13
- protected computeScope(node: AstNode, referenceType: string): Stream<AstNodeDescription>;
13
+ protected computeScope(node: AstNode, referenceType: string): Scope;
14
14
  /**
15
15
  * Create a global scope filtered for the given reference type.
16
16
  */
17
17
  protected getGlobalScope(referenceType: string): Scope;
18
18
  }
19
+ //# sourceMappingURL=scope-provider.d.ts.map
@@ -8,5 +8,6 @@ export declare class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
8
8
  * your language, which can be either loaded from provided files or constructed in memory.
9
9
  */
10
10
  protected loadAdditionalDocuments(_folders: WorkspaceFolder[], _collector: (document: LangiumDocument) => void): Promise<void>;
11
- workspace(): any;
11
+ workspace(): WorkspaceFolder | null;
12
12
  }
13
+ //# sourceMappingURL=WorkspaceManager.d.ts.map
@@ -1 +1,2 @@
1
1
  export * from './WorkspaceManager';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -1 +1,2 @@
1
1
  export * from './testServices';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -1,15 +1,16 @@
1
1
  import type { LikeC4LangiumDocument } from '../ast';
2
2
  export declare function createTestServices(workspace?: string): {
3
- services: any;
3
+ services: import("../module").LikeC4Services;
4
4
  parse: (input: string, uri?: string) => Promise<LikeC4LangiumDocument>;
5
5
  validate: (input: string | LikeC4LangiumDocument, uri?: string) => Promise<{
6
6
  document: LikeC4LangiumDocument;
7
- diagnostics: any;
8
- errors: any;
7
+ diagnostics: import("vscode-languageserver-types").Diagnostic[];
8
+ errors: string[];
9
9
  }>;
10
10
  validateAll: () => Promise<{
11
- diagnostics: any;
12
- errors: any;
11
+ diagnostics: import("vscode-languageserver-types").Diagnostic[];
12
+ errors: string[];
13
13
  }>;
14
- buildModel: () => Promise<any>;
14
+ buildModel: () => Promise<import("@likec4/core").LikeC4Model>;
15
15
  };
16
+ //# sourceMappingURL=testServices.d.ts.map
@@ -13,7 +13,9 @@ export function createTestServices(workspace = "file:///test/workspace") {
13
13
  name: "test",
14
14
  uri: workspaceUri.toString()
15
15
  };
16
- const initPromise = services.shared.workspace.WorkspaceManager.initializeWorkspace([workspaceFolder]);
16
+ const initPromise = services.shared.workspace.WorkspaceManager.initializeWorkspace([
17
+ workspaceFolder
18
+ ]);
17
19
  void initPromise.finally(() => {
18
20
  Object.assign(services.shared.workspace.WorkspaceManager, {
19
21
  folders: [workspaceFolder]
@@ -22,8 +24,15 @@ export function createTestServices(workspace = "file:///test/workspace") {
22
24
  let documentIndex = 1;
23
25
  const parse = async (input, uri) => {
24
26
  await initPromise;
25
- const docUri = Utils.resolvePath(workspaceUri, "./src/", uri ?? `${documentIndex++}${metaData.fileExtensions[0]}`);
26
- const document = services.shared.workspace.LangiumDocumentFactory.fromString(stripIndent(input), docUri);
27
+ const docUri = Utils.resolvePath(
28
+ workspaceUri,
29
+ "./src/",
30
+ uri ?? `${documentIndex++}${metaData.fileExtensions[0]}`
31
+ );
32
+ const document = services.shared.workspace.LangiumDocumentFactory.fromString(
33
+ stripIndent(input),
34
+ docUri
35
+ );
27
36
  langiumDocuments.addDocument(document);
28
37
  await documentBuilder.build([document], { validation: false });
29
38
  return document;
package/dist/utils.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import type { LangiumDocument } from 'langium';
2
2
  export declare const printDocs: (docs: LangiumDocument[]) => string;
3
3
  export declare function queueMicrotask(cb: () => void): Promise<void>;
4
+ //# sourceMappingURL=utils.d.ts.map
@@ -1,3 +1,6 @@
1
+ /// <reference types="react" />
2
+ import { type ValidationCheck } from 'langium';
1
3
  import type { ast } from '../ast';
2
4
  import type { LikeC4Services } from '../module';
3
5
  export declare const elementChecks: (services: LikeC4Services) => ValidationCheck<ast.Element>;
6
+ //# sourceMappingURL=element.d.ts.map
@@ -12,19 +12,23 @@ export const elementChecks = (services) => {
12
12
  }
13
13
  const withSameFqn = fqnIndex.byFqn(fqn).filter((v) => v.el !== el).head();
14
14
  if (withSameFqn) {
15
- accept("error", `Duplicate element name ${el.name !== fqn ? el.name + " (" + fqn + ")" : el.name}`, {
16
- node: el,
17
- property: "name",
18
- relatedInformation: [
19
- {
20
- location: {
21
- range: withSameFqn.el.$cstNode.range,
22
- uri: getDocument(withSameFqn.el).uri.toString()
23
- },
24
- message: `Already defined here`
25
- }
26
- ]
27
- });
15
+ accept(
16
+ "error",
17
+ `Duplicate element name ${el.name !== fqn ? el.name + " (" + fqn + ")" : el.name}`,
18
+ {
19
+ node: el,
20
+ property: "name",
21
+ relatedInformation: [
22
+ {
23
+ location: {
24
+ range: withSameFqn.el.$cstNode.range,
25
+ uri: getDocument(withSameFqn.el).uri.toString()
26
+ },
27
+ message: `Already defined here`
28
+ }
29
+ ]
30
+ }
31
+ );
28
32
  }
29
33
  };
30
34
  };
@@ -1,2 +1,3 @@
1
1
  import type { LikeC4Services } from '../module';
2
2
  export declare function registerValidationChecks(services: LikeC4Services): void;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -2,7 +2,7 @@ import * as vscodeUri from "vscode-uri";
2
2
  import { logger } from "../logger.js";
3
3
  import { elementChecks } from "./element.js";
4
4
  import { relationChecks } from "./relation.js";
5
- import { elementKindChecks, tagChecks } from "./specification.js";
5
+ import { elementKindChecks, tagChecks, relationshipChecks } from "./specification.js";
6
6
  import { viewChecks } from "./view.js";
7
7
  export function registerValidationChecks(services) {
8
8
  logger.info("registerValidationChecks");
@@ -12,7 +12,8 @@ export function registerValidationChecks(services) {
12
12
  Element: elementChecks(services),
13
13
  ElementKind: elementKindChecks(services),
14
14
  Relation: relationChecks(services),
15
- Tag: tagChecks(services)
15
+ Tag: tagChecks(services),
16
+ RelationshipKind: relationshipChecks(services)
16
17
  });
17
18
  const connection = services.shared.lsp.Connection;
18
19
  if (connection) {
@@ -1,3 +1,5 @@
1
+ import type { ValidationCheck } from 'langium';
1
2
  import { ast } from '../ast';
2
3
  import type { LikeC4Services } from '../module';
3
4
  export declare const relationChecks: (services: LikeC4Services) => ValidationCheck<ast.Relation>;
5
+ //# sourceMappingURL=relation.d.ts.map
@@ -19,10 +19,14 @@ export const relationChecks = (services) => {
19
19
  sourceEl = elementRef(el.source);
20
20
  } else {
21
21
  if (!ast.isElementBody(el.$container)) {
22
- accept("error", "Invalid relation, expected to have source defined or be inside the element", {
23
- node: el,
24
- keyword: "->"
25
- });
22
+ accept(
23
+ "error",
24
+ "Invalid relation, expected to have source defined or be inside the element",
25
+ {
26
+ node: el,
27
+ keyword: "->"
28
+ }
29
+ );
26
30
  } else {
27
31
  sourceEl = el.$container.$container;
28
32
  }
@@ -1,4 +1,7 @@
1
+ import type { ValidationCheck } from 'langium';
1
2
  import { ast } from '../ast';
2
3
  import type { LikeC4Services } from '../module';
3
4
  export declare const elementKindChecks: (services: LikeC4Services) => ValidationCheck<ast.ElementKind>;
4
5
  export declare const tagChecks: (services: LikeC4Services) => ValidationCheck<ast.Tag>;
6
+ export declare const relationshipChecks: (services: LikeC4Services) => ValidationCheck<ast.RelationshipKind>;
7
+ //# sourceMappingURL=specification.d.ts.map
@@ -23,3 +23,15 @@ export const tagChecks = (services) => {
23
23
  }
24
24
  };
25
25
  };
26
+ export const relationshipChecks = (services) => {
27
+ const index = services.shared.workspace.IndexManager;
28
+ return (node, accept) => {
29
+ const sameKinds = index.allElements(ast.RelationshipKind).filter((n) => n.name === node.name).limit(2).count();
30
+ if (sameKinds > 1) {
31
+ accept("error", `Duplicate RelationshipKind '${node.name}'`, {
32
+ node,
33
+ property: "name"
34
+ });
35
+ }
36
+ };
37
+ };
@@ -1,3 +1,5 @@
1
+ import type { ValidationCheck } from 'langium';
1
2
  import { ast } from '../ast';
2
3
  import type { LikeC4Services } from '../module';
3
4
  export declare const viewChecks: (services: LikeC4Services) => ValidationCheck<ast.ElementView>;
5
+ //# sourceMappingURL=view.d.ts.map
@@ -0,0 +1,3 @@
1
+ import type { ComputedView } from '@likec4/core';
2
+ export declare function assignNavigateTo<R extends Iterable<ComputedView>>(views: R): R;
3
+ //# sourceMappingURL=assignNavigateTo.d.ts.map
@@ -0,0 +1,20 @@
1
+ import { find } from "remeda";
2
+ export function assignNavigateTo(views) {
3
+ const allElementViews = /* @__PURE__ */ new Map();
4
+ for (const v of views) {
5
+ if (v.viewOf && !v.extends) {
6
+ const viewsOf = allElementViews.get(v.viewOf) ?? [];
7
+ viewsOf.push(v.id);
8
+ allElementViews.set(v.viewOf, viewsOf);
9
+ }
10
+ }
11
+ for (const { id, nodes } of views) {
12
+ for (const node of nodes) {
13
+ const navigateTo = find(allElementViews.get(node.id) ?? [], (v) => v !== id);
14
+ if (navigateTo) {
15
+ node.navigateTo = navigateTo;
16
+ }
17
+ }
18
+ }
19
+ return views;
20
+ }
@@ -0,0 +1,4 @@
1
+ export * from './assignNavigateTo';
2
+ export * from './resolve-extended-views';
3
+ export * from './resolve-relative-paths';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,3 @@
1
+ export * from "./assignNavigateTo.js";
2
+ export * from "./resolve-extended-views.js";
3
+ export * from "./resolve-relative-paths.js";
@@ -0,0 +1,7 @@
1
+ import { type ElementView } from '@likec4/core';
2
+ /**
3
+ * Resolve rules of extended views
4
+ * (Removes invalid views)
5
+ */
6
+ export declare function resolveRulesExtendedViews<V extends Record<any, ElementView>>(unresolvedViews: V): V;
7
+ //# sourceMappingURL=resolve-extended-views.d.ts.map
@@ -0,0 +1,41 @@
1
+ import graphlib from "@dagrejs/graphlib";
2
+ import { isExtendsElementView } from "@likec4/core";
3
+ const { Graph, alg } = graphlib;
4
+ export function resolveRulesExtendedViews(unresolvedViews) {
5
+ const g = new Graph({
6
+ directed: true,
7
+ multigraph: false,
8
+ compound: false
9
+ });
10
+ for (const view of Object.values(unresolvedViews)) {
11
+ g.setNode(view.id);
12
+ if (isExtendsElementView(view)) {
13
+ g.setEdge(view.id, view.extends);
14
+ }
15
+ }
16
+ const cycles = alg.findCycles(g);
17
+ if (cycles.length > 0) {
18
+ cycles.flat().forEach((id) => g.removeNode(id));
19
+ }
20
+ const ordered = alg.postorder(g, g.sources());
21
+ return ordered.reduce((acc, id) => {
22
+ const view = unresolvedViews[id];
23
+ if (!view) {
24
+ return acc;
25
+ }
26
+ if (isExtendsElementView(view)) {
27
+ const extendsFrom = acc[view.extends];
28
+ if (!extendsFrom) {
29
+ return acc;
30
+ }
31
+ return Object.assign(acc, {
32
+ [view.id]: {
33
+ ...extendsFrom,
34
+ ...view,
35
+ rules: [...extendsFrom.rules, ...view.rules]
36
+ }
37
+ });
38
+ }
39
+ return Object.assign(acc, { [view.id]: view });
40
+ }, {});
41
+ }
@@ -0,0 +1,3 @@
1
+ import type { ElementView } from '@likec4/core';
2
+ export declare function resolveRelativePaths(views: ElementView[]): ElementView[];
3
+ //# sourceMappingURL=resolve-relative-paths.d.ts.map
@@ -0,0 +1,76 @@
1
+ import { uniq, zip } from "rambdax";
2
+ import { hasAtLeast } from "remeda";
3
+ import { invariant } from "@likec4/core";
4
+ function commonAncestorPath(views, sep = "/") {
5
+ if (views.length <= 1)
6
+ return "";
7
+ const uniqURIs = uniq(views.flatMap(({ docUri }) => docUri ? [docUri] : []));
8
+ if (uniqURIs.length === 0)
9
+ return "";
10
+ if (uniqURIs.length === 1) {
11
+ invariant(hasAtLeast(uniqURIs, 1));
12
+ return new URL(uniqURIs[0]).pathname;
13
+ }
14
+ invariant(hasAtLeast(uniqURIs, 2), "Expected at least 2 unique URIs");
15
+ const [baseUri, ...tail] = uniqURIs;
16
+ const parts = new URL(baseUri).pathname.split(sep);
17
+ let endOfPrefix = parts.length;
18
+ for (const uri of tail) {
19
+ if (uri === baseUri) {
20
+ continue;
21
+ }
22
+ const compare = new URL(uri).pathname.split(sep);
23
+ for (let i = 0; i < endOfPrefix; i++) {
24
+ if (compare[i] !== parts[i]) {
25
+ endOfPrefix = i;
26
+ }
27
+ }
28
+ if (endOfPrefix === 0)
29
+ return "";
30
+ }
31
+ const prefix = parts.slice(0, endOfPrefix).join(sep);
32
+ return prefix.endsWith(sep) ? prefix : prefix + sep;
33
+ }
34
+ export function resolveRelativePaths(views) {
35
+ const commonPrefix = commonAncestorPath(views);
36
+ return views.map((view) => {
37
+ if (!view.docUri) {
38
+ return {
39
+ ...view,
40
+ parts: []
41
+ };
42
+ }
43
+ const path = new URL(view.docUri).pathname;
44
+ const parts = path.replace(commonPrefix, "").split("/");
45
+ parts.pop();
46
+ return {
47
+ ...view,
48
+ parts
49
+ };
50
+ }).sort((a, b) => {
51
+ if (a.parts.length === b.parts.length) {
52
+ if (a.parts.length === 0) {
53
+ return 0;
54
+ }
55
+ if (a.parts.length === 1 && hasAtLeast(a.parts, 1) && hasAtLeast(b.parts, 1)) {
56
+ return a.parts[0].localeCompare(b.parts[0]);
57
+ }
58
+ for (const [_a, _b] of zip(a.parts, b.parts)) {
59
+ const compare = _a.localeCompare(_b);
60
+ if (compare !== 0) {
61
+ return compare;
62
+ }
63
+ }
64
+ return 0;
65
+ }
66
+ return a.parts.length - b.parts.length;
67
+ }).map(({ parts, ...view }) => {
68
+ if (view.docUri) {
69
+ return {
70
+ ...view,
71
+ relativePath: parts.join("/")
72
+ };
73
+ }
74
+ return view;
75
+ });
76
+ }