@likec4/language-server 1.21.1 → 1.22.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 (113) hide show
  1. package/README.md +4 -1
  2. package/bin/likec4-language-server.mjs +5 -2
  3. package/dist/LikeC4FileSystem.js +2 -2
  4. package/dist/browser.d.ts +3 -3
  5. package/dist/browser.js +17 -2
  6. package/dist/bundled.d.ts +8 -0
  7. package/dist/bundled.js +25 -0
  8. package/dist/bundled.mjs +2555 -4022
  9. package/dist/index.d.ts +3 -3
  10. package/dist/index.js +23 -2
  11. package/dist/logger.d.ts +9 -3
  12. package/dist/logger.js +35 -55
  13. package/dist/model/fqn-computation.js +2 -2
  14. package/dist/model/model-builder.js +13 -14
  15. package/dist/model-change/ModelChanges.js +2 -2
  16. package/dist/module.js +1 -4
  17. package/dist/references/scope-provider.js +3 -3
  18. package/dist/view-utils/manual-layout.js +2 -2
  19. package/dist/views/configurable-layouter.js +4 -4
  20. package/dist/views/likec4-views.d.ts +2 -1
  21. package/dist/views/likec4-views.js +2 -2
  22. package/package.json +14 -12
  23. package/dist/test/setup.d.ts +0 -1
  24. package/dist/test/setup.js +0 -7
  25. package/src/LikeC4FileSystem.ts +0 -38
  26. package/src/Rpc.ts +0 -134
  27. package/src/ast.ts +0 -556
  28. package/src/browser.ts +0 -35
  29. package/src/documentation/documentation-provider.ts +0 -52
  30. package/src/documentation/index.ts +0 -1
  31. package/src/formatting/LikeC4Formatter.ts +0 -639
  32. package/src/formatting/utils.ts +0 -26
  33. package/src/generated/ast.ts +0 -3735
  34. package/src/generated/grammar.ts +0 -10
  35. package/src/generated/module.ts +0 -33
  36. package/src/generated-lib/icons.ts +0 -1538
  37. package/src/index.ts +0 -30
  38. package/src/like-c4.langium +0 -901
  39. package/src/likec4lib.ts +0 -6
  40. package/src/logger.ts +0 -80
  41. package/src/lsp/CodeLensProvider.ts +0 -50
  42. package/src/lsp/CompletionProvider.ts +0 -147
  43. package/src/lsp/DocumentHighlightProvider.ts +0 -12
  44. package/src/lsp/DocumentLinkProvider.ts +0 -65
  45. package/src/lsp/DocumentSymbolProvider.ts +0 -313
  46. package/src/lsp/HoverProvider.ts +0 -92
  47. package/src/lsp/RenameProvider.ts +0 -8
  48. package/src/lsp/SemanticTokenProvider.ts +0 -383
  49. package/src/lsp/index.ts +0 -8
  50. package/src/model/deployments-index.ts +0 -209
  51. package/src/model/fqn-computation.ts +0 -83
  52. package/src/model/fqn-index.ts +0 -138
  53. package/src/model/index.ts +0 -6
  54. package/src/model/model-builder.ts +0 -724
  55. package/src/model/model-locator.ts +0 -146
  56. package/src/model/model-parser-where.ts +0 -84
  57. package/src/model/model-parser.ts +0 -86
  58. package/src/model/parser/Base.ts +0 -113
  59. package/src/model/parser/DeploymentModelParser.ts +0 -192
  60. package/src/model/parser/DeploymentViewParser.ts +0 -122
  61. package/src/model/parser/FqnRefParser.ts +0 -143
  62. package/src/model/parser/GlobalsParser.ts +0 -96
  63. package/src/model/parser/ModelParser.ts +0 -170
  64. package/src/model/parser/PredicatesParser.ts +0 -315
  65. package/src/model/parser/SpecificationParser.ts +0 -133
  66. package/src/model/parser/ViewsParser.ts +0 -428
  67. package/src/model-change/ModelChanges.ts +0 -101
  68. package/src/model-change/changeElementStyle.ts +0 -172
  69. package/src/model-change/changeViewLayout.ts +0 -47
  70. package/src/model-change/saveManualLayout.ts +0 -41
  71. package/src/module.ts +0 -255
  72. package/src/protocol.ts +0 -93
  73. package/src/references/index.ts +0 -3
  74. package/src/references/name-provider.ts +0 -37
  75. package/src/references/scope-computation.ts +0 -364
  76. package/src/references/scope-provider.ts +0 -201
  77. package/src/shared/NodeKindProvider.ts +0 -121
  78. package/src/shared/WorkspaceManager.ts +0 -48
  79. package/src/shared/WorkspaceSymbolProvider.ts +0 -3
  80. package/src/shared/index.ts +0 -3
  81. package/src/test/index.ts +0 -1
  82. package/src/test/setup.ts +0 -8
  83. package/src/test/testServices.ts +0 -152
  84. package/src/utils/disposable.ts +0 -30
  85. package/src/utils/elementRef.ts +0 -26
  86. package/src/utils/fqnRef.ts +0 -56
  87. package/src/utils/index.ts +0 -2
  88. package/src/utils/printDocs.ts +0 -3
  89. package/src/utils/stringHash.ts +0 -6
  90. package/src/validation/_shared.ts +0 -29
  91. package/src/validation/deployment-checks.ts +0 -131
  92. package/src/validation/dynamic-view-rule.ts +0 -23
  93. package/src/validation/dynamic-view-step.ts +0 -36
  94. package/src/validation/element.ts +0 -56
  95. package/src/validation/index.ts +0 -171
  96. package/src/validation/property-checks.ts +0 -52
  97. package/src/validation/relation.ts +0 -63
  98. package/src/validation/specification.ts +0 -205
  99. package/src/validation/view-predicates/element-with.ts +0 -36
  100. package/src/validation/view-predicates/expanded-element.ts +0 -16
  101. package/src/validation/view-predicates/expression-v2.ts +0 -101
  102. package/src/validation/view-predicates/incoming.ts +0 -20
  103. package/src/validation/view-predicates/index.ts +0 -6
  104. package/src/validation/view-predicates/outgoing.ts +0 -20
  105. package/src/validation/view-predicates/relation-with.ts +0 -17
  106. package/src/validation/view.ts +0 -37
  107. package/src/view-utils/assignNavigateTo.ts +0 -31
  108. package/src/view-utils/index.ts +0 -2
  109. package/src/view-utils/manual-layout.ts +0 -116
  110. package/src/view-utils/resolve-relative-paths.ts +0 -90
  111. package/src/views/configurable-layouter.ts +0 -65
  112. package/src/views/index.ts +0 -1
  113. package/src/views/likec4-views.ts +0 -139
@@ -1,122 +0,0 @@
1
- import type * as c4 from '@likec4/core'
2
- import { invariant, isNonEmptyArray, nonexhaustive } from '@likec4/core'
3
- import { isNonNullish } from 'remeda'
4
- import { type ParsedAstDeploymentView, ast, toAutoLayout, toElementStyle, ViewOps } from '../../ast'
5
- import { logWarnError } from '../../logger'
6
- import { stringHash } from '../../utils'
7
- import { parseViewManualLayout } from '../../view-utils/manual-layout'
8
- import { removeIndent, toSingleLine } from './Base'
9
- import type { WithDeploymentModel } from './DeploymentModelParser'
10
- import type { WithExpressionV2 } from './FqnRefParser'
11
-
12
- export type WithDeploymentView = ReturnType<typeof DeploymentViewParser>
13
-
14
- export function DeploymentViewParser<TBase extends WithExpressionV2 & WithDeploymentModel>(B: TBase) {
15
- return class DeploymentViewParser extends B {
16
- parseDeploymentView(
17
- astNode: ast.DeploymentView,
18
- ): ParsedAstDeploymentView {
19
- const body = astNode.body
20
- invariant(body, 'DynamicElementView body is not defined')
21
- // only valid props
22
- const props = body.props.filter(this.isValid)
23
- const astPath = this.getAstNodePath(astNode)
24
-
25
- let id = astNode.name
26
- if (!id) {
27
- id = 'deployment_' + stringHash(
28
- this.doc.uri.toString(),
29
- astPath,
30
- ) as c4.ViewId
31
- }
32
-
33
- const title = toSingleLine(props.find(p => p.key === 'title')?.value) ?? null
34
- const description = removeIndent(props.find(p => p.key === 'description')?.value) ?? null
35
-
36
- const tags = this.convertTags(body)
37
- const links = this.convertLinks(body)
38
-
39
- ViewOps.writeId(astNode, id as c4.ViewId)
40
-
41
- const manualLayout = parseViewManualLayout(astNode)
42
-
43
- return {
44
- __: 'deployment',
45
- id: id as c4.ViewId,
46
- astPath,
47
- title,
48
- description,
49
- tags,
50
- links: isNonEmptyArray(links) ? links : null,
51
- rules: body.rules.flatMap(n => {
52
- try {
53
- return this.isValid(n) ? this.parseDeploymentViewRule(n) : []
54
- } catch (e) {
55
- logWarnError(e)
56
- return []
57
- }
58
- }),
59
- ...(manualLayout && { manualLayout }),
60
- }
61
- }
62
-
63
- parseDeploymentViewRule(astRule: ast.DeploymentViewRule): c4.DeploymentViewRule {
64
- if (ast.isDeploymentViewRulePredicate(astRule)) {
65
- return this.parseDeploymentViewRulePredicate(astRule)
66
- }
67
- if (ast.isViewRuleAutoLayout(astRule)) {
68
- return toAutoLayout(astRule)
69
- }
70
- if (ast.isDeploymentViewRuleStyle(astRule)) {
71
- return this.parseDeploymentViewRuleStyle(astRule)
72
- }
73
- nonexhaustive(astRule)
74
- }
75
-
76
- parseDeploymentViewRulePredicate(astRule: ast.DeploymentViewRulePredicate): c4.DeploymentViewRulePredicate {
77
- const exprs = [] as c4.ExpressionV2[]
78
- let iterator: ast.DeploymentViewRulePredicateExpression | undefined = astRule.expr
79
- while (iterator) {
80
- try {
81
- const expr = iterator.value
82
- if (isNonNullish(expr) && this.isValid(expr)) {
83
- switch (true) {
84
- case ast.isFqnExpr(expr):
85
- exprs.unshift(this.parseFqnExpr(expr))
86
- break
87
- case ast.isElementPredicateWhereV2(expr):
88
- exprs.unshift(this.parseElementWhereExpr(expr))
89
- break
90
- case ast.isRelationExpr(expr):
91
- exprs.unshift(this.parseRelationExpr(expr))
92
- break
93
- case ast.isRelationPredicateWhereV2(expr):
94
- exprs.unshift(this.parseRelationWhereExpr(expr))
95
- break
96
- default:
97
- nonexhaustive(expr)
98
- }
99
- }
100
- } catch (e) {
101
- logWarnError(e)
102
- }
103
- iterator = iterator.prev
104
- }
105
- return astRule.isInclude ? { include: exprs } : { exclude: exprs }
106
- }
107
-
108
- parseDeploymentViewRuleStyle(astRule: ast.DeploymentViewRuleStyle): c4.DeploymentViewRuleStyle {
109
- const styleProps = astRule.props.filter(ast.isStyleProperty)
110
- const notationProperty = astRule.props.find(ast.isNotationProperty)
111
- const notation = removeIndent(notationProperty?.value)
112
- const targets = this.parseFqnExpressions(astRule.targets)
113
- return {
114
- targets,
115
- ...(notation && { notation }),
116
- style: {
117
- ...toElementStyle(styleProps, this.isValid),
118
- },
119
- }
120
- }
121
- }
122
- }
@@ -1,143 +0,0 @@
1
- import type * as c4 from '@likec4/core'
2
- import { nonexhaustive, nonNullable } from '@likec4/core'
3
- import { isNonNullish } from 'remeda'
4
- import { ast } from '../../ast'
5
- import { logWarnError } from '../../logger'
6
- import { instanceRef } from '../../utils/fqnRef'
7
- import { parseWhereClause } from '../model-parser-where'
8
- import type { Base } from './Base'
9
-
10
- export type WithExpressionV2 = ReturnType<typeof ExpressionV2Parser>
11
-
12
- export function ExpressionV2Parser<TBase extends Base>(B: TBase) {
13
- return class ExpressionV2Parser extends B {
14
- parseFqnRef(astNode: ast.FqnRef): c4.FqnRef {
15
- const refValue = nonNullable(
16
- astNode.value.ref,
17
- `FqnRef is empty ${astNode.$cstNode?.range.start.line}:${astNode.$cstNode?.range.start.character}`,
18
- )
19
- if (ast.isElement(refValue)) {
20
- const deployedInstanceAst = instanceRef(astNode)
21
- if (!deployedInstanceAst) {
22
- return {
23
- model: this.resolveFqn(refValue),
24
- }
25
- }
26
- const deployment = this.resolveFqn(deployedInstanceAst)
27
- const element = this.resolveFqn(refValue)
28
- return {
29
- deployment,
30
- element,
31
- }
32
- }
33
-
34
- if (ast.isDeploymentElement(refValue)) {
35
- return {
36
- deployment: this.resolveFqn(refValue),
37
- }
38
- }
39
- nonexhaustive(refValue)
40
- }
41
-
42
- parseFqnExpr(astNode: ast.FqnExpr): c4.FqnExpr {
43
- if (ast.isWildcardExpression(astNode)) {
44
- return {
45
- wildcard: true,
46
- }
47
- }
48
- if (ast.isFqnRefExpr(astNode)) {
49
- return this.parseFqnRefExpr(astNode)
50
- }
51
- nonexhaustive(astNode)
52
- }
53
-
54
- parseFqnRefExpr(astNode: ast.FqnRefExpr): c4.FqnExpr.NonWildcard {
55
- const ref = this.parseFqnRef(astNode.ref)
56
- switch (true) {
57
- case astNode.selector === '._':
58
- return {
59
- ref,
60
- selector: 'expanded',
61
- } as c4.FqnExpr.NonWildcard
62
- case astNode.selector === '.**':
63
- return {
64
- ref,
65
- selector: 'descendants',
66
- } as c4.FqnExpr.NonWildcard
67
- case astNode.selector === '.*':
68
- return {
69
- ref,
70
- selector: 'children',
71
- } as c4.FqnExpr.NonWildcard
72
- default:
73
- return { ref } as c4.FqnExpr.NonWildcard
74
- }
75
- }
76
-
77
- parseElementWhereExpr(astNode: ast.ElementPredicateWhereV2): c4.RelationExpr {
78
- return {
79
- where: {
80
- expr: this.parseFqnExpr(astNode.subject as ast.FqnExpr),
81
- condition: astNode.where ? parseWhereClause(astNode.where) : {
82
- kind: { neq: '--always-true--' },
83
- },
84
- },
85
- }
86
- }
87
-
88
- parseFqnExpressions(astNode: ast.FqnExpressions): c4.FqnExpr[] {
89
- const exprs = [] as c4.FqnExpr[]
90
- let iter: ast.FqnExpressions['prev'] = astNode
91
- while (iter) {
92
- try {
93
- if (isNonNullish(iter.value) && this.isValid(iter.value)) {
94
- exprs.push(this.parseFqnExpr(iter.value))
95
- }
96
- } catch (e) {
97
- logWarnError(e)
98
- }
99
- iter = iter.prev
100
- }
101
- return exprs.reverse()
102
- }
103
-
104
- parseRelationWhereExpr(astNode: ast.RelationPredicateWhereV2): c4.RelationExpr {
105
- return {
106
- where: {
107
- expr: this.parseRelationExpr(astNode.subject as ast.RelationExpr),
108
- condition: astNode.where ? parseWhereClause(astNode.where) : {
109
- kind: { neq: '--always-true--' },
110
- },
111
- },
112
- }
113
- }
114
-
115
- parseRelationExpr(astNode: ast.RelationExpr): c4.RelationExpr {
116
- if (ast.isRelationPredicateWhere(astNode)) {
117
- }
118
- if (ast.isDirectedRelationExpr(astNode)) {
119
- return {
120
- source: this.parseFqnExpr(astNode.source.from),
121
- target: this.parseFqnExpr(astNode.target),
122
- isBidirectional: astNode.source.isBidirectional,
123
- }
124
- }
125
- if (ast.isInOutRelationExpr(astNode)) {
126
- return {
127
- inout: this.parseFqnExpr(astNode.inout.to),
128
- }
129
- }
130
- if (ast.isOutgoingRelationExpr(astNode)) {
131
- return {
132
- outgoing: this.parseFqnExpr(astNode.from),
133
- }
134
- }
135
- if (ast.isIncomingRelationExpr(astNode)) {
136
- return {
137
- incoming: this.parseFqnExpr(astNode.to),
138
- }
139
- }
140
- nonexhaustive(astNode)
141
- }
142
- }
143
- }
@@ -1,96 +0,0 @@
1
- import type * as c4 from '@likec4/core'
2
- import { nonexhaustive } from '@likec4/core'
3
- import { isTruthy } from 'remeda'
4
- import { ast, type ParsedAstGlobals } from '../../ast'
5
- import { logger, logWarnError } from '../../logger'
6
- import type { WithViewsParser } from './ViewsParser'
7
-
8
- export function GlobalsParser<TBase extends WithViewsParser>(B: TBase) {
9
- return class GlobalsParser extends B {
10
- parseGlobals() {
11
- const { parseResult, c4Globals } = this.doc
12
- const isValid = this.isValid
13
-
14
- const globals = parseResult.value.globals.filter(isValid)
15
-
16
- const elRelPredicates = globals.flatMap(r => r.predicates.filter(isValid))
17
- for (const predicate of elRelPredicates) {
18
- try {
19
- const globalPredicateId = predicate.name as c4.GlobalPredicateId
20
- if (!isTruthy(globalPredicateId)) {
21
- continue
22
- }
23
- if (globalPredicateId in c4Globals.predicates) {
24
- logger.warn(`Global predicate named "${globalPredicateId}" is already defined`)
25
- continue
26
- }
27
-
28
- this.parseAndStoreGlobalPredicateGroupOrDynamic(predicate, globalPredicateId, c4Globals)
29
- } catch (e) {
30
- logWarnError(e)
31
- }
32
- }
33
-
34
- const styles = globals.flatMap(r => r.styles.filter(isValid))
35
- for (const style of styles) {
36
- try {
37
- const globalStyleId = style.id.name as c4.GlobalStyleID
38
- if (!isTruthy(globalStyleId)) {
39
- continue
40
- }
41
- if (globalStyleId in c4Globals.styles) {
42
- logger.warn(`Global style named "${globalStyleId}" is already defined`)
43
- continue
44
- }
45
-
46
- const styles = this.parseGlobalStyleOrGroup(style)
47
- if (styles.length > 0) {
48
- c4Globals.styles[globalStyleId] = styles as c4.NonEmptyArray<c4.ViewRuleStyle>
49
- }
50
- } catch (e) {
51
- logWarnError(e)
52
- }
53
- }
54
- }
55
-
56
- parseAndStoreGlobalPredicateGroupOrDynamic(
57
- astRule: ast.GlobalPredicateGroup | ast.GlobalDynamicPredicateGroup,
58
- id: c4.GlobalPredicateId,
59
- c4Globals: ParsedAstGlobals
60
- ) {
61
- if (ast.isGlobalPredicateGroup(astRule)) {
62
- const predicates = this.parseGlobalPredicateGroup(astRule)
63
- if (predicates.length > 0) {
64
- c4Globals.predicates[id] = predicates as c4.NonEmptyArray<c4.ViewRulePredicate>
65
- }
66
- return
67
- }
68
- if (ast.isGlobalDynamicPredicateGroup(astRule)) {
69
- const predicates = this.parseGlobalDynamicPredicateGroup(astRule)
70
- if (predicates.length > 0) {
71
- c4Globals.dynamicPredicates[id] = predicates as c4.NonEmptyArray<c4.DynamicViewIncludeRule>
72
- }
73
- return
74
- }
75
- nonexhaustive(astRule)
76
- }
77
-
78
- parseGlobalPredicateGroup(astRule: ast.GlobalPredicateGroup): c4.ViewRulePredicate[] {
79
- return astRule.predicates.map(p => this.parseViewRulePredicate(p))
80
- }
81
-
82
- parseGlobalDynamicPredicateGroup(astRule: ast.GlobalDynamicPredicateGroup): c4.DynamicViewIncludeRule[] {
83
- return astRule.predicates.map(p => this.parseDynamicViewIncludePredicate(p))
84
- }
85
-
86
- parseGlobalStyleOrGroup(astRule: ast.GlobalStyle | ast.GlobalStyleGroup): c4.ViewRuleStyle[] {
87
- if (ast.isGlobalStyle(astRule)) {
88
- return [this.parseViewRuleStyle(astRule)]
89
- }
90
- if (ast.isGlobalStyleGroup(astRule)) {
91
- return astRule.styles.map(s => this.parseViewRuleStyle(s))
92
- }
93
- nonexhaustive(astRule)
94
- }
95
- }
96
- }
@@ -1,170 +0,0 @@
1
- import type * as c4 from '@likec4/core'
2
- import { isNonEmptyArray, nonexhaustive, nonNullable } from '@likec4/core'
3
- import { filter, first, isEmpty, isNonNullish, isTruthy, map, mapToObj, pipe } from 'remeda'
4
- import {
5
- type ParsedAstElement,
6
- type ParsedAstExtendElement,
7
- type ParsedAstRelation,
8
- ast,
9
- resolveRelationPoints,
10
- streamModel,
11
- toElementStyle,
12
- toRelationshipStyleExcludeDefaults,
13
- } from '../../ast'
14
- import { logWarnError } from '../../logger'
15
- import { stringHash } from '../../utils/stringHash'
16
- import { type Base, removeIndent, toSingleLine } from './Base'
17
-
18
- export type WithModel = ReturnType<typeof ModelParser>
19
-
20
- export function ModelParser<TBase extends Base>(B: TBase) {
21
- return class ModelParser extends B {
22
- parseModel() {
23
- const doc = this.doc
24
- for (const el of streamModel(doc)) {
25
- try {
26
- if (ast.isElement(el)) {
27
- doc.c4Elements.push(this.parseElement(el))
28
- continue
29
- }
30
- if (ast.isRelation(el)) {
31
- if (this.isValid(el)) {
32
- doc.c4Relations.push(this.parseRelation(el))
33
- }
34
- continue
35
- }
36
- if (ast.isExtendElement(el)) {
37
- const parsed = this.parseExtendElement(el)
38
- if (parsed) {
39
- doc.c4ExtendElements.push(parsed)
40
- }
41
- continue
42
- }
43
- nonexhaustive(el)
44
- } catch (e) {
45
- logWarnError(e)
46
- }
47
- }
48
- }
49
-
50
- parseElement(astNode: ast.Element): ParsedAstElement {
51
- const isValid = this.isValid
52
- const id = this.resolveFqn(astNode)
53
- const kind = nonNullable(astNode.kind.ref, 'Element kind is not resolved').name as c4.ElementKind
54
- const tags = this.parseTags(astNode.body)
55
- const stylePropsAst = astNode.body?.props.find(ast.isElementStyleProperty)?.props
56
- const style = toElementStyle(stylePropsAst, isValid)
57
- const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty))
58
- const astPath = this.getAstNodePath(astNode)
59
-
60
- let [title, description, technology] = astNode.props ?? []
61
-
62
- const bodyProps = pipe(
63
- astNode.body?.props ?? [],
64
- filter(isValid),
65
- filter(ast.isElementStringProperty),
66
- mapToObj(p => [p.key, p.value || undefined]),
67
- )
68
-
69
- title = removeIndent(title ?? bodyProps.title)
70
- description = removeIndent(bodyProps.description ?? description)
71
- technology = toSingleLine(bodyProps.technology ?? technology)
72
-
73
- const links = this.parseLinks(astNode.body)
74
-
75
- // Property has higher priority than from style
76
- const iconProp = astNode.body?.props.find(ast.isIconProperty)
77
- if (iconProp && isValid(iconProp)) {
78
- const value = iconProp.libicon?.ref?.name ?? iconProp.value
79
- if (isTruthy(value)) {
80
- style.icon = value as c4.IconUrl
81
- }
82
- }
83
-
84
- return {
85
- id,
86
- kind,
87
- astPath,
88
- title: title ?? astNode.name,
89
- ...(metadata && { metadata }),
90
- ...(tags && { tags }),
91
- ...(links && isNonEmptyArray(links) && { links }),
92
- ...(isTruthy(technology) && { technology }),
93
- ...(isTruthy(description) && { description }),
94
- style,
95
- }
96
- }
97
-
98
- parseExtendElement(astNode: ast.ExtendElement): ParsedAstExtendElement | null {
99
- const isValid = this.isValid
100
- const id = this.resolveFqn(astNode)
101
- const tags = this.parseTags(astNode.body)
102
- const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty))
103
- const astPath = this.getAstNodePath(astNode)
104
- const links = this.parseLinks(astNode.body) ?? []
105
-
106
- if (!tags && isEmpty(metadata ?? {}) && isEmpty(links)) {
107
- return null
108
- }
109
-
110
- return {
111
- id,
112
- astPath,
113
- ...(metadata && { metadata }),
114
- ...(tags && { tags }),
115
- ...(links && isNonEmptyArray(links) && { links }),
116
- }
117
- }
118
-
119
- parseRelation(astNode: ast.Relation): ParsedAstRelation {
120
- const isValid = this.isValid
121
- const coupling = resolveRelationPoints(astNode)
122
- const target = this.resolveFqn(coupling.target)
123
- const source = this.resolveFqn(coupling.source)
124
- const tags = this.parseTags(astNode) ?? this.parseTags(astNode.body)
125
- const links = this.parseLinks(astNode.body)
126
- const kind = astNode.kind?.ref?.name as (c4.RelationshipKind | undefined)
127
- const metadata = this.getMetadata(astNode.body?.props.find(ast.isMetadataProperty))
128
- const astPath = this.getAstNodePath(astNode)
129
-
130
- const bodyProps = mapToObj(
131
- astNode.body?.props.filter(ast.isRelationStringProperty).filter(p => isNonNullish(p.value)) ?? [],
132
- p => [p.key, p.value],
133
- )
134
-
135
- const navigateTo = pipe(
136
- astNode.body?.props ?? [],
137
- filter(ast.isRelationNavigateToProperty),
138
- map(p => p.value.view.ref?.name),
139
- filter(isTruthy),
140
- first(),
141
- )
142
-
143
- const title = removeIndent(astNode.title ?? bodyProps.title) ?? ''
144
- const description = removeIndent(bodyProps.description)
145
- const technology = toSingleLine(astNode.technology) ?? removeIndent(bodyProps.technology)
146
-
147
- const styleProp = astNode.body?.props.find(ast.isRelationStyleProperty)
148
- const id = stringHash(
149
- astPath,
150
- source,
151
- target,
152
- ) as c4.RelationId
153
- return {
154
- id,
155
- astPath,
156
- source,
157
- target,
158
- title,
159
- ...(metadata && { metadata }),
160
- ...(isTruthy(technology) && { technology }),
161
- ...(isTruthy(description) && { description }),
162
- ...(kind && { kind }),
163
- ...(tags && { tags }),
164
- ...(isNonEmptyArray(links) && { links }),
165
- ...toRelationshipStyleExcludeDefaults(styleProp?.props, isValid),
166
- ...(navigateTo && { navigateTo: navigateTo as c4.ViewId }),
167
- }
168
- }
169
- }
170
- }