@likec4/language-server 1.17.0 → 1.18.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.
- package/contrib/likec4.tmLanguage.json +1 -1
- package/dist/browser.cjs +1 -1
- package/dist/browser.d.cts +2 -2
- package/dist/browser.d.mts +2 -2
- package/dist/browser.d.ts +2 -2
- package/dist/browser.mjs +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/protocol.d.cts +8 -5
- package/dist/protocol.d.mts +8 -5
- package/dist/protocol.d.ts +8 -5
- package/dist/shared/{language-server.DZRuJVSg.cjs → language-server.CO_nmHiL.cjs} +5605 -4215
- package/dist/shared/{language-server.DJo88TnT.d.cts → language-server.Da6ey08o.d.cts} +391 -110
- package/dist/shared/{language-server.PEjk7U9s.d.ts → language-server.De7S3e5Z.d.ts} +391 -110
- package/dist/shared/{language-server.BgDKnNok.d.mts → language-server.Dj4iDjtB.d.mts} +391 -110
- package/dist/shared/{language-server.B8qSDsWW.mjs → language-server.oO_9JoAG.mjs} +5594 -4215
- package/package.json +17 -31
- package/src/Rpc.ts +6 -3
- package/src/ast.ts +124 -71
- package/src/formatting/LikeC4Formatter.ts +9 -4
- package/src/generated/ast.ts +656 -40
- package/src/generated/grammar.ts +2 -2
- package/src/generated/module.ts +3 -2
- package/src/index.ts +1 -0
- package/src/like-c4.langium +170 -22
- package/src/logger.ts +7 -2
- package/src/lsp/CodeLensProvider.ts +0 -1
- package/src/lsp/CompletionProvider.ts +17 -2
- package/src/lsp/DocumentSymbolProvider.ts +5 -2
- package/src/lsp/HoverProvider.ts +34 -2
- package/src/lsp/SemanticTokenProvider.ts +58 -32
- package/src/model/deployments-index.ts +218 -0
- package/src/model/fqn-computation.ts +1 -1
- package/src/model/fqn-index.ts +0 -1
- package/src/model/index.ts +1 -0
- package/src/model/model-builder.ts +172 -37
- package/src/model/model-locator.ts +36 -7
- package/src/model/model-parser.ts +554 -92
- package/src/model-change/changeViewLayout.ts +2 -2
- package/src/module.ts +10 -4
- package/src/protocol.ts +10 -6
- package/src/references/index.ts +1 -0
- package/src/references/name-provider.ts +37 -0
- package/src/references/scope-computation.ts +130 -21
- package/src/references/scope-provider.ts +63 -36
- package/src/shared/NodeKindProvider.ts +15 -3
- package/src/utils/deploymentRef.ts +31 -0
- package/src/{elementRef.ts → utils/elementRef.ts} +1 -1
- package/src/utils/stringHash.ts +2 -2
- package/src/validation/_shared.ts +7 -5
- package/src/validation/deployment-checks.ts +144 -0
- package/src/validation/dynamic-view-step.ts +1 -1
- package/src/validation/index.ts +7 -0
- package/src/validation/relation.ts +1 -1
- package/src/validation/view-predicates/deployments.ts +56 -0
- package/src/validation/view-predicates/index.ts +1 -0
- package/src/view-utils/assignNavigateTo.ts +6 -5
- package/src/view-utils/index.ts +0 -1
- package/dist/model-graph/index.cjs +0 -10
- package/dist/model-graph/index.d.cts +0 -81
- package/dist/model-graph/index.d.mts +0 -81
- package/dist/model-graph/index.d.ts +0 -81
- package/dist/model-graph/index.mjs +0 -1
- package/dist/shared/language-server.BGGRJRnr.d.mts +0 -1338
- package/dist/shared/language-server.BXFhlTPo.mjs +0 -1953
- package/dist/shared/language-server.Bmpq16Gw.d.ts +0 -1338
- package/dist/shared/language-server.C1ZfM22X.d.cts +0 -1338
- package/dist/shared/language-server.N8HLDQqz.cjs +0 -1967
- package/src/model-graph/LikeC4ModelGraph.ts +0 -338
- package/src/model-graph/compute-view/__test__/fixture.ts +0 -630
- package/src/model-graph/compute-view/compute.ts +0 -788
- package/src/model-graph/compute-view/index.ts +0 -33
- package/src/model-graph/compute-view/predicates.ts +0 -509
- package/src/model-graph/dynamic-view/__test__/fixture.ts +0 -61
- package/src/model-graph/dynamic-view/compute.ts +0 -281
- package/src/model-graph/dynamic-view/index.ts +0 -29
- package/src/model-graph/index.ts +0 -3
- package/src/model-graph/utils/applyCustomElementProperties.ts +0 -65
- package/src/model-graph/utils/applyCustomRelationProperties.ts +0 -41
- package/src/model-graph/utils/applyViewRuleStyles.ts +0 -49
- package/src/model-graph/utils/buildComputeNodes.ts +0 -113
- package/src/model-graph/utils/buildElementNotations.ts +0 -63
- package/src/model-graph/utils/elementExpressionToPredicate.ts +0 -39
- package/src/model-graph/utils/relationExpressionToPredicates.ts +0 -43
- package/src/model-graph/utils/sortNodes.ts +0 -105
- package/src/model-graph/utils/uniqueTags.test.ts +0 -42
- package/src/model-graph/utils/uniqueTags.ts +0 -19
- package/src/utils/graphlib.ts +0 -9
- package/src/view-utils/resolve-extended-views.ts +0 -66
- package/src/view-utils/resolve-global-rules.ts +0 -88
- package/src/view-utils/view-hash.ts +0 -27
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import { type HexColorLiteral, invariant, isNonEmptyArray, nonexhaustive } from '@likec4/core'
|
|
2
1
|
import type * as c4 from '@likec4/core'
|
|
2
|
+
import { type HexColorLiteral, invariant, isNonEmptyArray, nameFromFqn, nonexhaustive, nonNullable } from '@likec4/core'
|
|
3
3
|
import type { AstNode, LangiumDocument } from 'langium'
|
|
4
4
|
import { AstUtils, CstUtils } from 'langium'
|
|
5
|
-
import { filter, first, flatMap, isDefined, isNonNullish, isTruthy, map, mapToObj, pipe } from 'remeda'
|
|
5
|
+
import { filter, first, flatMap, isArray, isDefined, isNonNullish, isTruthy, map, mapToObj, pipe } from 'remeda'
|
|
6
6
|
import stripIndent from 'strip-indent'
|
|
7
7
|
import type { Writable } from 'type-fest'
|
|
8
8
|
import type {
|
|
9
9
|
ChecksFromDiagnostics,
|
|
10
10
|
FqnIndexedDocument,
|
|
11
|
+
ParsedAstDeployment,
|
|
12
|
+
ParsedAstDeploymentRelation,
|
|
13
|
+
ParsedAstDeploymentView,
|
|
11
14
|
ParsedAstDynamicView,
|
|
12
15
|
ParsedAstElement,
|
|
13
16
|
ParsedAstElementView,
|
|
@@ -30,11 +33,12 @@ import {
|
|
|
30
33
|
toRelationshipStyleExcludeDefaults,
|
|
31
34
|
ViewOps
|
|
32
35
|
} from '../ast'
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import { logError, logger, logWarnError } from '../logger'
|
|
36
|
+
import { type NotationProperty } from '../generated/ast'
|
|
37
|
+
import { logger, logWarnError } from '../logger'
|
|
36
38
|
import type { LikeC4Services } from '../module'
|
|
37
39
|
import { stringHash } from '../utils'
|
|
40
|
+
import { instanceRef } from '../utils/deploymentRef'
|
|
41
|
+
import { elementRef, getFqnElementRef } from '../utils/elementRef'
|
|
38
42
|
import { deserializeFromComment, hasManualLayout } from '../view-utils/manual-layout'
|
|
39
43
|
import type { FqnIndex } from './fqn-index'
|
|
40
44
|
import { parseWhereClause } from './model-parser-where'
|
|
@@ -60,21 +64,13 @@ export class LikeC4ModelParser {
|
|
|
60
64
|
logger.debug(`[ModelParser] Created`)
|
|
61
65
|
}
|
|
62
66
|
|
|
63
|
-
parse(doc: LangiumDocument
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
continue
|
|
70
|
-
}
|
|
71
|
-
try {
|
|
72
|
-
result.push(this.parseLikeC4Document(doc))
|
|
73
|
-
} catch (cause) {
|
|
74
|
-
logError(new Error(`Error parsing document ${doc.uri.toString()}`, { cause }))
|
|
75
|
-
}
|
|
67
|
+
parse(doc: LangiumDocument): ParsedLikeC4LangiumDocument {
|
|
68
|
+
invariant(isFqnIndexedDocument(doc), `Not a FqnIndexedDocument: ${doc.uri.toString(true)}`)
|
|
69
|
+
try {
|
|
70
|
+
return this.parseLikeC4Document(doc)
|
|
71
|
+
} catch (cause) {
|
|
72
|
+
throw new Error(`Error parsing document ${doc.uri.toString()}`, { cause })
|
|
76
73
|
}
|
|
77
|
-
return result
|
|
78
74
|
}
|
|
79
75
|
|
|
80
76
|
protected parseLikeC4Document(_doc: FqnIndexedDocument) {
|
|
@@ -83,14 +79,21 @@ export class LikeC4ModelParser {
|
|
|
83
79
|
this.parseSpecification(doc, isValid)
|
|
84
80
|
this.parseModel(doc, isValid)
|
|
85
81
|
this.parseGlobal(doc, isValid)
|
|
82
|
+
this.parseDeployment(doc, isValid)
|
|
86
83
|
this.parseViews(doc, isValid)
|
|
87
84
|
return doc
|
|
88
85
|
}
|
|
89
86
|
|
|
90
87
|
private parseSpecification(doc: ParsedLikeC4LangiumDocument, isValid: IsValidFn) {
|
|
91
|
-
const {
|
|
88
|
+
const {
|
|
89
|
+
parseResult: {
|
|
90
|
+
value: {
|
|
91
|
+
specifications
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
c4Specification
|
|
95
|
+
} = doc
|
|
92
96
|
|
|
93
|
-
const specifications = parseResult.value.specifications.filter(isValid)
|
|
94
97
|
const element_specs = specifications.flatMap(s => s.elements.filter(isValid))
|
|
95
98
|
for (const { kind, props } of element_specs) {
|
|
96
99
|
try {
|
|
@@ -103,9 +106,10 @@ export class LikeC4ModelParser {
|
|
|
103
106
|
continue
|
|
104
107
|
}
|
|
105
108
|
const style = props.find(ast.isElementStyleProperty)
|
|
106
|
-
const bodyProps =
|
|
107
|
-
props.filter(ast.isSpecificationElementStringProperty)
|
|
108
|
-
p =>
|
|
109
|
+
const bodyProps = pipe(
|
|
110
|
+
props.filter(ast.isSpecificationElementStringProperty) ?? [],
|
|
111
|
+
filter(p => isValid(p) && isNonNullish(p.value)),
|
|
112
|
+
mapToObj(p => [p.key, removeIndent(p.value)] satisfies [string, string])
|
|
109
113
|
)
|
|
110
114
|
c4Specification.elements[kindName] = {
|
|
111
115
|
...bodyProps,
|
|
@@ -129,13 +133,14 @@ export class LikeC4ModelParser {
|
|
|
129
133
|
logger.warn(`Relationship kind "${kindName}" is already defined`)
|
|
130
134
|
continue
|
|
131
135
|
}
|
|
132
|
-
const bodyProps =
|
|
133
|
-
props.filter(ast.isSpecificationRelationshipStringProperty)
|
|
134
|
-
p =>
|
|
136
|
+
const bodyProps = pipe(
|
|
137
|
+
props.filter(ast.isSpecificationRelationshipStringProperty) ?? [],
|
|
138
|
+
filter(p => isValid(p) && isNonNullish(p.value)),
|
|
139
|
+
mapToObj(p => [p.key, removeIndent(p.value)] satisfies [string, string])
|
|
135
140
|
)
|
|
136
141
|
c4Specification.relationships[kindName] = {
|
|
137
142
|
...bodyProps,
|
|
138
|
-
...toRelationshipStyleExcludeDefaults(props)
|
|
143
|
+
...toRelationshipStyleExcludeDefaults(props, isValid)
|
|
139
144
|
}
|
|
140
145
|
} catch (e) {
|
|
141
146
|
logWarnError(e)
|
|
@@ -150,6 +155,15 @@ export class LikeC4ModelParser {
|
|
|
150
155
|
}
|
|
151
156
|
}
|
|
152
157
|
|
|
158
|
+
const deploymentNodes_specs = specifications.flatMap(s => s.deploymentNodes.filter(isValid))
|
|
159
|
+
for (const deploymentNode of deploymentNodes_specs) {
|
|
160
|
+
try {
|
|
161
|
+
Object.assign(c4Specification.deployments, this.parseSpecificationDeploymentNodeKind(deploymentNode, isValid))
|
|
162
|
+
} catch (e) {
|
|
163
|
+
logWarnError(e)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
153
167
|
const colors_specs = specifications.flatMap(s => s.colors.filter(isValid))
|
|
154
168
|
for (const { name, color } of colors_specs) {
|
|
155
169
|
try {
|
|
@@ -168,6 +182,31 @@ export class LikeC4ModelParser {
|
|
|
168
182
|
}
|
|
169
183
|
}
|
|
170
184
|
|
|
185
|
+
private parseSpecificationDeploymentNodeKind(
|
|
186
|
+
{ kind, props }: ast.SpecificationDeploymentNodeKind,
|
|
187
|
+
isValid: IsValidFn
|
|
188
|
+
): { [key: c4.DeploymentNodeKind]: c4.DeploymentNodeKindSpecification } {
|
|
189
|
+
const kindName = kind.name as c4.DeploymentNodeKind
|
|
190
|
+
if (!isTruthy(kindName)) {
|
|
191
|
+
throw new Error('DeploymentNodeKind name is not resolved')
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const style = props.find(ast.isElementStyleProperty)
|
|
195
|
+
const bodyProps = pipe(
|
|
196
|
+
props.filter(ast.isSpecificationElementStringProperty) ?? [],
|
|
197
|
+
filter(p => isValid(p) && isNonNullish(p.value)),
|
|
198
|
+
mapToObj(p => [p.key, removeIndent(p.value)] satisfies [string, string])
|
|
199
|
+
)
|
|
200
|
+
return {
|
|
201
|
+
[kindName]: {
|
|
202
|
+
...bodyProps,
|
|
203
|
+
style: {
|
|
204
|
+
...toElementStyle(style?.props, isValid)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
171
210
|
private parseModel(doc: ParsedLikeC4LangiumDocument, isValid: IsValidFn) {
|
|
172
211
|
for (const el of streamModel(doc, isValid)) {
|
|
173
212
|
if (ast.isElement(el)) {
|
|
@@ -180,7 +219,7 @@ export class LikeC4ModelParser {
|
|
|
180
219
|
}
|
|
181
220
|
if (ast.isRelation(el)) {
|
|
182
221
|
try {
|
|
183
|
-
doc.c4Relations.push(this.parseRelation(el))
|
|
222
|
+
doc.c4Relations.push(this.parseRelation(el, isValid))
|
|
184
223
|
} catch (e) {
|
|
185
224
|
logWarnError(e)
|
|
186
225
|
}
|
|
@@ -192,7 +231,7 @@ export class LikeC4ModelParser {
|
|
|
192
231
|
|
|
193
232
|
private parseElement(astNode: ast.Element, isValid: IsValidFn): ParsedAstElement {
|
|
194
233
|
const id = this.resolveFqn(astNode)
|
|
195
|
-
const kind = astNode.kind
|
|
234
|
+
const kind = nonNullable(astNode.kind.ref, 'Element kind is not resolved').name as c4.ElementKind
|
|
196
235
|
const tags = this.convertTags(astNode.body)
|
|
197
236
|
const stylePropsAst = astNode.body?.props.find(ast.isElementStyleProperty)?.props
|
|
198
237
|
const style = toElementStyle(stylePropsAst, isValid)
|
|
@@ -201,12 +240,14 @@ export class LikeC4ModelParser {
|
|
|
201
240
|
|
|
202
241
|
let [title, description, technology] = astNode.props ?? []
|
|
203
242
|
|
|
204
|
-
const bodyProps =
|
|
205
|
-
astNode.body?.props
|
|
206
|
-
|
|
243
|
+
const bodyProps = pipe(
|
|
244
|
+
astNode.body?.props ?? [],
|
|
245
|
+
filter(isValid),
|
|
246
|
+
filter(ast.isElementStringProperty),
|
|
247
|
+
mapToObj(p => [p.key, p.value || undefined])
|
|
207
248
|
)
|
|
208
249
|
|
|
209
|
-
title =
|
|
250
|
+
title = removeIndent(title ?? bodyProps.title)
|
|
210
251
|
description = removeIndent(bodyProps.description ?? description)
|
|
211
252
|
technology = toSingleLine(bodyProps.technology ?? technology)
|
|
212
253
|
|
|
@@ -214,7 +255,7 @@ export class LikeC4ModelParser {
|
|
|
214
255
|
|
|
215
256
|
// Property has higher priority than from style
|
|
216
257
|
const iconProp = astNode.body?.props.find(ast.isIconProperty)
|
|
217
|
-
if (iconProp) {
|
|
258
|
+
if (iconProp && isValid(iconProp)) {
|
|
218
259
|
const value = iconProp.libicon?.ref?.name ?? iconProp.value
|
|
219
260
|
if (isTruthy(value)) {
|
|
220
261
|
style.icon = value as c4.IconUrl
|
|
@@ -235,7 +276,7 @@ export class LikeC4ModelParser {
|
|
|
235
276
|
}
|
|
236
277
|
}
|
|
237
278
|
|
|
238
|
-
private parseRelation(astNode: ast.Relation): ParsedAstRelation {
|
|
279
|
+
private parseRelation(astNode: ast.Relation, isValid: IsValidFn): ParsedAstRelation {
|
|
239
280
|
const coupling = resolveRelationPoints(astNode)
|
|
240
281
|
const target = this.resolveFqn(coupling.target)
|
|
241
282
|
const source = this.resolveFqn(coupling.source)
|
|
@@ -260,14 +301,14 @@ export class LikeC4ModelParser {
|
|
|
260
301
|
|
|
261
302
|
const title = removeIndent(astNode.title ?? bodyProps.title) ?? ''
|
|
262
303
|
const description = removeIndent(bodyProps.description)
|
|
263
|
-
const technology =
|
|
304
|
+
const technology = toSingleLine(astNode.technology) ?? removeIndent(bodyProps.technology)
|
|
264
305
|
|
|
265
306
|
const styleProp = astNode.body?.props.find(ast.isRelationStyleProperty)
|
|
266
307
|
const id = stringHash(
|
|
267
308
|
astPath,
|
|
268
309
|
source,
|
|
269
310
|
target
|
|
270
|
-
) as c4.
|
|
311
|
+
) as c4.RelationId
|
|
271
312
|
return {
|
|
272
313
|
id,
|
|
273
314
|
astPath,
|
|
@@ -280,8 +321,8 @@ export class LikeC4ModelParser {
|
|
|
280
321
|
...(kind && { kind }),
|
|
281
322
|
...(tags && { tags }),
|
|
282
323
|
...(isNonEmptyArray(links) && { links }),
|
|
283
|
-
...toRelationshipStyleExcludeDefaults(styleProp?.props),
|
|
284
|
-
...(navigateTo && { navigateTo: navigateTo as c4.
|
|
324
|
+
...toRelationshipStyleExcludeDefaults(styleProp?.props, isValid),
|
|
325
|
+
...(navigateTo && { navigateTo: navigateTo as c4.ViewId })
|
|
285
326
|
}
|
|
286
327
|
}
|
|
287
328
|
|
|
@@ -381,11 +422,10 @@ export class LikeC4ModelParser {
|
|
|
381
422
|
}
|
|
382
423
|
|
|
383
424
|
private parseViews(doc: ParsedLikeC4LangiumDocument, isValid: IsValidFn) {
|
|
384
|
-
const
|
|
385
|
-
for (const viewBlock of viewBlocks) {
|
|
425
|
+
for (const viewBlock of doc.parseResult.value.views) {
|
|
386
426
|
const localStyles = viewBlock.styles.flatMap(s => {
|
|
387
427
|
try {
|
|
388
|
-
return this.parseViewRuleStyleOrGlobalRef(s, isValid)
|
|
428
|
+
return isValid(s) ? this.parseViewRuleStyleOrGlobalRef(s, isValid) : []
|
|
389
429
|
} catch (e) {
|
|
390
430
|
logWarnError(e)
|
|
391
431
|
return []
|
|
@@ -397,11 +437,19 @@ export class LikeC4ModelParser {
|
|
|
397
437
|
if (!isValid(view)) {
|
|
398
438
|
continue
|
|
399
439
|
}
|
|
400
|
-
|
|
401
|
-
ast.isElementView(view)
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
440
|
+
switch (true) {
|
|
441
|
+
case ast.isElementView(view):
|
|
442
|
+
doc.c4Views.push(this.parseElementView(view, localStyles, isValid))
|
|
443
|
+
break
|
|
444
|
+
case ast.isDynamicView(view):
|
|
445
|
+
doc.c4Views.push(this.parseDynamicElementView(view, localStyles, isValid))
|
|
446
|
+
break
|
|
447
|
+
case ast.isDeploymentView(view):
|
|
448
|
+
doc.c4Views.push(this.parseDeploymentView(view, isValid))
|
|
449
|
+
break
|
|
450
|
+
default:
|
|
451
|
+
nonexhaustive(view)
|
|
452
|
+
}
|
|
405
453
|
} catch (e) {
|
|
406
454
|
logWarnError(e)
|
|
407
455
|
}
|
|
@@ -412,16 +460,20 @@ export class LikeC4ModelParser {
|
|
|
412
460
|
// TODO validate view rules
|
|
413
461
|
private parseViewRulePredicate(astNode: ast.ViewRulePredicate, _isValid: IsValidFn): c4.ViewRulePredicate {
|
|
414
462
|
const exprs = [] as c4.Expression[]
|
|
415
|
-
let predicate
|
|
463
|
+
let predicate = astNode.predicates
|
|
416
464
|
while (predicate) {
|
|
465
|
+
const { value, prev } = predicate
|
|
417
466
|
try {
|
|
418
|
-
if (isTruthy(
|
|
419
|
-
exprs.unshift(this.parsePredicate(
|
|
467
|
+
if (isTruthy(value) && _isValid(value as any)) {
|
|
468
|
+
exprs.unshift(this.parsePredicate(value, _isValid))
|
|
420
469
|
}
|
|
421
470
|
} catch (e) {
|
|
422
471
|
logWarnError(e)
|
|
423
472
|
}
|
|
424
|
-
|
|
473
|
+
if (!prev) {
|
|
474
|
+
break
|
|
475
|
+
}
|
|
476
|
+
predicate = prev
|
|
425
477
|
}
|
|
426
478
|
return ast.isIncludePredicate(astNode) ? { include: exprs } : { exclude: exprs }
|
|
427
479
|
}
|
|
@@ -441,7 +493,9 @@ export class LikeC4ModelParser {
|
|
|
441
493
|
let iter: ast.ElementExpressionsIterator['prev'] = astNode
|
|
442
494
|
while (iter) {
|
|
443
495
|
try {
|
|
444
|
-
|
|
496
|
+
if (iter.value) {
|
|
497
|
+
exprs.unshift(this.parseElementExpr(iter.value))
|
|
498
|
+
}
|
|
445
499
|
} catch (e) {
|
|
446
500
|
logWarnError(e)
|
|
447
501
|
}
|
|
@@ -501,7 +555,8 @@ export class LikeC4ModelParser {
|
|
|
501
555
|
const element = this.resolveFqn(elementNode)
|
|
502
556
|
return {
|
|
503
557
|
element,
|
|
504
|
-
|
|
558
|
+
isChildren: astNode.suffix === '.*',
|
|
559
|
+
isDescendants: astNode.suffix === '.**'
|
|
505
560
|
}
|
|
506
561
|
}
|
|
507
562
|
if (ast.isElementRef(astNode)) {
|
|
@@ -523,17 +578,19 @@ export class LikeC4ModelParser {
|
|
|
523
578
|
const props = astNode.custom?.props ?? []
|
|
524
579
|
return props.reduce(
|
|
525
580
|
(acc, prop) => {
|
|
581
|
+
if (!_isValid(prop)) {
|
|
582
|
+
return acc
|
|
583
|
+
}
|
|
526
584
|
if (ast.isNavigateToProperty(prop)) {
|
|
527
585
|
const viewId = prop.value.view.$refText
|
|
528
586
|
if (isTruthy(viewId)) {
|
|
529
|
-
acc.custom.navigateTo = viewId as c4.
|
|
587
|
+
acc.custom.navigateTo = viewId as c4.ViewId
|
|
530
588
|
}
|
|
531
589
|
return acc
|
|
532
590
|
}
|
|
533
591
|
if (ast.isElementStringProperty(prop)) {
|
|
534
592
|
if (isDefined(prop.value)) {
|
|
535
|
-
|
|
536
|
-
acc.custom[prop.key] = value || ''
|
|
593
|
+
acc.custom[prop.key] = removeIndent(prop.value) || ''
|
|
537
594
|
}
|
|
538
595
|
return acc
|
|
539
596
|
}
|
|
@@ -664,7 +721,7 @@ export class LikeC4ModelParser {
|
|
|
664
721
|
if (ast.isRelationNavigateToProperty(prop)) {
|
|
665
722
|
const viewId = prop.value.view.ref?.name
|
|
666
723
|
if (isTruthy(viewId)) {
|
|
667
|
-
acc.customRelation.navigateTo = viewId as c4.
|
|
724
|
+
acc.customRelation.navigateTo = viewId as c4.ViewId
|
|
668
725
|
}
|
|
669
726
|
return acc
|
|
670
727
|
}
|
|
@@ -803,7 +860,7 @@ export class LikeC4ModelParser {
|
|
|
803
860
|
}
|
|
804
861
|
}
|
|
805
862
|
|
|
806
|
-
private
|
|
863
|
+
private parseViewManualLayout(node: ast.LikeC4View): c4.ViewManualLayout | undefined {
|
|
807
864
|
const commentNode = CstUtils.findCommentNode(node.$cstNode, ['BLOCK_COMMENT'])
|
|
808
865
|
if (!commentNode || !hasManualLayout(commentNode.text)) {
|
|
809
866
|
return undefined
|
|
@@ -854,47 +911,53 @@ export class LikeC4ModelParser {
|
|
|
854
911
|
isBackward: true
|
|
855
912
|
}
|
|
856
913
|
}
|
|
857
|
-
if (
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
914
|
+
if (!isArray(node.custom?.props)) {
|
|
915
|
+
return step
|
|
916
|
+
}
|
|
917
|
+
for (const prop of node.custom.props) {
|
|
918
|
+
try {
|
|
919
|
+
switch (true) {
|
|
920
|
+
case ast.isRelationNavigateToProperty(prop): {
|
|
921
|
+
const viewId = prop.value.view.ref?.name
|
|
922
|
+
if (isTruthy(viewId)) {
|
|
923
|
+
step.navigateTo = viewId as c4.ViewId
|
|
924
|
+
}
|
|
925
|
+
break
|
|
926
|
+
}
|
|
927
|
+
case ast.isRelationStringProperty(prop):
|
|
928
|
+
case ast.isNotationProperty(prop):
|
|
929
|
+
case ast.isNotesProperty(prop): {
|
|
861
930
|
if (isDefined(prop.value)) {
|
|
862
931
|
step[prop.key] = removeIndent(prop.value) ?? ''
|
|
863
932
|
}
|
|
864
|
-
|
|
933
|
+
break
|
|
865
934
|
}
|
|
866
|
-
|
|
935
|
+
case ast.isArrowProperty(prop): {
|
|
867
936
|
if (isDefined(prop.value)) {
|
|
868
937
|
step[prop.key] = prop.value
|
|
869
938
|
}
|
|
870
|
-
|
|
939
|
+
break
|
|
871
940
|
}
|
|
872
|
-
|
|
941
|
+
case ast.isColorProperty(prop): {
|
|
873
942
|
const value = toColor(prop)
|
|
874
943
|
if (isDefined(value)) {
|
|
875
944
|
step[prop.key] = value
|
|
876
945
|
}
|
|
877
|
-
|
|
946
|
+
break
|
|
878
947
|
}
|
|
879
|
-
|
|
948
|
+
case ast.isLineProperty(prop): {
|
|
880
949
|
if (isDefined(prop.value)) {
|
|
881
950
|
step[prop.key] = prop.value
|
|
882
951
|
}
|
|
883
|
-
|
|
884
|
-
}
|
|
885
|
-
if (ast.isRelationNavigateToProperty(prop)) {
|
|
886
|
-
const viewId = prop.value.view.ref?.name
|
|
887
|
-
if (isTruthy(viewId)) {
|
|
888
|
-
step.navigateTo = viewId as c4.ViewID
|
|
889
|
-
}
|
|
890
|
-
continue
|
|
952
|
+
break
|
|
891
953
|
}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
catch (e) {
|
|
895
|
-
logWarnError(e)
|
|
954
|
+
default:
|
|
955
|
+
nonexhaustive(prop)
|
|
896
956
|
}
|
|
897
957
|
}
|
|
958
|
+
catch (e) {
|
|
959
|
+
logWarnError(e)
|
|
960
|
+
}
|
|
898
961
|
}
|
|
899
962
|
return step
|
|
900
963
|
}
|
|
@@ -925,7 +988,7 @@ export class LikeC4ModelParser {
|
|
|
925
988
|
getDocument(astNode).uri.toString(),
|
|
926
989
|
astPath,
|
|
927
990
|
viewOf ?? ''
|
|
928
|
-
) as c4.
|
|
991
|
+
) as c4.ViewId
|
|
929
992
|
}
|
|
930
993
|
|
|
931
994
|
const title = toSingleLine(body.props.find(p => p.key === 'title')?.value) ?? null
|
|
@@ -934,11 +997,11 @@ export class LikeC4ModelParser {
|
|
|
934
997
|
const tags = this.convertTags(body)
|
|
935
998
|
const links = this.convertLinks(body)
|
|
936
999
|
|
|
937
|
-
const manualLayout = this.
|
|
1000
|
+
const manualLayout = this.parseViewManualLayout(astNode)
|
|
938
1001
|
|
|
939
1002
|
const view: ParsedAstElementView = {
|
|
940
1003
|
__: 'element',
|
|
941
|
-
id: id as c4.
|
|
1004
|
+
id: id as c4.ViewId,
|
|
942
1005
|
astPath,
|
|
943
1006
|
title,
|
|
944
1007
|
description,
|
|
@@ -964,7 +1027,7 @@ export class LikeC4ModelParser {
|
|
|
964
1027
|
const extendsView = astNode.extends.view.ref
|
|
965
1028
|
invariant(extendsView?.name, 'view extends is not resolved: ' + astNode.$cstNode?.text)
|
|
966
1029
|
return Object.assign(view, {
|
|
967
|
-
extends: extendsView.name as c4.
|
|
1030
|
+
extends: extendsView.name as c4.ViewId
|
|
968
1031
|
})
|
|
969
1032
|
}
|
|
970
1033
|
|
|
@@ -987,7 +1050,7 @@ export class LikeC4ModelParser {
|
|
|
987
1050
|
id = 'dynamic_' + stringHash(
|
|
988
1051
|
getDocument(astNode).uri.toString(),
|
|
989
1052
|
astPath
|
|
990
|
-
) as c4.
|
|
1053
|
+
) as c4.ViewId
|
|
991
1054
|
}
|
|
992
1055
|
|
|
993
1056
|
const title = toSingleLine(props.find(p => p.key === 'title')?.value) ?? null
|
|
@@ -996,13 +1059,13 @@ export class LikeC4ModelParser {
|
|
|
996
1059
|
const tags = this.convertTags(body)
|
|
997
1060
|
const links = this.convertLinks(body)
|
|
998
1061
|
|
|
999
|
-
ViewOps.writeId(astNode, id as c4.
|
|
1062
|
+
ViewOps.writeId(astNode, id as c4.ViewId)
|
|
1000
1063
|
|
|
1001
|
-
const manualLayout = this.
|
|
1064
|
+
const manualLayout = this.parseViewManualLayout(astNode)
|
|
1002
1065
|
|
|
1003
1066
|
return {
|
|
1004
1067
|
__: 'dynamic',
|
|
1005
|
-
id: id as c4.
|
|
1068
|
+
id: id as c4.ViewId,
|
|
1006
1069
|
astPath,
|
|
1007
1070
|
title,
|
|
1008
1071
|
description,
|
|
@@ -1061,7 +1124,7 @@ export class LikeC4ModelParser {
|
|
|
1061
1124
|
let iter: ast.DynamicViewPredicateIterator | undefined = astRule.predicates
|
|
1062
1125
|
while (iter) {
|
|
1063
1126
|
try {
|
|
1064
|
-
if (isValid(iter.value as any)) {
|
|
1127
|
+
if (isNonNullish(iter.value) && isValid(iter.value as any)) {
|
|
1065
1128
|
const c4expr = this.parseElementPredicate(iter.value, isValid)
|
|
1066
1129
|
include.unshift(c4expr)
|
|
1067
1130
|
}
|
|
@@ -1073,7 +1136,406 @@ export class LikeC4ModelParser {
|
|
|
1073
1136
|
return { include }
|
|
1074
1137
|
}
|
|
1075
1138
|
|
|
1076
|
-
|
|
1139
|
+
private parseDeployment(doc: ParsedLikeC4LangiumDocument, isValid: IsValidFn) {
|
|
1140
|
+
type TraversePair = ast.DeployedInstance | ast.DeploymentNode | ast.DeploymentRelation
|
|
1141
|
+
const traverseStack: TraversePair[] = doc.parseResult.value.deployments.flatMap(d => d.elements)
|
|
1142
|
+
|
|
1143
|
+
let next: TraversePair | undefined
|
|
1144
|
+
while ((next = traverseStack.shift())) {
|
|
1145
|
+
if (ast.isDeploymentRelation(next)) {
|
|
1146
|
+
doc.c4DeploymentRelations.push(this.parseDeploymentRelation(next, isValid))
|
|
1147
|
+
continue
|
|
1148
|
+
}
|
|
1149
|
+
if (!isValid(next)) {
|
|
1150
|
+
continue
|
|
1151
|
+
}
|
|
1152
|
+
try {
|
|
1153
|
+
switch (true) {
|
|
1154
|
+
case ast.isDeployedInstance(next):
|
|
1155
|
+
doc.c4Deployments.push(this.parseDeployedInstance(next, isValid))
|
|
1156
|
+
break
|
|
1157
|
+
case ast.isDeploymentNode(next): {
|
|
1158
|
+
doc.c4Deployments.push(this.parseDeploymentNode(next, isValid))
|
|
1159
|
+
if (next.body && next.body.elements.length > 0) {
|
|
1160
|
+
traverseStack.push(...next.body.elements)
|
|
1161
|
+
}
|
|
1162
|
+
break
|
|
1163
|
+
}
|
|
1164
|
+
default:
|
|
1165
|
+
nonexhaustive(next)
|
|
1166
|
+
}
|
|
1167
|
+
} catch (e) {
|
|
1168
|
+
logWarnError(e)
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
public parseDeploymentNode(
|
|
1174
|
+
astNode: ast.DeploymentNode,
|
|
1175
|
+
isValid: IsValidFn
|
|
1176
|
+
): ParsedAstDeployment.Node {
|
|
1177
|
+
const id = this.resolveFqn(astNode)
|
|
1178
|
+
const kind = nonNullable(astNode.kind.ref, 'DeploymentKind not resolved').name as c4.DeploymentNodeKind
|
|
1179
|
+
const tags = this.convertTags(astNode.body)
|
|
1180
|
+
const stylePropsAst = astNode.body?.props.find(ast.isElementStyleProperty)?.props
|
|
1181
|
+
const style = toElementStyle(stylePropsAst, isValid)
|
|
1182
|
+
const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty))
|
|
1183
|
+
|
|
1184
|
+
const bodyProps = pipe(
|
|
1185
|
+
astNode.body?.props ?? [],
|
|
1186
|
+
filter(isValid),
|
|
1187
|
+
filter(ast.isElementStringProperty),
|
|
1188
|
+
mapToObj(p => [p.key, p.value || undefined])
|
|
1189
|
+
)
|
|
1190
|
+
|
|
1191
|
+
const title = removeIndent(astNode.title ?? bodyProps.title)
|
|
1192
|
+
const description = removeIndent(bodyProps.description)
|
|
1193
|
+
const technology = toSingleLine(bodyProps.technology)
|
|
1194
|
+
|
|
1195
|
+
const links = this.convertLinks(astNode.body)
|
|
1196
|
+
|
|
1197
|
+
// Property has higher priority than from style
|
|
1198
|
+
const iconProp = astNode.body?.props.find(ast.isIconProperty)
|
|
1199
|
+
if (iconProp && isValid(iconProp)) {
|
|
1200
|
+
const value = iconProp.libicon?.ref?.name ?? iconProp.value
|
|
1201
|
+
if (isTruthy(value)) {
|
|
1202
|
+
style.icon = value as c4.IconUrl
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
return {
|
|
1207
|
+
id,
|
|
1208
|
+
kind,
|
|
1209
|
+
title: title ?? nameFromFqn(id),
|
|
1210
|
+
...(metadata && { metadata }),
|
|
1211
|
+
...(tags && { tags }),
|
|
1212
|
+
...(links && isNonEmptyArray(links) && { links }),
|
|
1213
|
+
...(isTruthy(technology) && { technology }),
|
|
1214
|
+
...(isTruthy(description) && { description }),
|
|
1215
|
+
style
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
public parseDeployedInstance(
|
|
1220
|
+
astNode: ast.DeployedInstance,
|
|
1221
|
+
isValid: IsValidFn
|
|
1222
|
+
): ParsedAstDeployment.Instance {
|
|
1223
|
+
const id = this.resolveFqn(astNode)
|
|
1224
|
+
const element = this.resolveFqn(nonNullable(elementRef(astNode.element), 'DeployedInstance element not found'))
|
|
1225
|
+
|
|
1226
|
+
const tags = this.convertTags(astNode.body)
|
|
1227
|
+
const stylePropsAst = astNode.body?.props.find(ast.isElementStyleProperty)?.props
|
|
1228
|
+
const style = toElementStyle(stylePropsAst, isValid)
|
|
1229
|
+
const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty))
|
|
1230
|
+
|
|
1231
|
+
const bodyProps = pipe(
|
|
1232
|
+
astNode.body?.props ?? [],
|
|
1233
|
+
filter(isValid),
|
|
1234
|
+
filter(ast.isElementStringProperty),
|
|
1235
|
+
mapToObj(p => [p.key, p.value || undefined])
|
|
1236
|
+
)
|
|
1237
|
+
|
|
1238
|
+
const title = removeIndent(astNode.title ?? bodyProps.title)
|
|
1239
|
+
const description = removeIndent(bodyProps.description)
|
|
1240
|
+
const technology = toSingleLine(bodyProps.technology)
|
|
1241
|
+
|
|
1242
|
+
const links = this.convertLinks(astNode.body)
|
|
1243
|
+
|
|
1244
|
+
// Property has higher priority than from style
|
|
1245
|
+
const iconProp = astNode.body?.props.find(ast.isIconProperty)
|
|
1246
|
+
if (iconProp && isValid(iconProp)) {
|
|
1247
|
+
const value = iconProp.libicon?.ref?.name ?? iconProp.value
|
|
1248
|
+
if (isTruthy(value)) {
|
|
1249
|
+
style.icon = value as c4.IconUrl
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
return {
|
|
1254
|
+
id,
|
|
1255
|
+
element,
|
|
1256
|
+
...(metadata && { metadata }),
|
|
1257
|
+
...(title && { title }),
|
|
1258
|
+
...(tags && { tags }),
|
|
1259
|
+
...(links && isNonEmptyArray(links) && { links }),
|
|
1260
|
+
...(isTruthy(technology) && { technology }),
|
|
1261
|
+
...(isTruthy(description) && { description }),
|
|
1262
|
+
style
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
public parseDeploymentRelation(
|
|
1267
|
+
astNode: ast.DeploymentRelation,
|
|
1268
|
+
isValid: IsValidFn
|
|
1269
|
+
): ParsedAstDeploymentRelation {
|
|
1270
|
+
const astPath = this.getAstNodePath(astNode)
|
|
1271
|
+
const source = this.parseDeploymentDef(astNode.source)
|
|
1272
|
+
const target = this.parseDeploymentDef(astNode.target)
|
|
1273
|
+
|
|
1274
|
+
const tags = this.convertTags(astNode) ?? this.convertTags(astNode.body)
|
|
1275
|
+
const links = this.convertLinks(astNode.body)
|
|
1276
|
+
const kind = astNode.kind?.ref?.name as (c4.RelationshipKind | undefined)
|
|
1277
|
+
const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty))
|
|
1278
|
+
|
|
1279
|
+
const bodyProps = mapToObj(
|
|
1280
|
+
astNode.body?.props.filter(ast.isRelationStringProperty) ?? [],
|
|
1281
|
+
p => [p.key, p.value as string | undefined]
|
|
1282
|
+
)
|
|
1283
|
+
|
|
1284
|
+
const navigateTo = pipe(
|
|
1285
|
+
astNode.body?.props ?? [],
|
|
1286
|
+
filter(ast.isRelationNavigateToProperty),
|
|
1287
|
+
map(p => p.value.view.ref?.name),
|
|
1288
|
+
filter(isTruthy),
|
|
1289
|
+
first()
|
|
1290
|
+
)
|
|
1291
|
+
|
|
1292
|
+
const title = removeIndent(astNode.title ?? bodyProps.title)
|
|
1293
|
+
const description = removeIndent(bodyProps.description)
|
|
1294
|
+
const technology = toSingleLine(astNode.technology) ?? removeIndent(bodyProps.technology)
|
|
1295
|
+
|
|
1296
|
+
const styleProp = astNode.body?.props.find(ast.isRelationStyleProperty)
|
|
1297
|
+
|
|
1298
|
+
const id = stringHash(
|
|
1299
|
+
'deployment',
|
|
1300
|
+
astPath,
|
|
1301
|
+
source.id,
|
|
1302
|
+
target.id
|
|
1303
|
+
) as c4.RelationId
|
|
1304
|
+
|
|
1305
|
+
return {
|
|
1306
|
+
id,
|
|
1307
|
+
source,
|
|
1308
|
+
target,
|
|
1309
|
+
...title && { title },
|
|
1310
|
+
...(metadata && { metadata }),
|
|
1311
|
+
...(isTruthy(technology) && { technology }),
|
|
1312
|
+
...(isTruthy(description) && { description }),
|
|
1313
|
+
...(kind && { kind }),
|
|
1314
|
+
...(tags && { tags }),
|
|
1315
|
+
...(isNonEmptyArray(links) && { links }),
|
|
1316
|
+
...toRelationshipStyleExcludeDefaults(styleProp?.props, isValid),
|
|
1317
|
+
...(navigateTo && { navigateTo: navigateTo as c4.ViewId }),
|
|
1318
|
+
astPath
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
private parseDeploymentView(
|
|
1323
|
+
astNode: ast.DeploymentView,
|
|
1324
|
+
isValid: IsValidFn
|
|
1325
|
+
): ParsedAstDeploymentView {
|
|
1326
|
+
const body = astNode.body
|
|
1327
|
+
invariant(body, 'DynamicElementView body is not defined')
|
|
1328
|
+
// only valid props
|
|
1329
|
+
const props = body.props.filter(isValid)
|
|
1330
|
+
const astPath = this.getAstNodePath(astNode)
|
|
1331
|
+
|
|
1332
|
+
let id = astNode.name
|
|
1333
|
+
if (!id) {
|
|
1334
|
+
id = 'deployment_' + stringHash(
|
|
1335
|
+
getDocument(astNode).uri.toString(),
|
|
1336
|
+
astPath
|
|
1337
|
+
) as c4.ViewId
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
const title = toSingleLine(props.find(p => p.key === 'title')?.value) ?? null
|
|
1341
|
+
const description = removeIndent(props.find(p => p.key === 'description')?.value) ?? null
|
|
1342
|
+
|
|
1343
|
+
const tags = this.convertTags(body)
|
|
1344
|
+
const links = this.convertLinks(body)
|
|
1345
|
+
|
|
1346
|
+
ViewOps.writeId(astNode, id as c4.ViewId)
|
|
1347
|
+
|
|
1348
|
+
const manualLayout = this.parseViewManualLayout(astNode)
|
|
1349
|
+
|
|
1350
|
+
return {
|
|
1351
|
+
__: 'deployment',
|
|
1352
|
+
id: id as c4.ViewId,
|
|
1353
|
+
astPath,
|
|
1354
|
+
title,
|
|
1355
|
+
description,
|
|
1356
|
+
tags,
|
|
1357
|
+
links: isNonEmptyArray(links) ? links : null,
|
|
1358
|
+
rules: body.rules.flatMap(n => {
|
|
1359
|
+
try {
|
|
1360
|
+
return isValid(n) ? this.parseDeploymentViewRule(n, isValid) : []
|
|
1361
|
+
} catch (e) {
|
|
1362
|
+
logWarnError(e)
|
|
1363
|
+
return []
|
|
1364
|
+
}
|
|
1365
|
+
}),
|
|
1366
|
+
...(manualLayout && { manualLayout })
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
private parseDeploymentViewRule(astRule: ast.DeploymentViewRule, isValid: IsValidFn): c4.DeploymentViewRule {
|
|
1371
|
+
if (ast.isDeploymentViewRulePredicate(astRule)) {
|
|
1372
|
+
return this.parseDeploymentViewRulePredicate(astRule, isValid)
|
|
1373
|
+
}
|
|
1374
|
+
if (ast.isViewRuleAutoLayout(astRule)) {
|
|
1375
|
+
return toAutoLayout(astRule)
|
|
1376
|
+
}
|
|
1377
|
+
if (ast.isDeploymentViewRuleStyle(astRule)) {
|
|
1378
|
+
return this.parseDeploymentViewRuleStyle(astRule, isValid)
|
|
1379
|
+
}
|
|
1380
|
+
nonexhaustive(astRule)
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
private parseDeploymentViewRulePredicate(
|
|
1384
|
+
astRule: ast.DeploymentViewRulePredicate,
|
|
1385
|
+
isValid: IsValidFn
|
|
1386
|
+
): c4.DeploymentViewRulePredicate {
|
|
1387
|
+
const exprs = [] as c4.DeploymentExpression[]
|
|
1388
|
+
let iterator: ast.DeploymentViewRulePredicateExpression | undefined = astRule.expr
|
|
1389
|
+
while (iterator) {
|
|
1390
|
+
try {
|
|
1391
|
+
const expr = iterator.value
|
|
1392
|
+
if (isNonNullish(expr) && isValid(expr)) {
|
|
1393
|
+
switch (true) {
|
|
1394
|
+
case ast.isDeploymentElementExpression(expr):
|
|
1395
|
+
exprs.unshift(this.parseDeploymentElementExpression(expr))
|
|
1396
|
+
break
|
|
1397
|
+
case ast.isDeploymentRelationExpression(expr):
|
|
1398
|
+
exprs.unshift(this.parseDeploymentRelationExpression(expr))
|
|
1399
|
+
break
|
|
1400
|
+
default:
|
|
1401
|
+
nonexhaustive(expr)
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
} catch (e) {
|
|
1405
|
+
logWarnError(e)
|
|
1406
|
+
}
|
|
1407
|
+
iterator = iterator.prev
|
|
1408
|
+
}
|
|
1409
|
+
return astRule.isInclude ? { include: exprs } : { exclude: exprs }
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
private parseDeploymentElementExpression(astNode: ast.DeploymentElementExpression): c4.DeploymentExpression {
|
|
1413
|
+
if (ast.isWildcardExpression(astNode)) {
|
|
1414
|
+
return {
|
|
1415
|
+
wildcard: true
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
if (ast.isDeploymentRefExpression(astNode)) {
|
|
1419
|
+
const ref = this.parseDeploymentDef(astNode.ref)
|
|
1420
|
+
switch (true) {
|
|
1421
|
+
case astNode.selector === '._':
|
|
1422
|
+
return {
|
|
1423
|
+
ref,
|
|
1424
|
+
selector: 'expanded'
|
|
1425
|
+
}
|
|
1426
|
+
case astNode.selector === '.**':
|
|
1427
|
+
return {
|
|
1428
|
+
ref,
|
|
1429
|
+
selector: 'descendants'
|
|
1430
|
+
}
|
|
1431
|
+
case astNode.selector === '.*':
|
|
1432
|
+
return {
|
|
1433
|
+
ref,
|
|
1434
|
+
selector: 'children'
|
|
1435
|
+
}
|
|
1436
|
+
default:
|
|
1437
|
+
return { ref }
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
nonexhaustive(astNode)
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
private parseDeploymentRelationExpression(
|
|
1444
|
+
astNode: ast.DeploymentRelationExpression
|
|
1445
|
+
): c4.DeploymentRelationExpression {
|
|
1446
|
+
if (ast.isDirectedDeploymentRelationExpression(astNode)) {
|
|
1447
|
+
return {
|
|
1448
|
+
source: this.parseDeploymentElementExpression(astNode.source.from),
|
|
1449
|
+
target: this.parseDeploymentElementExpression(astNode.target),
|
|
1450
|
+
isBidirectional: astNode.source.isBidirectional
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
if (ast.isInOutDeploymentRelationExpression(astNode)) {
|
|
1454
|
+
return {
|
|
1455
|
+
inout: this.parseDeploymentElementExpression(astNode.inout.to)
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
if (ast.isOutgoingDeploymentRelationExpression(astNode)) {
|
|
1459
|
+
return {
|
|
1460
|
+
outgoing: this.parseDeploymentElementExpression(astNode.from)
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
if (ast.isIncomingDeploymentRelationExpression(astNode)) {
|
|
1464
|
+
return {
|
|
1465
|
+
incoming: this.parseDeploymentElementExpression(astNode.to)
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
nonexhaustive(astNode)
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
private parseDeploymentExpressionIterator(
|
|
1472
|
+
astNode: ast.DeploymentExpressionIterator,
|
|
1473
|
+
isValid: IsValidFn
|
|
1474
|
+
): c4.DeploymentExpression[] {
|
|
1475
|
+
const exprs = [] as c4.DeploymentExpression[]
|
|
1476
|
+
let iter: ast.DeploymentExpressionIterator['prev'] = astNode
|
|
1477
|
+
while (iter) {
|
|
1478
|
+
try {
|
|
1479
|
+
if (isNonNullish(iter.value) && isValid(iter.value)) {
|
|
1480
|
+
exprs.unshift(this.parseDeploymentElementExpression(iter.value))
|
|
1481
|
+
}
|
|
1482
|
+
} catch (e) {
|
|
1483
|
+
logWarnError(e)
|
|
1484
|
+
}
|
|
1485
|
+
iter = iter.prev
|
|
1486
|
+
}
|
|
1487
|
+
return exprs
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
private parseDeploymentDef(astNode: ast.DeploymentRef): c4.DeploymentRef {
|
|
1491
|
+
const refValue = nonNullable(
|
|
1492
|
+
astNode.value.ref,
|
|
1493
|
+
`Deployment ref is empty ${astNode.$cstNode?.range.start.line}:${astNode.$cstNode?.range.start.character}`
|
|
1494
|
+
)
|
|
1495
|
+
if (ast.isDeploymentNode(refValue)) {
|
|
1496
|
+
return {
|
|
1497
|
+
id: this.resolveFqn(refValue)
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
const deployedInstanceAst = nonNullable(instanceRef(astNode), 'Instance ref not found')
|
|
1501
|
+
const id = this.resolveFqn(deployedInstanceAst)
|
|
1502
|
+
|
|
1503
|
+
if (ast.isElement(refValue)) {
|
|
1504
|
+
const element = this.resolveFqn(refValue)
|
|
1505
|
+
return {
|
|
1506
|
+
id,
|
|
1507
|
+
element
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
// To ensure with compiler we processed all types
|
|
1512
|
+
invariant(ast.isDeployedInstance(refValue), 'Invalid deployment ref: ' + this.getAstNodePath(astNode))
|
|
1513
|
+
return {
|
|
1514
|
+
id
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
protected parseDeploymentViewRuleStyle(
|
|
1519
|
+
astRule: ast.DeploymentViewRuleStyle,
|
|
1520
|
+
isValid: IsValidFn
|
|
1521
|
+
): c4.DeploymentViewRuleStyle {
|
|
1522
|
+
const styleProps = astRule.props.filter(ast.isStyleProperty)
|
|
1523
|
+
const notationProperty = astRule.props.find(ast.isNotationProperty)
|
|
1524
|
+
const notation = removeIndent(notationProperty?.value)
|
|
1525
|
+
const targets = this.parseDeploymentExpressionIterator(astRule.target, isValid)
|
|
1526
|
+
return {
|
|
1527
|
+
targets,
|
|
1528
|
+
...(notation && { notation }),
|
|
1529
|
+
style: {
|
|
1530
|
+
...toElementStyle(styleProps, isValid)
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
protected resolveFqn(node: ast.FqnReferenceable): c4.Fqn {
|
|
1536
|
+
if (ast.isDeploymentElement(node)) {
|
|
1537
|
+
return this.services.likec4.DeploymentsIndex.getFqnName(node)
|
|
1538
|
+
}
|
|
1077
1539
|
if (ast.isExtendElement(node)) {
|
|
1078
1540
|
return getFqnElementRef(node.element)
|
|
1079
1541
|
}
|
|
@@ -1088,7 +1550,7 @@ export class LikeC4ModelParser {
|
|
|
1088
1550
|
|
|
1089
1551
|
private getMetadata(metadataAstNode: ast.MetadataProperty | undefined): { [key: string]: string } | undefined {
|
|
1090
1552
|
return metadataAstNode?.props != null
|
|
1091
|
-
? mapToObj(metadataAstNode.props, (p) => [p.key, removeIndent(p.value)])
|
|
1553
|
+
? mapToObj(metadataAstNode.props, (p) => [p.key, removeIndent(p.value)] as [string, string])
|
|
1092
1554
|
: undefined
|
|
1093
1555
|
}
|
|
1094
1556
|
|