react-doctor 0.9.3 → 0.9.4-dev.8b97fdc
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 +1143 -559
- package/dist/index.d.ts +9 -2
- package/dist/index.js +643 -349
- package/dist/lsp.js +655 -327
- package/dist/run-scan-app-QbtG43NL.js +2326 -0
- package/package.json +7 -6
- package/dist/run-scan-app-DHRvqfkX.js +0 -1733
package/dist/lsp.js
CHANGED
|
@@ -9,7 +9,7 @@ import { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FIL
|
|
|
9
9
|
import * as Crypto from "node:crypto";
|
|
10
10
|
import crypto, { createHash } from "node:crypto";
|
|
11
11
|
import * as NodeChildProcess from "node:child_process";
|
|
12
|
-
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
12
|
+
import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
13
13
|
import { ResolverFactory } from "oxc-resolver";
|
|
14
14
|
import ts from "typescript";
|
|
15
15
|
import * as NodeUrl from "node:url";
|
|
@@ -3647,7 +3647,7 @@ const flatMap$5 = /* @__PURE__ */ dual(2, (self, f) => isNone(self) ? none() : f
|
|
|
3647
3647
|
* @category filtering
|
|
3648
3648
|
* @since 2.0.0
|
|
3649
3649
|
*/
|
|
3650
|
-
const filter = /* @__PURE__ */ dual(2, (self, predicate) => isNone(self) ? none() : predicate(self.value) ? some(self.value) : none());
|
|
3650
|
+
const filter$1 = /* @__PURE__ */ dual(2, (self, predicate) => isNone(self) ? none() : predicate(self.value) ? some(self.value) : none());
|
|
3651
3651
|
//#endregion
|
|
3652
3652
|
//#region ../../node_modules/.pnpm/effect@4.0.0-beta.70/node_modules/effect/dist/Result.js
|
|
3653
3653
|
/**
|
|
@@ -15586,7 +15586,7 @@ function transformOptional(f) {
|
|
|
15586
15586
|
*/
|
|
15587
15587
|
function withDefault$1(defaultValue) {
|
|
15588
15588
|
return new Getter((o) => {
|
|
15589
|
-
const filtered = filter(o, isNotUndefined);
|
|
15589
|
+
const filtered = filter$1(o, isNotUndefined);
|
|
15590
15590
|
return isSome(filtered) ? succeed$2(filtered) : mapEager(defaultValue, some);
|
|
15591
15591
|
});
|
|
15592
15592
|
}
|
|
@@ -19434,6 +19434,10 @@ var JsonReportProjectEntryV3 = class extends Class("JsonReportProjectEntryV3")({
|
|
|
19434
19434
|
scannedFileCount: optional(Number$1),
|
|
19435
19435
|
elapsedMilliseconds: Number$1
|
|
19436
19436
|
}) {};
|
|
19437
|
+
var JsonReportSkippedProject = class extends Class("JsonReportSkippedProject")({
|
|
19438
|
+
directory: String$1,
|
|
19439
|
+
reason: Literal("max-duration")
|
|
19440
|
+
}) {};
|
|
19437
19441
|
/** Original full, diff, and staged report contract. */
|
|
19438
19442
|
var JsonReportV1 = class extends Class("JsonReportV1")({
|
|
19439
19443
|
schemaVersion: Literal(1),
|
|
@@ -19462,6 +19466,7 @@ var JsonReportV1 = class extends Class("JsonReportV1")({
|
|
|
19462
19466
|
reactDetected: optional(Boolean$1),
|
|
19463
19467
|
diff: NullOr(JsonReportDiffInfo),
|
|
19464
19468
|
projects: ArraySchema(JsonReportProjectEntry),
|
|
19469
|
+
skippedProjects: optional(ArraySchema(JsonReportSkippedProject)),
|
|
19465
19470
|
diagnostics: ArraySchema(Diagnostic),
|
|
19466
19471
|
summary: JsonReportSummary,
|
|
19467
19472
|
elapsedMilliseconds: Number$1,
|
|
@@ -19490,6 +19495,7 @@ var JsonReportV2 = class extends Class("JsonReportV2")({
|
|
|
19490
19495
|
diff: NullOr(JsonReportDiffInfo),
|
|
19491
19496
|
baseline: JsonReportBaseline,
|
|
19492
19497
|
projects: ArraySchema(JsonReportProjectEntry),
|
|
19498
|
+
skippedProjects: optional(ArraySchema(JsonReportSkippedProject)),
|
|
19493
19499
|
diagnostics: ArraySchema(Diagnostic),
|
|
19494
19500
|
summary: JsonReportSummary,
|
|
19495
19501
|
elapsedMilliseconds: Number$1,
|
|
@@ -19506,6 +19512,7 @@ var JsonReportV3 = class extends Class("JsonReportV3")({
|
|
|
19506
19512
|
diff: NullOr(JsonReportDiffInfo),
|
|
19507
19513
|
baseline: optional(JsonReportBaseline),
|
|
19508
19514
|
projects: ArraySchema(JsonReportProjectEntryV3),
|
|
19515
|
+
skippedProjects: optional(ArraySchema(JsonReportSkippedProject)),
|
|
19509
19516
|
diagnostics: ArraySchema(JsonReportDiagnosticV3),
|
|
19510
19517
|
summary: JsonReportSummary,
|
|
19511
19518
|
elapsedMilliseconds: Number$1,
|
|
@@ -53488,6 +53495,47 @@ const flattenArray = (self) => transformPull$1(self, (pull) => {
|
|
|
53488
53495
|
}));
|
|
53489
53496
|
});
|
|
53490
53497
|
/**
|
|
53498
|
+
* Filters arrays of elements emitted by a channel, applying the filter
|
|
53499
|
+
* to each element within the arrays and only emitting non-empty filtered arrays.
|
|
53500
|
+
*
|
|
53501
|
+
* **Example** (Filtering array output)
|
|
53502
|
+
*
|
|
53503
|
+
* ```ts
|
|
53504
|
+
* import { Array, Channel } from "effect"
|
|
53505
|
+
*
|
|
53506
|
+
* const nonEmptyArrayPredicate = Array.isReadonlyArrayNonEmpty
|
|
53507
|
+
*
|
|
53508
|
+
* // Create a channel that outputs arrays of mixed data
|
|
53509
|
+
* const arrayChannel = Channel.fromIterable([
|
|
53510
|
+
* Array.make(1, 2, 3, 4, 5),
|
|
53511
|
+
* Array.make(6, 7, 8, 9, 10),
|
|
53512
|
+
* Array.make(11, 12, 13, 14, 15)
|
|
53513
|
+
* ]).pipe(Channel.filter(nonEmptyArrayPredicate))
|
|
53514
|
+
*
|
|
53515
|
+
* // Filter arrays to keep only even numbers
|
|
53516
|
+
* const evenArraysChannel = Channel.filterArray(arrayChannel, (n) => n % 2 === 0)
|
|
53517
|
+
* // Outputs: [2, 4], [6, 8, 10], [12, 14]
|
|
53518
|
+
* // Note: Only non-empty filtered arrays are emitted
|
|
53519
|
+
*
|
|
53520
|
+
* // Arrays that would become empty after filtering are discarded entirely
|
|
53521
|
+
* const oddChannel = Channel.fromIterable([
|
|
53522
|
+
* Array.make(1, 3, 5),
|
|
53523
|
+
* Array.make(2, 4),
|
|
53524
|
+
* Array.make(7, 9)
|
|
53525
|
+
* ]).pipe(Channel.filter(nonEmptyArrayPredicate))
|
|
53526
|
+
* const filteredOddChannel = Channel.filterArray(oddChannel, (n) => n % 2 === 0)
|
|
53527
|
+
* // Outputs: [2, 4] (the arrays [1,3,5] and [7,9] are discarded)
|
|
53528
|
+
* ```
|
|
53529
|
+
*
|
|
53530
|
+
* @category filtering
|
|
53531
|
+
* @since 4.0.0
|
|
53532
|
+
*/
|
|
53533
|
+
const filterArray = /* @__PURE__ */ dual(2, (self, predicate) => transformPull$1(self, (pull) => succeed$2(flatMap$2(pull, function loop(arr) {
|
|
53534
|
+
const passes = [];
|
|
53535
|
+
for (let i = 0; i < arr.length; i++) if (predicate(arr[i])) passes.push(arr[i]);
|
|
53536
|
+
return isReadonlyArrayNonEmpty(passes) ? succeed$2(passes) : flatMap$2(pull, loop);
|
|
53537
|
+
}))));
|
|
53538
|
+
/**
|
|
53491
53539
|
* Filters and maps each element inside emitted non-empty arrays using a
|
|
53492
53540
|
* `Filter`.
|
|
53493
53541
|
*
|
|
@@ -55125,6 +55173,30 @@ const concat = /* @__PURE__ */ dual(2, (self, that) => flatten(fromArray([self,
|
|
|
55125
55173
|
*/
|
|
55126
55174
|
const merge$1 = /* @__PURE__ */ dual((args) => isStream(args[0]) && isStream(args[1]), (self, that, options) => fromChannel(merge$2(toChannel(self), toChannel(that), options)));
|
|
55127
55175
|
/**
|
|
55176
|
+
* Filters a stream to the elements that satisfy a predicate.
|
|
55177
|
+
*
|
|
55178
|
+
* **Example** (Filtering stream values)
|
|
55179
|
+
*
|
|
55180
|
+
* ```ts
|
|
55181
|
+
* import { Console, Effect, Stream } from "effect"
|
|
55182
|
+
*
|
|
55183
|
+
* const program = Effect.gen(function*() {
|
|
55184
|
+
* const stream = Stream.make(1, 2, 3, 4).pipe(
|
|
55185
|
+
* Stream.filter((n) => n % 2 === 0)
|
|
55186
|
+
* )
|
|
55187
|
+
* const values = yield* Stream.runCollect(stream)
|
|
55188
|
+
* yield* Console.log(values)
|
|
55189
|
+
* })
|
|
55190
|
+
*
|
|
55191
|
+
* Effect.runPromise(program)
|
|
55192
|
+
* // Output: [ 2, 4 ]
|
|
55193
|
+
* ```
|
|
55194
|
+
*
|
|
55195
|
+
* @category filtering
|
|
55196
|
+
* @since 2.0.0
|
|
55197
|
+
*/
|
|
55198
|
+
const filter = /* @__PURE__ */ dual(2, (self, predicate) => fromChannel(filterArray(toChannel(self), predicate)));
|
|
55199
|
+
/**
|
|
55128
55200
|
* Filters and maps stream elements in one pass using a `Filter`.
|
|
55129
55201
|
*
|
|
55130
55202
|
* @category filtering
|
|
@@ -61908,6 +61980,7 @@ const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
|
|
|
61908
61980
|
const GENERATED_SOURCE_DIRECTORY_PATTERN = /(?:^|\/)__generated__\//;
|
|
61909
61981
|
const MINIFIED_SNIFF_BYTES = 65536;
|
|
61910
61982
|
const GIT_LS_FILES_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
|
|
61983
|
+
const GIT_CHECK_ATTR_MAX_BUFFER_BYTES = GIT_LS_FILES_MAX_BUFFER_BYTES;
|
|
61911
61984
|
const SCANNED_DOT_DIRECTORIES = new Set([".dumi", ".storybook"]);
|
|
61912
61985
|
const IGNORED_DIRECTORIES = new Set([
|
|
61913
61986
|
".angular",
|
|
@@ -62216,13 +62289,9 @@ const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
|
|
|
62216
62289
|
const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
|
|
62217
62290
|
const ABORT_EXIT_CODES = new Set([134, 3221226505]);
|
|
62218
62291
|
const OXLINT_OOM_RESCUE_BUDGET_MS = 6e4;
|
|
62219
|
-
const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
|
|
62220
|
-
const LINT_PHASE_TIMEOUT_MS = 3e5;
|
|
62221
|
-
const SCAN_TOTAL_DEADLINE_MS = 9e5;
|
|
62222
62292
|
const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
|
|
62223
62293
|
const DEAD_CODE_WORKER_MEM_BUDGET_BYTES = 2 * 1024 * 1024 * 1024;
|
|
62224
62294
|
const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
|
|
62225
|
-
const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
|
|
62226
62295
|
const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
|
|
62227
62296
|
const RECOMMENDED_PNPM_MINIMUM_RELEASE_AGE_MINUTES = 10080;
|
|
62228
62297
|
const REACT_SERVER_DOM_PACKAGES = [
|
|
@@ -62269,7 +62338,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
62269
62338
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
62270
62339
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
62271
62340
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
62272
|
-
const CORE_PACKAGE_VERSION = "0.9.
|
|
62341
|
+
const CORE_PACKAGE_VERSION = "0.9.4";
|
|
62273
62342
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
62274
62343
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
62275
62344
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -62317,6 +62386,66 @@ const isPlainObject = (value) => {
|
|
|
62317
62386
|
const prototype = Object.getPrototypeOf(value);
|
|
62318
62387
|
return prototype === null || prototype === Object.prototype;
|
|
62319
62388
|
};
|
|
62389
|
+
const LINGUIST_ATTRIBUTES = ["linguist-generated", "linguist-vendored"];
|
|
62390
|
+
const DISABLED_ATTRIBUTE_VALUES = new Set([
|
|
62391
|
+
"unspecified",
|
|
62392
|
+
"unset",
|
|
62393
|
+
"false",
|
|
62394
|
+
"0",
|
|
62395
|
+
"off",
|
|
62396
|
+
"no"
|
|
62397
|
+
]);
|
|
62398
|
+
const buildInput = (relativePaths) => `${relativePaths.join("\0")}\0`;
|
|
62399
|
+
const parseIgnoredPaths = (output) => {
|
|
62400
|
+
const ignoredPaths = /* @__PURE__ */ new Set();
|
|
62401
|
+
const fields = output.split("\0");
|
|
62402
|
+
for (let fieldIndex = 0; fieldIndex + 2 < fields.length; fieldIndex += 3) {
|
|
62403
|
+
const filePath = fields[fieldIndex];
|
|
62404
|
+
const value = fields[fieldIndex + 2]?.toLowerCase();
|
|
62405
|
+
if (filePath && value && !DISABLED_ATTRIBUTE_VALUES.has(value)) ignoredPaths.add(filePath);
|
|
62406
|
+
}
|
|
62407
|
+
return ignoredPaths;
|
|
62408
|
+
};
|
|
62409
|
+
const collectGitLinguistIgnoredPaths = (rootDirectory, relativePaths) => {
|
|
62410
|
+
if (relativePaths.length === 0) return /* @__PURE__ */ new Set();
|
|
62411
|
+
const result = spawnSync("git", [
|
|
62412
|
+
"check-attr",
|
|
62413
|
+
"-z",
|
|
62414
|
+
"--stdin",
|
|
62415
|
+
...LINGUIST_ATTRIBUTES
|
|
62416
|
+
], {
|
|
62417
|
+
cwd: rootDirectory,
|
|
62418
|
+
encoding: "utf-8",
|
|
62419
|
+
input: buildInput(relativePaths),
|
|
62420
|
+
maxBuffer: GIT_CHECK_ATTR_MAX_BUFFER_BYTES
|
|
62421
|
+
});
|
|
62422
|
+
if (result.error || result.status !== 0) return /* @__PURE__ */ new Set();
|
|
62423
|
+
return parseIgnoredPaths(result.stdout);
|
|
62424
|
+
};
|
|
62425
|
+
const collectGitLinguistIgnoredPathsCooperative = (rootDirectory, relativePaths, signal) => {
|
|
62426
|
+
if (relativePaths.length === 0) return Promise.resolve(/* @__PURE__ */ new Set());
|
|
62427
|
+
return new Promise((resolve, reject) => {
|
|
62428
|
+
signal?.throwIfAborted();
|
|
62429
|
+
execFile("git", [
|
|
62430
|
+
"check-attr",
|
|
62431
|
+
"-z",
|
|
62432
|
+
"--stdin",
|
|
62433
|
+
...LINGUIST_ATTRIBUTES
|
|
62434
|
+
], {
|
|
62435
|
+
cwd: rootDirectory,
|
|
62436
|
+
encoding: "utf-8",
|
|
62437
|
+
killSignal: "SIGKILL",
|
|
62438
|
+
maxBuffer: GIT_CHECK_ATTR_MAX_BUFFER_BYTES,
|
|
62439
|
+
signal
|
|
62440
|
+
}, (error, stdout) => {
|
|
62441
|
+
if (signal?.aborted) {
|
|
62442
|
+
reject(signal.reason);
|
|
62443
|
+
return;
|
|
62444
|
+
}
|
|
62445
|
+
resolve(error ? /* @__PURE__ */ new Set() : parseIgnoredPaths(stdout));
|
|
62446
|
+
}).stdin?.end(buildInput(relativePaths));
|
|
62447
|
+
});
|
|
62448
|
+
};
|
|
62320
62449
|
const lastSourceMappingUrl = (fileContent) => {
|
|
62321
62450
|
const lastReference = [...fileContent.matchAll(/\/\/# sourceMappingURL=(\S+)/g)].at(-1);
|
|
62322
62451
|
return lastReference ? lastReference[1] : null;
|
|
@@ -62421,6 +62550,9 @@ function* walkSourceTreeFiles(rootDirectory) {
|
|
|
62421
62550
|
}
|
|
62422
62551
|
}
|
|
62423
62552
|
}
|
|
62553
|
+
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
62554
|
+
setImmediate(resolve);
|
|
62555
|
+
});
|
|
62424
62556
|
const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
|
|
62425
62557
|
const entries = [];
|
|
62426
62558
|
for (const relativePath of relativePaths) {
|
|
@@ -62434,23 +62566,55 @@ const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
|
|
|
62434
62566
|
}
|
|
62435
62567
|
return entries;
|
|
62436
62568
|
};
|
|
62437
|
-
const
|
|
62569
|
+
const collectSizedSourceFilesCooperative = async (rootDirectory, relativePaths, signal) => {
|
|
62570
|
+
const entries = [];
|
|
62571
|
+
let sliceStartedAt = performance.now();
|
|
62572
|
+
for (const relativePath of relativePaths) {
|
|
62573
|
+
signal?.throwIfAborted();
|
|
62574
|
+
const absolutePath = Path.resolve(rootDirectory, relativePath);
|
|
62575
|
+
const sizeBytes = statSourceFileSize(absolutePath);
|
|
62576
|
+
if (!isLargeMinifiedFile(absolutePath, sizeBytes)) entries.push({
|
|
62577
|
+
path: relativePath,
|
|
62578
|
+
sizeBytes: sizeBytes ?? 0
|
|
62579
|
+
});
|
|
62580
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
62581
|
+
await yieldToEventLoop();
|
|
62582
|
+
sliceStartedAt = performance.now();
|
|
62583
|
+
}
|
|
62584
|
+
}
|
|
62585
|
+
return entries;
|
|
62586
|
+
};
|
|
62587
|
+
const parseGitSourceFilePaths = (output) => {
|
|
62588
|
+
const trackedPaths = /* @__PURE__ */ new Set();
|
|
62589
|
+
const untrackedPaths = [];
|
|
62590
|
+
for (const entry of output.split("\0")) {
|
|
62591
|
+
if (entry.length === 0) continue;
|
|
62592
|
+
const trackedPath = /^[0-7]{6} [0-9a-f]+ [0-3]\t([\s\S]+)$/.exec(entry)?.[1];
|
|
62593
|
+
if (trackedPath === void 0) untrackedPaths.push(entry);
|
|
62594
|
+
else trackedPaths.add(trackedPath);
|
|
62595
|
+
}
|
|
62596
|
+
return {
|
|
62597
|
+
trackedPaths: [...trackedPaths],
|
|
62598
|
+
untrackedPaths
|
|
62599
|
+
};
|
|
62600
|
+
};
|
|
62601
|
+
const listGitSourceFilePaths = (rootDirectory) => {
|
|
62438
62602
|
const result = spawnSync("git", [
|
|
62439
62603
|
"ls-files",
|
|
62440
62604
|
"-z",
|
|
62441
|
-
|
|
62605
|
+
"--stage",
|
|
62606
|
+
"--others",
|
|
62607
|
+
"--exclude-standard"
|
|
62442
62608
|
], {
|
|
62443
62609
|
cwd: rootDirectory,
|
|
62444
62610
|
encoding: "utf-8",
|
|
62445
62611
|
maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES
|
|
62446
62612
|
});
|
|
62447
62613
|
if (result.error || result.status !== 0) return null;
|
|
62448
|
-
return result.stdout
|
|
62614
|
+
return parseGitSourceFilePaths(result.stdout);
|
|
62449
62615
|
};
|
|
62450
|
-
const
|
|
62451
|
-
const trackedPaths =
|
|
62452
|
-
const untrackedPaths = gitLsFiles(rootDirectory, ["--others", "--exclude-standard"]);
|
|
62453
|
-
if (trackedPaths === null || untrackedPaths === null) return null;
|
|
62616
|
+
const collectGitCandidateSourceFilePaths = (rootDirectory, paths) => {
|
|
62617
|
+
const { trackedPaths, untrackedPaths } = paths;
|
|
62454
62618
|
const emitDuplicateJsPaths = collectTypeScriptEmitDuplicateJsPaths({
|
|
62455
62619
|
trackedPaths: new Set(trackedPaths),
|
|
62456
62620
|
untrackedPaths,
|
|
@@ -62458,12 +62622,65 @@ const listSourceFilesViaGit = (rootDirectory) => {
|
|
|
62458
62622
|
});
|
|
62459
62623
|
return [...trackedPaths, ...untrackedPaths].filter((filePath) => isLintableSourceFile(filePath) && !hasIgnoredPathSegment(filePath) && !emitDuplicateJsPaths.has(filePath));
|
|
62460
62624
|
};
|
|
62625
|
+
const filterGitSourceFilePaths = (rootDirectory, paths) => {
|
|
62626
|
+
const candidatePaths = collectGitCandidateSourceFilePaths(rootDirectory, paths);
|
|
62627
|
+
const linguistIgnoredPaths = collectGitLinguistIgnoredPaths(rootDirectory, candidatePaths);
|
|
62628
|
+
return candidatePaths.filter((filePath) => !linguistIgnoredPaths.has(filePath));
|
|
62629
|
+
};
|
|
62630
|
+
const listSourceFilesViaGit = (rootDirectory) => {
|
|
62631
|
+
const paths = listGitSourceFilePaths(rootDirectory);
|
|
62632
|
+
return paths === null ? null : filterGitSourceFilePaths(rootDirectory, paths);
|
|
62633
|
+
};
|
|
62634
|
+
const listSourceFilesViaGitCooperative = async (rootDirectory, signal) => {
|
|
62635
|
+
const paths = await new Promise((resolve, reject) => {
|
|
62636
|
+
signal?.throwIfAborted();
|
|
62637
|
+
execFile("git", [
|
|
62638
|
+
"ls-files",
|
|
62639
|
+
"-z",
|
|
62640
|
+
"--stage",
|
|
62641
|
+
"--others",
|
|
62642
|
+
"--exclude-standard"
|
|
62643
|
+
], {
|
|
62644
|
+
cwd: rootDirectory,
|
|
62645
|
+
encoding: "utf-8",
|
|
62646
|
+
killSignal: "SIGKILL",
|
|
62647
|
+
maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES,
|
|
62648
|
+
signal
|
|
62649
|
+
}, (error, stdout) => {
|
|
62650
|
+
if (signal?.aborted) {
|
|
62651
|
+
reject(signal.reason);
|
|
62652
|
+
return;
|
|
62653
|
+
}
|
|
62654
|
+
resolve(error ? null : parseGitSourceFilePaths(stdout));
|
|
62655
|
+
});
|
|
62656
|
+
});
|
|
62657
|
+
if (paths === null) return null;
|
|
62658
|
+
const candidatePaths = collectGitCandidateSourceFilePaths(rootDirectory, paths);
|
|
62659
|
+
const linguistIgnoredPaths = await collectGitLinguistIgnoredPathsCooperative(rootDirectory, candidatePaths, signal);
|
|
62660
|
+
return candidatePaths.filter((filePath) => !linguistIgnoredPaths.has(filePath));
|
|
62661
|
+
};
|
|
62461
62662
|
const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
62462
62663
|
const filePaths = [];
|
|
62463
62664
|
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) if (isLintableSourceFile(name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
|
|
62464
62665
|
return filePaths;
|
|
62465
62666
|
};
|
|
62466
|
-
const
|
|
62667
|
+
const listSourceFilesViaFilesystemCooperative = async (rootDirectory, signal) => {
|
|
62668
|
+
const filePaths = [];
|
|
62669
|
+
let sliceStartedAt = performance.now();
|
|
62670
|
+
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
|
|
62671
|
+
signal?.throwIfAborted();
|
|
62672
|
+
if (isLintableSourceFile(name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
|
|
62673
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
62674
|
+
await yieldToEventLoop();
|
|
62675
|
+
sliceStartedAt = performance.now();
|
|
62676
|
+
}
|
|
62677
|
+
}
|
|
62678
|
+
return filePaths;
|
|
62679
|
+
};
|
|
62680
|
+
const listSourceFilePaths = (rootDirectory) => (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort();
|
|
62681
|
+
const listSourceFilePathsCooperative = async (rootDirectory, signal) => (await listSourceFilesViaGitCooperative(rootDirectory, signal) ?? await listSourceFilesViaFilesystemCooperative(rootDirectory, signal)).sort();
|
|
62682
|
+
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, listSourceFilePaths(rootDirectory));
|
|
62683
|
+
const listSourceFilesCooperative = async (rootDirectory, signal) => (await collectSizedSourceFilesCooperative(rootDirectory, await listSourceFilePathsCooperative(rootDirectory, signal), signal)).map((entry) => entry.path);
|
|
62467
62684
|
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
62468
62685
|
const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
|
|
62469
62686
|
const UTF8_BOM_CHARACTER = "";
|
|
@@ -64041,13 +64258,7 @@ const hasReactDependency = (packageJson) => {
|
|
|
64041
64258
|
};
|
|
64042
64259
|
return Object.keys(allDependencies).some((packageName) => REACT_DEPENDENCY_NAMES.has(packageName));
|
|
64043
64260
|
};
|
|
64044
|
-
const getPreactVersion = (packageJson) =>
|
|
64045
|
-
return {
|
|
64046
|
-
...packageJson.peerDependencies,
|
|
64047
|
-
...packageJson.dependencies,
|
|
64048
|
-
...packageJson.devDependencies
|
|
64049
|
-
}.preact ?? null;
|
|
64050
|
-
};
|
|
64261
|
+
const getPreactVersion = (packageJson) => getDependencySpec(packageJson, "preact");
|
|
64051
64262
|
const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, sourceDirectory = rootDirectory, sourcePackageJson = rootPackageJson, version }) => {
|
|
64052
64263
|
if (version === null || !isCatalogReference(version)) return version;
|
|
64053
64264
|
const catalogName = extractCatalogName(version);
|
|
@@ -64792,7 +65003,16 @@ const getBrowserslistProtectionRequirement = (directory) => {
|
|
|
64792
65003
|
if (!queries) return void 0;
|
|
64793
65004
|
let requirement;
|
|
64794
65005
|
let hasUnsupportedTarget = false;
|
|
64795
|
-
|
|
65006
|
+
const previousIgnoreOldData = process.env.BROWSERSLIST_IGNORE_OLD_DATA;
|
|
65007
|
+
let browserTargets = [];
|
|
65008
|
+
try {
|
|
65009
|
+
process.env.BROWSERSLIST_IGNORE_OLD_DATA = "true";
|
|
65010
|
+
browserTargets = (0, import_browserslist.default)(queries);
|
|
65011
|
+
} finally {
|
|
65012
|
+
if (previousIgnoreOldData === void 0) delete process.env.BROWSERSLIST_IGNORE_OLD_DATA;
|
|
65013
|
+
else process.env.BROWSERSLIST_IGNORE_OLD_DATA = previousIgnoreOldData;
|
|
65014
|
+
}
|
|
65015
|
+
for (const browserTarget of browserTargets) {
|
|
64796
65016
|
const browserRequirement = getBrowserProtectionRequirement(browserTarget);
|
|
64797
65017
|
if (browserRequirement === "unsupported") hasUnsupportedTarget = true;
|
|
64798
65018
|
if (browserRequirement === "noreferrer") requirement = "noreferrer";
|
|
@@ -64902,9 +65122,9 @@ const clearProjectCache = () => {
|
|
|
64902
65122
|
* framework-agnostic rules. Throws only when the directory has nothing
|
|
64903
65123
|
* to scan (no enclosing project and no source files of its own).
|
|
64904
65124
|
*/
|
|
64905
|
-
const discoverProjectWithoutPackageJson = (directory) => {
|
|
64906
|
-
const sourceFileCount = countSourceFiles(directory);
|
|
64907
|
-
const hasOwnTsConfig =
|
|
65125
|
+
const discoverProjectWithoutPackageJson = (directory, options) => {
|
|
65126
|
+
const sourceFileCount = options.sourceFileCount ?? countSourceFiles(directory);
|
|
65127
|
+
const hasOwnTsConfig = isFile(Path.join(directory, "tsconfig.json"));
|
|
64908
65128
|
const enclosingProjectRoot = findNearestAncestorPackageJson(directory);
|
|
64909
65129
|
const enclosingProject = enclosingProjectRoot !== null ? discoverProject(enclosingProjectRoot) : null;
|
|
64910
65130
|
if (enclosingProject !== null) return {
|
|
@@ -64970,12 +65190,15 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
64970
65190
|
sourceFileCount
|
|
64971
65191
|
};
|
|
64972
65192
|
};
|
|
64973
|
-
const discoverProject = (directory) => {
|
|
65193
|
+
const discoverProject = (directory, options = {}) => {
|
|
64974
65194
|
const cached = cachedProjectInfos.get(directory);
|
|
64975
|
-
if (cached !== void 0) return cached
|
|
65195
|
+
if (cached !== void 0) return options.sourceFileCount === void 0 ? cached : {
|
|
65196
|
+
...cached,
|
|
65197
|
+
sourceFileCount: options.sourceFileCount
|
|
65198
|
+
};
|
|
64976
65199
|
const packageJsonPath = Path.join(directory, "package.json");
|
|
64977
65200
|
if (!isFile(packageJsonPath)) {
|
|
64978
|
-
const synthesized = discoverProjectWithoutPackageJson(directory);
|
|
65201
|
+
const synthesized = discoverProjectWithoutPackageJson(directory, options);
|
|
64979
65202
|
cachedProjectInfos.set(directory, synthesized);
|
|
64980
65203
|
return synthesized;
|
|
64981
65204
|
}
|
|
@@ -65037,8 +65260,8 @@ const discoverProject = (directory) => {
|
|
|
65037
65260
|
const tailwindVersion = tailwindcss.version;
|
|
65038
65261
|
const zodVersion = zod.version;
|
|
65039
65262
|
const projectName = packageJson.name ?? Path.basename(directory);
|
|
65040
|
-
const hasTypeScript =
|
|
65041
|
-
const sourceFileCount = countSourceFiles(directory);
|
|
65263
|
+
const hasTypeScript = isFile(Path.join(directory, "tsconfig.json"));
|
|
65264
|
+
const sourceFileCount = options.sourceFileCount ?? countSourceFiles(directory);
|
|
65042
65265
|
const hasReactNativeWorkspace = framework === "expo" || framework === "react-native" || workspaceFacts.hasReactNativeAwarePackage;
|
|
65043
65266
|
const expoVersion = hasReactNativeWorkspace ? resolveCatalogBackedDependencyVersion({
|
|
65044
65267
|
rootDirectory: directory,
|
|
@@ -65167,134 +65390,6 @@ const discoverProject = (directory) => {
|
|
|
65167
65390
|
* single source file has nothing to analyze.
|
|
65168
65391
|
*/
|
|
65169
65392
|
const isAnalyzableProject = (project) => project.reactVersion !== null || project.preactVersion !== null || project.sourceFileCount > 0;
|
|
65170
|
-
const messageFromUnknown = (error) => error instanceof Error ? error.message : String(error);
|
|
65171
|
-
var InvalidGlobPatternError = class extends Error {
|
|
65172
|
-
pattern;
|
|
65173
|
-
reason;
|
|
65174
|
-
constructor(pattern, reason) {
|
|
65175
|
-
super(`Invalid glob pattern ${JSON.stringify(pattern)}: ${reason}`);
|
|
65176
|
-
this.name = "InvalidGlobPatternError";
|
|
65177
|
-
this.pattern = pattern;
|
|
65178
|
-
this.reason = reason;
|
|
65179
|
-
}
|
|
65180
|
-
};
|
|
65181
|
-
const assertGlobPattern = (condition, pattern, reason) => {
|
|
65182
|
-
if (!condition) throw new InvalidGlobPatternError(pattern, reason);
|
|
65183
|
-
};
|
|
65184
|
-
const countGlobWildcards = (pattern) => (pattern.match(/[*?]/g) ?? []).length;
|
|
65185
|
-
const normalizeGlobPattern = (pattern) => pattern.replace(/\\/g, "/").replace(/^\//, "");
|
|
65186
|
-
const PICOMATCH_OPTIONS = {
|
|
65187
|
-
dot: true,
|
|
65188
|
-
strictSlashes: false,
|
|
65189
|
-
windows: false
|
|
65190
|
-
};
|
|
65191
|
-
const compileGlobPattern = (rawPattern) => {
|
|
65192
|
-
assertGlobPattern(typeof rawPattern === "string" && rawPattern.length > 0, String(rawPattern), "pattern must be a non-empty string.");
|
|
65193
|
-
assertGlobPattern(rawPattern.length <= MAX_GLOB_PATTERN_LENGTH_CHARS, rawPattern, `pattern length ${rawPattern.length} exceeds the maximum of ${MAX_GLOB_PATTERN_LENGTH_CHARS} characters.`);
|
|
65194
|
-
const wildcardCount = countGlobWildcards(rawPattern);
|
|
65195
|
-
assertGlobPattern(wildcardCount <= 24, rawPattern, `pattern uses ${wildcardCount} wildcards (\`*\` / \`?\`), exceeding the maximum of 24. This guards against catastrophic backtracking from pathological patterns; split the pattern into multiple smaller entries.`);
|
|
65196
|
-
try {
|
|
65197
|
-
return import_picomatch.default.makeRe(normalizeGlobPattern(rawPattern), PICOMATCH_OPTIONS);
|
|
65198
|
-
} catch (caughtError) {
|
|
65199
|
-
throw new InvalidGlobPatternError(rawPattern, messageFromUnknown(caughtError));
|
|
65200
|
-
}
|
|
65201
|
-
};
|
|
65202
|
-
const compileGlobPatternsLenient = (patterns, onInvalid) => {
|
|
65203
|
-
const compiled = [];
|
|
65204
|
-
for (const pattern of patterns) try {
|
|
65205
|
-
compiled.push(compileGlobPattern(pattern));
|
|
65206
|
-
} catch (caughtError) {
|
|
65207
|
-
if (!(caughtError instanceof InvalidGlobPatternError)) throw caughtError;
|
|
65208
|
-
onInvalid(caughtError);
|
|
65209
|
-
}
|
|
65210
|
-
return compiled;
|
|
65211
|
-
};
|
|
65212
|
-
const toRelativePath = (filePath, rootDirectory) => {
|
|
65213
|
-
const normalizedFilePath = filePath.replace(/\\/g, "/");
|
|
65214
|
-
const normalizedRoot = rootDirectory.replace(/\\/g, "/").replace(/\/$/, "") + "/";
|
|
65215
|
-
if (normalizedFilePath.startsWith(normalizedRoot)) return normalizedFilePath.slice(normalizedRoot.length);
|
|
65216
|
-
return normalizedFilePath.replace(/^\.\//, "");
|
|
65217
|
-
};
|
|
65218
|
-
const warnConfigIssue = (message) => {
|
|
65219
|
-
process.stderr.write(`[react-doctor] ${message}\n`);
|
|
65220
|
-
};
|
|
65221
|
-
const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
65222
|
-
const collectStringList = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
65223
|
-
const validateOverrideEntry = (entry, index) => {
|
|
65224
|
-
if (!isPlainObject(entry)) {
|
|
65225
|
-
warnConfigIssue(`ignore.overrides[${index}] must be an object with { files, rules }; ignoring this entry.`);
|
|
65226
|
-
return null;
|
|
65227
|
-
}
|
|
65228
|
-
if (!isStringArray(entry.files)) {
|
|
65229
|
-
warnConfigIssue(`ignore.overrides[${index}].files must be an array of strings; ignoring this entry.`);
|
|
65230
|
-
return null;
|
|
65231
|
-
}
|
|
65232
|
-
if (entry.rules !== void 0 && !isStringArray(entry.rules)) {
|
|
65233
|
-
warnConfigIssue(`ignore.overrides[${index}].rules must be an array of "plugin/rule" strings or omitted; treating as missing (override would suppress every rule for the matched files).`);
|
|
65234
|
-
return { files: entry.files };
|
|
65235
|
-
}
|
|
65236
|
-
return entry.rules === void 0 ? { files: entry.files } : {
|
|
65237
|
-
files: entry.files,
|
|
65238
|
-
rules: entry.rules
|
|
65239
|
-
};
|
|
65240
|
-
};
|
|
65241
|
-
const compileIgnoreOverrides = (userConfig) => {
|
|
65242
|
-
const overrides = userConfig?.ignore?.overrides;
|
|
65243
|
-
if (overrides === void 0) return [];
|
|
65244
|
-
if (!Array.isArray(overrides)) {
|
|
65245
|
-
warnConfigIssue(`ignore.overrides must be an array of { files, rules } entries; ignoring.`);
|
|
65246
|
-
return [];
|
|
65247
|
-
}
|
|
65248
|
-
return overrides.flatMap((entry, index) => {
|
|
65249
|
-
const validated = validateOverrideEntry(entry, index);
|
|
65250
|
-
if (!validated) return [];
|
|
65251
|
-
const filePatterns = compileGlobPatternsLenient(collectStringList(validated.files), (error) => warnConfigIssue(`ignore.overrides[${index}]: ${error.message}`));
|
|
65252
|
-
if (filePatterns.length === 0) return [];
|
|
65253
|
-
return [{
|
|
65254
|
-
filePatterns,
|
|
65255
|
-
ruleIds: new Set(collectStringList(validated.rules))
|
|
65256
|
-
}];
|
|
65257
|
-
});
|
|
65258
|
-
};
|
|
65259
|
-
const isDiagnosticIgnoredByOverrides = (diagnostic, rootDirectory, overrides) => {
|
|
65260
|
-
if (overrides.length === 0) return false;
|
|
65261
|
-
const relativeFilePath = toRelativePath(diagnostic.filePath, rootDirectory);
|
|
65262
|
-
const ruleIdentifier = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
65263
|
-
return overrides.some((override) => override.filePatterns.some((pattern) => pattern.test(relativeFilePath)) && (override.ruleIds.size === 0 || override.ruleIds.has(ruleIdentifier)));
|
|
65264
|
-
};
|
|
65265
|
-
const SEVERITY_FOR_OVERRIDE = {
|
|
65266
|
-
error: "error",
|
|
65267
|
-
warn: "warning"
|
|
65268
|
-
};
|
|
65269
|
-
const restampSeverity = (diagnostic, override) => {
|
|
65270
|
-
const targetSeverity = SEVERITY_FOR_OVERRIDE[override];
|
|
65271
|
-
if (diagnostic.severity === targetSeverity) return diagnostic;
|
|
65272
|
-
return {
|
|
65273
|
-
...diagnostic,
|
|
65274
|
-
severity: targetSeverity
|
|
65275
|
-
};
|
|
65276
|
-
};
|
|
65277
|
-
/**
|
|
65278
|
-
* Assembles the internal `RuleSeverityControls` shape from a user
|
|
65279
|
-
* config's top-level `rules` and `categories` fields — the
|
|
65280
|
-
* ESLint / oxlint-shaped severity surface.
|
|
65281
|
-
*
|
|
65282
|
-
* Returns `undefined` when neither field is present so the common
|
|
65283
|
-
* path (no severity config at all) stays allocation-free for
|
|
65284
|
-
* downstream consumers.
|
|
65285
|
-
*/
|
|
65286
|
-
const buildRuleSeverityControls = (config) => {
|
|
65287
|
-
if (!config) return void 0;
|
|
65288
|
-
if (config.rules === void 0 && config.categories === void 0 && config.buckets === void 0) return;
|
|
65289
|
-
return {
|
|
65290
|
-
...config.rules !== void 0 ? { rules: config.rules } : {},
|
|
65291
|
-
...config.categories !== void 0 ? { categories: config.categories } : {},
|
|
65292
|
-
...config.buckets !== void 0 ? { buckets: config.buckets } : {}
|
|
65293
|
-
};
|
|
65294
|
-
};
|
|
65295
|
-
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
65296
|
-
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
65297
|
-
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
65298
65393
|
const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
65299
65394
|
"effect/no-adjust-state-on-prop-change": "react-doctor/no-adjust-state-on-prop-change",
|
|
65300
65395
|
"effect/no-chain-state-updates": "react-doctor/no-chain-state-updates",
|
|
@@ -65433,6 +65528,139 @@ const getEquivalentRuleKeys = (ruleKey) => {
|
|
|
65433
65528
|
const nativeRuleKey = canonicalizeRuleKey(ruleKey);
|
|
65434
65529
|
return [nativeRuleKey, ...getLegacyRuleKeysForNative(nativeRuleKey)];
|
|
65435
65530
|
};
|
|
65531
|
+
const messageFromUnknown = (error) => error instanceof Error ? error.message : String(error);
|
|
65532
|
+
var InvalidGlobPatternError = class extends Error {
|
|
65533
|
+
pattern;
|
|
65534
|
+
reason;
|
|
65535
|
+
constructor(pattern, reason) {
|
|
65536
|
+
super(`Invalid glob pattern ${JSON.stringify(pattern)}: ${reason}`);
|
|
65537
|
+
this.name = "InvalidGlobPatternError";
|
|
65538
|
+
this.pattern = pattern;
|
|
65539
|
+
this.reason = reason;
|
|
65540
|
+
}
|
|
65541
|
+
};
|
|
65542
|
+
const assertGlobPattern = (condition, pattern, reason) => {
|
|
65543
|
+
if (!condition) throw new InvalidGlobPatternError(pattern, reason);
|
|
65544
|
+
};
|
|
65545
|
+
const countGlobWildcards = (pattern) => (pattern.match(/[*?]/g) ?? []).length;
|
|
65546
|
+
const normalizeGlobPattern = (pattern) => pattern.replace(/\\/g, "/").replace(/^\//, "");
|
|
65547
|
+
const PICOMATCH_OPTIONS = {
|
|
65548
|
+
dot: true,
|
|
65549
|
+
strictSlashes: false,
|
|
65550
|
+
windows: false
|
|
65551
|
+
};
|
|
65552
|
+
const compileGlobPattern = (rawPattern) => {
|
|
65553
|
+
assertGlobPattern(typeof rawPattern === "string" && rawPattern.length > 0, String(rawPattern), "pattern must be a non-empty string.");
|
|
65554
|
+
assertGlobPattern(rawPattern.length <= MAX_GLOB_PATTERN_LENGTH_CHARS, rawPattern, `pattern length ${rawPattern.length} exceeds the maximum of ${MAX_GLOB_PATTERN_LENGTH_CHARS} characters.`);
|
|
65555
|
+
const wildcardCount = countGlobWildcards(rawPattern);
|
|
65556
|
+
assertGlobPattern(wildcardCount <= 24, rawPattern, `pattern uses ${wildcardCount} wildcards (\`*\` / \`?\`), exceeding the maximum of 24. This guards against catastrophic backtracking from pathological patterns; split the pattern into multiple smaller entries.`);
|
|
65557
|
+
try {
|
|
65558
|
+
return import_picomatch.default.makeRe(normalizeGlobPattern(rawPattern), PICOMATCH_OPTIONS);
|
|
65559
|
+
} catch (caughtError) {
|
|
65560
|
+
throw new InvalidGlobPatternError(rawPattern, messageFromUnknown(caughtError));
|
|
65561
|
+
}
|
|
65562
|
+
};
|
|
65563
|
+
const compileGlobPatternsLenient = (patterns, onInvalid) => {
|
|
65564
|
+
const compiled = [];
|
|
65565
|
+
for (const pattern of patterns) try {
|
|
65566
|
+
compiled.push(compileGlobPattern(pattern));
|
|
65567
|
+
} catch (caughtError) {
|
|
65568
|
+
if (!(caughtError instanceof InvalidGlobPatternError)) throw caughtError;
|
|
65569
|
+
onInvalid(caughtError);
|
|
65570
|
+
}
|
|
65571
|
+
return compiled;
|
|
65572
|
+
};
|
|
65573
|
+
const toRelativePath = (filePath, rootDirectory) => {
|
|
65574
|
+
const normalizedFilePath = filePath.replace(/\\/g, "/");
|
|
65575
|
+
const normalizedRoot = rootDirectory.replace(/\\/g, "/").replace(/\/$/, "") + "/";
|
|
65576
|
+
if (normalizedFilePath.startsWith(normalizedRoot)) return normalizedFilePath.slice(normalizedRoot.length);
|
|
65577
|
+
return normalizedFilePath.replace(/^\.\//, "");
|
|
65578
|
+
};
|
|
65579
|
+
const warnConfigIssue = (message) => {
|
|
65580
|
+
process.stderr.write(`[react-doctor] ${message}\n`);
|
|
65581
|
+
};
|
|
65582
|
+
const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
65583
|
+
const collectStringList = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
65584
|
+
const hasMatchingRuleOverride = (ruleIds, ruleIdentifier) => {
|
|
65585
|
+
if (ruleIds.size === 0) return true;
|
|
65586
|
+
for (const ruleId of ruleIds) if (isSameRuleKey(ruleId, ruleIdentifier)) return true;
|
|
65587
|
+
return false;
|
|
65588
|
+
};
|
|
65589
|
+
const validateOverrideEntry = (entry, index) => {
|
|
65590
|
+
if (!isPlainObject(entry)) {
|
|
65591
|
+
warnConfigIssue(`ignore.overrides[${index}] must be an object with { files, rules }; ignoring this entry.`);
|
|
65592
|
+
return null;
|
|
65593
|
+
}
|
|
65594
|
+
if (!isStringArray(entry.files)) {
|
|
65595
|
+
warnConfigIssue(`ignore.overrides[${index}].files must be an array of strings; ignoring this entry.`);
|
|
65596
|
+
return null;
|
|
65597
|
+
}
|
|
65598
|
+
if (entry.rules !== void 0 && !isStringArray(entry.rules)) {
|
|
65599
|
+
warnConfigIssue(`ignore.overrides[${index}].rules must be an array of "plugin/rule" strings or omitted; treating as missing (override would suppress every rule for the matched files).`);
|
|
65600
|
+
return { files: entry.files };
|
|
65601
|
+
}
|
|
65602
|
+
return entry.rules === void 0 ? { files: entry.files } : {
|
|
65603
|
+
files: entry.files,
|
|
65604
|
+
rules: entry.rules
|
|
65605
|
+
};
|
|
65606
|
+
};
|
|
65607
|
+
const compileIgnoreOverrides = (userConfig) => {
|
|
65608
|
+
const overrides = userConfig?.ignore?.overrides;
|
|
65609
|
+
if (overrides === void 0) return [];
|
|
65610
|
+
if (!Array.isArray(overrides)) {
|
|
65611
|
+
warnConfigIssue(`ignore.overrides must be an array of { files, rules } entries; ignoring.`);
|
|
65612
|
+
return [];
|
|
65613
|
+
}
|
|
65614
|
+
return overrides.flatMap((entry, index) => {
|
|
65615
|
+
const validated = validateOverrideEntry(entry, index);
|
|
65616
|
+
if (!validated) return [];
|
|
65617
|
+
const filePatterns = compileGlobPatternsLenient(collectStringList(validated.files), (error) => warnConfigIssue(`ignore.overrides[${index}]: ${error.message}`));
|
|
65618
|
+
if (filePatterns.length === 0) return [];
|
|
65619
|
+
return [{
|
|
65620
|
+
filePatterns,
|
|
65621
|
+
ruleIds: new Set(collectStringList(validated.rules))
|
|
65622
|
+
}];
|
|
65623
|
+
});
|
|
65624
|
+
};
|
|
65625
|
+
const isDiagnosticIgnoredByOverrides = (diagnostic, rootDirectory, overrides) => {
|
|
65626
|
+
if (overrides.length === 0) return false;
|
|
65627
|
+
const relativeFilePath = toRelativePath(diagnostic.filePath, rootDirectory);
|
|
65628
|
+
const ruleIdentifier = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
65629
|
+
return overrides.some((override) => override.filePatterns.some((pattern) => pattern.test(relativeFilePath)) && hasMatchingRuleOverride(override.ruleIds, ruleIdentifier));
|
|
65630
|
+
};
|
|
65631
|
+
const SEVERITY_FOR_OVERRIDE = {
|
|
65632
|
+
error: "error",
|
|
65633
|
+
warn: "warning"
|
|
65634
|
+
};
|
|
65635
|
+
const restampSeverity = (diagnostic, override) => {
|
|
65636
|
+
const targetSeverity = SEVERITY_FOR_OVERRIDE[override];
|
|
65637
|
+
if (diagnostic.severity === targetSeverity) return diagnostic;
|
|
65638
|
+
return {
|
|
65639
|
+
...diagnostic,
|
|
65640
|
+
severity: targetSeverity
|
|
65641
|
+
};
|
|
65642
|
+
};
|
|
65643
|
+
/**
|
|
65644
|
+
* Assembles the internal `RuleSeverityControls` shape from a user
|
|
65645
|
+
* config's top-level `rules` and `categories` fields — the
|
|
65646
|
+
* ESLint / oxlint-shaped severity surface.
|
|
65647
|
+
*
|
|
65648
|
+
* Returns `undefined` when neither field is present so the common
|
|
65649
|
+
* path (no severity config at all) stays allocation-free for
|
|
65650
|
+
* downstream consumers.
|
|
65651
|
+
*/
|
|
65652
|
+
const buildRuleSeverityControls = (config) => {
|
|
65653
|
+
if (!config) return void 0;
|
|
65654
|
+
if (config.rules === void 0 && config.categories === void 0 && config.buckets === void 0) return;
|
|
65655
|
+
return {
|
|
65656
|
+
...config.rules !== void 0 ? { rules: config.rules } : {},
|
|
65657
|
+
...config.categories !== void 0 ? { categories: config.categories } : {},
|
|
65658
|
+
...config.buckets !== void 0 ? { buckets: config.buckets } : {}
|
|
65659
|
+
};
|
|
65660
|
+
};
|
|
65661
|
+
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
65662
|
+
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
65663
|
+
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
65436
65664
|
const stripDescriptionTail = (ruleList) => {
|
|
65437
65665
|
const descriptionMatch = ruleList.match(/(?:^|\s)--\s/);
|
|
65438
65666
|
if (!descriptionMatch || descriptionMatch.index === void 0) return ruleList;
|
|
@@ -65845,6 +66073,10 @@ const classifyByDirectoryCohort = (packageDirectory) => {
|
|
|
65845
66073
|
current = parent;
|
|
65846
66074
|
}
|
|
65847
66075
|
};
|
|
66076
|
+
const clearPackageRoleCache = () => {
|
|
66077
|
+
cachedRoleByPackageDirectory.clear();
|
|
66078
|
+
cachedPackageDirectoryByFilename.clear();
|
|
66079
|
+
};
|
|
65848
66080
|
const classifyPackageRole = (filename) => {
|
|
65849
66081
|
if (!filename) return "unknown";
|
|
65850
66082
|
const packageDirectory = findNearestPackageDirectory(filename);
|
|
@@ -66079,12 +66311,8 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
66079
66311
|
if (rule.tags.includes("migration-hint")) return false;
|
|
66080
66312
|
return getFileContext(diagnostic.filePath) !== "production";
|
|
66081
66313
|
};
|
|
66082
|
-
const
|
|
66083
|
-
for (const
|
|
66084
|
-
return false;
|
|
66085
|
-
};
|
|
66086
|
-
const isAppOnlyRule = (ruleIdentifier) => {
|
|
66087
|
-
for (const appOnlyRuleKey of APP_ONLY_RULE_KEYS) if (isSameRuleKey(appOnlyRuleKey, ruleIdentifier)) return true;
|
|
66314
|
+
const matchesRuleKey = (ruleIdentifier, candidates) => {
|
|
66315
|
+
for (const candidate of candidates) if (isSameRuleKey(candidate, ruleIdentifier)) return true;
|
|
66088
66316
|
return false;
|
|
66089
66317
|
};
|
|
66090
66318
|
const isRnRawTextSuppressedByConfig = (diagnostic) => {
|
|
@@ -66126,12 +66354,13 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
66126
66354
|
if (explicitSeverityOverride === "off") return suppress(current, "config");
|
|
66127
66355
|
if (explicitSeverityOverride !== void 0) current = restampSeverity(current, explicitSeverityOverride);
|
|
66128
66356
|
}
|
|
66357
|
+
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
66129
66358
|
if (explicitRuleOverride === void 0) {
|
|
66130
|
-
if (
|
|
66359
|
+
if (matchesRuleKey(ruleIdentifier, APP_ONLY_RULE_KEYS) && isLibraryFile(current.filePath)) return null;
|
|
66131
66360
|
}
|
|
66132
66361
|
if (!showWarnings && current.severity === "warning" && explicitSeverityOverride !== "warn") return null;
|
|
66133
66362
|
if (userConfig) {
|
|
66134
|
-
if (
|
|
66363
|
+
if (matchesRuleKey(ruleIdentifier, ignoredRules)) return suppress(current, "config");
|
|
66135
66364
|
if (isFileIgnoredByPatterns(current.filePath, rootDirectory, ignoredFilePatterns)) return null;
|
|
66136
66365
|
if (isDiagnosticIgnoredByOverrides(current, rootDirectory, compiledOverrides)) return suppress(current, "override");
|
|
66137
66366
|
if (isRnRawTextSuppressedByConfig(current)) return null;
|
|
@@ -66140,7 +66369,6 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
66140
66369
|
if (respectInlineDisables && current.line > 0) {
|
|
66141
66370
|
const lines = getFileLines(current.filePath);
|
|
66142
66371
|
if (lines) {
|
|
66143
|
-
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
66144
66372
|
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
66145
66373
|
if (evaluation.isSuppressed) return suppress(current, evaluation.isForeignDirective ? "foreign-inline" : "inline");
|
|
66146
66374
|
if (evaluation.nearMissHint) current = {
|
|
@@ -66394,7 +66622,8 @@ const readSystemFacts$1 = () => ({
|
|
|
66394
66622
|
/**
|
|
66395
66623
|
* Auto lint-worker count: the smaller of the (cgroup-CPU-aware) core count and
|
|
66396
66624
|
* the number of `PER_WORKER_MEM_BUDGET_BYTES` workers that fit in available
|
|
66397
|
-
* memory,
|
|
66625
|
+
* memory, and the measured automatic parallel-efficiency ceiling, then clamped
|
|
66626
|
+
* to `[MIN, HARD_MAX]` by `resolveScanConcurrency`.
|
|
66398
66627
|
*
|
|
66399
66628
|
* `os.availableParallelism()` already respects cgroup CPU quotas, so the core
|
|
66400
66629
|
* term needs no help. Available memory is `os.totalmem()` floored by the cgroup
|
|
@@ -66410,7 +66639,7 @@ const readSystemFacts$1 = () => ({
|
|
|
66410
66639
|
const resolveAutoScanConcurrency = (facts = readSystemFacts$1()) => {
|
|
66411
66640
|
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
66412
66641
|
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / PER_WORKER_MEM_BUDGET_BYTES);
|
|
66413
|
-
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
66642
|
+
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers, 10));
|
|
66414
66643
|
};
|
|
66415
66644
|
const resolveLintBatchOrdering = () => {
|
|
66416
66645
|
return process.env["REACT_DOCTOR_LINT_BATCH_ORDERING"]?.trim().toLowerCase() === "arrival" ? "arrival" : "cost";
|
|
@@ -66423,24 +66652,22 @@ const resolveLintBatchOrdering = () => {
|
|
|
66423
66652
|
*/
|
|
66424
66653
|
var OxlintSpawnTimeoutMs = class extends Reference("react-doctor/OxlintSpawnTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_OXLINT_SPAWN_TIMEOUT_MS", OXLINT_SPAWN_TIMEOUT_MS) }) {};
|
|
66425
66654
|
/**
|
|
66426
|
-
* Effect-side cap on the lint phase.
|
|
66427
|
-
*
|
|
66655
|
+
* Optional Effect-side cap on the lint phase. Unbounded unless explicitly
|
|
66656
|
+
* configured through the env var or a test layer.
|
|
66428
66657
|
* Tests override via `Layer.succeed(LintPhaseTimeoutMs, ...)`.
|
|
66429
66658
|
*/
|
|
66430
|
-
var LintPhaseTimeoutMs = class extends Reference("react-doctor/LintPhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_LINT_PHASE_TIMEOUT_MS",
|
|
66659
|
+
var LintPhaseTimeoutMs = class extends Reference("react-doctor/LintPhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_LINT_PHASE_TIMEOUT_MS", null) }) {};
|
|
66431
66660
|
/**
|
|
66432
|
-
* Effect-side cap on the dead-code phase
|
|
66433
|
-
*
|
|
66434
|
-
* type-heavy projects; tests override via
|
|
66661
|
+
* Optional Effect-side cap on the dead-code phase. Unbounded unless explicitly
|
|
66662
|
+
* configured through the env var or a test layer. Tests override via
|
|
66435
66663
|
* `Layer.succeed(DeadCodePhaseTimeoutMs, ...)`.
|
|
66436
66664
|
*/
|
|
66437
|
-
var DeadCodePhaseTimeoutMs = class extends Reference("react-doctor/DeadCodePhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_DEAD_CODE_PHASE_TIMEOUT_MS",
|
|
66665
|
+
var DeadCodePhaseTimeoutMs = class extends Reference("react-doctor/DeadCodePhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_DEAD_CODE_PHASE_TIMEOUT_MS", null) }) {};
|
|
66438
66666
|
/**
|
|
66439
|
-
*
|
|
66440
|
-
*
|
|
66441
|
-
* large repos; tests override via `Layer.succeed(ScanDeadlineMs, ...)`.
|
|
66667
|
+
* Optional overall scan deadline. Unbounded unless explicitly configured
|
|
66668
|
+
* through the env var or a test layer.
|
|
66442
66669
|
*/
|
|
66443
|
-
var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SCAN_DEADLINE_MS",
|
|
66670
|
+
var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SCAN_DEADLINE_MS", null) }) {};
|
|
66444
66671
|
/**
|
|
66445
66672
|
* Wall-clock budget for the supply-chain check when it runs on a background
|
|
66446
66673
|
* fiber overlapping the lint pass. Reads from the env var on startup so the
|
|
@@ -66513,14 +66740,14 @@ var DeadCodeOverlap = class extends Reference("react-doctor/DeadCodeOverlap", {
|
|
|
66513
66740
|
/**
|
|
66514
66741
|
* How the full-scan lint pass plans its file batches. `"cost"` (the default)
|
|
66515
66742
|
* builds size-balanced LPT batches (`planLintBatches`): the same mandatory
|
|
66516
|
-
* batch count as greedy chunking
|
|
66517
|
-
*
|
|
66743
|
+
* batch count as greedy chunking, but every batch gets an even share of files
|
|
66744
|
+
* AND bytes, so no full chunk is a straggler while
|
|
66518
66745
|
* the remainder-batch worker idles — and the heavy files are SPREAD across
|
|
66519
66746
|
* batches, the precondition the old sort-desc-then-chunk-100 `cost` mode
|
|
66520
66747
|
* lacked (it packed the heaviest files into one wave-1 straggler batch,
|
|
66521
66748
|
* measurably regressing size-skewed repos, which is why it never earned the
|
|
66522
66749
|
* default). `"arrival"` (`REACT_DOCTOR_LINT_BATCH_ORDERING=arrival`) is the
|
|
66523
|
-
* rollback hatch to plain greedy
|
|
66750
|
+
* rollback hatch to plain greedy fixed-size chunking in discovery order. Tests
|
|
66524
66751
|
* override via `Layer.succeed(LintBatchOrdering, ...)`. Diff / staged scans
|
|
66525
66752
|
* never reach this — they pass user-scoped `includePaths` that skip discovery
|
|
66526
66753
|
* and stay in arrival order; only the full-scan branch reads it.
|
|
@@ -66615,6 +66842,7 @@ const DEFAULT_SURFACE_EXCLUDED_TAGS = {
|
|
|
66615
66842
|
score: ["design"],
|
|
66616
66843
|
ciFailure: ["design"]
|
|
66617
66844
|
};
|
|
66845
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
66618
66846
|
const VALID_RULE_SEVERITIES = [
|
|
66619
66847
|
"error",
|
|
66620
66848
|
"warn",
|
|
@@ -66657,7 +66885,6 @@ const SURFACE_CONTROL_FIELD_NAMES = [
|
|
|
66657
66885
|
];
|
|
66658
66886
|
const isIncludedFileContext = (value) => VALID_INCLUDED_FILE_CONTEXTS.some((fileContext) => fileContext === value);
|
|
66659
66887
|
const SEVERITY_FIELD_NAMES = ["rules", "categories"];
|
|
66660
|
-
const isPlainObject$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
66661
66888
|
const formatType = (value) => typeof value === "string" ? `"${value}"` : typeof value;
|
|
66662
66889
|
const isRuleSeverity = (value) => typeof value === "string" && VALID_RULE_SEVERITIES.includes(value);
|
|
66663
66890
|
const coerceMaybeBooleanString = (fieldName, value) => {
|
|
@@ -66685,7 +66912,7 @@ const validateStringArrayField = (fieldName, value) => {
|
|
|
66685
66912
|
});
|
|
66686
66913
|
};
|
|
66687
66914
|
const validateSurfaceControls = (surface, rawControls) => {
|
|
66688
|
-
if (!
|
|
66915
|
+
if (!isRecord(rawControls)) {
|
|
66689
66916
|
warnConfigIssue(`config field "surfaces.${surface}" must be an object (got ${typeof rawControls}); ignoring this surface.`);
|
|
66690
66917
|
return;
|
|
66691
66918
|
}
|
|
@@ -66709,7 +66936,7 @@ const validateSurfaceControls = (surface, rawControls) => {
|
|
|
66709
66936
|
return validatedSurfaceControls;
|
|
66710
66937
|
};
|
|
66711
66938
|
const validateSurfacesField = (rawSurfaces) => {
|
|
66712
|
-
if (!
|
|
66939
|
+
if (!isRecord(rawSurfaces)) {
|
|
66713
66940
|
warnConfigIssue(`config field "surfaces" must be an object (got ${typeof rawSurfaces}); ignoring this field.`);
|
|
66714
66941
|
return;
|
|
66715
66942
|
}
|
|
@@ -66725,7 +66952,7 @@ const validateSurfacesField = (rawSurfaces) => {
|
|
|
66725
66952
|
return validated;
|
|
66726
66953
|
};
|
|
66727
66954
|
const validateSeverityMap = (fieldName, rawMap, keysAreCategories = false) => {
|
|
66728
|
-
if (!
|
|
66955
|
+
if (!isRecord(rawMap)) {
|
|
66729
66956
|
warnConfigIssue(`config field "${fieldName}" must be an object (got ${typeof rawMap}); ignoring this field.`);
|
|
66730
66957
|
return;
|
|
66731
66958
|
}
|
|
@@ -67010,6 +67237,10 @@ const dedupeRelatedDiagnostics = (diagnostics) => {
|
|
|
67010
67237
|
}
|
|
67011
67238
|
return uniqueDiagnostics;
|
|
67012
67239
|
};
|
|
67240
|
+
const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
|
|
67241
|
+
const relativePath = Path.relative(parentAbsolutePath, childAbsolutePath);
|
|
67242
|
+
return Boolean(relativePath) && !relativePath.startsWith("..") && !Path.isAbsolute(relativePath);
|
|
67243
|
+
};
|
|
67013
67244
|
const compareStrings = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
67014
67245
|
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));
|
|
67015
67246
|
const getDirectDependencyNames = (packageJson) => new Set([...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.devDependencies ?? {})]);
|
|
@@ -68586,7 +68817,7 @@ const readScannedFile = (candidate) => {
|
|
|
68586
68817
|
return null;
|
|
68587
68818
|
}
|
|
68588
68819
|
};
|
|
68589
|
-
function* collectSecurityScanFiles(rootDirectory) {
|
|
68820
|
+
function* collectSecurityScanFiles(rootDirectory, excludedDirectories = /* @__PURE__ */ new Set()) {
|
|
68590
68821
|
const priorityCandidates = [];
|
|
68591
68822
|
const artifactCandidates = [];
|
|
68592
68823
|
const otherCandidates = [];
|
|
@@ -68601,7 +68832,7 @@ function* collectSecurityScanFiles(rootDirectory) {
|
|
|
68601
68832
|
for (const entry of readDirectoryEntries(current.absolutePath)) {
|
|
68602
68833
|
const absolutePath = Path.join(current.absolutePath, entry.name);
|
|
68603
68834
|
if (entry.isDirectory()) {
|
|
68604
|
-
if (!SKIPPED_DIRECTORY_NAMES.has(entry.name)) stack.push({
|
|
68835
|
+
if (!SKIPPED_DIRECTORY_NAMES.has(entry.name) && !excludedDirectories.has(Path.resolve(absolutePath))) stack.push({
|
|
68605
68836
|
absolutePath,
|
|
68606
68837
|
depth: current.depth + 1
|
|
68607
68838
|
});
|
|
@@ -68782,10 +69013,8 @@ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledWhe
|
|
|
68782
69013
|
}
|
|
68783
69014
|
return true;
|
|
68784
69015
|
};
|
|
69016
|
+
const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
|
|
68785
69017
|
const shouldEnableRuleByDefaultStatus = (options) => options.defaultEnabled !== false || options.hasExplicitOverride === true || options.includeTagDefaults && options.hasIncludedTags;
|
|
68786
|
-
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
68787
|
-
setImmediate(resolve);
|
|
68788
|
-
});
|
|
68789
69018
|
const createSecurityScanSession = (rootDirectory, options) => {
|
|
68790
69019
|
const capabilities = options.project ? getCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
68791
69020
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
@@ -68838,19 +69067,35 @@ const createSecurityScanSession = (rootDirectory, options) => {
|
|
|
68838
69067
|
};
|
|
68839
69068
|
};
|
|
68840
69069
|
const checkSecurityScanCooperative = async (rootDirectory, options = {}) => {
|
|
69070
|
+
const throwIfScanAborted = () => {
|
|
69071
|
+
options.signal?.throwIfAborted();
|
|
69072
|
+
};
|
|
69073
|
+
const didReachDeadline = () => {
|
|
69074
|
+
const hasDeadlineElapsed = options.deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(options.deadlineEpochMs) === 0;
|
|
69075
|
+
if (hasDeadlineElapsed) options.onDeadlineExceeded?.();
|
|
69076
|
+
return hasDeadlineElapsed;
|
|
69077
|
+
};
|
|
69078
|
+
throwIfScanAborted();
|
|
68841
69079
|
const session = createSecurityScanSession(rootDirectory, options);
|
|
68842
69080
|
if (session === null) return [];
|
|
69081
|
+
if (didReachDeadline()) return [];
|
|
68843
69082
|
let sliceStartedAt = performance.now();
|
|
68844
|
-
for (const file of collectSecurityScanFiles(rootDirectory)) {
|
|
69083
|
+
for (const file of collectSecurityScanFiles(rootDirectory, options.excludedDirectories)) {
|
|
69084
|
+
throwIfScanAborted();
|
|
69085
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
68845
69086
|
if (file === null) {
|
|
68846
69087
|
if (performance.now() - sliceStartedAt >= 12) {
|
|
68847
69088
|
await yieldToEventLoop();
|
|
69089
|
+
throwIfScanAborted();
|
|
69090
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
68848
69091
|
sliceStartedAt = performance.now();
|
|
68849
69092
|
}
|
|
68850
69093
|
continue;
|
|
68851
69094
|
}
|
|
68852
69095
|
for (const _ruleStep of session.scanFileByRule(file)) if (performance.now() - sliceStartedAt >= 12) {
|
|
68853
69096
|
await yieldToEventLoop();
|
|
69097
|
+
throwIfScanAborted();
|
|
69098
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
68854
69099
|
sliceStartedAt = performance.now();
|
|
68855
69100
|
}
|
|
68856
69101
|
}
|
|
@@ -69013,25 +69258,18 @@ const collectIgnorePatterns = (rootDirectory) => {
|
|
|
69013
69258
|
cachedPatternsByRoot.set(rootDirectory, patterns);
|
|
69014
69259
|
return patterns;
|
|
69015
69260
|
};
|
|
69016
|
-
const
|
|
69017
|
-
const KNIP_JSON_FILENAME = "knip.json";
|
|
69018
|
-
const readJsonFileSafe = (filePath) => {
|
|
69019
|
-
let rawContents;
|
|
69020
|
-
try {
|
|
69021
|
-
rawContents = fs.readFileSync(filePath, "utf-8");
|
|
69022
|
-
} catch {
|
|
69023
|
-
return null;
|
|
69024
|
-
}
|
|
69261
|
+
const failOpenReadJson = (filePath, fallback) => {
|
|
69025
69262
|
try {
|
|
69026
|
-
return JSON.parse(
|
|
69263
|
+
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
69027
69264
|
} catch {
|
|
69028
|
-
return
|
|
69265
|
+
return fallback;
|
|
69029
69266
|
}
|
|
69030
69267
|
};
|
|
69268
|
+
const KNIP_JSON_FILENAME = "knip.json";
|
|
69031
69269
|
const readKnipConfig = (rootDirectory) => {
|
|
69032
|
-
const knipJson =
|
|
69270
|
+
const knipJson = failOpenReadJson(path.join(rootDirectory, KNIP_JSON_FILENAME), null);
|
|
69033
69271
|
if (isRecord(knipJson)) return knipJson;
|
|
69034
|
-
const packageJson =
|
|
69272
|
+
const packageJson = failOpenReadJson(path.join(rootDirectory, "package.json"), null);
|
|
69035
69273
|
const packageKnipConfig = isRecord(packageJson) ? packageJson.knip : null;
|
|
69036
69274
|
return isRecord(packageKnipConfig) ? packageKnipConfig : null;
|
|
69037
69275
|
};
|
|
@@ -69079,13 +69317,6 @@ const atomicWriteJson = (filePath, value) => {
|
|
|
69079
69317
|
return;
|
|
69080
69318
|
}
|
|
69081
69319
|
};
|
|
69082
|
-
const failOpenReadJson = (filePath, fallback) => {
|
|
69083
|
-
try {
|
|
69084
|
-
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
69085
|
-
} catch {
|
|
69086
|
-
return fallback;
|
|
69087
|
-
}
|
|
69088
|
-
};
|
|
69089
69320
|
const hashFileContents = (filePath) => {
|
|
69090
69321
|
try {
|
|
69091
69322
|
return crypto.createHash("sha1").update(NFS.readFileSync(filePath)).digest("hex");
|
|
@@ -69764,7 +69995,6 @@ const isDiagnosticOnSurface = (diagnostic, surface, config) => {
|
|
|
69764
69995
|
return true;
|
|
69765
69996
|
};
|
|
69766
69997
|
const filterDiagnosticsForSurface = (diagnostics, surface, config) => diagnostics.filter((diagnostic) => isDiagnosticOnSurface(diagnostic, surface, config));
|
|
69767
|
-
const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
|
|
69768
69998
|
/**
|
|
69769
69999
|
* Budget for the dead-code phase, scaled to the work. deslop's graph build is
|
|
69770
70000
|
* CPU-bound and roughly linear in file count, so a fixed 120s cap is too tight
|
|
@@ -69772,25 +70002,28 @@ const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs
|
|
|
69772
70002
|
* tipped over by any concurrent load — silently dropping every dead-code
|
|
69773
70003
|
* finding. Scaling the budget with file count (and inversely with the core
|
|
69774
70004
|
* share when overlapped) lets the pass complete, while the ceiling still
|
|
69775
|
-
* reclaims a genuinely wedged worker.
|
|
69776
|
-
* and the Effect-side phase backstop that sits a margin above it.
|
|
70005
|
+
* reclaims a genuinely wedged worker.
|
|
69777
70006
|
*/
|
|
69778
70007
|
const resolveDeadCodeTimeout = (input) => {
|
|
69779
70008
|
const coreShareFactor = Math.max(1, input.fullConcurrency / Math.max(1, input.deadCodeConcurrency));
|
|
69780
|
-
|
|
69781
|
-
return {
|
|
69782
|
-
workerTimeoutMs,
|
|
69783
|
-
phaseTimeoutMs: workerTimeoutMs + DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS
|
|
69784
|
-
};
|
|
70009
|
+
return { workerTimeoutMs: Math.min(DEAD_CODE_TIMEOUT_CEILING_MS, Math.max(DEAD_CODE_WORKER_TIMEOUT_MS, Math.ceil(input.sourceFileCount * 30 * coreShareFactor))) };
|
|
69785
70010
|
};
|
|
69786
|
-
const resolveLintIncludePaths = (rootDirectory, userConfig) => {
|
|
70011
|
+
const resolveLintIncludePaths = (rootDirectory, userConfig, sourceFiles) => {
|
|
69787
70012
|
if (!Array.isArray(userConfig?.ignore?.files) || userConfig.ignore.files.length === 0) return;
|
|
69788
70013
|
const ignoredPatterns = compileIgnoredFilePatterns(userConfig);
|
|
69789
|
-
return listSourceFiles(rootDirectory).filter((filePath) => {
|
|
70014
|
+
return (sourceFiles ?? listSourceFiles(rootDirectory)).filter((filePath) => {
|
|
69790
70015
|
if (!isLintableSourceFile(filePath)) return false;
|
|
69791
70016
|
return !isFileIgnoredByPatterns(filePath, rootDirectory, ignoredPatterns);
|
|
69792
70017
|
});
|
|
69793
70018
|
};
|
|
70019
|
+
const filterPathsOutsideDirectories = (input) => {
|
|
70020
|
+
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}/`)));
|
|
70021
|
+
if (excludedPrefixes.length === 0) return [...input.relativePaths];
|
|
70022
|
+
return input.relativePaths.filter((relativePath) => {
|
|
70023
|
+
const normalizedRelativePath = relativePath.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
70024
|
+
return !excludedPrefixes.some((excludedPrefix) => normalizedRelativePath === excludedPrefix || normalizedRelativePath.startsWith(`${excludedPrefix}/`));
|
|
70025
|
+
});
|
|
70026
|
+
};
|
|
69794
70027
|
var Config = class Config extends Service()("react-doctor/Config") {
|
|
69795
70028
|
static layerNode = effect(Config, gen(function* () {
|
|
69796
70029
|
const cache = yield* make$3({
|
|
@@ -69852,6 +70085,7 @@ var Files = class Files extends Service()("react-doctor/Files") {
|
|
|
69852
70085
|
static layerNode = succeed$3(Files, Files.of({
|
|
69853
70086
|
readLines: (input) => sync(() => createNodeReadFileLinesSync(input.rootDirectory)(input.filePath)),
|
|
69854
70087
|
listSourceFiles: (rootDirectory) => sync(() => listSourceFiles(rootDirectory)),
|
|
70088
|
+
listSourceFilesCooperative: (input) => promise(() => listSourceFilesCooperative(input.rootDirectory, input.signal)),
|
|
69855
70089
|
isFile: (filePath) => sync(() => isFile(filePath)),
|
|
69856
70090
|
isDirectory: (filePath) => sync(() => isDirectory(filePath))
|
|
69857
70091
|
}));
|
|
@@ -69878,6 +70112,16 @@ var Files = class Files extends Service()("react-doctor/Files") {
|
|
|
69878
70112
|
}
|
|
69879
70113
|
return files;
|
|
69880
70114
|
}),
|
|
70115
|
+
listSourceFilesCooperative: (input) => sync(() => {
|
|
70116
|
+
const rootDirectory = input.rootDirectory;
|
|
70117
|
+
const prefix = rootDirectory.endsWith("/") ? rootDirectory : `${rootDirectory}/`;
|
|
70118
|
+
const files = [];
|
|
70119
|
+
for (const absolute of tree.keys()) {
|
|
70120
|
+
if (!absolute.startsWith(prefix)) continue;
|
|
70121
|
+
files.push(absolute.slice(prefix.length));
|
|
70122
|
+
}
|
|
70123
|
+
return files;
|
|
70124
|
+
}),
|
|
69881
70125
|
isFile: (filePath) => sync(() => tree.has(filePath)),
|
|
69882
70126
|
isDirectory: (filePath) => sync(() => {
|
|
69883
70127
|
const prefix = filePath.endsWith("/") ? filePath : `${filePath}/`;
|
|
@@ -70453,7 +70697,7 @@ const batchIncludePaths = (baseArgs, includePaths) => {
|
|
|
70453
70697
|
for (const filePath of includePaths) {
|
|
70454
70698
|
const entryLength = filePath.length + 1;
|
|
70455
70699
|
const exceedsArgLength = currentBatch.length > 0 && currentBatchLength + entryLength > 24e3;
|
|
70456
|
-
const exceedsFileCount = currentBatch.length >=
|
|
70700
|
+
const exceedsFileCount = currentBatch.length >= 200;
|
|
70457
70701
|
if (exceedsArgLength || exceedsFileCount) {
|
|
70458
70702
|
batches.push(currentBatch);
|
|
70459
70703
|
currentBatch = [];
|
|
@@ -70800,7 +71044,7 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
70800
71044
|
}
|
|
70801
71045
|
return enabled;
|
|
70802
71046
|
};
|
|
70803
|
-
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 }) => {
|
|
71047
|
+
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 }) => {
|
|
70804
71048
|
const hasIncludedTags = includedTags.size > 0;
|
|
70805
71049
|
const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" || hasIncludedTags ? null : resolveReactHooksJsPlugin(project.hasReactCompiler || project.hasReactCompilerLintPlugin === true, customRulesOnly);
|
|
70806
71050
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
@@ -70864,7 +71108,8 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
70864
71108
|
}))
|
|
70865
71109
|
} : {},
|
|
70866
71110
|
...project.shopifyFlashListMajorVersion !== null ? { shopifyFlashListMajorVersion: project.shopifyFlashListMajorVersion } : {},
|
|
70867
|
-
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {}
|
|
71111
|
+
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {},
|
|
71112
|
+
...projectIndexModuleSources !== void 0 ? { projectIndexModuleSources: [...projectIndexModuleSources] } : {}
|
|
70868
71113
|
} },
|
|
70869
71114
|
rules: {
|
|
70870
71115
|
...reactCompilerRules,
|
|
@@ -71113,11 +71358,17 @@ const collectUnpluginAutoImportGlobalScopes = ({ rootDirectory, candidateFiles }
|
|
|
71113
71358
|
return scopes.some((scope) => scope.names.length > 0) ? scopes : [];
|
|
71114
71359
|
};
|
|
71115
71360
|
const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
|
|
71361
|
+
const decodeDiagnostic = (raw) => {
|
|
71362
|
+
const diagnostic = validateDiagnostic$1(raw);
|
|
71363
|
+
return {
|
|
71364
|
+
...diagnostic,
|
|
71365
|
+
relatedLocations: diagnostic.relatedLocations?.map((location) => ({ ...location }))
|
|
71366
|
+
};
|
|
71367
|
+
};
|
|
71116
71368
|
const decodeFileDiagnostics = (raw) => {
|
|
71117
71369
|
if (!Array.isArray(raw)) return null;
|
|
71118
71370
|
try {
|
|
71119
|
-
|
|
71120
|
-
return raw;
|
|
71371
|
+
return raw.map((entry) => decodeDiagnostic(entry));
|
|
71121
71372
|
} catch {
|
|
71122
71373
|
return null;
|
|
71123
71374
|
}
|
|
@@ -72349,6 +72600,12 @@ const buildProfiledNodeArguments = (input) => [
|
|
|
72349
72600
|
...input.heapProfileDirectory ? ["--heap-prof", `--heap-prof-dir=${input.heapProfileDirectory}`] : [],
|
|
72350
72601
|
...input.argumentsList
|
|
72351
72602
|
];
|
|
72603
|
+
const lowerChildProcessPriority = (processId) => {
|
|
72604
|
+
if (processId === void 0) return;
|
|
72605
|
+
try {
|
|
72606
|
+
OS.setPriority(processId, OS.constants.priority.PRIORITY_BELOW_NORMAL);
|
|
72607
|
+
} catch {}
|
|
72608
|
+
};
|
|
72352
72609
|
const SANITIZED_ENV = buildOxlintChildEnv(process.env);
|
|
72353
72610
|
/**
|
|
72354
72611
|
* Spawn one oxlint subprocess with hard ceilings on wall time and
|
|
@@ -72387,6 +72644,7 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
|
|
|
72387
72644
|
"pipe"
|
|
72388
72645
|
]
|
|
72389
72646
|
});
|
|
72647
|
+
lowerChildProcessPriority(child.pid);
|
|
72390
72648
|
const onAbort = () => {
|
|
72391
72649
|
child.kill("SIGKILL");
|
|
72392
72650
|
reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) }));
|
|
@@ -72509,7 +72767,7 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
72509
72767
|
* loop with a slimmer config in that case.
|
|
72510
72768
|
*/
|
|
72511
72769
|
const spawnLintBatches = async (input) => {
|
|
72512
|
-
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, sourceMapByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth =
|
|
72770
|
+
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, sourceMapByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 9, signal, deadlineEpochMs } = input;
|
|
72513
72771
|
const resolveSourcePath = (filePath) => {
|
|
72514
72772
|
const absoluteFilePath = Path.isAbsolute(filePath) ? filePath : Path.resolve(rootDirectory, filePath);
|
|
72515
72773
|
return sourcePathByLintPath?.get(Path.normalize(absoluteFilePath)) ?? filePath;
|
|
@@ -72517,6 +72775,12 @@ const spawnLintBatches = async (input) => {
|
|
|
72517
72775
|
const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
|
|
72518
72776
|
let rescueDeadlineEpochMs;
|
|
72519
72777
|
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0 || rescueDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(rescueDeadlineEpochMs) === 0;
|
|
72778
|
+
const resolveSpawnTimeoutMs = () => {
|
|
72779
|
+
const deadlineBudgets = [deadlineEpochMs, rescueDeadlineEpochMs].filter((deadline) => deadline !== void 0).map(remainingDeadlineBudgetMs);
|
|
72780
|
+
if (deadlineBudgets.length === 0) return spawnTimeoutMs;
|
|
72781
|
+
const remainingDeadlineMs = Math.min(...deadlineBudgets);
|
|
72782
|
+
return spawnTimeoutMs === void 0 ? remainingDeadlineMs : Math.min(spawnTimeoutMs, remainingDeadlineMs);
|
|
72783
|
+
};
|
|
72520
72784
|
const runBatchPass = async (concurrency, passBatches, passOnFileProgress) => {
|
|
72521
72785
|
const totalFileCount = passBatches.reduce((sum, batch) => sum + batch.length, 0);
|
|
72522
72786
|
const allDiagnostics = [];
|
|
@@ -72534,12 +72798,13 @@ const spawnLintBatches = async (input) => {
|
|
|
72534
72798
|
const batchArgs = [...baseArgs, ...batch];
|
|
72535
72799
|
try {
|
|
72536
72800
|
const spawnBatch = () => {
|
|
72537
|
-
|
|
72801
|
+
const effectiveSpawnTimeoutMs = resolveSpawnTimeoutMs();
|
|
72802
|
+
if (effectiveSpawnTimeoutMs === 0) {
|
|
72538
72803
|
deadlineSkippedFiles.push(...batch);
|
|
72539
72804
|
batchState.deadlineSkippedFileCount += batch.length;
|
|
72540
72805
|
return Promise.resolve(null);
|
|
72541
72806
|
}
|
|
72542
|
-
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath,
|
|
72807
|
+
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, effectiveSpawnTimeoutMs, outputMaxBytes, signal, () => {
|
|
72543
72808
|
if (batchState.didStart) return;
|
|
72544
72809
|
batchState.didStart = true;
|
|
72545
72810
|
startedFileCount += batchState.initialFileCount;
|
|
@@ -72694,11 +72959,36 @@ const validateRuleRegistration = () => {
|
|
|
72694
72959
|
].filter((entry) => entry !== null).join("; ");
|
|
72695
72960
|
console.warn(`[react-doctor] rule-registration drift: ${detail}`);
|
|
72696
72961
|
};
|
|
72962
|
+
const PROJECT_INDEX_MODULE_SOURCES = [
|
|
72963
|
+
"next/og",
|
|
72964
|
+
"@vercel/og",
|
|
72965
|
+
"satori",
|
|
72966
|
+
"remotion"
|
|
72967
|
+
];
|
|
72968
|
+
const collectProjectIndexModuleSources = async (rootDirectory, candidateFiles) => {
|
|
72969
|
+
const foundModuleSources = /* @__PURE__ */ new Set();
|
|
72970
|
+
let sliceStartedAt = performance.now();
|
|
72971
|
+
for (const candidateFile of candidateFiles) {
|
|
72972
|
+
let sourceBuffer;
|
|
72973
|
+
try {
|
|
72974
|
+
sourceBuffer = NFS.readFileSync(Path.isAbsolute(candidateFile) ? candidateFile : Path.resolve(rootDirectory, candidateFile));
|
|
72975
|
+
} catch {
|
|
72976
|
+
continue;
|
|
72977
|
+
}
|
|
72978
|
+
for (const moduleSource of PROJECT_INDEX_MODULE_SOURCES) if (sourceBuffer.includes(`"${moduleSource}"`) || sourceBuffer.includes(`'${moduleSource}'`)) foundModuleSources.add(moduleSource);
|
|
72979
|
+
if (foundModuleSources.size === PROJECT_INDEX_MODULE_SOURCES.length) break;
|
|
72980
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
72981
|
+
await yieldToEventLoop();
|
|
72982
|
+
sliceStartedAt = performance.now();
|
|
72983
|
+
}
|
|
72984
|
+
}
|
|
72985
|
+
return [...foundModuleSources].sort();
|
|
72986
|
+
};
|
|
72697
72987
|
/**
|
|
72698
72988
|
* Balanced LPT (longest-processing-time-first) lint batch planner. Where
|
|
72699
|
-
* `batchIncludePaths` greedily fills
|
|
72700
|
-
* remainder batch
|
|
72701
|
-
*
|
|
72989
|
+
* `batchIncludePaths` greedily fills fixed-size batches and leaves a small
|
|
72990
|
+
* remainder batch, so the parallel pool's wall clock is set by whichever
|
|
72991
|
+
* full chunk happened to collect the most work while
|
|
72702
72992
|
* the remainder's worker idles), this planner keeps the SAME mandatory batch
|
|
72703
72993
|
* count — `ceil(files / OXLINT_MAX_FILES_PER_BATCH)`, the JS-plugin
|
|
72704
72994
|
* perf-cliff / native-binding SIGABRT guard — and assigns files largest-first,
|
|
@@ -72727,7 +73017,7 @@ const validateRuleRegistration = () => {
|
|
|
72727
73017
|
*/
|
|
72728
73018
|
const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
72729
73019
|
if (files.length === 0) return [];
|
|
72730
|
-
const batchCount = Math.ceil(files.length /
|
|
73020
|
+
const batchCount = Math.ceil(files.length / 200);
|
|
72731
73021
|
const maxFilesPerBatch = Math.ceil(files.length / batchCount);
|
|
72732
73022
|
const baseArgsLengthChars = estimateArgsLength(baseArgs);
|
|
72733
73023
|
const descendingSizeIndexes = [...files.keys()].sort((leftIndex, rightIndex) => (sizeByFile.get(files[rightIndex]) ?? 0) - (sizeByFile.get(files[leftIndex]) ?? 0));
|
|
@@ -73256,6 +73546,7 @@ const runOxlint = async (options) => {
|
|
|
73256
73546
|
await yieldToEventLoop();
|
|
73257
73547
|
const sizedScanFiles = includePaths === void 0 ? listSourceFilesWithSize(rootDirectory) : null;
|
|
73258
73548
|
const candidateFiles = includePaths !== void 0 ? includePaths : (sizedScanFiles ?? []).map((entry) => entry.path);
|
|
73549
|
+
const projectIndexModuleSources = includePaths === void 0 && options.deadlineEpochMs === void 0 ? await collectProjectIndexModuleSources(rootDirectory, candidateFiles) : void 0;
|
|
73259
73550
|
unpluginAutoImportGlobalScopes = collectUnpluginAutoImportGlobalScopes({
|
|
73260
73551
|
rootDirectory,
|
|
73261
73552
|
candidateFiles
|
|
@@ -73278,6 +73569,7 @@ const runOxlint = async (options) => {
|
|
|
73278
73569
|
runtimeGlobals,
|
|
73279
73570
|
unpluginAutoImportGlobalScopes,
|
|
73280
73571
|
serverAuthFunctionNames,
|
|
73572
|
+
projectIndexModuleSources,
|
|
73281
73573
|
severityControls,
|
|
73282
73574
|
userPlugins,
|
|
73283
73575
|
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
|
|
@@ -73745,10 +74037,10 @@ const translateProjectInfoError = (cause, directory) => {
|
|
|
73745
74037
|
}) });
|
|
73746
74038
|
};
|
|
73747
74039
|
var Project = class Project extends Service()("react-doctor/Project") {
|
|
73748
|
-
static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (
|
|
74040
|
+
static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (input) {
|
|
73749
74041
|
return yield* try_({
|
|
73750
|
-
try: () => discoverProject(directory),
|
|
73751
|
-
catch: (cause) => translateProjectInfoError(cause, directory)
|
|
74042
|
+
try: () => discoverProject(input.directory, { sourceFileCount: input.sourceFileCount }),
|
|
74043
|
+
catch: (cause) => translateProjectInfoError(cause, input.directory)
|
|
73752
74044
|
});
|
|
73753
74045
|
}) }));
|
|
73754
74046
|
static layerOf = (projectInfo) => succeed$3(Project, Project.of({ discover: () => succeed$2(projectInfo) }));
|
|
@@ -74333,9 +74625,10 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
74333
74625
|
* outage.
|
|
74334
74626
|
*/
|
|
74335
74627
|
var SupplyChain = class SupplyChain extends Service()("react-doctor/SupplyChain") {
|
|
74336
|
-
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (
|
|
74337
|
-
|
|
74338
|
-
|
|
74628
|
+
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (configuredTimeoutMs) => checkSupplyChain({
|
|
74629
|
+
rootDirectory: input.rootDirectory,
|
|
74630
|
+
userConfig: input.userConfig,
|
|
74631
|
+
totalTimeoutMs: input.timeoutMs ?? configuredTimeoutMs
|
|
74339
74632
|
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), withSpan("SupplyChain.run"))) }));
|
|
74340
74633
|
static layerOf = (diagnostics) => succeed$3(SupplyChain, SupplyChain.of({ run: () => fromIterable$1(diagnostics) }));
|
|
74341
74634
|
};
|
|
@@ -74370,10 +74663,6 @@ const NO_HOOKS = {
|
|
|
74370
74663
|
beforeLint: () => void_,
|
|
74371
74664
|
afterLint: () => void_
|
|
74372
74665
|
};
|
|
74373
|
-
const filterMapNullable = (transform) => fromPredicateOption((value) => {
|
|
74374
|
-
const result = transform(value);
|
|
74375
|
-
return result === null ? none() : some(result);
|
|
74376
|
-
});
|
|
74377
74666
|
const fileReader = (filesService, rootDirectory) => (filePath) => {
|
|
74378
74667
|
const lines = runSync(filesService.readLines({
|
|
74379
74668
|
filePath,
|
|
@@ -74397,7 +74686,7 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
74397
74686
|
* The GitHub viewer-permission lookup is forked onto a background
|
|
74398
74687
|
* fiber here and joined late (it feeds score metadata, not
|
|
74399
74688
|
* diagnostics).
|
|
74400
|
-
* 2. beforeLint hook (e.g. CLI
|
|
74689
|
+
* 2. beforeLint hook (e.g. CLI records project telemetry)
|
|
74401
74690
|
* 3. environment checks (reduced-motion + pnpm hardening +
|
|
74402
74691
|
* expo/react-native), collected synchronously. The heavier
|
|
74403
74692
|
* content-regex security scan is forked instead (like supply-chain
|
|
@@ -74410,13 +74699,11 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
74410
74699
|
* `SupplyChainOverlapTimeoutMs` (measured from fork) so a hung
|
|
74411
74700
|
* socket can't drag out its join; on timeout it fails open to no
|
|
74412
74701
|
* diagnostics — the same outcome class as a Socket outage.
|
|
74413
|
-
* 5. Linter.run runs; DeadCode.run
|
|
74414
|
-
*
|
|
74415
|
-
*
|
|
74416
|
-
*
|
|
74417
|
-
*
|
|
74418
|
-
* fiber is joined (or interrupted, SIGKILLing its worker, on lint
|
|
74419
|
-
* failure) before diagnostics are concatenated. The afterLint hook
|
|
74702
|
+
* 5. Linter.run runs; DeadCode.run forks only when
|
|
74703
|
+
* REACT_DOCTOR_DEAD_CODE_OVERLAP=on. The default "auto" and explicit
|
|
74704
|
+
* "off" paths stay sequential. The fiber is joined (or interrupted,
|
|
74705
|
+
* SIGKILLing its worker, on lint failure) before diagnostics are
|
|
74706
|
+
* concatenated. The afterLint hook
|
|
74420
74707
|
* fires between lint and dead-code. Progress spinner labels AND the
|
|
74421
74708
|
* final diagnostic / score order stay independent of execution
|
|
74422
74709
|
* order, so terminal output is identical either way; supply-chain
|
|
@@ -74451,8 +74738,28 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74451
74738
|
const partialFailuresRef = yield* LintPartialFailures;
|
|
74452
74739
|
const resolvedConfig = yield* configService.resolve(input.directory);
|
|
74453
74740
|
const scanDirectory = resolvedConfig.resolvedDirectory;
|
|
74454
|
-
const
|
|
74741
|
+
const excludedProjectDirectories = (input.excludedProjectDirectories ?? []).map((excludedDirectory) => Path.resolve(excludedDirectory)).filter((excludedDirectory) => isPathInsideDirectory(excludedDirectory, scanDirectory));
|
|
74742
|
+
const excludedProjectRelativePaths = excludedProjectDirectories.map((excludedDirectory) => Path.relative(scanDirectory, excludedDirectory).replaceAll("\\", "/"));
|
|
74743
|
+
const isExcludedProjectDiagnostic = (diagnostic) => {
|
|
74744
|
+
const relativeFilePath = toNormalizedRelativePath(diagnostic.filePath, scanDirectory);
|
|
74745
|
+
return excludedProjectRelativePaths.some((excludedRelativePath) => relativeFilePath === excludedRelativePath || relativeFilePath.startsWith(`${excludedRelativePath}/`));
|
|
74746
|
+
};
|
|
74747
|
+
const precomputedSourceFiles = input.suppressScanSummary === true || excludedProjectDirectories.length > 0 ? yield* filesService.listSourceFilesCooperative({
|
|
74748
|
+
rootDirectory: scanDirectory,
|
|
74749
|
+
signal: input.signal
|
|
74750
|
+
}) : null;
|
|
74751
|
+
const includedPrecomputedSourceFiles = precomputedSourceFiles !== null && excludedProjectDirectories.length > 0 ? filterPathsOutsideDirectories({
|
|
74752
|
+
rootDirectory: scanDirectory,
|
|
74753
|
+
relativePaths: precomputedSourceFiles,
|
|
74754
|
+
excludedDirectories: excludedProjectDirectories
|
|
74755
|
+
}) : precomputedSourceFiles;
|
|
74756
|
+
const sourceFileCount = excludedProjectDirectories.length > 0 ? includedPrecomputedSourceFiles?.length : input.precomputedSourceFileCount ?? includedPrecomputedSourceFiles?.length;
|
|
74757
|
+
const project = yield* projectService.discover({
|
|
74758
|
+
directory: scanDirectory,
|
|
74759
|
+
sourceFileCount
|
|
74760
|
+
});
|
|
74455
74761
|
if (!isAnalyzableProject(project)) return yield* new ReactDoctorError({ reason: new NoReactDependency({ directory: scanDirectory }) });
|
|
74762
|
+
const deadCodeSourceFileCount = input.retainExcludedProjectDeadCodeDiagnostics === true && precomputedSourceFiles !== null ? precomputedSourceFiles.length : project.sourceFileCount;
|
|
74456
74763
|
const [repo, sha, defaultBranch] = yield* all([
|
|
74457
74764
|
gitService.githubRepo(scanDirectory).pipe(orElseSucceed(() => null)),
|
|
74458
74765
|
gitService.headSha(scanDirectory).pipe(orElseSucceed(() => null)),
|
|
@@ -74463,11 +74770,16 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74463
74770
|
directory: scanDirectory,
|
|
74464
74771
|
repo
|
|
74465
74772
|
}).pipe(orElseSucceed(() => null)) : succeed$2(null));
|
|
74466
|
-
|
|
74467
|
-
|
|
74773
|
+
let lintIncludePaths = (input.skipExplicitIncludePathFilter ? input.includePaths.length > 0 ? [...input.includePaths] : void 0 : computeExplicitLintIncludePaths([...input.includePaths])) ?? resolveLintIncludePaths(scanDirectory, resolvedConfig.config, includedPrecomputedSourceFiles ?? void 0);
|
|
74774
|
+
if (excludedProjectDirectories.length > 0) lintIncludePaths = filterPathsOutsideDirectories({
|
|
74775
|
+
rootDirectory: scanDirectory,
|
|
74776
|
+
relativePaths: lintIncludePaths ?? includedPrecomputedSourceFiles ?? (yield* filesService.listSourceFiles(scanDirectory)),
|
|
74777
|
+
excludedDirectories: excludedProjectDirectories
|
|
74778
|
+
});
|
|
74779
|
+
const fallbackScannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? includedPrecomputedSourceFiles ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
|
|
74468
74780
|
const beforeLint = hooks.beforeLint ?? NO_HOOKS.beforeLint;
|
|
74469
74781
|
const afterLint = hooks.afterLint ?? NO_HOOKS.afterLint;
|
|
74470
|
-
yield* beforeLint(project
|
|
74782
|
+
yield* beforeLint(project);
|
|
74471
74783
|
const isDiffMode = input.includePaths.length > 0;
|
|
74472
74784
|
const showWarnings = input.warnings ?? resolvedConfig.config?.warnings ?? true;
|
|
74473
74785
|
const transform = buildDiagnosticPipeline({
|
|
@@ -74477,7 +74789,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74477
74789
|
respectInlineDisables: input.respectInlineDisables,
|
|
74478
74790
|
showWarnings
|
|
74479
74791
|
});
|
|
74480
|
-
const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(
|
|
74792
|
+
const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(fromPredicateOption((diagnostic) => {
|
|
74793
|
+
const filteredDiagnostic = transform.apply(diagnostic);
|
|
74794
|
+
return filteredDiagnostic === null ? none() : some(filteredDiagnostic);
|
|
74795
|
+
})));
|
|
74481
74796
|
const applyPerElementPipeline = (rawStream) => filterPerElementPipeline(rawStream).pipe(tap((diagnostic) => reporterService.emit(diagnostic)));
|
|
74482
74797
|
const envCollected = yield* runCollect(applyPerElementPipeline(fromIterable$1(isDiffMode ? [] : [
|
|
74483
74798
|
...checkReducedMotion(scanDirectory),
|
|
@@ -74487,17 +74802,31 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74487
74802
|
...checkReactNativeProject(scanDirectory, project)
|
|
74488
74803
|
])));
|
|
74489
74804
|
const securityScanFailedRef = yield* make$13(false);
|
|
74490
|
-
|
|
74805
|
+
let didSecurityScanReachDeadline = false;
|
|
74806
|
+
const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(tryPromise((signal) => checkSecurityScanCooperative(scanDirectory, {
|
|
74491
74807
|
project,
|
|
74492
74808
|
ignoredTags: input.ignoredTags,
|
|
74493
74809
|
includedTags: input.includedTags,
|
|
74494
|
-
includeTagDefaults: input.includeTagDefaults
|
|
74810
|
+
includeTagDefaults: input.includeTagDefaults,
|
|
74811
|
+
excludedDirectories: new Set(excludedProjectDirectories),
|
|
74812
|
+
deadlineEpochMs: input.deadlineEpochMs,
|
|
74813
|
+
signal,
|
|
74814
|
+
onDeadlineExceeded: () => {
|
|
74815
|
+
didSecurityScanReachDeadline = true;
|
|
74816
|
+
}
|
|
74495
74817
|
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), catch_$1(() => set(securityScanFailedRef, true).pipe(as(empty$4))))))).pipe(withSpan("SecurityScan.run")));
|
|
74818
|
+
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
74819
|
+
const capOptionalToDeadline = (phaseTimeoutMs) => {
|
|
74820
|
+
if (input.deadlineEpochMs === void 0) return phaseTimeoutMs;
|
|
74821
|
+
const remainingBudgetMs = remainingDeadlineBudgetMs(input.deadlineEpochMs);
|
|
74822
|
+
return phaseTimeoutMs === null ? remainingBudgetMs : Math.min(phaseTimeoutMs, remainingBudgetMs);
|
|
74823
|
+
};
|
|
74496
74824
|
const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
|
|
74497
|
-
const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
|
|
74825
|
+
const supplyChainOverlapTimeout = capToDeadline(yield* SupplyChainOverlapTimeoutMs);
|
|
74498
74826
|
const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
|
|
74499
74827
|
rootDirectory: scanDirectory,
|
|
74500
|
-
userConfig: resolvedConfig.config
|
|
74828
|
+
userConfig: resolvedConfig.config,
|
|
74829
|
+
timeoutMs: supplyChainOverlapTimeout
|
|
74501
74830
|
}))).pipe(map$3((diagnostics) => ({
|
|
74502
74831
|
diagnostics,
|
|
74503
74832
|
timedOut: false
|
|
@@ -74521,46 +74850,49 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74521
74850
|
const scanConcurrency = resolveScanConcurrency(yield* OxlintConcurrency);
|
|
74522
74851
|
const lintPhaseTimeoutMs = yield* LintPhaseTimeoutMs;
|
|
74523
74852
|
const deadCodePhaseTimeoutMs = yield* DeadCodePhaseTimeoutMs;
|
|
74524
|
-
const resolveDeadCodePhaseTimeoutMs = (scaledPhaseTimeoutMs) => deadCodePhaseTimeoutMs === 15e4 ? scaledPhaseTimeoutMs : deadCodePhaseTimeoutMs;
|
|
74525
74853
|
const workerCountSuffix = scanConcurrency > 1 ? ` ${highlighter.dim(`[~${scanConcurrency} workers]`)}` : "";
|
|
74526
|
-
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
74527
74854
|
const shouldRunDeadCode = input.runDeadCode && !isDiffMode && (showWarnings || deadCodeMaySurfaceWhenWarningsHidden(resolvedConfig.config));
|
|
74528
74855
|
const deadCodeOverlapMode = yield* DeadCodeOverlap;
|
|
74529
74856
|
const shouldOverlapDeadCode = shouldRunDeadCode && deadCodeOverlapMode === "on";
|
|
74530
74857
|
const deadCodeParseConcurrency = shouldOverlapDeadCode ? Math.max(1, Math.floor(scanConcurrency * DEAD_CODE_OVERLAP_PARSE_SHARE)) : void 0;
|
|
74531
74858
|
const lintConcurrency = deadCodeParseConcurrency === void 0 ? scanConcurrency : Math.max(1, scanConcurrency - deadCodeParseConcurrency);
|
|
74532
|
-
const buildCollectDeadCode = (deadCodeTimeout) =>
|
|
74533
|
-
|
|
74534
|
-
|
|
74535
|
-
|
|
74536
|
-
|
|
74537
|
-
|
|
74538
|
-
|
|
74539
|
-
|
|
74540
|
-
|
|
74541
|
-
|
|
74542
|
-
|
|
74543
|
-
|
|
74544
|
-
|
|
74545
|
-
|
|
74546
|
-
|
|
74547
|
-
|
|
74548
|
-
|
|
74549
|
-
|
|
74550
|
-
|
|
74551
|
-
|
|
74552
|
-
|
|
74553
|
-
|
|
74554
|
-
|
|
74555
|
-
|
|
74859
|
+
const buildCollectDeadCode = (deadCodeTimeout) => {
|
|
74860
|
+
const collectDeadCode = runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
74861
|
+
rootDirectory: scanDirectory,
|
|
74862
|
+
parseConcurrency: deadCodeParseConcurrency,
|
|
74863
|
+
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
|
|
74864
|
+
onCacheOutcome: (didHitCache) => {
|
|
74865
|
+
deadCodeCacheHit = didHitCache;
|
|
74866
|
+
},
|
|
74867
|
+
onSummaryCacheStats: (stats) => {
|
|
74868
|
+
deadCodeSummaryCacheHits = stats.hits;
|
|
74869
|
+
deadCodeSummaryCacheMisses = stats.misses;
|
|
74870
|
+
}
|
|
74871
|
+
}).pipe(filter((diagnostic) => input.retainExcludedProjectDeadCodeDiagnostics === true || !isExcludedProjectDiagnostic(diagnostic)), catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
74872
|
+
yield* set(deadCodeFailure, {
|
|
74873
|
+
didFail: true,
|
|
74874
|
+
reason: error.message
|
|
74875
|
+
});
|
|
74876
|
+
return empty$4;
|
|
74877
|
+
}))))));
|
|
74878
|
+
const phaseTimeoutMs = deadCodeTimeout.phaseTimeoutMs;
|
|
74879
|
+
if (phaseTimeoutMs === null) return collectDeadCode;
|
|
74880
|
+
return collectDeadCode.pipe(timeoutOption(phaseTimeoutMs), flatMap$2(match$3({
|
|
74881
|
+
onNone: () => set(deadCodeFailure, {
|
|
74882
|
+
didFail: true,
|
|
74883
|
+
reason: `Dead-code analysis exceeded ${Math.round(phaseTimeoutMs / MILLISECONDS_PER_SECOND)}s and was skipped.`
|
|
74884
|
+
}).pipe(as([])),
|
|
74885
|
+
onSome: succeed$2
|
|
74886
|
+
})));
|
|
74887
|
+
};
|
|
74556
74888
|
const overlapDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
74557
|
-
sourceFileCount:
|
|
74889
|
+
sourceFileCount: deadCodeSourceFileCount,
|
|
74558
74890
|
deadCodeConcurrency: deadCodeParseConcurrency ?? scanConcurrency,
|
|
74559
74891
|
fullConcurrency: scanConcurrency
|
|
74560
74892
|
});
|
|
74561
74893
|
const deadCodeFiber = shouldOverlapDeadCode ? yield* forkChild(buildCollectDeadCode({
|
|
74562
74894
|
workerTimeoutMs: overlapDeadCodeTimeout.workerTimeoutMs,
|
|
74563
|
-
phaseTimeoutMs:
|
|
74895
|
+
phaseTimeoutMs: capOptionalToDeadline(deadCodePhaseTimeoutMs)
|
|
74564
74896
|
})) : null;
|
|
74565
74897
|
const scanProgress = yield* progressService.start("Scanning...");
|
|
74566
74898
|
const scanStartTime = Date.now();
|
|
@@ -74611,7 +74943,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74611
74943
|
});
|
|
74612
74944
|
return empty$4;
|
|
74613
74945
|
}))));
|
|
74614
|
-
const
|
|
74946
|
+
const collectLintDiagnostics = runCollect(filterPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream));
|
|
74947
|
+
const lintCollected = dedupeRelatedDiagnostics(yield* lintPhaseTimeoutMs === null ? collectLintDiagnostics : collectLintDiagnostics.pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
|
|
74615
74948
|
onNone: () => set(lintFailure, {
|
|
74616
74949
|
didFail: true,
|
|
74617
74950
|
reason: `Lint analysis exceeded ${lintPhaseTimeoutMs / MILLISECONDS_PER_SECOND}s and was skipped.`,
|
|
@@ -74641,13 +74974,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74641
74974
|
} else {
|
|
74642
74975
|
yield* scanProgress.update(`Scanned ${scannedFilesLabel}, analyzing dead code...`);
|
|
74643
74976
|
const sequentialDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
74644
|
-
sourceFileCount: totalFileCount,
|
|
74977
|
+
sourceFileCount: input.retainExcludedProjectDeadCodeDiagnostics === true ? deadCodeSourceFileCount : totalFileCount,
|
|
74645
74978
|
deadCodeConcurrency: scanConcurrency,
|
|
74646
74979
|
fullConcurrency: scanConcurrency
|
|
74647
74980
|
});
|
|
74648
74981
|
deadCodeCollected = deadCodeFiber !== null ? yield* join(deadCodeFiber) : yield* buildCollectDeadCode({
|
|
74649
74982
|
workerTimeoutMs: sequentialDeadCodeTimeout.workerTimeoutMs,
|
|
74650
|
-
phaseTimeoutMs:
|
|
74983
|
+
phaseTimeoutMs: capOptionalToDeadline(deadCodePhaseTimeoutMs)
|
|
74651
74984
|
});
|
|
74652
74985
|
}
|
|
74653
74986
|
const deadCodeFailureState = lintFailureState.didFail ? {
|
|
@@ -74691,7 +75024,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74691
75024
|
metadata: scoreMetadata
|
|
74692
75025
|
});
|
|
74693
75026
|
const lintPartialFailures = yield* get$2(partialFailuresRef);
|
|
74694
|
-
const
|
|
75027
|
+
const didSecurityScanFail = yield* get$2(securityScanFailedRef);
|
|
75028
|
+
const securityScanFailed = didSecurityScanFail || didSecurityScanReachDeadline;
|
|
75029
|
+
let securityScanFailureReason = null;
|
|
75030
|
+
if (didSecurityScanReachDeadline) securityScanFailureReason = "Security scan reached the max scan duration; findings collected before the deadline were preserved.";
|
|
75031
|
+
else if (didSecurityScanFail) securityScanFailureReason = "Security scan failed and was skipped.";
|
|
74695
75032
|
return {
|
|
74696
75033
|
project,
|
|
74697
75034
|
userConfig: resolvedConfig.config,
|
|
@@ -74714,6 +75051,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74714
75051
|
scanConcurrency,
|
|
74715
75052
|
supplyChainOverlapTimedOut: supplyChainResult.timedOut,
|
|
74716
75053
|
securityScanFailed,
|
|
75054
|
+
securityScanFailureReason,
|
|
74717
75055
|
lintCacheHitFileCount,
|
|
74718
75056
|
lintCacheTotalFileCount,
|
|
74719
75057
|
lintSidecarReplayedFileCount,
|
|
@@ -74729,7 +75067,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74729
75067
|
"inspect.runDeadCode": input.runDeadCode,
|
|
74730
75068
|
"inspect.isCi": input.isCi,
|
|
74731
75069
|
"inspect.scoreSurface": input.scoreSurface ?? "score"
|
|
74732
|
-
} }), (scanProgram) => flatMap$2(ScanDeadlineMs, (scanDeadlineMs) =>
|
|
75070
|
+
} }), (scanProgram) => flatMap$2(ScanDeadlineMs, (scanDeadlineMs) => {
|
|
75071
|
+
if (scanDeadlineMs === null) return scanProgram;
|
|
75072
|
+
return scanProgram.pipe(timeout(scanDeadlineMs), catchTag$1("TimeoutError", () => new ReactDoctorError({ reason: new ScanDeadlineExceeded({ detail: `${scanDeadlineMs / MILLISECONDS_PER_SECOND}s elapsed` }) })));
|
|
75073
|
+
}));
|
|
74733
75074
|
const parseNodeVersion = (versionString) => {
|
|
74734
75075
|
const [major = 0, minor = 0, patch = 0] = versionString.replace(/^v/, "").trim().split(".").map(Number);
|
|
74735
75076
|
return {
|
|
@@ -74830,16 +75171,6 @@ const resolveNodeForOxlint = () => {
|
|
|
74830
75171
|
});
|
|
74831
75172
|
});
|
|
74832
75173
|
/**
|
|
74833
|
-
* Zip-Slip defense: relative paths come from git (`diff --name-only`), which
|
|
74834
|
-
* normalizes during ordinary adds, but a crafted index/pack/symlinked tree can
|
|
74835
|
-
* smuggle `..` segments that escape the temp root. Resolve against the temp dir
|
|
74836
|
-
* and reject anything that lands outside before writing.
|
|
74837
|
-
*/
|
|
74838
|
-
const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
|
|
74839
|
-
const relative = path.relative(parentAbsolutePath, childAbsolutePath);
|
|
74840
|
-
return Boolean(relative) && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
74841
|
-
};
|
|
74842
|
-
/**
|
|
74843
75174
|
* Writes a set of source files (supplied by `readContent` — e.g.
|
|
74844
75175
|
* `git show <ref>:<path>` for a baseline tree, or `git show :<path>` for the
|
|
74845
75176
|
* index) into a temp tree that mirrors the project layout, copying the
|
|
@@ -75016,14 +75347,6 @@ const runEditorScan = async (input) => {
|
|
|
75016
75347
|
error: messageFromUnknown(error)
|
|
75017
75348
|
};
|
|
75018
75349
|
};
|
|
75019
|
-
/**
|
|
75020
|
-
* Back-compat alias: the streaming pipeline holds its caches in
|
|
75021
|
-
* per-pipeline closures that are garbage-collected when the pipeline
|
|
75022
|
-
* goes out of scope, so there is nothing to clear at module scope. The
|
|
75023
|
-
* public CLI's `clearCaches()` still calls this for symmetry with the
|
|
75024
|
-
* other `clear*` helpers.
|
|
75025
|
-
*/
|
|
75026
|
-
const clearAutoSuppressionCaches = () => {};
|
|
75027
75350
|
const lookupOwnRule = (rule) => Object.hasOwn(REACT_DOCTOR_RULE_REGISTRY, rule) ? REACT_DOCTOR_RULE_REGISTRY[rule] : void 0;
|
|
75028
75351
|
/**
|
|
75029
75352
|
* Returns presentation metadata for `<plugin>/<rule>`, or `null` when the
|
|
@@ -75063,7 +75386,7 @@ const computeConfigFingerprint = (projectDirectory, version) => {
|
|
|
75063
75386
|
/** Display name used in client-facing messages and progress titles. */
|
|
75064
75387
|
const SERVER_DISPLAY_NAME = "React Doctor";
|
|
75065
75388
|
/** Server version reported in `serverInfo`; injected at build, `dev` from source. */
|
|
75066
|
-
const SERVER_VERSION = "0.9.
|
|
75389
|
+
const SERVER_VERSION = "0.9.4";
|
|
75067
75390
|
/** `Diagnostic.source` shown next to every published diagnostic. */
|
|
75068
75391
|
const DIAGNOSTIC_SOURCE = "react-doctor";
|
|
75069
75392
|
/**
|
|
@@ -75691,10 +76014,7 @@ const createProjectGraph = (options) => {
|
|
|
75691
76014
|
for (const root of roots) try {
|
|
75692
76015
|
for (const workspacePackage of discoverReactSubprojects(root)) {
|
|
75693
76016
|
const directory = normalizeDirectory(workspacePackage.directory);
|
|
75694
|
-
if (!seen.has(directory)) seen.set(directory, {
|
|
75695
|
-
directory,
|
|
75696
|
-
name: workspacePackage.name
|
|
75697
|
-
});
|
|
76017
|
+
if (!seen.has(directory)) seen.set(directory, { directory });
|
|
75698
76018
|
}
|
|
75699
76019
|
} catch (error) {
|
|
75700
76020
|
logger.warn(`Project discovery failed for ${root}: ${messageFromUnknown(error)}`);
|
|
@@ -75719,8 +76039,8 @@ const createProjectGraph = (options) => {
|
|
|
75719
76039
|
clearProjectCache();
|
|
75720
76040
|
clearConfigCache();
|
|
75721
76041
|
clearPackageJsonCache();
|
|
76042
|
+
clearPackageRoleCache();
|
|
75722
76043
|
clearIgnorePatternsCache();
|
|
75723
|
-
clearAutoSuppressionCaches();
|
|
75724
76044
|
clearMinifiedFileCache();
|
|
75725
76045
|
projects = null;
|
|
75726
76046
|
}
|
|
@@ -76116,11 +76436,9 @@ const createScheduler = (options) => {
|
|
|
76116
76436
|
timers.clear();
|
|
76117
76437
|
queue.length = 0;
|
|
76118
76438
|
};
|
|
76119
|
-
const pendingCount = () => queue.length + running + timers.size;
|
|
76120
76439
|
return {
|
|
76121
76440
|
enqueue,
|
|
76122
76441
|
cancelProject,
|
|
76123
|
-
pendingCount,
|
|
76124
76442
|
dispose
|
|
76125
76443
|
};
|
|
76126
76444
|
};
|
|
@@ -76767,8 +77085,11 @@ const METRIC = {
|
|
|
76767
77085
|
scanPhaseDuration: "scan.phase_duration",
|
|
76768
77086
|
scanFiles: "scan.files",
|
|
76769
77087
|
scanScore: "scan.score",
|
|
77088
|
+
scanScoreRetry: "scan.score_retry",
|
|
76770
77089
|
scanClean: "scan.clean",
|
|
76771
77090
|
scanCheckSkipped: "scan.check_skipped",
|
|
77091
|
+
scanProjectSkipped: "scan.project_skipped",
|
|
77092
|
+
scanWorkspaceDeadCodeShared: "scan.workspace_deadcode_shared",
|
|
76772
77093
|
scanNoReactDetected: "scan.no_react_detected",
|
|
76773
77094
|
baselineDegraded: "baseline.degraded",
|
|
76774
77095
|
ruleFired: "rule.fired",
|
|
@@ -76795,9 +77116,14 @@ const METRIC = {
|
|
|
76795
77116
|
lspScanCompleted: "lsp.scan.completed",
|
|
76796
77117
|
lspScanDuration: "lsp.scan.duration",
|
|
76797
77118
|
lspScanDiagnostics: "lsp.scan.diagnostics",
|
|
76798
|
-
tuiActionMenuOpened: "tui.action_menu_opened",
|
|
76799
77119
|
tuiCompactReportShown: "tui.compact_report_shown",
|
|
76800
77120
|
tuiFindingNavigated: "tui.finding_navigated",
|
|
77121
|
+
tuiIssueStreamShown: "tui.issue_stream_shown",
|
|
77122
|
+
tuiProjectPathContextShown: "tui.project_path_context_shown",
|
|
77123
|
+
tuiProjectSelectInlineShown: "tui.project_select_inline_shown",
|
|
77124
|
+
tuiReportActionSelected: "tui.report_action_selected",
|
|
77125
|
+
tuiCancelled: "tui.cancelled",
|
|
77126
|
+
tuiScanInlineShown: "tui.scan_inline_shown",
|
|
76801
77127
|
tuiStackedReportCapped: "tui.stacked_report_capped",
|
|
76802
77128
|
aiTrainingWarningShown: "ai.training.warning_shown",
|
|
76803
77129
|
jsonOutUsed: "json.out_used"
|
|
@@ -76812,6 +77138,9 @@ const METRIC = {
|
|
|
76812
77138
|
*/
|
|
76813
77139
|
const toCategoryKey = (category) => category.toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
76814
77140
|
//#endregion
|
|
77141
|
+
//#region src/cli/utils/is-env-flag-enabled.ts
|
|
77142
|
+
const isEnvFlagEnabled = (value) => value === "1" || value?.toLowerCase() === "true";
|
|
77143
|
+
//#endregion
|
|
76815
77144
|
//#region src/cli/utils/anonymize-text.ts
|
|
76816
77145
|
/**
|
|
76817
77146
|
* Free-text fields can carry both a home-directory path (the OS username) and a
|
|
@@ -76972,7 +77301,6 @@ const resolveTracesSampleRate = () => {
|
|
|
76972
77301
|
* session.
|
|
76973
77302
|
*/
|
|
76974
77303
|
let lspTracesSampleRate = 0;
|
|
76975
|
-
const isEnvFlagEnabled = (value) => value === "1" || value?.toLowerCase() === "true";
|
|
76976
77304
|
const nodeMajorVersion = () => Number.parseInt(process.versions.node.split(".", 1)[0] ?? "", 10) || 0;
|
|
76977
77305
|
const shouldEnableLspTelemetry = () => {
|
|
76978
77306
|
if (isEnvFlagEnabled(process.env.REACT_DOCTOR_NO_TELEMETRY)) return false;
|
|
@@ -77100,5 +77428,5 @@ const startLanguageServer = () => {
|
|
|
77100
77428
|
};
|
|
77101
77429
|
//#endregion
|
|
77102
77430
|
export { startLanguageServer };
|
|
77103
|
-
!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]="
|
|
77104
|
-
//# debugId=
|
|
77431
|
+
!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]="383b8ad0-ef31-54a1-9d90-cdefff0fb2cb")}catch(e){}}();
|
|
77432
|
+
//# debugId=383b8ad0-ef31-54a1-9d90-cdefff0fb2cb
|