@diagrammo/dgmo 0.2.26 → 0.2.28
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/.claude/skills/dgmo-chart/SKILL.md +107 -0
- package/.claude/skills/dgmo-flowchart/SKILL.md +61 -0
- package/.claude/skills/dgmo-generate/SKILL.md +58 -0
- package/.claude/skills/dgmo-sequence/SKILL.md +83 -0
- package/.cursorrules +117 -0
- package/.github/copilot-instructions.md +117 -0
- package/.windsurfrules +117 -0
- package/README.md +10 -3
- package/dist/cli.cjs +116 -108
- package/dist/index.cjs +563 -356
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -24
- package/dist/index.d.ts +39 -24
- package/dist/index.js +560 -355
- package/dist/index.js.map +1 -1
- package/docs/ai-integration.md +125 -0
- package/docs/language-reference.md +784 -0
- package/package.json +10 -3
- package/src/c4/parser.ts +90 -74
- package/src/c4/renderer.ts +13 -12
- package/src/c4/types.ts +6 -4
- package/src/chart.ts +3 -2
- package/src/class/parser.ts +2 -10
- package/src/class/types.ts +1 -1
- package/src/cli.ts +135 -19
- package/src/d3.ts +1 -1
- package/src/dgmo-mermaid.ts +1 -1
- package/src/dgmo-router.ts +1 -1
- package/src/echarts.ts +33 -13
- package/src/er/parser.ts +34 -43
- package/src/er/types.ts +1 -1
- package/src/graph/flowchart-parser.ts +2 -25
- package/src/graph/types.ts +1 -1
- package/src/index.ts +5 -0
- package/src/initiative-status/parser.ts +57 -11
- package/src/initiative-status/types.ts +1 -1
- package/src/kanban/parser.ts +32 -53
- package/src/kanban/renderer.ts +9 -8
- package/src/kanban/types.ts +6 -14
- package/src/org/parser.ts +47 -87
- package/src/org/resolver.ts +11 -12
- package/src/sequence/parser.ts +97 -15
- package/src/sequence/renderer.ts +62 -69
- package/src/utils/arrows.ts +75 -0
- package/src/utils/inline-markdown.ts +75 -0
- package/src/utils/parsing.ts +67 -0
- package/src/utils/tag-groups.ts +76 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diagrammo/dgmo",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.28",
|
|
4
4
|
"description": "DGMO diagram markup language — parser, renderer, and color system",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -24,7 +24,12 @@
|
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"dist",
|
|
27
|
-
"src"
|
|
27
|
+
"src",
|
|
28
|
+
"docs",
|
|
29
|
+
".claude/skills",
|
|
30
|
+
".github/copilot-instructions.md",
|
|
31
|
+
".cursorrules",
|
|
32
|
+
".windsurfrules"
|
|
28
33
|
],
|
|
29
34
|
"sideEffects": false,
|
|
30
35
|
"scripts": {
|
|
@@ -33,7 +38,8 @@
|
|
|
33
38
|
"dev": "tsup --watch",
|
|
34
39
|
"test": "vitest run",
|
|
35
40
|
"test:watch": "vitest",
|
|
36
|
-
"gallery": "pnpm build && node scripts/generate-gallery.mjs"
|
|
41
|
+
"gallery": "pnpm build && node scripts/generate-gallery.mjs",
|
|
42
|
+
"check:duplication": "jscpd ./src"
|
|
37
43
|
},
|
|
38
44
|
"dependencies": {
|
|
39
45
|
"@dagrejs/dagre": "^2.0.4",
|
|
@@ -59,6 +65,7 @@
|
|
|
59
65
|
"@types/d3-shape": "^3.1.7",
|
|
60
66
|
"@types/dagre": "^0.7.53",
|
|
61
67
|
"@types/jsdom": "^21.1.7",
|
|
68
|
+
"jscpd": "^4.0.8",
|
|
62
69
|
"tsup": "^8.5.1",
|
|
63
70
|
"typescript": "^5.7.3",
|
|
64
71
|
"vitest": "^3.0.0"
|
package/src/c4/parser.ts
CHANGED
|
@@ -2,12 +2,20 @@
|
|
|
2
2
|
// C4 Architecture Diagram — Parser
|
|
3
3
|
// ============================================================
|
|
4
4
|
|
|
5
|
-
import { resolveColor } from '../colors';
|
|
6
5
|
import type { PaletteColors } from '../palettes';
|
|
7
6
|
import type { DgmoError } from '../diagnostics';
|
|
8
7
|
import { makeDgmoError, formatDgmoError, suggest } from '../diagnostics';
|
|
9
|
-
import type {
|
|
8
|
+
import type { TagGroup } from '../utils/tag-groups';
|
|
9
|
+
import { matchTagBlockHeading } from '../utils/tag-groups';
|
|
10
10
|
import { inferParticipantType } from '../sequence/participant-inference';
|
|
11
|
+
import {
|
|
12
|
+
measureIndent,
|
|
13
|
+
extractColor,
|
|
14
|
+
parsePipeMetadata,
|
|
15
|
+
CHART_TYPE_RE,
|
|
16
|
+
TITLE_RE,
|
|
17
|
+
OPTION_RE,
|
|
18
|
+
} from '../utils/parsing';
|
|
11
19
|
import type {
|
|
12
20
|
ParsedC4,
|
|
13
21
|
C4Element,
|
|
@@ -23,12 +31,6 @@ import type {
|
|
|
23
31
|
// Regex patterns
|
|
24
32
|
// ============================================================
|
|
25
33
|
|
|
26
|
-
const CHART_TYPE_RE = /^chart\s*:\s*(.+)/i;
|
|
27
|
-
const TITLE_RE = /^title\s*:\s*(.+)/i;
|
|
28
|
-
const OPTION_RE = /^([a-z][a-z0-9-]*)\s*:\s*(.+)$/i;
|
|
29
|
-
const GROUP_HEADING_RE =
|
|
30
|
-
/^##\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/;
|
|
31
|
-
const COLOR_SUFFIX_RE = /\(([^)]+)\)\s*$/;
|
|
32
34
|
const CONTAINER_RE = /^\[([^\]]+)\]$/;
|
|
33
35
|
|
|
34
36
|
/** Matches element declarations: `person Name`, `system Name | k: v` */
|
|
@@ -40,6 +42,12 @@ const IS_A_RE = /\s+is\s+a(?:n)?\s+(\w+)\s*$/i;
|
|
|
40
42
|
/** Matches relationship arrows: `->`, `~>`, `<->`, `<~>` */
|
|
41
43
|
const RELATIONSHIP_RE = /^(<?-?>|<?~?>)\s+(.+)$/;
|
|
42
44
|
|
|
45
|
+
/** Labeled arrow relationships: -label->, ~label~>, <-label->, <~label~> */
|
|
46
|
+
const C4_LABELED_SYNC_RE = /^-(.+)->\s+(.+)$/;
|
|
47
|
+
const C4_LABELED_ASYNC_RE = /^~(.+)~>\s+(.+)$/;
|
|
48
|
+
const C4_LABELED_BIDI_SYNC_RE = /^<-(.+)->\s+(.+)$/;
|
|
49
|
+
const C4_LABELED_BIDI_ASYNC_RE = /^<~(.+)~>\s+(.+)$/;
|
|
50
|
+
|
|
43
51
|
/** Matches section headers: `containers:`, `components:`, `deployment:` */
|
|
44
52
|
const SECTION_HEADER_RE = /^(containers|components|deployment)\s*:\s*$/i;
|
|
45
53
|
|
|
@@ -53,28 +61,6 @@ const METADATA_RE = /^([^:]+):\s*(.+)$/;
|
|
|
53
61
|
// Helpers
|
|
54
62
|
// ============================================================
|
|
55
63
|
|
|
56
|
-
function measureIndent(line: string): number {
|
|
57
|
-
let indent = 0;
|
|
58
|
-
for (const ch of line) {
|
|
59
|
-
if (ch === ' ') indent++;
|
|
60
|
-
else if (ch === '\t') indent += 4;
|
|
61
|
-
else break;
|
|
62
|
-
}
|
|
63
|
-
return indent;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function extractColor(
|
|
67
|
-
label: string,
|
|
68
|
-
palette?: PaletteColors,
|
|
69
|
-
): { label: string; color?: string } {
|
|
70
|
-
const m = label.match(COLOR_SUFFIX_RE);
|
|
71
|
-
if (!m) return { label };
|
|
72
|
-
const colorName = m[1].trim();
|
|
73
|
-
return {
|
|
74
|
-
label: label.substring(0, m.index!).trim(),
|
|
75
|
-
color: resolveColor(colorName, palette),
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
64
|
|
|
79
65
|
const VALID_ELEMENT_TYPES = new Set<string>([
|
|
80
66
|
'person',
|
|
@@ -186,27 +172,6 @@ function parseRelationshipBody(
|
|
|
186
172
|
return { target, label: rest };
|
|
187
173
|
}
|
|
188
174
|
|
|
189
|
-
/** Parse pipe-delimited metadata from segments after the first (name) segment. */
|
|
190
|
-
function parsePipeMetadata(
|
|
191
|
-
segments: string[],
|
|
192
|
-
aliasMap: Map<string, string>,
|
|
193
|
-
): Record<string, string> {
|
|
194
|
-
const metadata: Record<string, string> = {};
|
|
195
|
-
for (let j = 1; j < segments.length; j++) {
|
|
196
|
-
for (const part of segments[j].split(',')) {
|
|
197
|
-
const trimmedPart = part.trim();
|
|
198
|
-
if (!trimmedPart) continue;
|
|
199
|
-
const colonIdx = trimmedPart.indexOf(':');
|
|
200
|
-
if (colonIdx > 0) {
|
|
201
|
-
const rawKey = trimmedPart.substring(0, colonIdx).trim().toLowerCase();
|
|
202
|
-
const key = aliasMap.get(rawKey) ?? rawKey;
|
|
203
|
-
const value = trimmedPart.substring(colonIdx + 1).trim();
|
|
204
|
-
metadata[key] = value;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
return metadata;
|
|
209
|
-
}
|
|
210
175
|
|
|
211
176
|
// ============================================================
|
|
212
177
|
// Stack entry types
|
|
@@ -287,7 +252,7 @@ export function parseC4(
|
|
|
287
252
|
let inDeployment = false;
|
|
288
253
|
|
|
289
254
|
// Tag group parsing state
|
|
290
|
-
let currentTagGroup:
|
|
255
|
+
let currentTagGroup: TagGroup | null = null;
|
|
291
256
|
const aliasMap = new Map<string, string>();
|
|
292
257
|
|
|
293
258
|
// Name uniqueness tracking
|
|
@@ -341,40 +306,42 @@ export function parseC4(
|
|
|
341
306
|
}
|
|
342
307
|
}
|
|
343
308
|
|
|
344
|
-
//
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
const key = optMatch[1].trim().toLowerCase();
|
|
349
|
-
if (key !== 'chart' && key !== 'title') {
|
|
350
|
-
result.options[key] = optMatch[2].trim();
|
|
351
|
-
continue;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// ## Tag group heading
|
|
357
|
-
const groupMatch = trimmed.match(GROUP_HEADING_RE);
|
|
358
|
-
if (groupMatch) {
|
|
309
|
+
// Tag group heading — `tag: Name` (new) or `## Name` (deprecated)
|
|
310
|
+
// Must be checked BEFORE OPTION_RE to prevent `tag: Rank` being swallowed as option
|
|
311
|
+
const tagBlockMatch = matchTagBlockHeading(trimmed);
|
|
312
|
+
if (tagBlockMatch) {
|
|
359
313
|
if (contentStarted) {
|
|
360
|
-
pushError(lineNumber, 'Tag groups
|
|
314
|
+
pushError(lineNumber, 'Tag groups must appear before content');
|
|
361
315
|
continue;
|
|
362
316
|
}
|
|
363
|
-
|
|
364
|
-
|
|
317
|
+
if (tagBlockMatch.deprecated) {
|
|
318
|
+
pushError(lineNumber, `'## ${tagBlockMatch.name}' is deprecated for tag groups — use 'tag: ${tagBlockMatch.name}' instead`, 'warning');
|
|
319
|
+
}
|
|
365
320
|
currentTagGroup = {
|
|
366
|
-
name:
|
|
367
|
-
alias,
|
|
321
|
+
name: tagBlockMatch.name,
|
|
322
|
+
alias: tagBlockMatch.alias,
|
|
368
323
|
entries: [],
|
|
369
324
|
lineNumber,
|
|
370
325
|
};
|
|
371
|
-
if (alias) {
|
|
372
|
-
aliasMap.set(alias.toLowerCase(),
|
|
326
|
+
if (tagBlockMatch.alias) {
|
|
327
|
+
aliasMap.set(tagBlockMatch.alias.toLowerCase(), tagBlockMatch.name.toLowerCase());
|
|
373
328
|
}
|
|
374
329
|
result.tagGroups.push(currentTagGroup);
|
|
375
330
|
continue;
|
|
376
331
|
}
|
|
377
332
|
|
|
333
|
+
// Generic header options
|
|
334
|
+
if (!contentStarted && !currentTagGroup && measureIndent(line) === 0) {
|
|
335
|
+
const optMatch = trimmed.match(OPTION_RE);
|
|
336
|
+
if (optMatch) {
|
|
337
|
+
const key = optMatch[1].trim().toLowerCase();
|
|
338
|
+
if (key !== 'chart' && key !== 'title') {
|
|
339
|
+
result.options[key] = optMatch[2].trim();
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
378
345
|
// Tag group entries
|
|
379
346
|
if (currentTagGroup && !contentStarted) {
|
|
380
347
|
const indent = measureIndent(line);
|
|
@@ -529,6 +496,55 @@ export function parseC4(
|
|
|
529
496
|
continue;
|
|
530
497
|
}
|
|
531
498
|
|
|
499
|
+
// ── Labeled arrow relationships: -label->, ~label~>, <-label->, <~label~> ──
|
|
500
|
+
// Must be checked BEFORE plain RELATIONSHIP_RE to avoid partial matches
|
|
501
|
+
{
|
|
502
|
+
const labeledPatterns: {
|
|
503
|
+
re: RegExp;
|
|
504
|
+
arrowType: C4ArrowType;
|
|
505
|
+
}[] = [
|
|
506
|
+
{ re: C4_LABELED_BIDI_SYNC_RE, arrowType: 'bidirectional' },
|
|
507
|
+
{ re: C4_LABELED_BIDI_ASYNC_RE, arrowType: 'bidirectional-async' },
|
|
508
|
+
{ re: C4_LABELED_SYNC_RE, arrowType: 'sync' },
|
|
509
|
+
{ re: C4_LABELED_ASYNC_RE, arrowType: 'async' },
|
|
510
|
+
];
|
|
511
|
+
let labeledHandled = false;
|
|
512
|
+
for (const { re, arrowType } of labeledPatterns) {
|
|
513
|
+
const m = trimmed.match(re);
|
|
514
|
+
if (!m) continue;
|
|
515
|
+
const rawLabel = m[1].trim();
|
|
516
|
+
const targetBody = m[2].trim();
|
|
517
|
+
if (!rawLabel) break; // empty label — fall through to plain arrow
|
|
518
|
+
|
|
519
|
+
// Extract [technology] from end of label
|
|
520
|
+
let label: string | undefined = rawLabel;
|
|
521
|
+
let technology: string | undefined;
|
|
522
|
+
const techMatch = rawLabel.match(/\[([^\]]+)\]\s*$/);
|
|
523
|
+
if (techMatch) {
|
|
524
|
+
label = rawLabel.substring(0, techMatch.index!).trim() || undefined;
|
|
525
|
+
technology = techMatch[1].trim();
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const rel: C4Relationship = {
|
|
529
|
+
target: targetBody,
|
|
530
|
+
label,
|
|
531
|
+
technology,
|
|
532
|
+
arrowType,
|
|
533
|
+
lineNumber,
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
const parentEntry = findParentElement(indent, stack);
|
|
537
|
+
if (parentEntry) {
|
|
538
|
+
parentEntry.element.relationships.push(rel);
|
|
539
|
+
} else {
|
|
540
|
+
result.relationships.push(rel);
|
|
541
|
+
}
|
|
542
|
+
labeledHandled = true;
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
if (labeledHandled) continue;
|
|
546
|
+
}
|
|
547
|
+
|
|
532
548
|
// ── Relationships ───────────────────────────────────────
|
|
533
549
|
const relMatch = trimmed.match(RELATIONSHIP_RE);
|
|
534
550
|
if (relMatch) {
|
package/src/c4/renderer.ts
CHANGED
|
@@ -6,6 +6,7 @@ import * as d3Selection from 'd3-selection';
|
|
|
6
6
|
import * as d3Shape from 'd3-shape';
|
|
7
7
|
import { FONT_FAMILY } from '../fonts';
|
|
8
8
|
import type { PaletteColors } from '../palettes';
|
|
9
|
+
import { renderInlineText } from '../utils/inline-markdown';
|
|
9
10
|
import type { ParsedC4 } from './types';
|
|
10
11
|
import type { C4Shape } from './types';
|
|
11
12
|
import type { C4LayoutResult, C4LayoutNode, C4LayoutEdge, C4LayoutBoundary } from './layout';
|
|
@@ -546,20 +547,20 @@ export function renderC4Context(
|
|
|
546
547
|
|
|
547
548
|
yPos += DIVIDER_GAP;
|
|
548
549
|
|
|
549
|
-
// Description (wrapping, muted)
|
|
550
|
+
// Description (wrapping, muted, inline markdown)
|
|
550
551
|
if (node.description) {
|
|
551
552
|
const contentWidth = w - CARD_H_PAD * 2;
|
|
552
553
|
const lines = wrapText(node.description, contentWidth, DESC_CHAR_WIDTH);
|
|
553
554
|
for (const line of lines) {
|
|
554
|
-
nodeG
|
|
555
|
+
const textEl = nodeG
|
|
555
556
|
.append('text')
|
|
556
557
|
.attr('x', 0)
|
|
557
558
|
.attr('y', yPos + DESC_FONT_SIZE / 2)
|
|
558
559
|
.attr('text-anchor', 'middle')
|
|
559
560
|
.attr('dominant-baseline', 'central')
|
|
560
561
|
.attr('fill', palette.textMuted)
|
|
561
|
-
.attr('font-size', DESC_FONT_SIZE)
|
|
562
|
-
|
|
562
|
+
.attr('font-size', DESC_FONT_SIZE);
|
|
563
|
+
renderInlineText(textEl, line, palette, DESC_FONT_SIZE);
|
|
563
564
|
yPos += DESC_LINE_HEIGHT;
|
|
564
565
|
}
|
|
565
566
|
}
|
|
@@ -1617,20 +1618,20 @@ export function renderC4Containers(
|
|
|
1617
1618
|
if (node.type === 'container') {
|
|
1618
1619
|
// Container cards: description above divider, metadata below
|
|
1619
1620
|
|
|
1620
|
-
// Description (above divider)
|
|
1621
|
+
// Description (above divider, inline markdown)
|
|
1621
1622
|
if (node.description) {
|
|
1622
1623
|
const contentWidth = w - CARD_H_PAD * 2;
|
|
1623
1624
|
const lines = wrapText(node.description, contentWidth, DESC_CHAR_WIDTH);
|
|
1624
1625
|
for (const line of lines) {
|
|
1625
|
-
nodeG
|
|
1626
|
+
const textEl = nodeG
|
|
1626
1627
|
.append('text')
|
|
1627
1628
|
.attr('x', 0)
|
|
1628
1629
|
.attr('y', yPos + DESC_FONT_SIZE / 2)
|
|
1629
1630
|
.attr('text-anchor', 'middle')
|
|
1630
1631
|
.attr('dominant-baseline', 'central')
|
|
1631
1632
|
.attr('fill', palette.textMuted)
|
|
1632
|
-
.attr('font-size', DESC_FONT_SIZE)
|
|
1633
|
-
|
|
1633
|
+
.attr('font-size', DESC_FONT_SIZE);
|
|
1634
|
+
renderInlineText(textEl, line, palette, DESC_FONT_SIZE);
|
|
1634
1635
|
yPos += DESC_LINE_HEIGHT;
|
|
1635
1636
|
}
|
|
1636
1637
|
}
|
|
@@ -1696,20 +1697,20 @@ export function renderC4Containers(
|
|
|
1696
1697
|
|
|
1697
1698
|
yPos += DIVIDER_GAP;
|
|
1698
1699
|
|
|
1699
|
-
// Description
|
|
1700
|
+
// Description (inline markdown)
|
|
1700
1701
|
if (node.description) {
|
|
1701
1702
|
const contentWidth = w - CARD_H_PAD * 2;
|
|
1702
1703
|
const lines = wrapText(node.description, contentWidth, DESC_CHAR_WIDTH);
|
|
1703
1704
|
for (const line of lines) {
|
|
1704
|
-
nodeG
|
|
1705
|
+
const textEl = nodeG
|
|
1705
1706
|
.append('text')
|
|
1706
1707
|
.attr('x', 0)
|
|
1707
1708
|
.attr('y', yPos + DESC_FONT_SIZE / 2)
|
|
1708
1709
|
.attr('text-anchor', 'middle')
|
|
1709
1710
|
.attr('dominant-baseline', 'central')
|
|
1710
1711
|
.attr('fill', palette.textMuted)
|
|
1711
|
-
.attr('font-size', DESC_FONT_SIZE)
|
|
1712
|
-
|
|
1712
|
+
.attr('font-size', DESC_FONT_SIZE);
|
|
1713
|
+
renderInlineText(textEl, line, palette, DESC_FONT_SIZE);
|
|
1713
1714
|
yPos += DESC_LINE_HEIGHT;
|
|
1714
1715
|
}
|
|
1715
1716
|
}
|
package/src/c4/types.ts
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
// C4 Architecture Diagram — Types
|
|
3
3
|
// ============================================================
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type { TagGroup, TagEntry } from '../utils/tag-groups';
|
|
6
6
|
import type { DgmoError } from '../diagnostics';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
export type
|
|
8
|
+
/** @deprecated Use `TagEntry` from `utils/tag-groups` */
|
|
9
|
+
export type C4TagEntry = TagEntry;
|
|
10
|
+
/** @deprecated Use `TagGroup` from `utils/tag-groups` */
|
|
11
|
+
export type C4TagGroup = TagGroup;
|
|
10
12
|
|
|
11
13
|
// ── String unions ────────────────────────────────────────────
|
|
12
14
|
|
|
@@ -77,7 +79,7 @@ export interface ParsedC4 {
|
|
|
77
79
|
title: string | null;
|
|
78
80
|
titleLineNumber: number | null;
|
|
79
81
|
options: Record<string, string>;
|
|
80
|
-
tagGroups:
|
|
82
|
+
tagGroups: TagGroup[];
|
|
81
83
|
elements: C4Element[];
|
|
82
84
|
relationships: C4Relationship[];
|
|
83
85
|
deployment: C4DeploymentNode[];
|
package/src/chart.ts
CHANGED
|
@@ -37,7 +37,7 @@ export interface ParsedChart {
|
|
|
37
37
|
labels?: 'name' | 'value' | 'percent' | 'full';
|
|
38
38
|
data: ChartDataPoint[];
|
|
39
39
|
diagnostics: DgmoError[];
|
|
40
|
-
error
|
|
40
|
+
error: string | null;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
// ============================================================
|
|
@@ -90,6 +90,7 @@ export function parseChart(
|
|
|
90
90
|
type: 'bar',
|
|
91
91
|
data: [],
|
|
92
92
|
diagnostics: [],
|
|
93
|
+
error: null,
|
|
93
94
|
};
|
|
94
95
|
|
|
95
96
|
const fail = (line: number, message: string): ParsedChart => {
|
|
@@ -110,7 +111,7 @@ export function parseChart(
|
|
|
110
111
|
if (/^#{2,}\s+/.test(trimmed)) continue;
|
|
111
112
|
|
|
112
113
|
// Skip comments
|
|
113
|
-
if (trimmed.startsWith('
|
|
114
|
+
if (trimmed.startsWith('//')) continue;
|
|
114
115
|
|
|
115
116
|
// Parse key: value pairs
|
|
116
117
|
const colonIndex = trimmed.indexOf(':');
|
package/src/class/parser.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolveColor } from '../colors';
|
|
2
2
|
import type { PaletteColors } from '../palettes';
|
|
3
3
|
import { makeDgmoError, formatDgmoError, suggest } from '../diagnostics';
|
|
4
|
+
import { measureIndent } from '../utils/parsing';
|
|
4
5
|
import type {
|
|
5
6
|
ParsedClassDiagram,
|
|
6
7
|
ClassNode,
|
|
@@ -15,16 +16,6 @@ import type {
|
|
|
15
16
|
// Helpers
|
|
16
17
|
// ============================================================
|
|
17
18
|
|
|
18
|
-
function measureIndent(line: string): number {
|
|
19
|
-
let indent = 0;
|
|
20
|
-
for (const ch of line) {
|
|
21
|
-
if (ch === ' ') indent++;
|
|
22
|
-
else if (ch === '\t') indent += 4;
|
|
23
|
-
else break;
|
|
24
|
-
}
|
|
25
|
-
return indent;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
19
|
function classId(name: string): string {
|
|
29
20
|
return name.toLowerCase().trim();
|
|
30
21
|
}
|
|
@@ -172,6 +163,7 @@ export function parseClassDiagram(
|
|
|
172
163
|
relationships: [],
|
|
173
164
|
options: {},
|
|
174
165
|
diagnostics: [],
|
|
166
|
+
error: null,
|
|
175
167
|
};
|
|
176
168
|
|
|
177
169
|
const fail = (line: number, message: string): ParsedClassDiagram => {
|
package/src/class/types.ts
CHANGED