react-doctor 0.7.6-dev.8c9a6c4 → 0.7.6-dev.b36e439
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 +221 -54
- package/dist/index.d.ts +1 -1
- package/dist/index.js +83 -30
- package/dist/lsp.js +81 -28
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="58e54b02-7ba2-5f13-a535-98c236467a86")}catch(e){}}();
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import * as NodeChildProcess from "node:child_process";
|
|
5
5
|
import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
@@ -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 {
|
|
@@ -41823,6 +41823,7 @@ const parseGithubViewerPermission = (stdout) => {
|
|
|
41823
41823
|
return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
|
|
41824
41824
|
};
|
|
41825
41825
|
const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
|
|
41826
|
+
const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
|
|
41826
41827
|
/**
|
|
41827
41828
|
* `Git` wraps every `git`-via-subprocess call react-doctor makes
|
|
41828
41829
|
* behind a `Context.Service`. The production layer (`layerNode`)
|
|
@@ -41893,6 +41894,14 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
41893
41894
|
args,
|
|
41894
41895
|
directory
|
|
41895
41896
|
});
|
|
41897
|
+
const listUntrackedFilePaths = (directory, includePaths = []) => runGit(directory, [
|
|
41898
|
+
"ls-files",
|
|
41899
|
+
"-z",
|
|
41900
|
+
"--others",
|
|
41901
|
+
"--exclude-standard",
|
|
41902
|
+
...includePaths.length > 0 ? ["--", ...includePaths] : []
|
|
41903
|
+
]).pipe(map$3((result) => result.status === 0 ? splitNullSeparated(result.stdout) : null));
|
|
41904
|
+
const mergeUntracked = (directory, trackedFilePaths, includeUntracked) => includeUntracked ? listUntrackedFilePaths(directory).pipe(map$3((untracked) => untracked === null ? trackedFilePaths : [...new Set([...trackedFilePaths, ...untracked])])) : succeed$2(trackedFilePaths);
|
|
41896
41905
|
const currentBranch = (directory) => runGit(directory, [
|
|
41897
41906
|
"rev-parse",
|
|
41898
41907
|
"--abbrev-ref",
|
|
@@ -42015,7 +42024,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42015
42024
|
githubViewerPermission,
|
|
42016
42025
|
branchExists,
|
|
42017
42026
|
mergeBase,
|
|
42018
|
-
diffSelection: ({ directory, explicitBaseBranch }) => gen(function* () {
|
|
42027
|
+
diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
|
|
42019
42028
|
if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
|
|
42020
42029
|
if (explicitBaseBranch !== void 0) {
|
|
42021
42030
|
const range = parseGitDiffRange(explicitBaseBranch);
|
|
@@ -42044,7 +42053,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42044
42053
|
"HEAD"
|
|
42045
42054
|
]);
|
|
42046
42055
|
if (uncommitted.status !== 0) return null;
|
|
42047
|
-
const files = splitNullSeparated(uncommitted.stdout);
|
|
42056
|
+
const files = yield* mergeUntracked(directory, splitNullSeparated(uncommitted.stdout), includeUntracked);
|
|
42048
42057
|
if (files.length === 0) return null;
|
|
42049
42058
|
return {
|
|
42050
42059
|
currentBranch: resolvedCurrentBranch,
|
|
@@ -42074,7 +42083,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42074
42083
|
currentBranch: resolvedCurrentBranch,
|
|
42075
42084
|
baseBranch,
|
|
42076
42085
|
diffBaseRef: mergeBaseRef,
|
|
42077
|
-
changedFiles: splitNullSeparated(diff.stdout),
|
|
42086
|
+
changedFiles: yield* mergeUntracked(directory, splitNullSeparated(diff.stdout), includeUntracked),
|
|
42078
42087
|
isCurrentChanges: false
|
|
42079
42088
|
};
|
|
42080
42089
|
}).pipe(withSpan("Git.diffSelection")),
|
|
@@ -42120,7 +42129,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42120
42129
|
stdout: result.stdout
|
|
42121
42130
|
};
|
|
42122
42131
|
}).pipe(withSpan("Git.grep")),
|
|
42123
|
-
changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
|
|
42132
|
+
changedLineRanges: ({ directory, baseRef, cached, files, includeUntracked = false }) => gen(function* () {
|
|
42124
42133
|
if (files.length === 0) return [];
|
|
42125
42134
|
if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
|
|
42126
42135
|
const result = yield* runGit(directory, [
|
|
@@ -42134,7 +42143,14 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
42134
42143
|
...files
|
|
42135
42144
|
]);
|
|
42136
42145
|
if (result.status !== 0) return null;
|
|
42137
|
-
|
|
42146
|
+
const changedLineRanges = parseChangedLineRanges(result.stdout);
|
|
42147
|
+
if (cached || !includeUntracked) return changedLineRanges;
|
|
42148
|
+
const untrackedFilePaths = yield* listUntrackedFilePaths(directory, files);
|
|
42149
|
+
if (untrackedFilePaths === null) return changedLineRanges;
|
|
42150
|
+
return [...changedLineRanges, ...untrackedFilePaths.map((file) => ({
|
|
42151
|
+
file,
|
|
42152
|
+
ranges: [[1, UNTRACKED_FILE_LAST_LINE]]
|
|
42153
|
+
}))];
|
|
42138
42154
|
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
42139
42155
|
});
|
|
42140
42156
|
})).pipe(provide$2(layer$3.pipe(provide$2(mergeAll$1(layer$2, layer$1)))));
|
|
@@ -42886,6 +42902,12 @@ const mapWithConcurrency = async (items, concurrency, task) => {
|
|
|
42886
42902
|
if (errors.length > 0) throw errors[0];
|
|
42887
42903
|
return results;
|
|
42888
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
|
+
};
|
|
42889
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";
|
|
42890
42912
|
const ACCESSORS_MIN_VERSION = "3.15.0";
|
|
42891
42913
|
const supportsAccessors = (reanimatedVersion) => {
|
|
@@ -43457,13 +43479,6 @@ const findOutermostEnclosingFunctionRange = (sourceFile, targetLine) => {
|
|
|
43457
43479
|
visit(sourceFile);
|
|
43458
43480
|
return foundRange;
|
|
43459
43481
|
};
|
|
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
|
-
};
|
|
43467
43482
|
/**
|
|
43468
43483
|
* Drops `react-doctor/react-compiler-no-manual-memoization` diagnostics
|
|
43469
43484
|
* whose enclosing function also carries a `react-hooks-js` (React
|
|
@@ -43644,11 +43659,11 @@ const buildRelatedLocations = (labels, filePath) => {
|
|
|
43644
43659
|
}
|
|
43645
43660
|
return related;
|
|
43646
43661
|
};
|
|
43647
|
-
const
|
|
43648
|
-
|
|
43649
|
-
|
|
43650
|
-
|
|
43651
|
-
|
|
43662
|
+
const isOxlintSpan = (value) => isRecord$1(value) && typeof value.offset === "number" && typeof value.length === "number" && typeof value.line === "number" && typeof value.column === "number";
|
|
43663
|
+
const isOxlintLabel = (value) => isRecord$1(value) && isOxlintSpan(value.span);
|
|
43664
|
+
const isMappableOxlintDiagnostic = (value) => isRecord$1(value) && typeof value.code === "string" && value.code.length > 0 && typeof value.filename === "string" && value.filename.length > 0 && (value.severity === "warning" || value.severity === "error") && Array.isArray(value.labels) && value.labels.every(isOxlintLabel);
|
|
43665
|
+
const isEngineFailureDiagnostic = (value) => !isRecord$1(value) || typeof value.filename !== "string" || value.filename.length === 0;
|
|
43666
|
+
const isOxlintOutput = (value) => isRecord$1(value) && Array.isArray(value.diagnostics);
|
|
43652
43667
|
/**
|
|
43653
43668
|
* Parses one oxlint subprocess's stdout into a flat `Diagnostic[]`.
|
|
43654
43669
|
* Tolerates the leading-notice-line shape oxlint sometimes prints
|
|
@@ -43667,23 +43682,59 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
43667
43682
|
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
43668
43683
|
}
|
|
43669
43684
|
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
43685
|
+
const engineFailureDiagnostic = parsed.diagnostics.find(isEngineFailureDiagnostic);
|
|
43686
|
+
if (engineFailureDiagnostic !== void 0) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: JSON.stringify(engineFailureDiagnostic).slice(0, 600) }) });
|
|
43670
43687
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
43688
|
+
const sourceBufferCache = /* @__PURE__ */ new Map();
|
|
43689
|
+
const sourceLinesCache = /* @__PURE__ */ new Map();
|
|
43690
|
+
const resolveAbsolutePath = (filename) => Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
43691
|
+
const readSourceBuffer = (filename) => {
|
|
43692
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
43693
|
+
const cached = sourceBufferCache.get(absolutePath);
|
|
43694
|
+
if (cached !== void 0) return cached;
|
|
43695
|
+
let sourceBuffer;
|
|
43696
|
+
try {
|
|
43697
|
+
sourceBuffer = NFS.readFileSync(absolutePath);
|
|
43698
|
+
} catch {
|
|
43699
|
+
sourceBuffer = null;
|
|
43700
|
+
}
|
|
43701
|
+
sourceBufferCache.set(absolutePath, sourceBuffer);
|
|
43702
|
+
return sourceBuffer;
|
|
43703
|
+
};
|
|
43704
|
+
const readSourceLines = (filename) => {
|
|
43705
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
43706
|
+
const cached = sourceLinesCache.get(absolutePath);
|
|
43707
|
+
if (cached !== void 0) return cached;
|
|
43708
|
+
const sourceBuffer = readSourceBuffer(filename);
|
|
43709
|
+
const sourceLines = sourceBuffer ? sourceBuffer.toString("utf8").split("\n") : null;
|
|
43710
|
+
sourceLinesCache.set(absolutePath, sourceLines);
|
|
43711
|
+
return sourceLines;
|
|
43712
|
+
};
|
|
43671
43713
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
43672
|
-
const absolutePath =
|
|
43714
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
43673
43715
|
const cached = minifiedFileCache.get(absolutePath);
|
|
43674
43716
|
if (cached !== void 0) return cached;
|
|
43675
43717
|
const minified = isMinifiedSource(absolutePath);
|
|
43676
43718
|
minifiedFileCache.set(absolutePath, minified);
|
|
43677
43719
|
return minified;
|
|
43678
43720
|
};
|
|
43679
|
-
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => diagnostic
|
|
43721
|
+
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
|
|
43680
43722
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
43681
43723
|
const primaryLabel = diagnostic.labels[0];
|
|
43682
43724
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
43683
43725
|
const normalizedFilePath = diagnostic.filename.replaceAll("\\", "/");
|
|
43684
43726
|
const primarySpan = primaryLabel?.span;
|
|
43727
|
+
const sourceBuffer = primarySpan ? readSourceBuffer(diagnostic.filename) : null;
|
|
43685
43728
|
const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath);
|
|
43686
43729
|
const category = resolveDiagnosticCategory(plugin, rule);
|
|
43730
|
+
const matchByOccurrence = resolveMatchByOccurrence(rule, category);
|
|
43731
|
+
const sourceLines = primarySpan ? readSourceLines(diagnostic.filename) : null;
|
|
43732
|
+
const primaryLineIndex = primarySpan ? primarySpan.line - 1 : -1;
|
|
43733
|
+
const primaryLine = sourceLines?.[primaryLineIndex];
|
|
43734
|
+
const primaryColumnIndex = primarySpan ? primarySpan.column - 1 : -1;
|
|
43735
|
+
const isJsxTagLabel = primaryLine !== void 0 && primaryColumnIndex >= 0 && (primaryLine[primaryColumnIndex] === "<" || primaryLine[primaryColumnIndex - 1] === "<");
|
|
43736
|
+
const jsxOpenerEndLineIndex = sourceLines && isJsxTagLabel ? findJsxOpenerSpan(sourceLines, primaryLineIndex) : null;
|
|
43737
|
+
const primarySpanEndLine = jsxOpenerEndLineIndex !== null ? jsxOpenerEndLineIndex + 1 : primarySpan && sourceBuffer ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset + Math.max(primarySpan.length - 1, 0)) : void 0;
|
|
43687
43738
|
return {
|
|
43688
43739
|
filePath: normalizedFilePath,
|
|
43689
43740
|
plugin,
|
|
@@ -43697,10 +43748,11 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
43697
43748
|
column: primarySpan?.column ?? 0,
|
|
43698
43749
|
...primarySpan ? {
|
|
43699
43750
|
offset: primarySpan.offset,
|
|
43700
|
-
length: primarySpan.length
|
|
43751
|
+
length: primarySpan.length,
|
|
43752
|
+
...primarySpanEndLine !== void 0 ? { endLine: primarySpanEndLine } : {}
|
|
43701
43753
|
} : {},
|
|
43702
43754
|
category,
|
|
43703
|
-
...
|
|
43755
|
+
...matchByOccurrence ? { matchByOccurrence: true } : {},
|
|
43704
43756
|
...relatedLocations.length > 0 ? { relatedLocations } : {}
|
|
43705
43757
|
};
|
|
43706
43758
|
}), rootDirectory);
|
|
@@ -46232,10 +46284,11 @@ const computeDiagnosticDelta = (input) => {
|
|
|
46232
46284
|
* render diff-base mistakes (`GitBaseBranchInvalid` /
|
|
46233
46285
|
* `GitBaseBranchMissing`) as clean user errors instead of crashes.
|
|
46234
46286
|
*/
|
|
46235
|
-
const getDiffInfo = (directory, explicitBaseBranch) => runPromise(gen(function* () {
|
|
46287
|
+
const getDiffInfo = (directory, explicitBaseBranch, includeUntracked) => runPromise(gen(function* () {
|
|
46236
46288
|
const selection = yield* (yield* Git).diffSelection({
|
|
46237
46289
|
directory,
|
|
46238
|
-
explicitBaseBranch
|
|
46290
|
+
explicitBaseBranch,
|
|
46291
|
+
includeUntracked
|
|
46239
46292
|
});
|
|
46240
46293
|
if (selection === null) return null;
|
|
46241
46294
|
return {
|
|
@@ -46571,6 +46624,43 @@ const isNonInteractiveEnvironment = () => NON_INTERACTIVE_ENVIRONMENT_VARIABLES.
|
|
|
46571
46624
|
//#region src/cli/utils/constants.ts
|
|
46572
46625
|
const REACT_DOCTOR_CONFIG_PROJECT_NAME = "react-doctor";
|
|
46573
46626
|
const STAGED_FILES_TEMP_DIR_PREFIX = "react-doctor-staged-";
|
|
46627
|
+
const STAGED_SNAPSHOT_ADDITIONAL_CONFIG_FILENAMES = [
|
|
46628
|
+
".babelrc",
|
|
46629
|
+
".babelrc.json",
|
|
46630
|
+
".eslintignore",
|
|
46631
|
+
".eslintrc.json",
|
|
46632
|
+
".oxlintignore",
|
|
46633
|
+
"app.config.cjs",
|
|
46634
|
+
"app.config.js",
|
|
46635
|
+
"app.config.json",
|
|
46636
|
+
"app.config.mjs",
|
|
46637
|
+
"app.config.ts",
|
|
46638
|
+
"app.json",
|
|
46639
|
+
"babel.config.cjs",
|
|
46640
|
+
"babel.config.js",
|
|
46641
|
+
"babel.config.json",
|
|
46642
|
+
"babel.config.mjs",
|
|
46643
|
+
"eslint.config.cjs",
|
|
46644
|
+
"eslint.config.cts",
|
|
46645
|
+
"eslint.config.js",
|
|
46646
|
+
"eslint.config.mjs",
|
|
46647
|
+
"eslint.config.mts",
|
|
46648
|
+
"eslint.config.ts",
|
|
46649
|
+
"next.config.cjs",
|
|
46650
|
+
"next.config.js",
|
|
46651
|
+
"next.config.mjs",
|
|
46652
|
+
"next.config.ts",
|
|
46653
|
+
"oxlint.config.json",
|
|
46654
|
+
"react-doctor.config.json",
|
|
46655
|
+
"vite.config.cjs",
|
|
46656
|
+
"vite.config.cts",
|
|
46657
|
+
"vite.config.js",
|
|
46658
|
+
"vite.config.mjs",
|
|
46659
|
+
"vite.config.mts",
|
|
46660
|
+
"vite.config.ts",
|
|
46661
|
+
"vitest.config.js",
|
|
46662
|
+
"vitest.config.ts"
|
|
46663
|
+
];
|
|
46574
46664
|
const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
|
|
46575
46665
|
const SCAN_RESULT_CACHE_FILENAME = "scan-cache.json";
|
|
46576
46666
|
const RUN_GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
@@ -46585,6 +46675,7 @@ const METRIC = {
|
|
|
46585
46675
|
cliInvoked: "cli.invoked",
|
|
46586
46676
|
cliError: "cli.error",
|
|
46587
46677
|
cliEnvironmentError: "cli.env_error",
|
|
46678
|
+
stagedSnapshotDivergence: "staged.snapshot_divergence",
|
|
46588
46679
|
projectDetected: "project.detected",
|
|
46589
46680
|
projectPathSelected: "project.path_selected",
|
|
46590
46681
|
projectConfigSelected: "project.config_selected",
|
|
@@ -46665,7 +46756,7 @@ const makeNoopConsole = () => ({
|
|
|
46665
46756
|
});
|
|
46666
46757
|
//#endregion
|
|
46667
46758
|
//#region src/cli/utils/version.ts
|
|
46668
|
-
const VERSION = "0.7.6-dev.
|
|
46759
|
+
const VERSION = "0.7.6-dev.b36e439";
|
|
46669
46760
|
//#endregion
|
|
46670
46761
|
//#region src/cli/utils/json-mode.ts
|
|
46671
46762
|
let context = null;
|
|
@@ -47034,13 +47125,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
47034
47125
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
47035
47126
|
* standard `SENTRY_RELEASE` override.
|
|
47036
47127
|
*/
|
|
47037
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.6-dev.
|
|
47128
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.6-dev.b36e439`;
|
|
47038
47129
|
/**
|
|
47039
47130
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
47040
47131
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
47041
47132
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
47042
47133
|
*/
|
|
47043
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.6-dev.
|
|
47134
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.6-dev.b36e439") ? "development" : "production");
|
|
47044
47135
|
/**
|
|
47045
47136
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
47046
47137
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -47232,7 +47323,7 @@ const NON_BLOCKING_REACT_DOCTOR_COMMAND = [
|
|
|
47232
47323
|
"fi"
|
|
47233
47324
|
].join(" ");
|
|
47234
47325
|
const PACKAGE_JSON_FILE_NAME = "package.json";
|
|
47235
|
-
const
|
|
47326
|
+
const runGitRaw = (projectRoot, args) => {
|
|
47236
47327
|
try {
|
|
47237
47328
|
return execFileSync("git", [...args], {
|
|
47238
47329
|
cwd: projectRoot,
|
|
@@ -47243,11 +47334,12 @@ const runGit = (projectRoot, args) => {
|
|
|
47243
47334
|
"ignore"
|
|
47244
47335
|
],
|
|
47245
47336
|
maxBuffer: RUN_GIT_MAX_BUFFER_BYTES
|
|
47246
|
-
})
|
|
47337
|
+
});
|
|
47247
47338
|
} catch {
|
|
47248
47339
|
return null;
|
|
47249
47340
|
}
|
|
47250
47341
|
};
|
|
47342
|
+
const runGit = (projectRoot, args) => runGitRaw(projectRoot, args)?.trim() ?? null;
|
|
47251
47343
|
const resolveGitPath = (baseDirectory, value) => Path.isAbsolute(value) ? value : Path.resolve(baseDirectory, value);
|
|
47252
47344
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
47253
47345
|
const getPackageJsonPath = (projectRoot) => Path.join(projectRoot, PACKAGE_JSON_FILE_NAME);
|
|
@@ -50805,7 +50897,7 @@ const BLOCKING_SUMMARY = {
|
|
|
50805
50897
|
const SCOPE_SUMMARY = {
|
|
50806
50898
|
full: "Scan the whole project on every run",
|
|
50807
50899
|
files: "Report every issue in changed files",
|
|
50808
|
-
lines: "Report issues
|
|
50900
|
+
lines: "Report issues whose source spans touch changed lines",
|
|
50809
50901
|
changed: "Report only the issues a change introduces"
|
|
50810
50902
|
};
|
|
50811
50903
|
const gatesEqual = (left, right) => left.blocking === right.blocking && left.scope === right.scope && left.comment === right.comment && left.reviewComments === right.reviewComments && left.commitStatus === right.commitStatus;
|
|
@@ -59118,6 +59210,17 @@ const buildRuleBlastRadii = (diagnostics) => buildSortedRuleGroups(diagnostics).
|
|
|
59118
59210
|
})).toSorted((left, right) => right.fileCount - left.fileCount);
|
|
59119
59211
|
const findMigrationScaleBuckets = (diagnostics) => buildRuleBlastRadii(diagnostics).filter((bucket) => bucket.fileCount >= 40);
|
|
59120
59212
|
//#endregion
|
|
59213
|
+
//#region src/cli/utils/has-lint-hard-failure.ts
|
|
59214
|
+
const hasLintHardFailure = (result) => result.skippedChecks.includes("lint");
|
|
59215
|
+
//#endregion
|
|
59216
|
+
//#region src/cli/utils/is-inspect-result-complete.ts
|
|
59217
|
+
const isInspectResultComplete = (result) => isScanComplete({
|
|
59218
|
+
analyzedFileCount: result.analyzedFiles?.length,
|
|
59219
|
+
scannedFileCount: result.scannedFileCount,
|
|
59220
|
+
skippedCheckCount: result.skippedChecks.length,
|
|
59221
|
+
skippedCheckReasonCount: Object.keys(result.skippedCheckReasons ?? {}).length
|
|
59222
|
+
});
|
|
59223
|
+
//#endregion
|
|
59121
59224
|
//#region src/cli/utils/scan-result-cache.ts
|
|
59122
59225
|
const CACHE_DISABLED_VALUES = new Set(["1", "true"]);
|
|
59123
59226
|
const TOOLCHAIN_PACKAGE_SPECIFIERS = [
|
|
@@ -59523,15 +59626,12 @@ const buildOutcomeAttributes = (input) => {
|
|
|
59523
59626
|
const summary = summarizeDiagnostics(result.diagnostics);
|
|
59524
59627
|
const blockingLevel = resolveTelemetryBlocking(input.userConfig);
|
|
59525
59628
|
const gateDiagnostics = filterDiagnosticsForSurface(result.diagnostics, "ciFailure", input.userConfig);
|
|
59629
|
+
const complete = isInspectResultComplete(result);
|
|
59630
|
+
const didLintHardFail = hasLintHardFailure(result);
|
|
59631
|
+
const failsOnHardFailure = didLintHardFail && blockingLevel !== "none";
|
|
59526
59632
|
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
|
-
});
|
|
59533
59633
|
const isClean = result.diagnostics.length === 0 && complete;
|
|
59534
|
-
const outcome = wouldBlock ? "blocked" : isClean ? "clean" : "ok";
|
|
59634
|
+
const outcome = didLintHardFail ? "error" : wouldBlock ? "blocked" : isClean ? "clean" : "ok";
|
|
59535
59635
|
const firings = summarizeRuleFirings(result.diagnostics);
|
|
59536
59636
|
const countByRule = /* @__PURE__ */ new Map();
|
|
59537
59637
|
const countByCategory = /* @__PURE__ */ new Map();
|
|
@@ -59576,7 +59676,7 @@ const buildOutcomeAttributes = (input) => {
|
|
|
59576
59676
|
const attributes = {
|
|
59577
59677
|
...withNamespace("outcome", {
|
|
59578
59678
|
status: outcome,
|
|
59579
|
-
exitCode: wouldBlock ? 1 : 0,
|
|
59679
|
+
exitCode: failsOnHardFailure || wouldBlock ? 1 : 0,
|
|
59580
59680
|
wouldBlock,
|
|
59581
59681
|
blocking: blockingLevel,
|
|
59582
59682
|
clean: isClean,
|
|
@@ -59675,7 +59775,8 @@ const buildScanAttributes = (input) => {
|
|
|
59675
59775
|
rulesConfigured: ruleKeys.length,
|
|
59676
59776
|
rulesDisabled: ruleKeys.filter((key) => ruleOverrides[key] === "off").length,
|
|
59677
59777
|
fileCount: input.result?.scannedFileCount ?? null,
|
|
59678
|
-
nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length ?? null
|
|
59778
|
+
nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length ?? null,
|
|
59779
|
+
multilineDiagnosticCount: input.result?.diagnostics.filter((diagnostic) => (diagnostic.endLine ?? diagnostic.line) > diagnostic.line).length ?? null
|
|
59679
59780
|
});
|
|
59680
59781
|
};
|
|
59681
59782
|
/**
|
|
@@ -59744,6 +59845,12 @@ const countDeadlineSkippedFiles = (lintPartialFailures) => sumFileCountsMatching
|
|
|
59744
59845
|
const DROPPED_FILES_MESSAGE_PATTERN = /^(\d+) file\(s\) failed to lint and were skipped/;
|
|
59745
59846
|
const countDroppedLintFiles = (lintPartialFailures) => sumFileCountsMatching(lintPartialFailures, DROPPED_FILES_MESSAGE_PATTERN);
|
|
59746
59847
|
//#endregion
|
|
59848
|
+
//#region src/cli/utils/diagnostic-intersects-line-ranges.ts
|
|
59849
|
+
const diagnosticIntersectsLineRanges = (diagnostic, lineRanges) => {
|
|
59850
|
+
const diagnosticEndLine = Math.max(diagnostic.line, diagnostic.endLine ?? diagnostic.line);
|
|
59851
|
+
return lineRanges.some(([rangeStartLine, rangeEndLine]) => diagnostic.line <= rangeEndLine && diagnosticEndLine >= rangeStartLine);
|
|
59852
|
+
};
|
|
59853
|
+
//#endregion
|
|
59747
59854
|
//#region src/cli/utils/materialize-baseline-files.ts
|
|
59748
59855
|
/**
|
|
59749
59856
|
* Materializes the `ref` version of `files` into `tempDirectory` (via
|
|
@@ -60929,7 +61036,7 @@ const buildChangedLineMatcher = (directory, changedLineRanges) => {
|
|
|
60929
61036
|
const relativePath = toForwardSlashes(path.isAbsolute(diagnostic.filePath) ? path.relative(directory, diagnostic.filePath) : diagnostic.filePath);
|
|
60930
61037
|
const ranges = rangesByFile.get(relativePath);
|
|
60931
61038
|
if (ranges === void 0) return false;
|
|
60932
|
-
return
|
|
61039
|
+
return diagnosticIntersectsLineRanges(diagnostic, ranges);
|
|
60933
61040
|
};
|
|
60934
61041
|
};
|
|
60935
61042
|
const buildIgnoredTags = (userConfig) => {
|
|
@@ -63751,10 +63858,15 @@ const warnIfAiTrainingEnvironment = () => {
|
|
|
63751
63858
|
//#region src/cli/utils/validate-mode-flags.ts
|
|
63752
63859
|
const usedDiffAlias = (flags) => flags.diff !== void 0 && flags.diff !== false && flags.diff !== "false" && flags.diff !== "";
|
|
63753
63860
|
const usedScope = (flags) => typeof flags.scope === "string" && flags.scope.length > 0;
|
|
63861
|
+
const validateIncludeUntrackedScope = (includeUntracked, scope) => {
|
|
63862
|
+
if (!includeUntracked || scope !== void 0 && scope !== "full") return;
|
|
63863
|
+
throw new CliInputError("--include-untracked requires the files, changed, or lines scope (via --scope, --diff, or config).");
|
|
63864
|
+
};
|
|
63754
63865
|
const validateModeFlags = (flags) => {
|
|
63755
63866
|
if (usedScope(flags) && usedDiffAlias(flags)) throw new CliInputError("Cannot combine --scope and --diff; --diff is the deprecated alias.");
|
|
63756
63867
|
if (flags.staged && usedDiffAlias(flags)) throw new CliInputError("Cannot combine --staged and --diff; pick one mode.");
|
|
63757
63868
|
if (flags.staged && (flags.scope === "full" || flags.scope === "changed")) throw new CliInputError(`Cannot combine --staged with --scope ${flags.scope}; use --scope files or --scope lines, or drop --scope.`);
|
|
63869
|
+
if (flags.includeUntracked && flags.staged) throw new CliInputError("Cannot combine --include-untracked with --staged; the git index never holds untracked files.");
|
|
63758
63870
|
if (flags.score && flags.json) throw new CliInputError("Cannot combine --score and --json; pick one output mode.");
|
|
63759
63871
|
if (flags.score && flags.telemetry === false) throw new CliInputError("Cannot combine --score with --no-telemetry; --score prints the score that --no-telemetry disables.");
|
|
63760
63872
|
if (flags.debug && (flags.score === false || flags.telemetry === false)) {
|
|
@@ -63763,6 +63875,39 @@ const validateModeFlags = (flags) => {
|
|
|
63763
63875
|
}
|
|
63764
63876
|
};
|
|
63765
63877
|
//#endregion
|
|
63878
|
+
//#region src/cli/utils/parse-staged-snapshot-divergences.ts
|
|
63879
|
+
const SNAPSHOT_CONFIG_FILENAMES = new Set([...STAGED_FILES_PROJECT_CONFIG_FILENAMES, ...STAGED_SNAPSHOT_ADDITIONAL_CONFIG_FILENAMES]);
|
|
63880
|
+
const parseStagedSnapshotDivergences = (statusOutput) => {
|
|
63881
|
+
const divergentConfigFiles = /* @__PURE__ */ new Set();
|
|
63882
|
+
const statusEntries = statusOutput.split("\0").filter((entry) => entry.length > 0);
|
|
63883
|
+
for (let entryIndex = 0; entryIndex < statusEntries.length; entryIndex += 1) {
|
|
63884
|
+
const entry = statusEntries[entryIndex];
|
|
63885
|
+
const [indexStatus, worktreeStatus] = entry;
|
|
63886
|
+
const filePath = entry.slice(3);
|
|
63887
|
+
const hasRenameOrCopySource = indexStatus === "R" || indexStatus === "C" || worktreeStatus === "R" || worktreeStatus === "C";
|
|
63888
|
+
const sourcePath = hasRenameOrCopySource ? statusEntries[entryIndex + 1] : void 0;
|
|
63889
|
+
const worktreePaths = worktreeStatus === "R" || worktreeStatus === "C" ? [filePath, sourcePath] : [filePath];
|
|
63890
|
+
if (worktreeStatus !== " ") {
|
|
63891
|
+
for (const worktreePath of worktreePaths) if (worktreePath && SNAPSHOT_CONFIG_FILENAMES.has(Path.basename(worktreePath))) divergentConfigFiles.add(worktreePath);
|
|
63892
|
+
}
|
|
63893
|
+
if (hasRenameOrCopySource) entryIndex += 1;
|
|
63894
|
+
}
|
|
63895
|
+
return [...divergentConfigFiles].sort();
|
|
63896
|
+
};
|
|
63897
|
+
//#endregion
|
|
63898
|
+
//#region src/cli/utils/find-staged-snapshot-divergences.ts
|
|
63899
|
+
const findStagedSnapshotDivergences = (directory) => {
|
|
63900
|
+
const statusOutput = runGitRaw(directory, [
|
|
63901
|
+
"status",
|
|
63902
|
+
"--porcelain=v1",
|
|
63903
|
+
"-z",
|
|
63904
|
+
"--untracked-files=all",
|
|
63905
|
+
"--ignored=matching"
|
|
63906
|
+
]);
|
|
63907
|
+
if (statusOutput === null) return null;
|
|
63908
|
+
return parseStagedSnapshotDivergences(statusOutput);
|
|
63909
|
+
};
|
|
63910
|
+
//#endregion
|
|
63766
63911
|
//#region src/cli/commands/inspect.ts
|
|
63767
63912
|
const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
|
|
63768
63913
|
if (categoryFilters.size === 0) return [...completedScans];
|
|
@@ -63777,9 +63922,14 @@ const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
|
|
|
63777
63922
|
/**
|
|
63778
63923
|
* Post-scan finalization shared by the staged-arm and project-loop
|
|
63779
63924
|
* paths of `inspectAction`: emit the JSON report (when in JSON mode)
|
|
63780
|
-
* and set `process.exitCode = 1` when
|
|
63781
|
-
*
|
|
63782
|
-
*
|
|
63925
|
+
* and set `process.exitCode = 1` when any scan's lint pass hard-failed
|
|
63926
|
+
* (an engine/plugin/binding failure destroys the findings, so success
|
|
63927
|
+
* would be a false clean) or a diagnostic at or above the `--blocking`
|
|
63928
|
+
* threshold (default `"error"`) reaches the `ciFailure` surface.
|
|
63929
|
+
* `--blocking none` keeps the scan advisory (always exits 0), and
|
|
63930
|
+
* fail-open degradations — `--no-lint`, `--max-duration` truncation,
|
|
63931
|
+
* supply-chain/security skips — stay advisory too, surfaced through
|
|
63932
|
+
* `complete: false` in the JSON report.
|
|
63783
63933
|
*/
|
|
63784
63934
|
const finalizeScans = (input) => {
|
|
63785
63935
|
const baselineDeltas = input.completedScans.flatMap((scan) => scan.result.baselineDelta ? [scan.result.baselineDelta] : []);
|
|
@@ -63809,8 +63959,13 @@ const finalizeScans = (input) => {
|
|
|
63809
63959
|
baselineDegraded
|
|
63810
63960
|
}));
|
|
63811
63961
|
}
|
|
63962
|
+
const blockingLevel = resolveBlockingLevel(input.flags, input.userConfig);
|
|
63963
|
+
if (input.completedScans.some(({ result }) => hasLintHardFailure(result)) && blockingLevel !== "none") {
|
|
63964
|
+
process.exitCode = 1;
|
|
63965
|
+
return;
|
|
63966
|
+
}
|
|
63812
63967
|
if (input.isScoreOnly || baselineDegraded) return;
|
|
63813
|
-
if (shouldBlockCi(filterScansForSurface(input.completedScans, "ciFailure"),
|
|
63968
|
+
if (shouldBlockCi(filterScansForSurface(input.completedScans, "ciFailure"), blockingLevel)) process.exitCode = 1;
|
|
63814
63969
|
};
|
|
63815
63970
|
const buildChangedFilesDiffInfo = (changedFiles) => ({
|
|
63816
63971
|
currentBranch: process.env.GITHUB_HEAD_REF?.trim() || null,
|
|
@@ -63848,6 +64003,7 @@ const inspectAction = async (directory, flags) => {
|
|
|
63848
64003
|
recordCount(METRIC.cliInvoked, 1, { command: "inspect" });
|
|
63849
64004
|
try {
|
|
63850
64005
|
validateModeFlags(flags);
|
|
64006
|
+
if (flags.staged) setJsonReportMode("staged");
|
|
63851
64007
|
await maybeMigrateLegacyConfig(requestedDirectory, {
|
|
63852
64008
|
isQuiet,
|
|
63853
64009
|
isStaged: Boolean(flags.staged)
|
|
@@ -63863,6 +64019,14 @@ const inspectAction = async (directory, flags) => {
|
|
|
63863
64019
|
cliLogger.dim(`Redirected to ${highlighter.info(toRelativePath(resolvedDirectory, requestedDirectory))} via react-doctor config "rootDir".`);
|
|
63864
64020
|
cliLogger.break();
|
|
63865
64021
|
}
|
|
64022
|
+
if (flags.staged) {
|
|
64023
|
+
const divergentConfigFiles = findStagedSnapshotDivergences(resolvedDirectory);
|
|
64024
|
+
if (divergentConfigFiles === null) throw new CliInputError("Could not verify that staged configuration matches the worktree. Run the command from a Git worktree with Git available.");
|
|
64025
|
+
if (divergentConfigFiles.length > 0) {
|
|
64026
|
+
recordCount(METRIC.stagedSnapshotDivergence, 1, { divergentInputCount: divergentConfigFiles.length });
|
|
64027
|
+
throw new CliInputError(`Cannot scan staged files while configuration differs between the index and worktree: ${divergentConfigFiles.join(", ")}. Stage or restore those files, then rerun react-doctor --staged.`);
|
|
64028
|
+
}
|
|
64029
|
+
}
|
|
63866
64030
|
const explainArgument = flags.explain;
|
|
63867
64031
|
if (explainArgument !== void 0) {
|
|
63868
64032
|
await runExplain(explainArgument, {
|
|
@@ -63883,7 +64047,6 @@ const inspectAction = async (directory, flags) => {
|
|
|
63883
64047
|
json: flags.json
|
|
63884
64048
|
});
|
|
63885
64049
|
if (flags.staged) {
|
|
63886
|
-
setJsonReportMode("staged");
|
|
63887
64050
|
const stagedFiles = await getStagedSourceFiles(resolvedDirectory);
|
|
63888
64051
|
if (stagedFiles.length === 0) {
|
|
63889
64052
|
if (isJsonMode) writeJsonReport(buildJsonReport({
|
|
@@ -63964,13 +64127,15 @@ const inspectAction = async (directory, flags) => {
|
|
|
63964
64127
|
const projectDirectories = await selectProjects(resolvedDirectory, flags.project, skipPrompts, userConfig?.projects);
|
|
63965
64128
|
const changedFilesDiffInfo = flags.changedFilesFrom ? buildChangedFilesDiffInfo(readChangedFilesFrom(Path.resolve(flags.changedFilesFrom))) : null;
|
|
63966
64129
|
const requestedScope = resolveScope(flags, userConfig);
|
|
64130
|
+
const includeUntracked = flags.includeUntracked ?? false;
|
|
63967
64131
|
const scopeRequest = requestedScope.scope === void 0 && changedFilesDiffInfo !== null ? {
|
|
63968
64132
|
...requestedScope,
|
|
63969
64133
|
scope: "changed"
|
|
63970
64134
|
} : requestedScope;
|
|
64135
|
+
validateIncludeUntrackedScope(includeUntracked, scopeRequest.scope);
|
|
63971
64136
|
const wantsDiffMode = scopeRequest.scope !== void 0 && scopeRequest.scope !== "full";
|
|
63972
64137
|
const shouldDetectDiff = changedFilesDiffInfo === null && (wantsDiffMode || scopeRequest.scope === void 0 && !skipPrompts && !isQuiet);
|
|
63973
|
-
const diffInfo = changedFilesDiffInfo ?? (shouldDetectDiff ? await getDiffInfo(resolvedDirectory, scopeRequest.base) : null);
|
|
64138
|
+
const diffInfo = changedFilesDiffInfo ?? (shouldDetectDiff ? await getDiffInfo(resolvedDirectory, scopeRequest.base, includeUntracked) : null);
|
|
63974
64139
|
const scope = await finalizeScope({
|
|
63975
64140
|
requested: scopeRequest,
|
|
63976
64141
|
diffInfo,
|
|
@@ -63984,7 +64149,8 @@ const inspectAction = async (directory, flags) => {
|
|
|
63984
64149
|
const changedLineRanges = scope === "lines" && diffInfo !== null && (diffInfo.isCurrentChanges || linesBaseRef !== null) && diffInfo !== null ? await getChangedLineRanges({
|
|
63985
64150
|
directory: resolvedDirectory,
|
|
63986
64151
|
baseRef: linesBaseRef ?? void 0,
|
|
63987
|
-
files: [...diffInfo.changedFiles]
|
|
64152
|
+
files: [...diffInfo.changedFiles],
|
|
64153
|
+
includeUntracked
|
|
63988
64154
|
}) : null;
|
|
63989
64155
|
if (scope === "lines" && changedLineRanges === null && !isQuiet) {
|
|
63990
64156
|
cliLogger.warn("Could not determine changed lines (no base ref or git diff failed); reporting all issues in changed files.");
|
|
@@ -64917,6 +65083,7 @@ const ROOT_FLAG_SPEC = {
|
|
|
64917
65083
|
"--dead-code",
|
|
64918
65084
|
"--debug",
|
|
64919
65085
|
"--help",
|
|
65086
|
+
"--include-untracked",
|
|
64920
65087
|
"--json",
|
|
64921
65088
|
"--json-compact",
|
|
64922
65089
|
"--lint",
|
|
@@ -65180,7 +65347,7 @@ ${highlighter.dim("Learn more:")}
|
|
|
65180
65347
|
${highlighter.info(CI_URL)}
|
|
65181
65348
|
`;
|
|
65182
65349
|
const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
|
|
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 (
|
|
65350
|
+
const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much supported JS/TS source to scan/report: full (default), files, changed (only new issues vs base), or lines (issues whose source spans touch changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").option("--include-untracked", "with --scope files/changed/lines, also scan ordinary untracked files (respects .gitignore)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks").option("--max-duration <seconds>", "scan time budget for the whole run, shared across workspace projects: past it, remaining lint batches and dead-code are skipped and partial results are reported (skipped files are listed in the JSON report)").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
|
|
65184
65351
|
program.action(inspectAction);
|
|
65185
65352
|
program.command("why <location>").description("Explain why a rule fired (or why a suppression didn't apply) at a file:line").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple)").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").action((location, options) => whyAction(location, options));
|
|
65186
65353
|
program.command("install").alias("setup").description("Install the react-doctor skill into your coding agents and optional git hook").option("-y, --yes", "skip prompts, install for all detected agents").option("--dry-run", "show what would be installed without writing files").option("--agent-hooks", "install native non-blocking agent hooks for Claude Code and Cursor").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderInstallHelpEpilog).action(installAction);
|
|
@@ -65230,4 +65397,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
65230
65397
|
export {};
|
|
65231
65398
|
|
|
65232
65399
|
//# sourceMappingURL=cli.js.map
|
|
65233
|
-
//# debugId=
|
|
65400
|
+
//# debugId=58e54b02-7ba2-5f13-a535-98c236467a86
|
package/dist/index.d.ts
CHANGED
|
@@ -10256,7 +10256,7 @@ declare const buildJsonReport: (input: BuildJsonReportInput) => JsonReportV3$1;
|
|
|
10256
10256
|
* render diff-base mistakes (`GitBaseBranchInvalid` /
|
|
10257
10257
|
* `GitBaseBranchMissing`) as clean user errors instead of crashes.
|
|
10258
10258
|
*/
|
|
10259
|
-
declare const getDiffInfo: (directory: string, explicitBaseBranch?: string) => Promise<DiffInfo | null>;
|
|
10259
|
+
declare const getDiffInfo: (directory: string, explicitBaseBranch?: string, includeUntracked?: boolean) => Promise<DiffInfo | null>;
|
|
10260
10260
|
declare const filterSourceFiles: (filePaths: string[]) => string[];
|
|
10261
10261
|
/**
|
|
10262
10262
|
* Programmatic façade over `Git.changedLineRanges` (the `lines` scope). Diffs
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="2aa6fc3c-96ab-5a4c-8a52-e74d27fb4b5a")}catch(e){}}();
|
|
3
3
|
import { r as __toESM$1, t as __commonJSMin$1 } from "./chunk-N93fKeF6.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, FRAMEWORK_TOKENS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
|
|
@@ -33142,13 +33142,15 @@ const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
|
33142
33142
|
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort());
|
|
33143
33143
|
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
33144
33144
|
const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
|
|
33145
|
+
const UTF8_BOM_CHARACTER = "";
|
|
33146
|
+
const stripUtf8Bom = (content) => content.startsWith(UTF8_BOM_CHARACTER) ? content.slice(1) : content;
|
|
33145
33147
|
const cachedPackageJsons = /* @__PURE__ */ new Map();
|
|
33146
33148
|
const clearPackageJsonCache = () => {
|
|
33147
33149
|
cachedPackageJsons.clear();
|
|
33148
33150
|
};
|
|
33149
33151
|
const readPackageJsonUncached = (packageJsonPath) => {
|
|
33150
33152
|
try {
|
|
33151
|
-
return JSON.parse(NFS.readFileSync(packageJsonPath, "utf-8"));
|
|
33153
|
+
return JSON.parse(stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8")));
|
|
33152
33154
|
} catch (error) {
|
|
33153
33155
|
if (error instanceof SyntaxError) return {};
|
|
33154
33156
|
if (isErrnoException(error)) {
|
|
@@ -36789,7 +36791,6 @@ const PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
|
|
|
36789
36791
|
const PNPM_LOCKFILE = "pnpm-lock.yaml";
|
|
36790
36792
|
const PACKAGE_JSON_FILE = "package.json";
|
|
36791
36793
|
const PNPM_HARDENING_RULE_KEY = "require-pnpm-hardening";
|
|
36792
|
-
const UTF8_BOM_CHAR = "";
|
|
36793
36794
|
const HARDENING_SETTING_KEYS = new Set([
|
|
36794
36795
|
"minimumReleaseAge",
|
|
36795
36796
|
"blockExoticSubdeps",
|
|
@@ -36814,12 +36815,11 @@ const stripInlineComment = (rawValue) => {
|
|
|
36814
36815
|
return rawValue;
|
|
36815
36816
|
};
|
|
36816
36817
|
const unquote = (rawValue) => rawValue.replace(/^["']|["']$/g, "");
|
|
36817
|
-
const stripBom = (rawContent) => rawContent.startsWith(UTF8_BOM_CHAR) ? rawContent.slice(1) : rawContent;
|
|
36818
36818
|
const parseHardeningSettings = (content) => {
|
|
36819
36819
|
let minimumReleaseAge = null;
|
|
36820
36820
|
let blockExoticSubdeps = null;
|
|
36821
36821
|
let trustPolicy = null;
|
|
36822
|
-
const lines =
|
|
36822
|
+
const lines = stripUtf8Bom(content).split(/\r?\n/);
|
|
36823
36823
|
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
36824
36824
|
const lineText = lines[lineIndex];
|
|
36825
36825
|
if (lineText === void 0) continue;
|
|
@@ -36853,7 +36853,7 @@ const isPnpmManagedProject = (rootDirectory) => {
|
|
|
36853
36853
|
const packageJsonPath = Path.join(rootDirectory, PACKAGE_JSON_FILE);
|
|
36854
36854
|
if (!isFile(packageJsonPath)) return false;
|
|
36855
36855
|
try {
|
|
36856
|
-
const packageJsonRaw = NFS.readFileSync(packageJsonPath, "utf-8");
|
|
36856
|
+
const packageJsonRaw = stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8"));
|
|
36857
36857
|
const packageJson = JSON.parse(packageJsonRaw);
|
|
36858
36858
|
if (packageJson !== null && typeof packageJson === "object" && "packageManager" in packageJson && typeof packageJson.packageManager === "string" && packageJson.packageManager.startsWith("pnpm@")) return true;
|
|
36859
36859
|
} catch {
|
|
@@ -38579,6 +38579,7 @@ const parseGithubViewerPermission = (stdout) => {
|
|
|
38579
38579
|
return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
|
|
38580
38580
|
};
|
|
38581
38581
|
const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
|
|
38582
|
+
const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
|
|
38582
38583
|
/**
|
|
38583
38584
|
* `Git` wraps every `git`-via-subprocess call react-doctor makes
|
|
38584
38585
|
* behind a `Context.Service`. The production layer (`layerNode`)
|
|
@@ -38649,6 +38650,14 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38649
38650
|
args,
|
|
38650
38651
|
directory
|
|
38651
38652
|
});
|
|
38653
|
+
const listUntrackedFilePaths = (directory, includePaths = []) => runGit(directory, [
|
|
38654
|
+
"ls-files",
|
|
38655
|
+
"-z",
|
|
38656
|
+
"--others",
|
|
38657
|
+
"--exclude-standard",
|
|
38658
|
+
...includePaths.length > 0 ? ["--", ...includePaths] : []
|
|
38659
|
+
]).pipe(map$3((result) => result.status === 0 ? splitNullSeparated(result.stdout) : null));
|
|
38660
|
+
const mergeUntracked = (directory, trackedFilePaths, includeUntracked) => includeUntracked ? listUntrackedFilePaths(directory).pipe(map$3((untracked) => untracked === null ? trackedFilePaths : [...new Set([...trackedFilePaths, ...untracked])])) : succeed$2(trackedFilePaths);
|
|
38652
38661
|
const currentBranch = (directory) => runGit(directory, [
|
|
38653
38662
|
"rev-parse",
|
|
38654
38663
|
"--abbrev-ref",
|
|
@@ -38771,7 +38780,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38771
38780
|
githubViewerPermission,
|
|
38772
38781
|
branchExists,
|
|
38773
38782
|
mergeBase,
|
|
38774
|
-
diffSelection: ({ directory, explicitBaseBranch }) => gen(function* () {
|
|
38783
|
+
diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
|
|
38775
38784
|
if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
|
|
38776
38785
|
if (explicitBaseBranch !== void 0) {
|
|
38777
38786
|
const range = parseGitDiffRange(explicitBaseBranch);
|
|
@@ -38800,7 +38809,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38800
38809
|
"HEAD"
|
|
38801
38810
|
]);
|
|
38802
38811
|
if (uncommitted.status !== 0) return null;
|
|
38803
|
-
const files = splitNullSeparated(uncommitted.stdout);
|
|
38812
|
+
const files = yield* mergeUntracked(directory, splitNullSeparated(uncommitted.stdout), includeUntracked);
|
|
38804
38813
|
if (files.length === 0) return null;
|
|
38805
38814
|
return {
|
|
38806
38815
|
currentBranch: resolvedCurrentBranch,
|
|
@@ -38830,7 +38839,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38830
38839
|
currentBranch: resolvedCurrentBranch,
|
|
38831
38840
|
baseBranch,
|
|
38832
38841
|
diffBaseRef: mergeBaseRef,
|
|
38833
|
-
changedFiles: splitNullSeparated(diff.stdout),
|
|
38842
|
+
changedFiles: yield* mergeUntracked(directory, splitNullSeparated(diff.stdout), includeUntracked),
|
|
38834
38843
|
isCurrentChanges: false
|
|
38835
38844
|
};
|
|
38836
38845
|
}).pipe(withSpan("Git.diffSelection")),
|
|
@@ -38876,7 +38885,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38876
38885
|
stdout: result.stdout
|
|
38877
38886
|
};
|
|
38878
38887
|
}).pipe(withSpan("Git.grep")),
|
|
38879
|
-
changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
|
|
38888
|
+
changedLineRanges: ({ directory, baseRef, cached, files, includeUntracked = false }) => gen(function* () {
|
|
38880
38889
|
if (files.length === 0) return [];
|
|
38881
38890
|
if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
|
|
38882
38891
|
const result = yield* runGit(directory, [
|
|
@@ -38890,7 +38899,14 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38890
38899
|
...files
|
|
38891
38900
|
]);
|
|
38892
38901
|
if (result.status !== 0) return null;
|
|
38893
|
-
|
|
38902
|
+
const changedLineRanges = parseChangedLineRanges(result.stdout);
|
|
38903
|
+
if (cached || !includeUntracked) return changedLineRanges;
|
|
38904
|
+
const untrackedFilePaths = yield* listUntrackedFilePaths(directory, files);
|
|
38905
|
+
if (untrackedFilePaths === null) return changedLineRanges;
|
|
38906
|
+
return [...changedLineRanges, ...untrackedFilePaths.map((file) => ({
|
|
38907
|
+
file,
|
|
38908
|
+
ranges: [[1, UNTRACKED_FILE_LAST_LINE]]
|
|
38909
|
+
}))];
|
|
38894
38910
|
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
38895
38911
|
});
|
|
38896
38912
|
})).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
|
|
@@ -39642,6 +39658,12 @@ const mapWithConcurrency = async (items, concurrency, task) => {
|
|
|
39642
39658
|
if (errors.length > 0) throw errors[0];
|
|
39643
39659
|
return results;
|
|
39644
39660
|
};
|
|
39661
|
+
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
39662
|
+
let lineNumber = 1;
|
|
39663
|
+
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
39664
|
+
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === 10) lineNumber++;
|
|
39665
|
+
return lineNumber;
|
|
39666
|
+
};
|
|
39645
39667
|
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";
|
|
39646
39668
|
const ACCESSORS_MIN_VERSION = "3.15.0";
|
|
39647
39669
|
const supportsAccessors = (reanimatedVersion) => {
|
|
@@ -40213,13 +40235,6 @@ const findOutermostEnclosingFunctionRange = (sourceFile, targetLine) => {
|
|
|
40213
40235
|
visit(sourceFile);
|
|
40214
40236
|
return foundRange;
|
|
40215
40237
|
};
|
|
40216
|
-
const NEWLINE_BYTE = 10;
|
|
40217
|
-
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
40218
|
-
let lineNumber = 1;
|
|
40219
|
-
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
40220
|
-
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === NEWLINE_BYTE) lineNumber++;
|
|
40221
|
-
return lineNumber;
|
|
40222
|
-
};
|
|
40223
40238
|
/**
|
|
40224
40239
|
* Drops `react-doctor/react-compiler-no-manual-memoization` diagnostics
|
|
40225
40240
|
* whose enclosing function also carries a `react-hooks-js` (React
|
|
@@ -40400,11 +40415,11 @@ const buildRelatedLocations = (labels, filePath) => {
|
|
|
40400
40415
|
}
|
|
40401
40416
|
return related;
|
|
40402
40417
|
};
|
|
40403
|
-
const
|
|
40404
|
-
|
|
40405
|
-
|
|
40406
|
-
|
|
40407
|
-
|
|
40418
|
+
const isOxlintSpan = (value) => isRecord(value) && typeof value.offset === "number" && typeof value.length === "number" && typeof value.line === "number" && typeof value.column === "number";
|
|
40419
|
+
const isOxlintLabel = (value) => isRecord(value) && isOxlintSpan(value.span);
|
|
40420
|
+
const isMappableOxlintDiagnostic = (value) => isRecord(value) && typeof value.code === "string" && value.code.length > 0 && typeof value.filename === "string" && value.filename.length > 0 && (value.severity === "warning" || value.severity === "error") && Array.isArray(value.labels) && value.labels.every(isOxlintLabel);
|
|
40421
|
+
const isEngineFailureDiagnostic = (value) => !isRecord(value) || typeof value.filename !== "string" || value.filename.length === 0;
|
|
40422
|
+
const isOxlintOutput = (value) => isRecord(value) && Array.isArray(value.diagnostics);
|
|
40408
40423
|
/**
|
|
40409
40424
|
* Parses one oxlint subprocess's stdout into a flat `Diagnostic[]`.
|
|
40410
40425
|
* Tolerates the leading-notice-line shape oxlint sometimes prints
|
|
@@ -40423,23 +40438,59 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
40423
40438
|
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
40424
40439
|
}
|
|
40425
40440
|
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
40441
|
+
const engineFailureDiagnostic = parsed.diagnostics.find(isEngineFailureDiagnostic);
|
|
40442
|
+
if (engineFailureDiagnostic !== void 0) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: JSON.stringify(engineFailureDiagnostic).slice(0, 600) }) });
|
|
40426
40443
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
40444
|
+
const sourceBufferCache = /* @__PURE__ */ new Map();
|
|
40445
|
+
const sourceLinesCache = /* @__PURE__ */ new Map();
|
|
40446
|
+
const resolveAbsolutePath = (filename) => Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
40447
|
+
const readSourceBuffer = (filename) => {
|
|
40448
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
40449
|
+
const cached = sourceBufferCache.get(absolutePath);
|
|
40450
|
+
if (cached !== void 0) return cached;
|
|
40451
|
+
let sourceBuffer;
|
|
40452
|
+
try {
|
|
40453
|
+
sourceBuffer = NFS.readFileSync(absolutePath);
|
|
40454
|
+
} catch {
|
|
40455
|
+
sourceBuffer = null;
|
|
40456
|
+
}
|
|
40457
|
+
sourceBufferCache.set(absolutePath, sourceBuffer);
|
|
40458
|
+
return sourceBuffer;
|
|
40459
|
+
};
|
|
40460
|
+
const readSourceLines = (filename) => {
|
|
40461
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
40462
|
+
const cached = sourceLinesCache.get(absolutePath);
|
|
40463
|
+
if (cached !== void 0) return cached;
|
|
40464
|
+
const sourceBuffer = readSourceBuffer(filename);
|
|
40465
|
+
const sourceLines = sourceBuffer ? sourceBuffer.toString("utf8").split("\n") : null;
|
|
40466
|
+
sourceLinesCache.set(absolutePath, sourceLines);
|
|
40467
|
+
return sourceLines;
|
|
40468
|
+
};
|
|
40427
40469
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
40428
|
-
const absolutePath =
|
|
40470
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
40429
40471
|
const cached = minifiedFileCache.get(absolutePath);
|
|
40430
40472
|
if (cached !== void 0) return cached;
|
|
40431
40473
|
const minified = isMinifiedSource(absolutePath);
|
|
40432
40474
|
minifiedFileCache.set(absolutePath, minified);
|
|
40433
40475
|
return minified;
|
|
40434
40476
|
};
|
|
40435
|
-
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => diagnostic
|
|
40477
|
+
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
|
|
40436
40478
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
40437
40479
|
const primaryLabel = diagnostic.labels[0];
|
|
40438
40480
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
40439
40481
|
const normalizedFilePath = diagnostic.filename.replaceAll("\\", "/");
|
|
40440
40482
|
const primarySpan = primaryLabel?.span;
|
|
40483
|
+
const sourceBuffer = primarySpan ? readSourceBuffer(diagnostic.filename) : null;
|
|
40441
40484
|
const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath);
|
|
40442
40485
|
const category = resolveDiagnosticCategory(plugin, rule);
|
|
40486
|
+
const matchByOccurrence = resolveMatchByOccurrence(rule, category);
|
|
40487
|
+
const sourceLines = primarySpan ? readSourceLines(diagnostic.filename) : null;
|
|
40488
|
+
const primaryLineIndex = primarySpan ? primarySpan.line - 1 : -1;
|
|
40489
|
+
const primaryLine = sourceLines?.[primaryLineIndex];
|
|
40490
|
+
const primaryColumnIndex = primarySpan ? primarySpan.column - 1 : -1;
|
|
40491
|
+
const isJsxTagLabel = primaryLine !== void 0 && primaryColumnIndex >= 0 && (primaryLine[primaryColumnIndex] === "<" || primaryLine[primaryColumnIndex - 1] === "<");
|
|
40492
|
+
const jsxOpenerEndLineIndex = sourceLines && isJsxTagLabel ? findJsxOpenerSpan(sourceLines, primaryLineIndex) : null;
|
|
40493
|
+
const primarySpanEndLine = jsxOpenerEndLineIndex !== null ? jsxOpenerEndLineIndex + 1 : primarySpan && sourceBuffer ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset + Math.max(primarySpan.length - 1, 0)) : void 0;
|
|
40443
40494
|
return {
|
|
40444
40495
|
filePath: normalizedFilePath,
|
|
40445
40496
|
plugin,
|
|
@@ -40453,10 +40504,11 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
40453
40504
|
column: primarySpan?.column ?? 0,
|
|
40454
40505
|
...primarySpan ? {
|
|
40455
40506
|
offset: primarySpan.offset,
|
|
40456
|
-
length: primarySpan.length
|
|
40507
|
+
length: primarySpan.length,
|
|
40508
|
+
...primarySpanEndLine !== void 0 ? { endLine: primarySpanEndLine } : {}
|
|
40457
40509
|
} : {},
|
|
40458
40510
|
category,
|
|
40459
|
-
...
|
|
40511
|
+
...matchByOccurrence ? { matchByOccurrence: true } : {},
|
|
40460
40512
|
...relatedLocations.length > 0 ? { relatedLocations } : {}
|
|
40461
40513
|
};
|
|
40462
40514
|
}), rootDirectory);
|
|
@@ -42914,10 +42966,11 @@ const buildSkippedChecks = (input) => {
|
|
|
42914
42966
|
* render diff-base mistakes (`GitBaseBranchInvalid` /
|
|
42915
42967
|
* `GitBaseBranchMissing`) as clean user errors instead of crashes.
|
|
42916
42968
|
*/
|
|
42917
|
-
const getDiffInfo = (directory, explicitBaseBranch) => runPromise(gen(function* () {
|
|
42969
|
+
const getDiffInfo = (directory, explicitBaseBranch, includeUntracked) => runPromise(gen(function* () {
|
|
42918
42970
|
const selection = yield* (yield* Git).diffSelection({
|
|
42919
42971
|
directory,
|
|
42920
|
-
explicitBaseBranch
|
|
42972
|
+
explicitBaseBranch,
|
|
42973
|
+
includeUntracked
|
|
42921
42974
|
});
|
|
42922
42975
|
if (selection === null) return null;
|
|
42923
42976
|
return {
|
|
@@ -43207,4 +43260,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
43207
43260
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
43208
43261
|
|
|
43209
43262
|
//# sourceMappingURL=index.js.map
|
|
43210
|
-
//# debugId=
|
|
43263
|
+
//# debugId=2aa6fc3c-96ab-5a4c-8a52-e74d27fb4b5a
|
package/dist/lsp.js
CHANGED
|
@@ -33194,13 +33194,15 @@ const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
|
33194
33194
|
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort());
|
|
33195
33195
|
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
33196
33196
|
const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
|
|
33197
|
+
const UTF8_BOM_CHARACTER = "";
|
|
33198
|
+
const stripUtf8Bom = (content) => content.startsWith(UTF8_BOM_CHARACTER) ? content.slice(1) : content;
|
|
33197
33199
|
const cachedPackageJsons = /* @__PURE__ */ new Map();
|
|
33198
33200
|
const clearPackageJsonCache = () => {
|
|
33199
33201
|
cachedPackageJsons.clear();
|
|
33200
33202
|
};
|
|
33201
33203
|
const readPackageJsonUncached = (packageJsonPath) => {
|
|
33202
33204
|
try {
|
|
33203
|
-
return JSON.parse(NFS.readFileSync(packageJsonPath, "utf-8"));
|
|
33205
|
+
return JSON.parse(stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8")));
|
|
33204
33206
|
} catch (error) {
|
|
33205
33207
|
if (error instanceof SyntaxError) return {};
|
|
33206
33208
|
if (isErrnoException(error)) {
|
|
@@ -36767,7 +36769,6 @@ const PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
|
|
|
36767
36769
|
const PNPM_LOCKFILE = "pnpm-lock.yaml";
|
|
36768
36770
|
const PACKAGE_JSON_FILE = "package.json";
|
|
36769
36771
|
const PNPM_HARDENING_RULE_KEY = "require-pnpm-hardening";
|
|
36770
|
-
const UTF8_BOM_CHAR = "";
|
|
36771
36772
|
const HARDENING_SETTING_KEYS = new Set([
|
|
36772
36773
|
"minimumReleaseAge",
|
|
36773
36774
|
"blockExoticSubdeps",
|
|
@@ -36792,12 +36793,11 @@ const stripInlineComment = (rawValue) => {
|
|
|
36792
36793
|
return rawValue;
|
|
36793
36794
|
};
|
|
36794
36795
|
const unquote = (rawValue) => rawValue.replace(/^["']|["']$/g, "");
|
|
36795
|
-
const stripBom = (rawContent) => rawContent.startsWith(UTF8_BOM_CHAR) ? rawContent.slice(1) : rawContent;
|
|
36796
36796
|
const parseHardeningSettings = (content) => {
|
|
36797
36797
|
let minimumReleaseAge = null;
|
|
36798
36798
|
let blockExoticSubdeps = null;
|
|
36799
36799
|
let trustPolicy = null;
|
|
36800
|
-
const lines =
|
|
36800
|
+
const lines = stripUtf8Bom(content).split(/\r?\n/);
|
|
36801
36801
|
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
36802
36802
|
const lineText = lines[lineIndex];
|
|
36803
36803
|
if (lineText === void 0) continue;
|
|
@@ -36831,7 +36831,7 @@ const isPnpmManagedProject = (rootDirectory) => {
|
|
|
36831
36831
|
const packageJsonPath = Path.join(rootDirectory, PACKAGE_JSON_FILE);
|
|
36832
36832
|
if (!isFile(packageJsonPath)) return false;
|
|
36833
36833
|
try {
|
|
36834
|
-
const packageJsonRaw = NFS.readFileSync(packageJsonPath, "utf-8");
|
|
36834
|
+
const packageJsonRaw = stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8"));
|
|
36835
36835
|
const packageJson = JSON.parse(packageJsonRaw);
|
|
36836
36836
|
if (packageJson !== null && typeof packageJson === "object" && "packageManager" in packageJson && typeof packageJson.packageManager === "string" && packageJson.packageManager.startsWith("pnpm@")) return true;
|
|
36837
36837
|
} catch {
|
|
@@ -38557,6 +38557,7 @@ const parseGithubViewerPermission = (stdout) => {
|
|
|
38557
38557
|
return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
|
|
38558
38558
|
};
|
|
38559
38559
|
const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
|
|
38560
|
+
const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
|
|
38560
38561
|
/**
|
|
38561
38562
|
* `Git` wraps every `git`-via-subprocess call react-doctor makes
|
|
38562
38563
|
* behind a `Context.Service`. The production layer (`layerNode`)
|
|
@@ -38627,6 +38628,14 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38627
38628
|
args,
|
|
38628
38629
|
directory
|
|
38629
38630
|
});
|
|
38631
|
+
const listUntrackedFilePaths = (directory, includePaths = []) => runGit(directory, [
|
|
38632
|
+
"ls-files",
|
|
38633
|
+
"-z",
|
|
38634
|
+
"--others",
|
|
38635
|
+
"--exclude-standard",
|
|
38636
|
+
...includePaths.length > 0 ? ["--", ...includePaths] : []
|
|
38637
|
+
]).pipe(map$3((result) => result.status === 0 ? splitNullSeparated(result.stdout) : null));
|
|
38638
|
+
const mergeUntracked = (directory, trackedFilePaths, includeUntracked) => includeUntracked ? listUntrackedFilePaths(directory).pipe(map$3((untracked) => untracked === null ? trackedFilePaths : [...new Set([...trackedFilePaths, ...untracked])])) : succeed$2(trackedFilePaths);
|
|
38630
38639
|
const currentBranch = (directory) => runGit(directory, [
|
|
38631
38640
|
"rev-parse",
|
|
38632
38641
|
"--abbrev-ref",
|
|
@@ -38749,7 +38758,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38749
38758
|
githubViewerPermission,
|
|
38750
38759
|
branchExists,
|
|
38751
38760
|
mergeBase,
|
|
38752
|
-
diffSelection: ({ directory, explicitBaseBranch }) => gen(function* () {
|
|
38761
|
+
diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
|
|
38753
38762
|
if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
|
|
38754
38763
|
if (explicitBaseBranch !== void 0) {
|
|
38755
38764
|
const range = parseGitDiffRange(explicitBaseBranch);
|
|
@@ -38778,7 +38787,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38778
38787
|
"HEAD"
|
|
38779
38788
|
]);
|
|
38780
38789
|
if (uncommitted.status !== 0) return null;
|
|
38781
|
-
const files = splitNullSeparated(uncommitted.stdout);
|
|
38790
|
+
const files = yield* mergeUntracked(directory, splitNullSeparated(uncommitted.stdout), includeUntracked);
|
|
38782
38791
|
if (files.length === 0) return null;
|
|
38783
38792
|
return {
|
|
38784
38793
|
currentBranch: resolvedCurrentBranch,
|
|
@@ -38808,7 +38817,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38808
38817
|
currentBranch: resolvedCurrentBranch,
|
|
38809
38818
|
baseBranch,
|
|
38810
38819
|
diffBaseRef: mergeBaseRef,
|
|
38811
|
-
changedFiles: splitNullSeparated(diff.stdout),
|
|
38820
|
+
changedFiles: yield* mergeUntracked(directory, splitNullSeparated(diff.stdout), includeUntracked),
|
|
38812
38821
|
isCurrentChanges: false
|
|
38813
38822
|
};
|
|
38814
38823
|
}).pipe(withSpan("Git.diffSelection")),
|
|
@@ -38854,7 +38863,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38854
38863
|
stdout: result.stdout
|
|
38855
38864
|
};
|
|
38856
38865
|
}).pipe(withSpan("Git.grep")),
|
|
38857
|
-
changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
|
|
38866
|
+
changedLineRanges: ({ directory, baseRef, cached, files, includeUntracked = false }) => gen(function* () {
|
|
38858
38867
|
if (files.length === 0) return [];
|
|
38859
38868
|
if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
|
|
38860
38869
|
const result = yield* runGit(directory, [
|
|
@@ -38868,7 +38877,14 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38868
38877
|
...files
|
|
38869
38878
|
]);
|
|
38870
38879
|
if (result.status !== 0) return null;
|
|
38871
|
-
|
|
38880
|
+
const changedLineRanges = parseChangedLineRanges(result.stdout);
|
|
38881
|
+
if (cached || !includeUntracked) return changedLineRanges;
|
|
38882
|
+
const untrackedFilePaths = yield* listUntrackedFilePaths(directory, files);
|
|
38883
|
+
if (untrackedFilePaths === null) return changedLineRanges;
|
|
38884
|
+
return [...changedLineRanges, ...untrackedFilePaths.map((file) => ({
|
|
38885
|
+
file,
|
|
38886
|
+
ranges: [[1, UNTRACKED_FILE_LAST_LINE]]
|
|
38887
|
+
}))];
|
|
38872
38888
|
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
38873
38889
|
});
|
|
38874
38890
|
})).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
|
|
@@ -39620,6 +39636,12 @@ const mapWithConcurrency = async (items, concurrency, task) => {
|
|
|
39620
39636
|
if (errors.length > 0) throw errors[0];
|
|
39621
39637
|
return results;
|
|
39622
39638
|
};
|
|
39639
|
+
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
39640
|
+
let lineNumber = 1;
|
|
39641
|
+
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
39642
|
+
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === 10) lineNumber++;
|
|
39643
|
+
return lineNumber;
|
|
39644
|
+
};
|
|
39623
39645
|
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";
|
|
39624
39646
|
const ACCESSORS_MIN_VERSION = "3.15.0";
|
|
39625
39647
|
const supportsAccessors = (reanimatedVersion) => {
|
|
@@ -40191,13 +40213,6 @@ const findOutermostEnclosingFunctionRange = (sourceFile, targetLine) => {
|
|
|
40191
40213
|
visit(sourceFile);
|
|
40192
40214
|
return foundRange;
|
|
40193
40215
|
};
|
|
40194
|
-
const NEWLINE_BYTE = 10;
|
|
40195
|
-
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
40196
|
-
let lineNumber = 1;
|
|
40197
|
-
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
40198
|
-
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === NEWLINE_BYTE) lineNumber++;
|
|
40199
|
-
return lineNumber;
|
|
40200
|
-
};
|
|
40201
40216
|
/**
|
|
40202
40217
|
* Drops `react-doctor/react-compiler-no-manual-memoization` diagnostics
|
|
40203
40218
|
* whose enclosing function also carries a `react-hooks-js` (React
|
|
@@ -40378,11 +40393,11 @@ const buildRelatedLocations = (labels, filePath) => {
|
|
|
40378
40393
|
}
|
|
40379
40394
|
return related;
|
|
40380
40395
|
};
|
|
40381
|
-
const
|
|
40382
|
-
|
|
40383
|
-
|
|
40384
|
-
|
|
40385
|
-
|
|
40396
|
+
const isOxlintSpan = (value) => isRecord(value) && typeof value.offset === "number" && typeof value.length === "number" && typeof value.line === "number" && typeof value.column === "number";
|
|
40397
|
+
const isOxlintLabel = (value) => isRecord(value) && isOxlintSpan(value.span);
|
|
40398
|
+
const isMappableOxlintDiagnostic = (value) => isRecord(value) && typeof value.code === "string" && value.code.length > 0 && typeof value.filename === "string" && value.filename.length > 0 && (value.severity === "warning" || value.severity === "error") && Array.isArray(value.labels) && value.labels.every(isOxlintLabel);
|
|
40399
|
+
const isEngineFailureDiagnostic = (value) => !isRecord(value) || typeof value.filename !== "string" || value.filename.length === 0;
|
|
40400
|
+
const isOxlintOutput = (value) => isRecord(value) && Array.isArray(value.diagnostics);
|
|
40386
40401
|
/**
|
|
40387
40402
|
* Parses one oxlint subprocess's stdout into a flat `Diagnostic[]`.
|
|
40388
40403
|
* Tolerates the leading-notice-line shape oxlint sometimes prints
|
|
@@ -40401,23 +40416,59 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
40401
40416
|
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
40402
40417
|
}
|
|
40403
40418
|
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
40419
|
+
const engineFailureDiagnostic = parsed.diagnostics.find(isEngineFailureDiagnostic);
|
|
40420
|
+
if (engineFailureDiagnostic !== void 0) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: JSON.stringify(engineFailureDiagnostic).slice(0, 600) }) });
|
|
40404
40421
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
40422
|
+
const sourceBufferCache = /* @__PURE__ */ new Map();
|
|
40423
|
+
const sourceLinesCache = /* @__PURE__ */ new Map();
|
|
40424
|
+
const resolveAbsolutePath = (filename) => Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
40425
|
+
const readSourceBuffer = (filename) => {
|
|
40426
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
40427
|
+
const cached = sourceBufferCache.get(absolutePath);
|
|
40428
|
+
if (cached !== void 0) return cached;
|
|
40429
|
+
let sourceBuffer;
|
|
40430
|
+
try {
|
|
40431
|
+
sourceBuffer = NFS.readFileSync(absolutePath);
|
|
40432
|
+
} catch {
|
|
40433
|
+
sourceBuffer = null;
|
|
40434
|
+
}
|
|
40435
|
+
sourceBufferCache.set(absolutePath, sourceBuffer);
|
|
40436
|
+
return sourceBuffer;
|
|
40437
|
+
};
|
|
40438
|
+
const readSourceLines = (filename) => {
|
|
40439
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
40440
|
+
const cached = sourceLinesCache.get(absolutePath);
|
|
40441
|
+
if (cached !== void 0) return cached;
|
|
40442
|
+
const sourceBuffer = readSourceBuffer(filename);
|
|
40443
|
+
const sourceLines = sourceBuffer ? sourceBuffer.toString("utf8").split("\n") : null;
|
|
40444
|
+
sourceLinesCache.set(absolutePath, sourceLines);
|
|
40445
|
+
return sourceLines;
|
|
40446
|
+
};
|
|
40405
40447
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
40406
|
-
const absolutePath =
|
|
40448
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
40407
40449
|
const cached = minifiedFileCache.get(absolutePath);
|
|
40408
40450
|
if (cached !== void 0) return cached;
|
|
40409
40451
|
const minified = isMinifiedSource(absolutePath);
|
|
40410
40452
|
minifiedFileCache.set(absolutePath, minified);
|
|
40411
40453
|
return minified;
|
|
40412
40454
|
};
|
|
40413
|
-
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => diagnostic
|
|
40455
|
+
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
|
|
40414
40456
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
40415
40457
|
const primaryLabel = diagnostic.labels[0];
|
|
40416
40458
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
40417
40459
|
const normalizedFilePath = diagnostic.filename.replaceAll("\\", "/");
|
|
40418
40460
|
const primarySpan = primaryLabel?.span;
|
|
40461
|
+
const sourceBuffer = primarySpan ? readSourceBuffer(diagnostic.filename) : null;
|
|
40419
40462
|
const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath);
|
|
40420
40463
|
const category = resolveDiagnosticCategory(plugin, rule);
|
|
40464
|
+
const matchByOccurrence = resolveMatchByOccurrence(rule, category);
|
|
40465
|
+
const sourceLines = primarySpan ? readSourceLines(diagnostic.filename) : null;
|
|
40466
|
+
const primaryLineIndex = primarySpan ? primarySpan.line - 1 : -1;
|
|
40467
|
+
const primaryLine = sourceLines?.[primaryLineIndex];
|
|
40468
|
+
const primaryColumnIndex = primarySpan ? primarySpan.column - 1 : -1;
|
|
40469
|
+
const isJsxTagLabel = primaryLine !== void 0 && primaryColumnIndex >= 0 && (primaryLine[primaryColumnIndex] === "<" || primaryLine[primaryColumnIndex - 1] === "<");
|
|
40470
|
+
const jsxOpenerEndLineIndex = sourceLines && isJsxTagLabel ? findJsxOpenerSpan(sourceLines, primaryLineIndex) : null;
|
|
40471
|
+
const primarySpanEndLine = jsxOpenerEndLineIndex !== null ? jsxOpenerEndLineIndex + 1 : primarySpan && sourceBuffer ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset + Math.max(primarySpan.length - 1, 0)) : void 0;
|
|
40421
40472
|
return {
|
|
40422
40473
|
filePath: normalizedFilePath,
|
|
40423
40474
|
plugin,
|
|
@@ -40431,10 +40482,11 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
40431
40482
|
column: primarySpan?.column ?? 0,
|
|
40432
40483
|
...primarySpan ? {
|
|
40433
40484
|
offset: primarySpan.offset,
|
|
40434
|
-
length: primarySpan.length
|
|
40485
|
+
length: primarySpan.length,
|
|
40486
|
+
...primarySpanEndLine !== void 0 ? { endLine: primarySpanEndLine } : {}
|
|
40435
40487
|
} : {},
|
|
40436
40488
|
category,
|
|
40437
|
-
...
|
|
40489
|
+
...matchByOccurrence ? { matchByOccurrence: true } : {},
|
|
40438
40490
|
...relatedLocations.length > 0 ? { relatedLocations } : {}
|
|
40439
40491
|
};
|
|
40440
40492
|
}), rootDirectory);
|
|
@@ -44490,6 +44542,7 @@ const METRIC = {
|
|
|
44490
44542
|
cliInvoked: "cli.invoked",
|
|
44491
44543
|
cliError: "cli.error",
|
|
44492
44544
|
cliEnvironmentError: "cli.env_error",
|
|
44545
|
+
stagedSnapshotDivergence: "staged.snapshot_divergence",
|
|
44493
44546
|
projectDetected: "project.detected",
|
|
44494
44547
|
projectPathSelected: "project.path_selected",
|
|
44495
44548
|
projectConfigSelected: "project.config_selected",
|
|
@@ -44844,5 +44897,5 @@ const startLanguageServer = () => {
|
|
|
44844
44897
|
};
|
|
44845
44898
|
//#endregion
|
|
44846
44899
|
export { startLanguageServer };
|
|
44847
|
-
!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]="
|
|
44848
|
-
//# debugId=
|
|
44900
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="8ed74459-2a69-556c-bb95-8cdebdb094cd")}catch(e){}}();
|
|
44901
|
+
//# debugId=8ed74459-2a69-556c-bb95-8cdebdb094cd
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-doctor",
|
|
3
|
-
"version": "0.7.6-dev.
|
|
3
|
+
"version": "0.7.6-dev.b36e439",
|
|
4
4
|
"description": "Your agent writes bad React. This catches it",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"accessibility",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"vscode-uri": "^3.1.0",
|
|
67
67
|
"yaml": "^2.9.0",
|
|
68
68
|
"deslop-js": "0.7.6",
|
|
69
|
-
"oxlint-plugin-react-doctor": "0.7.6-dev.
|
|
69
|
+
"oxlint-plugin-react-doctor": "0.7.6-dev.b36e439"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/babel__code-frame": "^7.27.0",
|