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/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
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]="e18e3c42-e18e-5087-9b71-eb1bcb203d6b")}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";
|
|
6
6
|
import * as Crypto from "node:crypto";
|
|
7
7
|
import crypto, { createHash } from "node:crypto";
|
|
8
|
-
import * as NFS from "node:fs";
|
|
9
|
-
import fs from "node:fs";
|
|
10
8
|
import * as Path from "node:path";
|
|
11
9
|
import path from "node:path";
|
|
10
|
+
import * as NFS from "node:fs";
|
|
11
|
+
import fs from "node:fs";
|
|
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 = [
|
|
@@ -62216,7 +62285,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
62216
62285
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
62217
62286
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
62218
62287
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
62219
|
-
const CORE_PACKAGE_VERSION = "0.9.
|
|
62288
|
+
const CORE_PACKAGE_VERSION = "0.9.4";
|
|
62220
62289
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
62221
62290
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
62222
62291
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -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 = "";
|
|
@@ -63988,13 +64205,7 @@ const hasReactDependency = (packageJson) => {
|
|
|
63988
64205
|
};
|
|
63989
64206
|
return Object.keys(allDependencies).some((packageName) => REACT_DEPENDENCY_NAMES.has(packageName));
|
|
63990
64207
|
};
|
|
63991
|
-
const getPreactVersion = (packageJson) =>
|
|
63992
|
-
return {
|
|
63993
|
-
...packageJson.peerDependencies,
|
|
63994
|
-
...packageJson.dependencies,
|
|
63995
|
-
...packageJson.devDependencies
|
|
63996
|
-
}.preact ?? null;
|
|
63997
|
-
};
|
|
64208
|
+
const getPreactVersion = (packageJson) => getDependencySpec(packageJson, "preact");
|
|
63998
64209
|
const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, sourceDirectory = rootDirectory, sourcePackageJson = rootPackageJson, version }) => {
|
|
63999
64210
|
if (version === null || !isCatalogReference(version)) return version;
|
|
64000
64211
|
const catalogName = extractCatalogName(version);
|
|
@@ -64739,7 +64950,16 @@ const getBrowserslistProtectionRequirement = (directory) => {
|
|
|
64739
64950
|
if (!queries) return void 0;
|
|
64740
64951
|
let requirement;
|
|
64741
64952
|
let hasUnsupportedTarget = false;
|
|
64742
|
-
|
|
64953
|
+
const previousIgnoreOldData = process.env.BROWSERSLIST_IGNORE_OLD_DATA;
|
|
64954
|
+
let browserTargets = [];
|
|
64955
|
+
try {
|
|
64956
|
+
process.env.BROWSERSLIST_IGNORE_OLD_DATA = "true";
|
|
64957
|
+
browserTargets = (0, import_browserslist.default)(queries);
|
|
64958
|
+
} finally {
|
|
64959
|
+
if (previousIgnoreOldData === void 0) delete process.env.BROWSERSLIST_IGNORE_OLD_DATA;
|
|
64960
|
+
else process.env.BROWSERSLIST_IGNORE_OLD_DATA = previousIgnoreOldData;
|
|
64961
|
+
}
|
|
64962
|
+
for (const browserTarget of browserTargets) {
|
|
64743
64963
|
const browserRequirement = getBrowserProtectionRequirement(browserTarget);
|
|
64744
64964
|
if (browserRequirement === "unsupported") hasUnsupportedTarget = true;
|
|
64745
64965
|
if (browserRequirement === "noreferrer") requirement = "noreferrer";
|
|
@@ -64849,9 +65069,9 @@ const clearProjectCache = () => {
|
|
|
64849
65069
|
* framework-agnostic rules. Throws only when the directory has nothing
|
|
64850
65070
|
* to scan (no enclosing project and no source files of its own).
|
|
64851
65071
|
*/
|
|
64852
|
-
const discoverProjectWithoutPackageJson = (directory) => {
|
|
64853
|
-
const sourceFileCount = countSourceFiles(directory);
|
|
64854
|
-
const hasOwnTsConfig =
|
|
65072
|
+
const discoverProjectWithoutPackageJson = (directory, options) => {
|
|
65073
|
+
const sourceFileCount = options.sourceFileCount ?? countSourceFiles(directory);
|
|
65074
|
+
const hasOwnTsConfig = isFile(Path.join(directory, "tsconfig.json"));
|
|
64855
65075
|
const enclosingProjectRoot = findNearestAncestorPackageJson(directory);
|
|
64856
65076
|
const enclosingProject = enclosingProjectRoot !== null ? discoverProject(enclosingProjectRoot) : null;
|
|
64857
65077
|
if (enclosingProject !== null) return {
|
|
@@ -64917,12 +65137,15 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
64917
65137
|
sourceFileCount
|
|
64918
65138
|
};
|
|
64919
65139
|
};
|
|
64920
|
-
const discoverProject = (directory) => {
|
|
65140
|
+
const discoverProject = (directory, options = {}) => {
|
|
64921
65141
|
const cached = cachedProjectInfos.get(directory);
|
|
64922
|
-
if (cached !== void 0) return cached
|
|
65142
|
+
if (cached !== void 0) return options.sourceFileCount === void 0 ? cached : {
|
|
65143
|
+
...cached,
|
|
65144
|
+
sourceFileCount: options.sourceFileCount
|
|
65145
|
+
};
|
|
64923
65146
|
const packageJsonPath = Path.join(directory, "package.json");
|
|
64924
65147
|
if (!isFile(packageJsonPath)) {
|
|
64925
|
-
const synthesized = discoverProjectWithoutPackageJson(directory);
|
|
65148
|
+
const synthesized = discoverProjectWithoutPackageJson(directory, options);
|
|
64926
65149
|
cachedProjectInfos.set(directory, synthesized);
|
|
64927
65150
|
return synthesized;
|
|
64928
65151
|
}
|
|
@@ -64984,8 +65207,8 @@ const discoverProject = (directory) => {
|
|
|
64984
65207
|
const tailwindVersion = tailwindcss.version;
|
|
64985
65208
|
const zodVersion = zod.version;
|
|
64986
65209
|
const projectName = packageJson.name ?? Path.basename(directory);
|
|
64987
|
-
const hasTypeScript =
|
|
64988
|
-
const sourceFileCount = countSourceFiles(directory);
|
|
65210
|
+
const hasTypeScript = isFile(Path.join(directory, "tsconfig.json"));
|
|
65211
|
+
const sourceFileCount = options.sourceFileCount ?? countSourceFiles(directory);
|
|
64989
65212
|
const hasReactNativeWorkspace = framework === "expo" || framework === "react-native" || workspaceFacts.hasReactNativeAwarePackage;
|
|
64990
65213
|
const expoVersion = hasReactNativeWorkspace ? resolveCatalogBackedDependencyVersion({
|
|
64991
65214
|
rootDirectory: directory,
|
|
@@ -65114,134 +65337,6 @@ const discoverProject = (directory) => {
|
|
|
65114
65337
|
* single source file has nothing to analyze.
|
|
65115
65338
|
*/
|
|
65116
65339
|
const isAnalyzableProject = (project) => project.reactVersion !== null || project.preactVersion !== null || project.sourceFileCount > 0;
|
|
65117
|
-
const messageFromUnknown = (error) => error instanceof Error ? error.message : String(error);
|
|
65118
|
-
var InvalidGlobPatternError = class extends Error {
|
|
65119
|
-
pattern;
|
|
65120
|
-
reason;
|
|
65121
|
-
constructor(pattern, reason) {
|
|
65122
|
-
super(`Invalid glob pattern ${JSON.stringify(pattern)}: ${reason}`);
|
|
65123
|
-
this.name = "InvalidGlobPatternError";
|
|
65124
|
-
this.pattern = pattern;
|
|
65125
|
-
this.reason = reason;
|
|
65126
|
-
}
|
|
65127
|
-
};
|
|
65128
|
-
const assertGlobPattern = (condition, pattern, reason) => {
|
|
65129
|
-
if (!condition) throw new InvalidGlobPatternError(pattern, reason);
|
|
65130
|
-
};
|
|
65131
|
-
const countGlobWildcards = (pattern) => (pattern.match(/[*?]/g) ?? []).length;
|
|
65132
|
-
const normalizeGlobPattern = (pattern) => pattern.replace(/\\/g, "/").replace(/^\//, "");
|
|
65133
|
-
const PICOMATCH_OPTIONS = {
|
|
65134
|
-
dot: true,
|
|
65135
|
-
strictSlashes: false,
|
|
65136
|
-
windows: false
|
|
65137
|
-
};
|
|
65138
|
-
const compileGlobPattern = (rawPattern) => {
|
|
65139
|
-
assertGlobPattern(typeof rawPattern === "string" && rawPattern.length > 0, String(rawPattern), "pattern must be a non-empty string.");
|
|
65140
|
-
assertGlobPattern(rawPattern.length <= MAX_GLOB_PATTERN_LENGTH_CHARS, rawPattern, `pattern length ${rawPattern.length} exceeds the maximum of ${MAX_GLOB_PATTERN_LENGTH_CHARS} characters.`);
|
|
65141
|
-
const wildcardCount = countGlobWildcards(rawPattern);
|
|
65142
|
-
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.`);
|
|
65143
|
-
try {
|
|
65144
|
-
return import_picomatch.default.makeRe(normalizeGlobPattern(rawPattern), PICOMATCH_OPTIONS);
|
|
65145
|
-
} catch (caughtError) {
|
|
65146
|
-
throw new InvalidGlobPatternError(rawPattern, messageFromUnknown(caughtError));
|
|
65147
|
-
}
|
|
65148
|
-
};
|
|
65149
|
-
const compileGlobPatternsLenient = (patterns, onInvalid) => {
|
|
65150
|
-
const compiled = [];
|
|
65151
|
-
for (const pattern of patterns) try {
|
|
65152
|
-
compiled.push(compileGlobPattern(pattern));
|
|
65153
|
-
} catch (caughtError) {
|
|
65154
|
-
if (!(caughtError instanceof InvalidGlobPatternError)) throw caughtError;
|
|
65155
|
-
onInvalid(caughtError);
|
|
65156
|
-
}
|
|
65157
|
-
return compiled;
|
|
65158
|
-
};
|
|
65159
|
-
const toRelativePath = (filePath, rootDirectory) => {
|
|
65160
|
-
const normalizedFilePath = filePath.replace(/\\/g, "/");
|
|
65161
|
-
const normalizedRoot = rootDirectory.replace(/\\/g, "/").replace(/\/$/, "") + "/";
|
|
65162
|
-
if (normalizedFilePath.startsWith(normalizedRoot)) return normalizedFilePath.slice(normalizedRoot.length);
|
|
65163
|
-
return normalizedFilePath.replace(/^\.\//, "");
|
|
65164
|
-
};
|
|
65165
|
-
const warnConfigIssue = (message) => {
|
|
65166
|
-
process.stderr.write(`[react-doctor] ${message}\n`);
|
|
65167
|
-
};
|
|
65168
|
-
const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
65169
|
-
const collectStringList = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
65170
|
-
const validateOverrideEntry = (entry, index) => {
|
|
65171
|
-
if (!isPlainObject(entry)) {
|
|
65172
|
-
warnConfigIssue(`ignore.overrides[${index}] must be an object with { files, rules }; ignoring this entry.`);
|
|
65173
|
-
return null;
|
|
65174
|
-
}
|
|
65175
|
-
if (!isStringArray(entry.files)) {
|
|
65176
|
-
warnConfigIssue(`ignore.overrides[${index}].files must be an array of strings; ignoring this entry.`);
|
|
65177
|
-
return null;
|
|
65178
|
-
}
|
|
65179
|
-
if (entry.rules !== void 0 && !isStringArray(entry.rules)) {
|
|
65180
|
-
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).`);
|
|
65181
|
-
return { files: entry.files };
|
|
65182
|
-
}
|
|
65183
|
-
return entry.rules === void 0 ? { files: entry.files } : {
|
|
65184
|
-
files: entry.files,
|
|
65185
|
-
rules: entry.rules
|
|
65186
|
-
};
|
|
65187
|
-
};
|
|
65188
|
-
const compileIgnoreOverrides = (userConfig) => {
|
|
65189
|
-
const overrides = userConfig?.ignore?.overrides;
|
|
65190
|
-
if (overrides === void 0) return [];
|
|
65191
|
-
if (!Array.isArray(overrides)) {
|
|
65192
|
-
warnConfigIssue(`ignore.overrides must be an array of { files, rules } entries; ignoring.`);
|
|
65193
|
-
return [];
|
|
65194
|
-
}
|
|
65195
|
-
return overrides.flatMap((entry, index) => {
|
|
65196
|
-
const validated = validateOverrideEntry(entry, index);
|
|
65197
|
-
if (!validated) return [];
|
|
65198
|
-
const filePatterns = compileGlobPatternsLenient(collectStringList(validated.files), (error) => warnConfigIssue(`ignore.overrides[${index}]: ${error.message}`));
|
|
65199
|
-
if (filePatterns.length === 0) return [];
|
|
65200
|
-
return [{
|
|
65201
|
-
filePatterns,
|
|
65202
|
-
ruleIds: new Set(collectStringList(validated.rules))
|
|
65203
|
-
}];
|
|
65204
|
-
});
|
|
65205
|
-
};
|
|
65206
|
-
const isDiagnosticIgnoredByOverrides = (diagnostic, rootDirectory, overrides) => {
|
|
65207
|
-
if (overrides.length === 0) return false;
|
|
65208
|
-
const relativeFilePath = toRelativePath(diagnostic.filePath, rootDirectory);
|
|
65209
|
-
const ruleIdentifier = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
65210
|
-
return overrides.some((override) => override.filePatterns.some((pattern) => pattern.test(relativeFilePath)) && (override.ruleIds.size === 0 || override.ruleIds.has(ruleIdentifier)));
|
|
65211
|
-
};
|
|
65212
|
-
const SEVERITY_FOR_OVERRIDE = {
|
|
65213
|
-
error: "error",
|
|
65214
|
-
warn: "warning"
|
|
65215
|
-
};
|
|
65216
|
-
const restampSeverity = (diagnostic, override) => {
|
|
65217
|
-
const targetSeverity = SEVERITY_FOR_OVERRIDE[override];
|
|
65218
|
-
if (diagnostic.severity === targetSeverity) return diagnostic;
|
|
65219
|
-
return {
|
|
65220
|
-
...diagnostic,
|
|
65221
|
-
severity: targetSeverity
|
|
65222
|
-
};
|
|
65223
|
-
};
|
|
65224
|
-
/**
|
|
65225
|
-
* Assembles the internal `RuleSeverityControls` shape from a user
|
|
65226
|
-
* config's top-level `rules` and `categories` fields — the
|
|
65227
|
-
* ESLint / oxlint-shaped severity surface.
|
|
65228
|
-
*
|
|
65229
|
-
* Returns `undefined` when neither field is present so the common
|
|
65230
|
-
* path (no severity config at all) stays allocation-free for
|
|
65231
|
-
* downstream consumers.
|
|
65232
|
-
*/
|
|
65233
|
-
const buildRuleSeverityControls = (config) => {
|
|
65234
|
-
if (!config) return void 0;
|
|
65235
|
-
if (config.rules === void 0 && config.categories === void 0 && config.buckets === void 0) return;
|
|
65236
|
-
return {
|
|
65237
|
-
...config.rules !== void 0 ? { rules: config.rules } : {},
|
|
65238
|
-
...config.categories !== void 0 ? { categories: config.categories } : {},
|
|
65239
|
-
...config.buckets !== void 0 ? { buckets: config.buckets } : {}
|
|
65240
|
-
};
|
|
65241
|
-
};
|
|
65242
|
-
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
65243
|
-
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
65244
|
-
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
65245
65340
|
const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
65246
65341
|
"effect/no-adjust-state-on-prop-change": "react-doctor/no-adjust-state-on-prop-change",
|
|
65247
65342
|
"effect/no-chain-state-updates": "react-doctor/no-chain-state-updates",
|
|
@@ -65380,6 +65475,139 @@ const getEquivalentRuleKeys = (ruleKey) => {
|
|
|
65380
65475
|
const nativeRuleKey = canonicalizeRuleKey(ruleKey);
|
|
65381
65476
|
return [nativeRuleKey, ...getLegacyRuleKeysForNative(nativeRuleKey)];
|
|
65382
65477
|
};
|
|
65478
|
+
const messageFromUnknown = (error) => error instanceof Error ? error.message : String(error);
|
|
65479
|
+
var InvalidGlobPatternError = class extends Error {
|
|
65480
|
+
pattern;
|
|
65481
|
+
reason;
|
|
65482
|
+
constructor(pattern, reason) {
|
|
65483
|
+
super(`Invalid glob pattern ${JSON.stringify(pattern)}: ${reason}`);
|
|
65484
|
+
this.name = "InvalidGlobPatternError";
|
|
65485
|
+
this.pattern = pattern;
|
|
65486
|
+
this.reason = reason;
|
|
65487
|
+
}
|
|
65488
|
+
};
|
|
65489
|
+
const assertGlobPattern = (condition, pattern, reason) => {
|
|
65490
|
+
if (!condition) throw new InvalidGlobPatternError(pattern, reason);
|
|
65491
|
+
};
|
|
65492
|
+
const countGlobWildcards = (pattern) => (pattern.match(/[*?]/g) ?? []).length;
|
|
65493
|
+
const normalizeGlobPattern = (pattern) => pattern.replace(/\\/g, "/").replace(/^\//, "");
|
|
65494
|
+
const PICOMATCH_OPTIONS = {
|
|
65495
|
+
dot: true,
|
|
65496
|
+
strictSlashes: false,
|
|
65497
|
+
windows: false
|
|
65498
|
+
};
|
|
65499
|
+
const compileGlobPattern = (rawPattern) => {
|
|
65500
|
+
assertGlobPattern(typeof rawPattern === "string" && rawPattern.length > 0, String(rawPattern), "pattern must be a non-empty string.");
|
|
65501
|
+
assertGlobPattern(rawPattern.length <= MAX_GLOB_PATTERN_LENGTH_CHARS, rawPattern, `pattern length ${rawPattern.length} exceeds the maximum of ${MAX_GLOB_PATTERN_LENGTH_CHARS} characters.`);
|
|
65502
|
+
const wildcardCount = countGlobWildcards(rawPattern);
|
|
65503
|
+
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.`);
|
|
65504
|
+
try {
|
|
65505
|
+
return import_picomatch.default.makeRe(normalizeGlobPattern(rawPattern), PICOMATCH_OPTIONS);
|
|
65506
|
+
} catch (caughtError) {
|
|
65507
|
+
throw new InvalidGlobPatternError(rawPattern, messageFromUnknown(caughtError));
|
|
65508
|
+
}
|
|
65509
|
+
};
|
|
65510
|
+
const compileGlobPatternsLenient = (patterns, onInvalid) => {
|
|
65511
|
+
const compiled = [];
|
|
65512
|
+
for (const pattern of patterns) try {
|
|
65513
|
+
compiled.push(compileGlobPattern(pattern));
|
|
65514
|
+
} catch (caughtError) {
|
|
65515
|
+
if (!(caughtError instanceof InvalidGlobPatternError)) throw caughtError;
|
|
65516
|
+
onInvalid(caughtError);
|
|
65517
|
+
}
|
|
65518
|
+
return compiled;
|
|
65519
|
+
};
|
|
65520
|
+
const toRelativePath = (filePath, rootDirectory) => {
|
|
65521
|
+
const normalizedFilePath = filePath.replace(/\\/g, "/");
|
|
65522
|
+
const normalizedRoot = rootDirectory.replace(/\\/g, "/").replace(/\/$/, "") + "/";
|
|
65523
|
+
if (normalizedFilePath.startsWith(normalizedRoot)) return normalizedFilePath.slice(normalizedRoot.length);
|
|
65524
|
+
return normalizedFilePath.replace(/^\.\//, "");
|
|
65525
|
+
};
|
|
65526
|
+
const warnConfigIssue = (message) => {
|
|
65527
|
+
process.stderr.write(`[react-doctor] ${message}\n`);
|
|
65528
|
+
};
|
|
65529
|
+
const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
65530
|
+
const collectStringList = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
65531
|
+
const hasMatchingRuleOverride = (ruleIds, ruleIdentifier) => {
|
|
65532
|
+
if (ruleIds.size === 0) return true;
|
|
65533
|
+
for (const ruleId of ruleIds) if (isSameRuleKey(ruleId, ruleIdentifier)) return true;
|
|
65534
|
+
return false;
|
|
65535
|
+
};
|
|
65536
|
+
const validateOverrideEntry = (entry, index) => {
|
|
65537
|
+
if (!isPlainObject(entry)) {
|
|
65538
|
+
warnConfigIssue(`ignore.overrides[${index}] must be an object with { files, rules }; ignoring this entry.`);
|
|
65539
|
+
return null;
|
|
65540
|
+
}
|
|
65541
|
+
if (!isStringArray(entry.files)) {
|
|
65542
|
+
warnConfigIssue(`ignore.overrides[${index}].files must be an array of strings; ignoring this entry.`);
|
|
65543
|
+
return null;
|
|
65544
|
+
}
|
|
65545
|
+
if (entry.rules !== void 0 && !isStringArray(entry.rules)) {
|
|
65546
|
+
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).`);
|
|
65547
|
+
return { files: entry.files };
|
|
65548
|
+
}
|
|
65549
|
+
return entry.rules === void 0 ? { files: entry.files } : {
|
|
65550
|
+
files: entry.files,
|
|
65551
|
+
rules: entry.rules
|
|
65552
|
+
};
|
|
65553
|
+
};
|
|
65554
|
+
const compileIgnoreOverrides = (userConfig) => {
|
|
65555
|
+
const overrides = userConfig?.ignore?.overrides;
|
|
65556
|
+
if (overrides === void 0) return [];
|
|
65557
|
+
if (!Array.isArray(overrides)) {
|
|
65558
|
+
warnConfigIssue(`ignore.overrides must be an array of { files, rules } entries; ignoring.`);
|
|
65559
|
+
return [];
|
|
65560
|
+
}
|
|
65561
|
+
return overrides.flatMap((entry, index) => {
|
|
65562
|
+
const validated = validateOverrideEntry(entry, index);
|
|
65563
|
+
if (!validated) return [];
|
|
65564
|
+
const filePatterns = compileGlobPatternsLenient(collectStringList(validated.files), (error) => warnConfigIssue(`ignore.overrides[${index}]: ${error.message}`));
|
|
65565
|
+
if (filePatterns.length === 0) return [];
|
|
65566
|
+
return [{
|
|
65567
|
+
filePatterns,
|
|
65568
|
+
ruleIds: new Set(collectStringList(validated.rules))
|
|
65569
|
+
}];
|
|
65570
|
+
});
|
|
65571
|
+
};
|
|
65572
|
+
const isDiagnosticIgnoredByOverrides = (diagnostic, rootDirectory, overrides) => {
|
|
65573
|
+
if (overrides.length === 0) return false;
|
|
65574
|
+
const relativeFilePath = toRelativePath(diagnostic.filePath, rootDirectory);
|
|
65575
|
+
const ruleIdentifier = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
65576
|
+
return overrides.some((override) => override.filePatterns.some((pattern) => pattern.test(relativeFilePath)) && hasMatchingRuleOverride(override.ruleIds, ruleIdentifier));
|
|
65577
|
+
};
|
|
65578
|
+
const SEVERITY_FOR_OVERRIDE = {
|
|
65579
|
+
error: "error",
|
|
65580
|
+
warn: "warning"
|
|
65581
|
+
};
|
|
65582
|
+
const restampSeverity = (diagnostic, override) => {
|
|
65583
|
+
const targetSeverity = SEVERITY_FOR_OVERRIDE[override];
|
|
65584
|
+
if (diagnostic.severity === targetSeverity) return diagnostic;
|
|
65585
|
+
return {
|
|
65586
|
+
...diagnostic,
|
|
65587
|
+
severity: targetSeverity
|
|
65588
|
+
};
|
|
65589
|
+
};
|
|
65590
|
+
/**
|
|
65591
|
+
* Assembles the internal `RuleSeverityControls` shape from a user
|
|
65592
|
+
* config's top-level `rules` and `categories` fields — the
|
|
65593
|
+
* ESLint / oxlint-shaped severity surface.
|
|
65594
|
+
*
|
|
65595
|
+
* Returns `undefined` when neither field is present so the common
|
|
65596
|
+
* path (no severity config at all) stays allocation-free for
|
|
65597
|
+
* downstream consumers.
|
|
65598
|
+
*/
|
|
65599
|
+
const buildRuleSeverityControls = (config) => {
|
|
65600
|
+
if (!config) return void 0;
|
|
65601
|
+
if (config.rules === void 0 && config.categories === void 0 && config.buckets === void 0) return;
|
|
65602
|
+
return {
|
|
65603
|
+
...config.rules !== void 0 ? { rules: config.rules } : {},
|
|
65604
|
+
...config.categories !== void 0 ? { categories: config.categories } : {},
|
|
65605
|
+
...config.buckets !== void 0 ? { buckets: config.buckets } : {}
|
|
65606
|
+
};
|
|
65607
|
+
};
|
|
65608
|
+
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
65609
|
+
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
65610
|
+
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
65383
65611
|
const stripDescriptionTail = (ruleList) => {
|
|
65384
65612
|
const descriptionMatch = ruleList.match(/(?:^|\s)--\s/);
|
|
65385
65613
|
if (!descriptionMatch || descriptionMatch.index === void 0) return ruleList;
|
|
@@ -66030,12 +66258,8 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
66030
66258
|
if (rule.tags.includes("migration-hint")) return false;
|
|
66031
66259
|
return getFileContext(diagnostic.filePath) !== "production";
|
|
66032
66260
|
};
|
|
66033
|
-
const
|
|
66034
|
-
for (const
|
|
66035
|
-
return false;
|
|
66036
|
-
};
|
|
66037
|
-
const isAppOnlyRule = (ruleIdentifier) => {
|
|
66038
|
-
for (const appOnlyRuleKey of APP_ONLY_RULE_KEYS) if (isSameRuleKey(appOnlyRuleKey, ruleIdentifier)) return true;
|
|
66261
|
+
const matchesRuleKey = (ruleIdentifier, candidates) => {
|
|
66262
|
+
for (const candidate of candidates) if (isSameRuleKey(candidate, ruleIdentifier)) return true;
|
|
66039
66263
|
return false;
|
|
66040
66264
|
};
|
|
66041
66265
|
const isRnRawTextSuppressedByConfig = (diagnostic) => {
|
|
@@ -66077,12 +66301,13 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
66077
66301
|
if (explicitSeverityOverride === "off") return suppress(current, "config");
|
|
66078
66302
|
if (explicitSeverityOverride !== void 0) current = restampSeverity(current, explicitSeverityOverride);
|
|
66079
66303
|
}
|
|
66304
|
+
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
66080
66305
|
if (explicitRuleOverride === void 0) {
|
|
66081
|
-
if (
|
|
66306
|
+
if (matchesRuleKey(ruleIdentifier, APP_ONLY_RULE_KEYS) && isLibraryFile(current.filePath)) return null;
|
|
66082
66307
|
}
|
|
66083
66308
|
if (!showWarnings && current.severity === "warning" && explicitSeverityOverride !== "warn") return null;
|
|
66084
66309
|
if (userConfig) {
|
|
66085
|
-
if (
|
|
66310
|
+
if (matchesRuleKey(ruleIdentifier, ignoredRules)) return suppress(current, "config");
|
|
66086
66311
|
if (isFileIgnoredByPatterns(current.filePath, rootDirectory, ignoredFilePatterns)) return null;
|
|
66087
66312
|
if (isDiagnosticIgnoredByOverrides(current, rootDirectory, compiledOverrides)) return suppress(current, "override");
|
|
66088
66313
|
if (isRnRawTextSuppressedByConfig(current)) return null;
|
|
@@ -66091,7 +66316,6 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
66091
66316
|
if (respectInlineDisables && current.line > 0) {
|
|
66092
66317
|
const lines = getFileLines(current.filePath);
|
|
66093
66318
|
if (lines) {
|
|
66094
|
-
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
66095
66319
|
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
66096
66320
|
if (evaluation.isSuppressed) return suppress(current, evaluation.isForeignDirective ? "foreign-inline" : "inline");
|
|
66097
66321
|
if (evaluation.nearMissHint) current = {
|
|
@@ -66367,7 +66591,8 @@ const readSystemFacts$1 = () => ({
|
|
|
66367
66591
|
/**
|
|
66368
66592
|
* Auto lint-worker count: the smaller of the (cgroup-CPU-aware) core count and
|
|
66369
66593
|
* the number of `PER_WORKER_MEM_BUDGET_BYTES` workers that fit in available
|
|
66370
|
-
* memory,
|
|
66594
|
+
* memory, and the measured automatic parallel-efficiency ceiling, then clamped
|
|
66595
|
+
* to `[MIN, HARD_MAX]` by `resolveScanConcurrency`.
|
|
66371
66596
|
*
|
|
66372
66597
|
* `os.availableParallelism()` already respects cgroup CPU quotas, so the core
|
|
66373
66598
|
* term needs no help. Available memory is `os.totalmem()` floored by the cgroup
|
|
@@ -66383,7 +66608,7 @@ const readSystemFacts$1 = () => ({
|
|
|
66383
66608
|
const resolveAutoScanConcurrency = (facts = readSystemFacts$1()) => {
|
|
66384
66609
|
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
66385
66610
|
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / PER_WORKER_MEM_BUDGET_BYTES);
|
|
66386
|
-
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
66611
|
+
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers, 10));
|
|
66387
66612
|
};
|
|
66388
66613
|
const resolveLintBatchOrdering = () => {
|
|
66389
66614
|
return process.env["REACT_DOCTOR_LINT_BATCH_ORDERING"]?.trim().toLowerCase() === "arrival" ? "arrival" : "cost";
|
|
@@ -66396,24 +66621,22 @@ const resolveLintBatchOrdering = () => {
|
|
|
66396
66621
|
*/
|
|
66397
66622
|
var OxlintSpawnTimeoutMs = class extends Reference("react-doctor/OxlintSpawnTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_OXLINT_SPAWN_TIMEOUT_MS", OXLINT_SPAWN_TIMEOUT_MS) }) {};
|
|
66398
66623
|
/**
|
|
66399
|
-
* Effect-side cap on the lint phase.
|
|
66400
|
-
*
|
|
66624
|
+
* Optional Effect-side cap on the lint phase. Unbounded unless explicitly
|
|
66625
|
+
* configured through the env var or a test layer.
|
|
66401
66626
|
* Tests override via `Layer.succeed(LintPhaseTimeoutMs, ...)`.
|
|
66402
66627
|
*/
|
|
66403
|
-
var LintPhaseTimeoutMs = class extends Reference("react-doctor/LintPhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_LINT_PHASE_TIMEOUT_MS",
|
|
66628
|
+
var LintPhaseTimeoutMs = class extends Reference("react-doctor/LintPhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_LINT_PHASE_TIMEOUT_MS", null) }) {};
|
|
66404
66629
|
/**
|
|
66405
|
-
* Effect-side cap on the dead-code phase
|
|
66406
|
-
*
|
|
66407
|
-
* type-heavy projects; tests override via
|
|
66630
|
+
* Optional Effect-side cap on the dead-code phase. Unbounded unless explicitly
|
|
66631
|
+
* configured through the env var or a test layer. Tests override via
|
|
66408
66632
|
* `Layer.succeed(DeadCodePhaseTimeoutMs, ...)`.
|
|
66409
66633
|
*/
|
|
66410
|
-
var DeadCodePhaseTimeoutMs = class extends Reference("react-doctor/DeadCodePhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_DEAD_CODE_PHASE_TIMEOUT_MS",
|
|
66634
|
+
var DeadCodePhaseTimeoutMs = class extends Reference("react-doctor/DeadCodePhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_DEAD_CODE_PHASE_TIMEOUT_MS", null) }) {};
|
|
66411
66635
|
/**
|
|
66412
|
-
*
|
|
66413
|
-
*
|
|
66414
|
-
* large repos; tests override via `Layer.succeed(ScanDeadlineMs, ...)`.
|
|
66636
|
+
* Optional overall scan deadline. Unbounded unless explicitly configured
|
|
66637
|
+
* through the env var or a test layer.
|
|
66415
66638
|
*/
|
|
66416
|
-
var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SCAN_DEADLINE_MS",
|
|
66639
|
+
var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SCAN_DEADLINE_MS", null) }) {};
|
|
66417
66640
|
/**
|
|
66418
66641
|
* Wall-clock budget for the supply-chain check when it runs on a background
|
|
66419
66642
|
* fiber overlapping the lint pass. Reads from the env var on startup so the
|
|
@@ -66486,14 +66709,14 @@ var DeadCodeOverlap = class extends Reference("react-doctor/DeadCodeOverlap", {
|
|
|
66486
66709
|
/**
|
|
66487
66710
|
* How the full-scan lint pass plans its file batches. `"cost"` (the default)
|
|
66488
66711
|
* builds size-balanced LPT batches (`planLintBatches`): the same mandatory
|
|
66489
|
-
* batch count as greedy chunking
|
|
66490
|
-
*
|
|
66712
|
+
* batch count as greedy chunking, but every batch gets an even share of files
|
|
66713
|
+
* AND bytes, so no full chunk is a straggler while
|
|
66491
66714
|
* the remainder-batch worker idles — and the heavy files are SPREAD across
|
|
66492
66715
|
* batches, the precondition the old sort-desc-then-chunk-100 `cost` mode
|
|
66493
66716
|
* lacked (it packed the heaviest files into one wave-1 straggler batch,
|
|
66494
66717
|
* measurably regressing size-skewed repos, which is why it never earned the
|
|
66495
66718
|
* default). `"arrival"` (`REACT_DOCTOR_LINT_BATCH_ORDERING=arrival`) is the
|
|
66496
|
-
* rollback hatch to plain greedy
|
|
66719
|
+
* rollback hatch to plain greedy fixed-size chunking in discovery order. Tests
|
|
66497
66720
|
* override via `Layer.succeed(LintBatchOrdering, ...)`. Diff / staged scans
|
|
66498
66721
|
* never reach this — they pass user-scoped `includePaths` that skip discovery
|
|
66499
66722
|
* and stay in arrival order; only the full-scan branch reads it.
|
|
@@ -66588,6 +66811,7 @@ const DEFAULT_SURFACE_EXCLUDED_TAGS = {
|
|
|
66588
66811
|
score: ["design"],
|
|
66589
66812
|
ciFailure: ["design"]
|
|
66590
66813
|
};
|
|
66814
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
66591
66815
|
const VALID_RULE_SEVERITIES = [
|
|
66592
66816
|
"error",
|
|
66593
66817
|
"warn",
|
|
@@ -66630,7 +66854,6 @@ const SURFACE_CONTROL_FIELD_NAMES = [
|
|
|
66630
66854
|
];
|
|
66631
66855
|
const isIncludedFileContext = (value) => VALID_INCLUDED_FILE_CONTEXTS.some((fileContext) => fileContext === value);
|
|
66632
66856
|
const SEVERITY_FIELD_NAMES = ["rules", "categories"];
|
|
66633
|
-
const isPlainObject$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
66634
66857
|
const formatType = (value) => typeof value === "string" ? `"${value}"` : typeof value;
|
|
66635
66858
|
const isRuleSeverity = (value) => typeof value === "string" && VALID_RULE_SEVERITIES.includes(value);
|
|
66636
66859
|
const coerceMaybeBooleanString = (fieldName, value) => {
|
|
@@ -66658,7 +66881,7 @@ const validateStringArrayField = (fieldName, value) => {
|
|
|
66658
66881
|
});
|
|
66659
66882
|
};
|
|
66660
66883
|
const validateSurfaceControls = (surface, rawControls) => {
|
|
66661
|
-
if (!
|
|
66884
|
+
if (!isRecord(rawControls)) {
|
|
66662
66885
|
warnConfigIssue(`config field "surfaces.${surface}" must be an object (got ${typeof rawControls}); ignoring this surface.`);
|
|
66663
66886
|
return;
|
|
66664
66887
|
}
|
|
@@ -66682,7 +66905,7 @@ const validateSurfaceControls = (surface, rawControls) => {
|
|
|
66682
66905
|
return validatedSurfaceControls;
|
|
66683
66906
|
};
|
|
66684
66907
|
const validateSurfacesField = (rawSurfaces) => {
|
|
66685
|
-
if (!
|
|
66908
|
+
if (!isRecord(rawSurfaces)) {
|
|
66686
66909
|
warnConfigIssue(`config field "surfaces" must be an object (got ${typeof rawSurfaces}); ignoring this field.`);
|
|
66687
66910
|
return;
|
|
66688
66911
|
}
|
|
@@ -66698,7 +66921,7 @@ const validateSurfacesField = (rawSurfaces) => {
|
|
|
66698
66921
|
return validated;
|
|
66699
66922
|
};
|
|
66700
66923
|
const validateSeverityMap = (fieldName, rawMap, keysAreCategories = false) => {
|
|
66701
|
-
if (!
|
|
66924
|
+
if (!isRecord(rawMap)) {
|
|
66702
66925
|
warnConfigIssue(`config field "${fieldName}" must be an object (got ${typeof rawMap}); ignoring this field.`);
|
|
66703
66926
|
return;
|
|
66704
66927
|
}
|
|
@@ -67031,6 +67254,10 @@ const dedupeRelatedDiagnostics = (diagnostics) => {
|
|
|
67031
67254
|
}
|
|
67032
67255
|
return uniqueDiagnostics;
|
|
67033
67256
|
};
|
|
67257
|
+
const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
|
|
67258
|
+
const relativePath = Path.relative(parentAbsolutePath, childAbsolutePath);
|
|
67259
|
+
return Boolean(relativePath) && !relativePath.startsWith("..") && !Path.isAbsolute(relativePath);
|
|
67260
|
+
};
|
|
67034
67261
|
const compareStrings = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
67035
67262
|
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
67263
|
const getDirectDependencyNames = (packageJson) => new Set([...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.devDependencies ?? {})]);
|
|
@@ -68607,7 +68834,7 @@ const readScannedFile = (candidate) => {
|
|
|
68607
68834
|
return null;
|
|
68608
68835
|
}
|
|
68609
68836
|
};
|
|
68610
|
-
function* collectSecurityScanFiles(rootDirectory) {
|
|
68837
|
+
function* collectSecurityScanFiles(rootDirectory, excludedDirectories = /* @__PURE__ */ new Set()) {
|
|
68611
68838
|
const priorityCandidates = [];
|
|
68612
68839
|
const artifactCandidates = [];
|
|
68613
68840
|
const otherCandidates = [];
|
|
@@ -68622,7 +68849,7 @@ function* collectSecurityScanFiles(rootDirectory) {
|
|
|
68622
68849
|
for (const entry of readDirectoryEntries(current.absolutePath)) {
|
|
68623
68850
|
const absolutePath = Path.join(current.absolutePath, entry.name);
|
|
68624
68851
|
if (entry.isDirectory()) {
|
|
68625
|
-
if (!SKIPPED_DIRECTORY_NAMES.has(entry.name)) stack.push({
|
|
68852
|
+
if (!SKIPPED_DIRECTORY_NAMES.has(entry.name) && !excludedDirectories.has(Path.resolve(absolutePath))) stack.push({
|
|
68626
68853
|
absolutePath,
|
|
68627
68854
|
depth: current.depth + 1
|
|
68628
68855
|
});
|
|
@@ -68803,10 +69030,8 @@ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledWhe
|
|
|
68803
69030
|
}
|
|
68804
69031
|
return true;
|
|
68805
69032
|
};
|
|
69033
|
+
const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
|
|
68806
69034
|
const shouldEnableRuleByDefaultStatus = (options) => options.defaultEnabled !== false || options.hasExplicitOverride === true || options.includeTagDefaults && options.hasIncludedTags;
|
|
68807
|
-
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
68808
|
-
setImmediate(resolve);
|
|
68809
|
-
});
|
|
68810
69035
|
const createSecurityScanSession = (rootDirectory, options) => {
|
|
68811
69036
|
const capabilities = options.project ? getCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
68812
69037
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
@@ -68859,19 +69084,35 @@ const createSecurityScanSession = (rootDirectory, options) => {
|
|
|
68859
69084
|
};
|
|
68860
69085
|
};
|
|
68861
69086
|
const checkSecurityScanCooperative = async (rootDirectory, options = {}) => {
|
|
69087
|
+
const throwIfScanAborted = () => {
|
|
69088
|
+
options.signal?.throwIfAborted();
|
|
69089
|
+
};
|
|
69090
|
+
const didReachDeadline = () => {
|
|
69091
|
+
const hasDeadlineElapsed = options.deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(options.deadlineEpochMs) === 0;
|
|
69092
|
+
if (hasDeadlineElapsed) options.onDeadlineExceeded?.();
|
|
69093
|
+
return hasDeadlineElapsed;
|
|
69094
|
+
};
|
|
69095
|
+
throwIfScanAborted();
|
|
68862
69096
|
const session = createSecurityScanSession(rootDirectory, options);
|
|
68863
69097
|
if (session === null) return [];
|
|
69098
|
+
if (didReachDeadline()) return [];
|
|
68864
69099
|
let sliceStartedAt = performance.now();
|
|
68865
|
-
for (const file of collectSecurityScanFiles(rootDirectory)) {
|
|
69100
|
+
for (const file of collectSecurityScanFiles(rootDirectory, options.excludedDirectories)) {
|
|
69101
|
+
throwIfScanAborted();
|
|
69102
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
68866
69103
|
if (file === null) {
|
|
68867
69104
|
if (performance.now() - sliceStartedAt >= 12) {
|
|
68868
69105
|
await yieldToEventLoop();
|
|
69106
|
+
throwIfScanAborted();
|
|
69107
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
68869
69108
|
sliceStartedAt = performance.now();
|
|
68870
69109
|
}
|
|
68871
69110
|
continue;
|
|
68872
69111
|
}
|
|
68873
69112
|
for (const _ruleStep of session.scanFileByRule(file)) if (performance.now() - sliceStartedAt >= 12) {
|
|
68874
69113
|
await yieldToEventLoop();
|
|
69114
|
+
throwIfScanAborted();
|
|
69115
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
68875
69116
|
sliceStartedAt = performance.now();
|
|
68876
69117
|
}
|
|
68877
69118
|
}
|
|
@@ -69034,25 +69275,18 @@ const collectIgnorePatterns = (rootDirectory) => {
|
|
|
69034
69275
|
cachedPatternsByRoot.set(rootDirectory, patterns);
|
|
69035
69276
|
return patterns;
|
|
69036
69277
|
};
|
|
69037
|
-
const
|
|
69038
|
-
const KNIP_JSON_FILENAME = "knip.json";
|
|
69039
|
-
const readJsonFileSafe = (filePath) => {
|
|
69040
|
-
let rawContents;
|
|
69041
|
-
try {
|
|
69042
|
-
rawContents = fs.readFileSync(filePath, "utf-8");
|
|
69043
|
-
} catch {
|
|
69044
|
-
return null;
|
|
69045
|
-
}
|
|
69278
|
+
const failOpenReadJson = (filePath, fallback) => {
|
|
69046
69279
|
try {
|
|
69047
|
-
return JSON.parse(
|
|
69280
|
+
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
69048
69281
|
} catch {
|
|
69049
|
-
return
|
|
69282
|
+
return fallback;
|
|
69050
69283
|
}
|
|
69051
69284
|
};
|
|
69285
|
+
const KNIP_JSON_FILENAME = "knip.json";
|
|
69052
69286
|
const readKnipConfig = (rootDirectory) => {
|
|
69053
|
-
const knipJson =
|
|
69287
|
+
const knipJson = failOpenReadJson(path.join(rootDirectory, KNIP_JSON_FILENAME), null);
|
|
69054
69288
|
if (isRecord(knipJson)) return knipJson;
|
|
69055
|
-
const packageJson =
|
|
69289
|
+
const packageJson = failOpenReadJson(path.join(rootDirectory, "package.json"), null);
|
|
69056
69290
|
const packageKnipConfig = isRecord(packageJson) ? packageJson.knip : null;
|
|
69057
69291
|
return isRecord(packageKnipConfig) ? packageKnipConfig : null;
|
|
69058
69292
|
};
|
|
@@ -69100,13 +69334,6 @@ const atomicWriteJson = (filePath, value) => {
|
|
|
69100
69334
|
return;
|
|
69101
69335
|
}
|
|
69102
69336
|
};
|
|
69103
|
-
const failOpenReadJson = (filePath, fallback) => {
|
|
69104
|
-
try {
|
|
69105
|
-
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
69106
|
-
} catch {
|
|
69107
|
-
return fallback;
|
|
69108
|
-
}
|
|
69109
|
-
};
|
|
69110
69337
|
const hashFileContents = (filePath) => {
|
|
69111
69338
|
try {
|
|
69112
69339
|
return crypto.createHash("sha1").update(NFS.readFileSync(filePath)).digest("hex");
|
|
@@ -69785,7 +70012,6 @@ const isDiagnosticOnSurface = (diagnostic, surface, config) => {
|
|
|
69785
70012
|
return true;
|
|
69786
70013
|
};
|
|
69787
70014
|
const filterDiagnosticsForSurface = (diagnostics, surface, config) => diagnostics.filter((diagnostic) => isDiagnosticOnSurface(diagnostic, surface, config));
|
|
69788
|
-
const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
|
|
69789
70015
|
/**
|
|
69790
70016
|
* Budget for the dead-code phase, scaled to the work. deslop's graph build is
|
|
69791
70017
|
* CPU-bound and roughly linear in file count, so a fixed 120s cap is too tight
|
|
@@ -69793,25 +70019,28 @@ const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs
|
|
|
69793
70019
|
* tipped over by any concurrent load — silently dropping every dead-code
|
|
69794
70020
|
* finding. Scaling the budget with file count (and inversely with the core
|
|
69795
70021
|
* 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.
|
|
70022
|
+
* reclaims a genuinely wedged worker.
|
|
69798
70023
|
*/
|
|
69799
70024
|
const resolveDeadCodeTimeout = (input) => {
|
|
69800
70025
|
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
|
-
};
|
|
70026
|
+
return { workerTimeoutMs: Math.min(DEAD_CODE_TIMEOUT_CEILING_MS, Math.max(DEAD_CODE_WORKER_TIMEOUT_MS, Math.ceil(input.sourceFileCount * 30 * coreShareFactor))) };
|
|
69806
70027
|
};
|
|
69807
|
-
const resolveLintIncludePaths = (rootDirectory, userConfig) => {
|
|
70028
|
+
const resolveLintIncludePaths = (rootDirectory, userConfig, sourceFiles) => {
|
|
69808
70029
|
if (!Array.isArray(userConfig?.ignore?.files) || userConfig.ignore.files.length === 0) return;
|
|
69809
70030
|
const ignoredPatterns = compileIgnoredFilePatterns(userConfig);
|
|
69810
|
-
return listSourceFiles(rootDirectory).filter((filePath) => {
|
|
70031
|
+
return (sourceFiles ?? listSourceFiles(rootDirectory)).filter((filePath) => {
|
|
69811
70032
|
if (!isLintableSourceFile(filePath)) return false;
|
|
69812
70033
|
return !isFileIgnoredByPatterns(filePath, rootDirectory, ignoredPatterns);
|
|
69813
70034
|
});
|
|
69814
70035
|
};
|
|
70036
|
+
const filterPathsOutsideDirectories = (input) => {
|
|
70037
|
+
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}/`)));
|
|
70038
|
+
if (excludedPrefixes.length === 0) return [...input.relativePaths];
|
|
70039
|
+
return input.relativePaths.filter((relativePath) => {
|
|
70040
|
+
const normalizedRelativePath = relativePath.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
70041
|
+
return !excludedPrefixes.some((excludedPrefix) => normalizedRelativePath === excludedPrefix || normalizedRelativePath.startsWith(`${excludedPrefix}/`));
|
|
70042
|
+
});
|
|
70043
|
+
};
|
|
69815
70044
|
var Config = class Config extends Service()("react-doctor/Config") {
|
|
69816
70045
|
static layerNode = effect(Config, gen(function* () {
|
|
69817
70046
|
const cache = yield* make$3({
|
|
@@ -69873,6 +70102,7 @@ var Files = class Files extends Service()("react-doctor/Files") {
|
|
|
69873
70102
|
static layerNode = succeed$3(Files, Files.of({
|
|
69874
70103
|
readLines: (input) => sync(() => createNodeReadFileLinesSync(input.rootDirectory)(input.filePath)),
|
|
69875
70104
|
listSourceFiles: (rootDirectory) => sync(() => listSourceFiles(rootDirectory)),
|
|
70105
|
+
listSourceFilesCooperative: (input) => promise(() => listSourceFilesCooperative(input.rootDirectory, input.signal)),
|
|
69876
70106
|
isFile: (filePath) => sync(() => isFile(filePath)),
|
|
69877
70107
|
isDirectory: (filePath) => sync(() => isDirectory(filePath))
|
|
69878
70108
|
}));
|
|
@@ -69899,6 +70129,16 @@ var Files = class Files extends Service()("react-doctor/Files") {
|
|
|
69899
70129
|
}
|
|
69900
70130
|
return files;
|
|
69901
70131
|
}),
|
|
70132
|
+
listSourceFilesCooperative: (input) => sync(() => {
|
|
70133
|
+
const rootDirectory = input.rootDirectory;
|
|
70134
|
+
const prefix = rootDirectory.endsWith("/") ? rootDirectory : `${rootDirectory}/`;
|
|
70135
|
+
const files = [];
|
|
70136
|
+
for (const absolute of tree.keys()) {
|
|
70137
|
+
if (!absolute.startsWith(prefix)) continue;
|
|
70138
|
+
files.push(absolute.slice(prefix.length));
|
|
70139
|
+
}
|
|
70140
|
+
return files;
|
|
70141
|
+
}),
|
|
69902
70142
|
isFile: (filePath) => sync(() => tree.has(filePath)),
|
|
69903
70143
|
isDirectory: (filePath) => sync(() => {
|
|
69904
70144
|
const prefix = filePath.endsWith("/") ? filePath : `${filePath}/`;
|
|
@@ -70474,7 +70714,7 @@ const batchIncludePaths = (baseArgs, includePaths) => {
|
|
|
70474
70714
|
for (const filePath of includePaths) {
|
|
70475
70715
|
const entryLength = filePath.length + 1;
|
|
70476
70716
|
const exceedsArgLength = currentBatch.length > 0 && currentBatchLength + entryLength > 24e3;
|
|
70477
|
-
const exceedsFileCount = currentBatch.length >=
|
|
70717
|
+
const exceedsFileCount = currentBatch.length >= 200;
|
|
70478
70718
|
if (exceedsArgLength || exceedsFileCount) {
|
|
70479
70719
|
batches.push(currentBatch);
|
|
70480
70720
|
currentBatch = [];
|
|
@@ -70821,7 +71061,7 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
70821
71061
|
}
|
|
70822
71062
|
return enabled;
|
|
70823
71063
|
};
|
|
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 }) => {
|
|
71064
|
+
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
71065
|
const hasIncludedTags = includedTags.size > 0;
|
|
70826
71066
|
const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" || hasIncludedTags ? null : resolveReactHooksJsPlugin(project.hasReactCompiler || project.hasReactCompilerLintPlugin === true, customRulesOnly);
|
|
70827
71067
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
@@ -70885,7 +71125,8 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
70885
71125
|
}))
|
|
70886
71126
|
} : {},
|
|
70887
71127
|
...project.shopifyFlashListMajorVersion !== null ? { shopifyFlashListMajorVersion: project.shopifyFlashListMajorVersion } : {},
|
|
70888
|
-
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {}
|
|
71128
|
+
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {},
|
|
71129
|
+
...projectIndexModuleSources !== void 0 ? { projectIndexModuleSources: [...projectIndexModuleSources] } : {}
|
|
70889
71130
|
} },
|
|
70890
71131
|
rules: {
|
|
70891
71132
|
...reactCompilerRules,
|
|
@@ -71134,11 +71375,17 @@ const collectUnpluginAutoImportGlobalScopes = ({ rootDirectory, candidateFiles }
|
|
|
71134
71375
|
return scopes.some((scope) => scope.names.length > 0) ? scopes : [];
|
|
71135
71376
|
};
|
|
71136
71377
|
const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
|
|
71378
|
+
const decodeDiagnostic = (raw) => {
|
|
71379
|
+
const diagnostic = validateDiagnostic$1(raw);
|
|
71380
|
+
return {
|
|
71381
|
+
...diagnostic,
|
|
71382
|
+
relatedLocations: diagnostic.relatedLocations?.map((location) => ({ ...location }))
|
|
71383
|
+
};
|
|
71384
|
+
};
|
|
71137
71385
|
const decodeFileDiagnostics = (raw) => {
|
|
71138
71386
|
if (!Array.isArray(raw)) return null;
|
|
71139
71387
|
try {
|
|
71140
|
-
|
|
71141
|
-
return raw;
|
|
71388
|
+
return raw.map((entry) => decodeDiagnostic(entry));
|
|
71142
71389
|
} catch {
|
|
71143
71390
|
return null;
|
|
71144
71391
|
}
|
|
@@ -72370,6 +72617,12 @@ const buildProfiledNodeArguments = (input) => [
|
|
|
72370
72617
|
...input.heapProfileDirectory ? ["--heap-prof", `--heap-prof-dir=${input.heapProfileDirectory}`] : [],
|
|
72371
72618
|
...input.argumentsList
|
|
72372
72619
|
];
|
|
72620
|
+
const lowerChildProcessPriority = (processId) => {
|
|
72621
|
+
if (processId === void 0) return;
|
|
72622
|
+
try {
|
|
72623
|
+
OS.setPriority(processId, OS.constants.priority.PRIORITY_BELOW_NORMAL);
|
|
72624
|
+
} catch {}
|
|
72625
|
+
};
|
|
72373
72626
|
const SANITIZED_ENV = buildOxlintChildEnv(process.env);
|
|
72374
72627
|
/**
|
|
72375
72628
|
* Spawn one oxlint subprocess with hard ceilings on wall time and
|
|
@@ -72408,6 +72661,7 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
|
|
|
72408
72661
|
"pipe"
|
|
72409
72662
|
]
|
|
72410
72663
|
});
|
|
72664
|
+
lowerChildProcessPriority(child.pid);
|
|
72411
72665
|
const onAbort = () => {
|
|
72412
72666
|
child.kill("SIGKILL");
|
|
72413
72667
|
reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) }));
|
|
@@ -72530,7 +72784,7 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
72530
72784
|
* loop with a slimmer config in that case.
|
|
72531
72785
|
*/
|
|
72532
72786
|
const spawnLintBatches = async (input) => {
|
|
72533
|
-
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, sourceMapByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth =
|
|
72787
|
+
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, sourceMapByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 9, signal, deadlineEpochMs } = input;
|
|
72534
72788
|
const resolveSourcePath = (filePath) => {
|
|
72535
72789
|
const absoluteFilePath = Path.isAbsolute(filePath) ? filePath : Path.resolve(rootDirectory, filePath);
|
|
72536
72790
|
return sourcePathByLintPath?.get(Path.normalize(absoluteFilePath)) ?? filePath;
|
|
@@ -72538,6 +72792,12 @@ const spawnLintBatches = async (input) => {
|
|
|
72538
72792
|
const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
|
|
72539
72793
|
let rescueDeadlineEpochMs;
|
|
72540
72794
|
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0 || rescueDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(rescueDeadlineEpochMs) === 0;
|
|
72795
|
+
const resolveSpawnTimeoutMs = () => {
|
|
72796
|
+
const deadlineBudgets = [deadlineEpochMs, rescueDeadlineEpochMs].filter((deadline) => deadline !== void 0).map(remainingDeadlineBudgetMs);
|
|
72797
|
+
if (deadlineBudgets.length === 0) return spawnTimeoutMs;
|
|
72798
|
+
const remainingDeadlineMs = Math.min(...deadlineBudgets);
|
|
72799
|
+
return spawnTimeoutMs === void 0 ? remainingDeadlineMs : Math.min(spawnTimeoutMs, remainingDeadlineMs);
|
|
72800
|
+
};
|
|
72541
72801
|
const runBatchPass = async (concurrency, passBatches, passOnFileProgress) => {
|
|
72542
72802
|
const totalFileCount = passBatches.reduce((sum, batch) => sum + batch.length, 0);
|
|
72543
72803
|
const allDiagnostics = [];
|
|
@@ -72555,12 +72815,13 @@ const spawnLintBatches = async (input) => {
|
|
|
72555
72815
|
const batchArgs = [...baseArgs, ...batch];
|
|
72556
72816
|
try {
|
|
72557
72817
|
const spawnBatch = () => {
|
|
72558
|
-
|
|
72818
|
+
const effectiveSpawnTimeoutMs = resolveSpawnTimeoutMs();
|
|
72819
|
+
if (effectiveSpawnTimeoutMs === 0) {
|
|
72559
72820
|
deadlineSkippedFiles.push(...batch);
|
|
72560
72821
|
batchState.deadlineSkippedFileCount += batch.length;
|
|
72561
72822
|
return Promise.resolve(null);
|
|
72562
72823
|
}
|
|
72563
|
-
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath,
|
|
72824
|
+
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, effectiveSpawnTimeoutMs, outputMaxBytes, signal, () => {
|
|
72564
72825
|
if (batchState.didStart) return;
|
|
72565
72826
|
batchState.didStart = true;
|
|
72566
72827
|
startedFileCount += batchState.initialFileCount;
|
|
@@ -72715,11 +72976,36 @@ const validateRuleRegistration = () => {
|
|
|
72715
72976
|
].filter((entry) => entry !== null).join("; ");
|
|
72716
72977
|
console.warn(`[react-doctor] rule-registration drift: ${detail}`);
|
|
72717
72978
|
};
|
|
72979
|
+
const PROJECT_INDEX_MODULE_SOURCES = [
|
|
72980
|
+
"next/og",
|
|
72981
|
+
"@vercel/og",
|
|
72982
|
+
"satori",
|
|
72983
|
+
"remotion"
|
|
72984
|
+
];
|
|
72985
|
+
const collectProjectIndexModuleSources = async (rootDirectory, candidateFiles) => {
|
|
72986
|
+
const foundModuleSources = /* @__PURE__ */ new Set();
|
|
72987
|
+
let sliceStartedAt = performance.now();
|
|
72988
|
+
for (const candidateFile of candidateFiles) {
|
|
72989
|
+
let sourceBuffer;
|
|
72990
|
+
try {
|
|
72991
|
+
sourceBuffer = NFS.readFileSync(Path.isAbsolute(candidateFile) ? candidateFile : Path.resolve(rootDirectory, candidateFile));
|
|
72992
|
+
} catch {
|
|
72993
|
+
continue;
|
|
72994
|
+
}
|
|
72995
|
+
for (const moduleSource of PROJECT_INDEX_MODULE_SOURCES) if (sourceBuffer.includes(`"${moduleSource}"`) || sourceBuffer.includes(`'${moduleSource}'`)) foundModuleSources.add(moduleSource);
|
|
72996
|
+
if (foundModuleSources.size === PROJECT_INDEX_MODULE_SOURCES.length) break;
|
|
72997
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
72998
|
+
await yieldToEventLoop();
|
|
72999
|
+
sliceStartedAt = performance.now();
|
|
73000
|
+
}
|
|
73001
|
+
}
|
|
73002
|
+
return [...foundModuleSources].sort();
|
|
73003
|
+
};
|
|
72718
73004
|
/**
|
|
72719
73005
|
* Balanced LPT (longest-processing-time-first) lint batch planner. Where
|
|
72720
|
-
* `batchIncludePaths` greedily fills
|
|
72721
|
-
* remainder batch
|
|
72722
|
-
*
|
|
73006
|
+
* `batchIncludePaths` greedily fills fixed-size batches and leaves a small
|
|
73007
|
+
* remainder batch, so the parallel pool's wall clock is set by whichever
|
|
73008
|
+
* full chunk happened to collect the most work while
|
|
72723
73009
|
* the remainder's worker idles), this planner keeps the SAME mandatory batch
|
|
72724
73010
|
* count — `ceil(files / OXLINT_MAX_FILES_PER_BATCH)`, the JS-plugin
|
|
72725
73011
|
* perf-cliff / native-binding SIGABRT guard — and assigns files largest-first,
|
|
@@ -72748,7 +73034,7 @@ const validateRuleRegistration = () => {
|
|
|
72748
73034
|
*/
|
|
72749
73035
|
const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
72750
73036
|
if (files.length === 0) return [];
|
|
72751
|
-
const batchCount = Math.ceil(files.length /
|
|
73037
|
+
const batchCount = Math.ceil(files.length / 200);
|
|
72752
73038
|
const maxFilesPerBatch = Math.ceil(files.length / batchCount);
|
|
72753
73039
|
const baseArgsLengthChars = estimateArgsLength(baseArgs);
|
|
72754
73040
|
const descendingSizeIndexes = [...files.keys()].sort((leftIndex, rightIndex) => (sizeByFile.get(files[rightIndex]) ?? 0) - (sizeByFile.get(files[leftIndex]) ?? 0));
|
|
@@ -73277,6 +73563,7 @@ const runOxlint = async (options) => {
|
|
|
73277
73563
|
await yieldToEventLoop();
|
|
73278
73564
|
const sizedScanFiles = includePaths === void 0 ? listSourceFilesWithSize(rootDirectory) : null;
|
|
73279
73565
|
const candidateFiles = includePaths !== void 0 ? includePaths : (sizedScanFiles ?? []).map((entry) => entry.path);
|
|
73566
|
+
const projectIndexModuleSources = includePaths === void 0 && options.deadlineEpochMs === void 0 ? await collectProjectIndexModuleSources(rootDirectory, candidateFiles) : void 0;
|
|
73280
73567
|
unpluginAutoImportGlobalScopes = collectUnpluginAutoImportGlobalScopes({
|
|
73281
73568
|
rootDirectory,
|
|
73282
73569
|
candidateFiles
|
|
@@ -73299,6 +73586,7 @@ const runOxlint = async (options) => {
|
|
|
73299
73586
|
runtimeGlobals,
|
|
73300
73587
|
unpluginAutoImportGlobalScopes,
|
|
73301
73588
|
serverAuthFunctionNames,
|
|
73589
|
+
projectIndexModuleSources,
|
|
73302
73590
|
severityControls,
|
|
73303
73591
|
userPlugins,
|
|
73304
73592
|
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
|
|
@@ -73766,10 +74054,10 @@ const translateProjectInfoError = (cause, directory) => {
|
|
|
73766
74054
|
}) });
|
|
73767
74055
|
};
|
|
73768
74056
|
var Project = class Project extends Service()("react-doctor/Project") {
|
|
73769
|
-
static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (
|
|
74057
|
+
static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (input) {
|
|
73770
74058
|
return yield* try_({
|
|
73771
|
-
try: () => discoverProject(directory),
|
|
73772
|
-
catch: (cause) => translateProjectInfoError(cause, directory)
|
|
74059
|
+
try: () => discoverProject(input.directory, { sourceFileCount: input.sourceFileCount }),
|
|
74060
|
+
catch: (cause) => translateProjectInfoError(cause, input.directory)
|
|
73773
74061
|
});
|
|
73774
74062
|
}) }));
|
|
73775
74063
|
static layerOf = (projectInfo) => succeed$3(Project, Project.of({ discover: () => succeed$2(projectInfo) }));
|
|
@@ -74354,9 +74642,10 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
74354
74642
|
* outage.
|
|
74355
74643
|
*/
|
|
74356
74644
|
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
|
-
|
|
74645
|
+
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (configuredTimeoutMs) => checkSupplyChain({
|
|
74646
|
+
rootDirectory: input.rootDirectory,
|
|
74647
|
+
userConfig: input.userConfig,
|
|
74648
|
+
totalTimeoutMs: input.timeoutMs ?? configuredTimeoutMs
|
|
74360
74649
|
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), withSpan("SupplyChain.run"))) }));
|
|
74361
74650
|
static layerOf = (diagnostics) => succeed$3(SupplyChain, SupplyChain.of({ run: () => fromIterable$1(diagnostics) }));
|
|
74362
74651
|
};
|
|
@@ -74391,10 +74680,6 @@ const NO_HOOKS = {
|
|
|
74391
74680
|
beforeLint: () => void_,
|
|
74392
74681
|
afterLint: () => void_
|
|
74393
74682
|
};
|
|
74394
|
-
const filterMapNullable = (transform) => fromPredicateOption((value) => {
|
|
74395
|
-
const result = transform(value);
|
|
74396
|
-
return result === null ? none() : some(result);
|
|
74397
|
-
});
|
|
74398
74683
|
const fileReader = (filesService, rootDirectory) => (filePath) => {
|
|
74399
74684
|
const lines = runSync(filesService.readLines({
|
|
74400
74685
|
filePath,
|
|
@@ -74418,7 +74703,7 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
74418
74703
|
* The GitHub viewer-permission lookup is forked onto a background
|
|
74419
74704
|
* fiber here and joined late (it feeds score metadata, not
|
|
74420
74705
|
* diagnostics).
|
|
74421
|
-
* 2. beforeLint hook (e.g. CLI
|
|
74706
|
+
* 2. beforeLint hook (e.g. CLI records project telemetry)
|
|
74422
74707
|
* 3. environment checks (reduced-motion + pnpm hardening +
|
|
74423
74708
|
* expo/react-native), collected synchronously. The heavier
|
|
74424
74709
|
* content-regex security scan is forked instead (like supply-chain
|
|
@@ -74431,13 +74716,11 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
74431
74716
|
* `SupplyChainOverlapTimeoutMs` (measured from fork) so a hung
|
|
74432
74717
|
* socket can't drag out its join; on timeout it fails open to no
|
|
74433
74718
|
* diagnostics — the same outcome class as a Socket outage.
|
|
74434
|
-
* 5. Linter.run runs; DeadCode.run
|
|
74435
|
-
*
|
|
74436
|
-
*
|
|
74437
|
-
*
|
|
74438
|
-
*
|
|
74439
|
-
* fiber is joined (or interrupted, SIGKILLing its worker, on lint
|
|
74440
|
-
* failure) before diagnostics are concatenated. The afterLint hook
|
|
74719
|
+
* 5. Linter.run runs; DeadCode.run forks only when
|
|
74720
|
+
* REACT_DOCTOR_DEAD_CODE_OVERLAP=on. The default "auto" and explicit
|
|
74721
|
+
* "off" paths stay sequential. The fiber is joined (or interrupted,
|
|
74722
|
+
* SIGKILLing its worker, on lint failure) before diagnostics are
|
|
74723
|
+
* concatenated. The afterLint hook
|
|
74441
74724
|
* fires between lint and dead-code. Progress spinner labels AND the
|
|
74442
74725
|
* final diagnostic / score order stay independent of execution
|
|
74443
74726
|
* order, so terminal output is identical either way; supply-chain
|
|
@@ -74472,8 +74755,28 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74472
74755
|
const partialFailuresRef = yield* LintPartialFailures;
|
|
74473
74756
|
const resolvedConfig = yield* configService.resolve(input.directory);
|
|
74474
74757
|
const scanDirectory = resolvedConfig.resolvedDirectory;
|
|
74475
|
-
const
|
|
74758
|
+
const excludedProjectDirectories = (input.excludedProjectDirectories ?? []).map((excludedDirectory) => Path.resolve(excludedDirectory)).filter((excludedDirectory) => isPathInsideDirectory(excludedDirectory, scanDirectory));
|
|
74759
|
+
const excludedProjectRelativePaths = excludedProjectDirectories.map((excludedDirectory) => Path.relative(scanDirectory, excludedDirectory).replaceAll("\\", "/"));
|
|
74760
|
+
const isExcludedProjectDiagnostic = (diagnostic) => {
|
|
74761
|
+
const relativeFilePath = toNormalizedRelativePath(diagnostic.filePath, scanDirectory);
|
|
74762
|
+
return excludedProjectRelativePaths.some((excludedRelativePath) => relativeFilePath === excludedRelativePath || relativeFilePath.startsWith(`${excludedRelativePath}/`));
|
|
74763
|
+
};
|
|
74764
|
+
const precomputedSourceFiles = input.suppressScanSummary === true || excludedProjectDirectories.length > 0 ? yield* filesService.listSourceFilesCooperative({
|
|
74765
|
+
rootDirectory: scanDirectory,
|
|
74766
|
+
signal: input.signal
|
|
74767
|
+
}) : null;
|
|
74768
|
+
const includedPrecomputedSourceFiles = precomputedSourceFiles !== null && excludedProjectDirectories.length > 0 ? filterPathsOutsideDirectories({
|
|
74769
|
+
rootDirectory: scanDirectory,
|
|
74770
|
+
relativePaths: precomputedSourceFiles,
|
|
74771
|
+
excludedDirectories: excludedProjectDirectories
|
|
74772
|
+
}) : precomputedSourceFiles;
|
|
74773
|
+
const sourceFileCount = excludedProjectDirectories.length > 0 ? includedPrecomputedSourceFiles?.length : input.precomputedSourceFileCount ?? includedPrecomputedSourceFiles?.length;
|
|
74774
|
+
const project = yield* projectService.discover({
|
|
74775
|
+
directory: scanDirectory,
|
|
74776
|
+
sourceFileCount
|
|
74777
|
+
});
|
|
74476
74778
|
if (!isAnalyzableProject(project)) return yield* new ReactDoctorError({ reason: new NoReactDependency({ directory: scanDirectory }) });
|
|
74779
|
+
const deadCodeSourceFileCount = input.retainExcludedProjectDeadCodeDiagnostics === true && precomputedSourceFiles !== null ? precomputedSourceFiles.length : project.sourceFileCount;
|
|
74477
74780
|
const [repo, sha, defaultBranch] = yield* all([
|
|
74478
74781
|
gitService.githubRepo(scanDirectory).pipe(orElseSucceed(() => null)),
|
|
74479
74782
|
gitService.headSha(scanDirectory).pipe(orElseSucceed(() => null)),
|
|
@@ -74484,11 +74787,16 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74484
74787
|
directory: scanDirectory,
|
|
74485
74788
|
repo
|
|
74486
74789
|
}).pipe(orElseSucceed(() => null)) : succeed$2(null));
|
|
74487
|
-
|
|
74488
|
-
|
|
74790
|
+
let lintIncludePaths = (input.skipExplicitIncludePathFilter ? input.includePaths.length > 0 ? [...input.includePaths] : void 0 : computeExplicitLintIncludePaths([...input.includePaths])) ?? resolveLintIncludePaths(scanDirectory, resolvedConfig.config, includedPrecomputedSourceFiles ?? void 0);
|
|
74791
|
+
if (excludedProjectDirectories.length > 0) lintIncludePaths = filterPathsOutsideDirectories({
|
|
74792
|
+
rootDirectory: scanDirectory,
|
|
74793
|
+
relativePaths: lintIncludePaths ?? includedPrecomputedSourceFiles ?? (yield* filesService.listSourceFiles(scanDirectory)),
|
|
74794
|
+
excludedDirectories: excludedProjectDirectories
|
|
74795
|
+
});
|
|
74796
|
+
const fallbackScannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? includedPrecomputedSourceFiles ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
|
|
74489
74797
|
const beforeLint = hooks.beforeLint ?? NO_HOOKS.beforeLint;
|
|
74490
74798
|
const afterLint = hooks.afterLint ?? NO_HOOKS.afterLint;
|
|
74491
|
-
yield* beforeLint(project
|
|
74799
|
+
yield* beforeLint(project);
|
|
74492
74800
|
const isDiffMode = input.includePaths.length > 0;
|
|
74493
74801
|
const showWarnings = input.warnings ?? resolvedConfig.config?.warnings ?? true;
|
|
74494
74802
|
const transform = buildDiagnosticPipeline({
|
|
@@ -74498,7 +74806,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74498
74806
|
respectInlineDisables: input.respectInlineDisables,
|
|
74499
74807
|
showWarnings
|
|
74500
74808
|
});
|
|
74501
|
-
const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(
|
|
74809
|
+
const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(fromPredicateOption((diagnostic) => {
|
|
74810
|
+
const filteredDiagnostic = transform.apply(diagnostic);
|
|
74811
|
+
return filteredDiagnostic === null ? none() : some(filteredDiagnostic);
|
|
74812
|
+
})));
|
|
74502
74813
|
const applyPerElementPipeline = (rawStream) => filterPerElementPipeline(rawStream).pipe(tap((diagnostic) => reporterService.emit(diagnostic)));
|
|
74503
74814
|
const envCollected = yield* runCollect(applyPerElementPipeline(fromIterable$1(isDiffMode ? [] : [
|
|
74504
74815
|
...checkReducedMotion(scanDirectory),
|
|
@@ -74508,17 +74819,31 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74508
74819
|
...checkReactNativeProject(scanDirectory, project)
|
|
74509
74820
|
])));
|
|
74510
74821
|
const securityScanFailedRef = yield* make$13(false);
|
|
74511
|
-
|
|
74822
|
+
let didSecurityScanReachDeadline = false;
|
|
74823
|
+
const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(tryPromise((signal) => checkSecurityScanCooperative(scanDirectory, {
|
|
74512
74824
|
project,
|
|
74513
74825
|
ignoredTags: input.ignoredTags,
|
|
74514
74826
|
includedTags: input.includedTags,
|
|
74515
|
-
includeTagDefaults: input.includeTagDefaults
|
|
74827
|
+
includeTagDefaults: input.includeTagDefaults,
|
|
74828
|
+
excludedDirectories: new Set(excludedProjectDirectories),
|
|
74829
|
+
deadlineEpochMs: input.deadlineEpochMs,
|
|
74830
|
+
signal,
|
|
74831
|
+
onDeadlineExceeded: () => {
|
|
74832
|
+
didSecurityScanReachDeadline = true;
|
|
74833
|
+
}
|
|
74516
74834
|
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), catch_$1(() => set(securityScanFailedRef, true).pipe(as(empty$4))))))).pipe(withSpan("SecurityScan.run")));
|
|
74835
|
+
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
74836
|
+
const capOptionalToDeadline = (phaseTimeoutMs) => {
|
|
74837
|
+
if (input.deadlineEpochMs === void 0) return phaseTimeoutMs;
|
|
74838
|
+
const remainingBudgetMs = remainingDeadlineBudgetMs(input.deadlineEpochMs);
|
|
74839
|
+
return phaseTimeoutMs === null ? remainingBudgetMs : Math.min(phaseTimeoutMs, remainingBudgetMs);
|
|
74840
|
+
};
|
|
74517
74841
|
const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
|
|
74518
|
-
const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
|
|
74842
|
+
const supplyChainOverlapTimeout = capToDeadline(yield* SupplyChainOverlapTimeoutMs);
|
|
74519
74843
|
const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
|
|
74520
74844
|
rootDirectory: scanDirectory,
|
|
74521
|
-
userConfig: resolvedConfig.config
|
|
74845
|
+
userConfig: resolvedConfig.config,
|
|
74846
|
+
timeoutMs: supplyChainOverlapTimeout
|
|
74522
74847
|
}))).pipe(map$3((diagnostics) => ({
|
|
74523
74848
|
diagnostics,
|
|
74524
74849
|
timedOut: false
|
|
@@ -74542,46 +74867,49 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74542
74867
|
const scanConcurrency = resolveScanConcurrency(yield* OxlintConcurrency);
|
|
74543
74868
|
const lintPhaseTimeoutMs = yield* LintPhaseTimeoutMs;
|
|
74544
74869
|
const deadCodePhaseTimeoutMs = yield* DeadCodePhaseTimeoutMs;
|
|
74545
|
-
const resolveDeadCodePhaseTimeoutMs = (scaledPhaseTimeoutMs) => deadCodePhaseTimeoutMs === 15e4 ? scaledPhaseTimeoutMs : deadCodePhaseTimeoutMs;
|
|
74546
74870
|
const workerCountSuffix = scanConcurrency > 1 ? ` ${highlighter.dim(`[~${scanConcurrency} workers]`)}` : "";
|
|
74547
|
-
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
74548
74871
|
const shouldRunDeadCode = input.runDeadCode && !isDiffMode && (showWarnings || deadCodeMaySurfaceWhenWarningsHidden(resolvedConfig.config));
|
|
74549
74872
|
const deadCodeOverlapMode = yield* DeadCodeOverlap;
|
|
74550
74873
|
const shouldOverlapDeadCode = shouldRunDeadCode && deadCodeOverlapMode === "on";
|
|
74551
74874
|
const deadCodeParseConcurrency = shouldOverlapDeadCode ? Math.max(1, Math.floor(scanConcurrency * DEAD_CODE_OVERLAP_PARSE_SHARE)) : void 0;
|
|
74552
74875
|
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
|
-
|
|
74876
|
+
const buildCollectDeadCode = (deadCodeTimeout) => {
|
|
74877
|
+
const collectDeadCode = runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
74878
|
+
rootDirectory: scanDirectory,
|
|
74879
|
+
parseConcurrency: deadCodeParseConcurrency,
|
|
74880
|
+
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
|
|
74881
|
+
onCacheOutcome: (didHitCache) => {
|
|
74882
|
+
deadCodeCacheHit = didHitCache;
|
|
74883
|
+
},
|
|
74884
|
+
onSummaryCacheStats: (stats) => {
|
|
74885
|
+
deadCodeSummaryCacheHits = stats.hits;
|
|
74886
|
+
deadCodeSummaryCacheMisses = stats.misses;
|
|
74887
|
+
}
|
|
74888
|
+
}).pipe(filter((diagnostic) => input.retainExcludedProjectDeadCodeDiagnostics === true || !isExcludedProjectDiagnostic(diagnostic)), catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
74889
|
+
yield* set(deadCodeFailure, {
|
|
74890
|
+
didFail: true,
|
|
74891
|
+
reason: error.message
|
|
74892
|
+
});
|
|
74893
|
+
return empty$4;
|
|
74894
|
+
}))))));
|
|
74895
|
+
const phaseTimeoutMs = deadCodeTimeout.phaseTimeoutMs;
|
|
74896
|
+
if (phaseTimeoutMs === null) return collectDeadCode;
|
|
74897
|
+
return collectDeadCode.pipe(timeoutOption(phaseTimeoutMs), flatMap$2(match$3({
|
|
74898
|
+
onNone: () => set(deadCodeFailure, {
|
|
74899
|
+
didFail: true,
|
|
74900
|
+
reason: `Dead-code analysis exceeded ${Math.round(phaseTimeoutMs / MILLISECONDS_PER_SECOND)}s and was skipped.`
|
|
74901
|
+
}).pipe(as([])),
|
|
74902
|
+
onSome: succeed$2
|
|
74903
|
+
})));
|
|
74904
|
+
};
|
|
74577
74905
|
const overlapDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
74578
|
-
sourceFileCount:
|
|
74906
|
+
sourceFileCount: deadCodeSourceFileCount,
|
|
74579
74907
|
deadCodeConcurrency: deadCodeParseConcurrency ?? scanConcurrency,
|
|
74580
74908
|
fullConcurrency: scanConcurrency
|
|
74581
74909
|
});
|
|
74582
74910
|
const deadCodeFiber = shouldOverlapDeadCode ? yield* forkChild(buildCollectDeadCode({
|
|
74583
74911
|
workerTimeoutMs: overlapDeadCodeTimeout.workerTimeoutMs,
|
|
74584
|
-
phaseTimeoutMs:
|
|
74912
|
+
phaseTimeoutMs: capOptionalToDeadline(deadCodePhaseTimeoutMs)
|
|
74585
74913
|
})) : null;
|
|
74586
74914
|
const scanProgress = yield* progressService.start("Scanning...");
|
|
74587
74915
|
const scanStartTime = Date.now();
|
|
@@ -74632,7 +74960,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74632
74960
|
});
|
|
74633
74961
|
return empty$4;
|
|
74634
74962
|
}))));
|
|
74635
|
-
const
|
|
74963
|
+
const collectLintDiagnostics = runCollect(filterPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream));
|
|
74964
|
+
const lintCollected = dedupeRelatedDiagnostics(yield* lintPhaseTimeoutMs === null ? collectLintDiagnostics : collectLintDiagnostics.pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
|
|
74636
74965
|
onNone: () => set(lintFailure, {
|
|
74637
74966
|
didFail: true,
|
|
74638
74967
|
reason: `Lint analysis exceeded ${lintPhaseTimeoutMs / MILLISECONDS_PER_SECOND}s and was skipped.`,
|
|
@@ -74662,13 +74991,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74662
74991
|
} else {
|
|
74663
74992
|
yield* scanProgress.update(`Scanned ${scannedFilesLabel}, analyzing dead code...`);
|
|
74664
74993
|
const sequentialDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
74665
|
-
sourceFileCount: totalFileCount,
|
|
74994
|
+
sourceFileCount: input.retainExcludedProjectDeadCodeDiagnostics === true ? deadCodeSourceFileCount : totalFileCount,
|
|
74666
74995
|
deadCodeConcurrency: scanConcurrency,
|
|
74667
74996
|
fullConcurrency: scanConcurrency
|
|
74668
74997
|
});
|
|
74669
74998
|
deadCodeCollected = deadCodeFiber !== null ? yield* join(deadCodeFiber) : yield* buildCollectDeadCode({
|
|
74670
74999
|
workerTimeoutMs: sequentialDeadCodeTimeout.workerTimeoutMs,
|
|
74671
|
-
phaseTimeoutMs:
|
|
75000
|
+
phaseTimeoutMs: capOptionalToDeadline(deadCodePhaseTimeoutMs)
|
|
74672
75001
|
});
|
|
74673
75002
|
}
|
|
74674
75003
|
const deadCodeFailureState = lintFailureState.didFail ? {
|
|
@@ -74712,7 +75041,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74712
75041
|
metadata: scoreMetadata
|
|
74713
75042
|
});
|
|
74714
75043
|
const lintPartialFailures = yield* get$2(partialFailuresRef);
|
|
74715
|
-
const
|
|
75044
|
+
const didSecurityScanFail = yield* get$2(securityScanFailedRef);
|
|
75045
|
+
const securityScanFailed = didSecurityScanFail || didSecurityScanReachDeadline;
|
|
75046
|
+
let securityScanFailureReason = null;
|
|
75047
|
+
if (didSecurityScanReachDeadline) securityScanFailureReason = "Security scan reached the max scan duration; findings collected before the deadline were preserved.";
|
|
75048
|
+
else if (didSecurityScanFail) securityScanFailureReason = "Security scan failed and was skipped.";
|
|
74716
75049
|
return {
|
|
74717
75050
|
project,
|
|
74718
75051
|
userConfig: resolvedConfig.config,
|
|
@@ -74735,6 +75068,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74735
75068
|
scanConcurrency,
|
|
74736
75069
|
supplyChainOverlapTimedOut: supplyChainResult.timedOut,
|
|
74737
75070
|
securityScanFailed,
|
|
75071
|
+
securityScanFailureReason,
|
|
74738
75072
|
lintCacheHitFileCount,
|
|
74739
75073
|
lintCacheTotalFileCount,
|
|
74740
75074
|
lintSidecarReplayedFileCount,
|
|
@@ -74750,7 +75084,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74750
75084
|
"inspect.runDeadCode": input.runDeadCode,
|
|
74751
75085
|
"inspect.isCi": input.isCi,
|
|
74752
75086
|
"inspect.scoreSurface": input.scoreSurface ?? "score"
|
|
74753
|
-
} }), (scanProgram) => flatMap$2(ScanDeadlineMs, (scanDeadlineMs) =>
|
|
75087
|
+
} }), (scanProgram) => flatMap$2(ScanDeadlineMs, (scanDeadlineMs) => {
|
|
75088
|
+
if (scanDeadlineMs === null) return scanProgram;
|
|
75089
|
+
return scanProgram.pipe(timeout(scanDeadlineMs), catchTag$1("TimeoutError", () => new ReactDoctorError({ reason: new ScanDeadlineExceeded({ detail: `${scanDeadlineMs / MILLISECONDS_PER_SECOND}s elapsed` }) })));
|
|
75090
|
+
}));
|
|
74754
75091
|
const parseNodeVersion = (versionString) => {
|
|
74755
75092
|
const [major = 0, minor = 0, patch = 0] = versionString.replace(/^v/, "").trim().split(".").map(Number);
|
|
74756
75093
|
return {
|
|
@@ -74851,16 +75188,6 @@ const resolveNodeForOxlint = () => {
|
|
|
74851
75188
|
});
|
|
74852
75189
|
});
|
|
74853
75190
|
/**
|
|
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
75191
|
* Writes a set of source files (supplied by `readContent` — e.g.
|
|
74865
75192
|
* `git show <ref>:<path>` for a baseline tree, or `git show :<path>` for the
|
|
74866
75193
|
* index) into a temp tree that mirrors the project layout, copying the
|
|
@@ -75135,6 +75462,7 @@ const buildJsonReport = (input) => {
|
|
|
75135
75462
|
directory: input.directory,
|
|
75136
75463
|
diff: toJsonDiff(input.diff),
|
|
75137
75464
|
projects,
|
|
75465
|
+
...input.skippedProjects && input.skippedProjects.length > 0 ? { skippedProjects: [...input.skippedProjects] } : {},
|
|
75138
75466
|
diagnostics: flattenedDiagnostics,
|
|
75139
75467
|
summary,
|
|
75140
75468
|
elapsedMilliseconds: input.totalElapsedMilliseconds,
|
|
@@ -75159,7 +75487,7 @@ const buildSkippedChecks = (input) => {
|
|
|
75159
75487
|
else if (input.lintPartialFailures.length > 0) skippedCheckReasons["lint:partial"] = input.lintPartialFailures.join("; ");
|
|
75160
75488
|
if (input.didDeadCodeFail && input.deadCodeFailureReason !== null) skippedCheckReasons["dead-code"] = input.deadCodeFailureReason;
|
|
75161
75489
|
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.";
|
|
75490
|
+
if (input.securityScanFailed) skippedCheckReasons["security-scan"] = input.securityScanFailureReason ?? "Security scan failed and was skipped.";
|
|
75163
75491
|
return {
|
|
75164
75492
|
skippedChecks,
|
|
75165
75493
|
skippedCheckReasons
|
|
@@ -75194,14 +75522,6 @@ const getDiffInfo = (directory, explicitBaseBranch, includeUntracked) => runProm
|
|
|
75194
75522
|
};
|
|
75195
75523
|
}).pipe(provide(Git.layerNode)));
|
|
75196
75524
|
const filterSourceFiles = (filePaths) => filePaths.filter(isLintableSourceFile);
|
|
75197
|
-
/**
|
|
75198
|
-
* Back-compat alias: the streaming pipeline holds its caches in
|
|
75199
|
-
* per-pipeline closures that are garbage-collected when the pipeline
|
|
75200
|
-
* goes out of scope, so there is nothing to clear at module scope. The
|
|
75201
|
-
* public CLI's `clearCaches()` still calls this for symmetry with the
|
|
75202
|
-
* other `clear*` helpers.
|
|
75203
|
-
*/
|
|
75204
|
-
const clearAutoSuppressionCaches = () => {};
|
|
75205
75525
|
const createLintPhaseAbortError = () => new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) });
|
|
75206
75526
|
const createOxlintSpawnSlots = (concurrency) => createWorkerSlots({
|
|
75207
75527
|
slotCount: resolveScanConcurrency(concurrency),
|
|
@@ -75214,59 +75534,34 @@ const createOxlintSpawnSlots = (concurrency) => createWorkerSlots({
|
|
|
75214
75534
|
* untouched so the loader sees the same plain object either way.
|
|
75215
75535
|
*/
|
|
75216
75536
|
const defineConfig = (config) => config;
|
|
75217
|
-
const
|
|
75218
|
-
["HF_DATASETS_CACHE", "huggingface"],
|
|
75219
|
-
["HF_HOME", "huggingface"],
|
|
75220
|
-
["HUGGINGFACE_HUB_CACHE", "huggingface"],
|
|
75221
|
-
["SPACE_ID", "huggingface-spaces"],
|
|
75222
|
-
["CUDA_VISIBLE_DEVICES", "cuda"],
|
|
75223
|
-
["NVIDIA_VISIBLE_DEVICES", "nvidia"],
|
|
75224
|
-
["ROCR_VISIBLE_DEVICES", "rocm"],
|
|
75225
|
-
["TPU_NAME", "google-tpu"],
|
|
75537
|
+
const AI_TRAINING_BY_ENVIRONMENT_VARIABLE = [
|
|
75226
75538
|
["WANDB_RUN_ID", "wandb"],
|
|
75227
75539
|
["WANDB_SWEEP_ID", "wandb"],
|
|
75228
75540
|
["MLFLOW_RUN_ID", "mlflow"],
|
|
75229
|
-
["MLFLOW_TRACKING_URI", "mlflow"],
|
|
75230
75541
|
["COMET_EXPERIMENT_KEY", "comet"],
|
|
75231
75542
|
["NEPTUNE_RUN_ID", "neptune"],
|
|
75232
75543
|
["CLEARML_TASK_ID", "clearml"],
|
|
75233
75544
|
["DVC_STAGE", "dvc"],
|
|
75234
75545
|
["RAY_WORKER_PROCESS", "ray"],
|
|
75235
|
-
["RAY_ADDRESS", "ray"],
|
|
75236
|
-
["MUJOCO_GL", "mujoco"],
|
|
75237
|
-
["MUJOCO_PATH", "mujoco"],
|
|
75238
|
-
["GYM_DISABLE_ENV_CHECKER", "gymnasium"],
|
|
75239
75546
|
["SM_TRAINING_ENV", "sagemaker"],
|
|
75240
75547
|
["TRAINING_JOB_ARN", "sagemaker"],
|
|
75241
|
-
["SAGEMAKER_BASE_DIR", "sagemaker"],
|
|
75242
75548
|
["AZUREML_RUN_ID", "azure-ml"],
|
|
75243
|
-
["AZURE_ML_MODEL_DIR", "azure-ml"],
|
|
75244
|
-
["CLOUD_ML_PROJECT_ID", "vertex-ai"],
|
|
75245
|
-
["VERTEX_AI_LOG_LEVEL", "vertex-ai"],
|
|
75246
|
-
["DET_MASTER", "determined-ai"],
|
|
75247
|
-
["LIGHTNING_USER_ID", "lightning-ai"],
|
|
75248
75549
|
["FLYTE_INTERNAL_EXECUTION_ID", "flyte"],
|
|
75249
|
-
["ARGO_WORKFLOW_NAME", "argo-workflows"],
|
|
75250
75550
|
["KFP_POD_NAME", "kubeflow-pipelines"],
|
|
75251
75551
|
["KAGGLE_KERNEL_RUN_TYPE", "kaggle"],
|
|
75252
75552
|
["COLAB_BACKEND_VERSION", "google-colab"],
|
|
75253
|
-
["DATABRICKS_RUNTIME_VERSION", "databricks"],
|
|
75254
75553
|
["DAYTONA_WS_ID", "daytona"],
|
|
75255
|
-
["DAYTONA_WS_NAME", "daytona"],
|
|
75256
75554
|
["E2B_SANDBOX_ID", "e2b"],
|
|
75257
75555
|
["MODAL_FUNCTION_ID", "modal"],
|
|
75258
75556
|
["MODAL_TASK_ID", "modal"],
|
|
75259
75557
|
["RUNPOD_POD_ID", "runpod"],
|
|
75260
|
-
["REPLICATE_USERNAME", "replicate"],
|
|
75261
75558
|
["VAST_CONTAINERLABEL", "vast-ai"],
|
|
75262
|
-
["HARBOR_URL", "harbor"],
|
|
75263
|
-
["HARBOR_HOSTNAME", "harbor"],
|
|
75264
75559
|
["SWE_BENCH_TASK", "swe-bench"],
|
|
75265
75560
|
["SWEBENCH_TASK", "swe-bench"],
|
|
75266
75561
|
["SWE_AGENT_MODEL", "swe-agent"]
|
|
75267
75562
|
];
|
|
75268
|
-
const detectAiTrainingEnvironment = () => {
|
|
75269
|
-
for (const [
|
|
75563
|
+
const detectAiTrainingEnvironment = (environment = process.env) => {
|
|
75564
|
+
for (const [environmentVariable, label] of AI_TRAINING_BY_ENVIRONMENT_VARIABLE) if (environment[environmentVariable]?.trim()) return label;
|
|
75270
75565
|
return null;
|
|
75271
75566
|
};
|
|
75272
75567
|
const unionValues = (baseValues, overrideValues) => [...new Set([...baseValues, ...overrideValues])];
|
|
@@ -75460,7 +75755,6 @@ const clearCaches = () => {
|
|
|
75460
75755
|
clearPackageJsonCache();
|
|
75461
75756
|
clearIgnorePatternsCache();
|
|
75462
75757
|
clearPackageRoleCache();
|
|
75463
|
-
clearAutoSuppressionCaches();
|
|
75464
75758
|
clearMinifiedFileCache();
|
|
75465
75759
|
};
|
|
75466
75760
|
const toJsonReport = (result, options) => buildJsonReport({
|
|
@@ -75487,4 +75781,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
75487
75781
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
75488
75782
|
|
|
75489
75783
|
//# sourceMappingURL=index.js.map
|
|
75490
|
-
//# debugId=
|
|
75784
|
+
//# debugId=e18e3c42-e18e-5087-9b71-eb1bcb203d6b
|