styled-components-to-stylex-codemod 0.0.20 → 0.0.22

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.
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as defineAdapter, i as AdapterInput, t as CollectedWarning } from "./logger-DSmZSMxN.mjs";
1
+ import { a as defineAdapter, i as AdapterInput, t as CollectedWarning } from "./logger-nDTvuKsY.mjs";
2
2
 
3
3
  //#region src/run.d.ts
4
4
  interface RunTransformOptions {
package/dist/index.mjs CHANGED
@@ -143,7 +143,7 @@ async function runTransform(options) {
143
143
  ].join("\n"));
144
144
  const { createModuleResolver } = await import("./resolve-imports-4bFqrkrQ.mjs");
145
145
  const sharedResolver = createModuleResolver();
146
- const { runPrepass } = await import("./run-prepass-BuYfEK4M.mjs");
146
+ const { runPrepass } = await import("./run-prepass-DtSt5v9e.mjs");
147
147
  const absoluteFiles = filePaths.map((f) => resolve(f));
148
148
  const absoluteConsumers = consumerFilePaths.map((f) => resolve(f));
149
149
  let prepassResult;
@@ -613,7 +613,7 @@ declare function defineAdapter<T extends AdapterInput>(adapter: T): T;
613
613
  //#endregion
614
614
  //#region src/internal/logger.d.ts
615
615
  type Severity = "info" | "warning" | "error";
616
- type WarningType = "`css` helper function switch must return css templates in all branches" | "`css` helper usage as a function call (css(...)) is not supported" | "`css` helper used outside of a styled component template cannot be statically transformed" | "Adapter helper call in border interpolation did not resolve to a single CSS value" | "Adapter resolveCall returned an unparseable styles expression" | "Adapter resolveCall returned an unparseable value expression" | "Adapter resolveCall returned StyleX styles for helper call where a CSS value was expected" | "Adapter resolveCall returned undefined for helper call" | "Adapter resolveBaseComponent threw an error" | "Adapter resolved StyleX styles cannot be applied under nested selectors/at-rules" | "Adapter resolved StyleX styles inside pseudo selector but did not provide cssText for property expansion — add cssText to resolveCall result to enable pseudo-wrapping" | 'Adapter resolveCall cssText could not be parsed as CSS declarations — expected semicolon-separated property: value pairs (e.g. "white-space: nowrap; overflow: hidden;")' | "Adapter resolveValue returned an unparseable value expression" | "Adapter resolveValue returned undefined for imported value" | "Arrow function: body is not a recognized pattern (expected ternary, logical, call, or member expression)" | "Arrow function: conditional branches could not be resolved to static or theme values" | "Arrow function: helper call body is not supported" | "Arrow function: indexed theme lookup pattern not matched" | "Arrow function: logical expression pattern not supported" | "Arrow function: prop access cannot be converted to style function for this CSS property" | "Arrow function: theme access path could not be resolved" | "Component selectors like `${OtherComponent}:hover &` are not directly representable in StyleX. Manual refactor is required" | "Conditional `css` block: !important is not supported in StyleX" | "Conditional `css` block: @-rules (e.g., @media, @supports) are not supported" | "CSS block contains unsupported at-rule (only @media and @container are supported; @supports, etc. require manual handling)" | "Conditional `css` block: dynamic interpolation could not be resolved to a single component prop" | "Conditional `css` block: failed to parse expression" | "Conditional `css` block: missing CSS property name" | "Conditional `css` block: missing interpolation expression" | "Conditional `css` block: mixed static/dynamic values with non-theme expressions cannot be safely transformed" | "Conditional `css` block: multiple interpolation slots in a single property value" | "Conditional `css` block: ternary branch value could not be resolved (imported values require adapter support)" | "Conditional `css` block: ternary expressions inside pseudo selectors are not supported" | "Conditional `css` block: unsupported selector" | "Directional border helper styles are not supported" | "Multi-slot border interpolation could not be resolved" | "createGlobalStyle is not supported in StyleX. Global styles should be handled separately (e.g., in a CSS file or using CSS reset libraries)" | "Dynamic styles inside pseudo elements (::before/::after) are not supported by StyleX. See https://github.com/facebook/stylex/issues/1396" | "Failed to parse theme expressions" | "Heterogeneous background values (mix of gradients and colors) not currently supported" | "Higher-order styled factory wrappers (e.g. hoc(styled)) are not supported" | "Imported CSS helper mixins: cannot determine inherited properties for correct pseudo selector handling" | "Local helper function returns CSS that cannot be decomposed into individual properties" | "Local helper function computes CSS values that cannot be statically traced to the component prop" | "Styled-components specificity hacks like `&&` / `&&&` are not representable in StyleX" | "Theme-dependent block-level conditional could not be fully resolved (branches may contain dynamic interpolations)" | "Theme-dependant call expression could not be resolved (e.g. theme helper calls like theme.highlight() are not supported)" | "Theme value with fallback (props.theme.X ?? / || default) cannot be resolved statically — use adapter.resolveValue to map theme paths to StyleX tokens" | "Theme-dependent nested prop access requires a project-specific theme source (e.g. useTheme())" | "Theme-dependent template literals require a project-specific theme source (e.g. useTheme())" | "Theme prop overrides on styled components are not supported" | "Universal selectors (`*`) are currently unsupported" | "Unsupported call expression (expected imported helper(...) or imported helper(...)(...))" | "Unsupported conditional test in shouldForwardProp" | "Unsupported shouldForwardProp pattern (only !prop.startsWith(), ![].includes(prop), and prop !== are supported)" | "Unsupported interpolation: arrow function" | "Unsupported interpolation: call expression" | "Unsupported interpolation: identifier" | "Unsupported interpolation: member expression" | "Unsupported interpolation: property" | "Unsupported interpolation: unknown" | "Unsupported nested conditional interpolation" | "Unsupported prop-based inline style expression cannot be safely inlined" | "Unsupported prop-based inline style props.theme access is not supported" | "Unsupported selector interpolation: imported value in selector position" | "Unsupported selector: class selector" | "Unsupported selector: comma-separated selectors must all be simple pseudos or pseudo-elements" | "Unsupported selector: descendant pseudo selector (space before pseudo)" | "Unsupported selector: descendant/child/sibling selector" | "Unsupported selector: interpolated pseudo selector" | "Unsupported selector: sibling combinator" | "Unsupported selector: unresolved interpolation in sibling selector" | "Unsupported selector: ambiguous element selector" | "Unsupported selector: attribute selector on unsupported element" | "Unsupported selector: element selector on exported component" | "Unsupported selector: element selector with combined ancestor and child pseudos" | "Unsupported selector: element selector with dynamic children" | "Unsupported selector: element selector with plain intrinsic children" | "Unsupported selector: element selector pseudo collision" | "Unsupported selector: unresolved interpolation in cross-file component selector" | "Unsupported selector: unresolved interpolation in descendant component selector" | "Unsupported selector: unresolved interpolation in element selector" | "Unsupported selector: unresolved interpolation in reverse component selector" | "Unsupported selector: grouped reverse selector references different components" | "Unsupported selector: unknown component selector" | "Unsupported css`` mixin: after-base mixin style is not a plain object" | "Unsupported css`` mixin: nested contextual conditions in after-base mixin" | "Unsupported css`` mixin: cannot infer base default for after-base contextual override (base value is non-literal)" | "css`` helper function interpolation references closure variable that cannot be hoisted" | "Sibling selector broadened: & + & (adjacent) becomes general sibling (~) in StyleX — interleaved non-matching elements will no longer block the match" | "Using styled-components components as mixins is not supported; use css`` mixins or strings instead" | "styled(ImportedComponent) wraps a component whose file contains internal styled-components — convert the base component's file first to avoid CSS cascade conflicts";
616
+ type WarningType = "`css` helper function switch must return css templates in all branches" | "`css` helper usage as a function call (css(...)) is not supported" | "`css` helper used outside of a styled component template cannot be statically transformed" | "Adapter helper call in border interpolation did not resolve to a single CSS value" | "Adapter resolveCall returned an unparseable styles expression" | "Adapter resolveCall returned an unparseable value expression" | "Adapter resolveCall returned StyleX styles for helper call where a CSS value was expected" | "Adapter resolveCall returned undefined for helper call" | "Adapter resolveBaseComponent threw an error" | "Adapter resolved StyleX styles cannot be applied under nested selectors/at-rules" | "Adapter resolved StyleX styles inside pseudo selector but did not provide cssText for property expansion — add cssText to resolveCall result to enable pseudo-wrapping" | 'Adapter resolveCall cssText could not be parsed as CSS declarations — expected semicolon-separated property: value pairs (e.g. "white-space: nowrap; overflow: hidden;")' | "Adapter resolveValue returned an unparseable value expression" | "Adapter resolveValue returned undefined for imported value" | "Arrow function: body is not a recognized pattern (expected ternary, logical, call, or member expression)" | "Arrow function: conditional branches could not be resolved to static or theme values" | "Arrow function: helper call body is not supported" | "Arrow function: indexed theme lookup pattern not matched" | "Arrow function: logical expression pattern not supported" | "Arrow function: prop access cannot be converted to style function for this CSS property" | "Arrow function: theme access path could not be resolved" | "Component selectors like `${OtherComponent}:hover &` are not directly representable in StyleX. Manual refactor is required" | "Conditional `css` block: !important is not supported in StyleX" | "Conditional `css` block: @-rules (e.g., @media, @supports) are not supported" | "CSS block contains unsupported at-rule (only @media and @container are supported; @supports, etc. require manual handling)" | "Conditional `css` block: dynamic interpolation could not be resolved to a single component prop" | "Conditional `css` block: failed to parse expression" | "Conditional `css` block: missing CSS property name" | "Conditional `css` block: missing interpolation expression" | "Conditional `css` block: mixed static/dynamic values with non-theme expressions cannot be safely transformed" | "Conditional `css` block: multiple interpolation slots in a single property value" | "Conditional `css` block: ternary branch value could not be resolved (imported values require adapter support)" | "Conditional `css` block: ternary expressions inside pseudo selectors are not supported" | "Conditional `css` block: media query interpolation must be a simple imported reference (expressions like `value + 1` are not supported)" | "Conditional `css` block: unsupported selector" | "Directional border helper styles are not supported" | "Multi-slot border interpolation could not be resolved" | "Resolved border helper value could not be expanded to longhand properties" | "createGlobalStyle is not supported in StyleX. Global styles should be handled separately (e.g., in a CSS file or using CSS reset libraries)" | "Dynamic styles inside pseudo elements (::before/::after) are not supported by StyleX. See https://github.com/facebook/stylex/issues/1396" | "Failed to parse theme expressions" | "Heterogeneous background values (mix of gradients and colors) not currently supported" | "Higher-order styled factory wrappers (e.g. hoc(styled)) are not supported" | "Imported CSS helper mixins: cannot determine inherited properties for correct pseudo selector handling" | "Local helper function returns CSS that cannot be decomposed into individual properties" | "Local helper function computes CSS values that cannot be statically traced to the component prop" | "Styled-components specificity hacks like `&&` / `&&&` are not representable in StyleX" | "Theme-dependent block-level conditional could not be fully resolved (branches may contain dynamic interpolations)" | "Theme-dependant call expression could not be resolved (e.g. theme helper calls like theme.highlight() are not supported)" | "Theme value with fallback (props.theme.X ?? / || default) cannot be resolved statically — use adapter.resolveValue to map theme paths to StyleX tokens" | "Theme-dependent nested prop access requires a project-specific theme source (e.g. useTheme())" | "Theme-dependent template literals require a project-specific theme source (e.g. useTheme())" | "Theme prop overrides on styled components are not supported" | "Universal selectors (`*`) are currently unsupported" | "Unsupported call expression (expected imported helper(...) or imported helper(...)(...))" | "Unsupported conditional test in shouldForwardProp" | "Unsupported shouldForwardProp pattern (only !prop.startsWith(), ![].includes(prop), and prop !== are supported)" | "Unsupported interpolation: arrow function" | "Unsupported interpolation: call expression" | "Unsupported interpolation: identifier" | "Unsupported interpolation: member expression" | "Unsupported interpolation: property" | "Unsupported interpolation: unknown" | "Unsupported nested conditional interpolation" | "Unsupported prop-based inline style expression cannot be safely inlined" | "Unsupported prop-based inline style props.theme access is not supported" | "Unsupported selector interpolation: imported value in selector position" | "Unsupported: media query interpolation must be a simple imported reference (expressions like `value + 1` are not supported)" | "Unsupported selector: class selector" | "Unsupported selector: comma-separated selectors must all be simple pseudos or pseudo-elements" | "Unsupported selector: descendant pseudo selector (space before pseudo)" | "Unsupported selector: descendant/child/sibling selector" | "Unsupported selector: interpolated pseudo selector" | "Unsupported selector: sibling combinator" | "Unsupported selector: unresolved interpolation in sibling selector" | "Unsupported selector: ambiguous element selector" | "Unsupported selector: attribute selector on unsupported element" | "Unsupported selector: element selector on exported component" | "Unsupported selector: element selector with combined ancestor and child pseudos" | "Unsupported selector: element selector with dynamic children" | "Unsupported selector: element selector with plain intrinsic children" | "Unsupported selector: element selector pseudo collision" | "Unsupported selector: unresolved interpolation in cross-file component selector" | "Unsupported selector: unresolved interpolation in descendant component selector" | "Unsupported selector: unresolved interpolation in element selector" | "Unsupported selector: unresolved interpolation in reverse component selector" | "Unsupported selector: grouped reverse selector references different components" | "Unsupported selector: unknown component selector" | "Unsupported css`` mixin: after-base mixin style is not a plain object" | "Unsupported css`` mixin: nested contextual conditions in after-base mixin" | "Unsupported css`` mixin: cannot infer base default for after-base contextual override (base value is non-literal)" | "css`` helper function interpolation references closure variable that cannot be hoisted" | "Sibling selector broadened: & + & (adjacent) becomes general sibling (~) in StyleX — interleaved non-matching elements will no longer block the match" | "Using styled-components components as mixins is not supported; use css`` mixins or strings instead" | "styled(ImportedComponent) wraps a component whose file contains internal styled-components — convert the base component's file first to avoid CSS cascade conflicts";
617
617
  interface WarningLog {
618
618
  severity: Severity;
619
619
  type: WarningType;
@@ -575,13 +575,20 @@ async function runPrepass(options) {
575
575
  wrappedName: m[2]
576
576
  });
577
577
  }
578
- if (hasAsProp) {
579
- JSX_AS_COMPONENT_RE.lastIndex = 0;
580
- for (const m of source.matchAll(JSX_AS_COMPONENT_RE)) if (m[1]) addToSetMap(asUsages, m[1], filePath);
581
- }
582
- if (hasRefProp) {
583
- JSX_REF_COMPONENT_RE.lastIndex = 0;
584
- for (const m of source.matchAll(JSX_REF_COMPONENT_RE)) if (m[1]) addToSetMap(refUsages, m[1], filePath);
578
+ if (hasAsProp || hasRefProp) {
579
+ let aliasMap;
580
+ const resolveTagName = (tagName) => {
581
+ aliasMap ??= buildLocalToImportedMap(source);
582
+ return aliasMap.get(tagName) ?? tagName;
583
+ };
584
+ if (hasAsProp) {
585
+ JSX_AS_COMPONENT_RE.lastIndex = 0;
586
+ for (const m of source.matchAll(JSX_AS_COMPONENT_RE)) if (m[1]) addToSetMap(asUsages, resolveTagName(m[1]), filePath);
587
+ }
588
+ if (hasRefProp) {
589
+ JSX_REF_COMPONENT_RE.lastIndex = 0;
590
+ for (const m of source.matchAll(JSX_REF_COMPONENT_RE)) if (m[1]) addToSetMap(refUsages, resolveTagName(m[1]), filePath);
591
+ }
585
592
  }
586
593
  }
587
594
  const styledFileCount = fileContents.size;
@@ -746,11 +753,14 @@ const IMPORT_ALIAS_ENTRY_RE = /\b(\w+)\s+as\s+(\w+)/g;
746
753
  /**
747
754
  * Build a mapping from local alias names to original imported names for a source file.
748
755
  * Only includes PascalCase names that differ from their original (actual aliases).
756
+ * Scans only import declarations to avoid false positives from TypeScript `as` casts
757
+ * (e.g., `foo as Button` would incorrectly map `Button → foo`).
749
758
  */
750
759
  function buildLocalToImportedMap(source) {
760
+ const importText = collectImportDeclarationText(source);
751
761
  const map = /* @__PURE__ */ new Map();
752
762
  IMPORT_ALIAS_ENTRY_RE.lastIndex = 0;
753
- for (const m of source.matchAll(IMPORT_ALIAS_ENTRY_RE)) {
763
+ for (const m of importText.matchAll(IMPORT_ALIAS_ENTRY_RE)) {
754
764
  const original = m[1];
755
765
  const local = m[2];
756
766
  if (original !== local && /^[A-Z]/.test(local)) map.set(local, original);
@@ -1,4 +1,4 @@
1
- import { n as WarningLog, r as Adapter } from "./logger-DSmZSMxN.mjs";
1
+ import { n as WarningLog, r as Adapter } from "./logger-nDTvuKsY.mjs";
2
2
  import "stylis";
3
3
  import { API, FileInfo, Options } from "jscodeshift";
4
4
 
@@ -1600,12 +1600,12 @@ function analyzeBeforeEmitStep(ctx) {
1600
1600
  };
1601
1601
  if (!ctx.stylesInsertPosition && isUsedAtModuleLevel()) ctx.stylesInsertPosition = "afterImports";
1602
1602
  if (ctx.resolvedStyleObjects) for (const decl of styledDecls) {
1603
- if (!decl.staticBooleanVariants?.length) continue;
1604
- for (const { propName, styleKey, styles } of decl.staticBooleanVariants) {
1603
+ if (decl.staticBooleanVariants?.length) for (const { propName, styleKey, styles } of decl.staticBooleanVariants) {
1605
1604
  ctx.resolvedStyleObjects.set(styleKey, styles);
1606
1605
  if (!decl.variantStyleKeys) decl.variantStyleKeys = {};
1607
1606
  decl.variantStyleKeys[propName] = styleKey;
1608
1607
  }
1608
+ if (decl.callSiteCombinedStyles?.length) for (const { styleKey, styles } of decl.callSiteCombinedStyles) ctx.resolvedStyleObjects.set(styleKey, styles);
1609
1609
  }
1610
1610
  return CONTINUE;
1611
1611
  }
@@ -5933,6 +5933,61 @@ function expandStaticAnimationShorthand(value, inlineKeyframeNames, j, styleObj,
5933
5933
  if (classified.iteration) styleObj.animationIterationCount = classified.iteration;
5934
5934
  return true;
5935
5935
  }
5936
+ /**
5937
+ * Resolves an interpolated animation declaration whose slot expressions
5938
+ * reference keyframes identifiers.
5939
+ *
5940
+ * Supports both the `animation` shorthand and `animation-name` longhand.
5941
+ * For shorthands, replaces each `__SC_EXPR_N__` placeholder with the
5942
+ * corresponding keyframes name, then delegates to
5943
+ * `expandStaticAnimationShorthand`. For `animation-name`, resolves the
5944
+ * single slot directly to a JS identifier.
5945
+ *
5946
+ * Bails (returns null) on comma-separated multi-animation shorthands,
5947
+ * which the single-tuple parser cannot correctly model.
5948
+ */
5949
+ function expandInterpolatedAnimationShorthand(args) {
5950
+ const { property = "animation", valueRaw, slotExprById, keyframesNames, j, inlineKeyframeNameMap } = args;
5951
+ if (property === "animation-name") return resolveAnimationNameSlot(valueRaw, slotExprById, keyframesNames, j);
5952
+ if (valueRaw.includes(",")) return null;
5953
+ return resolveAnimationShorthandSlots(valueRaw, slotExprById, keyframesNames, j, inlineKeyframeNameMap);
5954
+ }
5955
+ /** Resolves `animation-name: ${kf}` where the sole slot is a keyframes identifier. */
5956
+ function resolveAnimationNameSlot(valueRaw, slotExprById, keyframesNames, j) {
5957
+ const kfName = extractKeyframesIdentifierFromSlot(valueRaw, slotExprById, keyframesNames);
5958
+ if (!kfName) return null;
5959
+ return { animationName: j.identifier(kfName) };
5960
+ }
5961
+ /** Resolves `animation: ${kf} 1.6s ease-in-out infinite` by replacing placeholders and expanding. */
5962
+ function resolveAnimationShorthandSlots(valueRaw, slotExprById, keyframesNames, j, inlineKeyframeNameMap) {
5963
+ let modifiedValue = valueRaw;
5964
+ const usedKeyframeNames = /* @__PURE__ */ new Set();
5965
+ const placeholderRe = /__SC_EXPR_(\d+)__/g;
5966
+ let match;
5967
+ while (match = placeholderRe.exec(valueRaw)) {
5968
+ const slotId = Number(match[1]);
5969
+ const expr = slotExprById.get(slotId);
5970
+ if (!expr || expr.type !== "Identifier" || !expr.name || !keyframesNames.has(expr.name)) return null;
5971
+ modifiedValue = modifiedValue.replace(match[0], expr.name);
5972
+ usedKeyframeNames.add(expr.name);
5973
+ }
5974
+ if (usedKeyframeNames.size === 0) return null;
5975
+ const expanded = {};
5976
+ if (expandStaticAnimationShorthand(modifiedValue, usedKeyframeNames, j, expanded, inlineKeyframeNameMap)) return expanded;
5977
+ return null;
5978
+ }
5979
+ /**
5980
+ * If `valueRaw` is exactly a single `__SC_EXPR_N__` placeholder whose slot
5981
+ * expression is a keyframes identifier, returns the identifier name.
5982
+ */
5983
+ function extractKeyframesIdentifierFromSlot(valueRaw, slotExprById, keyframesNames) {
5984
+ const m = valueRaw.match(/^__SC_EXPR_(\d+)__$/);
5985
+ if (!m) return null;
5986
+ const slotId = Number(m[1]);
5987
+ const expr = slotExprById.get(slotId);
5988
+ if (!expr || expr.type !== "Identifier" || !expr.name || !keyframesNames.has(expr.name)) return null;
5989
+ return expr.name;
5990
+ }
5936
5991
 
5937
5992
  //#endregion
5938
5993
  //#region src/internal/transform-steps/convert-keyframes.ts
@@ -6439,7 +6494,8 @@ function emptyOkVariantOutcome(hasLocalCallsites, usedConsumedPropsAtCallSites)
6439
6494
  usedConsumedPropsAtCallSites,
6440
6495
  foldedBaseSx: {},
6441
6496
  bakedInConsumedProps: [],
6442
- staticBooleanVariants: []
6497
+ staticBooleanVariants: [],
6498
+ callSiteCombinedStyles: []
6443
6499
  };
6444
6500
  }
6445
6501
  function applyBaseComponentResolution(ctx, styledDecls) {
@@ -6490,7 +6546,8 @@ function applyBaseComponentResolution(ctx, styledDecls) {
6490
6546
  usedConsumedPropsAtCallSites: variantOutcome.usedConsumedPropsAtCallSites,
6491
6547
  foldedBaseSx: variantOutcome.foldedBaseSx,
6492
6548
  bakedInConsumedProps: variantOutcome.bakedInConsumedProps,
6493
- staticBooleanVariants: variantOutcome.staticBooleanVariants
6549
+ staticBooleanVariants: variantOutcome.staticBooleanVariants,
6550
+ callSiteCombinedStyles: variantOutcome.callSiteCombinedStyles
6494
6551
  });
6495
6552
  }
6496
6553
  }
@@ -6644,7 +6701,7 @@ function buildInlineResolverVariantDimensions(args) {
6644
6701
  continue;
6645
6702
  }
6646
6703
  const [singleKey, singleVariantStyles] = Object.entries(variants)[0];
6647
- if (singleKey === "true" && booleanOnlyProps.has(propName)) {
6704
+ if (isSingleVariantKeyTruthy(singleKey, booleanOnlyProps.has(propName))) {
6648
6705
  staticBooleanVariants.push({
6649
6706
  propName,
6650
6707
  styleKey: `${decl.styleKey}${toSuffixFromProp(propName)}`,
@@ -6661,6 +6718,31 @@ function buildInlineResolverVariantDimensions(args) {
6661
6718
  propTypeFromKeyof: true
6662
6719
  });
6663
6720
  }
6721
+ const callSiteCombinedStyles = buildCallSiteCombinedStyles({
6722
+ decl,
6723
+ staticBooleanVariants,
6724
+ dimensions,
6725
+ baseSx: {
6726
+ ...baseResult.sx,
6727
+ ...foldedBaseSx
6728
+ },
6729
+ propsByUsage: usageResult.propsByUsage,
6730
+ hasCompleteCallsiteVisibility: !willHaveExternalInterface(ctx, decl, styledDecls) && !decl.usedAsValue,
6731
+ hasPropReferencingTemplateExpressions: (decl.templateExpressions ?? []).some((expr) => {
6732
+ const type = expr?.type;
6733
+ return type === "ArrowFunctionExpression" || type === "FunctionExpression";
6734
+ })
6735
+ });
6736
+ if (callSiteCombinedStyles) return {
6737
+ kind: "ok",
6738
+ variantDimensions: dimensions,
6739
+ hasLocalCallsites,
6740
+ usedConsumedPropsAtCallSites,
6741
+ foldedBaseSx,
6742
+ bakedInConsumedProps,
6743
+ staticBooleanVariants: [],
6744
+ callSiteCombinedStyles
6745
+ };
6664
6746
  return {
6665
6747
  kind: "ok",
6666
6748
  variantDimensions: dimensions,
@@ -6668,7 +6750,8 @@ function buildInlineResolverVariantDimensions(args) {
6668
6750
  usedConsumedPropsAtCallSites,
6669
6751
  foldedBaseSx,
6670
6752
  bakedInConsumedProps,
6671
- staticBooleanVariants
6753
+ staticBooleanVariants,
6754
+ callSiteCombinedStyles: []
6672
6755
  };
6673
6756
  }
6674
6757
  /**
@@ -6804,12 +6887,59 @@ function diffSx(baseSx, siteSx) {
6804
6887
  for (const [prop, value] of Object.entries(siteSx)) if (!(prop in baseSx)) out[prop] = value;
6805
6888
  return out;
6806
6889
  }
6890
+ /**
6891
+ * For direct JSX resolution, merges individual single-key variant styles into
6892
+ * combined per-call-site entries. Returns the combined styles, or null if
6893
+ * combining is not applicable (e.g., multi-key variants, wrapper components).
6894
+ */
6895
+ function buildCallSiteCombinedStyles(args) {
6896
+ const { decl, staticBooleanVariants, dimensions, baseSx, propsByUsage, hasCompleteCallsiteVisibility, hasPropReferencingTemplateExpressions } = args;
6897
+ if (!decl.isDirectJsxResolution || !hasCompleteCallsiteVisibility || hasPropReferencingTemplateExpressions || staticBooleanVariants.length < 2 || dimensions.length > 0) return null;
6898
+ const variantsByProp = new Map(staticBooleanVariants.map((v) => [v.propName, v]));
6899
+ const combinationGroups = /* @__PURE__ */ new Map();
6900
+ for (const siteProps of propsByUsage) {
6901
+ const matchingPropNames = Object.keys(siteProps).filter((p) => variantsByProp.has(p)).sort();
6902
+ if (matchingPropNames.length === 0) continue;
6903
+ const groupKey = matchingPropNames.join(",");
6904
+ if (combinationGroups.has(groupKey)) continue;
6905
+ const mergedStyles = { ...baseSx };
6906
+ for (const propName of matchingPropNames) {
6907
+ const variant = variantsByProp.get(propName);
6908
+ Object.assign(mergedStyles, variant.styles);
6909
+ }
6910
+ combinationGroups.set(groupKey, {
6911
+ propNames: matchingPropNames,
6912
+ styles: mergedStyles
6913
+ });
6914
+ }
6915
+ if (combinationGroups.size === 0 || combinationGroups.size >= staticBooleanVariants.length) return null;
6916
+ const result = [];
6917
+ for (const [, { propNames, styles }] of combinationGroups) {
6918
+ const suffix = propNames.map((p) => toSuffixFromProp(p)).join("");
6919
+ result.push({
6920
+ propNames,
6921
+ styleKey: `${decl.styleKey}${suffix}`,
6922
+ styles
6923
+ });
6924
+ }
6925
+ return result;
6926
+ }
6927
+ /**
6928
+ * Checks whether a single variant key represents a value that is truthy at runtime.
6929
+ * For boolean props, only `"true"` is truthy (`false` is falsy).
6930
+ * For non-boolean props (numbers, strings), "0" and "" are falsy; everything else is truthy.
6931
+ * Falsy values cannot use the truthy-guard pattern (`prop && styles.key`) safely.
6932
+ */
6933
+ function isSingleVariantKeyTruthy(key, isBooleanProp) {
6934
+ if (isBooleanProp) return key === "true";
6935
+ return key !== "0" && key !== "";
6936
+ }
6807
6937
  function serializeRecord(record) {
6808
6938
  const ordered = Object.keys(record).sort().map((key) => [key, record[key]]);
6809
6939
  return JSON.stringify(ordered);
6810
6940
  }
6811
6941
  function inlineResolvedBaseComponent(args) {
6812
- const { ctx, decl, baseStaticProps, importSource, importedName, baseResult, consumedProps, variantDimensions, hasLocalCallsites, usedConsumedPropsAtCallSites, foldedBaseSx, bakedInConsumedProps, staticBooleanVariants } = args;
6942
+ const { ctx, decl, baseStaticProps, importSource, importedName, baseResult, consumedProps, variantDimensions, hasLocalCallsites, usedConsumedPropsAtCallSites, foldedBaseSx, bakedInConsumedProps, staticBooleanVariants, callSiteCombinedStyles } = args;
6813
6943
  const sxRule = createRuleFromStylexDeclarations(Object.keys(foldedBaseSx).length > 0 ? {
6814
6944
  ...baseResult.sx,
6815
6945
  ...foldedBaseSx
@@ -6853,6 +6983,7 @@ function inlineResolvedBaseComponent(args) {
6853
6983
  };
6854
6984
  if (variantDimensions.length > 0) decl.variantDimensions = [...decl.variantDimensions ?? [], ...variantDimensions];
6855
6985
  if (staticBooleanVariants.length > 0) decl.staticBooleanVariants = [...decl.staticBooleanVariants ?? [], ...staticBooleanVariants];
6986
+ if (callSiteCombinedStyles.length > 0) decl.callSiteCombinedStyles = [...decl.callSiteCombinedStyles ?? [], ...callSiteCombinedStyles];
6856
6987
  decl.inlinedBaseComponent = {
6857
6988
  importSource,
6858
6989
  importedName,
@@ -6986,7 +7117,8 @@ function resolveDirectJsxUsages(ctx, styledDecls) {
6986
7117
  usedConsumedPropsAtCallSites: variantOutcome.usedConsumedPropsAtCallSites,
6987
7118
  foldedBaseSx: variantOutcome.foldedBaseSx,
6988
7119
  bakedInConsumedProps: variantOutcome.bakedInConsumedProps,
6989
- staticBooleanVariants: variantOutcome.staticBooleanVariants
7120
+ staticBooleanVariants: variantOutcome.staticBooleanVariants,
7121
+ callSiteCombinedStyles: variantOutcome.callSiteCombinedStyles
6990
7122
  });
6991
7123
  usedStyleKeys.add(syntheticDecl.styleKey);
6992
7124
  styledDecls.push(syntheticDecl);
@@ -10273,7 +10405,7 @@ function emitSimpleWithConfigWrappers(ctx) {
10273
10405
  allowAsProp,
10274
10406
  allowClassNameProp: false,
10275
10407
  allowStyleProp: false,
10276
- includeRefProp: d.supportsRefProp ?? false,
10408
+ includeRefProp: (d.supportsRefProp ?? false) || !includeRest && willForwardRef,
10277
10409
  includeRest,
10278
10410
  defaultAttrs: d.attrsInfo?.defaultAttrs ?? [],
10279
10411
  conditionalAttrs: d.attrsInfo?.conditionalAttrs ?? [],
@@ -10680,7 +10812,7 @@ function emitSimpleExportedIntrinsicWrappers(ctx) {
10680
10812
  ...allowClassNameProp ? [ctx.patternProp("className", classNameId)] : [],
10681
10813
  ...includeChildren ? [ctx.patternProp("children", childrenId)] : [],
10682
10814
  ...allowStyleProp ? [ctx.patternProp("style", styleId)] : [],
10683
- ...d.supportsRefProp ?? false ? [ctx.patternProp("ref", refId)] : [],
10815
+ ...(d.supportsRefProp ?? false) || !restId && willForwardRef ? [ctx.patternProp("ref", refId)] : [],
10684
10816
  ...allowSxProp ? [ctx.patternProp("sx", sxId)] : []
10685
10817
  ],
10686
10818
  destructureProps,
@@ -10712,7 +10844,7 @@ function emitSimpleExportedIntrinsicWrappers(ctx) {
10712
10844
  attrsInfo: attrsInfoWithoutForwardedAsStatic,
10713
10845
  propExprFor: (prop) => j.identifier(prop)
10714
10846
  }),
10715
- ...d.supportsRefProp ?? false ? [j.jsxAttribute(j.jsxIdentifier("ref"), j.jsxExpressionContainer(refId))] : [],
10847
+ ...(d.supportsRefProp ?? false) || !restId && willForwardRef ? [j.jsxAttribute(j.jsxIdentifier("ref"), j.jsxExpressionContainer(refId))] : [],
10716
10848
  ...restId ? [j.jsxSpreadAttribute(restId)] : [],
10717
10849
  ...includesForwardedAs ? [j.jsxAttribute(j.jsxIdentifier("as"), j.jsxExpressionContainer(buildForwardedAsValueExpr(forwardedAsId, forwardedAsStaticFallback)))] : []
10718
10850
  ];
@@ -10748,7 +10880,7 @@ function emitSimpleExportedIntrinsicWrappers(ctx) {
10748
10880
  allowAsProp,
10749
10881
  allowClassNameProp: false,
10750
10882
  allowStyleProp: false,
10751
- includeRefProp: d.supportsRefProp ?? false,
10883
+ includeRefProp: (d.supportsRefProp ?? false) || !shouldIncludeRest && willForwardRef,
10752
10884
  includeRest: shouldIncludeRest,
10753
10885
  defaultAttrs: d.attrsInfo?.defaultAttrs ?? [],
10754
10886
  conditionalAttrs: d.attrsInfo?.conditionalAttrs ?? [],
@@ -13672,6 +13804,149 @@ function buildInterpolatedTemplate(args) {
13672
13804
  return j.templateLiteral(quasis, exprs);
13673
13805
  }
13674
13806
 
13807
+ //#endregion
13808
+ //#region src/internal/lower-rules/utils.ts
13809
+ /**
13810
+ * Shared lower-rules helpers for merging and formatting style objects.
13811
+ * Core concepts: deep merge semantics, AST node detection, and media query resolution.
13812
+ */
13813
+ /** Returns true for at-rules the codemod can transform (`@media`, `@container`). */
13814
+ function isSupportedAtRule(atRule) {
13815
+ return atRule.startsWith("@media") || atRule.startsWith("@container");
13816
+ }
13817
+ /** Finds the first supported at-rule (`@media` or `@container`) in the stack, if any. */
13818
+ function findSupportedAtRule(atRuleStack) {
13819
+ return atRuleStack.find(isSupportedAtRule);
13820
+ }
13821
+ /**
13822
+ * Resolves a media/container at-rule string that may contain `__SC_EXPR_N__` placeholders.
13823
+ *
13824
+ * First tries `resolveSelector` (when available) to produce a computed key like
13825
+ * `[breakpoints.phone]`. Falls back to static value substitution.
13826
+ *
13827
+ * Returns null if the placeholder cannot be resolved at all.
13828
+ */
13829
+ function resolveMediaAtRulePlaceholders(media, getSlotExpr, ctx) {
13830
+ if (!media.includes("__SC_EXPR_")) return {
13831
+ kind: "static",
13832
+ value: media
13833
+ };
13834
+ const globalRe = new RegExp(PLACEHOLDER_RE.source, "g");
13835
+ const matches = [...media.matchAll(globalRe)];
13836
+ if (matches.length === 1 && ctx.resolveSelector && ctx.parseExpr) {
13837
+ const expr = getSlotExpr(Number(matches[0][1]));
13838
+ if (expr && typeof expr === "object") {
13839
+ const info = extractRootAndPath(expr);
13840
+ if (info) {
13841
+ const imp = ctx.lookupImport(info.rootName, info.rootNode);
13842
+ if (imp) {
13843
+ const result = ctx.resolveSelector({
13844
+ kind: "selectorInterpolation",
13845
+ importedName: imp.importedName,
13846
+ source: imp.source,
13847
+ path: info.path.length > 0 ? info.path.join(".") : void 0,
13848
+ filePath: ctx.filePath
13849
+ });
13850
+ if (result?.kind === "media") {
13851
+ const keyExpr = ctx.parseExpr(result.expr);
13852
+ if (keyExpr) {
13853
+ for (const impSpec of result.imports ?? []) ctx.resolverImports.set(JSON.stringify(impSpec), impSpec);
13854
+ return {
13855
+ kind: "computed",
13856
+ keyExpr,
13857
+ imports: result.imports ?? []
13858
+ };
13859
+ }
13860
+ }
13861
+ }
13862
+ }
13863
+ }
13864
+ }
13865
+ const resolved = resolveMediaQueryPlaceholders(media, (slotId) => resolveSlotExprToStaticValue(getSlotExpr(slotId), ctx.lookupImport, ctx.resolveValue, ctx.filePath, ctx.resolverImports));
13866
+ if (resolved === null) return null;
13867
+ return {
13868
+ kind: "static",
13869
+ value: resolved
13870
+ };
13871
+ }
13872
+ /** Returns true if the key looks like a StyleX style condition (pseudo, media, container). */
13873
+ function isStyleConditionKey(key) {
13874
+ return key.startsWith(":") || key.startsWith("::") || key.startsWith("@media") || key.startsWith("@container");
13875
+ }
13876
+ /**
13877
+ * Merges tracked @media values into a base style object as nested StyleX objects.
13878
+ * Each property that has media-scoped values is wrapped in:
13879
+ * `{ default: baseValue, "@media (...)": mediaValue }`
13880
+ */
13881
+ function mergeMediaIntoStyles(base, mediaStyles) {
13882
+ for (const [mediaQuery, mediaStyle] of mediaStyles) for (const [prop, mediaValue] of Object.entries(mediaStyle)) base[prop] = {
13883
+ default: base[prop] ?? null,
13884
+ [mediaQuery]: mediaValue
13885
+ };
13886
+ }
13887
+ /**
13888
+ * Recursively merges style objects, combining nested objects rather than overwriting.
13889
+ *
13890
+ * Note: Security scanners may flag this as prototype pollution, but this is a false positive.
13891
+ * This is a codemod that runs locally on the developer's own source code - there is no
13892
+ * untrusted input that could exploit prototype pollution. The source objects are style
13893
+ * declarations extracted from the developer's own styled-components code.
13894
+ */
13895
+ function mergeStyleObjects(target, source) {
13896
+ for (const [key, value] of Object.entries(source)) {
13897
+ const existing = target[key];
13898
+ if (existing && value && typeof existing === "object" && typeof value === "object" && !Array.isArray(existing) && !Array.isArray(value) && !isAstNode(existing) && !isAstNode(value)) mergeStyleObjects(existing, value);
13899
+ else target[key] = value;
13900
+ }
13901
+ }
13902
+ /** Resolves `__SC_EXPR_N__` placeholders in a string to static values via a callback. */
13903
+ function resolveMediaQueryPlaceholders(mediaQuery, resolveSlot) {
13904
+ if (!mediaQuery.includes("__SC_EXPR_")) return mediaQuery;
13905
+ const globalRe = new RegExp(PLACEHOLDER_RE.source, "g");
13906
+ const matches = [];
13907
+ let match;
13908
+ while ((match = globalRe.exec(mediaQuery)) !== null) matches.push({
13909
+ full: match[0],
13910
+ slotId: Number(match[1])
13911
+ });
13912
+ let resolved = mediaQuery;
13913
+ for (const m of matches) {
13914
+ const staticVal = resolveSlot(m.slotId);
13915
+ if (staticVal === null) return null;
13916
+ resolved = resolved.replace(m.full, String(staticVal));
13917
+ }
13918
+ return resolved;
13919
+ }
13920
+ /** Resolves a slot expression AST node to a static string or number via the adapter. */
13921
+ function resolveSlotExprToStaticValue(expr, lookupImport, resolveValue, filePath, resolverImports) {
13922
+ if (!expr || typeof expr !== "object") return null;
13923
+ const staticVal = literalToStaticValue(expr);
13924
+ if (typeof staticVal === "string" || typeof staticVal === "number") return staticVal;
13925
+ const info = extractRootAndPath(expr);
13926
+ if (!info) return null;
13927
+ const imp = lookupImport(info.rootName, info.rootNode);
13928
+ if (!imp) return null;
13929
+ const result = resolveValue({
13930
+ kind: "importedValue",
13931
+ importedName: imp.importedName,
13932
+ source: imp.source,
13933
+ ...info.path.length > 0 ? { path: info.path.join(".") } : {},
13934
+ filePath
13935
+ });
13936
+ if (!result || !("expr" in result)) return null;
13937
+ for (const impSpec of result.imports ?? []) resolverImports.set(JSON.stringify(impSpec), impSpec);
13938
+ return parseStaticExprString(result.expr);
13939
+ }
13940
+ /** Parses a JS expression string as a static numeric or quoted string value. */
13941
+ function parseStaticExprString(expr) {
13942
+ const trimmed = expr.trim();
13943
+ if (!trimmed) return null;
13944
+ const num = Number(trimmed);
13945
+ if (!isNaN(num) && trimmed !== "") return num;
13946
+ if (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("'") && trimmed.endsWith("'")) return trimmed.slice(1, -1);
13947
+ return null;
13948
+ }
13949
+
13675
13950
  //#endregion
13676
13951
  //#region src/internal/lower-rules/css-helper.ts
13677
13952
  /**
@@ -13816,14 +14091,15 @@ function createCssHelperResolver(args) {
13816
14091
  exprString: JSON.stringify(v)
13817
14092
  };
13818
14093
  }
13819
- if (branch.type === "Identifier" && typeof branch.name === "string") {
13820
- const name = branch.name;
13821
- const imp = importMap.get(name);
14094
+ const info = extractRootAndPath(branch);
14095
+ if (info) {
14096
+ const imp = importMap.get(info.rootName);
13822
14097
  if (imp) {
13823
14098
  const res = resolveValue({
13824
14099
  kind: "importedValue",
13825
14100
  importedName: imp.importedName,
13826
14101
  source: imp.source,
14102
+ ...info.path.length > 0 ? { path: info.path.join(".") } : {},
13827
14103
  filePath,
13828
14104
  loc: getNodeLocStart(branch) ?? void 0
13829
14105
  });
@@ -13835,9 +14111,9 @@ function createCssHelperResolver(args) {
13835
14111
  exprString: res.expr
13836
14112
  } : null;
13837
14113
  }
13838
- return {
14114
+ if (branch.type === "Identifier") return {
13839
14115
  ast: branch,
13840
- exprString: name
14116
+ exprString: info.rootName
13841
14117
  };
13842
14118
  }
13843
14119
  return null;
@@ -13872,9 +14148,28 @@ function createCssHelperResolver(args) {
13872
14148
  const dynamicProps = [];
13873
14149
  const dynamicPropKeys = /* @__PURE__ */ new Set();
13874
14150
  const conditionalVariants = [];
14151
+ const lookupImport = (localName) => importMap.get(localName) ?? null;
13875
14152
  for (const rule of rules) {
13876
- const media = rule.atRuleStack.find((a) => a.startsWith("@media"));
13877
- if (rule.atRuleStack.length > 0 && !media) return bail("Conditional `css` block: @-rules (e.g., @media, @supports) are not supported");
14153
+ const rawMedia = findSupportedAtRule(rule.atRuleStack);
14154
+ if (rule.atRuleStack.length > 0 && !rawMedia) return bail("Conditional `css` block: @-rules (e.g., @media, @supports) are not supported");
14155
+ let media = rawMedia;
14156
+ let computedMediaKey = null;
14157
+ if (rawMedia) {
14158
+ const resolved = resolveMediaAtRulePlaceholders(rawMedia, (slotId) => slotExprById.get(slotId), {
14159
+ lookupImport,
14160
+ resolveValue,
14161
+ resolveSelector: args.resolveSelector,
14162
+ parseExpr,
14163
+ filePath,
14164
+ resolverImports
14165
+ });
14166
+ if (resolved === null) return bail("Conditional `css` block: media query interpolation must be a simple imported reference (expressions like `value + 1` are not supported)");
14167
+ if (resolved.kind === "static") media = resolved.value;
14168
+ else {
14169
+ computedMediaKey = resolved;
14170
+ media = void 0;
14171
+ }
14172
+ }
13878
14173
  const selector = (rule.selector ?? "").trim();
13879
14174
  const allowDynamicValues = selector === "&";
13880
14175
  let target = out;
@@ -13906,7 +14201,19 @@ function createCssHelperResolver(args) {
13906
14201
  }
13907
14202
  const mergeIntoContext = (value, prop, targetObj) => {
13908
14203
  const existing = targetObj[prop];
13909
- if (media && currentPseudoClass) return mergeIntoPseudoContext(mergeIntoPseudoContext(value, media, existing && typeof existing === "object" && !Array.isArray(existing) && !isAstNode(existing) ? existing[currentPseudoClass] : void 0), currentPseudoClass, existing);
14204
+ if (computedMediaKey) {
14205
+ const nested = existing && typeof existing === "object" && !Array.isArray(existing) && !isAstNode(existing) ? existing : { default: existing ?? null };
14206
+ if (!("default" in nested)) nested.default = null;
14207
+ nested.__computedKeys = [...nested.__computedKeys ?? [], {
14208
+ keyExpr: computedMediaKey.keyExpr,
14209
+ value
14210
+ }];
14211
+ return nested;
14212
+ }
14213
+ if (media && currentPseudoClass) {
14214
+ const pseudoExisting = existing && typeof existing === "object" && !Array.isArray(existing) && !isAstNode(existing) ? existing[currentPseudoClass] : void 0;
14215
+ return mergeIntoPseudoContext(mergeIntoPseudoContext(value, media, pseudoExisting), currentPseudoClass, existing);
14216
+ }
13910
14217
  if (media) return mergeIntoPseudoContext(value, media, existing);
13911
14218
  return mergeIntoPseudoContext(value, currentPseudoClass, existing);
13912
14219
  };
@@ -13933,6 +14240,20 @@ function createCssHelperResolver(args) {
13933
14240
  continue;
13934
14241
  }
13935
14242
  if (d.important) return bail("Conditional `css` block: !important is not supported in StyleX", { property: d.property });
14243
+ if ((d.property === "animation" || d.property === "animation-name") && args.keyframesNames && args.keyframesNames.size > 0 && args.j) {
14244
+ const expanded = expandInterpolatedAnimationShorthand({
14245
+ property: d.property,
14246
+ valueRaw: d.valueRaw,
14247
+ slotExprById,
14248
+ keyframesNames: args.keyframesNames,
14249
+ j: args.j,
14250
+ inlineKeyframeNameMap: args.inlineKeyframeNameMap
14251
+ });
14252
+ if (expanded) {
14253
+ for (const [prop, value] of Object.entries(expanded)) target[prop] = mergeIntoContext(value, prop, target);
14254
+ continue;
14255
+ }
14256
+ }
13936
14257
  const parts = d.value.parts ?? [];
13937
14258
  const slotParts = parts.filter((p) => p.kind === "slot");
13938
14259
  if (slotParts.length !== 1) return bail("Conditional `css` block: multiple interpolation slots in a single property value", { property: d.property });
@@ -14484,6 +14805,7 @@ function createLowerRulesState(ctx) {
14484
14805
  importMap,
14485
14806
  filePath,
14486
14807
  resolveValue,
14808
+ resolveSelector,
14487
14809
  parseExpr,
14488
14810
  resolverImports,
14489
14811
  warnings,
@@ -14556,39 +14878,6 @@ function createLowerRulesState(ctx) {
14556
14878
  return state;
14557
14879
  }
14558
14880
 
14559
- //#endregion
14560
- //#region src/internal/lower-rules/utils.ts
14561
- /**
14562
- * Shared lower-rules helpers for merging and formatting style objects.
14563
- * Core concepts: deep merge semantics and AST node detection.
14564
- */
14565
- /**
14566
- * Merges tracked @media values into a base style object as nested StyleX objects.
14567
- * Each property that has media-scoped values is wrapped in:
14568
- * `{ default: baseValue, "@media (...)": mediaValue }`
14569
- */
14570
- function mergeMediaIntoStyles(base, mediaStyles) {
14571
- for (const [mediaQuery, mediaStyle] of mediaStyles) for (const [prop, mediaValue] of Object.entries(mediaStyle)) base[prop] = {
14572
- default: base[prop] ?? null,
14573
- [mediaQuery]: mediaValue
14574
- };
14575
- }
14576
- /**
14577
- * Recursively merges style objects, combining nested objects rather than overwriting.
14578
- *
14579
- * Note: Security scanners may flag this as prototype pollution, but this is a false positive.
14580
- * This is a codemod that runs locally on the developer's own source code - there is no
14581
- * untrusted input that could exploit prototype pollution. The source objects are style
14582
- * declarations extracted from the developer's own styled-components code.
14583
- */
14584
- function mergeStyleObjects(target, source) {
14585
- for (const [key, value] of Object.entries(source)) {
14586
- const existing = target[key];
14587
- if (existing && value && typeof existing === "object" && typeof value === "object" && !Array.isArray(existing) && !Array.isArray(value) && !isAstNode(existing) && !isAstNode(value)) mergeStyleObjects(existing, value);
14588
- else target[key] = value;
14589
- }
14590
- }
14591
-
14592
14881
  //#endregion
14593
14882
  //#region src/internal/lower-rules/variant-utils.ts
14594
14883
  /**
@@ -16933,8 +17222,8 @@ function tryResolveInlineStyleValueForNestedPropAccess(node) {
16933
17222
  }
16934
17223
  function tryResolveInlineStyleValueFromArrowFn(node) {
16935
17224
  if (!node.css.property) return null;
16936
- const hasMediaAtRule = (node.css.atRuleStack ?? []).some((rule) => rule.startsWith("@media"));
16937
- const isMediaSelector = (node.css.selector ?? "").trim().startsWith("@media");
17225
+ const hasMediaAtRule = (node.css.atRuleStack ?? []).some(isSupportedAtRule);
17226
+ const isMediaSelector = isSupportedAtRule((node.css.selector ?? "").trim());
16938
17227
  if (!hasMediaAtRule && !isMediaSelector) return null;
16939
17228
  const expr = node.expr;
16940
17229
  if (!isArrowFunctionExpression(expr)) return null;
@@ -17032,7 +17321,7 @@ function resolveTemplateLiteralBranch(ctx, args) {
17032
17321
  const inlineEntries = [];
17033
17322
  const mediaStyles = /* @__PURE__ */ new Map();
17034
17323
  for (const rule of rules) {
17035
- const media = rule.atRuleStack.find((a) => a.startsWith("@media") || a.startsWith("@container"));
17324
+ let media = findSupportedAtRule(rule.atRuleStack);
17036
17325
  if (rule.atRuleStack.length > 0 && !media) {
17037
17326
  ctx.warnings?.push({
17038
17327
  severity: "warning",
@@ -17041,8 +17330,36 @@ function resolveTemplateLiteralBranch(ctx, args) {
17041
17330
  });
17042
17331
  return null;
17043
17332
  }
17333
+ let computedMediaKeyExpr;
17334
+ if (media) {
17335
+ const resolved = resolveMediaAtRulePlaceholders(media, (slotId) => slotExprById.get(slotId), {
17336
+ lookupImport: resolveImportInScope,
17337
+ resolveValue,
17338
+ resolveSelector: ctx.resolveSelector,
17339
+ parseExpr,
17340
+ filePath,
17341
+ resolverImports
17342
+ });
17343
+ if (resolved === null) return null;
17344
+ if (resolved.kind === "static") media = resolved.value;
17345
+ else {
17346
+ computedMediaKeyExpr = resolved.keyExpr;
17347
+ media = void 0;
17348
+ }
17349
+ }
17044
17350
  if ((rule.selector ?? "").trim() !== "&") return null;
17045
17351
  const setStyleValue = (prop, value) => {
17352
+ if (computedMediaKeyExpr) {
17353
+ const existing = style[prop];
17354
+ const nested = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : { default: existing ?? null };
17355
+ if (!("default" in nested)) nested.default = null;
17356
+ nested.__computedKeys = [...nested.__computedKeys ?? [], {
17357
+ keyExpr: computedMediaKeyExpr,
17358
+ value
17359
+ }];
17360
+ style[prop] = nested;
17361
+ return;
17362
+ }
17046
17363
  if (media) {
17047
17364
  const target = mediaStyles.get(media) ?? {};
17048
17365
  mediaStyles.set(media, target);
@@ -17072,6 +17389,20 @@ function resolveTemplateLiteralBranch(ctx, args) {
17072
17389
  continue;
17073
17390
  }
17074
17391
  if (d.value.kind !== "interpolated") return null;
17392
+ if ((d.property === "animation" || d.property === "animation-name") && ctx.keyframesNames && ctx.keyframesNames.size > 0) {
17393
+ const expanded = expandInterpolatedAnimationShorthand({
17394
+ property: d.property,
17395
+ valueRaw: d.valueRaw,
17396
+ slotExprById,
17397
+ keyframesNames: ctx.keyframesNames,
17398
+ j,
17399
+ inlineKeyframeNameMap: ctx.inlineKeyframeNameMap
17400
+ });
17401
+ if (expanded) {
17402
+ for (const [prop, value] of Object.entries(expanded)) setStyleValue(prop, value);
17403
+ continue;
17404
+ }
17405
+ }
17075
17406
  const parts = d.value.parts ?? [];
17076
17407
  const slotParts = parts.filter((p) => p.kind === "slot");
17077
17408
  if (slotParts.length === 0) return null;
@@ -17741,7 +18072,7 @@ const createCssHelperHandlers = (ctx) => {
17741
18072
  //#endregion
17742
18073
  //#region src/internal/lower-rules/css-helper-conditional.ts
17743
18074
  function createCssHelperConditionalHandler(ctx) {
17744
- const { j, decl, filePath, warnings, parseExpr, resolveValue, resolveCall, resolveImportInScope, resolverImports, componentInfo, handlerContext, styleObj, styleFnFromProps, styleFnDecls, inlineStyleProps, isCssHelperTaggedTemplate, resolveCssHelperTemplate, resolveStaticCssBlock, isPlainTemplateLiteral, isThemeAccessTest, applyVariant, dropAllTestInfoProps, annotateParamFromJsxProp, findJsxPropTsType, isJsxPropOptional, markBail, importMap, extraStyleObjects, resolvedStyleObjects } = ctx;
18075
+ const { j, decl, filePath, warnings, parseExpr, resolveValue, resolveCall, resolveSelector, resolveImportInScope, resolverImports, componentInfo, handlerContext, styleObj, styleFnFromProps, styleFnDecls, inlineStyleProps, isCssHelperTaggedTemplate, resolveCssHelperTemplate, resolveStaticCssBlock, isPlainTemplateLiteral, isThemeAccessTest, applyVariant, dropAllTestInfoProps, annotateParamFromJsxProp, findJsxPropTsType, isJsxPropOptional, markBail, importMap, extraStyleObjects, resolvedStyleObjects } = ctx;
17745
18076
  const avoidNames = new Set(importMap.keys());
17746
18077
  /**
17747
18078
  * Resolve the TS type node for a prop used in a style function parameter.
@@ -17910,21 +18241,57 @@ function createCssHelperConditionalHandler(ctx) {
17910
18241
  const { rules, slotExprById } = parseCssTemplateToRules(tpl);
17911
18242
  const out = /* @__PURE__ */ new Map();
17912
18243
  const mediaValues = /* @__PURE__ */ new Map();
17913
- const setValueForProp = (prop, value, media) => {
17914
- if (media) {
18244
+ const computedMediaValues = /* @__PURE__ */ new Map();
18245
+ const setValueForProp = (prop, value, media, computedKey) => {
18246
+ if (computedKey) {
18247
+ const arr = computedMediaValues.get(prop) ?? [];
18248
+ arr.push({
18249
+ keyExpr: computedKey,
18250
+ value
18251
+ });
18252
+ computedMediaValues.set(prop, arr);
18253
+ } else if (media) {
17915
18254
  if (!mediaValues.has(prop)) mediaValues.set(prop, /* @__PURE__ */ new Map());
17916
18255
  mediaValues.get(prop).set(media, value);
17917
18256
  } else out.set(prop, value);
17918
18257
  };
17919
18258
  for (const rule of rules) {
17920
- const media = rule.atRuleStack.find((a) => a.startsWith("@media"));
17921
- if (rule.atRuleStack.length > 0 && !media) return null;
18259
+ const rawMedia = findSupportedAtRule(rule.atRuleStack);
18260
+ if (rule.atRuleStack.length > 0 && !rawMedia) return null;
18261
+ let media = rawMedia;
18262
+ let computedMediaKeyExpr;
18263
+ if (rawMedia) {
18264
+ const resolved = resolveMediaAtRulePlaceholders(rawMedia, (slotId) => slotExprById.get(slotId), {
18265
+ lookupImport: resolveImportInScope,
18266
+ resolveValue,
18267
+ resolveSelector,
18268
+ parseExpr,
18269
+ filePath,
18270
+ resolverImports
18271
+ });
18272
+ if (resolved === null) return null;
18273
+ if (resolved.kind === "static") media = resolved.value;
18274
+ else {
18275
+ computedMediaKeyExpr = resolved.keyExpr;
18276
+ media = void 0;
18277
+ }
18278
+ }
17922
18279
  if ((rule.selector ?? "").trim() !== "&") return null;
18280
+ const applyExpandedAnimation = (expanded) => {
18281
+ for (const [prop, value] of Object.entries(expanded)) setValueForProp(prop, typeof value === "string" || typeof value === "number" ? staticValueToLiteral(j, value) : value, media, computedMediaKeyExpr);
18282
+ };
17923
18283
  for (const d of rule.declarations) {
17924
18284
  if (!d.property) return null;
17925
18285
  if (d.property.includes("__SC_EXPR_")) return null;
17926
18286
  if (d.important) return null;
17927
18287
  if (d.value.kind === "static") {
18288
+ if (d.property === "animation" && ctx.keyframesNames && ctx.keyframesNames.size > 0) {
18289
+ const expanded = {};
18290
+ if (expandStaticAnimationShorthand(d.valueRaw, ctx.keyframesNames, j, expanded, ctx.inlineKeyframeNameMap)) {
18291
+ applyExpandedAnimation(expanded);
18292
+ continue;
18293
+ }
18294
+ }
17928
18295
  for (const mapped of cssDeclarationToStylexDeclarations(d)) {
17929
18296
  let value = cssValueToJs(mapped.value, d.important, mapped.prop);
17930
18297
  if (mapped.prop === "content" && typeof value === "string") {
@@ -17932,12 +18299,26 @@ function createCssHelperConditionalHandler(ctx) {
17932
18299
  if (m) value = `"${m[1]}"`;
17933
18300
  else if (!value.startsWith("\"") && !value.endsWith("\"")) value = `"${value}"`;
17934
18301
  }
17935
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") setValueForProp(mapped.prop, staticValueToLiteral(j, value), media);
18302
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") setValueForProp(mapped.prop, staticValueToLiteral(j, value), media, computedMediaKeyExpr);
17936
18303
  else return null;
17937
18304
  }
17938
18305
  continue;
17939
18306
  }
17940
18307
  if (d.value.kind !== "interpolated") return null;
18308
+ if ((d.property === "animation" || d.property === "animation-name") && ctx.keyframesNames && ctx.keyframesNames.size > 0) {
18309
+ const expanded = expandInterpolatedAnimationShorthand({
18310
+ property: d.property,
18311
+ valueRaw: d.valueRaw,
18312
+ slotExprById,
18313
+ keyframesNames: ctx.keyframesNames,
18314
+ j,
18315
+ inlineKeyframeNameMap: ctx.inlineKeyframeNameMap
18316
+ });
18317
+ if (expanded) {
18318
+ applyExpandedAnimation(expanded);
18319
+ continue;
18320
+ }
18321
+ }
17941
18322
  const slotParts = (d.value.parts ?? []).filter((p) => p.kind === "slot");
17942
18323
  if (slotParts.length !== 1) return null;
17943
18324
  const slotExpr = slotExprById.get(slotParts[0].slotId);
@@ -17945,13 +18326,21 @@ function createCssHelperConditionalHandler(ctx) {
17945
18326
  const rawExpr = replaceParamWithProps(slotExpr);
17946
18327
  const { prefix, suffix } = extractStaticPartsForDecl(d);
17947
18328
  const valueExpr = prefix || suffix ? buildTemplateWithStaticParts(j, rawExpr, prefix, suffix) : rawExpr;
17948
- for (const mapped of cssDeclarationToStylexDeclarations(d)) setValueForProp(mapped.prop, valueExpr, media);
18329
+ for (const mapped of cssDeclarationToStylexDeclarations(d)) setValueForProp(mapped.prop, valueExpr, media, computedMediaKeyExpr);
17949
18330
  }
17950
18331
  }
17951
- for (const [prop, queries] of mediaValues) {
18332
+ const allMediaProps = new Set([...mediaValues.keys(), ...computedMediaValues.keys()]);
18333
+ for (const prop of allMediaProps) {
17952
18334
  const baseValue = out.get(prop);
17953
18335
  const properties = [j.property("init", j.identifier("default"), baseValue ?? j.literal(null))];
17954
- for (const [query, value] of queries) properties.push(j.property("init", j.literal(query), value));
18336
+ const queries = mediaValues.get(prop);
18337
+ if (queries) for (const [query, value] of queries) properties.push(j.property("init", j.literal(query), value));
18338
+ const computed = computedMediaValues.get(prop);
18339
+ if (computed) for (const { keyExpr, value } of computed) {
18340
+ const p = j.property("init", keyExpr, value);
18341
+ p.computed = true;
18342
+ properties.push(p);
18343
+ }
17955
18344
  out.set(prop, j.objectExpression(properties));
17956
18345
  }
17957
18346
  return out;
@@ -19284,7 +19673,7 @@ const createValuePatternHandlers = (ctx) => {
19284
19673
  * Core concepts: per-component style buckets, helper factories, and resolver wiring.
19285
19674
  */
19286
19675
  function createDeclProcessingState(state, decl) {
19287
- const { api, j, root, filePath, warnings, resolverImports, parseExpr, resolveValue, resolveCall, resolveCallOptional, importMap, cssHelperFunctions, stringMappingFns, hasLocalThemeBinding, isCssHelperTaggedTemplate, resolveCssHelperTemplate, resolveImportInScope, usedCssHelperFunctions, markBail } = state;
19676
+ const { api, j, root, filePath, warnings, resolverImports, parseExpr, resolveValue, resolveCall, resolveCallOptional, resolveSelector, importMap, cssHelperFunctions, stringMappingFns, hasLocalThemeBinding, isCssHelperTaggedTemplate, resolveCssHelperTemplate, resolveImportInScope, usedCssHelperFunctions, markBail } = state;
19288
19677
  const styleObj = {};
19289
19678
  const perPropPseudo = {};
19290
19679
  const perPropMedia = {};
@@ -19371,6 +19760,7 @@ function createDeclProcessingState(state, decl) {
19371
19760
  parseExpr,
19372
19761
  resolveValue,
19373
19762
  resolveCall,
19763
+ resolveSelector,
19374
19764
  resolveImportInScope,
19375
19765
  resolverImports,
19376
19766
  isCssHelperTaggedTemplate,
@@ -19432,7 +19822,7 @@ function createDeclProcessingState(state, decl) {
19432
19822
  const out = {};
19433
19823
  const mediaStyles = /* @__PURE__ */ new Map();
19434
19824
  for (const rule of rules) {
19435
- const media = rule.atRuleStack.find((a) => a.startsWith("@media") || a.startsWith("@container"));
19825
+ const media = findSupportedAtRule(rule.atRuleStack);
19436
19826
  if (rule.atRuleStack.length > 0 && !media) {
19437
19827
  warnings.push({
19438
19828
  severity: "warning",
@@ -19878,18 +20268,26 @@ function tryHandleInterpolatedBorder(ctx, args) {
19878
20268
  if (quasis.length === 2 && exprs.length === 1) {
19879
20269
  const prefix = quasis[0]?.value?.cooked ?? quasis[0]?.value?.raw ?? "";
19880
20270
  const suffix = quasis[1]?.value?.cooked ?? quasis[1]?.value?.raw ?? "";
19881
- if (suffix.trim() !== "") return null;
19882
- const parsed = parseInterpolatedBorderStaticParts({
19883
- prop,
19884
- prefix,
19885
- suffix
19886
- });
19887
- if (!parsed?.width || !parsed?.style) return null;
19888
- return {
19889
- width: parsed.width,
19890
- style: parsed.style,
19891
- colorExpr: exprs[0]
19892
- };
20271
+ if (suffix.trim() === "") {
20272
+ const parsed = parseInterpolatedBorderStaticParts({
20273
+ prop,
20274
+ prefix,
20275
+ suffix
20276
+ });
20277
+ if (parsed?.width && parsed?.style) return {
20278
+ width: parsed.width,
20279
+ style: parsed.style,
20280
+ colorExpr: exprs[0]
20281
+ };
20282
+ }
20283
+ if (prefix.trim() === "" && suffix.trim() !== "") {
20284
+ const parsed = parseBorderShorthandParts(suffix.trim());
20285
+ if (parsed?.style && parsed?.color) return {
20286
+ width: exprs[0],
20287
+ style: parsed.style,
20288
+ colorExpr: parsed.color
20289
+ };
20290
+ }
19893
20291
  }
19894
20292
  if (quasis.length === 3 && exprs.length === 2) {
19895
20293
  const prefix = quasis[0]?.value?.cooked ?? quasis[0]?.value?.raw ?? "";
@@ -19993,7 +20391,7 @@ function tryHandleInterpolatedBorder(ctx, args) {
19993
20391
  }
19994
20392
  }
19995
20393
  if (hasStaticWidthOrStyle) applyResolvedPropValue(targetProp, resolved.exprAst);
19996
- else applyResolvedPropValue(direction ? `border${direction}` : "border", resolved.exprAst);
20394
+ else bailUnsupportedWithContext("Resolved border helper value could not be expanded to longhand properties", { property: prop }, getNodeLocStart(expr));
19997
20395
  return true;
19998
20396
  }
19999
20397
  }
@@ -21058,6 +21456,20 @@ function handleInterpolatedDeclaration(args) {
21058
21456
  };
21059
21457
  if (tryHandleThemeValueInPseudo()) continue;
21060
21458
  const resolveImportedValueExpr = (expr) => {
21459
+ if (expr?.type === "BinaryExpression") {
21460
+ const leftResult = resolveImportedValueExpr(expr.left);
21461
+ const rightResult = resolveImportedValueExpr(expr.right);
21462
+ if (!leftResult && !rightResult) return null;
21463
+ if (leftResult && "bail" in leftResult) return leftResult;
21464
+ if (rightResult && "bail" in rightResult) return rightResult;
21465
+ const resolvedLeft = leftResult ? leftResult.resolved : expr.left;
21466
+ const resolvedRight = rightResult ? rightResult.resolved : expr.right;
21467
+ const imports = [...leftResult?.imports ?? [], ...rightResult?.imports ?? []];
21468
+ return {
21469
+ resolved: j.binaryExpression(expr.operator, resolvedLeft, resolvedRight),
21470
+ imports
21471
+ };
21472
+ }
21061
21473
  const info = getRootIdentifierInfo(expr);
21062
21474
  if (!info) return null;
21063
21475
  const imp = resolveImportInScope(info.rootName, info.rootNode);
@@ -22830,7 +23242,7 @@ function processDeclRules(ctx) {
22830
23242
  }
22831
23243
  }
22832
23244
  }
22833
- let media = rule.atRuleStack.find((a) => a.startsWith("@media") || a.startsWith("@container"));
23245
+ let media = findSupportedAtRule(rule.atRuleStack);
22834
23246
  const intrinsicTagName = decl.base.kind === "intrinsic" ? decl.base.tagName : null;
22835
23247
  let selector = normalizeSelectorForAttributePseudos(rule.selector, intrinsicTagName);
22836
23248
  selector = normalizeInterpolatedSelector(selector);
@@ -22843,10 +23255,37 @@ function processDeclRules(ctx) {
22843
23255
  first.leadingComment = first.leadingComment ? `${note}\n${first.leadingComment}` : note;
22844
23256
  }
22845
23257
  }
22846
- if (!media && (selector.trim().startsWith("@media") || selector.trim().startsWith("@container"))) {
23258
+ if (!media && isSupportedAtRule(selector.trim())) {
22847
23259
  media = selector.trim();
22848
23260
  selector = "&";
22849
23261
  }
23262
+ if (media) {
23263
+ const resolved = resolveMediaAtRulePlaceholders(media, (slotId) => decl.templateExpressions[slotId], {
23264
+ lookupImport: resolveImportInScope,
23265
+ resolveValue: state.resolveValue,
23266
+ resolveSelector,
23267
+ parseExpr,
23268
+ filePath: state.filePath,
23269
+ resolverImports
23270
+ });
23271
+ if (resolved === null) {
23272
+ state.markBail();
23273
+ warnings.push({
23274
+ severity: "warning",
23275
+ type: "Unsupported: media query interpolation must be a simple imported reference (expressions like `value + 1` are not supported)",
23276
+ loc: computeSelectorWarningLoc(decl.loc, decl.rawCss, rule.selector)
23277
+ });
23278
+ break;
23279
+ }
23280
+ if (resolved.kind === "static") media = resolved.value;
23281
+ else {
23282
+ resolvedSelectorMedia = {
23283
+ keyExpr: resolved.keyExpr,
23284
+ exprSource: ""
23285
+ };
23286
+ media = void 0;
23287
+ }
23288
+ }
22850
23289
  const parsedSelector = parseSelector(selector);
22851
23290
  if (parsedSelector.kind === "unsupported" && selector !== "&" && !rule.selector.includes("__SC_EXPR_")) {
22852
23291
  const elementAction = tryHandleElementSelector(selector, rule, decl);
@@ -23444,7 +23883,27 @@ function handleSiblingSelector(selector, rule, ctx) {
23444
23883
  return "break";
23445
23884
  }
23446
23885
  const makeSiblingKeyExpr = () => j.callExpression(j.memberExpression(j.memberExpression(j.identifier("stylex"), j.identifier("when")), j.identifier("siblingBefore")), [j.literal(":is(*)")]);
23447
- const media = rule.atRuleStack.find((a) => a.startsWith("@media") || a.startsWith("@container"));
23886
+ let media = findSupportedAtRule(rule.atRuleStack);
23887
+ if (media) {
23888
+ const resolved = resolveMediaAtRulePlaceholders(media, (slotId) => decl.templateExpressions[slotId], {
23889
+ lookupImport: state.resolveImportInScope,
23890
+ resolveValue: state.resolveValue,
23891
+ resolveSelector: state.resolveSelector,
23892
+ parseExpr: state.parseExpr,
23893
+ filePath: state.filePath,
23894
+ resolverImports: state.resolverImports
23895
+ });
23896
+ if (resolved === null) {
23897
+ warnings.push({
23898
+ severity: "warning",
23899
+ type: "Unsupported: media query interpolation must be a simple imported reference (expressions like `value + 1` are not supported)",
23900
+ loc: computeSelectorWarningLoc(decl.loc, decl.rawCss, rule.selector)
23901
+ });
23902
+ return "break";
23903
+ }
23904
+ if (resolved.kind === "static") media = resolved.value;
23905
+ else media = void 0;
23906
+ }
23448
23907
  for (const [prop, value] of Object.entries(bucket)) {
23449
23908
  const siblingValue = media ? {
23450
23909
  default: null,
@@ -23646,7 +24105,7 @@ function finalizeDeclProcessing(ctx) {
23646
24105
  if (!v || typeof v !== "object" || Array.isArray(v) || isAstNode(v)) return false;
23647
24106
  const keys = Object.keys(v);
23648
24107
  if (keys.length === 0) return false;
23649
- return keys.includes("default") || keys.some((k) => k.startsWith(":") || k.startsWith("@media") || k.startsWith("::"));
24108
+ return keys.includes("default") || keys.some(isStyleConditionKey);
23650
24109
  };
23651
24110
  if (!(() => {
23652
24111
  const disabledBucket = variantBuckets.get("disabled");
@@ -24871,6 +25330,8 @@ function rewriteJsxStep(ctx) {
24871
25330
  const inlineVariantDimensions = decl.inlinedBaseComponent?.hasInlineJsxVariants ? decl.variantDimensions ?? [] : [];
24872
25331
  const inlineVariantByProp = new Map(inlineVariantDimensions.map((dimension) => [dimension.propName, dimension]));
24873
25332
  const inlineVariantProps = new Set(inlineVariantDimensions.map((dimension) => dimension.propName));
25333
+ const combinedStylePropNames = new Set((decl.callSiteCombinedStyles ?? []).flatMap((c) => c.propNames));
25334
+ const matchedCombinedStyleKey = matchCallSiteCombinedStyle(decl.callSiteCombinedStyles, opening.attributes ?? []);
24874
25335
  const attrs = opening.attributes ?? [];
24875
25336
  for (const attr of attrs) {
24876
25337
  if (attr.type !== "JSXAttribute") continue;
@@ -24898,6 +25359,7 @@ function rewriteJsxStep(ctx) {
24898
25359
  const n = attr.name.name;
24899
25360
  if (decl.shouldForwardProp.dropProps.includes(n)) {
24900
25361
  if (inlineVariantProps.has(n)) return true;
25362
+ if (decl.variantStyleKeys && n in decl.variantStyleKeys) return true;
24901
25363
  return false;
24902
25364
  }
24903
25365
  if (decl.shouldForwardProp.dropPrefix && n.startsWith(decl.shouldForwardProp.dropPrefix)) return false;
@@ -24981,10 +25443,11 @@ function rewriteJsxStep(ctx) {
24981
25443
  else extraMixinArgs.push(expr);
24982
25444
  }
24983
25445
  }
25446
+ const baseStyleKey = matchedCombinedStyleKey ?? decl.styleKey;
24984
25447
  const styleArgs = [
24985
25448
  ...decl.extendsStyleKey ? [j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(decl.extendsStyleKey))] : [],
24986
25449
  ...extraMixinArgs,
24987
- j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(decl.styleKey)),
25450
+ j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(baseStyleKey)),
24988
25451
  ...extraAfterBaseArgs
24989
25452
  ];
24990
25453
  const variantKeys = decl.variantStyleKeys ?? {};
@@ -25006,6 +25469,7 @@ function rewriteJsxStep(ctx) {
25006
25469
  if (valueExpr) for (const p of pairs) styleArgs.push(j.callExpression(j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(p.fnKey)), [valueExpr]));
25007
25470
  return;
25008
25471
  }
25472
+ if (combinedStylePropNames.has(n) && matchedCombinedStyleKey) return;
25009
25473
  const inlineVariantDimension = inlineVariantByProp.get(n);
25010
25474
  if (inlineVariantDimension) {
25011
25475
  const variantLookup = buildInlineVariantLookupFromAttr(j, inlineVariantDimension.variantObjectName, attr);
@@ -25028,9 +25492,19 @@ function rewriteJsxStep(ctx) {
25028
25492
  return;
25029
25493
  }
25030
25494
  if (attr.value.type === "JSXExpressionContainer") {
25495
+ const staticLiteralValue = readStaticJsxLiteral(attr);
25496
+ if (staticLiteralValue !== void 0 && staticLiteralValue) {
25497
+ styleArgs.push(j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(variantStyleKey)));
25498
+ return;
25499
+ }
25031
25500
  styleArgs.push(j.logicalExpression("&&", attr.value.expression, j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(variantStyleKey))));
25032
25501
  return;
25033
25502
  }
25503
+ if (attr.value.type === "StringLiteral" || attr.value.type === "NumericLiteral" || attr.value.type === "Literal") {
25504
+ const literalVal = readStaticJsxLiteral(attr);
25505
+ if (literalVal !== void 0 && literalVal) styleArgs.push(j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(variantStyleKey)));
25506
+ return;
25507
+ }
25034
25508
  };
25035
25509
  for (const attr of leading) processAttr(attr, keptLeadingAfterVariants);
25036
25510
  for (const attr of rest) processAttr(attr, keptRestAfterVariants);
@@ -25087,6 +25561,22 @@ function extractJsxAttrValueExpr(j, attr) {
25087
25561
  if (a.value.type === "StringLiteral" || a.value.type === "Literal") return j.literal(a.value.value);
25088
25562
  if (a.value.type === "JSXExpressionContainer") return a.value.expression;
25089
25563
  }
25564
+ /**
25565
+ * Finds the combined style key matching the consumed props at a JSX call site.
25566
+ * Returns the style key if a matching combination exists, or undefined otherwise.
25567
+ */
25568
+ function matchCallSiteCombinedStyle(combinedStyles, attrs) {
25569
+ if (!combinedStyles?.length) return;
25570
+ const attrNames = /* @__PURE__ */ new Set();
25571
+ for (const a of attrs) {
25572
+ const attr = a;
25573
+ if (attr.type === "JSXAttribute" && attr.name?.type === "JSXIdentifier" && typeof attr.name.name === "string") attrNames.add(attr.name.name);
25574
+ }
25575
+ const presentPropNames = [...new Set(combinedStyles.flatMap((c) => c.propNames))].filter((p) => attrNames.has(p)).sort();
25576
+ if (presentPropNames.length === 0) return;
25577
+ const key = presentPropNames.join(",");
25578
+ return combinedStyles.find((c) => [...c.propNames].sort().join(",") === key)?.styleKey;
25579
+ }
25090
25580
 
25091
25581
  //#endregion
25092
25582
  //#region src/internal/transform-steps/upgrade-polymorphic-as-prop-types.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "styled-components-to-stylex-codemod",
3
- "version": "0.0.20",
3
+ "version": "0.0.22",
4
4
  "description": "Codemod to transform styled-components to StyleX",
5
5
  "keywords": [
6
6
  "codemod",