@cardenelabs/dragon 0.28.0 → 0.32.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/README.md +391 -16
- package/dist/index.cjs +13479 -7403
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2360 -208
- package/dist/index.d.ts +2360 -208
- package/dist/index.js +13462 -7405
- package/dist/index.js.map +1 -1
- package/package.json +12 -13
- package/src/compile/actor-ids.ts +297 -0
- package/src/compile/actor-option-notice.ts +379 -0
- package/src/compile/actors.ts +103 -0
- package/src/compile/c4.ts +74 -0
- package/src/compile/chain.ts +93 -0
- package/src/compile/chart-fields.ts +193 -0
- package/src/compile/deep-rewrite.ts +30 -0
- package/src/compile/derived-source.ts +18 -0
- package/src/compile/direction.ts +102 -0
- package/src/compile/edge-option-notice.ts +112 -0
- package/src/compile/er-relation.ts +200 -0
- package/src/compile/flow.ts +54 -0
- package/src/compile/flowchart.ts +344 -0
- package/src/compile/funnel.ts +87 -0
- package/src/compile/gantt.ts +337 -0
- package/src/compile/generic.ts +696 -0
- package/src/compile/hierarchy.ts +92 -0
- package/src/compile/journey.ts +93 -0
- package/src/compile/kinds.ts +148 -0
- package/src/compile/lanes.ts +138 -0
- package/src/compile/metro.ts +271 -0
- package/src/compile/mind.ts +393 -0
- package/src/compile/node-title.ts +24 -0
- package/src/compile/notice.ts +141 -0
- package/src/compile/order.ts +62 -0
- package/src/compile/parts-lookup.ts +17 -0
- package/src/compile/parts.ts +2709 -0
- package/src/compile/placement-apply.ts +689 -0
- package/src/compile/placement.ts +15 -0
- package/src/compile/quadrant.ts +265 -0
- package/src/compile/record.ts +75 -0
- package/src/compile/row-marks.ts +128 -0
- package/src/compile/rows.ts +40 -0
- package/src/compile/sequence.ts +305 -0
- package/src/compile/slug.ts +37 -0
- package/src/compile/stages.ts +247 -0
- package/src/compile/subtitle.ts +35 -0
- package/src/compile/swimlane-marks.ts +87 -0
- package/src/compile/swimlane.ts +82 -0
- package/src/compile/timeline.ts +213 -0
- package/src/compile/topology.ts +44 -0
- package/src/compile/tree.ts +69 -0
- package/src/compile/value-chart.ts +337 -0
- package/src/compile/values.ts +432 -0
- package/src/compile/word-state.ts +82 -0
- package/src/compile.ts +1806 -6971
- package/src/focus.ts +2 -2
- package/src/index.ts +42 -9
- package/src/json-line-map.ts +30 -0
- package/src/json-parser.ts +1760 -168
- package/src/keywords.ts +251 -17
- package/src/near-name.ts +42 -0
- package/src/notation-lint.ts +41 -64
- package/src/relative-pos.ts +71 -2
- package/src/schema.ts +1 -1
- package/src/schemas/diagram.json +680 -23
- package/src/types.ts +337 -36
- package/src/v05/parser.ts +2553 -354
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 箱に書いた指定が効かない時の、伝える欄の割り出しと呼び名 (#2368 で 1 箇所にまとめた)。
|
|
3
|
+
*
|
|
4
|
+
* 同じ判定を図種の族ごとの経路が使う (経路の数は下の表ではなく実装が SSOT)。
|
|
5
|
+
*
|
|
6
|
+
* | 経路 | 図種 | 起点 |
|
|
7
|
+
* |---|---|---|
|
|
8
|
+
* | 板の面 | `sequence` / `solidity` | #2358 |
|
|
9
|
+
* | 木の図 | `tree` | #2360 |
|
|
10
|
+
* | 値の図とじょうご | `pie` ほか 9 図種 + `funnel` | #2368 |
|
|
11
|
+
* | ガントチャートと体験の地図と四象限 | `gantt` / `journey` / `quadrant` | #2370 |
|
|
12
|
+
* | 骨組みの図 | `flow` / `flowchart` / `swimlane` / `topology` / `c4` / `record` | #2376 / #2382 / #2782 |
|
|
13
|
+
*
|
|
14
|
+
* 矢印の側は `edge-option-notice.ts` が同じ形を持つ (#2366)。
|
|
15
|
+
*/
|
|
16
|
+
import type { DslActor } from "../types";
|
|
17
|
+
import type { DslDiagramShape } from "../keywords";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 効かない箱の欄の呼び名 (#2358)。 **並べた順に知らせへ出す**。
|
|
21
|
+
*
|
|
22
|
+
* 呼び名を持たない欄は名前をそのまま出すので、ここに足し忘れても知らせは消えない。
|
|
23
|
+
* 複数の欄が 1 つの呼び名を持つ形 (位置が 4 欄) は、1 度だけ出す。
|
|
24
|
+
*
|
|
25
|
+
* **図種をまたいで 1 つの表にする** (#2360)。 同じ欄を図種ごとに別の文言で呼ぶと、
|
|
26
|
+
* 図種を変えた読み手が同じ指定だと気付けない。 どの欄を伝えるかは図種ごとの除外が決める。
|
|
27
|
+
*
|
|
28
|
+
* **箱に無い欄名を書けないようにする** (#2384)。 表と除外は箱の欄と文字で突き合わせるので、
|
|
29
|
+
* 綴りが箱と違う行は静かに何とも一致しない。 実測 = 記法の `色:` に引きずられて書いた
|
|
30
|
+
* `color` が、箱に生まれない欄名のまま残っていた (箱に入るのは `colorHex` か `tone`)。
|
|
31
|
+
*/
|
|
32
|
+
export const 効かない箱の欄の呼び名 = [
|
|
33
|
+
["kind", "種類"],
|
|
34
|
+
// 呼び名 (#2390)。 板 / 木 / 骨組みの図は箱に描くので、伝えるのは値として読む図だけ
|
|
35
|
+
["subtitle", "呼び名"],
|
|
36
|
+
["posW", "大きさ"],
|
|
37
|
+
["posH", "大きさ"],
|
|
38
|
+
["posX", "位置"],
|
|
39
|
+
["posY", "位置"],
|
|
40
|
+
["posRel", "位置"],
|
|
41
|
+
// 位置のずらし (#1971)。 板は面ごとの箱を持たないので動かす相手が無い
|
|
42
|
+
["layoutPos", "位置"],
|
|
43
|
+
["rows", "行"],
|
|
44
|
+
["tone", "色"],
|
|
45
|
+
["colorHex", "色"],
|
|
46
|
+
["eyebrow", "小見出し"],
|
|
47
|
+
["value", "値"],
|
|
48
|
+
["shape", "図形"],
|
|
49
|
+
["previous", "前の値"],
|
|
50
|
+
["marks", "印"],
|
|
51
|
+
["stack", "段"],
|
|
52
|
+
["initial", "始まりの印"],
|
|
53
|
+
["final", "終わりの印"],
|
|
54
|
+
["visibleIf", "出す条件"],
|
|
55
|
+
["opacity", "透け具合"],
|
|
56
|
+
["wBind", "値への追随"],
|
|
57
|
+
["hBind", "値への追随"],
|
|
58
|
+
["renderOffsetX", "値への追随"],
|
|
59
|
+
["renderOffsetY", "値への追随"],
|
|
60
|
+
["owner", "担当"],
|
|
61
|
+
["stage", "段階"],
|
|
62
|
+
["end", "終わる時期"],
|
|
63
|
+
["startLabel", "始まりの札"],
|
|
64
|
+
["milestone", "節目"],
|
|
65
|
+
["emphasis", "主役"],
|
|
66
|
+
["at", "点の位置"],
|
|
67
|
+
["atPos", "点の位置"],
|
|
68
|
+
["touchpoint", "接点"],
|
|
69
|
+
["opportunity", "伸びしろ"],
|
|
70
|
+
["opportunityPosition", "伸びしろの位置"],
|
|
71
|
+
] as const satisfies readonly (readonly [keyof DslActor, string])[];
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 体験と工程の欄 (#2380)。 **どの族の除外にも入る**。
|
|
75
|
+
*
|
|
76
|
+
* 担当 (`owner`) と終わる時期 (`end`) はガントチャートが、接点 (`touchpoint`) と伸びしろ
|
|
77
|
+
* (`opportunity`) と伸びしろの位置 (`opportunityPosition`) は体験の地図が描く。 描けない図種で書いた時は
|
|
78
|
+
* `reportChartFieldsNotHonored` が図種ごとの行き先を添えて伝える。
|
|
79
|
+
*
|
|
80
|
+
* 箱の知らせが同じ欄を重ねて伝えると、1 つの欄が 2 つの名前で呼ばれる (「担当」 と `owner`)
|
|
81
|
+
* うえ、案内が食い違う = 箱の知らせは「箱を持つ図種へ」 と言うが、担当は箱を持つ図種でも
|
|
82
|
+
* 効かない。
|
|
83
|
+
*
|
|
84
|
+
* **1 か所に置いて各族が読む**。 欄を族ごとに書き並べると、新しい欄を足した日に
|
|
85
|
+
* 直し忘れた族だけが 2 件並べるようになる (実測 = 板の族だけが 4 欄を持たず、
|
|
86
|
+
* `sequence` / `solidity` の 8 通りで知らせが 2 件並んでいた)。
|
|
87
|
+
*/
|
|
88
|
+
export const 体験と工程の欄: ReadonlySet<string> = new Set([
|
|
89
|
+
"owner",
|
|
90
|
+
"end",
|
|
91
|
+
"startLabel",
|
|
92
|
+
"milestone",
|
|
93
|
+
"emphasis",
|
|
94
|
+
"touchpoint",
|
|
95
|
+
"opportunity",
|
|
96
|
+
"opportunityPosition",
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* 板の面で、効かないと伝えない箱の欄 (#2358)。
|
|
101
|
+
*
|
|
102
|
+
* **除外側を書く**。 効かない欄を並べる形にすると、箱に欄を足した日にその欄だけが
|
|
103
|
+
* 一覧から漏れ、書いた人には「書いたのに何も起きない」 としか見えない。
|
|
104
|
+
*
|
|
105
|
+
* | 区分 | 欄 |
|
|
106
|
+
* |---|---|
|
|
107
|
+
* | 板が描く | 名前 / 題 / 呼び名 と、書いた場所と種類を書いたかの印 |
|
|
108
|
+
* | 別の知らせが受け持つ | 縦列 (`reportLaneNotHonored`) / 体験と工程の 4 欄 (#2380) |
|
|
109
|
+
*
|
|
110
|
+
* 種類 (`kind`) は図種で分かれるので、判定の側で足す。
|
|
111
|
+
*/
|
|
112
|
+
export const 板が伝えない箱の欄: ReadonlySet<string> = new Set([
|
|
113
|
+
"name",
|
|
114
|
+
"title",
|
|
115
|
+
"subtitle",
|
|
116
|
+
"pos",
|
|
117
|
+
"kindWritten",
|
|
118
|
+
"lane",
|
|
119
|
+
...体験と工程の欄,
|
|
120
|
+
]);
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 木の図で、効かないと伝えない箱の欄 (#2360)。
|
|
124
|
+
*
|
|
125
|
+
* | 区分 | 欄 |
|
|
126
|
+
* |---|---|
|
|
127
|
+
* | 木の図が描く | 名前 / 題 / 呼び名 / 値 と、書いた場所と種類を書いたかの印 |
|
|
128
|
+
* | 別の知らせが受け持つ | 縦列 / 位置のずらし / 体験と工程の 4 欄 |
|
|
129
|
+
*
|
|
130
|
+
* 別の知らせは順に `lane-not-honored` / `position-offset-ignored` / `chart-value-unreadable`。
|
|
131
|
+
* 除かないと同じ箱に 2 件並ぶ。 体験と工程の 4 欄は `体験と工程の欄` から読む (#2380)。
|
|
132
|
+
*/
|
|
133
|
+
export const 木の図が伝えない箱の欄: ReadonlySet<string> = new Set([
|
|
134
|
+
"name",
|
|
135
|
+
"title",
|
|
136
|
+
"subtitle",
|
|
137
|
+
"value",
|
|
138
|
+
"pos",
|
|
139
|
+
"kindWritten",
|
|
140
|
+
"lane",
|
|
141
|
+
"layoutPos",
|
|
142
|
+
...体験と工程の欄,
|
|
143
|
+
]);
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 箱を名前と値の組として読む図で、どの図種でも効かないと伝えない箱の欄 (#2368 / #2370)。
|
|
147
|
+
*
|
|
148
|
+
* | 区分 | 欄 |
|
|
149
|
+
* |---|---|
|
|
150
|
+
* | どの図種も読む | 名前 / 題 / 値 と、書いた場所と種類を書いたかの印 |
|
|
151
|
+
* | 別の知らせが受け持つ | 縦列 / 位置のずらし / 体験と工程の 4 欄 |
|
|
152
|
+
*
|
|
153
|
+
* 呼び名 (`subtitle`) は除かない (#2390)。 値を書いていない箱で **値として読まれる**
|
|
154
|
+
* (`a.value ?? a.subtitle`) ので除いていたが、**値を書いた箱では読む先が無い**。
|
|
155
|
+
* 除いた結果、値と呼び名を両方書いた箱では誰も伝えず、13 図種で黙って消えていた。
|
|
156
|
+
* 値を書いていない箱で鳴らさない判定は呼び元が持つ = 箱 1 つごとに決まる条件なので、
|
|
157
|
+
* 図種ごとの除外では表せない (#2384 の色番号と同じ形)。
|
|
158
|
+
*
|
|
159
|
+
* 位置 (`posX` / `posY`) は除かない = この図種では両方書いても効かないため、
|
|
160
|
+
* 片方だけの知らせ (#2362) ではなくこちらが伝える。
|
|
161
|
+
*
|
|
162
|
+
* 記法の `offsetX` / `offsetY` は `layoutPos` に入る (`position-offset-ignored` が受け持つ)。
|
|
163
|
+
* 欄の名前で判定するので、記法の項目名ではなく入った先の欄を書く。
|
|
164
|
+
*
|
|
165
|
+
* 体験と工程の欄 (`体験と工程の欄`) は、読む図種と別の知らせが受け持つ図種に分かれる。
|
|
166
|
+
* どちらも伝えないので 1 つにまとめて除く。
|
|
167
|
+
*/
|
|
168
|
+
const 値として読む図に共通の除外: ReadonlySet<string> = new Set([
|
|
169
|
+
"name",
|
|
170
|
+
"title",
|
|
171
|
+
"value",
|
|
172
|
+
"pos",
|
|
173
|
+
"kindWritten",
|
|
174
|
+
"lane",
|
|
175
|
+
"layoutPos",
|
|
176
|
+
...体験と工程の欄,
|
|
177
|
+
]);
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* 箱を名前と値の組として読む図種と、共通に加えて読む欄 (#2368 / #2370)。
|
|
181
|
+
*
|
|
182
|
+
* **表で持つ**。 図種ごとに除外の集合を丸ごと書くと、共通部分が図種の数だけ写され、
|
|
183
|
+
* 1 つ直した日に他が古いまま残る (実測 = 値の図とじょうごで 2 つに分かれていた)。
|
|
184
|
+
*
|
|
185
|
+
* | 図種 | 共通に加えて読む欄 |
|
|
186
|
+
* |---|---|
|
|
187
|
+
* | 数を描く図 (`chart`) | 色味 (`tone`) / 前の値 (`previous`) |
|
|
188
|
+
* | じょうご | 色味 (`tone`) |
|
|
189
|
+
* | 体験の地図 / 四象限 | 無し (名前と値だけを読む) |
|
|
190
|
+
* | ガントチャート | 色味 (`tone`)。 担当と終わる時期は共通の除外に入っている |
|
|
191
|
+
*/
|
|
192
|
+
export const 値として読む図種: ReadonlyMap<string, readonly string[]> = new Map([
|
|
193
|
+
["chart", ["tone", "previous"]],
|
|
194
|
+
["funnel", ["tone"]],
|
|
195
|
+
["gantt", ["tone"]],
|
|
196
|
+
["journey", []],
|
|
197
|
+
["quadrant", ["at", "atPos"]],
|
|
198
|
+
]);
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* 件ごとの主役 (`emphasis`) を実際に描く値の図。
|
|
202
|
+
*
|
|
203
|
+
* `chart` という node の種類だけでは円・折れ線なども混ざるため、`chart/stacked` まで
|
|
204
|
+
* 形を含めて区別する。じょうごは形を持たず、段の `emphasis` を読む。
|
|
205
|
+
*/
|
|
206
|
+
const 主役を読む値の図: ReadonlySet<string> = new Set(["chart/stacked", "funnel"]);
|
|
207
|
+
|
|
208
|
+
/** 件ごとの `emphasis` がその図で描かれるか */
|
|
209
|
+
export function 値として読む図が主役を読む(図種: string, 形?: DslDiagramShape): boolean {
|
|
210
|
+
return 主役を読む値の図.has(形 === undefined ? 図種 : `${図種}/${形}`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* その図種で、効かないと伝えない箱の欄を返す (#2370)。
|
|
215
|
+
*
|
|
216
|
+
* @param 図種 `値として読む図種` に載っている図種
|
|
217
|
+
* @returns 共通の除外に、その図種が読む欄を足した集合
|
|
218
|
+
*/
|
|
219
|
+
export function 値として読む図が伝えない箱の欄(図種: string): ReadonlySet<string> {
|
|
220
|
+
const 読む欄 = 値として読む図種.get(図種) ?? [];
|
|
221
|
+
if (読む欄.length === 0) return 値として読む図に共通の除外;
|
|
222
|
+
return new Set([...値として読む図に共通の除外, ...読む欄]);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* 箱を並べて線で繋ぐ図 (骨組みの図) で、どの図種でも効かないと伝えない箱の欄 (#2376)。
|
|
227
|
+
*
|
|
228
|
+
* | 区分 | 欄 |
|
|
229
|
+
* |---|---|
|
|
230
|
+
* | 骨組みの図が描く | 名前 / 題 / 呼び名 / 小見出し / 値 / 行 / 色味 / 図形 / 種類 / 段 / 透け具合 / 出す条件 |
|
|
231
|
+
* | 位置と大きさ | `posX` / `posY` / `posW` / `posH` / `posRel` / `layoutPos` |
|
|
232
|
+
* | 値への追随 | `wBind` / `hBind` / `renderOffsetX` / `renderOffsetY` |
|
|
233
|
+
* | 別の知らせが受け持つ | 縦列 / 体験と工程の 4 欄 |
|
|
234
|
+
*
|
|
235
|
+
* 色番号 (`colorHex`) は除かない (#2384)。 部品を置いた箱でだけ効く欄なので
|
|
236
|
+
* 「部品の側が伝えるから重ねない」 として除いていたが、**呼び元が部品を置いた箱を
|
|
237
|
+
* 判定の入口で飛ばす** (`a.partId !== undefined`) ため重なりようがなかった。
|
|
238
|
+
* 除いた結果、部品でない箱では誰も伝えず、7 図種で黙って消えていた。
|
|
239
|
+
*
|
|
240
|
+
* 記法の `色:` は 16 進数なら `colorHex` に、色の名前なら `tone` に入る。
|
|
241
|
+
* 名前で書いた形は箱の色として効くので、伝えるのは 16 進数で書いた形だけになる。
|
|
242
|
+
*
|
|
243
|
+
* 印 (`marks`) は除かない (#2377)。 行頭の記号に読み替えるのは ER 図と状態遷移図だけ
|
|
244
|
+
* (`行頭の印にする`) で、骨組みの残りの図種では行と組にしても効かない。
|
|
245
|
+
* 効く図種で鳴らさない判定は呼び元が持つ = 図種の一覧をここに写すと、印を読む図種を
|
|
246
|
+
* 足した日に古くなる。
|
|
247
|
+
*
|
|
248
|
+
* 残るのは始まりの印 / 終わりの印 / 印 / 前の値の 4 つ。
|
|
249
|
+
* 実測 = フロー 447 枚 ・ 泳路の図 35 枚 ・ 配置の図 8 枚の見本すべてで、この 4 件が
|
|
250
|
+
* 図も変えず知らせも出さなかった。
|
|
251
|
+
*
|
|
252
|
+
* **図種ごとの違いは `骨組みの図種` が持つ** (#2382)。 状態遷移図は始まりの印と終わりの印を
|
|
253
|
+
* 読むので、その 2 欄だけ除外に足す。
|
|
254
|
+
*/
|
|
255
|
+
const 骨組みの図に共通の除外: ReadonlySet<string> = new Set([
|
|
256
|
+
"name",
|
|
257
|
+
"title",
|
|
258
|
+
"subtitle",
|
|
259
|
+
"pos",
|
|
260
|
+
"kindWritten",
|
|
261
|
+
"value",
|
|
262
|
+
"eyebrow",
|
|
263
|
+
"tone",
|
|
264
|
+
"rows",
|
|
265
|
+
// cdl 0.129.0-0.136.0 の表箱・部品箱の欄。 applyV05Extensions が node へ渡し、
|
|
266
|
+
// 描き手が箱の種類に応じて題・罫線・印・入れ子・線画・足として読む (#2854)。
|
|
267
|
+
"kindForm",
|
|
268
|
+
"icon",
|
|
269
|
+
"emptyRowsNote",
|
|
270
|
+
"titleAlign",
|
|
271
|
+
"rowRules",
|
|
272
|
+
"rowStripe",
|
|
273
|
+
"rowLayout",
|
|
274
|
+
"rowDividerForm",
|
|
275
|
+
"rowWell",
|
|
276
|
+
"rowMarkForm",
|
|
277
|
+
"nestIn",
|
|
278
|
+
"figureCard",
|
|
279
|
+
"shape",
|
|
280
|
+
"kind",
|
|
281
|
+
"stack",
|
|
282
|
+
"opacity",
|
|
283
|
+
"visibleIf",
|
|
284
|
+
"posX",
|
|
285
|
+
"posY",
|
|
286
|
+
"posW",
|
|
287
|
+
"posH",
|
|
288
|
+
"posRel",
|
|
289
|
+
"layoutPos",
|
|
290
|
+
"wBind",
|
|
291
|
+
"hBind",
|
|
292
|
+
"renderOffsetX",
|
|
293
|
+
"renderOffsetY",
|
|
294
|
+
"lane",
|
|
295
|
+
...体験と工程の欄,
|
|
296
|
+
]);
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* 図種ごとの、共通の除外との違い (#2382)。
|
|
300
|
+
*
|
|
301
|
+
* **両方向に持つ**。 共通の除外は「どの骨組みの図も読む欄」 を集めたものなので、
|
|
302
|
+
* 図種ごとの違いは 2 通り出る。 外す側だけだと、1 つの図種だけが読む欄を持つ図種を
|
|
303
|
+
* 族に入れられない (実測 = 状態遷移図が始まりの印を読むため、族の外に置かれていた)。
|
|
304
|
+
*/
|
|
305
|
+
export type 骨組みの図の違い = {
|
|
306
|
+
/** その図種だけが読まない欄。 共通の除外から外して、伝える側へ回す */
|
|
307
|
+
readonly 読まない?: readonly string[];
|
|
308
|
+
/** その図種だけが読む欄。 共通の除外に足して、伝えない側へ回す */
|
|
309
|
+
readonly 読む?: readonly string[];
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* 箱を並べて線で繋ぐ図種と、共通の除外との違い (#2376 / #2382)。
|
|
314
|
+
*
|
|
315
|
+
* | 図種 | 違い | 理由 |
|
|
316
|
+
* |---|---|---|
|
|
317
|
+
* | フロー / 泳路の図 / 配置の図 | 無し | `compile/generic.ts` が共通の経路で箱を作る |
|
|
318
|
+
* | 分かれ道の図 (`flowchart`) | 無し | 種類は専用の形または箱の見た目として読む |
|
|
319
|
+
* | 構成の図 (`c4`) | 段 (`stack`) を読まない | `compile/c4.ts` が `a.stack` を読まない |
|
|
320
|
+
* | 行を持つ図 (`record`) | 始まりの印と終わりの印を読む | `compile/generic.ts` の札がこの図種でだけ出る |
|
|
321
|
+
*/
|
|
322
|
+
export const 骨組みの図種: ReadonlyMap<string, 骨組みの図の違い> = new Map([
|
|
323
|
+
["flow", {}],
|
|
324
|
+
["flowchart", {}],
|
|
325
|
+
["swimlane", {}],
|
|
326
|
+
["topology", {}],
|
|
327
|
+
["c4", { 読まない: ["stack"] }],
|
|
328
|
+
["record", { 読む: ["initial", "final"] }],
|
|
329
|
+
]);
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* その図種で、効かないと伝えない箱の欄を返す (#2376 / #2382)。
|
|
333
|
+
*
|
|
334
|
+
* @param 図種 `骨組みの図種` に載っている図種
|
|
335
|
+
* @returns 共通の除外から読まない欄を抜き、読む欄を足した集合
|
|
336
|
+
*/
|
|
337
|
+
export function 骨組みの図が伝えない箱の欄(図種: string): ReadonlySet<string> {
|
|
338
|
+
const 違い = 骨組みの図種.get(図種) ?? {};
|
|
339
|
+
const 読まない欄 = 違い.読まない ?? [];
|
|
340
|
+
const 読む欄 = 違い.読む ?? [];
|
|
341
|
+
if (読まない欄.length === 0 && 読む欄.length === 0) return 骨組みの図に共通の除外;
|
|
342
|
+
const 残り = new Set(骨組みの図に共通の除外);
|
|
343
|
+
for (const 欄 of 読まない欄) 残り.delete(欄);
|
|
344
|
+
for (const 欄 of 読む欄) 残り.add(欄);
|
|
345
|
+
return 残り;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* 箱に書いた欄のうち、効かないものの呼び名を並べる (#2368)。
|
|
350
|
+
*
|
|
351
|
+
* **伝える欄を並べず、読む欄を除いた残り全部を返す**。 効かない欄を並べる形にすると、
|
|
352
|
+
* 箱に欄を足した日にその欄だけが一覧から漏れ、書いた人には「書いたのに何も起きない」
|
|
353
|
+
* としか見えない。 除外側を書けば、足し忘れは「余計に知らせる」 側へ倒れる。
|
|
354
|
+
*
|
|
355
|
+
* @param 箱 記法から読んだ箱 1 つ
|
|
356
|
+
* @param 伝えない欄 その図種が読む欄と、別の知らせが受け持つ欄
|
|
357
|
+
* @param 判定で外す欄 図種の中でさらに外す欄 (種類が図種で分かれる形、#2358)
|
|
358
|
+
* @returns 知らせに出す呼び名。 空なら伝えるものが無い
|
|
359
|
+
*/
|
|
360
|
+
export function 効かない箱の欄を並べる(
|
|
361
|
+
箱: object,
|
|
362
|
+
伝えない欄: ReadonlySet<string>,
|
|
363
|
+
判定で外す欄: readonly string[] = [],
|
|
364
|
+
): string[] {
|
|
365
|
+
const 残り = new Set(
|
|
366
|
+
Object.entries(箱 as Record<string, unknown>)
|
|
367
|
+
.filter(([欄, 値]) => 値 !== undefined && !伝えない欄.has(欄))
|
|
368
|
+
.map(([欄]) => 欄),
|
|
369
|
+
);
|
|
370
|
+
for (const 欄 of 判定で外す欄) 残り.delete(欄);
|
|
371
|
+
const 効かない: string[] = [];
|
|
372
|
+
for (const [欄, 呼び名] of 効かない箱の欄の呼び名) {
|
|
373
|
+
if (!残り.delete(欄)) continue;
|
|
374
|
+
if (!効かない.includes(呼び名)) 効かない.push(呼び名);
|
|
375
|
+
}
|
|
376
|
+
// 呼び名を持たない欄は名前をそのまま出す = 読みにくい名前でも、黙って落とすよりは伝わる
|
|
377
|
+
効かない.push(...[...残り].sort());
|
|
378
|
+
return 効かない;
|
|
379
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { DslActor, DslDocument } from "../types";
|
|
2
|
+
import { slugify } from "./slug";
|
|
3
|
+
/**
|
|
4
|
+
* 登場人物の参照表と、並びの整え (#2030 で `compile.ts` から移した)。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 矢印の指す先を `actors` に書いた **正規の名前** へ解決する表 (#1209)。
|
|
9
|
+
*
|
|
10
|
+
* 名前そのものに加えて **一意な slug も受ける**。 動きを書いた図の組み立ては `slugify` に
|
|
11
|
+
* 落として引くため、 `API Gateway` を `api-gateway` と書いた形が届く。 2 つ以上の名前が
|
|
12
|
+
* 同じ slug になる時は受けない = どちらを指したか決められない。
|
|
13
|
+
*
|
|
14
|
+
* **返すのは正規の名前で、 slug ではない**。 slug を返すと、 動きを書いていない図の組み立て
|
|
15
|
+
* (名前の完全一致で引く) と食い違う = 知らせは出ないのに label が消える / 題が slug に化ける /
|
|
16
|
+
* 依存が切れる、 という形になる (Round 1 で実測)。 中央で名前へ揃えれば全経路が同じ相手を指す。
|
|
17
|
+
*/
|
|
18
|
+
export function actorRefTable(doc: DslDocument): Map<string, string> {
|
|
19
|
+
const 表 = new Map<string, string>();
|
|
20
|
+
const slug数 = new Map<string, number>();
|
|
21
|
+
for (const a of doc.actors) {
|
|
22
|
+
const sl = slugify(a.name);
|
|
23
|
+
slug数.set(sl, (slug数.get(sl) ?? 0) + 1);
|
|
24
|
+
}
|
|
25
|
+
for (const a of doc.actors) {
|
|
26
|
+
表.set(a.name, a.name);
|
|
27
|
+
const sl = slugify(a.name);
|
|
28
|
+
if ((slug数.get(sl) ?? 0) === 1) 表.set(sl, a.name);
|
|
29
|
+
}
|
|
30
|
+
return 表;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 矢印の指す先を正規の名前へ揃えた `flow` を返す (#1209)。
|
|
35
|
+
*
|
|
36
|
+
* 解決できない矢印はそのまま残す = 図種ごとに扱いが違う (木は独自の知らせを出し、 値で描く図は
|
|
37
|
+
* 落とす)。 中央で消すとその扱いが効かなくなる。 残した分は `reportMissingFlowActors` が
|
|
38
|
+
* 知らせ、 動きを書いた図の組み立てが落とす。
|
|
39
|
+
*/
|
|
40
|
+
export function canonicalizeFlowActors(doc: DslDocument): DslDocument {
|
|
41
|
+
const 表 = actorRefTable(doc);
|
|
42
|
+
let 変えた = false;
|
|
43
|
+
const flow = doc.flow.map((s) => {
|
|
44
|
+
const from = 表.get(s.from) ?? s.from;
|
|
45
|
+
const to = 表.get(s.to) ?? s.to;
|
|
46
|
+
if (from === s.from && to === s.to) return s;
|
|
47
|
+
変えた = true;
|
|
48
|
+
return { ...s, from, to };
|
|
49
|
+
});
|
|
50
|
+
return 変えた ? { ...doc, flow } : doc;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 状態遷移図で、どの箱を始まり / 終わりとみなすか (#1275)。
|
|
55
|
+
*
|
|
56
|
+
* **書いた値が勝つ**。 `initial:` / `final:` は記法で書けるのに 1 度も読まれておらず、
|
|
57
|
+
* 位置だけで決まっていた (実測 = 中央の箱に `final: true` を書いても、最後に書いた箱が
|
|
58
|
+
* 「最終」 になった)。
|
|
59
|
+
*
|
|
60
|
+
* 1 つも書いていなければ従来どおり位置で決める = 書かない記法の図は変わらない。
|
|
61
|
+
* 片方だけ書いた形も、書いた側だけが切り替わる。
|
|
62
|
+
*
|
|
63
|
+
* ## 打ち消し (`initial: false`) は書いた箱にだけ効く (#2418)
|
|
64
|
+
*
|
|
65
|
+
* 書いたかどうかを `true` だけで数えていた間、`false` しか書かれていない図は
|
|
66
|
+
* 「1 つも書いていない」 扱いになって位置で決める既定へ落ちていた。 その既定は
|
|
67
|
+
* 「1 番目の箱が始まり」 なので、**打ち消しを書いた当の箱に札が出ていた** (知らせも 0 件)。
|
|
68
|
+
*
|
|
69
|
+
* 位置で決める既定から、打ち消しを書いた箱だけを外す。
|
|
70
|
+
*
|
|
71
|
+
* **「どれか 1 つでも書いたら既定を捨てる」 形にはしない** = 途中の箱に書いた
|
|
72
|
+
* `initial: false` が 1 番目の箱の札まで消す。 書き手は途中の箱のことしか言っていない。
|
|
73
|
+
*/
|
|
74
|
+
export function 始まりと終わりの決め方(
|
|
75
|
+
doc: DslDocument,
|
|
76
|
+
/**
|
|
77
|
+
* 並びで決める既定を使うか (#2349)。
|
|
78
|
+
*
|
|
79
|
+
* **格子に置く形では並びが始まりと終わりを意味しない**。 縦列と段を書いた図は
|
|
80
|
+
* 書いた順に左から並ぶわけではないので、「最初に書いた箱」 を初期とみなす既定は
|
|
81
|
+
* 図の見た目と合わない (実測 = 印の箱を持つ見本で、既に形が始点を示している箱へ
|
|
82
|
+
* 「初期」 の字が重ねて出た)。 書いた値はどの並べ方でも効く。
|
|
83
|
+
*/
|
|
84
|
+
opts: { 並びで決める?: boolean } = {},
|
|
85
|
+
): {
|
|
86
|
+
始まり: (a: DslActor, idx: number) => boolean;
|
|
87
|
+
終わり: (a: DslActor, idx: number) => boolean;
|
|
88
|
+
} {
|
|
89
|
+
const 並びで決める = opts.並びで決める ?? true;
|
|
90
|
+
const 書いた始まり = doc.actors.some((a) => a.initial === true);
|
|
91
|
+
const 書いた終わり = doc.actors.some((a) => a.final === true);
|
|
92
|
+
return {
|
|
93
|
+
始まり: (a, idx) =>
|
|
94
|
+
書いた始まり ? a.initial === true : 並びで決める && idx === 0 && a.initial !== false,
|
|
95
|
+
終わり: (a, idx) =>
|
|
96
|
+
書いた終わり
|
|
97
|
+
? a.final === true
|
|
98
|
+
: 並びで決める &&
|
|
99
|
+
idx === doc.actors.length - 1 &&
|
|
100
|
+
doc.actors.length > 1 &&
|
|
101
|
+
a.final !== false,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { diagram } from "@cardenelabs/cdl";
|
|
2
|
+
import type { CdlDiagram } from "@cardenelabs/cdl";
|
|
3
|
+
import type { DslDocument } from "../types";
|
|
4
|
+
import { 描ける種別 } from "./kinds";
|
|
5
|
+
import { 段を読み取る } from "./rows";
|
|
6
|
+
import { slugify } from "./slug";
|
|
7
|
+
/**
|
|
8
|
+
* C4 preset (C4 model 階層 system context 専用 layout)
|
|
9
|
+
*
|
|
10
|
+
* 設計 ... actor.subtitle の先頭に "L1" / "L2" / "L3" を置き、 階層 lane を生成。
|
|
11
|
+
* - L1 = 全体の見取り図 (C4 model の System Context)
|
|
12
|
+
* - L2 = 動かす単位 (同 Container = 単体で動かす application や保管庫)
|
|
13
|
+
* - L3 = 部品 (同 Component)
|
|
14
|
+
*
|
|
15
|
+
* 実装 ... **中身のある段だけ** lane を作り、 使う段を左から順に詰めて横並び (contain: true で
|
|
16
|
+
* 囲む) 配置する。 3 lane を常に作ると中身のない枠が画面に残り、 描かれ損ねたように見える (#1078)。
|
|
17
|
+
* 同 lane 内の actor は内部 stack で縦並びになる (横並びは layout 制約上不可、
|
|
18
|
+
* 段の区別が視覚的に最重要)。 subtitle marker 未指定なら L1 fallback。
|
|
19
|
+
*
|
|
20
|
+
* flow ... actor 間の関係を edge で表現。
|
|
21
|
+
*/
|
|
22
|
+
export function compileC4(doc: DslDocument): CdlDiagram {
|
|
23
|
+
const b = diagram(slugify(doc.title), { topic: doc.title, type: "infrastructure" });
|
|
24
|
+
const LANE_W = 400;
|
|
25
|
+
const LANE_GAP = 80;
|
|
26
|
+
// 段の名前は **枠に描く字** で、記法に打ち込む識別子ではない (#1886)。 打ち込む字は目印
|
|
27
|
+
// (`L1` / `L2` / `L3`) の側で、そちらは綴りを変えない
|
|
28
|
+
const 段の名前: Record<number, string> = { 1: "全体の見取り図", 2: "動かす単位", 3: "部品" };
|
|
29
|
+
|
|
30
|
+
// 段は **先頭一致** で読み、 読んだ目印は説明から落とす (`段を読み取る` の説明を参照)
|
|
31
|
+
const 割当 = doc.actors.map((a, idx) => {
|
|
32
|
+
const { 段, 説明 } = 段を読み取る(a.subtitle);
|
|
33
|
+
return { 段, 説明, id: slugify(a.name) || `n${idx}`, actor: a };
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// **中身のある段だけ枠を作る**。 3 段を必ず作ると、 書いていない段が空の点線枠として残り、
|
|
37
|
+
// 見た人には「何かが描かれ損ねた」 ようにしか見えない
|
|
38
|
+
const 使う段 = [1, 2, 3].filter((lv) => 割当.some((x) => x.段 === lv));
|
|
39
|
+
使う段.forEach((lv, i) => {
|
|
40
|
+
b.lane(`c4-l${lv}`, {
|
|
41
|
+
// 空の段を飛ばした分だけ左に詰める。 飛ばした位置に隙間を残すと、 やはり
|
|
42
|
+
// 「何かが抜けている」 ように見える
|
|
43
|
+
x: i * (LANE_W + LANE_GAP),
|
|
44
|
+
width: LANE_W,
|
|
45
|
+
label: 段の名前[lv]!,
|
|
46
|
+
contain: true,
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const stackPerLane: Record<string, number> = { "c4-l1": 0, "c4-l2": 0, "c4-l3": 0 };
|
|
51
|
+
for (const x of 割当) {
|
|
52
|
+
const lid = `c4-l${x.段}`;
|
|
53
|
+
const stack = stackPerLane[lid]!;
|
|
54
|
+
stackPerLane[lid] = stack + 1;
|
|
55
|
+
b.node(x.id, {
|
|
56
|
+
lane: lid,
|
|
57
|
+
stack,
|
|
58
|
+
kind: 描ける種別(x.actor.kind),
|
|
59
|
+
title: x.actor.name,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const s of doc.flow) {
|
|
64
|
+
const fromId = slugify(s.from);
|
|
65
|
+
const toId = slugify(s.to);
|
|
66
|
+
b.edge(fromId, toId, {
|
|
67
|
+
label: s.label,
|
|
68
|
+
...(s.sub ? { sub: s.sub } : {}),
|
|
69
|
+
...(s.side ? { side: s.side } : {}),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return b.build();
|
|
74
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { CdlDiagram } from "@cardenelabs/cdl";
|
|
2
|
+
import type { DslActor, DslDocument } from "../types";
|
|
3
|
+
import { 書いた縦列に置く } from "./lanes";
|
|
4
|
+
import { lookupPartRaw } from "./parts-lookup";
|
|
5
|
+
/**
|
|
6
|
+
* 箱を鎖のように 1 本につなぐ形か (#2030 で `compile.ts` から移した)。
|
|
7
|
+
*
|
|
8
|
+
* 図種ごとの file と、`compile.ts` に残る部品の取り込みの両方が呼ぶ。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 静止した `type: flow` かどうか。 この形だけ矢印を鎖状に作る (`compileFlow`)。
|
|
13
|
+
*
|
|
14
|
+
* 段を持つ形・縦列を書いた形・向きを書いた形は generic 経路へ回るため鎖にならない。
|
|
15
|
+
*
|
|
16
|
+
* **`compileFlow` の分岐もこの判定を使う**。 判定を 2 か所に書くと、組み立てだけが別経路へ回り、
|
|
17
|
+
* 行の対応と端の知らせが鎖のまま残る。 向きを書いた形 (#1494) は組み立てにだけ足され、`A -> C` に
|
|
18
|
+
* 書いた `head` が `C -> B` に載り、書いた端のとおりの矢印に「端は使われません」 が出ていた (#1986)。
|
|
19
|
+
*/
|
|
20
|
+
export function 鎖でつなぐ形か(doc: DslDocument): boolean {
|
|
21
|
+
if (doc.type !== "flow") return false;
|
|
22
|
+
return !鎖にしない書き方.some((x) => x.あてはまる(doc));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 鎖をやめて、書いた端どおりに繋ぐ書き方 (#2374)。
|
|
27
|
+
*
|
|
28
|
+
* **判定と案内を組にして持つ**。 以前は判定が 3 つの条件を見るのに、知らせの案内は
|
|
29
|
+
* 手で並べた 2 つだった (実測 = 動きを書く経路が案内に無く、読み手はその道を知れなかった)。
|
|
30
|
+
*
|
|
31
|
+
* 表から判定を導き、案内も同じ表から作る。 条件を足した日に案内が自動で追随する。
|
|
32
|
+
*
|
|
33
|
+
* どれも図の見た目を変えるので、**優先順位は付けない**。 向きを書けば並びが変わり、
|
|
34
|
+
* 縦列を書けば列ができ、動きを書けば段が付く。 どれを選ぶかは書き手が決める。
|
|
35
|
+
*/
|
|
36
|
+
export const 鎖にしない書き方: readonly {
|
|
37
|
+
/** 検査と読み手のための短い呼び名 */
|
|
38
|
+
名: string;
|
|
39
|
+
/** その書き方をしているか */
|
|
40
|
+
あてはまる: (doc: DslDocument) => boolean;
|
|
41
|
+
/** 知らせの案内に出す文 */
|
|
42
|
+
案内: string;
|
|
43
|
+
}[] = [
|
|
44
|
+
{
|
|
45
|
+
名: "動き",
|
|
46
|
+
あてはまる: (doc) => doc.animate !== undefined && doc.animate.phases.length > 0,
|
|
47
|
+
案内: "動き (animation:) を書く",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
名: "向き",
|
|
51
|
+
あてはまる: (doc) => doc.direction !== undefined,
|
|
52
|
+
案内: "向き (direction: 縦) を書く",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
名: "縦列",
|
|
56
|
+
あてはまる: (doc) => 書いた縦列に置く("flow", doc),
|
|
57
|
+
案内: "箱に縦列 (lane:) を書く",
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
/** 知らせの案内に出す、鎖をやめる書き方の並び (#2374) */
|
|
62
|
+
export function 鎖にしない書き方の案内(): string {
|
|
63
|
+
return 鎖にしない書き方.map((x) => x.案内).join(" / ");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 静止したフローの鎖に並べる登場人物 (#1987)。
|
|
68
|
+
*
|
|
69
|
+
* **どの行にも書かれていない部品は鎖に並べない**。 並べると、部品の前後の並び順の矢印は書き手が
|
|
70
|
+
* 部品へ引いたものではないため部品へ繋がずに外され (#1979)、部品を途中に置いた図から矢印が全て
|
|
71
|
+
* 消えていた (実測 = `受付 / 印 / 出荷` で矢印 0 本、知らせ 0 件)。 編集画面は部品を本文から抜いてから
|
|
72
|
+
* 鎖を作るので `受付 -> 出荷` を描き、カタログと絵が食い違っていた。 部品は図の下の格子に置くため、
|
|
73
|
+
* 鎖から外しても流れの列に空きは生まれない。
|
|
74
|
+
*
|
|
75
|
+
* - 部品の一覧に無い部品は仮の箱のまま描かれるので残す (一覧を渡さない組み立ても同じ)
|
|
76
|
+
* - 大きすぎて取り込まない部品は一覧にあるので外す (図に入らない場所へ鎖を通さない)
|
|
77
|
+
* - 行の端に書かれた部品は残し、#1979 の繋ぎ方に従う
|
|
78
|
+
*
|
|
79
|
+
* 鎖を作る `compileFlow`、行との対応 (`鎖のどの行から来たか`)、端の知らせ
|
|
80
|
+
* (`reportFlowEndpointNotHonored`) の 3 つがこの並びを使う。 1 つでも `doc.actors` を見ると、
|
|
81
|
+
* N 本目の矢印と行の対応がずれる。
|
|
82
|
+
*/
|
|
83
|
+
export function 鎖に並べる登場人物(
|
|
84
|
+
doc: DslDocument,
|
|
85
|
+
partsCatalog: Record<string, CdlDiagram> | undefined,
|
|
86
|
+
): DslActor[] {
|
|
87
|
+
if (!partsCatalog) return doc.actors;
|
|
88
|
+
const 行に書かれた = new Set(doc.flow.flatMap((s) => [s.from, s.to]));
|
|
89
|
+
// 部品でない箱は `partId` を持たず一覧を引けないので、一覧に無い部品と同じく残る
|
|
90
|
+
return doc.actors.filter(
|
|
91
|
+
(a) => 行に書かれた.has(a.name) || lookupPartRaw(partsCatalog, a.partId) === undefined,
|
|
92
|
+
);
|
|
93
|
+
}
|