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/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="e74e6a68-20e1-59fe-8db0-62b87e8fe049")}catch(e){}}();
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import * as NodeChildProcess from "node:child_process";
|
|
5
5
|
import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
@@ -6666,7 +6666,7 @@ const flatMap$5 = /* @__PURE__ */ dual(2, (self, f) => isNone(self) ? none() : f
|
|
|
6666
6666
|
* @category filtering
|
|
6667
6667
|
* @since 2.0.0
|
|
6668
6668
|
*/
|
|
6669
|
-
const filter = /* @__PURE__ */ dual(2, (self, predicate) => isNone(self) ? none() : predicate(self.value) ? some(self.value) : none());
|
|
6669
|
+
const filter$1 = /* @__PURE__ */ dual(2, (self, predicate) => isNone(self) ? none() : predicate(self.value) ? some(self.value) : none());
|
|
6670
6670
|
//#endregion
|
|
6671
6671
|
//#region ../../node_modules/.pnpm/effect@4.0.0-beta.70/node_modules/effect/dist/Result.js
|
|
6672
6672
|
/**
|
|
@@ -18657,7 +18657,7 @@ function transformOptional(f) {
|
|
|
18657
18657
|
*/
|
|
18658
18658
|
function withDefault$1(defaultValue) {
|
|
18659
18659
|
return new Getter((o) => {
|
|
18660
|
-
const filtered = filter(o, isNotUndefined);
|
|
18660
|
+
const filtered = filter$1(o, isNotUndefined);
|
|
18661
18661
|
return isSome(filtered) ? succeed$2(filtered) : mapEager(defaultValue, some);
|
|
18662
18662
|
});
|
|
18663
18663
|
}
|
|
@@ -22505,6 +22505,10 @@ var JsonReportProjectEntryV3 = class extends Class("JsonReportProjectEntryV3")({
|
|
|
22505
22505
|
scannedFileCount: optional(Number$1),
|
|
22506
22506
|
elapsedMilliseconds: Number$1
|
|
22507
22507
|
}) {};
|
|
22508
|
+
var JsonReportSkippedProject = class extends Class("JsonReportSkippedProject")({
|
|
22509
|
+
directory: String$1,
|
|
22510
|
+
reason: Literal("max-duration")
|
|
22511
|
+
}) {};
|
|
22508
22512
|
/** Original full, diff, and staged report contract. */
|
|
22509
22513
|
var JsonReportV1 = class extends Class("JsonReportV1")({
|
|
22510
22514
|
schemaVersion: Literal(1),
|
|
@@ -22533,6 +22537,7 @@ var JsonReportV1 = class extends Class("JsonReportV1")({
|
|
|
22533
22537
|
reactDetected: optional(Boolean$1),
|
|
22534
22538
|
diff: NullOr(JsonReportDiffInfo),
|
|
22535
22539
|
projects: ArraySchema(JsonReportProjectEntry),
|
|
22540
|
+
skippedProjects: optional(ArraySchema(JsonReportSkippedProject)),
|
|
22536
22541
|
diagnostics: ArraySchema(Diagnostic),
|
|
22537
22542
|
summary: JsonReportSummary,
|
|
22538
22543
|
elapsedMilliseconds: Number$1,
|
|
@@ -22561,6 +22566,7 @@ var JsonReportV2 = class extends Class("JsonReportV2")({
|
|
|
22561
22566
|
diff: NullOr(JsonReportDiffInfo),
|
|
22562
22567
|
baseline: JsonReportBaseline,
|
|
22563
22568
|
projects: ArraySchema(JsonReportProjectEntry),
|
|
22569
|
+
skippedProjects: optional(ArraySchema(JsonReportSkippedProject)),
|
|
22564
22570
|
diagnostics: ArraySchema(Diagnostic),
|
|
22565
22571
|
summary: JsonReportSummary,
|
|
22566
22572
|
elapsedMilliseconds: Number$1,
|
|
@@ -22577,6 +22583,7 @@ var JsonReportV3 = class extends Class("JsonReportV3")({
|
|
|
22577
22583
|
diff: NullOr(JsonReportDiffInfo),
|
|
22578
22584
|
baseline: optional(JsonReportBaseline),
|
|
22579
22585
|
projects: ArraySchema(JsonReportProjectEntryV3),
|
|
22586
|
+
skippedProjects: optional(ArraySchema(JsonReportSkippedProject)),
|
|
22580
22587
|
diagnostics: ArraySchema(JsonReportDiagnosticV3),
|
|
22581
22588
|
summary: JsonReportSummary,
|
|
22582
22589
|
elapsedMilliseconds: Number$1,
|
|
@@ -56559,6 +56566,47 @@ const flattenArray = (self) => transformPull$1(self, (pull) => {
|
|
|
56559
56566
|
}));
|
|
56560
56567
|
});
|
|
56561
56568
|
/**
|
|
56569
|
+
* Filters arrays of elements emitted by a channel, applying the filter
|
|
56570
|
+
* to each element within the arrays and only emitting non-empty filtered arrays.
|
|
56571
|
+
*
|
|
56572
|
+
* **Example** (Filtering array output)
|
|
56573
|
+
*
|
|
56574
|
+
* ```ts
|
|
56575
|
+
* import { Array, Channel } from "effect"
|
|
56576
|
+
*
|
|
56577
|
+
* const nonEmptyArrayPredicate = Array.isReadonlyArrayNonEmpty
|
|
56578
|
+
*
|
|
56579
|
+
* // Create a channel that outputs arrays of mixed data
|
|
56580
|
+
* const arrayChannel = Channel.fromIterable([
|
|
56581
|
+
* Array.make(1, 2, 3, 4, 5),
|
|
56582
|
+
* Array.make(6, 7, 8, 9, 10),
|
|
56583
|
+
* Array.make(11, 12, 13, 14, 15)
|
|
56584
|
+
* ]).pipe(Channel.filter(nonEmptyArrayPredicate))
|
|
56585
|
+
*
|
|
56586
|
+
* // Filter arrays to keep only even numbers
|
|
56587
|
+
* const evenArraysChannel = Channel.filterArray(arrayChannel, (n) => n % 2 === 0)
|
|
56588
|
+
* // Outputs: [2, 4], [6, 8, 10], [12, 14]
|
|
56589
|
+
* // Note: Only non-empty filtered arrays are emitted
|
|
56590
|
+
*
|
|
56591
|
+
* // Arrays that would become empty after filtering are discarded entirely
|
|
56592
|
+
* const oddChannel = Channel.fromIterable([
|
|
56593
|
+
* Array.make(1, 3, 5),
|
|
56594
|
+
* Array.make(2, 4),
|
|
56595
|
+
* Array.make(7, 9)
|
|
56596
|
+
* ]).pipe(Channel.filter(nonEmptyArrayPredicate))
|
|
56597
|
+
* const filteredOddChannel = Channel.filterArray(oddChannel, (n) => n % 2 === 0)
|
|
56598
|
+
* // Outputs: [2, 4] (the arrays [1,3,5] and [7,9] are discarded)
|
|
56599
|
+
* ```
|
|
56600
|
+
*
|
|
56601
|
+
* @category filtering
|
|
56602
|
+
* @since 4.0.0
|
|
56603
|
+
*/
|
|
56604
|
+
const filterArray = /* @__PURE__ */ dual(2, (self, predicate) => transformPull$1(self, (pull) => succeed$2(flatMap$2(pull, function loop(arr) {
|
|
56605
|
+
const passes = [];
|
|
56606
|
+
for (let i = 0; i < arr.length; i++) if (predicate(arr[i])) passes.push(arr[i]);
|
|
56607
|
+
return isReadonlyArrayNonEmpty(passes) ? succeed$2(passes) : flatMap$2(pull, loop);
|
|
56608
|
+
}))));
|
|
56609
|
+
/**
|
|
56562
56610
|
* Filters and maps each element inside emitted non-empty arrays using a
|
|
56563
56611
|
* `Filter`.
|
|
56564
56612
|
*
|
|
@@ -58196,6 +58244,30 @@ const concat = /* @__PURE__ */ dual(2, (self, that) => flatten(fromArray([self,
|
|
|
58196
58244
|
*/
|
|
58197
58245
|
const merge$1 = /* @__PURE__ */ dual((args) => isStream(args[0]) && isStream(args[1]), (self, that, options) => fromChannel(merge$2(toChannel(self), toChannel(that), options)));
|
|
58198
58246
|
/**
|
|
58247
|
+
* Filters a stream to the elements that satisfy a predicate.
|
|
58248
|
+
*
|
|
58249
|
+
* **Example** (Filtering stream values)
|
|
58250
|
+
*
|
|
58251
|
+
* ```ts
|
|
58252
|
+
* import { Console, Effect, Stream } from "effect"
|
|
58253
|
+
*
|
|
58254
|
+
* const program = Effect.gen(function*() {
|
|
58255
|
+
* const stream = Stream.make(1, 2, 3, 4).pipe(
|
|
58256
|
+
* Stream.filter((n) => n % 2 === 0)
|
|
58257
|
+
* )
|
|
58258
|
+
* const values = yield* Stream.runCollect(stream)
|
|
58259
|
+
* yield* Console.log(values)
|
|
58260
|
+
* })
|
|
58261
|
+
*
|
|
58262
|
+
* Effect.runPromise(program)
|
|
58263
|
+
* // Output: [ 2, 4 ]
|
|
58264
|
+
* ```
|
|
58265
|
+
*
|
|
58266
|
+
* @category filtering
|
|
58267
|
+
* @since 2.0.0
|
|
58268
|
+
*/
|
|
58269
|
+
const filter = /* @__PURE__ */ dual(2, (self, predicate) => fromChannel(filterArray(toChannel(self), predicate)));
|
|
58270
|
+
/**
|
|
58199
58271
|
* Filters and maps stream elements in one pass using a `Filter`.
|
|
58200
58272
|
*
|
|
58201
58273
|
* @category filtering
|
|
@@ -65046,6 +65118,7 @@ const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
|
|
|
65046
65118
|
const GENERATED_SOURCE_DIRECTORY_PATTERN = /(?:^|\/)__generated__\//;
|
|
65047
65119
|
const MINIFIED_SNIFF_BYTES = 65536;
|
|
65048
65120
|
const GIT_LS_FILES_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
|
|
65121
|
+
const GIT_CHECK_ATTR_MAX_BUFFER_BYTES = GIT_LS_FILES_MAX_BUFFER_BYTES;
|
|
65049
65122
|
const SCANNED_DOT_DIRECTORIES = new Set([".dumi", ".storybook"]);
|
|
65050
65123
|
const IGNORED_DIRECTORIES = new Set([
|
|
65051
65124
|
".angular",
|
|
@@ -65365,13 +65438,9 @@ const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
|
|
|
65365
65438
|
const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
|
|
65366
65439
|
const ABORT_EXIT_CODES = new Set([134, 3221226505]);
|
|
65367
65440
|
const OXLINT_OOM_RESCUE_BUDGET_MS = 6e4;
|
|
65368
|
-
const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
|
|
65369
|
-
const LINT_PHASE_TIMEOUT_MS = 3e5;
|
|
65370
|
-
const SCAN_TOTAL_DEADLINE_MS = 9e5;
|
|
65371
65441
|
const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
|
|
65372
65442
|
const DEAD_CODE_WORKER_MEM_BUDGET_BYTES = 2 * 1024 * 1024 * 1024;
|
|
65373
65443
|
const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
|
|
65374
|
-
const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
|
|
65375
65444
|
const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
|
|
65376
65445
|
const RECOMMENDED_PNPM_MINIMUM_RELEASE_AGE_MINUTES = 10080;
|
|
65377
65446
|
const REACT_SERVER_DOM_PACKAGES = [
|
|
@@ -65419,7 +65488,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
65419
65488
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
65420
65489
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
65421
65490
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
65422
|
-
const CORE_PACKAGE_VERSION = "0.9.
|
|
65491
|
+
const CORE_PACKAGE_VERSION = "0.9.4";
|
|
65423
65492
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
65424
65493
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
65425
65494
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -65432,17 +65501,20 @@ const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
|
|
|
65432
65501
|
const isErrnoException = (error) => error instanceof Error && "code" in error;
|
|
65433
65502
|
const IGNORABLE_READDIR_ERROR_CODES = new Set([
|
|
65434
65503
|
"EACCES",
|
|
65504
|
+
"EBUSY",
|
|
65435
65505
|
"EPERM",
|
|
65436
65506
|
"ENOENT",
|
|
65437
65507
|
"ENOTDIR",
|
|
65438
65508
|
"EINVAL",
|
|
65439
65509
|
"ELOOP",
|
|
65440
|
-
"ENAMETOOLONG"
|
|
65510
|
+
"ENAMETOOLONG",
|
|
65511
|
+
"ETIMEDOUT",
|
|
65512
|
+
"UNKNOWN"
|
|
65441
65513
|
]);
|
|
65442
65514
|
const isIgnorableReaddirError = (error) => isErrnoException(error) && typeof error.code === "string" && IGNORABLE_READDIR_ERROR_CODES.has(error.code);
|
|
65443
65515
|
const readDirectoryEntries = (directoryPath) => {
|
|
65444
65516
|
try {
|
|
65445
|
-
return
|
|
65517
|
+
return fs.readdirSync(directoryPath, { withFileTypes: true });
|
|
65446
65518
|
} catch (error) {
|
|
65447
65519
|
if (isIgnorableReaddirError(error)) return [];
|
|
65448
65520
|
throw error;
|
|
@@ -65450,14 +65522,14 @@ const readDirectoryEntries = (directoryPath) => {
|
|
|
65450
65522
|
};
|
|
65451
65523
|
const isFile = (filePath) => {
|
|
65452
65524
|
try {
|
|
65453
|
-
return
|
|
65525
|
+
return fs.statSync(filePath).isFile();
|
|
65454
65526
|
} catch {
|
|
65455
65527
|
return false;
|
|
65456
65528
|
}
|
|
65457
65529
|
};
|
|
65458
65530
|
const isDirectory = (directoryPath) => {
|
|
65459
65531
|
try {
|
|
65460
|
-
return
|
|
65532
|
+
return fs.statSync(directoryPath).isDirectory();
|
|
65461
65533
|
} catch {
|
|
65462
65534
|
return false;
|
|
65463
65535
|
}
|
|
@@ -65467,6 +65539,66 @@ const isPlainObject = (value) => {
|
|
|
65467
65539
|
const prototype = Object.getPrototypeOf(value);
|
|
65468
65540
|
return prototype === null || prototype === Object.prototype;
|
|
65469
65541
|
};
|
|
65542
|
+
const LINGUIST_ATTRIBUTES = ["linguist-generated", "linguist-vendored"];
|
|
65543
|
+
const DISABLED_ATTRIBUTE_VALUES = new Set([
|
|
65544
|
+
"unspecified",
|
|
65545
|
+
"unset",
|
|
65546
|
+
"false",
|
|
65547
|
+
"0",
|
|
65548
|
+
"off",
|
|
65549
|
+
"no"
|
|
65550
|
+
]);
|
|
65551
|
+
const buildInput = (relativePaths) => `${relativePaths.join("\0")}\0`;
|
|
65552
|
+
const parseIgnoredPaths = (output) => {
|
|
65553
|
+
const ignoredPaths = /* @__PURE__ */ new Set();
|
|
65554
|
+
const fields = output.split("\0");
|
|
65555
|
+
for (let fieldIndex = 0; fieldIndex + 2 < fields.length; fieldIndex += 3) {
|
|
65556
|
+
const filePath = fields[fieldIndex];
|
|
65557
|
+
const value = fields[fieldIndex + 2]?.toLowerCase();
|
|
65558
|
+
if (filePath && value && !DISABLED_ATTRIBUTE_VALUES.has(value)) ignoredPaths.add(filePath);
|
|
65559
|
+
}
|
|
65560
|
+
return ignoredPaths;
|
|
65561
|
+
};
|
|
65562
|
+
const collectGitLinguistIgnoredPaths = (rootDirectory, relativePaths) => {
|
|
65563
|
+
if (relativePaths.length === 0) return /* @__PURE__ */ new Set();
|
|
65564
|
+
const result = spawnSync("git", [
|
|
65565
|
+
"check-attr",
|
|
65566
|
+
"-z",
|
|
65567
|
+
"--stdin",
|
|
65568
|
+
...LINGUIST_ATTRIBUTES
|
|
65569
|
+
], {
|
|
65570
|
+
cwd: rootDirectory,
|
|
65571
|
+
encoding: "utf-8",
|
|
65572
|
+
input: buildInput(relativePaths),
|
|
65573
|
+
maxBuffer: GIT_CHECK_ATTR_MAX_BUFFER_BYTES
|
|
65574
|
+
});
|
|
65575
|
+
if (result.error || result.status !== 0) return /* @__PURE__ */ new Set();
|
|
65576
|
+
return parseIgnoredPaths(result.stdout);
|
|
65577
|
+
};
|
|
65578
|
+
const collectGitLinguistIgnoredPathsCooperative = (rootDirectory, relativePaths, signal) => {
|
|
65579
|
+
if (relativePaths.length === 0) return Promise.resolve(/* @__PURE__ */ new Set());
|
|
65580
|
+
return new Promise((resolve, reject) => {
|
|
65581
|
+
signal?.throwIfAborted();
|
|
65582
|
+
execFile("git", [
|
|
65583
|
+
"check-attr",
|
|
65584
|
+
"-z",
|
|
65585
|
+
"--stdin",
|
|
65586
|
+
...LINGUIST_ATTRIBUTES
|
|
65587
|
+
], {
|
|
65588
|
+
cwd: rootDirectory,
|
|
65589
|
+
encoding: "utf-8",
|
|
65590
|
+
killSignal: "SIGKILL",
|
|
65591
|
+
maxBuffer: GIT_CHECK_ATTR_MAX_BUFFER_BYTES,
|
|
65592
|
+
signal
|
|
65593
|
+
}, (error, stdout) => {
|
|
65594
|
+
if (signal?.aborted) {
|
|
65595
|
+
reject(signal.reason);
|
|
65596
|
+
return;
|
|
65597
|
+
}
|
|
65598
|
+
resolve(error ? /* @__PURE__ */ new Set() : parseIgnoredPaths(stdout));
|
|
65599
|
+
}).stdin?.end(buildInput(relativePaths));
|
|
65600
|
+
});
|
|
65601
|
+
};
|
|
65470
65602
|
const lastSourceMappingUrl = (fileContent) => {
|
|
65471
65603
|
const lastReference = [...fileContent.matchAll(/\/\/# sourceMappingURL=(\S+)/g)].at(-1);
|
|
65472
65604
|
return lastReference ? lastReference[1] : null;
|
|
@@ -65568,6 +65700,9 @@ function* walkSourceTreeFiles(rootDirectory) {
|
|
|
65568
65700
|
}
|
|
65569
65701
|
}
|
|
65570
65702
|
}
|
|
65703
|
+
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
65704
|
+
setImmediate(resolve);
|
|
65705
|
+
});
|
|
65571
65706
|
const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
|
|
65572
65707
|
const entries = [];
|
|
65573
65708
|
for (const relativePath of relativePaths) {
|
|
@@ -65581,23 +65716,55 @@ const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
|
|
|
65581
65716
|
}
|
|
65582
65717
|
return entries;
|
|
65583
65718
|
};
|
|
65584
|
-
const
|
|
65719
|
+
const collectSizedSourceFilesCooperative = async (rootDirectory, relativePaths, signal) => {
|
|
65720
|
+
const entries = [];
|
|
65721
|
+
let sliceStartedAt = performance.now();
|
|
65722
|
+
for (const relativePath of relativePaths) {
|
|
65723
|
+
signal?.throwIfAborted();
|
|
65724
|
+
const absolutePath = Path.resolve(rootDirectory, relativePath);
|
|
65725
|
+
const sizeBytes = statSourceFileSize(absolutePath);
|
|
65726
|
+
if (!isLargeMinifiedFile(absolutePath, sizeBytes)) entries.push({
|
|
65727
|
+
path: relativePath,
|
|
65728
|
+
sizeBytes: sizeBytes ?? 0
|
|
65729
|
+
});
|
|
65730
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
65731
|
+
await yieldToEventLoop();
|
|
65732
|
+
sliceStartedAt = performance.now();
|
|
65733
|
+
}
|
|
65734
|
+
}
|
|
65735
|
+
return entries;
|
|
65736
|
+
};
|
|
65737
|
+
const parseGitSourceFilePaths = (output) => {
|
|
65738
|
+
const trackedPaths = /* @__PURE__ */ new Set();
|
|
65739
|
+
const untrackedPaths = [];
|
|
65740
|
+
for (const entry of output.split("\0")) {
|
|
65741
|
+
if (entry.length === 0) continue;
|
|
65742
|
+
const trackedPath = /^[0-7]{6} [0-9a-f]+ [0-3]\t([\s\S]+)$/.exec(entry)?.[1];
|
|
65743
|
+
if (trackedPath === void 0) untrackedPaths.push(entry);
|
|
65744
|
+
else trackedPaths.add(trackedPath);
|
|
65745
|
+
}
|
|
65746
|
+
return {
|
|
65747
|
+
trackedPaths: [...trackedPaths],
|
|
65748
|
+
untrackedPaths
|
|
65749
|
+
};
|
|
65750
|
+
};
|
|
65751
|
+
const listGitSourceFilePaths = (rootDirectory) => {
|
|
65585
65752
|
const result = spawnSync("git", [
|
|
65586
65753
|
"ls-files",
|
|
65587
65754
|
"-z",
|
|
65588
|
-
|
|
65755
|
+
"--stage",
|
|
65756
|
+
"--others",
|
|
65757
|
+
"--exclude-standard"
|
|
65589
65758
|
], {
|
|
65590
65759
|
cwd: rootDirectory,
|
|
65591
65760
|
encoding: "utf-8",
|
|
65592
65761
|
maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES
|
|
65593
65762
|
});
|
|
65594
65763
|
if (result.error || result.status !== 0) return null;
|
|
65595
|
-
return result.stdout
|
|
65764
|
+
return parseGitSourceFilePaths(result.stdout);
|
|
65596
65765
|
};
|
|
65597
|
-
const
|
|
65598
|
-
const trackedPaths =
|
|
65599
|
-
const untrackedPaths = gitLsFiles(rootDirectory, ["--others", "--exclude-standard"]);
|
|
65600
|
-
if (trackedPaths === null || untrackedPaths === null) return null;
|
|
65766
|
+
const collectGitCandidateSourceFilePaths = (rootDirectory, paths) => {
|
|
65767
|
+
const { trackedPaths, untrackedPaths } = paths;
|
|
65601
65768
|
const emitDuplicateJsPaths = collectTypeScriptEmitDuplicateJsPaths({
|
|
65602
65769
|
trackedPaths: new Set(trackedPaths),
|
|
65603
65770
|
untrackedPaths,
|
|
@@ -65605,12 +65772,65 @@ const listSourceFilesViaGit = (rootDirectory) => {
|
|
|
65605
65772
|
});
|
|
65606
65773
|
return [...trackedPaths, ...untrackedPaths].filter((filePath) => isLintableSourceFile(filePath) && !hasIgnoredPathSegment(filePath) && !emitDuplicateJsPaths.has(filePath));
|
|
65607
65774
|
};
|
|
65775
|
+
const filterGitSourceFilePaths = (rootDirectory, paths) => {
|
|
65776
|
+
const candidatePaths = collectGitCandidateSourceFilePaths(rootDirectory, paths);
|
|
65777
|
+
const linguistIgnoredPaths = collectGitLinguistIgnoredPaths(rootDirectory, candidatePaths);
|
|
65778
|
+
return candidatePaths.filter((filePath) => !linguistIgnoredPaths.has(filePath));
|
|
65779
|
+
};
|
|
65780
|
+
const listSourceFilesViaGit = (rootDirectory) => {
|
|
65781
|
+
const paths = listGitSourceFilePaths(rootDirectory);
|
|
65782
|
+
return paths === null ? null : filterGitSourceFilePaths(rootDirectory, paths);
|
|
65783
|
+
};
|
|
65784
|
+
const listSourceFilesViaGitCooperative = async (rootDirectory, signal) => {
|
|
65785
|
+
const paths = await new Promise((resolve, reject) => {
|
|
65786
|
+
signal?.throwIfAborted();
|
|
65787
|
+
execFile("git", [
|
|
65788
|
+
"ls-files",
|
|
65789
|
+
"-z",
|
|
65790
|
+
"--stage",
|
|
65791
|
+
"--others",
|
|
65792
|
+
"--exclude-standard"
|
|
65793
|
+
], {
|
|
65794
|
+
cwd: rootDirectory,
|
|
65795
|
+
encoding: "utf-8",
|
|
65796
|
+
killSignal: "SIGKILL",
|
|
65797
|
+
maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES,
|
|
65798
|
+
signal
|
|
65799
|
+
}, (error, stdout) => {
|
|
65800
|
+
if (signal?.aborted) {
|
|
65801
|
+
reject(signal.reason);
|
|
65802
|
+
return;
|
|
65803
|
+
}
|
|
65804
|
+
resolve(error ? null : parseGitSourceFilePaths(stdout));
|
|
65805
|
+
});
|
|
65806
|
+
});
|
|
65807
|
+
if (paths === null) return null;
|
|
65808
|
+
const candidatePaths = collectGitCandidateSourceFilePaths(rootDirectory, paths);
|
|
65809
|
+
const linguistIgnoredPaths = await collectGitLinguistIgnoredPathsCooperative(rootDirectory, candidatePaths, signal);
|
|
65810
|
+
return candidatePaths.filter((filePath) => !linguistIgnoredPaths.has(filePath));
|
|
65811
|
+
};
|
|
65608
65812
|
const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
65609
65813
|
const filePaths = [];
|
|
65610
65814
|
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) if (isLintableSourceFile(name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
|
|
65611
65815
|
return filePaths;
|
|
65612
65816
|
};
|
|
65613
|
-
const
|
|
65817
|
+
const listSourceFilesViaFilesystemCooperative = async (rootDirectory, signal) => {
|
|
65818
|
+
const filePaths = [];
|
|
65819
|
+
let sliceStartedAt = performance.now();
|
|
65820
|
+
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
|
|
65821
|
+
signal?.throwIfAborted();
|
|
65822
|
+
if (isLintableSourceFile(name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
|
|
65823
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
65824
|
+
await yieldToEventLoop();
|
|
65825
|
+
sliceStartedAt = performance.now();
|
|
65826
|
+
}
|
|
65827
|
+
}
|
|
65828
|
+
return filePaths;
|
|
65829
|
+
};
|
|
65830
|
+
const listSourceFilePaths = (rootDirectory) => (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort();
|
|
65831
|
+
const listSourceFilePathsCooperative = async (rootDirectory, signal) => (await listSourceFilesViaGitCooperative(rootDirectory, signal) ?? await listSourceFilesViaFilesystemCooperative(rootDirectory, signal)).sort();
|
|
65832
|
+
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, listSourceFilePaths(rootDirectory));
|
|
65833
|
+
const listSourceFilesCooperative = async (rootDirectory, signal) => (await collectSizedSourceFilesCooperative(rootDirectory, await listSourceFilePathsCooperative(rootDirectory, signal), signal)).map((entry) => entry.path);
|
|
65614
65834
|
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
65615
65835
|
const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
|
|
65616
65836
|
const UTF8_BOM_CHARACTER = "";
|
|
@@ -65733,7 +65953,12 @@ const mergeCompilerOptions = (inherited, current) => {
|
|
|
65733
65953
|
};
|
|
65734
65954
|
};
|
|
65735
65955
|
const readResolvedCompilerOptions = (tsConfigPath, extendsDepth, visitedPaths) => {
|
|
65736
|
-
|
|
65956
|
+
let realPath;
|
|
65957
|
+
try {
|
|
65958
|
+
realPath = NFS.realpathSync.native(tsConfigPath);
|
|
65959
|
+
} catch {
|
|
65960
|
+
return null;
|
|
65961
|
+
}
|
|
65737
65962
|
if (visitedPaths.has(realPath)) return null;
|
|
65738
65963
|
const tsConfig = readTsConfig(realPath);
|
|
65739
65964
|
if (!tsConfig) return null;
|
|
@@ -66261,27 +66486,30 @@ const getSelectedObjectProperty = (expression, propertyName, analysis, visitedEx
|
|
|
66261
66486
|
}
|
|
66262
66487
|
return null;
|
|
66263
66488
|
};
|
|
66264
|
-
const configExpressionMayDefineProperty = (expression, propertyName, analysis) => {
|
|
66489
|
+
const configExpressionMayDefineProperty = (expression, propertyName, analysis, visitedExpressions = /* @__PURE__ */ new Set()) => {
|
|
66265
66490
|
let resolvedExpression = expression;
|
|
66266
66491
|
while (ts.isParenthesizedExpression(resolvedExpression) || ts.isAsExpression(resolvedExpression) || ts.isTypeAssertionExpression(resolvedExpression) || ts.isSatisfiesExpression(resolvedExpression) || ts.isNonNullExpression(resolvedExpression)) resolvedExpression = resolvedExpression.expression;
|
|
66492
|
+
if (visitedExpressions.has(resolvedExpression)) return true;
|
|
66493
|
+
const nextVisitedExpressions = new Set(visitedExpressions);
|
|
66494
|
+
nextVisitedExpressions.add(resolvedExpression);
|
|
66267
66495
|
if (ts.isIdentifier(resolvedExpression)) {
|
|
66268
66496
|
if (analysis.localBindings.has(resolvedExpression.text)) {
|
|
66269
66497
|
const localReference = analysis.localBindings.get(resolvedExpression.text);
|
|
66270
|
-
return localReference?.expression ? configExpressionMayDefineProperty(localReference.expression, propertyName, localReference.analysis) : true;
|
|
66498
|
+
return localReference?.expression ? configExpressionMayDefineProperty(localReference.expression, propertyName, localReference.analysis, nextVisitedExpressions) : true;
|
|
66271
66499
|
}
|
|
66272
66500
|
const scopedBinding = getScopedConfigBinding(resolvedExpression);
|
|
66273
|
-
if (scopedBinding.wasFound) return scopedBinding.initializer ? configExpressionMayDefineProperty(scopedBinding.initializer, propertyName, analysis) : true;
|
|
66501
|
+
if (scopedBinding.wasFound) return scopedBinding.initializer ? configExpressionMayDefineProperty(scopedBinding.initializer, propertyName, analysis, nextVisitedExpressions) : true;
|
|
66274
66502
|
const topLevelBinding = getTopLevelBinding(analysis.sourceFile, resolvedExpression.text);
|
|
66275
|
-
return topLevelBinding && ts.isExpression(topLevelBinding) ? configExpressionMayDefineProperty(topLevelBinding, propertyName, analysis) : true;
|
|
66503
|
+
return topLevelBinding && ts.isExpression(topLevelBinding) ? configExpressionMayDefineProperty(topLevelBinding, propertyName, analysis, nextVisitedExpressions) : true;
|
|
66276
66504
|
}
|
|
66277
66505
|
if (ts.isConditionalExpression(resolvedExpression)) {
|
|
66278
|
-
if (isStaticallyTruthyConfigExpression(resolvedExpression.condition, analysis)) return configExpressionMayDefineProperty(resolvedExpression.whenTrue, propertyName, analysis);
|
|
66279
|
-
if (isStaticallyDisabledConfigExpression(resolvedExpression.condition, analysis)) return configExpressionMayDefineProperty(resolvedExpression.whenFalse, propertyName, analysis);
|
|
66280
|
-
return configExpressionMayDefineProperty(resolvedExpression.whenTrue, propertyName, analysis) || configExpressionMayDefineProperty(resolvedExpression.whenFalse, propertyName, analysis);
|
|
66506
|
+
if (isStaticallyTruthyConfigExpression(resolvedExpression.condition, analysis)) return configExpressionMayDefineProperty(resolvedExpression.whenTrue, propertyName, analysis, nextVisitedExpressions);
|
|
66507
|
+
if (isStaticallyDisabledConfigExpression(resolvedExpression.condition, analysis)) return configExpressionMayDefineProperty(resolvedExpression.whenFalse, propertyName, analysis, nextVisitedExpressions);
|
|
66508
|
+
return configExpressionMayDefineProperty(resolvedExpression.whenTrue, propertyName, analysis, nextVisitedExpressions) || configExpressionMayDefineProperty(resolvedExpression.whenFalse, propertyName, analysis, nextVisitedExpressions);
|
|
66281
66509
|
}
|
|
66282
66510
|
if (!ts.isObjectLiteralExpression(resolvedExpression)) return true;
|
|
66283
66511
|
return resolvedExpression.properties.some((property) => {
|
|
66284
|
-
if (ts.isSpreadAssignment(property)) return configExpressionMayDefineProperty(property.expression, propertyName, analysis);
|
|
66512
|
+
if (ts.isSpreadAssignment(property)) return configExpressionMayDefineProperty(property.expression, propertyName, analysis, nextVisitedExpressions);
|
|
66285
66513
|
if (ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property) || ts.isMethodDeclaration(property) || ts.isGetAccessorDeclaration(property) || ts.isSetAccessorDeclaration(property)) {
|
|
66286
66514
|
const staticPropertyName = getStaticPropertyName$2(property.name);
|
|
66287
66515
|
return staticPropertyName === null || staticPropertyName === propertyName;
|
|
@@ -66357,7 +66585,13 @@ const analyzeConfigModuleExport = (filePath, exportName, allowCompilerTransform,
|
|
|
66357
66585
|
if (visitedModules.has(moduleVisitKey)) return false;
|
|
66358
66586
|
const nextVisitedModules = new Set(visitedModules);
|
|
66359
66587
|
nextVisitedModules.add(moduleVisitKey);
|
|
66360
|
-
|
|
66588
|
+
let sourceText;
|
|
66589
|
+
try {
|
|
66590
|
+
sourceText = NFS.readFileSync(filePath, "utf-8");
|
|
66591
|
+
} catch {
|
|
66592
|
+
return false;
|
|
66593
|
+
}
|
|
66594
|
+
return analyzeConfigSourceFileExport(parseConfigSourceFile(filePath, sourceText), filePath, exportName, allowCompilerTransform, importDepth, nextVisitedModules, argumentsList, argumentAnalysis);
|
|
66361
66595
|
};
|
|
66362
66596
|
const analyzeConfigSourceFileExport = (sourceFile, filePath, exportName, allowCompilerTransform, importDepth, visitedModules, argumentsList = [], argumentAnalysis) => {
|
|
66363
66597
|
const analysis = {
|
|
@@ -67185,13 +67419,7 @@ const hasReactDependency = (packageJson) => {
|
|
|
67185
67419
|
};
|
|
67186
67420
|
return Object.keys(allDependencies).some((packageName) => REACT_DEPENDENCY_NAMES.has(packageName));
|
|
67187
67421
|
};
|
|
67188
|
-
const getPreactVersion = (packageJson) =>
|
|
67189
|
-
return {
|
|
67190
|
-
...packageJson.peerDependencies,
|
|
67191
|
-
...packageJson.dependencies,
|
|
67192
|
-
...packageJson.devDependencies
|
|
67193
|
-
}.preact ?? null;
|
|
67194
|
-
};
|
|
67422
|
+
const getPreactVersion = (packageJson) => getDependencySpec(packageJson, "preact");
|
|
67195
67423
|
const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, sourceDirectory = rootDirectory, sourcePackageJson = rootPackageJson, version }) => {
|
|
67196
67424
|
if (version === null || !isCatalogReference(version)) return version;
|
|
67197
67425
|
const catalogName = extractCatalogName(version);
|
|
@@ -67933,7 +68161,16 @@ const getBrowserslistProtectionRequirement = (directory) => {
|
|
|
67933
68161
|
if (!queries) return void 0;
|
|
67934
68162
|
let requirement;
|
|
67935
68163
|
let hasUnsupportedTarget = false;
|
|
67936
|
-
|
|
68164
|
+
const previousIgnoreOldData = process.env.BROWSERSLIST_IGNORE_OLD_DATA;
|
|
68165
|
+
let browserTargets = [];
|
|
68166
|
+
try {
|
|
68167
|
+
process.env.BROWSERSLIST_IGNORE_OLD_DATA = "true";
|
|
68168
|
+
browserTargets = (0, import_browserslist.default)(queries);
|
|
68169
|
+
} finally {
|
|
68170
|
+
if (previousIgnoreOldData === void 0) delete process.env.BROWSERSLIST_IGNORE_OLD_DATA;
|
|
68171
|
+
else process.env.BROWSERSLIST_IGNORE_OLD_DATA = previousIgnoreOldData;
|
|
68172
|
+
}
|
|
68173
|
+
for (const browserTarget of browserTargets) {
|
|
67937
68174
|
const browserRequirement = getBrowserProtectionRequirement(browserTarget);
|
|
67938
68175
|
if (browserRequirement === "unsupported") hasUnsupportedTarget = true;
|
|
67939
68176
|
if (browserRequirement === "noreferrer") requirement = "noreferrer";
|
|
@@ -68039,9 +68276,9 @@ const cachedProjectInfos = /* @__PURE__ */ new Map();
|
|
|
68039
68276
|
* framework-agnostic rules. Throws only when the directory has nothing
|
|
68040
68277
|
* to scan (no enclosing project and no source files of its own).
|
|
68041
68278
|
*/
|
|
68042
|
-
const discoverProjectWithoutPackageJson = (directory) => {
|
|
68043
|
-
const sourceFileCount = countSourceFiles(directory);
|
|
68044
|
-
const hasOwnTsConfig =
|
|
68279
|
+
const discoverProjectWithoutPackageJson = (directory, options) => {
|
|
68280
|
+
const sourceFileCount = options.sourceFileCount ?? countSourceFiles(directory);
|
|
68281
|
+
const hasOwnTsConfig = isFile(Path.join(directory, "tsconfig.json"));
|
|
68045
68282
|
const enclosingProjectRoot = findNearestAncestorPackageJson(directory);
|
|
68046
68283
|
const enclosingProject = enclosingProjectRoot !== null ? discoverProject(enclosingProjectRoot) : null;
|
|
68047
68284
|
if (enclosingProject !== null) return {
|
|
@@ -68107,12 +68344,15 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
68107
68344
|
sourceFileCount
|
|
68108
68345
|
};
|
|
68109
68346
|
};
|
|
68110
|
-
const discoverProject = (directory) => {
|
|
68347
|
+
const discoverProject = (directory, options = {}) => {
|
|
68111
68348
|
const cached = cachedProjectInfos.get(directory);
|
|
68112
|
-
if (cached !== void 0) return cached
|
|
68349
|
+
if (cached !== void 0) return options.sourceFileCount === void 0 ? cached : {
|
|
68350
|
+
...cached,
|
|
68351
|
+
sourceFileCount: options.sourceFileCount
|
|
68352
|
+
};
|
|
68113
68353
|
const packageJsonPath = Path.join(directory, "package.json");
|
|
68114
68354
|
if (!isFile(packageJsonPath)) {
|
|
68115
|
-
const synthesized = discoverProjectWithoutPackageJson(directory);
|
|
68355
|
+
const synthesized = discoverProjectWithoutPackageJson(directory, options);
|
|
68116
68356
|
cachedProjectInfos.set(directory, synthesized);
|
|
68117
68357
|
return synthesized;
|
|
68118
68358
|
}
|
|
@@ -68174,8 +68414,8 @@ const discoverProject = (directory) => {
|
|
|
68174
68414
|
const tailwindVersion = tailwindcss.version;
|
|
68175
68415
|
const zodVersion = zod.version;
|
|
68176
68416
|
const projectName = packageJson.name ?? Path.basename(directory);
|
|
68177
|
-
const hasTypeScript =
|
|
68178
|
-
const sourceFileCount = countSourceFiles(directory);
|
|
68417
|
+
const hasTypeScript = isFile(Path.join(directory, "tsconfig.json"));
|
|
68418
|
+
const sourceFileCount = options.sourceFileCount ?? countSourceFiles(directory);
|
|
68179
68419
|
const hasReactNativeWorkspace = framework === "expo" || framework === "react-native" || workspaceFacts.hasReactNativeAwarePackage;
|
|
68180
68420
|
const expoVersion = hasReactNativeWorkspace ? resolveCatalogBackedDependencyVersion({
|
|
68181
68421
|
rootDirectory: directory,
|
|
@@ -68304,6 +68544,155 @@ const discoverProject = (directory) => {
|
|
|
68304
68544
|
* single source file has nothing to analyze.
|
|
68305
68545
|
*/
|
|
68306
68546
|
const isAnalyzableProject = (project) => project.reactVersion !== null || project.preactVersion !== null || project.sourceFileCount > 0;
|
|
68547
|
+
const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
68548
|
+
"effect/no-adjust-state-on-prop-change": "react-doctor/no-adjust-state-on-prop-change",
|
|
68549
|
+
"effect/no-chain-state-updates": "react-doctor/no-chain-state-updates",
|
|
68550
|
+
"effect/no-derived-state": "react-doctor/no-derived-state",
|
|
68551
|
+
"effect/no-event-handler": "react-doctor/no-event-handler",
|
|
68552
|
+
"effect/no-initialize-state": "react-doctor/no-initialize-state",
|
|
68553
|
+
"effect/no-pass-data-to-parent": "react-doctor/no-pass-data-to-parent",
|
|
68554
|
+
"effect/no-pass-live-state-to-parent": "react-doctor/no-pass-live-state-to-parent",
|
|
68555
|
+
"effect/no-reset-all-state-on-prop-change": "react-doctor/no-reset-all-state-on-prop-change",
|
|
68556
|
+
"jsx-a11y/alt-text": "react-doctor/alt-text",
|
|
68557
|
+
"jsx-a11y/anchor-ambiguous-text": "react-doctor/anchor-ambiguous-text",
|
|
68558
|
+
"jsx-a11y/anchor-has-content": "react-doctor/anchor-has-content",
|
|
68559
|
+
"jsx-a11y/anchor-is-valid": "react-doctor/anchor-is-valid",
|
|
68560
|
+
"jsx-a11y/aria-activedescendant-has-tabindex": "react-doctor/aria-activedescendant-has-tabindex",
|
|
68561
|
+
"jsx-a11y/aria-props": "react-doctor/aria-props",
|
|
68562
|
+
"jsx-a11y/aria-proptypes": "react-doctor/aria-proptypes",
|
|
68563
|
+
"jsx-a11y/aria-role": "react-doctor/aria-role",
|
|
68564
|
+
"jsx-a11y/aria-unsupported-elements": "react-doctor/aria-unsupported-elements",
|
|
68565
|
+
"jsx-a11y/autocomplete-valid": "react-doctor/autocomplete-valid",
|
|
68566
|
+
"jsx-a11y/click-events-have-key-events": "react-doctor/click-events-have-key-events",
|
|
68567
|
+
"jsx-a11y/control-has-associated-label": "react-doctor/control-has-associated-label",
|
|
68568
|
+
"jsx-a11y/heading-has-content": "react-doctor/heading-has-content",
|
|
68569
|
+
"jsx-a11y/html-has-lang": "react-doctor/html-has-lang",
|
|
68570
|
+
"jsx-a11y/iframe-has-title": "react-doctor/iframe-has-title",
|
|
68571
|
+
"jsx-a11y/img-redundant-alt": "react-doctor/img-redundant-alt",
|
|
68572
|
+
"jsx-a11y/interactive-supports-focus": "react-doctor/interactive-supports-focus",
|
|
68573
|
+
"jsx-a11y/label-has-associated-control": "react-doctor/label-has-associated-control",
|
|
68574
|
+
"jsx-a11y/lang": "react-doctor/lang",
|
|
68575
|
+
"jsx-a11y/media-has-caption": "react-doctor/media-has-caption",
|
|
68576
|
+
"jsx-a11y/mouse-events-have-key-events": "react-doctor/mouse-events-have-key-events",
|
|
68577
|
+
"jsx-a11y/no-access-key": "react-doctor/no-access-key",
|
|
68578
|
+
"jsx-a11y/no-aria-hidden-on-focusable": "react-doctor/no-aria-hidden-on-focusable",
|
|
68579
|
+
"jsx-a11y/no-autofocus": "react-doctor/no-autofocus",
|
|
68580
|
+
"jsx-a11y/no-distracting-elements": "react-doctor/no-distracting-elements",
|
|
68581
|
+
"jsx-a11y/no-interactive-element-to-noninteractive-role": "react-doctor/no-interactive-element-to-noninteractive-role",
|
|
68582
|
+
"jsx-a11y/no-noninteractive-element-interactions": "react-doctor/no-noninteractive-element-interactions",
|
|
68583
|
+
"jsx-a11y/no-noninteractive-element-to-interactive-role": "react-doctor/no-noninteractive-element-to-interactive-role",
|
|
68584
|
+
"jsx-a11y/no-noninteractive-tabindex": "react-doctor/no-noninteractive-tabindex",
|
|
68585
|
+
"jsx-a11y/no-redundant-roles": "react-doctor/no-redundant-roles",
|
|
68586
|
+
"jsx-a11y/no-static-element-interactions": "react-doctor/no-static-element-interactions",
|
|
68587
|
+
"jsx-a11y/prefer-tag-over-role": "react-doctor/prefer-tag-over-role",
|
|
68588
|
+
"jsx-a11y/role-has-required-aria-props": "react-doctor/role-has-required-aria-props",
|
|
68589
|
+
"jsx-a11y/role-supports-aria-props": "react-doctor/role-supports-aria-props",
|
|
68590
|
+
"jsx-a11y/scope": "react-doctor/scope",
|
|
68591
|
+
"jsx-a11y/tabindex-no-positive": "react-doctor/tabindex-no-positive",
|
|
68592
|
+
"react-hooks/exhaustive-deps": "react-doctor/exhaustive-deps",
|
|
68593
|
+
"react-hooks/rules-of-hooks": "react-doctor/rules-of-hooks",
|
|
68594
|
+
"react-perf/jsx-no-jsx-as-prop": "react-doctor/jsx-no-jsx-as-prop",
|
|
68595
|
+
"react-perf/jsx-no-new-array-as-prop": "react-doctor/jsx-no-new-array-as-prop",
|
|
68596
|
+
"react-perf/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
68597
|
+
"react-perf/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
68598
|
+
"react-refresh/only-export-components": "react-doctor/only-export-components",
|
|
68599
|
+
"react/button-has-type": "react-doctor/button-has-type",
|
|
68600
|
+
"react/checked-requires-onchange-or-readonly": "react-doctor/checked-requires-onchange-or-readonly",
|
|
68601
|
+
"react/display-name": "react-doctor/display-name",
|
|
68602
|
+
"react/exhaustive-deps": "react-doctor/exhaustive-deps",
|
|
68603
|
+
"react/forbid-component-props": "react-doctor/forbid-component-props",
|
|
68604
|
+
"react/forbid-dom-props": "react-doctor/forbid-dom-props",
|
|
68605
|
+
"react/forbid-elements": "react-doctor/forbid-elements",
|
|
68606
|
+
"react/forward-ref-uses-ref": "react-doctor/forward-ref-uses-ref",
|
|
68607
|
+
"react/hook-use-state": "react-doctor/hook-use-state",
|
|
68608
|
+
"react/iframe-missing-sandbox": "react-doctor/iframe-missing-sandbox",
|
|
68609
|
+
"react/jsx-boolean-value": "react-doctor/jsx-boolean-value",
|
|
68610
|
+
"react/jsx-curly-brace-presence": "react-doctor/jsx-curly-brace-presence",
|
|
68611
|
+
"react/jsx-filename-extension": "react-doctor/jsx-filename-extension",
|
|
68612
|
+
"react/jsx-fragments": "react-doctor/jsx-fragments",
|
|
68613
|
+
"react/jsx-handler-names": "react-doctor/jsx-handler-names",
|
|
68614
|
+
"react/jsx-key": "react-doctor/jsx-key",
|
|
68615
|
+
"react/jsx-max-depth": "react-doctor/jsx-max-depth",
|
|
68616
|
+
"react/jsx-no-comment-textnodes": "react-doctor/jsx-no-comment-textnodes",
|
|
68617
|
+
"react/jsx-no-constructed-context-values": "react-doctor/jsx-no-constructed-context-values",
|
|
68618
|
+
"react/jsx-no-duplicate-props": "react-doctor/jsx-no-duplicate-props",
|
|
68619
|
+
"react/jsx-no-jsx-as-prop": "react-doctor/jsx-no-jsx-as-prop",
|
|
68620
|
+
"react/jsx-no-new-array-as-prop": "react-doctor/jsx-no-new-array-as-prop",
|
|
68621
|
+
"react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
68622
|
+
"react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
68623
|
+
"react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
|
|
68624
|
+
"react/jsx-no-undef": "react-doctor/jsx-no-undef",
|
|
68625
|
+
"react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
|
|
68626
|
+
"react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
|
|
68627
|
+
"react/jsx-props-no-spread-multi": "react-doctor/jsx-props-no-spread-multi",
|
|
68628
|
+
"react/jsx-props-no-spreading": "react-doctor/jsx-props-no-spreading",
|
|
68629
|
+
"react/no-array-index-key": "react-doctor/no-array-index-key",
|
|
68630
|
+
"react/no-children-prop": "react-doctor/no-children-prop",
|
|
68631
|
+
"react/no-clone-element": "react-doctor/no-clone-element",
|
|
68632
|
+
"react/no-danger": "react-doctor/no-danger",
|
|
68633
|
+
"react/no-danger-with-children": "react-doctor/no-danger-with-children",
|
|
68634
|
+
"react/no-did-mount-set-state": "react-doctor/no-did-mount-set-state",
|
|
68635
|
+
"react/no-did-update-set-state": "react-doctor/no-did-update-set-state",
|
|
68636
|
+
"react/no-direct-mutation-state": "react-doctor/no-direct-mutation-state",
|
|
68637
|
+
"react/no-find-dom-node": "react-doctor/no-find-dom-node",
|
|
68638
|
+
"react/no-is-mounted": "react-doctor/no-is-mounted",
|
|
68639
|
+
"react/no-multi-comp": "react-doctor/no-multi-comp",
|
|
68640
|
+
"react/no-namespace": "react-doctor/no-namespace",
|
|
68641
|
+
"react/no-react-children": "react-doctor/no-react-children",
|
|
68642
|
+
"react/no-redundant-should-component-update": "react-doctor/no-redundant-should-component-update",
|
|
68643
|
+
"react/no-render-return-value": "react-doctor/no-render-return-value",
|
|
68644
|
+
"react/no-set-state": "react-doctor/no-set-state",
|
|
68645
|
+
"react/no-string-refs": "react-doctor/no-string-refs",
|
|
68646
|
+
"react/no-this-in-sfc": "react-doctor/no-this-in-sfc",
|
|
68647
|
+
"react/no-unescaped-entities": "react-doctor/no-unescaped-entities",
|
|
68648
|
+
"react/no-unknown-property": "react-doctor/no-unknown-property",
|
|
68649
|
+
"react/no-unsafe": "react-doctor/no-unsafe",
|
|
68650
|
+
"react/no-unstable-nested-components": "react-doctor/no-unstable-nested-components",
|
|
68651
|
+
"react/no-will-update-set-state": "react-doctor/no-will-update-set-state",
|
|
68652
|
+
"react/only-export-components": "react-doctor/only-export-components",
|
|
68653
|
+
"react/prefer-es6-class": "react-doctor/prefer-es6-class",
|
|
68654
|
+
"react/prefer-function-component": "react-doctor/prefer-function-component",
|
|
68655
|
+
"react/react-in-jsx-scope": "react-doctor/react-in-jsx-scope",
|
|
68656
|
+
"react/require-render-return": "react-doctor/require-render-return",
|
|
68657
|
+
"react/rules-of-hooks": "react-doctor/rules-of-hooks",
|
|
68658
|
+
"react/self-closing-comp": "react-doctor/self-closing-comp",
|
|
68659
|
+
"react/state-in-constructor": "react-doctor/state-in-constructor",
|
|
68660
|
+
"react/style-prop-object": "react-doctor/style-prop-object",
|
|
68661
|
+
"react/void-dom-elements-no-children": "react-doctor/void-dom-elements-no-children"
|
|
68662
|
+
};
|
|
68663
|
+
const NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS = /* @__PURE__ */ new Map();
|
|
68664
|
+
for (const [legacyRuleKey, nativeRuleKey] of Object.entries(LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY)) {
|
|
68665
|
+
const aliases = NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.get(nativeRuleKey) ?? [];
|
|
68666
|
+
aliases.push(legacyRuleKey);
|
|
68667
|
+
NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.set(nativeRuleKey, aliases);
|
|
68668
|
+
}
|
|
68669
|
+
const getLegacyRuleKeysForNative = (ruleKey) => NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.get(ruleKey) ?? [];
|
|
68670
|
+
const REACT_DOCTOR_RULE_KEY_PREFIX = "react-doctor/";
|
|
68671
|
+
const canonicalizeRuleKey = (ruleKey) => {
|
|
68672
|
+
const nativeRuleKey = LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY[ruleKey];
|
|
68673
|
+
return typeof nativeRuleKey === "string" ? nativeRuleKey : ruleKey;
|
|
68674
|
+
};
|
|
68675
|
+
const isReactDoctorShortIdOf = (bareRuleKey, qualifiedRuleKey) => !bareRuleKey.includes("/") && qualifiedRuleKey === `react-doctor/${bareRuleKey}`;
|
|
68676
|
+
/**
|
|
68677
|
+
* Canonicalizes a rule key as users write it in config: a legacy alias
|
|
68678
|
+
* (`react/jsx-key`) maps to its native key, and a bare short id (`no-eval`)
|
|
68679
|
+
* qualifies as `react-doctor/<id>` — mirroring `isSameRuleKey`'s matching —
|
|
68680
|
+
* so telemetry groups every spelling of one rule under one key.
|
|
68681
|
+
*/
|
|
68682
|
+
const canonicalizeUserRuleKey = (ruleKey) => {
|
|
68683
|
+
const nativeRuleKey = canonicalizeRuleKey(ruleKey);
|
|
68684
|
+
return nativeRuleKey.includes("/") ? nativeRuleKey : `${REACT_DOCTOR_RULE_KEY_PREFIX}${nativeRuleKey}`;
|
|
68685
|
+
};
|
|
68686
|
+
const isSameRuleKey = (candidateRuleKey, targetRuleKey) => {
|
|
68687
|
+
const canonicalCandidate = canonicalizeRuleKey(candidateRuleKey);
|
|
68688
|
+
const canonicalTarget = canonicalizeRuleKey(targetRuleKey);
|
|
68689
|
+
if (canonicalCandidate === canonicalTarget) return true;
|
|
68690
|
+
return isReactDoctorShortIdOf(canonicalCandidate, canonicalTarget) || isReactDoctorShortIdOf(canonicalTarget, canonicalCandidate);
|
|
68691
|
+
};
|
|
68692
|
+
const getEquivalentRuleKeys = (ruleKey) => {
|
|
68693
|
+
const nativeRuleKey = canonicalizeRuleKey(ruleKey);
|
|
68694
|
+
return [nativeRuleKey, ...getLegacyRuleKeysForNative(nativeRuleKey)];
|
|
68695
|
+
};
|
|
68307
68696
|
const messageFromUnknown = (error) => error instanceof Error ? error.message : String(error);
|
|
68308
68697
|
var InvalidGlobPatternError = class extends Error {
|
|
68309
68698
|
pattern;
|
|
@@ -68357,6 +68746,11 @@ const warnConfigIssue = (message) => {
|
|
|
68357
68746
|
};
|
|
68358
68747
|
const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
68359
68748
|
const collectStringList = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
68749
|
+
const hasMatchingRuleOverride = (ruleIds, ruleIdentifier) => {
|
|
68750
|
+
if (ruleIds.size === 0) return true;
|
|
68751
|
+
for (const ruleId of ruleIds) if (isSameRuleKey(ruleId, ruleIdentifier)) return true;
|
|
68752
|
+
return false;
|
|
68753
|
+
};
|
|
68360
68754
|
const validateOverrideEntry = (entry, index) => {
|
|
68361
68755
|
if (!isPlainObject(entry)) {
|
|
68362
68756
|
warnConfigIssue(`ignore.overrides[${index}] must be an object with { files, rules }; ignoring this entry.`);
|
|
@@ -68397,7 +68791,7 @@ const isDiagnosticIgnoredByOverrides = (diagnostic, rootDirectory, overrides) =>
|
|
|
68397
68791
|
if (overrides.length === 0) return false;
|
|
68398
68792
|
const relativeFilePath = toRelativePath(diagnostic.filePath, rootDirectory);
|
|
68399
68793
|
const ruleIdentifier = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
68400
|
-
return overrides.some((override) => override.filePatterns.some((pattern) => pattern.test(relativeFilePath)) && (override.ruleIds
|
|
68794
|
+
return overrides.some((override) => override.filePatterns.some((pattern) => pattern.test(relativeFilePath)) && hasMatchingRuleOverride(override.ruleIds, ruleIdentifier));
|
|
68401
68795
|
};
|
|
68402
68796
|
const SEVERITY_FOR_OVERRIDE = {
|
|
68403
68797
|
error: "error",
|
|
@@ -68432,155 +68826,6 @@ const buildRuleSeverityControls = (config) => {
|
|
|
68432
68826
|
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
68433
68827
|
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
68434
68828
|
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
68435
|
-
const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
68436
|
-
"effect/no-adjust-state-on-prop-change": "react-doctor/no-adjust-state-on-prop-change",
|
|
68437
|
-
"effect/no-chain-state-updates": "react-doctor/no-chain-state-updates",
|
|
68438
|
-
"effect/no-derived-state": "react-doctor/no-derived-state",
|
|
68439
|
-
"effect/no-event-handler": "react-doctor/no-event-handler",
|
|
68440
|
-
"effect/no-initialize-state": "react-doctor/no-initialize-state",
|
|
68441
|
-
"effect/no-pass-data-to-parent": "react-doctor/no-pass-data-to-parent",
|
|
68442
|
-
"effect/no-pass-live-state-to-parent": "react-doctor/no-pass-live-state-to-parent",
|
|
68443
|
-
"effect/no-reset-all-state-on-prop-change": "react-doctor/no-reset-all-state-on-prop-change",
|
|
68444
|
-
"jsx-a11y/alt-text": "react-doctor/alt-text",
|
|
68445
|
-
"jsx-a11y/anchor-ambiguous-text": "react-doctor/anchor-ambiguous-text",
|
|
68446
|
-
"jsx-a11y/anchor-has-content": "react-doctor/anchor-has-content",
|
|
68447
|
-
"jsx-a11y/anchor-is-valid": "react-doctor/anchor-is-valid",
|
|
68448
|
-
"jsx-a11y/aria-activedescendant-has-tabindex": "react-doctor/aria-activedescendant-has-tabindex",
|
|
68449
|
-
"jsx-a11y/aria-props": "react-doctor/aria-props",
|
|
68450
|
-
"jsx-a11y/aria-proptypes": "react-doctor/aria-proptypes",
|
|
68451
|
-
"jsx-a11y/aria-role": "react-doctor/aria-role",
|
|
68452
|
-
"jsx-a11y/aria-unsupported-elements": "react-doctor/aria-unsupported-elements",
|
|
68453
|
-
"jsx-a11y/autocomplete-valid": "react-doctor/autocomplete-valid",
|
|
68454
|
-
"jsx-a11y/click-events-have-key-events": "react-doctor/click-events-have-key-events",
|
|
68455
|
-
"jsx-a11y/control-has-associated-label": "react-doctor/control-has-associated-label",
|
|
68456
|
-
"jsx-a11y/heading-has-content": "react-doctor/heading-has-content",
|
|
68457
|
-
"jsx-a11y/html-has-lang": "react-doctor/html-has-lang",
|
|
68458
|
-
"jsx-a11y/iframe-has-title": "react-doctor/iframe-has-title",
|
|
68459
|
-
"jsx-a11y/img-redundant-alt": "react-doctor/img-redundant-alt",
|
|
68460
|
-
"jsx-a11y/interactive-supports-focus": "react-doctor/interactive-supports-focus",
|
|
68461
|
-
"jsx-a11y/label-has-associated-control": "react-doctor/label-has-associated-control",
|
|
68462
|
-
"jsx-a11y/lang": "react-doctor/lang",
|
|
68463
|
-
"jsx-a11y/media-has-caption": "react-doctor/media-has-caption",
|
|
68464
|
-
"jsx-a11y/mouse-events-have-key-events": "react-doctor/mouse-events-have-key-events",
|
|
68465
|
-
"jsx-a11y/no-access-key": "react-doctor/no-access-key",
|
|
68466
|
-
"jsx-a11y/no-aria-hidden-on-focusable": "react-doctor/no-aria-hidden-on-focusable",
|
|
68467
|
-
"jsx-a11y/no-autofocus": "react-doctor/no-autofocus",
|
|
68468
|
-
"jsx-a11y/no-distracting-elements": "react-doctor/no-distracting-elements",
|
|
68469
|
-
"jsx-a11y/no-interactive-element-to-noninteractive-role": "react-doctor/no-interactive-element-to-noninteractive-role",
|
|
68470
|
-
"jsx-a11y/no-noninteractive-element-interactions": "react-doctor/no-noninteractive-element-interactions",
|
|
68471
|
-
"jsx-a11y/no-noninteractive-element-to-interactive-role": "react-doctor/no-noninteractive-element-to-interactive-role",
|
|
68472
|
-
"jsx-a11y/no-noninteractive-tabindex": "react-doctor/no-noninteractive-tabindex",
|
|
68473
|
-
"jsx-a11y/no-redundant-roles": "react-doctor/no-redundant-roles",
|
|
68474
|
-
"jsx-a11y/no-static-element-interactions": "react-doctor/no-static-element-interactions",
|
|
68475
|
-
"jsx-a11y/prefer-tag-over-role": "react-doctor/prefer-tag-over-role",
|
|
68476
|
-
"jsx-a11y/role-has-required-aria-props": "react-doctor/role-has-required-aria-props",
|
|
68477
|
-
"jsx-a11y/role-supports-aria-props": "react-doctor/role-supports-aria-props",
|
|
68478
|
-
"jsx-a11y/scope": "react-doctor/scope",
|
|
68479
|
-
"jsx-a11y/tabindex-no-positive": "react-doctor/tabindex-no-positive",
|
|
68480
|
-
"react-hooks/exhaustive-deps": "react-doctor/exhaustive-deps",
|
|
68481
|
-
"react-hooks/rules-of-hooks": "react-doctor/rules-of-hooks",
|
|
68482
|
-
"react-perf/jsx-no-jsx-as-prop": "react-doctor/jsx-no-jsx-as-prop",
|
|
68483
|
-
"react-perf/jsx-no-new-array-as-prop": "react-doctor/jsx-no-new-array-as-prop",
|
|
68484
|
-
"react-perf/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
68485
|
-
"react-perf/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
68486
|
-
"react-refresh/only-export-components": "react-doctor/only-export-components",
|
|
68487
|
-
"react/button-has-type": "react-doctor/button-has-type",
|
|
68488
|
-
"react/checked-requires-onchange-or-readonly": "react-doctor/checked-requires-onchange-or-readonly",
|
|
68489
|
-
"react/display-name": "react-doctor/display-name",
|
|
68490
|
-
"react/exhaustive-deps": "react-doctor/exhaustive-deps",
|
|
68491
|
-
"react/forbid-component-props": "react-doctor/forbid-component-props",
|
|
68492
|
-
"react/forbid-dom-props": "react-doctor/forbid-dom-props",
|
|
68493
|
-
"react/forbid-elements": "react-doctor/forbid-elements",
|
|
68494
|
-
"react/forward-ref-uses-ref": "react-doctor/forward-ref-uses-ref",
|
|
68495
|
-
"react/hook-use-state": "react-doctor/hook-use-state",
|
|
68496
|
-
"react/iframe-missing-sandbox": "react-doctor/iframe-missing-sandbox",
|
|
68497
|
-
"react/jsx-boolean-value": "react-doctor/jsx-boolean-value",
|
|
68498
|
-
"react/jsx-curly-brace-presence": "react-doctor/jsx-curly-brace-presence",
|
|
68499
|
-
"react/jsx-filename-extension": "react-doctor/jsx-filename-extension",
|
|
68500
|
-
"react/jsx-fragments": "react-doctor/jsx-fragments",
|
|
68501
|
-
"react/jsx-handler-names": "react-doctor/jsx-handler-names",
|
|
68502
|
-
"react/jsx-key": "react-doctor/jsx-key",
|
|
68503
|
-
"react/jsx-max-depth": "react-doctor/jsx-max-depth",
|
|
68504
|
-
"react/jsx-no-comment-textnodes": "react-doctor/jsx-no-comment-textnodes",
|
|
68505
|
-
"react/jsx-no-constructed-context-values": "react-doctor/jsx-no-constructed-context-values",
|
|
68506
|
-
"react/jsx-no-duplicate-props": "react-doctor/jsx-no-duplicate-props",
|
|
68507
|
-
"react/jsx-no-jsx-as-prop": "react-doctor/jsx-no-jsx-as-prop",
|
|
68508
|
-
"react/jsx-no-new-array-as-prop": "react-doctor/jsx-no-new-array-as-prop",
|
|
68509
|
-
"react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
68510
|
-
"react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
68511
|
-
"react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
|
|
68512
|
-
"react/jsx-no-undef": "react-doctor/jsx-no-undef",
|
|
68513
|
-
"react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
|
|
68514
|
-
"react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
|
|
68515
|
-
"react/jsx-props-no-spread-multi": "react-doctor/jsx-props-no-spread-multi",
|
|
68516
|
-
"react/jsx-props-no-spreading": "react-doctor/jsx-props-no-spreading",
|
|
68517
|
-
"react/no-array-index-key": "react-doctor/no-array-index-key",
|
|
68518
|
-
"react/no-children-prop": "react-doctor/no-children-prop",
|
|
68519
|
-
"react/no-clone-element": "react-doctor/no-clone-element",
|
|
68520
|
-
"react/no-danger": "react-doctor/no-danger",
|
|
68521
|
-
"react/no-danger-with-children": "react-doctor/no-danger-with-children",
|
|
68522
|
-
"react/no-did-mount-set-state": "react-doctor/no-did-mount-set-state",
|
|
68523
|
-
"react/no-did-update-set-state": "react-doctor/no-did-update-set-state",
|
|
68524
|
-
"react/no-direct-mutation-state": "react-doctor/no-direct-mutation-state",
|
|
68525
|
-
"react/no-find-dom-node": "react-doctor/no-find-dom-node",
|
|
68526
|
-
"react/no-is-mounted": "react-doctor/no-is-mounted",
|
|
68527
|
-
"react/no-multi-comp": "react-doctor/no-multi-comp",
|
|
68528
|
-
"react/no-namespace": "react-doctor/no-namespace",
|
|
68529
|
-
"react/no-react-children": "react-doctor/no-react-children",
|
|
68530
|
-
"react/no-redundant-should-component-update": "react-doctor/no-redundant-should-component-update",
|
|
68531
|
-
"react/no-render-return-value": "react-doctor/no-render-return-value",
|
|
68532
|
-
"react/no-set-state": "react-doctor/no-set-state",
|
|
68533
|
-
"react/no-string-refs": "react-doctor/no-string-refs",
|
|
68534
|
-
"react/no-this-in-sfc": "react-doctor/no-this-in-sfc",
|
|
68535
|
-
"react/no-unescaped-entities": "react-doctor/no-unescaped-entities",
|
|
68536
|
-
"react/no-unknown-property": "react-doctor/no-unknown-property",
|
|
68537
|
-
"react/no-unsafe": "react-doctor/no-unsafe",
|
|
68538
|
-
"react/no-unstable-nested-components": "react-doctor/no-unstable-nested-components",
|
|
68539
|
-
"react/no-will-update-set-state": "react-doctor/no-will-update-set-state",
|
|
68540
|
-
"react/only-export-components": "react-doctor/only-export-components",
|
|
68541
|
-
"react/prefer-es6-class": "react-doctor/prefer-es6-class",
|
|
68542
|
-
"react/prefer-function-component": "react-doctor/prefer-function-component",
|
|
68543
|
-
"react/react-in-jsx-scope": "react-doctor/react-in-jsx-scope",
|
|
68544
|
-
"react/require-render-return": "react-doctor/require-render-return",
|
|
68545
|
-
"react/rules-of-hooks": "react-doctor/rules-of-hooks",
|
|
68546
|
-
"react/self-closing-comp": "react-doctor/self-closing-comp",
|
|
68547
|
-
"react/state-in-constructor": "react-doctor/state-in-constructor",
|
|
68548
|
-
"react/style-prop-object": "react-doctor/style-prop-object",
|
|
68549
|
-
"react/void-dom-elements-no-children": "react-doctor/void-dom-elements-no-children"
|
|
68550
|
-
};
|
|
68551
|
-
const NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS = /* @__PURE__ */ new Map();
|
|
68552
|
-
for (const [legacyRuleKey, nativeRuleKey] of Object.entries(LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY)) {
|
|
68553
|
-
const aliases = NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.get(nativeRuleKey) ?? [];
|
|
68554
|
-
aliases.push(legacyRuleKey);
|
|
68555
|
-
NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.set(nativeRuleKey, aliases);
|
|
68556
|
-
}
|
|
68557
|
-
const getLegacyRuleKeysForNative = (ruleKey) => NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.get(ruleKey) ?? [];
|
|
68558
|
-
const REACT_DOCTOR_RULE_KEY_PREFIX = "react-doctor/";
|
|
68559
|
-
const canonicalizeRuleKey = (ruleKey) => {
|
|
68560
|
-
const nativeRuleKey = LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY[ruleKey];
|
|
68561
|
-
return typeof nativeRuleKey === "string" ? nativeRuleKey : ruleKey;
|
|
68562
|
-
};
|
|
68563
|
-
const isReactDoctorShortIdOf = (bareRuleKey, qualifiedRuleKey) => !bareRuleKey.includes("/") && qualifiedRuleKey === `react-doctor/${bareRuleKey}`;
|
|
68564
|
-
/**
|
|
68565
|
-
* Canonicalizes a rule key as users write it in config: a legacy alias
|
|
68566
|
-
* (`react/jsx-key`) maps to its native key, and a bare short id (`no-eval`)
|
|
68567
|
-
* qualifies as `react-doctor/<id>` — mirroring `isSameRuleKey`'s matching —
|
|
68568
|
-
* so telemetry groups every spelling of one rule under one key.
|
|
68569
|
-
*/
|
|
68570
|
-
const canonicalizeUserRuleKey = (ruleKey) => {
|
|
68571
|
-
const nativeRuleKey = canonicalizeRuleKey(ruleKey);
|
|
68572
|
-
return nativeRuleKey.includes("/") ? nativeRuleKey : `${REACT_DOCTOR_RULE_KEY_PREFIX}${nativeRuleKey}`;
|
|
68573
|
-
};
|
|
68574
|
-
const isSameRuleKey = (candidateRuleKey, targetRuleKey) => {
|
|
68575
|
-
const canonicalCandidate = canonicalizeRuleKey(candidateRuleKey);
|
|
68576
|
-
const canonicalTarget = canonicalizeRuleKey(targetRuleKey);
|
|
68577
|
-
if (canonicalCandidate === canonicalTarget) return true;
|
|
68578
|
-
return isReactDoctorShortIdOf(canonicalCandidate, canonicalTarget) || isReactDoctorShortIdOf(canonicalTarget, canonicalCandidate);
|
|
68579
|
-
};
|
|
68580
|
-
const getEquivalentRuleKeys = (ruleKey) => {
|
|
68581
|
-
const nativeRuleKey = canonicalizeRuleKey(ruleKey);
|
|
68582
|
-
return [nativeRuleKey, ...getLegacyRuleKeysForNative(nativeRuleKey)];
|
|
68583
|
-
};
|
|
68584
68829
|
const stripDescriptionTail = (ruleList) => {
|
|
68585
68830
|
const descriptionMatch = ruleList.match(/(?:^|\s)--\s/);
|
|
68586
68831
|
if (!descriptionMatch || descriptionMatch.index === void 0) return ruleList;
|
|
@@ -69227,12 +69472,8 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
69227
69472
|
if (rule.tags.includes("migration-hint")) return false;
|
|
69228
69473
|
return getFileContext(diagnostic.filePath) !== "production";
|
|
69229
69474
|
};
|
|
69230
|
-
const
|
|
69231
|
-
for (const
|
|
69232
|
-
return false;
|
|
69233
|
-
};
|
|
69234
|
-
const isAppOnlyRule = (ruleIdentifier) => {
|
|
69235
|
-
for (const appOnlyRuleKey of APP_ONLY_RULE_KEYS) if (isSameRuleKey(appOnlyRuleKey, ruleIdentifier)) return true;
|
|
69475
|
+
const matchesRuleKey = (ruleIdentifier, candidates) => {
|
|
69476
|
+
for (const candidate of candidates) if (isSameRuleKey(candidate, ruleIdentifier)) return true;
|
|
69236
69477
|
return false;
|
|
69237
69478
|
};
|
|
69238
69479
|
const isRnRawTextSuppressedByConfig = (diagnostic) => {
|
|
@@ -69274,12 +69515,13 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
69274
69515
|
if (explicitSeverityOverride === "off") return suppress(current, "config");
|
|
69275
69516
|
if (explicitSeverityOverride !== void 0) current = restampSeverity(current, explicitSeverityOverride);
|
|
69276
69517
|
}
|
|
69518
|
+
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
69277
69519
|
if (explicitRuleOverride === void 0) {
|
|
69278
|
-
if (
|
|
69520
|
+
if (matchesRuleKey(ruleIdentifier, APP_ONLY_RULE_KEYS) && isLibraryFile(current.filePath)) return null;
|
|
69279
69521
|
}
|
|
69280
69522
|
if (!showWarnings && current.severity === "warning" && explicitSeverityOverride !== "warn") return null;
|
|
69281
69523
|
if (userConfig) {
|
|
69282
|
-
if (
|
|
69524
|
+
if (matchesRuleKey(ruleIdentifier, ignoredRules)) return suppress(current, "config");
|
|
69283
69525
|
if (isFileIgnoredByPatterns(current.filePath, rootDirectory, ignoredFilePatterns)) return null;
|
|
69284
69526
|
if (isDiagnosticIgnoredByOverrides(current, rootDirectory, compiledOverrides)) return suppress(current, "override");
|
|
69285
69527
|
if (isRnRawTextSuppressedByConfig(current)) return null;
|
|
@@ -69288,7 +69530,6 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
69288
69530
|
if (respectInlineDisables && current.line > 0) {
|
|
69289
69531
|
const lines = getFileLines(current.filePath);
|
|
69290
69532
|
if (lines) {
|
|
69291
|
-
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
69292
69533
|
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
69293
69534
|
if (evaluation.isSuppressed) return suppress(current, evaluation.isForeignDirective ? "foreign-inline" : "inline");
|
|
69294
69535
|
if (evaluation.nearMissHint) current = {
|
|
@@ -69564,7 +69805,8 @@ const readSystemFacts$1 = () => ({
|
|
|
69564
69805
|
/**
|
|
69565
69806
|
* Auto lint-worker count: the smaller of the (cgroup-CPU-aware) core count and
|
|
69566
69807
|
* the number of `PER_WORKER_MEM_BUDGET_BYTES` workers that fit in available
|
|
69567
|
-
* memory,
|
|
69808
|
+
* memory, and the measured automatic parallel-efficiency ceiling, then clamped
|
|
69809
|
+
* to `[MIN, HARD_MAX]` by `resolveScanConcurrency`.
|
|
69568
69810
|
*
|
|
69569
69811
|
* `os.availableParallelism()` already respects cgroup CPU quotas, so the core
|
|
69570
69812
|
* term needs no help. Available memory is `os.totalmem()` floored by the cgroup
|
|
@@ -69580,7 +69822,7 @@ const readSystemFacts$1 = () => ({
|
|
|
69580
69822
|
const resolveAutoScanConcurrency = (facts = readSystemFacts$1()) => {
|
|
69581
69823
|
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
69582
69824
|
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / PER_WORKER_MEM_BUDGET_BYTES);
|
|
69583
|
-
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
69825
|
+
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers, 10));
|
|
69584
69826
|
};
|
|
69585
69827
|
const resolveLintBatchOrdering = () => {
|
|
69586
69828
|
return process.env["REACT_DOCTOR_LINT_BATCH_ORDERING"]?.trim().toLowerCase() === "arrival" ? "arrival" : "cost";
|
|
@@ -69593,24 +69835,22 @@ const resolveLintBatchOrdering = () => {
|
|
|
69593
69835
|
*/
|
|
69594
69836
|
var OxlintSpawnTimeoutMs = class extends Reference("react-doctor/OxlintSpawnTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_OXLINT_SPAWN_TIMEOUT_MS", OXLINT_SPAWN_TIMEOUT_MS) }) {};
|
|
69595
69837
|
/**
|
|
69596
|
-
* Effect-side cap on the lint phase.
|
|
69597
|
-
*
|
|
69838
|
+
* Optional Effect-side cap on the lint phase. Unbounded unless explicitly
|
|
69839
|
+
* configured through the env var or a test layer.
|
|
69598
69840
|
* Tests override via `Layer.succeed(LintPhaseTimeoutMs, ...)`.
|
|
69599
69841
|
*/
|
|
69600
|
-
var LintPhaseTimeoutMs = class extends Reference("react-doctor/LintPhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_LINT_PHASE_TIMEOUT_MS",
|
|
69842
|
+
var LintPhaseTimeoutMs = class extends Reference("react-doctor/LintPhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_LINT_PHASE_TIMEOUT_MS", null) }) {};
|
|
69601
69843
|
/**
|
|
69602
|
-
* Effect-side cap on the dead-code phase
|
|
69603
|
-
*
|
|
69604
|
-
* type-heavy projects; tests override via
|
|
69844
|
+
* Optional Effect-side cap on the dead-code phase. Unbounded unless explicitly
|
|
69845
|
+
* configured through the env var or a test layer. Tests override via
|
|
69605
69846
|
* `Layer.succeed(DeadCodePhaseTimeoutMs, ...)`.
|
|
69606
69847
|
*/
|
|
69607
|
-
var DeadCodePhaseTimeoutMs = class extends Reference("react-doctor/DeadCodePhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_DEAD_CODE_PHASE_TIMEOUT_MS",
|
|
69848
|
+
var DeadCodePhaseTimeoutMs = class extends Reference("react-doctor/DeadCodePhaseTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_DEAD_CODE_PHASE_TIMEOUT_MS", null) }) {};
|
|
69608
69849
|
/**
|
|
69609
|
-
*
|
|
69610
|
-
*
|
|
69611
|
-
* large repos; tests override via `Layer.succeed(ScanDeadlineMs, ...)`.
|
|
69850
|
+
* Optional overall scan deadline. Unbounded unless explicitly configured
|
|
69851
|
+
* through the env var or a test layer.
|
|
69612
69852
|
*/
|
|
69613
|
-
var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SCAN_DEADLINE_MS",
|
|
69853
|
+
var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SCAN_DEADLINE_MS", null) }) {};
|
|
69614
69854
|
/**
|
|
69615
69855
|
* Wall-clock budget for the supply-chain check when it runs on a background
|
|
69616
69856
|
* fiber overlapping the lint pass. Reads from the env var on startup so the
|
|
@@ -69683,14 +69923,14 @@ var DeadCodeOverlap = class extends Reference("react-doctor/DeadCodeOverlap", {
|
|
|
69683
69923
|
/**
|
|
69684
69924
|
* How the full-scan lint pass plans its file batches. `"cost"` (the default)
|
|
69685
69925
|
* builds size-balanced LPT batches (`planLintBatches`): the same mandatory
|
|
69686
|
-
* batch count as greedy chunking
|
|
69687
|
-
*
|
|
69926
|
+
* batch count as greedy chunking, but every batch gets an even share of files
|
|
69927
|
+
* AND bytes, so no full chunk is a straggler while
|
|
69688
69928
|
* the remainder-batch worker idles — and the heavy files are SPREAD across
|
|
69689
69929
|
* batches, the precondition the old sort-desc-then-chunk-100 `cost` mode
|
|
69690
69930
|
* lacked (it packed the heaviest files into one wave-1 straggler batch,
|
|
69691
69931
|
* measurably regressing size-skewed repos, which is why it never earned the
|
|
69692
69932
|
* default). `"arrival"` (`REACT_DOCTOR_LINT_BATCH_ORDERING=arrival`) is the
|
|
69693
|
-
* rollback hatch to plain greedy
|
|
69933
|
+
* rollback hatch to plain greedy fixed-size chunking in discovery order. Tests
|
|
69694
69934
|
* override via `Layer.succeed(LintBatchOrdering, ...)`. Diff / staged scans
|
|
69695
69935
|
* never reach this — they pass user-scoped `includePaths` that skip discovery
|
|
69696
69936
|
* and stay in arrival order; only the full-scan branch reads it.
|
|
@@ -69785,6 +70025,7 @@ const DEFAULT_SURFACE_EXCLUDED_TAGS = {
|
|
|
69785
70025
|
score: ["design"],
|
|
69786
70026
|
ciFailure: ["design"]
|
|
69787
70027
|
};
|
|
70028
|
+
const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
69788
70029
|
const VALID_RULE_SEVERITIES = [
|
|
69789
70030
|
"error",
|
|
69790
70031
|
"warn",
|
|
@@ -69827,7 +70068,6 @@ const SURFACE_CONTROL_FIELD_NAMES = [
|
|
|
69827
70068
|
];
|
|
69828
70069
|
const isIncludedFileContext = (value) => VALID_INCLUDED_FILE_CONTEXTS.some((fileContext) => fileContext === value);
|
|
69829
70070
|
const SEVERITY_FIELD_NAMES = ["rules", "categories"];
|
|
69830
|
-
const isPlainObject$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
69831
70071
|
const formatType = (value) => typeof value === "string" ? `"${value}"` : typeof value;
|
|
69832
70072
|
const isRuleSeverity = (value) => typeof value === "string" && VALID_RULE_SEVERITIES.includes(value);
|
|
69833
70073
|
const coerceMaybeBooleanString = (fieldName, value) => {
|
|
@@ -69854,8 +70094,45 @@ const validateStringArrayField = (fieldName, value) => {
|
|
|
69854
70094
|
return false;
|
|
69855
70095
|
});
|
|
69856
70096
|
};
|
|
70097
|
+
const validateIgnoreOverride = (rawOverride, index) => {
|
|
70098
|
+
const fieldName = `ignore.overrides[${index}]`;
|
|
70099
|
+
if (!isRecord$1(rawOverride)) {
|
|
70100
|
+
warnConfigIssue(`config field "${fieldName}" must be an object (got ${typeof rawOverride}); ignoring this override.`);
|
|
70101
|
+
return;
|
|
70102
|
+
}
|
|
70103
|
+
const files = validateStringArrayField(`${fieldName}.files`, rawOverride.files);
|
|
70104
|
+
if (files === void 0) return void 0;
|
|
70105
|
+
const validatedOverride = { files };
|
|
70106
|
+
if (rawOverride.rules !== void 0) {
|
|
70107
|
+
const rules = validateStringArrayField(`${fieldName}.rules`, rawOverride.rules);
|
|
70108
|
+
if (rules !== void 0) validatedOverride.rules = rules;
|
|
70109
|
+
}
|
|
70110
|
+
return validatedOverride;
|
|
70111
|
+
};
|
|
70112
|
+
const validateIgnoreField = (rawIgnore) => {
|
|
70113
|
+
if (!isRecord$1(rawIgnore)) {
|
|
70114
|
+
warnConfigIssue(`config field "ignore" must be an object (got ${typeof rawIgnore}); ignoring this field.`);
|
|
70115
|
+
return;
|
|
70116
|
+
}
|
|
70117
|
+
const validatedIgnore = {};
|
|
70118
|
+
for (const fieldName of [
|
|
70119
|
+
"rules",
|
|
70120
|
+
"files",
|
|
70121
|
+
"tags"
|
|
70122
|
+
]) {
|
|
70123
|
+
if (rawIgnore[fieldName] === void 0) continue;
|
|
70124
|
+
const validatedValues = validateStringArrayField(`ignore.${fieldName}`, rawIgnore[fieldName]);
|
|
70125
|
+
if (validatedValues !== void 0) validatedIgnore[fieldName] = validatedValues;
|
|
70126
|
+
}
|
|
70127
|
+
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.`);
|
|
70128
|
+
else validatedIgnore.overrides = rawIgnore.overrides.flatMap((rawOverride, index) => {
|
|
70129
|
+
const validatedOverride = validateIgnoreOverride(rawOverride, index);
|
|
70130
|
+
return validatedOverride ? [validatedOverride] : [];
|
|
70131
|
+
});
|
|
70132
|
+
return validatedIgnore;
|
|
70133
|
+
};
|
|
69857
70134
|
const validateSurfaceControls = (surface, rawControls) => {
|
|
69858
|
-
if (!
|
|
70135
|
+
if (!isRecord$1(rawControls)) {
|
|
69859
70136
|
warnConfigIssue(`config field "surfaces.${surface}" must be an object (got ${typeof rawControls}); ignoring this surface.`);
|
|
69860
70137
|
return;
|
|
69861
70138
|
}
|
|
@@ -69879,7 +70156,7 @@ const validateSurfaceControls = (surface, rawControls) => {
|
|
|
69879
70156
|
return validatedSurfaceControls;
|
|
69880
70157
|
};
|
|
69881
70158
|
const validateSurfacesField = (rawSurfaces) => {
|
|
69882
|
-
if (!
|
|
70159
|
+
if (!isRecord$1(rawSurfaces)) {
|
|
69883
70160
|
warnConfigIssue(`config field "surfaces" must be an object (got ${typeof rawSurfaces}); ignoring this field.`);
|
|
69884
70161
|
return;
|
|
69885
70162
|
}
|
|
@@ -69895,7 +70172,7 @@ const validateSurfacesField = (rawSurfaces) => {
|
|
|
69895
70172
|
return validated;
|
|
69896
70173
|
};
|
|
69897
70174
|
const validateSeverityMap = (fieldName, rawMap, keysAreCategories = false) => {
|
|
69898
|
-
if (!
|
|
70175
|
+
if (!isRecord$1(rawMap)) {
|
|
69899
70176
|
warnConfigIssue(`config field "${fieldName}" must be an object (got ${typeof rawMap}); ignoring this field.`);
|
|
69900
70177
|
return;
|
|
69901
70178
|
}
|
|
@@ -69929,6 +70206,7 @@ const validateConfigTypes = (config) => {
|
|
|
69929
70206
|
for (const fieldName of BOOLEAN_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => coerceMaybeBooleanString(fieldName, value));
|
|
69930
70207
|
for (const fieldName of STRING_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateString(fieldName, value));
|
|
69931
70208
|
for (const fieldName of STRING_ARRAY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateStringArrayField(fieldName, value));
|
|
70209
|
+
applyFieldValidator(config, validated, "ignore", validateIgnoreField);
|
|
69932
70210
|
applyFieldValidator(config, validated, "surfaces", validateSurfacesField);
|
|
69933
70211
|
for (const fieldName of SEVERITY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateSeverityMap(fieldName, value, fieldName === "categories"));
|
|
69934
70212
|
applyFieldValidator(config, validated, "plugins", (value) => validateStringArrayField("plugins", value));
|
|
@@ -70256,6 +70534,10 @@ const dedupeRelatedDiagnostics = (diagnostics) => {
|
|
|
70256
70534
|
}
|
|
70257
70535
|
return uniqueDiagnostics;
|
|
70258
70536
|
};
|
|
70537
|
+
const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
|
|
70538
|
+
const relativePath = Path.relative(parentAbsolutePath, childAbsolutePath);
|
|
70539
|
+
return Boolean(relativePath) && !relativePath.startsWith("..") && !Path.isAbsolute(relativePath);
|
|
70540
|
+
};
|
|
70259
70541
|
const compareStrings = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
70260
70542
|
const sortDiagnosticsStable = (diagnostics) => [...diagnostics].sort((left, right) => compareStrings(left.filePath, right.filePath) || left.line - right.line || left.column - right.column || compareStrings(left.plugin, right.plugin) || compareStrings(left.rule, right.rule) || compareStrings(left.severity, right.severity) || compareStrings(left.message, right.message));
|
|
70261
70543
|
const getDirectDependencyNames = (packageJson) => new Set([...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.devDependencies ?? {})]);
|
|
@@ -71617,7 +71899,7 @@ const collectScriptMotionEvidence = (sourceFile, typeChecker, program) => {
|
|
|
71617
71899
|
if (calleeEvidence.isReducedMotionHook && isExpressionValueConsumed(node)) evidence.hasReducedMotionHandling = true;
|
|
71618
71900
|
}
|
|
71619
71901
|
if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
|
|
71620
|
-
const tagEvidence = ts.
|
|
71902
|
+
const tagEvidence = ts.isIdentifier(node.tagName) || ts.isPropertyAccessExpression(node.tagName) ? resolveMotionExpressionEvidence(node.tagName, typeChecker, program) : EMPTY_MOTION_EXPRESSION_EVIDENCE;
|
|
71621
71903
|
if (tagEvidence.isMotionComponent || tagEvidence.isMotionComponentFactory) evidence.hasMotionUse = true;
|
|
71622
71904
|
if (tagEvidence.isMotionConfig && jsxElementHasReducedMotionConfiguration(node.attributes, typeChecker)) evidence.hasReducedMotionHandling = true;
|
|
71623
71905
|
}
|
|
@@ -71832,7 +72114,7 @@ const readScannedFile = (candidate) => {
|
|
|
71832
72114
|
return null;
|
|
71833
72115
|
}
|
|
71834
72116
|
};
|
|
71835
|
-
function* collectSecurityScanFiles(rootDirectory) {
|
|
72117
|
+
function* collectSecurityScanFiles(rootDirectory, excludedDirectories = /* @__PURE__ */ new Set()) {
|
|
71836
72118
|
const priorityCandidates = [];
|
|
71837
72119
|
const artifactCandidates = [];
|
|
71838
72120
|
const otherCandidates = [];
|
|
@@ -71847,7 +72129,7 @@ function* collectSecurityScanFiles(rootDirectory) {
|
|
|
71847
72129
|
for (const entry of readDirectoryEntries(current.absolutePath)) {
|
|
71848
72130
|
const absolutePath = Path.join(current.absolutePath, entry.name);
|
|
71849
72131
|
if (entry.isDirectory()) {
|
|
71850
|
-
if (!SKIPPED_DIRECTORY_NAMES.has(entry.name)) stack.push({
|
|
72132
|
+
if (!SKIPPED_DIRECTORY_NAMES.has(entry.name) && !excludedDirectories.has(Path.resolve(absolutePath))) stack.push({
|
|
71851
72133
|
absolutePath,
|
|
71852
72134
|
depth: current.depth + 1
|
|
71853
72135
|
});
|
|
@@ -72028,10 +72310,8 @@ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledWhe
|
|
|
72028
72310
|
}
|
|
72029
72311
|
return true;
|
|
72030
72312
|
};
|
|
72313
|
+
const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
|
|
72031
72314
|
const shouldEnableRuleByDefaultStatus = (options) => options.defaultEnabled !== false || options.hasExplicitOverride === true || options.includeTagDefaults && options.hasIncludedTags;
|
|
72032
|
-
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
72033
|
-
setImmediate(resolve);
|
|
72034
|
-
});
|
|
72035
72315
|
const createSecurityScanSession = (rootDirectory, options) => {
|
|
72036
72316
|
const capabilities = options.project ? getCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
72037
72317
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
@@ -72084,19 +72364,35 @@ const createSecurityScanSession = (rootDirectory, options) => {
|
|
|
72084
72364
|
};
|
|
72085
72365
|
};
|
|
72086
72366
|
const checkSecurityScanCooperative = async (rootDirectory, options = {}) => {
|
|
72367
|
+
const throwIfScanAborted = () => {
|
|
72368
|
+
options.signal?.throwIfAborted();
|
|
72369
|
+
};
|
|
72370
|
+
const didReachDeadline = () => {
|
|
72371
|
+
const hasDeadlineElapsed = options.deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(options.deadlineEpochMs) === 0;
|
|
72372
|
+
if (hasDeadlineElapsed) options.onDeadlineExceeded?.();
|
|
72373
|
+
return hasDeadlineElapsed;
|
|
72374
|
+
};
|
|
72375
|
+
throwIfScanAborted();
|
|
72087
72376
|
const session = createSecurityScanSession(rootDirectory, options);
|
|
72088
72377
|
if (session === null) return [];
|
|
72378
|
+
if (didReachDeadline()) return [];
|
|
72089
72379
|
let sliceStartedAt = performance.now();
|
|
72090
|
-
for (const file of collectSecurityScanFiles(rootDirectory)) {
|
|
72380
|
+
for (const file of collectSecurityScanFiles(rootDirectory, options.excludedDirectories)) {
|
|
72381
|
+
throwIfScanAborted();
|
|
72382
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
72091
72383
|
if (file === null) {
|
|
72092
72384
|
if (performance.now() - sliceStartedAt >= 12) {
|
|
72093
72385
|
await yieldToEventLoop();
|
|
72386
|
+
throwIfScanAborted();
|
|
72387
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
72094
72388
|
sliceStartedAt = performance.now();
|
|
72095
72389
|
}
|
|
72096
72390
|
continue;
|
|
72097
72391
|
}
|
|
72098
72392
|
for (const _ruleStep of session.scanFileByRule(file)) if (performance.now() - sliceStartedAt >= 12) {
|
|
72099
72393
|
await yieldToEventLoop();
|
|
72394
|
+
throwIfScanAborted();
|
|
72395
|
+
if (didReachDeadline()) return session.diagnostics;
|
|
72100
72396
|
sliceStartedAt = performance.now();
|
|
72101
72397
|
}
|
|
72102
72398
|
}
|
|
@@ -72275,25 +72571,18 @@ const collectIgnorePatterns = (rootDirectory) => {
|
|
|
72275
72571
|
cachedPatternsByRoot.set(rootDirectory, patterns);
|
|
72276
72572
|
return patterns;
|
|
72277
72573
|
};
|
|
72278
|
-
const
|
|
72279
|
-
const KNIP_JSON_FILENAME = "knip.json";
|
|
72280
|
-
const readJsonFileSafe = (filePath) => {
|
|
72281
|
-
let rawContents;
|
|
72282
|
-
try {
|
|
72283
|
-
rawContents = fs.readFileSync(filePath, "utf-8");
|
|
72284
|
-
} catch {
|
|
72285
|
-
return null;
|
|
72286
|
-
}
|
|
72574
|
+
const failOpenReadJson = (filePath, fallback) => {
|
|
72287
72575
|
try {
|
|
72288
|
-
return JSON.parse(
|
|
72576
|
+
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
72289
72577
|
} catch {
|
|
72290
|
-
return
|
|
72578
|
+
return fallback;
|
|
72291
72579
|
}
|
|
72292
72580
|
};
|
|
72581
|
+
const KNIP_JSON_FILENAME = "knip.json";
|
|
72293
72582
|
const readKnipConfig = (rootDirectory) => {
|
|
72294
|
-
const knipJson =
|
|
72583
|
+
const knipJson = failOpenReadJson(path.join(rootDirectory, KNIP_JSON_FILENAME), null);
|
|
72295
72584
|
if (isRecord$1(knipJson)) return knipJson;
|
|
72296
|
-
const packageJson =
|
|
72585
|
+
const packageJson = failOpenReadJson(path.join(rootDirectory, "package.json"), null);
|
|
72297
72586
|
const packageKnipConfig = isRecord$1(packageJson) ? packageJson.knip : null;
|
|
72298
72587
|
return isRecord$1(packageKnipConfig) ? packageKnipConfig : null;
|
|
72299
72588
|
};
|
|
@@ -72341,13 +72630,6 @@ const atomicWriteJson = (filePath, value) => {
|
|
|
72341
72630
|
return;
|
|
72342
72631
|
}
|
|
72343
72632
|
};
|
|
72344
|
-
const failOpenReadJson = (filePath, fallback) => {
|
|
72345
|
-
try {
|
|
72346
|
-
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
72347
|
-
} catch {
|
|
72348
|
-
return fallback;
|
|
72349
|
-
}
|
|
72350
|
-
};
|
|
72351
72633
|
const hashFileContents = (filePath) => {
|
|
72352
72634
|
try {
|
|
72353
72635
|
return crypto.createHash("sha1").update(NFS.readFileSync(filePath)).digest("hex");
|
|
@@ -73026,7 +73308,6 @@ const isDiagnosticOnSurface = (diagnostic, surface, config) => {
|
|
|
73026
73308
|
return true;
|
|
73027
73309
|
};
|
|
73028
73310
|
const filterDiagnosticsForSurface = (diagnostics, surface, config) => diagnostics.filter((diagnostic) => isDiagnosticOnSurface(diagnostic, surface, config));
|
|
73029
|
-
const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
|
|
73030
73311
|
/**
|
|
73031
73312
|
* Budget for the dead-code phase, scaled to the work. deslop's graph build is
|
|
73032
73313
|
* CPU-bound and roughly linear in file count, so a fixed 120s cap is too tight
|
|
@@ -73034,25 +73315,28 @@ const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs
|
|
|
73034
73315
|
* tipped over by any concurrent load — silently dropping every dead-code
|
|
73035
73316
|
* finding. Scaling the budget with file count (and inversely with the core
|
|
73036
73317
|
* share when overlapped) lets the pass complete, while the ceiling still
|
|
73037
|
-
* reclaims a genuinely wedged worker.
|
|
73038
|
-
* and the Effect-side phase backstop that sits a margin above it.
|
|
73318
|
+
* reclaims a genuinely wedged worker.
|
|
73039
73319
|
*/
|
|
73040
73320
|
const resolveDeadCodeTimeout = (input) => {
|
|
73041
73321
|
const coreShareFactor = Math.max(1, input.fullConcurrency / Math.max(1, input.deadCodeConcurrency));
|
|
73042
|
-
|
|
73043
|
-
return {
|
|
73044
|
-
workerTimeoutMs,
|
|
73045
|
-
phaseTimeoutMs: workerTimeoutMs + DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS
|
|
73046
|
-
};
|
|
73322
|
+
return { workerTimeoutMs: Math.min(DEAD_CODE_TIMEOUT_CEILING_MS, Math.max(DEAD_CODE_WORKER_TIMEOUT_MS, Math.ceil(input.sourceFileCount * 30 * coreShareFactor))) };
|
|
73047
73323
|
};
|
|
73048
|
-
const resolveLintIncludePaths = (rootDirectory, userConfig) => {
|
|
73324
|
+
const resolveLintIncludePaths = (rootDirectory, userConfig, sourceFiles) => {
|
|
73049
73325
|
if (!Array.isArray(userConfig?.ignore?.files) || userConfig.ignore.files.length === 0) return;
|
|
73050
73326
|
const ignoredPatterns = compileIgnoredFilePatterns(userConfig);
|
|
73051
|
-
return listSourceFiles(rootDirectory).filter((filePath) => {
|
|
73327
|
+
return (sourceFiles ?? listSourceFiles(rootDirectory)).filter((filePath) => {
|
|
73052
73328
|
if (!isLintableSourceFile(filePath)) return false;
|
|
73053
73329
|
return !isFileIgnoredByPatterns(filePath, rootDirectory, ignoredPatterns);
|
|
73054
73330
|
});
|
|
73055
73331
|
};
|
|
73332
|
+
const filterPathsOutsideDirectories = (input) => {
|
|
73333
|
+
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}/`)));
|
|
73334
|
+
if (excludedPrefixes.length === 0) return [...input.relativePaths];
|
|
73335
|
+
return input.relativePaths.filter((relativePath) => {
|
|
73336
|
+
const normalizedRelativePath = relativePath.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
73337
|
+
return !excludedPrefixes.some((excludedPrefix) => normalizedRelativePath === excludedPrefix || normalizedRelativePath.startsWith(`${excludedPrefix}/`));
|
|
73338
|
+
});
|
|
73339
|
+
};
|
|
73056
73340
|
var Config = class Config extends Service()("react-doctor/Config") {
|
|
73057
73341
|
static layerNode = effect(Config, gen(function* () {
|
|
73058
73342
|
const cache = yield* make$3({
|
|
@@ -73114,6 +73398,7 @@ var Files = class Files extends Service()("react-doctor/Files") {
|
|
|
73114
73398
|
static layerNode = succeed$3(Files, Files.of({
|
|
73115
73399
|
readLines: (input) => sync(() => createNodeReadFileLinesSync(input.rootDirectory)(input.filePath)),
|
|
73116
73400
|
listSourceFiles: (rootDirectory) => sync(() => listSourceFiles(rootDirectory)),
|
|
73401
|
+
listSourceFilesCooperative: (input) => promise(() => listSourceFilesCooperative(input.rootDirectory, input.signal)),
|
|
73117
73402
|
isFile: (filePath) => sync(() => isFile(filePath)),
|
|
73118
73403
|
isDirectory: (filePath) => sync(() => isDirectory(filePath))
|
|
73119
73404
|
}));
|
|
@@ -73140,6 +73425,16 @@ var Files = class Files extends Service()("react-doctor/Files") {
|
|
|
73140
73425
|
}
|
|
73141
73426
|
return files;
|
|
73142
73427
|
}),
|
|
73428
|
+
listSourceFilesCooperative: (input) => sync(() => {
|
|
73429
|
+
const rootDirectory = input.rootDirectory;
|
|
73430
|
+
const prefix = rootDirectory.endsWith("/") ? rootDirectory : `${rootDirectory}/`;
|
|
73431
|
+
const files = [];
|
|
73432
|
+
for (const absolute of tree.keys()) {
|
|
73433
|
+
if (!absolute.startsWith(prefix)) continue;
|
|
73434
|
+
files.push(absolute.slice(prefix.length));
|
|
73435
|
+
}
|
|
73436
|
+
return files;
|
|
73437
|
+
}),
|
|
73143
73438
|
isFile: (filePath) => sync(() => tree.has(filePath)),
|
|
73144
73439
|
isDirectory: (filePath) => sync(() => {
|
|
73145
73440
|
const prefix = filePath.endsWith("/") ? filePath : `${filePath}/`;
|
|
@@ -73604,7 +73899,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
73604
73899
|
changedFiles: yield* mergeUntracked(directory, splitNullSeparated(diff.stdout), includeUntracked),
|
|
73605
73900
|
isCurrentChanges: false
|
|
73606
73901
|
};
|
|
73607
|
-
}).pipe(withSpan("Git.diffSelection")),
|
|
73902
|
+
}).pipe(catchReasons("ReactDoctorError", { GitInvocationFailed: () => succeed$2(null) }, (_reason, error) => fail$4(error)), withSpan("Git.diffSelection")),
|
|
73608
73903
|
stagedFilePaths: (directory) => runGit(directory, [
|
|
73609
73904
|
"diff",
|
|
73610
73905
|
"--no-ext-diff",
|
|
@@ -73715,7 +74010,7 @@ const batchIncludePaths = (baseArgs, includePaths) => {
|
|
|
73715
74010
|
for (const filePath of includePaths) {
|
|
73716
74011
|
const entryLength = filePath.length + 1;
|
|
73717
74012
|
const exceedsArgLength = currentBatch.length > 0 && currentBatchLength + entryLength > 24e3;
|
|
73718
|
-
const exceedsFileCount = currentBatch.length >=
|
|
74013
|
+
const exceedsFileCount = currentBatch.length >= 200;
|
|
73719
74014
|
if (exceedsArgLength || exceedsFileCount) {
|
|
73720
74015
|
batches.push(currentBatch);
|
|
73721
74016
|
currentBatch = [];
|
|
@@ -73820,6 +74115,33 @@ const detectUserLintConfigPaths = (rootDirectory) => {
|
|
|
73820
74115
|
return [];
|
|
73821
74116
|
};
|
|
73822
74117
|
const DISABLE_DIRECTIVE_PATTERN = /(eslint|oxlint)-disable/;
|
|
74118
|
+
const neutralizedFileLeases = /* @__PURE__ */ new Map();
|
|
74119
|
+
const activeNeutralizationInitializations = /* @__PURE__ */ new Set();
|
|
74120
|
+
const directoriesOverlap = (left, right) => {
|
|
74121
|
+
const isWithin = (parent, candidate) => {
|
|
74122
|
+
const relativePath = Path.relative(parent, candidate);
|
|
74123
|
+
return relativePath.length === 0 || !relativePath.startsWith(`..${Path.sep}`) && relativePath !== ".." && !Path.isAbsolute(relativePath);
|
|
74124
|
+
};
|
|
74125
|
+
return isWithin(left, right) || isWithin(right, left);
|
|
74126
|
+
};
|
|
74127
|
+
const withOverlappingInitializationLock = async (rootDirectory, operation) => {
|
|
74128
|
+
const blockers = [...activeNeutralizationInitializations].filter((initialization) => directoriesOverlap(initialization.rootDirectory, rootDirectory)).map((initialization) => initialization.done);
|
|
74129
|
+
let release;
|
|
74130
|
+
const initialization = {
|
|
74131
|
+
rootDirectory,
|
|
74132
|
+
done: new Promise((resolve) => {
|
|
74133
|
+
release = resolve;
|
|
74134
|
+
})
|
|
74135
|
+
};
|
|
74136
|
+
activeNeutralizationInitializations.add(initialization);
|
|
74137
|
+
await Promise.all(blockers);
|
|
74138
|
+
try {
|
|
74139
|
+
return await operation();
|
|
74140
|
+
} finally {
|
|
74141
|
+
activeNeutralizationInitializations.delete(initialization);
|
|
74142
|
+
release();
|
|
74143
|
+
}
|
|
74144
|
+
};
|
|
73823
74145
|
const findFilesWithDisableDirectivesViaGit = async (rootDirectory, includePaths) => {
|
|
73824
74146
|
const program = gen(function* () {
|
|
73825
74147
|
return yield* (yield* Git).grep({
|
|
@@ -73862,34 +74184,84 @@ const findFilesWithDisableDirectivesViaFilesystem = (rootDirectory, includePaths
|
|
|
73862
74184
|
};
|
|
73863
74185
|
const findFilesWithDisableDirectives = async (rootDirectory, includePaths) => await findFilesWithDisableDirectivesViaGit(rootDirectory, includePaths) ?? findFilesWithDisableDirectivesViaFilesystem(rootDirectory, includePaths);
|
|
73864
74186
|
const neutralizeContent = (content) => content.replaceAll("eslint-disable", "eslint_disable").replaceAll("oxlint-disable", "oxlint_disable");
|
|
73865
|
-
const
|
|
73866
|
-
const
|
|
73867
|
-
const
|
|
74187
|
+
const collectNeutralizationCandidatePaths = (rootDirectory, discoveredRelativePaths, includePaths) => {
|
|
74188
|
+
const resolvedRootDirectory = NFS.realpathSync(rootDirectory);
|
|
74189
|
+
const requestedPaths = includePaths && includePaths.length > 0 ? new Set(includePaths.flatMap((includePath) => {
|
|
74190
|
+
try {
|
|
74191
|
+
return [NFS.realpathSync(Path.resolve(resolvedRootDirectory, includePath))];
|
|
74192
|
+
} catch {
|
|
74193
|
+
return [];
|
|
74194
|
+
}
|
|
74195
|
+
})) : null;
|
|
74196
|
+
const candidatePaths = /* @__PURE__ */ new Set();
|
|
74197
|
+
for (const relativePath of discoveredRelativePaths) try {
|
|
74198
|
+
candidatePaths.add(NFS.realpathSync(Path.resolve(resolvedRootDirectory, relativePath)));
|
|
74199
|
+
} catch {
|
|
74200
|
+
continue;
|
|
74201
|
+
}
|
|
74202
|
+
for (const leasedPath of neutralizedFileLeases.keys()) {
|
|
74203
|
+
const relativePath = Path.relative(resolvedRootDirectory, leasedPath);
|
|
74204
|
+
if (!(relativePath.length === 0 || !relativePath.startsWith(`..${Path.sep}`) && relativePath !== ".." && !Path.isAbsolute(relativePath)) || requestedPaths && !requestedPaths.has(leasedPath)) continue;
|
|
74205
|
+
candidatePaths.add(leasedPath);
|
|
74206
|
+
}
|
|
74207
|
+
return candidatePaths;
|
|
74208
|
+
};
|
|
74209
|
+
const neutralizeDisableDirectives = async (rootDirectory, includePaths, options = {}) => {
|
|
74210
|
+
const resolvedRootDirectory = NFS.realpathSync(rootDirectory);
|
|
74211
|
+
const leasedPaths = /* @__PURE__ */ new Set();
|
|
73868
74212
|
let isRestored = false;
|
|
73869
74213
|
const restore = () => {
|
|
73870
74214
|
if (isRestored) return;
|
|
73871
74215
|
isRestored = true;
|
|
73872
|
-
for (const
|
|
73873
|
-
|
|
73874
|
-
|
|
73875
|
-
|
|
74216
|
+
for (const absolutePath of leasedPaths) {
|
|
74217
|
+
const lease = neutralizedFileLeases.get(absolutePath);
|
|
74218
|
+
if (!lease) continue;
|
|
74219
|
+
lease.referenceCount -= 1;
|
|
74220
|
+
if (lease.referenceCount > 0) continue;
|
|
74221
|
+
neutralizedFileLeases.delete(absolutePath);
|
|
74222
|
+
try {
|
|
74223
|
+
NFS.writeFileSync(absolutePath, lease.originalContent);
|
|
74224
|
+
} catch (error) {
|
|
74225
|
+
process.stderr.write(`[react-doctor] Failed to restore inline disable directives in ${absolutePath}: ${messageFromUnknown(error)}\n[react-doctor] Run: git checkout -- ${absolutePath}\n`);
|
|
74226
|
+
}
|
|
73876
74227
|
}
|
|
73877
74228
|
};
|
|
73878
74229
|
const onExit = () => restore();
|
|
73879
74230
|
process.once("exit", onExit);
|
|
73880
|
-
|
|
73881
|
-
|
|
74231
|
+
const leaseCandidatePath = (absolutePath) => {
|
|
74232
|
+
if (leasedPaths.has(absolutePath)) return;
|
|
74233
|
+
const existingLease = neutralizedFileLeases.get(absolutePath);
|
|
74234
|
+
if (existingLease) {
|
|
74235
|
+
existingLease.referenceCount += 1;
|
|
74236
|
+
leasedPaths.add(absolutePath);
|
|
74237
|
+
return;
|
|
74238
|
+
}
|
|
73882
74239
|
let originalContent;
|
|
73883
74240
|
try {
|
|
73884
74241
|
originalContent = NFS.readFileSync(absolutePath, "utf-8");
|
|
73885
74242
|
} catch {
|
|
73886
|
-
|
|
74243
|
+
return;
|
|
73887
74244
|
}
|
|
73888
74245
|
const neutralizedContent = neutralizeContent(originalContent);
|
|
73889
74246
|
if (neutralizedContent !== originalContent) {
|
|
73890
|
-
originalContents.set(absolutePath, originalContent);
|
|
73891
74247
|
NFS.writeFileSync(absolutePath, neutralizedContent);
|
|
74248
|
+
neutralizedFileLeases.set(absolutePath, {
|
|
74249
|
+
originalContent,
|
|
74250
|
+
referenceCount: 1
|
|
74251
|
+
});
|
|
74252
|
+
leasedPaths.add(absolutePath);
|
|
73892
74253
|
}
|
|
74254
|
+
};
|
|
74255
|
+
try {
|
|
74256
|
+
await withOverlappingInitializationLock(resolvedRootDirectory, async () => {
|
|
74257
|
+
for (const absolutePath of collectNeutralizationCandidatePaths(resolvedRootDirectory, [], includePaths)) if (neutralizedFileLeases.has(absolutePath)) leaseCandidatePath(absolutePath);
|
|
74258
|
+
const discoveredRelativePaths = await (options.findFiles ?? findFilesWithDisableDirectives)(resolvedRootDirectory, includePaths);
|
|
74259
|
+
for (const absolutePath of collectNeutralizationCandidatePaths(resolvedRootDirectory, discoveredRelativePaths, includePaths)) leaseCandidatePath(absolutePath);
|
|
74260
|
+
});
|
|
74261
|
+
} catch (error) {
|
|
74262
|
+
restore();
|
|
74263
|
+
process.removeListener("exit", onExit);
|
|
74264
|
+
throw error;
|
|
73893
74265
|
}
|
|
73894
74266
|
return () => {
|
|
73895
74267
|
restore();
|
|
@@ -74062,7 +74434,7 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
74062
74434
|
}
|
|
74063
74435
|
return enabled;
|
|
74064
74436
|
};
|
|
74065
|
-
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), includedTags = /* @__PURE__ */ new Set(), includeTagDefaults = false, runtimeGlobals, unpluginAutoImportGlobalScopes, serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection, sidecarRuleIdFilter }) => {
|
|
74437
|
+
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), includedTags = /* @__PURE__ */ new Set(), includeTagDefaults = false, runtimeGlobals, unpluginAutoImportGlobalScopes, serverAuthFunctionNames, projectIndexModuleSources, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection, sidecarRuleIdFilter }) => {
|
|
74066
74438
|
const hasIncludedTags = includedTags.size > 0;
|
|
74067
74439
|
const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" || hasIncludedTags ? null : resolveReactHooksJsPlugin(project.hasReactCompiler || project.hasReactCompilerLintPlugin === true, customRulesOnly);
|
|
74068
74440
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
@@ -74126,7 +74498,8 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
74126
74498
|
}))
|
|
74127
74499
|
} : {},
|
|
74128
74500
|
...project.shopifyFlashListMajorVersion !== null ? { shopifyFlashListMajorVersion: project.shopifyFlashListMajorVersion } : {},
|
|
74129
|
-
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {}
|
|
74501
|
+
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {},
|
|
74502
|
+
...projectIndexModuleSources !== void 0 ? { projectIndexModuleSources: [...projectIndexModuleSources] } : {}
|
|
74130
74503
|
} },
|
|
74131
74504
|
rules: {
|
|
74132
74505
|
...reactCompilerRules,
|
|
@@ -74375,11 +74748,17 @@ const collectUnpluginAutoImportGlobalScopes = ({ rootDirectory, candidateFiles }
|
|
|
74375
74748
|
return scopes.some((scope) => scope.names.length > 0) ? scopes : [];
|
|
74376
74749
|
};
|
|
74377
74750
|
const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
|
|
74751
|
+
const decodeDiagnostic = (raw) => {
|
|
74752
|
+
const diagnostic = validateDiagnostic$1(raw);
|
|
74753
|
+
return {
|
|
74754
|
+
...diagnostic,
|
|
74755
|
+
relatedLocations: diagnostic.relatedLocations?.map((location) => ({ ...location }))
|
|
74756
|
+
};
|
|
74757
|
+
};
|
|
74378
74758
|
const decodeFileDiagnostics = (raw) => {
|
|
74379
74759
|
if (!Array.isArray(raw)) return null;
|
|
74380
74760
|
try {
|
|
74381
|
-
|
|
74382
|
-
return raw;
|
|
74761
|
+
return raw.map((entry) => decodeDiagnostic(entry));
|
|
74383
74762
|
} catch {
|
|
74384
74763
|
return null;
|
|
74385
74764
|
}
|
|
@@ -75611,6 +75990,12 @@ const buildProfiledNodeArguments = (input) => [
|
|
|
75611
75990
|
...input.heapProfileDirectory ? ["--heap-prof", `--heap-prof-dir=${input.heapProfileDirectory}`] : [],
|
|
75612
75991
|
...input.argumentsList
|
|
75613
75992
|
];
|
|
75993
|
+
const lowerChildProcessPriority = (processId) => {
|
|
75994
|
+
if (processId === void 0) return;
|
|
75995
|
+
try {
|
|
75996
|
+
OS.setPriority(processId, OS.constants.priority.PRIORITY_BELOW_NORMAL);
|
|
75997
|
+
} catch {}
|
|
75998
|
+
};
|
|
75614
75999
|
const SANITIZED_ENV = buildOxlintChildEnv(process.env);
|
|
75615
76000
|
/**
|
|
75616
76001
|
* Spawn one oxlint subprocess with hard ceilings on wall time and
|
|
@@ -75649,6 +76034,7 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
|
|
|
75649
76034
|
"pipe"
|
|
75650
76035
|
]
|
|
75651
76036
|
});
|
|
76037
|
+
lowerChildProcessPriority(child.pid);
|
|
75652
76038
|
const onAbort = () => {
|
|
75653
76039
|
child.kill("SIGKILL");
|
|
75654
76040
|
reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) }));
|
|
@@ -75771,7 +76157,7 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
75771
76157
|
* loop with a slimmer config in that case.
|
|
75772
76158
|
*/
|
|
75773
76159
|
const spawnLintBatches = async (input) => {
|
|
75774
|
-
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, sourceMapByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth =
|
|
76160
|
+
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, sourceMapByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 9, signal, deadlineEpochMs } = input;
|
|
75775
76161
|
const resolveSourcePath = (filePath) => {
|
|
75776
76162
|
const absoluteFilePath = Path.isAbsolute(filePath) ? filePath : Path.resolve(rootDirectory, filePath);
|
|
75777
76163
|
return sourcePathByLintPath?.get(Path.normalize(absoluteFilePath)) ?? filePath;
|
|
@@ -75779,6 +76165,12 @@ const spawnLintBatches = async (input) => {
|
|
|
75779
76165
|
const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
|
|
75780
76166
|
let rescueDeadlineEpochMs;
|
|
75781
76167
|
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0 || rescueDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(rescueDeadlineEpochMs) === 0;
|
|
76168
|
+
const resolveSpawnTimeoutMs = () => {
|
|
76169
|
+
const deadlineBudgets = [deadlineEpochMs, rescueDeadlineEpochMs].filter((deadline) => deadline !== void 0).map(remainingDeadlineBudgetMs);
|
|
76170
|
+
if (deadlineBudgets.length === 0) return spawnTimeoutMs;
|
|
76171
|
+
const remainingDeadlineMs = Math.min(...deadlineBudgets);
|
|
76172
|
+
return spawnTimeoutMs === void 0 ? remainingDeadlineMs : Math.min(spawnTimeoutMs, remainingDeadlineMs);
|
|
76173
|
+
};
|
|
75782
76174
|
const runBatchPass = async (concurrency, passBatches, passOnFileProgress) => {
|
|
75783
76175
|
const totalFileCount = passBatches.reduce((sum, batch) => sum + batch.length, 0);
|
|
75784
76176
|
const allDiagnostics = [];
|
|
@@ -75796,12 +76188,13 @@ const spawnLintBatches = async (input) => {
|
|
|
75796
76188
|
const batchArgs = [...baseArgs, ...batch];
|
|
75797
76189
|
try {
|
|
75798
76190
|
const spawnBatch = () => {
|
|
75799
|
-
|
|
76191
|
+
const effectiveSpawnTimeoutMs = resolveSpawnTimeoutMs();
|
|
76192
|
+
if (effectiveSpawnTimeoutMs === 0) {
|
|
75800
76193
|
deadlineSkippedFiles.push(...batch);
|
|
75801
76194
|
batchState.deadlineSkippedFileCount += batch.length;
|
|
75802
76195
|
return Promise.resolve(null);
|
|
75803
76196
|
}
|
|
75804
|
-
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath,
|
|
76197
|
+
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, effectiveSpawnTimeoutMs, outputMaxBytes, signal, () => {
|
|
75805
76198
|
if (batchState.didStart) return;
|
|
75806
76199
|
batchState.didStart = true;
|
|
75807
76200
|
startedFileCount += batchState.initialFileCount;
|
|
@@ -75956,11 +76349,36 @@ const validateRuleRegistration = () => {
|
|
|
75956
76349
|
].filter((entry) => entry !== null).join("; ");
|
|
75957
76350
|
console.warn(`[react-doctor] rule-registration drift: ${detail}`);
|
|
75958
76351
|
};
|
|
76352
|
+
const PROJECT_INDEX_MODULE_SOURCES = [
|
|
76353
|
+
"next/og",
|
|
76354
|
+
"@vercel/og",
|
|
76355
|
+
"satori",
|
|
76356
|
+
"remotion"
|
|
76357
|
+
];
|
|
76358
|
+
const collectProjectIndexModuleSources = async (rootDirectory, candidateFiles) => {
|
|
76359
|
+
const foundModuleSources = /* @__PURE__ */ new Set();
|
|
76360
|
+
let sliceStartedAt = performance.now();
|
|
76361
|
+
for (const candidateFile of candidateFiles) {
|
|
76362
|
+
let sourceBuffer;
|
|
76363
|
+
try {
|
|
76364
|
+
sourceBuffer = NFS.readFileSync(Path.isAbsolute(candidateFile) ? candidateFile : Path.resolve(rootDirectory, candidateFile));
|
|
76365
|
+
} catch {
|
|
76366
|
+
continue;
|
|
76367
|
+
}
|
|
76368
|
+
for (const moduleSource of PROJECT_INDEX_MODULE_SOURCES) if (sourceBuffer.includes(`"${moduleSource}"`) || sourceBuffer.includes(`'${moduleSource}'`)) foundModuleSources.add(moduleSource);
|
|
76369
|
+
if (foundModuleSources.size === PROJECT_INDEX_MODULE_SOURCES.length) break;
|
|
76370
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
76371
|
+
await yieldToEventLoop();
|
|
76372
|
+
sliceStartedAt = performance.now();
|
|
76373
|
+
}
|
|
76374
|
+
}
|
|
76375
|
+
return [...foundModuleSources].sort();
|
|
76376
|
+
};
|
|
75959
76377
|
/**
|
|
75960
76378
|
* Balanced LPT (longest-processing-time-first) lint batch planner. Where
|
|
75961
|
-
* `batchIncludePaths` greedily fills
|
|
75962
|
-
* remainder batch
|
|
75963
|
-
*
|
|
76379
|
+
* `batchIncludePaths` greedily fills fixed-size batches and leaves a small
|
|
76380
|
+
* remainder batch, so the parallel pool's wall clock is set by whichever
|
|
76381
|
+
* full chunk happened to collect the most work while
|
|
75964
76382
|
* the remainder's worker idles), this planner keeps the SAME mandatory batch
|
|
75965
76383
|
* count — `ceil(files / OXLINT_MAX_FILES_PER_BATCH)`, the JS-plugin
|
|
75966
76384
|
* perf-cliff / native-binding SIGABRT guard — and assigns files largest-first,
|
|
@@ -75989,7 +76407,7 @@ const validateRuleRegistration = () => {
|
|
|
75989
76407
|
*/
|
|
75990
76408
|
const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
75991
76409
|
if (files.length === 0) return [];
|
|
75992
|
-
const batchCount = Math.ceil(files.length /
|
|
76410
|
+
const batchCount = Math.ceil(files.length / 200);
|
|
75993
76411
|
const maxFilesPerBatch = Math.ceil(files.length / batchCount);
|
|
75994
76412
|
const baseArgsLengthChars = estimateArgsLength(baseArgs);
|
|
75995
76413
|
const descendingSizeIndexes = [...files.keys()].sort((leftIndex, rightIndex) => (sizeByFile.get(files[rightIndex]) ?? 0) - (sizeByFile.get(files[leftIndex]) ?? 0));
|
|
@@ -76518,6 +76936,7 @@ const runOxlint = async (options) => {
|
|
|
76518
76936
|
await yieldToEventLoop();
|
|
76519
76937
|
const sizedScanFiles = includePaths === void 0 ? listSourceFilesWithSize(rootDirectory) : null;
|
|
76520
76938
|
const candidateFiles = includePaths !== void 0 ? includePaths : (sizedScanFiles ?? []).map((entry) => entry.path);
|
|
76939
|
+
const projectIndexModuleSources = includePaths === void 0 && options.deadlineEpochMs === void 0 ? await collectProjectIndexModuleSources(rootDirectory, candidateFiles) : void 0;
|
|
76521
76940
|
unpluginAutoImportGlobalScopes = collectUnpluginAutoImportGlobalScopes({
|
|
76522
76941
|
rootDirectory,
|
|
76523
76942
|
candidateFiles
|
|
@@ -76540,6 +76959,7 @@ const runOxlint = async (options) => {
|
|
|
76540
76959
|
runtimeGlobals,
|
|
76541
76960
|
unpluginAutoImportGlobalScopes,
|
|
76542
76961
|
serverAuthFunctionNames,
|
|
76962
|
+
projectIndexModuleSources,
|
|
76543
76963
|
severityControls,
|
|
76544
76964
|
userPlugins,
|
|
76545
76965
|
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
|
|
@@ -77007,10 +77427,10 @@ const translateProjectInfoError = (cause, directory) => {
|
|
|
77007
77427
|
}) });
|
|
77008
77428
|
};
|
|
77009
77429
|
var Project = class Project extends Service()("react-doctor/Project") {
|
|
77010
|
-
static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (
|
|
77430
|
+
static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (input) {
|
|
77011
77431
|
return yield* try_({
|
|
77012
|
-
try: () => discoverProject(directory),
|
|
77013
|
-
catch: (cause) => translateProjectInfoError(cause, directory)
|
|
77432
|
+
try: () => discoverProject(input.directory, { sourceFileCount: input.sourceFileCount }),
|
|
77433
|
+
catch: (cause) => translateProjectInfoError(cause, input.directory)
|
|
77014
77434
|
});
|
|
77015
77435
|
}) }));
|
|
77016
77436
|
static layerOf = (projectInfo) => succeed$3(Project, Project.of({ discover: () => succeed$2(projectInfo) }));
|
|
@@ -77595,9 +78015,10 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
77595
78015
|
* outage.
|
|
77596
78016
|
*/
|
|
77597
78017
|
var SupplyChain = class SupplyChain extends Service()("react-doctor/SupplyChain") {
|
|
77598
|
-
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (
|
|
77599
|
-
|
|
77600
|
-
|
|
78018
|
+
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (configuredTimeoutMs) => checkSupplyChain({
|
|
78019
|
+
rootDirectory: input.rootDirectory,
|
|
78020
|
+
userConfig: input.userConfig,
|
|
78021
|
+
totalTimeoutMs: input.timeoutMs ?? configuredTimeoutMs
|
|
77601
78022
|
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), withSpan("SupplyChain.run"))) }));
|
|
77602
78023
|
static layerOf = (diagnostics) => succeed$3(SupplyChain, SupplyChain.of({ run: () => fromIterable$1(diagnostics) }));
|
|
77603
78024
|
};
|
|
@@ -77632,10 +78053,6 @@ const NO_HOOKS = {
|
|
|
77632
78053
|
beforeLint: () => void_,
|
|
77633
78054
|
afterLint: () => void_
|
|
77634
78055
|
};
|
|
77635
|
-
const filterMapNullable = (transform) => fromPredicateOption((value) => {
|
|
77636
|
-
const result = transform(value);
|
|
77637
|
-
return result === null ? none() : some(result);
|
|
77638
|
-
});
|
|
77639
78056
|
const fileReader = (filesService, rootDirectory) => (filePath) => {
|
|
77640
78057
|
const lines = runSync(filesService.readLines({
|
|
77641
78058
|
filePath,
|
|
@@ -77659,7 +78076,7 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
77659
78076
|
* The GitHub viewer-permission lookup is forked onto a background
|
|
77660
78077
|
* fiber here and joined late (it feeds score metadata, not
|
|
77661
78078
|
* diagnostics).
|
|
77662
|
-
* 2. beforeLint hook (e.g. CLI
|
|
78079
|
+
* 2. beforeLint hook (e.g. CLI records project telemetry)
|
|
77663
78080
|
* 3. environment checks (reduced-motion + pnpm hardening +
|
|
77664
78081
|
* expo/react-native), collected synchronously. The heavier
|
|
77665
78082
|
* content-regex security scan is forked instead (like supply-chain
|
|
@@ -77672,13 +78089,11 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
77672
78089
|
* `SupplyChainOverlapTimeoutMs` (measured from fork) so a hung
|
|
77673
78090
|
* socket can't drag out its join; on timeout it fails open to no
|
|
77674
78091
|
* diagnostics — the same outcome class as a Socket outage.
|
|
77675
|
-
* 5. Linter.run runs; DeadCode.run
|
|
77676
|
-
*
|
|
77677
|
-
*
|
|
77678
|
-
*
|
|
77679
|
-
*
|
|
77680
|
-
* fiber is joined (or interrupted, SIGKILLing its worker, on lint
|
|
77681
|
-
* failure) before diagnostics are concatenated. The afterLint hook
|
|
78092
|
+
* 5. Linter.run runs; DeadCode.run forks only when
|
|
78093
|
+
* REACT_DOCTOR_DEAD_CODE_OVERLAP=on. The default "auto" and explicit
|
|
78094
|
+
* "off" paths stay sequential. The fiber is joined (or interrupted,
|
|
78095
|
+
* SIGKILLing its worker, on lint failure) before diagnostics are
|
|
78096
|
+
* concatenated. The afterLint hook
|
|
77682
78097
|
* fires between lint and dead-code. Progress spinner labels AND the
|
|
77683
78098
|
* final diagnostic / score order stay independent of execution
|
|
77684
78099
|
* order, so terminal output is identical either way; supply-chain
|
|
@@ -77713,8 +78128,28 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77713
78128
|
const partialFailuresRef = yield* LintPartialFailures;
|
|
77714
78129
|
const resolvedConfig = yield* configService.resolve(input.directory);
|
|
77715
78130
|
const scanDirectory = resolvedConfig.resolvedDirectory;
|
|
77716
|
-
const
|
|
78131
|
+
const excludedProjectDirectories = (input.excludedProjectDirectories ?? []).map((excludedDirectory) => Path.resolve(excludedDirectory)).filter((excludedDirectory) => isPathInsideDirectory(excludedDirectory, scanDirectory));
|
|
78132
|
+
const excludedProjectRelativePaths = excludedProjectDirectories.map((excludedDirectory) => Path.relative(scanDirectory, excludedDirectory).replaceAll("\\", "/"));
|
|
78133
|
+
const isExcludedProjectDiagnostic = (diagnostic) => {
|
|
78134
|
+
const relativeFilePath = toNormalizedRelativePath(diagnostic.filePath, scanDirectory);
|
|
78135
|
+
return excludedProjectRelativePaths.some((excludedRelativePath) => relativeFilePath === excludedRelativePath || relativeFilePath.startsWith(`${excludedRelativePath}/`));
|
|
78136
|
+
};
|
|
78137
|
+
const precomputedSourceFiles = input.suppressScanSummary === true || excludedProjectDirectories.length > 0 ? yield* filesService.listSourceFilesCooperative({
|
|
78138
|
+
rootDirectory: scanDirectory,
|
|
78139
|
+
signal: input.signal
|
|
78140
|
+
}) : null;
|
|
78141
|
+
const includedPrecomputedSourceFiles = precomputedSourceFiles !== null && excludedProjectDirectories.length > 0 ? filterPathsOutsideDirectories({
|
|
78142
|
+
rootDirectory: scanDirectory,
|
|
78143
|
+
relativePaths: precomputedSourceFiles,
|
|
78144
|
+
excludedDirectories: excludedProjectDirectories
|
|
78145
|
+
}) : precomputedSourceFiles;
|
|
78146
|
+
const sourceFileCount = excludedProjectDirectories.length > 0 ? includedPrecomputedSourceFiles?.length : input.precomputedSourceFileCount ?? includedPrecomputedSourceFiles?.length;
|
|
78147
|
+
const project = yield* projectService.discover({
|
|
78148
|
+
directory: scanDirectory,
|
|
78149
|
+
sourceFileCount
|
|
78150
|
+
});
|
|
77717
78151
|
if (!isAnalyzableProject(project)) return yield* new ReactDoctorError({ reason: new NoReactDependency({ directory: scanDirectory }) });
|
|
78152
|
+
const deadCodeSourceFileCount = input.retainExcludedProjectDeadCodeDiagnostics === true && precomputedSourceFiles !== null ? precomputedSourceFiles.length : project.sourceFileCount;
|
|
77718
78153
|
const [repo, sha, defaultBranch] = yield* all([
|
|
77719
78154
|
gitService.githubRepo(scanDirectory).pipe(orElseSucceed(() => null)),
|
|
77720
78155
|
gitService.headSha(scanDirectory).pipe(orElseSucceed(() => null)),
|
|
@@ -77725,11 +78160,16 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77725
78160
|
directory: scanDirectory,
|
|
77726
78161
|
repo
|
|
77727
78162
|
}).pipe(orElseSucceed(() => null)) : succeed$2(null));
|
|
77728
|
-
|
|
77729
|
-
|
|
78163
|
+
let lintIncludePaths = (input.skipExplicitIncludePathFilter ? input.includePaths.length > 0 ? [...input.includePaths] : void 0 : computeExplicitLintIncludePaths([...input.includePaths])) ?? resolveLintIncludePaths(scanDirectory, resolvedConfig.config, includedPrecomputedSourceFiles ?? void 0);
|
|
78164
|
+
if (excludedProjectDirectories.length > 0) lintIncludePaths = filterPathsOutsideDirectories({
|
|
78165
|
+
rootDirectory: scanDirectory,
|
|
78166
|
+
relativePaths: lintIncludePaths ?? includedPrecomputedSourceFiles ?? (yield* filesService.listSourceFiles(scanDirectory)),
|
|
78167
|
+
excludedDirectories: excludedProjectDirectories
|
|
78168
|
+
});
|
|
78169
|
+
const fallbackScannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? includedPrecomputedSourceFiles ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
|
|
77730
78170
|
const beforeLint = hooks.beforeLint ?? NO_HOOKS.beforeLint;
|
|
77731
78171
|
const afterLint = hooks.afterLint ?? NO_HOOKS.afterLint;
|
|
77732
|
-
yield* beforeLint(project
|
|
78172
|
+
yield* beforeLint(project);
|
|
77733
78173
|
const isDiffMode = input.includePaths.length > 0;
|
|
77734
78174
|
const showWarnings = input.warnings ?? resolvedConfig.config?.warnings ?? true;
|
|
77735
78175
|
const transform = buildDiagnosticPipeline({
|
|
@@ -77739,7 +78179,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77739
78179
|
respectInlineDisables: input.respectInlineDisables,
|
|
77740
78180
|
showWarnings
|
|
77741
78181
|
});
|
|
77742
|
-
const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(
|
|
78182
|
+
const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(fromPredicateOption((diagnostic) => {
|
|
78183
|
+
const filteredDiagnostic = transform.apply(diagnostic);
|
|
78184
|
+
return filteredDiagnostic === null ? none() : some(filteredDiagnostic);
|
|
78185
|
+
})));
|
|
77743
78186
|
const applyPerElementPipeline = (rawStream) => filterPerElementPipeline(rawStream).pipe(tap((diagnostic) => reporterService.emit(diagnostic)));
|
|
77744
78187
|
const envCollected = yield* runCollect(applyPerElementPipeline(fromIterable$1(isDiffMode ? [] : [
|
|
77745
78188
|
...checkReducedMotion(scanDirectory),
|
|
@@ -77749,17 +78192,31 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77749
78192
|
...checkReactNativeProject(scanDirectory, project)
|
|
77750
78193
|
])));
|
|
77751
78194
|
const securityScanFailedRef = yield* make$13(false);
|
|
77752
|
-
|
|
78195
|
+
let didSecurityScanReachDeadline = false;
|
|
78196
|
+
const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(tryPromise((signal) => checkSecurityScanCooperative(scanDirectory, {
|
|
77753
78197
|
project,
|
|
77754
78198
|
ignoredTags: input.ignoredTags,
|
|
77755
78199
|
includedTags: input.includedTags,
|
|
77756
|
-
includeTagDefaults: input.includeTagDefaults
|
|
78200
|
+
includeTagDefaults: input.includeTagDefaults,
|
|
78201
|
+
excludedDirectories: new Set(excludedProjectDirectories),
|
|
78202
|
+
deadlineEpochMs: input.deadlineEpochMs,
|
|
78203
|
+
signal,
|
|
78204
|
+
onDeadlineExceeded: () => {
|
|
78205
|
+
didSecurityScanReachDeadline = true;
|
|
78206
|
+
}
|
|
77757
78207
|
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), catch_$1(() => set(securityScanFailedRef, true).pipe(as(empty$4))))))).pipe(withSpan("SecurityScan.run")));
|
|
78208
|
+
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
78209
|
+
const capOptionalToDeadline = (phaseTimeoutMs) => {
|
|
78210
|
+
if (input.deadlineEpochMs === void 0) return phaseTimeoutMs;
|
|
78211
|
+
const remainingBudgetMs = remainingDeadlineBudgetMs(input.deadlineEpochMs);
|
|
78212
|
+
return phaseTimeoutMs === null ? remainingBudgetMs : Math.min(phaseTimeoutMs, remainingBudgetMs);
|
|
78213
|
+
};
|
|
77758
78214
|
const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
|
|
77759
|
-
const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
|
|
78215
|
+
const supplyChainOverlapTimeout = capToDeadline(yield* SupplyChainOverlapTimeoutMs);
|
|
77760
78216
|
const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
|
|
77761
78217
|
rootDirectory: scanDirectory,
|
|
77762
|
-
userConfig: resolvedConfig.config
|
|
78218
|
+
userConfig: resolvedConfig.config,
|
|
78219
|
+
timeoutMs: supplyChainOverlapTimeout
|
|
77763
78220
|
}))).pipe(map$3((diagnostics) => ({
|
|
77764
78221
|
diagnostics,
|
|
77765
78222
|
timedOut: false
|
|
@@ -77783,46 +78240,49 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77783
78240
|
const scanConcurrency = resolveScanConcurrency(yield* OxlintConcurrency);
|
|
77784
78241
|
const lintPhaseTimeoutMs = yield* LintPhaseTimeoutMs;
|
|
77785
78242
|
const deadCodePhaseTimeoutMs = yield* DeadCodePhaseTimeoutMs;
|
|
77786
|
-
const resolveDeadCodePhaseTimeoutMs = (scaledPhaseTimeoutMs) => deadCodePhaseTimeoutMs === 15e4 ? scaledPhaseTimeoutMs : deadCodePhaseTimeoutMs;
|
|
77787
78243
|
const workerCountSuffix = scanConcurrency > 1 ? ` ${highlighter.dim(`[~${scanConcurrency} workers]`)}` : "";
|
|
77788
|
-
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
77789
78244
|
const shouldRunDeadCode = input.runDeadCode && !isDiffMode && (showWarnings || deadCodeMaySurfaceWhenWarningsHidden(resolvedConfig.config));
|
|
77790
78245
|
const deadCodeOverlapMode = yield* DeadCodeOverlap;
|
|
77791
78246
|
const shouldOverlapDeadCode = shouldRunDeadCode && deadCodeOverlapMode === "on";
|
|
77792
78247
|
const deadCodeParseConcurrency = shouldOverlapDeadCode ? Math.max(1, Math.floor(scanConcurrency * DEAD_CODE_OVERLAP_PARSE_SHARE)) : void 0;
|
|
77793
78248
|
const lintConcurrency = deadCodeParseConcurrency === void 0 ? scanConcurrency : Math.max(1, scanConcurrency - deadCodeParseConcurrency);
|
|
77794
|
-
const buildCollectDeadCode = (deadCodeTimeout) =>
|
|
77795
|
-
|
|
77796
|
-
|
|
77797
|
-
|
|
77798
|
-
|
|
77799
|
-
|
|
77800
|
-
|
|
77801
|
-
|
|
77802
|
-
|
|
77803
|
-
|
|
77804
|
-
|
|
77805
|
-
|
|
77806
|
-
|
|
77807
|
-
|
|
77808
|
-
|
|
77809
|
-
|
|
77810
|
-
|
|
77811
|
-
|
|
77812
|
-
|
|
77813
|
-
|
|
77814
|
-
|
|
77815
|
-
|
|
77816
|
-
|
|
77817
|
-
|
|
78249
|
+
const buildCollectDeadCode = (deadCodeTimeout) => {
|
|
78250
|
+
const collectDeadCode = runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
78251
|
+
rootDirectory: scanDirectory,
|
|
78252
|
+
parseConcurrency: deadCodeParseConcurrency,
|
|
78253
|
+
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
|
|
78254
|
+
onCacheOutcome: (didHitCache) => {
|
|
78255
|
+
deadCodeCacheHit = didHitCache;
|
|
78256
|
+
},
|
|
78257
|
+
onSummaryCacheStats: (stats) => {
|
|
78258
|
+
deadCodeSummaryCacheHits = stats.hits;
|
|
78259
|
+
deadCodeSummaryCacheMisses = stats.misses;
|
|
78260
|
+
}
|
|
78261
|
+
}).pipe(filter((diagnostic) => input.retainExcludedProjectDeadCodeDiagnostics === true || !isExcludedProjectDiagnostic(diagnostic)), catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
78262
|
+
yield* set(deadCodeFailure, {
|
|
78263
|
+
didFail: true,
|
|
78264
|
+
reason: error.message
|
|
78265
|
+
});
|
|
78266
|
+
return empty$4;
|
|
78267
|
+
}))))));
|
|
78268
|
+
const phaseTimeoutMs = deadCodeTimeout.phaseTimeoutMs;
|
|
78269
|
+
if (phaseTimeoutMs === null) return collectDeadCode;
|
|
78270
|
+
return collectDeadCode.pipe(timeoutOption(phaseTimeoutMs), flatMap$2(match$3({
|
|
78271
|
+
onNone: () => set(deadCodeFailure, {
|
|
78272
|
+
didFail: true,
|
|
78273
|
+
reason: `Dead-code analysis exceeded ${Math.round(phaseTimeoutMs / MILLISECONDS_PER_SECOND)}s and was skipped.`
|
|
78274
|
+
}).pipe(as([])),
|
|
78275
|
+
onSome: succeed$2
|
|
78276
|
+
})));
|
|
78277
|
+
};
|
|
77818
78278
|
const overlapDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
77819
|
-
sourceFileCount:
|
|
78279
|
+
sourceFileCount: deadCodeSourceFileCount,
|
|
77820
78280
|
deadCodeConcurrency: deadCodeParseConcurrency ?? scanConcurrency,
|
|
77821
78281
|
fullConcurrency: scanConcurrency
|
|
77822
78282
|
});
|
|
77823
78283
|
const deadCodeFiber = shouldOverlapDeadCode ? yield* forkChild(buildCollectDeadCode({
|
|
77824
78284
|
workerTimeoutMs: overlapDeadCodeTimeout.workerTimeoutMs,
|
|
77825
|
-
phaseTimeoutMs:
|
|
78285
|
+
phaseTimeoutMs: capOptionalToDeadline(deadCodePhaseTimeoutMs)
|
|
77826
78286
|
})) : null;
|
|
77827
78287
|
const scanProgress = yield* progressService.start("Scanning...");
|
|
77828
78288
|
const scanStartTime = Date.now();
|
|
@@ -77873,7 +78333,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77873
78333
|
});
|
|
77874
78334
|
return empty$4;
|
|
77875
78335
|
}))));
|
|
77876
|
-
const
|
|
78336
|
+
const collectLintDiagnostics = runCollect(filterPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream));
|
|
78337
|
+
const lintCollected = dedupeRelatedDiagnostics(yield* lintPhaseTimeoutMs === null ? collectLintDiagnostics : collectLintDiagnostics.pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
|
|
77877
78338
|
onNone: () => set(lintFailure, {
|
|
77878
78339
|
didFail: true,
|
|
77879
78340
|
reason: `Lint analysis exceeded ${lintPhaseTimeoutMs / MILLISECONDS_PER_SECOND}s and was skipped.`,
|
|
@@ -77903,13 +78364,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77903
78364
|
} else {
|
|
77904
78365
|
yield* scanProgress.update(`Scanned ${scannedFilesLabel}, analyzing dead code...`);
|
|
77905
78366
|
const sequentialDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
77906
|
-
sourceFileCount: totalFileCount,
|
|
78367
|
+
sourceFileCount: input.retainExcludedProjectDeadCodeDiagnostics === true ? deadCodeSourceFileCount : totalFileCount,
|
|
77907
78368
|
deadCodeConcurrency: scanConcurrency,
|
|
77908
78369
|
fullConcurrency: scanConcurrency
|
|
77909
78370
|
});
|
|
77910
78371
|
deadCodeCollected = deadCodeFiber !== null ? yield* join(deadCodeFiber) : yield* buildCollectDeadCode({
|
|
77911
78372
|
workerTimeoutMs: sequentialDeadCodeTimeout.workerTimeoutMs,
|
|
77912
|
-
phaseTimeoutMs:
|
|
78373
|
+
phaseTimeoutMs: capOptionalToDeadline(deadCodePhaseTimeoutMs)
|
|
77913
78374
|
});
|
|
77914
78375
|
}
|
|
77915
78376
|
const deadCodeFailureState = lintFailureState.didFail ? {
|
|
@@ -77953,7 +78414,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77953
78414
|
metadata: scoreMetadata
|
|
77954
78415
|
});
|
|
77955
78416
|
const lintPartialFailures = yield* get$2(partialFailuresRef);
|
|
77956
|
-
const
|
|
78417
|
+
const didSecurityScanFail = yield* get$2(securityScanFailedRef);
|
|
78418
|
+
const securityScanFailed = didSecurityScanFail || didSecurityScanReachDeadline;
|
|
78419
|
+
let securityScanFailureReason = null;
|
|
78420
|
+
if (didSecurityScanReachDeadline) securityScanFailureReason = "Security scan reached the max scan duration; findings collected before the deadline were preserved.";
|
|
78421
|
+
else if (didSecurityScanFail) securityScanFailureReason = "Security scan failed and was skipped.";
|
|
77957
78422
|
return {
|
|
77958
78423
|
project,
|
|
77959
78424
|
userConfig: resolvedConfig.config,
|
|
@@ -77976,6 +78441,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77976
78441
|
scanConcurrency,
|
|
77977
78442
|
supplyChainOverlapTimedOut: supplyChainResult.timedOut,
|
|
77978
78443
|
securityScanFailed,
|
|
78444
|
+
securityScanFailureReason,
|
|
77979
78445
|
lintCacheHitFileCount,
|
|
77980
78446
|
lintCacheTotalFileCount,
|
|
77981
78447
|
lintSidecarReplayedFileCount,
|
|
@@ -77991,7 +78457,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77991
78457
|
"inspect.runDeadCode": input.runDeadCode,
|
|
77992
78458
|
"inspect.isCi": input.isCi,
|
|
77993
78459
|
"inspect.scoreSurface": input.scoreSurface ?? "score"
|
|
77994
|
-
} }), (scanProgram) => flatMap$2(ScanDeadlineMs, (scanDeadlineMs) =>
|
|
78460
|
+
} }), (scanProgram) => flatMap$2(ScanDeadlineMs, (scanDeadlineMs) => {
|
|
78461
|
+
if (scanDeadlineMs === null) return scanProgram;
|
|
78462
|
+
return scanProgram.pipe(timeout(scanDeadlineMs), catchTag$1("TimeoutError", () => new ReactDoctorError({ reason: new ScanDeadlineExceeded({ detail: `${scanDeadlineMs / MILLISECONDS_PER_SECOND}s elapsed` }) })));
|
|
78463
|
+
}));
|
|
77995
78464
|
const parseNodeVersion = (versionString) => {
|
|
77996
78465
|
const [major = 0, minor = 0, patch = 0] = versionString.replace(/^v/, "").trim().split(".").map(Number);
|
|
77997
78466
|
return {
|
|
@@ -78103,16 +78572,6 @@ var NodeResolver = class NodeResolver extends Service()("react-doctor/NodeResolv
|
|
|
78103
78572
|
});
|
|
78104
78573
|
};
|
|
78105
78574
|
/**
|
|
78106
|
-
* Zip-Slip defense: relative paths come from git (`diff --name-only`), which
|
|
78107
|
-
* normalizes during ordinary adds, but a crafted index/pack/symlinked tree can
|
|
78108
|
-
* smuggle `..` segments that escape the temp root. Resolve against the temp dir
|
|
78109
|
-
* and reject anything that lands outside before writing.
|
|
78110
|
-
*/
|
|
78111
|
-
const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
|
|
78112
|
-
const relative = path.relative(parentAbsolutePath, childAbsolutePath);
|
|
78113
|
-
return Boolean(relative) && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
78114
|
-
};
|
|
78115
|
-
/**
|
|
78116
78575
|
* Writes a set of source files (supplied by `readContent` — e.g.
|
|
78117
78576
|
* `git show <ref>:<path>` for a baseline tree, or `git show :<path>` for the
|
|
78118
78577
|
* index) into a temp tree that mirrors the project layout, copying the
|
|
@@ -78148,7 +78607,7 @@ const materializeSourceTree = (input) => gen(function* () {
|
|
|
78148
78607
|
const sourcePath = path.join(input.directory, subdirectory, configFilename);
|
|
78149
78608
|
const targetPath = path.resolve(resolvedTempDirectory, subdirectory, configFilename);
|
|
78150
78609
|
if (!isPathInsideDirectory(targetPath, resolvedTempDirectory)) continue;
|
|
78151
|
-
if (
|
|
78610
|
+
if (isFile(sourcePath) && !fs.existsSync(targetPath)) {
|
|
78152
78611
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
78153
78612
|
fs.cpSync(sourcePath, targetPath);
|
|
78154
78613
|
}
|
|
@@ -78400,6 +78859,7 @@ const buildJsonReport = (input) => {
|
|
|
78400
78859
|
directory: input.directory,
|
|
78401
78860
|
diff: toJsonDiff(input.diff),
|
|
78402
78861
|
projects,
|
|
78862
|
+
...input.skippedProjects && input.skippedProjects.length > 0 ? { skippedProjects: [...input.skippedProjects] } : {},
|
|
78403
78863
|
diagnostics: flattenedDiagnostics,
|
|
78404
78864
|
summary,
|
|
78405
78865
|
elapsedMilliseconds: input.totalElapsedMilliseconds,
|
|
@@ -78424,7 +78884,7 @@ const buildSkippedChecks = (input) => {
|
|
|
78424
78884
|
else if (input.lintPartialFailures.length > 0) skippedCheckReasons["lint:partial"] = input.lintPartialFailures.join("; ");
|
|
78425
78885
|
if (input.didDeadCodeFail && input.deadCodeFailureReason !== null) skippedCheckReasons["dead-code"] = input.deadCodeFailureReason;
|
|
78426
78886
|
if (input.supplyChainOverlapTimedOut) skippedCheckReasons["supply-chain"] = "Supply-chain analysis timed out and was skipped.";
|
|
78427
|
-
if (input.securityScanFailed) skippedCheckReasons["security-scan"] = "Security scan failed and was skipped.";
|
|
78887
|
+
if (input.securityScanFailed) skippedCheckReasons["security-scan"] = input.securityScanFailureReason ?? "Security scan failed and was skipped.";
|
|
78428
78888
|
return {
|
|
78429
78889
|
skippedChecks,
|
|
78430
78890
|
skippedCheckReasons
|
|
@@ -78564,6 +79024,33 @@ const getChangedLineRanges = (input) => runPromise(gen(function* () {
|
|
|
78564
79024
|
* instead of being improvised per diagnostic.
|
|
78565
79025
|
*/
|
|
78566
79026
|
const buildRuleDocsUrl = (plugin, rule) => `${DOCS_RULES_BASE_URL}/${plugin}/${rule}`;
|
|
79027
|
+
const collectSourceFileCountsByDirectory = async (rootDirectory, projectDirectories, signal) => {
|
|
79028
|
+
const resolvedRootDirectory = Path.resolve(rootDirectory);
|
|
79029
|
+
const sourceFileCounts = /* @__PURE__ */ new Map();
|
|
79030
|
+
const projectDirectoryByRelativePath = /* @__PURE__ */ new Map();
|
|
79031
|
+
for (const projectDirectory of projectDirectories) {
|
|
79032
|
+
const resolvedProjectDirectory = Path.resolve(projectDirectory);
|
|
79033
|
+
const relativeDirectory = Path.relative(resolvedRootDirectory, resolvedProjectDirectory);
|
|
79034
|
+
if (Path.isAbsolute(relativeDirectory) || relativeDirectory.startsWith("..")) continue;
|
|
79035
|
+
sourceFileCounts.set(resolvedProjectDirectory, 0);
|
|
79036
|
+
projectDirectoryByRelativePath.set(relativeDirectory.replaceAll(Path.sep, "/"), resolvedProjectDirectory);
|
|
79037
|
+
}
|
|
79038
|
+
const sourceFilePaths = await listSourceFilesCooperative(resolvedRootDirectory, signal);
|
|
79039
|
+
for (const sourceFilePath of sourceFilePaths) {
|
|
79040
|
+
let relativeDirectory = Path.posix.dirname(sourceFilePath);
|
|
79041
|
+
while (true) {
|
|
79042
|
+
const normalizedRelativeDirectory = relativeDirectory === "." ? "" : relativeDirectory;
|
|
79043
|
+
const projectDirectory = projectDirectoryByRelativePath.get(normalizedRelativeDirectory);
|
|
79044
|
+
if (projectDirectory !== void 0) {
|
|
79045
|
+
sourceFileCounts.set(projectDirectory, (sourceFileCounts.get(projectDirectory) ?? 0) + 1);
|
|
79046
|
+
break;
|
|
79047
|
+
}
|
|
79048
|
+
if (relativeDirectory === ".") break;
|
|
79049
|
+
relativeDirectory = Path.posix.dirname(relativeDirectory);
|
|
79050
|
+
}
|
|
79051
|
+
}
|
|
79052
|
+
return sourceFileCounts;
|
|
79053
|
+
};
|
|
78567
79054
|
const computeConfigFingerprint = (projectDirectory, version) => {
|
|
78568
79055
|
const parts = [`v=${version}`];
|
|
78569
79056
|
let directory = projectDirectory;
|
|
@@ -78583,59 +79070,34 @@ const createOxlintSpawnSlots = (concurrency) => createWorkerSlots({
|
|
|
78583
79070
|
slotCount: resolveScanConcurrency(concurrency),
|
|
78584
79071
|
createAbortError: createLintPhaseAbortError
|
|
78585
79072
|
});
|
|
78586
|
-
const
|
|
78587
|
-
["HF_DATASETS_CACHE", "huggingface"],
|
|
78588
|
-
["HF_HOME", "huggingface"],
|
|
78589
|
-
["HUGGINGFACE_HUB_CACHE", "huggingface"],
|
|
78590
|
-
["SPACE_ID", "huggingface-spaces"],
|
|
78591
|
-
["CUDA_VISIBLE_DEVICES", "cuda"],
|
|
78592
|
-
["NVIDIA_VISIBLE_DEVICES", "nvidia"],
|
|
78593
|
-
["ROCR_VISIBLE_DEVICES", "rocm"],
|
|
78594
|
-
["TPU_NAME", "google-tpu"],
|
|
79073
|
+
const AI_TRAINING_BY_ENVIRONMENT_VARIABLE = [
|
|
78595
79074
|
["WANDB_RUN_ID", "wandb"],
|
|
78596
79075
|
["WANDB_SWEEP_ID", "wandb"],
|
|
78597
79076
|
["MLFLOW_RUN_ID", "mlflow"],
|
|
78598
|
-
["MLFLOW_TRACKING_URI", "mlflow"],
|
|
78599
79077
|
["COMET_EXPERIMENT_KEY", "comet"],
|
|
78600
79078
|
["NEPTUNE_RUN_ID", "neptune"],
|
|
78601
79079
|
["CLEARML_TASK_ID", "clearml"],
|
|
78602
79080
|
["DVC_STAGE", "dvc"],
|
|
78603
79081
|
["RAY_WORKER_PROCESS", "ray"],
|
|
78604
|
-
["RAY_ADDRESS", "ray"],
|
|
78605
|
-
["MUJOCO_GL", "mujoco"],
|
|
78606
|
-
["MUJOCO_PATH", "mujoco"],
|
|
78607
|
-
["GYM_DISABLE_ENV_CHECKER", "gymnasium"],
|
|
78608
79082
|
["SM_TRAINING_ENV", "sagemaker"],
|
|
78609
79083
|
["TRAINING_JOB_ARN", "sagemaker"],
|
|
78610
|
-
["SAGEMAKER_BASE_DIR", "sagemaker"],
|
|
78611
79084
|
["AZUREML_RUN_ID", "azure-ml"],
|
|
78612
|
-
["AZURE_ML_MODEL_DIR", "azure-ml"],
|
|
78613
|
-
["CLOUD_ML_PROJECT_ID", "vertex-ai"],
|
|
78614
|
-
["VERTEX_AI_LOG_LEVEL", "vertex-ai"],
|
|
78615
|
-
["DET_MASTER", "determined-ai"],
|
|
78616
|
-
["LIGHTNING_USER_ID", "lightning-ai"],
|
|
78617
79085
|
["FLYTE_INTERNAL_EXECUTION_ID", "flyte"],
|
|
78618
|
-
["ARGO_WORKFLOW_NAME", "argo-workflows"],
|
|
78619
79086
|
["KFP_POD_NAME", "kubeflow-pipelines"],
|
|
78620
79087
|
["KAGGLE_KERNEL_RUN_TYPE", "kaggle"],
|
|
78621
79088
|
["COLAB_BACKEND_VERSION", "google-colab"],
|
|
78622
|
-
["DATABRICKS_RUNTIME_VERSION", "databricks"],
|
|
78623
79089
|
["DAYTONA_WS_ID", "daytona"],
|
|
78624
|
-
["DAYTONA_WS_NAME", "daytona"],
|
|
78625
79090
|
["E2B_SANDBOX_ID", "e2b"],
|
|
78626
79091
|
["MODAL_FUNCTION_ID", "modal"],
|
|
78627
79092
|
["MODAL_TASK_ID", "modal"],
|
|
78628
79093
|
["RUNPOD_POD_ID", "runpod"],
|
|
78629
|
-
["REPLICATE_USERNAME", "replicate"],
|
|
78630
79094
|
["VAST_CONTAINERLABEL", "vast-ai"],
|
|
78631
|
-
["HARBOR_URL", "harbor"],
|
|
78632
|
-
["HARBOR_HOSTNAME", "harbor"],
|
|
78633
79095
|
["SWE_BENCH_TASK", "swe-bench"],
|
|
78634
79096
|
["SWEBENCH_TASK", "swe-bench"],
|
|
78635
79097
|
["SWE_AGENT_MODEL", "swe-agent"]
|
|
78636
79098
|
];
|
|
78637
|
-
const detectAiTrainingEnvironment = () => {
|
|
78638
|
-
for (const [
|
|
79099
|
+
const detectAiTrainingEnvironment = (environment = process.env) => {
|
|
79100
|
+
for (const [environmentVariable, label] of AI_TRAINING_BY_ENVIRONMENT_VARIABLE) if (environment[environmentVariable]?.trim()) return label;
|
|
78639
79101
|
return null;
|
|
78640
79102
|
};
|
|
78641
79103
|
const groupBy = (items, keyFn) => {
|
|
@@ -78663,16 +79125,17 @@ const groupBy = (items, keyFn) => {
|
|
|
78663
79125
|
* a 404. Gate the directive on this predicate.
|
|
78664
79126
|
*/
|
|
78665
79127
|
const hasPublishedFixRecipe = (diagnostic) => diagnostic.plugin === "react-doctor" && Object.hasOwn(REACT_DOCTOR_RULE_REGISTRY, diagnostic.rule);
|
|
78666
|
-
const
|
|
79128
|
+
const stringValues = (values) => Array.isArray(values) ? values.filter((value) => typeof value === "string") : [];
|
|
79129
|
+
const unionValues = (baseValues, overrideValues) => [...new Set([...stringValues(baseValues), ...stringValues(overrideValues)])];
|
|
78667
79130
|
const mergeIgnores = (baseIgnore, overrideIgnore) => {
|
|
78668
79131
|
const mergedIgnore = {
|
|
78669
79132
|
...baseIgnore,
|
|
78670
79133
|
...overrideIgnore
|
|
78671
79134
|
};
|
|
78672
|
-
if (baseIgnore.rules
|
|
78673
|
-
if (baseIgnore.files
|
|
78674
|
-
if (baseIgnore.tags
|
|
78675
|
-
if (baseIgnore.overrides
|
|
79135
|
+
if (baseIgnore.rules !== void 0 || overrideIgnore.rules !== void 0) mergedIgnore.rules = unionValues(baseIgnore.rules, overrideIgnore.rules);
|
|
79136
|
+
if (baseIgnore.files !== void 0 || overrideIgnore.files !== void 0) mergedIgnore.files = unionValues(baseIgnore.files, overrideIgnore.files);
|
|
79137
|
+
if (baseIgnore.tags !== void 0 || overrideIgnore.tags !== void 0) mergedIgnore.tags = unionValues(baseIgnore.tags, overrideIgnore.tags);
|
|
79138
|
+
if (baseIgnore.overrides !== void 0 || overrideIgnore.overrides !== void 0) mergedIgnore.overrides = [...Array.isArray(baseIgnore.overrides) ? baseIgnore.overrides : [], ...Array.isArray(overrideIgnore.overrides) ? overrideIgnore.overrides : []];
|
|
78676
79139
|
return mergedIgnore;
|
|
78677
79140
|
};
|
|
78678
79141
|
/**
|
|
@@ -78867,6 +79330,21 @@ const NON_INTERACTIVE_ENVIRONMENT_VARIABLES = [
|
|
|
78867
79330
|
];
|
|
78868
79331
|
const isNonInteractiveEnvironment = () => NON_INTERACTIVE_ENVIRONMENT_VARIABLES.some((envVariable) => Boolean(process.env[envVariable])) || isCodingAgentEnvironment();
|
|
78869
79332
|
//#endregion
|
|
79333
|
+
//#region src/cli/utils/cli-input-error.ts
|
|
79334
|
+
/**
|
|
79335
|
+
* A mistake in *how the CLI was invoked* — a malformed `<file>:<line>`
|
|
79336
|
+
* argument, mutually exclusive flags, or an unknown `--project` name. This is
|
|
79337
|
+
* user input, not a react-doctor bug, so `isExpectedUserError` routes it
|
|
79338
|
+
* through `handleUserError`: a clean, single-line message with no Sentry
|
|
79339
|
+
* report and no "Something went wrong, open a prefilled issue" block.
|
|
79340
|
+
*
|
|
79341
|
+
* The `message` is rendered verbatim, so it must read as a complete,
|
|
79342
|
+
* human-readable sentence that tells the user how to fix their invocation.
|
|
79343
|
+
*/
|
|
79344
|
+
var CliInputError = class extends Error {
|
|
79345
|
+
name = "CliInputError";
|
|
79346
|
+
};
|
|
79347
|
+
//#endregion
|
|
78870
79348
|
//#region src/cli/utils/constants.ts
|
|
78871
79349
|
const REACT_DOCTOR_CONFIG_PROJECT_NAME = "react-doctor";
|
|
78872
79350
|
const STAGED_FILES_TEMP_DIR_PREFIX = "react-doctor-staged-";
|
|
@@ -78911,7 +79389,7 @@ const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
|
|
|
78911
79389
|
const SCAN_RESULT_CACHE_FILENAME = "scan-cache.json";
|
|
78912
79390
|
const RUN_GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
78913
79391
|
const GH_DEFAULT_BRANCH_PROBE_TIMEOUT_MS = 5e3;
|
|
78914
|
-
const TUI_REPORT_COMPACT_MAX_ROWS =
|
|
79392
|
+
const TUI_REPORT_COMPACT_MAX_ROWS = 23;
|
|
78915
79393
|
const TUI_REPORT_DETAIL_WIDTH_FRACTION = .6;
|
|
78916
79394
|
const CI_TRUST_COMPANIES = "PayPal, Rippling, and Alibaba";
|
|
78917
79395
|
const WELCOME_EXPLANATION_HOLD_MS = 1e3;
|
|
@@ -78934,8 +79412,11 @@ const METRIC = {
|
|
|
78934
79412
|
scanPhaseDuration: "scan.phase_duration",
|
|
78935
79413
|
scanFiles: "scan.files",
|
|
78936
79414
|
scanScore: "scan.score",
|
|
79415
|
+
scanScoreRetry: "scan.score_retry",
|
|
78937
79416
|
scanClean: "scan.clean",
|
|
78938
79417
|
scanCheckSkipped: "scan.check_skipped",
|
|
79418
|
+
scanProjectSkipped: "scan.project_skipped",
|
|
79419
|
+
scanWorkspaceDeadCodeShared: "scan.workspace_deadcode_shared",
|
|
78939
79420
|
scanNoReactDetected: "scan.no_react_detected",
|
|
78940
79421
|
baselineDegraded: "baseline.degraded",
|
|
78941
79422
|
ruleFired: "rule.fired",
|
|
@@ -78962,9 +79443,14 @@ const METRIC = {
|
|
|
78962
79443
|
lspScanCompleted: "lsp.scan.completed",
|
|
78963
79444
|
lspScanDuration: "lsp.scan.duration",
|
|
78964
79445
|
lspScanDiagnostics: "lsp.scan.diagnostics",
|
|
78965
|
-
tuiActionMenuOpened: "tui.action_menu_opened",
|
|
78966
79446
|
tuiCompactReportShown: "tui.compact_report_shown",
|
|
78967
79447
|
tuiFindingNavigated: "tui.finding_navigated",
|
|
79448
|
+
tuiIssueStreamShown: "tui.issue_stream_shown",
|
|
79449
|
+
tuiProjectPathContextShown: "tui.project_path_context_shown",
|
|
79450
|
+
tuiProjectSelectInlineShown: "tui.project_select_inline_shown",
|
|
79451
|
+
tuiReportActionSelected: "tui.report_action_selected",
|
|
79452
|
+
tuiCancelled: "tui.cancelled",
|
|
79453
|
+
tuiScanInlineShown: "tui.scan_inline_shown",
|
|
78968
79454
|
tuiStackedReportCapped: "tui.stacked_report_capped",
|
|
78969
79455
|
aiTrainingWarningShown: "ai.training.warning_shown",
|
|
78970
79456
|
jsonOutUsed: "json.out_used"
|
|
@@ -79010,7 +79496,7 @@ const makeNoopConsole = () => ({
|
|
|
79010
79496
|
});
|
|
79011
79497
|
//#endregion
|
|
79012
79498
|
//#region src/cli/utils/version.ts
|
|
79013
|
-
const VERSION = "0.9.
|
|
79499
|
+
const VERSION = "0.9.4-dev.25dbf6d";
|
|
79014
79500
|
//#endregion
|
|
79015
79501
|
//#region src/cli/utils/json-mode.ts
|
|
79016
79502
|
let context = null;
|
|
@@ -79054,9 +79540,14 @@ const enableJsonMode = ({ compact, directory, outputFile }) => {
|
|
|
79054
79540
|
directory,
|
|
79055
79541
|
startTime: performance$1.now(),
|
|
79056
79542
|
mode: "full",
|
|
79057
|
-
outputFile:
|
|
79543
|
+
outputFile: null
|
|
79058
79544
|
};
|
|
79059
79545
|
installSilentConsole();
|
|
79546
|
+
if (outputFile) {
|
|
79547
|
+
const resolvedOutputFile = Path.resolve(outputFile);
|
|
79548
|
+
if (NFS.existsSync(resolvedOutputFile) && !NFS.statSync(resolvedOutputFile).isFile()) throw new CliInputError(`--json-out must point to a file, not ${resolvedOutputFile}.`);
|
|
79549
|
+
context.outputFile = outputFile;
|
|
79550
|
+
}
|
|
79060
79551
|
};
|
|
79061
79552
|
const isJsonModeActive = () => context !== null;
|
|
79062
79553
|
const setJsonReportDirectory = (directory) => {
|
|
@@ -79247,6 +79738,104 @@ const buildSentryScope = (runContext = buildRunContext()) => {
|
|
|
79247
79738
|
};
|
|
79248
79739
|
};
|
|
79249
79740
|
//#endregion
|
|
79741
|
+
//#region src/cli/utils/is-env-flag-enabled.ts
|
|
79742
|
+
const isEnvFlagEnabled = (value) => value === "1" || value?.toLowerCase() === "true";
|
|
79743
|
+
//#endregion
|
|
79744
|
+
//#region src/cli/utils/is-environment-error.ts
|
|
79745
|
+
const ENVIRONMENT_ERROR_CODES = new Set([
|
|
79746
|
+
"EACCES",
|
|
79747
|
+
"EIO",
|
|
79748
|
+
"ENOSPC",
|
|
79749
|
+
"ENOTDIR",
|
|
79750
|
+
"EPERM",
|
|
79751
|
+
"EROFS"
|
|
79752
|
+
]);
|
|
79753
|
+
const ENVIRONMENT_FILESYSTEM_SYSCALLS = new Set([
|
|
79754
|
+
"lstat",
|
|
79755
|
+
"open",
|
|
79756
|
+
"read",
|
|
79757
|
+
"readdir",
|
|
79758
|
+
"readlink",
|
|
79759
|
+
"realpath",
|
|
79760
|
+
"scandir",
|
|
79761
|
+
"stat"
|
|
79762
|
+
]);
|
|
79763
|
+
const TRANSIENT_FILESYSTEM_ERROR_CODES = new Set([
|
|
79764
|
+
"EBUSY",
|
|
79765
|
+
"ETIMEDOUT",
|
|
79766
|
+
"UNKNOWN"
|
|
79767
|
+
]);
|
|
79768
|
+
const findEnvironmentError = (error) => {
|
|
79769
|
+
const pendingErrors = [error];
|
|
79770
|
+
const visitedErrors = /* @__PURE__ */ new Set();
|
|
79771
|
+
while (pendingErrors.length > 0) {
|
|
79772
|
+
const currentError = pendingErrors.pop();
|
|
79773
|
+
if (typeof currentError !== "object" || currentError === null) continue;
|
|
79774
|
+
if (visitedErrors.has(currentError)) continue;
|
|
79775
|
+
visitedErrors.add(currentError);
|
|
79776
|
+
if (isErrnoException(currentError) && typeof currentError.code === "string") {
|
|
79777
|
+
const syscall = currentError.syscall ?? "";
|
|
79778
|
+
if (ENVIRONMENT_ERROR_CODES.has(currentError.code) || currentError.code === "ENOENT" && syscall.startsWith("spawn") || TRANSIENT_FILESYSTEM_ERROR_CODES.has(currentError.code) && ENVIRONMENT_FILESYSTEM_SYSCALLS.has(syscall)) return currentError;
|
|
79779
|
+
}
|
|
79780
|
+
if (currentError instanceof Error && currentError.cause !== void 0) pendingErrors.push(currentError.cause);
|
|
79781
|
+
if (isReactDoctorError(currentError) && "cause" in currentError.reason) pendingErrors.push(currentError.reason.cause);
|
|
79782
|
+
}
|
|
79783
|
+
return null;
|
|
79784
|
+
};
|
|
79785
|
+
const isEnvironmentError = (error) => findEnvironmentError(error) !== null;
|
|
79786
|
+
const formatEnvironmentError = (error) => {
|
|
79787
|
+
const environmentError = findEnvironmentError(error);
|
|
79788
|
+
if (!environmentError) return messageFromUnknown(error);
|
|
79789
|
+
switch (environmentError.code) {
|
|
79790
|
+
case "EBUSY": return environmentError.path ? `Resource busy or locked at ${environmentError.path}. Close the process using it and try again.` : "Resource busy or locked. Close the process using it and try again.";
|
|
79791
|
+
case "ENOSPC": return "No space left on device. Free up disk space and try again.";
|
|
79792
|
+
case "EIO": return "I/O error: the filesystem or disk may be failing. Check your system logs.";
|
|
79793
|
+
case "EROFS": return "Read-only filesystem: cannot write to this location.";
|
|
79794
|
+
case "EACCES":
|
|
79795
|
+
case "EPERM": return environmentError.path ? `Permission denied accessing ${environmentError.path}. Check file permissions and try again.` : "Permission denied. Check file permissions and try again.";
|
|
79796
|
+
case "ENOTDIR": return environmentError.path ? `A file exists at ${environmentError.path} or one of its parent paths where a directory was expected.` : "A file exists where a directory was expected.";
|
|
79797
|
+
case "ENOENT": return "Required command not found. Ensure the tool (e.g. git) is installed and on your PATH.";
|
|
79798
|
+
case "ETIMEDOUT": return environmentError.path ? `Timed out accessing ${environmentError.path}. Check the network or cloud drive and try again.` : "Filesystem access timed out. Check the network or cloud drive and try again.";
|
|
79799
|
+
case "UNKNOWN": return environmentError.path ? `The operating system could not access ${environmentError.path}. Check the filesystem and try again.` : "The operating system could not access the filesystem. Check it and try again.";
|
|
79800
|
+
default: return environmentError.message;
|
|
79801
|
+
}
|
|
79802
|
+
};
|
|
79803
|
+
//#endregion
|
|
79804
|
+
//#region src/cli/utils/is-expected-user-error.ts
|
|
79805
|
+
/**
|
|
79806
|
+
* Whether `error` is an expected, user-actionable failure — the user's project
|
|
79807
|
+
* or input, not a react-doctor bug. Such failures must be kept out of crash
|
|
79808
|
+
* reporting (Sentry + the alertable error-rate metric) and rendered via
|
|
79809
|
+
* `handleUserError` (a plain message — no "Something went wrong", prefilled
|
|
79810
|
+
* issue, Discord link, or Sentry reference), since there is no bug to report.
|
|
79811
|
+
*
|
|
79812
|
+
* Four distinct shapes reach the CLI's catch blocks:
|
|
79813
|
+
*
|
|
79814
|
+
* - **Project-discovery failures** (`NoReactDependencyError`,
|
|
79815
|
+
* `ProjectNotFoundError`, `PackageJsonNotFoundError`, `NotADirectoryError`,
|
|
79816
|
+
* `AmbiguousProjectError`) arrive as their plain legacy classes (so
|
|
79817
|
+
* `isReactDoctorError` is `false` for them) — narrow with
|
|
79818
|
+
* `isProjectDiscoveryError`. Running react-doctor against a directory that
|
|
79819
|
+
* has no React, or a path that doesn't exist, is the canonical example.
|
|
79820
|
+
* - **CLI invocation mistakes** (`CliInputError`): a malformed
|
|
79821
|
+
* `<file>:<line>` argument, mutually exclusive flags, or an unknown
|
|
79822
|
+
* `--project` name.
|
|
79823
|
+
* - **Bad `--diff` input** (`GitBaseBranchInvalid` / `GitBaseBranchMissing`)
|
|
79824
|
+
* stays the tagged `ReactDoctorError`, so dispatch on the reason `_tag`.
|
|
79825
|
+
* - **Environment failures** (`ENOSPC`, `EIO`, `EROFS`, `EACCES`, `EPERM`,
|
|
79826
|
+
* `ENOTDIR`, plus a `spawn`-scoped `ENOENT` for a missing binary) — disk
|
|
79827
|
+
* full / failing / read-only, permission denied, or a path blocked by a
|
|
79828
|
+
* file. React Doctor cannot fix the user's environment; exit cleanly with an
|
|
79829
|
+
* actionable message instead of crashing. See `is-environment-error.ts` for
|
|
79830
|
+
* why the set stays narrow (codes that usually mean our bug keep reaching
|
|
79831
|
+
* Sentry).
|
|
79832
|
+
*
|
|
79833
|
+
* This composes the existing core narrowers rather than introducing a new
|
|
79834
|
+
* error-shape helper (AGENTS.md): it encodes CLI-layer reporting policy, not
|
|
79835
|
+
* knowledge of the `ReactDoctorError` shape.
|
|
79836
|
+
*/
|
|
79837
|
+
const isExpectedUserError = (error) => error instanceof CliInputError || isProjectDiscoveryError(error) || isEnvironmentError(error) || isReactDoctorError(error) && (error.reason._tag === "GitBaseBranchInvalid" || error.reason._tag === "GitBaseBranchMissing");
|
|
79838
|
+
//#endregion
|
|
79250
79839
|
//#region src/cli/utils/anonymize-text.ts
|
|
79251
79840
|
/**
|
|
79252
79841
|
* Free-text fields can carry both a home-directory path (the OS username) and a
|
|
@@ -79368,13 +79957,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
79368
79957
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
79369
79958
|
* standard `SENTRY_RELEASE` override.
|
|
79370
79959
|
*/
|
|
79371
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.
|
|
79960
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.4-dev.25dbf6d`;
|
|
79372
79961
|
/**
|
|
79373
79962
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
79374
79963
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
79375
79964
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
79376
79965
|
*/
|
|
79377
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.
|
|
79966
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.4-dev.25dbf6d") ? "development" : "production");
|
|
79378
79967
|
/**
|
|
79379
79968
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
79380
79969
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -79397,7 +79986,6 @@ const shouldEnableSentry = () => {
|
|
|
79397
79986
|
if (process.env.VITEST || process.env.NODE_ENV === "test") return false;
|
|
79398
79987
|
return true;
|
|
79399
79988
|
};
|
|
79400
|
-
const isEnvFlagEnabled = (value) => value === "1" || value?.toLowerCase() === "true";
|
|
79401
79989
|
/**
|
|
79402
79990
|
* Whether performance traces will actually be recorded — Sentry is live and the
|
|
79403
79991
|
* resolved sample rate is above zero. Used to gate the per-run root span and
|
|
@@ -79440,21 +80028,25 @@ const initializeSentry = () => {
|
|
|
79440
80028
|
isInitialized = true;
|
|
79441
80029
|
resolvedTracesSampleRate = isDebugFlagEnabled() ? 1 : resolveTracesSampleRate();
|
|
79442
80030
|
const { tags, contexts } = buildSentryScope();
|
|
79443
|
-
|
|
79444
|
-
|
|
79445
|
-
|
|
79446
|
-
|
|
79447
|
-
|
|
79448
|
-
|
|
79449
|
-
|
|
79450
|
-
|
|
79451
|
-
|
|
79452
|
-
|
|
79453
|
-
|
|
79454
|
-
|
|
79455
|
-
|
|
79456
|
-
|
|
79457
|
-
|
|
80031
|
+
try {
|
|
80032
|
+
Sentry.init({
|
|
80033
|
+
dsn: process.env.SENTRY_DSN || "https://f253d570240a59b8dbd77b7a548ef133@o4510226365743104.ingest.us.sentry.io/4511487817809920",
|
|
80034
|
+
release: resolveSentryRelease(),
|
|
80035
|
+
environment: resolveSentryEnvironment(),
|
|
80036
|
+
sendDefaultPii: false,
|
|
80037
|
+
tracesSampleRate: resolvedTracesSampleRate,
|
|
80038
|
+
debug: isEnvFlagEnabled(process.env.SENTRY_DEBUG),
|
|
80039
|
+
initialScope: {
|
|
80040
|
+
tags,
|
|
80041
|
+
contexts
|
|
80042
|
+
},
|
|
80043
|
+
beforeSend: (event, hint) => isExpectedUserError(hint.originalException) ? null : scrubSentryEvent(event),
|
|
80044
|
+
beforeSendTransaction: (event) => scrubSentryEvent(event),
|
|
80045
|
+
beforeSendMetric: (metric) => scrubSentryMetric(metric)
|
|
80046
|
+
});
|
|
80047
|
+
} catch {
|
|
80048
|
+
resolvedTracesSampleRate = 0;
|
|
80049
|
+
}
|
|
79458
80050
|
};
|
|
79459
80051
|
//#endregion
|
|
79460
80052
|
//#region src/cli/utils/cli-logger.ts
|
|
@@ -83013,27 +83605,21 @@ const prompts = (questions, options = {}) => {
|
|
|
83013
83605
|
//#endregion
|
|
83014
83606
|
//#region src/cli/utils/to-span-attributes.ts
|
|
83015
83607
|
/**
|
|
83016
|
-
* Converts a Sentry tag map (which permits `null` to denote an
|
|
83017
|
-
* into Sentry/OTel span attributes, which only accept primitives.
|
|
83018
|
-
* are dropped rather than coerced, so an absent signal doesn't
|
|
83019
|
-
* misleading `"null"` attribute.
|
|
83608
|
+
* Converts a Sentry tag map (which permits `null`/`undefined` to denote an
|
|
83609
|
+
* absent signal) into Sentry/OTel span attributes, which only accept primitives.
|
|
83610
|
+
* Missing values are dropped rather than coerced, so an absent signal doesn't
|
|
83611
|
+
* become a misleading `"null"` or `"undefined"` attribute.
|
|
83020
83612
|
*/
|
|
83021
83613
|
const toSpanAttributes = (tags) => {
|
|
83022
83614
|
const attributes = {};
|
|
83023
|
-
for (const [key, value] of Object.entries(tags)) if (value
|
|
83615
|
+
for (const [key, value] of Object.entries(tags)) if (value != null) attributes[key] = value;
|
|
83024
83616
|
return attributes;
|
|
83025
83617
|
};
|
|
83026
83618
|
//#endregion
|
|
83027
83619
|
//#region src/cli/utils/record-metric.ts
|
|
83028
|
-
const cleanAttributes = (attributes) => {
|
|
83029
|
-
const cleaned = {};
|
|
83030
|
-
if (!attributes) return cleaned;
|
|
83031
|
-
for (const [key, value] of Object.entries(attributes)) if (value !== null && value !== void 0) cleaned[key] = value;
|
|
83032
|
-
return cleaned;
|
|
83033
|
-
};
|
|
83034
83620
|
const withRunAttributes = (attributes) => ({
|
|
83035
83621
|
...toSpanAttributes(buildSentryScope().tags),
|
|
83036
|
-
...
|
|
83622
|
+
...toSpanAttributes(attributes ?? {})
|
|
83037
83623
|
});
|
|
83038
83624
|
/**
|
|
83039
83625
|
* Emits a Sentry counter. A guarded, swallow-on-throw no-op unless the CLI's
|
|
@@ -90775,34 +91361,6 @@ const runCiConfig = async (options = {}) => {
|
|
|
90775
91361
|
});
|
|
90776
91362
|
};
|
|
90777
91363
|
//#endregion
|
|
90778
|
-
//#region src/cli/utils/is-environment-error.ts
|
|
90779
|
-
const ENVIRONMENT_ERROR_CODES = new Set([
|
|
90780
|
-
"ENOSPC",
|
|
90781
|
-
"EIO",
|
|
90782
|
-
"EROFS",
|
|
90783
|
-
"EACCES",
|
|
90784
|
-
"EPERM",
|
|
90785
|
-
"ENOTDIR"
|
|
90786
|
-
]);
|
|
90787
|
-
const isEnvironmentError = (error) => {
|
|
90788
|
-
if (!isErrnoException(error) || typeof error.code !== "string") return false;
|
|
90789
|
-
if (error.code === "ENOENT") return error.syscall?.startsWith("spawn") ?? false;
|
|
90790
|
-
return ENVIRONMENT_ERROR_CODES.has(error.code);
|
|
90791
|
-
};
|
|
90792
|
-
const formatEnvironmentError = (error) => {
|
|
90793
|
-
if (!isErrnoException(error)) return messageFromUnknown(error);
|
|
90794
|
-
switch (error.code) {
|
|
90795
|
-
case "ENOSPC": return "No space left on device. Free up disk space and try again.";
|
|
90796
|
-
case "EIO": return "I/O error: the filesystem or disk may be failing. Check your system logs.";
|
|
90797
|
-
case "EROFS": return "Read-only filesystem: cannot write to this location.";
|
|
90798
|
-
case "EACCES":
|
|
90799
|
-
case "EPERM": return error.path ? `Permission denied accessing ${error.path}. Check file permissions and try again.` : "Permission denied. Check file permissions and try again.";
|
|
90800
|
-
case "ENOTDIR": return error.path ? `A file exists at ${error.path} or one of its parent paths where a directory was expected.` : "A file exists where a directory was expected.";
|
|
90801
|
-
case "ENOENT": return "Required command not found. Ensure the tool (e.g. git) is installed and on your PATH.";
|
|
90802
|
-
default: return error.message;
|
|
90803
|
-
}
|
|
90804
|
-
};
|
|
90805
|
-
//#endregion
|
|
90806
91364
|
//#region src/cli/utils/handle-error.ts
|
|
90807
91365
|
const OTLP_ENDPOINT_ENVIRONMENT_VARIABLE = "REACT_DOCTOR_OTLP_ENDPOINT";
|
|
90808
91366
|
const OTLP_AUTH_HEADER_ENVIRONMENT_VARIABLE = "REACT_DOCTOR_OTLP_AUTH_HEADER";
|
|
@@ -90904,56 +91462,6 @@ const handleUserError = (error, options = {}) => {
|
|
|
90904
91462
|
process.exitCode = 1;
|
|
90905
91463
|
};
|
|
90906
91464
|
//#endregion
|
|
90907
|
-
//#region src/cli/utils/cli-input-error.ts
|
|
90908
|
-
/**
|
|
90909
|
-
* A mistake in *how the CLI was invoked* — a malformed `<file>:<line>`
|
|
90910
|
-
* argument, mutually exclusive flags, or an unknown `--project` name. This is
|
|
90911
|
-
* user input, not a react-doctor bug, so `isExpectedUserError` routes it
|
|
90912
|
-
* through `handleUserError`: a clean, single-line message with no Sentry
|
|
90913
|
-
* report and no "Something went wrong, open a prefilled issue" block.
|
|
90914
|
-
*
|
|
90915
|
-
* The `message` is rendered verbatim, so it must read as a complete,
|
|
90916
|
-
* human-readable sentence that tells the user how to fix their invocation.
|
|
90917
|
-
*/
|
|
90918
|
-
var CliInputError = class extends Error {
|
|
90919
|
-
name = "CliInputError";
|
|
90920
|
-
};
|
|
90921
|
-
//#endregion
|
|
90922
|
-
//#region src/cli/utils/is-expected-user-error.ts
|
|
90923
|
-
/**
|
|
90924
|
-
* Whether `error` is an expected, user-actionable failure — the user's project
|
|
90925
|
-
* or input, not a react-doctor bug. Such failures must be kept out of crash
|
|
90926
|
-
* reporting (Sentry + the alertable error-rate metric) and rendered via
|
|
90927
|
-
* `handleUserError` (a plain message — no "Something went wrong", prefilled
|
|
90928
|
-
* issue, Discord link, or Sentry reference), since there is no bug to report.
|
|
90929
|
-
*
|
|
90930
|
-
* Four distinct shapes reach the CLI's catch blocks:
|
|
90931
|
-
*
|
|
90932
|
-
* - **Project-discovery failures** (`NoReactDependencyError`,
|
|
90933
|
-
* `ProjectNotFoundError`, `PackageJsonNotFoundError`, `NotADirectoryError`,
|
|
90934
|
-
* `AmbiguousProjectError`) arrive as their plain legacy classes (so
|
|
90935
|
-
* `isReactDoctorError` is `false` for them) — narrow with
|
|
90936
|
-
* `isProjectDiscoveryError`. Running react-doctor against a directory that
|
|
90937
|
-
* has no React, or a path that doesn't exist, is the canonical example.
|
|
90938
|
-
* - **CLI invocation mistakes** (`CliInputError`): a malformed
|
|
90939
|
-
* `<file>:<line>` argument, mutually exclusive flags, or an unknown
|
|
90940
|
-
* `--project` name.
|
|
90941
|
-
* - **Bad `--diff` input** (`GitBaseBranchInvalid` / `GitBaseBranchMissing`)
|
|
90942
|
-
* stays the tagged `ReactDoctorError`, so dispatch on the reason `_tag`.
|
|
90943
|
-
* - **Environment failures** (`ENOSPC`, `EIO`, `EROFS`, `EACCES`, `EPERM`,
|
|
90944
|
-
* `ENOTDIR`, plus a `spawn`-scoped `ENOENT` for a missing binary) — disk
|
|
90945
|
-
* full / failing / read-only, permission denied, or a path blocked by a
|
|
90946
|
-
* file. React Doctor cannot fix the user's environment; exit cleanly with an
|
|
90947
|
-
* actionable message instead of crashing. See `is-environment-error.ts` for
|
|
90948
|
-
* why the set stays narrow (codes that usually mean our bug keep reaching
|
|
90949
|
-
* Sentry).
|
|
90950
|
-
*
|
|
90951
|
-
* This composes the existing core narrowers rather than introducing a new
|
|
90952
|
-
* error-shape helper (AGENTS.md): it encodes CLI-layer reporting policy, not
|
|
90953
|
-
* knowledge of the `ReactDoctorError` shape.
|
|
90954
|
-
*/
|
|
90955
|
-
const isExpectedUserError = (error) => error instanceof CliInputError || isProjectDiscoveryError(error) || isEnvironmentError(error) || isReactDoctorError(error) && (error.reason._tag === "GitBaseBranchInvalid" || error.reason._tag === "GitBaseBranchMissing");
|
|
90956
|
-
//#endregion
|
|
90957
91465
|
//#region src/cli/utils/print-branded-header.ts
|
|
90958
91466
|
/**
|
|
90959
91467
|
* Single branded line every command prints first when not in JSON
|
|
@@ -91050,6 +91558,19 @@ const ciInstallAction = (options) => runCiCommand("install", runCiInstall, optio
|
|
|
91050
91558
|
const ciUpgradeAction = (options) => runCiCommand("upgrade", runCiUpgrade, options);
|
|
91051
91559
|
const ciConfigAction = (options) => runCiCommand("config", runCiConfig, options);
|
|
91052
91560
|
//#endregion
|
|
91561
|
+
//#region src/cli/utils/active-scan-abort-registry.ts
|
|
91562
|
+
const activeScanAbortControllers = /* @__PURE__ */ new Set();
|
|
91563
|
+
const activeScanAbortRegistry = {
|
|
91564
|
+
register: (controller) => {
|
|
91565
|
+
activeScanAbortControllers.add(controller);
|
|
91566
|
+
return () => activeScanAbortControllers.delete(controller);
|
|
91567
|
+
},
|
|
91568
|
+
abortAll: () => {
|
|
91569
|
+
for (const controller of activeScanAbortControllers) controller.abort();
|
|
91570
|
+
activeScanAbortControllers.clear();
|
|
91571
|
+
}
|
|
91572
|
+
};
|
|
91573
|
+
//#endregion
|
|
91053
91574
|
//#region src/cli/utils/sentry-tracer.ts
|
|
91054
91575
|
const toHrTime = (epochNanoseconds) => [Number(epochNanoseconds / NANOSECONDS_PER_SECOND), Number(epochNanoseconds % NANOSECONDS_PER_SECOND)];
|
|
91055
91576
|
const SPAN_KIND_TO_OTEL = {
|
|
@@ -91466,7 +91987,14 @@ const isInspectResultComplete = (result) => isScanComplete({
|
|
|
91466
91987
|
skippedCheckReasonCount: Object.keys(result.skippedCheckReasons ?? {}).length
|
|
91467
91988
|
});
|
|
91468
91989
|
//#endregion
|
|
91990
|
+
//#region src/cli/utils/resolve-score-unavailable-reason.ts
|
|
91991
|
+
const resolveScoreUnavailableReason = (input) => {
|
|
91992
|
+
if (input.isScoreDisabled) return "disabled";
|
|
91993
|
+
return input.isAnalysisIncomplete ? "analysis-incomplete" : "api-unavailable";
|
|
91994
|
+
};
|
|
91995
|
+
//#endregion
|
|
91469
91996
|
//#region src/cli/utils/scan-result-cache.ts
|
|
91997
|
+
const createScanResultCacheInvocationState = () => ({ repositoryIdentityByRoot: /* @__PURE__ */ new Map() });
|
|
91470
91998
|
const CACHE_DISABLED_VALUES = new Set(["1", "true"]);
|
|
91471
91999
|
const TOOLCHAIN_PACKAGE_SPECIFIERS = [
|
|
91472
92000
|
"oxlint/package.json",
|
|
@@ -91551,7 +92079,7 @@ const buildDirtyPathContentFingerprint = (absolutePath, statusCode) => {
|
|
|
91551
92079
|
if (stats.size > 10485760) return null;
|
|
91552
92080
|
return hashFileContents(absolutePath);
|
|
91553
92081
|
};
|
|
91554
|
-
const buildWorktreeFingerprint = (projectDirectory) => {
|
|
92082
|
+
const buildWorktreeFingerprint = (projectDirectory, repositoryRoot) => {
|
|
91555
92083
|
const statusOutput = runGit(projectDirectory, [
|
|
91556
92084
|
"status",
|
|
91557
92085
|
"--porcelain=v1",
|
|
@@ -91563,8 +92091,6 @@ const buildWorktreeFingerprint = (projectDirectory) => {
|
|
|
91563
92091
|
const statusEntries = parseWorktreeStatusRecords(statusOutput);
|
|
91564
92092
|
if (statusEntries === null) return null;
|
|
91565
92093
|
if (statusEntries.length > 300) return null;
|
|
91566
|
-
const repositoryRoot = runGit(projectDirectory, ["rev-parse", "--show-toplevel"]);
|
|
91567
|
-
if (repositoryRoot === null) return null;
|
|
91568
92094
|
const dirtyEntries = [];
|
|
91569
92095
|
for (const statusEntry of statusEntries) {
|
|
91570
92096
|
const contentFingerprint = buildDirtyPathContentFingerprint(path.join(repositoryRoot, statusEntry.path), statusEntry.statusCode);
|
|
@@ -91580,6 +92106,20 @@ const buildWorktreeFingerprint = (projectDirectory) => {
|
|
|
91580
92106
|
return firstSortKey < secondSortKey ? -1 : firstSortKey > secondSortKey ? 1 : 0;
|
|
91581
92107
|
});
|
|
91582
92108
|
};
|
|
92109
|
+
const resolveRepositoryCacheIdentity = (projectDirectory, invocationState) => {
|
|
92110
|
+
const repositoryRoot = runGit(projectDirectory, ["rev-parse", "--show-toplevel"]);
|
|
92111
|
+
if (repositoryRoot === null) return null;
|
|
92112
|
+
const cachedIdentity = invocationState.repositoryIdentityByRoot.get(repositoryRoot);
|
|
92113
|
+
if (cachedIdentity !== void 0) return cachedIdentity;
|
|
92114
|
+
const worktreeFingerprint = buildWorktreeFingerprint(projectDirectory, repositoryRoot);
|
|
92115
|
+
const headSha = readHeadSha(repositoryRoot);
|
|
92116
|
+
const identity = worktreeFingerprint === null || headSha === null ? null : {
|
|
92117
|
+
headSha,
|
|
92118
|
+
worktreeFingerprint
|
|
92119
|
+
};
|
|
92120
|
+
invocationState.repositoryIdentityByRoot.set(repositoryRoot, identity);
|
|
92121
|
+
return identity;
|
|
92122
|
+
};
|
|
91583
92123
|
const DOTENV_FILE_NAME_PATTERN = /^\.env(\.|$)/;
|
|
91584
92124
|
const resolveDotenvFingerprint = (projectDirectory) => {
|
|
91585
92125
|
try {
|
|
@@ -91674,16 +92214,14 @@ const resolveToolchainFingerprint = (nodeBinaryPath) => {
|
|
|
91674
92214
|
const buildScanResultCacheKey = (input) => {
|
|
91675
92215
|
if (isCacheGloballyDisabled()) return null;
|
|
91676
92216
|
if (!isGitIdentityTrustworthy(input.projectDirectory)) return null;
|
|
91677
|
-
const
|
|
91678
|
-
if (
|
|
91679
|
-
const headSha = readHeadSha(input.projectDirectory);
|
|
91680
|
-
if (headSha === null) return null;
|
|
92217
|
+
const repositoryIdentity = resolveRepositoryCacheIdentity(input.projectDirectory, input.invocationState);
|
|
92218
|
+
if (repositoryIdentity === null) return null;
|
|
91681
92219
|
if (stringifyStableJson(input.userConfig) === null) return null;
|
|
91682
92220
|
const cacheKeyJson = stringifyStableJson({
|
|
91683
92221
|
schemaVersion: 6,
|
|
91684
92222
|
projectIdentity: resolveProjectIdentity(input.projectDirectory),
|
|
91685
|
-
headSha,
|
|
91686
|
-
worktreeFingerprint,
|
|
92223
|
+
headSha: repositoryIdentity.headSha,
|
|
92224
|
+
worktreeFingerprint: repositoryIdentity.worktreeFingerprint,
|
|
91687
92225
|
dotenvFingerprint: resolveDotenvFingerprint(input.projectDirectory),
|
|
91688
92226
|
reactDoctorVersion: input.version,
|
|
91689
92227
|
nodeVersion: process.version,
|
|
@@ -91697,6 +92235,8 @@ const buildScanResultCacheKey = (input) => {
|
|
|
91697
92235
|
deadCode: input.options.deadCode,
|
|
91698
92236
|
supplyChain: input.options.supplyChain,
|
|
91699
92237
|
includePaths: [...input.options.includePaths].sort(),
|
|
92238
|
+
excludedProjectDirectories: [...input.options.excludedProjectDirectories ?? []].sort(),
|
|
92239
|
+
retainExcludedProjectDeadCodeDiagnostics: input.options.retainExcludedProjectDeadCodeDiagnostics ?? false,
|
|
91700
92240
|
customRulesOnly: input.options.customRulesOnly,
|
|
91701
92241
|
respectInlineDisables: input.options.respectInlineDisables,
|
|
91702
92242
|
warnings: input.options.warnings,
|
|
@@ -91774,8 +92314,8 @@ const toCategoryKey = (category) => category.toLowerCase().replace(/[^a-z0-9]+/g
|
|
|
91774
92314
|
* browser and stay group-/filter-/aggregate-able in the Spans dataset.
|
|
91775
92315
|
*
|
|
91776
92316
|
* Value types are preserved verbatim (numbers stay numbers so Sentry infers a
|
|
91777
|
-
* numeric attribute and `p75(...)`/`avg(...)` work; `null`
|
|
91778
|
-
* `toSpanAttributes` can drop absent signals rather than coerce them).
|
|
92317
|
+
* numeric attribute and `p75(...)`/`avg(...)` work; `null`/`undefined` are kept
|
|
92318
|
+
* so `toSpanAttributes` can drop absent signals rather than coerce them).
|
|
91779
92319
|
*/
|
|
91780
92320
|
const withNamespace = (namespace, attributes) => {
|
|
91781
92321
|
const namespaced = {};
|
|
@@ -91841,7 +92381,7 @@ const buildCacheAttributes = (input) => {
|
|
|
91841
92381
|
const warmth = knownSubsystemCount > 0 ? reuseRatioSum / knownSubsystemCount : null;
|
|
91842
92382
|
const temperature = warmth !== null && warmth > 0 ? "warm" : isCacheGloballyDisabled() ? "disabled" : "cold";
|
|
91843
92383
|
return withNamespace("cache", {
|
|
91844
|
-
wholeRepoHit: input.wholeRepoCacheHit
|
|
92384
|
+
wholeRepoHit: input.wholeRepoCacheHit,
|
|
91845
92385
|
temperature,
|
|
91846
92386
|
warmth
|
|
91847
92387
|
});
|
|
@@ -91963,33 +92503,37 @@ const buildOutcomeAttributes = (input) => {
|
|
|
91963
92503
|
...withNamespace("score", {
|
|
91964
92504
|
value: result.score ? result.score.score : null,
|
|
91965
92505
|
label: result.score ? result.score.label : null,
|
|
91966
|
-
available: result.score !== null
|
|
92506
|
+
available: result.score !== null,
|
|
92507
|
+
unavailableReason: result.score === null ? resolveScoreUnavailableReason({
|
|
92508
|
+
isScoreDisabled: input.noScore,
|
|
92509
|
+
isAnalysisIncomplete: input.didLintFail === true || input.didDeadCodeFail === true
|
|
92510
|
+
}) : null
|
|
91967
92511
|
}),
|
|
91968
92512
|
...withNamespace("lint", {
|
|
91969
|
-
failed: input.didLintFail
|
|
91970
|
-
failureReasonKind: input.lintFailureReasonKind
|
|
91971
|
-
partialFailureCount: input.lintPartialFailureCount
|
|
91972
|
-
droppedFileCount: input.lintDroppedFileCount
|
|
91973
|
-
deadlineSkippedFileCount: input.lintDeadlineSkippedFileCount
|
|
91974
|
-
cacheHitFiles: result.lintCacheHitFileCount
|
|
91975
|
-
cacheTotalFiles: result.lintCacheTotalFileCount
|
|
92513
|
+
failed: input.didLintFail,
|
|
92514
|
+
failureReasonKind: input.lintFailureReasonKind,
|
|
92515
|
+
partialFailureCount: input.lintPartialFailureCount,
|
|
92516
|
+
droppedFileCount: input.lintDroppedFileCount,
|
|
92517
|
+
deadlineSkippedFileCount: input.lintDeadlineSkippedFileCount,
|
|
92518
|
+
cacheHitFiles: result.lintCacheHitFileCount,
|
|
92519
|
+
cacheTotalFiles: result.lintCacheTotalFileCount,
|
|
91976
92520
|
cacheHitRatio: ratioOf(result.lintCacheHitFileCount, result.lintCacheTotalFileCount),
|
|
91977
|
-
sidecarReplayedFiles: result.lintSidecarReplayedFileCount
|
|
91978
|
-
sidecarTotalFiles: result.lintSidecarTotalFileCount
|
|
92521
|
+
sidecarReplayedFiles: result.lintSidecarReplayedFileCount,
|
|
92522
|
+
sidecarTotalFiles: result.lintSidecarTotalFileCount,
|
|
91979
92523
|
sidecarReplayRatio: ratioOf(result.lintSidecarReplayedFileCount, result.lintSidecarTotalFileCount)
|
|
91980
92524
|
}),
|
|
91981
92525
|
...withNamespace("deadCode", {
|
|
91982
|
-
failed: input.didDeadCodeFail
|
|
91983
|
-
overlapped: input.deadCodeOverlapped
|
|
91984
|
-
cacheHit: result.deadCodeCacheHit
|
|
91985
|
-
summaryCacheHits: result.deadCodeSummaryCacheHits
|
|
91986
|
-
summaryCacheMisses: result.deadCodeSummaryCacheMisses
|
|
92526
|
+
failed: input.didDeadCodeFail,
|
|
92527
|
+
overlapped: input.deadCodeOverlapped,
|
|
92528
|
+
cacheHit: result.deadCodeCacheHit,
|
|
92529
|
+
summaryCacheHits: result.deadCodeSummaryCacheHits,
|
|
92530
|
+
summaryCacheMisses: result.deadCodeSummaryCacheMisses
|
|
91987
92531
|
}),
|
|
91988
|
-
...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut
|
|
91989
|
-
...withNamespace("securityScan", { failed: input.securityScanFailed
|
|
92532
|
+
...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut }),
|
|
92533
|
+
...withNamespace("securityScan", { failed: input.securityScanFailed }),
|
|
91990
92534
|
...withNamespace("timing", {
|
|
91991
92535
|
elapsedMs: result.elapsedMilliseconds,
|
|
91992
|
-
scanMs: result.scanElapsedMilliseconds
|
|
92536
|
+
scanMs: result.scanElapsedMilliseconds
|
|
91993
92537
|
}),
|
|
91994
92538
|
...withNamespace("migration", {
|
|
91995
92539
|
largestRuleBucketFiles: largestRuleBucket ? largestRuleBucket.fileCount : null,
|
|
@@ -92001,7 +92545,7 @@ const buildOutcomeAttributes = (input) => {
|
|
|
92001
92545
|
new: summary.totalDiagnosticCount,
|
|
92002
92546
|
fixed: result.baselineDelta.fixedCount,
|
|
92003
92547
|
baseTotal: result.baselineDelta.baseTotalCount,
|
|
92004
|
-
crossFileMatches: result.baselineDelta.crossFileMatchCount
|
|
92548
|
+
crossFileMatches: result.baselineDelta.crossFileMatchCount,
|
|
92005
92549
|
degraded: false
|
|
92006
92550
|
}));
|
|
92007
92551
|
else if (input.gateExempt) attributes["baseline.degraded"] = true;
|
|
@@ -92010,7 +92554,7 @@ const buildOutcomeAttributes = (input) => {
|
|
|
92010
92554
|
const buildActionAttributes = () => {
|
|
92011
92555
|
const { githubActorAssociation } = resolveGithubActionsScoreMetadata();
|
|
92012
92556
|
return withNamespace("action", {
|
|
92013
|
-
actorAssociation: githubActorAssociation
|
|
92557
|
+
actorAssociation: githubActorAssociation,
|
|
92014
92558
|
runnerOs: detectRunnerOs(),
|
|
92015
92559
|
comment: readEnvBoolean(ACTION_INPUT_ENVIRONMENT_VARIABLES.comment),
|
|
92016
92560
|
reviewComments: readEnvBoolean(ACTION_INPUT_ENVIRONMENT_VARIABLES.reviewComments),
|
|
@@ -92024,7 +92568,7 @@ const buildScanAttributes = (input) => {
|
|
|
92024
92568
|
mode: input.mode,
|
|
92025
92569
|
scope: input.scope,
|
|
92026
92570
|
parallel: input.parallel,
|
|
92027
|
-
workerCount: input.workerCount
|
|
92571
|
+
workerCount: input.workerCount,
|
|
92028
92572
|
maxDurationMs: input.maxDurationMs,
|
|
92029
92573
|
lint: input.lint,
|
|
92030
92574
|
deadCode: input.deadCode,
|
|
@@ -92038,10 +92582,10 @@ const buildScanAttributes = (input) => {
|
|
|
92038
92582
|
hasCustomConfig: input.hasCustomConfig,
|
|
92039
92583
|
rulesConfigured: ruleKeys.length,
|
|
92040
92584
|
rulesDisabled: ruleKeys.filter((key) => ruleOverrides[key] === "off").length,
|
|
92041
|
-
fileCount: input.result?.scannedFileCount
|
|
92042
|
-
nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length
|
|
92043
|
-
htmlFileCount: input.result?.analyzedFiles?.filter((filePath) => HTML_FILE_PATTERN.test(filePath)).length
|
|
92044
|
-
multilineDiagnosticCount: input.result?.diagnostics.filter((diagnostic) => (diagnostic.endLine ?? diagnostic.line) > diagnostic.line).length
|
|
92585
|
+
fileCount: input.result?.scannedFileCount,
|
|
92586
|
+
nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length,
|
|
92587
|
+
htmlFileCount: input.result?.analyzedFiles?.filter((filePath) => HTML_FILE_PATTERN.test(filePath)).length,
|
|
92588
|
+
multilineDiagnosticCount: input.result?.diagnostics.filter((diagnostic) => (diagnostic.endLine ?? diagnostic.line) > diagnostic.line).length
|
|
92045
92589
|
});
|
|
92046
92590
|
};
|
|
92047
92591
|
/**
|
|
@@ -92053,11 +92597,11 @@ const buildScanAttributes = (input) => {
|
|
|
92053
92597
|
* tree up in Sentry's attribute browser and stay filter-/group-/aggregate-able
|
|
92054
92598
|
* in the Spans dataset. Pure and exported so the projection (outcome
|
|
92055
92599
|
* precedence, rule/category rollups, CI knobs, config shape) is unit-testable
|
|
92056
|
-
* without a live Sentry client. `null` values are dropped so absent
|
|
92057
|
-
* never become misleading `"null"` attributes. The run +
|
|
92058
|
-
* (version, command, ci/provider, framework, …) is already
|
|
92059
|
-
* `withSentryRunSpan` / `recordSentryProjectContext`, so this
|
|
92060
|
-
* those don't carry.
|
|
92600
|
+
* without a live Sentry client. `null`/`undefined` values are dropped so absent
|
|
92601
|
+
* signals never become misleading `"null"`/`"undefined"` attributes. The run +
|
|
92602
|
+
* project base context (version, command, ci/provider, framework, …) is already
|
|
92603
|
+
* on the span from `withSentryRunSpan` / `recordSentryProjectContext`, so this
|
|
92604
|
+
* adds only what those don't carry.
|
|
92061
92605
|
*/
|
|
92062
92606
|
const buildRunEventAttributes = (input) => toSpanAttributes({
|
|
92063
92607
|
...buildScanAttributes(input),
|
|
@@ -92531,8 +93075,35 @@ const createDiagnosticEvidenceReader = (rootDirectory, options = {}) => {
|
|
|
92531
93075
|
//#endregion
|
|
92532
93076
|
//#region src/cli/utils/build-no-score-message.ts
|
|
92533
93077
|
const ENTERPRISE_CONTACT_HINT = `Want something custom to your company? Contact us at ${ENTERPRISE_CONTACT_URL}.`;
|
|
92534
|
-
const buildNoScoreMessage = (
|
|
92535
|
-
|
|
93078
|
+
const buildNoScoreMessage = (input) => {
|
|
93079
|
+
const unavailableReason = resolveScoreUnavailableReason({
|
|
93080
|
+
isScoreDisabled: input.isScoreDisabled,
|
|
93081
|
+
isAnalysisIncomplete: input.isAnalysisIncomplete ?? false
|
|
93082
|
+
});
|
|
93083
|
+
let reason;
|
|
93084
|
+
switch (unavailableReason) {
|
|
93085
|
+
case "disabled":
|
|
93086
|
+
reason = input.disabledMessage ?? "Score disabled by --no-score.";
|
|
93087
|
+
break;
|
|
93088
|
+
case "analysis-incomplete":
|
|
93089
|
+
reason = "Score not shown because lint or dead-code analysis could not complete.";
|
|
93090
|
+
break;
|
|
93091
|
+
case "api-unavailable":
|
|
93092
|
+
reason = "Score unavailable (could not reach the score API).";
|
|
93093
|
+
break;
|
|
93094
|
+
}
|
|
93095
|
+
return `${reason} ${ENTERPRISE_CONTACT_HINT}`;
|
|
93096
|
+
};
|
|
93097
|
+
//#endregion
|
|
93098
|
+
//#region src/cli/utils/has-incomplete-score-analysis.ts
|
|
93099
|
+
const hasIncompleteScoreAnalysis = (skippedChecks) => skippedChecks.includes("lint") || skippedChecks.includes("dead-code");
|
|
93100
|
+
//#endregion
|
|
93101
|
+
//#region src/cli/utils/build-empty-report-message.ts
|
|
93102
|
+
const buildEmptyReportMessage = (input) => {
|
|
93103
|
+
const categoryFilters = [...input.categoryFilters];
|
|
93104
|
+
if (categoryFilters.length > 0) return `No issues found in category ${categoryFilters.join(", ")}!`;
|
|
93105
|
+
if (input.demotedDiagnosticCount > 0) return `No issues found! (${input.demotedDiagnosticCount} demoted from the ${input.outputSurface} surface — see config.surfaces.)`;
|
|
93106
|
+
return "No issues found!";
|
|
92536
93107
|
};
|
|
92537
93108
|
//#endregion
|
|
92538
93109
|
//#region src/cli/utils/render-agent-guidance.ts
|
|
@@ -93279,9 +93850,6 @@ const markOnboardingComplete = (options = {}) => {
|
|
|
93279
93850
|
if (isGatePending(ONBOARDING_GATE, {}, options)) recordGate(ONBOARDING_GATE, {}, options);
|
|
93280
93851
|
};
|
|
93281
93852
|
//#endregion
|
|
93282
|
-
//#region src/cli/utils/render-project-detection.ts
|
|
93283
|
-
const printProjectDetection = (_input) => void_;
|
|
93284
|
-
//#endregion
|
|
93285
93853
|
//#region src/cli/utils/colorize-by-score.ts
|
|
93286
93854
|
const colorizeByScore = (text, score) => {
|
|
93287
93855
|
if (score >= 75) return highlighter.success(text);
|
|
@@ -93495,6 +94063,26 @@ const printNoScoreHeader = (noScoreMessage) => gen(function* () {
|
|
|
93495
94063
|
yield* log("");
|
|
93496
94064
|
});
|
|
93497
94065
|
//#endregion
|
|
94066
|
+
//#region src/cli/utils/build-footer-description-lines.ts
|
|
94067
|
+
const FOOTER_DESCRIPTION_INDENT = " ";
|
|
94068
|
+
const buildFooterDescriptionLines = (description) => {
|
|
94069
|
+
return wrapTextToWidth(description, resolveMeasureWidth(2)).map((line) => highlighter.dim(`${FOOTER_DESCRIPTION_INDENT}${line}`));
|
|
94070
|
+
};
|
|
94071
|
+
//#endregion
|
|
94072
|
+
//#region src/cli/utils/build-footer-link-lines.ts
|
|
94073
|
+
const buildFooterLinkLines = ({ shareUrl }) => [
|
|
94074
|
+
...shareUrl === null ? [] : [
|
|
94075
|
+
` ${highlighter.bold("Share:")} ${highlighter.info(shareUrl)}`,
|
|
94076
|
+
...buildFooterDescriptionLines("Tell others how you did on socials"),
|
|
94077
|
+
""
|
|
94078
|
+
],
|
|
94079
|
+
` ${highlighter.bold("Docs:")} ${highlighter.info(DOCS_URL)}`,
|
|
94080
|
+
...buildFooterDescriptionLines("Learn more about fixing issues, setting up CI/CD, and configuring rules with a config file"),
|
|
94081
|
+
"",
|
|
94082
|
+
` ${highlighter.bold("GitHub:")} ${highlighter.info(CANONICAL_GITHUB_URL)}`,
|
|
94083
|
+
...buildFooterDescriptionLines("Report issues and star the repository!")
|
|
94084
|
+
];
|
|
94085
|
+
//#endregion
|
|
93498
94086
|
//#region src/cli/utils/write-diagnostics-directory.ts
|
|
93499
94087
|
const ruleDumpFileName = (ruleKey) => ruleKey.replace(/\//g, "--") + ".txt";
|
|
93500
94088
|
const readPreviousRuleDumpFileNames = (directory) => {
|
|
@@ -93515,11 +94103,6 @@ const writeDiagnosticsDirectory = (diagnostics, outputDirectory) => {
|
|
|
93515
94103
|
};
|
|
93516
94104
|
//#endregion
|
|
93517
94105
|
//#region src/cli/utils/render-summary.ts
|
|
93518
|
-
const FOOTER_DESCRIPTION_INDENT = " ";
|
|
93519
|
-
const printFooterDescription = (description) => gen(function* () {
|
|
93520
|
-
const wrapWidth = resolveMeasureWidth(2);
|
|
93521
|
-
for (const line of wrapTextToWidth(description, wrapWidth)) yield* log(highlighter.dim(`${FOOTER_DESCRIPTION_INDENT}${line}`));
|
|
93522
|
-
});
|
|
93523
94106
|
const buildShareUrl = (diagnostics, scoreResult, projectName) => {
|
|
93524
94107
|
const errorCount = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
|
|
93525
94108
|
const warningCount = diagnostics.filter((diagnostic) => diagnostic.severity === "warning").length;
|
|
@@ -93536,17 +94119,8 @@ const printFooter = (input) => gen(function* () {
|
|
|
93536
94119
|
yield* log("");
|
|
93537
94120
|
yield* log(buildSectionDivider());
|
|
93538
94121
|
yield* log("");
|
|
93539
|
-
|
|
93540
|
-
|
|
93541
|
-
yield* log(` ${highlighter.bold("Share:")} ${highlighter.info(shareUrl)}`);
|
|
93542
|
-
yield* printFooterDescription("Tell others how you did on socials");
|
|
93543
|
-
yield* log("");
|
|
93544
|
-
}
|
|
93545
|
-
yield* log(` ${highlighter.bold("Docs:")} ${highlighter.info(DOCS_URL)}`);
|
|
93546
|
-
yield* printFooterDescription("Learn more about fixing issues, setting up CI/CD, and configuring rules with a config file");
|
|
93547
|
-
yield* log("");
|
|
93548
|
-
yield* log(` ${highlighter.bold("GitHub:")} ${highlighter.info(CANONICAL_GITHUB_URL)}`);
|
|
93549
|
-
yield* printFooterDescription("Report issues and star the repository!");
|
|
94122
|
+
const shareUrl = input.isOffline ? null : buildShareUrl(input.diagnostics, input.scoreResult, input.projectName);
|
|
94123
|
+
for (const line of buildFooterLinkLines({ shareUrl })) yield* log(line);
|
|
93550
94124
|
});
|
|
93551
94125
|
const printDiagnosticsDump = (diagnostics, outputDirectory, verbose, stream = "stdout") => gen(function* () {
|
|
93552
94126
|
const writtenDirectory = yield* try_({
|
|
@@ -93643,9 +94217,6 @@ const resolveCliCategories = (categoryFlag) => {
|
|
|
93643
94217
|
//#endregion
|
|
93644
94218
|
//#region src/inspect.ts
|
|
93645
94219
|
const silentConsole = makeNoopConsole();
|
|
93646
|
-
const runConsole = (effect) => {
|
|
93647
|
-
runSync(effect);
|
|
93648
|
-
};
|
|
93649
94220
|
const recordOnboardingCompletion = (options) => {
|
|
93650
94221
|
const forceOnboarding = isOnboardingForced();
|
|
93651
94222
|
if (shouldRecordOnboarding({
|
|
@@ -93655,7 +94226,6 @@ const recordOnboardingCompletion = (options) => {
|
|
|
93655
94226
|
isNonInteractiveEnvironment: options.isNonInteractiveEnvironment
|
|
93656
94227
|
})) markOnboardingComplete();
|
|
93657
94228
|
};
|
|
93658
|
-
const formatCategorySelection = (categoryFilters) => [...categoryFilters].join(", ");
|
|
93659
94229
|
const buildChangedLineMatcher = (directory, changedLineRanges) => {
|
|
93660
94230
|
const rangesByFile = /* @__PURE__ */ new Map();
|
|
93661
94231
|
for (const entry of changedLineRanges) rangesByFile.set(toForwardSlashes(entry.file), entry.ranges);
|
|
@@ -93705,7 +94275,10 @@ const mergeInspectOptions = (inputOptions, userConfig) => {
|
|
|
93705
94275
|
maxDurationMs: inputOptions.maxDurationMs ?? null,
|
|
93706
94276
|
baseline: inputOptions.baseline ?? null,
|
|
93707
94277
|
changedLineRanges: inputOptions.changedLineRanges ?? null,
|
|
93708
|
-
supplyChainManifestChanged: inputOptions.supplyChainManifestChanged ?? false
|
|
94278
|
+
supplyChainManifestChanged: inputOptions.supplyChainManifestChanged ?? false,
|
|
94279
|
+
excludedProjectDirectories: inputOptions.excludedProjectDirectories ?? [],
|
|
94280
|
+
retainExcludedProjectDeadCodeDiagnostics: inputOptions.retainExcludedProjectDeadCodeDiagnostics ?? false,
|
|
94281
|
+
precomputedSourceFileCount: inputOptions.precomputedSourceFileCount
|
|
93709
94282
|
};
|
|
93710
94283
|
};
|
|
93711
94284
|
const deriveScope = (options) => {
|
|
@@ -93738,13 +94311,14 @@ const inspectWithOxlintRuntime = async (directory, inputOptions, oxlintRuntime)
|
|
|
93738
94311
|
const deadlineEpochMs = inputOptions.deadlineEpochMs ?? (inputOptions.maxDurationMs != null ? Date.now() + inputOptions.maxDurationMs : null);
|
|
93739
94312
|
const isConcurrentScan = inputOptions.concurrentScan === true;
|
|
93740
94313
|
if (!isConcurrentScan) resetSentryRunState();
|
|
93741
|
-
const
|
|
94314
|
+
const configOverride = inputOptions.configOverride;
|
|
94315
|
+
const hasConfigOverride = configOverride !== void 0;
|
|
93742
94316
|
let scanDirectory;
|
|
93743
94317
|
let userConfig;
|
|
93744
94318
|
let configSourceDirectory;
|
|
93745
94319
|
if (hasConfigOverride) {
|
|
93746
94320
|
scanDirectory = directory;
|
|
93747
|
-
userConfig =
|
|
94321
|
+
userConfig = configOverride;
|
|
93748
94322
|
configSourceDirectory = inputOptions.configSourceDirectory ?? null;
|
|
93749
94323
|
} else {
|
|
93750
94324
|
const scanTarget = await resolveScanTarget(directory);
|
|
@@ -93777,11 +94351,22 @@ const inspectWithOxlintRuntime = async (directory, inputOptions, oxlintRuntime)
|
|
|
93777
94351
|
};
|
|
93778
94352
|
const createInvocationInspect = (requestedOxlintConcurrency) => {
|
|
93779
94353
|
const concurrency = resolveScanConcurrency(requestedOxlintConcurrency ?? runSync(OxlintConcurrency));
|
|
93780
|
-
const
|
|
93781
|
-
|
|
93782
|
-
|
|
94354
|
+
const spawnSlots = createOxlintSpawnSlots(concurrency);
|
|
94355
|
+
const scanResultCacheInvocationState = createScanResultCacheInvocationState();
|
|
94356
|
+
return async (directory, inputOptions = {}) => {
|
|
94357
|
+
const abortController = new AbortController();
|
|
94358
|
+
const unregisterAbortController = activeScanAbortRegistry.register(abortController);
|
|
94359
|
+
try {
|
|
94360
|
+
return await inspectWithOxlintRuntime(directory, inputOptions, {
|
|
94361
|
+
concurrency,
|
|
94362
|
+
spawnSlots,
|
|
94363
|
+
abortSignal: abortController.signal,
|
|
94364
|
+
scanResultCacheInvocationState
|
|
94365
|
+
});
|
|
94366
|
+
} finally {
|
|
94367
|
+
unregisterAbortController();
|
|
94368
|
+
}
|
|
93783
94369
|
};
|
|
93784
|
-
return (directory, inputOptions = {}) => inspectWithOxlintRuntime(directory, inputOptions, oxlintRuntime);
|
|
93785
94370
|
};
|
|
93786
94371
|
const inspect = async (directory, inputOptions = {}) => createInvocationInspect(inputOptions.concurrency)(directory, inputOptions);
|
|
93787
94372
|
const countIncompleteLintFiles = (lintPartialFailures) => countDroppedLintFiles(lintPartialFailures) + countDeadlineSkippedFiles(lintPartialFailures);
|
|
@@ -93794,12 +94379,27 @@ const countIncompleteLintFiles = (lintPartialFailures) => countDroppedLintFiles(
|
|
|
93794
94379
|
*/
|
|
93795
94380
|
const runBaselineComparison = async (params) => {
|
|
93796
94381
|
const tempDirectory = mkdtempSync(path.join(tmpdir(), BASELINE_FILES_TEMP_DIR_PREFIX));
|
|
94382
|
+
const baselineIncludePaths = filterPathsOutsideDirectories({
|
|
94383
|
+
rootDirectory: params.directory,
|
|
94384
|
+
relativePaths: params.options.includePaths,
|
|
94385
|
+
excludedDirectories: params.options.excludedProjectDirectories
|
|
94386
|
+
});
|
|
94387
|
+
const baselineBaseFiles = params.baseFiles ? filterPathsOutsideDirectories({
|
|
94388
|
+
rootDirectory: params.directory,
|
|
94389
|
+
relativePaths: params.baseFiles,
|
|
94390
|
+
excludedDirectories: params.options.excludedProjectDirectories
|
|
94391
|
+
}) : void 0;
|
|
94392
|
+
const baselineHeadFiles = params.headFiles ? filterPathsOutsideDirectories({
|
|
94393
|
+
rootDirectory: params.directory,
|
|
94394
|
+
relativePaths: params.headFiles,
|
|
94395
|
+
excludedDirectories: params.options.excludedProjectDirectories
|
|
94396
|
+
}) : void 0;
|
|
93797
94397
|
const snapshot = await materializeBaselineFiles({
|
|
93798
94398
|
directory: params.directory,
|
|
93799
94399
|
ref: params.baselineRef,
|
|
93800
|
-
files:
|
|
93801
|
-
baseFiles:
|
|
93802
|
-
headFiles:
|
|
94400
|
+
files: baselineIncludePaths,
|
|
94401
|
+
baseFiles: baselineBaseFiles,
|
|
94402
|
+
headFiles: baselineHeadFiles,
|
|
93803
94403
|
tempDirectory
|
|
93804
94404
|
}).catch((error) => {
|
|
93805
94405
|
rmSync(tempDirectory, {
|
|
@@ -93821,7 +94421,7 @@ const runBaselineComparison = async (params) => {
|
|
|
93821
94421
|
const baseFiles = new Set(snapshot.baseFiles.map(toForwardSlashes));
|
|
93822
94422
|
const trackedHeadFiles = new Set(snapshot.headFiles.map(toForwardSlashes));
|
|
93823
94423
|
const expectedHeadFiles = new Set(trackedHeadFiles);
|
|
93824
|
-
for (const filePath of
|
|
94424
|
+
for (const filePath of baselineIncludePaths) {
|
|
93825
94425
|
const normalizedFilePath = toForwardSlashes(filePath);
|
|
93826
94426
|
if (!baseFiles.has(normalizedFilePath)) expectedHeadFiles.add(normalizedFilePath);
|
|
93827
94427
|
}
|
|
@@ -93858,8 +94458,9 @@ const runBaselineComparison = async (params) => {
|
|
|
93858
94458
|
resolveLocalGithubViewerPermission: false,
|
|
93859
94459
|
suppressScanSummary: true,
|
|
93860
94460
|
supplyChainManifestChanged: params.options.supplyChainManifestChanged,
|
|
93861
|
-
deadlineEpochMs: params.deadlineEpochMs ?? void 0
|
|
93862
|
-
|
|
94461
|
+
deadlineEpochMs: params.deadlineEpochMs ?? void 0,
|
|
94462
|
+
signal: params.oxlintRuntime.abortSignal
|
|
94463
|
+
}, {}).pipe(provide(baseLayers), provideService$2(PerFileLintCacheEnabled, false), provideService$2(SidecarLintCacheEnabled, false), provideService$2(Console, silentConsole))), { signal: params.oxlintRuntime.abortSignal });
|
|
93863
94464
|
if (baseOutput.didLintFail || countIncompleteLintFiles(baseOutput.lintPartialFailures) > 0) return null;
|
|
93864
94465
|
const hasUnscannedUntrackedSourceFiles = filterSourceFiles(snapshot.untrackedFiles.map(toForwardSlashes)).some((filePath) => !analyzedHeadFiles.has(filePath));
|
|
93865
94466
|
const delta = computeDiagnosticDelta({
|
|
@@ -93887,6 +94488,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93887
94488
|
const isDiffMode = options.includePaths.length > 0;
|
|
93888
94489
|
const resolvedNodeBinaryPath = await resolveOxlintNode(options.lint, options.scoreOnly || options.silent);
|
|
93889
94490
|
const lintBindingMissing = options.lint && !resolvedNodeBinaryPath;
|
|
94491
|
+
await yieldToEventLoop();
|
|
93890
94492
|
const cacheKey = buildScanResultCacheKey({
|
|
93891
94493
|
projectDirectory: directory,
|
|
93892
94494
|
version: VERSION,
|
|
@@ -93894,19 +94496,14 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93894
94496
|
options,
|
|
93895
94497
|
userConfig,
|
|
93896
94498
|
hasConfigOverride,
|
|
93897
|
-
configSourceDirectory
|
|
94499
|
+
configSourceDirectory,
|
|
94500
|
+
invocationState: oxlintRuntime.scanResultCacheInvocationState
|
|
93898
94501
|
});
|
|
93899
94502
|
const scanResultCache = cacheKey === null ? null : createScanResultCache(directory);
|
|
93900
|
-
const cachedPayload = cacheKey === null ? null : scanResultCache
|
|
94503
|
+
const cachedPayload = cacheKey === null || scanResultCache === null ? null : scanResultCache.lookup(cacheKey);
|
|
93901
94504
|
if (cachedPayload) {
|
|
93902
94505
|
recordSentryProjectContext(cachedPayload.project, rootSentrySpan, { concurrentScan: options.concurrentScan });
|
|
93903
94506
|
recordCount(METRIC.projectDetected, 1);
|
|
93904
|
-
await renderCachedProjectDetection({
|
|
93905
|
-
payload: cachedPayload,
|
|
93906
|
-
options,
|
|
93907
|
-
userConfig,
|
|
93908
|
-
isDiffMode
|
|
93909
|
-
});
|
|
93910
94507
|
const baselineDegraded = Boolean(options.baseline) && isDiffMode && cachedPayload.baselineDelta === void 0;
|
|
93911
94508
|
const result = await renderAndRecordScan({
|
|
93912
94509
|
payload: cachedPayload,
|
|
@@ -93940,6 +94537,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93940
94537
|
});
|
|
93941
94538
|
const program = runInspect({
|
|
93942
94539
|
directory,
|
|
94540
|
+
precomputedSourceFileCount: options.precomputedSourceFileCount,
|
|
93943
94541
|
includePaths: options.includePaths,
|
|
93944
94542
|
customRulesOnly: options.customRulesOnly,
|
|
93945
94543
|
respectInlineDisables: options.respectInlineDisables,
|
|
@@ -93956,26 +94554,19 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93956
94554
|
resolveLocalGithubViewerPermission: !options.noScore,
|
|
93957
94555
|
suppressScanSummary: options.suppressRendering,
|
|
93958
94556
|
supplyChainManifestChanged: options.supplyChainManifestChanged,
|
|
93959
|
-
|
|
93960
|
-
|
|
93961
|
-
|
|
94557
|
+
deadlineEpochMs: deadlineEpochMs ?? void 0,
|
|
94558
|
+
signal: oxlintRuntime.abortSignal,
|
|
94559
|
+
excludedProjectDirectories: options.excludedProjectDirectories,
|
|
94560
|
+
retainExcludedProjectDeadCodeDiagnostics: options.retainExcludedProjectDeadCodeDiagnostics
|
|
94561
|
+
}, { beforeLint: (projectInfo) => sync(() => {
|
|
93962
94562
|
recordSentryProjectContext(projectInfo, rootSentrySpan, { concurrentScan: options.concurrentScan });
|
|
93963
94563
|
recordCount(METRIC.projectDetected, 1);
|
|
93964
|
-
if (options.scoreOnly || options.suppressRendering) return;
|
|
93965
|
-
const lintSourceFileCount = lintIncludePaths?.length ?? projectInfo.sourceFileCount;
|
|
93966
|
-
yield* printProjectDetection({
|
|
93967
|
-
projectInfo,
|
|
93968
|
-
userConfig,
|
|
93969
|
-
isDiffMode,
|
|
93970
|
-
includePaths: options.includePaths,
|
|
93971
|
-
lintSourceFileCount
|
|
93972
|
-
});
|
|
93973
94564
|
}) });
|
|
93974
|
-
const output = await runPromise(restoreLegacyThrow(applyObservability(options.silent ? program.pipe(provide(layers), provideService$2(Console, silentConsole)) : program.pipe(provide(layers)), rootSentrySpan)));
|
|
94565
|
+
const output = await runPromise(restoreLegacyThrow(applyObservability(options.silent ? program.pipe(provide(layers), provideService$2(Console, silentConsole)) : program.pipe(provide(layers)), rootSentrySpan)), { signal: oxlintRuntime.abortSignal });
|
|
93975
94566
|
const didLintFail = lintBindingMissing || output.didLintFail;
|
|
93976
94567
|
const lintFailureReason = lintBindingMissing ? `oxlint native binding not found for Node ${process.version}; expected one matching ${OXLINT_NODE_REQUIREMENT}` : output.lintFailureReason;
|
|
93977
|
-
if (!options.scoreOnly && !options.uiLayers && !lintBindingMissing && output.didLintFail && lintFailureReason !== null) if (output.lintFailureReasonKind === "native-binding-missing")
|
|
93978
|
-
else
|
|
94568
|
+
if (!options.scoreOnly && !options.uiLayers && !lintBindingMissing && output.didLintFail && lintFailureReason !== null) if (output.lintFailureReasonKind === "native-binding-missing") runSync(log(highlighter.gray(` Upgrade to Node ${OXLINT_NODE_REQUIREMENT} or run: npx -p oxlint@latest react-doctor@latest`)));
|
|
94569
|
+
else runSync(error$1(highlighter.error(lintFailureReason)));
|
|
93979
94570
|
let inspectDiagnostics = output.diagnostics;
|
|
93980
94571
|
let baselineDelta;
|
|
93981
94572
|
if (options.baseline && isDiffMode && !didLintFail && countIncompleteLintFiles(output.lintPartialFailures) === 0) {
|
|
@@ -94025,6 +94616,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
94025
94616
|
lintFailureReasonKind: lintBindingMissing ? "native-binding-missing" : output.lintFailureReasonKind,
|
|
94026
94617
|
supplyChainOverlapTimedOut: output.supplyChainOverlapTimedOut,
|
|
94027
94618
|
securityScanFailed: output.securityScanFailed,
|
|
94619
|
+
securityScanFailureReason: output.securityScanFailureReason,
|
|
94028
94620
|
suppressedRuleCounts: output.suppressedRuleCounts
|
|
94029
94621
|
};
|
|
94030
94622
|
if (cacheKey !== null && scanResultCache !== null && shouldStoreScanPayload(payload) && !baselineDegraded) scanResultCache.store(cacheKey, payload);
|
|
@@ -94050,16 +94642,6 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
94050
94642
|
return result;
|
|
94051
94643
|
};
|
|
94052
94644
|
const runMaybeSilent = (effect, silent) => silent ? effect.pipe(provideService$2(Console, silentConsole)) : effect;
|
|
94053
|
-
const renderCachedProjectDetection = async (input) => {
|
|
94054
|
-
if (input.options.scoreOnly || input.options.suppressRendering) return;
|
|
94055
|
-
await runPromise(runMaybeSilent(printProjectDetection({
|
|
94056
|
-
projectInfo: input.payload.project,
|
|
94057
|
-
userConfig: input.userConfig,
|
|
94058
|
-
isDiffMode: input.isDiffMode,
|
|
94059
|
-
includePaths: input.options.includePaths,
|
|
94060
|
-
lintSourceFileCount: input.payload.scannedFileCount
|
|
94061
|
-
}), input.options.silent));
|
|
94062
|
-
};
|
|
94063
94645
|
const renderAndRecordScan = async (input) => {
|
|
94064
94646
|
const result = await runPromise(runMaybeSilent(finalizeAndRender({
|
|
94065
94647
|
options: input.options,
|
|
@@ -94075,6 +94657,7 @@ const renderAndRecordScan = async (input) => {
|
|
|
94075
94657
|
deadCodeFailureReason: input.payload.deadCodeFailureReason,
|
|
94076
94658
|
supplyChainOverlapTimedOut: input.payload.supplyChainOverlapTimedOut,
|
|
94077
94659
|
securityScanFailed: input.payload.securityScanFailed ?? false,
|
|
94660
|
+
securityScanFailureReason: input.payload.securityScanFailureReason ?? null,
|
|
94078
94661
|
directory: input.payload.directory,
|
|
94079
94662
|
scannedFileCount: input.payload.scannedFileCount,
|
|
94080
94663
|
scannedFilePaths: input.payload.scannedFilePaths,
|
|
@@ -94126,7 +94709,7 @@ const renderAndRecordScan = async (input) => {
|
|
|
94126
94709
|
return result;
|
|
94127
94710
|
};
|
|
94128
94711
|
const finalizeAndRender = (input) => gen(function* () {
|
|
94129
|
-
const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, supplyChainOverlapTimedOut, securityScanFailed, directory, scannedFileCount, scannedFilePaths, analyzedFiles, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount, lintSidecarReplayedFileCount, lintSidecarTotalFileCount, deadCodeCacheHit, deadCodeSummaryCacheHits, deadCodeSummaryCacheMisses, baselineDelta } = input;
|
|
94712
|
+
const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, supplyChainOverlapTimedOut, securityScanFailed, securityScanFailureReason, directory, scannedFileCount, scannedFilePaths, analyzedFiles, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount, lintSidecarReplayedFileCount, lintSidecarTotalFileCount, deadCodeCacheHit, deadCodeSummaryCacheHits, deadCodeSummaryCacheMisses, baselineDelta } = input;
|
|
94130
94713
|
const { skippedChecks, skippedCheckReasons } = buildSkippedChecks({
|
|
94131
94714
|
didLintFail,
|
|
94132
94715
|
lintFailureReason,
|
|
@@ -94134,10 +94717,15 @@ const finalizeAndRender = (input) => gen(function* () {
|
|
|
94134
94717
|
didDeadCodeFail,
|
|
94135
94718
|
deadCodeFailureReason,
|
|
94136
94719
|
supplyChainOverlapTimedOut,
|
|
94137
|
-
securityScanFailed
|
|
94720
|
+
securityScanFailed,
|
|
94721
|
+
securityScanFailureReason
|
|
94138
94722
|
});
|
|
94139
94723
|
const hasSkippedChecks = skippedChecks.length > 0;
|
|
94140
|
-
const noScoreMessage = buildNoScoreMessage(
|
|
94724
|
+
const noScoreMessage = buildNoScoreMessage({
|
|
94725
|
+
isScoreDisabled: options.noScore,
|
|
94726
|
+
isAnalysisIncomplete: hasIncompleteScoreAnalysis(skippedChecks),
|
|
94727
|
+
disabledMessage: options.scoreDisabledMessage
|
|
94728
|
+
});
|
|
94141
94729
|
const buildResult = () => ({
|
|
94142
94730
|
diagnostics: [...diagnostics],
|
|
94143
94731
|
score,
|
|
@@ -94183,9 +94771,11 @@ const finalizeAndRender = (input) => gen(function* () {
|
|
|
94183
94771
|
if (hasSkippedChecks) {
|
|
94184
94772
|
const skippedLabel = skippedChecks.join(" and ");
|
|
94185
94773
|
yield* warn$1(highlighter.warn(`No issues detected, but ${skippedLabel} checks failed — results are incomplete.`));
|
|
94186
|
-
} else
|
|
94187
|
-
|
|
94188
|
-
|
|
94774
|
+
} else yield* log(highlighter.success(buildEmptyReportMessage({
|
|
94775
|
+
categoryFilters: options.categoryFilters,
|
|
94776
|
+
demotedDiagnosticCount,
|
|
94777
|
+
outputSurface: options.outputSurface
|
|
94778
|
+
})));
|
|
94189
94779
|
yield* log("");
|
|
94190
94780
|
yield* pause;
|
|
94191
94781
|
if (hasSkippedChecks) {
|
|
@@ -94265,6 +94855,32 @@ const materializeStagedFiles = async (input) => {
|
|
|
94265
94855
|
};
|
|
94266
94856
|
};
|
|
94267
94857
|
//#endregion
|
|
94858
|
+
//#region src/cli/utils/deduplicate-project-scans.ts
|
|
94859
|
+
const deduplicateProjectScans = (projectScans) => {
|
|
94860
|
+
const uniqueProjectScans = [];
|
|
94861
|
+
const seenDirectories = /* @__PURE__ */ new Set();
|
|
94862
|
+
for (const projectScan of projectScans) {
|
|
94863
|
+
if (seenDirectories.has(projectScan.directory)) continue;
|
|
94864
|
+
seenDirectories.add(projectScan.directory);
|
|
94865
|
+
uniqueProjectScans.push(projectScan);
|
|
94866
|
+
}
|
|
94867
|
+
return uniqueProjectScans;
|
|
94868
|
+
};
|
|
94869
|
+
//#endregion
|
|
94870
|
+
//#region src/cli/utils/collect-project-source-file-counts.ts
|
|
94871
|
+
const collectProjectSourceFileCounts = async (rootDirectory, projectDirectories) => {
|
|
94872
|
+
const abortController = new AbortController();
|
|
94873
|
+
const unregisterAbortController = activeScanAbortRegistry.register(abortController);
|
|
94874
|
+
try {
|
|
94875
|
+
return await collectSourceFileCountsByDirectory(rootDirectory, projectDirectories, abortController.signal);
|
|
94876
|
+
} finally {
|
|
94877
|
+
unregisterAbortController();
|
|
94878
|
+
}
|
|
94879
|
+
};
|
|
94880
|
+
//#endregion
|
|
94881
|
+
//#region src/cli/utils/format-skipped-projects-message.ts
|
|
94882
|
+
const formatSkippedProjectsMessage = (projectCount) => `${projectCount} ${projectCount === 1 ? "project was" : "projects were"} skipped because the max scan duration was reached.`;
|
|
94883
|
+
//#endregion
|
|
94268
94884
|
//#region src/cli/utils/is-share-opted-out.ts
|
|
94269
94885
|
const isShareOptedOut = (scans, flagNoScore) => Boolean(flagNoScore) || scans.some((scan) => Boolean(scan.config?.noScore) || scan.config?.share === false);
|
|
94270
94886
|
//#endregion
|
|
@@ -94276,7 +94892,7 @@ const buildHandoffPayload = (input) => {
|
|
|
94276
94892
|
try {
|
|
94277
94893
|
outputDirectory = writeDiagnosticsDirectory([...input.diagnostics], input.outputDirectory);
|
|
94278
94894
|
} catch {}
|
|
94279
|
-
const lines = [`
|
|
94895
|
+
const lines = [`Review and fix the top ${topGroups.length} React Doctor ${topGroups.length === 1 ? "finding" : "findings"} in ${input.projectName}. Leave the rest for a follow-up.`, ""];
|
|
94280
94896
|
topGroups.forEach(([ruleKey, ruleDiagnostics], index) => {
|
|
94281
94897
|
const representative = ruleDiagnostics[0];
|
|
94282
94898
|
const severityLabel = representative.severity === "error" ? "ERROR" : "WARN";
|
|
@@ -94304,7 +94920,7 @@ const buildHandoffPayload = (input) => {
|
|
|
94304
94920
|
const ruleSummaries = deferredMigrationBuckets.map((bucket) => `${bucket.title} (${bucket.fileCount} files)`).join(", ");
|
|
94305
94921
|
lines.push(`Some of the rest are migration-scale (span dozens of files): ${ruleSummaries}. For each, fix a representative sample, confirm the recipe holds, and get the code owner's sign-off before changing the rest in one pass.`, "");
|
|
94306
94922
|
}
|
|
94307
|
-
lines.push("
|
|
94923
|
+
lines.push("Stop after this pass. Summarize the remaining findings for a follow-up.");
|
|
94308
94924
|
return lines.join("\n");
|
|
94309
94925
|
};
|
|
94310
94926
|
//#endregion
|
|
@@ -94514,20 +95130,20 @@ const resolveOpenCommand = (url) => {
|
|
|
94514
95130
|
args: [url]
|
|
94515
95131
|
};
|
|
94516
95132
|
};
|
|
94517
|
-
const openUrl = (url) => {
|
|
95133
|
+
const openUrl = (url) => new Promise((resolve) => {
|
|
94518
95134
|
try {
|
|
94519
95135
|
const { command, args } = resolveOpenCommand(url);
|
|
94520
95136
|
const child = spawn(command, args, {
|
|
94521
95137
|
detached: true,
|
|
94522
95138
|
stdio: "ignore"
|
|
94523
95139
|
});
|
|
94524
|
-
child.
|
|
95140
|
+
child.once("spawn", () => resolve(true));
|
|
95141
|
+
child.once("error", () => resolve(false));
|
|
94525
95142
|
child.unref();
|
|
94526
|
-
return true;
|
|
94527
95143
|
} catch {
|
|
94528
|
-
|
|
95144
|
+
resolve(false);
|
|
94529
95145
|
}
|
|
94530
|
-
};
|
|
95146
|
+
});
|
|
94531
95147
|
//#endregion
|
|
94532
95148
|
//#region src/cli/utils/ask-add-to-github-actions.ts
|
|
94533
95149
|
const CI_YES_CHOICE = "ci-yes";
|
|
@@ -94568,7 +95184,7 @@ const askAddToGitHubActions = async (prompt = prompts) => {
|
|
|
94568
95184
|
if (ciChoice === void 0) return "cancel";
|
|
94569
95185
|
if (ciChoice === CI_YES_CHOICE) return "yes";
|
|
94570
95186
|
if (ciChoice === CI_NO_CHOICE) return "no";
|
|
94571
|
-
const opened = openUrl(GITHUB_ACTIONS_SETUP_URL);
|
|
95187
|
+
const opened = await openUrl(GITHUB_ACTIONS_SETUP_URL);
|
|
94572
95188
|
cliLogger.log(opened ? `Opened ${highlighter.info(GITHUB_ACTIONS_SETUP_URL)} in your browser.` : `Visit ${highlighter.info(GITHUB_ACTIONS_SETUP_URL)} to learn more.`);
|
|
94573
95189
|
cliLogger.break();
|
|
94574
95190
|
}
|
|
@@ -95665,7 +96281,7 @@ const runInstallReactDoctor = async (options = {}) => {
|
|
|
95665
96281
|
const shouldInstallGitHook = gitHookPath != null && (Boolean(options.yes) || !didSkipOptionalSetup && selectedSetupActions.includes(SETUP_OPTION_GIT_HOOK));
|
|
95666
96282
|
const shouldInstallAgentHooks = Boolean(options.agentHooks) || !didSkipOptionalSetup && selectedSetupActions.includes(SETUP_OPTION_AGENT_HOOKS);
|
|
95667
96283
|
if (!options.dryRun) {
|
|
95668
|
-
if (shouldInstallGitHook && gitHookTarget
|
|
96284
|
+
if (shouldInstallGitHook && gitHookTarget != null) installReactDoctorGitHookStep(gitHookTarget);
|
|
95669
96285
|
if (shouldInstallAgentHooks) installReactDoctorAgentHooksStep(projectRoot, selectedAgents);
|
|
95670
96286
|
}
|
|
95671
96287
|
if (options.dryRun) {
|
|
@@ -96283,6 +96899,27 @@ const resolveCliInspectOptions = (flags, userConfig) => {
|
|
|
96283
96899
|
};
|
|
96284
96900
|
};
|
|
96285
96901
|
//#endregion
|
|
96902
|
+
//#region src/cli/utils/resolve-workspace-dead-code-owner.ts
|
|
96903
|
+
const resolveWorkspaceDeadCodeOwner = (input) => {
|
|
96904
|
+
if (!input.isRootDeadCodeEnabled) return null;
|
|
96905
|
+
const resolvedRootDirectory = Path.resolve(input.rootDirectory);
|
|
96906
|
+
return input.projectDirectories.find((projectDirectory) => Path.resolve(projectDirectory) === resolvedRootDirectory) ?? null;
|
|
96907
|
+
};
|
|
96908
|
+
//#endregion
|
|
96909
|
+
//#region src/cli/utils/retry-missing-project-scores.ts
|
|
96910
|
+
const retryMissingProjectScores = async (projectScans) => mapWithConcurrency(projectScans, 64, async (projectScan) => {
|
|
96911
|
+
if (!(projectScan.result.score === null && !projectScan.isScoreDisabled && !hasIncompleteScoreAnalysis(projectScan.result.skippedChecks))) return projectScan;
|
|
96912
|
+
const score = await calculateScore(filterScansForSurface([projectScan], "score"));
|
|
96913
|
+
recordCount(METRIC.scanScoreRetry, 1, { succeeded: score !== null });
|
|
96914
|
+
return score === null ? projectScan : {
|
|
96915
|
+
...projectScan,
|
|
96916
|
+
result: {
|
|
96917
|
+
...projectScan.result,
|
|
96918
|
+
score
|
|
96919
|
+
}
|
|
96920
|
+
};
|
|
96921
|
+
});
|
|
96922
|
+
//#endregion
|
|
96286
96923
|
//#region src/cli/utils/resolve-project-relative-directory.ts
|
|
96287
96924
|
/**
|
|
96288
96925
|
* The scanned project's directory relative to the diff root, forward-slashed —
|
|
@@ -96343,6 +96980,16 @@ const resolveProjectChangedLineRanges = (rootDirectory, projectDirectory, ranges
|
|
|
96343
96980
|
});
|
|
96344
96981
|
};
|
|
96345
96982
|
//#endregion
|
|
96983
|
+
//#region src/cli/utils/resolve-project-scan.ts
|
|
96984
|
+
const resolveProjectScan = async (rootScanTarget, projectDirectory) => {
|
|
96985
|
+
const projectScanTarget = projectDirectory === rootScanTarget.resolvedDirectory ? rootScanTarget : await resolveScanTarget(projectDirectory, { allowAmbiguous: true });
|
|
96986
|
+
return {
|
|
96987
|
+
directory: projectScanTarget.resolvedDirectory,
|
|
96988
|
+
config: projectDirectory === rootScanTarget.resolvedDirectory ? rootScanTarget.userConfig : mergeReactDoctorConfigs(rootScanTarget.userConfig, projectScanTarget.userConfig ?? void 0),
|
|
96989
|
+
configSourceDirectory: projectScanTarget.userConfig?.plugins === void 0 ? rootScanTarget.configSourceDirectory : projectScanTarget.configSourceDirectory
|
|
96990
|
+
};
|
|
96991
|
+
};
|
|
96992
|
+
//#endregion
|
|
96346
96993
|
//#region src/cli/utils/build-diagnostic-issue-url.ts
|
|
96347
96994
|
const formatRuleIdentifier = (diagnostic) => `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
96348
96995
|
const buildDiagnosticIssueBody = (input) => {
|
|
@@ -96485,7 +97132,7 @@ const resolveExplainTargetDirectory = async (filePath, context) => {
|
|
|
96485
97132
|
if (context.projectFlag) {
|
|
96486
97133
|
const matchedDirectories = await selectProjects(context.resolvedDirectory, context.projectFlag, true);
|
|
96487
97134
|
if (matchedDirectories.length === 0) return context.resolvedDirectory;
|
|
96488
|
-
if (matchedDirectories.length > 1) throw new Error(
|
|
97135
|
+
if (matchedDirectories.length > 1) throw new Error(`why takes a single project; --project resolved to ${matchedDirectories.length} projects.`);
|
|
96489
97136
|
return matchedDirectories[0];
|
|
96490
97137
|
}
|
|
96491
97138
|
return findOwningProjectDirectory(context.resolvedDirectory, filePath);
|
|
@@ -96704,6 +97351,15 @@ const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
|
|
|
96704
97351
|
}
|
|
96705
97352
|
}));
|
|
96706
97353
|
};
|
|
97354
|
+
const reportSkippedProjects = (input) => {
|
|
97355
|
+
input.skippedProjects.sort((left, right) => left.directory.localeCompare(right.directory));
|
|
97356
|
+
if (input.skippedProjects.length === 0) return;
|
|
97357
|
+
recordCount(METRIC.scanProjectSkipped, input.skippedProjects.length, { reason: "max-duration" });
|
|
97358
|
+
if (!input.isQuiet) {
|
|
97359
|
+
cliLogger.warn(formatSkippedProjectsMessage(input.skippedProjects.length));
|
|
97360
|
+
cliLogger.break();
|
|
97361
|
+
}
|
|
97362
|
+
};
|
|
96707
97363
|
/**
|
|
96708
97364
|
* Post-scan finalization shared by the staged-arm and project-loop
|
|
96709
97365
|
* paths of `inspectAction`: emit the JSON report (when in JSON mode)
|
|
@@ -96718,7 +97374,7 @@ const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
|
|
|
96718
97374
|
*/
|
|
96719
97375
|
const finalizeScans = (input) => {
|
|
96720
97376
|
const baselineDeltas = input.completedScans.flatMap((scan) => scan.result.baselineDelta ? [scan.result.baselineDelta] : []);
|
|
96721
|
-
const baselineComputed = input.completedScans.length > 0 && input.completedScans.every((scan) => scan.result.baselineDelta !== void 0);
|
|
97377
|
+
const baselineComputed = input.skippedProjects.length === 0 && input.completedScans.length > 0 && input.completedScans.every((scan) => scan.result.baselineDelta !== void 0);
|
|
96722
97378
|
const baselineDegraded = input.baselineIntended && !baselineComputed;
|
|
96723
97379
|
const mode = baselineDegraded ? "diff" : input.mode;
|
|
96724
97380
|
const isReactDetected = input.completedScans.some((scan) => hasReactRuntime(scan.result.project));
|
|
@@ -96739,6 +97395,7 @@ const finalizeScans = (input) => {
|
|
|
96739
97395
|
mode,
|
|
96740
97396
|
diff: input.diff,
|
|
96741
97397
|
scans: jsonCompletedScans,
|
|
97398
|
+
skippedProjects: input.skippedProjects,
|
|
96742
97399
|
totalElapsedMilliseconds: performance$1.now() - input.startTime,
|
|
96743
97400
|
baseline,
|
|
96744
97401
|
baselineDegraded
|
|
@@ -96777,16 +97434,16 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
96777
97434
|
const requestedDirectory = Path.resolve(directory);
|
|
96778
97435
|
const startTime = performance$1.now();
|
|
96779
97436
|
let scanStartupSpinner = null;
|
|
96780
|
-
if (isJsonMode) {
|
|
96781
|
-
enableJsonMode({
|
|
96782
|
-
compact: Boolean(flags.jsonCompact),
|
|
96783
|
-
directory: requestedDirectory,
|
|
96784
|
-
outputFile: flags.jsonOut
|
|
96785
|
-
});
|
|
96786
|
-
if (flags.jsonOut) recordCount(METRIC.jsonOutUsed, 1);
|
|
96787
|
-
}
|
|
96788
|
-
recordCount(METRIC.cliInvoked, 1, { command: invocationCommand });
|
|
96789
97437
|
try {
|
|
97438
|
+
if (isJsonMode) {
|
|
97439
|
+
enableJsonMode({
|
|
97440
|
+
compact: Boolean(flags.jsonCompact),
|
|
97441
|
+
directory: requestedDirectory,
|
|
97442
|
+
outputFile: flags.jsonOut
|
|
97443
|
+
});
|
|
97444
|
+
if (flags.jsonOut) recordCount(METRIC.jsonOutUsed, 1);
|
|
97445
|
+
}
|
|
97446
|
+
recordCount(METRIC.cliInvoked, 1, { command: invocationCommand });
|
|
96790
97447
|
validateModeFlags(flags);
|
|
96791
97448
|
if (flags.staged) setJsonReportMode("staged");
|
|
96792
97449
|
await maybeMigrateLegacyConfig(requestedDirectory, {
|
|
@@ -96859,16 +97516,16 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
96859
97516
|
return;
|
|
96860
97517
|
}
|
|
96861
97518
|
const buildProjectScanContext = async (projectDirectory) => {
|
|
96862
|
-
const
|
|
96863
|
-
const scanDirectory =
|
|
97519
|
+
const projectScan = await resolveProjectScan(scanTarget, projectDirectory);
|
|
97520
|
+
const scanDirectory = projectScan.directory;
|
|
96864
97521
|
const treeRelativeDirectory = resolveProjectRelativeDirectory(resolvedDirectory, scanDirectory);
|
|
96865
97522
|
if (treeRelativeDirectory === null) return null;
|
|
96866
97523
|
return {
|
|
96867
97524
|
projectDirectory,
|
|
96868
97525
|
scanDirectory,
|
|
96869
97526
|
treeRelativeDirectory,
|
|
96870
|
-
projectConfig:
|
|
96871
|
-
projectConfigSourceDirectory:
|
|
97527
|
+
projectConfig: projectScan.config,
|
|
97528
|
+
projectConfigSourceDirectory: projectScan.configSourceDirectory
|
|
96872
97529
|
};
|
|
96873
97530
|
};
|
|
96874
97531
|
const projectScanContexts = [];
|
|
@@ -96955,6 +97612,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
96955
97612
|
includePaths: resolveProjectSourceFilePaths(resolvedDirectory, projectScan.scanDirectory, projectScan.stagedFiles.filter((stagedFile) => materializedStagedFiles.has(stagedFile)))
|
|
96956
97613
|
})).filter((projectRun) => projectRun.includePaths.length > 0);
|
|
96957
97614
|
const isMultiProject = stagedProjectRuns.length > 1;
|
|
97615
|
+
const skippedProjects = [];
|
|
96958
97616
|
if (stagedProjectRuns.length === 0) {
|
|
96959
97617
|
snapshot.cleanup();
|
|
96960
97618
|
reportNothingToScan({
|
|
@@ -96970,6 +97628,13 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
96970
97628
|
}
|
|
96971
97629
|
const scanStagedProject = async (projectRun) => {
|
|
96972
97630
|
const { projectScan, includePaths } = projectRun;
|
|
97631
|
+
if (scanDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(scanDeadlineEpochMs) === 0) {
|
|
97632
|
+
skippedProjects.push({
|
|
97633
|
+
directory: projectScan.scanDirectory,
|
|
97634
|
+
reason: "max-duration"
|
|
97635
|
+
});
|
|
97636
|
+
return null;
|
|
97637
|
+
}
|
|
96973
97638
|
const projectTempDirectory = Path.join(snapshot.tempDirectory, projectScan.treeRelativeDirectory);
|
|
96974
97639
|
const scanResult = await inspectProject(projectTempDirectory, {
|
|
96975
97640
|
...scanOptions,
|
|
@@ -97010,7 +97675,11 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97010
97675
|
});
|
|
97011
97676
|
const completedScans = stagedBatch.completedScans;
|
|
97012
97677
|
try {
|
|
97013
|
-
|
|
97678
|
+
reportSkippedProjects({
|
|
97679
|
+
skippedProjects,
|
|
97680
|
+
isQuiet
|
|
97681
|
+
});
|
|
97682
|
+
if (!isQuiet && isMultiProject && completedScans.length > 0) {
|
|
97014
97683
|
const showShareLink = !isShareOptedOut(completedScans, scanOptions.noScore) && !scanOptions.isCi;
|
|
97015
97684
|
await runPromise(printMultiProjectSummary({
|
|
97016
97685
|
completedScans,
|
|
@@ -97028,6 +97697,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97028
97697
|
}
|
|
97029
97698
|
finalizeScans({
|
|
97030
97699
|
completedScans,
|
|
97700
|
+
skippedProjects,
|
|
97031
97701
|
mode: "staged",
|
|
97032
97702
|
diff: null,
|
|
97033
97703
|
baselineIntended: false,
|
|
@@ -97103,12 +97773,28 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97103
97773
|
}
|
|
97104
97774
|
cliLogger.break();
|
|
97105
97775
|
}
|
|
97106
|
-
const
|
|
97107
|
-
const
|
|
97108
|
-
|
|
97109
|
-
|
|
97110
|
-
|
|
97111
|
-
|
|
97776
|
+
const projectScans = deduplicateProjectScans(await Promise.all(projectDirectories.map((projectDirectory) => resolveProjectScan(scanTarget, projectDirectory))));
|
|
97777
|
+
const isMultiProject = projectScans.length > 1;
|
|
97778
|
+
const rootProjectScan = projectScans.find((projectScan) => Path.resolve(projectScan.directory) === Path.resolve(resolvedDirectory));
|
|
97779
|
+
const workspaceDeadCodeOwner = resolveWorkspaceDeadCodeOwner({
|
|
97780
|
+
rootDirectory: resolvedDirectory,
|
|
97781
|
+
projectDirectories: projectScans.map((projectScan) => projectScan.directory),
|
|
97782
|
+
isRootDeadCodeEnabled: scanOptions.deadCode ?? rootProjectScan?.config?.deadCode ?? true
|
|
97783
|
+
});
|
|
97784
|
+
if (workspaceDeadCodeOwner !== null) recordCount(METRIC.scanWorkspaceDeadCodeShared, 1, { projectCount: projectScans.length });
|
|
97785
|
+
const precomputedSourceFileCounts = isMultiProject && !isDiffMode ? await collectProjectSourceFileCounts(resolvedDirectory, projectScans.map((projectScan) => projectScan.directory)) : null;
|
|
97786
|
+
const skippedProjects = [];
|
|
97787
|
+
const scanProject = async (projectScan) => {
|
|
97788
|
+
if (scanDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(scanDeadlineEpochMs) === 0) {
|
|
97789
|
+
skippedProjects.push({
|
|
97790
|
+
directory: projectScan.directory,
|
|
97791
|
+
reason: "max-duration"
|
|
97792
|
+
});
|
|
97793
|
+
return null;
|
|
97794
|
+
}
|
|
97795
|
+
const scanDirectory = projectScan.directory;
|
|
97796
|
+
const projectConfig = projectScan.config;
|
|
97797
|
+
const ownsWorkspaceDeadCode = scanDirectory === workspaceDeadCodeOwner;
|
|
97112
97798
|
const supplyChainEnabled = flags.supplyChain ?? projectConfig?.supplyChain?.enabled !== false;
|
|
97113
97799
|
let includePaths;
|
|
97114
97800
|
let supplyChainManifestChanged = false;
|
|
@@ -97132,12 +97818,16 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97132
97818
|
if (!isQuiet && !isMultiProject) cliLogger.dim(" ");
|
|
97133
97819
|
const scanResult = await inspectProject(scanDirectory, {
|
|
97134
97820
|
...scanOptions,
|
|
97821
|
+
deadCode: workspaceDeadCodeOwner === null ? scanOptions.deadCode : ownsWorkspaceDeadCode,
|
|
97822
|
+
precomputedSourceFileCount: precomputedSourceFileCounts?.get(scanDirectory),
|
|
97135
97823
|
deadlineEpochMs: scanDeadlineEpochMs,
|
|
97136
97824
|
includePaths,
|
|
97137
97825
|
configOverride: projectConfig,
|
|
97138
|
-
configSourceDirectory:
|
|
97826
|
+
configSourceDirectory: projectScan.configSourceDirectory ?? void 0,
|
|
97139
97827
|
suppressRendering: isMultiProject,
|
|
97140
97828
|
concurrentScan: isMultiProject,
|
|
97829
|
+
excludedProjectDirectories: projectScans.filter((candidateProjectScan) => isPathInsideDirectory(candidateProjectScan.directory, scanDirectory)).map((candidateProjectScan) => candidateProjectScan.directory),
|
|
97830
|
+
retainExcludedProjectDeadCodeDiagnostics: ownsWorkspaceDeadCode,
|
|
97141
97831
|
baseline: baselineRef !== null && projectBaselineBaseFiles !== null && projectBaselineHeadFiles !== null ? {
|
|
97142
97832
|
ref: baselineRef,
|
|
97143
97833
|
baseFiles: projectBaselineBaseFiles,
|
|
@@ -97154,12 +97844,19 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97154
97844
|
};
|
|
97155
97845
|
};
|
|
97156
97846
|
const projectBatch = await runProjectScanBatch({
|
|
97157
|
-
projects:
|
|
97847
|
+
projects: projectScans,
|
|
97158
97848
|
isQuiet,
|
|
97159
97849
|
isSilent: scanOptions.silent === true,
|
|
97160
97850
|
scanProject
|
|
97161
97851
|
});
|
|
97162
|
-
const completedScans = projectBatch.completedScans
|
|
97852
|
+
const completedScans = await retryMissingProjectScores(projectBatch.completedScans.map((completedScan) => ({
|
|
97853
|
+
...completedScan,
|
|
97854
|
+
isScoreDisabled: scanOptions.noScore ?? completedScan.config?.noScore ?? false
|
|
97855
|
+
})));
|
|
97856
|
+
reportSkippedProjects({
|
|
97857
|
+
skippedProjects,
|
|
97858
|
+
isQuiet
|
|
97859
|
+
});
|
|
97163
97860
|
if (!isQuiet && isMultiProject && completedScans.length > 0) {
|
|
97164
97861
|
const showShareLink = !isShareOptedOut(completedScans, scanOptions.noScore) && !scanOptions.isCi;
|
|
97165
97862
|
await runPromise(printMultiProjectSummary({
|
|
@@ -97177,6 +97874,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97177
97874
|
if (flags.outputDir && !didScansWriteDump) await runPromise(printDiagnosticsDump(selectedSurfaceDiagnostics, flags.outputDir, false, isQuiet ? "stderr" : "stdout"));
|
|
97178
97875
|
finalizeScans({
|
|
97179
97876
|
completedScans,
|
|
97877
|
+
skippedProjects,
|
|
97180
97878
|
mode: baselineRef ? "baseline" : isDiffMode ? "diff" : "full",
|
|
97181
97879
|
diff: isDiffMode ? diffInfo : null,
|
|
97182
97880
|
baselineIntended: scope === "changed" && diffInfo !== null && !diffInfo.isCurrentChanges,
|
|
@@ -97261,6 +97959,56 @@ const installAction = async (options, command) => {
|
|
|
97261
97959
|
}
|
|
97262
97960
|
};
|
|
97263
97961
|
//#endregion
|
|
97962
|
+
//#region src/cli/utils/should-use-tui.ts
|
|
97963
|
+
const shouldUseTui = (input) => {
|
|
97964
|
+
if (input.isNonInteractiveEnvironment || !input.stdinIsTty || !input.stdoutIsTty || !input.supportsRawMode || input.nodeMajorVersion < 22 || input.terminalName === "dumb") return false;
|
|
97965
|
+
const flags = input.flags;
|
|
97966
|
+
let requiresStableConfiguredScope = false;
|
|
97967
|
+
if (input.userConfig?.scope !== void 0) requiresStableConfiguredScope = input.userConfig.scope !== "full";
|
|
97968
|
+
else if (input.userConfig?.diff !== void 0) requiresStableConfiguredScope = input.userConfig.diff !== false && input.userConfig.diff !== "false";
|
|
97969
|
+
else if (input.userConfig?.base !== void 0) requiresStableConfiguredScope = true;
|
|
97970
|
+
return !(flags.verbose === true || flags.outputDir !== void 0 || flags.score === true || flags.json === true || flags.jsonCompact === true || flags.jsonOut !== void 0 || flags.staged === true || flags.scope !== void 0 || flags.base !== void 0 || flags.includeUntracked === true || flags.diff !== void 0 || flags.changedFilesFrom !== void 0 || flags.debug === true || flags.failOn !== void 0 || requiresStableConfiguredScope);
|
|
97971
|
+
};
|
|
97972
|
+
//#endregion
|
|
97973
|
+
//#region src/cli/commands/scan.ts
|
|
97974
|
+
const runScanCommand = async (input) => {
|
|
97975
|
+
if (input.flags.cache === false) process.env.REACT_DOCTOR_NO_CACHE = "1";
|
|
97976
|
+
const nodeMajorVersion = Number(process.versions.node.split(".")[0]);
|
|
97977
|
+
const tuiEnvironment = {
|
|
97978
|
+
flags: input.flags,
|
|
97979
|
+
isNonInteractiveEnvironment: isNonInteractiveEnvironment(),
|
|
97980
|
+
nodeMajorVersion,
|
|
97981
|
+
stdinIsTty: process.stdin.isTTY === true,
|
|
97982
|
+
stdoutIsTty: process.stdout.isTTY === true,
|
|
97983
|
+
supportsRawMode: typeof process.stdin.setRawMode === "function",
|
|
97984
|
+
terminalName: process.env.TERM
|
|
97985
|
+
};
|
|
97986
|
+
if (!shouldUseTui(tuiEnvironment)) {
|
|
97987
|
+
await inspectAction(input.directory, input.flags, input.invocationCommand);
|
|
97988
|
+
return;
|
|
97989
|
+
}
|
|
97990
|
+
await runProjectMigrations(path.resolve(input.directory));
|
|
97991
|
+
const scanTarget = await resolveScanTarget(input.directory, { allowAmbiguous: true });
|
|
97992
|
+
if (!shouldUseTui({
|
|
97993
|
+
...tuiEnvironment,
|
|
97994
|
+
userConfig: scanTarget.userConfig
|
|
97995
|
+
})) {
|
|
97996
|
+
await inspectAction(input.directory, input.flags, input.invocationCommand);
|
|
97997
|
+
return;
|
|
97998
|
+
}
|
|
97999
|
+
recordCount(METRIC.cliInvoked, 1, { command: input.invocationCommand });
|
|
98000
|
+
const { runScanApp } = await import("./run-scan-app-C8iWVgvR.js");
|
|
98001
|
+
const { shouldFail } = await runScanApp({
|
|
98002
|
+
directory: input.directory,
|
|
98003
|
+
scanTarget,
|
|
98004
|
+
options: resolveCliInspectOptions(input.flags, null),
|
|
98005
|
+
projectFlag: input.flags.project,
|
|
98006
|
+
skipPrompts: input.flags.yes ?? false,
|
|
98007
|
+
blocking: input.flags.blocking
|
|
98008
|
+
});
|
|
98009
|
+
if (shouldFail) process.exitCode = 1;
|
|
98010
|
+
};
|
|
98011
|
+
//#endregion
|
|
97264
98012
|
//#region src/cli/utils/rule-catalog.ts
|
|
97265
98013
|
const buildRuleCatalog = () => REACT_DOCTOR_RULES.map((entry) => ({
|
|
97266
98014
|
key: entry.key,
|
|
@@ -97901,13 +98649,39 @@ const ensureWindowsUtf8Console = () => {
|
|
|
97901
98649
|
} catch {}
|
|
97902
98650
|
};
|
|
97903
98651
|
//#endregion
|
|
98652
|
+
//#region src/cli/utils/active-tui-renderer.ts
|
|
98653
|
+
let activeTuiRenderer = null;
|
|
98654
|
+
const registerActiveTuiRenderer = (renderer) => {
|
|
98655
|
+
activeTuiRenderer = renderer;
|
|
98656
|
+
return () => {
|
|
98657
|
+
if (activeTuiRenderer === renderer) activeTuiRenderer = null;
|
|
98658
|
+
};
|
|
98659
|
+
};
|
|
98660
|
+
const clearActiveTuiRenderer = () => {
|
|
98661
|
+
const renderer = activeTuiRenderer;
|
|
98662
|
+
activeTuiRenderer = null;
|
|
98663
|
+
renderer?.clear();
|
|
98664
|
+
};
|
|
98665
|
+
//#endregion
|
|
97904
98666
|
//#region src/cli/utils/exit-gracefully.ts
|
|
98667
|
+
let didStartExiting = false;
|
|
97905
98668
|
const exitGracefully = () => {
|
|
98669
|
+
if (didStartExiting) return process.exit(130);
|
|
98670
|
+
didStartExiting = true;
|
|
98671
|
+
activeScanAbortRegistry.abortAll();
|
|
98672
|
+
clearActiveTuiRenderer();
|
|
97906
98673
|
try {
|
|
97907
98674
|
if (isJsonModeActive()) writeJsonErrorReport(/* @__PURE__ */ new Error("Scan cancelled by user (SIGINT/SIGTERM)"));
|
|
97908
|
-
else console.log(
|
|
98675
|
+
else console.log([
|
|
98676
|
+
"",
|
|
98677
|
+
"Cancelled.",
|
|
98678
|
+
"",
|
|
98679
|
+
buildSectionDivider(),
|
|
98680
|
+
"",
|
|
98681
|
+
...buildFooterLinkLines({ shareUrl: null })
|
|
98682
|
+
].join("\n"));
|
|
97909
98683
|
} catch {}
|
|
97910
|
-
process.exit(130);
|
|
98684
|
+
flushSentry().finally(() => process.exit(130));
|
|
97911
98685
|
};
|
|
97912
98686
|
//#endregion
|
|
97913
98687
|
//#region src/cli/utils/guard-stdin.ts
|
|
@@ -98025,6 +98799,7 @@ const ROOT_FLAG_SPEC = {
|
|
|
98025
98799
|
"--json-compact",
|
|
98026
98800
|
"--lint",
|
|
98027
98801
|
"--no-color",
|
|
98802
|
+
"--no-cache",
|
|
98028
98803
|
"--no-dead-code",
|
|
98029
98804
|
"--no-lint",
|
|
98030
98805
|
"--no-parallel",
|
|
@@ -98148,13 +98923,18 @@ const COMMAND_FLAG_SPECS = new Map([
|
|
|
98148
98923
|
longOptionsWithoutValues: new Set([
|
|
98149
98924
|
"--color",
|
|
98150
98925
|
"--help",
|
|
98926
|
+
"--no-cache",
|
|
98151
98927
|
"--no-color",
|
|
98152
98928
|
"--no-dead-code",
|
|
98153
98929
|
"--no-score",
|
|
98154
98930
|
"--no-supply-chain",
|
|
98155
98931
|
"--yes"
|
|
98156
98932
|
]),
|
|
98157
|
-
longOptionsWithRequiredValues: new Set([
|
|
98933
|
+
longOptionsWithRequiredValues: new Set([
|
|
98934
|
+
"--blocking",
|
|
98935
|
+
"--max-duration",
|
|
98936
|
+
"--project"
|
|
98937
|
+
]),
|
|
98158
98938
|
longOptionsWithOptionalValues: /* @__PURE__ */ new Set(),
|
|
98159
98939
|
shortOptionsWithoutValues: new Set(["-h", "-y"]),
|
|
98160
98940
|
shortOptionsWithRequiredValues: new Set(["-p"])
|
|
@@ -98296,6 +99076,7 @@ ${highlighter.dim("Scope:")}
|
|
|
98296
99076
|
Dead-code, supply-chain, external lint-config, custom-plugin, and health-score passes are skipped.
|
|
98297
99077
|
Standard scan flags such as ${highlighter.info("--scope")}, ${highlighter.info("--project")}, ${highlighter.info("--verbose")}, and ${highlighter.info("--json")} still work.
|
|
98298
99078
|
`;
|
|
99079
|
+
const MAX_DURATION_OPTION_DESCRIPTION = "scan time budget for the whole run, shared across workspace projects: past it, queued projects, remaining lint batches, and dead-code are skipped and partial results are reported (skipped files and projects are listed in the JSON report)";
|
|
98299
99080
|
const renderCiHelpEpilog = () => `
|
|
98300
99081
|
${highlighter.dim("Examples:")}
|
|
98301
99082
|
${formatExampleLines([
|
|
@@ -98315,8 +99096,12 @@ ${highlighter.dim("Learn more:")}
|
|
|
98315
99096
|
${highlighter.info(CI_URL)}
|
|
98316
99097
|
`;
|
|
98317
99098
|
const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
|
|
98318
|
-
const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much supported JS/TS and inline HTML script source to scan/report: full (default), files, changed (only new issues vs base), or lines (issues whose source spans touch changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").option("--include-untracked", "with --scope files/changed/lines, also scan ordinary untracked files (respects .gitignore)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks (honors --project and config `projects`; exits 0 when nothing is staged)").option("--max-duration <seconds>",
|
|
98319
|
-
program.action(
|
|
99099
|
+
const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("--no-cache", "disable all scan caches for this run").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much supported JS/TS and inline HTML script source to scan/report: full (default), files, changed (only new issues vs base), or lines (issues whose source spans touch changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").option("--include-untracked", "with --scope files/changed/lines, also scan ordinary untracked files (respects .gitignore)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks (honors --project and config `projects`; exits 0 when nothing is staged)").option("--max-duration <seconds>", MAX_DURATION_OPTION_DESCRIPTION).option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
|
|
99100
|
+
program.action((directory = ".", flags) => runScanCommand({
|
|
99101
|
+
directory,
|
|
99102
|
+
flags,
|
|
99103
|
+
invocationCommand: "inspect"
|
|
99104
|
+
}));
|
|
98320
99105
|
program.command("design [directory]").description("Run only the focused UI design diagnostics").addHelpText("after", renderDesignHelpEpilog).action((directory, _options, command) => designAction(directory ?? ".", command.optsWithGlobals()));
|
|
98321
99106
|
program.command("why <location>").description("Explain why a rule fired (or why a suppression didn't apply) at a file:line").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple)").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").action((location, options) => whyAction(location, options));
|
|
98322
99107
|
program.command("install").alias("setup").description("Install the react-doctor skill into your coding agents and optional git hook").option("-y, --yes", "skip prompts, install for all detected agents").option("--dry-run", "show what would be installed without writing files").option("--agent-hooks", "install native non-blocking agent hooks for Claude Code and Cursor").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderInstallHelpEpilog).action(installAction);
|
|
@@ -98337,41 +99122,17 @@ rules.command("category <category> <severity>").description("Set the severity fo
|
|
|
98337
99122
|
rules.command("ignore-tag <tag>").description("Skip a whole rule family by tag before linting (e.g. design)").option("-c, --cwd <cwd>", "working directory", process.cwd()).action((tag, _options, command) => rulesIgnoreTagAction(tag, command.optsWithGlobals()));
|
|
98338
99123
|
rules.command("unignore-tag <tag>").description("Stop ignoring a tag previously skipped via ignore-tag").option("-c, --cwd <cwd>", "working directory", process.cwd()).action((tag, _options, command) => rulesUnignoreTagAction(tag, command.optsWithGlobals()));
|
|
98339
99124
|
program.command("experimental-lsp", { hidden: false }).description("[experimental] run the React Doctor language server over stdio (for editors)").allowUnknownOption().action(() => {});
|
|
98340
|
-
program.command("experimental-tui [directory]", { hidden: true }).description("[experimental] interactive, scrollable scan report").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").option("--no-dead-code", "skip dead-code analysis").option("--no-supply-chain", "skip the dependency supply-chain scan").option("--no-score", "skip the score API, the share URL, and crash reporting").option("-p, --project <names>", "scan specific workspace projects (comma-separated, or *)").option("-y, --yes", "skip the project prompt and scan every discovered project").action(
|
|
98341
|
-
|
|
98342
|
-
|
|
98343
|
-
|
|
98344
|
-
|
|
98345
|
-
const nodeMajorVersion = Number(process.versions.node.split(".")[0]);
|
|
98346
|
-
if (process.stdout.isTTY !== true || process.stdin.isTTY !== true || isNonInteractiveEnvironment() || nodeMajorVersion < 22) {
|
|
98347
|
-
await inspectAction(directory, {
|
|
98348
|
-
deadCode,
|
|
98349
|
-
score: options.score === false ? false : void 0,
|
|
98350
|
-
supplyChain,
|
|
98351
|
-
project: options.project,
|
|
98352
|
-
yes: options.yes,
|
|
98353
|
-
blocking: options.blocking
|
|
98354
|
-
});
|
|
98355
|
-
return;
|
|
98356
|
-
}
|
|
98357
|
-
recordCount(METRIC.cliInvoked, 1, { command: "experimental-tui" });
|
|
98358
|
-
const { runScanApp } = await import("./run-scan-app-DHRvqfkX.js");
|
|
98359
|
-
const { shouldFail } = await runScanApp({
|
|
98360
|
-
directory,
|
|
98361
|
-
options: {
|
|
98362
|
-
deadCode,
|
|
98363
|
-
noScore,
|
|
98364
|
-
supplyChain
|
|
98365
|
-
},
|
|
98366
|
-
projectFlag: options.project,
|
|
98367
|
-
skipPrompts: options.yes ?? false,
|
|
98368
|
-
blocking: options.blocking
|
|
98369
|
-
});
|
|
98370
|
-
if (shouldFail) process.exitCode = 1;
|
|
98371
|
-
});
|
|
99125
|
+
program.command("experimental-tui [directory]", { hidden: true }).description("[experimental] interactive, scrollable scan report").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").option("--no-dead-code", "skip dead-code analysis").option("--no-supply-chain", "skip the dependency supply-chain scan").option("--score", "only print the numeric score (for scripts and CI)").option("--no-score", "skip the score API, the share URL, and crash reporting").option("--no-cache", "disable all scan caches for this run").option("--max-duration <seconds>", MAX_DURATION_OPTION_DESCRIPTION).option("-p, --project <names>", "scan specific workspace projects (comma-separated, or *)").option("-y, --yes", "skip the project prompt and scan every discovered project").action((directory = ".", _localOptions, command) => runScanCommand({
|
|
99126
|
+
directory,
|
|
99127
|
+
flags: command.optsWithGlobals(),
|
|
99128
|
+
invocationCommand: "experimental-tui"
|
|
99129
|
+
}));
|
|
98372
99130
|
process.stdout.on("error", (error) => {
|
|
98373
99131
|
if (error.code === "EPIPE") process.exit(0);
|
|
98374
99132
|
});
|
|
99133
|
+
process.stderr.on("error", (error) => {
|
|
99134
|
+
if (error.code === "EPIPE") process.exit(0);
|
|
99135
|
+
});
|
|
98375
99136
|
const knownCommands = program.commands.flatMap((command) => [command.name(), ...command.aliases()]);
|
|
98376
99137
|
const strippedArgv = stripUnknownCliFlags(process.argv);
|
|
98377
99138
|
if (process.argv.includes("-V") && !strippedArgv.includes("-V")) {
|
|
@@ -98395,7 +99156,7 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
98395
99156
|
handleError(error, { sentryEventId });
|
|
98396
99157
|
});
|
|
98397
99158
|
//#endregion
|
|
98398
|
-
export {
|
|
99159
|
+
export { TUI_REPORT_DETAIL_WIDTH_FRACTION as $, shouldRecordOnboarding as A, buildSortedRuleGroups as B, createInvocationInspect as C, markOnboardingComplete as D, doctorFace as E, buildEmptyReportMessage as F, recordCount as G, formatLearnMoreLine as H, hasIncompleteScoreAnalysis as I, findNearestPackageDirectory as J, recordDistribution as K, buildNoScoreMessage as L, computeProjectedScore as M, formatElapsedTime as N, canAnimateOnboarding as O, buildCodeFrame as P, TUI_REPORT_COMPACT_MAX_ROWS as Q, hasLintHardFailure as R, deduplicateProjectScans as S, easeOutCubic as T, resolveBlockingLevel as U, formatFixRecipeLine as V, isReactDoctorWorkflowInstalled as W, CI_TRUST_COMPANIES as X, isUnicodeSupported as Y, METRIC as Z, launchCliAgent as _, succeed$3 as _t, selectProjects as a, buildRuleDocsUrl as at, formatSkippedProjectsMessage as b, resolveWorkspaceDeadCodeOwner as c, highlighter as ct, countUniqueScannedFiles as d, remainingDeadlineBudgetMs as dt, isCiEnvironment as et, setUpGitHubActions as f, resolveScanTarget as ft, copyToClipboard as g, void_ as gt, CLI_AGENT_BINARIES as h, sync as ht, discoverWorkspacePackages as i, Reporter as it, filterDiagnosticsByCategories as j, isOnboardingForced as k, resolveCliInspectOptions as l, isPathInsideDirectory as lt, detectLaunchableAgents as m, runPromise as mt, registerActiveTuiRenderer as n, GITHUB_ACTIONS_SETUP_URL as nt, resolveProjectScan as o, getCategoryImpact as ot, openUrl as p, yieldToEventLoop as pt, toForwardSlashes as q, shouldFailScanGate as r, Progress as rt, retryMissingProjectScores as s, hasPublishedFixRecipe as st, exitGracefully as t, DIAGNOSTIC_CATEGORY_BUCKETS as tt, filterScansForSurface as u, mapWithConcurrency as ut, buildHandoffPayload as v, printFooter as w, collectProjectSourceFileCounts as x, isShareOptedOut as y, buildRulePriorityMap as z };
|
|
98399
99160
|
|
|
98400
99161
|
//# sourceMappingURL=cli.js.map
|
|
98401
|
-
//# debugId=
|
|
99162
|
+
//# debugId=e74e6a68-20e1-59fe-8db0-62b87e8fe049
|