sparkle-design-cli 1.3.10 → 1.4.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
@@ -23,7 +23,7 @@ npx sparkle-design-cli check src --strict
23
23
  npx sparkle-design-cli check src --format json
24
24
 
25
25
  # Claude / Codex / Cursor 向けの guard 設定を差し込む
26
- npx sparkle-design-cli setup --assistant claude
26
+ npx --yes sparkle-design-cli setup --assistant claude
27
27
  ```
28
28
 
29
29
  ### 後方互換モード
@@ -125,7 +125,7 @@ sparkle-design-cli check --help
125
125
  ```json
126
126
  {
127
127
  "scripts": {
128
- "lint:sparkle": "sparkle-design-cli check src --strict"
128
+ "lint:sparkle": "npx --yes sparkle-design-cli check src --strict"
129
129
  }
130
130
  }
131
131
  ```
@@ -138,16 +138,16 @@ AI エージェントや CI からこの `lint:sparkle` を呼ぶ運用にする
138
138
 
139
139
  ```bash
140
140
  # Claude Code 向け
141
- sparkle-design-cli setup --assistant claude
141
+ npx --yes sparkle-design-cli setup --assistant claude
142
142
 
143
143
  # Codex / AGENTS.md 向け
144
- sparkle-design-cli setup --assistant codex
144
+ npx --yes sparkle-design-cli setup --assistant codex
145
145
 
146
146
  # Cursor rules 向け
147
- sparkle-design-cli setup --assistant cursor
147
+ npx --yes sparkle-design-cli setup --assistant cursor
148
148
 
149
149
  # generic な Markdown を生成
150
- sparkle-design-cli setup --assistant generic
150
+ npx --yes sparkle-design-cli setup --assistant generic
151
151
  ```
152
152
 
153
153
  追加されるもの:
@@ -178,45 +178,86 @@ sparkle-design-cli setup --assistant generic
178
178
 
179
179
  ### 設定オプション
180
180
 
181
+ #### Core(プラグインが出力)
182
+
181
183
  - `primary`: プライマリカラー(blue, red, orange など)
182
- - `font-pro`: プロポーショナルフォント(Google Fonts の名前)
183
- - `font-mono`: モノスペースフォント(Google Fonts の名前)
184
+ - `font-pro`: プロポーショナルフォント(文字列または配列。配列の場合はフォールバックチェーンになる)
185
+ - `font-mono`: モノスペースフォント(文字列または配列)
184
186
  - `radius`: 角丸設定(sm, md, lg など)
187
+
188
+ #### Core Options
189
+
190
+ - `font-pro-weights`: (オプション)プロポーショナルフォントのウェイト配列(デフォルト: `[400, 700]`)
191
+ - `font-mono-weights`: (オプション)モノスペースフォントのウェイト配列(デフォルト: `[400, 700]`)
185
192
  - `source-packages`: (オプション)`@source` ディレクティブで追加スキャンする npm パッケージの配列
186
193
 
187
- ### source-packages オプション
194
+ #### Custom Settings
188
195
 
189
- `@goodpatch/sparkle-design` を npm パッケージとして利用するプロジェクトでは、TailwindCSS v4 がパッケージ内のユーティリティクラスを検出できるように `@source` ディレクティブが必要です。
196
+ - `custom-css`: (オプション)プロジェクト固有のカスタムトークン CSS ファイルのパス。`globals.css` に `@import` が自動挿入される
190
197
 
191
- `source-packages` を設定すると、CLI が `globals.css` に `@source` ディレクティブを自動挿入します。`@goodpatch/sparkle-design` は常にデフォルトで含まれます。
198
+ ### フォントウェイトとフォールバック
199
+
200
+ `font-pro-weights` / `font-mono-weights` でフォントウェイトをカスタマイズできます。未指定時は `[400, 700]` がデフォルトです。
201
+
202
+ `font-pro` / `font-mono` に配列を指定すると、フォントフォールバックチェーンとして複数の Google Fonts import が生成されます。
192
203
 
193
204
  ```json
194
205
  {
195
206
  "primary": "blue",
196
- "font-mono": "BIZ UDGothic",
197
- "font-pro": "BIZ UDPGothic",
198
- "radius": "md",
199
- "source-packages": []
207
+ "font-pro": ["Montserrat", "Noto Sans JP"],
208
+ "font-pro-weights": [400, 500, 600, 700],
209
+ "font-mono": "Roboto Mono",
210
+ "font-mono-weights": [400, 700],
211
+ "radius": "md"
200
212
  }
201
213
  ```
202
214
 
203
- 追加の npm パッケージ(拡張リポジトリなど)をスキャン対象にする場合:
215
+ 生成される CSS:
216
+
217
+ ```css
218
+ --font-family-pro: 'Montserrat', 'Noto Sans JP', sans-serif;
219
+ ```
220
+
221
+ ```css
222
+ @import 'https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap';
223
+ @import 'https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;600;700&display=swap';
224
+ ```
225
+
226
+ ### custom-css オプション
227
+
228
+ プロジェクト固有のカスタムトークン(Display, Heading, Body 等のタイポグラフィトークン、追加のフォントウェイト、メディアクエリなど)を `sparkle-design.css` に直接追加するのは避けてください。`generate` 実行時に上書きされます。
229
+
230
+ 代わりに、カスタムトークンを別の CSS ファイルに定義し、`custom-css` で指定してください:
204
231
 
205
232
  ```json
206
233
  {
207
- "source-packages": ["@scope/my-extension-design"]
234
+ "custom-css": "./src/app/custom-tokens.css"
208
235
  }
209
236
  ```
210
237
 
211
- 生成される `globals.css`:
238
+ CLI が `globals.css` に `@import` を自動挿入します。CLI はファイルの中身を一切触りません。
212
239
 
213
240
  ```css
214
- @import 'tailwindcss';
215
- /* npm パッケージのコンテンツスキャン(Tailwind がクラスを検出するために必要) */
216
- @source "../node_modules/@goodpatch/sparkle-design/dist";
217
- @source "../node_modules/@scope/my-extension-design/dist";
218
- /* Sparkle Design のカスタム定義(Tailwindの後にインポート) */
219
- @import './sparkle-design.css';
241
+ /* globals.css に自動生成される構造 */
242
+ /* フォントのインポート */
243
+ @import '...google fonts...';
244
+ @import "tailwindcss";
245
+ /* Sparkle Design のカスタム定義 */
246
+ @import "./sparkle-design.css";
247
+ /* プロジェクト固有のカスタムトークン */
248
+ @import "./custom-tokens.css";
249
+ ```
250
+
251
+ ### source-packages オプション
252
+
253
+ `@goodpatch/sparkle-design` を npm パッケージとして利用するプロジェクトでは、TailwindCSS v4 がパッケージ内のユーティリティクラスを検出できるように `@source` ディレクティブが必要です。
254
+
255
+ `source-packages` を設定すると、CLI が `globals.css` に `@source` ディレクティブを自動挿入します。`@goodpatch/sparkle-design` は常にデフォルトで含まれます。
256
+
257
+ ```json
258
+ {
259
+ "source-packages": ["@scope/my-extension-design"]
260
+ }
220
261
  ```
221
262
 
222
263
  > **注意**: `source-packages` を指定しない場合、`@source` ディレクティブは生成されません。
package/lib/constants.js CHANGED
@@ -37,8 +37,13 @@ export const REGEX = {
37
37
  SOURCE_DIRECTIVE: /@source\s+["'][^"']*["'];?\s*\n?/g,
38
38
  SOURCE_COMMENT: /\/\*\s*npm パッケージのコンテンツスキャン[^*]*\*\/\s*\n?/g,
39
39
 
40
+ // カスタムCSS関連
41
+ CUSTOM_CSS_IMPORT:
42
+ /\/\*\s*プロジェクト固有のカスタムトークン[^*]*\*\/\s*\n?|@import\s+['"][^"']*custom[^"']*['"];?\s*\n?/g,
43
+
40
44
  // テンプレート関連
41
45
  COLOR_TOKENS_PLACEHOLDER: /[ \t]*\/\* \{\{COLOR_TOKENS\}\} \*\/[ \t]*\n?/,
46
+ FONT_IMPORTS_PLACEHOLDER: /[ \t]*\/\* \{\{FONT_IMPORTS\}\} \*\/[ \t]*\n?/,
42
47
  RADIUS_SEMANTIC_PATTERN: (semanticName) =>
43
48
  new RegExp(`--radius-${semanticName}:\\s*var\\(--radius-[^)]+\\)`, 'g'),
44
49
  };
@@ -46,6 +51,20 @@ export const REGEX = {
46
51
  // テンプレートプレースホルダー
47
52
  export const PLACEHOLDERS = {
48
53
  COLOR_TOKENS: '{{COLOR_TOKENS}}',
54
+ FONT_IMPORTS: '{{FONT_IMPORTS}}',
55
+ FONT_FAMILY_PRO: '{{FONT_FAMILY_PRO}}',
56
+ FONT_FAMILY_MONO: '{{FONT_FAMILY_MONO}}',
57
+ };
58
+
59
+ // フォントのデフォルト設定
60
+ export const FONT_DEFAULTS = {
61
+ WEIGHTS: [400, 700],
62
+ MATERIAL_SYMBOLS_IMPORT:
63
+ "@import 'https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,0..200&display=block';",
64
+ GENERIC_FAMILY: {
65
+ pro: 'sans-serif',
66
+ mono: 'monospace',
67
+ },
49
68
  };
50
69
 
51
70
  // カラー変換精度
@@ -76,6 +95,7 @@ export const COMMENTS = {
76
95
  SPARKLE_IMPORT: '/* Sparkle Design のカスタム定義(Tailwindの後にインポート) */',
77
96
  TAILWIND_IMPORT: '/* Tailwindのインポート */',
78
97
  SOURCE_DIRECTIVE: '/* npm パッケージのコンテンツスキャン(Tailwind がクラスを検出するために必要) */',
98
+ CUSTOM_CSS: '/* プロジェクト固有のカスタムトークン */',
79
99
  };
80
100
 
81
101
  // インポート文のテンプレート
@@ -5,7 +5,7 @@
5
5
 
6
6
  import fs from 'fs';
7
7
  import path from 'path';
8
- import { REGEX, COMMENTS, IMPORTS, MESSAGES } from './constants.js';
8
+ import { REGEX, COMMENTS, IMPORTS, MESSAGES, FONT_DEFAULTS } from './constants.js';
9
9
 
10
10
  /**
11
11
  * sparkle-design.css からフォントの@import文を抽出する
@@ -72,18 +72,56 @@ function createSourceBlock(sourcePackages = [], nodeModulesRel = '../node_module
72
72
  return [COMMENTS.SOURCE_DIRECTIVE, ...sourceLines].join('\n');
73
73
  }
74
74
 
75
+ /**
76
+ * カスタムCSS importブロックを生成する
77
+ * @param {string} customCssPath custom-css ファイルの相対パス
78
+ * @param {string} globalsPath globals.css の絶対パス(相対パス計算用)
79
+ * @returns {string} カスタムCSS importブロック
80
+ */
81
+ function createCustomCssImportBlock(customCssPath, globalsPath) {
82
+ if (!customCssPath) {
83
+ return '';
84
+ }
85
+
86
+ // custom-css パスは sparkle.config.json からの相対パス(= プロジェクトルートからの相対パス)
87
+ // globals.css からの相対パスに変換
88
+ const globalsDir = path.dirname(globalsPath);
89
+ const absoluteCustomPath = path.resolve(process.cwd(), customCssPath);
90
+ let relativePath = path.relative(globalsDir, absoluteCustomPath).split(path.sep).join('/');
91
+
92
+ // 相対パスが ./ で始まらない場合は付与
93
+ if (!relativePath.startsWith('.')) {
94
+ relativePath = `./${relativePath}`;
95
+ }
96
+
97
+ return [COMMENTS.CUSTOM_CSS, `@import "${relativePath}";`].join('\n');
98
+ }
99
+
75
100
  /**
76
101
  * sparkle-design.css importブロックを生成する
77
102
  * @param {Array<string>} sourcePackages 追加パッケージ名の配列
78
103
  * @param {string} globalsPath globals.css の絶対パス(@source パス計算用)
104
+ * @param {string|null} customCssPath custom-css ファイルの相対パス
79
105
  * @returns {string} sparkle-design.css importブロック
80
106
  */
81
- function createSparkleImportBlock(sourcePackages = [], globalsPath = null) {
82
- if (sourcePackages === null || sourcePackages === undefined) {
83
- return ['', COMMENTS.SPARKLE_IMPORT, IMPORTS.SPARKLE_DESIGN, ''].join('\n');
107
+ function createSparkleImportBlock(sourcePackages = [], globalsPath = null, customCssPath = null) {
108
+ const parts = [''];
109
+
110
+ if (sourcePackages !== null && sourcePackages !== undefined) {
111
+ const nodeModulesRel = globalsPath ? resolveNodeModulesRelPath(globalsPath) : '../node_modules';
112
+ parts.push(createSourceBlock(sourcePackages, nodeModulesRel));
113
+ }
114
+
115
+ parts.push(COMMENTS.SPARKLE_IMPORT, IMPORTS.SPARKLE_DESIGN);
116
+
117
+ // カスタムCSS import を sparkle-design.css の後に配置
118
+ const customBlock = createCustomCssImportBlock(customCssPath, globalsPath);
119
+ if (customBlock) {
120
+ parts.push(customBlock);
84
121
  }
85
- const nodeModulesRel = globalsPath ? resolveNodeModulesRelPath(globalsPath) : '../node_modules';
86
- return ['', createSourceBlock(sourcePackages, nodeModulesRel), COMMENTS.SPARKLE_IMPORT, IMPORTS.SPARKLE_DESIGN, ''].join('\n');
122
+
123
+ parts.push('');
124
+ return parts.join('\n');
87
125
  }
88
126
 
89
127
  /**
@@ -104,6 +142,9 @@ function removeExistingImports(globalsContent) {
104
142
  // 既存のsparkle-design.css importを削除
105
143
  cleaned = cleaned.replace(REGEX.SPARKLE_IMPORT, '');
106
144
 
145
+ // 既存のカスタムCSS importを削除
146
+ cleaned = cleaned.replace(REGEX.CUSTOM_CSS_IMPORT, '');
147
+
107
148
  return cleaned;
108
149
  }
109
150
 
@@ -159,9 +200,10 @@ function reconstructGlobalsCss(globalsContent, fontImportBlock, sparkleImportBlo
159
200
  * @param {Array<string>} fontImports フォントimport文の配列
160
201
  * @param {string} globalsPath globals.cssのパス
161
202
  * @param {Array<string>|null} sourcePackages 追加パッケージ名の配列(null の場合は @source を生成しない)
203
+ * @param {string|null} customCssPath custom-css ファイルの相対パス
162
204
  * @returns {boolean} globals.css の更新に成功した場合は true
163
205
  */
164
- export function updateGlobalsWithFonts(fontImports, globalsPath, sourcePackages = null) {
206
+ export function updateGlobalsWithFonts(fontImports, globalsPath, sourcePackages = null, customCssPath = null) {
165
207
  if (fontImports.length === 0) {
166
208
  return false;
167
209
  }
@@ -182,7 +224,7 @@ export function updateGlobalsWithFonts(fontImports, globalsPath, sourcePackages
182
224
 
183
225
  // 4. importブロックを生成
184
226
  const fontImportBlock = createFontImportBlock(fontImports);
185
- const sparkleImportBlock = createSparkleImportBlock(sourcePackages, globalsPath);
227
+ const sparkleImportBlock = createSparkleImportBlock(sourcePackages, globalsPath, customCssPath);
186
228
 
187
229
  // 5. globals.css を再構築
188
230
  const reconstructedContent = reconstructGlobalsCss(
@@ -208,8 +250,9 @@ export function updateGlobalsWithFonts(fontImports, globalsPath, sourcePackages
208
250
  * sparkle-design.css からフォントimportを抽出し、globals.css に移動する
209
251
  * @param {string} sparkleDesignPath sparkle-design.cssのパス
210
252
  * @param {Array<string>|null} sourcePackages 追加パッケージ名の配列(null の場合は @source を生成しない)
253
+ * @param {string|null} customCssPath custom-css ファイルの相対パス
211
254
  */
212
- export function manageFontImports(sparkleDesignPath, sourcePackages = null) {
255
+ export function manageFontImports(sparkleDesignPath, sourcePackages = null, customCssPath = null) {
213
256
  try {
214
257
  // 1. globals.cssのパスを推定(sparkle-design.cssと同じディレクトリ)
215
258
  const globalsPath = path.join(path.dirname(sparkleDesignPath), 'globals.css');
@@ -232,7 +275,7 @@ export function manageFontImports(sparkleDesignPath, sourcePackages = null) {
232
275
  console.log(MESSAGES.FONT_DETECTED(fontImports.length));
233
276
 
234
277
  // 4. globals.css にフォントimportを追加
235
- const globalsUpdated = updateGlobalsWithFonts(fontImports, globalsPath, sourcePackages);
278
+ const globalsUpdated = updateGlobalsWithFonts(fontImports, globalsPath, sourcePackages, customCssPath);
236
279
 
237
280
  if (!globalsUpdated) {
238
281
  console.log(MESSAGES.FONT_REMOVE_SKIPPED);
@@ -7,7 +7,7 @@
7
7
 
8
8
  import fs from 'fs';
9
9
  import path from 'path';
10
- import { REGEX, PATHS, MESSAGES } from './constants.js';
10
+ import { REGEX, PATHS, MESSAGES, FONT_DEFAULTS } from './constants.js';
11
11
  import {
12
12
  loadConfig,
13
13
  loadTemplate,
@@ -23,6 +23,96 @@ import {
23
23
  updateGlobalsWithFonts,
24
24
  } from './font-manager.js';
25
25
 
26
+ /**
27
+ * 同一のフォント @import を重複排除する
28
+ * @param {string} cssContent CSS内容
29
+ * @returns {string} 重複排除後のCSS
30
+ */
31
+ function dedupeFontImports(cssContent) {
32
+ const seen = new Set();
33
+
34
+ return cssContent
35
+ .split('\n')
36
+ .filter(line => {
37
+ if (!line.includes('fonts.googleapis.com')) {
38
+ return true;
39
+ }
40
+
41
+ if (seen.has(line)) {
42
+ return false;
43
+ }
44
+
45
+ seen.add(line);
46
+ return true;
47
+ })
48
+ .join('\n');
49
+ }
50
+
51
+ /**
52
+ * フォント設定を正規化する(文字列 → 配列)
53
+ * @param {string|string[]} fontValue フォント名(文字列または配列)
54
+ * @returns {string[]} フォント名の配列
55
+ */
56
+ function normalizeFontConfig(fontValue) {
57
+ if (Array.isArray(fontValue)) {
58
+ return fontValue;
59
+ }
60
+ return [fontValue];
61
+ }
62
+
63
+ /**
64
+ * Google Fonts の @import 行を生成する
65
+ * @param {string[]} fonts フォント名の配列
66
+ * @param {number[]} weights ウェイトの配列
67
+ * @returns {string[]} @import 文の配列
68
+ */
69
+ function generateGoogleFontImports(fonts, weights) {
70
+ const weightsStr = weights.sort((a, b) => a - b).join(';');
71
+ return fonts.map(font => {
72
+ const urlName = font.replace(/\s+/g, '+');
73
+ return `@import 'https://fonts.googleapis.com/css2?family=${urlName}:wght@${weightsStr}&display=swap';`;
74
+ });
75
+ }
76
+
77
+ /**
78
+ * フォントファミリーの CSS 値を生成する
79
+ * @param {string[]} fonts フォント名の配列
80
+ * @param {string} genericFamily ジェネリックファミリー(sans-serif / monospace)
81
+ * @returns {string} CSS font-family 値(例: 'Montserrat', 'Noto Sans JP', sans-serif)
82
+ */
83
+ function generateFontFamilyValue(fonts, genericFamily) {
84
+ const quoted = fonts.map(f => `'${f}'`);
85
+ return [...quoted, genericFamily].join(', ');
86
+ }
87
+
88
+ /**
89
+ * フォント import ブロック全体を生成する(Material Symbols + PRO + MONO)
90
+ * @param {Object} config 設定オブジェクト
91
+ * @returns {string} フォント import ブロック
92
+ */
93
+ function generateFontImportsBlock(config) {
94
+ const proFonts = normalizeFontConfig(config['font-pro']);
95
+ const monoFonts = normalizeFontConfig(config['font-mono']);
96
+ const proWeights = config['font-pro-weights'] || FONT_DEFAULTS.WEIGHTS;
97
+ const monoWeights = config['font-mono-weights'] || FONT_DEFAULTS.WEIGHTS;
98
+
99
+ const imports = [
100
+ FONT_DEFAULTS.MATERIAL_SYMBOLS_IMPORT,
101
+ ...generateGoogleFontImports(proFonts, proWeights),
102
+ ...generateGoogleFontImports(monoFonts, monoWeights),
103
+ ];
104
+
105
+ // 重複排除(font-pro と font-mono が同じフォントでウェイトも同じ場合)
106
+ const seen = new Set();
107
+ const deduped = imports.filter(line => {
108
+ if (seen.has(line)) return false;
109
+ seen.add(line);
110
+ return true;
111
+ });
112
+
113
+ return deduped.join('\n');
114
+ }
115
+
26
116
  /**
27
117
  * テンプレート変数を設定値で置換する
28
118
  * @param {string} template CSSテンプレート
@@ -40,8 +130,24 @@ function processTemplate(template, config, grayMapping, radiusMapping, colors) {
40
130
  // プレースホルダーを色のトークンで置換(改行を含む)
41
131
  processedCSS = processedCSS.replace(REGEX.COLOR_TOKENS_PLACEHOLDER, `${colorTokens}\n`);
42
132
 
43
- // 2. 基本的な設定値による置換
133
+ // 2. フォント import ブロックを生成して置換
134
+ const fontImportsBlock = generateFontImportsBlock(config);
135
+ processedCSS = processedCSS.replace(REGEX.FONT_IMPORTS_PLACEHOLDER, `${fontImportsBlock}\n`);
136
+
137
+ // 3. フォントファミリー CSS 値を生成して置換
138
+ const proFonts = normalizeFontConfig(config['font-pro']);
139
+ const monoFonts = normalizeFontConfig(config['font-mono']);
140
+ const proFamilyValue = generateFontFamilyValue(proFonts, FONT_DEFAULTS.GENERIC_FAMILY.pro);
141
+ const monoFamilyValue = generateFontFamilyValue(monoFonts, FONT_DEFAULTS.GENERIC_FAMILY.mono);
142
+ processedCSS = processedCSS.replace(/\{\{FONT_FAMILY_PRO\}\}/g, proFamilyValue);
143
+ processedCSS = processedCSS.replace(/\{\{FONT_FAMILY_MONO\}\}/g, monoFamilyValue);
144
+
145
+ // 4. 基本的な設定値による置換(font-pro / font-mono の配列以外)
44
146
  Object.entries(config).forEach(([key, value]) => {
147
+ // 配列や font-*-weights はスキップ
148
+ if (Array.isArray(value) || key.endsWith('-weights') || key === 'custom-css') {
149
+ return;
150
+ }
45
151
  // 通常のプレースホルダー(CSS用 - スペースはそのまま)
46
152
  const placeholder = `{{${key.toUpperCase().replace('-', '_')}}}`;
47
153
  processedCSS = processedCSS.replace(new RegExp(placeholder, 'g'), value);
@@ -54,7 +160,7 @@ function processTemplate(template, config, grayMapping, radiusMapping, colors) {
54
160
  }
55
161
  });
56
162
 
57
- // 3. Radiusの一括置換
163
+ // 6. Radiusの一括置換
58
164
  if (config.radius && radiusMapping[config.radius]) {
59
165
  const radiusValues = radiusMapping[config.radius];
60
166
 
@@ -71,6 +177,8 @@ function processTemplate(template, config, grayMapping, radiusMapping, colors) {
71
177
  console.log(MESSAGES.RADIUS_APPLIED(config.radius));
72
178
  }
73
179
 
180
+ processedCSS = dedupeFontImports(processedCSS);
181
+
74
182
  console.log(MESSAGES.TEMPLATE_PROCESSED);
75
183
  return processedCSS;
76
184
  }
@@ -138,7 +246,8 @@ export function generateCSS(configPath = null, outputPath = null) {
138
246
  // 6. フォント管理の自動処理を実行
139
247
  console.log(MESSAGES.FONT_MANAGEMENT_START);
140
248
  const sourcePackages = 'source-packages' in config ? (config['source-packages'] || []) : null;
141
- manageFontImports(resolvedOutputPath, sourcePackages);
249
+ const customCssPath = config['custom-css'] || null;
250
+ manageFontImports(resolvedOutputPath, sourcePackages, customCssPath);
142
251
 
143
252
  console.log(MESSAGES.SUCCESS);
144
253
  }
@@ -163,6 +272,12 @@ export {
163
272
  hexToRgba,
164
273
  hexToOklch,
165
274
  generateColorTokens,
275
+ dedupeFontImports,
276
+ // フォント処理
277
+ normalizeFontConfig,
278
+ generateGoogleFontImports,
279
+ generateFontFamilyValue,
280
+ generateFontImportsBlock,
166
281
  // フォント管理
167
282
  extractFontImports,
168
283
  removeFontImportsFromCSS,
package/lib/setup.js CHANGED
@@ -21,6 +21,11 @@ const BLOCK_END = '<!-- sparkle-design-cli:setup:end -->';
21
21
  const TARGET_CANDIDATES = ['src', 'src/components', 'src/features', 'app', 'components'];
22
22
  const UNSAFE_TARGET_PATTERN = /[\s"'`;$&|<>()[\]{}\\]/;
23
23
 
24
+ function buildCheckScript(target, mode) {
25
+ const trailingArgs = mode === 'strict' ? '--strict' : '--format json';
26
+ return `npx --yes sparkle-design-cli check ${target} ${trailingArgs}`;
27
+ }
28
+
24
29
 
25
30
  function escapeRegExp(value) {
26
31
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
@@ -91,11 +96,18 @@ function isManagedSparkleScript(value, mode) {
91
96
  return false;
92
97
  }
93
98
 
94
- const tokens = value.trim().split(/\s+/);
99
+ let tokens = value.trim().split(/\s+/);
95
100
  if (tokens.length < 3) {
96
101
  return false;
97
102
  }
98
103
 
104
+ if (tokens[0] === 'npx') {
105
+ tokens = tokens.slice(1);
106
+ if (tokens[0] === '--yes') {
107
+ tokens = tokens.slice(1);
108
+ }
109
+ }
110
+
99
111
  if (tokens[0] !== 'sparkle-design-cli' || tokens[1] !== 'check') {
100
112
  return false;
101
113
  }
@@ -169,13 +181,13 @@ function updatePackageJson(filePath, target, force) {
169
181
 
170
182
  const strictResult = resolveScriptUpdate(
171
183
  scripts['lint:sparkle'],
172
- `sparkle-design-cli check ${target} --strict`,
184
+ buildCheckScript(target, 'strict'),
173
185
  'strict',
174
186
  force
175
187
  );
176
188
  const jsonResult = resolveScriptUpdate(
177
189
  scripts['lint:sparkle:json'],
178
- `sparkle-design-cli check ${target} --format json`,
190
+ buildCheckScript(target, 'json'),
179
191
  'json',
180
192
  force
181
193
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sparkle-design-cli",
3
- "version": "1.3.10",
3
+ "version": "1.4.0",
4
4
  "description": "Sparkle Design CSS Generator - デザインシステムCSSを設定ファイルから生成するツール",
5
5
  "main": "lib/generate-css.js",
6
6
  "type": "module",
@@ -1,7 +1,5 @@
1
1
  /* フォントのインポート */
2
- @import 'https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,0..200&display=block';
3
- @import 'https://fonts.googleapis.com/css2?family={{FONT_PRO_URL}}:wght@400;700&display=swap';
4
- @import 'https://fonts.googleapis.com/css2?family={{FONT_MONO_URL}}:wght@400;700&display=swap';
2
+ /* {{FONT_IMPORTS}} */
5
3
 
6
4
  /* Primitive Tokens */
7
5
  :root {
@@ -21,8 +19,8 @@
21
19
 
22
20
  /* タイポグラフィ変数 */
23
21
  /* フォントファミリー */
24
- --font-family-pro: '{{FONT_PRO}}', sans-serif;
25
- --font-family-mono: '{{FONT_MONO}}', monospace;
22
+ --font-family-pro: {{FONT_FAMILY_PRO}};
23
+ --font-family-mono: {{FONT_FAMILY_MONO}};
26
24
  --font-family-icon: 'Material Symbols Rounded';
27
25
 
28
26
  /* フォントサイズ */