react-doctor 0.5.6-dev.b08ca1c → 0.5.6-dev.b317164
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/cli.js +262 -84
- package/dist/index.js +44 -11
- package/dist/lsp.js +44 -11
- package/package.json +3 -3
package/dist/cli.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]="
|
|
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]="ad091b20-c3e2-5c96-93ac-9a910745a035")}catch(e){}}();
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import * as NodeChildProcess from "node:child_process";
|
|
5
5
|
import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
@@ -14,7 +14,7 @@ import * as OS from "node:os";
|
|
|
14
14
|
import os, { tmpdir } from "node:os";
|
|
15
15
|
import { parseJSON5 } from "confbox";
|
|
16
16
|
import * as NodeUrl from "node:url";
|
|
17
|
-
import { fileURLToPath } from "node:url";
|
|
17
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
18
18
|
import { createJiti } from "jiti";
|
|
19
19
|
import * as Crypto from "node:crypto";
|
|
20
20
|
import crypto, { createHash, randomUUID } from "node:crypto";
|
|
@@ -41320,8 +41320,8 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
41320
41320
|
}
|
|
41321
41321
|
return enabled;
|
|
41322
41322
|
};
|
|
41323
|
-
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [] }) => {
|
|
41324
|
-
const reactHooksJsPlugin = resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
41323
|
+
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false }) => {
|
|
41324
|
+
const reactHooksJsPlugin = disableReactHooksJsPlugin ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
41325
41325
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
41326
41326
|
const jsPlugins = [];
|
|
41327
41327
|
if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
|
|
@@ -42018,9 +42018,9 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
42018
42018
|
try {
|
|
42019
42019
|
parsed = JSON.parse(sanitizedStdout);
|
|
42020
42020
|
} catch {
|
|
42021
|
-
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0,
|
|
42021
|
+
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
42022
42022
|
}
|
|
42023
|
-
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0,
|
|
42023
|
+
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
42024
42024
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
42025
42025
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
42026
42026
|
const absolutePath = Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
@@ -42311,6 +42311,28 @@ const writeOxlintConfig = (configPath, configToWrite) => {
|
|
|
42311
42311
|
NFS.closeSync(fileHandle);
|
|
42312
42312
|
}
|
|
42313
42313
|
};
|
|
42314
|
+
const REACT_HOOKS_JS_DROP_PREFIX = "React Compiler rules (react-hooks-js/*) skipped — eslint-plugin-react-hooks failed to load in this environment";
|
|
42315
|
+
/**
|
|
42316
|
+
* Detects an oxlint config-load crash caused by the optional
|
|
42317
|
+
* `react-hooks-js` (eslint-plugin-react-hooks) React Compiler plugin and
|
|
42318
|
+
* builds the partial-failure note for it; returns `null` when the failure
|
|
42319
|
+
* was anything else.
|
|
42320
|
+
*
|
|
42321
|
+
* oxlint prints a framed error to stdout (not stderr) and exits non-zero
|
|
42322
|
+
* when a `jsPlugins` entry can't be imported; that non-JSON stdout
|
|
42323
|
+
* surfaces as `OxlintOutputUnparseable`. Because oxlint fails the WHOLE
|
|
42324
|
+
* config load on it, leaving the plugin in would drop every curated
|
|
42325
|
+
* react-doctor diagnostic too — so the caller retries with the plugin
|
|
42326
|
+
* stripped (issue #833). Both markers sit at the start of oxlint's
|
|
42327
|
+
* message, so they survive the `preview` slice even for deep pnpm paths.
|
|
42328
|
+
*/
|
|
42329
|
+
const reactHooksJsPluginDropNote = (error) => {
|
|
42330
|
+
if (!(error instanceof ReactDoctorError) || error.reason._tag !== "OxlintOutputUnparseable") return null;
|
|
42331
|
+
const { preview } = error.reason;
|
|
42332
|
+
if (!preview.includes("Failed to load JS plugin") || !preview.includes("eslint-plugin-react-hooks")) return null;
|
|
42333
|
+
const underlyingReason = preview.match(/Error:[^\n]*/)?.[0]?.trim();
|
|
42334
|
+
return `${REACT_HOOKS_JS_DROP_PREFIX}${underlyingReason ? `: ${underlyingReason}` : ""}. Other rules ran normally.`;
|
|
42335
|
+
};
|
|
42314
42336
|
/**
|
|
42315
42337
|
* The oxlint runner. Composed of three pieces in `runners/oxlint/`:
|
|
42316
42338
|
*
|
|
@@ -42338,15 +42360,16 @@ const runOxlint = async (options) => {
|
|
|
42338
42360
|
const pluginPath = resolvePluginPath();
|
|
42339
42361
|
const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
|
|
42340
42362
|
const userPlugins = resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
|
|
42341
|
-
const buildConfig = (
|
|
42363
|
+
const buildConfig = (overrides) => createOxlintConfig({
|
|
42342
42364
|
pluginPath,
|
|
42343
42365
|
project,
|
|
42344
42366
|
customRulesOnly,
|
|
42345
|
-
extendsPaths:
|
|
42367
|
+
extendsPaths: overrides.extendsPaths,
|
|
42346
42368
|
ignoredTags,
|
|
42347
42369
|
serverAuthFunctionNames,
|
|
42348
42370
|
severityControls,
|
|
42349
|
-
userPlugins
|
|
42371
|
+
userPlugins,
|
|
42372
|
+
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin
|
|
42350
42373
|
});
|
|
42351
42374
|
const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
|
|
42352
42375
|
const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
|
|
@@ -42382,12 +42405,22 @@ const runOxlint = async (options) => {
|
|
|
42382
42405
|
outputMaxBytes,
|
|
42383
42406
|
concurrency: options.concurrency
|
|
42384
42407
|
});
|
|
42385
|
-
writeOxlintConfig(configPath, buildConfig(extendsPaths));
|
|
42408
|
+
writeOxlintConfig(configPath, buildConfig({ extendsPaths }));
|
|
42386
42409
|
try {
|
|
42387
42410
|
return await runBatches();
|
|
42388
42411
|
} catch (error) {
|
|
42412
|
+
const reactHooksJsDropNote = reactHooksJsPluginDropNote(error);
|
|
42413
|
+
if (reactHooksJsDropNote !== null) {
|
|
42414
|
+
writeOxlintConfig(configPath, buildConfig({
|
|
42415
|
+
extendsPaths,
|
|
42416
|
+
disableReactHooksJsPlugin: true
|
|
42417
|
+
}));
|
|
42418
|
+
const diagnostics = await runBatches();
|
|
42419
|
+
onPartialFailure?.(reactHooksJsDropNote);
|
|
42420
|
+
return diagnostics;
|
|
42421
|
+
}
|
|
42389
42422
|
if (extendsPaths.length === 0) throw error;
|
|
42390
|
-
writeOxlintConfig(configPath, buildConfig([]));
|
|
42423
|
+
writeOxlintConfig(configPath, buildConfig({ extendsPaths: [] }));
|
|
42391
42424
|
return await runBatches();
|
|
42392
42425
|
}
|
|
42393
42426
|
} finally {
|
|
@@ -43839,7 +43872,7 @@ const FALSY_CI_FLAG_VALUES = new Set([
|
|
|
43839
43872
|
"false"
|
|
43840
43873
|
]);
|
|
43841
43874
|
const isCiFlagSet = (value) => value !== void 0 && !FALSY_CI_FLAG_VALUES.has(value.toLowerCase());
|
|
43842
|
-
const isCiEnvironment = () => CI_ENVIRONMENT_VARIABLES.some((environmentVariable) => Boolean(
|
|
43875
|
+
const isCiEnvironment = (env = process.env) => CI_ENVIRONMENT_VARIABLES.some((environmentVariable) => Boolean(env[environmentVariable])) || isCiFlagSet(env.CI);
|
|
43843
43876
|
const detectCiProvider = () => {
|
|
43844
43877
|
for (const [environmentVariable, provider] of CI_PROVIDER_BY_ENVIRONMENT_VARIABLE) if (process.env[environmentVariable]) return provider;
|
|
43845
43878
|
return isCiFlagSet(process.env.CI) ? "unknown" : null;
|
|
@@ -43864,6 +43897,42 @@ const detectCodingAgent = () => {
|
|
|
43864
43897
|
const isCodingAgentEnvironment = () => detectCodingAgent() !== null;
|
|
43865
43898
|
const isCiOrCodingAgentEnvironment = () => isCiEnvironment() || isCodingAgentEnvironment();
|
|
43866
43899
|
//#endregion
|
|
43900
|
+
//#region src/cli/utils/detect-terminal-kind.ts
|
|
43901
|
+
const TERMINAL_BY_TERM_PROGRAM = [
|
|
43902
|
+
["vscode", "vscode"],
|
|
43903
|
+
["iTerm.app", "iterm"],
|
|
43904
|
+
["Apple_Terminal", "apple-terminal"],
|
|
43905
|
+
["WezTerm", "wezterm"],
|
|
43906
|
+
["ghostty", "ghostty"],
|
|
43907
|
+
["Hyper", "hyper"],
|
|
43908
|
+
["Tabby", "tabby"],
|
|
43909
|
+
["rio", "rio"]
|
|
43910
|
+
];
|
|
43911
|
+
/**
|
|
43912
|
+
* Best-effort label for the terminal emulator / editor hosting the CLI,
|
|
43913
|
+
* derived from terminal-identity env vars. Recorded as the `terminalKind` run
|
|
43914
|
+
* tag so we can see where React Doctor is actually run (nvim, VS Code, iTerm,
|
|
43915
|
+
* …) — the split Sentry can't otherwise see. Low-cardinality and free of any
|
|
43916
|
+
* username/path/secret, so it's safe as a tag. Editor terminals (nvim/vim)
|
|
43917
|
+
* win over the outer emulator because that's the surface a user is reading in;
|
|
43918
|
+
* "ci" marks a run with no interactive terminal; "unknown" when nothing matches.
|
|
43919
|
+
*/
|
|
43920
|
+
const detectTerminalKind = (env = process.env) => {
|
|
43921
|
+
if (env.NVIM) return "neovim";
|
|
43922
|
+
if (env.VIM_TERMINAL) return "vim";
|
|
43923
|
+
const termProgram = env.TERM_PROGRAM;
|
|
43924
|
+
if (termProgram) {
|
|
43925
|
+
for (const [marker, label] of TERMINAL_BY_TERM_PROGRAM) if (termProgram === marker) return label;
|
|
43926
|
+
}
|
|
43927
|
+
if (env.KITTY_WINDOW_ID || env.TERM === "xterm-kitty") return "kitty";
|
|
43928
|
+
if (env.WT_SESSION) return "windows-terminal";
|
|
43929
|
+
if (env.ALACRITTY_WINDOW_ID || env.TERM === "alacritty") return "alacritty";
|
|
43930
|
+
if (env.VTE_VERSION) return "vte";
|
|
43931
|
+
if (env.TMUX) return "tmux";
|
|
43932
|
+
if (isCiEnvironment(env)) return "ci";
|
|
43933
|
+
return "unknown";
|
|
43934
|
+
};
|
|
43935
|
+
//#endregion
|
|
43867
43936
|
//#region src/cli/utils/is-git-hook-environment.ts
|
|
43868
43937
|
const isGitHookEnvironment = () => Boolean(process.env.GIT_DIR);
|
|
43869
43938
|
//#endregion
|
|
@@ -43886,6 +43955,7 @@ const NON_INTERACTIVE_ENVIRONMENT_VARIABLES = [
|
|
|
43886
43955
|
const isNonInteractiveEnvironment = () => NON_INTERACTIVE_ENVIRONMENT_VARIABLES.some((envVariable) => Boolean(process.env[envVariable])) || isCodingAgentEnvironment();
|
|
43887
43956
|
//#endregion
|
|
43888
43957
|
//#region src/cli/utils/constants.ts
|
|
43958
|
+
const REACT_DOCTOR_CONFIG_PROJECT_NAME = "react-doctor";
|
|
43889
43959
|
const STAGED_FILES_TEMP_DIR_PREFIX = "react-doctor-staged-";
|
|
43890
43960
|
const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
|
|
43891
43961
|
const GH_DEFAULT_BRANCH_PROBE_TIMEOUT_MS = 5e3;
|
|
@@ -43970,7 +44040,7 @@ const makeNoopConsole = () => ({
|
|
|
43970
44040
|
});
|
|
43971
44041
|
//#endregion
|
|
43972
44042
|
//#region src/cli/utils/version.ts
|
|
43973
|
-
const VERSION = "0.5.6-dev.
|
|
44043
|
+
const VERSION = "0.5.6-dev.b317164";
|
|
43974
44044
|
//#endregion
|
|
43975
44045
|
//#region src/cli/utils/json-mode.ts
|
|
43976
44046
|
let context = null;
|
|
@@ -44120,6 +44190,7 @@ const buildRunContext = () => {
|
|
|
44120
44190
|
viaAction: isOfficialGithubAction(),
|
|
44121
44191
|
codingAgent: detectCodingAgent(),
|
|
44122
44192
|
interactive: !isNonInteractiveEnvironment(),
|
|
44193
|
+
terminalKind: detectTerminalKind(),
|
|
44123
44194
|
jsonMode: isJsonModeActive(),
|
|
44124
44195
|
invokedVia: detectInvokedVia()
|
|
44125
44196
|
};
|
|
@@ -44190,6 +44261,7 @@ const buildSentryScope = (runContext = buildRunContext()) => {
|
|
|
44190
44261
|
viaAction: runContext.viaAction,
|
|
44191
44262
|
codingAgent: runContext.codingAgent,
|
|
44192
44263
|
interactive: runContext.interactive,
|
|
44264
|
+
terminalKind: runContext.terminalKind,
|
|
44193
44265
|
jsonMode: runContext.jsonMode,
|
|
44194
44266
|
invokedVia: runContext.invokedVia,
|
|
44195
44267
|
nodeMajor: runContext.nodeMajor
|
|
@@ -44328,13 +44400,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
44328
44400
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
44329
44401
|
* standard `SENTRY_RELEASE` override.
|
|
44330
44402
|
*/
|
|
44331
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.5.6-dev.
|
|
44403
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.5.6-dev.b317164`;
|
|
44332
44404
|
/**
|
|
44333
44405
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
44334
44406
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
44335
44407
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
44336
44408
|
*/
|
|
44337
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.5.6-dev.
|
|
44409
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.5.6-dev.b317164") ? "development" : "production");
|
|
44338
44410
|
/**
|
|
44339
44411
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
44340
44412
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -48116,7 +48188,7 @@ const AGENT_GUIDANCE_LINES = [
|
|
|
48116
48188
|
"Investigate deeply where relevant: race conditions, security-sensitive flows, state propagation, multi-file refactors, and downstream dependency chains.",
|
|
48117
48189
|
"Ignore pure style preferences, theoretical issues without real impact, missing features, and unrelated pre-existing code.",
|
|
48118
48190
|
"Start with high-confidence fixes that preserve behavior. Leave low-confidence or product-dependent changes as notes.",
|
|
48119
|
-
"Run `npx react-doctor@latest --verbose --
|
|
48191
|
+
"Run `npx react-doctor@latest --verbose --scope changed` before and after changes, plus relevant tests after each focused batch.",
|
|
48120
48192
|
"When available, spawn subagents or isolated worktrees for independent rule families, then review and merge only the best safe fixes.",
|
|
48121
48193
|
"Split unrelated, broad, or behavior-changing work into separate PRs/branches instead of one large cleanup.",
|
|
48122
48194
|
"For confirmed issues that cannot be fixed now, create GitHub issues with the rule, file/line, confidence, impact, and proposed fix.",
|
|
@@ -48191,6 +48263,15 @@ const boxText = (content, innerWidth) => {
|
|
|
48191
48263
|
].join("\n");
|
|
48192
48264
|
};
|
|
48193
48265
|
//#endregion
|
|
48266
|
+
//#region src/cli/utils/resolve-absolute-path.ts
|
|
48267
|
+
/**
|
|
48268
|
+
* Resolves a diagnostic's `filePath` (relative to its project root, or
|
|
48269
|
+
* already absolute) to an absolute path. Shared by the code-frame reader and
|
|
48270
|
+
* the terminal hyperlink builder so both turn a relative path into the same
|
|
48271
|
+
* on-disk location.
|
|
48272
|
+
*/
|
|
48273
|
+
const resolveAbsolutePath = (filePath, rootDirectory) => Path.isAbsolute(filePath) ? filePath : Path.resolve(rootDirectory || ".", filePath);
|
|
48274
|
+
//#endregion
|
|
48194
48275
|
//#region src/cli/utils/build-code-frame.ts
|
|
48195
48276
|
/**
|
|
48196
48277
|
* Renders a syntax-highlighted source excerpt around a diagnostic site
|
|
@@ -48201,7 +48282,7 @@ const boxText = (content, innerWidth) => {
|
|
|
48201
48282
|
*/
|
|
48202
48283
|
const buildCodeFrame = (input) => {
|
|
48203
48284
|
if (input.line <= 0) return null;
|
|
48204
|
-
const absolutePath =
|
|
48285
|
+
const absolutePath = resolveAbsolutePath(input.filePath, input.rootDirectory);
|
|
48205
48286
|
let source;
|
|
48206
48287
|
try {
|
|
48207
48288
|
source = NFS.readFileSync(absolutePath, "utf8");
|
|
@@ -48241,6 +48322,16 @@ const resolveMeasureWidth = (reservedColumns = 0) => resolveClampedWidth({
|
|
|
48241
48322
|
const DIVIDER_INDENT = " ";
|
|
48242
48323
|
const buildSectionDivider = () => highlighter.dim(`${DIVIDER_INDENT}${"─".repeat(resolveMeasureWidth(2))}`);
|
|
48243
48324
|
//#endregion
|
|
48325
|
+
//#region src/cli/utils/format-hyperlink.ts
|
|
48326
|
+
const OSC = "\x1B]";
|
|
48327
|
+
const ST = "\x1B\\";
|
|
48328
|
+
/**
|
|
48329
|
+
* Wraps `text` in an OSC 8 hyperlink pointing at `uri`. The visible characters
|
|
48330
|
+
* are exactly `text`; the link is carried in escape sequences a capable
|
|
48331
|
+
* terminal turns into a click target.
|
|
48332
|
+
*/
|
|
48333
|
+
const formatHyperlink = (text, uri) => `${OSC}8;;${uri}${ST}${text}${OSC}8;;${ST}`;
|
|
48334
|
+
//#endregion
|
|
48244
48335
|
//#region src/cli/utils/indent-multiline-text.ts
|
|
48245
48336
|
const indentMultilineText = (text, linePrefix) => text.split("\n").map((lineText) => `${linePrefix}${lineText}`).join("\n");
|
|
48246
48337
|
//#endregion
|
|
@@ -48394,17 +48485,23 @@ const clusterNearbyDiagnostics = (diagnostics) => {
|
|
|
48394
48485
|
}
|
|
48395
48486
|
return clusters;
|
|
48396
48487
|
};
|
|
48397
|
-
const
|
|
48488
|
+
const formatClusterLocationText = (cluster) => {
|
|
48489
|
+
const { filePath } = cluster.diagnostics[0];
|
|
48490
|
+
if (cluster.startLine <= 0) return filePath;
|
|
48491
|
+
if (cluster.endLine > cluster.startLine) return `${filePath}:${cluster.startLine}-${cluster.endLine}`;
|
|
48492
|
+
return `${filePath}:${cluster.startLine}`;
|
|
48493
|
+
};
|
|
48494
|
+
const formatClusterLocation = (cluster, resolveSourceRoot, hyperlinks) => {
|
|
48398
48495
|
const lead = cluster.diagnostics[0];
|
|
48399
48496
|
const contextTag = formatFileContextTag(lead);
|
|
48400
|
-
|
|
48401
|
-
if (
|
|
48402
|
-
return `${lead.filePath
|
|
48497
|
+
const location = formatClusterLocationText(cluster);
|
|
48498
|
+
if (!hyperlinks) return `${location}${contextTag}`;
|
|
48499
|
+
return `${formatHyperlink(location, pathToFileURL(resolveAbsolutePath(lead.filePath, resolveSourceRoot(lead))).href)}${contextTag}`;
|
|
48403
48500
|
};
|
|
48404
|
-
const buildDiagnosticClusterLines = (cluster, resolveSourceRoot, renderCodeFrame) => {
|
|
48501
|
+
const buildDiagnosticClusterLines = (cluster, resolveSourceRoot, renderCodeFrame, hyperlinks) => {
|
|
48405
48502
|
const lead = cluster.diagnostics[0];
|
|
48406
48503
|
const isMultiSite = cluster.diagnostics.length > 1;
|
|
48407
|
-
const lines = ["", highlighter.gray(`${TOP_ERROR_DETAIL_INDENT}${formatClusterLocation(cluster)}`)];
|
|
48504
|
+
const lines = ["", highlighter.gray(`${TOP_ERROR_DETAIL_INDENT}${formatClusterLocation(cluster, resolveSourceRoot, hyperlinks)}`)];
|
|
48408
48505
|
const codeFrame = renderCodeFrame ? buildCodeFrame({
|
|
48409
48506
|
filePath: lead.filePath,
|
|
48410
48507
|
line: cluster.startLine,
|
|
@@ -48423,7 +48520,7 @@ const buildDiagnosticClusterLines = (cluster, resolveSourceRoot, renderCodeFrame
|
|
|
48423
48520
|
}
|
|
48424
48521
|
return lines;
|
|
48425
48522
|
};
|
|
48426
|
-
const buildRuleDetailBlock = (ruleKey, ruleDiagnostics, resolveSourceRoot, renderEverySite, isAgentEnvironment) => {
|
|
48523
|
+
const buildRuleDetailBlock = (ruleKey, ruleDiagnostics, resolveSourceRoot, renderEverySite, isAgentEnvironment, hyperlinks) => {
|
|
48427
48524
|
const representative = pickRepresentativeDiagnostic(ruleDiagnostics);
|
|
48428
48525
|
const { severity } = representative;
|
|
48429
48526
|
const trailingBadge = formatTrailingSiteBadge(ruleDiagnostics.length);
|
|
@@ -48443,7 +48540,7 @@ const buildRuleDetailBlock = (ruleKey, ruleDiagnostics, resolveSourceRoot, rende
|
|
|
48443
48540
|
}
|
|
48444
48541
|
const renderCodeFrame = severity === "error";
|
|
48445
48542
|
const sites = renderEverySite ? ruleDiagnostics : [representative];
|
|
48446
|
-
if (!(isCollapsedWarningGroup && representative.help.includes(representative.filePath))) for (const cluster of clusterNearbyDiagnostics(sites)) lines.push(...buildDiagnosticClusterLines(cluster, resolveSourceRoot, renderCodeFrame));
|
|
48543
|
+
if (!(isCollapsedWarningGroup && representative.help.includes(representative.filePath))) for (const cluster of clusterNearbyDiagnostics(sites)) lines.push(...buildDiagnosticClusterLines(cluster, resolveSourceRoot, renderCodeFrame, hyperlinks));
|
|
48447
48544
|
return lines;
|
|
48448
48545
|
};
|
|
48449
48546
|
const selectErrorRuleGroups = (diagnostics, rulePriority) => buildSortedRuleGroups(diagnostics.filter((diagnostic) => diagnostic.severity === "error"), rulePriority);
|
|
@@ -48456,7 +48553,7 @@ const buildOverflowSummaryLine = (diagnostics, rulePriority) => {
|
|
|
48456
48553
|
return ` ${highlighter.dim("Run")} ${command} ${highlighter.dim("to list every error and warning")}`;
|
|
48457
48554
|
};
|
|
48458
48555
|
const getTopErrorRuleKeys = (diagnostics, limit, rulePriority) => new Set(selectTopErrorRuleGroups(diagnostics, limit, rulePriority).map(([ruleKey]) => ruleKey));
|
|
48459
|
-
const buildTopErrorsSection = (diagnostics, resolveSourceRoot, rulePriority) => {
|
|
48556
|
+
const buildTopErrorsSection = (diagnostics, resolveSourceRoot, hyperlinks, rulePriority) => {
|
|
48460
48557
|
const topRuleGroups = selectErrorRuleGroups(diagnostics, rulePriority).slice(0, 3);
|
|
48461
48558
|
if (topRuleGroups.length === 0) return {
|
|
48462
48559
|
lines: [],
|
|
@@ -48466,7 +48563,7 @@ const buildTopErrorsSection = (diagnostics, resolveSourceRoot, rulePriority) =>
|
|
|
48466
48563
|
const blockOffsets = [];
|
|
48467
48564
|
for (const [ruleKey, ruleDiagnostics] of topRuleGroups) {
|
|
48468
48565
|
blockOffsets.push(lines.length);
|
|
48469
|
-
lines.push(...buildRuleDetailBlock(ruleKey, ruleDiagnostics, resolveSourceRoot, false, false));
|
|
48566
|
+
lines.push(...buildRuleDetailBlock(ruleKey, ruleDiagnostics, resolveSourceRoot, false, false, hyperlinks));
|
|
48470
48567
|
lines.push("");
|
|
48471
48568
|
}
|
|
48472
48569
|
return {
|
|
@@ -48504,18 +48601,18 @@ const buildOverviewHeaderLines = (diagnostics) => {
|
|
|
48504
48601
|
* single Effect.forEach over Console.log so failures or fiber
|
|
48505
48602
|
* interruption produce predictable partial output.
|
|
48506
48603
|
*/
|
|
48507
|
-
const printDiagnostics = (diagnostics, isVerbose, sourceRoot, rulePriority, isAgentEnvironment = false, onboarding = {}) => gen(function* () {
|
|
48604
|
+
const printDiagnostics = (diagnostics, isVerbose, sourceRoot, rulePriority, isAgentEnvironment = false, onboarding = {}, hyperlinks = false) => gen(function* () {
|
|
48508
48605
|
const sectionPause = onboarding.sectionPause ?? void_;
|
|
48509
48606
|
const animateCountUp = onboarding.animateCountUp ?? false;
|
|
48510
48607
|
const resolveSourceRoot = typeof sourceRoot === "function" ? sourceRoot : () => sourceRoot;
|
|
48511
48608
|
let detailLines;
|
|
48512
48609
|
let topErrorBlockOffsets = [];
|
|
48513
48610
|
if (!isVerbose) {
|
|
48514
|
-
const topErrors = buildTopErrorsSection(diagnostics, resolveSourceRoot, rulePriority);
|
|
48611
|
+
const topErrors = buildTopErrorsSection(diagnostics, resolveSourceRoot, hyperlinks, rulePriority);
|
|
48515
48612
|
detailLines = topErrors.lines;
|
|
48516
48613
|
topErrorBlockOffsets = topErrors.blockOffsets;
|
|
48517
48614
|
} else detailLines = buildSortedRuleGroups(diagnostics, rulePriority).flatMap(([ruleKey, ruleDiagnostics]) => {
|
|
48518
|
-
return [...buildRuleDetailBlock(ruleKey, ruleDiagnostics, resolveSourceRoot, true, isAgentEnvironment), ""];
|
|
48615
|
+
return [...buildRuleDetailBlock(ruleKey, ruleDiagnostics, resolveSourceRoot, true, isAgentEnvironment, hyperlinks), ""];
|
|
48519
48616
|
});
|
|
48520
48617
|
const overflowLine = isVerbose ? void 0 : buildOverflowSummaryLine(diagnostics, rulePriority);
|
|
48521
48618
|
const categoryTallies = buildCategoryDiagnosticGroups(diagnostics, rulePriority).map(buildCategoryTally);
|
|
@@ -48576,6 +48673,48 @@ const computeProjectedScore = async (topErrorSource, rescoreSource, currentScore
|
|
|
48576
48673
|
//#endregion
|
|
48577
48674
|
//#region src/cli/utils/filter-diagnostics-by-categories.ts
|
|
48578
48675
|
const filterDiagnosticsByCategories = (diagnostics, categories) => categories.size === 0 ? [...diagnostics] : diagnostics.filter((diagnostic) => categories.has(diagnostic.category));
|
|
48676
|
+
//#endregion
|
|
48677
|
+
//#region src/cli/utils/supports-hyperlinks.ts
|
|
48678
|
+
const HYPERLINK_CAPABLE_TERM_PROGRAMS = new Set([
|
|
48679
|
+
"iTerm.app",
|
|
48680
|
+
"WezTerm",
|
|
48681
|
+
"vscode",
|
|
48682
|
+
"Hyper",
|
|
48683
|
+
"ghostty",
|
|
48684
|
+
"Tabby",
|
|
48685
|
+
"rio"
|
|
48686
|
+
]);
|
|
48687
|
+
const parseVteVersion = (raw) => {
|
|
48688
|
+
const parsed = Number.parseInt(raw ?? "", 10);
|
|
48689
|
+
return Number.isNaN(parsed) ? 0 : parsed;
|
|
48690
|
+
};
|
|
48691
|
+
/**
|
|
48692
|
+
* Whether `stream` is a terminal that renders OSC 8 hyperlinks. Auto-detected
|
|
48693
|
+
* from terminal-identity env vars; the de-facto `FORCE_HYPERLINK` env var
|
|
48694
|
+
* overrides detection (`FORCE_HYPERLINK=0`/`false` forces off, any other value
|
|
48695
|
+
* forces on), mirroring how the ecosystem's terminal libraries gate the same
|
|
48696
|
+
* feature. Off for non-TTYs, `TERM=dumb`, and CI (whose log viewers render the
|
|
48697
|
+
* raw escape rather than a link). Unknown terminals default to off.
|
|
48698
|
+
*/
|
|
48699
|
+
const supportsHyperlinks = (stream = process.stdout, env = process.env) => {
|
|
48700
|
+
const forced = env.FORCE_HYPERLINK;
|
|
48701
|
+
if (forced !== void 0 && forced !== "") return forced !== "0" && forced.toLowerCase() !== "false";
|
|
48702
|
+
if (stream.isTTY !== true) return false;
|
|
48703
|
+
if (env.TERM === "dumb") return false;
|
|
48704
|
+
if (isCiEnvironment(env)) return false;
|
|
48705
|
+
if (env.WT_SESSION) return true;
|
|
48706
|
+
if (env.KITTY_WINDOW_ID || env.TERM === "xterm-kitty") return true;
|
|
48707
|
+
if (parseVteVersion(env.VTE_VERSION) >= 5e3) return true;
|
|
48708
|
+
return Boolean(env.TERM_PROGRAM && HYPERLINK_CAPABLE_TERM_PROGRAMS.has(env.TERM_PROGRAM));
|
|
48709
|
+
};
|
|
48710
|
+
//#endregion
|
|
48711
|
+
//#region src/cli/utils/should-render-hyperlinks.ts
|
|
48712
|
+
/**
|
|
48713
|
+
* Whether to emit OSC 8 clickable `file:line` locations for this run: a
|
|
48714
|
+
* hyperlink-capable terminal AND not a coding agent (whose output parsers
|
|
48715
|
+
* would choke on the escape sequences).
|
|
48716
|
+
*/
|
|
48717
|
+
const shouldRenderHyperlinks = (stream = process.stdout) => supportsHyperlinks(stream) && !isCodingAgentEnvironment();
|
|
48579
48718
|
const FORCE_ONBOARDING_ENV_VAR = "REACT_DOCTOR_FORCE_ONBOARDING";
|
|
48580
48719
|
const FALSY_FLAG_VALUES = new Set([
|
|
48581
48720
|
"",
|
|
@@ -48595,10 +48734,9 @@ const canAnimateOnboarding = (stream = process.stdout) => {
|
|
|
48595
48734
|
};
|
|
48596
48735
|
//#endregion
|
|
48597
48736
|
//#region src/cli/utils/onboarding-state.ts
|
|
48598
|
-
const GLOBAL_CONFIG_PROJECT_NAME$2 = "react-doctor";
|
|
48599
48737
|
const ONBOARDED_AT_KEY = "onboardedAt";
|
|
48600
48738
|
const getOnboardingStore = (options = {}) => new Conf({
|
|
48601
|
-
projectName:
|
|
48739
|
+
projectName: REACT_DOCTOR_CONFIG_PROJECT_NAME,
|
|
48602
48740
|
cwd: options.cwd
|
|
48603
48741
|
});
|
|
48604
48742
|
const hasCompletedOnboarding = (options = {}) => {
|
|
@@ -49674,6 +49812,7 @@ const finalizeAndRender = (input) => gen(function* () {
|
|
|
49674
49812
|
}
|
|
49675
49813
|
const animateRender = !options.silent && !options.verbose && canAnimateOnboarding(process.stdout);
|
|
49676
49814
|
const pause = onboardingSectionPause(animateRender);
|
|
49815
|
+
const useHyperlinks = shouldRenderHyperlinks(process.stdout);
|
|
49677
49816
|
const demotedDiagnosticCount = diagnostics.length - surfaceDiagnostics.length;
|
|
49678
49817
|
const lintSourceFileCount = options.includePaths.length > 0 ? options.includePaths.length : project.sourceFileCount;
|
|
49679
49818
|
if (printedDiagnostics.length === 0) {
|
|
@@ -49699,7 +49838,7 @@ const finalizeAndRender = (input) => gen(function* () {
|
|
|
49699
49838
|
yield* printDiagnostics([...printedDiagnostics], options.verbose, directory, buildRulePriorityMap([score]), isCodingAgentEnvironment(), {
|
|
49700
49839
|
sectionPause: pause,
|
|
49701
49840
|
animateCountUp: animateRender
|
|
49702
|
-
});
|
|
49841
|
+
}, useHyperlinks);
|
|
49703
49842
|
if (options.isNonInteractiveEnvironment && options.outputSurface !== "prComment") yield* printAgentGuidance();
|
|
49704
49843
|
if (options.categoryFilters.size === 0 && demotedDiagnosticCount > 0) {
|
|
49705
49844
|
yield* log(highlighter.gray(` ${demotedDiagnosticCount} demoted from the ${options.outputSurface} surface (e.g. design cleanup) — run \`npx react-doctor@latest .\` locally for the full list.`));
|
|
@@ -49892,16 +50031,7 @@ const buildHandoffPayload = (input) => {
|
|
|
49892
50031
|
try {
|
|
49893
50032
|
outputDirectory = writeDiagnosticsDirectory([...input.diagnostics], input.outputDirectory);
|
|
49894
50033
|
} catch {}
|
|
49895
|
-
const lines = [
|
|
49896
|
-
"Before fixing anything, first ask me whether I'd like to add React Doctor to CI so every pull request is scanned automatically. Explain that:",
|
|
49897
|
-
`- It's used by teams at ${CI_TRUST_COMPANIES}.`,
|
|
49898
|
-
"- I don't have to fix everything at once; CI keeps new PRs clean while I incrementally pay down the existing issues.",
|
|
49899
|
-
`- Setup guide: ${CI_URL}`,
|
|
49900
|
-
"If I say yes, run `npx react-doctor install` (or add the GitHub Actions workflow). Either way, then continue with the fixes below.",
|
|
49901
|
-
"",
|
|
49902
|
-
`Fix the top ${topGroups.length} React Doctor ${topGroups.length === 1 ? "issue" : "issues"} in ${input.projectName} on this pass — leave the rest for a follow-up.`,
|
|
49903
|
-
""
|
|
49904
|
-
];
|
|
50034
|
+
const lines = [`Fix the top ${topGroups.length} React Doctor ${topGroups.length === 1 ? "issue" : "issues"} in ${input.projectName} on this pass — leave the rest for a follow-up.`, ""];
|
|
49905
50035
|
topGroups.forEach(([ruleKey, ruleDiagnostics], index) => {
|
|
49906
50036
|
const representative = ruleDiagnostics[0];
|
|
49907
50037
|
const severityLabel = representative.severity === "error" ? "ERROR" : "WARN";
|
|
@@ -50271,38 +50401,52 @@ const upgradeReactDoctorWorkflowInPlace = (projectRoot) => {
|
|
|
50271
50401
|
//#region src/cli/utils/hash-project-root.ts
|
|
50272
50402
|
const hashProjectRoot = (projectRoot) => createHash("sha256").update(Path.resolve(projectRoot)).digest("hex");
|
|
50273
50403
|
//#endregion
|
|
50274
|
-
//#region src/cli/utils/
|
|
50275
|
-
const
|
|
50276
|
-
const
|
|
50277
|
-
|
|
50278
|
-
|
|
50279
|
-
});
|
|
50280
|
-
|
|
50281
|
-
|
|
50282
|
-
|
|
50283
|
-
|
|
50284
|
-
|
|
50285
|
-
|
|
50286
|
-
|
|
50287
|
-
};
|
|
50288
|
-
const recordActionUpgradeDecision = (projectRoot, outcome, storeOptions = {}) => {
|
|
50289
|
-
try {
|
|
50290
|
-
const store = getActionUpgradeStore(storeOptions);
|
|
50291
|
-
const upgrades = store.get("actionUpgrades", {});
|
|
50292
|
-
store.set("actionUpgrades", {
|
|
50293
|
-
...upgrades,
|
|
50294
|
-
[hashProjectRoot(projectRoot)]: {
|
|
50295
|
-
rootDirectory: Path.resolve(projectRoot),
|
|
50296
|
-
outcome,
|
|
50297
|
-
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
50404
|
+
//#region src/cli/utils/project-decision-store.ts
|
|
50405
|
+
const createProjectDecisionStore = (storeKey) => {
|
|
50406
|
+
const getStore = (options = {}) => new Conf({
|
|
50407
|
+
projectName: REACT_DOCTOR_CONFIG_PROJECT_NAME,
|
|
50408
|
+
cwd: options.cwd
|
|
50409
|
+
});
|
|
50410
|
+
return {
|
|
50411
|
+
getConfigPath: (options = {}) => getStore(options).path,
|
|
50412
|
+
hasHandled: (projectRoot, options = {}) => {
|
|
50413
|
+
try {
|
|
50414
|
+
return Boolean(getStore(options).get(storeKey, {})[hashProjectRoot(projectRoot)]);
|
|
50415
|
+
} catch {
|
|
50416
|
+
return true;
|
|
50298
50417
|
}
|
|
50299
|
-
}
|
|
50300
|
-
|
|
50301
|
-
|
|
50302
|
-
|
|
50303
|
-
|
|
50418
|
+
},
|
|
50419
|
+
record: (projectRoot, outcome, options = {}) => {
|
|
50420
|
+
try {
|
|
50421
|
+
const store = getStore(options);
|
|
50422
|
+
store.set(storeKey, {
|
|
50423
|
+
...store.get(storeKey, {}),
|
|
50424
|
+
[hashProjectRoot(projectRoot)]: {
|
|
50425
|
+
rootDirectory: Path.resolve(projectRoot),
|
|
50426
|
+
outcome,
|
|
50427
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
50428
|
+
}
|
|
50429
|
+
});
|
|
50430
|
+
return true;
|
|
50431
|
+
} catch {
|
|
50432
|
+
return false;
|
|
50433
|
+
}
|
|
50434
|
+
}
|
|
50435
|
+
};
|
|
50304
50436
|
};
|
|
50305
50437
|
//#endregion
|
|
50438
|
+
//#region src/cli/utils/action-upgrade-prompt.ts
|
|
50439
|
+
const store$1 = createProjectDecisionStore("actionUpgrades");
|
|
50440
|
+
store$1.getConfigPath;
|
|
50441
|
+
const hasHandledActionUpgrade = store$1.hasHandled;
|
|
50442
|
+
const recordActionUpgradeDecision = store$1.record;
|
|
50443
|
+
//#endregion
|
|
50444
|
+
//#region src/cli/utils/ci-prompt-decision.ts
|
|
50445
|
+
const store = createProjectDecisionStore("ciPrompts");
|
|
50446
|
+
store.getConfigPath;
|
|
50447
|
+
const hasHandledCiPrompt = store.hasHandled;
|
|
50448
|
+
const recordCiPromptDecision = store.record;
|
|
50449
|
+
//#endregion
|
|
50306
50450
|
//#region src/cli/utils/open-url.ts
|
|
50307
50451
|
const resolveOpenCommand = (url) => {
|
|
50308
50452
|
if (process$1.platform === "darwin") return {
|
|
@@ -50758,22 +50902,22 @@ const buildAgentHookScript = () => [
|
|
|
50758
50902
|
"",
|
|
50759
50903
|
"run_react_doctor() {",
|
|
50760
50904
|
" if [ -x ./node_modules/.bin/react-doctor ]; then",
|
|
50761
|
-
" ./node_modules/.bin/react-doctor --verbose --
|
|
50905
|
+
" ./node_modules/.bin/react-doctor --verbose --scope changed --blocking warning --no-score",
|
|
50762
50906
|
" return",
|
|
50763
50907
|
" fi",
|
|
50764
50908
|
"",
|
|
50765
50909
|
" if command -v react-doctor >/dev/null 2>&1; then",
|
|
50766
|
-
" react-doctor --verbose --
|
|
50910
|
+
" react-doctor --verbose --scope changed --blocking warning --no-score",
|
|
50767
50911
|
" return",
|
|
50768
50912
|
" fi",
|
|
50769
50913
|
"",
|
|
50770
50914
|
" if command -v pnpm >/dev/null 2>&1; then",
|
|
50771
|
-
" pnpm dlx react-doctor@latest --verbose --
|
|
50915
|
+
" pnpm dlx react-doctor@latest --verbose --scope changed --blocking warning --no-score",
|
|
50772
50916
|
" return",
|
|
50773
50917
|
" fi",
|
|
50774
50918
|
"",
|
|
50775
50919
|
" if command -v npx >/dev/null 2>&1; then",
|
|
50776
|
-
" npx --yes react-doctor@latest --verbose --
|
|
50920
|
+
" npx --yes react-doctor@latest --verbose --scope changed --blocking warning --no-score",
|
|
50777
50921
|
" return",
|
|
50778
50922
|
" fi",
|
|
50779
50923
|
"",
|
|
@@ -51519,10 +51663,12 @@ const runInstallReactDoctor = async (options = {}) => {
|
|
|
51519
51663
|
const existingWorkflow = readReactDoctorWorkflow(projectRoot);
|
|
51520
51664
|
const canInstallWorkflow = !NFS.existsSync(workflowTargetPath);
|
|
51521
51665
|
const canUpgradeWorkflow = existingWorkflow !== null && workflowUsesV1Action(existingWorkflow.content) && !hasHandledActionUpgrade(projectRoot);
|
|
51522
|
-
const
|
|
51666
|
+
const ciPromptOutcome = canInstallWorkflow && !options.yes && !skipPrompts && !hasHandledCiPrompt(projectRoot) ? await askAddToGitHubActions(prompt) : null;
|
|
51667
|
+
const shouldInstallWorkflow = canInstallWorkflow && (Boolean(options.yes) || ciPromptOutcome === "yes");
|
|
51523
51668
|
const upgradePromptOutcome = canUpgradeWorkflow && !options.yes && !skipPrompts ? await askUpgradeActionVersion(prompt) : null;
|
|
51524
51669
|
const shouldUpgradeWorkflow = canUpgradeWorkflow && (Boolean(options.yes) || upgradePromptOutcome === "yes");
|
|
51525
51670
|
if (upgradePromptOutcome === "no" && !options.dryRun) recordActionUpgradeDecision(projectRoot, "declined");
|
|
51671
|
+
if ((ciPromptOutcome === "yes" || ciPromptOutcome === "no") && !options.dryRun) recordCiPromptDecision(projectRoot, ciPromptOutcome === "yes" ? "accepted" : "declined");
|
|
51526
51672
|
const selectedAgents = skipPrompts ? detectedAgents : (await prompt({
|
|
51527
51673
|
type: "multiselect",
|
|
51528
51674
|
name: "agents",
|
|
@@ -51769,18 +51915,24 @@ const handoffToAgent = async (input) => {
|
|
|
51769
51915
|
if (!input.interactive || input.diagnostics.length === 0) return;
|
|
51770
51916
|
cliLogger.break();
|
|
51771
51917
|
const projectRootForCi = findNearestPackageDirectory(input.rootDirectory) ?? input.rootDirectory;
|
|
51772
|
-
|
|
51918
|
+
const isGitHubActionsConfigured = isReactDoctorWorkflowInstalled(projectRootForCi);
|
|
51919
|
+
if (!isGitHubActionsConfigured && !hasHandledCiPrompt(projectRootForCi)) {
|
|
51773
51920
|
const ciOutcome = await askAddToGitHubActions();
|
|
51774
51921
|
recordCount(METRIC.agentHandoff, 1, {
|
|
51775
51922
|
outcome: `ci-${ciOutcome}`,
|
|
51776
51923
|
diagnosticsCount: input.diagnostics.length
|
|
51777
51924
|
});
|
|
51778
51925
|
if (ciOutcome === "cancel") return;
|
|
51926
|
+
recordCiPromptDecision(projectRootForCi, ciOutcome === "yes" ? "accepted" : "declined");
|
|
51779
51927
|
if (ciOutcome === "yes") {
|
|
51780
51928
|
await setUpGitHubActions({ rootDirectory: input.rootDirectory });
|
|
51781
51929
|
cliLogger.break();
|
|
51782
51930
|
}
|
|
51783
|
-
} else await maybeOfferActionUpgrade(projectRootForCi);
|
|
51931
|
+
} else if (isGitHubActionsConfigured) await maybeOfferActionUpgrade(projectRootForCi);
|
|
51932
|
+
else recordCount(METRIC.agentHandoff, 1, {
|
|
51933
|
+
outcome: "ci-suppressed",
|
|
51934
|
+
diagnosticsCount: input.diagnostics.length
|
|
51935
|
+
});
|
|
51784
51936
|
const { handoffTarget } = await prompts({
|
|
51785
51937
|
type: "select",
|
|
51786
51938
|
name: "handoffTarget",
|
|
@@ -52086,7 +52238,7 @@ const printMultiProjectSummary = (input) => gen(function* () {
|
|
|
52086
52238
|
yield* log(`${highlighter.success("✔")} Scanned ${totalScannedFileCount} ${totalScannedFileCount === 1 ? "file" : "files"} in ${formatElapsedTime(totalElapsedMilliseconds)}`);
|
|
52087
52239
|
if (displayDiagnostics.length > 0) {
|
|
52088
52240
|
yield* log("");
|
|
52089
|
-
yield* printDiagnostics(displayDiagnostics, verbose, resolveDiagnosticSourceRoot, buildRulePriorityMap(completedScans.map((scan) => scan.result.score)), isCodingAgentEnvironment(), { animateCountUp: animateRender });
|
|
52241
|
+
yield* printDiagnostics(displayDiagnostics, verbose, resolveDiagnosticSourceRoot, buildRulePriorityMap(completedScans.map((scan) => scan.result.score)), isCodingAgentEnvironment(), { animateCountUp: animateRender }, shouldRenderHyperlinks(process.stdout));
|
|
52090
52242
|
}
|
|
52091
52243
|
const lowestScoredScan = findLowestScoredScan(completedScans);
|
|
52092
52244
|
const aggregateScore = lowestScoredScan?.result.score ?? null;
|
|
@@ -52124,9 +52276,8 @@ const printMultiProjectSummary = (input) => gen(function* () {
|
|
|
52124
52276
|
});
|
|
52125
52277
|
//#endregion
|
|
52126
52278
|
//#region src/cli/utils/prompt-install-setup.ts
|
|
52127
|
-
const GLOBAL_CONFIG_PROJECT_NAME = "react-doctor";
|
|
52128
52279
|
const getSetupPromptStore = (options = {}) => new Conf({
|
|
52129
|
-
projectName:
|
|
52280
|
+
projectName: REACT_DOCTOR_CONFIG_PROJECT_NAME,
|
|
52130
52281
|
cwd: options.cwd
|
|
52131
52282
|
});
|
|
52132
52283
|
const getSetupPromptProjectKey = (projectRoot) => hashProjectRoot(projectRoot);
|
|
@@ -52137,6 +52288,24 @@ const hasDisabledSetupPrompt = (projectRoot, storeOptions = {}) => {
|
|
|
52137
52288
|
return false;
|
|
52138
52289
|
}
|
|
52139
52290
|
};
|
|
52291
|
+
const disableSetupPrompt = (projectRoot, storeOptions = {}) => {
|
|
52292
|
+
try {
|
|
52293
|
+
const store = getSetupPromptStore(storeOptions);
|
|
52294
|
+
const projects = store.get("projects", {});
|
|
52295
|
+
const projectKey = getSetupPromptProjectKey(projectRoot);
|
|
52296
|
+
store.set("projects", {
|
|
52297
|
+
...projects,
|
|
52298
|
+
[projectKey]: {
|
|
52299
|
+
...projects[projectKey] ?? {},
|
|
52300
|
+
rootDirectory: Path.resolve(projectRoot),
|
|
52301
|
+
setupPrompt: false
|
|
52302
|
+
}
|
|
52303
|
+
});
|
|
52304
|
+
return true;
|
|
52305
|
+
} catch {
|
|
52306
|
+
return false;
|
|
52307
|
+
}
|
|
52308
|
+
};
|
|
52140
52309
|
const resolveInstallSetupProjectRoot = (options) => {
|
|
52141
52310
|
if (options.scanDirectories.length === 0) return findNearestPackageDirectory(options.scanRoot) ?? options.scanRoot;
|
|
52142
52311
|
const packageDirectories = /* @__PURE__ */ new Set();
|
|
@@ -52543,6 +52712,14 @@ const runExplain = async (fileLineArgument, context) => {
|
|
|
52543
52712
|
const colorizedRule = colorizeRuleByDiagnostic(ruleIdentifier, diagnostic.severity);
|
|
52544
52713
|
const severityLabel = colorizeRuleByDiagnostic(diagnostic.severity, diagnostic.severity);
|
|
52545
52714
|
cliLogger.log(`${severitySymbol} ${colorizedRule} ${highlighter.dim(`(${severityLabel})`)} — ${diagnostic.message}`);
|
|
52715
|
+
const codeFrame = buildCodeFrame({
|
|
52716
|
+
filePath: diagnostic.filePath,
|
|
52717
|
+
line: diagnostic.line,
|
|
52718
|
+
column: diagnostic.column,
|
|
52719
|
+
endLine: diagnostic.endLine,
|
|
52720
|
+
rootDirectory: targetDirectory
|
|
52721
|
+
});
|
|
52722
|
+
if (codeFrame) cliLogger.log(indentMultilineText(codeFrame, " "));
|
|
52546
52723
|
if (diagnostic.category) cliLogger.dim(` Category: ${diagnostic.category}`);
|
|
52547
52724
|
if (diagnostic.help) cliLogger.dim(` ${diagnostic.help}`);
|
|
52548
52725
|
cliLogger.dim(` If this needs follow-up or looks like a false positive, open: ${buildDiagnosticIssueUrl({
|
|
@@ -52939,6 +53116,7 @@ const inspectAction = async (directory, flags) => {
|
|
|
52939
53116
|
})) {
|
|
52940
53117
|
printAgentInstallHint();
|
|
52941
53118
|
recordCount(METRIC.agentInstallHintShown, 1);
|
|
53119
|
+
disableSetupPrompt(setupProjectRoot);
|
|
52942
53120
|
}
|
|
52943
53121
|
}
|
|
52944
53122
|
} catch (error) {
|
|
@@ -53870,7 +54048,7 @@ ${highlighter.dim("Examples:")}
|
|
|
53870
54048
|
${formatExampleLines([
|
|
53871
54049
|
["react-doctor", "scan the current project"],
|
|
53872
54050
|
["react-doctor ./apps/web", "scan a specific directory"],
|
|
53873
|
-
["react-doctor --
|
|
54051
|
+
["react-doctor --scope changed --base main", "scan only new issues vs. main"],
|
|
53874
54052
|
["react-doctor --project modules/a,modules/b", "score each module separately (names or paths)"],
|
|
53875
54053
|
["react-doctor --staged", "scan staged files (pre-commit hook)"],
|
|
53876
54054
|
["react-doctor --category Security", "show only one diagnostic category"],
|
|
@@ -53946,4 +54124,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
53946
54124
|
export {};
|
|
53947
54125
|
|
|
53948
54126
|
//# sourceMappingURL=cli.js.map
|
|
53949
|
-
//# debugId=
|
|
54127
|
+
//# debugId=ad091b20-c3e2-5c96-93ac-9a910745a035
|
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]="
|
|
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]="4e83db09-9255-525f-b6fd-405784826e7d")}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";
|
|
@@ -38084,8 +38084,8 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
38084
38084
|
}
|
|
38085
38085
|
return enabled;
|
|
38086
38086
|
};
|
|
38087
|
-
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [] }) => {
|
|
38088
|
-
const reactHooksJsPlugin = resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
38087
|
+
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false }) => {
|
|
38088
|
+
const reactHooksJsPlugin = disableReactHooksJsPlugin ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
38089
38089
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
38090
38090
|
const jsPlugins = [];
|
|
38091
38091
|
if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
|
|
@@ -38782,9 +38782,9 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
38782
38782
|
try {
|
|
38783
38783
|
parsed = JSON.parse(sanitizedStdout);
|
|
38784
38784
|
} catch {
|
|
38785
|
-
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0,
|
|
38785
|
+
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
38786
38786
|
}
|
|
38787
|
-
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0,
|
|
38787
|
+
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
38788
38788
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
38789
38789
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
38790
38790
|
const absolutePath = Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
@@ -39075,6 +39075,28 @@ const writeOxlintConfig = (configPath, configToWrite) => {
|
|
|
39075
39075
|
NFS.closeSync(fileHandle);
|
|
39076
39076
|
}
|
|
39077
39077
|
};
|
|
39078
|
+
const REACT_HOOKS_JS_DROP_PREFIX = "React Compiler rules (react-hooks-js/*) skipped — eslint-plugin-react-hooks failed to load in this environment";
|
|
39079
|
+
/**
|
|
39080
|
+
* Detects an oxlint config-load crash caused by the optional
|
|
39081
|
+
* `react-hooks-js` (eslint-plugin-react-hooks) React Compiler plugin and
|
|
39082
|
+
* builds the partial-failure note for it; returns `null` when the failure
|
|
39083
|
+
* was anything else.
|
|
39084
|
+
*
|
|
39085
|
+
* oxlint prints a framed error to stdout (not stderr) and exits non-zero
|
|
39086
|
+
* when a `jsPlugins` entry can't be imported; that non-JSON stdout
|
|
39087
|
+
* surfaces as `OxlintOutputUnparseable`. Because oxlint fails the WHOLE
|
|
39088
|
+
* config load on it, leaving the plugin in would drop every curated
|
|
39089
|
+
* react-doctor diagnostic too — so the caller retries with the plugin
|
|
39090
|
+
* stripped (issue #833). Both markers sit at the start of oxlint's
|
|
39091
|
+
* message, so they survive the `preview` slice even for deep pnpm paths.
|
|
39092
|
+
*/
|
|
39093
|
+
const reactHooksJsPluginDropNote = (error) => {
|
|
39094
|
+
if (!(error instanceof ReactDoctorError) || error.reason._tag !== "OxlintOutputUnparseable") return null;
|
|
39095
|
+
const { preview } = error.reason;
|
|
39096
|
+
if (!preview.includes("Failed to load JS plugin") || !preview.includes("eslint-plugin-react-hooks")) return null;
|
|
39097
|
+
const underlyingReason = preview.match(/Error:[^\n]*/)?.[0]?.trim();
|
|
39098
|
+
return `${REACT_HOOKS_JS_DROP_PREFIX}${underlyingReason ? `: ${underlyingReason}` : ""}. Other rules ran normally.`;
|
|
39099
|
+
};
|
|
39078
39100
|
/**
|
|
39079
39101
|
* The oxlint runner. Composed of three pieces in `runners/oxlint/`:
|
|
39080
39102
|
*
|
|
@@ -39102,15 +39124,16 @@ const runOxlint = async (options) => {
|
|
|
39102
39124
|
const pluginPath = resolvePluginPath();
|
|
39103
39125
|
const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
|
|
39104
39126
|
const userPlugins = resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
|
|
39105
|
-
const buildConfig = (
|
|
39127
|
+
const buildConfig = (overrides) => createOxlintConfig({
|
|
39106
39128
|
pluginPath,
|
|
39107
39129
|
project,
|
|
39108
39130
|
customRulesOnly,
|
|
39109
|
-
extendsPaths:
|
|
39131
|
+
extendsPaths: overrides.extendsPaths,
|
|
39110
39132
|
ignoredTags,
|
|
39111
39133
|
serverAuthFunctionNames,
|
|
39112
39134
|
severityControls,
|
|
39113
|
-
userPlugins
|
|
39135
|
+
userPlugins,
|
|
39136
|
+
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin
|
|
39114
39137
|
});
|
|
39115
39138
|
const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
|
|
39116
39139
|
const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
|
|
@@ -39146,12 +39169,22 @@ const runOxlint = async (options) => {
|
|
|
39146
39169
|
outputMaxBytes,
|
|
39147
39170
|
concurrency: options.concurrency
|
|
39148
39171
|
});
|
|
39149
|
-
writeOxlintConfig(configPath, buildConfig(extendsPaths));
|
|
39172
|
+
writeOxlintConfig(configPath, buildConfig({ extendsPaths }));
|
|
39150
39173
|
try {
|
|
39151
39174
|
return await runBatches();
|
|
39152
39175
|
} catch (error) {
|
|
39176
|
+
const reactHooksJsDropNote = reactHooksJsPluginDropNote(error);
|
|
39177
|
+
if (reactHooksJsDropNote !== null) {
|
|
39178
|
+
writeOxlintConfig(configPath, buildConfig({
|
|
39179
|
+
extendsPaths,
|
|
39180
|
+
disableReactHooksJsPlugin: true
|
|
39181
|
+
}));
|
|
39182
|
+
const diagnostics = await runBatches();
|
|
39183
|
+
onPartialFailure?.(reactHooksJsDropNote);
|
|
39184
|
+
return diagnostics;
|
|
39185
|
+
}
|
|
39153
39186
|
if (extendsPaths.length === 0) throw error;
|
|
39154
|
-
writeOxlintConfig(configPath, buildConfig([]));
|
|
39187
|
+
writeOxlintConfig(configPath, buildConfig({ extendsPaths: [] }));
|
|
39155
39188
|
return await runBatches();
|
|
39156
39189
|
}
|
|
39157
39190
|
} finally {
|
|
@@ -40571,4 +40604,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
40571
40604
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
40572
40605
|
|
|
40573
40606
|
//# sourceMappingURL=index.js.map
|
|
40574
|
-
//# debugId=
|
|
40607
|
+
//# debugId=4e83db09-9255-525f-b6fd-405784826e7d
|
package/dist/lsp.js
CHANGED
|
@@ -38070,8 +38070,8 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
38070
38070
|
}
|
|
38071
38071
|
return enabled;
|
|
38072
38072
|
};
|
|
38073
|
-
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [] }) => {
|
|
38074
|
-
const reactHooksJsPlugin = resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
38073
|
+
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false }) => {
|
|
38074
|
+
const reactHooksJsPlugin = disableReactHooksJsPlugin ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
38075
38075
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
38076
38076
|
const jsPlugins = [];
|
|
38077
38077
|
if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
|
|
@@ -38768,9 +38768,9 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
38768
38768
|
try {
|
|
38769
38769
|
parsed = JSON.parse(sanitizedStdout);
|
|
38770
38770
|
} catch {
|
|
38771
|
-
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0,
|
|
38771
|
+
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
38772
38772
|
}
|
|
38773
|
-
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0,
|
|
38773
|
+
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
38774
38774
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
38775
38775
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
38776
38776
|
const absolutePath = Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
@@ -39061,6 +39061,28 @@ const writeOxlintConfig = (configPath, configToWrite) => {
|
|
|
39061
39061
|
NFS.closeSync(fileHandle);
|
|
39062
39062
|
}
|
|
39063
39063
|
};
|
|
39064
|
+
const REACT_HOOKS_JS_DROP_PREFIX = "React Compiler rules (react-hooks-js/*) skipped — eslint-plugin-react-hooks failed to load in this environment";
|
|
39065
|
+
/**
|
|
39066
|
+
* Detects an oxlint config-load crash caused by the optional
|
|
39067
|
+
* `react-hooks-js` (eslint-plugin-react-hooks) React Compiler plugin and
|
|
39068
|
+
* builds the partial-failure note for it; returns `null` when the failure
|
|
39069
|
+
* was anything else.
|
|
39070
|
+
*
|
|
39071
|
+
* oxlint prints a framed error to stdout (not stderr) and exits non-zero
|
|
39072
|
+
* when a `jsPlugins` entry can't be imported; that non-JSON stdout
|
|
39073
|
+
* surfaces as `OxlintOutputUnparseable`. Because oxlint fails the WHOLE
|
|
39074
|
+
* config load on it, leaving the plugin in would drop every curated
|
|
39075
|
+
* react-doctor diagnostic too — so the caller retries with the plugin
|
|
39076
|
+
* stripped (issue #833). Both markers sit at the start of oxlint's
|
|
39077
|
+
* message, so they survive the `preview` slice even for deep pnpm paths.
|
|
39078
|
+
*/
|
|
39079
|
+
const reactHooksJsPluginDropNote = (error) => {
|
|
39080
|
+
if (!(error instanceof ReactDoctorError) || error.reason._tag !== "OxlintOutputUnparseable") return null;
|
|
39081
|
+
const { preview } = error.reason;
|
|
39082
|
+
if (!preview.includes("Failed to load JS plugin") || !preview.includes("eslint-plugin-react-hooks")) return null;
|
|
39083
|
+
const underlyingReason = preview.match(/Error:[^\n]*/)?.[0]?.trim();
|
|
39084
|
+
return `${REACT_HOOKS_JS_DROP_PREFIX}${underlyingReason ? `: ${underlyingReason}` : ""}. Other rules ran normally.`;
|
|
39085
|
+
};
|
|
39064
39086
|
/**
|
|
39065
39087
|
* The oxlint runner. Composed of three pieces in `runners/oxlint/`:
|
|
39066
39088
|
*
|
|
@@ -39088,15 +39110,16 @@ const runOxlint = async (options) => {
|
|
|
39088
39110
|
const pluginPath = resolvePluginPath();
|
|
39089
39111
|
const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
|
|
39090
39112
|
const userPlugins = resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
|
|
39091
|
-
const buildConfig = (
|
|
39113
|
+
const buildConfig = (overrides) => createOxlintConfig({
|
|
39092
39114
|
pluginPath,
|
|
39093
39115
|
project,
|
|
39094
39116
|
customRulesOnly,
|
|
39095
|
-
extendsPaths:
|
|
39117
|
+
extendsPaths: overrides.extendsPaths,
|
|
39096
39118
|
ignoredTags,
|
|
39097
39119
|
serverAuthFunctionNames,
|
|
39098
39120
|
severityControls,
|
|
39099
|
-
userPlugins
|
|
39121
|
+
userPlugins,
|
|
39122
|
+
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin
|
|
39100
39123
|
});
|
|
39101
39124
|
const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
|
|
39102
39125
|
const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
|
|
@@ -39132,12 +39155,22 @@ const runOxlint = async (options) => {
|
|
|
39132
39155
|
outputMaxBytes,
|
|
39133
39156
|
concurrency: options.concurrency
|
|
39134
39157
|
});
|
|
39135
|
-
writeOxlintConfig(configPath, buildConfig(extendsPaths));
|
|
39158
|
+
writeOxlintConfig(configPath, buildConfig({ extendsPaths }));
|
|
39136
39159
|
try {
|
|
39137
39160
|
return await runBatches();
|
|
39138
39161
|
} catch (error) {
|
|
39162
|
+
const reactHooksJsDropNote = reactHooksJsPluginDropNote(error);
|
|
39163
|
+
if (reactHooksJsDropNote !== null) {
|
|
39164
|
+
writeOxlintConfig(configPath, buildConfig({
|
|
39165
|
+
extendsPaths,
|
|
39166
|
+
disableReactHooksJsPlugin: true
|
|
39167
|
+
}));
|
|
39168
|
+
const diagnostics = await runBatches();
|
|
39169
|
+
onPartialFailure?.(reactHooksJsDropNote);
|
|
39170
|
+
return diagnostics;
|
|
39171
|
+
}
|
|
39139
39172
|
if (extendsPaths.length === 0) throw error;
|
|
39140
|
-
writeOxlintConfig(configPath, buildConfig([]));
|
|
39173
|
+
writeOxlintConfig(configPath, buildConfig({ extendsPaths: [] }));
|
|
39141
39174
|
return await runBatches();
|
|
39142
39175
|
}
|
|
39143
39176
|
} finally {
|
|
@@ -42356,5 +42389,5 @@ const startLanguageServer = () => {
|
|
|
42356
42389
|
};
|
|
42357
42390
|
//#endregion
|
|
42358
42391
|
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]="
|
|
42360
|
-
//# debugId=
|
|
42392
|
+
!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]="7deadb2d-94c2-54e2-bd0d-808ee8d4c380")}catch(e){}}();
|
|
42393
|
+
//# debugId=7deadb2d-94c2-54e2-bd0d-808ee8d4c380
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-doctor",
|
|
3
|
-
"version": "0.5.6-dev.
|
|
3
|
+
"version": "0.5.6-dev.b317164",
|
|
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.
|
|
67
|
+
"oxlint-plugin-react-doctor": "0.5.6-dev.b317164"
|
|
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/language-server": "0.5.6",
|
|
76
75
|
"@react-doctor/api": "0.5.6",
|
|
76
|
+
"@react-doctor/language-server": "0.5.6",
|
|
77
77
|
"@react-doctor/core": "0.5.6"
|
|
78
78
|
},
|
|
79
79
|
"engines": {
|