react-doctor 0.7.6-dev.3f22fe3 → 0.7.6-dev.4fb3694
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 +88 -273
- package/dist/index.d.ts +1 -1
- package/dist/index.js +64 -135
- package/dist/lsp.js +62 -133
- package/package.json +5 -5
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]="bc56f5c4-5b95-5727-8e86-d43a534abbbc")}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";
|
|
@@ -36341,12 +36341,10 @@ 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;
|
|
36346
36344
|
const cachedPackageJsons = /* @__PURE__ */ new Map();
|
|
36347
36345
|
const readPackageJsonUncached = (packageJsonPath) => {
|
|
36348
36346
|
try {
|
|
36349
|
-
return JSON.parse(
|
|
36347
|
+
return JSON.parse(NFS.readFileSync(packageJsonPath, "utf-8"));
|
|
36350
36348
|
} catch (error) {
|
|
36351
36349
|
if (error instanceof SyntaxError) return {};
|
|
36352
36350
|
if (isErrnoException(error)) {
|
|
@@ -39594,49 +39592,6 @@ const assignFixGroups = (diagnostics) => {
|
|
|
39594
39592
|
};
|
|
39595
39593
|
});
|
|
39596
39594
|
};
|
|
39597
|
-
const DERIVED_STATE_RULE_PRIORITY = new Map([
|
|
39598
|
-
["no-initialize-state", 0],
|
|
39599
|
-
["no-adjust-state-on-prop-change", 1],
|
|
39600
|
-
["no-derived-state", 2],
|
|
39601
|
-
["no-derived-state-effect", 3]
|
|
39602
|
-
]);
|
|
39603
|
-
const buildDiagnosticSiteKey = (diagnostic) => `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}`;
|
|
39604
|
-
const isReactDoctorRulesOfHooksDiagnostic = (diagnostic) => diagnostic.plugin === "react-doctor" && diagnostic.rule === "rules-of-hooks";
|
|
39605
|
-
const isReactCompilerHooksDiagnostic = (diagnostic) => diagnostic.plugin === "react-hooks-js" && diagnostic.rule === "hooks";
|
|
39606
|
-
const doDiagnosticSpansOverlap = (first, second) => {
|
|
39607
|
-
if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
|
|
39608
|
-
if (first.offset !== void 0 && second.offset !== void 0) {
|
|
39609
|
-
const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
|
|
39610
|
-
const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
|
|
39611
|
-
return first.offset < secondEnd && second.offset < firstEnd;
|
|
39612
|
-
}
|
|
39613
|
-
const firstEndLine = first.endLine ?? first.line;
|
|
39614
|
-
const secondEndLine = second.endLine ?? second.line;
|
|
39615
|
-
return first.line <= secondEndLine && second.line <= firstEndLine;
|
|
39616
|
-
};
|
|
39617
|
-
const keepHighestSeverity = (survivingDiagnostic, collapsedDiagnostic) => collapsedDiagnostic.severity === "error" && survivingDiagnostic.severity !== "error" ? {
|
|
39618
|
-
...survivingDiagnostic,
|
|
39619
|
-
severity: "error"
|
|
39620
|
-
} : survivingDiagnostic;
|
|
39621
|
-
const dedupeRelatedDiagnostics = (diagnostics) => {
|
|
39622
|
-
const reactDoctorHookSites = new Set(diagnostics.filter(isReactDoctorRulesOfHooksDiagnostic).map(buildDiagnosticSiteKey));
|
|
39623
|
-
const uniqueDiagnostics = [];
|
|
39624
|
-
for (const diagnostic of diagnostics) {
|
|
39625
|
-
if (isReactCompilerHooksDiagnostic(diagnostic) && reactDoctorHookSites.has(buildDiagnosticSiteKey(diagnostic))) continue;
|
|
39626
|
-
const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
|
|
39627
|
-
if (priority !== void 0) {
|
|
39628
|
-
const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
|
|
39629
|
-
if (overlappingDiagnosticIndex >= 0) {
|
|
39630
|
-
const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
|
|
39631
|
-
const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
|
|
39632
|
-
if (existingDiagnostic && existingPriority !== void 0) uniqueDiagnostics[overlappingDiagnosticIndex] = priority < existingPriority ? keepHighestSeverity(diagnostic, existingDiagnostic) : keepHighestSeverity(existingDiagnostic, diagnostic);
|
|
39633
|
-
continue;
|
|
39634
|
-
}
|
|
39635
|
-
}
|
|
39636
|
-
uniqueDiagnostics.push(diagnostic);
|
|
39637
|
-
}
|
|
39638
|
-
return uniqueDiagnostics;
|
|
39639
|
-
};
|
|
39640
39595
|
const compareStrings = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
39641
39596
|
const sortDiagnosticsStable = (diagnostics) => [...diagnostics].sort((left, right) => compareStrings(left.filePath, right.filePath) || left.line - right.line || left.column - right.column || compareStrings(left.plugin, right.plugin) || compareStrings(left.rule, right.rule) || compareStrings(left.severity, right.severity) || compareStrings(left.message, right.message));
|
|
39642
39597
|
const getDirectDependencyNames = (packageJson) => new Set([...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.devDependencies ?? {})]);
|
|
@@ -40062,6 +40017,7 @@ const PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
|
|
|
40062
40017
|
const PNPM_LOCKFILE = "pnpm-lock.yaml";
|
|
40063
40018
|
const PACKAGE_JSON_FILE = "package.json";
|
|
40064
40019
|
const PNPM_HARDENING_RULE_KEY = "require-pnpm-hardening";
|
|
40020
|
+
const UTF8_BOM_CHAR = "";
|
|
40065
40021
|
const HARDENING_SETTING_KEYS = new Set([
|
|
40066
40022
|
"minimumReleaseAge",
|
|
40067
40023
|
"blockExoticSubdeps",
|
|
@@ -40086,11 +40042,12 @@ const stripInlineComment = (rawValue) => {
|
|
|
40086
40042
|
return rawValue;
|
|
40087
40043
|
};
|
|
40088
40044
|
const unquote = (rawValue) => rawValue.replace(/^["']|["']$/g, "");
|
|
40045
|
+
const stripBom = (rawContent) => rawContent.startsWith(UTF8_BOM_CHAR) ? rawContent.slice(1) : rawContent;
|
|
40089
40046
|
const parseHardeningSettings = (content) => {
|
|
40090
40047
|
let minimumReleaseAge = null;
|
|
40091
40048
|
let blockExoticSubdeps = null;
|
|
40092
40049
|
let trustPolicy = null;
|
|
40093
|
-
const lines =
|
|
40050
|
+
const lines = stripBom(content).split(/\r?\n/);
|
|
40094
40051
|
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
40095
40052
|
const lineText = lines[lineIndex];
|
|
40096
40053
|
if (lineText === void 0) continue;
|
|
@@ -40124,7 +40081,7 @@ const isPnpmManagedProject = (rootDirectory) => {
|
|
|
40124
40081
|
const packageJsonPath = Path.join(rootDirectory, PACKAGE_JSON_FILE);
|
|
40125
40082
|
if (!isFile(packageJsonPath)) return false;
|
|
40126
40083
|
try {
|
|
40127
|
-
const packageJsonRaw =
|
|
40084
|
+
const packageJsonRaw = NFS.readFileSync(packageJsonPath, "utf-8");
|
|
40128
40085
|
const packageJson = JSON.parse(packageJsonRaw);
|
|
40129
40086
|
if (packageJson !== null && typeof packageJson === "object" && "packageManager" in packageJson && typeof packageJson.packageManager === "string" && packageJson.packageManager.startsWith("pnpm@")) return true;
|
|
40130
40087
|
} catch {
|
|
@@ -41866,7 +41823,6 @@ const parseGithubViewerPermission = (stdout) => {
|
|
|
41866
41823
|
return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
|
|
41867
41824
|
};
|
|
41868
41825
|
const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
|
|
41869
|
-
const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
|
|
41870
41826
|
/**
|
|
41871
41827
|
* `Git` wraps every `git`-via-subprocess call react-doctor makes
|
|
41872
41828
|
* behind a `Context.Service`. The production layer (`layerNode`)
|
|
@@ -41937,14 +41893,6 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
41937
41893
|
args,
|
|
41938
41894
|
directory
|
|
41939
41895
|
});
|
|
41940
|
-
const listUntrackedFilePaths = (directory, includePaths = []) => runGit(directory, [
|
|
41941
|
-
"ls-files",
|
|
41942
|
-
"-z",
|
|
41943
|
-
"--others",
|
|
41944
|
-
"--exclude-standard",
|
|
41945
|
-
...includePaths.length > 0 ? ["--", ...includePaths] : []
|
|
41946
|
-
]).pipe(map$3((result) => result.status === 0 ? splitNullSeparated(result.stdout) : null));
|
|
41947
|
-
const mergeUntracked = (directory, trackedFilePaths, includeUntracked) => includeUntracked ? listUntrackedFilePaths(directory).pipe(map$3((untracked) => untracked === null ? trackedFilePaths : [...new Set([...trackedFilePaths, ...untracked])])) : succeed$2(trackedFilePaths);
|
|
41948
41896
|
const currentBranch = (directory) => runGit(directory, [
|
|
41949
41897
|
"rev-parse",
|
|
41950
41898
|
"--abbrev-ref",
|
|
@@ -42067,7 +42015,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42067
42015
|
githubViewerPermission,
|
|
42068
42016
|
branchExists,
|
|
42069
42017
|
mergeBase,
|
|
42070
|
-
diffSelection: ({ directory, explicitBaseBranch
|
|
42018
|
+
diffSelection: ({ directory, explicitBaseBranch }) => gen(function* () {
|
|
42071
42019
|
if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
|
|
42072
42020
|
if (explicitBaseBranch !== void 0) {
|
|
42073
42021
|
const range = parseGitDiffRange(explicitBaseBranch);
|
|
@@ -42096,7 +42044,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42096
42044
|
"HEAD"
|
|
42097
42045
|
]);
|
|
42098
42046
|
if (uncommitted.status !== 0) return null;
|
|
42099
|
-
const files =
|
|
42047
|
+
const files = splitNullSeparated(uncommitted.stdout);
|
|
42100
42048
|
if (files.length === 0) return null;
|
|
42101
42049
|
return {
|
|
42102
42050
|
currentBranch: resolvedCurrentBranch,
|
|
@@ -42126,7 +42074,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42126
42074
|
currentBranch: resolvedCurrentBranch,
|
|
42127
42075
|
baseBranch,
|
|
42128
42076
|
diffBaseRef: mergeBaseRef,
|
|
42129
|
-
changedFiles:
|
|
42077
|
+
changedFiles: splitNullSeparated(diff.stdout),
|
|
42130
42078
|
isCurrentChanges: false
|
|
42131
42079
|
};
|
|
42132
42080
|
}).pipe(withSpan("Git.diffSelection")),
|
|
@@ -42172,7 +42120,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42172
42120
|
stdout: result.stdout
|
|
42173
42121
|
};
|
|
42174
42122
|
}).pipe(withSpan("Git.grep")),
|
|
42175
|
-
changedLineRanges: ({ directory, baseRef, cached, files
|
|
42123
|
+
changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
|
|
42176
42124
|
if (files.length === 0) return [];
|
|
42177
42125
|
if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
|
|
42178
42126
|
const result = yield* runGit(directory, [
|
|
@@ -42186,14 +42134,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42186
42134
|
...files
|
|
42187
42135
|
]);
|
|
42188
42136
|
if (result.status !== 0) return null;
|
|
42189
|
-
|
|
42190
|
-
if (cached || !includeUntracked) return changedLineRanges;
|
|
42191
|
-
const untrackedFilePaths = yield* listUntrackedFilePaths(directory, files);
|
|
42192
|
-
if (untrackedFilePaths === null) return changedLineRanges;
|
|
42193
|
-
return [...changedLineRanges, ...untrackedFilePaths.map((file) => ({
|
|
42194
|
-
file,
|
|
42195
|
-
ranges: [[1, UNTRACKED_FILE_LAST_LINE]]
|
|
42196
|
-
}))];
|
|
42137
|
+
return parseChangedLineRanges(result.stdout);
|
|
42197
42138
|
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
42198
42139
|
});
|
|
42199
42140
|
})).pipe(provide$2(layer$3.pipe(provide$2(mergeAll$1(layer$2, layer$1)))));
|
|
@@ -42778,13 +42719,13 @@ const encodeSidecarFileEntry = (entry) => ({
|
|
|
42778
42719
|
diagnostics: entry.diagnostics
|
|
42779
42720
|
});
|
|
42780
42721
|
const emptyCache = () => ({
|
|
42781
|
-
version:
|
|
42722
|
+
version: 1,
|
|
42782
42723
|
rulesets: {}
|
|
42783
42724
|
});
|
|
42784
42725
|
const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
|
|
42785
42726
|
const entries = /* @__PURE__ */ new Map();
|
|
42786
42727
|
const persisted = failOpenReadJson(cacheFilePath, emptyCache());
|
|
42787
|
-
if (persisted.version !==
|
|
42728
|
+
if (persisted.version !== 1 || !isRecord$1(persisted.rulesets)) return entries;
|
|
42788
42729
|
const bucket = persisted.rulesets[rulesetHash];
|
|
42789
42730
|
if (!isRecord$1(bucket) || !isRecord$1(bucket.files)) return entries;
|
|
42790
42731
|
for (const [fileKey, rawEntry] of Object.entries(bucket.files)) {
|
|
@@ -42804,7 +42745,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
42804
42745
|
},
|
|
42805
42746
|
persist: () => {
|
|
42806
42747
|
const onDisk = failOpenReadJson(cacheFilePath, emptyCache());
|
|
42807
|
-
const rulesets = onDisk.version ===
|
|
42748
|
+
const rulesets = onDisk.version === 1 && isRecord$1(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
|
|
42808
42749
|
const existingBucket = rulesets[rulesetHash];
|
|
42809
42750
|
const existingFiles = isRecord$1(existingBucket) && isRecord$1(existingBucket.files) ? existingBucket.files : {};
|
|
42810
42751
|
const ourFiles = {};
|
|
@@ -42818,7 +42759,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
42818
42759
|
files: Object.fromEntries(cappedEntries)
|
|
42819
42760
|
};
|
|
42820
42761
|
atomicWriteJson(cacheFilePath, {
|
|
42821
|
-
version:
|
|
42762
|
+
version: 1,
|
|
42822
42763
|
rulesets: Object.fromEntries(Object.entries(rulesets).filter(([, bucket]) => isRecord$1(bucket) && typeof bucket.updatedAtMs === "number").sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8))
|
|
42823
42764
|
});
|
|
42824
42765
|
}
|
|
@@ -42869,6 +42810,23 @@ const resolveTsConfigRelativePath = (rootDirectory) => {
|
|
|
42869
42810
|
for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
|
|
42870
42811
|
return null;
|
|
42871
42812
|
};
|
|
42813
|
+
const DERIVED_STATE_RULE_PRIORITY = new Map([
|
|
42814
|
+
["no-initialize-state", 0],
|
|
42815
|
+
["no-adjust-state-on-prop-change", 1],
|
|
42816
|
+
["no-derived-state", 2],
|
|
42817
|
+
["no-derived-state-effect", 3]
|
|
42818
|
+
]);
|
|
42819
|
+
const doDiagnosticSpansOverlap = (first, second) => {
|
|
42820
|
+
if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
|
|
42821
|
+
if (first.offset !== void 0 && second.offset !== void 0) {
|
|
42822
|
+
const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
|
|
42823
|
+
const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
|
|
42824
|
+
return first.offset < secondEnd && second.offset < firstEnd;
|
|
42825
|
+
}
|
|
42826
|
+
const firstEndLine = first.endLine ?? first.line;
|
|
42827
|
+
const secondEndLine = second.endLine ?? second.line;
|
|
42828
|
+
return first.line <= secondEndLine && second.line <= firstEndLine;
|
|
42829
|
+
};
|
|
42872
42830
|
const dedupeDiagnostics = (diagnostics) => {
|
|
42873
42831
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
42874
42832
|
const uniqueDiagnostics = [];
|
|
@@ -42876,6 +42834,16 @@ const dedupeDiagnostics = (diagnostics) => {
|
|
|
42876
42834
|
const key = `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}\u0000${diagnostic.plugin}\u0000${diagnostic.rule}\u0000${diagnostic.severity}\u0000${diagnostic.message}`;
|
|
42877
42835
|
if (seenKeys.has(key)) continue;
|
|
42878
42836
|
seenKeys.add(key);
|
|
42837
|
+
const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
|
|
42838
|
+
if (priority !== void 0) {
|
|
42839
|
+
const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && candidate.severity === diagnostic.severity && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
|
|
42840
|
+
if (overlappingDiagnosticIndex >= 0) {
|
|
42841
|
+
const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
|
|
42842
|
+
const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
|
|
42843
|
+
if (existingPriority !== void 0 && priority < existingPriority) uniqueDiagnostics[overlappingDiagnosticIndex] = diagnostic;
|
|
42844
|
+
continue;
|
|
42845
|
+
}
|
|
42846
|
+
}
|
|
42879
42847
|
uniqueDiagnostics.push(diagnostic);
|
|
42880
42848
|
}
|
|
42881
42849
|
return uniqueDiagnostics;
|
|
@@ -42918,12 +42886,6 @@ const mapWithConcurrency = async (items, concurrency, task) => {
|
|
|
42918
42886
|
if (errors.length > 0) throw errors[0];
|
|
42919
42887
|
return results;
|
|
42920
42888
|
};
|
|
42921
|
-
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
42922
|
-
let lineNumber = 1;
|
|
42923
|
-
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
42924
|
-
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === 10) lineNumber++;
|
|
42925
|
-
return lineNumber;
|
|
42926
|
-
};
|
|
42927
42889
|
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";
|
|
42928
42890
|
const ACCESSORS_MIN_VERSION = "3.15.0";
|
|
42929
42891
|
const supportsAccessors = (reanimatedVersion) => {
|
|
@@ -43495,6 +43457,13 @@ const findOutermostEnclosingFunctionRange = (sourceFile, targetLine) => {
|
|
|
43495
43457
|
visit(sourceFile);
|
|
43496
43458
|
return foundRange;
|
|
43497
43459
|
};
|
|
43460
|
+
const NEWLINE_BYTE = 10;
|
|
43461
|
+
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
43462
|
+
let lineNumber = 1;
|
|
43463
|
+
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
43464
|
+
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === NEWLINE_BYTE) lineNumber++;
|
|
43465
|
+
return lineNumber;
|
|
43466
|
+
};
|
|
43498
43467
|
/**
|
|
43499
43468
|
* Drops `react-doctor/react-compiler-no-manual-memoization` diagnostics
|
|
43500
43469
|
* whose enclosing function also carries a `react-hooks-js` (React
|
|
@@ -43675,11 +43644,11 @@ const buildRelatedLocations = (labels, filePath) => {
|
|
|
43675
43644
|
}
|
|
43676
43645
|
return related;
|
|
43677
43646
|
};
|
|
43678
|
-
const
|
|
43679
|
-
|
|
43680
|
-
const
|
|
43681
|
-
|
|
43682
|
-
|
|
43647
|
+
const isOxlintOutput = (value) => {
|
|
43648
|
+
if (typeof value !== "object" || value === null) return false;
|
|
43649
|
+
const candidate = value;
|
|
43650
|
+
return Array.isArray(candidate.diagnostics);
|
|
43651
|
+
};
|
|
43683
43652
|
/**
|
|
43684
43653
|
* Parses one oxlint subprocess's stdout into a flat `Diagnostic[]`.
|
|
43685
43654
|
* Tolerates the leading-notice-line shape oxlint sometimes prints
|
|
@@ -43698,59 +43667,23 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
43698
43667
|
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
43699
43668
|
}
|
|
43700
43669
|
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
43701
|
-
const engineFailureDiagnostic = parsed.diagnostics.find(isEngineFailureDiagnostic);
|
|
43702
|
-
if (engineFailureDiagnostic !== void 0) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: JSON.stringify(engineFailureDiagnostic).slice(0, 600) }) });
|
|
43703
43670
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
43704
|
-
const sourceBufferCache = /* @__PURE__ */ new Map();
|
|
43705
|
-
const sourceLinesCache = /* @__PURE__ */ new Map();
|
|
43706
|
-
const resolveAbsolutePath = (filename) => Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
43707
|
-
const readSourceBuffer = (filename) => {
|
|
43708
|
-
const absolutePath = resolveAbsolutePath(filename);
|
|
43709
|
-
const cached = sourceBufferCache.get(absolutePath);
|
|
43710
|
-
if (cached !== void 0) return cached;
|
|
43711
|
-
let sourceBuffer;
|
|
43712
|
-
try {
|
|
43713
|
-
sourceBuffer = NFS.readFileSync(absolutePath);
|
|
43714
|
-
} catch {
|
|
43715
|
-
sourceBuffer = null;
|
|
43716
|
-
}
|
|
43717
|
-
sourceBufferCache.set(absolutePath, sourceBuffer);
|
|
43718
|
-
return sourceBuffer;
|
|
43719
|
-
};
|
|
43720
|
-
const readSourceLines = (filename) => {
|
|
43721
|
-
const absolutePath = resolveAbsolutePath(filename);
|
|
43722
|
-
const cached = sourceLinesCache.get(absolutePath);
|
|
43723
|
-
if (cached !== void 0) return cached;
|
|
43724
|
-
const sourceBuffer = readSourceBuffer(filename);
|
|
43725
|
-
const sourceLines = sourceBuffer ? sourceBuffer.toString("utf8").split("\n") : null;
|
|
43726
|
-
sourceLinesCache.set(absolutePath, sourceLines);
|
|
43727
|
-
return sourceLines;
|
|
43728
|
-
};
|
|
43729
43671
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
43730
|
-
const absolutePath =
|
|
43672
|
+
const absolutePath = Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
43731
43673
|
const cached = minifiedFileCache.get(absolutePath);
|
|
43732
43674
|
if (cached !== void 0) return cached;
|
|
43733
43675
|
const minified = isMinifiedSource(absolutePath);
|
|
43734
43676
|
minifiedFileCache.set(absolutePath, minified);
|
|
43735
43677
|
return minified;
|
|
43736
43678
|
};
|
|
43737
|
-
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) =>
|
|
43679
|
+
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => diagnostic.code && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
|
|
43738
43680
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
43739
43681
|
const primaryLabel = diagnostic.labels[0];
|
|
43740
43682
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
43741
43683
|
const normalizedFilePath = diagnostic.filename.replaceAll("\\", "/");
|
|
43742
43684
|
const primarySpan = primaryLabel?.span;
|
|
43743
|
-
const sourceBuffer = primarySpan ? readSourceBuffer(diagnostic.filename) : null;
|
|
43744
43685
|
const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath);
|
|
43745
43686
|
const category = resolveDiagnosticCategory(plugin, rule);
|
|
43746
|
-
const matchByOccurrence = resolveMatchByOccurrence(rule, category);
|
|
43747
|
-
const sourceLines = primarySpan ? readSourceLines(diagnostic.filename) : null;
|
|
43748
|
-
const primaryLineIndex = primarySpan ? primarySpan.line - 1 : -1;
|
|
43749
|
-
const primaryLine = sourceLines?.[primaryLineIndex];
|
|
43750
|
-
const primaryColumnIndex = primarySpan ? primarySpan.column - 1 : -1;
|
|
43751
|
-
const isJsxTagLabel = primaryLine !== void 0 && primaryColumnIndex >= 0 && (primaryLine[primaryColumnIndex] === "<" || primaryLine[primaryColumnIndex - 1] === "<");
|
|
43752
|
-
const jsxOpenerEndLineIndex = sourceLines && isJsxTagLabel ? findJsxOpenerSpan(sourceLines, primaryLineIndex) : null;
|
|
43753
|
-
const primarySpanEndLine = jsxOpenerEndLineIndex !== null ? jsxOpenerEndLineIndex + 1 : primarySpan && sourceBuffer ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset + Math.max(primarySpan.length - 1, 0)) : void 0;
|
|
43754
43687
|
return {
|
|
43755
43688
|
filePath: normalizedFilePath,
|
|
43756
43689
|
plugin,
|
|
@@ -43764,11 +43697,10 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
43764
43697
|
column: primarySpan?.column ?? 0,
|
|
43765
43698
|
...primarySpan ? {
|
|
43766
43699
|
offset: primarySpan.offset,
|
|
43767
|
-
length: primarySpan.length
|
|
43768
|
-
...primarySpanEndLine !== void 0 ? { endLine: primarySpanEndLine } : {}
|
|
43700
|
+
length: primarySpan.length
|
|
43769
43701
|
} : {},
|
|
43770
43702
|
category,
|
|
43771
|
-
...
|
|
43703
|
+
...resolveMatchByOccurrence(rule, category) ? { matchByOccurrence: true } : {},
|
|
43772
43704
|
...relatedLocations.length > 0 ? { relatedLocations } : {}
|
|
43773
43705
|
};
|
|
43774
43706
|
}), rootDirectory);
|
|
@@ -45569,8 +45501,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
45569
45501
|
respectInlineDisables: input.respectInlineDisables,
|
|
45570
45502
|
showWarnings
|
|
45571
45503
|
});
|
|
45572
|
-
const
|
|
45573
|
-
const applyPerElementPipeline = (rawStream) => filterPerElementPipeline(rawStream).pipe(tap((diagnostic) => reporterService.emit(diagnostic)));
|
|
45504
|
+
const applyPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(filterMapNullable(transform.apply)), tap((diagnostic) => reporterService.emit(diagnostic)));
|
|
45574
45505
|
const envCollected = yield* runCollect(applyPerElementPipeline(fromIterable$1(isDiffMode ? [] : [
|
|
45575
45506
|
...checkReducedMotion(scanDirectory),
|
|
45576
45507
|
...checkPnpmHardening(scanDirectory),
|
|
@@ -45699,7 +45630,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
45699
45630
|
});
|
|
45700
45631
|
return empty$4;
|
|
45701
45632
|
}))));
|
|
45702
|
-
const lintCollected =
|
|
45633
|
+
const lintCollected = yield* runCollect(applyPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream)).pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
|
|
45703
45634
|
onNone: () => set(lintFailure, {
|
|
45704
45635
|
didFail: true,
|
|
45705
45636
|
reason: `Lint analysis exceeded ${lintPhaseTimeoutMs / MILLISECONDS_PER_SECOND}s and was skipped.`,
|
|
@@ -45707,8 +45638,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
45707
45638
|
reasonKind: null
|
|
45708
45639
|
}).pipe(as([])),
|
|
45709
45640
|
onSome: succeed$2
|
|
45710
|
-
})))
|
|
45711
|
-
yield* forEach$1(lintCollected, reporterService.emit, { discard: true });
|
|
45641
|
+
})));
|
|
45712
45642
|
const lintFailureState = yield* get$2(lintFailure);
|
|
45713
45643
|
yield* afterLint(lintFailureState.didFail);
|
|
45714
45644
|
if (lintFailureState.didFail) yield* scanProgress.fail(formatLintFailText(lintFailureState.reasonTag, process.version));
|
|
@@ -46302,11 +46232,10 @@ const computeDiagnosticDelta = (input) => {
|
|
|
46302
46232
|
* render diff-base mistakes (`GitBaseBranchInvalid` /
|
|
46303
46233
|
* `GitBaseBranchMissing`) as clean user errors instead of crashes.
|
|
46304
46234
|
*/
|
|
46305
|
-
const getDiffInfo = (directory, explicitBaseBranch
|
|
46235
|
+
const getDiffInfo = (directory, explicitBaseBranch) => runPromise(gen(function* () {
|
|
46306
46236
|
const selection = yield* (yield* Git).diffSelection({
|
|
46307
46237
|
directory,
|
|
46308
|
-
explicitBaseBranch
|
|
46309
|
-
includeUntracked
|
|
46238
|
+
explicitBaseBranch
|
|
46310
46239
|
});
|
|
46311
46240
|
if (selection === null) return null;
|
|
46312
46241
|
return {
|
|
@@ -46642,43 +46571,6 @@ const isNonInteractiveEnvironment = () => NON_INTERACTIVE_ENVIRONMENT_VARIABLES.
|
|
|
46642
46571
|
//#region src/cli/utils/constants.ts
|
|
46643
46572
|
const REACT_DOCTOR_CONFIG_PROJECT_NAME = "react-doctor";
|
|
46644
46573
|
const STAGED_FILES_TEMP_DIR_PREFIX = "react-doctor-staged-";
|
|
46645
|
-
const STAGED_SNAPSHOT_ADDITIONAL_CONFIG_FILENAMES = [
|
|
46646
|
-
".babelrc",
|
|
46647
|
-
".babelrc.json",
|
|
46648
|
-
".eslintignore",
|
|
46649
|
-
".eslintrc.json",
|
|
46650
|
-
".oxlintignore",
|
|
46651
|
-
"app.config.cjs",
|
|
46652
|
-
"app.config.js",
|
|
46653
|
-
"app.config.json",
|
|
46654
|
-
"app.config.mjs",
|
|
46655
|
-
"app.config.ts",
|
|
46656
|
-
"app.json",
|
|
46657
|
-
"babel.config.cjs",
|
|
46658
|
-
"babel.config.js",
|
|
46659
|
-
"babel.config.json",
|
|
46660
|
-
"babel.config.mjs",
|
|
46661
|
-
"eslint.config.cjs",
|
|
46662
|
-
"eslint.config.cts",
|
|
46663
|
-
"eslint.config.js",
|
|
46664
|
-
"eslint.config.mjs",
|
|
46665
|
-
"eslint.config.mts",
|
|
46666
|
-
"eslint.config.ts",
|
|
46667
|
-
"next.config.cjs",
|
|
46668
|
-
"next.config.js",
|
|
46669
|
-
"next.config.mjs",
|
|
46670
|
-
"next.config.ts",
|
|
46671
|
-
"oxlint.config.json",
|
|
46672
|
-
"react-doctor.config.json",
|
|
46673
|
-
"vite.config.cjs",
|
|
46674
|
-
"vite.config.cts",
|
|
46675
|
-
"vite.config.js",
|
|
46676
|
-
"vite.config.mjs",
|
|
46677
|
-
"vite.config.mts",
|
|
46678
|
-
"vite.config.ts",
|
|
46679
|
-
"vitest.config.js",
|
|
46680
|
-
"vitest.config.ts"
|
|
46681
|
-
];
|
|
46682
46574
|
const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
|
|
46683
46575
|
const SCAN_RESULT_CACHE_FILENAME = "scan-cache.json";
|
|
46684
46576
|
const RUN_GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
@@ -46693,7 +46585,6 @@ const METRIC = {
|
|
|
46693
46585
|
cliInvoked: "cli.invoked",
|
|
46694
46586
|
cliError: "cli.error",
|
|
46695
46587
|
cliEnvironmentError: "cli.env_error",
|
|
46696
|
-
stagedSnapshotDivergence: "staged.snapshot_divergence",
|
|
46697
46588
|
projectDetected: "project.detected",
|
|
46698
46589
|
projectPathSelected: "project.path_selected",
|
|
46699
46590
|
projectConfigSelected: "project.config_selected",
|
|
@@ -46774,7 +46665,7 @@ const makeNoopConsole = () => ({
|
|
|
46774
46665
|
});
|
|
46775
46666
|
//#endregion
|
|
46776
46667
|
//#region src/cli/utils/version.ts
|
|
46777
|
-
const VERSION = "0.7.6-dev.
|
|
46668
|
+
const VERSION = "0.7.6-dev.4fb3694";
|
|
46778
46669
|
//#endregion
|
|
46779
46670
|
//#region src/cli/utils/json-mode.ts
|
|
46780
46671
|
let context = null;
|
|
@@ -47143,13 +47034,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
47143
47034
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
47144
47035
|
* standard `SENTRY_RELEASE` override.
|
|
47145
47036
|
*/
|
|
47146
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.6-dev.
|
|
47037
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.6-dev.4fb3694`;
|
|
47147
47038
|
/**
|
|
47148
47039
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
47149
47040
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
47150
47041
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
47151
47042
|
*/
|
|
47152
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.6-dev.
|
|
47043
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.6-dev.4fb3694") ? "development" : "production");
|
|
47153
47044
|
/**
|
|
47154
47045
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
47155
47046
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -47341,7 +47232,7 @@ const NON_BLOCKING_REACT_DOCTOR_COMMAND = [
|
|
|
47341
47232
|
"fi"
|
|
47342
47233
|
].join(" ");
|
|
47343
47234
|
const PACKAGE_JSON_FILE_NAME = "package.json";
|
|
47344
|
-
const
|
|
47235
|
+
const runGit = (projectRoot, args) => {
|
|
47345
47236
|
try {
|
|
47346
47237
|
return execFileSync("git", [...args], {
|
|
47347
47238
|
cwd: projectRoot,
|
|
@@ -47352,12 +47243,11 @@ const runGitRaw = (projectRoot, args) => {
|
|
|
47352
47243
|
"ignore"
|
|
47353
47244
|
],
|
|
47354
47245
|
maxBuffer: RUN_GIT_MAX_BUFFER_BYTES
|
|
47355
|
-
});
|
|
47246
|
+
}).trim();
|
|
47356
47247
|
} catch {
|
|
47357
47248
|
return null;
|
|
47358
47249
|
}
|
|
47359
47250
|
};
|
|
47360
|
-
const runGit = (projectRoot, args) => runGitRaw(projectRoot, args)?.trim() ?? null;
|
|
47361
47251
|
const resolveGitPath = (baseDirectory, value) => Path.isAbsolute(value) ? value : Path.resolve(baseDirectory, value);
|
|
47362
47252
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
47363
47253
|
const getPackageJsonPath = (projectRoot) => Path.join(projectRoot, PACKAGE_JSON_FILE_NAME);
|
|
@@ -50915,7 +50805,7 @@ const BLOCKING_SUMMARY = {
|
|
|
50915
50805
|
const SCOPE_SUMMARY = {
|
|
50916
50806
|
full: "Scan the whole project on every run",
|
|
50917
50807
|
files: "Report every issue in changed files",
|
|
50918
|
-
lines: "Report issues
|
|
50808
|
+
lines: "Report issues on changed lines",
|
|
50919
50809
|
changed: "Report only the issues a change introduces"
|
|
50920
50810
|
};
|
|
50921
50811
|
const gatesEqual = (left, right) => left.blocking === right.blocking && left.scope === right.scope && left.comment === right.comment && left.reviewComments === right.reviewComments && left.commitStatus === right.commitStatus;
|
|
@@ -59228,17 +59118,6 @@ const buildRuleBlastRadii = (diagnostics) => buildSortedRuleGroups(diagnostics).
|
|
|
59228
59118
|
})).toSorted((left, right) => right.fileCount - left.fileCount);
|
|
59229
59119
|
const findMigrationScaleBuckets = (diagnostics) => buildRuleBlastRadii(diagnostics).filter((bucket) => bucket.fileCount >= 40);
|
|
59230
59120
|
//#endregion
|
|
59231
|
-
//#region src/cli/utils/has-lint-hard-failure.ts
|
|
59232
|
-
const hasLintHardFailure = (result) => result.skippedChecks.includes("lint");
|
|
59233
|
-
//#endregion
|
|
59234
|
-
//#region src/cli/utils/is-inspect-result-complete.ts
|
|
59235
|
-
const isInspectResultComplete = (result) => isScanComplete({
|
|
59236
|
-
analyzedFileCount: result.analyzedFiles?.length,
|
|
59237
|
-
scannedFileCount: result.scannedFileCount,
|
|
59238
|
-
skippedCheckCount: result.skippedChecks.length,
|
|
59239
|
-
skippedCheckReasonCount: Object.keys(result.skippedCheckReasons ?? {}).length
|
|
59240
|
-
});
|
|
59241
|
-
//#endregion
|
|
59242
59121
|
//#region src/cli/utils/scan-result-cache.ts
|
|
59243
59122
|
const CACHE_DISABLED_VALUES = new Set(["1", "true"]);
|
|
59244
59123
|
const TOOLCHAIN_PACKAGE_SPECIFIERS = [
|
|
@@ -59644,12 +59523,15 @@ const buildOutcomeAttributes = (input) => {
|
|
|
59644
59523
|
const summary = summarizeDiagnostics(result.diagnostics);
|
|
59645
59524
|
const blockingLevel = resolveTelemetryBlocking(input.userConfig);
|
|
59646
59525
|
const gateDiagnostics = filterDiagnosticsForSurface(result.diagnostics, "ciFailure", input.userConfig);
|
|
59647
|
-
const complete = isInspectResultComplete(result);
|
|
59648
|
-
const didLintHardFail = hasLintHardFailure(result);
|
|
59649
|
-
const failsOnHardFailure = didLintHardFail && blockingLevel !== "none";
|
|
59650
59526
|
const wouldBlock = !input.scoreOnly && !input.gateExempt && shouldBlockCi(gateDiagnostics, blockingLevel);
|
|
59527
|
+
const complete = isScanComplete({
|
|
59528
|
+
analyzedFileCount: result.analyzedFiles?.length,
|
|
59529
|
+
scannedFileCount: result.scannedFileCount,
|
|
59530
|
+
skippedCheckCount: result.skippedChecks.length,
|
|
59531
|
+
skippedCheckReasonCount: Object.keys(result.skippedCheckReasons ?? {}).length
|
|
59532
|
+
});
|
|
59651
59533
|
const isClean = result.diagnostics.length === 0 && complete;
|
|
59652
|
-
const outcome =
|
|
59534
|
+
const outcome = wouldBlock ? "blocked" : isClean ? "clean" : "ok";
|
|
59653
59535
|
const firings = summarizeRuleFirings(result.diagnostics);
|
|
59654
59536
|
const countByRule = /* @__PURE__ */ new Map();
|
|
59655
59537
|
const countByCategory = /* @__PURE__ */ new Map();
|
|
@@ -59694,7 +59576,7 @@ const buildOutcomeAttributes = (input) => {
|
|
|
59694
59576
|
const attributes = {
|
|
59695
59577
|
...withNamespace("outcome", {
|
|
59696
59578
|
status: outcome,
|
|
59697
|
-
exitCode:
|
|
59579
|
+
exitCode: wouldBlock ? 1 : 0,
|
|
59698
59580
|
wouldBlock,
|
|
59699
59581
|
blocking: blockingLevel,
|
|
59700
59582
|
clean: isClean,
|
|
@@ -59793,8 +59675,7 @@ const buildScanAttributes = (input) => {
|
|
|
59793
59675
|
rulesConfigured: ruleKeys.length,
|
|
59794
59676
|
rulesDisabled: ruleKeys.filter((key) => ruleOverrides[key] === "off").length,
|
|
59795
59677
|
fileCount: input.result?.scannedFileCount ?? null,
|
|
59796
|
-
nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length ?? null
|
|
59797
|
-
multilineDiagnosticCount: input.result?.diagnostics.filter((diagnostic) => (diagnostic.endLine ?? diagnostic.line) > diagnostic.line).length ?? null
|
|
59678
|
+
nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length ?? null
|
|
59798
59679
|
});
|
|
59799
59680
|
};
|
|
59800
59681
|
/**
|
|
@@ -59863,12 +59744,6 @@ const countDeadlineSkippedFiles = (lintPartialFailures) => sumFileCountsMatching
|
|
|
59863
59744
|
const DROPPED_FILES_MESSAGE_PATTERN = /^(\d+) file\(s\) failed to lint and were skipped/;
|
|
59864
59745
|
const countDroppedLintFiles = (lintPartialFailures) => sumFileCountsMatching(lintPartialFailures, DROPPED_FILES_MESSAGE_PATTERN);
|
|
59865
59746
|
//#endregion
|
|
59866
|
-
//#region src/cli/utils/diagnostic-intersects-line-ranges.ts
|
|
59867
|
-
const diagnosticIntersectsLineRanges = (diagnostic, lineRanges) => {
|
|
59868
|
-
const diagnosticEndLine = Math.max(diagnostic.line, diagnostic.endLine ?? diagnostic.line);
|
|
59869
|
-
return lineRanges.some(([rangeStartLine, rangeEndLine]) => diagnostic.line <= rangeEndLine && diagnosticEndLine >= rangeStartLine);
|
|
59870
|
-
};
|
|
59871
|
-
//#endregion
|
|
59872
59747
|
//#region src/cli/utils/materialize-baseline-files.ts
|
|
59873
59748
|
/**
|
|
59874
59749
|
* Materializes the `ref` version of `files` into `tempDirectory` (via
|
|
@@ -61054,7 +60929,7 @@ const buildChangedLineMatcher = (directory, changedLineRanges) => {
|
|
|
61054
60929
|
const relativePath = toForwardSlashes(path.isAbsolute(diagnostic.filePath) ? path.relative(directory, diagnostic.filePath) : diagnostic.filePath);
|
|
61055
60930
|
const ranges = rangesByFile.get(relativePath);
|
|
61056
60931
|
if (ranges === void 0) return false;
|
|
61057
|
-
return
|
|
60932
|
+
return ranges.some(([start, end]) => diagnostic.line >= start && diagnostic.line <= end);
|
|
61058
60933
|
};
|
|
61059
60934
|
};
|
|
61060
60935
|
const buildIgnoredTags = (userConfig) => {
|
|
@@ -63876,15 +63751,10 @@ const warnIfAiTrainingEnvironment = () => {
|
|
|
63876
63751
|
//#region src/cli/utils/validate-mode-flags.ts
|
|
63877
63752
|
const usedDiffAlias = (flags) => flags.diff !== void 0 && flags.diff !== false && flags.diff !== "false" && flags.diff !== "";
|
|
63878
63753
|
const usedScope = (flags) => typeof flags.scope === "string" && flags.scope.length > 0;
|
|
63879
|
-
const validateIncludeUntrackedScope = (includeUntracked, scope) => {
|
|
63880
|
-
if (!includeUntracked || scope !== void 0 && scope !== "full") return;
|
|
63881
|
-
throw new CliInputError("--include-untracked requires the files, changed, or lines scope (via --scope, --diff, or config).");
|
|
63882
|
-
};
|
|
63883
63754
|
const validateModeFlags = (flags) => {
|
|
63884
63755
|
if (usedScope(flags) && usedDiffAlias(flags)) throw new CliInputError("Cannot combine --scope and --diff; --diff is the deprecated alias.");
|
|
63885
63756
|
if (flags.staged && usedDiffAlias(flags)) throw new CliInputError("Cannot combine --staged and --diff; pick one mode.");
|
|
63886
63757
|
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.`);
|
|
63887
|
-
if (flags.includeUntracked && flags.staged) throw new CliInputError("Cannot combine --include-untracked with --staged; the git index never holds untracked files.");
|
|
63888
63758
|
if (flags.score && flags.json) throw new CliInputError("Cannot combine --score and --json; pick one output mode.");
|
|
63889
63759
|
if (flags.score && flags.telemetry === false) throw new CliInputError("Cannot combine --score with --no-telemetry; --score prints the score that --no-telemetry disables.");
|
|
63890
63760
|
if (flags.debug && (flags.score === false || flags.telemetry === false)) {
|
|
@@ -63893,39 +63763,6 @@ const validateModeFlags = (flags) => {
|
|
|
63893
63763
|
}
|
|
63894
63764
|
};
|
|
63895
63765
|
//#endregion
|
|
63896
|
-
//#region src/cli/utils/parse-staged-snapshot-divergences.ts
|
|
63897
|
-
const SNAPSHOT_CONFIG_FILENAMES = new Set([...STAGED_FILES_PROJECT_CONFIG_FILENAMES, ...STAGED_SNAPSHOT_ADDITIONAL_CONFIG_FILENAMES]);
|
|
63898
|
-
const parseStagedSnapshotDivergences = (statusOutput) => {
|
|
63899
|
-
const divergentConfigFiles = /* @__PURE__ */ new Set();
|
|
63900
|
-
const statusEntries = statusOutput.split("\0").filter((entry) => entry.length > 0);
|
|
63901
|
-
for (let entryIndex = 0; entryIndex < statusEntries.length; entryIndex += 1) {
|
|
63902
|
-
const entry = statusEntries[entryIndex];
|
|
63903
|
-
const [indexStatus, worktreeStatus] = entry;
|
|
63904
|
-
const filePath = entry.slice(3);
|
|
63905
|
-
const hasRenameOrCopySource = indexStatus === "R" || indexStatus === "C" || worktreeStatus === "R" || worktreeStatus === "C";
|
|
63906
|
-
const sourcePath = hasRenameOrCopySource ? statusEntries[entryIndex + 1] : void 0;
|
|
63907
|
-
const worktreePaths = worktreeStatus === "R" || worktreeStatus === "C" ? [filePath, sourcePath] : [filePath];
|
|
63908
|
-
if (worktreeStatus !== " ") {
|
|
63909
|
-
for (const worktreePath of worktreePaths) if (worktreePath && SNAPSHOT_CONFIG_FILENAMES.has(Path.basename(worktreePath))) divergentConfigFiles.add(worktreePath);
|
|
63910
|
-
}
|
|
63911
|
-
if (hasRenameOrCopySource) entryIndex += 1;
|
|
63912
|
-
}
|
|
63913
|
-
return [...divergentConfigFiles].sort();
|
|
63914
|
-
};
|
|
63915
|
-
//#endregion
|
|
63916
|
-
//#region src/cli/utils/find-staged-snapshot-divergences.ts
|
|
63917
|
-
const findStagedSnapshotDivergences = (directory) => {
|
|
63918
|
-
const statusOutput = runGitRaw(directory, [
|
|
63919
|
-
"status",
|
|
63920
|
-
"--porcelain=v1",
|
|
63921
|
-
"-z",
|
|
63922
|
-
"--untracked-files=all",
|
|
63923
|
-
"--ignored=matching"
|
|
63924
|
-
]);
|
|
63925
|
-
if (statusOutput === null) return null;
|
|
63926
|
-
return parseStagedSnapshotDivergences(statusOutput);
|
|
63927
|
-
};
|
|
63928
|
-
//#endregion
|
|
63929
63766
|
//#region src/cli/commands/inspect.ts
|
|
63930
63767
|
const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
|
|
63931
63768
|
if (categoryFilters.size === 0) return [...completedScans];
|
|
@@ -63940,14 +63777,9 @@ const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
|
|
|
63940
63777
|
/**
|
|
63941
63778
|
* Post-scan finalization shared by the staged-arm and project-loop
|
|
63942
63779
|
* paths of `inspectAction`: emit the JSON report (when in JSON mode)
|
|
63943
|
-
* and set `process.exitCode = 1` when
|
|
63944
|
-
* (
|
|
63945
|
-
*
|
|
63946
|
-
* threshold (default `"error"`) reaches the `ciFailure` surface.
|
|
63947
|
-
* `--blocking none` keeps the scan advisory (always exits 0), and
|
|
63948
|
-
* fail-open degradations — `--no-lint`, `--max-duration` truncation,
|
|
63949
|
-
* supply-chain/security skips — stay advisory too, surfaced through
|
|
63950
|
-
* `complete: false` in the JSON report.
|
|
63780
|
+
* and set `process.exitCode = 1` when a diagnostic at or above the
|
|
63781
|
+
* `--blocking` threshold (default `"error"`) reaches the `ciFailure`
|
|
63782
|
+
* surface. `--blocking none` keeps the scan advisory (always exits 0).
|
|
63951
63783
|
*/
|
|
63952
63784
|
const finalizeScans = (input) => {
|
|
63953
63785
|
const baselineDeltas = input.completedScans.flatMap((scan) => scan.result.baselineDelta ? [scan.result.baselineDelta] : []);
|
|
@@ -63977,13 +63809,8 @@ const finalizeScans = (input) => {
|
|
|
63977
63809
|
baselineDegraded
|
|
63978
63810
|
}));
|
|
63979
63811
|
}
|
|
63980
|
-
const blockingLevel = resolveBlockingLevel(input.flags, input.userConfig);
|
|
63981
|
-
if (input.completedScans.some(({ result }) => hasLintHardFailure(result)) && blockingLevel !== "none") {
|
|
63982
|
-
process.exitCode = 1;
|
|
63983
|
-
return;
|
|
63984
|
-
}
|
|
63985
63812
|
if (input.isScoreOnly || baselineDegraded) return;
|
|
63986
|
-
if (shouldBlockCi(filterScansForSurface(input.completedScans, "ciFailure"),
|
|
63813
|
+
if (shouldBlockCi(filterScansForSurface(input.completedScans, "ciFailure"), resolveBlockingLevel(input.flags, input.userConfig))) process.exitCode = 1;
|
|
63987
63814
|
};
|
|
63988
63815
|
const buildChangedFilesDiffInfo = (changedFiles) => ({
|
|
63989
63816
|
currentBranch: process.env.GITHUB_HEAD_REF?.trim() || null,
|
|
@@ -64021,7 +63848,6 @@ const inspectAction = async (directory, flags) => {
|
|
|
64021
63848
|
recordCount(METRIC.cliInvoked, 1, { command: "inspect" });
|
|
64022
63849
|
try {
|
|
64023
63850
|
validateModeFlags(flags);
|
|
64024
|
-
if (flags.staged) setJsonReportMode("staged");
|
|
64025
63851
|
await maybeMigrateLegacyConfig(requestedDirectory, {
|
|
64026
63852
|
isQuiet,
|
|
64027
63853
|
isStaged: Boolean(flags.staged)
|
|
@@ -64037,14 +63863,6 @@ const inspectAction = async (directory, flags) => {
|
|
|
64037
63863
|
cliLogger.dim(`Redirected to ${highlighter.info(toRelativePath(resolvedDirectory, requestedDirectory))} via react-doctor config "rootDir".`);
|
|
64038
63864
|
cliLogger.break();
|
|
64039
63865
|
}
|
|
64040
|
-
if (flags.staged) {
|
|
64041
|
-
const divergentConfigFiles = findStagedSnapshotDivergences(resolvedDirectory);
|
|
64042
|
-
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.");
|
|
64043
|
-
if (divergentConfigFiles.length > 0) {
|
|
64044
|
-
recordCount(METRIC.stagedSnapshotDivergence, 1, { divergentInputCount: divergentConfigFiles.length });
|
|
64045
|
-
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.`);
|
|
64046
|
-
}
|
|
64047
|
-
}
|
|
64048
63866
|
const explainArgument = flags.explain;
|
|
64049
63867
|
if (explainArgument !== void 0) {
|
|
64050
63868
|
await runExplain(explainArgument, {
|
|
@@ -64065,6 +63883,7 @@ const inspectAction = async (directory, flags) => {
|
|
|
64065
63883
|
json: flags.json
|
|
64066
63884
|
});
|
|
64067
63885
|
if (flags.staged) {
|
|
63886
|
+
setJsonReportMode("staged");
|
|
64068
63887
|
const stagedFiles = await getStagedSourceFiles(resolvedDirectory);
|
|
64069
63888
|
if (stagedFiles.length === 0) {
|
|
64070
63889
|
if (isJsonMode) writeJsonReport(buildJsonReport({
|
|
@@ -64145,15 +63964,13 @@ const inspectAction = async (directory, flags) => {
|
|
|
64145
63964
|
const projectDirectories = await selectProjects(resolvedDirectory, flags.project, skipPrompts, userConfig?.projects);
|
|
64146
63965
|
const changedFilesDiffInfo = flags.changedFilesFrom ? buildChangedFilesDiffInfo(readChangedFilesFrom(Path.resolve(flags.changedFilesFrom))) : null;
|
|
64147
63966
|
const requestedScope = resolveScope(flags, userConfig);
|
|
64148
|
-
const includeUntracked = flags.includeUntracked ?? false;
|
|
64149
63967
|
const scopeRequest = requestedScope.scope === void 0 && changedFilesDiffInfo !== null ? {
|
|
64150
63968
|
...requestedScope,
|
|
64151
63969
|
scope: "changed"
|
|
64152
63970
|
} : requestedScope;
|
|
64153
|
-
validateIncludeUntrackedScope(includeUntracked, scopeRequest.scope);
|
|
64154
63971
|
const wantsDiffMode = scopeRequest.scope !== void 0 && scopeRequest.scope !== "full";
|
|
64155
63972
|
const shouldDetectDiff = changedFilesDiffInfo === null && (wantsDiffMode || scopeRequest.scope === void 0 && !skipPrompts && !isQuiet);
|
|
64156
|
-
const diffInfo = changedFilesDiffInfo ?? (shouldDetectDiff ? await getDiffInfo(resolvedDirectory, scopeRequest.base
|
|
63973
|
+
const diffInfo = changedFilesDiffInfo ?? (shouldDetectDiff ? await getDiffInfo(resolvedDirectory, scopeRequest.base) : null);
|
|
64157
63974
|
const scope = await finalizeScope({
|
|
64158
63975
|
requested: scopeRequest,
|
|
64159
63976
|
diffInfo,
|
|
@@ -64167,8 +63984,7 @@ const inspectAction = async (directory, flags) => {
|
|
|
64167
63984
|
const changedLineRanges = scope === "lines" && diffInfo !== null && (diffInfo.isCurrentChanges || linesBaseRef !== null) && diffInfo !== null ? await getChangedLineRanges({
|
|
64168
63985
|
directory: resolvedDirectory,
|
|
64169
63986
|
baseRef: linesBaseRef ?? void 0,
|
|
64170
|
-
files: [...diffInfo.changedFiles]
|
|
64171
|
-
includeUntracked
|
|
63987
|
+
files: [...diffInfo.changedFiles]
|
|
64172
63988
|
}) : null;
|
|
64173
63989
|
if (scope === "lines" && changedLineRanges === null && !isQuiet) {
|
|
64174
63990
|
cliLogger.warn("Could not determine changed lines (no base ref or git diff failed); reporting all issues in changed files.");
|
|
@@ -65101,7 +64917,6 @@ const ROOT_FLAG_SPEC = {
|
|
|
65101
64917
|
"--dead-code",
|
|
65102
64918
|
"--debug",
|
|
65103
64919
|
"--help",
|
|
65104
|
-
"--include-untracked",
|
|
65105
64920
|
"--json",
|
|
65106
64921
|
"--json-compact",
|
|
65107
64922
|
"--lint",
|
|
@@ -65365,7 +65180,7 @@ ${highlighter.dim("Learn more:")}
|
|
|
65365
65180
|
${highlighter.info(CI_URL)}
|
|
65366
65181
|
`;
|
|
65367
65182
|
const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
|
|
65368
|
-
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 (
|
|
65183
|
+
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 (only 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);
|
|
65369
65184
|
program.action(inspectAction);
|
|
65370
65185
|
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));
|
|
65371
65186
|
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);
|
|
@@ -65415,4 +65230,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
65415
65230
|
export {};
|
|
65416
65231
|
|
|
65417
65232
|
//# sourceMappingURL=cli.js.map
|
|
65418
|
-
//# debugId=
|
|
65233
|
+
//# debugId=bc56f5c4-5b95-5727-8e86-d43a534abbbc
|