Skip to content

Commit 3dd6ab4

Browse files
cursoragentskovhus
andcommitted
feat(css-vars): resolve var() with default value via adapter inside AST nodes
The var() rewriter only walked plain string values in style objects, so var() calls embedded inside a TemplateLiteral AST node (e.g. inside a styleFn body like `flexShrink: \`var(--x, ${expr})\``) were never passed to adapter.resolveValue. When the adapter resolves a var, the default value is now dropped — the resolved StyleX token supersedes the runtime fallback. Co-authored-by: Kenneth Skovhus <skovhus@users.noreply.github.com>
1 parent e39f77c commit 3dd6ab4

7 files changed

Lines changed: 347 additions & 49 deletions

File tree

src/internal/css-vars.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,21 @@ export function rewriteCssVarsInString(args: {
1818
return rewriteCssVarsInStringImpl(args);
1919
}
2020

21-
type VarCall = {
21+
export function findCssVarCallsInString(raw: string): CssVarCall[] {
22+
return findCssVarCalls(raw);
23+
}
24+
25+
type ExpressionKind = Parameters<JSCodeshift["expressionStatement"]>[0];
26+
27+
type CssVarCall = {
2228
start: number;
2329
end: number;
2430
name: string;
2531
fallback?: string;
2632
};
2733

28-
function findCssVarCalls(raw: string): VarCall[] {
29-
const out: VarCall[] = [];
34+
function findCssVarCalls(raw: string): CssVarCall[] {
35+
const out: CssVarCall[] = [];
3036
let i = 0;
3137
while (i < raw.length) {
3238
const idx = raw.indexOf("var(", i);
@@ -184,5 +190,3 @@ function rewriteCssVarsInStringImpl(args: {
184190
quasis.push(j.templateElement({ raw: q, cooked: q }, true));
185191
return j.templateLiteral(quasis, exprs);
186192
}
187-
188-
type ExpressionKind = Parameters<JSCodeshift["expressionStatement"]>[0];

src/internal/lower-rules/finalize-decl.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export function finalizeDeclProcessing(ctx: DeclProcessingState): void {
5353
} = ctx;
5454
const {
5555
rewriteCssVarsInStyleObject,
56+
rewriteCssVarsInAstNode,
5657
relationOverridePseudoBuckets,
5758
relationOverrides,
5859
ancestorSelectorParents,
@@ -106,6 +107,20 @@ export function finalizeDeclProcessing(ctx: DeclProcessingState): void {
106107

107108
const varsToDrop = new Set<string>();
108109
rewriteCssVarsInStyleObject(styleObj, localVarValues, varsToDrop);
110+
for (const extraObj of extraStyleObjects.values()) {
111+
rewriteCssVarsInStyleObject(extraObj, localVarValues, varsToDrop);
112+
}
113+
for (const variantObj of variantBuckets.values()) {
114+
rewriteCssVarsInStyleObject(variantObj, localVarValues, varsToDrop);
115+
}
116+
// styleFnDecls hold AST nodes (ArrowFunctionExpression bodies). Walking their
117+
// template-literal quasis lets us resolve var() calls embedded inside dynamic
118+
// style functions (e.g. `flexShrink: \`var(--x, ${expr})\``).
119+
for (const fnAst of styleFnDecls.values()) {
120+
if (fnAst && typeof fnAst === "object" && isAstNode(fnAst)) {
121+
rewriteCssVarsInAstNode(fnAst, localVarValues, varsToDrop);
122+
}
123+
}
109124
for (const name of varsToDrop) {
110125
delete (styleObj as any)[name];
111126
}

src/internal/lower-rules/state.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export function createLowerRulesState(ctx: TransformContext) {
4545
keyframesNames,
4646
parseExpr,
4747
rewriteCssVarsInStyleObject,
48+
rewriteCssVarsInAstNode,
4849
} = ctx;
4950
const filePath = file.path;
5051
const resolveValue = ctx.resolveValueSafe;
@@ -262,6 +263,7 @@ export function createLowerRulesState(ctx: TransformContext) {
262263
keyframesNames,
263264
parseExpr,
264265
rewriteCssVarsInStyleObject,
266+
rewriteCssVarsInAstNode,
265267
resolveValue,
266268
resolveValueOptional,
267269
resolveValueDirectional,

src/internal/transform-context.ts

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ import { assertValidAdapter } from "./public-api-validation.js";
1717
import type { WarningLog } from "./logger.js";
1818
import { parseExpr as parseExprImpl } from "./transform-parse-expr.js";
1919
import { createResolveAdapterSafe } from "./transform-resolve-value.js";
20-
import { rewriteCssVarsInStyleObject as rewriteCssVarsInStyleObjectImpl } from "./transform-css-vars.js";
20+
import {
21+
rewriteCssVarsInAstNodeRoot as rewriteCssVarsInAstNodeRootImpl,
22+
rewriteCssVarsInStyleObject as rewriteCssVarsInStyleObjectImpl,
23+
} from "./transform-css-vars.js";
2124
import {
2225
getStaticPropertiesFromImport as getStaticPropertiesFromImportImpl,
2326
patternProp as patternPropImpl,
@@ -53,6 +56,11 @@ export class TransformContext {
5356
definedVars: Map<string, string>,
5457
varsToDrop: Set<string>,
5558
) => void;
59+
rewriteCssVarsInAstNode: (
60+
node: { type: string },
61+
definedVars: Map<string, string>,
62+
varsToDrop: Set<string>,
63+
) => void;
5664

5765
preserveReactImport = false;
5866
styledImports?: any;
@@ -143,20 +151,37 @@ export class TransformContext {
143151

144152
const parseExpr = (exprSource: string): any => parseExprImpl(api, exprSource);
145153

154+
const buildCssVarRewriteContext = (
155+
definedVars: Map<string, string>,
156+
varsToDrop: Set<string>,
157+
) => ({
158+
filePath: file.path,
159+
definedVars,
160+
varsToDrop,
161+
resolveValue: resolveValueSafe,
162+
addImport: (imp: ImportSpec) => resolverImports.set(JSON.stringify(imp), imp),
163+
parseExpr,
164+
j,
165+
});
166+
146167
const rewriteCssVarsInStyleObject = (
147168
obj: Record<string, unknown>,
148169
definedVars: Map<string, string>,
149170
varsToDrop: Set<string>,
150171
): void =>
151172
rewriteCssVarsInStyleObjectImpl({
152173
obj,
153-
filePath: file.path,
154-
definedVars,
155-
varsToDrop,
156-
resolveValue: resolveValueSafe,
157-
addImport: (imp: ImportSpec) => resolverImports.set(JSON.stringify(imp), imp),
158-
parseExpr,
159-
j,
174+
...buildCssVarRewriteContext(definedVars, varsToDrop),
175+
});
176+
177+
const rewriteCssVarsInAstNode = (
178+
node: { type: string },
179+
definedVars: Map<string, string>,
180+
varsToDrop: Set<string>,
181+
): void =>
182+
rewriteCssVarsInAstNodeRootImpl({
183+
node,
184+
...buildCssVarRewriteContext(definedVars, varsToDrop),
160185
});
161186

162187
const patternProp = (keyName: string, valueId?: any) => patternPropImpl(j, keyName, valueId);
@@ -182,6 +207,7 @@ export class TransformContext {
182207
this.getStaticPropertiesFromImport = getStaticPropertiesFromImport;
183208
this.parseExpr = parseExpr;
184209
this.rewriteCssVarsInStyleObject = rewriteCssVarsInStyleObject;
210+
this.rewriteCssVarsInAstNode = rewriteCssVarsInAstNode;
185211
this.styledLocalNames = new Set<string>();
186212
this.isStyledTag = () => false;
187213
this.keyframesNames = new Set<string>();

0 commit comments

Comments
 (0)