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

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-B_bwk2gd.mjs";
1
+ import { a as defineAdapter, i as AdapterInput, t as CollectedWarning } from "./logger-DKOqU7as.mjs";
2
2
 
3
3
  //#region src/run.d.ts
4
4
  interface RunTransformOptions {
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as assertValidAdapterInput, o as describeValue, r as defineAdapter, t as Logger } from "./logger-StI3W9gi.mjs";
1
+ import { o as assertValidAdapterInput, r as defineAdapter, s as describeValue, t as Logger } from "./logger-Bt4qdjjb.mjs";
2
2
  import { run } from "jscodeshift/src/Runner.js";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { dirname, join, resolve } from "node:path";
@@ -143,7 +143,7 @@ async function runTransform(options) {
143
143
  ].join("\n"));
144
144
  const { createModuleResolver } = await import("./resolve-imports-CN7ulI4X.mjs");
145
145
  const sharedResolver = createModuleResolver();
146
- const { runPrepass } = await import("./run-prepass-Dl49r3ug.mjs");
146
+ const { runPrepass } = await import("./run-prepass-BInfVTeK.mjs");
147
147
  const absoluteFiles = filePaths.map((f) => resolve(f));
148
148
  const absoluteConsumers = consumerFilePaths.map((f) => resolve(f));
149
149
  let prepassResult;
@@ -182,6 +182,12 @@ const ADAPTER_DOCS_URL = `https://github.com/skovhus/styled-components-to-stylex
182
182
  * Adapter entry point for customizing the codemod.
183
183
  * Core concepts: value resolution hooks and adapter validation.
184
184
  */
185
+ /**
186
+ * Type guard: checks whether a resolve result is a directional expansion.
187
+ */
188
+ function isDirectionalResult(r) {
189
+ return "directional" in r;
190
+ }
185
191
  const DEFAULT_THEME_HOOK = {
186
192
  functionName: "useTheme",
187
193
  importSource: {
@@ -199,6 +205,15 @@ const DEFAULT_THEME_HOOK = {
199
205
  * export default defineAdapter({
200
206
  * resolveValue(ctx) {
201
207
  * if (ctx.kind === "theme") {
208
+ * // For shorthand properties with multi-value tokens, return directional entries
209
+ * if (ctx.cssProperty === "padding" && ctx.path === "input.padding") {
210
+ * return {
211
+ * directional: [
212
+ * { prop: "paddingBlock", expr: "$input.paddingBlock", imports: [{ from: { kind: "specifier", value: "./tokens" }, names: [{ imported: "$input" }] }] },
213
+ * { prop: "paddingInline", expr: "$input.paddingInline", imports: [{ from: { kind: "specifier", value: "./tokens" }, names: [{ imported: "$input" }] }] },
214
+ * ],
215
+ * };
216
+ * }
202
217
  * return {
203
218
  * expr: `tokens.${ctx.path}`,
204
219
  * imports: [
@@ -486,4 +501,4 @@ const SECTION_COLOR = "\x1B[36m";
486
501
  const RESET_COLOR = "\x1B[0m";
487
502
 
488
503
  //#endregion
489
- export { assertValidAdapterInput as a, assertValidAdapter as i, DEFAULT_THEME_HOOK as n, describeValue as o, defineAdapter as r, Logger as t };
504
+ export { assertValidAdapter as a, isDirectionalResult as i, DEFAULT_THEME_HOOK as n, assertValidAdapterInput as o, defineAdapter as r, describeValue as s, Logger as t };
@@ -19,6 +19,12 @@ type ThemeResolveContext = {
19
19
  line: number;
20
20
  column: number;
21
21
  };
22
+ /**
23
+ * CSS property being set (when available).
24
+ * Useful for adapters to return directional results for shorthand properties.
25
+ * Example: "padding", "margin", "border"
26
+ */
27
+ cssProperty?: string;
22
28
  };
23
29
  type CssVariableResolveContext = {
24
30
  kind: "cssVariable";
@@ -68,6 +74,12 @@ type ImportedValueResolveContext = {
68
74
  line: number;
69
75
  column: number;
70
76
  };
77
+ /**
78
+ * CSS property being set (when available).
79
+ * Useful for adapters to return directional results for shorthand properties.
80
+ * Example: "padding", "margin", "border"
81
+ */
82
+ cssProperty?: string;
71
83
  };
72
84
  type CallResolveContext = {
73
85
  /**
@@ -133,6 +145,21 @@ type CallResolveContext = {
133
145
  * Helper calls are handled separately via `adapter.resolveCall(...)`.
134
146
  */
135
147
  type ResolveValueContext = ThemeResolveContext | CssVariableResolveContext | ImportedValueResolveContext;
148
+ /**
149
+ * Result for `adapter.resolveValue(...)` when returning directional expansion
150
+ * instead of a single value. Use this for shorthand CSS properties (e.g., `padding`)
151
+ * whose theme token resolves to a multi-value string (e.g., `"6px 12px"`).
152
+ *
153
+ * Instead of assigning the opaque token to the shorthand (which StyleX would expand
154
+ * incorrectly to all longhands), the adapter returns pre-split directional entries.
155
+ */
156
+ type ResolveValueDirectionalResult = {
157
+ /** Directional expansion entries — use instead of shorthand property */directional: Array<{
158
+ /** camelCase CSS property (e.g., "paddingBlock", "paddingInline") */prop: string; /** JS expression string to inline into generated output */
159
+ expr: string; /** Import statements required by `expr` */
160
+ imports: ImportSpec[];
161
+ }>;
162
+ };
136
163
  /**
137
164
  * Result for `adapter.resolveValue(...)` (theme + css variables + imported values).
138
165
  */
@@ -423,7 +450,11 @@ interface ExternalInterfaceContext {
423
450
  type ExternalInterfaceResult = {
424
451
  styles: boolean;
425
452
  as: boolean;
426
- ref: boolean;
453
+ ref: boolean; /** Whether cross-file consumers pass className prop (undefined → derive from `styles`) */
454
+ className?: boolean; /** Whether cross-file consumers pass style prop (undefined → derive from `styles`) */
455
+ style?: boolean; /** Whether cross-file consumers pass element-specific props (onClick, aria-*, etc.) */
456
+ elementProps?: boolean; /** Whether cross-file consumers use JSX spread ({...props}) */
457
+ spreadProps?: boolean;
427
458
  };
428
459
  /**
429
460
  * Configuration for a custom style merger function that combines stylex.props()
@@ -470,10 +501,12 @@ interface Adapter {
470
501
  *
471
502
  * Return:
472
503
  * - `{ expr, imports }` for theme, css variables, and imported values.
504
+ * - `{ directional: [...] }` for shorthand properties (e.g., `padding`) whose token
505
+ * resolves to a multi-value string. Each entry specifies a longhand property and expression.
473
506
  * - Optionally return `{ dropDefinition: true }` for css variables to remove the local `--x: ...` definition.
474
507
  * - `undefined` to bail/skip the file (for cssVariable: keeps the original `var(...)` unchanged)
475
508
  */
476
- resolveValue: (context: ResolveValueContext) => ResolveValueResult | undefined;
509
+ resolveValue: (context: ResolveValueContext) => ResolveValueResult | ResolveValueDirectionalResult | undefined;
477
510
  /**
478
511
  * Resolver for helper calls found inside template interpolations.
479
512
  *
@@ -594,6 +627,15 @@ interface AdapterInput {
594
627
  * export default defineAdapter({
595
628
  * resolveValue(ctx) {
596
629
  * if (ctx.kind === "theme") {
630
+ * // For shorthand properties with multi-value tokens, return directional entries
631
+ * if (ctx.cssProperty === "padding" && ctx.path === "input.padding") {
632
+ * return {
633
+ * directional: [
634
+ * { prop: "paddingBlock", expr: "$input.paddingBlock", imports: [{ from: { kind: "specifier", value: "./tokens" }, names: [{ imported: "$input" }] }] },
635
+ * { prop: "paddingInline", expr: "$input.paddingInline", imports: [{ from: { kind: "specifier", value: "./tokens" }, names: [{ imported: "$input" }] }] },
636
+ * ],
637
+ * };
638
+ * }
597
639
  * return {
598
640
  * expr: `tokens.${ctx.path}`,
599
641
  * imports: [
@@ -648,7 +690,7 @@ declare function defineAdapter<T extends AdapterInput>(adapter: T): T;
648
690
  //#endregion
649
691
  //#region src/internal/logger.d.ts
650
692
  type Severity = "info" | "warning" | "error";
651
- 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" | "Resolved conditional border variant 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: cross-file component selector target has no JSX usage in this file" | "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" | "Transient $-prefixed props renamed on exported component — update consumer call sites to use the new prop names";
693
+ 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" | "Resolved conditional border variant 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: cross-file component selector target has no JSX usage in this file" | "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" | "Transient $-prefixed props renamed on exported component — update consumer call sites to use the new prop names" | "Shorthand property has an opaque value that StyleX will expand to longhands — use `directional` in resolveValue to return separate longhand tokens";
652
694
  interface WarningLog {
653
695
  severity: Severity;
654
696
  type: WarningType;
@@ -527,6 +527,10 @@ async function runPrepass(options) {
527
527
  const styledCallUsages = [];
528
528
  const styledDefFiles = /* @__PURE__ */ new Map();
529
529
  const classNameStyleUsages = /* @__PURE__ */ new Map();
530
+ const classNameUsages = /* @__PURE__ */ new Map();
531
+ const styleUsages = /* @__PURE__ */ new Map();
532
+ const elementPropUsages = /* @__PURE__ */ new Map();
533
+ const spreadPropUsages = /* @__PURE__ */ new Map();
530
534
  const styledWrapperUsages = [];
531
535
  const fileContents = /* @__PURE__ */ new Map();
532
536
  const cachedRead = (filePath) => {
@@ -598,12 +602,21 @@ async function runPrepass(options) {
598
602
  for (const names of styledDefFiles.values()) for (const name of names) allStyledNames.add(name);
599
603
  if (allStyledNames.size > 0) {
600
604
  const rgHits = rgClassNameStyleFilter(uniqueAllFiles);
601
- for (const [filePath, source] of fileContents) for (const name of scanClassNameStyleUsages(source, allStyledNames)) addToSetMap(classNameStyleUsages, name, filePath);
605
+ const scanAndRecord = (filePath, source) => {
606
+ for (const result of scanConsumerProps(source, allStyledNames)) {
607
+ addToSetMap(classNameStyleUsages, result.name, filePath);
608
+ if (result.className) addToSetMap(classNameUsages, result.name, filePath);
609
+ if (result.style) addToSetMap(styleUsages, result.name, filePath);
610
+ if (result.elementProps) addToSetMap(elementPropUsages, result.name, filePath);
611
+ if (result.spreadProps) addToSetMap(spreadPropUsages, result.name, filePath);
612
+ }
613
+ };
614
+ for (const [filePath, source] of fileContents) scanAndRecord(filePath, source);
602
615
  const filesToScan = rgHits ? [...rgHits].filter((f) => allFilesSet.has(f) && !fileContents.has(f)) : uniqueAllFiles.filter((f) => !fileContents.has(f));
603
616
  for (const filePath of filesToScan) {
604
617
  const source = cachedRead(filePath);
605
618
  if (!source) continue;
606
- for (const name of scanClassNameStyleUsages(source, allStyledNames)) addToSetMap(classNameStyleUsages, name, filePath);
619
+ scanAndRecord(filePath, source);
607
620
  }
608
621
  }
609
622
  }
@@ -617,7 +630,11 @@ async function runPrepass(options) {
617
630
  entry = {
618
631
  styles: false,
619
632
  as: false,
620
- ref: false
633
+ ref: false,
634
+ className: false,
635
+ style: false,
636
+ elementProps: false,
637
+ spreadProps: false
621
638
  };
622
639
  consumerAnalysis.set(key, entry);
623
640
  }
@@ -645,6 +662,10 @@ async function runPrepass(options) {
645
662
  matchUsagesToDefinitions(asUsages, "as");
646
663
  matchUsagesToDefinitions(refUsages, "ref");
647
664
  matchUsagesToDefinitions(classNameStyleUsages, "styles");
665
+ matchUsagesToDefinitions(classNameUsages, "className");
666
+ matchUsagesToDefinitions(styleUsages, "style");
667
+ matchUsagesToDefinitions(elementPropUsages, "elementProps");
668
+ matchUsagesToDefinitions(spreadPropUsages, "spreadProps");
648
669
  {
649
670
  const seen = /* @__PURE__ */ new Set();
650
671
  for (const { file, name } of styledCallUsages) {
@@ -662,7 +683,10 @@ async function runPrepass(options) {
662
683
  } catch {
663
684
  continue;
664
685
  }
665
- ensure(defFile, exportedName).styles = true;
686
+ const entry = ensure(defFile, exportedName);
687
+ entry.styles = true;
688
+ entry.elementProps = true;
689
+ entry.spreadProps = true;
666
690
  }
667
691
  }
668
692
  }
@@ -765,30 +789,65 @@ function buildLocalToImportedMap(source) {
765
789
  }
766
790
  return map;
767
791
  }
792
+ /** Props that don't indicate element-specific usage (non-element props). */
793
+ const KNOWN_NON_ELEMENT_PROPS = new Set([
794
+ "className",
795
+ "style",
796
+ "as",
797
+ "ref",
798
+ "forwardedAs",
799
+ "key",
800
+ "children"
801
+ ]);
768
802
  /**
769
- * Scan source for JSX usage of specific components with className or style props.
803
+ * Scan source for JSX usage of specific components with className, style,
804
+ * element-specific props, or JSX spread.
770
805
  * Uses a two-step approach: first quick-checks for className/style keywords,
771
806
  * then scans JSX open tags to match component names — avoids building a huge
772
807
  * alternation regex when there are hundreds of component names.
773
808
  * Handles aliased imports (e.g., `import { Alert as MyAlert }`) by resolving
774
809
  * local tag names back to their original exported names.
775
810
  */
776
- function scanClassNameStyleUsages(source, componentNames) {
811
+ function scanConsumerProps(source, componentNames) {
777
812
  if (!CLASSNAME_STYLE_QUICK_RE.test(source)) return [];
778
813
  let aliasMap;
779
- const matches = [];
780
- for (const m of source.matchAll(/<([A-Z][A-Za-z0-9]*)\b[^<>]*\b(?:className|style)\s*[={]/gs)) {
814
+ const resultMap = /* @__PURE__ */ new Map();
815
+ for (const m of source.matchAll(/<([A-Z][A-Za-z0-9]*)\b([^<>]*?)(?:\/>|>)/gs)) {
781
816
  const tagName = m[1];
817
+ const attrText = m[2] ?? "";
782
818
  if (!tagName) continue;
783
- if (componentNames.has(tagName)) {
784
- matches.push(tagName);
785
- continue;
819
+ let resolvedName;
820
+ if (componentNames.has(tagName)) resolvedName = tagName;
821
+ else {
822
+ aliasMap ??= buildLocalToImportedMap(source);
823
+ const originalName = aliasMap.get(tagName);
824
+ if (originalName && componentNames.has(originalName)) resolvedName = originalName;
825
+ }
826
+ if (!resolvedName) continue;
827
+ if (!/\b(?:className|style)\s*[={]/.test(attrText) && !/\{\.\.\./.test(attrText)) continue;
828
+ let entry = resultMap.get(resolvedName);
829
+ if (!entry) {
830
+ entry = {
831
+ name: resolvedName,
832
+ className: false,
833
+ style: false,
834
+ elementProps: false,
835
+ spreadProps: false
836
+ };
837
+ resultMap.set(resolvedName, entry);
838
+ }
839
+ if (/\bclassName\s*[={]/.test(attrText)) entry.className = true;
840
+ if (/\bstyle\s*[={]/.test(attrText)) entry.style = true;
841
+ if (/\{\.\.\./.test(attrText)) entry.spreadProps = true;
842
+ for (const pm of attrText.matchAll(/\b([a-z][a-zA-Z-]*)(?=\s*[={]|\s+[a-z]|\s*$)/gi)) {
843
+ const propName = pm[1];
844
+ if (!KNOWN_NON_ELEMENT_PROPS.has(propName) && !propName.startsWith("$")) {
845
+ entry.elementProps = true;
846
+ break;
847
+ }
786
848
  }
787
- aliasMap ??= buildLocalToImportedMap(source);
788
- const originalName = aliasMap.get(tagName);
789
- if (originalName && componentNames.has(originalName)) matches.push(originalName);
790
849
  }
791
- return matches;
850
+ return [...resultMap.values()];
792
851
  }
793
852
  /**
794
853
  * Use ripgrep to find files containing `className` or `style` props.
@@ -1,4 +1,4 @@
1
- import { n as WarningLog, r as Adapter } from "./logger-B_bwk2gd.mjs";
1
+ import { n as WarningLog, r as Adapter } from "./logger-DKOqU7as.mjs";
2
2
  import "stylis";
3
3
  import { API, FileInfo, Options } from "jscodeshift";
4
4