@cardenelabs/dragon 0.28.0 → 0.30.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.
Files changed (57) hide show
  1. package/README.md +78 -5
  2. package/dist/index.cjs +9246 -8698
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +186 -66
  5. package/dist/index.d.ts +186 -66
  6. package/dist/index.js +9247 -8700
  7. package/dist/index.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/compile/actor-ids.ts +306 -0
  10. package/src/compile/actors.ts +74 -0
  11. package/src/compile/c4.ts +77 -0
  12. package/src/compile/chain.ts +54 -0
  13. package/src/compile/chart-fields.ts +178 -0
  14. package/src/compile/class.ts +100 -0
  15. package/src/compile/deep-rewrite.ts +30 -0
  16. package/src/compile/derived-source.ts +18 -0
  17. package/src/compile/direction.ts +63 -0
  18. package/src/compile/er-relation.ts +200 -0
  19. package/src/compile/er.ts +44 -0
  20. package/src/compile/flow.ts +54 -0
  21. package/src/compile/funnel.ts +75 -0
  22. package/src/compile/gantt.ts +232 -0
  23. package/src/compile/generic.ts +284 -0
  24. package/src/compile/hierarchy.ts +92 -0
  25. package/src/compile/journey.ts +84 -0
  26. package/src/compile/kinds.ts +156 -0
  27. package/src/compile/lanes.ts +56 -0
  28. package/src/compile/mind.ts +343 -0
  29. package/src/compile/node-title.ts +24 -0
  30. package/src/compile/notice.ts +94 -0
  31. package/src/compile/parts-lookup.ts +17 -0
  32. package/src/compile/parts.ts +2635 -0
  33. package/src/compile/placement-apply.ts +673 -0
  34. package/src/compile/placement.ts +15 -0
  35. package/src/compile/quadrant.ts +110 -0
  36. package/src/compile/rows.ts +40 -0
  37. package/src/compile/sequence.ts +226 -0
  38. package/src/compile/slug.ts +29 -0
  39. package/src/compile/solidity.ts +39 -0
  40. package/src/compile/state.ts +71 -0
  41. package/src/compile/subtitle.ts +22 -0
  42. package/src/compile/swimlane.ts +83 -0
  43. package/src/compile/topology.ts +50 -0
  44. package/src/compile/tree.ts +60 -0
  45. package/src/compile/value-chart.ts +183 -0
  46. package/src/compile/values.ts +432 -0
  47. package/src/compile/word-state.ts +82 -0
  48. package/src/compile.ts +513 -6858
  49. package/src/index.ts +8 -7
  50. package/src/json-line-map.ts +30 -0
  51. package/src/json-parser.ts +356 -123
  52. package/src/near-name.ts +42 -0
  53. package/src/relative-pos.ts +71 -2
  54. package/src/schema.ts +1 -1
  55. package/src/schemas/diagram.json +38 -4
  56. package/src/types.ts +1 -1
  57. package/src/v05/parser.ts +91 -72
@@ -0,0 +1,50 @@
1
+ import { diagram, topology } from "@cardenelabs/cdl";
2
+ import { 箱の題 } from "./node-title";
3
+ import type { CdlDiagram } from "@cardenelabs/cdl";
4
+ import type { DslDocument } from "../types";
5
+
6
+ import { compileGenericWithAnimate } from "./generic";
7
+ import { 描ける種別 } from "./kinds";
8
+ import { 書いた縦列に置く } from "./lanes";
9
+
10
+ import { slugify } from "./slug";
11
+
12
+ export function compileTopology(doc: DslDocument): CdlDiagram {
13
+ // v0.4 ... animation あり時 builder 直接経路 (各 actor を別 lane に)
14
+ // **縦列を書いた形は動きの有無に関わらず generic 経路へ** (#1263)。 動く図だけで効かせると、
15
+ // 同じ記法でも静止図では指定が黙って消える (実測 = 縦列 3 本のはずが 1 本になり知らせも出ない)
16
+ if ((doc.animate && doc.animate.phases.length > 0) || 書いた縦列に置く("topology", doc)) {
17
+ return compileGenericWithAnimate(doc, { kind: "topology", laneWidth: 460 });
18
+ }
19
+ // 登場人物が 0 人なら枠も作らない。 描画側の `topology()` は枠を必ず 1 つ作るため、 そのまま
20
+ // 通すと中身の無い枠が残る (#1096)
21
+ if (doc.actors.length === 0) {
22
+ return diagram(slugify(doc.title), { topic: doc.title, type: "topology" }).build();
23
+ }
24
+ // topology preset ... actors を 1 つの group 内 container として配置
25
+ // v0.3 で「group」 ブロックを追加して複数 group 対応検討
26
+ const topo = topology({
27
+ id: slugify(doc.title),
28
+ topic: doc.title,
29
+ });
30
+ const groupId = "main";
31
+ const groupBuilder = topo.group(groupId, { label: doc.title });
32
+ for (const a of doc.actors) {
33
+ groupBuilder.add({
34
+ id: slugify(a.name) || a.name,
35
+ kind: 描ける種別(a.kind),
36
+ title: 箱の題(a),
37
+ });
38
+ }
39
+ for (const s of doc.flow) {
40
+ topo.connect(slugify(s.from), slugify(s.to), {
41
+ label: s.label,
42
+ ...(s.sub ? { sub: s.sub } : {}),
43
+ ...(s.side ? { side: s.side } : {}),
44
+
45
+ ...(s.tone ? { tone: s.tone } : {}),
46
+ ...(s.style ? { style: s.style } : {}),
47
+ });
48
+ }
49
+ return topo.build();
50
+ }
@@ -0,0 +1,60 @@
1
+ import { diagram } from "@cardenelabs/cdl";
2
+ import type { CdlDiagram } from "@cardenelabs/cdl";
3
+ import type { DslDocument } from "../types";
4
+ import { 図表の大きさ } from "./chart-fields";
5
+ import { 矢印から親を決める, 放射に出す文字 } from "./hierarchy";
6
+ import type { CompileNotice } from "./notice";
7
+ import { slugify } from "./slug";
8
+ import { 図の小見出し } from "./subtitle";
9
+ /**
10
+ * 木 (`type: tree`) の組み立て (#1154)。 描画側に 1 つの箱で渡す。
11
+ *
12
+ * 親子を `flow` の矢印で読む (`親 -> 子`)。 他の図表と違って `flow` を読むのは、親子が
13
+ * 2 つの名前の関係で、 1 行 1 値では書けないため。
14
+ */
15
+ export function compileTree(doc: DslDocument, onNotice?: (n: CompileNotice) => void): CdlDiagram {
16
+ const b = diagram(slugify(doc.title), { topic: doc.title, type: "tree" });
17
+ const { w: W, h: H } = 図表の大きさ.tree;
18
+ b.lane("chart", { width: W + 64 });
19
+ // **同じ slug になる名前を先に見る**。 違う名前が同じ id に潰れると、 自分を親にしたと
20
+ // 誤判定したり、 同じ id の要素が 2 つできたりする (review 指摘)
21
+ const slug別 = new Map<string, string[]>();
22
+ for (const a of doc.actors) {
23
+ const k = slugify(a.name);
24
+ slug別.set(k, [...(slug別.get(k) ?? []), a.name]);
25
+ }
26
+ const 名前 = new Set(slug別.keys());
27
+ // **行番号を渡す**。 `DslActor` / `DslStep` は `pos.line` を持つので遡れる。 前回「持てない」
28
+ // と書いたのは誤り (review 指摘)。 0 にすると画面が問題の行を案内できない
29
+ const 伝える = (名: string, message: string, line = 0) => {
30
+ onNotice?.({ kind: "chart-value-unreadable", actor: 名, line, message });
31
+ if (typeof console !== "undefined" && console.warn) console.warn(`[dragon] ${message}`);
32
+ };
33
+ for (const [k, 群] of slug別) {
34
+ if (群.length > 1) {
35
+ 伝える(
36
+ 群[0]!,
37
+ `type: tree で ${群.join(" / ")} が同じ id (${k}) になります。 名前を変えてください`,
38
+ );
39
+ }
40
+ }
41
+ const 親 = 矢印から親を決める(doc, "tree", 名前, 伝える);
42
+ const data: NonNullable<CdlDiagram["nodes"][number]["treeData"]> = doc.actors.map((a) => {
43
+ const id = slugify(a.name);
44
+ const p3 = 親.get(id);
45
+ // 木も放射と同じく名前と補足を分けて渡す (#1332)。 分けないと補足が捨てられ、
46
+ // 書いた文字が図に出ない
47
+ return { id, ...放射に出す文字(a), ...(p3 !== undefined ? { parent: p3 } : {}) };
48
+ });
49
+ b.node(`${slugify(doc.title) || "tree"}-chart`, {
50
+ lane: "chart",
51
+ stack: 0,
52
+ kind: "tree-hierarchy",
53
+ title: doc.title,
54
+ ...図の小見出し(doc),
55
+ w: W,
56
+ h: H,
57
+ treeData: data,
58
+ });
59
+ return b.build();
60
+ }
@@ -0,0 +1,183 @@
1
+ import { diagram } from "@cardenelabs/cdl";
2
+ import type { CdlDiagram } from "@cardenelabs/cdl";
3
+ import type { DslDocument } from "../types";
4
+ import { parseChartValue, 数の欄から参照できる名前, 参照する名前 } from "./chart-fields";
5
+
6
+ import { 箱の題 } from "./node-title";
7
+ import type { CompileNotice } from "./notice";
8
+ import { slugify } from "./slug";
9
+ import { 図の小見出し } from "./subtitle";
10
+ /**
11
+ * 棒 / 折れ線の組立て。 円グラフと **入力の形が同じ**なので 1 つにまとめる。
12
+ *
13
+ * 3 種とも `- 名前: "45"` の 1 行 1 値で書く。 違うのは描画側の種別と、 値の意味だけ。
14
+ *
15
+ * | 型 | 種別 | 値の意味 |
16
+ * |---|---|---|
17
+ * | `pie` | `chart-pie` | 全体に対する取り分 |
18
+ * | `bar` | `chart-bar` | 棒の高さ (単位は問わない) |
19
+ * | `line` | `chart-line` | 線の高さ。 **書いた順に並ぶ** |
20
+ *
21
+ * 値を読めない項目は載せず、 まとめて警告に出す。 **黙って 0 にしない** = その項目だけ欠けた
22
+ * 図が「正しい図」 として出てしまうため。
23
+ *
24
+ * 矢印は描けない。 書かれていたら警告に出して捨てる (「書いたのに効かない」 を残さない)。
25
+ */
26
+ export function compileValueChart(
27
+ doc: DslDocument,
28
+ 型: "pie" | "bar" | "line" | "gauge" | "radial" | "stat" | "waffle" | "stacked" | "slope",
29
+ kind:
30
+ | "chart-pie"
31
+ | "chart-bar"
32
+ | "chart-line"
33
+ | "chart-gauge"
34
+ | "chart-radial"
35
+ | "chart-stat"
36
+ | "chart-waffle"
37
+ | "chart-stacked-bar"
38
+ | "chart-slope",
39
+ onNotice?: (notice: CompileNotice) => void,
40
+ ): CdlDiagram {
41
+ const b = diagram(slugify(doc.title), { topic: doc.title, type: "chart" });
42
+ const CHART_W = 640;
43
+ // **高さは型で違い、 格子に載せる**。 描画側 (`cdl` の `chart()` preset) は `pie` を 320、
44
+ // 棒と折れ線を 360 とした上で **16 の倍数へ切り上げる** (360 は 16 で割り切れないので 368)。
45
+ // 切り上げないと下端が格子から外れ、 全図で位置の警告が出る (review 指摘)
46
+ // 半円と弧は縦を使わないので円と同じ 320。 描画側 (`cdl` の `chart()` preset) が
47
+ // `pie` / `gauge` / `radial` を 320、棒と折れ線を 360 とし、16 の倍数へ切り上げる
48
+ // 縦に余白が要らない型。 描画側 (`cdl` の `chart()` preset) と揃える。
49
+ // 半円 / 弧 / 割合の印 は縦を使わず、値 1 つを大きく示す図も縦に伸びない
50
+ const 低い型 =
51
+ 型 === "pie" || 型 === "gauge" || 型 === "radial" || 型 === "waffle" || 型 === "stat";
52
+ const CHART_H = 低い型 ? 320 : 368;
53
+ b.lane("chart", { width: CHART_W + 64 });
54
+
55
+ const data: NonNullable<CdlDiagram["nodes"][number]["chartData"]> = [];
56
+ const 読めない: string[] = [];
57
+ const 前が読めない: string[] = [];
58
+ let 前が読めない行 = 0;
59
+ const 未宣言: string[] = [];
60
+ let 未宣言行 = 0;
61
+ const 参照できる = 数の欄から参照できる名前(doc);
62
+ // 最初に読めなかった行を覚える。 画面が案内できるようにする
63
+ let 読めない行 = 0;
64
+ for (const a of doc.actors) {
65
+ // 値の置き場所は記法で 2 通りある。 略記 (`- TypeScript: "45%"`) は説明文に、
66
+ // 縦書きの map (`- SliceA: { kind: card, value: "30%" }`) は値に入る。 両方を読む
67
+ const value = parseChartValue(a.value ?? a.subtitle);
68
+ // **負を受けるのは折れ線と傾き図だけ**。 どちらも増減を追う図なので、気温や損益のように
69
+ // 0 を跨ぐ値が来る。 円は取り分、 棒は高さで、 どちらも負に意味が無い (review 指摘)。
70
+ // 状態を読む欄 (`{名前}`) は書いた時点で符号が決まらないため、この検査を通す
71
+ if (
72
+ value === null ||
73
+ (typeof value === "number" && value < 0 && 型 !== "line" && 型 !== "slope")
74
+ ) {
75
+ // `pos` を持たない経路がある (JSON 経路で組み立てた actor)。 無ければ 0 のまま
76
+ if (読めない.length === 0) 読めない行 = a.pos?.line ?? 0;
77
+ 読めない.push(a.name);
78
+ continue;
79
+ }
80
+ // 数にならない参照は落とす。 通すと図は出るのに数が入っていない状態になる
81
+ const 名前 = 参照する名前(value);
82
+ if (名前 !== null && !参照できる.has(名前)) {
83
+ if (未宣言.length === 0) 未宣言行 = a.pos?.line ?? 0;
84
+ 未宣言.push(a.name);
85
+ continue;
86
+ }
87
+ // 色はそのまま渡す。 箱が 1 つになっても、 書いた色が消えないようにする
88
+ // 前の時点の値 (#1450)。 読めない形は黙って捨てず知らせる = 書いたのに 2 本目の帯が
89
+ // 出ない状態になり、手掛かりが残らない
90
+ const 前 = a.previous === undefined ? null : parseChartValue(a.previous);
91
+ if (a.previous !== undefined && 前 === null) {
92
+ if (前が読めない.length === 0) 前が読めない行 = a.pos?.line ?? 0;
93
+ 前が読めない.push(a.name);
94
+ }
95
+ data.push({
96
+ label: 箱の題(a),
97
+ value,
98
+ ...(前 === null ? {} : { previous: 前 }),
99
+ ...(a.tone !== undefined ? { tone: a.tone } : {}),
100
+ });
101
+ }
102
+ // 案内の言葉は型ごとに変える。 共通化した時に `pie` の「割合 / 円 / 45%」 が「値 / 図 / 45」 に
103
+ // 薄まり、 既存の案内が後退した (review 指摘)。 何を書けばよいかは型ごとに違う
104
+ // 何を書けばよいかは型ごとに違う。 まとめると「割合 / 円 / 45%」 が「値 / 図 / 45」 に薄まる
105
+ //
106
+ // **表で持つ**。 三項の連鎖にすると、型が増えるたびに深さが増え、最後の枝が
107
+ // 「それ以外」 になるため型検査が新しい型の漏れを教えてくれない
108
+ const 語の表: Record<typeof 型, { 量: string; 図: string; 例: string }> = {
109
+ pie: { 量: "割合", 図: "円", 例: '"45%"' },
110
+ bar: { 量: "値", 図: "棒", 例: '"420"' },
111
+ line: { 量: "値", 図: "折れ線", 例: '"180"' },
112
+ gauge: { 量: "値", 図: "半円", 例: '"680"' },
113
+ radial: { 量: "値", 図: "弧", 例: '"72"' },
114
+ stat: { 量: "値", 図: "大きな数字", 例: '"1200"' },
115
+ waffle: { 量: "割合", 図: "100 個の印", 例: '"45%"' },
116
+ stacked: { 量: "内訳の値", 図: "帯", 例: '"320"' },
117
+ slope: { 量: "値", 図: "傾き図", 例: '"320"' },
118
+ };
119
+ const 語 = 語の表[型];
120
+
121
+ /**
122
+ * 利用者に伝える。 **`console.warn` だけにしない**。 エディタは受け取った notice を画面に
123
+ * 出す経路を持っており、 log だけだと項目が消えた理由が誰にも見えない (review 指摘)。
124
+ */
125
+ const 伝える = (種類: CompileNotice["kind"], 名前: string, message: string, line = 0) => {
126
+ onNotice?.({ kind: 種類, actor: 名前, line, message });
127
+ if (typeof console !== "undefined" && console.warn) console.warn(`[dragon] ${message}`);
128
+ };
129
+
130
+ if (読めない.length > 0) {
131
+ 伝える(
132
+ "chart-value-unreadable",
133
+ 読めない[0]!,
134
+ `type: ${型} で${語.量}を読めない項目があります (${語.図}に載せません): ${読めない.join(", ")}。` +
135
+ ` \`- 名前: ${語.例}\` の形で書いてください`,
136
+ 読めない行,
137
+ );
138
+ }
139
+ if (未宣言.length > 0) {
140
+ 伝える(
141
+ "chart-value-unreadable",
142
+ 未宣言[0]!,
143
+ `type: ${型} で数にならない値を参照した項目があります (${語.図}に載せません): ${未宣言.join(", ")}。` +
144
+ ` \`states:\` にその名前を数で書いてください`,
145
+ 未宣言行,
146
+ );
147
+ }
148
+ if (前が読めない.length > 0) {
149
+ 伝える(
150
+ "chart-value-unreadable",
151
+ 前が読めない[0]!,
152
+ `type: ${型} で前の時点の値を読めない項目があります (前の値を使いません): ${前が読めない.join(", ")}。` +
153
+ " `- 名前: { value: " +
154
+ 語.例 +
155
+ ", previous: " +
156
+ 語.例 +
157
+ " }` の形で書いてください",
158
+ 前が読めない行,
159
+ );
160
+ }
161
+ if (doc.flow.length > 0) {
162
+ 伝える(
163
+ "chart-edge-dropped",
164
+ doc.flow[0]?.from ?? "",
165
+ `type: ${型} では矢印を描けません (${doc.flow.length} 本を無視しました)。` +
166
+ ` 関係を描くなら type: flow を使ってください`,
167
+ doc.flow[0]?.pos?.line ?? 0,
168
+ );
169
+ }
170
+
171
+ b.node(`${slugify(doc.title) || 型}-chart`, {
172
+ lane: "chart",
173
+ stack: 0,
174
+ kind,
175
+ title: doc.title,
176
+ ...図の小見出し(doc),
177
+ w: CHART_W,
178
+ h: CHART_H,
179
+ chartData: data,
180
+ });
181
+
182
+ return b.build();
183
+ }