react-doctor 0.9.3 → 0.9.4-dev.212b8b4
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 +1368 -684
- package/dist/index.d.ts +10 -2
- package/dist/index.js +721 -371
- package/dist/lsp.js +773 -344
- 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]="7d7e0bde-eb19-58b5-b161-625bcb1b2588")}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 = [];
|
|
@@ -74062,7 +74357,7 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
74062
74357
|
}
|
|
74063
74358
|
return enabled;
|
|
74064
74359
|
};
|
|
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 }) => {
|
|
74360
|
+
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
74361
|
const hasIncludedTags = includedTags.size > 0;
|
|
74067
74362
|
const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" || hasIncludedTags ? null : resolveReactHooksJsPlugin(project.hasReactCompiler || project.hasReactCompilerLintPlugin === true, customRulesOnly);
|
|
74068
74363
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
@@ -74126,7 +74421,8 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
74126
74421
|
}))
|
|
74127
74422
|
} : {},
|
|
74128
74423
|
...project.shopifyFlashListMajorVersion !== null ? { shopifyFlashListMajorVersion: project.shopifyFlashListMajorVersion } : {},
|
|
74129
|
-
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {}
|
|
74424
|
+
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {},
|
|
74425
|
+
...projectIndexModuleSources !== void 0 ? { projectIndexModuleSources: [...projectIndexModuleSources] } : {}
|
|
74130
74426
|
} },
|
|
74131
74427
|
rules: {
|
|
74132
74428
|
...reactCompilerRules,
|
|
@@ -74375,11 +74671,17 @@ const collectUnpluginAutoImportGlobalScopes = ({ rootDirectory, candidateFiles }
|
|
|
74375
74671
|
return scopes.some((scope) => scope.names.length > 0) ? scopes : [];
|
|
74376
74672
|
};
|
|
74377
74673
|
const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
|
|
74674
|
+
const decodeDiagnostic = (raw) => {
|
|
74675
|
+
const diagnostic = validateDiagnostic$1(raw);
|
|
74676
|
+
return {
|
|
74677
|
+
...diagnostic,
|
|
74678
|
+
relatedLocations: diagnostic.relatedLocations?.map((location) => ({ ...location }))
|
|
74679
|
+
};
|
|
74680
|
+
};
|
|
74378
74681
|
const decodeFileDiagnostics = (raw) => {
|
|
74379
74682
|
if (!Array.isArray(raw)) return null;
|
|
74380
74683
|
try {
|
|
74381
|
-
|
|
74382
|
-
return raw;
|
|
74684
|
+
return raw.map((entry) => decodeDiagnostic(entry));
|
|
74383
74685
|
} catch {
|
|
74384
74686
|
return null;
|
|
74385
74687
|
}
|
|
@@ -75611,6 +75913,12 @@ const buildProfiledNodeArguments = (input) => [
|
|
|
75611
75913
|
...input.heapProfileDirectory ? ["--heap-prof", `--heap-prof-dir=${input.heapProfileDirectory}`] : [],
|
|
75612
75914
|
...input.argumentsList
|
|
75613
75915
|
];
|
|
75916
|
+
const lowerChildProcessPriority = (processId) => {
|
|
75917
|
+
if (processId === void 0) return;
|
|
75918
|
+
try {
|
|
75919
|
+
OS.setPriority(processId, OS.constants.priority.PRIORITY_BELOW_NORMAL);
|
|
75920
|
+
} catch {}
|
|
75921
|
+
};
|
|
75614
75922
|
const SANITIZED_ENV = buildOxlintChildEnv(process.env);
|
|
75615
75923
|
/**
|
|
75616
75924
|
* Spawn one oxlint subprocess with hard ceilings on wall time and
|
|
@@ -75649,6 +75957,7 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
|
|
|
75649
75957
|
"pipe"
|
|
75650
75958
|
]
|
|
75651
75959
|
});
|
|
75960
|
+
lowerChildProcessPriority(child.pid);
|
|
75652
75961
|
const onAbort = () => {
|
|
75653
75962
|
child.kill("SIGKILL");
|
|
75654
75963
|
reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) }));
|
|
@@ -75771,7 +76080,7 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
75771
76080
|
* loop with a slimmer config in that case.
|
|
75772
76081
|
*/
|
|
75773
76082
|
const spawnLintBatches = async (input) => {
|
|
75774
|
-
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, sourceMapByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth =
|
|
76083
|
+
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, sourceMapByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 9, signal, deadlineEpochMs } = input;
|
|
75775
76084
|
const resolveSourcePath = (filePath) => {
|
|
75776
76085
|
const absoluteFilePath = Path.isAbsolute(filePath) ? filePath : Path.resolve(rootDirectory, filePath);
|
|
75777
76086
|
return sourcePathByLintPath?.get(Path.normalize(absoluteFilePath)) ?? filePath;
|
|
@@ -75779,6 +76088,12 @@ const spawnLintBatches = async (input) => {
|
|
|
75779
76088
|
const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
|
|
75780
76089
|
let rescueDeadlineEpochMs;
|
|
75781
76090
|
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0 || rescueDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(rescueDeadlineEpochMs) === 0;
|
|
76091
|
+
const resolveSpawnTimeoutMs = () => {
|
|
76092
|
+
const deadlineBudgets = [deadlineEpochMs, rescueDeadlineEpochMs].filter((deadline) => deadline !== void 0).map(remainingDeadlineBudgetMs);
|
|
76093
|
+
if (deadlineBudgets.length === 0) return spawnTimeoutMs;
|
|
76094
|
+
const remainingDeadlineMs = Math.min(...deadlineBudgets);
|
|
76095
|
+
return spawnTimeoutMs === void 0 ? remainingDeadlineMs : Math.min(spawnTimeoutMs, remainingDeadlineMs);
|
|
76096
|
+
};
|
|
75782
76097
|
const runBatchPass = async (concurrency, passBatches, passOnFileProgress) => {
|
|
75783
76098
|
const totalFileCount = passBatches.reduce((sum, batch) => sum + batch.length, 0);
|
|
75784
76099
|
const allDiagnostics = [];
|
|
@@ -75796,12 +76111,13 @@ const spawnLintBatches = async (input) => {
|
|
|
75796
76111
|
const batchArgs = [...baseArgs, ...batch];
|
|
75797
76112
|
try {
|
|
75798
76113
|
const spawnBatch = () => {
|
|
75799
|
-
|
|
76114
|
+
const effectiveSpawnTimeoutMs = resolveSpawnTimeoutMs();
|
|
76115
|
+
if (effectiveSpawnTimeoutMs === 0) {
|
|
75800
76116
|
deadlineSkippedFiles.push(...batch);
|
|
75801
76117
|
batchState.deadlineSkippedFileCount += batch.length;
|
|
75802
76118
|
return Promise.resolve(null);
|
|
75803
76119
|
}
|
|
75804
|
-
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath,
|
|
76120
|
+
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, effectiveSpawnTimeoutMs, outputMaxBytes, signal, () => {
|
|
75805
76121
|
if (batchState.didStart) return;
|
|
75806
76122
|
batchState.didStart = true;
|
|
75807
76123
|
startedFileCount += batchState.initialFileCount;
|
|
@@ -75956,11 +76272,36 @@ const validateRuleRegistration = () => {
|
|
|
75956
76272
|
].filter((entry) => entry !== null).join("; ");
|
|
75957
76273
|
console.warn(`[react-doctor] rule-registration drift: ${detail}`);
|
|
75958
76274
|
};
|
|
76275
|
+
const PROJECT_INDEX_MODULE_SOURCES = [
|
|
76276
|
+
"next/og",
|
|
76277
|
+
"@vercel/og",
|
|
76278
|
+
"satori",
|
|
76279
|
+
"remotion"
|
|
76280
|
+
];
|
|
76281
|
+
const collectProjectIndexModuleSources = async (rootDirectory, candidateFiles) => {
|
|
76282
|
+
const foundModuleSources = /* @__PURE__ */ new Set();
|
|
76283
|
+
let sliceStartedAt = performance.now();
|
|
76284
|
+
for (const candidateFile of candidateFiles) {
|
|
76285
|
+
let sourceBuffer;
|
|
76286
|
+
try {
|
|
76287
|
+
sourceBuffer = NFS.readFileSync(Path.isAbsolute(candidateFile) ? candidateFile : Path.resolve(rootDirectory, candidateFile));
|
|
76288
|
+
} catch {
|
|
76289
|
+
continue;
|
|
76290
|
+
}
|
|
76291
|
+
for (const moduleSource of PROJECT_INDEX_MODULE_SOURCES) if (sourceBuffer.includes(`"${moduleSource}"`) || sourceBuffer.includes(`'${moduleSource}'`)) foundModuleSources.add(moduleSource);
|
|
76292
|
+
if (foundModuleSources.size === PROJECT_INDEX_MODULE_SOURCES.length) break;
|
|
76293
|
+
if (performance.now() - sliceStartedAt >= 12) {
|
|
76294
|
+
await yieldToEventLoop();
|
|
76295
|
+
sliceStartedAt = performance.now();
|
|
76296
|
+
}
|
|
76297
|
+
}
|
|
76298
|
+
return [...foundModuleSources].sort();
|
|
76299
|
+
};
|
|
75959
76300
|
/**
|
|
75960
76301
|
* Balanced LPT (longest-processing-time-first) lint batch planner. Where
|
|
75961
|
-
* `batchIncludePaths` greedily fills
|
|
75962
|
-
* remainder batch
|
|
75963
|
-
*
|
|
76302
|
+
* `batchIncludePaths` greedily fills fixed-size batches and leaves a small
|
|
76303
|
+
* remainder batch, so the parallel pool's wall clock is set by whichever
|
|
76304
|
+
* full chunk happened to collect the most work while
|
|
75964
76305
|
* the remainder's worker idles), this planner keeps the SAME mandatory batch
|
|
75965
76306
|
* count — `ceil(files / OXLINT_MAX_FILES_PER_BATCH)`, the JS-plugin
|
|
75966
76307
|
* perf-cliff / native-binding SIGABRT guard — and assigns files largest-first,
|
|
@@ -75989,7 +76330,7 @@ const validateRuleRegistration = () => {
|
|
|
75989
76330
|
*/
|
|
75990
76331
|
const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
75991
76332
|
if (files.length === 0) return [];
|
|
75992
|
-
const batchCount = Math.ceil(files.length /
|
|
76333
|
+
const batchCount = Math.ceil(files.length / 200);
|
|
75993
76334
|
const maxFilesPerBatch = Math.ceil(files.length / batchCount);
|
|
75994
76335
|
const baseArgsLengthChars = estimateArgsLength(baseArgs);
|
|
75995
76336
|
const descendingSizeIndexes = [...files.keys()].sort((leftIndex, rightIndex) => (sizeByFile.get(files[rightIndex]) ?? 0) - (sizeByFile.get(files[leftIndex]) ?? 0));
|
|
@@ -76518,6 +76859,7 @@ const runOxlint = async (options) => {
|
|
|
76518
76859
|
await yieldToEventLoop();
|
|
76519
76860
|
const sizedScanFiles = includePaths === void 0 ? listSourceFilesWithSize(rootDirectory) : null;
|
|
76520
76861
|
const candidateFiles = includePaths !== void 0 ? includePaths : (sizedScanFiles ?? []).map((entry) => entry.path);
|
|
76862
|
+
const projectIndexModuleSources = includePaths === void 0 && options.deadlineEpochMs === void 0 ? await collectProjectIndexModuleSources(rootDirectory, candidateFiles) : void 0;
|
|
76521
76863
|
unpluginAutoImportGlobalScopes = collectUnpluginAutoImportGlobalScopes({
|
|
76522
76864
|
rootDirectory,
|
|
76523
76865
|
candidateFiles
|
|
@@ -76540,6 +76882,7 @@ const runOxlint = async (options) => {
|
|
|
76540
76882
|
runtimeGlobals,
|
|
76541
76883
|
unpluginAutoImportGlobalScopes,
|
|
76542
76884
|
serverAuthFunctionNames,
|
|
76885
|
+
projectIndexModuleSources,
|
|
76543
76886
|
severityControls,
|
|
76544
76887
|
userPlugins,
|
|
76545
76888
|
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
|
|
@@ -77007,10 +77350,10 @@ const translateProjectInfoError = (cause, directory) => {
|
|
|
77007
77350
|
}) });
|
|
77008
77351
|
};
|
|
77009
77352
|
var Project = class Project extends Service()("react-doctor/Project") {
|
|
77010
|
-
static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (
|
|
77353
|
+
static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (input) {
|
|
77011
77354
|
return yield* try_({
|
|
77012
|
-
try: () => discoverProject(directory),
|
|
77013
|
-
catch: (cause) => translateProjectInfoError(cause, directory)
|
|
77355
|
+
try: () => discoverProject(input.directory, { sourceFileCount: input.sourceFileCount }),
|
|
77356
|
+
catch: (cause) => translateProjectInfoError(cause, input.directory)
|
|
77014
77357
|
});
|
|
77015
77358
|
}) }));
|
|
77016
77359
|
static layerOf = (projectInfo) => succeed$3(Project, Project.of({ discover: () => succeed$2(projectInfo) }));
|
|
@@ -77595,9 +77938,10 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
77595
77938
|
* outage.
|
|
77596
77939
|
*/
|
|
77597
77940
|
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
|
-
|
|
77941
|
+
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (configuredTimeoutMs) => checkSupplyChain({
|
|
77942
|
+
rootDirectory: input.rootDirectory,
|
|
77943
|
+
userConfig: input.userConfig,
|
|
77944
|
+
totalTimeoutMs: input.timeoutMs ?? configuredTimeoutMs
|
|
77601
77945
|
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), withSpan("SupplyChain.run"))) }));
|
|
77602
77946
|
static layerOf = (diagnostics) => succeed$3(SupplyChain, SupplyChain.of({ run: () => fromIterable$1(diagnostics) }));
|
|
77603
77947
|
};
|
|
@@ -77632,10 +77976,6 @@ const NO_HOOKS = {
|
|
|
77632
77976
|
beforeLint: () => void_,
|
|
77633
77977
|
afterLint: () => void_
|
|
77634
77978
|
};
|
|
77635
|
-
const filterMapNullable = (transform) => fromPredicateOption((value) => {
|
|
77636
|
-
const result = transform(value);
|
|
77637
|
-
return result === null ? none() : some(result);
|
|
77638
|
-
});
|
|
77639
77979
|
const fileReader = (filesService, rootDirectory) => (filePath) => {
|
|
77640
77980
|
const lines = runSync(filesService.readLines({
|
|
77641
77981
|
filePath,
|
|
@@ -77659,7 +77999,7 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
77659
77999
|
* The GitHub viewer-permission lookup is forked onto a background
|
|
77660
78000
|
* fiber here and joined late (it feeds score metadata, not
|
|
77661
78001
|
* diagnostics).
|
|
77662
|
-
* 2. beforeLint hook (e.g. CLI
|
|
78002
|
+
* 2. beforeLint hook (e.g. CLI records project telemetry)
|
|
77663
78003
|
* 3. environment checks (reduced-motion + pnpm hardening +
|
|
77664
78004
|
* expo/react-native), collected synchronously. The heavier
|
|
77665
78005
|
* content-regex security scan is forked instead (like supply-chain
|
|
@@ -77672,13 +78012,11 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
77672
78012
|
* `SupplyChainOverlapTimeoutMs` (measured from fork) so a hung
|
|
77673
78013
|
* socket can't drag out its join; on timeout it fails open to no
|
|
77674
78014
|
* 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
|
|
78015
|
+
* 5. Linter.run runs; DeadCode.run forks only when
|
|
78016
|
+
* REACT_DOCTOR_DEAD_CODE_OVERLAP=on. The default "auto" and explicit
|
|
78017
|
+
* "off" paths stay sequential. The fiber is joined (or interrupted,
|
|
78018
|
+
* SIGKILLing its worker, on lint failure) before diagnostics are
|
|
78019
|
+
* concatenated. The afterLint hook
|
|
77682
78020
|
* fires between lint and dead-code. Progress spinner labels AND the
|
|
77683
78021
|
* final diagnostic / score order stay independent of execution
|
|
77684
78022
|
* order, so terminal output is identical either way; supply-chain
|
|
@@ -77713,8 +78051,28 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77713
78051
|
const partialFailuresRef = yield* LintPartialFailures;
|
|
77714
78052
|
const resolvedConfig = yield* configService.resolve(input.directory);
|
|
77715
78053
|
const scanDirectory = resolvedConfig.resolvedDirectory;
|
|
77716
|
-
const
|
|
78054
|
+
const excludedProjectDirectories = (input.excludedProjectDirectories ?? []).map((excludedDirectory) => Path.resolve(excludedDirectory)).filter((excludedDirectory) => isPathInsideDirectory(excludedDirectory, scanDirectory));
|
|
78055
|
+
const excludedProjectRelativePaths = excludedProjectDirectories.map((excludedDirectory) => Path.relative(scanDirectory, excludedDirectory).replaceAll("\\", "/"));
|
|
78056
|
+
const isExcludedProjectDiagnostic = (diagnostic) => {
|
|
78057
|
+
const relativeFilePath = toNormalizedRelativePath(diagnostic.filePath, scanDirectory);
|
|
78058
|
+
return excludedProjectRelativePaths.some((excludedRelativePath) => relativeFilePath === excludedRelativePath || relativeFilePath.startsWith(`${excludedRelativePath}/`));
|
|
78059
|
+
};
|
|
78060
|
+
const precomputedSourceFiles = input.suppressScanSummary === true || excludedProjectDirectories.length > 0 ? yield* filesService.listSourceFilesCooperative({
|
|
78061
|
+
rootDirectory: scanDirectory,
|
|
78062
|
+
signal: input.signal
|
|
78063
|
+
}) : null;
|
|
78064
|
+
const includedPrecomputedSourceFiles = precomputedSourceFiles !== null && excludedProjectDirectories.length > 0 ? filterPathsOutsideDirectories({
|
|
78065
|
+
rootDirectory: scanDirectory,
|
|
78066
|
+
relativePaths: precomputedSourceFiles,
|
|
78067
|
+
excludedDirectories: excludedProjectDirectories
|
|
78068
|
+
}) : precomputedSourceFiles;
|
|
78069
|
+
const sourceFileCount = excludedProjectDirectories.length > 0 ? includedPrecomputedSourceFiles?.length : input.precomputedSourceFileCount ?? includedPrecomputedSourceFiles?.length;
|
|
78070
|
+
const project = yield* projectService.discover({
|
|
78071
|
+
directory: scanDirectory,
|
|
78072
|
+
sourceFileCount
|
|
78073
|
+
});
|
|
77717
78074
|
if (!isAnalyzableProject(project)) return yield* new ReactDoctorError({ reason: new NoReactDependency({ directory: scanDirectory }) });
|
|
78075
|
+
const deadCodeSourceFileCount = input.retainExcludedProjectDeadCodeDiagnostics === true && precomputedSourceFiles !== null ? precomputedSourceFiles.length : project.sourceFileCount;
|
|
77718
78076
|
const [repo, sha, defaultBranch] = yield* all([
|
|
77719
78077
|
gitService.githubRepo(scanDirectory).pipe(orElseSucceed(() => null)),
|
|
77720
78078
|
gitService.headSha(scanDirectory).pipe(orElseSucceed(() => null)),
|
|
@@ -77725,11 +78083,16 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77725
78083
|
directory: scanDirectory,
|
|
77726
78084
|
repo
|
|
77727
78085
|
}).pipe(orElseSucceed(() => null)) : succeed$2(null));
|
|
77728
|
-
|
|
77729
|
-
|
|
78086
|
+
let lintIncludePaths = (input.skipExplicitIncludePathFilter ? input.includePaths.length > 0 ? [...input.includePaths] : void 0 : computeExplicitLintIncludePaths([...input.includePaths])) ?? resolveLintIncludePaths(scanDirectory, resolvedConfig.config, includedPrecomputedSourceFiles ?? void 0);
|
|
78087
|
+
if (excludedProjectDirectories.length > 0) lintIncludePaths = filterPathsOutsideDirectories({
|
|
78088
|
+
rootDirectory: scanDirectory,
|
|
78089
|
+
relativePaths: lintIncludePaths ?? includedPrecomputedSourceFiles ?? (yield* filesService.listSourceFiles(scanDirectory)),
|
|
78090
|
+
excludedDirectories: excludedProjectDirectories
|
|
78091
|
+
});
|
|
78092
|
+
const fallbackScannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? includedPrecomputedSourceFiles ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
|
|
77730
78093
|
const beforeLint = hooks.beforeLint ?? NO_HOOKS.beforeLint;
|
|
77731
78094
|
const afterLint = hooks.afterLint ?? NO_HOOKS.afterLint;
|
|
77732
|
-
yield* beforeLint(project
|
|
78095
|
+
yield* beforeLint(project);
|
|
77733
78096
|
const isDiffMode = input.includePaths.length > 0;
|
|
77734
78097
|
const showWarnings = input.warnings ?? resolvedConfig.config?.warnings ?? true;
|
|
77735
78098
|
const transform = buildDiagnosticPipeline({
|
|
@@ -77739,7 +78102,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77739
78102
|
respectInlineDisables: input.respectInlineDisables,
|
|
77740
78103
|
showWarnings
|
|
77741
78104
|
});
|
|
77742
|
-
const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(
|
|
78105
|
+
const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(fromPredicateOption((diagnostic) => {
|
|
78106
|
+
const filteredDiagnostic = transform.apply(diagnostic);
|
|
78107
|
+
return filteredDiagnostic === null ? none() : some(filteredDiagnostic);
|
|
78108
|
+
})));
|
|
77743
78109
|
const applyPerElementPipeline = (rawStream) => filterPerElementPipeline(rawStream).pipe(tap((diagnostic) => reporterService.emit(diagnostic)));
|
|
77744
78110
|
const envCollected = yield* runCollect(applyPerElementPipeline(fromIterable$1(isDiffMode ? [] : [
|
|
77745
78111
|
...checkReducedMotion(scanDirectory),
|
|
@@ -77749,17 +78115,31 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77749
78115
|
...checkReactNativeProject(scanDirectory, project)
|
|
77750
78116
|
])));
|
|
77751
78117
|
const securityScanFailedRef = yield* make$13(false);
|
|
77752
|
-
|
|
78118
|
+
let didSecurityScanReachDeadline = false;
|
|
78119
|
+
const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(tryPromise((signal) => checkSecurityScanCooperative(scanDirectory, {
|
|
77753
78120
|
project,
|
|
77754
78121
|
ignoredTags: input.ignoredTags,
|
|
77755
78122
|
includedTags: input.includedTags,
|
|
77756
|
-
includeTagDefaults: input.includeTagDefaults
|
|
78123
|
+
includeTagDefaults: input.includeTagDefaults,
|
|
78124
|
+
excludedDirectories: new Set(excludedProjectDirectories),
|
|
78125
|
+
deadlineEpochMs: input.deadlineEpochMs,
|
|
78126
|
+
signal,
|
|
78127
|
+
onDeadlineExceeded: () => {
|
|
78128
|
+
didSecurityScanReachDeadline = true;
|
|
78129
|
+
}
|
|
77757
78130
|
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), catch_$1(() => set(securityScanFailedRef, true).pipe(as(empty$4))))))).pipe(withSpan("SecurityScan.run")));
|
|
78131
|
+
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
78132
|
+
const capOptionalToDeadline = (phaseTimeoutMs) => {
|
|
78133
|
+
if (input.deadlineEpochMs === void 0) return phaseTimeoutMs;
|
|
78134
|
+
const remainingBudgetMs = remainingDeadlineBudgetMs(input.deadlineEpochMs);
|
|
78135
|
+
return phaseTimeoutMs === null ? remainingBudgetMs : Math.min(phaseTimeoutMs, remainingBudgetMs);
|
|
78136
|
+
};
|
|
77758
78137
|
const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
|
|
77759
|
-
const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
|
|
78138
|
+
const supplyChainOverlapTimeout = capToDeadline(yield* SupplyChainOverlapTimeoutMs);
|
|
77760
78139
|
const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
|
|
77761
78140
|
rootDirectory: scanDirectory,
|
|
77762
|
-
userConfig: resolvedConfig.config
|
|
78141
|
+
userConfig: resolvedConfig.config,
|
|
78142
|
+
timeoutMs: supplyChainOverlapTimeout
|
|
77763
78143
|
}))).pipe(map$3((diagnostics) => ({
|
|
77764
78144
|
diagnostics,
|
|
77765
78145
|
timedOut: false
|
|
@@ -77783,46 +78163,49 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77783
78163
|
const scanConcurrency = resolveScanConcurrency(yield* OxlintConcurrency);
|
|
77784
78164
|
const lintPhaseTimeoutMs = yield* LintPhaseTimeoutMs;
|
|
77785
78165
|
const deadCodePhaseTimeoutMs = yield* DeadCodePhaseTimeoutMs;
|
|
77786
|
-
const resolveDeadCodePhaseTimeoutMs = (scaledPhaseTimeoutMs) => deadCodePhaseTimeoutMs === 15e4 ? scaledPhaseTimeoutMs : deadCodePhaseTimeoutMs;
|
|
77787
78166
|
const workerCountSuffix = scanConcurrency > 1 ? ` ${highlighter.dim(`[~${scanConcurrency} workers]`)}` : "";
|
|
77788
|
-
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
77789
78167
|
const shouldRunDeadCode = input.runDeadCode && !isDiffMode && (showWarnings || deadCodeMaySurfaceWhenWarningsHidden(resolvedConfig.config));
|
|
77790
78168
|
const deadCodeOverlapMode = yield* DeadCodeOverlap;
|
|
77791
78169
|
const shouldOverlapDeadCode = shouldRunDeadCode && deadCodeOverlapMode === "on";
|
|
77792
78170
|
const deadCodeParseConcurrency = shouldOverlapDeadCode ? Math.max(1, Math.floor(scanConcurrency * DEAD_CODE_OVERLAP_PARSE_SHARE)) : void 0;
|
|
77793
78171
|
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
|
-
|
|
78172
|
+
const buildCollectDeadCode = (deadCodeTimeout) => {
|
|
78173
|
+
const collectDeadCode = runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
78174
|
+
rootDirectory: scanDirectory,
|
|
78175
|
+
parseConcurrency: deadCodeParseConcurrency,
|
|
78176
|
+
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
|
|
78177
|
+
onCacheOutcome: (didHitCache) => {
|
|
78178
|
+
deadCodeCacheHit = didHitCache;
|
|
78179
|
+
},
|
|
78180
|
+
onSummaryCacheStats: (stats) => {
|
|
78181
|
+
deadCodeSummaryCacheHits = stats.hits;
|
|
78182
|
+
deadCodeSummaryCacheMisses = stats.misses;
|
|
78183
|
+
}
|
|
78184
|
+
}).pipe(filter((diagnostic) => input.retainExcludedProjectDeadCodeDiagnostics === true || !isExcludedProjectDiagnostic(diagnostic)), catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
78185
|
+
yield* set(deadCodeFailure, {
|
|
78186
|
+
didFail: true,
|
|
78187
|
+
reason: error.message
|
|
78188
|
+
});
|
|
78189
|
+
return empty$4;
|
|
78190
|
+
}))))));
|
|
78191
|
+
const phaseTimeoutMs = deadCodeTimeout.phaseTimeoutMs;
|
|
78192
|
+
if (phaseTimeoutMs === null) return collectDeadCode;
|
|
78193
|
+
return collectDeadCode.pipe(timeoutOption(phaseTimeoutMs), flatMap$2(match$3({
|
|
78194
|
+
onNone: () => set(deadCodeFailure, {
|
|
78195
|
+
didFail: true,
|
|
78196
|
+
reason: `Dead-code analysis exceeded ${Math.round(phaseTimeoutMs / MILLISECONDS_PER_SECOND)}s and was skipped.`
|
|
78197
|
+
}).pipe(as([])),
|
|
78198
|
+
onSome: succeed$2
|
|
78199
|
+
})));
|
|
78200
|
+
};
|
|
77818
78201
|
const overlapDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
77819
|
-
sourceFileCount:
|
|
78202
|
+
sourceFileCount: deadCodeSourceFileCount,
|
|
77820
78203
|
deadCodeConcurrency: deadCodeParseConcurrency ?? scanConcurrency,
|
|
77821
78204
|
fullConcurrency: scanConcurrency
|
|
77822
78205
|
});
|
|
77823
78206
|
const deadCodeFiber = shouldOverlapDeadCode ? yield* forkChild(buildCollectDeadCode({
|
|
77824
78207
|
workerTimeoutMs: overlapDeadCodeTimeout.workerTimeoutMs,
|
|
77825
|
-
phaseTimeoutMs:
|
|
78208
|
+
phaseTimeoutMs: capOptionalToDeadline(deadCodePhaseTimeoutMs)
|
|
77826
78209
|
})) : null;
|
|
77827
78210
|
const scanProgress = yield* progressService.start("Scanning...");
|
|
77828
78211
|
const scanStartTime = Date.now();
|
|
@@ -77873,7 +78256,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77873
78256
|
});
|
|
77874
78257
|
return empty$4;
|
|
77875
78258
|
}))));
|
|
77876
|
-
const
|
|
78259
|
+
const collectLintDiagnostics = runCollect(filterPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream));
|
|
78260
|
+
const lintCollected = dedupeRelatedDiagnostics(yield* lintPhaseTimeoutMs === null ? collectLintDiagnostics : collectLintDiagnostics.pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
|
|
77877
78261
|
onNone: () => set(lintFailure, {
|
|
77878
78262
|
didFail: true,
|
|
77879
78263
|
reason: `Lint analysis exceeded ${lintPhaseTimeoutMs / MILLISECONDS_PER_SECOND}s and was skipped.`,
|
|
@@ -77903,13 +78287,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77903
78287
|
} else {
|
|
77904
78288
|
yield* scanProgress.update(`Scanned ${scannedFilesLabel}, analyzing dead code...`);
|
|
77905
78289
|
const sequentialDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
77906
|
-
sourceFileCount: totalFileCount,
|
|
78290
|
+
sourceFileCount: input.retainExcludedProjectDeadCodeDiagnostics === true ? deadCodeSourceFileCount : totalFileCount,
|
|
77907
78291
|
deadCodeConcurrency: scanConcurrency,
|
|
77908
78292
|
fullConcurrency: scanConcurrency
|
|
77909
78293
|
});
|
|
77910
78294
|
deadCodeCollected = deadCodeFiber !== null ? yield* join(deadCodeFiber) : yield* buildCollectDeadCode({
|
|
77911
78295
|
workerTimeoutMs: sequentialDeadCodeTimeout.workerTimeoutMs,
|
|
77912
|
-
phaseTimeoutMs:
|
|
78296
|
+
phaseTimeoutMs: capOptionalToDeadline(deadCodePhaseTimeoutMs)
|
|
77913
78297
|
});
|
|
77914
78298
|
}
|
|
77915
78299
|
const deadCodeFailureState = lintFailureState.didFail ? {
|
|
@@ -77953,7 +78337,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77953
78337
|
metadata: scoreMetadata
|
|
77954
78338
|
});
|
|
77955
78339
|
const lintPartialFailures = yield* get$2(partialFailuresRef);
|
|
77956
|
-
const
|
|
78340
|
+
const didSecurityScanFail = yield* get$2(securityScanFailedRef);
|
|
78341
|
+
const securityScanFailed = didSecurityScanFail || didSecurityScanReachDeadline;
|
|
78342
|
+
let securityScanFailureReason = null;
|
|
78343
|
+
if (didSecurityScanReachDeadline) securityScanFailureReason = "Security scan reached the max scan duration; findings collected before the deadline were preserved.";
|
|
78344
|
+
else if (didSecurityScanFail) securityScanFailureReason = "Security scan failed and was skipped.";
|
|
77957
78345
|
return {
|
|
77958
78346
|
project,
|
|
77959
78347
|
userConfig: resolvedConfig.config,
|
|
@@ -77976,6 +78364,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77976
78364
|
scanConcurrency,
|
|
77977
78365
|
supplyChainOverlapTimedOut: supplyChainResult.timedOut,
|
|
77978
78366
|
securityScanFailed,
|
|
78367
|
+
securityScanFailureReason,
|
|
77979
78368
|
lintCacheHitFileCount,
|
|
77980
78369
|
lintCacheTotalFileCount,
|
|
77981
78370
|
lintSidecarReplayedFileCount,
|
|
@@ -77991,7 +78380,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
77991
78380
|
"inspect.runDeadCode": input.runDeadCode,
|
|
77992
78381
|
"inspect.isCi": input.isCi,
|
|
77993
78382
|
"inspect.scoreSurface": input.scoreSurface ?? "score"
|
|
77994
|
-
} }), (scanProgram) => flatMap$2(ScanDeadlineMs, (scanDeadlineMs) =>
|
|
78383
|
+
} }), (scanProgram) => flatMap$2(ScanDeadlineMs, (scanDeadlineMs) => {
|
|
78384
|
+
if (scanDeadlineMs === null) return scanProgram;
|
|
78385
|
+
return scanProgram.pipe(timeout(scanDeadlineMs), catchTag$1("TimeoutError", () => new ReactDoctorError({ reason: new ScanDeadlineExceeded({ detail: `${scanDeadlineMs / MILLISECONDS_PER_SECOND}s elapsed` }) })));
|
|
78386
|
+
}));
|
|
77995
78387
|
const parseNodeVersion = (versionString) => {
|
|
77996
78388
|
const [major = 0, minor = 0, patch = 0] = versionString.replace(/^v/, "").trim().split(".").map(Number);
|
|
77997
78389
|
return {
|
|
@@ -78103,16 +78495,6 @@ var NodeResolver = class NodeResolver extends Service()("react-doctor/NodeResolv
|
|
|
78103
78495
|
});
|
|
78104
78496
|
};
|
|
78105
78497
|
/**
|
|
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
78498
|
* Writes a set of source files (supplied by `readContent` — e.g.
|
|
78117
78499
|
* `git show <ref>:<path>` for a baseline tree, or `git show :<path>` for the
|
|
78118
78500
|
* index) into a temp tree that mirrors the project layout, copying the
|
|
@@ -78148,7 +78530,7 @@ const materializeSourceTree = (input) => gen(function* () {
|
|
|
78148
78530
|
const sourcePath = path.join(input.directory, subdirectory, configFilename);
|
|
78149
78531
|
const targetPath = path.resolve(resolvedTempDirectory, subdirectory, configFilename);
|
|
78150
78532
|
if (!isPathInsideDirectory(targetPath, resolvedTempDirectory)) continue;
|
|
78151
|
-
if (
|
|
78533
|
+
if (isFile(sourcePath) && !fs.existsSync(targetPath)) {
|
|
78152
78534
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
78153
78535
|
fs.cpSync(sourcePath, targetPath);
|
|
78154
78536
|
}
|
|
@@ -78400,6 +78782,7 @@ const buildJsonReport = (input) => {
|
|
|
78400
78782
|
directory: input.directory,
|
|
78401
78783
|
diff: toJsonDiff(input.diff),
|
|
78402
78784
|
projects,
|
|
78785
|
+
...input.skippedProjects && input.skippedProjects.length > 0 ? { skippedProjects: [...input.skippedProjects] } : {},
|
|
78403
78786
|
diagnostics: flattenedDiagnostics,
|
|
78404
78787
|
summary,
|
|
78405
78788
|
elapsedMilliseconds: input.totalElapsedMilliseconds,
|
|
@@ -78424,7 +78807,7 @@ const buildSkippedChecks = (input) => {
|
|
|
78424
78807
|
else if (input.lintPartialFailures.length > 0) skippedCheckReasons["lint:partial"] = input.lintPartialFailures.join("; ");
|
|
78425
78808
|
if (input.didDeadCodeFail && input.deadCodeFailureReason !== null) skippedCheckReasons["dead-code"] = input.deadCodeFailureReason;
|
|
78426
78809
|
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.";
|
|
78810
|
+
if (input.securityScanFailed) skippedCheckReasons["security-scan"] = input.securityScanFailureReason ?? "Security scan failed and was skipped.";
|
|
78428
78811
|
return {
|
|
78429
78812
|
skippedChecks,
|
|
78430
78813
|
skippedCheckReasons
|
|
@@ -78564,6 +78947,33 @@ const getChangedLineRanges = (input) => runPromise(gen(function* () {
|
|
|
78564
78947
|
* instead of being improvised per diagnostic.
|
|
78565
78948
|
*/
|
|
78566
78949
|
const buildRuleDocsUrl = (plugin, rule) => `${DOCS_RULES_BASE_URL}/${plugin}/${rule}`;
|
|
78950
|
+
const collectSourceFileCountsByDirectory = async (rootDirectory, projectDirectories, signal) => {
|
|
78951
|
+
const resolvedRootDirectory = Path.resolve(rootDirectory);
|
|
78952
|
+
const sourceFileCounts = /* @__PURE__ */ new Map();
|
|
78953
|
+
const projectDirectoryByRelativePath = /* @__PURE__ */ new Map();
|
|
78954
|
+
for (const projectDirectory of projectDirectories) {
|
|
78955
|
+
const resolvedProjectDirectory = Path.resolve(projectDirectory);
|
|
78956
|
+
const relativeDirectory = Path.relative(resolvedRootDirectory, resolvedProjectDirectory);
|
|
78957
|
+
if (Path.isAbsolute(relativeDirectory) || relativeDirectory.startsWith("..")) continue;
|
|
78958
|
+
sourceFileCounts.set(resolvedProjectDirectory, 0);
|
|
78959
|
+
projectDirectoryByRelativePath.set(relativeDirectory.replaceAll(Path.sep, "/"), resolvedProjectDirectory);
|
|
78960
|
+
}
|
|
78961
|
+
const sourceFilePaths = await listSourceFilesCooperative(resolvedRootDirectory, signal);
|
|
78962
|
+
for (const sourceFilePath of sourceFilePaths) {
|
|
78963
|
+
let relativeDirectory = Path.posix.dirname(sourceFilePath);
|
|
78964
|
+
while (true) {
|
|
78965
|
+
const normalizedRelativeDirectory = relativeDirectory === "." ? "" : relativeDirectory;
|
|
78966
|
+
const projectDirectory = projectDirectoryByRelativePath.get(normalizedRelativeDirectory);
|
|
78967
|
+
if (projectDirectory !== void 0) {
|
|
78968
|
+
sourceFileCounts.set(projectDirectory, (sourceFileCounts.get(projectDirectory) ?? 0) + 1);
|
|
78969
|
+
break;
|
|
78970
|
+
}
|
|
78971
|
+
if (relativeDirectory === ".") break;
|
|
78972
|
+
relativeDirectory = Path.posix.dirname(relativeDirectory);
|
|
78973
|
+
}
|
|
78974
|
+
}
|
|
78975
|
+
return sourceFileCounts;
|
|
78976
|
+
};
|
|
78567
78977
|
const computeConfigFingerprint = (projectDirectory, version) => {
|
|
78568
78978
|
const parts = [`v=${version}`];
|
|
78569
78979
|
let directory = projectDirectory;
|
|
@@ -78583,59 +78993,34 @@ const createOxlintSpawnSlots = (concurrency) => createWorkerSlots({
|
|
|
78583
78993
|
slotCount: resolveScanConcurrency(concurrency),
|
|
78584
78994
|
createAbortError: createLintPhaseAbortError
|
|
78585
78995
|
});
|
|
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"],
|
|
78996
|
+
const AI_TRAINING_BY_ENVIRONMENT_VARIABLE = [
|
|
78595
78997
|
["WANDB_RUN_ID", "wandb"],
|
|
78596
78998
|
["WANDB_SWEEP_ID", "wandb"],
|
|
78597
78999
|
["MLFLOW_RUN_ID", "mlflow"],
|
|
78598
|
-
["MLFLOW_TRACKING_URI", "mlflow"],
|
|
78599
79000
|
["COMET_EXPERIMENT_KEY", "comet"],
|
|
78600
79001
|
["NEPTUNE_RUN_ID", "neptune"],
|
|
78601
79002
|
["CLEARML_TASK_ID", "clearml"],
|
|
78602
79003
|
["DVC_STAGE", "dvc"],
|
|
78603
79004
|
["RAY_WORKER_PROCESS", "ray"],
|
|
78604
|
-
["RAY_ADDRESS", "ray"],
|
|
78605
|
-
["MUJOCO_GL", "mujoco"],
|
|
78606
|
-
["MUJOCO_PATH", "mujoco"],
|
|
78607
|
-
["GYM_DISABLE_ENV_CHECKER", "gymnasium"],
|
|
78608
79005
|
["SM_TRAINING_ENV", "sagemaker"],
|
|
78609
79006
|
["TRAINING_JOB_ARN", "sagemaker"],
|
|
78610
|
-
["SAGEMAKER_BASE_DIR", "sagemaker"],
|
|
78611
79007
|
["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
79008
|
["FLYTE_INTERNAL_EXECUTION_ID", "flyte"],
|
|
78618
|
-
["ARGO_WORKFLOW_NAME", "argo-workflows"],
|
|
78619
79009
|
["KFP_POD_NAME", "kubeflow-pipelines"],
|
|
78620
79010
|
["KAGGLE_KERNEL_RUN_TYPE", "kaggle"],
|
|
78621
79011
|
["COLAB_BACKEND_VERSION", "google-colab"],
|
|
78622
|
-
["DATABRICKS_RUNTIME_VERSION", "databricks"],
|
|
78623
79012
|
["DAYTONA_WS_ID", "daytona"],
|
|
78624
|
-
["DAYTONA_WS_NAME", "daytona"],
|
|
78625
79013
|
["E2B_SANDBOX_ID", "e2b"],
|
|
78626
79014
|
["MODAL_FUNCTION_ID", "modal"],
|
|
78627
79015
|
["MODAL_TASK_ID", "modal"],
|
|
78628
79016
|
["RUNPOD_POD_ID", "runpod"],
|
|
78629
|
-
["REPLICATE_USERNAME", "replicate"],
|
|
78630
79017
|
["VAST_CONTAINERLABEL", "vast-ai"],
|
|
78631
|
-
["HARBOR_URL", "harbor"],
|
|
78632
|
-
["HARBOR_HOSTNAME", "harbor"],
|
|
78633
79018
|
["SWE_BENCH_TASK", "swe-bench"],
|
|
78634
79019
|
["SWEBENCH_TASK", "swe-bench"],
|
|
78635
79020
|
["SWE_AGENT_MODEL", "swe-agent"]
|
|
78636
79021
|
];
|
|
78637
|
-
const detectAiTrainingEnvironment = () => {
|
|
78638
|
-
for (const [
|
|
79022
|
+
const detectAiTrainingEnvironment = (environment = process.env) => {
|
|
79023
|
+
for (const [environmentVariable, label] of AI_TRAINING_BY_ENVIRONMENT_VARIABLE) if (environment[environmentVariable]?.trim()) return label;
|
|
78639
79024
|
return null;
|
|
78640
79025
|
};
|
|
78641
79026
|
const groupBy = (items, keyFn) => {
|
|
@@ -78663,16 +79048,17 @@ const groupBy = (items, keyFn) => {
|
|
|
78663
79048
|
* a 404. Gate the directive on this predicate.
|
|
78664
79049
|
*/
|
|
78665
79050
|
const hasPublishedFixRecipe = (diagnostic) => diagnostic.plugin === "react-doctor" && Object.hasOwn(REACT_DOCTOR_RULE_REGISTRY, diagnostic.rule);
|
|
78666
|
-
const
|
|
79051
|
+
const stringValues = (values) => Array.isArray(values) ? values.filter((value) => typeof value === "string") : [];
|
|
79052
|
+
const unionValues = (baseValues, overrideValues) => [...new Set([...stringValues(baseValues), ...stringValues(overrideValues)])];
|
|
78667
79053
|
const mergeIgnores = (baseIgnore, overrideIgnore) => {
|
|
78668
79054
|
const mergedIgnore = {
|
|
78669
79055
|
...baseIgnore,
|
|
78670
79056
|
...overrideIgnore
|
|
78671
79057
|
};
|
|
78672
|
-
if (baseIgnore.rules
|
|
78673
|
-
if (baseIgnore.files
|
|
78674
|
-
if (baseIgnore.tags
|
|
78675
|
-
if (baseIgnore.overrides
|
|
79058
|
+
if (baseIgnore.rules !== void 0 || overrideIgnore.rules !== void 0) mergedIgnore.rules = unionValues(baseIgnore.rules, overrideIgnore.rules);
|
|
79059
|
+
if (baseIgnore.files !== void 0 || overrideIgnore.files !== void 0) mergedIgnore.files = unionValues(baseIgnore.files, overrideIgnore.files);
|
|
79060
|
+
if (baseIgnore.tags !== void 0 || overrideIgnore.tags !== void 0) mergedIgnore.tags = unionValues(baseIgnore.tags, overrideIgnore.tags);
|
|
79061
|
+
if (baseIgnore.overrides !== void 0 || overrideIgnore.overrides !== void 0) mergedIgnore.overrides = [...Array.isArray(baseIgnore.overrides) ? baseIgnore.overrides : [], ...Array.isArray(overrideIgnore.overrides) ? overrideIgnore.overrides : []];
|
|
78676
79062
|
return mergedIgnore;
|
|
78677
79063
|
};
|
|
78678
79064
|
/**
|
|
@@ -78867,6 +79253,21 @@ const NON_INTERACTIVE_ENVIRONMENT_VARIABLES = [
|
|
|
78867
79253
|
];
|
|
78868
79254
|
const isNonInteractiveEnvironment = () => NON_INTERACTIVE_ENVIRONMENT_VARIABLES.some((envVariable) => Boolean(process.env[envVariable])) || isCodingAgentEnvironment();
|
|
78869
79255
|
//#endregion
|
|
79256
|
+
//#region src/cli/utils/cli-input-error.ts
|
|
79257
|
+
/**
|
|
79258
|
+
* A mistake in *how the CLI was invoked* — a malformed `<file>:<line>`
|
|
79259
|
+
* argument, mutually exclusive flags, or an unknown `--project` name. This is
|
|
79260
|
+
* user input, not a react-doctor bug, so `isExpectedUserError` routes it
|
|
79261
|
+
* through `handleUserError`: a clean, single-line message with no Sentry
|
|
79262
|
+
* report and no "Something went wrong, open a prefilled issue" block.
|
|
79263
|
+
*
|
|
79264
|
+
* The `message` is rendered verbatim, so it must read as a complete,
|
|
79265
|
+
* human-readable sentence that tells the user how to fix their invocation.
|
|
79266
|
+
*/
|
|
79267
|
+
var CliInputError = class extends Error {
|
|
79268
|
+
name = "CliInputError";
|
|
79269
|
+
};
|
|
79270
|
+
//#endregion
|
|
78870
79271
|
//#region src/cli/utils/constants.ts
|
|
78871
79272
|
const REACT_DOCTOR_CONFIG_PROJECT_NAME = "react-doctor";
|
|
78872
79273
|
const STAGED_FILES_TEMP_DIR_PREFIX = "react-doctor-staged-";
|
|
@@ -78911,7 +79312,7 @@ const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
|
|
|
78911
79312
|
const SCAN_RESULT_CACHE_FILENAME = "scan-cache.json";
|
|
78912
79313
|
const RUN_GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
78913
79314
|
const GH_DEFAULT_BRANCH_PROBE_TIMEOUT_MS = 5e3;
|
|
78914
|
-
const TUI_REPORT_COMPACT_MAX_ROWS =
|
|
79315
|
+
const TUI_REPORT_COMPACT_MAX_ROWS = 23;
|
|
78915
79316
|
const TUI_REPORT_DETAIL_WIDTH_FRACTION = .6;
|
|
78916
79317
|
const CI_TRUST_COMPANIES = "PayPal, Rippling, and Alibaba";
|
|
78917
79318
|
const WELCOME_EXPLANATION_HOLD_MS = 1e3;
|
|
@@ -78934,8 +79335,11 @@ const METRIC = {
|
|
|
78934
79335
|
scanPhaseDuration: "scan.phase_duration",
|
|
78935
79336
|
scanFiles: "scan.files",
|
|
78936
79337
|
scanScore: "scan.score",
|
|
79338
|
+
scanScoreRetry: "scan.score_retry",
|
|
78937
79339
|
scanClean: "scan.clean",
|
|
78938
79340
|
scanCheckSkipped: "scan.check_skipped",
|
|
79341
|
+
scanProjectSkipped: "scan.project_skipped",
|
|
79342
|
+
scanWorkspaceDeadCodeShared: "scan.workspace_deadcode_shared",
|
|
78939
79343
|
scanNoReactDetected: "scan.no_react_detected",
|
|
78940
79344
|
baselineDegraded: "baseline.degraded",
|
|
78941
79345
|
ruleFired: "rule.fired",
|
|
@@ -78962,9 +79366,14 @@ const METRIC = {
|
|
|
78962
79366
|
lspScanCompleted: "lsp.scan.completed",
|
|
78963
79367
|
lspScanDuration: "lsp.scan.duration",
|
|
78964
79368
|
lspScanDiagnostics: "lsp.scan.diagnostics",
|
|
78965
|
-
tuiActionMenuOpened: "tui.action_menu_opened",
|
|
78966
79369
|
tuiCompactReportShown: "tui.compact_report_shown",
|
|
78967
79370
|
tuiFindingNavigated: "tui.finding_navigated",
|
|
79371
|
+
tuiIssueStreamShown: "tui.issue_stream_shown",
|
|
79372
|
+
tuiProjectPathContextShown: "tui.project_path_context_shown",
|
|
79373
|
+
tuiProjectSelectInlineShown: "tui.project_select_inline_shown",
|
|
79374
|
+
tuiReportActionSelected: "tui.report_action_selected",
|
|
79375
|
+
tuiCancelled: "tui.cancelled",
|
|
79376
|
+
tuiScanInlineShown: "tui.scan_inline_shown",
|
|
78968
79377
|
tuiStackedReportCapped: "tui.stacked_report_capped",
|
|
78969
79378
|
aiTrainingWarningShown: "ai.training.warning_shown",
|
|
78970
79379
|
jsonOutUsed: "json.out_used"
|
|
@@ -79010,7 +79419,7 @@ const makeNoopConsole = () => ({
|
|
|
79010
79419
|
});
|
|
79011
79420
|
//#endregion
|
|
79012
79421
|
//#region src/cli/utils/version.ts
|
|
79013
|
-
const VERSION = "0.9.
|
|
79422
|
+
const VERSION = "0.9.4-dev.212b8b4";
|
|
79014
79423
|
//#endregion
|
|
79015
79424
|
//#region src/cli/utils/json-mode.ts
|
|
79016
79425
|
let context = null;
|
|
@@ -79054,9 +79463,14 @@ const enableJsonMode = ({ compact, directory, outputFile }) => {
|
|
|
79054
79463
|
directory,
|
|
79055
79464
|
startTime: performance$1.now(),
|
|
79056
79465
|
mode: "full",
|
|
79057
|
-
outputFile:
|
|
79466
|
+
outputFile: null
|
|
79058
79467
|
};
|
|
79059
79468
|
installSilentConsole();
|
|
79469
|
+
if (outputFile) {
|
|
79470
|
+
const resolvedOutputFile = Path.resolve(outputFile);
|
|
79471
|
+
if (NFS.existsSync(resolvedOutputFile) && !NFS.statSync(resolvedOutputFile).isFile()) throw new CliInputError(`--json-out must point to a file, not ${resolvedOutputFile}.`);
|
|
79472
|
+
context.outputFile = outputFile;
|
|
79473
|
+
}
|
|
79060
79474
|
};
|
|
79061
79475
|
const isJsonModeActive = () => context !== null;
|
|
79062
79476
|
const setJsonReportDirectory = (directory) => {
|
|
@@ -79247,6 +79661,104 @@ const buildSentryScope = (runContext = buildRunContext()) => {
|
|
|
79247
79661
|
};
|
|
79248
79662
|
};
|
|
79249
79663
|
//#endregion
|
|
79664
|
+
//#region src/cli/utils/is-env-flag-enabled.ts
|
|
79665
|
+
const isEnvFlagEnabled = (value) => value === "1" || value?.toLowerCase() === "true";
|
|
79666
|
+
//#endregion
|
|
79667
|
+
//#region src/cli/utils/is-environment-error.ts
|
|
79668
|
+
const ENVIRONMENT_ERROR_CODES = new Set([
|
|
79669
|
+
"EACCES",
|
|
79670
|
+
"EIO",
|
|
79671
|
+
"ENOSPC",
|
|
79672
|
+
"ENOTDIR",
|
|
79673
|
+
"EPERM",
|
|
79674
|
+
"EROFS"
|
|
79675
|
+
]);
|
|
79676
|
+
const ENVIRONMENT_FILESYSTEM_SYSCALLS = new Set([
|
|
79677
|
+
"lstat",
|
|
79678
|
+
"open",
|
|
79679
|
+
"read",
|
|
79680
|
+
"readdir",
|
|
79681
|
+
"readlink",
|
|
79682
|
+
"realpath",
|
|
79683
|
+
"scandir",
|
|
79684
|
+
"stat"
|
|
79685
|
+
]);
|
|
79686
|
+
const TRANSIENT_FILESYSTEM_ERROR_CODES = new Set([
|
|
79687
|
+
"EBUSY",
|
|
79688
|
+
"ETIMEDOUT",
|
|
79689
|
+
"UNKNOWN"
|
|
79690
|
+
]);
|
|
79691
|
+
const findEnvironmentError = (error) => {
|
|
79692
|
+
const pendingErrors = [error];
|
|
79693
|
+
const visitedErrors = /* @__PURE__ */ new Set();
|
|
79694
|
+
while (pendingErrors.length > 0) {
|
|
79695
|
+
const currentError = pendingErrors.pop();
|
|
79696
|
+
if (typeof currentError !== "object" || currentError === null) continue;
|
|
79697
|
+
if (visitedErrors.has(currentError)) continue;
|
|
79698
|
+
visitedErrors.add(currentError);
|
|
79699
|
+
if (isErrnoException(currentError) && typeof currentError.code === "string") {
|
|
79700
|
+
const syscall = currentError.syscall ?? "";
|
|
79701
|
+
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;
|
|
79702
|
+
}
|
|
79703
|
+
if (currentError instanceof Error && currentError.cause !== void 0) pendingErrors.push(currentError.cause);
|
|
79704
|
+
if (isReactDoctorError(currentError) && "cause" in currentError.reason) pendingErrors.push(currentError.reason.cause);
|
|
79705
|
+
}
|
|
79706
|
+
return null;
|
|
79707
|
+
};
|
|
79708
|
+
const isEnvironmentError = (error) => findEnvironmentError(error) !== null;
|
|
79709
|
+
const formatEnvironmentError = (error) => {
|
|
79710
|
+
const environmentError = findEnvironmentError(error);
|
|
79711
|
+
if (!environmentError) return messageFromUnknown(error);
|
|
79712
|
+
switch (environmentError.code) {
|
|
79713
|
+
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.";
|
|
79714
|
+
case "ENOSPC": return "No space left on device. Free up disk space and try again.";
|
|
79715
|
+
case "EIO": return "I/O error: the filesystem or disk may be failing. Check your system logs.";
|
|
79716
|
+
case "EROFS": return "Read-only filesystem: cannot write to this location.";
|
|
79717
|
+
case "EACCES":
|
|
79718
|
+
case "EPERM": return environmentError.path ? `Permission denied accessing ${environmentError.path}. Check file permissions and try again.` : "Permission denied. Check file permissions and try again.";
|
|
79719
|
+
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.";
|
|
79720
|
+
case "ENOENT": return "Required command not found. Ensure the tool (e.g. git) is installed and on your PATH.";
|
|
79721
|
+
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.";
|
|
79722
|
+
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.";
|
|
79723
|
+
default: return environmentError.message;
|
|
79724
|
+
}
|
|
79725
|
+
};
|
|
79726
|
+
//#endregion
|
|
79727
|
+
//#region src/cli/utils/is-expected-user-error.ts
|
|
79728
|
+
/**
|
|
79729
|
+
* Whether `error` is an expected, user-actionable failure — the user's project
|
|
79730
|
+
* or input, not a react-doctor bug. Such failures must be kept out of crash
|
|
79731
|
+
* reporting (Sentry + the alertable error-rate metric) and rendered via
|
|
79732
|
+
* `handleUserError` (a plain message — no "Something went wrong", prefilled
|
|
79733
|
+
* issue, Discord link, or Sentry reference), since there is no bug to report.
|
|
79734
|
+
*
|
|
79735
|
+
* Four distinct shapes reach the CLI's catch blocks:
|
|
79736
|
+
*
|
|
79737
|
+
* - **Project-discovery failures** (`NoReactDependencyError`,
|
|
79738
|
+
* `ProjectNotFoundError`, `PackageJsonNotFoundError`, `NotADirectoryError`,
|
|
79739
|
+
* `AmbiguousProjectError`) arrive as their plain legacy classes (so
|
|
79740
|
+
* `isReactDoctorError` is `false` for them) — narrow with
|
|
79741
|
+
* `isProjectDiscoveryError`. Running react-doctor against a directory that
|
|
79742
|
+
* has no React, or a path that doesn't exist, is the canonical example.
|
|
79743
|
+
* - **CLI invocation mistakes** (`CliInputError`): a malformed
|
|
79744
|
+
* `<file>:<line>` argument, mutually exclusive flags, or an unknown
|
|
79745
|
+
* `--project` name.
|
|
79746
|
+
* - **Bad `--diff` input** (`GitBaseBranchInvalid` / `GitBaseBranchMissing`)
|
|
79747
|
+
* stays the tagged `ReactDoctorError`, so dispatch on the reason `_tag`.
|
|
79748
|
+
* - **Environment failures** (`ENOSPC`, `EIO`, `EROFS`, `EACCES`, `EPERM`,
|
|
79749
|
+
* `ENOTDIR`, plus a `spawn`-scoped `ENOENT` for a missing binary) — disk
|
|
79750
|
+
* full / failing / read-only, permission denied, or a path blocked by a
|
|
79751
|
+
* file. React Doctor cannot fix the user's environment; exit cleanly with an
|
|
79752
|
+
* actionable message instead of crashing. See `is-environment-error.ts` for
|
|
79753
|
+
* why the set stays narrow (codes that usually mean our bug keep reaching
|
|
79754
|
+
* Sentry).
|
|
79755
|
+
*
|
|
79756
|
+
* This composes the existing core narrowers rather than introducing a new
|
|
79757
|
+
* error-shape helper (AGENTS.md): it encodes CLI-layer reporting policy, not
|
|
79758
|
+
* knowledge of the `ReactDoctorError` shape.
|
|
79759
|
+
*/
|
|
79760
|
+
const isExpectedUserError = (error) => error instanceof CliInputError || isProjectDiscoveryError(error) || isEnvironmentError(error) || isReactDoctorError(error) && (error.reason._tag === "GitBaseBranchInvalid" || error.reason._tag === "GitBaseBranchMissing");
|
|
79761
|
+
//#endregion
|
|
79250
79762
|
//#region src/cli/utils/anonymize-text.ts
|
|
79251
79763
|
/**
|
|
79252
79764
|
* Free-text fields can carry both a home-directory path (the OS username) and a
|
|
@@ -79368,13 +79880,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
79368
79880
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
79369
79881
|
* standard `SENTRY_RELEASE` override.
|
|
79370
79882
|
*/
|
|
79371
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.
|
|
79883
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.4-dev.212b8b4`;
|
|
79372
79884
|
/**
|
|
79373
79885
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
79374
79886
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
79375
79887
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
79376
79888
|
*/
|
|
79377
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.
|
|
79889
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.4-dev.212b8b4") ? "development" : "production");
|
|
79378
79890
|
/**
|
|
79379
79891
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
79380
79892
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -79397,7 +79909,6 @@ const shouldEnableSentry = () => {
|
|
|
79397
79909
|
if (process.env.VITEST || process.env.NODE_ENV === "test") return false;
|
|
79398
79910
|
return true;
|
|
79399
79911
|
};
|
|
79400
|
-
const isEnvFlagEnabled = (value) => value === "1" || value?.toLowerCase() === "true";
|
|
79401
79912
|
/**
|
|
79402
79913
|
* Whether performance traces will actually be recorded — Sentry is live and the
|
|
79403
79914
|
* resolved sample rate is above zero. Used to gate the per-run root span and
|
|
@@ -79440,21 +79951,25 @@ const initializeSentry = () => {
|
|
|
79440
79951
|
isInitialized = true;
|
|
79441
79952
|
resolvedTracesSampleRate = isDebugFlagEnabled() ? 1 : resolveTracesSampleRate();
|
|
79442
79953
|
const { tags, contexts } = buildSentryScope();
|
|
79443
|
-
|
|
79444
|
-
|
|
79445
|
-
|
|
79446
|
-
|
|
79447
|
-
|
|
79448
|
-
|
|
79449
|
-
|
|
79450
|
-
|
|
79451
|
-
|
|
79452
|
-
|
|
79453
|
-
|
|
79454
|
-
|
|
79455
|
-
|
|
79456
|
-
|
|
79457
|
-
|
|
79954
|
+
try {
|
|
79955
|
+
Sentry.init({
|
|
79956
|
+
dsn: process.env.SENTRY_DSN || "https://f253d570240a59b8dbd77b7a548ef133@o4510226365743104.ingest.us.sentry.io/4511487817809920",
|
|
79957
|
+
release: resolveSentryRelease(),
|
|
79958
|
+
environment: resolveSentryEnvironment(),
|
|
79959
|
+
sendDefaultPii: false,
|
|
79960
|
+
tracesSampleRate: resolvedTracesSampleRate,
|
|
79961
|
+
debug: isEnvFlagEnabled(process.env.SENTRY_DEBUG),
|
|
79962
|
+
initialScope: {
|
|
79963
|
+
tags,
|
|
79964
|
+
contexts
|
|
79965
|
+
},
|
|
79966
|
+
beforeSend: (event, hint) => isExpectedUserError(hint.originalException) ? null : scrubSentryEvent(event),
|
|
79967
|
+
beforeSendTransaction: (event) => scrubSentryEvent(event),
|
|
79968
|
+
beforeSendMetric: (metric) => scrubSentryMetric(metric)
|
|
79969
|
+
});
|
|
79970
|
+
} catch {
|
|
79971
|
+
resolvedTracesSampleRate = 0;
|
|
79972
|
+
}
|
|
79458
79973
|
};
|
|
79459
79974
|
//#endregion
|
|
79460
79975
|
//#region src/cli/utils/cli-logger.ts
|
|
@@ -83013,27 +83528,21 @@ const prompts = (questions, options = {}) => {
|
|
|
83013
83528
|
//#endregion
|
|
83014
83529
|
//#region src/cli/utils/to-span-attributes.ts
|
|
83015
83530
|
/**
|
|
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.
|
|
83531
|
+
* Converts a Sentry tag map (which permits `null`/`undefined` to denote an
|
|
83532
|
+
* absent signal) into Sentry/OTel span attributes, which only accept primitives.
|
|
83533
|
+
* Missing values are dropped rather than coerced, so an absent signal doesn't
|
|
83534
|
+
* become a misleading `"null"` or `"undefined"` attribute.
|
|
83020
83535
|
*/
|
|
83021
83536
|
const toSpanAttributes = (tags) => {
|
|
83022
83537
|
const attributes = {};
|
|
83023
|
-
for (const [key, value] of Object.entries(tags)) if (value
|
|
83538
|
+
for (const [key, value] of Object.entries(tags)) if (value != null) attributes[key] = value;
|
|
83024
83539
|
return attributes;
|
|
83025
83540
|
};
|
|
83026
83541
|
//#endregion
|
|
83027
83542
|
//#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
83543
|
const withRunAttributes = (attributes) => ({
|
|
83035
83544
|
...toSpanAttributes(buildSentryScope().tags),
|
|
83036
|
-
...
|
|
83545
|
+
...toSpanAttributes(attributes ?? {})
|
|
83037
83546
|
});
|
|
83038
83547
|
/**
|
|
83039
83548
|
* Emits a Sentry counter. A guarded, swallow-on-throw no-op unless the CLI's
|
|
@@ -90775,34 +91284,6 @@ const runCiConfig = async (options = {}) => {
|
|
|
90775
91284
|
});
|
|
90776
91285
|
};
|
|
90777
91286
|
//#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
91287
|
//#region src/cli/utils/handle-error.ts
|
|
90807
91288
|
const OTLP_ENDPOINT_ENVIRONMENT_VARIABLE = "REACT_DOCTOR_OTLP_ENDPOINT";
|
|
90808
91289
|
const OTLP_AUTH_HEADER_ENVIRONMENT_VARIABLE = "REACT_DOCTOR_OTLP_AUTH_HEADER";
|
|
@@ -90904,56 +91385,6 @@ const handleUserError = (error, options = {}) => {
|
|
|
90904
91385
|
process.exitCode = 1;
|
|
90905
91386
|
};
|
|
90906
91387
|
//#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
91388
|
//#region src/cli/utils/print-branded-header.ts
|
|
90958
91389
|
/**
|
|
90959
91390
|
* Single branded line every command prints first when not in JSON
|
|
@@ -91050,6 +91481,19 @@ const ciInstallAction = (options) => runCiCommand("install", runCiInstall, optio
|
|
|
91050
91481
|
const ciUpgradeAction = (options) => runCiCommand("upgrade", runCiUpgrade, options);
|
|
91051
91482
|
const ciConfigAction = (options) => runCiCommand("config", runCiConfig, options);
|
|
91052
91483
|
//#endregion
|
|
91484
|
+
//#region src/cli/utils/active-scan-abort-registry.ts
|
|
91485
|
+
const activeScanAbortControllers = /* @__PURE__ */ new Set();
|
|
91486
|
+
const activeScanAbortRegistry = {
|
|
91487
|
+
register: (controller) => {
|
|
91488
|
+
activeScanAbortControllers.add(controller);
|
|
91489
|
+
return () => activeScanAbortControllers.delete(controller);
|
|
91490
|
+
},
|
|
91491
|
+
abortAll: () => {
|
|
91492
|
+
for (const controller of activeScanAbortControllers) controller.abort();
|
|
91493
|
+
activeScanAbortControllers.clear();
|
|
91494
|
+
}
|
|
91495
|
+
};
|
|
91496
|
+
//#endregion
|
|
91053
91497
|
//#region src/cli/utils/sentry-tracer.ts
|
|
91054
91498
|
const toHrTime = (epochNanoseconds) => [Number(epochNanoseconds / NANOSECONDS_PER_SECOND), Number(epochNanoseconds % NANOSECONDS_PER_SECOND)];
|
|
91055
91499
|
const SPAN_KIND_TO_OTEL = {
|
|
@@ -91466,7 +91910,14 @@ const isInspectResultComplete = (result) => isScanComplete({
|
|
|
91466
91910
|
skippedCheckReasonCount: Object.keys(result.skippedCheckReasons ?? {}).length
|
|
91467
91911
|
});
|
|
91468
91912
|
//#endregion
|
|
91913
|
+
//#region src/cli/utils/resolve-score-unavailable-reason.ts
|
|
91914
|
+
const resolveScoreUnavailableReason = (input) => {
|
|
91915
|
+
if (input.isScoreDisabled) return "disabled";
|
|
91916
|
+
return input.isAnalysisIncomplete ? "analysis-incomplete" : "api-unavailable";
|
|
91917
|
+
};
|
|
91918
|
+
//#endregion
|
|
91469
91919
|
//#region src/cli/utils/scan-result-cache.ts
|
|
91920
|
+
const createScanResultCacheInvocationState = () => ({ repositoryIdentityByRoot: /* @__PURE__ */ new Map() });
|
|
91470
91921
|
const CACHE_DISABLED_VALUES = new Set(["1", "true"]);
|
|
91471
91922
|
const TOOLCHAIN_PACKAGE_SPECIFIERS = [
|
|
91472
91923
|
"oxlint/package.json",
|
|
@@ -91551,7 +92002,7 @@ const buildDirtyPathContentFingerprint = (absolutePath, statusCode) => {
|
|
|
91551
92002
|
if (stats.size > 10485760) return null;
|
|
91552
92003
|
return hashFileContents(absolutePath);
|
|
91553
92004
|
};
|
|
91554
|
-
const buildWorktreeFingerprint = (projectDirectory) => {
|
|
92005
|
+
const buildWorktreeFingerprint = (projectDirectory, repositoryRoot) => {
|
|
91555
92006
|
const statusOutput = runGit(projectDirectory, [
|
|
91556
92007
|
"status",
|
|
91557
92008
|
"--porcelain=v1",
|
|
@@ -91563,8 +92014,6 @@ const buildWorktreeFingerprint = (projectDirectory) => {
|
|
|
91563
92014
|
const statusEntries = parseWorktreeStatusRecords(statusOutput);
|
|
91564
92015
|
if (statusEntries === null) return null;
|
|
91565
92016
|
if (statusEntries.length > 300) return null;
|
|
91566
|
-
const repositoryRoot = runGit(projectDirectory, ["rev-parse", "--show-toplevel"]);
|
|
91567
|
-
if (repositoryRoot === null) return null;
|
|
91568
92017
|
const dirtyEntries = [];
|
|
91569
92018
|
for (const statusEntry of statusEntries) {
|
|
91570
92019
|
const contentFingerprint = buildDirtyPathContentFingerprint(path.join(repositoryRoot, statusEntry.path), statusEntry.statusCode);
|
|
@@ -91580,6 +92029,20 @@ const buildWorktreeFingerprint = (projectDirectory) => {
|
|
|
91580
92029
|
return firstSortKey < secondSortKey ? -1 : firstSortKey > secondSortKey ? 1 : 0;
|
|
91581
92030
|
});
|
|
91582
92031
|
};
|
|
92032
|
+
const resolveRepositoryCacheIdentity = (projectDirectory, invocationState) => {
|
|
92033
|
+
const repositoryRoot = runGit(projectDirectory, ["rev-parse", "--show-toplevel"]);
|
|
92034
|
+
if (repositoryRoot === null) return null;
|
|
92035
|
+
const cachedIdentity = invocationState.repositoryIdentityByRoot.get(repositoryRoot);
|
|
92036
|
+
if (cachedIdentity !== void 0) return cachedIdentity;
|
|
92037
|
+
const worktreeFingerprint = buildWorktreeFingerprint(projectDirectory, repositoryRoot);
|
|
92038
|
+
const headSha = readHeadSha(repositoryRoot);
|
|
92039
|
+
const identity = worktreeFingerprint === null || headSha === null ? null : {
|
|
92040
|
+
headSha,
|
|
92041
|
+
worktreeFingerprint
|
|
92042
|
+
};
|
|
92043
|
+
invocationState.repositoryIdentityByRoot.set(repositoryRoot, identity);
|
|
92044
|
+
return identity;
|
|
92045
|
+
};
|
|
91583
92046
|
const DOTENV_FILE_NAME_PATTERN = /^\.env(\.|$)/;
|
|
91584
92047
|
const resolveDotenvFingerprint = (projectDirectory) => {
|
|
91585
92048
|
try {
|
|
@@ -91674,16 +92137,14 @@ const resolveToolchainFingerprint = (nodeBinaryPath) => {
|
|
|
91674
92137
|
const buildScanResultCacheKey = (input) => {
|
|
91675
92138
|
if (isCacheGloballyDisabled()) return null;
|
|
91676
92139
|
if (!isGitIdentityTrustworthy(input.projectDirectory)) return null;
|
|
91677
|
-
const
|
|
91678
|
-
if (
|
|
91679
|
-
const headSha = readHeadSha(input.projectDirectory);
|
|
91680
|
-
if (headSha === null) return null;
|
|
92140
|
+
const repositoryIdentity = resolveRepositoryCacheIdentity(input.projectDirectory, input.invocationState);
|
|
92141
|
+
if (repositoryIdentity === null) return null;
|
|
91681
92142
|
if (stringifyStableJson(input.userConfig) === null) return null;
|
|
91682
92143
|
const cacheKeyJson = stringifyStableJson({
|
|
91683
92144
|
schemaVersion: 6,
|
|
91684
92145
|
projectIdentity: resolveProjectIdentity(input.projectDirectory),
|
|
91685
|
-
headSha,
|
|
91686
|
-
worktreeFingerprint,
|
|
92146
|
+
headSha: repositoryIdentity.headSha,
|
|
92147
|
+
worktreeFingerprint: repositoryIdentity.worktreeFingerprint,
|
|
91687
92148
|
dotenvFingerprint: resolveDotenvFingerprint(input.projectDirectory),
|
|
91688
92149
|
reactDoctorVersion: input.version,
|
|
91689
92150
|
nodeVersion: process.version,
|
|
@@ -91697,6 +92158,8 @@ const buildScanResultCacheKey = (input) => {
|
|
|
91697
92158
|
deadCode: input.options.deadCode,
|
|
91698
92159
|
supplyChain: input.options.supplyChain,
|
|
91699
92160
|
includePaths: [...input.options.includePaths].sort(),
|
|
92161
|
+
excludedProjectDirectories: [...input.options.excludedProjectDirectories ?? []].sort(),
|
|
92162
|
+
retainExcludedProjectDeadCodeDiagnostics: input.options.retainExcludedProjectDeadCodeDiagnostics ?? false,
|
|
91700
92163
|
customRulesOnly: input.options.customRulesOnly,
|
|
91701
92164
|
respectInlineDisables: input.options.respectInlineDisables,
|
|
91702
92165
|
warnings: input.options.warnings,
|
|
@@ -91774,8 +92237,8 @@ const toCategoryKey = (category) => category.toLowerCase().replace(/[^a-z0-9]+/g
|
|
|
91774
92237
|
* browser and stay group-/filter-/aggregate-able in the Spans dataset.
|
|
91775
92238
|
*
|
|
91776
92239
|
* 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).
|
|
92240
|
+
* numeric attribute and `p75(...)`/`avg(...)` work; `null`/`undefined` are kept
|
|
92241
|
+
* so `toSpanAttributes` can drop absent signals rather than coerce them).
|
|
91779
92242
|
*/
|
|
91780
92243
|
const withNamespace = (namespace, attributes) => {
|
|
91781
92244
|
const namespaced = {};
|
|
@@ -91841,7 +92304,7 @@ const buildCacheAttributes = (input) => {
|
|
|
91841
92304
|
const warmth = knownSubsystemCount > 0 ? reuseRatioSum / knownSubsystemCount : null;
|
|
91842
92305
|
const temperature = warmth !== null && warmth > 0 ? "warm" : isCacheGloballyDisabled() ? "disabled" : "cold";
|
|
91843
92306
|
return withNamespace("cache", {
|
|
91844
|
-
wholeRepoHit: input.wholeRepoCacheHit
|
|
92307
|
+
wholeRepoHit: input.wholeRepoCacheHit,
|
|
91845
92308
|
temperature,
|
|
91846
92309
|
warmth
|
|
91847
92310
|
});
|
|
@@ -91963,33 +92426,37 @@ const buildOutcomeAttributes = (input) => {
|
|
|
91963
92426
|
...withNamespace("score", {
|
|
91964
92427
|
value: result.score ? result.score.score : null,
|
|
91965
92428
|
label: result.score ? result.score.label : null,
|
|
91966
|
-
available: result.score !== null
|
|
92429
|
+
available: result.score !== null,
|
|
92430
|
+
unavailableReason: result.score === null ? resolveScoreUnavailableReason({
|
|
92431
|
+
isScoreDisabled: input.noScore,
|
|
92432
|
+
isAnalysisIncomplete: input.didLintFail === true || input.didDeadCodeFail === true
|
|
92433
|
+
}) : null
|
|
91967
92434
|
}),
|
|
91968
92435
|
...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
|
|
92436
|
+
failed: input.didLintFail,
|
|
92437
|
+
failureReasonKind: input.lintFailureReasonKind,
|
|
92438
|
+
partialFailureCount: input.lintPartialFailureCount,
|
|
92439
|
+
droppedFileCount: input.lintDroppedFileCount,
|
|
92440
|
+
deadlineSkippedFileCount: input.lintDeadlineSkippedFileCount,
|
|
92441
|
+
cacheHitFiles: result.lintCacheHitFileCount,
|
|
92442
|
+
cacheTotalFiles: result.lintCacheTotalFileCount,
|
|
91976
92443
|
cacheHitRatio: ratioOf(result.lintCacheHitFileCount, result.lintCacheTotalFileCount),
|
|
91977
|
-
sidecarReplayedFiles: result.lintSidecarReplayedFileCount
|
|
91978
|
-
sidecarTotalFiles: result.lintSidecarTotalFileCount
|
|
92444
|
+
sidecarReplayedFiles: result.lintSidecarReplayedFileCount,
|
|
92445
|
+
sidecarTotalFiles: result.lintSidecarTotalFileCount,
|
|
91979
92446
|
sidecarReplayRatio: ratioOf(result.lintSidecarReplayedFileCount, result.lintSidecarTotalFileCount)
|
|
91980
92447
|
}),
|
|
91981
92448
|
...withNamespace("deadCode", {
|
|
91982
|
-
failed: input.didDeadCodeFail
|
|
91983
|
-
overlapped: input.deadCodeOverlapped
|
|
91984
|
-
cacheHit: result.deadCodeCacheHit
|
|
91985
|
-
summaryCacheHits: result.deadCodeSummaryCacheHits
|
|
91986
|
-
summaryCacheMisses: result.deadCodeSummaryCacheMisses
|
|
92449
|
+
failed: input.didDeadCodeFail,
|
|
92450
|
+
overlapped: input.deadCodeOverlapped,
|
|
92451
|
+
cacheHit: result.deadCodeCacheHit,
|
|
92452
|
+
summaryCacheHits: result.deadCodeSummaryCacheHits,
|
|
92453
|
+
summaryCacheMisses: result.deadCodeSummaryCacheMisses
|
|
91987
92454
|
}),
|
|
91988
|
-
...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut
|
|
91989
|
-
...withNamespace("securityScan", { failed: input.securityScanFailed
|
|
92455
|
+
...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut }),
|
|
92456
|
+
...withNamespace("securityScan", { failed: input.securityScanFailed }),
|
|
91990
92457
|
...withNamespace("timing", {
|
|
91991
92458
|
elapsedMs: result.elapsedMilliseconds,
|
|
91992
|
-
scanMs: result.scanElapsedMilliseconds
|
|
92459
|
+
scanMs: result.scanElapsedMilliseconds
|
|
91993
92460
|
}),
|
|
91994
92461
|
...withNamespace("migration", {
|
|
91995
92462
|
largestRuleBucketFiles: largestRuleBucket ? largestRuleBucket.fileCount : null,
|
|
@@ -92001,7 +92468,7 @@ const buildOutcomeAttributes = (input) => {
|
|
|
92001
92468
|
new: summary.totalDiagnosticCount,
|
|
92002
92469
|
fixed: result.baselineDelta.fixedCount,
|
|
92003
92470
|
baseTotal: result.baselineDelta.baseTotalCount,
|
|
92004
|
-
crossFileMatches: result.baselineDelta.crossFileMatchCount
|
|
92471
|
+
crossFileMatches: result.baselineDelta.crossFileMatchCount,
|
|
92005
92472
|
degraded: false
|
|
92006
92473
|
}));
|
|
92007
92474
|
else if (input.gateExempt) attributes["baseline.degraded"] = true;
|
|
@@ -92010,7 +92477,7 @@ const buildOutcomeAttributes = (input) => {
|
|
|
92010
92477
|
const buildActionAttributes = () => {
|
|
92011
92478
|
const { githubActorAssociation } = resolveGithubActionsScoreMetadata();
|
|
92012
92479
|
return withNamespace("action", {
|
|
92013
|
-
actorAssociation: githubActorAssociation
|
|
92480
|
+
actorAssociation: githubActorAssociation,
|
|
92014
92481
|
runnerOs: detectRunnerOs(),
|
|
92015
92482
|
comment: readEnvBoolean(ACTION_INPUT_ENVIRONMENT_VARIABLES.comment),
|
|
92016
92483
|
reviewComments: readEnvBoolean(ACTION_INPUT_ENVIRONMENT_VARIABLES.reviewComments),
|
|
@@ -92024,7 +92491,7 @@ const buildScanAttributes = (input) => {
|
|
|
92024
92491
|
mode: input.mode,
|
|
92025
92492
|
scope: input.scope,
|
|
92026
92493
|
parallel: input.parallel,
|
|
92027
|
-
workerCount: input.workerCount
|
|
92494
|
+
workerCount: input.workerCount,
|
|
92028
92495
|
maxDurationMs: input.maxDurationMs,
|
|
92029
92496
|
lint: input.lint,
|
|
92030
92497
|
deadCode: input.deadCode,
|
|
@@ -92038,10 +92505,10 @@ const buildScanAttributes = (input) => {
|
|
|
92038
92505
|
hasCustomConfig: input.hasCustomConfig,
|
|
92039
92506
|
rulesConfigured: ruleKeys.length,
|
|
92040
92507
|
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
|
|
92508
|
+
fileCount: input.result?.scannedFileCount,
|
|
92509
|
+
nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length,
|
|
92510
|
+
htmlFileCount: input.result?.analyzedFiles?.filter((filePath) => HTML_FILE_PATTERN.test(filePath)).length,
|
|
92511
|
+
multilineDiagnosticCount: input.result?.diagnostics.filter((diagnostic) => (diagnostic.endLine ?? diagnostic.line) > diagnostic.line).length
|
|
92045
92512
|
});
|
|
92046
92513
|
};
|
|
92047
92514
|
/**
|
|
@@ -92053,11 +92520,11 @@ const buildScanAttributes = (input) => {
|
|
|
92053
92520
|
* tree up in Sentry's attribute browser and stay filter-/group-/aggregate-able
|
|
92054
92521
|
* in the Spans dataset. Pure and exported so the projection (outcome
|
|
92055
92522
|
* 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.
|
|
92523
|
+
* without a live Sentry client. `null`/`undefined` values are dropped so absent
|
|
92524
|
+
* signals never become misleading `"null"`/`"undefined"` attributes. The run +
|
|
92525
|
+
* project base context (version, command, ci/provider, framework, …) is already
|
|
92526
|
+
* on the span from `withSentryRunSpan` / `recordSentryProjectContext`, so this
|
|
92527
|
+
* adds only what those don't carry.
|
|
92061
92528
|
*/
|
|
92062
92529
|
const buildRunEventAttributes = (input) => toSpanAttributes({
|
|
92063
92530
|
...buildScanAttributes(input),
|
|
@@ -92531,8 +92998,35 @@ const createDiagnosticEvidenceReader = (rootDirectory, options = {}) => {
|
|
|
92531
92998
|
//#endregion
|
|
92532
92999
|
//#region src/cli/utils/build-no-score-message.ts
|
|
92533
93000
|
const ENTERPRISE_CONTACT_HINT = `Want something custom to your company? Contact us at ${ENTERPRISE_CONTACT_URL}.`;
|
|
92534
|
-
const buildNoScoreMessage = (
|
|
92535
|
-
|
|
93001
|
+
const buildNoScoreMessage = (input) => {
|
|
93002
|
+
const unavailableReason = resolveScoreUnavailableReason({
|
|
93003
|
+
isScoreDisabled: input.isScoreDisabled,
|
|
93004
|
+
isAnalysisIncomplete: input.isAnalysisIncomplete ?? false
|
|
93005
|
+
});
|
|
93006
|
+
let reason;
|
|
93007
|
+
switch (unavailableReason) {
|
|
93008
|
+
case "disabled":
|
|
93009
|
+
reason = input.disabledMessage ?? "Score disabled by --no-score.";
|
|
93010
|
+
break;
|
|
93011
|
+
case "analysis-incomplete":
|
|
93012
|
+
reason = "Score not shown because lint or dead-code analysis could not complete.";
|
|
93013
|
+
break;
|
|
93014
|
+
case "api-unavailable":
|
|
93015
|
+
reason = "Score unavailable (could not reach the score API).";
|
|
93016
|
+
break;
|
|
93017
|
+
}
|
|
93018
|
+
return `${reason} ${ENTERPRISE_CONTACT_HINT}`;
|
|
93019
|
+
};
|
|
93020
|
+
//#endregion
|
|
93021
|
+
//#region src/cli/utils/has-incomplete-score-analysis.ts
|
|
93022
|
+
const hasIncompleteScoreAnalysis = (skippedChecks) => skippedChecks.includes("lint") || skippedChecks.includes("dead-code");
|
|
93023
|
+
//#endregion
|
|
93024
|
+
//#region src/cli/utils/build-empty-report-message.ts
|
|
93025
|
+
const buildEmptyReportMessage = (input) => {
|
|
93026
|
+
const categoryFilters = [...input.categoryFilters];
|
|
93027
|
+
if (categoryFilters.length > 0) return `No issues found in category ${categoryFilters.join(", ")}!`;
|
|
93028
|
+
if (input.demotedDiagnosticCount > 0) return `No issues found! (${input.demotedDiagnosticCount} demoted from the ${input.outputSurface} surface — see config.surfaces.)`;
|
|
93029
|
+
return "No issues found!";
|
|
92536
93030
|
};
|
|
92537
93031
|
//#endregion
|
|
92538
93032
|
//#region src/cli/utils/render-agent-guidance.ts
|
|
@@ -93279,9 +93773,6 @@ const markOnboardingComplete = (options = {}) => {
|
|
|
93279
93773
|
if (isGatePending(ONBOARDING_GATE, {}, options)) recordGate(ONBOARDING_GATE, {}, options);
|
|
93280
93774
|
};
|
|
93281
93775
|
//#endregion
|
|
93282
|
-
//#region src/cli/utils/render-project-detection.ts
|
|
93283
|
-
const printProjectDetection = (_input) => void_;
|
|
93284
|
-
//#endregion
|
|
93285
93776
|
//#region src/cli/utils/colorize-by-score.ts
|
|
93286
93777
|
const colorizeByScore = (text, score) => {
|
|
93287
93778
|
if (score >= 75) return highlighter.success(text);
|
|
@@ -93495,6 +93986,26 @@ const printNoScoreHeader = (noScoreMessage) => gen(function* () {
|
|
|
93495
93986
|
yield* log("");
|
|
93496
93987
|
});
|
|
93497
93988
|
//#endregion
|
|
93989
|
+
//#region src/cli/utils/build-footer-description-lines.ts
|
|
93990
|
+
const FOOTER_DESCRIPTION_INDENT = " ";
|
|
93991
|
+
const buildFooterDescriptionLines = (description) => {
|
|
93992
|
+
return wrapTextToWidth(description, resolveMeasureWidth(2)).map((line) => highlighter.dim(`${FOOTER_DESCRIPTION_INDENT}${line}`));
|
|
93993
|
+
};
|
|
93994
|
+
//#endregion
|
|
93995
|
+
//#region src/cli/utils/build-footer-link-lines.ts
|
|
93996
|
+
const buildFooterLinkLines = ({ shareUrl }) => [
|
|
93997
|
+
...shareUrl === null ? [] : [
|
|
93998
|
+
` ${highlighter.bold("Share:")} ${highlighter.info(shareUrl)}`,
|
|
93999
|
+
...buildFooterDescriptionLines("Tell others how you did on socials"),
|
|
94000
|
+
""
|
|
94001
|
+
],
|
|
94002
|
+
` ${highlighter.bold("Docs:")} ${highlighter.info(DOCS_URL)}`,
|
|
94003
|
+
...buildFooterDescriptionLines("Learn more about fixing issues, setting up CI/CD, and configuring rules with a config file"),
|
|
94004
|
+
"",
|
|
94005
|
+
` ${highlighter.bold("GitHub:")} ${highlighter.info(CANONICAL_GITHUB_URL)}`,
|
|
94006
|
+
...buildFooterDescriptionLines("Report issues and star the repository!")
|
|
94007
|
+
];
|
|
94008
|
+
//#endregion
|
|
93498
94009
|
//#region src/cli/utils/write-diagnostics-directory.ts
|
|
93499
94010
|
const ruleDumpFileName = (ruleKey) => ruleKey.replace(/\//g, "--") + ".txt";
|
|
93500
94011
|
const readPreviousRuleDumpFileNames = (directory) => {
|
|
@@ -93515,11 +94026,6 @@ const writeDiagnosticsDirectory = (diagnostics, outputDirectory) => {
|
|
|
93515
94026
|
};
|
|
93516
94027
|
//#endregion
|
|
93517
94028
|
//#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
94029
|
const buildShareUrl = (diagnostics, scoreResult, projectName) => {
|
|
93524
94030
|
const errorCount = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
|
|
93525
94031
|
const warningCount = diagnostics.filter((diagnostic) => diagnostic.severity === "warning").length;
|
|
@@ -93536,17 +94042,8 @@ const printFooter = (input) => gen(function* () {
|
|
|
93536
94042
|
yield* log("");
|
|
93537
94043
|
yield* log(buildSectionDivider());
|
|
93538
94044
|
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!");
|
|
94045
|
+
const shareUrl = input.isOffline ? null : buildShareUrl(input.diagnostics, input.scoreResult, input.projectName);
|
|
94046
|
+
for (const line of buildFooterLinkLines({ shareUrl })) yield* log(line);
|
|
93550
94047
|
});
|
|
93551
94048
|
const printDiagnosticsDump = (diagnostics, outputDirectory, verbose, stream = "stdout") => gen(function* () {
|
|
93552
94049
|
const writtenDirectory = yield* try_({
|
|
@@ -93643,9 +94140,6 @@ const resolveCliCategories = (categoryFlag) => {
|
|
|
93643
94140
|
//#endregion
|
|
93644
94141
|
//#region src/inspect.ts
|
|
93645
94142
|
const silentConsole = makeNoopConsole();
|
|
93646
|
-
const runConsole = (effect) => {
|
|
93647
|
-
runSync(effect);
|
|
93648
|
-
};
|
|
93649
94143
|
const recordOnboardingCompletion = (options) => {
|
|
93650
94144
|
const forceOnboarding = isOnboardingForced();
|
|
93651
94145
|
if (shouldRecordOnboarding({
|
|
@@ -93655,7 +94149,6 @@ const recordOnboardingCompletion = (options) => {
|
|
|
93655
94149
|
isNonInteractiveEnvironment: options.isNonInteractiveEnvironment
|
|
93656
94150
|
})) markOnboardingComplete();
|
|
93657
94151
|
};
|
|
93658
|
-
const formatCategorySelection = (categoryFilters) => [...categoryFilters].join(", ");
|
|
93659
94152
|
const buildChangedLineMatcher = (directory, changedLineRanges) => {
|
|
93660
94153
|
const rangesByFile = /* @__PURE__ */ new Map();
|
|
93661
94154
|
for (const entry of changedLineRanges) rangesByFile.set(toForwardSlashes(entry.file), entry.ranges);
|
|
@@ -93705,7 +94198,10 @@ const mergeInspectOptions = (inputOptions, userConfig) => {
|
|
|
93705
94198
|
maxDurationMs: inputOptions.maxDurationMs ?? null,
|
|
93706
94199
|
baseline: inputOptions.baseline ?? null,
|
|
93707
94200
|
changedLineRanges: inputOptions.changedLineRanges ?? null,
|
|
93708
|
-
supplyChainManifestChanged: inputOptions.supplyChainManifestChanged ?? false
|
|
94201
|
+
supplyChainManifestChanged: inputOptions.supplyChainManifestChanged ?? false,
|
|
94202
|
+
excludedProjectDirectories: inputOptions.excludedProjectDirectories ?? [],
|
|
94203
|
+
retainExcludedProjectDeadCodeDiagnostics: inputOptions.retainExcludedProjectDeadCodeDiagnostics ?? false,
|
|
94204
|
+
precomputedSourceFileCount: inputOptions.precomputedSourceFileCount
|
|
93709
94205
|
};
|
|
93710
94206
|
};
|
|
93711
94207
|
const deriveScope = (options) => {
|
|
@@ -93738,13 +94234,14 @@ const inspectWithOxlintRuntime = async (directory, inputOptions, oxlintRuntime)
|
|
|
93738
94234
|
const deadlineEpochMs = inputOptions.deadlineEpochMs ?? (inputOptions.maxDurationMs != null ? Date.now() + inputOptions.maxDurationMs : null);
|
|
93739
94235
|
const isConcurrentScan = inputOptions.concurrentScan === true;
|
|
93740
94236
|
if (!isConcurrentScan) resetSentryRunState();
|
|
93741
|
-
const
|
|
94237
|
+
const configOverride = inputOptions.configOverride;
|
|
94238
|
+
const hasConfigOverride = configOverride !== void 0;
|
|
93742
94239
|
let scanDirectory;
|
|
93743
94240
|
let userConfig;
|
|
93744
94241
|
let configSourceDirectory;
|
|
93745
94242
|
if (hasConfigOverride) {
|
|
93746
94243
|
scanDirectory = directory;
|
|
93747
|
-
userConfig =
|
|
94244
|
+
userConfig = configOverride;
|
|
93748
94245
|
configSourceDirectory = inputOptions.configSourceDirectory ?? null;
|
|
93749
94246
|
} else {
|
|
93750
94247
|
const scanTarget = await resolveScanTarget(directory);
|
|
@@ -93777,11 +94274,22 @@ const inspectWithOxlintRuntime = async (directory, inputOptions, oxlintRuntime)
|
|
|
93777
94274
|
};
|
|
93778
94275
|
const createInvocationInspect = (requestedOxlintConcurrency) => {
|
|
93779
94276
|
const concurrency = resolveScanConcurrency(requestedOxlintConcurrency ?? runSync(OxlintConcurrency));
|
|
93780
|
-
const
|
|
93781
|
-
|
|
93782
|
-
|
|
94277
|
+
const spawnSlots = createOxlintSpawnSlots(concurrency);
|
|
94278
|
+
const scanResultCacheInvocationState = createScanResultCacheInvocationState();
|
|
94279
|
+
return async (directory, inputOptions = {}) => {
|
|
94280
|
+
const abortController = new AbortController();
|
|
94281
|
+
const unregisterAbortController = activeScanAbortRegistry.register(abortController);
|
|
94282
|
+
try {
|
|
94283
|
+
return await inspectWithOxlintRuntime(directory, inputOptions, {
|
|
94284
|
+
concurrency,
|
|
94285
|
+
spawnSlots,
|
|
94286
|
+
abortSignal: abortController.signal,
|
|
94287
|
+
scanResultCacheInvocationState
|
|
94288
|
+
});
|
|
94289
|
+
} finally {
|
|
94290
|
+
unregisterAbortController();
|
|
94291
|
+
}
|
|
93783
94292
|
};
|
|
93784
|
-
return (directory, inputOptions = {}) => inspectWithOxlintRuntime(directory, inputOptions, oxlintRuntime);
|
|
93785
94293
|
};
|
|
93786
94294
|
const inspect = async (directory, inputOptions = {}) => createInvocationInspect(inputOptions.concurrency)(directory, inputOptions);
|
|
93787
94295
|
const countIncompleteLintFiles = (lintPartialFailures) => countDroppedLintFiles(lintPartialFailures) + countDeadlineSkippedFiles(lintPartialFailures);
|
|
@@ -93794,12 +94302,27 @@ const countIncompleteLintFiles = (lintPartialFailures) => countDroppedLintFiles(
|
|
|
93794
94302
|
*/
|
|
93795
94303
|
const runBaselineComparison = async (params) => {
|
|
93796
94304
|
const tempDirectory = mkdtempSync(path.join(tmpdir(), BASELINE_FILES_TEMP_DIR_PREFIX));
|
|
94305
|
+
const baselineIncludePaths = filterPathsOutsideDirectories({
|
|
94306
|
+
rootDirectory: params.directory,
|
|
94307
|
+
relativePaths: params.options.includePaths,
|
|
94308
|
+
excludedDirectories: params.options.excludedProjectDirectories
|
|
94309
|
+
});
|
|
94310
|
+
const baselineBaseFiles = params.baseFiles ? filterPathsOutsideDirectories({
|
|
94311
|
+
rootDirectory: params.directory,
|
|
94312
|
+
relativePaths: params.baseFiles,
|
|
94313
|
+
excludedDirectories: params.options.excludedProjectDirectories
|
|
94314
|
+
}) : void 0;
|
|
94315
|
+
const baselineHeadFiles = params.headFiles ? filterPathsOutsideDirectories({
|
|
94316
|
+
rootDirectory: params.directory,
|
|
94317
|
+
relativePaths: params.headFiles,
|
|
94318
|
+
excludedDirectories: params.options.excludedProjectDirectories
|
|
94319
|
+
}) : void 0;
|
|
93797
94320
|
const snapshot = await materializeBaselineFiles({
|
|
93798
94321
|
directory: params.directory,
|
|
93799
94322
|
ref: params.baselineRef,
|
|
93800
|
-
files:
|
|
93801
|
-
baseFiles:
|
|
93802
|
-
headFiles:
|
|
94323
|
+
files: baselineIncludePaths,
|
|
94324
|
+
baseFiles: baselineBaseFiles,
|
|
94325
|
+
headFiles: baselineHeadFiles,
|
|
93803
94326
|
tempDirectory
|
|
93804
94327
|
}).catch((error) => {
|
|
93805
94328
|
rmSync(tempDirectory, {
|
|
@@ -93821,7 +94344,7 @@ const runBaselineComparison = async (params) => {
|
|
|
93821
94344
|
const baseFiles = new Set(snapshot.baseFiles.map(toForwardSlashes));
|
|
93822
94345
|
const trackedHeadFiles = new Set(snapshot.headFiles.map(toForwardSlashes));
|
|
93823
94346
|
const expectedHeadFiles = new Set(trackedHeadFiles);
|
|
93824
|
-
for (const filePath of
|
|
94347
|
+
for (const filePath of baselineIncludePaths) {
|
|
93825
94348
|
const normalizedFilePath = toForwardSlashes(filePath);
|
|
93826
94349
|
if (!baseFiles.has(normalizedFilePath)) expectedHeadFiles.add(normalizedFilePath);
|
|
93827
94350
|
}
|
|
@@ -93858,8 +94381,9 @@ const runBaselineComparison = async (params) => {
|
|
|
93858
94381
|
resolveLocalGithubViewerPermission: false,
|
|
93859
94382
|
suppressScanSummary: true,
|
|
93860
94383
|
supplyChainManifestChanged: params.options.supplyChainManifestChanged,
|
|
93861
|
-
deadlineEpochMs: params.deadlineEpochMs ?? void 0
|
|
93862
|
-
|
|
94384
|
+
deadlineEpochMs: params.deadlineEpochMs ?? void 0,
|
|
94385
|
+
signal: params.oxlintRuntime.abortSignal
|
|
94386
|
+
}, {}).pipe(provide(baseLayers), provideService$2(PerFileLintCacheEnabled, false), provideService$2(SidecarLintCacheEnabled, false), provideService$2(Console, silentConsole))), { signal: params.oxlintRuntime.abortSignal });
|
|
93863
94387
|
if (baseOutput.didLintFail || countIncompleteLintFiles(baseOutput.lintPartialFailures) > 0) return null;
|
|
93864
94388
|
const hasUnscannedUntrackedSourceFiles = filterSourceFiles(snapshot.untrackedFiles.map(toForwardSlashes)).some((filePath) => !analyzedHeadFiles.has(filePath));
|
|
93865
94389
|
const delta = computeDiagnosticDelta({
|
|
@@ -93887,6 +94411,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93887
94411
|
const isDiffMode = options.includePaths.length > 0;
|
|
93888
94412
|
const resolvedNodeBinaryPath = await resolveOxlintNode(options.lint, options.scoreOnly || options.silent);
|
|
93889
94413
|
const lintBindingMissing = options.lint && !resolvedNodeBinaryPath;
|
|
94414
|
+
await yieldToEventLoop();
|
|
93890
94415
|
const cacheKey = buildScanResultCacheKey({
|
|
93891
94416
|
projectDirectory: directory,
|
|
93892
94417
|
version: VERSION,
|
|
@@ -93894,19 +94419,14 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93894
94419
|
options,
|
|
93895
94420
|
userConfig,
|
|
93896
94421
|
hasConfigOverride,
|
|
93897
|
-
configSourceDirectory
|
|
94422
|
+
configSourceDirectory,
|
|
94423
|
+
invocationState: oxlintRuntime.scanResultCacheInvocationState
|
|
93898
94424
|
});
|
|
93899
94425
|
const scanResultCache = cacheKey === null ? null : createScanResultCache(directory);
|
|
93900
|
-
const cachedPayload = cacheKey === null ? null : scanResultCache
|
|
94426
|
+
const cachedPayload = cacheKey === null || scanResultCache === null ? null : scanResultCache.lookup(cacheKey);
|
|
93901
94427
|
if (cachedPayload) {
|
|
93902
94428
|
recordSentryProjectContext(cachedPayload.project, rootSentrySpan, { concurrentScan: options.concurrentScan });
|
|
93903
94429
|
recordCount(METRIC.projectDetected, 1);
|
|
93904
|
-
await renderCachedProjectDetection({
|
|
93905
|
-
payload: cachedPayload,
|
|
93906
|
-
options,
|
|
93907
|
-
userConfig,
|
|
93908
|
-
isDiffMode
|
|
93909
|
-
});
|
|
93910
94430
|
const baselineDegraded = Boolean(options.baseline) && isDiffMode && cachedPayload.baselineDelta === void 0;
|
|
93911
94431
|
const result = await renderAndRecordScan({
|
|
93912
94432
|
payload: cachedPayload,
|
|
@@ -93940,6 +94460,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93940
94460
|
});
|
|
93941
94461
|
const program = runInspect({
|
|
93942
94462
|
directory,
|
|
94463
|
+
precomputedSourceFileCount: options.precomputedSourceFileCount,
|
|
93943
94464
|
includePaths: options.includePaths,
|
|
93944
94465
|
customRulesOnly: options.customRulesOnly,
|
|
93945
94466
|
respectInlineDisables: options.respectInlineDisables,
|
|
@@ -93956,26 +94477,19 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93956
94477
|
resolveLocalGithubViewerPermission: !options.noScore,
|
|
93957
94478
|
suppressScanSummary: options.suppressRendering,
|
|
93958
94479
|
supplyChainManifestChanged: options.supplyChainManifestChanged,
|
|
93959
|
-
|
|
93960
|
-
|
|
93961
|
-
|
|
94480
|
+
deadlineEpochMs: deadlineEpochMs ?? void 0,
|
|
94481
|
+
signal: oxlintRuntime.abortSignal,
|
|
94482
|
+
excludedProjectDirectories: options.excludedProjectDirectories,
|
|
94483
|
+
retainExcludedProjectDeadCodeDiagnostics: options.retainExcludedProjectDeadCodeDiagnostics
|
|
94484
|
+
}, { beforeLint: (projectInfo) => sync(() => {
|
|
93962
94485
|
recordSentryProjectContext(projectInfo, rootSentrySpan, { concurrentScan: options.concurrentScan });
|
|
93963
94486
|
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
94487
|
}) });
|
|
93974
|
-
const output = await runPromise(restoreLegacyThrow(applyObservability(options.silent ? program.pipe(provide(layers), provideService$2(Console, silentConsole)) : program.pipe(provide(layers)), rootSentrySpan)));
|
|
94488
|
+
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
94489
|
const didLintFail = lintBindingMissing || output.didLintFail;
|
|
93976
94490
|
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
|
|
94491
|
+
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`)));
|
|
94492
|
+
else runSync(error$1(highlighter.error(lintFailureReason)));
|
|
93979
94493
|
let inspectDiagnostics = output.diagnostics;
|
|
93980
94494
|
let baselineDelta;
|
|
93981
94495
|
if (options.baseline && isDiffMode && !didLintFail && countIncompleteLintFiles(output.lintPartialFailures) === 0) {
|
|
@@ -94025,6 +94539,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
94025
94539
|
lintFailureReasonKind: lintBindingMissing ? "native-binding-missing" : output.lintFailureReasonKind,
|
|
94026
94540
|
supplyChainOverlapTimedOut: output.supplyChainOverlapTimedOut,
|
|
94027
94541
|
securityScanFailed: output.securityScanFailed,
|
|
94542
|
+
securityScanFailureReason: output.securityScanFailureReason,
|
|
94028
94543
|
suppressedRuleCounts: output.suppressedRuleCounts
|
|
94029
94544
|
};
|
|
94030
94545
|
if (cacheKey !== null && scanResultCache !== null && shouldStoreScanPayload(payload) && !baselineDegraded) scanResultCache.store(cacheKey, payload);
|
|
@@ -94050,16 +94565,6 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
94050
94565
|
return result;
|
|
94051
94566
|
};
|
|
94052
94567
|
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
94568
|
const renderAndRecordScan = async (input) => {
|
|
94064
94569
|
const result = await runPromise(runMaybeSilent(finalizeAndRender({
|
|
94065
94570
|
options: input.options,
|
|
@@ -94075,6 +94580,7 @@ const renderAndRecordScan = async (input) => {
|
|
|
94075
94580
|
deadCodeFailureReason: input.payload.deadCodeFailureReason,
|
|
94076
94581
|
supplyChainOverlapTimedOut: input.payload.supplyChainOverlapTimedOut,
|
|
94077
94582
|
securityScanFailed: input.payload.securityScanFailed ?? false,
|
|
94583
|
+
securityScanFailureReason: input.payload.securityScanFailureReason ?? null,
|
|
94078
94584
|
directory: input.payload.directory,
|
|
94079
94585
|
scannedFileCount: input.payload.scannedFileCount,
|
|
94080
94586
|
scannedFilePaths: input.payload.scannedFilePaths,
|
|
@@ -94126,7 +94632,7 @@ const renderAndRecordScan = async (input) => {
|
|
|
94126
94632
|
return result;
|
|
94127
94633
|
};
|
|
94128
94634
|
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;
|
|
94635
|
+
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
94636
|
const { skippedChecks, skippedCheckReasons } = buildSkippedChecks({
|
|
94131
94637
|
didLintFail,
|
|
94132
94638
|
lintFailureReason,
|
|
@@ -94134,10 +94640,15 @@ const finalizeAndRender = (input) => gen(function* () {
|
|
|
94134
94640
|
didDeadCodeFail,
|
|
94135
94641
|
deadCodeFailureReason,
|
|
94136
94642
|
supplyChainOverlapTimedOut,
|
|
94137
|
-
securityScanFailed
|
|
94643
|
+
securityScanFailed,
|
|
94644
|
+
securityScanFailureReason
|
|
94138
94645
|
});
|
|
94139
94646
|
const hasSkippedChecks = skippedChecks.length > 0;
|
|
94140
|
-
const noScoreMessage = buildNoScoreMessage(
|
|
94647
|
+
const noScoreMessage = buildNoScoreMessage({
|
|
94648
|
+
isScoreDisabled: options.noScore,
|
|
94649
|
+
isAnalysisIncomplete: hasIncompleteScoreAnalysis(skippedChecks),
|
|
94650
|
+
disabledMessage: options.scoreDisabledMessage
|
|
94651
|
+
});
|
|
94141
94652
|
const buildResult = () => ({
|
|
94142
94653
|
diagnostics: [...diagnostics],
|
|
94143
94654
|
score,
|
|
@@ -94183,9 +94694,11 @@ const finalizeAndRender = (input) => gen(function* () {
|
|
|
94183
94694
|
if (hasSkippedChecks) {
|
|
94184
94695
|
const skippedLabel = skippedChecks.join(" and ");
|
|
94185
94696
|
yield* warn$1(highlighter.warn(`No issues detected, but ${skippedLabel} checks failed — results are incomplete.`));
|
|
94186
|
-
} else
|
|
94187
|
-
|
|
94188
|
-
|
|
94697
|
+
} else yield* log(highlighter.success(buildEmptyReportMessage({
|
|
94698
|
+
categoryFilters: options.categoryFilters,
|
|
94699
|
+
demotedDiagnosticCount,
|
|
94700
|
+
outputSurface: options.outputSurface
|
|
94701
|
+
})));
|
|
94189
94702
|
yield* log("");
|
|
94190
94703
|
yield* pause;
|
|
94191
94704
|
if (hasSkippedChecks) {
|
|
@@ -94265,6 +94778,32 @@ const materializeStagedFiles = async (input) => {
|
|
|
94265
94778
|
};
|
|
94266
94779
|
};
|
|
94267
94780
|
//#endregion
|
|
94781
|
+
//#region src/cli/utils/deduplicate-project-scans.ts
|
|
94782
|
+
const deduplicateProjectScans = (projectScans) => {
|
|
94783
|
+
const uniqueProjectScans = [];
|
|
94784
|
+
const seenDirectories = /* @__PURE__ */ new Set();
|
|
94785
|
+
for (const projectScan of projectScans) {
|
|
94786
|
+
if (seenDirectories.has(projectScan.directory)) continue;
|
|
94787
|
+
seenDirectories.add(projectScan.directory);
|
|
94788
|
+
uniqueProjectScans.push(projectScan);
|
|
94789
|
+
}
|
|
94790
|
+
return uniqueProjectScans;
|
|
94791
|
+
};
|
|
94792
|
+
//#endregion
|
|
94793
|
+
//#region src/cli/utils/collect-project-source-file-counts.ts
|
|
94794
|
+
const collectProjectSourceFileCounts = async (rootDirectory, projectDirectories) => {
|
|
94795
|
+
const abortController = new AbortController();
|
|
94796
|
+
const unregisterAbortController = activeScanAbortRegistry.register(abortController);
|
|
94797
|
+
try {
|
|
94798
|
+
return await collectSourceFileCountsByDirectory(rootDirectory, projectDirectories, abortController.signal);
|
|
94799
|
+
} finally {
|
|
94800
|
+
unregisterAbortController();
|
|
94801
|
+
}
|
|
94802
|
+
};
|
|
94803
|
+
//#endregion
|
|
94804
|
+
//#region src/cli/utils/format-skipped-projects-message.ts
|
|
94805
|
+
const formatSkippedProjectsMessage = (projectCount) => `${projectCount} ${projectCount === 1 ? "project was" : "projects were"} skipped because the max scan duration was reached.`;
|
|
94806
|
+
//#endregion
|
|
94268
94807
|
//#region src/cli/utils/is-share-opted-out.ts
|
|
94269
94808
|
const isShareOptedOut = (scans, flagNoScore) => Boolean(flagNoScore) || scans.some((scan) => Boolean(scan.config?.noScore) || scan.config?.share === false);
|
|
94270
94809
|
//#endregion
|
|
@@ -94276,7 +94815,7 @@ const buildHandoffPayload = (input) => {
|
|
|
94276
94815
|
try {
|
|
94277
94816
|
outputDirectory = writeDiagnosticsDirectory([...input.diagnostics], input.outputDirectory);
|
|
94278
94817
|
} catch {}
|
|
94279
|
-
const lines = [`
|
|
94818
|
+
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
94819
|
topGroups.forEach(([ruleKey, ruleDiagnostics], index) => {
|
|
94281
94820
|
const representative = ruleDiagnostics[0];
|
|
94282
94821
|
const severityLabel = representative.severity === "error" ? "ERROR" : "WARN";
|
|
@@ -94304,7 +94843,7 @@ const buildHandoffPayload = (input) => {
|
|
|
94304
94843
|
const ruleSummaries = deferredMigrationBuckets.map((bucket) => `${bucket.title} (${bucket.fileCount} files)`).join(", ");
|
|
94305
94844
|
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
94845
|
}
|
|
94307
|
-
lines.push("
|
|
94846
|
+
lines.push("Stop after this pass. Summarize the remaining findings for a follow-up.");
|
|
94308
94847
|
return lines.join("\n");
|
|
94309
94848
|
};
|
|
94310
94849
|
//#endregion
|
|
@@ -94514,20 +95053,20 @@ const resolveOpenCommand = (url) => {
|
|
|
94514
95053
|
args: [url]
|
|
94515
95054
|
};
|
|
94516
95055
|
};
|
|
94517
|
-
const openUrl = (url) => {
|
|
95056
|
+
const openUrl = (url) => new Promise((resolve) => {
|
|
94518
95057
|
try {
|
|
94519
95058
|
const { command, args } = resolveOpenCommand(url);
|
|
94520
95059
|
const child = spawn(command, args, {
|
|
94521
95060
|
detached: true,
|
|
94522
95061
|
stdio: "ignore"
|
|
94523
95062
|
});
|
|
94524
|
-
child.
|
|
95063
|
+
child.once("spawn", () => resolve(true));
|
|
95064
|
+
child.once("error", () => resolve(false));
|
|
94525
95065
|
child.unref();
|
|
94526
|
-
return true;
|
|
94527
95066
|
} catch {
|
|
94528
|
-
|
|
95067
|
+
resolve(false);
|
|
94529
95068
|
}
|
|
94530
|
-
};
|
|
95069
|
+
});
|
|
94531
95070
|
//#endregion
|
|
94532
95071
|
//#region src/cli/utils/ask-add-to-github-actions.ts
|
|
94533
95072
|
const CI_YES_CHOICE = "ci-yes";
|
|
@@ -94568,7 +95107,7 @@ const askAddToGitHubActions = async (prompt = prompts) => {
|
|
|
94568
95107
|
if (ciChoice === void 0) return "cancel";
|
|
94569
95108
|
if (ciChoice === CI_YES_CHOICE) return "yes";
|
|
94570
95109
|
if (ciChoice === CI_NO_CHOICE) return "no";
|
|
94571
|
-
const opened = openUrl(GITHUB_ACTIONS_SETUP_URL);
|
|
95110
|
+
const opened = await openUrl(GITHUB_ACTIONS_SETUP_URL);
|
|
94572
95111
|
cliLogger.log(opened ? `Opened ${highlighter.info(GITHUB_ACTIONS_SETUP_URL)} in your browser.` : `Visit ${highlighter.info(GITHUB_ACTIONS_SETUP_URL)} to learn more.`);
|
|
94573
95112
|
cliLogger.break();
|
|
94574
95113
|
}
|
|
@@ -95665,7 +96204,7 @@ const runInstallReactDoctor = async (options = {}) => {
|
|
|
95665
96204
|
const shouldInstallGitHook = gitHookPath != null && (Boolean(options.yes) || !didSkipOptionalSetup && selectedSetupActions.includes(SETUP_OPTION_GIT_HOOK));
|
|
95666
96205
|
const shouldInstallAgentHooks = Boolean(options.agentHooks) || !didSkipOptionalSetup && selectedSetupActions.includes(SETUP_OPTION_AGENT_HOOKS);
|
|
95667
96206
|
if (!options.dryRun) {
|
|
95668
|
-
if (shouldInstallGitHook && gitHookTarget
|
|
96207
|
+
if (shouldInstallGitHook && gitHookTarget != null) installReactDoctorGitHookStep(gitHookTarget);
|
|
95669
96208
|
if (shouldInstallAgentHooks) installReactDoctorAgentHooksStep(projectRoot, selectedAgents);
|
|
95670
96209
|
}
|
|
95671
96210
|
if (options.dryRun) {
|
|
@@ -96283,6 +96822,27 @@ const resolveCliInspectOptions = (flags, userConfig) => {
|
|
|
96283
96822
|
};
|
|
96284
96823
|
};
|
|
96285
96824
|
//#endregion
|
|
96825
|
+
//#region src/cli/utils/resolve-workspace-dead-code-owner.ts
|
|
96826
|
+
const resolveWorkspaceDeadCodeOwner = (input) => {
|
|
96827
|
+
if (!input.isRootDeadCodeEnabled) return null;
|
|
96828
|
+
const resolvedRootDirectory = Path.resolve(input.rootDirectory);
|
|
96829
|
+
return input.projectDirectories.find((projectDirectory) => Path.resolve(projectDirectory) === resolvedRootDirectory) ?? null;
|
|
96830
|
+
};
|
|
96831
|
+
//#endregion
|
|
96832
|
+
//#region src/cli/utils/retry-missing-project-scores.ts
|
|
96833
|
+
const retryMissingProjectScores = async (projectScans) => mapWithConcurrency(projectScans, 64, async (projectScan) => {
|
|
96834
|
+
if (!(projectScan.result.score === null && !projectScan.isScoreDisabled && !hasIncompleteScoreAnalysis(projectScan.result.skippedChecks))) return projectScan;
|
|
96835
|
+
const score = await calculateScore(filterScansForSurface([projectScan], "score"));
|
|
96836
|
+
recordCount(METRIC.scanScoreRetry, 1, { succeeded: score !== null });
|
|
96837
|
+
return score === null ? projectScan : {
|
|
96838
|
+
...projectScan,
|
|
96839
|
+
result: {
|
|
96840
|
+
...projectScan.result,
|
|
96841
|
+
score
|
|
96842
|
+
}
|
|
96843
|
+
};
|
|
96844
|
+
});
|
|
96845
|
+
//#endregion
|
|
96286
96846
|
//#region src/cli/utils/resolve-project-relative-directory.ts
|
|
96287
96847
|
/**
|
|
96288
96848
|
* The scanned project's directory relative to the diff root, forward-slashed —
|
|
@@ -96343,6 +96903,16 @@ const resolveProjectChangedLineRanges = (rootDirectory, projectDirectory, ranges
|
|
|
96343
96903
|
});
|
|
96344
96904
|
};
|
|
96345
96905
|
//#endregion
|
|
96906
|
+
//#region src/cli/utils/resolve-project-scan.ts
|
|
96907
|
+
const resolveProjectScan = async (rootScanTarget, projectDirectory) => {
|
|
96908
|
+
const projectScanTarget = projectDirectory === rootScanTarget.resolvedDirectory ? rootScanTarget : await resolveScanTarget(projectDirectory, { allowAmbiguous: true });
|
|
96909
|
+
return {
|
|
96910
|
+
directory: projectScanTarget.resolvedDirectory,
|
|
96911
|
+
config: projectDirectory === rootScanTarget.resolvedDirectory ? rootScanTarget.userConfig : mergeReactDoctorConfigs(rootScanTarget.userConfig, projectScanTarget.userConfig ?? void 0),
|
|
96912
|
+
configSourceDirectory: projectScanTarget.userConfig?.plugins === void 0 ? rootScanTarget.configSourceDirectory : projectScanTarget.configSourceDirectory
|
|
96913
|
+
};
|
|
96914
|
+
};
|
|
96915
|
+
//#endregion
|
|
96346
96916
|
//#region src/cli/utils/build-diagnostic-issue-url.ts
|
|
96347
96917
|
const formatRuleIdentifier = (diagnostic) => `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
96348
96918
|
const buildDiagnosticIssueBody = (input) => {
|
|
@@ -96485,7 +97055,7 @@ const resolveExplainTargetDirectory = async (filePath, context) => {
|
|
|
96485
97055
|
if (context.projectFlag) {
|
|
96486
97056
|
const matchedDirectories = await selectProjects(context.resolvedDirectory, context.projectFlag, true);
|
|
96487
97057
|
if (matchedDirectories.length === 0) return context.resolvedDirectory;
|
|
96488
|
-
if (matchedDirectories.length > 1) throw new Error(
|
|
97058
|
+
if (matchedDirectories.length > 1) throw new Error(`why takes a single project; --project resolved to ${matchedDirectories.length} projects.`);
|
|
96489
97059
|
return matchedDirectories[0];
|
|
96490
97060
|
}
|
|
96491
97061
|
return findOwningProjectDirectory(context.resolvedDirectory, filePath);
|
|
@@ -96704,6 +97274,15 @@ const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
|
|
|
96704
97274
|
}
|
|
96705
97275
|
}));
|
|
96706
97276
|
};
|
|
97277
|
+
const reportSkippedProjects = (input) => {
|
|
97278
|
+
input.skippedProjects.sort((left, right) => left.directory.localeCompare(right.directory));
|
|
97279
|
+
if (input.skippedProjects.length === 0) return;
|
|
97280
|
+
recordCount(METRIC.scanProjectSkipped, input.skippedProjects.length, { reason: "max-duration" });
|
|
97281
|
+
if (!input.isQuiet) {
|
|
97282
|
+
cliLogger.warn(formatSkippedProjectsMessage(input.skippedProjects.length));
|
|
97283
|
+
cliLogger.break();
|
|
97284
|
+
}
|
|
97285
|
+
};
|
|
96707
97286
|
/**
|
|
96708
97287
|
* Post-scan finalization shared by the staged-arm and project-loop
|
|
96709
97288
|
* paths of `inspectAction`: emit the JSON report (when in JSON mode)
|
|
@@ -96718,7 +97297,7 @@ const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
|
|
|
96718
97297
|
*/
|
|
96719
97298
|
const finalizeScans = (input) => {
|
|
96720
97299
|
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);
|
|
97300
|
+
const baselineComputed = input.skippedProjects.length === 0 && input.completedScans.length > 0 && input.completedScans.every((scan) => scan.result.baselineDelta !== void 0);
|
|
96722
97301
|
const baselineDegraded = input.baselineIntended && !baselineComputed;
|
|
96723
97302
|
const mode = baselineDegraded ? "diff" : input.mode;
|
|
96724
97303
|
const isReactDetected = input.completedScans.some((scan) => hasReactRuntime(scan.result.project));
|
|
@@ -96739,6 +97318,7 @@ const finalizeScans = (input) => {
|
|
|
96739
97318
|
mode,
|
|
96740
97319
|
diff: input.diff,
|
|
96741
97320
|
scans: jsonCompletedScans,
|
|
97321
|
+
skippedProjects: input.skippedProjects,
|
|
96742
97322
|
totalElapsedMilliseconds: performance$1.now() - input.startTime,
|
|
96743
97323
|
baseline,
|
|
96744
97324
|
baselineDegraded
|
|
@@ -96777,16 +97357,16 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
96777
97357
|
const requestedDirectory = Path.resolve(directory);
|
|
96778
97358
|
const startTime = performance$1.now();
|
|
96779
97359
|
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
97360
|
try {
|
|
97361
|
+
if (isJsonMode) {
|
|
97362
|
+
enableJsonMode({
|
|
97363
|
+
compact: Boolean(flags.jsonCompact),
|
|
97364
|
+
directory: requestedDirectory,
|
|
97365
|
+
outputFile: flags.jsonOut
|
|
97366
|
+
});
|
|
97367
|
+
if (flags.jsonOut) recordCount(METRIC.jsonOutUsed, 1);
|
|
97368
|
+
}
|
|
97369
|
+
recordCount(METRIC.cliInvoked, 1, { command: invocationCommand });
|
|
96790
97370
|
validateModeFlags(flags);
|
|
96791
97371
|
if (flags.staged) setJsonReportMode("staged");
|
|
96792
97372
|
await maybeMigrateLegacyConfig(requestedDirectory, {
|
|
@@ -96859,16 +97439,16 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
96859
97439
|
return;
|
|
96860
97440
|
}
|
|
96861
97441
|
const buildProjectScanContext = async (projectDirectory) => {
|
|
96862
|
-
const
|
|
96863
|
-
const scanDirectory =
|
|
97442
|
+
const projectScan = await resolveProjectScan(scanTarget, projectDirectory);
|
|
97443
|
+
const scanDirectory = projectScan.directory;
|
|
96864
97444
|
const treeRelativeDirectory = resolveProjectRelativeDirectory(resolvedDirectory, scanDirectory);
|
|
96865
97445
|
if (treeRelativeDirectory === null) return null;
|
|
96866
97446
|
return {
|
|
96867
97447
|
projectDirectory,
|
|
96868
97448
|
scanDirectory,
|
|
96869
97449
|
treeRelativeDirectory,
|
|
96870
|
-
projectConfig:
|
|
96871
|
-
projectConfigSourceDirectory:
|
|
97450
|
+
projectConfig: projectScan.config,
|
|
97451
|
+
projectConfigSourceDirectory: projectScan.configSourceDirectory
|
|
96872
97452
|
};
|
|
96873
97453
|
};
|
|
96874
97454
|
const projectScanContexts = [];
|
|
@@ -96955,6 +97535,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
96955
97535
|
includePaths: resolveProjectSourceFilePaths(resolvedDirectory, projectScan.scanDirectory, projectScan.stagedFiles.filter((stagedFile) => materializedStagedFiles.has(stagedFile)))
|
|
96956
97536
|
})).filter((projectRun) => projectRun.includePaths.length > 0);
|
|
96957
97537
|
const isMultiProject = stagedProjectRuns.length > 1;
|
|
97538
|
+
const skippedProjects = [];
|
|
96958
97539
|
if (stagedProjectRuns.length === 0) {
|
|
96959
97540
|
snapshot.cleanup();
|
|
96960
97541
|
reportNothingToScan({
|
|
@@ -96970,6 +97551,13 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
96970
97551
|
}
|
|
96971
97552
|
const scanStagedProject = async (projectRun) => {
|
|
96972
97553
|
const { projectScan, includePaths } = projectRun;
|
|
97554
|
+
if (scanDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(scanDeadlineEpochMs) === 0) {
|
|
97555
|
+
skippedProjects.push({
|
|
97556
|
+
directory: projectScan.scanDirectory,
|
|
97557
|
+
reason: "max-duration"
|
|
97558
|
+
});
|
|
97559
|
+
return null;
|
|
97560
|
+
}
|
|
96973
97561
|
const projectTempDirectory = Path.join(snapshot.tempDirectory, projectScan.treeRelativeDirectory);
|
|
96974
97562
|
const scanResult = await inspectProject(projectTempDirectory, {
|
|
96975
97563
|
...scanOptions,
|
|
@@ -97010,7 +97598,11 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97010
97598
|
});
|
|
97011
97599
|
const completedScans = stagedBatch.completedScans;
|
|
97012
97600
|
try {
|
|
97013
|
-
|
|
97601
|
+
reportSkippedProjects({
|
|
97602
|
+
skippedProjects,
|
|
97603
|
+
isQuiet
|
|
97604
|
+
});
|
|
97605
|
+
if (!isQuiet && isMultiProject && completedScans.length > 0) {
|
|
97014
97606
|
const showShareLink = !isShareOptedOut(completedScans, scanOptions.noScore) && !scanOptions.isCi;
|
|
97015
97607
|
await runPromise(printMultiProjectSummary({
|
|
97016
97608
|
completedScans,
|
|
@@ -97028,6 +97620,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97028
97620
|
}
|
|
97029
97621
|
finalizeScans({
|
|
97030
97622
|
completedScans,
|
|
97623
|
+
skippedProjects,
|
|
97031
97624
|
mode: "staged",
|
|
97032
97625
|
diff: null,
|
|
97033
97626
|
baselineIntended: false,
|
|
@@ -97103,12 +97696,28 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97103
97696
|
}
|
|
97104
97697
|
cliLogger.break();
|
|
97105
97698
|
}
|
|
97106
|
-
const
|
|
97107
|
-
const
|
|
97108
|
-
|
|
97109
|
-
|
|
97110
|
-
|
|
97111
|
-
|
|
97699
|
+
const projectScans = deduplicateProjectScans(await Promise.all(projectDirectories.map((projectDirectory) => resolveProjectScan(scanTarget, projectDirectory))));
|
|
97700
|
+
const isMultiProject = projectScans.length > 1;
|
|
97701
|
+
const rootProjectScan = projectScans.find((projectScan) => Path.resolve(projectScan.directory) === Path.resolve(resolvedDirectory));
|
|
97702
|
+
const workspaceDeadCodeOwner = resolveWorkspaceDeadCodeOwner({
|
|
97703
|
+
rootDirectory: resolvedDirectory,
|
|
97704
|
+
projectDirectories: projectScans.map((projectScan) => projectScan.directory),
|
|
97705
|
+
isRootDeadCodeEnabled: scanOptions.deadCode ?? rootProjectScan?.config?.deadCode ?? true
|
|
97706
|
+
});
|
|
97707
|
+
if (workspaceDeadCodeOwner !== null) recordCount(METRIC.scanWorkspaceDeadCodeShared, 1, { projectCount: projectScans.length });
|
|
97708
|
+
const precomputedSourceFileCounts = isMultiProject && !isDiffMode ? await collectProjectSourceFileCounts(resolvedDirectory, projectScans.map((projectScan) => projectScan.directory)) : null;
|
|
97709
|
+
const skippedProjects = [];
|
|
97710
|
+
const scanProject = async (projectScan) => {
|
|
97711
|
+
if (scanDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(scanDeadlineEpochMs) === 0) {
|
|
97712
|
+
skippedProjects.push({
|
|
97713
|
+
directory: projectScan.directory,
|
|
97714
|
+
reason: "max-duration"
|
|
97715
|
+
});
|
|
97716
|
+
return null;
|
|
97717
|
+
}
|
|
97718
|
+
const scanDirectory = projectScan.directory;
|
|
97719
|
+
const projectConfig = projectScan.config;
|
|
97720
|
+
const ownsWorkspaceDeadCode = scanDirectory === workspaceDeadCodeOwner;
|
|
97112
97721
|
const supplyChainEnabled = flags.supplyChain ?? projectConfig?.supplyChain?.enabled !== false;
|
|
97113
97722
|
let includePaths;
|
|
97114
97723
|
let supplyChainManifestChanged = false;
|
|
@@ -97132,12 +97741,16 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97132
97741
|
if (!isQuiet && !isMultiProject) cliLogger.dim(" ");
|
|
97133
97742
|
const scanResult = await inspectProject(scanDirectory, {
|
|
97134
97743
|
...scanOptions,
|
|
97744
|
+
deadCode: workspaceDeadCodeOwner === null ? scanOptions.deadCode : ownsWorkspaceDeadCode,
|
|
97745
|
+
precomputedSourceFileCount: precomputedSourceFileCounts?.get(scanDirectory),
|
|
97135
97746
|
deadlineEpochMs: scanDeadlineEpochMs,
|
|
97136
97747
|
includePaths,
|
|
97137
97748
|
configOverride: projectConfig,
|
|
97138
|
-
configSourceDirectory:
|
|
97749
|
+
configSourceDirectory: projectScan.configSourceDirectory ?? void 0,
|
|
97139
97750
|
suppressRendering: isMultiProject,
|
|
97140
97751
|
concurrentScan: isMultiProject,
|
|
97752
|
+
excludedProjectDirectories: projectScans.filter((candidateProjectScan) => isPathInsideDirectory(candidateProjectScan.directory, scanDirectory)).map((candidateProjectScan) => candidateProjectScan.directory),
|
|
97753
|
+
retainExcludedProjectDeadCodeDiagnostics: ownsWorkspaceDeadCode,
|
|
97141
97754
|
baseline: baselineRef !== null && projectBaselineBaseFiles !== null && projectBaselineHeadFiles !== null ? {
|
|
97142
97755
|
ref: baselineRef,
|
|
97143
97756
|
baseFiles: projectBaselineBaseFiles,
|
|
@@ -97154,12 +97767,19 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97154
97767
|
};
|
|
97155
97768
|
};
|
|
97156
97769
|
const projectBatch = await runProjectScanBatch({
|
|
97157
|
-
projects:
|
|
97770
|
+
projects: projectScans,
|
|
97158
97771
|
isQuiet,
|
|
97159
97772
|
isSilent: scanOptions.silent === true,
|
|
97160
97773
|
scanProject
|
|
97161
97774
|
});
|
|
97162
|
-
const completedScans = projectBatch.completedScans
|
|
97775
|
+
const completedScans = await retryMissingProjectScores(projectBatch.completedScans.map((completedScan) => ({
|
|
97776
|
+
...completedScan,
|
|
97777
|
+
isScoreDisabled: scanOptions.noScore ?? completedScan.config?.noScore ?? false
|
|
97778
|
+
})));
|
|
97779
|
+
reportSkippedProjects({
|
|
97780
|
+
skippedProjects,
|
|
97781
|
+
isQuiet
|
|
97782
|
+
});
|
|
97163
97783
|
if (!isQuiet && isMultiProject && completedScans.length > 0) {
|
|
97164
97784
|
const showShareLink = !isShareOptedOut(completedScans, scanOptions.noScore) && !scanOptions.isCi;
|
|
97165
97785
|
await runPromise(printMultiProjectSummary({
|
|
@@ -97177,6 +97797,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97177
97797
|
if (flags.outputDir && !didScansWriteDump) await runPromise(printDiagnosticsDump(selectedSurfaceDiagnostics, flags.outputDir, false, isQuiet ? "stderr" : "stdout"));
|
|
97178
97798
|
finalizeScans({
|
|
97179
97799
|
completedScans,
|
|
97800
|
+
skippedProjects,
|
|
97180
97801
|
mode: baselineRef ? "baseline" : isDiffMode ? "diff" : "full",
|
|
97181
97802
|
diff: isDiffMode ? diffInfo : null,
|
|
97182
97803
|
baselineIntended: scope === "changed" && diffInfo !== null && !diffInfo.isCurrentChanges,
|
|
@@ -97261,6 +97882,56 @@ const installAction = async (options, command) => {
|
|
|
97261
97882
|
}
|
|
97262
97883
|
};
|
|
97263
97884
|
//#endregion
|
|
97885
|
+
//#region src/cli/utils/should-use-tui.ts
|
|
97886
|
+
const shouldUseTui = (input) => {
|
|
97887
|
+
if (input.isNonInteractiveEnvironment || !input.stdinIsTty || !input.stdoutIsTty || !input.supportsRawMode || input.nodeMajorVersion < 22 || input.terminalName === "dumb") return false;
|
|
97888
|
+
const flags = input.flags;
|
|
97889
|
+
let requiresStableConfiguredScope = false;
|
|
97890
|
+
if (input.userConfig?.scope !== void 0) requiresStableConfiguredScope = input.userConfig.scope !== "full";
|
|
97891
|
+
else if (input.userConfig?.diff !== void 0) requiresStableConfiguredScope = input.userConfig.diff !== false && input.userConfig.diff !== "false";
|
|
97892
|
+
else if (input.userConfig?.base !== void 0) requiresStableConfiguredScope = true;
|
|
97893
|
+
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);
|
|
97894
|
+
};
|
|
97895
|
+
//#endregion
|
|
97896
|
+
//#region src/cli/commands/scan.ts
|
|
97897
|
+
const runScanCommand = async (input) => {
|
|
97898
|
+
if (input.flags.cache === false) process.env.REACT_DOCTOR_NO_CACHE = "1";
|
|
97899
|
+
const nodeMajorVersion = Number(process.versions.node.split(".")[0]);
|
|
97900
|
+
const tuiEnvironment = {
|
|
97901
|
+
flags: input.flags,
|
|
97902
|
+
isNonInteractiveEnvironment: isNonInteractiveEnvironment(),
|
|
97903
|
+
nodeMajorVersion,
|
|
97904
|
+
stdinIsTty: process.stdin.isTTY === true,
|
|
97905
|
+
stdoutIsTty: process.stdout.isTTY === true,
|
|
97906
|
+
supportsRawMode: typeof process.stdin.setRawMode === "function",
|
|
97907
|
+
terminalName: process.env.TERM
|
|
97908
|
+
};
|
|
97909
|
+
if (!shouldUseTui(tuiEnvironment)) {
|
|
97910
|
+
await inspectAction(input.directory, input.flags, input.invocationCommand);
|
|
97911
|
+
return;
|
|
97912
|
+
}
|
|
97913
|
+
await runProjectMigrations(path.resolve(input.directory));
|
|
97914
|
+
const scanTarget = await resolveScanTarget(input.directory, { allowAmbiguous: true });
|
|
97915
|
+
if (!shouldUseTui({
|
|
97916
|
+
...tuiEnvironment,
|
|
97917
|
+
userConfig: scanTarget.userConfig
|
|
97918
|
+
})) {
|
|
97919
|
+
await inspectAction(input.directory, input.flags, input.invocationCommand);
|
|
97920
|
+
return;
|
|
97921
|
+
}
|
|
97922
|
+
recordCount(METRIC.cliInvoked, 1, { command: input.invocationCommand });
|
|
97923
|
+
const { runScanApp } = await import("./run-scan-app-C8iWVgvR.js");
|
|
97924
|
+
const { shouldFail } = await runScanApp({
|
|
97925
|
+
directory: input.directory,
|
|
97926
|
+
scanTarget,
|
|
97927
|
+
options: resolveCliInspectOptions(input.flags, null),
|
|
97928
|
+
projectFlag: input.flags.project,
|
|
97929
|
+
skipPrompts: input.flags.yes ?? false,
|
|
97930
|
+
blocking: input.flags.blocking
|
|
97931
|
+
});
|
|
97932
|
+
if (shouldFail) process.exitCode = 1;
|
|
97933
|
+
};
|
|
97934
|
+
//#endregion
|
|
97264
97935
|
//#region src/cli/utils/rule-catalog.ts
|
|
97265
97936
|
const buildRuleCatalog = () => REACT_DOCTOR_RULES.map((entry) => ({
|
|
97266
97937
|
key: entry.key,
|
|
@@ -97901,13 +98572,39 @@ const ensureWindowsUtf8Console = () => {
|
|
|
97901
98572
|
} catch {}
|
|
97902
98573
|
};
|
|
97903
98574
|
//#endregion
|
|
98575
|
+
//#region src/cli/utils/active-tui-renderer.ts
|
|
98576
|
+
let activeTuiRenderer = null;
|
|
98577
|
+
const registerActiveTuiRenderer = (renderer) => {
|
|
98578
|
+
activeTuiRenderer = renderer;
|
|
98579
|
+
return () => {
|
|
98580
|
+
if (activeTuiRenderer === renderer) activeTuiRenderer = null;
|
|
98581
|
+
};
|
|
98582
|
+
};
|
|
98583
|
+
const clearActiveTuiRenderer = () => {
|
|
98584
|
+
const renderer = activeTuiRenderer;
|
|
98585
|
+
activeTuiRenderer = null;
|
|
98586
|
+
renderer?.clear();
|
|
98587
|
+
};
|
|
98588
|
+
//#endregion
|
|
97904
98589
|
//#region src/cli/utils/exit-gracefully.ts
|
|
98590
|
+
let didStartExiting = false;
|
|
97905
98591
|
const exitGracefully = () => {
|
|
98592
|
+
if (didStartExiting) return process.exit(130);
|
|
98593
|
+
didStartExiting = true;
|
|
98594
|
+
activeScanAbortRegistry.abortAll();
|
|
98595
|
+
clearActiveTuiRenderer();
|
|
97906
98596
|
try {
|
|
97907
98597
|
if (isJsonModeActive()) writeJsonErrorReport(/* @__PURE__ */ new Error("Scan cancelled by user (SIGINT/SIGTERM)"));
|
|
97908
|
-
else console.log(
|
|
98598
|
+
else console.log([
|
|
98599
|
+
"",
|
|
98600
|
+
"Cancelled.",
|
|
98601
|
+
"",
|
|
98602
|
+
buildSectionDivider(),
|
|
98603
|
+
"",
|
|
98604
|
+
...buildFooterLinkLines({ shareUrl: null })
|
|
98605
|
+
].join("\n"));
|
|
97909
98606
|
} catch {}
|
|
97910
|
-
process.exit(130);
|
|
98607
|
+
flushSentry().finally(() => process.exit(130));
|
|
97911
98608
|
};
|
|
97912
98609
|
//#endregion
|
|
97913
98610
|
//#region src/cli/utils/guard-stdin.ts
|
|
@@ -98025,6 +98722,7 @@ const ROOT_FLAG_SPEC = {
|
|
|
98025
98722
|
"--json-compact",
|
|
98026
98723
|
"--lint",
|
|
98027
98724
|
"--no-color",
|
|
98725
|
+
"--no-cache",
|
|
98028
98726
|
"--no-dead-code",
|
|
98029
98727
|
"--no-lint",
|
|
98030
98728
|
"--no-parallel",
|
|
@@ -98148,13 +98846,18 @@ const COMMAND_FLAG_SPECS = new Map([
|
|
|
98148
98846
|
longOptionsWithoutValues: new Set([
|
|
98149
98847
|
"--color",
|
|
98150
98848
|
"--help",
|
|
98849
|
+
"--no-cache",
|
|
98151
98850
|
"--no-color",
|
|
98152
98851
|
"--no-dead-code",
|
|
98153
98852
|
"--no-score",
|
|
98154
98853
|
"--no-supply-chain",
|
|
98155
98854
|
"--yes"
|
|
98156
98855
|
]),
|
|
98157
|
-
longOptionsWithRequiredValues: new Set([
|
|
98856
|
+
longOptionsWithRequiredValues: new Set([
|
|
98857
|
+
"--blocking",
|
|
98858
|
+
"--max-duration",
|
|
98859
|
+
"--project"
|
|
98860
|
+
]),
|
|
98158
98861
|
longOptionsWithOptionalValues: /* @__PURE__ */ new Set(),
|
|
98159
98862
|
shortOptionsWithoutValues: new Set(["-h", "-y"]),
|
|
98160
98863
|
shortOptionsWithRequiredValues: new Set(["-p"])
|
|
@@ -98296,6 +98999,7 @@ ${highlighter.dim("Scope:")}
|
|
|
98296
98999
|
Dead-code, supply-chain, external lint-config, custom-plugin, and health-score passes are skipped.
|
|
98297
99000
|
Standard scan flags such as ${highlighter.info("--scope")}, ${highlighter.info("--project")}, ${highlighter.info("--verbose")}, and ${highlighter.info("--json")} still work.
|
|
98298
99001
|
`;
|
|
99002
|
+
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
99003
|
const renderCiHelpEpilog = () => `
|
|
98300
99004
|
${highlighter.dim("Examples:")}
|
|
98301
99005
|
${formatExampleLines([
|
|
@@ -98315,8 +99019,12 @@ ${highlighter.dim("Learn more:")}
|
|
|
98315
99019
|
${highlighter.info(CI_URL)}
|
|
98316
99020
|
`;
|
|
98317
99021
|
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(
|
|
99022
|
+
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);
|
|
99023
|
+
program.action((directory = ".", flags) => runScanCommand({
|
|
99024
|
+
directory,
|
|
99025
|
+
flags,
|
|
99026
|
+
invocationCommand: "inspect"
|
|
99027
|
+
}));
|
|
98320
99028
|
program.command("design [directory]").description("Run only the focused UI design diagnostics").addHelpText("after", renderDesignHelpEpilog).action((directory, _options, command) => designAction(directory ?? ".", command.optsWithGlobals()));
|
|
98321
99029
|
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
99030
|
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 +99045,17 @@ rules.command("category <category> <severity>").description("Set the severity fo
|
|
|
98337
99045
|
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
99046
|
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
99047
|
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
|
-
});
|
|
99048
|
+
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({
|
|
99049
|
+
directory,
|
|
99050
|
+
flags: command.optsWithGlobals(),
|
|
99051
|
+
invocationCommand: "experimental-tui"
|
|
99052
|
+
}));
|
|
98372
99053
|
process.stdout.on("error", (error) => {
|
|
98373
99054
|
if (error.code === "EPIPE") process.exit(0);
|
|
98374
99055
|
});
|
|
99056
|
+
process.stderr.on("error", (error) => {
|
|
99057
|
+
if (error.code === "EPIPE") process.exit(0);
|
|
99058
|
+
});
|
|
98375
99059
|
const knownCommands = program.commands.flatMap((command) => [command.name(), ...command.aliases()]);
|
|
98376
99060
|
const strippedArgv = stripUnknownCliFlags(process.argv);
|
|
98377
99061
|
if (process.argv.includes("-V") && !strippedArgv.includes("-V")) {
|
|
@@ -98395,7 +99079,7 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
98395
99079
|
handleError(error, { sentryEventId });
|
|
98396
99080
|
});
|
|
98397
99081
|
//#endregion
|
|
98398
|
-
export {
|
|
99082
|
+
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
99083
|
|
|
98400
99084
|
//# sourceMappingURL=cli.js.map
|
|
98401
|
-
//# debugId=
|
|
99085
|
+
//# debugId=7d7e0bde-eb19-58b5-b161-625bcb1b2588
|