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

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-tKRY0yOA.mjs";
2
2
 
3
3
  //#region src/run.d.ts
4
4
  interface RunTransformOptions {
@@ -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 contains unresolvable interpolation" | "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 contains unresolvable interpolation" | "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;
@@ -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-tKRY0yOA.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
  }
@@ -6439,7 +6439,8 @@ function emptyOkVariantOutcome(hasLocalCallsites, usedConsumedPropsAtCallSites)
6439
6439
  usedConsumedPropsAtCallSites,
6440
6440
  foldedBaseSx: {},
6441
6441
  bakedInConsumedProps: [],
6442
- staticBooleanVariants: []
6442
+ staticBooleanVariants: [],
6443
+ callSiteCombinedStyles: []
6443
6444
  };
6444
6445
  }
6445
6446
  function applyBaseComponentResolution(ctx, styledDecls) {
@@ -6490,7 +6491,8 @@ function applyBaseComponentResolution(ctx, styledDecls) {
6490
6491
  usedConsumedPropsAtCallSites: variantOutcome.usedConsumedPropsAtCallSites,
6491
6492
  foldedBaseSx: variantOutcome.foldedBaseSx,
6492
6493
  bakedInConsumedProps: variantOutcome.bakedInConsumedProps,
6493
- staticBooleanVariants: variantOutcome.staticBooleanVariants
6494
+ staticBooleanVariants: variantOutcome.staticBooleanVariants,
6495
+ callSiteCombinedStyles: variantOutcome.callSiteCombinedStyles
6494
6496
  });
6495
6497
  }
6496
6498
  }
@@ -6644,7 +6646,7 @@ function buildInlineResolverVariantDimensions(args) {
6644
6646
  continue;
6645
6647
  }
6646
6648
  const [singleKey, singleVariantStyles] = Object.entries(variants)[0];
6647
- if (singleKey === "true" && booleanOnlyProps.has(propName)) {
6649
+ if (isSingleVariantKeyTruthy(singleKey, booleanOnlyProps.has(propName))) {
6648
6650
  staticBooleanVariants.push({
6649
6651
  propName,
6650
6652
  styleKey: `${decl.styleKey}${toSuffixFromProp(propName)}`,
@@ -6661,6 +6663,31 @@ function buildInlineResolverVariantDimensions(args) {
6661
6663
  propTypeFromKeyof: true
6662
6664
  });
6663
6665
  }
6666
+ const callSiteCombinedStyles = buildCallSiteCombinedStyles({
6667
+ decl,
6668
+ staticBooleanVariants,
6669
+ dimensions,
6670
+ baseSx: {
6671
+ ...baseResult.sx,
6672
+ ...foldedBaseSx
6673
+ },
6674
+ propsByUsage: usageResult.propsByUsage,
6675
+ hasCompleteCallsiteVisibility: !willHaveExternalInterface(ctx, decl, styledDecls) && !decl.usedAsValue,
6676
+ hasPropReferencingTemplateExpressions: (decl.templateExpressions ?? []).some((expr) => {
6677
+ const type = expr?.type;
6678
+ return type === "ArrowFunctionExpression" || type === "FunctionExpression";
6679
+ })
6680
+ });
6681
+ if (callSiteCombinedStyles) return {
6682
+ kind: "ok",
6683
+ variantDimensions: dimensions,
6684
+ hasLocalCallsites,
6685
+ usedConsumedPropsAtCallSites,
6686
+ foldedBaseSx,
6687
+ bakedInConsumedProps,
6688
+ staticBooleanVariants: [],
6689
+ callSiteCombinedStyles
6690
+ };
6664
6691
  return {
6665
6692
  kind: "ok",
6666
6693
  variantDimensions: dimensions,
@@ -6668,7 +6695,8 @@ function buildInlineResolverVariantDimensions(args) {
6668
6695
  usedConsumedPropsAtCallSites,
6669
6696
  foldedBaseSx,
6670
6697
  bakedInConsumedProps,
6671
- staticBooleanVariants
6698
+ staticBooleanVariants,
6699
+ callSiteCombinedStyles: []
6672
6700
  };
6673
6701
  }
6674
6702
  /**
@@ -6804,12 +6832,59 @@ function diffSx(baseSx, siteSx) {
6804
6832
  for (const [prop, value] of Object.entries(siteSx)) if (!(prop in baseSx)) out[prop] = value;
6805
6833
  return out;
6806
6834
  }
6835
+ /**
6836
+ * For direct JSX resolution, merges individual single-key variant styles into
6837
+ * combined per-call-site entries. Returns the combined styles, or null if
6838
+ * combining is not applicable (e.g., multi-key variants, wrapper components).
6839
+ */
6840
+ function buildCallSiteCombinedStyles(args) {
6841
+ const { decl, staticBooleanVariants, dimensions, baseSx, propsByUsage, hasCompleteCallsiteVisibility, hasPropReferencingTemplateExpressions } = args;
6842
+ if (!decl.isDirectJsxResolution || !hasCompleteCallsiteVisibility || hasPropReferencingTemplateExpressions || staticBooleanVariants.length < 2 || dimensions.length > 0) return null;
6843
+ const variantsByProp = new Map(staticBooleanVariants.map((v) => [v.propName, v]));
6844
+ const combinationGroups = /* @__PURE__ */ new Map();
6845
+ for (const siteProps of propsByUsage) {
6846
+ const matchingPropNames = Object.keys(siteProps).filter((p) => variantsByProp.has(p)).sort();
6847
+ if (matchingPropNames.length === 0) continue;
6848
+ const groupKey = matchingPropNames.join(",");
6849
+ if (combinationGroups.has(groupKey)) continue;
6850
+ const mergedStyles = { ...baseSx };
6851
+ for (const propName of matchingPropNames) {
6852
+ const variant = variantsByProp.get(propName);
6853
+ Object.assign(mergedStyles, variant.styles);
6854
+ }
6855
+ combinationGroups.set(groupKey, {
6856
+ propNames: matchingPropNames,
6857
+ styles: mergedStyles
6858
+ });
6859
+ }
6860
+ if (combinationGroups.size === 0 || combinationGroups.size >= staticBooleanVariants.length) return null;
6861
+ const result = [];
6862
+ for (const [, { propNames, styles }] of combinationGroups) {
6863
+ const suffix = propNames.map((p) => toSuffixFromProp(p)).join("");
6864
+ result.push({
6865
+ propNames,
6866
+ styleKey: `${decl.styleKey}${suffix}`,
6867
+ styles
6868
+ });
6869
+ }
6870
+ return result;
6871
+ }
6872
+ /**
6873
+ * Checks whether a single variant key represents a value that is truthy at runtime.
6874
+ * For boolean props, only `"true"` is truthy (`false` is falsy).
6875
+ * For non-boolean props (numbers, strings), "0" and "" are falsy; everything else is truthy.
6876
+ * Falsy values cannot use the truthy-guard pattern (`prop && styles.key`) safely.
6877
+ */
6878
+ function isSingleVariantKeyTruthy(key, isBooleanProp) {
6879
+ if (isBooleanProp) return key === "true";
6880
+ return key !== "0" && key !== "";
6881
+ }
6807
6882
  function serializeRecord(record) {
6808
6883
  const ordered = Object.keys(record).sort().map((key) => [key, record[key]]);
6809
6884
  return JSON.stringify(ordered);
6810
6885
  }
6811
6886
  function inlineResolvedBaseComponent(args) {
6812
- const { ctx, decl, baseStaticProps, importSource, importedName, baseResult, consumedProps, variantDimensions, hasLocalCallsites, usedConsumedPropsAtCallSites, foldedBaseSx, bakedInConsumedProps, staticBooleanVariants } = args;
6887
+ const { ctx, decl, baseStaticProps, importSource, importedName, baseResult, consumedProps, variantDimensions, hasLocalCallsites, usedConsumedPropsAtCallSites, foldedBaseSx, bakedInConsumedProps, staticBooleanVariants, callSiteCombinedStyles } = args;
6813
6888
  const sxRule = createRuleFromStylexDeclarations(Object.keys(foldedBaseSx).length > 0 ? {
6814
6889
  ...baseResult.sx,
6815
6890
  ...foldedBaseSx
@@ -6853,6 +6928,7 @@ function inlineResolvedBaseComponent(args) {
6853
6928
  };
6854
6929
  if (variantDimensions.length > 0) decl.variantDimensions = [...decl.variantDimensions ?? [], ...variantDimensions];
6855
6930
  if (staticBooleanVariants.length > 0) decl.staticBooleanVariants = [...decl.staticBooleanVariants ?? [], ...staticBooleanVariants];
6931
+ if (callSiteCombinedStyles.length > 0) decl.callSiteCombinedStyles = [...decl.callSiteCombinedStyles ?? [], ...callSiteCombinedStyles];
6856
6932
  decl.inlinedBaseComponent = {
6857
6933
  importSource,
6858
6934
  importedName,
@@ -6986,7 +7062,8 @@ function resolveDirectJsxUsages(ctx, styledDecls) {
6986
7062
  usedConsumedPropsAtCallSites: variantOutcome.usedConsumedPropsAtCallSites,
6987
7063
  foldedBaseSx: variantOutcome.foldedBaseSx,
6988
7064
  bakedInConsumedProps: variantOutcome.bakedInConsumedProps,
6989
- staticBooleanVariants: variantOutcome.staticBooleanVariants
7065
+ staticBooleanVariants: variantOutcome.staticBooleanVariants,
7066
+ callSiteCombinedStyles: variantOutcome.callSiteCombinedStyles
6990
7067
  });
6991
7068
  usedStyleKeys.add(syntheticDecl.styleKey);
6992
7069
  styledDecls.push(syntheticDecl);
@@ -13672,6 +13749,149 @@ function buildInterpolatedTemplate(args) {
13672
13749
  return j.templateLiteral(quasis, exprs);
13673
13750
  }
13674
13751
 
13752
+ //#endregion
13753
+ //#region src/internal/lower-rules/utils.ts
13754
+ /**
13755
+ * Shared lower-rules helpers for merging and formatting style objects.
13756
+ * Core concepts: deep merge semantics, AST node detection, and media query resolution.
13757
+ */
13758
+ /** Returns true for at-rules the codemod can transform (`@media`, `@container`). */
13759
+ function isSupportedAtRule(atRule) {
13760
+ return atRule.startsWith("@media") || atRule.startsWith("@container");
13761
+ }
13762
+ /** Finds the first supported at-rule (`@media` or `@container`) in the stack, if any. */
13763
+ function findSupportedAtRule(atRuleStack) {
13764
+ return atRuleStack.find(isSupportedAtRule);
13765
+ }
13766
+ /**
13767
+ * Resolves a media/container at-rule string that may contain `__SC_EXPR_N__` placeholders.
13768
+ *
13769
+ * First tries `resolveSelector` (when available) to produce a computed key like
13770
+ * `[breakpoints.phone]`. Falls back to static value substitution.
13771
+ *
13772
+ * Returns null if the placeholder cannot be resolved at all.
13773
+ */
13774
+ function resolveMediaAtRulePlaceholders(media, getSlotExpr, ctx) {
13775
+ if (!media.includes("__SC_EXPR_")) return {
13776
+ kind: "static",
13777
+ value: media
13778
+ };
13779
+ const globalRe = new RegExp(PLACEHOLDER_RE.source, "g");
13780
+ const matches = [...media.matchAll(globalRe)];
13781
+ if (matches.length === 1 && ctx.resolveSelector && ctx.parseExpr) {
13782
+ const expr = getSlotExpr(Number(matches[0][1]));
13783
+ if (expr && typeof expr === "object") {
13784
+ const info = extractRootAndPath(expr);
13785
+ if (info) {
13786
+ const imp = ctx.lookupImport(info.rootName, info.rootNode);
13787
+ if (imp) {
13788
+ const result = ctx.resolveSelector({
13789
+ kind: "selectorInterpolation",
13790
+ importedName: imp.importedName,
13791
+ source: imp.source,
13792
+ path: info.path.length > 0 ? info.path.join(".") : void 0,
13793
+ filePath: ctx.filePath
13794
+ });
13795
+ if (result?.kind === "media") {
13796
+ const keyExpr = ctx.parseExpr(result.expr);
13797
+ if (keyExpr) {
13798
+ for (const impSpec of result.imports ?? []) ctx.resolverImports.set(JSON.stringify(impSpec), impSpec);
13799
+ return {
13800
+ kind: "computed",
13801
+ keyExpr,
13802
+ imports: result.imports ?? []
13803
+ };
13804
+ }
13805
+ }
13806
+ }
13807
+ }
13808
+ }
13809
+ }
13810
+ const resolved = resolveMediaQueryPlaceholders(media, (slotId) => resolveSlotExprToStaticValue(getSlotExpr(slotId), ctx.lookupImport, ctx.resolveValue, ctx.filePath, ctx.resolverImports));
13811
+ if (resolved === null) return null;
13812
+ return {
13813
+ kind: "static",
13814
+ value: resolved
13815
+ };
13816
+ }
13817
+ /** Returns true if the key looks like a StyleX style condition (pseudo, media, container). */
13818
+ function isStyleConditionKey(key) {
13819
+ return key.startsWith(":") || key.startsWith("::") || key.startsWith("@media") || key.startsWith("@container");
13820
+ }
13821
+ /**
13822
+ * Merges tracked @media values into a base style object as nested StyleX objects.
13823
+ * Each property that has media-scoped values is wrapped in:
13824
+ * `{ default: baseValue, "@media (...)": mediaValue }`
13825
+ */
13826
+ function mergeMediaIntoStyles(base, mediaStyles) {
13827
+ for (const [mediaQuery, mediaStyle] of mediaStyles) for (const [prop, mediaValue] of Object.entries(mediaStyle)) base[prop] = {
13828
+ default: base[prop] ?? null,
13829
+ [mediaQuery]: mediaValue
13830
+ };
13831
+ }
13832
+ /**
13833
+ * Recursively merges style objects, combining nested objects rather than overwriting.
13834
+ *
13835
+ * Note: Security scanners may flag this as prototype pollution, but this is a false positive.
13836
+ * This is a codemod that runs locally on the developer's own source code - there is no
13837
+ * untrusted input that could exploit prototype pollution. The source objects are style
13838
+ * declarations extracted from the developer's own styled-components code.
13839
+ */
13840
+ function mergeStyleObjects(target, source) {
13841
+ for (const [key, value] of Object.entries(source)) {
13842
+ const existing = target[key];
13843
+ if (existing && value && typeof existing === "object" && typeof value === "object" && !Array.isArray(existing) && !Array.isArray(value) && !isAstNode(existing) && !isAstNode(value)) mergeStyleObjects(existing, value);
13844
+ else target[key] = value;
13845
+ }
13846
+ }
13847
+ /** Resolves `__SC_EXPR_N__` placeholders in a string to static values via a callback. */
13848
+ function resolveMediaQueryPlaceholders(mediaQuery, resolveSlot) {
13849
+ if (!mediaQuery.includes("__SC_EXPR_")) return mediaQuery;
13850
+ const globalRe = new RegExp(PLACEHOLDER_RE.source, "g");
13851
+ const matches = [];
13852
+ let match;
13853
+ while ((match = globalRe.exec(mediaQuery)) !== null) matches.push({
13854
+ full: match[0],
13855
+ slotId: Number(match[1])
13856
+ });
13857
+ let resolved = mediaQuery;
13858
+ for (const m of matches) {
13859
+ const staticVal = resolveSlot(m.slotId);
13860
+ if (staticVal === null) return null;
13861
+ resolved = resolved.replace(m.full, String(staticVal));
13862
+ }
13863
+ return resolved;
13864
+ }
13865
+ /** Resolves a slot expression AST node to a static string or number via the adapter. */
13866
+ function resolveSlotExprToStaticValue(expr, lookupImport, resolveValue, filePath, resolverImports) {
13867
+ if (!expr || typeof expr !== "object") return null;
13868
+ const staticVal = literalToStaticValue(expr);
13869
+ if (typeof staticVal === "string" || typeof staticVal === "number") return staticVal;
13870
+ const info = extractRootAndPath(expr);
13871
+ if (!info) return null;
13872
+ const imp = lookupImport(info.rootName, info.rootNode);
13873
+ if (!imp) return null;
13874
+ const result = resolveValue({
13875
+ kind: "importedValue",
13876
+ importedName: imp.importedName,
13877
+ source: imp.source,
13878
+ ...info.path.length > 0 ? { path: info.path.join(".") } : {},
13879
+ filePath
13880
+ });
13881
+ if (!result || !("expr" in result)) return null;
13882
+ for (const impSpec of result.imports ?? []) resolverImports.set(JSON.stringify(impSpec), impSpec);
13883
+ return parseStaticExprString(result.expr);
13884
+ }
13885
+ /** Parses a JS expression string as a static numeric or quoted string value. */
13886
+ function parseStaticExprString(expr) {
13887
+ const trimmed = expr.trim();
13888
+ if (!trimmed) return null;
13889
+ const num = Number(trimmed);
13890
+ if (!isNaN(num) && trimmed !== "") return num;
13891
+ if (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("'") && trimmed.endsWith("'")) return trimmed.slice(1, -1);
13892
+ return null;
13893
+ }
13894
+
13675
13895
  //#endregion
13676
13896
  //#region src/internal/lower-rules/css-helper.ts
13677
13897
  /**
@@ -13816,14 +14036,15 @@ function createCssHelperResolver(args) {
13816
14036
  exprString: JSON.stringify(v)
13817
14037
  };
13818
14038
  }
13819
- if (branch.type === "Identifier" && typeof branch.name === "string") {
13820
- const name = branch.name;
13821
- const imp = importMap.get(name);
14039
+ const info = extractRootAndPath(branch);
14040
+ if (info) {
14041
+ const imp = importMap.get(info.rootName);
13822
14042
  if (imp) {
13823
14043
  const res = resolveValue({
13824
14044
  kind: "importedValue",
13825
14045
  importedName: imp.importedName,
13826
14046
  source: imp.source,
14047
+ ...info.path.length > 0 ? { path: info.path.join(".") } : {},
13827
14048
  filePath,
13828
14049
  loc: getNodeLocStart(branch) ?? void 0
13829
14050
  });
@@ -13835,9 +14056,9 @@ function createCssHelperResolver(args) {
13835
14056
  exprString: res.expr
13836
14057
  } : null;
13837
14058
  }
13838
- return {
14059
+ if (branch.type === "Identifier") return {
13839
14060
  ast: branch,
13840
- exprString: name
14061
+ exprString: info.rootName
13841
14062
  };
13842
14063
  }
13843
14064
  return null;
@@ -13872,9 +14093,28 @@ function createCssHelperResolver(args) {
13872
14093
  const dynamicProps = [];
13873
14094
  const dynamicPropKeys = /* @__PURE__ */ new Set();
13874
14095
  const conditionalVariants = [];
14096
+ const lookupImport = (localName) => importMap.get(localName) ?? null;
13875
14097
  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");
14098
+ const rawMedia = findSupportedAtRule(rule.atRuleStack);
14099
+ if (rule.atRuleStack.length > 0 && !rawMedia) return bail("Conditional `css` block: @-rules (e.g., @media, @supports) are not supported");
14100
+ let media = rawMedia;
14101
+ let computedMediaKey = null;
14102
+ if (rawMedia) {
14103
+ const resolved = resolveMediaAtRulePlaceholders(rawMedia, (slotId) => slotExprById.get(slotId), {
14104
+ lookupImport,
14105
+ resolveValue,
14106
+ resolveSelector: args.resolveSelector,
14107
+ parseExpr,
14108
+ filePath,
14109
+ resolverImports
14110
+ });
14111
+ if (resolved === null) return bail("Conditional `css` block: media query contains unresolvable interpolation");
14112
+ if (resolved.kind === "static") media = resolved.value;
14113
+ else {
14114
+ computedMediaKey = resolved;
14115
+ media = void 0;
14116
+ }
14117
+ }
13878
14118
  const selector = (rule.selector ?? "").trim();
13879
14119
  const allowDynamicValues = selector === "&";
13880
14120
  let target = out;
@@ -13906,7 +14146,19 @@ function createCssHelperResolver(args) {
13906
14146
  }
13907
14147
  const mergeIntoContext = (value, prop, targetObj) => {
13908
14148
  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);
14149
+ if (computedMediaKey) {
14150
+ const nested = existing && typeof existing === "object" && !Array.isArray(existing) && !isAstNode(existing) ? existing : { default: existing ?? null };
14151
+ if (!("default" in nested)) nested.default = null;
14152
+ nested.__computedKeys = [...nested.__computedKeys ?? [], {
14153
+ keyExpr: computedMediaKey.keyExpr,
14154
+ value
14155
+ }];
14156
+ return nested;
14157
+ }
14158
+ if (media && currentPseudoClass) {
14159
+ const pseudoExisting = existing && typeof existing === "object" && !Array.isArray(existing) && !isAstNode(existing) ? existing[currentPseudoClass] : void 0;
14160
+ return mergeIntoPseudoContext(mergeIntoPseudoContext(value, media, pseudoExisting), currentPseudoClass, existing);
14161
+ }
13910
14162
  if (media) return mergeIntoPseudoContext(value, media, existing);
13911
14163
  return mergeIntoPseudoContext(value, currentPseudoClass, existing);
13912
14164
  };
@@ -14484,6 +14736,7 @@ function createLowerRulesState(ctx) {
14484
14736
  importMap,
14485
14737
  filePath,
14486
14738
  resolveValue,
14739
+ resolveSelector,
14487
14740
  parseExpr,
14488
14741
  resolverImports,
14489
14742
  warnings,
@@ -14556,39 +14809,6 @@ function createLowerRulesState(ctx) {
14556
14809
  return state;
14557
14810
  }
14558
14811
 
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
14812
  //#endregion
14593
14813
  //#region src/internal/lower-rules/variant-utils.ts
14594
14814
  /**
@@ -16933,8 +17153,8 @@ function tryResolveInlineStyleValueForNestedPropAccess(node) {
16933
17153
  }
16934
17154
  function tryResolveInlineStyleValueFromArrowFn(node) {
16935
17155
  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");
17156
+ const hasMediaAtRule = (node.css.atRuleStack ?? []).some(isSupportedAtRule);
17157
+ const isMediaSelector = isSupportedAtRule((node.css.selector ?? "").trim());
16938
17158
  if (!hasMediaAtRule && !isMediaSelector) return null;
16939
17159
  const expr = node.expr;
16940
17160
  if (!isArrowFunctionExpression(expr)) return null;
@@ -17032,7 +17252,7 @@ function resolveTemplateLiteralBranch(ctx, args) {
17032
17252
  const inlineEntries = [];
17033
17253
  const mediaStyles = /* @__PURE__ */ new Map();
17034
17254
  for (const rule of rules) {
17035
- const media = rule.atRuleStack.find((a) => a.startsWith("@media") || a.startsWith("@container"));
17255
+ let media = findSupportedAtRule(rule.atRuleStack);
17036
17256
  if (rule.atRuleStack.length > 0 && !media) {
17037
17257
  ctx.warnings?.push({
17038
17258
  severity: "warning",
@@ -17041,8 +17261,36 @@ function resolveTemplateLiteralBranch(ctx, args) {
17041
17261
  });
17042
17262
  return null;
17043
17263
  }
17264
+ let computedMediaKeyExpr;
17265
+ if (media) {
17266
+ const resolved = resolveMediaAtRulePlaceholders(media, (slotId) => slotExprById.get(slotId), {
17267
+ lookupImport: resolveImportInScope,
17268
+ resolveValue,
17269
+ resolveSelector: ctx.resolveSelector,
17270
+ parseExpr,
17271
+ filePath,
17272
+ resolverImports
17273
+ });
17274
+ if (resolved === null) return null;
17275
+ if (resolved.kind === "static") media = resolved.value;
17276
+ else {
17277
+ computedMediaKeyExpr = resolved.keyExpr;
17278
+ media = void 0;
17279
+ }
17280
+ }
17044
17281
  if ((rule.selector ?? "").trim() !== "&") return null;
17045
17282
  const setStyleValue = (prop, value) => {
17283
+ if (computedMediaKeyExpr) {
17284
+ const existing = style[prop];
17285
+ const nested = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : { default: existing ?? null };
17286
+ if (!("default" in nested)) nested.default = null;
17287
+ nested.__computedKeys = [...nested.__computedKeys ?? [], {
17288
+ keyExpr: computedMediaKeyExpr,
17289
+ value
17290
+ }];
17291
+ style[prop] = nested;
17292
+ return;
17293
+ }
17046
17294
  if (media) {
17047
17295
  const target = mediaStyles.get(media) ?? {};
17048
17296
  mediaStyles.set(media, target);
@@ -17741,7 +17989,7 @@ const createCssHelperHandlers = (ctx) => {
17741
17989
  //#endregion
17742
17990
  //#region src/internal/lower-rules/css-helper-conditional.ts
17743
17991
  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;
17992
+ 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
17993
  const avoidNames = new Set(importMap.keys());
17746
17994
  /**
17747
17995
  * Resolve the TS type node for a prop used in a style function parameter.
@@ -17910,15 +18158,41 @@ function createCssHelperConditionalHandler(ctx) {
17910
18158
  const { rules, slotExprById } = parseCssTemplateToRules(tpl);
17911
18159
  const out = /* @__PURE__ */ new Map();
17912
18160
  const mediaValues = /* @__PURE__ */ new Map();
17913
- const setValueForProp = (prop, value, media) => {
17914
- if (media) {
18161
+ const computedMediaValues = /* @__PURE__ */ new Map();
18162
+ const setValueForProp = (prop, value, media, computedKey) => {
18163
+ if (computedKey) {
18164
+ const arr = computedMediaValues.get(prop) ?? [];
18165
+ arr.push({
18166
+ keyExpr: computedKey,
18167
+ value
18168
+ });
18169
+ computedMediaValues.set(prop, arr);
18170
+ } else if (media) {
17915
18171
  if (!mediaValues.has(prop)) mediaValues.set(prop, /* @__PURE__ */ new Map());
17916
18172
  mediaValues.get(prop).set(media, value);
17917
18173
  } else out.set(prop, value);
17918
18174
  };
17919
18175
  for (const rule of rules) {
17920
- const media = rule.atRuleStack.find((a) => a.startsWith("@media"));
17921
- if (rule.atRuleStack.length > 0 && !media) return null;
18176
+ const rawMedia = findSupportedAtRule(rule.atRuleStack);
18177
+ if (rule.atRuleStack.length > 0 && !rawMedia) return null;
18178
+ let media = rawMedia;
18179
+ let computedMediaKeyExpr;
18180
+ if (rawMedia) {
18181
+ const resolved = resolveMediaAtRulePlaceholders(rawMedia, (slotId) => slotExprById.get(slotId), {
18182
+ lookupImport: resolveImportInScope,
18183
+ resolveValue,
18184
+ resolveSelector,
18185
+ parseExpr,
18186
+ filePath,
18187
+ resolverImports
18188
+ });
18189
+ if (resolved === null) return null;
18190
+ if (resolved.kind === "static") media = resolved.value;
18191
+ else {
18192
+ computedMediaKeyExpr = resolved.keyExpr;
18193
+ media = void 0;
18194
+ }
18195
+ }
17922
18196
  if ((rule.selector ?? "").trim() !== "&") return null;
17923
18197
  for (const d of rule.declarations) {
17924
18198
  if (!d.property) return null;
@@ -17932,7 +18206,7 @@ function createCssHelperConditionalHandler(ctx) {
17932
18206
  if (m) value = `"${m[1]}"`;
17933
18207
  else if (!value.startsWith("\"") && !value.endsWith("\"")) value = `"${value}"`;
17934
18208
  }
17935
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") setValueForProp(mapped.prop, staticValueToLiteral(j, value), media);
18209
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") setValueForProp(mapped.prop, staticValueToLiteral(j, value), media, computedMediaKeyExpr);
17936
18210
  else return null;
17937
18211
  }
17938
18212
  continue;
@@ -17945,13 +18219,21 @@ function createCssHelperConditionalHandler(ctx) {
17945
18219
  const rawExpr = replaceParamWithProps(slotExpr);
17946
18220
  const { prefix, suffix } = extractStaticPartsForDecl(d);
17947
18221
  const valueExpr = prefix || suffix ? buildTemplateWithStaticParts(j, rawExpr, prefix, suffix) : rawExpr;
17948
- for (const mapped of cssDeclarationToStylexDeclarations(d)) setValueForProp(mapped.prop, valueExpr, media);
18222
+ for (const mapped of cssDeclarationToStylexDeclarations(d)) setValueForProp(mapped.prop, valueExpr, media, computedMediaKeyExpr);
17949
18223
  }
17950
18224
  }
17951
- for (const [prop, queries] of mediaValues) {
18225
+ const allMediaProps = new Set([...mediaValues.keys(), ...computedMediaValues.keys()]);
18226
+ for (const prop of allMediaProps) {
17952
18227
  const baseValue = out.get(prop);
17953
18228
  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));
18229
+ const queries = mediaValues.get(prop);
18230
+ if (queries) for (const [query, value] of queries) properties.push(j.property("init", j.literal(query), value));
18231
+ const computed = computedMediaValues.get(prop);
18232
+ if (computed) for (const { keyExpr, value } of computed) {
18233
+ const p = j.property("init", keyExpr, value);
18234
+ p.computed = true;
18235
+ properties.push(p);
18236
+ }
17955
18237
  out.set(prop, j.objectExpression(properties));
17956
18238
  }
17957
18239
  return out;
@@ -19284,7 +19566,7 @@ const createValuePatternHandlers = (ctx) => {
19284
19566
  * Core concepts: per-component style buckets, helper factories, and resolver wiring.
19285
19567
  */
19286
19568
  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;
19569
+ const { api, j, root, filePath, warnings, resolverImports, parseExpr, resolveValue, resolveCall, resolveCallOptional, resolveSelector, importMap, cssHelperFunctions, stringMappingFns, hasLocalThemeBinding, isCssHelperTaggedTemplate, resolveCssHelperTemplate, resolveImportInScope, usedCssHelperFunctions, markBail } = state;
19288
19570
  const styleObj = {};
19289
19571
  const perPropPseudo = {};
19290
19572
  const perPropMedia = {};
@@ -19371,6 +19653,7 @@ function createDeclProcessingState(state, decl) {
19371
19653
  parseExpr,
19372
19654
  resolveValue,
19373
19655
  resolveCall,
19656
+ resolveSelector,
19374
19657
  resolveImportInScope,
19375
19658
  resolverImports,
19376
19659
  isCssHelperTaggedTemplate,
@@ -19432,7 +19715,7 @@ function createDeclProcessingState(state, decl) {
19432
19715
  const out = {};
19433
19716
  const mediaStyles = /* @__PURE__ */ new Map();
19434
19717
  for (const rule of rules) {
19435
- const media = rule.atRuleStack.find((a) => a.startsWith("@media") || a.startsWith("@container"));
19718
+ const media = findSupportedAtRule(rule.atRuleStack);
19436
19719
  if (rule.atRuleStack.length > 0 && !media) {
19437
19720
  warnings.push({
19438
19721
  severity: "warning",
@@ -19878,18 +20161,26 @@ function tryHandleInterpolatedBorder(ctx, args) {
19878
20161
  if (quasis.length === 2 && exprs.length === 1) {
19879
20162
  const prefix = quasis[0]?.value?.cooked ?? quasis[0]?.value?.raw ?? "";
19880
20163
  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
- };
20164
+ if (suffix.trim() === "") {
20165
+ const parsed = parseInterpolatedBorderStaticParts({
20166
+ prop,
20167
+ prefix,
20168
+ suffix
20169
+ });
20170
+ if (parsed?.width && parsed?.style) return {
20171
+ width: parsed.width,
20172
+ style: parsed.style,
20173
+ colorExpr: exprs[0]
20174
+ };
20175
+ }
20176
+ if (prefix.trim() === "" && suffix.trim() !== "") {
20177
+ const parsed = parseBorderShorthandParts(suffix.trim());
20178
+ if (parsed?.style && parsed?.color) return {
20179
+ width: exprs[0],
20180
+ style: parsed.style,
20181
+ colorExpr: parsed.color
20182
+ };
20183
+ }
19893
20184
  }
19894
20185
  if (quasis.length === 3 && exprs.length === 2) {
19895
20186
  const prefix = quasis[0]?.value?.cooked ?? quasis[0]?.value?.raw ?? "";
@@ -19993,7 +20284,7 @@ function tryHandleInterpolatedBorder(ctx, args) {
19993
20284
  }
19994
20285
  }
19995
20286
  if (hasStaticWidthOrStyle) applyResolvedPropValue(targetProp, resolved.exprAst);
19996
- else applyResolvedPropValue(direction ? `border${direction}` : "border", resolved.exprAst);
20287
+ else bailUnsupportedWithContext("Resolved border helper value could not be expanded to longhand properties", { property: prop }, getNodeLocStart(expr));
19997
20288
  return true;
19998
20289
  }
19999
20290
  }
@@ -21058,6 +21349,20 @@ function handleInterpolatedDeclaration(args) {
21058
21349
  };
21059
21350
  if (tryHandleThemeValueInPseudo()) continue;
21060
21351
  const resolveImportedValueExpr = (expr) => {
21352
+ if (expr?.type === "BinaryExpression") {
21353
+ const leftResult = resolveImportedValueExpr(expr.left);
21354
+ const rightResult = resolveImportedValueExpr(expr.right);
21355
+ if (!leftResult && !rightResult) return null;
21356
+ if (leftResult && "bail" in leftResult) return leftResult;
21357
+ if (rightResult && "bail" in rightResult) return rightResult;
21358
+ const resolvedLeft = leftResult ? leftResult.resolved : expr.left;
21359
+ const resolvedRight = rightResult ? rightResult.resolved : expr.right;
21360
+ const imports = [...leftResult?.imports ?? [], ...rightResult?.imports ?? []];
21361
+ return {
21362
+ resolved: j.binaryExpression(expr.operator, resolvedLeft, resolvedRight),
21363
+ imports
21364
+ };
21365
+ }
21061
21366
  const info = getRootIdentifierInfo(expr);
21062
21367
  if (!info) return null;
21063
21368
  const imp = resolveImportInScope(info.rootName, info.rootNode);
@@ -22830,7 +23135,7 @@ function processDeclRules(ctx) {
22830
23135
  }
22831
23136
  }
22832
23137
  }
22833
- let media = rule.atRuleStack.find((a) => a.startsWith("@media") || a.startsWith("@container"));
23138
+ let media = findSupportedAtRule(rule.atRuleStack);
22834
23139
  const intrinsicTagName = decl.base.kind === "intrinsic" ? decl.base.tagName : null;
22835
23140
  let selector = normalizeSelectorForAttributePseudos(rule.selector, intrinsicTagName);
22836
23141
  selector = normalizeInterpolatedSelector(selector);
@@ -22843,10 +23148,37 @@ function processDeclRules(ctx) {
22843
23148
  first.leadingComment = first.leadingComment ? `${note}\n${first.leadingComment}` : note;
22844
23149
  }
22845
23150
  }
22846
- if (!media && (selector.trim().startsWith("@media") || selector.trim().startsWith("@container"))) {
23151
+ if (!media && isSupportedAtRule(selector.trim())) {
22847
23152
  media = selector.trim();
22848
23153
  selector = "&";
22849
23154
  }
23155
+ if (media) {
23156
+ const resolved = resolveMediaAtRulePlaceholders(media, (slotId) => decl.templateExpressions[slotId], {
23157
+ lookupImport: resolveImportInScope,
23158
+ resolveValue: state.resolveValue,
23159
+ resolveSelector,
23160
+ parseExpr,
23161
+ filePath: state.filePath,
23162
+ resolverImports
23163
+ });
23164
+ if (resolved === null) {
23165
+ state.markBail();
23166
+ warnings.push({
23167
+ severity: "warning",
23168
+ type: "Unsupported: media query contains unresolvable interpolation",
23169
+ loc: computeSelectorWarningLoc(decl.loc, decl.rawCss, rule.selector)
23170
+ });
23171
+ break;
23172
+ }
23173
+ if (resolved.kind === "static") media = resolved.value;
23174
+ else {
23175
+ resolvedSelectorMedia = {
23176
+ keyExpr: resolved.keyExpr,
23177
+ exprSource: ""
23178
+ };
23179
+ media = void 0;
23180
+ }
23181
+ }
22850
23182
  const parsedSelector = parseSelector(selector);
22851
23183
  if (parsedSelector.kind === "unsupported" && selector !== "&" && !rule.selector.includes("__SC_EXPR_")) {
22852
23184
  const elementAction = tryHandleElementSelector(selector, rule, decl);
@@ -23444,7 +23776,27 @@ function handleSiblingSelector(selector, rule, ctx) {
23444
23776
  return "break";
23445
23777
  }
23446
23778
  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"));
23779
+ let media = findSupportedAtRule(rule.atRuleStack);
23780
+ if (media) {
23781
+ const resolved = resolveMediaAtRulePlaceholders(media, (slotId) => decl.templateExpressions[slotId], {
23782
+ lookupImport: state.resolveImportInScope,
23783
+ resolveValue: state.resolveValue,
23784
+ resolveSelector: state.resolveSelector,
23785
+ parseExpr: state.parseExpr,
23786
+ filePath: state.filePath,
23787
+ resolverImports: state.resolverImports
23788
+ });
23789
+ if (resolved === null) {
23790
+ warnings.push({
23791
+ severity: "warning",
23792
+ type: "Unsupported: media query contains unresolvable interpolation",
23793
+ loc: computeSelectorWarningLoc(decl.loc, decl.rawCss, rule.selector)
23794
+ });
23795
+ return "break";
23796
+ }
23797
+ if (resolved.kind === "static") media = resolved.value;
23798
+ else media = void 0;
23799
+ }
23448
23800
  for (const [prop, value] of Object.entries(bucket)) {
23449
23801
  const siblingValue = media ? {
23450
23802
  default: null,
@@ -23646,7 +23998,7 @@ function finalizeDeclProcessing(ctx) {
23646
23998
  if (!v || typeof v !== "object" || Array.isArray(v) || isAstNode(v)) return false;
23647
23999
  const keys = Object.keys(v);
23648
24000
  if (keys.length === 0) return false;
23649
- return keys.includes("default") || keys.some((k) => k.startsWith(":") || k.startsWith("@media") || k.startsWith("::"));
24001
+ return keys.includes("default") || keys.some(isStyleConditionKey);
23650
24002
  };
23651
24003
  if (!(() => {
23652
24004
  const disabledBucket = variantBuckets.get("disabled");
@@ -24871,6 +25223,8 @@ function rewriteJsxStep(ctx) {
24871
25223
  const inlineVariantDimensions = decl.inlinedBaseComponent?.hasInlineJsxVariants ? decl.variantDimensions ?? [] : [];
24872
25224
  const inlineVariantByProp = new Map(inlineVariantDimensions.map((dimension) => [dimension.propName, dimension]));
24873
25225
  const inlineVariantProps = new Set(inlineVariantDimensions.map((dimension) => dimension.propName));
25226
+ const combinedStylePropNames = new Set((decl.callSiteCombinedStyles ?? []).flatMap((c) => c.propNames));
25227
+ const matchedCombinedStyleKey = matchCallSiteCombinedStyle(decl.callSiteCombinedStyles, opening.attributes ?? []);
24874
25228
  const attrs = opening.attributes ?? [];
24875
25229
  for (const attr of attrs) {
24876
25230
  if (attr.type !== "JSXAttribute") continue;
@@ -24898,6 +25252,7 @@ function rewriteJsxStep(ctx) {
24898
25252
  const n = attr.name.name;
24899
25253
  if (decl.shouldForwardProp.dropProps.includes(n)) {
24900
25254
  if (inlineVariantProps.has(n)) return true;
25255
+ if (decl.variantStyleKeys && n in decl.variantStyleKeys) return true;
24901
25256
  return false;
24902
25257
  }
24903
25258
  if (decl.shouldForwardProp.dropPrefix && n.startsWith(decl.shouldForwardProp.dropPrefix)) return false;
@@ -24981,10 +25336,11 @@ function rewriteJsxStep(ctx) {
24981
25336
  else extraMixinArgs.push(expr);
24982
25337
  }
24983
25338
  }
25339
+ const baseStyleKey = matchedCombinedStyleKey ?? decl.styleKey;
24984
25340
  const styleArgs = [
24985
25341
  ...decl.extendsStyleKey ? [j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(decl.extendsStyleKey))] : [],
24986
25342
  ...extraMixinArgs,
24987
- j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(decl.styleKey)),
25343
+ j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(baseStyleKey)),
24988
25344
  ...extraAfterBaseArgs
24989
25345
  ];
24990
25346
  const variantKeys = decl.variantStyleKeys ?? {};
@@ -25006,6 +25362,7 @@ function rewriteJsxStep(ctx) {
25006
25362
  if (valueExpr) for (const p of pairs) styleArgs.push(j.callExpression(j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(p.fnKey)), [valueExpr]));
25007
25363
  return;
25008
25364
  }
25365
+ if (combinedStylePropNames.has(n) && matchedCombinedStyleKey) return;
25009
25366
  const inlineVariantDimension = inlineVariantByProp.get(n);
25010
25367
  if (inlineVariantDimension) {
25011
25368
  const variantLookup = buildInlineVariantLookupFromAttr(j, inlineVariantDimension.variantObjectName, attr);
@@ -25028,9 +25385,19 @@ function rewriteJsxStep(ctx) {
25028
25385
  return;
25029
25386
  }
25030
25387
  if (attr.value.type === "JSXExpressionContainer") {
25388
+ const staticLiteralValue = readStaticJsxLiteral(attr);
25389
+ if (staticLiteralValue !== void 0 && staticLiteralValue) {
25390
+ styleArgs.push(j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(variantStyleKey)));
25391
+ return;
25392
+ }
25031
25393
  styleArgs.push(j.logicalExpression("&&", attr.value.expression, j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(variantStyleKey))));
25032
25394
  return;
25033
25395
  }
25396
+ if (attr.value.type === "StringLiteral" || attr.value.type === "NumericLiteral" || attr.value.type === "Literal") {
25397
+ const literalVal = readStaticJsxLiteral(attr);
25398
+ if (literalVal !== void 0 && literalVal) styleArgs.push(j.memberExpression(j.identifier(ctx.stylesIdentifier ?? "styles"), j.identifier(variantStyleKey)));
25399
+ return;
25400
+ }
25034
25401
  };
25035
25402
  for (const attr of leading) processAttr(attr, keptLeadingAfterVariants);
25036
25403
  for (const attr of rest) processAttr(attr, keptRestAfterVariants);
@@ -25087,6 +25454,22 @@ function extractJsxAttrValueExpr(j, attr) {
25087
25454
  if (a.value.type === "StringLiteral" || a.value.type === "Literal") return j.literal(a.value.value);
25088
25455
  if (a.value.type === "JSXExpressionContainer") return a.value.expression;
25089
25456
  }
25457
+ /**
25458
+ * Finds the combined style key matching the consumed props at a JSX call site.
25459
+ * Returns the style key if a matching combination exists, or undefined otherwise.
25460
+ */
25461
+ function matchCallSiteCombinedStyle(combinedStyles, attrs) {
25462
+ if (!combinedStyles?.length) return;
25463
+ const attrNames = /* @__PURE__ */ new Set();
25464
+ for (const a of attrs) {
25465
+ const attr = a;
25466
+ if (attr.type === "JSXAttribute" && attr.name?.type === "JSXIdentifier" && typeof attr.name.name === "string") attrNames.add(attr.name.name);
25467
+ }
25468
+ const presentPropNames = [...new Set(combinedStyles.flatMap((c) => c.propNames))].filter((p) => attrNames.has(p)).sort();
25469
+ if (presentPropNames.length === 0) return;
25470
+ const key = presentPropNames.join(",");
25471
+ return combinedStyles.find((c) => [...c.propNames].sort().join(",") === key)?.styleKey;
25472
+ }
25090
25473
 
25091
25474
  //#endregion
25092
25475
  //#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.21",
4
4
  "description": "Codemod to transform styled-components to StyleX",
5
5
  "keywords": [
6
6
  "codemod",