ksk-design-system 1.61.1 → 1.63.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/MIGRATION.md +78 -0
- package/README.md +100 -2
- package/bin/check-duplicates.js +120 -19
- package/bin/init.js +17 -2
- package/bin/lint.js +812 -70
- package/contracts/components.json +248 -35
- package/contracts/rules.json +55 -7
- package/contracts/token-hex-cache.json +1 -1
- package/dist/class-names.js +1 -1
- package/dist/index.js +4743 -4366
- package/dist/native/ui.js +24 -13
- package/dist/{server-variants-B3uIQjUG.js → server-variants-DTpkG8A7.js} +2 -2
- package/dist/types/components/patterns/_internal/carousel-primitives.d.ts +5 -1
- package/dist/types/components/patterns/admin/bulk-actions.d.ts +3 -1
- package/dist/types/components/patterns/admin/data-table.d.ts +8 -2
- package/dist/types/components/patterns/admin/image-uploader.d.ts +3 -1
- package/dist/types/components/patterns/admin/kebab-menu.d.ts +3 -1
- package/dist/types/components/patterns/app-header.d.ts +19 -1
- package/dist/types/components/patterns/banner-carousel.d.ts +3 -1
- package/dist/types/components/patterns/celebration.d.ts +3 -1
- package/dist/types/components/patterns/chip-selector.d.ts +80 -12
- package/dist/types/components/patterns/chip.d.ts +3 -3
- package/dist/types/components/patterns/collapsible-chip-field.d.ts +18 -1
- package/dist/types/components/patterns/commerce/bottom-tab-bar.d.ts +3 -1
- package/dist/types/components/patterns/commerce/filter-bar.d.ts +5 -1
- package/dist/types/components/patterns/commerce/image-carousel.d.ts +7 -1
- package/dist/types/components/patterns/commerce/quantity-selector.d.ts +9 -1
- package/dist/types/components/patterns/content-carousel.d.ts +5 -1
- package/dist/types/components/patterns/filter-pill.d.ts +79 -0
- package/dist/types/components/patterns/footer.d.ts +48 -12
- package/dist/types/components/patterns/image-overlay-action.d.ts +63 -0
- package/dist/types/components/patterns/list-item.d.ts +17 -1
- package/dist/types/components/patterns/mobile-floating-action-button.d.ts +9 -1
- package/dist/types/components/patterns/share-buttons.d.ts +3 -1
- package/dist/types/components/patterns/simple-pagination.d.ts +3 -1
- package/dist/types/components/patterns/tag.d.ts +48 -1
- package/dist/types/components/patterns/thumbnail-card.d.ts +79 -0
- package/dist/types/components/ui/button.d.ts +21 -1
- package/dist/types/components/ui/dropdown-filter.d.ts +3 -1
- package/dist/types/components/ui/input.d.ts +20 -1
- package/dist/types/components/ui/multi-select.d.ts +3 -1
- package/dist/types/components/ui/pagination.d.ts +5 -1
- package/dist/types/components/ui/star-rating.d.ts +15 -1
- package/dist/types/components/ui/textarea.d.ts +13 -1
- package/dist/types/components/ui/toast.d.ts +16 -3
- package/dist/types/index.d.ts +11 -3
- package/dist/types/lib/server-variants/unstyled.d.ts +21 -0
- package/dist/types/native/components/CollapsibleChipField.d.ts +6 -1
- package/package.json +19 -6
- package/src/components/COMPONENT_LOOKUP.md +12 -9
- package/src/styles/source-safelist.css +55 -0
- package/tokens.json +1 -0
package/MIGRATION.md
CHANGED
|
@@ -53,6 +53,84 @@ npx ksk-ds check-migration ./src
|
|
|
53
53
|
|
|
54
54
|
## v1 系内の minor 変更(参考)
|
|
55
55
|
|
|
56
|
+
### 次のリリース — `typescript` が必須依存から optional peer dependency になった(issue #409)
|
|
57
|
+
|
|
58
|
+
CLI の1ルール(P046)のためだけに typescript(24MB)が全 consumer の
|
|
59
|
+
node_modules に入っていたのを解消した。install サイズが減り、`ksk-ds lint` の
|
|
60
|
+
起動も速くなる(実測 655ms → 407ms)。
|
|
61
|
+
|
|
62
|
+
- **通常の TypeScript プロジェクト**: 自前で typescript を持っているので影響なし
|
|
63
|
+
- **typescript を持たないプロジェクト**: P046(card-child-spacing)だけが
|
|
64
|
+
「typescript が無いため skip」の1行案内つきでスキップされる。他の全ルールは動く
|
|
65
|
+
- DS の typescript に transitive 依存していた場合のみ、自分の devDependencies に
|
|
66
|
+
typescript を追加すること
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
### 次のリリース — `ksk-ds lint` の severity 語彙が `warn` → `warning` に統一(要確認)
|
|
70
|
+
|
|
71
|
+
`--format json` の `results[].severity` と text 出力の severity 表記が、これまで
|
|
72
|
+
`"warn"` と `"warning"` で不統一だった(contract の `contracts/rules.json` は
|
|
73
|
+
`"warning"`、CLI 出力は `"warn"`)。**CLI 出力側を `"warning"` に揃えた**(issue #406)。
|
|
74
|
+
|
|
75
|
+
CI で JSON をフィルタしている場合は置き換えが必要:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# before
|
|
79
|
+
jq '[.results[] | select(.severity == "warn")] | length'
|
|
80
|
+
# after
|
|
81
|
+
jq '[.results[] | select(.severity == "warning")] | length'
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
text 出力の要約行も `0 error / 3 warn` → `0 error / 3 warning` に変わる。
|
|
85
|
+
|
|
86
|
+
あわせて `ksk-ds lint` に次を追加した:
|
|
87
|
+
|
|
88
|
+
- `--strict` … warning があれば exit 1
|
|
89
|
+
- `--max-warnings N` … warning が N 件を超えたら exit 1
|
|
90
|
+
- **未知のオプションはエラー終了**(従来は黙って捨てられ、CI に書いた
|
|
91
|
+
`--strict` が無言で効かないまま緑になっていた)
|
|
92
|
+
|
|
93
|
+
### 次のリリース — `ksk-ds lint` の escape がルール単位・行単位で書けるようになった
|
|
94
|
+
|
|
95
|
+
ファイル全体・全ルールを無期限に外す `// ksk-ds-allow-custom-ui: 理由` は
|
|
96
|
+
後方互換で残るが**非推奨**。新規はルール単位を使う(issue #405):
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
// ksk-ds-lint-ignore P008 -- ブランドロゴの規定色のため ← 直下 2 行以内に効く
|
|
100
|
+
// ksk-ds-lint-ignore-file P008 -- ブランド定数ファイル ← そのファイル全体・P008 のみ
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
理由は **5 文字以上が必須**。空虚な理由は `ESCAPE001` / `ESCAPE002` として報告され、
|
|
104
|
+
ignore は効かない。あわせて escape マーカーは**文字列リテラルの中では発火しなくなった**
|
|
105
|
+
(ドキュメント URL に `ksk-ds-allow-custom-ui` が含まれるだけで lint 全体が黙って
|
|
106
|
+
無効化されていた)。既に short な理由を書いていたファイルは lint が復活するので、
|
|
107
|
+
理由を書き足すか違反を直すこと。
|
|
108
|
+
|
|
109
|
+
### 次のリリース — 自動生成物と DS 自身のトークン CSS を lint 対象外にした
|
|
110
|
+
|
|
111
|
+
- 先頭 12 行に自動生成マーカー(`AUTO-GENERATED` / `DO NOT EDIT` / `自動生成` 等)を
|
|
112
|
+
持つファイルは全ルールを skip する(issue #408)。直す先は生成物ではなく生成元。
|
|
113
|
+
- P049(product theme の無許可上書き)は、DS 自身 / ベンダリングされた DS の
|
|
114
|
+
トークン定義 CSS を対象外にする(issue #407)。`vendor/ksk-design-system/**` のように
|
|
115
|
+
DS を再梱包しているリポで P049 が大量に出ていたのが解消する。
|
|
116
|
+
|
|
117
|
+
### 次のリリース — `Footer` の `paymentIcons` 既定値が空になった(挙動変更)
|
|
118
|
+
|
|
119
|
+
`Footer` は `paymentIcons` を省略すると `["VISA", "Master", "JCB", "AmEx", "PayPay", "LINE Pay"]`
|
|
120
|
+
の決済バッジを既定で描画していた。EC 以外のプロダクトでも「取り扱っていない決済手段」が
|
|
121
|
+
出てしまうため、**既定値を `[]`(非表示)に変更**した。
|
|
122
|
+
|
|
123
|
+
従来の表示を維持したい場合は明示的に渡す:
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
<Footer
|
|
127
|
+
paymentIcons={["VISA", "Master", "JCB", "AmEx", "PayPay", "LINE Pay"]}
|
|
128
|
+
copyright="© 2026 KSK Inc."
|
|
129
|
+
/>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`paymentIcons` を明示的に渡していた箇所は影響なし。
|
|
133
|
+
|
|
56
134
|
### 次のリリース — `postinstall` による AI ルールファイル自動設置を廃止(要確認)
|
|
57
135
|
|
|
58
136
|
これまで `npm install ksk-design-system` の `postinstall` フックが、消費側の
|
package/README.md
CHANGED
|
@@ -87,6 +87,50 @@ import { Button, Card, Input, FormField } from "ksk-design-system"
|
|
|
87
87
|
|
|
88
88
|
新規クライアント案件では、テーマファイルで `--Primitive-Brand-500` などブランドカラーの 10 行を定義するだけで、全コンポーネントがそのブランドカラーで動作します。
|
|
89
89
|
|
|
90
|
+
### 段階移行レシピ(既存 CSS を保ったまま DS 化する)
|
|
91
|
+
|
|
92
|
+
既存の手書き CSS で作られた画面を DS へ移すときは、**見た目を後回しにして挙動と
|
|
93
|
+
アクセシビリティだけ先に統一する**のが最も安全です。生タグ(`<button>` / `<input>` /
|
|
94
|
+
`<textarea>`)を DS コンポーネントへ置き換え、既存クラスを `className` へ渡し、
|
|
95
|
+
`unstyled` を付けます。
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
// before — 生タグ + 手書き CSS
|
|
99
|
+
<button className="btn btn-primary" onClick={submit}>ログイン</button>
|
|
100
|
+
<textarea className="composer-input" value={text} onChange={onChange} />
|
|
101
|
+
|
|
102
|
+
// after — 挙動と a11y は DS、見た目は既存 CSS のまま
|
|
103
|
+
<Button unstyled className="btn btn-primary" onClick={submit}>ログイン</Button>
|
|
104
|
+
<Textarea unstyled className="composer-input" value={text} onChange={onChange} />
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**なぜ `unstyled` が要るのか。** 手書き CSS は非レイヤー、DS のユーティリティは
|
|
108
|
+
`@layer utilities` なので、宣言が衝突すれば手書き側が勝ちます。しかし手書きクラスの
|
|
109
|
+
大半は height / white-space / font-weight / border-radius / background を宣言して
|
|
110
|
+
おらず、**衝突しないプロパティは DS の base + variant がそのまま効きます**。
|
|
111
|
+
`className` を渡すだけでは見た目は保てません。aikoibito web の実測では、
|
|
112
|
+
チャット入力欄が `--Field-Min-Height: 5rem` の流入で **43px → 80px** に、
|
|
113
|
+
ログイン CTA が **52px → 40px** に変わりました(issue #420)。
|
|
114
|
+
|
|
115
|
+
`unstyled` を付けたときに DS が出すのは次だけです。
|
|
116
|
+
|
|
117
|
+
| コンポーネント | 維持されるもの | 出さないもの |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| `Button` | `type` 既定 `"button"` / disabled・aria-disabled クリックの抑止 / `haptic` / `asChild` | `inline-flex` `items-center` `justify-center` `gap-*` `whitespace-nowrap` `typo-*` `cursor-pointer` と variant・size・layout の全クラス |
|
|
120
|
+
| `Input` | `showCount`(IME 追従)/ adornment の配置土台 | `h-*` `w-full` `border-*` `bg-*` `px-*` `typo-*` `placeholder:*` |
|
|
121
|
+
| `Textarea` | `autoGrow` / `showCount` | `min-h-*` `w-full` `border-*` `bg-*` `px-*` `py-*` `typo-*` `placeholder:*` |
|
|
122
|
+
|
|
123
|
+
キーボード操作時の `focus-visible` リングだけは a11y のため既定で残ります。手書き CSS が
|
|
124
|
+
自前のフォーカス表現を持つ場合は `className="focus-visible:ring-0"` で消せます。
|
|
125
|
+
|
|
126
|
+
移行の順番は次のとおりです。
|
|
127
|
+
|
|
128
|
+
1. 生タグを DS コンポーネント + `unstyled` + 既存クラスへ置き換える(見た目は 1px も変わらない)
|
|
129
|
+
2. この状態で `npx ksk-ds lint src` の P001〜P006 を通す
|
|
130
|
+
3. 画面ごとに、既存クラスを DS のトークン・variant へ置き換えて `unstyled` を外す
|
|
131
|
+
|
|
132
|
+
`unstyled` は移行の足場であって最終形ではありません。3 が済んだ画面から順に外してください。
|
|
133
|
+
|
|
90
134
|
### Consumer lint
|
|
91
135
|
|
|
92
136
|
consumer 側のローカル grep script が古くならないよう、DS 本体から `contracts/rules.json` を読む lint CLI を同梱しています。
|
|
@@ -95,14 +139,68 @@ consumer 側のローカル grep script が古くならないよう、DS 本体
|
|
|
95
139
|
npx ksk-ds lint src
|
|
96
140
|
npx ksk-ds lint src --format json
|
|
97
141
|
npx ksk-ds lint --changed
|
|
142
|
+
npx ksk-ds lint src --platform native
|
|
143
|
+
npx ksk-ds lint src --strict # warning も exit 1
|
|
144
|
+
npx ksk-ds lint src --max-warnings=0 # warning が N 件を超えたら exit 1
|
|
98
145
|
```
|
|
99
146
|
|
|
100
|
-
|
|
147
|
+
既定では `severity: "error"` のルールだけが exit code に影響します。a11y 系を含む
|
|
148
|
+
`warning` ルールも CI で締めたい場合は `--strict` か `--max-warnings N` を使ってください。
|
|
149
|
+
**未知のオプションはエラー終了**します(黙って捨てないので、CI に書いたフラグが
|
|
150
|
+
無言で効かないことはありません)。
|
|
151
|
+
|
|
152
|
+
ルールはファイルごとに **capability(そのファイルがどの記法を持ちうるか)** を判定して
|
|
153
|
+
出し分けます(`contracts/rules.json` の `appliesTo`)。
|
|
154
|
+
|
|
155
|
+
| ファイル | capability | 当たるルール |
|
|
156
|
+
|---|---|---|
|
|
157
|
+
| web(native シグナルなし) | `dom` `tailwind` `web` | 全部 |
|
|
158
|
+
| React Native + `className` あり(**NativeWind**) | `native` `tailwind` | Tailwind 系は当たる/DOM 生タグ系と CSS 変数系は外れる |
|
|
159
|
+
| React Native + `className` なし(**StyleSheet**) | `native` | Tailwind 系も外れる |
|
|
160
|
+
|
|
161
|
+
native の判定シグナルは `*.native.tsx` というファイル名、`react-native` /
|
|
162
|
+
`ksk-design-system/native` の **import 文**、`StyleSheet.create` の使用です。
|
|
163
|
+
自動判定が合わない場合は `--platform web` / `--platform native` で上書きできます
|
|
164
|
+
(ファイル判定より優先)。ただし `--platform native` を付けても Tailwind を持つかどうかは
|
|
165
|
+
ソースの `className` の有無で決まるので、NativeWind の consumer が Tailwind 系ルールを
|
|
166
|
+
失うことはありません。
|
|
167
|
+
|
|
168
|
+
`appliesTo` を持たないルール(`#hex` 直書きの P008 等)は従来どおり全ファイルに適用され、
|
|
169
|
+
native では RN 向けの fix 文言を表示します。
|
|
170
|
+
|
|
171
|
+
出力は `file:line rule severity fix` を含みます。severity の語彙は `error` / `warning` で、
|
|
172
|
+
`contracts/rules.json` と JSON 出力・text 出力すべてで一致しています。
|
|
173
|
+
|
|
174
|
+
#### 例外(escape)
|
|
175
|
+
|
|
176
|
+
どうしても DS で表現できない箇所は、**ルール単位・理由付き**の escape コメントを置きます
|
|
177
|
+
(理由は 5 文字以上が必須。空虚な理由は `ESCAPE002` として報告され、ignore は効きません)。
|
|
101
178
|
|
|
102
179
|
```tsx
|
|
103
|
-
// ksk-ds-
|
|
180
|
+
// ksk-ds-lint-ignore P008 -- ブランドロゴの規定色のため
|
|
181
|
+
const BRAND = "#06C755"
|
|
104
182
|
```
|
|
105
183
|
|
|
184
|
+
| 書き方 | スコープ |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `// ksk-ds-lint-ignore P008 -- 理由` | そのコメント行から**下 2 行以内**・指定ルールのみ |
|
|
187
|
+
| `// ksk-ds-lint-ignore-file P008 -- 理由` | **そのファイル全体**・指定ルールのみ |
|
|
188
|
+
| `// ksk-ds-allow-custom-ui: 理由` | そのファイル全体・**全ルール**(非推奨・後方互換) |
|
|
189
|
+
|
|
190
|
+
`ksk-ds-allow-custom-ui` はスコープが広すぎて将来の違反まで隠すため、新規では使わず
|
|
191
|
+
ルール単位の `ksk-ds-lint-ignore` を使ってください。escape マーカーは**コメントの中でのみ**
|
|
192
|
+
有効で、文字列リテラル(ドキュメント URL 等)の中では発火しません。
|
|
193
|
+
理由は 5 文字以上が必須です。また理由文字列に `*` は使えません
|
|
194
|
+
(`*/` を誤って飲み込まないための制約で、`*` 以降は理由として認識されません)。
|
|
195
|
+
|
|
196
|
+
#### lint されないファイル
|
|
197
|
+
|
|
198
|
+
- 先頭 12 行に自動生成マーカー(`AUTO-GENERATED` / `DO NOT EDIT` / `自動生成` 等)を持つ
|
|
199
|
+
ファイルは全ルールを skip します。直す先は生成物ではなく生成元です。
|
|
200
|
+
- DS 自身のトークン定義 CSS(`src/styles/*.css` / `src/themes/*.css` / `src/preset.css` と、
|
|
201
|
+
それを再梱包したベンダリングコピー)は P049 の対象外です。トークンを**定義**している側で
|
|
202
|
+
あって、consumer による上書きではないためです。
|
|
203
|
+
|
|
106
204
|
### Jest(CommonJS)でコンポーネントをテストする
|
|
107
205
|
|
|
108
206
|
このパッケージは **ESM-only** です。CJS との dual build は配布せず、Jest
|
package/bin/check-duplicates.js
CHANGED
|
@@ -36,11 +36,15 @@ export function runCheckDuplicatesCli(
|
|
|
36
36
|
|
|
37
37
|
const registry = loadComponentRegistry(contractsPath)
|
|
38
38
|
const files = collectSourceFiles(target)
|
|
39
|
-
const
|
|
40
|
-
|
|
39
|
+
const results = files.map((file) => findDuplicateExports(file, cwd, registry))
|
|
40
|
+
const findings = results
|
|
41
|
+
.flatMap((result) => result.findings)
|
|
41
42
|
.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.name.localeCompare(b.name))
|
|
43
|
+
const wrapperExclusions = results
|
|
44
|
+
.flatMap((result) => result.wrapperExclusions)
|
|
45
|
+
.sort((a, b) => a.file.localeCompare(b.file) || a.name.localeCompare(b.name))
|
|
42
46
|
|
|
43
|
-
printFindings(findings, files.length, options.strict)
|
|
47
|
+
printFindings(findings, wrapperExclusions, files.length, options.strict)
|
|
44
48
|
return options.strict && findings.length > 0 ? 1 : 0
|
|
45
49
|
}
|
|
46
50
|
|
|
@@ -117,10 +121,83 @@ function collectSourceFiles(target) {
|
|
|
117
121
|
return files.sort()
|
|
118
122
|
}
|
|
119
123
|
|
|
124
|
+
// ksk-design-system 本体(サブパス含む)からの named import/re-export かどうか。
|
|
125
|
+
// 例: "ksk-design-system", "ksk-design-system/native", "ksk-design-system/native/ui"
|
|
126
|
+
function isDsModuleSpecifier(moduleSpecifier) {
|
|
127
|
+
return moduleSpecifier === "ksk-design-system" || moduleSpecifier.startsWith("ksk-design-system/")
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// `{ A, B as C, type D }` のような specifier リストを
|
|
131
|
+
// [{ sourceName: "A", localName: "A" }, { sourceName: "B", localName: "C" }, ...] に分解する。
|
|
132
|
+
// エイリアス判定は「import 指定子の imported 名(sourceName)」を用いる。ローカル名(localName)は問わない。
|
|
133
|
+
function parseSpecifierList(specifierList) {
|
|
134
|
+
return specifierList
|
|
135
|
+
.split(",")
|
|
136
|
+
.map((spec) => spec.trim())
|
|
137
|
+
.filter((spec) => spec.length > 0)
|
|
138
|
+
.map((spec) => {
|
|
139
|
+
const withoutTypeModifier = spec.replace(/^type\s+/, "")
|
|
140
|
+
const [sourceName, localName] = withoutTypeModifier.split(/\s+as\s+/).map((part) => part.trim())
|
|
141
|
+
return { sourceName, localName: localName ?? sourceName }
|
|
142
|
+
})
|
|
143
|
+
.filter((entry) => entry.sourceName)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// import/re-export 検出はコメントを除外してから行う(JSDoc の使用例コメント
|
|
147
|
+
// `* import { X } from "ksk-design-system"` を実コードの import と誤認しないため)。
|
|
148
|
+
// 文字列リテラル内の `/* `等までは考慮しないが、このファイル全体の regex ベース方針と
|
|
149
|
+
// 整合する範囲の簡易対応とする。
|
|
150
|
+
function stripComments(source) {
|
|
151
|
+
return source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "")
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// このファイルが ksk-design-system から named import している「imported 名(sourceName)」の集合。
|
|
155
|
+
// `import { EmptyState as DSEmptyState } from 'ksk-design-system/native/ui'` なら "EmptyState"。
|
|
156
|
+
// ローカルの alias 名(DSEmptyState)ではなく、パッケージ側の元の名前で判定する。
|
|
157
|
+
function collectDsImportSourceNames(source) {
|
|
158
|
+
const names = new Set()
|
|
159
|
+
const importPattern = /import\s*\{([^}]*)\}\s*from\s*["']([^"']+)["']/g
|
|
160
|
+
|
|
161
|
+
for (const match of source.matchAll(importPattern)) {
|
|
162
|
+
const [, specifierList, moduleSpecifier] = match
|
|
163
|
+
if (!isDsModuleSpecifier(moduleSpecifier)) continue
|
|
164
|
+
for (const { sourceName } of parseSpecifierList(specifierList)) {
|
|
165
|
+
names.add(sourceName)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return names
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// `export { X as Y } from 'ksk-design-system/...'` のような re-export 形式。
|
|
173
|
+
// これは常に DS の実装をそのまま右から左へ渡すだけなので、localName が
|
|
174
|
+
// registry と一致する場合は無条件でラッパー(除外対象)として扱う。
|
|
175
|
+
function collectDsReExports(source) {
|
|
176
|
+
const reExports = []
|
|
177
|
+
const reExportPattern = /export\s*\{([^}]*)\}\s*from\s*["']([^"']+)["']/g
|
|
178
|
+
|
|
179
|
+
for (const match of source.matchAll(reExportPattern)) {
|
|
180
|
+
const [, specifierList, moduleSpecifier] = match
|
|
181
|
+
if (!isDsModuleSpecifier(moduleSpecifier)) continue
|
|
182
|
+
for (const { localName } of parseSpecifierList(specifierList)) {
|
|
183
|
+
reExports.push(localName)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return reExports
|
|
188
|
+
}
|
|
189
|
+
|
|
120
190
|
function findDuplicateExports(file, cwd, registry) {
|
|
121
191
|
const source = readFileSync(file, "utf8")
|
|
122
192
|
const lines = source.split(/\r?\n/)
|
|
123
193
|
const findings = []
|
|
194
|
+
const wrapperExclusions = []
|
|
195
|
+
const relFile = normalize(relative(cwd, file))
|
|
196
|
+
|
|
197
|
+
const codeForImportScan = stripComments(source)
|
|
198
|
+
const dsImportSourceNames = collectDsImportSourceNames(codeForImportScan)
|
|
199
|
+
const dsReExportLocalNames = collectDsReExports(codeForImportScan)
|
|
200
|
+
|
|
124
201
|
const declarationPattern =
|
|
125
202
|
/^\s*export\s+(?:default\s+)?(?:(?:async|declare)\s+)?(?:function|const)\s+([A-Z][A-Za-z0-9]*)\b/
|
|
126
203
|
|
|
@@ -130,37 +207,59 @@ function findDuplicateExports(file, cwd, registry) {
|
|
|
130
207
|
const name = match[1]
|
|
131
208
|
const components = registry.get(name)
|
|
132
209
|
if (!components) continue
|
|
210
|
+
|
|
211
|
+
// 同名のローカル宣言があっても、同ファイルが ksk-design-system から同名(元の
|
|
212
|
+
// export 名基準。ローカル alias は問わない)を import して使っているなら、
|
|
213
|
+
// DS 委譲ラッパーとして重複報告から除外する(DS が推奨する段階移行の型のため)。
|
|
214
|
+
if (dsImportSourceNames.has(name)) {
|
|
215
|
+
wrapperExclusions.push({ file: relFile, name })
|
|
216
|
+
continue
|
|
217
|
+
}
|
|
218
|
+
|
|
133
219
|
findings.push({
|
|
134
220
|
components,
|
|
135
|
-
file:
|
|
221
|
+
file: relFile,
|
|
136
222
|
line: index + 1,
|
|
137
223
|
name,
|
|
138
224
|
})
|
|
139
225
|
}
|
|
140
226
|
|
|
141
|
-
|
|
227
|
+
// `export { X as Y } from 'ksk-design-system/...'` 形式の re-export。
|
|
228
|
+
// registry と一致する localName だけを対象にする(DS に無い名前の re-export は無関係)。
|
|
229
|
+
for (const localName of dsReExportLocalNames) {
|
|
230
|
+
if (!registry.get(localName)) continue
|
|
231
|
+
wrapperExclusions.push({ file: relFile, name: localName })
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return { findings, wrapperExclusions }
|
|
142
235
|
}
|
|
143
236
|
|
|
144
|
-
function printFindings(findings, scannedFiles, strict) {
|
|
237
|
+
function printFindings(findings, wrapperExclusions, scannedFiles, strict) {
|
|
145
238
|
if (findings.length === 0) {
|
|
146
239
|
console.log(`ksk-ds check-duplicates: 重複候補はありません(${scannedFiles} files)`)
|
|
147
|
-
|
|
240
|
+
} else {
|
|
241
|
+
console.log(`ksk-ds check-duplicates: DS と同名のローカル実装を ${findings.length} 件検出しました`)
|
|
242
|
+
for (const finding of findings) {
|
|
243
|
+
console.log(`\n${finding.file}:${finding.line} ${finding.name}`)
|
|
244
|
+
for (const component of finding.components) {
|
|
245
|
+
console.log(` DS: ${component.tier} / ${component.path}`)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
console.log("\nローカル実装を削除して ksk-design-system から import できないか確認してください。")
|
|
249
|
+
console.log("DS に必要な機能が不足する場合は、ローカル台帳を作らず DS 側に Issue を登録してください。")
|
|
250
|
+
console.log(
|
|
251
|
+
strict
|
|
252
|
+
? "strict mode: 重複候補があるため終了コード 1"
|
|
253
|
+
: "advice mode: 終了コード 0(CI で失敗させる場合は --strict)",
|
|
254
|
+
)
|
|
148
255
|
}
|
|
149
256
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
console.log(` DS: ${component.tier} / ${component.path}`)
|
|
257
|
+
if (wrapperExclusions.length > 0) {
|
|
258
|
+
console.log(`\nℹ ラッパー(DS 委譲済み)として除外: ${wrapperExclusions.length} 件`)
|
|
259
|
+
for (const wrapper of wrapperExclusions) {
|
|
260
|
+
console.log(` ℹ ラッパー(DS 委譲済み)として除外: ${wrapper.name} (${wrapper.file})`)
|
|
155
261
|
}
|
|
156
262
|
}
|
|
157
|
-
console.log("\nローカル実装を削除して ksk-design-system から import できないか確認してください。")
|
|
158
|
-
console.log("DS に必要な機能が不足する場合は、ローカル台帳を作らず DS 側に Issue を登録してください。")
|
|
159
|
-
console.log(
|
|
160
|
-
strict
|
|
161
|
-
? "strict mode: 重複候補があるため終了コード 1"
|
|
162
|
-
: "advice mode: 終了コード 0(CI で失敗させる場合は --strict)",
|
|
163
|
-
)
|
|
164
263
|
}
|
|
165
264
|
|
|
166
265
|
function printHelp() {
|
|
@@ -176,6 +275,8 @@ DIR の既定値:
|
|
|
176
275
|
動作:
|
|
177
276
|
export function / export const の PascalCase 宣言を
|
|
178
277
|
contracts/components.json と名前の完全一致で照合します。
|
|
278
|
+
ただし、同ファイルが ksk-design-system から同名(alias 可)を import/re-export して
|
|
279
|
+
使っているものは DS 委譲ラッパーとみなし、重複報告から除外します(info 行で件数報告)。
|
|
179
280
|
既定は助言モード(常に exit 0)、--strict は検出時 exit 1 です。
|
|
180
281
|
`)
|
|
181
282
|
}
|
package/bin/init.js
CHANGED
|
@@ -82,8 +82,23 @@ if (cmd === "lint") {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
if (cmd === "check-migration") {
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
// check-migration.js は TypeScript の AST 解析そのものが本体なので、
|
|
86
|
+
// typescript 不在なら raw な ERR_MODULE_NOT_FOUND ではなく案内を出して
|
|
87
|
+
// skip する(exit 0。lint 同様に「無言で落ちる」を避ける/issue #409)。
|
|
88
|
+
let mod
|
|
89
|
+
try {
|
|
90
|
+
mod = await import("./check-migration.js")
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (error?.code === "ERR_MODULE_NOT_FOUND" && String(error.message ?? "").includes("typescript")) {
|
|
93
|
+
console.log(
|
|
94
|
+
`[ksk-ds check-migration] typescript が見つからないため skip します。` +
|
|
95
|
+
`devDependencies に typescript を追加すると検査できます。`,
|
|
96
|
+
)
|
|
97
|
+
await exitWith(0)
|
|
98
|
+
}
|
|
99
|
+
throw error
|
|
100
|
+
}
|
|
101
|
+
const status = mod.runCheckMigrationCli(args.slice(1), { cwd: process.cwd(), pkgRoot })
|
|
87
102
|
await exitWith(status)
|
|
88
103
|
}
|
|
89
104
|
|