oxlint-plugin-react-doctor 0.6.1-dev.f07ee37 → 0.6.1
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.js +91 -94
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -212,16 +212,8 @@ const sliceBelowSourceRoot = (filename) => {
|
|
|
212
212
|
if (cutAt < 0) return filename;
|
|
213
213
|
return filename.slice(cutAt);
|
|
214
214
|
};
|
|
215
|
-
let lastFilename;
|
|
216
|
-
let lastResult = false;
|
|
217
215
|
const isTestlikeFilename = (rawFilename) => {
|
|
218
216
|
if (!rawFilename) return false;
|
|
219
|
-
if (rawFilename === lastFilename) return lastResult;
|
|
220
|
-
lastFilename = rawFilename;
|
|
221
|
-
lastResult = computeIsTestlikeFilename(rawFilename);
|
|
222
|
-
return lastResult;
|
|
223
|
-
};
|
|
224
|
-
const computeIsTestlikeFilename = (rawFilename) => {
|
|
225
217
|
const filename = rawFilename.replaceAll("\\", "/");
|
|
226
218
|
const lastSlash = filename.lastIndexOf("/");
|
|
227
219
|
const basename = lastSlash === -1 ? filename : filename.slice(lastSlash + 1);
|
|
@@ -991,8 +983,16 @@ const buildBindingIndex = (root) => {
|
|
|
991
983
|
}
|
|
992
984
|
}
|
|
993
985
|
}
|
|
986
|
+
const nodeRecord = node;
|
|
987
|
+
for (const key of Object.keys(nodeRecord)) {
|
|
988
|
+
if (key === "parent") continue;
|
|
989
|
+
const child = nodeRecord[key];
|
|
990
|
+
if (Array.isArray(child)) {
|
|
991
|
+
for (const item of child) if (isAstNode(item)) visit(item);
|
|
992
|
+
} else if (isAstNode(child)) visit(child);
|
|
993
|
+
}
|
|
994
994
|
};
|
|
995
|
-
|
|
995
|
+
visit(root);
|
|
996
996
|
return out;
|
|
997
997
|
};
|
|
998
998
|
const programRootCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -1423,40 +1423,49 @@ const getElementType = (openingElement, settings) => {
|
|
|
1423
1423
|
//#region src/plugin/utils/find-import-source-for-name.ts
|
|
1424
1424
|
const collectFromProgram = (programRoot) => {
|
|
1425
1425
|
const lookup = /* @__PURE__ */ new Map();
|
|
1426
|
-
const
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
if (typeof local.name !== "string") continue;
|
|
1436
|
-
if (specifier.type === "ImportDefaultSpecifier") lookup.set(local.name, {
|
|
1437
|
-
source,
|
|
1438
|
-
imported: null,
|
|
1439
|
-
isDefault: true,
|
|
1440
|
-
isNamespace: false
|
|
1441
|
-
});
|
|
1442
|
-
else if (specifier.type === "ImportNamespaceSpecifier") lookup.set(local.name, {
|
|
1443
|
-
source,
|
|
1444
|
-
imported: null,
|
|
1445
|
-
isDefault: false,
|
|
1446
|
-
isNamespace: true
|
|
1447
|
-
});
|
|
1448
|
-
else if (specifier.type === "ImportSpecifier") {
|
|
1449
|
-
const importedNode = specifier.imported;
|
|
1450
|
-
const importedName = importedNode?.name ?? (typeof importedNode?.value === "string" ? importedNode.value : null);
|
|
1451
|
-
lookup.set(local.name, {
|
|
1426
|
+
const visit = (node) => {
|
|
1427
|
+
if (node.type === "ImportDeclaration" && "source" in node && node.source) {
|
|
1428
|
+
const source = node.source.value;
|
|
1429
|
+
if (typeof source !== "string") return;
|
|
1430
|
+
if ("specifiers" in node && Array.isArray(node.specifiers)) for (const specifier of node.specifiers) {
|
|
1431
|
+
if (!("local" in specifier) || !specifier.local) continue;
|
|
1432
|
+
const local = specifier.local;
|
|
1433
|
+
if (typeof local.name !== "string") continue;
|
|
1434
|
+
if (specifier.type === "ImportDefaultSpecifier") lookup.set(local.name, {
|
|
1452
1435
|
source,
|
|
1453
|
-
imported:
|
|
1454
|
-
isDefault:
|
|
1436
|
+
imported: null,
|
|
1437
|
+
isDefault: true,
|
|
1455
1438
|
isNamespace: false
|
|
1456
1439
|
});
|
|
1440
|
+
else if (specifier.type === "ImportNamespaceSpecifier") lookup.set(local.name, {
|
|
1441
|
+
source,
|
|
1442
|
+
imported: null,
|
|
1443
|
+
isDefault: false,
|
|
1444
|
+
isNamespace: true
|
|
1445
|
+
});
|
|
1446
|
+
else if (specifier.type === "ImportSpecifier") {
|
|
1447
|
+
const importedNode = specifier.imported;
|
|
1448
|
+
const importedName = importedNode?.name ?? (typeof importedNode?.value === "string" ? importedNode.value : null);
|
|
1449
|
+
lookup.set(local.name, {
|
|
1450
|
+
source,
|
|
1451
|
+
imported: importedName,
|
|
1452
|
+
isDefault: false,
|
|
1453
|
+
isNamespace: false
|
|
1454
|
+
});
|
|
1455
|
+
}
|
|
1457
1456
|
}
|
|
1457
|
+
return;
|
|
1458
1458
|
}
|
|
1459
|
-
|
|
1459
|
+
const nodeRecord = node;
|
|
1460
|
+
for (const key of Object.keys(nodeRecord)) {
|
|
1461
|
+
if (key === "parent") continue;
|
|
1462
|
+
const child = nodeRecord[key];
|
|
1463
|
+
if (Array.isArray(child)) {
|
|
1464
|
+
for (const item of child) if (isAstNode(item)) visit(item);
|
|
1465
|
+
} else if (isAstNode(child)) visit(child);
|
|
1466
|
+
}
|
|
1467
|
+
};
|
|
1468
|
+
visit(programRoot);
|
|
1460
1469
|
return lookup;
|
|
1461
1470
|
};
|
|
1462
1471
|
const importLookupCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -1470,12 +1479,6 @@ const getImportLookup = (node) => {
|
|
|
1470
1479
|
}
|
|
1471
1480
|
return cached;
|
|
1472
1481
|
};
|
|
1473
|
-
const hasImportFromModules = (contextNode, moduleSources) => {
|
|
1474
|
-
const lookup = getImportLookup(contextNode);
|
|
1475
|
-
if (!lookup) return false;
|
|
1476
|
-
for (const info of lookup.values()) if (moduleSources.includes(info.source)) return true;
|
|
1477
|
-
return false;
|
|
1478
|
-
};
|
|
1479
1482
|
const isImportedFromModule = (contextNode, localIdentifierName, moduleSource) => {
|
|
1480
1483
|
const lookup = getImportLookup(contextNode);
|
|
1481
1484
|
if (!lookup) return false;
|
|
@@ -1628,7 +1631,6 @@ const normalizeFilename = (filename) => filename.replaceAll("\\", "/");
|
|
|
1628
1631
|
//#region src/plugin/utils/is-generated-image-render-context.ts
|
|
1629
1632
|
const IMAGE_RESPONSE_MODULES = ["next/og", "@vercel/og"];
|
|
1630
1633
|
const SATORI_MODULE = "satori";
|
|
1631
|
-
const GENERATED_IMAGE_MODULES = [...IMAGE_RESPONSE_MODULES, SATORI_MODULE];
|
|
1632
1634
|
const generatedImageJsxCache = /* @__PURE__ */ new WeakMap();
|
|
1633
1635
|
const isGeneratedImageRenderFilename = (rawFilename) => {
|
|
1634
1636
|
if (!rawFilename) return false;
|
|
@@ -1772,7 +1774,7 @@ const collectGeneratedImageJsxNodes = (programRoot) => {
|
|
|
1772
1774
|
const cached = generatedImageJsxCache.get(programRoot);
|
|
1773
1775
|
if (cached) return cached;
|
|
1774
1776
|
const generatedImageJsxNodes = /* @__PURE__ */ new WeakSet();
|
|
1775
|
-
|
|
1777
|
+
walkAst(programRoot, (descendantNode) => {
|
|
1776
1778
|
if (!isGeneratedImageRendererCall(descendantNode)) return;
|
|
1777
1779
|
for (const argument of descendantNode.arguments) markGeneratedImageExpression(argument, programRoot, generatedImageJsxNodes, /* @__PURE__ */ new Set());
|
|
1778
1780
|
});
|
|
@@ -3965,11 +3967,9 @@ const collectScopedReferenceIdentifierNames = (node, into, shadowed) => {
|
|
|
3965
3967
|
return;
|
|
3966
3968
|
}
|
|
3967
3969
|
if (typeof node.type === "string" && node.type.startsWith("TS")) return;
|
|
3968
|
-
const
|
|
3969
|
-
for (const key of Object.keys(nodeRecord)) {
|
|
3970
|
+
for (const [key, child] of Object.entries(node)) {
|
|
3970
3971
|
if (key === "parent") continue;
|
|
3971
3972
|
if (TYPE_POSITION_CHILD_KEYS.has(key)) continue;
|
|
3972
|
-
const child = nodeRecord[key];
|
|
3973
3973
|
if (Array.isArray(child)) {
|
|
3974
3974
|
for (const item of child) if (isAstNode(item)) collectScopedReferenceIdentifierNames(item, into, shadowed);
|
|
3975
3975
|
} else if (isAstNode(child)) collectScopedReferenceIdentifierNames(child, into, shadowed);
|
|
@@ -10439,15 +10439,10 @@ const jsCombineIterations = defineRule({
|
|
|
10439
10439
|
severity: "warn",
|
|
10440
10440
|
recommendation: "Combine `.map().filter()` style chains into one pass with `.reduce()` or a `for...of` loop, so you only loop over the list once",
|
|
10441
10441
|
create: (context) => {
|
|
10442
|
-
let
|
|
10443
|
-
let generatorNamesInFile = null;
|
|
10444
|
-
const getGeneratorNamesInFile = () => {
|
|
10445
|
-
generatorNamesInFile ??= programNode ? collectGeneratorNames(programNode) : /* @__PURE__ */ new Set();
|
|
10446
|
-
return generatorNamesInFile;
|
|
10447
|
-
};
|
|
10442
|
+
let generatorNamesInFile = /* @__PURE__ */ new Set();
|
|
10448
10443
|
return {
|
|
10449
|
-
Program(
|
|
10450
|
-
|
|
10444
|
+
Program(programNode) {
|
|
10445
|
+
generatorNamesInFile = collectGeneratorNames(programNode);
|
|
10451
10446
|
},
|
|
10452
10447
|
CallExpression(node) {
|
|
10453
10448
|
if (!isNodeOfType(node.callee, "MemberExpression") || !isNodeOfType(node.callee.property, "Identifier")) return;
|
|
@@ -10469,7 +10464,7 @@ const jsCombineIterations = defineRule({
|
|
|
10469
10464
|
if (isNullFilteringPredicate(filterArgument)) return;
|
|
10470
10465
|
if (isTypePredicateArrow(filterArgument)) return;
|
|
10471
10466
|
}
|
|
10472
|
-
if (isReceiverChainIteratorRooted(innerCall.callee.object,
|
|
10467
|
+
if (isReceiverChainIteratorRooted(innerCall.callee.object, generatorNamesInFile)) return;
|
|
10473
10468
|
if (isSmallLiteralArrayRootedChain(innerCall.callee.object)) return;
|
|
10474
10469
|
if (isStringSplitRootedChain(innerCall.callee.object)) return;
|
|
10475
10470
|
context.report({
|
|
@@ -29526,18 +29521,21 @@ const classifyExport = (name, reportNode, isFunction, initializer, state) => {
|
|
|
29526
29521
|
reportNode
|
|
29527
29522
|
};
|
|
29528
29523
|
};
|
|
29529
|
-
const
|
|
29530
|
-
const
|
|
29531
|
-
const
|
|
29532
|
-
|
|
29533
|
-
const
|
|
29534
|
-
|
|
29535
|
-
|
|
29536
|
-
|
|
29537
|
-
|
|
29538
|
-
|
|
29539
|
-
|
|
29524
|
+
const collectAllNodes = (programRoot) => {
|
|
29525
|
+
const out = [];
|
|
29526
|
+
const visit = (node) => {
|
|
29527
|
+
out.push(node);
|
|
29528
|
+
const record = node;
|
|
29529
|
+
for (const key of Object.keys(record)) {
|
|
29530
|
+
if (key === "parent") continue;
|
|
29531
|
+
const child = record[key];
|
|
29532
|
+
if (Array.isArray(child)) {
|
|
29533
|
+
for (const item of child) if (isAstNode(item)) visit(item);
|
|
29534
|
+
} else if (isAstNode(child)) visit(child);
|
|
29535
|
+
}
|
|
29540
29536
|
};
|
|
29537
|
+
visit(programRoot);
|
|
29538
|
+
return out;
|
|
29541
29539
|
};
|
|
29542
29540
|
const isEntryPointFile = (filename) => {
|
|
29543
29541
|
const lastSlash = Math.max(filename.lastIndexOf("/"), filename.lastIndexOf("\\"));
|
|
@@ -29584,13 +29582,13 @@ const onlyExportComponents = defineRule({
|
|
|
29584
29582
|
};
|
|
29585
29583
|
return { Program(node) {
|
|
29586
29584
|
if (!isFileNameAllowed(normalizeFilename(context.filename ?? ""), settings.checkJS)) return;
|
|
29587
|
-
const
|
|
29585
|
+
const allNodes = collectAllNodes(node);
|
|
29588
29586
|
const exports = [];
|
|
29589
29587
|
let hasReactExport = false;
|
|
29590
29588
|
let hasAnyExports = false;
|
|
29591
29589
|
const localComponents = [];
|
|
29592
29590
|
const isExportedNodeIds = /* @__PURE__ */ new WeakSet();
|
|
29593
|
-
for (const child of
|
|
29591
|
+
for (const child of allNodes) {
|
|
29594
29592
|
if (isNodeOfType(child, "ExportAllDeclaration")) {
|
|
29595
29593
|
if (child.exportKind === "type") continue;
|
|
29596
29594
|
hasAnyExports = true;
|
|
@@ -29726,7 +29724,7 @@ const onlyExportComponents = defineRule({
|
|
|
29726
29724
|
}
|
|
29727
29725
|
return false;
|
|
29728
29726
|
};
|
|
29729
|
-
for (const child of
|
|
29727
|
+
for (const child of allNodes) {
|
|
29730
29728
|
if (isNodeOfType(child, "FunctionDeclaration") && child.id) {
|
|
29731
29729
|
if (isReactComponentName(child.id.name) && !isInsideExport(child)) localComponents.push(child.id);
|
|
29732
29730
|
}
|
|
@@ -46843,29 +46841,23 @@ const FALLBACK_CFG = {
|
|
|
46843
46841
|
enclosingFunction: () => null,
|
|
46844
46842
|
isUnconditionalFromEntry: () => false
|
|
46845
46843
|
};
|
|
46846
|
-
const scopesByProgram = /* @__PURE__ */ new WeakMap();
|
|
46847
|
-
const cfgByProgram = /* @__PURE__ */ new WeakMap();
|
|
46848
46844
|
const wrapWithSemanticContext = (rule) => ({
|
|
46849
46845
|
...rule,
|
|
46850
46846
|
create: (baseContext) => {
|
|
46851
46847
|
let programRoot = null;
|
|
46848
|
+
let cachedScopes = null;
|
|
46849
|
+
let cachedCfg = null;
|
|
46852
46850
|
const getScopes = () => {
|
|
46851
|
+
if (cachedScopes) return cachedScopes;
|
|
46853
46852
|
if (!programRoot) return buildFallbackScopes();
|
|
46854
|
-
|
|
46855
|
-
|
|
46856
|
-
scopes = analyzeScopes(programRoot);
|
|
46857
|
-
scopesByProgram.set(programRoot, scopes);
|
|
46858
|
-
}
|
|
46859
|
-
return scopes;
|
|
46853
|
+
cachedScopes = analyzeScopes(programRoot);
|
|
46854
|
+
return cachedScopes;
|
|
46860
46855
|
};
|
|
46861
46856
|
const getCfg = () => {
|
|
46857
|
+
if (cachedCfg) return cachedCfg;
|
|
46862
46858
|
if (!programRoot) return FALLBACK_CFG;
|
|
46863
|
-
|
|
46864
|
-
|
|
46865
|
-
cfg = analyzeControlFlow(programRoot);
|
|
46866
|
-
cfgByProgram.set(programRoot, cfg);
|
|
46867
|
-
}
|
|
46868
|
-
return cfg;
|
|
46859
|
+
cachedCfg = analyzeControlFlow(programRoot);
|
|
46860
|
+
return cachedCfg;
|
|
46869
46861
|
};
|
|
46870
46862
|
const enrichedContext = {
|
|
46871
46863
|
report: baseContext.report,
|
|
@@ -46880,18 +46872,23 @@ const wrapWithSemanticContext = (rule) => ({
|
|
|
46880
46872
|
return getCfg();
|
|
46881
46873
|
}
|
|
46882
46874
|
};
|
|
46875
|
+
const captureRootIfNeeded = (node) => {
|
|
46876
|
+
if (programRoot) return;
|
|
46877
|
+
programRoot = findProgramRoot(node);
|
|
46878
|
+
};
|
|
46883
46879
|
const visitors = rule.create(enrichedContext);
|
|
46884
|
-
const
|
|
46880
|
+
const wrappedVisitors = {};
|
|
46885
46881
|
for (const [nodeType, handler] of Object.entries(visitors)) {
|
|
46886
46882
|
if (typeof handler !== "function") continue;
|
|
46887
|
-
|
|
46883
|
+
wrappedVisitors[nodeType] = ((node) => {
|
|
46884
|
+
captureRootIfNeeded(node);
|
|
46885
|
+
handler(node);
|
|
46886
|
+
});
|
|
46888
46887
|
}
|
|
46889
|
-
|
|
46890
|
-
|
|
46891
|
-
|
|
46892
|
-
|
|
46893
|
-
});
|
|
46894
|
-
return passthroughVisitors;
|
|
46888
|
+
if (!visitors.Program) wrappedVisitors.Program = (node) => {
|
|
46889
|
+
captureRootIfNeeded(node);
|
|
46890
|
+
};
|
|
46891
|
+
return wrappedVisitors;
|
|
46895
46892
|
}
|
|
46896
46893
|
});
|
|
46897
46894
|
//#endregion
|