react-doctor 0.9.3-dev.0b673cc → 0.9.3-dev.3466fe1
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 +911 -286
- package/dist/index.d.ts +9 -2
- package/dist/index.js +478 -137
- package/dist/lsp.js +491 -143
- package/dist/run-scan-app-QbtG43NL.js +2326 -0
- package/package.json +4 -3
- package/dist/run-scan-app-DHRvqfkX.js +0 -1733
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]="e021759c-79c5-5b42-8720-2243f4e6a8f8")}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";
|
|
@@ -6666,7 +6666,7 @@ const flatMap$5 = /* @__PURE__ */ dual(2, (self, f) => isNone(self) ? none() : f
|
|
|
6666
6666
|
* @category filtering
|
|
6667
6667
|
* @since 2.0.0
|
|
6668
6668
|
*/
|
|
6669
|
-
const filter = /* @__PURE__ */ dual(2, (self, predicate) => isNone(self) ? none() : predicate(self.value) ? some(self.value) : none());
|
|
6669
|
+
const filter$1 = /* @__PURE__ */ dual(2, (self, predicate) => isNone(self) ? none() : predicate(self.value) ? some(self.value) : none());
|
|
6670
6670
|
//#endregion
|
|
6671
6671
|
//#region ../../node_modules/.pnpm/effect@4.0.0-beta.70/node_modules/effect/dist/Result.js
|
|
6672
6672
|
/**
|
|
@@ -18657,7 +18657,7 @@ function transformOptional(f) {
|
|
|
18657
18657
|
*/
|
|
18658
18658
|
function withDefault$1(defaultValue) {
|
|
18659
18659
|
return new Getter((o) => {
|
|
18660
|
-
const filtered = filter(o, isNotUndefined);
|
|
18660
|
+
const filtered = filter$1(o, isNotUndefined);
|
|
18661
18661
|
return isSome(filtered) ? succeed$2(filtered) : mapEager(defaultValue, some);
|
|
18662
18662
|
});
|
|
18663
18663
|
}
|
|
@@ -22505,6 +22505,10 @@ var JsonReportProjectEntryV3 = class extends Class("JsonReportProjectEntryV3")({
|
|
|
22505
22505
|
scannedFileCount: optional(Number$1),
|
|
22506
22506
|
elapsedMilliseconds: Number$1
|
|
22507
22507
|
}) {};
|
|
22508
|
+
var JsonReportSkippedProject = class extends Class("JsonReportSkippedProject")({
|
|
22509
|
+
directory: String$1,
|
|
22510
|
+
reason: Literal("max-duration")
|
|
22511
|
+
}) {};
|
|
22508
22512
|
/** Original full, diff, and staged report contract. */
|
|
22509
22513
|
var JsonReportV1 = class extends Class("JsonReportV1")({
|
|
22510
22514
|
schemaVersion: Literal(1),
|
|
@@ -22533,6 +22537,7 @@ var JsonReportV1 = class extends Class("JsonReportV1")({
|
|
|
22533
22537
|
reactDetected: optional(Boolean$1),
|
|
22534
22538
|
diff: NullOr(JsonReportDiffInfo),
|
|
22535
22539
|
projects: ArraySchema(JsonReportProjectEntry),
|
|
22540
|
+
skippedProjects: optional(ArraySchema(JsonReportSkippedProject)),
|
|
22536
22541
|
diagnostics: ArraySchema(Diagnostic),
|
|
22537
22542
|
summary: JsonReportSummary,
|
|
22538
22543
|
elapsedMilliseconds: Number$1,
|
|
@@ -22561,6 +22566,7 @@ var JsonReportV2 = class extends Class("JsonReportV2")({
|
|
|
22561
22566
|
diff: NullOr(JsonReportDiffInfo),
|
|
22562
22567
|
baseline: JsonReportBaseline,
|
|
22563
22568
|
projects: ArraySchema(JsonReportProjectEntry),
|
|
22569
|
+
skippedProjects: optional(ArraySchema(JsonReportSkippedProject)),
|
|
22564
22570
|
diagnostics: ArraySchema(Diagnostic),
|
|
22565
22571
|
summary: JsonReportSummary,
|
|
22566
22572
|
elapsedMilliseconds: Number$1,
|
|
@@ -22577,6 +22583,7 @@ var JsonReportV3 = class extends Class("JsonReportV3")({
|
|
|
22577
22583
|
diff: NullOr(JsonReportDiffInfo),
|
|
22578
22584
|
baseline: optional(JsonReportBaseline),
|
|
22579
22585
|
projects: ArraySchema(JsonReportProjectEntryV3),
|
|
22586
|
+
skippedProjects: optional(ArraySchema(JsonReportSkippedProject)),
|
|
22580
22587
|
diagnostics: ArraySchema(JsonReportDiagnosticV3),
|
|
22581
22588
|
summary: JsonReportSummary,
|
|
22582
22589
|
elapsedMilliseconds: Number$1,
|
|
@@ -56559,6 +56566,47 @@ const flattenArray = (self) => transformPull$1(self, (pull) => {
|
|
|
56559
56566
|
}));
|
|
56560
56567
|
});
|
|
56561
56568
|
/**
|
|
56569
|
+
* Filters arrays of elements emitted by a channel, applying the filter
|
|
56570
|
+
* to each element within the arrays and only emitting non-empty filtered arrays.
|
|
56571
|
+
*
|
|
56572
|
+
* **Example** (Filtering array output)
|
|
56573
|
+
*
|
|
56574
|
+
* ```ts
|
|
56575
|
+
* import { Array, Channel } from "effect"
|
|
56576
|
+
*
|
|
56577
|
+
* const nonEmptyArrayPredicate = Array.isReadonlyArrayNonEmpty
|
|
56578
|
+
*
|
|
56579
|
+
* // Create a channel that outputs arrays of mixed data
|
|
56580
|
+
* const arrayChannel = Channel.fromIterable([
|
|
56581
|
+
* Array.make(1, 2, 3, 4, 5),
|
|
56582
|
+
* Array.make(6, 7, 8, 9, 10),
|
|
56583
|
+
* Array.make(11, 12, 13, 14, 15)
|
|
56584
|
+
* ]).pipe(Channel.filter(nonEmptyArrayPredicate))
|
|
56585
|
+
*
|
|
56586
|
+
* // Filter arrays to keep only even numbers
|
|
56587
|
+
* const evenArraysChannel = Channel.filterArray(arrayChannel, (n) => n % 2 === 0)
|
|
56588
|
+
* // Outputs: [2, 4], [6, 8, 10], [12, 14]
|
|
56589
|
+
* // Note: Only non-empty filtered arrays are emitted
|
|
56590
|
+
*
|
|
56591
|
+
* // Arrays that would become empty after filtering are discarded entirely
|
|
56592
|
+
* const oddChannel = Channel.fromIterable([
|
|
56593
|
+
* Array.make(1, 3, 5),
|
|
56594
|
+
* Array.make(2, 4),
|
|
56595
|
+
* Array.make(7, 9)
|
|
56596
|
+
* ]).pipe(Channel.filter(nonEmptyArrayPredicate))
|
|
56597
|
+
* const filteredOddChannel = Channel.filterArray(oddChannel, (n) => n % 2 === 0)
|
|
56598
|
+
* // Outputs: [2, 4] (the arrays [1,3,5] and [7,9] are discarded)
|
|
56599
|
+
* ```
|
|
56600
|
+
*
|
|
56601
|
+
* @category filtering
|
|
56602
|
+
* @since 4.0.0
|
|
56603
|
+
*/
|
|
56604
|
+
const filterArray = /* @__PURE__ */ dual(2, (self, predicate) => transformPull$1(self, (pull) => succeed$2(flatMap$2(pull, function loop(arr) {
|
|
56605
|
+
const passes = [];
|
|
56606
|
+
for (let i = 0; i < arr.length; i++) if (predicate(arr[i])) passes.push(arr[i]);
|
|
56607
|
+
return isReadonlyArrayNonEmpty(passes) ? succeed$2(passes) : flatMap$2(pull, loop);
|
|
56608
|
+
}))));
|
|
56609
|
+
/**
|
|
56562
56610
|
* Filters and maps each element inside emitted non-empty arrays using a
|
|
56563
56611
|
* `Filter`.
|
|
56564
56612
|
*
|
|
@@ -58196,6 +58244,30 @@ const concat = /* @__PURE__ */ dual(2, (self, that) => flatten(fromArray([self,
|
|
|
58196
58244
|
*/
|
|
58197
58245
|
const merge$1 = /* @__PURE__ */ dual((args) => isStream(args[0]) && isStream(args[1]), (self, that, options) => fromChannel(merge$2(toChannel(self), toChannel(that), options)));
|
|
58198
58246
|
/**
|
|
58247
|
+
* Filters a stream to the elements that satisfy a predicate.
|
|
58248
|
+
*
|
|
58249
|
+
* **Example** (Filtering stream values)
|
|
58250
|
+
*
|
|
58251
|
+
* ```ts
|
|
58252
|
+
* import { Console, Effect, Stream } from "effect"
|
|
58253
|
+
*
|
|
58254
|
+
* const program = Effect.gen(function*() {
|
|
58255
|
+
* const stream = Stream.make(1, 2, 3, 4).pipe(
|
|
58256
|
+
* Stream.filter((n) => n % 2 === 0)
|
|
58257
|
+
* )
|
|
58258
|
+
* const values = yield* Stream.runCollect(stream)
|
|
58259
|
+
* yield* Console.log(values)
|
|
58260
|
+
* })
|
|
58261
|
+
*
|
|
58262
|
+
* Effect.runPromise(program)
|
|
58263
|
+
* // Output: [ 2, 4 ]
|
|
58264
|
+
* ```
|
|
58265
|
+
*
|
|
58266
|
+
* @category filtering
|
|
58267
|
+
* @since 2.0.0
|
|
58268
|
+
*/
|
|
58269
|
+
const filter = /* @__PURE__ */ dual(2, (self, predicate) => fromChannel(filterArray(toChannel(self), predicate)));
|
|
58270
|
+
/**
|
|
58199
58271
|
* Filters and maps stream elements in one pass using a `Filter`.
|
|
58200
58272
|
*
|
|
58201
58273
|
* @category filtering
|
|
@@ -65046,6 +65118,7 @@ const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
|
|
|
65046
65118
|
const GENERATED_SOURCE_DIRECTORY_PATTERN = /(?:^|\/)__generated__\//;
|
|
65047
65119
|
const MINIFIED_SNIFF_BYTES = 65536;
|
|
65048
65120
|
const GIT_LS_FILES_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
|
|
65121
|
+
const GIT_CHECK_ATTR_MAX_BUFFER_BYTES = GIT_LS_FILES_MAX_BUFFER_BYTES;
|
|
65049
65122
|
const SCANNED_DOT_DIRECTORIES = new Set([".dumi", ".storybook"]);
|
|
65050
65123
|
const IGNORED_DIRECTORIES = new Set([
|
|
65051
65124
|
".angular",
|
|
@@ -65365,13 +65438,9 @@ const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
|
|
|
65365
65438
|
const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
|
|
65366
65439
|
const ABORT_EXIT_CODES = new Set([134, 3221226505]);
|
|
65367
65440
|
const OXLINT_OOM_RESCUE_BUDGET_MS = 6e4;
|
|
65368
|
-
const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
|
|
65369
|
-
const LINT_PHASE_TIMEOUT_MS = 3e5;
|
|
65370
|
-
const SCAN_TOTAL_DEADLINE_MS = 9e5;
|
|
65371
65441
|
const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
|
|
65372
65442
|
const DEAD_CODE_WORKER_MEM_BUDGET_BYTES = 2 * 1024 * 1024 * 1024;
|
|
65373
65443
|
const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
|
|
65374
|
-
const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
|
|
65375
65444
|
const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
|
|
65376
65445
|
const RECOMMENDED_PNPM_MINIMUM_RELEASE_AGE_MINUTES = 10080;
|
|
65377
65446
|
const REACT_SERVER_DOM_PACKAGES = [
|
|
@@ -65467,6 +65536,66 @@ const isPlainObject = (value) => {
|
|
|
65467
65536
|
const prototype = Object.getPrototypeOf(value);
|
|
65468
65537
|
return prototype === null || prototype === Object.prototype;
|
|
65469
65538
|
};
|
|
65539
|
+
const LINGUIST_ATTRIBUTES = ["linguist-generated", "linguist-vendored"];
|
|
65540
|
+
const DISABLED_ATTRIBUTE_VALUES = new Set([
|
|
65541
|
+
"unspecified",
|
|
65542
|
+
"unset",
|
|
65543
|
+
"false",
|
|
65544
|
+
"0",
|
|
65545
|
+
"off",
|
|
65546
|
+
"no"
|
|
65547
|
+
]);
|
|
65548
|
+
const buildInput = (relativePaths) => `${relativePaths.join("\0")}\0`;
|
|
65549
|
+
const parseIgnoredPaths = (output) => {
|
|
65550
|
+
const ignoredPaths = /* @__PURE__ */ new Set();
|
|
65551
|
+
const fields = output.split("\0");
|
|
65552
|
+
for (let fieldIndex = 0; fieldIndex + 2 < fields.length; fieldIndex += 3) {
|
|
65553
|
+
const filePath = fields[fieldIndex];
|
|
65554
|
+
const value = fields[fieldIndex + 2]?.toLowerCase();
|
|
65555
|
+
if (filePath && value && !DISABLED_ATTRIBUTE_VALUES.has(value)) ignoredPaths.add(filePath);
|
|
65556
|
+
}
|
|
65557
|
+
return ignoredPaths;
|
|
65558
|
+
};
|
|
65559
|
+
const collectGitLinguistIgnoredPaths = (rootDirectory, relativePaths) => {
|
|
65560
|
+
if (relativePaths.length === 0) return /* @__PURE__ */ new Set();
|
|
65561
|
+
const result = spawnSync("git", [
|
|
65562
|
+
"check-attr",
|
|
65563
|
+
"-z",
|
|
65564
|
+
"--stdin",
|
|
65565
|
+
...LINGUIST_ATTRIBUTES
|
|
65566
|
+
], {
|
|
65567
|
+
cwd: rootDirectory,
|
|
65568
|
+
encoding: "utf-8",
|
|
65569
|
+
input: buildInput(relativePaths),
|
|
65570
|
+
maxBuffer: GIT_CHECK_ATTR_MAX_BUFFER_BYTES
|
|
65571
|
+
});
|
|
65572
|
+
if (result.error || result.status !== 0) return /* @__PURE__ */ new Set();
|
|
65573
|
+
return parseIgnoredPaths(result.stdout);
|
|
65574
|
+
};
|
|
65575
|
+
const collectGitLinguistIgnoredPathsCooperative = (rootDirectory, relativePaths, signal) => {
|
|
65576
|
+
if (relativePaths.length === 0) return Promise.resolve(/* @__PURE__ */ new Set());
|
|
65577
|
+
return new Promise((resolve, reject) => {
|
|
65578
|
+
signal?.throwIfAborted();
|
|
65579
|
+
execFile("git", [
|
|
65580
|
+
"check-attr",
|
|
65581
|
+
"-z",
|
|
65582
|
+
"--stdin",
|
|
65583
|
+
...LINGUIST_ATTRIBUTES
|
|
65584
|
+
], {
|
|
65585
|
+
cwd: rootDirectory,
|
|
65586
|
+
encoding: "utf-8",
|
|
65587
|
+
killSignal: "SIGKILL",
|
|
65588
|
+
maxBuffer: GIT_CHECK_ATTR_MAX_BUFFER_BYTES,
|
|
65589
|
+
signal
|
|
65590
|
+
}, (error, stdout) => {
|
|
65591
|
+
if (signal?.aborted) {
|
|
65592
|
+
reject(signal.reason);
|
|
65593
|
+
return;
|
|
65594
|
+
}
|
|
65595
|
+
resolve(error ? /* @__PURE__ */ new Set() : parseIgnoredPaths(stdout));
|
|
65596
|
+
}).stdin?.end(buildInput(relativePaths));
|
|
65597
|
+
});
|
|
65598
|
+
};
|
|
65470
65599
|
const lastSourceMappingUrl = (fileContent) => {
|
|
65471
65600
|
const lastReference = [...fileContent.matchAll(/\/\/# sourceMappingURL=(\S+)/g)].at(-1);
|
|
65472
65601
|
return lastReference ? lastReference[1] : null;
|
|
@@ -65568,6 +65697,9 @@ function* walkSourceTreeFiles(rootDirectory) {
|
|
|
65568
65697
|
}
|
|
65569
65698
|
}
|
|
65570
65699
|
}
|
|
65700
|
+
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
65701
|
+
setImmediate(resolve);
|
|
65702
|
+
});
|
|
65571
65703
|
const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
|
|
65572
65704
|
const entries = [];
|
|
65573
65705
|
for (const relativePath of relativePaths) {
|
|
@@ -65581,23 +65713,55 @@ const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
|
|
|
65581
65713
|
}
|
|
65582
65714
|
return entries;
|
|
65583
65715
|
};
|
|
65584
|
-
const
|
|
65716
|
+
const collectSizedSourceFilesCooperative = async (rootDirectory, relativePaths, signal) => {
|
|
65717
|
+
const entries = [];
|
|
65718
|
+
let sliceStartedAt = performance.now();
|
|
65719
|
+
for (const relativePath of relativePaths) {
|
|
65720
|
+
signal?.throwIfAborted();
|
|
65721
|
+
const absolutePath = Path.resolve(rootDirectory, relativePath);
|
|
65722
|
+
const sizeBytes = statSourceFileSize(absolutePath);
|
|
65723
|
+
if (!isLargeMinifiedFile(absolutePath, sizeBytes)) entries.push({
|
|
65724
|
+
path: relativePath,
|
|
65725
|
+
sizeBytes: sizeBytes ?? 0
|
|
65726
|
+
});
|
|
65727
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
65728
|
+
await yieldToEventLoop();
|
|
65729
|
+
sliceStartedAt = performance.now();
|
|
65730
|
+
}
|
|
65731
|
+
}
|
|
65732
|
+
return entries;
|
|
65733
|
+
};
|
|
65734
|
+
const parseGitSourceFilePaths = (output) => {
|
|
65735
|
+
const trackedPaths = /* @__PURE__ */ new Set();
|
|
65736
|
+
const untrackedPaths = [];
|
|
65737
|
+
for (const entry of output.split("\0")) {
|
|
65738
|
+
if (entry.length === 0) continue;
|
|
65739
|
+
const trackedPath = /^[0-7]{6} [0-9a-f]+ [0-3]\t([\s\S]+)$/.exec(entry)?.[1];
|
|
65740
|
+
if (trackedPath === void 0) untrackedPaths.push(entry);
|
|
65741
|
+
else trackedPaths.add(trackedPath);
|
|
65742
|
+
}
|
|
65743
|
+
return {
|
|
65744
|
+
trackedPaths: [...trackedPaths],
|
|
65745
|
+
untrackedPaths
|
|
65746
|
+
};
|
|
65747
|
+
};
|
|
65748
|
+
const listGitSourceFilePaths = (rootDirectory) => {
|
|
65585
65749
|
const result = spawnSync("git", [
|
|
65586
65750
|
"ls-files",
|
|
65587
65751
|
"-z",
|
|
65588
|
-
|
|
65752
|
+
"--stage",
|
|
65753
|
+
"--others",
|
|
65754
|
+
"--exclude-standard"
|
|
65589
65755
|
], {
|
|
65590
65756
|
cwd: rootDirectory,
|
|
65591
65757
|
encoding: "utf-8",
|
|
65592
65758
|
maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES
|
|
65593
65759
|
});
|
|
65594
65760
|
if (result.error || result.status !== 0) return null;
|
|
65595
|
-
return result.stdout
|
|
65761
|
+
return parseGitSourceFilePaths(result.stdout);
|
|
65596
65762
|
};
|
|
65597
|
-
const
|
|
65598
|
-
const trackedPaths =
|
|
65599
|
-
const untrackedPaths = gitLsFiles(rootDirectory, ["--others", "--exclude-standard"]);
|
|
65600
|
-
if (trackedPaths === null || untrackedPaths === null) return null;
|
|
65763
|
+
const collectGitCandidateSourceFilePaths = (rootDirectory, paths) => {
|
|
65764
|
+
const { trackedPaths, untrackedPaths } = paths;
|
|
65601
65765
|
const emitDuplicateJsPaths = collectTypeScriptEmitDuplicateJsPaths({
|
|
65602
65766
|
trackedPaths: new Set(trackedPaths),
|
|
65603
65767
|
untrackedPaths,
|
|
@@ -65605,12 +65769,65 @@ const listSourceFilesViaGit = (rootDirectory) => {
|
|
|
65605
65769
|
});
|
|
65606
65770
|
return [...trackedPaths, ...untrackedPaths].filter((filePath) => isLintableSourceFile(filePath) && !hasIgnoredPathSegment(filePath) && !emitDuplicateJsPaths.has(filePath));
|
|
65607
65771
|
};
|
|
65772
|
+
const filterGitSourceFilePaths = (rootDirectory, paths) => {
|
|
65773
|
+
const candidatePaths = collectGitCandidateSourceFilePaths(rootDirectory, paths);
|
|
65774
|
+
const linguistIgnoredPaths = collectGitLinguistIgnoredPaths(rootDirectory, candidatePaths);
|
|
65775
|
+
return candidatePaths.filter((filePath) => !linguistIgnoredPaths.has(filePath));
|
|
65776
|
+
};
|
|
65777
|
+
const listSourceFilesViaGit = (rootDirectory) => {
|
|
65778
|
+
const paths = listGitSourceFilePaths(rootDirectory);
|
|
65779
|
+
return paths === null ? null : filterGitSourceFilePaths(rootDirectory, paths);
|
|
65780
|
+
};
|
|
65781
|
+
const listSourceFilesViaGitCooperative = async (rootDirectory, signal) => {
|
|
65782
|
+
const paths = await new Promise((resolve, reject) => {
|
|
65783
|
+
signal?.throwIfAborted();
|
|
65784
|
+
execFile("git", [
|
|
65785
|
+
"ls-files",
|
|
65786
|
+
"-z",
|
|
65787
|
+
"--stage",
|
|
65788
|
+
"--others",
|
|
65789
|
+
"--exclude-standard"
|
|
65790
|
+
], {
|
|
65791
|
+
cwd: rootDirectory,
|
|
65792
|
+
encoding: "utf-8",
|
|
65793
|
+
killSignal: "SIGKILL",
|
|
65794
|
+
maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES,
|
|
65795
|
+
signal
|
|
65796
|
+
}, (error, stdout) => {
|
|
65797
|
+
if (signal?.aborted) {
|
|
65798
|
+
reject(signal.reason);
|
|
65799
|
+
return;
|
|
65800
|
+
}
|
|
65801
|
+
resolve(error ? null : parseGitSourceFilePaths(stdout));
|
|
65802
|
+
});
|
|
65803
|
+
});
|
|
65804
|
+
if (paths === null) return null;
|
|
65805
|
+
const candidatePaths = collectGitCandidateSourceFilePaths(rootDirectory, paths);
|
|
65806
|
+
const linguistIgnoredPaths = await collectGitLinguistIgnoredPathsCooperative(rootDirectory, candidatePaths, signal);
|
|
65807
|
+
return candidatePaths.filter((filePath) => !linguistIgnoredPaths.has(filePath));
|
|
65808
|
+
};
|
|
65608
65809
|
const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
65609
65810
|
const filePaths = [];
|
|
65610
65811
|
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) if (isLintableSourceFile(name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
|
|
65611
65812
|
return filePaths;
|
|
65612
65813
|
};
|
|
65613
|
-
const
|
|
65814
|
+
const listSourceFilesViaFilesystemCooperative = async (rootDirectory, signal) => {
|
|
65815
|
+
const filePaths = [];
|
|
65816
|
+
let sliceStartedAt = performance.now();
|
|
65817
|
+
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
|
|
65818
|
+
signal?.throwIfAborted();
|
|
65819
|
+
if (isLintableSourceFile(name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
|
|
65820
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
65821
|
+
await yieldToEventLoop();
|
|
65822
|
+
sliceStartedAt = performance.now();
|
|
65823
|
+
}
|
|
65824
|
+
}
|
|
65825
|
+
return filePaths;
|
|
65826
|
+
};
|
|
65827
|
+
const listSourceFilePaths = (rootDirectory) => (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort();
|
|
65828
|
+
const listSourceFilePathsCooperative = async (rootDirectory, signal) => (await listSourceFilesViaGitCooperative(rootDirectory, signal) ?? await listSourceFilesViaFilesystemCooperative(rootDirectory, signal)).sort();
|
|
65829
|
+
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, listSourceFilePaths(rootDirectory));
|
|
65830
|
+
const listSourceFilesCooperative = async (rootDirectory, signal) => (await collectSizedSourceFilesCooperative(rootDirectory, await listSourceFilePathsCooperative(rootDirectory, signal), signal)).map((entry) => entry.path);
|
|
65614
65831
|
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
65615
65832
|
const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
|
|
65616
65833
|
const UTF8_BOM_CHARACTER = "";
|
|
@@ -67933,7 +68150,16 @@ const getBrowserslistProtectionRequirement = (directory) => {
|
|
|
67933
68150
|
if (!queries) return void 0;
|
|
67934
68151
|
let requirement;
|
|
67935
68152
|
let hasUnsupportedTarget = false;
|
|
67936
|
-
|
|
68153
|
+
const previousIgnoreOldData = process.env.BROWSERSLIST_IGNORE_OLD_DATA;
|
|
68154
|
+
let browserTargets = [];
|
|
68155
|
+
try {
|
|
68156
|
+
process.env.BROWSERSLIST_IGNORE_OLD_DATA = "true";
|
|
68157
|
+
browserTargets = (0, import_browserslist.default)(queries);
|
|
68158
|
+
} finally {
|
|
68159
|
+
if (previousIgnoreOldData === void 0) delete process.env.BROWSERSLIST_IGNORE_OLD_DATA;
|
|
68160
|
+
else process.env.BROWSERSLIST_IGNORE_OLD_DATA = previousIgnoreOldData;
|
|
68161
|
+
}
|
|
68162
|
+
for (const browserTarget of browserTargets) {
|
|
67937
68163
|
const browserRequirement = getBrowserProtectionRequirement(browserTarget);
|
|
67938
68164
|
if (browserRequirement === "unsupported") hasUnsupportedTarget = true;
|
|
67939
68165
|
if (browserRequirement === "noreferrer") requirement = "noreferrer";
|
|
@@ -68039,8 +68265,8 @@ const cachedProjectInfos = /* @__PURE__ */ new Map();
|
|
|
68039
68265
|
* framework-agnostic rules. Throws only when the directory has nothing
|
|
68040
68266
|
* to scan (no enclosing project and no source files of its own).
|
|
68041
68267
|
*/
|
|
68042
|
-
const discoverProjectWithoutPackageJson = (directory) => {
|
|
68043
|
-
const sourceFileCount = countSourceFiles(directory);
|
|
68268
|
+
const discoverProjectWithoutPackageJson = (directory, options) => {
|
|
68269
|
+
const sourceFileCount = options.sourceFileCount ?? countSourceFiles(directory);
|
|
68044
68270
|
const hasOwnTsConfig = NFS.existsSync(Path.join(directory, "tsconfig.json"));
|
|
68045
68271
|
const enclosingProjectRoot = findNearestAncestorPackageJson(directory);
|
|
68046
68272
|
const enclosingProject = enclosingProjectRoot !== null ? discoverProject(enclosingProjectRoot) : null;
|
|
@@ -68107,12 +68333,15 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
68107
68333
|
sourceFileCount
|
|
68108
68334
|
};
|
|
68109
68335
|
};
|
|
68110
|
-
const discoverProject = (directory) => {
|
|
68336
|
+
const discoverProject = (directory, options = {}) => {
|
|
68111
68337
|
const cached = cachedProjectInfos.get(directory);
|
|
68112
|
-
if (cached !== void 0) return cached
|
|
68338
|
+
if (cached !== void 0) return options.sourceFileCount === void 0 ? cached : {
|
|
68339
|
+
...cached,
|
|
68340
|
+
sourceFileCount: options.sourceFileCount
|
|
68341
|
+
};
|
|
68113
68342
|
const packageJsonPath = Path.join(directory, "package.json");
|
|
68114
68343
|
if (!isFile(packageJsonPath)) {
|
|
68115
|
-
const synthesized = discoverProjectWithoutPackageJson(directory);
|
|
68344
|
+
const synthesized = discoverProjectWithoutPackageJson(directory, options);
|
|
68116
68345
|
cachedProjectInfos.set(directory, synthesized);
|
|
68117
68346
|
return synthesized;
|
|
68118
68347
|
}
|
|
@@ -68175,7 +68404,7 @@ const discoverProject = (directory) => {
|
|
|
68175
68404
|
const zodVersion = zod.version;
|
|
68176
68405
|
const projectName = packageJson.name ?? Path.basename(directory);
|
|
68177
68406
|
const hasTypeScript = NFS.existsSync(Path.join(directory, "tsconfig.json"));
|
|
68178
|
-
const sourceFileCount = countSourceFiles(directory);
|
|
68407
|
+
const sourceFileCount = options.sourceFileCount ?? countSourceFiles(directory);
|
|
68179
68408
|
const hasReactNativeWorkspace = framework === "expo" || framework === "react-native" || workspaceFacts.hasReactNativeAwarePackage;
|
|
68180
68409
|
const expoVersion = hasReactNativeWorkspace ? resolveCatalogBackedDependencyVersion({
|
|
68181
68410
|
rootDirectory: directory,
|
|
@@ -69564,7 +69793,8 @@ const readSystemFacts$1 = () => ({
|
|
|
69564
69793
|
/**
|
|
69565
69794
|
* Auto lint-worker count: the smaller of the (cgroup-CPU-aware) core count and
|
|
69566
69795
|
* the number of `PER_WORKER_MEM_BUDGET_BYTES` workers that fit in available
|
|
69567
|
-
* memory,
|
|
69796
|
+
* memory, and the measured automatic parallel-efficiency ceiling, then clamped
|
|
69797
|
+
* to `[MIN, HARD_MAX]` by `resolveScanConcurrency`.
|
|
69568
69798
|
*
|
|
69569
69799
|
* `os.availableParallelism()` already respects cgroup CPU quotas, so the core
|
|
69570
69800
|
* term needs no help. Available memory is `os.totalmem()` floored by the cgroup
|
|
@@ -69580,7 +69810,7 @@ const readSystemFacts$1 = () => ({
|
|
|
69580
69810
|
const resolveAutoScanConcurrency = (facts = readSystemFacts$1()) => {
|
|
69581
69811
|
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
69582
69812
|
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / PER_WORKER_MEM_BUDGET_BYTES);
|
|
69583
|
-
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
69813
|
+
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers, 10));
|
|
69584
69814
|
};
|
|
69585
69815
|
const resolveLintBatchOrdering = () => {
|
|
69586
69816
|
return process.env["REACT_DOCTOR_LINT_BATCH_ORDERING"]?.trim().toLowerCase() === "arrival" ? "arrival" : "cost";
|
|
@@ -69593,24 +69823,22 @@ const resolveLintBatchOrdering = () => {
|
|
|
69593
69823
|
*/
|
|
69594
69824
|
var OxlintSpawnTimeoutMs = class extends Reference("react-doctor/OxlintSpawnTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_OXLINT_SPAWN_TIMEOUT_MS", OXLINT_SPAWN_TIMEOUT_MS) }) {};
|
|
69595
69825
|
/**
|
|
69596
|
-
* Effect-side cap on the lint phase.
|
|
69597
|
-
*
|
|
69826
|
+
* Optional Effect-side cap on the lint phase. Unbounded unless explicitly
|
|
69827
|
+
* configured through the env var or a test layer.
|
|
69598
69828
|
* Tests override via `Layer.succeed(LintPhaseTimeoutMs, ...)`.
|
|
69599
69829
|
*/
|
|
69600
|
-
var LintPhaseTimeoutMs = class extends Reference("react-doctor/LintPhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_LINT_PHASE_TIMEOUT_MS",
|
|
69830
|
+
var LintPhaseTimeoutMs = class extends Reference("react-doctor/LintPhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_LINT_PHASE_TIMEOUT_MS", null) }) {};
|
|
69601
69831
|
/**
|
|
69602
|
-
* Effect-side cap on the dead-code phase
|
|
69603
|
-
*
|
|
69604
|
-
* type-heavy projects; tests override via
|
|
69832
|
+
* Optional Effect-side cap on the dead-code phase. Unbounded unless explicitly
|
|
69833
|
+
* configured through the env var or a test layer. Tests override via
|
|
69605
69834
|
* `Layer.succeed(DeadCodePhaseTimeoutMs, ...)`.
|
|
69606
69835
|
*/
|
|
69607
|
-
var DeadCodePhaseTimeoutMs = class extends Reference("react-doctor/DeadCodePhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_DEAD_CODE_PHASE_TIMEOUT_MS",
|
|
69836
|
+
var DeadCodePhaseTimeoutMs = class extends Reference("react-doctor/DeadCodePhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_DEAD_CODE_PHASE_TIMEOUT_MS", null) }) {};
|
|
69608
69837
|
/**
|
|
69609
|
-
*
|
|
69610
|
-
*
|
|
69611
|
-
* large repos; tests override via `Layer.succeed(ScanDeadlineMs, ...)`.
|
|
69838
|
+
* Optional overall scan deadline. Unbounded unless explicitly configured
|
|
69839
|
+
* through the env var or a test layer.
|
|
69612
69840
|
*/
|
|
69613
|
-
var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SCAN_DEADLINE_MS",
|
|
69841
|
+
var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SCAN_DEADLINE_MS", null) }) {};
|
|
69614
69842
|
/**
|
|
69615
69843
|
* Wall-clock budget for the supply-chain check when it runs on a background
|
|
69616
69844
|
* fiber overlapping the lint pass. Reads from the env var on startup so the
|
|
@@ -69683,14 +69911,14 @@ var DeadCodeOverlap = class extends Reference("react-doctor/DeadCodeOverlap", {
|
|
|
69683
69911
|
/**
|
|
69684
69912
|
* How the full-scan lint pass plans its file batches. `"cost"` (the default)
|
|
69685
69913
|
* builds size-balanced LPT batches (`planLintBatches`): the same mandatory
|
|
69686
|
-
* batch count as greedy chunking
|
|
69687
|
-
*
|
|
69914
|
+
* batch count as greedy chunking, but every batch gets an even share of files
|
|
69915
|
+
* AND bytes, so no full chunk is a straggler while
|
|
69688
69916
|
* the remainder-batch worker idles — and the heavy files are SPREAD across
|
|
69689
69917
|
* batches, the precondition the old sort-desc-then-chunk-100 `cost` mode
|
|
69690
69918
|
* lacked (it packed the heaviest files into one wave-1 straggler batch,
|
|
69691
69919
|
* measurably regressing size-skewed repos, which is why it never earned the
|
|
69692
69920
|
* default). `"arrival"` (`REACT_DOCTOR_LINT_BATCH_ORDERING=arrival`) is the
|
|
69693
|
-
* rollback hatch to plain greedy
|
|
69921
|
+
* rollback hatch to plain greedy fixed-size chunking in discovery order. Tests
|
|
69694
69922
|
* override via `Layer.succeed(LintBatchOrdering, ...)`. Diff / staged scans
|
|
69695
69923
|
* never reach this — they pass user-scoped `includePaths` that skip discovery
|
|
69696
69924
|
* and stay in arrival order; only the full-scan branch reads it.
|
|
@@ -70256,6 +70484,10 @@ const dedupeRelatedDiagnostics = (diagnostics) => {
|
|
|
70256
70484
|
}
|
|
70257
70485
|
return uniqueDiagnostics;
|
|
70258
70486
|
};
|
|
70487
|
+
const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
|
|
70488
|
+
const relativePath = Path.relative(parentAbsolutePath, childAbsolutePath);
|
|
70489
|
+
return Boolean(relativePath) && !relativePath.startsWith("..") && !Path.isAbsolute(relativePath);
|
|
70490
|
+
};
|
|
70259
70491
|
const compareStrings = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
70260
70492
|
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));
|
|
70261
70493
|
const getDirectDependencyNames = (packageJson) => new Set([...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.devDependencies ?? {})]);
|
|
@@ -71832,7 +72064,7 @@ const readScannedFile = (candidate) => {
|
|
|
71832
72064
|
return null;
|
|
71833
72065
|
}
|
|
71834
72066
|
};
|
|
71835
|
-
function* collectSecurityScanFiles(rootDirectory) {
|
|
72067
|
+
function* collectSecurityScanFiles(rootDirectory, excludedDirectories = /* @__PURE__ */ new Set()) {
|
|
71836
72068
|
const priorityCandidates = [];
|
|
71837
72069
|
const artifactCandidates = [];
|
|
71838
72070
|
const otherCandidates = [];
|
|
@@ -71847,7 +72079,7 @@ function* collectSecurityScanFiles(rootDirectory) {
|
|
|
71847
72079
|
for (const entry of readDirectoryEntries(current.absolutePath)) {
|
|
71848
72080
|
const absolutePath = Path.join(current.absolutePath, entry.name);
|
|
71849
72081
|
if (entry.isDirectory()) {
|
|
71850
|
-
if (!SKIPPED_DIRECTORY_NAMES.has(entry.name)) stack.push({
|
|
72082
|
+
if (!SKIPPED_DIRECTORY_NAMES.has(entry.name) && !excludedDirectories.has(Path.resolve(absolutePath))) stack.push({
|
|
71851
72083
|
absolutePath,
|
|
71852
72084
|
depth: current.depth + 1
|
|
71853
72085
|
});
|
|
@@ -72028,10 +72260,8 @@ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledWhe
|
|
|
72028
72260
|
}
|
|
72029
72261
|
return true;
|
|
72030
72262
|
};
|
|
72263
|
+
const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
|
|
72031
72264
|
const shouldEnableRuleByDefaultStatus = (options) => options.defaultEnabled !== false || options.hasExplicitOverride === true || options.includeTagDefaults && options.hasIncludedTags;
|
|
72032
|
-
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
72033
|
-
setImmediate(resolve);
|
|
72034
|
-
});
|
|
72035
72265
|
const createSecurityScanSession = (rootDirectory, options) => {
|
|
72036
72266
|
const capabilities = options.project ? getCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
72037
72267
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
@@ -72084,19 +72314,35 @@ const createSecurityScanSession = (rootDirectory, options) => {
|
|
|
72084
72314
|
};
|
|
72085
72315
|
};
|
|
72086
72316
|
const checkSecurityScanCooperative = async (rootDirectory, options = {}) => {
|
|
72317
|
+
const throwIfScanAborted = () => {
|
|
72318
|
+
options.signal?.throwIfAborted();
|
|
72319
|
+
};
|
|
72320
|
+
const didReachDeadline = () => {
|
|
72321
|
+
const hasDeadlineElapsed = options.deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(options.deadlineEpochMs) === 0;
|
|
72322
|
+
if (hasDeadlineElapsed) options.onDeadlineExceeded?.();
|
|
72323
|
+
return hasDeadlineElapsed;
|
|
72324
|
+
};
|
|
72325
|
+
throwIfScanAborted();
|
|
72087
72326
|
const session = createSecurityScanSession(rootDirectory, options);
|
|
72088
72327
|
if (session === null) return [];
|
|
72328
|
+
if (didReachDeadline()) return [];
|
|
72089
72329
|
let sliceStartedAt = performance.now();
|
|
72090
|
-
for (const file of collectSecurityScanFiles(rootDirectory)) {
|
|
72330
|
+
for (const file of collectSecurityScanFiles(rootDirectory, options.excludedDirectories)) {
|
|
72331
|
+
throwIfScanAborted();
|
|
72332
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
72091
72333
|
if (file === null) {
|
|
72092
72334
|
if (performance.now() - sliceStartedAt >= 12) {
|
|
72093
72335
|
await yieldToEventLoop();
|
|
72336
|
+
throwIfScanAborted();
|
|
72337
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
72094
72338
|
sliceStartedAt = performance.now();
|
|
72095
72339
|
}
|
|
72096
72340
|
continue;
|
|
72097
72341
|
}
|
|
72098
72342
|
for (const _ruleStep of session.scanFileByRule(file)) if (performance.now() - sliceStartedAt >= 12) {
|
|
72099
72343
|
await yieldToEventLoop();
|
|
72344
|
+
throwIfScanAborted();
|
|
72345
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
72100
72346
|
sliceStartedAt = performance.now();
|
|
72101
72347
|
}
|
|
72102
72348
|
}
|
|
@@ -73026,7 +73272,6 @@ const isDiagnosticOnSurface = (diagnostic, surface, config) => {
|
|
|
73026
73272
|
return true;
|
|
73027
73273
|
};
|
|
73028
73274
|
const filterDiagnosticsForSurface = (diagnostics, surface, config) => diagnostics.filter((diagnostic) => isDiagnosticOnSurface(diagnostic, surface, config));
|
|
73029
|
-
const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
|
|
73030
73275
|
/**
|
|
73031
73276
|
* Budget for the dead-code phase, scaled to the work. deslop's graph build is
|
|
73032
73277
|
* CPU-bound and roughly linear in file count, so a fixed 120s cap is too tight
|
|
@@ -73034,25 +73279,28 @@ const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs
|
|
|
73034
73279
|
* tipped over by any concurrent load — silently dropping every dead-code
|
|
73035
73280
|
* finding. Scaling the budget with file count (and inversely with the core
|
|
73036
73281
|
* share when overlapped) lets the pass complete, while the ceiling still
|
|
73037
|
-
* reclaims a genuinely wedged worker.
|
|
73038
|
-
* and the Effect-side phase backstop that sits a margin above it.
|
|
73282
|
+
* reclaims a genuinely wedged worker.
|
|
73039
73283
|
*/
|
|
73040
73284
|
const resolveDeadCodeTimeout = (input) => {
|
|
73041
73285
|
const coreShareFactor = Math.max(1, input.fullConcurrency / Math.max(1, input.deadCodeConcurrency));
|
|
73042
|
-
|
|
73043
|
-
return {
|
|
73044
|
-
workerTimeoutMs,
|
|
73045
|
-
phaseTimeoutMs: workerTimeoutMs + DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS
|
|
73046
|
-
};
|
|
73286
|
+
return { workerTimeoutMs: Math.min(DEAD_CODE_TIMEOUT_CEILING_MS, Math.max(DEAD_CODE_WORKER_TIMEOUT_MS, Math.ceil(input.sourceFileCount * 30 * coreShareFactor))) };
|
|
73047
73287
|
};
|
|
73048
|
-
const resolveLintIncludePaths = (rootDirectory, userConfig) => {
|
|
73288
|
+
const resolveLintIncludePaths = (rootDirectory, userConfig, sourceFiles) => {
|
|
73049
73289
|
if (!Array.isArray(userConfig?.ignore?.files) || userConfig.ignore.files.length === 0) return;
|
|
73050
73290
|
const ignoredPatterns = compileIgnoredFilePatterns(userConfig);
|
|
73051
|
-
return listSourceFiles(rootDirectory).filter((filePath) => {
|
|
73291
|
+
return (sourceFiles ?? listSourceFiles(rootDirectory)).filter((filePath) => {
|
|
73052
73292
|
if (!isLintableSourceFile(filePath)) return false;
|
|
73053
73293
|
return !isFileIgnoredByPatterns(filePath, rootDirectory, ignoredPatterns);
|
|
73054
73294
|
});
|
|
73055
73295
|
};
|
|
73296
|
+
const filterPathsOutsideDirectories = (input) => {
|
|
73297
|
+
const excludedPrefixes = input.excludedDirectories.map((excludedDirectory) => Path.resolve(excludedDirectory)).filter((excludedDirectory) => isPathInsideDirectory(excludedDirectory, input.rootDirectory)).map((excludedDirectory) => Path.relative(input.rootDirectory, excludedDirectory).replaceAll("\\", "/")).sort((left, right) => left.length - right.length).filter((candidatePrefix, index, prefixes) => !prefixes.slice(0, index).some((prefix) => candidatePrefix.startsWith(`${prefix}/`)));
|
|
73298
|
+
if (excludedPrefixes.length === 0) return [...input.relativePaths];
|
|
73299
|
+
return input.relativePaths.filter((relativePath) => {
|
|
73300
|
+
const normalizedRelativePath = relativePath.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
73301
|
+
return !excludedPrefixes.some((excludedPrefix) => normalizedRelativePath === excludedPrefix || normalizedRelativePath.startsWith(`${excludedPrefix}/`));
|
|
73302
|
+
});
|
|
73303
|
+
};
|
|
73056
73304
|
var Config = class Config extends Service()("react-doctor/Config") {
|
|
73057
73305
|
static layerNode = effect(Config, gen(function* () {
|
|
73058
73306
|
const cache = yield* make$3({
|
|
@@ -73114,6 +73362,7 @@ var Files = class Files extends Service()("react-doctor/Files") {
|
|
|
73114
73362
|
static layerNode = succeed$3(Files, Files.of({
|
|
73115
73363
|
readLines: (input) => sync(() => createNodeReadFileLinesSync(input.rootDirectory)(input.filePath)),
|
|
73116
73364
|
listSourceFiles: (rootDirectory) => sync(() => listSourceFiles(rootDirectory)),
|
|
73365
|
+
listSourceFilesCooperative: (input) => promise(() => listSourceFilesCooperative(input.rootDirectory, input.signal)),
|
|
73117
73366
|
isFile: (filePath) => sync(() => isFile(filePath)),
|
|
73118
73367
|
isDirectory: (filePath) => sync(() => isDirectory(filePath))
|
|
73119
73368
|
}));
|
|
@@ -73140,6 +73389,16 @@ var Files = class Files extends Service()("react-doctor/Files") {
|
|
|
73140
73389
|
}
|
|
73141
73390
|
return files;
|
|
73142
73391
|
}),
|
|
73392
|
+
listSourceFilesCooperative: (input) => sync(() => {
|
|
73393
|
+
const rootDirectory = input.rootDirectory;
|
|
73394
|
+
const prefix = rootDirectory.endsWith("/") ? rootDirectory : `${rootDirectory}/`;
|
|
73395
|
+
const files = [];
|
|
73396
|
+
for (const absolute of tree.keys()) {
|
|
73397
|
+
if (!absolute.startsWith(prefix)) continue;
|
|
73398
|
+
files.push(absolute.slice(prefix.length));
|
|
73399
|
+
}
|
|
73400
|
+
return files;
|
|
73401
|
+
}),
|
|
73143
73402
|
isFile: (filePath) => sync(() => tree.has(filePath)),
|
|
73144
73403
|
isDirectory: (filePath) => sync(() => {
|
|
73145
73404
|
const prefix = filePath.endsWith("/") ? filePath : `${filePath}/`;
|
|
@@ -73715,7 +73974,7 @@ const batchIncludePaths = (baseArgs, includePaths) => {
|
|
|
73715
73974
|
for (const filePath of includePaths) {
|
|
73716
73975
|
const entryLength = filePath.length + 1;
|
|
73717
73976
|
const exceedsArgLength = currentBatch.length > 0 && currentBatchLength + entryLength > 24e3;
|
|
73718
|
-
const exceedsFileCount = currentBatch.length >=
|
|
73977
|
+
const exceedsFileCount = currentBatch.length >= 200;
|
|
73719
73978
|
if (exceedsArgLength || exceedsFileCount) {
|
|
73720
73979
|
batches.push(currentBatch);
|
|
73721
73980
|
currentBatch = [];
|
|
@@ -74062,7 +74321,7 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
74062
74321
|
}
|
|
74063
74322
|
return enabled;
|
|
74064
74323
|
};
|
|
74065
|
-
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), includedTags = /* @__PURE__ */ new Set(), includeTagDefaults = false, runtimeGlobals, unpluginAutoImportGlobalScopes, serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection, sidecarRuleIdFilter }) => {
|
|
74324
|
+
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), includedTags = /* @__PURE__ */ new Set(), includeTagDefaults = false, runtimeGlobals, unpluginAutoImportGlobalScopes, serverAuthFunctionNames, projectIndexModuleSources, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection, sidecarRuleIdFilter }) => {
|
|
74066
74325
|
const hasIncludedTags = includedTags.size > 0;
|
|
74067
74326
|
const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" || hasIncludedTags ? null : resolveReactHooksJsPlugin(project.hasReactCompiler || project.hasReactCompilerLintPlugin === true, customRulesOnly);
|
|
74068
74327
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
@@ -74126,7 +74385,8 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
74126
74385
|
}))
|
|
74127
74386
|
} : {},
|
|
74128
74387
|
...project.shopifyFlashListMajorVersion !== null ? { shopifyFlashListMajorVersion: project.shopifyFlashListMajorVersion } : {},
|
|
74129
|
-
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {}
|
|
74388
|
+
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {},
|
|
74389
|
+
...projectIndexModuleSources !== void 0 ? { projectIndexModuleSources: [...projectIndexModuleSources] } : {}
|
|
74130
74390
|
} },
|
|
74131
74391
|
rules: {
|
|
74132
74392
|
...reactCompilerRules,
|
|
@@ -75611,6 +75871,12 @@ const buildProfiledNodeArguments = (input) => [
|
|
|
75611
75871
|
...input.heapProfileDirectory ? ["--heap-prof", `--heap-prof-dir=${input.heapProfileDirectory}`] : [],
|
|
75612
75872
|
...input.argumentsList
|
|
75613
75873
|
];
|
|
75874
|
+
const lowerChildProcessPriority = (processId) => {
|
|
75875
|
+
if (processId === void 0) return;
|
|
75876
|
+
try {
|
|
75877
|
+
OS.setPriority(processId, OS.constants.priority.PRIORITY_BELOW_NORMAL);
|
|
75878
|
+
} catch {}
|
|
75879
|
+
};
|
|
75614
75880
|
const SANITIZED_ENV = buildOxlintChildEnv(process.env);
|
|
75615
75881
|
/**
|
|
75616
75882
|
* Spawn one oxlint subprocess with hard ceilings on wall time and
|
|
@@ -75649,6 +75915,7 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
|
|
|
75649
75915
|
"pipe"
|
|
75650
75916
|
]
|
|
75651
75917
|
});
|
|
75918
|
+
lowerChildProcessPriority(child.pid);
|
|
75652
75919
|
const onAbort = () => {
|
|
75653
75920
|
child.kill("SIGKILL");
|
|
75654
75921
|
reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) }));
|
|
@@ -75771,7 +76038,7 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
75771
76038
|
* loop with a slimmer config in that case.
|
|
75772
76039
|
*/
|
|
75773
76040
|
const spawnLintBatches = async (input) => {
|
|
75774
|
-
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, sourceMapByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth =
|
|
76041
|
+
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, sourceMapByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 9, signal, deadlineEpochMs } = input;
|
|
75775
76042
|
const resolveSourcePath = (filePath) => {
|
|
75776
76043
|
const absoluteFilePath = Path.isAbsolute(filePath) ? filePath : Path.resolve(rootDirectory, filePath);
|
|
75777
76044
|
return sourcePathByLintPath?.get(Path.normalize(absoluteFilePath)) ?? filePath;
|
|
@@ -75779,6 +76046,12 @@ const spawnLintBatches = async (input) => {
|
|
|
75779
76046
|
const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
|
|
75780
76047
|
let rescueDeadlineEpochMs;
|
|
75781
76048
|
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0 || rescueDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(rescueDeadlineEpochMs) === 0;
|
|
76049
|
+
const resolveSpawnTimeoutMs = () => {
|
|
76050
|
+
const deadlineBudgets = [deadlineEpochMs, rescueDeadlineEpochMs].filter((deadline) => deadline !== void 0).map(remainingDeadlineBudgetMs);
|
|
76051
|
+
if (deadlineBudgets.length === 0) return spawnTimeoutMs;
|
|
76052
|
+
const remainingDeadlineMs = Math.min(...deadlineBudgets);
|
|
76053
|
+
return spawnTimeoutMs === void 0 ? remainingDeadlineMs : Math.min(spawnTimeoutMs, remainingDeadlineMs);
|
|
76054
|
+
};
|
|
75782
76055
|
const runBatchPass = async (concurrency, passBatches, passOnFileProgress) => {
|
|
75783
76056
|
const totalFileCount = passBatches.reduce((sum, batch) => sum + batch.length, 0);
|
|
75784
76057
|
const allDiagnostics = [];
|
|
@@ -75796,12 +76069,13 @@ const spawnLintBatches = async (input) => {
|
|
|
75796
76069
|
const batchArgs = [...baseArgs, ...batch];
|
|
75797
76070
|
try {
|
|
75798
76071
|
const spawnBatch = () => {
|
|
75799
|
-
|
|
76072
|
+
const effectiveSpawnTimeoutMs = resolveSpawnTimeoutMs();
|
|
76073
|
+
if (effectiveSpawnTimeoutMs === 0) {
|
|
75800
76074
|
deadlineSkippedFiles.push(...batch);
|
|
75801
76075
|
batchState.deadlineSkippedFileCount += batch.length;
|
|
75802
76076
|
return Promise.resolve(null);
|
|
75803
76077
|
}
|
|
75804
|
-
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath,
|
|
76078
|
+
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, effectiveSpawnTimeoutMs, outputMaxBytes, signal, () => {
|
|
75805
76079
|
if (batchState.didStart) return;
|
|
75806
76080
|
batchState.didStart = true;
|
|
75807
76081
|
startedFileCount += batchState.initialFileCount;
|
|
@@ -75956,11 +76230,36 @@ const validateRuleRegistration = () => {
|
|
|
75956
76230
|
].filter((entry) => entry !== null).join("; ");
|
|
75957
76231
|
console.warn(`[react-doctor] rule-registration drift: ${detail}`);
|
|
75958
76232
|
};
|
|
76233
|
+
const PROJECT_INDEX_MODULE_SOURCES = [
|
|
76234
|
+
"next/og",
|
|
76235
|
+
"@vercel/og",
|
|
76236
|
+
"satori",
|
|
76237
|
+
"remotion"
|
|
76238
|
+
];
|
|
76239
|
+
const collectProjectIndexModuleSources = async (rootDirectory, candidateFiles) => {
|
|
76240
|
+
const foundModuleSources = /* @__PURE__ */ new Set();
|
|
76241
|
+
let sliceStartedAt = performance.now();
|
|
76242
|
+
for (const candidateFile of candidateFiles) {
|
|
76243
|
+
let sourceBuffer;
|
|
76244
|
+
try {
|
|
76245
|
+
sourceBuffer = NFS.readFileSync(Path.isAbsolute(candidateFile) ? candidateFile : Path.resolve(rootDirectory, candidateFile));
|
|
76246
|
+
} catch {
|
|
76247
|
+
continue;
|
|
76248
|
+
}
|
|
76249
|
+
for (const moduleSource of PROJECT_INDEX_MODULE_SOURCES) if (sourceBuffer.includes(`"${moduleSource}"`) || sourceBuffer.includes(`'${moduleSource}'`)) foundModuleSources.add(moduleSource);
|
|
76250
|
+
if (foundModuleSources.size === PROJECT_INDEX_MODULE_SOURCES.length) break;
|
|
76251
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
76252
|
+
await yieldToEventLoop();
|
|
76253
|
+
sliceStartedAt = performance.now();
|
|
76254
|
+
}
|
|
76255
|
+
}
|
|
76256
|
+
return [...foundModuleSources].sort();
|
|
76257
|
+
};
|
|
75959
76258
|
/**
|
|
75960
76259
|
* Balanced LPT (longest-processing-time-first) lint batch planner. Where
|
|
75961
|
-
* `batchIncludePaths` greedily fills
|
|
75962
|
-
* remainder batch
|
|
75963
|
-
*
|
|
76260
|
+
* `batchIncludePaths` greedily fills fixed-size batches and leaves a small
|
|
76261
|
+
* remainder batch, so the parallel pool's wall clock is set by whichever
|
|
76262
|
+
* full chunk happened to collect the most work while
|
|
75964
76263
|
* the remainder's worker idles), this planner keeps the SAME mandatory batch
|
|
75965
76264
|
* count — `ceil(files / OXLINT_MAX_FILES_PER_BATCH)`, the JS-plugin
|
|
75966
76265
|
* perf-cliff / native-binding SIGABRT guard — and assigns files largest-first,
|
|
@@ -75989,7 +76288,7 @@ const validateRuleRegistration = () => {
|
|
|
75989
76288
|
*/
|
|
75990
76289
|
const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
75991
76290
|
if (files.length === 0) return [];
|
|
75992
|
-
const batchCount = Math.ceil(files.length /
|
|
76291
|
+
const batchCount = Math.ceil(files.length / 200);
|
|
75993
76292
|
const maxFilesPerBatch = Math.ceil(files.length / batchCount);
|
|
75994
76293
|
const baseArgsLengthChars = estimateArgsLength(baseArgs);
|
|
75995
76294
|
const descendingSizeIndexes = [...files.keys()].sort((leftIndex, rightIndex) => (sizeByFile.get(files[rightIndex]) ?? 0) - (sizeByFile.get(files[leftIndex]) ?? 0));
|
|
@@ -76518,6 +76817,7 @@ const runOxlint = async (options) => {
|
|
|
76518
76817
|
await yieldToEventLoop();
|
|
76519
76818
|
const sizedScanFiles = includePaths === void 0 ? listSourceFilesWithSize(rootDirectory) : null;
|
|
76520
76819
|
const candidateFiles = includePaths !== void 0 ? includePaths : (sizedScanFiles ?? []).map((entry) => entry.path);
|
|
76820
|
+
const projectIndexModuleSources = includePaths === void 0 && options.deadlineEpochMs === void 0 ? await collectProjectIndexModuleSources(rootDirectory, candidateFiles) : void 0;
|
|
76521
76821
|
unpluginAutoImportGlobalScopes = collectUnpluginAutoImportGlobalScopes({
|
|
76522
76822
|
rootDirectory,
|
|
76523
76823
|
candidateFiles
|
|
@@ -76540,6 +76840,7 @@ const runOxlint = async (options) => {
|
|
|
76540
76840
|
runtimeGlobals,
|
|
76541
76841
|
unpluginAutoImportGlobalScopes,
|
|
76542
76842
|
serverAuthFunctionNames,
|
|
76843
|
+
projectIndexModuleSources,
|
|
76543
76844
|
severityControls,
|
|
76544
76845
|
userPlugins,
|
|
76545
76846
|
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
|
|
@@ -77007,10 +77308,10 @@ const translateProjectInfoError = (cause, directory) => {
|
|
|
77007
77308
|
}) });
|
|
77008
77309
|
};
|
|
77009
77310
|
var Project = class Project extends Service()("react-doctor/Project") {
|
|
77010
|
-
static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (
|
|
77311
|
+
static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (input) {
|
|
77011
77312
|
return yield* try_({
|
|
77012
|
-
try: () => discoverProject(directory),
|
|
77013
|
-
catch: (cause) => translateProjectInfoError(cause, directory)
|
|
77313
|
+
try: () => discoverProject(input.directory, { sourceFileCount: input.sourceFileCount }),
|
|
77314
|
+
catch: (cause) => translateProjectInfoError(cause, input.directory)
|
|
77014
77315
|
});
|
|
77015
77316
|
}) }));
|
|
77016
77317
|
static layerOf = (projectInfo) => succeed$3(Project, Project.of({ discover: () => succeed$2(projectInfo) }));
|
|
@@ -77595,9 +77896,10 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
77595
77896
|
* outage.
|
|
77596
77897
|
*/
|
|
77597
77898
|
var SupplyChain = class SupplyChain extends Service()("react-doctor/SupplyChain") {
|
|
77598
|
-
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (
|
|
77599
|
-
|
|
77600
|
-
|
|
77899
|
+
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (configuredTimeoutMs) => checkSupplyChain({
|
|
77900
|
+
rootDirectory: input.rootDirectory,
|
|
77901
|
+
userConfig: input.userConfig,
|
|
77902
|
+
totalTimeoutMs: input.timeoutMs ?? configuredTimeoutMs
|
|
77601
77903
|
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), withSpan("SupplyChain.run"))) }));
|
|
77602
77904
|
static layerOf = (diagnostics) => succeed$3(SupplyChain, SupplyChain.of({ run: () => fromIterable$1(diagnostics) }));
|
|
77603
77905
|
};
|
|
@@ -77632,10 +77934,6 @@ const NO_HOOKS = {
|
|
|
77632
77934
|
beforeLint: () => void_,
|
|
77633
77935
|
afterLint: () => void_
|
|
77634
77936
|
};
|
|
77635
|
-
const filterMapNullable = (transform) => fromPredicateOption((value) => {
|
|
77636
|
-
const result = transform(value);
|
|
77637
|
-
return result === null ? none() : some(result);
|
|
77638
|
-
});
|
|
77639
77937
|
const fileReader = (filesService, rootDirectory) => (filePath) => {
|
|
77640
77938
|
const lines = runSync(filesService.readLines({
|
|
77641
77939
|
filePath,
|
|
@@ -77659,7 +77957,7 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
77659
77957
|
* The GitHub viewer-permission lookup is forked onto a background
|
|
77660
77958
|
* fiber here and joined late (it feeds score metadata, not
|
|
77661
77959
|
* diagnostics).
|
|
77662
|
-
* 2. beforeLint hook (e.g. CLI
|
|
77960
|
+
* 2. beforeLint hook (e.g. CLI records project telemetry)
|
|
77663
77961
|
* 3. environment checks (reduced-motion + pnpm hardening +
|
|
77664
77962
|
* expo/react-native), collected synchronously. The heavier
|
|
77665
77963
|
* content-regex security scan is forked instead (like supply-chain
|
|
@@ -77672,13 +77970,11 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
77672
77970
|
* `SupplyChainOverlapTimeoutMs` (measured from fork) so a hung
|
|
77673
77971
|
* socket can't drag out its join; on timeout it fails open to no
|
|
77674
77972
|
* diagnostics — the same outcome class as a Socket outage.
|
|
77675
|
-
* 5. Linter.run runs; DeadCode.run
|
|
77676
|
-
*
|
|
77677
|
-
*
|
|
77678
|
-
*
|
|
77679
|
-
*
|
|
77680
|
-
* fiber is joined (or interrupted, SIGKILLing its worker, on lint
|
|
77681
|
-
* failure) before diagnostics are concatenated. The afterLint hook
|
|
77973
|
+
* 5. Linter.run runs; DeadCode.run forks only when
|
|
77974
|
+
* REACT_DOCTOR_DEAD_CODE_OVERLAP=on. The default "auto" and explicit
|
|
77975
|
+
* "off" paths stay sequential. The fiber is joined (or interrupted,
|
|
77976
|
+
* SIGKILLing its worker, on lint failure) before diagnostics are
|
|
77977
|
+
* concatenated. The afterLint hook
|
|
77682
77978
|
* fires between lint and dead-code. Progress spinner labels AND the
|
|
77683
77979
|
* final diagnostic / score order stay independent of execution
|
|
77684
77980
|
* order, so terminal output is identical either way; supply-chain
|
|
@@ -77713,8 +78009,28 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77713
78009
|
const partialFailuresRef = yield* LintPartialFailures;
|
|
77714
78010
|
const resolvedConfig = yield* configService.resolve(input.directory);
|
|
77715
78011
|
const scanDirectory = resolvedConfig.resolvedDirectory;
|
|
77716
|
-
const
|
|
78012
|
+
const excludedProjectDirectories = (input.excludedProjectDirectories ?? []).map((excludedDirectory) => Path.resolve(excludedDirectory)).filter((excludedDirectory) => isPathInsideDirectory(excludedDirectory, scanDirectory));
|
|
78013
|
+
const excludedProjectRelativePaths = excludedProjectDirectories.map((excludedDirectory) => Path.relative(scanDirectory, excludedDirectory).replaceAll("\\", "/"));
|
|
78014
|
+
const isExcludedProjectDiagnostic = (diagnostic) => {
|
|
78015
|
+
const relativeFilePath = toNormalizedRelativePath(diagnostic.filePath, scanDirectory);
|
|
78016
|
+
return excludedProjectRelativePaths.some((excludedRelativePath) => relativeFilePath === excludedRelativePath || relativeFilePath.startsWith(`${excludedRelativePath}/`));
|
|
78017
|
+
};
|
|
78018
|
+
const precomputedSourceFiles = input.suppressScanSummary === true || excludedProjectDirectories.length > 0 ? yield* filesService.listSourceFilesCooperative({
|
|
78019
|
+
rootDirectory: scanDirectory,
|
|
78020
|
+
signal: input.signal
|
|
78021
|
+
}) : null;
|
|
78022
|
+
const includedPrecomputedSourceFiles = precomputedSourceFiles !== null && excludedProjectDirectories.length > 0 ? filterPathsOutsideDirectories({
|
|
78023
|
+
rootDirectory: scanDirectory,
|
|
78024
|
+
relativePaths: precomputedSourceFiles,
|
|
78025
|
+
excludedDirectories: excludedProjectDirectories
|
|
78026
|
+
}) : precomputedSourceFiles;
|
|
78027
|
+
const sourceFileCount = excludedProjectDirectories.length > 0 ? includedPrecomputedSourceFiles?.length : input.precomputedSourceFileCount ?? includedPrecomputedSourceFiles?.length;
|
|
78028
|
+
const project = yield* projectService.discover({
|
|
78029
|
+
directory: scanDirectory,
|
|
78030
|
+
sourceFileCount
|
|
78031
|
+
});
|
|
77717
78032
|
if (!isAnalyzableProject(project)) return yield* new ReactDoctorError({ reason: new NoReactDependency({ directory: scanDirectory }) });
|
|
78033
|
+
const deadCodeSourceFileCount = input.retainExcludedProjectDeadCodeDiagnostics === true && precomputedSourceFiles !== null ? precomputedSourceFiles.length : project.sourceFileCount;
|
|
77718
78034
|
const [repo, sha, defaultBranch] = yield* all([
|
|
77719
78035
|
gitService.githubRepo(scanDirectory).pipe(orElseSucceed(() => null)),
|
|
77720
78036
|
gitService.headSha(scanDirectory).pipe(orElseSucceed(() => null)),
|
|
@@ -77725,11 +78041,16 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77725
78041
|
directory: scanDirectory,
|
|
77726
78042
|
repo
|
|
77727
78043
|
}).pipe(orElseSucceed(() => null)) : succeed$2(null));
|
|
77728
|
-
|
|
77729
|
-
|
|
78044
|
+
let lintIncludePaths = (input.skipExplicitIncludePathFilter ? input.includePaths.length > 0 ? [...input.includePaths] : void 0 : computeExplicitLintIncludePaths([...input.includePaths])) ?? resolveLintIncludePaths(scanDirectory, resolvedConfig.config, includedPrecomputedSourceFiles ?? void 0);
|
|
78045
|
+
if (excludedProjectDirectories.length > 0) lintIncludePaths = filterPathsOutsideDirectories({
|
|
78046
|
+
rootDirectory: scanDirectory,
|
|
78047
|
+
relativePaths: lintIncludePaths ?? includedPrecomputedSourceFiles ?? (yield* filesService.listSourceFiles(scanDirectory)),
|
|
78048
|
+
excludedDirectories: excludedProjectDirectories
|
|
78049
|
+
});
|
|
78050
|
+
const fallbackScannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? includedPrecomputedSourceFiles ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
|
|
77730
78051
|
const beforeLint = hooks.beforeLint ?? NO_HOOKS.beforeLint;
|
|
77731
78052
|
const afterLint = hooks.afterLint ?? NO_HOOKS.afterLint;
|
|
77732
|
-
yield* beforeLint(project
|
|
78053
|
+
yield* beforeLint(project);
|
|
77733
78054
|
const isDiffMode = input.includePaths.length > 0;
|
|
77734
78055
|
const showWarnings = input.warnings ?? resolvedConfig.config?.warnings ?? true;
|
|
77735
78056
|
const transform = buildDiagnosticPipeline({
|
|
@@ -77739,7 +78060,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77739
78060
|
respectInlineDisables: input.respectInlineDisables,
|
|
77740
78061
|
showWarnings
|
|
77741
78062
|
});
|
|
77742
|
-
const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(
|
|
78063
|
+
const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(fromPredicateOption((diagnostic) => {
|
|
78064
|
+
const filteredDiagnostic = transform.apply(diagnostic);
|
|
78065
|
+
return filteredDiagnostic === null ? none() : some(filteredDiagnostic);
|
|
78066
|
+
})));
|
|
77743
78067
|
const applyPerElementPipeline = (rawStream) => filterPerElementPipeline(rawStream).pipe(tap((diagnostic) => reporterService.emit(diagnostic)));
|
|
77744
78068
|
const envCollected = yield* runCollect(applyPerElementPipeline(fromIterable$1(isDiffMode ? [] : [
|
|
77745
78069
|
...checkReducedMotion(scanDirectory),
|
|
@@ -77749,17 +78073,31 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77749
78073
|
...checkReactNativeProject(scanDirectory, project)
|
|
77750
78074
|
])));
|
|
77751
78075
|
const securityScanFailedRef = yield* make$13(false);
|
|
77752
|
-
|
|
78076
|
+
let didSecurityScanReachDeadline = false;
|
|
78077
|
+
const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(tryPromise((signal) => checkSecurityScanCooperative(scanDirectory, {
|
|
77753
78078
|
project,
|
|
77754
78079
|
ignoredTags: input.ignoredTags,
|
|
77755
78080
|
includedTags: input.includedTags,
|
|
77756
|
-
includeTagDefaults: input.includeTagDefaults
|
|
78081
|
+
includeTagDefaults: input.includeTagDefaults,
|
|
78082
|
+
excludedDirectories: new Set(excludedProjectDirectories),
|
|
78083
|
+
deadlineEpochMs: input.deadlineEpochMs,
|
|
78084
|
+
signal,
|
|
78085
|
+
onDeadlineExceeded: () => {
|
|
78086
|
+
didSecurityScanReachDeadline = true;
|
|
78087
|
+
}
|
|
77757
78088
|
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), catch_$1(() => set(securityScanFailedRef, true).pipe(as(empty$4))))))).pipe(withSpan("SecurityScan.run")));
|
|
78089
|
+
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
78090
|
+
const capOptionalToDeadline = (phaseTimeoutMs) => {
|
|
78091
|
+
if (input.deadlineEpochMs === void 0) return phaseTimeoutMs;
|
|
78092
|
+
const remainingBudgetMs = remainingDeadlineBudgetMs(input.deadlineEpochMs);
|
|
78093
|
+
return phaseTimeoutMs === null ? remainingBudgetMs : Math.min(phaseTimeoutMs, remainingBudgetMs);
|
|
78094
|
+
};
|
|
77758
78095
|
const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
|
|
77759
|
-
const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
|
|
78096
|
+
const supplyChainOverlapTimeout = capToDeadline(yield* SupplyChainOverlapTimeoutMs);
|
|
77760
78097
|
const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
|
|
77761
78098
|
rootDirectory: scanDirectory,
|
|
77762
|
-
userConfig: resolvedConfig.config
|
|
78099
|
+
userConfig: resolvedConfig.config,
|
|
78100
|
+
timeoutMs: supplyChainOverlapTimeout
|
|
77763
78101
|
}))).pipe(map$3((diagnostics) => ({
|
|
77764
78102
|
diagnostics,
|
|
77765
78103
|
timedOut: false
|
|
@@ -77783,46 +78121,49 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77783
78121
|
const scanConcurrency = resolveScanConcurrency(yield* OxlintConcurrency);
|
|
77784
78122
|
const lintPhaseTimeoutMs = yield* LintPhaseTimeoutMs;
|
|
77785
78123
|
const deadCodePhaseTimeoutMs = yield* DeadCodePhaseTimeoutMs;
|
|
77786
|
-
const resolveDeadCodePhaseTimeoutMs = (scaledPhaseTimeoutMs) => deadCodePhaseTimeoutMs === 15e4 ? scaledPhaseTimeoutMs : deadCodePhaseTimeoutMs;
|
|
77787
78124
|
const workerCountSuffix = scanConcurrency > 1 ? ` ${highlighter.dim(`[~${scanConcurrency} workers]`)}` : "";
|
|
77788
|
-
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
77789
78125
|
const shouldRunDeadCode = input.runDeadCode && !isDiffMode && (showWarnings || deadCodeMaySurfaceWhenWarningsHidden(resolvedConfig.config));
|
|
77790
78126
|
const deadCodeOverlapMode = yield* DeadCodeOverlap;
|
|
77791
78127
|
const shouldOverlapDeadCode = shouldRunDeadCode && deadCodeOverlapMode === "on";
|
|
77792
78128
|
const deadCodeParseConcurrency = shouldOverlapDeadCode ? Math.max(1, Math.floor(scanConcurrency * DEAD_CODE_OVERLAP_PARSE_SHARE)) : void 0;
|
|
77793
78129
|
const lintConcurrency = deadCodeParseConcurrency === void 0 ? scanConcurrency : Math.max(1, scanConcurrency - deadCodeParseConcurrency);
|
|
77794
|
-
const buildCollectDeadCode = (deadCodeTimeout) =>
|
|
77795
|
-
|
|
77796
|
-
|
|
77797
|
-
|
|
77798
|
-
|
|
77799
|
-
|
|
77800
|
-
|
|
77801
|
-
|
|
77802
|
-
|
|
77803
|
-
|
|
77804
|
-
|
|
77805
|
-
|
|
77806
|
-
|
|
77807
|
-
|
|
77808
|
-
|
|
77809
|
-
|
|
77810
|
-
|
|
77811
|
-
|
|
77812
|
-
|
|
77813
|
-
|
|
77814
|
-
|
|
77815
|
-
|
|
77816
|
-
|
|
77817
|
-
|
|
78130
|
+
const buildCollectDeadCode = (deadCodeTimeout) => {
|
|
78131
|
+
const collectDeadCode = runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
78132
|
+
rootDirectory: scanDirectory,
|
|
78133
|
+
parseConcurrency: deadCodeParseConcurrency,
|
|
78134
|
+
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
|
|
78135
|
+
onCacheOutcome: (didHitCache) => {
|
|
78136
|
+
deadCodeCacheHit = didHitCache;
|
|
78137
|
+
},
|
|
78138
|
+
onSummaryCacheStats: (stats) => {
|
|
78139
|
+
deadCodeSummaryCacheHits = stats.hits;
|
|
78140
|
+
deadCodeSummaryCacheMisses = stats.misses;
|
|
78141
|
+
}
|
|
78142
|
+
}).pipe(filter((diagnostic) => input.retainExcludedProjectDeadCodeDiagnostics === true || !isExcludedProjectDiagnostic(diagnostic)), catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
78143
|
+
yield* set(deadCodeFailure, {
|
|
78144
|
+
didFail: true,
|
|
78145
|
+
reason: error.message
|
|
78146
|
+
});
|
|
78147
|
+
return empty$4;
|
|
78148
|
+
}))))));
|
|
78149
|
+
const phaseTimeoutMs = deadCodeTimeout.phaseTimeoutMs;
|
|
78150
|
+
if (phaseTimeoutMs === null) return collectDeadCode;
|
|
78151
|
+
return collectDeadCode.pipe(timeoutOption(phaseTimeoutMs), flatMap$2(match$3({
|
|
78152
|
+
onNone: () => set(deadCodeFailure, {
|
|
78153
|
+
didFail: true,
|
|
78154
|
+
reason: `Dead-code analysis exceeded ${Math.round(phaseTimeoutMs / MILLISECONDS_PER_SECOND)}s and was skipped.`
|
|
78155
|
+
}).pipe(as([])),
|
|
78156
|
+
onSome: succeed$2
|
|
78157
|
+
})));
|
|
78158
|
+
};
|
|
77818
78159
|
const overlapDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
77819
|
-
sourceFileCount:
|
|
78160
|
+
sourceFileCount: deadCodeSourceFileCount,
|
|
77820
78161
|
deadCodeConcurrency: deadCodeParseConcurrency ?? scanConcurrency,
|
|
77821
78162
|
fullConcurrency: scanConcurrency
|
|
77822
78163
|
});
|
|
77823
78164
|
const deadCodeFiber = shouldOverlapDeadCode ? yield* forkChild(buildCollectDeadCode({
|
|
77824
78165
|
workerTimeoutMs: overlapDeadCodeTimeout.workerTimeoutMs,
|
|
77825
|
-
phaseTimeoutMs:
|
|
78166
|
+
phaseTimeoutMs: capOptionalToDeadline(deadCodePhaseTimeoutMs)
|
|
77826
78167
|
})) : null;
|
|
77827
78168
|
const scanProgress = yield* progressService.start("Scanning...");
|
|
77828
78169
|
const scanStartTime = Date.now();
|
|
@@ -77873,7 +78214,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77873
78214
|
});
|
|
77874
78215
|
return empty$4;
|
|
77875
78216
|
}))));
|
|
77876
|
-
const
|
|
78217
|
+
const collectLintDiagnostics = runCollect(filterPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream));
|
|
78218
|
+
const lintCollected = dedupeRelatedDiagnostics(yield* lintPhaseTimeoutMs === null ? collectLintDiagnostics : collectLintDiagnostics.pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
|
|
77877
78219
|
onNone: () => set(lintFailure, {
|
|
77878
78220
|
didFail: true,
|
|
77879
78221
|
reason: `Lint analysis exceeded ${lintPhaseTimeoutMs / MILLISECONDS_PER_SECOND}s and was skipped.`,
|
|
@@ -77903,13 +78245,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77903
78245
|
} else {
|
|
77904
78246
|
yield* scanProgress.update(`Scanned ${scannedFilesLabel}, analyzing dead code...`);
|
|
77905
78247
|
const sequentialDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
77906
|
-
sourceFileCount: totalFileCount,
|
|
78248
|
+
sourceFileCount: input.retainExcludedProjectDeadCodeDiagnostics === true ? deadCodeSourceFileCount : totalFileCount,
|
|
77907
78249
|
deadCodeConcurrency: scanConcurrency,
|
|
77908
78250
|
fullConcurrency: scanConcurrency
|
|
77909
78251
|
});
|
|
77910
78252
|
deadCodeCollected = deadCodeFiber !== null ? yield* join(deadCodeFiber) : yield* buildCollectDeadCode({
|
|
77911
78253
|
workerTimeoutMs: sequentialDeadCodeTimeout.workerTimeoutMs,
|
|
77912
|
-
phaseTimeoutMs:
|
|
78254
|
+
phaseTimeoutMs: capOptionalToDeadline(deadCodePhaseTimeoutMs)
|
|
77913
78255
|
});
|
|
77914
78256
|
}
|
|
77915
78257
|
const deadCodeFailureState = lintFailureState.didFail ? {
|
|
@@ -77953,7 +78295,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77953
78295
|
metadata: scoreMetadata
|
|
77954
78296
|
});
|
|
77955
78297
|
const lintPartialFailures = yield* get$2(partialFailuresRef);
|
|
77956
|
-
const
|
|
78298
|
+
const didSecurityScanFail = yield* get$2(securityScanFailedRef);
|
|
78299
|
+
const securityScanFailed = didSecurityScanFail || didSecurityScanReachDeadline;
|
|
78300
|
+
let securityScanFailureReason = null;
|
|
78301
|
+
if (didSecurityScanReachDeadline) securityScanFailureReason = "Security scan reached the max scan duration; findings collected before the deadline were preserved.";
|
|
78302
|
+
else if (didSecurityScanFail) securityScanFailureReason = "Security scan failed and was skipped.";
|
|
77957
78303
|
return {
|
|
77958
78304
|
project,
|
|
77959
78305
|
userConfig: resolvedConfig.config,
|
|
@@ -77976,6 +78322,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77976
78322
|
scanConcurrency,
|
|
77977
78323
|
supplyChainOverlapTimedOut: supplyChainResult.timedOut,
|
|
77978
78324
|
securityScanFailed,
|
|
78325
|
+
securityScanFailureReason,
|
|
77979
78326
|
lintCacheHitFileCount,
|
|
77980
78327
|
lintCacheTotalFileCount,
|
|
77981
78328
|
lintSidecarReplayedFileCount,
|
|
@@ -77991,7 +78338,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77991
78338
|
"inspect.runDeadCode": input.runDeadCode,
|
|
77992
78339
|
"inspect.isCi": input.isCi,
|
|
77993
78340
|
"inspect.scoreSurface": input.scoreSurface ?? "score"
|
|
77994
|
-
} }), (scanProgram) => flatMap$2(ScanDeadlineMs, (scanDeadlineMs) =>
|
|
78341
|
+
} }), (scanProgram) => flatMap$2(ScanDeadlineMs, (scanDeadlineMs) => {
|
|
78342
|
+
if (scanDeadlineMs === null) return scanProgram;
|
|
78343
|
+
return scanProgram.pipe(timeout(scanDeadlineMs), catchTag$1("TimeoutError", () => new ReactDoctorError({ reason: new ScanDeadlineExceeded({ detail: `${scanDeadlineMs / MILLISECONDS_PER_SECOND}s elapsed` }) })));
|
|
78344
|
+
}));
|
|
77995
78345
|
const parseNodeVersion = (versionString) => {
|
|
77996
78346
|
const [major = 0, minor = 0, patch = 0] = versionString.replace(/^v/, "").trim().split(".").map(Number);
|
|
77997
78347
|
return {
|
|
@@ -78103,16 +78453,6 @@ var NodeResolver = class NodeResolver extends Service()("react-doctor/NodeResolv
|
|
|
78103
78453
|
});
|
|
78104
78454
|
};
|
|
78105
78455
|
/**
|
|
78106
|
-
* Zip-Slip defense: relative paths come from git (`diff --name-only`), which
|
|
78107
|
-
* normalizes during ordinary adds, but a crafted index/pack/symlinked tree can
|
|
78108
|
-
* smuggle `..` segments that escape the temp root. Resolve against the temp dir
|
|
78109
|
-
* and reject anything that lands outside before writing.
|
|
78110
|
-
*/
|
|
78111
|
-
const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
|
|
78112
|
-
const relative = path.relative(parentAbsolutePath, childAbsolutePath);
|
|
78113
|
-
return Boolean(relative) && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
78114
|
-
};
|
|
78115
|
-
/**
|
|
78116
78456
|
* Writes a set of source files (supplied by `readContent` — e.g.
|
|
78117
78457
|
* `git show <ref>:<path>` for a baseline tree, or `git show :<path>` for the
|
|
78118
78458
|
* index) into a temp tree that mirrors the project layout, copying the
|
|
@@ -78400,6 +78740,7 @@ const buildJsonReport = (input) => {
|
|
|
78400
78740
|
directory: input.directory,
|
|
78401
78741
|
diff: toJsonDiff(input.diff),
|
|
78402
78742
|
projects,
|
|
78743
|
+
...input.skippedProjects && input.skippedProjects.length > 0 ? { skippedProjects: [...input.skippedProjects] } : {},
|
|
78403
78744
|
diagnostics: flattenedDiagnostics,
|
|
78404
78745
|
summary,
|
|
78405
78746
|
elapsedMilliseconds: input.totalElapsedMilliseconds,
|
|
@@ -78424,7 +78765,7 @@ const buildSkippedChecks = (input) => {
|
|
|
78424
78765
|
else if (input.lintPartialFailures.length > 0) skippedCheckReasons["lint:partial"] = input.lintPartialFailures.join("; ");
|
|
78425
78766
|
if (input.didDeadCodeFail && input.deadCodeFailureReason !== null) skippedCheckReasons["dead-code"] = input.deadCodeFailureReason;
|
|
78426
78767
|
if (input.supplyChainOverlapTimedOut) skippedCheckReasons["supply-chain"] = "Supply-chain analysis timed out and was skipped.";
|
|
78427
|
-
if (input.securityScanFailed) skippedCheckReasons["security-scan"] = "Security scan failed and was skipped.";
|
|
78768
|
+
if (input.securityScanFailed) skippedCheckReasons["security-scan"] = input.securityScanFailureReason ?? "Security scan failed and was skipped.";
|
|
78428
78769
|
return {
|
|
78429
78770
|
skippedChecks,
|
|
78430
78771
|
skippedCheckReasons
|
|
@@ -78564,6 +78905,33 @@ const getChangedLineRanges = (input) => runPromise(gen(function* () {
|
|
|
78564
78905
|
* instead of being improvised per diagnostic.
|
|
78565
78906
|
*/
|
|
78566
78907
|
const buildRuleDocsUrl = (plugin, rule) => `${DOCS_RULES_BASE_URL}/${plugin}/${rule}`;
|
|
78908
|
+
const collectSourceFileCountsByDirectory = async (rootDirectory, projectDirectories, signal) => {
|
|
78909
|
+
const resolvedRootDirectory = Path.resolve(rootDirectory);
|
|
78910
|
+
const sourceFileCounts = /* @__PURE__ */ new Map();
|
|
78911
|
+
const projectDirectoryByRelativePath = /* @__PURE__ */ new Map();
|
|
78912
|
+
for (const projectDirectory of projectDirectories) {
|
|
78913
|
+
const resolvedProjectDirectory = Path.resolve(projectDirectory);
|
|
78914
|
+
const relativeDirectory = Path.relative(resolvedRootDirectory, resolvedProjectDirectory);
|
|
78915
|
+
if (Path.isAbsolute(relativeDirectory) || relativeDirectory.startsWith("..")) continue;
|
|
78916
|
+
sourceFileCounts.set(resolvedProjectDirectory, 0);
|
|
78917
|
+
projectDirectoryByRelativePath.set(relativeDirectory.replaceAll(Path.sep, "/"), resolvedProjectDirectory);
|
|
78918
|
+
}
|
|
78919
|
+
const sourceFilePaths = await listSourceFilesCooperative(resolvedRootDirectory, signal);
|
|
78920
|
+
for (const sourceFilePath of sourceFilePaths) {
|
|
78921
|
+
let relativeDirectory = Path.posix.dirname(sourceFilePath);
|
|
78922
|
+
while (true) {
|
|
78923
|
+
const normalizedRelativeDirectory = relativeDirectory === "." ? "" : relativeDirectory;
|
|
78924
|
+
const projectDirectory = projectDirectoryByRelativePath.get(normalizedRelativeDirectory);
|
|
78925
|
+
if (projectDirectory !== void 0) {
|
|
78926
|
+
sourceFileCounts.set(projectDirectory, (sourceFileCounts.get(projectDirectory) ?? 0) + 1);
|
|
78927
|
+
break;
|
|
78928
|
+
}
|
|
78929
|
+
if (relativeDirectory === ".") break;
|
|
78930
|
+
relativeDirectory = Path.posix.dirname(relativeDirectory);
|
|
78931
|
+
}
|
|
78932
|
+
}
|
|
78933
|
+
return sourceFileCounts;
|
|
78934
|
+
};
|
|
78567
78935
|
const computeConfigFingerprint = (projectDirectory, version) => {
|
|
78568
78936
|
const parts = [`v=${version}`];
|
|
78569
78937
|
let directory = projectDirectory;
|
|
@@ -78911,7 +79279,7 @@ const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
|
|
|
78911
79279
|
const SCAN_RESULT_CACHE_FILENAME = "scan-cache.json";
|
|
78912
79280
|
const RUN_GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
78913
79281
|
const GH_DEFAULT_BRANCH_PROBE_TIMEOUT_MS = 5e3;
|
|
78914
|
-
const TUI_REPORT_COMPACT_MAX_ROWS =
|
|
79282
|
+
const TUI_REPORT_COMPACT_MAX_ROWS = 23;
|
|
78915
79283
|
const TUI_REPORT_DETAIL_WIDTH_FRACTION = .6;
|
|
78916
79284
|
const CI_TRUST_COMPANIES = "PayPal, Rippling, and Alibaba";
|
|
78917
79285
|
const WELCOME_EXPLANATION_HOLD_MS = 1e3;
|
|
@@ -78934,8 +79302,11 @@ const METRIC = {
|
|
|
78934
79302
|
scanPhaseDuration: "scan.phase_duration",
|
|
78935
79303
|
scanFiles: "scan.files",
|
|
78936
79304
|
scanScore: "scan.score",
|
|
79305
|
+
scanScoreRetry: "scan.score_retry",
|
|
78937
79306
|
scanClean: "scan.clean",
|
|
78938
79307
|
scanCheckSkipped: "scan.check_skipped",
|
|
79308
|
+
scanProjectSkipped: "scan.project_skipped",
|
|
79309
|
+
scanWorkspaceDeadCodeShared: "scan.workspace_deadcode_shared",
|
|
78939
79310
|
scanNoReactDetected: "scan.no_react_detected",
|
|
78940
79311
|
baselineDegraded: "baseline.degraded",
|
|
78941
79312
|
ruleFired: "rule.fired",
|
|
@@ -78962,9 +79333,14 @@ const METRIC = {
|
|
|
78962
79333
|
lspScanCompleted: "lsp.scan.completed",
|
|
78963
79334
|
lspScanDuration: "lsp.scan.duration",
|
|
78964
79335
|
lspScanDiagnostics: "lsp.scan.diagnostics",
|
|
78965
|
-
tuiActionMenuOpened: "tui.action_menu_opened",
|
|
78966
79336
|
tuiCompactReportShown: "tui.compact_report_shown",
|
|
78967
79337
|
tuiFindingNavigated: "tui.finding_navigated",
|
|
79338
|
+
tuiIssueStreamShown: "tui.issue_stream_shown",
|
|
79339
|
+
tuiProjectPathContextShown: "tui.project_path_context_shown",
|
|
79340
|
+
tuiProjectSelectInlineShown: "tui.project_select_inline_shown",
|
|
79341
|
+
tuiReportActionSelected: "tui.report_action_selected",
|
|
79342
|
+
tuiCancelled: "tui.cancelled",
|
|
79343
|
+
tuiScanInlineShown: "tui.scan_inline_shown",
|
|
78968
79344
|
tuiStackedReportCapped: "tui.stacked_report_capped",
|
|
78969
79345
|
aiTrainingWarningShown: "ai.training.warning_shown",
|
|
78970
79346
|
jsonOutUsed: "json.out_used"
|
|
@@ -79010,7 +79386,7 @@ const makeNoopConsole = () => ({
|
|
|
79010
79386
|
});
|
|
79011
79387
|
//#endregion
|
|
79012
79388
|
//#region src/cli/utils/version.ts
|
|
79013
|
-
const VERSION = "0.9.3-dev.
|
|
79389
|
+
const VERSION = "0.9.3-dev.3466fe1";
|
|
79014
79390
|
//#endregion
|
|
79015
79391
|
//#region src/cli/utils/json-mode.ts
|
|
79016
79392
|
let context = null;
|
|
@@ -79368,13 +79744,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
79368
79744
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
79369
79745
|
* standard `SENTRY_RELEASE` override.
|
|
79370
79746
|
*/
|
|
79371
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.3-dev.
|
|
79747
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.3-dev.3466fe1`;
|
|
79372
79748
|
/**
|
|
79373
79749
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
79374
79750
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
79375
79751
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
79376
79752
|
*/
|
|
79377
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.3-dev.
|
|
79753
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.3-dev.3466fe1") ? "development" : "production");
|
|
79378
79754
|
/**
|
|
79379
79755
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
79380
79756
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -91050,6 +91426,19 @@ const ciInstallAction = (options) => runCiCommand("install", runCiInstall, optio
|
|
|
91050
91426
|
const ciUpgradeAction = (options) => runCiCommand("upgrade", runCiUpgrade, options);
|
|
91051
91427
|
const ciConfigAction = (options) => runCiCommand("config", runCiConfig, options);
|
|
91052
91428
|
//#endregion
|
|
91429
|
+
//#region src/cli/utils/active-scan-abort-registry.ts
|
|
91430
|
+
const activeScanAbortControllers = /* @__PURE__ */ new Set();
|
|
91431
|
+
const activeScanAbortRegistry = {
|
|
91432
|
+
register: (controller) => {
|
|
91433
|
+
activeScanAbortControllers.add(controller);
|
|
91434
|
+
return () => activeScanAbortControllers.delete(controller);
|
|
91435
|
+
},
|
|
91436
|
+
abortAll: () => {
|
|
91437
|
+
for (const controller of activeScanAbortControllers) controller.abort();
|
|
91438
|
+
activeScanAbortControllers.clear();
|
|
91439
|
+
}
|
|
91440
|
+
};
|
|
91441
|
+
//#endregion
|
|
91053
91442
|
//#region src/cli/utils/sentry-tracer.ts
|
|
91054
91443
|
const toHrTime = (epochNanoseconds) => [Number(epochNanoseconds / NANOSECONDS_PER_SECOND), Number(epochNanoseconds % NANOSECONDS_PER_SECOND)];
|
|
91055
91444
|
const SPAN_KIND_TO_OTEL = {
|
|
@@ -91466,7 +91855,14 @@ const isInspectResultComplete = (result) => isScanComplete({
|
|
|
91466
91855
|
skippedCheckReasonCount: Object.keys(result.skippedCheckReasons ?? {}).length
|
|
91467
91856
|
});
|
|
91468
91857
|
//#endregion
|
|
91858
|
+
//#region src/cli/utils/resolve-score-unavailable-reason.ts
|
|
91859
|
+
const resolveScoreUnavailableReason = (input) => {
|
|
91860
|
+
if (input.isScoreDisabled) return "disabled";
|
|
91861
|
+
return input.isAnalysisIncomplete ? "analysis-incomplete" : "api-unavailable";
|
|
91862
|
+
};
|
|
91863
|
+
//#endregion
|
|
91469
91864
|
//#region src/cli/utils/scan-result-cache.ts
|
|
91865
|
+
const createScanResultCacheInvocationState = () => ({ repositoryIdentityByRoot: /* @__PURE__ */ new Map() });
|
|
91470
91866
|
const CACHE_DISABLED_VALUES = new Set(["1", "true"]);
|
|
91471
91867
|
const TOOLCHAIN_PACKAGE_SPECIFIERS = [
|
|
91472
91868
|
"oxlint/package.json",
|
|
@@ -91551,7 +91947,7 @@ const buildDirtyPathContentFingerprint = (absolutePath, statusCode) => {
|
|
|
91551
91947
|
if (stats.size > 10485760) return null;
|
|
91552
91948
|
return hashFileContents(absolutePath);
|
|
91553
91949
|
};
|
|
91554
|
-
const buildWorktreeFingerprint = (projectDirectory) => {
|
|
91950
|
+
const buildWorktreeFingerprint = (projectDirectory, repositoryRoot) => {
|
|
91555
91951
|
const statusOutput = runGit(projectDirectory, [
|
|
91556
91952
|
"status",
|
|
91557
91953
|
"--porcelain=v1",
|
|
@@ -91563,8 +91959,6 @@ const buildWorktreeFingerprint = (projectDirectory) => {
|
|
|
91563
91959
|
const statusEntries = parseWorktreeStatusRecords(statusOutput);
|
|
91564
91960
|
if (statusEntries === null) return null;
|
|
91565
91961
|
if (statusEntries.length > 300) return null;
|
|
91566
|
-
const repositoryRoot = runGit(projectDirectory, ["rev-parse", "--show-toplevel"]);
|
|
91567
|
-
if (repositoryRoot === null) return null;
|
|
91568
91962
|
const dirtyEntries = [];
|
|
91569
91963
|
for (const statusEntry of statusEntries) {
|
|
91570
91964
|
const contentFingerprint = buildDirtyPathContentFingerprint(path.join(repositoryRoot, statusEntry.path), statusEntry.statusCode);
|
|
@@ -91580,6 +91974,20 @@ const buildWorktreeFingerprint = (projectDirectory) => {
|
|
|
91580
91974
|
return firstSortKey < secondSortKey ? -1 : firstSortKey > secondSortKey ? 1 : 0;
|
|
91581
91975
|
});
|
|
91582
91976
|
};
|
|
91977
|
+
const resolveRepositoryCacheIdentity = (projectDirectory, invocationState) => {
|
|
91978
|
+
const repositoryRoot = runGit(projectDirectory, ["rev-parse", "--show-toplevel"]);
|
|
91979
|
+
if (repositoryRoot === null) return null;
|
|
91980
|
+
const cachedIdentity = invocationState.repositoryIdentityByRoot.get(repositoryRoot);
|
|
91981
|
+
if (cachedIdentity !== void 0) return cachedIdentity;
|
|
91982
|
+
const worktreeFingerprint = buildWorktreeFingerprint(projectDirectory, repositoryRoot);
|
|
91983
|
+
const headSha = readHeadSha(repositoryRoot);
|
|
91984
|
+
const identity = worktreeFingerprint === null || headSha === null ? null : {
|
|
91985
|
+
headSha,
|
|
91986
|
+
worktreeFingerprint
|
|
91987
|
+
};
|
|
91988
|
+
invocationState.repositoryIdentityByRoot.set(repositoryRoot, identity);
|
|
91989
|
+
return identity;
|
|
91990
|
+
};
|
|
91583
91991
|
const DOTENV_FILE_NAME_PATTERN = /^\.env(\.|$)/;
|
|
91584
91992
|
const resolveDotenvFingerprint = (projectDirectory) => {
|
|
91585
91993
|
try {
|
|
@@ -91674,16 +92082,14 @@ const resolveToolchainFingerprint = (nodeBinaryPath) => {
|
|
|
91674
92082
|
const buildScanResultCacheKey = (input) => {
|
|
91675
92083
|
if (isCacheGloballyDisabled()) return null;
|
|
91676
92084
|
if (!isGitIdentityTrustworthy(input.projectDirectory)) return null;
|
|
91677
|
-
const
|
|
91678
|
-
if (
|
|
91679
|
-
const headSha = readHeadSha(input.projectDirectory);
|
|
91680
|
-
if (headSha === null) return null;
|
|
92085
|
+
const repositoryIdentity = resolveRepositoryCacheIdentity(input.projectDirectory, input.invocationState);
|
|
92086
|
+
if (repositoryIdentity === null) return null;
|
|
91681
92087
|
if (stringifyStableJson(input.userConfig) === null) return null;
|
|
91682
92088
|
const cacheKeyJson = stringifyStableJson({
|
|
91683
92089
|
schemaVersion: 6,
|
|
91684
92090
|
projectIdentity: resolveProjectIdentity(input.projectDirectory),
|
|
91685
|
-
headSha,
|
|
91686
|
-
worktreeFingerprint,
|
|
92091
|
+
headSha: repositoryIdentity.headSha,
|
|
92092
|
+
worktreeFingerprint: repositoryIdentity.worktreeFingerprint,
|
|
91687
92093
|
dotenvFingerprint: resolveDotenvFingerprint(input.projectDirectory),
|
|
91688
92094
|
reactDoctorVersion: input.version,
|
|
91689
92095
|
nodeVersion: process.version,
|
|
@@ -91697,6 +92103,8 @@ const buildScanResultCacheKey = (input) => {
|
|
|
91697
92103
|
deadCode: input.options.deadCode,
|
|
91698
92104
|
supplyChain: input.options.supplyChain,
|
|
91699
92105
|
includePaths: [...input.options.includePaths].sort(),
|
|
92106
|
+
excludedProjectDirectories: [...input.options.excludedProjectDirectories ?? []].sort(),
|
|
92107
|
+
retainExcludedProjectDeadCodeDiagnostics: input.options.retainExcludedProjectDeadCodeDiagnostics ?? false,
|
|
91700
92108
|
customRulesOnly: input.options.customRulesOnly,
|
|
91701
92109
|
respectInlineDisables: input.options.respectInlineDisables,
|
|
91702
92110
|
warnings: input.options.warnings,
|
|
@@ -91963,7 +92371,11 @@ const buildOutcomeAttributes = (input) => {
|
|
|
91963
92371
|
...withNamespace("score", {
|
|
91964
92372
|
value: result.score ? result.score.score : null,
|
|
91965
92373
|
label: result.score ? result.score.label : null,
|
|
91966
|
-
available: result.score !== null
|
|
92374
|
+
available: result.score !== null,
|
|
92375
|
+
unavailableReason: result.score === null ? resolveScoreUnavailableReason({
|
|
92376
|
+
isScoreDisabled: input.noScore,
|
|
92377
|
+
isAnalysisIncomplete: input.didLintFail === true || input.didDeadCodeFail === true
|
|
92378
|
+
}) : null
|
|
91967
92379
|
}),
|
|
91968
92380
|
...withNamespace("lint", {
|
|
91969
92381
|
failed: input.didLintFail ?? null,
|
|
@@ -92531,8 +92943,35 @@ const createDiagnosticEvidenceReader = (rootDirectory, options = {}) => {
|
|
|
92531
92943
|
//#endregion
|
|
92532
92944
|
//#region src/cli/utils/build-no-score-message.ts
|
|
92533
92945
|
const ENTERPRISE_CONTACT_HINT = `Want something custom to your company? Contact us at ${ENTERPRISE_CONTACT_URL}.`;
|
|
92534
|
-
const buildNoScoreMessage = (
|
|
92535
|
-
|
|
92946
|
+
const buildNoScoreMessage = (input) => {
|
|
92947
|
+
const unavailableReason = resolveScoreUnavailableReason({
|
|
92948
|
+
isScoreDisabled: input.isScoreDisabled,
|
|
92949
|
+
isAnalysisIncomplete: input.isAnalysisIncomplete ?? false
|
|
92950
|
+
});
|
|
92951
|
+
let reason;
|
|
92952
|
+
switch (unavailableReason) {
|
|
92953
|
+
case "disabled":
|
|
92954
|
+
reason = input.disabledMessage ?? "Score disabled by --no-score.";
|
|
92955
|
+
break;
|
|
92956
|
+
case "analysis-incomplete":
|
|
92957
|
+
reason = "Score not shown because lint or dead-code analysis could not complete.";
|
|
92958
|
+
break;
|
|
92959
|
+
case "api-unavailable":
|
|
92960
|
+
reason = "Score unavailable (could not reach the score API).";
|
|
92961
|
+
break;
|
|
92962
|
+
}
|
|
92963
|
+
return `${reason} ${ENTERPRISE_CONTACT_HINT}`;
|
|
92964
|
+
};
|
|
92965
|
+
//#endregion
|
|
92966
|
+
//#region src/cli/utils/has-incomplete-score-analysis.ts
|
|
92967
|
+
const hasIncompleteScoreAnalysis = (skippedChecks) => skippedChecks.includes("lint") || skippedChecks.includes("dead-code");
|
|
92968
|
+
//#endregion
|
|
92969
|
+
//#region src/cli/utils/build-empty-report-message.ts
|
|
92970
|
+
const buildEmptyReportMessage = (input) => {
|
|
92971
|
+
const categoryFilters = [...input.categoryFilters];
|
|
92972
|
+
if (categoryFilters.length > 0) return `No issues found in category ${categoryFilters.join(", ")}!`;
|
|
92973
|
+
if (input.demotedDiagnosticCount > 0) return `No issues found! (${input.demotedDiagnosticCount} demoted from the ${input.outputSurface} surface — see config.surfaces.)`;
|
|
92974
|
+
return "No issues found!";
|
|
92536
92975
|
};
|
|
92537
92976
|
//#endregion
|
|
92538
92977
|
//#region src/cli/utils/render-agent-guidance.ts
|
|
@@ -93279,9 +93718,6 @@ const markOnboardingComplete = (options = {}) => {
|
|
|
93279
93718
|
if (isGatePending(ONBOARDING_GATE, {}, options)) recordGate(ONBOARDING_GATE, {}, options);
|
|
93280
93719
|
};
|
|
93281
93720
|
//#endregion
|
|
93282
|
-
//#region src/cli/utils/render-project-detection.ts
|
|
93283
|
-
const printProjectDetection = (_input) => void_;
|
|
93284
|
-
//#endregion
|
|
93285
93721
|
//#region src/cli/utils/colorize-by-score.ts
|
|
93286
93722
|
const colorizeByScore = (text, score) => {
|
|
93287
93723
|
if (score >= 75) return highlighter.success(text);
|
|
@@ -93495,6 +93931,26 @@ const printNoScoreHeader = (noScoreMessage) => gen(function* () {
|
|
|
93495
93931
|
yield* log("");
|
|
93496
93932
|
});
|
|
93497
93933
|
//#endregion
|
|
93934
|
+
//#region src/cli/utils/build-footer-description-lines.ts
|
|
93935
|
+
const FOOTER_DESCRIPTION_INDENT = " ";
|
|
93936
|
+
const buildFooterDescriptionLines = (description) => {
|
|
93937
|
+
return wrapTextToWidth(description, resolveMeasureWidth(2)).map((line) => highlighter.dim(`${FOOTER_DESCRIPTION_INDENT}${line}`));
|
|
93938
|
+
};
|
|
93939
|
+
//#endregion
|
|
93940
|
+
//#region src/cli/utils/build-footer-link-lines.ts
|
|
93941
|
+
const buildFooterLinkLines = ({ shareUrl }) => [
|
|
93942
|
+
...shareUrl === null ? [] : [
|
|
93943
|
+
` ${highlighter.bold("Share:")} ${highlighter.info(shareUrl)}`,
|
|
93944
|
+
...buildFooterDescriptionLines("Tell others how you did on socials"),
|
|
93945
|
+
""
|
|
93946
|
+
],
|
|
93947
|
+
` ${highlighter.bold("Docs:")} ${highlighter.info(DOCS_URL)}`,
|
|
93948
|
+
...buildFooterDescriptionLines("Learn more about fixing issues, setting up CI/CD, and configuring rules with a config file"),
|
|
93949
|
+
"",
|
|
93950
|
+
` ${highlighter.bold("GitHub:")} ${highlighter.info(CANONICAL_GITHUB_URL)}`,
|
|
93951
|
+
...buildFooterDescriptionLines("Report issues and star the repository!")
|
|
93952
|
+
];
|
|
93953
|
+
//#endregion
|
|
93498
93954
|
//#region src/cli/utils/write-diagnostics-directory.ts
|
|
93499
93955
|
const ruleDumpFileName = (ruleKey) => ruleKey.replace(/\//g, "--") + ".txt";
|
|
93500
93956
|
const readPreviousRuleDumpFileNames = (directory) => {
|
|
@@ -93515,11 +93971,6 @@ const writeDiagnosticsDirectory = (diagnostics, outputDirectory) => {
|
|
|
93515
93971
|
};
|
|
93516
93972
|
//#endregion
|
|
93517
93973
|
//#region src/cli/utils/render-summary.ts
|
|
93518
|
-
const FOOTER_DESCRIPTION_INDENT = " ";
|
|
93519
|
-
const printFooterDescription = (description) => gen(function* () {
|
|
93520
|
-
const wrapWidth = resolveMeasureWidth(2);
|
|
93521
|
-
for (const line of wrapTextToWidth(description, wrapWidth)) yield* log(highlighter.dim(`${FOOTER_DESCRIPTION_INDENT}${line}`));
|
|
93522
|
-
});
|
|
93523
93974
|
const buildShareUrl = (diagnostics, scoreResult, projectName) => {
|
|
93524
93975
|
const errorCount = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
|
|
93525
93976
|
const warningCount = diagnostics.filter((diagnostic) => diagnostic.severity === "warning").length;
|
|
@@ -93536,17 +93987,8 @@ const printFooter = (input) => gen(function* () {
|
|
|
93536
93987
|
yield* log("");
|
|
93537
93988
|
yield* log(buildSectionDivider());
|
|
93538
93989
|
yield* log("");
|
|
93539
|
-
|
|
93540
|
-
|
|
93541
|
-
yield* log(` ${highlighter.bold("Share:")} ${highlighter.info(shareUrl)}`);
|
|
93542
|
-
yield* printFooterDescription("Tell others how you did on socials");
|
|
93543
|
-
yield* log("");
|
|
93544
|
-
}
|
|
93545
|
-
yield* log(` ${highlighter.bold("Docs:")} ${highlighter.info(DOCS_URL)}`);
|
|
93546
|
-
yield* printFooterDescription("Learn more about fixing issues, setting up CI/CD, and configuring rules with a config file");
|
|
93547
|
-
yield* log("");
|
|
93548
|
-
yield* log(` ${highlighter.bold("GitHub:")} ${highlighter.info(CANONICAL_GITHUB_URL)}`);
|
|
93549
|
-
yield* printFooterDescription("Report issues and star the repository!");
|
|
93990
|
+
const shareUrl = input.isOffline ? null : buildShareUrl(input.diagnostics, input.scoreResult, input.projectName);
|
|
93991
|
+
for (const line of buildFooterLinkLines({ shareUrl })) yield* log(line);
|
|
93550
93992
|
});
|
|
93551
93993
|
const printDiagnosticsDump = (diagnostics, outputDirectory, verbose, stream = "stdout") => gen(function* () {
|
|
93552
93994
|
const writtenDirectory = yield* try_({
|
|
@@ -93643,9 +94085,6 @@ const resolveCliCategories = (categoryFlag) => {
|
|
|
93643
94085
|
//#endregion
|
|
93644
94086
|
//#region src/inspect.ts
|
|
93645
94087
|
const silentConsole = makeNoopConsole();
|
|
93646
|
-
const runConsole = (effect) => {
|
|
93647
|
-
runSync(effect);
|
|
93648
|
-
};
|
|
93649
94088
|
const recordOnboardingCompletion = (options) => {
|
|
93650
94089
|
const forceOnboarding = isOnboardingForced();
|
|
93651
94090
|
if (shouldRecordOnboarding({
|
|
@@ -93655,7 +94094,6 @@ const recordOnboardingCompletion = (options) => {
|
|
|
93655
94094
|
isNonInteractiveEnvironment: options.isNonInteractiveEnvironment
|
|
93656
94095
|
})) markOnboardingComplete();
|
|
93657
94096
|
};
|
|
93658
|
-
const formatCategorySelection = (categoryFilters) => [...categoryFilters].join(", ");
|
|
93659
94097
|
const buildChangedLineMatcher = (directory, changedLineRanges) => {
|
|
93660
94098
|
const rangesByFile = /* @__PURE__ */ new Map();
|
|
93661
94099
|
for (const entry of changedLineRanges) rangesByFile.set(toForwardSlashes(entry.file), entry.ranges);
|
|
@@ -93705,7 +94143,10 @@ const mergeInspectOptions = (inputOptions, userConfig) => {
|
|
|
93705
94143
|
maxDurationMs: inputOptions.maxDurationMs ?? null,
|
|
93706
94144
|
baseline: inputOptions.baseline ?? null,
|
|
93707
94145
|
changedLineRanges: inputOptions.changedLineRanges ?? null,
|
|
93708
|
-
supplyChainManifestChanged: inputOptions.supplyChainManifestChanged ?? false
|
|
94146
|
+
supplyChainManifestChanged: inputOptions.supplyChainManifestChanged ?? false,
|
|
94147
|
+
excludedProjectDirectories: inputOptions.excludedProjectDirectories ?? [],
|
|
94148
|
+
retainExcludedProjectDeadCodeDiagnostics: inputOptions.retainExcludedProjectDeadCodeDiagnostics ?? false,
|
|
94149
|
+
precomputedSourceFileCount: inputOptions.precomputedSourceFileCount
|
|
93709
94150
|
};
|
|
93710
94151
|
};
|
|
93711
94152
|
const deriveScope = (options) => {
|
|
@@ -93777,11 +94218,22 @@ const inspectWithOxlintRuntime = async (directory, inputOptions, oxlintRuntime)
|
|
|
93777
94218
|
};
|
|
93778
94219
|
const createInvocationInspect = (requestedOxlintConcurrency) => {
|
|
93779
94220
|
const concurrency = resolveScanConcurrency(requestedOxlintConcurrency ?? runSync(OxlintConcurrency));
|
|
93780
|
-
const
|
|
93781
|
-
|
|
93782
|
-
|
|
94221
|
+
const spawnSlots = createOxlintSpawnSlots(concurrency);
|
|
94222
|
+
const scanResultCacheInvocationState = createScanResultCacheInvocationState();
|
|
94223
|
+
return async (directory, inputOptions = {}) => {
|
|
94224
|
+
const abortController = new AbortController();
|
|
94225
|
+
const unregisterAbortController = activeScanAbortRegistry.register(abortController);
|
|
94226
|
+
try {
|
|
94227
|
+
return await inspectWithOxlintRuntime(directory, inputOptions, {
|
|
94228
|
+
concurrency,
|
|
94229
|
+
spawnSlots,
|
|
94230
|
+
abortSignal: abortController.signal,
|
|
94231
|
+
scanResultCacheInvocationState
|
|
94232
|
+
});
|
|
94233
|
+
} finally {
|
|
94234
|
+
unregisterAbortController();
|
|
94235
|
+
}
|
|
93783
94236
|
};
|
|
93784
|
-
return (directory, inputOptions = {}) => inspectWithOxlintRuntime(directory, inputOptions, oxlintRuntime);
|
|
93785
94237
|
};
|
|
93786
94238
|
const inspect = async (directory, inputOptions = {}) => createInvocationInspect(inputOptions.concurrency)(directory, inputOptions);
|
|
93787
94239
|
const countIncompleteLintFiles = (lintPartialFailures) => countDroppedLintFiles(lintPartialFailures) + countDeadlineSkippedFiles(lintPartialFailures);
|
|
@@ -93794,12 +94246,27 @@ const countIncompleteLintFiles = (lintPartialFailures) => countDroppedLintFiles(
|
|
|
93794
94246
|
*/
|
|
93795
94247
|
const runBaselineComparison = async (params) => {
|
|
93796
94248
|
const tempDirectory = mkdtempSync(path.join(tmpdir(), BASELINE_FILES_TEMP_DIR_PREFIX));
|
|
94249
|
+
const baselineIncludePaths = filterPathsOutsideDirectories({
|
|
94250
|
+
rootDirectory: params.directory,
|
|
94251
|
+
relativePaths: params.options.includePaths,
|
|
94252
|
+
excludedDirectories: params.options.excludedProjectDirectories
|
|
94253
|
+
});
|
|
94254
|
+
const baselineBaseFiles = params.baseFiles ? filterPathsOutsideDirectories({
|
|
94255
|
+
rootDirectory: params.directory,
|
|
94256
|
+
relativePaths: params.baseFiles,
|
|
94257
|
+
excludedDirectories: params.options.excludedProjectDirectories
|
|
94258
|
+
}) : void 0;
|
|
94259
|
+
const baselineHeadFiles = params.headFiles ? filterPathsOutsideDirectories({
|
|
94260
|
+
rootDirectory: params.directory,
|
|
94261
|
+
relativePaths: params.headFiles,
|
|
94262
|
+
excludedDirectories: params.options.excludedProjectDirectories
|
|
94263
|
+
}) : void 0;
|
|
93797
94264
|
const snapshot = await materializeBaselineFiles({
|
|
93798
94265
|
directory: params.directory,
|
|
93799
94266
|
ref: params.baselineRef,
|
|
93800
|
-
files:
|
|
93801
|
-
baseFiles:
|
|
93802
|
-
headFiles:
|
|
94267
|
+
files: baselineIncludePaths,
|
|
94268
|
+
baseFiles: baselineBaseFiles,
|
|
94269
|
+
headFiles: baselineHeadFiles,
|
|
93803
94270
|
tempDirectory
|
|
93804
94271
|
}).catch((error) => {
|
|
93805
94272
|
rmSync(tempDirectory, {
|
|
@@ -93821,7 +94288,7 @@ const runBaselineComparison = async (params) => {
|
|
|
93821
94288
|
const baseFiles = new Set(snapshot.baseFiles.map(toForwardSlashes));
|
|
93822
94289
|
const trackedHeadFiles = new Set(snapshot.headFiles.map(toForwardSlashes));
|
|
93823
94290
|
const expectedHeadFiles = new Set(trackedHeadFiles);
|
|
93824
|
-
for (const filePath of
|
|
94291
|
+
for (const filePath of baselineIncludePaths) {
|
|
93825
94292
|
const normalizedFilePath = toForwardSlashes(filePath);
|
|
93826
94293
|
if (!baseFiles.has(normalizedFilePath)) expectedHeadFiles.add(normalizedFilePath);
|
|
93827
94294
|
}
|
|
@@ -93858,8 +94325,9 @@ const runBaselineComparison = async (params) => {
|
|
|
93858
94325
|
resolveLocalGithubViewerPermission: false,
|
|
93859
94326
|
suppressScanSummary: true,
|
|
93860
94327
|
supplyChainManifestChanged: params.options.supplyChainManifestChanged,
|
|
93861
|
-
deadlineEpochMs: params.deadlineEpochMs ?? void 0
|
|
93862
|
-
|
|
94328
|
+
deadlineEpochMs: params.deadlineEpochMs ?? void 0,
|
|
94329
|
+
signal: params.oxlintRuntime.abortSignal
|
|
94330
|
+
}, {}).pipe(provide(baseLayers), provideService$2(PerFileLintCacheEnabled, false), provideService$2(SidecarLintCacheEnabled, false), provideService$2(Console, silentConsole))), { signal: params.oxlintRuntime.abortSignal });
|
|
93863
94331
|
if (baseOutput.didLintFail || countIncompleteLintFiles(baseOutput.lintPartialFailures) > 0) return null;
|
|
93864
94332
|
const hasUnscannedUntrackedSourceFiles = filterSourceFiles(snapshot.untrackedFiles.map(toForwardSlashes)).some((filePath) => !analyzedHeadFiles.has(filePath));
|
|
93865
94333
|
const delta = computeDiagnosticDelta({
|
|
@@ -93887,6 +94355,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93887
94355
|
const isDiffMode = options.includePaths.length > 0;
|
|
93888
94356
|
const resolvedNodeBinaryPath = await resolveOxlintNode(options.lint, options.scoreOnly || options.silent);
|
|
93889
94357
|
const lintBindingMissing = options.lint && !resolvedNodeBinaryPath;
|
|
94358
|
+
await yieldToEventLoop();
|
|
93890
94359
|
const cacheKey = buildScanResultCacheKey({
|
|
93891
94360
|
projectDirectory: directory,
|
|
93892
94361
|
version: VERSION,
|
|
@@ -93894,19 +94363,14 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93894
94363
|
options,
|
|
93895
94364
|
userConfig,
|
|
93896
94365
|
hasConfigOverride,
|
|
93897
|
-
configSourceDirectory
|
|
94366
|
+
configSourceDirectory,
|
|
94367
|
+
invocationState: oxlintRuntime.scanResultCacheInvocationState
|
|
93898
94368
|
});
|
|
93899
94369
|
const scanResultCache = cacheKey === null ? null : createScanResultCache(directory);
|
|
93900
94370
|
const cachedPayload = cacheKey === null ? null : scanResultCache?.lookup(cacheKey) ?? null;
|
|
93901
94371
|
if (cachedPayload) {
|
|
93902
94372
|
recordSentryProjectContext(cachedPayload.project, rootSentrySpan, { concurrentScan: options.concurrentScan });
|
|
93903
94373
|
recordCount(METRIC.projectDetected, 1);
|
|
93904
|
-
await renderCachedProjectDetection({
|
|
93905
|
-
payload: cachedPayload,
|
|
93906
|
-
options,
|
|
93907
|
-
userConfig,
|
|
93908
|
-
isDiffMode
|
|
93909
|
-
});
|
|
93910
94374
|
const baselineDegraded = Boolean(options.baseline) && isDiffMode && cachedPayload.baselineDelta === void 0;
|
|
93911
94375
|
const result = await renderAndRecordScan({
|
|
93912
94376
|
payload: cachedPayload,
|
|
@@ -93940,6 +94404,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93940
94404
|
});
|
|
93941
94405
|
const program = runInspect({
|
|
93942
94406
|
directory,
|
|
94407
|
+
precomputedSourceFileCount: options.precomputedSourceFileCount,
|
|
93943
94408
|
includePaths: options.includePaths,
|
|
93944
94409
|
customRulesOnly: options.customRulesOnly,
|
|
93945
94410
|
respectInlineDisables: options.respectInlineDisables,
|
|
@@ -93956,26 +94421,19 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93956
94421
|
resolveLocalGithubViewerPermission: !options.noScore,
|
|
93957
94422
|
suppressScanSummary: options.suppressRendering,
|
|
93958
94423
|
supplyChainManifestChanged: options.supplyChainManifestChanged,
|
|
93959
|
-
|
|
93960
|
-
|
|
93961
|
-
|
|
94424
|
+
deadlineEpochMs: deadlineEpochMs ?? void 0,
|
|
94425
|
+
signal: oxlintRuntime.abortSignal,
|
|
94426
|
+
excludedProjectDirectories: options.excludedProjectDirectories,
|
|
94427
|
+
retainExcludedProjectDeadCodeDiagnostics: options.retainExcludedProjectDeadCodeDiagnostics
|
|
94428
|
+
}, { beforeLint: (projectInfo) => sync(() => {
|
|
93962
94429
|
recordSentryProjectContext(projectInfo, rootSentrySpan, { concurrentScan: options.concurrentScan });
|
|
93963
94430
|
recordCount(METRIC.projectDetected, 1);
|
|
93964
|
-
if (options.scoreOnly || options.suppressRendering) return;
|
|
93965
|
-
const lintSourceFileCount = lintIncludePaths?.length ?? projectInfo.sourceFileCount;
|
|
93966
|
-
yield* printProjectDetection({
|
|
93967
|
-
projectInfo,
|
|
93968
|
-
userConfig,
|
|
93969
|
-
isDiffMode,
|
|
93970
|
-
includePaths: options.includePaths,
|
|
93971
|
-
lintSourceFileCount
|
|
93972
|
-
});
|
|
93973
94431
|
}) });
|
|
93974
|
-
const output = await runPromise(restoreLegacyThrow(applyObservability(options.silent ? program.pipe(provide(layers), provideService$2(Console, silentConsole)) : program.pipe(provide(layers)), rootSentrySpan)));
|
|
94432
|
+
const output = await runPromise(restoreLegacyThrow(applyObservability(options.silent ? program.pipe(provide(layers), provideService$2(Console, silentConsole)) : program.pipe(provide(layers)), rootSentrySpan)), { signal: oxlintRuntime.abortSignal });
|
|
93975
94433
|
const didLintFail = lintBindingMissing || output.didLintFail;
|
|
93976
94434
|
const lintFailureReason = lintBindingMissing ? `oxlint native binding not found for Node ${process.version}; expected one matching ${OXLINT_NODE_REQUIREMENT}` : output.lintFailureReason;
|
|
93977
|
-
if (!options.scoreOnly && !options.uiLayers && !lintBindingMissing && output.didLintFail && lintFailureReason !== null) if (output.lintFailureReasonKind === "native-binding-missing")
|
|
93978
|
-
else
|
|
94435
|
+
if (!options.scoreOnly && !options.uiLayers && !lintBindingMissing && output.didLintFail && lintFailureReason !== null) if (output.lintFailureReasonKind === "native-binding-missing") runSync(log(highlighter.gray(` Upgrade to Node ${OXLINT_NODE_REQUIREMENT} or run: npx -p oxlint@latest react-doctor@latest`)));
|
|
94436
|
+
else runSync(error$1(highlighter.error(lintFailureReason)));
|
|
93979
94437
|
let inspectDiagnostics = output.diagnostics;
|
|
93980
94438
|
let baselineDelta;
|
|
93981
94439
|
if (options.baseline && isDiffMode && !didLintFail && countIncompleteLintFiles(output.lintPartialFailures) === 0) {
|
|
@@ -94025,6 +94483,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
94025
94483
|
lintFailureReasonKind: lintBindingMissing ? "native-binding-missing" : output.lintFailureReasonKind,
|
|
94026
94484
|
supplyChainOverlapTimedOut: output.supplyChainOverlapTimedOut,
|
|
94027
94485
|
securityScanFailed: output.securityScanFailed,
|
|
94486
|
+
securityScanFailureReason: output.securityScanFailureReason,
|
|
94028
94487
|
suppressedRuleCounts: output.suppressedRuleCounts
|
|
94029
94488
|
};
|
|
94030
94489
|
if (cacheKey !== null && scanResultCache !== null && shouldStoreScanPayload(payload) && !baselineDegraded) scanResultCache.store(cacheKey, payload);
|
|
@@ -94050,16 +94509,6 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
94050
94509
|
return result;
|
|
94051
94510
|
};
|
|
94052
94511
|
const runMaybeSilent = (effect, silent) => silent ? effect.pipe(provideService$2(Console, silentConsole)) : effect;
|
|
94053
|
-
const renderCachedProjectDetection = async (input) => {
|
|
94054
|
-
if (input.options.scoreOnly || input.options.suppressRendering) return;
|
|
94055
|
-
await runPromise(runMaybeSilent(printProjectDetection({
|
|
94056
|
-
projectInfo: input.payload.project,
|
|
94057
|
-
userConfig: input.userConfig,
|
|
94058
|
-
isDiffMode: input.isDiffMode,
|
|
94059
|
-
includePaths: input.options.includePaths,
|
|
94060
|
-
lintSourceFileCount: input.payload.scannedFileCount
|
|
94061
|
-
}), input.options.silent));
|
|
94062
|
-
};
|
|
94063
94512
|
const renderAndRecordScan = async (input) => {
|
|
94064
94513
|
const result = await runPromise(runMaybeSilent(finalizeAndRender({
|
|
94065
94514
|
options: input.options,
|
|
@@ -94075,6 +94524,7 @@ const renderAndRecordScan = async (input) => {
|
|
|
94075
94524
|
deadCodeFailureReason: input.payload.deadCodeFailureReason,
|
|
94076
94525
|
supplyChainOverlapTimedOut: input.payload.supplyChainOverlapTimedOut,
|
|
94077
94526
|
securityScanFailed: input.payload.securityScanFailed ?? false,
|
|
94527
|
+
securityScanFailureReason: input.payload.securityScanFailureReason ?? null,
|
|
94078
94528
|
directory: input.payload.directory,
|
|
94079
94529
|
scannedFileCount: input.payload.scannedFileCount,
|
|
94080
94530
|
scannedFilePaths: input.payload.scannedFilePaths,
|
|
@@ -94126,7 +94576,7 @@ const renderAndRecordScan = async (input) => {
|
|
|
94126
94576
|
return result;
|
|
94127
94577
|
};
|
|
94128
94578
|
const finalizeAndRender = (input) => gen(function* () {
|
|
94129
|
-
const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, supplyChainOverlapTimedOut, securityScanFailed, directory, scannedFileCount, scannedFilePaths, analyzedFiles, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount, lintSidecarReplayedFileCount, lintSidecarTotalFileCount, deadCodeCacheHit, deadCodeSummaryCacheHits, deadCodeSummaryCacheMisses, baselineDelta } = input;
|
|
94579
|
+
const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, supplyChainOverlapTimedOut, securityScanFailed, securityScanFailureReason, directory, scannedFileCount, scannedFilePaths, analyzedFiles, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount, lintSidecarReplayedFileCount, lintSidecarTotalFileCount, deadCodeCacheHit, deadCodeSummaryCacheHits, deadCodeSummaryCacheMisses, baselineDelta } = input;
|
|
94130
94580
|
const { skippedChecks, skippedCheckReasons } = buildSkippedChecks({
|
|
94131
94581
|
didLintFail,
|
|
94132
94582
|
lintFailureReason,
|
|
@@ -94134,10 +94584,15 @@ const finalizeAndRender = (input) => gen(function* () {
|
|
|
94134
94584
|
didDeadCodeFail,
|
|
94135
94585
|
deadCodeFailureReason,
|
|
94136
94586
|
supplyChainOverlapTimedOut,
|
|
94137
|
-
securityScanFailed
|
|
94587
|
+
securityScanFailed,
|
|
94588
|
+
securityScanFailureReason
|
|
94138
94589
|
});
|
|
94139
94590
|
const hasSkippedChecks = skippedChecks.length > 0;
|
|
94140
|
-
const noScoreMessage = buildNoScoreMessage(
|
|
94591
|
+
const noScoreMessage = buildNoScoreMessage({
|
|
94592
|
+
isScoreDisabled: options.noScore,
|
|
94593
|
+
isAnalysisIncomplete: hasIncompleteScoreAnalysis(skippedChecks),
|
|
94594
|
+
disabledMessage: options.scoreDisabledMessage
|
|
94595
|
+
});
|
|
94141
94596
|
const buildResult = () => ({
|
|
94142
94597
|
diagnostics: [...diagnostics],
|
|
94143
94598
|
score,
|
|
@@ -94183,9 +94638,11 @@ const finalizeAndRender = (input) => gen(function* () {
|
|
|
94183
94638
|
if (hasSkippedChecks) {
|
|
94184
94639
|
const skippedLabel = skippedChecks.join(" and ");
|
|
94185
94640
|
yield* warn$1(highlighter.warn(`No issues detected, but ${skippedLabel} checks failed — results are incomplete.`));
|
|
94186
|
-
} else
|
|
94187
|
-
|
|
94188
|
-
|
|
94641
|
+
} else yield* log(highlighter.success(buildEmptyReportMessage({
|
|
94642
|
+
categoryFilters: options.categoryFilters,
|
|
94643
|
+
demotedDiagnosticCount,
|
|
94644
|
+
outputSurface: options.outputSurface
|
|
94645
|
+
})));
|
|
94189
94646
|
yield* log("");
|
|
94190
94647
|
yield* pause;
|
|
94191
94648
|
if (hasSkippedChecks) {
|
|
@@ -94265,6 +94722,32 @@ const materializeStagedFiles = async (input) => {
|
|
|
94265
94722
|
};
|
|
94266
94723
|
};
|
|
94267
94724
|
//#endregion
|
|
94725
|
+
//#region src/cli/utils/deduplicate-project-scans.ts
|
|
94726
|
+
const deduplicateProjectScans = (projectScans) => {
|
|
94727
|
+
const uniqueProjectScans = [];
|
|
94728
|
+
const seenDirectories = /* @__PURE__ */ new Set();
|
|
94729
|
+
for (const projectScan of projectScans) {
|
|
94730
|
+
if (seenDirectories.has(projectScan.directory)) continue;
|
|
94731
|
+
seenDirectories.add(projectScan.directory);
|
|
94732
|
+
uniqueProjectScans.push(projectScan);
|
|
94733
|
+
}
|
|
94734
|
+
return uniqueProjectScans;
|
|
94735
|
+
};
|
|
94736
|
+
//#endregion
|
|
94737
|
+
//#region src/cli/utils/collect-project-source-file-counts.ts
|
|
94738
|
+
const collectProjectSourceFileCounts = async (rootDirectory, projectDirectories) => {
|
|
94739
|
+
const abortController = new AbortController();
|
|
94740
|
+
const unregisterAbortController = activeScanAbortRegistry.register(abortController);
|
|
94741
|
+
try {
|
|
94742
|
+
return await collectSourceFileCountsByDirectory(rootDirectory, projectDirectories, abortController.signal);
|
|
94743
|
+
} finally {
|
|
94744
|
+
unregisterAbortController();
|
|
94745
|
+
}
|
|
94746
|
+
};
|
|
94747
|
+
//#endregion
|
|
94748
|
+
//#region src/cli/utils/format-skipped-projects-message.ts
|
|
94749
|
+
const formatSkippedProjectsMessage = (projectCount) => `${projectCount} ${projectCount === 1 ? "project was" : "projects were"} skipped because the max scan duration was reached.`;
|
|
94750
|
+
//#endregion
|
|
94268
94751
|
//#region src/cli/utils/is-share-opted-out.ts
|
|
94269
94752
|
const isShareOptedOut = (scans, flagNoScore) => Boolean(flagNoScore) || scans.some((scan) => Boolean(scan.config?.noScore) || scan.config?.share === false);
|
|
94270
94753
|
//#endregion
|
|
@@ -94276,7 +94759,7 @@ const buildHandoffPayload = (input) => {
|
|
|
94276
94759
|
try {
|
|
94277
94760
|
outputDirectory = writeDiagnosticsDirectory([...input.diagnostics], input.outputDirectory);
|
|
94278
94761
|
} catch {}
|
|
94279
|
-
const lines = [`
|
|
94762
|
+
const lines = [`Review and fix the top ${topGroups.length} React Doctor ${topGroups.length === 1 ? "finding" : "findings"} in ${input.projectName}. Leave the rest for a follow-up.`, ""];
|
|
94280
94763
|
topGroups.forEach(([ruleKey, ruleDiagnostics], index) => {
|
|
94281
94764
|
const representative = ruleDiagnostics[0];
|
|
94282
94765
|
const severityLabel = representative.severity === "error" ? "ERROR" : "WARN";
|
|
@@ -94304,7 +94787,7 @@ const buildHandoffPayload = (input) => {
|
|
|
94304
94787
|
const ruleSummaries = deferredMigrationBuckets.map((bucket) => `${bucket.title} (${bucket.fileCount} files)`).join(", ");
|
|
94305
94788
|
lines.push(`Some of the rest are migration-scale (span dozens of files): ${ruleSummaries}. For each, fix a representative sample, confirm the recipe holds, and get the code owner's sign-off before changing the rest in one pass.`, "");
|
|
94306
94789
|
}
|
|
94307
|
-
lines.push("
|
|
94790
|
+
lines.push("Stop after this pass. Summarize the remaining findings for a follow-up.");
|
|
94308
94791
|
return lines.join("\n");
|
|
94309
94792
|
};
|
|
94310
94793
|
//#endregion
|
|
@@ -94514,20 +94997,20 @@ const resolveOpenCommand = (url) => {
|
|
|
94514
94997
|
args: [url]
|
|
94515
94998
|
};
|
|
94516
94999
|
};
|
|
94517
|
-
const openUrl = (url) => {
|
|
95000
|
+
const openUrl = (url) => new Promise((resolve) => {
|
|
94518
95001
|
try {
|
|
94519
95002
|
const { command, args } = resolveOpenCommand(url);
|
|
94520
95003
|
const child = spawn(command, args, {
|
|
94521
95004
|
detached: true,
|
|
94522
95005
|
stdio: "ignore"
|
|
94523
95006
|
});
|
|
94524
|
-
child.
|
|
95007
|
+
child.once("spawn", () => resolve(true));
|
|
95008
|
+
child.once("error", () => resolve(false));
|
|
94525
95009
|
child.unref();
|
|
94526
|
-
return true;
|
|
94527
95010
|
} catch {
|
|
94528
|
-
|
|
95011
|
+
resolve(false);
|
|
94529
95012
|
}
|
|
94530
|
-
};
|
|
95013
|
+
});
|
|
94531
95014
|
//#endregion
|
|
94532
95015
|
//#region src/cli/utils/ask-add-to-github-actions.ts
|
|
94533
95016
|
const CI_YES_CHOICE = "ci-yes";
|
|
@@ -94568,7 +95051,7 @@ const askAddToGitHubActions = async (prompt = prompts) => {
|
|
|
94568
95051
|
if (ciChoice === void 0) return "cancel";
|
|
94569
95052
|
if (ciChoice === CI_YES_CHOICE) return "yes";
|
|
94570
95053
|
if (ciChoice === CI_NO_CHOICE) return "no";
|
|
94571
|
-
const opened = openUrl(GITHUB_ACTIONS_SETUP_URL);
|
|
95054
|
+
const opened = await openUrl(GITHUB_ACTIONS_SETUP_URL);
|
|
94572
95055
|
cliLogger.log(opened ? `Opened ${highlighter.info(GITHUB_ACTIONS_SETUP_URL)} in your browser.` : `Visit ${highlighter.info(GITHUB_ACTIONS_SETUP_URL)} to learn more.`);
|
|
94573
95056
|
cliLogger.break();
|
|
94574
95057
|
}
|
|
@@ -96283,6 +96766,27 @@ const resolveCliInspectOptions = (flags, userConfig) => {
|
|
|
96283
96766
|
};
|
|
96284
96767
|
};
|
|
96285
96768
|
//#endregion
|
|
96769
|
+
//#region src/cli/utils/resolve-workspace-dead-code-owner.ts
|
|
96770
|
+
const resolveWorkspaceDeadCodeOwner = (input) => {
|
|
96771
|
+
if (!input.isRootDeadCodeEnabled) return null;
|
|
96772
|
+
const resolvedRootDirectory = Path.resolve(input.rootDirectory);
|
|
96773
|
+
return input.projectDirectories.find((projectDirectory) => Path.resolve(projectDirectory) === resolvedRootDirectory) ?? null;
|
|
96774
|
+
};
|
|
96775
|
+
//#endregion
|
|
96776
|
+
//#region src/cli/utils/retry-missing-project-scores.ts
|
|
96777
|
+
const retryMissingProjectScores = async (projectScans) => mapWithConcurrency(projectScans, 64, async (projectScan) => {
|
|
96778
|
+
if (!(projectScan.result.score === null && !projectScan.isScoreDisabled && !hasIncompleteScoreAnalysis(projectScan.result.skippedChecks))) return projectScan;
|
|
96779
|
+
const score = await calculateScore(filterScansForSurface([projectScan], "score"));
|
|
96780
|
+
recordCount(METRIC.scanScoreRetry, 1, { succeeded: score !== null });
|
|
96781
|
+
return score === null ? projectScan : {
|
|
96782
|
+
...projectScan,
|
|
96783
|
+
result: {
|
|
96784
|
+
...projectScan.result,
|
|
96785
|
+
score
|
|
96786
|
+
}
|
|
96787
|
+
};
|
|
96788
|
+
});
|
|
96789
|
+
//#endregion
|
|
96286
96790
|
//#region src/cli/utils/resolve-project-relative-directory.ts
|
|
96287
96791
|
/**
|
|
96288
96792
|
* The scanned project's directory relative to the diff root, forward-slashed —
|
|
@@ -96343,6 +96847,16 @@ const resolveProjectChangedLineRanges = (rootDirectory, projectDirectory, ranges
|
|
|
96343
96847
|
});
|
|
96344
96848
|
};
|
|
96345
96849
|
//#endregion
|
|
96850
|
+
//#region src/cli/utils/resolve-project-scan.ts
|
|
96851
|
+
const resolveProjectScan = async (rootScanTarget, projectDirectory) => {
|
|
96852
|
+
const projectScanTarget = projectDirectory === rootScanTarget.resolvedDirectory ? rootScanTarget : await resolveScanTarget(projectDirectory, { allowAmbiguous: true });
|
|
96853
|
+
return {
|
|
96854
|
+
directory: projectScanTarget.resolvedDirectory,
|
|
96855
|
+
config: projectDirectory === rootScanTarget.resolvedDirectory ? rootScanTarget.userConfig : mergeReactDoctorConfigs(rootScanTarget.userConfig, projectScanTarget.userConfig ?? void 0),
|
|
96856
|
+
configSourceDirectory: projectScanTarget.userConfig?.plugins === void 0 ? rootScanTarget.configSourceDirectory : projectScanTarget.configSourceDirectory
|
|
96857
|
+
};
|
|
96858
|
+
};
|
|
96859
|
+
//#endregion
|
|
96346
96860
|
//#region src/cli/utils/build-diagnostic-issue-url.ts
|
|
96347
96861
|
const formatRuleIdentifier = (diagnostic) => `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
96348
96862
|
const buildDiagnosticIssueBody = (input) => {
|
|
@@ -96704,6 +97218,15 @@ const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
|
|
|
96704
97218
|
}
|
|
96705
97219
|
}));
|
|
96706
97220
|
};
|
|
97221
|
+
const reportSkippedProjects = (input) => {
|
|
97222
|
+
input.skippedProjects.sort((left, right) => left.directory.localeCompare(right.directory));
|
|
97223
|
+
if (input.skippedProjects.length === 0) return;
|
|
97224
|
+
recordCount(METRIC.scanProjectSkipped, input.skippedProjects.length, { reason: "max-duration" });
|
|
97225
|
+
if (!input.isQuiet) {
|
|
97226
|
+
cliLogger.warn(formatSkippedProjectsMessage(input.skippedProjects.length));
|
|
97227
|
+
cliLogger.break();
|
|
97228
|
+
}
|
|
97229
|
+
};
|
|
96707
97230
|
/**
|
|
96708
97231
|
* Post-scan finalization shared by the staged-arm and project-loop
|
|
96709
97232
|
* paths of `inspectAction`: emit the JSON report (when in JSON mode)
|
|
@@ -96718,7 +97241,7 @@ const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
|
|
|
96718
97241
|
*/
|
|
96719
97242
|
const finalizeScans = (input) => {
|
|
96720
97243
|
const baselineDeltas = input.completedScans.flatMap((scan) => scan.result.baselineDelta ? [scan.result.baselineDelta] : []);
|
|
96721
|
-
const baselineComputed = input.completedScans.length > 0 && input.completedScans.every((scan) => scan.result.baselineDelta !== void 0);
|
|
97244
|
+
const baselineComputed = input.skippedProjects.length === 0 && input.completedScans.length > 0 && input.completedScans.every((scan) => scan.result.baselineDelta !== void 0);
|
|
96722
97245
|
const baselineDegraded = input.baselineIntended && !baselineComputed;
|
|
96723
97246
|
const mode = baselineDegraded ? "diff" : input.mode;
|
|
96724
97247
|
const isReactDetected = input.completedScans.some((scan) => hasReactRuntime(scan.result.project));
|
|
@@ -96739,6 +97262,7 @@ const finalizeScans = (input) => {
|
|
|
96739
97262
|
mode,
|
|
96740
97263
|
diff: input.diff,
|
|
96741
97264
|
scans: jsonCompletedScans,
|
|
97265
|
+
skippedProjects: input.skippedProjects,
|
|
96742
97266
|
totalElapsedMilliseconds: performance$1.now() - input.startTime,
|
|
96743
97267
|
baseline,
|
|
96744
97268
|
baselineDegraded
|
|
@@ -96859,16 +97383,16 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
96859
97383
|
return;
|
|
96860
97384
|
}
|
|
96861
97385
|
const buildProjectScanContext = async (projectDirectory) => {
|
|
96862
|
-
const
|
|
96863
|
-
const scanDirectory =
|
|
97386
|
+
const projectScan = await resolveProjectScan(scanTarget, projectDirectory);
|
|
97387
|
+
const scanDirectory = projectScan.directory;
|
|
96864
97388
|
const treeRelativeDirectory = resolveProjectRelativeDirectory(resolvedDirectory, scanDirectory);
|
|
96865
97389
|
if (treeRelativeDirectory === null) return null;
|
|
96866
97390
|
return {
|
|
96867
97391
|
projectDirectory,
|
|
96868
97392
|
scanDirectory,
|
|
96869
97393
|
treeRelativeDirectory,
|
|
96870
|
-
projectConfig:
|
|
96871
|
-
projectConfigSourceDirectory:
|
|
97394
|
+
projectConfig: projectScan.config,
|
|
97395
|
+
projectConfigSourceDirectory: projectScan.configSourceDirectory
|
|
96872
97396
|
};
|
|
96873
97397
|
};
|
|
96874
97398
|
const projectScanContexts = [];
|
|
@@ -96955,6 +97479,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
96955
97479
|
includePaths: resolveProjectSourceFilePaths(resolvedDirectory, projectScan.scanDirectory, projectScan.stagedFiles.filter((stagedFile) => materializedStagedFiles.has(stagedFile)))
|
|
96956
97480
|
})).filter((projectRun) => projectRun.includePaths.length > 0);
|
|
96957
97481
|
const isMultiProject = stagedProjectRuns.length > 1;
|
|
97482
|
+
const skippedProjects = [];
|
|
96958
97483
|
if (stagedProjectRuns.length === 0) {
|
|
96959
97484
|
snapshot.cleanup();
|
|
96960
97485
|
reportNothingToScan({
|
|
@@ -96970,6 +97495,13 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
96970
97495
|
}
|
|
96971
97496
|
const scanStagedProject = async (projectRun) => {
|
|
96972
97497
|
const { projectScan, includePaths } = projectRun;
|
|
97498
|
+
if (scanDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(scanDeadlineEpochMs) === 0) {
|
|
97499
|
+
skippedProjects.push({
|
|
97500
|
+
directory: projectScan.scanDirectory,
|
|
97501
|
+
reason: "max-duration"
|
|
97502
|
+
});
|
|
97503
|
+
return null;
|
|
97504
|
+
}
|
|
96973
97505
|
const projectTempDirectory = Path.join(snapshot.tempDirectory, projectScan.treeRelativeDirectory);
|
|
96974
97506
|
const scanResult = await inspectProject(projectTempDirectory, {
|
|
96975
97507
|
...scanOptions,
|
|
@@ -97010,7 +97542,11 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97010
97542
|
});
|
|
97011
97543
|
const completedScans = stagedBatch.completedScans;
|
|
97012
97544
|
try {
|
|
97013
|
-
|
|
97545
|
+
reportSkippedProjects({
|
|
97546
|
+
skippedProjects,
|
|
97547
|
+
isQuiet
|
|
97548
|
+
});
|
|
97549
|
+
if (!isQuiet && isMultiProject && completedScans.length > 0) {
|
|
97014
97550
|
const showShareLink = !isShareOptedOut(completedScans, scanOptions.noScore) && !scanOptions.isCi;
|
|
97015
97551
|
await runPromise(printMultiProjectSummary({
|
|
97016
97552
|
completedScans,
|
|
@@ -97028,6 +97564,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97028
97564
|
}
|
|
97029
97565
|
finalizeScans({
|
|
97030
97566
|
completedScans,
|
|
97567
|
+
skippedProjects,
|
|
97031
97568
|
mode: "staged",
|
|
97032
97569
|
diff: null,
|
|
97033
97570
|
baselineIntended: false,
|
|
@@ -97103,12 +97640,28 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97103
97640
|
}
|
|
97104
97641
|
cliLogger.break();
|
|
97105
97642
|
}
|
|
97106
|
-
const
|
|
97107
|
-
const
|
|
97108
|
-
|
|
97109
|
-
|
|
97110
|
-
|
|
97111
|
-
|
|
97643
|
+
const projectScans = deduplicateProjectScans(await Promise.all(projectDirectories.map((projectDirectory) => resolveProjectScan(scanTarget, projectDirectory))));
|
|
97644
|
+
const isMultiProject = projectScans.length > 1;
|
|
97645
|
+
const rootProjectScan = projectScans.find((projectScan) => Path.resolve(projectScan.directory) === Path.resolve(resolvedDirectory));
|
|
97646
|
+
const workspaceDeadCodeOwner = resolveWorkspaceDeadCodeOwner({
|
|
97647
|
+
rootDirectory: resolvedDirectory,
|
|
97648
|
+
projectDirectories: projectScans.map((projectScan) => projectScan.directory),
|
|
97649
|
+
isRootDeadCodeEnabled: scanOptions.deadCode ?? rootProjectScan?.config?.deadCode ?? true
|
|
97650
|
+
});
|
|
97651
|
+
if (workspaceDeadCodeOwner !== null) recordCount(METRIC.scanWorkspaceDeadCodeShared, 1, { projectCount: projectScans.length });
|
|
97652
|
+
const precomputedSourceFileCounts = isMultiProject && !isDiffMode ? await collectProjectSourceFileCounts(resolvedDirectory, projectScans.map((projectScan) => projectScan.directory)) : null;
|
|
97653
|
+
const skippedProjects = [];
|
|
97654
|
+
const scanProject = async (projectScan) => {
|
|
97655
|
+
if (scanDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(scanDeadlineEpochMs) === 0) {
|
|
97656
|
+
skippedProjects.push({
|
|
97657
|
+
directory: projectScan.directory,
|
|
97658
|
+
reason: "max-duration"
|
|
97659
|
+
});
|
|
97660
|
+
return null;
|
|
97661
|
+
}
|
|
97662
|
+
const scanDirectory = projectScan.directory;
|
|
97663
|
+
const projectConfig = projectScan.config;
|
|
97664
|
+
const ownsWorkspaceDeadCode = scanDirectory === workspaceDeadCodeOwner;
|
|
97112
97665
|
const supplyChainEnabled = flags.supplyChain ?? projectConfig?.supplyChain?.enabled !== false;
|
|
97113
97666
|
let includePaths;
|
|
97114
97667
|
let supplyChainManifestChanged = false;
|
|
@@ -97132,12 +97685,16 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97132
97685
|
if (!isQuiet && !isMultiProject) cliLogger.dim(" ");
|
|
97133
97686
|
const scanResult = await inspectProject(scanDirectory, {
|
|
97134
97687
|
...scanOptions,
|
|
97688
|
+
deadCode: workspaceDeadCodeOwner === null ? scanOptions.deadCode : ownsWorkspaceDeadCode,
|
|
97689
|
+
precomputedSourceFileCount: precomputedSourceFileCounts?.get(scanDirectory),
|
|
97135
97690
|
deadlineEpochMs: scanDeadlineEpochMs,
|
|
97136
97691
|
includePaths,
|
|
97137
97692
|
configOverride: projectConfig,
|
|
97138
|
-
configSourceDirectory:
|
|
97693
|
+
configSourceDirectory: projectScan.configSourceDirectory ?? void 0,
|
|
97139
97694
|
suppressRendering: isMultiProject,
|
|
97140
97695
|
concurrentScan: isMultiProject,
|
|
97696
|
+
excludedProjectDirectories: projectScans.filter((candidateProjectScan) => isPathInsideDirectory(candidateProjectScan.directory, scanDirectory)).map((candidateProjectScan) => candidateProjectScan.directory),
|
|
97697
|
+
retainExcludedProjectDeadCodeDiagnostics: ownsWorkspaceDeadCode,
|
|
97141
97698
|
baseline: baselineRef !== null && projectBaselineBaseFiles !== null && projectBaselineHeadFiles !== null ? {
|
|
97142
97699
|
ref: baselineRef,
|
|
97143
97700
|
baseFiles: projectBaselineBaseFiles,
|
|
@@ -97154,12 +97711,19 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97154
97711
|
};
|
|
97155
97712
|
};
|
|
97156
97713
|
const projectBatch = await runProjectScanBatch({
|
|
97157
|
-
projects:
|
|
97714
|
+
projects: projectScans,
|
|
97158
97715
|
isQuiet,
|
|
97159
97716
|
isSilent: scanOptions.silent === true,
|
|
97160
97717
|
scanProject
|
|
97161
97718
|
});
|
|
97162
|
-
const completedScans = projectBatch.completedScans
|
|
97719
|
+
const completedScans = await retryMissingProjectScores(projectBatch.completedScans.map((completedScan) => ({
|
|
97720
|
+
...completedScan,
|
|
97721
|
+
isScoreDisabled: scanOptions.noScore ?? completedScan.config?.noScore ?? false
|
|
97722
|
+
})));
|
|
97723
|
+
reportSkippedProjects({
|
|
97724
|
+
skippedProjects,
|
|
97725
|
+
isQuiet
|
|
97726
|
+
});
|
|
97163
97727
|
if (!isQuiet && isMultiProject && completedScans.length > 0) {
|
|
97164
97728
|
const showShareLink = !isShareOptedOut(completedScans, scanOptions.noScore) && !scanOptions.isCi;
|
|
97165
97729
|
await runPromise(printMultiProjectSummary({
|
|
@@ -97177,6 +97741,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97177
97741
|
if (flags.outputDir && !didScansWriteDump) await runPromise(printDiagnosticsDump(selectedSurfaceDiagnostics, flags.outputDir, false, isQuiet ? "stderr" : "stdout"));
|
|
97178
97742
|
finalizeScans({
|
|
97179
97743
|
completedScans,
|
|
97744
|
+
skippedProjects,
|
|
97180
97745
|
mode: baselineRef ? "baseline" : isDiffMode ? "diff" : "full",
|
|
97181
97746
|
diff: isDiffMode ? diffInfo : null,
|
|
97182
97747
|
baselineIntended: scope === "changed" && diffInfo !== null && !diffInfo.isCurrentChanges,
|
|
@@ -97261,6 +97826,56 @@ const installAction = async (options, command) => {
|
|
|
97261
97826
|
}
|
|
97262
97827
|
};
|
|
97263
97828
|
//#endregion
|
|
97829
|
+
//#region src/cli/utils/should-use-tui.ts
|
|
97830
|
+
const shouldUseTui = (input) => {
|
|
97831
|
+
if (input.isNonInteractiveEnvironment || !input.stdinIsTty || !input.stdoutIsTty || !input.supportsRawMode || input.nodeMajorVersion < 22 || input.terminalName === "dumb") return false;
|
|
97832
|
+
const flags = input.flags;
|
|
97833
|
+
let requiresStableConfiguredScope = false;
|
|
97834
|
+
if (input.userConfig?.scope !== void 0) requiresStableConfiguredScope = input.userConfig.scope !== "full";
|
|
97835
|
+
else if (input.userConfig?.diff !== void 0) requiresStableConfiguredScope = input.userConfig.diff !== false && input.userConfig.diff !== "false";
|
|
97836
|
+
else if (input.userConfig?.base !== void 0) requiresStableConfiguredScope = true;
|
|
97837
|
+
return !(flags.verbose === true || flags.outputDir !== void 0 || flags.score === true || flags.json === true || flags.jsonCompact === true || flags.jsonOut !== void 0 || flags.staged === true || flags.scope !== void 0 || flags.base !== void 0 || flags.includeUntracked === true || flags.diff !== void 0 || flags.changedFilesFrom !== void 0 || flags.debug === true || flags.failOn !== void 0 || requiresStableConfiguredScope);
|
|
97838
|
+
};
|
|
97839
|
+
//#endregion
|
|
97840
|
+
//#region src/cli/commands/scan.ts
|
|
97841
|
+
const runScanCommand = async (input) => {
|
|
97842
|
+
if (input.flags.cache === false) process.env.REACT_DOCTOR_NO_CACHE = "1";
|
|
97843
|
+
const nodeMajorVersion = Number(process.versions.node.split(".")[0]);
|
|
97844
|
+
const tuiEnvironment = {
|
|
97845
|
+
flags: input.flags,
|
|
97846
|
+
isNonInteractiveEnvironment: isNonInteractiveEnvironment(),
|
|
97847
|
+
nodeMajorVersion,
|
|
97848
|
+
stdinIsTty: process.stdin.isTTY === true,
|
|
97849
|
+
stdoutIsTty: process.stdout.isTTY === true,
|
|
97850
|
+
supportsRawMode: typeof process.stdin.setRawMode === "function",
|
|
97851
|
+
terminalName: process.env.TERM
|
|
97852
|
+
};
|
|
97853
|
+
if (!shouldUseTui(tuiEnvironment)) {
|
|
97854
|
+
await inspectAction(input.directory, input.flags, input.invocationCommand);
|
|
97855
|
+
return;
|
|
97856
|
+
}
|
|
97857
|
+
await runProjectMigrations(path.resolve(input.directory));
|
|
97858
|
+
const scanTarget = await resolveScanTarget(input.directory, { allowAmbiguous: true });
|
|
97859
|
+
if (!shouldUseTui({
|
|
97860
|
+
...tuiEnvironment,
|
|
97861
|
+
userConfig: scanTarget.userConfig
|
|
97862
|
+
})) {
|
|
97863
|
+
await inspectAction(input.directory, input.flags, input.invocationCommand);
|
|
97864
|
+
return;
|
|
97865
|
+
}
|
|
97866
|
+
recordCount(METRIC.cliInvoked, 1, { command: input.invocationCommand });
|
|
97867
|
+
const { runScanApp } = await import("./run-scan-app-QbtG43NL.js");
|
|
97868
|
+
const { shouldFail } = await runScanApp({
|
|
97869
|
+
directory: input.directory,
|
|
97870
|
+
scanTarget,
|
|
97871
|
+
options: resolveCliInspectOptions(input.flags, null),
|
|
97872
|
+
projectFlag: input.flags.project,
|
|
97873
|
+
skipPrompts: input.flags.yes ?? false,
|
|
97874
|
+
blocking: input.flags.blocking
|
|
97875
|
+
});
|
|
97876
|
+
if (shouldFail) process.exitCode = 1;
|
|
97877
|
+
};
|
|
97878
|
+
//#endregion
|
|
97264
97879
|
//#region src/cli/utils/rule-catalog.ts
|
|
97265
97880
|
const buildRuleCatalog = () => REACT_DOCTOR_RULES.map((entry) => ({
|
|
97266
97881
|
key: entry.key,
|
|
@@ -97901,13 +98516,39 @@ const ensureWindowsUtf8Console = () => {
|
|
|
97901
98516
|
} catch {}
|
|
97902
98517
|
};
|
|
97903
98518
|
//#endregion
|
|
98519
|
+
//#region src/cli/utils/active-tui-renderer.ts
|
|
98520
|
+
let activeTuiRenderer = null;
|
|
98521
|
+
const registerActiveTuiRenderer = (renderer) => {
|
|
98522
|
+
activeTuiRenderer = renderer;
|
|
98523
|
+
return () => {
|
|
98524
|
+
if (activeTuiRenderer === renderer) activeTuiRenderer = null;
|
|
98525
|
+
};
|
|
98526
|
+
};
|
|
98527
|
+
const clearActiveTuiRenderer = () => {
|
|
98528
|
+
const renderer = activeTuiRenderer;
|
|
98529
|
+
activeTuiRenderer = null;
|
|
98530
|
+
renderer?.clear();
|
|
98531
|
+
};
|
|
98532
|
+
//#endregion
|
|
97904
98533
|
//#region src/cli/utils/exit-gracefully.ts
|
|
98534
|
+
let didStartExiting = false;
|
|
97905
98535
|
const exitGracefully = () => {
|
|
98536
|
+
if (didStartExiting) return process.exit(130);
|
|
98537
|
+
didStartExiting = true;
|
|
98538
|
+
activeScanAbortRegistry.abortAll();
|
|
98539
|
+
clearActiveTuiRenderer();
|
|
97906
98540
|
try {
|
|
97907
98541
|
if (isJsonModeActive()) writeJsonErrorReport(/* @__PURE__ */ new Error("Scan cancelled by user (SIGINT/SIGTERM)"));
|
|
97908
|
-
else console.log(
|
|
98542
|
+
else console.log([
|
|
98543
|
+
"",
|
|
98544
|
+
"Cancelled.",
|
|
98545
|
+
"",
|
|
98546
|
+
buildSectionDivider(),
|
|
98547
|
+
"",
|
|
98548
|
+
...buildFooterLinkLines({ shareUrl: null })
|
|
98549
|
+
].join("\n"));
|
|
97909
98550
|
} catch {}
|
|
97910
|
-
process.exit(130);
|
|
98551
|
+
flushSentry().finally(() => process.exit(130));
|
|
97911
98552
|
};
|
|
97912
98553
|
//#endregion
|
|
97913
98554
|
//#region src/cli/utils/guard-stdin.ts
|
|
@@ -98025,6 +98666,7 @@ const ROOT_FLAG_SPEC = {
|
|
|
98025
98666
|
"--json-compact",
|
|
98026
98667
|
"--lint",
|
|
98027
98668
|
"--no-color",
|
|
98669
|
+
"--no-cache",
|
|
98028
98670
|
"--no-dead-code",
|
|
98029
98671
|
"--no-lint",
|
|
98030
98672
|
"--no-parallel",
|
|
@@ -98148,13 +98790,18 @@ const COMMAND_FLAG_SPECS = new Map([
|
|
|
98148
98790
|
longOptionsWithoutValues: new Set([
|
|
98149
98791
|
"--color",
|
|
98150
98792
|
"--help",
|
|
98793
|
+
"--no-cache",
|
|
98151
98794
|
"--no-color",
|
|
98152
98795
|
"--no-dead-code",
|
|
98153
98796
|
"--no-score",
|
|
98154
98797
|
"--no-supply-chain",
|
|
98155
98798
|
"--yes"
|
|
98156
98799
|
]),
|
|
98157
|
-
longOptionsWithRequiredValues: new Set([
|
|
98800
|
+
longOptionsWithRequiredValues: new Set([
|
|
98801
|
+
"--blocking",
|
|
98802
|
+
"--max-duration",
|
|
98803
|
+
"--project"
|
|
98804
|
+
]),
|
|
98158
98805
|
longOptionsWithOptionalValues: /* @__PURE__ */ new Set(),
|
|
98159
98806
|
shortOptionsWithoutValues: new Set(["-h", "-y"]),
|
|
98160
98807
|
shortOptionsWithRequiredValues: new Set(["-p"])
|
|
@@ -98296,6 +98943,7 @@ ${highlighter.dim("Scope:")}
|
|
|
98296
98943
|
Dead-code, supply-chain, external lint-config, custom-plugin, and health-score passes are skipped.
|
|
98297
98944
|
Standard scan flags such as ${highlighter.info("--scope")}, ${highlighter.info("--project")}, ${highlighter.info("--verbose")}, and ${highlighter.info("--json")} still work.
|
|
98298
98945
|
`;
|
|
98946
|
+
const MAX_DURATION_OPTION_DESCRIPTION = "scan time budget for the whole run, shared across workspace projects: past it, queued projects, remaining lint batches, and dead-code are skipped and partial results are reported (skipped files and projects are listed in the JSON report)";
|
|
98299
98947
|
const renderCiHelpEpilog = () => `
|
|
98300
98948
|
${highlighter.dim("Examples:")}
|
|
98301
98949
|
${formatExampleLines([
|
|
@@ -98315,8 +98963,12 @@ ${highlighter.dim("Learn more:")}
|
|
|
98315
98963
|
${highlighter.info(CI_URL)}
|
|
98316
98964
|
`;
|
|
98317
98965
|
const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
|
|
98318
|
-
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 and inline HTML script 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 (honors --project and config `projects`; exits 0 when nothing is staged)").option("--max-duration <seconds>",
|
|
98319
|
-
program.action(
|
|
98966
|
+
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("--no-cache", "disable all scan caches for this run").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 and inline HTML script 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 (honors --project and config `projects`; exits 0 when nothing is staged)").option("--max-duration <seconds>", MAX_DURATION_OPTION_DESCRIPTION).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);
|
|
98967
|
+
program.action((directory = ".", flags) => runScanCommand({
|
|
98968
|
+
directory,
|
|
98969
|
+
flags,
|
|
98970
|
+
invocationCommand: "inspect"
|
|
98971
|
+
}));
|
|
98320
98972
|
program.command("design [directory]").description("Run only the focused UI design diagnostics").addHelpText("after", renderDesignHelpEpilog).action((directory, _options, command) => designAction(directory ?? ".", command.optsWithGlobals()));
|
|
98321
98973
|
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));
|
|
98322
98974
|
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);
|
|
@@ -98337,38 +98989,11 @@ rules.command("category <category> <severity>").description("Set the severity fo
|
|
|
98337
98989
|
rules.command("ignore-tag <tag>").description("Skip a whole rule family by tag before linting (e.g. design)").option("-c, --cwd <cwd>", "working directory", process.cwd()).action((tag, _options, command) => rulesIgnoreTagAction(tag, command.optsWithGlobals()));
|
|
98338
98990
|
rules.command("unignore-tag <tag>").description("Stop ignoring a tag previously skipped via ignore-tag").option("-c, --cwd <cwd>", "working directory", process.cwd()).action((tag, _options, command) => rulesUnignoreTagAction(tag, command.optsWithGlobals()));
|
|
98339
98991
|
program.command("experimental-lsp", { hidden: false }).description("[experimental] run the React Doctor language server over stdio (for editors)").allowUnknownOption().action(() => {});
|
|
98340
|
-
program.command("experimental-tui [directory]", { hidden: true }).description("[experimental] interactive, scrollable scan report").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").option("--no-dead-code", "skip dead-code analysis").option("--no-supply-chain", "skip the dependency supply-chain scan").option("--no-score", "skip the score API, the share URL, and crash reporting").option("-p, --project <names>", "scan specific workspace projects (comma-separated, or *)").option("-y, --yes", "skip the project prompt and scan every discovered project").action(
|
|
98341
|
-
|
|
98342
|
-
|
|
98343
|
-
|
|
98344
|
-
|
|
98345
|
-
const nodeMajorVersion = Number(process.versions.node.split(".")[0]);
|
|
98346
|
-
if (process.stdout.isTTY !== true || process.stdin.isTTY !== true || isNonInteractiveEnvironment() || nodeMajorVersion < 22) {
|
|
98347
|
-
await inspectAction(directory, {
|
|
98348
|
-
deadCode,
|
|
98349
|
-
score: options.score === false ? false : void 0,
|
|
98350
|
-
supplyChain,
|
|
98351
|
-
project: options.project,
|
|
98352
|
-
yes: options.yes,
|
|
98353
|
-
blocking: options.blocking
|
|
98354
|
-
});
|
|
98355
|
-
return;
|
|
98356
|
-
}
|
|
98357
|
-
recordCount(METRIC.cliInvoked, 1, { command: "experimental-tui" });
|
|
98358
|
-
const { runScanApp } = await import("./run-scan-app-DHRvqfkX.js");
|
|
98359
|
-
const { shouldFail } = await runScanApp({
|
|
98360
|
-
directory,
|
|
98361
|
-
options: {
|
|
98362
|
-
deadCode,
|
|
98363
|
-
noScore,
|
|
98364
|
-
supplyChain
|
|
98365
|
-
},
|
|
98366
|
-
projectFlag: options.project,
|
|
98367
|
-
skipPrompts: options.yes ?? false,
|
|
98368
|
-
blocking: options.blocking
|
|
98369
|
-
});
|
|
98370
|
-
if (shouldFail) process.exitCode = 1;
|
|
98371
|
-
});
|
|
98992
|
+
program.command("experimental-tui [directory]", { hidden: true }).description("[experimental] interactive, scrollable scan report").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").option("--no-dead-code", "skip dead-code analysis").option("--no-supply-chain", "skip the dependency supply-chain scan").option("--score", "only print the numeric score (for scripts and CI)").option("--no-score", "skip the score API, the share URL, and crash reporting").option("--no-cache", "disable all scan caches for this run").option("--max-duration <seconds>", MAX_DURATION_OPTION_DESCRIPTION).option("-p, --project <names>", "scan specific workspace projects (comma-separated, or *)").option("-y, --yes", "skip the project prompt and scan every discovered project").action((directory = ".", _localOptions, command) => runScanCommand({
|
|
98993
|
+
directory,
|
|
98994
|
+
flags: command.optsWithGlobals(),
|
|
98995
|
+
invocationCommand: "experimental-tui"
|
|
98996
|
+
}));
|
|
98372
98997
|
process.stdout.on("error", (error) => {
|
|
98373
98998
|
if (error.code === "EPIPE") process.exit(0);
|
|
98374
98999
|
});
|
|
@@ -98395,7 +99020,7 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
98395
99020
|
handleError(error, { sentryEventId });
|
|
98396
99021
|
});
|
|
98397
99022
|
//#endregion
|
|
98398
|
-
export {
|
|
99023
|
+
export { TUI_REPORT_DETAIL_WIDTH_FRACTION as $, shouldRecordOnboarding as A, buildSortedRuleGroups as B, createInvocationInspect as C, markOnboardingComplete as D, doctorFace as E, buildEmptyReportMessage as F, recordCount as G, formatLearnMoreLine as H, hasIncompleteScoreAnalysis as I, findNearestPackageDirectory as J, recordDistribution as K, buildNoScoreMessage as L, computeProjectedScore as M, formatElapsedTime as N, canAnimateOnboarding as O, buildCodeFrame as P, TUI_REPORT_COMPACT_MAX_ROWS as Q, hasLintHardFailure as R, deduplicateProjectScans as S, easeOutCubic as T, resolveBlockingLevel as U, formatFixRecipeLine as V, isReactDoctorWorkflowInstalled as W, CI_TRUST_COMPANIES as X, isUnicodeSupported as Y, METRIC as Z, launchCliAgent as _, succeed$3 as _t, selectProjects as a, buildRuleDocsUrl as at, formatSkippedProjectsMessage as b, resolveWorkspaceDeadCodeOwner as c, highlighter as ct, countUniqueScannedFiles as d, remainingDeadlineBudgetMs as dt, isCiEnvironment as et, setUpGitHubActions as f, resolveScanTarget as ft, copyToClipboard as g, void_ as gt, CLI_AGENT_BINARIES as h, sync as ht, discoverWorkspacePackages as i, Reporter as it, filterDiagnosticsByCategories as j, isOnboardingForced as k, resolveCliInspectOptions as l, isPathInsideDirectory as lt, detectLaunchableAgents as m, runPromise as mt, registerActiveTuiRenderer as n, GITHUB_ACTIONS_SETUP_URL as nt, resolveProjectScan as o, getCategoryImpact as ot, openUrl as p, yieldToEventLoop as pt, toForwardSlashes as q, shouldFailScanGate as r, Progress as rt, retryMissingProjectScores as s, hasPublishedFixRecipe as st, exitGracefully as t, DIAGNOSTIC_CATEGORY_BUCKETS as tt, filterScansForSurface as u, mapWithConcurrency as ut, buildHandoffPayload as v, printFooter as w, collectProjectSourceFileCounts as x, isShareOptedOut as y, buildRulePriorityMap as z };
|
|
98399
99024
|
|
|
98400
99025
|
//# sourceMappingURL=cli.js.map
|
|
98401
|
-
//# debugId=
|
|
99026
|
+
//# debugId=e021759c-79c5-5b42-8720-2243f4e6a8f8
|