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/index.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]="fb390a00-3616-5547-b572-1ce120b0e1a2")}catch(e){}}();
|
|
3
3
|
import { r as __toESM$1, t as __commonJSMin$1 } from "./chunk-N93fKeF6.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import * as NFS from "node:fs";
|
|
@@ -9,7 +9,7 @@ import path from "node:path";
|
|
|
9
9
|
import * as NodeChildProcess from "node:child_process";
|
|
10
10
|
import { execFileSync, spawn, spawnSync } from "node:child_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 from "node:os";
|
|
15
15
|
import { parseJSON5 } from "confbox";
|
|
@@ -18,7 +18,6 @@ import { fileURLToPath } from "node:url";
|
|
|
18
18
|
import { createJiti } from "jiti";
|
|
19
19
|
import * as Crypto from "node:crypto";
|
|
20
20
|
import crypto, { createHash } from "node:crypto";
|
|
21
|
-
import { ANALYZED_MANIFEST_FILENAMES, DEFAULT_EXTENSIONS } from "deslop-js/analyzed-inputs";
|
|
22
21
|
import { gzipSync } from "node:zlib";
|
|
23
22
|
//#region ../../node_modules/.pnpm/effect@4.0.0-beta.70/node_modules/effect/dist/Pipeable.js
|
|
24
23
|
/**
|
|
@@ -32928,15 +32927,11 @@ const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
|
|
|
32928
32927
|
const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
32929
32928
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
32930
32929
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
32931
|
-
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
32932
|
-
const CORE_PACKAGE_VERSION = "0.6.3";
|
|
32933
32930
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
32934
|
-
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
32935
32931
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
32936
32932
|
const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
|
|
32937
32933
|
const SUPPLY_CHAIN_CATEGORY = "Security";
|
|
32938
32934
|
const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
|
|
32939
|
-
const SUPPLY_CHAIN_CACHE_TTL_MS = 864e5;
|
|
32940
32935
|
const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
|
|
32941
32936
|
const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
|
|
32942
32937
|
const hasIgnoredPathSegment = (relativePath) => relativePath.split(/[/\\]/).slice(0, -1).some((segment) => IGNORED_DIRECTORIES.has(segment));
|
|
@@ -35620,29 +35615,6 @@ var PerFileLintCacheEnabled = class extends Reference("react-doctor/PerFileLintC
|
|
|
35620
35615
|
return true;
|
|
35621
35616
|
} }) {};
|
|
35622
35617
|
/**
|
|
35623
|
-
* Whether the sidecar lint cache (`runners/oxlint/sidecar-lint-cache.ts`) is
|
|
35624
|
-
* active. Defaults ON — warm rescans replay the cross-file rules' cached
|
|
35625
|
-
* diagnostics for every file whose dependency fingerprint (the probe set the
|
|
35626
|
-
* plugin's collectors recorded) still matches the tree, and re-lint only the
|
|
35627
|
-
* rest. Only reachable when the per-file lint cache itself is on (the
|
|
35628
|
-
* sidecar covers its cache hits), so `REACT_DOCTOR_NO_CACHE` /
|
|
35629
|
-
* `REACT_DOCTOR_NO_FILE_CACHE` implicitly disable it too. Opt-OUT knobs:
|
|
35630
|
-
*
|
|
35631
|
-
* - `REACT_DOCTOR_NO_CACHE` — the global off-switch.
|
|
35632
|
-
* - `REACT_DOCTOR_NO_SIDECAR_CACHE` — granular rollback hatch: keep the
|
|
35633
|
-
* per-file cache but run the always-fresh sidecar over every hit
|
|
35634
|
-
* (the pre-cache behavior).
|
|
35635
|
-
*
|
|
35636
|
-
* Tests override via `Layer.succeed(SidecarLintCacheEnabled, false)`.
|
|
35637
|
-
*/
|
|
35638
|
-
var SidecarLintCacheEnabled = class extends Reference("react-doctor/SidecarLintCacheEnabled", { defaultValue: () => {
|
|
35639
|
-
const noCache = process.env["REACT_DOCTOR_NO_CACHE"]?.toLowerCase() ?? "";
|
|
35640
|
-
const noSidecarCache = process.env["REACT_DOCTOR_NO_SIDECAR_CACHE"]?.toLowerCase() ?? "";
|
|
35641
|
-
if (CACHE_DISABLED_VALUES.has(noCache)) return false;
|
|
35642
|
-
if (CACHE_DISABLED_VALUES.has(noSidecarCache)) return false;
|
|
35643
|
-
return true;
|
|
35644
|
-
} }) {};
|
|
35645
|
-
/**
|
|
35646
35618
|
* Whether the whole-project dead-code result cache
|
|
35647
35619
|
* (`dead-code/dead-code-result-cache.ts`) is active. Defaults ON — a rescan
|
|
35648
35620
|
* whose inputs (source tree, manifests, configs, analyzer version) are
|
|
@@ -37366,37 +37338,51 @@ const failOpenReadJson = (filePath, fallback) => {
|
|
|
37366
37338
|
return fallback;
|
|
37367
37339
|
}
|
|
37368
37340
|
};
|
|
37369
|
-
const
|
|
37370
|
-
|
|
37371
|
-
|
|
37372
|
-
|
|
37373
|
-
|
|
37374
|
-
|
|
37375
|
-
|
|
37376
|
-
|
|
37377
|
-
|
|
37378
|
-
|
|
37341
|
+
const ANALYZED_FILE_EXTENSIONS = new Set([
|
|
37342
|
+
".ts",
|
|
37343
|
+
".tsx",
|
|
37344
|
+
".js",
|
|
37345
|
+
".jsx",
|
|
37346
|
+
".mts",
|
|
37347
|
+
".mjs",
|
|
37348
|
+
".cts",
|
|
37349
|
+
".cjs",
|
|
37350
|
+
".mdx",
|
|
37351
|
+
".astro",
|
|
37352
|
+
".graphql",
|
|
37353
|
+
".gql",
|
|
37354
|
+
".css",
|
|
37355
|
+
".scss",
|
|
37356
|
+
".vue",
|
|
37357
|
+
".svelte"
|
|
37358
|
+
]);
|
|
37359
|
+
const ANALYZED_MANIFEST_NAMES = new Set([
|
|
37360
|
+
"package.json",
|
|
37361
|
+
"knip.json",
|
|
37362
|
+
"app.json",
|
|
37363
|
+
"pnpm-workspace.yaml",
|
|
37364
|
+
"lerna.json",
|
|
37365
|
+
"pnpm-lock.yaml",
|
|
37366
|
+
"package-lock.json",
|
|
37367
|
+
"yarn.lock",
|
|
37368
|
+
"bun.lock",
|
|
37369
|
+
"bun.lockb",
|
|
37370
|
+
"deno.lock",
|
|
37371
|
+
".gitignore"
|
|
37372
|
+
]);
|
|
37379
37373
|
const isTsConfigLikeFile = (fileName) => (fileName.startsWith("tsconfig") || fileName.startsWith("jsconfig")) && fileName.endsWith(".json");
|
|
37380
37374
|
const isFingerprintedFile = (fileName) => ANALYZED_FILE_EXTENSIONS.has(Path.extname(fileName).toLowerCase()) || ANALYZED_MANIFEST_NAMES.has(fileName) || isTsConfigLikeFile(fileName);
|
|
37381
|
-
|
|
37382
|
-
|
|
37383
|
-
* `/`-separated path. Taken BEFORE the (long) analysis so a stored result is
|
|
37384
|
-
* verified against the tree it started from.
|
|
37385
|
-
*/
|
|
37386
|
-
const collectAnalyzedFileStats = (rootDirectory) => {
|
|
37387
|
-
const statByRelativePath = /* @__PURE__ */ new Map();
|
|
37375
|
+
const collectAnalyzedFileFingerprints = (rootDirectory) => {
|
|
37376
|
+
const fingerprints = [];
|
|
37388
37377
|
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
|
|
37389
37378
|
if (!isFingerprintedFile(name)) continue;
|
|
37390
37379
|
try {
|
|
37391
37380
|
const fileStat = NFS.statSync(absolutePath);
|
|
37392
37381
|
const relativePath = Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/");
|
|
37393
|
-
|
|
37394
|
-
mtimeMs: fileStat.mtimeMs,
|
|
37395
|
-
size: fileStat.size
|
|
37396
|
-
});
|
|
37382
|
+
fingerprints.push(`${relativePath}:${fileStat.mtimeMs}:${fileStat.size}`);
|
|
37397
37383
|
} catch {}
|
|
37398
37384
|
}
|
|
37399
|
-
return
|
|
37385
|
+
return fingerprints.sort();
|
|
37400
37386
|
};
|
|
37401
37387
|
const bundledRequire$2 = createRequire(import.meta.url);
|
|
37402
37388
|
const resolveDeslopVersion = () => {
|
|
@@ -37408,85 +37394,33 @@ const resolveDeslopVersion = () => {
|
|
|
37408
37394
|
}
|
|
37409
37395
|
};
|
|
37410
37396
|
const computeDeadCodeCacheKey = (input) => crypto.createHash("sha1").update(JSON.stringify({
|
|
37411
|
-
schemaVersion:
|
|
37412
|
-
coreVersion: input.coreVersion,
|
|
37397
|
+
schemaVersion: 1,
|
|
37413
37398
|
deslopVersion: resolveDeslopVersion(),
|
|
37414
37399
|
deslopJsModuleSpecifier: input.deslopJsModuleSpecifier,
|
|
37415
37400
|
entryPatterns: input.entryPatterns,
|
|
37416
37401
|
ignorePatterns: input.ignorePatterns,
|
|
37417
|
-
tsConfigFile: input.tsConfigPath === void 0 ? null : Path.relative(input.rootDirectory, input.tsConfigPath).replace(/\\/g, "/")
|
|
37402
|
+
tsConfigFile: input.tsConfigPath === void 0 ? null : Path.relative(input.rootDirectory, input.tsConfigPath).replace(/\\/g, "/"),
|
|
37403
|
+
files: collectAnalyzedFileFingerprints(input.rootDirectory)
|
|
37418
37404
|
})).digest("hex");
|
|
37419
|
-
const validateDiagnostic$
|
|
37405
|
+
const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
|
|
37420
37406
|
const decodeCachedDiagnostics = (raw) => {
|
|
37421
37407
|
try {
|
|
37422
|
-
for (const entry of raw) validateDiagnostic$
|
|
37408
|
+
for (const entry of raw) validateDiagnostic$1(entry);
|
|
37423
37409
|
return raw;
|
|
37424
37410
|
} catch {
|
|
37425
37411
|
return null;
|
|
37426
37412
|
}
|
|
37427
37413
|
};
|
|
37428
|
-
const
|
|
37429
|
-
const
|
|
37430
|
-
|
|
37431
|
-
|
|
37432
|
-
if (persisted === null || !isRecord(persisted) || persisted.version !== 2 || persisted.key !== input.cacheKey || !isRecord(persisted.files) || !Array.isArray(persisted.diagnostics)) return null;
|
|
37433
|
-
const storedFileEntries = Object.entries(persisted.files);
|
|
37434
|
-
if (storedFileEntries.length !== input.currentFileStats.size) return null;
|
|
37435
|
-
const repairedFiles = {};
|
|
37436
|
-
let repairedCount = 0;
|
|
37437
|
-
for (const [relativePath, storedIdentity] of storedFileEntries) {
|
|
37438
|
-
if (!isPersistedFileIdentity(storedIdentity)) return null;
|
|
37439
|
-
const currentStat = input.currentFileStats.get(relativePath);
|
|
37440
|
-
if (currentStat === void 0) return null;
|
|
37441
|
-
const [storedMtimeMs, storedSize, storedContentHash] = storedIdentity;
|
|
37442
|
-
if (currentStat.mtimeMs === storedMtimeMs && currentStat.size === storedSize) {
|
|
37443
|
-
repairedFiles[relativePath] = storedIdentity;
|
|
37444
|
-
continue;
|
|
37445
|
-
}
|
|
37446
|
-
if (currentStat.size !== storedSize) return null;
|
|
37447
|
-
const currentContentHash = hashFileContents(Path.join(input.rootDirectory, relativePath));
|
|
37448
|
-
if (currentContentHash === null || currentContentHash !== storedContentHash) return null;
|
|
37449
|
-
repairedFiles[relativePath] = [
|
|
37450
|
-
currentStat.mtimeMs,
|
|
37451
|
-
currentStat.size,
|
|
37452
|
-
storedContentHash
|
|
37453
|
-
];
|
|
37454
|
-
repairedCount += 1;
|
|
37455
|
-
}
|
|
37456
|
-
const diagnostics = decodeCachedDiagnostics(persisted.diagnostics);
|
|
37457
|
-
if (diagnostics === null) return null;
|
|
37458
|
-
if (repairedCount > 0) atomicWriteJson(cacheFilePath, {
|
|
37459
|
-
version: 2,
|
|
37460
|
-
key: input.cacheKey,
|
|
37461
|
-
files: repairedFiles,
|
|
37462
|
-
diagnostics: persisted.diagnostics
|
|
37463
|
-
});
|
|
37464
|
-
return diagnostics;
|
|
37414
|
+
const lookupDeadCodeResultCache = (cacheDirectory, cacheKey) => {
|
|
37415
|
+
const persisted = failOpenReadJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), null);
|
|
37416
|
+
if (persisted === null || !isRecord(persisted) || persisted.version !== 1 || persisted.key !== cacheKey || !Array.isArray(persisted.diagnostics)) return null;
|
|
37417
|
+
return decodeCachedDiagnostics(persisted.diagnostics);
|
|
37465
37418
|
};
|
|
37466
|
-
const storeDeadCodeResultCache = (
|
|
37467
|
-
|
|
37468
|
-
|
|
37469
|
-
|
|
37470
|
-
|
|
37471
|
-
if (contentHash === null) return;
|
|
37472
|
-
let currentStat;
|
|
37473
|
-
try {
|
|
37474
|
-
currentStat = NFS.statSync(absolutePath);
|
|
37475
|
-
} catch {
|
|
37476
|
-
return;
|
|
37477
|
-
}
|
|
37478
|
-
if (currentStat.mtimeMs !== snapshotStat.mtimeMs || currentStat.size !== snapshotStat.size) return;
|
|
37479
|
-
persistedFiles[relativePath] = [
|
|
37480
|
-
snapshotStat.mtimeMs,
|
|
37481
|
-
snapshotStat.size,
|
|
37482
|
-
contentHash
|
|
37483
|
-
];
|
|
37484
|
-
}
|
|
37485
|
-
atomicWriteJson(Path.join(input.cacheDirectory, DEAD_CODE_CACHE_FILENAME), {
|
|
37486
|
-
version: 2,
|
|
37487
|
-
key: input.cacheKey,
|
|
37488
|
-
files: persistedFiles,
|
|
37489
|
-
diagnostics: input.diagnostics
|
|
37419
|
+
const storeDeadCodeResultCache = (cacheDirectory, cacheKey, diagnostics) => {
|
|
37420
|
+
atomicWriteJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), {
|
|
37421
|
+
version: 1,
|
|
37422
|
+
key: cacheKey,
|
|
37423
|
+
diagnostics
|
|
37490
37424
|
});
|
|
37491
37425
|
};
|
|
37492
37426
|
const readSystemFacts = () => ({
|
|
@@ -37625,14 +37559,6 @@ process.stdin.on("end", () => {
|
|
|
37625
37559
|
circularDependencies: result.circularDependencies.map((cycle) => ({
|
|
37626
37560
|
files: cycle.files,
|
|
37627
37561
|
})),
|
|
37628
|
-
...(result.incrementalCacheStats
|
|
37629
|
-
? {
|
|
37630
|
-
summaryCacheStats: {
|
|
37631
|
-
hits: result.incrementalCacheStats.summaryHits,
|
|
37632
|
-
misses: result.incrementalCacheStats.summaryMisses,
|
|
37633
|
-
},
|
|
37634
|
-
}
|
|
37635
|
-
: {}),
|
|
37636
37562
|
});
|
|
37637
37563
|
|
|
37638
37564
|
const serializeError = (error) =>
|
|
@@ -37656,9 +37582,6 @@ process.stdin.on("end", () => {
|
|
|
37656
37582
|
...(workerInput.ignorePatterns.length > 0
|
|
37657
37583
|
? { ignorePatterns: workerInput.ignorePatterns }
|
|
37658
37584
|
: {}),
|
|
37659
|
-
...(workerInput.incrementalCachePath
|
|
37660
|
-
? { incrementalCachePath: workerInput.incrementalCachePath }
|
|
37661
|
-
: {}),
|
|
37662
37585
|
// We consume only deslop's GRAPH-based findings (unusedFiles, unusedExports,
|
|
37663
37586
|
// unusedDependencies, circularDependencies). Everything else deslop can compute
|
|
37664
37587
|
// is pure wasted work for us, and it's the bulk of the runtime:
|
|
@@ -37669,18 +37592,12 @@ process.stdin.on("end", () => {
|
|
|
37669
37592
|
// are the single most expensive pass — duplicate-block detection alone was
|
|
37670
37593
|
// ~83s of a ~130s Sentry scan — so skipping them is an ~8.5x dead-code
|
|
37671
37594
|
// speedup on a large repo.
|
|
37672
|
-
//
|
|
37673
|
-
// simplifiable functions, identity wrappers, …) — ~120 ms of discarded
|
|
37674
|
-
// output per scan, and skipping them lets the incremental cache drop the
|
|
37675
|
-
// DRY-pattern summary fields (the largest slice of the cache file).
|
|
37676
|
-
// All are provably safe: the consumed graph findings are computed by their own
|
|
37595
|
+
// Both are provably safe: the consumed graph findings are computed by their own
|
|
37677
37596
|
// detectors, independent of these passes (confirmed byte-identical on
|
|
37678
|
-
// excalidraw + mui-material + sentry
|
|
37679
|
-
//
|
|
37680
|
-
// for path-alias resolution in the import graph.
|
|
37597
|
+
// excalidraw + mui-material + sentry). tsConfigPath stays — the module resolver
|
|
37598
|
+
// needs it for path-alias resolution in the import graph.
|
|
37681
37599
|
semantic: { enabled: false },
|
|
37682
37600
|
reportCodeQuality: false,
|
|
37683
|
-
reportRedundancy: false,
|
|
37684
37601
|
};
|
|
37685
37602
|
const result = await analyze(defineConfig(config));
|
|
37686
37603
|
emit({ ok: true, result: normalizeResult(result) });
|
|
@@ -37764,23 +37681,13 @@ const parseCircularDependencies = (value) => {
|
|
|
37764
37681
|
}
|
|
37765
37682
|
return circularDependencies;
|
|
37766
37683
|
};
|
|
37767
|
-
const parseSummaryCacheStats = (value) => {
|
|
37768
|
-
if (!isRecord(value)) return void 0;
|
|
37769
|
-
if (typeof value.hits !== "number" || typeof value.misses !== "number") return void 0;
|
|
37770
|
-
return {
|
|
37771
|
-
hits: value.hits,
|
|
37772
|
-
misses: value.misses
|
|
37773
|
-
};
|
|
37774
|
-
};
|
|
37775
37684
|
const parseDeadCodeWorkerResult = (value) => {
|
|
37776
37685
|
if (!isRecord(value)) throw new Error("Dead-code worker returned an invalid result.");
|
|
37777
|
-
const summaryCacheStats = parseSummaryCacheStats(value.summaryCacheStats);
|
|
37778
37686
|
return {
|
|
37779
37687
|
unusedFiles: parseUnusedFiles(value.unusedFiles),
|
|
37780
37688
|
unusedExports: parseUnusedExports(value.unusedExports),
|
|
37781
37689
|
unusedDependencies: parseUnusedDependencies(value.unusedDependencies),
|
|
37782
|
-
circularDependencies: parseCircularDependencies(value.circularDependencies)
|
|
37783
|
-
...summaryCacheStats ? { summaryCacheStats } : {}
|
|
37690
|
+
circularDependencies: parseCircularDependencies(value.circularDependencies)
|
|
37784
37691
|
};
|
|
37785
37692
|
};
|
|
37786
37693
|
const parseDeadCodeWorkerError = (value) => {
|
|
@@ -37896,26 +37803,18 @@ const checkDeadCode = async (options) => {
|
|
|
37896
37803
|
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
37897
37804
|
const tsConfigPath = resolveTsConfigPath(rootDirectory);
|
|
37898
37805
|
const deslopJsModuleSpecifier = options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js");
|
|
37899
|
-
const
|
|
37900
|
-
const cacheKey = fileStatsSnapshot === null ? null : computeDeadCodeCacheKey({
|
|
37806
|
+
const cacheKey = options.cacheEnabled ? computeDeadCodeCacheKey({
|
|
37901
37807
|
rootDirectory,
|
|
37902
37808
|
entryPatterns,
|
|
37903
37809
|
ignorePatterns,
|
|
37904
37810
|
tsConfigPath,
|
|
37905
|
-
deslopJsModuleSpecifier
|
|
37906
|
-
|
|
37907
|
-
|
|
37908
|
-
|
|
37909
|
-
const cachedDiagnostics = lookupDeadCodeResultCache({
|
|
37910
|
-
cacheDirectory: resolveReactDoctorCacheDir(rootDirectory),
|
|
37911
|
-
cacheKey,
|
|
37912
|
-
rootDirectory,
|
|
37913
|
-
currentFileStats: fileStatsSnapshot
|
|
37914
|
-
});
|
|
37811
|
+
deslopJsModuleSpecifier
|
|
37812
|
+
}) : null;
|
|
37813
|
+
if (cacheKey !== null) {
|
|
37814
|
+
const cachedDiagnostics = lookupDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey);
|
|
37915
37815
|
options.onCacheOutcome?.(cachedDiagnostics !== null);
|
|
37916
37816
|
if (cachedDiagnostics !== null) return [...cachedDiagnostics];
|
|
37917
37817
|
}
|
|
37918
|
-
const incrementalCachePath = options.cacheEnabled === true ? Path.join(resolveReactDoctorCacheDir(rootDirectory), DEAD_CODE_SUMMARY_CACHE_FILENAME) : void 0;
|
|
37919
37818
|
const spawnAndRun = () => {
|
|
37920
37819
|
return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
37921
37820
|
rootDirectory,
|
|
@@ -37923,12 +37822,10 @@ const checkDeadCode = async (options) => {
|
|
|
37923
37822
|
tsConfigPath,
|
|
37924
37823
|
ignorePatterns,
|
|
37925
37824
|
deslopJsModuleSpecifier,
|
|
37926
|
-
parseConcurrency: options.parseConcurrency
|
|
37927
|
-
incrementalCachePath
|
|
37825
|
+
parseConcurrency: options.parseConcurrency
|
|
37928
37826
|
}), options.workerTimeoutMs ?? 12e4, options.abortSignal);
|
|
37929
37827
|
};
|
|
37930
37828
|
const result = parseDeadCodeWorkerResult(options.createWorker === void 0 ? await withDeadCodeWorkerSlot(spawnAndRun, options.abortSignal) : await spawnAndRun());
|
|
37931
|
-
if (result.summaryCacheStats !== void 0) options.onSummaryCacheStats?.(result.summaryCacheStats);
|
|
37932
37829
|
const toRelative = (filePath) => toRelativeFilePath(rootDirectory, filePath);
|
|
37933
37830
|
const diagnostics = [];
|
|
37934
37831
|
for (const unusedFile of result.unusedFiles) diagnostics.push({
|
|
@@ -37985,13 +37882,7 @@ const checkDeadCode = async (options) => {
|
|
|
37985
37882
|
category: DEAD_CODE_CATEGORY
|
|
37986
37883
|
});
|
|
37987
37884
|
}
|
|
37988
|
-
if (cacheKey !== null
|
|
37989
|
-
cacheDirectory: resolveReactDoctorCacheDir(rootDirectory),
|
|
37990
|
-
cacheKey,
|
|
37991
|
-
rootDirectory,
|
|
37992
|
-
snapshotFileStats: fileStatsSnapshot,
|
|
37993
|
-
diagnostics
|
|
37994
|
-
});
|
|
37885
|
+
if (cacheKey !== null) storeDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey, diagnostics);
|
|
37995
37886
|
return diagnostics;
|
|
37996
37887
|
};
|
|
37997
37888
|
const DEAD_CODE_RULE_KEY_PREFIX = `${DEAD_CODE_PLUGIN}/`;
|
|
@@ -38102,8 +37993,7 @@ var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
|
|
|
38102
37993
|
workerTimeoutMs: input.workerTimeoutMs,
|
|
38103
37994
|
abortSignal: signal,
|
|
38104
37995
|
cacheEnabled,
|
|
38105
|
-
onCacheOutcome: input.onCacheOutcome
|
|
38106
|
-
onSummaryCacheStats: input.onSummaryCacheStats
|
|
37996
|
+
onCacheOutcome: input.onCacheOutcome
|
|
38107
37997
|
}),
|
|
38108
37998
|
catch: (cause) => new ReactDoctorError({ reason: new DeadCodeAnalysisFailed({ cause }) })
|
|
38109
37999
|
}).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)));
|
|
@@ -38973,7 +38863,7 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
38973
38863
|
}
|
|
38974
38864
|
return enabled;
|
|
38975
38865
|
};
|
|
38976
|
-
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection
|
|
38866
|
+
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection }) => {
|
|
38977
38867
|
const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
38978
38868
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
38979
38869
|
const jsPlugins = [];
|
|
@@ -38985,7 +38875,6 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
38985
38875
|
if (!rule) continue;
|
|
38986
38876
|
if (ruleSelection === "cacheable" && CROSS_FILE_RULE_IDS.has(registryEntry.id)) continue;
|
|
38987
38877
|
if (ruleSelection === "sidecar" && !CROSS_FILE_RULE_IDS.has(registryEntry.id)) continue;
|
|
38988
|
-
if (ruleSelection === "sidecar" && sidecarRuleIdFilter !== void 0 && !sidecarRuleIdFilter.has(registryEntry.id)) continue;
|
|
38989
38878
|
if (rule.scan !== void 0) continue;
|
|
38990
38879
|
if (customRulesOnly && registryEntry.originallyExternal) continue;
|
|
38991
38880
|
if (rule.framework !== "global" && !rule.requires) continue;
|
|
@@ -39030,23 +38919,23 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
39030
38919
|
}
|
|
39031
38920
|
};
|
|
39032
38921
|
};
|
|
39033
|
-
const validateDiagnostic
|
|
38922
|
+
const validateDiagnostic = decodeUnknownSync(Diagnostic);
|
|
39034
38923
|
const decodeFileDiagnostics = (raw) => {
|
|
39035
38924
|
if (!Array.isArray(raw)) return null;
|
|
39036
38925
|
try {
|
|
39037
|
-
for (const entry of raw) validateDiagnostic
|
|
38926
|
+
for (const entry of raw) validateDiagnostic(entry);
|
|
39038
38927
|
return raw;
|
|
39039
38928
|
} catch {
|
|
39040
38929
|
return null;
|
|
39041
38930
|
}
|
|
39042
38931
|
};
|
|
39043
|
-
const emptyCache
|
|
38932
|
+
const emptyCache = () => ({
|
|
39044
38933
|
version: 1,
|
|
39045
38934
|
rulesets: {}
|
|
39046
38935
|
});
|
|
39047
|
-
const loadRulesetEntries
|
|
38936
|
+
const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
|
|
39048
38937
|
const entries = /* @__PURE__ */ new Map();
|
|
39049
|
-
const persisted = failOpenReadJson(cacheFilePath, emptyCache
|
|
38938
|
+
const persisted = failOpenReadJson(cacheFilePath, emptyCache());
|
|
39050
38939
|
if (persisted.version !== 1 || !isRecord(persisted.rulesets)) return entries;
|
|
39051
38940
|
const bucket = persisted.rulesets[rulesetHash];
|
|
39052
38941
|
if (!isRecord(bucket) || !isRecord(bucket.files)) return entries;
|
|
@@ -39058,149 +38947,20 @@ const loadRulesetEntries$1 = (cacheFilePath, rulesetHash) => {
|
|
|
39058
38947
|
};
|
|
39059
38948
|
const createFileLintCache = (cacheDirectory, rulesetHash) => {
|
|
39060
38949
|
const cacheFilePath = Path.join(cacheDirectory, FILE_LINT_CACHE_FILENAME);
|
|
39061
|
-
const entries = loadRulesetEntries
|
|
38950
|
+
const entries = loadRulesetEntries(cacheFilePath, rulesetHash);
|
|
39062
38951
|
return {
|
|
39063
38952
|
lookup: (fileKey) => entries.get(fileKey) ?? null,
|
|
39064
38953
|
store: (fileKey, diagnostics) => {
|
|
39065
38954
|
entries.delete(fileKey);
|
|
39066
38955
|
entries.set(fileKey, diagnostics);
|
|
39067
38956
|
},
|
|
39068
|
-
persist: () => {
|
|
39069
|
-
const onDisk = failOpenReadJson(cacheFilePath, emptyCache$1());
|
|
39070
|
-
const rulesets = onDisk.version === 1 && isRecord(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
|
|
39071
|
-
const existingBucket = rulesets[rulesetHash];
|
|
39072
|
-
const existingFiles = isRecord(existingBucket) && isRecord(existingBucket.files) ? existingBucket.files : {};
|
|
39073
|
-
const ourFiles = {};
|
|
39074
|
-
for (const [fileKey, diagnostics] of entries) ourFiles[fileKey] = diagnostics;
|
|
39075
|
-
const cappedEntries = Object.entries({
|
|
39076
|
-
...existingFiles,
|
|
39077
|
-
...ourFiles
|
|
39078
|
-
}).slice(-FILE_LINT_CACHE_MAX_FILE_COUNT);
|
|
39079
|
-
rulesets[rulesetHash] = {
|
|
39080
|
-
updatedAtMs: Date.now(),
|
|
39081
|
-
files: Object.fromEntries(cappedEntries)
|
|
39082
|
-
};
|
|
39083
|
-
atomicWriteJson(cacheFilePath, {
|
|
39084
|
-
version: 1,
|
|
39085
|
-
rulesets: Object.fromEntries(Object.entries(rulesets).filter(([, bucket]) => isRecord(bucket) && typeof bucket.updatedAtMs === "number").sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8))
|
|
39086
|
-
});
|
|
39087
|
-
}
|
|
39088
|
-
};
|
|
39089
|
-
};
|
|
39090
|
-
const ABSENT_CONTENT_ANSWER = "absent";
|
|
39091
|
-
/**
|
|
39092
|
-
* Answers dependency probes for the sidecar lint cache — used both when
|
|
39093
|
-
* STORING an entry (turn the collector's probe paths into answers) and when
|
|
39094
|
-
* VERIFYING one (re-answer each stored probe against the current tree).
|
|
39095
|
-
* Store and verify must agree exactly, so both go through this one resolver.
|
|
39096
|
-
*
|
|
39097
|
-
* - `content` — SHA-1 of the file's bytes, `"absent"` when unreadable
|
|
39098
|
-
* (missing, a directory, permission failure). Source files reuse the
|
|
39099
|
-
* hashes the cache-partition loop already computed
|
|
39100
|
-
* (`contentHashByRelativePath`); everything else (package.json,
|
|
39101
|
-
* tsconfig, ignored files) is hashed on demand.
|
|
39102
|
-
* - `exists` — the existence class a module-resolution candidate probe
|
|
39103
|
-
* sees: `"file"`, `"dir"`, or `"none"` (missing or an exotic entry —
|
|
39104
|
-
* `statSync` follows symlinks, matching the resolvers).
|
|
39105
|
-
*
|
|
39106
|
-
* Answers are memoized per scan: the filesystem is frozen for the run, and
|
|
39107
|
-
* hot dependency paths (a barrel imported by hundreds of files, the shared
|
|
39108
|
-
* tsconfig chain) repeat across many files' probe sets.
|
|
39109
|
-
*/
|
|
39110
|
-
const createSidecarProbeAnswerResolver = (input) => {
|
|
39111
|
-
const contentAnswerByPath = /* @__PURE__ */ new Map();
|
|
39112
|
-
const existsAnswerByPath = /* @__PURE__ */ new Map();
|
|
39113
|
-
const toRelativePath = (absolutePath) => Path.relative(input.rootDirectory, absolutePath).replaceAll("\\", "/");
|
|
39114
|
-
const contentAnswer = (relativePath) => {
|
|
39115
|
-
const memoized = contentAnswerByPath.get(relativePath);
|
|
39116
|
-
if (memoized !== void 0) return memoized;
|
|
39117
|
-
const answer = input.contentHashByRelativePath.get(relativePath) ?? hashFileContents(Path.resolve(input.rootDirectory, relativePath)) ?? ABSENT_CONTENT_ANSWER;
|
|
39118
|
-
contentAnswerByPath.set(relativePath, answer);
|
|
39119
|
-
return answer;
|
|
39120
|
-
};
|
|
39121
|
-
const existsAnswer = (relativePath) => {
|
|
39122
|
-
const memoized = existsAnswerByPath.get(relativePath);
|
|
39123
|
-
if (memoized !== void 0) return memoized;
|
|
39124
|
-
let answer;
|
|
39125
|
-
try {
|
|
39126
|
-
const stat = NFS.statSync(Path.resolve(input.rootDirectory, relativePath));
|
|
39127
|
-
answer = stat.isFile() ? "file" : stat.isDirectory() ? "dir" : "none";
|
|
39128
|
-
} catch {
|
|
39129
|
-
answer = "none";
|
|
39130
|
-
}
|
|
39131
|
-
existsAnswerByPath.set(relativePath, answer);
|
|
39132
|
-
return answer;
|
|
39133
|
-
};
|
|
39134
|
-
return {
|
|
39135
|
-
answerFor: (kind, relativePath) => kind === "content" ? contentAnswer(relativePath) : existsAnswer(relativePath),
|
|
39136
|
-
toRelativePath
|
|
39137
|
-
};
|
|
39138
|
-
};
|
|
39139
|
-
const validateDiagnostic = decodeUnknownSync(Diagnostic);
|
|
39140
|
-
const isProbeKind = (value) => value === "content" || value === "exists";
|
|
39141
|
-
const decodeSidecarFileEntry = (raw) => {
|
|
39142
|
-
if (!isRecord(raw) || !Array.isArray(raw.probes) || !Array.isArray(raw.diagnostics)) return null;
|
|
39143
|
-
const probes = [];
|
|
39144
|
-
for (const rawProbe of raw.probes) {
|
|
39145
|
-
if (!Array.isArray(rawProbe) || rawProbe.length !== 3) return null;
|
|
39146
|
-
const [kind, probePath, answer] = rawProbe;
|
|
39147
|
-
if (!isProbeKind(kind) || typeof probePath !== "string" || typeof answer !== "string") return null;
|
|
39148
|
-
probes.push({
|
|
39149
|
-
kind,
|
|
39150
|
-
path: probePath,
|
|
39151
|
-
answer
|
|
39152
|
-
});
|
|
39153
|
-
}
|
|
39154
|
-
try {
|
|
39155
|
-
for (const entry of raw.diagnostics) validateDiagnostic(entry);
|
|
39156
|
-
} catch {
|
|
39157
|
-
return null;
|
|
39158
|
-
}
|
|
39159
|
-
return {
|
|
39160
|
-
probes,
|
|
39161
|
-
diagnostics: raw.diagnostics
|
|
39162
|
-
};
|
|
39163
|
-
};
|
|
39164
|
-
const encodeSidecarFileEntry = (entry) => ({
|
|
39165
|
-
probes: entry.probes.map((probe) => [
|
|
39166
|
-
probe.kind,
|
|
39167
|
-
probe.path,
|
|
39168
|
-
probe.answer
|
|
39169
|
-
]),
|
|
39170
|
-
diagnostics: entry.diagnostics
|
|
39171
|
-
});
|
|
39172
|
-
const emptyCache = () => ({
|
|
39173
|
-
version: 1,
|
|
39174
|
-
rulesets: {}
|
|
39175
|
-
});
|
|
39176
|
-
const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
|
|
39177
|
-
const entries = /* @__PURE__ */ new Map();
|
|
39178
|
-
const persisted = failOpenReadJson(cacheFilePath, emptyCache());
|
|
39179
|
-
if (persisted.version !== 1 || !isRecord(persisted.rulesets)) return entries;
|
|
39180
|
-
const bucket = persisted.rulesets[rulesetHash];
|
|
39181
|
-
if (!isRecord(bucket) || !isRecord(bucket.files)) return entries;
|
|
39182
|
-
for (const [fileKey, rawEntry] of Object.entries(bucket.files)) {
|
|
39183
|
-
const decoded = decodeSidecarFileEntry(rawEntry);
|
|
39184
|
-
if (decoded !== null) entries.set(fileKey, decoded);
|
|
39185
|
-
}
|
|
39186
|
-
return entries;
|
|
39187
|
-
};
|
|
39188
|
-
const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
|
|
39189
|
-
const cacheFilePath = Path.join(cacheDirectory, SIDECAR_LINT_CACHE_FILENAME);
|
|
39190
|
-
const entries = loadRulesetEntries(cacheFilePath, rulesetHash);
|
|
39191
|
-
return {
|
|
39192
|
-
lookup: (fileKey) => entries.get(fileKey) ?? null,
|
|
39193
|
-
store: (fileKey, entry) => {
|
|
39194
|
-
entries.delete(fileKey);
|
|
39195
|
-
entries.set(fileKey, entry);
|
|
39196
|
-
},
|
|
39197
38957
|
persist: () => {
|
|
39198
38958
|
const onDisk = failOpenReadJson(cacheFilePath, emptyCache());
|
|
39199
38959
|
const rulesets = onDisk.version === 1 && isRecord(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
|
|
39200
38960
|
const existingBucket = rulesets[rulesetHash];
|
|
39201
38961
|
const existingFiles = isRecord(existingBucket) && isRecord(existingBucket.files) ? existingBucket.files : {};
|
|
39202
38962
|
const ourFiles = {};
|
|
39203
|
-
for (const [fileKey,
|
|
38963
|
+
for (const [fileKey, diagnostics] of entries) ourFiles[fileKey] = diagnostics;
|
|
39204
38964
|
const cappedEntries = Object.entries({
|
|
39205
38965
|
...existingFiles,
|
|
39206
38966
|
...ourFiles
|
|
@@ -40215,6 +39975,13 @@ const validateRuleRegistration = () => {
|
|
|
40215
39975
|
].filter((entry) => entry !== null).join("; ");
|
|
40216
39976
|
console.warn(`[react-doctor] rule-registration drift: ${detail}`);
|
|
40217
39977
|
};
|
|
39978
|
+
const hashFileContents = (filePath) => {
|
|
39979
|
+
try {
|
|
39980
|
+
return crypto.createHash("sha1").update(NFS.readFileSync(filePath)).digest("hex");
|
|
39981
|
+
} catch {
|
|
39982
|
+
return null;
|
|
39983
|
+
}
|
|
39984
|
+
};
|
|
40218
39985
|
/**
|
|
40219
39986
|
* Balanced LPT (longest-processing-time-first) lint batch planner. Where
|
|
40220
39987
|
* `batchIncludePaths` greedily fills 100-file batches and leaves a small
|
|
@@ -40296,106 +40063,6 @@ const writeOxlintConfig = (configPath, configToWrite) => {
|
|
|
40296
40063
|
NFS.closeSync(fileHandle);
|
|
40297
40064
|
}
|
|
40298
40065
|
};
|
|
40299
|
-
/**
|
|
40300
|
-
* Attributes diagnostics back to the file that produced them by the
|
|
40301
|
-
* normalized path oxlint echoes. Returns `null` when ANY diagnostic can't be
|
|
40302
|
-
* attributed — the path forms don't align, so the caller must skip its cache
|
|
40303
|
-
* store rather than risk caching a wrong empty result for a file that
|
|
40304
|
-
* actually had diagnostics.
|
|
40305
|
-
*/
|
|
40306
|
-
const attributeDiagnosticsToFiles = (diagnostics, files) => {
|
|
40307
|
-
const fileByNormalizedPath = /* @__PURE__ */ new Map();
|
|
40308
|
-
for (const file of files) fileByNormalizedPath.set(file.replaceAll("\\", "/"), file);
|
|
40309
|
-
const diagnosticsByFile = /* @__PURE__ */ new Map();
|
|
40310
|
-
for (const diagnostic of diagnostics) {
|
|
40311
|
-
const file = fileByNormalizedPath.get(diagnostic.filePath);
|
|
40312
|
-
if (file === void 0) return null;
|
|
40313
|
-
const fileDiagnostics = diagnosticsByFile.get(file) ?? [];
|
|
40314
|
-
fileDiagnostics.push(diagnostic);
|
|
40315
|
-
diagnosticsByFile.set(file, fileDiagnostics);
|
|
40316
|
-
}
|
|
40317
|
-
return diagnosticsByFile;
|
|
40318
|
-
};
|
|
40319
|
-
/**
|
|
40320
|
-
* Collects each file's cross-file dependency probes (via the plugin's
|
|
40321
|
-
* collectors) with their current answers. Fail-open per file: an unreadable /
|
|
40322
|
-
* unparseable / collector-failing file maps to `null` (no entry stored — it
|
|
40323
|
-
* re-lints next scan). The filesystem is frozen for the scan, so this can run
|
|
40324
|
-
* CONCURRENTLY with an oxlint child-process await — the caller starts it
|
|
40325
|
-
* unawaited before a lint pass and awaits it at store time, hiding the
|
|
40326
|
-
* in-process parse + resolution cost under the subprocess wait. The loop
|
|
40327
|
-
* yields on the cooperative time budget so the child's stdout handling (and
|
|
40328
|
-
* sibling fibers) keep flowing; each per-file collection is synchronous, so
|
|
40329
|
-
* the module-level probe recorder never sees interleaved use. Never rejects.
|
|
40330
|
-
*/
|
|
40331
|
-
const collectSidecarProbesForFiles = async (input) => {
|
|
40332
|
-
const buildProbes = (file) => {
|
|
40333
|
-
const absoluteFilePath = Path.resolve(input.rootDirectory, file);
|
|
40334
|
-
let trace;
|
|
40335
|
-
try {
|
|
40336
|
-
trace = collectCrossFileDependencyProbes({
|
|
40337
|
-
absoluteFilePath,
|
|
40338
|
-
sourceText: NFS.readFileSync(absoluteFilePath, "utf8"),
|
|
40339
|
-
ruleIds: input.boundedSidecarRuleIds
|
|
40340
|
-
});
|
|
40341
|
-
} catch {
|
|
40342
|
-
return null;
|
|
40343
|
-
}
|
|
40344
|
-
if (trace === null) return null;
|
|
40345
|
-
const probes = [];
|
|
40346
|
-
for (const contentPath of trace.contentPaths) {
|
|
40347
|
-
const relativePath = input.probeAnswers.toRelativePath(contentPath);
|
|
40348
|
-
probes.push({
|
|
40349
|
-
kind: "content",
|
|
40350
|
-
path: relativePath,
|
|
40351
|
-
answer: input.probeAnswers.answerFor("content", relativePath)
|
|
40352
|
-
});
|
|
40353
|
-
}
|
|
40354
|
-
for (const existencePath of trace.existencePaths) {
|
|
40355
|
-
const relativePath = input.probeAnswers.toRelativePath(existencePath);
|
|
40356
|
-
probes.push({
|
|
40357
|
-
kind: "exists",
|
|
40358
|
-
path: relativePath,
|
|
40359
|
-
answer: input.probeAnswers.answerFor("exists", relativePath)
|
|
40360
|
-
});
|
|
40361
|
-
}
|
|
40362
|
-
return probes;
|
|
40363
|
-
};
|
|
40364
|
-
const probesByFile = /* @__PURE__ */ new Map();
|
|
40365
|
-
let collectSliceStartedAt = performance.now();
|
|
40366
|
-
for (const file of input.files) {
|
|
40367
|
-
probesByFile.set(file, buildProbes(file));
|
|
40368
|
-
if (performance.now() - collectSliceStartedAt >= 12) {
|
|
40369
|
-
await yieldToEventLoop();
|
|
40370
|
-
collectSliceStartedAt = performance.now();
|
|
40371
|
-
}
|
|
40372
|
-
}
|
|
40373
|
-
return probesByFile;
|
|
40374
|
-
};
|
|
40375
|
-
/**
|
|
40376
|
-
* Stores each freshly-linted file's probe set + cross-file diagnostics so
|
|
40377
|
-
* the next scan can replay instead of re-linting.
|
|
40378
|
-
*/
|
|
40379
|
-
const storeSidecarEntries = (input) => {
|
|
40380
|
-
let didStoreAnyEntry = false;
|
|
40381
|
-
for (const pass of input.storablePasses) {
|
|
40382
|
-
if (!pass.isTrusted) continue;
|
|
40383
|
-
const diagnosticsByFile = attributeDiagnosticsToFiles(pass.diagnostics, pass.files);
|
|
40384
|
-
if (diagnosticsByFile === null) continue;
|
|
40385
|
-
for (const file of pass.files) {
|
|
40386
|
-
const cacheKey = input.cacheKeyByFile.get(file);
|
|
40387
|
-
if (cacheKey === void 0) continue;
|
|
40388
|
-
const probes = pass.probesByFile.get(file);
|
|
40389
|
-
if (probes === void 0 || probes === null) continue;
|
|
40390
|
-
input.sidecarCache.store(cacheKey, {
|
|
40391
|
-
probes,
|
|
40392
|
-
diagnostics: diagnosticsByFile.get(file) ?? []
|
|
40393
|
-
});
|
|
40394
|
-
didStoreAnyEntry = true;
|
|
40395
|
-
}
|
|
40396
|
-
}
|
|
40397
|
-
if (didStoreAnyEntry) input.sidecarCache.persist();
|
|
40398
|
-
};
|
|
40399
40066
|
const REACT_HOOKS_JS_DROP_PREFIX = "React Compiler rules (react-hooks-js/*) skipped — eslint-plugin-react-hooks failed to load in this environment";
|
|
40400
40067
|
/**
|
|
40401
40068
|
* Detects an oxlint config-load crash caused by the optional
|
|
@@ -40437,7 +40104,7 @@ const reactHooksJsPluginDropNote = (error) => {
|
|
|
40437
40104
|
* 6. always restore disable directives + clean up the temp dir
|
|
40438
40105
|
*/
|
|
40439
40106
|
const runOxlint = async (options) => {
|
|
40440
|
-
const { rootDirectory, project, includePaths, nodeBinaryPath = process.execPath, customRulesOnly = false, respectInlineDisables = true, adoptExistingLintConfig = true, ignoredTags = /* @__PURE__ */ new Set(), userConfig, configSourceDirectory = rootDirectory, onPartialFailure, perFileLintCacheEnabled = false,
|
|
40107
|
+
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;
|
|
40441
40108
|
const serverAuthFunctionNames = Array.isArray(userConfig?.serverAuthFunctionNames) ? userConfig.serverAuthFunctionNames.filter((entry) => typeof entry === "string" && entry.length > 0) : void 0;
|
|
40442
40109
|
const severityControls = buildRuleSeverityControls(userConfig);
|
|
40443
40110
|
validateRuleRegistration();
|
|
@@ -40455,8 +40122,7 @@ const runOxlint = async (options) => {
|
|
|
40455
40122
|
severityControls,
|
|
40456
40123
|
userPlugins,
|
|
40457
40124
|
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
|
|
40458
|
-
ruleSelection: overrides.ruleSelection
|
|
40459
|
-
sidecarRuleIdFilter: overrides.sidecarRuleIdFilter
|
|
40125
|
+
ruleSelection: overrides.ruleSelection
|
|
40460
40126
|
});
|
|
40461
40127
|
const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
|
|
40462
40128
|
const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
|
|
@@ -40559,18 +40225,15 @@ const runOxlint = async (options) => {
|
|
|
40559
40225
|
});
|
|
40560
40226
|
const cache = createFileLintCache(resolveReactDoctorCacheDir(rootDirectory), rulesetHash);
|
|
40561
40227
|
const cacheKeyByFile = /* @__PURE__ */ new Map();
|
|
40562
|
-
const contentHashByRelativePath = /* @__PURE__ */ new Map();
|
|
40563
40228
|
const missFiles = [];
|
|
40564
40229
|
const replayedDiagnostics = [];
|
|
40565
40230
|
let hashSliceStartedAt = performance.now();
|
|
40566
40231
|
for (const candidateFile of candidateFiles) {
|
|
40567
|
-
const
|
|
40568
|
-
const contentHash = hashFileContents(absoluteCandidatePath);
|
|
40232
|
+
const contentHash = hashFileContents(Path.resolve(rootDirectory, candidateFile));
|
|
40569
40233
|
if (contentHash === null) {
|
|
40570
40234
|
missFiles.push(candidateFile);
|
|
40571
40235
|
continue;
|
|
40572
40236
|
}
|
|
40573
|
-
contentHashByRelativePath.set(Path.relative(rootDirectory, absoluteCandidatePath).replaceAll("\\", "/"), contentHash);
|
|
40574
40237
|
const cacheKey = `${candidateFile.replaceAll("\\", "/")}\u0000${contentHash}`;
|
|
40575
40238
|
cacheKeyByFile.set(candidateFile, cacheKey);
|
|
40576
40239
|
const cachedDiagnostics = cache.lookup(cacheKey);
|
|
@@ -40582,111 +40245,43 @@ const runOxlint = async (options) => {
|
|
|
40582
40245
|
}
|
|
40583
40246
|
}
|
|
40584
40247
|
const cacheHitFileCount = candidateFiles.length - missFiles.length;
|
|
40585
|
-
const enabledSidecarRuleIds = Object.keys(buildConfig({
|
|
40586
|
-
extendsPaths: [],
|
|
40587
|
-
ruleSelection: "sidecar"
|
|
40588
|
-
}).rules).map((ruleKey) => ruleKey.slice(ruleKey.lastIndexOf("/") + 1)).filter((ruleId) => CROSS_FILE_RULE_IDS.has(ruleId));
|
|
40589
|
-
const boundedSidecarRuleIds = enabledSidecarRuleIds.filter((ruleId) => CROSS_FILE_DEPENDENCY_COLLECTORS.has(ruleId));
|
|
40590
|
-
const boundedSidecarRuleIdSet = new Set(boundedSidecarRuleIds);
|
|
40591
|
-
const unboundedSidecarRuleIds = enabledSidecarRuleIds.filter((ruleId) => !CROSS_FILE_DEPENDENCY_COLLECTORS.has(ruleId));
|
|
40592
|
-
const useSidecarCache = sidecarLintCacheEnabled && boundedSidecarRuleIds.length > 0;
|
|
40593
|
-
const sidecarCache = useSidecarCache ? createSidecarLintCache(resolveReactDoctorCacheDir(rootDirectory), computeRulesetHash({
|
|
40594
|
-
config: buildConfig({
|
|
40595
|
-
extendsPaths: [],
|
|
40596
|
-
ruleSelection: "sidecar",
|
|
40597
|
-
sidecarRuleIdFilter: boundedSidecarRuleIdSet
|
|
40598
|
-
}),
|
|
40599
|
-
toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
|
|
40600
|
-
ignorePatterns: combinedPatterns,
|
|
40601
|
-
tsconfigContent
|
|
40602
|
-
})) : null;
|
|
40603
|
-
const probeAnswers = createSidecarProbeAnswerResolver({
|
|
40604
|
-
rootDirectory,
|
|
40605
|
-
contentHashByRelativePath
|
|
40606
|
-
});
|
|
40607
|
-
const missProbesTask = sidecarCache === null ? null : collectSidecarProbesForFiles({
|
|
40608
|
-
files: missFiles,
|
|
40609
|
-
rootDirectory,
|
|
40610
|
-
boundedSidecarRuleIds,
|
|
40611
|
-
probeAnswers
|
|
40612
|
-
});
|
|
40613
40248
|
const fullResult = await runConfigOverFiles((overrides) => buildConfig({
|
|
40614
40249
|
extendsPaths: [],
|
|
40615
40250
|
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin
|
|
40616
40251
|
}), "oxlintrc.full.json", missFiles, options.onFileProgress && ((scannedFileCount) => options.onFileProgress?.(scannedFileCount, candidateFiles.length)));
|
|
40617
40252
|
const missFileSet = new Set(missFiles);
|
|
40618
|
-
const
|
|
40619
|
-
const sidecarReplayedDiagnostics = [];
|
|
40620
|
-
const sidecarLintFiles = [];
|
|
40621
|
-
if (sidecarCache === null) sidecarLintFiles.push(...hitFiles);
|
|
40622
|
-
else {
|
|
40623
|
-
let verifySliceStartedAt = performance.now();
|
|
40624
|
-
for (const hitFile of hitFiles) {
|
|
40625
|
-
const cacheKey = cacheKeyByFile.get(hitFile);
|
|
40626
|
-
const entry = cacheKey === void 0 ? null : sidecarCache.lookup(cacheKey);
|
|
40627
|
-
if (entry !== null && entry.probes.every((probe) => probeAnswers.answerFor(probe.kind, probe.path) === probe.answer)) sidecarReplayedDiagnostics.push(...entry.diagnostics);
|
|
40628
|
-
else sidecarLintFiles.push(hitFile);
|
|
40629
|
-
if (performance.now() - verifySliceStartedAt >= 12) {
|
|
40630
|
-
await yieldToEventLoop();
|
|
40631
|
-
verifySliceStartedAt = performance.now();
|
|
40632
|
-
}
|
|
40633
|
-
}
|
|
40634
|
-
}
|
|
40635
|
-
const sidecarProbesTask = sidecarCache === null ? null : collectSidecarProbesForFiles({
|
|
40636
|
-
files: sidecarLintFiles,
|
|
40637
|
-
rootDirectory,
|
|
40638
|
-
boundedSidecarRuleIds,
|
|
40639
|
-
probeAnswers
|
|
40640
|
-
});
|
|
40641
|
-
const sidecarReplayedFileCount = hitFiles.length - sidecarLintFiles.length;
|
|
40642
|
-
if (sidecarReplayedFileCount > 0) options.onFileProgress?.(missFiles.length + sidecarReplayedFileCount, candidateFiles.length);
|
|
40643
|
-
const boundedSidecarResult = await runConfigOverFiles(() => buildConfig({
|
|
40644
|
-
extendsPaths: [],
|
|
40645
|
-
ruleSelection: "sidecar",
|
|
40646
|
-
...useSidecarCache ? { sidecarRuleIdFilter: boundedSidecarRuleIdSet } : {}
|
|
40647
|
-
}), "oxlintrc.sidecar.json", sidecarLintFiles, options.onFileProgress && ((scannedFileCount) => options.onFileProgress?.(missFiles.length + sidecarReplayedFileCount + scannedFileCount, candidateFiles.length)));
|
|
40648
|
-
const unboundedSidecarResult = useSidecarCache && unboundedSidecarRuleIds.length > 0 ? await runConfigOverFiles(() => buildConfig({
|
|
40253
|
+
const sidecarResult = await runConfigOverFiles(() => buildConfig({
|
|
40649
40254
|
extendsPaths: [],
|
|
40650
|
-
ruleSelection: "sidecar"
|
|
40651
|
-
|
|
40652
|
-
}), "oxlintrc.sidecar-unbounded.json", hitFiles, void 0) : {
|
|
40653
|
-
diagnostics: [],
|
|
40654
|
-
didDropReactHooksJsPlugin: false,
|
|
40655
|
-
hadPartialFailure: false
|
|
40656
|
-
};
|
|
40255
|
+
ruleSelection: "sidecar"
|
|
40256
|
+
}), "oxlintrc.sidecar.json", candidateFiles.filter((candidateFile) => !missFileSet.has(candidateFile)), options.onFileProgress && ((scannedFileCount) => options.onFileProgress?.(missFiles.length + scannedFileCount, candidateFiles.length)));
|
|
40657
40257
|
const freshCacheableDiagnostics = [];
|
|
40658
40258
|
for (const diagnostic of fullResult.diagnostics) if (!CROSS_FILE_RULE_IDS.has(diagnostic.rule)) freshCacheableDiagnostics.push(diagnostic);
|
|
40659
40259
|
onCacheStats?.(cacheHitFileCount, candidateFiles.length);
|
|
40660
|
-
|
|
40661
|
-
const
|
|
40662
|
-
|
|
40260
|
+
const missFileByNormalizedPath = /* @__PURE__ */ new Map();
|
|
40261
|
+
for (const missFile of missFiles) missFileByNormalizedPath.set(missFile.replaceAll("\\", "/"), missFile);
|
|
40262
|
+
const freshDiagnosticsByFile = /* @__PURE__ */ new Map();
|
|
40263
|
+
let isAttributionSound = true;
|
|
40264
|
+
for (const diagnostic of freshCacheableDiagnostics) {
|
|
40265
|
+
const missFile = missFileByNormalizedPath.get(diagnostic.filePath);
|
|
40266
|
+
if (missFile === void 0) {
|
|
40267
|
+
isAttributionSound = false;
|
|
40268
|
+
break;
|
|
40269
|
+
}
|
|
40270
|
+
const fileDiagnostics = freshDiagnosticsByFile.get(missFile) ?? [];
|
|
40271
|
+
fileDiagnostics.push(diagnostic);
|
|
40272
|
+
freshDiagnosticsByFile.set(missFile, fileDiagnostics);
|
|
40273
|
+
}
|
|
40274
|
+
if (!fullResult.didDropReactHooksJsPlugin && !fullResult.hadPartialFailure && isAttributionSound) {
|
|
40663
40275
|
for (const missFile of missFiles) {
|
|
40664
40276
|
const cacheKey = cacheKeyByFile.get(missFile);
|
|
40665
40277
|
if (cacheKey !== void 0) cache.store(cacheKey, freshDiagnosticsByFile.get(missFile) ?? []);
|
|
40666
40278
|
}
|
|
40667
40279
|
cache.persist();
|
|
40668
40280
|
}
|
|
40669
|
-
if (sidecarCache !== null && missProbesTask !== null && sidecarProbesTask !== null) storeSidecarEntries({
|
|
40670
|
-
sidecarCache,
|
|
40671
|
-
cacheKeyByFile,
|
|
40672
|
-
storablePasses: [{
|
|
40673
|
-
files: sidecarLintFiles,
|
|
40674
|
-
diagnostics: boundedSidecarResult.diagnostics,
|
|
40675
|
-
isTrusted: !boundedSidecarResult.hadPartialFailure,
|
|
40676
|
-
probesByFile: await sidecarProbesTask
|
|
40677
|
-
}, {
|
|
40678
|
-
files: missFiles,
|
|
40679
|
-
diagnostics: fullResult.diagnostics.filter((diagnostic) => boundedSidecarRuleIdSet.has(diagnostic.rule)),
|
|
40680
|
-
isTrusted: !fullResult.didDropReactHooksJsPlugin && !fullResult.hadPartialFailure,
|
|
40681
|
-
probesByFile: await missProbesTask
|
|
40682
|
-
}]
|
|
40683
|
-
});
|
|
40684
40281
|
return dedupeDiagnostics([
|
|
40685
40282
|
...replayedDiagnostics,
|
|
40686
40283
|
...fullResult.diagnostics,
|
|
40687
|
-
...
|
|
40688
|
-
...boundedSidecarResult.diagnostics,
|
|
40689
|
-
...unboundedSidecarResult.diagnostics
|
|
40284
|
+
...sidecarResult.diagnostics
|
|
40690
40285
|
]);
|
|
40691
40286
|
}
|
|
40692
40287
|
const baseArgs = makeBaseArgs(configPath);
|
|
@@ -40784,7 +40379,6 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
|
|
|
40784
40379
|
const concurrency = yield* OxlintConcurrency;
|
|
40785
40380
|
const lintBatchOrdering = yield* LintBatchOrdering;
|
|
40786
40381
|
const perFileLintCacheEnabled = yield* PerFileLintCacheEnabled;
|
|
40787
|
-
const sidecarLintCacheEnabled = yield* SidecarLintCacheEnabled;
|
|
40788
40382
|
const collectedFailures = [];
|
|
40789
40383
|
const diagnostics = yield* tryPromise({
|
|
40790
40384
|
try: (signal) => runOxlint({
|
|
@@ -40803,9 +40397,7 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
|
|
|
40803
40397
|
},
|
|
40804
40398
|
onFileProgress: input.onFileProgress,
|
|
40805
40399
|
perFileLintCacheEnabled,
|
|
40806
|
-
sidecarLintCacheEnabled,
|
|
40807
40400
|
onCacheStats: input.onCacheStats,
|
|
40808
|
-
onSidecarStats: input.onSidecarStats,
|
|
40809
40401
|
spawnTimeoutMs,
|
|
40810
40402
|
outputMaxBytes,
|
|
40811
40403
|
concurrency,
|
|
@@ -41227,20 +40819,6 @@ const writeCachedSocketBody = (cacheFile, body) => {
|
|
|
41227
40819
|
}));
|
|
41228
40820
|
} catch {}
|
|
41229
40821
|
};
|
|
41230
|
-
const pruneExpiredSocketCache = (cacheDirectory) => {
|
|
41231
|
-
try {
|
|
41232
|
-
const supplyChainCacheDirectory = Path.join(cacheDirectory, SUPPLY_CHAIN_CACHE_SUBDIR);
|
|
41233
|
-
const expiryThresholdMs = Date.now() - SUPPLY_CHAIN_CACHE_TTL_MS;
|
|
41234
|
-
for (const entryName of NFS.readdirSync(supplyChainCacheDirectory)) {
|
|
41235
|
-
const entryPath = Path.join(supplyChainCacheDirectory, entryName);
|
|
41236
|
-
try {
|
|
41237
|
-
if (NFS.statSync(entryPath).mtimeMs < expiryThresholdMs) NFS.rmSync(entryPath);
|
|
41238
|
-
} catch {
|
|
41239
|
-
continue;
|
|
41240
|
-
}
|
|
41241
|
-
}
|
|
41242
|
-
} catch {}
|
|
41243
|
-
};
|
|
41244
40822
|
const fetchSocketArtifact = (dependency, cacheDirectory) => tryPromise(async (signal) => {
|
|
41245
40823
|
const cacheFile = cacheDirectory === null ? null : supplyChainCacheFile(cacheDirectory, dependency);
|
|
41246
40824
|
if (cacheFile !== null) {
|
|
@@ -41368,7 +40946,6 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
41368
40946
|
const dependencies = collectDependenciesToScore(readPackageJson(packageJsonPath), readPackageJsonText(packageJsonPath), options.includeDevDependencies);
|
|
41369
40947
|
if (dependencies.length === 0) return [];
|
|
41370
40948
|
const cacheDirectory = isSupplyChainCacheDisabled() ? null : resolveReactDoctorCacheDir(input.rootDirectory);
|
|
41371
|
-
if (cacheDirectory !== null) pruneExpiredSocketCache(cacheDirectory);
|
|
41372
40949
|
const artifacts = yield* forEach$1(dependencies, (dependency) => fetchSocketArtifact(dependency, cacheDirectory), { concurrency: 8 }).pipe(timeoutOption(input.totalTimeoutMs ?? 9e4), map$3((maybeArtifacts) => getOrElse$1(maybeArtifacts, () => [])));
|
|
41373
40950
|
const diagnostics = [];
|
|
41374
40951
|
for (let index = 0; index < dependencies.length; index += 1) {
|
|
@@ -41598,10 +41175,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41598
41175
|
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
|
|
41599
41176
|
onCacheOutcome: (didHitCache) => {
|
|
41600
41177
|
deadCodeCacheHit = didHitCache;
|
|
41601
|
-
},
|
|
41602
|
-
onSummaryCacheStats: (stats) => {
|
|
41603
|
-
deadCodeSummaryCacheHits = stats.hits;
|
|
41604
|
-
deadCodeSummaryCacheMisses = stats.misses;
|
|
41605
41178
|
}
|
|
41606
41179
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
41607
41180
|
yield* set(deadCodeFailure, {
|
|
@@ -41630,11 +41203,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41630
41203
|
let lastReportedTotalFileCount = 0;
|
|
41631
41204
|
let lintCacheHitFileCount = null;
|
|
41632
41205
|
let lintCacheTotalFileCount = null;
|
|
41633
|
-
let lintSidecarReplayedFileCount = null;
|
|
41634
|
-
let lintSidecarTotalFileCount = null;
|
|
41635
41206
|
let deadCodeCacheHit = null;
|
|
41636
|
-
let deadCodeSummaryCacheHits = null;
|
|
41637
|
-
let deadCodeSummaryCacheMisses = null;
|
|
41638
41207
|
const baseLintStream = linterService.run({
|
|
41639
41208
|
rootDirectory: scanDirectory,
|
|
41640
41209
|
project,
|
|
@@ -41654,10 +41223,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41654
41223
|
lintCacheHitFileCount = cacheHitFileCount;
|
|
41655
41224
|
lintCacheTotalFileCount = totalConsideredFileCount;
|
|
41656
41225
|
},
|
|
41657
|
-
onSidecarStats: (sidecarReplayedFileCount, sidecarConsideredFileCount) => {
|
|
41658
|
-
lintSidecarReplayedFileCount = sidecarReplayedFileCount;
|
|
41659
|
-
lintSidecarTotalFileCount = sidecarConsideredFileCount;
|
|
41660
|
-
},
|
|
41661
41226
|
deadlineEpochMs: input.deadlineEpochMs
|
|
41662
41227
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
41663
41228
|
yield* set(lintFailure, {
|
|
@@ -41768,11 +41333,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41768
41333
|
securityScanFailed,
|
|
41769
41334
|
lintCacheHitFileCount,
|
|
41770
41335
|
lintCacheTotalFileCount,
|
|
41771
|
-
lintSidecarReplayedFileCount,
|
|
41772
|
-
lintSidecarTotalFileCount,
|
|
41773
41336
|
deadCodeCacheHit: lintFailureState.didFail ? null : deadCodeCacheHit,
|
|
41774
|
-
deadCodeSummaryCacheHits: lintFailureState.didFail ? null : deadCodeSummaryCacheHits,
|
|
41775
|
-
deadCodeSummaryCacheMisses: lintFailureState.didFail ? null : deadCodeSummaryCacheMisses,
|
|
41776
41337
|
suppressedRuleCounts: transform.summarizeSuppressions()
|
|
41777
41338
|
};
|
|
41778
41339
|
}).pipe(withSpan("runInspect", { attributes: {
|
|
@@ -42413,4 +41974,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
42413
41974
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
42414
41975
|
|
|
42415
41976
|
//# sourceMappingURL=index.js.map
|
|
42416
|
-
//# debugId=
|
|
41977
|
+
//# debugId=fb390a00-3616-5547-b572-1ce120b0e1a2
|