sparkle-design-cli 2.0.7-rc.2 → 2.0.7-rc.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.
@@ -925,7 +925,7 @@ const ANTI_PATTERN_GROUPS = [
925
925
  check: {
926
926
  description: 'Tailwind デフォルト typography を Sparkle Design コンポーネント内で使わない',
927
927
  recommendation:
928
- 'text-sm / text-xs / text-base / font-medium / font-semibold / font-bold は character-* utility に置き換えてください。',
928
+ '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` コメントを付けて例外扱いとして残すこともできます。',
929
929
  pattern: /\b(text-(?:xs|sm|base|lg|xl|2xl)|font-(?:medium|semibold|bold|normal|light))\b/g,
930
930
  },
931
931
  featureSection: lines([
@@ -937,9 +937,15 @@ const ANTI_PATTERN_GROUPS = [
937
937
  '',
938
938
  '// ❌ Wrong — Tailwind デフォルトの typography を使わない',
939
939
  '<span className="text-sm font-medium">テキスト</span>',
940
+ '',
941
+ '// 例外 — character-* に対応するサイズが無い場合は arbitrary value を使うか',
942
+ '// suppress コメントを付けて残す',
943
+ '<span className="text-[10px] text-text-low">12px 未満の極小メタ情報</span>',
944
+ '{/* sparkle-disable-next-line tailwind-typography */}',
945
+ '<span className="text-xs">どうしても text-xs で残したいケース</span>',
940
946
  '```',
941
947
  '',
942
- 'Sparkle Design コンポーネント内では `character-*-pro` / `character-*-mono` を使用する。Tailwind の `text-sm` / `font-medium` 等は使わない。',
948
+ 'Sparkle Design コンポーネント内では `character-*-pro` / `character-*-mono` を使用する。character-1(12px)より小さい指定や、対応 token が無いサイズは Tailwind の arbitrary value (`text-[10px]` 等) で表現するか、`// sparkle-disable-line tailwind-typography` で個別に例外指定する。',
943
949
  ]),
944
950
  jsdocTargets: [],
945
951
  },
package/lib/check.js CHANGED
@@ -85,8 +85,66 @@ function formatSnippet(text) {
85
85
  return text.replace(/\s+/g, ' ').trim().slice(0, 120);
86
86
  }
87
87
 
88
+ /**
89
+ * ESLint 風の suppression コメントをサポートする。以下のいずれかが findings
90
+ * と同じ行、または直前行にあれば該当 rule の finding は emit しない:
91
+ *
92
+ * // sparkle-disable-line <rule-id>
93
+ * // sparkle-disable-next-line <rule-id>
94
+ * {/* sparkle-disable-line <rule-id> *\/} (JSX コメント)
95
+ *
96
+ * ユーザーがどうしても character-* に移行できない typography(token に
97
+ * 存在しないサイズなど)や、特定 file だけ例外扱いしたい場合の escape
98
+ * hatch として用意する。ルール ID は複数カンマ区切り可(例:
99
+ * `sparkle-disable-line tailwind-typography, card-padding-override`)。
100
+ *
101
+ * en: ESLint-style suppression comments so specific findings can be opted
102
+ * out when character-* / Sparkle tokens don't map cleanly (e.g. sub-token
103
+ * font sizes). Same-line or previous-line comment is honored.
104
+ */
105
+ const SUPPRESS_LINE = /(?:\/\/|\/\*|\{\/\*)\s*sparkle-disable-line\s+([A-Za-z0-9,\s_-]+?)\s*(?:\*\/\}?|$)/;
106
+ const SUPPRESS_NEXT_LINE =
107
+ /(?:\/\/|\/\*|\{\/\*)\s*sparkle-disable-next-line\s+([A-Za-z0-9,\s_-]+?)\s*(?:\*\/\}?|$)/;
108
+
109
+ function extractSuppressedRules(source, regex) {
110
+ if (!source) return null;
111
+ const m = source.match(regex);
112
+ if (!m) return null;
113
+ return new Set(
114
+ m[1]
115
+ .split(',')
116
+ .map((id) => id.trim())
117
+ .filter(Boolean)
118
+ );
119
+ }
120
+
121
+ function isSuppressed(ruleId, contentLines, lineNumber) {
122
+ // lineNumber は 1-origin
123
+ // en: 1-based line numbers from getLineNumber.
124
+ const current = contentLines[lineNumber - 1] ?? '';
125
+ const previous = contentLines[lineNumber - 2] ?? '';
126
+ const sameLine = extractSuppressedRules(current, SUPPRESS_LINE);
127
+ if (sameLine && sameLine.has(ruleId)) return true;
128
+ const prevLine = extractSuppressedRules(previous, SUPPRESS_NEXT_LINE);
129
+ if (prevLine && prevLine.has(ruleId)) return true;
130
+ return false;
131
+ }
132
+
88
133
  function collectFindings(filePath, content) {
89
134
  const findings = [];
135
+ const contentLines = content.split(/\r?\n/);
136
+ const pushFinding = (rule, index, snippet) => {
137
+ const line = getLineNumber(content, index ?? 0);
138
+ if (isSuppressed(rule.id, contentLines, line)) return;
139
+ findings.push({
140
+ filePath,
141
+ id: rule.id,
142
+ description: rule.description,
143
+ recommendation: rule.recommendation,
144
+ line,
145
+ snippet,
146
+ });
147
+ };
90
148
 
91
149
  for (const rule of RULES) {
92
150
  if (typeof rule.match === 'function') {
@@ -94,26 +152,12 @@ function collectFindings(filePath, content) {
94
152
  // 複雑な照合が必要なルール向けの opt-in API。2-pass 方式などで regex 単体の
95
153
  // backtracking リスクを回避したいときに使う。index は content 内の絶対オフセット。
96
154
  for (const hit of rule.match(content)) {
97
- findings.push({
98
- filePath,
99
- id: rule.id,
100
- description: rule.description,
101
- recommendation: rule.recommendation,
102
- line: getLineNumber(content, hit.index ?? 0),
103
- snippet: formatSnippet(hit.text ?? ''),
104
- });
155
+ pushFinding(rule, hit.index ?? 0, formatSnippet(hit.text ?? ''));
105
156
  }
106
157
  continue;
107
158
  }
108
159
  for (const match of content.matchAll(rule.pattern)) {
109
- findings.push({
110
- filePath,
111
- id: rule.id,
112
- description: rule.description,
113
- recommendation: rule.recommendation,
114
- line: getLineNumber(content, match.index ?? 0),
115
- snippet: formatMatch(match),
116
- });
160
+ pushFinding(rule, match.index ?? 0, formatMatch(match));
117
161
  }
118
162
  }
119
163
 
package/lib/setup.js CHANGED
@@ -220,7 +220,7 @@ function buildInstructionBlock(target, assistant) {
220
220
  ' - Sparkle fonts (Google Fonts preconnect + Material Symbols + the configured pro/mono fonts) must be present in the document `<head>`: React layouts via `<SparkleHead />` placed inside `<head>` in the root layout, Vite projects via the managed `<!-- sparkle-design-cli:fonts:start -->` … `end` block in `index.html`.',
221
221
  ' - Do **not** hand-define `--color-primary-*` or other Sparkle tokens as fallbacks when they look missing. Those come from `sparkle-design.css`; missing values mean the `@import` path is wrong (e.g. `./sparkle-design.css` used when the file lives under `./app/`). Fix the import path, do not duplicate the tokens.',
222
222
  ' - If you detect any of the above drifted, re-run `npx --yes sparkle-design-cli generate` before hand-editing. The CLI restores the canonical state (correct relative paths, `@source` placement, `index.html` injection).',
223
- '- **Required**: After creating or modifying any UI component, run `lint:sparkle` before finishing. This catches Sparkle Design anti-patterns.',
223
+ '- **Required**: After creating or modifying any UI component, run `lint:sparkle` before finishing. This catches Sparkle Design anti-patterns. To opt out a specific line from a rule when the Sparkle token does not map cleanly (e.g. sub-12px font sizes for `tailwind-typography`), add `// sparkle-disable-line <rule-id>` on the same line or `// sparkle-disable-next-line <rule-id>` on the line above — but use it sparingly and only when the migration would actually break the design.',
224
224
  `- For AI review, prefer \`lint:sparkle:json\` or run \`npx --yes sparkle-design-cli check ${target} --format json\` directly.`,
225
225
  '- You must inspect every entry in both `findings` and `manualReviewReminders`. For each entry in `manualReviewReminders`, **echo the reminder ID in your final reply together with a short review note** (e.g. `[badge-tag-semantics] reviewed — Badge is used only for counts, so the usage is fine.`). Silence on a reminder means it was skipped; always state at least a one-line judgment per ID. This is not blocked by the Stop hook (reminders are judgment calls), but reviewers / humans rely on the explicit acknowledgment to trust that each item was actually considered.',
226
226
  BLOCK_END,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sparkle-design-cli",
3
- "version": "2.0.7-rc.2",
3
+ "version": "2.0.7-rc.3",
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",