oxlint-plugin-react-doctor 0.6.2-dev.ac71a3b → 0.6.2-dev.d8628d7

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.
Files changed (2) hide show
  1. package/dist/index.js +73 -33
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -378,13 +378,18 @@ const isBrowserArtifactPath = (relativePath, isGeneratedBundle) => {
378
378
  const isConfigOrCiPath = (relativePath) => /(?:^|\/)(?:package\.json|Dockerfile|docker-compose\.ya?ml|\.github\/workflows\/[^/]+\.ya?ml|vercel\.json|next\.config\.[cm]?[jt]s|netlify\.toml)$/i.test(relativePath);
379
379
  //#endregion
380
380
  //#region src/plugin/rules/security-scan/utils/is-production-file-path.ts
381
+ const classificationCacheByPattern = /* @__PURE__ */ new Map();
381
382
  const isProductionFilePath = (relativePath, sourceFilePattern) => {
382
- if (!sourceFilePattern.test(relativePath)) return false;
383
- if (TEST_CONTEXT_PATTERN.test(relativePath)) return false;
384
- if (BUILD_SCRIPT_CONTEXT_PATTERN.test(relativePath)) return false;
385
- if (DOCUMENTATION_CONTEXT_PATTERN.test(relativePath)) return false;
386
- if (GENERATED_SOURCE_CONTEXT_PATTERN.test(relativePath)) return false;
387
- return true;
383
+ let classificationByPath = classificationCacheByPattern.get(sourceFilePattern);
384
+ if (!classificationByPath) {
385
+ classificationByPath = /* @__PURE__ */ new Map();
386
+ classificationCacheByPattern.set(sourceFilePattern, classificationByPath);
387
+ }
388
+ const cached = classificationByPath.get(relativePath);
389
+ if (cached !== void 0) return cached;
390
+ const isProduction = sourceFilePattern.test(relativePath) && !TEST_CONTEXT_PATTERN.test(relativePath) && !BUILD_SCRIPT_CONTEXT_PATTERN.test(relativePath) && !DOCUMENTATION_CONTEXT_PATTERN.test(relativePath) && !GENERATED_SOURCE_CONTEXT_PATTERN.test(relativePath);
391
+ classificationByPath.set(relativePath, isProduction);
392
+ return isProduction;
388
393
  };
389
394
  //#endregion
390
395
  //#region src/plugin/rules/security-scan/utils/is-production-source-path.ts
@@ -1677,7 +1682,7 @@ const isNextjsMetadataImageRouteFilename = (rawFilename) => {
1677
1682
  };
1678
1683
  //#endregion
1679
1684
  //#region src/plugin/utils/normalize-filename.ts
1680
- const normalizeFilename = (filename) => filename.replaceAll("\\", "/");
1685
+ const normalizeFilename = (filename) => filename.includes("\\") ? filename.replaceAll("\\", "/") : filename;
1681
1686
  //#endregion
1682
1687
  //#region src/plugin/utils/is-generated-image-render-context.ts
1683
1688
  const IMAGE_RESPONSE_MODULES = ["next/og", "@vercel/og"];
@@ -15521,16 +15526,30 @@ const stripJsComments = (sourceText) => sourceText.replace(BLOCK_COMMENT_PATTERN
15521
15526
  const DEFAULT_EXPORT_DECLARATION_PATTERN = /^\s*export\s+default\b/m;
15522
15527
  const NAMED_EXPORT_DECLARATION_PATTERN = /^\s*export\s+(?:declare\s+)?(?:(?:async\s+)?function|(?:abstract\s+)?class|const|let|var|enum|interface|type)\s+([\w$]+)/gm;
15523
15528
  const LOCAL_EXPORT_SPECIFIER_DECLARATION_PATTERN$1 = /^\s*export\s+(?:type\s+)?\{([\s\S]*?)\}(?:\s+from\s+["'][^"']+["'])?\s*;?\s*(?:(?:\/\/[^\n]*)?\s*)/gm;
15524
- const doesSourceTextExportName = (sourceText, exportedName) => {
15529
+ const collectSourceTextExportNames = (sourceText) => {
15525
15530
  const strippedSource = stripJsComments(sourceText);
15526
- if (exportedName === "default" && DEFAULT_EXPORT_DECLARATION_PATTERN.test(strippedSource)) return true;
15527
- for (const match of strippedSource.matchAll(NAMED_EXPORT_DECLARATION_PATTERN)) if (match[1] === exportedName) return true;
15528
- for (const match of strippedSource.matchAll(LOCAL_EXPORT_SPECIFIER_DECLARATION_PATTERN$1)) if (parseExportSpecifiers(match[1] ?? "", false).map((specifier) => specifier.exportedName).includes(exportedName)) return true;
15529
- return false;
15531
+ const exportedNames = /* @__PURE__ */ new Set();
15532
+ if (DEFAULT_EXPORT_DECLARATION_PATTERN.test(strippedSource)) exportedNames.add("default");
15533
+ for (const match of strippedSource.matchAll(NAMED_EXPORT_DECLARATION_PATTERN)) if (match[1]) exportedNames.add(match[1]);
15534
+ for (const match of strippedSource.matchAll(LOCAL_EXPORT_SPECIFIER_DECLARATION_PATTERN$1)) {
15535
+ const specifiersText = match[1] ?? "";
15536
+ for (const specifier of parseExportSpecifiers(specifiersText, false)) exportedNames.add(specifier.exportedName);
15537
+ }
15538
+ return exportedNames;
15530
15539
  };
15540
+ const exportNamesCache = /* @__PURE__ */ new Map();
15531
15541
  const doesModuleExportName = (filePath, exportedName) => {
15532
15542
  try {
15533
- return doesSourceTextExportName(fs.readFileSync(filePath, "utf8"), exportedName);
15543
+ const fileStat = fs.statSync(filePath);
15544
+ const cached = exportNamesCache.get(filePath);
15545
+ if (cached && cached.mtimeMs === fileStat.mtimeMs && cached.size === fileStat.size) return cached.exportedNames.has(exportedName);
15546
+ const exportedNames = collectSourceTextExportNames(fs.readFileSync(filePath, "utf8"));
15547
+ exportNamesCache.set(filePath, {
15548
+ mtimeMs: fileStat.mtimeMs,
15549
+ size: fileStat.size,
15550
+ exportedNames
15551
+ });
15552
+ return exportedNames.has(exportedName);
15534
15553
  } catch {
15535
15554
  return false;
15536
15555
  }
@@ -17413,8 +17432,15 @@ const getProgramAnalysis = (anyNode) => {
17413
17432
  programToAnalysis.set(programNode, analysis);
17414
17433
  return analysis;
17415
17434
  };
17435
+ const scopeByNodeCache = /* @__PURE__ */ new WeakMap();
17416
17436
  const getScopeForNode = (node, manager) => {
17417
17437
  if (!node.range) return null;
17438
+ let scopeByNode = scopeByNodeCache.get(manager);
17439
+ if (!scopeByNode) {
17440
+ scopeByNode = /* @__PURE__ */ new WeakMap();
17441
+ scopeByNodeCache.set(manager, scopeByNode);
17442
+ }
17443
+ if (scopeByNode.has(node)) return scopeByNode.get(node) ?? null;
17418
17444
  let bestScope = null;
17419
17445
  let bestSize = Infinity;
17420
17446
  for (const scope of manager.scopes) {
@@ -17427,6 +17453,7 @@ const getScopeForNode = (node, manager) => {
17427
17453
  bestScope = scope;
17428
17454
  }
17429
17455
  }
17456
+ scopeByNode.set(node, bestScope);
17430
17457
  return bestScope;
17431
17458
  };
17432
17459
  //#endregion
@@ -17461,11 +17488,20 @@ const descend = (node, visit, visited = /* @__PURE__ */ new Set()) => {
17461
17488
  } else if (isAstNode(child)) descend(child, visit, visited);
17462
17489
  }
17463
17490
  };
17491
+ const upstreamRefsCache = /* @__PURE__ */ new WeakMap();
17464
17492
  const getUpstreamRefs = (analysis, ref) => {
17493
+ let upstreamByRef = upstreamRefsCache.get(analysis);
17494
+ if (!upstreamByRef) {
17495
+ upstreamByRef = /* @__PURE__ */ new WeakMap();
17496
+ upstreamRefsCache.set(analysis, upstreamByRef);
17497
+ }
17498
+ const cached = upstreamByRef.get(ref);
17499
+ if (cached) return cached;
17465
17500
  const refs = [];
17466
17501
  ascend(analysis, ref, (upRef) => {
17467
17502
  refs.push(upRef);
17468
17503
  });
17504
+ upstreamByRef.set(ref, refs);
17469
17505
  return refs;
17470
17506
  };
17471
17507
  const findDownstreamNodes = (topNode, type) => {
@@ -17475,11 +17511,24 @@ const findDownstreamNodes = (topNode, type) => {
17475
17511
  });
17476
17512
  return nodes;
17477
17513
  };
17514
+ const refByIdentifierCache = /* @__PURE__ */ new WeakMap();
17478
17515
  const getRef = (analysis, identifier) => {
17516
+ let refByIdentifier = refByIdentifierCache.get(analysis);
17517
+ if (!refByIdentifier) {
17518
+ refByIdentifier = /* @__PURE__ */ new WeakMap();
17519
+ refByIdentifierCache.set(analysis, refByIdentifier);
17520
+ }
17521
+ if (refByIdentifier.has(identifier)) return refByIdentifier.get(identifier) ?? null;
17522
+ let resolvedReference = null;
17479
17523
  const scope = getScopeForNode(identifier, analysis.scopeManager);
17480
- if (!scope) return null;
17481
- for (const reference of scope.references) if (reference.identifier === identifier) return reference;
17482
- return null;
17524
+ if (scope) {
17525
+ for (const reference of scope.references) if (reference.identifier === identifier) {
17526
+ resolvedReference = reference;
17527
+ break;
17528
+ }
17529
+ }
17530
+ refByIdentifier.set(identifier, resolvedReference);
17531
+ return resolvedReference;
17483
17532
  };
17484
17533
  const downstreamRefsCache = /* @__PURE__ */ new WeakMap();
17485
17534
  const getDownstreamRefs = (analysis, node) => {
@@ -17554,22 +17603,6 @@ const isEventualCallTo = (analysis, ref, predicate) => {
17554
17603
  };
17555
17604
  //#endregion
17556
17605
  //#region src/plugin/rules/state-and-effects/utils/effect/react.ts
17557
- const getOuterScopeContaining = (analysis, node) => {
17558
- if (!node.range) return null;
17559
- let best = null;
17560
- let bestSize = Infinity;
17561
- for (const scope of analysis.scopeManager.scopes) {
17562
- const block = scope.block;
17563
- if (!block?.range) continue;
17564
- if (node.range[0] < block.range[0] || node.range[1] > block.range[1]) continue;
17565
- const size = block.range[1] - block.range[0];
17566
- if (size <= bestSize) {
17567
- bestSize = size;
17568
- best = scope;
17569
- }
17570
- }
17571
- return best;
17572
- };
17573
17606
  const KNOWN_PURE_HOC_NAMES = new Set(["memo", "forwardRef"]);
17574
17607
  const startsWithUppercase = (name) => Boolean(name && name.length > 0 && name[0] >= "A" && name[0] <= "Z");
17575
17608
  const isReactFunctionalComponent = (node) => {
@@ -17600,7 +17633,7 @@ const isReactFunctionalHOC = (analysis, node) => {
17600
17633
  const isWrappedSeparately = () => {
17601
17634
  if (!isNodeOfType(node.id, "Identifier")) return false;
17602
17635
  const bindingName = node.id.name;
17603
- const containingScope = getOuterScopeContaining(analysis, node);
17636
+ const containingScope = getScopeForNode(node, analysis.scopeManager);
17604
17637
  if (!containingScope) return false;
17605
17638
  const variable = containingScope.variables.find((v) => v.name === bindingName);
17606
17639
  if (!variable) return false;
@@ -41598,7 +41631,14 @@ const getStaticPropertyName = (member) => {
41598
41631
  if (member.computed && isNodeOfType(property, "Literal") && typeof property.value === "string") return property.value;
41599
41632
  return null;
41600
41633
  };
41634
+ const importInfoCache = /* @__PURE__ */ new WeakMap();
41601
41635
  const getImportInfoForIdentifier = (identifier) => {
41636
+ if (importInfoCache.has(identifier)) return importInfoCache.get(identifier) ?? null;
41637
+ const importInfo = computeImportInfoForIdentifier(identifier);
41638
+ importInfoCache.set(identifier, importInfo);
41639
+ return importInfo;
41640
+ };
41641
+ const computeImportInfoForIdentifier = (identifier) => {
41602
41642
  const specifier = findVariableInitializer(identifier, identifier.name)?.initializer;
41603
41643
  if (!specifier) return null;
41604
41644
  const declaration = specifier.parent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-react-doctor",
3
- "version": "0.6.2-dev.ac71a3b",
3
+ "version": "0.6.2-dev.d8628d7",
4
4
  "description": "React Doctor rules for oxlint.",
5
5
  "keywords": [
6
6
  "accessibility",