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/lsp.js CHANGED
@@ -19335,6 +19335,7 @@ var JsonReportProjectEntry = class extends Class("JsonReportProjectEntry")({
19335
19335
  score: Unknown,
19336
19336
  skippedChecks: ArraySchema(String$1),
19337
19337
  skippedCheckReasons: optional(Record$1(String$1, String$1)),
19338
+ scannedFileCount: optional(Number$1),
19338
19339
  elapsedMilliseconds: Number$1
19339
19340
  }) {};
19340
19341
  /**
@@ -32761,6 +32762,7 @@ const isLargeMinifiedFile = (absolutePath) => {
32761
32762
  if (sizeBytes < 2e4) return false;
32762
32763
  return isMinifiedSource(absolutePath);
32763
32764
  };
32765
+ const isErrnoException = (error) => error instanceof Error && "code" in error;
32764
32766
  const IGNORABLE_READDIR_ERROR_CODES = new Set([
32765
32767
  "EACCES",
32766
32768
  "EPERM",
@@ -32770,11 +32772,7 @@ const IGNORABLE_READDIR_ERROR_CODES = new Set([
32770
32772
  "ELOOP",
32771
32773
  "ENAMETOOLONG"
32772
32774
  ]);
32773
- const isIgnorableReaddirError = (error) => {
32774
- if (typeof error !== "object" || error === null) return false;
32775
- const errorCode = error.code;
32776
- return typeof errorCode === "string" && IGNORABLE_READDIR_ERROR_CODES.has(errorCode);
32777
- };
32775
+ const isIgnorableReaddirError = (error) => isErrnoException(error) && typeof error.code === "string" && IGNORABLE_READDIR_ERROR_CODES.has(error.code);
32778
32776
  const readDirectoryEntries = (directoryPath) => {
32779
32777
  try {
32780
32778
  return NFS.readdirSync(directoryPath, { withFileTypes: true });
@@ -32824,7 +32822,7 @@ const readPackageJsonUncached = (packageJsonPath) => {
32824
32822
  return JSON.parse(NFS.readFileSync(packageJsonPath, "utf-8"));
32825
32823
  } catch (error) {
32826
32824
  if (error instanceof SyntaxError) return {};
32827
- if (error instanceof Error && "code" in error) {
32825
+ if (isErrnoException(error)) {
32828
32826
  const { code } = error;
32829
32827
  if (code === "EISDIR" || code === "EACCES" || code === "EPERM" || code === "ENOENT") return {};
32830
32828
  }
@@ -33549,17 +33547,13 @@ const isPackageJsonReactNativeAware = (packageJson) => {
33549
33547
  return false;
33550
33548
  };
33551
33549
  const hasReactNativeWorkspaceAnywhere = (rootDirectory, rootPackageJson) => someWorkspacePackageJson(rootDirectory, rootPackageJson, isPackageJsonReactNativeAware);
33552
- const getExpoDependencySpec = (packageJson) => {
33553
- const spec = packageJson.dependencies?.expo ?? packageJson.devDependencies?.expo ?? packageJson.peerDependencies?.expo ?? packageJson.optionalDependencies?.expo;
33550
+ const getDependencySpec = (packageJson, packageName) => {
33551
+ const spec = packageJson.dependencies?.[packageName] ?? packageJson.devDependencies?.[packageName] ?? packageJson.peerDependencies?.[packageName] ?? packageJson.optionalDependencies?.[packageName];
33554
33552
  return typeof spec === "string" ? spec : null;
33555
33553
  };
33556
- const findExpoVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, getExpoDependencySpec);
33554
+ const findExpoVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, "expo"));
33557
33555
  const SHOPIFY_FLASH_LIST_PACKAGE_NAME = "@shopify/flash-list";
33558
- const getShopifyFlashListDependencySpec = (packageJson) => {
33559
- const spec = packageJson.dependencies?.["@shopify/flash-list"] ?? packageJson.devDependencies?.["@shopify/flash-list"] ?? packageJson.peerDependencies?.["@shopify/flash-list"] ?? packageJson.optionalDependencies?.["@shopify/flash-list"];
33560
- return typeof spec === "string" ? spec : null;
33561
- };
33562
- const findShopifyFlashListVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, getShopifyFlashListDependencySpec);
33556
+ const findShopifyFlashListVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, SHOPIFY_FLASH_LIST_PACKAGE_NAME));
33563
33557
  const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, version }) => {
33564
33558
  if (version === null || !isCatalogReference(version)) return version;
33565
33559
  const catalogName = extractCatalogName(version);
@@ -33571,11 +33565,7 @@ const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson,
33571
33565
  if (!isFile(monorepoPackageJsonPath)) return version;
33572
33566
  return resolveCatalogVersion(readPackageJson(monorepoPackageJsonPath), packageName, monorepoRoot, catalogName) ?? version;
33573
33567
  };
33574
- const getNextjsDependencySpec = (packageJson) => {
33575
- const spec = packageJson.dependencies?.next ?? packageJson.devDependencies?.next ?? packageJson.peerDependencies?.next ?? packageJson.optionalDependencies?.next;
33576
- return typeof spec === "string" ? spec : null;
33577
- };
33578
- const findNextjsVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, getNextjsDependencySpec);
33568
+ const findNextjsVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, "next"));
33579
33569
  const getPreactVersion = (packageJson) => {
33580
33570
  return {
33581
33571
  ...packageJson.peerDependencies,
@@ -33657,6 +33647,11 @@ const ES_TARGET_YEAR_BY_NAME = {
33657
33647
  esnext: 9999
33658
33648
  };
33659
33649
  /**
33650
+ * tsconfig filenames probed when resolving a project's TypeScript
33651
+ * compiler options — the root config first, then a monorepo base config.
33652
+ */
33653
+ const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
33654
+ /**
33660
33655
  * Project-config files that `StagedFiles.materialize` copies into
33661
33656
  * the temp directory alongside staged sources so oxlint resolves
33662
33657
  * `tsconfig` / `package.json` / lint configs the same way it would
@@ -34178,6 +34173,7 @@ const isTailwindAtLeast = (detected, required) => {
34178
34173
  if (detected.major !== required.major) return detected.major > required.major;
34179
34174
  return detected.minor >= required.minor;
34180
34175
  };
34176
+ const messageFromUnknown = (error) => error instanceof Error ? error.message : String(error);
34181
34177
  var InvalidGlobPatternError = class extends Error {
34182
34178
  pattern;
34183
34179
  reason;
@@ -34206,7 +34202,7 @@ const compileGlobPattern = (rawPattern) => {
34206
34202
  try {
34207
34203
  return import_picomatch.default.makeRe(normalizeGlobPattern(rawPattern), PICOMATCH_OPTIONS);
34208
34204
  } catch (caughtError) {
34209
- throw new InvalidGlobPatternError(rawPattern, caughtError instanceof Error ? caughtError.message : String(caughtError));
34205
+ throw new InvalidGlobPatternError(rawPattern, messageFromUnknown(caughtError));
34210
34206
  }
34211
34207
  };
34212
34208
  const compileGlobPatternsLenient = (patterns, onInvalid) => {
@@ -35362,7 +35358,6 @@ const PACKAGE_JSON_FILENAME = "package.json";
35362
35358
  const PACKAGE_JSON_CONFIG_KEY = "reactDoctor";
35363
35359
  const LEGACY_CONFIG_FILENAME = "react-doctor.config.json";
35364
35360
  const jiti = createJiti(import.meta.url);
35365
- const formatError = (error) => error instanceof Error ? error.message : String(error);
35366
35361
  const importDefaultExport = async (jitiInstance, filePath) => {
35367
35362
  const imported = await jitiInstance.import(filePath);
35368
35363
  return imported?.default ?? imported;
@@ -35394,7 +35389,7 @@ const loadModuleConfig = async (filePath) => {
35394
35389
  try {
35395
35390
  return await importDefaultExport(aliasJiti, filePath);
35396
35391
  } catch (retryError) {
35397
- throw new Error(`${formatError(error)} (retry with ${SELF_PACKAGE_IMPORT_SPECIFIER} aliased to the running react-doctor package also failed: ${formatError(retryError)})`, { cause: retryError });
35392
+ throw new Error(`${messageFromUnknown(error)} (retry with ${SELF_PACKAGE_IMPORT_SPECIFIER} aliased to the running react-doctor package also failed: ${messageFromUnknown(retryError)})`, { cause: retryError });
35398
35393
  }
35399
35394
  }
35400
35395
  };
@@ -35443,7 +35438,7 @@ const loadLegacyConfig = (directory) => {
35443
35438
  }
35444
35439
  warn(`${LEGACY_CONFIG_FILENAME} must contain an object, ignoring.`);
35445
35440
  } catch (error) {
35446
- warn(`Failed to load ${LEGACY_CONFIG_FILENAME}: ${formatError(error)}`);
35441
+ warn(`Failed to load ${LEGACY_CONFIG_FILENAME}: ${messageFromUnknown(error)}`);
35447
35442
  }
35448
35443
  return {
35449
35444
  status: "invalid",
@@ -35470,7 +35465,7 @@ const loadConfigFromDirectory = async (directory) => {
35470
35465
  warn(`${CONFIG_BASENAME}.${extension} must export an object, ignoring.`);
35471
35466
  sawBrokenConfigFile = true;
35472
35467
  } catch (error) {
35473
- warn(`Failed to load ${CONFIG_BASENAME}.${extension}: ${formatError(error)}`);
35468
+ warn(`Failed to load ${CONFIG_BASENAME}.${extension}: ${messageFromUnknown(error)}`);
35474
35469
  sawBrokenConfigFile = true;
35475
35470
  }
35476
35471
  }
@@ -36650,7 +36645,7 @@ const readIgnoreFile = (filePath) => {
36650
36645
  try {
36651
36646
  content = NFS.readFileSync(filePath, "utf-8");
36652
36647
  } catch (error) {
36653
- const errnoCode = error?.code;
36648
+ const errnoCode = isErrnoException(error) ? error.code : void 0;
36654
36649
  if (errnoCode && errnoCode !== "ENOENT") runSync(warn$1(`Could not read ignore file ${filePath}: ${errnoCode}`));
36655
36650
  return [];
36656
36651
  }
@@ -36691,8 +36686,8 @@ const collectIgnorePatterns = (rootDirectory) => {
36691
36686
  cachedPatternsByRoot.set(rootDirectory, patterns);
36692
36687
  return patterns;
36693
36688
  };
36689
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36694
36690
  const KNIP_JSON_FILENAME = "knip.json";
36695
- const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36696
36691
  const readJsonFileSafe = (filePath) => {
36697
36692
  let rawContents;
36698
36693
  try {
@@ -36708,10 +36703,10 @@ const readJsonFileSafe = (filePath) => {
36708
36703
  };
36709
36704
  const readKnipConfig = (rootDirectory) => {
36710
36705
  const knipJson = readJsonFileSafe(path.join(rootDirectory, KNIP_JSON_FILENAME));
36711
- if (isRecord$1(knipJson)) return knipJson;
36706
+ if (isRecord(knipJson)) return knipJson;
36712
36707
  const packageJson = readJsonFileSafe(path.join(rootDirectory, "package.json"));
36713
- const packageKnipConfig = isRecord$1(packageJson) ? packageJson.knip : null;
36714
- return isRecord$1(packageKnipConfig) ? packageKnipConfig : null;
36708
+ const packageKnipConfig = isRecord(packageJson) ? packageJson.knip : null;
36709
+ return isRecord(packageKnipConfig) ? packageKnipConfig : null;
36715
36710
  };
36716
36711
  const normalizePatternList = (value) => {
36717
36712
  if (typeof value === "string" && value.length > 0) return [value];
@@ -36723,10 +36718,10 @@ const prefixWorkspacePatterns = (workspacePattern, patterns) => {
36723
36718
  return patterns.map((pattern) => pattern.startsWith("!") ? `!${normalizedWorkspacePattern}/${pattern.slice(1)}` : `${normalizedWorkspacePattern}/${pattern}`);
36724
36719
  };
36725
36720
  const collectKnipWorkspacePatterns = (workspaces, settingName) => {
36726
- if (!isRecord$1(workspaces)) return [];
36721
+ if (!isRecord(workspaces)) return [];
36727
36722
  const patterns = [];
36728
36723
  for (const [workspacePattern, workspaceConfig] of Object.entries(workspaces)) {
36729
- if (!isRecord$1(workspaceConfig)) continue;
36724
+ if (!isRecord(workspaceConfig)) continue;
36730
36725
  patterns.push(...prefixWorkspacePatterns(workspacePattern, normalizePatternList(workspaceConfig[settingName])));
36731
36726
  }
36732
36727
  return patterns;
@@ -36771,8 +36766,6 @@ const toCanonicalPath = (filePath) => {
36771
36766
  };
36772
36767
  const DEAD_CODE_PLUGIN = "deslop";
36773
36768
  const DEAD_CODE_CATEGORY = "Maintainability";
36774
- const TSCONFIG_FILENAMES$1 = ["tsconfig.json", "tsconfig.base.json"];
36775
- const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36776
36769
  const DEAD_CODE_WORKER_SCRIPT = `
36777
36770
  const inputChunks = [];
36778
36771
  process.stdin.on("data", (chunk) => inputChunks.push(chunk));
@@ -36830,7 +36823,7 @@ process.stdin.on("end", () => {
36830
36823
  });
36831
36824
  `;
36832
36825
  const resolveTsConfigPath = (rootDirectory) => {
36833
- for (const filename of TSCONFIG_FILENAMES$1) {
36826
+ for (const filename of TSCONFIG_FILENAMES) {
36834
36827
  const candidate = Path.join(rootDirectory, filename);
36835
36828
  if (NFS.existsSync(candidate)) return candidate;
36836
36829
  }
@@ -37211,15 +37204,13 @@ var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
37211
37204
  })()) }));
37212
37205
  static layerOf = (diagnostics) => succeed$3(DeadCode, DeadCode.of({ run: () => fromIterable$1(diagnostics) }));
37213
37206
  };
37214
- const createNodeReadFileLinesSync = (rootDirectory) => {
37215
- return (filePath) => {
37216
- const absolutePath = Path.isAbsolute(filePath) ? filePath : Path.join(rootDirectory, filePath);
37217
- try {
37218
- return NFS.readFileSync(absolutePath, "utf-8").split("\n");
37219
- } catch {
37220
- return null;
37221
- }
37222
- };
37207
+ const createNodeReadFileLinesSync = (rootDirectory) => (filePath) => {
37208
+ const absolutePath = Path.isAbsolute(filePath) ? filePath : Path.join(rootDirectory, filePath);
37209
+ try {
37210
+ return NFS.readFileSync(absolutePath, "utf-8").split("\n");
37211
+ } catch {
37212
+ return null;
37213
+ }
37223
37214
  };
37224
37215
  var Files = class Files extends Service()("react-doctor/Files") {
37225
37216
  static layerNode = succeed$3(Files, Files.of({
@@ -37430,7 +37421,10 @@ var Git = class Git extends Service()("react-doctor/Git") {
37430
37421
  directory: input.directory,
37431
37422
  cause
37432
37423
  }) });
37433
- }));
37424
+ }), withSpan("git.exec", { attributes: {
37425
+ "git.command": input.command,
37426
+ "git.subcommand": input.args[0] ?? ""
37427
+ } }));
37434
37428
  const runGit = (directory, args) => runCommand({
37435
37429
  command: "git",
37436
37430
  args,
@@ -37458,7 +37452,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
37458
37452
  ]);
37459
37453
  if (candidates.status !== 0) return null;
37460
37454
  return trimOrNull(candidates.stdout.split("\n")[0] ?? "");
37461
- });
37455
+ }).pipe(withSpan("Git.defaultBranch"));
37462
37456
  const branchExists = (directory, branch) => runGit(directory, [
37463
37457
  "rev-parse",
37464
37458
  "--verify",
@@ -37505,7 +37499,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
37505
37499
  const result = resultOption.value;
37506
37500
  if (result.status !== 0) return null;
37507
37501
  return parseGithubViewerPermission(result.stdout);
37508
- }).pipe(catch_$1(() => succeed$2(null)));
37502
+ }).pipe(catch_$1(() => succeed$2(null)), withSpan("Git.githubViewerPermission"));
37509
37503
  /**
37510
37504
  * Resolves a `--diff A..B` / `A...B` commit range into a changed-file
37511
37505
  * selection. Each endpoint is validated with `isSafeGitRevision`
@@ -37619,7 +37613,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
37619
37613
  changedFiles: splitNullSeparated(diff.stdout),
37620
37614
  isCurrentChanges: false
37621
37615
  };
37622
- }),
37616
+ }).pipe(withSpan("Git.diffSelection")),
37623
37617
  stagedFilePaths: (directory) => runGit(directory, [
37624
37618
  "diff",
37625
37619
  "--cached",
@@ -37661,7 +37655,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
37661
37655
  status: result.status,
37662
37656
  stdout: result.stdout
37663
37657
  };
37664
- }),
37658
+ }).pipe(withSpan("Git.grep")),
37665
37659
  changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
37666
37660
  if (files.length === 0) return [];
37667
37661
  if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
@@ -37677,7 +37671,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
37677
37671
  ]);
37678
37672
  if (result.status !== 0) return null;
37679
37673
  return parseChangedLineRanges(result.stdout);
37680
- })
37674
+ }).pipe(withSpan("Git.changedLineRanges"))
37681
37675
  });
37682
37676
  })).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
37683
37677
  /**
@@ -37892,7 +37886,7 @@ const neutralizeDisableDirectives = async (rootDirectory, includePaths) => {
37892
37886
  for (const [absolutePath, originalContent] of originalContents) try {
37893
37887
  NFS.writeFileSync(absolutePath, originalContent);
37894
37888
  } catch (error) {
37895
- 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`);
37889
+ process.stderr.write(`[react-doctor] Failed to restore inline disable directives in ${absolutePath}: ${messageFromUnknown(error)}\n[react-doctor] Run: git checkout -- ${absolutePath}\n`);
37896
37890
  }
37897
37891
  };
37898
37892
  const onExit = () => restore();
@@ -37998,7 +37992,7 @@ const resolveUserPlugin = (spec, configSourceDirectory) => {
37998
37992
  try {
37999
37993
  resolvedSpecifier = isRelative ? Path.resolve(configSourceDirectory, spec) : candidateRequire.resolve(spec);
38000
37994
  } catch (error) {
38001
- warnConfigIssue(`config.plugins entry "${spec}" could not be resolved from ${configSourceDirectory}: ${error instanceof Error ? error.message : String(error)}`);
37995
+ warnConfigIssue(`config.plugins entry "${spec}" could not be resolved from ${configSourceDirectory}: ${messageFromUnknown(error)}`);
38002
37996
  return null;
38003
37997
  }
38004
37998
  const { name, ruleNames } = readPluginShape(resolvedSpecifier, (target) => candidateRequire(target));
@@ -38070,8 +38064,8 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
38070
38064
  }
38071
38065
  return enabled;
38072
38066
  };
38073
- const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [] }) => {
38074
- const reactHooksJsPlugin = resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
38067
+ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false }) => {
38068
+ const reactHooksJsPlugin = disableReactHooksJsPlugin ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
38075
38069
  const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
38076
38070
  const jsPlugins = [];
38077
38071
  if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
@@ -38131,7 +38125,6 @@ const resolveOxlintBinary = () => {
38131
38125
  return Path.join(oxlintPackageDirectory, "bin", "oxlint");
38132
38126
  };
38133
38127
  const resolvePluginPath = () => esmRequire.resolve("oxlint-plugin-react-doctor");
38134
- const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
38135
38128
  const resolveTsConfigRelativePath = (rootDirectory) => {
38136
38129
  for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
38137
38130
  return null;
@@ -38503,7 +38496,7 @@ const scopeContainsNonImportBinding = (node, scopeNode, identifierName) => {
38503
38496
  const isIdentifierShadowedByLocalBinding = (identifier, sourceFile) => {
38504
38497
  let currentNode = identifier.parent;
38505
38498
  while (currentNode) {
38506
- if (isScopeNode(currentNode)) {
38499
+ if (isScopeBoundary(currentNode)) {
38507
38500
  if (scopeContainsNonImportBinding(currentNode, currentNode, identifier.text)) return true;
38508
38501
  }
38509
38502
  if (currentNode === sourceFile) return false;
@@ -38594,11 +38587,10 @@ const findResolutionInScope = (scopeNode, identifierName, reactImportBindings, s
38594
38587
  });
38595
38588
  return resolution;
38596
38589
  };
38597
- const isScopeNode = isScopeBoundary;
38598
38590
  const resolveIdentifierBinding = (identifier, reactImportBindings, sourceFile, visitedDeclarations = /* @__PURE__ */ new Set()) => {
38599
38591
  let currentNode = identifier.parent;
38600
38592
  while (currentNode) {
38601
- if (isScopeNode(currentNode)) {
38593
+ if (isScopeBoundary(currentNode)) {
38602
38594
  const resolution = findResolutionInScope(currentNode, identifier.text, reactImportBindings, sourceFile, visitedDeclarations);
38603
38595
  if (resolution) return resolution;
38604
38596
  }
@@ -38768,9 +38760,9 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
38768
38760
  try {
38769
38761
  parsed = JSON.parse(sanitizedStdout);
38770
38762
  } catch {
38771
- throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 200) }) });
38763
+ throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
38772
38764
  }
38773
- if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 200) }) });
38765
+ if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
38774
38766
  const minifiedFileCache = /* @__PURE__ */ new Map();
38775
38767
  const isMinifiedDiagnosticFile = (filename) => {
38776
38768
  const absolutePath = Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
@@ -38846,7 +38838,7 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
38846
38838
  child.kill("SIGKILL");
38847
38839
  reject(new ReactDoctorError({ reason: new OxlintBatchExceeded({
38848
38840
  kind: "timeout",
38849
- detail: `${spawnTimeoutMs / 1e3}s budget exceeded`
38841
+ detail: `${spawnTimeoutMs / MILLISECONDS_PER_SECOND}s budget exceeded`
38850
38842
  }) }));
38851
38843
  }, spawnTimeoutMs);
38852
38844
  timeoutHandle.unref?.();
@@ -39061,6 +39053,28 @@ const writeOxlintConfig = (configPath, configToWrite) => {
39061
39053
  NFS.closeSync(fileHandle);
39062
39054
  }
39063
39055
  };
39056
+ const REACT_HOOKS_JS_DROP_PREFIX = "React Compiler rules (react-hooks-js/*) skipped — eslint-plugin-react-hooks failed to load in this environment";
39057
+ /**
39058
+ * Detects an oxlint config-load crash caused by the optional
39059
+ * `react-hooks-js` (eslint-plugin-react-hooks) React Compiler plugin and
39060
+ * builds the partial-failure note for it; returns `null` when the failure
39061
+ * was anything else.
39062
+ *
39063
+ * oxlint prints a framed error to stdout (not stderr) and exits non-zero
39064
+ * when a `jsPlugins` entry can't be imported; that non-JSON stdout
39065
+ * surfaces as `OxlintOutputUnparseable`. Because oxlint fails the WHOLE
39066
+ * config load on it, leaving the plugin in would drop every curated
39067
+ * react-doctor diagnostic too — so the caller retries with the plugin
39068
+ * stripped (issue #833). Both markers sit at the start of oxlint's
39069
+ * message, so they survive the `preview` slice even for deep pnpm paths.
39070
+ */
39071
+ const reactHooksJsPluginDropNote = (error) => {
39072
+ if (!(error instanceof ReactDoctorError) || error.reason._tag !== "OxlintOutputUnparseable") return null;
39073
+ const { preview } = error.reason;
39074
+ if (!preview.includes("Failed to load JS plugin") || !preview.includes("eslint-plugin-react-hooks")) return null;
39075
+ const underlyingReason = preview.match(/Error:[^\n]*/)?.[0]?.trim();
39076
+ return `${REACT_HOOKS_JS_DROP_PREFIX}${underlyingReason ? `: ${underlyingReason}` : ""}. Other rules ran normally.`;
39077
+ };
39064
39078
  /**
39065
39079
  * The oxlint runner. Composed of three pieces in `runners/oxlint/`:
39066
39080
  *
@@ -39088,15 +39102,16 @@ const runOxlint = async (options) => {
39088
39102
  const pluginPath = resolvePluginPath();
39089
39103
  const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
39090
39104
  const userPlugins = resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
39091
- const buildConfig = (extendsForThisAttempt) => createOxlintConfig({
39105
+ const buildConfig = (overrides) => createOxlintConfig({
39092
39106
  pluginPath,
39093
39107
  project,
39094
39108
  customRulesOnly,
39095
- extendsPaths: extendsForThisAttempt,
39109
+ extendsPaths: overrides.extendsPaths,
39096
39110
  ignoredTags,
39097
39111
  serverAuthFunctionNames,
39098
39112
  severityControls,
39099
- userPlugins
39113
+ userPlugins,
39114
+ disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin
39100
39115
  });
39101
39116
  const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
39102
39117
  const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
@@ -39132,12 +39147,22 @@ const runOxlint = async (options) => {
39132
39147
  outputMaxBytes,
39133
39148
  concurrency: options.concurrency
39134
39149
  });
39135
- writeOxlintConfig(configPath, buildConfig(extendsPaths));
39150
+ writeOxlintConfig(configPath, buildConfig({ extendsPaths }));
39136
39151
  try {
39137
39152
  return await runBatches();
39138
39153
  } catch (error) {
39154
+ const reactHooksJsDropNote = reactHooksJsPluginDropNote(error);
39155
+ if (reactHooksJsDropNote !== null) {
39156
+ writeOxlintConfig(configPath, buildConfig({
39157
+ extendsPaths,
39158
+ disableReactHooksJsPlugin: true
39159
+ }));
39160
+ const diagnostics = await runBatches();
39161
+ onPartialFailure?.(reactHooksJsDropNote);
39162
+ return diagnostics;
39163
+ }
39139
39164
  if (extendsPaths.length === 0) throw error;
39140
- writeOxlintConfig(configPath, buildConfig([]));
39165
+ writeOxlintConfig(configPath, buildConfig({ extendsPaths: [] }));
39141
39166
  return await runBatches();
39142
39167
  }
39143
39168
  } finally {
@@ -39935,7 +39960,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
39935
39960
  }))))))));
39936
39961
  const deadCodeFailureState = yield* get$2(deadCodeFailure);
39937
39962
  const scanElapsedMilliseconds = Date.now() - scanStartTime;
39938
- const scanElapsedSeconds = (scanElapsedMilliseconds / 1e3).toFixed(1);
39963
+ const scanElapsedSeconds = (scanElapsedMilliseconds / MILLISECONDS_PER_SECOND).toFixed(1);
39939
39964
  if (!lintFailureState.didFail) if (deadCodeFailureState.didFail) yield* scanProgress.fail(DEAD_CODE_FAIL_TEXT);
39940
39965
  else if (input.suppressScanSummary) yield* scanProgress.stop();
39941
39966
  else yield* scanProgress.succeed(`Scanned ${scannedFilesLabel} in ${scanElapsedSeconds}s${workerCountSuffix}`);
@@ -40149,7 +40174,7 @@ const materializeSourceTree = (input) => gen(function* () {
40149
40174
  static layerNode = effect(StagedFiles, gen(function* () {
40150
40175
  const git = yield* Git;
40151
40176
  return StagedFiles.of({
40152
- discoverSourceFiles: (directory) => git.stagedFilePaths(directory).pipe(map$3((entries) => entries.filter(isLintableSourceFile))),
40177
+ discoverSourceFiles: (directory) => git.stagedFilePaths(directory).pipe(map$3((entries) => entries.filter(isLintableSourceFile)), withSpan("StagedFiles.discoverSourceFiles")),
40153
40178
  materialize: ({ directory, stagedFiles, tempDirectory }) => materializeSourceTree({
40154
40179
  directory,
40155
40180
  files: stagedFiles,
@@ -40159,7 +40184,7 @@ const materializeSourceTree = (input) => gen(function* () {
40159
40184
  tempDirectory: tree.tempDirectory,
40160
40185
  stagedFiles: tree.materializedFiles,
40161
40186
  cleanup: tree.cleanup
40162
- })))
40187
+ })), withSpan("StagedFiles.materialize"))
40163
40188
  });
40164
40189
  }));
40165
40190
  /**
@@ -40227,7 +40252,10 @@ const runEditorScan = async (input) => {
40227
40252
  isCi: false,
40228
40253
  resolveLocalGithubViewerPermission: false,
40229
40254
  skipJsxIncludeFilter: true
40230
- }).pipe(provide(layers), provide(layerOtlp)));
40255
+ }).pipe(withSpan("runEditorScan", { attributes: {
40256
+ "editor.lint": lint,
40257
+ "editor.runDeadCode": runDeadCode
40258
+ } }), provide(layers), provide(layerOtlp)));
40231
40259
  if (isSuccess(exit)) {
40232
40260
  const output = exit.value;
40233
40261
  return {
@@ -40257,7 +40285,7 @@ const runEditorScan = async (input) => {
40257
40285
  didDeadCodeFail: false,
40258
40286
  deadCodeFailureReason: null,
40259
40287
  lintPartialFailures: [],
40260
- error: error instanceof Error ? error.message : String(error)
40288
+ error: messageFromUnknown(error)
40261
40289
  };
40262
40290
  };
40263
40291
  /**
@@ -40539,7 +40567,6 @@ const toLspDiagnostic = (input) => {
40539
40567
  data
40540
40568
  };
40541
40569
  };
40542
- const toUri = (absoluteFilePath) => fsPathToUri(absoluteFilePath);
40543
40570
  /**
40544
40571
  * Owns the published-diagnostic state. Maps scan outcomes to LSP
40545
40572
  * diagnostics, publishes complete per-URI replacement sets (so the
@@ -40569,7 +40596,7 @@ var DiagnosticsManager = class {
40569
40596
  const isProtectedPath = (fsPath) => protectOpen && this.isOpen(fsPath);
40570
40597
  for (const [fsPath, coreDiagnostics] of outcome.byFile) {
40571
40598
  if (isProtectedPath(fsPath)) continue;
40572
- const uri = toUri(fsPath);
40599
+ const uri = fsPathToUri(fsPath);
40573
40600
  const text = this.textProvider(fsPath);
40574
40601
  const lspDiagnostics = coreDiagnostics.map((diagnostic) => toLspDiagnostic({
40575
40602
  diagnostic,
@@ -40591,7 +40618,7 @@ var DiagnosticsManager = class {
40591
40618
  for (const fsPath of outcome.requestedPaths) {
40592
40619
  if (isProtectedPath(fsPath)) continue;
40593
40620
  if (outcome.byFile.has(fsPath)) continue;
40594
- const uri = toUri(fsPath);
40621
+ const uri = fsPathToUri(fsPath);
40595
40622
  if (this.byUri.has(uri)) this.byUri.delete(uri);
40596
40623
  this.publish(uri, []);
40597
40624
  }
@@ -40616,7 +40643,7 @@ var DiagnosticsManager = class {
40616
40643
  const set = this.projectUris.get(project) ?? /* @__PURE__ */ new Set();
40617
40644
  for (const uri of liveUris) set.add(uri);
40618
40645
  for (const fsPath of outcome.requestedPaths) {
40619
- const uri = toUri(fsPath);
40646
+ const uri = fsPathToUri(fsPath);
40620
40647
  if (!liveUris.has(uri)) set.delete(uri);
40621
40648
  }
40622
40649
  this.projectUris.set(project, set);
@@ -40644,7 +40671,7 @@ var DiagnosticsManager = class {
40644
40671
  const tracked = this.projectUris.get(project);
40645
40672
  if (!tracked) return;
40646
40673
  const liveUris = /* @__PURE__ */ new Set();
40647
- for (const fsPath of liveFsPaths) liveUris.add(toUri(fsPath));
40674
+ for (const fsPath of liveFsPaths) liveUris.add(fsPathToUri(fsPath));
40648
40675
  for (const uri of [...tracked]) {
40649
40676
  if (liveUris.has(uri)) continue;
40650
40677
  this.byUri.delete(uri);
@@ -40941,7 +40968,7 @@ const createProjectGraph = (options) => {
40941
40968
  });
40942
40969
  }
40943
40970
  } catch (error) {
40944
- logger.warn(`Project discovery failed for ${root}: ${error instanceof Error ? error.message : String(error)}`);
40971
+ logger.warn(`Project discovery failed for ${root}: ${messageFromUnknown(error)}`);
40945
40972
  }
40946
40973
  return [...seen.values()].sort((first, second) => second.directory.length - first.directory.length);
40947
40974
  };
@@ -40969,6 +40996,11 @@ const createProjectGraph = (options) => {
40969
40996
  }
40970
40997
  };
40971
40998
  };
40999
+ const toProjectRelative = (projectDirectory, filePath) => {
41000
+ const relative = Path.relative(projectDirectory, filePath).replace(/\\/g, "/");
41001
+ if (relative.length === 0 || relative.startsWith("../") || Path.isAbsolute(relative)) return null;
41002
+ return relative;
41003
+ };
40972
41004
  const resolveCacheFilePath = (projectDirectory) => {
40973
41005
  const nodeModules = path.join(projectDirectory, "node_modules");
40974
41006
  if (fs.existsSync(nodeModules)) return path.join(nodeModules, ".cache", "react-doctor", "lint-cache.json");
@@ -41003,7 +41035,7 @@ const createLintCache = (input) => {
41003
41035
  fs.writeFileSync(tempPath, JSON.stringify(payload));
41004
41036
  fs.renameSync(tempPath, cacheFilePath);
41005
41037
  } catch (error) {
41006
- logger.warn(`Failed to persist lint cache: ${error instanceof Error ? error.message : String(error)}`);
41038
+ logger.warn(`Failed to persist lint cache: ${messageFromUnknown(error)}`);
41007
41039
  }
41008
41040
  };
41009
41041
  return {
@@ -41029,11 +41061,6 @@ const createLintCache = (input) => {
41029
41061
  };
41030
41062
  const OVERLAY_TEMP_PREFIX = "react-doctor-lsp-";
41031
41063
  const OVERLAY_CONFIG_FILENAMES = [...new Set([...STAGED_FILES_PROJECT_CONFIG_FILENAMES, ...ADOPTABLE_LINT_CONFIG_FILENAMES])];
41032
- const toProjectRelative$1 = (projectDirectory, filePath) => {
41033
- const relative = path.relative(projectDirectory, filePath).replace(/\\/g, "/");
41034
- if (relative.length === 0 || relative.startsWith("../") || path.isAbsolute(relative)) return null;
41035
- return relative;
41036
- };
41037
41064
  /**
41038
41065
  * Writes the live (possibly unsaved) content of the target files into a
41039
41066
  * throwaway temp tree that mirrors the project, alongside the well-known
@@ -41047,7 +41074,7 @@ const materializeOverlay = (input) => {
41047
41074
  const relativePaths = [];
41048
41075
  try {
41049
41076
  for (const filePath of input.files) {
41050
- const relative = toProjectRelative$1(input.projectDirectory, filePath);
41077
+ const relative = toProjectRelative(input.projectDirectory, filePath);
41051
41078
  if (relative === null) continue;
41052
41079
  const content = input.readText(filePath);
41053
41080
  if (content === null) continue;
@@ -41095,11 +41122,6 @@ const materializeOverlay = (input) => {
41095
41122
  throw error;
41096
41123
  }
41097
41124
  };
41098
- const toProjectRelative = (projectDirectory, filePath) => {
41099
- const relative = path.relative(projectDirectory, filePath).replace(/\\/g, "/");
41100
- if (relative.length === 0 || relative.startsWith("../") || path.isAbsolute(relative)) return null;
41101
- return relative;
41102
- };
41103
41125
  /**
41104
41126
  * Resolves a diagnostic's (possibly relative, possibly overlay-temp)
41105
41127
  * file path back to the canonical absolute path inside the real project.
@@ -41321,7 +41343,7 @@ const createScheduler = (options) => {
41321
41343
  if (outcome && !token.isCancelled) options.onResult(outcome);
41322
41344
  }).catch((error) => {
41323
41345
  if (options.onError) options.onError(error, request);
41324
- else logger.error(`Scan failed: ${error instanceof Error ? error.message : String(error)}`);
41346
+ else logger.error(`Scan failed: ${messageFromUnknown(error)}`);
41325
41347
  }).finally(() => {
41326
41348
  running -= 1;
41327
41349
  if (isBackground) runningBackground -= 1;
@@ -41708,7 +41730,7 @@ const createServer = (connection, options = {}) => {
41708
41730
  maybeWarnLintUnavailable(outcome);
41709
41731
  if (outcome.request.priority === "background") scanTelemetry.accumulate(outcome);
41710
41732
  },
41711
- onError: (error, request) => logger.error(`Scan of ${request.projectDirectory} threw: ${error instanceof Error ? error.message : String(error)}`),
41733
+ onError: (error, request) => logger.error(`Scan of ${request.projectDirectory} threw: ${messageFromUnknown(error)}`),
41712
41734
  onIdleChange: (idle) => {
41713
41735
  setBusy(!idle);
41714
41736
  if (idle) scanTelemetry.finish();
@@ -42356,5 +42378,5 @@ const startLanguageServer = () => {
42356
42378
  };
42357
42379
  //#endregion
42358
42380
  export { startLanguageServer };
42359
- !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]="03349093-b1b2-5f21-bad3-7e212e5a7f91")}catch(e){}}();
42360
- //# debugId=03349093-b1b2-5f21-bad3-7e212e5a7f91
42381
+ !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]="657a7bbf-22e5-505b-a956-410727faec39")}catch(e){}}();
42382
+ //# debugId=657a7bbf-22e5-505b-a956-410727faec39
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor",
3
- "version": "0.5.6-dev.451beeb",
3
+ "version": "0.5.6-dev.4e06b2a",
4
4
  "description": "Your agent writes bad React. This catches it",
5
5
  "keywords": [
6
6
  "accessibility",
@@ -64,7 +64,7 @@
64
64
  "vscode-languageserver": "^9.0.1",
65
65
  "vscode-languageserver-textdocument": "^1.0.12",
66
66
  "vscode-uri": "^3.1.0",
67
- "oxlint-plugin-react-doctor": "0.5.6-dev.451beeb"
67
+ "oxlint-plugin-react-doctor": "0.5.6-dev.4e06b2a"
68
68
  },
69
69
  "devDependencies": {
70
70
  "@types/babel__code-frame": "^7.27.0",