sparkle-design-cli 2.2.0 → 2.2.1
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/bin/sparkle-design.js +3 -1
- package/lib/font-manager.js +11 -4
- package/lib/generate-css.js +158 -9
- package/package.json +1 -1
package/bin/sparkle-design.js
CHANGED
|
@@ -174,7 +174,9 @@ Setup:
|
|
|
174
174
|
Generate options:
|
|
175
175
|
-h, --help このヘルプメッセージを表示
|
|
176
176
|
-c, --config <path> 設定ファイルのパス (default: ./sparkle.config.json)
|
|
177
|
-
-o, --output <path> 出力ファイルのパス
|
|
177
|
+
-o, --output <path> 出力ファイルのパス
|
|
178
|
+
(default: globals-path 指定時はその entry CSS と同じ
|
|
179
|
+
ディレクトリ、未指定時は ./src/app/sparkle-design.css)
|
|
178
180
|
--globals-path <path> Tailwind エントリポイント CSS のパス (default: 自動検出)
|
|
179
181
|
--strict globals.css への @source 注入等が失敗したら exit 1
|
|
180
182
|
(CI 向け。既定は warn + 継続で後方互換を維持)
|
package/lib/font-manager.js
CHANGED
|
@@ -259,9 +259,10 @@ function reconstructGlobalsCss(globalsContent, sparkleImportBlock, tailwindInfo)
|
|
|
259
259
|
* @param {string} globalsPath globals.cssのパス
|
|
260
260
|
* @param {Array<string>|null} sourcePackages 追加パッケージ名の配列(null の場合は @source を生成しない)
|
|
261
261
|
* @param {string|null} customCssPath custom-css ファイルの相対パス
|
|
262
|
-
* @returns {{ status: 'skipped' | 'updated' | 'failed', reason?: string }}
|
|
262
|
+
* @returns {{ status: 'skipped' | 'updated' | 'failed', reason?: string, globalsPath?: string, sourcePackages?: Array<string>|null }}
|
|
263
263
|
* skipped: 触る理由がない (fonts/sourcePackages/customCss すべて空)
|
|
264
|
-
* updated: globals.css
|
|
264
|
+
* updated: globals.css を正常に書き換えた(patch した globalsPath と挿入した
|
|
265
|
+
* sourcePackages を併せて返す — issue #56 のサマリ表示用)
|
|
265
266
|
* failed: 書き換え対象だったが失敗した (TAILWIND_IMPORT 欠落、write 失敗等)
|
|
266
267
|
*/
|
|
267
268
|
export function updateGlobalsWithFonts(
|
|
@@ -342,7 +343,11 @@ export function updateGlobalsWithFonts(
|
|
|
342
343
|
// 6. 更新したglobals.cssを書き込む
|
|
343
344
|
fs.writeFileSync(globalsPath, reconstructedContent, 'utf8');
|
|
344
345
|
console.log(MESSAGES.GLOBALS_UPDATED(globalsPath));
|
|
345
|
-
|
|
346
|
+
// `globalsPath` / `sourcePackages` は generate のサマリ出力(issue #56)が
|
|
347
|
+
// 「どの entry CSS に何の @source を挿入したか」を表示するために返す。
|
|
348
|
+
// en: Surface which entry CSS was patched and with which @source packages
|
|
349
|
+
// so the caller can render the generate summary (issue #56).
|
|
350
|
+
return { status: 'updated', globalsPath, sourcePackages };
|
|
346
351
|
} catch (error) {
|
|
347
352
|
// `E_UNSAFE_RELATIVE_PATH`(custom-css の path traversal 等)や
|
|
348
353
|
// `E_TAILWIND_IMPORT_PREPEND_FAILED`(内部不整合)は silent fallback
|
|
@@ -564,7 +569,9 @@ function resolveGlobalsPath(sparkleDesignPath, explicitGlobalsPath = null) {
|
|
|
564
569
|
* @param {string|null} customCssPath custom-css ファイルの相対パス
|
|
565
570
|
* @param {string|null} globalsPathOverride 明示的に指定された globals パス
|
|
566
571
|
* @param {{ strict?: boolean }} [options] strict=true のとき失敗を throw する
|
|
567
|
-
* @returns {{ status: 'skipped' | 'updated' | 'failed', reason?: string }}
|
|
572
|
+
* @returns {{ status: 'skipped' | 'updated' | 'failed', reason?: string, globalsPath?: string, sourcePackages?: Array<string>|null }}
|
|
573
|
+
* updated のときは patch した entry CSS の globalsPath と挿入した sourcePackages
|
|
574
|
+
* を含む(issue #56 の generate サマリ用)。
|
|
568
575
|
*/
|
|
569
576
|
export function manageFontImports(
|
|
570
577
|
sparkleDesignPath,
|
package/lib/generate-css.js
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
updateGlobalsWithFonts,
|
|
24
24
|
isViteProject,
|
|
25
25
|
} from './font-manager.js';
|
|
26
|
+
import { assertSafeRelativePath, toPosixPath } from './path-utils.js';
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* config の extend セクションを解決する
|
|
@@ -344,6 +345,7 @@ function writeSparkleHead(content, sparkleDesignCssPath) {
|
|
|
344
345
|
fs.writeFileSync(outputPath, content, 'utf8');
|
|
345
346
|
console.log(`✅ SparkleHead.tsx を生成しました: ${outputPath}`);
|
|
346
347
|
console.log(' → ルートレイアウトの <head> 内に <SparkleHead /> を追加してください');
|
|
348
|
+
return outputPath;
|
|
347
349
|
}
|
|
348
350
|
|
|
349
351
|
const VITE_INDEX_HTML_BLOCK_START = '<!-- sparkle-design-cli:fonts:start -->';
|
|
@@ -602,6 +604,73 @@ function detectSourcePackagesFromPackageJson(cwd = process.cwd()) {
|
|
|
602
604
|
return null;
|
|
603
605
|
}
|
|
604
606
|
|
|
607
|
+
/**
|
|
608
|
+
* `--output` 未指定時に、明示された globals-path(CLI `--globals-path` または
|
|
609
|
+
* `extend.globals-path`)と同じディレクトリを出力先に使えるか判定する。
|
|
610
|
+
*
|
|
611
|
+
* 以前は framework に依らず常に `DEFAULT_OUTPUT_DIR = src/app/` に書き出していた
|
|
612
|
+
* ため、Vite の `src/index.css` 構成で `globals-path: src/index.css` を指定しても
|
|
613
|
+
* `sparkle-design.css` が `src/app/` に落ち、本来不要な `src/app/` ディレクトリが
|
|
614
|
+
* 生成されてレビュー / クリーンアップを混乱させていた(issue #56 確定バグ)。
|
|
615
|
+
*
|
|
616
|
+
* - globals-path が未指定なら null(従来どおり src/app/ を使う)
|
|
617
|
+
* - 不正な path(traversal / shell メタ文字 / 絶対パス)は `assertSafeRelativePath`
|
|
618
|
+
* が throw(後段 `resolveGlobalsPath` でも同様に弾かれるので早期 fail-loud)
|
|
619
|
+
* - 解決した path が実在しなければ null。実在しない globals-path は
|
|
620
|
+
* `manageFontImports` が `E_EXPLICIT_GLOBALS_PATH_NOT_FOUND` で loud に throw
|
|
621
|
+
* するので、ここで typo 由来の誤ディレクトリを掘らないよう存在チェックする
|
|
622
|
+
* - 実在しても**通常ファイルでない**(ディレクトリ等)なら null。例えば
|
|
623
|
+
* `globals-path: src` のようにディレクトリを誤指定すると、存在チェックだけ
|
|
624
|
+
* では通過し `path.dirname('.../src')` がプロジェクト root を指して出力先が
|
|
625
|
+
* root に誤誘導される。entry CSS は CSS ファイル前提なので `isFile()` で弾く。
|
|
626
|
+
*
|
|
627
|
+
* en: When `--output` is omitted, return the directory of an explicit
|
|
628
|
+
* globals-path so sparkle-design.css lands next to the entry CSS instead of
|
|
629
|
+
* always under src/app/. Returns null (→ keep src/app/ default) when no
|
|
630
|
+
* globals-path is given, it doesn't exist yet, or it isn't a regular file
|
|
631
|
+
* (e.g. a directory was passed by mistake).
|
|
632
|
+
*
|
|
633
|
+
* @param {string|null} globalsPathOverride
|
|
634
|
+
* @returns {string|null} entry CSS が通常ファイルとして実在する場合その絶対ディレクトリ、なければ null
|
|
635
|
+
*/
|
|
636
|
+
function resolveGlobalsDirForOutput(globalsPathOverride) {
|
|
637
|
+
if (!globalsPathOverride) {
|
|
638
|
+
return null;
|
|
639
|
+
}
|
|
640
|
+
assertSafeRelativePath(globalsPathOverride, 'globals-path');
|
|
641
|
+
const resolved = path.resolve(process.cwd(), globalsPathOverride);
|
|
642
|
+
let stat;
|
|
643
|
+
try {
|
|
644
|
+
stat = fs.statSync(resolved);
|
|
645
|
+
} catch {
|
|
646
|
+
// 実在しない(ENOENT 等)。出力先は src/app/ 既定に委ね、entry CSS の
|
|
647
|
+
// not-found は manageFontImports が loud に throw する。
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
if (!stat.isFile()) {
|
|
651
|
+
return null;
|
|
652
|
+
}
|
|
653
|
+
return path.dirname(resolved);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* sparkle-design.css の出力先絶対パスを決定する。
|
|
658
|
+
* 優先順位: `--output` 明示 > globals-path と同じディレクトリ > `src/app/`(既定)
|
|
659
|
+
* @param {string|null} outputPath `--output` の値
|
|
660
|
+
* @param {string|null} globalsPathOverride globals-path(CLI > config)
|
|
661
|
+
* @returns {string} 出力先の絶対パス
|
|
662
|
+
*/
|
|
663
|
+
function resolveOutputPath(outputPath, globalsPathOverride) {
|
|
664
|
+
if (outputPath) {
|
|
665
|
+
return path.resolve(outputPath);
|
|
666
|
+
}
|
|
667
|
+
const globalsDir = resolveGlobalsDirForOutput(globalsPathOverride);
|
|
668
|
+
if (globalsDir) {
|
|
669
|
+
return path.join(globalsDir, 'sparkle-design.css');
|
|
670
|
+
}
|
|
671
|
+
return path.resolve(process.cwd(), ...PATHS.DEFAULT_OUTPUT_DIR, 'sparkle-design.css');
|
|
672
|
+
}
|
|
673
|
+
|
|
605
674
|
function writeCSS(cssContent, outputPath = null) {
|
|
606
675
|
// カスタムパスが指定されていない場合は実行場所からsrc/appディレクトリに出力
|
|
607
676
|
const defaultOutputPath = path.resolve(
|
|
@@ -626,6 +695,79 @@ function writeCSS(cssContent, outputPath = null) {
|
|
|
626
695
|
}
|
|
627
696
|
}
|
|
628
697
|
|
|
698
|
+
/**
|
|
699
|
+
* `@source` ブロックに実際に書き出されるパッケージ名一覧を組み立てる。
|
|
700
|
+
* `createSourceBlock`(font-manager.js)と同じく default の `sparkle-design` を
|
|
701
|
+
* 先頭に入れ、重複を除いた配列を返す。サマリ表示専用。
|
|
702
|
+
* en: Mirror createSourceBlock's package list (default sparkle-design first)
|
|
703
|
+
* for the summary output.
|
|
704
|
+
* @param {Array<string>|null|undefined} sourcePackages
|
|
705
|
+
* @returns {Array<string>|null} null は @source 未挿入
|
|
706
|
+
*/
|
|
707
|
+
function describeInsertedSourcePackages(sourcePackages) {
|
|
708
|
+
if (sourcePackages === null || sourcePackages === undefined) {
|
|
709
|
+
return null;
|
|
710
|
+
}
|
|
711
|
+
const defaultPackage = 'sparkle-design';
|
|
712
|
+
return [defaultPackage, ...sourcePackages.filter((p) => p !== defaultPackage)];
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* generate 完了時に「何を・どこに書いたか」を 1 ブロックで要約出力する。
|
|
717
|
+
* issue #56 の可観測性要求への対応。とくに entry CSS と出力先のディレクトリが
|
|
718
|
+
* 乖離している場合(Vite で `src/index.css` を patch しつつ出力が `src/app/` に
|
|
719
|
+
* 落ちる等)に警告を出し、`@source` が想定どおり挿入されたかを目視確認できる
|
|
720
|
+
* ようにする。
|
|
721
|
+
* en: Print a one-block summary of what `generate` patched / wrote and warn
|
|
722
|
+
* when the entry CSS dir and the output dir diverge (issue #56 observability).
|
|
723
|
+
*
|
|
724
|
+
* @param {Object} args
|
|
725
|
+
* @param {string} args.outputPath sparkle-design.css の絶対パス
|
|
726
|
+
* @param {string} args.sparkleHeadPath SparkleHead.tsx の絶対パス
|
|
727
|
+
* @param {{ status: string, reason?: string, globalsPath?: string, sourcePackages?: Array<string>|null }} args.globalsResult
|
|
728
|
+
*/
|
|
729
|
+
function printGenerateSummary({ outputPath, sparkleHeadPath, globalsResult }) {
|
|
730
|
+
const cwd = process.cwd();
|
|
731
|
+
const rel = (p) => (p ? path.relative(cwd, p) || '.' : null);
|
|
732
|
+
const lines = ['', '📋 生成サマリ (issue #56):'];
|
|
733
|
+
lines.push(` • sparkle-design.css: ${rel(outputPath)}`);
|
|
734
|
+
lines.push(` • SparkleHead.tsx: ${rel(sparkleHeadPath)}`);
|
|
735
|
+
|
|
736
|
+
if (globalsResult.status === 'updated' && globalsResult.globalsPath) {
|
|
737
|
+
const entryPath = globalsResult.globalsPath;
|
|
738
|
+
lines.push(` • パッチした entry CSS: ${rel(entryPath)}`);
|
|
739
|
+
|
|
740
|
+
const inserted = describeInsertedSourcePackages(globalsResult.sourcePackages);
|
|
741
|
+
if (inserted && inserted.length > 0) {
|
|
742
|
+
lines.push(` • 挿入した @source: ${inserted.join(', ')}`);
|
|
743
|
+
} else {
|
|
744
|
+
lines.push(' • 挿入した @source: なし(source-packages 未指定)');
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
const entryDir = path.dirname(entryPath);
|
|
748
|
+
const outputDir = path.dirname(outputPath);
|
|
749
|
+
if (path.resolve(entryDir) === path.resolve(outputDir)) {
|
|
750
|
+
lines.push(` • entry CSS と出力先 dir: 一致 (${rel(entryDir)})`);
|
|
751
|
+
} else {
|
|
752
|
+
const suggestedOutput = toPosixPath(path.join(rel(entryDir), 'sparkle-design.css'));
|
|
753
|
+
lines.push(
|
|
754
|
+
` ⚠️ entry CSS dir (${rel(entryDir)}) と出力先 dir (${rel(outputDir)}) が異なります。`
|
|
755
|
+
);
|
|
756
|
+
lines.push(
|
|
757
|
+
` @import は相対パスで解決されるため動作はしますが、出力先を揃えるには ` +
|
|
758
|
+
`--output ${suggestedOutput} の指定、または extend.globals-path と同じ ` +
|
|
759
|
+
`ディレクトリへの出力を検討してください。`
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
} else if (globalsResult.status === 'skipped') {
|
|
763
|
+
lines.push(` • entry CSS パッチ: スキップ (${globalsResult.reason ?? 'no-work'})`);
|
|
764
|
+
} else if (globalsResult.status === 'failed') {
|
|
765
|
+
lines.push(` • entry CSS パッチ: ❌ 失敗 (${globalsResult.reason ?? 'unknown'})`);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
console.log(lines.join('\n'));
|
|
769
|
+
}
|
|
770
|
+
|
|
629
771
|
/**
|
|
630
772
|
* メイン処理
|
|
631
773
|
* @param {string|null} configPath カスタム設定ファイルのパス(オプション)
|
|
@@ -666,18 +808,21 @@ export function generateCSS(
|
|
|
666
808
|
colors
|
|
667
809
|
);
|
|
668
810
|
|
|
811
|
+
// globals-path(CLI `--globals-path` > `extend.globals-path`)を先に解決する。
|
|
812
|
+
// 出力先デフォルトの決定とフォント管理(@source 挿入)の両方で同じ値を使う。
|
|
813
|
+
// en: Resolve globals-path once (CLI over config); both the default output
|
|
814
|
+
// location and the font-import patcher consume it.
|
|
815
|
+
const globalsPathOverride = globalsPath || config['globals-path'] || null;
|
|
816
|
+
|
|
669
817
|
// 6. CSSファイルを書き出し
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
);
|
|
675
|
-
const resolvedOutputPath = outputPath ? path.resolve(outputPath) : defaultOutputPath;
|
|
676
|
-
writeCSS(processedCSS, outputPath);
|
|
818
|
+
// `--output` 未指定時は globals-path と同じディレクトリ(実在する場合)に出す。
|
|
819
|
+
// 詳細は resolveOutputPath / resolveGlobalsDirForOutput の comment 参照(#56)。
|
|
820
|
+
const resolvedOutputPath = resolveOutputPath(outputPath, globalsPathOverride);
|
|
821
|
+
writeCSS(processedCSS, resolvedOutputPath);
|
|
677
822
|
|
|
678
823
|
// 7. SparkleHead.tsx を生成(processTemplate で解決済みの resolvedFonts を再利用)
|
|
679
824
|
const sparkleHeadContent = generateSparkleHeadContent(resolvedFonts);
|
|
680
|
-
writeSparkleHead(sparkleHeadContent, resolvedOutputPath);
|
|
825
|
+
const sparkleHeadPath = writeSparkleHead(sparkleHeadContent, resolvedOutputPath);
|
|
681
826
|
|
|
682
827
|
// 7.5 Vite プロジェクトの index.html に Sparkle のフォント <link> タグを upsert
|
|
683
828
|
// する。Vite は <head> が index.html 側にあるため、React コンポーネントの
|
|
@@ -740,7 +885,6 @@ export function generateCSS(
|
|
|
740
885
|
? [...new Set([...(detectedPackages ?? []), ...(explicitPackages ?? [])])]
|
|
741
886
|
: null;
|
|
742
887
|
const customCssPath = config['custom-css'] || null;
|
|
743
|
-
const globalsPathOverride = globalsPath || config['globals-path'] || null;
|
|
744
888
|
const globalsResult = manageFontImports(
|
|
745
889
|
resolvedOutputPath,
|
|
746
890
|
sourcePackages,
|
|
@@ -749,6 +893,11 @@ export function generateCSS(
|
|
|
749
893
|
{ strict: Boolean(options.strict) }
|
|
750
894
|
);
|
|
751
895
|
|
|
896
|
+
// 9. 生成サマリ(可観測性 / issue #56)。何を・どこに書いたか、@source が
|
|
897
|
+
// 挿入されたか、entry CSS と出力先の dir が乖離していないかを 1 ブロックで
|
|
898
|
+
// 出力する。設定は正しいのに @source が silent に落ちる事象を即検知できる。
|
|
899
|
+
printGenerateSummary({ outputPath: resolvedOutputPath, sparkleHeadPath, globalsResult });
|
|
900
|
+
|
|
752
901
|
console.log(MESSAGES.SUCCESS);
|
|
753
902
|
return { globalsResult };
|
|
754
903
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sparkle-design-cli",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "Sparkle Design CLI — プロジェクトセットアップ、CSS・フォント生成、アンチパターン検査、AI エージェント(Claude Code / Cursor / Codex)向けのガードと hook 設定まで一括で行う sparkle-design 公式 CLI。",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org",
|