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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { o as resolveBarrelReExportBinding, r as findImportSource } from "./extract-external-interface-B61N1a6q.mjs";
|
|
2
2
|
//#region src/internal/prepass/compute-leaf-set.ts
|
|
3
3
|
/**
|
|
4
4
|
* Computes which styled-component bindings are "leaves" for leaves-only mode:
|
|
@@ -171,9 +171,12 @@ function computeGlobalLeafKeys(args) {
|
|
|
171
171
|
if (!importInfo) return false;
|
|
172
172
|
const initialDefFile = resolve(importInfo.source, file);
|
|
173
173
|
if (!initialDefFile) return false;
|
|
174
|
-
const
|
|
174
|
+
const reExport = resolveBarrelReExportBinding(initialDefFile, importInfo.isDefault ? "default" : importInfo.exportedName, resolve, cachedRead);
|
|
175
|
+
const defFile = reExport?.filePath ?? initialDefFile;
|
|
176
|
+
const exportedName = reExport?.exportedName ?? importInfo.exportedName;
|
|
177
|
+
const defReal = toRealPath(defFile);
|
|
175
178
|
if (!transformSet.has(defReal)) return false;
|
|
176
|
-
return leafKeyExists(defReal,
|
|
179
|
+
return leafKeyExists(defReal, exportedName, exportedName === "default" || importInfo.isDefault, cachedRead, globalLeaves);
|
|
177
180
|
};
|
|
178
181
|
const tryResolveAdapterIntrinsic = (file, ident) => {
|
|
179
182
|
if (!resolveBaseComponent) return false;
|
package/dist/{extract-external-interface-CdHbvfxu.mjs → extract-external-interface-B61N1a6q.mjs}
RENAMED
|
@@ -292,6 +292,9 @@ function getImportSourceRes(localName) {
|
|
|
292
292
|
return cached;
|
|
293
293
|
}
|
|
294
294
|
function resolveBarrelReExport(filePath, name, resolve, read) {
|
|
295
|
+
return resolveBarrelReExportBinding(filePath, name, resolve, read)?.filePath ?? null;
|
|
296
|
+
}
|
|
297
|
+
function resolveBarrelReExportBinding(filePath, name, resolve, read) {
|
|
295
298
|
const basename = path.basename(filePath);
|
|
296
299
|
if (basename !== "index.ts" && basename !== "index.tsx") return null;
|
|
297
300
|
let src;
|
|
@@ -300,14 +303,25 @@ function resolveBarrelReExport(filePath, name, resolve, read) {
|
|
|
300
303
|
} catch {
|
|
301
304
|
return null;
|
|
302
305
|
}
|
|
303
|
-
const
|
|
304
|
-
|
|
306
|
+
for (const match of src.matchAll(/export\s*\{([^}]+)\}\s*from\s*["']([^"']+)["']/g)) {
|
|
307
|
+
const sourceName = getReExportedSourceName(match[1] ?? "", name);
|
|
308
|
+
const specifier = match[2];
|
|
309
|
+
if (!sourceName || !specifier) continue;
|
|
310
|
+
const resolved = resolve(specifier, filePath);
|
|
311
|
+
if (resolved) return {
|
|
312
|
+
filePath: resolved,
|
|
313
|
+
exportedName: sourceName
|
|
314
|
+
};
|
|
315
|
+
}
|
|
305
316
|
for (const match of src.matchAll(/export\s*\*\s*from\s*["']([^"']+)["']/g)) {
|
|
306
317
|
const specifier = match[1];
|
|
307
318
|
if (!specifier) continue;
|
|
308
319
|
const resolved = resolve(specifier, filePath);
|
|
309
320
|
if (resolved) try {
|
|
310
|
-
if (fileExports(read(resolved), name)) return
|
|
321
|
+
if (fileExports(read(resolved), name)) return {
|
|
322
|
+
filePath: resolved,
|
|
323
|
+
exportedName: name
|
|
324
|
+
};
|
|
311
325
|
} catch {}
|
|
312
326
|
}
|
|
313
327
|
return null;
|
|
@@ -315,6 +329,16 @@ function resolveBarrelReExport(filePath, name, resolve, read) {
|
|
|
315
329
|
function fileExports(src, name) {
|
|
316
330
|
return getFileExportsRe(name).test(src);
|
|
317
331
|
}
|
|
332
|
+
function getReExportedSourceName(specifiers, exportedName) {
|
|
333
|
+
for (const raw of specifiers.split(",")) {
|
|
334
|
+
const parts = raw.trim().split(/\s+as\s+/);
|
|
335
|
+
const local = parts[0]?.trim();
|
|
336
|
+
const exported = (parts[1] ?? parts[0])?.trim();
|
|
337
|
+
if (!local || !exported) continue;
|
|
338
|
+
if (exported === exportedName) return local;
|
|
339
|
+
}
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
318
342
|
const fileExportsReCache = /* @__PURE__ */ new Map();
|
|
319
343
|
function getFileExportsRe(name) {
|
|
320
344
|
let re = fileExportsReCache.get(name);
|
|
@@ -324,16 +348,7 @@ function getFileExportsRe(name) {
|
|
|
324
348
|
}
|
|
325
349
|
return re;
|
|
326
350
|
}
|
|
327
|
-
|
|
328
|
-
function getBarrelExportRe(name) {
|
|
329
|
-
let re = barrelExportReCache.get(name);
|
|
330
|
-
if (!re) {
|
|
331
|
-
re = new RegExp(String.raw`export\s*\{[^}]*\b${name}\b[^}]*\}\s*from\s*["']([^"']+)["']`);
|
|
332
|
-
barrelExportReCache.set(name, re);
|
|
333
|
-
}
|
|
334
|
-
return re;
|
|
335
|
-
}
|
|
336
|
-
function fileImportsFrom(usageSrc, usageFile, name, defFile, resolve) {
|
|
351
|
+
function fileImportsFrom(usageSrc, usageFile, name, defFile, resolve, read) {
|
|
337
352
|
const [namedRe, defaultRe] = getFileImportsFromRes(name);
|
|
338
353
|
namedRe.lastIndex = 0;
|
|
339
354
|
defaultRe.lastIndex = 0;
|
|
@@ -343,11 +358,17 @@ function fileImportsFrom(usageSrc, usageFile, name, defFile, resolve) {
|
|
|
343
358
|
const specifier = match[1];
|
|
344
359
|
if (!specifier) continue;
|
|
345
360
|
const resolved = resolve(specifier, usageFile);
|
|
346
|
-
if (resolved &&
|
|
361
|
+
if (resolved && importCanReferenceDefinition(resolved, name, defFile, resolve, read)) return true;
|
|
347
362
|
if (specifier.endsWith(stem) || specifier.endsWith(`${parent}/${stem}`) || specifier.endsWith(parent)) return true;
|
|
348
363
|
}
|
|
349
364
|
return false;
|
|
350
365
|
}
|
|
366
|
+
function importCanReferenceDefinition(resolvedImport, name, defFile, resolve, read) {
|
|
367
|
+
if (path.resolve(resolvedImport) === path.resolve(defFile)) return true;
|
|
368
|
+
if (!read) return false;
|
|
369
|
+
const reExportedFile = resolveBarrelReExport(resolvedImport, name, resolve, read);
|
|
370
|
+
return reExportedFile !== null && path.resolve(reExportedFile) === path.resolve(defFile);
|
|
371
|
+
}
|
|
351
372
|
const fileImportsFromReCache = /* @__PURE__ */ new Map();
|
|
352
373
|
function getFileImportsFromRes(name) {
|
|
353
374
|
let cached = fileImportsFromReCache.get(name);
|
|
@@ -358,4 +379,4 @@ function getFileImportsFromRes(name) {
|
|
|
358
379
|
return cached;
|
|
359
380
|
}
|
|
360
381
|
//#endregion
|
|
361
|
-
export {
|
|
382
|
+
export { resolveBarrelReExport as a, getReExportedSourceName as i, fileImportsFrom as n, resolveBarrelReExportBinding as o, findImportSource as r, Logger as s, fileExports as t };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as CollectedWarning, c as MarkerFileContext, l as defineAdapter, n as TransformMode, o as AdapterInput, s as ImportSource } from "./transform-types-
|
|
1
|
+
import { a as CollectedWarning, c as MarkerFileContext, l as defineAdapter, n as TransformMode, o as AdapterInput, s as ImportSource } from "./transform-types-Bl-oPkjl.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/run.d.ts
|
|
4
4
|
interface RunTransformOptions {
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as
|
|
3
|
-
import { r as extractStyledDefBasesFromSource } from "./compute-leaf-set-
|
|
1
|
+
import { a as defineAdapter, c as assertValidAdapterInput, l as describeValue, r as mergeMarkerDeclarations, t as detectExportedComponentSxProp } from "./wrapped-component-interface-yvZ-_HMO.mjs";
|
|
2
|
+
import { a as resolveBarrelReExport, s as Logger } from "./extract-external-interface-B61N1a6q.mjs";
|
|
3
|
+
import { r as extractStyledDefBasesFromSource } from "./compute-leaf-set-Be5Cgxnr.mjs";
|
|
4
4
|
import { n as toRealPath } from "./path-utils-BIpoL4Ue.mjs";
|
|
5
5
|
import jscodeshift from "jscodeshift";
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -146,10 +146,10 @@ async function runTransform(options) {
|
|
|
146
146
|
`Pattern(s): ${consumerPatterns.join(", ")}`,
|
|
147
147
|
"Check that the glob pattern is correct and files exist."
|
|
148
148
|
].join("\n"));
|
|
149
|
-
const { createModuleResolver } = await import("./resolve-imports-
|
|
149
|
+
const { createModuleResolver } = await import("./resolve-imports-DgSAddIF.mjs").then((n) => n.n);
|
|
150
150
|
const sharedResolver = createModuleResolver();
|
|
151
151
|
filePaths = orderFilesByLocalImportDependencies(filePaths, sharedResolver, toRealPath);
|
|
152
|
-
const { runPrepass } = await import("./run-prepass-
|
|
152
|
+
const { runPrepass } = await import("./run-prepass-J4t6jkCI.mjs");
|
|
153
153
|
const absoluteFiles = filePaths.map((f) => resolve(f));
|
|
154
154
|
const absoluteConsumers = consumerFilePaths.map((f) => resolve(f));
|
|
155
155
|
let prepassResult;
|
|
@@ -180,6 +180,7 @@ async function runTransform(options) {
|
|
|
180
180
|
};
|
|
181
181
|
}
|
|
182
182
|
const transformedFiles = /* @__PURE__ */ new Set();
|
|
183
|
+
const transformedFileSources = /* @__PURE__ */ new Map();
|
|
183
184
|
const crossFilePrepassResult = {
|
|
184
185
|
...prepassResult.crossFileInfo,
|
|
185
186
|
transformedFiles
|
|
@@ -243,12 +244,16 @@ async function runTransform(options) {
|
|
|
243
244
|
if (!resolvedImport) return;
|
|
244
245
|
const resolvedPath = toRealPath(resolvedImport);
|
|
245
246
|
const definitionSourcePath = resolveExistingSourcePath(resolveBarrelReExport(resolvedPath, ctx.importedName, prepassResolve, cachedRead) ?? resolvedPath);
|
|
246
|
-
if (!transformedFiles.has(toRealPath(definitionSourcePath))) return;
|
|
247
247
|
const autoInterfaceNames = ctx.importedName === "default" ? [ctx.localName, ctx.importedName] : [ctx.importedName];
|
|
248
248
|
const styledDefinitionNames = getStyledDefinitionNames(definitionSourcePath);
|
|
249
249
|
const sourceComponentNames = ctx.importedName === "default" ? [ctx.localName, getDefaultExportedName(definitionSourcePath)].filter((name) => typeof name === "string") : [ctx.importedName];
|
|
250
|
-
|
|
251
|
-
|
|
250
|
+
if (sourceComponentNames.some((name) => detectExportedComponentSxProp({
|
|
251
|
+
absolutePath: definitionSourcePath,
|
|
252
|
+
componentName: name,
|
|
253
|
+
sourceOverrides: transformedFileSources
|
|
254
|
+
}))) return { acceptsSx: true };
|
|
255
|
+
if (!transformedFiles.has(toRealPath(definitionSourcePath))) return;
|
|
256
|
+
if (!sourceComponentNames.some((name) => styledDefinitionNames.has(name))) return;
|
|
252
257
|
return autoInterfaceNames.map((name) => lookupAutoExternalInterface(definitionSourcePath, name)).find((result) => result !== void 0)?.styles ? { acceptsSx: true } : void 0;
|
|
253
258
|
}
|
|
254
259
|
};
|
|
@@ -297,7 +302,7 @@ async function runTransform(options) {
|
|
|
297
302
|
sidecarFiles,
|
|
298
303
|
bridgeResults,
|
|
299
304
|
transformedFiles,
|
|
300
|
-
transformedFileSources
|
|
305
|
+
transformedFileSources,
|
|
301
306
|
transientPropRenames,
|
|
302
307
|
allowPartialMigration: options.allowPartialMigration ?? (leavesOnly ? true : false),
|
|
303
308
|
transformMode: leavesOnly ? "leavesOnly" : options.transformMode ?? "all",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { n as extractStyledDefBasesFromAstProgram, r as extractStyledDefBasesFromSource, t as computeGlobalLeafKeys } from "./compute-leaf-set-
|
|
1
|
+
import { a as resolveBarrelReExport, n as fileImportsFrom, o as resolveBarrelReExportBinding, r as findImportSource, s as Logger, t as fileExports } from "./extract-external-interface-B61N1a6q.mjs";
|
|
2
|
+
import { n as extractStyledDefBasesFromAstProgram, r as extractStyledDefBasesFromSource, t as computeGlobalLeafKeys } from "./compute-leaf-set-Be5Cgxnr.mjs";
|
|
3
3
|
import { r as escapeRegex } from "./string-utils-DD9wdRHW.mjs";
|
|
4
4
|
import { a as PLACEHOLDER_RE, i as readStaticJsxLiteral, n as createComponentPropUsageInfo, r as mergeComponentPropUsage, t as KNOWN_NON_ELEMENT_PROPS } from "./prop-usage-D6ZiDfzz.mjs";
|
|
5
5
|
import { t as isSelectorContext } from "./selector-context-heuristic-6_jSRGkZ.mjs";
|
|
@@ -229,6 +229,10 @@ function buildImportMapFromNodes(importNodes) {
|
|
|
229
229
|
source: sourceValue,
|
|
230
230
|
importedName: "default"
|
|
231
231
|
});
|
|
232
|
+
else if (spec.type === "ImportNamespaceSpecifier") map.set(localName, {
|
|
233
|
+
source: sourceValue,
|
|
234
|
+
importedName: "*"
|
|
235
|
+
});
|
|
232
236
|
else if (spec.type === "ImportSpecifier") {
|
|
233
237
|
const importedName = getNodeName(spec.imported) ?? localName;
|
|
234
238
|
map.set(localName, {
|
|
@@ -533,14 +537,19 @@ async function runPrepass(options) {
|
|
|
533
537
|
const rgHits = createExternalInterface ? rgClassNameStyleFilter(uniqueAllFiles) : void 0;
|
|
534
538
|
const jsxHits = rgJsxComponentFilter(uniqueAllFiles);
|
|
535
539
|
const scanAndRecord = (filePath, source) => {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
540
|
+
const jsxScan = scanConsumerJsxUsages(filePath, source, allStyledNames, parser);
|
|
541
|
+
if (createExternalInterface) for (const result of jsxScan.propResults) {
|
|
542
|
+
const usage = {
|
|
543
|
+
filePath,
|
|
544
|
+
importSource: result.importSource
|
|
545
|
+
};
|
|
546
|
+
if (result.className || result.style || result.spreadProps) addConsumerUsage(classNameStyleUsages, result.name, usage);
|
|
547
|
+
if (result.className) addConsumerUsage(classNameUsages, result.name, usage);
|
|
548
|
+
if (result.style) addConsumerUsage(styleUsages, result.name, usage);
|
|
549
|
+
if (result.elementProps) addConsumerUsage(elementPropUsages, result.name, usage);
|
|
550
|
+
if (result.spreadProps) addConsumerUsage(spreadPropUsages, result.name, usage);
|
|
542
551
|
}
|
|
543
|
-
for (const usage of
|
|
552
|
+
for (const usage of jsxScan.staticPropUsages) {
|
|
544
553
|
const entries = propUsageCandidates.get(usage.name) ?? [];
|
|
545
554
|
entries.push(usage);
|
|
546
555
|
propUsageCandidates.set(usage.name, entries);
|
|
@@ -584,7 +593,7 @@ async function runPrepass(options) {
|
|
|
584
593
|
}
|
|
585
594
|
return entry;
|
|
586
595
|
};
|
|
587
|
-
const
|
|
596
|
+
const matchUsageFilesToDefinitions = (usages, field) => {
|
|
588
597
|
if (usages.size === 0) return;
|
|
589
598
|
for (const [defFile, names] of styledDefFiles) {
|
|
590
599
|
const defSrc = cachedRead(defFile);
|
|
@@ -596,20 +605,32 @@ async function runPrepass(options) {
|
|
|
596
605
|
ensure(defFile, name)[field] = true;
|
|
597
606
|
continue;
|
|
598
607
|
}
|
|
599
|
-
for (const usageFile of usageFiles) if (fileImportsFrom(cachedRead(usageFile), usageFile, name, defFile, resolve$1)) {
|
|
608
|
+
for (const usageFile of usageFiles) if (fileImportsFrom(cachedRead(usageFile), usageFile, name, defFile, resolve$1, cachedRead)) {
|
|
600
609
|
ensure(defFile, name)[field] = true;
|
|
601
610
|
break;
|
|
602
611
|
}
|
|
603
612
|
}
|
|
604
613
|
}
|
|
605
614
|
};
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
615
|
+
const matchConsumerUsagesToDefinitions = (usages, field) => {
|
|
616
|
+
if (usages.size === 0) return;
|
|
617
|
+
for (const [defFile, names] of styledDefFiles) {
|
|
618
|
+
const defSrc = cachedRead(defFile);
|
|
619
|
+
for (const name of names) {
|
|
620
|
+
const usageRefs = usages.get(name);
|
|
621
|
+
if (!usageRefs) continue;
|
|
622
|
+
if (!fileExports(defSrc, name)) continue;
|
|
623
|
+
if (usageRefs.some((usageRef) => consumerUsageReferencesDefinition(usageRef, name, defFile, cachedRead, resolve$1))) ensure(defFile, name)[field] = true;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
matchUsageFilesToDefinitions(asUsages, "as");
|
|
628
|
+
matchUsageFilesToDefinitions(refUsages, "ref");
|
|
629
|
+
matchConsumerUsagesToDefinitions(classNameStyleUsages, "styles");
|
|
630
|
+
matchConsumerUsagesToDefinitions(classNameUsages, "className");
|
|
631
|
+
matchConsumerUsagesToDefinitions(styleUsages, "style");
|
|
632
|
+
matchConsumerUsagesToDefinitions(elementPropUsages, "elementProps");
|
|
633
|
+
matchConsumerUsagesToDefinitions(spreadPropUsages, "spreadProps");
|
|
613
634
|
{
|
|
614
635
|
const seen = /* @__PURE__ */ new Set();
|
|
615
636
|
for (const { file, name } of styledCallUsages) {
|
|
@@ -759,8 +780,27 @@ function importTextMentionsIdentifier(importText, identifier) {
|
|
|
759
780
|
}
|
|
760
781
|
return re.test(importText);
|
|
761
782
|
}
|
|
762
|
-
|
|
763
|
-
const
|
|
783
|
+
function addConsumerUsage(map, name, usage) {
|
|
784
|
+
const entries = map.get(name) ?? [];
|
|
785
|
+
if (!entries.some((entry) => entry.filePath === usage.filePath && entry.importSource === usage.importSource)) {
|
|
786
|
+
entries.push(usage);
|
|
787
|
+
map.set(name, entries);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
function consumerUsageReferencesDefinition(usage, name, defFile, cachedRead, resolve) {
|
|
791
|
+
if (usage.importSource) return importSourceReferencesDefinition(usage.importSource, usage.filePath, name, defFile, resolve, cachedRead);
|
|
792
|
+
if (usage.filePath === defFile) return true;
|
|
793
|
+
return fileImportsFrom(cachedRead(usage.filePath), usage.filePath, name, defFile, resolve, cachedRead);
|
|
794
|
+
}
|
|
795
|
+
function importSourceReferencesDefinition(importSource, usageFile, name, defFile, resolve$2, cachedRead) {
|
|
796
|
+
const resolved = resolve$2(importSource, usageFile);
|
|
797
|
+
if (!resolved) return false;
|
|
798
|
+
if (resolve(resolved) === resolve(defFile)) return true;
|
|
799
|
+
const binding = resolveBarrelReExportBinding(resolved, name, resolve$2, cachedRead);
|
|
800
|
+
return binding !== null && resolve(binding.filePath) === resolve(defFile);
|
|
801
|
+
}
|
|
802
|
+
/** Quick pre-check: does this source mention JSX that might use external consumer props? */
|
|
803
|
+
const CONSUMER_PROPS_QUICK_RE = /<(?:[A-Z]|[A-Za-z_$][A-Za-z0-9_$]*\.)|\b(className|style)\s*[={]|\{\.\.\./;
|
|
764
804
|
/** Matches `import { Original as Local, ... }` — captures original and local names. */
|
|
765
805
|
const IMPORT_ALIAS_ENTRY_RE = /\b(\w+)\s+as\s+(\w+)/g;
|
|
766
806
|
/**
|
|
@@ -783,99 +823,166 @@ function buildLocalToImportedMap(source) {
|
|
|
783
823
|
/**
|
|
784
824
|
* Scan source for JSX usage of specific components with className, style,
|
|
785
825
|
* element-specific props, or JSX spread.
|
|
786
|
-
* Uses
|
|
787
|
-
*
|
|
788
|
-
* alternation regex when there are hundreds of component names.
|
|
789
|
-
* Handles aliased imports (e.g., `import { Alert as MyAlert }`) by resolving
|
|
790
|
-
* local tag names back to their original exported names.
|
|
826
|
+
* Uses the prepass parser so comments and string literals cannot be mistaken
|
|
827
|
+
* for real JSX consumers.
|
|
791
828
|
*/
|
|
792
|
-
function
|
|
793
|
-
if (!
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
const tagName = m[1];
|
|
798
|
-
const attrText = m[2] ?? "";
|
|
799
|
-
if (!tagName) continue;
|
|
800
|
-
let resolvedName;
|
|
801
|
-
if (componentNames.has(tagName)) resolvedName = tagName;
|
|
802
|
-
else {
|
|
803
|
-
aliasMap ??= buildLocalToImportedMap(source);
|
|
804
|
-
const originalName = aliasMap.get(tagName);
|
|
805
|
-
if (originalName && componentNames.has(originalName)) resolvedName = originalName;
|
|
806
|
-
}
|
|
807
|
-
if (!resolvedName) continue;
|
|
808
|
-
if (!/\b(?:className|style)\s*[={]/.test(attrText) && !/\{\.\.\./.test(attrText)) continue;
|
|
809
|
-
let entry = resultMap.get(resolvedName);
|
|
810
|
-
if (!entry) {
|
|
811
|
-
entry = {
|
|
812
|
-
name: resolvedName,
|
|
813
|
-
className: false,
|
|
814
|
-
style: false,
|
|
815
|
-
elementProps: false,
|
|
816
|
-
spreadProps: false
|
|
817
|
-
};
|
|
818
|
-
resultMap.set(resolvedName, entry);
|
|
819
|
-
}
|
|
820
|
-
if (/\bclassName\s*[={]/.test(attrText)) entry.className = true;
|
|
821
|
-
if (/\bstyle\s*[={]/.test(attrText)) entry.style = true;
|
|
822
|
-
if (/\{\.\.\./.test(attrText)) entry.spreadProps = true;
|
|
823
|
-
for (const pm of attrText.matchAll(/\b([a-z][a-zA-Z-]*)(?=\s*[={]|\s+[a-z]|\s*$)/gi)) {
|
|
824
|
-
const propName = pm[1];
|
|
825
|
-
if (!KNOWN_NON_ELEMENT_PROPS.has(propName) && !propName.startsWith("$")) {
|
|
826
|
-
entry.elementProps = true;
|
|
827
|
-
break;
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
return [...resultMap.values()];
|
|
832
|
-
}
|
|
833
|
-
function scanConsumerStaticPropUsages(filePath, source, componentNames, parser) {
|
|
834
|
-
if (!/<[A-Z]/.test(source)) return [];
|
|
829
|
+
function scanConsumerJsxUsages(filePath, source, componentNames, parser) {
|
|
830
|
+
if (!CONSUMER_PROPS_QUICK_RE.test(source)) return {
|
|
831
|
+
propResults: [],
|
|
832
|
+
staticPropUsages: []
|
|
833
|
+
};
|
|
835
834
|
let ast;
|
|
836
835
|
try {
|
|
837
836
|
ast = parser.parse(source);
|
|
838
837
|
} catch {
|
|
839
|
-
return
|
|
838
|
+
return {
|
|
839
|
+
propResults: scanConsumerPropsRegexFallback(source, componentNames),
|
|
840
|
+
staticPropUsages: []
|
|
841
|
+
};
|
|
840
842
|
}
|
|
841
843
|
const importNodes = [];
|
|
842
844
|
const jsxOpenings = [];
|
|
843
845
|
walkForImportsAndJsxOpenings(ast.program ?? ast, importNodes, jsxOpenings);
|
|
844
846
|
const importMap = buildImportMapFromNodes(importNodes);
|
|
845
|
-
const
|
|
847
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
848
|
+
const staticPropUsages = [];
|
|
846
849
|
for (const opening of jsxOpenings) {
|
|
847
|
-
const
|
|
848
|
-
if (!
|
|
849
|
-
const
|
|
850
|
-
const
|
|
851
|
-
if (
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
850
|
+
const resolvedComponent = resolveJsxOpeningComponent(opening.name, importMap, componentNames);
|
|
851
|
+
if (!resolvedComponent) continue;
|
|
852
|
+
const openingUsage = readConsumerOpeningUsage(opening);
|
|
853
|
+
const { externalProps } = openingUsage;
|
|
854
|
+
if (externalProps.className || externalProps.style || externalProps.spreadProps || externalProps.elementProps) {
|
|
855
|
+
const key = `${resolvedComponent.name}\0${resolvedComponent.importSource ?? ""}`;
|
|
856
|
+
let entry = resultMap.get(key);
|
|
857
|
+
if (!entry) {
|
|
858
|
+
entry = {
|
|
859
|
+
name: resolvedComponent.name,
|
|
860
|
+
importSource: resolvedComponent.importSource,
|
|
861
|
+
className: false,
|
|
862
|
+
style: false,
|
|
863
|
+
elementProps: false,
|
|
864
|
+
spreadProps: false
|
|
865
|
+
};
|
|
866
|
+
resultMap.set(key, entry);
|
|
859
867
|
}
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
868
|
+
entry.className ||= externalProps.className;
|
|
869
|
+
entry.style ||= externalProps.style;
|
|
870
|
+
entry.spreadProps ||= externalProps.spreadProps;
|
|
871
|
+
entry.elementProps ||= externalProps.elementProps;
|
|
872
|
+
}
|
|
873
|
+
staticPropUsages.push({
|
|
874
|
+
name: resolvedComponent.name,
|
|
875
|
+
filePath,
|
|
876
|
+
importSource: resolvedComponent.importSource,
|
|
877
|
+
usage: openingUsage.staticUsage
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
return {
|
|
881
|
+
propResults: [...resultMap.values()],
|
|
882
|
+
staticPropUsages
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
function resolveJsxOpeningComponent(name, importMap, componentNames) {
|
|
886
|
+
const localName = getJsxOpeningIdentifierName(name);
|
|
887
|
+
if (localName) {
|
|
888
|
+
if (componentNames.has(localName)) return { name: localName };
|
|
889
|
+
const importEntry = importMap.get(localName);
|
|
890
|
+
return importEntry && componentNames.has(importEntry.importedName) ? {
|
|
891
|
+
name: importEntry.importedName,
|
|
892
|
+
importSource: importEntry.source
|
|
893
|
+
} : void 0;
|
|
894
|
+
}
|
|
895
|
+
const member = getJsxMemberNameParts(name);
|
|
896
|
+
if (!member || !componentNames.has(member.propertyName)) return;
|
|
897
|
+
const importEntry = importMap.get(member.objectName);
|
|
898
|
+
return importEntry?.importedName === "*" ? {
|
|
899
|
+
name: member.propertyName,
|
|
900
|
+
importSource: importEntry.source
|
|
901
|
+
} : void 0;
|
|
902
|
+
}
|
|
903
|
+
function readConsumerOpeningUsage(opening) {
|
|
904
|
+
const externalProps = {
|
|
905
|
+
className: false,
|
|
906
|
+
style: false,
|
|
907
|
+
elementProps: false,
|
|
908
|
+
spreadProps: false
|
|
909
|
+
};
|
|
910
|
+
const props = {};
|
|
911
|
+
let hasSpread = false;
|
|
912
|
+
for (const attr of opening.attributes ?? []) {
|
|
913
|
+
if (!attr) continue;
|
|
914
|
+
if (attr.type === "JSXSpreadAttribute") {
|
|
915
|
+
externalProps.spreadProps = true;
|
|
916
|
+
hasSpread = true;
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
if (attr.type !== "JSXAttribute") continue;
|
|
920
|
+
const propName = getJsxAttributeName(attr.name);
|
|
921
|
+
if (!propName) continue;
|
|
922
|
+
if (propName === "className") externalProps.className = true;
|
|
923
|
+
else if (propName === "style") externalProps.style = true;
|
|
924
|
+
else if (isElementConsumerProp(propName)) externalProps.elementProps = true;
|
|
925
|
+
if (!KNOWN_NON_ELEMENT_PROPS.has(propName)) {
|
|
863
926
|
const value = readStaticJsxLiteral(attr);
|
|
864
927
|
props[propName] = value === void 0 ? { kind: "unknown" } : {
|
|
865
928
|
kind: "static",
|
|
866
929
|
value
|
|
867
930
|
};
|
|
868
931
|
}
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
932
|
+
}
|
|
933
|
+
return {
|
|
934
|
+
externalProps,
|
|
935
|
+
staticUsage: {
|
|
936
|
+
props,
|
|
937
|
+
hasSpread
|
|
938
|
+
}
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
function isElementConsumerProp(propName) {
|
|
942
|
+
return !KNOWN_NON_ELEMENT_PROPS.has(propName) && !propName.startsWith("$");
|
|
943
|
+
}
|
|
944
|
+
function scanConsumerPropsRegexFallback(source, componentNames) {
|
|
945
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
946
|
+
let aliasMap;
|
|
947
|
+
for (const match of source.matchAll(/<([A-Z][A-Za-z0-9]*)\b([^<>]*?)(?:\/>|>)/gs)) {
|
|
948
|
+
const tagName = match[1];
|
|
949
|
+
const attrText = match[2] ?? "";
|
|
950
|
+
if (!tagName) continue;
|
|
951
|
+
const resolvedName = componentNames.has(tagName) ? tagName : (() => {
|
|
952
|
+
aliasMap ??= buildLocalToImportedMap(source);
|
|
953
|
+
const originalName = aliasMap.get(tagName);
|
|
954
|
+
return originalName && componentNames.has(originalName) ? originalName : void 0;
|
|
955
|
+
})();
|
|
956
|
+
if (!resolvedName) continue;
|
|
957
|
+
const className = /\bclassName\s*[={]/.test(attrText);
|
|
958
|
+
const style = /\bstyle\s*[={]/.test(attrText);
|
|
959
|
+
const spreadProps = /\{\.\.\./.test(attrText);
|
|
960
|
+
let elementProps = false;
|
|
961
|
+
for (const propMatch of attrText.matchAll(/\b([a-z][a-zA-Z-]*)(?=\s*[={]|\s+[a-z]|\s*$)/gi)) {
|
|
962
|
+
const propName = propMatch[1];
|
|
963
|
+
if (isElementConsumerProp(propName)) {
|
|
964
|
+
elementProps = true;
|
|
965
|
+
break;
|
|
875
966
|
}
|
|
876
|
-
}
|
|
967
|
+
}
|
|
968
|
+
if (!className && !style && !spreadProps && !elementProps) continue;
|
|
969
|
+
let entry = resultMap.get(resolvedName);
|
|
970
|
+
if (!entry) {
|
|
971
|
+
entry = {
|
|
972
|
+
name: resolvedName,
|
|
973
|
+
className: false,
|
|
974
|
+
style: false,
|
|
975
|
+
elementProps: false,
|
|
976
|
+
spreadProps: false
|
|
977
|
+
};
|
|
978
|
+
resultMap.set(resolvedName, entry);
|
|
979
|
+
}
|
|
980
|
+
entry.className ||= className;
|
|
981
|
+
entry.style ||= style;
|
|
982
|
+
entry.spreadProps ||= spreadProps;
|
|
983
|
+
entry.elementProps ||= elementProps;
|
|
877
984
|
}
|
|
878
|
-
return
|
|
985
|
+
return [...resultMap.values()];
|
|
879
986
|
}
|
|
880
987
|
function buildPropUsageByFile(args) {
|
|
881
988
|
const { styledDefFiles, propUsageCandidates, cachedRead, resolve, toRealPath } = args;
|
|
@@ -886,8 +993,7 @@ function buildPropUsageByFile(args) {
|
|
|
886
993
|
const candidates = propUsageCandidates.get(name);
|
|
887
994
|
if (!candidates || !fileExports(defSrc, name)) continue;
|
|
888
995
|
for (const candidate of candidates) {
|
|
889
|
-
|
|
890
|
-
if (usageFile !== defFile && !fileImportsFrom(cachedRead(usageFile), usageFile, name, defFile, resolve)) continue;
|
|
996
|
+
if (!consumerUsageReferencesDefinition(candidate, name, defFile, cachedRead, resolve)) continue;
|
|
891
997
|
mergeComponentPropUsage(getOrCreateComponentPropUsage(getOrCreatePropUsageFileMap(propUsageByFile, toRealPath(defFile)), name), candidate.usage);
|
|
892
998
|
}
|
|
893
999
|
}
|
|
@@ -917,10 +1023,7 @@ function walkForImportsAndJsxOpenings(node, imports, jsxOpenings) {
|
|
|
917
1023
|
imports.push(n);
|
|
918
1024
|
return;
|
|
919
1025
|
}
|
|
920
|
-
if (n.type === "JSXOpeningElement")
|
|
921
|
-
jsxOpenings.push(n);
|
|
922
|
-
return;
|
|
923
|
-
}
|
|
1026
|
+
if (n.type === "JSXOpeningElement") jsxOpenings.push(n);
|
|
924
1027
|
for (const key of Object.keys(n)) {
|
|
925
1028
|
if (key === "type" || key === "start" || key === "end" || key === "loc" || key === "leadingComments" || key === "trailingComments") continue;
|
|
926
1029
|
const val = n[key];
|
|
@@ -933,6 +1036,16 @@ function getJsxOpeningIdentifierName(name) {
|
|
|
933
1036
|
if (name.type === "JSXIdentifier" && typeof name.name === "string") return name.name;
|
|
934
1037
|
return null;
|
|
935
1038
|
}
|
|
1039
|
+
function getJsxMemberNameParts(name) {
|
|
1040
|
+
if (!name || name.type !== "JSXMemberExpression") return null;
|
|
1041
|
+
const object = name.object;
|
|
1042
|
+
const property = name.property;
|
|
1043
|
+
if (object?.type !== "JSXIdentifier" || typeof object.name !== "string" || property?.type !== "JSXIdentifier" || typeof property.name !== "string") return null;
|
|
1044
|
+
return {
|
|
1045
|
+
objectName: object.name,
|
|
1046
|
+
propertyName: property.name
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
936
1049
|
function getJsxAttributeName(name) {
|
|
937
1050
|
if (!name) return null;
|
|
938
1051
|
if (name.type === "JSXIdentifier" && typeof name.name === "string") return name.name;
|
|
@@ -967,7 +1080,7 @@ function rgClassNameStyleFilter(files) {
|
|
|
967
1080
|
return;
|
|
968
1081
|
}
|
|
969
1082
|
}
|
|
970
|
-
/** Use ripgrep to find files with PascalCase JSX tags. */
|
|
1083
|
+
/** Use ripgrep to find files with PascalCase or namespace JSX tags. */
|
|
971
1084
|
function rgJsxComponentFilter(files) {
|
|
972
1085
|
const dirs = deduplicateParentDirs(files);
|
|
973
1086
|
if (dirs.length === 0) return;
|
|
@@ -982,7 +1095,7 @@ function rgJsxComponentFilter(files) {
|
|
|
982
1095
|
"*.mjs",
|
|
983
1096
|
"*.cjs"
|
|
984
1097
|
].map((glob) => `--glob ${shellQuote(glob)}`).join(" ");
|
|
985
|
-
const output = execSync(`rg -l ${shellQuote(String.raw`<[A-Z]`)} ${globArgs} ${dirs.map(shellQuote).join(" ")}`, {
|
|
1098
|
+
const output = execSync(`rg -l ${shellQuote(String.raw`<([A-Z]|[A-Za-z_$][A-Za-z0-9_$]*\.)`)} ${globArgs} ${dirs.map(shellQuote).join(" ")}`, {
|
|
986
1099
|
encoding: "utf-8",
|
|
987
1100
|
maxBuffer: 10 * 1024 * 1024
|
|
988
1101
|
});
|