@likec4/core 1.18.0 → 1.19.1
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/LICENSE +1 -1
- package/dist/builder/index.d.mts +502 -0
- package/dist/builder/index.d.ts +502 -0
- package/dist/builder/index.js +871 -0
- package/dist/compute-view/index.d.mts +9 -78
- package/dist/compute-view/index.d.ts +9 -78
- package/dist/compute-view/index.js +78 -0
- package/dist/index.d.mts +9 -730
- package/dist/index.d.ts +9 -730
- package/dist/{index.mjs → index.js} +7 -784
- package/dist/model/index.d.mts +218 -606
- package/dist/model/index.d.ts +218 -606
- package/dist/model/index.js +1 -0
- package/dist/shared/chunk-RAAYCPUM.M-JWF7SS.js +21 -0
- package/dist/shared/core.BIfgabEw.d.ts +719 -0
- package/dist/shared/core.BZ9Msq7w.d.mts +719 -0
- package/dist/shared/{core.DMYY7C8s.d.mts → core.C05RDLhi.d.ts} +179 -230
- package/dist/shared/{core.DMYY7C8s.d.ts → core.CmYMqgha.d.mts} +179 -230
- package/dist/shared/core.D6-fWbM3.d.ts +127 -0
- package/dist/shared/core.D74xkKkG.d.mts +148 -0
- package/dist/shared/core.D74xkKkG.d.ts +148 -0
- package/dist/shared/core.sLaWHBjR.d.mts +127 -0
- package/dist/shared/index.DafVOuVd.js +7443 -0
- package/dist/shared/object_hash.CE_oF3I3.js +1222 -0
- package/dist/shared/view.CyZrG8BJ.js +354 -0
- package/dist/types/index.d.mts +4 -2
- package/dist/types/index.d.ts +4 -2
- package/dist/types/index.js +20 -0
- package/dist/utils/index.d.mts +419 -0
- package/dist/utils/index.d.ts +419 -0
- package/dist/utils/index.js +347 -0
- package/package.json +47 -33
- package/src/builder/Builder-style1.test-d.ts +190 -0
- package/src/builder/Builder-style2.test-d.ts +175 -0
- package/src/builder/Builder.deployment.ts +25 -20
- package/src/builder/Builder.deploymentModel.ts +80 -55
- package/src/builder/Builder.element.ts +2 -0
- package/src/builder/Builder.model.ts +57 -43
- package/src/builder/Builder.ts +412 -431
- package/src/builder/Builder.view-common.ts +234 -0
- package/src/builder/Builder.view-deployment.ts +135 -0
- package/src/builder/Builder.view-element.ts +189 -0
- package/src/builder/Builder.views.ts +109 -33
- package/src/builder/Builder.with.ts +475 -0
- package/src/builder/__snapshots__/Builder-style1.spec.ts.snap +374 -0
- package/src/builder/__snapshots__/Builder-style2.spec.ts.snap +183 -0
- package/src/builder/__snapshots__/Builder.spec.ts.snap +374 -0
- package/src/builder/_types.ts +28 -5
- package/src/builder/index.ts +10 -4
- package/src/colors/index.ts +2 -2
- package/src/compute-view/compute-view.ts +45 -54
- package/src/compute-view/deployment-view/__test__/TestHelper.ts +236 -0
- package/src/compute-view/deployment-view/__test__/fixture.ts +50 -62
- package/src/compute-view/deployment-view/_types.ts +26 -18
- package/src/compute-view/deployment-view/clean-connections.ts +173 -164
- package/src/compute-view/deployment-view/compute.ts +51 -37
- package/src/compute-view/deployment-view/memory/index.ts +9 -0
- package/src/compute-view/deployment-view/memory/memory.ts +97 -0
- package/src/compute-view/deployment-view/predicates/elements.ts +144 -0
- package/src/compute-view/deployment-view/predicates/relation-direct.ts +183 -76
- package/src/compute-view/deployment-view/predicates/relation-in-out.ts +38 -20
- package/src/compute-view/deployment-view/predicates/relation-incoming.ts +72 -23
- package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +61 -22
- package/src/compute-view/deployment-view/predicates/wildcard.ts +15 -15
- package/src/compute-view/deployment-view/stages/stage-exclude.ts +5 -0
- package/src/compute-view/deployment-view/stages/stage-final.ts +245 -0
- package/src/compute-view/deployment-view/stages/stage-include.ts +95 -0
- package/src/compute-view/deployment-view/utils.ts +95 -31
- package/src/compute-view/dynamic-view/__test__/fixture.ts +4 -4
- package/src/compute-view/dynamic-view/compute.ts +59 -45
- package/src/compute-view/element-view/__test__/TestHelper.ts +258 -0
- package/src/compute-view/element-view/__test__/__snapshots__/legacy.spec.ts.snap +64 -62
- package/src/compute-view/element-view/__test__/fixture.ts +147 -117
- package/src/compute-view/element-view/_types.ts +48 -0
- package/src/compute-view/element-view/clean-connections.ts +113 -0
- package/src/compute-view/element-view/compute.ts +264 -683
- package/src/compute-view/element-view/memory/NodeGroup.ts +31 -0
- package/src/compute-view/element-view/memory/index.ts +10 -0
- package/src/compute-view/element-view/memory/memory.ts +177 -0
- package/src/compute-view/element-view/memory/stage-exclude.ts +59 -0
- package/src/compute-view/element-view/memory/stage-final.ts +129 -0
- package/src/compute-view/element-view/memory/stage-include.ts +128 -0
- package/src/compute-view/element-view/predicates/_utils.ts +86 -0
- package/src/compute-view/element-view/predicates/element-expand.ts +33 -0
- package/src/compute-view/element-view/predicates/element-kind-tag.ts +26 -0
- package/src/compute-view/element-view/predicates/element-ref.ts +37 -0
- package/src/compute-view/element-view/predicates/relation-direct.ts +309 -0
- package/src/compute-view/element-view/predicates/relation-in-out.ts +66 -0
- package/src/compute-view/element-view/predicates/relation-in.ts +118 -0
- package/src/compute-view/element-view/predicates/relation-out.ts +116 -0
- package/src/compute-view/element-view/predicates/wildcard.ts +88 -0
- package/src/compute-view/element-view/utils.ts +59 -0
- package/src/compute-view/index.ts +3 -4
- package/src/compute-view/memory/AbstractMemory.ts +45 -0
- package/src/compute-view/memory/AbstractStageExclude.ts +216 -0
- package/src/compute-view/memory/AbstractStageInclude.ts +200 -0
- package/src/compute-view/memory/_types.ts +147 -0
- package/src/compute-view/memory/index.ts +5 -0
- package/src/compute-view/memory/ops.ts +43 -0
- package/src/compute-view/utils/buildComputedNodes.ts +4 -4
- package/src/compute-view/utils/elementExpressionToPredicate.ts +1 -3
- package/src/compute-view/utils/link-nodes-with-edges.ts +3 -7
- package/src/compute-view/utils/{derive-edge-props-from-relationships.ts → merge-props-from-relationships.ts} +54 -23
- package/src/compute-view/utils/relationExpressionToPredicates.ts +2 -2
- package/src/compute-view/utils/resolve-extended-views.ts +44 -40
- package/src/compute-view/utils/resolve-global-rules.ts +6 -6
- package/src/compute-view/utils/topological-sort.ts +167 -71
- package/src/compute-view/utils/with-readable-edges.ts +14 -9
- package/src/index.ts +22 -16
- package/src/model/DeploymentElementModel.ts +150 -85
- package/src/model/DeploymentModel.ts +27 -21
- package/src/model/ElementModel.ts +51 -25
- package/src/model/LikeC4Model.test-d.ts +4 -3
- package/src/model/LikeC4Model.ts +98 -42
- package/src/model/RelationModel.ts +5 -4
- package/src/model/__test__/fixture.ts +5 -9
- package/src/model/connection/Connection.ts +72 -0
- package/src/model/connection/deployment/DeploymentConnectionModel.ts +178 -0
- package/src/model/connection/{deployment.ts → deployment/find.ts} +14 -18
- package/src/model/connection/deployment/index.ts +8 -0
- package/src/model/connection/index.ts +22 -0
- package/src/model/connection/model/ConnectionModel.ts +157 -0
- package/src/model/connection/{model.ts → model/find.ts} +43 -49
- package/src/model/connection/model/index.ts +8 -0
- package/src/model/connection/ops.ts +274 -0
- package/src/model/guards.ts +9 -0
- package/src/model/index.ts +45 -3
- package/src/model/types.ts +35 -18
- package/src/model/view/EdgeModel.ts +6 -6
- package/src/model/view/LikeC4ViewModel.ts +14 -8
- package/src/model/view/NodeModel.ts +6 -5
- package/src/types/_common.ts +53 -1
- package/src/types/deployments.ts +88 -156
- package/src/types/element.ts +2 -2
- package/src/types/expression-v2.ts +172 -0
- package/src/types/expression.ts +9 -5
- package/src/types/global.ts +3 -3
- package/src/types/index.ts +220 -13
- package/src/types/model.ts +28 -24
- package/src/types/operators.ts +1 -1
- package/src/types/relation.ts +14 -10
- package/src/types/theme.ts +0 -1
- package/src/types/view.ts +20 -18
- package/src/utils/const.ts +1 -0
- package/src/utils/fqn.ts +140 -49
- package/src/utils/getOrCreate.ts +1 -1
- package/src/utils/index.ts +75 -9
- package/src/utils/iterable/_types.ts +5 -0
- package/src/utils/iterable/filter.ts +55 -0
- package/src/utils/iterable/find.ts +36 -0
- package/src/utils/iterable/flat.ts +17 -0
- package/src/utils/iterable/index.ts +13 -0
- package/src/utils/iterable/map.ts +40 -0
- package/src/utils/iterable/reduce.ts +24 -0
- package/src/utils/iterable/some.ts +36 -0
- package/src/utils/iterable/to.ts +19 -0
- package/src/utils/iterable/unique.ts +29 -0
- package/src/utils/relations.ts +3 -64
- package/src/utils/set.ts +11 -9
- package/src/utils/string-hash.ts +1 -1
- package/dist/compute-view/index.mjs +0 -79
- package/dist/model/index.mjs +0 -2
- package/dist/shared/core.BBgiAAv1.mjs +0 -144
- package/dist/shared/core.BPyJvuMU.mjs +0 -2420
- package/dist/shared/core.BQ4AbfvB.mjs +0 -9228
- package/dist/shared/core.k12s_keD.mjs +0 -203
- package/dist/types/index.mjs +0 -21
- package/src/builder/Builder.test-d.ts +0 -124
- package/src/builder/Builder.view.ts +0 -543
- package/src/compute-view/LikeC4ModelGraph.ts +0 -330
- package/src/compute-view/deployment-view/Memory.ts +0 -108
- package/src/compute-view/deployment-view/Stage.ts +0 -145
- package/src/compute-view/deployment-view/predicates/deployment-ref.ts +0 -146
- package/src/compute-view/element-view/predicates.ts +0 -517
- package/src/compute-view/utils/sortNodes.ts +0 -101
- package/src/model/connection/ConnectionModel.ts +0 -94
- package/src/model/connection/DeploymentConnectionModel.ts +0 -99
|
@@ -1,734 +1,315 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
concat,
|
|
4
|
-
difference,
|
|
5
|
-
filter,
|
|
6
|
-
findLast,
|
|
7
|
-
first,
|
|
8
|
-
hasAtLeast,
|
|
9
|
-
isNonNull,
|
|
10
|
-
isTruthy,
|
|
11
|
-
map,
|
|
12
|
-
only,
|
|
13
|
-
pipe,
|
|
14
|
-
reduce,
|
|
15
|
-
sort,
|
|
16
|
-
unique
|
|
17
|
-
} from 'remeda'
|
|
1
|
+
import DefaultMap from 'mnemonist/default-map'
|
|
2
|
+
import { filter, findLast, forEach, map, pipe } from 'remeda'
|
|
18
3
|
import { invariant, nonexhaustive, nonNullable } from '../../errors'
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
isViewRuleAutoLayout,
|
|
31
|
-
isViewRuleGroup,
|
|
32
|
-
isViewRulePredicate,
|
|
33
|
-
type NodeId,
|
|
34
|
-
type NonEmptyArray,
|
|
35
|
-
type Relation,
|
|
36
|
-
type RelationPredicateExpression,
|
|
37
|
-
type RelationshipArrowType,
|
|
38
|
-
type RelationshipKind,
|
|
39
|
-
type RelationshipLineType,
|
|
40
|
-
type Tag,
|
|
41
|
-
type ViewId,
|
|
42
|
-
type ViewRuleGroup,
|
|
43
|
-
type ViewRulePredicate,
|
|
44
|
-
whereOperatorAsPredicate
|
|
4
|
+
import { type ElementModel, LikeC4Model } from '../../model'
|
|
5
|
+
import { ConnectionModel } from '../../model/connection/model/ConnectionModel'
|
|
6
|
+
import type { RelationshipModel } from '../../model/RelationModel'
|
|
7
|
+
import type { AnyAux } from '../../model/types'
|
|
8
|
+
import type {
|
|
9
|
+
ComputedElementView,
|
|
10
|
+
ElementPredicateExpression,
|
|
11
|
+
ElementView,
|
|
12
|
+
NodeId,
|
|
13
|
+
RelationPredicateExpression,
|
|
14
|
+
ViewRule,
|
|
45
15
|
} from '../../types'
|
|
16
|
+
import { isViewRuleAutoLayout, isViewRuleGroup, isViewRulePredicate, whereOperatorAsPredicate } from '../../types'
|
|
46
17
|
import * as Expr from '../../types/expression'
|
|
47
|
-
import {
|
|
48
|
-
import { commonAncestor, isAncestor } from '../../utils/fqn'
|
|
49
|
-
import { compareRelations } from '../../utils/relations'
|
|
50
|
-
import type { LikeC4ModelGraph } from '../LikeC4ModelGraph'
|
|
18
|
+
import { sortNaturalByFqn, sortParentsFirst } from '../../utils'
|
|
51
19
|
import { applyCustomElementProperties } from '../utils/applyCustomElementProperties'
|
|
52
20
|
import { applyCustomRelationProperties } from '../utils/applyCustomRelationProperties'
|
|
53
21
|
import { applyViewRuleStyles } from '../utils/applyViewRuleStyles'
|
|
54
|
-
import { buildComputedNodesFromElements } from '../utils/buildComputedNodes'
|
|
55
22
|
import { buildElementNotations } from '../utils/buildElementNotations'
|
|
56
23
|
import { linkNodesWithEdges } from '../utils/link-nodes-with-edges'
|
|
57
24
|
import { resolveGlobalRulesInElementView } from '../utils/resolve-global-rules'
|
|
58
|
-
import {
|
|
59
|
-
import { uniqueTags } from '../utils/uniqueTags'
|
|
25
|
+
import { topologicalSort } from '../utils/topological-sort'
|
|
60
26
|
import { calcViewLayoutHash } from '../utils/view-hash'
|
|
61
|
-
import {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function compareEdges(a: ComputeCtx.Edge, b: ComputeCtx.Edge) {
|
|
93
|
-
return compareRelations(
|
|
94
|
-
{ source: a.source.id, target: a.target.id },
|
|
95
|
-
{ source: b.source.id, target: b.target.id }
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// If there is only one relation and it has same source and target as edge
|
|
100
|
-
function isDirectEdge({ relations: [rel, ...tail], source, target }: ComputeCtx.Edge) {
|
|
101
|
-
if (rel && tail.length === 0) {
|
|
102
|
-
return rel.source === source.id && rel.target === target.id
|
|
103
|
-
}
|
|
104
|
-
return false
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export class NodesGroup {
|
|
108
|
-
static readonly kind = ElementKind.Group
|
|
109
|
-
|
|
110
|
-
static root() {
|
|
111
|
-
return new NodesGroup('@root' as NodeId, { title: null, groupRules: [] })
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
static is(node: ComputedNode) {
|
|
115
|
-
return node.kind === NodesGroup.kind
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
readonly explicits = new Set<Element>()
|
|
119
|
-
readonly implicits = new Set<Element>()
|
|
120
|
-
|
|
121
|
-
constructor(
|
|
122
|
-
public id: NodeId,
|
|
123
|
-
public viewRule: ViewRuleGroup,
|
|
124
|
-
public parent: NodeId | null = null
|
|
125
|
-
) {
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Add element explicitly
|
|
130
|
-
* Included even without relationships
|
|
131
|
-
*/
|
|
132
|
-
addElement(...el: Element[]) {
|
|
133
|
-
for (const r of el) {
|
|
134
|
-
this.explicits.add(r)
|
|
135
|
-
this.implicits.add(r)
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Add element implicitly
|
|
141
|
-
* Included if only has relationships
|
|
142
|
-
*/
|
|
143
|
-
addImplicit(...el: Element[]) {
|
|
144
|
-
for (const r of el) {
|
|
145
|
-
this.implicits.add(r)
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
excludeElement(...excludes: Element[]) {
|
|
150
|
-
for (const el of excludes) {
|
|
151
|
-
this.explicits.delete(el)
|
|
152
|
-
this.implicits.delete(el)
|
|
27
|
+
import { type Connection, type Elem, type PredicateCtx } from './_types'
|
|
28
|
+
import { type NodesGroup, type Stage, Memory } from './memory'
|
|
29
|
+
import { ActiveGroupMemory } from './memory/memory'
|
|
30
|
+
import { StageFinal } from './memory/stage-final'
|
|
31
|
+
import { ExpandedElementPredicate } from './predicates/element-expand'
|
|
32
|
+
import { ElementKindOrTagPredicate } from './predicates/element-kind-tag'
|
|
33
|
+
import { ElementRefPredicate } from './predicates/element-ref'
|
|
34
|
+
import { DirectRelationExprPredicate } from './predicates/relation-direct'
|
|
35
|
+
import { IncomingExprPredicate } from './predicates/relation-in'
|
|
36
|
+
import { InOutRelationPredicate } from './predicates/relation-in-out'
|
|
37
|
+
import { OutgoingExprPredicate } from './predicates/relation-out'
|
|
38
|
+
import { WildcardPredicate } from './predicates/wildcard'
|
|
39
|
+
import { buildNodes, NoFilter, NoWhere, toComputedEdges } from './utils'
|
|
40
|
+
|
|
41
|
+
function processElementPredicate(
|
|
42
|
+
// ...args:
|
|
43
|
+
// | [expr: ElementPredicateExpression, op: 'include', IncludePredicateCtx<ElementPredicateExpression>]
|
|
44
|
+
// | [expr: ElementPredicateExpression, op: 'exclude', ExcludePredicateCtx<ElementPredicateExpression>]
|
|
45
|
+
expr: ElementPredicateExpression,
|
|
46
|
+
op: 'include' | 'exclude',
|
|
47
|
+
ctx: Omit<PredicateCtx<RelationPredicateExpression>, 'expr'>,
|
|
48
|
+
): Stage {
|
|
49
|
+
switch (true) {
|
|
50
|
+
case Expr.isCustomElement(expr): {
|
|
51
|
+
if (op === 'include') {
|
|
52
|
+
return processElementPredicate(expr.custom.expr, op, ctx)
|
|
53
|
+
}
|
|
54
|
+
return ctx.stage
|
|
153
55
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
56
|
+
case Expr.isElementWhere(expr): {
|
|
57
|
+
const where = whereOperatorAsPredicate(expr.where.condition)
|
|
58
|
+
const filterWhere = filter<Elem>(where)
|
|
59
|
+
return processElementPredicate(expr.where.expr, op, { ...ctx, where, filterWhere } as any)
|
|
60
|
+
}
|
|
61
|
+
case Expr.isExpandedElementExpr(expr): {
|
|
62
|
+
return ExpandedElementPredicate[op]({ ...ctx, expr } as any) ?? ctx.stage
|
|
63
|
+
}
|
|
64
|
+
case Expr.isElementRef(expr): {
|
|
65
|
+
return ElementRefPredicate[op]({ ...ctx, expr } as any) ?? ctx.stage
|
|
66
|
+
}
|
|
67
|
+
case Expr.isWildcard(expr):
|
|
68
|
+
return WildcardPredicate[op]({ ...ctx, expr } as any) ?? ctx.stage
|
|
69
|
+
case Expr.isElementKindExpr(expr):
|
|
70
|
+
case Expr.isElementTagExpr(expr):
|
|
71
|
+
return ElementKindOrTagPredicate[op]({ ...ctx, expr } as any) ?? ctx.stage
|
|
72
|
+
default:
|
|
73
|
+
nonexhaustive(expr)
|
|
158
74
|
}
|
|
159
75
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
private __rootGroup = NodesGroup.root()
|
|
172
|
-
private groups = [] as NodesGroup[]
|
|
173
|
-
private activeGroupStack = [] as NodesGroup[]
|
|
174
|
-
|
|
175
|
-
protected get activeGroup() {
|
|
176
|
-
return this.activeGroupStack[0] ?? this.__rootGroup
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
protected get includedElements() {
|
|
180
|
-
return new Set([
|
|
181
|
-
...this.explicits,
|
|
182
|
-
...this.ctxEdges.flatMap(e => [e.source, e.target])
|
|
183
|
-
]) as ReadonlySet<Element>
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
protected get resolvedElements() {
|
|
187
|
-
return new Set([
|
|
188
|
-
...this.explicits,
|
|
189
|
-
...this.implicits,
|
|
190
|
-
...this.ctxEdges.flatMap(e => [e.source, e.target])
|
|
191
|
-
]) as ReadonlySet<Element>
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
protected get edges() {
|
|
195
|
-
return this.ctxEdges
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
protected get root() {
|
|
199
|
-
return isScopedElementView(this.view) ? this.view.viewOf : null
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
public static elementView(view: ElementView, graph: LikeC4ModelGraph) {
|
|
203
|
-
return new ComputeCtx(view, graph).compute()
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
private constructor(
|
|
207
|
-
protected view: ElementView,
|
|
208
|
-
protected graph: LikeC4ModelGraph
|
|
209
|
-
) {}
|
|
210
|
-
|
|
211
|
-
protected compute(): ComputedElementView {
|
|
212
|
-
this.reset()
|
|
213
|
-
const {
|
|
214
|
-
docUri: _docUri, // exclude docUri
|
|
215
|
-
rules: _rules, // exclude rules
|
|
216
|
-
...view
|
|
217
|
-
} = this.view
|
|
218
|
-
|
|
219
|
-
const rules = resolveGlobalRulesInElementView(this.view, this.graph.globals)
|
|
220
|
-
|
|
221
|
-
const viewPredicates = rules.filter(anyPass([isViewRulePredicate, isViewRuleGroup])) as Array<
|
|
222
|
-
ViewRulePredicate | ViewRuleGroup
|
|
223
|
-
>
|
|
224
|
-
if (this.root && viewPredicates.length == 0) {
|
|
225
|
-
this.addElement(this.graph.element(this.root))
|
|
226
|
-
}
|
|
227
|
-
this.processPredicates(viewPredicates)
|
|
228
|
-
this.removeRedundantImplicitEdges()
|
|
229
|
-
if (this.groups.length > 0) {
|
|
230
|
-
this.assignElementsToGroups()
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const elements = [...this.includedElements]
|
|
234
|
-
const nodesMap = buildComputedNodesFromElements(elements, this.groups)
|
|
235
|
-
|
|
236
|
-
const edges = this.computeEdges()
|
|
237
|
-
const edgesMap = new Map<EdgeId, ComputedEdge>(edges.map(edge => [edge.id, edge]))
|
|
238
|
-
|
|
239
|
-
linkNodesWithEdges(nodesMap, edges)
|
|
240
|
-
|
|
241
|
-
// nodesMap sorted hierarchically,
|
|
242
|
-
// but we need to keep the initial sort
|
|
243
|
-
let initialSort = elements.map(e => nonNullable(nodesMap.get(e.id), `Node ${e.id} not found in nodesMap`))
|
|
244
|
-
|
|
245
|
-
if (this.groups.length > 0) {
|
|
246
|
-
initialSort = concat(
|
|
247
|
-
this.groups.map(g => nonNullable(nodesMap.get(g.id), `Node ${g.id} not found in nodesMap`)),
|
|
248
|
-
initialSort
|
|
249
|
-
)
|
|
76
|
+
function processRelationtPredicate(
|
|
77
|
+
expr: RelationPredicateExpression,
|
|
78
|
+
op: 'include' | 'exclude',
|
|
79
|
+
ctx: Omit<PredicateCtx<RelationPredicateExpression>, 'expr'>,
|
|
80
|
+
): Stage {
|
|
81
|
+
switch (true) {
|
|
82
|
+
case Expr.isCustomRelationExpr(expr): {
|
|
83
|
+
if (op === 'include') {
|
|
84
|
+
return processRelationtPredicate(expr.customRelation.relation, op, ctx)
|
|
85
|
+
}
|
|
86
|
+
return ctx.stage
|
|
250
87
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
rules,
|
|
256
|
-
// Build graph and apply postorder sort
|
|
257
|
-
sortNodes({
|
|
258
|
-
nodes: initialSort,
|
|
259
|
-
edges
|
|
260
|
-
})
|
|
261
|
-
)
|
|
262
|
-
)
|
|
263
|
-
const sortedEdges = new Set([
|
|
264
|
-
...nodes.flatMap(n => n.children.length === 0 ? n.outEdges.flatMap(id => edgesMap.get(id) ?? []) : []),
|
|
265
|
-
...edges
|
|
266
|
-
])
|
|
267
|
-
|
|
268
|
-
const autoLayoutRule = findLast(this.view.rules, isViewRuleAutoLayout)
|
|
269
|
-
|
|
270
|
-
const elementNotations = buildElementNotations(nodes)
|
|
271
|
-
|
|
272
|
-
return calcViewLayoutHash({
|
|
273
|
-
...view,
|
|
274
|
-
autoLayout: {
|
|
275
|
-
direction: autoLayoutRule?.direction ?? 'TB',
|
|
276
|
-
...(autoLayoutRule?.nodeSep && { nodeSep: autoLayoutRule.nodeSep }),
|
|
277
|
-
...(autoLayoutRule?.rankSep && { rankSep: autoLayoutRule.rankSep })
|
|
278
|
-
},
|
|
279
|
-
nodes: map(nodes, n => {
|
|
280
|
-
// omit notation
|
|
281
|
-
delete n.notation
|
|
282
|
-
if (n.icon === 'none') {
|
|
283
|
-
delete n.icon
|
|
284
|
-
}
|
|
285
|
-
return n
|
|
286
|
-
}),
|
|
287
|
-
edges: applyCustomRelationProperties(rules, nodes, sortedEdges),
|
|
288
|
-
...(elementNotations.length > 0 && {
|
|
289
|
-
notation: {
|
|
290
|
-
elements: elementNotations
|
|
291
|
-
}
|
|
292
|
-
})
|
|
293
|
-
})
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
protected computeEdges(): ComputedEdge[] {
|
|
297
|
-
return this.ctxEdges.map((e): ComputedEdge => {
|
|
298
|
-
invariant(hasAtLeast(e.relations, 1), 'Edge must have at least one relation')
|
|
299
|
-
const relations = sort(e.relations, compareRelations)
|
|
300
|
-
const source = e.source.id
|
|
301
|
-
const target = e.target.id
|
|
302
|
-
|
|
303
|
-
const edge: ComputedEdge = {
|
|
304
|
-
id: stringHash(`${source}:${target}`) as EdgeId,
|
|
305
|
-
parent: commonAncestor(source, target),
|
|
306
|
-
source,
|
|
307
|
-
target,
|
|
308
|
-
label: null,
|
|
309
|
-
relations: relations.map(r => r.id)
|
|
88
|
+
case Expr.isRelationWhere(expr): {
|
|
89
|
+
const where = whereOperatorAsPredicate(expr.where.condition)
|
|
90
|
+
const filterRelations = (relations: ReadonlySet<RelationshipModel>) => {
|
|
91
|
+
return new Set(filter([...relations], where))
|
|
310
92
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
technology?: string | undefined
|
|
317
|
-
kind?: RelationshipKind | undefined
|
|
318
|
-
color?: Color | undefined
|
|
319
|
-
line?: RelationshipLineType | undefined
|
|
320
|
-
head?: RelationshipArrowType | undefined
|
|
321
|
-
tail?: RelationshipArrowType | undefined
|
|
322
|
-
tags?: NonEmptyArray<Tag>
|
|
323
|
-
navigateTo?: ViewId | undefined
|
|
324
|
-
} | undefined
|
|
325
|
-
relation = only(relations) ?? pipe(
|
|
326
|
-
relations,
|
|
327
|
-
filter(r => r.source === source && r.target === target),
|
|
328
|
-
only()
|
|
329
|
-
)
|
|
330
|
-
|
|
331
|
-
// This edge represents mutliple relations
|
|
332
|
-
// We use label if only it is the same for all relations
|
|
333
|
-
if (!relation) {
|
|
334
|
-
const allprops = pipe(
|
|
335
|
-
relations,
|
|
336
|
-
reduce((acc, r) => {
|
|
337
|
-
if (isTruthy(r.title) && !acc.title.includes(r.title)) {
|
|
338
|
-
acc.title.push(r.title)
|
|
339
|
-
}
|
|
340
|
-
if (isTruthy(r.description) && !acc.description.includes(r.description)) {
|
|
341
|
-
acc.description.push(r.description)
|
|
342
|
-
}
|
|
343
|
-
if (isTruthy(r.technology) && !acc.technology.includes(r.technology)) {
|
|
344
|
-
acc.technology.push(r.technology)
|
|
345
|
-
}
|
|
346
|
-
if (isTruthy(r.kind) && !acc.kind.includes(r.kind)) {
|
|
347
|
-
acc.kind.push(r.kind)
|
|
348
|
-
}
|
|
349
|
-
if (isTruthy(r.color) && !acc.color.includes(r.color)) {
|
|
350
|
-
acc.color.push(r.color)
|
|
351
|
-
}
|
|
352
|
-
if (isTruthy(r.line) && !acc.line.includes(r.line)) {
|
|
353
|
-
acc.line.push(r.line)
|
|
354
|
-
}
|
|
355
|
-
if (isTruthy(r.head) && !acc.head.includes(r.head)) {
|
|
356
|
-
acc.head.push(r.head)
|
|
357
|
-
}
|
|
358
|
-
if (isTruthy(r.tail) && !acc.tail.includes(r.tail)) {
|
|
359
|
-
acc.tail.push(r.tail)
|
|
360
|
-
}
|
|
361
|
-
if (isTruthy(r.navigateTo) && !acc.navigateTo.includes(r.navigateTo)) {
|
|
362
|
-
acc.navigateTo.push(r.navigateTo)
|
|
363
|
-
}
|
|
364
|
-
return acc
|
|
365
|
-
}, {
|
|
366
|
-
title: [] as string[],
|
|
367
|
-
description: [] as string[],
|
|
368
|
-
technology: [] as string[],
|
|
369
|
-
kind: [] as RelationshipKind[],
|
|
370
|
-
head: [] as RelationshipArrowType[],
|
|
371
|
-
tail: [] as RelationshipArrowType[],
|
|
372
|
-
color: [] as Color[],
|
|
373
|
-
line: [] as RelationshipLineType[],
|
|
374
|
-
navigateTo: [] as ViewId[]
|
|
375
|
-
})
|
|
93
|
+
const filterWhere = (connections: ReadonlyArray<Connection>) => {
|
|
94
|
+
return pipe(
|
|
95
|
+
connections,
|
|
96
|
+
map(c => new ConnectionModel(c.source, c.target, filterRelations(c.relations))),
|
|
97
|
+
filter(c => c.nonEmpty()),
|
|
376
98
|
)
|
|
377
|
-
relation = {
|
|
378
|
-
title: only(allprops.title) ?? '[...]',
|
|
379
|
-
description: only(allprops.description),
|
|
380
|
-
technology: only(allprops.technology),
|
|
381
|
-
kind: only(allprops.kind),
|
|
382
|
-
head: only(allprops.head),
|
|
383
|
-
tail: only(allprops.tail),
|
|
384
|
-
color: only(allprops.color),
|
|
385
|
-
line: only(allprops.line),
|
|
386
|
-
navigateTo: only(allprops.navigateTo)
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
const tags = uniqueTags(relations)
|
|
391
|
-
|
|
392
|
-
return Object.assign(
|
|
393
|
-
edge,
|
|
394
|
-
this.getEdgeLabel(relation),
|
|
395
|
-
isTruthy(relation.description) && { description: relation.description },
|
|
396
|
-
isTruthy(relation.technology) && { technology: relation.technology },
|
|
397
|
-
isTruthy(relation.kind) && { kind: relation.kind },
|
|
398
|
-
relation.color && { color: relation.color },
|
|
399
|
-
relation.line && { line: relation.line },
|
|
400
|
-
relation.head && { head: relation.head },
|
|
401
|
-
relation.tail && { tail: relation.tail },
|
|
402
|
-
relation.navigateTo && { navigateTo: relation.navigateTo },
|
|
403
|
-
tags && { tags }
|
|
404
|
-
)
|
|
405
|
-
})
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
protected addEdges(edges: ComputeCtx.Edge[]) {
|
|
409
|
-
const added = [] as ComputeCtx.Edge[]
|
|
410
|
-
for (const e of edges) {
|
|
411
|
-
if (!hasAtLeast(e.relations, 1)) {
|
|
412
|
-
continue
|
|
413
99
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
added.push(existing)
|
|
420
|
-
continue
|
|
421
|
-
}
|
|
422
|
-
added.push(e)
|
|
423
|
-
this.ctxEdges.push(e)
|
|
100
|
+
return processRelationtPredicate(expr.where.expr, op, {
|
|
101
|
+
...ctx,
|
|
102
|
+
where,
|
|
103
|
+
filterWhere,
|
|
104
|
+
})
|
|
424
105
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* Add element explicitly
|
|
430
|
-
* Included even without relationships
|
|
431
|
-
*/
|
|
432
|
-
protected addElement(...el: Element[]) {
|
|
433
|
-
for (const r of el) {
|
|
434
|
-
if (!this.explicits.has(r)) {
|
|
435
|
-
this.activeGroup.addElement(r)
|
|
436
|
-
} else if (this.activeGroup !== this.__rootGroup) {
|
|
437
|
-
this.groups.forEach(g => {
|
|
438
|
-
g.implicits.delete(r)
|
|
439
|
-
})
|
|
440
|
-
this.activeGroup.addImplicit(r)
|
|
441
|
-
}
|
|
442
|
-
this.explicits.add(r)
|
|
443
|
-
this.implicits.add(r)
|
|
106
|
+
case Expr.isInOut(expr): {
|
|
107
|
+
return InOutRelationPredicate[op]({ ...ctx, expr } as any) ?? ctx.stage
|
|
444
108
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* Add element implicitly
|
|
449
|
-
* Included if only has relationships
|
|
450
|
-
*/
|
|
451
|
-
protected addImplicit(...el: Element[]) {
|
|
452
|
-
for (const r of el) {
|
|
453
|
-
this.implicits.add(r)
|
|
454
|
-
// Remove implicits from other groups
|
|
455
|
-
if (this.activeGroup !== this.__rootGroup) {
|
|
456
|
-
this.groups.forEach(g => {
|
|
457
|
-
g.implicits.delete(r)
|
|
458
|
-
})
|
|
459
|
-
}
|
|
109
|
+
case Expr.isRelation(expr): {
|
|
110
|
+
return DirectRelationExprPredicate[op]({ ...ctx, expr } as any) ?? ctx.stage
|
|
460
111
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
protected excludeElement(...excludes: Element[]) {
|
|
465
|
-
for (const el of excludes) {
|
|
466
|
-
this.ctxEdges = this.ctxEdges.filter(e => e.source.id !== el.id && e.target.id !== el.id)
|
|
467
|
-
this.explicits.delete(el)
|
|
468
|
-
this.implicits.delete(el)
|
|
112
|
+
case Expr.isOutgoing(expr): {
|
|
113
|
+
return OutgoingExprPredicate[op]({ ...ctx, expr } as any) ?? ctx.stage
|
|
469
114
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
115
|
+
case Expr.isIncoming(expr): {
|
|
116
|
+
return IncomingExprPredicate[op]({ ...ctx, expr } as any) ?? ctx.stage
|
|
117
|
+
}
|
|
118
|
+
default:
|
|
119
|
+
nonexhaustive(expr)
|
|
474
120
|
}
|
|
121
|
+
}
|
|
475
122
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
123
|
+
export function processPredicates<M extends AnyAux>(
|
|
124
|
+
model: LikeC4Model<M>,
|
|
125
|
+
memory: Memory,
|
|
126
|
+
rules: ViewRule[],
|
|
127
|
+
): Memory {
|
|
128
|
+
const ctx = {
|
|
129
|
+
model,
|
|
130
|
+
scope: memory.scope,
|
|
131
|
+
where: NoWhere,
|
|
132
|
+
filterWhere: NoFilter,
|
|
133
|
+
}
|
|
134
|
+
for (const rule of rules) {
|
|
135
|
+
if (isViewRuleGroup(rule)) {
|
|
136
|
+
const groupMemory = ActiveGroupMemory.enter(memory, rule)
|
|
137
|
+
memory = processPredicates(model, groupMemory, rule.groupRules)
|
|
138
|
+
invariant(memory instanceof ActiveGroupMemory, 'processPredicates must return ActiveGroupMemory')
|
|
139
|
+
memory = memory.leave()
|
|
140
|
+
continue
|
|
141
|
+
}
|
|
142
|
+
if (isViewRulePredicate(rule)) {
|
|
143
|
+
const op = 'include' in rule ? 'include' : 'exclude'
|
|
144
|
+
const exprs = rule.include ?? rule.exclude
|
|
145
|
+
for (const expr of exprs) {
|
|
146
|
+
let stage = op === 'include' ? memory.stageInclude(expr) : memory.stageExclude(expr)
|
|
147
|
+
switch (true) {
|
|
148
|
+
case Expr.isElementPredicateExpr(expr):
|
|
149
|
+
stage = processElementPredicate(expr, op, {
|
|
150
|
+
...ctx,
|
|
151
|
+
stage,
|
|
152
|
+
memory,
|
|
153
|
+
} as any) ?? stage
|
|
154
|
+
break
|
|
155
|
+
case Expr.isRelationPredicateExpr(expr):
|
|
156
|
+
stage = processRelationtPredicate(expr, op, {
|
|
157
|
+
...ctx,
|
|
158
|
+
stage,
|
|
159
|
+
memory,
|
|
160
|
+
}) ?? stage
|
|
161
|
+
break
|
|
162
|
+
default:
|
|
163
|
+
nonexhaustive(expr)
|
|
495
164
|
}
|
|
496
|
-
|
|
497
|
-
}),
|
|
498
|
-
filter(isNonNull)
|
|
499
|
-
)
|
|
500
|
-
this.ctxEdges = ctxEdges
|
|
501
|
-
const remaining = this.includedElements
|
|
502
|
-
if (remaining.size === 0) {
|
|
503
|
-
this.implicits.clear()
|
|
504
|
-
this.__rootGroup.implicits.clear()
|
|
505
|
-
this.groups.forEach(g => g.implicits.clear())
|
|
506
|
-
return
|
|
507
|
-
}
|
|
508
|
-
for (const el of excludedImplicits) {
|
|
509
|
-
if (!remaining.has(el)) {
|
|
510
|
-
this.implicits.delete(el)
|
|
511
|
-
this.__rootGroup.implicits.delete(el)
|
|
512
|
-
this.groups.forEach(g => g.implicits.delete(el))
|
|
165
|
+
memory = stage.commit()
|
|
513
166
|
}
|
|
514
167
|
}
|
|
515
168
|
}
|
|
169
|
+
return StageFinal.for(memory).commit()
|
|
170
|
+
}
|
|
516
171
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
//
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
172
|
+
export function computeElementView<M extends AnyAux>(
|
|
173
|
+
likec4model: LikeC4Model<M>,
|
|
174
|
+
{
|
|
175
|
+
docUri: _docUri, // exclude docUri
|
|
176
|
+
rules, // exclude rules
|
|
177
|
+
...view
|
|
178
|
+
}: ElementView,
|
|
179
|
+
): ComputedElementView<M['ViewId']> {
|
|
180
|
+
rules = resolveGlobalRulesInElementView(rules, likec4model.globals())
|
|
181
|
+
const scope = view.viewOf ? likec4model.element(view.viewOf) : null
|
|
182
|
+
let memory = processPredicates<M>(
|
|
183
|
+
likec4model,
|
|
184
|
+
Memory.empty(scope),
|
|
185
|
+
rules,
|
|
186
|
+
)
|
|
187
|
+
if (memory.isEmpty() && scope) {
|
|
188
|
+
memory = memory.update({
|
|
189
|
+
final: new Set([scope]),
|
|
190
|
+
})
|
|
525
191
|
}
|
|
192
|
+
memory = assignElementsToGroups(memory)
|
|
526
193
|
|
|
527
|
-
|
|
528
|
-
// i.e. remove implicit edges, derived from childs
|
|
529
|
-
protected removeRedundantImplicitEdges() {
|
|
530
|
-
const processedRelations = new Set<Relation>()
|
|
194
|
+
const nodesMap = buildNodes(memory)
|
|
531
195
|
|
|
532
|
-
|
|
533
|
-
const excludeProcessed = (relations: Relation[]) =>
|
|
534
|
-
relations.reduce((acc, rel) => {
|
|
535
|
-
if (!processedRelations.has(rel)) {
|
|
536
|
-
acc.push(rel)
|
|
537
|
-
processedRelations.add(rel)
|
|
538
|
-
}
|
|
539
|
-
return acc
|
|
540
|
-
}, [] as Relation[])
|
|
196
|
+
const computedEdges = toComputedEdges(memory.connections)
|
|
541
197
|
|
|
542
|
-
|
|
543
|
-
const isNestedEdgeOf = (parent: ComputeCtx.Edge) => {
|
|
544
|
-
const { source, target } = parent
|
|
545
|
-
// Checks if edge is between descendants of source and target of the parent edge
|
|
546
|
-
return (edge: ComputeCtx.Edge) => {
|
|
547
|
-
const isSameSource = source.id === edge.source.id
|
|
548
|
-
const isSameTarget = target.id === edge.target.id
|
|
549
|
-
if (isSameSource && isSameTarget) {
|
|
550
|
-
return true
|
|
551
|
-
}
|
|
552
|
-
const isSourceNested = isAncestor(source.id, edge.source.id)
|
|
553
|
-
const isTargetNested = isAncestor(target.id, edge.target.id)
|
|
554
|
-
return (
|
|
555
|
-
(isSourceNested && isTargetNested)
|
|
556
|
-
|| (isSameSource && isTargetNested)
|
|
557
|
-
|| (isSameTarget && isSourceNested)
|
|
558
|
-
)
|
|
559
|
-
}
|
|
560
|
-
}
|
|
198
|
+
linkNodesWithEdges(nodesMap, computedEdges)
|
|
561
199
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
if (relations.length === 0) {
|
|
567
|
-
return acc
|
|
568
|
-
}
|
|
569
|
-
// If there is an edge between descendants of current edge,
|
|
570
|
-
// then we don't add this edge
|
|
571
|
-
if (acc.length > 0 && acc.some(isNestedEdgeOf(e))) {
|
|
572
|
-
return acc
|
|
573
|
-
}
|
|
574
|
-
acc.push({
|
|
575
|
-
source: e.source,
|
|
576
|
-
target: e.target,
|
|
577
|
-
relations
|
|
578
|
-
})
|
|
579
|
-
return acc
|
|
580
|
-
}, [] as ComputeCtx.Edge[])
|
|
581
|
-
}
|
|
200
|
+
const sorted = topologicalSort({
|
|
201
|
+
nodes: nodesMap,
|
|
202
|
+
edges: computedEdges,
|
|
203
|
+
})
|
|
582
204
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
205
|
+
const nodes = applyCustomElementProperties(
|
|
206
|
+
rules,
|
|
207
|
+
applyViewRuleStyles(
|
|
208
|
+
rules,
|
|
209
|
+
sorted.nodes,
|
|
210
|
+
),
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
const autoLayoutRule = findLast(rules, isViewRuleAutoLayout)
|
|
214
|
+
|
|
215
|
+
const elementNotations = buildElementNotations(nodes)
|
|
216
|
+
|
|
217
|
+
return calcViewLayoutHash({
|
|
218
|
+
...view,
|
|
219
|
+
autoLayout: {
|
|
220
|
+
direction: autoLayoutRule?.direction ?? 'TB',
|
|
221
|
+
...(autoLayoutRule?.nodeSep && { nodeSep: autoLayoutRule.nodeSep }),
|
|
222
|
+
...(autoLayoutRule?.rankSep && { rankSep: autoLayoutRule.rankSep }),
|
|
223
|
+
},
|
|
224
|
+
edges: applyCustomRelationProperties(rules, nodes, sorted.edges),
|
|
225
|
+
nodes: map(nodes, n => {
|
|
226
|
+
// omit notation
|
|
227
|
+
delete n.notation
|
|
228
|
+
if (n.icon === 'none') {
|
|
229
|
+
delete n.icon
|
|
607
230
|
}
|
|
608
|
-
|
|
609
|
-
}
|
|
231
|
+
return n
|
|
232
|
+
}),
|
|
233
|
+
...(elementNotations.length > 0 && {
|
|
234
|
+
notation: {
|
|
235
|
+
elements: elementNotations,
|
|
236
|
+
},
|
|
237
|
+
}),
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Clean up group.explicits and group.implicits
|
|
243
|
+
*/
|
|
244
|
+
function assignElementsToGroups(memory: Memory) {
|
|
245
|
+
if (memory.groups.length === 0) {
|
|
246
|
+
return memory
|
|
610
247
|
}
|
|
248
|
+
const groupAssignments = new DefaultMap<NodeId, Set<Elem>>(() => new Set())
|
|
611
249
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
this.processPredicates(rule.groupRules)
|
|
622
|
-
this.activeGroupStack.shift()
|
|
623
|
-
continue
|
|
624
|
-
}
|
|
625
|
-
const isInclude = 'include' in rule
|
|
626
|
-
const exprs = rule.include ?? rule.exclude
|
|
627
|
-
for (const expr of exprs) {
|
|
628
|
-
if (Expr.isElementPredicateExpr(expr)) {
|
|
629
|
-
this.processElementPredicate(expr, isInclude)
|
|
630
|
-
continue
|
|
631
|
-
}
|
|
632
|
-
if (Expr.isRelationPredicateExpr(expr)) {
|
|
633
|
-
this.processRelationPredicate(expr, isInclude)
|
|
634
|
-
continue
|
|
635
|
-
}
|
|
636
|
-
nonexhaustive(expr)
|
|
250
|
+
const assignedTo = new Map<ElementModel, NodesGroup['id']>()
|
|
251
|
+
|
|
252
|
+
const isAncestorAssigned = (el: Elem) => {
|
|
253
|
+
for (const parent of el.ancestors()) {
|
|
254
|
+
const groupId = assignedTo.get(parent)
|
|
255
|
+
if (groupId) {
|
|
256
|
+
assignedTo.set(el, groupId)
|
|
257
|
+
groupAssignments.get(groupId).add(el)
|
|
258
|
+
return true
|
|
637
259
|
}
|
|
638
260
|
}
|
|
639
|
-
return
|
|
261
|
+
return false
|
|
640
262
|
}
|
|
641
263
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
this.processElementPredicate(expr.custom.expr, isInclude)
|
|
264
|
+
const isDescendantAssigned = (el: Elem) => {
|
|
265
|
+
for (const descendant of el.descendants('asc')) {
|
|
266
|
+
const groupId = assignedTo.get(descendant)
|
|
267
|
+
if (groupId) {
|
|
268
|
+
assignedTo.set(el, groupId)
|
|
269
|
+
groupAssignments.get(groupId).add(el)
|
|
270
|
+
return true
|
|
650
271
|
}
|
|
651
|
-
return this
|
|
652
|
-
}
|
|
653
|
-
if (Expr.isElementWhere(expr)) {
|
|
654
|
-
const where = whereOperatorAsPredicate(expr.where.condition)
|
|
655
|
-
this.processElementPredicate(expr.where.expr, isInclude, where)
|
|
656
|
-
return this
|
|
657
|
-
}
|
|
658
|
-
if (Expr.isExpandedElementExpr(expr)) {
|
|
659
|
-
isInclude
|
|
660
|
-
? includeExpandedElementExpr.call(this, expr, where)
|
|
661
|
-
: excludeExpandedElementExpr.call(this, expr, where)
|
|
662
|
-
return this
|
|
663
|
-
}
|
|
664
|
-
if (Expr.isElementKindExpr(expr) || Expr.isElementTagExpr(expr)) {
|
|
665
|
-
isInclude
|
|
666
|
-
? includeElementKindOrTag.call(this, expr, where)
|
|
667
|
-
: excludeElementKindOrTag.call(this, expr, where)
|
|
668
|
-
return this
|
|
669
|
-
}
|
|
670
|
-
if (Expr.isElementRef(expr)) {
|
|
671
|
-
isInclude ? includeElementRef.call(this, expr, where) : excludeElementRef.call(this, expr, where)
|
|
672
|
-
return this
|
|
673
272
|
}
|
|
674
|
-
|
|
675
|
-
isInclude ? includeWildcardRef.call(this, expr, where) : excludeWildcardRef.call(this, expr, where)
|
|
676
|
-
return this
|
|
677
|
-
}
|
|
678
|
-
nonexhaustive(expr)
|
|
273
|
+
return false
|
|
679
274
|
}
|
|
680
275
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
this.processRelationPredicate(expr.customRelation.relation, isInclude)
|
|
276
|
+
pipe(
|
|
277
|
+
sortParentsFirst([...memory.explicitFirstSeenIn.keys()]),
|
|
278
|
+
forEach((el) => {
|
|
279
|
+
if (!isAncestorAssigned(el)) {
|
|
280
|
+
const groupId = nonNullable(memory.explicitFirstSeenIn.get(el))
|
|
281
|
+
assignedTo.set(el, groupId)
|
|
282
|
+
groupAssignments.get(groupId).add(el)
|
|
689
283
|
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
if (Expr.isRelationWhere(expr)) {
|
|
693
|
-
const where = whereOperatorAsPredicate(expr.where.condition)
|
|
694
|
-
this.processRelationPredicate(expr.where.expr, isInclude, where)
|
|
695
|
-
return this
|
|
696
|
-
}
|
|
697
|
-
if (Expr.isIncoming(expr)) {
|
|
698
|
-
isInclude ? includeIncomingExpr.call(this, expr, where) : excludeIncomingExpr.call(this, expr, where)
|
|
699
|
-
return this
|
|
700
|
-
}
|
|
701
|
-
if (Expr.isOutgoing(expr)) {
|
|
702
|
-
isInclude ? includeOutgoingExpr.call(this, expr, where) : excludeOutgoingExpr.call(this, expr, where)
|
|
703
|
-
return this
|
|
704
|
-
}
|
|
705
|
-
if (Expr.isInOut(expr)) {
|
|
706
|
-
isInclude ? includeInOutExpr.call(this, expr, where) : excludeInOutExpr.call(this, expr, where)
|
|
707
|
-
return this
|
|
708
|
-
}
|
|
709
|
-
if (Expr.isRelation(expr)) {
|
|
710
|
-
isInclude ? includeRelationExpr.call(this, expr, where) : excludeRelationExpr.call(this, expr, where)
|
|
711
|
-
return this
|
|
712
|
-
}
|
|
713
|
-
nonexhaustive(expr)
|
|
714
|
-
}
|
|
284
|
+
}),
|
|
285
|
+
)
|
|
715
286
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
287
|
+
pipe(
|
|
288
|
+
sortParentsFirst([...memory.lastSeenIn.keys()]),
|
|
289
|
+
filter(el => !assignedTo.has(el)),
|
|
290
|
+
forEach((el) => {
|
|
291
|
+
if (isAncestorAssigned(el)) {
|
|
292
|
+
return
|
|
293
|
+
}
|
|
294
|
+
if (isDescendantAssigned(el)) {
|
|
295
|
+
return
|
|
296
|
+
}
|
|
297
|
+
const groupId = nonNullable(memory.lastSeenIn.get(el))
|
|
298
|
+
assignedTo.set(el, groupId)
|
|
299
|
+
groupAssignments.get(groupId).add(el)
|
|
300
|
+
}),
|
|
301
|
+
)
|
|
723
302
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
303
|
+
if (groupAssignments.size === 0) {
|
|
304
|
+
return memory
|
|
305
|
+
}
|
|
727
306
|
|
|
728
|
-
|
|
729
|
-
|
|
307
|
+
let groups = memory.groups.map(group => {
|
|
308
|
+
const explicits = groupAssignments.get(group.id)
|
|
309
|
+
if (!explicits) {
|
|
310
|
+
return group
|
|
730
311
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
}
|
|
312
|
+
return group.update(explicits)
|
|
313
|
+
})
|
|
314
|
+
return memory.update({ groups })
|
|
734
315
|
}
|