react-doctor 0.9.3-dev.9bb44e7 → 0.9.3-dev.afd60db
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 +896 -237
- package/dist/index.d.ts +9 -2
- package/dist/index.js +467 -123
- package/dist/lsp.js +473 -123
- package/dist/run-scan-app-BphjJB7x.js +2325 -0
- package/package.json +5 -4
- package/dist/run-scan-app-DHRvqfkX.js +0 -1733
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="4b6a1dd7-2795-5a24-b1f2-8de4e74574e3")}catch(e){}}();
|
|
3
3
|
import { i as __toESM$1, r as __require, t as __commonJSMin$1 } from "./chunk-ChKA3wi6.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, FRAMEWORK_TOKENS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, REACT_DOCTOR_RULE_REGISTRY, REACT_DOCTOR_SCAN_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor/core";
|
|
@@ -10,7 +10,7 @@ import fs from "node:fs";
|
|
|
10
10
|
import * as Path from "node:path";
|
|
11
11
|
import path from "node:path";
|
|
12
12
|
import * as NodeChildProcess from "node:child_process";
|
|
13
|
-
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
13
|
+
import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
14
14
|
import { ResolverFactory } from "oxc-resolver";
|
|
15
15
|
import ts from "typescript";
|
|
16
16
|
import * as NodeUrl from "node:url";
|
|
@@ -3621,7 +3621,7 @@ const flatMap$5 = /* @__PURE__ */ dual(2, (self, f) => isNone(self) ? none() : f
|
|
|
3621
3621
|
* @category filtering
|
|
3622
3622
|
* @since 2.0.0
|
|
3623
3623
|
*/
|
|
3624
|
-
const filter = /* @__PURE__ */ dual(2, (self, predicate) => isNone(self) ? none() : predicate(self.value) ? some(self.value) : none());
|
|
3624
|
+
const filter$1 = /* @__PURE__ */ dual(2, (self, predicate) => isNone(self) ? none() : predicate(self.value) ? some(self.value) : none());
|
|
3625
3625
|
//#endregion
|
|
3626
3626
|
//#region ../../node_modules/.pnpm/effect@4.0.0-beta.70/node_modules/effect/dist/Result.js
|
|
3627
3627
|
/**
|
|
@@ -15556,7 +15556,7 @@ function transformOptional(f) {
|
|
|
15556
15556
|
*/
|
|
15557
15557
|
function withDefault$1(defaultValue) {
|
|
15558
15558
|
return new Getter((o) => {
|
|
15559
|
-
const filtered = filter(o, isNotUndefined);
|
|
15559
|
+
const filtered = filter$1(o, isNotUndefined);
|
|
15560
15560
|
return isSome(filtered) ? succeed$2(filtered) : mapEager(defaultValue, some);
|
|
15561
15561
|
});
|
|
15562
15562
|
}
|
|
@@ -19404,6 +19404,10 @@ var JsonReportProjectEntryV3 = class extends Class("JsonReportProjectEntryV3")({
|
|
|
19404
19404
|
scannedFileCount: optional(Number$1),
|
|
19405
19405
|
elapsedMilliseconds: Number$1
|
|
19406
19406
|
}) {};
|
|
19407
|
+
var JsonReportSkippedProject = class extends Class("JsonReportSkippedProject")({
|
|
19408
|
+
directory: String$1,
|
|
19409
|
+
reason: Literal("max-duration")
|
|
19410
|
+
}) {};
|
|
19407
19411
|
/** Original full, diff, and staged report contract. */
|
|
19408
19412
|
var JsonReportV1 = class extends Class("JsonReportV1")({
|
|
19409
19413
|
schemaVersion: Literal(1),
|
|
@@ -19432,6 +19436,7 @@ var JsonReportV1 = class extends Class("JsonReportV1")({
|
|
|
19432
19436
|
reactDetected: optional(Boolean$1),
|
|
19433
19437
|
diff: NullOr(JsonReportDiffInfo),
|
|
19434
19438
|
projects: ArraySchema(JsonReportProjectEntry),
|
|
19439
|
+
skippedProjects: optional(ArraySchema(JsonReportSkippedProject)),
|
|
19435
19440
|
diagnostics: ArraySchema(Diagnostic),
|
|
19436
19441
|
summary: JsonReportSummary,
|
|
19437
19442
|
elapsedMilliseconds: Number$1,
|
|
@@ -19460,6 +19465,7 @@ var JsonReportV2 = class extends Class("JsonReportV2")({
|
|
|
19460
19465
|
diff: NullOr(JsonReportDiffInfo),
|
|
19461
19466
|
baseline: JsonReportBaseline,
|
|
19462
19467
|
projects: ArraySchema(JsonReportProjectEntry),
|
|
19468
|
+
skippedProjects: optional(ArraySchema(JsonReportSkippedProject)),
|
|
19463
19469
|
diagnostics: ArraySchema(Diagnostic),
|
|
19464
19470
|
summary: JsonReportSummary,
|
|
19465
19471
|
elapsedMilliseconds: Number$1,
|
|
@@ -19476,6 +19482,7 @@ var JsonReportV3 = class extends Class("JsonReportV3")({
|
|
|
19476
19482
|
diff: NullOr(JsonReportDiffInfo),
|
|
19477
19483
|
baseline: optional(JsonReportBaseline),
|
|
19478
19484
|
projects: ArraySchema(JsonReportProjectEntryV3),
|
|
19485
|
+
skippedProjects: optional(ArraySchema(JsonReportSkippedProject)),
|
|
19479
19486
|
diagnostics: ArraySchema(JsonReportDiagnosticV3),
|
|
19480
19487
|
summary: JsonReportSummary,
|
|
19481
19488
|
elapsedMilliseconds: Number$1,
|
|
@@ -53458,6 +53465,47 @@ const flattenArray = (self) => transformPull$1(self, (pull) => {
|
|
|
53458
53465
|
}));
|
|
53459
53466
|
});
|
|
53460
53467
|
/**
|
|
53468
|
+
* Filters arrays of elements emitted by a channel, applying the filter
|
|
53469
|
+
* to each element within the arrays and only emitting non-empty filtered arrays.
|
|
53470
|
+
*
|
|
53471
|
+
* **Example** (Filtering array output)
|
|
53472
|
+
*
|
|
53473
|
+
* ```ts
|
|
53474
|
+
* import { Array, Channel } from "effect"
|
|
53475
|
+
*
|
|
53476
|
+
* const nonEmptyArrayPredicate = Array.isReadonlyArrayNonEmpty
|
|
53477
|
+
*
|
|
53478
|
+
* // Create a channel that outputs arrays of mixed data
|
|
53479
|
+
* const arrayChannel = Channel.fromIterable([
|
|
53480
|
+
* Array.make(1, 2, 3, 4, 5),
|
|
53481
|
+
* Array.make(6, 7, 8, 9, 10),
|
|
53482
|
+
* Array.make(11, 12, 13, 14, 15)
|
|
53483
|
+
* ]).pipe(Channel.filter(nonEmptyArrayPredicate))
|
|
53484
|
+
*
|
|
53485
|
+
* // Filter arrays to keep only even numbers
|
|
53486
|
+
* const evenArraysChannel = Channel.filterArray(arrayChannel, (n) => n % 2 === 0)
|
|
53487
|
+
* // Outputs: [2, 4], [6, 8, 10], [12, 14]
|
|
53488
|
+
* // Note: Only non-empty filtered arrays are emitted
|
|
53489
|
+
*
|
|
53490
|
+
* // Arrays that would become empty after filtering are discarded entirely
|
|
53491
|
+
* const oddChannel = Channel.fromIterable([
|
|
53492
|
+
* Array.make(1, 3, 5),
|
|
53493
|
+
* Array.make(2, 4),
|
|
53494
|
+
* Array.make(7, 9)
|
|
53495
|
+
* ]).pipe(Channel.filter(nonEmptyArrayPredicate))
|
|
53496
|
+
* const filteredOddChannel = Channel.filterArray(oddChannel, (n) => n % 2 === 0)
|
|
53497
|
+
* // Outputs: [2, 4] (the arrays [1,3,5] and [7,9] are discarded)
|
|
53498
|
+
* ```
|
|
53499
|
+
*
|
|
53500
|
+
* @category filtering
|
|
53501
|
+
* @since 4.0.0
|
|
53502
|
+
*/
|
|
53503
|
+
const filterArray = /* @__PURE__ */ dual(2, (self, predicate) => transformPull$1(self, (pull) => succeed$2(flatMap$2(pull, function loop(arr) {
|
|
53504
|
+
const passes = [];
|
|
53505
|
+
for (let i = 0; i < arr.length; i++) if (predicate(arr[i])) passes.push(arr[i]);
|
|
53506
|
+
return isReadonlyArrayNonEmpty(passes) ? succeed$2(passes) : flatMap$2(pull, loop);
|
|
53507
|
+
}))));
|
|
53508
|
+
/**
|
|
53461
53509
|
* Filters and maps each element inside emitted non-empty arrays using a
|
|
53462
53510
|
* `Filter`.
|
|
53463
53511
|
*
|
|
@@ -55095,6 +55143,30 @@ const concat = /* @__PURE__ */ dual(2, (self, that) => flatten(fromArray([self,
|
|
|
55095
55143
|
*/
|
|
55096
55144
|
const merge$1 = /* @__PURE__ */ dual((args) => isStream(args[0]) && isStream(args[1]), (self, that, options) => fromChannel(merge$2(toChannel(self), toChannel(that), options)));
|
|
55097
55145
|
/**
|
|
55146
|
+
* Filters a stream to the elements that satisfy a predicate.
|
|
55147
|
+
*
|
|
55148
|
+
* **Example** (Filtering stream values)
|
|
55149
|
+
*
|
|
55150
|
+
* ```ts
|
|
55151
|
+
* import { Console, Effect, Stream } from "effect"
|
|
55152
|
+
*
|
|
55153
|
+
* const program = Effect.gen(function*() {
|
|
55154
|
+
* const stream = Stream.make(1, 2, 3, 4).pipe(
|
|
55155
|
+
* Stream.filter((n) => n % 2 === 0)
|
|
55156
|
+
* )
|
|
55157
|
+
* const values = yield* Stream.runCollect(stream)
|
|
55158
|
+
* yield* Console.log(values)
|
|
55159
|
+
* })
|
|
55160
|
+
*
|
|
55161
|
+
* Effect.runPromise(program)
|
|
55162
|
+
* // Output: [ 2, 4 ]
|
|
55163
|
+
* ```
|
|
55164
|
+
*
|
|
55165
|
+
* @category filtering
|
|
55166
|
+
* @since 2.0.0
|
|
55167
|
+
*/
|
|
55168
|
+
const filter = /* @__PURE__ */ dual(2, (self, predicate) => fromChannel(filterArray(toChannel(self), predicate)));
|
|
55169
|
+
/**
|
|
55098
55170
|
* Filters and maps stream elements in one pass using a `Filter`.
|
|
55099
55171
|
*
|
|
55100
55172
|
* @category filtering
|
|
@@ -61878,6 +61950,7 @@ const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
|
|
|
61878
61950
|
const GENERATED_SOURCE_DIRECTORY_PATTERN = /(?:^|\/)__generated__\//;
|
|
61879
61951
|
const MINIFIED_SNIFF_BYTES = 65536;
|
|
61880
61952
|
const GIT_LS_FILES_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
|
|
61953
|
+
const GIT_CHECK_ATTR_MAX_BUFFER_BYTES = GIT_LS_FILES_MAX_BUFFER_BYTES;
|
|
61881
61954
|
const SCANNED_DOT_DIRECTORIES = new Set([".dumi", ".storybook"]);
|
|
61882
61955
|
const IGNORED_DIRECTORIES = new Set([
|
|
61883
61956
|
".angular",
|
|
@@ -62163,13 +62236,9 @@ const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
|
|
|
62163
62236
|
const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
|
|
62164
62237
|
const ABORT_EXIT_CODES = new Set([134, 3221226505]);
|
|
62165
62238
|
const OXLINT_OOM_RESCUE_BUDGET_MS = 6e4;
|
|
62166
|
-
const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
|
|
62167
|
-
const LINT_PHASE_TIMEOUT_MS = 3e5;
|
|
62168
|
-
const SCAN_TOTAL_DEADLINE_MS = 9e5;
|
|
62169
62239
|
const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
|
|
62170
62240
|
const DEAD_CODE_WORKER_MEM_BUDGET_BYTES = 2 * 1024 * 1024 * 1024;
|
|
62171
62241
|
const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
|
|
62172
|
-
const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
|
|
62173
62242
|
const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
|
|
62174
62243
|
const RECOMMENDED_PNPM_MINIMUM_RELEASE_AGE_MINUTES = 10080;
|
|
62175
62244
|
const REACT_SERVER_DOM_PACKAGES = [
|
|
@@ -62264,6 +62333,66 @@ const isPlainObject = (value) => {
|
|
|
62264
62333
|
const prototype = Object.getPrototypeOf(value);
|
|
62265
62334
|
return prototype === null || prototype === Object.prototype;
|
|
62266
62335
|
};
|
|
62336
|
+
const LINGUIST_ATTRIBUTES = ["linguist-generated", "linguist-vendored"];
|
|
62337
|
+
const DISABLED_ATTRIBUTE_VALUES = new Set([
|
|
62338
|
+
"unspecified",
|
|
62339
|
+
"unset",
|
|
62340
|
+
"false",
|
|
62341
|
+
"0",
|
|
62342
|
+
"off",
|
|
62343
|
+
"no"
|
|
62344
|
+
]);
|
|
62345
|
+
const buildInput = (relativePaths) => `${relativePaths.join("\0")}\0`;
|
|
62346
|
+
const parseIgnoredPaths = (output) => {
|
|
62347
|
+
const ignoredPaths = /* @__PURE__ */ new Set();
|
|
62348
|
+
const fields = output.split("\0");
|
|
62349
|
+
for (let fieldIndex = 0; fieldIndex + 2 < fields.length; fieldIndex += 3) {
|
|
62350
|
+
const filePath = fields[fieldIndex];
|
|
62351
|
+
const value = fields[fieldIndex + 2]?.toLowerCase();
|
|
62352
|
+
if (filePath && value && !DISABLED_ATTRIBUTE_VALUES.has(value)) ignoredPaths.add(filePath);
|
|
62353
|
+
}
|
|
62354
|
+
return ignoredPaths;
|
|
62355
|
+
};
|
|
62356
|
+
const collectGitLinguistIgnoredPaths = (rootDirectory, relativePaths) => {
|
|
62357
|
+
if (relativePaths.length === 0) return /* @__PURE__ */ new Set();
|
|
62358
|
+
const result = spawnSync("git", [
|
|
62359
|
+
"check-attr",
|
|
62360
|
+
"-z",
|
|
62361
|
+
"--stdin",
|
|
62362
|
+
...LINGUIST_ATTRIBUTES
|
|
62363
|
+
], {
|
|
62364
|
+
cwd: rootDirectory,
|
|
62365
|
+
encoding: "utf-8",
|
|
62366
|
+
input: buildInput(relativePaths),
|
|
62367
|
+
maxBuffer: GIT_CHECK_ATTR_MAX_BUFFER_BYTES
|
|
62368
|
+
});
|
|
62369
|
+
if (result.error || result.status !== 0) return /* @__PURE__ */ new Set();
|
|
62370
|
+
return parseIgnoredPaths(result.stdout);
|
|
62371
|
+
};
|
|
62372
|
+
const collectGitLinguistIgnoredPathsCooperative = (rootDirectory, relativePaths, signal) => {
|
|
62373
|
+
if (relativePaths.length === 0) return Promise.resolve(/* @__PURE__ */ new Set());
|
|
62374
|
+
return new Promise((resolve, reject) => {
|
|
62375
|
+
signal?.throwIfAborted();
|
|
62376
|
+
execFile("git", [
|
|
62377
|
+
"check-attr",
|
|
62378
|
+
"-z",
|
|
62379
|
+
"--stdin",
|
|
62380
|
+
...LINGUIST_ATTRIBUTES
|
|
62381
|
+
], {
|
|
62382
|
+
cwd: rootDirectory,
|
|
62383
|
+
encoding: "utf-8",
|
|
62384
|
+
killSignal: "SIGKILL",
|
|
62385
|
+
maxBuffer: GIT_CHECK_ATTR_MAX_BUFFER_BYTES,
|
|
62386
|
+
signal
|
|
62387
|
+
}, (error, stdout) => {
|
|
62388
|
+
if (signal?.aborted) {
|
|
62389
|
+
reject(signal.reason);
|
|
62390
|
+
return;
|
|
62391
|
+
}
|
|
62392
|
+
resolve(error ? /* @__PURE__ */ new Set() : parseIgnoredPaths(stdout));
|
|
62393
|
+
}).stdin?.end(buildInput(relativePaths));
|
|
62394
|
+
});
|
|
62395
|
+
};
|
|
62267
62396
|
const lastSourceMappingUrl = (fileContent) => {
|
|
62268
62397
|
const lastReference = [...fileContent.matchAll(/\/\/# sourceMappingURL=(\S+)/g)].at(-1);
|
|
62269
62398
|
return lastReference ? lastReference[1] : null;
|
|
@@ -62368,6 +62497,9 @@ function* walkSourceTreeFiles(rootDirectory) {
|
|
|
62368
62497
|
}
|
|
62369
62498
|
}
|
|
62370
62499
|
}
|
|
62500
|
+
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
62501
|
+
setImmediate(resolve);
|
|
62502
|
+
});
|
|
62371
62503
|
const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
|
|
62372
62504
|
const entries = [];
|
|
62373
62505
|
for (const relativePath of relativePaths) {
|
|
@@ -62381,23 +62513,55 @@ const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
|
|
|
62381
62513
|
}
|
|
62382
62514
|
return entries;
|
|
62383
62515
|
};
|
|
62384
|
-
const
|
|
62516
|
+
const collectSizedSourceFilesCooperative = async (rootDirectory, relativePaths, signal) => {
|
|
62517
|
+
const entries = [];
|
|
62518
|
+
let sliceStartedAt = performance.now();
|
|
62519
|
+
for (const relativePath of relativePaths) {
|
|
62520
|
+
signal?.throwIfAborted();
|
|
62521
|
+
const absolutePath = Path.resolve(rootDirectory, relativePath);
|
|
62522
|
+
const sizeBytes = statSourceFileSize(absolutePath);
|
|
62523
|
+
if (!isLargeMinifiedFile(absolutePath, sizeBytes)) entries.push({
|
|
62524
|
+
path: relativePath,
|
|
62525
|
+
sizeBytes: sizeBytes ?? 0
|
|
62526
|
+
});
|
|
62527
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
62528
|
+
await yieldToEventLoop();
|
|
62529
|
+
sliceStartedAt = performance.now();
|
|
62530
|
+
}
|
|
62531
|
+
}
|
|
62532
|
+
return entries;
|
|
62533
|
+
};
|
|
62534
|
+
const parseGitSourceFilePaths = (output) => {
|
|
62535
|
+
const trackedPaths = /* @__PURE__ */ new Set();
|
|
62536
|
+
const untrackedPaths = [];
|
|
62537
|
+
for (const entry of output.split("\0")) {
|
|
62538
|
+
if (entry.length === 0) continue;
|
|
62539
|
+
const trackedPath = /^[0-7]{6} [0-9a-f]+ [0-3]\t([\s\S]+)$/.exec(entry)?.[1];
|
|
62540
|
+
if (trackedPath === void 0) untrackedPaths.push(entry);
|
|
62541
|
+
else trackedPaths.add(trackedPath);
|
|
62542
|
+
}
|
|
62543
|
+
return {
|
|
62544
|
+
trackedPaths: [...trackedPaths],
|
|
62545
|
+
untrackedPaths
|
|
62546
|
+
};
|
|
62547
|
+
};
|
|
62548
|
+
const listGitSourceFilePaths = (rootDirectory) => {
|
|
62385
62549
|
const result = spawnSync("git", [
|
|
62386
62550
|
"ls-files",
|
|
62387
62551
|
"-z",
|
|
62388
|
-
|
|
62552
|
+
"--stage",
|
|
62553
|
+
"--others",
|
|
62554
|
+
"--exclude-standard"
|
|
62389
62555
|
], {
|
|
62390
62556
|
cwd: rootDirectory,
|
|
62391
62557
|
encoding: "utf-8",
|
|
62392
62558
|
maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES
|
|
62393
62559
|
});
|
|
62394
62560
|
if (result.error || result.status !== 0) return null;
|
|
62395
|
-
return result.stdout
|
|
62561
|
+
return parseGitSourceFilePaths(result.stdout);
|
|
62396
62562
|
};
|
|
62397
|
-
const
|
|
62398
|
-
const trackedPaths =
|
|
62399
|
-
const untrackedPaths = gitLsFiles(rootDirectory, ["--others", "--exclude-standard"]);
|
|
62400
|
-
if (trackedPaths === null || untrackedPaths === null) return null;
|
|
62563
|
+
const collectGitCandidateSourceFilePaths = (rootDirectory, paths) => {
|
|
62564
|
+
const { trackedPaths, untrackedPaths } = paths;
|
|
62401
62565
|
const emitDuplicateJsPaths = collectTypeScriptEmitDuplicateJsPaths({
|
|
62402
62566
|
trackedPaths: new Set(trackedPaths),
|
|
62403
62567
|
untrackedPaths,
|
|
@@ -62405,12 +62569,65 @@ const listSourceFilesViaGit = (rootDirectory) => {
|
|
|
62405
62569
|
});
|
|
62406
62570
|
return [...trackedPaths, ...untrackedPaths].filter((filePath) => isLintableSourceFile(filePath) && !hasIgnoredPathSegment(filePath) && !emitDuplicateJsPaths.has(filePath));
|
|
62407
62571
|
};
|
|
62572
|
+
const filterGitSourceFilePaths = (rootDirectory, paths) => {
|
|
62573
|
+
const candidatePaths = collectGitCandidateSourceFilePaths(rootDirectory, paths);
|
|
62574
|
+
const linguistIgnoredPaths = collectGitLinguistIgnoredPaths(rootDirectory, candidatePaths);
|
|
62575
|
+
return candidatePaths.filter((filePath) => !linguistIgnoredPaths.has(filePath));
|
|
62576
|
+
};
|
|
62577
|
+
const listSourceFilesViaGit = (rootDirectory) => {
|
|
62578
|
+
const paths = listGitSourceFilePaths(rootDirectory);
|
|
62579
|
+
return paths === null ? null : filterGitSourceFilePaths(rootDirectory, paths);
|
|
62580
|
+
};
|
|
62581
|
+
const listSourceFilesViaGitCooperative = async (rootDirectory, signal) => {
|
|
62582
|
+
const paths = await new Promise((resolve, reject) => {
|
|
62583
|
+
signal?.throwIfAborted();
|
|
62584
|
+
execFile("git", [
|
|
62585
|
+
"ls-files",
|
|
62586
|
+
"-z",
|
|
62587
|
+
"--stage",
|
|
62588
|
+
"--others",
|
|
62589
|
+
"--exclude-standard"
|
|
62590
|
+
], {
|
|
62591
|
+
cwd: rootDirectory,
|
|
62592
|
+
encoding: "utf-8",
|
|
62593
|
+
killSignal: "SIGKILL",
|
|
62594
|
+
maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES,
|
|
62595
|
+
signal
|
|
62596
|
+
}, (error, stdout) => {
|
|
62597
|
+
if (signal?.aborted) {
|
|
62598
|
+
reject(signal.reason);
|
|
62599
|
+
return;
|
|
62600
|
+
}
|
|
62601
|
+
resolve(error ? null : parseGitSourceFilePaths(stdout));
|
|
62602
|
+
});
|
|
62603
|
+
});
|
|
62604
|
+
if (paths === null) return null;
|
|
62605
|
+
const candidatePaths = collectGitCandidateSourceFilePaths(rootDirectory, paths);
|
|
62606
|
+
const linguistIgnoredPaths = await collectGitLinguistIgnoredPathsCooperative(rootDirectory, candidatePaths, signal);
|
|
62607
|
+
return candidatePaths.filter((filePath) => !linguistIgnoredPaths.has(filePath));
|
|
62608
|
+
};
|
|
62408
62609
|
const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
62409
62610
|
const filePaths = [];
|
|
62410
62611
|
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) if (isLintableSourceFile(name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
|
|
62411
62612
|
return filePaths;
|
|
62412
62613
|
};
|
|
62413
|
-
const
|
|
62614
|
+
const listSourceFilesViaFilesystemCooperative = async (rootDirectory, signal) => {
|
|
62615
|
+
const filePaths = [];
|
|
62616
|
+
let sliceStartedAt = performance.now();
|
|
62617
|
+
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
|
|
62618
|
+
signal?.throwIfAborted();
|
|
62619
|
+
if (isLintableSourceFile(name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
|
|
62620
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
62621
|
+
await yieldToEventLoop();
|
|
62622
|
+
sliceStartedAt = performance.now();
|
|
62623
|
+
}
|
|
62624
|
+
}
|
|
62625
|
+
return filePaths;
|
|
62626
|
+
};
|
|
62627
|
+
const listSourceFilePaths = (rootDirectory) => (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort();
|
|
62628
|
+
const listSourceFilePathsCooperative = async (rootDirectory, signal) => (await listSourceFilesViaGitCooperative(rootDirectory, signal) ?? await listSourceFilesViaFilesystemCooperative(rootDirectory, signal)).sort();
|
|
62629
|
+
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, listSourceFilePaths(rootDirectory));
|
|
62630
|
+
const listSourceFilesCooperative = async (rootDirectory, signal) => (await collectSizedSourceFilesCooperative(rootDirectory, await listSourceFilePathsCooperative(rootDirectory, signal), signal)).map((entry) => entry.path);
|
|
62414
62631
|
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
62415
62632
|
const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
|
|
62416
62633
|
const UTF8_BOM_CHARACTER = "";
|
|
@@ -64739,7 +64956,16 @@ const getBrowserslistProtectionRequirement = (directory) => {
|
|
|
64739
64956
|
if (!queries) return void 0;
|
|
64740
64957
|
let requirement;
|
|
64741
64958
|
let hasUnsupportedTarget = false;
|
|
64742
|
-
|
|
64959
|
+
const previousIgnoreOldData = process.env.BROWSERSLIST_IGNORE_OLD_DATA;
|
|
64960
|
+
let browserTargets = [];
|
|
64961
|
+
try {
|
|
64962
|
+
process.env.BROWSERSLIST_IGNORE_OLD_DATA = "true";
|
|
64963
|
+
browserTargets = (0, import_browserslist.default)(queries);
|
|
64964
|
+
} finally {
|
|
64965
|
+
if (previousIgnoreOldData === void 0) delete process.env.BROWSERSLIST_IGNORE_OLD_DATA;
|
|
64966
|
+
else process.env.BROWSERSLIST_IGNORE_OLD_DATA = previousIgnoreOldData;
|
|
64967
|
+
}
|
|
64968
|
+
for (const browserTarget of browserTargets) {
|
|
64743
64969
|
const browserRequirement = getBrowserProtectionRequirement(browserTarget);
|
|
64744
64970
|
if (browserRequirement === "unsupported") hasUnsupportedTarget = true;
|
|
64745
64971
|
if (browserRequirement === "noreferrer") requirement = "noreferrer";
|
|
@@ -64849,8 +65075,8 @@ const clearProjectCache = () => {
|
|
|
64849
65075
|
* framework-agnostic rules. Throws only when the directory has nothing
|
|
64850
65076
|
* to scan (no enclosing project and no source files of its own).
|
|
64851
65077
|
*/
|
|
64852
|
-
const discoverProjectWithoutPackageJson = (directory) => {
|
|
64853
|
-
const sourceFileCount = countSourceFiles(directory);
|
|
65078
|
+
const discoverProjectWithoutPackageJson = (directory, options) => {
|
|
65079
|
+
const sourceFileCount = options.sourceFileCount ?? countSourceFiles(directory);
|
|
64854
65080
|
const hasOwnTsConfig = NFS.existsSync(Path.join(directory, "tsconfig.json"));
|
|
64855
65081
|
const enclosingProjectRoot = findNearestAncestorPackageJson(directory);
|
|
64856
65082
|
const enclosingProject = enclosingProjectRoot !== null ? discoverProject(enclosingProjectRoot) : null;
|
|
@@ -64917,12 +65143,15 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
64917
65143
|
sourceFileCount
|
|
64918
65144
|
};
|
|
64919
65145
|
};
|
|
64920
|
-
const discoverProject = (directory) => {
|
|
65146
|
+
const discoverProject = (directory, options = {}) => {
|
|
64921
65147
|
const cached = cachedProjectInfos.get(directory);
|
|
64922
|
-
if (cached !== void 0) return cached
|
|
65148
|
+
if (cached !== void 0) return options.sourceFileCount === void 0 ? cached : {
|
|
65149
|
+
...cached,
|
|
65150
|
+
sourceFileCount: options.sourceFileCount
|
|
65151
|
+
};
|
|
64923
65152
|
const packageJsonPath = Path.join(directory, "package.json");
|
|
64924
65153
|
if (!isFile(packageJsonPath)) {
|
|
64925
|
-
const synthesized = discoverProjectWithoutPackageJson(directory);
|
|
65154
|
+
const synthesized = discoverProjectWithoutPackageJson(directory, options);
|
|
64926
65155
|
cachedProjectInfos.set(directory, synthesized);
|
|
64927
65156
|
return synthesized;
|
|
64928
65157
|
}
|
|
@@ -64985,7 +65214,7 @@ const discoverProject = (directory) => {
|
|
|
64985
65214
|
const zodVersion = zod.version;
|
|
64986
65215
|
const projectName = packageJson.name ?? Path.basename(directory);
|
|
64987
65216
|
const hasTypeScript = NFS.existsSync(Path.join(directory, "tsconfig.json"));
|
|
64988
|
-
const sourceFileCount = countSourceFiles(directory);
|
|
65217
|
+
const sourceFileCount = options.sourceFileCount ?? countSourceFiles(directory);
|
|
64989
65218
|
const hasReactNativeWorkspace = framework === "expo" || framework === "react-native" || workspaceFacts.hasReactNativeAwarePackage;
|
|
64990
65219
|
const expoVersion = hasReactNativeWorkspace ? resolveCatalogBackedDependencyVersion({
|
|
64991
65220
|
rootDirectory: directory,
|
|
@@ -66367,7 +66596,8 @@ const readSystemFacts$1 = () => ({
|
|
|
66367
66596
|
/**
|
|
66368
66597
|
* Auto lint-worker count: the smaller of the (cgroup-CPU-aware) core count and
|
|
66369
66598
|
* the number of `PER_WORKER_MEM_BUDGET_BYTES` workers that fit in available
|
|
66370
|
-
* memory,
|
|
66599
|
+
* memory, and the measured automatic parallel-efficiency ceiling, then clamped
|
|
66600
|
+
* to `[MIN, HARD_MAX]` by `resolveScanConcurrency`.
|
|
66371
66601
|
*
|
|
66372
66602
|
* `os.availableParallelism()` already respects cgroup CPU quotas, so the core
|
|
66373
66603
|
* term needs no help. Available memory is `os.totalmem()` floored by the cgroup
|
|
@@ -66383,7 +66613,7 @@ const readSystemFacts$1 = () => ({
|
|
|
66383
66613
|
const resolveAutoScanConcurrency = (facts = readSystemFacts$1()) => {
|
|
66384
66614
|
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
66385
66615
|
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / PER_WORKER_MEM_BUDGET_BYTES);
|
|
66386
|
-
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
66616
|
+
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers, 10));
|
|
66387
66617
|
};
|
|
66388
66618
|
const resolveLintBatchOrdering = () => {
|
|
66389
66619
|
return process.env["REACT_DOCTOR_LINT_BATCH_ORDERING"]?.trim().toLowerCase() === "arrival" ? "arrival" : "cost";
|
|
@@ -66396,24 +66626,22 @@ const resolveLintBatchOrdering = () => {
|
|
|
66396
66626
|
*/
|
|
66397
66627
|
var OxlintSpawnTimeoutMs = class extends Reference("react-doctor/OxlintSpawnTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_OXLINT_SPAWN_TIMEOUT_MS", OXLINT_SPAWN_TIMEOUT_MS) }) {};
|
|
66398
66628
|
/**
|
|
66399
|
-
* Effect-side cap on the lint phase.
|
|
66400
|
-
*
|
|
66629
|
+
* Optional Effect-side cap on the lint phase. Unbounded unless explicitly
|
|
66630
|
+
* configured through the env var or a test layer.
|
|
66401
66631
|
* Tests override via `Layer.succeed(LintPhaseTimeoutMs, ...)`.
|
|
66402
66632
|
*/
|
|
66403
|
-
var LintPhaseTimeoutMs = class extends Reference("react-doctor/LintPhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_LINT_PHASE_TIMEOUT_MS",
|
|
66633
|
+
var LintPhaseTimeoutMs = class extends Reference("react-doctor/LintPhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_LINT_PHASE_TIMEOUT_MS", null) }) {};
|
|
66404
66634
|
/**
|
|
66405
|
-
* Effect-side cap on the dead-code phase
|
|
66406
|
-
*
|
|
66407
|
-
* type-heavy projects; tests override via
|
|
66635
|
+
* Optional Effect-side cap on the dead-code phase. Unbounded unless explicitly
|
|
66636
|
+
* configured through the env var or a test layer. Tests override via
|
|
66408
66637
|
* `Layer.succeed(DeadCodePhaseTimeoutMs, ...)`.
|
|
66409
66638
|
*/
|
|
66410
|
-
var DeadCodePhaseTimeoutMs = class extends Reference("react-doctor/DeadCodePhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_DEAD_CODE_PHASE_TIMEOUT_MS",
|
|
66639
|
+
var DeadCodePhaseTimeoutMs = class extends Reference("react-doctor/DeadCodePhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_DEAD_CODE_PHASE_TIMEOUT_MS", null) }) {};
|
|
66411
66640
|
/**
|
|
66412
|
-
*
|
|
66413
|
-
*
|
|
66414
|
-
* large repos; tests override via `Layer.succeed(ScanDeadlineMs, ...)`.
|
|
66641
|
+
* Optional overall scan deadline. Unbounded unless explicitly configured
|
|
66642
|
+
* through the env var or a test layer.
|
|
66415
66643
|
*/
|
|
66416
|
-
var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SCAN_DEADLINE_MS",
|
|
66644
|
+
var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SCAN_DEADLINE_MS", null) }) {};
|
|
66417
66645
|
/**
|
|
66418
66646
|
* Wall-clock budget for the supply-chain check when it runs on a background
|
|
66419
66647
|
* fiber overlapping the lint pass. Reads from the env var on startup so the
|
|
@@ -66486,14 +66714,14 @@ var DeadCodeOverlap = class extends Reference("react-doctor/DeadCodeOverlap", {
|
|
|
66486
66714
|
/**
|
|
66487
66715
|
* How the full-scan lint pass plans its file batches. `"cost"` (the default)
|
|
66488
66716
|
* builds size-balanced LPT batches (`planLintBatches`): the same mandatory
|
|
66489
|
-
* batch count as greedy chunking
|
|
66490
|
-
*
|
|
66717
|
+
* batch count as greedy chunking, but every batch gets an even share of files
|
|
66718
|
+
* AND bytes, so no full chunk is a straggler while
|
|
66491
66719
|
* the remainder-batch worker idles — and the heavy files are SPREAD across
|
|
66492
66720
|
* batches, the precondition the old sort-desc-then-chunk-100 `cost` mode
|
|
66493
66721
|
* lacked (it packed the heaviest files into one wave-1 straggler batch,
|
|
66494
66722
|
* measurably regressing size-skewed repos, which is why it never earned the
|
|
66495
66723
|
* default). `"arrival"` (`REACT_DOCTOR_LINT_BATCH_ORDERING=arrival`) is the
|
|
66496
|
-
* rollback hatch to plain greedy
|
|
66724
|
+
* rollback hatch to plain greedy fixed-size chunking in discovery order. Tests
|
|
66497
66725
|
* override via `Layer.succeed(LintBatchOrdering, ...)`. Diff / staged scans
|
|
66498
66726
|
* never reach this — they pass user-scoped `includePaths` that skip discovery
|
|
66499
66727
|
* and stay in arrival order; only the full-scan branch reads it.
|
|
@@ -67031,6 +67259,10 @@ const dedupeRelatedDiagnostics = (diagnostics) => {
|
|
|
67031
67259
|
}
|
|
67032
67260
|
return uniqueDiagnostics;
|
|
67033
67261
|
};
|
|
67262
|
+
const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
|
|
67263
|
+
const relativePath = Path.relative(parentAbsolutePath, childAbsolutePath);
|
|
67264
|
+
return Boolean(relativePath) && !relativePath.startsWith("..") && !Path.isAbsolute(relativePath);
|
|
67265
|
+
};
|
|
67034
67266
|
const compareStrings = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
67035
67267
|
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));
|
|
67036
67268
|
const getDirectDependencyNames = (packageJson) => new Set([...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.devDependencies ?? {})]);
|
|
@@ -68607,7 +68839,7 @@ const readScannedFile = (candidate) => {
|
|
|
68607
68839
|
return null;
|
|
68608
68840
|
}
|
|
68609
68841
|
};
|
|
68610
|
-
function* collectSecurityScanFiles(rootDirectory) {
|
|
68842
|
+
function* collectSecurityScanFiles(rootDirectory, excludedDirectories = /* @__PURE__ */ new Set()) {
|
|
68611
68843
|
const priorityCandidates = [];
|
|
68612
68844
|
const artifactCandidates = [];
|
|
68613
68845
|
const otherCandidates = [];
|
|
@@ -68622,7 +68854,7 @@ function* collectSecurityScanFiles(rootDirectory) {
|
|
|
68622
68854
|
for (const entry of readDirectoryEntries(current.absolutePath)) {
|
|
68623
68855
|
const absolutePath = Path.join(current.absolutePath, entry.name);
|
|
68624
68856
|
if (entry.isDirectory()) {
|
|
68625
|
-
if (!SKIPPED_DIRECTORY_NAMES.has(entry.name)) stack.push({
|
|
68857
|
+
if (!SKIPPED_DIRECTORY_NAMES.has(entry.name) && !excludedDirectories.has(Path.resolve(absolutePath))) stack.push({
|
|
68626
68858
|
absolutePath,
|
|
68627
68859
|
depth: current.depth + 1
|
|
68628
68860
|
});
|
|
@@ -68803,10 +69035,8 @@ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledWhe
|
|
|
68803
69035
|
}
|
|
68804
69036
|
return true;
|
|
68805
69037
|
};
|
|
69038
|
+
const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
|
|
68806
69039
|
const shouldEnableRuleByDefaultStatus = (options) => options.defaultEnabled !== false || options.hasExplicitOverride === true || options.includeTagDefaults && options.hasIncludedTags;
|
|
68807
|
-
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
68808
|
-
setImmediate(resolve);
|
|
68809
|
-
});
|
|
68810
69040
|
const createSecurityScanSession = (rootDirectory, options) => {
|
|
68811
69041
|
const capabilities = options.project ? getCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
68812
69042
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
@@ -68859,19 +69089,35 @@ const createSecurityScanSession = (rootDirectory, options) => {
|
|
|
68859
69089
|
};
|
|
68860
69090
|
};
|
|
68861
69091
|
const checkSecurityScanCooperative = async (rootDirectory, options = {}) => {
|
|
69092
|
+
const throwIfScanAborted = () => {
|
|
69093
|
+
options.signal?.throwIfAborted();
|
|
69094
|
+
};
|
|
69095
|
+
const didReachDeadline = () => {
|
|
69096
|
+
const hasDeadlineElapsed = options.deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(options.deadlineEpochMs) === 0;
|
|
69097
|
+
if (hasDeadlineElapsed) options.onDeadlineExceeded?.();
|
|
69098
|
+
return hasDeadlineElapsed;
|
|
69099
|
+
};
|
|
69100
|
+
throwIfScanAborted();
|
|
68862
69101
|
const session = createSecurityScanSession(rootDirectory, options);
|
|
68863
69102
|
if (session === null) return [];
|
|
69103
|
+
if (didReachDeadline()) return [];
|
|
68864
69104
|
let sliceStartedAt = performance.now();
|
|
68865
|
-
for (const file of collectSecurityScanFiles(rootDirectory)) {
|
|
69105
|
+
for (const file of collectSecurityScanFiles(rootDirectory, options.excludedDirectories)) {
|
|
69106
|
+
throwIfScanAborted();
|
|
69107
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
68866
69108
|
if (file === null) {
|
|
68867
69109
|
if (performance.now() - sliceStartedAt >= 12) {
|
|
68868
69110
|
await yieldToEventLoop();
|
|
69111
|
+
throwIfScanAborted();
|
|
69112
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
68869
69113
|
sliceStartedAt = performance.now();
|
|
68870
69114
|
}
|
|
68871
69115
|
continue;
|
|
68872
69116
|
}
|
|
68873
69117
|
for (const _ruleStep of session.scanFileByRule(file)) if (performance.now() - sliceStartedAt >= 12) {
|
|
68874
69118
|
await yieldToEventLoop();
|
|
69119
|
+
throwIfScanAborted();
|
|
69120
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
68875
69121
|
sliceStartedAt = performance.now();
|
|
68876
69122
|
}
|
|
68877
69123
|
}
|
|
@@ -69785,7 +70031,6 @@ const isDiagnosticOnSurface = (diagnostic, surface, config) => {
|
|
|
69785
70031
|
return true;
|
|
69786
70032
|
};
|
|
69787
70033
|
const filterDiagnosticsForSurface = (diagnostics, surface, config) => diagnostics.filter((diagnostic) => isDiagnosticOnSurface(diagnostic, surface, config));
|
|
69788
|
-
const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
|
|
69789
70034
|
/**
|
|
69790
70035
|
* Budget for the dead-code phase, scaled to the work. deslop's graph build is
|
|
69791
70036
|
* CPU-bound and roughly linear in file count, so a fixed 120s cap is too tight
|
|
@@ -69793,25 +70038,28 @@ const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs
|
|
|
69793
70038
|
* tipped over by any concurrent load — silently dropping every dead-code
|
|
69794
70039
|
* finding. Scaling the budget with file count (and inversely with the core
|
|
69795
70040
|
* share when overlapped) lets the pass complete, while the ceiling still
|
|
69796
|
-
* reclaims a genuinely wedged worker.
|
|
69797
|
-
* and the Effect-side phase backstop that sits a margin above it.
|
|
70041
|
+
* reclaims a genuinely wedged worker.
|
|
69798
70042
|
*/
|
|
69799
70043
|
const resolveDeadCodeTimeout = (input) => {
|
|
69800
70044
|
const coreShareFactor = Math.max(1, input.fullConcurrency / Math.max(1, input.deadCodeConcurrency));
|
|
69801
|
-
|
|
69802
|
-
return {
|
|
69803
|
-
workerTimeoutMs,
|
|
69804
|
-
phaseTimeoutMs: workerTimeoutMs + DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS
|
|
69805
|
-
};
|
|
70045
|
+
return { workerTimeoutMs: Math.min(DEAD_CODE_TIMEOUT_CEILING_MS, Math.max(DEAD_CODE_WORKER_TIMEOUT_MS, Math.ceil(input.sourceFileCount * 30 * coreShareFactor))) };
|
|
69806
70046
|
};
|
|
69807
|
-
const resolveLintIncludePaths = (rootDirectory, userConfig) => {
|
|
70047
|
+
const resolveLintIncludePaths = (rootDirectory, userConfig, sourceFiles) => {
|
|
69808
70048
|
if (!Array.isArray(userConfig?.ignore?.files) || userConfig.ignore.files.length === 0) return;
|
|
69809
70049
|
const ignoredPatterns = compileIgnoredFilePatterns(userConfig);
|
|
69810
|
-
return listSourceFiles(rootDirectory).filter((filePath) => {
|
|
70050
|
+
return (sourceFiles ?? listSourceFiles(rootDirectory)).filter((filePath) => {
|
|
69811
70051
|
if (!isLintableSourceFile(filePath)) return false;
|
|
69812
70052
|
return !isFileIgnoredByPatterns(filePath, rootDirectory, ignoredPatterns);
|
|
69813
70053
|
});
|
|
69814
70054
|
};
|
|
70055
|
+
const filterPathsOutsideDirectories = (input) => {
|
|
70056
|
+
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}/`)));
|
|
70057
|
+
if (excludedPrefixes.length === 0) return [...input.relativePaths];
|
|
70058
|
+
return input.relativePaths.filter((relativePath) => {
|
|
70059
|
+
const normalizedRelativePath = relativePath.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
70060
|
+
return !excludedPrefixes.some((excludedPrefix) => normalizedRelativePath === excludedPrefix || normalizedRelativePath.startsWith(`${excludedPrefix}/`));
|
|
70061
|
+
});
|
|
70062
|
+
};
|
|
69815
70063
|
var Config = class Config extends Service()("react-doctor/Config") {
|
|
69816
70064
|
static layerNode = effect(Config, gen(function* () {
|
|
69817
70065
|
const cache = yield* make$3({
|
|
@@ -69873,6 +70121,7 @@ var Files = class Files extends Service()("react-doctor/Files") {
|
|
|
69873
70121
|
static layerNode = succeed$3(Files, Files.of({
|
|
69874
70122
|
readLines: (input) => sync(() => createNodeReadFileLinesSync(input.rootDirectory)(input.filePath)),
|
|
69875
70123
|
listSourceFiles: (rootDirectory) => sync(() => listSourceFiles(rootDirectory)),
|
|
70124
|
+
listSourceFilesCooperative: (input) => promise(() => listSourceFilesCooperative(input.rootDirectory, input.signal)),
|
|
69876
70125
|
isFile: (filePath) => sync(() => isFile(filePath)),
|
|
69877
70126
|
isDirectory: (filePath) => sync(() => isDirectory(filePath))
|
|
69878
70127
|
}));
|
|
@@ -69899,6 +70148,16 @@ var Files = class Files extends Service()("react-doctor/Files") {
|
|
|
69899
70148
|
}
|
|
69900
70149
|
return files;
|
|
69901
70150
|
}),
|
|
70151
|
+
listSourceFilesCooperative: (input) => sync(() => {
|
|
70152
|
+
const rootDirectory = input.rootDirectory;
|
|
70153
|
+
const prefix = rootDirectory.endsWith("/") ? rootDirectory : `${rootDirectory}/`;
|
|
70154
|
+
const files = [];
|
|
70155
|
+
for (const absolute of tree.keys()) {
|
|
70156
|
+
if (!absolute.startsWith(prefix)) continue;
|
|
70157
|
+
files.push(absolute.slice(prefix.length));
|
|
70158
|
+
}
|
|
70159
|
+
return files;
|
|
70160
|
+
}),
|
|
69902
70161
|
isFile: (filePath) => sync(() => tree.has(filePath)),
|
|
69903
70162
|
isDirectory: (filePath) => sync(() => {
|
|
69904
70163
|
const prefix = filePath.endsWith("/") ? filePath : `${filePath}/`;
|
|
@@ -70474,7 +70733,7 @@ const batchIncludePaths = (baseArgs, includePaths) => {
|
|
|
70474
70733
|
for (const filePath of includePaths) {
|
|
70475
70734
|
const entryLength = filePath.length + 1;
|
|
70476
70735
|
const exceedsArgLength = currentBatch.length > 0 && currentBatchLength + entryLength > 24e3;
|
|
70477
|
-
const exceedsFileCount = currentBatch.length >=
|
|
70736
|
+
const exceedsFileCount = currentBatch.length >= 200;
|
|
70478
70737
|
if (exceedsArgLength || exceedsFileCount) {
|
|
70479
70738
|
batches.push(currentBatch);
|
|
70480
70739
|
currentBatch = [];
|
|
@@ -70821,7 +71080,7 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
70821
71080
|
}
|
|
70822
71081
|
return enabled;
|
|
70823
71082
|
};
|
|
70824
|
-
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 }) => {
|
|
71083
|
+
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 }) => {
|
|
70825
71084
|
const hasIncludedTags = includedTags.size > 0;
|
|
70826
71085
|
const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" || hasIncludedTags ? null : resolveReactHooksJsPlugin(project.hasReactCompiler || project.hasReactCompilerLintPlugin === true, customRulesOnly);
|
|
70827
71086
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
@@ -70885,7 +71144,8 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
70885
71144
|
}))
|
|
70886
71145
|
} : {},
|
|
70887
71146
|
...project.shopifyFlashListMajorVersion !== null ? { shopifyFlashListMajorVersion: project.shopifyFlashListMajorVersion } : {},
|
|
70888
|
-
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {}
|
|
71147
|
+
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {},
|
|
71148
|
+
...projectIndexModuleSources !== void 0 ? { projectIndexModuleSources: [...projectIndexModuleSources] } : {}
|
|
70889
71149
|
} },
|
|
70890
71150
|
rules: {
|
|
70891
71151
|
...reactCompilerRules,
|
|
@@ -72370,6 +72630,12 @@ const buildProfiledNodeArguments = (input) => [
|
|
|
72370
72630
|
...input.heapProfileDirectory ? ["--heap-prof", `--heap-prof-dir=${input.heapProfileDirectory}`] : [],
|
|
72371
72631
|
...input.argumentsList
|
|
72372
72632
|
];
|
|
72633
|
+
const lowerChildProcessPriority = (processId) => {
|
|
72634
|
+
if (processId === void 0) return;
|
|
72635
|
+
try {
|
|
72636
|
+
OS.setPriority(processId, OS.constants.priority.PRIORITY_BELOW_NORMAL);
|
|
72637
|
+
} catch {}
|
|
72638
|
+
};
|
|
72373
72639
|
const SANITIZED_ENV = buildOxlintChildEnv(process.env);
|
|
72374
72640
|
/**
|
|
72375
72641
|
* Spawn one oxlint subprocess with hard ceilings on wall time and
|
|
@@ -72408,6 +72674,7 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
|
|
|
72408
72674
|
"pipe"
|
|
72409
72675
|
]
|
|
72410
72676
|
});
|
|
72677
|
+
lowerChildProcessPriority(child.pid);
|
|
72411
72678
|
const onAbort = () => {
|
|
72412
72679
|
child.kill("SIGKILL");
|
|
72413
72680
|
reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) }));
|
|
@@ -72530,7 +72797,7 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
72530
72797
|
* loop with a slimmer config in that case.
|
|
72531
72798
|
*/
|
|
72532
72799
|
const spawnLintBatches = async (input) => {
|
|
72533
|
-
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, sourceMapByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth =
|
|
72800
|
+
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, sourceMapByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 9, signal, deadlineEpochMs } = input;
|
|
72534
72801
|
const resolveSourcePath = (filePath) => {
|
|
72535
72802
|
const absoluteFilePath = Path.isAbsolute(filePath) ? filePath : Path.resolve(rootDirectory, filePath);
|
|
72536
72803
|
return sourcePathByLintPath?.get(Path.normalize(absoluteFilePath)) ?? filePath;
|
|
@@ -72538,6 +72805,12 @@ const spawnLintBatches = async (input) => {
|
|
|
72538
72805
|
const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
|
|
72539
72806
|
let rescueDeadlineEpochMs;
|
|
72540
72807
|
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0 || rescueDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(rescueDeadlineEpochMs) === 0;
|
|
72808
|
+
const resolveSpawnTimeoutMs = () => {
|
|
72809
|
+
const deadlineBudgets = [deadlineEpochMs, rescueDeadlineEpochMs].filter((deadline) => deadline !== void 0).map(remainingDeadlineBudgetMs);
|
|
72810
|
+
if (deadlineBudgets.length === 0) return spawnTimeoutMs;
|
|
72811
|
+
const remainingDeadlineMs = Math.min(...deadlineBudgets);
|
|
72812
|
+
return spawnTimeoutMs === void 0 ? remainingDeadlineMs : Math.min(spawnTimeoutMs, remainingDeadlineMs);
|
|
72813
|
+
};
|
|
72541
72814
|
const runBatchPass = async (concurrency, passBatches, passOnFileProgress) => {
|
|
72542
72815
|
const totalFileCount = passBatches.reduce((sum, batch) => sum + batch.length, 0);
|
|
72543
72816
|
const allDiagnostics = [];
|
|
@@ -72555,12 +72828,13 @@ const spawnLintBatches = async (input) => {
|
|
|
72555
72828
|
const batchArgs = [...baseArgs, ...batch];
|
|
72556
72829
|
try {
|
|
72557
72830
|
const spawnBatch = () => {
|
|
72558
|
-
|
|
72831
|
+
const effectiveSpawnTimeoutMs = resolveSpawnTimeoutMs();
|
|
72832
|
+
if (effectiveSpawnTimeoutMs === 0) {
|
|
72559
72833
|
deadlineSkippedFiles.push(...batch);
|
|
72560
72834
|
batchState.deadlineSkippedFileCount += batch.length;
|
|
72561
72835
|
return Promise.resolve(null);
|
|
72562
72836
|
}
|
|
72563
|
-
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath,
|
|
72837
|
+
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, effectiveSpawnTimeoutMs, outputMaxBytes, signal, () => {
|
|
72564
72838
|
if (batchState.didStart) return;
|
|
72565
72839
|
batchState.didStart = true;
|
|
72566
72840
|
startedFileCount += batchState.initialFileCount;
|
|
@@ -72715,11 +72989,36 @@ const validateRuleRegistration = () => {
|
|
|
72715
72989
|
].filter((entry) => entry !== null).join("; ");
|
|
72716
72990
|
console.warn(`[react-doctor] rule-registration drift: ${detail}`);
|
|
72717
72991
|
};
|
|
72992
|
+
const PROJECT_INDEX_MODULE_SOURCES = [
|
|
72993
|
+
"next/og",
|
|
72994
|
+
"@vercel/og",
|
|
72995
|
+
"satori",
|
|
72996
|
+
"remotion"
|
|
72997
|
+
];
|
|
72998
|
+
const collectProjectIndexModuleSources = async (rootDirectory, candidateFiles) => {
|
|
72999
|
+
const foundModuleSources = /* @__PURE__ */ new Set();
|
|
73000
|
+
let sliceStartedAt = performance.now();
|
|
73001
|
+
for (const candidateFile of candidateFiles) {
|
|
73002
|
+
let sourceBuffer;
|
|
73003
|
+
try {
|
|
73004
|
+
sourceBuffer = NFS.readFileSync(Path.isAbsolute(candidateFile) ? candidateFile : Path.resolve(rootDirectory, candidateFile));
|
|
73005
|
+
} catch {
|
|
73006
|
+
continue;
|
|
73007
|
+
}
|
|
73008
|
+
for (const moduleSource of PROJECT_INDEX_MODULE_SOURCES) if (sourceBuffer.includes(`"${moduleSource}"`) || sourceBuffer.includes(`'${moduleSource}'`)) foundModuleSources.add(moduleSource);
|
|
73009
|
+
if (foundModuleSources.size === PROJECT_INDEX_MODULE_SOURCES.length) break;
|
|
73010
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
73011
|
+
await yieldToEventLoop();
|
|
73012
|
+
sliceStartedAt = performance.now();
|
|
73013
|
+
}
|
|
73014
|
+
}
|
|
73015
|
+
return [...foundModuleSources].sort();
|
|
73016
|
+
};
|
|
72718
73017
|
/**
|
|
72719
73018
|
* Balanced LPT (longest-processing-time-first) lint batch planner. Where
|
|
72720
|
-
* `batchIncludePaths` greedily fills
|
|
72721
|
-
* remainder batch
|
|
72722
|
-
*
|
|
73019
|
+
* `batchIncludePaths` greedily fills fixed-size batches and leaves a small
|
|
73020
|
+
* remainder batch, so the parallel pool's wall clock is set by whichever
|
|
73021
|
+
* full chunk happened to collect the most work while
|
|
72723
73022
|
* the remainder's worker idles), this planner keeps the SAME mandatory batch
|
|
72724
73023
|
* count — `ceil(files / OXLINT_MAX_FILES_PER_BATCH)`, the JS-plugin
|
|
72725
73024
|
* perf-cliff / native-binding SIGABRT guard — and assigns files largest-first,
|
|
@@ -72748,7 +73047,7 @@ const validateRuleRegistration = () => {
|
|
|
72748
73047
|
*/
|
|
72749
73048
|
const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
72750
73049
|
if (files.length === 0) return [];
|
|
72751
|
-
const batchCount = Math.ceil(files.length /
|
|
73050
|
+
const batchCount = Math.ceil(files.length / 200);
|
|
72752
73051
|
const maxFilesPerBatch = Math.ceil(files.length / batchCount);
|
|
72753
73052
|
const baseArgsLengthChars = estimateArgsLength(baseArgs);
|
|
72754
73053
|
const descendingSizeIndexes = [...files.keys()].sort((leftIndex, rightIndex) => (sizeByFile.get(files[rightIndex]) ?? 0) - (sizeByFile.get(files[leftIndex]) ?? 0));
|
|
@@ -73277,6 +73576,7 @@ const runOxlint = async (options) => {
|
|
|
73277
73576
|
await yieldToEventLoop();
|
|
73278
73577
|
const sizedScanFiles = includePaths === void 0 ? listSourceFilesWithSize(rootDirectory) : null;
|
|
73279
73578
|
const candidateFiles = includePaths !== void 0 ? includePaths : (sizedScanFiles ?? []).map((entry) => entry.path);
|
|
73579
|
+
const projectIndexModuleSources = includePaths === void 0 && options.deadlineEpochMs === void 0 ? await collectProjectIndexModuleSources(rootDirectory, candidateFiles) : void 0;
|
|
73280
73580
|
unpluginAutoImportGlobalScopes = collectUnpluginAutoImportGlobalScopes({
|
|
73281
73581
|
rootDirectory,
|
|
73282
73582
|
candidateFiles
|
|
@@ -73299,6 +73599,7 @@ const runOxlint = async (options) => {
|
|
|
73299
73599
|
runtimeGlobals,
|
|
73300
73600
|
unpluginAutoImportGlobalScopes,
|
|
73301
73601
|
serverAuthFunctionNames,
|
|
73602
|
+
projectIndexModuleSources,
|
|
73302
73603
|
severityControls,
|
|
73303
73604
|
userPlugins,
|
|
73304
73605
|
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
|
|
@@ -73766,10 +74067,10 @@ const translateProjectInfoError = (cause, directory) => {
|
|
|
73766
74067
|
}) });
|
|
73767
74068
|
};
|
|
73768
74069
|
var Project = class Project extends Service()("react-doctor/Project") {
|
|
73769
|
-
static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (
|
|
74070
|
+
static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (input) {
|
|
73770
74071
|
return yield* try_({
|
|
73771
|
-
try: () => discoverProject(directory),
|
|
73772
|
-
catch: (cause) => translateProjectInfoError(cause, directory)
|
|
74072
|
+
try: () => discoverProject(input.directory, { sourceFileCount: input.sourceFileCount }),
|
|
74073
|
+
catch: (cause) => translateProjectInfoError(cause, input.directory)
|
|
73773
74074
|
});
|
|
73774
74075
|
}) }));
|
|
73775
74076
|
static layerOf = (projectInfo) => succeed$3(Project, Project.of({ discover: () => succeed$2(projectInfo) }));
|
|
@@ -74354,9 +74655,10 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
74354
74655
|
* outage.
|
|
74355
74656
|
*/
|
|
74356
74657
|
var SupplyChain = class SupplyChain extends Service()("react-doctor/SupplyChain") {
|
|
74357
|
-
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (
|
|
74358
|
-
|
|
74359
|
-
|
|
74658
|
+
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (configuredTimeoutMs) => checkSupplyChain({
|
|
74659
|
+
rootDirectory: input.rootDirectory,
|
|
74660
|
+
userConfig: input.userConfig,
|
|
74661
|
+
totalTimeoutMs: input.timeoutMs ?? configuredTimeoutMs
|
|
74360
74662
|
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), withSpan("SupplyChain.run"))) }));
|
|
74361
74663
|
static layerOf = (diagnostics) => succeed$3(SupplyChain, SupplyChain.of({ run: () => fromIterable$1(diagnostics) }));
|
|
74362
74664
|
};
|
|
@@ -74472,8 +74774,28 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74472
74774
|
const partialFailuresRef = yield* LintPartialFailures;
|
|
74473
74775
|
const resolvedConfig = yield* configService.resolve(input.directory);
|
|
74474
74776
|
const scanDirectory = resolvedConfig.resolvedDirectory;
|
|
74475
|
-
const
|
|
74777
|
+
const excludedProjectDirectories = (input.excludedProjectDirectories ?? []).map((excludedDirectory) => Path.resolve(excludedDirectory)).filter((excludedDirectory) => isPathInsideDirectory(excludedDirectory, scanDirectory));
|
|
74778
|
+
const excludedProjectRelativePaths = excludedProjectDirectories.map((excludedDirectory) => Path.relative(scanDirectory, excludedDirectory).replaceAll("\\", "/"));
|
|
74779
|
+
const isExcludedProjectDiagnostic = (diagnostic) => {
|
|
74780
|
+
const relativeFilePath = toNormalizedRelativePath(diagnostic.filePath, scanDirectory);
|
|
74781
|
+
return excludedProjectRelativePaths.some((excludedRelativePath) => relativeFilePath === excludedRelativePath || relativeFilePath.startsWith(`${excludedRelativePath}/`));
|
|
74782
|
+
};
|
|
74783
|
+
const precomputedSourceFiles = input.suppressScanSummary === true || excludedProjectDirectories.length > 0 ? yield* filesService.listSourceFilesCooperative({
|
|
74784
|
+
rootDirectory: scanDirectory,
|
|
74785
|
+
signal: input.signal
|
|
74786
|
+
}) : null;
|
|
74787
|
+
const includedPrecomputedSourceFiles = precomputedSourceFiles !== null && excludedProjectDirectories.length > 0 ? filterPathsOutsideDirectories({
|
|
74788
|
+
rootDirectory: scanDirectory,
|
|
74789
|
+
relativePaths: precomputedSourceFiles,
|
|
74790
|
+
excludedDirectories: excludedProjectDirectories
|
|
74791
|
+
}) : precomputedSourceFiles;
|
|
74792
|
+
const sourceFileCount = excludedProjectDirectories.length > 0 ? includedPrecomputedSourceFiles?.length : input.precomputedSourceFileCount ?? includedPrecomputedSourceFiles?.length;
|
|
74793
|
+
const project = yield* projectService.discover({
|
|
74794
|
+
directory: scanDirectory,
|
|
74795
|
+
sourceFileCount
|
|
74796
|
+
});
|
|
74476
74797
|
if (!isAnalyzableProject(project)) return yield* new ReactDoctorError({ reason: new NoReactDependency({ directory: scanDirectory }) });
|
|
74798
|
+
const deadCodeSourceFileCount = input.retainExcludedProjectDeadCodeDiagnostics === true && precomputedSourceFiles !== null ? precomputedSourceFiles.length : project.sourceFileCount;
|
|
74477
74799
|
const [repo, sha, defaultBranch] = yield* all([
|
|
74478
74800
|
gitService.githubRepo(scanDirectory).pipe(orElseSucceed(() => null)),
|
|
74479
74801
|
gitService.headSha(scanDirectory).pipe(orElseSucceed(() => null)),
|
|
@@ -74484,8 +74806,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74484
74806
|
directory: scanDirectory,
|
|
74485
74807
|
repo
|
|
74486
74808
|
}).pipe(orElseSucceed(() => null)) : succeed$2(null));
|
|
74487
|
-
|
|
74488
|
-
|
|
74809
|
+
let lintIncludePaths = (input.skipExplicitIncludePathFilter ? input.includePaths.length > 0 ? [...input.includePaths] : void 0 : computeExplicitLintIncludePaths([...input.includePaths])) ?? resolveLintIncludePaths(scanDirectory, resolvedConfig.config, includedPrecomputedSourceFiles ?? void 0);
|
|
74810
|
+
if (excludedProjectDirectories.length > 0) lintIncludePaths = filterPathsOutsideDirectories({
|
|
74811
|
+
rootDirectory: scanDirectory,
|
|
74812
|
+
relativePaths: lintIncludePaths ?? includedPrecomputedSourceFiles ?? (yield* filesService.listSourceFiles(scanDirectory)),
|
|
74813
|
+
excludedDirectories: excludedProjectDirectories
|
|
74814
|
+
});
|
|
74815
|
+
const fallbackScannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? includedPrecomputedSourceFiles ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
|
|
74489
74816
|
const beforeLint = hooks.beforeLint ?? NO_HOOKS.beforeLint;
|
|
74490
74817
|
const afterLint = hooks.afterLint ?? NO_HOOKS.afterLint;
|
|
74491
74818
|
yield* beforeLint(project, lintIncludePaths ?? void 0);
|
|
@@ -74508,17 +74835,31 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74508
74835
|
...checkReactNativeProject(scanDirectory, project)
|
|
74509
74836
|
])));
|
|
74510
74837
|
const securityScanFailedRef = yield* make$13(false);
|
|
74511
|
-
|
|
74838
|
+
let didSecurityScanReachDeadline = false;
|
|
74839
|
+
const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(tryPromise((signal) => checkSecurityScanCooperative(scanDirectory, {
|
|
74512
74840
|
project,
|
|
74513
74841
|
ignoredTags: input.ignoredTags,
|
|
74514
74842
|
includedTags: input.includedTags,
|
|
74515
|
-
includeTagDefaults: input.includeTagDefaults
|
|
74843
|
+
includeTagDefaults: input.includeTagDefaults,
|
|
74844
|
+
excludedDirectories: new Set(excludedProjectDirectories),
|
|
74845
|
+
deadlineEpochMs: input.deadlineEpochMs,
|
|
74846
|
+
signal,
|
|
74847
|
+
onDeadlineExceeded: () => {
|
|
74848
|
+
didSecurityScanReachDeadline = true;
|
|
74849
|
+
}
|
|
74516
74850
|
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), catch_$1(() => set(securityScanFailedRef, true).pipe(as(empty$4))))))).pipe(withSpan("SecurityScan.run")));
|
|
74851
|
+
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
74852
|
+
const capOptionalToDeadline = (phaseTimeoutMs) => {
|
|
74853
|
+
if (input.deadlineEpochMs === void 0) return phaseTimeoutMs;
|
|
74854
|
+
const remainingBudgetMs = remainingDeadlineBudgetMs(input.deadlineEpochMs);
|
|
74855
|
+
return phaseTimeoutMs === null ? remainingBudgetMs : Math.min(phaseTimeoutMs, remainingBudgetMs);
|
|
74856
|
+
};
|
|
74517
74857
|
const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
|
|
74518
|
-
const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
|
|
74858
|
+
const supplyChainOverlapTimeout = capToDeadline(yield* SupplyChainOverlapTimeoutMs);
|
|
74519
74859
|
const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
|
|
74520
74860
|
rootDirectory: scanDirectory,
|
|
74521
|
-
userConfig: resolvedConfig.config
|
|
74861
|
+
userConfig: resolvedConfig.config,
|
|
74862
|
+
timeoutMs: supplyChainOverlapTimeout
|
|
74522
74863
|
}))).pipe(map$3((diagnostics) => ({
|
|
74523
74864
|
diagnostics,
|
|
74524
74865
|
timedOut: false
|
|
@@ -74542,46 +74883,49 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74542
74883
|
const scanConcurrency = resolveScanConcurrency(yield* OxlintConcurrency);
|
|
74543
74884
|
const lintPhaseTimeoutMs = yield* LintPhaseTimeoutMs;
|
|
74544
74885
|
const deadCodePhaseTimeoutMs = yield* DeadCodePhaseTimeoutMs;
|
|
74545
|
-
const resolveDeadCodePhaseTimeoutMs = (scaledPhaseTimeoutMs) => deadCodePhaseTimeoutMs === 15e4 ? scaledPhaseTimeoutMs : deadCodePhaseTimeoutMs;
|
|
74546
74886
|
const workerCountSuffix = scanConcurrency > 1 ? ` ${highlighter.dim(`[~${scanConcurrency} workers]`)}` : "";
|
|
74547
|
-
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
74548
74887
|
const shouldRunDeadCode = input.runDeadCode && !isDiffMode && (showWarnings || deadCodeMaySurfaceWhenWarningsHidden(resolvedConfig.config));
|
|
74549
74888
|
const deadCodeOverlapMode = yield* DeadCodeOverlap;
|
|
74550
74889
|
const shouldOverlapDeadCode = shouldRunDeadCode && deadCodeOverlapMode === "on";
|
|
74551
74890
|
const deadCodeParseConcurrency = shouldOverlapDeadCode ? Math.max(1, Math.floor(scanConcurrency * DEAD_CODE_OVERLAP_PARSE_SHARE)) : void 0;
|
|
74552
74891
|
const lintConcurrency = deadCodeParseConcurrency === void 0 ? scanConcurrency : Math.max(1, scanConcurrency - deadCodeParseConcurrency);
|
|
74553
|
-
const buildCollectDeadCode = (deadCodeTimeout) =>
|
|
74554
|
-
|
|
74555
|
-
|
|
74556
|
-
|
|
74557
|
-
|
|
74558
|
-
|
|
74559
|
-
|
|
74560
|
-
|
|
74561
|
-
|
|
74562
|
-
|
|
74563
|
-
|
|
74564
|
-
|
|
74565
|
-
|
|
74566
|
-
|
|
74567
|
-
|
|
74568
|
-
|
|
74569
|
-
|
|
74570
|
-
|
|
74571
|
-
|
|
74572
|
-
|
|
74573
|
-
|
|
74574
|
-
|
|
74575
|
-
|
|
74576
|
-
|
|
74892
|
+
const buildCollectDeadCode = (deadCodeTimeout) => {
|
|
74893
|
+
const collectDeadCode = runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
74894
|
+
rootDirectory: scanDirectory,
|
|
74895
|
+
parseConcurrency: deadCodeParseConcurrency,
|
|
74896
|
+
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
|
|
74897
|
+
onCacheOutcome: (didHitCache) => {
|
|
74898
|
+
deadCodeCacheHit = didHitCache;
|
|
74899
|
+
},
|
|
74900
|
+
onSummaryCacheStats: (stats) => {
|
|
74901
|
+
deadCodeSummaryCacheHits = stats.hits;
|
|
74902
|
+
deadCodeSummaryCacheMisses = stats.misses;
|
|
74903
|
+
}
|
|
74904
|
+
}).pipe(filter((diagnostic) => input.retainExcludedProjectDeadCodeDiagnostics === true || !isExcludedProjectDiagnostic(diagnostic)), catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
74905
|
+
yield* set(deadCodeFailure, {
|
|
74906
|
+
didFail: true,
|
|
74907
|
+
reason: error.message
|
|
74908
|
+
});
|
|
74909
|
+
return empty$4;
|
|
74910
|
+
}))))));
|
|
74911
|
+
const phaseTimeoutMs = deadCodeTimeout.phaseTimeoutMs;
|
|
74912
|
+
if (phaseTimeoutMs === null) return collectDeadCode;
|
|
74913
|
+
return collectDeadCode.pipe(timeoutOption(phaseTimeoutMs), flatMap$2(match$3({
|
|
74914
|
+
onNone: () => set(deadCodeFailure, {
|
|
74915
|
+
didFail: true,
|
|
74916
|
+
reason: `Dead-code analysis exceeded ${Math.round(phaseTimeoutMs / MILLISECONDS_PER_SECOND)}s and was skipped.`
|
|
74917
|
+
}).pipe(as([])),
|
|
74918
|
+
onSome: succeed$2
|
|
74919
|
+
})));
|
|
74920
|
+
};
|
|
74577
74921
|
const overlapDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
74578
|
-
sourceFileCount:
|
|
74922
|
+
sourceFileCount: deadCodeSourceFileCount,
|
|
74579
74923
|
deadCodeConcurrency: deadCodeParseConcurrency ?? scanConcurrency,
|
|
74580
74924
|
fullConcurrency: scanConcurrency
|
|
74581
74925
|
});
|
|
74582
74926
|
const deadCodeFiber = shouldOverlapDeadCode ? yield* forkChild(buildCollectDeadCode({
|
|
74583
74927
|
workerTimeoutMs: overlapDeadCodeTimeout.workerTimeoutMs,
|
|
74584
|
-
phaseTimeoutMs:
|
|
74928
|
+
phaseTimeoutMs: capOptionalToDeadline(deadCodePhaseTimeoutMs)
|
|
74585
74929
|
})) : null;
|
|
74586
74930
|
const scanProgress = yield* progressService.start("Scanning...");
|
|
74587
74931
|
const scanStartTime = Date.now();
|
|
@@ -74632,7 +74976,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74632
74976
|
});
|
|
74633
74977
|
return empty$4;
|
|
74634
74978
|
}))));
|
|
74635
|
-
const
|
|
74979
|
+
const collectLintDiagnostics = runCollect(filterPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream));
|
|
74980
|
+
const lintCollected = dedupeRelatedDiagnostics(yield* lintPhaseTimeoutMs === null ? collectLintDiagnostics : collectLintDiagnostics.pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
|
|
74636
74981
|
onNone: () => set(lintFailure, {
|
|
74637
74982
|
didFail: true,
|
|
74638
74983
|
reason: `Lint analysis exceeded ${lintPhaseTimeoutMs / MILLISECONDS_PER_SECOND}s and was skipped.`,
|
|
@@ -74662,13 +75007,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74662
75007
|
} else {
|
|
74663
75008
|
yield* scanProgress.update(`Scanned ${scannedFilesLabel}, analyzing dead code...`);
|
|
74664
75009
|
const sequentialDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
74665
|
-
sourceFileCount: totalFileCount,
|
|
75010
|
+
sourceFileCount: input.retainExcludedProjectDeadCodeDiagnostics === true ? deadCodeSourceFileCount : totalFileCount,
|
|
74666
75011
|
deadCodeConcurrency: scanConcurrency,
|
|
74667
75012
|
fullConcurrency: scanConcurrency
|
|
74668
75013
|
});
|
|
74669
75014
|
deadCodeCollected = deadCodeFiber !== null ? yield* join(deadCodeFiber) : yield* buildCollectDeadCode({
|
|
74670
75015
|
workerTimeoutMs: sequentialDeadCodeTimeout.workerTimeoutMs,
|
|
74671
|
-
phaseTimeoutMs:
|
|
75016
|
+
phaseTimeoutMs: capOptionalToDeadline(deadCodePhaseTimeoutMs)
|
|
74672
75017
|
});
|
|
74673
75018
|
}
|
|
74674
75019
|
const deadCodeFailureState = lintFailureState.didFail ? {
|
|
@@ -74712,7 +75057,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74712
75057
|
metadata: scoreMetadata
|
|
74713
75058
|
});
|
|
74714
75059
|
const lintPartialFailures = yield* get$2(partialFailuresRef);
|
|
74715
|
-
const
|
|
75060
|
+
const didSecurityScanFail = yield* get$2(securityScanFailedRef);
|
|
75061
|
+
const securityScanFailed = didSecurityScanFail || didSecurityScanReachDeadline;
|
|
75062
|
+
let securityScanFailureReason = null;
|
|
75063
|
+
if (didSecurityScanReachDeadline) securityScanFailureReason = "Security scan reached the max scan duration; findings collected before the deadline were preserved.";
|
|
75064
|
+
else if (didSecurityScanFail) securityScanFailureReason = "Security scan failed and was skipped.";
|
|
74716
75065
|
return {
|
|
74717
75066
|
project,
|
|
74718
75067
|
userConfig: resolvedConfig.config,
|
|
@@ -74735,6 +75084,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74735
75084
|
scanConcurrency,
|
|
74736
75085
|
supplyChainOverlapTimedOut: supplyChainResult.timedOut,
|
|
74737
75086
|
securityScanFailed,
|
|
75087
|
+
securityScanFailureReason,
|
|
74738
75088
|
lintCacheHitFileCount,
|
|
74739
75089
|
lintCacheTotalFileCount,
|
|
74740
75090
|
lintSidecarReplayedFileCount,
|
|
@@ -74750,7 +75100,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74750
75100
|
"inspect.runDeadCode": input.runDeadCode,
|
|
74751
75101
|
"inspect.isCi": input.isCi,
|
|
74752
75102
|
"inspect.scoreSurface": input.scoreSurface ?? "score"
|
|
74753
|
-
} }), (scanProgram) => flatMap$2(ScanDeadlineMs, (scanDeadlineMs) =>
|
|
75103
|
+
} }), (scanProgram) => flatMap$2(ScanDeadlineMs, (scanDeadlineMs) => {
|
|
75104
|
+
if (scanDeadlineMs === null) return scanProgram;
|
|
75105
|
+
return scanProgram.pipe(timeout(scanDeadlineMs), catchTag$1("TimeoutError", () => new ReactDoctorError({ reason: new ScanDeadlineExceeded({ detail: `${scanDeadlineMs / MILLISECONDS_PER_SECOND}s elapsed` }) })));
|
|
75106
|
+
}));
|
|
74754
75107
|
const parseNodeVersion = (versionString) => {
|
|
74755
75108
|
const [major = 0, minor = 0, patch = 0] = versionString.replace(/^v/, "").trim().split(".").map(Number);
|
|
74756
75109
|
return {
|
|
@@ -74851,16 +75204,6 @@ const resolveNodeForOxlint = () => {
|
|
|
74851
75204
|
});
|
|
74852
75205
|
});
|
|
74853
75206
|
/**
|
|
74854
|
-
* Zip-Slip defense: relative paths come from git (`diff --name-only`), which
|
|
74855
|
-
* normalizes during ordinary adds, but a crafted index/pack/symlinked tree can
|
|
74856
|
-
* smuggle `..` segments that escape the temp root. Resolve against the temp dir
|
|
74857
|
-
* and reject anything that lands outside before writing.
|
|
74858
|
-
*/
|
|
74859
|
-
const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
|
|
74860
|
-
const relative = path.relative(parentAbsolutePath, childAbsolutePath);
|
|
74861
|
-
return Boolean(relative) && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
74862
|
-
};
|
|
74863
|
-
/**
|
|
74864
75207
|
* Writes a set of source files (supplied by `readContent` — e.g.
|
|
74865
75208
|
* `git show <ref>:<path>` for a baseline tree, or `git show :<path>` for the
|
|
74866
75209
|
* index) into a temp tree that mirrors the project layout, copying the
|
|
@@ -75135,6 +75478,7 @@ const buildJsonReport = (input) => {
|
|
|
75135
75478
|
directory: input.directory,
|
|
75136
75479
|
diff: toJsonDiff(input.diff),
|
|
75137
75480
|
projects,
|
|
75481
|
+
...input.skippedProjects && input.skippedProjects.length > 0 ? { skippedProjects: [...input.skippedProjects] } : {},
|
|
75138
75482
|
diagnostics: flattenedDiagnostics,
|
|
75139
75483
|
summary,
|
|
75140
75484
|
elapsedMilliseconds: input.totalElapsedMilliseconds,
|
|
@@ -75159,7 +75503,7 @@ const buildSkippedChecks = (input) => {
|
|
|
75159
75503
|
else if (input.lintPartialFailures.length > 0) skippedCheckReasons["lint:partial"] = input.lintPartialFailures.join("; ");
|
|
75160
75504
|
if (input.didDeadCodeFail && input.deadCodeFailureReason !== null) skippedCheckReasons["dead-code"] = input.deadCodeFailureReason;
|
|
75161
75505
|
if (input.supplyChainOverlapTimedOut) skippedCheckReasons["supply-chain"] = "Supply-chain analysis timed out and was skipped.";
|
|
75162
|
-
if (input.securityScanFailed) skippedCheckReasons["security-scan"] = "Security scan failed and was skipped.";
|
|
75506
|
+
if (input.securityScanFailed) skippedCheckReasons["security-scan"] = input.securityScanFailureReason ?? "Security scan failed and was skipped.";
|
|
75163
75507
|
return {
|
|
75164
75508
|
skippedChecks,
|
|
75165
75509
|
skippedCheckReasons
|
|
@@ -75487,4 +75831,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
75487
75831
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
75488
75832
|
|
|
75489
75833
|
//# sourceMappingURL=index.js.map
|
|
75490
|
-
//# debugId=
|
|
75834
|
+
//# debugId=4b6a1dd7-2795-5a24-b1f2-8de4e74574e3
|