react-doctor 0.5.8-dev.157177a → 0.5.8-dev.229ea2e
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 +169 -61
- package/dist/index.js +50 -36
- package/dist/lsp.js +51 -36
- package/package.json +2 -2
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]="6c903b87-8faf-5702-b078-03465e326385")}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";
|
|
@@ -26,7 +26,7 @@ import tty from "node:tty";
|
|
|
26
26
|
import { codeFrameColumns } from "@babel/code-frame";
|
|
27
27
|
import Conf from "conf";
|
|
28
28
|
import basePrompts from "prompts";
|
|
29
|
-
import { SKILL_MANIFEST_FILE, detectInstalledSkillAgents, getSkillAgentConfig, getSkillAgentTypes, installSkillsFromSource } from "agent-install";
|
|
29
|
+
import { SKILL_MANIFEST_FILE, detectInstalledSkillAgents, getSkillAgentConfig, getSkillAgentTypes, installSkillsFromSource, isSkillAgentType } from "agent-install";
|
|
30
30
|
import { generateCode, loadFile, writeFile } from "magicast";
|
|
31
31
|
import { getConfigFromVariableDeclaration, getDefaultExportOptions } from "magicast/helpers";
|
|
32
32
|
//#region \0rolldown/runtime.js
|
|
@@ -36862,6 +36862,7 @@ const DOCS_URL = "https://react.doctor/docs";
|
|
|
36862
36862
|
const DOCS_RULES_BASE_URL = `${DOCS_URL}/rules`;
|
|
36863
36863
|
const FETCH_TIMEOUT_MS = 1e4;
|
|
36864
36864
|
const GITHUB_VIEWER_PERMISSION_TIMEOUT_MS = 2e3;
|
|
36865
|
+
const SPAWN_ARGS_MAX_LENGTH_CHARS = 24e3;
|
|
36865
36866
|
const PER_WORKER_MEM_BUDGET_BYTES = 1024 * 1024 * 1024;
|
|
36866
36867
|
const DEFAULT_BRANCH_CANDIDATES = ["main", "master"];
|
|
36867
36868
|
const ADOPTABLE_LINT_CONFIG_FILENAMES = [".oxlintrc.json", ".eslintrc.json"];
|
|
@@ -37679,7 +37680,10 @@ for (const [legacyRuleKey, nativeRuleKey] of Object.entries(LEGACY_RULE_KEY_TO_N
|
|
|
37679
37680
|
NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.set(nativeRuleKey, aliases);
|
|
37680
37681
|
}
|
|
37681
37682
|
const getLegacyRuleKeysForNative = (ruleKey) => NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.get(ruleKey) ?? [];
|
|
37682
|
-
const canonicalizeRuleKey = (ruleKey) =>
|
|
37683
|
+
const canonicalizeRuleKey = (ruleKey) => {
|
|
37684
|
+
const nativeRuleKey = LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY[ruleKey];
|
|
37685
|
+
return typeof nativeRuleKey === "string" ? nativeRuleKey : ruleKey;
|
|
37686
|
+
};
|
|
37683
37687
|
const isReactDoctorShortIdOf = (bareRuleKey, qualifiedRuleKey) => !bareRuleKey.includes("/") && qualifiedRuleKey === `react-doctor/${bareRuleKey}`;
|
|
37684
37688
|
const isSameRuleKey = (candidateRuleKey, targetRuleKey) => {
|
|
37685
37689
|
const canonicalCandidate = canonicalizeRuleKey(candidateRuleKey);
|
|
@@ -38765,6 +38769,12 @@ const BOOLEAN_FIELD_NAMES = [
|
|
|
38765
38769
|
"adoptExistingLintConfig"
|
|
38766
38770
|
];
|
|
38767
38771
|
const STRING_FIELD_NAMES = ["rootDir"];
|
|
38772
|
+
const STRING_ARRAY_FIELD_NAMES = [
|
|
38773
|
+
"projects",
|
|
38774
|
+
"textComponents",
|
|
38775
|
+
"rawTextWrapperComponents",
|
|
38776
|
+
"serverAuthFunctionNames"
|
|
38777
|
+
];
|
|
38768
38778
|
const SURFACE_CONTROL_FIELD_NAMES = [
|
|
38769
38779
|
"includeTags",
|
|
38770
38780
|
"excludeTags",
|
|
@@ -38866,6 +38876,7 @@ const validateConfigTypes = (config) => {
|
|
|
38866
38876
|
const validated = { ...config };
|
|
38867
38877
|
for (const fieldName of BOOLEAN_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => coerceMaybeBooleanString(fieldName, value));
|
|
38868
38878
|
for (const fieldName of STRING_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateString(fieldName, value));
|
|
38879
|
+
for (const fieldName of STRING_ARRAY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateStringArrayField(fieldName, value));
|
|
38869
38880
|
applyFieldValidator(config, validated, "surfaces", validateSurfacesField);
|
|
38870
38881
|
for (const fieldName of SEVERITY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateSeverityMap(fieldName, value, fieldName === "categories"));
|
|
38871
38882
|
applyFieldValidator(config, validated, "plugins", (value) => validateStringArrayField("plugins", value));
|
|
@@ -41094,43 +41105,46 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
41094
41105
|
* reason: GitInvocationFailed })` so the rest of the codebase
|
|
41095
41106
|
* sees a single failure channel.
|
|
41096
41107
|
*/
|
|
41097
|
-
const runCommand = (input) =>
|
|
41098
|
-
const
|
|
41099
|
-
cwd: input.directory,
|
|
41100
|
-
env: input.env,
|
|
41101
|
-
extendEnv: true
|
|
41102
|
-
}));
|
|
41103
|
-
const maxStdoutBytes = input.maxStdoutBytes;
|
|
41104
|
-
const stdoutByteCount = yield* make$13(0);
|
|
41105
|
-
const [stdout, stderr, status] = yield* all([
|
|
41106
|
-
mkString(decodeText(maxStdoutBytes === void 0 ? handle.stdout : handle.stdout.pipe(tap((chunk) => updateAndGet(stdoutByteCount, (total) => total + chunk.length).pipe(flatMap$2((total) => total > maxStdoutBytes ? fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
41107
|
-
args: [...input.args],
|
|
41108
|
-
directory: input.directory,
|
|
41109
|
-
cause: /* @__PURE__ */ new Error(`git stdout exceeded ${maxStdoutBytes} bytes`)
|
|
41110
|
-
}) })) : void_)))))),
|
|
41111
|
-
mkString(decodeText(handle.stderr)),
|
|
41112
|
-
handle.exitCode
|
|
41113
|
-
], { concurrency: 3 });
|
|
41114
|
-
return {
|
|
41115
|
-
status,
|
|
41116
|
-
stdout,
|
|
41117
|
-
stderr
|
|
41118
|
-
};
|
|
41119
|
-
})).pipe(catchTag$1("PlatformError", (cause) => {
|
|
41120
|
-
if (input.command !== "git") return succeed$2({
|
|
41108
|
+
const runCommand = (input) => {
|
|
41109
|
+
const foldSpawnFailure = (cause) => input.command !== "git" ? succeed$2({
|
|
41121
41110
|
status: 127,
|
|
41122
41111
|
stdout: "",
|
|
41123
41112
|
stderr: String(cause)
|
|
41124
|
-
})
|
|
41125
|
-
return new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
41113
|
+
}) : fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
41126
41114
|
args: [...input.args],
|
|
41127
41115
|
directory: input.directory,
|
|
41128
41116
|
cause
|
|
41129
|
-
}) });
|
|
41130
|
-
|
|
41131
|
-
|
|
41132
|
-
|
|
41133
|
-
|
|
41117
|
+
}) }));
|
|
41118
|
+
return scoped(gen(function* () {
|
|
41119
|
+
if (!isDirectory(input.directory)) return yield* foldSpawnFailure(`spawn ENOTDIR (cwd is not a directory: ${input.directory})`);
|
|
41120
|
+
const argvLengthChars = input.command.length + 1 + input.args.reduce((total, arg) => total + arg.length + 1, 0);
|
|
41121
|
+
if (argvLengthChars > 24e3) return yield* foldSpawnFailure(`spawn ENAMETOOLONG (${argvLengthChars} argv chars exceed ${SPAWN_ARGS_MAX_LENGTH_CHARS})`);
|
|
41122
|
+
const handle = yield* spawner.spawn(make$1(input.command, [...input.args], {
|
|
41123
|
+
cwd: input.directory,
|
|
41124
|
+
env: input.env,
|
|
41125
|
+
extendEnv: true
|
|
41126
|
+
}));
|
|
41127
|
+
const maxStdoutBytes = input.maxStdoutBytes;
|
|
41128
|
+
const stdoutByteCount = yield* make$13(0);
|
|
41129
|
+
const [stdout, stderr, status] = yield* all([
|
|
41130
|
+
mkString(decodeText(maxStdoutBytes === void 0 ? handle.stdout : handle.stdout.pipe(tap((chunk) => updateAndGet(stdoutByteCount, (total) => total + chunk.length).pipe(flatMap$2((total) => total > maxStdoutBytes ? fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
41131
|
+
args: [...input.args],
|
|
41132
|
+
directory: input.directory,
|
|
41133
|
+
cause: /* @__PURE__ */ new Error(`git stdout exceeded ${maxStdoutBytes} bytes`)
|
|
41134
|
+
}) })) : void_)))))),
|
|
41135
|
+
mkString(decodeText(handle.stderr)),
|
|
41136
|
+
handle.exitCode
|
|
41137
|
+
], { concurrency: 3 });
|
|
41138
|
+
return {
|
|
41139
|
+
status,
|
|
41140
|
+
stdout,
|
|
41141
|
+
stderr
|
|
41142
|
+
};
|
|
41143
|
+
})).pipe(catchTag$1("PlatformError", foldSpawnFailure), withSpan("git.exec", { attributes: {
|
|
41144
|
+
"git.command": input.command,
|
|
41145
|
+
"git.subcommand": input.args[0] ?? ""
|
|
41146
|
+
} }));
|
|
41147
|
+
};
|
|
41134
41148
|
const runGit = (directory, args) => runCommand({
|
|
41135
41149
|
command: "git",
|
|
41136
41150
|
args,
|
|
@@ -41163,7 +41177,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
41163
41177
|
"rev-parse",
|
|
41164
41178
|
"--verify",
|
|
41165
41179
|
branch
|
|
41166
|
-
]).pipe(map$3((result) => result.status === 0));
|
|
41180
|
+
]).pipe(map$3((result) => result.status === 0), catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(false) : fail$4(error)));
|
|
41167
41181
|
const headSha = (directory) => runGit(directory, ["rev-parse", "HEAD"]).pipe(map$3((result) => result.status === 0 ? trimOrNull(result.stdout) : null));
|
|
41168
41182
|
const mergeBase = (input) => isSafeGitRevision(input.ref) ? runGit(input.directory, [
|
|
41169
41183
|
"merge-base",
|
|
@@ -41377,7 +41391,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
41377
41391
|
]);
|
|
41378
41392
|
if (result.status !== 0) return null;
|
|
41379
41393
|
return parseChangedLineRanges(result.stdout);
|
|
41380
|
-
}).pipe(withSpan("Git.changedLineRanges"))
|
|
41394
|
+
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
41381
41395
|
});
|
|
41382
41396
|
})).pipe(provide$2(layer$3.pipe(provide$2(mergeAll$1(layer$2, layer$1)))));
|
|
41383
41397
|
/**
|
|
@@ -44839,6 +44853,7 @@ const NANOSECONDS_PER_SECOND = 1000000000n;
|
|
|
44839
44853
|
const METRIC = {
|
|
44840
44854
|
cliInvoked: "cli.invoked",
|
|
44841
44855
|
cliError: "cli.error",
|
|
44856
|
+
cliEnvironmentError: "cli.env_error",
|
|
44842
44857
|
projectDetected: "project.detected",
|
|
44843
44858
|
projectPathSelected: "project.path_selected",
|
|
44844
44859
|
projectConfigSelected: "project.config_selected",
|
|
@@ -44911,7 +44926,7 @@ const makeNoopConsole = () => ({
|
|
|
44911
44926
|
});
|
|
44912
44927
|
//#endregion
|
|
44913
44928
|
//#region src/cli/utils/version.ts
|
|
44914
|
-
const VERSION = "0.5.8-dev.
|
|
44929
|
+
const VERSION = "0.5.8-dev.229ea2e";
|
|
44915
44930
|
//#endregion
|
|
44916
44931
|
//#region src/cli/utils/json-mode.ts
|
|
44917
44932
|
let context = null;
|
|
@@ -45275,13 +45290,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
45275
45290
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
45276
45291
|
* standard `SENTRY_RELEASE` override.
|
|
45277
45292
|
*/
|
|
45278
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.5.8-dev.
|
|
45293
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.5.8-dev.229ea2e`;
|
|
45279
45294
|
/**
|
|
45280
45295
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
45281
45296
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
45282
45297
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
45283
45298
|
*/
|
|
45284
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.5.8-dev.
|
|
45299
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.5.8-dev.229ea2e") ? "development" : "production");
|
|
45285
45300
|
/**
|
|
45286
45301
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
45287
45302
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -49690,6 +49705,7 @@ const CI_PITCH_EVENT = "ci-pitch";
|
|
|
49690
49705
|
const ACTION_UPGRADE_EVENT = "action-upgrade-v2";
|
|
49691
49706
|
const SETUP_HINT_EVENT = "setup-hint";
|
|
49692
49707
|
const HANDOFF_TARGET_PREFERENCE_ID = "handoff-target";
|
|
49708
|
+
const INSTALL_AGENTS_PREFERENCE_ID = "install-agents";
|
|
49693
49709
|
const foldLegacyDecisions = (projects, legacy, eventId) => {
|
|
49694
49710
|
for (const [hash, record] of Object.entries(legacy ?? {})) {
|
|
49695
49711
|
const existing = projects[hash] ?? { rootDirectory: record.rootDirectory ?? "" };
|
|
@@ -50353,7 +50369,7 @@ const readPackageJson = (projectRoot) => {
|
|
|
50353
50369
|
return null;
|
|
50354
50370
|
}
|
|
50355
50371
|
};
|
|
50356
|
-
const writeJsonFile
|
|
50372
|
+
const writeJsonFile = (filePath, value) => {
|
|
50357
50373
|
NFS.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
50358
50374
|
};
|
|
50359
50375
|
const packageHasDependency = (projectRoot, dependencyName) => {
|
|
@@ -51073,7 +51089,8 @@ const getStagedSourceFiles = async (directory) => {
|
|
|
51073
51089
|
return [...await runPromise(gen(function* () {
|
|
51074
51090
|
return yield* (yield* StagedFiles).discoverSourceFiles(directory);
|
|
51075
51091
|
}).pipe(provide(stagedFilesLayer)))];
|
|
51076
|
-
} catch {
|
|
51092
|
+
} catch (error) {
|
|
51093
|
+
cliLogger.warn(`Failed to discover staged files: ${error instanceof Error ? error.message : String(error)}`);
|
|
51077
51094
|
return [];
|
|
51078
51095
|
}
|
|
51079
51096
|
};
|
|
@@ -51092,6 +51109,35 @@ const materializeStagedFiles = async (directory, stagedFiles, tempDirectory) =>
|
|
|
51092
51109
|
};
|
|
51093
51110
|
};
|
|
51094
51111
|
//#endregion
|
|
51112
|
+
//#region src/cli/utils/is-environment-error.ts
|
|
51113
|
+
const isNodeSystemError = (error) => error instanceof Error && typeof error.code === "string";
|
|
51114
|
+
const ENVIRONMENT_ERROR_CODES = new Set([
|
|
51115
|
+
"ENOSPC",
|
|
51116
|
+
"EIO",
|
|
51117
|
+
"EROFS",
|
|
51118
|
+
"EACCES",
|
|
51119
|
+
"EPERM",
|
|
51120
|
+
"ENOTDIR"
|
|
51121
|
+
]);
|
|
51122
|
+
const isEnvironmentError = (error) => {
|
|
51123
|
+
if (!isNodeSystemError(error)) return false;
|
|
51124
|
+
if (error.code === "ENOENT") return error.syscall?.startsWith("spawn") ?? false;
|
|
51125
|
+
return error.code !== void 0 && ENVIRONMENT_ERROR_CODES.has(error.code);
|
|
51126
|
+
};
|
|
51127
|
+
const formatEnvironmentError = (error) => {
|
|
51128
|
+
if (!isNodeSystemError(error)) return error instanceof Error ? error.message : String(error);
|
|
51129
|
+
switch (error.code) {
|
|
51130
|
+
case "ENOSPC": return "No space left on device. Free up disk space and try again.";
|
|
51131
|
+
case "EIO": return "I/O error: the filesystem or disk may be failing. Check your system logs.";
|
|
51132
|
+
case "EROFS": return "Read-only filesystem: cannot write to this location.";
|
|
51133
|
+
case "EACCES":
|
|
51134
|
+
case "EPERM": return error.path ? `Permission denied accessing ${error.path}. Check file permissions and try again.` : "Permission denied. Check file permissions and try again.";
|
|
51135
|
+
case "ENOTDIR": return error.path ? `A file exists at ${error.path} or one of its parent paths where a directory was expected.` : "A file exists where a directory was expected.";
|
|
51136
|
+
case "ENOENT": return "Required command not found. Ensure the tool (e.g. git) is installed and on your PATH.";
|
|
51137
|
+
default: return error.message;
|
|
51138
|
+
}
|
|
51139
|
+
};
|
|
51140
|
+
//#endregion
|
|
51095
51141
|
//#region src/cli/utils/handle-error.ts
|
|
51096
51142
|
const OTLP_ENDPOINT_ENVIRONMENT_VARIABLE = "REACT_DOCTOR_OTLP_ENDPOINT";
|
|
51097
51143
|
const OTLP_AUTH_HEADER_ENVIRONMENT_VARIABLE = "REACT_DOCTOR_OTLP_AUTH_HEADER";
|
|
@@ -51174,15 +51220,19 @@ const handleError = (error, options = {}) => {
|
|
|
51174
51220
|
process.exitCode = 1;
|
|
51175
51221
|
};
|
|
51176
51222
|
/**
|
|
51177
|
-
* Renderer for expected, user-actionable failures — a bad `--diff` value
|
|
51178
|
-
* a base branch that isn't fetched
|
|
51179
|
-
*
|
|
51180
|
-
*
|
|
51223
|
+
* Renderer for expected, user-actionable failures — a bad `--diff` value,
|
|
51224
|
+
* a base branch that isn't fetched, or environment errors like disk-full or
|
|
51225
|
+
* permission-denied. Prints just the (already human-readable) message — no
|
|
51226
|
+
* "Something went wrong", prefilled issue, Discord link, or Sentry reference
|
|
51227
|
+
* — because there is no bug to report.
|
|
51181
51228
|
*/
|
|
51182
51229
|
const handleUserError = (error, options = {}) => {
|
|
51230
|
+
const isEnvError = isEnvironmentError(error);
|
|
51231
|
+
if (isEnvError) recordCount(METRIC.cliEnvironmentError, 1, { code: error.code ?? "unknown" });
|
|
51232
|
+
const message = isEnvError ? formatEnvironmentError(error) : formatErrorForReport(error);
|
|
51183
51233
|
runSync(gen(function* () {
|
|
51184
51234
|
yield* error$1("");
|
|
51185
|
-
yield* error$1(highlighter.error(
|
|
51235
|
+
yield* error$1(highlighter.error(message));
|
|
51186
51236
|
yield* error$1("");
|
|
51187
51237
|
}));
|
|
51188
51238
|
if (options.shouldExit !== false) process.exit(1);
|
|
@@ -51197,7 +51247,7 @@ const handleUserError = (error, options = {}) => {
|
|
|
51197
51247
|
* `handleUserError` (a plain message — no "Something went wrong", prefilled
|
|
51198
51248
|
* issue, Discord link, or Sentry reference), since there is no bug to report.
|
|
51199
51249
|
*
|
|
51200
|
-
*
|
|
51250
|
+
* Four distinct shapes reach the CLI's catch blocks:
|
|
51201
51251
|
*
|
|
51202
51252
|
* - **Project-discovery failures** (`NoReactDependencyError`,
|
|
51203
51253
|
* `ProjectNotFoundError`, `PackageJsonNotFoundError`, `NotADirectoryError`,
|
|
@@ -51210,12 +51260,19 @@ const handleUserError = (error, options = {}) => {
|
|
|
51210
51260
|
* `--project` name.
|
|
51211
51261
|
* - **Bad `--diff` input** (`GitBaseBranchInvalid` / `GitBaseBranchMissing`)
|
|
51212
51262
|
* stays the tagged `ReactDoctorError`, so dispatch on the reason `_tag`.
|
|
51263
|
+
* - **Environment failures** (`ENOSPC`, `EIO`, `EROFS`, `EACCES`, `EPERM`,
|
|
51264
|
+
* `ENOTDIR`, plus a `spawn`-scoped `ENOENT` for a missing binary) — disk
|
|
51265
|
+
* full / failing / read-only, permission denied, or a path blocked by a
|
|
51266
|
+
* file. React Doctor cannot fix the user's environment; exit cleanly with an
|
|
51267
|
+
* actionable message instead of crashing. See `is-environment-error.ts` for
|
|
51268
|
+
* why the set stays narrow (codes that usually mean our bug keep reaching
|
|
51269
|
+
* Sentry).
|
|
51213
51270
|
*
|
|
51214
51271
|
* This composes the existing core narrowers rather than introducing a new
|
|
51215
51272
|
* error-shape helper (AGENTS.md): it encodes CLI-layer reporting policy, not
|
|
51216
51273
|
* knowledge of the `ReactDoctorError` shape.
|
|
51217
51274
|
*/
|
|
51218
|
-
const isExpectedUserError = (error) => error instanceof CliInputError || isProjectDiscoveryError(error) || isReactDoctorError(error) && (error.reason._tag === "GitBaseBranchInvalid" || error.reason._tag === "GitBaseBranchMissing");
|
|
51275
|
+
const isExpectedUserError = (error) => error instanceof CliInputError || isProjectDiscoveryError(error) || isEnvironmentError(error) || isReactDoctorError(error) && (error.reason._tag === "GitBaseBranchInvalid" || error.reason._tag === "GitBaseBranchMissing");
|
|
51219
51276
|
//#endregion
|
|
51220
51277
|
//#region src/cli/utils/build-handoff-payload.ts
|
|
51221
51278
|
const buildHandoffPayload = (input) => {
|
|
@@ -51296,6 +51353,20 @@ const detectAvailableAgents = async () => {
|
|
|
51296
51353
|
const detected = new Set([...detectPathAvailableAgents(), ...await detectInstalledSkillAgents()]);
|
|
51297
51354
|
return getSkillAgentTypes().filter((agent) => agent !== "universal" && detected.has(agent));
|
|
51298
51355
|
};
|
|
51356
|
+
const DEFAULT_INSTALL_AGENTS = [
|
|
51357
|
+
"claude-code",
|
|
51358
|
+
"cursor",
|
|
51359
|
+
"codex",
|
|
51360
|
+
"opencode"
|
|
51361
|
+
];
|
|
51362
|
+
const computeDefaultSelectedAgents = (detectedAgents, rememberedAgents) => {
|
|
51363
|
+
const detected = new Set(detectedAgents);
|
|
51364
|
+
const remembered = rememberedAgents.filter((agent) => detected.has(agent));
|
|
51365
|
+
if (remembered.length > 0) return remembered;
|
|
51366
|
+
const defaults = DEFAULT_INSTALL_AGENTS.filter((agent) => detected.has(agent));
|
|
51367
|
+
if (defaults.length > 0) return defaults;
|
|
51368
|
+
return detectedAgents.length === 1 ? [...detectedAgents] : [];
|
|
51369
|
+
};
|
|
51299
51370
|
//#endregion
|
|
51300
51371
|
//#region src/cli/utils/install-doctor-script.ts
|
|
51301
51372
|
const DOCTOR_SCRIPT_NAME = "doctor";
|
|
@@ -51377,7 +51448,7 @@ const installDoctorScript = (options) => {
|
|
|
51377
51448
|
};
|
|
51378
51449
|
})();
|
|
51379
51450
|
const scriptStatus = scriptTarget.status;
|
|
51380
|
-
if (scriptStatus === "created") writeJsonFile
|
|
51451
|
+
if (scriptStatus === "created") writeJsonFile(packageJsonPath, {
|
|
51381
51452
|
...packageJson,
|
|
51382
51453
|
scripts: {
|
|
51383
51454
|
...isRecord$1(scripts) ? scripts : {},
|
|
@@ -51948,6 +52019,19 @@ const setUpGitHubActions = async (options) => {
|
|
|
51948
52019
|
return didCreateWorkflow;
|
|
51949
52020
|
};
|
|
51950
52021
|
//#endregion
|
|
52022
|
+
//#region src/cli/utils/install-agents-preference.ts
|
|
52023
|
+
const INSTALL_AGENTS_PREFERENCE = {
|
|
52024
|
+
id: INSTALL_AGENTS_PREFERENCE_ID,
|
|
52025
|
+
scope: "global"
|
|
52026
|
+
};
|
|
52027
|
+
const PREFERENCE_SEPARATOR = ",";
|
|
52028
|
+
const readInstallAgents = (options = {}) => {
|
|
52029
|
+
const stored = readPreference(INSTALL_AGENTS_PREFERENCE, {}, options);
|
|
52030
|
+
if (stored === null) return [];
|
|
52031
|
+
return stored.split(PREFERENCE_SEPARATOR).map((entry) => entry.trim()).filter((entry) => isSkillAgentType(entry));
|
|
52032
|
+
};
|
|
52033
|
+
const rememberInstallAgents = (agents, options = {}) => writePreference(INSTALL_AGENTS_PREFERENCE, agents.join(PREFERENCE_SEPARATOR), {}, options);
|
|
52034
|
+
//#endregion
|
|
51951
52035
|
//#region src/cli/utils/install-agent-hooks.ts
|
|
51952
52036
|
const CLAUDE_AGENT = "claude-code";
|
|
51953
52037
|
const CURSOR_AGENT = "cursor";
|
|
@@ -51958,20 +52042,34 @@ const CURSOR_HOOKS_RELATIVE_PATH = ".cursor/hooks.json";
|
|
|
51958
52042
|
const CURSOR_HOOK_RELATIVE_PATH = ".cursor/hooks/react-doctor.sh";
|
|
51959
52043
|
const CURSOR_HOOK_MATCHER = "Write|Edit|MultiEdit|ApplyPatch";
|
|
51960
52044
|
const CURSOR_HOOKS_SCHEMA_VERSION = 1;
|
|
51961
|
-
const JSON_INDENT_SPACES$1 = 2;
|
|
51962
52045
|
const isSupportedAgent = (agent) => agent === CLAUDE_AGENT || agent === CURSOR_AGENT;
|
|
51963
52046
|
const readJsonFile = (filePath, fallback) => {
|
|
51964
52047
|
if (!NFS.existsSync(filePath)) return fallback;
|
|
51965
52048
|
const content = NFS.readFileSync(filePath, "utf8").trim();
|
|
51966
52049
|
if (content.length === 0) return fallback;
|
|
51967
|
-
|
|
52050
|
+
try {
|
|
52051
|
+
return JSON.parse(content);
|
|
52052
|
+
} catch (error) {
|
|
52053
|
+
if (error instanceof SyntaxError) throw new CliInputError(`Could not parse ${filePath}: the file contains invalid JSON. Fix the syntax errors in this file and re-run the install command.`);
|
|
52054
|
+
throw error;
|
|
52055
|
+
}
|
|
51968
52056
|
};
|
|
51969
|
-
const
|
|
51970
|
-
|
|
51971
|
-
|
|
52057
|
+
const ensureDirectoryExists = (directoryPath) => {
|
|
52058
|
+
try {
|
|
52059
|
+
NFS.mkdirSync(directoryPath, { recursive: true });
|
|
52060
|
+
} catch (error) {
|
|
52061
|
+
const code = error.code;
|
|
52062
|
+
if (code === "EACCES" || code === "EPERM") throw new CliInputError(`Could not create directory ${directoryPath}: permission denied. Ensure you have write permissions for this location and re-run the install command.`);
|
|
52063
|
+
if (code === "ENOTDIR" || code === "EEXIST") throw new CliInputError(`Could not create directory ${directoryPath}: a file exists at this path or one of its parent paths. Remove the conflicting file and re-run the install command.`);
|
|
52064
|
+
throw error;
|
|
52065
|
+
}
|
|
52066
|
+
};
|
|
52067
|
+
const writeJsonFileWithDirectoryCheck = (filePath, value) => {
|
|
52068
|
+
ensureDirectoryExists(Path.dirname(filePath));
|
|
52069
|
+
writeJsonFile(filePath, value);
|
|
51972
52070
|
};
|
|
51973
52071
|
const writeHookScript = (filePath) => {
|
|
51974
|
-
|
|
52072
|
+
ensureDirectoryExists(Path.dirname(filePath));
|
|
51975
52073
|
NFS.writeFileSync(filePath, buildAgentHookScript());
|
|
51976
52074
|
NFS.chmodSync(filePath, 493);
|
|
51977
52075
|
};
|
|
@@ -51987,7 +52085,7 @@ const installClaudeHook = (projectRoot) => {
|
|
|
51987
52085
|
command: CLAUDE_HOOK_COMMAND
|
|
51988
52086
|
}] });
|
|
51989
52087
|
hooks.PostToolBatch = postToolBatchHooks;
|
|
51990
|
-
|
|
52088
|
+
writeJsonFileWithDirectoryCheck(settingsPath, {
|
|
51991
52089
|
...settings,
|
|
51992
52090
|
hooks
|
|
51993
52091
|
});
|
|
@@ -52007,7 +52105,7 @@ const installCursorHook = (projectRoot) => {
|
|
|
52007
52105
|
timeout: 120
|
|
52008
52106
|
});
|
|
52009
52107
|
hooks.postToolUse = postToolUseHooks;
|
|
52010
|
-
|
|
52108
|
+
writeJsonFileWithDirectoryCheck(configPath, {
|
|
52011
52109
|
...config,
|
|
52012
52110
|
version: config.version ?? CURSOR_HOOKS_SCHEMA_VERSION,
|
|
52013
52111
|
hooks
|
|
@@ -52243,7 +52341,7 @@ const installPackageJsonHook = (options, strategy) => {
|
|
|
52243
52341
|
parent = cloned;
|
|
52244
52342
|
}
|
|
52245
52343
|
parent[leafKey] = strategy.leafShape === "array" ? appendArrayCommand(parent[leafKey]) : appendStringCommand(parent[leafKey]);
|
|
52246
|
-
writeJsonFile
|
|
52344
|
+
writeJsonFile(packageJsonPath, nextPackageJson);
|
|
52247
52345
|
removeLegacyManagedRunner(options.projectRoot);
|
|
52248
52346
|
return {
|
|
52249
52347
|
hookPath: packageJsonPath,
|
|
@@ -52826,6 +52924,9 @@ const runInstallReactDoctor = async (options = {}) => {
|
|
|
52826
52924
|
const shouldUpgradeWorkflow = canUpgradeWorkflow && (Boolean(options.yes) || upgradePromptOutcome === "yes");
|
|
52827
52925
|
if (upgradePromptOutcome === "no" && !options.dryRun) recordActionUpgradeDecision(projectRoot, "declined");
|
|
52828
52926
|
if ((ciPromptOutcome === "yes" || ciPromptOutcome === "no") && !options.dryRun) recordCiPromptDecision(projectRoot, ciPromptOutcome === "yes" ? "accepted" : "declined");
|
|
52927
|
+
const rememberedAgents = options.lastSelectedAgents ?? readInstallAgents();
|
|
52928
|
+
const defaultSelectedAgents = computeDefaultSelectedAgents(detectedAgents, rememberedAgents);
|
|
52929
|
+
const usedRememberedAgents = rememberedAgents.some((agent) => detectedAgents.includes(agent));
|
|
52829
52930
|
const selectedAgents = skipPrompts ? detectedAgents : (await prompt({
|
|
52830
52931
|
type: "multiselect",
|
|
52831
52932
|
name: "agents",
|
|
@@ -52833,12 +52934,13 @@ const runInstallReactDoctor = async (options = {}) => {
|
|
|
52833
52934
|
choices: detectedAgents.map((agent) => ({
|
|
52834
52935
|
title: getSkillAgentConfig(agent).displayName,
|
|
52835
52936
|
value: agent,
|
|
52836
|
-
selected:
|
|
52937
|
+
selected: defaultSelectedAgents.includes(agent)
|
|
52837
52938
|
})),
|
|
52838
52939
|
instructions: false,
|
|
52839
52940
|
min: 1
|
|
52840
52941
|
}, promptOptions)).agents ?? [];
|
|
52841
52942
|
if (selectedAgents.length === 0) return;
|
|
52943
|
+
if (!skipPrompts && !options.dryRun) rememberInstallAgents(selectedAgents);
|
|
52842
52944
|
let dependencyResult;
|
|
52843
52945
|
if (!options.dryRun) {
|
|
52844
52946
|
await installReactDoctorSkillStep(sourceDir, selectedAgents, projectRoot);
|
|
@@ -52897,6 +52999,8 @@ const runInstallReactDoctor = async (options = {}) => {
|
|
|
52897
52999
|
}
|
|
52898
53000
|
recordCount(METRIC.installCompleted, 1, {
|
|
52899
53001
|
agentsCount: selectedAgents.length,
|
|
53002
|
+
agentsDetected: detectedAgents.length,
|
|
53003
|
+
usedRememberedAgents,
|
|
52900
53004
|
gitHook: shouldInstallGitHook,
|
|
52901
53005
|
agentHooks: shouldInstallAgentHooks,
|
|
52902
53006
|
workflow: didInstallWorkflow,
|
|
@@ -53676,7 +53780,7 @@ const warnDeprecatedDiff = (flags, userConfig) => {
|
|
|
53676
53780
|
};
|
|
53677
53781
|
const warnDiffUnavailable = (requested, isQuiet) => {
|
|
53678
53782
|
if (isQuiet) return;
|
|
53679
|
-
if (typeof requested.base === "string") cliLogger.warn(`Could not compute diff against "${requested.base}" (
|
|
53783
|
+
if (typeof requested.base === "string") cliLogger.warn(`Could not compute diff against "${requested.base}" (git unavailable, ref not found, or merge-base failed). Running full scan.`);
|
|
53680
53784
|
else cliLogger.warn("No feature branch or uncommitted changes detected. Running full scan.");
|
|
53681
53785
|
cliLogger.break();
|
|
53682
53786
|
};
|
|
@@ -54367,6 +54471,10 @@ const installAction = async (options, command) => {
|
|
|
54367
54471
|
projectRoot: options.cwd ?? process.cwd()
|
|
54368
54472
|
});
|
|
54369
54473
|
} catch (error) {
|
|
54474
|
+
if (isExpectedUserError(error)) {
|
|
54475
|
+
handleUserError(error);
|
|
54476
|
+
return;
|
|
54477
|
+
}
|
|
54370
54478
|
handleError(error, { sentryEventId: await reportErrorToSentry(error) });
|
|
54371
54479
|
}
|
|
54372
54480
|
};
|
|
@@ -55374,4 +55482,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
55374
55482
|
export {};
|
|
55375
55483
|
|
|
55376
55484
|
//# sourceMappingURL=cli.js.map
|
|
55377
|
-
//# debugId=
|
|
55485
|
+
//# debugId=6c903b87-8faf-5702-b078-03465e326385
|
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]="dccb6d63-63d4-5e91-af21-2756a08fe93a")}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";
|
|
@@ -33694,6 +33694,7 @@ const MILLISECONDS_PER_SECOND = 1e3;
|
|
|
33694
33694
|
const SCORE_API_URL = "https://www.react.doctor/api/score";
|
|
33695
33695
|
const FETCH_TIMEOUT_MS = 1e4;
|
|
33696
33696
|
const GITHUB_VIEWER_PERMISSION_TIMEOUT_MS = 2e3;
|
|
33697
|
+
const SPAWN_ARGS_MAX_LENGTH_CHARS = 24e3;
|
|
33697
33698
|
const PER_WORKER_MEM_BUDGET_BYTES = 1024 * 1024 * 1024;
|
|
33698
33699
|
const DEFAULT_BRANCH_CANDIDATES = ["main", "master"];
|
|
33699
33700
|
const ADOPTABLE_LINT_CONFIG_FILENAMES = [".oxlintrc.json", ".eslintrc.json"];
|
|
@@ -34487,7 +34488,10 @@ for (const [legacyRuleKey, nativeRuleKey] of Object.entries(LEGACY_RULE_KEY_TO_N
|
|
|
34487
34488
|
NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.set(nativeRuleKey, aliases);
|
|
34488
34489
|
}
|
|
34489
34490
|
const getLegacyRuleKeysForNative = (ruleKey) => NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.get(ruleKey) ?? [];
|
|
34490
|
-
const canonicalizeRuleKey = (ruleKey) =>
|
|
34491
|
+
const canonicalizeRuleKey = (ruleKey) => {
|
|
34492
|
+
const nativeRuleKey = LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY[ruleKey];
|
|
34493
|
+
return typeof nativeRuleKey === "string" ? nativeRuleKey : ruleKey;
|
|
34494
|
+
};
|
|
34491
34495
|
const isReactDoctorShortIdOf = (bareRuleKey, qualifiedRuleKey) => !bareRuleKey.includes("/") && qualifiedRuleKey === `react-doctor/${bareRuleKey}`;
|
|
34492
34496
|
const isSameRuleKey = (candidateRuleKey, targetRuleKey) => {
|
|
34493
34497
|
const canonicalCandidate = canonicalizeRuleKey(candidateRuleKey);
|
|
@@ -35577,6 +35581,12 @@ const BOOLEAN_FIELD_NAMES = [
|
|
|
35577
35581
|
"adoptExistingLintConfig"
|
|
35578
35582
|
];
|
|
35579
35583
|
const STRING_FIELD_NAMES = ["rootDir"];
|
|
35584
|
+
const STRING_ARRAY_FIELD_NAMES = [
|
|
35585
|
+
"projects",
|
|
35586
|
+
"textComponents",
|
|
35587
|
+
"rawTextWrapperComponents",
|
|
35588
|
+
"serverAuthFunctionNames"
|
|
35589
|
+
];
|
|
35580
35590
|
const SURFACE_CONTROL_FIELD_NAMES = [
|
|
35581
35591
|
"includeTags",
|
|
35582
35592
|
"excludeTags",
|
|
@@ -35678,6 +35688,7 @@ const validateConfigTypes = (config) => {
|
|
|
35678
35688
|
const validated = { ...config };
|
|
35679
35689
|
for (const fieldName of BOOLEAN_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => coerceMaybeBooleanString(fieldName, value));
|
|
35680
35690
|
for (const fieldName of STRING_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateString(fieldName, value));
|
|
35691
|
+
for (const fieldName of STRING_ARRAY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateStringArrayField(fieldName, value));
|
|
35681
35692
|
applyFieldValidator(config, validated, "surfaces", validateSurfacesField);
|
|
35682
35693
|
for (const fieldName of SEVERITY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateSeverityMap(fieldName, value, fieldName === "categories"));
|
|
35683
35694
|
applyFieldValidator(config, validated, "plugins", (value) => validateStringArrayField("plugins", value));
|
|
@@ -37862,43 +37873,46 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37862
37873
|
* reason: GitInvocationFailed })` so the rest of the codebase
|
|
37863
37874
|
* sees a single failure channel.
|
|
37864
37875
|
*/
|
|
37865
|
-
const runCommand = (input) =>
|
|
37866
|
-
const
|
|
37867
|
-
cwd: input.directory,
|
|
37868
|
-
env: input.env,
|
|
37869
|
-
extendEnv: true
|
|
37870
|
-
}));
|
|
37871
|
-
const maxStdoutBytes = input.maxStdoutBytes;
|
|
37872
|
-
const stdoutByteCount = yield* make$13(0);
|
|
37873
|
-
const [stdout, stderr, status] = yield* all([
|
|
37874
|
-
mkString(decodeText(maxStdoutBytes === void 0 ? handle.stdout : handle.stdout.pipe(tap((chunk) => updateAndGet(stdoutByteCount, (total) => total + chunk.length).pipe(flatMap$2((total) => total > maxStdoutBytes ? fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
37875
|
-
args: [...input.args],
|
|
37876
|
-
directory: input.directory,
|
|
37877
|
-
cause: /* @__PURE__ */ new Error(`git stdout exceeded ${maxStdoutBytes} bytes`)
|
|
37878
|
-
}) })) : void_)))))),
|
|
37879
|
-
mkString(decodeText(handle.stderr)),
|
|
37880
|
-
handle.exitCode
|
|
37881
|
-
], { concurrency: 3 });
|
|
37882
|
-
return {
|
|
37883
|
-
status,
|
|
37884
|
-
stdout,
|
|
37885
|
-
stderr
|
|
37886
|
-
};
|
|
37887
|
-
})).pipe(catchTag$1("PlatformError", (cause) => {
|
|
37888
|
-
if (input.command !== "git") return succeed$2({
|
|
37876
|
+
const runCommand = (input) => {
|
|
37877
|
+
const foldSpawnFailure = (cause) => input.command !== "git" ? succeed$2({
|
|
37889
37878
|
status: 127,
|
|
37890
37879
|
stdout: "",
|
|
37891
37880
|
stderr: String(cause)
|
|
37892
|
-
})
|
|
37893
|
-
return new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
37881
|
+
}) : fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
37894
37882
|
args: [...input.args],
|
|
37895
37883
|
directory: input.directory,
|
|
37896
37884
|
cause
|
|
37897
|
-
}) });
|
|
37898
|
-
|
|
37899
|
-
|
|
37900
|
-
|
|
37901
|
-
|
|
37885
|
+
}) }));
|
|
37886
|
+
return scoped(gen(function* () {
|
|
37887
|
+
if (!isDirectory(input.directory)) return yield* foldSpawnFailure(`spawn ENOTDIR (cwd is not a directory: ${input.directory})`);
|
|
37888
|
+
const argvLengthChars = input.command.length + 1 + input.args.reduce((total, arg) => total + arg.length + 1, 0);
|
|
37889
|
+
if (argvLengthChars > 24e3) return yield* foldSpawnFailure(`spawn ENAMETOOLONG (${argvLengthChars} argv chars exceed ${SPAWN_ARGS_MAX_LENGTH_CHARS})`);
|
|
37890
|
+
const handle = yield* spawner.spawn(make$1(input.command, [...input.args], {
|
|
37891
|
+
cwd: input.directory,
|
|
37892
|
+
env: input.env,
|
|
37893
|
+
extendEnv: true
|
|
37894
|
+
}));
|
|
37895
|
+
const maxStdoutBytes = input.maxStdoutBytes;
|
|
37896
|
+
const stdoutByteCount = yield* make$13(0);
|
|
37897
|
+
const [stdout, stderr, status] = yield* all([
|
|
37898
|
+
mkString(decodeText(maxStdoutBytes === void 0 ? handle.stdout : handle.stdout.pipe(tap((chunk) => updateAndGet(stdoutByteCount, (total) => total + chunk.length).pipe(flatMap$2((total) => total > maxStdoutBytes ? fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
37899
|
+
args: [...input.args],
|
|
37900
|
+
directory: input.directory,
|
|
37901
|
+
cause: /* @__PURE__ */ new Error(`git stdout exceeded ${maxStdoutBytes} bytes`)
|
|
37902
|
+
}) })) : void_)))))),
|
|
37903
|
+
mkString(decodeText(handle.stderr)),
|
|
37904
|
+
handle.exitCode
|
|
37905
|
+
], { concurrency: 3 });
|
|
37906
|
+
return {
|
|
37907
|
+
status,
|
|
37908
|
+
stdout,
|
|
37909
|
+
stderr
|
|
37910
|
+
};
|
|
37911
|
+
})).pipe(catchTag$1("PlatformError", foldSpawnFailure), withSpan("git.exec", { attributes: {
|
|
37912
|
+
"git.command": input.command,
|
|
37913
|
+
"git.subcommand": input.args[0] ?? ""
|
|
37914
|
+
} }));
|
|
37915
|
+
};
|
|
37902
37916
|
const runGit = (directory, args) => runCommand({
|
|
37903
37917
|
command: "git",
|
|
37904
37918
|
args,
|
|
@@ -37931,7 +37945,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37931
37945
|
"rev-parse",
|
|
37932
37946
|
"--verify",
|
|
37933
37947
|
branch
|
|
37934
|
-
]).pipe(map$3((result) => result.status === 0));
|
|
37948
|
+
]).pipe(map$3((result) => result.status === 0), catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(false) : fail$4(error)));
|
|
37935
37949
|
const headSha = (directory) => runGit(directory, ["rev-parse", "HEAD"]).pipe(map$3((result) => result.status === 0 ? trimOrNull(result.stdout) : null));
|
|
37936
37950
|
const mergeBase = (input) => isSafeGitRevision(input.ref) ? runGit(input.directory, [
|
|
37937
37951
|
"merge-base",
|
|
@@ -38145,7 +38159,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38145
38159
|
]);
|
|
38146
38160
|
if (result.status !== 0) return null;
|
|
38147
38161
|
return parseChangedLineRanges(result.stdout);
|
|
38148
|
-
}).pipe(withSpan("Git.changedLineRanges"))
|
|
38162
|
+
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
38149
38163
|
});
|
|
38150
38164
|
})).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
|
|
38151
38165
|
/**
|
|
@@ -41469,4 +41483,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
41469
41483
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
41470
41484
|
|
|
41471
41485
|
//# sourceMappingURL=index.js.map
|
|
41472
|
-
//# debugId=
|
|
41486
|
+
//# debugId=dccb6d63-63d4-5e91-af21-2756a08fe93a
|
package/dist/lsp.js
CHANGED
|
@@ -33730,6 +33730,7 @@ const MILLISECONDS_PER_SECOND = 1e3;
|
|
|
33730
33730
|
const SCORE_API_URL = "https://www.react.doctor/api/score";
|
|
33731
33731
|
const FETCH_TIMEOUT_MS = 1e4;
|
|
33732
33732
|
const GITHUB_VIEWER_PERMISSION_TIMEOUT_MS = 2e3;
|
|
33733
|
+
const SPAWN_ARGS_MAX_LENGTH_CHARS = 24e3;
|
|
33733
33734
|
const PER_WORKER_MEM_BUDGET_BYTES = 1024 * 1024 * 1024;
|
|
33734
33735
|
const DEFAULT_BRANCH_CANDIDATES = ["main", "master"];
|
|
33735
33736
|
const ADOPTABLE_LINT_CONFIG_FILENAMES = [".oxlintrc.json", ".eslintrc.json"];
|
|
@@ -34546,7 +34547,10 @@ for (const [legacyRuleKey, nativeRuleKey] of Object.entries(LEGACY_RULE_KEY_TO_N
|
|
|
34546
34547
|
NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.set(nativeRuleKey, aliases);
|
|
34547
34548
|
}
|
|
34548
34549
|
const getLegacyRuleKeysForNative = (ruleKey) => NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.get(ruleKey) ?? [];
|
|
34549
|
-
const canonicalizeRuleKey = (ruleKey) =>
|
|
34550
|
+
const canonicalizeRuleKey = (ruleKey) => {
|
|
34551
|
+
const nativeRuleKey = LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY[ruleKey];
|
|
34552
|
+
return typeof nativeRuleKey === "string" ? nativeRuleKey : ruleKey;
|
|
34553
|
+
};
|
|
34550
34554
|
const isReactDoctorShortIdOf = (bareRuleKey, qualifiedRuleKey) => !bareRuleKey.includes("/") && qualifiedRuleKey === `react-doctor/${bareRuleKey}`;
|
|
34551
34555
|
const isSameRuleKey = (candidateRuleKey, targetRuleKey) => {
|
|
34552
34556
|
const canonicalCandidate = canonicalizeRuleKey(candidateRuleKey);
|
|
@@ -35610,6 +35614,12 @@ const BOOLEAN_FIELD_NAMES = [
|
|
|
35610
35614
|
"adoptExistingLintConfig"
|
|
35611
35615
|
];
|
|
35612
35616
|
const STRING_FIELD_NAMES = ["rootDir"];
|
|
35617
|
+
const STRING_ARRAY_FIELD_NAMES = [
|
|
35618
|
+
"projects",
|
|
35619
|
+
"textComponents",
|
|
35620
|
+
"rawTextWrapperComponents",
|
|
35621
|
+
"serverAuthFunctionNames"
|
|
35622
|
+
];
|
|
35613
35623
|
const SURFACE_CONTROL_FIELD_NAMES = [
|
|
35614
35624
|
"includeTags",
|
|
35615
35625
|
"excludeTags",
|
|
@@ -35711,6 +35721,7 @@ const validateConfigTypes = (config) => {
|
|
|
35711
35721
|
const validated = { ...config };
|
|
35712
35722
|
for (const fieldName of BOOLEAN_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => coerceMaybeBooleanString(fieldName, value));
|
|
35713
35723
|
for (const fieldName of STRING_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateString(fieldName, value));
|
|
35724
|
+
for (const fieldName of STRING_ARRAY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateStringArrayField(fieldName, value));
|
|
35714
35725
|
applyFieldValidator(config, validated, "surfaces", validateSurfacesField);
|
|
35715
35726
|
for (const fieldName of SEVERITY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateSeverityMap(fieldName, value, fieldName === "categories"));
|
|
35716
35727
|
applyFieldValidator(config, validated, "plugins", (value) => validateStringArrayField("plugins", value));
|
|
@@ -37847,43 +37858,46 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37847
37858
|
* reason: GitInvocationFailed })` so the rest of the codebase
|
|
37848
37859
|
* sees a single failure channel.
|
|
37849
37860
|
*/
|
|
37850
|
-
const runCommand = (input) =>
|
|
37851
|
-
const
|
|
37852
|
-
cwd: input.directory,
|
|
37853
|
-
env: input.env,
|
|
37854
|
-
extendEnv: true
|
|
37855
|
-
}));
|
|
37856
|
-
const maxStdoutBytes = input.maxStdoutBytes;
|
|
37857
|
-
const stdoutByteCount = yield* make$13(0);
|
|
37858
|
-
const [stdout, stderr, status] = yield* all([
|
|
37859
|
-
mkString(decodeText(maxStdoutBytes === void 0 ? handle.stdout : handle.stdout.pipe(tap((chunk) => updateAndGet(stdoutByteCount, (total) => total + chunk.length).pipe(flatMap$2((total) => total > maxStdoutBytes ? fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
37860
|
-
args: [...input.args],
|
|
37861
|
-
directory: input.directory,
|
|
37862
|
-
cause: /* @__PURE__ */ new Error(`git stdout exceeded ${maxStdoutBytes} bytes`)
|
|
37863
|
-
}) })) : void_)))))),
|
|
37864
|
-
mkString(decodeText(handle.stderr)),
|
|
37865
|
-
handle.exitCode
|
|
37866
|
-
], { concurrency: 3 });
|
|
37867
|
-
return {
|
|
37868
|
-
status,
|
|
37869
|
-
stdout,
|
|
37870
|
-
stderr
|
|
37871
|
-
};
|
|
37872
|
-
})).pipe(catchTag$1("PlatformError", (cause) => {
|
|
37873
|
-
if (input.command !== "git") return succeed$2({
|
|
37861
|
+
const runCommand = (input) => {
|
|
37862
|
+
const foldSpawnFailure = (cause) => input.command !== "git" ? succeed$2({
|
|
37874
37863
|
status: 127,
|
|
37875
37864
|
stdout: "",
|
|
37876
37865
|
stderr: String(cause)
|
|
37877
|
-
})
|
|
37878
|
-
return new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
37866
|
+
}) : fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
37879
37867
|
args: [...input.args],
|
|
37880
37868
|
directory: input.directory,
|
|
37881
37869
|
cause
|
|
37882
|
-
}) });
|
|
37883
|
-
|
|
37884
|
-
|
|
37885
|
-
|
|
37886
|
-
|
|
37870
|
+
}) }));
|
|
37871
|
+
return scoped(gen(function* () {
|
|
37872
|
+
if (!isDirectory(input.directory)) return yield* foldSpawnFailure(`spawn ENOTDIR (cwd is not a directory: ${input.directory})`);
|
|
37873
|
+
const argvLengthChars = input.command.length + 1 + input.args.reduce((total, arg) => total + arg.length + 1, 0);
|
|
37874
|
+
if (argvLengthChars > 24e3) return yield* foldSpawnFailure(`spawn ENAMETOOLONG (${argvLengthChars} argv chars exceed ${SPAWN_ARGS_MAX_LENGTH_CHARS})`);
|
|
37875
|
+
const handle = yield* spawner.spawn(make$1(input.command, [...input.args], {
|
|
37876
|
+
cwd: input.directory,
|
|
37877
|
+
env: input.env,
|
|
37878
|
+
extendEnv: true
|
|
37879
|
+
}));
|
|
37880
|
+
const maxStdoutBytes = input.maxStdoutBytes;
|
|
37881
|
+
const stdoutByteCount = yield* make$13(0);
|
|
37882
|
+
const [stdout, stderr, status] = yield* all([
|
|
37883
|
+
mkString(decodeText(maxStdoutBytes === void 0 ? handle.stdout : handle.stdout.pipe(tap((chunk) => updateAndGet(stdoutByteCount, (total) => total + chunk.length).pipe(flatMap$2((total) => total > maxStdoutBytes ? fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
37884
|
+
args: [...input.args],
|
|
37885
|
+
directory: input.directory,
|
|
37886
|
+
cause: /* @__PURE__ */ new Error(`git stdout exceeded ${maxStdoutBytes} bytes`)
|
|
37887
|
+
}) })) : void_)))))),
|
|
37888
|
+
mkString(decodeText(handle.stderr)),
|
|
37889
|
+
handle.exitCode
|
|
37890
|
+
], { concurrency: 3 });
|
|
37891
|
+
return {
|
|
37892
|
+
status,
|
|
37893
|
+
stdout,
|
|
37894
|
+
stderr
|
|
37895
|
+
};
|
|
37896
|
+
})).pipe(catchTag$1("PlatformError", foldSpawnFailure), withSpan("git.exec", { attributes: {
|
|
37897
|
+
"git.command": input.command,
|
|
37898
|
+
"git.subcommand": input.args[0] ?? ""
|
|
37899
|
+
} }));
|
|
37900
|
+
};
|
|
37887
37901
|
const runGit = (directory, args) => runCommand({
|
|
37888
37902
|
command: "git",
|
|
37889
37903
|
args,
|
|
@@ -37916,7 +37930,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37916
37930
|
"rev-parse",
|
|
37917
37931
|
"--verify",
|
|
37918
37932
|
branch
|
|
37919
|
-
]).pipe(map$3((result) => result.status === 0));
|
|
37933
|
+
]).pipe(map$3((result) => result.status === 0), catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(false) : fail$4(error)));
|
|
37920
37934
|
const headSha = (directory) => runGit(directory, ["rev-parse", "HEAD"]).pipe(map$3((result) => result.status === 0 ? trimOrNull(result.stdout) : null));
|
|
37921
37935
|
const mergeBase = (input) => isSafeGitRevision(input.ref) ? runGit(input.directory, [
|
|
37922
37936
|
"merge-base",
|
|
@@ -38130,7 +38144,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38130
38144
|
]);
|
|
38131
38145
|
if (result.status !== 0) return null;
|
|
38132
38146
|
return parseChangedLineRanges(result.stdout);
|
|
38133
|
-
}).pipe(withSpan("Git.changedLineRanges"))
|
|
38147
|
+
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
38134
38148
|
});
|
|
38135
38149
|
})).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
|
|
38136
38150
|
/**
|
|
@@ -42903,6 +42917,7 @@ const SENTRY_FLUSH_TIMEOUT_MS = 2e3;
|
|
|
42903
42917
|
const METRIC = {
|
|
42904
42918
|
cliInvoked: "cli.invoked",
|
|
42905
42919
|
cliError: "cli.error",
|
|
42920
|
+
cliEnvironmentError: "cli.env_error",
|
|
42906
42921
|
projectDetected: "project.detected",
|
|
42907
42922
|
projectPathSelected: "project.path_selected",
|
|
42908
42923
|
projectConfigSelected: "project.config_selected",
|
|
@@ -43249,5 +43264,5 @@ const startLanguageServer = () => {
|
|
|
43249
43264
|
};
|
|
43250
43265
|
//#endregion
|
|
43251
43266
|
export { startLanguageServer };
|
|
43252
|
-
!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]="
|
|
43253
|
-
//# debugId=
|
|
43267
|
+
!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]="d12f3c5d-14fd-59ba-9d8a-7e3f589c88ad")}catch(e){}}();
|
|
43268
|
+
//# debugId=d12f3c5d-14fd-59ba-9d8a-7e3f589c88ad
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-doctor",
|
|
3
|
-
"version": "0.5.8-dev.
|
|
3
|
+
"version": "0.5.8-dev.229ea2e",
|
|
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-textdocument": "^1.0.12",
|
|
65
65
|
"vscode-uri": "^3.1.0",
|
|
66
66
|
"deslop-js": "0.5.8",
|
|
67
|
-
"oxlint-plugin-react-doctor": "0.5.8-dev.
|
|
67
|
+
"oxlint-plugin-react-doctor": "0.5.8-dev.229ea2e"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@types/babel__code-frame": "^7.27.0",
|