Skip to content

Commit 6f599bf

Browse files
authored
[fix] Fix issue with dynamic contextual styles (facebook#1467)
1 parent 560dd8b commit 6f599bf

2 files changed

Lines changed: 147 additions & 2 deletions

File tree

packages/@stylexjs/babel-plugin/__tests__/transform-stylex-create-test.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4865,6 +4865,90 @@ describe('@stylexjs/babel-plugin', () => {
48654865
}
48664866
`);
48674867
});
4868+
4869+
test('media query values with nullish coalescing', () => {
4870+
const { code, metadata } = transform(`
4871+
import * as stylex from '@stylexjs/stylex';
4872+
export const styles = stylex.create({
4873+
root: (a, b, c) => ({
4874+
fontSize: {
4875+
default: a ? '16px' : undefined,
4876+
'@media (min-width: 800px)': b ? '18px' : undefined,
4877+
'@media (min-width: 1280px)': c ? '20px' : undefined,
4878+
}
4879+
}),
4880+
});
4881+
stylex.props(styles.root(true, false, true));
4882+
`);
4883+
expect(code).toMatchInlineSnapshot(`
4884+
"import * as stylex from '@stylexjs/stylex';
4885+
export const styles = {
4886+
root: (a, b, c) => [{
4887+
kGuDYH: ((a ? '16px' : undefined) != null ? "xww4jgc " : a ? '16px' : undefined) + ((b ? '18px' : undefined) != null ? "xqdov8i " : b ? '18px' : undefined) + ((c ? '20px' : undefined) != null ? "x1j86d60" : c ? '20px' : undefined),
4888+
$$css: true
4889+
}, {
4890+
"--x-19zvkyr": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(a ? '16px' : undefined),
4891+
"--x-1bks2es": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(b ? '18px' : undefined),
4892+
"--x-q0n1i6": (val => typeof val === "number" ? val + "px" : val != null ? val : undefined)(c ? '20px' : undefined)
4893+
}]
4894+
};
4895+
stylex.props(styles.root(true, false, true));"
4896+
`);
4897+
expect(metadata).toMatchInlineSnapshot(`
4898+
{
4899+
"stylex": [
4900+
[
4901+
"xww4jgc",
4902+
{
4903+
"ltr": ".xww4jgc{font-size:var(--x-19zvkyr)}",
4904+
"rtl": null,
4905+
},
4906+
3000,
4907+
],
4908+
[
4909+
"xqdov8i",
4910+
{
4911+
"ltr": "@media (min-width: 800px) and (max-width: 1279.99px){.xqdov8i.xqdov8i{font-size:var(--x-1bks2es)}}",
4912+
"rtl": null,
4913+
},
4914+
3200,
4915+
],
4916+
[
4917+
"x1j86d60",
4918+
{
4919+
"ltr": "@media (min-width: 1280px){.x1j86d60.x1j86d60{font-size:var(--x-q0n1i6)}}",
4920+
"rtl": null,
4921+
},
4922+
3200,
4923+
],
4924+
[
4925+
"--x-19zvkyr",
4926+
{
4927+
"ltr": "@property --x-19zvkyr { syntax: "*"; inherits: false;}",
4928+
"rtl": null,
4929+
},
4930+
0,
4931+
],
4932+
[
4933+
"--x-1bks2es",
4934+
{
4935+
"ltr": "@property --x-1bks2es { syntax: "*"; inherits: false;}",
4936+
"rtl": null,
4937+
},
4938+
0,
4939+
],
4940+
[
4941+
"--x-q0n1i6",
4942+
{
4943+
"ltr": "@property --x-q0n1i6 { syntax: "*"; inherits: false;}",
4944+
"rtl": null,
4945+
},
4946+
0,
4947+
],
4948+
],
4949+
}
4950+
`);
4951+
});
48684952
});
48694953
});
48704954

packages/@stylexjs/babel-plugin/src/visitors/stylex-create.js

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,30 @@ function isSafeToSkipNullCheck(expr: t.Expression): boolean {
7373
return false;
7474
}
7575

76+
function hasExplicitNullishFallback(expr: t.Expression): boolean {
77+
if (t.isNullLiteral(expr)) return true;
78+
79+
if (t.isIdentifier(expr) && expr.name === 'undefined') return true;
80+
81+
if (t.isUnaryExpression(expr) && expr.operator === 'void') return true;
82+
83+
if (t.isConditionalExpression(expr)) {
84+
return (
85+
hasExplicitNullishFallback(expr.consequent) ||
86+
hasExplicitNullishFallback(expr.alternate)
87+
);
88+
}
89+
90+
if (t.isLogicalExpression(expr)) {
91+
return (
92+
hasExplicitNullishFallback(expr.left) ||
93+
hasExplicitNullishFallback(expr.right)
94+
);
95+
}
96+
97+
return false;
98+
}
99+
76100
/// This function looks for `stylex.create` calls and transforms them.
77101
/// 1. It finds the first argument to `stylex.create` and validates it.
78102
/// 2. It pre-processes valid-dynamic parts of style object such as custom presets (spreads)
@@ -289,8 +313,9 @@ export default function transformStyleXCreate(
289313
let dynamicStyles: $ReadOnlyArray<{
290314
+expression: t.Expression,
291315
+key: string,
316+
+varName: string,
292317
path: string,
293-
}> = Object.entries(inlineStyles).map(([_key, v]) => ({
318+
}> = Object.entries(inlineStyles).map(([varName, v]) => ({
294319
expression: v.originalExpression,
295320
key: v.path
296321
.slice(
@@ -300,13 +325,21 @@ export default function transformStyleXCreate(
300325
) + 1,
301326
)
302327
.join('_'),
328+
varName,
303329
path: v.path.join('_'),
304330
}));
305331

306332
if (state.options.styleResolution === 'legacy-expand-shorthands') {
307333
dynamicStyles = legacyExpandShorthands(dynamicStyles);
308334
}
309335

336+
const nullishVarExpressions = new Map<string, t.Expression>();
337+
dynamicStyles.forEach((style) => {
338+
if (hasExplicitNullishFallback(style.expression)) {
339+
nullishVarExpressions.set(style.varName, style.expression);
340+
}
341+
});
342+
310343
if (t.isObjectExpression(prop.value)) {
311344
const value: t.ObjectExpression = prop.value;
312345

@@ -348,10 +381,36 @@ export default function transformStyleXCreate(
348381
const exprList: t.Expression[] = [];
349382

350383
classList.forEach((cls, index) => {
351-
const expr = dynamicStyles.find(
384+
let expr = dynamicStyles.find(
352385
({ path }) => origClassPaths[cls] === path,
353386
)?.expression;
354387

388+
if (expr == null && nullishVarExpressions.size > 0) {
389+
const injectedStyle = injectedStyles[cls];
390+
const rule =
391+
injectedStyle != null
392+
? typeof injectedStyle.ltr === 'string'
393+
? injectedStyle.ltr
394+
: typeof injectedStyle.rtl === 'string'
395+
? injectedStyle.rtl
396+
: null
397+
: null;
398+
399+
if (rule != null) {
400+
const matches = rule.matchAll(
401+
/var\((--x-[^,)]+)[^)]*\)/g,
402+
);
403+
404+
for (const match of matches) {
405+
const varExpr = nullishVarExpressions.get(match[1]);
406+
if (varExpr != null) {
407+
expr = varExpr;
408+
break;
409+
}
410+
}
411+
}
412+
}
413+
355414
const isLast = index === classList.length - 1;
356415
const clsWithSpace = isLast ? cls : cls + ' ';
357416

@@ -473,11 +532,13 @@ function legacyExpandShorthands(
473532
dynamicStyles: $ReadOnlyArray<{
474533
+expression: t.Expression,
475534
+key: string,
535+
+varName: string,
476536
path: string,
477537
}>,
478538
): $ReadOnlyArray<{
479539
+expression: t.Expression,
480540
+key: string,
541+
+varName: string,
481542
path: string,
482543
}> {
483544
const expandedKeysToKeyPaths = dynamicStyles

0 commit comments

Comments
 (0)