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