react-doctor 0.6.3-dev.ea9a775 → 0.6.3
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 +92 -678
- package/dist/index.d.ts +0 -26
- package/dist/index.js +77 -557
- package/dist/lsp.js +77 -557
- package/package.json +5 -5
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]="328318eb-6af2-5007-b816-51c953a96047")}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,10 +36128,6 @@ 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
|
-
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
36135
|
-
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
36136
36131
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
36137
36132
|
const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
|
|
36138
36133
|
const SUPPLY_CHAIN_CATEGORY = "Security";
|
|
@@ -38817,48 +38812,6 @@ var PerFileLintCacheEnabled = class extends Reference("react-doctor/PerFileLintC
|
|
|
38817
38812
|
if (CACHE_DISABLED_VALUES$1.has(noFileCache)) return false;
|
|
38818
38813
|
return true;
|
|
38819
38814
|
} }) {};
|
|
38820
|
-
/**
|
|
38821
|
-
* Whether the sidecar lint cache (`runners/oxlint/sidecar-lint-cache.ts`) is
|
|
38822
|
-
* active. Defaults ON — warm rescans replay the cross-file rules' cached
|
|
38823
|
-
* diagnostics for every file whose dependency fingerprint (the probe set the
|
|
38824
|
-
* plugin's collectors recorded) still matches the tree, and re-lint only the
|
|
38825
|
-
* rest. Only reachable when the per-file lint cache itself is on (the
|
|
38826
|
-
* sidecar covers its cache hits), so `REACT_DOCTOR_NO_CACHE` /
|
|
38827
|
-
* `REACT_DOCTOR_NO_FILE_CACHE` implicitly disable it too. Opt-OUT knobs:
|
|
38828
|
-
*
|
|
38829
|
-
* - `REACT_DOCTOR_NO_CACHE` — the global off-switch.
|
|
38830
|
-
* - `REACT_DOCTOR_NO_SIDECAR_CACHE` — granular rollback hatch: keep the
|
|
38831
|
-
* per-file cache but run the always-fresh sidecar over every hit
|
|
38832
|
-
* (the pre-cache behavior).
|
|
38833
|
-
*
|
|
38834
|
-
* Tests override via `Layer.succeed(SidecarLintCacheEnabled, false)`.
|
|
38835
|
-
*/
|
|
38836
|
-
var SidecarLintCacheEnabled = class extends Reference("react-doctor/SidecarLintCacheEnabled", { defaultValue: () => {
|
|
38837
|
-
const noCache = process.env["REACT_DOCTOR_NO_CACHE"]?.toLowerCase() ?? "";
|
|
38838
|
-
const noSidecarCache = process.env["REACT_DOCTOR_NO_SIDECAR_CACHE"]?.toLowerCase() ?? "";
|
|
38839
|
-
if (CACHE_DISABLED_VALUES$1.has(noCache)) return false;
|
|
38840
|
-
if (CACHE_DISABLED_VALUES$1.has(noSidecarCache)) return false;
|
|
38841
|
-
return true;
|
|
38842
|
-
} }) {};
|
|
38843
|
-
/**
|
|
38844
|
-
* Whether the whole-project dead-code result cache
|
|
38845
|
-
* (`dead-code/dead-code-result-cache.ts`) is active. Defaults ON — a rescan
|
|
38846
|
-
* whose inputs (source tree, manifests, configs, analyzer version) are
|
|
38847
|
-
* unchanged replays the stored diagnostics instead of re-running the
|
|
38848
|
-
* analysis worker. Opt-OUT, two knobs (matching the per-file lint cache):
|
|
38849
|
-
*
|
|
38850
|
-
* - `REACT_DOCTOR_NO_CACHE` — the global off-switch.
|
|
38851
|
-
* - `REACT_DOCTOR_NO_DEAD_CODE_CACHE` — granular: bust only this cache.
|
|
38852
|
-
*
|
|
38853
|
-
* Tests override via `Layer.succeed(DeadCodeResultCacheEnabled, false)`.
|
|
38854
|
-
*/
|
|
38855
|
-
var DeadCodeResultCacheEnabled = class extends Reference("react-doctor/DeadCodeResultCacheEnabled", { defaultValue: () => {
|
|
38856
|
-
const noCache = process.env["REACT_DOCTOR_NO_CACHE"]?.toLowerCase() ?? "";
|
|
38857
|
-
const noDeadCodeCache = process.env["REACT_DOCTOR_NO_DEAD_CODE_CACHE"]?.toLowerCase() ?? "";
|
|
38858
|
-
if (CACHE_DISABLED_VALUES$1.has(noCache)) return false;
|
|
38859
|
-
if (CACHE_DISABLED_VALUES$1.has(noDeadCodeCache)) return false;
|
|
38860
|
-
return true;
|
|
38861
|
-
} }) {};
|
|
38862
38815
|
const DIAGNOSTIC_SURFACES = [
|
|
38863
38816
|
"cli",
|
|
38864
38817
|
"prComment",
|
|
@@ -40591,80 +40544,6 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
|
|
|
40591
40544
|
return [...seen].filter((pattern) => pattern.length > 0);
|
|
40592
40545
|
};
|
|
40593
40546
|
const collectDeadCodeEntryPatterns = (rootDirectory) => [...new Set(collectKnipPatterns(rootDirectory, "entry"))].filter((pattern) => pattern.length > 0);
|
|
40594
|
-
const atomicWriteJson = (filePath, value) => {
|
|
40595
|
-
try {
|
|
40596
|
-
NFS.mkdirSync(Path.dirname(filePath), { recursive: true });
|
|
40597
|
-
const temporaryPath = `${filePath}.${process.pid}.tmp`;
|
|
40598
|
-
NFS.writeFileSync(temporaryPath, JSON.stringify(value));
|
|
40599
|
-
NFS.renameSync(temporaryPath, filePath);
|
|
40600
|
-
} catch {
|
|
40601
|
-
return;
|
|
40602
|
-
}
|
|
40603
|
-
};
|
|
40604
|
-
const failOpenReadJson = (filePath, fallback) => {
|
|
40605
|
-
try {
|
|
40606
|
-
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
40607
|
-
} catch {
|
|
40608
|
-
return fallback;
|
|
40609
|
-
}
|
|
40610
|
-
};
|
|
40611
|
-
const ANALYZED_FILE_EXTENSIONS = new Set(DEFAULT_EXTENSIONS);
|
|
40612
|
-
const CORE_SIDE_MANIFEST_NAMES = ["knip.json", "deno.lock"];
|
|
40613
|
-
const ANALYZED_MANIFEST_NAMES = new Set([...ANALYZED_MANIFEST_FILENAMES, ...CORE_SIDE_MANIFEST_NAMES]);
|
|
40614
|
-
const isTsConfigLikeFile = (fileName) => (fileName.startsWith("tsconfig") || fileName.startsWith("jsconfig")) && fileName.endsWith(".json");
|
|
40615
|
-
const isFingerprintedFile = (fileName) => ANALYZED_FILE_EXTENSIONS.has(Path.extname(fileName).toLowerCase()) || ANALYZED_MANIFEST_NAMES.has(fileName) || isTsConfigLikeFile(fileName);
|
|
40616
|
-
const collectAnalyzedFileFingerprints = (rootDirectory) => {
|
|
40617
|
-
const fingerprints = [];
|
|
40618
|
-
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
|
|
40619
|
-
if (!isFingerprintedFile(name)) continue;
|
|
40620
|
-
try {
|
|
40621
|
-
const fileStat = NFS.statSync(absolutePath);
|
|
40622
|
-
const relativePath = Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/");
|
|
40623
|
-
fingerprints.push(`${relativePath}:${fileStat.mtimeMs}:${fileStat.size}`);
|
|
40624
|
-
} catch {}
|
|
40625
|
-
}
|
|
40626
|
-
return fingerprints.sort();
|
|
40627
|
-
};
|
|
40628
|
-
const bundledRequire$2 = createRequire(import.meta.url);
|
|
40629
|
-
const resolveDeslopVersion = () => {
|
|
40630
|
-
try {
|
|
40631
|
-
const packageJson = JSON.parse(NFS.readFileSync(bundledRequire$2.resolve("deslop-js/package.json"), "utf8"));
|
|
40632
|
-
return isRecord$1(packageJson) && typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
40633
|
-
} catch {
|
|
40634
|
-
return "unknown";
|
|
40635
|
-
}
|
|
40636
|
-
};
|
|
40637
|
-
const computeDeadCodeCacheKey = (input) => crypto.createHash("sha1").update(JSON.stringify({
|
|
40638
|
-
schemaVersion: 1,
|
|
40639
|
-
coreVersion: input.coreVersion,
|
|
40640
|
-
deslopVersion: resolveDeslopVersion(),
|
|
40641
|
-
deslopJsModuleSpecifier: input.deslopJsModuleSpecifier,
|
|
40642
|
-
entryPatterns: input.entryPatterns,
|
|
40643
|
-
ignorePatterns: input.ignorePatterns,
|
|
40644
|
-
tsConfigFile: input.tsConfigPath === void 0 ? null : Path.relative(input.rootDirectory, input.tsConfigPath).replace(/\\/g, "/"),
|
|
40645
|
-
files: collectAnalyzedFileFingerprints(input.rootDirectory)
|
|
40646
|
-
})).digest("hex");
|
|
40647
|
-
const validateDiagnostic$2 = decodeUnknownSync(Diagnostic);
|
|
40648
|
-
const decodeCachedDiagnostics = (raw) => {
|
|
40649
|
-
try {
|
|
40650
|
-
for (const entry of raw) validateDiagnostic$2(entry);
|
|
40651
|
-
return raw;
|
|
40652
|
-
} catch {
|
|
40653
|
-
return null;
|
|
40654
|
-
}
|
|
40655
|
-
};
|
|
40656
|
-
const lookupDeadCodeResultCache = (cacheDirectory, cacheKey) => {
|
|
40657
|
-
const persisted = failOpenReadJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), null);
|
|
40658
|
-
if (persisted === null || !isRecord$1(persisted) || persisted.version !== 1 || persisted.key !== cacheKey || !Array.isArray(persisted.diagnostics)) return null;
|
|
40659
|
-
return decodeCachedDiagnostics(persisted.diagnostics);
|
|
40660
|
-
};
|
|
40661
|
-
const storeDeadCodeResultCache = (cacheDirectory, cacheKey, diagnostics) => {
|
|
40662
|
-
atomicWriteJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), {
|
|
40663
|
-
version: 1,
|
|
40664
|
-
key: cacheKey,
|
|
40665
|
-
diagnostics
|
|
40666
|
-
});
|
|
40667
|
-
};
|
|
40668
40547
|
const readSystemFacts = () => ({
|
|
40669
40548
|
availableCores: os.availableParallelism(),
|
|
40670
40549
|
totalMemoryBytes: os.totalmem(),
|
|
@@ -40733,21 +40612,6 @@ const withDeadCodeWorkerSlot = async (task, abortSignal) => {
|
|
|
40733
40612
|
releaseSlot();
|
|
40734
40613
|
}
|
|
40735
40614
|
};
|
|
40736
|
-
const projectCacheSubdir = (projectDirectory) => crypto.createHash("sha256").update(projectDirectory).digest("hex").slice(0, 16);
|
|
40737
|
-
const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
40738
|
-
const cacheDirOverride = process.env["REACT_DOCTOR_CACHE_DIR"]?.trim();
|
|
40739
|
-
if (cacheDirOverride) return Path.join(cacheDirOverride, projectCacheSubdir(projectDirectory));
|
|
40740
|
-
const nodeModulesDirectory = Path.join(projectDirectory, "node_modules");
|
|
40741
|
-
if (NFS.existsSync(nodeModulesDirectory)) return Path.join(nodeModulesDirectory, ".cache", "react-doctor");
|
|
40742
|
-
return Path.join(os.tmpdir(), `react-doctor-cache-${resolveUserCacheScope()}`, projectCacheSubdir(projectDirectory));
|
|
40743
|
-
};
|
|
40744
|
-
const resolveUserCacheScope = () => {
|
|
40745
|
-
try {
|
|
40746
|
-
return String(process.getuid?.() ?? os.userInfo().username);
|
|
40747
|
-
} catch {
|
|
40748
|
-
return "shared";
|
|
40749
|
-
}
|
|
40750
|
-
};
|
|
40751
40615
|
/**
|
|
40752
40616
|
* Resolves a path to its canonical, symlink-free form, falling back to
|
|
40753
40617
|
* the input when it cannot be realpath'd (broken symlink, permission
|
|
@@ -40801,14 +40665,6 @@ process.stdin.on("end", () => {
|
|
|
40801
40665
|
circularDependencies: result.circularDependencies.map((cycle) => ({
|
|
40802
40666
|
files: cycle.files,
|
|
40803
40667
|
})),
|
|
40804
|
-
...(result.incrementalCacheStats
|
|
40805
|
-
? {
|
|
40806
|
-
summaryCacheStats: {
|
|
40807
|
-
hits: result.incrementalCacheStats.summaryHits,
|
|
40808
|
-
misses: result.incrementalCacheStats.summaryMisses,
|
|
40809
|
-
},
|
|
40810
|
-
}
|
|
40811
|
-
: {}),
|
|
40812
40668
|
});
|
|
40813
40669
|
|
|
40814
40670
|
const serializeError = (error) =>
|
|
@@ -40832,9 +40688,6 @@ process.stdin.on("end", () => {
|
|
|
40832
40688
|
...(workerInput.ignorePatterns.length > 0
|
|
40833
40689
|
? { ignorePatterns: workerInput.ignorePatterns }
|
|
40834
40690
|
: {}),
|
|
40835
|
-
...(workerInput.incrementalCachePath
|
|
40836
|
-
? { incrementalCachePath: workerInput.incrementalCachePath }
|
|
40837
|
-
: {}),
|
|
40838
40691
|
// We consume only deslop's GRAPH-based findings (unusedFiles, unusedExports,
|
|
40839
40692
|
// unusedDependencies, circularDependencies). Everything else deslop can compute
|
|
40840
40693
|
// is pure wasted work for us, and it's the bulk of the runtime:
|
|
@@ -40845,18 +40698,12 @@ process.stdin.on("end", () => {
|
|
|
40845
40698
|
// are the single most expensive pass — duplicate-block detection alone was
|
|
40846
40699
|
// ~83s of a ~130s Sentry scan — so skipping them is an ~8.5x dead-code
|
|
40847
40700
|
// speedup on a large repo.
|
|
40848
|
-
//
|
|
40849
|
-
// simplifiable functions, identity wrappers, …) — ~120 ms of discarded
|
|
40850
|
-
// output per scan, and skipping them lets the incremental cache drop the
|
|
40851
|
-
// DRY-pattern summary fields (the largest slice of the cache file).
|
|
40852
|
-
// All are provably safe: the consumed graph findings are computed by their own
|
|
40701
|
+
// Both are provably safe: the consumed graph findings are computed by their own
|
|
40853
40702
|
// detectors, independent of these passes (confirmed byte-identical on
|
|
40854
|
-
// excalidraw + mui-material + sentry
|
|
40855
|
-
//
|
|
40856
|
-
// for path-alias resolution in the import graph.
|
|
40703
|
+
// excalidraw + mui-material + sentry). tsConfigPath stays — the module resolver
|
|
40704
|
+
// needs it for path-alias resolution in the import graph.
|
|
40857
40705
|
semantic: { enabled: false },
|
|
40858
40706
|
reportCodeQuality: false,
|
|
40859
|
-
reportRedundancy: false,
|
|
40860
40707
|
};
|
|
40861
40708
|
const result = await analyze(defineConfig(config));
|
|
40862
40709
|
emit({ ok: true, result: normalizeResult(result) });
|
|
@@ -40940,23 +40787,13 @@ const parseCircularDependencies = (value) => {
|
|
|
40940
40787
|
}
|
|
40941
40788
|
return circularDependencies;
|
|
40942
40789
|
};
|
|
40943
|
-
const parseSummaryCacheStats = (value) => {
|
|
40944
|
-
if (!isRecord$1(value)) return void 0;
|
|
40945
|
-
if (typeof value.hits !== "number" || typeof value.misses !== "number") return void 0;
|
|
40946
|
-
return {
|
|
40947
|
-
hits: value.hits,
|
|
40948
|
-
misses: value.misses
|
|
40949
|
-
};
|
|
40950
|
-
};
|
|
40951
40790
|
const parseDeadCodeWorkerResult = (value) => {
|
|
40952
40791
|
if (!isRecord$1(value)) throw new Error("Dead-code worker returned an invalid result.");
|
|
40953
|
-
const summaryCacheStats = parseSummaryCacheStats(value.summaryCacheStats);
|
|
40954
40792
|
return {
|
|
40955
40793
|
unusedFiles: parseUnusedFiles(value.unusedFiles),
|
|
40956
40794
|
unusedExports: parseUnusedExports(value.unusedExports),
|
|
40957
40795
|
unusedDependencies: parseUnusedDependencies(value.unusedDependencies),
|
|
40958
|
-
circularDependencies: parseCircularDependencies(value.circularDependencies)
|
|
40959
|
-
...summaryCacheStats ? { summaryCacheStats } : {}
|
|
40796
|
+
circularDependencies: parseCircularDependencies(value.circularDependencies)
|
|
40960
40797
|
};
|
|
40961
40798
|
};
|
|
40962
40799
|
const parseDeadCodeWorkerError = (value) => {
|
|
@@ -41070,35 +40907,17 @@ const checkDeadCode = async (options) => {
|
|
|
41070
40907
|
if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
|
|
41071
40908
|
const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
|
|
41072
40909
|
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
41073
|
-
const tsConfigPath = resolveTsConfigPath(rootDirectory);
|
|
41074
|
-
const deslopJsModuleSpecifier = options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js");
|
|
41075
|
-
const cacheKey = options.cacheEnabled ? computeDeadCodeCacheKey({
|
|
41076
|
-
rootDirectory,
|
|
41077
|
-
entryPatterns,
|
|
41078
|
-
ignorePatterns,
|
|
41079
|
-
tsConfigPath,
|
|
41080
|
-
deslopJsModuleSpecifier,
|
|
41081
|
-
coreVersion: CORE_PACKAGE_VERSION
|
|
41082
|
-
}) : null;
|
|
41083
|
-
if (cacheKey !== null) {
|
|
41084
|
-
const cachedDiagnostics = lookupDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey);
|
|
41085
|
-
options.onCacheOutcome?.(cachedDiagnostics !== null);
|
|
41086
|
-
if (cachedDiagnostics !== null) return [...cachedDiagnostics];
|
|
41087
|
-
}
|
|
41088
|
-
const incrementalCachePath = options.cacheEnabled === true ? Path.join(resolveReactDoctorCacheDir(rootDirectory), DEAD_CODE_SUMMARY_CACHE_FILENAME) : void 0;
|
|
41089
40910
|
const spawnAndRun = () => {
|
|
41090
40911
|
return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
41091
40912
|
rootDirectory,
|
|
41092
40913
|
entryPatterns,
|
|
41093
|
-
tsConfigPath,
|
|
40914
|
+
tsConfigPath: resolveTsConfigPath(rootDirectory),
|
|
41094
40915
|
ignorePatterns,
|
|
41095
|
-
deslopJsModuleSpecifier,
|
|
41096
|
-
parseConcurrency: options.parseConcurrency
|
|
41097
|
-
incrementalCachePath
|
|
40916
|
+
deslopJsModuleSpecifier: options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js"),
|
|
40917
|
+
parseConcurrency: options.parseConcurrency
|
|
41098
40918
|
}), options.workerTimeoutMs ?? 12e4, options.abortSignal);
|
|
41099
40919
|
};
|
|
41100
40920
|
const result = parseDeadCodeWorkerResult(options.createWorker === void 0 ? await withDeadCodeWorkerSlot(spawnAndRun, options.abortSignal) : await spawnAndRun());
|
|
41101
|
-
if (result.summaryCacheStats !== void 0) options.onSummaryCacheStats?.(result.summaryCacheStats);
|
|
41102
40921
|
const toRelative = (filePath) => toRelativeFilePath(rootDirectory, filePath);
|
|
41103
40922
|
const diagnostics = [];
|
|
41104
40923
|
for (const unusedFile of result.unusedFiles) diagnostics.push({
|
|
@@ -41155,7 +40974,6 @@ const checkDeadCode = async (options) => {
|
|
|
41155
40974
|
category: DEAD_CODE_CATEGORY
|
|
41156
40975
|
});
|
|
41157
40976
|
}
|
|
41158
|
-
if (cacheKey !== null) storeDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey, diagnostics);
|
|
41159
40977
|
return diagnostics;
|
|
41160
40978
|
};
|
|
41161
40979
|
const DEAD_CODE_RULE_KEY_PREFIX = `${DEAD_CODE_PLUGIN}/`;
|
|
@@ -41258,16 +41076,12 @@ var Config = class Config extends Service()("react-doctor/Config") {
|
|
|
41258
41076
|
*/
|
|
41259
41077
|
var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
|
|
41260
41078
|
static layerNode = succeed$3(DeadCode, DeadCode.of({ run: (input) => unwrap(fn("DeadCode.run")(function* () {
|
|
41261
|
-
const cacheEnabled = yield* DeadCodeResultCacheEnabled;
|
|
41262
41079
|
return yield* tryPromise({
|
|
41263
41080
|
try: (signal) => checkDeadCode({
|
|
41264
41081
|
rootDirectory: input.rootDirectory,
|
|
41265
41082
|
parseConcurrency: input.parseConcurrency,
|
|
41266
41083
|
workerTimeoutMs: input.workerTimeoutMs,
|
|
41267
|
-
abortSignal: signal
|
|
41268
|
-
cacheEnabled,
|
|
41269
|
-
onCacheOutcome: input.onCacheOutcome,
|
|
41270
|
-
onSummaryCacheStats: input.onSummaryCacheStats
|
|
41084
|
+
abortSignal: signal
|
|
41271
41085
|
}),
|
|
41272
41086
|
catch: (cause) => new ReactDoctorError({ reason: new DeadCodeAnalysisFailed({ cause }) })
|
|
41273
41087
|
}).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)));
|
|
@@ -42137,7 +41951,7 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
42137
41951
|
}
|
|
42138
41952
|
return enabled;
|
|
42139
41953
|
};
|
|
42140
|
-
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection
|
|
41954
|
+
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection }) => {
|
|
42141
41955
|
const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
42142
41956
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
42143
41957
|
const jsPlugins = [];
|
|
@@ -42149,7 +41963,6 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
42149
41963
|
if (!rule) continue;
|
|
42150
41964
|
if (ruleSelection === "cacheable" && CROSS_FILE_RULE_IDS.has(registryEntry.id)) continue;
|
|
42151
41965
|
if (ruleSelection === "sidecar" && !CROSS_FILE_RULE_IDS.has(registryEntry.id)) continue;
|
|
42152
|
-
if (ruleSelection === "sidecar" && sidecarRuleIdFilter !== void 0 && !sidecarRuleIdFilter.has(registryEntry.id)) continue;
|
|
42153
41966
|
if (rule.scan !== void 0) continue;
|
|
42154
41967
|
if (customRulesOnly && registryEntry.originallyExternal) continue;
|
|
42155
41968
|
if (rule.framework !== "global" && !rule.requires) continue;
|
|
@@ -42194,152 +42007,33 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
42194
42007
|
}
|
|
42195
42008
|
};
|
|
42196
42009
|
};
|
|
42197
|
-
const
|
|
42198
|
-
const decodeFileDiagnostics = (raw) => {
|
|
42199
|
-
if (!Array.isArray(raw)) return null;
|
|
42010
|
+
const atomicWriteJson = (filePath, value) => {
|
|
42200
42011
|
try {
|
|
42201
|
-
|
|
42202
|
-
|
|
42012
|
+
NFS.mkdirSync(Path.dirname(filePath), { recursive: true });
|
|
42013
|
+
const temporaryPath = `${filePath}.${process.pid}.tmp`;
|
|
42014
|
+
NFS.writeFileSync(temporaryPath, JSON.stringify(value));
|
|
42015
|
+
NFS.renameSync(temporaryPath, filePath);
|
|
42203
42016
|
} catch {
|
|
42204
|
-
return
|
|
42205
|
-
}
|
|
42206
|
-
};
|
|
42207
|
-
const emptyCache$1 = () => ({
|
|
42208
|
-
version: 1,
|
|
42209
|
-
rulesets: {}
|
|
42210
|
-
});
|
|
42211
|
-
const loadRulesetEntries$1 = (cacheFilePath, rulesetHash) => {
|
|
42212
|
-
const entries = /* @__PURE__ */ new Map();
|
|
42213
|
-
const persisted = failOpenReadJson(cacheFilePath, emptyCache$1());
|
|
42214
|
-
if (persisted.version !== 1 || !isRecord$1(persisted.rulesets)) return entries;
|
|
42215
|
-
const bucket = persisted.rulesets[rulesetHash];
|
|
42216
|
-
if (!isRecord$1(bucket) || !isRecord$1(bucket.files)) return entries;
|
|
42217
|
-
for (const [fileKey, rawDiagnostics] of Object.entries(bucket.files)) {
|
|
42218
|
-
const decoded = decodeFileDiagnostics(rawDiagnostics);
|
|
42219
|
-
if (decoded !== null) entries.set(fileKey, decoded);
|
|
42017
|
+
return;
|
|
42220
42018
|
}
|
|
42221
|
-
return entries;
|
|
42222
42019
|
};
|
|
42223
|
-
const
|
|
42224
|
-
const cacheFilePath = Path.join(cacheDirectory, FILE_LINT_CACHE_FILENAME);
|
|
42225
|
-
const entries = loadRulesetEntries$1(cacheFilePath, rulesetHash);
|
|
42226
|
-
return {
|
|
42227
|
-
lookup: (fileKey) => entries.get(fileKey) ?? null,
|
|
42228
|
-
store: (fileKey, diagnostics) => {
|
|
42229
|
-
entries.delete(fileKey);
|
|
42230
|
-
entries.set(fileKey, diagnostics);
|
|
42231
|
-
},
|
|
42232
|
-
persist: () => {
|
|
42233
|
-
const onDisk = failOpenReadJson(cacheFilePath, emptyCache$1());
|
|
42234
|
-
const rulesets = onDisk.version === 1 && isRecord$1(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
|
|
42235
|
-
const existingBucket = rulesets[rulesetHash];
|
|
42236
|
-
const existingFiles = isRecord$1(existingBucket) && isRecord$1(existingBucket.files) ? existingBucket.files : {};
|
|
42237
|
-
const ourFiles = {};
|
|
42238
|
-
for (const [fileKey, diagnostics] of entries) ourFiles[fileKey] = diagnostics;
|
|
42239
|
-
const cappedEntries = Object.entries({
|
|
42240
|
-
...existingFiles,
|
|
42241
|
-
...ourFiles
|
|
42242
|
-
}).slice(-FILE_LINT_CACHE_MAX_FILE_COUNT);
|
|
42243
|
-
rulesets[rulesetHash] = {
|
|
42244
|
-
updatedAtMs: Date.now(),
|
|
42245
|
-
files: Object.fromEntries(cappedEntries)
|
|
42246
|
-
};
|
|
42247
|
-
atomicWriteJson(cacheFilePath, {
|
|
42248
|
-
version: 1,
|
|
42249
|
-
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))
|
|
42250
|
-
});
|
|
42251
|
-
}
|
|
42252
|
-
};
|
|
42253
|
-
};
|
|
42254
|
-
const hashFileContents = (filePath) => {
|
|
42020
|
+
const failOpenReadJson = (filePath, fallback) => {
|
|
42255
42021
|
try {
|
|
42256
|
-
return
|
|
42022
|
+
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
42257
42023
|
} catch {
|
|
42258
|
-
return
|
|
42024
|
+
return fallback;
|
|
42259
42025
|
}
|
|
42260
42026
|
};
|
|
42261
|
-
const ABSENT_CONTENT_ANSWER = "absent";
|
|
42262
|
-
/**
|
|
42263
|
-
* Answers dependency probes for the sidecar lint cache — used both when
|
|
42264
|
-
* STORING an entry (turn the collector's probe paths into answers) and when
|
|
42265
|
-
* VERIFYING one (re-answer each stored probe against the current tree).
|
|
42266
|
-
* Store and verify must agree exactly, so both go through this one resolver.
|
|
42267
|
-
*
|
|
42268
|
-
* - `content` — SHA-1 of the file's bytes, `"absent"` when unreadable
|
|
42269
|
-
* (missing, a directory, permission failure). Source files reuse the
|
|
42270
|
-
* hashes the cache-partition loop already computed
|
|
42271
|
-
* (`contentHashByRelativePath`); everything else (package.json,
|
|
42272
|
-
* tsconfig, ignored files) is hashed on demand.
|
|
42273
|
-
* - `exists` — the existence class a module-resolution candidate probe
|
|
42274
|
-
* sees: `"file"`, `"dir"`, or `"none"` (missing or an exotic entry —
|
|
42275
|
-
* `statSync` follows symlinks, matching the resolvers).
|
|
42276
|
-
*
|
|
42277
|
-
* Answers are memoized per scan: the filesystem is frozen for the run, and
|
|
42278
|
-
* hot dependency paths (a barrel imported by hundreds of files, the shared
|
|
42279
|
-
* tsconfig chain) repeat across many files' probe sets.
|
|
42280
|
-
*/
|
|
42281
|
-
const createSidecarProbeAnswerResolver = (input) => {
|
|
42282
|
-
const contentAnswerByPath = /* @__PURE__ */ new Map();
|
|
42283
|
-
const existsAnswerByPath = /* @__PURE__ */ new Map();
|
|
42284
|
-
const toRelativePath = (absolutePath) => Path.relative(input.rootDirectory, absolutePath).replaceAll("\\", "/");
|
|
42285
|
-
const contentAnswer = (relativePath) => {
|
|
42286
|
-
const memoized = contentAnswerByPath.get(relativePath);
|
|
42287
|
-
if (memoized !== void 0) return memoized;
|
|
42288
|
-
const answer = input.contentHashByRelativePath.get(relativePath) ?? hashFileContents(Path.resolve(input.rootDirectory, relativePath)) ?? ABSENT_CONTENT_ANSWER;
|
|
42289
|
-
contentAnswerByPath.set(relativePath, answer);
|
|
42290
|
-
return answer;
|
|
42291
|
-
};
|
|
42292
|
-
const existsAnswer = (relativePath) => {
|
|
42293
|
-
const memoized = existsAnswerByPath.get(relativePath);
|
|
42294
|
-
if (memoized !== void 0) return memoized;
|
|
42295
|
-
let answer;
|
|
42296
|
-
try {
|
|
42297
|
-
const stat = NFS.statSync(Path.resolve(input.rootDirectory, relativePath));
|
|
42298
|
-
answer = stat.isFile() ? "file" : stat.isDirectory() ? "dir" : "none";
|
|
42299
|
-
} catch {
|
|
42300
|
-
answer = "none";
|
|
42301
|
-
}
|
|
42302
|
-
existsAnswerByPath.set(relativePath, answer);
|
|
42303
|
-
return answer;
|
|
42304
|
-
};
|
|
42305
|
-
return {
|
|
42306
|
-
answerFor: (kind, relativePath) => kind === "content" ? contentAnswer(relativePath) : existsAnswer(relativePath),
|
|
42307
|
-
toRelativePath
|
|
42308
|
-
};
|
|
42309
|
-
};
|
|
42310
42027
|
const validateDiagnostic = decodeUnknownSync(Diagnostic);
|
|
42311
|
-
const
|
|
42312
|
-
|
|
42313
|
-
if (!isRecord$1(raw) || !Array.isArray(raw.probes) || !Array.isArray(raw.diagnostics)) return null;
|
|
42314
|
-
const probes = [];
|
|
42315
|
-
for (const rawProbe of raw.probes) {
|
|
42316
|
-
if (!Array.isArray(rawProbe) || rawProbe.length !== 3) return null;
|
|
42317
|
-
const [kind, probePath, answer] = rawProbe;
|
|
42318
|
-
if (!isProbeKind(kind) || typeof probePath !== "string" || typeof answer !== "string") return null;
|
|
42319
|
-
probes.push({
|
|
42320
|
-
kind,
|
|
42321
|
-
path: probePath,
|
|
42322
|
-
answer
|
|
42323
|
-
});
|
|
42324
|
-
}
|
|
42028
|
+
const decodeFileDiagnostics = (raw) => {
|
|
42029
|
+
if (!Array.isArray(raw)) return null;
|
|
42325
42030
|
try {
|
|
42326
|
-
for (const entry of raw
|
|
42031
|
+
for (const entry of raw) validateDiagnostic(entry);
|
|
42032
|
+
return raw;
|
|
42327
42033
|
} catch {
|
|
42328
42034
|
return null;
|
|
42329
42035
|
}
|
|
42330
|
-
return {
|
|
42331
|
-
probes,
|
|
42332
|
-
diagnostics: raw.diagnostics
|
|
42333
|
-
};
|
|
42334
42036
|
};
|
|
42335
|
-
const encodeSidecarFileEntry = (entry) => ({
|
|
42336
|
-
probes: entry.probes.map((probe) => [
|
|
42337
|
-
probe.kind,
|
|
42338
|
-
probe.path,
|
|
42339
|
-
probe.answer
|
|
42340
|
-
]),
|
|
42341
|
-
diagnostics: entry.diagnostics
|
|
42342
|
-
});
|
|
42343
42037
|
const emptyCache = () => ({
|
|
42344
42038
|
version: 1,
|
|
42345
42039
|
rulesets: {}
|
|
@@ -42350,20 +42044,20 @@ const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
|
|
|
42350
42044
|
if (persisted.version !== 1 || !isRecord$1(persisted.rulesets)) return entries;
|
|
42351
42045
|
const bucket = persisted.rulesets[rulesetHash];
|
|
42352
42046
|
if (!isRecord$1(bucket) || !isRecord$1(bucket.files)) return entries;
|
|
42353
|
-
for (const [fileKey,
|
|
42354
|
-
const decoded =
|
|
42047
|
+
for (const [fileKey, rawDiagnostics] of Object.entries(bucket.files)) {
|
|
42048
|
+
const decoded = decodeFileDiagnostics(rawDiagnostics);
|
|
42355
42049
|
if (decoded !== null) entries.set(fileKey, decoded);
|
|
42356
42050
|
}
|
|
42357
42051
|
return entries;
|
|
42358
42052
|
};
|
|
42359
|
-
const
|
|
42360
|
-
const cacheFilePath = Path.join(cacheDirectory,
|
|
42053
|
+
const createFileLintCache = (cacheDirectory, rulesetHash) => {
|
|
42054
|
+
const cacheFilePath = Path.join(cacheDirectory, FILE_LINT_CACHE_FILENAME);
|
|
42361
42055
|
const entries = loadRulesetEntries(cacheFilePath, rulesetHash);
|
|
42362
42056
|
return {
|
|
42363
42057
|
lookup: (fileKey) => entries.get(fileKey) ?? null,
|
|
42364
|
-
store: (fileKey,
|
|
42058
|
+
store: (fileKey, diagnostics) => {
|
|
42365
42059
|
entries.delete(fileKey);
|
|
42366
|
-
entries.set(fileKey,
|
|
42060
|
+
entries.set(fileKey, diagnostics);
|
|
42367
42061
|
},
|
|
42368
42062
|
persist: () => {
|
|
42369
42063
|
const onDisk = failOpenReadJson(cacheFilePath, emptyCache());
|
|
@@ -42371,7 +42065,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
42371
42065
|
const existingBucket = rulesets[rulesetHash];
|
|
42372
42066
|
const existingFiles = isRecord$1(existingBucket) && isRecord$1(existingBucket.files) ? existingBucket.files : {};
|
|
42373
42067
|
const ourFiles = {};
|
|
42374
|
-
for (const [fileKey,
|
|
42068
|
+
for (const [fileKey, diagnostics] of entries) ourFiles[fileKey] = diagnostics;
|
|
42375
42069
|
const cappedEntries = Object.entries({
|
|
42376
42070
|
...existingFiles,
|
|
42377
42071
|
...ourFiles
|
|
@@ -42387,7 +42081,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
42387
42081
|
}
|
|
42388
42082
|
};
|
|
42389
42083
|
};
|
|
42390
|
-
const bundledRequire$
|
|
42084
|
+
const bundledRequire$2 = createRequire(import.meta.url);
|
|
42391
42085
|
const childNodeVersionByBinaryPath = /* @__PURE__ */ new Map();
|
|
42392
42086
|
const resolveChildNodeVersion = (nodeBinaryPath) => {
|
|
42393
42087
|
if (nodeBinaryPath === process.execPath) return process.version;
|
|
@@ -42413,7 +42107,7 @@ const TOOLCHAIN_PACKAGE_SPECIFIERS$1 = [
|
|
|
42413
42107
|
const resolveOxlintToolchainVersions = (nodeBinaryPath = process.execPath) => {
|
|
42414
42108
|
const versions = [`node=${resolveChildNodeVersion(nodeBinaryPath)}`];
|
|
42415
42109
|
for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS$1) try {
|
|
42416
|
-
const packageJson = bundledRequire$
|
|
42110
|
+
const packageJson = bundledRequire$2(specifier);
|
|
42417
42111
|
const version = typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
42418
42112
|
versions.push(`${specifier}=${version}`);
|
|
42419
42113
|
} catch {
|
|
@@ -43386,6 +43080,13 @@ const validateRuleRegistration = () => {
|
|
|
43386
43080
|
].filter((entry) => entry !== null).join("; ");
|
|
43387
43081
|
console.warn(`[react-doctor] rule-registration drift: ${detail}`);
|
|
43388
43082
|
};
|
|
43083
|
+
const hashFileContents = (filePath) => {
|
|
43084
|
+
try {
|
|
43085
|
+
return crypto.createHash("sha1").update(NFS.readFileSync(filePath)).digest("hex");
|
|
43086
|
+
} catch {
|
|
43087
|
+
return null;
|
|
43088
|
+
}
|
|
43089
|
+
};
|
|
43389
43090
|
/**
|
|
43390
43091
|
* Balanced LPT (longest-processing-time-first) lint batch planner. Where
|
|
43391
43092
|
* `batchIncludePaths` greedily fills 100-file batches and leaves a small
|
|
@@ -43450,6 +43151,21 @@ const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
|
43450
43151
|
}
|
|
43451
43152
|
return batches.filter((batch) => batch.fileIndexes.length > 0).map((batch) => batch.fileIndexes.sort((leftIndex, rightIndex) => leftIndex - rightIndex).map((fileIndex) => files[fileIndex]));
|
|
43452
43153
|
};
|
|
43154
|
+
const projectCacheSubdir = (projectDirectory) => crypto.createHash("sha256").update(projectDirectory).digest("hex").slice(0, 16);
|
|
43155
|
+
const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
43156
|
+
const cacheDirOverride = process.env["REACT_DOCTOR_CACHE_DIR"]?.trim();
|
|
43157
|
+
if (cacheDirOverride) return Path.join(cacheDirOverride, projectCacheSubdir(projectDirectory));
|
|
43158
|
+
const nodeModulesDirectory = Path.join(projectDirectory, "node_modules");
|
|
43159
|
+
if (NFS.existsSync(nodeModulesDirectory)) return Path.join(nodeModulesDirectory, ".cache", "react-doctor");
|
|
43160
|
+
return Path.join(os.tmpdir(), `react-doctor-cache-${resolveUserCacheScope()}`, projectCacheSubdir(projectDirectory));
|
|
43161
|
+
};
|
|
43162
|
+
const resolveUserCacheScope = () => {
|
|
43163
|
+
try {
|
|
43164
|
+
return String(process.getuid?.() ?? os.userInfo().username);
|
|
43165
|
+
} catch {
|
|
43166
|
+
return "shared";
|
|
43167
|
+
}
|
|
43168
|
+
};
|
|
43453
43169
|
/**
|
|
43454
43170
|
* Atomically (re)writes the generated oxlintrc.json. Used twice in
|
|
43455
43171
|
* the runner: once for the primary scan, once for the
|
|
@@ -43467,106 +43183,6 @@ const writeOxlintConfig = (configPath, configToWrite) => {
|
|
|
43467
43183
|
NFS.closeSync(fileHandle);
|
|
43468
43184
|
}
|
|
43469
43185
|
};
|
|
43470
|
-
/**
|
|
43471
|
-
* Attributes diagnostics back to the file that produced them by the
|
|
43472
|
-
* normalized path oxlint echoes. Returns `null` when ANY diagnostic can't be
|
|
43473
|
-
* attributed — the path forms don't align, so the caller must skip its cache
|
|
43474
|
-
* store rather than risk caching a wrong empty result for a file that
|
|
43475
|
-
* actually had diagnostics.
|
|
43476
|
-
*/
|
|
43477
|
-
const attributeDiagnosticsToFiles = (diagnostics, files) => {
|
|
43478
|
-
const fileByNormalizedPath = /* @__PURE__ */ new Map();
|
|
43479
|
-
for (const file of files) fileByNormalizedPath.set(file.replaceAll("\\", "/"), file);
|
|
43480
|
-
const diagnosticsByFile = /* @__PURE__ */ new Map();
|
|
43481
|
-
for (const diagnostic of diagnostics) {
|
|
43482
|
-
const file = fileByNormalizedPath.get(diagnostic.filePath);
|
|
43483
|
-
if (file === void 0) return null;
|
|
43484
|
-
const fileDiagnostics = diagnosticsByFile.get(file) ?? [];
|
|
43485
|
-
fileDiagnostics.push(diagnostic);
|
|
43486
|
-
diagnosticsByFile.set(file, fileDiagnostics);
|
|
43487
|
-
}
|
|
43488
|
-
return diagnosticsByFile;
|
|
43489
|
-
};
|
|
43490
|
-
/**
|
|
43491
|
-
* Collects each file's cross-file dependency probes (via the plugin's
|
|
43492
|
-
* collectors) with their current answers. Fail-open per file: an unreadable /
|
|
43493
|
-
* unparseable / collector-failing file maps to `null` (no entry stored — it
|
|
43494
|
-
* re-lints next scan). The filesystem is frozen for the scan, so this can run
|
|
43495
|
-
* CONCURRENTLY with an oxlint child-process await — the caller starts it
|
|
43496
|
-
* unawaited before a lint pass and awaits it at store time, hiding the
|
|
43497
|
-
* in-process parse + resolution cost under the subprocess wait. The loop
|
|
43498
|
-
* yields on the cooperative time budget so the child's stdout handling (and
|
|
43499
|
-
* sibling fibers) keep flowing; each per-file collection is synchronous, so
|
|
43500
|
-
* the module-level probe recorder never sees interleaved use. Never rejects.
|
|
43501
|
-
*/
|
|
43502
|
-
const collectSidecarProbesForFiles = async (input) => {
|
|
43503
|
-
const buildProbes = (file) => {
|
|
43504
|
-
const absoluteFilePath = Path.resolve(input.rootDirectory, file);
|
|
43505
|
-
let trace;
|
|
43506
|
-
try {
|
|
43507
|
-
trace = collectCrossFileDependencyProbes({
|
|
43508
|
-
absoluteFilePath,
|
|
43509
|
-
sourceText: NFS.readFileSync(absoluteFilePath, "utf8"),
|
|
43510
|
-
ruleIds: input.boundedSidecarRuleIds
|
|
43511
|
-
});
|
|
43512
|
-
} catch {
|
|
43513
|
-
return null;
|
|
43514
|
-
}
|
|
43515
|
-
if (trace === null) return null;
|
|
43516
|
-
const probes = [];
|
|
43517
|
-
for (const contentPath of trace.contentPaths) {
|
|
43518
|
-
const relativePath = input.probeAnswers.toRelativePath(contentPath);
|
|
43519
|
-
probes.push({
|
|
43520
|
-
kind: "content",
|
|
43521
|
-
path: relativePath,
|
|
43522
|
-
answer: input.probeAnswers.answerFor("content", relativePath)
|
|
43523
|
-
});
|
|
43524
|
-
}
|
|
43525
|
-
for (const existencePath of trace.existencePaths) {
|
|
43526
|
-
const relativePath = input.probeAnswers.toRelativePath(existencePath);
|
|
43527
|
-
probes.push({
|
|
43528
|
-
kind: "exists",
|
|
43529
|
-
path: relativePath,
|
|
43530
|
-
answer: input.probeAnswers.answerFor("exists", relativePath)
|
|
43531
|
-
});
|
|
43532
|
-
}
|
|
43533
|
-
return probes;
|
|
43534
|
-
};
|
|
43535
|
-
const probesByFile = /* @__PURE__ */ new Map();
|
|
43536
|
-
let collectSliceStartedAt = performance.now();
|
|
43537
|
-
for (const file of input.files) {
|
|
43538
|
-
probesByFile.set(file, buildProbes(file));
|
|
43539
|
-
if (performance.now() - collectSliceStartedAt >= 12) {
|
|
43540
|
-
await yieldToEventLoop();
|
|
43541
|
-
collectSliceStartedAt = performance.now();
|
|
43542
|
-
}
|
|
43543
|
-
}
|
|
43544
|
-
return probesByFile;
|
|
43545
|
-
};
|
|
43546
|
-
/**
|
|
43547
|
-
* Stores each freshly-linted file's probe set + cross-file diagnostics so
|
|
43548
|
-
* the next scan can replay instead of re-linting.
|
|
43549
|
-
*/
|
|
43550
|
-
const storeSidecarEntries = (input) => {
|
|
43551
|
-
let didStoreAnyEntry = false;
|
|
43552
|
-
for (const pass of input.storablePasses) {
|
|
43553
|
-
if (!pass.isTrusted) continue;
|
|
43554
|
-
const diagnosticsByFile = attributeDiagnosticsToFiles(pass.diagnostics, pass.files);
|
|
43555
|
-
if (diagnosticsByFile === null) continue;
|
|
43556
|
-
for (const file of pass.files) {
|
|
43557
|
-
const cacheKey = input.cacheKeyByFile.get(file);
|
|
43558
|
-
if (cacheKey === void 0) continue;
|
|
43559
|
-
const probes = pass.probesByFile.get(file);
|
|
43560
|
-
if (probes === void 0 || probes === null) continue;
|
|
43561
|
-
input.sidecarCache.store(cacheKey, {
|
|
43562
|
-
probes,
|
|
43563
|
-
diagnostics: diagnosticsByFile.get(file) ?? []
|
|
43564
|
-
});
|
|
43565
|
-
didStoreAnyEntry = true;
|
|
43566
|
-
}
|
|
43567
|
-
}
|
|
43568
|
-
if (didStoreAnyEntry) input.sidecarCache.persist();
|
|
43569
|
-
};
|
|
43570
43186
|
const REACT_HOOKS_JS_DROP_PREFIX = "React Compiler rules (react-hooks-js/*) skipped — eslint-plugin-react-hooks failed to load in this environment";
|
|
43571
43187
|
/**
|
|
43572
43188
|
* Detects an oxlint config-load crash caused by the optional
|
|
@@ -43608,7 +43224,7 @@ const reactHooksJsPluginDropNote = (error) => {
|
|
|
43608
43224
|
* 6. always restore disable directives + clean up the temp dir
|
|
43609
43225
|
*/
|
|
43610
43226
|
const runOxlint = async (options) => {
|
|
43611
|
-
const { rootDirectory, project, includePaths, nodeBinaryPath = process.execPath, customRulesOnly = false, respectInlineDisables = true, adoptExistingLintConfig = true, ignoredTags = /* @__PURE__ */ new Set(), userConfig, configSourceDirectory = rootDirectory, onPartialFailure, perFileLintCacheEnabled = false,
|
|
43227
|
+
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;
|
|
43612
43228
|
const serverAuthFunctionNames = Array.isArray(userConfig?.serverAuthFunctionNames) ? userConfig.serverAuthFunctionNames.filter((entry) => typeof entry === "string" && entry.length > 0) : void 0;
|
|
43613
43229
|
const severityControls = buildRuleSeverityControls(userConfig);
|
|
43614
43230
|
validateRuleRegistration();
|
|
@@ -43626,8 +43242,7 @@ const runOxlint = async (options) => {
|
|
|
43626
43242
|
severityControls,
|
|
43627
43243
|
userPlugins,
|
|
43628
43244
|
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
|
|
43629
|
-
ruleSelection: overrides.ruleSelection
|
|
43630
|
-
sidecarRuleIdFilter: overrides.sidecarRuleIdFilter
|
|
43245
|
+
ruleSelection: overrides.ruleSelection
|
|
43631
43246
|
});
|
|
43632
43247
|
const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
|
|
43633
43248
|
const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
|
|
@@ -43730,18 +43345,15 @@ const runOxlint = async (options) => {
|
|
|
43730
43345
|
});
|
|
43731
43346
|
const cache = createFileLintCache(resolveReactDoctorCacheDir(rootDirectory), rulesetHash);
|
|
43732
43347
|
const cacheKeyByFile = /* @__PURE__ */ new Map();
|
|
43733
|
-
const contentHashByRelativePath = /* @__PURE__ */ new Map();
|
|
43734
43348
|
const missFiles = [];
|
|
43735
43349
|
const replayedDiagnostics = [];
|
|
43736
43350
|
let hashSliceStartedAt = performance.now();
|
|
43737
43351
|
for (const candidateFile of candidateFiles) {
|
|
43738
|
-
const
|
|
43739
|
-
const contentHash = hashFileContents(absoluteCandidatePath);
|
|
43352
|
+
const contentHash = hashFileContents(Path.resolve(rootDirectory, candidateFile));
|
|
43740
43353
|
if (contentHash === null) {
|
|
43741
43354
|
missFiles.push(candidateFile);
|
|
43742
43355
|
continue;
|
|
43743
43356
|
}
|
|
43744
|
-
contentHashByRelativePath.set(Path.relative(rootDirectory, absoluteCandidatePath).replaceAll("\\", "/"), contentHash);
|
|
43745
43357
|
const cacheKey = `${candidateFile.replaceAll("\\", "/")}\u0000${contentHash}`;
|
|
43746
43358
|
cacheKeyByFile.set(candidateFile, cacheKey);
|
|
43747
43359
|
const cachedDiagnostics = cache.lookup(cacheKey);
|
|
@@ -43753,111 +43365,43 @@ const runOxlint = async (options) => {
|
|
|
43753
43365
|
}
|
|
43754
43366
|
}
|
|
43755
43367
|
const cacheHitFileCount = candidateFiles.length - missFiles.length;
|
|
43756
|
-
const enabledSidecarRuleIds = Object.keys(buildConfig({
|
|
43757
|
-
extendsPaths: [],
|
|
43758
|
-
ruleSelection: "sidecar"
|
|
43759
|
-
}).rules).map((ruleKey) => ruleKey.slice(ruleKey.lastIndexOf("/") + 1)).filter((ruleId) => CROSS_FILE_RULE_IDS.has(ruleId));
|
|
43760
|
-
const boundedSidecarRuleIds = enabledSidecarRuleIds.filter((ruleId) => CROSS_FILE_DEPENDENCY_COLLECTORS.has(ruleId));
|
|
43761
|
-
const boundedSidecarRuleIdSet = new Set(boundedSidecarRuleIds);
|
|
43762
|
-
const unboundedSidecarRuleIds = enabledSidecarRuleIds.filter((ruleId) => !CROSS_FILE_DEPENDENCY_COLLECTORS.has(ruleId));
|
|
43763
|
-
const useSidecarCache = sidecarLintCacheEnabled && boundedSidecarRuleIds.length > 0;
|
|
43764
|
-
const sidecarCache = useSidecarCache ? createSidecarLintCache(resolveReactDoctorCacheDir(rootDirectory), computeRulesetHash({
|
|
43765
|
-
config: buildConfig({
|
|
43766
|
-
extendsPaths: [],
|
|
43767
|
-
ruleSelection: "sidecar",
|
|
43768
|
-
sidecarRuleIdFilter: boundedSidecarRuleIdSet
|
|
43769
|
-
}),
|
|
43770
|
-
toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
|
|
43771
|
-
ignorePatterns: combinedPatterns,
|
|
43772
|
-
tsconfigContent
|
|
43773
|
-
})) : null;
|
|
43774
|
-
const probeAnswers = createSidecarProbeAnswerResolver({
|
|
43775
|
-
rootDirectory,
|
|
43776
|
-
contentHashByRelativePath
|
|
43777
|
-
});
|
|
43778
|
-
const missProbesTask = sidecarCache === null ? null : collectSidecarProbesForFiles({
|
|
43779
|
-
files: missFiles,
|
|
43780
|
-
rootDirectory,
|
|
43781
|
-
boundedSidecarRuleIds,
|
|
43782
|
-
probeAnswers
|
|
43783
|
-
});
|
|
43784
43368
|
const fullResult = await runConfigOverFiles((overrides) => buildConfig({
|
|
43785
43369
|
extendsPaths: [],
|
|
43786
43370
|
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin
|
|
43787
43371
|
}), "oxlintrc.full.json", missFiles, options.onFileProgress && ((scannedFileCount) => options.onFileProgress?.(scannedFileCount, candidateFiles.length)));
|
|
43788
43372
|
const missFileSet = new Set(missFiles);
|
|
43789
|
-
const
|
|
43790
|
-
const sidecarReplayedDiagnostics = [];
|
|
43791
|
-
const sidecarLintFiles = [];
|
|
43792
|
-
if (sidecarCache === null) sidecarLintFiles.push(...hitFiles);
|
|
43793
|
-
else {
|
|
43794
|
-
let verifySliceStartedAt = performance.now();
|
|
43795
|
-
for (const hitFile of hitFiles) {
|
|
43796
|
-
const cacheKey = cacheKeyByFile.get(hitFile);
|
|
43797
|
-
const entry = cacheKey === void 0 ? null : sidecarCache.lookup(cacheKey);
|
|
43798
|
-
if (entry !== null && entry.probes.every((probe) => probeAnswers.answerFor(probe.kind, probe.path) === probe.answer)) sidecarReplayedDiagnostics.push(...entry.diagnostics);
|
|
43799
|
-
else sidecarLintFiles.push(hitFile);
|
|
43800
|
-
if (performance.now() - verifySliceStartedAt >= 12) {
|
|
43801
|
-
await yieldToEventLoop();
|
|
43802
|
-
verifySliceStartedAt = performance.now();
|
|
43803
|
-
}
|
|
43804
|
-
}
|
|
43805
|
-
}
|
|
43806
|
-
const sidecarProbesTask = sidecarCache === null ? null : collectSidecarProbesForFiles({
|
|
43807
|
-
files: sidecarLintFiles,
|
|
43808
|
-
rootDirectory,
|
|
43809
|
-
boundedSidecarRuleIds,
|
|
43810
|
-
probeAnswers
|
|
43811
|
-
});
|
|
43812
|
-
const sidecarReplayedFileCount = hitFiles.length - sidecarLintFiles.length;
|
|
43813
|
-
if (sidecarReplayedFileCount > 0) options.onFileProgress?.(missFiles.length + sidecarReplayedFileCount, candidateFiles.length);
|
|
43814
|
-
const boundedSidecarResult = await runConfigOverFiles(() => buildConfig({
|
|
43373
|
+
const sidecarResult = await runConfigOverFiles(() => buildConfig({
|
|
43815
43374
|
extendsPaths: [],
|
|
43816
|
-
ruleSelection: "sidecar"
|
|
43817
|
-
|
|
43818
|
-
}), "oxlintrc.sidecar.json", sidecarLintFiles, options.onFileProgress && ((scannedFileCount) => options.onFileProgress?.(missFiles.length + sidecarReplayedFileCount + scannedFileCount, candidateFiles.length)));
|
|
43819
|
-
const unboundedSidecarResult = useSidecarCache && unboundedSidecarRuleIds.length > 0 ? await runConfigOverFiles(() => buildConfig({
|
|
43820
|
-
extendsPaths: [],
|
|
43821
|
-
ruleSelection: "sidecar",
|
|
43822
|
-
sidecarRuleIdFilter: new Set(unboundedSidecarRuleIds)
|
|
43823
|
-
}), "oxlintrc.sidecar-unbounded.json", hitFiles, void 0) : {
|
|
43824
|
-
diagnostics: [],
|
|
43825
|
-
didDropReactHooksJsPlugin: false,
|
|
43826
|
-
hadPartialFailure: false
|
|
43827
|
-
};
|
|
43375
|
+
ruleSelection: "sidecar"
|
|
43376
|
+
}), "oxlintrc.sidecar.json", candidateFiles.filter((candidateFile) => !missFileSet.has(candidateFile)), options.onFileProgress && ((scannedFileCount) => options.onFileProgress?.(missFiles.length + scannedFileCount, candidateFiles.length)));
|
|
43828
43377
|
const freshCacheableDiagnostics = [];
|
|
43829
43378
|
for (const diagnostic of fullResult.diagnostics) if (!CROSS_FILE_RULE_IDS.has(diagnostic.rule)) freshCacheableDiagnostics.push(diagnostic);
|
|
43830
43379
|
onCacheStats?.(cacheHitFileCount, candidateFiles.length);
|
|
43831
|
-
|
|
43832
|
-
const
|
|
43833
|
-
|
|
43380
|
+
const missFileByNormalizedPath = /* @__PURE__ */ new Map();
|
|
43381
|
+
for (const missFile of missFiles) missFileByNormalizedPath.set(missFile.replaceAll("\\", "/"), missFile);
|
|
43382
|
+
const freshDiagnosticsByFile = /* @__PURE__ */ new Map();
|
|
43383
|
+
let isAttributionSound = true;
|
|
43384
|
+
for (const diagnostic of freshCacheableDiagnostics) {
|
|
43385
|
+
const missFile = missFileByNormalizedPath.get(diagnostic.filePath);
|
|
43386
|
+
if (missFile === void 0) {
|
|
43387
|
+
isAttributionSound = false;
|
|
43388
|
+
break;
|
|
43389
|
+
}
|
|
43390
|
+
const fileDiagnostics = freshDiagnosticsByFile.get(missFile) ?? [];
|
|
43391
|
+
fileDiagnostics.push(diagnostic);
|
|
43392
|
+
freshDiagnosticsByFile.set(missFile, fileDiagnostics);
|
|
43393
|
+
}
|
|
43394
|
+
if (!fullResult.didDropReactHooksJsPlugin && !fullResult.hadPartialFailure && isAttributionSound) {
|
|
43834
43395
|
for (const missFile of missFiles) {
|
|
43835
43396
|
const cacheKey = cacheKeyByFile.get(missFile);
|
|
43836
43397
|
if (cacheKey !== void 0) cache.store(cacheKey, freshDiagnosticsByFile.get(missFile) ?? []);
|
|
43837
43398
|
}
|
|
43838
43399
|
cache.persist();
|
|
43839
43400
|
}
|
|
43840
|
-
if (sidecarCache !== null && missProbesTask !== null && sidecarProbesTask !== null) storeSidecarEntries({
|
|
43841
|
-
sidecarCache,
|
|
43842
|
-
cacheKeyByFile,
|
|
43843
|
-
storablePasses: [{
|
|
43844
|
-
files: sidecarLintFiles,
|
|
43845
|
-
diagnostics: boundedSidecarResult.diagnostics,
|
|
43846
|
-
isTrusted: !boundedSidecarResult.hadPartialFailure,
|
|
43847
|
-
probesByFile: await sidecarProbesTask
|
|
43848
|
-
}, {
|
|
43849
|
-
files: missFiles,
|
|
43850
|
-
diagnostics: fullResult.diagnostics.filter((diagnostic) => boundedSidecarRuleIdSet.has(diagnostic.rule)),
|
|
43851
|
-
isTrusted: !fullResult.didDropReactHooksJsPlugin && !fullResult.hadPartialFailure,
|
|
43852
|
-
probesByFile: await missProbesTask
|
|
43853
|
-
}]
|
|
43854
|
-
});
|
|
43855
43401
|
return dedupeDiagnostics([
|
|
43856
43402
|
...replayedDiagnostics,
|
|
43857
43403
|
...fullResult.diagnostics,
|
|
43858
|
-
...
|
|
43859
|
-
...boundedSidecarResult.diagnostics,
|
|
43860
|
-
...unboundedSidecarResult.diagnostics
|
|
43404
|
+
...sidecarResult.diagnostics
|
|
43861
43405
|
]);
|
|
43862
43406
|
}
|
|
43863
43407
|
const baseArgs = makeBaseArgs(configPath);
|
|
@@ -43955,7 +43499,6 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
|
|
|
43955
43499
|
const concurrency = yield* OxlintConcurrency;
|
|
43956
43500
|
const lintBatchOrdering = yield* LintBatchOrdering;
|
|
43957
43501
|
const perFileLintCacheEnabled = yield* PerFileLintCacheEnabled;
|
|
43958
|
-
const sidecarLintCacheEnabled = yield* SidecarLintCacheEnabled;
|
|
43959
43502
|
const collectedFailures = [];
|
|
43960
43503
|
const diagnostics = yield* tryPromise({
|
|
43961
43504
|
try: (signal) => runOxlint({
|
|
@@ -43974,9 +43517,7 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
|
|
|
43974
43517
|
},
|
|
43975
43518
|
onFileProgress: input.onFileProgress,
|
|
43976
43519
|
perFileLintCacheEnabled,
|
|
43977
|
-
sidecarLintCacheEnabled,
|
|
43978
43520
|
onCacheStats: input.onCacheStats,
|
|
43979
|
-
onSidecarStats: input.onSidecarStats,
|
|
43980
43521
|
spawnTimeoutMs,
|
|
43981
43522
|
outputMaxBytes,
|
|
43982
43523
|
concurrency,
|
|
@@ -44751,14 +44292,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
44751
44292
|
const buildCollectDeadCode = (deadCodeTimeout) => runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
44752
44293
|
rootDirectory: scanDirectory,
|
|
44753
44294
|
parseConcurrency: deadCodeParseConcurrency,
|
|
44754
|
-
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs
|
|
44755
|
-
onCacheOutcome: (didHitCache) => {
|
|
44756
|
-
deadCodeCacheHit = didHitCache;
|
|
44757
|
-
},
|
|
44758
|
-
onSummaryCacheStats: (stats) => {
|
|
44759
|
-
deadCodeSummaryCacheHits = stats.hits;
|
|
44760
|
-
deadCodeSummaryCacheMisses = stats.misses;
|
|
44761
|
-
}
|
|
44295
|
+
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs
|
|
44762
44296
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
44763
44297
|
yield* set(deadCodeFailure, {
|
|
44764
44298
|
didFail: true,
|
|
@@ -44786,11 +44320,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
44786
44320
|
let lastReportedTotalFileCount = 0;
|
|
44787
44321
|
let lintCacheHitFileCount = null;
|
|
44788
44322
|
let lintCacheTotalFileCount = null;
|
|
44789
|
-
let lintSidecarReplayedFileCount = null;
|
|
44790
|
-
let lintSidecarTotalFileCount = null;
|
|
44791
|
-
let deadCodeCacheHit = null;
|
|
44792
|
-
let deadCodeSummaryCacheHits = null;
|
|
44793
|
-
let deadCodeSummaryCacheMisses = null;
|
|
44794
44323
|
const baseLintStream = linterService.run({
|
|
44795
44324
|
rootDirectory: scanDirectory,
|
|
44796
44325
|
project,
|
|
@@ -44810,10 +44339,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
44810
44339
|
lintCacheHitFileCount = cacheHitFileCount;
|
|
44811
44340
|
lintCacheTotalFileCount = totalConsideredFileCount;
|
|
44812
44341
|
},
|
|
44813
|
-
onSidecarStats: (sidecarReplayedFileCount, sidecarConsideredFileCount) => {
|
|
44814
|
-
lintSidecarReplayedFileCount = sidecarReplayedFileCount;
|
|
44815
|
-
lintSidecarTotalFileCount = sidecarConsideredFileCount;
|
|
44816
|
-
},
|
|
44817
44342
|
deadlineEpochMs: input.deadlineEpochMs
|
|
44818
44343
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
44819
44344
|
yield* set(lintFailure, {
|
|
@@ -44924,11 +44449,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
44924
44449
|
securityScanFailed,
|
|
44925
44450
|
lintCacheHitFileCount,
|
|
44926
44451
|
lintCacheTotalFileCount,
|
|
44927
|
-
lintSidecarReplayedFileCount,
|
|
44928
|
-
lintSidecarTotalFileCount,
|
|
44929
|
-
deadCodeCacheHit: lintFailureState.didFail ? null : deadCodeCacheHit,
|
|
44930
|
-
deadCodeSummaryCacheHits: lintFailureState.didFail ? null : deadCodeSummaryCacheHits,
|
|
44931
|
-
deadCodeSummaryCacheMisses: lintFailureState.didFail ? null : deadCodeSummaryCacheMisses,
|
|
44932
44452
|
suppressedRuleCounts: transform.summarizeSuppressions()
|
|
44933
44453
|
};
|
|
44934
44454
|
}).pipe(withSpan("runInspect", { attributes: {
|
|
@@ -45710,7 +45230,6 @@ const isNonInteractiveEnvironment = () => NON_INTERACTIVE_ENVIRONMENT_VARIABLES.
|
|
|
45710
45230
|
const REACT_DOCTOR_CONFIG_PROJECT_NAME = "react-doctor";
|
|
45711
45231
|
const STAGED_FILES_TEMP_DIR_PREFIX = "react-doctor-staged-";
|
|
45712
45232
|
const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
|
|
45713
|
-
const RUN_GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
45714
45233
|
const GH_DEFAULT_BRANCH_PROBE_TIMEOUT_MS = 5e3;
|
|
45715
45234
|
const CI_TRUST_COMPANIES = "PayPal, Rippling, and Alibaba";
|
|
45716
45235
|
const WELCOME_EXPLANATION_HOLD_MS = 1e3;
|
|
@@ -45801,7 +45320,7 @@ const makeNoopConsole = () => ({
|
|
|
45801
45320
|
});
|
|
45802
45321
|
//#endregion
|
|
45803
45322
|
//#region src/cli/utils/version.ts
|
|
45804
|
-
const VERSION = "0.6.3
|
|
45323
|
+
const VERSION = "0.6.3";
|
|
45805
45324
|
//#endregion
|
|
45806
45325
|
//#region src/cli/utils/json-mode.ts
|
|
45807
45326
|
let context = null;
|
|
@@ -46170,13 +45689,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
46170
45689
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
46171
45690
|
* standard `SENTRY_RELEASE` override.
|
|
46172
45691
|
*/
|
|
46173
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.6.3
|
|
45692
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.6.3`;
|
|
46174
45693
|
/**
|
|
46175
45694
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
46176
45695
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
46177
45696
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
46178
45697
|
*/
|
|
46179
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.6.3
|
|
45698
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.6.3") ? "development" : "production");
|
|
46180
45699
|
/**
|
|
46181
45700
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
46182
45701
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -46377,8 +45896,7 @@ const runGit = (projectRoot, args) => {
|
|
|
46377
45896
|
"ignore",
|
|
46378
45897
|
"pipe",
|
|
46379
45898
|
"ignore"
|
|
46380
|
-
]
|
|
46381
|
-
maxBuffer: RUN_GIT_MAX_BUFFER_BYTES
|
|
45899
|
+
]
|
|
46382
45900
|
}).trim();
|
|
46383
45901
|
} catch {
|
|
46384
45902
|
return null;
|
|
@@ -58400,17 +57918,11 @@ const buildOutcomeAttributes = (input) => {
|
|
|
58400
57918
|
deadlineSkippedFileCount: input.lintDeadlineSkippedFileCount ?? null,
|
|
58401
57919
|
cacheHitFiles: result.lintCacheHitFileCount ?? null,
|
|
58402
57920
|
cacheTotalFiles: result.lintCacheTotalFileCount ?? null,
|
|
58403
|
-
cacheHitRatio: result.lintCacheTotalFileCount != null && result.lintCacheTotalFileCount > 0 ? (result.lintCacheHitFileCount ?? 0) / result.lintCacheTotalFileCount : null
|
|
58404
|
-
sidecarReplayedFiles: result.lintSidecarReplayedFileCount ?? null,
|
|
58405
|
-
sidecarTotalFiles: result.lintSidecarTotalFileCount ?? null,
|
|
58406
|
-
sidecarReplayRatio: result.lintSidecarTotalFileCount != null && result.lintSidecarTotalFileCount > 0 ? (result.lintSidecarReplayedFileCount ?? 0) / result.lintSidecarTotalFileCount : null
|
|
57921
|
+
cacheHitRatio: result.lintCacheTotalFileCount != null && result.lintCacheTotalFileCount > 0 ? (result.lintCacheHitFileCount ?? 0) / result.lintCacheTotalFileCount : null
|
|
58407
57922
|
}),
|
|
58408
57923
|
...withNamespace("deadCode", {
|
|
58409
57924
|
failed: input.didDeadCodeFail ?? null,
|
|
58410
|
-
overlapped: input.deadCodeOverlapped ?? null
|
|
58411
|
-
cacheHit: result.deadCodeCacheHit ?? null,
|
|
58412
|
-
summaryCacheHits: result.deadCodeSummaryCacheHits ?? null,
|
|
58413
|
-
summaryCacheMisses: result.deadCodeSummaryCacheMisses ?? null
|
|
57925
|
+
overlapped: input.deadCodeOverlapped ?? null
|
|
58414
57926
|
}),
|
|
58415
57927
|
...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut ?? null }),
|
|
58416
57928
|
...withNamespace("securityScan", { failed: input.securityScanFailed ?? null }),
|
|
@@ -59733,89 +59245,13 @@ const stringifyStableJson = (value) => {
|
|
|
59733
59245
|
};
|
|
59734
59246
|
const hashString = (value) => crypto.createHash("sha1").update(value).digest("hex");
|
|
59735
59247
|
const readHeadSha = (projectDirectory) => runGit(projectDirectory, ["rev-parse", "HEAD"]);
|
|
59736
|
-
const
|
|
59737
|
-
const
|
|
59738
|
-
const tokens = statusOutput.split("\0");
|
|
59739
|
-
for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex += 1) {
|
|
59740
|
-
let record = tokens[tokenIndex];
|
|
59741
|
-
if (record.length === 0) continue;
|
|
59742
|
-
if (record.length < 3 || record[2] !== " ") {
|
|
59743
|
-
if (tokenIndex !== 0) return null;
|
|
59744
|
-
record = ` ${record}`;
|
|
59745
|
-
}
|
|
59746
|
-
if (record.length < 4 || record[2] !== " ") return null;
|
|
59747
|
-
const statusCode = record.slice(0, 2);
|
|
59748
|
-
const recordPath = record.slice(3);
|
|
59749
|
-
if (statusCode.includes("R") || statusCode.includes("C")) {
|
|
59750
|
-
tokenIndex += 1;
|
|
59751
|
-
const originPath = tokens[tokenIndex];
|
|
59752
|
-
if (originPath === void 0 || originPath.length === 0) return null;
|
|
59753
|
-
entries.push({
|
|
59754
|
-
statusCode,
|
|
59755
|
-
path: recordPath,
|
|
59756
|
-
originPath
|
|
59757
|
-
});
|
|
59758
|
-
continue;
|
|
59759
|
-
}
|
|
59760
|
-
entries.push({
|
|
59761
|
-
statusCode,
|
|
59762
|
-
path: recordPath
|
|
59763
|
-
});
|
|
59764
|
-
}
|
|
59765
|
-
return entries;
|
|
59766
|
-
};
|
|
59767
|
-
const buildDirtyPathContentFingerprint = (absolutePath, statusCode) => {
|
|
59768
|
-
let stats;
|
|
59769
|
-
try {
|
|
59770
|
-
stats = fs.lstatSync(absolutePath);
|
|
59771
|
-
} catch {
|
|
59772
|
-
return statusCode.includes("D") ? "deleted" : null;
|
|
59773
|
-
}
|
|
59774
|
-
if (stats.isSymbolicLink()) try {
|
|
59775
|
-
return `link:${fs.readlinkSync(absolutePath)}`;
|
|
59776
|
-
} catch {
|
|
59777
|
-
return null;
|
|
59778
|
-
}
|
|
59779
|
-
if (!stats.isFile()) return null;
|
|
59780
|
-
if (stats.size > 10485760) return `stat:${stats.mtimeMs}:${stats.size}`;
|
|
59781
|
-
return hashFileContents(absolutePath);
|
|
59782
|
-
};
|
|
59783
|
-
const buildWorktreeFingerprint = (projectDirectory) => {
|
|
59784
|
-
const statusOutput = runGit(projectDirectory, [
|
|
59248
|
+
const isWorktreeClean = (projectDirectory) => {
|
|
59249
|
+
const status = runGit(projectDirectory, [
|
|
59785
59250
|
"status",
|
|
59786
59251
|
"--porcelain=v1",
|
|
59787
|
-
"-
|
|
59788
|
-
"--untracked-files=all"
|
|
59252
|
+
"--untracked-files=normal"
|
|
59789
59253
|
]);
|
|
59790
|
-
|
|
59791
|
-
if (statusOutput.length === 0) return [];
|
|
59792
|
-
const statusEntries = parseWorktreeStatusRecords(statusOutput);
|
|
59793
|
-
if (statusEntries === null) return null;
|
|
59794
|
-
if (statusEntries.length > 300) return null;
|
|
59795
|
-
const repositoryRoot = runGit(projectDirectory, ["rev-parse", "--show-toplevel"]);
|
|
59796
|
-
if (repositoryRoot === null) return null;
|
|
59797
|
-
const dirtyEntries = [];
|
|
59798
|
-
for (const statusEntry of statusEntries) {
|
|
59799
|
-
const contentFingerprint = buildDirtyPathContentFingerprint(path.join(repositoryRoot, statusEntry.path), statusEntry.statusCode);
|
|
59800
|
-
if (contentFingerprint === null) return null;
|
|
59801
|
-
dirtyEntries.push({
|
|
59802
|
-
...statusEntry,
|
|
59803
|
-
contentFingerprint
|
|
59804
|
-
});
|
|
59805
|
-
}
|
|
59806
|
-
return dirtyEntries.sort((firstEntry, secondEntry) => {
|
|
59807
|
-
const firstSortKey = `${firstEntry.path}${firstEntry.statusCode}`;
|
|
59808
|
-
const secondSortKey = `${secondEntry.path}${secondEntry.statusCode}`;
|
|
59809
|
-
return firstSortKey < secondSortKey ? -1 : firstSortKey > secondSortKey ? 1 : 0;
|
|
59810
|
-
});
|
|
59811
|
-
};
|
|
59812
|
-
const DOTENV_FILE_NAME_PATTERN = /^\.env(\.|$)/;
|
|
59813
|
-
const resolveDotenvFingerprint = (projectDirectory) => {
|
|
59814
|
-
try {
|
|
59815
|
-
return fs.readdirSync(projectDirectory).filter((entryName) => DOTENV_FILE_NAME_PATTERN.test(entryName)).sort().map((entryName) => `${entryName}=${fileFingerprint(path.join(projectDirectory, entryName)) ?? "unreadable"}`);
|
|
59816
|
-
} catch {
|
|
59817
|
-
return [];
|
|
59818
|
-
}
|
|
59254
|
+
return status !== null && status.length === 0;
|
|
59819
59255
|
};
|
|
59820
59256
|
const hasHiddenTrackedFileState = (projectDirectory) => {
|
|
59821
59257
|
const output = runGit(projectDirectory, ["ls-files", "-v"]);
|
|
@@ -59898,9 +59334,8 @@ const resolveToolchainFingerprint = (nodeBinaryPath) => {
|
|
|
59898
59334
|
};
|
|
59899
59335
|
const buildScanResultCacheKey = (input) => {
|
|
59900
59336
|
if (isScanResultCacheDisabled()) return null;
|
|
59337
|
+
if (!isWorktreeClean(input.projectDirectory)) return null;
|
|
59901
59338
|
if (hasHiddenTrackedFileState(input.projectDirectory)) return null;
|
|
59902
|
-
const worktreeFingerprint = buildWorktreeFingerprint(input.projectDirectory);
|
|
59903
|
-
if (worktreeFingerprint === null) return null;
|
|
59904
59339
|
const headSha = readHeadSha(input.projectDirectory);
|
|
59905
59340
|
if (headSha === null) return null;
|
|
59906
59341
|
if (stringifyStableJson(input.userConfig) === null) return null;
|
|
@@ -59908,8 +59343,6 @@ const buildScanResultCacheKey = (input) => {
|
|
|
59908
59343
|
schemaVersion: 3,
|
|
59909
59344
|
projectIdentity: resolveProjectIdentity(input.projectDirectory),
|
|
59910
59345
|
headSha,
|
|
59911
|
-
worktreeFingerprint,
|
|
59912
|
-
dotenvFingerprint: resolveDotenvFingerprint(input.projectDirectory),
|
|
59913
59346
|
reactDoctorVersion: input.version,
|
|
59914
59347
|
nodeVersion: process.version,
|
|
59915
59348
|
toolchainFingerprint: resolveToolchainFingerprint(input.nodeBinaryPath),
|
|
@@ -60302,12 +59735,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
60302
59735
|
scanMode: baselineDelta ? "baseline" : isDiffMode ? "diff" : "full",
|
|
60303
59736
|
baselineDegraded,
|
|
60304
59737
|
lintCacheHitFileCount: output.lintCacheHitFileCount,
|
|
60305
|
-
lintCacheTotalFileCount: output.lintCacheTotalFileCount
|
|
60306
|
-
lintSidecarReplayedFileCount: output.lintSidecarReplayedFileCount,
|
|
60307
|
-
lintSidecarTotalFileCount: output.lintSidecarTotalFileCount,
|
|
60308
|
-
deadCodeCacheHit: output.deadCodeCacheHit,
|
|
60309
|
-
deadCodeSummaryCacheHits: output.deadCodeSummaryCacheHits,
|
|
60310
|
-
deadCodeSummaryCacheMisses: output.deadCodeSummaryCacheMisses
|
|
59738
|
+
lintCacheTotalFileCount: output.lintCacheTotalFileCount
|
|
60311
59739
|
});
|
|
60312
59740
|
recordOnboardingCompletion(options);
|
|
60313
59741
|
return result;
|
|
@@ -60342,11 +59770,6 @@ const renderAndRecordScan = async (input) => {
|
|
|
60342
59770
|
scanElapsedMilliseconds: input.payload.scanElapsedMilliseconds,
|
|
60343
59771
|
lintCacheHitFileCount: input.lintCacheHitFileCount ?? null,
|
|
60344
59772
|
lintCacheTotalFileCount: input.lintCacheTotalFileCount ?? null,
|
|
60345
|
-
lintSidecarReplayedFileCount: input.lintSidecarReplayedFileCount ?? null,
|
|
60346
|
-
lintSidecarTotalFileCount: input.lintSidecarTotalFileCount ?? null,
|
|
60347
|
-
deadCodeCacheHit: input.deadCodeCacheHit ?? null,
|
|
60348
|
-
deadCodeSummaryCacheHits: input.deadCodeSummaryCacheHits ?? null,
|
|
60349
|
-
deadCodeSummaryCacheMisses: input.deadCodeSummaryCacheMisses ?? null,
|
|
60350
59773
|
baselineDelta: input.payload.baselineDelta
|
|
60351
59774
|
}), input.options.silent));
|
|
60352
59775
|
const { workerCount: resolvedWorkerCount, parallel } = resolveWorkerTelemetry(input.payload.scanConcurrency, input.options.concurrency);
|
|
@@ -60385,7 +59808,7 @@ const renderAndRecordScan = async (input) => {
|
|
|
60385
59808
|
return result;
|
|
60386
59809
|
};
|
|
60387
59810
|
const finalizeAndRender = (input) => gen(function* () {
|
|
60388
|
-
const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, directory, scannedFileCount, scannedFilePaths, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount,
|
|
59811
|
+
const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, directory, scannedFileCount, scannedFilePaths, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount, baselineDelta } = input;
|
|
60389
59812
|
const { skippedChecks, skippedCheckReasons } = buildSkippedChecks({
|
|
60390
59813
|
didLintFail,
|
|
60391
59814
|
lintFailureReason,
|
|
@@ -60409,15 +59832,6 @@ const finalizeAndRender = (input) => gen(function* () {
|
|
|
60409
59832
|
lintCacheHitFileCount,
|
|
60410
59833
|
lintCacheTotalFileCount
|
|
60411
59834
|
} : {},
|
|
60412
|
-
...lintSidecarTotalFileCount !== null ? {
|
|
60413
|
-
lintSidecarReplayedFileCount,
|
|
60414
|
-
lintSidecarTotalFileCount
|
|
60415
|
-
} : {},
|
|
60416
|
-
...deadCodeCacheHit !== null ? { deadCodeCacheHit } : {},
|
|
60417
|
-
...deadCodeSummaryCacheHits !== null && deadCodeSummaryCacheMisses !== null ? {
|
|
60418
|
-
deadCodeSummaryCacheHits,
|
|
60419
|
-
deadCodeSummaryCacheMisses
|
|
60420
|
-
} : {},
|
|
60421
59835
|
...baselineDelta ? { baselineDelta } : {}
|
|
60422
59836
|
});
|
|
60423
59837
|
if (options.suppressRendering) return buildResult();
|
|
@@ -64264,4 +63678,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
64264
63678
|
export {};
|
|
64265
63679
|
|
|
64266
63680
|
//# sourceMappingURL=cli.js.map
|
|
64267
|
-
//# debugId=
|
|
63681
|
+
//# debugId=328318eb-6af2-5007-b816-51c953a96047
|