@likec4/core 1.41.0 → 1.42.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/builder/package.json +4 -0
- package/compute-view/package.json +4 -0
- package/compute-view/relationships/package.json +4 -0
- package/dist/builder/index.d.mts +13 -11
- package/dist/builder/index.mjs +111 -91
- package/dist/compute-view/index.d.mts +8 -7
- package/dist/compute-view/index.mjs +14 -11
- package/dist/compute-view/relationships-view/index.d.mts +9 -8
- package/dist/compute-view/relationships-view/index.mjs +6 -6
- package/dist/index.d.mts +7 -7
- package/dist/index.mjs +11 -11
- package/dist/model/connection/deployment/index.d.mts +11 -10
- package/dist/model/connection/deployment/index.mjs +2 -2
- package/dist/model/connection/index.d.mts +12 -11
- package/dist/model/connection/index.mjs +3 -3
- package/dist/model/connection/model/index.d.mts +11 -10
- package/dist/model/connection/model/index.mjs +2 -2
- package/dist/model/index.d.mts +14 -14
- package/dist/model/index.mjs +4 -5
- package/dist/shared/{core.Ae86Y3tB.mjs → core.01VTr9qu.mjs} +8 -26
- package/dist/shared/core.1vX5a9AY.mjs +86 -0
- package/dist/shared/{core.BVNaR17u.mjs → core.B7KhhkRY.mjs} +1 -1
- package/dist/shared/core.B85MJLTf.mjs +5 -0
- package/dist/shared/{core.DA5vzwZ9.mjs → core.BIqHjQiH.mjs} +56 -42
- package/dist/shared/core.BKlholso.mjs +3 -0
- package/dist/shared/core.BR4zXDH-.mjs +13 -0
- package/dist/shared/{core.CXy1NLUG.mjs → core.BYNkXQ8A.mjs} +21 -12
- package/dist/shared/{core.BuoXfSMj.mjs → core.BbE4y-yl.mjs} +1 -1
- package/dist/shared/core.BoYfZOBm.mjs +3 -0
- package/dist/shared/core.BsSXS-_R.mjs +7 -0
- package/dist/shared/{core.DJp8fgok.mjs → core.BzTakU3I.mjs} +14 -10
- package/dist/shared/{core.4u1dVmfX.mjs → core.C2TiyjI6.mjs} +2 -2
- package/dist/shared/{core.DLpidoWc.mjs → core.C3JowhaZ.mjs} +3 -3
- package/dist/shared/{core.CNGyqkJh.mjs → core.C4x8aaWa.mjs} +3 -1
- package/dist/shared/{core.BB6zA-UU.mjs → core.C8YY6Uub.mjs} +38 -9
- package/dist/shared/{core.Asc11Mjy.mjs → core.C8sl5Y8Z.mjs} +122 -58
- package/dist/shared/{core.BvMRMo7Z.mjs → core.CNgZvKb8.mjs} +3 -5198
- package/dist/shared/{core.CUG6vpyL.d.mts → core.CQngdcuj.d.mts} +85 -4
- package/dist/shared/{core.Be1n9a0i.mjs → core.C_Ni6yHl.mjs} +5 -4
- package/dist/shared/core.CbT8JVYL.mjs +5 -0
- package/dist/shared/core.CqAJJpK_.mjs +3 -0
- package/dist/shared/{core.BXsxn-hf.d.mts → core.D0PjZzIS.d.mts} +27 -15
- package/dist/shared/{core.DgesAwC8.d.mts → core.D5fzJWbF.d.mts} +2 -2
- package/dist/shared/{core.hfOE3RfK.d.mts → core.DACpKp35.d.mts} +5 -5
- package/dist/shared/core.DFi2dOTl.mjs +39548 -0
- package/dist/shared/{core.xSQO0XYD.d.mts → core.DIol3syK.d.mts} +2 -2
- package/dist/shared/{core.DlG1xWt2.mjs → core.DMMOWQqc.mjs} +3 -2
- package/dist/shared/core.DNbUPnve.mjs +3990 -0
- package/dist/shared/{core.CCD1ID81.mjs → core.DRJma0Ol.mjs} +4 -3
- package/dist/shared/{core.DgttqsJc.mjs → core.DRhg-gVU.mjs} +9 -9
- package/dist/shared/core.DSO_rTC2.mjs +5322 -0
- package/dist/shared/{core.FUPplpae.d.mts → core.D_vkffV4.d.mts} +4 -2
- package/dist/shared/core.DdMJk7nx.d.mts +476 -0
- package/dist/shared/{core.BhpgpOep.d.mts → core.Dgs6-nuz.d.mts} +2 -2
- package/dist/shared/{core.Be4Jy9Qx.mjs → core.FZ305VYd.mjs} +2 -2
- package/dist/shared/core.OHD4KXm9.mjs +5 -0
- package/dist/shared/{core.GTgsmXS_.mjs → core.QJD_fcrc.mjs} +2 -2
- package/dist/shared/core.RfnaEmgA.mjs +7 -0
- package/dist/shared/core.fHR_8Tnq.d.mts +450 -0
- package/dist/shared/{core.D0EgVMQK.mjs → core.lRvxPoao.mjs} +14 -12
- package/dist/shared/{core.CaT0SUUK.mjs → core.rCeaAqgW.mjs} +1 -84
- package/dist/shared/{core.zxTBF_PD.d.mts → core.s9uwOt60.d.mts} +4 -4
- package/dist/shared/core.yna0OdV5.d.mts +98 -0
- package/dist/styles/index.d.mts +106 -0
- package/dist/styles/index.mjs +1 -0
- package/dist/types/_aux.d.mts +2 -2
- package/dist/types/expression-model.d.mts +3 -4
- package/dist/types/expression.d.mts +3 -4
- package/dist/types/fqnRef.d.mts +2 -2
- package/dist/types/fqnRef.mjs +3 -3
- package/dist/types/index.d.mts +8 -8
- package/dist/types/index.mjs +150 -5
- package/dist/types/scalar.mjs +3 -2
- package/dist/utils/graphology/index.d.mts +1864 -0
- package/dist/utils/graphology/index.mjs +1 -0
- package/dist/utils/index.d.mts +11 -6
- package/dist/utils/index.mjs +21 -37
- package/dist/utils/iterable/index.d.mts +1 -1
- package/dist/utils/iterable/index.mjs +45 -7
- package/model/package.json +4 -0
- package/package.json +42 -24
- package/styles/package.json +4 -0
- package/types/package.json +4 -0
- package/utils/graphology/package.json +4 -0
- package/utils/package.json +4 -0
- package/dist/shared/core.6_Vp2iiN.mjs +0 -172
- package/dist/shared/core.CUw_2J9j.mjs +0 -47
- package/dist/shared/core.CzdwsPmY.d.mts +0 -69
- package/dist/shared/core.D-ZGWDDt.d.mts +0 -831
- package/dist/shared/core.DoM6aM9N.mjs +0 -7
- package/dist/shared/core.ILwny7BV.mjs +0 -38
- package/dist/theme/index.d.mts +0 -11
- package/dist/theme/index.mjs +0 -232
- package/src/builder/Builder.deployment.ts +0 -224
- package/src/builder/Builder.deploymentModel.ts +0 -449
- package/src/builder/Builder.element.ts +0 -241
- package/src/builder/Builder.model.ts +0 -494
- package/src/builder/Builder.ts +0 -946
- package/src/builder/Builder.view-common.ts +0 -273
- package/src/builder/Builder.view-deployment.ts +0 -135
- package/src/builder/Builder.view-element.ts +0 -202
- package/src/builder/Builder.views.ts +0 -399
- package/src/builder/Builder.with.ts +0 -156
- package/src/builder/_types.ts +0 -324
- package/src/builder/index.ts +0 -11
- package/src/compute-view/compute-view.ts +0 -80
- package/src/compute-view/deployment-view/_types.ts +0 -38
- package/src/compute-view/deployment-view/clean-connections.ts +0 -236
- package/src/compute-view/deployment-view/compute.ts +0 -94
- package/src/compute-view/deployment-view/memory/index.ts +0 -9
- package/src/compute-view/deployment-view/memory/memory.ts +0 -96
- package/src/compute-view/deployment-view/predicates/deploymentRefs-where.ts +0 -18
- package/src/compute-view/deployment-view/predicates/deploymentRefs.ts +0 -154
- package/src/compute-view/deployment-view/predicates/index.ts +0 -8
- package/src/compute-view/deployment-view/predicates/relation-direct.ts +0 -243
- package/src/compute-view/deployment-view/predicates/relation-in-out.ts +0 -70
- package/src/compute-view/deployment-view/predicates/relation-incoming.ts +0 -101
- package/src/compute-view/deployment-view/predicates/relation-outgoing.ts +0 -95
- package/src/compute-view/deployment-view/predicates/relation-where.ts +0 -18
- package/src/compute-view/deployment-view/predicates/utils.ts +0 -282
- package/src/compute-view/deployment-view/predicates/wildcard.ts +0 -41
- package/src/compute-view/deployment-view/stages/stage-exclude.ts +0 -5
- package/src/compute-view/deployment-view/stages/stage-final.ts +0 -245
- package/src/compute-view/deployment-view/stages/stage-include.ts +0 -95
- package/src/compute-view/deployment-view/utils.ts +0 -297
- package/src/compute-view/dynamic-view/compute.ts +0 -312
- package/src/compute-view/element-view/_types.ts +0 -47
- package/src/compute-view/element-view/clean-connections.ts +0 -117
- package/src/compute-view/element-view/compute.ts +0 -324
- package/src/compute-view/element-view/memory/NodeGroup.ts +0 -31
- package/src/compute-view/element-view/memory/index.ts +0 -10
- package/src/compute-view/element-view/memory/memory.ts +0 -178
- package/src/compute-view/element-view/memory/stage-exclude.ts +0 -59
- package/src/compute-view/element-view/memory/stage-final.ts +0 -129
- package/src/compute-view/element-view/memory/stage-include.ts +0 -130
- package/src/compute-view/element-view/predicates/_utils.ts +0 -91
- package/src/compute-view/element-view/predicates/element-expand.ts +0 -29
- package/src/compute-view/element-view/predicates/element-kind-tag.ts +0 -27
- package/src/compute-view/element-view/predicates/element-ref.ts +0 -24
- package/src/compute-view/element-view/predicates/relation-direct.ts +0 -321
- package/src/compute-view/element-view/predicates/relation-in-out.ts +0 -64
- package/src/compute-view/element-view/predicates/relation-in.ts +0 -115
- package/src/compute-view/element-view/predicates/relation-out.ts +0 -119
- package/src/compute-view/element-view/predicates/wildcard.ts +0 -88
- package/src/compute-view/element-view/utils.ts +0 -65
- package/src/compute-view/index.ts +0 -4
- package/src/compute-view/memory/AbstractMemory.ts +0 -45
- package/src/compute-view/memory/AbstractStageExclude.ts +0 -215
- package/src/compute-view/memory/AbstractStageInclude.ts +0 -200
- package/src/compute-view/memory/_types.ts +0 -148
- package/src/compute-view/memory/index.ts +0 -5
- package/src/compute-view/memory/ops.ts +0 -43
- package/src/compute-view/relationships-view/_types.ts +0 -16
- package/src/compute-view/relationships-view/compute.ts +0 -159
- package/src/compute-view/relationships-view/index.ts +0 -3
- package/src/compute-view/relationships-view/layout.ts +0 -358
- package/src/compute-view/relationships-view/utils.ts +0 -73
- package/src/compute-view/utils/ancestorsOfNode.ts +0 -19
- package/src/compute-view/utils/applyCustomElementProperties.ts +0 -107
- package/src/compute-view/utils/applyCustomRelationProperties.ts +0 -58
- package/src/compute-view/utils/applyViewRuleStyles.ts +0 -73
- package/src/compute-view/utils/buildComputedNodes.ts +0 -166
- package/src/compute-view/utils/buildElementNotations.ts +0 -64
- package/src/compute-view/utils/elementExpressionToPredicate.ts +0 -50
- package/src/compute-view/utils/link-nodes-with-edges.ts +0 -56
- package/src/compute-view/utils/merge-props-from-relationships.ts +0 -161
- package/src/compute-view/utils/relationExpressionToPredicates.ts +0 -55
- package/src/compute-view/utils/resolve-extended-views.ts +0 -85
- package/src/compute-view/utils/resolve-global-rules.ts +0 -88
- package/src/compute-view/utils/topological-sort.ts +0 -256
- package/src/compute-view/utils/view-hash.ts +0 -45
- package/src/compute-view/utils/with-readable-edges.ts +0 -40
- package/src/index.ts +0 -32
- package/src/model/DeploymentElementModel.ts +0 -712
- package/src/model/DeploymentModel.ts +0 -393
- package/src/model/ElementModel.ts +0 -316
- package/src/model/LikeC4Model.ts +0 -882
- package/src/model/RelationModel.ts +0 -152
- package/src/model/connection/Connection.ts +0 -72
- package/src/model/connection/deployment/DeploymentConnectionModel.ts +0 -185
- package/src/model/connection/deployment/find.ts +0 -120
- package/src/model/connection/deployment/index.ts +0 -8
- package/src/model/connection/index.ts +0 -22
- package/src/model/connection/model/ConnectionModel.ts +0 -158
- package/src/model/connection/model/find.ts +0 -117
- package/src/model/connection/model/index.ts +0 -8
- package/src/model/connection/ops.ts +0 -276
- package/src/model/guards.ts +0 -77
- package/src/model/index.ts +0 -72
- package/src/model/types.ts +0 -67
- package/src/model/utils.ts +0 -66
- package/src/model/view/EdgeModel.ts +0 -121
- package/src/model/view/LikeC4ViewModel.ts +0 -361
- package/src/model/view/LikeC4ViewsFolder.ts +0 -151
- package/src/model/view/NodeModel.ts +0 -247
- package/src/theme/element.ts +0 -61
- package/src/theme/index.ts +0 -127
- package/src/theme/relationships.ts +0 -54
- package/src/types/RichText.ts +0 -205
- package/src/types/_aux.ts +0 -317
- package/src/types/_common.ts +0 -102
- package/src/types/const.ts +0 -34
- package/src/types/expression-model.ts +0 -294
- package/src/types/expression.ts +0 -313
- package/src/types/fqnRef.ts +0 -91
- package/src/types/geometry.ts +0 -159
- package/src/types/global.ts +0 -21
- package/src/types/index.ts +0 -44
- package/src/types/model-data.ts +0 -77
- package/src/types/model-deployment.ts +0 -78
- package/src/types/model-dump.ts +0 -79
- package/src/types/model-logical.ts +0 -126
- package/src/types/model-spec.ts +0 -88
- package/src/types/operators.ts +0 -156
- package/src/types/project.ts +0 -14
- package/src/types/scalar.ts +0 -162
- package/src/types/styles.ts +0 -122
- package/src/types/view-changes.ts +0 -32
- package/src/types/view-common.ts +0 -157
- package/src/types/view-computed.ts +0 -126
- package/src/types/view-layouted.ts +0 -94
- package/src/types/view-parsed.deployment.ts +0 -39
- package/src/types/view-parsed.dynamic.ts +0 -89
- package/src/types/view-parsed.element.ts +0 -65
- package/src/types/view.ts +0 -113
- package/src/utils/common-head.ts +0 -22
- package/src/utils/compare-natural.ts +0 -60
- package/src/utils/const.ts +0 -1
- package/src/utils/fqn.ts +0 -302
- package/src/utils/getOrCreate.ts +0 -8
- package/src/utils/guards.ts +0 -9
- package/src/utils/index.ts +0 -68
- package/src/utils/invariant.ts +0 -29
- package/src/utils/iterable/_types.ts +0 -5
- package/src/utils/iterable/filter.ts +0 -55
- package/src/utils/iterable/find.ts +0 -38
- package/src/utils/iterable/first.ts +0 -48
- package/src/utils/iterable/flat.ts +0 -17
- package/src/utils/iterable/head.ts +0 -22
- package/src/utils/iterable/index.ts +0 -15
- package/src/utils/iterable/map.ts +0 -40
- package/src/utils/iterable/reduce.ts +0 -24
- package/src/utils/iterable/some.ts +0 -36
- package/src/utils/iterable/to.ts +0 -19
- package/src/utils/iterable/unique.ts +0 -23
- package/src/utils/markdown/index.ts +0 -2
- package/src/utils/markdown/to-html.ts +0 -32
- package/src/utils/markdown/to-text.ts +0 -14
- package/src/utils/memoize-prop.ts +0 -30
- package/src/utils/mnemonist.ts +0 -9
- package/src/utils/object-hash.ts +0 -14
- package/src/utils/promises.ts +0 -24
- package/src/utils/relations.ts +0 -36
- package/src/utils/set.ts +0 -83
- package/src/utils/string-hash.ts +0 -16
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { Connection } from '../../model'
|
|
2
|
-
import { isAncestor, sortParentsFirst } from '../../utils'
|
|
3
|
-
import { toArray } from '../../utils/iterable'
|
|
4
|
-
import { DefaultMap } from '../../utils/mnemonist'
|
|
5
|
-
import type { AnyCtx, ComputeMemory, CtxElement } from './_types'
|
|
6
|
-
|
|
7
|
-
export function treeFromMemoryState<T extends AnyCtx, M extends ComputeMemory<T> = ComputeMemory<T>>(
|
|
8
|
-
memory: M,
|
|
9
|
-
filter: 'all' | 'final' = 'final',
|
|
10
|
-
) {
|
|
11
|
-
const sorted = sortParentsFirst(toArray(filter === 'final' ? memory.final : memory.elements))
|
|
12
|
-
const connected = new Set(memory.connections.flatMap(c => [c.source, c.target]))
|
|
13
|
-
const root = new Set(sorted)
|
|
14
|
-
const parents = new DefaultMap<CtxElement<T>, CtxElement<T> | null>(() => null)
|
|
15
|
-
const children = sorted.reduce((acc, parent, index, all) => {
|
|
16
|
-
acc.set(
|
|
17
|
-
parent,
|
|
18
|
-
all
|
|
19
|
-
.slice(index + 1)
|
|
20
|
-
.filter(e => isAncestor(parent, e))
|
|
21
|
-
.map(e => {
|
|
22
|
-
root.delete(e)
|
|
23
|
-
return e
|
|
24
|
-
})
|
|
25
|
-
.reduce((acc, el) => {
|
|
26
|
-
if (!acc.some(e => isAncestor(e, el))) {
|
|
27
|
-
acc.push(el)
|
|
28
|
-
parents.set(el, parent)
|
|
29
|
-
}
|
|
30
|
-
return acc
|
|
31
|
-
}, [] as CtxElement<T>[]),
|
|
32
|
-
)
|
|
33
|
-
return acc
|
|
34
|
-
}, new DefaultMap<CtxElement<T>, ReadonlyArray<CtxElement<T>>>(() => []))
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
root: root as ReadonlySet<CtxElement<T>>,
|
|
38
|
-
connected: connected as ReadonlySet<CtxElement<T>>,
|
|
39
|
-
hasInOut: (el: CtxElement<T>) => memory.connections.some(Connection.isAnyInOut(el.id)),
|
|
40
|
-
parent: (el: CtxElement<T>) => parents.get(el),
|
|
41
|
-
children: (el: CtxElement<T>) => children.get(el),
|
|
42
|
-
}
|
|
43
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { ElementModel, RelationshipModel } from '../../model'
|
|
2
|
-
import type { AnyAux, Unknown } from '../../types'
|
|
3
|
-
|
|
4
|
-
// type Pair = {
|
|
5
|
-
// left: ReadonlySet<ElementModel>
|
|
6
|
-
// relationships: ReadonlySet<RelationshipModel>
|
|
7
|
-
// right: ReadonlySet<ElementModel>
|
|
8
|
-
// }
|
|
9
|
-
|
|
10
|
-
export type RelationshipsViewData<M extends AnyAux = Unknown> = {
|
|
11
|
-
incomers: ReadonlySet<ElementModel<M>>
|
|
12
|
-
incoming: ReadonlySet<RelationshipModel<M>>
|
|
13
|
-
subjects: ReadonlySet<ElementModel<M>>
|
|
14
|
-
outgoing: ReadonlySet<RelationshipModel<M>>
|
|
15
|
-
outgoers: ReadonlySet<ElementModel<M>>
|
|
16
|
-
}
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { pipe } from 'remeda'
|
|
2
|
-
import type { ElementModel, LikeC4Model, LikeC4ViewModel, RelationshipModel } from '../../model'
|
|
3
|
-
import type { AnyAux, aux } from '../../types'
|
|
4
|
-
import { invariant } from '../../utils'
|
|
5
|
-
import { isDescendantOf, sortParentsFirst } from '../../utils/fqn'
|
|
6
|
-
import { ifilter, imap, toArray, toSet } from '../../utils/iterable'
|
|
7
|
-
import type { RelationshipsViewData } from './_types'
|
|
8
|
-
import { treeFromElements } from './utils'
|
|
9
|
-
|
|
10
|
-
const finalize = <M extends AnyAux>(
|
|
11
|
-
elements: Set<ElementModel<M>>,
|
|
12
|
-
explicits: Set<ElementModel<M>>,
|
|
13
|
-
): Set<ElementModel<M>> => {
|
|
14
|
-
if (elements.size > 2 && explicits.size !== elements.size) {
|
|
15
|
-
return new Set(sortParentsFirst([
|
|
16
|
-
...treeFromElements(elements).flatten(),
|
|
17
|
-
...explicits,
|
|
18
|
-
]))
|
|
19
|
-
}
|
|
20
|
-
if (elements.size > 1) {
|
|
21
|
-
return new Set(sortParentsFirst([...elements]))
|
|
22
|
-
}
|
|
23
|
-
return elements
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function computeRelationships<const M extends AnyAux>(
|
|
27
|
-
subject: ElementModel<M>,
|
|
28
|
-
others: Set<ElementModel<M>>,
|
|
29
|
-
relationships: {
|
|
30
|
-
incoming: ReadonlyArray<RelationshipModel<M>>
|
|
31
|
-
outgoing: ReadonlyArray<RelationshipModel<M>>
|
|
32
|
-
},
|
|
33
|
-
): Omit<RelationshipsViewData<M>, 'subjectExistsInScope'> {
|
|
34
|
-
const isOther = (e: ElementModel<M>) => others.has(e)
|
|
35
|
-
let subjects = new Set([subject])
|
|
36
|
-
const explicits = {
|
|
37
|
-
incomers: new Set<ElementModel<M>>(),
|
|
38
|
-
subjects: new Set([subject]),
|
|
39
|
-
outgoers: new Set<ElementModel<M>>(),
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Iterate over incoming and outgoing relationships
|
|
43
|
-
// and add the source and target elements to appropriate sets.
|
|
44
|
-
// If element is deeper in the hierarchy - add all ancestors until that is a sibling of the subject
|
|
45
|
-
let incomers = new Set(relationships.incoming.flatMap((r) => {
|
|
46
|
-
explicits.subjects.add(r.target)
|
|
47
|
-
explicits.incomers.add(r.source)
|
|
48
|
-
|
|
49
|
-
subjects.add(r.target)
|
|
50
|
-
if (r.target !== subject) {
|
|
51
|
-
let target = r.target.parent
|
|
52
|
-
while (target && target !== subject) {
|
|
53
|
-
subjects.add(target)
|
|
54
|
-
target = target.parent
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
let source = r.source
|
|
59
|
-
const incomerBranch = [] as ElementModel<M>[]
|
|
60
|
-
while (true) {
|
|
61
|
-
incomerBranch.push(source)
|
|
62
|
-
if (isOther(source) || !source.parent) {
|
|
63
|
-
break
|
|
64
|
-
}
|
|
65
|
-
source = source.parent
|
|
66
|
-
}
|
|
67
|
-
return incomerBranch
|
|
68
|
-
}))
|
|
69
|
-
|
|
70
|
-
let outgoers = new Set(relationships.outgoing.flatMap((r) => {
|
|
71
|
-
explicits.subjects.add(r.source)
|
|
72
|
-
explicits.outgoers.add(r.target)
|
|
73
|
-
|
|
74
|
-
subjects.add(r.source)
|
|
75
|
-
if (r.source !== subject) {
|
|
76
|
-
let source = r.source.parent
|
|
77
|
-
while (source && source !== subject) {
|
|
78
|
-
subjects.add(source)
|
|
79
|
-
source = source.parent
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
let target = r.target
|
|
84
|
-
const outgoerBranch = [] as ElementModel<M>[]
|
|
85
|
-
while (true) {
|
|
86
|
-
outgoerBranch.push(target)
|
|
87
|
-
if (isOther(target) || !target.parent) {
|
|
88
|
-
break
|
|
89
|
-
}
|
|
90
|
-
target = target.parent
|
|
91
|
-
}
|
|
92
|
-
return outgoerBranch
|
|
93
|
-
}))
|
|
94
|
-
|
|
95
|
-
return {
|
|
96
|
-
incomers: finalize(incomers, explicits.incomers),
|
|
97
|
-
incoming: new Set(relationships.incoming),
|
|
98
|
-
subjects: finalize(subjects, explicits.subjects),
|
|
99
|
-
outgoing: new Set(relationships.outgoing),
|
|
100
|
-
outgoers: finalize(outgoers, explicits.outgoers),
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function computeRelationshipsView<M extends AnyAux>(
|
|
105
|
-
subjectId: aux.ElementId<M>,
|
|
106
|
-
likec4model: LikeC4Model<M>,
|
|
107
|
-
scopeViewId: aux.ViewId<M> | null,
|
|
108
|
-
scope: 'global' | 'view' = 'global',
|
|
109
|
-
): RelationshipsViewData<M> {
|
|
110
|
-
let subjectExistsInScope = true
|
|
111
|
-
const view = scopeViewId ? likec4model.findView(scopeViewId) : null
|
|
112
|
-
if (scope === 'view') {
|
|
113
|
-
invariant(
|
|
114
|
-
view,
|
|
115
|
-
'Scope view id is required when scope is "view"',
|
|
116
|
-
)
|
|
117
|
-
return computeScopedRelationshipsView(subjectId, view, likec4model)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const subject = likec4model.element(subjectId)
|
|
121
|
-
const subjectSiblings = toSet(subject.ascendingSiblings())
|
|
122
|
-
return computeRelationships(
|
|
123
|
-
subject,
|
|
124
|
-
subjectSiblings,
|
|
125
|
-
{
|
|
126
|
-
incoming: [...subject.incoming()],
|
|
127
|
-
outgoing: [...subject.outgoing()],
|
|
128
|
-
},
|
|
129
|
-
)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function computeScopedRelationshipsView<M extends AnyAux>(
|
|
133
|
-
subjectId: NoInfer<aux.ElementId<M>>,
|
|
134
|
-
view: LikeC4ViewModel<M>,
|
|
135
|
-
likec4model: LikeC4Model<M>,
|
|
136
|
-
): RelationshipsViewData<M> {
|
|
137
|
-
const subject = likec4model.element(subjectId)
|
|
138
|
-
let relationships = {
|
|
139
|
-
incoming: toArray(ifilter(subject.incoming(), (r) => view.includesRelation(r.id))),
|
|
140
|
-
outgoing: toArray(ifilter(subject.outgoing(), (r) => view.includesRelation(r.id))),
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const isDescendant = isDescendantOf(subject)
|
|
144
|
-
|
|
145
|
-
const others = new Set([
|
|
146
|
-
...subject.ascendingSiblings(),
|
|
147
|
-
...pipe(
|
|
148
|
-
view.elements(),
|
|
149
|
-
imap((e) => e.element),
|
|
150
|
-
ifilter((e): e is ElementModel<M> => e !== subject && isDescendant(e)),
|
|
151
|
-
),
|
|
152
|
-
])
|
|
153
|
-
|
|
154
|
-
return computeRelationships(
|
|
155
|
-
subject,
|
|
156
|
-
others,
|
|
157
|
-
relationships,
|
|
158
|
-
)
|
|
159
|
-
}
|
|
@@ -1,358 +0,0 @@
|
|
|
1
|
-
import dagre, { type EdgeConfig, type GraphLabel } from '@dagrejs/dagre'
|
|
2
|
-
import { concat, filter, forEachObj, groupBy, map, mapToObj, pipe, prop, reduce, tap } from 'remeda'
|
|
3
|
-
import type { ElementModel } from '../../model/ElementModel'
|
|
4
|
-
import type { RelationshipModel } from '../../model/RelationModel'
|
|
5
|
-
import { type DiagramNode, type DiagramView, type Fqn, type NodeId, omitUndefined } from '../../types'
|
|
6
|
-
import { invariant, sortParentsFirst } from '../../utils'
|
|
7
|
-
import { toArray } from '../../utils/iterable'
|
|
8
|
-
import { DefaultMap } from '../../utils/mnemonist'
|
|
9
|
-
import type { RelationshipsViewData } from './_types'
|
|
10
|
-
import { treeFromElements } from './utils'
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* All constants related to the layout
|
|
14
|
-
*/
|
|
15
|
-
const Sizes = {
|
|
16
|
-
dagre: {
|
|
17
|
-
ranksep: 60,
|
|
18
|
-
nodesep: 35,
|
|
19
|
-
edgesep: 25,
|
|
20
|
-
} satisfies GraphLabel,
|
|
21
|
-
edgeLabel: {
|
|
22
|
-
width: 120,
|
|
23
|
-
height: 10,
|
|
24
|
-
minlen: 1,
|
|
25
|
-
weight: 1,
|
|
26
|
-
} satisfies EdgeConfig,
|
|
27
|
-
|
|
28
|
-
emptyNodeOffset: 120,
|
|
29
|
-
|
|
30
|
-
nodeWidth: 320,
|
|
31
|
-
nodeHeight: 180,
|
|
32
|
-
|
|
33
|
-
// Spacer between elements in a compound node
|
|
34
|
-
// 0 means no spacer
|
|
35
|
-
spacerHeight: 0,
|
|
36
|
-
|
|
37
|
-
compound: {
|
|
38
|
-
labelHeight: 1,
|
|
39
|
-
paddingTop: 60,
|
|
40
|
-
paddingBottom: 32,
|
|
41
|
-
},
|
|
42
|
-
}
|
|
43
|
-
type NodeData = {
|
|
44
|
-
portId: string
|
|
45
|
-
element: ElementModel<any>
|
|
46
|
-
isCompound: boolean
|
|
47
|
-
}
|
|
48
|
-
function createGraph() {
|
|
49
|
-
const g = new dagre.graphlib.Graph<NodeData>({
|
|
50
|
-
directed: true,
|
|
51
|
-
compound: true,
|
|
52
|
-
multigraph: true,
|
|
53
|
-
})
|
|
54
|
-
g.setGraph({
|
|
55
|
-
...Sizes.dagre,
|
|
56
|
-
rankdir: 'LR',
|
|
57
|
-
})
|
|
58
|
-
g.setDefaultEdgeLabel(() => ({ ...Sizes.edgeLabel }))
|
|
59
|
-
g.setDefaultNodeLabel(() => ({}))
|
|
60
|
-
|
|
61
|
-
// columns.reduce((prev, column) => {
|
|
62
|
-
// const c = graphColumn(column)
|
|
63
|
-
// g.setNode(c.id, {})
|
|
64
|
-
// g.setNode(c.anchor, { width: 40, height: 2 })
|
|
65
|
-
// g.setParent(c.anchor, c.id)
|
|
66
|
-
// if (prev) {
|
|
67
|
-
// g.setEdge(prev, c.anchor, {
|
|
68
|
-
// width: 0
|
|
69
|
-
// })
|
|
70
|
-
// }
|
|
71
|
-
// return c.anchor
|
|
72
|
-
// }, null as string | null)
|
|
73
|
-
|
|
74
|
-
return g
|
|
75
|
-
}
|
|
76
|
-
type G = ReturnType<typeof createGraph>
|
|
77
|
-
|
|
78
|
-
const PortSuffix = '-port'
|
|
79
|
-
|
|
80
|
-
function createNodes(
|
|
81
|
-
prefix: string,
|
|
82
|
-
elements: ReadonlySet<ElementModel<any>>,
|
|
83
|
-
g: G,
|
|
84
|
-
) {
|
|
85
|
-
const graphNodes = new DefaultMap<Fqn, { id: string; portId: string }>(key => ({
|
|
86
|
-
id: `${prefix}-${key}`,
|
|
87
|
-
portId: `${prefix}-${key}`,
|
|
88
|
-
}))
|
|
89
|
-
const tree = treeFromElements(elements)
|
|
90
|
-
for (const element of tree.sorted) {
|
|
91
|
-
const isCompound = tree.children(element).length > 0
|
|
92
|
-
const fqn = element.id
|
|
93
|
-
const id = `${prefix}-${fqn}`
|
|
94
|
-
const portId = isCompound ? `${id}${PortSuffix}` : id
|
|
95
|
-
graphNodes.set(fqn, {
|
|
96
|
-
id,
|
|
97
|
-
portId,
|
|
98
|
-
})
|
|
99
|
-
g.setNode(id, {
|
|
100
|
-
element,
|
|
101
|
-
isCompound,
|
|
102
|
-
portId,
|
|
103
|
-
width: Sizes.nodeWidth,
|
|
104
|
-
height: Sizes.nodeHeight,
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
if (isCompound) {
|
|
108
|
-
g.setNode(portId, {
|
|
109
|
-
element,
|
|
110
|
-
portId,
|
|
111
|
-
isCompound,
|
|
112
|
-
width: Sizes.nodeWidth - Sizes.dagre.ranksep,
|
|
113
|
-
height: Sizes.compound.labelHeight,
|
|
114
|
-
})
|
|
115
|
-
g.setParent(portId, id)
|
|
116
|
-
// g.node(id).padding = 60
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const parent = tree.parent(element)
|
|
120
|
-
if (parent) {
|
|
121
|
-
g.setParent(id, `${prefix}-${parent.id}`)
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return {
|
|
125
|
-
...tree,
|
|
126
|
-
byId: (id: string) => {
|
|
127
|
-
const element = tree.byId(id)
|
|
128
|
-
const graph = graphNodes.get(element.id)
|
|
129
|
-
return {
|
|
130
|
-
element,
|
|
131
|
-
graph,
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
graphNodes,
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Apply dagre layout to the graph
|
|
140
|
-
* And return a function to get node bounds for xyflow
|
|
141
|
-
*/
|
|
142
|
-
function applyDagreLayout(g: G) {
|
|
143
|
-
dagre.layout(g, {
|
|
144
|
-
// disableOptimalOrderHeuristic: true,
|
|
145
|
-
})
|
|
146
|
-
return (nodeId: string) => {
|
|
147
|
-
const node = g.node(nodeId)
|
|
148
|
-
const { x, y, width, height } = node
|
|
149
|
-
return {
|
|
150
|
-
position: {
|
|
151
|
-
x: x - Math.round(width / 2),
|
|
152
|
-
y: y - Math.round(height / 2),
|
|
153
|
-
},
|
|
154
|
-
width,
|
|
155
|
-
height,
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export function layoutRelationshipsView(data: RelationshipsViewData): Pick<DiagramView, 'nodes' | 'edges' | 'bounds'> {
|
|
161
|
-
const g = createGraph()
|
|
162
|
-
|
|
163
|
-
const incomers = createNodes('in', data.incomers, g),
|
|
164
|
-
subjects = createNodes('subject', data.subjects, g),
|
|
165
|
-
outgoers = createNodes('out', data.outgoers, g)
|
|
166
|
-
|
|
167
|
-
const edges = [] as Array<{
|
|
168
|
-
name: string
|
|
169
|
-
source: string // node id
|
|
170
|
-
target: string // node id
|
|
171
|
-
relations: RelationshipModel[]
|
|
172
|
-
}>
|
|
173
|
-
|
|
174
|
-
pipe(
|
|
175
|
-
concat(
|
|
176
|
-
pipe(
|
|
177
|
-
toArray(data.incoming),
|
|
178
|
-
map(r => ({
|
|
179
|
-
id: r.source.id,
|
|
180
|
-
source: incomers.byId(r.source.id).graph,
|
|
181
|
-
target: subjects.byId(r.target.id).graph,
|
|
182
|
-
relation: r,
|
|
183
|
-
})),
|
|
184
|
-
// Sort by source
|
|
185
|
-
sortParentsFirst,
|
|
186
|
-
),
|
|
187
|
-
pipe(
|
|
188
|
-
toArray(data.outgoing),
|
|
189
|
-
map(r => ({
|
|
190
|
-
id: r.target.id,
|
|
191
|
-
source: subjects.byId(r.source.id).graph,
|
|
192
|
-
target: outgoers.byId(r.target.id).graph,
|
|
193
|
-
relation: r,
|
|
194
|
-
})),
|
|
195
|
-
// Sort by target
|
|
196
|
-
sortParentsFirst,
|
|
197
|
-
),
|
|
198
|
-
),
|
|
199
|
-
map(r => ({
|
|
200
|
-
...r,
|
|
201
|
-
expr: `${r.source.id}->${r.target.id}`,
|
|
202
|
-
})),
|
|
203
|
-
// Group if same source and target
|
|
204
|
-
groupBy(prop('expr')),
|
|
205
|
-
forEachObj((grouped) => {
|
|
206
|
-
const source = grouped[0].source
|
|
207
|
-
const target = grouped[0].target
|
|
208
|
-
const name = grouped[0].expr
|
|
209
|
-
|
|
210
|
-
g.setEdge(source.portId, target.portId, {
|
|
211
|
-
...Sizes.edgeLabel,
|
|
212
|
-
}, name)
|
|
213
|
-
edges.push({
|
|
214
|
-
name,
|
|
215
|
-
source: source.id,
|
|
216
|
-
target: target.id,
|
|
217
|
-
relations: map(grouped, prop('relation')),
|
|
218
|
-
})
|
|
219
|
-
}),
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
// Grow nodes with more than 2 ports
|
|
223
|
-
for (const subjectNode of subjects.graphNodes.values()) {
|
|
224
|
-
const nodeId = subjectNode.id
|
|
225
|
-
const node = g.node(nodeId)
|
|
226
|
-
if (node.isCompound) {
|
|
227
|
-
continue
|
|
228
|
-
}
|
|
229
|
-
const edgeCount = Math.max(g.inEdges(nodeId)?.length ?? 0, g.outEdges(nodeId)?.length ?? 0)
|
|
230
|
-
if (edgeCount > 2) {
|
|
231
|
-
node.height = node.height + (edgeCount - 3) * 14
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
const dagreBounds = applyDagreLayout(g)
|
|
236
|
-
|
|
237
|
-
const nodeIds = [
|
|
238
|
-
...incomers.graphNodes.values(),
|
|
239
|
-
...subjects.graphNodes.values(),
|
|
240
|
-
...outgoers.graphNodes.values(),
|
|
241
|
-
]
|
|
242
|
-
|
|
243
|
-
// Calculate bounds for all nodes except compounds
|
|
244
|
-
// We shrink compounds to fit their children
|
|
245
|
-
const _calculatedNodeBounds = pipe(
|
|
246
|
-
nodeIds,
|
|
247
|
-
// Compound nodes have different portId
|
|
248
|
-
filter(n => n.id === n.portId),
|
|
249
|
-
mapToObj(n => [n.id, dagreBounds(n.id)]),
|
|
250
|
-
)
|
|
251
|
-
|
|
252
|
-
function nodeBounds(nodeId: string): ReturnType<typeof dagreBounds> {
|
|
253
|
-
return _calculatedNodeBounds[nodeId] ??= pipe(
|
|
254
|
-
g.children(nodeId) as string[] | undefined ?? [],
|
|
255
|
-
filter(id => !id.endsWith(PortSuffix)),
|
|
256
|
-
map(id => nodeBounds(id)),
|
|
257
|
-
tap(bounds => {
|
|
258
|
-
invariant(bounds.length > 0, `Node ${nodeId} has no nested nodes`)
|
|
259
|
-
}),
|
|
260
|
-
reduce((acc, bounds) => {
|
|
261
|
-
return {
|
|
262
|
-
minY: Math.min(acc.minY, bounds.position.y),
|
|
263
|
-
maxY: Math.max(acc.maxY, bounds.position.y + bounds.height),
|
|
264
|
-
}
|
|
265
|
-
}, { minY: Infinity, maxY: -Infinity }),
|
|
266
|
-
({ minY, maxY }) => {
|
|
267
|
-
const {
|
|
268
|
-
position: { x },
|
|
269
|
-
width,
|
|
270
|
-
} = dagreBounds(nodeId)
|
|
271
|
-
minY = minY - Sizes.compound.paddingTop
|
|
272
|
-
maxY = maxY + Sizes.compound.paddingBottom
|
|
273
|
-
|
|
274
|
-
return {
|
|
275
|
-
position: {
|
|
276
|
-
x,
|
|
277
|
-
y: minY,
|
|
278
|
-
},
|
|
279
|
-
width,
|
|
280
|
-
height: maxY - minY,
|
|
281
|
-
}
|
|
282
|
-
},
|
|
283
|
-
)
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function nodeLevel(nodeId: string): number {
|
|
287
|
-
const parent = g.parent(nodeId)
|
|
288
|
-
if (parent) {
|
|
289
|
-
return nodeLevel(parent) + 1
|
|
290
|
-
}
|
|
291
|
-
return 0
|
|
292
|
-
}
|
|
293
|
-
function nodeDepth(nodeId: string): number {
|
|
294
|
-
const children = g.children(nodeId) as string[] | undefined ?? []
|
|
295
|
-
if (children.length === 0) {
|
|
296
|
-
return 0
|
|
297
|
-
}
|
|
298
|
-
return 1 + Math.max(...children.map(nodeDepth))
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const nodes = nodeIds.map(({ id }): DiagramNode => {
|
|
302
|
-
const { element } = g.node(id)
|
|
303
|
-
let { position, width, height } = nodeBounds(id)
|
|
304
|
-
const parentId = g.parent(id)
|
|
305
|
-
// if (parentId) {
|
|
306
|
-
// const parentPos = nodeBounds(parentId).position
|
|
307
|
-
// position = {
|
|
308
|
-
// x: position.x - parentPos.x,
|
|
309
|
-
// y: position.y - parentPos.y,
|
|
310
|
-
// }
|
|
311
|
-
// }
|
|
312
|
-
const children = (g.children(id) as NodeId[] | undefined ?? []).filter(c => !c.endsWith(PortSuffix))
|
|
313
|
-
|
|
314
|
-
return omitUndefined({
|
|
315
|
-
id: id as NodeId,
|
|
316
|
-
parent: parentId as NodeId ?? null,
|
|
317
|
-
title: element.title,
|
|
318
|
-
x: position.x,
|
|
319
|
-
y: position.y,
|
|
320
|
-
position: [position.x, position.y],
|
|
321
|
-
description: element.$element.summary ?? element.$element.description ?? null,
|
|
322
|
-
technology: element.technology,
|
|
323
|
-
tags: [],
|
|
324
|
-
links: null,
|
|
325
|
-
color: element.color,
|
|
326
|
-
shape: element.shape,
|
|
327
|
-
modelRef: element.id,
|
|
328
|
-
kind: element.kind,
|
|
329
|
-
level: nodeLevel(id),
|
|
330
|
-
labelBBox: {
|
|
331
|
-
x: position.x,
|
|
332
|
-
y: position.y,
|
|
333
|
-
width: width,
|
|
334
|
-
height: height,
|
|
335
|
-
},
|
|
336
|
-
style: {
|
|
337
|
-
...element.$element.style,
|
|
338
|
-
},
|
|
339
|
-
inEdges: [],
|
|
340
|
-
outEdges: [],
|
|
341
|
-
depth: children.length > 0 ? nodeDepth(id) : 0,
|
|
342
|
-
children,
|
|
343
|
-
width,
|
|
344
|
-
height,
|
|
345
|
-
})
|
|
346
|
-
})
|
|
347
|
-
|
|
348
|
-
return {
|
|
349
|
-
bounds: {
|
|
350
|
-
x: 0,
|
|
351
|
-
y: 0,
|
|
352
|
-
width: g.graph().width ?? 100,
|
|
353
|
-
height: g.graph().height ?? 100,
|
|
354
|
-
},
|
|
355
|
-
nodes,
|
|
356
|
-
edges: [],
|
|
357
|
-
}
|
|
358
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import type { ElementModel } from '../../model/ElementModel'
|
|
2
|
-
import type { AnyAux, aux } from '../../types'
|
|
3
|
-
import { nonNullable } from '../../utils'
|
|
4
|
-
import { isAncestor, isDescendantOf, sortParentsFirst } from '../../utils/fqn'
|
|
5
|
-
import { DefaultMap } from '../../utils/mnemonist'
|
|
6
|
-
|
|
7
|
-
export function treeFromElements<M extends AnyAux>(elements: Iterable<ElementModel<M>>) {
|
|
8
|
-
const sorted = sortParentsFirst([...elements]) as ReadonlyArray<ElementModel<M>>
|
|
9
|
-
const root = new Set(sorted)
|
|
10
|
-
const map = new Map(sorted.map(e => [e._literalId, e]))
|
|
11
|
-
const parents = new DefaultMap<ElementModel<M>, ElementModel<M> | null>(() => null)
|
|
12
|
-
const children = sorted.reduce((acc, parent, index, all) => {
|
|
13
|
-
acc.set(
|
|
14
|
-
parent,
|
|
15
|
-
all
|
|
16
|
-
.slice(index + 1)
|
|
17
|
-
.filter(isDescendantOf(parent))
|
|
18
|
-
.map(e => {
|
|
19
|
-
root.delete(e)
|
|
20
|
-
return e
|
|
21
|
-
})
|
|
22
|
-
.reduce((acc, el) => {
|
|
23
|
-
if (!acc.some(isAncestor(el))) {
|
|
24
|
-
acc.push(el)
|
|
25
|
-
parents.set(el, parent)
|
|
26
|
-
}
|
|
27
|
-
return acc
|
|
28
|
-
}, [] as ElementModel<M>[]),
|
|
29
|
-
)
|
|
30
|
-
return acc
|
|
31
|
-
}, new DefaultMap<ElementModel<M>, ElementModel<M>[]>(() => []))
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
sorted,
|
|
35
|
-
byId: (id: aux.ElementId<M>): ElementModel<M> => nonNullable(map.get(id), `Element not found by id: ${id}`),
|
|
36
|
-
root: root as ReadonlySet<ElementModel<M>>,
|
|
37
|
-
parent: (el: ElementModel<M>): ElementModel<M> | null => parents.get(el),
|
|
38
|
-
children: (el: ElementModel<M>): ReadonlyArray<ElementModel<M>> => children.get(el),
|
|
39
|
-
/**
|
|
40
|
-
* Flattens the tree structure by removing redundant hierarchy levels.
|
|
41
|
-
* @example
|
|
42
|
-
* A
|
|
43
|
-
* └── B
|
|
44
|
-
* ├── C
|
|
45
|
-
* │ └── D
|
|
46
|
-
* │ └── E
|
|
47
|
-
* └── F
|
|
48
|
-
* └── G
|
|
49
|
-
* becomes
|
|
50
|
-
* A
|
|
51
|
-
* ├── C
|
|
52
|
-
* │ └── E
|
|
53
|
-
* └── F
|
|
54
|
-
* └── G
|
|
55
|
-
*/
|
|
56
|
-
flatten: (): Set<ElementModel<M>> => {
|
|
57
|
-
return new Set([
|
|
58
|
-
...root,
|
|
59
|
-
...sorted.reduce((acc, el) => {
|
|
60
|
-
const _children = children.get(el)
|
|
61
|
-
if (_children.length === 0) {
|
|
62
|
-
acc.push(el)
|
|
63
|
-
return acc
|
|
64
|
-
}
|
|
65
|
-
if (_children.length > 1) {
|
|
66
|
-
acc.push(..._children)
|
|
67
|
-
}
|
|
68
|
-
return acc
|
|
69
|
-
}, [] as ElementModel<M>[]),
|
|
70
|
-
])
|
|
71
|
-
},
|
|
72
|
-
}
|
|
73
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { AnyAux, ComputedNode } from '../../types'
|
|
2
|
-
import { nonNullable } from '../../utils'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Returns the ancestors of given computed node, starting with the direct parent and ending with the root node.
|
|
6
|
-
*/
|
|
7
|
-
export function ancestorsOfNode<A extends AnyAux>(
|
|
8
|
-
node: ComputedNode<A>,
|
|
9
|
-
nodes: ReadonlyMap<string, ComputedNode<A>>,
|
|
10
|
-
): ReadonlyArray<ComputedNode<A>> {
|
|
11
|
-
const ancestors = [] as ComputedNode<A>[]
|
|
12
|
-
let parentId = node.parent
|
|
13
|
-
while (parentId) {
|
|
14
|
-
const parentNode = nonNullable(nodes.get(parentId), `Parent node ${parentId} not found`)
|
|
15
|
-
ancestors.push(parentNode)
|
|
16
|
-
parentId = parentNode.parent
|
|
17
|
-
}
|
|
18
|
-
return ancestors
|
|
19
|
-
}
|