oxlint-plugin-react-doctor 0.6.3-dev.ced746f → 0.6.3-dev.e257a5e

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.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { StaticImport } from "oxc-parser";
2
1
  import { TSESTree } from "@typescript-eslint/types";
3
2
 
4
3
  //#region src/plugin/utils/es-tree-node.d.ts
@@ -16645,85 +16644,6 @@ declare const MOTION_LIBRARY_PACKAGES: Set<string>;
16645
16644
  //#region src/plugin/constants/cross-file-rule-ids.d.ts
16646
16645
  declare const CROSS_FILE_RULE_IDS: ReadonlySet<string>;
16647
16646
  //#endregion
16648
- //#region src/plugin/utils/cross-file-probe-recorder.d.ts
16649
- interface CrossFileProbeTrace {
16650
- /** Paths whose existence classification (file / dir / none) was consulted. */
16651
- readonly existencePaths: Set<string>;
16652
- /** Paths whose content (bytes, exports, parse) was consulted. */
16653
- readonly contentPaths: Set<string>;
16654
- }
16655
- //#endregion
16656
- //#region src/plugin/cross-file-dependencies.d.ts
16657
- /**
16658
- * Per-rule cross-file dependency collectors — the foundation of the sidecar
16659
- * lint cache's dependency fingerprints (`@react-doctor/core`,
16660
- * `runners/oxlint/sidecar-lint-cache.ts`).
16661
- *
16662
- * A collector re-runs the SAME resolver helpers its rule calls at lint time
16663
- * (import resolution, barrel following, ancestor-layout walks, package.json
16664
- * classification) under the probe recorder
16665
- * (`utils/cross-file-probe-recorder.ts`), so the recorded probe set is the
16666
- * exact set of filesystem facts the rule's execution consulted — including
16667
- * the negative probes (extension candidates that did NOT exist) that make
16668
- * resolution shadowing detectable when a new file appears.
16669
- *
16670
- * SOUNDNESS INVARIANT — each collector's probe set must be a SUPERSET of
16671
- * every filesystem read its rule can make while linting the file, for any
16672
- * file content and any filesystem state:
16673
- *
16674
- * - Over-approximation is always safe (extra probes only cause a spurious
16675
- * re-lint); under-approximation is forbidden (a missed probe could
16676
- * replay a stale verdict).
16677
- * - Where a rule gates its cross-file reads on in-file conditions, a
16678
- * collector may skip the gate (probe more) or must mirror it EXACTLY
16679
- * (probe the same). Each collector documents which it does per gate.
16680
- * - The replay argument: the stored probe set is the complete read set of
16681
- * the collector's execution at store time. If every stored probe has
16682
- * the same answer on a later scan, re-executing the collector — and
16683
- * therefore the rule, whose reads are a subset — reproduces the same
16684
- * execution step by step (the file's own content is pinned separately
16685
- * by the cache key's content hash), so the stored diagnostics are
16686
- * exactly what a fresh sidecar lint would produce.
16687
- *
16688
- * A cross-file rule WITHOUT a collector here must be listed in
16689
- * `UNBOUNDED_CROSS_FILE_RULE_IDS` — it then re-lints every file on every
16690
- * scan (the sound fallback). `cross-file-rule-ids.test.ts` in
16691
- * `@react-doctor/core` forces every `CROSS_FILE_RULE_IDS` entry into
16692
- * exactly one of the two classifications.
16693
- */
16694
- interface CrossFileDependencyCollectorInput {
16695
- /** Absolute, forward-slash-normalized path of the file being fingerprinted. */
16696
- readonly absoluteFilePath: string;
16697
- readonly sourceText: string;
16698
- /** oxc module record — the file's static import declarations. */
16699
- readonly staticImports: ReadonlyArray<StaticImport>;
16700
- /** Parsed program (no parent references attached). */
16701
- readonly program: EsTreeNode;
16702
- }
16703
- type CrossFileDependencyCollector = (input: CrossFileDependencyCollectorInput) => void;
16704
- declare const CROSS_FILE_DEPENDENCY_COLLECTORS: ReadonlyMap<string, CrossFileDependencyCollector>;
16705
- /**
16706
- * Cross-file rules whose dependency set CANNOT be soundly bounded — they are
16707
- * excluded from fingerprinting and re-lint every file on every scan. Empty
16708
- * today; a new cross-file rule must be added either here or to
16709
- * `CROSS_FILE_DEPENDENCY_COLLECTORS` (the core guard test enforces the
16710
- * partition), forcing a conscious classification.
16711
- */
16712
- declare const UNBOUNDED_CROSS_FILE_RULE_IDS: ReadonlySet<string>;
16713
- /**
16714
- * Runs the collectors for `ruleIds` over one file and returns every
16715
- * filesystem probe they made — the file's cross-file dependency set.
16716
- *
16717
- * Returns `null` (caller must treat the file as unfingerprintable and always
16718
- * re-lint it) when the file has a fatal parse error — an execution the
16719
- * collectors cannot mirror — or when a requested rule has no collector.
16720
- */
16721
- declare const collectCrossFileDependencyProbes: (input: {
16722
- absoluteFilePath: string;
16723
- sourceText: string;
16724
- ruleIds: ReadonlyArray<string>;
16725
- }) => CrossFileProbeTrace | null;
16726
- //#endregion
16727
16647
  //#region src/plugin/rules/security-scan/utils/classify-security-scan-file.d.ts
16728
16648
  interface SecurityScanFileClassification {
16729
16649
  readonly bucket: "priority" | "artifact" | "other";
@@ -16737,5 +16657,5 @@ declare const REACT_NATIVE_DEPENDENCY_NAMES: ReadonlySet<string>;
16737
16657
  declare const REACT_NATIVE_DEPENDENCY_PREFIXES: ReadonlyArray<string>;
16738
16658
  declare const isReactNativeDependencyName: (dependencyName: string) => boolean;
16739
16659
  //#endregion
16740
- export { ALL_REACT_DOCTOR_RULES, ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, type CrossFileProbeTrace, EXTERNAL_RULES, type EsTreeNode, FRAMEWORK_SPECIFIC_RULE_KEYS, type FileScan, MOTION_LIBRARY_PACKAGES, NEXTJS_RULES, type OxlintRuleSeverity, PREACT_RULES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, REACT_NATIVE_DEPENDENCY_NAMES, REACT_NATIVE_DEPENDENCY_PREFIXES, REACT_NATIVE_RULES, RECOMMENDED_RULES, RULES, type Rule, type RuleFramework, type RulePlugin, type RuleSeverity, type RuleVisitors, type ScanFinding, type ScannedFile, TANSTACK_QUERY_RULES, TANSTACK_START_RULES, UNBOUNDED_CROSS_FILE_RULE_IDS, classifySecurityScanFile, collectCrossFileDependencyProbes, plugin as default, isReactNativeDependencyName, shouldReadSecurityScanContent };
16660
+ export { ALL_REACT_DOCTOR_RULES, ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_RULE_IDS, EXTERNAL_RULES, type EsTreeNode, FRAMEWORK_SPECIFIC_RULE_KEYS, type FileScan, MOTION_LIBRARY_PACKAGES, NEXTJS_RULES, type OxlintRuleSeverity, PREACT_RULES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, REACT_NATIVE_DEPENDENCY_NAMES, REACT_NATIVE_DEPENDENCY_PREFIXES, REACT_NATIVE_RULES, RECOMMENDED_RULES, RULES, type Rule, type RuleFramework, type RulePlugin, type RuleSeverity, type RuleVisitors, type ScanFinding, type ScannedFile, TANSTACK_QUERY_RULES, TANSTACK_START_RULES, classifySecurityScanFile, plugin as default, isReactNativeDependencyName, shouldReadSecurityScanContent };
16741
16661
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -15564,30 +15564,6 @@ const nextjsInlineScriptMissingId = defineRule({
15564
15564
  } })
15565
15565
  });
15566
15566
  //#endregion
15567
- //#region src/plugin/utils/cross-file-probe-recorder.ts
15568
- let activeProbeTrace = null;
15569
- const recordExistenceProbe = (absolutePath) => {
15570
- activeProbeTrace?.existencePaths.add(absolutePath);
15571
- };
15572
- const recordContentProbe = (absolutePath) => {
15573
- activeProbeTrace?.contentPaths.add(absolutePath);
15574
- };
15575
- const isProbeRecorderActive = () => activeProbeTrace !== null;
15576
- const collectCrossFileProbes = (collect) => {
15577
- const previousTrace = activeProbeTrace;
15578
- const trace = {
15579
- existencePaths: /* @__PURE__ */ new Set(),
15580
- contentPaths: /* @__PURE__ */ new Set()
15581
- };
15582
- activeProbeTrace = trace;
15583
- try {
15584
- collect();
15585
- } finally {
15586
- activeProbeTrace = previousTrace;
15587
- }
15588
- return trace;
15589
- };
15590
- //#endregion
15591
15567
  //#region src/plugin/utils/parse-export-specifiers.ts
15592
15568
  const getSpecifierName = (rawName) => rawName.replace(/^type\s+/, "").trim();
15593
15569
  const parseExportSpecifiers = (specifiersText, declarationIsTypeOnly) => specifiersText.split(",").map((specifierText) => specifierText.trim()).filter(Boolean).map((specifierText) => {
@@ -15623,7 +15599,6 @@ const collectSourceTextExportNames = (sourceText) => {
15623
15599
  };
15624
15600
  const exportNamesCache = /* @__PURE__ */ new Map();
15625
15601
  const doesModuleExportName = (filePath, exportedName) => {
15626
- recordContentProbe(filePath);
15627
15602
  try {
15628
15603
  const fileStat = fs.statSync(filePath);
15629
15604
  const cached = exportNamesCache.get(filePath);
@@ -16668,7 +16643,6 @@ const parseSourceText = (filename, sourceText) => {
16668
16643
  };
16669
16644
  const parseCache = /* @__PURE__ */ new Map();
16670
16645
  const parseSourceFile = (absoluteFilePath) => {
16671
- if (!(absoluteFilePath.endsWith(".d.ts") || absoluteFilePath.endsWith(".d.mts") || absoluteFilePath.endsWith(".d.cts"))) recordContentProbe(absoluteFilePath);
16672
16646
  let fileStat;
16673
16647
  try {
16674
16648
  fileStat = fs.statSync(absoluteFilePath);
@@ -16815,27 +16789,15 @@ const classifyBarrelModule = (sourceText) => {
16815
16789
  };
16816
16790
  const getBarrelIndexModuleInfo = (filePath) => {
16817
16791
  if (!isIndexModuleFilePath(filePath)) return createNonBarrelInfo();
16818
- recordContentProbe(filePath);
16819
- let fileStat;
16820
- try {
16821
- fileStat = fs.statSync(filePath);
16822
- } catch {
16823
- fileStat = null;
16824
- }
16825
16792
  const cachedResult = barrelIndexModuleInfoCache.get(filePath);
16826
- if (cachedResult !== void 0 && fileStat !== null && cachedResult.mtimeMs === fileStat.mtimeMs && cachedResult.size === fileStat.size) return cachedResult.moduleInfo;
16827
- if (fileStat === null) return createNonBarrelInfo();
16793
+ if (cachedResult !== void 0) return cachedResult;
16828
16794
  let moduleInfo = createNonBarrelInfo();
16829
16795
  try {
16830
16796
  moduleInfo = classifyBarrelModule(fs.readFileSync(filePath, "utf8"));
16831
16797
  } catch {
16832
16798
  moduleInfo = createNonBarrelInfo();
16833
16799
  }
16834
- barrelIndexModuleInfoCache.set(filePath, {
16835
- mtimeMs: fileStat.mtimeMs,
16836
- size: fileStat.size,
16837
- moduleInfo
16838
- });
16800
+ barrelIndexModuleInfoCache.set(filePath, moduleInfo);
16839
16801
  return moduleInfo;
16840
16802
  };
16841
16803
  const isBarrelIndexModule = (filePath) => getBarrelIndexModuleInfo(filePath).isBarrel;
@@ -16864,7 +16826,6 @@ const PACKAGE_ENTRY_FIELDS = [
16864
16826
  "browser"
16865
16827
  ];
16866
16828
  const getExistingFilePath = (filePath) => {
16867
- recordExistenceProbe(filePath);
16868
16829
  try {
16869
16830
  return fs.statSync(filePath).isFile() ? filePath : null;
16870
16831
  } catch {
@@ -16872,7 +16833,6 @@ const getExistingFilePath = (filePath) => {
16872
16833
  }
16873
16834
  };
16874
16835
  const getExistingDirectoryPath = (directoryPath) => {
16875
- recordExistenceProbe(directoryPath);
16876
16836
  try {
16877
16837
  return fs.statSync(directoryPath).isDirectory() ? directoryPath : null;
16878
16838
  } catch {
@@ -16928,7 +16888,6 @@ const resolvePackageDirectoryEntry = (directoryPath) => {
16928
16888
  const existingDirectoryPath = getExistingDirectoryPath(directoryPath);
16929
16889
  if (!existingDirectoryPath) return null;
16930
16890
  const packageJsonPath = path.join(existingDirectoryPath, "package.json");
16931
- recordContentProbe(packageJsonPath);
16932
16891
  try {
16933
16892
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
16934
16893
  const packageEntry = getPackageExportEntry(packageJson) ?? PACKAGE_ENTRY_FIELDS.map((fieldName) => packageJson[fieldName]).find((value) => typeof value === "string");
@@ -17037,9 +16996,7 @@ const stripJsonComments = (text) => {
17037
16996
  }
17038
16997
  return output.replace(/,(\s*[}\]])/g, "$1");
17039
16998
  };
17040
- const parseTsconfigFile = (configFilePath, probedPaths) => {
17041
- recordContentProbe(configFilePath);
17042
- probedPaths.push(configFilePath);
16999
+ const parseTsconfigFile = (configFilePath) => {
17043
17000
  let sourceText;
17044
17001
  try {
17045
17002
  sourceText = fs.readFileSync(configFilePath, "utf8");
@@ -17068,8 +17025,8 @@ const parsePathsField = (pathsField) => {
17068
17025
  }
17069
17026
  return paths;
17070
17027
  };
17071
- const readResolvedTsconfig = (configFilePath, extendsDepth, probedPaths) => {
17072
- const parsed = parseTsconfigFile(configFilePath, probedPaths);
17028
+ const readResolvedTsconfig = (configFilePath, extendsDepth) => {
17029
+ const parsed = parseTsconfigFile(configFilePath);
17073
17030
  if (!parsed) return null;
17074
17031
  const configDirectory = path.dirname(configFilePath);
17075
17032
  const compilerOptions = isObjectRecord(parsed.compilerOptions) ? parsed.compilerOptions : {};
@@ -17083,7 +17040,7 @@ const readResolvedTsconfig = (configFilePath, extendsDepth, probedPaths) => {
17083
17040
  };
17084
17041
  if (typeof parsed.extends === "string" && extendsDepth < 8) {
17085
17042
  const parentPath = resolveExtendsPath(parsed.extends, configDirectory);
17086
- const inherited = parentPath ? readResolvedTsconfig(parentPath, extendsDepth + 1, probedPaths) : null;
17043
+ const inherited = parentPath ? readResolvedTsconfig(parentPath, extendsDepth + 1) : null;
17087
17044
  if (inherited) return inherited;
17088
17045
  }
17089
17046
  return hasExplicitBaseUrl ? {
@@ -17094,7 +17051,6 @@ const readResolvedTsconfig = (configFilePath, extendsDepth, probedPaths) => {
17094
17051
  };
17095
17052
  const configByFilePath = /* @__PURE__ */ new Map();
17096
17053
  const loadTsconfigCached = (configFilePath) => {
17097
- recordContentProbe(configFilePath);
17098
17054
  let fileStat;
17099
17055
  try {
17100
17056
  fileStat = fs.statSync(configFilePath);
@@ -17102,16 +17058,11 @@ const loadTsconfigCached = (configFilePath) => {
17102
17058
  return null;
17103
17059
  }
17104
17060
  const cached = configByFilePath.get(configFilePath);
17105
- if (cached && cached.mtimeMs === fileStat.mtimeMs) {
17106
- for (const probedPath of cached.probedChainPaths) recordContentProbe(probedPath);
17107
- return cached.config;
17108
- }
17109
- const probedChainPaths = [];
17110
- const config = readResolvedTsconfig(configFilePath, 0, probedChainPaths);
17061
+ if (cached && cached.mtimeMs === fileStat.mtimeMs) return cached.config;
17062
+ const config = readResolvedTsconfig(configFilePath, 0);
17111
17063
  configByFilePath.set(configFilePath, {
17112
17064
  mtimeMs: fileStat.mtimeMs,
17113
- config,
17114
- probedChainPaths
17065
+ config
17115
17066
  });
17116
17067
  return config;
17117
17068
  };
@@ -18958,23 +18909,10 @@ const cachedPackageDirectoryByFilename = /* @__PURE__ */ new Map();
18958
18909
  const findNearestPackageDirectory = (filename) => {
18959
18910
  if (!filename) return null;
18960
18911
  const fromCache = cachedPackageDirectoryByFilename.get(filename);
18961
- if (fromCache !== void 0) {
18962
- if (isProbeRecorderActive()) {
18963
- let probedDirectory = path.dirname(filename);
18964
- while (true) {
18965
- recordExistenceProbe(path.join(probedDirectory, "package.json"));
18966
- if (probedDirectory === fromCache) break;
18967
- const parentDirectory = path.dirname(probedDirectory);
18968
- if (parentDirectory === probedDirectory) break;
18969
- probedDirectory = parentDirectory;
18970
- }
18971
- }
18972
- return fromCache;
18973
- }
18912
+ if (fromCache !== void 0) return fromCache;
18974
18913
  let currentDirectory = path.dirname(filename);
18975
18914
  while (true) {
18976
18915
  const candidatePackageJsonPath = path.join(currentDirectory, "package.json");
18977
- recordExistenceProbe(candidatePackageJsonPath);
18978
18916
  let hasPackageJson = false;
18979
18917
  try {
18980
18918
  hasPackageJson = fs.statSync(candidatePackageJsonPath).isFile();
@@ -19037,7 +18975,6 @@ const isWebFrameworkOnly = (packageJson) => {
19037
18975
  const classifyPackagePlatform = (filename) => {
19038
18976
  const packageDirectory = findNearestPackageDirectory(filename);
19039
18977
  if (!packageDirectory) return "unknown";
19040
- recordContentProbe(path.join(packageDirectory, "package.json"));
19041
18978
  const cached = cachedPlatformByPackageDirectory.get(packageDirectory);
19042
18979
  if (cached !== void 0) return cached;
19043
18980
  const packageJson = readPackageJsonSafe(path.join(packageDirectory, "package.json"));
@@ -47549,178 +47486,6 @@ const CROSS_FILE_RULE_IDS = new Set([
47549
47486
  "rn-prefer-expo-image"
47550
47487
  ]);
47551
47488
  //#endregion
47552
- //#region src/plugin/cross-file-dependencies.ts
47553
- const flattenImportEntries = (staticImports) => {
47554
- const flattened = [];
47555
- for (const staticImport of staticImports) for (const entry of staticImport.entries) {
47556
- if (entry.importName.kind === "NamespaceObject") continue;
47557
- const exportedName = entry.importName.kind === "Default" ? "default" : entry.importName.name ?? null;
47558
- if (exportedName === null) continue;
47559
- flattened.push({
47560
- source: staticImport.moduleRequest.value,
47561
- exportedName
47562
- });
47563
- }
47564
- return flattened;
47565
- };
47566
- const collectNoBarrelImportDependencies = ({ absoluteFilePath, staticImports }) => {
47567
- let didProbePackagePlatform = false;
47568
- for (const staticImport of staticImports) {
47569
- const source = staticImport.moduleRequest.value;
47570
- if (!source.startsWith(".")) continue;
47571
- const runtimeEntries = staticImport.entries.filter((entry) => !entry.isType);
47572
- if (runtimeEntries.length === 0) continue;
47573
- const resolvedImportPath = resolveRelativeImportPath(absoluteFilePath, source);
47574
- if (!resolvedImportPath || !isBarrelIndexModule(resolvedImportPath)) continue;
47575
- if (!didProbePackagePlatform) {
47576
- didProbePackagePlatform = true;
47577
- classifyPackagePlatform(absoluteFilePath);
47578
- }
47579
- for (const entry of runtimeEntries) {
47580
- const importedName = entry.importName.kind === "Default" ? "default" : entry.importName.name;
47581
- if (importedName) resolveBarrelExportFilePath(resolvedImportPath, importedName);
47582
- }
47583
- }
47584
- };
47585
- const collectNextjsMissingMetadataDependencies = ({ absoluteFilePath }) => {
47586
- if (!PAGE_FILE_PATTERN.test(absoluteFilePath)) return;
47587
- if (INTERNAL_PAGE_PATH_PATTERN.test(absoluteFilePath)) return;
47588
- hasAncestorMetadataLayout(absoluteFilePath);
47589
- };
47590
- const collectNextjsSearchParamsDependencies = ({ absoluteFilePath, staticImports }) => {
47591
- if (!PAGE_OR_LAYOUT_FILE_PATTERN.test(absoluteFilePath)) return;
47592
- if (hasAncestorSuspenseLayout(absoluteFilePath)) return;
47593
- for (const entry of flattenImportEntries(staticImports)) resolveCrossFileFunctionExport(absoluteFilePath, entry.source, entry.exportedName);
47594
- };
47595
- const collectMutatingReducerDependencies = ({ absoluteFilePath, sourceText, staticImports, program }) => {
47596
- const namedUseReducerLocals = /* @__PURE__ */ new Set();
47597
- const reactObjectLocals = /* @__PURE__ */ new Set();
47598
- for (const staticImport of staticImports) {
47599
- if (staticImport.moduleRequest.value !== "react") continue;
47600
- for (const entry of staticImport.entries) if (entry.importName.kind === "Name" && entry.importName.name === "useReducer") namedUseReducerLocals.add(entry.localName.value);
47601
- else if (entry.importName.kind === "Default" || entry.importName.kind === "NamespaceObject") reactObjectLocals.add(entry.localName.value);
47602
- }
47603
- if (!(namedUseReducerLocals.size > 0 || reactObjectLocals.size > 0 && (sourceText.includes("useReducer") || sourceText.includes("\\u")))) return;
47604
- const identifierName = (node) => {
47605
- if (node?.type !== "Identifier") return null;
47606
- const { name } = node;
47607
- return typeof name === "string" ? name : null;
47608
- };
47609
- const isUseReducerCallee = (callee) => {
47610
- const calleeName = identifierName(callee);
47611
- if (calleeName !== null) return namedUseReducerLocals.has(calleeName);
47612
- if (callee.type !== "MemberExpression") return false;
47613
- const member = callee;
47614
- const objectName = identifierName(member.object);
47615
- return objectName !== null && reactObjectLocals.has(objectName) && identifierName(member.property) === "useReducer";
47616
- };
47617
- const reducerArgumentNames = /* @__PURE__ */ new Set();
47618
- walkAst(program, (node) => {
47619
- if (node.type !== "CallExpression") return;
47620
- const call = node;
47621
- if (!call.callee || !isUseReducerCallee(call.callee)) return;
47622
- const reducerArgument = call.arguments?.[0];
47623
- if (!reducerArgument) return;
47624
- const argumentName = identifierName(stripParenExpression(reducerArgument));
47625
- if (argumentName !== null) reducerArgumentNames.add(argumentName);
47626
- });
47627
- if (reducerArgumentNames.size === 0) return;
47628
- for (const staticImport of staticImports) for (const entry of staticImport.entries) {
47629
- if (entry.importName.kind === "NamespaceObject") continue;
47630
- if (!reducerArgumentNames.has(entry.localName.value)) continue;
47631
- const exportedName = entry.importName.kind === "Default" ? "default" : entry.importName.name;
47632
- if (exportedName) resolveCrossFileFunctionExport(absoluteFilePath, staticImport.moduleRequest.value, exportedName);
47633
- }
47634
- };
47635
- const collectJsxBoundaryNames = (program) => {
47636
- const jsxNames = /* @__PURE__ */ new Set();
47637
- walkAst(program, (node) => {
47638
- if (node.type !== "JSXOpeningElement") return;
47639
- const nameNode = node.name;
47640
- if (!nameNode) return;
47641
- if (nameNode.type === "JSXIdentifier") {
47642
- const name = nameNode.name;
47643
- if (typeof name === "string") jsxNames.add(name);
47644
- } else if (nameNode.type === "JSXMemberExpression") {
47645
- const property = nameNode.property;
47646
- if (property?.type === "JSXIdentifier" && typeof property.name === "string") jsxNames.add(property.name);
47647
- } else if (nameNode.type === "JSXNamespacedName") {
47648
- const namespace = nameNode.namespace;
47649
- if (typeof namespace?.name === "string") jsxNames.add(namespace.name);
47650
- }
47651
- });
47652
- return jsxNames;
47653
- };
47654
- const collectRnNoRawTextDependencies = ({ absoluteFilePath, staticImports, program }) => {
47655
- classifyPackagePlatform(absoluteFilePath);
47656
- const jsxNames = collectJsxBoundaryNames(program);
47657
- if (jsxNames.size === 0) return;
47658
- for (const staticImport of staticImports) for (const entry of staticImport.entries) {
47659
- if (entry.importName.kind === "NamespaceObject") continue;
47660
- if (!jsxNames.has(entry.localName.value)) continue;
47661
- const exportedName = entry.importName.kind === "Default" ? "default" : entry.importName.name;
47662
- if (exportedName) resolveCrossFileFunctionExport(absoluteFilePath, staticImport.moduleRequest.value, exportedName);
47663
- }
47664
- };
47665
- const collectRnPreferExpoImageDependencies = ({ absoluteFilePath }) => {
47666
- classifyPackagePlatform(absoluteFilePath);
47667
- };
47668
- const CROSS_FILE_DEPENDENCY_COLLECTORS = new Map([
47669
- ["no-barrel-import", collectNoBarrelImportDependencies],
47670
- ["nextjs-missing-metadata", collectNextjsMissingMetadataDependencies],
47671
- ["nextjs-no-use-search-params-without-suspense", collectNextjsSearchParamsDependencies],
47672
- ["no-mutating-reducer-state", collectMutatingReducerDependencies],
47673
- ["rn-no-raw-text", collectRnNoRawTextDependencies],
47674
- ["rn-prefer-expo-image", collectRnPreferExpoImageDependencies]
47675
- ]);
47676
- /**
47677
- * Cross-file rules whose dependency set CANNOT be soundly bounded — they are
47678
- * excluded from fingerprinting and re-lint every file on every scan. Empty
47679
- * today; a new cross-file rule must be added either here or to
47680
- * `CROSS_FILE_DEPENDENCY_COLLECTORS` (the core guard test enforces the
47681
- * partition), forcing a conscious classification.
47682
- */
47683
- const UNBOUNDED_CROSS_FILE_RULE_IDS = /* @__PURE__ */ new Set();
47684
- /**
47685
- * Runs the collectors for `ruleIds` over one file and returns every
47686
- * filesystem probe they made — the file's cross-file dependency set.
47687
- *
47688
- * Returns `null` (caller must treat the file as unfingerprintable and always
47689
- * re-lint it) when the file has a fatal parse error — an execution the
47690
- * collectors cannot mirror — or when a requested rule has no collector.
47691
- */
47692
- const collectCrossFileDependencyProbes = (input) => {
47693
- const collectors = [];
47694
- for (const ruleId of input.ruleIds) {
47695
- const collector = CROSS_FILE_DEPENDENCY_COLLECTORS.get(ruleId);
47696
- if (!collector) return null;
47697
- collectors.push(collector);
47698
- }
47699
- const absoluteFilePath = normalizeFilename(input.absoluteFilePath);
47700
- let staticImports;
47701
- let program;
47702
- try {
47703
- const parseResult = parseSync(absoluteFilePath, input.sourceText, {
47704
- astType: "ts",
47705
- lang: resolveLang(absoluteFilePath)
47706
- });
47707
- if (parseResult.errors.some((parseError) => parseError.severity === "Error")) return null;
47708
- staticImports = parseResult.module.staticImports;
47709
- program = parseResult.program;
47710
- } catch {
47711
- return null;
47712
- }
47713
- const collectorInput = {
47714
- absoluteFilePath,
47715
- sourceText: input.sourceText,
47716
- staticImports,
47717
- program
47718
- };
47719
- return collectCrossFileProbes(() => {
47720
- for (const collector of collectors) collector(collectorInput);
47721
- });
47722
- };
47723
- //#endregion
47724
47489
  //#region src/plugin/rules/security-scan/utils/is-probably-text-file.ts
47725
47490
  const isProbablyTextFile = (relativePath) => TEXT_FILE_PATTERN.test(relativePath) || DOTENV_FILE_PATTERN.test(relativePath);
47726
47491
  //#endregion
@@ -47746,6 +47511,6 @@ const shouldReadSecurityScanContent = (relativePath, isGeneratedBundle) => isGen
47746
47511
  //#region src/index.ts
47747
47512
  var src_default = plugin;
47748
47513
  //#endregion
47749
- export { ALL_REACT_DOCTOR_RULES, ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, EXTERNAL_RULES, FRAMEWORK_SPECIFIC_RULE_KEYS, MOTION_LIBRARY_PACKAGES, NEXTJS_RULES, PREACT_RULES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, REACT_NATIVE_DEPENDENCY_NAMES, REACT_NATIVE_DEPENDENCY_PREFIXES, REACT_NATIVE_RULES, RECOMMENDED_RULES, RULES, TANSTACK_QUERY_RULES, TANSTACK_START_RULES, UNBOUNDED_CROSS_FILE_RULE_IDS, classifySecurityScanFile, collectCrossFileDependencyProbes, src_default as default, isReactNativeDependencyName, shouldReadSecurityScanContent };
47514
+ export { ALL_REACT_DOCTOR_RULES, ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_RULE_IDS, EXTERNAL_RULES, FRAMEWORK_SPECIFIC_RULE_KEYS, MOTION_LIBRARY_PACKAGES, NEXTJS_RULES, PREACT_RULES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, REACT_NATIVE_DEPENDENCY_NAMES, REACT_NATIVE_DEPENDENCY_PREFIXES, REACT_NATIVE_RULES, RECOMMENDED_RULES, RULES, TANSTACK_QUERY_RULES, TANSTACK_START_RULES, classifySecurityScanFile, src_default as default, isReactNativeDependencyName, shouldReadSecurityScanContent };
47750
47515
 
47751
47516
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-react-doctor",
3
- "version": "0.6.3-dev.ced746f",
3
+ "version": "0.6.3-dev.e257a5e",
4
4
  "description": "React Doctor rules for oxlint.",
5
5
  "keywords": [
6
6
  "accessibility",