@likec4/language-server 1.30.0 → 1.32.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 (80) hide show
  1. package/dist/LikeC4LanguageServices.d.ts +2 -1
  2. package/dist/LikeC4LanguageServices.js +4 -3
  3. package/dist/Rpc.js +11 -4
  4. package/dist/ast.d.ts +11 -15
  5. package/dist/bundled.mjs +2545 -2475
  6. package/dist/formatting/LikeC4Formatter.d.ts +26 -2
  7. package/dist/formatting/LikeC4Formatter.js +106 -8
  8. package/dist/generated/ast.d.ts +171 -153
  9. package/dist/generated/ast.js +40 -18
  10. package/dist/generated/grammar.d.ts +1 -1
  11. package/dist/generated/grammar.js +1 -1
  12. package/dist/generated/module.d.ts +1 -1
  13. package/dist/lsp/CompletionProvider.js +1 -1
  14. package/dist/lsp/DocumentLinkProvider.js +4 -3
  15. package/dist/lsp/HoverProvider.js +9 -1
  16. package/dist/lsp/SemanticTokenProvider.js +18 -4
  17. package/dist/mcp/LikeC4MCPServerFactory.d.ts +5 -2
  18. package/dist/mcp/LikeC4MCPServerFactory.js +2 -72
  19. package/dist/mcp/LikeC4MCPTools.js +5 -3
  20. package/dist/mcp/sseserver/MCPServerFactory.d.ts +8 -0
  21. package/dist/mcp/sseserver/MCPServerFactory.js +78 -0
  22. package/dist/mcp/sseserver/with-mcp-server.d.ts +2 -0
  23. package/dist/mcp/sseserver/with-mcp-server.js +5 -1
  24. package/dist/mcp/utils.d.ts +5 -4
  25. package/dist/mcp/utils.js +2 -2
  26. package/dist/model/builder/MergedExtends.d.ts +4 -4
  27. package/dist/model/builder/MergedExtends.js +1 -1
  28. package/dist/model/builder/MergedSpecification.d.ts +4 -3
  29. package/dist/model/builder/MergedSpecification.js +9 -8
  30. package/dist/model/builder/assignTagColors.d.ts +7 -0
  31. package/dist/model/builder/assignTagColors.js +51 -0
  32. package/dist/model/builder/buildModel.d.ts +2 -2
  33. package/dist/model/builder/buildModel.js +40 -18
  34. package/dist/model/deployments-index.js +2 -2
  35. package/dist/model/fqn-index.d.ts +1 -1
  36. package/dist/model/fqn-index.js +6 -6
  37. package/dist/model/model-builder.d.ts +10 -4
  38. package/dist/model/model-builder.js +22 -19
  39. package/dist/model/model-locator.d.ts +10 -2
  40. package/dist/model/model-locator.js +65 -9
  41. package/dist/model/model-parser-where.d.ts +1 -1
  42. package/dist/model/model-parser-where.js +1 -1
  43. package/dist/model/model-parser.d.ts +10 -11
  44. package/dist/model/model-parser.js +11 -7
  45. package/dist/model/parser/Base.js +2 -3
  46. package/dist/model/parser/DeploymentModelParser.d.ts +1 -1
  47. package/dist/model/parser/DeploymentModelParser.js +7 -5
  48. package/dist/model/parser/DeploymentViewParser.d.ts +3 -3
  49. package/dist/model/parser/DeploymentViewParser.js +2 -1
  50. package/dist/model/parser/FqnRefParser.d.ts +1 -1
  51. package/dist/model/parser/FqnRefParser.js +2 -5
  52. package/dist/model/parser/GlobalsParser.d.ts +23 -24
  53. package/dist/model/parser/GlobalsParser.js +4 -4
  54. package/dist/model/parser/ModelParser.d.ts +1 -1
  55. package/dist/model/parser/ModelParser.js +1 -1
  56. package/dist/model/parser/PredicatesParser.d.ts +12 -12
  57. package/dist/model/parser/SpecificationParser.d.ts +5 -2
  58. package/dist/model/parser/SpecificationParser.js +18 -30
  59. package/dist/model/parser/ViewsParser.d.ts +23 -23
  60. package/dist/model/parser/ViewsParser.js +12 -19
  61. package/dist/model-change/changeElementStyle.d.ts +2 -2
  62. package/dist/model-change/changeElementStyle.js +6 -2
  63. package/dist/module.d.ts +1 -1
  64. package/dist/module.js +5 -2
  65. package/dist/protocol.d.ts +24 -3
  66. package/dist/protocol.js +4 -0
  67. package/dist/references/scope-computation.js +2 -3
  68. package/dist/test/testServices.d.ts +15 -0
  69. package/dist/test/testServices.js +4 -1
  70. package/dist/validation/relation.js +1 -1
  71. package/dist/validation/specification.js +1 -1
  72. package/dist/view-utils/assignNavigateTo.d.ts +1 -1
  73. package/dist/view-utils/assignNavigateTo.js +3 -3
  74. package/dist/views/likec4-views.d.ts +1 -3
  75. package/dist/views/likec4-views.js +68 -44
  76. package/dist/workspace/WorkspaceManager.d.ts +2 -2
  77. package/dist/workspace/WorkspaceManager.js +2 -2
  78. package/package.json +27 -25
  79. /package/dist/mcp/sseserver/{SSELikeC4MCPServer.d.ts → MCPServer.d.ts} +0 -0
  80. /package/dist/mcp/sseserver/{SSELikeC4MCPServer.js → MCPServer.js} +0 -0
@@ -1,5 +1,5 @@
1
1
  /******************************************************************************
2
- * This file was generated by langium-cli 3.4.0.
2
+ * This file was generated by langium-cli 3.5.0.
3
3
  * DO NOT EDIT MANUALLY!
4
4
  ******************************************************************************/
5
5
  import type { LangiumSharedCoreServices, LangiumCoreServices, LangiumGeneratedCoreServices, LangiumGeneratedSharedCoreServices, Module, IParserConfig } from 'langium';
@@ -22,7 +22,7 @@ export class LikeC4CompletionProvider extends DefaultCompletionProvider {
22
22
  this.services = services;
23
23
  }
24
24
  completionOptions = {
25
- triggerCharacters: ["."]
25
+ triggerCharacters: [".", "#"]
26
26
  };
27
27
  completionForKeyword(context, keyword, acceptor) {
28
28
  if (!this.filterKeyword(context, keyword)) {
@@ -41,10 +41,11 @@ export class LikeC4DocumentLinkProvider {
41
41
  return withoutLeadingSlash(link);
42
42
  }
43
43
  if (isRelative(link)) {
44
- const base = new URL(doc.uri.toString(true));
45
- const linkURL = new URL(link, base).toString();
44
+ const base = this.services.shared.workspace.ProjectsManager.getProject(doc).folder.toString();
45
+ const docURL = new URL(doc.uri.toString());
46
+ const linkURL = new URL(link, docURL).toString();
46
47
  return withoutLeadingSlash(
47
- withoutBase(linkURL, this.services.shared.workspace.WorkspaceManager.workspaceURL.toString())
48
+ withoutBase(linkURL, base)
48
49
  );
49
50
  }
50
51
  return null;
@@ -39,7 +39,7 @@ export class LikeC4HoverProvider extends AstNodeHoverProvider {
39
39
  const instance = this.parser.forDocument(doc).parseDeployedInstance(node);
40
40
  const [projectId, fqn] = FqnRef.isImportRef(instance.element) ? [instance.element.project, instance.element.model] : [doc.likec4ProjectId, instance.element.model];
41
41
  const el = projectId ? this.locator.getParsedElement(fqn, projectId) : this.locator.getParsedElement(fqn);
42
- const lines = [instance.id + " ", `instance of \`${instance.element}\``];
42
+ const lines = [instance.id + " ", `instance of \`${FqnRef.flatten(instance.element)}\``];
43
43
  if (el) {
44
44
  lines.push(`### ${el.title}`, "element kind `" + el.kind + "` ");
45
45
  }
@@ -66,6 +66,14 @@ export class LikeC4HoverProvider extends AstNodeHoverProvider {
66
66
  }
67
67
  };
68
68
  }
69
+ if (ast.isRelationshipKind(node)) {
70
+ return {
71
+ contents: {
72
+ kind: "markdown",
73
+ value: "relationship kind `" + node.name + "`"
74
+ }
75
+ };
76
+ }
69
77
  if (ast.isElement(node)) {
70
78
  const el = this.locator.getParsedElement(node);
71
79
  if (!el) {
@@ -62,6 +62,13 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
62
62
  });
63
63
  return "prune";
64
64
  }
65
+ if (ast.isRelationKindDotRef(node)) {
66
+ acceptor({
67
+ cst: node.$cstNode,
68
+ type: SemanticTokenTypes.function
69
+ });
70
+ return "prune";
71
+ }
65
72
  if (ast.isWhereRelationKind(node) && isTruthy(node.value)) {
66
73
  acceptor({
67
74
  node,
@@ -196,6 +203,14 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
196
203
  });
197
204
  return;
198
205
  }
206
+ if (ast.isSpecificationTag(node) && isTruthy(node.color)) {
207
+ acceptor({
208
+ node,
209
+ keyword: "color",
210
+ type: SemanticTokenTypes.property
211
+ });
212
+ return;
213
+ }
199
214
  if (ast.isSpecificationElementKind(node) || ast.isSpecificationRelationshipKind(node) || ast.isSpecificationDeploymentNodeKind(node)) {
200
215
  acceptor({
201
216
  node,
@@ -207,11 +222,10 @@ export class LikeC4SemanticTokenProvider extends AbstractSemanticTokenProvider {
207
222
  ]
208
223
  });
209
224
  }
210
- if (ast.isTags(node)) {
225
+ if (ast.isTagRef(node)) {
211
226
  acceptor({
212
- node,
213
- property: "values",
214
- type: SemanticTokenTypes.interface
227
+ cst: node.$cstNode,
228
+ type: SemanticTokenTypes.type
215
229
  });
216
230
  return "prune";
217
231
  }
@@ -1,15 +1,18 @@
1
1
  import type { ServerOptions } from '@modelcontextprotocol/sdk/server/index.js';
2
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import type { LikeC4Services } from '../module';
4
4
  export interface LikeC4MCPServer {
5
5
  start(port: number): Promise<void>;
6
6
  stop(): Promise<void>;
7
7
  }
8
+ export interface LikeC4MCPServerFactory {
9
+ create(options?: ServerOptions): McpServer;
10
+ }
8
11
  export declare class NoopLikeC4MCPServer implements LikeC4MCPServer {
9
12
  start(port: number): Promise<never>;
10
13
  stop(): Promise<never>;
11
14
  }
12
- export declare class LikeC4MCPServerFactory {
15
+ export declare class NoopLikeC4MCPServerFactory implements LikeC4MCPServerFactory {
13
16
  private services;
14
17
  constructor(services: LikeC4Services);
15
18
  create(options?: ServerOptions): McpServer;
@@ -1,6 +1,3 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import packageJson from "../../package.json" with { type: "json" };
3
- import { LikeC4MCPTools } from "./LikeC4MCPTools.js";
4
1
  export class NoopLikeC4MCPServer {
5
2
  start(port) {
6
3
  return Promise.reject(new Error("Not implemented"));
@@ -9,78 +6,11 @@ export class NoopLikeC4MCPServer {
9
6
  return Promise.reject(new Error("Not implemented"));
10
7
  }
11
8
  }
12
- function toolResponse(text) {
13
- return {
14
- content: [{
15
- type: "text",
16
- text
17
- }]
18
- };
19
- }
20
- export class LikeC4MCPServerFactory {
9
+ export class NoopLikeC4MCPServerFactory {
21
10
  constructor(services) {
22
11
  this.services = services;
23
12
  }
24
13
  create(options) {
25
- const {
26
- instructions,
27
- listProjects,
28
- readProjectSummary,
29
- searchElement,
30
- readElement,
31
- readView
32
- } = LikeC4MCPTools;
33
- const mcp = new McpServer({
34
- name: "LikeC4",
35
- version: packageJson.version
36
- }, {
37
- instructions,
38
- ...options,
39
- capabilities: {
40
- tools: {},
41
- ...options?.capabilities
42
- }
43
- });
44
- const tools = this.services.mcp.Tools;
45
- mcp.tool(
46
- listProjects.name,
47
- listProjects.description,
48
- async () => {
49
- return toolResponse(await tools.listProjects());
50
- }
51
- );
52
- mcp.tool(
53
- readProjectSummary.name,
54
- readProjectSummary.description,
55
- readProjectSummary.paramsSchema,
56
- async (params) => {
57
- return toolResponse(await tools.readProjectSummary(params.project));
58
- }
59
- );
60
- mcp.tool(
61
- searchElement.name,
62
- searchElement.description,
63
- searchElement.paramsSchema,
64
- async (params) => {
65
- return toolResponse(await tools.searchElement(params));
66
- }
67
- );
68
- mcp.tool(
69
- readElement.name,
70
- readElement.description,
71
- readElement.paramsSchema,
72
- async (params) => {
73
- return toolResponse(await tools.readElement(params));
74
- }
75
- );
76
- mcp.tool(
77
- readView.name,
78
- readView.description,
79
- readView.paramsSchema,
80
- async (params) => {
81
- return toolResponse(await tools.readView(params));
82
- }
83
- );
84
- return mcp;
14
+ throw new Error("NoopLikeC4MCPServerFactory - switch to implementation");
85
15
  }
86
16
  }
@@ -1,3 +1,4 @@
1
+ import { _type } from "@likec4/core";
1
2
  import { loggable } from "@likec4/log";
2
3
  import { flatMap } from "remeda";
3
4
  import stripIndent from "strip-indent";
@@ -194,10 +195,11 @@ export class DefaultLikeC4MCPTools {
194
195
  ""
195
196
  );
196
197
  }
197
- if (model.allTags().length > 0) {
198
+ const tags = model.tags;
199
+ if (tags.length > 0) {
198
200
  response.push(
199
201
  "tags:",
200
- ...model.allTags().map((t) => `- ${t}`),
202
+ ...tags.map((t) => `- ${t}`),
201
203
  ""
202
204
  );
203
205
  }
@@ -224,7 +226,7 @@ export class DefaultLikeC4MCPTools {
224
226
  "<views>",
225
227
  ...outputEach(model.views(), "No views", (v) => [
226
228
  `- id: ${v.id}`,
227
- ` viewType: ${v.__}`,
229
+ ` viewType: ${v[_type]}`,
228
230
  ` title: ${singleLine(v.title)}`,
229
231
  ""
230
232
  ]),
@@ -0,0 +1,8 @@
1
+ import type { ServerOptions } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import type { LikeC4Services } from '../../module';
4
+ export declare class LikeC4MCPServerFactory {
5
+ private services;
6
+ constructor(services: LikeC4Services);
7
+ create(options?: ServerOptions): McpServer;
8
+ }
@@ -0,0 +1,78 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import packageJson from "../../../package.json" with { type: "json" };
3
+ import { LikeC4MCPTools } from "../LikeC4MCPTools.js";
4
+ function toolResponse(text) {
5
+ return {
6
+ content: [{
7
+ type: "text",
8
+ text
9
+ }]
10
+ };
11
+ }
12
+ export class LikeC4MCPServerFactory {
13
+ constructor(services) {
14
+ this.services = services;
15
+ }
16
+ create(options) {
17
+ const {
18
+ instructions,
19
+ listProjects,
20
+ readProjectSummary,
21
+ searchElement,
22
+ readElement,
23
+ readView
24
+ } = LikeC4MCPTools;
25
+ const mcp = new McpServer({
26
+ name: "LikeC4",
27
+ version: packageJson.version
28
+ }, {
29
+ instructions,
30
+ ...options,
31
+ capabilities: {
32
+ tools: {},
33
+ ...options?.capabilities
34
+ }
35
+ });
36
+ const tools = this.services.mcp.Tools;
37
+ mcp.tool(
38
+ listProjects.name,
39
+ listProjects.description,
40
+ async () => {
41
+ return toolResponse(await tools.listProjects());
42
+ }
43
+ );
44
+ mcp.tool(
45
+ readProjectSummary.name,
46
+ readProjectSummary.description,
47
+ readProjectSummary.paramsSchema,
48
+ async (params) => {
49
+ return toolResponse(await tools.readProjectSummary(params.project));
50
+ }
51
+ );
52
+ mcp.tool(
53
+ searchElement.name,
54
+ searchElement.description,
55
+ searchElement.paramsSchema,
56
+ async (params) => {
57
+ return toolResponse(await tools.searchElement(params));
58
+ }
59
+ );
60
+ mcp.tool(
61
+ readElement.name,
62
+ readElement.description,
63
+ readElement.paramsSchema,
64
+ async (params) => {
65
+ return toolResponse(await tools.readElement(params));
66
+ }
67
+ );
68
+ mcp.tool(
69
+ readView.name,
70
+ readView.description,
71
+ readView.paramsSchema,
72
+ async (params) => {
73
+ return toolResponse(await tools.readView(params));
74
+ }
75
+ );
76
+ return mcp;
77
+ }
78
+ }
@@ -1,7 +1,9 @@
1
1
  import type { LikeC4Services } from '../../module';
2
2
  import type { LikeC4MCPServer } from '../LikeC4MCPServerFactory';
3
+ import { LikeC4MCPServerFactory } from './MCPServerFactory';
3
4
  export declare const WithMCPServer: {
4
5
  mcp: {
5
6
  Server(services: LikeC4Services): LikeC4MCPServer;
7
+ ServerFactory(services: LikeC4Services): LikeC4MCPServerFactory;
6
8
  };
7
9
  };
@@ -1,5 +1,6 @@
1
1
  import { logger } from "../../logger.js";
2
- import { SSELikeC4MCPServer } from "./SSELikeC4MCPServer.js";
2
+ import { SSELikeC4MCPServer } from "./MCPServer.js";
3
+ import { LikeC4MCPServerFactory } from "./MCPServerFactory.js";
3
4
  export const WithMCPServer = {
4
5
  mcp: {
5
6
  Server(services) {
@@ -28,6 +29,9 @@ export const WithMCPServer = {
28
29
  }).catch((err) => logger.error("Failed to start LikeC4 MCP Server", { err }));
29
30
  });
30
31
  return server;
32
+ },
33
+ ServerFactory(services) {
34
+ return new LikeC4MCPServerFactory(services);
31
35
  }
32
36
  }
33
37
  };
@@ -1,6 +1,7 @@
1
- import { type LikeC4Model, type ProjectId } from '@likec4/core';
1
+ import { type Any, type aux, type ProjectId } from '@likec4/core';
2
+ import { type ElementModel, type LikeC4ViewModel } from '@likec4/core/model';
2
3
  import type { LikeC4LanguageServices } from '../LikeC4LanguageServices';
3
- export declare function elementResource(languageServices: LikeC4LanguageServices, el: LikeC4Model.Element, projectId?: ProjectId): {
4
+ export declare function elementResource<A extends Any = Any>(languageServices: LikeC4LanguageServices, el: ElementModel<A>, projectId?: aux.ProjectId<A>): {
4
5
  id: any;
5
6
  parent: {
6
7
  id: any;
@@ -22,11 +23,11 @@ export declare function elementResource(languageServices: LikeC4LanguageServices
22
23
  views: any;
23
24
  sourceFile: import("../protocol").Locate.Res;
24
25
  };
25
- export declare function modelViewResource(languageServices: LikeC4LanguageServices, view: LikeC4Model.View, projectId?: ProjectId): {
26
+ export declare function modelViewResource(languageServices: LikeC4LanguageServices, view: LikeC4ViewModel<aux.Any>, projectId?: ProjectId): {
26
27
  id: any;
27
28
  title: any;
28
29
  projectId: any;
29
- viewType: any;
30
+ viewType: LikeC4ViewModel<aux.Any>;
30
31
  description: any;
31
32
  tags: any;
32
33
  nodes: any;
package/dist/mcp/utils.js CHANGED
@@ -1,4 +1,4 @@
1
- import { imap, toArray } from "@likec4/core";
1
+ import { _type, imap, toArray } from "@likec4/core";
2
2
  export function elementResource(languageServices, el, projectId) {
3
3
  return {
4
4
  id: el.id,
@@ -78,7 +78,7 @@ export function modelViewResource(languageServices, view, projectId) {
78
78
  id: view.id,
79
79
  title: view.title,
80
80
  projectId,
81
- viewType: view.__,
81
+ viewType: view[_type],
82
82
  description: view.$view.description ?? "",
83
83
  tags: view.tags,
84
84
  nodes: toArray(imap(view.nodes(), (node) => ({
@@ -3,10 +3,10 @@ import type { ParsedAstExtend } from '../../ast';
3
3
  export declare class MergedExtends {
4
4
  private mergedData;
5
5
  merge(parsedExtends: ParsedAstExtend[]): void;
6
- apply<E extends {
7
- id: c4.Fqn;
8
- tags?: c4.NonEmptyArray<c4.Tag> | null;
9
- links?: c4.NonEmptyArray<c4.Link> | null;
6
+ applyExtended<E extends {
7
+ id: string;
8
+ tags?: readonly string[] | null;
9
+ links?: readonly c4.Link[] | null;
10
10
  metadata?: Record<string, string>;
11
11
  }>(el: E): E;
12
12
  }
@@ -31,7 +31,7 @@ export class MergedExtends {
31
31
  this.mergedData.set(id, existing);
32
32
  }
33
33
  }
34
- apply(el) {
34
+ applyExtended(el) {
35
35
  const extendData = this.mergedData.get(el.id);
36
36
  if (!extendData) {
37
37
  return el;
@@ -8,7 +8,8 @@ import type { ParsedAstDeployment, ParsedAstDeploymentRelation, ParsedAstElement
8
8
  * and provides methods to convert parsed models into C4 model elements and relations.
9
9
  */
10
10
  export declare class MergedSpecification {
11
- readonly specs: ParsedAstSpecification;
11
+ readonly specs: Omit<ParsedAstSpecification, 'tags'>;
12
+ readonly tags: Readonly<Record<c4.Tag, c4.TagSpecification>>;
12
13
  readonly globals: c4.ModelGlobals;
13
14
  readonly imports: MultiMap<c4.ProjectId, c4.Fqn, Set<c4.Fqn>>;
14
15
  constructor(docs: ParsedLikeC4LangiumDocument[]);
@@ -19,7 +20,7 @@ export declare class MergedSpecification {
19
20
  /**
20
21
  * Converts a parsed model into a C4 model relation.
21
22
  */
22
- toModelRelation: ({ astPath, source: sourceFqnRef, target: targetFqnRef, kind, links, id, ...model }: ParsedAstRelation) => c4.ModelRelation | null;
23
+ toModelRelation: ({ astPath, source, target, kind, links, id, ...model }: ParsedAstRelation) => c4.Relationship | null;
23
24
  /**
24
25
  * Converts a parsed deployment model into a C4 deployment model
25
26
  */
@@ -27,5 +28,5 @@ export declare class MergedSpecification {
27
28
  /**
28
29
  * Converts a parsed deployment relation into a C4 deployment relation.
29
30
  */
30
- toDeploymentRelation: ({ astPath, source, target, kind, links, id, ...model }: ParsedAstDeploymentRelation) => c4.DeploymentRelation | null;
31
+ toDeploymentRelation: ({ astPath, source, target, kind, links, id, ...model }: ParsedAstDeploymentRelation) => c4.DeploymentRelationship | null;
31
32
  }
@@ -9,14 +9,15 @@ import {
9
9
  isNumber
10
10
  } from "remeda";
11
11
  import { logger, logWarnError } from "../../logger.js";
12
+ import { assignTagColors } from "./assignTagColors.js";
12
13
  export class MergedSpecification {
13
14
  specs = {
14
- tags: /* @__PURE__ */ new Set(),
15
15
  elements: {},
16
16
  deployments: {},
17
17
  relationships: {},
18
18
  colors: {}
19
19
  };
20
+ tags;
20
21
  globals = {
21
22
  predicates: {},
22
23
  dynamicPredicates: {},
@@ -24,13 +25,14 @@ export class MergedSpecification {
24
25
  };
25
26
  imports = new MultiMap(Set);
26
27
  constructor(docs) {
28
+ const tags = {};
27
29
  for (const doc of docs) {
28
30
  const {
29
31
  c4Specification: spec,
30
32
  c4Globals,
31
33
  c4Imports
32
34
  } = doc;
33
- spec.tags.forEach((t) => this.specs.tags.add(t));
35
+ Object.assign(tags, spec.tags);
34
36
  Object.assign(this.specs.elements, spec.elements);
35
37
  Object.assign(this.specs.relationships, spec.relationships);
36
38
  Object.assign(this.specs.colors, spec.colors);
@@ -42,6 +44,7 @@ export class MergedSpecification {
42
44
  this.imports.set(projectId, fqn);
43
45
  }
44
46
  }
47
+ this.tags = assignTagColors(tags);
45
48
  }
46
49
  /**
47
50
  * Converts a parsed model into a C4 model element.
@@ -98,7 +101,7 @@ export class MergedSpecification {
98
101
  ...isNumber(opacity) && { opacity }
99
102
  },
100
103
  links: links ?? null,
101
- tags: tags ?? null,
104
+ tags: tags ?? [],
102
105
  technology: technology ?? null,
103
106
  description: description ?? null,
104
107
  title,
@@ -115,15 +118,13 @@ export class MergedSpecification {
115
118
  */
116
119
  toModelRelation = ({
117
120
  astPath,
118
- source: sourceFqnRef,
119
- target: targetFqnRef,
121
+ source,
122
+ target,
120
123
  kind,
121
124
  links,
122
125
  id,
123
126
  ...model
124
127
  }) => {
125
- const target = FqnRef.toModelFqn(targetFqnRef);
126
- const source = FqnRef.toModelFqn(sourceFqnRef);
127
128
  if (isNonNullish(kind) && this.specs.relationships[kind]) {
128
129
  return {
129
130
  ...this.specs.relationships[kind],
@@ -150,7 +151,7 @@ export class MergedSpecification {
150
151
  if ("element" in parsed && !("kind" in parsed)) {
151
152
  return {
152
153
  ...parsed,
153
- element: FqnRef.toModelFqn(parsed.element)
154
+ element: FqnRef.flatten(parsed.element)
154
155
  };
155
156
  }
156
157
  if ("element" in parsed) {
@@ -0,0 +1,7 @@
1
+ import type * as c4 from '@likec4/core/types';
2
+ import type { ParsedAstSpecification } from '../../ast';
3
+ /**
4
+ * Colors are taken from the styles presets of the LikeC4
5
+ */
6
+ export declare const radixColors: string[];
7
+ export declare function assignTagColors(tags: ParsedAstSpecification['tags']): Record<c4.Tag, c4.TagSpecification>;
@@ -0,0 +1,51 @@
1
+ import { compareNatural, nonNullable } from "@likec4/core";
2
+ import { concat, entries, isTruthy, map, pipe, prop, pullObject, sort } from "remeda";
3
+ export const radixColors = [
4
+ "tomato",
5
+ "grass",
6
+ "blue",
7
+ "ruby",
8
+ "orange",
9
+ "indigo",
10
+ "pink",
11
+ "teal",
12
+ "purple",
13
+ "amber",
14
+ "crimson",
15
+ "red",
16
+ "lime",
17
+ "yellow",
18
+ "violet"
19
+ ];
20
+ export function assignTagColors(tags) {
21
+ const tagsWithColors = [];
22
+ const tagsWithoutColors = [];
23
+ for (const [tag, spec] of entries(tags)) {
24
+ if (isTruthy(spec.color)) {
25
+ tagsWithColors.push({
26
+ tag,
27
+ spec: {
28
+ color: spec.color
29
+ }
30
+ });
31
+ } else {
32
+ tagsWithoutColors.push(tag);
33
+ }
34
+ }
35
+ return pipe(
36
+ tagsWithoutColors,
37
+ sort(compareNatural),
38
+ map((tag, idx) => {
39
+ const color = nonNullable(radixColors[idx % radixColors.length]);
40
+ return {
41
+ tag,
42
+ spec: {
43
+ color
44
+ }
45
+ };
46
+ }),
47
+ concat(tagsWithColors),
48
+ sort((a, b) => compareNatural(a.tag, b.tag)),
49
+ pullObject(prop("tag"), prop("spec"))
50
+ );
51
+ }
@@ -1,4 +1,4 @@
1
- import type * as c4 from '@likec4/core';
1
+ import * as c4 from '@likec4/core';
2
2
  import { type MultiMap } from '@likec4/core';
3
3
  import type { ParsedLikeC4LangiumDocument } from '../../ast';
4
4
  export type BuildModelData = {
@@ -12,4 +12,4 @@ export type BuildModelData = {
12
12
  * This function builds a model from all documents, merging the specifications
13
13
  * and globals, and applying the extends to the elements.
14
14
  */
15
- export declare function buildModelData(docs: ParsedLikeC4LangiumDocument[]): BuildModelData;
15
+ export declare function buildModelData(projectId: string, docs: ParsedLikeC4LangiumDocument[]): BuildModelData;