react-doctor 0.9.3 → 0.9.4-dev.25dbf6d
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 +1456 -695
- package/dist/index.d.ts +10 -2
- package/dist/index.js +820 -393
- package/dist/lsp.js +861 -355
- package/dist/run-scan-app-C8iWVgvR.js +2326 -0
- package/package.json +7 -6
- package/dist/run-scan-app-DHRvqfkX.js +0 -1733
package/dist/lsp.js
CHANGED
|
@@ -9,7 +9,7 @@ import { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FIL
|
|
|
9
9
|
import * as Crypto from "node:crypto";
|
|
10
10
|
import crypto, { createHash } from "node:crypto";
|
|
11
11
|
import * as NodeChildProcess from "node:child_process";
|
|
12
|
-
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
12
|
+
import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
13
13
|
import { ResolverFactory } from "oxc-resolver";
|
|
14
14
|
import ts from "typescript";
|
|
15
15
|
import * as NodeUrl from "node:url";
|
|
@@ -3647,7 +3647,7 @@ const flatMap$5 = /* @__PURE__ */ dual(2, (self, f) => isNone(self) ? none() : f
|
|
|
3647
3647
|
* @category filtering
|
|
3648
3648
|
* @since 2.0.0
|
|
3649
3649
|
*/
|
|
3650
|
-
const filter = /* @__PURE__ */ dual(2, (self, predicate) => isNone(self) ? none() : predicate(self.value) ? some(self.value) : none());
|
|
3650
|
+
const filter$1 = /* @__PURE__ */ dual(2, (self, predicate) => isNone(self) ? none() : predicate(self.value) ? some(self.value) : none());
|
|
3651
3651
|
//#endregion
|
|
3652
3652
|
//#region ../../node_modules/.pnpm/effect@4.0.0-beta.70/node_modules/effect/dist/Result.js
|
|
3653
3653
|
/**
|
|
@@ -7275,6 +7275,16 @@ const catchTag$2 = /* @__PURE__ */ dual((args) => isEffect$1(args[0]), (self, k,
|
|
|
7275
7275
|
return catchIf$2(self, Array.isArray(k) ? (e) => hasProperty(e, "_tag") && k.includes(e._tag) : isTagged(k), f, orElse);
|
|
7276
7276
|
});
|
|
7277
7277
|
/** @internal */
|
|
7278
|
+
const catchReasons$1 = /* @__PURE__ */ dual((args) => isEffect$1(args[0]), (self, errorTag, cases, orElse) => {
|
|
7279
|
+
let keys;
|
|
7280
|
+
return catchIf$2(self, (e) => isTagged(e, errorTag) && hasProperty(e, "reason") && hasProperty(e.reason, "_tag") && isString(e.reason._tag), (e) => {
|
|
7281
|
+
const reason = e.reason;
|
|
7282
|
+
keys ??= Object.keys(cases);
|
|
7283
|
+
if (keys.includes(reason._tag)) return internalCall(() => cases[reason._tag](reason, e));
|
|
7284
|
+
return orElse ? internalCall(() => orElse(reason, e)) : fail$7(e);
|
|
7285
|
+
});
|
|
7286
|
+
});
|
|
7287
|
+
/** @internal */
|
|
7278
7288
|
const mapError = /* @__PURE__ */ dual(2, (self, f) => catch_$2(self, (error) => failSync(() => f(error))));
|
|
7279
7289
|
const mapBoth = /* @__PURE__ */ dual(2, (self, options) => matchEffect$2(self, {
|
|
7280
7290
|
onFailure: (e) => failSync(() => options.onFailure(e)),
|
|
@@ -12184,6 +12194,42 @@ const catch_$1 = catch_$2;
|
|
|
12184
12194
|
*/
|
|
12185
12195
|
const catchTag$1 = catchTag$2;
|
|
12186
12196
|
/**
|
|
12197
|
+
* Catches multiple reasons within a tagged error using an object of handlers.
|
|
12198
|
+
*
|
|
12199
|
+
* **Example** (Handling multiple error reasons)
|
|
12200
|
+
*
|
|
12201
|
+
* ```ts
|
|
12202
|
+
* import { Data, Effect } from "effect"
|
|
12203
|
+
*
|
|
12204
|
+
* class RateLimitError extends Data.TaggedError("RateLimitError")<{
|
|
12205
|
+
* retryAfter: number
|
|
12206
|
+
* }> {}
|
|
12207
|
+
*
|
|
12208
|
+
* class QuotaExceededError extends Data.TaggedError("QuotaExceededError")<{
|
|
12209
|
+
* limit: number
|
|
12210
|
+
* }> {}
|
|
12211
|
+
*
|
|
12212
|
+
* class AiError extends Data.TaggedError("AiError")<{
|
|
12213
|
+
* reason: RateLimitError | QuotaExceededError
|
|
12214
|
+
* }> {}
|
|
12215
|
+
*
|
|
12216
|
+
* declare const program: Effect.Effect<string, AiError>
|
|
12217
|
+
*
|
|
12218
|
+
* const handled = program.pipe(
|
|
12219
|
+
* Effect.catchReasons("AiError", {
|
|
12220
|
+
* RateLimitError: (reason) =>
|
|
12221
|
+
* Effect.succeed(`Retry after ${reason.retryAfter}s`),
|
|
12222
|
+
* QuotaExceededError: (reason) =>
|
|
12223
|
+
* Effect.succeed(`Quota exceeded: ${reason.limit}`)
|
|
12224
|
+
* })
|
|
12225
|
+
* )
|
|
12226
|
+
* ```
|
|
12227
|
+
*
|
|
12228
|
+
* @category error handling
|
|
12229
|
+
* @since 4.0.0
|
|
12230
|
+
*/
|
|
12231
|
+
const catchReasons = catchReasons$1;
|
|
12232
|
+
/**
|
|
12187
12233
|
* Handles both recoverable and unrecoverable errors by providing a recovery
|
|
12188
12234
|
* effect.
|
|
12189
12235
|
*
|
|
@@ -15586,7 +15632,7 @@ function transformOptional(f) {
|
|
|
15586
15632
|
*/
|
|
15587
15633
|
function withDefault$1(defaultValue) {
|
|
15588
15634
|
return new Getter((o) => {
|
|
15589
|
-
const filtered = filter(o, isNotUndefined);
|
|
15635
|
+
const filtered = filter$1(o, isNotUndefined);
|
|
15590
15636
|
return isSome(filtered) ? succeed$2(filtered) : mapEager(defaultValue, some);
|
|
15591
15637
|
});
|
|
15592
15638
|
}
|
|
@@ -19434,6 +19480,10 @@ var JsonReportProjectEntryV3 = class extends Class("JsonReportProjectEntryV3")({
|
|
|
19434
19480
|
scannedFileCount: optional(Number$1),
|
|
19435
19481
|
elapsedMilliseconds: Number$1
|
|
19436
19482
|
}) {};
|
|
19483
|
+
var JsonReportSkippedProject = class extends Class("JsonReportSkippedProject")({
|
|
19484
|
+
directory: String$1,
|
|
19485
|
+
reason: Literal("max-duration")
|
|
19486
|
+
}) {};
|
|
19437
19487
|
/** Original full, diff, and staged report contract. */
|
|
19438
19488
|
var JsonReportV1 = class extends Class("JsonReportV1")({
|
|
19439
19489
|
schemaVersion: Literal(1),
|
|
@@ -19462,6 +19512,7 @@ var JsonReportV1 = class extends Class("JsonReportV1")({
|
|
|
19462
19512
|
reactDetected: optional(Boolean$1),
|
|
19463
19513
|
diff: NullOr(JsonReportDiffInfo),
|
|
19464
19514
|
projects: ArraySchema(JsonReportProjectEntry),
|
|
19515
|
+
skippedProjects: optional(ArraySchema(JsonReportSkippedProject)),
|
|
19465
19516
|
diagnostics: ArraySchema(Diagnostic),
|
|
19466
19517
|
summary: JsonReportSummary,
|
|
19467
19518
|
elapsedMilliseconds: Number$1,
|
|
@@ -19490,6 +19541,7 @@ var JsonReportV2 = class extends Class("JsonReportV2")({
|
|
|
19490
19541
|
diff: NullOr(JsonReportDiffInfo),
|
|
19491
19542
|
baseline: JsonReportBaseline,
|
|
19492
19543
|
projects: ArraySchema(JsonReportProjectEntry),
|
|
19544
|
+
skippedProjects: optional(ArraySchema(JsonReportSkippedProject)),
|
|
19493
19545
|
diagnostics: ArraySchema(Diagnostic),
|
|
19494
19546
|
summary: JsonReportSummary,
|
|
19495
19547
|
elapsedMilliseconds: Number$1,
|
|
@@ -19506,6 +19558,7 @@ var JsonReportV3 = class extends Class("JsonReportV3")({
|
|
|
19506
19558
|
diff: NullOr(JsonReportDiffInfo),
|
|
19507
19559
|
baseline: optional(JsonReportBaseline),
|
|
19508
19560
|
projects: ArraySchema(JsonReportProjectEntryV3),
|
|
19561
|
+
skippedProjects: optional(ArraySchema(JsonReportSkippedProject)),
|
|
19509
19562
|
diagnostics: ArraySchema(JsonReportDiagnosticV3),
|
|
19510
19563
|
summary: JsonReportSummary,
|
|
19511
19564
|
elapsedMilliseconds: Number$1,
|
|
@@ -53488,6 +53541,47 @@ const flattenArray = (self) => transformPull$1(self, (pull) => {
|
|
|
53488
53541
|
}));
|
|
53489
53542
|
});
|
|
53490
53543
|
/**
|
|
53544
|
+
* Filters arrays of elements emitted by a channel, applying the filter
|
|
53545
|
+
* to each element within the arrays and only emitting non-empty filtered arrays.
|
|
53546
|
+
*
|
|
53547
|
+
* **Example** (Filtering array output)
|
|
53548
|
+
*
|
|
53549
|
+
* ```ts
|
|
53550
|
+
* import { Array, Channel } from "effect"
|
|
53551
|
+
*
|
|
53552
|
+
* const nonEmptyArrayPredicate = Array.isReadonlyArrayNonEmpty
|
|
53553
|
+
*
|
|
53554
|
+
* // Create a channel that outputs arrays of mixed data
|
|
53555
|
+
* const arrayChannel = Channel.fromIterable([
|
|
53556
|
+
* Array.make(1, 2, 3, 4, 5),
|
|
53557
|
+
* Array.make(6, 7, 8, 9, 10),
|
|
53558
|
+
* Array.make(11, 12, 13, 14, 15)
|
|
53559
|
+
* ]).pipe(Channel.filter(nonEmptyArrayPredicate))
|
|
53560
|
+
*
|
|
53561
|
+
* // Filter arrays to keep only even numbers
|
|
53562
|
+
* const evenArraysChannel = Channel.filterArray(arrayChannel, (n) => n % 2 === 0)
|
|
53563
|
+
* // Outputs: [2, 4], [6, 8, 10], [12, 14]
|
|
53564
|
+
* // Note: Only non-empty filtered arrays are emitted
|
|
53565
|
+
*
|
|
53566
|
+
* // Arrays that would become empty after filtering are discarded entirely
|
|
53567
|
+
* const oddChannel = Channel.fromIterable([
|
|
53568
|
+
* Array.make(1, 3, 5),
|
|
53569
|
+
* Array.make(2, 4),
|
|
53570
|
+
* Array.make(7, 9)
|
|
53571
|
+
* ]).pipe(Channel.filter(nonEmptyArrayPredicate))
|
|
53572
|
+
* const filteredOddChannel = Channel.filterArray(oddChannel, (n) => n % 2 === 0)
|
|
53573
|
+
* // Outputs: [2, 4] (the arrays [1,3,5] and [7,9] are discarded)
|
|
53574
|
+
* ```
|
|
53575
|
+
*
|
|
53576
|
+
* @category filtering
|
|
53577
|
+
* @since 4.0.0
|
|
53578
|
+
*/
|
|
53579
|
+
const filterArray = /* @__PURE__ */ dual(2, (self, predicate) => transformPull$1(self, (pull) => succeed$2(flatMap$2(pull, function loop(arr) {
|
|
53580
|
+
const passes = [];
|
|
53581
|
+
for (let i = 0; i < arr.length; i++) if (predicate(arr[i])) passes.push(arr[i]);
|
|
53582
|
+
return isReadonlyArrayNonEmpty(passes) ? succeed$2(passes) : flatMap$2(pull, loop);
|
|
53583
|
+
}))));
|
|
53584
|
+
/**
|
|
53491
53585
|
* Filters and maps each element inside emitted non-empty arrays using a
|
|
53492
53586
|
* `Filter`.
|
|
53493
53587
|
*
|
|
@@ -55125,6 +55219,30 @@ const concat = /* @__PURE__ */ dual(2, (self, that) => flatten(fromArray([self,
|
|
|
55125
55219
|
*/
|
|
55126
55220
|
const merge$1 = /* @__PURE__ */ dual((args) => isStream(args[0]) && isStream(args[1]), (self, that, options) => fromChannel(merge$2(toChannel(self), toChannel(that), options)));
|
|
55127
55221
|
/**
|
|
55222
|
+
* Filters a stream to the elements that satisfy a predicate.
|
|
55223
|
+
*
|
|
55224
|
+
* **Example** (Filtering stream values)
|
|
55225
|
+
*
|
|
55226
|
+
* ```ts
|
|
55227
|
+
* import { Console, Effect, Stream } from "effect"
|
|
55228
|
+
*
|
|
55229
|
+
* const program = Effect.gen(function*() {
|
|
55230
|
+
* const stream = Stream.make(1, 2, 3, 4).pipe(
|
|
55231
|
+
* Stream.filter((n) => n % 2 === 0)
|
|
55232
|
+
* )
|
|
55233
|
+
* const values = yield* Stream.runCollect(stream)
|
|
55234
|
+
* yield* Console.log(values)
|
|
55235
|
+
* })
|
|
55236
|
+
*
|
|
55237
|
+
* Effect.runPromise(program)
|
|
55238
|
+
* // Output: [ 2, 4 ]
|
|
55239
|
+
* ```
|
|
55240
|
+
*
|
|
55241
|
+
* @category filtering
|
|
55242
|
+
* @since 2.0.0
|
|
55243
|
+
*/
|
|
55244
|
+
const filter = /* @__PURE__ */ dual(2, (self, predicate) => fromChannel(filterArray(toChannel(self), predicate)));
|
|
55245
|
+
/**
|
|
55128
55246
|
* Filters and maps stream elements in one pass using a `Filter`.
|
|
55129
55247
|
*
|
|
55130
55248
|
* @category filtering
|
|
@@ -61908,6 +62026,7 @@ const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
|
|
|
61908
62026
|
const GENERATED_SOURCE_DIRECTORY_PATTERN = /(?:^|\/)__generated__\//;
|
|
61909
62027
|
const MINIFIED_SNIFF_BYTES = 65536;
|
|
61910
62028
|
const GIT_LS_FILES_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
|
|
62029
|
+
const GIT_CHECK_ATTR_MAX_BUFFER_BYTES = GIT_LS_FILES_MAX_BUFFER_BYTES;
|
|
61911
62030
|
const SCANNED_DOT_DIRECTORIES = new Set([".dumi", ".storybook"]);
|
|
61912
62031
|
const IGNORED_DIRECTORIES = new Set([
|
|
61913
62032
|
".angular",
|
|
@@ -62216,13 +62335,9 @@ const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
|
|
|
62216
62335
|
const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
|
|
62217
62336
|
const ABORT_EXIT_CODES = new Set([134, 3221226505]);
|
|
62218
62337
|
const OXLINT_OOM_RESCUE_BUDGET_MS = 6e4;
|
|
62219
|
-
const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
|
|
62220
|
-
const LINT_PHASE_TIMEOUT_MS = 3e5;
|
|
62221
|
-
const SCAN_TOTAL_DEADLINE_MS = 9e5;
|
|
62222
62338
|
const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
|
|
62223
62339
|
const DEAD_CODE_WORKER_MEM_BUDGET_BYTES = 2 * 1024 * 1024 * 1024;
|
|
62224
62340
|
const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
|
|
62225
|
-
const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
|
|
62226
62341
|
const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
|
|
62227
62342
|
const RECOMMENDED_PNPM_MINIMUM_RELEASE_AGE_MINUTES = 10080;
|
|
62228
62343
|
const REACT_SERVER_DOM_PACKAGES = [
|
|
@@ -62269,7 +62384,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
62269
62384
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
62270
62385
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
62271
62386
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
62272
|
-
const CORE_PACKAGE_VERSION = "0.9.
|
|
62387
|
+
const CORE_PACKAGE_VERSION = "0.9.4";
|
|
62273
62388
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
62274
62389
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
62275
62390
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -62282,17 +62397,20 @@ const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
|
|
|
62282
62397
|
const isErrnoException = (error) => error instanceof Error && "code" in error;
|
|
62283
62398
|
const IGNORABLE_READDIR_ERROR_CODES = new Set([
|
|
62284
62399
|
"EACCES",
|
|
62400
|
+
"EBUSY",
|
|
62285
62401
|
"EPERM",
|
|
62286
62402
|
"ENOENT",
|
|
62287
62403
|
"ENOTDIR",
|
|
62288
62404
|
"EINVAL",
|
|
62289
62405
|
"ELOOP",
|
|
62290
|
-
"ENAMETOOLONG"
|
|
62406
|
+
"ENAMETOOLONG",
|
|
62407
|
+
"ETIMEDOUT",
|
|
62408
|
+
"UNKNOWN"
|
|
62291
62409
|
]);
|
|
62292
62410
|
const isIgnorableReaddirError = (error) => isErrnoException(error) && typeof error.code === "string" && IGNORABLE_READDIR_ERROR_CODES.has(error.code);
|
|
62293
62411
|
const readDirectoryEntries = (directoryPath) => {
|
|
62294
62412
|
try {
|
|
62295
|
-
return
|
|
62413
|
+
return fs.readdirSync(directoryPath, { withFileTypes: true });
|
|
62296
62414
|
} catch (error) {
|
|
62297
62415
|
if (isIgnorableReaddirError(error)) return [];
|
|
62298
62416
|
throw error;
|
|
@@ -62300,14 +62418,14 @@ const readDirectoryEntries = (directoryPath) => {
|
|
|
62300
62418
|
};
|
|
62301
62419
|
const isFile = (filePath) => {
|
|
62302
62420
|
try {
|
|
62303
|
-
return
|
|
62421
|
+
return fs.statSync(filePath).isFile();
|
|
62304
62422
|
} catch {
|
|
62305
62423
|
return false;
|
|
62306
62424
|
}
|
|
62307
62425
|
};
|
|
62308
62426
|
const isDirectory = (directoryPath) => {
|
|
62309
62427
|
try {
|
|
62310
|
-
return
|
|
62428
|
+
return fs.statSync(directoryPath).isDirectory();
|
|
62311
62429
|
} catch {
|
|
62312
62430
|
return false;
|
|
62313
62431
|
}
|
|
@@ -62317,6 +62435,66 @@ const isPlainObject = (value) => {
|
|
|
62317
62435
|
const prototype = Object.getPrototypeOf(value);
|
|
62318
62436
|
return prototype === null || prototype === Object.prototype;
|
|
62319
62437
|
};
|
|
62438
|
+
const LINGUIST_ATTRIBUTES = ["linguist-generated", "linguist-vendored"];
|
|
62439
|
+
const DISABLED_ATTRIBUTE_VALUES = new Set([
|
|
62440
|
+
"unspecified",
|
|
62441
|
+
"unset",
|
|
62442
|
+
"false",
|
|
62443
|
+
"0",
|
|
62444
|
+
"off",
|
|
62445
|
+
"no"
|
|
62446
|
+
]);
|
|
62447
|
+
const buildInput = (relativePaths) => `${relativePaths.join("\0")}\0`;
|
|
62448
|
+
const parseIgnoredPaths = (output) => {
|
|
62449
|
+
const ignoredPaths = /* @__PURE__ */ new Set();
|
|
62450
|
+
const fields = output.split("\0");
|
|
62451
|
+
for (let fieldIndex = 0; fieldIndex + 2 < fields.length; fieldIndex += 3) {
|
|
62452
|
+
const filePath = fields[fieldIndex];
|
|
62453
|
+
const value = fields[fieldIndex + 2]?.toLowerCase();
|
|
62454
|
+
if (filePath && value && !DISABLED_ATTRIBUTE_VALUES.has(value)) ignoredPaths.add(filePath);
|
|
62455
|
+
}
|
|
62456
|
+
return ignoredPaths;
|
|
62457
|
+
};
|
|
62458
|
+
const collectGitLinguistIgnoredPaths = (rootDirectory, relativePaths) => {
|
|
62459
|
+
if (relativePaths.length === 0) return /* @__PURE__ */ new Set();
|
|
62460
|
+
const result = spawnSync("git", [
|
|
62461
|
+
"check-attr",
|
|
62462
|
+
"-z",
|
|
62463
|
+
"--stdin",
|
|
62464
|
+
...LINGUIST_ATTRIBUTES
|
|
62465
|
+
], {
|
|
62466
|
+
cwd: rootDirectory,
|
|
62467
|
+
encoding: "utf-8",
|
|
62468
|
+
input: buildInput(relativePaths),
|
|
62469
|
+
maxBuffer: GIT_CHECK_ATTR_MAX_BUFFER_BYTES
|
|
62470
|
+
});
|
|
62471
|
+
if (result.error || result.status !== 0) return /* @__PURE__ */ new Set();
|
|
62472
|
+
return parseIgnoredPaths(result.stdout);
|
|
62473
|
+
};
|
|
62474
|
+
const collectGitLinguistIgnoredPathsCooperative = (rootDirectory, relativePaths, signal) => {
|
|
62475
|
+
if (relativePaths.length === 0) return Promise.resolve(/* @__PURE__ */ new Set());
|
|
62476
|
+
return new Promise((resolve, reject) => {
|
|
62477
|
+
signal?.throwIfAborted();
|
|
62478
|
+
execFile("git", [
|
|
62479
|
+
"check-attr",
|
|
62480
|
+
"-z",
|
|
62481
|
+
"--stdin",
|
|
62482
|
+
...LINGUIST_ATTRIBUTES
|
|
62483
|
+
], {
|
|
62484
|
+
cwd: rootDirectory,
|
|
62485
|
+
encoding: "utf-8",
|
|
62486
|
+
killSignal: "SIGKILL",
|
|
62487
|
+
maxBuffer: GIT_CHECK_ATTR_MAX_BUFFER_BYTES,
|
|
62488
|
+
signal
|
|
62489
|
+
}, (error, stdout) => {
|
|
62490
|
+
if (signal?.aborted) {
|
|
62491
|
+
reject(signal.reason);
|
|
62492
|
+
return;
|
|
62493
|
+
}
|
|
62494
|
+
resolve(error ? /* @__PURE__ */ new Set() : parseIgnoredPaths(stdout));
|
|
62495
|
+
}).stdin?.end(buildInput(relativePaths));
|
|
62496
|
+
});
|
|
62497
|
+
};
|
|
62320
62498
|
const lastSourceMappingUrl = (fileContent) => {
|
|
62321
62499
|
const lastReference = [...fileContent.matchAll(/\/\/# sourceMappingURL=(\S+)/g)].at(-1);
|
|
62322
62500
|
return lastReference ? lastReference[1] : null;
|
|
@@ -62421,6 +62599,9 @@ function* walkSourceTreeFiles(rootDirectory) {
|
|
|
62421
62599
|
}
|
|
62422
62600
|
}
|
|
62423
62601
|
}
|
|
62602
|
+
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
62603
|
+
setImmediate(resolve);
|
|
62604
|
+
});
|
|
62424
62605
|
const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
|
|
62425
62606
|
const entries = [];
|
|
62426
62607
|
for (const relativePath of relativePaths) {
|
|
@@ -62434,23 +62615,55 @@ const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
|
|
|
62434
62615
|
}
|
|
62435
62616
|
return entries;
|
|
62436
62617
|
};
|
|
62437
|
-
const
|
|
62618
|
+
const collectSizedSourceFilesCooperative = async (rootDirectory, relativePaths, signal) => {
|
|
62619
|
+
const entries = [];
|
|
62620
|
+
let sliceStartedAt = performance.now();
|
|
62621
|
+
for (const relativePath of relativePaths) {
|
|
62622
|
+
signal?.throwIfAborted();
|
|
62623
|
+
const absolutePath = Path.resolve(rootDirectory, relativePath);
|
|
62624
|
+
const sizeBytes = statSourceFileSize(absolutePath);
|
|
62625
|
+
if (!isLargeMinifiedFile(absolutePath, sizeBytes)) entries.push({
|
|
62626
|
+
path: relativePath,
|
|
62627
|
+
sizeBytes: sizeBytes ?? 0
|
|
62628
|
+
});
|
|
62629
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
62630
|
+
await yieldToEventLoop();
|
|
62631
|
+
sliceStartedAt = performance.now();
|
|
62632
|
+
}
|
|
62633
|
+
}
|
|
62634
|
+
return entries;
|
|
62635
|
+
};
|
|
62636
|
+
const parseGitSourceFilePaths = (output) => {
|
|
62637
|
+
const trackedPaths = /* @__PURE__ */ new Set();
|
|
62638
|
+
const untrackedPaths = [];
|
|
62639
|
+
for (const entry of output.split("\0")) {
|
|
62640
|
+
if (entry.length === 0) continue;
|
|
62641
|
+
const trackedPath = /^[0-7]{6} [0-9a-f]+ [0-3]\t([\s\S]+)$/.exec(entry)?.[1];
|
|
62642
|
+
if (trackedPath === void 0) untrackedPaths.push(entry);
|
|
62643
|
+
else trackedPaths.add(trackedPath);
|
|
62644
|
+
}
|
|
62645
|
+
return {
|
|
62646
|
+
trackedPaths: [...trackedPaths],
|
|
62647
|
+
untrackedPaths
|
|
62648
|
+
};
|
|
62649
|
+
};
|
|
62650
|
+
const listGitSourceFilePaths = (rootDirectory) => {
|
|
62438
62651
|
const result = spawnSync("git", [
|
|
62439
62652
|
"ls-files",
|
|
62440
62653
|
"-z",
|
|
62441
|
-
|
|
62654
|
+
"--stage",
|
|
62655
|
+
"--others",
|
|
62656
|
+
"--exclude-standard"
|
|
62442
62657
|
], {
|
|
62443
62658
|
cwd: rootDirectory,
|
|
62444
62659
|
encoding: "utf-8",
|
|
62445
62660
|
maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES
|
|
62446
62661
|
});
|
|
62447
62662
|
if (result.error || result.status !== 0) return null;
|
|
62448
|
-
return result.stdout
|
|
62663
|
+
return parseGitSourceFilePaths(result.stdout);
|
|
62449
62664
|
};
|
|
62450
|
-
const
|
|
62451
|
-
const trackedPaths =
|
|
62452
|
-
const untrackedPaths = gitLsFiles(rootDirectory, ["--others", "--exclude-standard"]);
|
|
62453
|
-
if (trackedPaths === null || untrackedPaths === null) return null;
|
|
62665
|
+
const collectGitCandidateSourceFilePaths = (rootDirectory, paths) => {
|
|
62666
|
+
const { trackedPaths, untrackedPaths } = paths;
|
|
62454
62667
|
const emitDuplicateJsPaths = collectTypeScriptEmitDuplicateJsPaths({
|
|
62455
62668
|
trackedPaths: new Set(trackedPaths),
|
|
62456
62669
|
untrackedPaths,
|
|
@@ -62458,12 +62671,65 @@ const listSourceFilesViaGit = (rootDirectory) => {
|
|
|
62458
62671
|
});
|
|
62459
62672
|
return [...trackedPaths, ...untrackedPaths].filter((filePath) => isLintableSourceFile(filePath) && !hasIgnoredPathSegment(filePath) && !emitDuplicateJsPaths.has(filePath));
|
|
62460
62673
|
};
|
|
62674
|
+
const filterGitSourceFilePaths = (rootDirectory, paths) => {
|
|
62675
|
+
const candidatePaths = collectGitCandidateSourceFilePaths(rootDirectory, paths);
|
|
62676
|
+
const linguistIgnoredPaths = collectGitLinguistIgnoredPaths(rootDirectory, candidatePaths);
|
|
62677
|
+
return candidatePaths.filter((filePath) => !linguistIgnoredPaths.has(filePath));
|
|
62678
|
+
};
|
|
62679
|
+
const listSourceFilesViaGit = (rootDirectory) => {
|
|
62680
|
+
const paths = listGitSourceFilePaths(rootDirectory);
|
|
62681
|
+
return paths === null ? null : filterGitSourceFilePaths(rootDirectory, paths);
|
|
62682
|
+
};
|
|
62683
|
+
const listSourceFilesViaGitCooperative = async (rootDirectory, signal) => {
|
|
62684
|
+
const paths = await new Promise((resolve, reject) => {
|
|
62685
|
+
signal?.throwIfAborted();
|
|
62686
|
+
execFile("git", [
|
|
62687
|
+
"ls-files",
|
|
62688
|
+
"-z",
|
|
62689
|
+
"--stage",
|
|
62690
|
+
"--others",
|
|
62691
|
+
"--exclude-standard"
|
|
62692
|
+
], {
|
|
62693
|
+
cwd: rootDirectory,
|
|
62694
|
+
encoding: "utf-8",
|
|
62695
|
+
killSignal: "SIGKILL",
|
|
62696
|
+
maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES,
|
|
62697
|
+
signal
|
|
62698
|
+
}, (error, stdout) => {
|
|
62699
|
+
if (signal?.aborted) {
|
|
62700
|
+
reject(signal.reason);
|
|
62701
|
+
return;
|
|
62702
|
+
}
|
|
62703
|
+
resolve(error ? null : parseGitSourceFilePaths(stdout));
|
|
62704
|
+
});
|
|
62705
|
+
});
|
|
62706
|
+
if (paths === null) return null;
|
|
62707
|
+
const candidatePaths = collectGitCandidateSourceFilePaths(rootDirectory, paths);
|
|
62708
|
+
const linguistIgnoredPaths = await collectGitLinguistIgnoredPathsCooperative(rootDirectory, candidatePaths, signal);
|
|
62709
|
+
return candidatePaths.filter((filePath) => !linguistIgnoredPaths.has(filePath));
|
|
62710
|
+
};
|
|
62461
62711
|
const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
62462
62712
|
const filePaths = [];
|
|
62463
62713
|
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) if (isLintableSourceFile(name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
|
|
62464
62714
|
return filePaths;
|
|
62465
62715
|
};
|
|
62466
|
-
const
|
|
62716
|
+
const listSourceFilesViaFilesystemCooperative = async (rootDirectory, signal) => {
|
|
62717
|
+
const filePaths = [];
|
|
62718
|
+
let sliceStartedAt = performance.now();
|
|
62719
|
+
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
|
|
62720
|
+
signal?.throwIfAborted();
|
|
62721
|
+
if (isLintableSourceFile(name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
|
|
62722
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
62723
|
+
await yieldToEventLoop();
|
|
62724
|
+
sliceStartedAt = performance.now();
|
|
62725
|
+
}
|
|
62726
|
+
}
|
|
62727
|
+
return filePaths;
|
|
62728
|
+
};
|
|
62729
|
+
const listSourceFilePaths = (rootDirectory) => (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort();
|
|
62730
|
+
const listSourceFilePathsCooperative = async (rootDirectory, signal) => (await listSourceFilesViaGitCooperative(rootDirectory, signal) ?? await listSourceFilesViaFilesystemCooperative(rootDirectory, signal)).sort();
|
|
62731
|
+
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, listSourceFilePaths(rootDirectory));
|
|
62732
|
+
const listSourceFilesCooperative = async (rootDirectory, signal) => (await collectSizedSourceFilesCooperative(rootDirectory, await listSourceFilePathsCooperative(rootDirectory, signal), signal)).map((entry) => entry.path);
|
|
62467
62733
|
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
62468
62734
|
const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
|
|
62469
62735
|
const UTF8_BOM_CHARACTER = "";
|
|
@@ -62589,7 +62855,12 @@ const mergeCompilerOptions = (inherited, current) => {
|
|
|
62589
62855
|
};
|
|
62590
62856
|
};
|
|
62591
62857
|
const readResolvedCompilerOptions = (tsConfigPath, extendsDepth, visitedPaths) => {
|
|
62592
|
-
|
|
62858
|
+
let realPath;
|
|
62859
|
+
try {
|
|
62860
|
+
realPath = NFS.realpathSync.native(tsConfigPath);
|
|
62861
|
+
} catch {
|
|
62862
|
+
return null;
|
|
62863
|
+
}
|
|
62593
62864
|
if (visitedPaths.has(realPath)) return null;
|
|
62594
62865
|
const tsConfig = readTsConfig(realPath);
|
|
62595
62866
|
if (!tsConfig) return null;
|
|
@@ -63117,27 +63388,30 @@ const getSelectedObjectProperty = (expression, propertyName, analysis, visitedEx
|
|
|
63117
63388
|
}
|
|
63118
63389
|
return null;
|
|
63119
63390
|
};
|
|
63120
|
-
const configExpressionMayDefineProperty = (expression, propertyName, analysis) => {
|
|
63391
|
+
const configExpressionMayDefineProperty = (expression, propertyName, analysis, visitedExpressions = /* @__PURE__ */ new Set()) => {
|
|
63121
63392
|
let resolvedExpression = expression;
|
|
63122
63393
|
while (ts.isParenthesizedExpression(resolvedExpression) || ts.isAsExpression(resolvedExpression) || ts.isTypeAssertionExpression(resolvedExpression) || ts.isSatisfiesExpression(resolvedExpression) || ts.isNonNullExpression(resolvedExpression)) resolvedExpression = resolvedExpression.expression;
|
|
63394
|
+
if (visitedExpressions.has(resolvedExpression)) return true;
|
|
63395
|
+
const nextVisitedExpressions = new Set(visitedExpressions);
|
|
63396
|
+
nextVisitedExpressions.add(resolvedExpression);
|
|
63123
63397
|
if (ts.isIdentifier(resolvedExpression)) {
|
|
63124
63398
|
if (analysis.localBindings.has(resolvedExpression.text)) {
|
|
63125
63399
|
const localReference = analysis.localBindings.get(resolvedExpression.text);
|
|
63126
|
-
return localReference?.expression ? configExpressionMayDefineProperty(localReference.expression, propertyName, localReference.analysis) : true;
|
|
63400
|
+
return localReference?.expression ? configExpressionMayDefineProperty(localReference.expression, propertyName, localReference.analysis, nextVisitedExpressions) : true;
|
|
63127
63401
|
}
|
|
63128
63402
|
const scopedBinding = getScopedConfigBinding(resolvedExpression);
|
|
63129
|
-
if (scopedBinding.wasFound) return scopedBinding.initializer ? configExpressionMayDefineProperty(scopedBinding.initializer, propertyName, analysis) : true;
|
|
63403
|
+
if (scopedBinding.wasFound) return scopedBinding.initializer ? configExpressionMayDefineProperty(scopedBinding.initializer, propertyName, analysis, nextVisitedExpressions) : true;
|
|
63130
63404
|
const topLevelBinding = getTopLevelBinding(analysis.sourceFile, resolvedExpression.text);
|
|
63131
|
-
return topLevelBinding && ts.isExpression(topLevelBinding) ? configExpressionMayDefineProperty(topLevelBinding, propertyName, analysis) : true;
|
|
63405
|
+
return topLevelBinding && ts.isExpression(topLevelBinding) ? configExpressionMayDefineProperty(topLevelBinding, propertyName, analysis, nextVisitedExpressions) : true;
|
|
63132
63406
|
}
|
|
63133
63407
|
if (ts.isConditionalExpression(resolvedExpression)) {
|
|
63134
|
-
if (isStaticallyTruthyConfigExpression(resolvedExpression.condition, analysis)) return configExpressionMayDefineProperty(resolvedExpression.whenTrue, propertyName, analysis);
|
|
63135
|
-
if (isStaticallyDisabledConfigExpression(resolvedExpression.condition, analysis)) return configExpressionMayDefineProperty(resolvedExpression.whenFalse, propertyName, analysis);
|
|
63136
|
-
return configExpressionMayDefineProperty(resolvedExpression.whenTrue, propertyName, analysis) || configExpressionMayDefineProperty(resolvedExpression.whenFalse, propertyName, analysis);
|
|
63408
|
+
if (isStaticallyTruthyConfigExpression(resolvedExpression.condition, analysis)) return configExpressionMayDefineProperty(resolvedExpression.whenTrue, propertyName, analysis, nextVisitedExpressions);
|
|
63409
|
+
if (isStaticallyDisabledConfigExpression(resolvedExpression.condition, analysis)) return configExpressionMayDefineProperty(resolvedExpression.whenFalse, propertyName, analysis, nextVisitedExpressions);
|
|
63410
|
+
return configExpressionMayDefineProperty(resolvedExpression.whenTrue, propertyName, analysis, nextVisitedExpressions) || configExpressionMayDefineProperty(resolvedExpression.whenFalse, propertyName, analysis, nextVisitedExpressions);
|
|
63137
63411
|
}
|
|
63138
63412
|
if (!ts.isObjectLiteralExpression(resolvedExpression)) return true;
|
|
63139
63413
|
return resolvedExpression.properties.some((property) => {
|
|
63140
|
-
if (ts.isSpreadAssignment(property)) return configExpressionMayDefineProperty(property.expression, propertyName, analysis);
|
|
63414
|
+
if (ts.isSpreadAssignment(property)) return configExpressionMayDefineProperty(property.expression, propertyName, analysis, nextVisitedExpressions);
|
|
63141
63415
|
if (ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property) || ts.isMethodDeclaration(property) || ts.isGetAccessorDeclaration(property) || ts.isSetAccessorDeclaration(property)) {
|
|
63142
63416
|
const staticPropertyName = getStaticPropertyName$2(property.name);
|
|
63143
63417
|
return staticPropertyName === null || staticPropertyName === propertyName;
|
|
@@ -63213,7 +63487,13 @@ const analyzeConfigModuleExport = (filePath, exportName, allowCompilerTransform,
|
|
|
63213
63487
|
if (visitedModules.has(moduleVisitKey)) return false;
|
|
63214
63488
|
const nextVisitedModules = new Set(visitedModules);
|
|
63215
63489
|
nextVisitedModules.add(moduleVisitKey);
|
|
63216
|
-
|
|
63490
|
+
let sourceText;
|
|
63491
|
+
try {
|
|
63492
|
+
sourceText = NFS.readFileSync(filePath, "utf-8");
|
|
63493
|
+
} catch {
|
|
63494
|
+
return false;
|
|
63495
|
+
}
|
|
63496
|
+
return analyzeConfigSourceFileExport(parseConfigSourceFile(filePath, sourceText), filePath, exportName, allowCompilerTransform, importDepth, nextVisitedModules, argumentsList, argumentAnalysis);
|
|
63217
63497
|
};
|
|
63218
63498
|
const analyzeConfigSourceFileExport = (sourceFile, filePath, exportName, allowCompilerTransform, importDepth, visitedModules, argumentsList = [], argumentAnalysis) => {
|
|
63219
63499
|
const analysis = {
|
|
@@ -64041,13 +64321,7 @@ const hasReactDependency = (packageJson) => {
|
|
|
64041
64321
|
};
|
|
64042
64322
|
return Object.keys(allDependencies).some((packageName) => REACT_DEPENDENCY_NAMES.has(packageName));
|
|
64043
64323
|
};
|
|
64044
|
-
const getPreactVersion = (packageJson) =>
|
|
64045
|
-
return {
|
|
64046
|
-
...packageJson.peerDependencies,
|
|
64047
|
-
...packageJson.dependencies,
|
|
64048
|
-
...packageJson.devDependencies
|
|
64049
|
-
}.preact ?? null;
|
|
64050
|
-
};
|
|
64324
|
+
const getPreactVersion = (packageJson) => getDependencySpec(packageJson, "preact");
|
|
64051
64325
|
const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, sourceDirectory = rootDirectory, sourcePackageJson = rootPackageJson, version }) => {
|
|
64052
64326
|
if (version === null || !isCatalogReference(version)) return version;
|
|
64053
64327
|
const catalogName = extractCatalogName(version);
|
|
@@ -64792,7 +65066,16 @@ const getBrowserslistProtectionRequirement = (directory) => {
|
|
|
64792
65066
|
if (!queries) return void 0;
|
|
64793
65067
|
let requirement;
|
|
64794
65068
|
let hasUnsupportedTarget = false;
|
|
64795
|
-
|
|
65069
|
+
const previousIgnoreOldData = process.env.BROWSERSLIST_IGNORE_OLD_DATA;
|
|
65070
|
+
let browserTargets = [];
|
|
65071
|
+
try {
|
|
65072
|
+
process.env.BROWSERSLIST_IGNORE_OLD_DATA = "true";
|
|
65073
|
+
browserTargets = (0, import_browserslist.default)(queries);
|
|
65074
|
+
} finally {
|
|
65075
|
+
if (previousIgnoreOldData === void 0) delete process.env.BROWSERSLIST_IGNORE_OLD_DATA;
|
|
65076
|
+
else process.env.BROWSERSLIST_IGNORE_OLD_DATA = previousIgnoreOldData;
|
|
65077
|
+
}
|
|
65078
|
+
for (const browserTarget of browserTargets) {
|
|
64796
65079
|
const browserRequirement = getBrowserProtectionRequirement(browserTarget);
|
|
64797
65080
|
if (browserRequirement === "unsupported") hasUnsupportedTarget = true;
|
|
64798
65081
|
if (browserRequirement === "noreferrer") requirement = "noreferrer";
|
|
@@ -64902,9 +65185,9 @@ const clearProjectCache = () => {
|
|
|
64902
65185
|
* framework-agnostic rules. Throws only when the directory has nothing
|
|
64903
65186
|
* to scan (no enclosing project and no source files of its own).
|
|
64904
65187
|
*/
|
|
64905
|
-
const discoverProjectWithoutPackageJson = (directory) => {
|
|
64906
|
-
const sourceFileCount = countSourceFiles(directory);
|
|
64907
|
-
const hasOwnTsConfig =
|
|
65188
|
+
const discoverProjectWithoutPackageJson = (directory, options) => {
|
|
65189
|
+
const sourceFileCount = options.sourceFileCount ?? countSourceFiles(directory);
|
|
65190
|
+
const hasOwnTsConfig = isFile(Path.join(directory, "tsconfig.json"));
|
|
64908
65191
|
const enclosingProjectRoot = findNearestAncestorPackageJson(directory);
|
|
64909
65192
|
const enclosingProject = enclosingProjectRoot !== null ? discoverProject(enclosingProjectRoot) : null;
|
|
64910
65193
|
if (enclosingProject !== null) return {
|
|
@@ -64970,12 +65253,15 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
64970
65253
|
sourceFileCount
|
|
64971
65254
|
};
|
|
64972
65255
|
};
|
|
64973
|
-
const discoverProject = (directory) => {
|
|
65256
|
+
const discoverProject = (directory, options = {}) => {
|
|
64974
65257
|
const cached = cachedProjectInfos.get(directory);
|
|
64975
|
-
if (cached !== void 0) return cached
|
|
65258
|
+
if (cached !== void 0) return options.sourceFileCount === void 0 ? cached : {
|
|
65259
|
+
...cached,
|
|
65260
|
+
sourceFileCount: options.sourceFileCount
|
|
65261
|
+
};
|
|
64976
65262
|
const packageJsonPath = Path.join(directory, "package.json");
|
|
64977
65263
|
if (!isFile(packageJsonPath)) {
|
|
64978
|
-
const synthesized = discoverProjectWithoutPackageJson(directory);
|
|
65264
|
+
const synthesized = discoverProjectWithoutPackageJson(directory, options);
|
|
64979
65265
|
cachedProjectInfos.set(directory, synthesized);
|
|
64980
65266
|
return synthesized;
|
|
64981
65267
|
}
|
|
@@ -65037,8 +65323,8 @@ const discoverProject = (directory) => {
|
|
|
65037
65323
|
const tailwindVersion = tailwindcss.version;
|
|
65038
65324
|
const zodVersion = zod.version;
|
|
65039
65325
|
const projectName = packageJson.name ?? Path.basename(directory);
|
|
65040
|
-
const hasTypeScript =
|
|
65041
|
-
const sourceFileCount = countSourceFiles(directory);
|
|
65326
|
+
const hasTypeScript = isFile(Path.join(directory, "tsconfig.json"));
|
|
65327
|
+
const sourceFileCount = options.sourceFileCount ?? countSourceFiles(directory);
|
|
65042
65328
|
const hasReactNativeWorkspace = framework === "expo" || framework === "react-native" || workspaceFacts.hasReactNativeAwarePackage;
|
|
65043
65329
|
const expoVersion = hasReactNativeWorkspace ? resolveCatalogBackedDependencyVersion({
|
|
65044
65330
|
rootDirectory: directory,
|
|
@@ -65167,134 +65453,6 @@ const discoverProject = (directory) => {
|
|
|
65167
65453
|
* single source file has nothing to analyze.
|
|
65168
65454
|
*/
|
|
65169
65455
|
const isAnalyzableProject = (project) => project.reactVersion !== null || project.preactVersion !== null || project.sourceFileCount > 0;
|
|
65170
|
-
const messageFromUnknown = (error) => error instanceof Error ? error.message : String(error);
|
|
65171
|
-
var InvalidGlobPatternError = class extends Error {
|
|
65172
|
-
pattern;
|
|
65173
|
-
reason;
|
|
65174
|
-
constructor(pattern, reason) {
|
|
65175
|
-
super(`Invalid glob pattern ${JSON.stringify(pattern)}: ${reason}`);
|
|
65176
|
-
this.name = "InvalidGlobPatternError";
|
|
65177
|
-
this.pattern = pattern;
|
|
65178
|
-
this.reason = reason;
|
|
65179
|
-
}
|
|
65180
|
-
};
|
|
65181
|
-
const assertGlobPattern = (condition, pattern, reason) => {
|
|
65182
|
-
if (!condition) throw new InvalidGlobPatternError(pattern, reason);
|
|
65183
|
-
};
|
|
65184
|
-
const countGlobWildcards = (pattern) => (pattern.match(/[*?]/g) ?? []).length;
|
|
65185
|
-
const normalizeGlobPattern = (pattern) => pattern.replace(/\\/g, "/").replace(/^\//, "");
|
|
65186
|
-
const PICOMATCH_OPTIONS = {
|
|
65187
|
-
dot: true,
|
|
65188
|
-
strictSlashes: false,
|
|
65189
|
-
windows: false
|
|
65190
|
-
};
|
|
65191
|
-
const compileGlobPattern = (rawPattern) => {
|
|
65192
|
-
assertGlobPattern(typeof rawPattern === "string" && rawPattern.length > 0, String(rawPattern), "pattern must be a non-empty string.");
|
|
65193
|
-
assertGlobPattern(rawPattern.length <= MAX_GLOB_PATTERN_LENGTH_CHARS, rawPattern, `pattern length ${rawPattern.length} exceeds the maximum of ${MAX_GLOB_PATTERN_LENGTH_CHARS} characters.`);
|
|
65194
|
-
const wildcardCount = countGlobWildcards(rawPattern);
|
|
65195
|
-
assertGlobPattern(wildcardCount <= 24, rawPattern, `pattern uses ${wildcardCount} wildcards (\`*\` / \`?\`), exceeding the maximum of 24. This guards against catastrophic backtracking from pathological patterns; split the pattern into multiple smaller entries.`);
|
|
65196
|
-
try {
|
|
65197
|
-
return import_picomatch.default.makeRe(normalizeGlobPattern(rawPattern), PICOMATCH_OPTIONS);
|
|
65198
|
-
} catch (caughtError) {
|
|
65199
|
-
throw new InvalidGlobPatternError(rawPattern, messageFromUnknown(caughtError));
|
|
65200
|
-
}
|
|
65201
|
-
};
|
|
65202
|
-
const compileGlobPatternsLenient = (patterns, onInvalid) => {
|
|
65203
|
-
const compiled = [];
|
|
65204
|
-
for (const pattern of patterns) try {
|
|
65205
|
-
compiled.push(compileGlobPattern(pattern));
|
|
65206
|
-
} catch (caughtError) {
|
|
65207
|
-
if (!(caughtError instanceof InvalidGlobPatternError)) throw caughtError;
|
|
65208
|
-
onInvalid(caughtError);
|
|
65209
|
-
}
|
|
65210
|
-
return compiled;
|
|
65211
|
-
};
|
|
65212
|
-
const toRelativePath = (filePath, rootDirectory) => {
|
|
65213
|
-
const normalizedFilePath = filePath.replace(/\\/g, "/");
|
|
65214
|
-
const normalizedRoot = rootDirectory.replace(/\\/g, "/").replace(/\/$/, "") + "/";
|
|
65215
|
-
if (normalizedFilePath.startsWith(normalizedRoot)) return normalizedFilePath.slice(normalizedRoot.length);
|
|
65216
|
-
return normalizedFilePath.replace(/^\.\//, "");
|
|
65217
|
-
};
|
|
65218
|
-
const warnConfigIssue = (message) => {
|
|
65219
|
-
process.stderr.write(`[react-doctor] ${message}\n`);
|
|
65220
|
-
};
|
|
65221
|
-
const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
65222
|
-
const collectStringList = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
65223
|
-
const validateOverrideEntry = (entry, index) => {
|
|
65224
|
-
if (!isPlainObject(entry)) {
|
|
65225
|
-
warnConfigIssue(`ignore.overrides[${index}] must be an object with { files, rules }; ignoring this entry.`);
|
|
65226
|
-
return null;
|
|
65227
|
-
}
|
|
65228
|
-
if (!isStringArray(entry.files)) {
|
|
65229
|
-
warnConfigIssue(`ignore.overrides[${index}].files must be an array of strings; ignoring this entry.`);
|
|
65230
|
-
return null;
|
|
65231
|
-
}
|
|
65232
|
-
if (entry.rules !== void 0 && !isStringArray(entry.rules)) {
|
|
65233
|
-
warnConfigIssue(`ignore.overrides[${index}].rules must be an array of "plugin/rule" strings or omitted; treating as missing (override would suppress every rule for the matched files).`);
|
|
65234
|
-
return { files: entry.files };
|
|
65235
|
-
}
|
|
65236
|
-
return entry.rules === void 0 ? { files: entry.files } : {
|
|
65237
|
-
files: entry.files,
|
|
65238
|
-
rules: entry.rules
|
|
65239
|
-
};
|
|
65240
|
-
};
|
|
65241
|
-
const compileIgnoreOverrides = (userConfig) => {
|
|
65242
|
-
const overrides = userConfig?.ignore?.overrides;
|
|
65243
|
-
if (overrides === void 0) return [];
|
|
65244
|
-
if (!Array.isArray(overrides)) {
|
|
65245
|
-
warnConfigIssue(`ignore.overrides must be an array of { files, rules } entries; ignoring.`);
|
|
65246
|
-
return [];
|
|
65247
|
-
}
|
|
65248
|
-
return overrides.flatMap((entry, index) => {
|
|
65249
|
-
const validated = validateOverrideEntry(entry, index);
|
|
65250
|
-
if (!validated) return [];
|
|
65251
|
-
const filePatterns = compileGlobPatternsLenient(collectStringList(validated.files), (error) => warnConfigIssue(`ignore.overrides[${index}]: ${error.message}`));
|
|
65252
|
-
if (filePatterns.length === 0) return [];
|
|
65253
|
-
return [{
|
|
65254
|
-
filePatterns,
|
|
65255
|
-
ruleIds: new Set(collectStringList(validated.rules))
|
|
65256
|
-
}];
|
|
65257
|
-
});
|
|
65258
|
-
};
|
|
65259
|
-
const isDiagnosticIgnoredByOverrides = (diagnostic, rootDirectory, overrides) => {
|
|
65260
|
-
if (overrides.length === 0) return false;
|
|
65261
|
-
const relativeFilePath = toRelativePath(diagnostic.filePath, rootDirectory);
|
|
65262
|
-
const ruleIdentifier = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
65263
|
-
return overrides.some((override) => override.filePatterns.some((pattern) => pattern.test(relativeFilePath)) && (override.ruleIds.size === 0 || override.ruleIds.has(ruleIdentifier)));
|
|
65264
|
-
};
|
|
65265
|
-
const SEVERITY_FOR_OVERRIDE = {
|
|
65266
|
-
error: "error",
|
|
65267
|
-
warn: "warning"
|
|
65268
|
-
};
|
|
65269
|
-
const restampSeverity = (diagnostic, override) => {
|
|
65270
|
-
const targetSeverity = SEVERITY_FOR_OVERRIDE[override];
|
|
65271
|
-
if (diagnostic.severity === targetSeverity) return diagnostic;
|
|
65272
|
-
return {
|
|
65273
|
-
...diagnostic,
|
|
65274
|
-
severity: targetSeverity
|
|
65275
|
-
};
|
|
65276
|
-
};
|
|
65277
|
-
/**
|
|
65278
|
-
* Assembles the internal `RuleSeverityControls` shape from a user
|
|
65279
|
-
* config's top-level `rules` and `categories` fields — the
|
|
65280
|
-
* ESLint / oxlint-shaped severity surface.
|
|
65281
|
-
*
|
|
65282
|
-
* Returns `undefined` when neither field is present so the common
|
|
65283
|
-
* path (no severity config at all) stays allocation-free for
|
|
65284
|
-
* downstream consumers.
|
|
65285
|
-
*/
|
|
65286
|
-
const buildRuleSeverityControls = (config) => {
|
|
65287
|
-
if (!config) return void 0;
|
|
65288
|
-
if (config.rules === void 0 && config.categories === void 0 && config.buckets === void 0) return;
|
|
65289
|
-
return {
|
|
65290
|
-
...config.rules !== void 0 ? { rules: config.rules } : {},
|
|
65291
|
-
...config.categories !== void 0 ? { categories: config.categories } : {},
|
|
65292
|
-
...config.buckets !== void 0 ? { buckets: config.buckets } : {}
|
|
65293
|
-
};
|
|
65294
|
-
};
|
|
65295
|
-
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
65296
|
-
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
65297
|
-
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
65298
65456
|
const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
65299
65457
|
"effect/no-adjust-state-on-prop-change": "react-doctor/no-adjust-state-on-prop-change",
|
|
65300
65458
|
"effect/no-chain-state-updates": "react-doctor/no-chain-state-updates",
|
|
@@ -65433,6 +65591,139 @@ const getEquivalentRuleKeys = (ruleKey) => {
|
|
|
65433
65591
|
const nativeRuleKey = canonicalizeRuleKey(ruleKey);
|
|
65434
65592
|
return [nativeRuleKey, ...getLegacyRuleKeysForNative(nativeRuleKey)];
|
|
65435
65593
|
};
|
|
65594
|
+
const messageFromUnknown = (error) => error instanceof Error ? error.message : String(error);
|
|
65595
|
+
var InvalidGlobPatternError = class extends Error {
|
|
65596
|
+
pattern;
|
|
65597
|
+
reason;
|
|
65598
|
+
constructor(pattern, reason) {
|
|
65599
|
+
super(`Invalid glob pattern ${JSON.stringify(pattern)}: ${reason}`);
|
|
65600
|
+
this.name = "InvalidGlobPatternError";
|
|
65601
|
+
this.pattern = pattern;
|
|
65602
|
+
this.reason = reason;
|
|
65603
|
+
}
|
|
65604
|
+
};
|
|
65605
|
+
const assertGlobPattern = (condition, pattern, reason) => {
|
|
65606
|
+
if (!condition) throw new InvalidGlobPatternError(pattern, reason);
|
|
65607
|
+
};
|
|
65608
|
+
const countGlobWildcards = (pattern) => (pattern.match(/[*?]/g) ?? []).length;
|
|
65609
|
+
const normalizeGlobPattern = (pattern) => pattern.replace(/\\/g, "/").replace(/^\//, "");
|
|
65610
|
+
const PICOMATCH_OPTIONS = {
|
|
65611
|
+
dot: true,
|
|
65612
|
+
strictSlashes: false,
|
|
65613
|
+
windows: false
|
|
65614
|
+
};
|
|
65615
|
+
const compileGlobPattern = (rawPattern) => {
|
|
65616
|
+
assertGlobPattern(typeof rawPattern === "string" && rawPattern.length > 0, String(rawPattern), "pattern must be a non-empty string.");
|
|
65617
|
+
assertGlobPattern(rawPattern.length <= MAX_GLOB_PATTERN_LENGTH_CHARS, rawPattern, `pattern length ${rawPattern.length} exceeds the maximum of ${MAX_GLOB_PATTERN_LENGTH_CHARS} characters.`);
|
|
65618
|
+
const wildcardCount = countGlobWildcards(rawPattern);
|
|
65619
|
+
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.`);
|
|
65620
|
+
try {
|
|
65621
|
+
return import_picomatch.default.makeRe(normalizeGlobPattern(rawPattern), PICOMATCH_OPTIONS);
|
|
65622
|
+
} catch (caughtError) {
|
|
65623
|
+
throw new InvalidGlobPatternError(rawPattern, messageFromUnknown(caughtError));
|
|
65624
|
+
}
|
|
65625
|
+
};
|
|
65626
|
+
const compileGlobPatternsLenient = (patterns, onInvalid) => {
|
|
65627
|
+
const compiled = [];
|
|
65628
|
+
for (const pattern of patterns) try {
|
|
65629
|
+
compiled.push(compileGlobPattern(pattern));
|
|
65630
|
+
} catch (caughtError) {
|
|
65631
|
+
if (!(caughtError instanceof InvalidGlobPatternError)) throw caughtError;
|
|
65632
|
+
onInvalid(caughtError);
|
|
65633
|
+
}
|
|
65634
|
+
return compiled;
|
|
65635
|
+
};
|
|
65636
|
+
const toRelativePath = (filePath, rootDirectory) => {
|
|
65637
|
+
const normalizedFilePath = filePath.replace(/\\/g, "/");
|
|
65638
|
+
const normalizedRoot = rootDirectory.replace(/\\/g, "/").replace(/\/$/, "") + "/";
|
|
65639
|
+
if (normalizedFilePath.startsWith(normalizedRoot)) return normalizedFilePath.slice(normalizedRoot.length);
|
|
65640
|
+
return normalizedFilePath.replace(/^\.\//, "");
|
|
65641
|
+
};
|
|
65642
|
+
const warnConfigIssue = (message) => {
|
|
65643
|
+
process.stderr.write(`[react-doctor] ${message}\n`);
|
|
65644
|
+
};
|
|
65645
|
+
const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
65646
|
+
const collectStringList = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
65647
|
+
const hasMatchingRuleOverride = (ruleIds, ruleIdentifier) => {
|
|
65648
|
+
if (ruleIds.size === 0) return true;
|
|
65649
|
+
for (const ruleId of ruleIds) if (isSameRuleKey(ruleId, ruleIdentifier)) return true;
|
|
65650
|
+
return false;
|
|
65651
|
+
};
|
|
65652
|
+
const validateOverrideEntry = (entry, index) => {
|
|
65653
|
+
if (!isPlainObject(entry)) {
|
|
65654
|
+
warnConfigIssue(`ignore.overrides[${index}] must be an object with { files, rules }; ignoring this entry.`);
|
|
65655
|
+
return null;
|
|
65656
|
+
}
|
|
65657
|
+
if (!isStringArray(entry.files)) {
|
|
65658
|
+
warnConfigIssue(`ignore.overrides[${index}].files must be an array of strings; ignoring this entry.`);
|
|
65659
|
+
return null;
|
|
65660
|
+
}
|
|
65661
|
+
if (entry.rules !== void 0 && !isStringArray(entry.rules)) {
|
|
65662
|
+
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).`);
|
|
65663
|
+
return { files: entry.files };
|
|
65664
|
+
}
|
|
65665
|
+
return entry.rules === void 0 ? { files: entry.files } : {
|
|
65666
|
+
files: entry.files,
|
|
65667
|
+
rules: entry.rules
|
|
65668
|
+
};
|
|
65669
|
+
};
|
|
65670
|
+
const compileIgnoreOverrides = (userConfig) => {
|
|
65671
|
+
const overrides = userConfig?.ignore?.overrides;
|
|
65672
|
+
if (overrides === void 0) return [];
|
|
65673
|
+
if (!Array.isArray(overrides)) {
|
|
65674
|
+
warnConfigIssue(`ignore.overrides must be an array of { files, rules } entries; ignoring.`);
|
|
65675
|
+
return [];
|
|
65676
|
+
}
|
|
65677
|
+
return overrides.flatMap((entry, index) => {
|
|
65678
|
+
const validated = validateOverrideEntry(entry, index);
|
|
65679
|
+
if (!validated) return [];
|
|
65680
|
+
const filePatterns = compileGlobPatternsLenient(collectStringList(validated.files), (error) => warnConfigIssue(`ignore.overrides[${index}]: ${error.message}`));
|
|
65681
|
+
if (filePatterns.length === 0) return [];
|
|
65682
|
+
return [{
|
|
65683
|
+
filePatterns,
|
|
65684
|
+
ruleIds: new Set(collectStringList(validated.rules))
|
|
65685
|
+
}];
|
|
65686
|
+
});
|
|
65687
|
+
};
|
|
65688
|
+
const isDiagnosticIgnoredByOverrides = (diagnostic, rootDirectory, overrides) => {
|
|
65689
|
+
if (overrides.length === 0) return false;
|
|
65690
|
+
const relativeFilePath = toRelativePath(diagnostic.filePath, rootDirectory);
|
|
65691
|
+
const ruleIdentifier = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
65692
|
+
return overrides.some((override) => override.filePatterns.some((pattern) => pattern.test(relativeFilePath)) && hasMatchingRuleOverride(override.ruleIds, ruleIdentifier));
|
|
65693
|
+
};
|
|
65694
|
+
const SEVERITY_FOR_OVERRIDE = {
|
|
65695
|
+
error: "error",
|
|
65696
|
+
warn: "warning"
|
|
65697
|
+
};
|
|
65698
|
+
const restampSeverity = (diagnostic, override) => {
|
|
65699
|
+
const targetSeverity = SEVERITY_FOR_OVERRIDE[override];
|
|
65700
|
+
if (diagnostic.severity === targetSeverity) return diagnostic;
|
|
65701
|
+
return {
|
|
65702
|
+
...diagnostic,
|
|
65703
|
+
severity: targetSeverity
|
|
65704
|
+
};
|
|
65705
|
+
};
|
|
65706
|
+
/**
|
|
65707
|
+
* Assembles the internal `RuleSeverityControls` shape from a user
|
|
65708
|
+
* config's top-level `rules` and `categories` fields — the
|
|
65709
|
+
* ESLint / oxlint-shaped severity surface.
|
|
65710
|
+
*
|
|
65711
|
+
* Returns `undefined` when neither field is present so the common
|
|
65712
|
+
* path (no severity config at all) stays allocation-free for
|
|
65713
|
+
* downstream consumers.
|
|
65714
|
+
*/
|
|
65715
|
+
const buildRuleSeverityControls = (config) => {
|
|
65716
|
+
if (!config) return void 0;
|
|
65717
|
+
if (config.rules === void 0 && config.categories === void 0 && config.buckets === void 0) return;
|
|
65718
|
+
return {
|
|
65719
|
+
...config.rules !== void 0 ? { rules: config.rules } : {},
|
|
65720
|
+
...config.categories !== void 0 ? { categories: config.categories } : {},
|
|
65721
|
+
...config.buckets !== void 0 ? { buckets: config.buckets } : {}
|
|
65722
|
+
};
|
|
65723
|
+
};
|
|
65724
|
+
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
65725
|
+
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
65726
|
+
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
65436
65727
|
const stripDescriptionTail = (ruleList) => {
|
|
65437
65728
|
const descriptionMatch = ruleList.match(/(?:^|\s)--\s/);
|
|
65438
65729
|
if (!descriptionMatch || descriptionMatch.index === void 0) return ruleList;
|
|
@@ -65845,6 +66136,10 @@ const classifyByDirectoryCohort = (packageDirectory) => {
|
|
|
65845
66136
|
current = parent;
|
|
65846
66137
|
}
|
|
65847
66138
|
};
|
|
66139
|
+
const clearPackageRoleCache = () => {
|
|
66140
|
+
cachedRoleByPackageDirectory.clear();
|
|
66141
|
+
cachedPackageDirectoryByFilename.clear();
|
|
66142
|
+
};
|
|
65848
66143
|
const classifyPackageRole = (filename) => {
|
|
65849
66144
|
if (!filename) return "unknown";
|
|
65850
66145
|
const packageDirectory = findNearestPackageDirectory(filename);
|
|
@@ -66079,12 +66374,8 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
66079
66374
|
if (rule.tags.includes("migration-hint")) return false;
|
|
66080
66375
|
return getFileContext(diagnostic.filePath) !== "production";
|
|
66081
66376
|
};
|
|
66082
|
-
const
|
|
66083
|
-
for (const
|
|
66084
|
-
return false;
|
|
66085
|
-
};
|
|
66086
|
-
const isAppOnlyRule = (ruleIdentifier) => {
|
|
66087
|
-
for (const appOnlyRuleKey of APP_ONLY_RULE_KEYS) if (isSameRuleKey(appOnlyRuleKey, ruleIdentifier)) return true;
|
|
66377
|
+
const matchesRuleKey = (ruleIdentifier, candidates) => {
|
|
66378
|
+
for (const candidate of candidates) if (isSameRuleKey(candidate, ruleIdentifier)) return true;
|
|
66088
66379
|
return false;
|
|
66089
66380
|
};
|
|
66090
66381
|
const isRnRawTextSuppressedByConfig = (diagnostic) => {
|
|
@@ -66126,12 +66417,13 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
66126
66417
|
if (explicitSeverityOverride === "off") return suppress(current, "config");
|
|
66127
66418
|
if (explicitSeverityOverride !== void 0) current = restampSeverity(current, explicitSeverityOverride);
|
|
66128
66419
|
}
|
|
66420
|
+
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
66129
66421
|
if (explicitRuleOverride === void 0) {
|
|
66130
|
-
if (
|
|
66422
|
+
if (matchesRuleKey(ruleIdentifier, APP_ONLY_RULE_KEYS) && isLibraryFile(current.filePath)) return null;
|
|
66131
66423
|
}
|
|
66132
66424
|
if (!showWarnings && current.severity === "warning" && explicitSeverityOverride !== "warn") return null;
|
|
66133
66425
|
if (userConfig) {
|
|
66134
|
-
if (
|
|
66426
|
+
if (matchesRuleKey(ruleIdentifier, ignoredRules)) return suppress(current, "config");
|
|
66135
66427
|
if (isFileIgnoredByPatterns(current.filePath, rootDirectory, ignoredFilePatterns)) return null;
|
|
66136
66428
|
if (isDiagnosticIgnoredByOverrides(current, rootDirectory, compiledOverrides)) return suppress(current, "override");
|
|
66137
66429
|
if (isRnRawTextSuppressedByConfig(current)) return null;
|
|
@@ -66140,7 +66432,6 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
66140
66432
|
if (respectInlineDisables && current.line > 0) {
|
|
66141
66433
|
const lines = getFileLines(current.filePath);
|
|
66142
66434
|
if (lines) {
|
|
66143
|
-
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
66144
66435
|
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
66145
66436
|
if (evaluation.isSuppressed) return suppress(current, evaluation.isForeignDirective ? "foreign-inline" : "inline");
|
|
66146
66437
|
if (evaluation.nearMissHint) current = {
|
|
@@ -66394,7 +66685,8 @@ const readSystemFacts$1 = () => ({
|
|
|
66394
66685
|
/**
|
|
66395
66686
|
* Auto lint-worker count: the smaller of the (cgroup-CPU-aware) core count and
|
|
66396
66687
|
* the number of `PER_WORKER_MEM_BUDGET_BYTES` workers that fit in available
|
|
66397
|
-
* memory,
|
|
66688
|
+
* memory, and the measured automatic parallel-efficiency ceiling, then clamped
|
|
66689
|
+
* to `[MIN, HARD_MAX]` by `resolveScanConcurrency`.
|
|
66398
66690
|
*
|
|
66399
66691
|
* `os.availableParallelism()` already respects cgroup CPU quotas, so the core
|
|
66400
66692
|
* term needs no help. Available memory is `os.totalmem()` floored by the cgroup
|
|
@@ -66410,7 +66702,7 @@ const readSystemFacts$1 = () => ({
|
|
|
66410
66702
|
const resolveAutoScanConcurrency = (facts = readSystemFacts$1()) => {
|
|
66411
66703
|
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
66412
66704
|
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / PER_WORKER_MEM_BUDGET_BYTES);
|
|
66413
|
-
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
66705
|
+
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers, 10));
|
|
66414
66706
|
};
|
|
66415
66707
|
const resolveLintBatchOrdering = () => {
|
|
66416
66708
|
return process.env["REACT_DOCTOR_LINT_BATCH_ORDERING"]?.trim().toLowerCase() === "arrival" ? "arrival" : "cost";
|
|
@@ -66423,24 +66715,22 @@ const resolveLintBatchOrdering = () => {
|
|
|
66423
66715
|
*/
|
|
66424
66716
|
var OxlintSpawnTimeoutMs = class extends Reference("react-doctor/OxlintSpawnTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_OXLINT_SPAWN_TIMEOUT_MS", OXLINT_SPAWN_TIMEOUT_MS) }) {};
|
|
66425
66717
|
/**
|
|
66426
|
-
* Effect-side cap on the lint phase.
|
|
66427
|
-
*
|
|
66718
|
+
* Optional Effect-side cap on the lint phase. Unbounded unless explicitly
|
|
66719
|
+
* configured through the env var or a test layer.
|
|
66428
66720
|
* Tests override via `Layer.succeed(LintPhaseTimeoutMs, ...)`.
|
|
66429
66721
|
*/
|
|
66430
|
-
var LintPhaseTimeoutMs = class extends Reference("react-doctor/LintPhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_LINT_PHASE_TIMEOUT_MS",
|
|
66722
|
+
var LintPhaseTimeoutMs = class extends Reference("react-doctor/LintPhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_LINT_PHASE_TIMEOUT_MS", null) }) {};
|
|
66431
66723
|
/**
|
|
66432
|
-
* Effect-side cap on the dead-code phase
|
|
66433
|
-
*
|
|
66434
|
-
* type-heavy projects; tests override via
|
|
66724
|
+
* Optional Effect-side cap on the dead-code phase. Unbounded unless explicitly
|
|
66725
|
+
* configured through the env var or a test layer. Tests override via
|
|
66435
66726
|
* `Layer.succeed(DeadCodePhaseTimeoutMs, ...)`.
|
|
66436
66727
|
*/
|
|
66437
|
-
var DeadCodePhaseTimeoutMs = class extends Reference("react-doctor/DeadCodePhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_DEAD_CODE_PHASE_TIMEOUT_MS",
|
|
66728
|
+
var DeadCodePhaseTimeoutMs = class extends Reference("react-doctor/DeadCodePhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_DEAD_CODE_PHASE_TIMEOUT_MS", null) }) {};
|
|
66438
66729
|
/**
|
|
66439
|
-
*
|
|
66440
|
-
*
|
|
66441
|
-
* large repos; tests override via `Layer.succeed(ScanDeadlineMs, ...)`.
|
|
66730
|
+
* Optional overall scan deadline. Unbounded unless explicitly configured
|
|
66731
|
+
* through the env var or a test layer.
|
|
66442
66732
|
*/
|
|
66443
|
-
var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SCAN_DEADLINE_MS",
|
|
66733
|
+
var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SCAN_DEADLINE_MS", null) }) {};
|
|
66444
66734
|
/**
|
|
66445
66735
|
* Wall-clock budget for the supply-chain check when it runs on a background
|
|
66446
66736
|
* fiber overlapping the lint pass. Reads from the env var on startup so the
|
|
@@ -66513,14 +66803,14 @@ var DeadCodeOverlap = class extends Reference("react-doctor/DeadCodeOverlap", {
|
|
|
66513
66803
|
/**
|
|
66514
66804
|
* How the full-scan lint pass plans its file batches. `"cost"` (the default)
|
|
66515
66805
|
* builds size-balanced LPT batches (`planLintBatches`): the same mandatory
|
|
66516
|
-
* batch count as greedy chunking
|
|
66517
|
-
*
|
|
66806
|
+
* batch count as greedy chunking, but every batch gets an even share of files
|
|
66807
|
+
* AND bytes, so no full chunk is a straggler while
|
|
66518
66808
|
* the remainder-batch worker idles — and the heavy files are SPREAD across
|
|
66519
66809
|
* batches, the precondition the old sort-desc-then-chunk-100 `cost` mode
|
|
66520
66810
|
* lacked (it packed the heaviest files into one wave-1 straggler batch,
|
|
66521
66811
|
* measurably regressing size-skewed repos, which is why it never earned the
|
|
66522
66812
|
* default). `"arrival"` (`REACT_DOCTOR_LINT_BATCH_ORDERING=arrival`) is the
|
|
66523
|
-
* rollback hatch to plain greedy
|
|
66813
|
+
* rollback hatch to plain greedy fixed-size chunking in discovery order. Tests
|
|
66524
66814
|
* override via `Layer.succeed(LintBatchOrdering, ...)`. Diff / staged scans
|
|
66525
66815
|
* never reach this — they pass user-scoped `includePaths` that skip discovery
|
|
66526
66816
|
* and stay in arrival order; only the full-scan branch reads it.
|
|
@@ -66615,6 +66905,7 @@ const DEFAULT_SURFACE_EXCLUDED_TAGS = {
|
|
|
66615
66905
|
score: ["design"],
|
|
66616
66906
|
ciFailure: ["design"]
|
|
66617
66907
|
};
|
|
66908
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
66618
66909
|
const VALID_RULE_SEVERITIES = [
|
|
66619
66910
|
"error",
|
|
66620
66911
|
"warn",
|
|
@@ -66657,7 +66948,6 @@ const SURFACE_CONTROL_FIELD_NAMES = [
|
|
|
66657
66948
|
];
|
|
66658
66949
|
const isIncludedFileContext = (value) => VALID_INCLUDED_FILE_CONTEXTS.some((fileContext) => fileContext === value);
|
|
66659
66950
|
const SEVERITY_FIELD_NAMES = ["rules", "categories"];
|
|
66660
|
-
const isPlainObject$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
66661
66951
|
const formatType = (value) => typeof value === "string" ? `"${value}"` : typeof value;
|
|
66662
66952
|
const isRuleSeverity = (value) => typeof value === "string" && VALID_RULE_SEVERITIES.includes(value);
|
|
66663
66953
|
const coerceMaybeBooleanString = (fieldName, value) => {
|
|
@@ -66684,8 +66974,45 @@ const validateStringArrayField = (fieldName, value) => {
|
|
|
66684
66974
|
return false;
|
|
66685
66975
|
});
|
|
66686
66976
|
};
|
|
66977
|
+
const validateIgnoreOverride = (rawOverride, index) => {
|
|
66978
|
+
const fieldName = `ignore.overrides[${index}]`;
|
|
66979
|
+
if (!isRecord(rawOverride)) {
|
|
66980
|
+
warnConfigIssue(`config field "${fieldName}" must be an object (got ${typeof rawOverride}); ignoring this override.`);
|
|
66981
|
+
return;
|
|
66982
|
+
}
|
|
66983
|
+
const files = validateStringArrayField(`${fieldName}.files`, rawOverride.files);
|
|
66984
|
+
if (files === void 0) return void 0;
|
|
66985
|
+
const validatedOverride = { files };
|
|
66986
|
+
if (rawOverride.rules !== void 0) {
|
|
66987
|
+
const rules = validateStringArrayField(`${fieldName}.rules`, rawOverride.rules);
|
|
66988
|
+
if (rules !== void 0) validatedOverride.rules = rules;
|
|
66989
|
+
}
|
|
66990
|
+
return validatedOverride;
|
|
66991
|
+
};
|
|
66992
|
+
const validateIgnoreField = (rawIgnore) => {
|
|
66993
|
+
if (!isRecord(rawIgnore)) {
|
|
66994
|
+
warnConfigIssue(`config field "ignore" must be an object (got ${typeof rawIgnore}); ignoring this field.`);
|
|
66995
|
+
return;
|
|
66996
|
+
}
|
|
66997
|
+
const validatedIgnore = {};
|
|
66998
|
+
for (const fieldName of [
|
|
66999
|
+
"rules",
|
|
67000
|
+
"files",
|
|
67001
|
+
"tags"
|
|
67002
|
+
]) {
|
|
67003
|
+
if (rawIgnore[fieldName] === void 0) continue;
|
|
67004
|
+
const validatedValues = validateStringArrayField(`ignore.${fieldName}`, rawIgnore[fieldName]);
|
|
67005
|
+
if (validatedValues !== void 0) validatedIgnore[fieldName] = validatedValues;
|
|
67006
|
+
}
|
|
67007
|
+
if (rawIgnore.overrides !== void 0) if (!Array.isArray(rawIgnore.overrides)) warnConfigIssue(`config field "ignore.overrides" must be an array (got ${typeof rawIgnore.overrides}); ignoring this field.`);
|
|
67008
|
+
else validatedIgnore.overrides = rawIgnore.overrides.flatMap((rawOverride, index) => {
|
|
67009
|
+
const validatedOverride = validateIgnoreOverride(rawOverride, index);
|
|
67010
|
+
return validatedOverride ? [validatedOverride] : [];
|
|
67011
|
+
});
|
|
67012
|
+
return validatedIgnore;
|
|
67013
|
+
};
|
|
66687
67014
|
const validateSurfaceControls = (surface, rawControls) => {
|
|
66688
|
-
if (!
|
|
67015
|
+
if (!isRecord(rawControls)) {
|
|
66689
67016
|
warnConfigIssue(`config field "surfaces.${surface}" must be an object (got ${typeof rawControls}); ignoring this surface.`);
|
|
66690
67017
|
return;
|
|
66691
67018
|
}
|
|
@@ -66709,7 +67036,7 @@ const validateSurfaceControls = (surface, rawControls) => {
|
|
|
66709
67036
|
return validatedSurfaceControls;
|
|
66710
67037
|
};
|
|
66711
67038
|
const validateSurfacesField = (rawSurfaces) => {
|
|
66712
|
-
if (!
|
|
67039
|
+
if (!isRecord(rawSurfaces)) {
|
|
66713
67040
|
warnConfigIssue(`config field "surfaces" must be an object (got ${typeof rawSurfaces}); ignoring this field.`);
|
|
66714
67041
|
return;
|
|
66715
67042
|
}
|
|
@@ -66725,7 +67052,7 @@ const validateSurfacesField = (rawSurfaces) => {
|
|
|
66725
67052
|
return validated;
|
|
66726
67053
|
};
|
|
66727
67054
|
const validateSeverityMap = (fieldName, rawMap, keysAreCategories = false) => {
|
|
66728
|
-
if (!
|
|
67055
|
+
if (!isRecord(rawMap)) {
|
|
66729
67056
|
warnConfigIssue(`config field "${fieldName}" must be an object (got ${typeof rawMap}); ignoring this field.`);
|
|
66730
67057
|
return;
|
|
66731
67058
|
}
|
|
@@ -66759,6 +67086,7 @@ const validateConfigTypes = (config) => {
|
|
|
66759
67086
|
for (const fieldName of BOOLEAN_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => coerceMaybeBooleanString(fieldName, value));
|
|
66760
67087
|
for (const fieldName of STRING_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateString(fieldName, value));
|
|
66761
67088
|
for (const fieldName of STRING_ARRAY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateStringArrayField(fieldName, value));
|
|
67089
|
+
applyFieldValidator(config, validated, "ignore", validateIgnoreField);
|
|
66762
67090
|
applyFieldValidator(config, validated, "surfaces", validateSurfacesField);
|
|
66763
67091
|
for (const fieldName of SEVERITY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateSeverityMap(fieldName, value, fieldName === "categories"));
|
|
66764
67092
|
applyFieldValidator(config, validated, "plugins", (value) => validateStringArrayField("plugins", value));
|
|
@@ -67010,6 +67338,10 @@ const dedupeRelatedDiagnostics = (diagnostics) => {
|
|
|
67010
67338
|
}
|
|
67011
67339
|
return uniqueDiagnostics;
|
|
67012
67340
|
};
|
|
67341
|
+
const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
|
|
67342
|
+
const relativePath = Path.relative(parentAbsolutePath, childAbsolutePath);
|
|
67343
|
+
return Boolean(relativePath) && !relativePath.startsWith("..") && !Path.isAbsolute(relativePath);
|
|
67344
|
+
};
|
|
67013
67345
|
const compareStrings = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
67014
67346
|
const sortDiagnosticsStable = (diagnostics) => [...diagnostics].sort((left, right) => compareStrings(left.filePath, right.filePath) || left.line - right.line || left.column - right.column || compareStrings(left.plugin, right.plugin) || compareStrings(left.rule, right.rule) || compareStrings(left.severity, right.severity) || compareStrings(left.message, right.message));
|
|
67015
67347
|
const getDirectDependencyNames = (packageJson) => new Set([...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.devDependencies ?? {})]);
|
|
@@ -68371,7 +68703,7 @@ const collectScriptMotionEvidence = (sourceFile, typeChecker, program) => {
|
|
|
68371
68703
|
if (calleeEvidence.isReducedMotionHook && isExpressionValueConsumed(node)) evidence.hasReducedMotionHandling = true;
|
|
68372
68704
|
}
|
|
68373
68705
|
if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
|
|
68374
|
-
const tagEvidence = ts.
|
|
68706
|
+
const tagEvidence = ts.isIdentifier(node.tagName) || ts.isPropertyAccessExpression(node.tagName) ? resolveMotionExpressionEvidence(node.tagName, typeChecker, program) : EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
68375
68707
|
if (tagEvidence.isMotionComponent || tagEvidence.isMotionComponentFactory) evidence.hasMotionUse = true;
|
|
68376
68708
|
if (tagEvidence.isMotionConfig && jsxElementHasReducedMotionConfiguration(node.attributes, typeChecker)) evidence.hasReducedMotionHandling = true;
|
|
68377
68709
|
}
|
|
@@ -68586,7 +68918,7 @@ const readScannedFile = (candidate) => {
|
|
|
68586
68918
|
return null;
|
|
68587
68919
|
}
|
|
68588
68920
|
};
|
|
68589
|
-
function* collectSecurityScanFiles(rootDirectory) {
|
|
68921
|
+
function* collectSecurityScanFiles(rootDirectory, excludedDirectories = /* @__PURE__ */ new Set()) {
|
|
68590
68922
|
const priorityCandidates = [];
|
|
68591
68923
|
const artifactCandidates = [];
|
|
68592
68924
|
const otherCandidates = [];
|
|
@@ -68601,7 +68933,7 @@ function* collectSecurityScanFiles(rootDirectory) {
|
|
|
68601
68933
|
for (const entry of readDirectoryEntries(current.absolutePath)) {
|
|
68602
68934
|
const absolutePath = Path.join(current.absolutePath, entry.name);
|
|
68603
68935
|
if (entry.isDirectory()) {
|
|
68604
|
-
if (!SKIPPED_DIRECTORY_NAMES.has(entry.name)) stack.push({
|
|
68936
|
+
if (!SKIPPED_DIRECTORY_NAMES.has(entry.name) && !excludedDirectories.has(Path.resolve(absolutePath))) stack.push({
|
|
68605
68937
|
absolutePath,
|
|
68606
68938
|
depth: current.depth + 1
|
|
68607
68939
|
});
|
|
@@ -68782,10 +69114,8 @@ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledWhe
|
|
|
68782
69114
|
}
|
|
68783
69115
|
return true;
|
|
68784
69116
|
};
|
|
69117
|
+
const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
|
|
68785
69118
|
const shouldEnableRuleByDefaultStatus = (options) => options.defaultEnabled !== false || options.hasExplicitOverride === true || options.includeTagDefaults && options.hasIncludedTags;
|
|
68786
|
-
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
68787
|
-
setImmediate(resolve);
|
|
68788
|
-
});
|
|
68789
69119
|
const createSecurityScanSession = (rootDirectory, options) => {
|
|
68790
69120
|
const capabilities = options.project ? getCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
68791
69121
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
@@ -68838,19 +69168,35 @@ const createSecurityScanSession = (rootDirectory, options) => {
|
|
|
68838
69168
|
};
|
|
68839
69169
|
};
|
|
68840
69170
|
const checkSecurityScanCooperative = async (rootDirectory, options = {}) => {
|
|
69171
|
+
const throwIfScanAborted = () => {
|
|
69172
|
+
options.signal?.throwIfAborted();
|
|
69173
|
+
};
|
|
69174
|
+
const didReachDeadline = () => {
|
|
69175
|
+
const hasDeadlineElapsed = options.deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(options.deadlineEpochMs) === 0;
|
|
69176
|
+
if (hasDeadlineElapsed) options.onDeadlineExceeded?.();
|
|
69177
|
+
return hasDeadlineElapsed;
|
|
69178
|
+
};
|
|
69179
|
+
throwIfScanAborted();
|
|
68841
69180
|
const session = createSecurityScanSession(rootDirectory, options);
|
|
68842
69181
|
if (session === null) return [];
|
|
69182
|
+
if (didReachDeadline()) return [];
|
|
68843
69183
|
let sliceStartedAt = performance.now();
|
|
68844
|
-
for (const file of collectSecurityScanFiles(rootDirectory)) {
|
|
69184
|
+
for (const file of collectSecurityScanFiles(rootDirectory, options.excludedDirectories)) {
|
|
69185
|
+
throwIfScanAborted();
|
|
69186
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
68845
69187
|
if (file === null) {
|
|
68846
69188
|
if (performance.now() - sliceStartedAt >= 12) {
|
|
68847
69189
|
await yieldToEventLoop();
|
|
69190
|
+
throwIfScanAborted();
|
|
69191
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
68848
69192
|
sliceStartedAt = performance.now();
|
|
68849
69193
|
}
|
|
68850
69194
|
continue;
|
|
68851
69195
|
}
|
|
68852
69196
|
for (const _ruleStep of session.scanFileByRule(file)) if (performance.now() - sliceStartedAt >= 12) {
|
|
68853
69197
|
await yieldToEventLoop();
|
|
69198
|
+
throwIfScanAborted();
|
|
69199
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
68854
69200
|
sliceStartedAt = performance.now();
|
|
68855
69201
|
}
|
|
68856
69202
|
}
|
|
@@ -69013,25 +69359,18 @@ const collectIgnorePatterns = (rootDirectory) => {
|
|
|
69013
69359
|
cachedPatternsByRoot.set(rootDirectory, patterns);
|
|
69014
69360
|
return patterns;
|
|
69015
69361
|
};
|
|
69016
|
-
const
|
|
69017
|
-
const KNIP_JSON_FILENAME = "knip.json";
|
|
69018
|
-
const readJsonFileSafe = (filePath) => {
|
|
69019
|
-
let rawContents;
|
|
69020
|
-
try {
|
|
69021
|
-
rawContents = fs.readFileSync(filePath, "utf-8");
|
|
69022
|
-
} catch {
|
|
69023
|
-
return null;
|
|
69024
|
-
}
|
|
69362
|
+
const failOpenReadJson = (filePath, fallback) => {
|
|
69025
69363
|
try {
|
|
69026
|
-
return JSON.parse(
|
|
69364
|
+
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
69027
69365
|
} catch {
|
|
69028
|
-
return
|
|
69366
|
+
return fallback;
|
|
69029
69367
|
}
|
|
69030
69368
|
};
|
|
69369
|
+
const KNIP_JSON_FILENAME = "knip.json";
|
|
69031
69370
|
const readKnipConfig = (rootDirectory) => {
|
|
69032
|
-
const knipJson =
|
|
69371
|
+
const knipJson = failOpenReadJson(path.join(rootDirectory, KNIP_JSON_FILENAME), null);
|
|
69033
69372
|
if (isRecord(knipJson)) return knipJson;
|
|
69034
|
-
const packageJson =
|
|
69373
|
+
const packageJson = failOpenReadJson(path.join(rootDirectory, "package.json"), null);
|
|
69035
69374
|
const packageKnipConfig = isRecord(packageJson) ? packageJson.knip : null;
|
|
69036
69375
|
return isRecord(packageKnipConfig) ? packageKnipConfig : null;
|
|
69037
69376
|
};
|
|
@@ -69079,13 +69418,6 @@ const atomicWriteJson = (filePath, value) => {
|
|
|
69079
69418
|
return;
|
|
69080
69419
|
}
|
|
69081
69420
|
};
|
|
69082
|
-
const failOpenReadJson = (filePath, fallback) => {
|
|
69083
|
-
try {
|
|
69084
|
-
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
69085
|
-
} catch {
|
|
69086
|
-
return fallback;
|
|
69087
|
-
}
|
|
69088
|
-
};
|
|
69089
69421
|
const hashFileContents = (filePath) => {
|
|
69090
69422
|
try {
|
|
69091
69423
|
return crypto.createHash("sha1").update(NFS.readFileSync(filePath)).digest("hex");
|
|
@@ -69764,7 +70096,6 @@ const isDiagnosticOnSurface = (diagnostic, surface, config) => {
|
|
|
69764
70096
|
return true;
|
|
69765
70097
|
};
|
|
69766
70098
|
const filterDiagnosticsForSurface = (diagnostics, surface, config) => diagnostics.filter((diagnostic) => isDiagnosticOnSurface(diagnostic, surface, config));
|
|
69767
|
-
const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
|
|
69768
70099
|
/**
|
|
69769
70100
|
* Budget for the dead-code phase, scaled to the work. deslop's graph build is
|
|
69770
70101
|
* CPU-bound and roughly linear in file count, so a fixed 120s cap is too tight
|
|
@@ -69772,25 +70103,28 @@ const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs
|
|
|
69772
70103
|
* tipped over by any concurrent load — silently dropping every dead-code
|
|
69773
70104
|
* finding. Scaling the budget with file count (and inversely with the core
|
|
69774
70105
|
* share when overlapped) lets the pass complete, while the ceiling still
|
|
69775
|
-
* reclaims a genuinely wedged worker.
|
|
69776
|
-
* and the Effect-side phase backstop that sits a margin above it.
|
|
70106
|
+
* reclaims a genuinely wedged worker.
|
|
69777
70107
|
*/
|
|
69778
70108
|
const resolveDeadCodeTimeout = (input) => {
|
|
69779
70109
|
const coreShareFactor = Math.max(1, input.fullConcurrency / Math.max(1, input.deadCodeConcurrency));
|
|
69780
|
-
|
|
69781
|
-
return {
|
|
69782
|
-
workerTimeoutMs,
|
|
69783
|
-
phaseTimeoutMs: workerTimeoutMs + DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS
|
|
69784
|
-
};
|
|
70110
|
+
return { workerTimeoutMs: Math.min(DEAD_CODE_TIMEOUT_CEILING_MS, Math.max(DEAD_CODE_WORKER_TIMEOUT_MS, Math.ceil(input.sourceFileCount * 30 * coreShareFactor))) };
|
|
69785
70111
|
};
|
|
69786
|
-
const resolveLintIncludePaths = (rootDirectory, userConfig) => {
|
|
70112
|
+
const resolveLintIncludePaths = (rootDirectory, userConfig, sourceFiles) => {
|
|
69787
70113
|
if (!Array.isArray(userConfig?.ignore?.files) || userConfig.ignore.files.length === 0) return;
|
|
69788
70114
|
const ignoredPatterns = compileIgnoredFilePatterns(userConfig);
|
|
69789
|
-
return listSourceFiles(rootDirectory).filter((filePath) => {
|
|
70115
|
+
return (sourceFiles ?? listSourceFiles(rootDirectory)).filter((filePath) => {
|
|
69790
70116
|
if (!isLintableSourceFile(filePath)) return false;
|
|
69791
70117
|
return !isFileIgnoredByPatterns(filePath, rootDirectory, ignoredPatterns);
|
|
69792
70118
|
});
|
|
69793
70119
|
};
|
|
70120
|
+
const filterPathsOutsideDirectories = (input) => {
|
|
70121
|
+
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}/`)));
|
|
70122
|
+
if (excludedPrefixes.length === 0) return [...input.relativePaths];
|
|
70123
|
+
return input.relativePaths.filter((relativePath) => {
|
|
70124
|
+
const normalizedRelativePath = relativePath.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
70125
|
+
return !excludedPrefixes.some((excludedPrefix) => normalizedRelativePath === excludedPrefix || normalizedRelativePath.startsWith(`${excludedPrefix}/`));
|
|
70126
|
+
});
|
|
70127
|
+
};
|
|
69794
70128
|
var Config = class Config extends Service()("react-doctor/Config") {
|
|
69795
70129
|
static layerNode = effect(Config, gen(function* () {
|
|
69796
70130
|
const cache = yield* make$3({
|
|
@@ -69852,6 +70186,7 @@ var Files = class Files extends Service()("react-doctor/Files") {
|
|
|
69852
70186
|
static layerNode = succeed$3(Files, Files.of({
|
|
69853
70187
|
readLines: (input) => sync(() => createNodeReadFileLinesSync(input.rootDirectory)(input.filePath)),
|
|
69854
70188
|
listSourceFiles: (rootDirectory) => sync(() => listSourceFiles(rootDirectory)),
|
|
70189
|
+
listSourceFilesCooperative: (input) => promise(() => listSourceFilesCooperative(input.rootDirectory, input.signal)),
|
|
69855
70190
|
isFile: (filePath) => sync(() => isFile(filePath)),
|
|
69856
70191
|
isDirectory: (filePath) => sync(() => isDirectory(filePath))
|
|
69857
70192
|
}));
|
|
@@ -69878,6 +70213,16 @@ var Files = class Files extends Service()("react-doctor/Files") {
|
|
|
69878
70213
|
}
|
|
69879
70214
|
return files;
|
|
69880
70215
|
}),
|
|
70216
|
+
listSourceFilesCooperative: (input) => sync(() => {
|
|
70217
|
+
const rootDirectory = input.rootDirectory;
|
|
70218
|
+
const prefix = rootDirectory.endsWith("/") ? rootDirectory : `${rootDirectory}/`;
|
|
70219
|
+
const files = [];
|
|
70220
|
+
for (const absolute of tree.keys()) {
|
|
70221
|
+
if (!absolute.startsWith(prefix)) continue;
|
|
70222
|
+
files.push(absolute.slice(prefix.length));
|
|
70223
|
+
}
|
|
70224
|
+
return files;
|
|
70225
|
+
}),
|
|
69881
70226
|
isFile: (filePath) => sync(() => tree.has(filePath)),
|
|
69882
70227
|
isDirectory: (filePath) => sync(() => {
|
|
69883
70228
|
const prefix = filePath.endsWith("/") ? filePath : `${filePath}/`;
|
|
@@ -70342,7 +70687,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
70342
70687
|
changedFiles: yield* mergeUntracked(directory, splitNullSeparated(diff.stdout), includeUntracked),
|
|
70343
70688
|
isCurrentChanges: false
|
|
70344
70689
|
};
|
|
70345
|
-
}).pipe(withSpan("Git.diffSelection")),
|
|
70690
|
+
}).pipe(catchReasons("ReactDoctorError", { GitInvocationFailed: () => succeed$2(null) }, (_reason, error) => fail$4(error)), withSpan("Git.diffSelection")),
|
|
70346
70691
|
stagedFilePaths: (directory) => runGit(directory, [
|
|
70347
70692
|
"diff",
|
|
70348
70693
|
"--no-ext-diff",
|
|
@@ -70453,7 +70798,7 @@ const batchIncludePaths = (baseArgs, includePaths) => {
|
|
|
70453
70798
|
for (const filePath of includePaths) {
|
|
70454
70799
|
const entryLength = filePath.length + 1;
|
|
70455
70800
|
const exceedsArgLength = currentBatch.length > 0 && currentBatchLength + entryLength > 24e3;
|
|
70456
|
-
const exceedsFileCount = currentBatch.length >=
|
|
70801
|
+
const exceedsFileCount = currentBatch.length >= 200;
|
|
70457
70802
|
if (exceedsArgLength || exceedsFileCount) {
|
|
70458
70803
|
batches.push(currentBatch);
|
|
70459
70804
|
currentBatch = [];
|
|
@@ -70558,6 +70903,33 @@ const detectUserLintConfigPaths = (rootDirectory) => {
|
|
|
70558
70903
|
return [];
|
|
70559
70904
|
};
|
|
70560
70905
|
const DISABLE_DIRECTIVE_PATTERN = /(eslint|oxlint)-disable/;
|
|
70906
|
+
const neutralizedFileLeases = /* @__PURE__ */ new Map();
|
|
70907
|
+
const activeNeutralizationInitializations = /* @__PURE__ */ new Set();
|
|
70908
|
+
const directoriesOverlap = (left, right) => {
|
|
70909
|
+
const isWithin = (parent, candidate) => {
|
|
70910
|
+
const relativePath = Path.relative(parent, candidate);
|
|
70911
|
+
return relativePath.length === 0 || !relativePath.startsWith(`..${Path.sep}`) && relativePath !== ".." && !Path.isAbsolute(relativePath);
|
|
70912
|
+
};
|
|
70913
|
+
return isWithin(left, right) || isWithin(right, left);
|
|
70914
|
+
};
|
|
70915
|
+
const withOverlappingInitializationLock = async (rootDirectory, operation) => {
|
|
70916
|
+
const blockers = [...activeNeutralizationInitializations].filter((initialization) => directoriesOverlap(initialization.rootDirectory, rootDirectory)).map((initialization) => initialization.done);
|
|
70917
|
+
let release;
|
|
70918
|
+
const initialization = {
|
|
70919
|
+
rootDirectory,
|
|
70920
|
+
done: new Promise((resolve) => {
|
|
70921
|
+
release = resolve;
|
|
70922
|
+
})
|
|
70923
|
+
};
|
|
70924
|
+
activeNeutralizationInitializations.add(initialization);
|
|
70925
|
+
await Promise.all(blockers);
|
|
70926
|
+
try {
|
|
70927
|
+
return await operation();
|
|
70928
|
+
} finally {
|
|
70929
|
+
activeNeutralizationInitializations.delete(initialization);
|
|
70930
|
+
release();
|
|
70931
|
+
}
|
|
70932
|
+
};
|
|
70561
70933
|
const findFilesWithDisableDirectivesViaGit = async (rootDirectory, includePaths) => {
|
|
70562
70934
|
const program = gen(function* () {
|
|
70563
70935
|
return yield* (yield* Git).grep({
|
|
@@ -70600,34 +70972,84 @@ const findFilesWithDisableDirectivesViaFilesystem = (rootDirectory, includePaths
|
|
|
70600
70972
|
};
|
|
70601
70973
|
const findFilesWithDisableDirectives = async (rootDirectory, includePaths) => await findFilesWithDisableDirectivesViaGit(rootDirectory, includePaths) ?? findFilesWithDisableDirectivesViaFilesystem(rootDirectory, includePaths);
|
|
70602
70974
|
const neutralizeContent = (content) => content.replaceAll("eslint-disable", "eslint_disable").replaceAll("oxlint-disable", "oxlint_disable");
|
|
70603
|
-
const
|
|
70604
|
-
const
|
|
70605
|
-
const
|
|
70975
|
+
const collectNeutralizationCandidatePaths = (rootDirectory, discoveredRelativePaths, includePaths) => {
|
|
70976
|
+
const resolvedRootDirectory = NFS.realpathSync(rootDirectory);
|
|
70977
|
+
const requestedPaths = includePaths && includePaths.length > 0 ? new Set(includePaths.flatMap((includePath) => {
|
|
70978
|
+
try {
|
|
70979
|
+
return [NFS.realpathSync(Path.resolve(resolvedRootDirectory, includePath))];
|
|
70980
|
+
} catch {
|
|
70981
|
+
return [];
|
|
70982
|
+
}
|
|
70983
|
+
})) : null;
|
|
70984
|
+
const candidatePaths = /* @__PURE__ */ new Set();
|
|
70985
|
+
for (const relativePath of discoveredRelativePaths) try {
|
|
70986
|
+
candidatePaths.add(NFS.realpathSync(Path.resolve(resolvedRootDirectory, relativePath)));
|
|
70987
|
+
} catch {
|
|
70988
|
+
continue;
|
|
70989
|
+
}
|
|
70990
|
+
for (const leasedPath of neutralizedFileLeases.keys()) {
|
|
70991
|
+
const relativePath = Path.relative(resolvedRootDirectory, leasedPath);
|
|
70992
|
+
if (!(relativePath.length === 0 || !relativePath.startsWith(`..${Path.sep}`) && relativePath !== ".." && !Path.isAbsolute(relativePath)) || requestedPaths && !requestedPaths.has(leasedPath)) continue;
|
|
70993
|
+
candidatePaths.add(leasedPath);
|
|
70994
|
+
}
|
|
70995
|
+
return candidatePaths;
|
|
70996
|
+
};
|
|
70997
|
+
const neutralizeDisableDirectives = async (rootDirectory, includePaths, options = {}) => {
|
|
70998
|
+
const resolvedRootDirectory = NFS.realpathSync(rootDirectory);
|
|
70999
|
+
const leasedPaths = /* @__PURE__ */ new Set();
|
|
70606
71000
|
let isRestored = false;
|
|
70607
71001
|
const restore = () => {
|
|
70608
71002
|
if (isRestored) return;
|
|
70609
71003
|
isRestored = true;
|
|
70610
|
-
for (const
|
|
70611
|
-
|
|
70612
|
-
|
|
70613
|
-
|
|
71004
|
+
for (const absolutePath of leasedPaths) {
|
|
71005
|
+
const lease = neutralizedFileLeases.get(absolutePath);
|
|
71006
|
+
if (!lease) continue;
|
|
71007
|
+
lease.referenceCount -= 1;
|
|
71008
|
+
if (lease.referenceCount > 0) continue;
|
|
71009
|
+
neutralizedFileLeases.delete(absolutePath);
|
|
71010
|
+
try {
|
|
71011
|
+
NFS.writeFileSync(absolutePath, lease.originalContent);
|
|
71012
|
+
} catch (error) {
|
|
71013
|
+
process.stderr.write(`[react-doctor] Failed to restore inline disable directives in ${absolutePath}: ${messageFromUnknown(error)}\n[react-doctor] Run: git checkout -- ${absolutePath}\n`);
|
|
71014
|
+
}
|
|
70614
71015
|
}
|
|
70615
71016
|
};
|
|
70616
71017
|
const onExit = () => restore();
|
|
70617
71018
|
process.once("exit", onExit);
|
|
70618
|
-
|
|
70619
|
-
|
|
71019
|
+
const leaseCandidatePath = (absolutePath) => {
|
|
71020
|
+
if (leasedPaths.has(absolutePath)) return;
|
|
71021
|
+
const existingLease = neutralizedFileLeases.get(absolutePath);
|
|
71022
|
+
if (existingLease) {
|
|
71023
|
+
existingLease.referenceCount += 1;
|
|
71024
|
+
leasedPaths.add(absolutePath);
|
|
71025
|
+
return;
|
|
71026
|
+
}
|
|
70620
71027
|
let originalContent;
|
|
70621
71028
|
try {
|
|
70622
71029
|
originalContent = NFS.readFileSync(absolutePath, "utf-8");
|
|
70623
71030
|
} catch {
|
|
70624
|
-
|
|
71031
|
+
return;
|
|
70625
71032
|
}
|
|
70626
71033
|
const neutralizedContent = neutralizeContent(originalContent);
|
|
70627
71034
|
if (neutralizedContent !== originalContent) {
|
|
70628
|
-
originalContents.set(absolutePath, originalContent);
|
|
70629
71035
|
NFS.writeFileSync(absolutePath, neutralizedContent);
|
|
71036
|
+
neutralizedFileLeases.set(absolutePath, {
|
|
71037
|
+
originalContent,
|
|
71038
|
+
referenceCount: 1
|
|
71039
|
+
});
|
|
71040
|
+
leasedPaths.add(absolutePath);
|
|
70630
71041
|
}
|
|
71042
|
+
};
|
|
71043
|
+
try {
|
|
71044
|
+
await withOverlappingInitializationLock(resolvedRootDirectory, async () => {
|
|
71045
|
+
for (const absolutePath of collectNeutralizationCandidatePaths(resolvedRootDirectory, [], includePaths)) if (neutralizedFileLeases.has(absolutePath)) leaseCandidatePath(absolutePath);
|
|
71046
|
+
const discoveredRelativePaths = await (options.findFiles ?? findFilesWithDisableDirectives)(resolvedRootDirectory, includePaths);
|
|
71047
|
+
for (const absolutePath of collectNeutralizationCandidatePaths(resolvedRootDirectory, discoveredRelativePaths, includePaths)) leaseCandidatePath(absolutePath);
|
|
71048
|
+
});
|
|
71049
|
+
} catch (error) {
|
|
71050
|
+
restore();
|
|
71051
|
+
process.removeListener("exit", onExit);
|
|
71052
|
+
throw error;
|
|
70631
71053
|
}
|
|
70632
71054
|
return () => {
|
|
70633
71055
|
restore();
|
|
@@ -70800,7 +71222,7 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
70800
71222
|
}
|
|
70801
71223
|
return enabled;
|
|
70802
71224
|
};
|
|
70803
|
-
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), includedTags = /* @__PURE__ */ new Set(), includeTagDefaults = false, runtimeGlobals, unpluginAutoImportGlobalScopes, serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection, sidecarRuleIdFilter }) => {
|
|
71225
|
+
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), includedTags = /* @__PURE__ */ new Set(), includeTagDefaults = false, runtimeGlobals, unpluginAutoImportGlobalScopes, serverAuthFunctionNames, projectIndexModuleSources, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection, sidecarRuleIdFilter }) => {
|
|
70804
71226
|
const hasIncludedTags = includedTags.size > 0;
|
|
70805
71227
|
const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" || hasIncludedTags ? null : resolveReactHooksJsPlugin(project.hasReactCompiler || project.hasReactCompilerLintPlugin === true, customRulesOnly);
|
|
70806
71228
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
@@ -70864,7 +71286,8 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
70864
71286
|
}))
|
|
70865
71287
|
} : {},
|
|
70866
71288
|
...project.shopifyFlashListMajorVersion !== null ? { shopifyFlashListMajorVersion: project.shopifyFlashListMajorVersion } : {},
|
|
70867
|
-
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {}
|
|
71289
|
+
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {},
|
|
71290
|
+
...projectIndexModuleSources !== void 0 ? { projectIndexModuleSources: [...projectIndexModuleSources] } : {}
|
|
70868
71291
|
} },
|
|
70869
71292
|
rules: {
|
|
70870
71293
|
...reactCompilerRules,
|
|
@@ -71113,11 +71536,17 @@ const collectUnpluginAutoImportGlobalScopes = ({ rootDirectory, candidateFiles }
|
|
|
71113
71536
|
return scopes.some((scope) => scope.names.length > 0) ? scopes : [];
|
|
71114
71537
|
};
|
|
71115
71538
|
const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
|
|
71539
|
+
const decodeDiagnostic = (raw) => {
|
|
71540
|
+
const diagnostic = validateDiagnostic$1(raw);
|
|
71541
|
+
return {
|
|
71542
|
+
...diagnostic,
|
|
71543
|
+
relatedLocations: diagnostic.relatedLocations?.map((location) => ({ ...location }))
|
|
71544
|
+
};
|
|
71545
|
+
};
|
|
71116
71546
|
const decodeFileDiagnostics = (raw) => {
|
|
71117
71547
|
if (!Array.isArray(raw)) return null;
|
|
71118
71548
|
try {
|
|
71119
|
-
|
|
71120
|
-
return raw;
|
|
71549
|
+
return raw.map((entry) => decodeDiagnostic(entry));
|
|
71121
71550
|
} catch {
|
|
71122
71551
|
return null;
|
|
71123
71552
|
}
|
|
@@ -72349,6 +72778,12 @@ const buildProfiledNodeArguments = (input) => [
|
|
|
72349
72778
|
...input.heapProfileDirectory ? ["--heap-prof", `--heap-prof-dir=${input.heapProfileDirectory}`] : [],
|
|
72350
72779
|
...input.argumentsList
|
|
72351
72780
|
];
|
|
72781
|
+
const lowerChildProcessPriority = (processId) => {
|
|
72782
|
+
if (processId === void 0) return;
|
|
72783
|
+
try {
|
|
72784
|
+
OS.setPriority(processId, OS.constants.priority.PRIORITY_BELOW_NORMAL);
|
|
72785
|
+
} catch {}
|
|
72786
|
+
};
|
|
72352
72787
|
const SANITIZED_ENV = buildOxlintChildEnv(process.env);
|
|
72353
72788
|
/**
|
|
72354
72789
|
* Spawn one oxlint subprocess with hard ceilings on wall time and
|
|
@@ -72387,6 +72822,7 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
|
|
|
72387
72822
|
"pipe"
|
|
72388
72823
|
]
|
|
72389
72824
|
});
|
|
72825
|
+
lowerChildProcessPriority(child.pid);
|
|
72390
72826
|
const onAbort = () => {
|
|
72391
72827
|
child.kill("SIGKILL");
|
|
72392
72828
|
reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) }));
|
|
@@ -72509,7 +72945,7 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
72509
72945
|
* loop with a slimmer config in that case.
|
|
72510
72946
|
*/
|
|
72511
72947
|
const spawnLintBatches = async (input) => {
|
|
72512
|
-
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, sourceMapByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth =
|
|
72948
|
+
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, sourceMapByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 9, signal, deadlineEpochMs } = input;
|
|
72513
72949
|
const resolveSourcePath = (filePath) => {
|
|
72514
72950
|
const absoluteFilePath = Path.isAbsolute(filePath) ? filePath : Path.resolve(rootDirectory, filePath);
|
|
72515
72951
|
return sourcePathByLintPath?.get(Path.normalize(absoluteFilePath)) ?? filePath;
|
|
@@ -72517,6 +72953,12 @@ const spawnLintBatches = async (input) => {
|
|
|
72517
72953
|
const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
|
|
72518
72954
|
let rescueDeadlineEpochMs;
|
|
72519
72955
|
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0 || rescueDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(rescueDeadlineEpochMs) === 0;
|
|
72956
|
+
const resolveSpawnTimeoutMs = () => {
|
|
72957
|
+
const deadlineBudgets = [deadlineEpochMs, rescueDeadlineEpochMs].filter((deadline) => deadline !== void 0).map(remainingDeadlineBudgetMs);
|
|
72958
|
+
if (deadlineBudgets.length === 0) return spawnTimeoutMs;
|
|
72959
|
+
const remainingDeadlineMs = Math.min(...deadlineBudgets);
|
|
72960
|
+
return spawnTimeoutMs === void 0 ? remainingDeadlineMs : Math.min(spawnTimeoutMs, remainingDeadlineMs);
|
|
72961
|
+
};
|
|
72520
72962
|
const runBatchPass = async (concurrency, passBatches, passOnFileProgress) => {
|
|
72521
72963
|
const totalFileCount = passBatches.reduce((sum, batch) => sum + batch.length, 0);
|
|
72522
72964
|
const allDiagnostics = [];
|
|
@@ -72534,12 +72976,13 @@ const spawnLintBatches = async (input) => {
|
|
|
72534
72976
|
const batchArgs = [...baseArgs, ...batch];
|
|
72535
72977
|
try {
|
|
72536
72978
|
const spawnBatch = () => {
|
|
72537
|
-
|
|
72979
|
+
const effectiveSpawnTimeoutMs = resolveSpawnTimeoutMs();
|
|
72980
|
+
if (effectiveSpawnTimeoutMs === 0) {
|
|
72538
72981
|
deadlineSkippedFiles.push(...batch);
|
|
72539
72982
|
batchState.deadlineSkippedFileCount += batch.length;
|
|
72540
72983
|
return Promise.resolve(null);
|
|
72541
72984
|
}
|
|
72542
|
-
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath,
|
|
72985
|
+
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, effectiveSpawnTimeoutMs, outputMaxBytes, signal, () => {
|
|
72543
72986
|
if (batchState.didStart) return;
|
|
72544
72987
|
batchState.didStart = true;
|
|
72545
72988
|
startedFileCount += batchState.initialFileCount;
|
|
@@ -72694,11 +73137,36 @@ const validateRuleRegistration = () => {
|
|
|
72694
73137
|
].filter((entry) => entry !== null).join("; ");
|
|
72695
73138
|
console.warn(`[react-doctor] rule-registration drift: ${detail}`);
|
|
72696
73139
|
};
|
|
73140
|
+
const PROJECT_INDEX_MODULE_SOURCES = [
|
|
73141
|
+
"next/og",
|
|
73142
|
+
"@vercel/og",
|
|
73143
|
+
"satori",
|
|
73144
|
+
"remotion"
|
|
73145
|
+
];
|
|
73146
|
+
const collectProjectIndexModuleSources = async (rootDirectory, candidateFiles) => {
|
|
73147
|
+
const foundModuleSources = /* @__PURE__ */ new Set();
|
|
73148
|
+
let sliceStartedAt = performance.now();
|
|
73149
|
+
for (const candidateFile of candidateFiles) {
|
|
73150
|
+
let sourceBuffer;
|
|
73151
|
+
try {
|
|
73152
|
+
sourceBuffer = NFS.readFileSync(Path.isAbsolute(candidateFile) ? candidateFile : Path.resolve(rootDirectory, candidateFile));
|
|
73153
|
+
} catch {
|
|
73154
|
+
continue;
|
|
73155
|
+
}
|
|
73156
|
+
for (const moduleSource of PROJECT_INDEX_MODULE_SOURCES) if (sourceBuffer.includes(`"${moduleSource}"`) || sourceBuffer.includes(`'${moduleSource}'`)) foundModuleSources.add(moduleSource);
|
|
73157
|
+
if (foundModuleSources.size === PROJECT_INDEX_MODULE_SOURCES.length) break;
|
|
73158
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
73159
|
+
await yieldToEventLoop();
|
|
73160
|
+
sliceStartedAt = performance.now();
|
|
73161
|
+
}
|
|
73162
|
+
}
|
|
73163
|
+
return [...foundModuleSources].sort();
|
|
73164
|
+
};
|
|
72697
73165
|
/**
|
|
72698
73166
|
* Balanced LPT (longest-processing-time-first) lint batch planner. Where
|
|
72699
|
-
* `batchIncludePaths` greedily fills
|
|
72700
|
-
* remainder batch
|
|
72701
|
-
*
|
|
73167
|
+
* `batchIncludePaths` greedily fills fixed-size batches and leaves a small
|
|
73168
|
+
* remainder batch, so the parallel pool's wall clock is set by whichever
|
|
73169
|
+
* full chunk happened to collect the most work while
|
|
72702
73170
|
* the remainder's worker idles), this planner keeps the SAME mandatory batch
|
|
72703
73171
|
* count — `ceil(files / OXLINT_MAX_FILES_PER_BATCH)`, the JS-plugin
|
|
72704
73172
|
* perf-cliff / native-binding SIGABRT guard — and assigns files largest-first,
|
|
@@ -72727,7 +73195,7 @@ const validateRuleRegistration = () => {
|
|
|
72727
73195
|
*/
|
|
72728
73196
|
const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
72729
73197
|
if (files.length === 0) return [];
|
|
72730
|
-
const batchCount = Math.ceil(files.length /
|
|
73198
|
+
const batchCount = Math.ceil(files.length / 200);
|
|
72731
73199
|
const maxFilesPerBatch = Math.ceil(files.length / batchCount);
|
|
72732
73200
|
const baseArgsLengthChars = estimateArgsLength(baseArgs);
|
|
72733
73201
|
const descendingSizeIndexes = [...files.keys()].sort((leftIndex, rightIndex) => (sizeByFile.get(files[rightIndex]) ?? 0) - (sizeByFile.get(files[leftIndex]) ?? 0));
|
|
@@ -73256,6 +73724,7 @@ const runOxlint = async (options) => {
|
|
|
73256
73724
|
await yieldToEventLoop();
|
|
73257
73725
|
const sizedScanFiles = includePaths === void 0 ? listSourceFilesWithSize(rootDirectory) : null;
|
|
73258
73726
|
const candidateFiles = includePaths !== void 0 ? includePaths : (sizedScanFiles ?? []).map((entry) => entry.path);
|
|
73727
|
+
const projectIndexModuleSources = includePaths === void 0 && options.deadlineEpochMs === void 0 ? await collectProjectIndexModuleSources(rootDirectory, candidateFiles) : void 0;
|
|
73259
73728
|
unpluginAutoImportGlobalScopes = collectUnpluginAutoImportGlobalScopes({
|
|
73260
73729
|
rootDirectory,
|
|
73261
73730
|
candidateFiles
|
|
@@ -73278,6 +73747,7 @@ const runOxlint = async (options) => {
|
|
|
73278
73747
|
runtimeGlobals,
|
|
73279
73748
|
unpluginAutoImportGlobalScopes,
|
|
73280
73749
|
serverAuthFunctionNames,
|
|
73750
|
+
projectIndexModuleSources,
|
|
73281
73751
|
severityControls,
|
|
73282
73752
|
userPlugins,
|
|
73283
73753
|
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
|
|
@@ -73745,10 +74215,10 @@ const translateProjectInfoError = (cause, directory) => {
|
|
|
73745
74215
|
}) });
|
|
73746
74216
|
};
|
|
73747
74217
|
var Project = class Project extends Service()("react-doctor/Project") {
|
|
73748
|
-
static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (
|
|
74218
|
+
static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (input) {
|
|
73749
74219
|
return yield* try_({
|
|
73750
|
-
try: () => discoverProject(directory),
|
|
73751
|
-
catch: (cause) => translateProjectInfoError(cause, directory)
|
|
74220
|
+
try: () => discoverProject(input.directory, { sourceFileCount: input.sourceFileCount }),
|
|
74221
|
+
catch: (cause) => translateProjectInfoError(cause, input.directory)
|
|
73752
74222
|
});
|
|
73753
74223
|
}) }));
|
|
73754
74224
|
static layerOf = (projectInfo) => succeed$3(Project, Project.of({ discover: () => succeed$2(projectInfo) }));
|
|
@@ -74333,9 +74803,10 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
74333
74803
|
* outage.
|
|
74334
74804
|
*/
|
|
74335
74805
|
var SupplyChain = class SupplyChain extends Service()("react-doctor/SupplyChain") {
|
|
74336
|
-
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (
|
|
74337
|
-
|
|
74338
|
-
|
|
74806
|
+
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (configuredTimeoutMs) => checkSupplyChain({
|
|
74807
|
+
rootDirectory: input.rootDirectory,
|
|
74808
|
+
userConfig: input.userConfig,
|
|
74809
|
+
totalTimeoutMs: input.timeoutMs ?? configuredTimeoutMs
|
|
74339
74810
|
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), withSpan("SupplyChain.run"))) }));
|
|
74340
74811
|
static layerOf = (diagnostics) => succeed$3(SupplyChain, SupplyChain.of({ run: () => fromIterable$1(diagnostics) }));
|
|
74341
74812
|
};
|
|
@@ -74370,10 +74841,6 @@ const NO_HOOKS = {
|
|
|
74370
74841
|
beforeLint: () => void_,
|
|
74371
74842
|
afterLint: () => void_
|
|
74372
74843
|
};
|
|
74373
|
-
const filterMapNullable = (transform) => fromPredicateOption((value) => {
|
|
74374
|
-
const result = transform(value);
|
|
74375
|
-
return result === null ? none() : some(result);
|
|
74376
|
-
});
|
|
74377
74844
|
const fileReader = (filesService, rootDirectory) => (filePath) => {
|
|
74378
74845
|
const lines = runSync(filesService.readLines({
|
|
74379
74846
|
filePath,
|
|
@@ -74397,7 +74864,7 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
74397
74864
|
* The GitHub viewer-permission lookup is forked onto a background
|
|
74398
74865
|
* fiber here and joined late (it feeds score metadata, not
|
|
74399
74866
|
* diagnostics).
|
|
74400
|
-
* 2. beforeLint hook (e.g. CLI
|
|
74867
|
+
* 2. beforeLint hook (e.g. CLI records project telemetry)
|
|
74401
74868
|
* 3. environment checks (reduced-motion + pnpm hardening +
|
|
74402
74869
|
* expo/react-native), collected synchronously. The heavier
|
|
74403
74870
|
* content-regex security scan is forked instead (like supply-chain
|
|
@@ -74410,13 +74877,11 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
74410
74877
|
* `SupplyChainOverlapTimeoutMs` (measured from fork) so a hung
|
|
74411
74878
|
* socket can't drag out its join; on timeout it fails open to no
|
|
74412
74879
|
* diagnostics — the same outcome class as a Socket outage.
|
|
74413
|
-
* 5. Linter.run runs; DeadCode.run
|
|
74414
|
-
*
|
|
74415
|
-
*
|
|
74416
|
-
*
|
|
74417
|
-
*
|
|
74418
|
-
* fiber is joined (or interrupted, SIGKILLing its worker, on lint
|
|
74419
|
-
* failure) before diagnostics are concatenated. The afterLint hook
|
|
74880
|
+
* 5. Linter.run runs; DeadCode.run forks only when
|
|
74881
|
+
* REACT_DOCTOR_DEAD_CODE_OVERLAP=on. The default "auto" and explicit
|
|
74882
|
+
* "off" paths stay sequential. The fiber is joined (or interrupted,
|
|
74883
|
+
* SIGKILLing its worker, on lint failure) before diagnostics are
|
|
74884
|
+
* concatenated. The afterLint hook
|
|
74420
74885
|
* fires between lint and dead-code. Progress spinner labels AND the
|
|
74421
74886
|
* final diagnostic / score order stay independent of execution
|
|
74422
74887
|
* order, so terminal output is identical either way; supply-chain
|
|
@@ -74451,8 +74916,28 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74451
74916
|
const partialFailuresRef = yield* LintPartialFailures;
|
|
74452
74917
|
const resolvedConfig = yield* configService.resolve(input.directory);
|
|
74453
74918
|
const scanDirectory = resolvedConfig.resolvedDirectory;
|
|
74454
|
-
const
|
|
74919
|
+
const excludedProjectDirectories = (input.excludedProjectDirectories ?? []).map((excludedDirectory) => Path.resolve(excludedDirectory)).filter((excludedDirectory) => isPathInsideDirectory(excludedDirectory, scanDirectory));
|
|
74920
|
+
const excludedProjectRelativePaths = excludedProjectDirectories.map((excludedDirectory) => Path.relative(scanDirectory, excludedDirectory).replaceAll("\\", "/"));
|
|
74921
|
+
const isExcludedProjectDiagnostic = (diagnostic) => {
|
|
74922
|
+
const relativeFilePath = toNormalizedRelativePath(diagnostic.filePath, scanDirectory);
|
|
74923
|
+
return excludedProjectRelativePaths.some((excludedRelativePath) => relativeFilePath === excludedRelativePath || relativeFilePath.startsWith(`${excludedRelativePath}/`));
|
|
74924
|
+
};
|
|
74925
|
+
const precomputedSourceFiles = input.suppressScanSummary === true || excludedProjectDirectories.length > 0 ? yield* filesService.listSourceFilesCooperative({
|
|
74926
|
+
rootDirectory: scanDirectory,
|
|
74927
|
+
signal: input.signal
|
|
74928
|
+
}) : null;
|
|
74929
|
+
const includedPrecomputedSourceFiles = precomputedSourceFiles !== null && excludedProjectDirectories.length > 0 ? filterPathsOutsideDirectories({
|
|
74930
|
+
rootDirectory: scanDirectory,
|
|
74931
|
+
relativePaths: precomputedSourceFiles,
|
|
74932
|
+
excludedDirectories: excludedProjectDirectories
|
|
74933
|
+
}) : precomputedSourceFiles;
|
|
74934
|
+
const sourceFileCount = excludedProjectDirectories.length > 0 ? includedPrecomputedSourceFiles?.length : input.precomputedSourceFileCount ?? includedPrecomputedSourceFiles?.length;
|
|
74935
|
+
const project = yield* projectService.discover({
|
|
74936
|
+
directory: scanDirectory,
|
|
74937
|
+
sourceFileCount
|
|
74938
|
+
});
|
|
74455
74939
|
if (!isAnalyzableProject(project)) return yield* new ReactDoctorError({ reason: new NoReactDependency({ directory: scanDirectory }) });
|
|
74940
|
+
const deadCodeSourceFileCount = input.retainExcludedProjectDeadCodeDiagnostics === true && precomputedSourceFiles !== null ? precomputedSourceFiles.length : project.sourceFileCount;
|
|
74456
74941
|
const [repo, sha, defaultBranch] = yield* all([
|
|
74457
74942
|
gitService.githubRepo(scanDirectory).pipe(orElseSucceed(() => null)),
|
|
74458
74943
|
gitService.headSha(scanDirectory).pipe(orElseSucceed(() => null)),
|
|
@@ -74463,11 +74948,16 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74463
74948
|
directory: scanDirectory,
|
|
74464
74949
|
repo
|
|
74465
74950
|
}).pipe(orElseSucceed(() => null)) : succeed$2(null));
|
|
74466
|
-
|
|
74467
|
-
|
|
74951
|
+
let lintIncludePaths = (input.skipExplicitIncludePathFilter ? input.includePaths.length > 0 ? [...input.includePaths] : void 0 : computeExplicitLintIncludePaths([...input.includePaths])) ?? resolveLintIncludePaths(scanDirectory, resolvedConfig.config, includedPrecomputedSourceFiles ?? void 0);
|
|
74952
|
+
if (excludedProjectDirectories.length > 0) lintIncludePaths = filterPathsOutsideDirectories({
|
|
74953
|
+
rootDirectory: scanDirectory,
|
|
74954
|
+
relativePaths: lintIncludePaths ?? includedPrecomputedSourceFiles ?? (yield* filesService.listSourceFiles(scanDirectory)),
|
|
74955
|
+
excludedDirectories: excludedProjectDirectories
|
|
74956
|
+
});
|
|
74957
|
+
const fallbackScannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? includedPrecomputedSourceFiles ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
|
|
74468
74958
|
const beforeLint = hooks.beforeLint ?? NO_HOOKS.beforeLint;
|
|
74469
74959
|
const afterLint = hooks.afterLint ?? NO_HOOKS.afterLint;
|
|
74470
|
-
yield* beforeLint(project
|
|
74960
|
+
yield* beforeLint(project);
|
|
74471
74961
|
const isDiffMode = input.includePaths.length > 0;
|
|
74472
74962
|
const showWarnings = input.warnings ?? resolvedConfig.config?.warnings ?? true;
|
|
74473
74963
|
const transform = buildDiagnosticPipeline({
|
|
@@ -74477,7 +74967,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74477
74967
|
respectInlineDisables: input.respectInlineDisables,
|
|
74478
74968
|
showWarnings
|
|
74479
74969
|
});
|
|
74480
|
-
const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(
|
|
74970
|
+
const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(fromPredicateOption((diagnostic) => {
|
|
74971
|
+
const filteredDiagnostic = transform.apply(diagnostic);
|
|
74972
|
+
return filteredDiagnostic === null ? none() : some(filteredDiagnostic);
|
|
74973
|
+
})));
|
|
74481
74974
|
const applyPerElementPipeline = (rawStream) => filterPerElementPipeline(rawStream).pipe(tap((diagnostic) => reporterService.emit(diagnostic)));
|
|
74482
74975
|
const envCollected = yield* runCollect(applyPerElementPipeline(fromIterable$1(isDiffMode ? [] : [
|
|
74483
74976
|
...checkReducedMotion(scanDirectory),
|
|
@@ -74487,17 +74980,31 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74487
74980
|
...checkReactNativeProject(scanDirectory, project)
|
|
74488
74981
|
])));
|
|
74489
74982
|
const securityScanFailedRef = yield* make$13(false);
|
|
74490
|
-
|
|
74983
|
+
let didSecurityScanReachDeadline = false;
|
|
74984
|
+
const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(tryPromise((signal) => checkSecurityScanCooperative(scanDirectory, {
|
|
74491
74985
|
project,
|
|
74492
74986
|
ignoredTags: input.ignoredTags,
|
|
74493
74987
|
includedTags: input.includedTags,
|
|
74494
|
-
includeTagDefaults: input.includeTagDefaults
|
|
74988
|
+
includeTagDefaults: input.includeTagDefaults,
|
|
74989
|
+
excludedDirectories: new Set(excludedProjectDirectories),
|
|
74990
|
+
deadlineEpochMs: input.deadlineEpochMs,
|
|
74991
|
+
signal,
|
|
74992
|
+
onDeadlineExceeded: () => {
|
|
74993
|
+
didSecurityScanReachDeadline = true;
|
|
74994
|
+
}
|
|
74495
74995
|
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), catch_$1(() => set(securityScanFailedRef, true).pipe(as(empty$4))))))).pipe(withSpan("SecurityScan.run")));
|
|
74996
|
+
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
74997
|
+
const capOptionalToDeadline = (phaseTimeoutMs) => {
|
|
74998
|
+
if (input.deadlineEpochMs === void 0) return phaseTimeoutMs;
|
|
74999
|
+
const remainingBudgetMs = remainingDeadlineBudgetMs(input.deadlineEpochMs);
|
|
75000
|
+
return phaseTimeoutMs === null ? remainingBudgetMs : Math.min(phaseTimeoutMs, remainingBudgetMs);
|
|
75001
|
+
};
|
|
74496
75002
|
const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
|
|
74497
|
-
const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
|
|
75003
|
+
const supplyChainOverlapTimeout = capToDeadline(yield* SupplyChainOverlapTimeoutMs);
|
|
74498
75004
|
const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
|
|
74499
75005
|
rootDirectory: scanDirectory,
|
|
74500
|
-
userConfig: resolvedConfig.config
|
|
75006
|
+
userConfig: resolvedConfig.config,
|
|
75007
|
+
timeoutMs: supplyChainOverlapTimeout
|
|
74501
75008
|
}))).pipe(map$3((diagnostics) => ({
|
|
74502
75009
|
diagnostics,
|
|
74503
75010
|
timedOut: false
|
|
@@ -74521,46 +75028,49 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74521
75028
|
const scanConcurrency = resolveScanConcurrency(yield* OxlintConcurrency);
|
|
74522
75029
|
const lintPhaseTimeoutMs = yield* LintPhaseTimeoutMs;
|
|
74523
75030
|
const deadCodePhaseTimeoutMs = yield* DeadCodePhaseTimeoutMs;
|
|
74524
|
-
const resolveDeadCodePhaseTimeoutMs = (scaledPhaseTimeoutMs) => deadCodePhaseTimeoutMs === 15e4 ? scaledPhaseTimeoutMs : deadCodePhaseTimeoutMs;
|
|
74525
75031
|
const workerCountSuffix = scanConcurrency > 1 ? ` ${highlighter.dim(`[~${scanConcurrency} workers]`)}` : "";
|
|
74526
|
-
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
74527
75032
|
const shouldRunDeadCode = input.runDeadCode && !isDiffMode && (showWarnings || deadCodeMaySurfaceWhenWarningsHidden(resolvedConfig.config));
|
|
74528
75033
|
const deadCodeOverlapMode = yield* DeadCodeOverlap;
|
|
74529
75034
|
const shouldOverlapDeadCode = shouldRunDeadCode && deadCodeOverlapMode === "on";
|
|
74530
75035
|
const deadCodeParseConcurrency = shouldOverlapDeadCode ? Math.max(1, Math.floor(scanConcurrency * DEAD_CODE_OVERLAP_PARSE_SHARE)) : void 0;
|
|
74531
75036
|
const lintConcurrency = deadCodeParseConcurrency === void 0 ? scanConcurrency : Math.max(1, scanConcurrency - deadCodeParseConcurrency);
|
|
74532
|
-
const buildCollectDeadCode = (deadCodeTimeout) =>
|
|
74533
|
-
|
|
74534
|
-
|
|
74535
|
-
|
|
74536
|
-
|
|
74537
|
-
|
|
74538
|
-
|
|
74539
|
-
|
|
74540
|
-
|
|
74541
|
-
|
|
74542
|
-
|
|
74543
|
-
|
|
74544
|
-
|
|
74545
|
-
|
|
74546
|
-
|
|
74547
|
-
|
|
74548
|
-
|
|
74549
|
-
|
|
74550
|
-
|
|
74551
|
-
|
|
74552
|
-
|
|
74553
|
-
|
|
74554
|
-
|
|
74555
|
-
|
|
75037
|
+
const buildCollectDeadCode = (deadCodeTimeout) => {
|
|
75038
|
+
const collectDeadCode = runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
75039
|
+
rootDirectory: scanDirectory,
|
|
75040
|
+
parseConcurrency: deadCodeParseConcurrency,
|
|
75041
|
+
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
|
|
75042
|
+
onCacheOutcome: (didHitCache) => {
|
|
75043
|
+
deadCodeCacheHit = didHitCache;
|
|
75044
|
+
},
|
|
75045
|
+
onSummaryCacheStats: (stats) => {
|
|
75046
|
+
deadCodeSummaryCacheHits = stats.hits;
|
|
75047
|
+
deadCodeSummaryCacheMisses = stats.misses;
|
|
75048
|
+
}
|
|
75049
|
+
}).pipe(filter((diagnostic) => input.retainExcludedProjectDeadCodeDiagnostics === true || !isExcludedProjectDiagnostic(diagnostic)), catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
75050
|
+
yield* set(deadCodeFailure, {
|
|
75051
|
+
didFail: true,
|
|
75052
|
+
reason: error.message
|
|
75053
|
+
});
|
|
75054
|
+
return empty$4;
|
|
75055
|
+
}))))));
|
|
75056
|
+
const phaseTimeoutMs = deadCodeTimeout.phaseTimeoutMs;
|
|
75057
|
+
if (phaseTimeoutMs === null) return collectDeadCode;
|
|
75058
|
+
return collectDeadCode.pipe(timeoutOption(phaseTimeoutMs), flatMap$2(match$3({
|
|
75059
|
+
onNone: () => set(deadCodeFailure, {
|
|
75060
|
+
didFail: true,
|
|
75061
|
+
reason: `Dead-code analysis exceeded ${Math.round(phaseTimeoutMs / MILLISECONDS_PER_SECOND)}s and was skipped.`
|
|
75062
|
+
}).pipe(as([])),
|
|
75063
|
+
onSome: succeed$2
|
|
75064
|
+
})));
|
|
75065
|
+
};
|
|
74556
75066
|
const overlapDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
74557
|
-
sourceFileCount:
|
|
75067
|
+
sourceFileCount: deadCodeSourceFileCount,
|
|
74558
75068
|
deadCodeConcurrency: deadCodeParseConcurrency ?? scanConcurrency,
|
|
74559
75069
|
fullConcurrency: scanConcurrency
|
|
74560
75070
|
});
|
|
74561
75071
|
const deadCodeFiber = shouldOverlapDeadCode ? yield* forkChild(buildCollectDeadCode({
|
|
74562
75072
|
workerTimeoutMs: overlapDeadCodeTimeout.workerTimeoutMs,
|
|
74563
|
-
phaseTimeoutMs:
|
|
75073
|
+
phaseTimeoutMs: capOptionalToDeadline(deadCodePhaseTimeoutMs)
|
|
74564
75074
|
})) : null;
|
|
74565
75075
|
const scanProgress = yield* progressService.start("Scanning...");
|
|
74566
75076
|
const scanStartTime = Date.now();
|
|
@@ -74611,7 +75121,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74611
75121
|
});
|
|
74612
75122
|
return empty$4;
|
|
74613
75123
|
}))));
|
|
74614
|
-
const
|
|
75124
|
+
const collectLintDiagnostics = runCollect(filterPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream));
|
|
75125
|
+
const lintCollected = dedupeRelatedDiagnostics(yield* lintPhaseTimeoutMs === null ? collectLintDiagnostics : collectLintDiagnostics.pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
|
|
74615
75126
|
onNone: () => set(lintFailure, {
|
|
74616
75127
|
didFail: true,
|
|
74617
75128
|
reason: `Lint analysis exceeded ${lintPhaseTimeoutMs / MILLISECONDS_PER_SECOND}s and was skipped.`,
|
|
@@ -74641,13 +75152,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74641
75152
|
} else {
|
|
74642
75153
|
yield* scanProgress.update(`Scanned ${scannedFilesLabel}, analyzing dead code...`);
|
|
74643
75154
|
const sequentialDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
74644
|
-
sourceFileCount: totalFileCount,
|
|
75155
|
+
sourceFileCount: input.retainExcludedProjectDeadCodeDiagnostics === true ? deadCodeSourceFileCount : totalFileCount,
|
|
74645
75156
|
deadCodeConcurrency: scanConcurrency,
|
|
74646
75157
|
fullConcurrency: scanConcurrency
|
|
74647
75158
|
});
|
|
74648
75159
|
deadCodeCollected = deadCodeFiber !== null ? yield* join(deadCodeFiber) : yield* buildCollectDeadCode({
|
|
74649
75160
|
workerTimeoutMs: sequentialDeadCodeTimeout.workerTimeoutMs,
|
|
74650
|
-
phaseTimeoutMs:
|
|
75161
|
+
phaseTimeoutMs: capOptionalToDeadline(deadCodePhaseTimeoutMs)
|
|
74651
75162
|
});
|
|
74652
75163
|
}
|
|
74653
75164
|
const deadCodeFailureState = lintFailureState.didFail ? {
|
|
@@ -74691,7 +75202,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74691
75202
|
metadata: scoreMetadata
|
|
74692
75203
|
});
|
|
74693
75204
|
const lintPartialFailures = yield* get$2(partialFailuresRef);
|
|
74694
|
-
const
|
|
75205
|
+
const didSecurityScanFail = yield* get$2(securityScanFailedRef);
|
|
75206
|
+
const securityScanFailed = didSecurityScanFail || didSecurityScanReachDeadline;
|
|
75207
|
+
let securityScanFailureReason = null;
|
|
75208
|
+
if (didSecurityScanReachDeadline) securityScanFailureReason = "Security scan reached the max scan duration; findings collected before the deadline were preserved.";
|
|
75209
|
+
else if (didSecurityScanFail) securityScanFailureReason = "Security scan failed and was skipped.";
|
|
74695
75210
|
return {
|
|
74696
75211
|
project,
|
|
74697
75212
|
userConfig: resolvedConfig.config,
|
|
@@ -74714,6 +75229,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74714
75229
|
scanConcurrency,
|
|
74715
75230
|
supplyChainOverlapTimedOut: supplyChainResult.timedOut,
|
|
74716
75231
|
securityScanFailed,
|
|
75232
|
+
securityScanFailureReason,
|
|
74717
75233
|
lintCacheHitFileCount,
|
|
74718
75234
|
lintCacheTotalFileCount,
|
|
74719
75235
|
lintSidecarReplayedFileCount,
|
|
@@ -74729,7 +75245,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
74729
75245
|
"inspect.runDeadCode": input.runDeadCode,
|
|
74730
75246
|
"inspect.isCi": input.isCi,
|
|
74731
75247
|
"inspect.scoreSurface": input.scoreSurface ?? "score"
|
|
74732
|
-
} }), (scanProgram) => flatMap$2(ScanDeadlineMs, (scanDeadlineMs) =>
|
|
75248
|
+
} }), (scanProgram) => flatMap$2(ScanDeadlineMs, (scanDeadlineMs) => {
|
|
75249
|
+
if (scanDeadlineMs === null) return scanProgram;
|
|
75250
|
+
return scanProgram.pipe(timeout(scanDeadlineMs), catchTag$1("TimeoutError", () => new ReactDoctorError({ reason: new ScanDeadlineExceeded({ detail: `${scanDeadlineMs / MILLISECONDS_PER_SECOND}s elapsed` }) })));
|
|
75251
|
+
}));
|
|
74733
75252
|
const parseNodeVersion = (versionString) => {
|
|
74734
75253
|
const [major = 0, minor = 0, patch = 0] = versionString.replace(/^v/, "").trim().split(".").map(Number);
|
|
74735
75254
|
return {
|
|
@@ -74830,16 +75349,6 @@ const resolveNodeForOxlint = () => {
|
|
|
74830
75349
|
});
|
|
74831
75350
|
});
|
|
74832
75351
|
/**
|
|
74833
|
-
* Zip-Slip defense: relative paths come from git (`diff --name-only`), which
|
|
74834
|
-
* normalizes during ordinary adds, but a crafted index/pack/symlinked tree can
|
|
74835
|
-
* smuggle `..` segments that escape the temp root. Resolve against the temp dir
|
|
74836
|
-
* and reject anything that lands outside before writing.
|
|
74837
|
-
*/
|
|
74838
|
-
const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
|
|
74839
|
-
const relative = path.relative(parentAbsolutePath, childAbsolutePath);
|
|
74840
|
-
return Boolean(relative) && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
74841
|
-
};
|
|
74842
|
-
/**
|
|
74843
75352
|
* Writes a set of source files (supplied by `readContent` — e.g.
|
|
74844
75353
|
* `git show <ref>:<path>` for a baseline tree, or `git show :<path>` for the
|
|
74845
75354
|
* index) into a temp tree that mirrors the project layout, copying the
|
|
@@ -74875,7 +75384,7 @@ const materializeSourceTree = (input) => gen(function* () {
|
|
|
74875
75384
|
const sourcePath = path.join(input.directory, subdirectory, configFilename);
|
|
74876
75385
|
const targetPath = path.resolve(resolvedTempDirectory, subdirectory, configFilename);
|
|
74877
75386
|
if (!isPathInsideDirectory(targetPath, resolvedTempDirectory)) continue;
|
|
74878
|
-
if (
|
|
75387
|
+
if (isFile(sourcePath) && !fs.existsSync(targetPath)) {
|
|
74879
75388
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
74880
75389
|
fs.cpSync(sourcePath, targetPath);
|
|
74881
75390
|
}
|
|
@@ -75016,14 +75525,6 @@ const runEditorScan = async (input) => {
|
|
|
75016
75525
|
error: messageFromUnknown(error)
|
|
75017
75526
|
};
|
|
75018
75527
|
};
|
|
75019
|
-
/**
|
|
75020
|
-
* Back-compat alias: the streaming pipeline holds its caches in
|
|
75021
|
-
* per-pipeline closures that are garbage-collected when the pipeline
|
|
75022
|
-
* goes out of scope, so there is nothing to clear at module scope. The
|
|
75023
|
-
* public CLI's `clearCaches()` still calls this for symmetry with the
|
|
75024
|
-
* other `clear*` helpers.
|
|
75025
|
-
*/
|
|
75026
|
-
const clearAutoSuppressionCaches = () => {};
|
|
75027
75528
|
const lookupOwnRule = (rule) => Object.hasOwn(REACT_DOCTOR_RULE_REGISTRY, rule) ? REACT_DOCTOR_RULE_REGISTRY[rule] : void 0;
|
|
75028
75529
|
/**
|
|
75029
75530
|
* Returns presentation metadata for `<plugin>/<rule>`, or `null` when the
|
|
@@ -75063,7 +75564,7 @@ const computeConfigFingerprint = (projectDirectory, version) => {
|
|
|
75063
75564
|
/** Display name used in client-facing messages and progress titles. */
|
|
75064
75565
|
const SERVER_DISPLAY_NAME = "React Doctor";
|
|
75065
75566
|
/** Server version reported in `serverInfo`; injected at build, `dev` from source. */
|
|
75066
|
-
const SERVER_VERSION = "0.9.
|
|
75567
|
+
const SERVER_VERSION = "0.9.4";
|
|
75067
75568
|
/** `Diagnostic.source` shown next to every published diagnostic. */
|
|
75068
75569
|
const DIAGNOSTIC_SOURCE = "react-doctor";
|
|
75069
75570
|
/**
|
|
@@ -75691,10 +76192,7 @@ const createProjectGraph = (options) => {
|
|
|
75691
76192
|
for (const root of roots) try {
|
|
75692
76193
|
for (const workspacePackage of discoverReactSubprojects(root)) {
|
|
75693
76194
|
const directory = normalizeDirectory(workspacePackage.directory);
|
|
75694
|
-
if (!seen.has(directory)) seen.set(directory, {
|
|
75695
|
-
directory,
|
|
75696
|
-
name: workspacePackage.name
|
|
75697
|
-
});
|
|
76195
|
+
if (!seen.has(directory)) seen.set(directory, { directory });
|
|
75698
76196
|
}
|
|
75699
76197
|
} catch (error) {
|
|
75700
76198
|
logger.warn(`Project discovery failed for ${root}: ${messageFromUnknown(error)}`);
|
|
@@ -75719,8 +76217,8 @@ const createProjectGraph = (options) => {
|
|
|
75719
76217
|
clearProjectCache();
|
|
75720
76218
|
clearConfigCache();
|
|
75721
76219
|
clearPackageJsonCache();
|
|
76220
|
+
clearPackageRoleCache();
|
|
75722
76221
|
clearIgnorePatternsCache();
|
|
75723
|
-
clearAutoSuppressionCaches();
|
|
75724
76222
|
clearMinifiedFileCache();
|
|
75725
76223
|
projects = null;
|
|
75726
76224
|
}
|
|
@@ -76116,11 +76614,9 @@ const createScheduler = (options) => {
|
|
|
76116
76614
|
timers.clear();
|
|
76117
76615
|
queue.length = 0;
|
|
76118
76616
|
};
|
|
76119
|
-
const pendingCount = () => queue.length + running + timers.size;
|
|
76120
76617
|
return {
|
|
76121
76618
|
enqueue,
|
|
76122
76619
|
cancelProject,
|
|
76123
|
-
pendingCount,
|
|
76124
76620
|
dispose
|
|
76125
76621
|
};
|
|
76126
76622
|
};
|
|
@@ -76767,8 +77263,11 @@ const METRIC = {
|
|
|
76767
77263
|
scanPhaseDuration: "scan.phase_duration",
|
|
76768
77264
|
scanFiles: "scan.files",
|
|
76769
77265
|
scanScore: "scan.score",
|
|
77266
|
+
scanScoreRetry: "scan.score_retry",
|
|
76770
77267
|
scanClean: "scan.clean",
|
|
76771
77268
|
scanCheckSkipped: "scan.check_skipped",
|
|
77269
|
+
scanProjectSkipped: "scan.project_skipped",
|
|
77270
|
+
scanWorkspaceDeadCodeShared: "scan.workspace_deadcode_shared",
|
|
76772
77271
|
scanNoReactDetected: "scan.no_react_detected",
|
|
76773
77272
|
baselineDegraded: "baseline.degraded",
|
|
76774
77273
|
ruleFired: "rule.fired",
|
|
@@ -76795,9 +77294,14 @@ const METRIC = {
|
|
|
76795
77294
|
lspScanCompleted: "lsp.scan.completed",
|
|
76796
77295
|
lspScanDuration: "lsp.scan.duration",
|
|
76797
77296
|
lspScanDiagnostics: "lsp.scan.diagnostics",
|
|
76798
|
-
tuiActionMenuOpened: "tui.action_menu_opened",
|
|
76799
77297
|
tuiCompactReportShown: "tui.compact_report_shown",
|
|
76800
77298
|
tuiFindingNavigated: "tui.finding_navigated",
|
|
77299
|
+
tuiIssueStreamShown: "tui.issue_stream_shown",
|
|
77300
|
+
tuiProjectPathContextShown: "tui.project_path_context_shown",
|
|
77301
|
+
tuiProjectSelectInlineShown: "tui.project_select_inline_shown",
|
|
77302
|
+
tuiReportActionSelected: "tui.report_action_selected",
|
|
77303
|
+
tuiCancelled: "tui.cancelled",
|
|
77304
|
+
tuiScanInlineShown: "tui.scan_inline_shown",
|
|
76801
77305
|
tuiStackedReportCapped: "tui.stacked_report_capped",
|
|
76802
77306
|
aiTrainingWarningShown: "ai.training.warning_shown",
|
|
76803
77307
|
jsonOutUsed: "json.out_used"
|
|
@@ -76812,6 +77316,9 @@ const METRIC = {
|
|
|
76812
77316
|
*/
|
|
76813
77317
|
const toCategoryKey = (category) => category.toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
76814
77318
|
//#endregion
|
|
77319
|
+
//#region src/cli/utils/is-env-flag-enabled.ts
|
|
77320
|
+
const isEnvFlagEnabled = (value) => value === "1" || value?.toLowerCase() === "true";
|
|
77321
|
+
//#endregion
|
|
76815
77322
|
//#region src/cli/utils/anonymize-text.ts
|
|
76816
77323
|
/**
|
|
76817
77324
|
* Free-text fields can carry both a home-directory path (the OS username) and a
|
|
@@ -76972,7 +77479,6 @@ const resolveTracesSampleRate = () => {
|
|
|
76972
77479
|
* session.
|
|
76973
77480
|
*/
|
|
76974
77481
|
let lspTracesSampleRate = 0;
|
|
76975
|
-
const isEnvFlagEnabled = (value) => value === "1" || value?.toLowerCase() === "true";
|
|
76976
77482
|
const nodeMajorVersion = () => Number.parseInt(process.versions.node.split(".", 1)[0] ?? "", 10) || 0;
|
|
76977
77483
|
const shouldEnableLspTelemetry = () => {
|
|
76978
77484
|
if (isEnvFlagEnabled(process.env.REACT_DOCTOR_NO_TELEMETRY)) return false;
|
|
@@ -77100,5 +77606,5 @@ const startLanguageServer = () => {
|
|
|
77100
77606
|
};
|
|
77101
77607
|
//#endregion
|
|
77102
77608
|
export { startLanguageServer };
|
|
77103
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
77104
|
-
//# debugId=
|
|
77609
|
+
!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]="8e8ec622-f2dc-5f52-bfe8-232b07051a7f")}catch(e){}}();
|
|
77610
|
+
//# debugId=8e8ec622-f2dc-5f52-bfe8-232b07051a7f
|