styled-components-to-stylex-codemod 0.0.26 → 0.0.27

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.
Files changed (2) hide show
  1. package/dist/transform.mjs +594 -243
  2. package/package.json +1 -1
@@ -591,6 +591,51 @@ function literalToString(node) {
591
591
  return typeof v === "string" ? v : null;
592
592
  }
593
593
  /**
594
+ * Scans the file AST for TypeScript string/numeric enum declarations and
595
+ * returns a two-level map: enumName -> memberName -> static value.
596
+ * Only handles members with literal initializers.
597
+ */
598
+ function buildEnumValueMap(root, j) {
599
+ const map = /* @__PURE__ */ new Map();
600
+ root.find(j.TSEnumDeclaration).forEach((path) => {
601
+ const enumNode = path.value;
602
+ const enumName = enumNode.id.name;
603
+ const members = /* @__PURE__ */ new Map();
604
+ let nextNumericValue = 0;
605
+ for (const member of enumNode.members) {
606
+ const memberName = member.id.type === "Identifier" ? member.id.name : member.id.value;
607
+ if (member.initializer) {
608
+ const val = literalToStaticValue(member.initializer);
609
+ if (val !== null) {
610
+ members.set(memberName, val);
611
+ nextNumericValue = typeof val === "number" ? val + 1 : 0;
612
+ }
613
+ } else {
614
+ members.set(memberName, nextNumericValue);
615
+ nextNumericValue++;
616
+ }
617
+ }
618
+ if (members.size > 0) map.set(enumName, members);
619
+ });
620
+ return map;
621
+ }
622
+ /**
623
+ * Resolves a MemberExpression node (e.g., `ProgressType.success`) to its
624
+ * static enum value using a pre-built enum map. Returns null if the node
625
+ * is not a MemberExpression or doesn't reference a known enum member.
626
+ */
627
+ function resolveStaticExpressionValue(node, enumValueMap) {
628
+ const v = literalToStaticValue(node);
629
+ if (v !== null) return v;
630
+ if (!enumValueMap || !node || typeof node !== "object") return null;
631
+ const n = node;
632
+ if (n.type !== "MemberExpression" || n.computed) return null;
633
+ const obj = n.object;
634
+ const prop = n.property;
635
+ if (obj?.type !== "Identifier" || !obj.name || prop?.type !== "Identifier" || !prop.name) return null;
636
+ return enumValueMap.get(obj.name)?.get(prop.name) ?? null;
637
+ }
638
+ /**
594
639
  * Returns true if an AST node represents an "empty" CSS branch value.
595
640
  * Styled-components treats falsy interpolations as "omit this declaration".
596
641
  *
@@ -1360,7 +1405,7 @@ const isValidIdentifier = (name) => /^[$A-Z_][0-9A-Z_$]*$/i.test(name);
1360
1405
  * - Disjunction: `"a || b"` → `a || b`
1361
1406
  * - Grouped negation: `"!(a || b)"` → `!(a || b)`
1362
1407
  */
1363
- function parseVariantWhenToAst(j, when) {
1408
+ function parseVariantWhenToAst(j, when, booleanProps) {
1364
1409
  const buildMemberExpr = (raw) => {
1365
1410
  if (!raw.includes(".")) return null;
1366
1411
  const parts = raw.split(".").map((part) => part.trim()).filter(Boolean);
@@ -1414,7 +1459,7 @@ function parseVariantWhenToAst(j, when) {
1414
1459
  isBoolean: true
1415
1460
  };
1416
1461
  if (trimmed.startsWith("!(") && trimmed.endsWith(")")) {
1417
- const innerParsed = parseVariantWhenToAst(j, trimmed.slice(2, -1).trim());
1462
+ const innerParsed = parseVariantWhenToAst(j, trimmed.slice(2, -1).trim(), booleanProps);
1418
1463
  return {
1419
1464
  cond: j.unaryExpression("!", innerParsed.cond),
1420
1465
  props: innerParsed.props,
@@ -1422,7 +1467,7 @@ function parseVariantWhenToAst(j, when) {
1422
1467
  };
1423
1468
  }
1424
1469
  if (trimmed.includes("&&")) {
1425
- const parsed = trimmed.split("&&").map((s) => s.trim()).filter(Boolean).map((p) => parseVariantWhenToAst(j, p));
1470
+ const parsed = trimmed.split("&&").map((s) => s.trim()).filter(Boolean).map((p) => parseVariantWhenToAst(j, p, booleanProps));
1426
1471
  const firstParsed = parsed[0];
1427
1472
  if (!firstParsed) return {
1428
1473
  cond: j.identifier("true"),
@@ -1436,7 +1481,7 @@ function parseVariantWhenToAst(j, when) {
1436
1481
  };
1437
1482
  }
1438
1483
  if (trimmed.includes(" || ")) {
1439
- const parsed = trimmed.split(" || ").map((s) => s.trim()).filter(Boolean).map((p) => parseVariantWhenToAst(j, p));
1484
+ const parsed = trimmed.split(" || ").map((s) => s.trim()).filter(Boolean).map((p) => parseVariantWhenToAst(j, p, booleanProps));
1440
1485
  const firstParsedOr = parsed[0];
1441
1486
  if (!firstParsedOr) return {
1442
1487
  cond: j.identifier("true"),
@@ -1450,7 +1495,7 @@ function parseVariantWhenToAst(j, when) {
1450
1495
  };
1451
1496
  }
1452
1497
  if (trimmed.startsWith("!")) {
1453
- const innerParsed = parseVariantWhenToAst(j, trimmed.slice(1).trim());
1498
+ const innerParsed = parseVariantWhenToAst(j, trimmed.slice(1).trim(), booleanProps);
1454
1499
  return {
1455
1500
  cond: j.unaryExpression("!", innerParsed.cond),
1456
1501
  props: innerParsed.props,
@@ -1471,10 +1516,11 @@ function parseVariantWhenToAst(j, when) {
1471
1516
  };
1472
1517
  }
1473
1518
  const simple = parsePropRef(trimmed);
1519
+ const propIsBoolean = !!(simple.propName && booleanProps?.has(simple.propName));
1474
1520
  return {
1475
1521
  cond: simple.expr,
1476
1522
  props: simple.propName ? [simple.propName] : [],
1477
- isBoolean: false
1523
+ isBoolean: propIsBoolean
1478
1524
  };
1479
1525
  }
1480
1526
  /**
@@ -1483,8 +1529,8 @@ function parseVariantWhenToAst(j, when) {
1483
1529
  * function's parameter destructuring.
1484
1530
  */
1485
1531
  function collectConditionProps(j, args) {
1486
- const { when, destructureProps } = args;
1487
- const parsed = parseVariantWhenToAst(j, when);
1532
+ const { when, destructureProps, booleanProps } = args;
1533
+ const parsed = parseVariantWhenToAst(j, when, booleanProps);
1488
1534
  if (destructureProps) {
1489
1535
  for (const p of parsed.props) if (p && !destructureProps.includes(p)) destructureProps.push(p);
1490
1536
  }
@@ -1492,8 +1538,9 @@ function collectConditionProps(j, args) {
1492
1538
  }
1493
1539
  /**
1494
1540
  * Creates a conditional style expression that's safe for stylex.props().
1495
- * For boolean conditions, uses && (since false is valid for stylex.props).
1496
- * For non-boolean conditions (could be "" or 0), uses ternary with undefined fallback.
1541
+ * For boolean conditions, uses `cond && expr` (since `false` is a valid StyleXArray element).
1542
+ * For non-boolean conditions, uses `cond ? expr : undefined` to avoid producing
1543
+ * values like `""` or `0` which are not valid StyleXArray elements.
1497
1544
  */
1498
1545
  function makeConditionalStyleExpr(j, args) {
1499
1546
  const { cond, expr, isBoolean } = args;
@@ -1525,7 +1572,7 @@ function areEquivalentWhen(left, right) {
1525
1572
  * `prop ? trueExpr : falseExpr` instead of emitting separate conditionals.
1526
1573
  */
1527
1574
  function buildExtraStylexPropsExprs(j, args) {
1528
- const { entries, destructureProps } = args;
1575
+ const { entries, destructureProps, booleanProps } = args;
1529
1576
  const result = [];
1530
1577
  const consumed = /* @__PURE__ */ new Set();
1531
1578
  for (let i = 0; i < entries.length; i++) {
@@ -1552,7 +1599,8 @@ function buildExtraStylexPropsExprs(j, args) {
1552
1599
  }
1553
1600
  const { cond, isBoolean } = collectConditionProps(j, {
1554
1601
  when: entry.when,
1555
- destructureProps
1602
+ destructureProps,
1603
+ booleanProps
1556
1604
  });
1557
1605
  result.push(makeConditionalStyleExpr(j, {
1558
1606
  cond,
@@ -1880,10 +1928,11 @@ function analyzeBeforeEmitStep(ctx) {
1880
1928
  if (decl.isDirectJsxResolution) continue;
1881
1929
  if (decl.base.kind !== "intrinsic") continue;
1882
1930
  if (relationChildStyleKeys.has(decl.styleKey)) continue;
1883
- if (decl.promotedStyleProps?.length) continue;
1931
+ const usageCount = getJsxUsageCount(decl.localName);
1932
+ if (decl.promotedStyleProps?.length && decl.promotedStyleProps.length >= usageCount) continue;
1884
1933
  const { ref } = getJsxAttributeUsage(decl.localName);
1885
1934
  if (ref) continue;
1886
- if (getJsxUsageCount(decl.localName) > INLINE_USAGE_THRESHOLD) decl.needsWrapperComponent = true;
1935
+ if (usageCount > INLINE_USAGE_THRESHOLD) decl.needsWrapperComponent = true;
1887
1936
  }
1888
1937
  const hasSpreadInJsx = (name) => {
1889
1938
  let found = false;
@@ -1970,11 +2019,13 @@ function analyzeBeforeEmitStep(ctx) {
1970
2019
  existingPropNames.add("className").add("style").add("children").add("ref").add("key").add("as");
1971
2020
  if (decl.base.kind === "component") collectBaseComponentPropNames(root, j, decl.base.ident, existingPropNames);
1972
2021
  if (decl.base.kind === "intrinsic") for (const attr of BLOCKED_INTRINSIC_ATTR_RENAMES[decl.base.tagName] ?? []) existingPropNames.add(attr);
1973
- if (collectCallSiteAttrNames(root, j, decl.localName, existingPropNames)) continue;
2022
+ const { hasSpread, explicitTransientAtSpreadSites } = collectCallSiteAttrNames(root, j, decl.localName, existingPropNames);
1974
2023
  const renames = /* @__PURE__ */ new Map();
1975
2024
  for (const prop of transientProps) {
1976
2025
  const stripped = prop.slice(1);
1977
- if (!existingPropNames.has(stripped)) renames.set(prop, stripped);
2026
+ if (existingPropNames.has(stripped)) continue;
2027
+ if (hasSpread && !explicitTransientAtSpreadSites?.has(prop)) continue;
2028
+ renames.set(prop, stripped);
1978
2029
  }
1979
2030
  if (renames.size > 0) {
1980
2031
  if (collectReferencedTypeNames(decl.propsType).some((name) => isTypeNameUsedElsewhere(root, j, name, decl.localName) || !isTypeLocallyDefined(root, j, name))) continue;
@@ -2148,10 +2199,12 @@ function analyzeBeforeEmitStep(ctx) {
2148
2199
  decl.variantStyleKeys[whenKey] = styleKey;
2149
2200
  }
2150
2201
  if (decl.callSiteCombinedStyles?.length) for (const { styleKey, styles } of decl.callSiteCombinedStyles) ctx.resolvedStyleObjects.set(styleKey, styles);
2151
- if (decl.promotedStyleProps?.length) for (const entry of decl.promotedStyleProps) if (entry.mergeIntoBase) {
2202
+ if (decl.promotedStyleProps?.length) for (const entry of decl.promotedStyleProps) if (entry.mergeIntoBase) if (isAstNode(entry.styleValue)) ctx.resolvedStyleObjects.set(decl.styleKey, entry.styleValue);
2203
+ else {
2152
2204
  const existing = ctx.resolvedStyleObjects.get(decl.styleKey);
2153
2205
  if (existing && typeof existing === "object" && !isAstNode(existing)) Object.assign(existing, entry.styleValue);
2154
- } else ctx.resolvedStyleObjects.set(entry.styleKey, entry.styleValue);
2206
+ }
2207
+ else ctx.resolvedStyleObjects.set(entry.styleKey, entry.styleValue);
2155
2208
  }
2156
2209
  return CONTINUE;
2157
2210
  }
@@ -2245,19 +2298,26 @@ function collectResolverImportNames(ctx) {
2245
2298
  }
2246
2299
  return names;
2247
2300
  }
2248
- /**
2249
- * Collects non-`$`-prefixed attribute names from JSX call sites of a component.
2250
- * Returns true if any call site uses a JSX spread attribute (e.g., `{...props}`),
2251
- * which means the spread may contain `$`-prefixed keys at runtime — all renames
2252
- * must be blocked to prevent mismatches.
2253
- */
2254
2301
  function collectCallSiteAttrNames(root, j, componentName, names) {
2255
2302
  let hasSpread = false;
2303
+ const spreadSiteTransientProps = [];
2256
2304
  const collectFromElement = (openingElement) => {
2257
- for (const attr of openingElement.attributes ?? []) if (attr.type === "JSXSpreadAttribute") hasSpread = true;
2258
- else if (attr.type === "JSXAttribute" && attr.name?.type === "JSXIdentifier") {
2305
+ let siteHasSpread = false;
2306
+ const siteTransientAfterSpread = /* @__PURE__ */ new Set();
2307
+ const siteTransientBeforeSpread = /* @__PURE__ */ new Set();
2308
+ for (const attr of openingElement.attributes ?? []) if (attr.type === "JSXSpreadAttribute") {
2309
+ hasSpread = true;
2310
+ siteHasSpread = true;
2311
+ for (const name of siteTransientAfterSpread) siteTransientBeforeSpread.add(name);
2312
+ siteTransientAfterSpread.clear();
2313
+ } else if (attr.type === "JSXAttribute" && attr.name?.type === "JSXIdentifier") {
2259
2314
  const name = attr.name.name;
2260
- if (!name.startsWith("$")) names.add(name);
2315
+ if (name.startsWith("$")) siteTransientAfterSpread.add(name);
2316
+ else names.add(name);
2317
+ }
2318
+ if (siteHasSpread) {
2319
+ for (const name of siteTransientBeforeSpread) siteTransientAfterSpread.delete(name);
2320
+ spreadSiteTransientProps.push(siteTransientAfterSpread);
2261
2321
  }
2262
2322
  };
2263
2323
  root.find(j.JSXElement, { openingElement: { name: {
@@ -2268,7 +2328,20 @@ function collectCallSiteAttrNames(root, j, componentName, names) {
2268
2328
  type: "JSXIdentifier",
2269
2329
  name: componentName
2270
2330
  } }).forEach((p) => collectFromElement(p.node));
2271
- return hasSpread;
2331
+ if (!hasSpread) return {
2332
+ hasSpread: false,
2333
+ explicitTransientAtSpreadSites: null
2334
+ };
2335
+ if (spreadSiteTransientProps.length === 0) return {
2336
+ hasSpread: true,
2337
+ explicitTransientAtSpreadSites: /* @__PURE__ */ new Set()
2338
+ };
2339
+ const intersection = new Set(spreadSiteTransientProps[0]);
2340
+ for (let i = 1; i < spreadSiteTransientProps.length; i++) for (const prop of intersection) if (!spreadSiteTransientProps[i].has(prop)) intersection.delete(prop);
2341
+ return {
2342
+ hasSpread: true,
2343
+ explicitTransientAtSpreadSites: intersection
2344
+ };
2272
2345
  }
2273
2346
  /**
2274
2347
  * Collects prop names from a locally-defined base component's type that match
@@ -2382,6 +2455,7 @@ function applyTransientPropRenames(decl, renames) {
2382
2455
  }
2383
2456
  if (decl.styleFnFromProps) for (const sf of decl.styleFnFromProps) {
2384
2457
  sf.jsxProp = renames.get(sf.jsxProp) ?? sf.jsxProp;
2458
+ if (sf.propsObjectKey) sf.propsObjectKey = renames.get(sf.propsObjectKey) ?? sf.propsObjectKey;
2385
2459
  if (sf.conditionWhen) sf.conditionWhen = renamePropsInWhenString(sf.conditionWhen, renames);
2386
2460
  if (sf.callArg) renameIdentifiersInAst(sf.callArg, renames);
2387
2461
  }
@@ -2466,7 +2540,6 @@ function renameIdentifiersInAst(node, renames) {
2466
2540
  if (n.type === "Identifier" && typeof n.name === "string") {
2467
2541
  const renamed = renames.get(n.name);
2468
2542
  if (renamed) n.name = renamed;
2469
- return;
2470
2543
  }
2471
2544
  for (const [key, value] of Object.entries(n)) {
2472
2545
  if (AST_METADATA_KEYS.has(key)) continue;
@@ -2621,8 +2694,11 @@ const NUMERIC_CSS_PROPS = new Set([
2621
2694
  "widows",
2622
2695
  "columnCount"
2623
2696
  ]);
2624
- /** CSS properties that accept both numeric and string values (e.g., grid line numbers or span syntax). */
2625
- const NUMBER_OR_STRING_CSS_PROPS = new Set([
2697
+ /**
2698
+ * CSS properties that accept numeric values in standard CSS / React inline styles
2699
+ * but are typed as `string` in StyleX. Numeric values must be coerced to strings.
2700
+ */
2701
+ const STYLEX_STRING_ONLY_CSS_PROPS = new Set([
2626
2702
  "gridRow",
2627
2703
  "gridColumn",
2628
2704
  "gridRowStart",
@@ -2635,16 +2711,22 @@ const IDENTIFIER_NAME_RE = /^[$A-Z_][0-9A-Z_$]*$/i;
2635
2711
  function isValidIdentifierName$1(name) {
2636
2712
  return IDENTIFIER_NAME_RE.test(name);
2637
2713
  }
2714
+ function coerceToStringForStyleX(cssProp, value) {
2715
+ if (STYLEX_STRING_ONLY_CSS_PROPS.has(cssProp) && typeof value === "number") return String(value);
2716
+ return value;
2717
+ }
2638
2718
  /**
2639
2719
  * Infers a TS type keyword for a dynamic expression based on the CSS property it's assigned to.
2640
2720
  * Numeric-only properties get `number`; ambiguous length-like values get `number | string`.
2721
+ * Properties in STYLEX_STRING_ONLY_CSS_PROPS always get `string` even when the value is numeric.
2641
2722
  */
2642
2723
  function inferTypeForCssProp(cssProp, expr) {
2724
+ if (STYLEX_STRING_ONLY_CSS_PROPS.has(cssProp)) return "string";
2643
2725
  const staticVal = literalToStaticValue(expr);
2644
2726
  if (typeof staticVal === "number") return "number";
2645
2727
  if (typeof staticVal === "string") return "string";
2646
2728
  if (NUMERIC_CSS_PROPS.has(cssProp)) return "number";
2647
- if (NUMBER_OR_STRING_CSS_PROPS.has(cssProp) || LENGTH_LIKE_CSS_PROP_RE.test(cssProp)) return "numberOrString";
2729
+ if (LENGTH_LIKE_CSS_PROP_RE.test(cssProp)) return "numberOrString";
2648
2730
  return "string";
2649
2731
  }
2650
2732
  /**
@@ -2773,7 +2855,7 @@ function analyzePromotableStyleProps(root, j, styledDecls, declByLocal, getJsxUs
2773
2855
  const hasDynamic = site.properties.some((p) => p.dynamicExpr !== null);
2774
2856
  if (allStatic && !hasDynamic) {
2775
2857
  const staticObj = {};
2776
- for (const p of site.properties) staticObj[p.key] = p.staticValue;
2858
+ for (const p of site.properties) staticObj[p.key] = coerceToStringForStyleX(p.key, p.staticValue);
2777
2859
  if (usageCount <= 1 && !decl.isExported && !hasPropertyOverlap(staticObj, resolvedStyleObjects.get(decl.styleKey))) {
2778
2860
  promotedEntries.push({
2779
2861
  styleKey: decl.styleKey,
@@ -2791,15 +2873,31 @@ function analyzePromotableStyleProps(root, j, styledDecls, declByLocal, getJsxUs
2791
2873
  site.opening.__promotedStyleKey = styleKey;
2792
2874
  }
2793
2875
  } else if (hasDynamic) {
2794
- const styleKey = generatePromotedDynamicStyleKey(decl.styleKey, usedKeyNames, site.children);
2795
- usedKeyNames.add(styleKey);
2796
- const staticObj = {};
2876
+ const inlineStaticObj = {};
2797
2877
  const dynamicParams = [];
2798
- for (const p of site.properties) if (p.staticValue !== null) staticObj[p.key] = p.staticValue;
2878
+ for (const p of site.properties) if (p.staticValue !== null) inlineStaticObj[p.key] = coerceToStringForStyleX(p.key, p.staticValue);
2799
2879
  else dynamicParams.push({
2800
2880
  cssProp: p.key,
2801
2881
  expr: p.dynamicExpr
2802
2882
  });
2883
+ const baseObj = resolvedStyleObjects.get(decl.styleKey);
2884
+ const baseIsSimpleObject = baseObj && typeof baseObj === "object" && !isAstNode(baseObj) && Object.values(baseObj).every((v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean" || isAstNode(v));
2885
+ const isExtendedByOther = styledDecls.some((d) => d !== decl && d.base.kind === "component" && d.base.ident === decl.localName);
2886
+ const canMergeDynamic = usageCount <= 1 && !decl.isExported && !isExtendedByOther && baseIsSimpleObject && !hasPropertyOverlap(inlineStaticObj, baseObj);
2887
+ const dynamicPropKeys = new Set(dynamicParams.map((dp) => dp.cssProp));
2888
+ const mergedStaticProps = [];
2889
+ if (canMergeDynamic) {
2890
+ for (const [k, v] of Object.entries(baseObj)) if (!dynamicPropKeys.has(k)) mergedStaticProps.push({
2891
+ key: k,
2892
+ value: v
2893
+ });
2894
+ }
2895
+ for (const [k, v] of Object.entries(inlineStaticObj)) mergedStaticProps.push({
2896
+ key: k,
2897
+ value: v
2898
+ });
2899
+ const styleKey = canMergeDynamic ? decl.styleKey : generatePromotedDynamicStyleKey(decl.styleKey, usedKeyNames, site.children);
2900
+ if (!canMergeDynamic) usedKeyNames.add(styleKey);
2803
2901
  const paramEntries = [];
2804
2902
  const seenParamNames = /* @__PURE__ */ new Set();
2805
2903
  for (const dp of dynamicParams) {
@@ -2819,24 +2917,34 @@ function analyzePromotableStyleProps(root, j, styledDecls, declByLocal, getJsxUs
2819
2917
  id.typeAnnotation = j.tsTypeAnnotation(typeNode);
2820
2918
  return id;
2821
2919
  });
2822
- const bodyProperties = site.properties.map((p) => {
2823
- if (p.staticValue !== null) {
2824
- const val = typeof p.staticValue === "string" ? j.stringLiteral(p.staticValue) : typeof p.staticValue === "number" ? j.numericLiteral(p.staticValue) : j.booleanLiteral(p.staticValue);
2825
- return j.property("init", j.identifier(p.key), val);
2826
- } else {
2827
- const prop = j.property("init", j.identifier(p.key), j.identifier(p.key));
2828
- prop.shorthand = true;
2829
- return prop;
2830
- }
2831
- });
2920
+ const bodyProperties = [];
2921
+ for (const sp of mergedStaticProps) {
2922
+ const val = isAstNode(sp.value) ? sp.value : typeof sp.value === "string" ? j.stringLiteral(sp.value) : typeof sp.value === "number" ? j.numericLiteral(sp.value) : j.booleanLiteral(sp.value);
2923
+ bodyProperties.push(j.property("init", j.identifier(sp.key), val));
2924
+ }
2925
+ for (const p of site.properties) if (p.dynamicExpr !== null) {
2926
+ const prop = j.property("init", j.identifier(p.key), j.identifier(p.key));
2927
+ prop.shorthand = true;
2928
+ bodyProperties.push(prop);
2929
+ }
2832
2930
  const fnNode = j.arrowFunctionExpression(params, j.objectExpression(bodyProperties));
2833
- promotedEntries.push({
2834
- styleKey,
2835
- styleValue: fnNode
2836
- });
2837
- site.opening.__promotedStyleKey = styleKey;
2838
- const callArgs = dynamicParams.map((dp) => dp.expr);
2839
- site.opening.__promotedStyleArgs = callArgs;
2931
+ if (canMergeDynamic) {
2932
+ promotedEntries.push({
2933
+ styleKey: decl.styleKey,
2934
+ styleValue: fnNode,
2935
+ mergeIntoBase: true
2936
+ });
2937
+ site.opening.__promotedMergeIntoBase = true;
2938
+ site.opening.__promotedMergeArgs = dynamicParams.map((dp) => STYLEX_STRING_ONLY_CSS_PROPS.has(dp.cssProp) ? j.callExpression(j.identifier("String"), [dp.expr]) : dp.expr);
2939
+ } else {
2940
+ promotedEntries.push({
2941
+ styleKey,
2942
+ styleValue: fnNode
2943
+ });
2944
+ site.opening.__promotedStyleKey = styleKey;
2945
+ const callArgs = dynamicParams.map((dp) => STYLEX_STRING_ONLY_CSS_PROPS.has(dp.cssProp) ? j.callExpression(j.identifier("String"), [dp.expr]) : dp.expr);
2946
+ site.opening.__promotedStyleArgs = callArgs;
2947
+ }
2840
2948
  }
2841
2949
  }
2842
2950
  if (promotedEntries.length > 0) decl.promotedStyleProps = promotedEntries;
@@ -7991,8 +8099,8 @@ function buildInlineResolverVariantDimensions(args) {
7991
8099
  return type === "ArrowFunctionExpression" || type === "FunctionExpression";
7992
8100
  });
7993
8101
  const hasCompleteCallsiteVisibility = !willHaveExternalInterface(ctx, decl, styledDecls) && !decl.usedAsValue;
7994
- if (variantKeys.length === 1 && !hasPropReferencingTemplateExpressions) {
7995
- if (hasCompleteCallsiteVisibility) {
8102
+ if (variantKeys.length === 1) {
8103
+ if (hasCompleteCallsiteVisibility && !hasPropReferencingTemplateExpressions) {
7996
8104
  if (usageResult.propsByUsage.filter((siteProps) => propName in siteProps).length === totalCallSites) {
7997
8105
  const [, singleVariantStyles] = Object.entries(variants)[0];
7998
8106
  for (const [cssKey, cssVal] of Object.entries(singleVariantStyles)) foldedBaseSx[cssKey] = String(cssVal);
@@ -9625,6 +9733,22 @@ function buildVariantDimPropTypeMap(d) {
9625
9733
  return [dim.propName, typeText];
9626
9734
  }));
9627
9735
  }
9736
+ /**
9737
+ * Collects prop names that are known to have `boolean` type from all available sources:
9738
+ * - `staticBooleanVariants` without a variantKey (confirmed boolean from prepass)
9739
+ * - `variantDimensions` with `isBooleanProp` flag
9740
+ * - `propsType` AST type literal with `boolean` type annotations
9741
+ *
9742
+ * Used to emit `cond && styles.key` (valid for StyleXArray since `false` is accepted)
9743
+ * instead of `cond ? styles.key : undefined` for boolean-guarded variant conditions.
9744
+ */
9745
+ function collectBooleanPropNames(d) {
9746
+ const result = /* @__PURE__ */ new Set();
9747
+ for (const sbv of d.staticBooleanVariants ?? []) if (!sbv.variantKey) result.add(sbv.propName);
9748
+ for (const dim of d.variantDimensions ?? []) if (dim.isBooleanProp) result.add(dim.propName);
9749
+ collectBooleanPropsFromTypeLiteral(d.propsType, result);
9750
+ return result;
9751
+ }
9628
9752
  function getAttrsAsString(d) {
9629
9753
  const v = d.attrsInfo?.staticAttrs?.as;
9630
9754
  return typeof v === "string" ? v : null;
@@ -9658,6 +9782,41 @@ function injectStylePropsIntoTypeLiteralString(typeText, options) {
9658
9782
  }
9659
9783
  return `${typeText} & { ${propsToAdd.join(", ")} }`;
9660
9784
  }
9785
+ function collectBooleanPropsFromTypeLiteral(node, result) {
9786
+ for (const name of extractBooleanProps(node)) {
9787
+ result.add(name);
9788
+ if (name.startsWith("$")) result.add(name.slice(1));
9789
+ }
9790
+ }
9791
+ function extractBooleanProps(node) {
9792
+ const typed = node;
9793
+ if (!typed || typeof typed !== "object") return /* @__PURE__ */ new Set();
9794
+ if (typed.type === "TSTypeLiteral") {
9795
+ const props = /* @__PURE__ */ new Set();
9796
+ for (const member of typed.members ?? []) {
9797
+ if (member?.type !== "TSPropertySignature") continue;
9798
+ const key = member.key;
9799
+ const name = key?.type === "Identifier" ? key.name : null;
9800
+ if (!name) continue;
9801
+ if ((member.typeAnnotation?.typeAnnotation)?.type === "TSBooleanKeyword") props.add(name);
9802
+ }
9803
+ return props;
9804
+ }
9805
+ if (typed.type === "TSIntersectionType") {
9806
+ const combined = /* @__PURE__ */ new Set();
9807
+ for (const t of typed.types ?? []) for (const p of extractBooleanProps(t)) combined.add(p);
9808
+ return combined;
9809
+ }
9810
+ if (typed.type === "TSUnionType") {
9811
+ const arms = (typed.types ?? []).map(extractBooleanProps);
9812
+ if (arms.length === 0) return /* @__PURE__ */ new Set();
9813
+ const first = arms[0];
9814
+ const intersection = /* @__PURE__ */ new Set();
9815
+ for (const name of first) if (arms.every((arm) => arm.has(name))) intersection.add(name);
9816
+ return intersection;
9817
+ }
9818
+ return /* @__PURE__ */ new Set();
9819
+ }
9661
9820
 
9662
9821
  //#endregion
9663
9822
  //#region src/internal/emit-wrappers/jsx-builders.ts
@@ -9816,6 +9975,15 @@ function styleRef(j, stylesIdentifier, key) {
9816
9975
  return j.memberExpression(j.identifier(stylesIdentifier), j.identifier(key));
9817
9976
  }
9818
9977
  /**
9978
+ * When a style function uses a `props` object parameter, wraps the raw call
9979
+ * argument in `{ [propsObjectKey]: rawArg }`. Returns `rawArg` unchanged when
9980
+ * `propsObjectKey` is not set.
9981
+ */
9982
+ function wrapCallArgForPropsObject(j, rawArg, propsObjectKey) {
9983
+ if (!propsObjectKey) return rawArg;
9984
+ return j.objectExpression([j.property("init", j.identifier(propsObjectKey), rawArg)]);
9985
+ }
9986
+ /**
9819
9987
  * Splits a declaration's extra style keys into those that appear before and
9820
9988
  * after the base style in stylex.props() argument order.
9821
9989
  */
@@ -10093,9 +10261,10 @@ function buildStyleFnExpressions(emitter, args) {
10093
10261
  }
10094
10262
  return null;
10095
10263
  };
10264
+ const booleanProps = collectBooleanPropNames(d);
10096
10265
  for (const p of styleFnPairs) {
10097
10266
  const propExpr = p.jsxProp === "__props" ? propsId : propExprBuilder(p.jsxProp);
10098
- const callArg = p.callArg ?? propExpr;
10267
+ const callArg = wrapCallArgForPropsObject(j, p.callArg ?? propExpr, p.propsObjectKey);
10099
10268
  const call = j.callExpression(styleRef(j, stylesIdentifier, p.fnKey), [callArg]);
10100
10269
  if (p.callArg?.type === "Identifier") {
10101
10270
  const name = p.callArg.name;
@@ -10122,7 +10291,8 @@ function buildStyleFnExpressions(emitter, args) {
10122
10291
  if (p.conditionWhen) {
10123
10292
  const { cond, isBoolean } = collectConditionProps(j, {
10124
10293
  when: p.conditionWhen,
10125
- destructureProps
10294
+ destructureProps,
10295
+ booleanProps
10126
10296
  });
10127
10297
  pushExpr(makeConditionalStyleExpr(j, {
10128
10298
  cond,
@@ -11769,6 +11939,7 @@ function emitSimpleExportedIntrinsicWrappers(ctx) {
11769
11939
  const needsUseTheme = appendThemeBooleanStyleArgs(d.needsUseThemeHook, styleArgs, j, stylesIdentifier, () => ctx.markNeedsUseThemeImport());
11770
11940
  for (const gp of appendAllPseudoStyleArgs(d, styleArgs, j, stylesIdentifier)) if (!destructureProps.includes(gp)) destructureProps.push(gp);
11771
11941
  const compoundVariantKeys = collectCompoundVariantKeys(d.compoundVariants);
11942
+ const booleanProps = collectBooleanPropNames(d);
11772
11943
  const hasSourceOrder = !!(d.variantSourceOrder && Object.keys(d.variantSourceOrder).length > 0);
11773
11944
  const orderedEntries = [];
11774
11945
  if (d.variantStyleKeys) {
@@ -11787,7 +11958,8 @@ function emitSimpleExportedIntrinsicWrappers(ctx) {
11787
11958
  const positiveWhen = getPositiveWhen(when, otherWhen) ?? when;
11788
11959
  const { cond } = emitter.collectConditionProps({
11789
11960
  when: positiveWhen,
11790
- destructureProps
11961
+ destructureProps,
11962
+ booleanProps
11791
11963
  });
11792
11964
  const isCurrentPositive = areEquivalentWhen(when, positiveWhen);
11793
11965
  const trueKey = isCurrentPositive ? variantKey : otherKey;
@@ -11805,7 +11977,8 @@ function emitSimpleExportedIntrinsicWrappers(ctx) {
11805
11977
  }
11806
11978
  const { cond, isBoolean } = emitter.collectConditionProps({
11807
11979
  when,
11808
- destructureProps
11980
+ destructureProps,
11981
+ booleanProps
11809
11982
  });
11810
11983
  const styleExpr = j.memberExpression(j.identifier(stylesIdentifier), j.identifier(variantKey));
11811
11984
  const expr = emitter.makeConditionalStyleExpr({
@@ -12167,12 +12340,12 @@ function appendAllPseudoStyleArgs(d, styleArgs, j, stylesIdentifier) {
12167
12340
  * Each entry is mapped to an expression via `buildExpr`, then optionally
12168
12341
  * wrapped in a conditional if the entry has a `guard`.
12169
12342
  */
12170
- function appendGuardedStyleArgs(entries, styleArgs, j, buildExpr) {
12343
+ function appendGuardedStyleArgs(entries, styleArgs, j, buildExpr, booleanProps) {
12171
12344
  const guardProps = [];
12172
12345
  for (const entry of entries) {
12173
12346
  const expr = buildExpr(entry);
12174
12347
  if (entry.guard) {
12175
- const parsed = parseVariantWhenToAst(j, entry.guard.when);
12348
+ const parsed = parseVariantWhenToAst(j, entry.guard.when, booleanProps);
12176
12349
  for (const p of parsed.props) if (p && !guardProps.includes(p)) guardProps.push(p);
12177
12350
  styleArgs.push(makeConditionalStyleExpr(j, {
12178
12351
  cond: parsed.cond,
@@ -12364,6 +12537,7 @@ function emitComponentWrappers(emitter) {
12364
12537
  ];
12365
12538
  const hasSourceOrder = !!(d.variantSourceOrder && Object.keys(d.variantSourceOrder).length > 0);
12366
12539
  const orderedEntries = [];
12540
+ const booleanProps = collectBooleanPropNames(d);
12367
12541
  if (d.variantStyleKeys) {
12368
12542
  const sortedEntries = sortVariantEntriesBySpecificity(Object.entries(d.variantStyleKeys));
12369
12543
  const consumedVariantIndices = /* @__PURE__ */ new Set();
@@ -12378,7 +12552,8 @@ function emitComponentWrappers(emitter) {
12378
12552
  const positiveWhen = getPositiveWhen(when, otherWhen) ?? when;
12379
12553
  const { cond } = emitter.collectConditionProps({
12380
12554
  when: positiveWhen,
12381
- destructureProps
12555
+ destructureProps,
12556
+ booleanProps
12382
12557
  });
12383
12558
  for (let i = prevLength; i < destructureProps.length; i++) styleOnlyConditionProps.add(destructureProps[i]);
12384
12559
  const isCurrentPositive = areEquivalentWhen(when, positiveWhen);
@@ -12397,7 +12572,8 @@ function emitComponentWrappers(emitter) {
12397
12572
  }
12398
12573
  const { cond, isBoolean } = emitter.collectConditionProps({
12399
12574
  when,
12400
- destructureProps
12575
+ destructureProps,
12576
+ booleanProps
12401
12577
  });
12402
12578
  for (let i = prevLength; i < destructureProps.length; i++) styleOnlyConditionProps.add(destructureProps[i]);
12403
12579
  const styleExpr = j.memberExpression(j.identifier(stylesIdentifier), j.identifier(variantKey));
@@ -13344,6 +13520,7 @@ function emitIntrinsicPolymorphicWrappers(ctx) {
13344
13520
  ...extraStyleArgsAfterBase
13345
13521
  ];
13346
13522
  const compoundVariantKeys = collectCompoundVariantKeys(d.compoundVariants);
13523
+ const booleanProps = collectBooleanPropNames(d);
13347
13524
  const hasSourceOrder = !!(d.variantSourceOrder && Object.keys(d.variantSourceOrder).length > 0);
13348
13525
  const orderedEntries = [];
13349
13526
  if (d.variantStyleKeys) {
@@ -13352,7 +13529,8 @@ function emitIntrinsicPolymorphicWrappers(ctx) {
13352
13529
  if (compoundVariantKeys.has(when)) continue;
13353
13530
  const { cond, isBoolean } = emitter.collectConditionProps({
13354
13531
  when,
13355
- destructureProps
13532
+ destructureProps,
13533
+ booleanProps
13356
13534
  });
13357
13535
  const styleExpr = j.memberExpression(j.identifier(stylesIdentifier), j.identifier(variantKey));
13358
13536
  const expr = emitter.makeConditionalStyleExpr({
@@ -13653,13 +13831,17 @@ function emitShouldForwardPropWrappers(ctx) {
13653
13831
  const needsUseTheme = appendThemeBooleanStyleArgs(d.needsUseThemeHook, styleArgs, j, stylesIdentifier, () => ctx.markNeedsUseThemeImport());
13654
13832
  const pseudoGuardProps = appendAllPseudoStyleArgs(d, styleArgs, j, stylesIdentifier);
13655
13833
  const compoundVariantKeys = collectCompoundVariantKeys(d.compoundVariants);
13834
+ const booleanProps = collectBooleanPropNames(d);
13656
13835
  const hasSourceOrder = !!(d.variantSourceOrder && Object.keys(d.variantSourceOrder).length > 0);
13657
13836
  const orderedEntries = [];
13658
13837
  if (d.variantStyleKeys) {
13659
13838
  const sortedEntries = sortVariantEntriesBySpecificity(Object.entries(d.variantStyleKeys));
13660
13839
  for (const [when, variantKey] of sortedEntries) {
13661
13840
  if (compoundVariantKeys.has(when)) continue;
13662
- const { cond, isBoolean } = emitter.collectConditionProps({ when });
13841
+ const { cond, isBoolean } = emitter.collectConditionProps({
13842
+ when,
13843
+ booleanProps
13844
+ });
13663
13845
  const styleExpr = j.memberExpression(j.identifier(stylesIdentifier), j.identifier(variantKey));
13664
13846
  const expr = emitter.makeConditionalStyleExpr({
13665
13847
  cond,
@@ -13703,13 +13885,14 @@ function emitShouldForwardPropWrappers(ctx) {
13703
13885
  const prefix = dropPrefix;
13704
13886
  const isPrefixProp = !!prefix && typeof p.jsxProp === "string" && p.jsxProp !== "__props" && p.jsxProp.startsWith(prefix);
13705
13887
  const propExpr = isPrefixProp ? knownPrefixPropsSet.has(p.jsxProp) ? j.identifier(p.jsxProp) : j.memberExpression(j.identifier("props"), j.literal(p.jsxProp), true) : p.jsxProp === "__props" ? j.identifier("props") : j.identifier(p.jsxProp);
13706
- const callArg = p.callArg ?? propExpr;
13888
+ const callArg = wrapCallArgForPropsObject(j, p.callArg ?? propExpr, p.propsObjectKey);
13707
13889
  const call = j.callExpression(styleRef(j, stylesIdentifier, p.fnKey), [callArg]);
13708
13890
  let expr;
13709
13891
  if (p.conditionWhen) {
13710
13892
  const { cond, isBoolean } = emitter.collectConditionProps({
13711
13893
  when: p.conditionWhen,
13712
- destructureProps: destructureParts
13894
+ destructureProps: destructureParts,
13895
+ booleanProps
13713
13896
  });
13714
13897
  expr = emitter.makeConditionalStyleExpr({
13715
13898
  cond,
@@ -16088,6 +16271,7 @@ function createLowerRulesState(ctx) {
16088
16271
  j,
16089
16272
  importMap
16090
16273
  });
16274
+ const enumValueMap = buildEnumValueMap(root, j);
16091
16275
  const crossFileSelectorsByLocal = /* @__PURE__ */ new Map();
16092
16276
  if (ctx.crossFileSelectorUsages) for (const usage of ctx.crossFileSelectorUsages) crossFileSelectorsByLocal.set(usage.localName, usage);
16093
16277
  const state = {
@@ -16132,6 +16316,7 @@ function createLowerRulesState(ctx) {
16132
16316
  resolveCssHelperTemplate,
16133
16317
  resolveImportInScope,
16134
16318
  resolveImportForExpr,
16319
+ enumValueMap,
16135
16320
  crossFileSelectorsByLocal,
16136
16321
  inlineKeyframeNameMap: void 0,
16137
16322
  bail: false,
@@ -17024,7 +17209,15 @@ function tryResolveConditionalValue(node, ctx) {
17024
17209
  if (!isArrowFunctionExpression(expr)) return null;
17025
17210
  const info = getArrowFnThemeParamInfo(expr);
17026
17211
  const paramName = info?.kind === "propsParam" ? info.propsName : null;
17212
+ const propsParamName = paramName ?? void 0;
17213
+ const themeBindingName = info?.kind === "themeBinding" ? info.themeName : void 0;
17027
17214
  const paramBindings = !paramName && !info ? getArrowFnParamBindings(expr) : null;
17215
+ const runtimeCallState = { info: null };
17216
+ const buildRuntimeCallResult = () => runtimeCallState.info ? {
17217
+ type: "runtimeCallOnly",
17218
+ resolveCallContext: runtimeCallState.info.resolveCallContext,
17219
+ resolveCallResult: runtimeCallState.info.resolveCallResult
17220
+ } : null;
17028
17221
  const body = getFunctionBodyExpr(expr);
17029
17222
  if (!body || body.type !== "ConditionalExpression") return null;
17030
17223
  const checkThemeBooleanTest = (test) => {
@@ -17148,17 +17341,66 @@ function tryResolveConditionalValue(node, ctx) {
17148
17341
  };
17149
17342
  }
17150
17343
  if (!b || typeof b !== "object") return null;
17344
+ const callHasThemeArg = (call) => (call.arguments ?? []).some((arg) => {
17345
+ if (!arg || typeof arg !== "object" || arg.type !== "MemberExpression") return false;
17346
+ if (propsParamName) {
17347
+ const parts = getMemberPathFromIdentifier(arg, propsParamName);
17348
+ return parts !== null && parts[0] === "theme" && parts.length > 1;
17349
+ }
17350
+ if (themeBindingName) {
17351
+ const parts = getMemberPathFromIdentifier(arg, themeBindingName);
17352
+ return parts !== null && parts.length > 0;
17353
+ }
17354
+ return false;
17355
+ });
17356
+ const markAsRuntimeCall = (call) => {
17357
+ runtimeCallState.info = {
17358
+ resolveCallContext: {
17359
+ callSiteFilePath: ctx.filePath,
17360
+ calleeImportedName: "<local>",
17361
+ calleeSource: {
17362
+ kind: "specifier",
17363
+ value: ctx.filePath
17364
+ },
17365
+ args: [],
17366
+ ...call.loc?.start ? { loc: {
17367
+ line: call.loc.start.line,
17368
+ column: call.loc.start.column
17369
+ } } : {}
17370
+ },
17371
+ resolveCallResult: { preserveRuntimeCall: true }
17372
+ };
17373
+ };
17151
17374
  const resolveCallExpr = (call, cssProperty) => {
17152
- const res = resolveImportedHelperCall(call, ctx, void 0, cssProperty);
17153
- if (res.kind === "resolved" && "expr" in res.result) return res.result;
17375
+ const res = resolveImportedHelperCall(call, ctx, propsParamName, cssProperty, themeBindingName);
17376
+ if (res.kind === "resolved") {
17377
+ if ("expr" in res.result) return res.result;
17378
+ if (res.result.preserveRuntimeCall) {
17379
+ runtimeCallState.info = {
17380
+ resolveCallContext: res.resolveCallContext,
17381
+ resolveCallResult: res.resolveCallResult
17382
+ };
17383
+ return null;
17384
+ }
17385
+ }
17154
17386
  if (isCallExpressionNode(call.callee)) {
17155
17387
  const inner = call.callee;
17156
17388
  const outerArgs = call.arguments ?? [];
17157
17389
  if (outerArgs.length === 1 && outerArgs[0] && typeof outerArgs[0] === "object") {
17158
- const innerRes = resolveImportedHelperCall(inner, ctx, void 0, cssProperty);
17159
- if (innerRes.kind === "resolved" && "expr" in innerRes.result) return innerRes.result;
17390
+ const innerRes = resolveImportedHelperCall(inner, ctx, propsParamName, cssProperty, themeBindingName);
17391
+ if (innerRes.kind === "resolved") {
17392
+ if ("expr" in innerRes.result) return innerRes.result;
17393
+ if (innerRes.result.preserveRuntimeCall) {
17394
+ runtimeCallState.info = {
17395
+ resolveCallContext: innerRes.resolveCallContext,
17396
+ resolveCallResult: innerRes.resolveCallResult
17397
+ };
17398
+ return null;
17399
+ }
17400
+ }
17160
17401
  }
17161
17402
  }
17403
+ if (callHasThemeArg(call)) markAsRuntimeCall(call);
17162
17404
  return null;
17163
17405
  };
17164
17406
  const templateResult = resolveTemplateLiteralExpressions(b, (expr) => {
@@ -17239,7 +17481,7 @@ function tryResolveConditionalValue(node, ctx) {
17239
17481
  };
17240
17482
  const extractConditionInfo = (test) => {
17241
17483
  if (test.type !== "BinaryExpression" || test.operator !== "===" && test.operator !== "!==") return null;
17242
- const rhsRaw = literalToStaticValue(test.right);
17484
+ const rhsRaw = resolveStaticExpressionValue(test.right, ctx.enumValueMap);
17243
17485
  if (rhsRaw === null) return null;
17244
17486
  if (paramName && test.left.type === "MemberExpression") {
17245
17487
  const leftPath = getMemberPathFromIdentifier(test.left, paramName);
@@ -17389,7 +17631,7 @@ function tryResolveConditionalValue(node, ctx) {
17389
17631
  }
17390
17632
  }
17391
17633
  }
17392
- if (!cons || !alt) return null;
17634
+ if (!cons || !alt) return buildRuntimeCallResult();
17393
17635
  if (new Set([cons.usage, alt.usage]).size !== 1) return null;
17394
17636
  const usage = cons.usage;
17395
17637
  const variants = [{
@@ -17438,6 +17680,7 @@ function tryResolveConditionalValue(node, ctx) {
17438
17680
  };
17439
17681
  }
17440
17682
  }
17683
+ if (!cons || !alt) return buildRuntimeCallResult();
17441
17684
  }
17442
17685
  if (paramBindings?.kind === "destructured" && test.type === "Identifier" && typeof test.name === "string") {
17443
17686
  const resolvedProp = resolveIdentifierToPropName(test, paramBindings);
@@ -17475,6 +17718,7 @@ function tryResolveConditionalValue(node, ctx) {
17475
17718
  if (result) return result;
17476
17719
  }
17477
17720
  }
17721
+ if (!cons || !alt) return buildRuntimeCallResult();
17478
17722
  }
17479
17723
  }
17480
17724
  const condInfo = extractConditionInfo(test);
@@ -17520,7 +17764,7 @@ function tryResolveConditionalValue(node, ctx) {
17520
17764
  };
17521
17765
  }
17522
17766
  }
17523
- return null;
17767
+ return buildRuntimeCallResult();
17524
17768
  }
17525
17769
  function tryResolveConditionalCssBlock(node, ctx) {
17526
17770
  const expr = node.expr;
@@ -17600,7 +17844,7 @@ function tryResolveConditionalCssBlockTernary(node, ctx) {
17600
17844
  return null;
17601
17845
  }
17602
17846
  if (t.type === "BinaryExpression" && (t.operator === "===" || t.operator === "!==")) {
17603
- const rhsRaw = literalToStaticValue(t.right);
17847
+ const rhsRaw = resolveStaticExpressionValue(t.right, ctx.enumValueMap);
17604
17848
  if (rhsRaw === null) return null;
17605
17849
  const left = t.left;
17606
17850
  if (paramName && left?.type === "MemberExpression") {
@@ -17982,7 +18226,7 @@ function replaceThemeRefsWithHookVar(expr, paramName, info) {
17982
18226
  * Order matters: more-specific transforms first, then fall back to prop-access emission.
17983
18227
  */
17984
18228
  function resolveDynamicNode(node, ctx) {
17985
- return tryResolveThemeAccess(node, ctx) ?? tryResolveCallExpression(node, ctx) ?? tryResolveArrowFnHelperCallWithThemeArg(node, ctx) ?? tryResolveArrowFnCallWithConditionalArgs(node, ctx) ?? tryResolveConditionalValue(node, ctx) ?? tryResolveIndexedThemeWithPropFallback(node, ctx) ?? tryResolveConditionalCssBlockTernary(node, ctx) ?? tryResolveConditionalCssBlock(node, ctx) ?? tryResolveArrowFnCallWithSinglePropArg(node, ctx) ?? tryResolveThemeDependentTemplateLiteral(node, ctx) ?? tryResolveStyleFunctionFromTemplateLiteral(node) ?? tryResolveInlineStyleValueForNestedPropAccess(node) ?? tryResolvePropAccess(node) ?? tryResolveConditionalPropStyleFunction(node) ?? tryResolveInlineStyleValueForConditionalExpression(node) ?? tryResolveInlineStyleValueForLogicalExpression(node) ?? tryResolveInlineStyleValueFromArrowFn(node);
18229
+ return tryResolveThemeAccess(node, ctx) ?? tryResolveCallExpression(node, ctx) ?? tryResolveArrowFnHelperCallWithThemeArg(node, ctx) ?? tryResolveArrowFnCallWithConditionalArgs(node, ctx) ?? tryResolveConditionalValue(node, ctx) ?? tryResolveIndexedThemeWithPropFallback(node, ctx) ?? tryResolveConditionalCssBlockTernary(node, ctx) ?? tryResolveConditionalCssBlock(node, ctx) ?? tryResolveArrowFnCallWithSinglePropArg(node, ctx) ?? tryResolveThemeDependentTemplateLiteral(node, ctx) ?? tryResolveStyleFunctionFromTemplateLiteral(node) ?? tryResolveInlineStyleValueForNestedPropAccess(node) ?? tryResolvePropAccess(node) ?? tryResolveConditionalPropStyleFunction(node) ?? tryResolveArrowFnPropExpression(node) ?? tryResolveInlineStyleValueForConditionalExpression(node) ?? tryResolveInlineStyleValueForLogicalExpression(node) ?? tryResolveInlineStyleValueFromArrowFn(node);
17986
18230
  }
17987
18231
  function tryResolveThemeAccess(node, ctx) {
17988
18232
  const expr = node.expr;
@@ -18375,6 +18619,29 @@ function collectPropsFromExprTree(nodes, paramName) {
18375
18619
  props
18376
18620
  };
18377
18621
  }
18622
+ /**
18623
+ * Checks whether the param name is used as a bare identifier anywhere in the
18624
+ * expression tree (i.e., not as the `object` of a non-computed MemberExpression
18625
+ * like `props.X`). This detects patterns like `helper(props)` or computed access
18626
+ * `props[expr]` where the full props object is needed, which would break
18627
+ * `emitStyleFunctionFromPropsObject` since that handler only forwards a subset
18628
+ * of collected prop names.
18629
+ */
18630
+ function hasBareParamUsage(root, paramName) {
18631
+ const visit = (node, skipIdent) => {
18632
+ if (!node || typeof node !== "object") return false;
18633
+ if (Array.isArray(node)) return node.some((child) => visit(child, false));
18634
+ const n = node;
18635
+ if (n.type === "Identifier" && n.name === paramName && !skipIdent) return true;
18636
+ for (const key of Object.keys(n)) {
18637
+ if (key === "loc" || key === "comments") continue;
18638
+ const child = n[key];
18639
+ if (visit(child, (n.type === "MemberExpression" || n.type === "OptionalMemberExpression") && !n.computed && (key === "object" || key === "property"))) return true;
18640
+ }
18641
+ return false;
18642
+ };
18643
+ return visit(root, false);
18644
+ }
18378
18645
  function tryResolveStyleFunctionFromTemplateLiteral(node) {
18379
18646
  if (!node.css.property) return null;
18380
18647
  const expr = node.expr;
@@ -18460,6 +18727,35 @@ function tryDecomposeConditionalBranches(condBody, paramName) {
18460
18727
  isStaticWhenFalse
18461
18728
  };
18462
18729
  }
18730
+ /**
18731
+ * Handles arrow functions whose body is a computational expression referencing props.
18732
+ *
18733
+ * Catches expression types not covered by specific handlers (e.g. BinaryExpression,
18734
+ * UnaryExpression) and emits a StyleX style function that takes the props object.
18735
+ *
18736
+ * Pattern: `(props) => props.$depth * 16 + 4`
18737
+ * Output: `(props) => ({ paddingLeft: \`${props.$depth * 16 + 4}px\` })`
18738
+ */
18739
+ function tryResolveArrowFnPropExpression(node) {
18740
+ if (!node.css.property) return null;
18741
+ const expr = node.expr;
18742
+ if (!isArrowFunctionExpression(expr)) return null;
18743
+ const paramName = getArrowFnSingleParamName(expr);
18744
+ if (!paramName) return null;
18745
+ const body = getFunctionBodyExpr(expr);
18746
+ if (!body) return null;
18747
+ const bodyType = body.type;
18748
+ if (bodyType !== "BinaryExpression" && bodyType !== "UnaryExpression") return null;
18749
+ if (hasThemeAccessInArrowFn(expr)) return null;
18750
+ if (hasBareParamUsage(body, paramName)) return null;
18751
+ const { hasUsableProps, hasNonTransientProps, props } = collectPropsFromExprTree([body], paramName);
18752
+ if (!hasUsableProps) return null;
18753
+ if (hasNonTransientProps && node.component.withConfig?.shouldForwardProp) return null;
18754
+ return {
18755
+ type: "emitStyleFunctionFromPropsObject",
18756
+ props
18757
+ };
18758
+ }
18463
18759
  function tryResolveInlineStyleValueForNestedPropAccess(node) {
18464
18760
  if (!node.css.property) return null;
18465
18761
  const expr = node.expr;
@@ -20729,7 +21025,7 @@ const createValuePatternHandlers = (ctx) => {
20729
21025
  const p = leftPath[0];
20730
21026
  propName = propName ?? p;
20731
21027
  if (propName !== p) return false;
20732
- const rhs = literalToStaticValue(test.right);
21028
+ const rhs = resolveStaticExpressionValue(test.right, ctx.enumValueMap);
20733
21029
  if (rhs === null) return false;
20734
21030
  const retValue = readIfReturnValue(stmt);
20735
21031
  if (retValue === null) return false;
@@ -20892,7 +21188,7 @@ const createValuePatternHandlers = (ctx) => {
20892
21188
  * Core concepts: per-component style buckets, helper factories, and resolver wiring.
20893
21189
  */
20894
21190
  function createDeclProcessingState(state, decl) {
20895
- const { api, j, root, filePath, warnings, resolverImports, parseExpr, resolveValue, resolveValueDirectional, resolveCall, resolveCallOptional, resolveSelector, importMap, cssHelperFunctions, stringMappingFns, hasLocalThemeBinding, isCssHelperTaggedTemplate, resolveCssHelperTemplate, resolveImportInScope, usedCssHelperFunctions, markBail } = state;
21191
+ const { api, j, root, filePath, warnings, resolverImports, parseExpr, resolveValue, resolveValueDirectional, resolveCall, resolveCallOptional, resolveSelector, importMap, cssHelperFunctions, stringMappingFns, hasLocalThemeBinding, isCssHelperTaggedTemplate, resolveCssHelperTemplate, resolveImportInScope, usedCssHelperFunctions, enumValueMap, markBail } = state;
20896
21192
  const styleObj = {};
20897
21193
  const perPropPseudo = {};
20898
21194
  const perPropMedia = {};
@@ -20990,6 +21286,7 @@ function createDeclProcessingState(state, decl) {
20990
21286
  ...sharedFromState,
20991
21287
  api,
20992
21288
  importMap,
21289
+ enumValueMap,
20993
21290
  decl,
20994
21291
  styleObj,
20995
21292
  variantBuckets,
@@ -21009,7 +21306,8 @@ function createDeclProcessingState(state, decl) {
21009
21306
  resolveCall,
21010
21307
  resolveCallOptional,
21011
21308
  resolveImport: resolveImportInScope,
21012
- hasImportIgnoringShadowing: (localName) => importMap.has(localName)
21309
+ hasImportIgnoringShadowing: (localName) => importMap.has(localName),
21310
+ enumValueMap
21013
21311
  };
21014
21312
  const withConfig = decl.shouldForwardProp ? { shouldForwardProp: true } : void 0;
21015
21313
  const componentInfo = decl.base.kind === "intrinsic" ? {
@@ -21859,13 +22157,13 @@ function handleSplitVariantsResolvedValue(ctx) {
21859
22157
  }
21860
22158
  if (isHeterogeneousBackground) {
21861
22159
  const isNestedTernary = allPosParsed.length > 1;
22160
+ const defaultStylexProp = neg ? resolveBackgroundStylexProp(neg.expr) : null;
21862
22161
  if (neg && negParsed) {
21863
- const negStylexProp = resolveBackgroundStylexProp(neg.expr);
21864
- const bucket = { ...variantBuckets.get(neg.when) };
21865
- applyParsed(bucket, negParsed, negStylexProp);
21866
- variantBuckets.set(neg.when, bucket);
21867
- const suffix = toSuffixFromProp(neg.when);
21868
- variantStyleKeys[neg.when] ??= `${decl.styleKey}${suffix}`;
22162
+ if (!applyParsed(styleObj, negParsed, defaultStylexProp)) {
22163
+ bailUnsupported(decl, "Resolved conditional border variant could not be expanded to longhand properties");
22164
+ setBail();
22165
+ return true;
22166
+ }
21869
22167
  }
21870
22168
  for (let i = 0; i < allPosParsed.length; i++) {
21871
22169
  const { when, nameHint, parsed } = allPosParsed[i];
@@ -21874,6 +22172,7 @@ function handleSplitVariantsResolvedValue(ctx) {
21874
22172
  const whenClean = when.replace(/^!/, "");
21875
22173
  const bucket = { ...variantBuckets.get(whenClean) };
21876
22174
  applyParsed(bucket, parsed, posStylexProp);
22175
+ if (defaultStylexProp && posStylexProp !== defaultStylexProp) bucket[defaultStylexProp] = j.literal("transparent");
21877
22176
  variantBuckets.set(whenClean, bucket);
21878
22177
  const suffix = isNestedTernary && nameHint && !new Set([
21879
22178
  "truthy",
@@ -23477,6 +23776,12 @@ function handleInterpolatedDeclaration(args) {
23477
23776
  const { prefix, suffix } = extractStaticPartsForDecl(d);
23478
23777
  const valueExpr = prefix || suffix ? buildTemplateWithStaticParts(j, valueExprRaw, prefix, suffix) : valueExprRaw;
23479
23778
  const param = j.identifier(paramName);
23779
+ if (/\.(ts|tsx)$/.test(filePath)) {
23780
+ if (!(decl.propsType?.type === "TSTypeReference")) {
23781
+ const typeName = `${decl.localName}Props`;
23782
+ param.typeAnnotation = j.tsTypeAnnotation(j.tsTypeReference(j.identifier(typeName)));
23783
+ }
23784
+ }
23480
23785
  const body = j.objectExpression([j.property("init", makeCssPropKey(j, out.prop), buildPseudoMediaPropValue({
23481
23786
  j,
23482
23787
  valueExpr,
@@ -23485,18 +23790,10 @@ function handleInterpolatedDeclaration(args) {
23485
23790
  }))]);
23486
23791
  styleFnDecls.set(fnKey, j.arrowFunctionExpression([param], body));
23487
23792
  }
23488
- if (!styleFnFromProps.some((p) => p.fnKey === fnKey)) {
23489
- const callArg = j.objectExpression((res.props ?? []).map((propName) => {
23490
- const prop = j.property("init", j.identifier(propName), j.identifier(propName));
23491
- prop.shorthand = true;
23492
- return prop;
23493
- }));
23494
- styleFnFromProps.push({
23495
- fnKey,
23496
- jsxProp: "__props",
23497
- callArg
23498
- });
23499
- }
23793
+ if (!styleFnFromProps.some((p) => p.fnKey === fnKey)) styleFnFromProps.push({
23794
+ fnKey,
23795
+ jsxProp: "__props"
23796
+ });
23500
23797
  }
23501
23798
  continue;
23502
23799
  }
@@ -25560,11 +25857,195 @@ function finalizeDeclProcessing(ctx) {
25560
25857
  });
25561
25858
  decl.styleFnFromProps = styleFnFromProps;
25562
25859
  }
25563
- for (const [k, v] of styleFnDecls.entries()) resolvedStyleObjects.set(k, v);
25860
+ mergeBaseIntoSingleStyleFn({
25861
+ j: state.j,
25862
+ decl,
25863
+ styleObj,
25864
+ styleFnFromProps,
25865
+ styleFnDecls,
25866
+ remainingStyleKeys,
25867
+ extraStyleObjects,
25868
+ styledDecls: state.styledDecls
25869
+ });
25870
+ convertStyleFnsToPropsPattern(state.j, styleFnDecls, styleFnFromProps, decl.styleKey);
25871
+ insertStyleFnDeclsAfterComponent(resolvedStyleObjects, styleFnDecls, {
25872
+ styleKey: decl.styleKey,
25873
+ extraStyleObjects,
25874
+ remainingStyleKeys,
25875
+ attrBuckets,
25876
+ enumVariant: decl.enumVariant
25877
+ });
25564
25878
  if (styleFnDecls.has(decl.styleKey) && Object.keys(styleObj).length === 0) decl.skipBaseStyleRef = true;
25565
25879
  if (inlineStyleProps.length) decl.inlineStyleProps = inlineStyleProps;
25566
25880
  }
25567
25881
  /**
25882
+ * Inserts styleFnDecls entries into resolvedStyleObjects right after the last
25883
+ * entry belonging to the current component. This ensures dynamic style functions
25884
+ * appear adjacent to their static counterparts in stylex.create() output.
25885
+ */
25886
+ function insertStyleFnDeclsAfterComponent(resolvedStyleObjects, styleFnDecls, component) {
25887
+ if (styleFnDecls.size === 0) return;
25888
+ const componentKeys = /* @__PURE__ */ new Set();
25889
+ componentKeys.add(component.styleKey);
25890
+ for (const k of component.extraStyleObjects.keys()) componentKeys.add(k);
25891
+ for (const k of Object.values(component.remainingStyleKeys)) componentKeys.add(k);
25892
+ for (const k of component.attrBuckets.keys()) componentKeys.add(k);
25893
+ if (component.enumVariant) {
25894
+ componentKeys.add(component.enumVariant.baseKey);
25895
+ for (const c of component.enumVariant.cases) componentKeys.add(c.styleKey);
25896
+ }
25897
+ for (const k of styleFnDecls.keys()) if (resolvedStyleObjects.has(k)) componentKeys.add(k);
25898
+ let lastComponentKey = null;
25899
+ for (const k of resolvedStyleObjects.keys()) if (componentKeys.has(k)) lastComponentKey = k;
25900
+ if (lastComponentKey === null) {
25901
+ for (const [k, v] of styleFnDecls.entries()) resolvedStyleObjects.set(k, v);
25902
+ return;
25903
+ }
25904
+ const emittedFnKeys = /* @__PURE__ */ new Set();
25905
+ const entries = [...resolvedStyleObjects.entries()];
25906
+ resolvedStyleObjects.clear();
25907
+ for (const [k, v] of entries) {
25908
+ if (styleFnDecls.has(k)) {
25909
+ resolvedStyleObjects.set(k, styleFnDecls.get(k));
25910
+ emittedFnKeys.add(k);
25911
+ } else resolvedStyleObjects.set(k, v);
25912
+ if (k === lastComponentKey) {
25913
+ for (const [fk, fv] of styleFnDecls.entries()) if (!emittedFnKeys.has(fk)) {
25914
+ resolvedStyleObjects.set(fk, fv);
25915
+ emittedFnKeys.add(fk);
25916
+ }
25917
+ }
25918
+ }
25919
+ }
25920
+ /**
25921
+ * Merges static base properties into a single unconditional style function.
25922
+ *
25923
+ * When a styled component has both static CSS properties and a single
25924
+ * unconditional dynamic style function, the static properties are folded
25925
+ * into the function's return object so that the emitted code uses a single
25926
+ * `styles.key(arg)` call instead of separate `styles.key, styles.keyDynamic(arg)`.
25927
+ *
25928
+ * Preconditions:
25929
+ * - Exactly one unconditional styleFn entry (no conditionWhen)
25930
+ * - Base styleObj has at least one property
25931
+ * - No variant style keys, extra style objects, or enum variants
25932
+ * - The component is not extended by other styled components
25933
+ */
25934
+ function mergeBaseIntoSingleStyleFn(args) {
25935
+ const { j, decl, styleObj, styleFnFromProps, styleFnDecls, remainingStyleKeys, extraStyleObjects, styledDecls } = args;
25936
+ if (Object.keys(styleObj).length === 0) return;
25937
+ if (styleFnFromProps.length === 0) return;
25938
+ if (Object.keys(remainingStyleKeys).length > 0) return;
25939
+ if (extraStyleObjects.size > 0) return;
25940
+ if (decl.enumVariant) return;
25941
+ for (const other of styledDecls) if (other !== decl && other.extendsStyleKey === decl.styleKey) return;
25942
+ const unconditionalEntries = styleFnFromProps.filter((p) => !p.conditionWhen && p.condition === "always");
25943
+ if (unconditionalEntries.length !== 1) return;
25944
+ const entry = unconditionalEntries[0];
25945
+ const fnKey = entry.fnKey;
25946
+ const fnAst = styleFnDecls.get(fnKey);
25947
+ if (!fnAst || typeof fnAst !== "object") return;
25948
+ const body = getFunctionBodyExpr(fnAst);
25949
+ if (!body || body.type !== "ObjectExpression") return;
25950
+ const bodyObj = body;
25951
+ if (!Array.isArray(bodyObj.properties)) return;
25952
+ const existingKeys = /* @__PURE__ */ new Set();
25953
+ for (const prop of bodyObj.properties) {
25954
+ const key = prop.key;
25955
+ if (key) existingKeys.add(key.name ?? key.value ?? "");
25956
+ }
25957
+ if (Object.keys(styleObj).filter((k) => !k.startsWith("__")).some((k) => existingKeys.has(k))) return;
25958
+ const prependProps = [];
25959
+ for (const [cssProp, cssValue] of Object.entries(styleObj)) {
25960
+ if (cssProp.startsWith("__")) continue;
25961
+ const valueAst = literalToAst(j, cssValue);
25962
+ const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(cssProp) ? j.identifier(cssProp) : j.literal(cssProp);
25963
+ prependProps.push(j.property("init", key, valueAst));
25964
+ }
25965
+ bodyObj.properties.unshift(...prependProps);
25966
+ if (fnKey !== decl.styleKey) {
25967
+ styleFnDecls.delete(fnKey);
25968
+ styleFnDecls.set(decl.styleKey, fnAst);
25969
+ entry.fnKey = decl.styleKey;
25970
+ }
25971
+ for (const key of Object.keys(styleObj)) delete styleObj[key];
25972
+ }
25973
+ /**
25974
+ * Converts single-positional-param style functions to use a named `props`
25975
+ * object parameter. Skips functions that already use a `props` parameter
25976
+ * (e.g. consolidated multi-param functions).
25977
+ *
25978
+ * Before: `(color: string) => ({ color })`
25979
+ * After: `(props: { color: string }) => ({ color: props.color })`
25980
+ */
25981
+ function convertStyleFnsToPropsPattern(j, styleFnDecls, styleFnFromProps, baseStyleKey) {
25982
+ const managedFnKeys = new Set(styleFnFromProps.map((p) => p.fnKey));
25983
+ for (const [fnKey, fnAst] of styleFnDecls.entries()) {
25984
+ if (fnKey !== baseStyleKey) continue;
25985
+ if (!managedFnKeys.has(fnKey)) continue;
25986
+ if (!fnAst || typeof fnAst !== "object") continue;
25987
+ const fn = fnAst;
25988
+ if (!Array.isArray(fn.params) || fn.params.length !== 1) continue;
25989
+ const param = fn.params[0];
25990
+ if (param.type !== "Identifier" || !param.name || param.name === "props") continue;
25991
+ const paramName = param.name;
25992
+ const paramTypeAnnotation = param.typeAnnotation;
25993
+ const body = getFunctionBodyExpr(fn);
25994
+ if (!body || body.type !== "ObjectExpression") continue;
25995
+ replaceIdentifierInAst(j, body, paramName);
25996
+ const propsParam = j.identifier("props");
25997
+ if (paramTypeAnnotation) {
25998
+ const innerType = paramTypeAnnotation.typeAnnotation;
25999
+ if (innerType) {
26000
+ const propSignature = j.tsPropertySignature(j.identifier(paramName), j.tsTypeAnnotation(innerType));
26001
+ propsParam.typeAnnotation = j.tsTypeAnnotation(j.tsTypeLiteral([propSignature]));
26002
+ }
26003
+ }
26004
+ fn.params[0] = propsParam;
26005
+ for (const entry of styleFnFromProps) if (entry.fnKey === fnKey && !entry.propsObjectKey) entry.propsObjectKey = paramName;
26006
+ }
26007
+ }
26008
+ /**
26009
+ * Recursively replaces all `Identifier` references matching `oldName` with
26010
+ * `props.oldName` (a MemberExpression). Handles shorthand properties by
26011
+ * un-shorthanding them.
26012
+ */
26013
+ function replaceIdentifierInAst(j, node, oldName) {
26014
+ if (!node || typeof node !== "object") return;
26015
+ const n = node;
26016
+ if (n.type === "ObjectExpression") {
26017
+ const properties = n.properties;
26018
+ if (!Array.isArray(properties)) return;
26019
+ for (const prop of properties) {
26020
+ if (prop.type === "SpreadElement" || prop.type === "SpreadProperty") {
26021
+ if (prop.argument?.type === "Identifier" && prop.argument.name === oldName) prop.argument = j.memberExpression(j.identifier("props"), j.identifier(oldName));
26022
+ else replaceIdentifierInAst(j, prop.argument, oldName);
26023
+ continue;
26024
+ }
26025
+ if (prop.type !== "Property") continue;
26026
+ if (prop.shorthand && prop.value?.type === "Identifier" && prop.value.name === oldName) {
26027
+ prop.shorthand = false;
26028
+ prop.value = j.memberExpression(j.identifier("props"), j.identifier(oldName));
26029
+ continue;
26030
+ }
26031
+ if (prop.computed) if (prop.key?.type === "Identifier" && prop.key.name === oldName) prop.key = j.memberExpression(j.identifier("props"), j.identifier(oldName));
26032
+ else replaceIdentifierInAst(j, prop.key, oldName);
26033
+ if (prop.value?.type === "Identifier" && prop.value.name === oldName) prop.value = j.memberExpression(j.identifier("props"), j.identifier(oldName));
26034
+ else replaceIdentifierInAst(j, prop.value, oldName);
26035
+ }
26036
+ return;
26037
+ }
26038
+ for (const key of Object.keys(n)) {
26039
+ if (key === "type" || key === "loc" || key === "start" || key === "end" || key === "comments") continue;
26040
+ if (key === "property" && n.type === "MemberExpression" && !n.computed) continue;
26041
+ const child = n[key];
26042
+ if (Array.isArray(child)) for (let i = 0; i < child.length; i++) if (child[i]?.type === "Identifier" && child[i].name === oldName) child[i] = j.memberExpression(j.identifier("props"), j.identifier(oldName));
26043
+ else replaceIdentifierInAst(j, child[i], oldName);
26044
+ else if (child?.type === "Identifier" && child.name === oldName) n[key] = j.memberExpression(j.identifier("props"), j.identifier(oldName));
26045
+ else if (child && typeof child === "object" && child.type) replaceIdentifierInAst(j, child, oldName);
26046
+ }
26047
+ }
26048
+ /**
25568
26049
  * Merges a per-property condition bucket (pseudo or media) into the style object.
25569
26050
  * When a property already exists as an object in styleObj, merges entries to
25570
26051
  * preserve both pseudo-class and media query entries on the same property.
@@ -26623,123 +27104,11 @@ function appendToMapList(map, key, value) {
26623
27104
  else map.set(key, [value]);
26624
27105
  }
26625
27106
 
26626
- //#endregion
26627
- //#region src/internal/post-process/event-handler-annotations.ts
26628
- /** Maps event handler prop names to their React event type. */
26629
- const EVENT_TYPE_MAP = {
26630
- onClick: "React.MouseEvent",
26631
- onContextMenu: "React.MouseEvent",
26632
- onMouseDown: "React.MouseEvent",
26633
- onMouseUp: "React.MouseEvent",
26634
- onMouseEnter: "React.MouseEvent",
26635
- onMouseLeave: "React.MouseEvent",
26636
- onMouseMove: "React.MouseEvent",
26637
- onMouseOver: "React.MouseEvent",
26638
- onDoubleClick: "React.MouseEvent",
26639
- onKeyDown: "React.KeyboardEvent",
26640
- onKeyUp: "React.KeyboardEvent",
26641
- onKeyPress: "React.KeyboardEvent",
26642
- onChange: "React.ChangeEvent",
26643
- onInput: "React.FormEvent",
26644
- onFocus: "React.FocusEvent",
26645
- onBlur: "React.FocusEvent",
26646
- onSubmit: "React.FormEvent",
26647
- onScroll: "React.UIEvent",
26648
- onWheel: "React.WheelEvent",
26649
- onDragStart: "React.DragEvent",
26650
- onDragEnd: "React.DragEvent",
26651
- onDrop: "React.DragEvent",
26652
- onDragOver: "React.DragEvent",
26653
- onTouchStart: "React.TouchEvent",
26654
- onTouchEnd: "React.TouchEvent",
26655
- onTouchMove: "React.TouchEvent",
26656
- onPointerDown: "React.PointerEvent",
26657
- onPointerUp: "React.PointerEvent",
26658
- onPointerMove: "React.PointerEvent",
26659
- onCopy: "React.ClipboardEvent",
26660
- onCut: "React.ClipboardEvent",
26661
- onPaste: "React.ClipboardEvent",
26662
- onAnimationEnd: "React.AnimationEvent",
26663
- onAnimationStart: "React.AnimationEvent",
26664
- onTransitionEnd: "React.TransitionEvent"
26665
- };
26666
- /** Maps intrinsic JSX tag names to their DOM element interface names. */
26667
- const INTRINSIC_TAG_TO_ELEMENT_TYPE = {
26668
- a: "HTMLAnchorElement",
26669
- button: "HTMLButtonElement",
26670
- div: "HTMLDivElement",
26671
- form: "HTMLFormElement",
26672
- img: "HTMLImageElement",
26673
- input: "HTMLInputElement",
26674
- label: "HTMLLabelElement",
26675
- li: "HTMLLIElement",
26676
- ol: "HTMLOListElement",
26677
- option: "HTMLOptionElement",
26678
- select: "HTMLSelectElement",
26679
- span: "HTMLSpanElement",
26680
- svg: "SVGSVGElement",
26681
- table: "HTMLTableElement",
26682
- tbody: "HTMLTableSectionElement",
26683
- td: "HTMLTableCellElement",
26684
- textarea: "HTMLTextAreaElement",
26685
- th: "HTMLTableCellElement",
26686
- thead: "HTMLTableSectionElement",
26687
- tr: "HTMLTableRowElement",
26688
- ul: "HTMLUListElement"
26689
- };
26690
- /**
26691
- * Annotates event handler arrow function parameters at JSX usage sites of converted components.
26692
- *
26693
- * `componentTagMap` is best-effort and only populated for intrinsic-base conversions.
26694
- * For wrappers around non-intrinsic components, event annotations stay non-generic.
26695
- *
26696
- * @returns true if any annotations were added
26697
- */
26698
- function annotateEventHandlerParams(args) {
26699
- const { root, j, convertedNames, componentTagMap } = args;
26700
- if (convertedNames.size === 0) return false;
26701
- let changed = false;
26702
- root.find(j.JSXOpeningElement).filter((path) => {
26703
- const name = path.node.name;
26704
- if (name.type === "JSXIdentifier") return convertedNames.has(name.name);
26705
- return false;
26706
- }).forEach((jsxPath) => {
26707
- const openingName = jsxPath.node.name;
26708
- const componentName = openingName.type === "JSXIdentifier" ? openingName.name : null;
26709
- const intrinsicTag = componentName ? componentTagMap.get(componentName) : void 0;
26710
- const elementType = intrinsicTag ? INTRINSIC_TAG_TO_ELEMENT_TYPE[intrinsicTag] : void 0;
26711
- for (const attr of jsxPath.node.attributes ?? []) {
26712
- if (attr.type !== "JSXAttribute" || !attr.name || attr.name.type !== "JSXIdentifier") continue;
26713
- const eventType = EVENT_TYPE_MAP[attr.name.name];
26714
- if (!eventType) continue;
26715
- const value = attr.value;
26716
- if (!value || value.type !== "JSXExpressionContainer") continue;
26717
- const expr = value.expression;
26718
- if (!expr || expr.type !== "ArrowFunctionExpression") continue;
26719
- const firstParam = expr.params[0];
26720
- if (!firstParam) continue;
26721
- if (firstParam.typeAnnotation) continue;
26722
- if (firstParam.type !== "Identifier") continue;
26723
- const parts = eventType.split(".");
26724
- const typeRef = j.tsTypeReference(j.tsQualifiedName(j.identifier(parts[0]), j.identifier(parts[1])));
26725
- if (elementType) typeRef.typeParameters = j.tsTypeParameterInstantiation([j.tsTypeReference(j.identifier(elementType))]);
26726
- const annotatedParam = j.identifier(firstParam.name);
26727
- annotatedParam.typeAnnotation = j.tsTypeAnnotation(typeRef);
26728
- const newArrow = j.arrowFunctionExpression([annotatedParam, ...expr.params.slice(1)], expr.body, expr.expression);
26729
- newArrow.async = expr.async ?? false;
26730
- newArrow.returnType = expr.returnType ?? null;
26731
- value.expression = newArrow;
26732
- changed = true;
26733
- }
26734
- });
26735
- return changed;
26736
- }
26737
-
26738
27107
  //#endregion
26739
27108
  //#region src/internal/transform-steps/post-process.ts
26740
27109
  /**
26741
27110
  * Step: post-process transformed AST and cleanup imports.
26742
- * Core concepts: relation overrides, import reconciliation, and event handler annotations.
27111
+ * Core concepts: relation overrides and import reconciliation.
26743
27112
  */
26744
27113
  /**
26745
27114
  * Performs post-processing rewrites, import cleanup, and descendant/ancestor selector adjustments.
@@ -26799,34 +27168,7 @@ function postProcessStep(ctx) {
26799
27168
  return true;
26800
27169
  }).size() === 0) fnPaths.forEach((p) => p.prune());
26801
27170
  }
26802
- if (/\.(ts|tsx)$/.test(file.path)) {
26803
- const hasLocalAsUsage = (componentName) => {
26804
- const hasAsAttr = (attrs) => (attrs ?? []).some((attr) => attr?.type === "JSXAttribute" && attr.name?.type === "JSXIdentifier" && (attr.name.name === "as" || attr.name.name === "forwardedAs"));
26805
- if (root.find(j.JSXElement, { openingElement: { name: {
26806
- type: "JSXIdentifier",
26807
- name: componentName
26808
- } } }).some((p) => hasAsAttr(p.node?.openingElement?.attributes))) return true;
26809
- return root.find(j.JSXSelfClosingElement, { name: {
26810
- type: "JSXIdentifier",
26811
- name: componentName
26812
- } }).some((p) => hasAsAttr(p.node?.attributes));
26813
- };
26814
- const convertedNames = /* @__PURE__ */ new Set();
26815
- const componentTagMap = /* @__PURE__ */ new Map();
26816
- for (const decl of styledDecls) {
26817
- const isPolymorphic = !!decl.supportsAsProp || hasLocalAsUsage(decl.localName);
26818
- if (decl.base.kind === "intrinsic" && !isPolymorphic) {
26819
- convertedNames.add(decl.localName);
26820
- componentTagMap.set(decl.localName, decl.base.tagName);
26821
- }
26822
- }
26823
- if (annotateEventHandlerParams({
26824
- root,
26825
- j,
26826
- convertedNames,
26827
- componentTagMap
26828
- })) ctx.markChanged();
26829
- }
27171
+ if (/\.(ts|tsx)$/.test(file.path)) {}
26830
27172
  return CONTINUE;
26831
27173
  }
26832
27174
 
@@ -26921,13 +27263,16 @@ function rewriteJsxStep(ctx) {
26921
27263
  if (renamed) attr.name.name = renamed;
26922
27264
  }
26923
27265
  });
26924
- continue;
27266
+ if (!decl.promotedStyleProps?.length) continue;
26925
27267
  }
26926
27268
  root.find(j.JSXElement, { openingElement: { name: {
26927
27269
  type: "JSXIdentifier",
26928
27270
  name: decl.localName
26929
27271
  } } }).forEach((p) => {
26930
27272
  const opening = p.node.openingElement;
27273
+ if (decl.needsWrapperComponent) {
27274
+ if (!(opening.__promotedStyleKey && !opening.__promotedMergeIntoBase || opening.__promotedMergeArgs)) return;
27275
+ }
26931
27276
  const closing = p.node.closingElement;
26932
27277
  let finalTag = decl.base.kind === "intrinsic" ? decl.base.tagName : decl.base.ident;
26933
27278
  const inlineVariantDimensions = decl.inlinedBaseComponent?.hasInlineJsxVariants ? decl.variantDimensions ?? [] : [];
@@ -27049,10 +27394,13 @@ function rewriteJsxStep(ctx) {
27049
27394
  }
27050
27395
  }
27051
27396
  const baseStyleKey = matchedCombinedStyleKey ?? decl.styleKey;
27397
+ const mergeArgs = matchedCombinedStyleKey ? void 0 : opening.__promotedMergeArgs;
27398
+ const baseMember = j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(baseStyleKey));
27399
+ const baseExpr = mergeArgs ? j.callExpression(baseMember, mergeArgs) : baseMember;
27052
27400
  const styleArgs = [
27053
27401
  ...decl.extendsStyleKey ? [j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(decl.extendsStyleKey))] : [],
27054
27402
  ...extraMixinArgs,
27055
- j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(baseStyleKey)),
27403
+ baseExpr,
27056
27404
  ...extraAfterBaseArgs
27057
27405
  ];
27058
27406
  const variantKeys = decl.variantStyleKeys ?? {};
@@ -27095,7 +27443,10 @@ function rewriteJsxStep(ctx) {
27095
27443
  if (styleFnProps.has(n)) {
27096
27444
  const pairs = styleFnPairs.filter((p) => p.jsxProp === n);
27097
27445
  const valueExpr = !attr.value ? j.literal(true) : attr.value.type === "StringLiteral" ? j.literal(attr.value.value) : attr.value.type === "Literal" ? j.literal(attr.value.value) : attr.value.type === "JSXExpressionContainer" ? attr.value.expression : null;
27098
- if (valueExpr) for (const p of pairs) styleArgs.push(j.callExpression(j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(p.fnKey)), [valueExpr]));
27446
+ if (valueExpr) for (const p of pairs) {
27447
+ const callArg = wrapCallArgForPropsObject(j, valueExpr, p.propsObjectKey);
27448
+ styleArgs.push(j.callExpression(j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(p.fnKey)), [callArg]));
27449
+ }
27099
27450
  return;
27100
27451
  }
27101
27452
  if (combinedStylePropNames.has(n) && matchedCombinedStyleKey) return;