sparkle-design-cli 2.5.0-beta.1 → 2.5.0-beta.3

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/CONTRIBUTING.md CHANGED
@@ -27,6 +27,15 @@ npm run sync:anti-pattern-docs # ルールの解説を隣接リポジトリの
27
27
  ```
28
28
 
29
29
  > **`sync:anti-pattern-docs` の注意:** ワークスペース内の隣接リポジトリ(`../sparkle-design` / `../sparkle-design-internal`)のファイルを書き換える横断スクリプトです。両リポジトリが隣にある Sparkle ワークスペース内で実行してください(worktree からは sibling が解決できず落ちます)。**このリポジトリの README は書き換えません** — ルール一覧は `rules` コマンドが出すためです。
30
+ >
31
+ > **共有チェックアウトに向けて実行しないこと。** 隣接リポジトリで誰かが作業中だと、その未コミット変更に自分の生成物が混ざります。実行後に `git checkout -- .` で戻そうとして**他人の変更まで消す事故が実際に起きました**。同期専用の worktree を切り、`SPARKLE_DESIGN_ROOT` / `SPARKLE_DESIGN_INTERNAL_ROOT` でそこを指してください。
32
+ >
33
+ > ```bash
34
+ > git -C ../sparkle-design worktree add .claude/worktrees/docs-sync -b chore/sync-anti-pattern-docs
35
+ > SPARKLE_DESIGN_ROOT=../sparkle-design/.claude/worktrees/docs-sync npm run sync:anti-pattern-docs
36
+ > ```
37
+ >
38
+ > 実行前に対象リポジトリが clean であることを `git status` で確認し、戻すときも**自分が触ったファイルだけを明示的に指定**してください(`git checkout -- <path>`)。
30
39
 
31
40
  ## リリース手順(メンテナ向け)
32
41
 
package/README.md CHANGED
@@ -73,6 +73,9 @@ npx sparkle-design-cli check src --format json
73
73
 
74
74
  # 現在有効なアンチパターンルールを一覧表示(プラグイン由来のものも含む)
75
75
  npx sparkle-design-cli rules
76
+
77
+ # Artifact Registry の npm registry 用トークンを ~/.npmrc に書く(beta)
78
+ npx --yes sparkle-design-cli@beta auth
76
79
  ```
77
80
 
78
81
  ### generate: 基本的な使用方法
@@ -94,7 +97,7 @@ npx sparkle-design-cli rules
94
97
  npx sparkle-design-cli generate
95
98
  ```
96
99
 
97
- 3. `src/app/sparkle-design.css` に CSS ファイルが生成されます。
100
+ 3. CSS ファイルが生成されます(既定は `src/app/sparkle-design.css`。`extend.globals-path` や `--globals-path` で Tailwind エントリ CSS を明示していて、それが実在する場合はそのディレクトリに出ます)。
98
101
 
99
102
  ### generate: コマンドオプション
100
103
 
@@ -125,7 +128,7 @@ sparkle-design-cli generate -c ./config/sparkle.admin.json --scope '[data-tenant
125
128
 
126
129
  - `-h, --help`: ヘルプメッセージを表示
127
130
  - `-c, --config <パス>`: 設定ファイルのパス(デフォルト: `./sparkle.config.json`)
128
- - `-o, --output <パス>`: 出力ファイルのパス(デフォルト: `./src/app/sparkle-design.css`)
131
+ - `-o, --output <パス>`: 出力ファイルのパス。未指定時は、明示された Tailwind エントリ CSS(`--globals-path` / `extend.globals-path`)が実在すればそのディレクトリ、無ければ `./src/app/sparkle-design.css`
129
132
  - `--globals-path <パス>`: Tailwind エントリポイント CSS のパス(デフォルト: 自動検出)。
130
133
  **指定したパスが存在しない場合は `--strict` の有無に関わらず常に exit 1**(`sparkle.config.json` の `extend.globals-path` も同様)
131
134
  - `--strict`: 以下を warn ではなく **exit 1** に昇格させる(CI 向け。既定は warn + 継続で後方互換を維持):
@@ -197,6 +200,23 @@ npx sparkle-design-cli rules --format json
197
200
 
198
201
  AI エージェントや CI からこの `lint:sparkle` を呼ぶ運用にすると、ガイドラインの注意書きだけに頼らず機械的に検査できます。
199
202
 
203
+ ### auth: Artifact Registry の認証(beta)
204
+
205
+ 社内パッケージの配信先を Google Artifact Registry(AR)に移行するための準備です(goodpatch/sparkle-design-internal#261)。Google の資格情報から AR 用のアクセストークンを取得し、ユーザー単位の `~/.npmrc` に書きます。
206
+
207
+ ```bash
208
+ gcloud auth login # 初回のみ(ADC を使う場合は gcloud auth application-default login)
209
+ npx --yes sparkle-design-cli@beta auth # プロジェクトの .npmrc の @goodpatch:registry が対象
210
+ pnpm install
211
+ ```
212
+
213
+ - 対象の registry は `--registry <url>`、無ければプロジェクトの `.npmrc`(親ディレクトリも辿る)の `@goodpatch:registry=` です。プロジェクトの `.npmrc` には registry 行だけを置けばよく、そのままコミットできます
214
+ - トークンの取得は ADC → `gcloud auth print-access-token` の順です。どちらも使えなければ `gcloud auth login` を案内して終了します
215
+ - **トークンは約 60 分で失効します。** `pnpm install` が 401 で落ちたら(npm は `npm adduser` を案内しますが、AR では解決しません)もう一度 `auth` を実行してください
216
+ - 同じ registry の行は置き換えるので、何度実行しても `~/.npmrc` に行は積み上がりません。`always-auth` は書きません
217
+ - トークンは Artifact Registry(`<location>-npm.pkg.dev`)以外には書きません。`cloud-platform` スコープの Google トークンなので、ほかのホストに渡すと資格情報の漏えいになるためです
218
+ - トークンを画面に出さず、refresh token などの長期クレデンシャルもコピーしません(gcloud の管理下に置いたまま)
219
+
200
220
  ### setup: プロジェクトのフルセットアップ
201
221
 
202
222
  `sparkle-design-cli setup` は、Sparkle Design の導入に必要な作業をまとめて行います:
@@ -274,7 +294,7 @@ npx --yes sparkle-design-cli setup --assistant claude --skip-install --skip-scaf
274
294
 
275
295
  ## 出力
276
296
 
277
- - デフォルト出力先: `src/app/sparkle-design.css`
297
+ - 既定の出力先: `src/app/sparkle-design.css`(Tailwind エントリ CSS を明示していて実在する場合はそのディレクトリ)
278
298
  - カスタム出力先: `-o` オプションで指定可能
279
299
  - 実行場所を基準として相対パスで処理されます
280
300
 
@@ -283,7 +303,7 @@ npx --yes sparkle-design-cli setup --assistant claude --skip-install --skip-scaf
283
303
  CLI は **Tailwind エントリ CSS(`@import "tailwindcss"` を含む CSS ファイル)** を自動検出し、以下を 1 回で揃えます:
284
304
 
285
305
  - `sparkle-design.css` の `@import`
286
- - `@source "../node_modules/sparkle-design/dist"`(v4 が node_modules のクラスを拾うのに必要)
306
+ - `@source "<エントリ CSS から node_modules への相対パス>/sparkle-design/dist"`(v4 が node_modules のクラスを拾うのに必要)。相対パスはエントリ CSS の位置から毎回計算されるので、`src/app/globals.css` なら `../../node_modules/...` になります
287
307
  - フォント `<link>` タグ(React 向けは `SparkleHead.tsx`、Vite 向けは `index.html` の managed block に自動注入)
288
308
 
289
309
  CSS 仕様上 `@import` は他の at-rule より前に書く必要があるため、順序も適切に整えます。`@import "tailwindcss"` が欠けている場合は先頭に自動追記されます。
@@ -7,8 +7,17 @@ import { runStopHook } from '../lib/stop-hook.js';
7
7
  import { PLUGIN_SPEC_MARKDOWN } from '../lib/plugin-api.js';
8
8
  import { loadAntiPatternPlugins } from '../lib/load-plugins.js';
9
9
  import { runRules } from '../lib/rules-report.js';
10
-
11
- const SUBCOMMANDS = new Set(['generate', 'check', 'rules', 'setup', 'stop-hook', 'plugin-spec']);
10
+ import { runAuth } from '../lib/auth.js';
11
+
12
+ const SUBCOMMANDS = new Set([
13
+ 'generate',
14
+ 'check',
15
+ 'rules',
16
+ 'setup',
17
+ 'auth',
18
+ 'stop-hook',
19
+ 'plugin-spec',
20
+ ]);
12
21
 
13
22
  function requireOptionValue(args, index, flags) {
14
23
  const value = args[index + 1];
@@ -146,11 +155,15 @@ function parseSetupOptions(args) {
146
155
  * and exit 0, handing non-JSON stdout to a caller that asked for JSON.
147
156
  */
148
157
  function parseRulesOptions(args) {
149
- const options = { format: 'text' };
158
+ const options = { format: 'text', help: false };
150
159
  const FORMATS = new Set(['text', 'json']);
151
160
 
152
161
  for (let i = 0; i < args.length; i += 1) {
153
162
  const arg = args[i];
163
+ if (arg === '-h' || arg === '--help') {
164
+ options.help = true;
165
+ continue;
166
+ }
154
167
  if (arg === '--format') {
155
168
  const value = requireOptionValue(args, i, '--format');
156
169
  if (!FORMATS.has(value)) {
@@ -166,6 +179,30 @@ function parseRulesOptions(args) {
166
179
  return options;
167
180
  }
168
181
 
182
+ /**
183
+ * `auth` のオプション。未知のフラグはその場で失敗させる(他のサブコマンドと同じ)。
184
+ * en: Unknown flags fail fast, same as the other subcommands.
185
+ */
186
+ function parseAuthOptions(args) {
187
+ const options = { registry: null, help: false };
188
+
189
+ for (let i = 0; i < args.length; i += 1) {
190
+ const arg = args[i];
191
+ if (arg === '-h' || arg === '--help') {
192
+ options.help = true;
193
+ continue;
194
+ }
195
+ if (arg === '--registry') {
196
+ options.registry = requireOptionValue(args, i, '--registry');
197
+ i += 1;
198
+ continue;
199
+ }
200
+ throw new Error(`Unknown option for auth: ${arg}`);
201
+ }
202
+
203
+ return options;
204
+ }
205
+
169
206
  function showHelp() {
170
207
  console.log(`
171
208
  Sparkle Design CLI
@@ -178,6 +215,7 @@ Commands:
178
215
  check Sparkle Design のアンチパターンを検査
179
216
  rules 現在有効なアンチパターンルールを一覧表示(プラグイン由来のものも含む)
180
217
  setup Sparkle Design プロジェクトをセットアップ(パッケージ導入 + 初期ファイル + AI ガード + generate)
218
+ auth Artifact Registry の npm registry 用アクセストークンを ~/.npmrc に書く(beta)
181
219
  stop-hook AI assistant の Stop hook 用 internal subcommand(severity=error の findings か、
182
220
  実行に失敗して未検査のルールがあるときに exit 2 で 1 度だけ停止をブロック / 再発火は自動回避。
183
221
  warning / info はブロックせず件数の要約を stderr に出す)
@@ -189,7 +227,7 @@ Generate:
189
227
  sparkle-design-cli generate --output ./styles/design.css
190
228
  sparkle-design-cli generate -c ./config/custom.json -o ./dist/styles.css
191
229
 
192
- # 単一バンドル内でランタイムにテーマ切替したい場合(--scope。詳細は README 参照)
230
+ # 単一バンドル内でランタイムにテーマ切替したい場合(--scope。詳細は docs/theming.md 参照)
193
231
  sparkle-design-cli generate -c ./config/sparkle.employee.json -o ./src/styles/sparkle-employee.css
194
232
  sparkle-design-cli generate -c ./config/sparkle.admin.json --scope '[data-tenant-theme="admin"]' -o ./src/styles/sparkle-admin-scope.css
195
233
 
@@ -207,6 +245,10 @@ Plugin spec:
207
245
  sparkle-design-cli plugin-spec # アンチパターン拡張プラグインの契約仕様を出力
208
246
  sparkle-design-cli plugin-spec --list # 現在のプロジェクトで発見されたプラグインを表示
209
247
 
248
+ Auth (beta):
249
+ sparkle-design-cli auth # プロジェクトの .npmrc の @goodpatch:registry を対象に認証
250
+ sparkle-design-cli auth --registry https://asia-northeast1-npm.pkg.dev/<project>/<repository>/
251
+
210
252
  Setup:
211
253
  sparkle-design-cli setup # フルセットアップ(推奨・新規導入時)
212
254
  sparkle-design-cli setup --assistant claude # Claude 向けガードも同時にセットアップ
@@ -232,8 +274,7 @@ Generate options:
232
274
  だけを実値までリテラル化して指定セレクタの中にラップ出力する
233
275
  (例: '[data-tenant-theme="admin"]')。-o/--output の指定が必須。
234
276
  --strict / --globals-path とは併用不可(グローバル CSS を
235
- パッチしないため)。詳細は README の「単一バンドルでランタイム
236
- 切替する場合」を参照
277
+ パッチしないため)。詳細は docs/theming.md の「ケース B」を参照
237
278
 
238
279
  sparkle.config.json の設定フィールド:
239
280
 
@@ -241,7 +282,7 @@ sparkle.config.json の設定フィールド:
241
282
  primary プライマリカラー (必須。blue, red, orange, yellow, purple, green, pink の
242
283
  いずれか。未指定、またはそれ以外の値は generate 実行時にエラーになります。
243
284
  7色にないブランドカラーを使いたい場合は extend.custom-css で
244
- --color-primary-* / --color-gray-* を再定義してください。詳細は README を参照)
285
+ --color-primary-* / --color-gray-* を再定義してください。詳細は docs/config.md を参照)
245
286
  font-pro プロポーショナルフォント (Google Fonts の名前)
246
287
  font-mono モノスペースフォント (Google Fonts の名前)
247
288
  radius 角丸設定 (必須。none, xs, sm, md, lg, xl, 2xl, 3xl のいずれか。
@@ -272,6 +313,19 @@ Check options:
272
313
  (warning / info は報告のみで exit code に影響しない)
273
314
  --format <text|json> 出力形式 (default: text)
274
315
 
316
+ Auth options:
317
+ -h, --help このヘルプメッセージを表示
318
+ --registry <url> 対象の registry(default: プロジェクトの .npmrc の @goodpatch:registry)
319
+
320
+ Auth の動作:
321
+ 1. Google の資格情報(ADC → gcloud auth print-access-token の順)からアクセストークンを取得
322
+ どちらも無い / 期限切れなら gcloud auth login を案内して終了
323
+ 2. ユーザー単位の ~/.npmrc(NPM_CONFIG_USERCONFIG があればそちら)に、同じ registry の行を
324
+ 置き換えて書く(プロジェクトの .npmrc は registry 行のみのまま、コミットできる状態を保つ)
325
+ 3. 有効期限(約 60 分)を表示する。切れたらもう一度 auth を実行する
326
+ トークンは Artifact Registry(<location>-npm.pkg.dev)にだけ書き、画面には出しません。
327
+ refresh token などの長期クレデンシャルは gcloud の管理下に置いたままで、コピーしません。
328
+
275
329
  Setup options:
276
330
  -h, --help このヘルプメッセージを表示
277
331
  --assistant <name> claude / codex / cursor / generic (default: generic)
@@ -407,18 +461,43 @@ async function main() {
407
461
  return;
408
462
  }
409
463
 
464
+ if (command === 'auth') {
465
+ const options = parseAuthOptions(args.slice(1));
466
+ if (options.help) {
467
+ showHelp();
468
+ process.exit(0);
469
+ }
470
+ await runAuth(options);
471
+ return;
472
+ }
473
+
410
474
  if (command === 'stop-hook') {
411
475
  // setup で各 agent の hook 設定ファイルから呼ばれる internal subcommand。
412
- // 第 2 引数は lint 対象 path(setup 時に決まったもの)。option flag は未対応。
413
- // en: Internal subcommand invoked by agent stop hooks. The single positional
414
- // arg is the lint target path determined at setup time.
415
- const target = args[1];
416
- const exitCode = await runStopHook(target);
476
+ // 以降の引数はすべて lint 対象 path。setup が書き出す hook は 1 つだが、
477
+ // 利用側が `stop-hook apps/web/app apps/web/components` のように手で複数
478
+ // 並べている実例があり、以前は args[1] しか読まず 2 つ目以降が黙って
479
+ // 未検査になっていた(issue #85)。option flag は未対応なので、`-` 始まりは
480
+ // path として渡さず警告する。
481
+ // en: Forward every positional arg. Previously only the first was used, so
482
+ // extra paths in a hand-written hook command were silently never checked.
483
+ const positional = args.slice(1).filter((arg) => !arg.startsWith('-'));
484
+ const flags = args.slice(1).filter((arg) => arg.startsWith('-'));
485
+ if (flags.length > 0) {
486
+ console.warn(
487
+ `⚠️ stop-hook はオプションを受け付けません。無視します: ${flags.join(' ')} / stop-hook takes target paths only.`
488
+ );
489
+ }
490
+ const exitCode = await runStopHook(positional);
417
491
  process.exit(exitCode);
418
492
  }
419
493
 
420
494
  if (command === 'rules') {
421
- await runRules(parseRulesOptions(args.slice(1)));
495
+ const options = parseRulesOptions(args.slice(1));
496
+ if (options.help) {
497
+ showHelp();
498
+ process.exit(0);
499
+ }
500
+ await runRules(options);
422
501
  return;
423
502
  }
424
503
 
package/docs/config.md CHANGED
@@ -34,13 +34,38 @@ Figma プラグインが出力する基本4項目に加えて、プロジェク
34
34
  }
35
35
  ```
36
36
 
37
+ ## extend.globals-path
38
+
39
+ Tailwind のエントリポイント CSS(`@import "tailwindcss";` を書いているファイル)のパスです。未指定なら自動検出します。CLI の `--globals-path` でも同じ指定ができ、そちらが優先されます。
40
+
41
+ ```json
42
+ {
43
+ "primary": "blue",
44
+ "extend": {
45
+ "globals-path": "src/styles/app.css"
46
+ }
47
+ }
48
+ ```
49
+
50
+ 指定すると次の 2 つに効きます。
51
+
52
+ | 効き先 | 内容 |
53
+ | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
54
+ | `@source` と `sparkle-design.css` の import 先 | そのファイルにパッチする(**フォントの `@import` は入りません**。フォント読み込みは `SparkleHead.tsx` 側に移っています) |
55
+ | `generate` の出力先 | `-o/--output` 未指定なら**そのファイルと同じディレクトリ**に `sparkle-design.css` と `SparkleHead.tsx` を出す |
56
+
57
+ 後者が重要です。既定の `src/app/` に固定されるのは globals-path を明示していない場合だけなので、`src/index.css` や `src/styles/app.css` 構成のプロジェクトで意図しない `src/app/` ディレクトリが増えることはありません。
58
+
59
+ > [!WARNING]
60
+ > **指定したパスが存在しない場合は `--strict` の有無に関わらず常に exit 1** です。typo に気付かないまま誤ったディレクトリを掘るより、その場で止めるほうが安全なためです。相対パスのみで、`..` を含む traversal や絶対パスは弾かれます。
61
+
37
62
  ## extend.fonts
38
63
 
39
64
  フォントごとにウェイトを個別指定。`extend.fonts` がない場合は `font-pro` / `font-mono` + デフォルトウェイト `[400, 700]` が使われます。`extend.fonts` が存在しても `fonts.pro` / `fonts.mono` のどちらかが未指定なら、そのスロットのみ `font-pro` / `font-mono` にフォールバックします。同じフォントファミリーが `pro` と `mono` で重複する場合、ウェイトはマージされ import は 1 行に統合されます。
40
65
 
41
66
  ## extend.source-packages
42
67
 
43
- 既知のデザインシステムパッケージ(`sparkle-design` など)は `package.json` から**自動検出**されるので、通常は指定不要です。クライアント固有のパッケージなど自動検出の対象外を追加したいときだけ指定してください。指定分は自動検出分とマージされ、Tailwind エントリ CSS(自動検出)に `@source` ディレクティブとして挿入されます。両方とも空のときだけ `@source` を出力しません。
68
+ 既知のデザインシステムパッケージ(`sparkle-design` など)は `package.json` から**自動検出**されるので、通常は指定不要です。クライアント固有のパッケージなど自動検出の対象外を追加したいときだけ指定してください。指定分は自動検出分とマージされ、Tailwind エントリ CSS(自動検出)に `@source` ディレクティブとして挿入されます。`@source` をまったく出さないのは、**自動検出がゼロで、かつ `source-packages` キー自体を書いていない**ときだけです(キーがあれば空配列でも既定の `sparkle-design` 1 行が出ます)。
44
69
 
45
70
  ## extend.custom-css
46
71
 
@@ -50,7 +75,7 @@ Figma プラグインが出力する基本4項目に加えて、プロジェク
50
75
 
51
76
  ### カスタムブランドカラーを primary にしたい場合
52
77
 
53
- `primary` は 7 色のいずれかしか受け付けません(前述の Core セクション参照)。7 色にないブランドカラーを使いたい場合は、`primary` は 7 色から見た目が近いものを仮に選んだ上で、`extend.custom-css` で `--color-primary-*` と `--color-gray-*` をブランドカラー基準の値で丸ごと再定義してください。`primary` の選択自体は `custom-css` 側の定義で完全に上書きされるため実質的な意味を持たなくなりますが、フィールドとしては有効な値を入れておく必要があります。
78
+ `primary` は 7 色のいずれかしか受け付けません([README の「設定オプション」](../README.md#設定オプション) を参照)。7 色にないブランドカラーを使いたい場合は、`primary` は 7 色から見た目が近いものを仮に選んだ上で、`extend.custom-css` で `--color-primary-*` と `--color-gray-*` をブランドカラー基準の値で丸ごと再定義してください。`primary` の選択自体は `custom-css` 側の定義で完全に上書きされるため実質的な意味を持たなくなりますが、フィールドとしては有効な値を入れておく必要があります。
54
79
 
55
80
  ```css
56
81
  /* custom-tokens.css */
@@ -4,8 +4,10 @@ import {
4
4
  buildLegacyCssVarPattern,
5
5
  buildLegacyScalePattern,
6
6
  buildLegacyTokenPattern,
7
+ buildTailwindPalettePattern,
7
8
  resolveLegacyCssVar,
8
9
  resolveLegacyUtility,
10
+ resolveTailwindPaletteUtility,
9
11
  } from './token-migration.js';
10
12
  import { buildSpacingUtilityPattern, pxToStep, resolveSpacingStep } from './spacing-scale.js';
11
13
 
@@ -378,7 +380,10 @@ const COMPONENT_ANTI_PATTERN_GROUPS = [
378
380
  description: 'shadcn/ui 既定 token を Sparkle Design 内へ持ち込まない',
379
381
  recommendation:
380
382
  'text-muted-foreground / bg-background / border-border などは Sparkle Design token に置き換えてください。',
381
- pattern: /\b(text-muted-foreground|bg-background|border-border)\b/g,
383
+ // 新セマンティックトークン(border-border-neutral-* 等)へ前方一致しないよう、
384
+ // 直後にハイフン/単語構成文字が続くケースを除外する
385
+ // en: exclude prefix matches against new semantic tokens (e.g. border-border-neutral-*)
386
+ pattern: /\b(text-muted-foreground|bg-background|border-border)(?![\w-])/g,
382
387
  },
383
388
  featureSection: lines([
384
389
  '### shadcn/ui 由来の class / token をそのまま使わない',
@@ -1013,8 +1018,17 @@ const COMPONENT_ANTI_PATTERN_GROUPS = [
1013
1018
  check: {
1014
1019
  description: 'Tailwind デフォルト typography を Sparkle Design コンポーネント内で使わない',
1015
1020
  recommendation:
1016
- 'text-sm / text-xs / text-base / font-medium / font-semibold / font-bold は character-* utility に置き換えてください。ただし character-* に対応する token が無いサイズ(text-[10px] 等の arbitrary value、あるいは意図的に token 外のサイズを使う場合)は、同一行または直前行に `// sparkle-disable-line tailwind-typography` コメントを付けて例外扱いとして残すこともできます。font-medium(500) / font-semibold(600) は character-* に対応する token が無いため、`extend.custom-css` で独自クラスを定義してください(詳細は README の「character-* に無いウェイトを使いたい場合」参照)。',
1017
- pattern: /\b(text-(?:xs|sm|base|lg|xl|2xl)|font-(?:medium|semibold|bold|normal|light))\b/g,
1021
+ 'text-xs 〜 text-9xl / font-medium / font-semibold / font-bold は character-* utility に置き換えてください。ただし character-* に対応する token が無いサイズ(text-[10px] 等の arbitrary value、あるいは意図的に token 外のサイズを使う場合)は、同一行または直前行に `// sparkle-disable-line tailwind-typography` コメントを付けて例外扱いとして残すこともできます。font-medium(500) / font-semibold(600) は character-* に対応する token が無いため、`extend.custom-css` で独自クラスを定義してください(詳細は README の「character-* に無いウェイトを使いたい場合」参照)。',
1022
+ // text-base は旧カラートークンの text-base-50 〜 text-base-900 へ前方一致するため、
1023
+ // shadcn-token と同様に直後のハイフン/単語構成文字を除外する
1024
+ // en: exclude prefix matches such as text-base-900 (legacy color token)
1025
+ //
1026
+ // `2xl` までしか見ておらず text-3xl 以上が漏れていた(issue #84)。見出しほど
1027
+ // 大きいサイズを使うので、抜けていた側のほうが目立つ誤りだった。Tailwind の
1028
+ // 既定スケールは text-9xl まで。
1029
+ // en: Sizes above 2xl were missed entirely; Tailwind's scale goes to 9xl.
1030
+ pattern:
1031
+ /\b(text-(?:xs|sm|base|lg|xl|[2-9]xl)|font-(?:medium|semibold|bold|normal|light))(?![\w-])/g,
1018
1032
  },
1019
1033
  featureSection: lines([
1020
1034
  '### Tailwind デフォルト typography を使わない',
@@ -1402,6 +1416,70 @@ function migrationMatcher(pattern, resolve, label) {
1402
1416
  };
1403
1417
  }
1404
1418
 
1419
+ const TAILWIND_PALETTE_PATTERN = buildTailwindPalettePattern();
1420
+
1421
+ /**
1422
+ * そのファイルが Sparkle Design を使っているか判定する材料。
1423
+ *
1424
+ * Tailwind 既定パレットの色は「Sparkle を導入したプロジェクトなのに色だけ移行が
1425
+ * 取り残されている」箇所を拾うためのルールなので、Sparkle と無関係なファイル
1426
+ * (素の React コード、管理用スクリプト、vendored なコード)まで報告すると
1427
+ * ノイズになる(issue #84)。
1428
+ *
1429
+ * 2 つの材料を見る:
1430
+ * 1. Sparkle パッケージからの import — `sparkle-design` / `@goodpatch/sparkle-design-internal`
1431
+ * のほか、`sparkle-design/components/...` のようなサブパスも拾えるよう部分一致にする
1432
+ * 2. `character-*` typography の使用 — Sparkle 固有のユーティリティで、
1433
+ * **これが使われている時点でそのファイルは Sparkle に載っている**。
1434
+ * issue #84 の実例はまさに「typography は character-* に移行済みなのに色だけ
1435
+ * Tailwind 既定パレットのまま」という形で、import 判定だけだと
1436
+ * re-export 経由(`@/components/ui/...`)のファイルを取りこぼす
1437
+ *
1438
+ * en: Gate the rule to files that actually use Sparkle — either an import from a
1439
+ * Sparkle package, or a `character-*` utility (Sparkle-specific, and the exact
1440
+ * signal in issue #84 where typography had migrated but colours had not).
1441
+ */
1442
+ const SPARKLE_USAGE_PATTERNS = [
1443
+ /\b(?:from|import)\s*\(?\s*['"][^'"]*sparkle-design[^'"]*['"]/,
1444
+ /\brequire\(\s*['"][^'"]*sparkle-design[^'"]*['"]\s*\)/,
1445
+ /(?<![\w-])character-\d/,
1446
+ ];
1447
+
1448
+ function usesSparkle(content) {
1449
+ return SPARKLE_USAGE_PATTERNS.some((pattern) => pattern.test(content));
1450
+ }
1451
+
1452
+ /**
1453
+ * Tailwind 既定パレットの色ユーティリティを拾う。
1454
+ *
1455
+ * `shadcn-token` の説明文は Tailwind パレット色(`text-slate-500`)も Wrong として
1456
+ * 示していたのに、実際の `check.pattern` は shadcn 由来の 3 語しか見ていなかった。
1457
+ * 「説明が禁止しているものを検査が拾っていない」状態を解消する(issue #84)。
1458
+ *
1459
+ * 生の hex(`#6b7280`)は**検出しない**。SVG・チャート描画・ユーザー定義色など、
1460
+ * 移行対象ではない動的な用途が大半を占めるため(報告元のプロジェクトでは 43 件中
1461
+ * 32 件がこれ)、ルール化すると偽陽性で埋まる。
1462
+ * en: Raw hex values are intentionally out of scope — most of them are dynamic
1463
+ * (SVG, charts, user-defined colours) and would drown the report in false positives.
1464
+ *
1465
+ * `targets` を指定していないので対象はソースファイルのみ。`legacy-color-token` が
1466
+ * `.css` も見るのに対し、こちらは `@apply` を拾わない。CSS には import が無く、
1467
+ * `usesSparkle` の 2 材料のうち `character-*` しか効かないため、対象に加えると
1468
+ * 「CSS の一部だけ検査される」という説明しにくい半端なカバレッジになる。
1469
+ * **意図的な線引きなので、featureSection と description に明記してある。**
1470
+ * en: Source files only. CSS has no imports, so the Sparkle gate would work only
1471
+ * half the time there — an unexplainable partial coverage. The limitation is
1472
+ * stated in the rule's description and feature section rather than left implicit.
1473
+ */
1474
+ function tailwindPaletteMatcher(rawContent) {
1475
+ if (!usesSparkle(maskBlockComments(rawContent))) return [];
1476
+ return migrationMatcher(
1477
+ TAILWIND_PALETTE_PATTERN,
1478
+ resolveTailwindPaletteUtility,
1479
+ 'Tailwind 既定パレットの色'
1480
+ )(rawContent);
1481
+ }
1482
+
1405
1483
  const SPACING_UTILITY_PATTERN = buildSpacingUtilityPattern();
1406
1484
 
1407
1485
  /**
@@ -1505,6 +1583,61 @@ const TOKEN_MIGRATION_GROUPS = [
1505
1583
  ]),
1506
1584
  jsdocTargets: [],
1507
1585
  },
1586
+ {
1587
+ id: 'tailwind-palette-color',
1588
+ check: {
1589
+ severity: SEVERITY.WARNING,
1590
+ description:
1591
+ 'Tailwind 既定パレットの色ユーティリティを使っています(Sparkle のセマンティックトークンに置き換えます)',
1592
+ recommendation:
1593
+ 'text-gray-* / bg-slate-* / border-red-* のような Tailwind 既定パレットの色は、用途別セマンティックトークン(bg- → surface / text- → text / border- → border / fill- → object)に置き換えてください。検査対象はソースファイルのみで、.css の @apply と生の hex 値は見ていません。',
1594
+ match: tailwindPaletteMatcher,
1595
+ },
1596
+ featureSection: lines([
1597
+ '### Tailwind 既定パレットの色をそのまま使わない',
1598
+ '',
1599
+ '```tsx',
1600
+ '// ✅ Correct — 用途別セマンティックトークンを使う',
1601
+ '<main className="bg-surface-base-100">',
1602
+ ' <h1 className="character-6-bold-pro text-text-negative-enabled">認証エラー</h1>',
1603
+ '</main>',
1604
+ '',
1605
+ '// ❌ Wrong — Tailwind 既定パレットの色を直接指定する',
1606
+ '<main className="bg-gray-50">',
1607
+ ' <h1 className="character-6-bold-pro text-red-600">認証エラー</h1>',
1608
+ '</main>',
1609
+ '```',
1610
+ '',
1611
+ 'Sparkle のプリミティブは Tailwind の同名変数をそのまま参照しているため',
1612
+ '(`--color-negative-600: var(--color-red-600)`)、`gray` / `red` / `green` / `yellow` / `blue`',
1613
+ 'の 5 系統は**置き換えても値が変わらない**。それ以外(`slate` / `zinc` / `emerald` …)は',
1614
+ '色味が変わるので、移行先は候補として提示するだけで自動変換はしない。',
1615
+ '',
1616
+ '| if(状況) | then(移行先) |',
1617
+ '|---|---|',
1618
+ '| 背景に `bg-gray-100` | `bg-surface-base-100`(ページ地の専用トークン) |',
1619
+ '| 文字色に `text-red-600` | `text-text-negative-enabled` |',
1620
+ '| 枠線に `border-gray-200` | `border-border-neutral-*` |',
1621
+ '| アイコン色に `fill-red-600` | `fill-object-negative-enabled` |',
1622
+ '| `bg-blue-600` | ブランド色なら `surface-primary-*`、状態表示なら `surface-info-*`。**Figma を見て決める** |',
1623
+ '| `text-purple-500` など対応する意味が無い色 | 用途から選び直す。装飾用の面なら `bg-surface-accent-1` 〜 `3` |',
1624
+ '',
1625
+ '`surface-base-*` は `0` / `100` / `200` の 3 段しか無い。`bg-gray-50` のように対応する',
1626
+ '段が無いレベルは `surface-neutral-*` 側に案内される。**移行先は check の出力に従うこと**',
1627
+ '(この表は用途の考え方を示すもので、レベルごとの対応はコマンドが出す)。',
1628
+ '',
1629
+ '`text-neutral-*` は Sparkle の旧セマンティック層と同名なので、このルールではなく',
1630
+ '`legacy-color-token` が扱う(同じ箇所を二重に報告しないため)。',
1631
+ '',
1632
+ 'このルールは **Sparkle を使っているファイルにだけ**適用される(Sparkle からの import か',
1633
+ '`character-*` の使用がある場合)。素の React コードや、Sparkle と無関係なユーティリティは対象外。',
1634
+ '',
1635
+ '検査対象は `.js` / `.jsx` / `.ts` / `.tsx` のみで、**`.css` の `@apply` は見ていない**',
1636
+ '(`legacy-color-token` は `.css` も見るので、そちらとは対象範囲が違う)。CSS 側に',
1637
+ 'Tailwind 既定パレットの色が残っていないかは手で確認すること。',
1638
+ ]),
1639
+ jsdocTargets: [],
1640
+ },
1508
1641
  {
1509
1642
  id: 'legacy-color-var',
1510
1643
  check: {
@@ -1635,6 +1768,7 @@ const BUILTIN_CHECK_ORDER = [
1635
1768
  // en: This only orders rule *evaluation*. Report ordering is by severity in
1636
1769
  // check.js — don't read this list as a display-order guarantee.
1637
1770
  'legacy-color-token',
1771
+ 'tailwind-palette-color',
1638
1772
  'legacy-color-var',
1639
1773
  'deprecated-radius-alias',
1640
1774
  'use-figma-spacing-scale',