react-doctor 0.7.5 → 0.7.6-dev.0a8d30b
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 +151 -47
- package/dist/index.d.ts +8 -7
- package/dist/index.js +124 -38
- package/dist/lsp.js +141 -63
- package/package.json +6 -6
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]="5dcb5b0b-ca1e-5655-8f52-3c22b421256d")}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";
|
|
@@ -36241,7 +36241,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
36241
36241
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
36242
36242
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
36243
36243
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
36244
|
-
const CORE_PACKAGE_VERSION = "0.7.
|
|
36244
|
+
const CORE_PACKAGE_VERSION = "0.7.6";
|
|
36245
36245
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
36246
36246
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
36247
36247
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -36341,10 +36341,12 @@ const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
|
36341
36341
|
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort());
|
|
36342
36342
|
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
36343
36343
|
const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
|
|
36344
|
+
const UTF8_BOM_CHARACTER = "";
|
|
36345
|
+
const stripUtf8Bom = (content) => content.startsWith(UTF8_BOM_CHARACTER) ? content.slice(1) : content;
|
|
36344
36346
|
const cachedPackageJsons = /* @__PURE__ */ new Map();
|
|
36345
36347
|
const readPackageJsonUncached = (packageJsonPath) => {
|
|
36346
36348
|
try {
|
|
36347
|
-
return JSON.parse(NFS.readFileSync(packageJsonPath, "utf-8"));
|
|
36349
|
+
return JSON.parse(stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8")));
|
|
36348
36350
|
} catch (error) {
|
|
36349
36351
|
if (error instanceof SyntaxError) return {};
|
|
36350
36352
|
if (isErrnoException(error)) {
|
|
@@ -40017,7 +40019,6 @@ const PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
|
|
|
40017
40019
|
const PNPM_LOCKFILE = "pnpm-lock.yaml";
|
|
40018
40020
|
const PACKAGE_JSON_FILE = "package.json";
|
|
40019
40021
|
const PNPM_HARDENING_RULE_KEY = "require-pnpm-hardening";
|
|
40020
|
-
const UTF8_BOM_CHAR = "";
|
|
40021
40022
|
const HARDENING_SETTING_KEYS = new Set([
|
|
40022
40023
|
"minimumReleaseAge",
|
|
40023
40024
|
"blockExoticSubdeps",
|
|
@@ -40042,12 +40043,11 @@ const stripInlineComment = (rawValue) => {
|
|
|
40042
40043
|
return rawValue;
|
|
40043
40044
|
};
|
|
40044
40045
|
const unquote = (rawValue) => rawValue.replace(/^["']|["']$/g, "");
|
|
40045
|
-
const stripBom = (rawContent) => rawContent.startsWith(UTF8_BOM_CHAR) ? rawContent.slice(1) : rawContent;
|
|
40046
40046
|
const parseHardeningSettings = (content) => {
|
|
40047
40047
|
let minimumReleaseAge = null;
|
|
40048
40048
|
let blockExoticSubdeps = null;
|
|
40049
40049
|
let trustPolicy = null;
|
|
40050
|
-
const lines =
|
|
40050
|
+
const lines = stripUtf8Bom(content).split(/\r?\n/);
|
|
40051
40051
|
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
40052
40052
|
const lineText = lines[lineIndex];
|
|
40053
40053
|
if (lineText === void 0) continue;
|
|
@@ -40081,7 +40081,7 @@ const isPnpmManagedProject = (rootDirectory) => {
|
|
|
40081
40081
|
const packageJsonPath = Path.join(rootDirectory, PACKAGE_JSON_FILE);
|
|
40082
40082
|
if (!isFile(packageJsonPath)) return false;
|
|
40083
40083
|
try {
|
|
40084
|
-
const packageJsonRaw = NFS.readFileSync(packageJsonPath, "utf-8");
|
|
40084
|
+
const packageJsonRaw = stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8"));
|
|
40085
40085
|
const packageJson = JSON.parse(packageJsonRaw);
|
|
40086
40086
|
if (packageJson !== null && typeof packageJson === "object" && "packageManager" in packageJson && typeof packageJson.packageManager === "string" && packageJson.packageManager.startsWith("pnpm@")) return true;
|
|
40087
40087
|
} catch {
|
|
@@ -40762,9 +40762,7 @@ const setColorEnabled = (enabled) => {
|
|
|
40762
40762
|
highlighter.gray = colors.gray;
|
|
40763
40763
|
highlighter.bold = colors.bold;
|
|
40764
40764
|
};
|
|
40765
|
-
const
|
|
40766
|
-
const shouldIncludeExplicitLintPath = (filePath, project) => JSX_FILE_PATTERN.test(filePath) || project?.framework === "nextjs" && NEXT_ENTRY_FILE_PATTERN.test(filePath);
|
|
40767
|
-
const computeExplicitLintIncludePaths = (includePaths, project) => includePaths.length > 0 ? includePaths.filter((filePath) => shouldIncludeExplicitLintPath(filePath, project)) : void 0;
|
|
40765
|
+
const computeExplicitLintIncludePaths = (includePaths) => includePaths.length > 0 ? includePaths.filter(isLintableSourceFile) : void 0;
|
|
40768
40766
|
const LINGUIST_ATTRIBUTE_PATTERN = /^linguist-(?:vendored|generated)(?:=([a-zA-Z0-9]+))?$/i;
|
|
40769
40767
|
const FALSY_VALUES = new Set([
|
|
40770
40768
|
"false",
|
|
@@ -41603,11 +41601,11 @@ const resolveDeadCodeTimeout = (input) => {
|
|
|
41603
41601
|
phaseTimeoutMs: workerTimeoutMs + DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS
|
|
41604
41602
|
};
|
|
41605
41603
|
};
|
|
41606
|
-
const resolveLintIncludePaths = (rootDirectory, userConfig
|
|
41604
|
+
const resolveLintIncludePaths = (rootDirectory, userConfig) => {
|
|
41607
41605
|
if (!Array.isArray(userConfig?.ignore?.files) || userConfig.ignore.files.length === 0) return;
|
|
41608
41606
|
const ignoredPatterns = compileIgnoredFilePatterns(userConfig);
|
|
41609
41607
|
return listSourceFiles(rootDirectory).filter((filePath) => {
|
|
41610
|
-
if (!
|
|
41608
|
+
if (!isLintableSourceFile(filePath)) return false;
|
|
41611
41609
|
return !isFileIgnoredByPatterns(filePath, rootDirectory, ignoredPatterns);
|
|
41612
41610
|
});
|
|
41613
41611
|
};
|
|
@@ -41825,6 +41823,7 @@ const parseGithubViewerPermission = (stdout) => {
|
|
|
41825
41823
|
return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
|
|
41826
41824
|
};
|
|
41827
41825
|
const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
|
|
41826
|
+
const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
|
|
41828
41827
|
/**
|
|
41829
41828
|
* `Git` wraps every `git`-via-subprocess call react-doctor makes
|
|
41830
41829
|
* behind a `Context.Service`. The production layer (`layerNode`)
|
|
@@ -41895,6 +41894,14 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
41895
41894
|
args,
|
|
41896
41895
|
directory
|
|
41897
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);
|
|
41898
41905
|
const currentBranch = (directory) => runGit(directory, [
|
|
41899
41906
|
"rev-parse",
|
|
41900
41907
|
"--abbrev-ref",
|
|
@@ -42017,7 +42024,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42017
42024
|
githubViewerPermission,
|
|
42018
42025
|
branchExists,
|
|
42019
42026
|
mergeBase,
|
|
42020
|
-
diffSelection: ({ directory, explicitBaseBranch }) => gen(function* () {
|
|
42027
|
+
diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
|
|
42021
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." }) }));
|
|
42022
42029
|
if (explicitBaseBranch !== void 0) {
|
|
42023
42030
|
const range = parseGitDiffRange(explicitBaseBranch);
|
|
@@ -42046,7 +42053,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42046
42053
|
"HEAD"
|
|
42047
42054
|
]);
|
|
42048
42055
|
if (uncommitted.status !== 0) return null;
|
|
42049
|
-
const files = splitNullSeparated(uncommitted.stdout);
|
|
42056
|
+
const files = yield* mergeUntracked(directory, splitNullSeparated(uncommitted.stdout), includeUntracked);
|
|
42050
42057
|
if (files.length === 0) return null;
|
|
42051
42058
|
return {
|
|
42052
42059
|
currentBranch: resolvedCurrentBranch,
|
|
@@ -42076,7 +42083,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42076
42083
|
currentBranch: resolvedCurrentBranch,
|
|
42077
42084
|
baseBranch,
|
|
42078
42085
|
diffBaseRef: mergeBaseRef,
|
|
42079
|
-
changedFiles: splitNullSeparated(diff.stdout),
|
|
42086
|
+
changedFiles: yield* mergeUntracked(directory, splitNullSeparated(diff.stdout), includeUntracked),
|
|
42080
42087
|
isCurrentChanges: false
|
|
42081
42088
|
};
|
|
42082
42089
|
}).pipe(withSpan("Git.diffSelection")),
|
|
@@ -42122,7 +42129,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42122
42129
|
stdout: result.stdout
|
|
42123
42130
|
};
|
|
42124
42131
|
}).pipe(withSpan("Git.grep")),
|
|
42125
|
-
changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
|
|
42132
|
+
changedLineRanges: ({ directory, baseRef, cached, files, includeUntracked = false }) => gen(function* () {
|
|
42126
42133
|
if (files.length === 0) return [];
|
|
42127
42134
|
if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
|
|
42128
42135
|
const result = yield* runGit(directory, [
|
|
@@ -42136,7 +42143,14 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42136
42143
|
...files
|
|
42137
42144
|
]);
|
|
42138
42145
|
if (result.status !== 0) return null;
|
|
42139
|
-
|
|
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
|
+
}))];
|
|
42140
42154
|
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
42141
42155
|
});
|
|
42142
42156
|
})).pipe(provide$2(layer$3.pipe(provide$2(mergeAll$1(layer$2, layer$1)))));
|
|
@@ -42368,7 +42382,7 @@ const normalizeConfigForHash = (config) => {
|
|
|
42368
42382
|
if (Array.isArray(clone?.jsPlugins)) clone.jsPlugins = clone.jsPlugins.map((_, index) => `<plugin:${index}>`);
|
|
42369
42383
|
return clone;
|
|
42370
42384
|
};
|
|
42371
|
-
const computeRulesetHash = (input) => crypto.createHash("sha1").update(JSON.stringify(normalizeConfigForHash(input.config))).update("\0").update([...input.toolchainVersions].join("\0")).update("\0").update([...input.ignorePatterns].join("\n")).update("\0").update(input.tsconfigContent ?? "").digest("hex");
|
|
42385
|
+
const computeRulesetHash = (input) => crypto.createHash("sha1").update(JSON.stringify(normalizeConfigForHash(input.config))).update("\0").update([...input.toolchainVersions].join("\0")).update("\0").update([...input.ignorePatterns].join("\n")).update("\0").update(input.tsconfigContent ?? "").update("\0").update(input.respectInlineDisables ? "respect-inline-disables" : "audit-mode").digest("hex");
|
|
42372
42386
|
/**
|
|
42373
42387
|
* Loads a plugin module via the local require resolver and extracts
|
|
42374
42388
|
* `(name, ruleNames)` from either `module.exports.meta + rules` or
|
|
@@ -42404,7 +42418,7 @@ const resolveReactHooksJsPlugin = (hasReactCompiler, customRulesOnly) => {
|
|
|
42404
42418
|
} catch {
|
|
42405
42419
|
return null;
|
|
42406
42420
|
}
|
|
42407
|
-
const { ruleNames } = readPluginShape(pluginSpecifier, (
|
|
42421
|
+
const { ruleNames } = readPluginShape(pluginSpecifier, (specifier) => bundledRequire$1(specifier));
|
|
42408
42422
|
return {
|
|
42409
42423
|
entry: {
|
|
42410
42424
|
name: "react-hooks-js",
|
|
@@ -42812,6 +42826,23 @@ const resolveTsConfigRelativePath = (rootDirectory) => {
|
|
|
42812
42826
|
for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
|
|
42813
42827
|
return null;
|
|
42814
42828
|
};
|
|
42829
|
+
const DERIVED_STATE_RULE_PRIORITY = new Map([
|
|
42830
|
+
["no-initialize-state", 0],
|
|
42831
|
+
["no-adjust-state-on-prop-change", 1],
|
|
42832
|
+
["no-derived-state", 2],
|
|
42833
|
+
["no-derived-state-effect", 3]
|
|
42834
|
+
]);
|
|
42835
|
+
const doDiagnosticSpansOverlap = (first, second) => {
|
|
42836
|
+
if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
|
|
42837
|
+
if (first.offset !== void 0 && second.offset !== void 0) {
|
|
42838
|
+
const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
|
|
42839
|
+
const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
|
|
42840
|
+
return first.offset < secondEnd && second.offset < firstEnd;
|
|
42841
|
+
}
|
|
42842
|
+
const firstEndLine = first.endLine ?? first.line;
|
|
42843
|
+
const secondEndLine = second.endLine ?? second.line;
|
|
42844
|
+
return first.line <= secondEndLine && second.line <= firstEndLine;
|
|
42845
|
+
};
|
|
42815
42846
|
const dedupeDiagnostics = (diagnostics) => {
|
|
42816
42847
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
42817
42848
|
const uniqueDiagnostics = [];
|
|
@@ -42819,6 +42850,16 @@ const dedupeDiagnostics = (diagnostics) => {
|
|
|
42819
42850
|
const key = `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}\u0000${diagnostic.plugin}\u0000${diagnostic.rule}\u0000${diagnostic.severity}\u0000${diagnostic.message}`;
|
|
42820
42851
|
if (seenKeys.has(key)) continue;
|
|
42821
42852
|
seenKeys.add(key);
|
|
42853
|
+
const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
|
|
42854
|
+
if (priority !== void 0) {
|
|
42855
|
+
const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && candidate.severity === diagnostic.severity && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
|
|
42856
|
+
if (overlappingDiagnosticIndex >= 0) {
|
|
42857
|
+
const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
|
|
42858
|
+
const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
|
|
42859
|
+
if (existingPriority !== void 0 && priority < existingPriority) uniqueDiagnostics[overlappingDiagnosticIndex] = diagnostic;
|
|
42860
|
+
continue;
|
|
42861
|
+
}
|
|
42862
|
+
}
|
|
42822
42863
|
uniqueDiagnostics.push(diagnostic);
|
|
42823
42864
|
}
|
|
42824
42865
|
return uniqueDiagnostics;
|
|
@@ -42861,6 +42902,12 @@ const mapWithConcurrency = async (items, concurrency, task) => {
|
|
|
42861
42902
|
if (errors.length > 0) throw errors[0];
|
|
42862
42903
|
return results;
|
|
42863
42904
|
};
|
|
42905
|
+
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
42906
|
+
let lineNumber = 1;
|
|
42907
|
+
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
42908
|
+
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === 10) lineNumber++;
|
|
42909
|
+
return lineNumber;
|
|
42910
|
+
};
|
|
42864
42911
|
const REANIMATED_SHARED_VALUE_HINT = "If this is a Reanimated shared value, prefer its React Compiler-compatible `.get()` / `.set()` accessors over `.value` — https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue/#react-compiler-support";
|
|
42865
42912
|
const ACCESSORS_MIN_VERSION = "3.15.0";
|
|
42866
42913
|
const supportsAccessors = (reanimatedVersion) => {
|
|
@@ -43432,13 +43479,6 @@ const findOutermostEnclosingFunctionRange = (sourceFile, targetLine) => {
|
|
|
43432
43479
|
visit(sourceFile);
|
|
43433
43480
|
return foundRange;
|
|
43434
43481
|
};
|
|
43435
|
-
const NEWLINE_BYTE = 10;
|
|
43436
|
-
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
43437
|
-
let lineNumber = 1;
|
|
43438
|
-
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
43439
|
-
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === NEWLINE_BYTE) lineNumber++;
|
|
43440
|
-
return lineNumber;
|
|
43441
|
-
};
|
|
43442
43482
|
/**
|
|
43443
43483
|
* Drops `react-doctor/react-compiler-no-manual-memoization` diagnostics
|
|
43444
43484
|
* whose enclosing function also carries a `react-hooks-js` (React
|
|
@@ -43643,8 +43683,33 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
43643
43683
|
}
|
|
43644
43684
|
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
43645
43685
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
43686
|
+
const sourceBufferCache = /* @__PURE__ */ new Map();
|
|
43687
|
+
const sourceLinesCache = /* @__PURE__ */ new Map();
|
|
43688
|
+
const resolveAbsolutePath = (filename) => Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
43689
|
+
const readSourceBuffer = (filename) => {
|
|
43690
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
43691
|
+
const cached = sourceBufferCache.get(absolutePath);
|
|
43692
|
+
if (cached !== void 0) return cached;
|
|
43693
|
+
let sourceBuffer;
|
|
43694
|
+
try {
|
|
43695
|
+
sourceBuffer = NFS.readFileSync(absolutePath);
|
|
43696
|
+
} catch {
|
|
43697
|
+
sourceBuffer = null;
|
|
43698
|
+
}
|
|
43699
|
+
sourceBufferCache.set(absolutePath, sourceBuffer);
|
|
43700
|
+
return sourceBuffer;
|
|
43701
|
+
};
|
|
43702
|
+
const readSourceLines = (filename) => {
|
|
43703
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
43704
|
+
const cached = sourceLinesCache.get(absolutePath);
|
|
43705
|
+
if (cached !== void 0) return cached;
|
|
43706
|
+
const sourceBuffer = readSourceBuffer(filename);
|
|
43707
|
+
const sourceLines = sourceBuffer ? sourceBuffer.toString("utf8").split("\n") : null;
|
|
43708
|
+
sourceLinesCache.set(absolutePath, sourceLines);
|
|
43709
|
+
return sourceLines;
|
|
43710
|
+
};
|
|
43646
43711
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
43647
|
-
const absolutePath =
|
|
43712
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
43648
43713
|
const cached = minifiedFileCache.get(absolutePath);
|
|
43649
43714
|
if (cached !== void 0) return cached;
|
|
43650
43715
|
const minified = isMinifiedSource(absolutePath);
|
|
@@ -43657,8 +43722,17 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
43657
43722
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
43658
43723
|
const normalizedFilePath = diagnostic.filename.replaceAll("\\", "/");
|
|
43659
43724
|
const primarySpan = primaryLabel?.span;
|
|
43725
|
+
const sourceBuffer = primarySpan ? readSourceBuffer(diagnostic.filename) : null;
|
|
43660
43726
|
const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath);
|
|
43661
43727
|
const category = resolveDiagnosticCategory(plugin, rule);
|
|
43728
|
+
const matchByOccurrence = resolveMatchByOccurrence(rule, category);
|
|
43729
|
+
const sourceLines = primarySpan ? readSourceLines(diagnostic.filename) : null;
|
|
43730
|
+
const primaryLineIndex = primarySpan ? primarySpan.line - 1 : -1;
|
|
43731
|
+
const primaryLine = sourceLines?.[primaryLineIndex];
|
|
43732
|
+
const primaryColumnIndex = primarySpan ? primarySpan.column - 1 : -1;
|
|
43733
|
+
const isJsxTagLabel = primaryLine !== void 0 && primaryColumnIndex >= 0 && (primaryLine[primaryColumnIndex] === "<" || primaryLine[primaryColumnIndex - 1] === "<");
|
|
43734
|
+
const jsxOpenerEndLineIndex = sourceLines && isJsxTagLabel ? findJsxOpenerSpan(sourceLines, primaryLineIndex) : null;
|
|
43735
|
+
const primarySpanEndLine = jsxOpenerEndLineIndex !== null ? jsxOpenerEndLineIndex + 1 : primarySpan && sourceBuffer ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset + Math.max(primarySpan.length - 1, 0)) : void 0;
|
|
43662
43736
|
return {
|
|
43663
43737
|
filePath: normalizedFilePath,
|
|
43664
43738
|
plugin,
|
|
@@ -43672,10 +43746,11 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
43672
43746
|
column: primarySpan?.column ?? 0,
|
|
43673
43747
|
...primarySpan ? {
|
|
43674
43748
|
offset: primarySpan.offset,
|
|
43675
|
-
length: primarySpan.length
|
|
43749
|
+
length: primarySpan.length,
|
|
43750
|
+
...primarySpanEndLine !== void 0 ? { endLine: primarySpanEndLine } : {}
|
|
43676
43751
|
} : {},
|
|
43677
43752
|
category,
|
|
43678
|
-
...
|
|
43753
|
+
...matchByOccurrence ? { matchByOccurrence: true } : {},
|
|
43679
43754
|
...relatedLocations.length > 0 ? { relatedLocations } : {}
|
|
43680
43755
|
};
|
|
43681
43756
|
}), rootDirectory);
|
|
@@ -43692,6 +43767,11 @@ const buildOxlintChildEnv = (sourceEnv) => {
|
|
|
43692
43767
|
if (!isCompileCacheDisabled && !isCompileCacheAlreadySet) childEnv.NODE_COMPILE_CACHE = Path.join(os.tmpdir(), NODE_COMPILE_CACHE_DIR_NAME);
|
|
43693
43768
|
return childEnv;
|
|
43694
43769
|
};
|
|
43770
|
+
const buildProfiledNodeArguments = (input) => [
|
|
43771
|
+
...input.cpuProfileDirectory ? ["--cpu-prof", `--cpu-prof-dir=${input.cpuProfileDirectory}`] : [],
|
|
43772
|
+
...input.heapProfileDirectory ? ["--heap-prof", `--heap-prof-dir=${input.heapProfileDirectory}`] : [],
|
|
43773
|
+
...input.argumentsList
|
|
43774
|
+
];
|
|
43695
43775
|
const SANITIZED_ENV = buildOxlintChildEnv(process.env);
|
|
43696
43776
|
/**
|
|
43697
43777
|
* Spawn one oxlint subprocess with hard ceilings on wall time and
|
|
@@ -43716,7 +43796,11 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
|
|
|
43716
43796
|
reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) }));
|
|
43717
43797
|
return;
|
|
43718
43798
|
}
|
|
43719
|
-
const child = spawn(nodeBinaryPath,
|
|
43799
|
+
const child = spawn(nodeBinaryPath, buildProfiledNodeArguments({
|
|
43800
|
+
argumentsList: args,
|
|
43801
|
+
cpuProfileDirectory: SANITIZED_ENV.REACT_DOCTOR_CPU_PROFILE_DIR,
|
|
43802
|
+
heapProfileDirectory: SANITIZED_ENV.REACT_DOCTOR_HEAP_PROFILE_DIR
|
|
43803
|
+
}), {
|
|
43720
43804
|
cwd: rootDirectory,
|
|
43721
43805
|
env: SANITIZED_ENV,
|
|
43722
43806
|
stdio: [
|
|
@@ -44360,7 +44444,7 @@ const runOxlint = async (options) => {
|
|
|
44360
44444
|
};
|
|
44361
44445
|
}
|
|
44362
44446
|
};
|
|
44363
|
-
if (perFileLintCacheEnabled &&
|
|
44447
|
+
if (perFileLintCacheEnabled && !project.hasReactCompiler && extendsPaths.length === 0 && userPlugins.length === 0) {
|
|
44364
44448
|
const rulesetHash = computeRulesetHash({
|
|
44365
44449
|
config: buildConfig({
|
|
44366
44450
|
extendsPaths: [],
|
|
@@ -44368,7 +44452,8 @@ const runOxlint = async (options) => {
|
|
|
44368
44452
|
}),
|
|
44369
44453
|
toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
|
|
44370
44454
|
ignorePatterns: combinedPatterns,
|
|
44371
|
-
tsconfigContent
|
|
44455
|
+
tsconfigContent,
|
|
44456
|
+
respectInlineDisables
|
|
44372
44457
|
});
|
|
44373
44458
|
const cache = createFileLintCache(resolveReactDoctorCacheDir(rootDirectory), rulesetHash);
|
|
44374
44459
|
const cacheKeyByFile = /* @__PURE__ */ new Map();
|
|
@@ -44411,7 +44496,8 @@ const runOxlint = async (options) => {
|
|
|
44411
44496
|
}),
|
|
44412
44497
|
toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
|
|
44413
44498
|
ignorePatterns: combinedPatterns,
|
|
44414
|
-
tsconfigContent
|
|
44499
|
+
tsconfigContent,
|
|
44500
|
+
respectInlineDisables
|
|
44415
44501
|
})) : null;
|
|
44416
44502
|
const probeAnswers = createSidecarProbeAnswerResolver({
|
|
44417
44503
|
rootDirectory,
|
|
@@ -45451,7 +45537,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
45451
45537
|
directory: scanDirectory,
|
|
45452
45538
|
repo
|
|
45453
45539
|
}).pipe(orElseSucceed(() => null)) : succeed$2(null));
|
|
45454
|
-
const lintIncludePaths = (input.
|
|
45540
|
+
const lintIncludePaths = (input.skipExplicitIncludePathFilter ? input.includePaths.length > 0 ? [...input.includePaths] : void 0 : computeExplicitLintIncludePaths([...input.includePaths])) ?? resolveLintIncludePaths(scanDirectory, resolvedConfig.config);
|
|
45455
45541
|
const fallbackScannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
|
|
45456
45542
|
const beforeLint = hooks.beforeLint ?? NO_HOOKS.beforeLint;
|
|
45457
45543
|
const afterLint = hooks.afterLint ?? NO_HOOKS.afterLint;
|
|
@@ -46196,10 +46282,11 @@ const computeDiagnosticDelta = (input) => {
|
|
|
46196
46282
|
* render diff-base mistakes (`GitBaseBranchInvalid` /
|
|
46197
46283
|
* `GitBaseBranchMissing`) as clean user errors instead of crashes.
|
|
46198
46284
|
*/
|
|
46199
|
-
const getDiffInfo = (directory, explicitBaseBranch) => runPromise(gen(function* () {
|
|
46285
|
+
const getDiffInfo = (directory, explicitBaseBranch, includeUntracked) => runPromise(gen(function* () {
|
|
46200
46286
|
const selection = yield* (yield* Git).diffSelection({
|
|
46201
46287
|
directory,
|
|
46202
|
-
explicitBaseBranch
|
|
46288
|
+
explicitBaseBranch,
|
|
46289
|
+
includeUntracked
|
|
46203
46290
|
});
|
|
46204
46291
|
if (selection === null) return null;
|
|
46205
46292
|
return {
|
|
@@ -46629,7 +46716,7 @@ const makeNoopConsole = () => ({
|
|
|
46629
46716
|
});
|
|
46630
46717
|
//#endregion
|
|
46631
46718
|
//#region src/cli/utils/version.ts
|
|
46632
|
-
const VERSION = "0.7.
|
|
46719
|
+
const VERSION = "0.7.6-dev.0a8d30b";
|
|
46633
46720
|
//#endregion
|
|
46634
46721
|
//#region src/cli/utils/json-mode.ts
|
|
46635
46722
|
let context = null;
|
|
@@ -46998,13 +47085,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
46998
47085
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
46999
47086
|
* standard `SENTRY_RELEASE` override.
|
|
47000
47087
|
*/
|
|
47001
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.
|
|
47088
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.6-dev.0a8d30b`;
|
|
47002
47089
|
/**
|
|
47003
47090
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
47004
47091
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
47005
47092
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
47006
47093
|
*/
|
|
47007
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.
|
|
47094
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.6-dev.0a8d30b") ? "development" : "production");
|
|
47008
47095
|
/**
|
|
47009
47096
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
47010
47097
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -50769,7 +50856,7 @@ const BLOCKING_SUMMARY = {
|
|
|
50769
50856
|
const SCOPE_SUMMARY = {
|
|
50770
50857
|
full: "Scan the whole project on every run",
|
|
50771
50858
|
files: "Report every issue in changed files",
|
|
50772
|
-
lines: "Report issues
|
|
50859
|
+
lines: "Report issues whose source spans touch changed lines",
|
|
50773
50860
|
changed: "Report only the issues a change introduces"
|
|
50774
50861
|
};
|
|
50775
50862
|
const gatesEqual = (left, right) => left.blocking === right.blocking && left.scope === right.scope && left.comment === right.comment && left.reviewComments === right.reviewComments && left.commitStatus === right.commitStatus;
|
|
@@ -59164,7 +59251,7 @@ const buildDirtyPathContentFingerprint = (absolutePath, statusCode) => {
|
|
|
59164
59251
|
return null;
|
|
59165
59252
|
}
|
|
59166
59253
|
if (!stats.isFile()) return null;
|
|
59167
|
-
if (stats.size > 10485760) return
|
|
59254
|
+
if (stats.size > 10485760) return null;
|
|
59168
59255
|
return hashFileContents(absolutePath);
|
|
59169
59256
|
};
|
|
59170
59257
|
const buildWorktreeFingerprint = (projectDirectory) => {
|
|
@@ -59638,7 +59725,9 @@ const buildScanAttributes = (input) => {
|
|
|
59638
59725
|
hasCustomConfig: input.hasCustomConfig,
|
|
59639
59726
|
rulesConfigured: ruleKeys.length,
|
|
59640
59727
|
rulesDisabled: ruleKeys.filter((key) => ruleOverrides[key] === "off").length,
|
|
59641
|
-
fileCount: input.result?.scannedFileCount ?? null
|
|
59728
|
+
fileCount: input.result?.scannedFileCount ?? null,
|
|
59729
|
+
nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length ?? null,
|
|
59730
|
+
multilineDiagnosticCount: input.result?.diagnostics.filter((diagnostic) => (diagnostic.endLine ?? diagnostic.line) > diagnostic.line).length ?? null
|
|
59642
59731
|
});
|
|
59643
59732
|
};
|
|
59644
59733
|
/**
|
|
@@ -59707,6 +59796,12 @@ const countDeadlineSkippedFiles = (lintPartialFailures) => sumFileCountsMatching
|
|
|
59707
59796
|
const DROPPED_FILES_MESSAGE_PATTERN = /^(\d+) file\(s\) failed to lint and were skipped/;
|
|
59708
59797
|
const countDroppedLintFiles = (lintPartialFailures) => sumFileCountsMatching(lintPartialFailures, DROPPED_FILES_MESSAGE_PATTERN);
|
|
59709
59798
|
//#endregion
|
|
59799
|
+
//#region src/cli/utils/diagnostic-intersects-line-ranges.ts
|
|
59800
|
+
const diagnosticIntersectsLineRanges = (diagnostic, lineRanges) => {
|
|
59801
|
+
const diagnosticEndLine = Math.max(diagnostic.line, diagnostic.endLine ?? diagnostic.line);
|
|
59802
|
+
return lineRanges.some(([rangeStartLine, rangeEndLine]) => diagnostic.line <= rangeEndLine && diagnosticEndLine >= rangeStartLine);
|
|
59803
|
+
};
|
|
59804
|
+
//#endregion
|
|
59710
59805
|
//#region src/cli/utils/materialize-baseline-files.ts
|
|
59711
59806
|
/**
|
|
59712
59807
|
* Materializes the `ref` version of `files` into `tempDirectory` (via
|
|
@@ -60892,7 +60987,7 @@ const buildChangedLineMatcher = (directory, changedLineRanges) => {
|
|
|
60892
60987
|
const relativePath = toForwardSlashes(path.isAbsolute(diagnostic.filePath) ? path.relative(directory, diagnostic.filePath) : diagnostic.filePath);
|
|
60893
60988
|
const ranges = rangesByFile.get(relativePath);
|
|
60894
60989
|
if (ranges === void 0) return false;
|
|
60895
|
-
return
|
|
60990
|
+
return diagnosticIntersectsLineRanges(diagnostic, ranges);
|
|
60896
60991
|
};
|
|
60897
60992
|
};
|
|
60898
60993
|
const buildIgnoredTags = (userConfig) => {
|
|
@@ -63714,10 +63809,15 @@ const warnIfAiTrainingEnvironment = () => {
|
|
|
63714
63809
|
//#region src/cli/utils/validate-mode-flags.ts
|
|
63715
63810
|
const usedDiffAlias = (flags) => flags.diff !== void 0 && flags.diff !== false && flags.diff !== "false" && flags.diff !== "";
|
|
63716
63811
|
const usedScope = (flags) => typeof flags.scope === "string" && flags.scope.length > 0;
|
|
63812
|
+
const validateIncludeUntrackedScope = (includeUntracked, scope) => {
|
|
63813
|
+
if (!includeUntracked || scope !== void 0 && scope !== "full") return;
|
|
63814
|
+
throw new CliInputError("--include-untracked requires the files, changed, or lines scope (via --scope, --diff, or config).");
|
|
63815
|
+
};
|
|
63717
63816
|
const validateModeFlags = (flags) => {
|
|
63718
63817
|
if (usedScope(flags) && usedDiffAlias(flags)) throw new CliInputError("Cannot combine --scope and --diff; --diff is the deprecated alias.");
|
|
63719
63818
|
if (flags.staged && usedDiffAlias(flags)) throw new CliInputError("Cannot combine --staged and --diff; pick one mode.");
|
|
63720
63819
|
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.`);
|
|
63820
|
+
if (flags.includeUntracked && flags.staged) throw new CliInputError("Cannot combine --include-untracked with --staged; the git index never holds untracked files.");
|
|
63721
63821
|
if (flags.score && flags.json) throw new CliInputError("Cannot combine --score and --json; pick one output mode.");
|
|
63722
63822
|
if (flags.score && flags.telemetry === false) throw new CliInputError("Cannot combine --score with --no-telemetry; --score prints the score that --no-telemetry disables.");
|
|
63723
63823
|
if (flags.debug && (flags.score === false || flags.telemetry === false)) {
|
|
@@ -63927,13 +64027,15 @@ const inspectAction = async (directory, flags) => {
|
|
|
63927
64027
|
const projectDirectories = await selectProjects(resolvedDirectory, flags.project, skipPrompts, userConfig?.projects);
|
|
63928
64028
|
const changedFilesDiffInfo = flags.changedFilesFrom ? buildChangedFilesDiffInfo(readChangedFilesFrom(Path.resolve(flags.changedFilesFrom))) : null;
|
|
63929
64029
|
const requestedScope = resolveScope(flags, userConfig);
|
|
64030
|
+
const includeUntracked = flags.includeUntracked ?? false;
|
|
63930
64031
|
const scopeRequest = requestedScope.scope === void 0 && changedFilesDiffInfo !== null ? {
|
|
63931
64032
|
...requestedScope,
|
|
63932
64033
|
scope: "changed"
|
|
63933
64034
|
} : requestedScope;
|
|
64035
|
+
validateIncludeUntrackedScope(includeUntracked, scopeRequest.scope);
|
|
63934
64036
|
const wantsDiffMode = scopeRequest.scope !== void 0 && scopeRequest.scope !== "full";
|
|
63935
64037
|
const shouldDetectDiff = changedFilesDiffInfo === null && (wantsDiffMode || scopeRequest.scope === void 0 && !skipPrompts && !isQuiet);
|
|
63936
|
-
const diffInfo = changedFilesDiffInfo ?? (shouldDetectDiff ? await getDiffInfo(resolvedDirectory, scopeRequest.base) : null);
|
|
64038
|
+
const diffInfo = changedFilesDiffInfo ?? (shouldDetectDiff ? await getDiffInfo(resolvedDirectory, scopeRequest.base, includeUntracked) : null);
|
|
63937
64039
|
const scope = await finalizeScope({
|
|
63938
64040
|
requested: scopeRequest,
|
|
63939
64041
|
diffInfo,
|
|
@@ -63947,7 +64049,8 @@ const inspectAction = async (directory, flags) => {
|
|
|
63947
64049
|
const changedLineRanges = scope === "lines" && diffInfo !== null && (diffInfo.isCurrentChanges || linesBaseRef !== null) && diffInfo !== null ? await getChangedLineRanges({
|
|
63948
64050
|
directory: resolvedDirectory,
|
|
63949
64051
|
baseRef: linesBaseRef ?? void 0,
|
|
63950
|
-
files: [...diffInfo.changedFiles]
|
|
64052
|
+
files: [...diffInfo.changedFiles],
|
|
64053
|
+
includeUntracked
|
|
63951
64054
|
}) : null;
|
|
63952
64055
|
if (scope === "lines" && changedLineRanges === null && !isQuiet) {
|
|
63953
64056
|
cliLogger.warn("Could not determine changed lines (no base ref or git diff failed); reporting all issues in changed files.");
|
|
@@ -64880,6 +64983,7 @@ const ROOT_FLAG_SPEC = {
|
|
|
64880
64983
|
"--dead-code",
|
|
64881
64984
|
"--debug",
|
|
64882
64985
|
"--help",
|
|
64986
|
+
"--include-untracked",
|
|
64883
64987
|
"--json",
|
|
64884
64988
|
"--json-compact",
|
|
64885
64989
|
"--lint",
|
|
@@ -65143,7 +65247,7 @@ ${highlighter.dim("Learn more:")}
|
|
|
65143
65247
|
${highlighter.info(CI_URL)}
|
|
65144
65248
|
`;
|
|
65145
65249
|
const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
|
|
65146
|
-
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 to scan/report: full (default), files, changed (only new issues vs base), or lines (
|
|
65250
|
+
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);
|
|
65147
65251
|
program.action(inspectAction);
|
|
65148
65252
|
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));
|
|
65149
65253
|
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);
|
|
@@ -65193,4 +65297,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
65193
65297
|
export {};
|
|
65194
65298
|
|
|
65195
65299
|
//# sourceMappingURL=cli.js.map
|
|
65196
|
-
//# debugId=
|
|
65300
|
+
//# debugId=5dcb5b0b-ca1e-5655-8f52-3c22b421256d
|
package/dist/index.d.ts
CHANGED
|
@@ -9654,6 +9654,7 @@ interface DiagnoseOptions {
|
|
|
9654
9654
|
/** See `ReactDoctorConfig.deadCode`. Ignored in diff mode. */
|
|
9655
9655
|
deadCode?: boolean;
|
|
9656
9656
|
verbose?: boolean;
|
|
9657
|
+
/** Restrict linting to these supported JS/TS source files. */
|
|
9657
9658
|
includePaths?: string[];
|
|
9658
9659
|
/**
|
|
9659
9660
|
* Per-call override for `ReactDoctorConfig.respectInlineDisables`.
|
|
@@ -9800,9 +9801,9 @@ interface InspectResult {
|
|
|
9800
9801
|
scanElapsedMilliseconds?: number;
|
|
9801
9802
|
/**
|
|
9802
9803
|
* Per-file lint cache outcome: files served from cache, and total files
|
|
9803
|
-
* considered. Both absent when the cache was off or bypassed (
|
|
9804
|
-
*
|
|
9805
|
-
* wide event as `lint.cacheHitRatio`.
|
|
9804
|
+
* considered. Both absent when the cache was off or bypassed (adopted
|
|
9805
|
+
* `extends`, user plugins, React Compiler). The CLI projects these onto the
|
|
9806
|
+
* Sentry wide event as `lint.cacheHitRatio`.
|
|
9806
9807
|
*/
|
|
9807
9808
|
lintCacheHitFileCount?: number | null;
|
|
9808
9809
|
lintCacheTotalFileCount?: number | null;
|
|
@@ -9915,9 +9916,9 @@ interface JsonReportProjectEntry {
|
|
|
9915
9916
|
skippedCheckReasons?: Record<string, string>;
|
|
9916
9917
|
/**
|
|
9917
9918
|
* Number of source files this scan's linter examined. In diff / changed
|
|
9918
|
-
* mode it's the count of changed
|
|
9919
|
-
*
|
|
9920
|
-
*
|
|
9919
|
+
* mode it's the count of changed supported JS/TS source files; in a full
|
|
9920
|
+
* scan it's the whole source tree. `0` in a diff scan means the changed
|
|
9921
|
+
* files held nothing React Doctor lints —
|
|
9921
9922
|
* the GitHub Action reads that as "nothing to report" (skips the PR comment;
|
|
9922
9923
|
* the commit status says "skipped"). Optional: absent on reports from
|
|
9923
9924
|
* constructors that don't track it (e.g. `toJsonReport`).
|
|
@@ -10255,7 +10256,7 @@ declare const buildJsonReport: (input: BuildJsonReportInput) => JsonReportV3$1;
|
|
|
10255
10256
|
* render diff-base mistakes (`GitBaseBranchInvalid` /
|
|
10256
10257
|
* `GitBaseBranchMissing`) as clean user errors instead of crashes.
|
|
10257
10258
|
*/
|
|
10258
|
-
declare const getDiffInfo: (directory: string, explicitBaseBranch?: string) => Promise<DiffInfo | null>;
|
|
10259
|
+
declare const getDiffInfo: (directory: string, explicitBaseBranch?: string, includeUntracked?: boolean) => Promise<DiffInfo | null>;
|
|
10259
10260
|
declare const filterSourceFiles: (filePaths: string[]) => string[];
|
|
10260
10261
|
/**
|
|
10261
10262
|
* Programmatic façade over `Git.changedLineRanges` (the `lines` scope). Diffs
|