react-doctor 0.6.3-dev.ced746f → 0.6.3-dev.e257a5e
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 +308 -908
- package/dist/index.d.ts +0 -18
- package/dist/index.js +102 -541
- package/dist/lsp.js +107 -544
- package/package.json +4 -4
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]="82500030-a7a7-5da8-b18e-cbdb42102dc3")}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";
|
|
@@ -9,7 +9,7 @@ import * as NFS from "node:fs";
|
|
|
9
9
|
import fs, { mkdtempSync, rmSync } from "node:fs";
|
|
10
10
|
import process$1 from "node:process";
|
|
11
11
|
import * as ts from "typescript";
|
|
12
|
-
import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS,
|
|
12
|
+
import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
|
|
13
13
|
import * as OS from "node:os";
|
|
14
14
|
import os, { tmpdir } from "node:os";
|
|
15
15
|
import { parseJSON5 } from "confbox";
|
|
@@ -18,7 +18,6 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
18
18
|
import { createJiti } from "jiti";
|
|
19
19
|
import * as Crypto from "node:crypto";
|
|
20
20
|
import crypto, { createHash, randomUUID } from "node:crypto";
|
|
21
|
-
import { ANALYZED_MANIFEST_FILENAMES, DEFAULT_EXTENSIONS } from "deslop-js/analyzed-inputs";
|
|
22
21
|
import { gzipSync } from "node:zlib";
|
|
23
22
|
import * as Sentry from "@sentry/node";
|
|
24
23
|
import { performance as performance$1 } from "node:perf_hooks";
|
|
@@ -36129,15 +36128,11 @@ const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
|
|
|
36129
36128
|
const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
36130
36129
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
36131
36130
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
36132
|
-
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
36133
|
-
const CORE_PACKAGE_VERSION = "0.6.3";
|
|
36134
36131
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
36135
|
-
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
36136
36132
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
36137
36133
|
const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
|
|
36138
36134
|
const SUPPLY_CHAIN_CATEGORY = "Security";
|
|
36139
36135
|
const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
|
|
36140
|
-
const SUPPLY_CHAIN_CACHE_TTL_MS = 864e5;
|
|
36141
36136
|
const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
|
|
36142
36137
|
const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
|
|
36143
36138
|
const hasIgnoredPathSegment = (relativePath) => relativePath.split(/[/\\]/).slice(0, -1).some((segment) => IGNORED_DIRECTORIES.has(segment));
|
|
@@ -38819,29 +38814,6 @@ var PerFileLintCacheEnabled = class extends Reference("react-doctor/PerFileLintC
|
|
|
38819
38814
|
return true;
|
|
38820
38815
|
} }) {};
|
|
38821
38816
|
/**
|
|
38822
|
-
* Whether the sidecar lint cache (`runners/oxlint/sidecar-lint-cache.ts`) is
|
|
38823
|
-
* active. Defaults ON — warm rescans replay the cross-file rules' cached
|
|
38824
|
-
* diagnostics for every file whose dependency fingerprint (the probe set the
|
|
38825
|
-
* plugin's collectors recorded) still matches the tree, and re-lint only the
|
|
38826
|
-
* rest. Only reachable when the per-file lint cache itself is on (the
|
|
38827
|
-
* sidecar covers its cache hits), so `REACT_DOCTOR_NO_CACHE` /
|
|
38828
|
-
* `REACT_DOCTOR_NO_FILE_CACHE` implicitly disable it too. Opt-OUT knobs:
|
|
38829
|
-
*
|
|
38830
|
-
* - `REACT_DOCTOR_NO_CACHE` — the global off-switch.
|
|
38831
|
-
* - `REACT_DOCTOR_NO_SIDECAR_CACHE` — granular rollback hatch: keep the
|
|
38832
|
-
* per-file cache but run the always-fresh sidecar over every hit
|
|
38833
|
-
* (the pre-cache behavior).
|
|
38834
|
-
*
|
|
38835
|
-
* Tests override via `Layer.succeed(SidecarLintCacheEnabled, false)`.
|
|
38836
|
-
*/
|
|
38837
|
-
var SidecarLintCacheEnabled = class extends Reference("react-doctor/SidecarLintCacheEnabled", { defaultValue: () => {
|
|
38838
|
-
const noCache = process.env["REACT_DOCTOR_NO_CACHE"]?.toLowerCase() ?? "";
|
|
38839
|
-
const noSidecarCache = process.env["REACT_DOCTOR_NO_SIDECAR_CACHE"]?.toLowerCase() ?? "";
|
|
38840
|
-
if (CACHE_DISABLED_VALUES$1.has(noCache)) return false;
|
|
38841
|
-
if (CACHE_DISABLED_VALUES$1.has(noSidecarCache)) return false;
|
|
38842
|
-
return true;
|
|
38843
|
-
} }) {};
|
|
38844
|
-
/**
|
|
38845
38817
|
* Whether the whole-project dead-code result cache
|
|
38846
38818
|
* (`dead-code/dead-code-result-cache.ts`) is active. Defaults ON — a rescan
|
|
38847
38819
|
* whose inputs (source tree, manifests, configs, analyzer version) are
|
|
@@ -40609,37 +40581,51 @@ const failOpenReadJson = (filePath, fallback) => {
|
|
|
40609
40581
|
return fallback;
|
|
40610
40582
|
}
|
|
40611
40583
|
};
|
|
40612
|
-
const
|
|
40613
|
-
|
|
40614
|
-
|
|
40615
|
-
|
|
40616
|
-
|
|
40617
|
-
|
|
40618
|
-
|
|
40619
|
-
|
|
40620
|
-
|
|
40621
|
-
|
|
40584
|
+
const ANALYZED_FILE_EXTENSIONS = new Set([
|
|
40585
|
+
".ts",
|
|
40586
|
+
".tsx",
|
|
40587
|
+
".js",
|
|
40588
|
+
".jsx",
|
|
40589
|
+
".mts",
|
|
40590
|
+
".mjs",
|
|
40591
|
+
".cts",
|
|
40592
|
+
".cjs",
|
|
40593
|
+
".mdx",
|
|
40594
|
+
".astro",
|
|
40595
|
+
".graphql",
|
|
40596
|
+
".gql",
|
|
40597
|
+
".css",
|
|
40598
|
+
".scss",
|
|
40599
|
+
".vue",
|
|
40600
|
+
".svelte"
|
|
40601
|
+
]);
|
|
40602
|
+
const ANALYZED_MANIFEST_NAMES = new Set([
|
|
40603
|
+
"package.json",
|
|
40604
|
+
"knip.json",
|
|
40605
|
+
"app.json",
|
|
40606
|
+
"pnpm-workspace.yaml",
|
|
40607
|
+
"lerna.json",
|
|
40608
|
+
"pnpm-lock.yaml",
|
|
40609
|
+
"package-lock.json",
|
|
40610
|
+
"yarn.lock",
|
|
40611
|
+
"bun.lock",
|
|
40612
|
+
"bun.lockb",
|
|
40613
|
+
"deno.lock",
|
|
40614
|
+
".gitignore"
|
|
40615
|
+
]);
|
|
40622
40616
|
const isTsConfigLikeFile = (fileName) => (fileName.startsWith("tsconfig") || fileName.startsWith("jsconfig")) && fileName.endsWith(".json");
|
|
40623
40617
|
const isFingerprintedFile = (fileName) => ANALYZED_FILE_EXTENSIONS.has(Path.extname(fileName).toLowerCase()) || ANALYZED_MANIFEST_NAMES.has(fileName) || isTsConfigLikeFile(fileName);
|
|
40624
|
-
|
|
40625
|
-
|
|
40626
|
-
* `/`-separated path. Taken BEFORE the (long) analysis so a stored result is
|
|
40627
|
-
* verified against the tree it started from.
|
|
40628
|
-
*/
|
|
40629
|
-
const collectAnalyzedFileStats = (rootDirectory) => {
|
|
40630
|
-
const statByRelativePath = /* @__PURE__ */ new Map();
|
|
40618
|
+
const collectAnalyzedFileFingerprints = (rootDirectory) => {
|
|
40619
|
+
const fingerprints = [];
|
|
40631
40620
|
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
|
|
40632
40621
|
if (!isFingerprintedFile(name)) continue;
|
|
40633
40622
|
try {
|
|
40634
40623
|
const fileStat = NFS.statSync(absolutePath);
|
|
40635
40624
|
const relativePath = Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/");
|
|
40636
|
-
|
|
40637
|
-
mtimeMs: fileStat.mtimeMs,
|
|
40638
|
-
size: fileStat.size
|
|
40639
|
-
});
|
|
40625
|
+
fingerprints.push(`${relativePath}:${fileStat.mtimeMs}:${fileStat.size}`);
|
|
40640
40626
|
} catch {}
|
|
40641
40627
|
}
|
|
40642
|
-
return
|
|
40628
|
+
return fingerprints.sort();
|
|
40643
40629
|
};
|
|
40644
40630
|
const bundledRequire$2 = createRequire(import.meta.url);
|
|
40645
40631
|
const resolveDeslopVersion = () => {
|
|
@@ -40651,85 +40637,33 @@ const resolveDeslopVersion = () => {
|
|
|
40651
40637
|
}
|
|
40652
40638
|
};
|
|
40653
40639
|
const computeDeadCodeCacheKey = (input) => crypto.createHash("sha1").update(JSON.stringify({
|
|
40654
|
-
schemaVersion:
|
|
40655
|
-
coreVersion: input.coreVersion,
|
|
40640
|
+
schemaVersion: 1,
|
|
40656
40641
|
deslopVersion: resolveDeslopVersion(),
|
|
40657
40642
|
deslopJsModuleSpecifier: input.deslopJsModuleSpecifier,
|
|
40658
40643
|
entryPatterns: input.entryPatterns,
|
|
40659
40644
|
ignorePatterns: input.ignorePatterns,
|
|
40660
|
-
tsConfigFile: input.tsConfigPath === void 0 ? null : Path.relative(input.rootDirectory, input.tsConfigPath).replace(/\\/g, "/")
|
|
40645
|
+
tsConfigFile: input.tsConfigPath === void 0 ? null : Path.relative(input.rootDirectory, input.tsConfigPath).replace(/\\/g, "/"),
|
|
40646
|
+
files: collectAnalyzedFileFingerprints(input.rootDirectory)
|
|
40661
40647
|
})).digest("hex");
|
|
40662
|
-
const validateDiagnostic$
|
|
40648
|
+
const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
|
|
40663
40649
|
const decodeCachedDiagnostics = (raw) => {
|
|
40664
40650
|
try {
|
|
40665
|
-
for (const entry of raw) validateDiagnostic$
|
|
40651
|
+
for (const entry of raw) validateDiagnostic$1(entry);
|
|
40666
40652
|
return raw;
|
|
40667
40653
|
} catch {
|
|
40668
40654
|
return null;
|
|
40669
40655
|
}
|
|
40670
40656
|
};
|
|
40671
|
-
const
|
|
40672
|
-
const
|
|
40673
|
-
|
|
40674
|
-
|
|
40675
|
-
if (persisted === null || !isRecord$1(persisted) || persisted.version !== 2 || persisted.key !== input.cacheKey || !isRecord$1(persisted.files) || !Array.isArray(persisted.diagnostics)) return null;
|
|
40676
|
-
const storedFileEntries = Object.entries(persisted.files);
|
|
40677
|
-
if (storedFileEntries.length !== input.currentFileStats.size) return null;
|
|
40678
|
-
const repairedFiles = {};
|
|
40679
|
-
let repairedCount = 0;
|
|
40680
|
-
for (const [relativePath, storedIdentity] of storedFileEntries) {
|
|
40681
|
-
if (!isPersistedFileIdentity(storedIdentity)) return null;
|
|
40682
|
-
const currentStat = input.currentFileStats.get(relativePath);
|
|
40683
|
-
if (currentStat === void 0) return null;
|
|
40684
|
-
const [storedMtimeMs, storedSize, storedContentHash] = storedIdentity;
|
|
40685
|
-
if (currentStat.mtimeMs === storedMtimeMs && currentStat.size === storedSize) {
|
|
40686
|
-
repairedFiles[relativePath] = storedIdentity;
|
|
40687
|
-
continue;
|
|
40688
|
-
}
|
|
40689
|
-
if (currentStat.size !== storedSize) return null;
|
|
40690
|
-
const currentContentHash = hashFileContents(Path.join(input.rootDirectory, relativePath));
|
|
40691
|
-
if (currentContentHash === null || currentContentHash !== storedContentHash) return null;
|
|
40692
|
-
repairedFiles[relativePath] = [
|
|
40693
|
-
currentStat.mtimeMs,
|
|
40694
|
-
currentStat.size,
|
|
40695
|
-
storedContentHash
|
|
40696
|
-
];
|
|
40697
|
-
repairedCount += 1;
|
|
40698
|
-
}
|
|
40699
|
-
const diagnostics = decodeCachedDiagnostics(persisted.diagnostics);
|
|
40700
|
-
if (diagnostics === null) return null;
|
|
40701
|
-
if (repairedCount > 0) atomicWriteJson(cacheFilePath, {
|
|
40702
|
-
version: 2,
|
|
40703
|
-
key: input.cacheKey,
|
|
40704
|
-
files: repairedFiles,
|
|
40705
|
-
diagnostics: persisted.diagnostics
|
|
40706
|
-
});
|
|
40707
|
-
return diagnostics;
|
|
40657
|
+
const lookupDeadCodeResultCache = (cacheDirectory, cacheKey) => {
|
|
40658
|
+
const persisted = failOpenReadJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), null);
|
|
40659
|
+
if (persisted === null || !isRecord$1(persisted) || persisted.version !== 1 || persisted.key !== cacheKey || !Array.isArray(persisted.diagnostics)) return null;
|
|
40660
|
+
return decodeCachedDiagnostics(persisted.diagnostics);
|
|
40708
40661
|
};
|
|
40709
|
-
const storeDeadCodeResultCache = (
|
|
40710
|
-
|
|
40711
|
-
|
|
40712
|
-
|
|
40713
|
-
|
|
40714
|
-
if (contentHash === null) return;
|
|
40715
|
-
let currentStat;
|
|
40716
|
-
try {
|
|
40717
|
-
currentStat = NFS.statSync(absolutePath);
|
|
40718
|
-
} catch {
|
|
40719
|
-
return;
|
|
40720
|
-
}
|
|
40721
|
-
if (currentStat.mtimeMs !== snapshotStat.mtimeMs || currentStat.size !== snapshotStat.size) return;
|
|
40722
|
-
persistedFiles[relativePath] = [
|
|
40723
|
-
snapshotStat.mtimeMs,
|
|
40724
|
-
snapshotStat.size,
|
|
40725
|
-
contentHash
|
|
40726
|
-
];
|
|
40727
|
-
}
|
|
40728
|
-
atomicWriteJson(Path.join(input.cacheDirectory, DEAD_CODE_CACHE_FILENAME), {
|
|
40729
|
-
version: 2,
|
|
40730
|
-
key: input.cacheKey,
|
|
40731
|
-
files: persistedFiles,
|
|
40732
|
-
diagnostics: input.diagnostics
|
|
40662
|
+
const storeDeadCodeResultCache = (cacheDirectory, cacheKey, diagnostics) => {
|
|
40663
|
+
atomicWriteJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), {
|
|
40664
|
+
version: 1,
|
|
40665
|
+
key: cacheKey,
|
|
40666
|
+
diagnostics
|
|
40733
40667
|
});
|
|
40734
40668
|
};
|
|
40735
40669
|
const readSystemFacts = () => ({
|
|
@@ -40868,14 +40802,6 @@ process.stdin.on("end", () => {
|
|
|
40868
40802
|
circularDependencies: result.circularDependencies.map((cycle) => ({
|
|
40869
40803
|
files: cycle.files,
|
|
40870
40804
|
})),
|
|
40871
|
-
...(result.incrementalCacheStats
|
|
40872
|
-
? {
|
|
40873
|
-
summaryCacheStats: {
|
|
40874
|
-
hits: result.incrementalCacheStats.summaryHits,
|
|
40875
|
-
misses: result.incrementalCacheStats.summaryMisses,
|
|
40876
|
-
},
|
|
40877
|
-
}
|
|
40878
|
-
: {}),
|
|
40879
40805
|
});
|
|
40880
40806
|
|
|
40881
40807
|
const serializeError = (error) =>
|
|
@@ -40899,9 +40825,6 @@ process.stdin.on("end", () => {
|
|
|
40899
40825
|
...(workerInput.ignorePatterns.length > 0
|
|
40900
40826
|
? { ignorePatterns: workerInput.ignorePatterns }
|
|
40901
40827
|
: {}),
|
|
40902
|
-
...(workerInput.incrementalCachePath
|
|
40903
|
-
? { incrementalCachePath: workerInput.incrementalCachePath }
|
|
40904
|
-
: {}),
|
|
40905
40828
|
// We consume only deslop's GRAPH-based findings (unusedFiles, unusedExports,
|
|
40906
40829
|
// unusedDependencies, circularDependencies). Everything else deslop can compute
|
|
40907
40830
|
// is pure wasted work for us, and it's the bulk of the runtime:
|
|
@@ -40912,18 +40835,12 @@ process.stdin.on("end", () => {
|
|
|
40912
40835
|
// are the single most expensive pass — duplicate-block detection alone was
|
|
40913
40836
|
// ~83s of a ~130s Sentry scan — so skipping them is an ~8.5x dead-code
|
|
40914
40837
|
// speedup on a large repo.
|
|
40915
|
-
//
|
|
40916
|
-
// simplifiable functions, identity wrappers, …) — ~120 ms of discarded
|
|
40917
|
-
// output per scan, and skipping them lets the incremental cache drop the
|
|
40918
|
-
// DRY-pattern summary fields (the largest slice of the cache file).
|
|
40919
|
-
// All are provably safe: the consumed graph findings are computed by their own
|
|
40838
|
+
// Both are provably safe: the consumed graph findings are computed by their own
|
|
40920
40839
|
// detectors, independent of these passes (confirmed byte-identical on
|
|
40921
|
-
// excalidraw + mui-material + sentry
|
|
40922
|
-
//
|
|
40923
|
-
// for path-alias resolution in the import graph.
|
|
40840
|
+
// excalidraw + mui-material + sentry). tsConfigPath stays — the module resolver
|
|
40841
|
+
// needs it for path-alias resolution in the import graph.
|
|
40924
40842
|
semantic: { enabled: false },
|
|
40925
40843
|
reportCodeQuality: false,
|
|
40926
|
-
reportRedundancy: false,
|
|
40927
40844
|
};
|
|
40928
40845
|
const result = await analyze(defineConfig(config));
|
|
40929
40846
|
emit({ ok: true, result: normalizeResult(result) });
|
|
@@ -41007,23 +40924,13 @@ const parseCircularDependencies = (value) => {
|
|
|
41007
40924
|
}
|
|
41008
40925
|
return circularDependencies;
|
|
41009
40926
|
};
|
|
41010
|
-
const parseSummaryCacheStats = (value) => {
|
|
41011
|
-
if (!isRecord$1(value)) return void 0;
|
|
41012
|
-
if (typeof value.hits !== "number" || typeof value.misses !== "number") return void 0;
|
|
41013
|
-
return {
|
|
41014
|
-
hits: value.hits,
|
|
41015
|
-
misses: value.misses
|
|
41016
|
-
};
|
|
41017
|
-
};
|
|
41018
40927
|
const parseDeadCodeWorkerResult = (value) => {
|
|
41019
40928
|
if (!isRecord$1(value)) throw new Error("Dead-code worker returned an invalid result.");
|
|
41020
|
-
const summaryCacheStats = parseSummaryCacheStats(value.summaryCacheStats);
|
|
41021
40929
|
return {
|
|
41022
40930
|
unusedFiles: parseUnusedFiles(value.unusedFiles),
|
|
41023
40931
|
unusedExports: parseUnusedExports(value.unusedExports),
|
|
41024
40932
|
unusedDependencies: parseUnusedDependencies(value.unusedDependencies),
|
|
41025
|
-
circularDependencies: parseCircularDependencies(value.circularDependencies)
|
|
41026
|
-
...summaryCacheStats ? { summaryCacheStats } : {}
|
|
40933
|
+
circularDependencies: parseCircularDependencies(value.circularDependencies)
|
|
41027
40934
|
};
|
|
41028
40935
|
};
|
|
41029
40936
|
const parseDeadCodeWorkerError = (value) => {
|
|
@@ -41139,26 +41046,18 @@ const checkDeadCode = async (options) => {
|
|
|
41139
41046
|
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
41140
41047
|
const tsConfigPath = resolveTsConfigPath(rootDirectory);
|
|
41141
41048
|
const deslopJsModuleSpecifier = options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js");
|
|
41142
|
-
const
|
|
41143
|
-
const cacheKey = fileStatsSnapshot === null ? null : computeDeadCodeCacheKey({
|
|
41049
|
+
const cacheKey = options.cacheEnabled ? computeDeadCodeCacheKey({
|
|
41144
41050
|
rootDirectory,
|
|
41145
41051
|
entryPatterns,
|
|
41146
41052
|
ignorePatterns,
|
|
41147
41053
|
tsConfigPath,
|
|
41148
|
-
deslopJsModuleSpecifier
|
|
41149
|
-
|
|
41150
|
-
|
|
41151
|
-
|
|
41152
|
-
const cachedDiagnostics = lookupDeadCodeResultCache({
|
|
41153
|
-
cacheDirectory: resolveReactDoctorCacheDir(rootDirectory),
|
|
41154
|
-
cacheKey,
|
|
41155
|
-
rootDirectory,
|
|
41156
|
-
currentFileStats: fileStatsSnapshot
|
|
41157
|
-
});
|
|
41054
|
+
deslopJsModuleSpecifier
|
|
41055
|
+
}) : null;
|
|
41056
|
+
if (cacheKey !== null) {
|
|
41057
|
+
const cachedDiagnostics = lookupDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey);
|
|
41158
41058
|
options.onCacheOutcome?.(cachedDiagnostics !== null);
|
|
41159
41059
|
if (cachedDiagnostics !== null) return [...cachedDiagnostics];
|
|
41160
41060
|
}
|
|
41161
|
-
const incrementalCachePath = options.cacheEnabled === true ? Path.join(resolveReactDoctorCacheDir(rootDirectory), DEAD_CODE_SUMMARY_CACHE_FILENAME) : void 0;
|
|
41162
41061
|
const spawnAndRun = () => {
|
|
41163
41062
|
return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
41164
41063
|
rootDirectory,
|
|
@@ -41166,12 +41065,10 @@ const checkDeadCode = async (options) => {
|
|
|
41166
41065
|
tsConfigPath,
|
|
41167
41066
|
ignorePatterns,
|
|
41168
41067
|
deslopJsModuleSpecifier,
|
|
41169
|
-
parseConcurrency: options.parseConcurrency
|
|
41170
|
-
incrementalCachePath
|
|
41068
|
+
parseConcurrency: options.parseConcurrency
|
|
41171
41069
|
}), options.workerTimeoutMs ?? 12e4, options.abortSignal);
|
|
41172
41070
|
};
|
|
41173
41071
|
const result = parseDeadCodeWorkerResult(options.createWorker === void 0 ? await withDeadCodeWorkerSlot(spawnAndRun, options.abortSignal) : await spawnAndRun());
|
|
41174
|
-
if (result.summaryCacheStats !== void 0) options.onSummaryCacheStats?.(result.summaryCacheStats);
|
|
41175
41072
|
const toRelative = (filePath) => toRelativeFilePath(rootDirectory, filePath);
|
|
41176
41073
|
const diagnostics = [];
|
|
41177
41074
|
for (const unusedFile of result.unusedFiles) diagnostics.push({
|
|
@@ -41228,13 +41125,7 @@ const checkDeadCode = async (options) => {
|
|
|
41228
41125
|
category: DEAD_CODE_CATEGORY
|
|
41229
41126
|
});
|
|
41230
41127
|
}
|
|
41231
|
-
if (cacheKey !== null
|
|
41232
|
-
cacheDirectory: resolveReactDoctorCacheDir(rootDirectory),
|
|
41233
|
-
cacheKey,
|
|
41234
|
-
rootDirectory,
|
|
41235
|
-
snapshotFileStats: fileStatsSnapshot,
|
|
41236
|
-
diagnostics
|
|
41237
|
-
});
|
|
41128
|
+
if (cacheKey !== null) storeDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey, diagnostics);
|
|
41238
41129
|
return diagnostics;
|
|
41239
41130
|
};
|
|
41240
41131
|
const DEAD_CODE_RULE_KEY_PREFIX = `${DEAD_CODE_PLUGIN}/`;
|
|
@@ -41345,8 +41236,7 @@ var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
|
|
|
41345
41236
|
workerTimeoutMs: input.workerTimeoutMs,
|
|
41346
41237
|
abortSignal: signal,
|
|
41347
41238
|
cacheEnabled,
|
|
41348
|
-
onCacheOutcome: input.onCacheOutcome
|
|
41349
|
-
onSummaryCacheStats: input.onSummaryCacheStats
|
|
41239
|
+
onCacheOutcome: input.onCacheOutcome
|
|
41350
41240
|
}),
|
|
41351
41241
|
catch: (cause) => new ReactDoctorError({ reason: new DeadCodeAnalysisFailed({ cause }) })
|
|
41352
41242
|
}).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)));
|
|
@@ -42216,7 +42106,7 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
42216
42106
|
}
|
|
42217
42107
|
return enabled;
|
|
42218
42108
|
};
|
|
42219
|
-
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection
|
|
42109
|
+
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection }) => {
|
|
42220
42110
|
const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
42221
42111
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
42222
42112
|
const jsPlugins = [];
|
|
@@ -42228,7 +42118,6 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
42228
42118
|
if (!rule) continue;
|
|
42229
42119
|
if (ruleSelection === "cacheable" && CROSS_FILE_RULE_IDS.has(registryEntry.id)) continue;
|
|
42230
42120
|
if (ruleSelection === "sidecar" && !CROSS_FILE_RULE_IDS.has(registryEntry.id)) continue;
|
|
42231
|
-
if (ruleSelection === "sidecar" && sidecarRuleIdFilter !== void 0 && !sidecarRuleIdFilter.has(registryEntry.id)) continue;
|
|
42232
42121
|
if (rule.scan !== void 0) continue;
|
|
42233
42122
|
if (customRulesOnly && registryEntry.originallyExternal) continue;
|
|
42234
42123
|
if (rule.framework !== "global" && !rule.requires) continue;
|
|
@@ -42273,23 +42162,23 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
42273
42162
|
}
|
|
42274
42163
|
};
|
|
42275
42164
|
};
|
|
42276
|
-
const validateDiagnostic
|
|
42165
|
+
const validateDiagnostic = decodeUnknownSync(Diagnostic);
|
|
42277
42166
|
const decodeFileDiagnostics = (raw) => {
|
|
42278
42167
|
if (!Array.isArray(raw)) return null;
|
|
42279
42168
|
try {
|
|
42280
|
-
for (const entry of raw) validateDiagnostic
|
|
42169
|
+
for (const entry of raw) validateDiagnostic(entry);
|
|
42281
42170
|
return raw;
|
|
42282
42171
|
} catch {
|
|
42283
42172
|
return null;
|
|
42284
42173
|
}
|
|
42285
42174
|
};
|
|
42286
|
-
const emptyCache
|
|
42175
|
+
const emptyCache = () => ({
|
|
42287
42176
|
version: 1,
|
|
42288
42177
|
rulesets: {}
|
|
42289
42178
|
});
|
|
42290
|
-
const loadRulesetEntries
|
|
42179
|
+
const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
|
|
42291
42180
|
const entries = /* @__PURE__ */ new Map();
|
|
42292
|
-
const persisted = failOpenReadJson(cacheFilePath, emptyCache
|
|
42181
|
+
const persisted = failOpenReadJson(cacheFilePath, emptyCache());
|
|
42293
42182
|
if (persisted.version !== 1 || !isRecord$1(persisted.rulesets)) return entries;
|
|
42294
42183
|
const bucket = persisted.rulesets[rulesetHash];
|
|
42295
42184
|
if (!isRecord$1(bucket) || !isRecord$1(bucket.files)) return entries;
|
|
@@ -42301,149 +42190,20 @@ const loadRulesetEntries$1 = (cacheFilePath, rulesetHash) => {
|
|
|
42301
42190
|
};
|
|
42302
42191
|
const createFileLintCache = (cacheDirectory, rulesetHash) => {
|
|
42303
42192
|
const cacheFilePath = Path.join(cacheDirectory, FILE_LINT_CACHE_FILENAME);
|
|
42304
|
-
const entries = loadRulesetEntries
|
|
42193
|
+
const entries = loadRulesetEntries(cacheFilePath, rulesetHash);
|
|
42305
42194
|
return {
|
|
42306
42195
|
lookup: (fileKey) => entries.get(fileKey) ?? null,
|
|
42307
42196
|
store: (fileKey, diagnostics) => {
|
|
42308
42197
|
entries.delete(fileKey);
|
|
42309
42198
|
entries.set(fileKey, diagnostics);
|
|
42310
42199
|
},
|
|
42311
|
-
persist: () => {
|
|
42312
|
-
const onDisk = failOpenReadJson(cacheFilePath, emptyCache$1());
|
|
42313
|
-
const rulesets = onDisk.version === 1 && isRecord$1(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
|
|
42314
|
-
const existingBucket = rulesets[rulesetHash];
|
|
42315
|
-
const existingFiles = isRecord$1(existingBucket) && isRecord$1(existingBucket.files) ? existingBucket.files : {};
|
|
42316
|
-
const ourFiles = {};
|
|
42317
|
-
for (const [fileKey, diagnostics] of entries) ourFiles[fileKey] = diagnostics;
|
|
42318
|
-
const cappedEntries = Object.entries({
|
|
42319
|
-
...existingFiles,
|
|
42320
|
-
...ourFiles
|
|
42321
|
-
}).slice(-FILE_LINT_CACHE_MAX_FILE_COUNT);
|
|
42322
|
-
rulesets[rulesetHash] = {
|
|
42323
|
-
updatedAtMs: Date.now(),
|
|
42324
|
-
files: Object.fromEntries(cappedEntries)
|
|
42325
|
-
};
|
|
42326
|
-
atomicWriteJson(cacheFilePath, {
|
|
42327
|
-
version: 1,
|
|
42328
|
-
rulesets: Object.fromEntries(Object.entries(rulesets).filter(([, bucket]) => isRecord$1(bucket) && typeof bucket.updatedAtMs === "number").sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8))
|
|
42329
|
-
});
|
|
42330
|
-
}
|
|
42331
|
-
};
|
|
42332
|
-
};
|
|
42333
|
-
const ABSENT_CONTENT_ANSWER = "absent";
|
|
42334
|
-
/**
|
|
42335
|
-
* Answers dependency probes for the sidecar lint cache — used both when
|
|
42336
|
-
* STORING an entry (turn the collector's probe paths into answers) and when
|
|
42337
|
-
* VERIFYING one (re-answer each stored probe against the current tree).
|
|
42338
|
-
* Store and verify must agree exactly, so both go through this one resolver.
|
|
42339
|
-
*
|
|
42340
|
-
* - `content` — SHA-1 of the file's bytes, `"absent"` when unreadable
|
|
42341
|
-
* (missing, a directory, permission failure). Source files reuse the
|
|
42342
|
-
* hashes the cache-partition loop already computed
|
|
42343
|
-
* (`contentHashByRelativePath`); everything else (package.json,
|
|
42344
|
-
* tsconfig, ignored files) is hashed on demand.
|
|
42345
|
-
* - `exists` — the existence class a module-resolution candidate probe
|
|
42346
|
-
* sees: `"file"`, `"dir"`, or `"none"` (missing or an exotic entry —
|
|
42347
|
-
* `statSync` follows symlinks, matching the resolvers).
|
|
42348
|
-
*
|
|
42349
|
-
* Answers are memoized per scan: the filesystem is frozen for the run, and
|
|
42350
|
-
* hot dependency paths (a barrel imported by hundreds of files, the shared
|
|
42351
|
-
* tsconfig chain) repeat across many files' probe sets.
|
|
42352
|
-
*/
|
|
42353
|
-
const createSidecarProbeAnswerResolver = (input) => {
|
|
42354
|
-
const contentAnswerByPath = /* @__PURE__ */ new Map();
|
|
42355
|
-
const existsAnswerByPath = /* @__PURE__ */ new Map();
|
|
42356
|
-
const toRelativePath = (absolutePath) => Path.relative(input.rootDirectory, absolutePath).replaceAll("\\", "/");
|
|
42357
|
-
const contentAnswer = (relativePath) => {
|
|
42358
|
-
const memoized = contentAnswerByPath.get(relativePath);
|
|
42359
|
-
if (memoized !== void 0) return memoized;
|
|
42360
|
-
const answer = input.contentHashByRelativePath.get(relativePath) ?? hashFileContents(Path.resolve(input.rootDirectory, relativePath)) ?? ABSENT_CONTENT_ANSWER;
|
|
42361
|
-
contentAnswerByPath.set(relativePath, answer);
|
|
42362
|
-
return answer;
|
|
42363
|
-
};
|
|
42364
|
-
const existsAnswer = (relativePath) => {
|
|
42365
|
-
const memoized = existsAnswerByPath.get(relativePath);
|
|
42366
|
-
if (memoized !== void 0) return memoized;
|
|
42367
|
-
let answer;
|
|
42368
|
-
try {
|
|
42369
|
-
const stat = NFS.statSync(Path.resolve(input.rootDirectory, relativePath));
|
|
42370
|
-
answer = stat.isFile() ? "file" : stat.isDirectory() ? "dir" : "none";
|
|
42371
|
-
} catch {
|
|
42372
|
-
answer = "none";
|
|
42373
|
-
}
|
|
42374
|
-
existsAnswerByPath.set(relativePath, answer);
|
|
42375
|
-
return answer;
|
|
42376
|
-
};
|
|
42377
|
-
return {
|
|
42378
|
-
answerFor: (kind, relativePath) => kind === "content" ? contentAnswer(relativePath) : existsAnswer(relativePath),
|
|
42379
|
-
toRelativePath
|
|
42380
|
-
};
|
|
42381
|
-
};
|
|
42382
|
-
const validateDiagnostic = decodeUnknownSync(Diagnostic);
|
|
42383
|
-
const isProbeKind = (value) => value === "content" || value === "exists";
|
|
42384
|
-
const decodeSidecarFileEntry = (raw) => {
|
|
42385
|
-
if (!isRecord$1(raw) || !Array.isArray(raw.probes) || !Array.isArray(raw.diagnostics)) return null;
|
|
42386
|
-
const probes = [];
|
|
42387
|
-
for (const rawProbe of raw.probes) {
|
|
42388
|
-
if (!Array.isArray(rawProbe) || rawProbe.length !== 3) return null;
|
|
42389
|
-
const [kind, probePath, answer] = rawProbe;
|
|
42390
|
-
if (!isProbeKind(kind) || typeof probePath !== "string" || typeof answer !== "string") return null;
|
|
42391
|
-
probes.push({
|
|
42392
|
-
kind,
|
|
42393
|
-
path: probePath,
|
|
42394
|
-
answer
|
|
42395
|
-
});
|
|
42396
|
-
}
|
|
42397
|
-
try {
|
|
42398
|
-
for (const entry of raw.diagnostics) validateDiagnostic(entry);
|
|
42399
|
-
} catch {
|
|
42400
|
-
return null;
|
|
42401
|
-
}
|
|
42402
|
-
return {
|
|
42403
|
-
probes,
|
|
42404
|
-
diagnostics: raw.diagnostics
|
|
42405
|
-
};
|
|
42406
|
-
};
|
|
42407
|
-
const encodeSidecarFileEntry = (entry) => ({
|
|
42408
|
-
probes: entry.probes.map((probe) => [
|
|
42409
|
-
probe.kind,
|
|
42410
|
-
probe.path,
|
|
42411
|
-
probe.answer
|
|
42412
|
-
]),
|
|
42413
|
-
diagnostics: entry.diagnostics
|
|
42414
|
-
});
|
|
42415
|
-
const emptyCache = () => ({
|
|
42416
|
-
version: 1,
|
|
42417
|
-
rulesets: {}
|
|
42418
|
-
});
|
|
42419
|
-
const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
|
|
42420
|
-
const entries = /* @__PURE__ */ new Map();
|
|
42421
|
-
const persisted = failOpenReadJson(cacheFilePath, emptyCache());
|
|
42422
|
-
if (persisted.version !== 1 || !isRecord$1(persisted.rulesets)) return entries;
|
|
42423
|
-
const bucket = persisted.rulesets[rulesetHash];
|
|
42424
|
-
if (!isRecord$1(bucket) || !isRecord$1(bucket.files)) return entries;
|
|
42425
|
-
for (const [fileKey, rawEntry] of Object.entries(bucket.files)) {
|
|
42426
|
-
const decoded = decodeSidecarFileEntry(rawEntry);
|
|
42427
|
-
if (decoded !== null) entries.set(fileKey, decoded);
|
|
42428
|
-
}
|
|
42429
|
-
return entries;
|
|
42430
|
-
};
|
|
42431
|
-
const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
|
|
42432
|
-
const cacheFilePath = Path.join(cacheDirectory, SIDECAR_LINT_CACHE_FILENAME);
|
|
42433
|
-
const entries = loadRulesetEntries(cacheFilePath, rulesetHash);
|
|
42434
|
-
return {
|
|
42435
|
-
lookup: (fileKey) => entries.get(fileKey) ?? null,
|
|
42436
|
-
store: (fileKey, entry) => {
|
|
42437
|
-
entries.delete(fileKey);
|
|
42438
|
-
entries.set(fileKey, entry);
|
|
42439
|
-
},
|
|
42440
42200
|
persist: () => {
|
|
42441
42201
|
const onDisk = failOpenReadJson(cacheFilePath, emptyCache());
|
|
42442
42202
|
const rulesets = onDisk.version === 1 && isRecord$1(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
|
|
42443
42203
|
const existingBucket = rulesets[rulesetHash];
|
|
42444
42204
|
const existingFiles = isRecord$1(existingBucket) && isRecord$1(existingBucket.files) ? existingBucket.files : {};
|
|
42445
42205
|
const ourFiles = {};
|
|
42446
|
-
for (const [fileKey,
|
|
42206
|
+
for (const [fileKey, diagnostics] of entries) ourFiles[fileKey] = diagnostics;
|
|
42447
42207
|
const cappedEntries = Object.entries({
|
|
42448
42208
|
...existingFiles,
|
|
42449
42209
|
...ourFiles
|
|
@@ -43458,6 +43218,13 @@ const validateRuleRegistration = () => {
|
|
|
43458
43218
|
].filter((entry) => entry !== null).join("; ");
|
|
43459
43219
|
console.warn(`[react-doctor] rule-registration drift: ${detail}`);
|
|
43460
43220
|
};
|
|
43221
|
+
const hashFileContents = (filePath) => {
|
|
43222
|
+
try {
|
|
43223
|
+
return crypto.createHash("sha1").update(NFS.readFileSync(filePath)).digest("hex");
|
|
43224
|
+
} catch {
|
|
43225
|
+
return null;
|
|
43226
|
+
}
|
|
43227
|
+
};
|
|
43461
43228
|
/**
|
|
43462
43229
|
* Balanced LPT (longest-processing-time-first) lint batch planner. Where
|
|
43463
43230
|
* `batchIncludePaths` greedily fills 100-file batches and leaves a small
|
|
@@ -43539,106 +43306,6 @@ const writeOxlintConfig = (configPath, configToWrite) => {
|
|
|
43539
43306
|
NFS.closeSync(fileHandle);
|
|
43540
43307
|
}
|
|
43541
43308
|
};
|
|
43542
|
-
/**
|
|
43543
|
-
* Attributes diagnostics back to the file that produced them by the
|
|
43544
|
-
* normalized path oxlint echoes. Returns `null` when ANY diagnostic can't be
|
|
43545
|
-
* attributed — the path forms don't align, so the caller must skip its cache
|
|
43546
|
-
* store rather than risk caching a wrong empty result for a file that
|
|
43547
|
-
* actually had diagnostics.
|
|
43548
|
-
*/
|
|
43549
|
-
const attributeDiagnosticsToFiles = (diagnostics, files) => {
|
|
43550
|
-
const fileByNormalizedPath = /* @__PURE__ */ new Map();
|
|
43551
|
-
for (const file of files) fileByNormalizedPath.set(file.replaceAll("\\", "/"), file);
|
|
43552
|
-
const diagnosticsByFile = /* @__PURE__ */ new Map();
|
|
43553
|
-
for (const diagnostic of diagnostics) {
|
|
43554
|
-
const file = fileByNormalizedPath.get(diagnostic.filePath);
|
|
43555
|
-
if (file === void 0) return null;
|
|
43556
|
-
const fileDiagnostics = diagnosticsByFile.get(file) ?? [];
|
|
43557
|
-
fileDiagnostics.push(diagnostic);
|
|
43558
|
-
diagnosticsByFile.set(file, fileDiagnostics);
|
|
43559
|
-
}
|
|
43560
|
-
return diagnosticsByFile;
|
|
43561
|
-
};
|
|
43562
|
-
/**
|
|
43563
|
-
* Collects each file's cross-file dependency probes (via the plugin's
|
|
43564
|
-
* collectors) with their current answers. Fail-open per file: an unreadable /
|
|
43565
|
-
* unparseable / collector-failing file maps to `null` (no entry stored — it
|
|
43566
|
-
* re-lints next scan). The filesystem is frozen for the scan, so this can run
|
|
43567
|
-
* CONCURRENTLY with an oxlint child-process await — the caller starts it
|
|
43568
|
-
* unawaited before a lint pass and awaits it at store time, hiding the
|
|
43569
|
-
* in-process parse + resolution cost under the subprocess wait. The loop
|
|
43570
|
-
* yields on the cooperative time budget so the child's stdout handling (and
|
|
43571
|
-
* sibling fibers) keep flowing; each per-file collection is synchronous, so
|
|
43572
|
-
* the module-level probe recorder never sees interleaved use. Never rejects.
|
|
43573
|
-
*/
|
|
43574
|
-
const collectSidecarProbesForFiles = async (input) => {
|
|
43575
|
-
const buildProbes = (file) => {
|
|
43576
|
-
const absoluteFilePath = Path.resolve(input.rootDirectory, file);
|
|
43577
|
-
let trace;
|
|
43578
|
-
try {
|
|
43579
|
-
trace = collectCrossFileDependencyProbes({
|
|
43580
|
-
absoluteFilePath,
|
|
43581
|
-
sourceText: NFS.readFileSync(absoluteFilePath, "utf8"),
|
|
43582
|
-
ruleIds: input.boundedSidecarRuleIds
|
|
43583
|
-
});
|
|
43584
|
-
} catch {
|
|
43585
|
-
return null;
|
|
43586
|
-
}
|
|
43587
|
-
if (trace === null) return null;
|
|
43588
|
-
const probes = [];
|
|
43589
|
-
for (const contentPath of trace.contentPaths) {
|
|
43590
|
-
const relativePath = input.probeAnswers.toRelativePath(contentPath);
|
|
43591
|
-
probes.push({
|
|
43592
|
-
kind: "content",
|
|
43593
|
-
path: relativePath,
|
|
43594
|
-
answer: input.probeAnswers.answerFor("content", relativePath)
|
|
43595
|
-
});
|
|
43596
|
-
}
|
|
43597
|
-
for (const existencePath of trace.existencePaths) {
|
|
43598
|
-
const relativePath = input.probeAnswers.toRelativePath(existencePath);
|
|
43599
|
-
probes.push({
|
|
43600
|
-
kind: "exists",
|
|
43601
|
-
path: relativePath,
|
|
43602
|
-
answer: input.probeAnswers.answerFor("exists", relativePath)
|
|
43603
|
-
});
|
|
43604
|
-
}
|
|
43605
|
-
return probes;
|
|
43606
|
-
};
|
|
43607
|
-
const probesByFile = /* @__PURE__ */ new Map();
|
|
43608
|
-
let collectSliceStartedAt = performance.now();
|
|
43609
|
-
for (const file of input.files) {
|
|
43610
|
-
probesByFile.set(file, buildProbes(file));
|
|
43611
|
-
if (performance.now() - collectSliceStartedAt >= 12) {
|
|
43612
|
-
await yieldToEventLoop();
|
|
43613
|
-
collectSliceStartedAt = performance.now();
|
|
43614
|
-
}
|
|
43615
|
-
}
|
|
43616
|
-
return probesByFile;
|
|
43617
|
-
};
|
|
43618
|
-
/**
|
|
43619
|
-
* Stores each freshly-linted file's probe set + cross-file diagnostics so
|
|
43620
|
-
* the next scan can replay instead of re-linting.
|
|
43621
|
-
*/
|
|
43622
|
-
const storeSidecarEntries = (input) => {
|
|
43623
|
-
let didStoreAnyEntry = false;
|
|
43624
|
-
for (const pass of input.storablePasses) {
|
|
43625
|
-
if (!pass.isTrusted) continue;
|
|
43626
|
-
const diagnosticsByFile = attributeDiagnosticsToFiles(pass.diagnostics, pass.files);
|
|
43627
|
-
if (diagnosticsByFile === null) continue;
|
|
43628
|
-
for (const file of pass.files) {
|
|
43629
|
-
const cacheKey = input.cacheKeyByFile.get(file);
|
|
43630
|
-
if (cacheKey === void 0) continue;
|
|
43631
|
-
const probes = pass.probesByFile.get(file);
|
|
43632
|
-
if (probes === void 0 || probes === null) continue;
|
|
43633
|
-
input.sidecarCache.store(cacheKey, {
|
|
43634
|
-
probes,
|
|
43635
|
-
diagnostics: diagnosticsByFile.get(file) ?? []
|
|
43636
|
-
});
|
|
43637
|
-
didStoreAnyEntry = true;
|
|
43638
|
-
}
|
|
43639
|
-
}
|
|
43640
|
-
if (didStoreAnyEntry) input.sidecarCache.persist();
|
|
43641
|
-
};
|
|
43642
43309
|
const REACT_HOOKS_JS_DROP_PREFIX = "React Compiler rules (react-hooks-js/*) skipped — eslint-plugin-react-hooks failed to load in this environment";
|
|
43643
43310
|
/**
|
|
43644
43311
|
* Detects an oxlint config-load crash caused by the optional
|
|
@@ -43680,7 +43347,7 @@ const reactHooksJsPluginDropNote = (error) => {
|
|
|
43680
43347
|
* 6. always restore disable directives + clean up the temp dir
|
|
43681
43348
|
*/
|
|
43682
43349
|
const runOxlint = async (options) => {
|
|
43683
|
-
const { rootDirectory, project, includePaths, nodeBinaryPath = process.execPath, customRulesOnly = false, respectInlineDisables = true, adoptExistingLintConfig = true, ignoredTags = /* @__PURE__ */ new Set(), userConfig, configSourceDirectory = rootDirectory, onPartialFailure, perFileLintCacheEnabled = false,
|
|
43350
|
+
const { rootDirectory, project, includePaths, nodeBinaryPath = process.execPath, customRulesOnly = false, respectInlineDisables = true, adoptExistingLintConfig = true, ignoredTags = /* @__PURE__ */ new Set(), userConfig, configSourceDirectory = rootDirectory, onPartialFailure, perFileLintCacheEnabled = false, onCacheStats, spawnTimeoutMs, outputMaxBytes, lintBatchOrdering = "cost" } = options;
|
|
43684
43351
|
const serverAuthFunctionNames = Array.isArray(userConfig?.serverAuthFunctionNames) ? userConfig.serverAuthFunctionNames.filter((entry) => typeof entry === "string" && entry.length > 0) : void 0;
|
|
43685
43352
|
const severityControls = buildRuleSeverityControls(userConfig);
|
|
43686
43353
|
validateRuleRegistration();
|
|
@@ -43698,8 +43365,7 @@ const runOxlint = async (options) => {
|
|
|
43698
43365
|
severityControls,
|
|
43699
43366
|
userPlugins,
|
|
43700
43367
|
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
|
|
43701
|
-
ruleSelection: overrides.ruleSelection
|
|
43702
|
-
sidecarRuleIdFilter: overrides.sidecarRuleIdFilter
|
|
43368
|
+
ruleSelection: overrides.ruleSelection
|
|
43703
43369
|
});
|
|
43704
43370
|
const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
|
|
43705
43371
|
const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
|
|
@@ -43802,18 +43468,15 @@ const runOxlint = async (options) => {
|
|
|
43802
43468
|
});
|
|
43803
43469
|
const cache = createFileLintCache(resolveReactDoctorCacheDir(rootDirectory), rulesetHash);
|
|
43804
43470
|
const cacheKeyByFile = /* @__PURE__ */ new Map();
|
|
43805
|
-
const contentHashByRelativePath = /* @__PURE__ */ new Map();
|
|
43806
43471
|
const missFiles = [];
|
|
43807
43472
|
const replayedDiagnostics = [];
|
|
43808
43473
|
let hashSliceStartedAt = performance.now();
|
|
43809
43474
|
for (const candidateFile of candidateFiles) {
|
|
43810
|
-
const
|
|
43811
|
-
const contentHash = hashFileContents(absoluteCandidatePath);
|
|
43475
|
+
const contentHash = hashFileContents(Path.resolve(rootDirectory, candidateFile));
|
|
43812
43476
|
if (contentHash === null) {
|
|
43813
43477
|
missFiles.push(candidateFile);
|
|
43814
43478
|
continue;
|
|
43815
43479
|
}
|
|
43816
|
-
contentHashByRelativePath.set(Path.relative(rootDirectory, absoluteCandidatePath).replaceAll("\\", "/"), contentHash);
|
|
43817
43480
|
const cacheKey = `${candidateFile.replaceAll("\\", "/")}\u0000${contentHash}`;
|
|
43818
43481
|
cacheKeyByFile.set(candidateFile, cacheKey);
|
|
43819
43482
|
const cachedDiagnostics = cache.lookup(cacheKey);
|
|
@@ -43825,111 +43488,43 @@ const runOxlint = async (options) => {
|
|
|
43825
43488
|
}
|
|
43826
43489
|
}
|
|
43827
43490
|
const cacheHitFileCount = candidateFiles.length - missFiles.length;
|
|
43828
|
-
const enabledSidecarRuleIds = Object.keys(buildConfig({
|
|
43829
|
-
extendsPaths: [],
|
|
43830
|
-
ruleSelection: "sidecar"
|
|
43831
|
-
}).rules).map((ruleKey) => ruleKey.slice(ruleKey.lastIndexOf("/") + 1)).filter((ruleId) => CROSS_FILE_RULE_IDS.has(ruleId));
|
|
43832
|
-
const boundedSidecarRuleIds = enabledSidecarRuleIds.filter((ruleId) => CROSS_FILE_DEPENDENCY_COLLECTORS.has(ruleId));
|
|
43833
|
-
const boundedSidecarRuleIdSet = new Set(boundedSidecarRuleIds);
|
|
43834
|
-
const unboundedSidecarRuleIds = enabledSidecarRuleIds.filter((ruleId) => !CROSS_FILE_DEPENDENCY_COLLECTORS.has(ruleId));
|
|
43835
|
-
const useSidecarCache = sidecarLintCacheEnabled && boundedSidecarRuleIds.length > 0;
|
|
43836
|
-
const sidecarCache = useSidecarCache ? createSidecarLintCache(resolveReactDoctorCacheDir(rootDirectory), computeRulesetHash({
|
|
43837
|
-
config: buildConfig({
|
|
43838
|
-
extendsPaths: [],
|
|
43839
|
-
ruleSelection: "sidecar",
|
|
43840
|
-
sidecarRuleIdFilter: boundedSidecarRuleIdSet
|
|
43841
|
-
}),
|
|
43842
|
-
toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
|
|
43843
|
-
ignorePatterns: combinedPatterns,
|
|
43844
|
-
tsconfigContent
|
|
43845
|
-
})) : null;
|
|
43846
|
-
const probeAnswers = createSidecarProbeAnswerResolver({
|
|
43847
|
-
rootDirectory,
|
|
43848
|
-
contentHashByRelativePath
|
|
43849
|
-
});
|
|
43850
|
-
const missProbesTask = sidecarCache === null ? null : collectSidecarProbesForFiles({
|
|
43851
|
-
files: missFiles,
|
|
43852
|
-
rootDirectory,
|
|
43853
|
-
boundedSidecarRuleIds,
|
|
43854
|
-
probeAnswers
|
|
43855
|
-
});
|
|
43856
43491
|
const fullResult = await runConfigOverFiles((overrides) => buildConfig({
|
|
43857
43492
|
extendsPaths: [],
|
|
43858
43493
|
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin
|
|
43859
43494
|
}), "oxlintrc.full.json", missFiles, options.onFileProgress && ((scannedFileCount) => options.onFileProgress?.(scannedFileCount, candidateFiles.length)));
|
|
43860
43495
|
const missFileSet = new Set(missFiles);
|
|
43861
|
-
const
|
|
43862
|
-
const sidecarReplayedDiagnostics = [];
|
|
43863
|
-
const sidecarLintFiles = [];
|
|
43864
|
-
if (sidecarCache === null) sidecarLintFiles.push(...hitFiles);
|
|
43865
|
-
else {
|
|
43866
|
-
let verifySliceStartedAt = performance.now();
|
|
43867
|
-
for (const hitFile of hitFiles) {
|
|
43868
|
-
const cacheKey = cacheKeyByFile.get(hitFile);
|
|
43869
|
-
const entry = cacheKey === void 0 ? null : sidecarCache.lookup(cacheKey);
|
|
43870
|
-
if (entry !== null && entry.probes.every((probe) => probeAnswers.answerFor(probe.kind, probe.path) === probe.answer)) sidecarReplayedDiagnostics.push(...entry.diagnostics);
|
|
43871
|
-
else sidecarLintFiles.push(hitFile);
|
|
43872
|
-
if (performance.now() - verifySliceStartedAt >= 12) {
|
|
43873
|
-
await yieldToEventLoop();
|
|
43874
|
-
verifySliceStartedAt = performance.now();
|
|
43875
|
-
}
|
|
43876
|
-
}
|
|
43877
|
-
}
|
|
43878
|
-
const sidecarProbesTask = sidecarCache === null ? null : collectSidecarProbesForFiles({
|
|
43879
|
-
files: sidecarLintFiles,
|
|
43880
|
-
rootDirectory,
|
|
43881
|
-
boundedSidecarRuleIds,
|
|
43882
|
-
probeAnswers
|
|
43883
|
-
});
|
|
43884
|
-
const sidecarReplayedFileCount = hitFiles.length - sidecarLintFiles.length;
|
|
43885
|
-
if (sidecarReplayedFileCount > 0) options.onFileProgress?.(missFiles.length + sidecarReplayedFileCount, candidateFiles.length);
|
|
43886
|
-
const boundedSidecarResult = await runConfigOverFiles(() => buildConfig({
|
|
43887
|
-
extendsPaths: [],
|
|
43888
|
-
ruleSelection: "sidecar",
|
|
43889
|
-
...useSidecarCache ? { sidecarRuleIdFilter: boundedSidecarRuleIdSet } : {}
|
|
43890
|
-
}), "oxlintrc.sidecar.json", sidecarLintFiles, options.onFileProgress && ((scannedFileCount) => options.onFileProgress?.(missFiles.length + sidecarReplayedFileCount + scannedFileCount, candidateFiles.length)));
|
|
43891
|
-
const unboundedSidecarResult = useSidecarCache && unboundedSidecarRuleIds.length > 0 ? await runConfigOverFiles(() => buildConfig({
|
|
43496
|
+
const sidecarResult = await runConfigOverFiles(() => buildConfig({
|
|
43892
43497
|
extendsPaths: [],
|
|
43893
|
-
ruleSelection: "sidecar"
|
|
43894
|
-
|
|
43895
|
-
}), "oxlintrc.sidecar-unbounded.json", hitFiles, void 0) : {
|
|
43896
|
-
diagnostics: [],
|
|
43897
|
-
didDropReactHooksJsPlugin: false,
|
|
43898
|
-
hadPartialFailure: false
|
|
43899
|
-
};
|
|
43498
|
+
ruleSelection: "sidecar"
|
|
43499
|
+
}), "oxlintrc.sidecar.json", candidateFiles.filter((candidateFile) => !missFileSet.has(candidateFile)), options.onFileProgress && ((scannedFileCount) => options.onFileProgress?.(missFiles.length + scannedFileCount, candidateFiles.length)));
|
|
43900
43500
|
const freshCacheableDiagnostics = [];
|
|
43901
43501
|
for (const diagnostic of fullResult.diagnostics) if (!CROSS_FILE_RULE_IDS.has(diagnostic.rule)) freshCacheableDiagnostics.push(diagnostic);
|
|
43902
43502
|
onCacheStats?.(cacheHitFileCount, candidateFiles.length);
|
|
43903
|
-
|
|
43904
|
-
const
|
|
43905
|
-
|
|
43503
|
+
const missFileByNormalizedPath = /* @__PURE__ */ new Map();
|
|
43504
|
+
for (const missFile of missFiles) missFileByNormalizedPath.set(missFile.replaceAll("\\", "/"), missFile);
|
|
43505
|
+
const freshDiagnosticsByFile = /* @__PURE__ */ new Map();
|
|
43506
|
+
let isAttributionSound = true;
|
|
43507
|
+
for (const diagnostic of freshCacheableDiagnostics) {
|
|
43508
|
+
const missFile = missFileByNormalizedPath.get(diagnostic.filePath);
|
|
43509
|
+
if (missFile === void 0) {
|
|
43510
|
+
isAttributionSound = false;
|
|
43511
|
+
break;
|
|
43512
|
+
}
|
|
43513
|
+
const fileDiagnostics = freshDiagnosticsByFile.get(missFile) ?? [];
|
|
43514
|
+
fileDiagnostics.push(diagnostic);
|
|
43515
|
+
freshDiagnosticsByFile.set(missFile, fileDiagnostics);
|
|
43516
|
+
}
|
|
43517
|
+
if (!fullResult.didDropReactHooksJsPlugin && !fullResult.hadPartialFailure && isAttributionSound) {
|
|
43906
43518
|
for (const missFile of missFiles) {
|
|
43907
43519
|
const cacheKey = cacheKeyByFile.get(missFile);
|
|
43908
43520
|
if (cacheKey !== void 0) cache.store(cacheKey, freshDiagnosticsByFile.get(missFile) ?? []);
|
|
43909
43521
|
}
|
|
43910
43522
|
cache.persist();
|
|
43911
43523
|
}
|
|
43912
|
-
if (sidecarCache !== null && missProbesTask !== null && sidecarProbesTask !== null) storeSidecarEntries({
|
|
43913
|
-
sidecarCache,
|
|
43914
|
-
cacheKeyByFile,
|
|
43915
|
-
storablePasses: [{
|
|
43916
|
-
files: sidecarLintFiles,
|
|
43917
|
-
diagnostics: boundedSidecarResult.diagnostics,
|
|
43918
|
-
isTrusted: !boundedSidecarResult.hadPartialFailure,
|
|
43919
|
-
probesByFile: await sidecarProbesTask
|
|
43920
|
-
}, {
|
|
43921
|
-
files: missFiles,
|
|
43922
|
-
diagnostics: fullResult.diagnostics.filter((diagnostic) => boundedSidecarRuleIdSet.has(diagnostic.rule)),
|
|
43923
|
-
isTrusted: !fullResult.didDropReactHooksJsPlugin && !fullResult.hadPartialFailure,
|
|
43924
|
-
probesByFile: await missProbesTask
|
|
43925
|
-
}]
|
|
43926
|
-
});
|
|
43927
43524
|
return dedupeDiagnostics([
|
|
43928
43525
|
...replayedDiagnostics,
|
|
43929
43526
|
...fullResult.diagnostics,
|
|
43930
|
-
...
|
|
43931
|
-
...boundedSidecarResult.diagnostics,
|
|
43932
|
-
...unboundedSidecarResult.diagnostics
|
|
43527
|
+
...sidecarResult.diagnostics
|
|
43933
43528
|
]);
|
|
43934
43529
|
}
|
|
43935
43530
|
const baseArgs = makeBaseArgs(configPath);
|
|
@@ -44027,7 +43622,6 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
|
|
|
44027
43622
|
const concurrency = yield* OxlintConcurrency;
|
|
44028
43623
|
const lintBatchOrdering = yield* LintBatchOrdering;
|
|
44029
43624
|
const perFileLintCacheEnabled = yield* PerFileLintCacheEnabled;
|
|
44030
|
-
const sidecarLintCacheEnabled = yield* SidecarLintCacheEnabled;
|
|
44031
43625
|
const collectedFailures = [];
|
|
44032
43626
|
const diagnostics = yield* tryPromise({
|
|
44033
43627
|
try: (signal) => runOxlint({
|
|
@@ -44046,9 +43640,7 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
|
|
|
44046
43640
|
},
|
|
44047
43641
|
onFileProgress: input.onFileProgress,
|
|
44048
43642
|
perFileLintCacheEnabled,
|
|
44049
|
-
sidecarLintCacheEnabled,
|
|
44050
43643
|
onCacheStats: input.onCacheStats,
|
|
44051
|
-
onSidecarStats: input.onSidecarStats,
|
|
44052
43644
|
spawnTimeoutMs,
|
|
44053
43645
|
outputMaxBytes,
|
|
44054
43646
|
concurrency,
|
|
@@ -44470,20 +44062,6 @@ const writeCachedSocketBody = (cacheFile, body) => {
|
|
|
44470
44062
|
}));
|
|
44471
44063
|
} catch {}
|
|
44472
44064
|
};
|
|
44473
|
-
const pruneExpiredSocketCache = (cacheDirectory) => {
|
|
44474
|
-
try {
|
|
44475
|
-
const supplyChainCacheDirectory = Path.join(cacheDirectory, SUPPLY_CHAIN_CACHE_SUBDIR);
|
|
44476
|
-
const expiryThresholdMs = Date.now() - SUPPLY_CHAIN_CACHE_TTL_MS;
|
|
44477
|
-
for (const entryName of NFS.readdirSync(supplyChainCacheDirectory)) {
|
|
44478
|
-
const entryPath = Path.join(supplyChainCacheDirectory, entryName);
|
|
44479
|
-
try {
|
|
44480
|
-
if (NFS.statSync(entryPath).mtimeMs < expiryThresholdMs) NFS.rmSync(entryPath);
|
|
44481
|
-
} catch {
|
|
44482
|
-
continue;
|
|
44483
|
-
}
|
|
44484
|
-
}
|
|
44485
|
-
} catch {}
|
|
44486
|
-
};
|
|
44487
44065
|
const fetchSocketArtifact = (dependency, cacheDirectory) => tryPromise(async (signal) => {
|
|
44488
44066
|
const cacheFile = cacheDirectory === null ? null : supplyChainCacheFile(cacheDirectory, dependency);
|
|
44489
44067
|
if (cacheFile !== null) {
|
|
@@ -44611,7 +44189,6 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
44611
44189
|
const dependencies = collectDependenciesToScore(readPackageJson$1(packageJsonPath), readPackageJsonText(packageJsonPath), options.includeDevDependencies);
|
|
44612
44190
|
if (dependencies.length === 0) return [];
|
|
44613
44191
|
const cacheDirectory = isSupplyChainCacheDisabled() ? null : resolveReactDoctorCacheDir(input.rootDirectory);
|
|
44614
|
-
if (cacheDirectory !== null) pruneExpiredSocketCache(cacheDirectory);
|
|
44615
44192
|
const artifacts = yield* forEach$1(dependencies, (dependency) => fetchSocketArtifact(dependency, cacheDirectory), { concurrency: 8 }).pipe(timeoutOption(input.totalTimeoutMs ?? 9e4), map$3((maybeArtifacts) => getOrElse$1(maybeArtifacts, () => [])));
|
|
44616
44193
|
const diagnostics = [];
|
|
44617
44194
|
for (let index = 0; index < dependencies.length; index += 1) {
|
|
@@ -44841,10 +44418,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
44841
44418
|
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
|
|
44842
44419
|
onCacheOutcome: (didHitCache) => {
|
|
44843
44420
|
deadCodeCacheHit = didHitCache;
|
|
44844
|
-
},
|
|
44845
|
-
onSummaryCacheStats: (stats) => {
|
|
44846
|
-
deadCodeSummaryCacheHits = stats.hits;
|
|
44847
|
-
deadCodeSummaryCacheMisses = stats.misses;
|
|
44848
44421
|
}
|
|
44849
44422
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
44850
44423
|
yield* set(deadCodeFailure, {
|
|
@@ -44873,11 +44446,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
44873
44446
|
let lastReportedTotalFileCount = 0;
|
|
44874
44447
|
let lintCacheHitFileCount = null;
|
|
44875
44448
|
let lintCacheTotalFileCount = null;
|
|
44876
|
-
let lintSidecarReplayedFileCount = null;
|
|
44877
|
-
let lintSidecarTotalFileCount = null;
|
|
44878
44449
|
let deadCodeCacheHit = null;
|
|
44879
|
-
let deadCodeSummaryCacheHits = null;
|
|
44880
|
-
let deadCodeSummaryCacheMisses = null;
|
|
44881
44450
|
const baseLintStream = linterService.run({
|
|
44882
44451
|
rootDirectory: scanDirectory,
|
|
44883
44452
|
project,
|
|
@@ -44897,10 +44466,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
44897
44466
|
lintCacheHitFileCount = cacheHitFileCount;
|
|
44898
44467
|
lintCacheTotalFileCount = totalConsideredFileCount;
|
|
44899
44468
|
},
|
|
44900
|
-
onSidecarStats: (sidecarReplayedFileCount, sidecarConsideredFileCount) => {
|
|
44901
|
-
lintSidecarReplayedFileCount = sidecarReplayedFileCount;
|
|
44902
|
-
lintSidecarTotalFileCount = sidecarConsideredFileCount;
|
|
44903
|
-
},
|
|
44904
44469
|
deadlineEpochMs: input.deadlineEpochMs
|
|
44905
44470
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
44906
44471
|
yield* set(lintFailure, {
|
|
@@ -45011,11 +44576,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
45011
44576
|
securityScanFailed,
|
|
45012
44577
|
lintCacheHitFileCount,
|
|
45013
44578
|
lintCacheTotalFileCount,
|
|
45014
|
-
lintSidecarReplayedFileCount,
|
|
45015
|
-
lintSidecarTotalFileCount,
|
|
45016
44579
|
deadCodeCacheHit: lintFailureState.didFail ? null : deadCodeCacheHit,
|
|
45017
|
-
deadCodeSummaryCacheHits: lintFailureState.didFail ? null : deadCodeSummaryCacheHits,
|
|
45018
|
-
deadCodeSummaryCacheMisses: lintFailureState.didFail ? null : deadCodeSummaryCacheMisses,
|
|
45019
44580
|
suppressedRuleCounts: transform.summarizeSuppressions()
|
|
45020
44581
|
};
|
|
45021
44582
|
}).pipe(withSpan("runInspect", { attributes: {
|
|
@@ -45497,9 +45058,11 @@ const computeConfigFingerprint = (projectDirectory, version) => {
|
|
|
45497
45058
|
const parts = [`v=${version}`];
|
|
45498
45059
|
let directory = projectDirectory;
|
|
45499
45060
|
for (;;) {
|
|
45500
|
-
for (const filename of CONFIG_FINGERPRINT_FILENAMES) {
|
|
45501
|
-
const
|
|
45502
|
-
|
|
45061
|
+
for (const filename of CONFIG_FINGERPRINT_FILENAMES) try {
|
|
45062
|
+
const stat = fs.statSync(path.join(directory, filename));
|
|
45063
|
+
parts.push(`${directory}/${filename}=${stat.mtimeMs}:${stat.size}`);
|
|
45064
|
+
} catch {
|
|
45065
|
+
continue;
|
|
45503
45066
|
}
|
|
45504
45067
|
const parent = path.dirname(directory);
|
|
45505
45068
|
if (parent === directory) break;
|
|
@@ -45795,7 +45358,6 @@ const isNonInteractiveEnvironment = () => NON_INTERACTIVE_ENVIRONMENT_VARIABLES.
|
|
|
45795
45358
|
const REACT_DOCTOR_CONFIG_PROJECT_NAME = "react-doctor";
|
|
45796
45359
|
const STAGED_FILES_TEMP_DIR_PREFIX = "react-doctor-staged-";
|
|
45797
45360
|
const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
|
|
45798
|
-
const SCAN_RESULT_CACHE_FILENAME = "scan-cache.json";
|
|
45799
45361
|
const RUN_GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
45800
45362
|
const GH_DEFAULT_BRANCH_PROBE_TIMEOUT_MS = 5e3;
|
|
45801
45363
|
const CI_TRUST_COMPANIES = "PayPal, Rippling, and Alibaba";
|
|
@@ -45887,7 +45449,7 @@ const makeNoopConsole = () => ({
|
|
|
45887
45449
|
});
|
|
45888
45450
|
//#endregion
|
|
45889
45451
|
//#region src/cli/utils/version.ts
|
|
45890
|
-
const VERSION = "0.6.3-dev.
|
|
45452
|
+
const VERSION = "0.6.3-dev.e257a5e";
|
|
45891
45453
|
//#endregion
|
|
45892
45454
|
//#region src/cli/utils/json-mode.ts
|
|
45893
45455
|
let context = null;
|
|
@@ -46256,13 +45818,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
46256
45818
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
46257
45819
|
* standard `SENTRY_RELEASE` override.
|
|
46258
45820
|
*/
|
|
46259
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.6.3-dev.
|
|
45821
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.6.3-dev.e257a5e`;
|
|
46260
45822
|
/**
|
|
46261
45823
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
46262
45824
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
46263
45825
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
46264
45826
|
*/
|
|
46265
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.6.3-dev.
|
|
45827
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.6.3-dev.e257a5e") ? "development" : "production");
|
|
46266
45828
|
/**
|
|
46267
45829
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
46268
45830
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -58340,276 +57902,6 @@ const buildRuleBlastRadii = (diagnostics) => buildSortedRuleGroups(diagnostics).
|
|
|
58340
57902
|
})).toSorted((left, right) => right.fileCount - left.fileCount);
|
|
58341
57903
|
const findMigrationScaleBuckets = (diagnostics) => buildRuleBlastRadii(diagnostics).filter((bucket) => bucket.fileCount >= 40);
|
|
58342
57904
|
//#endregion
|
|
58343
|
-
//#region src/cli/utils/scan-result-cache.ts
|
|
58344
|
-
const CACHE_DISABLED_VALUES = new Set(["1", "true"]);
|
|
58345
|
-
const TOOLCHAIN_PACKAGE_SPECIFIERS = [
|
|
58346
|
-
"oxlint/package.json",
|
|
58347
|
-
"oxlint-plugin-react-doctor/package.json",
|
|
58348
|
-
"deslop-js/package.json",
|
|
58349
|
-
"eslint-plugin-react-hooks/package.json"
|
|
58350
|
-
];
|
|
58351
|
-
const bundledRequire = createRequire(import.meta.url);
|
|
58352
|
-
const normalizeForStableJson = (value) => {
|
|
58353
|
-
if (value === null) return null;
|
|
58354
|
-
if (value === void 0) return void 0;
|
|
58355
|
-
const valueType = typeof value;
|
|
58356
|
-
if (valueType === "string" || valueType === "number" || valueType === "boolean") return value;
|
|
58357
|
-
if (valueType === "bigint" || valueType === "function" || valueType === "symbol") throw new Error("Unsupported cache key value");
|
|
58358
|
-
if (Array.isArray(value)) return value.map((item) => {
|
|
58359
|
-
const normalized = normalizeForStableJson(item);
|
|
58360
|
-
return normalized === void 0 ? null : normalized;
|
|
58361
|
-
});
|
|
58362
|
-
const prototype = Object.getPrototypeOf(value);
|
|
58363
|
-
if (prototype !== Object.prototype && prototype !== null) throw new Error("Unsupported cache key object");
|
|
58364
|
-
if (!isRecord(value)) throw new Error("Unsupported cache key object");
|
|
58365
|
-
const normalizedRecord = {};
|
|
58366
|
-
for (const key of Object.keys(value).sort()) {
|
|
58367
|
-
const normalized = normalizeForStableJson(value[key]);
|
|
58368
|
-
if (normalized !== void 0) normalizedRecord[key] = normalized;
|
|
58369
|
-
}
|
|
58370
|
-
return normalizedRecord;
|
|
58371
|
-
};
|
|
58372
|
-
const stringifyStableJson = (value) => {
|
|
58373
|
-
try {
|
|
58374
|
-
return JSON.stringify(normalizeForStableJson(value));
|
|
58375
|
-
} catch {
|
|
58376
|
-
return null;
|
|
58377
|
-
}
|
|
58378
|
-
};
|
|
58379
|
-
const hashString = (value) => crypto.createHash("sha1").update(value).digest("hex");
|
|
58380
|
-
const readHeadSha = (projectDirectory) => runGit(projectDirectory, ["rev-parse", "HEAD"]);
|
|
58381
|
-
const parseWorktreeStatusRecords = (statusOutput) => {
|
|
58382
|
-
const entries = [];
|
|
58383
|
-
const tokens = statusOutput.split("\0");
|
|
58384
|
-
for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex += 1) {
|
|
58385
|
-
let record = tokens[tokenIndex];
|
|
58386
|
-
if (record.length === 0) continue;
|
|
58387
|
-
if (record.length < 3 || record[2] !== " ") {
|
|
58388
|
-
if (tokenIndex !== 0) return null;
|
|
58389
|
-
record = ` ${record}`;
|
|
58390
|
-
}
|
|
58391
|
-
if (record.length < 4 || record[2] !== " ") return null;
|
|
58392
|
-
const statusCode = record.slice(0, 2);
|
|
58393
|
-
const recordPath = record.slice(3);
|
|
58394
|
-
if (statusCode.includes("R") || statusCode.includes("C")) {
|
|
58395
|
-
tokenIndex += 1;
|
|
58396
|
-
const originPath = tokens[tokenIndex];
|
|
58397
|
-
if (originPath === void 0 || originPath.length === 0) return null;
|
|
58398
|
-
entries.push({
|
|
58399
|
-
statusCode,
|
|
58400
|
-
path: recordPath,
|
|
58401
|
-
originPath
|
|
58402
|
-
});
|
|
58403
|
-
continue;
|
|
58404
|
-
}
|
|
58405
|
-
entries.push({
|
|
58406
|
-
statusCode,
|
|
58407
|
-
path: recordPath
|
|
58408
|
-
});
|
|
58409
|
-
}
|
|
58410
|
-
return entries;
|
|
58411
|
-
};
|
|
58412
|
-
const buildDirtyPathContentFingerprint = (absolutePath, statusCode) => {
|
|
58413
|
-
let stats;
|
|
58414
|
-
try {
|
|
58415
|
-
stats = fs.lstatSync(absolutePath);
|
|
58416
|
-
} catch {
|
|
58417
|
-
return statusCode.includes("D") ? "deleted" : null;
|
|
58418
|
-
}
|
|
58419
|
-
if (stats.isSymbolicLink()) try {
|
|
58420
|
-
return `link:${fs.readlinkSync(absolutePath)}`;
|
|
58421
|
-
} catch {
|
|
58422
|
-
return null;
|
|
58423
|
-
}
|
|
58424
|
-
if (!stats.isFile()) return null;
|
|
58425
|
-
if (stats.size > 10485760) return `stat:${stats.mtimeMs}:${stats.size}`;
|
|
58426
|
-
return hashFileContents(absolutePath);
|
|
58427
|
-
};
|
|
58428
|
-
const buildWorktreeFingerprint = (projectDirectory) => {
|
|
58429
|
-
const statusOutput = runGit(projectDirectory, [
|
|
58430
|
-
"status",
|
|
58431
|
-
"--porcelain=v1",
|
|
58432
|
-
"-z",
|
|
58433
|
-
"--untracked-files=all"
|
|
58434
|
-
]);
|
|
58435
|
-
if (statusOutput === null) return null;
|
|
58436
|
-
if (statusOutput.length === 0) return [];
|
|
58437
|
-
const statusEntries = parseWorktreeStatusRecords(statusOutput);
|
|
58438
|
-
if (statusEntries === null) return null;
|
|
58439
|
-
if (statusEntries.length > 300) return null;
|
|
58440
|
-
const repositoryRoot = runGit(projectDirectory, ["rev-parse", "--show-toplevel"]);
|
|
58441
|
-
if (repositoryRoot === null) return null;
|
|
58442
|
-
const dirtyEntries = [];
|
|
58443
|
-
for (const statusEntry of statusEntries) {
|
|
58444
|
-
const contentFingerprint = buildDirtyPathContentFingerprint(path.join(repositoryRoot, statusEntry.path), statusEntry.statusCode);
|
|
58445
|
-
if (contentFingerprint === null) return null;
|
|
58446
|
-
dirtyEntries.push({
|
|
58447
|
-
...statusEntry,
|
|
58448
|
-
contentFingerprint
|
|
58449
|
-
});
|
|
58450
|
-
}
|
|
58451
|
-
return dirtyEntries.sort((firstEntry, secondEntry) => {
|
|
58452
|
-
const firstSortKey = `${firstEntry.path}${firstEntry.statusCode}`;
|
|
58453
|
-
const secondSortKey = `${secondEntry.path}${secondEntry.statusCode}`;
|
|
58454
|
-
return firstSortKey < secondSortKey ? -1 : firstSortKey > secondSortKey ? 1 : 0;
|
|
58455
|
-
});
|
|
58456
|
-
};
|
|
58457
|
-
const DOTENV_FILE_NAME_PATTERN = /^\.env(\.|$)/;
|
|
58458
|
-
const resolveDotenvFingerprint = (projectDirectory) => {
|
|
58459
|
-
try {
|
|
58460
|
-
return fs.readdirSync(projectDirectory).filter((entryName) => DOTENV_FILE_NAME_PATTERN.test(entryName)).sort().map((entryName) => `${entryName}=${hashFileContents(path.join(projectDirectory, entryName)) ?? "unreadable"}`);
|
|
58461
|
-
} catch {
|
|
58462
|
-
return [];
|
|
58463
|
-
}
|
|
58464
|
-
};
|
|
58465
|
-
const hasHiddenTrackedFileState = (projectDirectory) => {
|
|
58466
|
-
const output = runGit(projectDirectory, ["ls-files", "-v"]);
|
|
58467
|
-
if (output === null) return true;
|
|
58468
|
-
return output.split("\n").some((line) => line.length > 0 && line[0] !== "H");
|
|
58469
|
-
};
|
|
58470
|
-
const resolveCacheFilePath = (projectDirectory) => path.join(resolveReactDoctorCacheDir(projectDirectory), SCAN_RESULT_CACHE_FILENAME);
|
|
58471
|
-
const readPersistedCache = (cacheFilePath) => {
|
|
58472
|
-
try {
|
|
58473
|
-
const parsed = JSON.parse(fs.readFileSync(cacheFilePath, "utf8"));
|
|
58474
|
-
if (!isRecord(parsed) || parsed.version !== 3) return {
|
|
58475
|
-
version: 3,
|
|
58476
|
-
entries: []
|
|
58477
|
-
};
|
|
58478
|
-
if (!Array.isArray(parsed.entries)) return {
|
|
58479
|
-
version: 3,
|
|
58480
|
-
entries: []
|
|
58481
|
-
};
|
|
58482
|
-
const entries = [];
|
|
58483
|
-
for (const entry of parsed.entries) {
|
|
58484
|
-
if (!isRecord(entry) || typeof entry.key !== "string" || typeof entry.createdAtMs !== "number") continue;
|
|
58485
|
-
if (!isRecord(entry.payload) || !Array.isArray(entry.payload.diagnostics)) continue;
|
|
58486
|
-
entries.push(entry);
|
|
58487
|
-
}
|
|
58488
|
-
return {
|
|
58489
|
-
version: 3,
|
|
58490
|
-
entries
|
|
58491
|
-
};
|
|
58492
|
-
} catch {
|
|
58493
|
-
return {
|
|
58494
|
-
version: 3,
|
|
58495
|
-
entries: []
|
|
58496
|
-
};
|
|
58497
|
-
}
|
|
58498
|
-
};
|
|
58499
|
-
const writePersistedCache = (cacheFilePath, cache) => {
|
|
58500
|
-
try {
|
|
58501
|
-
fs.mkdirSync(path.dirname(cacheFilePath), { recursive: true });
|
|
58502
|
-
const tempPath = `${cacheFilePath}.${process.pid}.tmp`;
|
|
58503
|
-
fs.writeFileSync(tempPath, JSON.stringify(cache));
|
|
58504
|
-
fs.renameSync(tempPath, cacheFilePath);
|
|
58505
|
-
} catch {
|
|
58506
|
-
return;
|
|
58507
|
-
}
|
|
58508
|
-
};
|
|
58509
|
-
/**
|
|
58510
|
-
* The global cache off-switch (`REACT_DOCTOR_NO_CACHE`), which disables every
|
|
58511
|
-
* cache subsystem: this whole-repo scan cache plus core's per-file lint,
|
|
58512
|
-
* sidecar, and dead-code caches (their `Context.Reference` defaults read the
|
|
58513
|
-
* same variable). Exported for the wide event's `cache.temperature`
|
|
58514
|
-
* derivation, which reports `"disabled"` instead of `"cold"` when the switch
|
|
58515
|
-
* is on. Granular knobs (`REACT_DOCTOR_NO_FILE_CACHE`, …) are deliberately
|
|
58516
|
-
* not consulted — they leave the other subsystems live.
|
|
58517
|
-
*/
|
|
58518
|
-
const isCacheGloballyDisabled = () => CACHE_DISABLED_VALUES.has(process.env.REACT_DOCTOR_NO_CACHE?.toLowerCase() ?? "");
|
|
58519
|
-
const resolveProjectIdentity = (projectDirectory) => {
|
|
58520
|
-
try {
|
|
58521
|
-
return fs.realpathSync.native(projectDirectory);
|
|
58522
|
-
} catch {
|
|
58523
|
-
return path.resolve(projectDirectory);
|
|
58524
|
-
}
|
|
58525
|
-
};
|
|
58526
|
-
const fileFingerprint = (filePath) => {
|
|
58527
|
-
try {
|
|
58528
|
-
const stat = fs.statSync(filePath);
|
|
58529
|
-
return `${filePath}:${stat.mtimeMs}:${stat.size}`;
|
|
58530
|
-
} catch {
|
|
58531
|
-
return null;
|
|
58532
|
-
}
|
|
58533
|
-
};
|
|
58534
|
-
const resolveToolchainFingerprint = (nodeBinaryPath) => {
|
|
58535
|
-
const fingerprints = [];
|
|
58536
|
-
if (nodeBinaryPath !== null) fingerprints.push(nodeBinaryPath === process.execPath ? `node=${process.version}` : `node=${fileFingerprint(nodeBinaryPath) ?? "unreadable"}`);
|
|
58537
|
-
for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS) try {
|
|
58538
|
-
const packageJson = bundledRequire(specifier);
|
|
58539
|
-
const version = typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
58540
|
-
fingerprints.push(`${specifier}=${version}`);
|
|
58541
|
-
} catch {
|
|
58542
|
-
fingerprints.push(`${specifier}=missing`);
|
|
58543
|
-
}
|
|
58544
|
-
return fingerprints;
|
|
58545
|
-
};
|
|
58546
|
-
const buildScanResultCacheKey = (input) => {
|
|
58547
|
-
if (isCacheGloballyDisabled()) return null;
|
|
58548
|
-
if (hasHiddenTrackedFileState(input.projectDirectory)) return null;
|
|
58549
|
-
const worktreeFingerprint = buildWorktreeFingerprint(input.projectDirectory);
|
|
58550
|
-
if (worktreeFingerprint === null) return null;
|
|
58551
|
-
const headSha = readHeadSha(input.projectDirectory);
|
|
58552
|
-
if (headSha === null) return null;
|
|
58553
|
-
if (stringifyStableJson(input.userConfig) === null) return null;
|
|
58554
|
-
const cacheKeyJson = stringifyStableJson({
|
|
58555
|
-
schemaVersion: 3,
|
|
58556
|
-
projectIdentity: resolveProjectIdentity(input.projectDirectory),
|
|
58557
|
-
headSha,
|
|
58558
|
-
worktreeFingerprint,
|
|
58559
|
-
dotenvFingerprint: resolveDotenvFingerprint(input.projectDirectory),
|
|
58560
|
-
reactDoctorVersion: input.version,
|
|
58561
|
-
nodeVersion: process.version,
|
|
58562
|
-
toolchainFingerprint: resolveToolchainFingerprint(input.nodeBinaryPath),
|
|
58563
|
-
configFingerprint: computeConfigFingerprint(input.projectDirectory, input.version),
|
|
58564
|
-
hasConfigOverride: input.hasConfigOverride,
|
|
58565
|
-
configSourceDirectory: input.configSourceDirectory,
|
|
58566
|
-
userConfig: input.userConfig,
|
|
58567
|
-
engineOptions: {
|
|
58568
|
-
lint: input.options.lint,
|
|
58569
|
-
deadCode: input.options.deadCode,
|
|
58570
|
-
includePaths: [...input.options.includePaths].sort(),
|
|
58571
|
-
customRulesOnly: input.options.customRulesOnly,
|
|
58572
|
-
respectInlineDisables: input.options.respectInlineDisables,
|
|
58573
|
-
warnings: input.options.warnings,
|
|
58574
|
-
adoptExistingLintConfig: input.options.adoptExistingLintConfig,
|
|
58575
|
-
ignoredTags: [...input.options.ignoredTags].sort(),
|
|
58576
|
-
concurrency: input.options.concurrency,
|
|
58577
|
-
lintBatchOrdering: resolveLintBatchOrdering(),
|
|
58578
|
-
baselineRef: input.options.baseline?.ref,
|
|
58579
|
-
changedLineRanges: input.options.changedLineRanges ?? void 0,
|
|
58580
|
-
noScore: input.options.noScore,
|
|
58581
|
-
isCi: input.options.isCi,
|
|
58582
|
-
suppressRendering: input.options.suppressRendering,
|
|
58583
|
-
supplyChainManifestChanged: input.options.supplyChainManifestChanged
|
|
58584
|
-
}
|
|
58585
|
-
});
|
|
58586
|
-
return cacheKeyJson === null ? null : hashString(cacheKeyJson);
|
|
58587
|
-
};
|
|
58588
|
-
const createScanResultCache = (projectDirectory) => {
|
|
58589
|
-
const cacheFilePath = resolveCacheFilePath(projectDirectory);
|
|
58590
|
-
const persistedCache = readPersistedCache(cacheFilePath);
|
|
58591
|
-
const entries = /* @__PURE__ */ new Map();
|
|
58592
|
-
for (const entry of persistedCache.entries) entries.set(entry.key, entry);
|
|
58593
|
-
const persist = () => {
|
|
58594
|
-
writePersistedCache(cacheFilePath, {
|
|
58595
|
-
version: 3,
|
|
58596
|
-
entries: [...entries.values()].sort((firstEntry, secondEntry) => secondEntry.createdAtMs - firstEntry.createdAtMs).slice(0, 20)
|
|
58597
|
-
});
|
|
58598
|
-
};
|
|
58599
|
-
return {
|
|
58600
|
-
lookup: (key) => entries.get(key)?.payload ?? null,
|
|
58601
|
-
store: (key, payload) => {
|
|
58602
|
-
entries.set(key, {
|
|
58603
|
-
key,
|
|
58604
|
-
createdAtMs: Date.now(),
|
|
58605
|
-
payload
|
|
58606
|
-
});
|
|
58607
|
-
persist();
|
|
58608
|
-
}
|
|
58609
|
-
};
|
|
58610
|
-
};
|
|
58611
|
-
const shouldStoreScanPayload = (payload) => !payload.didLintFail && !payload.didDeadCodeFail && payload.lintPartialFailures.length === 0 && !payload.supplyChainOverlapTimedOut && payload.securityScanFailed !== true;
|
|
58612
|
-
//#endregion
|
|
58613
57905
|
//#region src/cli/utils/should-block-ci.ts
|
|
58614
57906
|
const shouldBlockCi = (diagnostics, blockingLevel) => {
|
|
58615
57907
|
if (blockingLevel === "none") return false;
|
|
@@ -58651,63 +57943,6 @@ const readEnvBoolean = (name) => {
|
|
|
58651
57943
|
if (value === void 0) return null;
|
|
58652
57944
|
return value.toLowerCase() === "true" || value === "1";
|
|
58653
57945
|
};
|
|
58654
|
-
const ratioOf = (numerator, denominator) => denominator != null && denominator > 0 ? (numerator ?? 0) / denominator : null;
|
|
58655
|
-
const resolveDeadCodeReuseRatio = (result) => {
|
|
58656
|
-
if (result.deadCodeCacheHit === true) return 1;
|
|
58657
|
-
const summaryTotal = (result.deadCodeSummaryCacheHits ?? 0) + (result.deadCodeSummaryCacheMisses ?? 0);
|
|
58658
|
-
if (summaryTotal > 0) return (result.deadCodeSummaryCacheHits ?? 0) / summaryTotal;
|
|
58659
|
-
return result.deadCodeCacheHit === false ? 0 : null;
|
|
58660
|
-
};
|
|
58661
|
-
/**
|
|
58662
|
-
* One queryable cache temperature per scan, derived from the whole stack:
|
|
58663
|
-
*
|
|
58664
|
-
* - `"turbo"` — whole-repo scan-result replay (the explicit
|
|
58665
|
-
* `wholeRepoCacheHit` flag from the CLI's cachedPayload
|
|
58666
|
-
* branch; no scan work ran).
|
|
58667
|
-
* - `"warm"` — any incremental reuse: per-file lint hits, sidecar
|
|
58668
|
-
* replays, a dead-code whole-result hit, or dead-code
|
|
58669
|
-
* summary-cache hits.
|
|
58670
|
-
* - `"disabled"` — zero reuse because the global `REACT_DOCTOR_NO_CACHE`
|
|
58671
|
-
* off-switch is on. Granular per-cache opt-outs
|
|
58672
|
-
* (`REACT_DOCTOR_NO_FILE_CACHE`, …) still read warm/cold,
|
|
58673
|
-
* since the other subsystems stay live.
|
|
58674
|
-
* - `"cold"` — caches on, zero reuse (first scan / everything changed).
|
|
58675
|
-
*
|
|
58676
|
-
* `cache.warmth` is the headline reuse magnitude in [0, 1]: the plain mean of
|
|
58677
|
-
* the subsystem reuse fractions known this run (lint hit ratio, sidecar
|
|
58678
|
-
* replay ratio, dead-code reuse), skipping subsystems that never consulted a
|
|
58679
|
-
* cache; `1` on turbo, dropped when nothing consulted any cache. Deliberately
|
|
58680
|
-
* unweighted — the per-subsystem dims stay the precise signal; warmth is the
|
|
58681
|
-
* p50/p90-able summary. Emitted only on the success path.
|
|
58682
|
-
*/
|
|
58683
|
-
const buildCacheAttributes = (input) => {
|
|
58684
|
-
if (input.result === void 0) return {};
|
|
58685
|
-
if (input.wholeRepoCacheHit) return withNamespace("cache", {
|
|
58686
|
-
wholeRepoHit: true,
|
|
58687
|
-
temperature: "turbo",
|
|
58688
|
-
warmth: 1
|
|
58689
|
-
});
|
|
58690
|
-
const result = input.result;
|
|
58691
|
-
const subsystemReuseRatios = [
|
|
58692
|
-
ratioOf(result.lintCacheHitFileCount, result.lintCacheTotalFileCount),
|
|
58693
|
-
ratioOf(result.lintSidecarReplayedFileCount, result.lintSidecarTotalFileCount),
|
|
58694
|
-
resolveDeadCodeReuseRatio(result)
|
|
58695
|
-
];
|
|
58696
|
-
let knownSubsystemCount = 0;
|
|
58697
|
-
let reuseRatioSum = 0;
|
|
58698
|
-
for (const reuseRatio of subsystemReuseRatios) {
|
|
58699
|
-
if (reuseRatio === null) continue;
|
|
58700
|
-
knownSubsystemCount += 1;
|
|
58701
|
-
reuseRatioSum += reuseRatio;
|
|
58702
|
-
}
|
|
58703
|
-
const warmth = knownSubsystemCount > 0 ? reuseRatioSum / knownSubsystemCount : null;
|
|
58704
|
-
const temperature = warmth !== null && warmth > 0 ? "warm" : isCacheGloballyDisabled() ? "disabled" : "cold";
|
|
58705
|
-
return withNamespace("cache", {
|
|
58706
|
-
wholeRepoHit: input.wholeRepoCacheHit ?? null,
|
|
58707
|
-
temperature,
|
|
58708
|
-
warmth
|
|
58709
|
-
});
|
|
58710
|
-
};
|
|
58711
57946
|
const resolveVersionPin = (versionInput) => {
|
|
58712
57947
|
if (versionInput === void 0 || versionInput.trim() === "") return null;
|
|
58713
57948
|
if (versionInput === "latest") return "latest";
|
|
@@ -58813,17 +58048,12 @@ const buildOutcomeAttributes = (input) => {
|
|
|
58813
58048
|
deadlineSkippedFileCount: input.lintDeadlineSkippedFileCount ?? null,
|
|
58814
58049
|
cacheHitFiles: result.lintCacheHitFileCount ?? null,
|
|
58815
58050
|
cacheTotalFiles: result.lintCacheTotalFileCount ?? null,
|
|
58816
|
-
cacheHitRatio:
|
|
58817
|
-
sidecarReplayedFiles: result.lintSidecarReplayedFileCount ?? null,
|
|
58818
|
-
sidecarTotalFiles: result.lintSidecarTotalFileCount ?? null,
|
|
58819
|
-
sidecarReplayRatio: ratioOf(result.lintSidecarReplayedFileCount, result.lintSidecarTotalFileCount)
|
|
58051
|
+
cacheHitRatio: result.lintCacheTotalFileCount != null && result.lintCacheTotalFileCount > 0 ? (result.lintCacheHitFileCount ?? 0) / result.lintCacheTotalFileCount : null
|
|
58820
58052
|
}),
|
|
58821
58053
|
...withNamespace("deadCode", {
|
|
58822
58054
|
failed: input.didDeadCodeFail ?? null,
|
|
58823
58055
|
overlapped: input.deadCodeOverlapped ?? null,
|
|
58824
|
-
cacheHit: result.deadCodeCacheHit ?? null
|
|
58825
|
-
summaryCacheHits: result.deadCodeSummaryCacheHits ?? null,
|
|
58826
|
-
summaryCacheMisses: result.deadCodeSummaryCacheMisses ?? null
|
|
58056
|
+
cacheHit: result.deadCodeCacheHit ?? null
|
|
58827
58057
|
}),
|
|
58828
58058
|
...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut ?? null }),
|
|
58829
58059
|
...withNamespace("securityScan", { failed: input.securityScanFailed ?? null }),
|
|
@@ -58883,7 +58113,7 @@ const buildScanAttributes = (input) => {
|
|
|
58883
58113
|
* Projects a scan into the namespaced attribute set for its root span — the
|
|
58884
58114
|
* canonical per-scan "wide event". Every attribute carries a dotted namespace
|
|
58885
58115
|
* that groups it by concept (`scan.*` config, `action.*` CI knobs, `outcome.*`
|
|
58886
|
-
* verdict, `diag.*` findings, `score.*`, `lint.*`, `deadCode.*`,
|
|
58116
|
+
* verdict, `diag.*` findings, `score.*`, `lint.*`, `deadCode.*`,
|
|
58887
58117
|
* `supplyChain.*`, `timing.*`, `migration.*`, `baseline.*`) so the attributes
|
|
58888
58118
|
* tree up in Sentry's attribute browser and stay filter-/group-/aggregate-able
|
|
58889
58119
|
* in the Spans dataset. Pure and exported so the projection (outcome
|
|
@@ -58897,8 +58127,7 @@ const buildScanAttributes = (input) => {
|
|
|
58897
58127
|
const buildRunEventAttributes = (input) => toSpanAttributes({
|
|
58898
58128
|
...buildScanAttributes(input),
|
|
58899
58129
|
...buildActionAttributes(),
|
|
58900
|
-
...buildOutcomeAttributes(input)
|
|
58901
|
-
...buildCacheAttributes(input)
|
|
58130
|
+
...buildOutcomeAttributes(input)
|
|
58902
58131
|
});
|
|
58903
58132
|
/**
|
|
58904
58133
|
* Stamps the wide-event attributes onto the run's root span. A guarded no-op
|
|
@@ -60108,6 +59337,196 @@ const resolveCliCategories = (categoryFlag) => {
|
|
|
60108
59337
|
return resolvedCategories.length > 0 ? resolvedCategories : void 0;
|
|
60109
59338
|
};
|
|
60110
59339
|
//#endregion
|
|
59340
|
+
//#region src/cli/utils/scan-result-cache.ts
|
|
59341
|
+
const CACHE_DISABLED_VALUES = new Set(["1", "true"]);
|
|
59342
|
+
const TOOLCHAIN_PACKAGE_SPECIFIERS = [
|
|
59343
|
+
"oxlint",
|
|
59344
|
+
"oxlint/package.json",
|
|
59345
|
+
"oxlint-plugin-react-doctor",
|
|
59346
|
+
"deslop-js/package.json",
|
|
59347
|
+
"eslint-plugin-react-hooks/package.json"
|
|
59348
|
+
];
|
|
59349
|
+
const bundledRequire = createRequire(import.meta.url);
|
|
59350
|
+
const normalizeForStableJson = (value) => {
|
|
59351
|
+
if (value === null) return null;
|
|
59352
|
+
if (value === void 0) return void 0;
|
|
59353
|
+
const valueType = typeof value;
|
|
59354
|
+
if (valueType === "string" || valueType === "number" || valueType === "boolean") return value;
|
|
59355
|
+
if (valueType === "bigint" || valueType === "function" || valueType === "symbol") throw new Error("Unsupported cache key value");
|
|
59356
|
+
if (Array.isArray(value)) return value.map((item) => {
|
|
59357
|
+
const normalized = normalizeForStableJson(item);
|
|
59358
|
+
return normalized === void 0 ? null : normalized;
|
|
59359
|
+
});
|
|
59360
|
+
const prototype = Object.getPrototypeOf(value);
|
|
59361
|
+
if (prototype !== Object.prototype && prototype !== null) throw new Error("Unsupported cache key object");
|
|
59362
|
+
if (!isRecord(value)) throw new Error("Unsupported cache key object");
|
|
59363
|
+
const normalizedRecord = {};
|
|
59364
|
+
for (const key of Object.keys(value).sort()) {
|
|
59365
|
+
const normalized = normalizeForStableJson(value[key]);
|
|
59366
|
+
if (normalized !== void 0) normalizedRecord[key] = normalized;
|
|
59367
|
+
}
|
|
59368
|
+
return normalizedRecord;
|
|
59369
|
+
};
|
|
59370
|
+
const stringifyStableJson = (value) => {
|
|
59371
|
+
try {
|
|
59372
|
+
return JSON.stringify(normalizeForStableJson(value));
|
|
59373
|
+
} catch {
|
|
59374
|
+
return null;
|
|
59375
|
+
}
|
|
59376
|
+
};
|
|
59377
|
+
const hashString = (value) => crypto.createHash("sha1").update(value).digest("hex");
|
|
59378
|
+
const readHeadSha = (projectDirectory) => runGit(projectDirectory, ["rev-parse", "HEAD"]);
|
|
59379
|
+
const isWorktreeClean = (projectDirectory) => {
|
|
59380
|
+
const status = runGit(projectDirectory, [
|
|
59381
|
+
"status",
|
|
59382
|
+
"--porcelain=v1",
|
|
59383
|
+
"--untracked-files=normal"
|
|
59384
|
+
]);
|
|
59385
|
+
return status !== null && status.length === 0;
|
|
59386
|
+
};
|
|
59387
|
+
const hasHiddenTrackedFileState = (projectDirectory) => {
|
|
59388
|
+
const output = runGit(projectDirectory, ["ls-files", "-v"]);
|
|
59389
|
+
if (output === null) return true;
|
|
59390
|
+
return output.split("\n").some((line) => line.length > 0 && line[0] !== "H");
|
|
59391
|
+
};
|
|
59392
|
+
const resolveCacheFilePath = (projectDirectory) => {
|
|
59393
|
+
const nodeModulesDirectory = path.join(projectDirectory, "node_modules");
|
|
59394
|
+
if (fs.existsSync(nodeModulesDirectory)) return path.join(nodeModulesDirectory, ".cache", "react-doctor", "scan-cache.json");
|
|
59395
|
+
const projectHash = hashString(projectDirectory).slice(0, 16);
|
|
59396
|
+
return path.join(os.tmpdir(), "react-doctor-cache", `scan-${projectHash}.json`);
|
|
59397
|
+
};
|
|
59398
|
+
const readPersistedCache = (cacheFilePath) => {
|
|
59399
|
+
try {
|
|
59400
|
+
const parsed = JSON.parse(fs.readFileSync(cacheFilePath, "utf8"));
|
|
59401
|
+
if (!isRecord(parsed) || parsed.version !== 3) return {
|
|
59402
|
+
version: 3,
|
|
59403
|
+
entries: []
|
|
59404
|
+
};
|
|
59405
|
+
if (!Array.isArray(parsed.entries)) return {
|
|
59406
|
+
version: 3,
|
|
59407
|
+
entries: []
|
|
59408
|
+
};
|
|
59409
|
+
const entries = [];
|
|
59410
|
+
for (const entry of parsed.entries) {
|
|
59411
|
+
if (!isRecord(entry) || typeof entry.key !== "string" || typeof entry.createdAtMs !== "number") continue;
|
|
59412
|
+
if (!isRecord(entry.payload) || !Array.isArray(entry.payload.diagnostics)) continue;
|
|
59413
|
+
entries.push(entry);
|
|
59414
|
+
}
|
|
59415
|
+
return {
|
|
59416
|
+
version: 3,
|
|
59417
|
+
entries
|
|
59418
|
+
};
|
|
59419
|
+
} catch {
|
|
59420
|
+
return {
|
|
59421
|
+
version: 3,
|
|
59422
|
+
entries: []
|
|
59423
|
+
};
|
|
59424
|
+
}
|
|
59425
|
+
};
|
|
59426
|
+
const writePersistedCache = (cacheFilePath, cache) => {
|
|
59427
|
+
try {
|
|
59428
|
+
fs.mkdirSync(path.dirname(cacheFilePath), { recursive: true });
|
|
59429
|
+
const tempPath = `${cacheFilePath}.${process.pid}.tmp`;
|
|
59430
|
+
fs.writeFileSync(tempPath, JSON.stringify(cache));
|
|
59431
|
+
fs.renameSync(tempPath, cacheFilePath);
|
|
59432
|
+
} catch {
|
|
59433
|
+
return;
|
|
59434
|
+
}
|
|
59435
|
+
};
|
|
59436
|
+
const isScanResultCacheDisabled = () => CACHE_DISABLED_VALUES.has(process.env.REACT_DOCTOR_NO_CACHE?.toLowerCase() ?? "");
|
|
59437
|
+
const resolveProjectIdentity = (projectDirectory) => {
|
|
59438
|
+
try {
|
|
59439
|
+
return fs.realpathSync.native(projectDirectory);
|
|
59440
|
+
} catch {
|
|
59441
|
+
return path.resolve(projectDirectory);
|
|
59442
|
+
}
|
|
59443
|
+
};
|
|
59444
|
+
const fileFingerprint = (filePath) => {
|
|
59445
|
+
try {
|
|
59446
|
+
const stat = fs.statSync(filePath);
|
|
59447
|
+
return `${filePath}:${stat.mtimeMs}:${stat.size}`;
|
|
59448
|
+
} catch {
|
|
59449
|
+
return null;
|
|
59450
|
+
}
|
|
59451
|
+
};
|
|
59452
|
+
const resolveToolchainFingerprint = (nodeBinaryPath) => {
|
|
59453
|
+
const fingerprints = [];
|
|
59454
|
+
if (nodeBinaryPath !== null) {
|
|
59455
|
+
const fingerprint = fileFingerprint(nodeBinaryPath);
|
|
59456
|
+
if (fingerprint !== null) fingerprints.push(`node=${fingerprint}`);
|
|
59457
|
+
}
|
|
59458
|
+
for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS) try {
|
|
59459
|
+
const fingerprint = fileFingerprint(bundledRequire.resolve(specifier));
|
|
59460
|
+
if (fingerprint !== null) fingerprints.push(`${specifier}=${fingerprint}`);
|
|
59461
|
+
} catch {
|
|
59462
|
+
continue;
|
|
59463
|
+
}
|
|
59464
|
+
return fingerprints;
|
|
59465
|
+
};
|
|
59466
|
+
const buildScanResultCacheKey = (input) => {
|
|
59467
|
+
if (isScanResultCacheDisabled()) return null;
|
|
59468
|
+
if (!isWorktreeClean(input.projectDirectory)) return null;
|
|
59469
|
+
if (hasHiddenTrackedFileState(input.projectDirectory)) return null;
|
|
59470
|
+
const headSha = readHeadSha(input.projectDirectory);
|
|
59471
|
+
if (headSha === null) return null;
|
|
59472
|
+
if (stringifyStableJson(input.userConfig) === null) return null;
|
|
59473
|
+
const cacheKeyJson = stringifyStableJson({
|
|
59474
|
+
schemaVersion: 3,
|
|
59475
|
+
projectIdentity: resolveProjectIdentity(input.projectDirectory),
|
|
59476
|
+
headSha,
|
|
59477
|
+
reactDoctorVersion: input.version,
|
|
59478
|
+
nodeVersion: process.version,
|
|
59479
|
+
toolchainFingerprint: resolveToolchainFingerprint(input.nodeBinaryPath),
|
|
59480
|
+
configFingerprint: computeConfigFingerprint(input.projectDirectory, input.version),
|
|
59481
|
+
hasConfigOverride: input.hasConfigOverride,
|
|
59482
|
+
configSourceDirectory: input.configSourceDirectory,
|
|
59483
|
+
userConfig: input.userConfig,
|
|
59484
|
+
engineOptions: {
|
|
59485
|
+
lint: input.options.lint,
|
|
59486
|
+
deadCode: input.options.deadCode,
|
|
59487
|
+
includePaths: [...input.options.includePaths].sort(),
|
|
59488
|
+
customRulesOnly: input.options.customRulesOnly,
|
|
59489
|
+
respectInlineDisables: input.options.respectInlineDisables,
|
|
59490
|
+
warnings: input.options.warnings,
|
|
59491
|
+
adoptExistingLintConfig: input.options.adoptExistingLintConfig,
|
|
59492
|
+
ignoredTags: [...input.options.ignoredTags].sort(),
|
|
59493
|
+
concurrency: input.options.concurrency,
|
|
59494
|
+
lintBatchOrdering: resolveLintBatchOrdering(),
|
|
59495
|
+
baselineRef: input.options.baseline?.ref,
|
|
59496
|
+
changedLineRanges: input.options.changedLineRanges ?? void 0,
|
|
59497
|
+
noScore: input.options.noScore,
|
|
59498
|
+
isCi: input.options.isCi,
|
|
59499
|
+
suppressRendering: input.options.suppressRendering,
|
|
59500
|
+
supplyChainManifestChanged: input.options.supplyChainManifestChanged
|
|
59501
|
+
}
|
|
59502
|
+
});
|
|
59503
|
+
return cacheKeyJson === null ? null : hashString(cacheKeyJson);
|
|
59504
|
+
};
|
|
59505
|
+
const createScanResultCache = (projectDirectory) => {
|
|
59506
|
+
const cacheFilePath = resolveCacheFilePath(projectDirectory);
|
|
59507
|
+
const persistedCache = readPersistedCache(cacheFilePath);
|
|
59508
|
+
const entries = /* @__PURE__ */ new Map();
|
|
59509
|
+
for (const entry of persistedCache.entries) entries.set(entry.key, entry);
|
|
59510
|
+
const persist = () => {
|
|
59511
|
+
writePersistedCache(cacheFilePath, {
|
|
59512
|
+
version: 3,
|
|
59513
|
+
entries: [...entries.values()].sort((firstEntry, secondEntry) => secondEntry.createdAtMs - firstEntry.createdAtMs).slice(0, 20)
|
|
59514
|
+
});
|
|
59515
|
+
};
|
|
59516
|
+
return {
|
|
59517
|
+
lookup: (key) => entries.get(key)?.payload ?? null,
|
|
59518
|
+
store: (key, payload) => {
|
|
59519
|
+
entries.set(key, {
|
|
59520
|
+
key,
|
|
59521
|
+
createdAtMs: Date.now(),
|
|
59522
|
+
payload
|
|
59523
|
+
});
|
|
59524
|
+
persist();
|
|
59525
|
+
}
|
|
59526
|
+
};
|
|
59527
|
+
};
|
|
59528
|
+
const shouldStoreScanPayload = (payload) => !payload.didLintFail && !payload.didDeadCodeFail && payload.lintPartialFailures.length === 0 && !payload.supplyChainOverlapTimedOut && payload.securityScanFailed !== true;
|
|
59529
|
+
//#endregion
|
|
60111
59530
|
//#region src/inspect.ts
|
|
60112
59531
|
const silentConsole = makeNoopConsole();
|
|
60113
59532
|
const runConsole = (effect) => {
|
|
@@ -60284,7 +59703,7 @@ const runBaselineComparison = async (params) => {
|
|
|
60284
59703
|
suppressScanSummary: true,
|
|
60285
59704
|
supplyChainManifestChanged: params.options.supplyChainManifestChanged,
|
|
60286
59705
|
deadlineEpochMs: params.deadlineEpochMs ?? void 0
|
|
60287
|
-
}, {}).pipe(provide(baseLayers), provideService$2(
|
|
59706
|
+
}, {}).pipe(provide(baseLayers), provideService$2(Console, silentConsole))));
|
|
60288
59707
|
if (baseOutput.didLintFail || countIncompleteLintFiles(baseOutput.lintPartialFailures) > 0) return null;
|
|
60289
59708
|
const delta = computeDiagnosticDelta({
|
|
60290
59709
|
headDiagnostics: params.headDiagnostics,
|
|
@@ -60337,8 +59756,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
60337
59756
|
startTime,
|
|
60338
59757
|
rootSentrySpan,
|
|
60339
59758
|
scanMode: cachedPayload.baselineDelta ? "baseline" : isDiffMode ? "diff" : "full",
|
|
60340
|
-
baselineDegraded
|
|
60341
|
-
wholeRepoCacheHit: true
|
|
59759
|
+
baselineDegraded
|
|
60342
59760
|
});
|
|
60343
59761
|
recordOnboardingCompletion(options);
|
|
60344
59762
|
return result;
|
|
@@ -60447,14 +59865,9 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
60447
59865
|
rootSentrySpan,
|
|
60448
59866
|
scanMode: baselineDelta ? "baseline" : isDiffMode ? "diff" : "full",
|
|
60449
59867
|
baselineDegraded,
|
|
60450
|
-
wholeRepoCacheHit: false,
|
|
60451
59868
|
lintCacheHitFileCount: output.lintCacheHitFileCount,
|
|
60452
59869
|
lintCacheTotalFileCount: output.lintCacheTotalFileCount,
|
|
60453
|
-
|
|
60454
|
-
lintSidecarTotalFileCount: output.lintSidecarTotalFileCount,
|
|
60455
|
-
deadCodeCacheHit: output.deadCodeCacheHit,
|
|
60456
|
-
deadCodeSummaryCacheHits: output.deadCodeSummaryCacheHits,
|
|
60457
|
-
deadCodeSummaryCacheMisses: output.deadCodeSummaryCacheMisses
|
|
59870
|
+
deadCodeCacheHit: output.deadCodeCacheHit
|
|
60458
59871
|
});
|
|
60459
59872
|
recordOnboardingCompletion(options);
|
|
60460
59873
|
return result;
|
|
@@ -60489,11 +59902,7 @@ const renderAndRecordScan = async (input) => {
|
|
|
60489
59902
|
scanElapsedMilliseconds: input.payload.scanElapsedMilliseconds,
|
|
60490
59903
|
lintCacheHitFileCount: input.lintCacheHitFileCount ?? null,
|
|
60491
59904
|
lintCacheTotalFileCount: input.lintCacheTotalFileCount ?? null,
|
|
60492
|
-
lintSidecarReplayedFileCount: input.lintSidecarReplayedFileCount ?? null,
|
|
60493
|
-
lintSidecarTotalFileCount: input.lintSidecarTotalFileCount ?? null,
|
|
60494
59905
|
deadCodeCacheHit: input.deadCodeCacheHit ?? null,
|
|
60495
|
-
deadCodeSummaryCacheHits: input.deadCodeSummaryCacheHits ?? null,
|
|
60496
|
-
deadCodeSummaryCacheMisses: input.deadCodeSummaryCacheMisses ?? null,
|
|
60497
59906
|
baselineDelta: input.payload.baselineDelta
|
|
60498
59907
|
}), input.options.silent));
|
|
60499
59908
|
const { workerCount: resolvedWorkerCount, parallel } = resolveWorkerTelemetry(input.payload.scanConcurrency, input.options.concurrency);
|
|
@@ -60518,7 +59927,6 @@ const renderAndRecordScan = async (input) => {
|
|
|
60518
59927
|
result,
|
|
60519
59928
|
mode: input.scanMode,
|
|
60520
59929
|
gateExempt: input.baselineDegraded,
|
|
60521
|
-
wholeRepoCacheHit: input.wholeRepoCacheHit,
|
|
60522
59930
|
didLintFail: input.payload.didLintFail,
|
|
60523
59931
|
lintFailureReasonKind: input.payload.lintFailureReasonKind,
|
|
60524
59932
|
lintPartialFailureCount: input.payload.lintPartialFailures.length,
|
|
@@ -60533,7 +59941,7 @@ const renderAndRecordScan = async (input) => {
|
|
|
60533
59941
|
return result;
|
|
60534
59942
|
};
|
|
60535
59943
|
const finalizeAndRender = (input) => gen(function* () {
|
|
60536
|
-
const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, directory, scannedFileCount, scannedFilePaths, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount,
|
|
59944
|
+
const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, directory, scannedFileCount, scannedFilePaths, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount, deadCodeCacheHit, baselineDelta } = input;
|
|
60537
59945
|
const { skippedChecks, skippedCheckReasons } = buildSkippedChecks({
|
|
60538
59946
|
didLintFail,
|
|
60539
59947
|
lintFailureReason,
|
|
@@ -60557,15 +59965,7 @@ const finalizeAndRender = (input) => gen(function* () {
|
|
|
60557
59965
|
lintCacheHitFileCount,
|
|
60558
59966
|
lintCacheTotalFileCount
|
|
60559
59967
|
} : {},
|
|
60560
|
-
...lintSidecarTotalFileCount !== null ? {
|
|
60561
|
-
lintSidecarReplayedFileCount,
|
|
60562
|
-
lintSidecarTotalFileCount
|
|
60563
|
-
} : {},
|
|
60564
59968
|
...deadCodeCacheHit !== null ? { deadCodeCacheHit } : {},
|
|
60565
|
-
...deadCodeSummaryCacheHits !== null && deadCodeSummaryCacheMisses !== null ? {
|
|
60566
|
-
deadCodeSummaryCacheHits,
|
|
60567
|
-
deadCodeSummaryCacheMisses
|
|
60568
|
-
} : {},
|
|
60569
59969
|
...baselineDelta ? { baselineDelta } : {}
|
|
60570
59970
|
});
|
|
60571
59971
|
if (options.suppressRendering) return buildResult();
|
|
@@ -64412,4 +63812,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
64412
63812
|
export {};
|
|
64413
63813
|
|
|
64414
63814
|
//# sourceMappingURL=cli.js.map
|
|
64415
|
-
//# debugId=
|
|
63815
|
+
//# debugId=82500030-a7a7-5da8-b18e-cbdb42102dc3
|