react-doctor 0.6.3-dev.20d81f6 → 0.6.3-dev.8325343

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 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]="d7bda084-a223-5aa7-8605-578888bd1b33")}catch(e){}}();
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]="2921b8ff-14cf-5604-8ae5-b65b4f1347fc")}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,7 +18,6 @@ import { fileURLToPath, pathToFileURL } from "node:url";
18
18
  import { createJiti } from "jiti";
19
19
  import * as Crypto from "node:crypto";
20
20
  import crypto, { createHash, randomUUID } from "node:crypto";
21
- import { ANALYZED_MANIFEST_FILENAMES, DEFAULT_EXTENSIONS } from "deslop-js/analyzed-inputs";
22
21
  import { gzipSync } from "node:zlib";
23
22
  import * as Sentry from "@sentry/node";
24
23
  import { performance as performance$1 } from "node:perf_hooks";
@@ -36129,8 +36128,6 @@ const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
36129
36128
  const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
36130
36129
  const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
36131
36130
  const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
36132
- const CORE_PACKAGE_VERSION = "0.6.3";
36133
- const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
36134
36131
  const SUPPLY_CHAIN_PLUGIN = "socket";
36135
36132
  const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
36136
36133
  const SUPPLY_CHAIN_CATEGORY = "Security";
@@ -38815,25 +38812,6 @@ var PerFileLintCacheEnabled = class extends Reference("react-doctor/PerFileLintC
38815
38812
  if (CACHE_DISABLED_VALUES$1.has(noFileCache)) return false;
38816
38813
  return true;
38817
38814
  } }) {};
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
- } }) {};
38837
38815
  const DIAGNOSTIC_SURFACES = [
38838
38816
  "cli",
38839
38817
  "prComment",
@@ -40566,80 +40544,6 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
40566
40544
  return [...seen].filter((pattern) => pattern.length > 0);
40567
40545
  };
40568
40546
  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
- };
40643
40547
  const readSystemFacts = () => ({
40644
40548
  availableCores: os.availableParallelism(),
40645
40549
  totalMemoryBytes: os.totalmem(),
@@ -40708,21 +40612,6 @@ const withDeadCodeWorkerSlot = async (task, abortSignal) => {
40708
40612
  releaseSlot();
40709
40613
  }
40710
40614
  };
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
- };
40726
40615
  /**
40727
40616
  * Resolves a path to its canonical, symlink-free form, falling back to
40728
40617
  * the input when it cannot be realpath'd (broken symlink, permission
@@ -41018,28 +40907,13 @@ const checkDeadCode = async (options) => {
41018
40907
  if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
41019
40908
  const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
41020
40909
  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
- }
41036
40910
  const spawnAndRun = () => {
41037
40911
  return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
41038
40912
  rootDirectory,
41039
40913
  entryPatterns,
41040
- tsConfigPath,
40914
+ tsConfigPath: resolveTsConfigPath(rootDirectory),
41041
40915
  ignorePatterns,
41042
- deslopJsModuleSpecifier,
40916
+ deslopJsModuleSpecifier: options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js"),
41043
40917
  parseConcurrency: options.parseConcurrency
41044
40918
  }), options.workerTimeoutMs ?? 12e4, options.abortSignal);
41045
40919
  };
@@ -41100,7 +40974,6 @@ const checkDeadCode = async (options) => {
41100
40974
  category: DEAD_CODE_CATEGORY
41101
40975
  });
41102
40976
  }
41103
- if (cacheKey !== null) storeDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey, diagnostics);
41104
40977
  return diagnostics;
41105
40978
  };
41106
40979
  const DEAD_CODE_RULE_KEY_PREFIX = `${DEAD_CODE_PLUGIN}/`;
@@ -41203,15 +41076,12 @@ var Config = class Config extends Service()("react-doctor/Config") {
41203
41076
  */
41204
41077
  var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
41205
41078
  static layerNode = succeed$3(DeadCode, DeadCode.of({ run: (input) => unwrap(fn("DeadCode.run")(function* () {
41206
- const cacheEnabled = yield* DeadCodeResultCacheEnabled;
41207
41079
  return yield* tryPromise({
41208
41080
  try: (signal) => checkDeadCode({
41209
41081
  rootDirectory: input.rootDirectory,
41210
41082
  parseConcurrency: input.parseConcurrency,
41211
41083
  workerTimeoutMs: input.workerTimeoutMs,
41212
- abortSignal: signal,
41213
- cacheEnabled,
41214
- onCacheOutcome: input.onCacheOutcome
41084
+ abortSignal: signal
41215
41085
  }),
41216
41086
  catch: (cause) => new ReactDoctorError({ reason: new DeadCodeAnalysisFailed({ cause }) })
41217
41087
  }).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)));
@@ -42137,6 +42007,23 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
42137
42007
  }
42138
42008
  };
42139
42009
  };
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
+ };
42140
42027
  const validateDiagnostic = decodeUnknownSync(Diagnostic);
42141
42028
  const decodeFileDiagnostics = (raw) => {
42142
42029
  if (!Array.isArray(raw)) return null;
@@ -42194,7 +42081,7 @@ const createFileLintCache = (cacheDirectory, rulesetHash) => {
42194
42081
  }
42195
42082
  };
42196
42083
  };
42197
- const bundledRequire$3 = createRequire(import.meta.url);
42084
+ const bundledRequire$2 = createRequire(import.meta.url);
42198
42085
  const childNodeVersionByBinaryPath = /* @__PURE__ */ new Map();
42199
42086
  const resolveChildNodeVersion = (nodeBinaryPath) => {
42200
42087
  if (nodeBinaryPath === process.execPath) return process.version;
@@ -42220,7 +42107,7 @@ const TOOLCHAIN_PACKAGE_SPECIFIERS$1 = [
42220
42107
  const resolveOxlintToolchainVersions = (nodeBinaryPath = process.execPath) => {
42221
42108
  const versions = [`node=${resolveChildNodeVersion(nodeBinaryPath)}`];
42222
42109
  for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS$1) try {
42223
- const packageJson = bundledRequire$3(specifier);
42110
+ const packageJson = bundledRequire$2(specifier);
42224
42111
  const version = typeof packageJson.version === "string" ? packageJson.version : "unknown";
42225
42112
  versions.push(`${specifier}=${version}`);
42226
42113
  } catch {
@@ -43264,6 +43151,21 @@ const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
43264
43151
  }
43265
43152
  return batches.filter((batch) => batch.fileIndexes.length > 0).map((batch) => batch.fileIndexes.sort((leftIndex, rightIndex) => leftIndex - rightIndex).map((fileIndex) => files[fileIndex]));
43266
43153
  };
43154
+ const projectCacheSubdir = (projectDirectory) => crypto.createHash("sha256").update(projectDirectory).digest("hex").slice(0, 16);
43155
+ const resolveReactDoctorCacheDir = (projectDirectory) => {
43156
+ const cacheDirOverride = process.env["REACT_DOCTOR_CACHE_DIR"]?.trim();
43157
+ if (cacheDirOverride) return Path.join(cacheDirOverride, projectCacheSubdir(projectDirectory));
43158
+ const nodeModulesDirectory = Path.join(projectDirectory, "node_modules");
43159
+ if (NFS.existsSync(nodeModulesDirectory)) return Path.join(nodeModulesDirectory, ".cache", "react-doctor");
43160
+ return Path.join(os.tmpdir(), `react-doctor-cache-${resolveUserCacheScope()}`, projectCacheSubdir(projectDirectory));
43161
+ };
43162
+ const resolveUserCacheScope = () => {
43163
+ try {
43164
+ return String(process.getuid?.() ?? os.userInfo().username);
43165
+ } catch {
43166
+ return "shared";
43167
+ }
43168
+ };
43267
43169
  /**
43268
43170
  * Atomically (re)writes the generated oxlintrc.json. Used twice in
43269
43171
  * the runner: once for the primary scan, once for the
@@ -44390,10 +44292,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
44390
44292
  const buildCollectDeadCode = (deadCodeTimeout) => runCollect(applyPerElementPipeline(deadCodeService.run({
44391
44293
  rootDirectory: scanDirectory,
44392
44294
  parseConcurrency: deadCodeParseConcurrency,
44393
- workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
44394
- onCacheOutcome: (didHitCache) => {
44395
- deadCodeCacheHit = didHitCache;
44396
- }
44295
+ workerTimeoutMs: deadCodeTimeout.workerTimeoutMs
44397
44296
  }).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
44398
44297
  yield* set(deadCodeFailure, {
44399
44298
  didFail: true,
@@ -44421,7 +44320,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
44421
44320
  let lastReportedTotalFileCount = 0;
44422
44321
  let lintCacheHitFileCount = null;
44423
44322
  let lintCacheTotalFileCount = null;
44424
- let deadCodeCacheHit = null;
44425
44323
  const baseLintStream = linterService.run({
44426
44324
  rootDirectory: scanDirectory,
44427
44325
  project,
@@ -44551,7 +44449,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
44551
44449
  securityScanFailed,
44552
44450
  lintCacheHitFileCount,
44553
44451
  lintCacheTotalFileCount,
44554
- deadCodeCacheHit: lintFailureState.didFail ? null : deadCodeCacheHit,
44555
44452
  suppressedRuleCounts: transform.summarizeSuppressions()
44556
44453
  };
44557
44454
  }).pipe(withSpan("runInspect", { attributes: {
@@ -45333,7 +45230,6 @@ const isNonInteractiveEnvironment = () => NON_INTERACTIVE_ENVIRONMENT_VARIABLES.
45333
45230
  const REACT_DOCTOR_CONFIG_PROJECT_NAME = "react-doctor";
45334
45231
  const STAGED_FILES_TEMP_DIR_PREFIX = "react-doctor-staged-";
45335
45232
  const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
45336
- const RUN_GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
45337
45233
  const GH_DEFAULT_BRANCH_PROBE_TIMEOUT_MS = 5e3;
45338
45234
  const CI_TRUST_COMPANIES = "PayPal, Rippling, and Alibaba";
45339
45235
  const WELCOME_EXPLANATION_HOLD_MS = 1e3;
@@ -45424,7 +45320,7 @@ const makeNoopConsole = () => ({
45424
45320
  });
45425
45321
  //#endregion
45426
45322
  //#region src/cli/utils/version.ts
45427
- const VERSION = "0.6.3-dev.20d81f6";
45323
+ const VERSION = "0.6.3-dev.8325343";
45428
45324
  //#endregion
45429
45325
  //#region src/cli/utils/json-mode.ts
45430
45326
  let context = null;
@@ -45793,13 +45689,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
45793
45689
  * uploads source-map artifacts under, so stack frames symbolicate. Honors the
45794
45690
  * standard `SENTRY_RELEASE` override.
45795
45691
  */
45796
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.6.3-dev.20d81f6`;
45692
+ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.6.3-dev.8325343`;
45797
45693
  /**
45798
45694
  * Deployment environment shown in Sentry's environment filter. Defaults to
45799
45695
  * `production` for tagged releases and `development` for dev/unbuilt versions,
45800
45696
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
45801
45697
  */
45802
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.6.3-dev.20d81f6") ? "development" : "production");
45698
+ const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.6.3-dev.8325343") ? "development" : "production");
45803
45699
  /**
45804
45700
  * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
45805
45701
  * (set to `0` to disable tracing) and falls back to
@@ -46000,8 +45896,7 @@ const runGit = (projectRoot, args) => {
46000
45896
  "ignore",
46001
45897
  "pipe",
46002
45898
  "ignore"
46003
- ],
46004
- maxBuffer: RUN_GIT_MAX_BUFFER_BYTES
45899
+ ]
46005
45900
  }).trim();
46006
45901
  } catch {
46007
45902
  return null;
@@ -58027,8 +57922,7 @@ const buildOutcomeAttributes = (input) => {
58027
57922
  }),
58028
57923
  ...withNamespace("deadCode", {
58029
57924
  failed: input.didDeadCodeFail ?? null,
58030
- overlapped: input.deadCodeOverlapped ?? null,
58031
- cacheHit: result.deadCodeCacheHit ?? null
57925
+ overlapped: input.deadCodeOverlapped ?? null
58032
57926
  }),
58033
57927
  ...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut ?? null }),
58034
57928
  ...withNamespace("securityScan", { failed: input.securityScanFailed ?? null }),
@@ -59351,89 +59245,13 @@ const stringifyStableJson = (value) => {
59351
59245
  };
59352
59246
  const hashString = (value) => crypto.createHash("sha1").update(value).digest("hex");
59353
59247
  const readHeadSha = (projectDirectory) => runGit(projectDirectory, ["rev-parse", "HEAD"]);
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, [
59248
+ const isWorktreeClean = (projectDirectory) => {
59249
+ const status = runGit(projectDirectory, [
59403
59250
  "status",
59404
59251
  "--porcelain=v1",
59405
- "-z",
59406
- "--untracked-files=all"
59252
+ "--untracked-files=normal"
59407
59253
  ]);
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
- }
59254
+ return status !== null && status.length === 0;
59437
59255
  };
59438
59256
  const hasHiddenTrackedFileState = (projectDirectory) => {
59439
59257
  const output = runGit(projectDirectory, ["ls-files", "-v"]);
@@ -59516,9 +59334,8 @@ const resolveToolchainFingerprint = (nodeBinaryPath) => {
59516
59334
  };
59517
59335
  const buildScanResultCacheKey = (input) => {
59518
59336
  if (isScanResultCacheDisabled()) return null;
59337
+ if (!isWorktreeClean(input.projectDirectory)) return null;
59519
59338
  if (hasHiddenTrackedFileState(input.projectDirectory)) return null;
59520
- const worktreeFingerprint = buildWorktreeFingerprint(input.projectDirectory);
59521
- if (worktreeFingerprint === null) return null;
59522
59339
  const headSha = readHeadSha(input.projectDirectory);
59523
59340
  if (headSha === null) return null;
59524
59341
  if (stringifyStableJson(input.userConfig) === null) return null;
@@ -59526,8 +59343,6 @@ const buildScanResultCacheKey = (input) => {
59526
59343
  schemaVersion: 3,
59527
59344
  projectIdentity: resolveProjectIdentity(input.projectDirectory),
59528
59345
  headSha,
59529
- worktreeFingerprint,
59530
- dotenvFingerprint: resolveDotenvFingerprint(input.projectDirectory),
59531
59346
  reactDoctorVersion: input.version,
59532
59347
  nodeVersion: process.version,
59533
59348
  toolchainFingerprint: resolveToolchainFingerprint(input.nodeBinaryPath),
@@ -59920,8 +59735,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
59920
59735
  scanMode: baselineDelta ? "baseline" : isDiffMode ? "diff" : "full",
59921
59736
  baselineDegraded,
59922
59737
  lintCacheHitFileCount: output.lintCacheHitFileCount,
59923
- lintCacheTotalFileCount: output.lintCacheTotalFileCount,
59924
- deadCodeCacheHit: output.deadCodeCacheHit
59738
+ lintCacheTotalFileCount: output.lintCacheTotalFileCount
59925
59739
  });
59926
59740
  recordOnboardingCompletion(options);
59927
59741
  return result;
@@ -59956,7 +59770,6 @@ const renderAndRecordScan = async (input) => {
59956
59770
  scanElapsedMilliseconds: input.payload.scanElapsedMilliseconds,
59957
59771
  lintCacheHitFileCount: input.lintCacheHitFileCount ?? null,
59958
59772
  lintCacheTotalFileCount: input.lintCacheTotalFileCount ?? null,
59959
- deadCodeCacheHit: input.deadCodeCacheHit ?? null,
59960
59773
  baselineDelta: input.payload.baselineDelta
59961
59774
  }), input.options.silent));
59962
59775
  const { workerCount: resolvedWorkerCount, parallel } = resolveWorkerTelemetry(input.payload.scanConcurrency, input.options.concurrency);
@@ -59995,7 +59808,7 @@ const renderAndRecordScan = async (input) => {
59995
59808
  return result;
59996
59809
  };
59997
59810
  const finalizeAndRender = (input) => gen(function* () {
59998
- const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, directory, scannedFileCount, scannedFilePaths, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount, deadCodeCacheHit, baselineDelta } = input;
59811
+ const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, directory, scannedFileCount, scannedFilePaths, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount, baselineDelta } = input;
59999
59812
  const { skippedChecks, skippedCheckReasons } = buildSkippedChecks({
60000
59813
  didLintFail,
60001
59814
  lintFailureReason,
@@ -60019,7 +59832,6 @@ const finalizeAndRender = (input) => gen(function* () {
60019
59832
  lintCacheHitFileCount,
60020
59833
  lintCacheTotalFileCount
60021
59834
  } : {},
60022
- ...deadCodeCacheHit !== null ? { deadCodeCacheHit } : {},
60023
59835
  ...baselineDelta ? { baselineDelta } : {}
60024
59836
  });
60025
59837
  if (options.suppressRendering) return buildResult();
@@ -63866,4 +63678,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
63866
63678
  export {};
63867
63679
 
63868
63680
  //# sourceMappingURL=cli.js.map
63869
- //# debugId=d7bda084-a223-5aa7-8605-578888bd1b33
63681
+ //# debugId=2921b8ff-14cf-5604-8ae5-b65b4f1347fc
package/dist/index.d.ts CHANGED
@@ -9743,14 +9743,6 @@ 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;
9754
9746
  /**
9755
9747
  * Present only for a baseline run (`InspectOptions.baseline` set). The
9756
9748
  * `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]="e96374bd-b4c9-5452-8570-b81c11a2959a")}catch(e){}}();
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]="8565c6ea-39f3-51e6-895a-ae9f7c026d0e")}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,7 +18,6 @@ import { fileURLToPath } from "node:url";
18
18
  import { createJiti } from "jiti";
19
19
  import * as Crypto from "node:crypto";
20
20
  import crypto, { createHash } from "node:crypto";
21
- import { ANALYZED_MANIFEST_FILENAMES, DEFAULT_EXTENSIONS } from "deslop-js/analyzed-inputs";
22
21
  import { gzipSync } from "node:zlib";
23
22
  //#region ../../node_modules/.pnpm/effect@4.0.0-beta.70/node_modules/effect/dist/Pipeable.js
24
23
  /**
@@ -32928,8 +32927,6 @@ const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
32928
32927
  const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
32929
32928
  const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
32930
32929
  const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
32931
- const CORE_PACKAGE_VERSION = "0.6.3";
32932
- const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
32933
32930
  const SUPPLY_CHAIN_PLUGIN = "socket";
32934
32931
  const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
32935
32932
  const SUPPLY_CHAIN_CATEGORY = "Security";
@@ -35616,25 +35613,6 @@ var PerFileLintCacheEnabled = class extends Reference("react-doctor/PerFileLintC
35616
35613
  if (CACHE_DISABLED_VALUES.has(noFileCache)) return false;
35617
35614
  return true;
35618
35615
  } }) {};
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
- } }) {};
35638
35616
  const DIAGNOSTIC_SURFACES = [
35639
35617
  "cli",
35640
35618
  "prComment",
@@ -37323,80 +37301,6 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
37323
37301
  return [...seen].filter((pattern) => pattern.length > 0);
37324
37302
  };
37325
37303
  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
- };
37400
37304
  const readSystemFacts = () => ({
37401
37305
  availableCores: os.availableParallelism(),
37402
37306
  totalMemoryBytes: os.totalmem(),
@@ -37465,21 +37369,6 @@ const withDeadCodeWorkerSlot = async (task, abortSignal) => {
37465
37369
  releaseSlot();
37466
37370
  }
37467
37371
  };
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
- };
37483
37372
  /**
37484
37373
  * Resolves a path to its canonical, symlink-free form, falling back to
37485
37374
  * the input when it cannot be realpath'd (broken symlink, permission
@@ -37775,28 +37664,13 @@ const checkDeadCode = async (options) => {
37775
37664
  if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
37776
37665
  const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
37777
37666
  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
- }
37793
37667
  const spawnAndRun = () => {
37794
37668
  return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
37795
37669
  rootDirectory,
37796
37670
  entryPatterns,
37797
- tsConfigPath,
37671
+ tsConfigPath: resolveTsConfigPath(rootDirectory),
37798
37672
  ignorePatterns,
37799
- deslopJsModuleSpecifier,
37673
+ deslopJsModuleSpecifier: options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js"),
37800
37674
  parseConcurrency: options.parseConcurrency
37801
37675
  }), options.workerTimeoutMs ?? 12e4, options.abortSignal);
37802
37676
  };
@@ -37857,7 +37731,6 @@ const checkDeadCode = async (options) => {
37857
37731
  category: DEAD_CODE_CATEGORY
37858
37732
  });
37859
37733
  }
37860
- if (cacheKey !== null) storeDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey, diagnostics);
37861
37734
  return diagnostics;
37862
37735
  };
37863
37736
  const DEAD_CODE_RULE_KEY_PREFIX = `${DEAD_CODE_PLUGIN}/`;
@@ -37960,15 +37833,12 @@ var Config = class Config extends Service()("react-doctor/Config") {
37960
37833
  */
37961
37834
  var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
37962
37835
  static layerNode = succeed$3(DeadCode, DeadCode.of({ run: (input) => unwrap(fn("DeadCode.run")(function* () {
37963
- const cacheEnabled = yield* DeadCodeResultCacheEnabled;
37964
37836
  return yield* tryPromise({
37965
37837
  try: (signal) => checkDeadCode({
37966
37838
  rootDirectory: input.rootDirectory,
37967
37839
  parseConcurrency: input.parseConcurrency,
37968
37840
  workerTimeoutMs: input.workerTimeoutMs,
37969
- abortSignal: signal,
37970
- cacheEnabled,
37971
- onCacheOutcome: input.onCacheOutcome
37841
+ abortSignal: signal
37972
37842
  }),
37973
37843
  catch: (cause) => new ReactDoctorError({ reason: new DeadCodeAnalysisFailed({ cause }) })
37974
37844
  }).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)));
@@ -38894,6 +38764,23 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
38894
38764
  }
38895
38765
  };
38896
38766
  };
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
+ };
38897
38784
  const validateDiagnostic = decodeUnknownSync(Diagnostic);
38898
38785
  const decodeFileDiagnostics = (raw) => {
38899
38786
  if (!Array.isArray(raw)) return null;
@@ -40021,6 +39908,21 @@ const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
40021
39908
  }
40022
39909
  return batches.filter((batch) => batch.fileIndexes.length > 0).map((batch) => batch.fileIndexes.sort((leftIndex, rightIndex) => leftIndex - rightIndex).map((fileIndex) => files[fileIndex]));
40023
39910
  };
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
+ };
40024
39926
  /**
40025
39927
  * Atomically (re)writes the generated oxlintrc.json. Used twice in
40026
39928
  * the runner: once for the primary scan, once for the
@@ -41147,10 +41049,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
41147
41049
  const buildCollectDeadCode = (deadCodeTimeout) => runCollect(applyPerElementPipeline(deadCodeService.run({
41148
41050
  rootDirectory: scanDirectory,
41149
41051
  parseConcurrency: deadCodeParseConcurrency,
41150
- workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
41151
- onCacheOutcome: (didHitCache) => {
41152
- deadCodeCacheHit = didHitCache;
41153
- }
41052
+ workerTimeoutMs: deadCodeTimeout.workerTimeoutMs
41154
41053
  }).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
41155
41054
  yield* set(deadCodeFailure, {
41156
41055
  didFail: true,
@@ -41178,7 +41077,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
41178
41077
  let lastReportedTotalFileCount = 0;
41179
41078
  let lintCacheHitFileCount = null;
41180
41079
  let lintCacheTotalFileCount = null;
41181
- let deadCodeCacheHit = null;
41182
41080
  const baseLintStream = linterService.run({
41183
41081
  rootDirectory: scanDirectory,
41184
41082
  project,
@@ -41308,7 +41206,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
41308
41206
  securityScanFailed,
41309
41207
  lintCacheHitFileCount,
41310
41208
  lintCacheTotalFileCount,
41311
- deadCodeCacheHit: lintFailureState.didFail ? null : deadCodeCacheHit,
41312
41209
  suppressedRuleCounts: transform.summarizeSuppressions()
41313
41210
  };
41314
41211
  }).pipe(withSpan("runInspect", { attributes: {
@@ -41949,4 +41846,4 @@ const toJsonReport = (result, options) => buildJsonReport({
41949
41846
  export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
41950
41847
 
41951
41848
  //# sourceMappingURL=index.js.map
41952
- //# debugId=e96374bd-b4c9-5452-8570-b81c11a2959a
41849
+ //# debugId=8565c6ea-39f3-51e6-895a-ae9f7c026d0e
package/dist/lsp.js CHANGED
@@ -15,7 +15,6 @@ 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";
19
18
  import { gzipSync } from "node:zlib";
20
19
  import { CodeActionKind, CodeActionTriggerKind, DidChangeWatchedFilesNotification, DocumentDiagnosticReportKind, FileChangeType, TextDocumentSyncKind, TextDocuments, createConnection } from "vscode-languageserver/node.js";
21
20
  import { TextDocument } from "vscode-languageserver-textdocument";
@@ -32987,8 +32986,6 @@ const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
32987
32986
  const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
32988
32987
  const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
32989
32988
  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";
32992
32989
  const SUPPLY_CHAIN_PLUGIN = "socket";
32993
32990
  const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
32994
32991
  const SUPPLY_CHAIN_CATEGORY = "Security";
@@ -35649,25 +35646,6 @@ var PerFileLintCacheEnabled = class extends Reference("react-doctor/PerFileLintC
35649
35646
  if (CACHE_DISABLED_VALUES.has(noFileCache)) return false;
35650
35647
  return true;
35651
35648
  } }) {};
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
- } }) {};
35671
35649
  const DIAGNOSTIC_SURFACES = [
35672
35650
  "cli",
35673
35651
  "prComment",
@@ -37308,80 +37286,6 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
37308
37286
  return [...seen].filter((pattern) => pattern.length > 0);
37309
37287
  };
37310
37288
  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
- };
37385
37289
  const readSystemFacts = () => ({
37386
37290
  availableCores: os.availableParallelism(),
37387
37291
  totalMemoryBytes: os.totalmem(),
@@ -37450,21 +37354,6 @@ const withDeadCodeWorkerSlot = async (task, abortSignal) => {
37450
37354
  releaseSlot();
37451
37355
  }
37452
37356
  };
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
- };
37468
37357
  /**
37469
37358
  * Resolves a path to its canonical, symlink-free form, falling back to
37470
37359
  * the input when it cannot be realpath'd (broken symlink, permission
@@ -37760,28 +37649,13 @@ const checkDeadCode = async (options) => {
37760
37649
  if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
37761
37650
  const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
37762
37651
  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
- }
37778
37652
  const spawnAndRun = () => {
37779
37653
  return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
37780
37654
  rootDirectory,
37781
37655
  entryPatterns,
37782
- tsConfigPath,
37656
+ tsConfigPath: resolveTsConfigPath(rootDirectory),
37783
37657
  ignorePatterns,
37784
- deslopJsModuleSpecifier,
37658
+ deslopJsModuleSpecifier: options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js"),
37785
37659
  parseConcurrency: options.parseConcurrency
37786
37660
  }), options.workerTimeoutMs ?? 12e4, options.abortSignal);
37787
37661
  };
@@ -37842,7 +37716,6 @@ const checkDeadCode = async (options) => {
37842
37716
  category: DEAD_CODE_CATEGORY
37843
37717
  });
37844
37718
  }
37845
- if (cacheKey !== null) storeDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey, diagnostics);
37846
37719
  return diagnostics;
37847
37720
  };
37848
37721
  const DEAD_CODE_RULE_KEY_PREFIX = `${DEAD_CODE_PLUGIN}/`;
@@ -37945,15 +37818,12 @@ var Config = class Config extends Service()("react-doctor/Config") {
37945
37818
  */
37946
37819
  var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
37947
37820
  static layerNode = succeed$3(DeadCode, DeadCode.of({ run: (input) => unwrap(fn("DeadCode.run")(function* () {
37948
- const cacheEnabled = yield* DeadCodeResultCacheEnabled;
37949
37821
  return yield* tryPromise({
37950
37822
  try: (signal) => checkDeadCode({
37951
37823
  rootDirectory: input.rootDirectory,
37952
37824
  parseConcurrency: input.parseConcurrency,
37953
37825
  workerTimeoutMs: input.workerTimeoutMs,
37954
- abortSignal: signal,
37955
- cacheEnabled,
37956
- onCacheOutcome: input.onCacheOutcome
37826
+ abortSignal: signal
37957
37827
  }),
37958
37828
  catch: (cause) => new ReactDoctorError({ reason: new DeadCodeAnalysisFailed({ cause }) })
37959
37829
  }).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)));
@@ -38879,6 +38749,23 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
38879
38749
  }
38880
38750
  };
38881
38751
  };
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
+ };
38882
38769
  const validateDiagnostic = decodeUnknownSync(Diagnostic);
38883
38770
  const decodeFileDiagnostics = (raw) => {
38884
38771
  if (!Array.isArray(raw)) return null;
@@ -40006,6 +39893,21 @@ const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
40006
39893
  }
40007
39894
  return batches.filter((batch) => batch.fileIndexes.length > 0).map((batch) => batch.fileIndexes.sort((leftIndex, rightIndex) => leftIndex - rightIndex).map((fileIndex) => files[fileIndex]));
40008
39895
  };
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
+ };
40009
39911
  /**
40010
39912
  * Atomically (re)writes the generated oxlintrc.json. Used twice in
40011
39913
  * the runner: once for the primary scan, once for the
@@ -41132,10 +41034,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
41132
41034
  const buildCollectDeadCode = (deadCodeTimeout) => runCollect(applyPerElementPipeline(deadCodeService.run({
41133
41035
  rootDirectory: scanDirectory,
41134
41036
  parseConcurrency: deadCodeParseConcurrency,
41135
- workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
41136
- onCacheOutcome: (didHitCache) => {
41137
- deadCodeCacheHit = didHitCache;
41138
- }
41037
+ workerTimeoutMs: deadCodeTimeout.workerTimeoutMs
41139
41038
  }).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
41140
41039
  yield* set(deadCodeFailure, {
41141
41040
  didFail: true,
@@ -41163,7 +41062,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
41163
41062
  let lastReportedTotalFileCount = 0;
41164
41063
  let lintCacheHitFileCount = null;
41165
41064
  let lintCacheTotalFileCount = null;
41166
- let deadCodeCacheHit = null;
41167
41065
  const baseLintStream = linterService.run({
41168
41066
  rootDirectory: scanDirectory,
41169
41067
  project,
@@ -41293,7 +41191,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
41293
41191
  securityScanFailed,
41294
41192
  lintCacheHitFileCount,
41295
41193
  lintCacheTotalFileCount,
41296
- deadCodeCacheHit: lintFailureState.didFail ? null : deadCodeCacheHit,
41297
41194
  suppressedRuleCounts: transform.summarizeSuppressions()
41298
41195
  };
41299
41196
  }).pipe(withSpan("runInspect", { attributes: {
@@ -43673,5 +43570,5 @@ const startLanguageServer = () => {
43673
43570
  };
43674
43571
  //#endregion
43675
43572
  export { startLanguageServer };
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
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]="94a57ec3-d5a9-52ea-a743-e91d996065b8")}catch(e){}}();
43574
+ //# debugId=94a57ec3-d5a9-52ea-a743-e91d996065b8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor",
3
- "version": "0.6.3-dev.20d81f6",
3
+ "version": "0.6.3-dev.8325343",
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.20d81f6"
69
+ "oxlint-plugin-react-doctor": "0.6.3-dev.8325343"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/babel__code-frame": "^7.27.0",