react-doctor 0.9.3-dev.cc28626 → 0.9.3-dev.f958625
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 +1022 -467
- package/dist/index.d.ts +9 -2
- package/dist/index.js +639 -320
- package/dist/lsp.js +649 -324
- package/dist/{run-scan-app-DtYeufgr.js → run-scan-app-QbtG43NL.js} +243 -112
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="2b3a9fe9-8109-5067-854f-a10b2e6454e7")}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 = "";
|
|
@@ -67185,13 +67402,7 @@ const hasReactDependency = (packageJson) => {
|
|
|
67185
67402
|
};
|
|
67186
67403
|
return Object.keys(allDependencies).some((packageName) => REACT_DEPENDENCY_NAMES.has(packageName));
|
|
67187
67404
|
};
|
|
67188
|
-
const getPreactVersion = (packageJson) =>
|
|
67189
|
-
return {
|
|
67190
|
-
...packageJson.peerDependencies,
|
|
67191
|
-
...packageJson.dependencies,
|
|
67192
|
-
...packageJson.devDependencies
|
|
67193
|
-
}.preact ?? null;
|
|
67194
|
-
};
|
|
67405
|
+
const getPreactVersion = (packageJson) => getDependencySpec(packageJson, "preact");
|
|
67195
67406
|
const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, sourceDirectory = rootDirectory, sourcePackageJson = rootPackageJson, version }) => {
|
|
67196
67407
|
if (version === null || !isCatalogReference(version)) return version;
|
|
67197
67408
|
const catalogName = extractCatalogName(version);
|
|
@@ -67933,7 +68144,16 @@ const getBrowserslistProtectionRequirement = (directory) => {
|
|
|
67933
68144
|
if (!queries) return void 0;
|
|
67934
68145
|
let requirement;
|
|
67935
68146
|
let hasUnsupportedTarget = false;
|
|
67936
|
-
|
|
68147
|
+
const previousIgnoreOldData = process.env.BROWSERSLIST_IGNORE_OLD_DATA;
|
|
68148
|
+
let browserTargets = [];
|
|
68149
|
+
try {
|
|
68150
|
+
process.env.BROWSERSLIST_IGNORE_OLD_DATA = "true";
|
|
68151
|
+
browserTargets = (0, import_browserslist.default)(queries);
|
|
68152
|
+
} finally {
|
|
68153
|
+
if (previousIgnoreOldData === void 0) delete process.env.BROWSERSLIST_IGNORE_OLD_DATA;
|
|
68154
|
+
else process.env.BROWSERSLIST_IGNORE_OLD_DATA = previousIgnoreOldData;
|
|
68155
|
+
}
|
|
68156
|
+
for (const browserTarget of browserTargets) {
|
|
67937
68157
|
const browserRequirement = getBrowserProtectionRequirement(browserTarget);
|
|
67938
68158
|
if (browserRequirement === "unsupported") hasUnsupportedTarget = true;
|
|
67939
68159
|
if (browserRequirement === "noreferrer") requirement = "noreferrer";
|
|
@@ -68039,9 +68259,9 @@ const cachedProjectInfos = /* @__PURE__ */ new Map();
|
|
|
68039
68259
|
* framework-agnostic rules. Throws only when the directory has nothing
|
|
68040
68260
|
* to scan (no enclosing project and no source files of its own).
|
|
68041
68261
|
*/
|
|
68042
|
-
const discoverProjectWithoutPackageJson = (directory) => {
|
|
68043
|
-
const sourceFileCount = countSourceFiles(directory);
|
|
68044
|
-
const hasOwnTsConfig =
|
|
68262
|
+
const discoverProjectWithoutPackageJson = (directory, options) => {
|
|
68263
|
+
const sourceFileCount = options.sourceFileCount ?? countSourceFiles(directory);
|
|
68264
|
+
const hasOwnTsConfig = isFile(Path.join(directory, "tsconfig.json"));
|
|
68045
68265
|
const enclosingProjectRoot = findNearestAncestorPackageJson(directory);
|
|
68046
68266
|
const enclosingProject = enclosingProjectRoot !== null ? discoverProject(enclosingProjectRoot) : null;
|
|
68047
68267
|
if (enclosingProject !== null) return {
|
|
@@ -68107,12 +68327,15 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
68107
68327
|
sourceFileCount
|
|
68108
68328
|
};
|
|
68109
68329
|
};
|
|
68110
|
-
const discoverProject = (directory) => {
|
|
68330
|
+
const discoverProject = (directory, options = {}) => {
|
|
68111
68331
|
const cached = cachedProjectInfos.get(directory);
|
|
68112
|
-
if (cached !== void 0) return cached
|
|
68332
|
+
if (cached !== void 0) return options.sourceFileCount === void 0 ? cached : {
|
|
68333
|
+
...cached,
|
|
68334
|
+
sourceFileCount: options.sourceFileCount
|
|
68335
|
+
};
|
|
68113
68336
|
const packageJsonPath = Path.join(directory, "package.json");
|
|
68114
68337
|
if (!isFile(packageJsonPath)) {
|
|
68115
|
-
const synthesized = discoverProjectWithoutPackageJson(directory);
|
|
68338
|
+
const synthesized = discoverProjectWithoutPackageJson(directory, options);
|
|
68116
68339
|
cachedProjectInfos.set(directory, synthesized);
|
|
68117
68340
|
return synthesized;
|
|
68118
68341
|
}
|
|
@@ -68174,8 +68397,8 @@ const discoverProject = (directory) => {
|
|
|
68174
68397
|
const tailwindVersion = tailwindcss.version;
|
|
68175
68398
|
const zodVersion = zod.version;
|
|
68176
68399
|
const projectName = packageJson.name ?? Path.basename(directory);
|
|
68177
|
-
const hasTypeScript =
|
|
68178
|
-
const sourceFileCount = countSourceFiles(directory);
|
|
68400
|
+
const hasTypeScript = isFile(Path.join(directory, "tsconfig.json"));
|
|
68401
|
+
const sourceFileCount = options.sourceFileCount ?? countSourceFiles(directory);
|
|
68179
68402
|
const hasReactNativeWorkspace = framework === "expo" || framework === "react-native" || workspaceFacts.hasReactNativeAwarePackage;
|
|
68180
68403
|
const expoVersion = hasReactNativeWorkspace ? resolveCatalogBackedDependencyVersion({
|
|
68181
68404
|
rootDirectory: directory,
|
|
@@ -68304,6 +68527,155 @@ const discoverProject = (directory) => {
|
|
|
68304
68527
|
* single source file has nothing to analyze.
|
|
68305
68528
|
*/
|
|
68306
68529
|
const isAnalyzableProject = (project) => project.reactVersion !== null || project.preactVersion !== null || project.sourceFileCount > 0;
|
|
68530
|
+
const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
68531
|
+
"effect/no-adjust-state-on-prop-change": "react-doctor/no-adjust-state-on-prop-change",
|
|
68532
|
+
"effect/no-chain-state-updates": "react-doctor/no-chain-state-updates",
|
|
68533
|
+
"effect/no-derived-state": "react-doctor/no-derived-state",
|
|
68534
|
+
"effect/no-event-handler": "react-doctor/no-event-handler",
|
|
68535
|
+
"effect/no-initialize-state": "react-doctor/no-initialize-state",
|
|
68536
|
+
"effect/no-pass-data-to-parent": "react-doctor/no-pass-data-to-parent",
|
|
68537
|
+
"effect/no-pass-live-state-to-parent": "react-doctor/no-pass-live-state-to-parent",
|
|
68538
|
+
"effect/no-reset-all-state-on-prop-change": "react-doctor/no-reset-all-state-on-prop-change",
|
|
68539
|
+
"jsx-a11y/alt-text": "react-doctor/alt-text",
|
|
68540
|
+
"jsx-a11y/anchor-ambiguous-text": "react-doctor/anchor-ambiguous-text",
|
|
68541
|
+
"jsx-a11y/anchor-has-content": "react-doctor/anchor-has-content",
|
|
68542
|
+
"jsx-a11y/anchor-is-valid": "react-doctor/anchor-is-valid",
|
|
68543
|
+
"jsx-a11y/aria-activedescendant-has-tabindex": "react-doctor/aria-activedescendant-has-tabindex",
|
|
68544
|
+
"jsx-a11y/aria-props": "react-doctor/aria-props",
|
|
68545
|
+
"jsx-a11y/aria-proptypes": "react-doctor/aria-proptypes",
|
|
68546
|
+
"jsx-a11y/aria-role": "react-doctor/aria-role",
|
|
68547
|
+
"jsx-a11y/aria-unsupported-elements": "react-doctor/aria-unsupported-elements",
|
|
68548
|
+
"jsx-a11y/autocomplete-valid": "react-doctor/autocomplete-valid",
|
|
68549
|
+
"jsx-a11y/click-events-have-key-events": "react-doctor/click-events-have-key-events",
|
|
68550
|
+
"jsx-a11y/control-has-associated-label": "react-doctor/control-has-associated-label",
|
|
68551
|
+
"jsx-a11y/heading-has-content": "react-doctor/heading-has-content",
|
|
68552
|
+
"jsx-a11y/html-has-lang": "react-doctor/html-has-lang",
|
|
68553
|
+
"jsx-a11y/iframe-has-title": "react-doctor/iframe-has-title",
|
|
68554
|
+
"jsx-a11y/img-redundant-alt": "react-doctor/img-redundant-alt",
|
|
68555
|
+
"jsx-a11y/interactive-supports-focus": "react-doctor/interactive-supports-focus",
|
|
68556
|
+
"jsx-a11y/label-has-associated-control": "react-doctor/label-has-associated-control",
|
|
68557
|
+
"jsx-a11y/lang": "react-doctor/lang",
|
|
68558
|
+
"jsx-a11y/media-has-caption": "react-doctor/media-has-caption",
|
|
68559
|
+
"jsx-a11y/mouse-events-have-key-events": "react-doctor/mouse-events-have-key-events",
|
|
68560
|
+
"jsx-a11y/no-access-key": "react-doctor/no-access-key",
|
|
68561
|
+
"jsx-a11y/no-aria-hidden-on-focusable": "react-doctor/no-aria-hidden-on-focusable",
|
|
68562
|
+
"jsx-a11y/no-autofocus": "react-doctor/no-autofocus",
|
|
68563
|
+
"jsx-a11y/no-distracting-elements": "react-doctor/no-distracting-elements",
|
|
68564
|
+
"jsx-a11y/no-interactive-element-to-noninteractive-role": "react-doctor/no-interactive-element-to-noninteractive-role",
|
|
68565
|
+
"jsx-a11y/no-noninteractive-element-interactions": "react-doctor/no-noninteractive-element-interactions",
|
|
68566
|
+
"jsx-a11y/no-noninteractive-element-to-interactive-role": "react-doctor/no-noninteractive-element-to-interactive-role",
|
|
68567
|
+
"jsx-a11y/no-noninteractive-tabindex": "react-doctor/no-noninteractive-tabindex",
|
|
68568
|
+
"jsx-a11y/no-redundant-roles": "react-doctor/no-redundant-roles",
|
|
68569
|
+
"jsx-a11y/no-static-element-interactions": "react-doctor/no-static-element-interactions",
|
|
68570
|
+
"jsx-a11y/prefer-tag-over-role": "react-doctor/prefer-tag-over-role",
|
|
68571
|
+
"jsx-a11y/role-has-required-aria-props": "react-doctor/role-has-required-aria-props",
|
|
68572
|
+
"jsx-a11y/role-supports-aria-props": "react-doctor/role-supports-aria-props",
|
|
68573
|
+
"jsx-a11y/scope": "react-doctor/scope",
|
|
68574
|
+
"jsx-a11y/tabindex-no-positive": "react-doctor/tabindex-no-positive",
|
|
68575
|
+
"react-hooks/exhaustive-deps": "react-doctor/exhaustive-deps",
|
|
68576
|
+
"react-hooks/rules-of-hooks": "react-doctor/rules-of-hooks",
|
|
68577
|
+
"react-perf/jsx-no-jsx-as-prop": "react-doctor/jsx-no-jsx-as-prop",
|
|
68578
|
+
"react-perf/jsx-no-new-array-as-prop": "react-doctor/jsx-no-new-array-as-prop",
|
|
68579
|
+
"react-perf/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
68580
|
+
"react-perf/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
68581
|
+
"react-refresh/only-export-components": "react-doctor/only-export-components",
|
|
68582
|
+
"react/button-has-type": "react-doctor/button-has-type",
|
|
68583
|
+
"react/checked-requires-onchange-or-readonly": "react-doctor/checked-requires-onchange-or-readonly",
|
|
68584
|
+
"react/display-name": "react-doctor/display-name",
|
|
68585
|
+
"react/exhaustive-deps": "react-doctor/exhaustive-deps",
|
|
68586
|
+
"react/forbid-component-props": "react-doctor/forbid-component-props",
|
|
68587
|
+
"react/forbid-dom-props": "react-doctor/forbid-dom-props",
|
|
68588
|
+
"react/forbid-elements": "react-doctor/forbid-elements",
|
|
68589
|
+
"react/forward-ref-uses-ref": "react-doctor/forward-ref-uses-ref",
|
|
68590
|
+
"react/hook-use-state": "react-doctor/hook-use-state",
|
|
68591
|
+
"react/iframe-missing-sandbox": "react-doctor/iframe-missing-sandbox",
|
|
68592
|
+
"react/jsx-boolean-value": "react-doctor/jsx-boolean-value",
|
|
68593
|
+
"react/jsx-curly-brace-presence": "react-doctor/jsx-curly-brace-presence",
|
|
68594
|
+
"react/jsx-filename-extension": "react-doctor/jsx-filename-extension",
|
|
68595
|
+
"react/jsx-fragments": "react-doctor/jsx-fragments",
|
|
68596
|
+
"react/jsx-handler-names": "react-doctor/jsx-handler-names",
|
|
68597
|
+
"react/jsx-key": "react-doctor/jsx-key",
|
|
68598
|
+
"react/jsx-max-depth": "react-doctor/jsx-max-depth",
|
|
68599
|
+
"react/jsx-no-comment-textnodes": "react-doctor/jsx-no-comment-textnodes",
|
|
68600
|
+
"react/jsx-no-constructed-context-values": "react-doctor/jsx-no-constructed-context-values",
|
|
68601
|
+
"react/jsx-no-duplicate-props": "react-doctor/jsx-no-duplicate-props",
|
|
68602
|
+
"react/jsx-no-jsx-as-prop": "react-doctor/jsx-no-jsx-as-prop",
|
|
68603
|
+
"react/jsx-no-new-array-as-prop": "react-doctor/jsx-no-new-array-as-prop",
|
|
68604
|
+
"react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
68605
|
+
"react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
68606
|
+
"react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
|
|
68607
|
+
"react/jsx-no-undef": "react-doctor/jsx-no-undef",
|
|
68608
|
+
"react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
|
|
68609
|
+
"react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
|
|
68610
|
+
"react/jsx-props-no-spread-multi": "react-doctor/jsx-props-no-spread-multi",
|
|
68611
|
+
"react/jsx-props-no-spreading": "react-doctor/jsx-props-no-spreading",
|
|
68612
|
+
"react/no-array-index-key": "react-doctor/no-array-index-key",
|
|
68613
|
+
"react/no-children-prop": "react-doctor/no-children-prop",
|
|
68614
|
+
"react/no-clone-element": "react-doctor/no-clone-element",
|
|
68615
|
+
"react/no-danger": "react-doctor/no-danger",
|
|
68616
|
+
"react/no-danger-with-children": "react-doctor/no-danger-with-children",
|
|
68617
|
+
"react/no-did-mount-set-state": "react-doctor/no-did-mount-set-state",
|
|
68618
|
+
"react/no-did-update-set-state": "react-doctor/no-did-update-set-state",
|
|
68619
|
+
"react/no-direct-mutation-state": "react-doctor/no-direct-mutation-state",
|
|
68620
|
+
"react/no-find-dom-node": "react-doctor/no-find-dom-node",
|
|
68621
|
+
"react/no-is-mounted": "react-doctor/no-is-mounted",
|
|
68622
|
+
"react/no-multi-comp": "react-doctor/no-multi-comp",
|
|
68623
|
+
"react/no-namespace": "react-doctor/no-namespace",
|
|
68624
|
+
"react/no-react-children": "react-doctor/no-react-children",
|
|
68625
|
+
"react/no-redundant-should-component-update": "react-doctor/no-redundant-should-component-update",
|
|
68626
|
+
"react/no-render-return-value": "react-doctor/no-render-return-value",
|
|
68627
|
+
"react/no-set-state": "react-doctor/no-set-state",
|
|
68628
|
+
"react/no-string-refs": "react-doctor/no-string-refs",
|
|
68629
|
+
"react/no-this-in-sfc": "react-doctor/no-this-in-sfc",
|
|
68630
|
+
"react/no-unescaped-entities": "react-doctor/no-unescaped-entities",
|
|
68631
|
+
"react/no-unknown-property": "react-doctor/no-unknown-property",
|
|
68632
|
+
"react/no-unsafe": "react-doctor/no-unsafe",
|
|
68633
|
+
"react/no-unstable-nested-components": "react-doctor/no-unstable-nested-components",
|
|
68634
|
+
"react/no-will-update-set-state": "react-doctor/no-will-update-set-state",
|
|
68635
|
+
"react/only-export-components": "react-doctor/only-export-components",
|
|
68636
|
+
"react/prefer-es6-class": "react-doctor/prefer-es6-class",
|
|
68637
|
+
"react/prefer-function-component": "react-doctor/prefer-function-component",
|
|
68638
|
+
"react/react-in-jsx-scope": "react-doctor/react-in-jsx-scope",
|
|
68639
|
+
"react/require-render-return": "react-doctor/require-render-return",
|
|
68640
|
+
"react/rules-of-hooks": "react-doctor/rules-of-hooks",
|
|
68641
|
+
"react/self-closing-comp": "react-doctor/self-closing-comp",
|
|
68642
|
+
"react/state-in-constructor": "react-doctor/state-in-constructor",
|
|
68643
|
+
"react/style-prop-object": "react-doctor/style-prop-object",
|
|
68644
|
+
"react/void-dom-elements-no-children": "react-doctor/void-dom-elements-no-children"
|
|
68645
|
+
};
|
|
68646
|
+
const NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS = /* @__PURE__ */ new Map();
|
|
68647
|
+
for (const [legacyRuleKey, nativeRuleKey] of Object.entries(LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY)) {
|
|
68648
|
+
const aliases = NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.get(nativeRuleKey) ?? [];
|
|
68649
|
+
aliases.push(legacyRuleKey);
|
|
68650
|
+
NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.set(nativeRuleKey, aliases);
|
|
68651
|
+
}
|
|
68652
|
+
const getLegacyRuleKeysForNative = (ruleKey) => NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.get(ruleKey) ?? [];
|
|
68653
|
+
const REACT_DOCTOR_RULE_KEY_PREFIX = "react-doctor/";
|
|
68654
|
+
const canonicalizeRuleKey = (ruleKey) => {
|
|
68655
|
+
const nativeRuleKey = LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY[ruleKey];
|
|
68656
|
+
return typeof nativeRuleKey === "string" ? nativeRuleKey : ruleKey;
|
|
68657
|
+
};
|
|
68658
|
+
const isReactDoctorShortIdOf = (bareRuleKey, qualifiedRuleKey) => !bareRuleKey.includes("/") && qualifiedRuleKey === `react-doctor/${bareRuleKey}`;
|
|
68659
|
+
/**
|
|
68660
|
+
* Canonicalizes a rule key as users write it in config: a legacy alias
|
|
68661
|
+
* (`react/jsx-key`) maps to its native key, and a bare short id (`no-eval`)
|
|
68662
|
+
* qualifies as `react-doctor/<id>` — mirroring `isSameRuleKey`'s matching —
|
|
68663
|
+
* so telemetry groups every spelling of one rule under one key.
|
|
68664
|
+
*/
|
|
68665
|
+
const canonicalizeUserRuleKey = (ruleKey) => {
|
|
68666
|
+
const nativeRuleKey = canonicalizeRuleKey(ruleKey);
|
|
68667
|
+
return nativeRuleKey.includes("/") ? nativeRuleKey : `${REACT_DOCTOR_RULE_KEY_PREFIX}${nativeRuleKey}`;
|
|
68668
|
+
};
|
|
68669
|
+
const isSameRuleKey = (candidateRuleKey, targetRuleKey) => {
|
|
68670
|
+
const canonicalCandidate = canonicalizeRuleKey(candidateRuleKey);
|
|
68671
|
+
const canonicalTarget = canonicalizeRuleKey(targetRuleKey);
|
|
68672
|
+
if (canonicalCandidate === canonicalTarget) return true;
|
|
68673
|
+
return isReactDoctorShortIdOf(canonicalCandidate, canonicalTarget) || isReactDoctorShortIdOf(canonicalTarget, canonicalCandidate);
|
|
68674
|
+
};
|
|
68675
|
+
const getEquivalentRuleKeys = (ruleKey) => {
|
|
68676
|
+
const nativeRuleKey = canonicalizeRuleKey(ruleKey);
|
|
68677
|
+
return [nativeRuleKey, ...getLegacyRuleKeysForNative(nativeRuleKey)];
|
|
68678
|
+
};
|
|
68307
68679
|
const messageFromUnknown = (error) => error instanceof Error ? error.message : String(error);
|
|
68308
68680
|
var InvalidGlobPatternError = class extends Error {
|
|
68309
68681
|
pattern;
|
|
@@ -68357,6 +68729,11 @@ const warnConfigIssue = (message) => {
|
|
|
68357
68729
|
};
|
|
68358
68730
|
const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
68359
68731
|
const collectStringList = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
68732
|
+
const hasMatchingRuleOverride = (ruleIds, ruleIdentifier) => {
|
|
68733
|
+
if (ruleIds.size === 0) return true;
|
|
68734
|
+
for (const ruleId of ruleIds) if (isSameRuleKey(ruleId, ruleIdentifier)) return true;
|
|
68735
|
+
return false;
|
|
68736
|
+
};
|
|
68360
68737
|
const validateOverrideEntry = (entry, index) => {
|
|
68361
68738
|
if (!isPlainObject(entry)) {
|
|
68362
68739
|
warnConfigIssue(`ignore.overrides[${index}] must be an object with { files, rules }; ignoring this entry.`);
|
|
@@ -68397,7 +68774,7 @@ const isDiagnosticIgnoredByOverrides = (diagnostic, rootDirectory, overrides) =>
|
|
|
68397
68774
|
if (overrides.length === 0) return false;
|
|
68398
68775
|
const relativeFilePath = toRelativePath(diagnostic.filePath, rootDirectory);
|
|
68399
68776
|
const ruleIdentifier = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
68400
|
-
return overrides.some((override) => override.filePatterns.some((pattern) => pattern.test(relativeFilePath)) && (override.ruleIds
|
|
68777
|
+
return overrides.some((override) => override.filePatterns.some((pattern) => pattern.test(relativeFilePath)) && hasMatchingRuleOverride(override.ruleIds, ruleIdentifier));
|
|
68401
68778
|
};
|
|
68402
68779
|
const SEVERITY_FOR_OVERRIDE = {
|
|
68403
68780
|
error: "error",
|
|
@@ -68432,155 +68809,6 @@ const buildRuleSeverityControls = (config) => {
|
|
|
68432
68809
|
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
68433
68810
|
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
68434
68811
|
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
68435
|
-
const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
68436
|
-
"effect/no-adjust-state-on-prop-change": "react-doctor/no-adjust-state-on-prop-change",
|
|
68437
|
-
"effect/no-chain-state-updates": "react-doctor/no-chain-state-updates",
|
|
68438
|
-
"effect/no-derived-state": "react-doctor/no-derived-state",
|
|
68439
|
-
"effect/no-event-handler": "react-doctor/no-event-handler",
|
|
68440
|
-
"effect/no-initialize-state": "react-doctor/no-initialize-state",
|
|
68441
|
-
"effect/no-pass-data-to-parent": "react-doctor/no-pass-data-to-parent",
|
|
68442
|
-
"effect/no-pass-live-state-to-parent": "react-doctor/no-pass-live-state-to-parent",
|
|
68443
|
-
"effect/no-reset-all-state-on-prop-change": "react-doctor/no-reset-all-state-on-prop-change",
|
|
68444
|
-
"jsx-a11y/alt-text": "react-doctor/alt-text",
|
|
68445
|
-
"jsx-a11y/anchor-ambiguous-text": "react-doctor/anchor-ambiguous-text",
|
|
68446
|
-
"jsx-a11y/anchor-has-content": "react-doctor/anchor-has-content",
|
|
68447
|
-
"jsx-a11y/anchor-is-valid": "react-doctor/anchor-is-valid",
|
|
68448
|
-
"jsx-a11y/aria-activedescendant-has-tabindex": "react-doctor/aria-activedescendant-has-tabindex",
|
|
68449
|
-
"jsx-a11y/aria-props": "react-doctor/aria-props",
|
|
68450
|
-
"jsx-a11y/aria-proptypes": "react-doctor/aria-proptypes",
|
|
68451
|
-
"jsx-a11y/aria-role": "react-doctor/aria-role",
|
|
68452
|
-
"jsx-a11y/aria-unsupported-elements": "react-doctor/aria-unsupported-elements",
|
|
68453
|
-
"jsx-a11y/autocomplete-valid": "react-doctor/autocomplete-valid",
|
|
68454
|
-
"jsx-a11y/click-events-have-key-events": "react-doctor/click-events-have-key-events",
|
|
68455
|
-
"jsx-a11y/control-has-associated-label": "react-doctor/control-has-associated-label",
|
|
68456
|
-
"jsx-a11y/heading-has-content": "react-doctor/heading-has-content",
|
|
68457
|
-
"jsx-a11y/html-has-lang": "react-doctor/html-has-lang",
|
|
68458
|
-
"jsx-a11y/iframe-has-title": "react-doctor/iframe-has-title",
|
|
68459
|
-
"jsx-a11y/img-redundant-alt": "react-doctor/img-redundant-alt",
|
|
68460
|
-
"jsx-a11y/interactive-supports-focus": "react-doctor/interactive-supports-focus",
|
|
68461
|
-
"jsx-a11y/label-has-associated-control": "react-doctor/label-has-associated-control",
|
|
68462
|
-
"jsx-a11y/lang": "react-doctor/lang",
|
|
68463
|
-
"jsx-a11y/media-has-caption": "react-doctor/media-has-caption",
|
|
68464
|
-
"jsx-a11y/mouse-events-have-key-events": "react-doctor/mouse-events-have-key-events",
|
|
68465
|
-
"jsx-a11y/no-access-key": "react-doctor/no-access-key",
|
|
68466
|
-
"jsx-a11y/no-aria-hidden-on-focusable": "react-doctor/no-aria-hidden-on-focusable",
|
|
68467
|
-
"jsx-a11y/no-autofocus": "react-doctor/no-autofocus",
|
|
68468
|
-
"jsx-a11y/no-distracting-elements": "react-doctor/no-distracting-elements",
|
|
68469
|
-
"jsx-a11y/no-interactive-element-to-noninteractive-role": "react-doctor/no-interactive-element-to-noninteractive-role",
|
|
68470
|
-
"jsx-a11y/no-noninteractive-element-interactions": "react-doctor/no-noninteractive-element-interactions",
|
|
68471
|
-
"jsx-a11y/no-noninteractive-element-to-interactive-role": "react-doctor/no-noninteractive-element-to-interactive-role",
|
|
68472
|
-
"jsx-a11y/no-noninteractive-tabindex": "react-doctor/no-noninteractive-tabindex",
|
|
68473
|
-
"jsx-a11y/no-redundant-roles": "react-doctor/no-redundant-roles",
|
|
68474
|
-
"jsx-a11y/no-static-element-interactions": "react-doctor/no-static-element-interactions",
|
|
68475
|
-
"jsx-a11y/prefer-tag-over-role": "react-doctor/prefer-tag-over-role",
|
|
68476
|
-
"jsx-a11y/role-has-required-aria-props": "react-doctor/role-has-required-aria-props",
|
|
68477
|
-
"jsx-a11y/role-supports-aria-props": "react-doctor/role-supports-aria-props",
|
|
68478
|
-
"jsx-a11y/scope": "react-doctor/scope",
|
|
68479
|
-
"jsx-a11y/tabindex-no-positive": "react-doctor/tabindex-no-positive",
|
|
68480
|
-
"react-hooks/exhaustive-deps": "react-doctor/exhaustive-deps",
|
|
68481
|
-
"react-hooks/rules-of-hooks": "react-doctor/rules-of-hooks",
|
|
68482
|
-
"react-perf/jsx-no-jsx-as-prop": "react-doctor/jsx-no-jsx-as-prop",
|
|
68483
|
-
"react-perf/jsx-no-new-array-as-prop": "react-doctor/jsx-no-new-array-as-prop",
|
|
68484
|
-
"react-perf/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
68485
|
-
"react-perf/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
68486
|
-
"react-refresh/only-export-components": "react-doctor/only-export-components",
|
|
68487
|
-
"react/button-has-type": "react-doctor/button-has-type",
|
|
68488
|
-
"react/checked-requires-onchange-or-readonly": "react-doctor/checked-requires-onchange-or-readonly",
|
|
68489
|
-
"react/display-name": "react-doctor/display-name",
|
|
68490
|
-
"react/exhaustive-deps": "react-doctor/exhaustive-deps",
|
|
68491
|
-
"react/forbid-component-props": "react-doctor/forbid-component-props",
|
|
68492
|
-
"react/forbid-dom-props": "react-doctor/forbid-dom-props",
|
|
68493
|
-
"react/forbid-elements": "react-doctor/forbid-elements",
|
|
68494
|
-
"react/forward-ref-uses-ref": "react-doctor/forward-ref-uses-ref",
|
|
68495
|
-
"react/hook-use-state": "react-doctor/hook-use-state",
|
|
68496
|
-
"react/iframe-missing-sandbox": "react-doctor/iframe-missing-sandbox",
|
|
68497
|
-
"react/jsx-boolean-value": "react-doctor/jsx-boolean-value",
|
|
68498
|
-
"react/jsx-curly-brace-presence": "react-doctor/jsx-curly-brace-presence",
|
|
68499
|
-
"react/jsx-filename-extension": "react-doctor/jsx-filename-extension",
|
|
68500
|
-
"react/jsx-fragments": "react-doctor/jsx-fragments",
|
|
68501
|
-
"react/jsx-handler-names": "react-doctor/jsx-handler-names",
|
|
68502
|
-
"react/jsx-key": "react-doctor/jsx-key",
|
|
68503
|
-
"react/jsx-max-depth": "react-doctor/jsx-max-depth",
|
|
68504
|
-
"react/jsx-no-comment-textnodes": "react-doctor/jsx-no-comment-textnodes",
|
|
68505
|
-
"react/jsx-no-constructed-context-values": "react-doctor/jsx-no-constructed-context-values",
|
|
68506
|
-
"react/jsx-no-duplicate-props": "react-doctor/jsx-no-duplicate-props",
|
|
68507
|
-
"react/jsx-no-jsx-as-prop": "react-doctor/jsx-no-jsx-as-prop",
|
|
68508
|
-
"react/jsx-no-new-array-as-prop": "react-doctor/jsx-no-new-array-as-prop",
|
|
68509
|
-
"react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
68510
|
-
"react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
68511
|
-
"react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
|
|
68512
|
-
"react/jsx-no-undef": "react-doctor/jsx-no-undef",
|
|
68513
|
-
"react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
|
|
68514
|
-
"react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
|
|
68515
|
-
"react/jsx-props-no-spread-multi": "react-doctor/jsx-props-no-spread-multi",
|
|
68516
|
-
"react/jsx-props-no-spreading": "react-doctor/jsx-props-no-spreading",
|
|
68517
|
-
"react/no-array-index-key": "react-doctor/no-array-index-key",
|
|
68518
|
-
"react/no-children-prop": "react-doctor/no-children-prop",
|
|
68519
|
-
"react/no-clone-element": "react-doctor/no-clone-element",
|
|
68520
|
-
"react/no-danger": "react-doctor/no-danger",
|
|
68521
|
-
"react/no-danger-with-children": "react-doctor/no-danger-with-children",
|
|
68522
|
-
"react/no-did-mount-set-state": "react-doctor/no-did-mount-set-state",
|
|
68523
|
-
"react/no-did-update-set-state": "react-doctor/no-did-update-set-state",
|
|
68524
|
-
"react/no-direct-mutation-state": "react-doctor/no-direct-mutation-state",
|
|
68525
|
-
"react/no-find-dom-node": "react-doctor/no-find-dom-node",
|
|
68526
|
-
"react/no-is-mounted": "react-doctor/no-is-mounted",
|
|
68527
|
-
"react/no-multi-comp": "react-doctor/no-multi-comp",
|
|
68528
|
-
"react/no-namespace": "react-doctor/no-namespace",
|
|
68529
|
-
"react/no-react-children": "react-doctor/no-react-children",
|
|
68530
|
-
"react/no-redundant-should-component-update": "react-doctor/no-redundant-should-component-update",
|
|
68531
|
-
"react/no-render-return-value": "react-doctor/no-render-return-value",
|
|
68532
|
-
"react/no-set-state": "react-doctor/no-set-state",
|
|
68533
|
-
"react/no-string-refs": "react-doctor/no-string-refs",
|
|
68534
|
-
"react/no-this-in-sfc": "react-doctor/no-this-in-sfc",
|
|
68535
|
-
"react/no-unescaped-entities": "react-doctor/no-unescaped-entities",
|
|
68536
|
-
"react/no-unknown-property": "react-doctor/no-unknown-property",
|
|
68537
|
-
"react/no-unsafe": "react-doctor/no-unsafe",
|
|
68538
|
-
"react/no-unstable-nested-components": "react-doctor/no-unstable-nested-components",
|
|
68539
|
-
"react/no-will-update-set-state": "react-doctor/no-will-update-set-state",
|
|
68540
|
-
"react/only-export-components": "react-doctor/only-export-components",
|
|
68541
|
-
"react/prefer-es6-class": "react-doctor/prefer-es6-class",
|
|
68542
|
-
"react/prefer-function-component": "react-doctor/prefer-function-component",
|
|
68543
|
-
"react/react-in-jsx-scope": "react-doctor/react-in-jsx-scope",
|
|
68544
|
-
"react/require-render-return": "react-doctor/require-render-return",
|
|
68545
|
-
"react/rules-of-hooks": "react-doctor/rules-of-hooks",
|
|
68546
|
-
"react/self-closing-comp": "react-doctor/self-closing-comp",
|
|
68547
|
-
"react/state-in-constructor": "react-doctor/state-in-constructor",
|
|
68548
|
-
"react/style-prop-object": "react-doctor/style-prop-object",
|
|
68549
|
-
"react/void-dom-elements-no-children": "react-doctor/void-dom-elements-no-children"
|
|
68550
|
-
};
|
|
68551
|
-
const NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS = /* @__PURE__ */ new Map();
|
|
68552
|
-
for (const [legacyRuleKey, nativeRuleKey] of Object.entries(LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY)) {
|
|
68553
|
-
const aliases = NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.get(nativeRuleKey) ?? [];
|
|
68554
|
-
aliases.push(legacyRuleKey);
|
|
68555
|
-
NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.set(nativeRuleKey, aliases);
|
|
68556
|
-
}
|
|
68557
|
-
const getLegacyRuleKeysForNative = (ruleKey) => NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.get(ruleKey) ?? [];
|
|
68558
|
-
const REACT_DOCTOR_RULE_KEY_PREFIX = "react-doctor/";
|
|
68559
|
-
const canonicalizeRuleKey = (ruleKey) => {
|
|
68560
|
-
const nativeRuleKey = LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY[ruleKey];
|
|
68561
|
-
return typeof nativeRuleKey === "string" ? nativeRuleKey : ruleKey;
|
|
68562
|
-
};
|
|
68563
|
-
const isReactDoctorShortIdOf = (bareRuleKey, qualifiedRuleKey) => !bareRuleKey.includes("/") && qualifiedRuleKey === `react-doctor/${bareRuleKey}`;
|
|
68564
|
-
/**
|
|
68565
|
-
* Canonicalizes a rule key as users write it in config: a legacy alias
|
|
68566
|
-
* (`react/jsx-key`) maps to its native key, and a bare short id (`no-eval`)
|
|
68567
|
-
* qualifies as `react-doctor/<id>` — mirroring `isSameRuleKey`'s matching —
|
|
68568
|
-
* so telemetry groups every spelling of one rule under one key.
|
|
68569
|
-
*/
|
|
68570
|
-
const canonicalizeUserRuleKey = (ruleKey) => {
|
|
68571
|
-
const nativeRuleKey = canonicalizeRuleKey(ruleKey);
|
|
68572
|
-
return nativeRuleKey.includes("/") ? nativeRuleKey : `${REACT_DOCTOR_RULE_KEY_PREFIX}${nativeRuleKey}`;
|
|
68573
|
-
};
|
|
68574
|
-
const isSameRuleKey = (candidateRuleKey, targetRuleKey) => {
|
|
68575
|
-
const canonicalCandidate = canonicalizeRuleKey(candidateRuleKey);
|
|
68576
|
-
const canonicalTarget = canonicalizeRuleKey(targetRuleKey);
|
|
68577
|
-
if (canonicalCandidate === canonicalTarget) return true;
|
|
68578
|
-
return isReactDoctorShortIdOf(canonicalCandidate, canonicalTarget) || isReactDoctorShortIdOf(canonicalTarget, canonicalCandidate);
|
|
68579
|
-
};
|
|
68580
|
-
const getEquivalentRuleKeys = (ruleKey) => {
|
|
68581
|
-
const nativeRuleKey = canonicalizeRuleKey(ruleKey);
|
|
68582
|
-
return [nativeRuleKey, ...getLegacyRuleKeysForNative(nativeRuleKey)];
|
|
68583
|
-
};
|
|
68584
68812
|
const stripDescriptionTail = (ruleList) => {
|
|
68585
68813
|
const descriptionMatch = ruleList.match(/(?:^|\s)--\s/);
|
|
68586
68814
|
if (!descriptionMatch || descriptionMatch.index === void 0) return ruleList;
|
|
@@ -69227,12 +69455,8 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
69227
69455
|
if (rule.tags.includes("migration-hint")) return false;
|
|
69228
69456
|
return getFileContext(diagnostic.filePath) !== "production";
|
|
69229
69457
|
};
|
|
69230
|
-
const
|
|
69231
|
-
for (const
|
|
69232
|
-
return false;
|
|
69233
|
-
};
|
|
69234
|
-
const isAppOnlyRule = (ruleIdentifier) => {
|
|
69235
|
-
for (const appOnlyRuleKey of APP_ONLY_RULE_KEYS) if (isSameRuleKey(appOnlyRuleKey, ruleIdentifier)) return true;
|
|
69458
|
+
const matchesRuleKey = (ruleIdentifier, candidates) => {
|
|
69459
|
+
for (const candidate of candidates) if (isSameRuleKey(candidate, ruleIdentifier)) return true;
|
|
69236
69460
|
return false;
|
|
69237
69461
|
};
|
|
69238
69462
|
const isRnRawTextSuppressedByConfig = (diagnostic) => {
|
|
@@ -69274,12 +69498,13 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
69274
69498
|
if (explicitSeverityOverride === "off") return suppress(current, "config");
|
|
69275
69499
|
if (explicitSeverityOverride !== void 0) current = restampSeverity(current, explicitSeverityOverride);
|
|
69276
69500
|
}
|
|
69501
|
+
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
69277
69502
|
if (explicitRuleOverride === void 0) {
|
|
69278
|
-
if (
|
|
69503
|
+
if (matchesRuleKey(ruleIdentifier, APP_ONLY_RULE_KEYS) && isLibraryFile(current.filePath)) return null;
|
|
69279
69504
|
}
|
|
69280
69505
|
if (!showWarnings && current.severity === "warning" && explicitSeverityOverride !== "warn") return null;
|
|
69281
69506
|
if (userConfig) {
|
|
69282
|
-
if (
|
|
69507
|
+
if (matchesRuleKey(ruleIdentifier, ignoredRules)) return suppress(current, "config");
|
|
69283
69508
|
if (isFileIgnoredByPatterns(current.filePath, rootDirectory, ignoredFilePatterns)) return null;
|
|
69284
69509
|
if (isDiagnosticIgnoredByOverrides(current, rootDirectory, compiledOverrides)) return suppress(current, "override");
|
|
69285
69510
|
if (isRnRawTextSuppressedByConfig(current)) return null;
|
|
@@ -69288,7 +69513,6 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
69288
69513
|
if (respectInlineDisables && current.line > 0) {
|
|
69289
69514
|
const lines = getFileLines(current.filePath);
|
|
69290
69515
|
if (lines) {
|
|
69291
|
-
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
69292
69516
|
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
69293
69517
|
if (evaluation.isSuppressed) return suppress(current, evaluation.isForeignDirective ? "foreign-inline" : "inline");
|
|
69294
69518
|
if (evaluation.nearMissHint) current = {
|
|
@@ -69564,7 +69788,8 @@ const readSystemFacts$1 = () => ({
|
|
|
69564
69788
|
/**
|
|
69565
69789
|
* Auto lint-worker count: the smaller of the (cgroup-CPU-aware) core count and
|
|
69566
69790
|
* the number of `PER_WORKER_MEM_BUDGET_BYTES` workers that fit in available
|
|
69567
|
-
* memory,
|
|
69791
|
+
* memory, and the measured automatic parallel-efficiency ceiling, then clamped
|
|
69792
|
+
* to `[MIN, HARD_MAX]` by `resolveScanConcurrency`.
|
|
69568
69793
|
*
|
|
69569
69794
|
* `os.availableParallelism()` already respects cgroup CPU quotas, so the core
|
|
69570
69795
|
* term needs no help. Available memory is `os.totalmem()` floored by the cgroup
|
|
@@ -69580,7 +69805,7 @@ const readSystemFacts$1 = () => ({
|
|
|
69580
69805
|
const resolveAutoScanConcurrency = (facts = readSystemFacts$1()) => {
|
|
69581
69806
|
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
69582
69807
|
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / PER_WORKER_MEM_BUDGET_BYTES);
|
|
69583
|
-
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
69808
|
+
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers, 10));
|
|
69584
69809
|
};
|
|
69585
69810
|
const resolveLintBatchOrdering = () => {
|
|
69586
69811
|
return process.env["REACT_DOCTOR_LINT_BATCH_ORDERING"]?.trim().toLowerCase() === "arrival" ? "arrival" : "cost";
|
|
@@ -69593,24 +69818,22 @@ const resolveLintBatchOrdering = () => {
|
|
|
69593
69818
|
*/
|
|
69594
69819
|
var OxlintSpawnTimeoutMs = class extends Reference("react-doctor/OxlintSpawnTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_OXLINT_SPAWN_TIMEOUT_MS", OXLINT_SPAWN_TIMEOUT_MS) }) {};
|
|
69595
69820
|
/**
|
|
69596
|
-
* Effect-side cap on the lint phase.
|
|
69597
|
-
*
|
|
69821
|
+
* Optional Effect-side cap on the lint phase. Unbounded unless explicitly
|
|
69822
|
+
* configured through the env var or a test layer.
|
|
69598
69823
|
* Tests override via `Layer.succeed(LintPhaseTimeoutMs, ...)`.
|
|
69599
69824
|
*/
|
|
69600
|
-
var LintPhaseTimeoutMs = class extends Reference("react-doctor/LintPhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_LINT_PHASE_TIMEOUT_MS",
|
|
69825
|
+
var LintPhaseTimeoutMs = class extends Reference("react-doctor/LintPhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_LINT_PHASE_TIMEOUT_MS", null) }) {};
|
|
69601
69826
|
/**
|
|
69602
|
-
* Effect-side cap on the dead-code phase
|
|
69603
|
-
*
|
|
69604
|
-
* type-heavy projects; tests override via
|
|
69827
|
+
* Optional Effect-side cap on the dead-code phase. Unbounded unless explicitly
|
|
69828
|
+
* configured through the env var or a test layer. Tests override via
|
|
69605
69829
|
* `Layer.succeed(DeadCodePhaseTimeoutMs, ...)`.
|
|
69606
69830
|
*/
|
|
69607
|
-
var DeadCodePhaseTimeoutMs = class extends Reference("react-doctor/DeadCodePhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_DEAD_CODE_PHASE_TIMEOUT_MS",
|
|
69831
|
+
var DeadCodePhaseTimeoutMs = class extends Reference("react-doctor/DeadCodePhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_DEAD_CODE_PHASE_TIMEOUT_MS", null) }) {};
|
|
69608
69832
|
/**
|
|
69609
|
-
*
|
|
69610
|
-
*
|
|
69611
|
-
* large repos; tests override via `Layer.succeed(ScanDeadlineMs, ...)`.
|
|
69833
|
+
* Optional overall scan deadline. Unbounded unless explicitly configured
|
|
69834
|
+
* through the env var or a test layer.
|
|
69612
69835
|
*/
|
|
69613
|
-
var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SCAN_DEADLINE_MS",
|
|
69836
|
+
var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SCAN_DEADLINE_MS", null) }) {};
|
|
69614
69837
|
/**
|
|
69615
69838
|
* Wall-clock budget for the supply-chain check when it runs on a background
|
|
69616
69839
|
* fiber overlapping the lint pass. Reads from the env var on startup so the
|
|
@@ -69683,14 +69906,14 @@ var DeadCodeOverlap = class extends Reference("react-doctor/DeadCodeOverlap", {
|
|
|
69683
69906
|
/**
|
|
69684
69907
|
* How the full-scan lint pass plans its file batches. `"cost"` (the default)
|
|
69685
69908
|
* builds size-balanced LPT batches (`planLintBatches`): the same mandatory
|
|
69686
|
-
* batch count as greedy chunking
|
|
69687
|
-
*
|
|
69909
|
+
* batch count as greedy chunking, but every batch gets an even share of files
|
|
69910
|
+
* AND bytes, so no full chunk is a straggler while
|
|
69688
69911
|
* the remainder-batch worker idles — and the heavy files are SPREAD across
|
|
69689
69912
|
* batches, the precondition the old sort-desc-then-chunk-100 `cost` mode
|
|
69690
69913
|
* lacked (it packed the heaviest files into one wave-1 straggler batch,
|
|
69691
69914
|
* measurably regressing size-skewed repos, which is why it never earned the
|
|
69692
69915
|
* default). `"arrival"` (`REACT_DOCTOR_LINT_BATCH_ORDERING=arrival`) is the
|
|
69693
|
-
* rollback hatch to plain greedy
|
|
69916
|
+
* rollback hatch to plain greedy fixed-size chunking in discovery order. Tests
|
|
69694
69917
|
* override via `Layer.succeed(LintBatchOrdering, ...)`. Diff / staged scans
|
|
69695
69918
|
* never reach this — they pass user-scoped `includePaths` that skip discovery
|
|
69696
69919
|
* and stay in arrival order; only the full-scan branch reads it.
|
|
@@ -69785,6 +70008,7 @@ const DEFAULT_SURFACE_EXCLUDED_TAGS = {
|
|
|
69785
70008
|
score: ["design"],
|
|
69786
70009
|
ciFailure: ["design"]
|
|
69787
70010
|
};
|
|
70011
|
+
const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
69788
70012
|
const VALID_RULE_SEVERITIES = [
|
|
69789
70013
|
"error",
|
|
69790
70014
|
"warn",
|
|
@@ -69827,7 +70051,6 @@ const SURFACE_CONTROL_FIELD_NAMES = [
|
|
|
69827
70051
|
];
|
|
69828
70052
|
const isIncludedFileContext = (value) => VALID_INCLUDED_FILE_CONTEXTS.some((fileContext) => fileContext === value);
|
|
69829
70053
|
const SEVERITY_FIELD_NAMES = ["rules", "categories"];
|
|
69830
|
-
const isPlainObject$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
69831
70054
|
const formatType = (value) => typeof value === "string" ? `"${value}"` : typeof value;
|
|
69832
70055
|
const isRuleSeverity = (value) => typeof value === "string" && VALID_RULE_SEVERITIES.includes(value);
|
|
69833
70056
|
const coerceMaybeBooleanString = (fieldName, value) => {
|
|
@@ -69855,7 +70078,7 @@ const validateStringArrayField = (fieldName, value) => {
|
|
|
69855
70078
|
});
|
|
69856
70079
|
};
|
|
69857
70080
|
const validateSurfaceControls = (surface, rawControls) => {
|
|
69858
|
-
if (!
|
|
70081
|
+
if (!isRecord$1(rawControls)) {
|
|
69859
70082
|
warnConfigIssue(`config field "surfaces.${surface}" must be an object (got ${typeof rawControls}); ignoring this surface.`);
|
|
69860
70083
|
return;
|
|
69861
70084
|
}
|
|
@@ -69879,7 +70102,7 @@ const validateSurfaceControls = (surface, rawControls) => {
|
|
|
69879
70102
|
return validatedSurfaceControls;
|
|
69880
70103
|
};
|
|
69881
70104
|
const validateSurfacesField = (rawSurfaces) => {
|
|
69882
|
-
if (!
|
|
70105
|
+
if (!isRecord$1(rawSurfaces)) {
|
|
69883
70106
|
warnConfigIssue(`config field "surfaces" must be an object (got ${typeof rawSurfaces}); ignoring this field.`);
|
|
69884
70107
|
return;
|
|
69885
70108
|
}
|
|
@@ -69895,7 +70118,7 @@ const validateSurfacesField = (rawSurfaces) => {
|
|
|
69895
70118
|
return validated;
|
|
69896
70119
|
};
|
|
69897
70120
|
const validateSeverityMap = (fieldName, rawMap, keysAreCategories = false) => {
|
|
69898
|
-
if (!
|
|
70121
|
+
if (!isRecord$1(rawMap)) {
|
|
69899
70122
|
warnConfigIssue(`config field "${fieldName}" must be an object (got ${typeof rawMap}); ignoring this field.`);
|
|
69900
70123
|
return;
|
|
69901
70124
|
}
|
|
@@ -70256,6 +70479,10 @@ const dedupeRelatedDiagnostics = (diagnostics) => {
|
|
|
70256
70479
|
}
|
|
70257
70480
|
return uniqueDiagnostics;
|
|
70258
70481
|
};
|
|
70482
|
+
const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
|
|
70483
|
+
const relativePath = Path.relative(parentAbsolutePath, childAbsolutePath);
|
|
70484
|
+
return Boolean(relativePath) && !relativePath.startsWith("..") && !Path.isAbsolute(relativePath);
|
|
70485
|
+
};
|
|
70259
70486
|
const compareStrings = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
70260
70487
|
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
70488
|
const getDirectDependencyNames = (packageJson) => new Set([...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.devDependencies ?? {})]);
|
|
@@ -71832,7 +72059,7 @@ const readScannedFile = (candidate) => {
|
|
|
71832
72059
|
return null;
|
|
71833
72060
|
}
|
|
71834
72061
|
};
|
|
71835
|
-
function* collectSecurityScanFiles(rootDirectory) {
|
|
72062
|
+
function* collectSecurityScanFiles(rootDirectory, excludedDirectories = /* @__PURE__ */ new Set()) {
|
|
71836
72063
|
const priorityCandidates = [];
|
|
71837
72064
|
const artifactCandidates = [];
|
|
71838
72065
|
const otherCandidates = [];
|
|
@@ -71847,7 +72074,7 @@ function* collectSecurityScanFiles(rootDirectory) {
|
|
|
71847
72074
|
for (const entry of readDirectoryEntries(current.absolutePath)) {
|
|
71848
72075
|
const absolutePath = Path.join(current.absolutePath, entry.name);
|
|
71849
72076
|
if (entry.isDirectory()) {
|
|
71850
|
-
if (!SKIPPED_DIRECTORY_NAMES.has(entry.name)) stack.push({
|
|
72077
|
+
if (!SKIPPED_DIRECTORY_NAMES.has(entry.name) && !excludedDirectories.has(Path.resolve(absolutePath))) stack.push({
|
|
71851
72078
|
absolutePath,
|
|
71852
72079
|
depth: current.depth + 1
|
|
71853
72080
|
});
|
|
@@ -72028,10 +72255,8 @@ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledWhe
|
|
|
72028
72255
|
}
|
|
72029
72256
|
return true;
|
|
72030
72257
|
};
|
|
72258
|
+
const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
|
|
72031
72259
|
const shouldEnableRuleByDefaultStatus = (options) => options.defaultEnabled !== false || options.hasExplicitOverride === true || options.includeTagDefaults && options.hasIncludedTags;
|
|
72032
|
-
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
72033
|
-
setImmediate(resolve);
|
|
72034
|
-
});
|
|
72035
72260
|
const createSecurityScanSession = (rootDirectory, options) => {
|
|
72036
72261
|
const capabilities = options.project ? getCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
72037
72262
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
@@ -72084,19 +72309,35 @@ const createSecurityScanSession = (rootDirectory, options) => {
|
|
|
72084
72309
|
};
|
|
72085
72310
|
};
|
|
72086
72311
|
const checkSecurityScanCooperative = async (rootDirectory, options = {}) => {
|
|
72312
|
+
const throwIfScanAborted = () => {
|
|
72313
|
+
options.signal?.throwIfAborted();
|
|
72314
|
+
};
|
|
72315
|
+
const didReachDeadline = () => {
|
|
72316
|
+
const hasDeadlineElapsed = options.deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(options.deadlineEpochMs) === 0;
|
|
72317
|
+
if (hasDeadlineElapsed) options.onDeadlineExceeded?.();
|
|
72318
|
+
return hasDeadlineElapsed;
|
|
72319
|
+
};
|
|
72320
|
+
throwIfScanAborted();
|
|
72087
72321
|
const session = createSecurityScanSession(rootDirectory, options);
|
|
72088
72322
|
if (session === null) return [];
|
|
72323
|
+
if (didReachDeadline()) return [];
|
|
72089
72324
|
let sliceStartedAt = performance.now();
|
|
72090
|
-
for (const file of collectSecurityScanFiles(rootDirectory)) {
|
|
72325
|
+
for (const file of collectSecurityScanFiles(rootDirectory, options.excludedDirectories)) {
|
|
72326
|
+
throwIfScanAborted();
|
|
72327
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
72091
72328
|
if (file === null) {
|
|
72092
72329
|
if (performance.now() - sliceStartedAt >= 12) {
|
|
72093
72330
|
await yieldToEventLoop();
|
|
72331
|
+
throwIfScanAborted();
|
|
72332
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
72094
72333
|
sliceStartedAt = performance.now();
|
|
72095
72334
|
}
|
|
72096
72335
|
continue;
|
|
72097
72336
|
}
|
|
72098
72337
|
for (const _ruleStep of session.scanFileByRule(file)) if (performance.now() - sliceStartedAt >= 12) {
|
|
72099
72338
|
await yieldToEventLoop();
|
|
72339
|
+
throwIfScanAborted();
|
|
72340
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
72100
72341
|
sliceStartedAt = performance.now();
|
|
72101
72342
|
}
|
|
72102
72343
|
}
|
|
@@ -72275,25 +72516,18 @@ const collectIgnorePatterns = (rootDirectory) => {
|
|
|
72275
72516
|
cachedPatternsByRoot.set(rootDirectory, patterns);
|
|
72276
72517
|
return patterns;
|
|
72277
72518
|
};
|
|
72278
|
-
const
|
|
72279
|
-
const KNIP_JSON_FILENAME = "knip.json";
|
|
72280
|
-
const readJsonFileSafe = (filePath) => {
|
|
72281
|
-
let rawContents;
|
|
72282
|
-
try {
|
|
72283
|
-
rawContents = fs.readFileSync(filePath, "utf-8");
|
|
72284
|
-
} catch {
|
|
72285
|
-
return null;
|
|
72286
|
-
}
|
|
72519
|
+
const failOpenReadJson = (filePath, fallback) => {
|
|
72287
72520
|
try {
|
|
72288
|
-
return JSON.parse(
|
|
72521
|
+
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
72289
72522
|
} catch {
|
|
72290
|
-
return
|
|
72523
|
+
return fallback;
|
|
72291
72524
|
}
|
|
72292
72525
|
};
|
|
72526
|
+
const KNIP_JSON_FILENAME = "knip.json";
|
|
72293
72527
|
const readKnipConfig = (rootDirectory) => {
|
|
72294
|
-
const knipJson =
|
|
72528
|
+
const knipJson = failOpenReadJson(path.join(rootDirectory, KNIP_JSON_FILENAME), null);
|
|
72295
72529
|
if (isRecord$1(knipJson)) return knipJson;
|
|
72296
|
-
const packageJson =
|
|
72530
|
+
const packageJson = failOpenReadJson(path.join(rootDirectory, "package.json"), null);
|
|
72297
72531
|
const packageKnipConfig = isRecord$1(packageJson) ? packageJson.knip : null;
|
|
72298
72532
|
return isRecord$1(packageKnipConfig) ? packageKnipConfig : null;
|
|
72299
72533
|
};
|
|
@@ -72341,13 +72575,6 @@ const atomicWriteJson = (filePath, value) => {
|
|
|
72341
72575
|
return;
|
|
72342
72576
|
}
|
|
72343
72577
|
};
|
|
72344
|
-
const failOpenReadJson = (filePath, fallback) => {
|
|
72345
|
-
try {
|
|
72346
|
-
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
72347
|
-
} catch {
|
|
72348
|
-
return fallback;
|
|
72349
|
-
}
|
|
72350
|
-
};
|
|
72351
72578
|
const hashFileContents = (filePath) => {
|
|
72352
72579
|
try {
|
|
72353
72580
|
return crypto.createHash("sha1").update(NFS.readFileSync(filePath)).digest("hex");
|
|
@@ -73026,7 +73253,6 @@ const isDiagnosticOnSurface = (diagnostic, surface, config) => {
|
|
|
73026
73253
|
return true;
|
|
73027
73254
|
};
|
|
73028
73255
|
const filterDiagnosticsForSurface = (diagnostics, surface, config) => diagnostics.filter((diagnostic) => isDiagnosticOnSurface(diagnostic, surface, config));
|
|
73029
|
-
const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
|
|
73030
73256
|
/**
|
|
73031
73257
|
* Budget for the dead-code phase, scaled to the work. deslop's graph build is
|
|
73032
73258
|
* CPU-bound and roughly linear in file count, so a fixed 120s cap is too tight
|
|
@@ -73034,25 +73260,28 @@ const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs
|
|
|
73034
73260
|
* tipped over by any concurrent load — silently dropping every dead-code
|
|
73035
73261
|
* finding. Scaling the budget with file count (and inversely with the core
|
|
73036
73262
|
* 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.
|
|
73263
|
+
* reclaims a genuinely wedged worker.
|
|
73039
73264
|
*/
|
|
73040
73265
|
const resolveDeadCodeTimeout = (input) => {
|
|
73041
73266
|
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
|
-
};
|
|
73267
|
+
return { workerTimeoutMs: Math.min(DEAD_CODE_TIMEOUT_CEILING_MS, Math.max(DEAD_CODE_WORKER_TIMEOUT_MS, Math.ceil(input.sourceFileCount * 30 * coreShareFactor))) };
|
|
73047
73268
|
};
|
|
73048
|
-
const resolveLintIncludePaths = (rootDirectory, userConfig) => {
|
|
73269
|
+
const resolveLintIncludePaths = (rootDirectory, userConfig, sourceFiles) => {
|
|
73049
73270
|
if (!Array.isArray(userConfig?.ignore?.files) || userConfig.ignore.files.length === 0) return;
|
|
73050
73271
|
const ignoredPatterns = compileIgnoredFilePatterns(userConfig);
|
|
73051
|
-
return listSourceFiles(rootDirectory).filter((filePath) => {
|
|
73272
|
+
return (sourceFiles ?? listSourceFiles(rootDirectory)).filter((filePath) => {
|
|
73052
73273
|
if (!isLintableSourceFile(filePath)) return false;
|
|
73053
73274
|
return !isFileIgnoredByPatterns(filePath, rootDirectory, ignoredPatterns);
|
|
73054
73275
|
});
|
|
73055
73276
|
};
|
|
73277
|
+
const filterPathsOutsideDirectories = (input) => {
|
|
73278
|
+
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}/`)));
|
|
73279
|
+
if (excludedPrefixes.length === 0) return [...input.relativePaths];
|
|
73280
|
+
return input.relativePaths.filter((relativePath) => {
|
|
73281
|
+
const normalizedRelativePath = relativePath.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
73282
|
+
return !excludedPrefixes.some((excludedPrefix) => normalizedRelativePath === excludedPrefix || normalizedRelativePath.startsWith(`${excludedPrefix}/`));
|
|
73283
|
+
});
|
|
73284
|
+
};
|
|
73056
73285
|
var Config = class Config extends Service()("react-doctor/Config") {
|
|
73057
73286
|
static layerNode = effect(Config, gen(function* () {
|
|
73058
73287
|
const cache = yield* make$3({
|
|
@@ -73114,6 +73343,7 @@ var Files = class Files extends Service()("react-doctor/Files") {
|
|
|
73114
73343
|
static layerNode = succeed$3(Files, Files.of({
|
|
73115
73344
|
readLines: (input) => sync(() => createNodeReadFileLinesSync(input.rootDirectory)(input.filePath)),
|
|
73116
73345
|
listSourceFiles: (rootDirectory) => sync(() => listSourceFiles(rootDirectory)),
|
|
73346
|
+
listSourceFilesCooperative: (input) => promise(() => listSourceFilesCooperative(input.rootDirectory, input.signal)),
|
|
73117
73347
|
isFile: (filePath) => sync(() => isFile(filePath)),
|
|
73118
73348
|
isDirectory: (filePath) => sync(() => isDirectory(filePath))
|
|
73119
73349
|
}));
|
|
@@ -73140,6 +73370,16 @@ var Files = class Files extends Service()("react-doctor/Files") {
|
|
|
73140
73370
|
}
|
|
73141
73371
|
return files;
|
|
73142
73372
|
}),
|
|
73373
|
+
listSourceFilesCooperative: (input) => sync(() => {
|
|
73374
|
+
const rootDirectory = input.rootDirectory;
|
|
73375
|
+
const prefix = rootDirectory.endsWith("/") ? rootDirectory : `${rootDirectory}/`;
|
|
73376
|
+
const files = [];
|
|
73377
|
+
for (const absolute of tree.keys()) {
|
|
73378
|
+
if (!absolute.startsWith(prefix)) continue;
|
|
73379
|
+
files.push(absolute.slice(prefix.length));
|
|
73380
|
+
}
|
|
73381
|
+
return files;
|
|
73382
|
+
}),
|
|
73143
73383
|
isFile: (filePath) => sync(() => tree.has(filePath)),
|
|
73144
73384
|
isDirectory: (filePath) => sync(() => {
|
|
73145
73385
|
const prefix = filePath.endsWith("/") ? filePath : `${filePath}/`;
|
|
@@ -73715,7 +73955,7 @@ const batchIncludePaths = (baseArgs, includePaths) => {
|
|
|
73715
73955
|
for (const filePath of includePaths) {
|
|
73716
73956
|
const entryLength = filePath.length + 1;
|
|
73717
73957
|
const exceedsArgLength = currentBatch.length > 0 && currentBatchLength + entryLength > 24e3;
|
|
73718
|
-
const exceedsFileCount = currentBatch.length >=
|
|
73958
|
+
const exceedsFileCount = currentBatch.length >= 200;
|
|
73719
73959
|
if (exceedsArgLength || exceedsFileCount) {
|
|
73720
73960
|
batches.push(currentBatch);
|
|
73721
73961
|
currentBatch = [];
|
|
@@ -74062,7 +74302,7 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
74062
74302
|
}
|
|
74063
74303
|
return enabled;
|
|
74064
74304
|
};
|
|
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 }) => {
|
|
74305
|
+
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
74306
|
const hasIncludedTags = includedTags.size > 0;
|
|
74067
74307
|
const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" || hasIncludedTags ? null : resolveReactHooksJsPlugin(project.hasReactCompiler || project.hasReactCompilerLintPlugin === true, customRulesOnly);
|
|
74068
74308
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
@@ -74126,7 +74366,8 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
74126
74366
|
}))
|
|
74127
74367
|
} : {},
|
|
74128
74368
|
...project.shopifyFlashListMajorVersion !== null ? { shopifyFlashListMajorVersion: project.shopifyFlashListMajorVersion } : {},
|
|
74129
|
-
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {}
|
|
74369
|
+
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {},
|
|
74370
|
+
...projectIndexModuleSources !== void 0 ? { projectIndexModuleSources: [...projectIndexModuleSources] } : {}
|
|
74130
74371
|
} },
|
|
74131
74372
|
rules: {
|
|
74132
74373
|
...reactCompilerRules,
|
|
@@ -74375,11 +74616,17 @@ const collectUnpluginAutoImportGlobalScopes = ({ rootDirectory, candidateFiles }
|
|
|
74375
74616
|
return scopes.some((scope) => scope.names.length > 0) ? scopes : [];
|
|
74376
74617
|
};
|
|
74377
74618
|
const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
|
|
74619
|
+
const decodeDiagnostic = (raw) => {
|
|
74620
|
+
const diagnostic = validateDiagnostic$1(raw);
|
|
74621
|
+
return {
|
|
74622
|
+
...diagnostic,
|
|
74623
|
+
relatedLocations: diagnostic.relatedLocations?.map((location) => ({ ...location }))
|
|
74624
|
+
};
|
|
74625
|
+
};
|
|
74378
74626
|
const decodeFileDiagnostics = (raw) => {
|
|
74379
74627
|
if (!Array.isArray(raw)) return null;
|
|
74380
74628
|
try {
|
|
74381
|
-
|
|
74382
|
-
return raw;
|
|
74629
|
+
return raw.map((entry) => decodeDiagnostic(entry));
|
|
74383
74630
|
} catch {
|
|
74384
74631
|
return null;
|
|
74385
74632
|
}
|
|
@@ -75611,6 +75858,12 @@ const buildProfiledNodeArguments = (input) => [
|
|
|
75611
75858
|
...input.heapProfileDirectory ? ["--heap-prof", `--heap-prof-dir=${input.heapProfileDirectory}`] : [],
|
|
75612
75859
|
...input.argumentsList
|
|
75613
75860
|
];
|
|
75861
|
+
const lowerChildProcessPriority = (processId) => {
|
|
75862
|
+
if (processId === void 0) return;
|
|
75863
|
+
try {
|
|
75864
|
+
OS.setPriority(processId, OS.constants.priority.PRIORITY_BELOW_NORMAL);
|
|
75865
|
+
} catch {}
|
|
75866
|
+
};
|
|
75614
75867
|
const SANITIZED_ENV = buildOxlintChildEnv(process.env);
|
|
75615
75868
|
/**
|
|
75616
75869
|
* Spawn one oxlint subprocess with hard ceilings on wall time and
|
|
@@ -75649,6 +75902,7 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
|
|
|
75649
75902
|
"pipe"
|
|
75650
75903
|
]
|
|
75651
75904
|
});
|
|
75905
|
+
lowerChildProcessPriority(child.pid);
|
|
75652
75906
|
const onAbort = () => {
|
|
75653
75907
|
child.kill("SIGKILL");
|
|
75654
75908
|
reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) }));
|
|
@@ -75771,7 +76025,7 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
75771
76025
|
* loop with a slimmer config in that case.
|
|
75772
76026
|
*/
|
|
75773
76027
|
const spawnLintBatches = async (input) => {
|
|
75774
|
-
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, sourceMapByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth =
|
|
76028
|
+
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, sourceMapByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 9, signal, deadlineEpochMs } = input;
|
|
75775
76029
|
const resolveSourcePath = (filePath) => {
|
|
75776
76030
|
const absoluteFilePath = Path.isAbsolute(filePath) ? filePath : Path.resolve(rootDirectory, filePath);
|
|
75777
76031
|
return sourcePathByLintPath?.get(Path.normalize(absoluteFilePath)) ?? filePath;
|
|
@@ -75779,6 +76033,12 @@ const spawnLintBatches = async (input) => {
|
|
|
75779
76033
|
const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
|
|
75780
76034
|
let rescueDeadlineEpochMs;
|
|
75781
76035
|
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0 || rescueDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(rescueDeadlineEpochMs) === 0;
|
|
76036
|
+
const resolveSpawnTimeoutMs = () => {
|
|
76037
|
+
const deadlineBudgets = [deadlineEpochMs, rescueDeadlineEpochMs].filter((deadline) => deadline !== void 0).map(remainingDeadlineBudgetMs);
|
|
76038
|
+
if (deadlineBudgets.length === 0) return spawnTimeoutMs;
|
|
76039
|
+
const remainingDeadlineMs = Math.min(...deadlineBudgets);
|
|
76040
|
+
return spawnTimeoutMs === void 0 ? remainingDeadlineMs : Math.min(spawnTimeoutMs, remainingDeadlineMs);
|
|
76041
|
+
};
|
|
75782
76042
|
const runBatchPass = async (concurrency, passBatches, passOnFileProgress) => {
|
|
75783
76043
|
const totalFileCount = passBatches.reduce((sum, batch) => sum + batch.length, 0);
|
|
75784
76044
|
const allDiagnostics = [];
|
|
@@ -75796,12 +76056,13 @@ const spawnLintBatches = async (input) => {
|
|
|
75796
76056
|
const batchArgs = [...baseArgs, ...batch];
|
|
75797
76057
|
try {
|
|
75798
76058
|
const spawnBatch = () => {
|
|
75799
|
-
|
|
76059
|
+
const effectiveSpawnTimeoutMs = resolveSpawnTimeoutMs();
|
|
76060
|
+
if (effectiveSpawnTimeoutMs === 0) {
|
|
75800
76061
|
deadlineSkippedFiles.push(...batch);
|
|
75801
76062
|
batchState.deadlineSkippedFileCount += batch.length;
|
|
75802
76063
|
return Promise.resolve(null);
|
|
75803
76064
|
}
|
|
75804
|
-
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath,
|
|
76065
|
+
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, effectiveSpawnTimeoutMs, outputMaxBytes, signal, () => {
|
|
75805
76066
|
if (batchState.didStart) return;
|
|
75806
76067
|
batchState.didStart = true;
|
|
75807
76068
|
startedFileCount += batchState.initialFileCount;
|
|
@@ -75956,11 +76217,36 @@ const validateRuleRegistration = () => {
|
|
|
75956
76217
|
].filter((entry) => entry !== null).join("; ");
|
|
75957
76218
|
console.warn(`[react-doctor] rule-registration drift: ${detail}`);
|
|
75958
76219
|
};
|
|
76220
|
+
const PROJECT_INDEX_MODULE_SOURCES = [
|
|
76221
|
+
"next/og",
|
|
76222
|
+
"@vercel/og",
|
|
76223
|
+
"satori",
|
|
76224
|
+
"remotion"
|
|
76225
|
+
];
|
|
76226
|
+
const collectProjectIndexModuleSources = async (rootDirectory, candidateFiles) => {
|
|
76227
|
+
const foundModuleSources = /* @__PURE__ */ new Set();
|
|
76228
|
+
let sliceStartedAt = performance.now();
|
|
76229
|
+
for (const candidateFile of candidateFiles) {
|
|
76230
|
+
let sourceBuffer;
|
|
76231
|
+
try {
|
|
76232
|
+
sourceBuffer = NFS.readFileSync(Path.isAbsolute(candidateFile) ? candidateFile : Path.resolve(rootDirectory, candidateFile));
|
|
76233
|
+
} catch {
|
|
76234
|
+
continue;
|
|
76235
|
+
}
|
|
76236
|
+
for (const moduleSource of PROJECT_INDEX_MODULE_SOURCES) if (sourceBuffer.includes(`"${moduleSource}"`) || sourceBuffer.includes(`'${moduleSource}'`)) foundModuleSources.add(moduleSource);
|
|
76237
|
+
if (foundModuleSources.size === PROJECT_INDEX_MODULE_SOURCES.length) break;
|
|
76238
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
76239
|
+
await yieldToEventLoop();
|
|
76240
|
+
sliceStartedAt = performance.now();
|
|
76241
|
+
}
|
|
76242
|
+
}
|
|
76243
|
+
return [...foundModuleSources].sort();
|
|
76244
|
+
};
|
|
75959
76245
|
/**
|
|
75960
76246
|
* Balanced LPT (longest-processing-time-first) lint batch planner. Where
|
|
75961
|
-
* `batchIncludePaths` greedily fills
|
|
75962
|
-
* remainder batch
|
|
75963
|
-
*
|
|
76247
|
+
* `batchIncludePaths` greedily fills fixed-size batches and leaves a small
|
|
76248
|
+
* remainder batch, so the parallel pool's wall clock is set by whichever
|
|
76249
|
+
* full chunk happened to collect the most work while
|
|
75964
76250
|
* the remainder's worker idles), this planner keeps the SAME mandatory batch
|
|
75965
76251
|
* count — `ceil(files / OXLINT_MAX_FILES_PER_BATCH)`, the JS-plugin
|
|
75966
76252
|
* perf-cliff / native-binding SIGABRT guard — and assigns files largest-first,
|
|
@@ -75989,7 +76275,7 @@ const validateRuleRegistration = () => {
|
|
|
75989
76275
|
*/
|
|
75990
76276
|
const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
75991
76277
|
if (files.length === 0) return [];
|
|
75992
|
-
const batchCount = Math.ceil(files.length /
|
|
76278
|
+
const batchCount = Math.ceil(files.length / 200);
|
|
75993
76279
|
const maxFilesPerBatch = Math.ceil(files.length / batchCount);
|
|
75994
76280
|
const baseArgsLengthChars = estimateArgsLength(baseArgs);
|
|
75995
76281
|
const descendingSizeIndexes = [...files.keys()].sort((leftIndex, rightIndex) => (sizeByFile.get(files[rightIndex]) ?? 0) - (sizeByFile.get(files[leftIndex]) ?? 0));
|
|
@@ -76518,6 +76804,7 @@ const runOxlint = async (options) => {
|
|
|
76518
76804
|
await yieldToEventLoop();
|
|
76519
76805
|
const sizedScanFiles = includePaths === void 0 ? listSourceFilesWithSize(rootDirectory) : null;
|
|
76520
76806
|
const candidateFiles = includePaths !== void 0 ? includePaths : (sizedScanFiles ?? []).map((entry) => entry.path);
|
|
76807
|
+
const projectIndexModuleSources = includePaths === void 0 && options.deadlineEpochMs === void 0 ? await collectProjectIndexModuleSources(rootDirectory, candidateFiles) : void 0;
|
|
76521
76808
|
unpluginAutoImportGlobalScopes = collectUnpluginAutoImportGlobalScopes({
|
|
76522
76809
|
rootDirectory,
|
|
76523
76810
|
candidateFiles
|
|
@@ -76540,6 +76827,7 @@ const runOxlint = async (options) => {
|
|
|
76540
76827
|
runtimeGlobals,
|
|
76541
76828
|
unpluginAutoImportGlobalScopes,
|
|
76542
76829
|
serverAuthFunctionNames,
|
|
76830
|
+
projectIndexModuleSources,
|
|
76543
76831
|
severityControls,
|
|
76544
76832
|
userPlugins,
|
|
76545
76833
|
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
|
|
@@ -77007,10 +77295,10 @@ const translateProjectInfoError = (cause, directory) => {
|
|
|
77007
77295
|
}) });
|
|
77008
77296
|
};
|
|
77009
77297
|
var Project = class Project extends Service()("react-doctor/Project") {
|
|
77010
|
-
static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (
|
|
77298
|
+
static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (input) {
|
|
77011
77299
|
return yield* try_({
|
|
77012
|
-
try: () => discoverProject(directory),
|
|
77013
|
-
catch: (cause) => translateProjectInfoError(cause, directory)
|
|
77300
|
+
try: () => discoverProject(input.directory, { sourceFileCount: input.sourceFileCount }),
|
|
77301
|
+
catch: (cause) => translateProjectInfoError(cause, input.directory)
|
|
77014
77302
|
});
|
|
77015
77303
|
}) }));
|
|
77016
77304
|
static layerOf = (projectInfo) => succeed$3(Project, Project.of({ discover: () => succeed$2(projectInfo) }));
|
|
@@ -77595,9 +77883,10 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
77595
77883
|
* outage.
|
|
77596
77884
|
*/
|
|
77597
77885
|
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
|
-
|
|
77886
|
+
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (configuredTimeoutMs) => checkSupplyChain({
|
|
77887
|
+
rootDirectory: input.rootDirectory,
|
|
77888
|
+
userConfig: input.userConfig,
|
|
77889
|
+
totalTimeoutMs: input.timeoutMs ?? configuredTimeoutMs
|
|
77601
77890
|
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), withSpan("SupplyChain.run"))) }));
|
|
77602
77891
|
static layerOf = (diagnostics) => succeed$3(SupplyChain, SupplyChain.of({ run: () => fromIterable$1(diagnostics) }));
|
|
77603
77892
|
};
|
|
@@ -77632,10 +77921,6 @@ const NO_HOOKS = {
|
|
|
77632
77921
|
beforeLint: () => void_,
|
|
77633
77922
|
afterLint: () => void_
|
|
77634
77923
|
};
|
|
77635
|
-
const filterMapNullable = (transform) => fromPredicateOption((value) => {
|
|
77636
|
-
const result = transform(value);
|
|
77637
|
-
return result === null ? none() : some(result);
|
|
77638
|
-
});
|
|
77639
77924
|
const fileReader = (filesService, rootDirectory) => (filePath) => {
|
|
77640
77925
|
const lines = runSync(filesService.readLines({
|
|
77641
77926
|
filePath,
|
|
@@ -77659,7 +77944,7 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
77659
77944
|
* The GitHub viewer-permission lookup is forked onto a background
|
|
77660
77945
|
* fiber here and joined late (it feeds score metadata, not
|
|
77661
77946
|
* diagnostics).
|
|
77662
|
-
* 2. beforeLint hook (e.g. CLI
|
|
77947
|
+
* 2. beforeLint hook (e.g. CLI records project telemetry)
|
|
77663
77948
|
* 3. environment checks (reduced-motion + pnpm hardening +
|
|
77664
77949
|
* expo/react-native), collected synchronously. The heavier
|
|
77665
77950
|
* content-regex security scan is forked instead (like supply-chain
|
|
@@ -77672,13 +77957,11 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
77672
77957
|
* `SupplyChainOverlapTimeoutMs` (measured from fork) so a hung
|
|
77673
77958
|
* socket can't drag out its join; on timeout it fails open to no
|
|
77674
77959
|
* 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
|
|
77960
|
+
* 5. Linter.run runs; DeadCode.run forks only when
|
|
77961
|
+
* REACT_DOCTOR_DEAD_CODE_OVERLAP=on. The default "auto" and explicit
|
|
77962
|
+
* "off" paths stay sequential. The fiber is joined (or interrupted,
|
|
77963
|
+
* SIGKILLing its worker, on lint failure) before diagnostics are
|
|
77964
|
+
* concatenated. The afterLint hook
|
|
77682
77965
|
* fires between lint and dead-code. Progress spinner labels AND the
|
|
77683
77966
|
* final diagnostic / score order stay independent of execution
|
|
77684
77967
|
* order, so terminal output is identical either way; supply-chain
|
|
@@ -77713,8 +77996,28 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77713
77996
|
const partialFailuresRef = yield* LintPartialFailures;
|
|
77714
77997
|
const resolvedConfig = yield* configService.resolve(input.directory);
|
|
77715
77998
|
const scanDirectory = resolvedConfig.resolvedDirectory;
|
|
77716
|
-
const
|
|
77999
|
+
const excludedProjectDirectories = (input.excludedProjectDirectories ?? []).map((excludedDirectory) => Path.resolve(excludedDirectory)).filter((excludedDirectory) => isPathInsideDirectory(excludedDirectory, scanDirectory));
|
|
78000
|
+
const excludedProjectRelativePaths = excludedProjectDirectories.map((excludedDirectory) => Path.relative(scanDirectory, excludedDirectory).replaceAll("\\", "/"));
|
|
78001
|
+
const isExcludedProjectDiagnostic = (diagnostic) => {
|
|
78002
|
+
const relativeFilePath = toNormalizedRelativePath(diagnostic.filePath, scanDirectory);
|
|
78003
|
+
return excludedProjectRelativePaths.some((excludedRelativePath) => relativeFilePath === excludedRelativePath || relativeFilePath.startsWith(`${excludedRelativePath}/`));
|
|
78004
|
+
};
|
|
78005
|
+
const precomputedSourceFiles = input.suppressScanSummary === true || excludedProjectDirectories.length > 0 ? yield* filesService.listSourceFilesCooperative({
|
|
78006
|
+
rootDirectory: scanDirectory,
|
|
78007
|
+
signal: input.signal
|
|
78008
|
+
}) : null;
|
|
78009
|
+
const includedPrecomputedSourceFiles = precomputedSourceFiles !== null && excludedProjectDirectories.length > 0 ? filterPathsOutsideDirectories({
|
|
78010
|
+
rootDirectory: scanDirectory,
|
|
78011
|
+
relativePaths: precomputedSourceFiles,
|
|
78012
|
+
excludedDirectories: excludedProjectDirectories
|
|
78013
|
+
}) : precomputedSourceFiles;
|
|
78014
|
+
const sourceFileCount = excludedProjectDirectories.length > 0 ? includedPrecomputedSourceFiles?.length : input.precomputedSourceFileCount ?? includedPrecomputedSourceFiles?.length;
|
|
78015
|
+
const project = yield* projectService.discover({
|
|
78016
|
+
directory: scanDirectory,
|
|
78017
|
+
sourceFileCount
|
|
78018
|
+
});
|
|
77717
78019
|
if (!isAnalyzableProject(project)) return yield* new ReactDoctorError({ reason: new NoReactDependency({ directory: scanDirectory }) });
|
|
78020
|
+
const deadCodeSourceFileCount = input.retainExcludedProjectDeadCodeDiagnostics === true && precomputedSourceFiles !== null ? precomputedSourceFiles.length : project.sourceFileCount;
|
|
77718
78021
|
const [repo, sha, defaultBranch] = yield* all([
|
|
77719
78022
|
gitService.githubRepo(scanDirectory).pipe(orElseSucceed(() => null)),
|
|
77720
78023
|
gitService.headSha(scanDirectory).pipe(orElseSucceed(() => null)),
|
|
@@ -77725,11 +78028,16 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77725
78028
|
directory: scanDirectory,
|
|
77726
78029
|
repo
|
|
77727
78030
|
}).pipe(orElseSucceed(() => null)) : succeed$2(null));
|
|
77728
|
-
|
|
77729
|
-
|
|
78031
|
+
let lintIncludePaths = (input.skipExplicitIncludePathFilter ? input.includePaths.length > 0 ? [...input.includePaths] : void 0 : computeExplicitLintIncludePaths([...input.includePaths])) ?? resolveLintIncludePaths(scanDirectory, resolvedConfig.config, includedPrecomputedSourceFiles ?? void 0);
|
|
78032
|
+
if (excludedProjectDirectories.length > 0) lintIncludePaths = filterPathsOutsideDirectories({
|
|
78033
|
+
rootDirectory: scanDirectory,
|
|
78034
|
+
relativePaths: lintIncludePaths ?? includedPrecomputedSourceFiles ?? (yield* filesService.listSourceFiles(scanDirectory)),
|
|
78035
|
+
excludedDirectories: excludedProjectDirectories
|
|
78036
|
+
});
|
|
78037
|
+
const fallbackScannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? includedPrecomputedSourceFiles ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
|
|
77730
78038
|
const beforeLint = hooks.beforeLint ?? NO_HOOKS.beforeLint;
|
|
77731
78039
|
const afterLint = hooks.afterLint ?? NO_HOOKS.afterLint;
|
|
77732
|
-
yield* beforeLint(project
|
|
78040
|
+
yield* beforeLint(project);
|
|
77733
78041
|
const isDiffMode = input.includePaths.length > 0;
|
|
77734
78042
|
const showWarnings = input.warnings ?? resolvedConfig.config?.warnings ?? true;
|
|
77735
78043
|
const transform = buildDiagnosticPipeline({
|
|
@@ -77739,7 +78047,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77739
78047
|
respectInlineDisables: input.respectInlineDisables,
|
|
77740
78048
|
showWarnings
|
|
77741
78049
|
});
|
|
77742
|
-
const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(
|
|
78050
|
+
const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(fromPredicateOption((diagnostic) => {
|
|
78051
|
+
const filteredDiagnostic = transform.apply(diagnostic);
|
|
78052
|
+
return filteredDiagnostic === null ? none() : some(filteredDiagnostic);
|
|
78053
|
+
})));
|
|
77743
78054
|
const applyPerElementPipeline = (rawStream) => filterPerElementPipeline(rawStream).pipe(tap((diagnostic) => reporterService.emit(diagnostic)));
|
|
77744
78055
|
const envCollected = yield* runCollect(applyPerElementPipeline(fromIterable$1(isDiffMode ? [] : [
|
|
77745
78056
|
...checkReducedMotion(scanDirectory),
|
|
@@ -77749,17 +78060,31 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77749
78060
|
...checkReactNativeProject(scanDirectory, project)
|
|
77750
78061
|
])));
|
|
77751
78062
|
const securityScanFailedRef = yield* make$13(false);
|
|
77752
|
-
|
|
78063
|
+
let didSecurityScanReachDeadline = false;
|
|
78064
|
+
const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(tryPromise((signal) => checkSecurityScanCooperative(scanDirectory, {
|
|
77753
78065
|
project,
|
|
77754
78066
|
ignoredTags: input.ignoredTags,
|
|
77755
78067
|
includedTags: input.includedTags,
|
|
77756
|
-
includeTagDefaults: input.includeTagDefaults
|
|
78068
|
+
includeTagDefaults: input.includeTagDefaults,
|
|
78069
|
+
excludedDirectories: new Set(excludedProjectDirectories),
|
|
78070
|
+
deadlineEpochMs: input.deadlineEpochMs,
|
|
78071
|
+
signal,
|
|
78072
|
+
onDeadlineExceeded: () => {
|
|
78073
|
+
didSecurityScanReachDeadline = true;
|
|
78074
|
+
}
|
|
77757
78075
|
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), catch_$1(() => set(securityScanFailedRef, true).pipe(as(empty$4))))))).pipe(withSpan("SecurityScan.run")));
|
|
78076
|
+
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
78077
|
+
const capOptionalToDeadline = (phaseTimeoutMs) => {
|
|
78078
|
+
if (input.deadlineEpochMs === void 0) return phaseTimeoutMs;
|
|
78079
|
+
const remainingBudgetMs = remainingDeadlineBudgetMs(input.deadlineEpochMs);
|
|
78080
|
+
return phaseTimeoutMs === null ? remainingBudgetMs : Math.min(phaseTimeoutMs, remainingBudgetMs);
|
|
78081
|
+
};
|
|
77758
78082
|
const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
|
|
77759
|
-
const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
|
|
78083
|
+
const supplyChainOverlapTimeout = capToDeadline(yield* SupplyChainOverlapTimeoutMs);
|
|
77760
78084
|
const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
|
|
77761
78085
|
rootDirectory: scanDirectory,
|
|
77762
|
-
userConfig: resolvedConfig.config
|
|
78086
|
+
userConfig: resolvedConfig.config,
|
|
78087
|
+
timeoutMs: supplyChainOverlapTimeout
|
|
77763
78088
|
}))).pipe(map$3((diagnostics) => ({
|
|
77764
78089
|
diagnostics,
|
|
77765
78090
|
timedOut: false
|
|
@@ -77783,46 +78108,49 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77783
78108
|
const scanConcurrency = resolveScanConcurrency(yield* OxlintConcurrency);
|
|
77784
78109
|
const lintPhaseTimeoutMs = yield* LintPhaseTimeoutMs;
|
|
77785
78110
|
const deadCodePhaseTimeoutMs = yield* DeadCodePhaseTimeoutMs;
|
|
77786
|
-
const resolveDeadCodePhaseTimeoutMs = (scaledPhaseTimeoutMs) => deadCodePhaseTimeoutMs === 15e4 ? scaledPhaseTimeoutMs : deadCodePhaseTimeoutMs;
|
|
77787
78111
|
const workerCountSuffix = scanConcurrency > 1 ? ` ${highlighter.dim(`[~${scanConcurrency} workers]`)}` : "";
|
|
77788
|
-
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
77789
78112
|
const shouldRunDeadCode = input.runDeadCode && !isDiffMode && (showWarnings || deadCodeMaySurfaceWhenWarningsHidden(resolvedConfig.config));
|
|
77790
78113
|
const deadCodeOverlapMode = yield* DeadCodeOverlap;
|
|
77791
78114
|
const shouldOverlapDeadCode = shouldRunDeadCode && deadCodeOverlapMode === "on";
|
|
77792
78115
|
const deadCodeParseConcurrency = shouldOverlapDeadCode ? Math.max(1, Math.floor(scanConcurrency * DEAD_CODE_OVERLAP_PARSE_SHARE)) : void 0;
|
|
77793
78116
|
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
|
-
|
|
78117
|
+
const buildCollectDeadCode = (deadCodeTimeout) => {
|
|
78118
|
+
const collectDeadCode = runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
78119
|
+
rootDirectory: scanDirectory,
|
|
78120
|
+
parseConcurrency: deadCodeParseConcurrency,
|
|
78121
|
+
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
|
|
78122
|
+
onCacheOutcome: (didHitCache) => {
|
|
78123
|
+
deadCodeCacheHit = didHitCache;
|
|
78124
|
+
},
|
|
78125
|
+
onSummaryCacheStats: (stats) => {
|
|
78126
|
+
deadCodeSummaryCacheHits = stats.hits;
|
|
78127
|
+
deadCodeSummaryCacheMisses = stats.misses;
|
|
78128
|
+
}
|
|
78129
|
+
}).pipe(filter((diagnostic) => input.retainExcludedProjectDeadCodeDiagnostics === true || !isExcludedProjectDiagnostic(diagnostic)), catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
78130
|
+
yield* set(deadCodeFailure, {
|
|
78131
|
+
didFail: true,
|
|
78132
|
+
reason: error.message
|
|
78133
|
+
});
|
|
78134
|
+
return empty$4;
|
|
78135
|
+
}))))));
|
|
78136
|
+
const phaseTimeoutMs = deadCodeTimeout.phaseTimeoutMs;
|
|
78137
|
+
if (phaseTimeoutMs === null) return collectDeadCode;
|
|
78138
|
+
return collectDeadCode.pipe(timeoutOption(phaseTimeoutMs), flatMap$2(match$3({
|
|
78139
|
+
onNone: () => set(deadCodeFailure, {
|
|
78140
|
+
didFail: true,
|
|
78141
|
+
reason: `Dead-code analysis exceeded ${Math.round(phaseTimeoutMs / MILLISECONDS_PER_SECOND)}s and was skipped.`
|
|
78142
|
+
}).pipe(as([])),
|
|
78143
|
+
onSome: succeed$2
|
|
78144
|
+
})));
|
|
78145
|
+
};
|
|
77818
78146
|
const overlapDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
77819
|
-
sourceFileCount:
|
|
78147
|
+
sourceFileCount: deadCodeSourceFileCount,
|
|
77820
78148
|
deadCodeConcurrency: deadCodeParseConcurrency ?? scanConcurrency,
|
|
77821
78149
|
fullConcurrency: scanConcurrency
|
|
77822
78150
|
});
|
|
77823
78151
|
const deadCodeFiber = shouldOverlapDeadCode ? yield* forkChild(buildCollectDeadCode({
|
|
77824
78152
|
workerTimeoutMs: overlapDeadCodeTimeout.workerTimeoutMs,
|
|
77825
|
-
phaseTimeoutMs:
|
|
78153
|
+
phaseTimeoutMs: capOptionalToDeadline(deadCodePhaseTimeoutMs)
|
|
77826
78154
|
})) : null;
|
|
77827
78155
|
const scanProgress = yield* progressService.start("Scanning...");
|
|
77828
78156
|
const scanStartTime = Date.now();
|
|
@@ -77873,7 +78201,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77873
78201
|
});
|
|
77874
78202
|
return empty$4;
|
|
77875
78203
|
}))));
|
|
77876
|
-
const
|
|
78204
|
+
const collectLintDiagnostics = runCollect(filterPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream));
|
|
78205
|
+
const lintCollected = dedupeRelatedDiagnostics(yield* lintPhaseTimeoutMs === null ? collectLintDiagnostics : collectLintDiagnostics.pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
|
|
77877
78206
|
onNone: () => set(lintFailure, {
|
|
77878
78207
|
didFail: true,
|
|
77879
78208
|
reason: `Lint analysis exceeded ${lintPhaseTimeoutMs / MILLISECONDS_PER_SECOND}s and was skipped.`,
|
|
@@ -77903,13 +78232,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77903
78232
|
} else {
|
|
77904
78233
|
yield* scanProgress.update(`Scanned ${scannedFilesLabel}, analyzing dead code...`);
|
|
77905
78234
|
const sequentialDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
77906
|
-
sourceFileCount: totalFileCount,
|
|
78235
|
+
sourceFileCount: input.retainExcludedProjectDeadCodeDiagnostics === true ? deadCodeSourceFileCount : totalFileCount,
|
|
77907
78236
|
deadCodeConcurrency: scanConcurrency,
|
|
77908
78237
|
fullConcurrency: scanConcurrency
|
|
77909
78238
|
});
|
|
77910
78239
|
deadCodeCollected = deadCodeFiber !== null ? yield* join(deadCodeFiber) : yield* buildCollectDeadCode({
|
|
77911
78240
|
workerTimeoutMs: sequentialDeadCodeTimeout.workerTimeoutMs,
|
|
77912
|
-
phaseTimeoutMs:
|
|
78241
|
+
phaseTimeoutMs: capOptionalToDeadline(deadCodePhaseTimeoutMs)
|
|
77913
78242
|
});
|
|
77914
78243
|
}
|
|
77915
78244
|
const deadCodeFailureState = lintFailureState.didFail ? {
|
|
@@ -77953,7 +78282,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77953
78282
|
metadata: scoreMetadata
|
|
77954
78283
|
});
|
|
77955
78284
|
const lintPartialFailures = yield* get$2(partialFailuresRef);
|
|
77956
|
-
const
|
|
78285
|
+
const didSecurityScanFail = yield* get$2(securityScanFailedRef);
|
|
78286
|
+
const securityScanFailed = didSecurityScanFail || didSecurityScanReachDeadline;
|
|
78287
|
+
let securityScanFailureReason = null;
|
|
78288
|
+
if (didSecurityScanReachDeadline) securityScanFailureReason = "Security scan reached the max scan duration; findings collected before the deadline were preserved.";
|
|
78289
|
+
else if (didSecurityScanFail) securityScanFailureReason = "Security scan failed and was skipped.";
|
|
77957
78290
|
return {
|
|
77958
78291
|
project,
|
|
77959
78292
|
userConfig: resolvedConfig.config,
|
|
@@ -77976,6 +78309,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77976
78309
|
scanConcurrency,
|
|
77977
78310
|
supplyChainOverlapTimedOut: supplyChainResult.timedOut,
|
|
77978
78311
|
securityScanFailed,
|
|
78312
|
+
securityScanFailureReason,
|
|
77979
78313
|
lintCacheHitFileCount,
|
|
77980
78314
|
lintCacheTotalFileCount,
|
|
77981
78315
|
lintSidecarReplayedFileCount,
|
|
@@ -77991,7 +78325,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77991
78325
|
"inspect.runDeadCode": input.runDeadCode,
|
|
77992
78326
|
"inspect.isCi": input.isCi,
|
|
77993
78327
|
"inspect.scoreSurface": input.scoreSurface ?? "score"
|
|
77994
|
-
} }), (scanProgram) => flatMap$2(ScanDeadlineMs, (scanDeadlineMs) =>
|
|
78328
|
+
} }), (scanProgram) => flatMap$2(ScanDeadlineMs, (scanDeadlineMs) => {
|
|
78329
|
+
if (scanDeadlineMs === null) return scanProgram;
|
|
78330
|
+
return scanProgram.pipe(timeout(scanDeadlineMs), catchTag$1("TimeoutError", () => new ReactDoctorError({ reason: new ScanDeadlineExceeded({ detail: `${scanDeadlineMs / MILLISECONDS_PER_SECOND}s elapsed` }) })));
|
|
78331
|
+
}));
|
|
77995
78332
|
const parseNodeVersion = (versionString) => {
|
|
77996
78333
|
const [major = 0, minor = 0, patch = 0] = versionString.replace(/^v/, "").trim().split(".").map(Number);
|
|
77997
78334
|
return {
|
|
@@ -78103,16 +78440,6 @@ var NodeResolver = class NodeResolver extends Service()("react-doctor/NodeResolv
|
|
|
78103
78440
|
});
|
|
78104
78441
|
};
|
|
78105
78442
|
/**
|
|
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
78443
|
* Writes a set of source files (supplied by `readContent` — e.g.
|
|
78117
78444
|
* `git show <ref>:<path>` for a baseline tree, or `git show :<path>` for the
|
|
78118
78445
|
* index) into a temp tree that mirrors the project layout, copying the
|
|
@@ -78400,6 +78727,7 @@ const buildJsonReport = (input) => {
|
|
|
78400
78727
|
directory: input.directory,
|
|
78401
78728
|
diff: toJsonDiff(input.diff),
|
|
78402
78729
|
projects,
|
|
78730
|
+
...input.skippedProjects && input.skippedProjects.length > 0 ? { skippedProjects: [...input.skippedProjects] } : {},
|
|
78403
78731
|
diagnostics: flattenedDiagnostics,
|
|
78404
78732
|
summary,
|
|
78405
78733
|
elapsedMilliseconds: input.totalElapsedMilliseconds,
|
|
@@ -78424,7 +78752,7 @@ const buildSkippedChecks = (input) => {
|
|
|
78424
78752
|
else if (input.lintPartialFailures.length > 0) skippedCheckReasons["lint:partial"] = input.lintPartialFailures.join("; ");
|
|
78425
78753
|
if (input.didDeadCodeFail && input.deadCodeFailureReason !== null) skippedCheckReasons["dead-code"] = input.deadCodeFailureReason;
|
|
78426
78754
|
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.";
|
|
78755
|
+
if (input.securityScanFailed) skippedCheckReasons["security-scan"] = input.securityScanFailureReason ?? "Security scan failed and was skipped.";
|
|
78428
78756
|
return {
|
|
78429
78757
|
skippedChecks,
|
|
78430
78758
|
skippedCheckReasons
|
|
@@ -78564,6 +78892,33 @@ const getChangedLineRanges = (input) => runPromise(gen(function* () {
|
|
|
78564
78892
|
* instead of being improvised per diagnostic.
|
|
78565
78893
|
*/
|
|
78566
78894
|
const buildRuleDocsUrl = (plugin, rule) => `${DOCS_RULES_BASE_URL}/${plugin}/${rule}`;
|
|
78895
|
+
const collectSourceFileCountsByDirectory = async (rootDirectory, projectDirectories, signal) => {
|
|
78896
|
+
const resolvedRootDirectory = Path.resolve(rootDirectory);
|
|
78897
|
+
const sourceFileCounts = /* @__PURE__ */ new Map();
|
|
78898
|
+
const projectDirectoryByRelativePath = /* @__PURE__ */ new Map();
|
|
78899
|
+
for (const projectDirectory of projectDirectories) {
|
|
78900
|
+
const resolvedProjectDirectory = Path.resolve(projectDirectory);
|
|
78901
|
+
const relativeDirectory = Path.relative(resolvedRootDirectory, resolvedProjectDirectory);
|
|
78902
|
+
if (Path.isAbsolute(relativeDirectory) || relativeDirectory.startsWith("..")) continue;
|
|
78903
|
+
sourceFileCounts.set(resolvedProjectDirectory, 0);
|
|
78904
|
+
projectDirectoryByRelativePath.set(relativeDirectory.replaceAll(Path.sep, "/"), resolvedProjectDirectory);
|
|
78905
|
+
}
|
|
78906
|
+
const sourceFilePaths = await listSourceFilesCooperative(resolvedRootDirectory, signal);
|
|
78907
|
+
for (const sourceFilePath of sourceFilePaths) {
|
|
78908
|
+
let relativeDirectory = Path.posix.dirname(sourceFilePath);
|
|
78909
|
+
while (true) {
|
|
78910
|
+
const normalizedRelativeDirectory = relativeDirectory === "." ? "" : relativeDirectory;
|
|
78911
|
+
const projectDirectory = projectDirectoryByRelativePath.get(normalizedRelativeDirectory);
|
|
78912
|
+
if (projectDirectory !== void 0) {
|
|
78913
|
+
sourceFileCounts.set(projectDirectory, (sourceFileCounts.get(projectDirectory) ?? 0) + 1);
|
|
78914
|
+
break;
|
|
78915
|
+
}
|
|
78916
|
+
if (relativeDirectory === ".") break;
|
|
78917
|
+
relativeDirectory = Path.posix.dirname(relativeDirectory);
|
|
78918
|
+
}
|
|
78919
|
+
}
|
|
78920
|
+
return sourceFileCounts;
|
|
78921
|
+
};
|
|
78567
78922
|
const computeConfigFingerprint = (projectDirectory, version) => {
|
|
78568
78923
|
const parts = [`v=${version}`];
|
|
78569
78924
|
let directory = projectDirectory;
|
|
@@ -78934,8 +79289,11 @@ const METRIC = {
|
|
|
78934
79289
|
scanPhaseDuration: "scan.phase_duration",
|
|
78935
79290
|
scanFiles: "scan.files",
|
|
78936
79291
|
scanScore: "scan.score",
|
|
79292
|
+
scanScoreRetry: "scan.score_retry",
|
|
78937
79293
|
scanClean: "scan.clean",
|
|
78938
79294
|
scanCheckSkipped: "scan.check_skipped",
|
|
79295
|
+
scanProjectSkipped: "scan.project_skipped",
|
|
79296
|
+
scanWorkspaceDeadCodeShared: "scan.workspace_deadcode_shared",
|
|
78939
79297
|
scanNoReactDetected: "scan.no_react_detected",
|
|
78940
79298
|
baselineDegraded: "baseline.degraded",
|
|
78941
79299
|
ruleFired: "rule.fired",
|
|
@@ -78966,8 +79324,10 @@ const METRIC = {
|
|
|
78966
79324
|
tuiFindingNavigated: "tui.finding_navigated",
|
|
78967
79325
|
tuiIssueStreamShown: "tui.issue_stream_shown",
|
|
78968
79326
|
tuiProjectPathContextShown: "tui.project_path_context_shown",
|
|
79327
|
+
tuiProjectSelectInlineShown: "tui.project_select_inline_shown",
|
|
78969
79328
|
tuiReportActionSelected: "tui.report_action_selected",
|
|
78970
79329
|
tuiCancelled: "tui.cancelled",
|
|
79330
|
+
tuiScanInlineShown: "tui.scan_inline_shown",
|
|
78971
79331
|
tuiStackedReportCapped: "tui.stacked_report_capped",
|
|
78972
79332
|
aiTrainingWarningShown: "ai.training.warning_shown",
|
|
78973
79333
|
jsonOutUsed: "json.out_used"
|
|
@@ -79013,7 +79373,7 @@ const makeNoopConsole = () => ({
|
|
|
79013
79373
|
});
|
|
79014
79374
|
//#endregion
|
|
79015
79375
|
//#region src/cli/utils/version.ts
|
|
79016
|
-
const VERSION = "0.9.3-dev.
|
|
79376
|
+
const VERSION = "0.9.3-dev.f958625";
|
|
79017
79377
|
//#endregion
|
|
79018
79378
|
//#region src/cli/utils/json-mode.ts
|
|
79019
79379
|
let context = null;
|
|
@@ -79250,6 +79610,9 @@ const buildSentryScope = (runContext = buildRunContext()) => {
|
|
|
79250
79610
|
};
|
|
79251
79611
|
};
|
|
79252
79612
|
//#endregion
|
|
79613
|
+
//#region src/cli/utils/is-env-flag-enabled.ts
|
|
79614
|
+
const isEnvFlagEnabled = (value) => value === "1" || value?.toLowerCase() === "true";
|
|
79615
|
+
//#endregion
|
|
79253
79616
|
//#region src/cli/utils/anonymize-text.ts
|
|
79254
79617
|
/**
|
|
79255
79618
|
* Free-text fields can carry both a home-directory path (the OS username) and a
|
|
@@ -79371,13 +79734,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
79371
79734
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
79372
79735
|
* standard `SENTRY_RELEASE` override.
|
|
79373
79736
|
*/
|
|
79374
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.3-dev.
|
|
79737
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.3-dev.f958625`;
|
|
79375
79738
|
/**
|
|
79376
79739
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
79377
79740
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
79378
79741
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
79379
79742
|
*/
|
|
79380
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.3-dev.
|
|
79743
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.3-dev.f958625") ? "development" : "production");
|
|
79381
79744
|
/**
|
|
79382
79745
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
79383
79746
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -79400,7 +79763,6 @@ const shouldEnableSentry = () => {
|
|
|
79400
79763
|
if (process.env.VITEST || process.env.NODE_ENV === "test") return false;
|
|
79401
79764
|
return true;
|
|
79402
79765
|
};
|
|
79403
|
-
const isEnvFlagEnabled = (value) => value === "1" || value?.toLowerCase() === "true";
|
|
79404
79766
|
/**
|
|
79405
79767
|
* Whether performance traces will actually be recorded — Sentry is live and the
|
|
79406
79768
|
* resolved sample rate is above zero. Used to gate the per-run root span and
|
|
@@ -83016,27 +83378,21 @@ const prompts = (questions, options = {}) => {
|
|
|
83016
83378
|
//#endregion
|
|
83017
83379
|
//#region src/cli/utils/to-span-attributes.ts
|
|
83018
83380
|
/**
|
|
83019
|
-
* Converts a Sentry tag map (which permits `null` to denote an
|
|
83020
|
-
* into Sentry/OTel span attributes, which only accept primitives.
|
|
83021
|
-
* are dropped rather than coerced, so an absent signal doesn't
|
|
83022
|
-
* misleading `"null"` attribute.
|
|
83381
|
+
* Converts a Sentry tag map (which permits `null`/`undefined` to denote an
|
|
83382
|
+
* absent signal) into Sentry/OTel span attributes, which only accept primitives.
|
|
83383
|
+
* Missing values are dropped rather than coerced, so an absent signal doesn't
|
|
83384
|
+
* become a misleading `"null"` or `"undefined"` attribute.
|
|
83023
83385
|
*/
|
|
83024
83386
|
const toSpanAttributes = (tags) => {
|
|
83025
83387
|
const attributes = {};
|
|
83026
|
-
for (const [key, value] of Object.entries(tags)) if (value
|
|
83388
|
+
for (const [key, value] of Object.entries(tags)) if (value != null) attributes[key] = value;
|
|
83027
83389
|
return attributes;
|
|
83028
83390
|
};
|
|
83029
83391
|
//#endregion
|
|
83030
83392
|
//#region src/cli/utils/record-metric.ts
|
|
83031
|
-
const cleanAttributes = (attributes) => {
|
|
83032
|
-
const cleaned = {};
|
|
83033
|
-
if (!attributes) return cleaned;
|
|
83034
|
-
for (const [key, value] of Object.entries(attributes)) if (value !== null && value !== void 0) cleaned[key] = value;
|
|
83035
|
-
return cleaned;
|
|
83036
|
-
};
|
|
83037
83393
|
const withRunAttributes = (attributes) => ({
|
|
83038
83394
|
...toSpanAttributes(buildSentryScope().tags),
|
|
83039
|
-
...
|
|
83395
|
+
...toSpanAttributes(attributes ?? {})
|
|
83040
83396
|
});
|
|
83041
83397
|
/**
|
|
83042
83398
|
* Emits a Sentry counter. A guarded, swallow-on-throw no-op unless the CLI's
|
|
@@ -91053,6 +91409,19 @@ const ciInstallAction = (options) => runCiCommand("install", runCiInstall, optio
|
|
|
91053
91409
|
const ciUpgradeAction = (options) => runCiCommand("upgrade", runCiUpgrade, options);
|
|
91054
91410
|
const ciConfigAction = (options) => runCiCommand("config", runCiConfig, options);
|
|
91055
91411
|
//#endregion
|
|
91412
|
+
//#region src/cli/utils/active-scan-abort-registry.ts
|
|
91413
|
+
const activeScanAbortControllers = /* @__PURE__ */ new Set();
|
|
91414
|
+
const activeScanAbortRegistry = {
|
|
91415
|
+
register: (controller) => {
|
|
91416
|
+
activeScanAbortControllers.add(controller);
|
|
91417
|
+
return () => activeScanAbortControllers.delete(controller);
|
|
91418
|
+
},
|
|
91419
|
+
abortAll: () => {
|
|
91420
|
+
for (const controller of activeScanAbortControllers) controller.abort();
|
|
91421
|
+
activeScanAbortControllers.clear();
|
|
91422
|
+
}
|
|
91423
|
+
};
|
|
91424
|
+
//#endregion
|
|
91056
91425
|
//#region src/cli/utils/sentry-tracer.ts
|
|
91057
91426
|
const toHrTime = (epochNanoseconds) => [Number(epochNanoseconds / NANOSECONDS_PER_SECOND), Number(epochNanoseconds % NANOSECONDS_PER_SECOND)];
|
|
91058
91427
|
const SPAN_KIND_TO_OTEL = {
|
|
@@ -91469,7 +91838,14 @@ const isInspectResultComplete = (result) => isScanComplete({
|
|
|
91469
91838
|
skippedCheckReasonCount: Object.keys(result.skippedCheckReasons ?? {}).length
|
|
91470
91839
|
});
|
|
91471
91840
|
//#endregion
|
|
91841
|
+
//#region src/cli/utils/resolve-score-unavailable-reason.ts
|
|
91842
|
+
const resolveScoreUnavailableReason = (input) => {
|
|
91843
|
+
if (input.isScoreDisabled) return "disabled";
|
|
91844
|
+
return input.isAnalysisIncomplete ? "analysis-incomplete" : "api-unavailable";
|
|
91845
|
+
};
|
|
91846
|
+
//#endregion
|
|
91472
91847
|
//#region src/cli/utils/scan-result-cache.ts
|
|
91848
|
+
const createScanResultCacheInvocationState = () => ({ repositoryIdentityByRoot: /* @__PURE__ */ new Map() });
|
|
91473
91849
|
const CACHE_DISABLED_VALUES = new Set(["1", "true"]);
|
|
91474
91850
|
const TOOLCHAIN_PACKAGE_SPECIFIERS = [
|
|
91475
91851
|
"oxlint/package.json",
|
|
@@ -91554,7 +91930,7 @@ const buildDirtyPathContentFingerprint = (absolutePath, statusCode) => {
|
|
|
91554
91930
|
if (stats.size > 10485760) return null;
|
|
91555
91931
|
return hashFileContents(absolutePath);
|
|
91556
91932
|
};
|
|
91557
|
-
const buildWorktreeFingerprint = (projectDirectory) => {
|
|
91933
|
+
const buildWorktreeFingerprint = (projectDirectory, repositoryRoot) => {
|
|
91558
91934
|
const statusOutput = runGit(projectDirectory, [
|
|
91559
91935
|
"status",
|
|
91560
91936
|
"--porcelain=v1",
|
|
@@ -91566,8 +91942,6 @@ const buildWorktreeFingerprint = (projectDirectory) => {
|
|
|
91566
91942
|
const statusEntries = parseWorktreeStatusRecords(statusOutput);
|
|
91567
91943
|
if (statusEntries === null) return null;
|
|
91568
91944
|
if (statusEntries.length > 300) return null;
|
|
91569
|
-
const repositoryRoot = runGit(projectDirectory, ["rev-parse", "--show-toplevel"]);
|
|
91570
|
-
if (repositoryRoot === null) return null;
|
|
91571
91945
|
const dirtyEntries = [];
|
|
91572
91946
|
for (const statusEntry of statusEntries) {
|
|
91573
91947
|
const contentFingerprint = buildDirtyPathContentFingerprint(path.join(repositoryRoot, statusEntry.path), statusEntry.statusCode);
|
|
@@ -91583,6 +91957,20 @@ const buildWorktreeFingerprint = (projectDirectory) => {
|
|
|
91583
91957
|
return firstSortKey < secondSortKey ? -1 : firstSortKey > secondSortKey ? 1 : 0;
|
|
91584
91958
|
});
|
|
91585
91959
|
};
|
|
91960
|
+
const resolveRepositoryCacheIdentity = (projectDirectory, invocationState) => {
|
|
91961
|
+
const repositoryRoot = runGit(projectDirectory, ["rev-parse", "--show-toplevel"]);
|
|
91962
|
+
if (repositoryRoot === null) return null;
|
|
91963
|
+
const cachedIdentity = invocationState.repositoryIdentityByRoot.get(repositoryRoot);
|
|
91964
|
+
if (cachedIdentity !== void 0) return cachedIdentity;
|
|
91965
|
+
const worktreeFingerprint = buildWorktreeFingerprint(projectDirectory, repositoryRoot);
|
|
91966
|
+
const headSha = readHeadSha(repositoryRoot);
|
|
91967
|
+
const identity = worktreeFingerprint === null || headSha === null ? null : {
|
|
91968
|
+
headSha,
|
|
91969
|
+
worktreeFingerprint
|
|
91970
|
+
};
|
|
91971
|
+
invocationState.repositoryIdentityByRoot.set(repositoryRoot, identity);
|
|
91972
|
+
return identity;
|
|
91973
|
+
};
|
|
91586
91974
|
const DOTENV_FILE_NAME_PATTERN = /^\.env(\.|$)/;
|
|
91587
91975
|
const resolveDotenvFingerprint = (projectDirectory) => {
|
|
91588
91976
|
try {
|
|
@@ -91677,16 +92065,14 @@ const resolveToolchainFingerprint = (nodeBinaryPath) => {
|
|
|
91677
92065
|
const buildScanResultCacheKey = (input) => {
|
|
91678
92066
|
if (isCacheGloballyDisabled()) return null;
|
|
91679
92067
|
if (!isGitIdentityTrustworthy(input.projectDirectory)) return null;
|
|
91680
|
-
const
|
|
91681
|
-
if (
|
|
91682
|
-
const headSha = readHeadSha(input.projectDirectory);
|
|
91683
|
-
if (headSha === null) return null;
|
|
92068
|
+
const repositoryIdentity = resolveRepositoryCacheIdentity(input.projectDirectory, input.invocationState);
|
|
92069
|
+
if (repositoryIdentity === null) return null;
|
|
91684
92070
|
if (stringifyStableJson(input.userConfig) === null) return null;
|
|
91685
92071
|
const cacheKeyJson = stringifyStableJson({
|
|
91686
92072
|
schemaVersion: 6,
|
|
91687
92073
|
projectIdentity: resolveProjectIdentity(input.projectDirectory),
|
|
91688
|
-
headSha,
|
|
91689
|
-
worktreeFingerprint,
|
|
92074
|
+
headSha: repositoryIdentity.headSha,
|
|
92075
|
+
worktreeFingerprint: repositoryIdentity.worktreeFingerprint,
|
|
91690
92076
|
dotenvFingerprint: resolveDotenvFingerprint(input.projectDirectory),
|
|
91691
92077
|
reactDoctorVersion: input.version,
|
|
91692
92078
|
nodeVersion: process.version,
|
|
@@ -91700,6 +92086,8 @@ const buildScanResultCacheKey = (input) => {
|
|
|
91700
92086
|
deadCode: input.options.deadCode,
|
|
91701
92087
|
supplyChain: input.options.supplyChain,
|
|
91702
92088
|
includePaths: [...input.options.includePaths].sort(),
|
|
92089
|
+
excludedProjectDirectories: [...input.options.excludedProjectDirectories ?? []].sort(),
|
|
92090
|
+
retainExcludedProjectDeadCodeDiagnostics: input.options.retainExcludedProjectDeadCodeDiagnostics ?? false,
|
|
91703
92091
|
customRulesOnly: input.options.customRulesOnly,
|
|
91704
92092
|
respectInlineDisables: input.options.respectInlineDisables,
|
|
91705
92093
|
warnings: input.options.warnings,
|
|
@@ -91777,8 +92165,8 @@ const toCategoryKey = (category) => category.toLowerCase().replace(/[^a-z0-9]+/g
|
|
|
91777
92165
|
* browser and stay group-/filter-/aggregate-able in the Spans dataset.
|
|
91778
92166
|
*
|
|
91779
92167
|
* Value types are preserved verbatim (numbers stay numbers so Sentry infers a
|
|
91780
|
-
* numeric attribute and `p75(...)`/`avg(...)` work; `null`
|
|
91781
|
-
* `toSpanAttributes` can drop absent signals rather than coerce them).
|
|
92168
|
+
* numeric attribute and `p75(...)`/`avg(...)` work; `null`/`undefined` are kept
|
|
92169
|
+
* so `toSpanAttributes` can drop absent signals rather than coerce them).
|
|
91782
92170
|
*/
|
|
91783
92171
|
const withNamespace = (namespace, attributes) => {
|
|
91784
92172
|
const namespaced = {};
|
|
@@ -91844,7 +92232,7 @@ const buildCacheAttributes = (input) => {
|
|
|
91844
92232
|
const warmth = knownSubsystemCount > 0 ? reuseRatioSum / knownSubsystemCount : null;
|
|
91845
92233
|
const temperature = warmth !== null && warmth > 0 ? "warm" : isCacheGloballyDisabled() ? "disabled" : "cold";
|
|
91846
92234
|
return withNamespace("cache", {
|
|
91847
|
-
wholeRepoHit: input.wholeRepoCacheHit
|
|
92235
|
+
wholeRepoHit: input.wholeRepoCacheHit,
|
|
91848
92236
|
temperature,
|
|
91849
92237
|
warmth
|
|
91850
92238
|
});
|
|
@@ -91966,33 +92354,37 @@ const buildOutcomeAttributes = (input) => {
|
|
|
91966
92354
|
...withNamespace("score", {
|
|
91967
92355
|
value: result.score ? result.score.score : null,
|
|
91968
92356
|
label: result.score ? result.score.label : null,
|
|
91969
|
-
available: result.score !== null
|
|
92357
|
+
available: result.score !== null,
|
|
92358
|
+
unavailableReason: result.score === null ? resolveScoreUnavailableReason({
|
|
92359
|
+
isScoreDisabled: input.noScore,
|
|
92360
|
+
isAnalysisIncomplete: input.didLintFail === true || input.didDeadCodeFail === true
|
|
92361
|
+
}) : null
|
|
91970
92362
|
}),
|
|
91971
92363
|
...withNamespace("lint", {
|
|
91972
|
-
failed: input.didLintFail
|
|
91973
|
-
failureReasonKind: input.lintFailureReasonKind
|
|
91974
|
-
partialFailureCount: input.lintPartialFailureCount
|
|
91975
|
-
droppedFileCount: input.lintDroppedFileCount
|
|
91976
|
-
deadlineSkippedFileCount: input.lintDeadlineSkippedFileCount
|
|
91977
|
-
cacheHitFiles: result.lintCacheHitFileCount
|
|
91978
|
-
cacheTotalFiles: result.lintCacheTotalFileCount
|
|
92364
|
+
failed: input.didLintFail,
|
|
92365
|
+
failureReasonKind: input.lintFailureReasonKind,
|
|
92366
|
+
partialFailureCount: input.lintPartialFailureCount,
|
|
92367
|
+
droppedFileCount: input.lintDroppedFileCount,
|
|
92368
|
+
deadlineSkippedFileCount: input.lintDeadlineSkippedFileCount,
|
|
92369
|
+
cacheHitFiles: result.lintCacheHitFileCount,
|
|
92370
|
+
cacheTotalFiles: result.lintCacheTotalFileCount,
|
|
91979
92371
|
cacheHitRatio: ratioOf(result.lintCacheHitFileCount, result.lintCacheTotalFileCount),
|
|
91980
|
-
sidecarReplayedFiles: result.lintSidecarReplayedFileCount
|
|
91981
|
-
sidecarTotalFiles: result.lintSidecarTotalFileCount
|
|
92372
|
+
sidecarReplayedFiles: result.lintSidecarReplayedFileCount,
|
|
92373
|
+
sidecarTotalFiles: result.lintSidecarTotalFileCount,
|
|
91982
92374
|
sidecarReplayRatio: ratioOf(result.lintSidecarReplayedFileCount, result.lintSidecarTotalFileCount)
|
|
91983
92375
|
}),
|
|
91984
92376
|
...withNamespace("deadCode", {
|
|
91985
|
-
failed: input.didDeadCodeFail
|
|
91986
|
-
overlapped: input.deadCodeOverlapped
|
|
91987
|
-
cacheHit: result.deadCodeCacheHit
|
|
91988
|
-
summaryCacheHits: result.deadCodeSummaryCacheHits
|
|
91989
|
-
summaryCacheMisses: result.deadCodeSummaryCacheMisses
|
|
92377
|
+
failed: input.didDeadCodeFail,
|
|
92378
|
+
overlapped: input.deadCodeOverlapped,
|
|
92379
|
+
cacheHit: result.deadCodeCacheHit,
|
|
92380
|
+
summaryCacheHits: result.deadCodeSummaryCacheHits,
|
|
92381
|
+
summaryCacheMisses: result.deadCodeSummaryCacheMisses
|
|
91990
92382
|
}),
|
|
91991
|
-
...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut
|
|
91992
|
-
...withNamespace("securityScan", { failed: input.securityScanFailed
|
|
92383
|
+
...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut }),
|
|
92384
|
+
...withNamespace("securityScan", { failed: input.securityScanFailed }),
|
|
91993
92385
|
...withNamespace("timing", {
|
|
91994
92386
|
elapsedMs: result.elapsedMilliseconds,
|
|
91995
|
-
scanMs: result.scanElapsedMilliseconds
|
|
92387
|
+
scanMs: result.scanElapsedMilliseconds
|
|
91996
92388
|
}),
|
|
91997
92389
|
...withNamespace("migration", {
|
|
91998
92390
|
largestRuleBucketFiles: largestRuleBucket ? largestRuleBucket.fileCount : null,
|
|
@@ -92004,7 +92396,7 @@ const buildOutcomeAttributes = (input) => {
|
|
|
92004
92396
|
new: summary.totalDiagnosticCount,
|
|
92005
92397
|
fixed: result.baselineDelta.fixedCount,
|
|
92006
92398
|
baseTotal: result.baselineDelta.baseTotalCount,
|
|
92007
|
-
crossFileMatches: result.baselineDelta.crossFileMatchCount
|
|
92399
|
+
crossFileMatches: result.baselineDelta.crossFileMatchCount,
|
|
92008
92400
|
degraded: false
|
|
92009
92401
|
}));
|
|
92010
92402
|
else if (input.gateExempt) attributes["baseline.degraded"] = true;
|
|
@@ -92013,7 +92405,7 @@ const buildOutcomeAttributes = (input) => {
|
|
|
92013
92405
|
const buildActionAttributes = () => {
|
|
92014
92406
|
const { githubActorAssociation } = resolveGithubActionsScoreMetadata();
|
|
92015
92407
|
return withNamespace("action", {
|
|
92016
|
-
actorAssociation: githubActorAssociation
|
|
92408
|
+
actorAssociation: githubActorAssociation,
|
|
92017
92409
|
runnerOs: detectRunnerOs(),
|
|
92018
92410
|
comment: readEnvBoolean(ACTION_INPUT_ENVIRONMENT_VARIABLES.comment),
|
|
92019
92411
|
reviewComments: readEnvBoolean(ACTION_INPUT_ENVIRONMENT_VARIABLES.reviewComments),
|
|
@@ -92027,7 +92419,7 @@ const buildScanAttributes = (input) => {
|
|
|
92027
92419
|
mode: input.mode,
|
|
92028
92420
|
scope: input.scope,
|
|
92029
92421
|
parallel: input.parallel,
|
|
92030
|
-
workerCount: input.workerCount
|
|
92422
|
+
workerCount: input.workerCount,
|
|
92031
92423
|
maxDurationMs: input.maxDurationMs,
|
|
92032
92424
|
lint: input.lint,
|
|
92033
92425
|
deadCode: input.deadCode,
|
|
@@ -92041,10 +92433,10 @@ const buildScanAttributes = (input) => {
|
|
|
92041
92433
|
hasCustomConfig: input.hasCustomConfig,
|
|
92042
92434
|
rulesConfigured: ruleKeys.length,
|
|
92043
92435
|
rulesDisabled: ruleKeys.filter((key) => ruleOverrides[key] === "off").length,
|
|
92044
|
-
fileCount: input.result?.scannedFileCount
|
|
92045
|
-
nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length
|
|
92046
|
-
htmlFileCount: input.result?.analyzedFiles?.filter((filePath) => HTML_FILE_PATTERN.test(filePath)).length
|
|
92047
|
-
multilineDiagnosticCount: input.result?.diagnostics.filter((diagnostic) => (diagnostic.endLine ?? diagnostic.line) > diagnostic.line).length
|
|
92436
|
+
fileCount: input.result?.scannedFileCount,
|
|
92437
|
+
nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length,
|
|
92438
|
+
htmlFileCount: input.result?.analyzedFiles?.filter((filePath) => HTML_FILE_PATTERN.test(filePath)).length,
|
|
92439
|
+
multilineDiagnosticCount: input.result?.diagnostics.filter((diagnostic) => (diagnostic.endLine ?? diagnostic.line) > diagnostic.line).length
|
|
92048
92440
|
});
|
|
92049
92441
|
};
|
|
92050
92442
|
/**
|
|
@@ -92056,11 +92448,11 @@ const buildScanAttributes = (input) => {
|
|
|
92056
92448
|
* tree up in Sentry's attribute browser and stay filter-/group-/aggregate-able
|
|
92057
92449
|
* in the Spans dataset. Pure and exported so the projection (outcome
|
|
92058
92450
|
* precedence, rule/category rollups, CI knobs, config shape) is unit-testable
|
|
92059
|
-
* without a live Sentry client. `null` values are dropped so absent
|
|
92060
|
-
* never become misleading `"null"` attributes. The run +
|
|
92061
|
-
* (version, command, ci/provider, framework, …) is already
|
|
92062
|
-
* `withSentryRunSpan` / `recordSentryProjectContext`, so this
|
|
92063
|
-
* those don't carry.
|
|
92451
|
+
* without a live Sentry client. `null`/`undefined` values are dropped so absent
|
|
92452
|
+
* signals never become misleading `"null"`/`"undefined"` attributes. The run +
|
|
92453
|
+
* project base context (version, command, ci/provider, framework, …) is already
|
|
92454
|
+
* on the span from `withSentryRunSpan` / `recordSentryProjectContext`, so this
|
|
92455
|
+
* adds only what those don't carry.
|
|
92064
92456
|
*/
|
|
92065
92457
|
const buildRunEventAttributes = (input) => toSpanAttributes({
|
|
92066
92458
|
...buildScanAttributes(input),
|
|
@@ -92534,10 +92926,29 @@ const createDiagnosticEvidenceReader = (rootDirectory, options = {}) => {
|
|
|
92534
92926
|
//#endregion
|
|
92535
92927
|
//#region src/cli/utils/build-no-score-message.ts
|
|
92536
92928
|
const ENTERPRISE_CONTACT_HINT = `Want something custom to your company? Contact us at ${ENTERPRISE_CONTACT_URL}.`;
|
|
92537
|
-
const buildNoScoreMessage = (
|
|
92538
|
-
|
|
92929
|
+
const buildNoScoreMessage = (input) => {
|
|
92930
|
+
const unavailableReason = resolveScoreUnavailableReason({
|
|
92931
|
+
isScoreDisabled: input.isScoreDisabled,
|
|
92932
|
+
isAnalysisIncomplete: input.isAnalysisIncomplete ?? false
|
|
92933
|
+
});
|
|
92934
|
+
let reason;
|
|
92935
|
+
switch (unavailableReason) {
|
|
92936
|
+
case "disabled":
|
|
92937
|
+
reason = input.disabledMessage ?? "Score disabled by --no-score.";
|
|
92938
|
+
break;
|
|
92939
|
+
case "analysis-incomplete":
|
|
92940
|
+
reason = "Score not shown because lint or dead-code analysis could not complete.";
|
|
92941
|
+
break;
|
|
92942
|
+
case "api-unavailable":
|
|
92943
|
+
reason = "Score unavailable (could not reach the score API).";
|
|
92944
|
+
break;
|
|
92945
|
+
}
|
|
92946
|
+
return `${reason} ${ENTERPRISE_CONTACT_HINT}`;
|
|
92539
92947
|
};
|
|
92540
92948
|
//#endregion
|
|
92949
|
+
//#region src/cli/utils/has-incomplete-score-analysis.ts
|
|
92950
|
+
const hasIncompleteScoreAnalysis = (skippedChecks) => skippedChecks.includes("lint") || skippedChecks.includes("dead-code");
|
|
92951
|
+
//#endregion
|
|
92541
92952
|
//#region src/cli/utils/build-empty-report-message.ts
|
|
92542
92953
|
const buildEmptyReportMessage = (input) => {
|
|
92543
92954
|
const categoryFilters = [...input.categoryFilters];
|
|
@@ -93290,9 +93701,6 @@ const markOnboardingComplete = (options = {}) => {
|
|
|
93290
93701
|
if (isGatePending(ONBOARDING_GATE, {}, options)) recordGate(ONBOARDING_GATE, {}, options);
|
|
93291
93702
|
};
|
|
93292
93703
|
//#endregion
|
|
93293
|
-
//#region src/cli/utils/render-project-detection.ts
|
|
93294
|
-
const printProjectDetection = (_input) => void_;
|
|
93295
|
-
//#endregion
|
|
93296
93704
|
//#region src/cli/utils/colorize-by-score.ts
|
|
93297
93705
|
const colorizeByScore = (text, score) => {
|
|
93298
93706
|
if (score >= 75) return highlighter.success(text);
|
|
@@ -93660,9 +94068,6 @@ const resolveCliCategories = (categoryFlag) => {
|
|
|
93660
94068
|
//#endregion
|
|
93661
94069
|
//#region src/inspect.ts
|
|
93662
94070
|
const silentConsole = makeNoopConsole();
|
|
93663
|
-
const runConsole = (effect) => {
|
|
93664
|
-
runSync(effect);
|
|
93665
|
-
};
|
|
93666
94071
|
const recordOnboardingCompletion = (options) => {
|
|
93667
94072
|
const forceOnboarding = isOnboardingForced();
|
|
93668
94073
|
if (shouldRecordOnboarding({
|
|
@@ -93721,7 +94126,10 @@ const mergeInspectOptions = (inputOptions, userConfig) => {
|
|
|
93721
94126
|
maxDurationMs: inputOptions.maxDurationMs ?? null,
|
|
93722
94127
|
baseline: inputOptions.baseline ?? null,
|
|
93723
94128
|
changedLineRanges: inputOptions.changedLineRanges ?? null,
|
|
93724
|
-
supplyChainManifestChanged: inputOptions.supplyChainManifestChanged ?? false
|
|
94129
|
+
supplyChainManifestChanged: inputOptions.supplyChainManifestChanged ?? false,
|
|
94130
|
+
excludedProjectDirectories: inputOptions.excludedProjectDirectories ?? [],
|
|
94131
|
+
retainExcludedProjectDeadCodeDiagnostics: inputOptions.retainExcludedProjectDeadCodeDiagnostics ?? false,
|
|
94132
|
+
precomputedSourceFileCount: inputOptions.precomputedSourceFileCount
|
|
93725
94133
|
};
|
|
93726
94134
|
};
|
|
93727
94135
|
const deriveScope = (options) => {
|
|
@@ -93754,13 +94162,14 @@ const inspectWithOxlintRuntime = async (directory, inputOptions, oxlintRuntime)
|
|
|
93754
94162
|
const deadlineEpochMs = inputOptions.deadlineEpochMs ?? (inputOptions.maxDurationMs != null ? Date.now() + inputOptions.maxDurationMs : null);
|
|
93755
94163
|
const isConcurrentScan = inputOptions.concurrentScan === true;
|
|
93756
94164
|
if (!isConcurrentScan) resetSentryRunState();
|
|
93757
|
-
const
|
|
94165
|
+
const configOverride = inputOptions.configOverride;
|
|
94166
|
+
const hasConfigOverride = configOverride !== void 0;
|
|
93758
94167
|
let scanDirectory;
|
|
93759
94168
|
let userConfig;
|
|
93760
94169
|
let configSourceDirectory;
|
|
93761
94170
|
if (hasConfigOverride) {
|
|
93762
94171
|
scanDirectory = directory;
|
|
93763
|
-
userConfig =
|
|
94172
|
+
userConfig = configOverride;
|
|
93764
94173
|
configSourceDirectory = inputOptions.configSourceDirectory ?? null;
|
|
93765
94174
|
} else {
|
|
93766
94175
|
const scanTarget = await resolveScanTarget(directory);
|
|
@@ -93793,11 +94202,22 @@ const inspectWithOxlintRuntime = async (directory, inputOptions, oxlintRuntime)
|
|
|
93793
94202
|
};
|
|
93794
94203
|
const createInvocationInspect = (requestedOxlintConcurrency) => {
|
|
93795
94204
|
const concurrency = resolveScanConcurrency(requestedOxlintConcurrency ?? runSync(OxlintConcurrency));
|
|
93796
|
-
const
|
|
93797
|
-
|
|
93798
|
-
|
|
94205
|
+
const spawnSlots = createOxlintSpawnSlots(concurrency);
|
|
94206
|
+
const scanResultCacheInvocationState = createScanResultCacheInvocationState();
|
|
94207
|
+
return async (directory, inputOptions = {}) => {
|
|
94208
|
+
const abortController = new AbortController();
|
|
94209
|
+
const unregisterAbortController = activeScanAbortRegistry.register(abortController);
|
|
94210
|
+
try {
|
|
94211
|
+
return await inspectWithOxlintRuntime(directory, inputOptions, {
|
|
94212
|
+
concurrency,
|
|
94213
|
+
spawnSlots,
|
|
94214
|
+
abortSignal: abortController.signal,
|
|
94215
|
+
scanResultCacheInvocationState
|
|
94216
|
+
});
|
|
94217
|
+
} finally {
|
|
94218
|
+
unregisterAbortController();
|
|
94219
|
+
}
|
|
93799
94220
|
};
|
|
93800
|
-
return (directory, inputOptions = {}) => inspectWithOxlintRuntime(directory, inputOptions, oxlintRuntime);
|
|
93801
94221
|
};
|
|
93802
94222
|
const inspect = async (directory, inputOptions = {}) => createInvocationInspect(inputOptions.concurrency)(directory, inputOptions);
|
|
93803
94223
|
const countIncompleteLintFiles = (lintPartialFailures) => countDroppedLintFiles(lintPartialFailures) + countDeadlineSkippedFiles(lintPartialFailures);
|
|
@@ -93810,12 +94230,27 @@ const countIncompleteLintFiles = (lintPartialFailures) => countDroppedLintFiles(
|
|
|
93810
94230
|
*/
|
|
93811
94231
|
const runBaselineComparison = async (params) => {
|
|
93812
94232
|
const tempDirectory = mkdtempSync(path.join(tmpdir(), BASELINE_FILES_TEMP_DIR_PREFIX));
|
|
94233
|
+
const baselineIncludePaths = filterPathsOutsideDirectories({
|
|
94234
|
+
rootDirectory: params.directory,
|
|
94235
|
+
relativePaths: params.options.includePaths,
|
|
94236
|
+
excludedDirectories: params.options.excludedProjectDirectories
|
|
94237
|
+
});
|
|
94238
|
+
const baselineBaseFiles = params.baseFiles ? filterPathsOutsideDirectories({
|
|
94239
|
+
rootDirectory: params.directory,
|
|
94240
|
+
relativePaths: params.baseFiles,
|
|
94241
|
+
excludedDirectories: params.options.excludedProjectDirectories
|
|
94242
|
+
}) : void 0;
|
|
94243
|
+
const baselineHeadFiles = params.headFiles ? filterPathsOutsideDirectories({
|
|
94244
|
+
rootDirectory: params.directory,
|
|
94245
|
+
relativePaths: params.headFiles,
|
|
94246
|
+
excludedDirectories: params.options.excludedProjectDirectories
|
|
94247
|
+
}) : void 0;
|
|
93813
94248
|
const snapshot = await materializeBaselineFiles({
|
|
93814
94249
|
directory: params.directory,
|
|
93815
94250
|
ref: params.baselineRef,
|
|
93816
|
-
files:
|
|
93817
|
-
baseFiles:
|
|
93818
|
-
headFiles:
|
|
94251
|
+
files: baselineIncludePaths,
|
|
94252
|
+
baseFiles: baselineBaseFiles,
|
|
94253
|
+
headFiles: baselineHeadFiles,
|
|
93819
94254
|
tempDirectory
|
|
93820
94255
|
}).catch((error) => {
|
|
93821
94256
|
rmSync(tempDirectory, {
|
|
@@ -93837,7 +94272,7 @@ const runBaselineComparison = async (params) => {
|
|
|
93837
94272
|
const baseFiles = new Set(snapshot.baseFiles.map(toForwardSlashes));
|
|
93838
94273
|
const trackedHeadFiles = new Set(snapshot.headFiles.map(toForwardSlashes));
|
|
93839
94274
|
const expectedHeadFiles = new Set(trackedHeadFiles);
|
|
93840
|
-
for (const filePath of
|
|
94275
|
+
for (const filePath of baselineIncludePaths) {
|
|
93841
94276
|
const normalizedFilePath = toForwardSlashes(filePath);
|
|
93842
94277
|
if (!baseFiles.has(normalizedFilePath)) expectedHeadFiles.add(normalizedFilePath);
|
|
93843
94278
|
}
|
|
@@ -93874,8 +94309,9 @@ const runBaselineComparison = async (params) => {
|
|
|
93874
94309
|
resolveLocalGithubViewerPermission: false,
|
|
93875
94310
|
suppressScanSummary: true,
|
|
93876
94311
|
supplyChainManifestChanged: params.options.supplyChainManifestChanged,
|
|
93877
|
-
deadlineEpochMs: params.deadlineEpochMs ?? void 0
|
|
93878
|
-
|
|
94312
|
+
deadlineEpochMs: params.deadlineEpochMs ?? void 0,
|
|
94313
|
+
signal: params.oxlintRuntime.abortSignal
|
|
94314
|
+
}, {}).pipe(provide(baseLayers), provideService$2(PerFileLintCacheEnabled, false), provideService$2(SidecarLintCacheEnabled, false), provideService$2(Console, silentConsole))), { signal: params.oxlintRuntime.abortSignal });
|
|
93879
94315
|
if (baseOutput.didLintFail || countIncompleteLintFiles(baseOutput.lintPartialFailures) > 0) return null;
|
|
93880
94316
|
const hasUnscannedUntrackedSourceFiles = filterSourceFiles(snapshot.untrackedFiles.map(toForwardSlashes)).some((filePath) => !analyzedHeadFiles.has(filePath));
|
|
93881
94317
|
const delta = computeDiagnosticDelta({
|
|
@@ -93903,6 +94339,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93903
94339
|
const isDiffMode = options.includePaths.length > 0;
|
|
93904
94340
|
const resolvedNodeBinaryPath = await resolveOxlintNode(options.lint, options.scoreOnly || options.silent);
|
|
93905
94341
|
const lintBindingMissing = options.lint && !resolvedNodeBinaryPath;
|
|
94342
|
+
await yieldToEventLoop();
|
|
93906
94343
|
const cacheKey = buildScanResultCacheKey({
|
|
93907
94344
|
projectDirectory: directory,
|
|
93908
94345
|
version: VERSION,
|
|
@@ -93910,19 +94347,14 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93910
94347
|
options,
|
|
93911
94348
|
userConfig,
|
|
93912
94349
|
hasConfigOverride,
|
|
93913
|
-
configSourceDirectory
|
|
94350
|
+
configSourceDirectory,
|
|
94351
|
+
invocationState: oxlintRuntime.scanResultCacheInvocationState
|
|
93914
94352
|
});
|
|
93915
94353
|
const scanResultCache = cacheKey === null ? null : createScanResultCache(directory);
|
|
93916
|
-
const cachedPayload = cacheKey === null ? null : scanResultCache
|
|
94354
|
+
const cachedPayload = cacheKey === null || scanResultCache === null ? null : scanResultCache.lookup(cacheKey);
|
|
93917
94355
|
if (cachedPayload) {
|
|
93918
94356
|
recordSentryProjectContext(cachedPayload.project, rootSentrySpan, { concurrentScan: options.concurrentScan });
|
|
93919
94357
|
recordCount(METRIC.projectDetected, 1);
|
|
93920
|
-
await renderCachedProjectDetection({
|
|
93921
|
-
payload: cachedPayload,
|
|
93922
|
-
options,
|
|
93923
|
-
userConfig,
|
|
93924
|
-
isDiffMode
|
|
93925
|
-
});
|
|
93926
94358
|
const baselineDegraded = Boolean(options.baseline) && isDiffMode && cachedPayload.baselineDelta === void 0;
|
|
93927
94359
|
const result = await renderAndRecordScan({
|
|
93928
94360
|
payload: cachedPayload,
|
|
@@ -93956,6 +94388,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93956
94388
|
});
|
|
93957
94389
|
const program = runInspect({
|
|
93958
94390
|
directory,
|
|
94391
|
+
precomputedSourceFileCount: options.precomputedSourceFileCount,
|
|
93959
94392
|
includePaths: options.includePaths,
|
|
93960
94393
|
customRulesOnly: options.customRulesOnly,
|
|
93961
94394
|
respectInlineDisables: options.respectInlineDisables,
|
|
@@ -93972,26 +94405,19 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93972
94405
|
resolveLocalGithubViewerPermission: !options.noScore,
|
|
93973
94406
|
suppressScanSummary: options.suppressRendering,
|
|
93974
94407
|
supplyChainManifestChanged: options.supplyChainManifestChanged,
|
|
93975
|
-
|
|
93976
|
-
|
|
93977
|
-
|
|
94408
|
+
deadlineEpochMs: deadlineEpochMs ?? void 0,
|
|
94409
|
+
signal: oxlintRuntime.abortSignal,
|
|
94410
|
+
excludedProjectDirectories: options.excludedProjectDirectories,
|
|
94411
|
+
retainExcludedProjectDeadCodeDiagnostics: options.retainExcludedProjectDeadCodeDiagnostics
|
|
94412
|
+
}, { beforeLint: (projectInfo) => sync(() => {
|
|
93978
94413
|
recordSentryProjectContext(projectInfo, rootSentrySpan, { concurrentScan: options.concurrentScan });
|
|
93979
94414
|
recordCount(METRIC.projectDetected, 1);
|
|
93980
|
-
if (options.scoreOnly || options.suppressRendering) return;
|
|
93981
|
-
const lintSourceFileCount = lintIncludePaths?.length ?? projectInfo.sourceFileCount;
|
|
93982
|
-
yield* printProjectDetection({
|
|
93983
|
-
projectInfo,
|
|
93984
|
-
userConfig,
|
|
93985
|
-
isDiffMode,
|
|
93986
|
-
includePaths: options.includePaths,
|
|
93987
|
-
lintSourceFileCount
|
|
93988
|
-
});
|
|
93989
94415
|
}) });
|
|
93990
|
-
const output = await runPromise(restoreLegacyThrow(applyObservability(options.silent ? program.pipe(provide(layers), provideService$2(Console, silentConsole)) : program.pipe(provide(layers)), rootSentrySpan)));
|
|
94416
|
+
const output = await runPromise(restoreLegacyThrow(applyObservability(options.silent ? program.pipe(provide(layers), provideService$2(Console, silentConsole)) : program.pipe(provide(layers)), rootSentrySpan)), { signal: oxlintRuntime.abortSignal });
|
|
93991
94417
|
const didLintFail = lintBindingMissing || output.didLintFail;
|
|
93992
94418
|
const lintFailureReason = lintBindingMissing ? `oxlint native binding not found for Node ${process.version}; expected one matching ${OXLINT_NODE_REQUIREMENT}` : output.lintFailureReason;
|
|
93993
|
-
if (!options.scoreOnly && !options.uiLayers && !lintBindingMissing && output.didLintFail && lintFailureReason !== null) if (output.lintFailureReasonKind === "native-binding-missing")
|
|
93994
|
-
else
|
|
94419
|
+
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`)));
|
|
94420
|
+
else runSync(error$1(highlighter.error(lintFailureReason)));
|
|
93995
94421
|
let inspectDiagnostics = output.diagnostics;
|
|
93996
94422
|
let baselineDelta;
|
|
93997
94423
|
if (options.baseline && isDiffMode && !didLintFail && countIncompleteLintFiles(output.lintPartialFailures) === 0) {
|
|
@@ -94041,6 +94467,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
94041
94467
|
lintFailureReasonKind: lintBindingMissing ? "native-binding-missing" : output.lintFailureReasonKind,
|
|
94042
94468
|
supplyChainOverlapTimedOut: output.supplyChainOverlapTimedOut,
|
|
94043
94469
|
securityScanFailed: output.securityScanFailed,
|
|
94470
|
+
securityScanFailureReason: output.securityScanFailureReason,
|
|
94044
94471
|
suppressedRuleCounts: output.suppressedRuleCounts
|
|
94045
94472
|
};
|
|
94046
94473
|
if (cacheKey !== null && scanResultCache !== null && shouldStoreScanPayload(payload) && !baselineDegraded) scanResultCache.store(cacheKey, payload);
|
|
@@ -94066,16 +94493,6 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
94066
94493
|
return result;
|
|
94067
94494
|
};
|
|
94068
94495
|
const runMaybeSilent = (effect, silent) => silent ? effect.pipe(provideService$2(Console, silentConsole)) : effect;
|
|
94069
|
-
const renderCachedProjectDetection = async (input) => {
|
|
94070
|
-
if (input.options.scoreOnly || input.options.suppressRendering) return;
|
|
94071
|
-
await runPromise(runMaybeSilent(printProjectDetection({
|
|
94072
|
-
projectInfo: input.payload.project,
|
|
94073
|
-
userConfig: input.userConfig,
|
|
94074
|
-
isDiffMode: input.isDiffMode,
|
|
94075
|
-
includePaths: input.options.includePaths,
|
|
94076
|
-
lintSourceFileCount: input.payload.scannedFileCount
|
|
94077
|
-
}), input.options.silent));
|
|
94078
|
-
};
|
|
94079
94496
|
const renderAndRecordScan = async (input) => {
|
|
94080
94497
|
const result = await runPromise(runMaybeSilent(finalizeAndRender({
|
|
94081
94498
|
options: input.options,
|
|
@@ -94091,6 +94508,7 @@ const renderAndRecordScan = async (input) => {
|
|
|
94091
94508
|
deadCodeFailureReason: input.payload.deadCodeFailureReason,
|
|
94092
94509
|
supplyChainOverlapTimedOut: input.payload.supplyChainOverlapTimedOut,
|
|
94093
94510
|
securityScanFailed: input.payload.securityScanFailed ?? false,
|
|
94511
|
+
securityScanFailureReason: input.payload.securityScanFailureReason ?? null,
|
|
94094
94512
|
directory: input.payload.directory,
|
|
94095
94513
|
scannedFileCount: input.payload.scannedFileCount,
|
|
94096
94514
|
scannedFilePaths: input.payload.scannedFilePaths,
|
|
@@ -94142,7 +94560,7 @@ const renderAndRecordScan = async (input) => {
|
|
|
94142
94560
|
return result;
|
|
94143
94561
|
};
|
|
94144
94562
|
const finalizeAndRender = (input) => gen(function* () {
|
|
94145
|
-
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;
|
|
94563
|
+
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;
|
|
94146
94564
|
const { skippedChecks, skippedCheckReasons } = buildSkippedChecks({
|
|
94147
94565
|
didLintFail,
|
|
94148
94566
|
lintFailureReason,
|
|
@@ -94150,10 +94568,15 @@ const finalizeAndRender = (input) => gen(function* () {
|
|
|
94150
94568
|
didDeadCodeFail,
|
|
94151
94569
|
deadCodeFailureReason,
|
|
94152
94570
|
supplyChainOverlapTimedOut,
|
|
94153
|
-
securityScanFailed
|
|
94571
|
+
securityScanFailed,
|
|
94572
|
+
securityScanFailureReason
|
|
94154
94573
|
});
|
|
94155
94574
|
const hasSkippedChecks = skippedChecks.length > 0;
|
|
94156
|
-
const noScoreMessage = buildNoScoreMessage(
|
|
94575
|
+
const noScoreMessage = buildNoScoreMessage({
|
|
94576
|
+
isScoreDisabled: options.noScore,
|
|
94577
|
+
isAnalysisIncomplete: hasIncompleteScoreAnalysis(skippedChecks),
|
|
94578
|
+
disabledMessage: options.scoreDisabledMessage
|
|
94579
|
+
});
|
|
94157
94580
|
const buildResult = () => ({
|
|
94158
94581
|
diagnostics: [...diagnostics],
|
|
94159
94582
|
score,
|
|
@@ -94283,6 +94706,32 @@ const materializeStagedFiles = async (input) => {
|
|
|
94283
94706
|
};
|
|
94284
94707
|
};
|
|
94285
94708
|
//#endregion
|
|
94709
|
+
//#region src/cli/utils/deduplicate-project-scans.ts
|
|
94710
|
+
const deduplicateProjectScans = (projectScans) => {
|
|
94711
|
+
const uniqueProjectScans = [];
|
|
94712
|
+
const seenDirectories = /* @__PURE__ */ new Set();
|
|
94713
|
+
for (const projectScan of projectScans) {
|
|
94714
|
+
if (seenDirectories.has(projectScan.directory)) continue;
|
|
94715
|
+
seenDirectories.add(projectScan.directory);
|
|
94716
|
+
uniqueProjectScans.push(projectScan);
|
|
94717
|
+
}
|
|
94718
|
+
return uniqueProjectScans;
|
|
94719
|
+
};
|
|
94720
|
+
//#endregion
|
|
94721
|
+
//#region src/cli/utils/collect-project-source-file-counts.ts
|
|
94722
|
+
const collectProjectSourceFileCounts = async (rootDirectory, projectDirectories) => {
|
|
94723
|
+
const abortController = new AbortController();
|
|
94724
|
+
const unregisterAbortController = activeScanAbortRegistry.register(abortController);
|
|
94725
|
+
try {
|
|
94726
|
+
return await collectSourceFileCountsByDirectory(rootDirectory, projectDirectories, abortController.signal);
|
|
94727
|
+
} finally {
|
|
94728
|
+
unregisterAbortController();
|
|
94729
|
+
}
|
|
94730
|
+
};
|
|
94731
|
+
//#endregion
|
|
94732
|
+
//#region src/cli/utils/format-skipped-projects-message.ts
|
|
94733
|
+
const formatSkippedProjectsMessage = (projectCount) => `${projectCount} ${projectCount === 1 ? "project was" : "projects were"} skipped because the max scan duration was reached.`;
|
|
94734
|
+
//#endregion
|
|
94286
94735
|
//#region src/cli/utils/is-share-opted-out.ts
|
|
94287
94736
|
const isShareOptedOut = (scans, flagNoScore) => Boolean(flagNoScore) || scans.some((scan) => Boolean(scan.config?.noScore) || scan.config?.share === false);
|
|
94288
94737
|
//#endregion
|
|
@@ -95683,7 +96132,7 @@ const runInstallReactDoctor = async (options = {}) => {
|
|
|
95683
96132
|
const shouldInstallGitHook = gitHookPath != null && (Boolean(options.yes) || !didSkipOptionalSetup && selectedSetupActions.includes(SETUP_OPTION_GIT_HOOK));
|
|
95684
96133
|
const shouldInstallAgentHooks = Boolean(options.agentHooks) || !didSkipOptionalSetup && selectedSetupActions.includes(SETUP_OPTION_AGENT_HOOKS);
|
|
95685
96134
|
if (!options.dryRun) {
|
|
95686
|
-
if (shouldInstallGitHook && gitHookTarget
|
|
96135
|
+
if (shouldInstallGitHook && gitHookTarget != null) installReactDoctorGitHookStep(gitHookTarget);
|
|
95687
96136
|
if (shouldInstallAgentHooks) installReactDoctorAgentHooksStep(projectRoot, selectedAgents);
|
|
95688
96137
|
}
|
|
95689
96138
|
if (options.dryRun) {
|
|
@@ -96301,6 +96750,27 @@ const resolveCliInspectOptions = (flags, userConfig) => {
|
|
|
96301
96750
|
};
|
|
96302
96751
|
};
|
|
96303
96752
|
//#endregion
|
|
96753
|
+
//#region src/cli/utils/resolve-workspace-dead-code-owner.ts
|
|
96754
|
+
const resolveWorkspaceDeadCodeOwner = (input) => {
|
|
96755
|
+
if (!input.isRootDeadCodeEnabled) return null;
|
|
96756
|
+
const resolvedRootDirectory = Path.resolve(input.rootDirectory);
|
|
96757
|
+
return input.projectDirectories.find((projectDirectory) => Path.resolve(projectDirectory) === resolvedRootDirectory) ?? null;
|
|
96758
|
+
};
|
|
96759
|
+
//#endregion
|
|
96760
|
+
//#region src/cli/utils/retry-missing-project-scores.ts
|
|
96761
|
+
const retryMissingProjectScores = async (projectScans) => mapWithConcurrency(projectScans, 64, async (projectScan) => {
|
|
96762
|
+
if (!(projectScan.result.score === null && !projectScan.isScoreDisabled && !hasIncompleteScoreAnalysis(projectScan.result.skippedChecks))) return projectScan;
|
|
96763
|
+
const score = await calculateScore(filterScansForSurface([projectScan], "score"));
|
|
96764
|
+
recordCount(METRIC.scanScoreRetry, 1, { succeeded: score !== null });
|
|
96765
|
+
return score === null ? projectScan : {
|
|
96766
|
+
...projectScan,
|
|
96767
|
+
result: {
|
|
96768
|
+
...projectScan.result,
|
|
96769
|
+
score
|
|
96770
|
+
}
|
|
96771
|
+
};
|
|
96772
|
+
});
|
|
96773
|
+
//#endregion
|
|
96304
96774
|
//#region src/cli/utils/resolve-project-relative-directory.ts
|
|
96305
96775
|
/**
|
|
96306
96776
|
* The scanned project's directory relative to the diff root, forward-slashed —
|
|
@@ -96361,6 +96831,16 @@ const resolveProjectChangedLineRanges = (rootDirectory, projectDirectory, ranges
|
|
|
96361
96831
|
});
|
|
96362
96832
|
};
|
|
96363
96833
|
//#endregion
|
|
96834
|
+
//#region src/cli/utils/resolve-project-scan.ts
|
|
96835
|
+
const resolveProjectScan = async (rootScanTarget, projectDirectory) => {
|
|
96836
|
+
const projectScanTarget = projectDirectory === rootScanTarget.resolvedDirectory ? rootScanTarget : await resolveScanTarget(projectDirectory, { allowAmbiguous: true });
|
|
96837
|
+
return {
|
|
96838
|
+
directory: projectScanTarget.resolvedDirectory,
|
|
96839
|
+
config: projectDirectory === rootScanTarget.resolvedDirectory ? rootScanTarget.userConfig : mergeReactDoctorConfigs(rootScanTarget.userConfig, projectScanTarget.userConfig ?? void 0),
|
|
96840
|
+
configSourceDirectory: projectScanTarget.userConfig?.plugins === void 0 ? rootScanTarget.configSourceDirectory : projectScanTarget.configSourceDirectory
|
|
96841
|
+
};
|
|
96842
|
+
};
|
|
96843
|
+
//#endregion
|
|
96364
96844
|
//#region src/cli/utils/build-diagnostic-issue-url.ts
|
|
96365
96845
|
const formatRuleIdentifier = (diagnostic) => `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
96366
96846
|
const buildDiagnosticIssueBody = (input) => {
|
|
@@ -96503,7 +96983,7 @@ const resolveExplainTargetDirectory = async (filePath, context) => {
|
|
|
96503
96983
|
if (context.projectFlag) {
|
|
96504
96984
|
const matchedDirectories = await selectProjects(context.resolvedDirectory, context.projectFlag, true);
|
|
96505
96985
|
if (matchedDirectories.length === 0) return context.resolvedDirectory;
|
|
96506
|
-
if (matchedDirectories.length > 1) throw new Error(
|
|
96986
|
+
if (matchedDirectories.length > 1) throw new Error(`why takes a single project; --project resolved to ${matchedDirectories.length} projects.`);
|
|
96507
96987
|
return matchedDirectories[0];
|
|
96508
96988
|
}
|
|
96509
96989
|
return findOwningProjectDirectory(context.resolvedDirectory, filePath);
|
|
@@ -96722,6 +97202,15 @@ const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
|
|
|
96722
97202
|
}
|
|
96723
97203
|
}));
|
|
96724
97204
|
};
|
|
97205
|
+
const reportSkippedProjects = (input) => {
|
|
97206
|
+
input.skippedProjects.sort((left, right) => left.directory.localeCompare(right.directory));
|
|
97207
|
+
if (input.skippedProjects.length === 0) return;
|
|
97208
|
+
recordCount(METRIC.scanProjectSkipped, input.skippedProjects.length, { reason: "max-duration" });
|
|
97209
|
+
if (!input.isQuiet) {
|
|
97210
|
+
cliLogger.warn(formatSkippedProjectsMessage(input.skippedProjects.length));
|
|
97211
|
+
cliLogger.break();
|
|
97212
|
+
}
|
|
97213
|
+
};
|
|
96725
97214
|
/**
|
|
96726
97215
|
* Post-scan finalization shared by the staged-arm and project-loop
|
|
96727
97216
|
* paths of `inspectAction`: emit the JSON report (when in JSON mode)
|
|
@@ -96736,7 +97225,7 @@ const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
|
|
|
96736
97225
|
*/
|
|
96737
97226
|
const finalizeScans = (input) => {
|
|
96738
97227
|
const baselineDeltas = input.completedScans.flatMap((scan) => scan.result.baselineDelta ? [scan.result.baselineDelta] : []);
|
|
96739
|
-
const baselineComputed = input.completedScans.length > 0 && input.completedScans.every((scan) => scan.result.baselineDelta !== void 0);
|
|
97228
|
+
const baselineComputed = input.skippedProjects.length === 0 && input.completedScans.length > 0 && input.completedScans.every((scan) => scan.result.baselineDelta !== void 0);
|
|
96740
97229
|
const baselineDegraded = input.baselineIntended && !baselineComputed;
|
|
96741
97230
|
const mode = baselineDegraded ? "diff" : input.mode;
|
|
96742
97231
|
const isReactDetected = input.completedScans.some((scan) => hasReactRuntime(scan.result.project));
|
|
@@ -96757,6 +97246,7 @@ const finalizeScans = (input) => {
|
|
|
96757
97246
|
mode,
|
|
96758
97247
|
diff: input.diff,
|
|
96759
97248
|
scans: jsonCompletedScans,
|
|
97249
|
+
skippedProjects: input.skippedProjects,
|
|
96760
97250
|
totalElapsedMilliseconds: performance$1.now() - input.startTime,
|
|
96761
97251
|
baseline,
|
|
96762
97252
|
baselineDegraded
|
|
@@ -96877,16 +97367,16 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
96877
97367
|
return;
|
|
96878
97368
|
}
|
|
96879
97369
|
const buildProjectScanContext = async (projectDirectory) => {
|
|
96880
|
-
const
|
|
96881
|
-
const scanDirectory =
|
|
97370
|
+
const projectScan = await resolveProjectScan(scanTarget, projectDirectory);
|
|
97371
|
+
const scanDirectory = projectScan.directory;
|
|
96882
97372
|
const treeRelativeDirectory = resolveProjectRelativeDirectory(resolvedDirectory, scanDirectory);
|
|
96883
97373
|
if (treeRelativeDirectory === null) return null;
|
|
96884
97374
|
return {
|
|
96885
97375
|
projectDirectory,
|
|
96886
97376
|
scanDirectory,
|
|
96887
97377
|
treeRelativeDirectory,
|
|
96888
|
-
projectConfig:
|
|
96889
|
-
projectConfigSourceDirectory:
|
|
97378
|
+
projectConfig: projectScan.config,
|
|
97379
|
+
projectConfigSourceDirectory: projectScan.configSourceDirectory
|
|
96890
97380
|
};
|
|
96891
97381
|
};
|
|
96892
97382
|
const projectScanContexts = [];
|
|
@@ -96973,6 +97463,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
96973
97463
|
includePaths: resolveProjectSourceFilePaths(resolvedDirectory, projectScan.scanDirectory, projectScan.stagedFiles.filter((stagedFile) => materializedStagedFiles.has(stagedFile)))
|
|
96974
97464
|
})).filter((projectRun) => projectRun.includePaths.length > 0);
|
|
96975
97465
|
const isMultiProject = stagedProjectRuns.length > 1;
|
|
97466
|
+
const skippedProjects = [];
|
|
96976
97467
|
if (stagedProjectRuns.length === 0) {
|
|
96977
97468
|
snapshot.cleanup();
|
|
96978
97469
|
reportNothingToScan({
|
|
@@ -96988,6 +97479,13 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
96988
97479
|
}
|
|
96989
97480
|
const scanStagedProject = async (projectRun) => {
|
|
96990
97481
|
const { projectScan, includePaths } = projectRun;
|
|
97482
|
+
if (scanDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(scanDeadlineEpochMs) === 0) {
|
|
97483
|
+
skippedProjects.push({
|
|
97484
|
+
directory: projectScan.scanDirectory,
|
|
97485
|
+
reason: "max-duration"
|
|
97486
|
+
});
|
|
97487
|
+
return null;
|
|
97488
|
+
}
|
|
96991
97489
|
const projectTempDirectory = Path.join(snapshot.tempDirectory, projectScan.treeRelativeDirectory);
|
|
96992
97490
|
const scanResult = await inspectProject(projectTempDirectory, {
|
|
96993
97491
|
...scanOptions,
|
|
@@ -97028,7 +97526,11 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97028
97526
|
});
|
|
97029
97527
|
const completedScans = stagedBatch.completedScans;
|
|
97030
97528
|
try {
|
|
97031
|
-
|
|
97529
|
+
reportSkippedProjects({
|
|
97530
|
+
skippedProjects,
|
|
97531
|
+
isQuiet
|
|
97532
|
+
});
|
|
97533
|
+
if (!isQuiet && isMultiProject && completedScans.length > 0) {
|
|
97032
97534
|
const showShareLink = !isShareOptedOut(completedScans, scanOptions.noScore) && !scanOptions.isCi;
|
|
97033
97535
|
await runPromise(printMultiProjectSummary({
|
|
97034
97536
|
completedScans,
|
|
@@ -97046,6 +97548,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97046
97548
|
}
|
|
97047
97549
|
finalizeScans({
|
|
97048
97550
|
completedScans,
|
|
97551
|
+
skippedProjects,
|
|
97049
97552
|
mode: "staged",
|
|
97050
97553
|
diff: null,
|
|
97051
97554
|
baselineIntended: false,
|
|
@@ -97121,12 +97624,28 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97121
97624
|
}
|
|
97122
97625
|
cliLogger.break();
|
|
97123
97626
|
}
|
|
97124
|
-
const
|
|
97125
|
-
const
|
|
97126
|
-
|
|
97127
|
-
|
|
97128
|
-
|
|
97129
|
-
|
|
97627
|
+
const projectScans = deduplicateProjectScans(await Promise.all(projectDirectories.map((projectDirectory) => resolveProjectScan(scanTarget, projectDirectory))));
|
|
97628
|
+
const isMultiProject = projectScans.length > 1;
|
|
97629
|
+
const rootProjectScan = projectScans.find((projectScan) => Path.resolve(projectScan.directory) === Path.resolve(resolvedDirectory));
|
|
97630
|
+
const workspaceDeadCodeOwner = resolveWorkspaceDeadCodeOwner({
|
|
97631
|
+
rootDirectory: resolvedDirectory,
|
|
97632
|
+
projectDirectories: projectScans.map((projectScan) => projectScan.directory),
|
|
97633
|
+
isRootDeadCodeEnabled: scanOptions.deadCode ?? rootProjectScan?.config?.deadCode ?? true
|
|
97634
|
+
});
|
|
97635
|
+
if (workspaceDeadCodeOwner !== null) recordCount(METRIC.scanWorkspaceDeadCodeShared, 1, { projectCount: projectScans.length });
|
|
97636
|
+
const precomputedSourceFileCounts = isMultiProject && !isDiffMode ? await collectProjectSourceFileCounts(resolvedDirectory, projectScans.map((projectScan) => projectScan.directory)) : null;
|
|
97637
|
+
const skippedProjects = [];
|
|
97638
|
+
const scanProject = async (projectScan) => {
|
|
97639
|
+
if (scanDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(scanDeadlineEpochMs) === 0) {
|
|
97640
|
+
skippedProjects.push({
|
|
97641
|
+
directory: projectScan.directory,
|
|
97642
|
+
reason: "max-duration"
|
|
97643
|
+
});
|
|
97644
|
+
return null;
|
|
97645
|
+
}
|
|
97646
|
+
const scanDirectory = projectScan.directory;
|
|
97647
|
+
const projectConfig = projectScan.config;
|
|
97648
|
+
const ownsWorkspaceDeadCode = scanDirectory === workspaceDeadCodeOwner;
|
|
97130
97649
|
const supplyChainEnabled = flags.supplyChain ?? projectConfig?.supplyChain?.enabled !== false;
|
|
97131
97650
|
let includePaths;
|
|
97132
97651
|
let supplyChainManifestChanged = false;
|
|
@@ -97150,12 +97669,16 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97150
97669
|
if (!isQuiet && !isMultiProject) cliLogger.dim(" ");
|
|
97151
97670
|
const scanResult = await inspectProject(scanDirectory, {
|
|
97152
97671
|
...scanOptions,
|
|
97672
|
+
deadCode: workspaceDeadCodeOwner === null ? scanOptions.deadCode : ownsWorkspaceDeadCode,
|
|
97673
|
+
precomputedSourceFileCount: precomputedSourceFileCounts?.get(scanDirectory),
|
|
97153
97674
|
deadlineEpochMs: scanDeadlineEpochMs,
|
|
97154
97675
|
includePaths,
|
|
97155
97676
|
configOverride: projectConfig,
|
|
97156
|
-
configSourceDirectory:
|
|
97677
|
+
configSourceDirectory: projectScan.configSourceDirectory ?? void 0,
|
|
97157
97678
|
suppressRendering: isMultiProject,
|
|
97158
97679
|
concurrentScan: isMultiProject,
|
|
97680
|
+
excludedProjectDirectories: projectScans.filter((candidateProjectScan) => isPathInsideDirectory(candidateProjectScan.directory, scanDirectory)).map((candidateProjectScan) => candidateProjectScan.directory),
|
|
97681
|
+
retainExcludedProjectDeadCodeDiagnostics: ownsWorkspaceDeadCode,
|
|
97159
97682
|
baseline: baselineRef !== null && projectBaselineBaseFiles !== null && projectBaselineHeadFiles !== null ? {
|
|
97160
97683
|
ref: baselineRef,
|
|
97161
97684
|
baseFiles: projectBaselineBaseFiles,
|
|
@@ -97172,12 +97695,19 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97172
97695
|
};
|
|
97173
97696
|
};
|
|
97174
97697
|
const projectBatch = await runProjectScanBatch({
|
|
97175
|
-
projects:
|
|
97698
|
+
projects: projectScans,
|
|
97176
97699
|
isQuiet,
|
|
97177
97700
|
isSilent: scanOptions.silent === true,
|
|
97178
97701
|
scanProject
|
|
97179
97702
|
});
|
|
97180
|
-
const completedScans = projectBatch.completedScans
|
|
97703
|
+
const completedScans = await retryMissingProjectScores(projectBatch.completedScans.map((completedScan) => ({
|
|
97704
|
+
...completedScan,
|
|
97705
|
+
isScoreDisabled: scanOptions.noScore ?? completedScan.config?.noScore ?? false
|
|
97706
|
+
})));
|
|
97707
|
+
reportSkippedProjects({
|
|
97708
|
+
skippedProjects,
|
|
97709
|
+
isQuiet
|
|
97710
|
+
});
|
|
97181
97711
|
if (!isQuiet && isMultiProject && completedScans.length > 0) {
|
|
97182
97712
|
const showShareLink = !isShareOptedOut(completedScans, scanOptions.noScore) && !scanOptions.isCi;
|
|
97183
97713
|
await runPromise(printMultiProjectSummary({
|
|
@@ -97195,6 +97725,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97195
97725
|
if (flags.outputDir && !didScansWriteDump) await runPromise(printDiagnosticsDump(selectedSurfaceDiagnostics, flags.outputDir, false, isQuiet ? "stderr" : "stdout"));
|
|
97196
97726
|
finalizeScans({
|
|
97197
97727
|
completedScans,
|
|
97728
|
+
skippedProjects,
|
|
97198
97729
|
mode: baselineRef ? "baseline" : isDiffMode ? "diff" : "full",
|
|
97199
97730
|
diff: isDiffMode ? diffInfo : null,
|
|
97200
97731
|
baselineIntended: scope === "changed" && diffInfo !== null && !diffInfo.isCurrentChanges,
|
|
@@ -97292,6 +97823,7 @@ const shouldUseTui = (input) => {
|
|
|
97292
97823
|
//#endregion
|
|
97293
97824
|
//#region src/cli/commands/scan.ts
|
|
97294
97825
|
const runScanCommand = async (input) => {
|
|
97826
|
+
if (input.flags.cache === false) process.env.REACT_DOCTOR_NO_CACHE = "1";
|
|
97295
97827
|
const nodeMajorVersion = Number(process.versions.node.split(".")[0]);
|
|
97296
97828
|
const tuiEnvironment = {
|
|
97297
97829
|
flags: input.flags,
|
|
@@ -97316,7 +97848,7 @@ const runScanCommand = async (input) => {
|
|
|
97316
97848
|
return;
|
|
97317
97849
|
}
|
|
97318
97850
|
recordCount(METRIC.cliInvoked, 1, { command: input.invocationCommand });
|
|
97319
|
-
const { runScanApp } = await import("./run-scan-app-
|
|
97851
|
+
const { runScanApp } = await import("./run-scan-app-QbtG43NL.js");
|
|
97320
97852
|
const { shouldFail } = await runScanApp({
|
|
97321
97853
|
directory: input.directory,
|
|
97322
97854
|
scanTarget,
|
|
@@ -97968,11 +98500,27 @@ const ensureWindowsUtf8Console = () => {
|
|
|
97968
98500
|
} catch {}
|
|
97969
98501
|
};
|
|
97970
98502
|
//#endregion
|
|
98503
|
+
//#region src/cli/utils/active-tui-renderer.ts
|
|
98504
|
+
let activeTuiRenderer = null;
|
|
98505
|
+
const registerActiveTuiRenderer = (renderer) => {
|
|
98506
|
+
activeTuiRenderer = renderer;
|
|
98507
|
+
return () => {
|
|
98508
|
+
if (activeTuiRenderer === renderer) activeTuiRenderer = null;
|
|
98509
|
+
};
|
|
98510
|
+
};
|
|
98511
|
+
const clearActiveTuiRenderer = () => {
|
|
98512
|
+
const renderer = activeTuiRenderer;
|
|
98513
|
+
activeTuiRenderer = null;
|
|
98514
|
+
renderer?.clear();
|
|
98515
|
+
};
|
|
98516
|
+
//#endregion
|
|
97971
98517
|
//#region src/cli/utils/exit-gracefully.ts
|
|
97972
98518
|
let didStartExiting = false;
|
|
97973
98519
|
const exitGracefully = () => {
|
|
97974
98520
|
if (didStartExiting) return process.exit(130);
|
|
97975
98521
|
didStartExiting = true;
|
|
98522
|
+
activeScanAbortRegistry.abortAll();
|
|
98523
|
+
clearActiveTuiRenderer();
|
|
97976
98524
|
try {
|
|
97977
98525
|
if (isJsonModeActive()) writeJsonErrorReport(/* @__PURE__ */ new Error("Scan cancelled by user (SIGINT/SIGTERM)"));
|
|
97978
98526
|
else console.log([
|
|
@@ -98102,6 +98650,7 @@ const ROOT_FLAG_SPEC = {
|
|
|
98102
98650
|
"--json-compact",
|
|
98103
98651
|
"--lint",
|
|
98104
98652
|
"--no-color",
|
|
98653
|
+
"--no-cache",
|
|
98105
98654
|
"--no-dead-code",
|
|
98106
98655
|
"--no-lint",
|
|
98107
98656
|
"--no-parallel",
|
|
@@ -98225,13 +98774,18 @@ const COMMAND_FLAG_SPECS = new Map([
|
|
|
98225
98774
|
longOptionsWithoutValues: new Set([
|
|
98226
98775
|
"--color",
|
|
98227
98776
|
"--help",
|
|
98777
|
+
"--no-cache",
|
|
98228
98778
|
"--no-color",
|
|
98229
98779
|
"--no-dead-code",
|
|
98230
98780
|
"--no-score",
|
|
98231
98781
|
"--no-supply-chain",
|
|
98232
98782
|
"--yes"
|
|
98233
98783
|
]),
|
|
98234
|
-
longOptionsWithRequiredValues: new Set([
|
|
98784
|
+
longOptionsWithRequiredValues: new Set([
|
|
98785
|
+
"--blocking",
|
|
98786
|
+
"--max-duration",
|
|
98787
|
+
"--project"
|
|
98788
|
+
]),
|
|
98235
98789
|
longOptionsWithOptionalValues: /* @__PURE__ */ new Set(),
|
|
98236
98790
|
shortOptionsWithoutValues: new Set(["-h", "-y"]),
|
|
98237
98791
|
shortOptionsWithRequiredValues: new Set(["-p"])
|
|
@@ -98373,6 +98927,7 @@ ${highlighter.dim("Scope:")}
|
|
|
98373
98927
|
Dead-code, supply-chain, external lint-config, custom-plugin, and health-score passes are skipped.
|
|
98374
98928
|
Standard scan flags such as ${highlighter.info("--scope")}, ${highlighter.info("--project")}, ${highlighter.info("--verbose")}, and ${highlighter.info("--json")} still work.
|
|
98375
98929
|
`;
|
|
98930
|
+
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)";
|
|
98376
98931
|
const renderCiHelpEpilog = () => `
|
|
98377
98932
|
${highlighter.dim("Examples:")}
|
|
98378
98933
|
${formatExampleLines([
|
|
@@ -98392,7 +98947,7 @@ ${highlighter.dim("Learn more:")}
|
|
|
98392
98947
|
${highlighter.info(CI_URL)}
|
|
98393
98948
|
`;
|
|
98394
98949
|
const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
|
|
98395
|
-
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>",
|
|
98950
|
+
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);
|
|
98396
98951
|
program.action((directory = ".", flags) => runScanCommand({
|
|
98397
98952
|
directory,
|
|
98398
98953
|
flags,
|
|
@@ -98418,7 +98973,7 @@ rules.command("category <category> <severity>").description("Set the severity fo
|
|
|
98418
98973
|
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()));
|
|
98419
98974
|
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()));
|
|
98420
98975
|
program.command("experimental-lsp", { hidden: false }).description("[experimental] run the React Doctor language server over stdio (for editors)").allowUnknownOption().action(() => {});
|
|
98421
|
-
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("-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({
|
|
98976
|
+
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({
|
|
98422
98977
|
directory,
|
|
98423
98978
|
flags: command.optsWithGlobals(),
|
|
98424
98979
|
invocationCommand: "experimental-tui"
|
|
@@ -98449,7 +99004,7 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
98449
99004
|
handleError(error, { sentryEventId });
|
|
98450
99005
|
});
|
|
98451
99006
|
//#endregion
|
|
98452
|
-
export {
|
|
99007
|
+
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 };
|
|
98453
99008
|
|
|
98454
99009
|
//# sourceMappingURL=cli.js.map
|
|
98455
|
-
//# debugId=
|
|
99010
|
+
//# debugId=2b3a9fe9-8109-5067-854f-a10b2e6454e7
|