sparkle-design-cli 2.0.1 → 2.0.4
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 +11 -23
- package/bin/sparkle-design.js +15 -38
- package/lib/anti-pattern-rules.js +56 -0
- package/lib/setup.js +23 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ setup は次を自動で行います:
|
|
|
17
17
|
3. 未作成の場合のみ初期ファイルを生成:
|
|
18
18
|
- `sparkle.config.json`(デフォルトは blue / Inter / JetBrains Mono / md)
|
|
19
19
|
- `postcss.config.mjs`
|
|
20
|
-
- `src/app/globals.css
|
|
20
|
+
- Tailwind エントリ CSS — Next.js App Router なら `src/app/globals.css`、Vite なら `src/index.css`、それ以外は `src/globals.css`(プロジェクト構成から自動判定)
|
|
21
21
|
4. AI 指示ファイル(`CLAUDE.md` / `AGENTS.md` / Cursor rules)に Sparkle Design Guard ブロックを追加
|
|
22
22
|
5. `sparkle-design-cli generate` を実行し、`sparkle-design.css` と `SparkleHead.tsx` を生成
|
|
23
23
|
|
|
@@ -49,11 +49,6 @@ npx sparkle-design-cli check src --strict
|
|
|
49
49
|
npx sparkle-design-cli check src --format json
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
### 後方互換モード
|
|
53
|
-
|
|
54
|
-
既存の `npx sparkle-design-cli` も引き続き動作し、内部的には `generate` として扱われます。
|
|
55
|
-
ただし後方互換のための動作なので、新規利用やドキュメント上の案内では `sparkle-design-cli generate` を使用してください。
|
|
56
|
-
|
|
57
52
|
### generate: 基本的な使用方法
|
|
58
53
|
|
|
59
54
|
1. プロジェクトのルートディレクトリに `sparkle.config.json` ファイルを作成または配置します:
|
|
@@ -73,12 +68,6 @@ npx sparkle-design-cli check src --format json
|
|
|
73
68
|
npx sparkle-design-cli generate
|
|
74
69
|
```
|
|
75
70
|
|
|
76
|
-
既存プロジェクトでサブコマンドなしの呼び出しを使っている場合も、後方互換として次の実行方法を継続利用できます:
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
npx sparkle-design-cli
|
|
80
|
-
```
|
|
81
|
-
|
|
82
71
|
3. `src/app/sparkle-design.css` に CSS ファイルが生成されます。
|
|
83
72
|
|
|
84
73
|
### generate: コマンドオプション
|
|
@@ -142,6 +131,7 @@ sparkle-design-cli check --help
|
|
|
142
131
|
- disabled を isDisabled の代わりに使わない
|
|
143
132
|
- Button の prefixIcon に JSX を渡さない
|
|
144
133
|
- Icon の children にテキストを渡さない
|
|
134
|
+
- クリック可能な Card を `<button>` / `<a>` / `role="button"` でラップせず、`ClickableCard` を使う
|
|
145
135
|
|
|
146
136
|
#### Manual Review Reminders
|
|
147
137
|
|
|
@@ -170,7 +160,7 @@ AI エージェントや CI からこの `lint:sparkle` を呼ぶ運用にする
|
|
|
170
160
|
|
|
171
161
|
1. **パッケージマネージャーの検出**(pnpm / yarn / bun / npm)
|
|
172
162
|
2. **パッケージのインストール** — `sparkle-design`(dependencies)、`tailwindcss` + `@tailwindcss/postcss`(devDependencies)
|
|
173
|
-
3. **初期ファイルの生成** — `sparkle.config.json` / `postcss.config.mjs` / `globals.css`
|
|
163
|
+
3. **初期ファイルの生成** — `sparkle.config.json` / `postcss.config.mjs` / Tailwind エントリ CSS(Next.js は `src/app/globals.css`、Vite は `src/index.css`)が無い場合のみ作成
|
|
174
164
|
4. **AI ガード設定** — `CLAUDE.md` などに Sparkle Design Guard ブロックと `lint:sparkle` script を追加
|
|
175
165
|
5. **`generate` の実行** — `sparkle-design.css` と `SparkleHead.tsx` を生成
|
|
176
166
|
|
|
@@ -189,7 +179,7 @@ npx --yes sparkle-design-cli setup --assistant claude --skip-install --skip-scaf
|
|
|
189
179
|
```
|
|
190
180
|
|
|
191
181
|
既存ファイルは上書きされません:
|
|
192
|
-
- `sparkle.config.json` / `postcss.config.*` /
|
|
182
|
+
- `sparkle.config.json` / `postcss.config.*` / Tailwind エントリ CSS が存在する場合はそのまま保持
|
|
193
183
|
- 既に依存関係に入っているパッケージはインストールをスキップ
|
|
194
184
|
- `package.json` の既存の `lint:sparkle*` 独自 script は保持(`--force-script-update` で上書き)
|
|
195
185
|
|
|
@@ -278,11 +268,11 @@ Figma プラグインが出力する基本4項目に加えて、プロジェク
|
|
|
278
268
|
|
|
279
269
|
#### extend.source-packages
|
|
280
270
|
|
|
281
|
-
`sparkle-design` を npm
|
|
271
|
+
`sparkle-design` を npm パッケージとして利用する場合に必須。Tailwind エントリ CSS(自動検出)に `@source` ディレクティブを自動挿入します。`sparkle-design` は常にデフォルトで含まれます。
|
|
282
272
|
|
|
283
273
|
#### extend.custom-css
|
|
284
274
|
|
|
285
|
-
プロジェクト固有のカスタムトークンの CSS
|
|
275
|
+
プロジェクト固有のカスタムトークンの CSS ファイルパス。Tailwind エントリ CSS に `@import` が自動挿入されます。`sparkle-design.css` に直接追加すると `generate` 実行時に上書きされるため、必ず別ファイルに分離してください。
|
|
286
276
|
|
|
287
277
|
## 出力
|
|
288
278
|
|
|
@@ -297,7 +287,7 @@ Figma プラグインが出力する基本4項目に加えて、プロジェク
|
|
|
297
287
|
CLI は自動的にフォント管理を行います:
|
|
298
288
|
|
|
299
289
|
1. **フォント検出**: `sparkle-design.css` から Google Fonts の `@import` 文を検出
|
|
300
|
-
2.
|
|
290
|
+
2. **エントリ CSS への移動**: フォントの `@import` を Tailwind エントリ CSS(`globals.css` / `index.css` 等、自動検出)の先頭に移動
|
|
301
291
|
3. **sparkle-design.css から削除**: 元のファイルからフォント `@import` を削除
|
|
302
292
|
|
|
303
293
|
### なぜこの処理が必要なのか?
|
|
@@ -312,15 +302,15 @@ CSS の仕様では、`@import` 文は `@charset` と `@layer` 以外のすべ
|
|
|
312
302
|
|
|
313
303
|
### 動作条件
|
|
314
304
|
|
|
315
|
-
- `
|
|
316
|
-
-
|
|
305
|
+
- Tailwind エントリ CSS(`@import "tailwindcss"` を含む CSS ファイル)が `sparkle-design.css` と同じディレクトリに存在する場合のみ実行されます(ファイル名に依存しません)
|
|
306
|
+
- エントリ CSS が存在しない場合は、フォント管理処理はスキップされます
|
|
317
307
|
|
|
318
308
|
### 出力例
|
|
319
309
|
|
|
320
310
|
```text
|
|
321
311
|
📦 フォント管理処理を開始します...
|
|
322
312
|
📝 3個のフォントimportを検出しました
|
|
323
|
-
✅
|
|
313
|
+
✅ エントリ CSS にフォントimportを追加しました
|
|
324
314
|
✅ sparkle-design.css からフォントimportを削除しました
|
|
325
315
|
```
|
|
326
316
|
|
|
@@ -364,10 +354,8 @@ npm run format:check
|
|
|
364
354
|
# CSS生成
|
|
365
355
|
sparkle-design-cli generate
|
|
366
356
|
|
|
367
|
-
# 既存呼び出しの後方互換モード
|
|
368
|
-
sparkle-design-cli
|
|
369
|
-
|
|
370
357
|
# ヘルプ表示
|
|
358
|
+
sparkle-design-cli --help
|
|
371
359
|
sparkle-design-cli generate --help
|
|
372
360
|
|
|
373
361
|
# 検査
|
package/bin/sparkle-design.js
CHANGED
|
@@ -133,11 +133,6 @@ Commands:
|
|
|
133
133
|
check Sparkle Design のアンチパターンを検査
|
|
134
134
|
setup Sparkle Design プロジェクトをセットアップ(パッケージ導入 + 初期ファイル + AI ガード + generate)
|
|
135
135
|
|
|
136
|
-
後方互換:
|
|
137
|
-
sparkle-design-cli [generate options]
|
|
138
|
-
サブコマンドなしの実行は現在も generate として動作しますが、
|
|
139
|
-
正式リリース時に削除予定です。今後は sparkle-design-cli generate を使ってください。
|
|
140
|
-
|
|
141
136
|
Generate:
|
|
142
137
|
sparkle-design-cli generate
|
|
143
138
|
sparkle-design-cli generate --config ./config/design.json
|
|
@@ -207,13 +202,14 @@ Setup options:
|
|
|
207
202
|
--instructions-path <path> AI 指示ファイルの出力先パス (default: アシスタント別のデフォルト)
|
|
208
203
|
--force-script-update 既存の lint:sparkle 系 script も上書きする
|
|
209
204
|
--skip-install パッケージインストール(sparkle-design, tailwindcss)をスキップ
|
|
210
|
-
--skip-scaffold 初期ファイル(sparkle.config.json, postcss.config,
|
|
205
|
+
--skip-scaffold 初期ファイル(sparkle.config.json, postcss.config, エントリ CSS)生成をスキップ
|
|
211
206
|
--skip-generate generate 実行をスキップ
|
|
212
207
|
|
|
213
208
|
Setup の動作:
|
|
214
209
|
1. パッケージマネージャー検出(pnpm / npm / yarn / bun)
|
|
215
210
|
2. sparkle-design を dependencies に追加、tailwindcss + @tailwindcss/postcss を devDependencies に追加
|
|
216
|
-
3. sparkle.config.json / postcss.config.mjs /
|
|
211
|
+
3. sparkle.config.json / postcss.config.mjs / Tailwind エントリ CSS(Next.js は
|
|
212
|
+
src/app/globals.css、Vite は src/index.css 等)が無ければ初期ファイルを生成
|
|
217
213
|
4. AI アシスタントのガードブロックを指定ファイルに挿入(存在しなければ新規作成)
|
|
218
214
|
5. sparkle-design-cli generate を実行し、sparkle-design.css と SparkleHead.tsx を生成
|
|
219
215
|
|
|
@@ -231,7 +227,8 @@ Check で検出する主なパターン:
|
|
|
231
227
|
- disabled を isDisabled の代わりに使っている
|
|
232
228
|
- Button の prefixIcon に JSX を渡している
|
|
233
229
|
- Icon の children にテキストを渡している
|
|
234
|
-
-
|
|
230
|
+
- <Card> を <button> / <a> / role="button" でラップしている(ClickableCard を使う)
|
|
231
|
+
- エントリ CSS にフォント @import が残っている(SparkleHead への移行が必要)
|
|
235
232
|
- CSP ヘッダーで Google Fonts がブロックされている可能性
|
|
236
233
|
|
|
237
234
|
JSON 出力では manualReviewReminders も返すため、AI から実行する場合は --format json を推奨
|
|
@@ -242,28 +239,16 @@ generate で生成されるファイル:
|
|
|
242
239
|
`);
|
|
243
240
|
}
|
|
244
241
|
|
|
245
|
-
function showGenerateDeprecationNotice() {
|
|
246
|
-
console.warn(
|
|
247
|
-
'[sparkle-design-cli] Running without a subcommand defaults to `generate`. ' +
|
|
248
|
-
'Use `sparkle-design-cli generate` instead. This compatibility mode will be removed at the formal release.'
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
242
|
function main() {
|
|
253
243
|
const args = process.argv.slice(2);
|
|
254
244
|
const command = args[0];
|
|
255
245
|
|
|
256
246
|
try {
|
|
257
|
-
if (args.length === 0) {
|
|
258
|
-
showGenerateDeprecationNotice();
|
|
259
|
-
generateCSS();
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (command === '-h' || command === '--help') {
|
|
247
|
+
if (args.length === 0 || command === '-h' || command === '--help') {
|
|
264
248
|
showHelp();
|
|
265
249
|
process.exit(0);
|
|
266
250
|
}
|
|
251
|
+
|
|
267
252
|
if (command === 'generate') {
|
|
268
253
|
const options = parseGenerateOptions(args.slice(1));
|
|
269
254
|
|
|
@@ -306,22 +291,14 @@ function main() {
|
|
|
306
291
|
return;
|
|
307
292
|
}
|
|
308
293
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
generateCSS(options.configPath, options.outputPath, options.globalsPath);
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
if (!SUBCOMMANDS.has(command)) {
|
|
323
|
-
throw new Error(`Unknown command: ${command}`);
|
|
324
|
-
}
|
|
294
|
+
// 未知のコマンド: エラー終了ではなく note + help 表示にする(typo 時の迷子を防ぐ)
|
|
295
|
+
// en: Unknown command: show a short note + help instead of throwing
|
|
296
|
+
console.warn(
|
|
297
|
+
`⚠️ Unknown command: ${command}. 次のいずれかを指定してください: ${[...SUBCOMMANDS].join(', ')}`
|
|
298
|
+
);
|
|
299
|
+
console.warn('');
|
|
300
|
+
showHelp();
|
|
301
|
+
process.exit(0);
|
|
325
302
|
} catch (error) {
|
|
326
303
|
console.error('❌ エラーが発生しました:', error.message);
|
|
327
304
|
process.exit(1);
|
|
@@ -489,6 +489,62 @@ const ANTI_PATTERN_GROUPS = [
|
|
|
489
489
|
},
|
|
490
490
|
],
|
|
491
491
|
},
|
|
492
|
+
{
|
|
493
|
+
id: 'card-clickable-wrap',
|
|
494
|
+
check: {
|
|
495
|
+
description: 'クリック可能な Card は ClickableCard を使う(button / a / role="button" でラップしない)',
|
|
496
|
+
recommendation:
|
|
497
|
+
'`<Card>` を `<button>` / `<a>` / `role="button"` を持つ要素で包まず、`ClickableCard` を使ってください。`ClickableCard` が適切な role / keyboard 対応 / focus ring を担保します。',
|
|
498
|
+
// <button> / <a> / role="button" が直接 <Card> を子に持つケース
|
|
499
|
+
// en: <button> / <a> / role="button" wrapping a <Card> directly
|
|
500
|
+
pattern:
|
|
501
|
+
/<(?:button\b|a\b|[A-Za-z][A-Za-z0-9]*\b[^>]*\brole\s*=\s*["']button["'])[^>]*>\s*<Card\b/g,
|
|
502
|
+
},
|
|
503
|
+
featureSection: lines([
|
|
504
|
+
'### クリック可能な Card: ClickableCard を使う',
|
|
505
|
+
'',
|
|
506
|
+
'```tsx',
|
|
507
|
+
'// ✅ Correct',
|
|
508
|
+
'<ClickableCard onClick={handle}>',
|
|
509
|
+
' <CardHeader><CardTitle>タイトル</CardTitle></CardHeader>',
|
|
510
|
+
'</ClickableCard>',
|
|
511
|
+
'',
|
|
512
|
+
'// ❌ Wrong - <button> / <a> で Card を包まない',
|
|
513
|
+
'<button type="button" onClick={handle}>',
|
|
514
|
+
' <Card>',
|
|
515
|
+
' <CardHeader><CardTitle>タイトル</CardTitle></CardHeader>',
|
|
516
|
+
' </Card>',
|
|
517
|
+
'</button>',
|
|
518
|
+
'```',
|
|
519
|
+
'',
|
|
520
|
+
'`ClickableCard` はクリック可能な Card のパターンとして必要な `role` / キーボード操作 / focus ring を提供する。`<button>` / `<a>` / `role="button"` でラップすると、ボタンの内側に対話型要素(リンクやフォーム要素)を置いたときにネストされた interactive 要素になりアクセシビリティ違反になる。',
|
|
521
|
+
]),
|
|
522
|
+
jsdocTargets: [
|
|
523
|
+
{
|
|
524
|
+
file: 'src/components/ui/card/index.tsx',
|
|
525
|
+
targetName: 'Card',
|
|
526
|
+
section: {
|
|
527
|
+
bullets: [
|
|
528
|
+
{
|
|
529
|
+
ja: '`<Card>` を `<button>` / `<a>` / `role="button"` で包まないでください。クリック可能な Card には専用の `ClickableCard` を使ってください。',
|
|
530
|
+
en: 'Do not wrap `<Card>` with `<button>` / `<a>` / `role="button"`. Use the dedicated `ClickableCard` component for clickable cards.',
|
|
531
|
+
},
|
|
532
|
+
],
|
|
533
|
+
example: lines([
|
|
534
|
+
'// ✅ Correct',
|
|
535
|
+
'<ClickableCard onClick={handle}>',
|
|
536
|
+
' <CardHeader><CardTitle>タイトル</CardTitle></CardHeader>',
|
|
537
|
+
'</ClickableCard>',
|
|
538
|
+
'',
|
|
539
|
+
'// ❌ Wrong',
|
|
540
|
+
'<button type="button" onClick={handle}>',
|
|
541
|
+
' <Card>...</Card>',
|
|
542
|
+
'</button>',
|
|
543
|
+
]),
|
|
544
|
+
},
|
|
545
|
+
},
|
|
546
|
+
],
|
|
547
|
+
},
|
|
492
548
|
{
|
|
493
549
|
id: 'icon-scale',
|
|
494
550
|
featureSection: lines([
|
package/lib/setup.js
CHANGED
|
@@ -309,12 +309,33 @@ function ensurePackagesInstalled(cwd, packageManager, candidates, { dev = false,
|
|
|
309
309
|
return { ran: true, reason: 'installed', packages: missing, packageManager, dev };
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
+
// Vite の設定ファイル候補(プロジェクト種別の判定に使う)
|
|
313
|
+
// en: Vite config files used to detect a Vite project
|
|
314
|
+
const VITE_CONFIG_FILES = [
|
|
315
|
+
'vite.config.ts',
|
|
316
|
+
'vite.config.js',
|
|
317
|
+
'vite.config.mjs',
|
|
318
|
+
'vite.config.cjs',
|
|
319
|
+
'vite.config.mts',
|
|
320
|
+
'vite.config.cts',
|
|
321
|
+
];
|
|
322
|
+
|
|
323
|
+
function isViteProject(cwd) {
|
|
324
|
+
return VITE_CONFIG_FILES.some((name) => fs.existsSync(path.join(cwd, name)));
|
|
325
|
+
}
|
|
326
|
+
|
|
312
327
|
/**
|
|
313
|
-
*
|
|
314
|
-
*
|
|
328
|
+
* エントリポイント CSS の新規作成先を決定する。
|
|
329
|
+
* - Next.js App Router (src/app あり) → src/app/globals.css
|
|
330
|
+
* - Vite プロジェクト → src/index.css(Vite の慣習に合わせる)
|
|
331
|
+
* - それ以外で src/ がある → src/globals.css
|
|
332
|
+
* - フォールバック → src/app/globals.css
|
|
333
|
+
* en: Pick the scaffold target for the entry CSS. Vite projects get src/index.css
|
|
334
|
+
* to match the common convention, Next.js App Router gets src/app/globals.css.
|
|
315
335
|
*/
|
|
316
336
|
function defaultGlobalsCssTarget(cwd) {
|
|
317
337
|
if (fs.existsSync(path.join(cwd, 'src/app'))) return 'src/app/globals.css';
|
|
338
|
+
if (isViteProject(cwd)) return 'src/index.css';
|
|
318
339
|
if (fs.existsSync(path.join(cwd, 'src'))) return 'src/globals.css';
|
|
319
340
|
return 'src/app/globals.css';
|
|
320
341
|
}
|