@likec4/generators 1.52.0 → 1.53.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/dist/_chunks/chunk.mjs +11 -0
- package/dist/index.d.mts +18 -1
- package/dist/index.mjs +189 -47
- package/dist/likec4/index.d.mts +277169 -0
- package/dist/likec4/index.mjs +1799 -0
- package/likec4/package.json +4 -0
- package/package.json +22 -8
- package/src/drawio/generate-drawio.ts +73 -8
- package/src/drawio/index.ts +1 -0
- package/src/drawio/parse-drawio.ts +172 -18
- package/src/index.ts +2 -0
- package/src/likec4/generate-likec4.ts +72 -0
- package/src/likec4/index.ts +12 -0
- package/src/likec4/operators/base.ts +938 -0
- package/src/likec4/operators/deployment.ts +263 -0
- package/src/likec4/operators/expressions.ts +422 -0
- package/src/likec4/operators/index.ts +13 -0
- package/src/likec4/operators/likec4data.ts +33 -0
- package/src/likec4/operators/model.ts +222 -0
- package/src/likec4/operators/properties.ts +244 -0
- package/src/likec4/operators/specification.ts +119 -0
- package/src/likec4/operators/views.ts +390 -0
- package/src/likec4/schemas/common.ts +123 -0
- package/src/likec4/schemas/deployment.ts +113 -0
- package/src/likec4/schemas/expression.ts +218 -0
- package/src/likec4/schemas/index.ts +83 -0
- package/src/likec4/schemas/likec4data.ts +76 -0
- package/src/likec4/schemas/model.ts +127 -0
- package/src/likec4/schemas/specification.ts +83 -0
- package/src/likec4/schemas/views.ts +321 -0
- package/src/model/generate-likec4.ts +0 -5
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __exportAll = (all, no_symbols) => {
|
|
3
|
+
let target = {};
|
|
4
|
+
for (var name in all) __defProp(target, name, {
|
|
5
|
+
get: all[name],
|
|
6
|
+
enumerable: true
|
|
7
|
+
});
|
|
8
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
9
|
+
return target;
|
|
10
|
+
};
|
|
11
|
+
export { __exportAll as t };
|
package/dist/index.d.mts
CHANGED
|
@@ -15,6 +15,8 @@ type DrawioViewModelLike = {
|
|
|
15
15
|
$view: ProcessedView<aux.Unknown>;
|
|
16
16
|
readonly $styles?: LikeC4Styles | null;
|
|
17
17
|
};
|
|
18
|
+
/** Draw.io export profile: default (round-trip) or leanix (bridge-managed metadata for LeanIX interoperability). */
|
|
19
|
+
type DrawioExportProfile = 'default' | 'leanix';
|
|
18
20
|
/** Optional overrides for round-trip (e.g. from parsed comment blocks). Keys are node/edge ids from the view. */
|
|
19
21
|
type GenerateDrawioOptions = {
|
|
20
22
|
/** Node id -> bbox to use instead of viewmodel layout */layoutOverride?: Record<string, BBox>; /** Node id -> stroke color hex (e.g. from likec4.strokeColor.vertices comment) */
|
|
@@ -31,6 +33,13 @@ type GenerateDrawioOptions = {
|
|
|
31
33
|
* Set for deterministic output (e.g. tests, content-addressable storage).
|
|
32
34
|
*/
|
|
33
35
|
modified?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Export profile. When 'leanix', adds bridge-managed metadata (likec4Id, likec4Kind, likec4ViewId,
|
|
38
|
+
* likec4ProjectId, likec4RelationId, bridgeManaged) for round-trip and LeanIX interoperability.
|
|
39
|
+
*/
|
|
40
|
+
profile?: DrawioExportProfile; /** Project id (included when profile is 'leanix' as likec4ProjectId). */
|
|
41
|
+
projectId?: string; /** Optional mapping of element kind -> LeanIX fact sheet type (included when profile is 'leanix' as leanixFactSheetType on vertices). */
|
|
42
|
+
leanixFactSheetTypeByKind?: Record<string, string>;
|
|
34
43
|
};
|
|
35
44
|
/**
|
|
36
45
|
* Generate a single DrawIO file from one view.
|
|
@@ -79,6 +88,14 @@ declare function buildDrawioExportOptionsForViews(viewIds: string[], sourceConte
|
|
|
79
88
|
declare function generateDrawioEditUrl(xml: string): string;
|
|
80
89
|
//#endregion
|
|
81
90
|
//#region src/drawio/parse-drawio.d.ts
|
|
91
|
+
/**
|
|
92
|
+
* Decompress draw.io diagram content: base64 → inflateRaw → decodeURIComponent.
|
|
93
|
+
* Exported for tests (error message contract). Handles both Node (Buffer) and browser (atob).
|
|
94
|
+
* @param base64Content - Compressed diagram string from <diagram> inner content.
|
|
95
|
+
* @returns Decoded mxGraphModel XML string.
|
|
96
|
+
* @throws Error when base64 decode, inflate, or URI decode fails.
|
|
97
|
+
*/
|
|
98
|
+
declare function decompressDrawioDiagram(base64Content: string): string;
|
|
82
99
|
/**
|
|
83
100
|
* One diagram's name, id and raw or compressed content from mxfile (single-tab or one tab in multi-tab).
|
|
84
101
|
* content is decompressed mxGraphModel XML when the source was compressed.
|
|
@@ -184,4 +201,4 @@ declare function generateViewsDataTs(diagrams: Iterable<DiagramView>): string;
|
|
|
184
201
|
*/
|
|
185
202
|
declare function generateViewsDataDTs(diagrams: Iterable<DiagramView>): string;
|
|
186
203
|
//#endregion
|
|
187
|
-
export { DEFAULT_DRAWIO_ALL_FILENAME, type DrawioViewModelLike, type GenerateDrawioOptions, buildDrawioExportOptionsForViews, buildDrawioExportOptionsFromSource, generateD2, generateDrawio, generateDrawioEditUrl, generateDrawioMulti, generateLikeC4Model, generateMermaid, generatePuml, generateReactNext, generateReactTypes, generateViewsDataDTs, generateViewsDataJs, generateViewsDataTs, getAllDiagrams, parseDrawioRoundtripComments, parseDrawioToLikeC4, parseDrawioToLikeC4Multi };
|
|
204
|
+
export { DEFAULT_DRAWIO_ALL_FILENAME, type DrawioExportProfile, type DrawioViewModelLike, type GenerateDrawioOptions, buildDrawioExportOptionsForViews, buildDrawioExportOptionsFromSource, decompressDrawioDiagram, generateD2, generateDrawio, generateDrawioEditUrl, generateDrawioMulti, generateLikeC4Model, generateMermaid, generatePuml, generateReactNext, generateReactTypes, generateViewsDataDTs, generateViewsDataJs, generateViewsDataTs, getAllDiagrams, parseDrawioRoundtripComments, parseDrawioToLikeC4, parseDrawioToLikeC4Multi };
|
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { LikeC4Styles, nonexhaustive } from "@likec4/core";
|
|
|
4
4
|
import { RichText, flattenMarkdownOrString } from "@likec4/core/types";
|
|
5
5
|
import pako from "pako";
|
|
6
6
|
import JSON5 from "json5";
|
|
7
|
-
import { compareNatural, invariant, sortNaturalByFqn } from "@likec4/core/utils";
|
|
7
|
+
import { compareNatural, invariant as invariant$1, sortNaturalByFqn } from "@likec4/core/utils";
|
|
8
8
|
const capitalizeFirstLetter$2 = (value) => value.charAt(0).toLocaleUpperCase() + value.slice(1);
|
|
9
9
|
const fqnName$2 = (nodeId) => nodeId.split(".").map(capitalizeFirstLetter$2).join("");
|
|
10
10
|
const nodeName$2 = (node) => {
|
|
@@ -165,6 +165,63 @@ function getAttr(attrs, name) {
|
|
|
165
165
|
i = start + 1;
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Fallback to extract style value from attrs when getAttr(attrs, 'style') returns undefined (e.g. spacing like "style =\"").
|
|
170
|
+
* Looks for style="..." (case-insensitive, optional spaces before '=') and returns the quoted value so the cell gets style when present in XML.
|
|
171
|
+
*/
|
|
172
|
+
function extractStyleFromAttrsFallback(attrs) {
|
|
173
|
+
const lower = attrs.toLowerCase();
|
|
174
|
+
let i = lower.indexOf("style");
|
|
175
|
+
while (i !== -1) {
|
|
176
|
+
if (!isAttrBoundaryChar(i === 0 ? " " : attrs[i - 1] ?? " ")) {
|
|
177
|
+
i = lower.indexOf("style", i + 1);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
let j = i + 5;
|
|
181
|
+
while (j < attrs.length && isAttrBoundaryChar(attrs[j] ?? "")) j += 1;
|
|
182
|
+
if (j < attrs.length && attrs[j] === "=") {
|
|
183
|
+
j += 1;
|
|
184
|
+
while (j < attrs.length && isAttrBoundaryChar(attrs[j] ?? "")) j += 1;
|
|
185
|
+
if (j < attrs.length && attrs[j] === "\"") {
|
|
186
|
+
const valueStart = j + 1;
|
|
187
|
+
const valueEnd = attrs.indexOf("\"", valueStart);
|
|
188
|
+
if (valueEnd !== -1) return attrs.slice(valueStart, valueEnd);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
i = lower.indexOf("style", i + 1);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/** Extract style value from full open tag (e.g. from fullTag.slice(0, fullTag.indexOf('>'))). Use when attrs-based extraction missed it. */
|
|
195
|
+
function extractStyleFromOpenTag(fullTag) {
|
|
196
|
+
const gt = fullTag.indexOf(">");
|
|
197
|
+
if (gt === -1) return void 0;
|
|
198
|
+
return getAttr(fullTag.slice(7, gt), "style") ?? extractStyleFromAttrsFallback(fullTag.slice(7, gt));
|
|
199
|
+
}
|
|
200
|
+
/** Max fullTag length to run style re-extraction (avoids scanning huge strings). */
|
|
201
|
+
const MAX_FULLTAG_LENGTH_FOR_STYLE_SCAN = 1e4;
|
|
202
|
+
/** Characters to scan for style=" or style=' when re-extracting from tag (covers open tag). */
|
|
203
|
+
const STYLE_VALUE_SCAN_CHARS = 1500;
|
|
204
|
+
/**
|
|
205
|
+
* Re-extract style attribute value from tag content when getAttr missed it.
|
|
206
|
+
* Scans the first maxScan chars for style="..." or style='...'.
|
|
207
|
+
*/
|
|
208
|
+
function extractStyleFromTagContent(fullTag, maxScan = STYLE_VALUE_SCAN_CHARS) {
|
|
209
|
+
const scan = fullTag.slice(0, maxScan);
|
|
210
|
+
const styleDq = scan.toLowerCase().indexOf("style=\"");
|
|
211
|
+
const styleSq = scan.toLowerCase().indexOf("style='");
|
|
212
|
+
const useDq = styleDq !== -1 && (styleSq === -1 || styleDq <= styleSq);
|
|
213
|
+
const styleIdx = useDq ? styleDq : styleSq;
|
|
214
|
+
const quote = styleIdx !== -1 ? useDq ? "\"" : "'" : "";
|
|
215
|
+
if (styleIdx === -1 || !quote) return void 0;
|
|
216
|
+
const valueStart = styleIdx + 7;
|
|
217
|
+
const valueEnd = scan.indexOf(quote, valueStart);
|
|
218
|
+
return valueEnd !== -1 ? scan.slice(valueStart, valueEnd) : void 0;
|
|
219
|
+
}
|
|
220
|
+
/** True when style or fullTag (lowercased) indicates actor/person shape. */
|
|
221
|
+
function styleOrTagIndicatesActor(style, fullTagLower) {
|
|
222
|
+
const s = style?.toLowerCase() ?? "";
|
|
223
|
+
return s.includes("shape=actor") || s.includes("shape=person") || s.includes("umlactor") || fullTagLower.includes("shape=actor") || fullTagLower.includes("shape=person") || fullTagLower.includes("umlactor");
|
|
224
|
+
}
|
|
168
225
|
/** Find end of XML open tag (first unquoted '>'). Handles both single- and double-quoted attributes. Avoids regex for S5852. */
|
|
169
226
|
function findOpenTagEnd(xml, start) {
|
|
170
227
|
let quoteChar = "";
|
|
@@ -336,12 +393,15 @@ function buildCellOptionalFields(params) {
|
|
|
336
393
|
const relationshipKind = getDecodedStyle(styleMap, "likec4relationshipkind");
|
|
337
394
|
const notation = getDecodedStyle(styleMap, "likec4notation");
|
|
338
395
|
const metadata = getDecodedStyle(styleMap, "likec4metadata");
|
|
396
|
+
const likec4Id = getDecodedStyle(styleMap, "likec4id");
|
|
397
|
+
const likec4RelationId = getDecodedStyle(styleMap, "likec4relationid");
|
|
339
398
|
const optional = {};
|
|
340
399
|
if (params.valueRaw != null && params.valueRaw !== "") optional.value = decodeXmlEntities(params.valueRaw);
|
|
341
400
|
if (params.parent != null && params.parent !== "") optional.parent = params.parent;
|
|
342
401
|
if (params.source != null && params.source !== "") optional.source = params.source;
|
|
343
402
|
if (params.target != null && params.target !== "") optional.target = params.target;
|
|
344
403
|
if (params.style != null && params.style !== "") optional.style = params.style;
|
|
404
|
+
else if (params.styleMap.has("shape")) optional.style = `shape=${params.styleMap.get("shape")};`;
|
|
345
405
|
if (x !== void 0) optional.x = x;
|
|
346
406
|
if (y !== void 0) optional.y = y;
|
|
347
407
|
if (width !== void 0) optional.width = width;
|
|
@@ -372,6 +432,12 @@ function buildCellOptionalFields(params) {
|
|
|
372
432
|
if (relationshipKind != null) optional.relationshipKind = relationshipKind;
|
|
373
433
|
if (notation != null) optional.notation = notation;
|
|
374
434
|
if (metadata != null && edge) optional.metadata = metadata;
|
|
435
|
+
if (likec4Id != null && vertex) optional.likec4Id = likec4Id;
|
|
436
|
+
if (likec4RelationId != null && edge) optional.likec4RelationId = likec4RelationId;
|
|
437
|
+
let shapeFromStyle = params.styleMap.get("shape")?.trim();
|
|
438
|
+
const fullTagLower = params.fullTag.toLowerCase();
|
|
439
|
+
if ((shapeFromStyle == null || shapeFromStyle === "") && vertex && styleOrTagIndicatesActor(params.style, fullTagLower)) shapeFromStyle = "actor";
|
|
440
|
+
if (shapeFromStyle != null && shapeFromStyle !== "" && vertex) optional.shapeFromStyle = shapeFromStyle;
|
|
375
441
|
if (userData.customData != null) optional.customData = userData.customData;
|
|
376
442
|
if (edge) {
|
|
377
443
|
const pts = parseEdgePoints(fullTag);
|
|
@@ -389,29 +455,37 @@ function buildCellFromMxCell(attrs, inner, fullTag, overrides) {
|
|
|
389
455
|
if (!id) return null;
|
|
390
456
|
const vertex = getAttr(attrs, "vertex") === "1";
|
|
391
457
|
const edge = getAttr(attrs, "edge") === "1";
|
|
392
|
-
|
|
458
|
+
let style = getAttr(attrs, "style") ?? extractStyleFromAttrsFallback(attrs) ?? extractStyleFromOpenTag(fullTag);
|
|
459
|
+
const styleMissing = !style || style.trim() === "";
|
|
460
|
+
const tagHasActor = fullTag.length < MAX_FULLTAG_LENGTH_FOR_STYLE_SCAN && fullTag.toLowerCase().includes("shape=actor");
|
|
461
|
+
if ((styleMissing || tagHasActor && !style?.toLowerCase().includes("shape=actor")) && fullTag.length < MAX_FULLTAG_LENGTH_FOR_STYLE_SCAN) {
|
|
462
|
+
const reExtracted = extractStyleFromTagContent(fullTag);
|
|
463
|
+
if (reExtracted != null) style = reExtracted;
|
|
464
|
+
}
|
|
393
465
|
const geomStr = extractMxGeometryOpenTag(fullTag);
|
|
394
|
-
const styleMap = parseStyle(style
|
|
466
|
+
const styleMap = parseStyle(style?.trim() || void 0);
|
|
395
467
|
const userData = parseUserData(inner);
|
|
396
468
|
const navigateTo = overrides?.navigateTo ?? getDecodedStyle(styleMap, "likec4navigateto");
|
|
469
|
+
const optional = buildCellOptionalFields({
|
|
470
|
+
valueRaw: getAttr(attrs, "value"),
|
|
471
|
+
parent: getAttr(attrs, "parent"),
|
|
472
|
+
source: getAttr(attrs, "source"),
|
|
473
|
+
target: getAttr(attrs, "target"),
|
|
474
|
+
style,
|
|
475
|
+
styleMap,
|
|
476
|
+
userData,
|
|
477
|
+
geomStr,
|
|
478
|
+
fullTag,
|
|
479
|
+
vertex,
|
|
480
|
+
edge,
|
|
481
|
+
navigateTo
|
|
482
|
+
});
|
|
397
483
|
return {
|
|
398
484
|
id,
|
|
399
485
|
vertex,
|
|
400
486
|
edge,
|
|
401
|
-
...
|
|
402
|
-
|
|
403
|
-
parent: getAttr(attrs, "parent"),
|
|
404
|
-
source: getAttr(attrs, "source"),
|
|
405
|
-
target: getAttr(attrs, "target"),
|
|
406
|
-
style: getAttr(attrs, "style"),
|
|
407
|
-
styleMap,
|
|
408
|
-
userData,
|
|
409
|
-
geomStr,
|
|
410
|
-
fullTag,
|
|
411
|
-
vertex,
|
|
412
|
-
edge,
|
|
413
|
-
navigateTo
|
|
414
|
-
})
|
|
487
|
+
...style != null && style !== "" ? { style } : {},
|
|
488
|
+
...optional
|
|
415
489
|
};
|
|
416
490
|
}
|
|
417
491
|
/** Extract one mxCell from xml starting at tagStart. Returns attrs, inner, fullTag and next search index, or null. */
|
|
@@ -516,26 +590,32 @@ function likec4LineType(dashed, dashPattern) {
|
|
|
516
590
|
return "dashed";
|
|
517
591
|
}
|
|
518
592
|
}
|
|
593
|
+
/** True when style or shapeFromStyle indicates actor/person (DrawIO shape=actor, shape=person, umlActor). */
|
|
594
|
+
function isActorShapeInStyle(style, shapeFromStyle) {
|
|
595
|
+
const s = style?.toLowerCase() ?? "";
|
|
596
|
+
const shape = shapeFromStyle?.toLowerCase().trim();
|
|
597
|
+
return shape === "actor" || shape === "person" || s.includes("shape=actor") || s.includes("shape=person") || s.includes("umlactor");
|
|
598
|
+
}
|
|
519
599
|
/**
|
|
520
600
|
* Infer LikeC4 element kind from DrawIO shape style. When parent is a container (container=1), child is component.
|
|
521
601
|
* Explicit container=1 in style → system (context box); others default to container unless actor/swimlane.
|
|
602
|
+
* Uses shapeFromStyle when raw style is missing so actor/person is still inferred.
|
|
522
603
|
*/
|
|
523
|
-
function inferKind(style, parentCell) {
|
|
604
|
+
function inferKind(style, parentCell, shapeFromStyle) {
|
|
524
605
|
const s = style?.toLowerCase() ?? "";
|
|
606
|
+
if (isActorShapeInStyle(style, shapeFromStyle)) return "actor";
|
|
525
607
|
switch (true) {
|
|
526
|
-
case !style: return parentCell?.style?.toLowerCase().includes("container=1") ? "component" : "container";
|
|
527
|
-
case s.includes("umlactor") || s.includes("shape=person") || s.includes("shape=actor"): return "actor";
|
|
608
|
+
case !style && !shapeFromStyle: return parentCell?.style?.toLowerCase().includes("container=1") ? "component" : "container";
|
|
528
609
|
case s.includes("swimlane"):
|
|
529
610
|
case s.includes("container=1"): return "system";
|
|
530
611
|
case !!parentCell?.style?.toLowerCase().includes("container=1"): return "component";
|
|
531
612
|
default: return "container";
|
|
532
613
|
}
|
|
533
614
|
}
|
|
534
|
-
/** Infer LikeC4 shape from DrawIO style when possible (person, cylinder, document, etc.). */
|
|
535
|
-
function inferShape(style) {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
if (s.includes("shape=actor") || s.includes("shape=person") || s.includes("umlactor")) return "person";
|
|
615
|
+
/** Infer LikeC4 shape from DrawIO style (or shapeFromStyle) when possible (person, cylinder, document, etc.). */
|
|
616
|
+
function inferShape(style, shapeFromStyle) {
|
|
617
|
+
const s = style?.toLowerCase() ?? "";
|
|
618
|
+
if (isActorShapeInStyle(style, shapeFromStyle)) return "person";
|
|
539
619
|
if (s.includes("shape=cylinder") || s.includes("cylinder3")) return "cylinder";
|
|
540
620
|
if (s.includes("shape=document")) return "document";
|
|
541
621
|
if (s.includes("shape=rectangle") && s.includes("rounded")) return "rectangle";
|
|
@@ -557,10 +637,41 @@ function makeUniqueName(usedNames) {
|
|
|
557
637
|
return n;
|
|
558
638
|
};
|
|
559
639
|
}
|
|
560
|
-
/**
|
|
561
|
-
function
|
|
640
|
+
/** True when s is a syntactically valid dot-separated FQN (each segment non-empty, identifier-like). */
|
|
641
|
+
function isValidFqn(s) {
|
|
642
|
+
if (s.length === 0) return false;
|
|
643
|
+
return s.split(".").every((seg) => /^[a-zA-Z0-9_-]+$/.test(seg));
|
|
644
|
+
}
|
|
645
|
+
/** Depth of vertex from root (0 = root or parent not in diagram). Cycle-safe: cycles in parent graph return 0. */
|
|
646
|
+
function vertexDepth(v, idToVertex, visited = /* @__PURE__ */ new Set()) {
|
|
647
|
+
if (visited.has(v.id)) return 0;
|
|
648
|
+
visited.add(v.id);
|
|
649
|
+
if (v.parent == null || !idToVertex.has(v.parent)) return 0;
|
|
650
|
+
return 1 + vertexDepth(idToVertex.get(v.parent), idToVertex, visited);
|
|
651
|
+
}
|
|
652
|
+
/** True when bridgeId is valid FQN and matches parent chain (root or prefix). */
|
|
653
|
+
function isUsableBridgeId(bridgeId, v, idToFqn, _idToVertex, isRootParent) {
|
|
654
|
+
if (!isValidFqn(bridgeId)) return false;
|
|
655
|
+
const parentFqn = v.parent ? idToFqn.get(v.parent) : void 0;
|
|
656
|
+
if (parentFqn === void 0) return isRootParent(v.parent);
|
|
657
|
+
return bridgeId.startsWith(parentFqn + ".") && bridgeId.length > parentFqn.length + 1;
|
|
658
|
+
}
|
|
659
|
+
/** Assign FQNs to element vertices: bridge-managed likec4Id first (when valid), then root, hierarchy, orphans (DRY). */
|
|
660
|
+
function assignFqnsToElementVertices(idToFqn, elementVertices, containerIdToTitle, isRootParent, uniqueName, usedNames) {
|
|
562
661
|
const baseName = (v) => v.value ?? containerIdToTitle.get(v.id) ?? v.id;
|
|
563
|
-
|
|
662
|
+
const idToVertex = new Map(elementVertices.map((v) => [v.id, v]));
|
|
663
|
+
const byDepth = [...elementVertices].sort((a, b) => vertexDepth(a, idToVertex) - vertexDepth(b, idToVertex));
|
|
664
|
+
for (const v of byDepth) {
|
|
665
|
+
const bridgeId = v.likec4Id?.trim();
|
|
666
|
+
if (bridgeId && isUsableBridgeId(bridgeId, v, idToFqn, idToVertex, isRootParent)) {
|
|
667
|
+
idToFqn.set(v.id, bridgeId);
|
|
668
|
+
for (const segment of bridgeId.split(".")) usedNames.add(segment);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
for (const v of elementVertices) {
|
|
672
|
+
if (idToFqn.has(v.id)) continue;
|
|
673
|
+
if (isRootParent(v.parent)) idToFqn.set(v.id, uniqueName(baseName(v)));
|
|
674
|
+
}
|
|
564
675
|
let changed = true;
|
|
565
676
|
while (changed) {
|
|
566
677
|
changed = false;
|
|
@@ -686,7 +797,7 @@ function pushElementHeader(ctx, pad, name, kind, title) {
|
|
|
686
797
|
function pushElementStyleBlock(ctx, pad, cell, colorName) {
|
|
687
798
|
const border = cell.border?.trim();
|
|
688
799
|
const opacityVal = cell.opacity;
|
|
689
|
-
const shapeOverride = inferShape(cell.style);
|
|
800
|
+
const shapeOverride = inferShape(cell.style, cell.shapeFromStyle);
|
|
690
801
|
const sizeVal = cell.size?.trim();
|
|
691
802
|
const paddingVal = cell.padding?.trim();
|
|
692
803
|
const textSizeVal = cell.textSize?.trim();
|
|
@@ -734,7 +845,7 @@ function pushElementLinks(ctx, pad, linksJson, nativeLink) {
|
|
|
734
845
|
}
|
|
735
846
|
/** Whether element has any body content (Clean Code: single place for hasBody condition). */
|
|
736
847
|
function elementHasBody(cell, childList, colorName, opts) {
|
|
737
|
-
return (childList?.length ?? 0) > 0 || !!opts.desc || !!opts.tech || !!opts.notes || !!opts.summary || !!opts.linksJson || !!opts.nativeLink || !!opts.notation || opts.tagList.length > 0 || !!colorName || !!cell.border?.trim() || !!cell.opacity || !!inferShape(cell.style) || !!cell.size || !!cell.padding || !!cell.textSize || !!cell.iconPosition || !!opts.navigateTo || !!opts.icon;
|
|
848
|
+
return (childList?.length ?? 0) > 0 || !!opts.desc || !!opts.tech || !!opts.notes || !!opts.summary || !!opts.linksJson || !!opts.nativeLink || !!opts.notation || opts.tagList.length > 0 || !!colorName || !!cell.border?.trim() || !!cell.opacity || !!inferShape(cell.style, cell.shapeFromStyle) || !!cell.size || !!cell.padding || !!cell.textSize || !!cell.iconPosition || !!opts.navigateTo || !!opts.icon;
|
|
738
849
|
}
|
|
739
850
|
/** Push element body lines (style, tags, description, links, children). */
|
|
740
851
|
function pushElementBody(ctx, pad, cell, childList, fqn, indent, colorName, opts) {
|
|
@@ -757,7 +868,7 @@ function emitElementToLines(ctx, cellId, fqn, indent) {
|
|
|
757
868
|
const cell = ctx.idToCell.get(cellId);
|
|
758
869
|
if (!cell) return;
|
|
759
870
|
const parentCell = cell.parent ? ctx.byId.get(cell.parent) : void 0;
|
|
760
|
-
const kind = inferKind(cell.style, parentCell);
|
|
871
|
+
const kind = inferKind(cell.style, parentCell, cell.shapeFromStyle);
|
|
761
872
|
const title = stripHtml(cell.value && cell.value.trim() || "") || (ctx.containerIdToTitle.get(cell.id) ?? ctx.containerIdToTitle.get(cellId) ?? "") || fqn.split(".").pop() || "Element";
|
|
762
873
|
const name = fqn.split(".").pop();
|
|
763
874
|
const pad = " ".repeat(indent);
|
|
@@ -1018,7 +1129,8 @@ function buildCommonDiagramStateFromCells(cells, diagramName) {
|
|
|
1018
1129
|
for (const v of vertices) idToCell.set(v.id, v);
|
|
1019
1130
|
const { containerIdToTitle, titleCellIds } = computeContainerTitles(vertices);
|
|
1020
1131
|
const elementVertices = vertices.filter((v) => !titleCellIds.has(v.id));
|
|
1021
|
-
|
|
1132
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
1133
|
+
assignFqnsToElementVertices(idToFqn, elementVertices, containerIdToTitle, isRootParent, makeUniqueName(usedNames), usedNames);
|
|
1022
1134
|
const hexToCustomName = buildHexToCustomName(elementVertices, edges);
|
|
1023
1135
|
const children = /* @__PURE__ */ new Map();
|
|
1024
1136
|
const roots = [];
|
|
@@ -1650,6 +1762,25 @@ function buildLikec4StyleForNode(params) {
|
|
|
1650
1762
|
pushStylePart(parts, "likec4Notation", params.nodeNotation ?? void 0);
|
|
1651
1763
|
return parts.length > 0 ? parts.join(";") + ";" : "";
|
|
1652
1764
|
}
|
|
1765
|
+
/** Bridge-managed style parts for profile 'leanix': likec4Id, likec4Kind, likec4ViewId, likec4ProjectId, bridgeManaged, optional leanixFactSheetType. */
|
|
1766
|
+
function buildBridgeManagedStyleForNode(nodeId, nodeKind, viewId, options) {
|
|
1767
|
+
if (options?.profile !== "leanix") return "";
|
|
1768
|
+
const parts = [
|
|
1769
|
+
"bridgeManaged=true",
|
|
1770
|
+
`likec4Id=${encodeURIComponent(nodeId)}`,
|
|
1771
|
+
`likec4Kind=${encodeURIComponent(nodeKind)}`,
|
|
1772
|
+
`likec4ViewId=${encodeURIComponent(viewId)}`
|
|
1773
|
+
];
|
|
1774
|
+
if (options.projectId != null && options.projectId !== "") parts.push(`likec4ProjectId=${encodeURIComponent(options.projectId)}`);
|
|
1775
|
+
const factSheetType = options.leanixFactSheetTypeByKind?.[nodeKind];
|
|
1776
|
+
if (factSheetType != null && factSheetType !== "") parts.push(`leanixFactSheetType=${encodeURIComponent(factSheetType)}`);
|
|
1777
|
+
return parts.join(";") + ";";
|
|
1778
|
+
}
|
|
1779
|
+
/** Bridge-managed style parts for edge when profile is 'leanix': likec4RelationId, bridgeManaged. */
|
|
1780
|
+
function buildBridgeManagedStyleForEdge(relationId, options) {
|
|
1781
|
+
if (options?.profile !== "leanix") return "";
|
|
1782
|
+
return `bridgeManaged=true;likec4RelationId=${encodeURIComponent(relationId)};`;
|
|
1783
|
+
}
|
|
1653
1784
|
/** Build mxUserObject XML from customData for round-trip; returns empty string when customData is missing or empty. */
|
|
1654
1785
|
function buildMxUserObjectXml(customData) {
|
|
1655
1786
|
if (!customData || typeof customData !== "object" || Array.isArray(customData) || Object.keys(customData).length === 0) return "";
|
|
@@ -1682,8 +1813,8 @@ function buildEdgeGeometryXml(edge, edgeWaypoints) {
|
|
|
1682
1813
|
if (!(edgePoints.length > 0)) return "<mxGeometry relative=\"1\" as=\"geometry\" />";
|
|
1683
1814
|
return `<mxGeometry relative="1" as="geometry">${"<Array as=\"points\">" + edgePoints.map(([px, py]) => `<mxPoint x="${Math.round(px)}" y="${Math.round(py)}"/>`).join("") + "</Array>"}</mxGeometry>`;
|
|
1684
1815
|
}
|
|
1685
|
-
/** Full edge style string for mxCell (arrows, anchors, stroke, dash, label, likec4 roundtrip). */
|
|
1686
|
-
function buildEdgeStyleString(edge, layout, viewmodel, label) {
|
|
1816
|
+
/** Full edge style string for mxCell (arrows, anchors, stroke, dash, label, likec4 roundtrip, optional bridge-managed). */
|
|
1817
|
+
function buildEdgeStyleString(edge, layout, viewmodel, label, options) {
|
|
1687
1818
|
const { bboxes, fontFamily } = layout;
|
|
1688
1819
|
const sourceBbox = bboxes.get(edge.source);
|
|
1689
1820
|
const targetBbox = bboxes.get(edge.target);
|
|
@@ -1708,8 +1839,9 @@ function buildEdgeStyleString(edge, layout, viewmodel, label) {
|
|
|
1708
1839
|
edgeLinksJson: linksToStyleJson(edgeOptionalFields.getLinks(edge)),
|
|
1709
1840
|
edgeMetadataJson: metadataToStyleJson(edgeOptionalFields.getMetadata(edge))
|
|
1710
1841
|
});
|
|
1842
|
+
const edgeBridgeStyle = buildBridgeManagedStyleForEdge(edge.id, options);
|
|
1711
1843
|
const edgeLabelColors = getEdgeLabelColors(viewmodel, edge.color);
|
|
1712
|
-
return `endArrow=${endArrow};startArrow=${startArrow};html=1;rounded=0;${anchorStyle}strokeColor=${strokeColor};strokeWidth=2;${dashStyle}${label === "" ? "" : `fontColor=${edgeLabelColors.font};fontSize=12;align=center;verticalAlign=middle;labelBackgroundColor=none;fontFamily=${encodeURIComponent(fontFamily)};`}${edgeLikec4Style}`;
|
|
1844
|
+
return `endArrow=${endArrow};startArrow=${startArrow};html=1;rounded=0;${anchorStyle}strokeColor=${strokeColor};strokeWidth=2;${dashStyle}${label === "" ? "" : `fontColor=${edgeLabelColors.font};fontSize=12;align=center;verticalAlign=middle;labelBackgroundColor=none;fontFamily=${encodeURIComponent(fontFamily)};`}${edgeLikec4Style}${edgeBridgeStyle}`;
|
|
1713
1845
|
}
|
|
1714
1846
|
/** Build a single edge mxCell XML (orchestrator: label + geometry + style + assembly). */
|
|
1715
1847
|
function buildEdgeCellXml(edge, layout, options, viewmodel, getCellId, edgeCellId) {
|
|
@@ -1718,7 +1850,7 @@ function buildEdgeCellXml(edge, layout, options, viewmodel, getCellId, edgeCellI
|
|
|
1718
1850
|
const targetId = getCellId(edge.target);
|
|
1719
1851
|
const label = buildEdgeLabelValue(edge);
|
|
1720
1852
|
const edgeGeometryXml = buildEdgeGeometryXml(edge, options?.edgeWaypoints);
|
|
1721
|
-
return `<mxCell id="${edgeCellId}" value="${label}" style="${buildEdgeStyleString(edge, layout, viewmodel, label)}" edge="1" parent="${defaultParentId}" source="${sourceId}" target="${targetId}">
|
|
1853
|
+
return `<mxCell id="${edgeCellId}" value="${label}" style="${buildEdgeStyleString(edge, layout, viewmodel, label, options)}" edge="1" parent="${defaultParentId}" source="${sourceId}" target="${targetId}">
|
|
1722
1854
|
${edgeGeometryXml}${buildMxUserObjectXml(edgeOptionalFields.getCustomData(edge))}
|
|
1723
1855
|
</mxCell>`;
|
|
1724
1856
|
}
|
|
@@ -1745,6 +1877,7 @@ function computeNodeStylePartsAndValue(node, layout, options, viewmodel) {
|
|
|
1745
1877
|
const strokeColorByNodeId = options?.strokeColorByNodeId;
|
|
1746
1878
|
const strokeWidthByNodeId = options?.strokeWidthByNodeId;
|
|
1747
1879
|
const isContainer = containerNodeIds.has(node.id);
|
|
1880
|
+
const nodeKind = node.kind ?? "";
|
|
1748
1881
|
const title = node.title;
|
|
1749
1882
|
const desc = toExportString(node.description);
|
|
1750
1883
|
const tech = toExportString(node.technology);
|
|
@@ -1753,7 +1886,8 @@ function computeNodeStylePartsAndValue(node, layout, options, viewmodel) {
|
|
|
1753
1886
|
const tagList = Array.isArray(tags) && tags.length > 0 ? tags.join(",") : "";
|
|
1754
1887
|
const navTo = toNonEmptyString(nodeOptionalFields.getNavigateTo(node));
|
|
1755
1888
|
const iconName = toNonEmptyString(nodeOptionalFields.getIcon(node));
|
|
1756
|
-
const
|
|
1889
|
+
const isActor = nodeKind === "actor" || node.shape === "person";
|
|
1890
|
+
const shapeStyle = isContainer ? "shape=rectangle;rounded=0;container=1;collapsible=0;startSize=0;" : isActor ? "shape=actor;" : drawioShape(node.shape);
|
|
1757
1891
|
const strokeColorOverride = strokeColorByNodeId?.[node.id];
|
|
1758
1892
|
const strokeWidthOverride = strokeWidthByNodeId?.[node.id];
|
|
1759
1893
|
const elemColors = strokeColorOverride ? applyStrokeColorOverride(getElementColors(viewmodel, node.color), strokeColorOverride) : getElementColors(viewmodel, node.color);
|
|
@@ -1770,7 +1904,7 @@ function computeNodeStylePartsAndValue(node, layout, options, viewmodel) {
|
|
|
1770
1904
|
const containerDashed = getContainerDashedStyle(isContainer, borderVal);
|
|
1771
1905
|
const containerOpacityNum = isContainer === true ? nodeStyle?.opacity ?? DEFAULT_CONTAINER_OPACITY : void 0;
|
|
1772
1906
|
const fillOpacityStyle = containerOpacityNum != null && isContainer === true ? `fillOpacity=${Math.min(100, Math.max(0, containerOpacityNum))};` : "";
|
|
1773
|
-
const
|
|
1907
|
+
const likec4StyleWithBridge = buildLikec4StyleForNode({
|
|
1774
1908
|
desc,
|
|
1775
1909
|
tech,
|
|
1776
1910
|
notes,
|
|
@@ -1786,12 +1920,12 @@ function computeNodeStylePartsAndValue(node, layout, options, viewmodel) {
|
|
|
1786
1920
|
nodeStyle,
|
|
1787
1921
|
strokeHex,
|
|
1788
1922
|
nodeNotation: nodeOptionalFields.getNotation(node)
|
|
1789
|
-
});
|
|
1923
|
+
}) + buildBridgeManagedStyleForNode(node.id, nodeKind, layout.view.id, options);
|
|
1790
1924
|
const userObjectXml = buildMxUserObjectXml(nodeOptionalFields.getCustomData(node));
|
|
1791
1925
|
const navLinkStyle = buildNavLinkStyle(navTo);
|
|
1792
1926
|
return {
|
|
1793
1927
|
value,
|
|
1794
|
-
styleStr: `${isContainer ? "align=left;verticalAlign=top;overflow=fill;whiteSpace=wrap;html=1;" : `align=center;verticalAlign=middle;verticalLabelPosition=middle;labelPosition=center;fontSize=${fontSizePx};fontStyle=1;spacingTop=4;spacingLeft=2;spacingRight=2;spacingBottom=2;overflow=fill;whiteSpace=wrap;html=1;fontFamily=${encodeURIComponent(fontFamily)};`}${shapeStyle}${colorStyle}${strokeWidthStyle}${containerDashed}${fillOpacityStyle}${navLinkStyle}${
|
|
1928
|
+
styleStr: `${isContainer ? "align=left;verticalAlign=top;overflow=fill;whiteSpace=wrap;html=1;" : `align=center;verticalAlign=middle;verticalLabelPosition=middle;labelPosition=center;fontSize=${fontSizePx};fontStyle=1;spacingTop=4;spacingLeft=2;spacingRight=2;spacingBottom=2;overflow=fill;whiteSpace=wrap;html=1;fontFamily=${encodeURIComponent(fontFamily)};`}${shapeStyle}${colorStyle}${strokeWidthStyle}${containerDashed}${fillOpacityStyle}${navLinkStyle}${likec4StyleWithBridge}`,
|
|
1795
1929
|
userObjectXml,
|
|
1796
1930
|
navTo,
|
|
1797
1931
|
isContainer,
|
|
@@ -1862,20 +1996,28 @@ function getViewDescriptionString(view) {
|
|
|
1862
1996
|
if (typeof raw === "string") return raw;
|
|
1863
1997
|
return "";
|
|
1864
1998
|
}
|
|
1865
|
-
/**
|
|
1866
|
-
function
|
|
1999
|
+
/** Returns draw.io style tokens for the leanix profile (bridgeManaged, likec4ViewId, likec4ProjectId). Each token ends with ";". */
|
|
2000
|
+
function getLeanixRootStyleParts(view, options) {
|
|
2001
|
+
const parts = ["bridgeManaged=true;", `likec4ViewId=${encodeURIComponent(view.id)};`];
|
|
2002
|
+
if (options.projectId != null && options.projectId !== "") parts.push(`likec4ProjectId=${encodeURIComponent(options.projectId)};`);
|
|
2003
|
+
return parts;
|
|
2004
|
+
}
|
|
2005
|
+
/** Build root cell style string from view metadata (title, description, notation) for round-trip; when profile is 'leanix' adds likec4ViewId, likec4ProjectId, bridgeManaged. */
|
|
2006
|
+
function buildRootCellStyle(view, options) {
|
|
1867
2007
|
const viewTitle = getViewTitle(view);
|
|
1868
2008
|
const viewDesc = getViewDescriptionString(view);
|
|
1869
2009
|
const viewDescEnc = viewDesc.trim() !== "" ? encodeURIComponent(viewDesc.trim()) : "";
|
|
1870
2010
|
const viewNotationRaw = view.notation;
|
|
1871
2011
|
const viewNotation = typeof viewNotationRaw === "string" && viewNotationRaw !== "" ? viewNotationRaw : void 0;
|
|
1872
2012
|
const viewNotationEnc = viewNotation != null ? encodeURIComponent(viewNotation) : "";
|
|
1873
|
-
|
|
2013
|
+
const rootParts = [
|
|
1874
2014
|
"rounded=1;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;",
|
|
1875
2015
|
`likec4ViewTitle=${encodeURIComponent(viewTitle ?? view.id)};`,
|
|
1876
2016
|
viewDescEnc !== "" ? `likec4ViewDescription=${viewDescEnc};` : "",
|
|
1877
2017
|
viewNotationEnc !== "" ? `likec4ViewNotation=${viewNotationEnc};` : ""
|
|
1878
|
-
]
|
|
2018
|
+
];
|
|
2019
|
+
if (options?.profile === "leanix") rootParts.push(...getLeanixRootStyleParts(view, options));
|
|
2020
|
+
return rootParts.join("");
|
|
1879
2021
|
}
|
|
1880
2022
|
/**
|
|
1881
2023
|
* Map LikeC4 RelationshipArrowType to draw.io endArrow/startArrow style value.
|
|
@@ -2078,7 +2220,7 @@ function generateDiagramContent(viewmodel, options) {
|
|
|
2078
2220
|
edgeCells.push(buildEdgeCellXml(edge, layout, options, viewmodel, getCellId, edgeId));
|
|
2079
2221
|
}
|
|
2080
2222
|
const allCells = [
|
|
2081
|
-
`<mxCell id="${defaultParentId}" value="" style="${buildRootCellStyle(view)}" vertex="1" parent="${rootId}">
|
|
2223
|
+
`<mxCell id="${defaultParentId}" value="" style="${buildRootCellStyle(view, options)}" vertex="1" parent="${rootId}">
|
|
2082
2224
|
<mxGeometry x="0" y="0" width="${canvasWidth}" height="${canvasHeight}" as="geometry" />
|
|
2083
2225
|
</mxCell>`,
|
|
2084
2226
|
...containerCells,
|
|
@@ -2646,7 +2788,7 @@ function generateIndex() {
|
|
|
2646
2788
|
}
|
|
2647
2789
|
function generateReactTypes(model, options = {}) {
|
|
2648
2790
|
const { useCorePackage = false } = options;
|
|
2649
|
-
invariant(!model.isParsed(), "can not generate react types for parsed model");
|
|
2791
|
+
invariant$1(!model.isParsed(), "can not generate react types for parsed model");
|
|
2650
2792
|
const aux = generateAux(model, options);
|
|
2651
2793
|
return `
|
|
2652
2794
|
/* prettier-ignore-start */
|
|
@@ -2706,4 +2848,4 @@ export {
|
|
|
2706
2848
|
/* prettier-ignore-end */
|
|
2707
2849
|
`.trimStart();
|
|
2708
2850
|
}
|
|
2709
|
-
export { DEFAULT_DRAWIO_ALL_FILENAME, buildDrawioExportOptionsForViews, buildDrawioExportOptionsFromSource, generateD2, generateDrawio, generateDrawioEditUrl, generateDrawioMulti, generateLikeC4Model, generateMermaid, generatePuml, generateReactNext, generateReactTypes, generateViewsDataDTs, generateViewsDataJs, generateViewsDataTs, getAllDiagrams, parseDrawioRoundtripComments, parseDrawioToLikeC4, parseDrawioToLikeC4Multi };
|
|
2851
|
+
export { DEFAULT_DRAWIO_ALL_FILENAME, buildDrawioExportOptionsForViews, buildDrawioExportOptionsFromSource, decompressDrawioDiagram, generateD2, generateDrawio, generateDrawioEditUrl, generateDrawioMulti, generateLikeC4Model, generateMermaid, generatePuml, generateReactNext, generateReactTypes, generateViewsDataDTs, generateViewsDataJs, generateViewsDataTs, getAllDiagrams, parseDrawioRoundtripComments, parseDrawioToLikeC4, parseDrawioToLikeC4Multi };
|