@miragon/wardley-dsl 0.2.0 → 0.3.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/index.cjs +554 -111
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +554 -112
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -16,7 +16,65 @@ function parseCoords(line) {
|
|
|
16
16
|
function stripCoords(line) {
|
|
17
17
|
return line.replace(COORDS_RE, " ");
|
|
18
18
|
}
|
|
19
|
-
|
|
19
|
+
function splitAtCoords(line) {
|
|
20
|
+
const m = COORDS_RE.exec(line);
|
|
21
|
+
if (!m) return null;
|
|
22
|
+
const a = Number(m[1]);
|
|
23
|
+
const b = Number(m[2]);
|
|
24
|
+
if (Number.isNaN(a) || Number.isNaN(b)) return null;
|
|
25
|
+
return {
|
|
26
|
+
name: line.slice(0, m.index).trim(),
|
|
27
|
+
coords: { a, b },
|
|
28
|
+
suffix: line.slice(m.index + m[0].length)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
var COORDS4_RE = /\[\s*([-\d.]+)\s*,\s*([-\d.]+)\s*,\s*([-\d.]+)\s*,\s*([-\d.]+)\s*\]/;
|
|
32
|
+
function parseCoords4(line) {
|
|
33
|
+
const m = COORDS4_RE.exec(line);
|
|
34
|
+
if (!m) return null;
|
|
35
|
+
const vals = [m[1], m[2], m[3], m[4]].map(Number);
|
|
36
|
+
if (vals.some(Number.isNaN)) return null;
|
|
37
|
+
return { a: vals[0], b: vals[1], c: vals[2], d: vals[3] };
|
|
38
|
+
}
|
|
39
|
+
var MULTI_COORDS_RE = /\[\s{0,8}(\[[^[\]]*\](?:\s{0,8},\s{0,8}\[[^[\]]*\])+)\s{0,8}\]/;
|
|
40
|
+
function parseMultiCoords(line) {
|
|
41
|
+
const m = MULTI_COORDS_RE.exec(line);
|
|
42
|
+
if (!m) return null;
|
|
43
|
+
const tuples = [];
|
|
44
|
+
const inner = /\[\s{0,8}([-\d.]+)\s{0,8},\s{0,8}([-\d.]+)\s{0,8}\]/g;
|
|
45
|
+
let t;
|
|
46
|
+
while (t = inner.exec(m[1])) {
|
|
47
|
+
const a = Number(t[1]);
|
|
48
|
+
const b = Number(t[2]);
|
|
49
|
+
if (!Number.isNaN(a) && !Number.isNaN(b)) tuples.push({ a, b });
|
|
50
|
+
}
|
|
51
|
+
if (!tuples.length) return null;
|
|
52
|
+
return { tuples, rest: line.replace(MULTI_COORDS_RE, " ") };
|
|
53
|
+
}
|
|
54
|
+
function splitLineComment(line) {
|
|
55
|
+
let inQuote = false;
|
|
56
|
+
for (let i = 0; i < line.length - 1; i++) {
|
|
57
|
+
const ch = line[i];
|
|
58
|
+
if (ch === "'") inQuote = !inQuote;
|
|
59
|
+
else if (!inQuote && ch === "/" && line[i + 1] === "/" && line[i - 1] !== ":") {
|
|
60
|
+
return { code: line.slice(0, i), comment: line.slice(i) };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { code: line, comment: null };
|
|
64
|
+
}
|
|
65
|
+
function indexOfOutsideQuotes(line, needle) {
|
|
66
|
+
let inQuote = false;
|
|
67
|
+
for (let i = 0; i + needle.length <= line.length; i++) {
|
|
68
|
+
const ch = line[i];
|
|
69
|
+
if (ch === "'") {
|
|
70
|
+
inQuote = !inQuote;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (!inQuote && line.startsWith(needle, i)) return i;
|
|
74
|
+
}
|
|
75
|
+
return -1;
|
|
76
|
+
}
|
|
77
|
+
var PAREN_RE = /\(([^()]*)\)/g;
|
|
20
78
|
var METHODS = /* @__PURE__ */ new Set(["build", "buy", "outsource"]);
|
|
21
79
|
function parseDecorators(line) {
|
|
22
80
|
const dec = {};
|
|
@@ -53,17 +111,47 @@ function parseLabelOffset(line) {
|
|
|
53
111
|
if (Number.isNaN(dx) || Number.isNaN(dy)) return { labelOffset: null, rest };
|
|
54
112
|
return { labelOffset: { dx, dy }, rest };
|
|
55
113
|
}
|
|
114
|
+
var URL_REF_RE = /\burl\s{0,8}\(([^()]*)\)/i;
|
|
115
|
+
function parseUrlRef(line) {
|
|
116
|
+
const m = URL_REF_RE.exec(line);
|
|
117
|
+
if (!m) return { urlRef: null, rest: line };
|
|
118
|
+
return { urlRef: m[1].trim() || null, rest: line.replace(URL_REF_RE, " ") };
|
|
119
|
+
}
|
|
56
120
|
function keywordOf(line) {
|
|
57
121
|
const m = /^\s*([A-Za-z][\w-]*)/.exec(line);
|
|
58
122
|
return m ? m[1].toLowerCase() : "";
|
|
59
123
|
}
|
|
60
124
|
function slug(label) {
|
|
61
|
-
return label.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_
|
|
125
|
+
return label.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_/, "").replace(/_$/, "") || "x";
|
|
62
126
|
}
|
|
63
127
|
|
|
64
128
|
// src/parser.ts
|
|
65
|
-
|
|
66
|
-
|
|
129
|
+
function splitDependency(core) {
|
|
130
|
+
const arrow = core.indexOf("->");
|
|
131
|
+
if (arrow <= 0) return null;
|
|
132
|
+
const left = core.slice(0, arrow).trim();
|
|
133
|
+
const right = core.slice(arrow + 2).trim();
|
|
134
|
+
return left && right ? { left, right } : null;
|
|
135
|
+
}
|
|
136
|
+
function splitFlow(core) {
|
|
137
|
+
for (let i = 1; i < core.length; i++) {
|
|
138
|
+
if (core[i] !== "+") continue;
|
|
139
|
+
let j = i + 1;
|
|
140
|
+
let value;
|
|
141
|
+
if (core[j] === "'") {
|
|
142
|
+
const end = core.indexOf("'", j + 1);
|
|
143
|
+
if (end < 0) continue;
|
|
144
|
+
value = core.slice(j + 1, end);
|
|
145
|
+
j = end + 1;
|
|
146
|
+
}
|
|
147
|
+
const op = core.startsWith("<>", j) ? "<>" : core[j] === ">" || core[j] === "<" ? core[j] : null;
|
|
148
|
+
if (!op) continue;
|
|
149
|
+
const left = core.slice(0, i).trim();
|
|
150
|
+
const right = core.slice(j + op.length).trim();
|
|
151
|
+
if (left && right) return { left, right, op, ...value ? { value } : {} };
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
67
155
|
var NON_LINK_KEYWORDS = /* @__PURE__ */ new Set([
|
|
68
156
|
"title",
|
|
69
157
|
"style",
|
|
@@ -72,7 +160,8 @@ var NON_LINK_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
72
160
|
"y-axis",
|
|
73
161
|
"annotations",
|
|
74
162
|
"annotation",
|
|
75
|
-
"evolve"
|
|
163
|
+
"evolve",
|
|
164
|
+
"line"
|
|
76
165
|
]);
|
|
77
166
|
var KNOWN_STYLES = /* @__PURE__ */ new Set(["wardley", "handwritten", "colour", "dark"]);
|
|
78
167
|
function compact(obj) {
|
|
@@ -92,6 +181,10 @@ var IdAllocator = class {
|
|
|
92
181
|
}
|
|
93
182
|
};
|
|
94
183
|
function parseDSL(text) {
|
|
184
|
+
return parseDSLWithDiagnostics(text).map;
|
|
185
|
+
}
|
|
186
|
+
function parseDSLWithDiagnostics(text) {
|
|
187
|
+
const diagnostics = [];
|
|
95
188
|
const ids = new IdAllocator();
|
|
96
189
|
const nameToId = /* @__PURE__ */ new Map();
|
|
97
190
|
const elements = [];
|
|
@@ -99,49 +192,130 @@ function parseDSL(text) {
|
|
|
99
192
|
const pendingLinks = [];
|
|
100
193
|
const pendingEvolve = [];
|
|
101
194
|
const pendingPipeline = [];
|
|
195
|
+
const pendingMethod = [];
|
|
102
196
|
let config = { title: "Untitled Map" };
|
|
103
197
|
let annoCounter = 0;
|
|
198
|
+
let inBlockComment = false;
|
|
199
|
+
const urlDefs = /* @__PURE__ */ new Map();
|
|
200
|
+
const pendingUrlRefs = [];
|
|
201
|
+
let lastPipeline = null;
|
|
202
|
+
let blockPipeline = null;
|
|
104
203
|
const register = (name, id) => {
|
|
105
204
|
if (!nameToId.has(name)) nameToId.set(name, id);
|
|
106
205
|
};
|
|
107
|
-
|
|
206
|
+
let lineNo = 0;
|
|
207
|
+
let currentLine = "";
|
|
208
|
+
const pushLink = (line) => {
|
|
108
209
|
const semi = line.indexOf(";");
|
|
109
210
|
const core = semi >= 0 ? line.slice(0, semi).trim() : line;
|
|
110
211
|
const linkLabel = semi >= 0 ? line.slice(semi + 1).trim() : "";
|
|
111
|
-
const dep =
|
|
112
|
-
const flow = dep ? null :
|
|
212
|
+
const dep = splitDependency(core);
|
|
213
|
+
const flow = dep ? null : splitFlow(core);
|
|
113
214
|
if (dep) {
|
|
114
215
|
pendingLinks.push({
|
|
115
|
-
left: dep
|
|
116
|
-
right: dep
|
|
216
|
+
left: dep.left,
|
|
217
|
+
right: dep.right,
|
|
117
218
|
kind: "dependency",
|
|
118
219
|
...linkLabel ? { label: linkLabel } : {},
|
|
119
|
-
raw
|
|
220
|
+
raw: line,
|
|
221
|
+
lineNo
|
|
120
222
|
});
|
|
121
223
|
return true;
|
|
122
224
|
}
|
|
123
225
|
if (flow) {
|
|
124
|
-
const op = flow[3];
|
|
125
226
|
pendingLinks.push({
|
|
126
|
-
left: flow
|
|
127
|
-
right: flow
|
|
227
|
+
left: flow.left,
|
|
228
|
+
right: flow.right,
|
|
128
229
|
kind: "flow",
|
|
129
|
-
bidirectional: op === "<>",
|
|
130
|
-
reverse: op === "<",
|
|
131
|
-
...flow
|
|
230
|
+
bidirectional: flow.op === "<>",
|
|
231
|
+
reverse: flow.op === "<",
|
|
232
|
+
...flow.value ? { flowValue: flow.value } : {},
|
|
132
233
|
...linkLabel ? { label: linkLabel } : {},
|
|
133
|
-
raw
|
|
234
|
+
raw: line,
|
|
235
|
+
lineNo
|
|
134
236
|
});
|
|
135
237
|
return true;
|
|
136
238
|
}
|
|
137
239
|
return false;
|
|
138
240
|
};
|
|
139
|
-
|
|
140
|
-
|
|
241
|
+
const diag = (message, atLine = lineNo, text_ = currentLine) => diagnostics.push({ line: atLine, message, text: text_ });
|
|
242
|
+
const failed = (l) => {
|
|
243
|
+
rawPassthrough.push(l);
|
|
244
|
+
diag("Line could not be interpreted (kept losslessly in rawPassthrough)");
|
|
245
|
+
};
|
|
246
|
+
const clampDiag = (n) => {
|
|
247
|
+
if (n < 0 || n > 1) diag(`Coordinate ${n} is outside [0,1] and was clamped`);
|
|
248
|
+
return n < 0 ? 0 : n > 1 ? 1 : n;
|
|
249
|
+
};
|
|
250
|
+
const pos = (visibility, evolution) => ({
|
|
251
|
+
visibility: clampDiag(visibility),
|
|
252
|
+
evolution: clampDiag(evolution)
|
|
253
|
+
});
|
|
254
|
+
const sourceLines = text.split(/\r?\n/);
|
|
255
|
+
for (let i = 0; i < sourceLines.length; i++) {
|
|
256
|
+
const raw = sourceLines[i];
|
|
257
|
+
lineNo = i + 1;
|
|
258
|
+
currentLine = raw.trim();
|
|
259
|
+
let working = raw;
|
|
260
|
+
if (inBlockComment) {
|
|
261
|
+
const close = working.indexOf("*/");
|
|
262
|
+
if (close < 0) {
|
|
263
|
+
rawPassthrough.push(raw);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
rawPassthrough.push(working.slice(0, close + 2));
|
|
267
|
+
working = working.slice(close + 2);
|
|
268
|
+
inBlockComment = false;
|
|
269
|
+
}
|
|
270
|
+
if (keywordOf(working) !== "url") {
|
|
271
|
+
let open = indexOfOutsideQuotes(working, "/*");
|
|
272
|
+
while (open >= 0) {
|
|
273
|
+
const close = working.indexOf("*/", open + 2);
|
|
274
|
+
if (close < 0) {
|
|
275
|
+
rawPassthrough.push(working.slice(open));
|
|
276
|
+
working = working.slice(0, open);
|
|
277
|
+
inBlockComment = true;
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
rawPassthrough.push(working.slice(open, close + 2));
|
|
281
|
+
working = `${working.slice(0, open)} ${working.slice(close + 2)}`;
|
|
282
|
+
open = indexOfOutsideQuotes(working, "/*");
|
|
283
|
+
}
|
|
284
|
+
const { code, comment } = splitLineComment(working);
|
|
285
|
+
if (comment !== null) rawPassthrough.push(comment);
|
|
286
|
+
working = code;
|
|
287
|
+
}
|
|
288
|
+
const line = working.trim();
|
|
141
289
|
if (!line) continue;
|
|
290
|
+
if (blockPipeline) {
|
|
291
|
+
if (line === "}") {
|
|
292
|
+
blockPipeline = null;
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const childKw = keywordOf(line);
|
|
296
|
+
if (childKw === "component") {
|
|
297
|
+
const child = parseBlockChild(line.slice(childKw.length).trim());
|
|
298
|
+
if (child) {
|
|
299
|
+
blockPipeline.children.push(child);
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
rawPassthrough.push(line);
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
if (line.startsWith("{")) {
|
|
307
|
+
if (lastPipeline) {
|
|
308
|
+
blockPipeline = lastPipeline;
|
|
309
|
+
lastPipeline = null;
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
rawPassthrough.push(line);
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
142
315
|
const kw = keywordOf(line);
|
|
143
316
|
const after = line.slice(kw.length).trim();
|
|
144
|
-
if (
|
|
317
|
+
if (kw !== "pipeline") lastPipeline = null;
|
|
318
|
+
if (!NON_LINK_KEYWORDS.has(kw) && !parseCoords(line) && pushLink(line)) {
|
|
145
319
|
continue;
|
|
146
320
|
}
|
|
147
321
|
switch (kw) {
|
|
@@ -151,7 +325,7 @@ function parseDSL(text) {
|
|
|
151
325
|
case "anchor": {
|
|
152
326
|
const node = parseNode(after);
|
|
153
327
|
if (!node) {
|
|
154
|
-
|
|
328
|
+
failed(line);
|
|
155
329
|
break;
|
|
156
330
|
}
|
|
157
331
|
const id = ids.alloc("anchor", node.name);
|
|
@@ -159,8 +333,9 @@ function parseDSL(text) {
|
|
|
159
333
|
id,
|
|
160
334
|
elementType: "anchor",
|
|
161
335
|
label: node.name,
|
|
162
|
-
position:
|
|
163
|
-
labelOffset: node.labelOffset
|
|
336
|
+
position: pos(node.coords.a, node.coords.b),
|
|
337
|
+
labelOffset: node.labelOffset,
|
|
338
|
+
color: node.color
|
|
164
339
|
});
|
|
165
340
|
elements.push(anchor);
|
|
166
341
|
register(node.name, id);
|
|
@@ -171,7 +346,7 @@ function parseDSL(text) {
|
|
|
171
346
|
case "ecosystem": {
|
|
172
347
|
const node = parseNode(after);
|
|
173
348
|
if (!node) {
|
|
174
|
-
|
|
349
|
+
failed(line);
|
|
175
350
|
break;
|
|
176
351
|
}
|
|
177
352
|
const decorators = mergeLegacy(kw, node.decorators);
|
|
@@ -180,11 +355,13 @@ function parseDSL(text) {
|
|
|
180
355
|
id,
|
|
181
356
|
elementType: "component",
|
|
182
357
|
label: node.name,
|
|
183
|
-
position:
|
|
358
|
+
position: pos(node.coords.a, node.coords.b),
|
|
184
359
|
labelOffset: node.labelOffset,
|
|
185
|
-
decorators: Object.keys(decorators).length ? decorators : void 0
|
|
360
|
+
decorators: Object.keys(decorators).length ? decorators : void 0,
|
|
361
|
+
color: node.color
|
|
186
362
|
});
|
|
187
363
|
elements.push(component);
|
|
364
|
+
if (node.urlRef) pendingUrlRefs.push({ index: elements.length - 1, ref: node.urlRef });
|
|
188
365
|
register(node.name, id);
|
|
189
366
|
break;
|
|
190
367
|
}
|
|
@@ -193,7 +370,7 @@ function parseDSL(text) {
|
|
|
193
370
|
const coords = parseCoords(rest);
|
|
194
371
|
const textPart = stripCoords(rest).trim().replace(/\\n/g, "\n");
|
|
195
372
|
if (!coords) {
|
|
196
|
-
|
|
373
|
+
failed(line);
|
|
197
374
|
break;
|
|
198
375
|
}
|
|
199
376
|
const id = ids.alloc("note", textPart || "note");
|
|
@@ -201,41 +378,60 @@ function parseDSL(text) {
|
|
|
201
378
|
id,
|
|
202
379
|
elementType: "note",
|
|
203
380
|
label: textPart,
|
|
204
|
-
position:
|
|
381
|
+
position: pos(coords.a, coords.b),
|
|
205
382
|
color
|
|
206
383
|
});
|
|
207
384
|
elements.push(note);
|
|
208
385
|
break;
|
|
209
386
|
}
|
|
210
387
|
case "pipeline": {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (
|
|
214
|
-
|
|
388
|
+
let body = after;
|
|
389
|
+
let opensBlock = false;
|
|
390
|
+
if (body.endsWith("{")) {
|
|
391
|
+
opensBlock = true;
|
|
392
|
+
body = body.slice(0, -1).trim();
|
|
393
|
+
}
|
|
394
|
+
const col = parseColor(body);
|
|
395
|
+
const yMatch = /\(\s{0,8}y\s{1,8}([\d.]+)\s{0,8}\)/.exec(col.rest);
|
|
396
|
+
const bodyRest = yMatch ? col.rest.replace(yMatch[0], " ") : col.rest;
|
|
397
|
+
const coords = parseCoords(bodyRest);
|
|
398
|
+
const name = stripCoords(bodyRest).trim();
|
|
399
|
+
if (!name) {
|
|
400
|
+
failed(line);
|
|
215
401
|
break;
|
|
216
402
|
}
|
|
217
|
-
|
|
403
|
+
const pending = {
|
|
404
|
+
name,
|
|
405
|
+
...coords ? { start: coords.a, end: coords.b } : {},
|
|
406
|
+
...yMatch ? { visibility: clamp01(Number(yMatch[1])) } : {},
|
|
407
|
+
...col.color ? { color: col.color } : {},
|
|
408
|
+
raw: line,
|
|
409
|
+
children: []
|
|
410
|
+
};
|
|
411
|
+
pendingPipeline.push(pending);
|
|
412
|
+
if (opensBlock) blockPipeline = pending;
|
|
413
|
+
else lastPipeline = pending;
|
|
218
414
|
break;
|
|
219
415
|
}
|
|
220
416
|
case "evolve": {
|
|
221
417
|
const ev = parseEvolve(after);
|
|
222
418
|
if (!ev) {
|
|
223
|
-
|
|
419
|
+
failed(line);
|
|
224
420
|
break;
|
|
225
421
|
}
|
|
226
|
-
pendingEvolve.push({ ...ev, raw });
|
|
422
|
+
pendingEvolve.push({ ...ev, target: clampDiag(ev.target), raw: line, lineNo });
|
|
227
423
|
break;
|
|
228
424
|
}
|
|
229
425
|
case "style": {
|
|
230
426
|
const s = after.toLowerCase();
|
|
231
427
|
if (KNOWN_STYLES.has(s)) config = { ...config, style: s };
|
|
232
|
-
else
|
|
428
|
+
else failed(line);
|
|
233
429
|
break;
|
|
234
430
|
}
|
|
235
431
|
case "size": {
|
|
236
432
|
const coords = parseCoords(after);
|
|
237
433
|
if (coords) config = { ...config, size: { width: coords.a, height: coords.b } };
|
|
238
|
-
else
|
|
434
|
+
else failed(line);
|
|
239
435
|
break;
|
|
240
436
|
}
|
|
241
437
|
case "evolution": {
|
|
@@ -247,8 +443,13 @@ function parseDSL(text) {
|
|
|
247
443
|
}
|
|
248
444
|
case "y-axis": {
|
|
249
445
|
const parts = after.split("->").map((s) => s.trim()).filter(Boolean);
|
|
250
|
-
if (parts.length)
|
|
251
|
-
|
|
446
|
+
if (parts.length) {
|
|
447
|
+
config = {
|
|
448
|
+
...config,
|
|
449
|
+
yAxisLabel: parts[0],
|
|
450
|
+
...parts.length >= 3 ? { yAxisEndLabels: [parts[1], parts[2]] } : {}
|
|
451
|
+
};
|
|
452
|
+
} else failed(line);
|
|
252
453
|
break;
|
|
253
454
|
}
|
|
254
455
|
case "annotations": {
|
|
@@ -256,61 +457,122 @@ function parseDSL(text) {
|
|
|
256
457
|
if (coords) {
|
|
257
458
|
config = {
|
|
258
459
|
...config,
|
|
259
|
-
annotationsBoxPosition:
|
|
460
|
+
annotationsBoxPosition: pos(coords.a, coords.b)
|
|
260
461
|
};
|
|
261
462
|
} else rawPassthrough.push(raw);
|
|
262
463
|
break;
|
|
263
464
|
}
|
|
264
465
|
case "annotation": {
|
|
265
|
-
const coords = parseCoords(after);
|
|
266
|
-
if (!coords) {
|
|
267
|
-
rawPassthrough.push(raw);
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
466
|
const numMatch = /^\s*(\d+)/.exec(after);
|
|
271
467
|
const number = numMatch ? Number(numMatch[1]) : ++annoCounter;
|
|
272
|
-
const
|
|
273
|
-
const
|
|
468
|
+
const colA = parseColor(after.replace(/^\s*\d+\s*/, ""));
|
|
469
|
+
const afterNum = colA.rest;
|
|
470
|
+
const multi = parseMultiCoords(afterNum);
|
|
471
|
+
let positions;
|
|
472
|
+
let text2;
|
|
473
|
+
if (multi) {
|
|
474
|
+
positions = multi.tuples.map((t) => pos(t.a, t.b));
|
|
475
|
+
text2 = multi.rest.trim();
|
|
476
|
+
} else {
|
|
477
|
+
const coords = parseCoords(afterNum);
|
|
478
|
+
if (!coords) {
|
|
479
|
+
rawPassthrough.push(line);
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
positions = [pos(coords.a, coords.b)];
|
|
483
|
+
text2 = stripCoords(afterNum).trim();
|
|
484
|
+
}
|
|
274
485
|
const annotation = {
|
|
275
486
|
id: ids.alloc("anno", String(number)),
|
|
276
487
|
elementType: "annotation",
|
|
277
488
|
label: text2,
|
|
278
|
-
position,
|
|
489
|
+
position: positions[0],
|
|
279
490
|
number,
|
|
280
|
-
positions
|
|
281
|
-
text: text2
|
|
491
|
+
positions,
|
|
492
|
+
text: text2,
|
|
493
|
+
...colA.color ? { color: colA.color } : {}
|
|
282
494
|
};
|
|
283
495
|
elements.push(annotation);
|
|
284
496
|
break;
|
|
285
497
|
}
|
|
498
|
+
case "line": {
|
|
499
|
+
const col = parseColor(after);
|
|
500
|
+
const multi = parseMultiCoords(col.rest);
|
|
501
|
+
if (!multi || multi.tuples.length < 2) {
|
|
502
|
+
failed(line);
|
|
503
|
+
break;
|
|
504
|
+
}
|
|
505
|
+
const flags = multi.rest.toLowerCase();
|
|
506
|
+
const strokeStyle = flags.includes("(dashed)") ? "dashed" : flags.includes("(dotted)") ? "dotted" : void 0;
|
|
507
|
+
const points = multi.tuples.map((t) => pos(t.a, t.b));
|
|
508
|
+
const drawing = {
|
|
509
|
+
id: ids.alloc("draw", "line"),
|
|
510
|
+
elementType: "drawing",
|
|
511
|
+
label: "",
|
|
512
|
+
position: points[0],
|
|
513
|
+
points,
|
|
514
|
+
...flags.includes("(closed)") ? { closed: true } : {},
|
|
515
|
+
...strokeStyle ? { strokeStyle } : {},
|
|
516
|
+
...col.color ? { color: col.color } : {}
|
|
517
|
+
};
|
|
518
|
+
elements.push(drawing);
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
286
521
|
case "pioneers":
|
|
287
522
|
case "settlers":
|
|
288
523
|
case "townplanners": {
|
|
289
|
-
const
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (!coords || Number.isNaN(width) || Number.isNaN(height)) {
|
|
294
|
-
rawPassthrough.push(raw);
|
|
524
|
+
const col = parseColor(after);
|
|
525
|
+
const four = parseCoords4(col.rest);
|
|
526
|
+
if (!four) {
|
|
527
|
+
failed(line);
|
|
295
528
|
break;
|
|
296
529
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
530
|
+
elements.push(
|
|
531
|
+
makeAttitude(ids, kw, four.a, four.b, four.c, four.d, col.color)
|
|
532
|
+
);
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
case "url": {
|
|
536
|
+
const open = after.indexOf("[");
|
|
537
|
+
const close = after.lastIndexOf("]");
|
|
538
|
+
const name = open > 0 ? after.slice(0, open).trim() : "";
|
|
539
|
+
const address = open > 0 && close > open ? after.slice(open + 1, close).trim() : "";
|
|
540
|
+
if (!name || !address || after.slice(close + 1).trim() !== "") {
|
|
541
|
+
failed(line);
|
|
542
|
+
break;
|
|
543
|
+
}
|
|
544
|
+
urlDefs.set(name, address);
|
|
545
|
+
break;
|
|
546
|
+
}
|
|
547
|
+
case "build":
|
|
548
|
+
case "buy":
|
|
549
|
+
case "outsource": {
|
|
550
|
+
const node = parseNode(after);
|
|
551
|
+
if (node) {
|
|
552
|
+
const id = ids.alloc("cmp", node.name);
|
|
553
|
+
const component = compact({
|
|
554
|
+
id,
|
|
555
|
+
elementType: "component",
|
|
556
|
+
label: node.name,
|
|
557
|
+
position: pos(node.coords.a, node.coords.b),
|
|
558
|
+
labelOffset: node.labelOffset,
|
|
559
|
+
decorators: { ...node.decorators, method: kw },
|
|
560
|
+
color: node.color
|
|
561
|
+
});
|
|
562
|
+
elements.push(component);
|
|
563
|
+
register(node.name, id);
|
|
564
|
+
} else if (after.trim()) {
|
|
565
|
+
pendingMethod.push({ name: after.trim(), method: kw, raw: line, lineNo });
|
|
566
|
+
} else {
|
|
567
|
+
rawPassthrough.push(line);
|
|
568
|
+
}
|
|
307
569
|
break;
|
|
308
570
|
}
|
|
309
571
|
case "accelerator":
|
|
310
572
|
case "deaccelerator": {
|
|
311
573
|
const node = parseNode(after);
|
|
312
574
|
if (!node) {
|
|
313
|
-
|
|
575
|
+
failed(line);
|
|
314
576
|
break;
|
|
315
577
|
}
|
|
316
578
|
const id = ids.alloc("accel", node.name);
|
|
@@ -319,7 +581,8 @@ function parseDSL(text) {
|
|
|
319
581
|
elementType: "accelerator",
|
|
320
582
|
direction: kw === "deaccelerator" ? "deaccelerate" : "accelerate",
|
|
321
583
|
label: node.name,
|
|
322
|
-
position:
|
|
584
|
+
position: pos(node.coords.a, node.coords.b),
|
|
585
|
+
...node.color ? { color: node.color } : {}
|
|
323
586
|
};
|
|
324
587
|
elements.push(accelerator);
|
|
325
588
|
register(node.name, id);
|
|
@@ -328,7 +591,7 @@ function parseDSL(text) {
|
|
|
328
591
|
case "submap": {
|
|
329
592
|
const node = parseNode(after);
|
|
330
593
|
if (!node) {
|
|
331
|
-
|
|
594
|
+
failed(line);
|
|
332
595
|
break;
|
|
333
596
|
}
|
|
334
597
|
const id = ids.alloc("submap", node.name);
|
|
@@ -336,46 +599,107 @@ function parseDSL(text) {
|
|
|
336
599
|
id,
|
|
337
600
|
elementType: "submap",
|
|
338
601
|
label: node.name,
|
|
339
|
-
position:
|
|
602
|
+
position: pos(node.coords.a, node.coords.b),
|
|
603
|
+
...node.color ? { color: node.color } : {}
|
|
340
604
|
};
|
|
341
605
|
elements.push(submap);
|
|
606
|
+
if (node.urlRef) pendingUrlRefs.push({ index: elements.length - 1, ref: node.urlRef });
|
|
342
607
|
register(node.name, id);
|
|
343
608
|
break;
|
|
344
609
|
}
|
|
345
610
|
default: {
|
|
346
|
-
if (!pushLink(line
|
|
611
|
+
if (!pushLink(line)) rawPassthrough.push(line);
|
|
347
612
|
}
|
|
348
613
|
}
|
|
349
614
|
}
|
|
615
|
+
for (const p of pendingPipeline) {
|
|
616
|
+
const refId = nameToId.get(p.name);
|
|
617
|
+
const ref = elements.find((e) => e.id === refId);
|
|
618
|
+
const visibility = p.visibility ?? (ref ? ref.position.visibility : 0.5);
|
|
619
|
+
const id = ids.alloc("pipeline", p.name);
|
|
620
|
+
register(p.name, id);
|
|
621
|
+
const childIds = [];
|
|
622
|
+
const childElements = [];
|
|
623
|
+
for (const c of p.children) {
|
|
624
|
+
const cid = ids.alloc("cmp", c.name);
|
|
625
|
+
childElements.push(
|
|
626
|
+
compact({
|
|
627
|
+
id: cid,
|
|
628
|
+
elementType: "component",
|
|
629
|
+
label: c.name,
|
|
630
|
+
position: { visibility, evolution: clamp01(c.maturity) },
|
|
631
|
+
labelOffset: c.labelOffset,
|
|
632
|
+
decorators: Object.keys(c.decorators).length ? c.decorators : void 0,
|
|
633
|
+
pipelineId: id,
|
|
634
|
+
color: c.color
|
|
635
|
+
})
|
|
636
|
+
);
|
|
637
|
+
register(c.name, cid);
|
|
638
|
+
childIds.push(cid);
|
|
639
|
+
}
|
|
640
|
+
let start = p.start ?? (p.children.length ? Math.min(...p.children.map((c) => c.maturity)) : NaN);
|
|
641
|
+
let end = p.end ?? (p.children.length ? Math.max(...p.children.map((c) => c.maturity)) : NaN);
|
|
642
|
+
if (Number.isNaN(start) || Number.isNaN(end)) {
|
|
643
|
+
rawPassthrough.push(p.raw);
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
start = clamp01(start);
|
|
647
|
+
end = clamp01(end);
|
|
648
|
+
if (end <= start) end = Math.min(1, start + 0.05);
|
|
649
|
+
const pipeline = {
|
|
650
|
+
id,
|
|
651
|
+
elementType: "pipeline",
|
|
652
|
+
label: p.name,
|
|
653
|
+
position: { visibility, evolution: (start + end) / 2 },
|
|
654
|
+
evolutionStart: start,
|
|
655
|
+
evolutionEnd: end,
|
|
656
|
+
childIds,
|
|
657
|
+
...p.color ? { color: p.color } : {}
|
|
658
|
+
};
|
|
659
|
+
elements.push(pipeline, ...childElements);
|
|
660
|
+
}
|
|
350
661
|
for (const ev of pendingEvolve) {
|
|
351
662
|
const id = nameToId.get(ev.name);
|
|
352
663
|
const idx = elements.findIndex((e) => e.id === id);
|
|
353
664
|
if (idx < 0 || elements[idx].elementType !== "component") {
|
|
354
665
|
rawPassthrough.push(ev.raw);
|
|
666
|
+
diag(`evolve: component "${ev.name}" not found`, ev.lineNo, ev.raw);
|
|
355
667
|
continue;
|
|
356
668
|
}
|
|
357
669
|
const movement = compact({
|
|
358
670
|
targetEvolution: ev.target,
|
|
359
671
|
newLabel: ev.newLabel,
|
|
360
|
-
method: ev.method
|
|
672
|
+
method: ev.method,
|
|
673
|
+
labelOffset: ev.labelOffset
|
|
361
674
|
});
|
|
362
675
|
elements[idx] = { ...elements[idx], movement };
|
|
363
676
|
}
|
|
364
|
-
for (const
|
|
365
|
-
const
|
|
366
|
-
const
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
677
|
+
for (const pm of pendingMethod) {
|
|
678
|
+
const id = nameToId.get(pm.name);
|
|
679
|
+
const idx = elements.findIndex((e) => e.id === id);
|
|
680
|
+
const el = idx >= 0 ? elements[idx] : void 0;
|
|
681
|
+
if (!el || el.elementType !== "component") {
|
|
682
|
+
rawPassthrough.push(pm.raw);
|
|
683
|
+
diag(`${pm.method}: component "${pm.name}" not found`, pm.lineNo, pm.raw);
|
|
684
|
+
continue;
|
|
685
|
+
}
|
|
686
|
+
elements[idx] = { ...el, decorators: { ...el.decorators, method: pm.method } };
|
|
687
|
+
}
|
|
688
|
+
const usedUrlDefs = /* @__PURE__ */ new Set();
|
|
689
|
+
for (const { index, ref } of pendingUrlRefs) {
|
|
690
|
+
const fromDef = urlDefs.get(ref);
|
|
691
|
+
if (fromDef) usedUrlDefs.add(ref);
|
|
692
|
+
const address = fromDef ?? (/^[a-z][\w+.-]*:\/\//i.test(ref) ? ref : void 0);
|
|
693
|
+
if (!address) continue;
|
|
694
|
+
const el = elements[index];
|
|
695
|
+
if (el.elementType === "component") {
|
|
696
|
+
elements[index] = { ...el, url: address };
|
|
697
|
+
} else if (el.elementType === "submap") {
|
|
698
|
+
elements[index] = { ...el, urlRef: address };
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
for (const [name, address] of urlDefs) {
|
|
702
|
+
if (!usedUrlDefs.has(name)) rawPassthrough.push(`url ${name} [${address}]`);
|
|
379
703
|
}
|
|
380
704
|
let depN = 0;
|
|
381
705
|
let flowN = 0;
|
|
@@ -385,6 +709,11 @@ function parseDSL(text) {
|
|
|
385
709
|
const toId = nameToId.get(link.right);
|
|
386
710
|
if (!fromId || !toId) {
|
|
387
711
|
rawPassthrough.push(link.raw);
|
|
712
|
+
diag(
|
|
713
|
+
`Link: ${!fromId ? `"${link.left}"` : `"${link.right}"`} not found`,
|
|
714
|
+
link.lineNo,
|
|
715
|
+
link.raw
|
|
716
|
+
);
|
|
388
717
|
continue;
|
|
389
718
|
}
|
|
390
719
|
if (link.kind === "dependency") {
|
|
@@ -419,22 +748,64 @@ function parseDSL(text) {
|
|
|
419
748
|
edges,
|
|
420
749
|
rawPassthrough: rawPassthrough.length ? rawPassthrough : void 0
|
|
421
750
|
});
|
|
422
|
-
return validateMap(map);
|
|
751
|
+
return { map: validateMap(map), diagnostics };
|
|
423
752
|
}
|
|
424
753
|
function parseNode(after) {
|
|
425
|
-
const
|
|
754
|
+
const split = splitAtCoords(after);
|
|
755
|
+
if (!split || !split.name) return null;
|
|
756
|
+
const col = parseColor(split.suffix);
|
|
757
|
+
const url = parseUrlRef(col.rest);
|
|
758
|
+
const lo = parseLabelOffset(url.rest);
|
|
426
759
|
const dec = parseDecorators(lo.rest);
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
760
|
+
return compact({
|
|
761
|
+
name: split.name,
|
|
762
|
+
coords: split.coords,
|
|
763
|
+
decorators: dec.decorators,
|
|
764
|
+
labelOffset: lo.labelOffset ?? void 0,
|
|
765
|
+
urlRef: url.urlRef ?? void 0,
|
|
766
|
+
color: col.color ?? void 0
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
function clamp01(n) {
|
|
770
|
+
return n < 0 ? 0 : n > 1 ? 1 : n;
|
|
771
|
+
}
|
|
772
|
+
var SINGLE_COORD_RE = /\[\s*([-\d.]+)\s*\]/;
|
|
773
|
+
function parseBlockChild(after) {
|
|
774
|
+
const m = SINGLE_COORD_RE.exec(after);
|
|
775
|
+
if (!m) return null;
|
|
776
|
+
const maturity = Number(m[1]);
|
|
777
|
+
if (Number.isNaN(maturity)) return null;
|
|
778
|
+
const name = after.slice(0, m.index).trim();
|
|
430
779
|
if (!name) return null;
|
|
780
|
+
const suffix = after.slice(m.index + m[0].length);
|
|
781
|
+
const col = parseColor(suffix);
|
|
782
|
+
const lo = parseLabelOffset(col.rest);
|
|
783
|
+
const dec = parseDecorators(lo.rest);
|
|
431
784
|
return compact({
|
|
432
785
|
name,
|
|
433
|
-
|
|
786
|
+
maturity,
|
|
434
787
|
decorators: dec.decorators,
|
|
435
|
-
labelOffset: lo.labelOffset ?? void 0
|
|
788
|
+
labelOffset: lo.labelOffset ?? void 0,
|
|
789
|
+
color: col.color ?? void 0
|
|
436
790
|
});
|
|
437
791
|
}
|
|
792
|
+
function makeAttitude(ids, kind, v1, m1, v2, m2, color) {
|
|
793
|
+
return {
|
|
794
|
+
id: ids.alloc("attitude", kind),
|
|
795
|
+
elementType: "attitude",
|
|
796
|
+
kind,
|
|
797
|
+
label: "",
|
|
798
|
+
...color ? { color } : {},
|
|
799
|
+
position: {
|
|
800
|
+
visibility: clamp01(Math.max(v1, v2)),
|
|
801
|
+
evolution: clamp01(Math.min(m1, m2))
|
|
802
|
+
},
|
|
803
|
+
corner2: {
|
|
804
|
+
visibility: clamp01(Math.min(v1, v2)),
|
|
805
|
+
evolution: clamp01(Math.max(m1, m2))
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
}
|
|
438
809
|
function mergeLegacy(kw, dec) {
|
|
439
810
|
const merged = { ...dec };
|
|
440
811
|
if (kw === "market") merged["market"] = true;
|
|
@@ -442,7 +813,8 @@ function mergeLegacy(kw, dec) {
|
|
|
442
813
|
return compact(merged);
|
|
443
814
|
}
|
|
444
815
|
function parseEvolve(after) {
|
|
445
|
-
const
|
|
816
|
+
const lo = parseLabelOffset(after);
|
|
817
|
+
const dec = parseDecorators(lo.rest);
|
|
446
818
|
const tokens = dec.rest.trim().split(/\s+/);
|
|
447
819
|
if (tokens.length < 2) return null;
|
|
448
820
|
const last = tokens[tokens.length - 1];
|
|
@@ -457,11 +829,17 @@ function parseEvolve(after) {
|
|
|
457
829
|
name,
|
|
458
830
|
newLabel: newLabel || void 0,
|
|
459
831
|
target,
|
|
460
|
-
method: dec.decorators.method
|
|
832
|
+
method: dec.decorators.method,
|
|
833
|
+
labelOffset: lo.labelOffset ?? void 0
|
|
461
834
|
});
|
|
462
835
|
}
|
|
463
836
|
|
|
464
837
|
// src/serializer.ts
|
|
838
|
+
function urlOf(el) {
|
|
839
|
+
if (el.elementType === "component") return el.url;
|
|
840
|
+
if (el.elementType === "submap") return el.urlRef;
|
|
841
|
+
return void 0;
|
|
842
|
+
}
|
|
465
843
|
function r(n) {
|
|
466
844
|
return String(Math.round(n * 1e3) / 1e3);
|
|
467
845
|
}
|
|
@@ -486,6 +864,9 @@ function uniqueNames(map) {
|
|
|
486
864
|
function offsetSuffix(lo) {
|
|
487
865
|
return lo ? ` label [${r(lo.dx)}, ${r(lo.dy)}]` : "";
|
|
488
866
|
}
|
|
867
|
+
function colorSuffix(el) {
|
|
868
|
+
return el.color ? ` (color ${el.color})` : "";
|
|
869
|
+
}
|
|
489
870
|
function decoratorSuffix(dec) {
|
|
490
871
|
if (!dec) return "";
|
|
491
872
|
const paren = [];
|
|
@@ -506,22 +887,75 @@ function serializeDSL(map) {
|
|
|
506
887
|
if (map.config.size)
|
|
507
888
|
lines.push(`size [${r(map.config.size.width)}, ${r(map.config.size.height)}]`);
|
|
508
889
|
if (map.config.evolutionLabels) lines.push(`evolution ${map.config.evolutionLabels.join("->")}`);
|
|
509
|
-
if (map.config.yAxisLabel)
|
|
890
|
+
if (map.config.yAxisLabel) {
|
|
891
|
+
const ends = map.config.yAxisEndLabels;
|
|
892
|
+
lines.push(`y-axis ${map.config.yAxisLabel}${ends ? `->${ends[0]}->${ends[1]}` : ""}`);
|
|
893
|
+
}
|
|
510
894
|
if (map.config.annotationsBoxPosition) {
|
|
511
895
|
const b = map.config.annotationsBoxPosition;
|
|
512
896
|
lines.push(`annotations [${r(b.visibility)}, ${r(b.evolution)}]`);
|
|
513
897
|
}
|
|
898
|
+
const urlDefNames = /* @__PURE__ */ new Map();
|
|
899
|
+
for (const el of map.elements) {
|
|
900
|
+
const address = urlOf(el);
|
|
901
|
+
if (!address) continue;
|
|
902
|
+
const defName = `${nameOf(el)} URL`;
|
|
903
|
+
urlDefNames.set(el.id, defName);
|
|
904
|
+
lines.push(`url ${defName} [${address}]`);
|
|
905
|
+
}
|
|
906
|
+
const urlSuffix = (el) => {
|
|
907
|
+
const def = urlDefNames.get(el.id);
|
|
908
|
+
return def ? ` url(${def})` : "";
|
|
909
|
+
};
|
|
910
|
+
const pipelineIds = new Set(
|
|
911
|
+
map.elements.filter((e) => e.elementType === "pipeline").map((e) => e.id)
|
|
912
|
+
);
|
|
913
|
+
const childrenByPipeline = /* @__PURE__ */ new Map();
|
|
914
|
+
for (const el of map.elements) {
|
|
915
|
+
if (el.elementType !== "component" || !el.pipelineId || !pipelineIds.has(el.pipelineId)) {
|
|
916
|
+
continue;
|
|
917
|
+
}
|
|
918
|
+
const list = childrenByPipeline.get(el.pipelineId) ?? [];
|
|
919
|
+
list.push(el);
|
|
920
|
+
childrenByPipeline.set(el.pipelineId, list);
|
|
921
|
+
}
|
|
922
|
+
const isPipelineChild = (el) => el.elementType === "component" && !!el.pipelineId && pipelineIds.has(el.pipelineId);
|
|
514
923
|
const evolveLines = [];
|
|
924
|
+
const componentVisByLabel = /* @__PURE__ */ new Map();
|
|
925
|
+
for (const el of map.elements) {
|
|
926
|
+
if (el.elementType === "component" && !componentVisByLabel.has(el.label)) {
|
|
927
|
+
componentVisByLabel.set(el.label, el.position.visibility);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
const pipelineYSuffix = (el) => {
|
|
931
|
+
if (el.elementType !== "pipeline") return "";
|
|
932
|
+
const derived = componentVisByLabel.get(el.label) ?? 0.5;
|
|
933
|
+
return Math.abs(el.position.visibility - derived) > 5e-4 ? ` (y ${r(el.position.visibility)})` : "";
|
|
934
|
+
};
|
|
515
935
|
for (const el of map.elements) {
|
|
516
|
-
lines.push(elementLine(el, nameOf(el)));
|
|
517
936
|
if (el.elementType === "component" && el.movement) {
|
|
518
937
|
evolveLines.push(evolveLine(el, nameOf(el)));
|
|
519
938
|
}
|
|
939
|
+
if (isPipelineChild(el)) continue;
|
|
940
|
+
lines.push(elementLine(el, nameOf(el)) + pipelineYSuffix(el) + urlSuffix(el));
|
|
941
|
+
if (el.elementType === "pipeline") {
|
|
942
|
+
const kids = childrenByPipeline.get(el.id) ?? [];
|
|
943
|
+
if (kids.length) {
|
|
944
|
+
lines.push("{");
|
|
945
|
+
for (const k of kids) {
|
|
946
|
+
lines.push(
|
|
947
|
+
` component ${nameOf(k)} [${r(k.position.evolution)}]${decoratorSuffix(k.decorators)}${colorSuffix(k)}${offsetSuffix(k.labelOffset)}`
|
|
948
|
+
);
|
|
949
|
+
}
|
|
950
|
+
lines.push("}");
|
|
951
|
+
}
|
|
952
|
+
}
|
|
520
953
|
}
|
|
521
954
|
for (const line of evolveLines) lines.push(line);
|
|
955
|
+
const labelsById = new Map(map.elements.map((el) => [el.id, el.label]));
|
|
522
956
|
for (const edge of map.edges) {
|
|
523
|
-
const from = names.get(edge.from) ?? edge.from;
|
|
524
|
-
const to = names.get(edge.to) ?? edge.to;
|
|
957
|
+
const from = names.get(edge.from) ?? labelsById.get(edge.from) ?? edge.from;
|
|
958
|
+
const to = names.get(edge.to) ?? labelsById.get(edge.to) ?? edge.to;
|
|
525
959
|
const annotation = edge.label ? `; ${edge.label}` : "";
|
|
526
960
|
if (edge.edgeType === "dependency") {
|
|
527
961
|
lines.push(`${from} -> ${to}${annotation}`);
|
|
@@ -545,30 +979,38 @@ function elementLine(el, name) {
|
|
|
545
979
|
const p = el.position;
|
|
546
980
|
switch (el.elementType) {
|
|
547
981
|
case "anchor":
|
|
548
|
-
return `anchor ${name} [${r(p.visibility)}, ${r(p.evolution)}]${offsetSuffix(el.labelOffset)}`;
|
|
982
|
+
return `anchor ${name} [${r(p.visibility)}, ${r(p.evolution)}]${colorSuffix(el)}${offsetSuffix(el.labelOffset)}`;
|
|
549
983
|
case "component":
|
|
550
|
-
return `component ${name} [${r(p.visibility)}, ${r(p.evolution)}]${decoratorSuffix(el.decorators)}${offsetSuffix(el.labelOffset)}`;
|
|
984
|
+
return `component ${name} [${r(p.visibility)}, ${r(p.evolution)}]${decoratorSuffix(el.decorators)}${colorSuffix(el)}${offsetSuffix(el.labelOffset)}`;
|
|
551
985
|
case "note":
|
|
552
|
-
return `note ${name.replace(/\n/g, "\\n")} [${r(p.visibility)}, ${r(p.evolution)}]${
|
|
986
|
+
return `note ${name.replace(/\n/g, "\\n")} [${r(p.visibility)}, ${r(p.evolution)}]${colorSuffix(el)}`;
|
|
553
987
|
case "pipeline":
|
|
554
|
-
return `pipeline ${name} [${r(el.evolutionStart)}, ${r(el.evolutionEnd)}]`;
|
|
988
|
+
return `pipeline ${name} [${r(el.evolutionStart)}, ${r(el.evolutionEnd)}]${colorSuffix(el)}`;
|
|
555
989
|
case "submap":
|
|
556
|
-
return `submap ${name} [${r(p.visibility)}, ${r(p.evolution)}]`;
|
|
557
|
-
case "annotation":
|
|
558
|
-
|
|
990
|
+
return `submap ${name} [${r(p.visibility)}, ${r(p.evolution)}]${colorSuffix(el)}`;
|
|
991
|
+
case "annotation": {
|
|
992
|
+
const pos = el.positions.length > 1 ? `[${el.positions.map((q) => `[${r(q.visibility)}, ${r(q.evolution)}]`).join(", ")}]` : `[${r(p.visibility)}, ${r(p.evolution)}]`;
|
|
993
|
+
return `annotation ${el.number} ${pos}${colorSuffix(el)} ${el.text}`;
|
|
994
|
+
}
|
|
559
995
|
case "accelerator":
|
|
560
|
-
return `${el.direction === "deaccelerate" ? "deaccelerator" : "accelerator"} ${name} [${r(p.visibility)}, ${r(p.evolution)}]`;
|
|
996
|
+
return `${el.direction === "deaccelerate" ? "deaccelerator" : "accelerator"} ${name} [${r(p.visibility)}, ${r(p.evolution)}]${colorSuffix(el)}`;
|
|
561
997
|
case "attitude":
|
|
562
|
-
return `${el.kind} [${r(p.visibility)}, ${r(p.evolution)}
|
|
998
|
+
return `${el.kind} [${r(p.visibility)}, ${r(p.evolution)}, ${r(el.corner2.visibility)}, ${r(el.corner2.evolution)}]${colorSuffix(el)}`;
|
|
999
|
+
case "drawing": {
|
|
1000
|
+
const pts = el.points.map((q) => `[${r(q.visibility)}, ${r(q.evolution)}]`).join(", ");
|
|
1001
|
+
const closed = el.closed ? " (closed)" : "";
|
|
1002
|
+
const stroke = el.strokeStyle && el.strokeStyle !== "solid" ? ` (${el.strokeStyle})` : "";
|
|
1003
|
+
return `line [${pts}]${closed}${stroke}${colorSuffix(el)}`;
|
|
1004
|
+
}
|
|
563
1005
|
}
|
|
564
1006
|
}
|
|
565
1007
|
function evolveLine(el, name) {
|
|
566
1008
|
const mv = el.movement;
|
|
567
1009
|
const label = mv.newLabel ? `${name}->${mv.newLabel}` : name;
|
|
568
1010
|
const method = mv.method ? ` (${mv.method})` : "";
|
|
569
|
-
return `evolve ${label} ${r(mv.targetEvolution)}${method}`;
|
|
1011
|
+
return `evolve ${label} ${r(mv.targetEvolution)}${method}${offsetSuffix(mv.labelOffset)}`;
|
|
570
1012
|
}
|
|
571
1013
|
|
|
572
|
-
export { keywordOf, parseCoords, parseDSL, parseDecorators, parseLabelOffset, serializeDSL, slug };
|
|
1014
|
+
export { keywordOf, parseCoords, parseDSL, parseDSLWithDiagnostics, parseDecorators, parseLabelOffset, serializeDSL, slug };
|
|
573
1015
|
//# sourceMappingURL=index.js.map
|
|
574
1016
|
//# sourceMappingURL=index.js.map
|