Skip to content

Commit 24245da

Browse files
authored
Merge pull request #290 from goodpatch/worktree-fix-registry-theme-self-ref
fix: build-sparkle-design-theme.mjs が @theme inline の自己参照で壊れる問題を修正
2 parents 2ec414c + df48ee2 commit 24245da

1 file changed

Lines changed: 54 additions & 26 deletions

File tree

scripts/build-sparkle-design-theme.mjs

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,39 @@ function sortObjectKeys(obj) {
2121
}
2222

2323
/**
24-
* ブロック抽出ヘルパー: 指定トークン直後の {} をパース
25-
* en: Extracts the brace block following a token (e.g. :root, @theme inline)
24+
* `token` に一致する {} ブロックをすべて抽出する。sparkle-design.css には
25+
* `:root { ... }` が2つある(プリミティブブロックと、実行時に参照できる
26+
* セマンティックトークンブロック)ため、両方を集める必要がある。
27+
* en: Extract every brace block matching `token`. sparkle-design.css
28+
* declares two `:root { ... }` blocks (a primitive block and a
29+
* semantic-token block meant to be usable at runtime), both of which need
30+
* to be collected here.
2631
*/
27-
function extractBraceBlock(source, token) {
28-
const start = source.indexOf(token);
29-
if (start === -1) return null;
30-
const open = source.indexOf("{", start);
31-
if (open === -1) return null;
32-
let depth = 0;
33-
for (let i = open; i < source.length; i++) {
34-
if (source[i] === "{") depth++;
35-
else if (source[i] === "}") {
36-
depth--;
37-
if (depth === 0) {
38-
// return inner content (without braces)
39-
return source.slice(open + 1, i);
32+
function extractAllBraceBlocks(source, token) {
33+
const blocks = [];
34+
let searchFrom = 0;
35+
while (true) {
36+
const start = source.indexOf(token, searchFrom);
37+
if (start === -1) break;
38+
const open = source.indexOf("{", start);
39+
if (open === -1) break;
40+
let depth = 0;
41+
let end = -1;
42+
for (let i = open; i < source.length; i++) {
43+
if (source[i] === "{") depth++;
44+
else if (source[i] === "}") {
45+
depth--;
46+
if (depth === 0) {
47+
end = i;
48+
break;
49+
}
4050
}
4151
}
52+
if (end === -1) break; // unbalanced
53+
blocks.push(source.slice(open + 1, end));
54+
searchFrom = end + 1;
4255
}
43-
return null;
56+
return blocks;
4457
}
4558

4659
/**
@@ -139,15 +152,31 @@ function buildTheme() {
139152
const importRules = parseImports(noComments);
140153

141154
// 2) :root variables
142-
const rootBlock = extractBraceBlock(noComments, ":root");
143-
const rootVars = rootBlock ? parseDeclarations(rootBlock) : {};
144-
145-
// 3) @theme inline variables (same treatment as vars)
146-
const themeInlineBlock = extractBraceBlock(noComments, "@theme inline");
147-
const themeVars = themeInlineBlock ? parseDeclarations(themeInlineBlock) : {};
148-
149-
// Merge vars (semantic overrides primitive)
150-
const cssVarsTheme = { ...rootVars, ...themeVars };
155+
// sparkle-design.css には `:root { ... }` が2つある
156+
// (プリミティブトークンブロックと、実行時に参照できるセマンティックトークン
157+
// ブロック)。どちらも実際に解決可能な値(リテラル、または他の :root 変数への
158+
// 参照)を持つため、両方をマージしてレジストリの cssVars に使う。
159+
//
160+
// `@theme inline` は意図的にここでは使わない: sparkle-design-cli 2.4.1+ の
161+
// `@theme inline` は各セマンティック宣言が同名変数へ自己参照する
162+
// (例: `--color-primary-500: var(--color-primary-500)`)設計になっており、
163+
// これは Tailwind の compiled utility class 生成のためだけの仕組みで、
164+
// レジストリ消費者へそのまま渡すと解決不能な循環参照になってしまう
165+
// (goodpatch/sparkle-design#289 のレビュー指摘)。
166+
// en: sparkle-design.css declares two `:root { ... }` blocks (primitive
167+
// tokens, and semantic tokens meant to be usable at runtime). Both resolve
168+
// to real values (literals or references to other :root vars), so merge
169+
// both for the registry's cssVars.
170+
// `@theme inline` is intentionally NOT used here: as of sparkle-design-cli
171+
// 2.4.1+, every `@theme inline` declaration self-references its own
172+
// semantic variable name (e.g. `--color-primary-500: var(--color-primary-500)`)
173+
// — a Tailwind-compiler-only construct — which would become an unresolvable
174+
// circular reference if handed to registry consumers as-is.
175+
const rootBlocks = extractAllBraceBlocks(noComments, ":root");
176+
const cssVarsTheme = rootBlocks.reduce(
177+
(acc, block) => ({ ...acc, ...parseDeclarations(block) }),
178+
{}
179+
);
151180

152181
// 4) @utility blocks -> css
153182
const utilities = parseUtilities(noComments);
@@ -190,4 +219,3 @@ if (import.meta.url === `file://${process.argv[1]}`) {
190219
}
191220

192221
export default buildTheme;
193-

0 commit comments

Comments
 (0)