sparkle-design-cli 2.0.4 → 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 +52 -0
- package/lib/font-manager.js +1 -10
- package/lib/generate-css.js +47 -1
- package/lib/setup.js +90 -13
- package/package.json +1 -1
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) ファイルを参照してください。
|
package/lib/font-manager.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import fs from 'fs';
|
|
7
7
|
import path from 'path';
|
|
8
|
-
import { REGEX, COMMENTS, IMPORTS, MESSAGES
|
|
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/generate-css.js
CHANGED
|
@@ -332,6 +332,40 @@ function processTemplate(template, config, grayMapping, radiusMapping, colors) {
|
|
|
332
332
|
* @param {string} cssContent CSS内容
|
|
333
333
|
* @param {string|null} outputPath カスタム出力パス(オプション)
|
|
334
334
|
*/
|
|
335
|
+
// 自動検出対象の既知デザインシステムパッケージ。
|
|
336
|
+
// `sparkle-design` は CLI 側のデフォルトパッケージなのでリストには加えず「存在判定」にだけ使う。
|
|
337
|
+
// en: Known design-system packages to auto-detect. `sparkle-design` is the CLI's
|
|
338
|
+
// default source package, so it only acts as an "enables @source" signal.
|
|
339
|
+
const KNOWN_DESIGN_SYSTEM_PACKAGES = [
|
|
340
|
+
'@goodpatch/sparkle-design-internal',
|
|
341
|
+
];
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* package.json の dependencies / devDependencies から既知のデザインシステムパッケージを検出する。
|
|
345
|
+
* - `sparkle-design` が入っていれば(CLI のデフォルトで @source に含まれるため)空配列を返し、@source 挿入を有効化
|
|
346
|
+
* - `@goodpatch/sparkle-design-internal` など追加の既知パッケージが入っていればリストに含める
|
|
347
|
+
* - いずれも見つからなければ null(従来通り @source を生成しない)
|
|
348
|
+
* en: Scan package.json and surface source-packages when a design-system package is present.
|
|
349
|
+
*/
|
|
350
|
+
function detectSourcePackagesFromPackageJson(cwd = process.cwd()) {
|
|
351
|
+
try {
|
|
352
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
|
|
353
|
+
const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
|
|
354
|
+
|
|
355
|
+
const hasDefault = Object.prototype.hasOwnProperty.call(deps, 'sparkle-design');
|
|
356
|
+
const extras = KNOWN_DESIGN_SYSTEM_PACKAGES.filter((name) =>
|
|
357
|
+
Object.prototype.hasOwnProperty.call(deps, name)
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
if (hasDefault || extras.length > 0) {
|
|
361
|
+
return extras;
|
|
362
|
+
}
|
|
363
|
+
return null;
|
|
364
|
+
} catch {
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
335
369
|
function writeCSS(cssContent, outputPath = null) {
|
|
336
370
|
// カスタムパスが指定されていない場合は実行場所からsrc/appディレクトリに出力
|
|
337
371
|
const defaultOutputPath = path.resolve(
|
|
@@ -395,8 +429,19 @@ export function generateCSS(configPath = null, outputPath = null, globalsPath =
|
|
|
395
429
|
writeSparkleHead(sparkleHeadContent, resolvedOutputPath);
|
|
396
430
|
|
|
397
431
|
// 8. フォント管理の自動処理を実行(globals.css にはフォント @import を差し込まない)
|
|
432
|
+
// source-packages は以下の合成で決まる:
|
|
433
|
+
// 1. package.json から既知のデザインシステムパッケージを自動検出(baseline)
|
|
434
|
+
// 2. config の extend.source-packages(ユーザー追記分、例: クライアント固有パッケージ)
|
|
435
|
+
// 両方空なら null(@source を生成しない)。
|
|
436
|
+
// en: source-packages are resolved by merging auto-detection (from package.json)
|
|
437
|
+
// with explicit config entries. If neither surfaces anything, @source is skipped.
|
|
398
438
|
console.log(MESSAGES.FONT_MANAGEMENT_START);
|
|
399
|
-
const
|
|
439
|
+
const detectedPackages = detectSourcePackagesFromPackageJson();
|
|
440
|
+
const explicitPackages = 'source-packages' in config ? (config['source-packages'] || []) : null;
|
|
441
|
+
const sourcePackages =
|
|
442
|
+
detectedPackages !== null || explicitPackages !== null
|
|
443
|
+
? [...new Set([...(detectedPackages ?? []), ...(explicitPackages ?? [])])]
|
|
444
|
+
: null;
|
|
400
445
|
const customCssPath = config['custom-css'] || null;
|
|
401
446
|
const globalsPathOverride = globalsPath || config['globals-path'] || null;
|
|
402
447
|
manageFontImports(resolvedOutputPath, sourcePackages, customCssPath, globalsPathOverride);
|
|
@@ -414,6 +459,7 @@ export {
|
|
|
414
459
|
// メイン関数
|
|
415
460
|
processTemplate,
|
|
416
461
|
writeCSS,
|
|
462
|
+
detectSourcePackagesFromPackageJson,
|
|
417
463
|
// ファイル読み込み
|
|
418
464
|
loadConfig,
|
|
419
465
|
loadTemplate,
|
package/lib/setup.js
CHANGED
|
@@ -5,10 +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 やドキュメントサイトと見た目が一致する状態で初回セットアップが完了する。
|
|
10
|
+
// @source ディレクティブの生成は generate 側が package.json から既知のデザインシステム
|
|
11
|
+
// パッケージ(sparkle-design / @goodpatch/sparkle-design-internal 等)を自動検出して
|
|
12
|
+
// 行うため、ここで extend.source-packages を指定する必要はない。独自パッケージを
|
|
13
|
+
// 追加する場合のみユーザーが extend.source-packages に追記する。
|
|
8
14
|
const DEFAULT_SPARKLE_CONFIG = {
|
|
9
15
|
primary: 'blue',
|
|
10
|
-
'font-pro': '
|
|
11
|
-
'font-mono': '
|
|
16
|
+
'font-pro': 'BIZ UDPGothic',
|
|
17
|
+
'font-mono': 'BIZ UDGothic',
|
|
12
18
|
radius: 'md',
|
|
13
19
|
};
|
|
14
20
|
|
|
@@ -70,7 +76,6 @@ function buildCheckScript(target, mode) {
|
|
|
70
76
|
return `npx --yes sparkle-design-cli check ${target} ${trailingArgs}`;
|
|
71
77
|
}
|
|
72
78
|
|
|
73
|
-
|
|
74
79
|
function ensureDir(filePath) {
|
|
75
80
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
76
81
|
}
|
|
@@ -187,9 +192,11 @@ function buildInstructionBlock(target, assistant) {
|
|
|
187
192
|
BLOCK_START,
|
|
188
193
|
heading,
|
|
189
194
|
'',
|
|
190
|
-
'-
|
|
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.',
|
|
191
197
|
`- For AI review, prefer \`lint:sparkle:json\` or run \`npx --yes sparkle-design-cli check ${target} --format json\` directly.`,
|
|
192
|
-
'-
|
|
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.',
|
|
193
200
|
'- If semantic guidance is still needed, consult Sparkle Design docs and JSDoc examples.',
|
|
194
201
|
BLOCK_END,
|
|
195
202
|
].join('\n');
|
|
@@ -222,7 +229,12 @@ function resolveScriptUpdate(existingValue, nextValue, mode, force) {
|
|
|
222
229
|
}
|
|
223
230
|
|
|
224
231
|
if (force || isManagedSparkleScript(existingValue, mode)) {
|
|
225
|
-
return {
|
|
232
|
+
return {
|
|
233
|
+
value: nextValue,
|
|
234
|
+
changed: true,
|
|
235
|
+
conflict: false,
|
|
236
|
+
reason: force ? 'forced' : 'updated',
|
|
237
|
+
};
|
|
226
238
|
}
|
|
227
239
|
|
|
228
240
|
return { value: existingValue, changed: false, conflict: true, reason: 'preserved-custom' };
|
|
@@ -276,7 +288,8 @@ const PM_COMMANDS = {
|
|
|
276
288
|
*/
|
|
277
289
|
function detectPackageManager(cwd) {
|
|
278
290
|
if (fs.existsSync(path.join(cwd, 'pnpm-lock.yaml'))) return 'pnpm';
|
|
279
|
-
if (fs.existsSync(path.join(cwd, 'bun.lockb')) || fs.existsSync(path.join(cwd, 'bun.lock')))
|
|
291
|
+
if (fs.existsSync(path.join(cwd, 'bun.lockb')) || fs.existsSync(path.join(cwd, 'bun.lock')))
|
|
292
|
+
return 'bun';
|
|
280
293
|
if (fs.existsSync(path.join(cwd, 'yarn.lock'))) return 'yarn';
|
|
281
294
|
return 'npm';
|
|
282
295
|
}
|
|
@@ -285,7 +298,12 @@ function detectPackageManager(cwd) {
|
|
|
285
298
|
* package.json に未登録のパッケージだけをインストールする
|
|
286
299
|
* en: Install only packages not yet listed in package.json
|
|
287
300
|
*/
|
|
288
|
-
function ensurePackagesInstalled(
|
|
301
|
+
function ensurePackagesInstalled(
|
|
302
|
+
cwd,
|
|
303
|
+
packageManager,
|
|
304
|
+
candidates,
|
|
305
|
+
{ dev = false, dryRun = false } = {}
|
|
306
|
+
) {
|
|
289
307
|
const packageJson = readJson(path.join(cwd, 'package.json'));
|
|
290
308
|
const deps = { ...(packageJson.dependencies ?? {}), ...(packageJson.devDependencies ?? {}) };
|
|
291
309
|
const missing = candidates.filter((pkg) => !Object.prototype.hasOwnProperty.call(deps, pkg));
|
|
@@ -352,7 +370,12 @@ function buildScaffoldTargets(cwd) {
|
|
|
352
370
|
},
|
|
353
371
|
{
|
|
354
372
|
name: 'postcssConfig',
|
|
355
|
-
candidates: [
|
|
373
|
+
candidates: [
|
|
374
|
+
'postcss.config.mjs',
|
|
375
|
+
'postcss.config.js',
|
|
376
|
+
'postcss.config.cjs',
|
|
377
|
+
'postcss.config.ts',
|
|
378
|
+
],
|
|
356
379
|
target: 'postcss.config.mjs',
|
|
357
380
|
write: (abs) => fs.writeFileSync(abs, INITIAL_POSTCSS_CONFIG, 'utf8'),
|
|
358
381
|
},
|
|
@@ -396,7 +419,10 @@ function runInstall(cwd, packageManager, { skipInstall, dryRun }) {
|
|
|
396
419
|
return {
|
|
397
420
|
skipped: false,
|
|
398
421
|
sparkle: ensurePackagesInstalled(cwd, packageManager, SPARKLE_PACKAGES, { dryRun }),
|
|
399
|
-
tailwind: ensurePackagesInstalled(cwd, packageManager, TAILWIND_PACKAGES, {
|
|
422
|
+
tailwind: ensurePackagesInstalled(cwd, packageManager, TAILWIND_PACKAGES, {
|
|
423
|
+
dev: true,
|
|
424
|
+
dryRun,
|
|
425
|
+
}),
|
|
400
426
|
};
|
|
401
427
|
}
|
|
402
428
|
|
|
@@ -411,7 +437,10 @@ function runAssistantGuard(cwd, packageJsonPath, options, assistantConfig) {
|
|
|
411
437
|
? normalizePath(cwd, options.target, 'Target path')
|
|
412
438
|
: normalizePath(cwd, detectTarget(cwd), 'Target path');
|
|
413
439
|
const instructionPath = options.instructionsPath
|
|
414
|
-
? path.resolve(
|
|
440
|
+
? path.resolve(
|
|
441
|
+
cwd,
|
|
442
|
+
normalizePath(cwd, options.instructionsPath, 'Instructions path', { checkParentOnly: true })
|
|
443
|
+
)
|
|
415
444
|
: path.resolve(cwd, assistantConfig.path);
|
|
416
445
|
|
|
417
446
|
const packageResult = updatePackageJson(packageJsonPath, target, force);
|
|
@@ -461,9 +490,17 @@ export function setupAssistant(options = {}) {
|
|
|
461
490
|
const dryRun = Boolean(options.dryRun);
|
|
462
491
|
const packageManager = detectPackageManager(cwd);
|
|
463
492
|
|
|
464
|
-
const install = runInstall(cwd, packageManager, {
|
|
493
|
+
const install = runInstall(cwd, packageManager, {
|
|
494
|
+
skipInstall: Boolean(options.skipInstall),
|
|
495
|
+
dryRun,
|
|
496
|
+
});
|
|
465
497
|
const scaffold = runScaffold(cwd, { skipScaffold: Boolean(options.skipScaffold), dryRun });
|
|
466
|
-
const guard = runAssistantGuard(
|
|
498
|
+
const guard = runAssistantGuard(
|
|
499
|
+
cwd,
|
|
500
|
+
packageJsonPath,
|
|
501
|
+
{ ...options, assistant, dryRun },
|
|
502
|
+
assistantConfig
|
|
503
|
+
);
|
|
467
504
|
const generate = runGenerate({ skipGenerate: Boolean(options.skipGenerate), dryRun });
|
|
468
505
|
|
|
469
506
|
const summary = {
|
|
@@ -493,5 +530,45 @@ export function setupAssistant(options = {}) {
|
|
|
493
530
|
};
|
|
494
531
|
|
|
495
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
|
+
|
|
496
539
|
return summary;
|
|
497
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
|
+
}
|