react-doctor 0.6.3-dev.8325343 → 0.6.3-dev.8c004f0
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 +265 -52
- package/dist/index.d.ts +8 -0
- package/dist/index.js +166 -38
- package/dist/lsp.js +166 -38
- package/package.json +2 -2
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]="e34cb22b-7751-5b1a-8422-6c5eb384dbef")}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";
|
|
@@ -36128,6 +36128,7 @@ const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
|
|
|
36128
36128
|
const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
36129
36129
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
36130
36130
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
36131
|
+
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
36131
36132
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
36132
36133
|
const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
|
|
36133
36134
|
const SUPPLY_CHAIN_CATEGORY = "Security";
|
|
@@ -38812,6 +38813,25 @@ var PerFileLintCacheEnabled = class extends Reference("react-doctor/PerFileLintC
|
|
|
38812
38813
|
if (CACHE_DISABLED_VALUES$1.has(noFileCache)) return false;
|
|
38813
38814
|
return true;
|
|
38814
38815
|
} }) {};
|
|
38816
|
+
/**
|
|
38817
|
+
* Whether the whole-project dead-code result cache
|
|
38818
|
+
* (`dead-code/dead-code-result-cache.ts`) is active. Defaults ON — a rescan
|
|
38819
|
+
* whose inputs (source tree, manifests, configs, analyzer version) are
|
|
38820
|
+
* unchanged replays the stored diagnostics instead of re-running the
|
|
38821
|
+
* analysis worker. Opt-OUT, two knobs (matching the per-file lint cache):
|
|
38822
|
+
*
|
|
38823
|
+
* - `REACT_DOCTOR_NO_CACHE` — the global off-switch.
|
|
38824
|
+
* - `REACT_DOCTOR_NO_DEAD_CODE_CACHE` — granular: bust only this cache.
|
|
38825
|
+
*
|
|
38826
|
+
* Tests override via `Layer.succeed(DeadCodeResultCacheEnabled, false)`.
|
|
38827
|
+
*/
|
|
38828
|
+
var DeadCodeResultCacheEnabled = class extends Reference("react-doctor/DeadCodeResultCacheEnabled", { defaultValue: () => {
|
|
38829
|
+
const noCache = process.env["REACT_DOCTOR_NO_CACHE"]?.toLowerCase() ?? "";
|
|
38830
|
+
const noDeadCodeCache = process.env["REACT_DOCTOR_NO_DEAD_CODE_CACHE"]?.toLowerCase() ?? "";
|
|
38831
|
+
if (CACHE_DISABLED_VALUES$1.has(noCache)) return false;
|
|
38832
|
+
if (CACHE_DISABLED_VALUES$1.has(noDeadCodeCache)) return false;
|
|
38833
|
+
return true;
|
|
38834
|
+
} }) {};
|
|
38815
38835
|
const DIAGNOSTIC_SURFACES = [
|
|
38816
38836
|
"cli",
|
|
38817
38837
|
"prComment",
|
|
@@ -40544,6 +40564,108 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
|
|
|
40544
40564
|
return [...seen].filter((pattern) => pattern.length > 0);
|
|
40545
40565
|
};
|
|
40546
40566
|
const collectDeadCodeEntryPatterns = (rootDirectory) => [...new Set(collectKnipPatterns(rootDirectory, "entry"))].filter((pattern) => pattern.length > 0);
|
|
40567
|
+
const atomicWriteJson = (filePath, value) => {
|
|
40568
|
+
try {
|
|
40569
|
+
NFS.mkdirSync(Path.dirname(filePath), { recursive: true });
|
|
40570
|
+
const temporaryPath = `${filePath}.${process.pid}.tmp`;
|
|
40571
|
+
NFS.writeFileSync(temporaryPath, JSON.stringify(value));
|
|
40572
|
+
NFS.renameSync(temporaryPath, filePath);
|
|
40573
|
+
} catch {
|
|
40574
|
+
return;
|
|
40575
|
+
}
|
|
40576
|
+
};
|
|
40577
|
+
const failOpenReadJson = (filePath, fallback) => {
|
|
40578
|
+
try {
|
|
40579
|
+
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
40580
|
+
} catch {
|
|
40581
|
+
return fallback;
|
|
40582
|
+
}
|
|
40583
|
+
};
|
|
40584
|
+
const ANALYZED_FILE_EXTENSIONS = new Set([
|
|
40585
|
+
".ts",
|
|
40586
|
+
".tsx",
|
|
40587
|
+
".js",
|
|
40588
|
+
".jsx",
|
|
40589
|
+
".mts",
|
|
40590
|
+
".mjs",
|
|
40591
|
+
".cts",
|
|
40592
|
+
".cjs",
|
|
40593
|
+
".mdx",
|
|
40594
|
+
".astro",
|
|
40595
|
+
".graphql",
|
|
40596
|
+
".gql",
|
|
40597
|
+
".css",
|
|
40598
|
+
".scss",
|
|
40599
|
+
".vue",
|
|
40600
|
+
".svelte"
|
|
40601
|
+
]);
|
|
40602
|
+
const ANALYZED_MANIFEST_NAMES = new Set([
|
|
40603
|
+
"package.json",
|
|
40604
|
+
"knip.json",
|
|
40605
|
+
"app.json",
|
|
40606
|
+
"pnpm-workspace.yaml",
|
|
40607
|
+
"lerna.json",
|
|
40608
|
+
"pnpm-lock.yaml",
|
|
40609
|
+
"package-lock.json",
|
|
40610
|
+
"yarn.lock",
|
|
40611
|
+
"bun.lock",
|
|
40612
|
+
"bun.lockb",
|
|
40613
|
+
"deno.lock",
|
|
40614
|
+
".gitignore"
|
|
40615
|
+
]);
|
|
40616
|
+
const isTsConfigLikeFile = (fileName) => (fileName.startsWith("tsconfig") || fileName.startsWith("jsconfig")) && fileName.endsWith(".json");
|
|
40617
|
+
const isFingerprintedFile = (fileName) => ANALYZED_FILE_EXTENSIONS.has(Path.extname(fileName).toLowerCase()) || ANALYZED_MANIFEST_NAMES.has(fileName) || isTsConfigLikeFile(fileName);
|
|
40618
|
+
const collectAnalyzedFileFingerprints = (rootDirectory) => {
|
|
40619
|
+
const fingerprints = [];
|
|
40620
|
+
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
|
|
40621
|
+
if (!isFingerprintedFile(name)) continue;
|
|
40622
|
+
try {
|
|
40623
|
+
const fileStat = NFS.statSync(absolutePath);
|
|
40624
|
+
const relativePath = Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/");
|
|
40625
|
+
fingerprints.push(`${relativePath}:${fileStat.mtimeMs}:${fileStat.size}`);
|
|
40626
|
+
} catch {}
|
|
40627
|
+
}
|
|
40628
|
+
return fingerprints.sort();
|
|
40629
|
+
};
|
|
40630
|
+
const bundledRequire$2 = createRequire(import.meta.url);
|
|
40631
|
+
const resolveDeslopVersion = () => {
|
|
40632
|
+
try {
|
|
40633
|
+
const packageJson = JSON.parse(NFS.readFileSync(bundledRequire$2.resolve("deslop-js/package.json"), "utf8"));
|
|
40634
|
+
return isRecord$1(packageJson) && typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
40635
|
+
} catch {
|
|
40636
|
+
return "unknown";
|
|
40637
|
+
}
|
|
40638
|
+
};
|
|
40639
|
+
const computeDeadCodeCacheKey = (input) => crypto.createHash("sha1").update(JSON.stringify({
|
|
40640
|
+
schemaVersion: 1,
|
|
40641
|
+
deslopVersion: resolveDeslopVersion(),
|
|
40642
|
+
deslopJsModuleSpecifier: input.deslopJsModuleSpecifier,
|
|
40643
|
+
entryPatterns: input.entryPatterns,
|
|
40644
|
+
ignorePatterns: input.ignorePatterns,
|
|
40645
|
+
tsConfigFile: input.tsConfigPath === void 0 ? null : Path.relative(input.rootDirectory, input.tsConfigPath).replace(/\\/g, "/"),
|
|
40646
|
+
files: collectAnalyzedFileFingerprints(input.rootDirectory)
|
|
40647
|
+
})).digest("hex");
|
|
40648
|
+
const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
|
|
40649
|
+
const decodeCachedDiagnostics = (raw) => {
|
|
40650
|
+
try {
|
|
40651
|
+
for (const entry of raw) validateDiagnostic$1(entry);
|
|
40652
|
+
return raw;
|
|
40653
|
+
} catch {
|
|
40654
|
+
return null;
|
|
40655
|
+
}
|
|
40656
|
+
};
|
|
40657
|
+
const lookupDeadCodeResultCache = (cacheDirectory, cacheKey) => {
|
|
40658
|
+
const persisted = failOpenReadJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), null);
|
|
40659
|
+
if (persisted === null || !isRecord$1(persisted) || persisted.version !== 1 || persisted.key !== cacheKey || !Array.isArray(persisted.diagnostics)) return null;
|
|
40660
|
+
return decodeCachedDiagnostics(persisted.diagnostics);
|
|
40661
|
+
};
|
|
40662
|
+
const storeDeadCodeResultCache = (cacheDirectory, cacheKey, diagnostics) => {
|
|
40663
|
+
atomicWriteJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), {
|
|
40664
|
+
version: 1,
|
|
40665
|
+
key: cacheKey,
|
|
40666
|
+
diagnostics
|
|
40667
|
+
});
|
|
40668
|
+
};
|
|
40547
40669
|
const readSystemFacts = () => ({
|
|
40548
40670
|
availableCores: os.availableParallelism(),
|
|
40549
40671
|
totalMemoryBytes: os.totalmem(),
|
|
@@ -40612,6 +40734,21 @@ const withDeadCodeWorkerSlot = async (task, abortSignal) => {
|
|
|
40612
40734
|
releaseSlot();
|
|
40613
40735
|
}
|
|
40614
40736
|
};
|
|
40737
|
+
const projectCacheSubdir = (projectDirectory) => crypto.createHash("sha256").update(projectDirectory).digest("hex").slice(0, 16);
|
|
40738
|
+
const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
40739
|
+
const cacheDirOverride = process.env["REACT_DOCTOR_CACHE_DIR"]?.trim();
|
|
40740
|
+
if (cacheDirOverride) return Path.join(cacheDirOverride, projectCacheSubdir(projectDirectory));
|
|
40741
|
+
const nodeModulesDirectory = Path.join(projectDirectory, "node_modules");
|
|
40742
|
+
if (NFS.existsSync(nodeModulesDirectory)) return Path.join(nodeModulesDirectory, ".cache", "react-doctor");
|
|
40743
|
+
return Path.join(os.tmpdir(), `react-doctor-cache-${resolveUserCacheScope()}`, projectCacheSubdir(projectDirectory));
|
|
40744
|
+
};
|
|
40745
|
+
const resolveUserCacheScope = () => {
|
|
40746
|
+
try {
|
|
40747
|
+
return String(process.getuid?.() ?? os.userInfo().username);
|
|
40748
|
+
} catch {
|
|
40749
|
+
return "shared";
|
|
40750
|
+
}
|
|
40751
|
+
};
|
|
40615
40752
|
/**
|
|
40616
40753
|
* Resolves a path to its canonical, symlink-free form, falling back to
|
|
40617
40754
|
* the input when it cannot be realpath'd (broken symlink, permission
|
|
@@ -40907,13 +41044,27 @@ const checkDeadCode = async (options) => {
|
|
|
40907
41044
|
if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
|
|
40908
41045
|
const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
|
|
40909
41046
|
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
41047
|
+
const tsConfigPath = resolveTsConfigPath(rootDirectory);
|
|
41048
|
+
const deslopJsModuleSpecifier = options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js");
|
|
41049
|
+
const cacheKey = options.cacheEnabled ? computeDeadCodeCacheKey({
|
|
41050
|
+
rootDirectory,
|
|
41051
|
+
entryPatterns,
|
|
41052
|
+
ignorePatterns,
|
|
41053
|
+
tsConfigPath,
|
|
41054
|
+
deslopJsModuleSpecifier
|
|
41055
|
+
}) : null;
|
|
41056
|
+
if (cacheKey !== null) {
|
|
41057
|
+
const cachedDiagnostics = lookupDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey);
|
|
41058
|
+
options.onCacheOutcome?.(cachedDiagnostics !== null);
|
|
41059
|
+
if (cachedDiagnostics !== null) return [...cachedDiagnostics];
|
|
41060
|
+
}
|
|
40910
41061
|
const spawnAndRun = () => {
|
|
40911
41062
|
return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
40912
41063
|
rootDirectory,
|
|
40913
41064
|
entryPatterns,
|
|
40914
|
-
tsConfigPath
|
|
41065
|
+
tsConfigPath,
|
|
40915
41066
|
ignorePatterns,
|
|
40916
|
-
deslopJsModuleSpecifier
|
|
41067
|
+
deslopJsModuleSpecifier,
|
|
40917
41068
|
parseConcurrency: options.parseConcurrency
|
|
40918
41069
|
}), options.workerTimeoutMs ?? 12e4, options.abortSignal);
|
|
40919
41070
|
};
|
|
@@ -40974,6 +41125,7 @@ const checkDeadCode = async (options) => {
|
|
|
40974
41125
|
category: DEAD_CODE_CATEGORY
|
|
40975
41126
|
});
|
|
40976
41127
|
}
|
|
41128
|
+
if (cacheKey !== null) storeDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey, diagnostics);
|
|
40977
41129
|
return diagnostics;
|
|
40978
41130
|
};
|
|
40979
41131
|
const DEAD_CODE_RULE_KEY_PREFIX = `${DEAD_CODE_PLUGIN}/`;
|
|
@@ -41076,12 +41228,15 @@ var Config = class Config extends Service()("react-doctor/Config") {
|
|
|
41076
41228
|
*/
|
|
41077
41229
|
var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
|
|
41078
41230
|
static layerNode = succeed$3(DeadCode, DeadCode.of({ run: (input) => unwrap(fn("DeadCode.run")(function* () {
|
|
41231
|
+
const cacheEnabled = yield* DeadCodeResultCacheEnabled;
|
|
41079
41232
|
return yield* tryPromise({
|
|
41080
41233
|
try: (signal) => checkDeadCode({
|
|
41081
41234
|
rootDirectory: input.rootDirectory,
|
|
41082
41235
|
parseConcurrency: input.parseConcurrency,
|
|
41083
41236
|
workerTimeoutMs: input.workerTimeoutMs,
|
|
41084
|
-
abortSignal: signal
|
|
41237
|
+
abortSignal: signal,
|
|
41238
|
+
cacheEnabled,
|
|
41239
|
+
onCacheOutcome: input.onCacheOutcome
|
|
41085
41240
|
}),
|
|
41086
41241
|
catch: (cause) => new ReactDoctorError({ reason: new DeadCodeAnalysisFailed({ cause }) })
|
|
41087
41242
|
}).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)));
|
|
@@ -42007,23 +42162,6 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
42007
42162
|
}
|
|
42008
42163
|
};
|
|
42009
42164
|
};
|
|
42010
|
-
const atomicWriteJson = (filePath, value) => {
|
|
42011
|
-
try {
|
|
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);
|
|
42016
|
-
} catch {
|
|
42017
|
-
return;
|
|
42018
|
-
}
|
|
42019
|
-
};
|
|
42020
|
-
const failOpenReadJson = (filePath, fallback) => {
|
|
42021
|
-
try {
|
|
42022
|
-
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
42023
|
-
} catch {
|
|
42024
|
-
return fallback;
|
|
42025
|
-
}
|
|
42026
|
-
};
|
|
42027
42165
|
const validateDiagnostic = decodeUnknownSync(Diagnostic);
|
|
42028
42166
|
const decodeFileDiagnostics = (raw) => {
|
|
42029
42167
|
if (!Array.isArray(raw)) return null;
|
|
@@ -42081,7 +42219,7 @@ const createFileLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
42081
42219
|
}
|
|
42082
42220
|
};
|
|
42083
42221
|
};
|
|
42084
|
-
const bundledRequire$
|
|
42222
|
+
const bundledRequire$3 = createRequire(import.meta.url);
|
|
42085
42223
|
const childNodeVersionByBinaryPath = /* @__PURE__ */ new Map();
|
|
42086
42224
|
const resolveChildNodeVersion = (nodeBinaryPath) => {
|
|
42087
42225
|
if (nodeBinaryPath === process.execPath) return process.version;
|
|
@@ -42107,7 +42245,7 @@ const TOOLCHAIN_PACKAGE_SPECIFIERS$1 = [
|
|
|
42107
42245
|
const resolveOxlintToolchainVersions = (nodeBinaryPath = process.execPath) => {
|
|
42108
42246
|
const versions = [`node=${resolveChildNodeVersion(nodeBinaryPath)}`];
|
|
42109
42247
|
for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS$1) try {
|
|
42110
|
-
const packageJson = bundledRequire$
|
|
42248
|
+
const packageJson = bundledRequire$3(specifier);
|
|
42111
42249
|
const version = typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
42112
42250
|
versions.push(`${specifier}=${version}`);
|
|
42113
42251
|
} catch {
|
|
@@ -43151,21 +43289,6 @@ const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
|
43151
43289
|
}
|
|
43152
43290
|
return batches.filter((batch) => batch.fileIndexes.length > 0).map((batch) => batch.fileIndexes.sort((leftIndex, rightIndex) => leftIndex - rightIndex).map((fileIndex) => files[fileIndex]));
|
|
43153
43291
|
};
|
|
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
|
-
};
|
|
43169
43292
|
/**
|
|
43170
43293
|
* Atomically (re)writes the generated oxlintrc.json. Used twice in
|
|
43171
43294
|
* the runner: once for the primary scan, once for the
|
|
@@ -44292,7 +44415,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
44292
44415
|
const buildCollectDeadCode = (deadCodeTimeout) => runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
44293
44416
|
rootDirectory: scanDirectory,
|
|
44294
44417
|
parseConcurrency: deadCodeParseConcurrency,
|
|
44295
|
-
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs
|
|
44418
|
+
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
|
|
44419
|
+
onCacheOutcome: (didHitCache) => {
|
|
44420
|
+
deadCodeCacheHit = didHitCache;
|
|
44421
|
+
}
|
|
44296
44422
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
44297
44423
|
yield* set(deadCodeFailure, {
|
|
44298
44424
|
didFail: true,
|
|
@@ -44320,6 +44446,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
44320
44446
|
let lastReportedTotalFileCount = 0;
|
|
44321
44447
|
let lintCacheHitFileCount = null;
|
|
44322
44448
|
let lintCacheTotalFileCount = null;
|
|
44449
|
+
let deadCodeCacheHit = null;
|
|
44323
44450
|
const baseLintStream = linterService.run({
|
|
44324
44451
|
rootDirectory: scanDirectory,
|
|
44325
44452
|
project,
|
|
@@ -44449,6 +44576,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
44449
44576
|
securityScanFailed,
|
|
44450
44577
|
lintCacheHitFileCount,
|
|
44451
44578
|
lintCacheTotalFileCount,
|
|
44579
|
+
deadCodeCacheHit: lintFailureState.didFail ? null : deadCodeCacheHit,
|
|
44452
44580
|
suppressedRuleCounts: transform.summarizeSuppressions()
|
|
44453
44581
|
};
|
|
44454
44582
|
}).pipe(withSpan("runInspect", { attributes: {
|
|
@@ -45230,6 +45358,7 @@ const isNonInteractiveEnvironment = () => NON_INTERACTIVE_ENVIRONMENT_VARIABLES.
|
|
|
45230
45358
|
const REACT_DOCTOR_CONFIG_PROJECT_NAME = "react-doctor";
|
|
45231
45359
|
const STAGED_FILES_TEMP_DIR_PREFIX = "react-doctor-staged-";
|
|
45232
45360
|
const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
|
|
45361
|
+
const RUN_GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
45233
45362
|
const GH_DEFAULT_BRANCH_PROBE_TIMEOUT_MS = 5e3;
|
|
45234
45363
|
const CI_TRUST_COMPANIES = "PayPal, Rippling, and Alibaba";
|
|
45235
45364
|
const WELCOME_EXPLANATION_HOLD_MS = 1e3;
|
|
@@ -45320,7 +45449,7 @@ const makeNoopConsole = () => ({
|
|
|
45320
45449
|
});
|
|
45321
45450
|
//#endregion
|
|
45322
45451
|
//#region src/cli/utils/version.ts
|
|
45323
|
-
const VERSION = "0.6.3-dev.
|
|
45452
|
+
const VERSION = "0.6.3-dev.8c004f0";
|
|
45324
45453
|
//#endregion
|
|
45325
45454
|
//#region src/cli/utils/json-mode.ts
|
|
45326
45455
|
let context = null;
|
|
@@ -45689,13 +45818,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
45689
45818
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
45690
45819
|
* standard `SENTRY_RELEASE` override.
|
|
45691
45820
|
*/
|
|
45692
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.6.3-dev.
|
|
45821
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.6.3-dev.8c004f0`;
|
|
45693
45822
|
/**
|
|
45694
45823
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
45695
45824
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
45696
45825
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
45697
45826
|
*/
|
|
45698
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.6.3-dev.
|
|
45827
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.6.3-dev.8c004f0") ? "development" : "production");
|
|
45699
45828
|
/**
|
|
45700
45829
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
45701
45830
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -45896,7 +46025,8 @@ const runGit = (projectRoot, args) => {
|
|
|
45896
46025
|
"ignore",
|
|
45897
46026
|
"pipe",
|
|
45898
46027
|
"ignore"
|
|
45899
|
-
]
|
|
46028
|
+
],
|
|
46029
|
+
maxBuffer: RUN_GIT_MAX_BUFFER_BYTES
|
|
45900
46030
|
}).trim();
|
|
45901
46031
|
} catch {
|
|
45902
46032
|
return null;
|
|
@@ -57922,7 +58052,8 @@ const buildOutcomeAttributes = (input) => {
|
|
|
57922
58052
|
}),
|
|
57923
58053
|
...withNamespace("deadCode", {
|
|
57924
58054
|
failed: input.didDeadCodeFail ?? null,
|
|
57925
|
-
overlapped: input.deadCodeOverlapped ?? null
|
|
58055
|
+
overlapped: input.deadCodeOverlapped ?? null,
|
|
58056
|
+
cacheHit: result.deadCodeCacheHit ?? null
|
|
57926
58057
|
}),
|
|
57927
58058
|
...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut ?? null }),
|
|
57928
58059
|
...withNamespace("securityScan", { failed: input.securityScanFailed ?? null }),
|
|
@@ -59245,13 +59376,89 @@ const stringifyStableJson = (value) => {
|
|
|
59245
59376
|
};
|
|
59246
59377
|
const hashString = (value) => crypto.createHash("sha1").update(value).digest("hex");
|
|
59247
59378
|
const readHeadSha = (projectDirectory) => runGit(projectDirectory, ["rev-parse", "HEAD"]);
|
|
59248
|
-
const
|
|
59249
|
-
const
|
|
59379
|
+
const parseWorktreeStatusRecords = (statusOutput) => {
|
|
59380
|
+
const entries = [];
|
|
59381
|
+
const tokens = statusOutput.split("\0");
|
|
59382
|
+
for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex += 1) {
|
|
59383
|
+
let record = tokens[tokenIndex];
|
|
59384
|
+
if (record.length === 0) continue;
|
|
59385
|
+
if (record.length < 3 || record[2] !== " ") {
|
|
59386
|
+
if (tokenIndex !== 0) return null;
|
|
59387
|
+
record = ` ${record}`;
|
|
59388
|
+
}
|
|
59389
|
+
if (record.length < 4 || record[2] !== " ") return null;
|
|
59390
|
+
const statusCode = record.slice(0, 2);
|
|
59391
|
+
const recordPath = record.slice(3);
|
|
59392
|
+
if (statusCode.includes("R") || statusCode.includes("C")) {
|
|
59393
|
+
tokenIndex += 1;
|
|
59394
|
+
const originPath = tokens[tokenIndex];
|
|
59395
|
+
if (originPath === void 0 || originPath.length === 0) return null;
|
|
59396
|
+
entries.push({
|
|
59397
|
+
statusCode,
|
|
59398
|
+
path: recordPath,
|
|
59399
|
+
originPath
|
|
59400
|
+
});
|
|
59401
|
+
continue;
|
|
59402
|
+
}
|
|
59403
|
+
entries.push({
|
|
59404
|
+
statusCode,
|
|
59405
|
+
path: recordPath
|
|
59406
|
+
});
|
|
59407
|
+
}
|
|
59408
|
+
return entries;
|
|
59409
|
+
};
|
|
59410
|
+
const buildDirtyPathContentFingerprint = (absolutePath, statusCode) => {
|
|
59411
|
+
let stats;
|
|
59412
|
+
try {
|
|
59413
|
+
stats = fs.lstatSync(absolutePath);
|
|
59414
|
+
} catch {
|
|
59415
|
+
return statusCode.includes("D") ? "deleted" : null;
|
|
59416
|
+
}
|
|
59417
|
+
if (stats.isSymbolicLink()) try {
|
|
59418
|
+
return `link:${fs.readlinkSync(absolutePath)}`;
|
|
59419
|
+
} catch {
|
|
59420
|
+
return null;
|
|
59421
|
+
}
|
|
59422
|
+
if (!stats.isFile()) return null;
|
|
59423
|
+
if (stats.size > 10485760) return `stat:${stats.mtimeMs}:${stats.size}`;
|
|
59424
|
+
return hashFileContents(absolutePath);
|
|
59425
|
+
};
|
|
59426
|
+
const buildWorktreeFingerprint = (projectDirectory) => {
|
|
59427
|
+
const statusOutput = runGit(projectDirectory, [
|
|
59250
59428
|
"status",
|
|
59251
59429
|
"--porcelain=v1",
|
|
59252
|
-
"
|
|
59430
|
+
"-z",
|
|
59431
|
+
"--untracked-files=all"
|
|
59253
59432
|
]);
|
|
59254
|
-
|
|
59433
|
+
if (statusOutput === null) return null;
|
|
59434
|
+
if (statusOutput.length === 0) return [];
|
|
59435
|
+
const statusEntries = parseWorktreeStatusRecords(statusOutput);
|
|
59436
|
+
if (statusEntries === null) return null;
|
|
59437
|
+
if (statusEntries.length > 300) return null;
|
|
59438
|
+
const repositoryRoot = runGit(projectDirectory, ["rev-parse", "--show-toplevel"]);
|
|
59439
|
+
if (repositoryRoot === null) return null;
|
|
59440
|
+
const dirtyEntries = [];
|
|
59441
|
+
for (const statusEntry of statusEntries) {
|
|
59442
|
+
const contentFingerprint = buildDirtyPathContentFingerprint(path.join(repositoryRoot, statusEntry.path), statusEntry.statusCode);
|
|
59443
|
+
if (contentFingerprint === null) return null;
|
|
59444
|
+
dirtyEntries.push({
|
|
59445
|
+
...statusEntry,
|
|
59446
|
+
contentFingerprint
|
|
59447
|
+
});
|
|
59448
|
+
}
|
|
59449
|
+
return dirtyEntries.sort((firstEntry, secondEntry) => {
|
|
59450
|
+
const firstSortKey = `${firstEntry.path}${firstEntry.statusCode}`;
|
|
59451
|
+
const secondSortKey = `${secondEntry.path}${secondEntry.statusCode}`;
|
|
59452
|
+
return firstSortKey < secondSortKey ? -1 : firstSortKey > secondSortKey ? 1 : 0;
|
|
59453
|
+
});
|
|
59454
|
+
};
|
|
59455
|
+
const DOTENV_FILE_NAME_PATTERN = /^\.env(\.|$)/;
|
|
59456
|
+
const resolveDotenvFingerprint = (projectDirectory) => {
|
|
59457
|
+
try {
|
|
59458
|
+
return fs.readdirSync(projectDirectory).filter((entryName) => DOTENV_FILE_NAME_PATTERN.test(entryName)).sort().map((entryName) => `${entryName}=${fileFingerprint(path.join(projectDirectory, entryName)) ?? "unreadable"}`);
|
|
59459
|
+
} catch {
|
|
59460
|
+
return [];
|
|
59461
|
+
}
|
|
59255
59462
|
};
|
|
59256
59463
|
const hasHiddenTrackedFileState = (projectDirectory) => {
|
|
59257
59464
|
const output = runGit(projectDirectory, ["ls-files", "-v"]);
|
|
@@ -59334,8 +59541,9 @@ const resolveToolchainFingerprint = (nodeBinaryPath) => {
|
|
|
59334
59541
|
};
|
|
59335
59542
|
const buildScanResultCacheKey = (input) => {
|
|
59336
59543
|
if (isScanResultCacheDisabled()) return null;
|
|
59337
|
-
if (!isWorktreeClean(input.projectDirectory)) return null;
|
|
59338
59544
|
if (hasHiddenTrackedFileState(input.projectDirectory)) return null;
|
|
59545
|
+
const worktreeFingerprint = buildWorktreeFingerprint(input.projectDirectory);
|
|
59546
|
+
if (worktreeFingerprint === null) return null;
|
|
59339
59547
|
const headSha = readHeadSha(input.projectDirectory);
|
|
59340
59548
|
if (headSha === null) return null;
|
|
59341
59549
|
if (stringifyStableJson(input.userConfig) === null) return null;
|
|
@@ -59343,6 +59551,8 @@ const buildScanResultCacheKey = (input) => {
|
|
|
59343
59551
|
schemaVersion: 3,
|
|
59344
59552
|
projectIdentity: resolveProjectIdentity(input.projectDirectory),
|
|
59345
59553
|
headSha,
|
|
59554
|
+
worktreeFingerprint,
|
|
59555
|
+
dotenvFingerprint: resolveDotenvFingerprint(input.projectDirectory),
|
|
59346
59556
|
reactDoctorVersion: input.version,
|
|
59347
59557
|
nodeVersion: process.version,
|
|
59348
59558
|
toolchainFingerprint: resolveToolchainFingerprint(input.nodeBinaryPath),
|
|
@@ -59735,7 +59945,8 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
59735
59945
|
scanMode: baselineDelta ? "baseline" : isDiffMode ? "diff" : "full",
|
|
59736
59946
|
baselineDegraded,
|
|
59737
59947
|
lintCacheHitFileCount: output.lintCacheHitFileCount,
|
|
59738
|
-
lintCacheTotalFileCount: output.lintCacheTotalFileCount
|
|
59948
|
+
lintCacheTotalFileCount: output.lintCacheTotalFileCount,
|
|
59949
|
+
deadCodeCacheHit: output.deadCodeCacheHit
|
|
59739
59950
|
});
|
|
59740
59951
|
recordOnboardingCompletion(options);
|
|
59741
59952
|
return result;
|
|
@@ -59770,6 +59981,7 @@ const renderAndRecordScan = async (input) => {
|
|
|
59770
59981
|
scanElapsedMilliseconds: input.payload.scanElapsedMilliseconds,
|
|
59771
59982
|
lintCacheHitFileCount: input.lintCacheHitFileCount ?? null,
|
|
59772
59983
|
lintCacheTotalFileCount: input.lintCacheTotalFileCount ?? null,
|
|
59984
|
+
deadCodeCacheHit: input.deadCodeCacheHit ?? null,
|
|
59773
59985
|
baselineDelta: input.payload.baselineDelta
|
|
59774
59986
|
}), input.options.silent));
|
|
59775
59987
|
const { workerCount: resolvedWorkerCount, parallel } = resolveWorkerTelemetry(input.payload.scanConcurrency, input.options.concurrency);
|
|
@@ -59808,7 +60020,7 @@ const renderAndRecordScan = async (input) => {
|
|
|
59808
60020
|
return result;
|
|
59809
60021
|
};
|
|
59810
60022
|
const finalizeAndRender = (input) => gen(function* () {
|
|
59811
|
-
const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, directory, scannedFileCount, scannedFilePaths, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount, baselineDelta } = input;
|
|
60023
|
+
const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, directory, scannedFileCount, scannedFilePaths, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount, deadCodeCacheHit, baselineDelta } = input;
|
|
59812
60024
|
const { skippedChecks, skippedCheckReasons } = buildSkippedChecks({
|
|
59813
60025
|
didLintFail,
|
|
59814
60026
|
lintFailureReason,
|
|
@@ -59832,6 +60044,7 @@ const finalizeAndRender = (input) => gen(function* () {
|
|
|
59832
60044
|
lintCacheHitFileCount,
|
|
59833
60045
|
lintCacheTotalFileCount
|
|
59834
60046
|
} : {},
|
|
60047
|
+
...deadCodeCacheHit !== null ? { deadCodeCacheHit } : {},
|
|
59835
60048
|
...baselineDelta ? { baselineDelta } : {}
|
|
59836
60049
|
});
|
|
59837
60050
|
if (options.suppressRendering) return buildResult();
|
|
@@ -63678,4 +63891,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
63678
63891
|
export {};
|
|
63679
63892
|
|
|
63680
63893
|
//# sourceMappingURL=cli.js.map
|
|
63681
|
-
//# debugId=
|
|
63894
|
+
//# debugId=e34cb22b-7751-5b1a-8422-6c5eb384dbef
|
package/dist/index.d.ts
CHANGED
|
@@ -9743,6 +9743,14 @@ interface InspectResult {
|
|
|
9743
9743
|
*/
|
|
9744
9744
|
lintCacheHitFileCount?: number | null;
|
|
9745
9745
|
lintCacheTotalFileCount?: number | null;
|
|
9746
|
+
/**
|
|
9747
|
+
* Dead-code result cache outcome: `true` when the pass replayed a cached
|
|
9748
|
+
* result (the analysis never ran), `false` on a fresh analysis. Absent when
|
|
9749
|
+
* the pass never consulted the cache — dead-code skipped, the cache
|
|
9750
|
+
* disabled, or a whole-repo cache replay where no analysis ran. The CLI
|
|
9751
|
+
* projects it onto the Sentry wide event as `deadCode.cacheHit`.
|
|
9752
|
+
*/
|
|
9753
|
+
deadCodeCacheHit?: boolean | null;
|
|
9746
9754
|
/**
|
|
9747
9755
|
* Present only for a baseline run (`InspectOptions.baseline` set). The
|
|
9748
9756
|
* `diagnostics` above are then the *introduced* findings only; this
|
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]="367db57b-e5c7-577e-9efa-ed68817a8271")}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";
|
|
@@ -32927,6 +32927,7 @@ const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
|
|
|
32927
32927
|
const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
32928
32928
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
32929
32929
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
32930
|
+
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
32930
32931
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
32931
32932
|
const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
|
|
32932
32933
|
const SUPPLY_CHAIN_CATEGORY = "Security";
|
|
@@ -35613,6 +35614,25 @@ var PerFileLintCacheEnabled = class extends Reference("react-doctor/PerFileLintC
|
|
|
35613
35614
|
if (CACHE_DISABLED_VALUES.has(noFileCache)) return false;
|
|
35614
35615
|
return true;
|
|
35615
35616
|
} }) {};
|
|
35617
|
+
/**
|
|
35618
|
+
* Whether the whole-project dead-code result cache
|
|
35619
|
+
* (`dead-code/dead-code-result-cache.ts`) is active. Defaults ON — a rescan
|
|
35620
|
+
* whose inputs (source tree, manifests, configs, analyzer version) are
|
|
35621
|
+
* unchanged replays the stored diagnostics instead of re-running the
|
|
35622
|
+
* analysis worker. Opt-OUT, two knobs (matching the per-file lint cache):
|
|
35623
|
+
*
|
|
35624
|
+
* - `REACT_DOCTOR_NO_CACHE` — the global off-switch.
|
|
35625
|
+
* - `REACT_DOCTOR_NO_DEAD_CODE_CACHE` — granular: bust only this cache.
|
|
35626
|
+
*
|
|
35627
|
+
* Tests override via `Layer.succeed(DeadCodeResultCacheEnabled, false)`.
|
|
35628
|
+
*/
|
|
35629
|
+
var DeadCodeResultCacheEnabled = class extends Reference("react-doctor/DeadCodeResultCacheEnabled", { defaultValue: () => {
|
|
35630
|
+
const noCache = process.env["REACT_DOCTOR_NO_CACHE"]?.toLowerCase() ?? "";
|
|
35631
|
+
const noDeadCodeCache = process.env["REACT_DOCTOR_NO_DEAD_CODE_CACHE"]?.toLowerCase() ?? "";
|
|
35632
|
+
if (CACHE_DISABLED_VALUES.has(noCache)) return false;
|
|
35633
|
+
if (CACHE_DISABLED_VALUES.has(noDeadCodeCache)) return false;
|
|
35634
|
+
return true;
|
|
35635
|
+
} }) {};
|
|
35616
35636
|
const DIAGNOSTIC_SURFACES = [
|
|
35617
35637
|
"cli",
|
|
35618
35638
|
"prComment",
|
|
@@ -37301,6 +37321,108 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
|
|
|
37301
37321
|
return [...seen].filter((pattern) => pattern.length > 0);
|
|
37302
37322
|
};
|
|
37303
37323
|
const collectDeadCodeEntryPatterns = (rootDirectory) => [...new Set(collectKnipPatterns(rootDirectory, "entry"))].filter((pattern) => pattern.length > 0);
|
|
37324
|
+
const atomicWriteJson = (filePath, value) => {
|
|
37325
|
+
try {
|
|
37326
|
+
NFS.mkdirSync(Path.dirname(filePath), { recursive: true });
|
|
37327
|
+
const temporaryPath = `${filePath}.${process.pid}.tmp`;
|
|
37328
|
+
NFS.writeFileSync(temporaryPath, JSON.stringify(value));
|
|
37329
|
+
NFS.renameSync(temporaryPath, filePath);
|
|
37330
|
+
} catch {
|
|
37331
|
+
return;
|
|
37332
|
+
}
|
|
37333
|
+
};
|
|
37334
|
+
const failOpenReadJson = (filePath, fallback) => {
|
|
37335
|
+
try {
|
|
37336
|
+
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
37337
|
+
} catch {
|
|
37338
|
+
return fallback;
|
|
37339
|
+
}
|
|
37340
|
+
};
|
|
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
|
+
]);
|
|
37373
|
+
const isTsConfigLikeFile = (fileName) => (fileName.startsWith("tsconfig") || fileName.startsWith("jsconfig")) && fileName.endsWith(".json");
|
|
37374
|
+
const isFingerprintedFile = (fileName) => ANALYZED_FILE_EXTENSIONS.has(Path.extname(fileName).toLowerCase()) || ANALYZED_MANIFEST_NAMES.has(fileName) || isTsConfigLikeFile(fileName);
|
|
37375
|
+
const collectAnalyzedFileFingerprints = (rootDirectory) => {
|
|
37376
|
+
const fingerprints = [];
|
|
37377
|
+
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
|
|
37378
|
+
if (!isFingerprintedFile(name)) continue;
|
|
37379
|
+
try {
|
|
37380
|
+
const fileStat = NFS.statSync(absolutePath);
|
|
37381
|
+
const relativePath = Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/");
|
|
37382
|
+
fingerprints.push(`${relativePath}:${fileStat.mtimeMs}:${fileStat.size}`);
|
|
37383
|
+
} catch {}
|
|
37384
|
+
}
|
|
37385
|
+
return fingerprints.sort();
|
|
37386
|
+
};
|
|
37387
|
+
const bundledRequire$2 = createRequire(import.meta.url);
|
|
37388
|
+
const resolveDeslopVersion = () => {
|
|
37389
|
+
try {
|
|
37390
|
+
const packageJson = JSON.parse(NFS.readFileSync(bundledRequire$2.resolve("deslop-js/package.json"), "utf8"));
|
|
37391
|
+
return isRecord(packageJson) && typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
37392
|
+
} catch {
|
|
37393
|
+
return "unknown";
|
|
37394
|
+
}
|
|
37395
|
+
};
|
|
37396
|
+
const computeDeadCodeCacheKey = (input) => crypto.createHash("sha1").update(JSON.stringify({
|
|
37397
|
+
schemaVersion: 1,
|
|
37398
|
+
deslopVersion: resolveDeslopVersion(),
|
|
37399
|
+
deslopJsModuleSpecifier: input.deslopJsModuleSpecifier,
|
|
37400
|
+
entryPatterns: input.entryPatterns,
|
|
37401
|
+
ignorePatterns: input.ignorePatterns,
|
|
37402
|
+
tsConfigFile: input.tsConfigPath === void 0 ? null : Path.relative(input.rootDirectory, input.tsConfigPath).replace(/\\/g, "/"),
|
|
37403
|
+
files: collectAnalyzedFileFingerprints(input.rootDirectory)
|
|
37404
|
+
})).digest("hex");
|
|
37405
|
+
const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
|
|
37406
|
+
const decodeCachedDiagnostics = (raw) => {
|
|
37407
|
+
try {
|
|
37408
|
+
for (const entry of raw) validateDiagnostic$1(entry);
|
|
37409
|
+
return raw;
|
|
37410
|
+
} catch {
|
|
37411
|
+
return null;
|
|
37412
|
+
}
|
|
37413
|
+
};
|
|
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);
|
|
37418
|
+
};
|
|
37419
|
+
const storeDeadCodeResultCache = (cacheDirectory, cacheKey, diagnostics) => {
|
|
37420
|
+
atomicWriteJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), {
|
|
37421
|
+
version: 1,
|
|
37422
|
+
key: cacheKey,
|
|
37423
|
+
diagnostics
|
|
37424
|
+
});
|
|
37425
|
+
};
|
|
37304
37426
|
const readSystemFacts = () => ({
|
|
37305
37427
|
availableCores: os.availableParallelism(),
|
|
37306
37428
|
totalMemoryBytes: os.totalmem(),
|
|
@@ -37369,6 +37491,21 @@ const withDeadCodeWorkerSlot = async (task, abortSignal) => {
|
|
|
37369
37491
|
releaseSlot();
|
|
37370
37492
|
}
|
|
37371
37493
|
};
|
|
37494
|
+
const projectCacheSubdir = (projectDirectory) => crypto.createHash("sha256").update(projectDirectory).digest("hex").slice(0, 16);
|
|
37495
|
+
const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
37496
|
+
const cacheDirOverride = process.env["REACT_DOCTOR_CACHE_DIR"]?.trim();
|
|
37497
|
+
if (cacheDirOverride) return Path.join(cacheDirOverride, projectCacheSubdir(projectDirectory));
|
|
37498
|
+
const nodeModulesDirectory = Path.join(projectDirectory, "node_modules");
|
|
37499
|
+
if (NFS.existsSync(nodeModulesDirectory)) return Path.join(nodeModulesDirectory, ".cache", "react-doctor");
|
|
37500
|
+
return Path.join(os.tmpdir(), `react-doctor-cache-${resolveUserCacheScope()}`, projectCacheSubdir(projectDirectory));
|
|
37501
|
+
};
|
|
37502
|
+
const resolveUserCacheScope = () => {
|
|
37503
|
+
try {
|
|
37504
|
+
return String(process.getuid?.() ?? os.userInfo().username);
|
|
37505
|
+
} catch {
|
|
37506
|
+
return "shared";
|
|
37507
|
+
}
|
|
37508
|
+
};
|
|
37372
37509
|
/**
|
|
37373
37510
|
* Resolves a path to its canonical, symlink-free form, falling back to
|
|
37374
37511
|
* the input when it cannot be realpath'd (broken symlink, permission
|
|
@@ -37664,13 +37801,27 @@ const checkDeadCode = async (options) => {
|
|
|
37664
37801
|
if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
|
|
37665
37802
|
const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
|
|
37666
37803
|
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
37804
|
+
const tsConfigPath = resolveTsConfigPath(rootDirectory);
|
|
37805
|
+
const deslopJsModuleSpecifier = options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js");
|
|
37806
|
+
const cacheKey = options.cacheEnabled ? computeDeadCodeCacheKey({
|
|
37807
|
+
rootDirectory,
|
|
37808
|
+
entryPatterns,
|
|
37809
|
+
ignorePatterns,
|
|
37810
|
+
tsConfigPath,
|
|
37811
|
+
deslopJsModuleSpecifier
|
|
37812
|
+
}) : null;
|
|
37813
|
+
if (cacheKey !== null) {
|
|
37814
|
+
const cachedDiagnostics = lookupDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey);
|
|
37815
|
+
options.onCacheOutcome?.(cachedDiagnostics !== null);
|
|
37816
|
+
if (cachedDiagnostics !== null) return [...cachedDiagnostics];
|
|
37817
|
+
}
|
|
37667
37818
|
const spawnAndRun = () => {
|
|
37668
37819
|
return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
37669
37820
|
rootDirectory,
|
|
37670
37821
|
entryPatterns,
|
|
37671
|
-
tsConfigPath
|
|
37822
|
+
tsConfigPath,
|
|
37672
37823
|
ignorePatterns,
|
|
37673
|
-
deslopJsModuleSpecifier
|
|
37824
|
+
deslopJsModuleSpecifier,
|
|
37674
37825
|
parseConcurrency: options.parseConcurrency
|
|
37675
37826
|
}), options.workerTimeoutMs ?? 12e4, options.abortSignal);
|
|
37676
37827
|
};
|
|
@@ -37731,6 +37882,7 @@ const checkDeadCode = async (options) => {
|
|
|
37731
37882
|
category: DEAD_CODE_CATEGORY
|
|
37732
37883
|
});
|
|
37733
37884
|
}
|
|
37885
|
+
if (cacheKey !== null) storeDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey, diagnostics);
|
|
37734
37886
|
return diagnostics;
|
|
37735
37887
|
};
|
|
37736
37888
|
const DEAD_CODE_RULE_KEY_PREFIX = `${DEAD_CODE_PLUGIN}/`;
|
|
@@ -37833,12 +37985,15 @@ var Config = class Config extends Service()("react-doctor/Config") {
|
|
|
37833
37985
|
*/
|
|
37834
37986
|
var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
|
|
37835
37987
|
static layerNode = succeed$3(DeadCode, DeadCode.of({ run: (input) => unwrap(fn("DeadCode.run")(function* () {
|
|
37988
|
+
const cacheEnabled = yield* DeadCodeResultCacheEnabled;
|
|
37836
37989
|
return yield* tryPromise({
|
|
37837
37990
|
try: (signal) => checkDeadCode({
|
|
37838
37991
|
rootDirectory: input.rootDirectory,
|
|
37839
37992
|
parseConcurrency: input.parseConcurrency,
|
|
37840
37993
|
workerTimeoutMs: input.workerTimeoutMs,
|
|
37841
|
-
abortSignal: signal
|
|
37994
|
+
abortSignal: signal,
|
|
37995
|
+
cacheEnabled,
|
|
37996
|
+
onCacheOutcome: input.onCacheOutcome
|
|
37842
37997
|
}),
|
|
37843
37998
|
catch: (cause) => new ReactDoctorError({ reason: new DeadCodeAnalysisFailed({ cause }) })
|
|
37844
37999
|
}).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)));
|
|
@@ -38764,23 +38919,6 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
38764
38919
|
}
|
|
38765
38920
|
};
|
|
38766
38921
|
};
|
|
38767
|
-
const atomicWriteJson = (filePath, value) => {
|
|
38768
|
-
try {
|
|
38769
|
-
NFS.mkdirSync(Path.dirname(filePath), { recursive: true });
|
|
38770
|
-
const temporaryPath = `${filePath}.${process.pid}.tmp`;
|
|
38771
|
-
NFS.writeFileSync(temporaryPath, JSON.stringify(value));
|
|
38772
|
-
NFS.renameSync(temporaryPath, filePath);
|
|
38773
|
-
} catch {
|
|
38774
|
-
return;
|
|
38775
|
-
}
|
|
38776
|
-
};
|
|
38777
|
-
const failOpenReadJson = (filePath, fallback) => {
|
|
38778
|
-
try {
|
|
38779
|
-
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
38780
|
-
} catch {
|
|
38781
|
-
return fallback;
|
|
38782
|
-
}
|
|
38783
|
-
};
|
|
38784
38922
|
const validateDiagnostic = decodeUnknownSync(Diagnostic);
|
|
38785
38923
|
const decodeFileDiagnostics = (raw) => {
|
|
38786
38924
|
if (!Array.isArray(raw)) return null;
|
|
@@ -39908,21 +40046,6 @@ const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
|
39908
40046
|
}
|
|
39909
40047
|
return batches.filter((batch) => batch.fileIndexes.length > 0).map((batch) => batch.fileIndexes.sort((leftIndex, rightIndex) => leftIndex - rightIndex).map((fileIndex) => files[fileIndex]));
|
|
39910
40048
|
};
|
|
39911
|
-
const projectCacheSubdir = (projectDirectory) => crypto.createHash("sha256").update(projectDirectory).digest("hex").slice(0, 16);
|
|
39912
|
-
const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
39913
|
-
const cacheDirOverride = process.env["REACT_DOCTOR_CACHE_DIR"]?.trim();
|
|
39914
|
-
if (cacheDirOverride) return Path.join(cacheDirOverride, projectCacheSubdir(projectDirectory));
|
|
39915
|
-
const nodeModulesDirectory = Path.join(projectDirectory, "node_modules");
|
|
39916
|
-
if (NFS.existsSync(nodeModulesDirectory)) return Path.join(nodeModulesDirectory, ".cache", "react-doctor");
|
|
39917
|
-
return Path.join(os.tmpdir(), `react-doctor-cache-${resolveUserCacheScope()}`, projectCacheSubdir(projectDirectory));
|
|
39918
|
-
};
|
|
39919
|
-
const resolveUserCacheScope = () => {
|
|
39920
|
-
try {
|
|
39921
|
-
return String(process.getuid?.() ?? os.userInfo().username);
|
|
39922
|
-
} catch {
|
|
39923
|
-
return "shared";
|
|
39924
|
-
}
|
|
39925
|
-
};
|
|
39926
40049
|
/**
|
|
39927
40050
|
* Atomically (re)writes the generated oxlintrc.json. Used twice in
|
|
39928
40051
|
* the runner: once for the primary scan, once for the
|
|
@@ -41049,7 +41172,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41049
41172
|
const buildCollectDeadCode = (deadCodeTimeout) => runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
41050
41173
|
rootDirectory: scanDirectory,
|
|
41051
41174
|
parseConcurrency: deadCodeParseConcurrency,
|
|
41052
|
-
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs
|
|
41175
|
+
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
|
|
41176
|
+
onCacheOutcome: (didHitCache) => {
|
|
41177
|
+
deadCodeCacheHit = didHitCache;
|
|
41178
|
+
}
|
|
41053
41179
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
41054
41180
|
yield* set(deadCodeFailure, {
|
|
41055
41181
|
didFail: true,
|
|
@@ -41077,6 +41203,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41077
41203
|
let lastReportedTotalFileCount = 0;
|
|
41078
41204
|
let lintCacheHitFileCount = null;
|
|
41079
41205
|
let lintCacheTotalFileCount = null;
|
|
41206
|
+
let deadCodeCacheHit = null;
|
|
41080
41207
|
const baseLintStream = linterService.run({
|
|
41081
41208
|
rootDirectory: scanDirectory,
|
|
41082
41209
|
project,
|
|
@@ -41206,6 +41333,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41206
41333
|
securityScanFailed,
|
|
41207
41334
|
lintCacheHitFileCount,
|
|
41208
41335
|
lintCacheTotalFileCount,
|
|
41336
|
+
deadCodeCacheHit: lintFailureState.didFail ? null : deadCodeCacheHit,
|
|
41209
41337
|
suppressedRuleCounts: transform.summarizeSuppressions()
|
|
41210
41338
|
};
|
|
41211
41339
|
}).pipe(withSpan("runInspect", { attributes: {
|
|
@@ -41846,4 +41974,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
41846
41974
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
41847
41975
|
|
|
41848
41976
|
//# sourceMappingURL=index.js.map
|
|
41849
|
-
//# debugId=
|
|
41977
|
+
//# debugId=367db57b-e5c7-577e-9efa-ed68817a8271
|
package/dist/lsp.js
CHANGED
|
@@ -32986,6 +32986,7 @@ const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
|
|
|
32986
32986
|
const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
32987
32987
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
32988
32988
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
32989
|
+
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
32989
32990
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
32990
32991
|
const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
|
|
32991
32992
|
const SUPPLY_CHAIN_CATEGORY = "Security";
|
|
@@ -35646,6 +35647,25 @@ var PerFileLintCacheEnabled = class extends Reference("react-doctor/PerFileLintC
|
|
|
35646
35647
|
if (CACHE_DISABLED_VALUES.has(noFileCache)) return false;
|
|
35647
35648
|
return true;
|
|
35648
35649
|
} }) {};
|
|
35650
|
+
/**
|
|
35651
|
+
* Whether the whole-project dead-code result cache
|
|
35652
|
+
* (`dead-code/dead-code-result-cache.ts`) is active. Defaults ON — a rescan
|
|
35653
|
+
* whose inputs (source tree, manifests, configs, analyzer version) are
|
|
35654
|
+
* unchanged replays the stored diagnostics instead of re-running the
|
|
35655
|
+
* analysis worker. Opt-OUT, two knobs (matching the per-file lint cache):
|
|
35656
|
+
*
|
|
35657
|
+
* - `REACT_DOCTOR_NO_CACHE` — the global off-switch.
|
|
35658
|
+
* - `REACT_DOCTOR_NO_DEAD_CODE_CACHE` — granular: bust only this cache.
|
|
35659
|
+
*
|
|
35660
|
+
* Tests override via `Layer.succeed(DeadCodeResultCacheEnabled, false)`.
|
|
35661
|
+
*/
|
|
35662
|
+
var DeadCodeResultCacheEnabled = class extends Reference("react-doctor/DeadCodeResultCacheEnabled", { defaultValue: () => {
|
|
35663
|
+
const noCache = process.env["REACT_DOCTOR_NO_CACHE"]?.toLowerCase() ?? "";
|
|
35664
|
+
const noDeadCodeCache = process.env["REACT_DOCTOR_NO_DEAD_CODE_CACHE"]?.toLowerCase() ?? "";
|
|
35665
|
+
if (CACHE_DISABLED_VALUES.has(noCache)) return false;
|
|
35666
|
+
if (CACHE_DISABLED_VALUES.has(noDeadCodeCache)) return false;
|
|
35667
|
+
return true;
|
|
35668
|
+
} }) {};
|
|
35649
35669
|
const DIAGNOSTIC_SURFACES = [
|
|
35650
35670
|
"cli",
|
|
35651
35671
|
"prComment",
|
|
@@ -37286,6 +37306,108 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
|
|
|
37286
37306
|
return [...seen].filter((pattern) => pattern.length > 0);
|
|
37287
37307
|
};
|
|
37288
37308
|
const collectDeadCodeEntryPatterns = (rootDirectory) => [...new Set(collectKnipPatterns(rootDirectory, "entry"))].filter((pattern) => pattern.length > 0);
|
|
37309
|
+
const atomicWriteJson = (filePath, value) => {
|
|
37310
|
+
try {
|
|
37311
|
+
NFS.mkdirSync(Path.dirname(filePath), { recursive: true });
|
|
37312
|
+
const temporaryPath = `${filePath}.${process.pid}.tmp`;
|
|
37313
|
+
NFS.writeFileSync(temporaryPath, JSON.stringify(value));
|
|
37314
|
+
NFS.renameSync(temporaryPath, filePath);
|
|
37315
|
+
} catch {
|
|
37316
|
+
return;
|
|
37317
|
+
}
|
|
37318
|
+
};
|
|
37319
|
+
const failOpenReadJson = (filePath, fallback) => {
|
|
37320
|
+
try {
|
|
37321
|
+
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
37322
|
+
} catch {
|
|
37323
|
+
return fallback;
|
|
37324
|
+
}
|
|
37325
|
+
};
|
|
37326
|
+
const ANALYZED_FILE_EXTENSIONS = new Set([
|
|
37327
|
+
".ts",
|
|
37328
|
+
".tsx",
|
|
37329
|
+
".js",
|
|
37330
|
+
".jsx",
|
|
37331
|
+
".mts",
|
|
37332
|
+
".mjs",
|
|
37333
|
+
".cts",
|
|
37334
|
+
".cjs",
|
|
37335
|
+
".mdx",
|
|
37336
|
+
".astro",
|
|
37337
|
+
".graphql",
|
|
37338
|
+
".gql",
|
|
37339
|
+
".css",
|
|
37340
|
+
".scss",
|
|
37341
|
+
".vue",
|
|
37342
|
+
".svelte"
|
|
37343
|
+
]);
|
|
37344
|
+
const ANALYZED_MANIFEST_NAMES = new Set([
|
|
37345
|
+
"package.json",
|
|
37346
|
+
"knip.json",
|
|
37347
|
+
"app.json",
|
|
37348
|
+
"pnpm-workspace.yaml",
|
|
37349
|
+
"lerna.json",
|
|
37350
|
+
"pnpm-lock.yaml",
|
|
37351
|
+
"package-lock.json",
|
|
37352
|
+
"yarn.lock",
|
|
37353
|
+
"bun.lock",
|
|
37354
|
+
"bun.lockb",
|
|
37355
|
+
"deno.lock",
|
|
37356
|
+
".gitignore"
|
|
37357
|
+
]);
|
|
37358
|
+
const isTsConfigLikeFile = (fileName) => (fileName.startsWith("tsconfig") || fileName.startsWith("jsconfig")) && fileName.endsWith(".json");
|
|
37359
|
+
const isFingerprintedFile = (fileName) => ANALYZED_FILE_EXTENSIONS.has(Path.extname(fileName).toLowerCase()) || ANALYZED_MANIFEST_NAMES.has(fileName) || isTsConfigLikeFile(fileName);
|
|
37360
|
+
const collectAnalyzedFileFingerprints = (rootDirectory) => {
|
|
37361
|
+
const fingerprints = [];
|
|
37362
|
+
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
|
|
37363
|
+
if (!isFingerprintedFile(name)) continue;
|
|
37364
|
+
try {
|
|
37365
|
+
const fileStat = NFS.statSync(absolutePath);
|
|
37366
|
+
const relativePath = Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/");
|
|
37367
|
+
fingerprints.push(`${relativePath}:${fileStat.mtimeMs}:${fileStat.size}`);
|
|
37368
|
+
} catch {}
|
|
37369
|
+
}
|
|
37370
|
+
return fingerprints.sort();
|
|
37371
|
+
};
|
|
37372
|
+
const bundledRequire$2 = createRequire(import.meta.url);
|
|
37373
|
+
const resolveDeslopVersion = () => {
|
|
37374
|
+
try {
|
|
37375
|
+
const packageJson = JSON.parse(NFS.readFileSync(bundledRequire$2.resolve("deslop-js/package.json"), "utf8"));
|
|
37376
|
+
return isRecord(packageJson) && typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
37377
|
+
} catch {
|
|
37378
|
+
return "unknown";
|
|
37379
|
+
}
|
|
37380
|
+
};
|
|
37381
|
+
const computeDeadCodeCacheKey = (input) => crypto.createHash("sha1").update(JSON.stringify({
|
|
37382
|
+
schemaVersion: 1,
|
|
37383
|
+
deslopVersion: resolveDeslopVersion(),
|
|
37384
|
+
deslopJsModuleSpecifier: input.deslopJsModuleSpecifier,
|
|
37385
|
+
entryPatterns: input.entryPatterns,
|
|
37386
|
+
ignorePatterns: input.ignorePatterns,
|
|
37387
|
+
tsConfigFile: input.tsConfigPath === void 0 ? null : Path.relative(input.rootDirectory, input.tsConfigPath).replace(/\\/g, "/"),
|
|
37388
|
+
files: collectAnalyzedFileFingerprints(input.rootDirectory)
|
|
37389
|
+
})).digest("hex");
|
|
37390
|
+
const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
|
|
37391
|
+
const decodeCachedDiagnostics = (raw) => {
|
|
37392
|
+
try {
|
|
37393
|
+
for (const entry of raw) validateDiagnostic$1(entry);
|
|
37394
|
+
return raw;
|
|
37395
|
+
} catch {
|
|
37396
|
+
return null;
|
|
37397
|
+
}
|
|
37398
|
+
};
|
|
37399
|
+
const lookupDeadCodeResultCache = (cacheDirectory, cacheKey) => {
|
|
37400
|
+
const persisted = failOpenReadJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), null);
|
|
37401
|
+
if (persisted === null || !isRecord(persisted) || persisted.version !== 1 || persisted.key !== cacheKey || !Array.isArray(persisted.diagnostics)) return null;
|
|
37402
|
+
return decodeCachedDiagnostics(persisted.diagnostics);
|
|
37403
|
+
};
|
|
37404
|
+
const storeDeadCodeResultCache = (cacheDirectory, cacheKey, diagnostics) => {
|
|
37405
|
+
atomicWriteJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), {
|
|
37406
|
+
version: 1,
|
|
37407
|
+
key: cacheKey,
|
|
37408
|
+
diagnostics
|
|
37409
|
+
});
|
|
37410
|
+
};
|
|
37289
37411
|
const readSystemFacts = () => ({
|
|
37290
37412
|
availableCores: os.availableParallelism(),
|
|
37291
37413
|
totalMemoryBytes: os.totalmem(),
|
|
@@ -37354,6 +37476,21 @@ const withDeadCodeWorkerSlot = async (task, abortSignal) => {
|
|
|
37354
37476
|
releaseSlot();
|
|
37355
37477
|
}
|
|
37356
37478
|
};
|
|
37479
|
+
const projectCacheSubdir = (projectDirectory) => crypto.createHash("sha256").update(projectDirectory).digest("hex").slice(0, 16);
|
|
37480
|
+
const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
37481
|
+
const cacheDirOverride = process.env["REACT_DOCTOR_CACHE_DIR"]?.trim();
|
|
37482
|
+
if (cacheDirOverride) return Path.join(cacheDirOverride, projectCacheSubdir(projectDirectory));
|
|
37483
|
+
const nodeModulesDirectory = Path.join(projectDirectory, "node_modules");
|
|
37484
|
+
if (NFS.existsSync(nodeModulesDirectory)) return Path.join(nodeModulesDirectory, ".cache", "react-doctor");
|
|
37485
|
+
return Path.join(os.tmpdir(), `react-doctor-cache-${resolveUserCacheScope()}`, projectCacheSubdir(projectDirectory));
|
|
37486
|
+
};
|
|
37487
|
+
const resolveUserCacheScope = () => {
|
|
37488
|
+
try {
|
|
37489
|
+
return String(process.getuid?.() ?? os.userInfo().username);
|
|
37490
|
+
} catch {
|
|
37491
|
+
return "shared";
|
|
37492
|
+
}
|
|
37493
|
+
};
|
|
37357
37494
|
/**
|
|
37358
37495
|
* Resolves a path to its canonical, symlink-free form, falling back to
|
|
37359
37496
|
* the input when it cannot be realpath'd (broken symlink, permission
|
|
@@ -37649,13 +37786,27 @@ const checkDeadCode = async (options) => {
|
|
|
37649
37786
|
if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
|
|
37650
37787
|
const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
|
|
37651
37788
|
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
37789
|
+
const tsConfigPath = resolveTsConfigPath(rootDirectory);
|
|
37790
|
+
const deslopJsModuleSpecifier = options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js");
|
|
37791
|
+
const cacheKey = options.cacheEnabled ? computeDeadCodeCacheKey({
|
|
37792
|
+
rootDirectory,
|
|
37793
|
+
entryPatterns,
|
|
37794
|
+
ignorePatterns,
|
|
37795
|
+
tsConfigPath,
|
|
37796
|
+
deslopJsModuleSpecifier
|
|
37797
|
+
}) : null;
|
|
37798
|
+
if (cacheKey !== null) {
|
|
37799
|
+
const cachedDiagnostics = lookupDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey);
|
|
37800
|
+
options.onCacheOutcome?.(cachedDiagnostics !== null);
|
|
37801
|
+
if (cachedDiagnostics !== null) return [...cachedDiagnostics];
|
|
37802
|
+
}
|
|
37652
37803
|
const spawnAndRun = () => {
|
|
37653
37804
|
return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
37654
37805
|
rootDirectory,
|
|
37655
37806
|
entryPatterns,
|
|
37656
|
-
tsConfigPath
|
|
37807
|
+
tsConfigPath,
|
|
37657
37808
|
ignorePatterns,
|
|
37658
|
-
deslopJsModuleSpecifier
|
|
37809
|
+
deslopJsModuleSpecifier,
|
|
37659
37810
|
parseConcurrency: options.parseConcurrency
|
|
37660
37811
|
}), options.workerTimeoutMs ?? 12e4, options.abortSignal);
|
|
37661
37812
|
};
|
|
@@ -37716,6 +37867,7 @@ const checkDeadCode = async (options) => {
|
|
|
37716
37867
|
category: DEAD_CODE_CATEGORY
|
|
37717
37868
|
});
|
|
37718
37869
|
}
|
|
37870
|
+
if (cacheKey !== null) storeDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey, diagnostics);
|
|
37719
37871
|
return diagnostics;
|
|
37720
37872
|
};
|
|
37721
37873
|
const DEAD_CODE_RULE_KEY_PREFIX = `${DEAD_CODE_PLUGIN}/`;
|
|
@@ -37818,12 +37970,15 @@ var Config = class Config extends Service()("react-doctor/Config") {
|
|
|
37818
37970
|
*/
|
|
37819
37971
|
var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
|
|
37820
37972
|
static layerNode = succeed$3(DeadCode, DeadCode.of({ run: (input) => unwrap(fn("DeadCode.run")(function* () {
|
|
37973
|
+
const cacheEnabled = yield* DeadCodeResultCacheEnabled;
|
|
37821
37974
|
return yield* tryPromise({
|
|
37822
37975
|
try: (signal) => checkDeadCode({
|
|
37823
37976
|
rootDirectory: input.rootDirectory,
|
|
37824
37977
|
parseConcurrency: input.parseConcurrency,
|
|
37825
37978
|
workerTimeoutMs: input.workerTimeoutMs,
|
|
37826
|
-
abortSignal: signal
|
|
37979
|
+
abortSignal: signal,
|
|
37980
|
+
cacheEnabled,
|
|
37981
|
+
onCacheOutcome: input.onCacheOutcome
|
|
37827
37982
|
}),
|
|
37828
37983
|
catch: (cause) => new ReactDoctorError({ reason: new DeadCodeAnalysisFailed({ cause }) })
|
|
37829
37984
|
}).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)));
|
|
@@ -38749,23 +38904,6 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
38749
38904
|
}
|
|
38750
38905
|
};
|
|
38751
38906
|
};
|
|
38752
|
-
const atomicWriteJson = (filePath, value) => {
|
|
38753
|
-
try {
|
|
38754
|
-
NFS.mkdirSync(Path.dirname(filePath), { recursive: true });
|
|
38755
|
-
const temporaryPath = `${filePath}.${process.pid}.tmp`;
|
|
38756
|
-
NFS.writeFileSync(temporaryPath, JSON.stringify(value));
|
|
38757
|
-
NFS.renameSync(temporaryPath, filePath);
|
|
38758
|
-
} catch {
|
|
38759
|
-
return;
|
|
38760
|
-
}
|
|
38761
|
-
};
|
|
38762
|
-
const failOpenReadJson = (filePath, fallback) => {
|
|
38763
|
-
try {
|
|
38764
|
-
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
38765
|
-
} catch {
|
|
38766
|
-
return fallback;
|
|
38767
|
-
}
|
|
38768
|
-
};
|
|
38769
38907
|
const validateDiagnostic = decodeUnknownSync(Diagnostic);
|
|
38770
38908
|
const decodeFileDiagnostics = (raw) => {
|
|
38771
38909
|
if (!Array.isArray(raw)) return null;
|
|
@@ -39893,21 +40031,6 @@ const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
|
39893
40031
|
}
|
|
39894
40032
|
return batches.filter((batch) => batch.fileIndexes.length > 0).map((batch) => batch.fileIndexes.sort((leftIndex, rightIndex) => leftIndex - rightIndex).map((fileIndex) => files[fileIndex]));
|
|
39895
40033
|
};
|
|
39896
|
-
const projectCacheSubdir = (projectDirectory) => crypto.createHash("sha256").update(projectDirectory).digest("hex").slice(0, 16);
|
|
39897
|
-
const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
39898
|
-
const cacheDirOverride = process.env["REACT_DOCTOR_CACHE_DIR"]?.trim();
|
|
39899
|
-
if (cacheDirOverride) return Path.join(cacheDirOverride, projectCacheSubdir(projectDirectory));
|
|
39900
|
-
const nodeModulesDirectory = Path.join(projectDirectory, "node_modules");
|
|
39901
|
-
if (NFS.existsSync(nodeModulesDirectory)) return Path.join(nodeModulesDirectory, ".cache", "react-doctor");
|
|
39902
|
-
return Path.join(os.tmpdir(), `react-doctor-cache-${resolveUserCacheScope()}`, projectCacheSubdir(projectDirectory));
|
|
39903
|
-
};
|
|
39904
|
-
const resolveUserCacheScope = () => {
|
|
39905
|
-
try {
|
|
39906
|
-
return String(process.getuid?.() ?? os.userInfo().username);
|
|
39907
|
-
} catch {
|
|
39908
|
-
return "shared";
|
|
39909
|
-
}
|
|
39910
|
-
};
|
|
39911
40034
|
/**
|
|
39912
40035
|
* Atomically (re)writes the generated oxlintrc.json. Used twice in
|
|
39913
40036
|
* the runner: once for the primary scan, once for the
|
|
@@ -41034,7 +41157,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41034
41157
|
const buildCollectDeadCode = (deadCodeTimeout) => runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
41035
41158
|
rootDirectory: scanDirectory,
|
|
41036
41159
|
parseConcurrency: deadCodeParseConcurrency,
|
|
41037
|
-
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs
|
|
41160
|
+
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
|
|
41161
|
+
onCacheOutcome: (didHitCache) => {
|
|
41162
|
+
deadCodeCacheHit = didHitCache;
|
|
41163
|
+
}
|
|
41038
41164
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
41039
41165
|
yield* set(deadCodeFailure, {
|
|
41040
41166
|
didFail: true,
|
|
@@ -41062,6 +41188,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41062
41188
|
let lastReportedTotalFileCount = 0;
|
|
41063
41189
|
let lintCacheHitFileCount = null;
|
|
41064
41190
|
let lintCacheTotalFileCount = null;
|
|
41191
|
+
let deadCodeCacheHit = null;
|
|
41065
41192
|
const baseLintStream = linterService.run({
|
|
41066
41193
|
rootDirectory: scanDirectory,
|
|
41067
41194
|
project,
|
|
@@ -41191,6 +41318,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41191
41318
|
securityScanFailed,
|
|
41192
41319
|
lintCacheHitFileCount,
|
|
41193
41320
|
lintCacheTotalFileCount,
|
|
41321
|
+
deadCodeCacheHit: lintFailureState.didFail ? null : deadCodeCacheHit,
|
|
41194
41322
|
suppressedRuleCounts: transform.summarizeSuppressions()
|
|
41195
41323
|
};
|
|
41196
41324
|
}).pipe(withSpan("runInspect", { attributes: {
|
|
@@ -43570,5 +43698,5 @@ const startLanguageServer = () => {
|
|
|
43570
43698
|
};
|
|
43571
43699
|
//#endregion
|
|
43572
43700
|
export { startLanguageServer };
|
|
43573
|
-
!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]="
|
|
43574
|
-
//# debugId=
|
|
43701
|
+
!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]="46e851cc-7b86-5c73-afb0-e09296beab28")}catch(e){}}();
|
|
43702
|
+
//# debugId=46e851cc-7b86-5c73-afb0-e09296beab28
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-doctor",
|
|
3
|
-
"version": "0.6.3-dev.
|
|
3
|
+
"version": "0.6.3-dev.8c004f0",
|
|
4
4
|
"description": "Your agent writes bad React. This catches it",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"accessibility",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"vscode-uri": "^3.1.0",
|
|
67
67
|
"yaml": "^2.9.0",
|
|
68
68
|
"deslop-js": "0.6.3",
|
|
69
|
-
"oxlint-plugin-react-doctor": "0.6.3-dev.
|
|
69
|
+
"oxlint-plugin-react-doctor": "0.6.3-dev.8c004f0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/babel__code-frame": "^7.27.0",
|