react-doctor 0.7.6-dev.0150fc9 → 0.7.6-dev.0d11bd5
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 +162 -36
- package/dist/index.d.ts +1 -1
- package/dist/index.js +34 -15
- package/dist/lsp.js +32 -13
- package/package.json +4 -4
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]="58e54b02-7ba2-5f13-a535-98c236467a86")}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";
|
|
@@ -41823,6 +41823,7 @@ const parseGithubViewerPermission = (stdout) => {
|
|
|
41823
41823
|
return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
|
|
41824
41824
|
};
|
|
41825
41825
|
const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
|
|
41826
|
+
const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
|
|
41826
41827
|
/**
|
|
41827
41828
|
* `Git` wraps every `git`-via-subprocess call react-doctor makes
|
|
41828
41829
|
* behind a `Context.Service`. The production layer (`layerNode`)
|
|
@@ -41893,6 +41894,14 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
41893
41894
|
args,
|
|
41894
41895
|
directory
|
|
41895
41896
|
});
|
|
41897
|
+
const listUntrackedFilePaths = (directory, includePaths = []) => runGit(directory, [
|
|
41898
|
+
"ls-files",
|
|
41899
|
+
"-z",
|
|
41900
|
+
"--others",
|
|
41901
|
+
"--exclude-standard",
|
|
41902
|
+
...includePaths.length > 0 ? ["--", ...includePaths] : []
|
|
41903
|
+
]).pipe(map$3((result) => result.status === 0 ? splitNullSeparated(result.stdout) : null));
|
|
41904
|
+
const mergeUntracked = (directory, trackedFilePaths, includeUntracked) => includeUntracked ? listUntrackedFilePaths(directory).pipe(map$3((untracked) => untracked === null ? trackedFilePaths : [...new Set([...trackedFilePaths, ...untracked])])) : succeed$2(trackedFilePaths);
|
|
41896
41905
|
const currentBranch = (directory) => runGit(directory, [
|
|
41897
41906
|
"rev-parse",
|
|
41898
41907
|
"--abbrev-ref",
|
|
@@ -42015,7 +42024,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42015
42024
|
githubViewerPermission,
|
|
42016
42025
|
branchExists,
|
|
42017
42026
|
mergeBase,
|
|
42018
|
-
diffSelection: ({ directory, explicitBaseBranch }) => gen(function* () {
|
|
42027
|
+
diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
|
|
42019
42028
|
if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
|
|
42020
42029
|
if (explicitBaseBranch !== void 0) {
|
|
42021
42030
|
const range = parseGitDiffRange(explicitBaseBranch);
|
|
@@ -42044,7 +42053,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42044
42053
|
"HEAD"
|
|
42045
42054
|
]);
|
|
42046
42055
|
if (uncommitted.status !== 0) return null;
|
|
42047
|
-
const files = splitNullSeparated(uncommitted.stdout);
|
|
42056
|
+
const files = yield* mergeUntracked(directory, splitNullSeparated(uncommitted.stdout), includeUntracked);
|
|
42048
42057
|
if (files.length === 0) return null;
|
|
42049
42058
|
return {
|
|
42050
42059
|
currentBranch: resolvedCurrentBranch,
|
|
@@ -42074,7 +42083,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42074
42083
|
currentBranch: resolvedCurrentBranch,
|
|
42075
42084
|
baseBranch,
|
|
42076
42085
|
diffBaseRef: mergeBaseRef,
|
|
42077
|
-
changedFiles: splitNullSeparated(diff.stdout),
|
|
42086
|
+
changedFiles: yield* mergeUntracked(directory, splitNullSeparated(diff.stdout), includeUntracked),
|
|
42078
42087
|
isCurrentChanges: false
|
|
42079
42088
|
};
|
|
42080
42089
|
}).pipe(withSpan("Git.diffSelection")),
|
|
@@ -42120,7 +42129,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42120
42129
|
stdout: result.stdout
|
|
42121
42130
|
};
|
|
42122
42131
|
}).pipe(withSpan("Git.grep")),
|
|
42123
|
-
changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
|
|
42132
|
+
changedLineRanges: ({ directory, baseRef, cached, files, includeUntracked = false }) => gen(function* () {
|
|
42124
42133
|
if (files.length === 0) return [];
|
|
42125
42134
|
if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
|
|
42126
42135
|
const result = yield* runGit(directory, [
|
|
@@ -42134,7 +42143,14 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42134
42143
|
...files
|
|
42135
42144
|
]);
|
|
42136
42145
|
if (result.status !== 0) return null;
|
|
42137
|
-
|
|
42146
|
+
const changedLineRanges = parseChangedLineRanges(result.stdout);
|
|
42147
|
+
if (cached || !includeUntracked) return changedLineRanges;
|
|
42148
|
+
const untrackedFilePaths = yield* listUntrackedFilePaths(directory, files);
|
|
42149
|
+
if (untrackedFilePaths === null) return changedLineRanges;
|
|
42150
|
+
return [...changedLineRanges, ...untrackedFilePaths.map((file) => ({
|
|
42151
|
+
file,
|
|
42152
|
+
ranges: [[1, UNTRACKED_FILE_LAST_LINE]]
|
|
42153
|
+
}))];
|
|
42138
42154
|
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
42139
42155
|
});
|
|
42140
42156
|
})).pipe(provide$2(layer$3.pipe(provide$2(mergeAll$1(layer$2, layer$1)))));
|
|
@@ -43643,11 +43659,11 @@ const buildRelatedLocations = (labels, filePath) => {
|
|
|
43643
43659
|
}
|
|
43644
43660
|
return related;
|
|
43645
43661
|
};
|
|
43646
|
-
const
|
|
43647
|
-
|
|
43648
|
-
|
|
43649
|
-
|
|
43650
|
-
|
|
43662
|
+
const isOxlintSpan = (value) => isRecord$1(value) && typeof value.offset === "number" && typeof value.length === "number" && typeof value.line === "number" && typeof value.column === "number";
|
|
43663
|
+
const isOxlintLabel = (value) => isRecord$1(value) && isOxlintSpan(value.span);
|
|
43664
|
+
const isMappableOxlintDiagnostic = (value) => isRecord$1(value) && typeof value.code === "string" && value.code.length > 0 && typeof value.filename === "string" && value.filename.length > 0 && (value.severity === "warning" || value.severity === "error") && Array.isArray(value.labels) && value.labels.every(isOxlintLabel);
|
|
43665
|
+
const isEngineFailureDiagnostic = (value) => !isRecord$1(value) || typeof value.filename !== "string" || value.filename.length === 0;
|
|
43666
|
+
const isOxlintOutput = (value) => isRecord$1(value) && Array.isArray(value.diagnostics);
|
|
43651
43667
|
/**
|
|
43652
43668
|
* Parses one oxlint subprocess's stdout into a flat `Diagnostic[]`.
|
|
43653
43669
|
* Tolerates the leading-notice-line shape oxlint sometimes prints
|
|
@@ -43666,6 +43682,8 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
43666
43682
|
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
43667
43683
|
}
|
|
43668
43684
|
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
43685
|
+
const engineFailureDiagnostic = parsed.diagnostics.find(isEngineFailureDiagnostic);
|
|
43686
|
+
if (engineFailureDiagnostic !== void 0) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: JSON.stringify(engineFailureDiagnostic).slice(0, 600) }) });
|
|
43669
43687
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
43670
43688
|
const sourceBufferCache = /* @__PURE__ */ new Map();
|
|
43671
43689
|
const sourceLinesCache = /* @__PURE__ */ new Map();
|
|
@@ -43700,7 +43718,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
43700
43718
|
minifiedFileCache.set(absolutePath, minified);
|
|
43701
43719
|
return minified;
|
|
43702
43720
|
};
|
|
43703
|
-
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => diagnostic
|
|
43721
|
+
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
|
|
43704
43722
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
43705
43723
|
const primaryLabel = diagnostic.labels[0];
|
|
43706
43724
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
@@ -46266,10 +46284,11 @@ const computeDiagnosticDelta = (input) => {
|
|
|
46266
46284
|
* render diff-base mistakes (`GitBaseBranchInvalid` /
|
|
46267
46285
|
* `GitBaseBranchMissing`) as clean user errors instead of crashes.
|
|
46268
46286
|
*/
|
|
46269
|
-
const getDiffInfo = (directory, explicitBaseBranch) => runPromise(gen(function* () {
|
|
46287
|
+
const getDiffInfo = (directory, explicitBaseBranch, includeUntracked) => runPromise(gen(function* () {
|
|
46270
46288
|
const selection = yield* (yield* Git).diffSelection({
|
|
46271
46289
|
directory,
|
|
46272
|
-
explicitBaseBranch
|
|
46290
|
+
explicitBaseBranch,
|
|
46291
|
+
includeUntracked
|
|
46273
46292
|
});
|
|
46274
46293
|
if (selection === null) return null;
|
|
46275
46294
|
return {
|
|
@@ -46605,6 +46624,43 @@ const isNonInteractiveEnvironment = () => NON_INTERACTIVE_ENVIRONMENT_VARIABLES.
|
|
|
46605
46624
|
//#region src/cli/utils/constants.ts
|
|
46606
46625
|
const REACT_DOCTOR_CONFIG_PROJECT_NAME = "react-doctor";
|
|
46607
46626
|
const STAGED_FILES_TEMP_DIR_PREFIX = "react-doctor-staged-";
|
|
46627
|
+
const STAGED_SNAPSHOT_ADDITIONAL_CONFIG_FILENAMES = [
|
|
46628
|
+
".babelrc",
|
|
46629
|
+
".babelrc.json",
|
|
46630
|
+
".eslintignore",
|
|
46631
|
+
".eslintrc.json",
|
|
46632
|
+
".oxlintignore",
|
|
46633
|
+
"app.config.cjs",
|
|
46634
|
+
"app.config.js",
|
|
46635
|
+
"app.config.json",
|
|
46636
|
+
"app.config.mjs",
|
|
46637
|
+
"app.config.ts",
|
|
46638
|
+
"app.json",
|
|
46639
|
+
"babel.config.cjs",
|
|
46640
|
+
"babel.config.js",
|
|
46641
|
+
"babel.config.json",
|
|
46642
|
+
"babel.config.mjs",
|
|
46643
|
+
"eslint.config.cjs",
|
|
46644
|
+
"eslint.config.cts",
|
|
46645
|
+
"eslint.config.js",
|
|
46646
|
+
"eslint.config.mjs",
|
|
46647
|
+
"eslint.config.mts",
|
|
46648
|
+
"eslint.config.ts",
|
|
46649
|
+
"next.config.cjs",
|
|
46650
|
+
"next.config.js",
|
|
46651
|
+
"next.config.mjs",
|
|
46652
|
+
"next.config.ts",
|
|
46653
|
+
"oxlint.config.json",
|
|
46654
|
+
"react-doctor.config.json",
|
|
46655
|
+
"vite.config.cjs",
|
|
46656
|
+
"vite.config.cts",
|
|
46657
|
+
"vite.config.js",
|
|
46658
|
+
"vite.config.mjs",
|
|
46659
|
+
"vite.config.mts",
|
|
46660
|
+
"vite.config.ts",
|
|
46661
|
+
"vitest.config.js",
|
|
46662
|
+
"vitest.config.ts"
|
|
46663
|
+
];
|
|
46608
46664
|
const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
|
|
46609
46665
|
const SCAN_RESULT_CACHE_FILENAME = "scan-cache.json";
|
|
46610
46666
|
const RUN_GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
@@ -46619,6 +46675,7 @@ const METRIC = {
|
|
|
46619
46675
|
cliInvoked: "cli.invoked",
|
|
46620
46676
|
cliError: "cli.error",
|
|
46621
46677
|
cliEnvironmentError: "cli.env_error",
|
|
46678
|
+
stagedSnapshotDivergence: "staged.snapshot_divergence",
|
|
46622
46679
|
projectDetected: "project.detected",
|
|
46623
46680
|
projectPathSelected: "project.path_selected",
|
|
46624
46681
|
projectConfigSelected: "project.config_selected",
|
|
@@ -46699,7 +46756,7 @@ const makeNoopConsole = () => ({
|
|
|
46699
46756
|
});
|
|
46700
46757
|
//#endregion
|
|
46701
46758
|
//#region src/cli/utils/version.ts
|
|
46702
|
-
const VERSION = "0.7.6-dev.
|
|
46759
|
+
const VERSION = "0.7.6-dev.0d11bd5";
|
|
46703
46760
|
//#endregion
|
|
46704
46761
|
//#region src/cli/utils/json-mode.ts
|
|
46705
46762
|
let context = null;
|
|
@@ -47068,13 +47125,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
47068
47125
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
47069
47126
|
* standard `SENTRY_RELEASE` override.
|
|
47070
47127
|
*/
|
|
47071
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.6-dev.
|
|
47128
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.6-dev.0d11bd5`;
|
|
47072
47129
|
/**
|
|
47073
47130
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
47074
47131
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
47075
47132
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
47076
47133
|
*/
|
|
47077
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.6-dev.
|
|
47134
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.6-dev.0d11bd5") ? "development" : "production");
|
|
47078
47135
|
/**
|
|
47079
47136
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
47080
47137
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -47266,7 +47323,7 @@ const NON_BLOCKING_REACT_DOCTOR_COMMAND = [
|
|
|
47266
47323
|
"fi"
|
|
47267
47324
|
].join(" ");
|
|
47268
47325
|
const PACKAGE_JSON_FILE_NAME = "package.json";
|
|
47269
|
-
const
|
|
47326
|
+
const runGitRaw = (projectRoot, args) => {
|
|
47270
47327
|
try {
|
|
47271
47328
|
return execFileSync("git", [...args], {
|
|
47272
47329
|
cwd: projectRoot,
|
|
@@ -47277,11 +47334,12 @@ const runGit = (projectRoot, args) => {
|
|
|
47277
47334
|
"ignore"
|
|
47278
47335
|
],
|
|
47279
47336
|
maxBuffer: RUN_GIT_MAX_BUFFER_BYTES
|
|
47280
|
-
})
|
|
47337
|
+
});
|
|
47281
47338
|
} catch {
|
|
47282
47339
|
return null;
|
|
47283
47340
|
}
|
|
47284
47341
|
};
|
|
47342
|
+
const runGit = (projectRoot, args) => runGitRaw(projectRoot, args)?.trim() ?? null;
|
|
47285
47343
|
const resolveGitPath = (baseDirectory, value) => Path.isAbsolute(value) ? value : Path.resolve(baseDirectory, value);
|
|
47286
47344
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
47287
47345
|
const getPackageJsonPath = (projectRoot) => Path.join(projectRoot, PACKAGE_JSON_FILE_NAME);
|
|
@@ -59152,6 +59210,17 @@ const buildRuleBlastRadii = (diagnostics) => buildSortedRuleGroups(diagnostics).
|
|
|
59152
59210
|
})).toSorted((left, right) => right.fileCount - left.fileCount);
|
|
59153
59211
|
const findMigrationScaleBuckets = (diagnostics) => buildRuleBlastRadii(diagnostics).filter((bucket) => bucket.fileCount >= 40);
|
|
59154
59212
|
//#endregion
|
|
59213
|
+
//#region src/cli/utils/has-lint-hard-failure.ts
|
|
59214
|
+
const hasLintHardFailure = (result) => result.skippedChecks.includes("lint");
|
|
59215
|
+
//#endregion
|
|
59216
|
+
//#region src/cli/utils/is-inspect-result-complete.ts
|
|
59217
|
+
const isInspectResultComplete = (result) => isScanComplete({
|
|
59218
|
+
analyzedFileCount: result.analyzedFiles?.length,
|
|
59219
|
+
scannedFileCount: result.scannedFileCount,
|
|
59220
|
+
skippedCheckCount: result.skippedChecks.length,
|
|
59221
|
+
skippedCheckReasonCount: Object.keys(result.skippedCheckReasons ?? {}).length
|
|
59222
|
+
});
|
|
59223
|
+
//#endregion
|
|
59155
59224
|
//#region src/cli/utils/scan-result-cache.ts
|
|
59156
59225
|
const CACHE_DISABLED_VALUES = new Set(["1", "true"]);
|
|
59157
59226
|
const TOOLCHAIN_PACKAGE_SPECIFIERS = [
|
|
@@ -59557,15 +59626,12 @@ const buildOutcomeAttributes = (input) => {
|
|
|
59557
59626
|
const summary = summarizeDiagnostics(result.diagnostics);
|
|
59558
59627
|
const blockingLevel = resolveTelemetryBlocking(input.userConfig);
|
|
59559
59628
|
const gateDiagnostics = filterDiagnosticsForSurface(result.diagnostics, "ciFailure", input.userConfig);
|
|
59629
|
+
const complete = isInspectResultComplete(result);
|
|
59630
|
+
const didLintHardFail = hasLintHardFailure(result);
|
|
59631
|
+
const failsOnHardFailure = didLintHardFail && blockingLevel !== "none";
|
|
59560
59632
|
const wouldBlock = !input.scoreOnly && !input.gateExempt && shouldBlockCi(gateDiagnostics, blockingLevel);
|
|
59561
|
-
const complete = isScanComplete({
|
|
59562
|
-
analyzedFileCount: result.analyzedFiles?.length,
|
|
59563
|
-
scannedFileCount: result.scannedFileCount,
|
|
59564
|
-
skippedCheckCount: result.skippedChecks.length,
|
|
59565
|
-
skippedCheckReasonCount: Object.keys(result.skippedCheckReasons ?? {}).length
|
|
59566
|
-
});
|
|
59567
59633
|
const isClean = result.diagnostics.length === 0 && complete;
|
|
59568
|
-
const outcome = wouldBlock ? "blocked" : isClean ? "clean" : "ok";
|
|
59634
|
+
const outcome = didLintHardFail ? "error" : wouldBlock ? "blocked" : isClean ? "clean" : "ok";
|
|
59569
59635
|
const firings = summarizeRuleFirings(result.diagnostics);
|
|
59570
59636
|
const countByRule = /* @__PURE__ */ new Map();
|
|
59571
59637
|
const countByCategory = /* @__PURE__ */ new Map();
|
|
@@ -59610,7 +59676,7 @@ const buildOutcomeAttributes = (input) => {
|
|
|
59610
59676
|
const attributes = {
|
|
59611
59677
|
...withNamespace("outcome", {
|
|
59612
59678
|
status: outcome,
|
|
59613
|
-
exitCode: wouldBlock ? 1 : 0,
|
|
59679
|
+
exitCode: failsOnHardFailure || wouldBlock ? 1 : 0,
|
|
59614
59680
|
wouldBlock,
|
|
59615
59681
|
blocking: blockingLevel,
|
|
59616
59682
|
clean: isClean,
|
|
@@ -63792,10 +63858,15 @@ const warnIfAiTrainingEnvironment = () => {
|
|
|
63792
63858
|
//#region src/cli/utils/validate-mode-flags.ts
|
|
63793
63859
|
const usedDiffAlias = (flags) => flags.diff !== void 0 && flags.diff !== false && flags.diff !== "false" && flags.diff !== "";
|
|
63794
63860
|
const usedScope = (flags) => typeof flags.scope === "string" && flags.scope.length > 0;
|
|
63861
|
+
const validateIncludeUntrackedScope = (includeUntracked, scope) => {
|
|
63862
|
+
if (!includeUntracked || scope !== void 0 && scope !== "full") return;
|
|
63863
|
+
throw new CliInputError("--include-untracked requires the files, changed, or lines scope (via --scope, --diff, or config).");
|
|
63864
|
+
};
|
|
63795
63865
|
const validateModeFlags = (flags) => {
|
|
63796
63866
|
if (usedScope(flags) && usedDiffAlias(flags)) throw new CliInputError("Cannot combine --scope and --diff; --diff is the deprecated alias.");
|
|
63797
63867
|
if (flags.staged && usedDiffAlias(flags)) throw new CliInputError("Cannot combine --staged and --diff; pick one mode.");
|
|
63798
63868
|
if (flags.staged && (flags.scope === "full" || flags.scope === "changed")) throw new CliInputError(`Cannot combine --staged with --scope ${flags.scope}; use --scope files or --scope lines, or drop --scope.`);
|
|
63869
|
+
if (flags.includeUntracked && flags.staged) throw new CliInputError("Cannot combine --include-untracked with --staged; the git index never holds untracked files.");
|
|
63799
63870
|
if (flags.score && flags.json) throw new CliInputError("Cannot combine --score and --json; pick one output mode.");
|
|
63800
63871
|
if (flags.score && flags.telemetry === false) throw new CliInputError("Cannot combine --score with --no-telemetry; --score prints the score that --no-telemetry disables.");
|
|
63801
63872
|
if (flags.debug && (flags.score === false || flags.telemetry === false)) {
|
|
@@ -63804,6 +63875,39 @@ const validateModeFlags = (flags) => {
|
|
|
63804
63875
|
}
|
|
63805
63876
|
};
|
|
63806
63877
|
//#endregion
|
|
63878
|
+
//#region src/cli/utils/parse-staged-snapshot-divergences.ts
|
|
63879
|
+
const SNAPSHOT_CONFIG_FILENAMES = new Set([...STAGED_FILES_PROJECT_CONFIG_FILENAMES, ...STAGED_SNAPSHOT_ADDITIONAL_CONFIG_FILENAMES]);
|
|
63880
|
+
const parseStagedSnapshotDivergences = (statusOutput) => {
|
|
63881
|
+
const divergentConfigFiles = /* @__PURE__ */ new Set();
|
|
63882
|
+
const statusEntries = statusOutput.split("\0").filter((entry) => entry.length > 0);
|
|
63883
|
+
for (let entryIndex = 0; entryIndex < statusEntries.length; entryIndex += 1) {
|
|
63884
|
+
const entry = statusEntries[entryIndex];
|
|
63885
|
+
const [indexStatus, worktreeStatus] = entry;
|
|
63886
|
+
const filePath = entry.slice(3);
|
|
63887
|
+
const hasRenameOrCopySource = indexStatus === "R" || indexStatus === "C" || worktreeStatus === "R" || worktreeStatus === "C";
|
|
63888
|
+
const sourcePath = hasRenameOrCopySource ? statusEntries[entryIndex + 1] : void 0;
|
|
63889
|
+
const worktreePaths = worktreeStatus === "R" || worktreeStatus === "C" ? [filePath, sourcePath] : [filePath];
|
|
63890
|
+
if (worktreeStatus !== " ") {
|
|
63891
|
+
for (const worktreePath of worktreePaths) if (worktreePath && SNAPSHOT_CONFIG_FILENAMES.has(Path.basename(worktreePath))) divergentConfigFiles.add(worktreePath);
|
|
63892
|
+
}
|
|
63893
|
+
if (hasRenameOrCopySource) entryIndex += 1;
|
|
63894
|
+
}
|
|
63895
|
+
return [...divergentConfigFiles].sort();
|
|
63896
|
+
};
|
|
63897
|
+
//#endregion
|
|
63898
|
+
//#region src/cli/utils/find-staged-snapshot-divergences.ts
|
|
63899
|
+
const findStagedSnapshotDivergences = (directory) => {
|
|
63900
|
+
const statusOutput = runGitRaw(directory, [
|
|
63901
|
+
"status",
|
|
63902
|
+
"--porcelain=v1",
|
|
63903
|
+
"-z",
|
|
63904
|
+
"--untracked-files=all",
|
|
63905
|
+
"--ignored=matching"
|
|
63906
|
+
]);
|
|
63907
|
+
if (statusOutput === null) return null;
|
|
63908
|
+
return parseStagedSnapshotDivergences(statusOutput);
|
|
63909
|
+
};
|
|
63910
|
+
//#endregion
|
|
63807
63911
|
//#region src/cli/commands/inspect.ts
|
|
63808
63912
|
const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
|
|
63809
63913
|
if (categoryFilters.size === 0) return [...completedScans];
|
|
@@ -63818,9 +63922,14 @@ const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
|
|
|
63818
63922
|
/**
|
|
63819
63923
|
* Post-scan finalization shared by the staged-arm and project-loop
|
|
63820
63924
|
* paths of `inspectAction`: emit the JSON report (when in JSON mode)
|
|
63821
|
-
* and set `process.exitCode = 1` when
|
|
63822
|
-
*
|
|
63823
|
-
*
|
|
63925
|
+
* and set `process.exitCode = 1` when any scan's lint pass hard-failed
|
|
63926
|
+
* (an engine/plugin/binding failure destroys the findings, so success
|
|
63927
|
+
* would be a false clean) or a diagnostic at or above the `--blocking`
|
|
63928
|
+
* threshold (default `"error"`) reaches the `ciFailure` surface.
|
|
63929
|
+
* `--blocking none` keeps the scan advisory (always exits 0), and
|
|
63930
|
+
* fail-open degradations — `--no-lint`, `--max-duration` truncation,
|
|
63931
|
+
* supply-chain/security skips — stay advisory too, surfaced through
|
|
63932
|
+
* `complete: false` in the JSON report.
|
|
63824
63933
|
*/
|
|
63825
63934
|
const finalizeScans = (input) => {
|
|
63826
63935
|
const baselineDeltas = input.completedScans.flatMap((scan) => scan.result.baselineDelta ? [scan.result.baselineDelta] : []);
|
|
@@ -63850,8 +63959,13 @@ const finalizeScans = (input) => {
|
|
|
63850
63959
|
baselineDegraded
|
|
63851
63960
|
}));
|
|
63852
63961
|
}
|
|
63962
|
+
const blockingLevel = resolveBlockingLevel(input.flags, input.userConfig);
|
|
63963
|
+
if (input.completedScans.some(({ result }) => hasLintHardFailure(result)) && blockingLevel !== "none") {
|
|
63964
|
+
process.exitCode = 1;
|
|
63965
|
+
return;
|
|
63966
|
+
}
|
|
63853
63967
|
if (input.isScoreOnly || baselineDegraded) return;
|
|
63854
|
-
if (shouldBlockCi(filterScansForSurface(input.completedScans, "ciFailure"),
|
|
63968
|
+
if (shouldBlockCi(filterScansForSurface(input.completedScans, "ciFailure"), blockingLevel)) process.exitCode = 1;
|
|
63855
63969
|
};
|
|
63856
63970
|
const buildChangedFilesDiffInfo = (changedFiles) => ({
|
|
63857
63971
|
currentBranch: process.env.GITHUB_HEAD_REF?.trim() || null,
|
|
@@ -63889,6 +64003,7 @@ const inspectAction = async (directory, flags) => {
|
|
|
63889
64003
|
recordCount(METRIC.cliInvoked, 1, { command: "inspect" });
|
|
63890
64004
|
try {
|
|
63891
64005
|
validateModeFlags(flags);
|
|
64006
|
+
if (flags.staged) setJsonReportMode("staged");
|
|
63892
64007
|
await maybeMigrateLegacyConfig(requestedDirectory, {
|
|
63893
64008
|
isQuiet,
|
|
63894
64009
|
isStaged: Boolean(flags.staged)
|
|
@@ -63904,6 +64019,14 @@ const inspectAction = async (directory, flags) => {
|
|
|
63904
64019
|
cliLogger.dim(`Redirected to ${highlighter.info(toRelativePath(resolvedDirectory, requestedDirectory))} via react-doctor config "rootDir".`);
|
|
63905
64020
|
cliLogger.break();
|
|
63906
64021
|
}
|
|
64022
|
+
if (flags.staged) {
|
|
64023
|
+
const divergentConfigFiles = findStagedSnapshotDivergences(resolvedDirectory);
|
|
64024
|
+
if (divergentConfigFiles === null) throw new CliInputError("Could not verify that staged configuration matches the worktree. Run the command from a Git worktree with Git available.");
|
|
64025
|
+
if (divergentConfigFiles.length > 0) {
|
|
64026
|
+
recordCount(METRIC.stagedSnapshotDivergence, 1, { divergentInputCount: divergentConfigFiles.length });
|
|
64027
|
+
throw new CliInputError(`Cannot scan staged files while configuration differs between the index and worktree: ${divergentConfigFiles.join(", ")}. Stage or restore those files, then rerun react-doctor --staged.`);
|
|
64028
|
+
}
|
|
64029
|
+
}
|
|
63907
64030
|
const explainArgument = flags.explain;
|
|
63908
64031
|
if (explainArgument !== void 0) {
|
|
63909
64032
|
await runExplain(explainArgument, {
|
|
@@ -63924,7 +64047,6 @@ const inspectAction = async (directory, flags) => {
|
|
|
63924
64047
|
json: flags.json
|
|
63925
64048
|
});
|
|
63926
64049
|
if (flags.staged) {
|
|
63927
|
-
setJsonReportMode("staged");
|
|
63928
64050
|
const stagedFiles = await getStagedSourceFiles(resolvedDirectory);
|
|
63929
64051
|
if (stagedFiles.length === 0) {
|
|
63930
64052
|
if (isJsonMode) writeJsonReport(buildJsonReport({
|
|
@@ -64005,13 +64127,15 @@ const inspectAction = async (directory, flags) => {
|
|
|
64005
64127
|
const projectDirectories = await selectProjects(resolvedDirectory, flags.project, skipPrompts, userConfig?.projects);
|
|
64006
64128
|
const changedFilesDiffInfo = flags.changedFilesFrom ? buildChangedFilesDiffInfo(readChangedFilesFrom(Path.resolve(flags.changedFilesFrom))) : null;
|
|
64007
64129
|
const requestedScope = resolveScope(flags, userConfig);
|
|
64130
|
+
const includeUntracked = flags.includeUntracked ?? false;
|
|
64008
64131
|
const scopeRequest = requestedScope.scope === void 0 && changedFilesDiffInfo !== null ? {
|
|
64009
64132
|
...requestedScope,
|
|
64010
64133
|
scope: "changed"
|
|
64011
64134
|
} : requestedScope;
|
|
64135
|
+
validateIncludeUntrackedScope(includeUntracked, scopeRequest.scope);
|
|
64012
64136
|
const wantsDiffMode = scopeRequest.scope !== void 0 && scopeRequest.scope !== "full";
|
|
64013
64137
|
const shouldDetectDiff = changedFilesDiffInfo === null && (wantsDiffMode || scopeRequest.scope === void 0 && !skipPrompts && !isQuiet);
|
|
64014
|
-
const diffInfo = changedFilesDiffInfo ?? (shouldDetectDiff ? await getDiffInfo(resolvedDirectory, scopeRequest.base) : null);
|
|
64138
|
+
const diffInfo = changedFilesDiffInfo ?? (shouldDetectDiff ? await getDiffInfo(resolvedDirectory, scopeRequest.base, includeUntracked) : null);
|
|
64015
64139
|
const scope = await finalizeScope({
|
|
64016
64140
|
requested: scopeRequest,
|
|
64017
64141
|
diffInfo,
|
|
@@ -64025,7 +64149,8 @@ const inspectAction = async (directory, flags) => {
|
|
|
64025
64149
|
const changedLineRanges = scope === "lines" && diffInfo !== null && (diffInfo.isCurrentChanges || linesBaseRef !== null) && diffInfo !== null ? await getChangedLineRanges({
|
|
64026
64150
|
directory: resolvedDirectory,
|
|
64027
64151
|
baseRef: linesBaseRef ?? void 0,
|
|
64028
|
-
files: [...diffInfo.changedFiles]
|
|
64152
|
+
files: [...diffInfo.changedFiles],
|
|
64153
|
+
includeUntracked
|
|
64029
64154
|
}) : null;
|
|
64030
64155
|
if (scope === "lines" && changedLineRanges === null && !isQuiet) {
|
|
64031
64156
|
cliLogger.warn("Could not determine changed lines (no base ref or git diff failed); reporting all issues in changed files.");
|
|
@@ -64958,6 +65083,7 @@ const ROOT_FLAG_SPEC = {
|
|
|
64958
65083
|
"--dead-code",
|
|
64959
65084
|
"--debug",
|
|
64960
65085
|
"--help",
|
|
65086
|
+
"--include-untracked",
|
|
64961
65087
|
"--json",
|
|
64962
65088
|
"--json-compact",
|
|
64963
65089
|
"--lint",
|
|
@@ -65221,7 +65347,7 @@ ${highlighter.dim("Learn more:")}
|
|
|
65221
65347
|
${highlighter.info(CI_URL)}
|
|
65222
65348
|
`;
|
|
65223
65349
|
const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
|
|
65224
|
-
const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much supported JS/TS source to scan/report: full (default), files, changed (only new issues vs base), or lines (issues whose source spans touch changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks").option("--max-duration <seconds>", "scan time budget for the whole run, shared across workspace projects: past it, remaining lint batches and dead-code are skipped and partial results are reported (skipped files are listed in the JSON report)").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
|
|
65350
|
+
const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much supported JS/TS source to scan/report: full (default), files, changed (only new issues vs base), or lines (issues whose source spans touch changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").option("--include-untracked", "with --scope files/changed/lines, also scan ordinary untracked files (respects .gitignore)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks").option("--max-duration <seconds>", "scan time budget for the whole run, shared across workspace projects: past it, remaining lint batches and dead-code are skipped and partial results are reported (skipped files are listed in the JSON report)").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
|
|
65225
65351
|
program.action(inspectAction);
|
|
65226
65352
|
program.command("why <location>").description("Explain why a rule fired (or why a suppression didn't apply) at a file:line").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple)").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").action((location, options) => whyAction(location, options));
|
|
65227
65353
|
program.command("install").alias("setup").description("Install the react-doctor skill into your coding agents and optional git hook").option("-y, --yes", "skip prompts, install for all detected agents").option("--dry-run", "show what would be installed without writing files").option("--agent-hooks", "install native non-blocking agent hooks for Claude Code and Cursor").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderInstallHelpEpilog).action(installAction);
|
|
@@ -65271,4 +65397,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
65271
65397
|
export {};
|
|
65272
65398
|
|
|
65273
65399
|
//# sourceMappingURL=cli.js.map
|
|
65274
|
-
//# debugId=
|
|
65400
|
+
//# debugId=58e54b02-7ba2-5f13-a535-98c236467a86
|
package/dist/index.d.ts
CHANGED
|
@@ -10256,7 +10256,7 @@ declare const buildJsonReport: (input: BuildJsonReportInput) => JsonReportV3$1;
|
|
|
10256
10256
|
* render diff-base mistakes (`GitBaseBranchInvalid` /
|
|
10257
10257
|
* `GitBaseBranchMissing`) as clean user errors instead of crashes.
|
|
10258
10258
|
*/
|
|
10259
|
-
declare const getDiffInfo: (directory: string, explicitBaseBranch?: string) => Promise<DiffInfo | null>;
|
|
10259
|
+
declare const getDiffInfo: (directory: string, explicitBaseBranch?: string, includeUntracked?: boolean) => Promise<DiffInfo | null>;
|
|
10260
10260
|
declare const filterSourceFiles: (filePaths: string[]) => string[];
|
|
10261
10261
|
/**
|
|
10262
10262
|
* Programmatic façade over `Git.changedLineRanges` (the `lines` scope). Diffs
|
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]="2aa6fc3c-96ab-5a4c-8a52-e74d27fb4b5a")}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 reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, FRAMEWORK_TOKENS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
|
|
@@ -38579,6 +38579,7 @@ const parseGithubViewerPermission = (stdout) => {
|
|
|
38579
38579
|
return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
|
|
38580
38580
|
};
|
|
38581
38581
|
const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
|
|
38582
|
+
const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
|
|
38582
38583
|
/**
|
|
38583
38584
|
* `Git` wraps every `git`-via-subprocess call react-doctor makes
|
|
38584
38585
|
* behind a `Context.Service`. The production layer (`layerNode`)
|
|
@@ -38649,6 +38650,14 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38649
38650
|
args,
|
|
38650
38651
|
directory
|
|
38651
38652
|
});
|
|
38653
|
+
const listUntrackedFilePaths = (directory, includePaths = []) => runGit(directory, [
|
|
38654
|
+
"ls-files",
|
|
38655
|
+
"-z",
|
|
38656
|
+
"--others",
|
|
38657
|
+
"--exclude-standard",
|
|
38658
|
+
...includePaths.length > 0 ? ["--", ...includePaths] : []
|
|
38659
|
+
]).pipe(map$3((result) => result.status === 0 ? splitNullSeparated(result.stdout) : null));
|
|
38660
|
+
const mergeUntracked = (directory, trackedFilePaths, includeUntracked) => includeUntracked ? listUntrackedFilePaths(directory).pipe(map$3((untracked) => untracked === null ? trackedFilePaths : [...new Set([...trackedFilePaths, ...untracked])])) : succeed$2(trackedFilePaths);
|
|
38652
38661
|
const currentBranch = (directory) => runGit(directory, [
|
|
38653
38662
|
"rev-parse",
|
|
38654
38663
|
"--abbrev-ref",
|
|
@@ -38771,7 +38780,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38771
38780
|
githubViewerPermission,
|
|
38772
38781
|
branchExists,
|
|
38773
38782
|
mergeBase,
|
|
38774
|
-
diffSelection: ({ directory, explicitBaseBranch }) => gen(function* () {
|
|
38783
|
+
diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
|
|
38775
38784
|
if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
|
|
38776
38785
|
if (explicitBaseBranch !== void 0) {
|
|
38777
38786
|
const range = parseGitDiffRange(explicitBaseBranch);
|
|
@@ -38800,7 +38809,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38800
38809
|
"HEAD"
|
|
38801
38810
|
]);
|
|
38802
38811
|
if (uncommitted.status !== 0) return null;
|
|
38803
|
-
const files = splitNullSeparated(uncommitted.stdout);
|
|
38812
|
+
const files = yield* mergeUntracked(directory, splitNullSeparated(uncommitted.stdout), includeUntracked);
|
|
38804
38813
|
if (files.length === 0) return null;
|
|
38805
38814
|
return {
|
|
38806
38815
|
currentBranch: resolvedCurrentBranch,
|
|
@@ -38830,7 +38839,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38830
38839
|
currentBranch: resolvedCurrentBranch,
|
|
38831
38840
|
baseBranch,
|
|
38832
38841
|
diffBaseRef: mergeBaseRef,
|
|
38833
|
-
changedFiles: splitNullSeparated(diff.stdout),
|
|
38842
|
+
changedFiles: yield* mergeUntracked(directory, splitNullSeparated(diff.stdout), includeUntracked),
|
|
38834
38843
|
isCurrentChanges: false
|
|
38835
38844
|
};
|
|
38836
38845
|
}).pipe(withSpan("Git.diffSelection")),
|
|
@@ -38876,7 +38885,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38876
38885
|
stdout: result.stdout
|
|
38877
38886
|
};
|
|
38878
38887
|
}).pipe(withSpan("Git.grep")),
|
|
38879
|
-
changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
|
|
38888
|
+
changedLineRanges: ({ directory, baseRef, cached, files, includeUntracked = false }) => gen(function* () {
|
|
38880
38889
|
if (files.length === 0) return [];
|
|
38881
38890
|
if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
|
|
38882
38891
|
const result = yield* runGit(directory, [
|
|
@@ -38890,7 +38899,14 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38890
38899
|
...files
|
|
38891
38900
|
]);
|
|
38892
38901
|
if (result.status !== 0) return null;
|
|
38893
|
-
|
|
38902
|
+
const changedLineRanges = parseChangedLineRanges(result.stdout);
|
|
38903
|
+
if (cached || !includeUntracked) return changedLineRanges;
|
|
38904
|
+
const untrackedFilePaths = yield* listUntrackedFilePaths(directory, files);
|
|
38905
|
+
if (untrackedFilePaths === null) return changedLineRanges;
|
|
38906
|
+
return [...changedLineRanges, ...untrackedFilePaths.map((file) => ({
|
|
38907
|
+
file,
|
|
38908
|
+
ranges: [[1, UNTRACKED_FILE_LAST_LINE]]
|
|
38909
|
+
}))];
|
|
38894
38910
|
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
38895
38911
|
});
|
|
38896
38912
|
})).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
|
|
@@ -40399,11 +40415,11 @@ const buildRelatedLocations = (labels, filePath) => {
|
|
|
40399
40415
|
}
|
|
40400
40416
|
return related;
|
|
40401
40417
|
};
|
|
40402
|
-
const
|
|
40403
|
-
|
|
40404
|
-
|
|
40405
|
-
|
|
40406
|
-
|
|
40418
|
+
const isOxlintSpan = (value) => isRecord(value) && typeof value.offset === "number" && typeof value.length === "number" && typeof value.line === "number" && typeof value.column === "number";
|
|
40419
|
+
const isOxlintLabel = (value) => isRecord(value) && isOxlintSpan(value.span);
|
|
40420
|
+
const isMappableOxlintDiagnostic = (value) => isRecord(value) && typeof value.code === "string" && value.code.length > 0 && typeof value.filename === "string" && value.filename.length > 0 && (value.severity === "warning" || value.severity === "error") && Array.isArray(value.labels) && value.labels.every(isOxlintLabel);
|
|
40421
|
+
const isEngineFailureDiagnostic = (value) => !isRecord(value) || typeof value.filename !== "string" || value.filename.length === 0;
|
|
40422
|
+
const isOxlintOutput = (value) => isRecord(value) && Array.isArray(value.diagnostics);
|
|
40407
40423
|
/**
|
|
40408
40424
|
* Parses one oxlint subprocess's stdout into a flat `Diagnostic[]`.
|
|
40409
40425
|
* Tolerates the leading-notice-line shape oxlint sometimes prints
|
|
@@ -40422,6 +40438,8 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
40422
40438
|
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
40423
40439
|
}
|
|
40424
40440
|
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
40441
|
+
const engineFailureDiagnostic = parsed.diagnostics.find(isEngineFailureDiagnostic);
|
|
40442
|
+
if (engineFailureDiagnostic !== void 0) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: JSON.stringify(engineFailureDiagnostic).slice(0, 600) }) });
|
|
40425
40443
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
40426
40444
|
const sourceBufferCache = /* @__PURE__ */ new Map();
|
|
40427
40445
|
const sourceLinesCache = /* @__PURE__ */ new Map();
|
|
@@ -40456,7 +40474,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
40456
40474
|
minifiedFileCache.set(absolutePath, minified);
|
|
40457
40475
|
return minified;
|
|
40458
40476
|
};
|
|
40459
|
-
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => diagnostic
|
|
40477
|
+
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
|
|
40460
40478
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
40461
40479
|
const primaryLabel = diagnostic.labels[0];
|
|
40462
40480
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
@@ -42948,10 +42966,11 @@ const buildSkippedChecks = (input) => {
|
|
|
42948
42966
|
* render diff-base mistakes (`GitBaseBranchInvalid` /
|
|
42949
42967
|
* `GitBaseBranchMissing`) as clean user errors instead of crashes.
|
|
42950
42968
|
*/
|
|
42951
|
-
const getDiffInfo = (directory, explicitBaseBranch) => runPromise(gen(function* () {
|
|
42969
|
+
const getDiffInfo = (directory, explicitBaseBranch, includeUntracked) => runPromise(gen(function* () {
|
|
42952
42970
|
const selection = yield* (yield* Git).diffSelection({
|
|
42953
42971
|
directory,
|
|
42954
|
-
explicitBaseBranch
|
|
42972
|
+
explicitBaseBranch,
|
|
42973
|
+
includeUntracked
|
|
42955
42974
|
});
|
|
42956
42975
|
if (selection === null) return null;
|
|
42957
42976
|
return {
|
|
@@ -43241,4 +43260,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
43241
43260
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
43242
43261
|
|
|
43243
43262
|
//# sourceMappingURL=index.js.map
|
|
43244
|
-
//# debugId=
|
|
43263
|
+
//# debugId=2aa6fc3c-96ab-5a4c-8a52-e74d27fb4b5a
|
package/dist/lsp.js
CHANGED
|
@@ -38557,6 +38557,7 @@ const parseGithubViewerPermission = (stdout) => {
|
|
|
38557
38557
|
return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
|
|
38558
38558
|
};
|
|
38559
38559
|
const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
|
|
38560
|
+
const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
|
|
38560
38561
|
/**
|
|
38561
38562
|
* `Git` wraps every `git`-via-subprocess call react-doctor makes
|
|
38562
38563
|
* behind a `Context.Service`. The production layer (`layerNode`)
|
|
@@ -38627,6 +38628,14 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38627
38628
|
args,
|
|
38628
38629
|
directory
|
|
38629
38630
|
});
|
|
38631
|
+
const listUntrackedFilePaths = (directory, includePaths = []) => runGit(directory, [
|
|
38632
|
+
"ls-files",
|
|
38633
|
+
"-z",
|
|
38634
|
+
"--others",
|
|
38635
|
+
"--exclude-standard",
|
|
38636
|
+
...includePaths.length > 0 ? ["--", ...includePaths] : []
|
|
38637
|
+
]).pipe(map$3((result) => result.status === 0 ? splitNullSeparated(result.stdout) : null));
|
|
38638
|
+
const mergeUntracked = (directory, trackedFilePaths, includeUntracked) => includeUntracked ? listUntrackedFilePaths(directory).pipe(map$3((untracked) => untracked === null ? trackedFilePaths : [...new Set([...trackedFilePaths, ...untracked])])) : succeed$2(trackedFilePaths);
|
|
38630
38639
|
const currentBranch = (directory) => runGit(directory, [
|
|
38631
38640
|
"rev-parse",
|
|
38632
38641
|
"--abbrev-ref",
|
|
@@ -38749,7 +38758,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38749
38758
|
githubViewerPermission,
|
|
38750
38759
|
branchExists,
|
|
38751
38760
|
mergeBase,
|
|
38752
|
-
diffSelection: ({ directory, explicitBaseBranch }) => gen(function* () {
|
|
38761
|
+
diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
|
|
38753
38762
|
if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
|
|
38754
38763
|
if (explicitBaseBranch !== void 0) {
|
|
38755
38764
|
const range = parseGitDiffRange(explicitBaseBranch);
|
|
@@ -38778,7 +38787,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38778
38787
|
"HEAD"
|
|
38779
38788
|
]);
|
|
38780
38789
|
if (uncommitted.status !== 0) return null;
|
|
38781
|
-
const files = splitNullSeparated(uncommitted.stdout);
|
|
38790
|
+
const files = yield* mergeUntracked(directory, splitNullSeparated(uncommitted.stdout), includeUntracked);
|
|
38782
38791
|
if (files.length === 0) return null;
|
|
38783
38792
|
return {
|
|
38784
38793
|
currentBranch: resolvedCurrentBranch,
|
|
@@ -38808,7 +38817,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38808
38817
|
currentBranch: resolvedCurrentBranch,
|
|
38809
38818
|
baseBranch,
|
|
38810
38819
|
diffBaseRef: mergeBaseRef,
|
|
38811
|
-
changedFiles: splitNullSeparated(diff.stdout),
|
|
38820
|
+
changedFiles: yield* mergeUntracked(directory, splitNullSeparated(diff.stdout), includeUntracked),
|
|
38812
38821
|
isCurrentChanges: false
|
|
38813
38822
|
};
|
|
38814
38823
|
}).pipe(withSpan("Git.diffSelection")),
|
|
@@ -38854,7 +38863,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38854
38863
|
stdout: result.stdout
|
|
38855
38864
|
};
|
|
38856
38865
|
}).pipe(withSpan("Git.grep")),
|
|
38857
|
-
changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
|
|
38866
|
+
changedLineRanges: ({ directory, baseRef, cached, files, includeUntracked = false }) => gen(function* () {
|
|
38858
38867
|
if (files.length === 0) return [];
|
|
38859
38868
|
if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
|
|
38860
38869
|
const result = yield* runGit(directory, [
|
|
@@ -38868,7 +38877,14 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38868
38877
|
...files
|
|
38869
38878
|
]);
|
|
38870
38879
|
if (result.status !== 0) return null;
|
|
38871
|
-
|
|
38880
|
+
const changedLineRanges = parseChangedLineRanges(result.stdout);
|
|
38881
|
+
if (cached || !includeUntracked) return changedLineRanges;
|
|
38882
|
+
const untrackedFilePaths = yield* listUntrackedFilePaths(directory, files);
|
|
38883
|
+
if (untrackedFilePaths === null) return changedLineRanges;
|
|
38884
|
+
return [...changedLineRanges, ...untrackedFilePaths.map((file) => ({
|
|
38885
|
+
file,
|
|
38886
|
+
ranges: [[1, UNTRACKED_FILE_LAST_LINE]]
|
|
38887
|
+
}))];
|
|
38872
38888
|
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
38873
38889
|
});
|
|
38874
38890
|
})).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
|
|
@@ -40377,11 +40393,11 @@ const buildRelatedLocations = (labels, filePath) => {
|
|
|
40377
40393
|
}
|
|
40378
40394
|
return related;
|
|
40379
40395
|
};
|
|
40380
|
-
const
|
|
40381
|
-
|
|
40382
|
-
|
|
40383
|
-
|
|
40384
|
-
|
|
40396
|
+
const isOxlintSpan = (value) => isRecord(value) && typeof value.offset === "number" && typeof value.length === "number" && typeof value.line === "number" && typeof value.column === "number";
|
|
40397
|
+
const isOxlintLabel = (value) => isRecord(value) && isOxlintSpan(value.span);
|
|
40398
|
+
const isMappableOxlintDiagnostic = (value) => isRecord(value) && typeof value.code === "string" && value.code.length > 0 && typeof value.filename === "string" && value.filename.length > 0 && (value.severity === "warning" || value.severity === "error") && Array.isArray(value.labels) && value.labels.every(isOxlintLabel);
|
|
40399
|
+
const isEngineFailureDiagnostic = (value) => !isRecord(value) || typeof value.filename !== "string" || value.filename.length === 0;
|
|
40400
|
+
const isOxlintOutput = (value) => isRecord(value) && Array.isArray(value.diagnostics);
|
|
40385
40401
|
/**
|
|
40386
40402
|
* Parses one oxlint subprocess's stdout into a flat `Diagnostic[]`.
|
|
40387
40403
|
* Tolerates the leading-notice-line shape oxlint sometimes prints
|
|
@@ -40400,6 +40416,8 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
40400
40416
|
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
40401
40417
|
}
|
|
40402
40418
|
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
40419
|
+
const engineFailureDiagnostic = parsed.diagnostics.find(isEngineFailureDiagnostic);
|
|
40420
|
+
if (engineFailureDiagnostic !== void 0) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: JSON.stringify(engineFailureDiagnostic).slice(0, 600) }) });
|
|
40403
40421
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
40404
40422
|
const sourceBufferCache = /* @__PURE__ */ new Map();
|
|
40405
40423
|
const sourceLinesCache = /* @__PURE__ */ new Map();
|
|
@@ -40434,7 +40452,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
40434
40452
|
minifiedFileCache.set(absolutePath, minified);
|
|
40435
40453
|
return minified;
|
|
40436
40454
|
};
|
|
40437
|
-
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => diagnostic
|
|
40455
|
+
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
|
|
40438
40456
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
40439
40457
|
const primaryLabel = diagnostic.labels[0];
|
|
40440
40458
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
@@ -44524,6 +44542,7 @@ const METRIC = {
|
|
|
44524
44542
|
cliInvoked: "cli.invoked",
|
|
44525
44543
|
cliError: "cli.error",
|
|
44526
44544
|
cliEnvironmentError: "cli.env_error",
|
|
44545
|
+
stagedSnapshotDivergence: "staged.snapshot_divergence",
|
|
44527
44546
|
projectDetected: "project.detected",
|
|
44528
44547
|
projectPathSelected: "project.path_selected",
|
|
44529
44548
|
projectConfigSelected: "project.config_selected",
|
|
@@ -44878,5 +44897,5 @@ const startLanguageServer = () => {
|
|
|
44878
44897
|
};
|
|
44879
44898
|
//#endregion
|
|
44880
44899
|
export { startLanguageServer };
|
|
44881
|
-
!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]="
|
|
44882
|
-
//# debugId=
|
|
44900
|
+
!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]="8ed74459-2a69-556c-bb95-8cdebdb094cd")}catch(e){}}();
|
|
44901
|
+
//# debugId=8ed74459-2a69-556c-bb95-8cdebdb094cd
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-doctor",
|
|
3
|
-
"version": "0.7.6-dev.
|
|
3
|
+
"version": "0.7.6-dev.0d11bd5",
|
|
4
4
|
"description": "Your agent writes bad React. This catches it",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"accessibility",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"vscode-uri": "^3.1.0",
|
|
67
67
|
"yaml": "^2.9.0",
|
|
68
68
|
"deslop-js": "0.7.6",
|
|
69
|
-
"oxlint-plugin-react-doctor": "0.7.6-dev.
|
|
69
|
+
"oxlint-plugin-react-doctor": "0.7.6-dev.0d11bd5"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/babel__code-frame": "^7.27.0",
|
|
@@ -75,8 +75,8 @@
|
|
|
75
75
|
"commander": "^14.0.3",
|
|
76
76
|
"ora": "^9.4.0",
|
|
77
77
|
"@react-doctor/api": "0.7.6",
|
|
78
|
-
"@react-doctor/
|
|
79
|
-
"@react-doctor/
|
|
78
|
+
"@react-doctor/core": "0.7.6",
|
|
79
|
+
"@react-doctor/language-server": "0.7.6"
|
|
80
80
|
},
|
|
81
81
|
"engines": {
|
|
82
82
|
"node": "^20.19.0 || >=22.13.0"
|