@likec4/language-server 1.32.1 → 1.33.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 (51) hide show
  1. package/dist/ast.d.ts +6 -5
  2. package/dist/ast.js +3 -0
  3. package/dist/bundled.mjs +3347 -2559
  4. package/dist/config/schema.d.ts +2 -2
  5. package/dist/config/schema.js +8 -11
  6. package/dist/formatting/LikeC4Formatter.js +2 -2
  7. package/dist/generated/ast.d.ts +22 -11
  8. package/dist/generated/ast.js +19 -5
  9. package/dist/generated/grammar.js +1 -1
  10. package/dist/lsp/SemanticTokenProvider.js +1 -1
  11. package/dist/mcp/LikeC4MCPServerFactory.d.ts +4 -0
  12. package/dist/mcp/LikeC4MCPServerFactory.js +6 -0
  13. package/dist/mcp/LikeC4MCPTools.js +5 -2
  14. package/dist/mcp/sseserver/MCPServer.d.ts +4 -2
  15. package/dist/mcp/sseserver/MCPServer.js +12 -6
  16. package/dist/mcp/sseserver/with-mcp-server.js +22 -6
  17. package/dist/model/builder/MergedSpecification.js +8 -4
  18. package/dist/model/index.d.ts +1 -0
  19. package/dist/model/index.js +1 -0
  20. package/dist/model/model-builder.js +20 -19
  21. package/dist/model/model-parser.d.ts +126 -0
  22. package/dist/model/parser/Base.d.ts +30 -2
  23. package/dist/model/parser/Base.js +54 -3
  24. package/dist/model/parser/DeploymentModelParser.d.ts +14 -0
  25. package/dist/model/parser/DeploymentModelParser.js +28 -23
  26. package/dist/model/parser/DeploymentViewParser.d.ts +14 -0
  27. package/dist/model/parser/DeploymentViewParser.js +15 -6
  28. package/dist/model/parser/FqnRefParser.d.ts +14 -0
  29. package/dist/model/parser/FqnRefParser.js +8 -6
  30. package/dist/model/parser/GlobalsParser.d.ts +14 -0
  31. package/dist/model/parser/ImportsParser.d.ts +14 -0
  32. package/dist/model/parser/ModelParser.d.ts +14 -0
  33. package/dist/model/parser/ModelParser.js +27 -17
  34. package/dist/model/parser/PredicatesParser.d.ts +14 -0
  35. package/dist/model/parser/SpecificationParser.d.ts +14 -0
  36. package/dist/model/parser/SpecificationParser.js +16 -11
  37. package/dist/model/parser/ValueConverter.d.ts +4 -0
  38. package/dist/model/parser/ValueConverter.js +12 -0
  39. package/dist/model/parser/ViewsParser.d.ts +14 -0
  40. package/dist/model/parser/ViewsParser.js +21 -7
  41. package/dist/module.d.ts +6 -3
  42. package/dist/module.js +9 -5
  43. package/dist/utils/index.d.ts +5 -0
  44. package/dist/utils/index.js +19 -0
  45. package/dist/views/configurable-layouter.d.ts +2 -2
  46. package/dist/views/configurable-layouter.js +23 -27
  47. package/dist/views/likec4-views.d.ts +2 -3
  48. package/dist/views/likec4-views.js +28 -50
  49. package/dist/workspace/ProjectsManager.d.ts +0 -1
  50. package/dist/workspace/ProjectsManager.js +15 -4
  51. package/package.json +24 -23
@@ -6,7 +6,6 @@ import {
6
6
  isArray,
7
7
  isBoolean,
8
8
  isEmpty,
9
- isNonNullish,
10
9
  isNumber,
11
10
  isString,
12
11
  isTruthy,
@@ -21,16 +20,47 @@ import {
21
20
  parseAstOpacityProperty,
22
21
  parseAstPercent,
23
22
  parseAstSizeValue,
23
+ parseMarkdownAsString,
24
24
  toColor
25
25
  } from "../../ast.js";
26
26
  import { projectIdFrom } from "../../utils/index.js";
27
27
  import { readStrictFqn } from "../../utils/elementRef.js";
28
28
  import { checksFromDiagnostics } from "../../validation/index.js";
29
29
  export function toSingleLine(str) {
30
- return isNonNullish(str) ? removeIndent(str).split("\n").join(" ") : void 0;
30
+ if (str === null || str === void 0) {
31
+ return void 0;
32
+ }
33
+ const without = removeIndent(str);
34
+ if (isString(without)) {
35
+ return without.split("\n").join(" ");
36
+ }
37
+ if ("md" in without) {
38
+ return {
39
+ md: without.md.split("\n").join(" ")
40
+ };
41
+ }
42
+ return {
43
+ txt: without.txt.split("\n").join(" ")
44
+ };
31
45
  }
32
46
  export function removeIndent(str) {
33
- return isNonNullish(str) ? stripIndent(str).trim() : void 0;
47
+ if (str === null || str === void 0) {
48
+ return void 0;
49
+ }
50
+ switch (true) {
51
+ case isString(str):
52
+ return stripIndent(str).trim();
53
+ case (ast.isMarkdownOrString(str) && isTruthy(str.markdown)):
54
+ return {
55
+ md: stripIndent(str.markdown).trim()
56
+ };
57
+ case (ast.isMarkdownOrString(str) && isTruthy(str.text)):
58
+ return {
59
+ txt: stripIndent(str.text).trim()
60
+ };
61
+ default:
62
+ return void 0;
63
+ }
34
64
  }
35
65
  export class BaseParser {
36
66
  constructor(services, doc) {
@@ -72,11 +102,18 @@ export class BaseParser {
72
102
  const data = pipe(
73
103
  metadataAstNode.props,
74
104
  map((p) => [p.key, removeIndent(p.value)]),
105
+ map(([key, value]) => [key, value.md || value.txt]),
75
106
  filter(([_, value]) => isTruthy(value)),
76
107
  fromEntries()
77
108
  );
78
109
  return isEmpty(data) ? void 0 : data;
79
110
  }
111
+ parseMarkdownOrString(markdownOrString) {
112
+ if (ast.isMarkdownOrString(markdownOrString)) {
113
+ return removeIndent(markdownOrString);
114
+ }
115
+ return void 0;
116
+ }
80
117
  convertTags(withTags) {
81
118
  return this.parseTags(withTags);
82
119
  }
@@ -258,4 +295,18 @@ export class BaseParser {
258
295
  }
259
296
  return result;
260
297
  }
298
+ /**
299
+ * Parses title, description and technology
300
+ * Inline properties (right on node) have higher priority than body properties (inside '{...}')
301
+ */
302
+ parseTitleDescriptionTechnology(inlineProps, bodyProps) {
303
+ const title = removeIndent(inlineProps.title ?? parseMarkdownAsString(bodyProps.title));
304
+ const description = inlineProps.description ? { txt: removeIndent(inlineProps.description) } : this.parseMarkdownOrString(bodyProps.description);
305
+ const technology = toSingleLine(inlineProps.technology) ?? removeIndent(parseMarkdownAsString(bodyProps.technology));
306
+ return {
307
+ ...isTruthy(title) && { title },
308
+ ...isTruthy(description) && { description },
309
+ ...isTruthy(technology) && { technology }
310
+ };
311
+ }
261
312
  }
@@ -38,6 +38,7 @@ export declare function DeploymentModelParser<TBase extends WithExpressionV2>(B:
38
38
  getMetadata(metadataAstNode: ast.MetadataProperty | undefined): {
39
39
  [key: string]: string;
40
40
  } | undefined;
41
+ parseMarkdownOrString(markdownOrString: ast.MarkdownOrString | undefined): c4.MarkdownOrString | undefined;
41
42
  convertTags<E extends {
42
43
  tags?: ast.Tags;
43
44
  }>(withTags?: E | undefined): any;
@@ -50,5 +51,18 @@ export declare function DeploymentModelParser<TBase extends WithExpressionV2>(B:
50
51
  parseColorLiteral(astNode: ast.ColorLiteral): c4.ColorLiteral | undefined;
51
52
  parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): import("../../ast").ParsedElementStyle;
52
53
  parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): import("../../ast").ParsedElementStyle;
54
+ parseTitleDescriptionTechnology(inlineProps: {
55
+ title?: string | undefined;
56
+ description?: string | undefined;
57
+ technology?: string | undefined;
58
+ }, bodyProps: {
59
+ title?: ast.MarkdownOrString | undefined;
60
+ description?: ast.MarkdownOrString | undefined;
61
+ technology?: ast.MarkdownOrString | undefined;
62
+ }): {
63
+ title?: string;
64
+ description?: c4.MarkdownOrString;
65
+ technology?: string;
66
+ };
53
67
  };
54
68
  } & TBase;
@@ -6,7 +6,6 @@ import {
6
6
  } from "../../ast.js";
7
7
  import { logWarnError } from "../../logger.js";
8
8
  import { stringHash } from "../../utils/stringHash.js";
9
- import { removeIndent, toSingleLine } from "./Base.js";
10
9
  function* streamDeploymentModel(doc) {
11
10
  const traverseStack = LinkedList.from(
12
11
  doc.parseResult.value.deployments.flatMap((m) => m.elements)
@@ -74,11 +73,14 @@ export function DeploymentModelParser(B) {
74
73
  astNode.body?.props ?? [],
75
74
  filter(isValid),
76
75
  filter(ast.isElementStringProperty),
77
- mapToObj((p) => [p.key, p.value || void 0])
76
+ mapToObj((p) => [p.key, p.value])
77
+ );
78
+ const { title, ...descAndTech } = this.parseTitleDescriptionTechnology(
79
+ {
80
+ title: astNode.title
81
+ },
82
+ bodyProps
78
83
  );
79
- const title = removeIndent(astNode.title ?? bodyProps.title);
80
- const description = removeIndent(bodyProps.description);
81
- const technology = toSingleLine(bodyProps.technology);
82
84
  const links = this.convertLinks(astNode.body);
83
85
  return {
84
86
  id,
@@ -87,8 +89,7 @@ export function DeploymentModelParser(B) {
87
89
  ...metadata && { metadata },
88
90
  ...tags && { tags },
89
91
  ...links && isNonEmptyArray(links) && { links },
90
- ...isTruthy(technology) && { technology },
91
- ...isTruthy(description) && { description },
92
+ ...descAndTech,
92
93
  style
93
94
  };
94
95
  }
@@ -104,21 +105,22 @@ export function DeploymentModelParser(B) {
104
105
  astNode.body?.props ?? [],
105
106
  filter(isValid),
106
107
  filter(ast.isElementStringProperty),
107
- mapToObj((p) => [p.key, p.value || void 0])
108
+ mapToObj((p) => [p.key, p.value])
109
+ );
110
+ const titleDescAndTech = this.parseTitleDescriptionTechnology(
111
+ {
112
+ title: astNode.title
113
+ },
114
+ bodyProps
108
115
  );
109
- const title = removeIndent(astNode.title ?? bodyProps.title);
110
- const description = removeIndent(bodyProps.description);
111
- const technology = toSingleLine(bodyProps.technology);
112
116
  const links = this.convertLinks(astNode.body);
113
117
  return {
114
118
  id,
115
119
  element: target,
116
120
  ...metadata && { metadata },
117
- ...title && { title },
118
121
  ...tags && { tags },
119
122
  ...links && isNonEmptyArray(links) && { links },
120
- ...isTruthy(technology) && { technology },
121
- ...isTruthy(description) && { description },
123
+ ...titleDescAndTech,
122
124
  style
123
125
  };
124
126
  }
@@ -164,9 +166,11 @@ export function DeploymentModelParser(B) {
164
166
  const links = this.convertLinks(astNode.body);
165
167
  const kind = (astNode.kind ?? astNode.dotKind?.kind)?.ref?.name;
166
168
  const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty));
167
- const bodyProps = mapToObj(
168
- astNode.body?.props.filter(ast.isRelationStringProperty) ?? [],
169
- (p) => [p.key, p.value]
169
+ const bodyProps = pipe(
170
+ astNode.body?.props ?? [],
171
+ filter(ast.isRelationStringProperty),
172
+ filter((p) => isTruthy(p.value)),
173
+ mapToObj((p) => [p.key, p.value])
170
174
  );
171
175
  const navigateTo = pipe(
172
176
  astNode.body?.props ?? [],
@@ -175,9 +179,12 @@ export function DeploymentModelParser(B) {
175
179
  filter(isTruthy),
176
180
  first()
177
181
  );
178
- const title = removeIndent(astNode.title ?? bodyProps.title);
179
- const description = removeIndent(bodyProps.description);
180
- const technology = toSingleLine(astNode.technology) ?? removeIndent(bodyProps.technology);
182
+ const titleDescAndTech = this.parseTitleDescriptionTechnology(
183
+ {
184
+ title: astNode.title
185
+ },
186
+ bodyProps
187
+ );
181
188
  const styleProp = astNode.body?.props.find(ast.isRelationStyleProperty);
182
189
  const id = stringHash(
183
190
  "deployment",
@@ -189,10 +196,8 @@ export function DeploymentModelParser(B) {
189
196
  id,
190
197
  source,
191
198
  target,
192
- ...title && { title },
199
+ ...titleDescAndTech,
193
200
  ...metadata && { metadata },
194
- ...isTruthy(technology) && { technology },
195
- ...isTruthy(description) && { description },
196
201
  ...kind && { kind },
197
202
  ...tags && { tags },
198
203
  ...isNonEmptyArray(links) && { links },
@@ -36,6 +36,7 @@ export declare function DeploymentViewParser<TBase extends WithExpressionV2 & Wi
36
36
  getMetadata(metadataAstNode: ast.MetadataProperty | undefined): {
37
37
  [key: string]: string;
38
38
  } | undefined;
39
+ parseMarkdownOrString(markdownOrString: ast.MarkdownOrString | undefined): c4.MarkdownOrString | undefined;
39
40
  convertTags<E extends {
40
41
  tags?: ast.Tags;
41
42
  }>(withTags?: E | undefined): any;
@@ -48,6 +49,19 @@ export declare function DeploymentViewParser<TBase extends WithExpressionV2 & Wi
48
49
  parseColorLiteral(astNode: ast.ColorLiteral): c4.ColorLiteral | undefined;
49
50
  parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): import("../../ast").ParsedElementStyle;
50
51
  parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): import("../../ast").ParsedElementStyle;
52
+ parseTitleDescriptionTechnology(inlineProps: {
53
+ title?: string | undefined;
54
+ description?: string | undefined;
55
+ technology?: string | undefined;
56
+ }, bodyProps: {
57
+ title?: ast.MarkdownOrString | undefined;
58
+ description?: ast.MarkdownOrString | undefined;
59
+ technology?: ast.MarkdownOrString | undefined;
60
+ }): {
61
+ title?: string;
62
+ description?: c4.MarkdownOrString;
63
+ technology?: string;
64
+ };
51
65
  parseDeployment(): void;
52
66
  parseDeploymentNode(astNode: ast.DeploymentNode): import("../../ast").ParsedAstDeployment.Node;
53
67
  parseDeployedInstance(astNode: ast.DeployedInstance): import("../../ast").ParsedAstDeployment.Instance;
@@ -1,11 +1,11 @@
1
1
  import * as c4 from "@likec4/core";
2
2
  import { invariant, isNonEmptyArray, nonexhaustive } from "@likec4/core";
3
- import { isNonNullish } from "remeda";
4
- import { ast, toAutoLayout, ViewOps } from "../../ast.js";
3
+ import { filter, isNonNullish, mapToObj, pipe } from "remeda";
4
+ import { ast, parseMarkdownAsString, toAutoLayout, ViewOps } from "../../ast.js";
5
5
  import { logWarnError } from "../../logger.js";
6
6
  import { stringHash } from "../../utils/index.js";
7
7
  import { parseViewManualLayout } from "../../view-utils/manual-layout.js";
8
- import { removeIndent, toSingleLine } from "./Base.js";
8
+ import { removeIndent } from "./Base.js";
9
9
  export function DeploymentViewParser(B) {
10
10
  return class DeploymentViewParser extends B {
11
11
  parseDeploymentView(astNode) {
@@ -20,8 +20,17 @@ export function DeploymentViewParser(B) {
20
20
  astPath
21
21
  );
22
22
  }
23
- const title = toSingleLine(props.find((p) => p.key === "title")?.value) ?? null;
24
- const description = removeIndent(props.find((p) => p.key === "description")?.value) ?? null;
23
+ const {
24
+ title = null,
25
+ description = null
26
+ } = this.parseTitleDescriptionTechnology(
27
+ {},
28
+ pipe(
29
+ props,
30
+ filter(ast.isViewStringProperty),
31
+ mapToObj((p) => [p.key, p.value])
32
+ )
33
+ );
25
34
  const tags = this.convertTags(body);
26
35
  const links = this.convertLinks(body);
27
36
  ViewOps.writeId(astNode, id);
@@ -75,7 +84,7 @@ export function DeploymentViewParser(B) {
75
84
  }
76
85
  parseDeploymentViewRuleStyle(astRule) {
77
86
  const style = this.parseStyleProps(astRule.props.filter(ast.isStyleProperty));
78
- const notation = removeIndent(astRule.props.find(ast.isNotationProperty)?.value);
87
+ const notation = removeIndent(parseMarkdownAsString(astRule.props.find(ast.isNotationProperty)?.value));
79
88
  const targets = this.parseFqnExpressions(astRule.targets);
80
89
  return {
81
90
  targets,
@@ -31,6 +31,7 @@ export declare function ExpressionV2Parser<TBase extends Base>(B: TBase): {
31
31
  getMetadata(metadataAstNode: ast.MetadataProperty | undefined): {
32
32
  [key: string]: string;
33
33
  } | undefined;
34
+ parseMarkdownOrString(markdownOrString: ast.MarkdownOrString | undefined): c4.MarkdownOrString | undefined;
34
35
  convertTags<E extends {
35
36
  tags?: ast.Tags;
36
37
  }>(withTags?: E | undefined): any;
@@ -43,5 +44,18 @@ export declare function ExpressionV2Parser<TBase extends Base>(B: TBase): {
43
44
  parseColorLiteral(astNode: ast.ColorLiteral): c4.ColorLiteral | undefined;
44
45
  parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): import("../../ast").ParsedElementStyle;
45
46
  parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): import("../../ast").ParsedElementStyle;
47
+ parseTitleDescriptionTechnology(inlineProps: {
48
+ title?: string | undefined;
49
+ description?: string | undefined;
50
+ technology?: string | undefined;
51
+ }, bodyProps: {
52
+ title?: ast.MarkdownOrString | undefined;
53
+ description?: ast.MarkdownOrString | undefined;
54
+ technology?: ast.MarkdownOrString | undefined;
55
+ }): {
56
+ title?: string;
57
+ description?: c4.MarkdownOrString;
58
+ technology?: string;
59
+ };
46
60
  };
47
61
  } & TBase;
@@ -1,6 +1,6 @@
1
1
  import { invariant, nonexhaustive, nonNullable } from "@likec4/core";
2
2
  import { isBoolean, isDefined, isNonNullish, isTruthy } from "remeda";
3
- import { ast, parseAstOpacityProperty, parseAstSizeValue, toColor } from "../../ast.js";
3
+ import { ast, parseAstOpacityProperty, parseAstSizeValue, parseMarkdownAsString, toColor } from "../../ast.js";
4
4
  import { logWarnError } from "../../logger.js";
5
5
  import { projectIdFrom } from "../../utils/index.js";
6
6
  import { importsRef, instanceRef } from "../../utils/fqnRef.js";
@@ -88,7 +88,7 @@ export function ExpressionV2Parser(B) {
88
88
  }
89
89
  if (ast.isElementStringProperty(prop)) {
90
90
  if (isDefined(prop.value)) {
91
- acc.custom[prop.key] = removeIndent(prop.value) || "";
91
+ acc.custom[prop.key] = removeIndent(parseMarkdownAsString(prop.value)) || "";
92
92
  }
93
93
  return acc;
94
94
  }
@@ -125,8 +125,9 @@ export function ExpressionV2Parser(B) {
125
125
  return acc;
126
126
  }
127
127
  if (ast.isNotationProperty(prop)) {
128
- if (isTruthy(prop.value)) {
129
- acc.custom[prop.key] = removeIndent(prop.value);
128
+ const value = isTruthy(prop.value) ? removeIndent(parseMarkdownAsString(prop.value)) : void 0;
129
+ if (value) {
130
+ acc.custom[prop.key] = value;
130
131
  }
131
132
  return acc;
132
133
  }
@@ -261,8 +262,9 @@ export function ExpressionV2Parser(B) {
261
262
  return props.reduce(
262
263
  (acc, prop) => {
263
264
  if (ast.isRelationStringProperty(prop) || ast.isNotationProperty(prop) || ast.isNotesProperty(prop)) {
264
- if (isDefined(prop.value)) {
265
- acc.customRelation[prop.key] = removeIndent(prop.value) ?? "";
265
+ const value = isTruthy(prop.value) ? removeIndent(parseMarkdownAsString(prop.value)) : void 0;
266
+ if (value) {
267
+ acc.customRelation[prop.key] = value;
266
268
  }
267
269
  return acc;
268
270
  }
@@ -60,6 +60,7 @@ export declare function GlobalsParser<TBase extends WithViewsParser>(B: TBase):
60
60
  getMetadata(metadataAstNode: ast.MetadataProperty | undefined): {
61
61
  [key: string]: string;
62
62
  } | undefined;
63
+ parseMarkdownOrString(markdownOrString: ast.MarkdownOrString | undefined): c4.MarkdownOrString | undefined;
63
64
  convertTags<E extends {
64
65
  tags?: ast.Tags;
65
66
  }>(withTags?: E | undefined): any;
@@ -72,6 +73,19 @@ export declare function GlobalsParser<TBase extends WithViewsParser>(B: TBase):
72
73
  parseColorLiteral(astNode: ast.ColorLiteral): c4.ColorLiteral | undefined;
73
74
  parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): import("../../ast").ParsedElementStyle;
74
75
  parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): import("../../ast").ParsedElementStyle;
76
+ parseTitleDescriptionTechnology(inlineProps: {
77
+ title?: string | undefined;
78
+ description?: string | undefined;
79
+ technology?: string | undefined;
80
+ }, bodyProps: {
81
+ title?: ast.MarkdownOrString | undefined;
82
+ description?: ast.MarkdownOrString | undefined;
83
+ technology?: ast.MarkdownOrString | undefined;
84
+ }): {
85
+ title?: string;
86
+ description?: c4.MarkdownOrString;
87
+ technology?: string;
88
+ };
75
89
  parseDeploymentView(astNode: ast.DeploymentView): import("../../ast").ParsedAstDeploymentView;
76
90
  parseDeploymentViewRule(astRule: ast.DeploymentViewRule): c4.DeploymentViewRule;
77
91
  parseDeploymentViewRulePredicate(astRule: ast.DeploymentViewRulePredicate): c4.DeploymentViewPredicate;
@@ -17,6 +17,7 @@ export declare function ImportsParser<TBase extends Base>(B: TBase): {
17
17
  getMetadata(metadataAstNode: ast.MetadataProperty | undefined): {
18
18
  [key: string]: string;
19
19
  } | undefined;
20
+ parseMarkdownOrString(markdownOrString: ast.MarkdownOrString | undefined): ProjectId | undefined;
20
21
  convertTags<E extends {
21
22
  tags?: ast.Tags;
22
23
  }>(withTags?: E | undefined): any;
@@ -29,5 +30,18 @@ export declare function ImportsParser<TBase extends Base>(B: TBase): {
29
30
  parseColorLiteral(astNode: ast.ColorLiteral): ProjectId | undefined;
30
31
  parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): import("../../ast").ParsedElementStyle;
31
32
  parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): import("../../ast").ParsedElementStyle;
33
+ parseTitleDescriptionTechnology(inlineProps: {
34
+ title?: string | undefined;
35
+ description?: string | undefined;
36
+ technology?: string | undefined;
37
+ }, bodyProps: {
38
+ title?: ast.MarkdownOrString | undefined;
39
+ description?: ast.MarkdownOrString | undefined;
40
+ technology?: ast.MarkdownOrString | undefined;
41
+ }): {
42
+ title?: string;
43
+ description?: ProjectId;
44
+ technology?: string;
45
+ };
32
46
  };
33
47
  } & TBase;
@@ -37,6 +37,7 @@ export declare function ModelParser<TBase extends WithExpressionV2>(B: TBase): {
37
37
  getMetadata(metadataAstNode: ast.MetadataProperty | undefined): {
38
38
  [key: string]: string;
39
39
  } | undefined;
40
+ parseMarkdownOrString(markdownOrString: ast.MarkdownOrString | undefined): c4.MarkdownOrString | undefined;
40
41
  convertTags<E extends {
41
42
  tags?: ast.Tags;
42
43
  }>(withTags?: E | undefined): any;
@@ -49,5 +50,18 @@ export declare function ModelParser<TBase extends WithExpressionV2>(B: TBase): {
49
50
  parseColorLiteral(astNode: ast.ColorLiteral): c4.ColorLiteral | undefined;
50
51
  parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): import("../../ast").ParsedElementStyle;
51
52
  parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): import("../../ast").ParsedElementStyle;
53
+ parseTitleDescriptionTechnology(inlineProps: {
54
+ title?: string | undefined;
55
+ description?: string | undefined;
56
+ technology?: string | undefined;
57
+ }, bodyProps: {
58
+ title?: ast.MarkdownOrString | undefined;
59
+ description?: ast.MarkdownOrString | undefined;
60
+ technology?: ast.MarkdownOrString | undefined;
61
+ }): {
62
+ title?: string;
63
+ description?: c4.MarkdownOrString;
64
+ technology?: string;
65
+ };
52
66
  };
53
67
  } & TBase;
@@ -1,14 +1,13 @@
1
1
  import { invariant, isNonEmptyArray, LinkedList, nonexhaustive, nonNullable } from "@likec4/core";
2
2
  import { FqnRef } from "@likec4/core/types";
3
3
  import { loggable } from "@likec4/log";
4
- import { filter, first, isDefined, isEmpty, isNonNullish, isTruthy, map, mapToObj, pipe } from "remeda";
4
+ import { filter, first, isDefined, isEmpty, isTruthy, map, mapToObj, pipe } from "remeda";
5
5
  import {
6
6
  ast,
7
7
  toRelationshipStyleExcludeDefaults
8
8
  } from "../../ast.js";
9
9
  import { logger as mainLogger } from "../../logger.js";
10
10
  import { stringHash } from "../../utils/stringHash.js";
11
- import { removeIndent, toSingleLine } from "./Base.js";
12
11
  const logger = mainLogger.getChild("ModelParser");
13
12
  function* streamModel(doc) {
14
13
  const traverseStack = LinkedList.from(doc.parseResult.value.models.flatMap((m) => m.elements));
@@ -76,16 +75,21 @@ ${error}`, {
76
75
  const style = this.parseElementStyle(astNode.body?.props);
77
76
  const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty));
78
77
  const astPath = this.getAstNodePath(astNode);
79
- let [title, description, technology] = astNode.props ?? [];
78
+ let [_title, _description, _technology] = astNode.props ?? [];
80
79
  const bodyProps = pipe(
81
80
  astNode.body?.props ?? [],
82
81
  filter(isValid),
83
82
  filter(ast.isElementStringProperty),
84
- mapToObj((p) => [p.key, p.value || void 0])
83
+ mapToObj((p) => [p.key, p.value])
84
+ );
85
+ const { title, ...descAndTech } = this.parseTitleDescriptionTechnology(
86
+ {
87
+ title: _title,
88
+ description: _description,
89
+ technology: _technology
90
+ },
91
+ bodyProps
85
92
  );
86
- title = removeIndent(title ?? bodyProps.title);
87
- description = removeIndent(bodyProps.description ?? description);
88
- technology = toSingleLine(bodyProps.technology ?? technology);
89
93
  const links = this.parseLinks(astNode.body);
90
94
  return {
91
95
  id,
@@ -95,8 +99,7 @@ ${error}`, {
95
99
  ...metadata && { metadata },
96
100
  ...tags && { tags },
97
101
  ...links && isNonEmptyArray(links) && { links },
98
- ...isTruthy(technology) && { technology },
99
- ...isTruthy(description) && { description },
102
+ ...descAndTech,
100
103
  style
101
104
  };
102
105
  }
@@ -140,9 +143,11 @@ ${error}`, {
140
143
  const kind = (astNode.kind ?? astNode.dotKind?.kind)?.ref?.name;
141
144
  const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty));
142
145
  const astPath = this.getAstNodePath(astNode);
143
- const bodyProps = mapToObj(
144
- astNode.body?.props.filter(ast.isRelationStringProperty).filter((p) => isNonNullish(p.value)) ?? [],
145
- (p) => [p.key, p.value]
146
+ const bodyProps = pipe(
147
+ astNode.body?.props ?? [],
148
+ filter(ast.isRelationStringProperty),
149
+ filter((p) => isTruthy(p.value)),
150
+ mapToObj((p) => [p.key, p.value])
146
151
  );
147
152
  const navigateTo = pipe(
148
153
  astNode.body?.props ?? [],
@@ -151,9 +156,15 @@ ${error}`, {
151
156
  filter(isTruthy),
152
157
  first()
153
158
  );
154
- const title = removeIndent(astNode.title ?? bodyProps.title) ?? "";
155
- const description = removeIndent(bodyProps.description);
156
- const technology = toSingleLine(astNode.technology) ?? removeIndent(bodyProps.technology);
159
+ const { title = "", ...descAndTech } = this.parseTitleDescriptionTechnology(
160
+ // inline props
161
+ {
162
+ title: astNode.title,
163
+ description: astNode.description,
164
+ technology: astNode.technology
165
+ },
166
+ bodyProps
167
+ );
157
168
  const styleProp = astNode.body?.props.find(ast.isRelationStyleProperty);
158
169
  const id = stringHash(
159
170
  astPath,
@@ -167,8 +178,7 @@ ${error}`, {
167
178
  target,
168
179
  title,
169
180
  ...metadata && { metadata },
170
- ...isTruthy(technology) && { technology },
171
- ...isTruthy(description) && { description },
181
+ ...descAndTech,
172
182
  ...kind && { kind },
173
183
  ...tags && { tags },
174
184
  ...isNonEmptyArray(links) && { links },
@@ -42,6 +42,7 @@ export declare function PredicatesParser<TBase extends WithExpressionV2>(B: TBas
42
42
  getMetadata(metadataAstNode: ast.MetadataProperty | undefined): {
43
43
  [key: string]: string;
44
44
  } | undefined;
45
+ parseMarkdownOrString(markdownOrString: ast.MarkdownOrString | undefined): c4.MarkdownOrString | undefined;
45
46
  convertTags<E extends {
46
47
  tags?: ast.Tags;
47
48
  }>(withTags?: E | undefined): any;
@@ -54,5 +55,18 @@ export declare function PredicatesParser<TBase extends WithExpressionV2>(B: TBas
54
55
  parseColorLiteral(astNode: ast.ColorLiteral): c4.ColorLiteral | undefined;
55
56
  parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): import("../../ast").ParsedElementStyle;
56
57
  parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): import("../../ast").ParsedElementStyle;
58
+ parseTitleDescriptionTechnology(inlineProps: {
59
+ title?: string | undefined;
60
+ description?: string | undefined;
61
+ technology?: string | undefined;
62
+ }, bodyProps: {
63
+ title?: ast.MarkdownOrString | undefined;
64
+ description?: ast.MarkdownOrString | undefined;
65
+ technology?: ast.MarkdownOrString | undefined;
66
+ }): {
67
+ title?: string;
68
+ description?: c4.MarkdownOrString;
69
+ technology?: string;
70
+ };
57
71
  };
58
72
  } & TBase;
@@ -23,6 +23,7 @@ export declare function SpecificationParser<TBase extends Base>(B: TBase): {
23
23
  getMetadata(metadataAstNode: ast.MetadataProperty | undefined): {
24
24
  [key: string]: string;
25
25
  } | undefined;
26
+ parseMarkdownOrString(markdownOrString: ast.MarkdownOrString | undefined): c4.MarkdownOrString | undefined;
26
27
  convertTags<E extends {
27
28
  tags?: ast.Tags;
28
29
  }>(withTags?: E | undefined): any;
@@ -35,5 +36,18 @@ export declare function SpecificationParser<TBase extends Base>(B: TBase): {
35
36
  parseColorLiteral(astNode: ast.ColorLiteral): c4.ColorLiteral | undefined;
36
37
  parseElementStyle(elementProps: Array<ast.ElementProperty> | ast.ElementStyleProperty | undefined): import("../../ast").ParsedElementStyle;
37
38
  parseStyleProps(styleProps: Array<ast.StyleProperty> | undefined): import("../../ast").ParsedElementStyle;
39
+ parseTitleDescriptionTechnology(inlineProps: {
40
+ title?: string | undefined;
41
+ description?: string | undefined;
42
+ technology?: string | undefined;
43
+ }, bodyProps: {
44
+ title?: ast.MarkdownOrString | undefined;
45
+ description?: ast.MarkdownOrString | undefined;
46
+ technology?: ast.MarkdownOrString | undefined;
47
+ }): {
48
+ title?: string;
49
+ description?: c4.MarkdownOrString;
50
+ technology?: string;
51
+ };
38
52
  };
39
53
  } & TBase;
@@ -1,6 +1,6 @@
1
1
  import { nonNullable } from "@likec4/core/utils";
2
- import { filter, isNonNullish, isTruthy, mapToObj, pipe } from "remeda";
3
- import { ast, toRelationshipStyleExcludeDefaults } from "../../ast.js";
2
+ import { filter, isNonNullish, isNullish, isTruthy, mapToObj, omitBy, pipe } from "remeda";
3
+ import { ast, parseMarkdownAsString, toRelationshipStyleExcludeDefaults } from "../../ast.js";
4
4
  import { logger, logWarnError } from "../../logger.js";
5
5
  import { removeIndent } from "./Base.js";
6
6
  export function SpecificationParser(B) {
@@ -43,7 +43,8 @@ export function SpecificationParser(B) {
43
43
  const bodyProps = pipe(
44
44
  props.filter(ast.isSpecificationRelationshipStringProperty) ?? [],
45
45
  filter((p) => this.isValid(p) && isNonNullish(p.value)),
46
- mapToObj((p) => [p.key, removeIndent(p.value)])
46
+ mapToObj((p) => [p.key, removeIndent(parseMarkdownAsString(p.value))]),
47
+ omitBy(isNullish)
47
48
  );
48
49
  c4Specification.relationships[kindName] = {
49
50
  ...bodyProps,
@@ -55,14 +56,18 @@ export function SpecificationParser(B) {
55
56
  }
56
57
  const tags_specs = specifications.flatMap((s) => s.tags.filter(this.isValid));
57
58
  for (const tagSpec of tags_specs) {
58
- const tag = tagSpec.tag.name;
59
- const astPath = this.getAstNodePath(tagSpec.tag);
60
- const color = tagSpec.color && this.parseColorLiteral(tagSpec.color);
61
- if (isTruthy(tag)) {
62
- c4Specification.tags[tag] = {
63
- astPath,
64
- ...color ? { color } : {}
65
- };
59
+ try {
60
+ const tag = tagSpec.tag.name;
61
+ const astPath = this.getAstNodePath(tagSpec.tag);
62
+ const color = tagSpec.color && this.parseColorLiteral(tagSpec.color);
63
+ if (isTruthy(tag)) {
64
+ c4Specification.tags[tag] = {
65
+ astPath,
66
+ ...color ? { color } : {}
67
+ };
68
+ }
69
+ } catch (e) {
70
+ logWarnError(e);
66
71
  }
67
72
  }
68
73
  const colors_specs = specifications.flatMap((s) => s.colors.filter(isValid));
@@ -0,0 +1,4 @@
1
+ import { type CstNode, type ValueType, DefaultValueConverter } from 'langium';
2
+ export declare class LikeC4ValueConverter extends DefaultValueConverter {
3
+ protected runConverter(rule: any, input: string, cstNode: CstNode): ValueType;
4
+ }