styled-components-to-stylex-codemod 0.0.39 → 0.0.41
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/{compute-leaf-set-Drcu2eju.mjs → compute-leaf-set-Be5Cgxnr.mjs} +6 -3
- package/dist/{extract-external-interface-CdHbvfxu.mjs → extract-external-interface-B61N1a6q.mjs} +36 -15
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +14 -9
- package/dist/{resolve-imports-BlxKezSJ.mjs → resolve-imports-DgSAddIF.mjs} +1 -0
- package/dist/{run-prepass-qEr_Mc3y.mjs → run-prepass-J4t6jkCI.mjs} +214 -101
- package/dist/{transform-types-DJpFQ5xm.d.mts → transform-types-Bl-oPkjl.d.mts} +2 -2
- package/dist/transform.d.mts +1 -1
- package/dist/transform.mjs +4019 -1298
- package/dist/wrapped-component-interface-yvZ-_HMO.mjs +880 -0
- package/package.json +4 -4
- package/dist/merge-markers-BC5YNB7D.mjs +0 -392
|
@@ -0,0 +1,880 @@
|
|
|
1
|
+
import { t as createModuleResolver } from "./resolve-imports-DgSAddIF.mjs";
|
|
2
|
+
import { n as toRealPath } from "./path-utils-BIpoL4Ue.mjs";
|
|
3
|
+
import jscodeshift from "jscodeshift";
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
//#region src/internal/public-api-validation.ts
|
|
6
|
+
function describeValue(value) {
|
|
7
|
+
if (value === null) return "null";
|
|
8
|
+
if (value === void 0) return "undefined";
|
|
9
|
+
if (Array.isArray(value)) return `Array(${value.length})`;
|
|
10
|
+
if (typeof value === "string") return `"${value}"`;
|
|
11
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
12
|
+
if (typeof value === "bigint") return value.toString();
|
|
13
|
+
if (typeof value === "symbol") return value.description ? `Symbol(${value.description})` : "Symbol()";
|
|
14
|
+
if (typeof value === "function") return "[Function]";
|
|
15
|
+
if (typeof value === "object") {
|
|
16
|
+
const ctor = value?.constructor?.name ?? "Object";
|
|
17
|
+
let keys = [];
|
|
18
|
+
try {
|
|
19
|
+
keys = Object.keys(value);
|
|
20
|
+
} catch {}
|
|
21
|
+
const preview = keys.slice(0, 5).join(", ");
|
|
22
|
+
const suffix = keys.length > 5 ? ", ..." : "";
|
|
23
|
+
return keys.length ? `${ctor} { ${preview}${suffix} }` : ctor;
|
|
24
|
+
}
|
|
25
|
+
return "[Unknown]";
|
|
26
|
+
}
|
|
27
|
+
/** Validates that the candidate is a fully-resolved `Adapter` (externalInterface must be a function). */
|
|
28
|
+
function assertValidAdapter(candidate, where) {
|
|
29
|
+
assertAdapterShape(candidate, where, false);
|
|
30
|
+
}
|
|
31
|
+
/** Validates that the candidate is a valid `AdapterInput` (externalInterface may be `"auto"` or a function). */
|
|
32
|
+
function assertValidAdapterInput(candidate, where) {
|
|
33
|
+
assertAdapterShape(candidate, where, true);
|
|
34
|
+
}
|
|
35
|
+
function assertAdapterShape(candidate, where, allowAutoExtIf) {
|
|
36
|
+
const obj = candidate;
|
|
37
|
+
const resolveValue = obj?.resolveValue;
|
|
38
|
+
const resolveCall = obj?.resolveCall;
|
|
39
|
+
const resolveSelector = obj?.resolveSelector;
|
|
40
|
+
const resolveBaseComponent = obj?.resolveBaseComponent;
|
|
41
|
+
const externalInterface = obj?.externalInterface;
|
|
42
|
+
if (!candidate || typeof candidate !== "object") throw new Error([
|
|
43
|
+
`${where}: expected an adapter object.`,
|
|
44
|
+
`Received: ${describeValue(candidate)}`,
|
|
45
|
+
"",
|
|
46
|
+
"Adapter requirements:",
|
|
47
|
+
" - adapter.resolveValue(context) is required",
|
|
48
|
+
" - adapter.resolveCall(context) is required",
|
|
49
|
+
" - adapter.resolveSelector(context) is required",
|
|
50
|
+
" - adapter.externalInterface(context) is required",
|
|
51
|
+
"",
|
|
52
|
+
"resolveValue(context) is called with one of these shapes:",
|
|
53
|
+
" - { kind: \"theme\", path }",
|
|
54
|
+
" - { kind: \"cssVariable\", name, fallback?, definedValue? }",
|
|
55
|
+
" - { kind: \"importedValue\", importedName, source, path? }",
|
|
56
|
+
"",
|
|
57
|
+
"resolveCall(context) is called with:",
|
|
58
|
+
" - { callSiteFilePath, calleeImportedName, calleeSource, args }",
|
|
59
|
+
"",
|
|
60
|
+
"resolveSelector(context) is called with:",
|
|
61
|
+
" - { kind: \"selectorInterpolation\", importedName, source, path? }",
|
|
62
|
+
" - { kind: \"mediaQueryInterpolation\", importedName, source, path?, mediaQuery }",
|
|
63
|
+
"",
|
|
64
|
+
`Docs/examples: ${ADAPTER_DOCS_URL}`
|
|
65
|
+
].join("\n"));
|
|
66
|
+
if (typeof resolveValue !== "function") throw new Error([
|
|
67
|
+
`${where}: adapter.resolveValue must be a function.`,
|
|
68
|
+
`Received: resolveValue=${describeValue(resolveValue)}`,
|
|
69
|
+
"",
|
|
70
|
+
"Adapter shape:",
|
|
71
|
+
" {",
|
|
72
|
+
" resolveValue(context) {",
|
|
73
|
+
" // theme/cssVariable -> { expr, imports, dropDefinition? } | null",
|
|
74
|
+
" }",
|
|
75
|
+
" resolveCall(context) { return { expr, imports } | null }",
|
|
76
|
+
" }",
|
|
77
|
+
"",
|
|
78
|
+
`Docs/examples: ${ADAPTER_DOCS_URL}`
|
|
79
|
+
].join("\n"));
|
|
80
|
+
if (typeof resolveCall !== "function") throw new Error([
|
|
81
|
+
`${where}: adapter.resolveCall must be a function.`,
|
|
82
|
+
`Received: resolveCall=${describeValue(resolveCall)}`,
|
|
83
|
+
"",
|
|
84
|
+
"Adapter shape:",
|
|
85
|
+
" {",
|
|
86
|
+
" resolveCall(context) { return { expr: string, imports: ImportSpec[] } | null }",
|
|
87
|
+
" }",
|
|
88
|
+
"",
|
|
89
|
+
`Docs/examples: ${ADAPTER_DOCS_URL}`
|
|
90
|
+
].join("\n"));
|
|
91
|
+
if (typeof resolveSelector !== "function") throw new Error([
|
|
92
|
+
`${where}: adapter.resolveSelector must be a function.`,
|
|
93
|
+
`Received: resolveSelector=${describeValue(resolveSelector)}`,
|
|
94
|
+
"",
|
|
95
|
+
"Adapter shape:",
|
|
96
|
+
" {",
|
|
97
|
+
" resolveSelector(context) { return { kind: \"media\" | \"pseudoAlias\" | \"pseudoExpand\", ... } | undefined }",
|
|
98
|
+
" }",
|
|
99
|
+
"",
|
|
100
|
+
`Docs/examples: ${ADAPTER_DOCS_URL}`
|
|
101
|
+
].join("\n"));
|
|
102
|
+
if (resolveBaseComponent !== void 0 && typeof resolveBaseComponent !== "function") throw new Error([
|
|
103
|
+
`${where}: adapter.resolveBaseComponent must be a function when provided.`,
|
|
104
|
+
`Received: resolveBaseComponent=${describeValue(resolveBaseComponent)}`,
|
|
105
|
+
"",
|
|
106
|
+
"Adapter shape:",
|
|
107
|
+
" {",
|
|
108
|
+
" resolveBaseComponent(context) {",
|
|
109
|
+
" return { tagName, consumedProps, sx?, mixins? } | undefined",
|
|
110
|
+
" }",
|
|
111
|
+
" }",
|
|
112
|
+
"",
|
|
113
|
+
`Docs/examples: ${ADAPTER_DOCS_URL}`
|
|
114
|
+
].join("\n"));
|
|
115
|
+
if (!(typeof externalInterface === "function" || allowAutoExtIf && externalInterface === "auto")) throw new Error([`${where}: ${allowAutoExtIf ? "adapter.externalInterface must be a function or \"auto\"." : "adapter.externalInterface must be a function."}`, `Received: externalInterface=${describeValue(externalInterface)}`].join("\n"));
|
|
116
|
+
const styleMerger = obj?.styleMerger;
|
|
117
|
+
if (styleMerger !== null && styleMerger !== void 0) {
|
|
118
|
+
if (typeof styleMerger !== "object") throw new Error([
|
|
119
|
+
`${where}: adapter.styleMerger must be null or an object.`,
|
|
120
|
+
`Received: styleMerger=${describeValue(styleMerger)}`,
|
|
121
|
+
"",
|
|
122
|
+
"Expected shape:",
|
|
123
|
+
" {",
|
|
124
|
+
" functionName: \"stylexProps\",",
|
|
125
|
+
" importSource: { kind: \"specifier\", value: \"@company/ui-utils\" }",
|
|
126
|
+
" }"
|
|
127
|
+
].join("\n"));
|
|
128
|
+
const { functionName, importSource } = styleMerger;
|
|
129
|
+
assertFunctionNameAndImportSource({
|
|
130
|
+
where,
|
|
131
|
+
configPath: "adapter.styleMerger",
|
|
132
|
+
functionName,
|
|
133
|
+
importSource,
|
|
134
|
+
specifierExample: "@company/ui-utils",
|
|
135
|
+
absolutePathExample: "/path/to/module.ts"
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
const markerFile = obj?.markerFile;
|
|
139
|
+
if (markerFile !== void 0 && markerFile !== null && typeof markerFile !== "function") throw new Error([
|
|
140
|
+
`${where}: adapter.markerFile must be a function when provided.`,
|
|
141
|
+
`Received: markerFile=${describeValue(markerFile)}`,
|
|
142
|
+
"",
|
|
143
|
+
"Expected signature:",
|
|
144
|
+
" markerFile(ctx: { filePath: string }) => { kind: \"specifier\" | \"absolutePath\", value: string }"
|
|
145
|
+
].join("\n"));
|
|
146
|
+
const wrappedComponentInterface = obj?.wrappedComponentInterface;
|
|
147
|
+
if (wrappedComponentInterface !== void 0 && wrappedComponentInterface !== null && typeof wrappedComponentInterface !== "function") throw new Error([
|
|
148
|
+
`${where}: adapter.wrappedComponentInterface must be a function when provided.`,
|
|
149
|
+
`Received: wrappedComponentInterface=${describeValue(wrappedComponentInterface)}`,
|
|
150
|
+
"",
|
|
151
|
+
"Expected signature:",
|
|
152
|
+
" wrappedComponentInterface(ctx: { localName: string; importSource: string; importedName: string; filePath: string })",
|
|
153
|
+
" => { acceptsSx: boolean } | undefined"
|
|
154
|
+
].join("\n"));
|
|
155
|
+
const themeHook = obj?.themeHook;
|
|
156
|
+
if (themeHook !== null && themeHook !== void 0) {
|
|
157
|
+
if (typeof themeHook !== "object") throw new Error([
|
|
158
|
+
`${where}: adapter.themeHook must be an object when provided.`,
|
|
159
|
+
`Received: themeHook=${describeValue(themeHook)}`,
|
|
160
|
+
"",
|
|
161
|
+
"Expected shape:",
|
|
162
|
+
" {",
|
|
163
|
+
" functionName: \"useTheme\",",
|
|
164
|
+
" importSource: { kind: \"specifier\", value: \"@company/theme-hooks\" }",
|
|
165
|
+
" }"
|
|
166
|
+
].join("\n"));
|
|
167
|
+
const { functionName, importSource } = themeHook;
|
|
168
|
+
assertFunctionNameAndImportSource({
|
|
169
|
+
where,
|
|
170
|
+
configPath: "adapter.themeHook",
|
|
171
|
+
functionName,
|
|
172
|
+
importSource,
|
|
173
|
+
specifierExample: "@company/theme-hooks",
|
|
174
|
+
absolutePathExample: "/path/to/theme-hooks.ts"
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function assertFunctionNameAndImportSource(args) {
|
|
179
|
+
const { where, configPath, functionName, importSource, specifierExample, absolutePathExample } = args;
|
|
180
|
+
if (typeof functionName !== "string" || !functionName.trim()) throw new Error([`${where}: ${configPath}.functionName must be a non-empty string.`, `Received: functionName=${describeValue(functionName)}`].join("\n"));
|
|
181
|
+
if (!importSource || typeof importSource !== "object") throw new Error([
|
|
182
|
+
`${where}: ${configPath}.importSource must be an object.`,
|
|
183
|
+
`Received: importSource=${describeValue(importSource)}`,
|
|
184
|
+
"",
|
|
185
|
+
"Expected shape:",
|
|
186
|
+
` { kind: "specifier", value: "${specifierExample}" }`,
|
|
187
|
+
" or",
|
|
188
|
+
` { kind: "absolutePath", value: "${absolutePathExample}" }`
|
|
189
|
+
].join("\n"));
|
|
190
|
+
const { kind, value } = importSource;
|
|
191
|
+
if (kind !== "specifier" && kind !== "absolutePath") throw new Error([`${where}: ${configPath}.importSource.kind must be "specifier" or "absolutePath".`, `Received: kind=${describeValue(kind)}`].join("\n"));
|
|
192
|
+
if (typeof value !== "string" || !value.trim()) throw new Error([`${where}: ${configPath}.importSource.value must be a non-empty string.`, `Received: value=${describeValue(value)}`].join("\n"));
|
|
193
|
+
}
|
|
194
|
+
const ADAPTER_DOCS_URL = `https://github.com/skovhus/styled-components-to-stylex-codemod#adapter`;
|
|
195
|
+
//#endregion
|
|
196
|
+
//#region src/adapter.ts
|
|
197
|
+
/**
|
|
198
|
+
* Adapter entry point for customizing the codemod.
|
|
199
|
+
* Core concepts: value resolution hooks and adapter validation.
|
|
200
|
+
*/
|
|
201
|
+
/**
|
|
202
|
+
* Type guard: checks whether a resolve result is a directional expansion.
|
|
203
|
+
*/
|
|
204
|
+
function isDirectionalResult(r) {
|
|
205
|
+
return "directional" in r;
|
|
206
|
+
}
|
|
207
|
+
const DEFAULT_THEME_HOOK = {
|
|
208
|
+
functionName: "useTheme",
|
|
209
|
+
importSource: {
|
|
210
|
+
kind: "specifier",
|
|
211
|
+
value: "styled-components"
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
/**
|
|
215
|
+
* Helper for nicer user authoring + type inference.
|
|
216
|
+
*
|
|
217
|
+
* `defineAdapter(...)` also performs runtime validation (helpful for JS consumers)
|
|
218
|
+
* and will throw a descriptive error message if the adapter shape is invalid.
|
|
219
|
+
*
|
|
220
|
+
* Usage:
|
|
221
|
+
* export default defineAdapter({
|
|
222
|
+
* resolveValue(ctx) {
|
|
223
|
+
* if (ctx.kind === "theme") {
|
|
224
|
+
* // For shorthand properties with multi-value tokens, return directional entries
|
|
225
|
+
* if (ctx.cssProperty === "padding" && ctx.path === "input.padding") {
|
|
226
|
+
* return {
|
|
227
|
+
* directional: [
|
|
228
|
+
* { prop: "paddingBlock", expr: "$input.paddingBlock", imports: [{ from: { kind: "specifier", value: "./tokens" }, names: [{ imported: "$input" }] }] },
|
|
229
|
+
* { prop: "paddingInline", expr: "$input.paddingInline", imports: [{ from: { kind: "specifier", value: "./tokens" }, names: [{ imported: "$input" }] }] },
|
|
230
|
+
* ],
|
|
231
|
+
* };
|
|
232
|
+
* }
|
|
233
|
+
* return {
|
|
234
|
+
* expr: `tokens.${ctx.path}`,
|
|
235
|
+
* imports: [
|
|
236
|
+
* { from: { kind: "specifier", value: "./tokens" }, names: [{ imported: "tokens" }] },
|
|
237
|
+
* ],
|
|
238
|
+
* };
|
|
239
|
+
* }
|
|
240
|
+
* // Return undefined to bail/skip the file
|
|
241
|
+
* },
|
|
242
|
+
*
|
|
243
|
+
* resolveCall(ctx) {
|
|
244
|
+
* // Resolve helper calls inside template interpolations.
|
|
245
|
+
* // Use ctx.cssProperty to determine context:
|
|
246
|
+
* // - If ctx.cssProperty exists → return a CSS value expression
|
|
247
|
+
* // - If ctx.cssProperty is undefined → return a StyleX style object reference
|
|
248
|
+
* // Return { expr, imports } or undefined to bail/skip the file
|
|
249
|
+
* void ctx;
|
|
250
|
+
* },
|
|
251
|
+
*
|
|
252
|
+
* resolveSelector(ctx) {
|
|
253
|
+
* // Resolve imported values used in selector position.
|
|
254
|
+
* // Return one of:
|
|
255
|
+
* // - { kind: "media", expr, imports } for media queries (e.g., breakpoints.phone)
|
|
256
|
+
* // - { kind: "pseudoAlias", values, styleSelectorExpr?, imports? } for pseudo-class expansion
|
|
257
|
+
* // - undefined to bail/skip the file
|
|
258
|
+
* // For @media placeholders, check ctx.kind === "mediaQueryInterpolation" and
|
|
259
|
+
* // use ctx.mediaQuery.feature to choose the correct defineConsts media key.
|
|
260
|
+
* void ctx;
|
|
261
|
+
* },
|
|
262
|
+
*
|
|
263
|
+
* // Configure external interface for exported components
|
|
264
|
+
* externalInterface(ctx) {
|
|
265
|
+
* // Example: Enable styles, `as`, and `ref` for shared components folder
|
|
266
|
+
* if (ctx.filePath.includes("/shared/components/")) {
|
|
267
|
+
* return { styles: true, as: true, ref: true };
|
|
268
|
+
* }
|
|
269
|
+
* return { styles: false, as: false, ref: false };
|
|
270
|
+
* },
|
|
271
|
+
*
|
|
272
|
+
* // Optional: provide a custom merger, or use `null` for the default verbose merge output
|
|
273
|
+
* styleMerger: null,
|
|
274
|
+
*
|
|
275
|
+
* // Emit sx={} JSX attributes instead of {...stylex.props()} spreads (requires StyleX ≥0.18)
|
|
276
|
+
* useSxProp: false,
|
|
277
|
+
*
|
|
278
|
+
* // Opt out of logical properties — use paddingTop/Right/Bottom/Left instead of Block/Inline
|
|
279
|
+
* // usePhysicalProperties: true,
|
|
280
|
+
*
|
|
281
|
+
* // Optional: customize runtime theme hook import/call used by emitted wrappers
|
|
282
|
+
* themeHook: {
|
|
283
|
+
* functionName: "useTheme",
|
|
284
|
+
* importSource: { kind: "specifier", value: "styled-components" },
|
|
285
|
+
* },
|
|
286
|
+
* });
|
|
287
|
+
*/
|
|
288
|
+
function defineAdapter(adapter) {
|
|
289
|
+
assertValidAdapterInput(adapter, "defineAdapter(adapter)");
|
|
290
|
+
return adapter;
|
|
291
|
+
}
|
|
292
|
+
//#endregion
|
|
293
|
+
//#region src/internal/merge-markers.ts
|
|
294
|
+
/**
|
|
295
|
+
* Shared utility for merging marker sidecar content.
|
|
296
|
+
* Core concepts: deduplication of defineMarker declarations across files.
|
|
297
|
+
*/
|
|
298
|
+
/** Regex matching a marker block: optional JSDoc comment followed by the export line. */
|
|
299
|
+
const MARKER_BLOCK_RE = /(?:\/\*\*[^]*?\*\/\n)?export const \w+ = stylex\.defineMarker\(\);/gm;
|
|
300
|
+
/** Regex matching just the export line (used for dedup checks). */
|
|
301
|
+
const MARKER_EXPORT_RE = /^export const \w+ = stylex\.defineMarker\(\);$/gm;
|
|
302
|
+
/** Regex matching a generated defineVars export block. */
|
|
303
|
+
const DEFINE_VARS_BLOCK_RE = /export const \w+ = stylex\.defineVars\(\{\n[\s\S]*?\n\}\);/gm;
|
|
304
|
+
/** Regex matching just the defineVars export line (used for dedup checks). */
|
|
305
|
+
const DEFINE_VARS_EXPORT_RE = /^export const \w+ = stylex\.defineVars\(\{$/gm;
|
|
306
|
+
/**
|
|
307
|
+
* Merge marker declarations from `incoming` into `base`, appending only new
|
|
308
|
+
* marker blocks (JSDoc + export). Returns `base` unchanged if all markers already exist.
|
|
309
|
+
*/
|
|
310
|
+
function mergeMarkerDeclarations(base, incoming) {
|
|
311
|
+
const mergedDefineVars = mergeDefineVarsBlocks(base, incoming);
|
|
312
|
+
const markerBlocks = getNewBlocks({
|
|
313
|
+
base: mergedDefineVars,
|
|
314
|
+
incoming,
|
|
315
|
+
blockRegex: MARKER_BLOCK_RE,
|
|
316
|
+
exportRegex: MARKER_EXPORT_RE
|
|
317
|
+
});
|
|
318
|
+
const defineVarsBlocks = getNewBlocks({
|
|
319
|
+
base: mergedDefineVars,
|
|
320
|
+
incoming,
|
|
321
|
+
blockRegex: DEFINE_VARS_BLOCK_RE,
|
|
322
|
+
exportRegex: DEFINE_VARS_EXPORT_RE
|
|
323
|
+
});
|
|
324
|
+
const blocksToAdd = [...markerBlocks, ...defineVarsBlocks];
|
|
325
|
+
if (blocksToAdd.length === 0) return mergedDefineVars;
|
|
326
|
+
let merged = mergedDefineVars;
|
|
327
|
+
if (!merged.includes("@stylexjs/stylex")) merged = `import * as stylex from "@stylexjs/stylex";\n\n${merged}`;
|
|
328
|
+
return merged.trimEnd() + "\n\n" + blocksToAdd.join("\n\n") + "\n";
|
|
329
|
+
}
|
|
330
|
+
function getNewBlocks(args) {
|
|
331
|
+
const { base, incoming, blockRegex, exportRegex } = args;
|
|
332
|
+
const incomingExports = [...incoming.matchAll(exportRegex)].map((m) => m[0]);
|
|
333
|
+
if (incomingExports.length === 0) return [];
|
|
334
|
+
const newExportLines = incomingExports.filter((line) => !base.includes(line));
|
|
335
|
+
if (newExportLines.length === 0) return [];
|
|
336
|
+
const newExportSet = new Set(newExportLines);
|
|
337
|
+
return [...incoming.matchAll(blockRegex)].map((m) => m[0]).filter((block) => {
|
|
338
|
+
const exportLine = block.match(exportRegex);
|
|
339
|
+
return exportLine && newExportSet.has(exportLine[0]);
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
function mergeDefineVarsBlocks(base, incoming) {
|
|
343
|
+
let merged = base;
|
|
344
|
+
for (const incomingBlock of incoming.matchAll(DEFINE_VARS_BLOCK_RE)) {
|
|
345
|
+
const incomingText = incomingBlock[0];
|
|
346
|
+
const exportName = readDefineVarsExportName(incomingText);
|
|
347
|
+
if (!exportName) continue;
|
|
348
|
+
const existingBlock = findDefineVarsBlockByExportName(merged, exportName);
|
|
349
|
+
if (!existingBlock) continue;
|
|
350
|
+
const entriesToAdd = getMissingDefineVarsEntries({
|
|
351
|
+
existingBlock: existingBlock.text,
|
|
352
|
+
incomingBlock: incomingText
|
|
353
|
+
});
|
|
354
|
+
if (entriesToAdd.length === 0) continue;
|
|
355
|
+
const insertionPoint = existingBlock.start + existingBlock.text.lastIndexOf("\n});");
|
|
356
|
+
const linesToAdd = entriesToAdd.map((entry) => entry.line);
|
|
357
|
+
merged = `${merged.slice(0, insertionPoint)}\n${linesToAdd.join("\n")}${merged.slice(insertionPoint)}`;
|
|
358
|
+
}
|
|
359
|
+
return merged;
|
|
360
|
+
}
|
|
361
|
+
function readDefineVarsExportName(block) {
|
|
362
|
+
return /^export const ([A-Za-z_$][\w$]*) = stylex\.defineVars\(\{/m.exec(block)?.[1] ?? null;
|
|
363
|
+
}
|
|
364
|
+
function findDefineVarsBlockByExportName(source, exportName) {
|
|
365
|
+
for (const match of source.matchAll(DEFINE_VARS_BLOCK_RE)) {
|
|
366
|
+
const text = match[0];
|
|
367
|
+
if (readDefineVarsExportName(text) === exportName && match.index !== void 0) return {
|
|
368
|
+
text,
|
|
369
|
+
start: match.index
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
function getMissingDefineVarsEntries(args) {
|
|
375
|
+
const { existingBlock, incomingBlock } = args;
|
|
376
|
+
const existingKeys = new Set(readDefineVarsEntryKeys(existingBlock));
|
|
377
|
+
return readDefineVarsEntries(incomingBlock).filter((entry) => !existingKeys.has(entry.key));
|
|
378
|
+
}
|
|
379
|
+
function readDefineVarsEntryKeys(block) {
|
|
380
|
+
return readDefineVarsEntries(block).map((entry) => entry.key);
|
|
381
|
+
}
|
|
382
|
+
function readDefineVarsEntries(block) {
|
|
383
|
+
return block.split("\n").map((line) => ({
|
|
384
|
+
line,
|
|
385
|
+
match: /^\s*(?:(["']--[^"']+["'])|([A-Za-z_$][\w$]*))\s*:/.exec(line)
|
|
386
|
+
})).filter((entry) => Boolean(entry.match)).map(({ line, match }) => ({
|
|
387
|
+
key: normalizeDefineVarsEntryKey(match[1] ?? match[2]),
|
|
388
|
+
line
|
|
389
|
+
}));
|
|
390
|
+
}
|
|
391
|
+
function normalizeDefineVarsEntryKey(key) {
|
|
392
|
+
if (key.length >= 2 && (key.startsWith("\"") && key.endsWith("\"") || key.startsWith("'") && key.endsWith("'"))) return key.slice(1, -1);
|
|
393
|
+
return key;
|
|
394
|
+
}
|
|
395
|
+
//#endregion
|
|
396
|
+
//#region src/internal/wrapped-component-interface.ts
|
|
397
|
+
/**
|
|
398
|
+
* Decides whether a `styled(Component)` wraps a component that already
|
|
399
|
+
* accepts a StyleX `sx` prop. When true, the codemod emits `sx={style}`
|
|
400
|
+
* instead of `{...stylex.props(style)}` on the wrapped element.
|
|
401
|
+
*
|
|
402
|
+
* Two signals are consulted, in order:
|
|
403
|
+
* 1. The adapter `wrappedComponentInterface` hook — explicit override.
|
|
404
|
+
* 2. Static auto-detection of an `sx?: …` member on the imported
|
|
405
|
+
* component's props type, or on a same-file component's props type when
|
|
406
|
+
* the current source is provided.
|
|
407
|
+
*
|
|
408
|
+
* Used by both the wrapper-emitter (full wrapper components) and the
|
|
409
|
+
* JSX-rewrite step (inlined re-styles).
|
|
410
|
+
*/
|
|
411
|
+
function detectExportedComponentSxProp(args) {
|
|
412
|
+
return detectExportedSxProp(args.absolutePath, args.componentName, args.sourceOverrides, args.visited ?? /* @__PURE__ */ new Set());
|
|
413
|
+
}
|
|
414
|
+
function isWrappedComponentSxAware(args) {
|
|
415
|
+
const { adapter, importMap, componentLocalName, filePath, localSource, sourceOverrides } = args;
|
|
416
|
+
if (!adapter.useSxProp) return false;
|
|
417
|
+
const importInfo = importMap?.get(componentLocalName);
|
|
418
|
+
if (!importInfo) {
|
|
419
|
+
const source = readSourceOverride(filePath, sourceOverrides) ?? localSource;
|
|
420
|
+
return source ? computeDetectionFromSource(source, componentLocalName, filePath, sourceOverrides) : false;
|
|
421
|
+
}
|
|
422
|
+
const adapterResult = adapter.wrappedComponentInterface?.({
|
|
423
|
+
localName: componentLocalName,
|
|
424
|
+
importSource: importInfo.source.value,
|
|
425
|
+
importedName: importInfo.importedName,
|
|
426
|
+
filePath
|
|
427
|
+
});
|
|
428
|
+
if (adapterResult !== void 0) return adapterResult.acceptsSx === true;
|
|
429
|
+
const absolutePath = resolveWrappedComponentSource(importInfo.source, filePath);
|
|
430
|
+
if (!absolutePath) return false;
|
|
431
|
+
return detectExportedComponentSxProp({
|
|
432
|
+
absolutePath,
|
|
433
|
+
componentName: importInfo.importedName,
|
|
434
|
+
sourceOverrides
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
const FILE_EXTENSIONS = [
|
|
438
|
+
"",
|
|
439
|
+
".tsx",
|
|
440
|
+
".ts",
|
|
441
|
+
".jsx",
|
|
442
|
+
".js"
|
|
443
|
+
];
|
|
444
|
+
const SX_PROP_NAME = "sx";
|
|
445
|
+
const moduleResolver = createModuleResolver();
|
|
446
|
+
/**
|
|
447
|
+
* Cache keyed by `absolutePath\u0000componentName`. Detection requires reading
|
|
448
|
+
* and parsing the source file, so memoize across the many `styled(X)` lookups
|
|
449
|
+
* that happen during a single transform pass.
|
|
450
|
+
*/
|
|
451
|
+
const detectionCache = /* @__PURE__ */ new Map();
|
|
452
|
+
function resolveWrappedComponentSource(source, filePath) {
|
|
453
|
+
if (source.kind === "absolutePath") return source.value;
|
|
454
|
+
return moduleResolver.resolve(filePath, source.value) ?? null;
|
|
455
|
+
}
|
|
456
|
+
function detectExportedSxProp(absolutePath, componentName, sourceOverrides, visited = /* @__PURE__ */ new Set()) {
|
|
457
|
+
const sourcePath = resolveSourcePath(absolutePath) ?? absolutePath;
|
|
458
|
+
const visitKey = `${toRealPath(sourcePath)}\u0000${componentName}`;
|
|
459
|
+
if (visited.has(visitKey)) return false;
|
|
460
|
+
visited.add(visitKey);
|
|
461
|
+
const sourceOverride = readSourceOverride(absolutePath, sourceOverrides);
|
|
462
|
+
if (sourceOverride !== void 0) return computeDetectionFromSource(sourceOverride, componentName, sourcePath, sourceOverrides, visited);
|
|
463
|
+
const cacheKey = `${absolutePath}\u0000${componentName}`;
|
|
464
|
+
if (!sourceOverrides) {
|
|
465
|
+
const cached = detectionCache.get(cacheKey);
|
|
466
|
+
if (cached !== void 0) return cached;
|
|
467
|
+
}
|
|
468
|
+
const result = computeDetection(absolutePath, componentName, visited, sourceOverrides);
|
|
469
|
+
if (!sourceOverrides) detectionCache.set(cacheKey, result);
|
|
470
|
+
return result;
|
|
471
|
+
}
|
|
472
|
+
function computeDetection(absolutePath, componentName, visited, sourceOverrides) {
|
|
473
|
+
const resolved = resolveSourcePath(absolutePath);
|
|
474
|
+
if (!resolved) return false;
|
|
475
|
+
let source;
|
|
476
|
+
try {
|
|
477
|
+
source = readFileSync(resolved, "utf8");
|
|
478
|
+
} catch {
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
return computeDetectionFromSource(source, componentName, resolved, sourceOverrides, visited);
|
|
482
|
+
}
|
|
483
|
+
function computeDetectionFromSource(source, componentName, filePath, sourceOverrides, visited = /* @__PURE__ */ new Set()) {
|
|
484
|
+
if (!source.includes(componentName) && !sourceHasReExports(source)) return false;
|
|
485
|
+
const parsed = parseSource(source);
|
|
486
|
+
if (!parsed) return false;
|
|
487
|
+
const { j, root } = parsed;
|
|
488
|
+
const propsTypeNode = findComponentPropsType(root, componentName);
|
|
489
|
+
if (!propsTypeNode) return filePath ? detectReExportedSxProp(root, componentName, filePath, sourceOverrides, visited) : false;
|
|
490
|
+
return typeMentionsSxMember({
|
|
491
|
+
j,
|
|
492
|
+
root,
|
|
493
|
+
filePath,
|
|
494
|
+
sourceOverrides,
|
|
495
|
+
visited,
|
|
496
|
+
visitedTypeNames: /* @__PURE__ */ new Set()
|
|
497
|
+
}, propsTypeNode);
|
|
498
|
+
}
|
|
499
|
+
function parseSource(source) {
|
|
500
|
+
try {
|
|
501
|
+
const j = jscodeshift.withParser("tsx");
|
|
502
|
+
return {
|
|
503
|
+
j,
|
|
504
|
+
root: j(source)
|
|
505
|
+
};
|
|
506
|
+
} catch {
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
function isKnownPropPreservingHocCallee(callee) {
|
|
511
|
+
const node = callee;
|
|
512
|
+
if (!node) return false;
|
|
513
|
+
if (node.type === "Identifier") return node.name === "memo" || node.name === "forwardRef";
|
|
514
|
+
return node.type === "MemberExpression" && node.property?.type === "Identifier" && (node.property.name === "memo" || node.property.name === "forwardRef");
|
|
515
|
+
}
|
|
516
|
+
function resolveSourcePath(absolutePath) {
|
|
517
|
+
for (const candidate of sourcePathCandidates(absolutePath)) if (existsSync(candidate)) return candidate;
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
function readSourceOverride(absolutePath, sourceOverrides) {
|
|
521
|
+
if (!sourceOverrides) return;
|
|
522
|
+
for (const candidate of sourcePathCandidates(absolutePath)) {
|
|
523
|
+
const source = sourceOverrides.get(toRealPath(candidate));
|
|
524
|
+
if (source !== void 0) return source;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
function sourcePathCandidates(absolutePath) {
|
|
528
|
+
return FILE_EXTENSIONS.map((ext) => absolutePath + ext);
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Locate a top-level `componentName` declaration in `root` and return the TS
|
|
532
|
+
* type annotation of its first parameter, or null.
|
|
533
|
+
*
|
|
534
|
+
* Handles:
|
|
535
|
+
* - `export function Name(props: T)` and `export default function Name(...)`.
|
|
536
|
+
* - `export const Name = (props: T) => …` / arrow function variants.
|
|
537
|
+
*/
|
|
538
|
+
function findComponentPropsType(root, componentName) {
|
|
539
|
+
let propsType = null;
|
|
540
|
+
const recordFromParam = (params) => {
|
|
541
|
+
if (propsType || !Array.isArray(params) || params.length === 0) return;
|
|
542
|
+
const ann = params[0]?.typeAnnotation?.typeAnnotation;
|
|
543
|
+
if (ann) propsType = ann;
|
|
544
|
+
};
|
|
545
|
+
const recordFromInit = (init) => {
|
|
546
|
+
if (propsType || !init || typeof init !== "object") return;
|
|
547
|
+
const node = init;
|
|
548
|
+
if (node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") {
|
|
549
|
+
recordFromParam(node.params);
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
if (node.type !== "CallExpression" || !Array.isArray(node.arguments)) return;
|
|
553
|
+
const canFollowIdentifierArguments = isKnownPropPreservingHocCallee(node.callee);
|
|
554
|
+
for (const arg of node.arguments) {
|
|
555
|
+
const argNode = arg;
|
|
556
|
+
if (canFollowIdentifierArguments && argNode.type === "Identifier" && argNode.name) {
|
|
557
|
+
recordFromNamedDeclaration(argNode.name);
|
|
558
|
+
if (propsType) return;
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
recordFromInit(arg);
|
|
562
|
+
if (propsType) return;
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
const recordFromNamedDeclaration = (name) => {
|
|
566
|
+
if (propsType) return;
|
|
567
|
+
const body = root.get().node.program.body;
|
|
568
|
+
for (const statement of body) {
|
|
569
|
+
const declaration = statement.type === "ExportNamedDeclaration" ? statement.declaration : statement;
|
|
570
|
+
if (!declaration) continue;
|
|
571
|
+
if (declaration.type === "FunctionDeclaration") {
|
|
572
|
+
if (declaration.id?.name === name) {
|
|
573
|
+
recordFromParam(declaration.params);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
if (declaration.type !== "VariableDeclaration") continue;
|
|
579
|
+
for (const declarator of declaration.declarations) {
|
|
580
|
+
const id = declarator.id;
|
|
581
|
+
if (id.type === "Identifier" && id.name === name) {
|
|
582
|
+
recordFromInit(declarator.init);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
if (componentName === "default") {
|
|
589
|
+
const body = root.get().node.program.body;
|
|
590
|
+
for (const statement of body) {
|
|
591
|
+
if (statement.type !== "ExportDefaultDeclaration") continue;
|
|
592
|
+
const declaration = statement.declaration;
|
|
593
|
+
if (!declaration) continue;
|
|
594
|
+
if (declaration.type === "FunctionDeclaration" || declaration.type === "FunctionExpression" || declaration.type === "ArrowFunctionExpression") {
|
|
595
|
+
recordFromParam(declaration.params);
|
|
596
|
+
return propsType;
|
|
597
|
+
}
|
|
598
|
+
if (declaration.type === "Identifier" && declaration.name) {
|
|
599
|
+
recordFromNamedDeclaration(declaration.name);
|
|
600
|
+
return propsType;
|
|
601
|
+
}
|
|
602
|
+
recordFromInit(declaration);
|
|
603
|
+
return propsType;
|
|
604
|
+
}
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
const body = root.get().node.program.body;
|
|
608
|
+
for (const statement of body) {
|
|
609
|
+
const declaration = statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" ? statement.declaration : statement;
|
|
610
|
+
if (!declaration) continue;
|
|
611
|
+
if (declaration.type === "FunctionDeclaration") {
|
|
612
|
+
if (declaration.id?.name === componentName) recordFromParam(declaration.params);
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
if (declaration.type !== "VariableDeclaration") continue;
|
|
616
|
+
for (const declarator of declaration.declarations) {
|
|
617
|
+
const id = declarator.id;
|
|
618
|
+
if (id.type !== "Identifier" || id.name !== componentName) continue;
|
|
619
|
+
recordFromInit(declarator.init);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return propsType;
|
|
623
|
+
}
|
|
624
|
+
function detectReExportedSxProp(root, componentName, filePath, sourceOverrides, visited) {
|
|
625
|
+
const body = root.get().node.program.body;
|
|
626
|
+
for (const statement of body) {
|
|
627
|
+
if (statement.type !== "ExportNamedDeclaration" && statement.type !== "ExportAllDeclaration") continue;
|
|
628
|
+
const source = statement.source?.value;
|
|
629
|
+
if (typeof source !== "string") continue;
|
|
630
|
+
let sourceComponentName = componentName;
|
|
631
|
+
if (statement.type === "ExportNamedDeclaration") {
|
|
632
|
+
let forwardedName = null;
|
|
633
|
+
for (const specifier of statement.specifiers ?? []) {
|
|
634
|
+
const spec = specifier;
|
|
635
|
+
if (spec.type !== "ExportSpecifier") continue;
|
|
636
|
+
const exportedName = getModuleName(spec.exported);
|
|
637
|
+
if (exportedName !== componentName) continue;
|
|
638
|
+
forwardedName = getModuleName(spec.local) ?? exportedName;
|
|
639
|
+
break;
|
|
640
|
+
}
|
|
641
|
+
if (!forwardedName) continue;
|
|
642
|
+
sourceComponentName = forwardedName;
|
|
643
|
+
}
|
|
644
|
+
const resolvedPath = moduleResolver.resolve(filePath, source);
|
|
645
|
+
if (resolvedPath && detectExportedComponentSxProp({
|
|
646
|
+
absolutePath: resolvedPath,
|
|
647
|
+
componentName: sourceComponentName,
|
|
648
|
+
sourceOverrides,
|
|
649
|
+
visited
|
|
650
|
+
})) return true;
|
|
651
|
+
}
|
|
652
|
+
return false;
|
|
653
|
+
}
|
|
654
|
+
function sourceHasReExports(source) {
|
|
655
|
+
return /\bexport\s+(?:\*|\{)/.test(source);
|
|
656
|
+
}
|
|
657
|
+
function getModuleName(node) {
|
|
658
|
+
const n = node;
|
|
659
|
+
if (!n) return null;
|
|
660
|
+
if (n.type === "Identifier") return n.name ?? null;
|
|
661
|
+
return typeof n.value === "string" ? n.value : null;
|
|
662
|
+
}
|
|
663
|
+
function typeMentionsSxMember(ctx, node) {
|
|
664
|
+
const n = node;
|
|
665
|
+
switch (n.type) {
|
|
666
|
+
case "TSTypeLiteral": return literalContainsSxMember(n.members);
|
|
667
|
+
case "TSIntersectionType":
|
|
668
|
+
case "TSUnionType":
|
|
669
|
+
for (const member of n.types ?? []) if (typeMentionsSxMember(ctx, member)) return true;
|
|
670
|
+
return false;
|
|
671
|
+
case "TSParenthesizedType": return n.typeAnnotation ? typeMentionsSxMember(ctx, n.typeAnnotation) : false;
|
|
672
|
+
case "TSTypeReference": {
|
|
673
|
+
const typeName = getTypeReferenceName(n.typeName);
|
|
674
|
+
return typeName ? typeReferenceNameMentionsSx(ctx, typeName, { allowImported: true }) : false;
|
|
675
|
+
}
|
|
676
|
+
default: return false;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
function getTypeReferenceName(typeName) {
|
|
680
|
+
if (typeName?.type === "Identifier" && typeName.name) return {
|
|
681
|
+
kind: "identifier",
|
|
682
|
+
name: typeName.name
|
|
683
|
+
};
|
|
684
|
+
if (typeName?.type === "TSQualifiedName" && typeName.left?.type === "Identifier" && typeName.left.name && typeName.right?.type === "Identifier" && typeName.right.name) return {
|
|
685
|
+
kind: "qualified",
|
|
686
|
+
namespace: typeName.left.name,
|
|
687
|
+
name: typeName.right.name
|
|
688
|
+
};
|
|
689
|
+
return null;
|
|
690
|
+
}
|
|
691
|
+
function typeReferenceNameMentionsSx(ctx, typeName, options) {
|
|
692
|
+
if (typeName.kind === "qualified") return options.allowImported ? importedNamespaceTypeReferenceMentionsSx(ctx, typeName) : false;
|
|
693
|
+
return typeReferenceMentionsSx(ctx, typeName.name, options);
|
|
694
|
+
}
|
|
695
|
+
function typeReferenceMentionsSx(ctx, typeName, options) {
|
|
696
|
+
const visitedKey = `${ctx.filePath ?? "<memory>"}\u0000${typeName}`;
|
|
697
|
+
if (ctx.visitedTypeNames.has(visitedKey)) return false;
|
|
698
|
+
ctx.visitedTypeNames.add(visitedKey);
|
|
699
|
+
const aliased = ctx.root.find(ctx.j.TSTypeAliasDeclaration).filter((p) => {
|
|
700
|
+
return p.node.id?.name === typeName;
|
|
701
|
+
});
|
|
702
|
+
if (aliased.size() > 0) {
|
|
703
|
+
const annotation = aliased.get().node.typeAnnotation;
|
|
704
|
+
if (annotation && typeMentionsSxMember(ctx, annotation)) return true;
|
|
705
|
+
}
|
|
706
|
+
const iface = ctx.root.find(ctx.j.TSInterfaceDeclaration).filter((p) => {
|
|
707
|
+
return p.node.id?.name === typeName;
|
|
708
|
+
});
|
|
709
|
+
if (iface.size() > 0) {
|
|
710
|
+
const node = iface.get().node;
|
|
711
|
+
if (literalContainsSxMember(node.body?.body)) return true;
|
|
712
|
+
if (interfaceExtendsMentionSx(ctx, node.extends)) return true;
|
|
713
|
+
}
|
|
714
|
+
if (typeName === "default" && defaultExportedTypeDeclarationMentionsSx(ctx)) return true;
|
|
715
|
+
if (exportedTypeReferenceMentionsSx(ctx, typeName)) return true;
|
|
716
|
+
return options.allowImported ? importedTypeReferenceMentionsSx(ctx, typeName) : false;
|
|
717
|
+
}
|
|
718
|
+
function defaultExportedTypeDeclarationMentionsSx(ctx) {
|
|
719
|
+
const body = ctx.root.get().node.program.body;
|
|
720
|
+
for (const statement of body) {
|
|
721
|
+
if (statement.type !== "ExportDefaultDeclaration") continue;
|
|
722
|
+
const declaration = statement.declaration;
|
|
723
|
+
if (!declaration) continue;
|
|
724
|
+
if (declaration.type === "TSInterfaceDeclaration") return literalContainsSxMember(declaration.body?.body) || interfaceExtendsMentionSx(ctx, declaration.extends);
|
|
725
|
+
if (declaration.type === "TSTypeAliasDeclaration" && declaration.typeAnnotation) return typeMentionsSxMember(ctx, declaration.typeAnnotation);
|
|
726
|
+
if (declaration.type === "Identifier" && typeof declaration.name === "string") return typeReferenceMentionsSx(ctx, declaration.name, { allowImported: true });
|
|
727
|
+
}
|
|
728
|
+
return false;
|
|
729
|
+
}
|
|
730
|
+
function exportedTypeReferenceMentionsSx(ctx, typeName) {
|
|
731
|
+
if (!ctx.filePath) return false;
|
|
732
|
+
const body = ctx.root.get().node.program.body;
|
|
733
|
+
for (const statement of body) {
|
|
734
|
+
if (statement.type === "ExportAllDeclaration") {
|
|
735
|
+
const source = statement.source?.value;
|
|
736
|
+
if (typeof source !== "string") continue;
|
|
737
|
+
const resolvedPath = moduleResolver.resolve(ctx.filePath, source);
|
|
738
|
+
if (!resolvedPath) continue;
|
|
739
|
+
const sourceText = readSourceOverride(resolvedPath, ctx.sourceOverrides) ?? readSource(resolvedPath);
|
|
740
|
+
if (!sourceText) continue;
|
|
741
|
+
const parsed = parseSource(sourceText);
|
|
742
|
+
if (!parsed) continue;
|
|
743
|
+
if (typeReferenceMentionsSx({
|
|
744
|
+
...ctx,
|
|
745
|
+
j: parsed.j,
|
|
746
|
+
root: parsed.root,
|
|
747
|
+
filePath: resolvedPath
|
|
748
|
+
}, typeName, { allowImported: true })) return true;
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
if (statement.type !== "ExportNamedDeclaration") continue;
|
|
752
|
+
const source = statement.source?.value;
|
|
753
|
+
for (const specifier of statement.specifiers ?? []) {
|
|
754
|
+
const spec = specifier;
|
|
755
|
+
if (spec.type !== "ExportSpecifier" && spec.type !== "ExportTypeSpecifier") continue;
|
|
756
|
+
const exportedName = getModuleName(spec.exported);
|
|
757
|
+
if (exportedName !== typeName) continue;
|
|
758
|
+
const sourceName = getModuleName(spec.local) ?? exportedName;
|
|
759
|
+
if (typeof source !== "string") {
|
|
760
|
+
if (typeReferenceMentionsSx(ctx, sourceName, { allowImported: true })) return true;
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
const resolvedPath = moduleResolver.resolve(ctx.filePath, source);
|
|
764
|
+
if (!resolvedPath) continue;
|
|
765
|
+
const sourceText = readSourceOverride(resolvedPath, ctx.sourceOverrides) ?? readSource(resolvedPath);
|
|
766
|
+
if (!sourceText) continue;
|
|
767
|
+
const parsed = parseSource(sourceText);
|
|
768
|
+
if (!parsed) continue;
|
|
769
|
+
if (typeReferenceMentionsSx({
|
|
770
|
+
...ctx,
|
|
771
|
+
j: parsed.j,
|
|
772
|
+
root: parsed.root,
|
|
773
|
+
filePath: resolvedPath
|
|
774
|
+
}, sourceName, { allowImported: true })) return true;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
return false;
|
|
778
|
+
}
|
|
779
|
+
function interfaceExtendsMentionSx(ctx, heritage) {
|
|
780
|
+
if (!Array.isArray(heritage)) return false;
|
|
781
|
+
for (const item of heritage) {
|
|
782
|
+
const typeName = getHeritageTypeName(item);
|
|
783
|
+
if (typeName && typeReferenceNameMentionsSx(ctx, typeName, { allowImported: true })) return true;
|
|
784
|
+
}
|
|
785
|
+
return false;
|
|
786
|
+
}
|
|
787
|
+
function getHeritageTypeName(node) {
|
|
788
|
+
if (!node || typeof node !== "object") return null;
|
|
789
|
+
const n = node;
|
|
790
|
+
return getTypeReferenceName(n.expression) ?? getTypeReferenceName(n.typeName);
|
|
791
|
+
}
|
|
792
|
+
function importedTypeReferenceMentionsSx(ctx, localTypeName) {
|
|
793
|
+
const importInfo = findTypeImport(ctx.root, localTypeName);
|
|
794
|
+
if (!importInfo || !ctx.filePath) return false;
|
|
795
|
+
const resolvedPath = moduleResolver.resolve(ctx.filePath, importInfo.source);
|
|
796
|
+
if (!resolvedPath) return false;
|
|
797
|
+
const source = readSourceOverride(resolvedPath, ctx.sourceOverrides) ?? readSource(resolvedPath);
|
|
798
|
+
if (!source) return false;
|
|
799
|
+
const parsed = parseSource(source);
|
|
800
|
+
if (!parsed) return false;
|
|
801
|
+
return typeReferenceMentionsSx({
|
|
802
|
+
...ctx,
|
|
803
|
+
j: parsed.j,
|
|
804
|
+
root: parsed.root,
|
|
805
|
+
filePath: resolvedPath
|
|
806
|
+
}, importInfo.importedName, { allowImported: true });
|
|
807
|
+
}
|
|
808
|
+
function importedNamespaceTypeReferenceMentionsSx(ctx, typeName) {
|
|
809
|
+
const importInfo = findNamespaceTypeImport(ctx.root, typeName.namespace);
|
|
810
|
+
if (!importInfo || !ctx.filePath) return false;
|
|
811
|
+
const resolvedPath = moduleResolver.resolve(ctx.filePath, importInfo.source);
|
|
812
|
+
if (!resolvedPath) return false;
|
|
813
|
+
const source = readSourceOverride(resolvedPath, ctx.sourceOverrides) ?? readSource(resolvedPath);
|
|
814
|
+
if (!source) return false;
|
|
815
|
+
const parsed = parseSource(source);
|
|
816
|
+
if (!parsed) return false;
|
|
817
|
+
return typeReferenceMentionsSx({
|
|
818
|
+
...ctx,
|
|
819
|
+
j: parsed.j,
|
|
820
|
+
root: parsed.root,
|
|
821
|
+
filePath: resolvedPath
|
|
822
|
+
}, typeName.name, { allowImported: true });
|
|
823
|
+
}
|
|
824
|
+
function findTypeImport(root, localTypeName) {
|
|
825
|
+
const body = root.get().node.program.body;
|
|
826
|
+
for (const statement of body) {
|
|
827
|
+
if (statement.type !== "ImportDeclaration") continue;
|
|
828
|
+
const source = statement.source.value;
|
|
829
|
+
if (typeof source !== "string") continue;
|
|
830
|
+
for (const specifier of statement.specifiers ?? []) {
|
|
831
|
+
const spec = specifier;
|
|
832
|
+
if (spec.local?.name !== localTypeName) continue;
|
|
833
|
+
if (spec.type === "ImportSpecifier") {
|
|
834
|
+
const importedName = spec.imported?.name ?? (typeof spec.imported?.value === "string" ? spec.imported.value : void 0);
|
|
835
|
+
return importedName ? {
|
|
836
|
+
importedName,
|
|
837
|
+
source
|
|
838
|
+
} : null;
|
|
839
|
+
}
|
|
840
|
+
if (spec.type === "ImportDefaultSpecifier") return {
|
|
841
|
+
importedName: "default",
|
|
842
|
+
source
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
return null;
|
|
847
|
+
}
|
|
848
|
+
function findNamespaceTypeImport(root, localNamespace) {
|
|
849
|
+
const body = root.get().node.program.body;
|
|
850
|
+
for (const statement of body) {
|
|
851
|
+
if (statement.type !== "ImportDeclaration") continue;
|
|
852
|
+
const source = statement.source.value;
|
|
853
|
+
if (typeof source !== "string") continue;
|
|
854
|
+
for (const specifier of statement.specifiers ?? []) {
|
|
855
|
+
const spec = specifier;
|
|
856
|
+
if (spec.type === "ImportNamespaceSpecifier" && spec.local?.name === localNamespace) return { source };
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return null;
|
|
860
|
+
}
|
|
861
|
+
function readSource(absolutePath) {
|
|
862
|
+
const resolved = resolveSourcePath(absolutePath);
|
|
863
|
+
if (!resolved) return null;
|
|
864
|
+
try {
|
|
865
|
+
return readFileSync(resolved, "utf8");
|
|
866
|
+
} catch {
|
|
867
|
+
return null;
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
function literalContainsSxMember(members) {
|
|
871
|
+
if (!Array.isArray(members)) return false;
|
|
872
|
+
for (const member of members) {
|
|
873
|
+
const m = member;
|
|
874
|
+
if (m?.type !== "TSPropertySignature" || !m.key) continue;
|
|
875
|
+
if ((m.key.type === "Identifier" ? m.key.name : (m.key.type === "StringLiteral" || m.key.type === "Literal") && typeof m.key.value === "string" ? m.key.value : null) === SX_PROP_NAME) return true;
|
|
876
|
+
}
|
|
877
|
+
return false;
|
|
878
|
+
}
|
|
879
|
+
//#endregion
|
|
880
|
+
export { defineAdapter as a, assertValidAdapterInput as c, DEFAULT_THEME_HOOK as i, describeValue as l, isWrappedComponentSxAware as n, isDirectionalResult as o, mergeMarkerDeclarations as r, assertValidAdapter as s, detectExportedComponentSxProp as t };
|