@stilhq/core 0.6.0 → 0.6.1
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/changeset/apply.d.ts +11 -0
- package/dist/changeset/apply.d.ts.map +1 -0
- package/dist/changeset/apply.js +239 -0
- package/dist/changeset/apply.js.map +1 -0
- package/dist/changeset/ci-report.d.ts +25 -0
- package/dist/changeset/ci-report.d.ts.map +1 -0
- package/dist/changeset/ci-report.js +206 -0
- package/dist/changeset/ci-report.js.map +1 -0
- package/dist/changeset/classify.d.ts +77 -0
- package/dist/changeset/classify.d.ts.map +1 -0
- package/dist/changeset/classify.js +285 -0
- package/dist/changeset/classify.js.map +1 -0
- package/dist/changeset/cli-gate.d.ts +46 -0
- package/dist/changeset/cli-gate.d.ts.map +1 -0
- package/dist/changeset/cli-gate.js +409 -0
- package/dist/changeset/cli-gate.js.map +1 -0
- package/dist/changeset/conflict.d.ts +11 -0
- package/dist/changeset/conflict.d.ts.map +1 -0
- package/dist/changeset/conflict.js +130 -0
- package/dist/changeset/conflict.js.map +1 -0
- package/dist/changeset/diff-components.d.ts +80 -0
- package/dist/changeset/diff-components.d.ts.map +1 -0
- package/dist/changeset/diff-components.js +264 -0
- package/dist/changeset/diff-components.js.map +1 -0
- package/dist/changeset/diff-text.d.ts +66 -0
- package/dist/changeset/diff-text.d.ts.map +1 -0
- package/dist/changeset/diff-text.js +284 -0
- package/dist/changeset/diff-text.js.map +1 -0
- package/dist/changeset/diff-tokens.d.ts +63 -0
- package/dist/changeset/diff-tokens.d.ts.map +1 -0
- package/dist/changeset/diff-tokens.js +483 -0
- package/dist/changeset/diff-tokens.js.map +1 -0
- package/dist/changeset/impact.d.ts +31 -0
- package/dist/changeset/impact.d.ts.map +1 -0
- package/dist/changeset/impact.js +188 -0
- package/dist/changeset/impact.js.map +1 -0
- package/dist/changeset/pr-body.d.ts +33 -0
- package/dist/changeset/pr-body.d.ts.map +1 -0
- package/dist/changeset/pr-body.js +421 -0
- package/dist/changeset/pr-body.js.map +1 -0
- package/dist/changeset/quality-gate.d.ts +10 -0
- package/dist/changeset/quality-gate.d.ts.map +1 -0
- package/dist/changeset/quality-gate.js +207 -0
- package/dist/changeset/quality-gate.js.map +1 -0
- package/dist/changeset/rollback.d.ts +19 -0
- package/dist/changeset/rollback.d.ts.map +1 -0
- package/dist/changeset/rollback.js +163 -0
- package/dist/changeset/rollback.js.map +1 -0
- package/dist/ci/template-generator.d.ts +22 -0
- package/dist/ci/template-generator.d.ts.map +1 -0
- package/dist/ci/template-generator.js +215 -0
- package/dist/ci/template-generator.js.map +1 -0
- package/dist/gate/engine.d.ts +20 -0
- package/dist/gate/engine.d.ts.map +1 -0
- package/dist/gate/engine.js +109 -0
- package/dist/gate/engine.js.map +1 -0
- package/dist/gate/types.d.ts +76 -0
- package/dist/gate/types.d.ts.map +1 -0
- package/dist/gate/types.js +6 -0
- package/dist/gate/types.js.map +1 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +144 -1
- package/dist/index.js.map +1 -1
- package/dist/pr/comment-manager.d.ts +33 -0
- package/dist/pr/comment-manager.d.ts.map +1 -0
- package/dist/pr/comment-manager.js +137 -0
- package/dist/pr/comment-manager.js.map +1 -0
- package/dist/pr/conflict.d.ts +34 -0
- package/dist/pr/conflict.d.ts.map +1 -0
- package/dist/pr/conflict.js +127 -0
- package/dist/pr/conflict.js.map +1 -0
- package/dist/pr/generator.d.ts +70 -0
- package/dist/pr/generator.d.ts.map +1 -0
- package/dist/pr/generator.js +241 -0
- package/dist/pr/generator.js.map +1 -0
- package/dist/pr/github-api.d.ts +122 -0
- package/dist/pr/github-api.d.ts.map +1 -0
- package/dist/pr/github-api.js +269 -0
- package/dist/pr/github-api.js.map +1 -0
- package/dist/report/changeset-report.d.ts +10 -0
- package/dist/report/changeset-report.d.ts.map +1 -0
- package/dist/report/changeset-report.js +14 -0
- package/dist/report/changeset-report.js.map +1 -0
- package/dist/report/classification-report.d.ts +12 -0
- package/dist/report/classification-report.d.ts.map +1 -0
- package/dist/report/classification-report.js +157 -0
- package/dist/report/classification-report.js.map +1 -0
- package/dist/report/impact-report.d.ts +10 -0
- package/dist/report/impact-report.d.ts.map +1 -0
- package/dist/report/impact-report.js +47 -0
- package/dist/report/impact-report.js.map +1 -0
- package/dist/report/summary-report.d.ts +10 -0
- package/dist/report/summary-report.d.ts.map +1 -0
- package/dist/report/summary-report.js +96 -0
- package/dist/report/summary-report.js.map +1 -0
- package/dist/report/writer.d.ts +17 -0
- package/dist/report/writer.d.ts.map +1 -0
- package/dist/report/writer.js +41 -0
- package/dist/report/writer.js.map +1 -0
- package/dist/rules/engine.d.ts +46 -0
- package/dist/rules/engine.d.ts.map +1 -0
- package/dist/rules/engine.js +200 -0
- package/dist/rules/engine.js.map +1 -0
- package/dist/rules/index.d.ts +10 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +25 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/loader.d.ts +20 -0
- package/dist/rules/loader.d.ts.map +1 -0
- package/dist/rules/loader.js +93 -0
- package/dist/rules/loader.js.map +1 -0
- package/dist/rules/packs/core.d.ts +14 -0
- package/dist/rules/packs/core.d.ts.map +1 -0
- package/dist/rules/packs/core.js +377 -0
- package/dist/rules/packs/core.js.map +1 -0
- package/dist/rules/schema.d.ts +12 -0
- package/dist/rules/schema.d.ts.map +1 -0
- package/dist/rules/schema.js +149 -0
- package/dist/rules/schema.js.map +1 -0
- package/dist/rules/types.d.ts +86 -0
- package/dist/rules/types.d.ts.map +1 -0
- package/dist/rules/types.js +9 -0
- package/dist/rules/types.js.map +1 -0
- package/dist/types/changeset.d.ts +509 -0
- package/dist/types/changeset.d.ts.map +1 -0
- package/dist/types/changeset.js +14 -0
- package/dist/types/changeset.js.map +1 -0
- package/dist/w3c/__tests__/fixtures/tokens-studio-export.json +58 -0
- package/dist/w3c/parser.d.ts +44 -0
- package/dist/w3c/parser.d.ts.map +1 -0
- package/dist/w3c/parser.js +146 -0
- package/dist/w3c/parser.js.map +1 -0
- package/dist/w3c/types.d.ts +31 -0
- package/dist/w3c/types.d.ts.map +1 -0
- package/dist/w3c/types.js +7 -0
- package/dist/w3c/types.js.map +1 -0
- package/dist/w3c/validator.d.ts +14 -0
- package/dist/w3c/validator.d.ts.map +1 -0
- package/dist/w3c/validator.js +162 -0
- package/dist/w3c/validator.js.map +1 -0
- package/package.json +3 -2
- package/templates/stil-quality-gate.yml +122 -0
- package/templates/workspace/.github/PULL_REQUEST_TEMPLATE.md +14 -0
- package/templates/workspace/.github/workflows/stil-gate.yml +42 -0
- package/templates/workspace/.github/workflows/stil-preview.yml +29 -0
- package/templates/workspace/.stil/components/snapshots/.gitkeep +0 -0
- package/templates/workspace/.stil/reports/.gitkeep +0 -0
- package/templates/workspace/.stil/rules/.gitkeep +0 -0
- package/templates/workspace/.stil/text/snapshots/.gitkeep +0 -0
- package/templates/workspace/.stil/tokens/config/mapping.json +16 -0
- package/templates/workspace/.stil/tokens/raw/.gitkeep +0 -0
- package/templates/workspace/README.md +40 -0
- package/templates/workspace/package.json +19 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// impact.ts — Impact v0(最小版)
|
|
3
|
+
//
|
|
4
|
+
// 各レーンの ChangeSet から影響範囲を推定する。
|
|
5
|
+
// v0 は L2 精度(推定値)。完全な依存グラフ解析は v1.5+。
|
|
6
|
+
//
|
|
7
|
+
// - tokenKey → consumers(alias chain を辿る)
|
|
8
|
+
// - i18nKey → 使用ノード数
|
|
9
|
+
// - componentKey → インスタンス数
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.calculateImpact = calculateImpact;
|
|
12
|
+
exports.applyImpact = applyImpact;
|
|
13
|
+
// ========================================
|
|
14
|
+
// Core: calculateImpact
|
|
15
|
+
// ========================================
|
|
16
|
+
/**
|
|
17
|
+
* ChangeSet から ImpactSummary を計算する。
|
|
18
|
+
*
|
|
19
|
+
* @param changeset - 対象 ChangeSet
|
|
20
|
+
* @param context - 外部から渡される使用状況マップ
|
|
21
|
+
* @returns 更新された ImpactSummary
|
|
22
|
+
*/
|
|
23
|
+
function calculateImpact(changeset, context = {}) {
|
|
24
|
+
switch (changeset.lane) {
|
|
25
|
+
case "tokens":
|
|
26
|
+
return calculateTokenImpact(changeset.items, context.aliasConsumers || {});
|
|
27
|
+
case "text":
|
|
28
|
+
return calculateTextImpact(changeset.items, context.textUsage || {});
|
|
29
|
+
case "components":
|
|
30
|
+
return calculateComponentImpact(changeset.items, context.componentInstances || {});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// ========================================
|
|
34
|
+
// Token Impact
|
|
35
|
+
// ========================================
|
|
36
|
+
/**
|
|
37
|
+
* Tokens レーンの影響計算。
|
|
38
|
+
*
|
|
39
|
+
* alias chain を辿って、変更されたトークンを消費している
|
|
40
|
+
* 下流のトークン・コンポーネント数を推定する。
|
|
41
|
+
*/
|
|
42
|
+
function calculateTokenImpact(items, aliasConsumers) {
|
|
43
|
+
const details = [];
|
|
44
|
+
const affectedTokenKeys = new Set();
|
|
45
|
+
for (const item of items) {
|
|
46
|
+
// 直接変更されたトークンを記録
|
|
47
|
+
affectedTokenKeys.add(item.tokenKey);
|
|
48
|
+
details.push({
|
|
49
|
+
type: "token",
|
|
50
|
+
id: item.tokenKey,
|
|
51
|
+
name: item.tokenKey,
|
|
52
|
+
reason: item.before === null
|
|
53
|
+
? "New token"
|
|
54
|
+
: item.after === null
|
|
55
|
+
? "Removed token"
|
|
56
|
+
: `Value changed: ${item.before} → ${item.after}`,
|
|
57
|
+
});
|
|
58
|
+
// alias chain を辿って consumers を追加
|
|
59
|
+
const consumers = collectAliasConsumers(item.tokenKey, aliasConsumers);
|
|
60
|
+
for (const consumer of consumers) {
|
|
61
|
+
if (!affectedTokenKeys.has(consumer)) {
|
|
62
|
+
affectedTokenKeys.add(consumer);
|
|
63
|
+
details.push({
|
|
64
|
+
type: "token",
|
|
65
|
+
id: consumer,
|
|
66
|
+
name: consumer,
|
|
67
|
+
reason: `Depends on changed token: ${item.tokenKey}`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
affectedTokens: affectedTokenKeys.size,
|
|
74
|
+
affectedComponents: 0,
|
|
75
|
+
affectedTextNodes: 0,
|
|
76
|
+
affectedPages: [],
|
|
77
|
+
details,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* alias chain を再帰的に辿って、全ての consumers を収集する。
|
|
82
|
+
*
|
|
83
|
+
* 循環参照を検出して無限ループを防止する。
|
|
84
|
+
*/
|
|
85
|
+
function collectAliasConsumers(tokenKey, aliasConsumers, visited = new Set()) {
|
|
86
|
+
if (visited.has(tokenKey))
|
|
87
|
+
return []; // 循環参照の防止
|
|
88
|
+
visited.add(tokenKey);
|
|
89
|
+
const directConsumers = aliasConsumers[tokenKey] || [];
|
|
90
|
+
const allConsumers = [...directConsumers];
|
|
91
|
+
for (const consumer of directConsumers) {
|
|
92
|
+
const transitive = collectAliasConsumers(consumer, aliasConsumers, visited);
|
|
93
|
+
allConsumers.push(...transitive);
|
|
94
|
+
}
|
|
95
|
+
return allConsumers;
|
|
96
|
+
}
|
|
97
|
+
// ========================================
|
|
98
|
+
// Text Impact
|
|
99
|
+
// ========================================
|
|
100
|
+
/**
|
|
101
|
+
* Text レーンの影響計算。
|
|
102
|
+
*
|
|
103
|
+
* i18nKey ごとの使用ノード数をカウントする。
|
|
104
|
+
*/
|
|
105
|
+
function calculateTextImpact(items, textUsage) {
|
|
106
|
+
const details = [];
|
|
107
|
+
let totalAffectedNodes = 0;
|
|
108
|
+
for (const item of items) {
|
|
109
|
+
const nodeCount = textUsage[item.i18nKey] || 1; // デフォルト 1(自身のみ)
|
|
110
|
+
totalAffectedNodes += nodeCount;
|
|
111
|
+
details.push({
|
|
112
|
+
type: "text",
|
|
113
|
+
id: item.i18nKey,
|
|
114
|
+
name: `${item.i18nKey} [${item.locale}]`,
|
|
115
|
+
reason: item.before === null
|
|
116
|
+
? `New text binding (${nodeCount} node(s))`
|
|
117
|
+
: item.after === null
|
|
118
|
+
? `Text removed (${nodeCount} node(s))`
|
|
119
|
+
: `Text changed (${nodeCount} node(s))`,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
affectedTokens: 0,
|
|
124
|
+
affectedComponents: 0,
|
|
125
|
+
affectedTextNodes: totalAffectedNodes,
|
|
126
|
+
affectedPages: [],
|
|
127
|
+
details,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
// ========================================
|
|
131
|
+
// Component Impact
|
|
132
|
+
// ========================================
|
|
133
|
+
/**
|
|
134
|
+
* Components レーンの影響計算。
|
|
135
|
+
*
|
|
136
|
+
* componentId ごとのインスタンス数をカウントする。
|
|
137
|
+
*/
|
|
138
|
+
function calculateComponentImpact(items, componentInstances) {
|
|
139
|
+
const details = [];
|
|
140
|
+
let totalAffectedInstances = 0;
|
|
141
|
+
for (const item of items) {
|
|
142
|
+
const instanceCount = componentInstances[item.componentId] || 0;
|
|
143
|
+
totalAffectedInstances += instanceCount;
|
|
144
|
+
details.push({
|
|
145
|
+
type: "component",
|
|
146
|
+
id: item.componentId,
|
|
147
|
+
name: `${item.componentId} (${item.figmaComponentKey})`,
|
|
148
|
+
reason: describeComponentImpact(item, instanceCount),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
affectedTokens: 0,
|
|
153
|
+
affectedComponents: items.length,
|
|
154
|
+
affectedTextNodes: 0,
|
|
155
|
+
affectedPages: [],
|
|
156
|
+
details,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function describeComponentImpact(item, instanceCount) {
|
|
160
|
+
const parts = [];
|
|
161
|
+
for (const [prop, values] of Object.entries(item.variantProps.added)) {
|
|
162
|
+
parts.push(`+${prop}: [${values.join(", ")}]`);
|
|
163
|
+
}
|
|
164
|
+
for (const [prop, values] of Object.entries(item.variantProps.removed)) {
|
|
165
|
+
parts.push(`-${prop}: [${values.join(", ")}]`);
|
|
166
|
+
}
|
|
167
|
+
for (const [oldName, newName] of Object.entries(item.variantProps.renamed)) {
|
|
168
|
+
parts.push(`${oldName} → ${newName}`);
|
|
169
|
+
}
|
|
170
|
+
const changes = parts.join("; ") || item.changeType;
|
|
171
|
+
return `${changes} (${instanceCount} instance(s))`;
|
|
172
|
+
}
|
|
173
|
+
// ========================================
|
|
174
|
+
// Convenience: ChangeSet に Impact を適用
|
|
175
|
+
// ========================================
|
|
176
|
+
/**
|
|
177
|
+
* ChangeSet の impactSummary を再計算して上書きする便利関数。
|
|
178
|
+
*
|
|
179
|
+
* @returns 新しい impactSummary が設定された ChangeSet のコピー
|
|
180
|
+
*/
|
|
181
|
+
function applyImpact(changeset, context = {}) {
|
|
182
|
+
const impactSummary = calculateImpact(changeset, context);
|
|
183
|
+
return {
|
|
184
|
+
...changeset,
|
|
185
|
+
impactSummary,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=impact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"impact.js","sourceRoot":"","sources":["../../src/changeset/impact.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ChangeSet, ImpactSummary } from "../types/changeset";
|
|
2
|
+
import type { GateResult } from "../gate/types";
|
|
3
|
+
/**
|
|
4
|
+
* ChangeSet と ImpactSummary から PR body(Markdown)を生成する。
|
|
5
|
+
*
|
|
6
|
+
* @param changeset - 対象 ChangeSet
|
|
7
|
+
* @param impact - 影響範囲サマリー
|
|
8
|
+
* @param previewUrl - Storybook preview URL(オプション)
|
|
9
|
+
* @returns Markdown 形式の PR body 文字列
|
|
10
|
+
*/
|
|
11
|
+
export declare function generatePRBody(changeset: ChangeSet, impact: ImpactSummary, previewUrl?: string): string;
|
|
12
|
+
export interface PRBodyOptions {
|
|
13
|
+
previewUrl?: string;
|
|
14
|
+
includeImpactCard?: boolean;
|
|
15
|
+
includeClassification?: boolean;
|
|
16
|
+
includeQualityGate?: boolean;
|
|
17
|
+
includeActionItems?: boolean;
|
|
18
|
+
maxItemsInBody?: number;
|
|
19
|
+
linkToFullReport?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Gate Report を含む PR body を生成。
|
|
23
|
+
* summary.md の内容をベースに、PR 向けにフォーマット。
|
|
24
|
+
*/
|
|
25
|
+
export declare function generateGatePRBody(changeSet: ChangeSet, gateResult: GateResult, options?: PRBodyOptions): string;
|
|
26
|
+
/**
|
|
27
|
+
* PR title を生成。
|
|
28
|
+
*
|
|
29
|
+
* Format: "[STIL {LANE}] {summary} ({classification})"
|
|
30
|
+
* Max 72 chars, truncated if necessary.
|
|
31
|
+
*/
|
|
32
|
+
export declare function generatePRTitle(changeSet: ChangeSet, gateResult: GateResult): string;
|
|
33
|
+
//# sourceMappingURL=pr-body.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pr-body.d.ts","sourceRoot":"","sources":["../../src/changeset/pr-body.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,SAAS,EAIT,aAAa,EACd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAMhD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,aAAa,EACrB,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CA4BR;AAyND,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAMD;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,UAAU,EACtB,OAAO,CAAC,EAAE,aAAa,GACtB,MAAM,CAoLR;AAwBD;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,UAAU,GACrB,MAAM,CAgCR"}
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// pr-body.ts — PR Body Generator
|
|
3
|
+
//
|
|
4
|
+
// ChangeSet から GitHub PR の body テキスト(Markdown)を生成するロジック。
|
|
5
|
+
// Figma→Code 方向の PR 自動生成時に使用される。
|
|
6
|
+
//
|
|
7
|
+
// ARCHITECTURE_V1.md Section 5.3 の PR Comment Template に基づく。
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.generatePRBody = generatePRBody;
|
|
10
|
+
exports.generateGatePRBody = generateGatePRBody;
|
|
11
|
+
exports.generatePRTitle = generatePRTitle;
|
|
12
|
+
// ========================================
|
|
13
|
+
// Core: generatePRBody
|
|
14
|
+
// ========================================
|
|
15
|
+
/**
|
|
16
|
+
* ChangeSet と ImpactSummary から PR body(Markdown)を生成する。
|
|
17
|
+
*
|
|
18
|
+
* @param changeset - 対象 ChangeSet
|
|
19
|
+
* @param impact - 影響範囲サマリー
|
|
20
|
+
* @param previewUrl - Storybook preview URL(オプション)
|
|
21
|
+
* @returns Markdown 形式の PR body 文字列
|
|
22
|
+
*/
|
|
23
|
+
function generatePRBody(changeset, impact, previewUrl) {
|
|
24
|
+
const sections = [];
|
|
25
|
+
// Header
|
|
26
|
+
const shortId = changeset.id.slice(0, 8);
|
|
27
|
+
sections.push(`## Stil ChangeSet: ${shortId}`);
|
|
28
|
+
// Change summary
|
|
29
|
+
sections.push(generateChangeSummary(changeset));
|
|
30
|
+
// Change details table
|
|
31
|
+
sections.push(generateChangeDetails(changeset));
|
|
32
|
+
// Impact section
|
|
33
|
+
sections.push(generateImpactSection(impact));
|
|
34
|
+
// Quality checks
|
|
35
|
+
sections.push(generateQualitySection(changeset));
|
|
36
|
+
// Preview section
|
|
37
|
+
sections.push(generatePreviewSection(previewUrl));
|
|
38
|
+
// Footer
|
|
39
|
+
sections.push("---\nGenerated by [Stil](https://github.com/techang/stil-systems)");
|
|
40
|
+
return sections.join("\n\n");
|
|
41
|
+
}
|
|
42
|
+
// ========================================
|
|
43
|
+
// Section generators
|
|
44
|
+
// ========================================
|
|
45
|
+
function generateChangeSummary(changeset) {
|
|
46
|
+
const laneLabel = formatLane(changeset.lane);
|
|
47
|
+
const directionLabel = formatDirection(changeset.direction);
|
|
48
|
+
const classificationLabel = formatClassification(changeset.classification);
|
|
49
|
+
return [
|
|
50
|
+
"### 変更サマリー",
|
|
51
|
+
`- **Lane**: ${laneLabel}`,
|
|
52
|
+
`- **Direction**: ${directionLabel}`,
|
|
53
|
+
`- **Classification**: ${classificationLabel}`,
|
|
54
|
+
`- **変更件数**: ${changeset.items.length} 件`,
|
|
55
|
+
].join("\n");
|
|
56
|
+
}
|
|
57
|
+
function generateChangeDetails(changeset) {
|
|
58
|
+
const lines = ["### 変更内容"];
|
|
59
|
+
switch (changeset.lane) {
|
|
60
|
+
case "tokens":
|
|
61
|
+
lines.push(generateTokensTable(changeset.items));
|
|
62
|
+
break;
|
|
63
|
+
case "text":
|
|
64
|
+
lines.push(generateTextTable(changeset.items));
|
|
65
|
+
break;
|
|
66
|
+
case "components":
|
|
67
|
+
lines.push(generateComponentsTable(changeset.items));
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
return lines.join("\n");
|
|
71
|
+
}
|
|
72
|
+
function generateTokensTable(items) {
|
|
73
|
+
const rows = items.map((item) => {
|
|
74
|
+
const before = item.before ?? "_(new)_";
|
|
75
|
+
const after = item.after ?? "_(deleted)_";
|
|
76
|
+
return `| \`${item.tokenKey}\` | ${item.changeType} | ${escapeCell(before)} | ${escapeCell(after)} |`;
|
|
77
|
+
});
|
|
78
|
+
return [
|
|
79
|
+
"| Key | Type | Before | After |",
|
|
80
|
+
"|-----|------|--------|-------|",
|
|
81
|
+
...rows,
|
|
82
|
+
].join("\n");
|
|
83
|
+
}
|
|
84
|
+
function generateTextTable(items) {
|
|
85
|
+
const rows = items.map((item) => {
|
|
86
|
+
const before = item.before ?? "_(new)_";
|
|
87
|
+
const after = item.after ?? "_(deleted)_";
|
|
88
|
+
return `| \`${item.i18nKey}\` | ${item.changeType} | ${escapeCell(before)} | ${escapeCell(after)} |`;
|
|
89
|
+
});
|
|
90
|
+
return [
|
|
91
|
+
"| Key | Type | Before | After |",
|
|
92
|
+
"|-----|------|--------|-------|",
|
|
93
|
+
...rows,
|
|
94
|
+
].join("\n");
|
|
95
|
+
}
|
|
96
|
+
function generateComponentsTable(items) {
|
|
97
|
+
const rows = items.map((item) => {
|
|
98
|
+
const changes = describeVariantDiff(item);
|
|
99
|
+
return `| \`${item.componentId}\` | ${item.changeType} | ${escapeCell(changes)} |`;
|
|
100
|
+
});
|
|
101
|
+
return [
|
|
102
|
+
"| Component | Type | Changes |",
|
|
103
|
+
"|-----------|------|---------|",
|
|
104
|
+
...rows,
|
|
105
|
+
].join("\n");
|
|
106
|
+
}
|
|
107
|
+
function describeVariantDiff(item) {
|
|
108
|
+
const parts = [];
|
|
109
|
+
for (const [prop, values] of Object.entries(item.variantProps.added)) {
|
|
110
|
+
parts.push(`+${prop}: [${values.join(", ")}]`);
|
|
111
|
+
}
|
|
112
|
+
for (const [prop, values] of Object.entries(item.variantProps.removed)) {
|
|
113
|
+
parts.push(`-${prop}: [${values.join(", ")}]`);
|
|
114
|
+
}
|
|
115
|
+
for (const [oldName, newName] of Object.entries(item.variantProps.renamed)) {
|
|
116
|
+
parts.push(`${oldName} → ${newName}`);
|
|
117
|
+
}
|
|
118
|
+
return parts.join("; ") || item.changeType;
|
|
119
|
+
}
|
|
120
|
+
function generateImpactSection(impact) {
|
|
121
|
+
const lines = ["### 影響範囲"];
|
|
122
|
+
if (impact.affectedTokens > 0) {
|
|
123
|
+
lines.push(`- 影響を受ける tokens: **${impact.affectedTokens}** 件`);
|
|
124
|
+
}
|
|
125
|
+
if (impact.affectedComponents > 0) {
|
|
126
|
+
lines.push(`- 影響を受ける components: **${impact.affectedComponents}** 件`);
|
|
127
|
+
}
|
|
128
|
+
if (impact.affectedTextNodes > 0) {
|
|
129
|
+
lines.push(`- 影響を受ける text nodes: **${impact.affectedTextNodes}** 件`);
|
|
130
|
+
}
|
|
131
|
+
if (impact.affectedPages.length > 0) {
|
|
132
|
+
lines.push(`- 影響を受けるページ: ${impact.affectedPages.join(", ")}`);
|
|
133
|
+
}
|
|
134
|
+
// No impact at all
|
|
135
|
+
if (impact.affectedTokens === 0 &&
|
|
136
|
+
impact.affectedComponents === 0 &&
|
|
137
|
+
impact.affectedTextNodes === 0) {
|
|
138
|
+
lines.push("- 影響範囲なし");
|
|
139
|
+
}
|
|
140
|
+
return lines.join("\n");
|
|
141
|
+
}
|
|
142
|
+
function generateQualitySection(changeset) {
|
|
143
|
+
const { checksSummary } = changeset;
|
|
144
|
+
const lines = ["### 品質チェック"];
|
|
145
|
+
if (checksSummary.errors === 0 && checksSummary.warnings === 0) {
|
|
146
|
+
lines.push("✅ すべてのチェックを通過");
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
if (checksSummary.errors > 0) {
|
|
150
|
+
lines.push(`❌ エラー: ${checksSummary.errors} 件`);
|
|
151
|
+
}
|
|
152
|
+
if (checksSummary.warnings > 0) {
|
|
153
|
+
lines.push(`⚠️ 警告: ${checksSummary.warnings} 件`);
|
|
154
|
+
}
|
|
155
|
+
for (const check of checksSummary.checks) {
|
|
156
|
+
if (check.severity === "error") {
|
|
157
|
+
lines.push(`- ❌ ${check.message}${check.target ? ` (\`${check.target}\`)` : ""}`);
|
|
158
|
+
}
|
|
159
|
+
else if (check.severity === "warning") {
|
|
160
|
+
lines.push(`- ⚠️ ${check.message}${check.target ? ` (\`${check.target}\`)` : ""}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return lines.join("\n");
|
|
165
|
+
}
|
|
166
|
+
function generatePreviewSection(previewUrl) {
|
|
167
|
+
if (previewUrl) {
|
|
168
|
+
return `### Preview\n[Storybook Preview を確認](${previewUrl})`;
|
|
169
|
+
}
|
|
170
|
+
return "### Preview\n_Preview URL は CI 完了後に更新されます_";
|
|
171
|
+
}
|
|
172
|
+
// ========================================
|
|
173
|
+
// Formatting helpers
|
|
174
|
+
// ========================================
|
|
175
|
+
function formatLane(lane) {
|
|
176
|
+
switch (lane) {
|
|
177
|
+
case "tokens":
|
|
178
|
+
return "Tokens";
|
|
179
|
+
case "text":
|
|
180
|
+
return "Text";
|
|
181
|
+
case "components":
|
|
182
|
+
return "Components";
|
|
183
|
+
default:
|
|
184
|
+
return lane;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function formatDirection(direction) {
|
|
188
|
+
switch (direction) {
|
|
189
|
+
case "figma-to-code":
|
|
190
|
+
return "Figma → Code";
|
|
191
|
+
case "code-to-figma":
|
|
192
|
+
return "Code → Figma";
|
|
193
|
+
default:
|
|
194
|
+
return direction;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function formatClassification(classification) {
|
|
198
|
+
switch (classification) {
|
|
199
|
+
case "safe":
|
|
200
|
+
return "safe ✅";
|
|
201
|
+
case "review":
|
|
202
|
+
return "review ⚠️";
|
|
203
|
+
case "blocked":
|
|
204
|
+
return "blocked 🚫";
|
|
205
|
+
default:
|
|
206
|
+
return classification;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Markdown table cell のエスケープ。
|
|
211
|
+
* パイプ文字と改行をエスケープする。
|
|
212
|
+
*/
|
|
213
|
+
function escapeCell(value) {
|
|
214
|
+
return value.replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
215
|
+
}
|
|
216
|
+
// ========================================
|
|
217
|
+
// Gate-aware PR Body
|
|
218
|
+
// ========================================
|
|
219
|
+
/**
|
|
220
|
+
* Gate Report を含む PR body を生成。
|
|
221
|
+
* summary.md の内容をベースに、PR 向けにフォーマット。
|
|
222
|
+
*/
|
|
223
|
+
function generateGatePRBody(changeSet, gateResult, options) {
|
|
224
|
+
const opts = {
|
|
225
|
+
previewUrl: options?.previewUrl ?? "",
|
|
226
|
+
includeImpactCard: options?.includeImpactCard ?? true,
|
|
227
|
+
includeClassification: options?.includeClassification ?? true,
|
|
228
|
+
includeQualityGate: options?.includeQualityGate ?? true,
|
|
229
|
+
includeActionItems: options?.includeActionItems ?? true,
|
|
230
|
+
maxItemsInBody: options?.maxItemsInBody ?? 20,
|
|
231
|
+
linkToFullReport: options?.linkToFullReport ?? true,
|
|
232
|
+
};
|
|
233
|
+
const sections = [];
|
|
234
|
+
// Header
|
|
235
|
+
const statusEmoji = gateResult.passed ? "✅ PASSED" : "❌ FAILED";
|
|
236
|
+
sections.push("## Stil Gate Report");
|
|
237
|
+
sections.push("");
|
|
238
|
+
sections.push(`**ChangeSet**: \`${changeSet.id}\``);
|
|
239
|
+
sections.push(`**Lane**: ${formatLane(changeSet.lane)} | **Direction**: ${formatDirection(changeSet.direction)}`);
|
|
240
|
+
sections.push(`**Gate Mode**: ${gateResult.mode} | **Status**: ${statusEmoji}`);
|
|
241
|
+
// Classification table
|
|
242
|
+
if (opts.includeClassification) {
|
|
243
|
+
const cls = gateResult.classification;
|
|
244
|
+
sections.push("");
|
|
245
|
+
sections.push("---");
|
|
246
|
+
sections.push("");
|
|
247
|
+
sections.push("### Classification");
|
|
248
|
+
sections.push("");
|
|
249
|
+
sections.push("| Status | Count |");
|
|
250
|
+
sections.push("|--------|-------|");
|
|
251
|
+
sections.push(`| ✅ Safe | ${cls.summary.safeCount} |`);
|
|
252
|
+
sections.push(`| ⚠️ Review | ${cls.summary.reviewCount} |`);
|
|
253
|
+
sections.push(`| ❌ Blocked | ${cls.summary.blockedCount} |`);
|
|
254
|
+
}
|
|
255
|
+
// Impact Analysis
|
|
256
|
+
if (opts.includeImpactCard) {
|
|
257
|
+
const ia = gateResult.impact.impactAnalysis;
|
|
258
|
+
sections.push("");
|
|
259
|
+
sections.push("---");
|
|
260
|
+
sections.push("");
|
|
261
|
+
sections.push("### Impact Analysis");
|
|
262
|
+
sections.push("");
|
|
263
|
+
sections.push("| Metric | Value |");
|
|
264
|
+
sections.push("|--------|-------|");
|
|
265
|
+
sections.push(`| Affected Tokens | ${ia.affectedTokens.length} |`);
|
|
266
|
+
sections.push(`| Affected Components | ${ia.affectedComponents.length} |`);
|
|
267
|
+
const totalInstances = ia.affectedComponents.reduce((sum, c) => sum + c.affectedInstances, 0);
|
|
268
|
+
if (totalInstances > 0) {
|
|
269
|
+
sections.push(`| Affected Instances | ~${totalInstances} |`);
|
|
270
|
+
}
|
|
271
|
+
if (ia.affectedFiles.length > 0) {
|
|
272
|
+
sections.push(`| Affected Files | ${ia.affectedFiles.length} |`);
|
|
273
|
+
}
|
|
274
|
+
const depTokens = ia.affectedTokens.reduce((sum, t) => sum + t.dependentTokens.length, 0);
|
|
275
|
+
if (depTokens > 0) {
|
|
276
|
+
sections.push(`| Dependent Tokens | ${depTokens} |`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// Quality Gate
|
|
280
|
+
if (opts.includeQualityGate) {
|
|
281
|
+
const checks = changeSet.checksSummary;
|
|
282
|
+
sections.push("");
|
|
283
|
+
sections.push("---");
|
|
284
|
+
sections.push("");
|
|
285
|
+
sections.push("### Quality Gate");
|
|
286
|
+
sections.push("");
|
|
287
|
+
sections.push(`**Passed**: ${checks.passed} | **Warnings**: ${checks.warnings} | **Errors**: ${checks.errors}`);
|
|
288
|
+
if (checks.checks.length > 0) {
|
|
289
|
+
sections.push("");
|
|
290
|
+
sections.push("| Check | Result |");
|
|
291
|
+
sections.push("|-------|--------|");
|
|
292
|
+
for (const check of checks.checks) {
|
|
293
|
+
const icon = check.severity === "error"
|
|
294
|
+
? "❌"
|
|
295
|
+
: check.severity === "warning"
|
|
296
|
+
? "⚠️"
|
|
297
|
+
: "✅";
|
|
298
|
+
const target = check.target ? ` (\`${check.target}\`)` : "";
|
|
299
|
+
sections.push(`| ${check.message}${target} | ${icon} |`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
// Changes Detail
|
|
304
|
+
const items = gateResult.classification.items;
|
|
305
|
+
if (items.length > 0) {
|
|
306
|
+
sections.push("");
|
|
307
|
+
sections.push("---");
|
|
308
|
+
sections.push("");
|
|
309
|
+
sections.push("### Changes Detail");
|
|
310
|
+
sections.push("");
|
|
311
|
+
const shown = items.slice(0, opts.maxItemsInBody);
|
|
312
|
+
const lines = [];
|
|
313
|
+
lines.push(`<details>\n<summary>${items.length} items changed (click to expand)</summary>`);
|
|
314
|
+
lines.push("");
|
|
315
|
+
for (const item of shown) {
|
|
316
|
+
lines.push(`#### ${item.itemId}`);
|
|
317
|
+
lines.push(`- **Classification**: ${item.classification}`);
|
|
318
|
+
if (item.reasons.length > 0) {
|
|
319
|
+
lines.push(`- **Reason**: ${item.reasons.join(", ")}`);
|
|
320
|
+
}
|
|
321
|
+
lines.push("");
|
|
322
|
+
}
|
|
323
|
+
if (items.length > opts.maxItemsInBody) {
|
|
324
|
+
lines.push(`_...and ${items.length - opts.maxItemsInBody} more items. See full report._`);
|
|
325
|
+
lines.push("");
|
|
326
|
+
}
|
|
327
|
+
lines.push("</details>");
|
|
328
|
+
sections.push(lines.join("\n"));
|
|
329
|
+
}
|
|
330
|
+
// Action Items
|
|
331
|
+
if (opts.includeActionItems) {
|
|
332
|
+
const actionItems = buildGateActionItems(gateResult);
|
|
333
|
+
if (actionItems.length > 0) {
|
|
334
|
+
sections.push("");
|
|
335
|
+
sections.push("---");
|
|
336
|
+
sections.push("");
|
|
337
|
+
sections.push("### Action Items");
|
|
338
|
+
sections.push("");
|
|
339
|
+
for (const ai of actionItems) {
|
|
340
|
+
sections.push(`- [ ] ${ai}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
// Preview
|
|
345
|
+
if (opts.previewUrl) {
|
|
346
|
+
sections.push("");
|
|
347
|
+
sections.push("---");
|
|
348
|
+
sections.push("");
|
|
349
|
+
sections.push("### Preview");
|
|
350
|
+
sections.push("");
|
|
351
|
+
sections.push(`[View in Storybook](${opts.previewUrl})`);
|
|
352
|
+
}
|
|
353
|
+
// Footer
|
|
354
|
+
sections.push("");
|
|
355
|
+
sections.push("---");
|
|
356
|
+
sections.push("");
|
|
357
|
+
if (opts.linkToFullReport) {
|
|
358
|
+
sections.push("<sub>Full report: `.stil/reports/` | Generated by [Stil Gate](https://github.com/techang/stil-systems)</sub>");
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
sections.push("<sub>Generated by [Stil Gate](https://github.com/techang/stil-systems)</sub>");
|
|
362
|
+
}
|
|
363
|
+
return sections.join("\n");
|
|
364
|
+
}
|
|
365
|
+
function buildGateActionItems(gateResult) {
|
|
366
|
+
const items = [];
|
|
367
|
+
const { classification } = gateResult;
|
|
368
|
+
if (classification.summary.blockedCount > 0) {
|
|
369
|
+
items.push("Resolve blocked items before merging");
|
|
370
|
+
}
|
|
371
|
+
if (classification.summary.reviewCount > 0) {
|
|
372
|
+
items.push("Review and approve items marked for review");
|
|
373
|
+
}
|
|
374
|
+
for (const ci of classification.items) {
|
|
375
|
+
for (const fix of ci.suggestedFixes) {
|
|
376
|
+
items.push(`${ci.itemId}: ${fix}`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return items;
|
|
380
|
+
}
|
|
381
|
+
// ========================================
|
|
382
|
+
// PR Title
|
|
383
|
+
// ========================================
|
|
384
|
+
/**
|
|
385
|
+
* PR title を生成。
|
|
386
|
+
*
|
|
387
|
+
* Format: "[STIL {LANE}] {summary} ({classification})"
|
|
388
|
+
* Max 72 chars, truncated if necessary.
|
|
389
|
+
*/
|
|
390
|
+
function generatePRTitle(changeSet, gateResult) {
|
|
391
|
+
const lane = changeSet.lane.toUpperCase();
|
|
392
|
+
const cls = gateResult.classification.summary;
|
|
393
|
+
// Build classification suffix
|
|
394
|
+
const parts = [];
|
|
395
|
+
if (cls.safeCount > 0)
|
|
396
|
+
parts.push(`${cls.safeCount} safe`);
|
|
397
|
+
if (cls.reviewCount > 0)
|
|
398
|
+
parts.push(`${cls.reviewCount} review`);
|
|
399
|
+
if (cls.blockedCount > 0)
|
|
400
|
+
parts.push(`${cls.blockedCount} blocked`);
|
|
401
|
+
const clsSuffix = parts.length > 0 ? ` (${parts.join(", ")})` : "";
|
|
402
|
+
// Summary from items
|
|
403
|
+
const itemCount = gateResult.classification.items.length;
|
|
404
|
+
let summary;
|
|
405
|
+
if (itemCount === 1) {
|
|
406
|
+
summary = gateResult.classification.items[0].itemId;
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
summary = `Update ${itemCount} items`;
|
|
410
|
+
}
|
|
411
|
+
// Prefix
|
|
412
|
+
const prefix = cls.blockedCount > 0 ? `[STIL ${lane}] ` : `[STIL ${lane}] `;
|
|
413
|
+
const blockedPrefix = cls.blockedCount > 0 ? "Warning: " : "";
|
|
414
|
+
let title = `${prefix}${blockedPrefix}${summary}${clsSuffix}`;
|
|
415
|
+
// Truncate to 72 chars
|
|
416
|
+
if (title.length > 72) {
|
|
417
|
+
title = title.slice(0, 69) + "...";
|
|
418
|
+
}
|
|
419
|
+
return title;
|
|
420
|
+
}
|
|
421
|
+
//# sourceMappingURL=pr-body.js.map
|