react-doctor 0.5.6-dev.451beeb → 0.5.6-dev.4e06b2a

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
@@ -9794,6 +9794,16 @@ interface JsonReportProjectEntry {
9794
9794
  skippedChecks: string[];
9795
9795
  /** Human-readable explanation per skipped check. See `InspectResult.skippedCheckReasons`. */
9796
9796
  skippedCheckReasons?: Record<string, string>;
9797
+ /**
9798
+ * Number of source files this scan's linter examined. In diff / changed
9799
+ * mode it's the count of changed React-eligible files (`.tsx`/`.jsx` plus
9800
+ * framework entry files); in a full scan it's the whole source tree. `0`
9801
+ * in a diff scan means the changed files held nothing React Doctor lints —
9802
+ * the GitHub Action reads that as "nothing to report" (skips the PR comment;
9803
+ * the commit status says "skipped"). Optional: absent on reports from
9804
+ * constructors that don't track it (e.g. `toJsonReport`).
9805
+ */
9806
+ scannedFileCount?: number;
9797
9807
  elapsedMilliseconds: number;
9798
9808
  }
9799
9809
  interface JsonReportSummary {
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a4394ddc-4e6c-5a18-aeeb-d60322b1c0dd")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="efaa91ed-3659-50a5-b802-51753472b2d6")}catch(e){}}();
3
3
  import { r as __toESM$1, t as __commonJSMin$1 } from "./chunk-N93fKeF6.js";
4
4
  import { createRequire } from "node:module";
5
5
  import * as NFS from "node:fs";
@@ -19298,6 +19298,7 @@ var JsonReportProjectEntry = class extends Class("JsonReportProjectEntry")({
19298
19298
  score: Unknown,
19299
19299
  skippedChecks: ArraySchema(String$1),
19300
19300
  skippedCheckReasons: optional(Record$1(String$1, String$1)),
19301
+ scannedFileCount: optional(Number$1),
19301
19302
  elapsedMilliseconds: Number$1
19302
19303
  }) {};
19303
19304
  /**
@@ -32724,6 +32725,7 @@ const isLargeMinifiedFile = (absolutePath) => {
32724
32725
  if (sizeBytes < 2e4) return false;
32725
32726
  return isMinifiedSource(absolutePath);
32726
32727
  };
32728
+ const isErrnoException = (error) => error instanceof Error && "code" in error;
32727
32729
  const IGNORABLE_READDIR_ERROR_CODES = new Set([
32728
32730
  "EACCES",
32729
32731
  "EPERM",
@@ -32733,11 +32735,7 @@ const IGNORABLE_READDIR_ERROR_CODES = new Set([
32733
32735
  "ELOOP",
32734
32736
  "ENAMETOOLONG"
32735
32737
  ]);
32736
- const isIgnorableReaddirError = (error) => {
32737
- if (typeof error !== "object" || error === null) return false;
32738
- const errorCode = error.code;
32739
- return typeof errorCode === "string" && IGNORABLE_READDIR_ERROR_CODES.has(errorCode);
32740
- };
32738
+ const isIgnorableReaddirError = (error) => isErrnoException(error) && typeof error.code === "string" && IGNORABLE_READDIR_ERROR_CODES.has(error.code);
32741
32739
  const readDirectoryEntries = (directoryPath) => {
32742
32740
  try {
32743
32741
  return NFS.readdirSync(directoryPath, { withFileTypes: true });
@@ -32787,7 +32785,7 @@ const readPackageJsonUncached = (packageJsonPath) => {
32787
32785
  return JSON.parse(NFS.readFileSync(packageJsonPath, "utf-8"));
32788
32786
  } catch (error) {
32789
32787
  if (error instanceof SyntaxError) return {};
32790
- if (error instanceof Error && "code" in error) {
32788
+ if (isErrnoException(error)) {
32791
32789
  const { code } = error;
32792
32790
  if (code === "EISDIR" || code === "EACCES" || code === "EPERM" || code === "ENOENT") return {};
32793
32791
  }
@@ -33512,17 +33510,13 @@ const isPackageJsonReactNativeAware = (packageJson) => {
33512
33510
  return false;
33513
33511
  };
33514
33512
  const hasReactNativeWorkspaceAnywhere = (rootDirectory, rootPackageJson) => someWorkspacePackageJson(rootDirectory, rootPackageJson, isPackageJsonReactNativeAware);
33515
- const getExpoDependencySpec = (packageJson) => {
33516
- const spec = packageJson.dependencies?.expo ?? packageJson.devDependencies?.expo ?? packageJson.peerDependencies?.expo ?? packageJson.optionalDependencies?.expo;
33513
+ const getDependencySpec = (packageJson, packageName) => {
33514
+ const spec = packageJson.dependencies?.[packageName] ?? packageJson.devDependencies?.[packageName] ?? packageJson.peerDependencies?.[packageName] ?? packageJson.optionalDependencies?.[packageName];
33517
33515
  return typeof spec === "string" ? spec : null;
33518
33516
  };
33519
- const findExpoVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, getExpoDependencySpec);
33517
+ const findExpoVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, "expo"));
33520
33518
  const SHOPIFY_FLASH_LIST_PACKAGE_NAME = "@shopify/flash-list";
33521
- const getShopifyFlashListDependencySpec = (packageJson) => {
33522
- const spec = packageJson.dependencies?.["@shopify/flash-list"] ?? packageJson.devDependencies?.["@shopify/flash-list"] ?? packageJson.peerDependencies?.["@shopify/flash-list"] ?? packageJson.optionalDependencies?.["@shopify/flash-list"];
33523
- return typeof spec === "string" ? spec : null;
33524
- };
33525
- const findShopifyFlashListVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, getShopifyFlashListDependencySpec);
33519
+ const findShopifyFlashListVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, SHOPIFY_FLASH_LIST_PACKAGE_NAME));
33526
33520
  const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, version }) => {
33527
33521
  if (version === null || !isCatalogReference(version)) return version;
33528
33522
  const catalogName = extractCatalogName(version);
@@ -33534,11 +33528,7 @@ const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson,
33534
33528
  if (!isFile(monorepoPackageJsonPath)) return version;
33535
33529
  return resolveCatalogVersion(readPackageJson(monorepoPackageJsonPath), packageName, monorepoRoot, catalogName) ?? version;
33536
33530
  };
33537
- const getNextjsDependencySpec = (packageJson) => {
33538
- const spec = packageJson.dependencies?.next ?? packageJson.devDependencies?.next ?? packageJson.peerDependencies?.next ?? packageJson.optionalDependencies?.next;
33539
- return typeof spec === "string" ? spec : null;
33540
- };
33541
- const findNextjsVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, getNextjsDependencySpec);
33531
+ const findNextjsVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, "next"));
33542
33532
  const getPreactVersion = (packageJson) => {
33543
33533
  return {
33544
33534
  ...packageJson.peerDependencies,
@@ -33620,6 +33610,11 @@ const ES_TARGET_YEAR_BY_NAME = {
33620
33610
  esnext: 9999
33621
33611
  };
33622
33612
  /**
33613
+ * tsconfig filenames probed when resolving a project's TypeScript
33614
+ * compiler options — the root config first, then a monorepo base config.
33615
+ */
33616
+ const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
33617
+ /**
33623
33618
  * Project-config files that `StagedFiles.materialize` copies into
33624
33619
  * the temp directory alongside staged sources so oxlint resolves
33625
33620
  * `tsconfig` / `package.json` / lint configs the same way it would
@@ -34118,6 +34113,7 @@ const isTailwindAtLeast = (detected, required) => {
34118
34113
  if (detected.major !== required.major) return detected.major > required.major;
34119
34114
  return detected.minor >= required.minor;
34120
34115
  };
34116
+ const messageFromUnknown = (error) => error instanceof Error ? error.message : String(error);
34121
34117
  var InvalidGlobPatternError = class extends Error {
34122
34118
  pattern;
34123
34119
  reason;
@@ -34146,7 +34142,7 @@ const compileGlobPattern = (rawPattern) => {
34146
34142
  try {
34147
34143
  return import_picomatch.default.makeRe(normalizeGlobPattern(rawPattern), PICOMATCH_OPTIONS);
34148
34144
  } catch (caughtError) {
34149
- throw new InvalidGlobPatternError(rawPattern, caughtError instanceof Error ? caughtError.message : String(caughtError));
34145
+ throw new InvalidGlobPatternError(rawPattern, messageFromUnknown(caughtError));
34150
34146
  }
34151
34147
  };
34152
34148
  const compileGlobPatternsLenient = (patterns, onInvalid) => {
@@ -35328,7 +35324,6 @@ const PACKAGE_JSON_FILENAME = "package.json";
35328
35324
  const PACKAGE_JSON_CONFIG_KEY = "reactDoctor";
35329
35325
  const LEGACY_CONFIG_FILENAME = "react-doctor.config.json";
35330
35326
  const jiti = createJiti(import.meta.url);
35331
- const formatError = (error) => error instanceof Error ? error.message : String(error);
35332
35327
  const importDefaultExport = async (jitiInstance, filePath) => {
35333
35328
  const imported = await jitiInstance.import(filePath);
35334
35329
  return imported?.default ?? imported;
@@ -35360,7 +35355,7 @@ const loadModuleConfig = async (filePath) => {
35360
35355
  try {
35361
35356
  return await importDefaultExport(aliasJiti, filePath);
35362
35357
  } catch (retryError) {
35363
- throw new Error(`${formatError(error)} (retry with ${SELF_PACKAGE_IMPORT_SPECIFIER} aliased to the running react-doctor package also failed: ${formatError(retryError)})`, { cause: retryError });
35358
+ throw new Error(`${messageFromUnknown(error)} (retry with ${SELF_PACKAGE_IMPORT_SPECIFIER} aliased to the running react-doctor package also failed: ${messageFromUnknown(retryError)})`, { cause: retryError });
35364
35359
  }
35365
35360
  }
35366
35361
  };
@@ -35409,7 +35404,7 @@ const loadLegacyConfig = (directory) => {
35409
35404
  }
35410
35405
  warn(`${LEGACY_CONFIG_FILENAME} must contain an object, ignoring.`);
35411
35406
  } catch (error) {
35412
- warn(`Failed to load ${LEGACY_CONFIG_FILENAME}: ${formatError(error)}`);
35407
+ warn(`Failed to load ${LEGACY_CONFIG_FILENAME}: ${messageFromUnknown(error)}`);
35413
35408
  }
35414
35409
  return {
35415
35410
  status: "invalid",
@@ -35436,7 +35431,7 @@ const loadConfigFromDirectory = async (directory) => {
35436
35431
  warn(`${CONFIG_BASENAME}.${extension} must export an object, ignoring.`);
35437
35432
  sawBrokenConfigFile = true;
35438
35433
  } catch (error) {
35439
- warn(`Failed to load ${CONFIG_BASENAME}.${extension}: ${formatError(error)}`);
35434
+ warn(`Failed to load ${CONFIG_BASENAME}.${extension}: ${messageFromUnknown(error)}`);
35440
35435
  sawBrokenConfigFile = true;
35441
35436
  }
35442
35437
  }
@@ -36664,7 +36659,7 @@ const readIgnoreFile = (filePath) => {
36664
36659
  try {
36665
36660
  content = NFS.readFileSync(filePath, "utf-8");
36666
36661
  } catch (error) {
36667
- const errnoCode = error?.code;
36662
+ const errnoCode = isErrnoException(error) ? error.code : void 0;
36668
36663
  if (errnoCode && errnoCode !== "ENOENT") runSync(warn$1(`Could not read ignore file ${filePath}: ${errnoCode}`));
36669
36664
  return [];
36670
36665
  }
@@ -36705,8 +36700,8 @@ const collectIgnorePatterns = (rootDirectory) => {
36705
36700
  cachedPatternsByRoot.set(rootDirectory, patterns);
36706
36701
  return patterns;
36707
36702
  };
36703
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36708
36704
  const KNIP_JSON_FILENAME = "knip.json";
36709
- const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36710
36705
  const readJsonFileSafe = (filePath) => {
36711
36706
  let rawContents;
36712
36707
  try {
@@ -36722,10 +36717,10 @@ const readJsonFileSafe = (filePath) => {
36722
36717
  };
36723
36718
  const readKnipConfig = (rootDirectory) => {
36724
36719
  const knipJson = readJsonFileSafe(path.join(rootDirectory, KNIP_JSON_FILENAME));
36725
- if (isRecord$1(knipJson)) return knipJson;
36720
+ if (isRecord(knipJson)) return knipJson;
36726
36721
  const packageJson = readJsonFileSafe(path.join(rootDirectory, "package.json"));
36727
- const packageKnipConfig = isRecord$1(packageJson) ? packageJson.knip : null;
36728
- return isRecord$1(packageKnipConfig) ? packageKnipConfig : null;
36722
+ const packageKnipConfig = isRecord(packageJson) ? packageJson.knip : null;
36723
+ return isRecord(packageKnipConfig) ? packageKnipConfig : null;
36729
36724
  };
36730
36725
  const normalizePatternList = (value) => {
36731
36726
  if (typeof value === "string" && value.length > 0) return [value];
@@ -36737,10 +36732,10 @@ const prefixWorkspacePatterns = (workspacePattern, patterns) => {
36737
36732
  return patterns.map((pattern) => pattern.startsWith("!") ? `!${normalizedWorkspacePattern}/${pattern.slice(1)}` : `${normalizedWorkspacePattern}/${pattern}`);
36738
36733
  };
36739
36734
  const collectKnipWorkspacePatterns = (workspaces, settingName) => {
36740
- if (!isRecord$1(workspaces)) return [];
36735
+ if (!isRecord(workspaces)) return [];
36741
36736
  const patterns = [];
36742
36737
  for (const [workspacePattern, workspaceConfig] of Object.entries(workspaces)) {
36743
- if (!isRecord$1(workspaceConfig)) continue;
36738
+ if (!isRecord(workspaceConfig)) continue;
36744
36739
  patterns.push(...prefixWorkspacePatterns(workspacePattern, normalizePatternList(workspaceConfig[settingName])));
36745
36740
  }
36746
36741
  return patterns;
@@ -36785,8 +36780,6 @@ const toCanonicalPath = (filePath) => {
36785
36780
  };
36786
36781
  const DEAD_CODE_PLUGIN = "deslop";
36787
36782
  const DEAD_CODE_CATEGORY = "Maintainability";
36788
- const TSCONFIG_FILENAMES$1 = ["tsconfig.json", "tsconfig.base.json"];
36789
- const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36790
36783
  const DEAD_CODE_WORKER_SCRIPT = `
36791
36784
  const inputChunks = [];
36792
36785
  process.stdin.on("data", (chunk) => inputChunks.push(chunk));
@@ -36844,7 +36837,7 @@ process.stdin.on("end", () => {
36844
36837
  });
36845
36838
  `;
36846
36839
  const resolveTsConfigPath = (rootDirectory) => {
36847
- for (const filename of TSCONFIG_FILENAMES$1) {
36840
+ for (const filename of TSCONFIG_FILENAMES) {
36848
36841
  const candidate = Path.join(rootDirectory, filename);
36849
36842
  if (NFS.existsSync(candidate)) return candidate;
36850
36843
  }
@@ -37225,15 +37218,13 @@ var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
37225
37218
  })()) }));
37226
37219
  static layerOf = (diagnostics) => succeed$3(DeadCode, DeadCode.of({ run: () => fromIterable$1(diagnostics) }));
37227
37220
  };
37228
- const createNodeReadFileLinesSync = (rootDirectory) => {
37229
- return (filePath) => {
37230
- const absolutePath = Path.isAbsolute(filePath) ? filePath : Path.join(rootDirectory, filePath);
37231
- try {
37232
- return NFS.readFileSync(absolutePath, "utf-8").split("\n");
37233
- } catch {
37234
- return null;
37235
- }
37236
- };
37221
+ const createNodeReadFileLinesSync = (rootDirectory) => (filePath) => {
37222
+ const absolutePath = Path.isAbsolute(filePath) ? filePath : Path.join(rootDirectory, filePath);
37223
+ try {
37224
+ return NFS.readFileSync(absolutePath, "utf-8").split("\n");
37225
+ } catch {
37226
+ return null;
37227
+ }
37237
37228
  };
37238
37229
  var Files = class Files extends Service()("react-doctor/Files") {
37239
37230
  static layerNode = succeed$3(Files, Files.of({
@@ -37444,7 +37435,10 @@ var Git = class Git extends Service()("react-doctor/Git") {
37444
37435
  directory: input.directory,
37445
37436
  cause
37446
37437
  }) });
37447
- }));
37438
+ }), withSpan("git.exec", { attributes: {
37439
+ "git.command": input.command,
37440
+ "git.subcommand": input.args[0] ?? ""
37441
+ } }));
37448
37442
  const runGit = (directory, args) => runCommand({
37449
37443
  command: "git",
37450
37444
  args,
@@ -37472,7 +37466,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
37472
37466
  ]);
37473
37467
  if (candidates.status !== 0) return null;
37474
37468
  return trimOrNull(candidates.stdout.split("\n")[0] ?? "");
37475
- });
37469
+ }).pipe(withSpan("Git.defaultBranch"));
37476
37470
  const branchExists = (directory, branch) => runGit(directory, [
37477
37471
  "rev-parse",
37478
37472
  "--verify",
@@ -37519,7 +37513,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
37519
37513
  const result = resultOption.value;
37520
37514
  if (result.status !== 0) return null;
37521
37515
  return parseGithubViewerPermission(result.stdout);
37522
- }).pipe(catch_$1(() => succeed$2(null)));
37516
+ }).pipe(catch_$1(() => succeed$2(null)), withSpan("Git.githubViewerPermission"));
37523
37517
  /**
37524
37518
  * Resolves a `--diff A..B` / `A...B` commit range into a changed-file
37525
37519
  * selection. Each endpoint is validated with `isSafeGitRevision`
@@ -37633,7 +37627,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
37633
37627
  changedFiles: splitNullSeparated(diff.stdout),
37634
37628
  isCurrentChanges: false
37635
37629
  };
37636
- }),
37630
+ }).pipe(withSpan("Git.diffSelection")),
37637
37631
  stagedFilePaths: (directory) => runGit(directory, [
37638
37632
  "diff",
37639
37633
  "--cached",
@@ -37675,7 +37669,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
37675
37669
  status: result.status,
37676
37670
  stdout: result.stdout
37677
37671
  };
37678
- }),
37672
+ }).pipe(withSpan("Git.grep")),
37679
37673
  changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
37680
37674
  if (files.length === 0) return [];
37681
37675
  if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
@@ -37691,7 +37685,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
37691
37685
  ]);
37692
37686
  if (result.status !== 0) return null;
37693
37687
  return parseChangedLineRanges(result.stdout);
37694
- })
37688
+ }).pipe(withSpan("Git.changedLineRanges"))
37695
37689
  });
37696
37690
  })).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
37697
37691
  /**
@@ -37906,7 +37900,7 @@ const neutralizeDisableDirectives = async (rootDirectory, includePaths) => {
37906
37900
  for (const [absolutePath, originalContent] of originalContents) try {
37907
37901
  NFS.writeFileSync(absolutePath, originalContent);
37908
37902
  } catch (error) {
37909
- process.stderr.write(`[react-doctor] Failed to restore inline disable directives in ${absolutePath}: ${error instanceof Error ? error.message : String(error)}\n[react-doctor] Run: git checkout -- ${absolutePath}\n`);
37903
+ process.stderr.write(`[react-doctor] Failed to restore inline disable directives in ${absolutePath}: ${messageFromUnknown(error)}\n[react-doctor] Run: git checkout -- ${absolutePath}\n`);
37910
37904
  }
37911
37905
  };
37912
37906
  const onExit = () => restore();
@@ -38012,7 +38006,7 @@ const resolveUserPlugin = (spec, configSourceDirectory) => {
38012
38006
  try {
38013
38007
  resolvedSpecifier = isRelative ? Path.resolve(configSourceDirectory, spec) : candidateRequire.resolve(spec);
38014
38008
  } catch (error) {
38015
- warnConfigIssue(`config.plugins entry "${spec}" could not be resolved from ${configSourceDirectory}: ${error instanceof Error ? error.message : String(error)}`);
38009
+ warnConfigIssue(`config.plugins entry "${spec}" could not be resolved from ${configSourceDirectory}: ${messageFromUnknown(error)}`);
38016
38010
  return null;
38017
38011
  }
38018
38012
  const { name, ruleNames } = readPluginShape(resolvedSpecifier, (target) => candidateRequire(target));
@@ -38084,8 +38078,8 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
38084
38078
  }
38085
38079
  return enabled;
38086
38080
  };
38087
- const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [] }) => {
38088
- const reactHooksJsPlugin = resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
38081
+ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false }) => {
38082
+ const reactHooksJsPlugin = disableReactHooksJsPlugin ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
38089
38083
  const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
38090
38084
  const jsPlugins = [];
38091
38085
  if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
@@ -38145,7 +38139,6 @@ const resolveOxlintBinary = () => {
38145
38139
  return Path.join(oxlintPackageDirectory, "bin", "oxlint");
38146
38140
  };
38147
38141
  const resolvePluginPath = () => esmRequire.resolve("oxlint-plugin-react-doctor");
38148
- const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
38149
38142
  const resolveTsConfigRelativePath = (rootDirectory) => {
38150
38143
  for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
38151
38144
  return null;
@@ -38517,7 +38510,7 @@ const scopeContainsNonImportBinding = (node, scopeNode, identifierName) => {
38517
38510
  const isIdentifierShadowedByLocalBinding = (identifier, sourceFile) => {
38518
38511
  let currentNode = identifier.parent;
38519
38512
  while (currentNode) {
38520
- if (isScopeNode(currentNode)) {
38513
+ if (isScopeBoundary(currentNode)) {
38521
38514
  if (scopeContainsNonImportBinding(currentNode, currentNode, identifier.text)) return true;
38522
38515
  }
38523
38516
  if (currentNode === sourceFile) return false;
@@ -38608,11 +38601,10 @@ const findResolutionInScope = (scopeNode, identifierName, reactImportBindings, s
38608
38601
  });
38609
38602
  return resolution;
38610
38603
  };
38611
- const isScopeNode = isScopeBoundary;
38612
38604
  const resolveIdentifierBinding = (identifier, reactImportBindings, sourceFile, visitedDeclarations = /* @__PURE__ */ new Set()) => {
38613
38605
  let currentNode = identifier.parent;
38614
38606
  while (currentNode) {
38615
- if (isScopeNode(currentNode)) {
38607
+ if (isScopeBoundary(currentNode)) {
38616
38608
  const resolution = findResolutionInScope(currentNode, identifier.text, reactImportBindings, sourceFile, visitedDeclarations);
38617
38609
  if (resolution) return resolution;
38618
38610
  }
@@ -38782,9 +38774,9 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
38782
38774
  try {
38783
38775
  parsed = JSON.parse(sanitizedStdout);
38784
38776
  } catch {
38785
- throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 200) }) });
38777
+ throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
38786
38778
  }
38787
- if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 200) }) });
38779
+ if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
38788
38780
  const minifiedFileCache = /* @__PURE__ */ new Map();
38789
38781
  const isMinifiedDiagnosticFile = (filename) => {
38790
38782
  const absolutePath = Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
@@ -38860,7 +38852,7 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
38860
38852
  child.kill("SIGKILL");
38861
38853
  reject(new ReactDoctorError({ reason: new OxlintBatchExceeded({
38862
38854
  kind: "timeout",
38863
- detail: `${spawnTimeoutMs / 1e3}s budget exceeded`
38855
+ detail: `${spawnTimeoutMs / MILLISECONDS_PER_SECOND}s budget exceeded`
38864
38856
  }) }));
38865
38857
  }, spawnTimeoutMs);
38866
38858
  timeoutHandle.unref?.();
@@ -39075,6 +39067,28 @@ const writeOxlintConfig = (configPath, configToWrite) => {
39075
39067
  NFS.closeSync(fileHandle);
39076
39068
  }
39077
39069
  };
39070
+ const REACT_HOOKS_JS_DROP_PREFIX = "React Compiler rules (react-hooks-js/*) skipped — eslint-plugin-react-hooks failed to load in this environment";
39071
+ /**
39072
+ * Detects an oxlint config-load crash caused by the optional
39073
+ * `react-hooks-js` (eslint-plugin-react-hooks) React Compiler plugin and
39074
+ * builds the partial-failure note for it; returns `null` when the failure
39075
+ * was anything else.
39076
+ *
39077
+ * oxlint prints a framed error to stdout (not stderr) and exits non-zero
39078
+ * when a `jsPlugins` entry can't be imported; that non-JSON stdout
39079
+ * surfaces as `OxlintOutputUnparseable`. Because oxlint fails the WHOLE
39080
+ * config load on it, leaving the plugin in would drop every curated
39081
+ * react-doctor diagnostic too — so the caller retries with the plugin
39082
+ * stripped (issue #833). Both markers sit at the start of oxlint's
39083
+ * message, so they survive the `preview` slice even for deep pnpm paths.
39084
+ */
39085
+ const reactHooksJsPluginDropNote = (error) => {
39086
+ if (!(error instanceof ReactDoctorError) || error.reason._tag !== "OxlintOutputUnparseable") return null;
39087
+ const { preview } = error.reason;
39088
+ if (!preview.includes("Failed to load JS plugin") || !preview.includes("eslint-plugin-react-hooks")) return null;
39089
+ const underlyingReason = preview.match(/Error:[^\n]*/)?.[0]?.trim();
39090
+ return `${REACT_HOOKS_JS_DROP_PREFIX}${underlyingReason ? `: ${underlyingReason}` : ""}. Other rules ran normally.`;
39091
+ };
39078
39092
  /**
39079
39093
  * The oxlint runner. Composed of three pieces in `runners/oxlint/`:
39080
39094
  *
@@ -39102,15 +39116,16 @@ const runOxlint = async (options) => {
39102
39116
  const pluginPath = resolvePluginPath();
39103
39117
  const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
39104
39118
  const userPlugins = resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
39105
- const buildConfig = (extendsForThisAttempt) => createOxlintConfig({
39119
+ const buildConfig = (overrides) => createOxlintConfig({
39106
39120
  pluginPath,
39107
39121
  project,
39108
39122
  customRulesOnly,
39109
- extendsPaths: extendsForThisAttempt,
39123
+ extendsPaths: overrides.extendsPaths,
39110
39124
  ignoredTags,
39111
39125
  serverAuthFunctionNames,
39112
39126
  severityControls,
39113
- userPlugins
39127
+ userPlugins,
39128
+ disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin
39114
39129
  });
39115
39130
  const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
39116
39131
  const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
@@ -39146,12 +39161,22 @@ const runOxlint = async (options) => {
39146
39161
  outputMaxBytes,
39147
39162
  concurrency: options.concurrency
39148
39163
  });
39149
- writeOxlintConfig(configPath, buildConfig(extendsPaths));
39164
+ writeOxlintConfig(configPath, buildConfig({ extendsPaths }));
39150
39165
  try {
39151
39166
  return await runBatches();
39152
39167
  } catch (error) {
39168
+ const reactHooksJsDropNote = reactHooksJsPluginDropNote(error);
39169
+ if (reactHooksJsDropNote !== null) {
39170
+ writeOxlintConfig(configPath, buildConfig({
39171
+ extendsPaths,
39172
+ disableReactHooksJsPlugin: true
39173
+ }));
39174
+ const diagnostics = await runBatches();
39175
+ onPartialFailure?.(reactHooksJsDropNote);
39176
+ return diagnostics;
39177
+ }
39153
39178
  if (extendsPaths.length === 0) throw error;
39154
- writeOxlintConfig(configPath, buildConfig([]));
39179
+ writeOxlintConfig(configPath, buildConfig({ extendsPaths: [] }));
39155
39180
  return await runBatches();
39156
39181
  }
39157
39182
  } finally {
@@ -39949,7 +39974,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
39949
39974
  }))))))));
39950
39975
  const deadCodeFailureState = yield* get$2(deadCodeFailure);
39951
39976
  const scanElapsedMilliseconds = Date.now() - scanStartTime;
39952
- const scanElapsedSeconds = (scanElapsedMilliseconds / 1e3).toFixed(1);
39977
+ const scanElapsedSeconds = (scanElapsedMilliseconds / MILLISECONDS_PER_SECOND).toFixed(1);
39953
39978
  if (!lintFailureState.didFail) if (deadCodeFailureState.didFail) yield* scanProgress.fail(DEAD_CODE_FAIL_TEXT);
39954
39979
  else if (input.suppressScanSummary) yield* scanProgress.stop();
39955
39980
  else yield* scanProgress.succeed(`Scanned ${scannedFilesLabel} in ${scanElapsedSeconds}s${workerCountSuffix}`);
@@ -40163,7 +40188,7 @@ const materializeSourceTree = (input) => gen(function* () {
40163
40188
  static layerNode = effect(StagedFiles, gen(function* () {
40164
40189
  const git = yield* Git;
40165
40190
  return StagedFiles.of({
40166
- discoverSourceFiles: (directory) => git.stagedFilePaths(directory).pipe(map$3((entries) => entries.filter(isLintableSourceFile))),
40191
+ discoverSourceFiles: (directory) => git.stagedFilePaths(directory).pipe(map$3((entries) => entries.filter(isLintableSourceFile)), withSpan("StagedFiles.discoverSourceFiles")),
40167
40192
  materialize: ({ directory, stagedFiles, tempDirectory }) => materializeSourceTree({
40168
40193
  directory,
40169
40194
  files: stagedFiles,
@@ -40173,7 +40198,7 @@ const materializeSourceTree = (input) => gen(function* () {
40173
40198
  tempDirectory: tree.tempDirectory,
40174
40199
  stagedFiles: tree.materializedFiles,
40175
40200
  cleanup: tree.cleanup
40176
- })))
40201
+ })), withSpan("StagedFiles.materialize"))
40177
40202
  });
40178
40203
  }));
40179
40204
  /**
@@ -40305,6 +40330,7 @@ const buildJsonReport = (input) => {
40305
40330
  score: result.score,
40306
40331
  skippedChecks: result.skippedChecks,
40307
40332
  ...result.skippedCheckReasons ? { skippedCheckReasons: result.skippedCheckReasons } : {},
40333
+ ...typeof result.scannedFileCount === "number" ? { scannedFileCount: result.scannedFileCount } : {},
40308
40334
  elapsedMilliseconds: result.elapsedMilliseconds
40309
40335
  }));
40310
40336
  const flattenedDiagnostics = projects.flatMap((entry) => entry.diagnostics);
@@ -40571,4 +40597,4 @@ const toJsonReport = (result, options) => buildJsonReport({
40571
40597
  export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
40572
40598
 
40573
40599
  //# sourceMappingURL=index.js.map
40574
- //# debugId=a4394ddc-4e6c-5a18-aeeb-d60322b1c0dd
40600
+ //# debugId=efaa91ed-3659-50a5-b802-51753472b2d6