@cardenelabs/dragon 0.27.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.
- package/README.md +119 -12
- package/dist/index.cjs +9619 -8023
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +407 -175
- package/dist/index.d.ts +407 -175
- package/dist/index.js +9617 -8024
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/compile/actor-ids.ts +306 -0
- package/src/compile/actors.ts +74 -0
- package/src/compile/c4.ts +77 -0
- package/src/compile/chain.ts +54 -0
- package/src/compile/chart-fields.ts +178 -0
- package/src/compile/class.ts +100 -0
- package/src/compile/deep-rewrite.ts +30 -0
- package/src/compile/derived-source.ts +18 -0
- package/src/compile/direction.ts +63 -0
- package/src/compile/er-relation.ts +200 -0
- package/src/compile/er.ts +44 -0
- package/src/compile/flow.ts +54 -0
- package/src/compile/funnel.ts +75 -0
- package/src/compile/gantt.ts +232 -0
- package/src/compile/generic.ts +284 -0
- package/src/compile/hierarchy.ts +92 -0
- package/src/compile/journey.ts +84 -0
- package/src/compile/kinds.ts +156 -0
- package/src/compile/lanes.ts +56 -0
- package/src/compile/mind.ts +343 -0
- package/src/compile/node-title.ts +24 -0
- package/src/compile/notice.ts +94 -0
- package/src/compile/parts-lookup.ts +17 -0
- package/src/compile/parts.ts +2635 -0
- package/src/compile/placement-apply.ts +673 -0
- package/src/compile/placement.ts +15 -0
- package/src/compile/quadrant.ts +110 -0
- package/src/compile/rows.ts +40 -0
- package/src/compile/sequence.ts +226 -0
- package/src/compile/slug.ts +29 -0
- package/src/compile/solidity.ts +39 -0
- package/src/compile/state.ts +71 -0
- package/src/compile/subtitle.ts +22 -0
- package/src/compile/swimlane.ts +83 -0
- package/src/compile/topology.ts +50 -0
- package/src/compile/tree.ts +60 -0
- package/src/compile/value-chart.ts +183 -0
- package/src/compile/values.ts +432 -0
- package/src/compile/word-state.ts +82 -0
- package/src/compile.ts +596 -5672
- package/src/index.ts +13 -10
- package/src/json-line-map.ts +30 -0
- package/src/json-parser.ts +509 -176
- package/src/near-name.ts +42 -0
- package/src/notation-lint.ts +112 -74
- package/src/relative-pos.ts +71 -2
- package/src/schema.ts +1 -1
- package/src/schemas/diagram.json +130 -34
- package/src/types.ts +60 -41
- package/src/v05/input-table.generated.ts +6 -4
- package/src/v05/parser-types.ts +12 -1
- package/src/v05/parser.ts +503 -149
package/src/near-name.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 書き間違えた名前に綴りの近い候補を探す (#1295 で JSON の項目名に作り、#2113 で部品の名前と共有した)。
|
|
3
|
+
*
|
|
4
|
+
* 「知らない名前です」 だけだと、書いた人は正しい綴りを探しに行く必要がある。
|
|
5
|
+
* 2 文字の違い (足りない / 多い / 別の字) までを候補とする。 隣どうしの入れ替わりは 2 文字の違いに数える。
|
|
6
|
+
*
|
|
7
|
+
* 遠い名前は勧めない。 無関係な名前を勧めると、書いた人がそちらへ直して二度手間になる。
|
|
8
|
+
* 大文字と小文字は区別しない。
|
|
9
|
+
*/
|
|
10
|
+
export function 近い名前(語: string, 候補: Iterable<string>): string | undefined {
|
|
11
|
+
const 小文字 = 語.toLowerCase();
|
|
12
|
+
let 最短: { 名: string; 距離: number } | undefined;
|
|
13
|
+
for (const c of 候補) {
|
|
14
|
+
const d = 編集距離(小文字, c.toLowerCase(), 2);
|
|
15
|
+
if (d <= 2 && (最短 === undefined || d < 最短.距離)) 最短 = { 名: c, 距離: d };
|
|
16
|
+
}
|
|
17
|
+
return 最短?.名;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 2 つの語の編集距離 (上限付き)。
|
|
22
|
+
*
|
|
23
|
+
* 上限を持つのは、長い語どうしで表を全部埋めないため。 上限を超えた時点で打ち切る。
|
|
24
|
+
*/
|
|
25
|
+
function 編集距離(a: string, b: string, 上限: number): number {
|
|
26
|
+
if (Math.abs(a.length - b.length) > 上限) return 上限 + 1;
|
|
27
|
+
let 前 = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
28
|
+
for (let i = 1; i <= a.length; i += 1) {
|
|
29
|
+
const 今: number[] = [i];
|
|
30
|
+
let 行の最小 = i;
|
|
31
|
+
for (let j = 1; j <= b.length; j += 1) {
|
|
32
|
+
const 費用 = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
33
|
+
const v = Math.min(今[j - 1]! + 1, 前[j]! + 1, 前[j - 1]! + 費用);
|
|
34
|
+
今.push(v);
|
|
35
|
+
if (v < 行の最小) 行の最小 = v;
|
|
36
|
+
}
|
|
37
|
+
// その行の最小が上限を超えたら、以降どう進んでも上限以下にはならない
|
|
38
|
+
if (行の最小 > 上限) return 上限 + 1;
|
|
39
|
+
前 = 今;
|
|
40
|
+
}
|
|
41
|
+
return 前[b.length]!;
|
|
42
|
+
}
|
package/src/notation-lint.ts
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 記法の検査 (図を書く人向けの修正システム)。
|
|
3
3
|
*
|
|
4
|
-
* cdl / dragon
|
|
5
|
-
*
|
|
4
|
+
* cdl / dragon の記法で書いた図 (`CdlDiagram`) を決まった規則で検査し、直し方を修正案
|
|
5
|
+
* (`suggestion`) として返す純粋関数。 言語モデルは使わない。
|
|
6
6
|
*
|
|
7
|
-
* 検知システム (`check:cdl` / `check:dragon` / `check:kind`)
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* 検知システム (`check:cdl` / `check:dragon` / `check:kind`) は開発者向けで実装の不具合を
|
|
8
|
+
* 見つける。 こちらは図を書く人向けで、読む人へ伝わらない書き方 (図の説明に入り込んだ実装の
|
|
9
|
+
* 書き方、無い部品を指す参照、値や項目が空の図表など) を見つける。
|
|
10
10
|
*
|
|
11
11
|
* 使い方:
|
|
12
|
-
* import { lintDiagram } from "@cardenelabs/dragon";
|
|
12
|
+
* import { lintDiagram, autoFix } from "@cardenelabs/dragon";
|
|
13
13
|
* const report = lintDiagram(diagram);
|
|
14
|
-
* // report.issues
|
|
15
|
-
* // report.
|
|
16
|
-
*
|
|
14
|
+
* // report.issues = LintIssue[]
|
|
15
|
+
* // report.autoFixableCount = 自動修正で実際に解消できる指摘の数
|
|
16
|
+
* const patched = autoFix(diagram); // 自動修正を当てた新しい図 (元の図は変えない)
|
|
17
17
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* 道具:
|
|
19
|
+
* node packages/dragon/scripts/dragon-lint.mjs apps/playground-spa/src/topics/catalog/presets.cdl.ts
|
|
20
20
|
*/
|
|
21
21
|
import type { CdlDiagram, CdlNode } from "@cardenelabs/cdl";
|
|
22
22
|
|
|
@@ -28,9 +28,12 @@ export type LintIssue = {
|
|
|
28
28
|
severity: LintSeverity;
|
|
29
29
|
/** 該当対象 (node id / edge id / diagram id) */
|
|
30
30
|
target: string;
|
|
31
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* 書き手向けの指摘の文。 書き手が打ち込む識別子と書き手の値は `` ` `` で囲み、 残りは日本語で書く
|
|
33
|
+
* (画面側の `lint-message-words.test.ts` が全規則の文を判定に通す)
|
|
34
|
+
*/
|
|
32
35
|
message: string;
|
|
33
|
-
/**
|
|
36
|
+
/** 修正案。 自動修正できる指摘は自動修正が書く値そのもの、 できない指摘は直し方の文 */
|
|
34
37
|
suggestion?: string;
|
|
35
38
|
/** autoFix() が本 issue を自動解消できるか */
|
|
36
39
|
autoFixable: boolean;
|
|
@@ -43,11 +46,21 @@ export type LintReport = {
|
|
|
43
46
|
autoFixableCount: number;
|
|
44
47
|
};
|
|
45
48
|
|
|
49
|
+
/**
|
|
50
|
+
* 図の説明に入り込んだ実装の書き方と、 読む人に何が伝わらないか。
|
|
51
|
+
*
|
|
52
|
+
* 文は理由だけを持ち、 直し方の例を書かない。 直し方は修正案 (`suggestion`) が自動修正の
|
|
53
|
+
* 出力から作る。 例を字で書くと、 自動修正の出力を変えた日に例だけが古い文型で残る
|
|
54
|
+
* (実測 = #1934 で出力を変えた後も `「〜 を示す図」 のように` が残っていた、 #1940)。
|
|
55
|
+
*/
|
|
46
56
|
const REDUNDANT_TOPIC_PATTERNS: Array<{ pattern: RegExp; hint: string }> = [
|
|
47
|
-
{ pattern: /\bpreset\s*\(/i, hint: "
|
|
48
|
-
{ pattern: /render\s*未実装/, hint: "
|
|
49
|
-
{
|
|
50
|
-
|
|
57
|
+
{ pattern: /\bpreset\s*\(/i, hint: "`preset (…)` は作り手の書き方で、 読む人には何を示す図かが伝わらない" },
|
|
58
|
+
{ pattern: /render\s*未実装/, hint: "`render 未実装` は作り手向けの控えで、 カタログに出す説明には要らない" },
|
|
59
|
+
{
|
|
60
|
+
pattern: /SVG\s+(polyline|arc|rect|path)/i,
|
|
61
|
+
hint: "`SVG` の描き方の名前 (`polyline` / `arc` / `rect` / `path`) は実装の詳細で、 何を示す図かが伝わらない",
|
|
62
|
+
},
|
|
63
|
+
{ pattern: /\bpolygon\b/i, hint: "`polygon` は実装の言葉で、 何を示す図かが伝わらない" },
|
|
51
64
|
];
|
|
52
65
|
|
|
53
66
|
/**
|
|
@@ -85,32 +98,46 @@ export function autoFix(d: CdlDiagram): CdlDiagram {
|
|
|
85
98
|
return patched;
|
|
86
99
|
}
|
|
87
100
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
101
|
+
/**
|
|
102
|
+
* 図の型ごとに、 自動修正が書く説明「{shows}を示す{name}」 の 2 つの部品。
|
|
103
|
+
*
|
|
104
|
+
* `name` はカタログが同じ型の見本に付けた名前 (`apps/playground-spa/src/lib/i18n.ts` の
|
|
105
|
+
* `ITEM_NAME_JA` の `preset*`) と同じ字にする。 利用者は自動修正で書き換わった説明の名前で
|
|
106
|
+
* カタログを探すため、 呼び名が割れると同じ型の見本に辿り着けない。 照らす検査は画面側の
|
|
107
|
+
* `lint-topic-names.test.ts` が持つ (この package は画面側の file を読まない)。
|
|
108
|
+
*
|
|
109
|
+
* 説明の末尾を型の名前にするのは、 名前の多くが `図` で終わるため。 旧い文型
|
|
110
|
+
* 「{名前} を示す図」 のまま名前を差し替えると `工程表 を示す図` / `状態遷移図 を示す図` の
|
|
111
|
+
* ように図が重なる。 カタログの見本の説明 (`全体に対する内訳の割合を示す円グラフ`) と同じ並びにした。
|
|
112
|
+
*
|
|
113
|
+
* `chart` と `bar chart` はカタログに同じ型の見本が無いので、 図表の日常語で呼ぶ。
|
|
114
|
+
*/
|
|
115
|
+
const KIND_TO_JA: Record<string, { shows: string; name: string }> = {
|
|
116
|
+
chart: { shows: "項目ごとの数値", name: "グラフ" },
|
|
117
|
+
"line chart": { shows: "値の移り変わり", name: "折れ線グラフ" },
|
|
118
|
+
"pie chart": { shows: "全体に対する内訳の割合", name: "円グラフ" },
|
|
119
|
+
"bar chart": { shows: "項目ごとの値の大きさ", name: "棒グラフ" },
|
|
120
|
+
flow: { shows: "処理の順番", name: "フロー" },
|
|
121
|
+
swimlane: { shows: "役割ごとの縦列に分けた処理の流れ", name: "スイムレーン" },
|
|
122
|
+
sequence: { shows: "要素どうしのやり取りの順番", name: "シーケンス図" },
|
|
123
|
+
topology: { shows: "システムの構成要素と接続", name: "トポロジー図" },
|
|
124
|
+
er: { shows: "表どうしの関係", name: "ER図" },
|
|
125
|
+
stateMachine: { shows: "状態と遷移の条件", name: "状態遷移図" },
|
|
126
|
+
stateMachine2: { shows: "親の状態の中に置いた子の状態と遷移の条件", name: "入れ子の状態遷移図" },
|
|
127
|
+
infrastructure: { shows: "クラウドとネットワークの構成", name: "階層構成図" },
|
|
128
|
+
classDiagram: { shows: "クラスどうしの関係", name: "クラス図" },
|
|
129
|
+
tree: { shows: "組織や分類の親子の関係", name: "階層図" },
|
|
130
|
+
userJourney: { shows: "利用者の気持ちの移り変わり", name: "体験の道筋" },
|
|
131
|
+
mindMap: { shows: "中心の主題から広がる発想", name: "枝分かれ図" },
|
|
132
|
+
funnel: { shows: "段階ごとに残る数と離れる数", name: "絞り込み図" },
|
|
133
|
+
quadrant: { shows: "2 つの軸で分けた項目の位置", name: "四象限図" },
|
|
134
|
+
gantt: { shows: "作業の期間と前後の関係", name: "工程表" },
|
|
135
|
+
flowchart: { shows: "分岐や判定を含む処理の順番", name: "流れ図" },
|
|
136
|
+
network: { shows: "機器と区画のつながり", name: "ネットワーク図" },
|
|
110
137
|
};
|
|
111
138
|
|
|
112
139
|
function applyTopicAutoFix(topic: string): string {
|
|
113
|
-
// 1. 先頭の kind name を検出、
|
|
140
|
+
// 1. 先頭の kind name を検出、 マッチしたら「{shows}を示す{name}」 に置換
|
|
114
141
|
const kindMatch = topic.match(
|
|
115
142
|
/^\s*(chart|flow|swimlane|sequence|topology|er|stateMachine2?|infrastructure|classDiagram|tree|userJourney|mindMap|funnel|quadrant|gantt|flowchart|network|line chart|pie chart|bar chart)\b/i,
|
|
116
143
|
);
|
|
@@ -118,7 +145,8 @@ function applyTopicAutoFix(topic: string): string {
|
|
|
118
145
|
const kind = kindMatch[1]!.toLowerCase();
|
|
119
146
|
const canonical = Object.keys(KIND_TO_JA).find((k) => k.toLowerCase() === kind);
|
|
120
147
|
if (canonical) {
|
|
121
|
-
|
|
148
|
+
const { shows, name } = KIND_TO_JA[canonical]!;
|
|
149
|
+
return `${shows}を示す${name}`;
|
|
122
150
|
}
|
|
123
151
|
}
|
|
124
152
|
|
|
@@ -131,21 +159,31 @@ function applyTopicAutoFix(topic: string): string {
|
|
|
131
159
|
return out;
|
|
132
160
|
}
|
|
133
161
|
|
|
162
|
+
/**
|
|
163
|
+
* 図の説明に実装の書き方が入っていないかを見る。
|
|
164
|
+
*
|
|
165
|
+
* **自動修正で消えるかは、 直した後の説明にもう 1 度当てて決める** (#1940)。 自動修正が消すのは
|
|
166
|
+
* 先頭の型の名前と、 括弧の中の実装の言葉だけで、 括弧の外の `SVG polyline を使う` は字が変わらない。
|
|
167
|
+
* 当てずに「直せる」 と返すと、 道具が直せる数に入れ、 直したはずの指摘が次の検査でまた出る。
|
|
168
|
+
*
|
|
169
|
+
* 修正案は、 直せる時は自動修正が書く値そのもの (`LintIssue.suggestion` の約束)、 直せない時は
|
|
170
|
+
* 直し方の文にする。 文の例は書き換え先の表から作り、 字で書かない。
|
|
171
|
+
*/
|
|
134
172
|
function ruleTopicRedundancy(d: CdlDiagram): LintIssue[] {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
173
|
+
const 当たる = REDUNDANT_TOPIC_PATTERNS.filter(({ pattern }) => pattern.test(d.topic));
|
|
174
|
+
if (当たる.length === 0) return [];
|
|
175
|
+
const 直した後 = applyTopicAutoFix(d.topic);
|
|
176
|
+
const 直せる = !REDUNDANT_TOPIC_PATTERNS.some(({ pattern }) => pattern.test(直した後));
|
|
177
|
+
const 例 = KIND_TO_JA.gantt!;
|
|
178
|
+
const 手で直す = `何を示す図かを文で書き直す (「${例.shows}を示す${例.name}」 のように)`;
|
|
179
|
+
return 当たる.map(({ hint }) => ({
|
|
180
|
+
rule: "topic-redundant-implementation-detail",
|
|
181
|
+
severity: "warn",
|
|
182
|
+
target: d.id,
|
|
183
|
+
message: `図の説明 \`${d.topic}\` に実装の書き方が入っている。 ${hint}`,
|
|
184
|
+
suggestion: 直せる ? 直した後 : 手で直す,
|
|
185
|
+
autoFixable: 直せる,
|
|
186
|
+
}));
|
|
149
187
|
}
|
|
150
188
|
|
|
151
189
|
function ruleEmptyChartData(d: CdlDiagram): LintIssue[] {
|
|
@@ -158,8 +196,8 @@ function ruleEmptyChartData(d: CdlDiagram): LintIssue[] {
|
|
|
158
196
|
rule: "chart-empty-datum",
|
|
159
197
|
severity: "warn",
|
|
160
198
|
target: n.id,
|
|
161
|
-
message:
|
|
162
|
-
suggestion:
|
|
199
|
+
message: `図表 \`${n.id}\` に値 (\`datum\`) が 1 件も無く、 図表が描かれない`,
|
|
200
|
+
suggestion: `\`.datum({ id, label, value })\` で値を 1 件以上足す`,
|
|
163
201
|
autoFixable: false,
|
|
164
202
|
});
|
|
165
203
|
}
|
|
@@ -168,8 +206,8 @@ function ruleEmptyChartData(d: CdlDiagram): LintIssue[] {
|
|
|
168
206
|
rule: "chart-single-datum",
|
|
169
207
|
severity: "info",
|
|
170
208
|
target: n.id,
|
|
171
|
-
message:
|
|
172
|
-
suggestion:
|
|
209
|
+
message: `図表 \`${n.id}\` の値 (\`datum\`) が 1 件だけで、 比べることも移り変わりを追うこともできない`,
|
|
210
|
+
suggestion: `値を 2 件以上にする (折れ線グラフは 3 件以上で傾きが読める)`,
|
|
173
211
|
autoFixable: false,
|
|
174
212
|
});
|
|
175
213
|
}
|
|
@@ -190,8 +228,8 @@ function ruleGanttUnknownDependsOn(d: CdlDiagram): LintIssue[] {
|
|
|
190
228
|
rule: "gantt-unknown-depends-on",
|
|
191
229
|
severity: "warn",
|
|
192
230
|
target: t.id,
|
|
193
|
-
message:
|
|
194
|
-
suggestion:
|
|
231
|
+
message: `作業 \`${t.id}\` の \`dependsOn\` が、 無い作業 \`${t.dependsOn}\` を指している`,
|
|
232
|
+
suggestion: `\`dependsOn\` を既にある作業の \`id\` に直すか、 \`dependsOn\` を外す`,
|
|
195
233
|
autoFixable: false,
|
|
196
234
|
});
|
|
197
235
|
}
|
|
@@ -225,8 +263,8 @@ function ruleMindMapParentReference(d: CdlDiagram): LintIssue[] {
|
|
|
225
263
|
rule: "mindmap-unknown-parent",
|
|
226
264
|
severity: "warn",
|
|
227
265
|
target: b.id,
|
|
228
|
-
message:
|
|
229
|
-
suggestion:
|
|
266
|
+
message: `枝 \`${b.id}\` の \`parent\` が、 無い枝 \`${b.parent}\` を指している`,
|
|
267
|
+
suggestion: `\`parent\` を中心 (\`${n.mindData.rootId}\`) か、 既にある枝の \`id\` に直す`,
|
|
230
268
|
autoFixable: false,
|
|
231
269
|
});
|
|
232
270
|
}
|
|
@@ -247,8 +285,8 @@ function ruleTreeParentReference(d: CdlDiagram): LintIssue[] {
|
|
|
247
285
|
rule: "tree-unknown-parent",
|
|
248
286
|
severity: "warn",
|
|
249
287
|
target: t.id,
|
|
250
|
-
message:
|
|
251
|
-
suggestion:
|
|
288
|
+
message: `階層図の項目 \`${t.id}\` の \`parent\` が、 無い項目 \`${t.parent}\` を指している`,
|
|
289
|
+
suggestion: `\`parent\` を既にある項目の \`id\` に直すか、 \`parent\` を外して一番上の項目にする`,
|
|
252
290
|
autoFixable: false,
|
|
253
291
|
});
|
|
254
292
|
}
|
|
@@ -268,8 +306,8 @@ function ruleQuadrantMissingItems(d: CdlDiagram): LintIssue[] {
|
|
|
268
306
|
rule: "quadrant-empty",
|
|
269
307
|
severity: "warn",
|
|
270
308
|
target: n.id,
|
|
271
|
-
message:
|
|
272
|
-
suggestion:
|
|
309
|
+
message: `四象限図 \`${n.id}\` に項目 (\`item\`) が 1 件も無く、 軸だけが描かれる`,
|
|
310
|
+
suggestion: `\`.item({ id, title, quadrant })\` で項目を 1 件以上足す (\`quadrant\` は \`topLeft\` などの 4 区画から選ぶ)`,
|
|
273
311
|
autoFixable: false,
|
|
274
312
|
});
|
|
275
313
|
}
|
|
@@ -279,8 +317,8 @@ function ruleQuadrantMissingItems(d: CdlDiagram): LintIssue[] {
|
|
|
279
317
|
rule: "quadrant-single-quadrant",
|
|
280
318
|
severity: "info",
|
|
281
319
|
target: n.id,
|
|
282
|
-
message:
|
|
283
|
-
suggestion:
|
|
320
|
+
message: `四象限図 \`${n.id}\` の項目がすべて 1 つの区画に集まり、 4 つに分けた意味が薄い`,
|
|
321
|
+
suggestion: `項目を 2 つ以上の区画に分ける。 強みと弱みを並べる分析 (\`SWOT\`) や優先度を決める図は、 4 つの区画に散らして使う`,
|
|
284
322
|
autoFixable: false,
|
|
285
323
|
});
|
|
286
324
|
}
|
|
@@ -290,13 +328,13 @@ function ruleQuadrantMissingItems(d: CdlDiagram): LintIssue[] {
|
|
|
290
328
|
}
|
|
291
329
|
|
|
292
330
|
/**
|
|
293
|
-
*
|
|
331
|
+
* 段階の人数が減っていくことを見る。
|
|
294
332
|
*
|
|
295
333
|
* 人数の欄は `{名前}` を書ける (状態から取る形、 cdl の `render/payload-binding.ts` が解く)。
|
|
296
334
|
* その場合ここでは値が決まらないので、**数どうしの組だけを比べる** (#1194)。
|
|
297
335
|
*
|
|
298
336
|
* 素通しで比べると文字列の大小比較になり、`{trial}` が `{signup}` より大きいという理由で
|
|
299
|
-
* 発火する。
|
|
337
|
+
* 発火する。 実際にカタログの funnel を状態から取る形にした時、その偽発火が出た。
|
|
300
338
|
*/
|
|
301
339
|
function ruleFunnelMonotonicCount(d: CdlDiagram): LintIssue[] {
|
|
302
340
|
const out: LintIssue[] = [];
|
|
@@ -312,8 +350,8 @@ function ruleFunnelMonotonicCount(d: CdlDiagram): LintIssue[] {
|
|
|
312
350
|
rule: "funnel-increasing-count",
|
|
313
351
|
severity: "warn",
|
|
314
352
|
target: stages[i]!.id,
|
|
315
|
-
message:
|
|
316
|
-
suggestion:
|
|
353
|
+
message: `段階 \`${stages[i]!.id}\` の数 (${今}) が前の段階 (${前}) より多い。 絞り込み図は段階が進むほど数が減る`,
|
|
354
|
+
suggestion: `段階の順番を確かめる。 数が増える流れを示すなら、 折れ線グラフ (\`chart-line\`) などの別の図にする`,
|
|
317
355
|
autoFixable: false,
|
|
318
356
|
});
|
|
319
357
|
}
|
package/src/relative-pos.ts
CHANGED
|
@@ -12,8 +12,20 @@
|
|
|
12
12
|
* 直らない事故を、 規則を共有することで構造的に防ぐ。
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
/**
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* 基準からどちら側に置くか。
|
|
17
|
+
*
|
|
18
|
+
* 語の一覧は下の `RELATIVE_DIRECTIONS` が持つ。 型を並べ直さず、一覧から導く。
|
|
19
|
+
*/
|
|
20
|
+
export type RelativeDirection = (typeof RELATIVE_DIRECTIONS)[number];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 向きとして受ける語の一覧。
|
|
24
|
+
*
|
|
25
|
+
* 読み取りの入口が 2 つある (本文の `位置: Web の右 200` と JSON の `posRel.dir`) ため、
|
|
26
|
+
* 一覧を 1 箇所に置いて両方から引く。 2 度書くと、向きを足した時に片方だけが取り残される。
|
|
27
|
+
*/
|
|
28
|
+
export const RELATIVE_DIRECTIONS = ["right", "left", "above", "below"] as const;
|
|
17
29
|
|
|
18
30
|
/** 相対で書かれた位置の指定。 */
|
|
19
31
|
export type RelativePos = {
|
|
@@ -180,3 +192,60 @@ export function orderByDependency(
|
|
|
180
192
|
}
|
|
181
193
|
return { order, cyclic: [...cyclic] };
|
|
182
194
|
}
|
|
195
|
+
|
|
196
|
+
/** 解けない書き方の種類。 誤りの文は入口ごとに作るので、ここでは種類だけを返す */
|
|
197
|
+
export type RelativeProblemKind = "self" | "missing-anchor" | "cyclic";
|
|
198
|
+
|
|
199
|
+
/** 解けない書き方 1 件。 `anchor` は相手が居ない時だけ持つ */
|
|
200
|
+
export type RelativeProblem = {
|
|
201
|
+
name: string;
|
|
202
|
+
kind: RelativeProblemKind;
|
|
203
|
+
anchor?: string;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 解けない相対の指定を挙げる (#2039)。
|
|
208
|
+
*
|
|
209
|
+
* 解けない書き方は 3 通りある。 相手が居ない / 自分を基準にした / 基準が輪になっている。
|
|
210
|
+
* どれも「書いたのに図が変わらない」 形で表に出るため、図を出す前に知らせる。
|
|
211
|
+
*
|
|
212
|
+
* **判定は本 file に 1 つだけ置く**。 読み取りの入口が 2 つある (本文と JSON) ため、
|
|
213
|
+
* 片方だけに判定を持つと、同じ書き方が入口によって通ったり通らなかったりする。
|
|
214
|
+
* 実際 JSON 側には判定が無く、相対で置く欄そのものが無かった。
|
|
215
|
+
*
|
|
216
|
+
* 誤りの文は返さない。 本文側は行番号を、JSON 側は欄の場所を添えるため、文の組み立ては
|
|
217
|
+
* 入口に任せる。 ここが返すのは「誰の」「どの種類の」 解けなさか だけ。
|
|
218
|
+
*
|
|
219
|
+
* 呼ぶ側は、挙がった名前から相対の指定を外す。 残したままだと、誤りを無視して読み込んだ
|
|
220
|
+
* 経路で解決できない指定が組み立てまで届く。
|
|
221
|
+
*/
|
|
222
|
+
export function findRelativeProblems(
|
|
223
|
+
items: ReadonlyArray<{ name: string; rel?: RelativePos }>,
|
|
224
|
+
): RelativeProblem[] {
|
|
225
|
+
const named = new Set(items.map((i) => i.name));
|
|
226
|
+
const broken = new Set<string>();
|
|
227
|
+
const out: RelativeProblem[] = [];
|
|
228
|
+
|
|
229
|
+
for (const it of items) {
|
|
230
|
+
const rel = it.rel;
|
|
231
|
+
if (!rel) continue;
|
|
232
|
+
if (rel.anchor === it.name) {
|
|
233
|
+
out.push({ name: it.name, kind: "self" });
|
|
234
|
+
broken.add(it.name);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (!named.has(rel.anchor)) {
|
|
238
|
+
out.push({ name: it.name, kind: "missing-anchor", anchor: rel.anchor });
|
|
239
|
+
broken.add(it.name);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// 輪は、既に解けないと判った分を外してから探す。 外さないと「相手が居ない」 1 件が
|
|
244
|
+
// 「輪になっている」 としても挙がり、同じ 1 つの誤りが 2 通りの直し方で出る
|
|
245
|
+
const { cyclic } = orderByDependency(
|
|
246
|
+
items.map((i) => ({ name: i.name, rel: broken.has(i.name) ? undefined : i.rel })),
|
|
247
|
+
);
|
|
248
|
+
for (const name of cyclic) out.push({ name, kind: "cyclic" });
|
|
249
|
+
|
|
250
|
+
return out;
|
|
251
|
+
}
|
package/src/schema.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* });
|
|
14
14
|
* const diagram = jsonToDiagram(res.content[0].input);
|
|
15
15
|
*
|
|
16
|
-
* schema JSON は `packages/dragon/schemas/diagram.json` が SSOT、 本 file は import して再 export するだけ。
|
|
16
|
+
* schema JSON は `packages/dragon/src/schemas/diagram.json` が SSOT、 本 file は import して再 export するだけ。
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import schemaJson from "./schemas/diagram.json" with { type: "json" };
|