styled-components-to-stylex-codemod 0.0.2 → 0.0.3

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/README.md CHANGED
@@ -147,6 +147,46 @@ Adapters are the main extension point. They let you control:
147
147
  - what extra imports to inject into transformed files (returned from `resolveValue`)
148
148
  - how helper calls are resolved (via `resolveValue({ kind: "call", ... })`)
149
149
  - which exported components should support external className/style extension (`shouldSupportExternalStyling`)
150
+ - how className/style merging is handled for components accepting external styling (`styleMerger`)
151
+
152
+ #### Style Merger
153
+
154
+ When a component accepts external `className` and/or `style` props (e.g., via `shouldSupportExternalStyling`, or when wrapping a base component that already accepts these props), the generated code needs to merge StyleX styles with externally passed values.
155
+
156
+ > **Note:** Allowing external className/style props is generally discouraged in StyleX as it bypasses the type-safe styling system. However, it can be useful during migration to maintain compatibility with existing code that passes these props.
157
+
158
+ By default, this generates verbose inline merging code. You can provide a `styleMerger` to use a helper function instead for cleaner output:
159
+
160
+ ```ts
161
+ const adapter = defineAdapter({
162
+ resolveValue(ctx) {
163
+ // ... value resolution logic
164
+ return null;
165
+ },
166
+
167
+ shouldSupportExternalStyling(ctx) {
168
+ return ctx.filePath.includes("/shared/components/");
169
+ },
170
+
171
+ // Use a custom merger function for cleaner output
172
+ styleMerger: {
173
+ functionName: "mergedSx",
174
+ importSource: { kind: "specifier", value: "./lib/mergedSx" },
175
+ },
176
+ });
177
+ ```
178
+
179
+ The merger function should have this signature:
180
+
181
+ ```ts
182
+ function mergedSx(
183
+ styles: StyleXStyles,
184
+ className?: string,
185
+ style?: React.CSSProperties
186
+ ): ReturnType<typeof stylex.props>;
187
+ ```
188
+
189
+ See [`test-cases/lib/mergedSx.ts`](./test-cases/lib/mergedSx.ts) for a reference implementation.
150
190
 
151
191
  #### External Styles Support
152
192
 
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as defineAdapter, i as Adapter, r as TransformWarning } from "./transform-types-Cry4CAGJ.mjs";
1
+ import { a as defineAdapter, i as Adapter, r as TransformWarning } from "./transform-types-t2Vk9Bka.mjs";
2
2
 
3
3
  //#region src/internal/logger.d.ts
4
4
  interface CollectedWarning extends TransformWarning {
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as describeValue, i as assertValidAdapter, n as logWarning, t as flushWarnings } from "./logger-Ckk2VkqY.mjs";
1
+ import { a as describeValue, i as assertValidAdapter, n as logWarning, t as flushWarnings } from "./logger-Dhb8r1Ry.mjs";
2
2
  import { run } from "jscodeshift/src/Runner.js";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { dirname, join } from "node:path";
@@ -93,6 +93,7 @@ async function runTransform(options) {
93
93
  const adapter = options.adapter;
94
94
  assertValidAdapter(adapter, "runTransform(options)");
95
95
  const adapterWithLogging = {
96
+ styleMerger: adapter.styleMerger,
96
97
  shouldSupportExternalStyling(ctx) {
97
98
  return adapter.shouldSupportExternalStyling(ctx);
98
99
  },
@@ -1,5 +1,4 @@
1
1
  //#region src/internal/public-api-validation.ts
2
- const ADAPTER_DOCS_URL = `https://github.com/skovhus/styled-components-to-stylex-codemod#adapter`;
3
2
  function describeValue(value) {
4
3
  if (value === null) return "null";
5
4
  if (value === void 0) return "undefined";
@@ -52,11 +51,38 @@ function assertValidAdapter(candidate, where) {
52
51
  `Docs/examples: ${ADAPTER_DOCS_URL}`
53
52
  ].join("\n"));
54
53
  if (typeof shouldSupportExternalStyling !== "function") throw new Error([`[styled-components-to-stylex] ${where}: adapter.shouldSupportExternalStyling must be a function.`, `Received: shouldSupportExternalStyling=${describeValue(shouldSupportExternalStyling)}`].join("\n"));
54
+ const styleMerger = obj?.styleMerger;
55
+ if (styleMerger !== null && styleMerger !== void 0) {
56
+ if (typeof styleMerger !== "object") throw new Error([
57
+ `[styled-components-to-stylex] ${where}: adapter.styleMerger must be null or an object.`,
58
+ `Received: styleMerger=${describeValue(styleMerger)}`,
59
+ "",
60
+ "Expected shape:",
61
+ " {",
62
+ " functionName: \"stylexProps\",",
63
+ " importSource: { kind: \"specifier\", value: \"@company/ui-utils\" }",
64
+ " }"
65
+ ].join("\n"));
66
+ const { functionName, importSource } = styleMerger;
67
+ if (typeof functionName !== "string" || !functionName.trim()) throw new Error([`[styled-components-to-stylex] ${where}: adapter.styleMerger.functionName must be a non-empty string.`, `Received: functionName=${describeValue(functionName)}`].join("\n"));
68
+ if (!importSource || typeof importSource !== "object") throw new Error([
69
+ `[styled-components-to-stylex] ${where}: adapter.styleMerger.importSource must be an object.`,
70
+ `Received: importSource=${describeValue(importSource)}`,
71
+ "",
72
+ "Expected shape:",
73
+ " { kind: \"specifier\", value: \"@company/ui-utils\" }",
74
+ " or",
75
+ " { kind: \"absolutePath\", value: \"/path/to/module.ts\" }"
76
+ ].join("\n"));
77
+ const { kind, value } = importSource;
78
+ if (kind !== "specifier" && kind !== "absolutePath") throw new Error([`[styled-components-to-stylex] ${where}: adapter.styleMerger.importSource.kind must be "specifier" or "absolutePath".`, `Received: kind=${describeValue(kind)}`].join("\n"));
79
+ if (typeof value !== "string" || !value.trim()) throw new Error([`[styled-components-to-stylex] ${where}: adapter.styleMerger.importSource.value must be a non-empty string.`, `Received: value=${describeValue(value)}`].join("\n"));
80
+ }
55
81
  }
82
+ const ADAPTER_DOCS_URL = `https://github.com/skovhus/styled-components-to-stylex-codemod#adapter`;
56
83
 
57
84
  //#endregion
58
85
  //#region src/internal/logger.ts
59
- let collected = [];
60
86
  /**
61
87
  * Clear collected warnings and return them.
62
88
  */
@@ -84,6 +110,7 @@ function logWarnings(warnings, filePath) {
84
110
  logWarning(`[styled-components-to-stylex] Warning${warning.loc ? ` (${filePath}:${warning.loc.line}:${warning.loc.column})` : ` (${filePath})`}: ${warning.message}\n`);
85
111
  }
86
112
  }
113
+ let collected = [];
87
114
 
88
115
  //#endregion
89
116
  export { describeValue as a, assertValidAdapter as i, logWarning as n, logWarnings as r, flushWarnings as t };
@@ -87,6 +87,26 @@ interface ExternalStylesContext {
87
87
  /** Whether it's a default export */
88
88
  isDefaultExport: boolean;
89
89
  }
90
+ /**
91
+ * Configuration for a custom style merger function that combines stylex.props()
92
+ * results with external className/style props.
93
+ *
94
+ * When configured, generates cleaner output:
95
+ * `{...stylexProps(styles.foo, className, style)}`
96
+ * instead of the verbose pattern:
97
+ * `{...sx} className={[sx.className, className].filter(Boolean).join(" ")} style={{...sx.style, ...style}}`
98
+ */
99
+ interface StyleMergerConfig {
100
+ /**
101
+ * Function name to use for merging (e.g., "stylexProps" or "mergeStylexProps").
102
+ */
103
+ functionName: string;
104
+ /**
105
+ * Import source for the merger function.
106
+ * Example: `{ kind: "specifier", value: "@company/ui-utils" }`
107
+ */
108
+ importSource: ImportSource;
109
+ }
90
110
  interface Adapter {
91
111
  /** Unified resolver for theme paths + CSS variables. Return null to leave unresolved. */
92
112
  resolveValue: (context: ResolveContext) => ResolveResult | null;
@@ -96,6 +116,22 @@ interface Adapter {
96
116
  * className/style/rest merging.
97
117
  */
98
118
  shouldSupportExternalStyling: (context: ExternalStylesContext) => boolean;
119
+ /**
120
+ * Custom merger function for className/style combining.
121
+ * When provided, generates cleaner output using this function instead of
122
+ * the verbose className/style merging pattern.
123
+ * Set to `null` to use the verbose pattern (default).
124
+ *
125
+ * Expected merger function signature:
126
+ * ```typescript
127
+ * function merger(
128
+ * styles: StyleXStyles | StyleXStyles[],
129
+ * className?: string,
130
+ * style?: React.CSSProperties
131
+ * ): { className?: string; style?: React.CSSProperties }
132
+ * ```
133
+ */
134
+ styleMerger: StyleMergerConfig | null;
99
135
  }
100
136
  /**
101
137
  * Helper for nicer user authoring + type inference.
@@ -1,4 +1,4 @@
1
- import { n as TransformResult, r as TransformWarning, t as TransformOptions } from "./transform-types-Cry4CAGJ.mjs";
1
+ import { n as TransformResult, r as TransformWarning, t as TransformOptions } from "./transform-types-t2Vk9Bka.mjs";
2
2
  import { API, FileInfo, Options } from "jscodeshift";
3
3
 
4
4
  //#region src/transform.d.ts