react-doctor 0.5.6-dev.eafac9d → 0.5.6-dev.ed0258c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js 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]="b4f6f7e7-99db-553a-aa15-14b646162f39")}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]="36898527-7b70-58ed-8b5b-19ca48903764")}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";
@@ -33620,6 +33620,11 @@ const ES_TARGET_YEAR_BY_NAME = {
33620
33620
  esnext: 9999
33621
33621
  };
33622
33622
  /**
33623
+ * tsconfig filenames probed when resolving a project's TypeScript
33624
+ * compiler options — the root config first, then a monorepo base config.
33625
+ */
33626
+ const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
33627
+ /**
33623
33628
  * Project-config files that `StagedFiles.materialize` copies into
33624
33629
  * the temp directory alongside staged sources so oxlint resolves
33625
33630
  * `tsconfig` / `package.json` / lint configs the same way it would
@@ -36472,15 +36477,10 @@ const buildCapabilities = (project) => {
36472
36477
  }
36473
36478
  if (project.tailwindVersion !== null) {
36474
36479
  capabilities.add("tailwind");
36475
- const tailwind = parseTailwindMajorMinor(project.tailwindVersion);
36476
- if (isTailwindAtLeast(tailwind, {
36480
+ if (isTailwindAtLeast(parseTailwindMajorMinor(project.tailwindVersion), {
36477
36481
  major: 3,
36478
36482
  minor: 4
36479
36483
  })) capabilities.add("tailwind:3.4");
36480
- if (tailwind !== null && isTailwindAtLeast(tailwind, {
36481
- major: 4,
36482
- minor: 0
36483
- })) capabilities.add("tailwind:4");
36484
36484
  }
36485
36485
  if (project.zodVersion !== null) {
36486
36486
  capabilities.add("zod");
@@ -36710,8 +36710,8 @@ const collectIgnorePatterns = (rootDirectory) => {
36710
36710
  cachedPatternsByRoot.set(rootDirectory, patterns);
36711
36711
  return patterns;
36712
36712
  };
36713
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36713
36714
  const KNIP_JSON_FILENAME = "knip.json";
36714
- const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36715
36715
  const readJsonFileSafe = (filePath) => {
36716
36716
  let rawContents;
36717
36717
  try {
@@ -36727,10 +36727,10 @@ const readJsonFileSafe = (filePath) => {
36727
36727
  };
36728
36728
  const readKnipConfig = (rootDirectory) => {
36729
36729
  const knipJson = readJsonFileSafe(path.join(rootDirectory, KNIP_JSON_FILENAME));
36730
- if (isRecord$1(knipJson)) return knipJson;
36730
+ if (isRecord(knipJson)) return knipJson;
36731
36731
  const packageJson = readJsonFileSafe(path.join(rootDirectory, "package.json"));
36732
- const packageKnipConfig = isRecord$1(packageJson) ? packageJson.knip : null;
36733
- return isRecord$1(packageKnipConfig) ? packageKnipConfig : null;
36732
+ const packageKnipConfig = isRecord(packageJson) ? packageJson.knip : null;
36733
+ return isRecord(packageKnipConfig) ? packageKnipConfig : null;
36734
36734
  };
36735
36735
  const normalizePatternList = (value) => {
36736
36736
  if (typeof value === "string" && value.length > 0) return [value];
@@ -36742,10 +36742,10 @@ const prefixWorkspacePatterns = (workspacePattern, patterns) => {
36742
36742
  return patterns.map((pattern) => pattern.startsWith("!") ? `!${normalizedWorkspacePattern}/${pattern.slice(1)}` : `${normalizedWorkspacePattern}/${pattern}`);
36743
36743
  };
36744
36744
  const collectKnipWorkspacePatterns = (workspaces, settingName) => {
36745
- if (!isRecord$1(workspaces)) return [];
36745
+ if (!isRecord(workspaces)) return [];
36746
36746
  const patterns = [];
36747
36747
  for (const [workspacePattern, workspaceConfig] of Object.entries(workspaces)) {
36748
- if (!isRecord$1(workspaceConfig)) continue;
36748
+ if (!isRecord(workspaceConfig)) continue;
36749
36749
  patterns.push(...prefixWorkspacePatterns(workspacePattern, normalizePatternList(workspaceConfig[settingName])));
36750
36750
  }
36751
36751
  return patterns;
@@ -36790,8 +36790,6 @@ const toCanonicalPath = (filePath) => {
36790
36790
  };
36791
36791
  const DEAD_CODE_PLUGIN = "deslop";
36792
36792
  const DEAD_CODE_CATEGORY = "Maintainability";
36793
- const TSCONFIG_FILENAMES$1 = ["tsconfig.json", "tsconfig.base.json"];
36794
- const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36795
36793
  const DEAD_CODE_WORKER_SCRIPT = `
36796
36794
  const inputChunks = [];
36797
36795
  process.stdin.on("data", (chunk) => inputChunks.push(chunk));
@@ -36849,7 +36847,7 @@ process.stdin.on("end", () => {
36849
36847
  });
36850
36848
  `;
36851
36849
  const resolveTsConfigPath = (rootDirectory) => {
36852
- for (const filename of TSCONFIG_FILENAMES$1) {
36850
+ for (const filename of TSCONFIG_FILENAMES) {
36853
36851
  const candidate = Path.join(rootDirectory, filename);
36854
36852
  if (NFS.existsSync(candidate)) return candidate;
36855
36853
  }
@@ -38089,8 +38087,8 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
38089
38087
  }
38090
38088
  return enabled;
38091
38089
  };
38092
- const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [] }) => {
38093
- const reactHooksJsPlugin = resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
38090
+ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false }) => {
38091
+ const reactHooksJsPlugin = disableReactHooksJsPlugin ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
38094
38092
  const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
38095
38093
  const jsPlugins = [];
38096
38094
  if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
@@ -38150,7 +38148,6 @@ const resolveOxlintBinary = () => {
38150
38148
  return Path.join(oxlintPackageDirectory, "bin", "oxlint");
38151
38149
  };
38152
38150
  const resolvePluginPath = () => esmRequire.resolve("oxlint-plugin-react-doctor");
38153
- const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
38154
38151
  const resolveTsConfigRelativePath = (rootDirectory) => {
38155
38152
  for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
38156
38153
  return null;
@@ -38522,7 +38519,7 @@ const scopeContainsNonImportBinding = (node, scopeNode, identifierName) => {
38522
38519
  const isIdentifierShadowedByLocalBinding = (identifier, sourceFile) => {
38523
38520
  let currentNode = identifier.parent;
38524
38521
  while (currentNode) {
38525
- if (isScopeNode(currentNode)) {
38522
+ if (isScopeBoundary(currentNode)) {
38526
38523
  if (scopeContainsNonImportBinding(currentNode, currentNode, identifier.text)) return true;
38527
38524
  }
38528
38525
  if (currentNode === sourceFile) return false;
@@ -38613,11 +38610,10 @@ const findResolutionInScope = (scopeNode, identifierName, reactImportBindings, s
38613
38610
  });
38614
38611
  return resolution;
38615
38612
  };
38616
- const isScopeNode = isScopeBoundary;
38617
38613
  const resolveIdentifierBinding = (identifier, reactImportBindings, sourceFile, visitedDeclarations = /* @__PURE__ */ new Set()) => {
38618
38614
  let currentNode = identifier.parent;
38619
38615
  while (currentNode) {
38620
- if (isScopeNode(currentNode)) {
38616
+ if (isScopeBoundary(currentNode)) {
38621
38617
  const resolution = findResolutionInScope(currentNode, identifier.text, reactImportBindings, sourceFile, visitedDeclarations);
38622
38618
  if (resolution) return resolution;
38623
38619
  }
@@ -38787,9 +38783,9 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
38787
38783
  try {
38788
38784
  parsed = JSON.parse(sanitizedStdout);
38789
38785
  } catch {
38790
- throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 200) }) });
38786
+ throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
38791
38787
  }
38792
- if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 200) }) });
38788
+ if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
38793
38789
  const minifiedFileCache = /* @__PURE__ */ new Map();
38794
38790
  const isMinifiedDiagnosticFile = (filename) => {
38795
38791
  const absolutePath = Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
@@ -39080,6 +39076,28 @@ const writeOxlintConfig = (configPath, configToWrite) => {
39080
39076
  NFS.closeSync(fileHandle);
39081
39077
  }
39082
39078
  };
39079
+ const REACT_HOOKS_JS_DROP_PREFIX = "React Compiler rules (react-hooks-js/*) skipped — eslint-plugin-react-hooks failed to load in this environment";
39080
+ /**
39081
+ * Detects an oxlint config-load crash caused by the optional
39082
+ * `react-hooks-js` (eslint-plugin-react-hooks) React Compiler plugin and
39083
+ * builds the partial-failure note for it; returns `null` when the failure
39084
+ * was anything else.
39085
+ *
39086
+ * oxlint prints a framed error to stdout (not stderr) and exits non-zero
39087
+ * when a `jsPlugins` entry can't be imported; that non-JSON stdout
39088
+ * surfaces as `OxlintOutputUnparseable`. Because oxlint fails the WHOLE
39089
+ * config load on it, leaving the plugin in would drop every curated
39090
+ * react-doctor diagnostic too — so the caller retries with the plugin
39091
+ * stripped (issue #833). Both markers sit at the start of oxlint's
39092
+ * message, so they survive the `preview` slice even for deep pnpm paths.
39093
+ */
39094
+ const reactHooksJsPluginDropNote = (error) => {
39095
+ if (!(error instanceof ReactDoctorError) || error.reason._tag !== "OxlintOutputUnparseable") return null;
39096
+ const { preview } = error.reason;
39097
+ if (!preview.includes("Failed to load JS plugin") || !preview.includes("eslint-plugin-react-hooks")) return null;
39098
+ const underlyingReason = preview.match(/Error:[^\n]*/)?.[0]?.trim();
39099
+ return `${REACT_HOOKS_JS_DROP_PREFIX}${underlyingReason ? `: ${underlyingReason}` : ""}. Other rules ran normally.`;
39100
+ };
39083
39101
  /**
39084
39102
  * The oxlint runner. Composed of three pieces in `runners/oxlint/`:
39085
39103
  *
@@ -39107,15 +39125,16 @@ const runOxlint = async (options) => {
39107
39125
  const pluginPath = resolvePluginPath();
39108
39126
  const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
39109
39127
  const userPlugins = resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
39110
- const buildConfig = (extendsForThisAttempt) => createOxlintConfig({
39128
+ const buildConfig = (overrides) => createOxlintConfig({
39111
39129
  pluginPath,
39112
39130
  project,
39113
39131
  customRulesOnly,
39114
- extendsPaths: extendsForThisAttempt,
39132
+ extendsPaths: overrides.extendsPaths,
39115
39133
  ignoredTags,
39116
39134
  serverAuthFunctionNames,
39117
39135
  severityControls,
39118
- userPlugins
39136
+ userPlugins,
39137
+ disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin
39119
39138
  });
39120
39139
  const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
39121
39140
  const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
@@ -39151,12 +39170,22 @@ const runOxlint = async (options) => {
39151
39170
  outputMaxBytes,
39152
39171
  concurrency: options.concurrency
39153
39172
  });
39154
- writeOxlintConfig(configPath, buildConfig(extendsPaths));
39173
+ writeOxlintConfig(configPath, buildConfig({ extendsPaths }));
39155
39174
  try {
39156
39175
  return await runBatches();
39157
39176
  } catch (error) {
39177
+ const reactHooksJsDropNote = reactHooksJsPluginDropNote(error);
39178
+ if (reactHooksJsDropNote !== null) {
39179
+ writeOxlintConfig(configPath, buildConfig({
39180
+ extendsPaths,
39181
+ disableReactHooksJsPlugin: true
39182
+ }));
39183
+ const diagnostics = await runBatches();
39184
+ onPartialFailure?.(reactHooksJsDropNote);
39185
+ return diagnostics;
39186
+ }
39158
39187
  if (extendsPaths.length === 0) throw error;
39159
- writeOxlintConfig(configPath, buildConfig([]));
39188
+ writeOxlintConfig(configPath, buildConfig({ extendsPaths: [] }));
39160
39189
  return await runBatches();
39161
39190
  }
39162
39191
  } finally {
@@ -40576,4 +40605,4 @@ const toJsonReport = (result, options) => buildJsonReport({
40576
40605
  export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
40577
40606
 
40578
40607
  //# sourceMappingURL=index.js.map
40579
- //# debugId=b4f6f7e7-99db-553a-aa15-14b646162f39
40608
+ //# debugId=36898527-7b70-58ed-8b5b-19ca48903764
package/dist/lsp.js CHANGED
@@ -33657,6 +33657,11 @@ const ES_TARGET_YEAR_BY_NAME = {
33657
33657
  esnext: 9999
33658
33658
  };
33659
33659
  /**
33660
+ * tsconfig filenames probed when resolving a project's TypeScript
33661
+ * compiler options — the root config first, then a monorepo base config.
33662
+ */
33663
+ const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
33664
+ /**
33660
33665
  * Project-config files that `StagedFiles.materialize` copies into
33661
33666
  * the temp directory alongside staged sources so oxlint resolves
33662
33667
  * `tsconfig` / `package.json` / lint configs the same way it would
@@ -36458,15 +36463,10 @@ const buildCapabilities = (project) => {
36458
36463
  }
36459
36464
  if (project.tailwindVersion !== null) {
36460
36465
  capabilities.add("tailwind");
36461
- const tailwind = parseTailwindMajorMinor(project.tailwindVersion);
36462
- if (isTailwindAtLeast(tailwind, {
36466
+ if (isTailwindAtLeast(parseTailwindMajorMinor(project.tailwindVersion), {
36463
36467
  major: 3,
36464
36468
  minor: 4
36465
36469
  })) capabilities.add("tailwind:3.4");
36466
- if (tailwind !== null && isTailwindAtLeast(tailwind, {
36467
- major: 4,
36468
- minor: 0
36469
- })) capabilities.add("tailwind:4");
36470
36470
  }
36471
36471
  if (project.zodVersion !== null) {
36472
36472
  capabilities.add("zod");
@@ -36696,8 +36696,8 @@ const collectIgnorePatterns = (rootDirectory) => {
36696
36696
  cachedPatternsByRoot.set(rootDirectory, patterns);
36697
36697
  return patterns;
36698
36698
  };
36699
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36699
36700
  const KNIP_JSON_FILENAME = "knip.json";
36700
- const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36701
36701
  const readJsonFileSafe = (filePath) => {
36702
36702
  let rawContents;
36703
36703
  try {
@@ -36713,10 +36713,10 @@ const readJsonFileSafe = (filePath) => {
36713
36713
  };
36714
36714
  const readKnipConfig = (rootDirectory) => {
36715
36715
  const knipJson = readJsonFileSafe(path.join(rootDirectory, KNIP_JSON_FILENAME));
36716
- if (isRecord$1(knipJson)) return knipJson;
36716
+ if (isRecord(knipJson)) return knipJson;
36717
36717
  const packageJson = readJsonFileSafe(path.join(rootDirectory, "package.json"));
36718
- const packageKnipConfig = isRecord$1(packageJson) ? packageJson.knip : null;
36719
- return isRecord$1(packageKnipConfig) ? packageKnipConfig : null;
36718
+ const packageKnipConfig = isRecord(packageJson) ? packageJson.knip : null;
36719
+ return isRecord(packageKnipConfig) ? packageKnipConfig : null;
36720
36720
  };
36721
36721
  const normalizePatternList = (value) => {
36722
36722
  if (typeof value === "string" && value.length > 0) return [value];
@@ -36728,10 +36728,10 @@ const prefixWorkspacePatterns = (workspacePattern, patterns) => {
36728
36728
  return patterns.map((pattern) => pattern.startsWith("!") ? `!${normalizedWorkspacePattern}/${pattern.slice(1)}` : `${normalizedWorkspacePattern}/${pattern}`);
36729
36729
  };
36730
36730
  const collectKnipWorkspacePatterns = (workspaces, settingName) => {
36731
- if (!isRecord$1(workspaces)) return [];
36731
+ if (!isRecord(workspaces)) return [];
36732
36732
  const patterns = [];
36733
36733
  for (const [workspacePattern, workspaceConfig] of Object.entries(workspaces)) {
36734
- if (!isRecord$1(workspaceConfig)) continue;
36734
+ if (!isRecord(workspaceConfig)) continue;
36735
36735
  patterns.push(...prefixWorkspacePatterns(workspacePattern, normalizePatternList(workspaceConfig[settingName])));
36736
36736
  }
36737
36737
  return patterns;
@@ -36776,8 +36776,6 @@ const toCanonicalPath = (filePath) => {
36776
36776
  };
36777
36777
  const DEAD_CODE_PLUGIN = "deslop";
36778
36778
  const DEAD_CODE_CATEGORY = "Maintainability";
36779
- const TSCONFIG_FILENAMES$1 = ["tsconfig.json", "tsconfig.base.json"];
36780
- const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36781
36779
  const DEAD_CODE_WORKER_SCRIPT = `
36782
36780
  const inputChunks = [];
36783
36781
  process.stdin.on("data", (chunk) => inputChunks.push(chunk));
@@ -36835,7 +36833,7 @@ process.stdin.on("end", () => {
36835
36833
  });
36836
36834
  `;
36837
36835
  const resolveTsConfigPath = (rootDirectory) => {
36838
- for (const filename of TSCONFIG_FILENAMES$1) {
36836
+ for (const filename of TSCONFIG_FILENAMES) {
36839
36837
  const candidate = Path.join(rootDirectory, filename);
36840
36838
  if (NFS.existsSync(candidate)) return candidate;
36841
36839
  }
@@ -38075,8 +38073,8 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
38075
38073
  }
38076
38074
  return enabled;
38077
38075
  };
38078
- const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [] }) => {
38079
- const reactHooksJsPlugin = resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
38076
+ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false }) => {
38077
+ const reactHooksJsPlugin = disableReactHooksJsPlugin ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
38080
38078
  const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
38081
38079
  const jsPlugins = [];
38082
38080
  if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
@@ -38136,7 +38134,6 @@ const resolveOxlintBinary = () => {
38136
38134
  return Path.join(oxlintPackageDirectory, "bin", "oxlint");
38137
38135
  };
38138
38136
  const resolvePluginPath = () => esmRequire.resolve("oxlint-plugin-react-doctor");
38139
- const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
38140
38137
  const resolveTsConfigRelativePath = (rootDirectory) => {
38141
38138
  for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
38142
38139
  return null;
@@ -38508,7 +38505,7 @@ const scopeContainsNonImportBinding = (node, scopeNode, identifierName) => {
38508
38505
  const isIdentifierShadowedByLocalBinding = (identifier, sourceFile) => {
38509
38506
  let currentNode = identifier.parent;
38510
38507
  while (currentNode) {
38511
- if (isScopeNode(currentNode)) {
38508
+ if (isScopeBoundary(currentNode)) {
38512
38509
  if (scopeContainsNonImportBinding(currentNode, currentNode, identifier.text)) return true;
38513
38510
  }
38514
38511
  if (currentNode === sourceFile) return false;
@@ -38599,11 +38596,10 @@ const findResolutionInScope = (scopeNode, identifierName, reactImportBindings, s
38599
38596
  });
38600
38597
  return resolution;
38601
38598
  };
38602
- const isScopeNode = isScopeBoundary;
38603
38599
  const resolveIdentifierBinding = (identifier, reactImportBindings, sourceFile, visitedDeclarations = /* @__PURE__ */ new Set()) => {
38604
38600
  let currentNode = identifier.parent;
38605
38601
  while (currentNode) {
38606
- if (isScopeNode(currentNode)) {
38602
+ if (isScopeBoundary(currentNode)) {
38607
38603
  const resolution = findResolutionInScope(currentNode, identifier.text, reactImportBindings, sourceFile, visitedDeclarations);
38608
38604
  if (resolution) return resolution;
38609
38605
  }
@@ -38773,9 +38769,9 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
38773
38769
  try {
38774
38770
  parsed = JSON.parse(sanitizedStdout);
38775
38771
  } catch {
38776
- throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 200) }) });
38772
+ throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
38777
38773
  }
38778
- if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 200) }) });
38774
+ if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
38779
38775
  const minifiedFileCache = /* @__PURE__ */ new Map();
38780
38776
  const isMinifiedDiagnosticFile = (filename) => {
38781
38777
  const absolutePath = Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
@@ -39066,6 +39062,28 @@ const writeOxlintConfig = (configPath, configToWrite) => {
39066
39062
  NFS.closeSync(fileHandle);
39067
39063
  }
39068
39064
  };
39065
+ const REACT_HOOKS_JS_DROP_PREFIX = "React Compiler rules (react-hooks-js/*) skipped — eslint-plugin-react-hooks failed to load in this environment";
39066
+ /**
39067
+ * Detects an oxlint config-load crash caused by the optional
39068
+ * `react-hooks-js` (eslint-plugin-react-hooks) React Compiler plugin and
39069
+ * builds the partial-failure note for it; returns `null` when the failure
39070
+ * was anything else.
39071
+ *
39072
+ * oxlint prints a framed error to stdout (not stderr) and exits non-zero
39073
+ * when a `jsPlugins` entry can't be imported; that non-JSON stdout
39074
+ * surfaces as `OxlintOutputUnparseable`. Because oxlint fails the WHOLE
39075
+ * config load on it, leaving the plugin in would drop every curated
39076
+ * react-doctor diagnostic too — so the caller retries with the plugin
39077
+ * stripped (issue #833). Both markers sit at the start of oxlint's
39078
+ * message, so they survive the `preview` slice even for deep pnpm paths.
39079
+ */
39080
+ const reactHooksJsPluginDropNote = (error) => {
39081
+ if (!(error instanceof ReactDoctorError) || error.reason._tag !== "OxlintOutputUnparseable") return null;
39082
+ const { preview } = error.reason;
39083
+ if (!preview.includes("Failed to load JS plugin") || !preview.includes("eslint-plugin-react-hooks")) return null;
39084
+ const underlyingReason = preview.match(/Error:[^\n]*/)?.[0]?.trim();
39085
+ return `${REACT_HOOKS_JS_DROP_PREFIX}${underlyingReason ? `: ${underlyingReason}` : ""}. Other rules ran normally.`;
39086
+ };
39069
39087
  /**
39070
39088
  * The oxlint runner. Composed of three pieces in `runners/oxlint/`:
39071
39089
  *
@@ -39093,15 +39111,16 @@ const runOxlint = async (options) => {
39093
39111
  const pluginPath = resolvePluginPath();
39094
39112
  const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
39095
39113
  const userPlugins = resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
39096
- const buildConfig = (extendsForThisAttempt) => createOxlintConfig({
39114
+ const buildConfig = (overrides) => createOxlintConfig({
39097
39115
  pluginPath,
39098
39116
  project,
39099
39117
  customRulesOnly,
39100
- extendsPaths: extendsForThisAttempt,
39118
+ extendsPaths: overrides.extendsPaths,
39101
39119
  ignoredTags,
39102
39120
  serverAuthFunctionNames,
39103
39121
  severityControls,
39104
- userPlugins
39122
+ userPlugins,
39123
+ disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin
39105
39124
  });
39106
39125
  const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
39107
39126
  const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
@@ -39137,12 +39156,22 @@ const runOxlint = async (options) => {
39137
39156
  outputMaxBytes,
39138
39157
  concurrency: options.concurrency
39139
39158
  });
39140
- writeOxlintConfig(configPath, buildConfig(extendsPaths));
39159
+ writeOxlintConfig(configPath, buildConfig({ extendsPaths }));
39141
39160
  try {
39142
39161
  return await runBatches();
39143
39162
  } catch (error) {
39163
+ const reactHooksJsDropNote = reactHooksJsPluginDropNote(error);
39164
+ if (reactHooksJsDropNote !== null) {
39165
+ writeOxlintConfig(configPath, buildConfig({
39166
+ extendsPaths,
39167
+ disableReactHooksJsPlugin: true
39168
+ }));
39169
+ const diagnostics = await runBatches();
39170
+ onPartialFailure?.(reactHooksJsDropNote);
39171
+ return diagnostics;
39172
+ }
39144
39173
  if (extendsPaths.length === 0) throw error;
39145
- writeOxlintConfig(configPath, buildConfig([]));
39174
+ writeOxlintConfig(configPath, buildConfig({ extendsPaths: [] }));
39146
39175
  return await runBatches();
39147
39176
  }
39148
39177
  } finally {
@@ -40974,6 +41003,11 @@ const createProjectGraph = (options) => {
40974
41003
  }
40975
41004
  };
40976
41005
  };
41006
+ const toProjectRelative = (projectDirectory, filePath) => {
41007
+ const relative = Path.relative(projectDirectory, filePath).replace(/\\/g, "/");
41008
+ if (relative.length === 0 || relative.startsWith("../") || Path.isAbsolute(relative)) return null;
41009
+ return relative;
41010
+ };
40977
41011
  const resolveCacheFilePath = (projectDirectory) => {
40978
41012
  const nodeModules = path.join(projectDirectory, "node_modules");
40979
41013
  if (fs.existsSync(nodeModules)) return path.join(nodeModules, ".cache", "react-doctor", "lint-cache.json");
@@ -41034,11 +41068,6 @@ const createLintCache = (input) => {
41034
41068
  };
41035
41069
  const OVERLAY_TEMP_PREFIX = "react-doctor-lsp-";
41036
41070
  const OVERLAY_CONFIG_FILENAMES = [...new Set([...STAGED_FILES_PROJECT_CONFIG_FILENAMES, ...ADOPTABLE_LINT_CONFIG_FILENAMES])];
41037
- const toProjectRelative$1 = (projectDirectory, filePath) => {
41038
- const relative = path.relative(projectDirectory, filePath).replace(/\\/g, "/");
41039
- if (relative.length === 0 || relative.startsWith("../") || path.isAbsolute(relative)) return null;
41040
- return relative;
41041
- };
41042
41071
  /**
41043
41072
  * Writes the live (possibly unsaved) content of the target files into a
41044
41073
  * throwaway temp tree that mirrors the project, alongside the well-known
@@ -41052,7 +41081,7 @@ const materializeOverlay = (input) => {
41052
41081
  const relativePaths = [];
41053
41082
  try {
41054
41083
  for (const filePath of input.files) {
41055
- const relative = toProjectRelative$1(input.projectDirectory, filePath);
41084
+ const relative = toProjectRelative(input.projectDirectory, filePath);
41056
41085
  if (relative === null) continue;
41057
41086
  const content = input.readText(filePath);
41058
41087
  if (content === null) continue;
@@ -41100,11 +41129,6 @@ const materializeOverlay = (input) => {
41100
41129
  throw error;
41101
41130
  }
41102
41131
  };
41103
- const toProjectRelative = (projectDirectory, filePath) => {
41104
- const relative = path.relative(projectDirectory, filePath).replace(/\\/g, "/");
41105
- if (relative.length === 0 || relative.startsWith("../") || path.isAbsolute(relative)) return null;
41106
- return relative;
41107
- };
41108
41132
  /**
41109
41133
  * Resolves a diagnostic's (possibly relative, possibly overlay-temp)
41110
41134
  * file path back to the canonical absolute path inside the real project.
@@ -42361,5 +42385,5 @@ const startLanguageServer = () => {
42361
42385
  };
42362
42386
  //#endregion
42363
42387
  export { startLanguageServer };
42364
- !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]="9421149c-0ed9-5e00-81da-4f7dd8faf332")}catch(e){}}();
42365
- //# debugId=9421149c-0ed9-5e00-81da-4f7dd8faf332
42388
+ !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]="42683a4c-8da1-5155-bf9c-b9a69b9718e6")}catch(e){}}();
42389
+ //# debugId=42683a4c-8da1-5155-bf9c-b9a69b9718e6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor",
3
- "version": "0.5.6-dev.eafac9d",
3
+ "version": "0.5.6-dev.ed0258c",
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.eafac9d"
67
+ "oxlint-plugin-react-doctor": "0.5.6-dev.ed0258c"
68
68
  },
69
69
  "devDependencies": {
70
70
  "@types/babel__code-frame": "^7.27.0",
@@ -72,8 +72,8 @@
72
72
  "@xterm/headless": "^6.0.0",
73
73
  "commander": "^14.0.3",
74
74
  "ora": "^9.4.0",
75
- "@react-doctor/core": "0.5.6",
76
75
  "@react-doctor/api": "0.5.6",
76
+ "@react-doctor/core": "0.5.6",
77
77
  "@react-doctor/language-server": "0.5.6"
78
78
  },
79
79
  "engines": {