react-doctor 0.6.3-dev.1571119 → 0.6.3-dev.20d81f6
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 +237 -51
- package/dist/index.d.ts +8 -0
- package/dist/index.js +141 -38
- package/dist/lsp.js +141 -38
- package/package.json +3 -3
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]="d7bda084-a223-5aa7-8605-578888bd1b33")}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";
|
|
@@ -18,6 +18,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
18
18
|
import { createJiti } from "jiti";
|
|
19
19
|
import * as Crypto from "node:crypto";
|
|
20
20
|
import crypto, { createHash, randomUUID } from "node:crypto";
|
|
21
|
+
import { ANALYZED_MANIFEST_FILENAMES, DEFAULT_EXTENSIONS } from "deslop-js/analyzed-inputs";
|
|
21
22
|
import { gzipSync } from "node:zlib";
|
|
22
23
|
import * as Sentry from "@sentry/node";
|
|
23
24
|
import { performance as performance$1 } from "node:perf_hooks";
|
|
@@ -36128,6 +36129,8 @@ const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
|
|
|
36128
36129
|
const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
36129
36130
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
36130
36131
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
36132
|
+
const CORE_PACKAGE_VERSION = "0.6.3";
|
|
36133
|
+
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
36131
36134
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
36132
36135
|
const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
|
|
36133
36136
|
const SUPPLY_CHAIN_CATEGORY = "Security";
|
|
@@ -38812,6 +38815,25 @@ var PerFileLintCacheEnabled = class extends Reference("react-doctor/PerFileLintC
|
|
|
38812
38815
|
if (CACHE_DISABLED_VALUES$1.has(noFileCache)) return false;
|
|
38813
38816
|
return true;
|
|
38814
38817
|
} }) {};
|
|
38818
|
+
/**
|
|
38819
|
+
* Whether the whole-project dead-code result cache
|
|
38820
|
+
* (`dead-code/dead-code-result-cache.ts`) is active. Defaults ON — a rescan
|
|
38821
|
+
* whose inputs (source tree, manifests, configs, analyzer version) are
|
|
38822
|
+
* unchanged replays the stored diagnostics instead of re-running the
|
|
38823
|
+
* analysis worker. Opt-OUT, two knobs (matching the per-file lint cache):
|
|
38824
|
+
*
|
|
38825
|
+
* - `REACT_DOCTOR_NO_CACHE` — the global off-switch.
|
|
38826
|
+
* - `REACT_DOCTOR_NO_DEAD_CODE_CACHE` — granular: bust only this cache.
|
|
38827
|
+
*
|
|
38828
|
+
* Tests override via `Layer.succeed(DeadCodeResultCacheEnabled, false)`.
|
|
38829
|
+
*/
|
|
38830
|
+
var DeadCodeResultCacheEnabled = class extends Reference("react-doctor/DeadCodeResultCacheEnabled", { defaultValue: () => {
|
|
38831
|
+
const noCache = process.env["REACT_DOCTOR_NO_CACHE"]?.toLowerCase() ?? "";
|
|
38832
|
+
const noDeadCodeCache = process.env["REACT_DOCTOR_NO_DEAD_CODE_CACHE"]?.toLowerCase() ?? "";
|
|
38833
|
+
if (CACHE_DISABLED_VALUES$1.has(noCache)) return false;
|
|
38834
|
+
if (CACHE_DISABLED_VALUES$1.has(noDeadCodeCache)) return false;
|
|
38835
|
+
return true;
|
|
38836
|
+
} }) {};
|
|
38815
38837
|
const DIAGNOSTIC_SURFACES = [
|
|
38816
38838
|
"cli",
|
|
38817
38839
|
"prComment",
|
|
@@ -40544,6 +40566,80 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
|
|
|
40544
40566
|
return [...seen].filter((pattern) => pattern.length > 0);
|
|
40545
40567
|
};
|
|
40546
40568
|
const collectDeadCodeEntryPatterns = (rootDirectory) => [...new Set(collectKnipPatterns(rootDirectory, "entry"))].filter((pattern) => pattern.length > 0);
|
|
40569
|
+
const atomicWriteJson = (filePath, value) => {
|
|
40570
|
+
try {
|
|
40571
|
+
NFS.mkdirSync(Path.dirname(filePath), { recursive: true });
|
|
40572
|
+
const temporaryPath = `${filePath}.${process.pid}.tmp`;
|
|
40573
|
+
NFS.writeFileSync(temporaryPath, JSON.stringify(value));
|
|
40574
|
+
NFS.renameSync(temporaryPath, filePath);
|
|
40575
|
+
} catch {
|
|
40576
|
+
return;
|
|
40577
|
+
}
|
|
40578
|
+
};
|
|
40579
|
+
const failOpenReadJson = (filePath, fallback) => {
|
|
40580
|
+
try {
|
|
40581
|
+
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
40582
|
+
} catch {
|
|
40583
|
+
return fallback;
|
|
40584
|
+
}
|
|
40585
|
+
};
|
|
40586
|
+
const ANALYZED_FILE_EXTENSIONS = new Set(DEFAULT_EXTENSIONS);
|
|
40587
|
+
const CORE_SIDE_MANIFEST_NAMES = ["knip.json", "deno.lock"];
|
|
40588
|
+
const ANALYZED_MANIFEST_NAMES = new Set([...ANALYZED_MANIFEST_FILENAMES, ...CORE_SIDE_MANIFEST_NAMES]);
|
|
40589
|
+
const isTsConfigLikeFile = (fileName) => (fileName.startsWith("tsconfig") || fileName.startsWith("jsconfig")) && fileName.endsWith(".json");
|
|
40590
|
+
const isFingerprintedFile = (fileName) => ANALYZED_FILE_EXTENSIONS.has(Path.extname(fileName).toLowerCase()) || ANALYZED_MANIFEST_NAMES.has(fileName) || isTsConfigLikeFile(fileName);
|
|
40591
|
+
const collectAnalyzedFileFingerprints = (rootDirectory) => {
|
|
40592
|
+
const fingerprints = [];
|
|
40593
|
+
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
|
|
40594
|
+
if (!isFingerprintedFile(name)) continue;
|
|
40595
|
+
try {
|
|
40596
|
+
const fileStat = NFS.statSync(absolutePath);
|
|
40597
|
+
const relativePath = Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/");
|
|
40598
|
+
fingerprints.push(`${relativePath}:${fileStat.mtimeMs}:${fileStat.size}`);
|
|
40599
|
+
} catch {}
|
|
40600
|
+
}
|
|
40601
|
+
return fingerprints.sort();
|
|
40602
|
+
};
|
|
40603
|
+
const bundledRequire$2 = createRequire(import.meta.url);
|
|
40604
|
+
const resolveDeslopVersion = () => {
|
|
40605
|
+
try {
|
|
40606
|
+
const packageJson = JSON.parse(NFS.readFileSync(bundledRequire$2.resolve("deslop-js/package.json"), "utf8"));
|
|
40607
|
+
return isRecord$1(packageJson) && typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
40608
|
+
} catch {
|
|
40609
|
+
return "unknown";
|
|
40610
|
+
}
|
|
40611
|
+
};
|
|
40612
|
+
const computeDeadCodeCacheKey = (input) => crypto.createHash("sha1").update(JSON.stringify({
|
|
40613
|
+
schemaVersion: 1,
|
|
40614
|
+
coreVersion: input.coreVersion,
|
|
40615
|
+
deslopVersion: resolveDeslopVersion(),
|
|
40616
|
+
deslopJsModuleSpecifier: input.deslopJsModuleSpecifier,
|
|
40617
|
+
entryPatterns: input.entryPatterns,
|
|
40618
|
+
ignorePatterns: input.ignorePatterns,
|
|
40619
|
+
tsConfigFile: input.tsConfigPath === void 0 ? null : Path.relative(input.rootDirectory, input.tsConfigPath).replace(/\\/g, "/"),
|
|
40620
|
+
files: collectAnalyzedFileFingerprints(input.rootDirectory)
|
|
40621
|
+
})).digest("hex");
|
|
40622
|
+
const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
|
|
40623
|
+
const decodeCachedDiagnostics = (raw) => {
|
|
40624
|
+
try {
|
|
40625
|
+
for (const entry of raw) validateDiagnostic$1(entry);
|
|
40626
|
+
return raw;
|
|
40627
|
+
} catch {
|
|
40628
|
+
return null;
|
|
40629
|
+
}
|
|
40630
|
+
};
|
|
40631
|
+
const lookupDeadCodeResultCache = (cacheDirectory, cacheKey) => {
|
|
40632
|
+
const persisted = failOpenReadJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), null);
|
|
40633
|
+
if (persisted === null || !isRecord$1(persisted) || persisted.version !== 1 || persisted.key !== cacheKey || !Array.isArray(persisted.diagnostics)) return null;
|
|
40634
|
+
return decodeCachedDiagnostics(persisted.diagnostics);
|
|
40635
|
+
};
|
|
40636
|
+
const storeDeadCodeResultCache = (cacheDirectory, cacheKey, diagnostics) => {
|
|
40637
|
+
atomicWriteJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), {
|
|
40638
|
+
version: 1,
|
|
40639
|
+
key: cacheKey,
|
|
40640
|
+
diagnostics
|
|
40641
|
+
});
|
|
40642
|
+
};
|
|
40547
40643
|
const readSystemFacts = () => ({
|
|
40548
40644
|
availableCores: os.availableParallelism(),
|
|
40549
40645
|
totalMemoryBytes: os.totalmem(),
|
|
@@ -40612,6 +40708,21 @@ const withDeadCodeWorkerSlot = async (task, abortSignal) => {
|
|
|
40612
40708
|
releaseSlot();
|
|
40613
40709
|
}
|
|
40614
40710
|
};
|
|
40711
|
+
const projectCacheSubdir = (projectDirectory) => crypto.createHash("sha256").update(projectDirectory).digest("hex").slice(0, 16);
|
|
40712
|
+
const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
40713
|
+
const cacheDirOverride = process.env["REACT_DOCTOR_CACHE_DIR"]?.trim();
|
|
40714
|
+
if (cacheDirOverride) return Path.join(cacheDirOverride, projectCacheSubdir(projectDirectory));
|
|
40715
|
+
const nodeModulesDirectory = Path.join(projectDirectory, "node_modules");
|
|
40716
|
+
if (NFS.existsSync(nodeModulesDirectory)) return Path.join(nodeModulesDirectory, ".cache", "react-doctor");
|
|
40717
|
+
return Path.join(os.tmpdir(), `react-doctor-cache-${resolveUserCacheScope()}`, projectCacheSubdir(projectDirectory));
|
|
40718
|
+
};
|
|
40719
|
+
const resolveUserCacheScope = () => {
|
|
40720
|
+
try {
|
|
40721
|
+
return String(process.getuid?.() ?? os.userInfo().username);
|
|
40722
|
+
} catch {
|
|
40723
|
+
return "shared";
|
|
40724
|
+
}
|
|
40725
|
+
};
|
|
40615
40726
|
/**
|
|
40616
40727
|
* Resolves a path to its canonical, symlink-free form, falling back to
|
|
40617
40728
|
* the input when it cannot be realpath'd (broken symlink, permission
|
|
@@ -40907,13 +41018,28 @@ const checkDeadCode = async (options) => {
|
|
|
40907
41018
|
if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
|
|
40908
41019
|
const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
|
|
40909
41020
|
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
41021
|
+
const tsConfigPath = resolveTsConfigPath(rootDirectory);
|
|
41022
|
+
const deslopJsModuleSpecifier = options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js");
|
|
41023
|
+
const cacheKey = options.cacheEnabled ? computeDeadCodeCacheKey({
|
|
41024
|
+
rootDirectory,
|
|
41025
|
+
entryPatterns,
|
|
41026
|
+
ignorePatterns,
|
|
41027
|
+
tsConfigPath,
|
|
41028
|
+
deslopJsModuleSpecifier,
|
|
41029
|
+
coreVersion: CORE_PACKAGE_VERSION
|
|
41030
|
+
}) : null;
|
|
41031
|
+
if (cacheKey !== null) {
|
|
41032
|
+
const cachedDiagnostics = lookupDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey);
|
|
41033
|
+
options.onCacheOutcome?.(cachedDiagnostics !== null);
|
|
41034
|
+
if (cachedDiagnostics !== null) return [...cachedDiagnostics];
|
|
41035
|
+
}
|
|
40910
41036
|
const spawnAndRun = () => {
|
|
40911
41037
|
return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
40912
41038
|
rootDirectory,
|
|
40913
41039
|
entryPatterns,
|
|
40914
|
-
tsConfigPath
|
|
41040
|
+
tsConfigPath,
|
|
40915
41041
|
ignorePatterns,
|
|
40916
|
-
deslopJsModuleSpecifier
|
|
41042
|
+
deslopJsModuleSpecifier,
|
|
40917
41043
|
parseConcurrency: options.parseConcurrency
|
|
40918
41044
|
}), options.workerTimeoutMs ?? 12e4, options.abortSignal);
|
|
40919
41045
|
};
|
|
@@ -40974,6 +41100,7 @@ const checkDeadCode = async (options) => {
|
|
|
40974
41100
|
category: DEAD_CODE_CATEGORY
|
|
40975
41101
|
});
|
|
40976
41102
|
}
|
|
41103
|
+
if (cacheKey !== null) storeDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey, diagnostics);
|
|
40977
41104
|
return diagnostics;
|
|
40978
41105
|
};
|
|
40979
41106
|
const DEAD_CODE_RULE_KEY_PREFIX = `${DEAD_CODE_PLUGIN}/`;
|
|
@@ -41076,12 +41203,15 @@ var Config = class Config extends Service()("react-doctor/Config") {
|
|
|
41076
41203
|
*/
|
|
41077
41204
|
var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
|
|
41078
41205
|
static layerNode = succeed$3(DeadCode, DeadCode.of({ run: (input) => unwrap(fn("DeadCode.run")(function* () {
|
|
41206
|
+
const cacheEnabled = yield* DeadCodeResultCacheEnabled;
|
|
41079
41207
|
return yield* tryPromise({
|
|
41080
41208
|
try: (signal) => checkDeadCode({
|
|
41081
41209
|
rootDirectory: input.rootDirectory,
|
|
41082
41210
|
parseConcurrency: input.parseConcurrency,
|
|
41083
41211
|
workerTimeoutMs: input.workerTimeoutMs,
|
|
41084
|
-
abortSignal: signal
|
|
41212
|
+
abortSignal: signal,
|
|
41213
|
+
cacheEnabled,
|
|
41214
|
+
onCacheOutcome: input.onCacheOutcome
|
|
41085
41215
|
}),
|
|
41086
41216
|
catch: (cause) => new ReactDoctorError({ reason: new DeadCodeAnalysisFailed({ cause }) })
|
|
41087
41217
|
}).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)));
|
|
@@ -42007,23 +42137,6 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
42007
42137
|
}
|
|
42008
42138
|
};
|
|
42009
42139
|
};
|
|
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
42140
|
const validateDiagnostic = decodeUnknownSync(Diagnostic);
|
|
42028
42141
|
const decodeFileDiagnostics = (raw) => {
|
|
42029
42142
|
if (!Array.isArray(raw)) return null;
|
|
@@ -42081,7 +42194,7 @@ const createFileLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
42081
42194
|
}
|
|
42082
42195
|
};
|
|
42083
42196
|
};
|
|
42084
|
-
const bundledRequire$
|
|
42197
|
+
const bundledRequire$3 = createRequire(import.meta.url);
|
|
42085
42198
|
const childNodeVersionByBinaryPath = /* @__PURE__ */ new Map();
|
|
42086
42199
|
const resolveChildNodeVersion = (nodeBinaryPath) => {
|
|
42087
42200
|
if (nodeBinaryPath === process.execPath) return process.version;
|
|
@@ -42107,7 +42220,7 @@ const TOOLCHAIN_PACKAGE_SPECIFIERS$1 = [
|
|
|
42107
42220
|
const resolveOxlintToolchainVersions = (nodeBinaryPath = process.execPath) => {
|
|
42108
42221
|
const versions = [`node=${resolveChildNodeVersion(nodeBinaryPath)}`];
|
|
42109
42222
|
for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS$1) try {
|
|
42110
|
-
const packageJson = bundledRequire$
|
|
42223
|
+
const packageJson = bundledRequire$3(specifier);
|
|
42111
42224
|
const version = typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
42112
42225
|
versions.push(`${specifier}=${version}`);
|
|
42113
42226
|
} catch {
|
|
@@ -43151,21 +43264,6 @@ const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
|
43151
43264
|
}
|
|
43152
43265
|
return batches.filter((batch) => batch.fileIndexes.length > 0).map((batch) => batch.fileIndexes.sort((leftIndex, rightIndex) => leftIndex - rightIndex).map((fileIndex) => files[fileIndex]));
|
|
43153
43266
|
};
|
|
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
43267
|
/**
|
|
43170
43268
|
* Atomically (re)writes the generated oxlintrc.json. Used twice in
|
|
43171
43269
|
* the runner: once for the primary scan, once for the
|
|
@@ -44292,7 +44390,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
44292
44390
|
const buildCollectDeadCode = (deadCodeTimeout) => runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
44293
44391
|
rootDirectory: scanDirectory,
|
|
44294
44392
|
parseConcurrency: deadCodeParseConcurrency,
|
|
44295
|
-
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs
|
|
44393
|
+
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
|
|
44394
|
+
onCacheOutcome: (didHitCache) => {
|
|
44395
|
+
deadCodeCacheHit = didHitCache;
|
|
44396
|
+
}
|
|
44296
44397
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
44297
44398
|
yield* set(deadCodeFailure, {
|
|
44298
44399
|
didFail: true,
|
|
@@ -44320,6 +44421,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
44320
44421
|
let lastReportedTotalFileCount = 0;
|
|
44321
44422
|
let lintCacheHitFileCount = null;
|
|
44322
44423
|
let lintCacheTotalFileCount = null;
|
|
44424
|
+
let deadCodeCacheHit = null;
|
|
44323
44425
|
const baseLintStream = linterService.run({
|
|
44324
44426
|
rootDirectory: scanDirectory,
|
|
44325
44427
|
project,
|
|
@@ -44449,6 +44551,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
44449
44551
|
securityScanFailed,
|
|
44450
44552
|
lintCacheHitFileCount,
|
|
44451
44553
|
lintCacheTotalFileCount,
|
|
44554
|
+
deadCodeCacheHit: lintFailureState.didFail ? null : deadCodeCacheHit,
|
|
44452
44555
|
suppressedRuleCounts: transform.summarizeSuppressions()
|
|
44453
44556
|
};
|
|
44454
44557
|
}).pipe(withSpan("runInspect", { attributes: {
|
|
@@ -45321,7 +45424,7 @@ const makeNoopConsole = () => ({
|
|
|
45321
45424
|
});
|
|
45322
45425
|
//#endregion
|
|
45323
45426
|
//#region src/cli/utils/version.ts
|
|
45324
|
-
const VERSION = "0.6.3-dev.
|
|
45427
|
+
const VERSION = "0.6.3-dev.20d81f6";
|
|
45325
45428
|
//#endregion
|
|
45326
45429
|
//#region src/cli/utils/json-mode.ts
|
|
45327
45430
|
let context = null;
|
|
@@ -45690,13 +45793,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
45690
45793
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
45691
45794
|
* standard `SENTRY_RELEASE` override.
|
|
45692
45795
|
*/
|
|
45693
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.6.3-dev.
|
|
45796
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.6.3-dev.20d81f6`;
|
|
45694
45797
|
/**
|
|
45695
45798
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
45696
45799
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
45697
45800
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
45698
45801
|
*/
|
|
45699
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.6.3-dev.
|
|
45802
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.6.3-dev.20d81f6") ? "development" : "production");
|
|
45700
45803
|
/**
|
|
45701
45804
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
45702
45805
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -57924,7 +58027,8 @@ const buildOutcomeAttributes = (input) => {
|
|
|
57924
58027
|
}),
|
|
57925
58028
|
...withNamespace("deadCode", {
|
|
57926
58029
|
failed: input.didDeadCodeFail ?? null,
|
|
57927
|
-
overlapped: input.deadCodeOverlapped ?? null
|
|
58030
|
+
overlapped: input.deadCodeOverlapped ?? null,
|
|
58031
|
+
cacheHit: result.deadCodeCacheHit ?? null
|
|
57928
58032
|
}),
|
|
57929
58033
|
...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut ?? null }),
|
|
57930
58034
|
...withNamespace("securityScan", { failed: input.securityScanFailed ?? null }),
|
|
@@ -59247,13 +59351,89 @@ const stringifyStableJson = (value) => {
|
|
|
59247
59351
|
};
|
|
59248
59352
|
const hashString = (value) => crypto.createHash("sha1").update(value).digest("hex");
|
|
59249
59353
|
const readHeadSha = (projectDirectory) => runGit(projectDirectory, ["rev-parse", "HEAD"]);
|
|
59250
|
-
const
|
|
59251
|
-
const
|
|
59354
|
+
const parseWorktreeStatusRecords = (statusOutput) => {
|
|
59355
|
+
const entries = [];
|
|
59356
|
+
const tokens = statusOutput.split("\0");
|
|
59357
|
+
for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex += 1) {
|
|
59358
|
+
let record = tokens[tokenIndex];
|
|
59359
|
+
if (record.length === 0) continue;
|
|
59360
|
+
if (record.length < 3 || record[2] !== " ") {
|
|
59361
|
+
if (tokenIndex !== 0) return null;
|
|
59362
|
+
record = ` ${record}`;
|
|
59363
|
+
}
|
|
59364
|
+
if (record.length < 4 || record[2] !== " ") return null;
|
|
59365
|
+
const statusCode = record.slice(0, 2);
|
|
59366
|
+
const recordPath = record.slice(3);
|
|
59367
|
+
if (statusCode.includes("R") || statusCode.includes("C")) {
|
|
59368
|
+
tokenIndex += 1;
|
|
59369
|
+
const originPath = tokens[tokenIndex];
|
|
59370
|
+
if (originPath === void 0 || originPath.length === 0) return null;
|
|
59371
|
+
entries.push({
|
|
59372
|
+
statusCode,
|
|
59373
|
+
path: recordPath,
|
|
59374
|
+
originPath
|
|
59375
|
+
});
|
|
59376
|
+
continue;
|
|
59377
|
+
}
|
|
59378
|
+
entries.push({
|
|
59379
|
+
statusCode,
|
|
59380
|
+
path: recordPath
|
|
59381
|
+
});
|
|
59382
|
+
}
|
|
59383
|
+
return entries;
|
|
59384
|
+
};
|
|
59385
|
+
const buildDirtyPathContentFingerprint = (absolutePath, statusCode) => {
|
|
59386
|
+
let stats;
|
|
59387
|
+
try {
|
|
59388
|
+
stats = fs.lstatSync(absolutePath);
|
|
59389
|
+
} catch {
|
|
59390
|
+
return statusCode.includes("D") ? "deleted" : null;
|
|
59391
|
+
}
|
|
59392
|
+
if (stats.isSymbolicLink()) try {
|
|
59393
|
+
return `link:${fs.readlinkSync(absolutePath)}`;
|
|
59394
|
+
} catch {
|
|
59395
|
+
return null;
|
|
59396
|
+
}
|
|
59397
|
+
if (!stats.isFile()) return null;
|
|
59398
|
+
if (stats.size > 10485760) return `stat:${stats.mtimeMs}:${stats.size}`;
|
|
59399
|
+
return hashFileContents(absolutePath);
|
|
59400
|
+
};
|
|
59401
|
+
const buildWorktreeFingerprint = (projectDirectory) => {
|
|
59402
|
+
const statusOutput = runGit(projectDirectory, [
|
|
59252
59403
|
"status",
|
|
59253
59404
|
"--porcelain=v1",
|
|
59254
|
-
"
|
|
59405
|
+
"-z",
|
|
59406
|
+
"--untracked-files=all"
|
|
59255
59407
|
]);
|
|
59256
|
-
|
|
59408
|
+
if (statusOutput === null) return null;
|
|
59409
|
+
if (statusOutput.length === 0) return [];
|
|
59410
|
+
const statusEntries = parseWorktreeStatusRecords(statusOutput);
|
|
59411
|
+
if (statusEntries === null) return null;
|
|
59412
|
+
if (statusEntries.length > 300) return null;
|
|
59413
|
+
const repositoryRoot = runGit(projectDirectory, ["rev-parse", "--show-toplevel"]);
|
|
59414
|
+
if (repositoryRoot === null) return null;
|
|
59415
|
+
const dirtyEntries = [];
|
|
59416
|
+
for (const statusEntry of statusEntries) {
|
|
59417
|
+
const contentFingerprint = buildDirtyPathContentFingerprint(path.join(repositoryRoot, statusEntry.path), statusEntry.statusCode);
|
|
59418
|
+
if (contentFingerprint === null) return null;
|
|
59419
|
+
dirtyEntries.push({
|
|
59420
|
+
...statusEntry,
|
|
59421
|
+
contentFingerprint
|
|
59422
|
+
});
|
|
59423
|
+
}
|
|
59424
|
+
return dirtyEntries.sort((firstEntry, secondEntry) => {
|
|
59425
|
+
const firstSortKey = `${firstEntry.path}${firstEntry.statusCode}`;
|
|
59426
|
+
const secondSortKey = `${secondEntry.path}${secondEntry.statusCode}`;
|
|
59427
|
+
return firstSortKey < secondSortKey ? -1 : firstSortKey > secondSortKey ? 1 : 0;
|
|
59428
|
+
});
|
|
59429
|
+
};
|
|
59430
|
+
const DOTENV_FILE_NAME_PATTERN = /^\.env(\.|$)/;
|
|
59431
|
+
const resolveDotenvFingerprint = (projectDirectory) => {
|
|
59432
|
+
try {
|
|
59433
|
+
return fs.readdirSync(projectDirectory).filter((entryName) => DOTENV_FILE_NAME_PATTERN.test(entryName)).sort().map((entryName) => `${entryName}=${fileFingerprint(path.join(projectDirectory, entryName)) ?? "unreadable"}`);
|
|
59434
|
+
} catch {
|
|
59435
|
+
return [];
|
|
59436
|
+
}
|
|
59257
59437
|
};
|
|
59258
59438
|
const hasHiddenTrackedFileState = (projectDirectory) => {
|
|
59259
59439
|
const output = runGit(projectDirectory, ["ls-files", "-v"]);
|
|
@@ -59336,8 +59516,9 @@ const resolveToolchainFingerprint = (nodeBinaryPath) => {
|
|
|
59336
59516
|
};
|
|
59337
59517
|
const buildScanResultCacheKey = (input) => {
|
|
59338
59518
|
if (isScanResultCacheDisabled()) return null;
|
|
59339
|
-
if (!isWorktreeClean(input.projectDirectory)) return null;
|
|
59340
59519
|
if (hasHiddenTrackedFileState(input.projectDirectory)) return null;
|
|
59520
|
+
const worktreeFingerprint = buildWorktreeFingerprint(input.projectDirectory);
|
|
59521
|
+
if (worktreeFingerprint === null) return null;
|
|
59341
59522
|
const headSha = readHeadSha(input.projectDirectory);
|
|
59342
59523
|
if (headSha === null) return null;
|
|
59343
59524
|
if (stringifyStableJson(input.userConfig) === null) return null;
|
|
@@ -59345,6 +59526,8 @@ const buildScanResultCacheKey = (input) => {
|
|
|
59345
59526
|
schemaVersion: 3,
|
|
59346
59527
|
projectIdentity: resolveProjectIdentity(input.projectDirectory),
|
|
59347
59528
|
headSha,
|
|
59529
|
+
worktreeFingerprint,
|
|
59530
|
+
dotenvFingerprint: resolveDotenvFingerprint(input.projectDirectory),
|
|
59348
59531
|
reactDoctorVersion: input.version,
|
|
59349
59532
|
nodeVersion: process.version,
|
|
59350
59533
|
toolchainFingerprint: resolveToolchainFingerprint(input.nodeBinaryPath),
|
|
@@ -59737,7 +59920,8 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
59737
59920
|
scanMode: baselineDelta ? "baseline" : isDiffMode ? "diff" : "full",
|
|
59738
59921
|
baselineDegraded,
|
|
59739
59922
|
lintCacheHitFileCount: output.lintCacheHitFileCount,
|
|
59740
|
-
lintCacheTotalFileCount: output.lintCacheTotalFileCount
|
|
59923
|
+
lintCacheTotalFileCount: output.lintCacheTotalFileCount,
|
|
59924
|
+
deadCodeCacheHit: output.deadCodeCacheHit
|
|
59741
59925
|
});
|
|
59742
59926
|
recordOnboardingCompletion(options);
|
|
59743
59927
|
return result;
|
|
@@ -59772,6 +59956,7 @@ const renderAndRecordScan = async (input) => {
|
|
|
59772
59956
|
scanElapsedMilliseconds: input.payload.scanElapsedMilliseconds,
|
|
59773
59957
|
lintCacheHitFileCount: input.lintCacheHitFileCount ?? null,
|
|
59774
59958
|
lintCacheTotalFileCount: input.lintCacheTotalFileCount ?? null,
|
|
59959
|
+
deadCodeCacheHit: input.deadCodeCacheHit ?? null,
|
|
59775
59960
|
baselineDelta: input.payload.baselineDelta
|
|
59776
59961
|
}), input.options.silent));
|
|
59777
59962
|
const { workerCount: resolvedWorkerCount, parallel } = resolveWorkerTelemetry(input.payload.scanConcurrency, input.options.concurrency);
|
|
@@ -59810,7 +59995,7 @@ const renderAndRecordScan = async (input) => {
|
|
|
59810
59995
|
return result;
|
|
59811
59996
|
};
|
|
59812
59997
|
const finalizeAndRender = (input) => gen(function* () {
|
|
59813
|
-
const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, directory, scannedFileCount, scannedFilePaths, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount, baselineDelta } = input;
|
|
59998
|
+
const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, directory, scannedFileCount, scannedFilePaths, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount, deadCodeCacheHit, baselineDelta } = input;
|
|
59814
59999
|
const { skippedChecks, skippedCheckReasons } = buildSkippedChecks({
|
|
59815
60000
|
didLintFail,
|
|
59816
60001
|
lintFailureReason,
|
|
@@ -59834,6 +60019,7 @@ const finalizeAndRender = (input) => gen(function* () {
|
|
|
59834
60019
|
lintCacheHitFileCount,
|
|
59835
60020
|
lintCacheTotalFileCount
|
|
59836
60021
|
} : {},
|
|
60022
|
+
...deadCodeCacheHit !== null ? { deadCodeCacheHit } : {},
|
|
59837
60023
|
...baselineDelta ? { baselineDelta } : {}
|
|
59838
60024
|
});
|
|
59839
60025
|
if (options.suppressRendering) return buildResult();
|
|
@@ -63680,4 +63866,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
63680
63866
|
export {};
|
|
63681
63867
|
|
|
63682
63868
|
//# sourceMappingURL=cli.js.map
|
|
63683
|
-
//# debugId=
|
|
63869
|
+
//# debugId=d7bda084-a223-5aa7-8605-578888bd1b33
|
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]="e96374bd-b4c9-5452-8570-b81c11a2959a")}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";
|
|
@@ -18,6 +18,7 @@ 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";
|
|
21
22
|
import { gzipSync } from "node:zlib";
|
|
22
23
|
//#region ../../node_modules/.pnpm/effect@4.0.0-beta.70/node_modules/effect/dist/Pipeable.js
|
|
23
24
|
/**
|
|
@@ -32927,6 +32928,8 @@ const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
|
|
|
32927
32928
|
const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
32928
32929
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
32929
32930
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
32931
|
+
const CORE_PACKAGE_VERSION = "0.6.3";
|
|
32932
|
+
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
32930
32933
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
32931
32934
|
const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
|
|
32932
32935
|
const SUPPLY_CHAIN_CATEGORY = "Security";
|
|
@@ -35613,6 +35616,25 @@ var PerFileLintCacheEnabled = class extends Reference("react-doctor/PerFileLintC
|
|
|
35613
35616
|
if (CACHE_DISABLED_VALUES.has(noFileCache)) return false;
|
|
35614
35617
|
return true;
|
|
35615
35618
|
} }) {};
|
|
35619
|
+
/**
|
|
35620
|
+
* Whether the whole-project dead-code result cache
|
|
35621
|
+
* (`dead-code/dead-code-result-cache.ts`) is active. Defaults ON — a rescan
|
|
35622
|
+
* whose inputs (source tree, manifests, configs, analyzer version) are
|
|
35623
|
+
* unchanged replays the stored diagnostics instead of re-running the
|
|
35624
|
+
* analysis worker. Opt-OUT, two knobs (matching the per-file lint cache):
|
|
35625
|
+
*
|
|
35626
|
+
* - `REACT_DOCTOR_NO_CACHE` — the global off-switch.
|
|
35627
|
+
* - `REACT_DOCTOR_NO_DEAD_CODE_CACHE` — granular: bust only this cache.
|
|
35628
|
+
*
|
|
35629
|
+
* Tests override via `Layer.succeed(DeadCodeResultCacheEnabled, false)`.
|
|
35630
|
+
*/
|
|
35631
|
+
var DeadCodeResultCacheEnabled = class extends Reference("react-doctor/DeadCodeResultCacheEnabled", { defaultValue: () => {
|
|
35632
|
+
const noCache = process.env["REACT_DOCTOR_NO_CACHE"]?.toLowerCase() ?? "";
|
|
35633
|
+
const noDeadCodeCache = process.env["REACT_DOCTOR_NO_DEAD_CODE_CACHE"]?.toLowerCase() ?? "";
|
|
35634
|
+
if (CACHE_DISABLED_VALUES.has(noCache)) return false;
|
|
35635
|
+
if (CACHE_DISABLED_VALUES.has(noDeadCodeCache)) return false;
|
|
35636
|
+
return true;
|
|
35637
|
+
} }) {};
|
|
35616
35638
|
const DIAGNOSTIC_SURFACES = [
|
|
35617
35639
|
"cli",
|
|
35618
35640
|
"prComment",
|
|
@@ -37301,6 +37323,80 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
|
|
|
37301
37323
|
return [...seen].filter((pattern) => pattern.length > 0);
|
|
37302
37324
|
};
|
|
37303
37325
|
const collectDeadCodeEntryPatterns = (rootDirectory) => [...new Set(collectKnipPatterns(rootDirectory, "entry"))].filter((pattern) => pattern.length > 0);
|
|
37326
|
+
const atomicWriteJson = (filePath, value) => {
|
|
37327
|
+
try {
|
|
37328
|
+
NFS.mkdirSync(Path.dirname(filePath), { recursive: true });
|
|
37329
|
+
const temporaryPath = `${filePath}.${process.pid}.tmp`;
|
|
37330
|
+
NFS.writeFileSync(temporaryPath, JSON.stringify(value));
|
|
37331
|
+
NFS.renameSync(temporaryPath, filePath);
|
|
37332
|
+
} catch {
|
|
37333
|
+
return;
|
|
37334
|
+
}
|
|
37335
|
+
};
|
|
37336
|
+
const failOpenReadJson = (filePath, fallback) => {
|
|
37337
|
+
try {
|
|
37338
|
+
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
37339
|
+
} catch {
|
|
37340
|
+
return fallback;
|
|
37341
|
+
}
|
|
37342
|
+
};
|
|
37343
|
+
const ANALYZED_FILE_EXTENSIONS = new Set(DEFAULT_EXTENSIONS);
|
|
37344
|
+
const CORE_SIDE_MANIFEST_NAMES = ["knip.json", "deno.lock"];
|
|
37345
|
+
const ANALYZED_MANIFEST_NAMES = new Set([...ANALYZED_MANIFEST_FILENAMES, ...CORE_SIDE_MANIFEST_NAMES]);
|
|
37346
|
+
const isTsConfigLikeFile = (fileName) => (fileName.startsWith("tsconfig") || fileName.startsWith("jsconfig")) && fileName.endsWith(".json");
|
|
37347
|
+
const isFingerprintedFile = (fileName) => ANALYZED_FILE_EXTENSIONS.has(Path.extname(fileName).toLowerCase()) || ANALYZED_MANIFEST_NAMES.has(fileName) || isTsConfigLikeFile(fileName);
|
|
37348
|
+
const collectAnalyzedFileFingerprints = (rootDirectory) => {
|
|
37349
|
+
const fingerprints = [];
|
|
37350
|
+
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
|
|
37351
|
+
if (!isFingerprintedFile(name)) continue;
|
|
37352
|
+
try {
|
|
37353
|
+
const fileStat = NFS.statSync(absolutePath);
|
|
37354
|
+
const relativePath = Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/");
|
|
37355
|
+
fingerprints.push(`${relativePath}:${fileStat.mtimeMs}:${fileStat.size}`);
|
|
37356
|
+
} catch {}
|
|
37357
|
+
}
|
|
37358
|
+
return fingerprints.sort();
|
|
37359
|
+
};
|
|
37360
|
+
const bundledRequire$2 = createRequire(import.meta.url);
|
|
37361
|
+
const resolveDeslopVersion = () => {
|
|
37362
|
+
try {
|
|
37363
|
+
const packageJson = JSON.parse(NFS.readFileSync(bundledRequire$2.resolve("deslop-js/package.json"), "utf8"));
|
|
37364
|
+
return isRecord(packageJson) && typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
37365
|
+
} catch {
|
|
37366
|
+
return "unknown";
|
|
37367
|
+
}
|
|
37368
|
+
};
|
|
37369
|
+
const computeDeadCodeCacheKey = (input) => crypto.createHash("sha1").update(JSON.stringify({
|
|
37370
|
+
schemaVersion: 1,
|
|
37371
|
+
coreVersion: input.coreVersion,
|
|
37372
|
+
deslopVersion: resolveDeslopVersion(),
|
|
37373
|
+
deslopJsModuleSpecifier: input.deslopJsModuleSpecifier,
|
|
37374
|
+
entryPatterns: input.entryPatterns,
|
|
37375
|
+
ignorePatterns: input.ignorePatterns,
|
|
37376
|
+
tsConfigFile: input.tsConfigPath === void 0 ? null : Path.relative(input.rootDirectory, input.tsConfigPath).replace(/\\/g, "/"),
|
|
37377
|
+
files: collectAnalyzedFileFingerprints(input.rootDirectory)
|
|
37378
|
+
})).digest("hex");
|
|
37379
|
+
const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
|
|
37380
|
+
const decodeCachedDiagnostics = (raw) => {
|
|
37381
|
+
try {
|
|
37382
|
+
for (const entry of raw) validateDiagnostic$1(entry);
|
|
37383
|
+
return raw;
|
|
37384
|
+
} catch {
|
|
37385
|
+
return null;
|
|
37386
|
+
}
|
|
37387
|
+
};
|
|
37388
|
+
const lookupDeadCodeResultCache = (cacheDirectory, cacheKey) => {
|
|
37389
|
+
const persisted = failOpenReadJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), null);
|
|
37390
|
+
if (persisted === null || !isRecord(persisted) || persisted.version !== 1 || persisted.key !== cacheKey || !Array.isArray(persisted.diagnostics)) return null;
|
|
37391
|
+
return decodeCachedDiagnostics(persisted.diagnostics);
|
|
37392
|
+
};
|
|
37393
|
+
const storeDeadCodeResultCache = (cacheDirectory, cacheKey, diagnostics) => {
|
|
37394
|
+
atomicWriteJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), {
|
|
37395
|
+
version: 1,
|
|
37396
|
+
key: cacheKey,
|
|
37397
|
+
diagnostics
|
|
37398
|
+
});
|
|
37399
|
+
};
|
|
37304
37400
|
const readSystemFacts = () => ({
|
|
37305
37401
|
availableCores: os.availableParallelism(),
|
|
37306
37402
|
totalMemoryBytes: os.totalmem(),
|
|
@@ -37369,6 +37465,21 @@ const withDeadCodeWorkerSlot = async (task, abortSignal) => {
|
|
|
37369
37465
|
releaseSlot();
|
|
37370
37466
|
}
|
|
37371
37467
|
};
|
|
37468
|
+
const projectCacheSubdir = (projectDirectory) => crypto.createHash("sha256").update(projectDirectory).digest("hex").slice(0, 16);
|
|
37469
|
+
const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
37470
|
+
const cacheDirOverride = process.env["REACT_DOCTOR_CACHE_DIR"]?.trim();
|
|
37471
|
+
if (cacheDirOverride) return Path.join(cacheDirOverride, projectCacheSubdir(projectDirectory));
|
|
37472
|
+
const nodeModulesDirectory = Path.join(projectDirectory, "node_modules");
|
|
37473
|
+
if (NFS.existsSync(nodeModulesDirectory)) return Path.join(nodeModulesDirectory, ".cache", "react-doctor");
|
|
37474
|
+
return Path.join(os.tmpdir(), `react-doctor-cache-${resolveUserCacheScope()}`, projectCacheSubdir(projectDirectory));
|
|
37475
|
+
};
|
|
37476
|
+
const resolveUserCacheScope = () => {
|
|
37477
|
+
try {
|
|
37478
|
+
return String(process.getuid?.() ?? os.userInfo().username);
|
|
37479
|
+
} catch {
|
|
37480
|
+
return "shared";
|
|
37481
|
+
}
|
|
37482
|
+
};
|
|
37372
37483
|
/**
|
|
37373
37484
|
* Resolves a path to its canonical, symlink-free form, falling back to
|
|
37374
37485
|
* the input when it cannot be realpath'd (broken symlink, permission
|
|
@@ -37664,13 +37775,28 @@ const checkDeadCode = async (options) => {
|
|
|
37664
37775
|
if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
|
|
37665
37776
|
const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
|
|
37666
37777
|
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
37778
|
+
const tsConfigPath = resolveTsConfigPath(rootDirectory);
|
|
37779
|
+
const deslopJsModuleSpecifier = options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js");
|
|
37780
|
+
const cacheKey = options.cacheEnabled ? computeDeadCodeCacheKey({
|
|
37781
|
+
rootDirectory,
|
|
37782
|
+
entryPatterns,
|
|
37783
|
+
ignorePatterns,
|
|
37784
|
+
tsConfigPath,
|
|
37785
|
+
deslopJsModuleSpecifier,
|
|
37786
|
+
coreVersion: CORE_PACKAGE_VERSION
|
|
37787
|
+
}) : null;
|
|
37788
|
+
if (cacheKey !== null) {
|
|
37789
|
+
const cachedDiagnostics = lookupDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey);
|
|
37790
|
+
options.onCacheOutcome?.(cachedDiagnostics !== null);
|
|
37791
|
+
if (cachedDiagnostics !== null) return [...cachedDiagnostics];
|
|
37792
|
+
}
|
|
37667
37793
|
const spawnAndRun = () => {
|
|
37668
37794
|
return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
37669
37795
|
rootDirectory,
|
|
37670
37796
|
entryPatterns,
|
|
37671
|
-
tsConfigPath
|
|
37797
|
+
tsConfigPath,
|
|
37672
37798
|
ignorePatterns,
|
|
37673
|
-
deslopJsModuleSpecifier
|
|
37799
|
+
deslopJsModuleSpecifier,
|
|
37674
37800
|
parseConcurrency: options.parseConcurrency
|
|
37675
37801
|
}), options.workerTimeoutMs ?? 12e4, options.abortSignal);
|
|
37676
37802
|
};
|
|
@@ -37731,6 +37857,7 @@ const checkDeadCode = async (options) => {
|
|
|
37731
37857
|
category: DEAD_CODE_CATEGORY
|
|
37732
37858
|
});
|
|
37733
37859
|
}
|
|
37860
|
+
if (cacheKey !== null) storeDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey, diagnostics);
|
|
37734
37861
|
return diagnostics;
|
|
37735
37862
|
};
|
|
37736
37863
|
const DEAD_CODE_RULE_KEY_PREFIX = `${DEAD_CODE_PLUGIN}/`;
|
|
@@ -37833,12 +37960,15 @@ var Config = class Config extends Service()("react-doctor/Config") {
|
|
|
37833
37960
|
*/
|
|
37834
37961
|
var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
|
|
37835
37962
|
static layerNode = succeed$3(DeadCode, DeadCode.of({ run: (input) => unwrap(fn("DeadCode.run")(function* () {
|
|
37963
|
+
const cacheEnabled = yield* DeadCodeResultCacheEnabled;
|
|
37836
37964
|
return yield* tryPromise({
|
|
37837
37965
|
try: (signal) => checkDeadCode({
|
|
37838
37966
|
rootDirectory: input.rootDirectory,
|
|
37839
37967
|
parseConcurrency: input.parseConcurrency,
|
|
37840
37968
|
workerTimeoutMs: input.workerTimeoutMs,
|
|
37841
|
-
abortSignal: signal
|
|
37969
|
+
abortSignal: signal,
|
|
37970
|
+
cacheEnabled,
|
|
37971
|
+
onCacheOutcome: input.onCacheOutcome
|
|
37842
37972
|
}),
|
|
37843
37973
|
catch: (cause) => new ReactDoctorError({ reason: new DeadCodeAnalysisFailed({ cause }) })
|
|
37844
37974
|
}).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)));
|
|
@@ -38764,23 +38894,6 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
38764
38894
|
}
|
|
38765
38895
|
};
|
|
38766
38896
|
};
|
|
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
38897
|
const validateDiagnostic = decodeUnknownSync(Diagnostic);
|
|
38785
38898
|
const decodeFileDiagnostics = (raw) => {
|
|
38786
38899
|
if (!Array.isArray(raw)) return null;
|
|
@@ -39908,21 +40021,6 @@ const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
|
39908
40021
|
}
|
|
39909
40022
|
return batches.filter((batch) => batch.fileIndexes.length > 0).map((batch) => batch.fileIndexes.sort((leftIndex, rightIndex) => leftIndex - rightIndex).map((fileIndex) => files[fileIndex]));
|
|
39910
40023
|
};
|
|
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
40024
|
/**
|
|
39927
40025
|
* Atomically (re)writes the generated oxlintrc.json. Used twice in
|
|
39928
40026
|
* the runner: once for the primary scan, once for the
|
|
@@ -41049,7 +41147,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41049
41147
|
const buildCollectDeadCode = (deadCodeTimeout) => runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
41050
41148
|
rootDirectory: scanDirectory,
|
|
41051
41149
|
parseConcurrency: deadCodeParseConcurrency,
|
|
41052
|
-
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs
|
|
41150
|
+
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
|
|
41151
|
+
onCacheOutcome: (didHitCache) => {
|
|
41152
|
+
deadCodeCacheHit = didHitCache;
|
|
41153
|
+
}
|
|
41053
41154
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
41054
41155
|
yield* set(deadCodeFailure, {
|
|
41055
41156
|
didFail: true,
|
|
@@ -41077,6 +41178,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41077
41178
|
let lastReportedTotalFileCount = 0;
|
|
41078
41179
|
let lintCacheHitFileCount = null;
|
|
41079
41180
|
let lintCacheTotalFileCount = null;
|
|
41181
|
+
let deadCodeCacheHit = null;
|
|
41080
41182
|
const baseLintStream = linterService.run({
|
|
41081
41183
|
rootDirectory: scanDirectory,
|
|
41082
41184
|
project,
|
|
@@ -41206,6 +41308,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41206
41308
|
securityScanFailed,
|
|
41207
41309
|
lintCacheHitFileCount,
|
|
41208
41310
|
lintCacheTotalFileCount,
|
|
41311
|
+
deadCodeCacheHit: lintFailureState.didFail ? null : deadCodeCacheHit,
|
|
41209
41312
|
suppressedRuleCounts: transform.summarizeSuppressions()
|
|
41210
41313
|
};
|
|
41211
41314
|
}).pipe(withSpan("runInspect", { attributes: {
|
|
@@ -41846,4 +41949,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
41846
41949
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
41847
41950
|
|
|
41848
41951
|
//# sourceMappingURL=index.js.map
|
|
41849
|
-
//# debugId=
|
|
41952
|
+
//# debugId=e96374bd-b4c9-5452-8570-b81c11a2959a
|
package/dist/lsp.js
CHANGED
|
@@ -15,6 +15,7 @@ import { fileURLToPath } from "node:url";
|
|
|
15
15
|
import { createJiti } from "jiti";
|
|
16
16
|
import * as Crypto from "node:crypto";
|
|
17
17
|
import crypto, { createHash } from "node:crypto";
|
|
18
|
+
import { ANALYZED_MANIFEST_FILENAMES, DEFAULT_EXTENSIONS } from "deslop-js/analyzed-inputs";
|
|
18
19
|
import { gzipSync } from "node:zlib";
|
|
19
20
|
import { CodeActionKind, CodeActionTriggerKind, DidChangeWatchedFilesNotification, DocumentDiagnosticReportKind, FileChangeType, TextDocumentSyncKind, TextDocuments, createConnection } from "vscode-languageserver/node.js";
|
|
20
21
|
import { TextDocument } from "vscode-languageserver-textdocument";
|
|
@@ -32986,6 +32987,8 @@ const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
|
|
|
32986
32987
|
const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
32987
32988
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
32988
32989
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
32990
|
+
const CORE_PACKAGE_VERSION = "0.6.3";
|
|
32991
|
+
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
32989
32992
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
32990
32993
|
const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
|
|
32991
32994
|
const SUPPLY_CHAIN_CATEGORY = "Security";
|
|
@@ -35646,6 +35649,25 @@ var PerFileLintCacheEnabled = class extends Reference("react-doctor/PerFileLintC
|
|
|
35646
35649
|
if (CACHE_DISABLED_VALUES.has(noFileCache)) return false;
|
|
35647
35650
|
return true;
|
|
35648
35651
|
} }) {};
|
|
35652
|
+
/**
|
|
35653
|
+
* Whether the whole-project dead-code result cache
|
|
35654
|
+
* (`dead-code/dead-code-result-cache.ts`) is active. Defaults ON — a rescan
|
|
35655
|
+
* whose inputs (source tree, manifests, configs, analyzer version) are
|
|
35656
|
+
* unchanged replays the stored diagnostics instead of re-running the
|
|
35657
|
+
* analysis worker. Opt-OUT, two knobs (matching the per-file lint cache):
|
|
35658
|
+
*
|
|
35659
|
+
* - `REACT_DOCTOR_NO_CACHE` — the global off-switch.
|
|
35660
|
+
* - `REACT_DOCTOR_NO_DEAD_CODE_CACHE` — granular: bust only this cache.
|
|
35661
|
+
*
|
|
35662
|
+
* Tests override via `Layer.succeed(DeadCodeResultCacheEnabled, false)`.
|
|
35663
|
+
*/
|
|
35664
|
+
var DeadCodeResultCacheEnabled = class extends Reference("react-doctor/DeadCodeResultCacheEnabled", { defaultValue: () => {
|
|
35665
|
+
const noCache = process.env["REACT_DOCTOR_NO_CACHE"]?.toLowerCase() ?? "";
|
|
35666
|
+
const noDeadCodeCache = process.env["REACT_DOCTOR_NO_DEAD_CODE_CACHE"]?.toLowerCase() ?? "";
|
|
35667
|
+
if (CACHE_DISABLED_VALUES.has(noCache)) return false;
|
|
35668
|
+
if (CACHE_DISABLED_VALUES.has(noDeadCodeCache)) return false;
|
|
35669
|
+
return true;
|
|
35670
|
+
} }) {};
|
|
35649
35671
|
const DIAGNOSTIC_SURFACES = [
|
|
35650
35672
|
"cli",
|
|
35651
35673
|
"prComment",
|
|
@@ -37286,6 +37308,80 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
|
|
|
37286
37308
|
return [...seen].filter((pattern) => pattern.length > 0);
|
|
37287
37309
|
};
|
|
37288
37310
|
const collectDeadCodeEntryPatterns = (rootDirectory) => [...new Set(collectKnipPatterns(rootDirectory, "entry"))].filter((pattern) => pattern.length > 0);
|
|
37311
|
+
const atomicWriteJson = (filePath, value) => {
|
|
37312
|
+
try {
|
|
37313
|
+
NFS.mkdirSync(Path.dirname(filePath), { recursive: true });
|
|
37314
|
+
const temporaryPath = `${filePath}.${process.pid}.tmp`;
|
|
37315
|
+
NFS.writeFileSync(temporaryPath, JSON.stringify(value));
|
|
37316
|
+
NFS.renameSync(temporaryPath, filePath);
|
|
37317
|
+
} catch {
|
|
37318
|
+
return;
|
|
37319
|
+
}
|
|
37320
|
+
};
|
|
37321
|
+
const failOpenReadJson = (filePath, fallback) => {
|
|
37322
|
+
try {
|
|
37323
|
+
return JSON.parse(NFS.readFileSync(filePath, "utf8"));
|
|
37324
|
+
} catch {
|
|
37325
|
+
return fallback;
|
|
37326
|
+
}
|
|
37327
|
+
};
|
|
37328
|
+
const ANALYZED_FILE_EXTENSIONS = new Set(DEFAULT_EXTENSIONS);
|
|
37329
|
+
const CORE_SIDE_MANIFEST_NAMES = ["knip.json", "deno.lock"];
|
|
37330
|
+
const ANALYZED_MANIFEST_NAMES = new Set([...ANALYZED_MANIFEST_FILENAMES, ...CORE_SIDE_MANIFEST_NAMES]);
|
|
37331
|
+
const isTsConfigLikeFile = (fileName) => (fileName.startsWith("tsconfig") || fileName.startsWith("jsconfig")) && fileName.endsWith(".json");
|
|
37332
|
+
const isFingerprintedFile = (fileName) => ANALYZED_FILE_EXTENSIONS.has(Path.extname(fileName).toLowerCase()) || ANALYZED_MANIFEST_NAMES.has(fileName) || isTsConfigLikeFile(fileName);
|
|
37333
|
+
const collectAnalyzedFileFingerprints = (rootDirectory) => {
|
|
37334
|
+
const fingerprints = [];
|
|
37335
|
+
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
|
|
37336
|
+
if (!isFingerprintedFile(name)) continue;
|
|
37337
|
+
try {
|
|
37338
|
+
const fileStat = NFS.statSync(absolutePath);
|
|
37339
|
+
const relativePath = Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/");
|
|
37340
|
+
fingerprints.push(`${relativePath}:${fileStat.mtimeMs}:${fileStat.size}`);
|
|
37341
|
+
} catch {}
|
|
37342
|
+
}
|
|
37343
|
+
return fingerprints.sort();
|
|
37344
|
+
};
|
|
37345
|
+
const bundledRequire$2 = createRequire(import.meta.url);
|
|
37346
|
+
const resolveDeslopVersion = () => {
|
|
37347
|
+
try {
|
|
37348
|
+
const packageJson = JSON.parse(NFS.readFileSync(bundledRequire$2.resolve("deslop-js/package.json"), "utf8"));
|
|
37349
|
+
return isRecord(packageJson) && typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
37350
|
+
} catch {
|
|
37351
|
+
return "unknown";
|
|
37352
|
+
}
|
|
37353
|
+
};
|
|
37354
|
+
const computeDeadCodeCacheKey = (input) => crypto.createHash("sha1").update(JSON.stringify({
|
|
37355
|
+
schemaVersion: 1,
|
|
37356
|
+
coreVersion: input.coreVersion,
|
|
37357
|
+
deslopVersion: resolveDeslopVersion(),
|
|
37358
|
+
deslopJsModuleSpecifier: input.deslopJsModuleSpecifier,
|
|
37359
|
+
entryPatterns: input.entryPatterns,
|
|
37360
|
+
ignorePatterns: input.ignorePatterns,
|
|
37361
|
+
tsConfigFile: input.tsConfigPath === void 0 ? null : Path.relative(input.rootDirectory, input.tsConfigPath).replace(/\\/g, "/"),
|
|
37362
|
+
files: collectAnalyzedFileFingerprints(input.rootDirectory)
|
|
37363
|
+
})).digest("hex");
|
|
37364
|
+
const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
|
|
37365
|
+
const decodeCachedDiagnostics = (raw) => {
|
|
37366
|
+
try {
|
|
37367
|
+
for (const entry of raw) validateDiagnostic$1(entry);
|
|
37368
|
+
return raw;
|
|
37369
|
+
} catch {
|
|
37370
|
+
return null;
|
|
37371
|
+
}
|
|
37372
|
+
};
|
|
37373
|
+
const lookupDeadCodeResultCache = (cacheDirectory, cacheKey) => {
|
|
37374
|
+
const persisted = failOpenReadJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), null);
|
|
37375
|
+
if (persisted === null || !isRecord(persisted) || persisted.version !== 1 || persisted.key !== cacheKey || !Array.isArray(persisted.diagnostics)) return null;
|
|
37376
|
+
return decodeCachedDiagnostics(persisted.diagnostics);
|
|
37377
|
+
};
|
|
37378
|
+
const storeDeadCodeResultCache = (cacheDirectory, cacheKey, diagnostics) => {
|
|
37379
|
+
atomicWriteJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), {
|
|
37380
|
+
version: 1,
|
|
37381
|
+
key: cacheKey,
|
|
37382
|
+
diagnostics
|
|
37383
|
+
});
|
|
37384
|
+
};
|
|
37289
37385
|
const readSystemFacts = () => ({
|
|
37290
37386
|
availableCores: os.availableParallelism(),
|
|
37291
37387
|
totalMemoryBytes: os.totalmem(),
|
|
@@ -37354,6 +37450,21 @@ const withDeadCodeWorkerSlot = async (task, abortSignal) => {
|
|
|
37354
37450
|
releaseSlot();
|
|
37355
37451
|
}
|
|
37356
37452
|
};
|
|
37453
|
+
const projectCacheSubdir = (projectDirectory) => crypto.createHash("sha256").update(projectDirectory).digest("hex").slice(0, 16);
|
|
37454
|
+
const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
37455
|
+
const cacheDirOverride = process.env["REACT_DOCTOR_CACHE_DIR"]?.trim();
|
|
37456
|
+
if (cacheDirOverride) return Path.join(cacheDirOverride, projectCacheSubdir(projectDirectory));
|
|
37457
|
+
const nodeModulesDirectory = Path.join(projectDirectory, "node_modules");
|
|
37458
|
+
if (NFS.existsSync(nodeModulesDirectory)) return Path.join(nodeModulesDirectory, ".cache", "react-doctor");
|
|
37459
|
+
return Path.join(os.tmpdir(), `react-doctor-cache-${resolveUserCacheScope()}`, projectCacheSubdir(projectDirectory));
|
|
37460
|
+
};
|
|
37461
|
+
const resolveUserCacheScope = () => {
|
|
37462
|
+
try {
|
|
37463
|
+
return String(process.getuid?.() ?? os.userInfo().username);
|
|
37464
|
+
} catch {
|
|
37465
|
+
return "shared";
|
|
37466
|
+
}
|
|
37467
|
+
};
|
|
37357
37468
|
/**
|
|
37358
37469
|
* Resolves a path to its canonical, symlink-free form, falling back to
|
|
37359
37470
|
* the input when it cannot be realpath'd (broken symlink, permission
|
|
@@ -37649,13 +37760,28 @@ const checkDeadCode = async (options) => {
|
|
|
37649
37760
|
if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
|
|
37650
37761
|
const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
|
|
37651
37762
|
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
37763
|
+
const tsConfigPath = resolveTsConfigPath(rootDirectory);
|
|
37764
|
+
const deslopJsModuleSpecifier = options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js");
|
|
37765
|
+
const cacheKey = options.cacheEnabled ? computeDeadCodeCacheKey({
|
|
37766
|
+
rootDirectory,
|
|
37767
|
+
entryPatterns,
|
|
37768
|
+
ignorePatterns,
|
|
37769
|
+
tsConfigPath,
|
|
37770
|
+
deslopJsModuleSpecifier,
|
|
37771
|
+
coreVersion: CORE_PACKAGE_VERSION
|
|
37772
|
+
}) : null;
|
|
37773
|
+
if (cacheKey !== null) {
|
|
37774
|
+
const cachedDiagnostics = lookupDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey);
|
|
37775
|
+
options.onCacheOutcome?.(cachedDiagnostics !== null);
|
|
37776
|
+
if (cachedDiagnostics !== null) return [...cachedDiagnostics];
|
|
37777
|
+
}
|
|
37652
37778
|
const spawnAndRun = () => {
|
|
37653
37779
|
return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
37654
37780
|
rootDirectory,
|
|
37655
37781
|
entryPatterns,
|
|
37656
|
-
tsConfigPath
|
|
37782
|
+
tsConfigPath,
|
|
37657
37783
|
ignorePatterns,
|
|
37658
|
-
deslopJsModuleSpecifier
|
|
37784
|
+
deslopJsModuleSpecifier,
|
|
37659
37785
|
parseConcurrency: options.parseConcurrency
|
|
37660
37786
|
}), options.workerTimeoutMs ?? 12e4, options.abortSignal);
|
|
37661
37787
|
};
|
|
@@ -37716,6 +37842,7 @@ const checkDeadCode = async (options) => {
|
|
|
37716
37842
|
category: DEAD_CODE_CATEGORY
|
|
37717
37843
|
});
|
|
37718
37844
|
}
|
|
37845
|
+
if (cacheKey !== null) storeDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey, diagnostics);
|
|
37719
37846
|
return diagnostics;
|
|
37720
37847
|
};
|
|
37721
37848
|
const DEAD_CODE_RULE_KEY_PREFIX = `${DEAD_CODE_PLUGIN}/`;
|
|
@@ -37818,12 +37945,15 @@ var Config = class Config extends Service()("react-doctor/Config") {
|
|
|
37818
37945
|
*/
|
|
37819
37946
|
var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
|
|
37820
37947
|
static layerNode = succeed$3(DeadCode, DeadCode.of({ run: (input) => unwrap(fn("DeadCode.run")(function* () {
|
|
37948
|
+
const cacheEnabled = yield* DeadCodeResultCacheEnabled;
|
|
37821
37949
|
return yield* tryPromise({
|
|
37822
37950
|
try: (signal) => checkDeadCode({
|
|
37823
37951
|
rootDirectory: input.rootDirectory,
|
|
37824
37952
|
parseConcurrency: input.parseConcurrency,
|
|
37825
37953
|
workerTimeoutMs: input.workerTimeoutMs,
|
|
37826
|
-
abortSignal: signal
|
|
37954
|
+
abortSignal: signal,
|
|
37955
|
+
cacheEnabled,
|
|
37956
|
+
onCacheOutcome: input.onCacheOutcome
|
|
37827
37957
|
}),
|
|
37828
37958
|
catch: (cause) => new ReactDoctorError({ reason: new DeadCodeAnalysisFailed({ cause }) })
|
|
37829
37959
|
}).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)));
|
|
@@ -38749,23 +38879,6 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
38749
38879
|
}
|
|
38750
38880
|
};
|
|
38751
38881
|
};
|
|
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
38882
|
const validateDiagnostic = decodeUnknownSync(Diagnostic);
|
|
38770
38883
|
const decodeFileDiagnostics = (raw) => {
|
|
38771
38884
|
if (!Array.isArray(raw)) return null;
|
|
@@ -39893,21 +40006,6 @@ const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
|
|
|
39893
40006
|
}
|
|
39894
40007
|
return batches.filter((batch) => batch.fileIndexes.length > 0).map((batch) => batch.fileIndexes.sort((leftIndex, rightIndex) => leftIndex - rightIndex).map((fileIndex) => files[fileIndex]));
|
|
39895
40008
|
};
|
|
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
40009
|
/**
|
|
39912
40010
|
* Atomically (re)writes the generated oxlintrc.json. Used twice in
|
|
39913
40011
|
* the runner: once for the primary scan, once for the
|
|
@@ -41034,7 +41132,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41034
41132
|
const buildCollectDeadCode = (deadCodeTimeout) => runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
41035
41133
|
rootDirectory: scanDirectory,
|
|
41036
41134
|
parseConcurrency: deadCodeParseConcurrency,
|
|
41037
|
-
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs
|
|
41135
|
+
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
|
|
41136
|
+
onCacheOutcome: (didHitCache) => {
|
|
41137
|
+
deadCodeCacheHit = didHitCache;
|
|
41138
|
+
}
|
|
41038
41139
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
41039
41140
|
yield* set(deadCodeFailure, {
|
|
41040
41141
|
didFail: true,
|
|
@@ -41062,6 +41163,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41062
41163
|
let lastReportedTotalFileCount = 0;
|
|
41063
41164
|
let lintCacheHitFileCount = null;
|
|
41064
41165
|
let lintCacheTotalFileCount = null;
|
|
41166
|
+
let deadCodeCacheHit = null;
|
|
41065
41167
|
const baseLintStream = linterService.run({
|
|
41066
41168
|
rootDirectory: scanDirectory,
|
|
41067
41169
|
project,
|
|
@@ -41191,6 +41293,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41191
41293
|
securityScanFailed,
|
|
41192
41294
|
lintCacheHitFileCount,
|
|
41193
41295
|
lintCacheTotalFileCount,
|
|
41296
|
+
deadCodeCacheHit: lintFailureState.didFail ? null : deadCodeCacheHit,
|
|
41194
41297
|
suppressedRuleCounts: transform.summarizeSuppressions()
|
|
41195
41298
|
};
|
|
41196
41299
|
}).pipe(withSpan("runInspect", { attributes: {
|
|
@@ -43570,5 +43673,5 @@ const startLanguageServer = () => {
|
|
|
43570
43673
|
};
|
|
43571
43674
|
//#endregion
|
|
43572
43675
|
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=
|
|
43676
|
+
!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]="440ae812-d58d-5654-b3ad-1087b4aa2060")}catch(e){}}();
|
|
43677
|
+
//# debugId=440ae812-d58d-5654-b3ad-1087b4aa2060
|
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.20d81f6",
|
|
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.20d81f6"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/babel__code-frame": "^7.27.0",
|
|
@@ -74,8 +74,8 @@
|
|
|
74
74
|
"@xterm/headless": "^6.0.0",
|
|
75
75
|
"commander": "^14.0.3",
|
|
76
76
|
"ora": "^9.4.0",
|
|
77
|
-
"@react-doctor/core": "0.6.3",
|
|
78
77
|
"@react-doctor/api": "0.6.3",
|
|
78
|
+
"@react-doctor/core": "0.6.3",
|
|
79
79
|
"@react-doctor/language-server": "0.6.3"
|
|
80
80
|
},
|
|
81
81
|
"engines": {
|