sparkle-design-cli 2.0.6 → 2.0.7-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -31,6 +31,25 @@ npm install -g sparkle-design-cli
31
31
 
32
32
  > `npx --yes sparkle-design-cli ...` で都度実行する運用を推奨します(常に最新版が利用されます)。
33
33
 
34
+ ### リリースチャネル
35
+
36
+ npm の dist-tag でチャネルを分離しています。通常は latest を使い、品質保証中の変更を試したい場合のみ beta を指定してください。
37
+
38
+ | チャネル | dist-tag | 用途 | 指定方法 |
39
+ |---------|---------|------|---------|
40
+ | 安定版 | `latest` | 本番運用向け。`npx --yes sparkle-design-cli` は常にこれを取得 | (デフォルト) |
41
+ | Beta | `beta` | 品質保証中の検証用 | `npx --yes sparkle-design-cli@beta` |
42
+
43
+ ```bash
44
+ # beta で setup を試す
45
+ npx --yes sparkle-design-cli@beta setup --assistant claude
46
+
47
+ # beta で check を試す
48
+ npx --yes sparkle-design-cli@beta check src --strict
49
+ ```
50
+
51
+ Beta バージョンは `X.Y.Z-beta.N` 形式で publish されます。GA(正式版)へ昇格するときは、beta の -beta.N を外した `X.Y.Z` を改めて publish します。
52
+
34
53
  ## 使用方法
35
54
 
36
55
  ### サブコマンド
@@ -362,6 +381,39 @@ sparkle-design-cli generate --help
362
381
  sparkle-design-cli check src --strict
363
382
  ```
364
383
 
384
+ ### リリース手順(メンテナ向け)
385
+
386
+ npm publish は GitHub Actions (`Publish to npm`) 経由で行います。ローカルから `npm publish` しないでください。
387
+
388
+ #### 安定版(latest)
389
+
390
+ 1. `main` ブランチに変更をマージ
391
+ 2. `package.json` の `version` を SemVer で更新(例: `2.0.6` → `2.1.0`)し、`CHANGELOG.md` に該当セクションを追加
392
+ 3. PR をマージ後、Actions の **Publish to npm** を `channel: auto`(または `latest`)で手動実行
393
+ 4. workflow は lint / test を流したうえで `npm publish`(dist-tag = latest)を実行
394
+
395
+ #### Beta(beta)
396
+
397
+ 品質保証中の変更を先行公開したいときに使います。latest には影響しません。
398
+
399
+ 1. `package.json` の `version` を `X.Y.Z-beta.N` 形式に更新(例: `2.1.0-beta.0`、次の beta は `2.1.0-beta.1`)
400
+ 2. `CHANGELOG.md` に `[X.Y.Z-beta.N]` セクションを追加
401
+ 3. PR をマージ後、Actions の **Publish to npm** を `channel: auto`(または `beta`)で手動実行
402
+ 4. workflow が version を判定して `npm publish --tag beta` を実行
403
+ 5. 利用者側は `npx --yes sparkle-design-cli@beta ...` で検証
404
+
405
+ #### Beta から GA(latest)へ昇格
406
+
407
+ 同じ成果物を latest にする場合は、新しい安定版 `X.Y.Z` を publish するか、既存 beta バージョンに latest タグを付け替えます。
408
+
409
+ ```bash
410
+ # 選択肢 A: 新たに X.Y.Z を publish する(推奨)
411
+ # version を X.Y.Z に更新 → workflow を latest で実行
412
+
413
+ # 選択肢 B: 既存の X.Y.Z-beta.N に latest タグを付け替える
414
+ npm dist-tag add sparkle-design-cli@X.Y.Z-beta.N latest
415
+ ```
416
+
365
417
  ## ライセンス
366
418
 
367
419
  MIT License - 詳細は [LICENSE](LICENSE) ファイルを参照してください。
@@ -5,7 +5,7 @@
5
5
 
6
6
  import fs from 'fs';
7
7
  import path from 'path';
8
- import { REGEX, COMMENTS, IMPORTS, MESSAGES, FONT_DEFAULTS } from './constants.js';
8
+ import { REGEX, COMMENTS, IMPORTS, MESSAGES } from './constants.js';
9
9
 
10
10
  /**
11
11
  * sparkle-design.css からフォントの@import文を抽出する
@@ -26,15 +26,6 @@ export function removeFontImportsFromCSS(cssContent) {
26
26
  return cssContent.replace(REGEX.FONT_IMPORT_WITH_COMMENT, '');
27
27
  }
28
28
 
29
- /**
30
- * フォントimportブロックを生成する
31
- * @param {Array<string>} fontImports フォントimport文の配列
32
- * @returns {string} フォントimportブロック
33
- */
34
- function createFontImportBlock(fontImports) {
35
- return [COMMENTS.FONT_IMPORT, ...fontImports, ''].join('\n');
36
- }
37
-
38
29
  /**
39
30
  * globals.css から node_modules への相対パスを計算する
40
31
  * @param {string} globalsPath globals.css の絶対パス
package/lib/setup.js CHANGED
@@ -5,14 +5,16 @@ import { generateCSS } from './generate-css.js';
5
5
 
6
6
  // デフォルトの sparkle.config.json テンプレート
7
7
  // en: Default sparkle.config.json template
8
+ // sparkle-design 本体の既定値(BIZ UDPGothic / BIZ UDGothic)に揃えておくことで、
9
+ // README やドキュメントサイトと見た目が一致する状態で初回セットアップが完了する。
8
10
  // @source ディレクティブの生成は generate 側が package.json から既知のデザインシステム
9
11
  // パッケージ(sparkle-design / @goodpatch/sparkle-design-internal 等)を自動検出して
10
12
  // 行うため、ここで extend.source-packages を指定する必要はない。独自パッケージを
11
13
  // 追加する場合のみユーザーが extend.source-packages に追記する。
12
14
  const DEFAULT_SPARKLE_CONFIG = {
13
15
  primary: 'blue',
14
- 'font-pro': 'Inter',
15
- 'font-mono': 'JetBrains Mono',
16
+ 'font-pro': 'BIZ UDPGothic',
17
+ 'font-mono': 'BIZ UDGothic',
16
18
  radius: 'md',
17
19
  };
18
20
 
@@ -74,7 +76,6 @@ function buildCheckScript(target, mode) {
74
76
  return `npx --yes sparkle-design-cli check ${target} ${trailingArgs}`;
75
77
  }
76
78
 
77
-
78
79
  function ensureDir(filePath) {
79
80
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
80
81
  }
@@ -191,9 +192,11 @@ function buildInstructionBlock(target, assistant) {
191
192
  BLOCK_START,
192
193
  heading,
193
194
  '',
194
- '- Run `lint:sparkle` when Sparkle Design changes are involved.',
195
+ '- **必ず実行**: UI コンポーネントを作成・変更した後は、終了前に `lint:sparkle` を実行してください。Sparkle Design のアンチパターンを検出します。',
196
+ '- **Required**: After creating or modifying any UI component, run `lint:sparkle` before finishing. This catches Sparkle Design anti-patterns.',
195
197
  `- For AI review, prefer \`lint:sparkle:json\` or run \`npx --yes sparkle-design-cli check ${target} --format json\` directly.`,
196
- '- Always inspect both `findings` and `manualReviewReminders` from the JSON output.',
198
+ '- JSON 出力の `findings` に加えて、**`manualReviewReminders` の内容も必ず 1 項目ずつ確認**してください。機械検出できない Badge/Tag の使い分けなどが含まれます。',
199
+ '- You must inspect every entry in both `findings` and `manualReviewReminders` — the reminders flag judgment calls (Badge vs Tag etc.) that the linter cannot detect.',
197
200
  '- If semantic guidance is still needed, consult Sparkle Design docs and JSDoc examples.',
198
201
  BLOCK_END,
199
202
  ].join('\n');
@@ -226,7 +229,12 @@ function resolveScriptUpdate(existingValue, nextValue, mode, force) {
226
229
  }
227
230
 
228
231
  if (force || isManagedSparkleScript(existingValue, mode)) {
229
- return { value: nextValue, changed: true, conflict: false, reason: force ? 'forced' : 'updated' };
232
+ return {
233
+ value: nextValue,
234
+ changed: true,
235
+ conflict: false,
236
+ reason: force ? 'forced' : 'updated',
237
+ };
230
238
  }
231
239
 
232
240
  return { value: existingValue, changed: false, conflict: true, reason: 'preserved-custom' };
@@ -280,7 +288,8 @@ const PM_COMMANDS = {
280
288
  */
281
289
  function detectPackageManager(cwd) {
282
290
  if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) return 'pnpm';
283
- if (fs.existsSync(path.join(cwd, 'bun.lockb')) || fs.existsSync(path.join(cwd, 'bun.lock'))) return 'bun';
291
+ if (fs.existsSync(path.join(cwd, 'bun.lockb')) || fs.existsSync(path.join(cwd, 'bun.lock')))
292
+ return 'bun';
284
293
  if (fs.existsSync(path.join(cwd, 'yarn.lock'))) return 'yarn';
285
294
  return 'npm';
286
295
  }
@@ -289,7 +298,12 @@ function detectPackageManager(cwd) {
289
298
  * package.json に未登録のパッケージだけをインストールする
290
299
  * en: Install only packages not yet listed in package.json
291
300
  */
292
- function ensurePackagesInstalled(cwd, packageManager, candidates, { dev = false, dryRun = false } = {}) {
301
+ function ensurePackagesInstalled(
302
+ cwd,
303
+ packageManager,
304
+ candidates,
305
+ { dev = false, dryRun = false } = {}
306
+ ) {
293
307
  const packageJson = readJson(path.join(cwd, 'package.json'));
294
308
  const deps = { ...(packageJson.dependencies ?? {}), ...(packageJson.devDependencies ?? {}) };
295
309
  const missing = candidates.filter((pkg) => !Object.prototype.hasOwnProperty.call(deps, pkg));
@@ -356,7 +370,12 @@ function buildScaffoldTargets(cwd) {
356
370
  },
357
371
  {
358
372
  name: 'postcssConfig',
359
- candidates: ['postcss.config.mjs', 'postcss.config.js', 'postcss.config.cjs', 'postcss.config.ts'],
373
+ candidates: [
374
+ 'postcss.config.mjs',
375
+ 'postcss.config.js',
376
+ 'postcss.config.cjs',
377
+ 'postcss.config.ts',
378
+ ],
360
379
  target: 'postcss.config.mjs',
361
380
  write: (abs) => fs.writeFileSync(abs, INITIAL_POSTCSS_CONFIG, 'utf8'),
362
381
  },
@@ -400,7 +419,10 @@ function runInstall(cwd, packageManager, { skipInstall, dryRun }) {
400
419
  return {
401
420
  skipped: false,
402
421
  sparkle: ensurePackagesInstalled(cwd, packageManager, SPARKLE_PACKAGES, { dryRun }),
403
- tailwind: ensurePackagesInstalled(cwd, packageManager, TAILWIND_PACKAGES, { dev: true, dryRun }),
422
+ tailwind: ensurePackagesInstalled(cwd, packageManager, TAILWIND_PACKAGES, {
423
+ dev: true,
424
+ dryRun,
425
+ }),
404
426
  };
405
427
  }
406
428
 
@@ -415,7 +437,10 @@ function runAssistantGuard(cwd, packageJsonPath, options, assistantConfig) {
415
437
  ? normalizePath(cwd, options.target, 'Target path')
416
438
  : normalizePath(cwd, detectTarget(cwd), 'Target path');
417
439
  const instructionPath = options.instructionsPath
418
- ? path.resolve(cwd, normalizePath(cwd, options.instructionsPath, 'Instructions path', { checkParentOnly: true }))
440
+ ? path.resolve(
441
+ cwd,
442
+ normalizePath(cwd, options.instructionsPath, 'Instructions path', { checkParentOnly: true })
443
+ )
419
444
  : path.resolve(cwd, assistantConfig.path);
420
445
 
421
446
  const packageResult = updatePackageJson(packageJsonPath, target, force);
@@ -465,9 +490,17 @@ export function setupAssistant(options = {}) {
465
490
  const dryRun = Boolean(options.dryRun);
466
491
  const packageManager = detectPackageManager(cwd);
467
492
 
468
- const install = runInstall(cwd, packageManager, { skipInstall: Boolean(options.skipInstall), dryRun });
493
+ const install = runInstall(cwd, packageManager, {
494
+ skipInstall: Boolean(options.skipInstall),
495
+ dryRun,
496
+ });
469
497
  const scaffold = runScaffold(cwd, { skipScaffold: Boolean(options.skipScaffold), dryRun });
470
- const guard = runAssistantGuard(cwd, packageJsonPath, { ...options, assistant, dryRun }, assistantConfig);
498
+ const guard = runAssistantGuard(
499
+ cwd,
500
+ packageJsonPath,
501
+ { ...options, assistant, dryRun },
502
+ assistantConfig
503
+ );
471
504
  const generate = runGenerate({ skipGenerate: Boolean(options.skipGenerate), dryRun });
472
505
 
473
506
  const summary = {
@@ -497,5 +530,45 @@ export function setupAssistant(options = {}) {
497
530
  };
498
531
 
499
532
  console.log(JSON.stringify(summary, null, 2));
533
+
534
+ // セットアップ後のリマインダー。AI の会話履歴にも残るよう stderr に出す。
535
+ // en: Post-setup reminder. Written to stderr so it stays in the AI transcript
536
+ // without polluting the JSON stdout payload that tooling parses.
537
+ printPostSetupReminder(guard.target, packageManager);
538
+
500
539
  return summary;
501
540
  }
541
+
542
+ // 検出した package manager に応じて `lint:sparkle` を呼び出すコマンドを返す。
543
+ // en: Returns the shell invocation for the `lint:sparkle` script based on the
544
+ // detected package manager.
545
+ function buildLintCommand(packageManager) {
546
+ switch (packageManager) {
547
+ case 'pnpm':
548
+ return 'pnpm lint:sparkle';
549
+ case 'yarn':
550
+ return 'yarn lint:sparkle';
551
+ case 'bun':
552
+ return 'bun run lint:sparkle';
553
+ case 'npm':
554
+ default:
555
+ return 'npm run lint:sparkle';
556
+ }
557
+ }
558
+
559
+ function printPostSetupReminder(target, packageManager) {
560
+ const lintTarget = target || 'src';
561
+ const lintCmd = buildLintCommand(packageManager);
562
+ const lines = [
563
+ '',
564
+ '📝 次のステップ / Next steps:',
565
+ ` 1. UI コンポーネントを作成・変更したら、必ず \`${lintCmd}\` (または \`npx --yes sparkle-design-cli check ${lintTarget} --strict\`) を実行してください。`,
566
+ ' After creating or modifying UI components, always run `lint:sparkle` to catch Sparkle Design anti-patterns.',
567
+ ' 2. AI でレビューする場合は `lint:sparkle:json` を使い、`findings` に加えて `manualReviewReminders` の各項目まで必ず確認してください。',
568
+ ' For AI review, use `lint:sparkle:json` and inspect every entry of both `findings` and `manualReviewReminders`.',
569
+ '',
570
+ ];
571
+ for (const line of lines) {
572
+ console.error(line);
573
+ }
574
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sparkle-design-cli",
3
- "version": "2.0.6",
3
+ "version": "2.0.7-beta.0",
4
4
  "description": "Sparkle Design CSS Generator - デザインシステムCSSを設定ファイルから生成するツール",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org",