react-doctor 0.6.3-dev.ced746f → 0.6.3-dev.e257a5e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js 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]="1722becf-6e91-56ae-afc9-1e1410647bc3")}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]="82500030-a7a7-5da8-b18e-cbdb42102dc3")}catch(e){}}();
3
3
  import { createRequire } from "node:module";
4
4
  import * as NodeChildProcess from "node:child_process";
5
5
  import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
@@ -9,7 +9,7 @@ import * as NFS from "node:fs";
9
9
  import fs, { mkdtempSync, rmSync } from "node:fs";
10
10
  import process$1 from "node:process";
11
11
  import * as ts from "typescript";
12
- import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
12
+ import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
13
13
  import * as OS from "node:os";
14
14
  import os, { tmpdir } from "node:os";
15
15
  import { parseJSON5 } from "confbox";
@@ -18,7 +18,6 @@ import { fileURLToPath, pathToFileURL } from "node:url";
18
18
  import { createJiti } from "jiti";
19
19
  import * as Crypto from "node:crypto";
20
20
  import crypto, { createHash, randomUUID } from "node:crypto";
21
- import { ANALYZED_MANIFEST_FILENAMES, DEFAULT_EXTENSIONS } from "deslop-js/analyzed-inputs";
22
21
  import { gzipSync } from "node:zlib";
23
22
  import * as Sentry from "@sentry/node";
24
23
  import { performance as performance$1 } from "node:perf_hooks";
@@ -36129,15 +36128,11 @@ const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
36129
36128
  const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
36130
36129
  const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
36131
36130
  const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
36132
- const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
36133
- const CORE_PACKAGE_VERSION = "0.6.3";
36134
36131
  const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
36135
- const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
36136
36132
  const SUPPLY_CHAIN_PLUGIN = "socket";
36137
36133
  const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
36138
36134
  const SUPPLY_CHAIN_CATEGORY = "Security";
36139
36135
  const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
36140
- const SUPPLY_CHAIN_CACHE_TTL_MS = 864e5;
36141
36136
  const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
36142
36137
  const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
36143
36138
  const hasIgnoredPathSegment = (relativePath) => relativePath.split(/[/\\]/).slice(0, -1).some((segment) => IGNORED_DIRECTORIES.has(segment));
@@ -38819,29 +38814,6 @@ var PerFileLintCacheEnabled = class extends Reference("react-doctor/PerFileLintC
38819
38814
  return true;
38820
38815
  } }) {};
38821
38816
  /**
38822
- * Whether the sidecar lint cache (`runners/oxlint/sidecar-lint-cache.ts`) is
38823
- * active. Defaults ON — warm rescans replay the cross-file rules' cached
38824
- * diagnostics for every file whose dependency fingerprint (the probe set the
38825
- * plugin's collectors recorded) still matches the tree, and re-lint only the
38826
- * rest. Only reachable when the per-file lint cache itself is on (the
38827
- * sidecar covers its cache hits), so `REACT_DOCTOR_NO_CACHE` /
38828
- * `REACT_DOCTOR_NO_FILE_CACHE` implicitly disable it too. Opt-OUT knobs:
38829
- *
38830
- * - `REACT_DOCTOR_NO_CACHE` — the global off-switch.
38831
- * - `REACT_DOCTOR_NO_SIDECAR_CACHE` — granular rollback hatch: keep the
38832
- * per-file cache but run the always-fresh sidecar over every hit
38833
- * (the pre-cache behavior).
38834
- *
38835
- * Tests override via `Layer.succeed(SidecarLintCacheEnabled, false)`.
38836
- */
38837
- var SidecarLintCacheEnabled = class extends Reference("react-doctor/SidecarLintCacheEnabled", { defaultValue: () => {
38838
- const noCache = process.env["REACT_DOCTOR_NO_CACHE"]?.toLowerCase() ?? "";
38839
- const noSidecarCache = process.env["REACT_DOCTOR_NO_SIDECAR_CACHE"]?.toLowerCase() ?? "";
38840
- if (CACHE_DISABLED_VALUES$1.has(noCache)) return false;
38841
- if (CACHE_DISABLED_VALUES$1.has(noSidecarCache)) return false;
38842
- return true;
38843
- } }) {};
38844
- /**
38845
38817
  * Whether the whole-project dead-code result cache
38846
38818
  * (`dead-code/dead-code-result-cache.ts`) is active. Defaults ON — a rescan
38847
38819
  * whose inputs (source tree, manifests, configs, analyzer version) are
@@ -40609,37 +40581,51 @@ const failOpenReadJson = (filePath, fallback) => {
40609
40581
  return fallback;
40610
40582
  }
40611
40583
  };
40612
- const hashFileContents = (filePath) => {
40613
- try {
40614
- return crypto.createHash("sha1").update(NFS.readFileSync(filePath)).digest("hex");
40615
- } catch {
40616
- return null;
40617
- }
40618
- };
40619
- const ANALYZED_FILE_EXTENSIONS = new Set(DEFAULT_EXTENSIONS);
40620
- const CORE_SIDE_MANIFEST_NAMES = ["knip.json", "deno.lock"];
40621
- const ANALYZED_MANIFEST_NAMES = new Set([...ANALYZED_MANIFEST_FILENAMES, ...CORE_SIDE_MANIFEST_NAMES]);
40584
+ const ANALYZED_FILE_EXTENSIONS = new Set([
40585
+ ".ts",
40586
+ ".tsx",
40587
+ ".js",
40588
+ ".jsx",
40589
+ ".mts",
40590
+ ".mjs",
40591
+ ".cts",
40592
+ ".cjs",
40593
+ ".mdx",
40594
+ ".astro",
40595
+ ".graphql",
40596
+ ".gql",
40597
+ ".css",
40598
+ ".scss",
40599
+ ".vue",
40600
+ ".svelte"
40601
+ ]);
40602
+ const ANALYZED_MANIFEST_NAMES = new Set([
40603
+ "package.json",
40604
+ "knip.json",
40605
+ "app.json",
40606
+ "pnpm-workspace.yaml",
40607
+ "lerna.json",
40608
+ "pnpm-lock.yaml",
40609
+ "package-lock.json",
40610
+ "yarn.lock",
40611
+ "bun.lock",
40612
+ "bun.lockb",
40613
+ "deno.lock",
40614
+ ".gitignore"
40615
+ ]);
40622
40616
  const isTsConfigLikeFile = (fileName) => (fileName.startsWith("tsconfig") || fileName.startsWith("jsconfig")) && fileName.endsWith(".json");
40623
40617
  const isFingerprintedFile = (fileName) => ANALYZED_FILE_EXTENSIONS.has(Path.extname(fileName).toLowerCase()) || ANALYZED_MANIFEST_NAMES.has(fileName) || isTsConfigLikeFile(fileName);
40624
- /**
40625
- * Stat snapshot of every file the analysis reads, keyed by root-relative
40626
- * `/`-separated path. Taken BEFORE the (long) analysis so a stored result is
40627
- * verified against the tree it started from.
40628
- */
40629
- const collectAnalyzedFileStats = (rootDirectory) => {
40630
- const statByRelativePath = /* @__PURE__ */ new Map();
40618
+ const collectAnalyzedFileFingerprints = (rootDirectory) => {
40619
+ const fingerprints = [];
40631
40620
  for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
40632
40621
  if (!isFingerprintedFile(name)) continue;
40633
40622
  try {
40634
40623
  const fileStat = NFS.statSync(absolutePath);
40635
40624
  const relativePath = Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/");
40636
- statByRelativePath.set(relativePath, {
40637
- mtimeMs: fileStat.mtimeMs,
40638
- size: fileStat.size
40639
- });
40625
+ fingerprints.push(`${relativePath}:${fileStat.mtimeMs}:${fileStat.size}`);
40640
40626
  } catch {}
40641
40627
  }
40642
- return statByRelativePath;
40628
+ return fingerprints.sort();
40643
40629
  };
40644
40630
  const bundledRequire$2 = createRequire(import.meta.url);
40645
40631
  const resolveDeslopVersion = () => {
@@ -40651,85 +40637,33 @@ const resolveDeslopVersion = () => {
40651
40637
  }
40652
40638
  };
40653
40639
  const computeDeadCodeCacheKey = (input) => crypto.createHash("sha1").update(JSON.stringify({
40654
- schemaVersion: 2,
40655
- coreVersion: input.coreVersion,
40640
+ schemaVersion: 1,
40656
40641
  deslopVersion: resolveDeslopVersion(),
40657
40642
  deslopJsModuleSpecifier: input.deslopJsModuleSpecifier,
40658
40643
  entryPatterns: input.entryPatterns,
40659
40644
  ignorePatterns: input.ignorePatterns,
40660
- tsConfigFile: input.tsConfigPath === void 0 ? null : Path.relative(input.rootDirectory, input.tsConfigPath).replace(/\\/g, "/")
40645
+ tsConfigFile: input.tsConfigPath === void 0 ? null : Path.relative(input.rootDirectory, input.tsConfigPath).replace(/\\/g, "/"),
40646
+ files: collectAnalyzedFileFingerprints(input.rootDirectory)
40661
40647
  })).digest("hex");
40662
- const validateDiagnostic$2 = decodeUnknownSync(Diagnostic);
40648
+ const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
40663
40649
  const decodeCachedDiagnostics = (raw) => {
40664
40650
  try {
40665
- for (const entry of raw) validateDiagnostic$2(entry);
40651
+ for (const entry of raw) validateDiagnostic$1(entry);
40666
40652
  return raw;
40667
40653
  } catch {
40668
40654
  return null;
40669
40655
  }
40670
40656
  };
40671
- const isPersistedFileIdentity = (value) => Array.isArray(value) && value.length === 3 && typeof value[0] === "number" && typeof value[1] === "number" && typeof value[2] === "string";
40672
- const lookupDeadCodeResultCache = (input) => {
40673
- const cacheFilePath = Path.join(input.cacheDirectory, DEAD_CODE_CACHE_FILENAME);
40674
- const persisted = failOpenReadJson(cacheFilePath, null);
40675
- if (persisted === null || !isRecord$1(persisted) || persisted.version !== 2 || persisted.key !== input.cacheKey || !isRecord$1(persisted.files) || !Array.isArray(persisted.diagnostics)) return null;
40676
- const storedFileEntries = Object.entries(persisted.files);
40677
- if (storedFileEntries.length !== input.currentFileStats.size) return null;
40678
- const repairedFiles = {};
40679
- let repairedCount = 0;
40680
- for (const [relativePath, storedIdentity] of storedFileEntries) {
40681
- if (!isPersistedFileIdentity(storedIdentity)) return null;
40682
- const currentStat = input.currentFileStats.get(relativePath);
40683
- if (currentStat === void 0) return null;
40684
- const [storedMtimeMs, storedSize, storedContentHash] = storedIdentity;
40685
- if (currentStat.mtimeMs === storedMtimeMs && currentStat.size === storedSize) {
40686
- repairedFiles[relativePath] = storedIdentity;
40687
- continue;
40688
- }
40689
- if (currentStat.size !== storedSize) return null;
40690
- const currentContentHash = hashFileContents(Path.join(input.rootDirectory, relativePath));
40691
- if (currentContentHash === null || currentContentHash !== storedContentHash) return null;
40692
- repairedFiles[relativePath] = [
40693
- currentStat.mtimeMs,
40694
- currentStat.size,
40695
- storedContentHash
40696
- ];
40697
- repairedCount += 1;
40698
- }
40699
- const diagnostics = decodeCachedDiagnostics(persisted.diagnostics);
40700
- if (diagnostics === null) return null;
40701
- if (repairedCount > 0) atomicWriteJson(cacheFilePath, {
40702
- version: 2,
40703
- key: input.cacheKey,
40704
- files: repairedFiles,
40705
- diagnostics: persisted.diagnostics
40706
- });
40707
- return diagnostics;
40657
+ const lookupDeadCodeResultCache = (cacheDirectory, cacheKey) => {
40658
+ const persisted = failOpenReadJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), null);
40659
+ if (persisted === null || !isRecord$1(persisted) || persisted.version !== 1 || persisted.key !== cacheKey || !Array.isArray(persisted.diagnostics)) return null;
40660
+ return decodeCachedDiagnostics(persisted.diagnostics);
40708
40661
  };
40709
- const storeDeadCodeResultCache = (input) => {
40710
- const persistedFiles = {};
40711
- for (const [relativePath, snapshotStat] of input.snapshotFileStats) {
40712
- const absolutePath = Path.join(input.rootDirectory, relativePath);
40713
- const contentHash = hashFileContents(absolutePath);
40714
- if (contentHash === null) return;
40715
- let currentStat;
40716
- try {
40717
- currentStat = NFS.statSync(absolutePath);
40718
- } catch {
40719
- return;
40720
- }
40721
- if (currentStat.mtimeMs !== snapshotStat.mtimeMs || currentStat.size !== snapshotStat.size) return;
40722
- persistedFiles[relativePath] = [
40723
- snapshotStat.mtimeMs,
40724
- snapshotStat.size,
40725
- contentHash
40726
- ];
40727
- }
40728
- atomicWriteJson(Path.join(input.cacheDirectory, DEAD_CODE_CACHE_FILENAME), {
40729
- version: 2,
40730
- key: input.cacheKey,
40731
- files: persistedFiles,
40732
- diagnostics: input.diagnostics
40662
+ const storeDeadCodeResultCache = (cacheDirectory, cacheKey, diagnostics) => {
40663
+ atomicWriteJson(Path.join(cacheDirectory, DEAD_CODE_CACHE_FILENAME), {
40664
+ version: 1,
40665
+ key: cacheKey,
40666
+ diagnostics
40733
40667
  });
40734
40668
  };
40735
40669
  const readSystemFacts = () => ({
@@ -40868,14 +40802,6 @@ process.stdin.on("end", () => {
40868
40802
  circularDependencies: result.circularDependencies.map((cycle) => ({
40869
40803
  files: cycle.files,
40870
40804
  })),
40871
- ...(result.incrementalCacheStats
40872
- ? {
40873
- summaryCacheStats: {
40874
- hits: result.incrementalCacheStats.summaryHits,
40875
- misses: result.incrementalCacheStats.summaryMisses,
40876
- },
40877
- }
40878
- : {}),
40879
40805
  });
40880
40806
 
40881
40807
  const serializeError = (error) =>
@@ -40899,9 +40825,6 @@ process.stdin.on("end", () => {
40899
40825
  ...(workerInput.ignorePatterns.length > 0
40900
40826
  ? { ignorePatterns: workerInput.ignorePatterns }
40901
40827
  : {}),
40902
- ...(workerInput.incrementalCachePath
40903
- ? { incrementalCachePath: workerInput.incrementalCachePath }
40904
- : {}),
40905
40828
  // We consume only deslop's GRAPH-based findings (unusedFiles, unusedExports,
40906
40829
  // unusedDependencies, circularDependencies). Everything else deslop can compute
40907
40830
  // is pure wasted work for us, and it's the bulk of the runtime:
@@ -40912,18 +40835,12 @@ process.stdin.on("end", () => {
40912
40835
  // are the single most expensive pass — duplicate-block detection alone was
40913
40836
  // ~83s of a ~130s Sentry scan — so skipping them is an ~8.5x dead-code
40914
40837
  // speedup on a large repo.
40915
- // - reportRedundancy: the DRY-pattern detectors (duplicate types/constants,
40916
- // simplifiable functions, identity wrappers, …) — ~120 ms of discarded
40917
- // output per scan, and skipping them lets the incremental cache drop the
40918
- // DRY-pattern summary fields (the largest slice of the cache file).
40919
- // All are provably safe: the consumed graph findings are computed by their own
40838
+ // Both are provably safe: the consumed graph findings are computed by their own
40920
40839
  // detectors, independent of these passes (confirmed byte-identical on
40921
- // excalidraw + mui-material + sentry; re-verified on sentry after the
40922
- // reportRedundancy flip). tsConfigPath stays — the module resolver needs it
40923
- // for path-alias resolution in the import graph.
40840
+ // excalidraw + mui-material + sentry). tsConfigPath stays — the module resolver
40841
+ // needs it for path-alias resolution in the import graph.
40924
40842
  semantic: { enabled: false },
40925
40843
  reportCodeQuality: false,
40926
- reportRedundancy: false,
40927
40844
  };
40928
40845
  const result = await analyze(defineConfig(config));
40929
40846
  emit({ ok: true, result: normalizeResult(result) });
@@ -41007,23 +40924,13 @@ const parseCircularDependencies = (value) => {
41007
40924
  }
41008
40925
  return circularDependencies;
41009
40926
  };
41010
- const parseSummaryCacheStats = (value) => {
41011
- if (!isRecord$1(value)) return void 0;
41012
- if (typeof value.hits !== "number" || typeof value.misses !== "number") return void 0;
41013
- return {
41014
- hits: value.hits,
41015
- misses: value.misses
41016
- };
41017
- };
41018
40927
  const parseDeadCodeWorkerResult = (value) => {
41019
40928
  if (!isRecord$1(value)) throw new Error("Dead-code worker returned an invalid result.");
41020
- const summaryCacheStats = parseSummaryCacheStats(value.summaryCacheStats);
41021
40929
  return {
41022
40930
  unusedFiles: parseUnusedFiles(value.unusedFiles),
41023
40931
  unusedExports: parseUnusedExports(value.unusedExports),
41024
40932
  unusedDependencies: parseUnusedDependencies(value.unusedDependencies),
41025
- circularDependencies: parseCircularDependencies(value.circularDependencies),
41026
- ...summaryCacheStats ? { summaryCacheStats } : {}
40933
+ circularDependencies: parseCircularDependencies(value.circularDependencies)
41027
40934
  };
41028
40935
  };
41029
40936
  const parseDeadCodeWorkerError = (value) => {
@@ -41139,26 +41046,18 @@ const checkDeadCode = async (options) => {
41139
41046
  const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
41140
41047
  const tsConfigPath = resolveTsConfigPath(rootDirectory);
41141
41048
  const deslopJsModuleSpecifier = options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js");
41142
- const fileStatsSnapshot = options.cacheEnabled ? collectAnalyzedFileStats(rootDirectory) : null;
41143
- const cacheKey = fileStatsSnapshot === null ? null : computeDeadCodeCacheKey({
41049
+ const cacheKey = options.cacheEnabled ? computeDeadCodeCacheKey({
41144
41050
  rootDirectory,
41145
41051
  entryPatterns,
41146
41052
  ignorePatterns,
41147
41053
  tsConfigPath,
41148
- deslopJsModuleSpecifier,
41149
- coreVersion: CORE_PACKAGE_VERSION
41150
- });
41151
- if (cacheKey !== null && fileStatsSnapshot !== null) {
41152
- const cachedDiagnostics = lookupDeadCodeResultCache({
41153
- cacheDirectory: resolveReactDoctorCacheDir(rootDirectory),
41154
- cacheKey,
41155
- rootDirectory,
41156
- currentFileStats: fileStatsSnapshot
41157
- });
41054
+ deslopJsModuleSpecifier
41055
+ }) : null;
41056
+ if (cacheKey !== null) {
41057
+ const cachedDiagnostics = lookupDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey);
41158
41058
  options.onCacheOutcome?.(cachedDiagnostics !== null);
41159
41059
  if (cachedDiagnostics !== null) return [...cachedDiagnostics];
41160
41060
  }
41161
- const incrementalCachePath = options.cacheEnabled === true ? Path.join(resolveReactDoctorCacheDir(rootDirectory), DEAD_CODE_SUMMARY_CACHE_FILENAME) : void 0;
41162
41061
  const spawnAndRun = () => {
41163
41062
  return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
41164
41063
  rootDirectory,
@@ -41166,12 +41065,10 @@ const checkDeadCode = async (options) => {
41166
41065
  tsConfigPath,
41167
41066
  ignorePatterns,
41168
41067
  deslopJsModuleSpecifier,
41169
- parseConcurrency: options.parseConcurrency,
41170
- incrementalCachePath
41068
+ parseConcurrency: options.parseConcurrency
41171
41069
  }), options.workerTimeoutMs ?? 12e4, options.abortSignal);
41172
41070
  };
41173
41071
  const result = parseDeadCodeWorkerResult(options.createWorker === void 0 ? await withDeadCodeWorkerSlot(spawnAndRun, options.abortSignal) : await spawnAndRun());
41174
- if (result.summaryCacheStats !== void 0) options.onSummaryCacheStats?.(result.summaryCacheStats);
41175
41072
  const toRelative = (filePath) => toRelativeFilePath(rootDirectory, filePath);
41176
41073
  const diagnostics = [];
41177
41074
  for (const unusedFile of result.unusedFiles) diagnostics.push({
@@ -41228,13 +41125,7 @@ const checkDeadCode = async (options) => {
41228
41125
  category: DEAD_CODE_CATEGORY
41229
41126
  });
41230
41127
  }
41231
- if (cacheKey !== null && fileStatsSnapshot !== null) storeDeadCodeResultCache({
41232
- cacheDirectory: resolveReactDoctorCacheDir(rootDirectory),
41233
- cacheKey,
41234
- rootDirectory,
41235
- snapshotFileStats: fileStatsSnapshot,
41236
- diagnostics
41237
- });
41128
+ if (cacheKey !== null) storeDeadCodeResultCache(resolveReactDoctorCacheDir(rootDirectory), cacheKey, diagnostics);
41238
41129
  return diagnostics;
41239
41130
  };
41240
41131
  const DEAD_CODE_RULE_KEY_PREFIX = `${DEAD_CODE_PLUGIN}/`;
@@ -41345,8 +41236,7 @@ var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
41345
41236
  workerTimeoutMs: input.workerTimeoutMs,
41346
41237
  abortSignal: signal,
41347
41238
  cacheEnabled,
41348
- onCacheOutcome: input.onCacheOutcome,
41349
- onSummaryCacheStats: input.onSummaryCacheStats
41239
+ onCacheOutcome: input.onCacheOutcome
41350
41240
  }),
41351
41241
  catch: (cause) => new ReactDoctorError({ reason: new DeadCodeAnalysisFailed({ cause }) })
41352
41242
  }).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)));
@@ -42216,7 +42106,7 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
42216
42106
  }
42217
42107
  return enabled;
42218
42108
  };
42219
- const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection, sidecarRuleIdFilter }) => {
42109
+ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection }) => {
42220
42110
  const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
42221
42111
  const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
42222
42112
  const jsPlugins = [];
@@ -42228,7 +42118,6 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
42228
42118
  if (!rule) continue;
42229
42119
  if (ruleSelection === "cacheable" && CROSS_FILE_RULE_IDS.has(registryEntry.id)) continue;
42230
42120
  if (ruleSelection === "sidecar" && !CROSS_FILE_RULE_IDS.has(registryEntry.id)) continue;
42231
- if (ruleSelection === "sidecar" && sidecarRuleIdFilter !== void 0 && !sidecarRuleIdFilter.has(registryEntry.id)) continue;
42232
42121
  if (rule.scan !== void 0) continue;
42233
42122
  if (customRulesOnly && registryEntry.originallyExternal) continue;
42234
42123
  if (rule.framework !== "global" && !rule.requires) continue;
@@ -42273,23 +42162,23 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
42273
42162
  }
42274
42163
  };
42275
42164
  };
42276
- const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
42165
+ const validateDiagnostic = decodeUnknownSync(Diagnostic);
42277
42166
  const decodeFileDiagnostics = (raw) => {
42278
42167
  if (!Array.isArray(raw)) return null;
42279
42168
  try {
42280
- for (const entry of raw) validateDiagnostic$1(entry);
42169
+ for (const entry of raw) validateDiagnostic(entry);
42281
42170
  return raw;
42282
42171
  } catch {
42283
42172
  return null;
42284
42173
  }
42285
42174
  };
42286
- const emptyCache$1 = () => ({
42175
+ const emptyCache = () => ({
42287
42176
  version: 1,
42288
42177
  rulesets: {}
42289
42178
  });
42290
- const loadRulesetEntries$1 = (cacheFilePath, rulesetHash) => {
42179
+ const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
42291
42180
  const entries = /* @__PURE__ */ new Map();
42292
- const persisted = failOpenReadJson(cacheFilePath, emptyCache$1());
42181
+ const persisted = failOpenReadJson(cacheFilePath, emptyCache());
42293
42182
  if (persisted.version !== 1 || !isRecord$1(persisted.rulesets)) return entries;
42294
42183
  const bucket = persisted.rulesets[rulesetHash];
42295
42184
  if (!isRecord$1(bucket) || !isRecord$1(bucket.files)) return entries;
@@ -42301,149 +42190,20 @@ const loadRulesetEntries$1 = (cacheFilePath, rulesetHash) => {
42301
42190
  };
42302
42191
  const createFileLintCache = (cacheDirectory, rulesetHash) => {
42303
42192
  const cacheFilePath = Path.join(cacheDirectory, FILE_LINT_CACHE_FILENAME);
42304
- const entries = loadRulesetEntries$1(cacheFilePath, rulesetHash);
42193
+ const entries = loadRulesetEntries(cacheFilePath, rulesetHash);
42305
42194
  return {
42306
42195
  lookup: (fileKey) => entries.get(fileKey) ?? null,
42307
42196
  store: (fileKey, diagnostics) => {
42308
42197
  entries.delete(fileKey);
42309
42198
  entries.set(fileKey, diagnostics);
42310
42199
  },
42311
- persist: () => {
42312
- const onDisk = failOpenReadJson(cacheFilePath, emptyCache$1());
42313
- const rulesets = onDisk.version === 1 && isRecord$1(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
42314
- const existingBucket = rulesets[rulesetHash];
42315
- const existingFiles = isRecord$1(existingBucket) && isRecord$1(existingBucket.files) ? existingBucket.files : {};
42316
- const ourFiles = {};
42317
- for (const [fileKey, diagnostics] of entries) ourFiles[fileKey] = diagnostics;
42318
- const cappedEntries = Object.entries({
42319
- ...existingFiles,
42320
- ...ourFiles
42321
- }).slice(-FILE_LINT_CACHE_MAX_FILE_COUNT);
42322
- rulesets[rulesetHash] = {
42323
- updatedAtMs: Date.now(),
42324
- files: Object.fromEntries(cappedEntries)
42325
- };
42326
- atomicWriteJson(cacheFilePath, {
42327
- version: 1,
42328
- rulesets: Object.fromEntries(Object.entries(rulesets).filter(([, bucket]) => isRecord$1(bucket) && typeof bucket.updatedAtMs === "number").sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8))
42329
- });
42330
- }
42331
- };
42332
- };
42333
- const ABSENT_CONTENT_ANSWER = "absent";
42334
- /**
42335
- * Answers dependency probes for the sidecar lint cache — used both when
42336
- * STORING an entry (turn the collector's probe paths into answers) and when
42337
- * VERIFYING one (re-answer each stored probe against the current tree).
42338
- * Store and verify must agree exactly, so both go through this one resolver.
42339
- *
42340
- * - `content` — SHA-1 of the file's bytes, `"absent"` when unreadable
42341
- * (missing, a directory, permission failure). Source files reuse the
42342
- * hashes the cache-partition loop already computed
42343
- * (`contentHashByRelativePath`); everything else (package.json,
42344
- * tsconfig, ignored files) is hashed on demand.
42345
- * - `exists` — the existence class a module-resolution candidate probe
42346
- * sees: `"file"`, `"dir"`, or `"none"` (missing or an exotic entry —
42347
- * `statSync` follows symlinks, matching the resolvers).
42348
- *
42349
- * Answers are memoized per scan: the filesystem is frozen for the run, and
42350
- * hot dependency paths (a barrel imported by hundreds of files, the shared
42351
- * tsconfig chain) repeat across many files' probe sets.
42352
- */
42353
- const createSidecarProbeAnswerResolver = (input) => {
42354
- const contentAnswerByPath = /* @__PURE__ */ new Map();
42355
- const existsAnswerByPath = /* @__PURE__ */ new Map();
42356
- const toRelativePath = (absolutePath) => Path.relative(input.rootDirectory, absolutePath).replaceAll("\\", "/");
42357
- const contentAnswer = (relativePath) => {
42358
- const memoized = contentAnswerByPath.get(relativePath);
42359
- if (memoized !== void 0) return memoized;
42360
- const answer = input.contentHashByRelativePath.get(relativePath) ?? hashFileContents(Path.resolve(input.rootDirectory, relativePath)) ?? ABSENT_CONTENT_ANSWER;
42361
- contentAnswerByPath.set(relativePath, answer);
42362
- return answer;
42363
- };
42364
- const existsAnswer = (relativePath) => {
42365
- const memoized = existsAnswerByPath.get(relativePath);
42366
- if (memoized !== void 0) return memoized;
42367
- let answer;
42368
- try {
42369
- const stat = NFS.statSync(Path.resolve(input.rootDirectory, relativePath));
42370
- answer = stat.isFile() ? "file" : stat.isDirectory() ? "dir" : "none";
42371
- } catch {
42372
- answer = "none";
42373
- }
42374
- existsAnswerByPath.set(relativePath, answer);
42375
- return answer;
42376
- };
42377
- return {
42378
- answerFor: (kind, relativePath) => kind === "content" ? contentAnswer(relativePath) : existsAnswer(relativePath),
42379
- toRelativePath
42380
- };
42381
- };
42382
- const validateDiagnostic = decodeUnknownSync(Diagnostic);
42383
- const isProbeKind = (value) => value === "content" || value === "exists";
42384
- const decodeSidecarFileEntry = (raw) => {
42385
- if (!isRecord$1(raw) || !Array.isArray(raw.probes) || !Array.isArray(raw.diagnostics)) return null;
42386
- const probes = [];
42387
- for (const rawProbe of raw.probes) {
42388
- if (!Array.isArray(rawProbe) || rawProbe.length !== 3) return null;
42389
- const [kind, probePath, answer] = rawProbe;
42390
- if (!isProbeKind(kind) || typeof probePath !== "string" || typeof answer !== "string") return null;
42391
- probes.push({
42392
- kind,
42393
- path: probePath,
42394
- answer
42395
- });
42396
- }
42397
- try {
42398
- for (const entry of raw.diagnostics) validateDiagnostic(entry);
42399
- } catch {
42400
- return null;
42401
- }
42402
- return {
42403
- probes,
42404
- diagnostics: raw.diagnostics
42405
- };
42406
- };
42407
- const encodeSidecarFileEntry = (entry) => ({
42408
- probes: entry.probes.map((probe) => [
42409
- probe.kind,
42410
- probe.path,
42411
- probe.answer
42412
- ]),
42413
- diagnostics: entry.diagnostics
42414
- });
42415
- const emptyCache = () => ({
42416
- version: 1,
42417
- rulesets: {}
42418
- });
42419
- const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
42420
- const entries = /* @__PURE__ */ new Map();
42421
- const persisted = failOpenReadJson(cacheFilePath, emptyCache());
42422
- if (persisted.version !== 1 || !isRecord$1(persisted.rulesets)) return entries;
42423
- const bucket = persisted.rulesets[rulesetHash];
42424
- if (!isRecord$1(bucket) || !isRecord$1(bucket.files)) return entries;
42425
- for (const [fileKey, rawEntry] of Object.entries(bucket.files)) {
42426
- const decoded = decodeSidecarFileEntry(rawEntry);
42427
- if (decoded !== null) entries.set(fileKey, decoded);
42428
- }
42429
- return entries;
42430
- };
42431
- const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
42432
- const cacheFilePath = Path.join(cacheDirectory, SIDECAR_LINT_CACHE_FILENAME);
42433
- const entries = loadRulesetEntries(cacheFilePath, rulesetHash);
42434
- return {
42435
- lookup: (fileKey) => entries.get(fileKey) ?? null,
42436
- store: (fileKey, entry) => {
42437
- entries.delete(fileKey);
42438
- entries.set(fileKey, entry);
42439
- },
42440
42200
  persist: () => {
42441
42201
  const onDisk = failOpenReadJson(cacheFilePath, emptyCache());
42442
42202
  const rulesets = onDisk.version === 1 && isRecord$1(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
42443
42203
  const existingBucket = rulesets[rulesetHash];
42444
42204
  const existingFiles = isRecord$1(existingBucket) && isRecord$1(existingBucket.files) ? existingBucket.files : {};
42445
42205
  const ourFiles = {};
42446
- for (const [fileKey, entry] of entries) ourFiles[fileKey] = encodeSidecarFileEntry(entry);
42206
+ for (const [fileKey, diagnostics] of entries) ourFiles[fileKey] = diagnostics;
42447
42207
  const cappedEntries = Object.entries({
42448
42208
  ...existingFiles,
42449
42209
  ...ourFiles
@@ -43458,6 +43218,13 @@ const validateRuleRegistration = () => {
43458
43218
  ].filter((entry) => entry !== null).join("; ");
43459
43219
  console.warn(`[react-doctor] rule-registration drift: ${detail}`);
43460
43220
  };
43221
+ const hashFileContents = (filePath) => {
43222
+ try {
43223
+ return crypto.createHash("sha1").update(NFS.readFileSync(filePath)).digest("hex");
43224
+ } catch {
43225
+ return null;
43226
+ }
43227
+ };
43461
43228
  /**
43462
43229
  * Balanced LPT (longest-processing-time-first) lint batch planner. Where
43463
43230
  * `batchIncludePaths` greedily fills 100-file batches and leaves a small
@@ -43539,106 +43306,6 @@ const writeOxlintConfig = (configPath, configToWrite) => {
43539
43306
  NFS.closeSync(fileHandle);
43540
43307
  }
43541
43308
  };
43542
- /**
43543
- * Attributes diagnostics back to the file that produced them by the
43544
- * normalized path oxlint echoes. Returns `null` when ANY diagnostic can't be
43545
- * attributed — the path forms don't align, so the caller must skip its cache
43546
- * store rather than risk caching a wrong empty result for a file that
43547
- * actually had diagnostics.
43548
- */
43549
- const attributeDiagnosticsToFiles = (diagnostics, files) => {
43550
- const fileByNormalizedPath = /* @__PURE__ */ new Map();
43551
- for (const file of files) fileByNormalizedPath.set(file.replaceAll("\\", "/"), file);
43552
- const diagnosticsByFile = /* @__PURE__ */ new Map();
43553
- for (const diagnostic of diagnostics) {
43554
- const file = fileByNormalizedPath.get(diagnostic.filePath);
43555
- if (file === void 0) return null;
43556
- const fileDiagnostics = diagnosticsByFile.get(file) ?? [];
43557
- fileDiagnostics.push(diagnostic);
43558
- diagnosticsByFile.set(file, fileDiagnostics);
43559
- }
43560
- return diagnosticsByFile;
43561
- };
43562
- /**
43563
- * Collects each file's cross-file dependency probes (via the plugin's
43564
- * collectors) with their current answers. Fail-open per file: an unreadable /
43565
- * unparseable / collector-failing file maps to `null` (no entry stored — it
43566
- * re-lints next scan). The filesystem is frozen for the scan, so this can run
43567
- * CONCURRENTLY with an oxlint child-process await — the caller starts it
43568
- * unawaited before a lint pass and awaits it at store time, hiding the
43569
- * in-process parse + resolution cost under the subprocess wait. The loop
43570
- * yields on the cooperative time budget so the child's stdout handling (and
43571
- * sibling fibers) keep flowing; each per-file collection is synchronous, so
43572
- * the module-level probe recorder never sees interleaved use. Never rejects.
43573
- */
43574
- const collectSidecarProbesForFiles = async (input) => {
43575
- const buildProbes = (file) => {
43576
- const absoluteFilePath = Path.resolve(input.rootDirectory, file);
43577
- let trace;
43578
- try {
43579
- trace = collectCrossFileDependencyProbes({
43580
- absoluteFilePath,
43581
- sourceText: NFS.readFileSync(absoluteFilePath, "utf8"),
43582
- ruleIds: input.boundedSidecarRuleIds
43583
- });
43584
- } catch {
43585
- return null;
43586
- }
43587
- if (trace === null) return null;
43588
- const probes = [];
43589
- for (const contentPath of trace.contentPaths) {
43590
- const relativePath = input.probeAnswers.toRelativePath(contentPath);
43591
- probes.push({
43592
- kind: "content",
43593
- path: relativePath,
43594
- answer: input.probeAnswers.answerFor("content", relativePath)
43595
- });
43596
- }
43597
- for (const existencePath of trace.existencePaths) {
43598
- const relativePath = input.probeAnswers.toRelativePath(existencePath);
43599
- probes.push({
43600
- kind: "exists",
43601
- path: relativePath,
43602
- answer: input.probeAnswers.answerFor("exists", relativePath)
43603
- });
43604
- }
43605
- return probes;
43606
- };
43607
- const probesByFile = /* @__PURE__ */ new Map();
43608
- let collectSliceStartedAt = performance.now();
43609
- for (const file of input.files) {
43610
- probesByFile.set(file, buildProbes(file));
43611
- if (performance.now() - collectSliceStartedAt >= 12) {
43612
- await yieldToEventLoop();
43613
- collectSliceStartedAt = performance.now();
43614
- }
43615
- }
43616
- return probesByFile;
43617
- };
43618
- /**
43619
- * Stores each freshly-linted file's probe set + cross-file diagnostics so
43620
- * the next scan can replay instead of re-linting.
43621
- */
43622
- const storeSidecarEntries = (input) => {
43623
- let didStoreAnyEntry = false;
43624
- for (const pass of input.storablePasses) {
43625
- if (!pass.isTrusted) continue;
43626
- const diagnosticsByFile = attributeDiagnosticsToFiles(pass.diagnostics, pass.files);
43627
- if (diagnosticsByFile === null) continue;
43628
- for (const file of pass.files) {
43629
- const cacheKey = input.cacheKeyByFile.get(file);
43630
- if (cacheKey === void 0) continue;
43631
- const probes = pass.probesByFile.get(file);
43632
- if (probes === void 0 || probes === null) continue;
43633
- input.sidecarCache.store(cacheKey, {
43634
- probes,
43635
- diagnostics: diagnosticsByFile.get(file) ?? []
43636
- });
43637
- didStoreAnyEntry = true;
43638
- }
43639
- }
43640
- if (didStoreAnyEntry) input.sidecarCache.persist();
43641
- };
43642
43309
  const REACT_HOOKS_JS_DROP_PREFIX = "React Compiler rules (react-hooks-js/*) skipped — eslint-plugin-react-hooks failed to load in this environment";
43643
43310
  /**
43644
43311
  * Detects an oxlint config-load crash caused by the optional
@@ -43680,7 +43347,7 @@ const reactHooksJsPluginDropNote = (error) => {
43680
43347
  * 6. always restore disable directives + clean up the temp dir
43681
43348
  */
43682
43349
  const runOxlint = async (options) => {
43683
- const { rootDirectory, project, includePaths, nodeBinaryPath = process.execPath, customRulesOnly = false, respectInlineDisables = true, adoptExistingLintConfig = true, ignoredTags = /* @__PURE__ */ new Set(), userConfig, configSourceDirectory = rootDirectory, onPartialFailure, perFileLintCacheEnabled = false, sidecarLintCacheEnabled = false, onCacheStats, onSidecarStats, spawnTimeoutMs, outputMaxBytes, lintBatchOrdering = "cost" } = options;
43350
+ const { rootDirectory, project, includePaths, nodeBinaryPath = process.execPath, customRulesOnly = false, respectInlineDisables = true, adoptExistingLintConfig = true, ignoredTags = /* @__PURE__ */ new Set(), userConfig, configSourceDirectory = rootDirectory, onPartialFailure, perFileLintCacheEnabled = false, onCacheStats, spawnTimeoutMs, outputMaxBytes, lintBatchOrdering = "cost" } = options;
43684
43351
  const serverAuthFunctionNames = Array.isArray(userConfig?.serverAuthFunctionNames) ? userConfig.serverAuthFunctionNames.filter((entry) => typeof entry === "string" && entry.length > 0) : void 0;
43685
43352
  const severityControls = buildRuleSeverityControls(userConfig);
43686
43353
  validateRuleRegistration();
@@ -43698,8 +43365,7 @@ const runOxlint = async (options) => {
43698
43365
  severityControls,
43699
43366
  userPlugins,
43700
43367
  disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
43701
- ruleSelection: overrides.ruleSelection,
43702
- sidecarRuleIdFilter: overrides.sidecarRuleIdFilter
43368
+ ruleSelection: overrides.ruleSelection
43703
43369
  });
43704
43370
  const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
43705
43371
  const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
@@ -43802,18 +43468,15 @@ const runOxlint = async (options) => {
43802
43468
  });
43803
43469
  const cache = createFileLintCache(resolveReactDoctorCacheDir(rootDirectory), rulesetHash);
43804
43470
  const cacheKeyByFile = /* @__PURE__ */ new Map();
43805
- const contentHashByRelativePath = /* @__PURE__ */ new Map();
43806
43471
  const missFiles = [];
43807
43472
  const replayedDiagnostics = [];
43808
43473
  let hashSliceStartedAt = performance.now();
43809
43474
  for (const candidateFile of candidateFiles) {
43810
- const absoluteCandidatePath = Path.resolve(rootDirectory, candidateFile);
43811
- const contentHash = hashFileContents(absoluteCandidatePath);
43475
+ const contentHash = hashFileContents(Path.resolve(rootDirectory, candidateFile));
43812
43476
  if (contentHash === null) {
43813
43477
  missFiles.push(candidateFile);
43814
43478
  continue;
43815
43479
  }
43816
- contentHashByRelativePath.set(Path.relative(rootDirectory, absoluteCandidatePath).replaceAll("\\", "/"), contentHash);
43817
43480
  const cacheKey = `${candidateFile.replaceAll("\\", "/")}\u0000${contentHash}`;
43818
43481
  cacheKeyByFile.set(candidateFile, cacheKey);
43819
43482
  const cachedDiagnostics = cache.lookup(cacheKey);
@@ -43825,111 +43488,43 @@ const runOxlint = async (options) => {
43825
43488
  }
43826
43489
  }
43827
43490
  const cacheHitFileCount = candidateFiles.length - missFiles.length;
43828
- const enabledSidecarRuleIds = Object.keys(buildConfig({
43829
- extendsPaths: [],
43830
- ruleSelection: "sidecar"
43831
- }).rules).map((ruleKey) => ruleKey.slice(ruleKey.lastIndexOf("/") + 1)).filter((ruleId) => CROSS_FILE_RULE_IDS.has(ruleId));
43832
- const boundedSidecarRuleIds = enabledSidecarRuleIds.filter((ruleId) => CROSS_FILE_DEPENDENCY_COLLECTORS.has(ruleId));
43833
- const boundedSidecarRuleIdSet = new Set(boundedSidecarRuleIds);
43834
- const unboundedSidecarRuleIds = enabledSidecarRuleIds.filter((ruleId) => !CROSS_FILE_DEPENDENCY_COLLECTORS.has(ruleId));
43835
- const useSidecarCache = sidecarLintCacheEnabled && boundedSidecarRuleIds.length > 0;
43836
- const sidecarCache = useSidecarCache ? createSidecarLintCache(resolveReactDoctorCacheDir(rootDirectory), computeRulesetHash({
43837
- config: buildConfig({
43838
- extendsPaths: [],
43839
- ruleSelection: "sidecar",
43840
- sidecarRuleIdFilter: boundedSidecarRuleIdSet
43841
- }),
43842
- toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
43843
- ignorePatterns: combinedPatterns,
43844
- tsconfigContent
43845
- })) : null;
43846
- const probeAnswers = createSidecarProbeAnswerResolver({
43847
- rootDirectory,
43848
- contentHashByRelativePath
43849
- });
43850
- const missProbesTask = sidecarCache === null ? null : collectSidecarProbesForFiles({
43851
- files: missFiles,
43852
- rootDirectory,
43853
- boundedSidecarRuleIds,
43854
- probeAnswers
43855
- });
43856
43491
  const fullResult = await runConfigOverFiles((overrides) => buildConfig({
43857
43492
  extendsPaths: [],
43858
43493
  disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin
43859
43494
  }), "oxlintrc.full.json", missFiles, options.onFileProgress && ((scannedFileCount) => options.onFileProgress?.(scannedFileCount, candidateFiles.length)));
43860
43495
  const missFileSet = new Set(missFiles);
43861
- const hitFiles = candidateFiles.filter((candidateFile) => !missFileSet.has(candidateFile));
43862
- const sidecarReplayedDiagnostics = [];
43863
- const sidecarLintFiles = [];
43864
- if (sidecarCache === null) sidecarLintFiles.push(...hitFiles);
43865
- else {
43866
- let verifySliceStartedAt = performance.now();
43867
- for (const hitFile of hitFiles) {
43868
- const cacheKey = cacheKeyByFile.get(hitFile);
43869
- const entry = cacheKey === void 0 ? null : sidecarCache.lookup(cacheKey);
43870
- if (entry !== null && entry.probes.every((probe) => probeAnswers.answerFor(probe.kind, probe.path) === probe.answer)) sidecarReplayedDiagnostics.push(...entry.diagnostics);
43871
- else sidecarLintFiles.push(hitFile);
43872
- if (performance.now() - verifySliceStartedAt >= 12) {
43873
- await yieldToEventLoop();
43874
- verifySliceStartedAt = performance.now();
43875
- }
43876
- }
43877
- }
43878
- const sidecarProbesTask = sidecarCache === null ? null : collectSidecarProbesForFiles({
43879
- files: sidecarLintFiles,
43880
- rootDirectory,
43881
- boundedSidecarRuleIds,
43882
- probeAnswers
43883
- });
43884
- const sidecarReplayedFileCount = hitFiles.length - sidecarLintFiles.length;
43885
- if (sidecarReplayedFileCount > 0) options.onFileProgress?.(missFiles.length + sidecarReplayedFileCount, candidateFiles.length);
43886
- const boundedSidecarResult = await runConfigOverFiles(() => buildConfig({
43887
- extendsPaths: [],
43888
- ruleSelection: "sidecar",
43889
- ...useSidecarCache ? { sidecarRuleIdFilter: boundedSidecarRuleIdSet } : {}
43890
- }), "oxlintrc.sidecar.json", sidecarLintFiles, options.onFileProgress && ((scannedFileCount) => options.onFileProgress?.(missFiles.length + sidecarReplayedFileCount + scannedFileCount, candidateFiles.length)));
43891
- const unboundedSidecarResult = useSidecarCache && unboundedSidecarRuleIds.length > 0 ? await runConfigOverFiles(() => buildConfig({
43496
+ const sidecarResult = await runConfigOverFiles(() => buildConfig({
43892
43497
  extendsPaths: [],
43893
- ruleSelection: "sidecar",
43894
- sidecarRuleIdFilter: new Set(unboundedSidecarRuleIds)
43895
- }), "oxlintrc.sidecar-unbounded.json", hitFiles, void 0) : {
43896
- diagnostics: [],
43897
- didDropReactHooksJsPlugin: false,
43898
- hadPartialFailure: false
43899
- };
43498
+ ruleSelection: "sidecar"
43499
+ }), "oxlintrc.sidecar.json", candidateFiles.filter((candidateFile) => !missFileSet.has(candidateFile)), options.onFileProgress && ((scannedFileCount) => options.onFileProgress?.(missFiles.length + scannedFileCount, candidateFiles.length)));
43900
43500
  const freshCacheableDiagnostics = [];
43901
43501
  for (const diagnostic of fullResult.diagnostics) if (!CROSS_FILE_RULE_IDS.has(diagnostic.rule)) freshCacheableDiagnostics.push(diagnostic);
43902
43502
  onCacheStats?.(cacheHitFileCount, candidateFiles.length);
43903
- if (sidecarCache !== null) onSidecarStats?.(hitFiles.length - sidecarLintFiles.length, hitFiles.length);
43904
- const freshDiagnosticsByFile = attributeDiagnosticsToFiles(freshCacheableDiagnostics, missFiles);
43905
- if (!fullResult.didDropReactHooksJsPlugin && !fullResult.hadPartialFailure && freshDiagnosticsByFile !== null) {
43503
+ const missFileByNormalizedPath = /* @__PURE__ */ new Map();
43504
+ for (const missFile of missFiles) missFileByNormalizedPath.set(missFile.replaceAll("\\", "/"), missFile);
43505
+ const freshDiagnosticsByFile = /* @__PURE__ */ new Map();
43506
+ let isAttributionSound = true;
43507
+ for (const diagnostic of freshCacheableDiagnostics) {
43508
+ const missFile = missFileByNormalizedPath.get(diagnostic.filePath);
43509
+ if (missFile === void 0) {
43510
+ isAttributionSound = false;
43511
+ break;
43512
+ }
43513
+ const fileDiagnostics = freshDiagnosticsByFile.get(missFile) ?? [];
43514
+ fileDiagnostics.push(diagnostic);
43515
+ freshDiagnosticsByFile.set(missFile, fileDiagnostics);
43516
+ }
43517
+ if (!fullResult.didDropReactHooksJsPlugin && !fullResult.hadPartialFailure && isAttributionSound) {
43906
43518
  for (const missFile of missFiles) {
43907
43519
  const cacheKey = cacheKeyByFile.get(missFile);
43908
43520
  if (cacheKey !== void 0) cache.store(cacheKey, freshDiagnosticsByFile.get(missFile) ?? []);
43909
43521
  }
43910
43522
  cache.persist();
43911
43523
  }
43912
- if (sidecarCache !== null && missProbesTask !== null && sidecarProbesTask !== null) storeSidecarEntries({
43913
- sidecarCache,
43914
- cacheKeyByFile,
43915
- storablePasses: [{
43916
- files: sidecarLintFiles,
43917
- diagnostics: boundedSidecarResult.diagnostics,
43918
- isTrusted: !boundedSidecarResult.hadPartialFailure,
43919
- probesByFile: await sidecarProbesTask
43920
- }, {
43921
- files: missFiles,
43922
- diagnostics: fullResult.diagnostics.filter((diagnostic) => boundedSidecarRuleIdSet.has(diagnostic.rule)),
43923
- isTrusted: !fullResult.didDropReactHooksJsPlugin && !fullResult.hadPartialFailure,
43924
- probesByFile: await missProbesTask
43925
- }]
43926
- });
43927
43524
  return dedupeDiagnostics([
43928
43525
  ...replayedDiagnostics,
43929
43526
  ...fullResult.diagnostics,
43930
- ...sidecarReplayedDiagnostics,
43931
- ...boundedSidecarResult.diagnostics,
43932
- ...unboundedSidecarResult.diagnostics
43527
+ ...sidecarResult.diagnostics
43933
43528
  ]);
43934
43529
  }
43935
43530
  const baseArgs = makeBaseArgs(configPath);
@@ -44027,7 +43622,6 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
44027
43622
  const concurrency = yield* OxlintConcurrency;
44028
43623
  const lintBatchOrdering = yield* LintBatchOrdering;
44029
43624
  const perFileLintCacheEnabled = yield* PerFileLintCacheEnabled;
44030
- const sidecarLintCacheEnabled = yield* SidecarLintCacheEnabled;
44031
43625
  const collectedFailures = [];
44032
43626
  const diagnostics = yield* tryPromise({
44033
43627
  try: (signal) => runOxlint({
@@ -44046,9 +43640,7 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
44046
43640
  },
44047
43641
  onFileProgress: input.onFileProgress,
44048
43642
  perFileLintCacheEnabled,
44049
- sidecarLintCacheEnabled,
44050
43643
  onCacheStats: input.onCacheStats,
44051
- onSidecarStats: input.onSidecarStats,
44052
43644
  spawnTimeoutMs,
44053
43645
  outputMaxBytes,
44054
43646
  concurrency,
@@ -44470,20 +44062,6 @@ const writeCachedSocketBody = (cacheFile, body) => {
44470
44062
  }));
44471
44063
  } catch {}
44472
44064
  };
44473
- const pruneExpiredSocketCache = (cacheDirectory) => {
44474
- try {
44475
- const supplyChainCacheDirectory = Path.join(cacheDirectory, SUPPLY_CHAIN_CACHE_SUBDIR);
44476
- const expiryThresholdMs = Date.now() - SUPPLY_CHAIN_CACHE_TTL_MS;
44477
- for (const entryName of NFS.readdirSync(supplyChainCacheDirectory)) {
44478
- const entryPath = Path.join(supplyChainCacheDirectory, entryName);
44479
- try {
44480
- if (NFS.statSync(entryPath).mtimeMs < expiryThresholdMs) NFS.rmSync(entryPath);
44481
- } catch {
44482
- continue;
44483
- }
44484
- }
44485
- } catch {}
44486
- };
44487
44065
  const fetchSocketArtifact = (dependency, cacheDirectory) => tryPromise(async (signal) => {
44488
44066
  const cacheFile = cacheDirectory === null ? null : supplyChainCacheFile(cacheDirectory, dependency);
44489
44067
  if (cacheFile !== null) {
@@ -44611,7 +44189,6 @@ const checkSupplyChain = (input) => gen(function* () {
44611
44189
  const dependencies = collectDependenciesToScore(readPackageJson$1(packageJsonPath), readPackageJsonText(packageJsonPath), options.includeDevDependencies);
44612
44190
  if (dependencies.length === 0) return [];
44613
44191
  const cacheDirectory = isSupplyChainCacheDisabled() ? null : resolveReactDoctorCacheDir(input.rootDirectory);
44614
- if (cacheDirectory !== null) pruneExpiredSocketCache(cacheDirectory);
44615
44192
  const artifacts = yield* forEach$1(dependencies, (dependency) => fetchSocketArtifact(dependency, cacheDirectory), { concurrency: 8 }).pipe(timeoutOption(input.totalTimeoutMs ?? 9e4), map$3((maybeArtifacts) => getOrElse$1(maybeArtifacts, () => [])));
44616
44193
  const diagnostics = [];
44617
44194
  for (let index = 0; index < dependencies.length; index += 1) {
@@ -44841,10 +44418,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
44841
44418
  workerTimeoutMs: deadCodeTimeout.workerTimeoutMs,
44842
44419
  onCacheOutcome: (didHitCache) => {
44843
44420
  deadCodeCacheHit = didHitCache;
44844
- },
44845
- onSummaryCacheStats: (stats) => {
44846
- deadCodeSummaryCacheHits = stats.hits;
44847
- deadCodeSummaryCacheMisses = stats.misses;
44848
44421
  }
44849
44422
  }).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
44850
44423
  yield* set(deadCodeFailure, {
@@ -44873,11 +44446,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
44873
44446
  let lastReportedTotalFileCount = 0;
44874
44447
  let lintCacheHitFileCount = null;
44875
44448
  let lintCacheTotalFileCount = null;
44876
- let lintSidecarReplayedFileCount = null;
44877
- let lintSidecarTotalFileCount = null;
44878
44449
  let deadCodeCacheHit = null;
44879
- let deadCodeSummaryCacheHits = null;
44880
- let deadCodeSummaryCacheMisses = null;
44881
44450
  const baseLintStream = linterService.run({
44882
44451
  rootDirectory: scanDirectory,
44883
44452
  project,
@@ -44897,10 +44466,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
44897
44466
  lintCacheHitFileCount = cacheHitFileCount;
44898
44467
  lintCacheTotalFileCount = totalConsideredFileCount;
44899
44468
  },
44900
- onSidecarStats: (sidecarReplayedFileCount, sidecarConsideredFileCount) => {
44901
- lintSidecarReplayedFileCount = sidecarReplayedFileCount;
44902
- lintSidecarTotalFileCount = sidecarConsideredFileCount;
44903
- },
44904
44469
  deadlineEpochMs: input.deadlineEpochMs
44905
44470
  }).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
44906
44471
  yield* set(lintFailure, {
@@ -45011,11 +44576,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
45011
44576
  securityScanFailed,
45012
44577
  lintCacheHitFileCount,
45013
44578
  lintCacheTotalFileCount,
45014
- lintSidecarReplayedFileCount,
45015
- lintSidecarTotalFileCount,
45016
44579
  deadCodeCacheHit: lintFailureState.didFail ? null : deadCodeCacheHit,
45017
- deadCodeSummaryCacheHits: lintFailureState.didFail ? null : deadCodeSummaryCacheHits,
45018
- deadCodeSummaryCacheMisses: lintFailureState.didFail ? null : deadCodeSummaryCacheMisses,
45019
44580
  suppressedRuleCounts: transform.summarizeSuppressions()
45020
44581
  };
45021
44582
  }).pipe(withSpan("runInspect", { attributes: {
@@ -45497,9 +45058,11 @@ const computeConfigFingerprint = (projectDirectory, version) => {
45497
45058
  const parts = [`v=${version}`];
45498
45059
  let directory = projectDirectory;
45499
45060
  for (;;) {
45500
- for (const filename of CONFIG_FINGERPRINT_FILENAMES) {
45501
- const contentHash = hashFileContents(path.join(directory, filename));
45502
- if (contentHash !== null) parts.push(`${directory}/${filename}=${contentHash}`);
45061
+ for (const filename of CONFIG_FINGERPRINT_FILENAMES) try {
45062
+ const stat = fs.statSync(path.join(directory, filename));
45063
+ parts.push(`${directory}/${filename}=${stat.mtimeMs}:${stat.size}`);
45064
+ } catch {
45065
+ continue;
45503
45066
  }
45504
45067
  const parent = path.dirname(directory);
45505
45068
  if (parent === directory) break;
@@ -45795,7 +45358,6 @@ const isNonInteractiveEnvironment = () => NON_INTERACTIVE_ENVIRONMENT_VARIABLES.
45795
45358
  const REACT_DOCTOR_CONFIG_PROJECT_NAME = "react-doctor";
45796
45359
  const STAGED_FILES_TEMP_DIR_PREFIX = "react-doctor-staged-";
45797
45360
  const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
45798
- const SCAN_RESULT_CACHE_FILENAME = "scan-cache.json";
45799
45361
  const RUN_GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
45800
45362
  const GH_DEFAULT_BRANCH_PROBE_TIMEOUT_MS = 5e3;
45801
45363
  const CI_TRUST_COMPANIES = "PayPal, Rippling, and Alibaba";
@@ -45887,7 +45449,7 @@ const makeNoopConsole = () => ({
45887
45449
  });
45888
45450
  //#endregion
45889
45451
  //#region src/cli/utils/version.ts
45890
- const VERSION = "0.6.3-dev.ced746f";
45452
+ const VERSION = "0.6.3-dev.e257a5e";
45891
45453
  //#endregion
45892
45454
  //#region src/cli/utils/json-mode.ts
45893
45455
  let context = null;
@@ -46256,13 +45818,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
46256
45818
  * uploads source-map artifacts under, so stack frames symbolicate. Honors the
46257
45819
  * standard `SENTRY_RELEASE` override.
46258
45820
  */
46259
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.6.3-dev.ced746f`;
45821
+ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.6.3-dev.e257a5e`;
46260
45822
  /**
46261
45823
  * Deployment environment shown in Sentry's environment filter. Defaults to
46262
45824
  * `production` for tagged releases and `development` for dev/unbuilt versions,
46263
45825
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
46264
45826
  */
46265
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.6.3-dev.ced746f") ? "development" : "production");
45827
+ const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.6.3-dev.e257a5e") ? "development" : "production");
46266
45828
  /**
46267
45829
  * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
46268
45830
  * (set to `0` to disable tracing) and falls back to
@@ -58340,276 +57902,6 @@ const buildRuleBlastRadii = (diagnostics) => buildSortedRuleGroups(diagnostics).
58340
57902
  })).toSorted((left, right) => right.fileCount - left.fileCount);
58341
57903
  const findMigrationScaleBuckets = (diagnostics) => buildRuleBlastRadii(diagnostics).filter((bucket) => bucket.fileCount >= 40);
58342
57904
  //#endregion
58343
- //#region src/cli/utils/scan-result-cache.ts
58344
- const CACHE_DISABLED_VALUES = new Set(["1", "true"]);
58345
- const TOOLCHAIN_PACKAGE_SPECIFIERS = [
58346
- "oxlint/package.json",
58347
- "oxlint-plugin-react-doctor/package.json",
58348
- "deslop-js/package.json",
58349
- "eslint-plugin-react-hooks/package.json"
58350
- ];
58351
- const bundledRequire = createRequire(import.meta.url);
58352
- const normalizeForStableJson = (value) => {
58353
- if (value === null) return null;
58354
- if (value === void 0) return void 0;
58355
- const valueType = typeof value;
58356
- if (valueType === "string" || valueType === "number" || valueType === "boolean") return value;
58357
- if (valueType === "bigint" || valueType === "function" || valueType === "symbol") throw new Error("Unsupported cache key value");
58358
- if (Array.isArray(value)) return value.map((item) => {
58359
- const normalized = normalizeForStableJson(item);
58360
- return normalized === void 0 ? null : normalized;
58361
- });
58362
- const prototype = Object.getPrototypeOf(value);
58363
- if (prototype !== Object.prototype && prototype !== null) throw new Error("Unsupported cache key object");
58364
- if (!isRecord(value)) throw new Error("Unsupported cache key object");
58365
- const normalizedRecord = {};
58366
- for (const key of Object.keys(value).sort()) {
58367
- const normalized = normalizeForStableJson(value[key]);
58368
- if (normalized !== void 0) normalizedRecord[key] = normalized;
58369
- }
58370
- return normalizedRecord;
58371
- };
58372
- const stringifyStableJson = (value) => {
58373
- try {
58374
- return JSON.stringify(normalizeForStableJson(value));
58375
- } catch {
58376
- return null;
58377
- }
58378
- };
58379
- const hashString = (value) => crypto.createHash("sha1").update(value).digest("hex");
58380
- const readHeadSha = (projectDirectory) => runGit(projectDirectory, ["rev-parse", "HEAD"]);
58381
- const parseWorktreeStatusRecords = (statusOutput) => {
58382
- const entries = [];
58383
- const tokens = statusOutput.split("\0");
58384
- for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex += 1) {
58385
- let record = tokens[tokenIndex];
58386
- if (record.length === 0) continue;
58387
- if (record.length < 3 || record[2] !== " ") {
58388
- if (tokenIndex !== 0) return null;
58389
- record = ` ${record}`;
58390
- }
58391
- if (record.length < 4 || record[2] !== " ") return null;
58392
- const statusCode = record.slice(0, 2);
58393
- const recordPath = record.slice(3);
58394
- if (statusCode.includes("R") || statusCode.includes("C")) {
58395
- tokenIndex += 1;
58396
- const originPath = tokens[tokenIndex];
58397
- if (originPath === void 0 || originPath.length === 0) return null;
58398
- entries.push({
58399
- statusCode,
58400
- path: recordPath,
58401
- originPath
58402
- });
58403
- continue;
58404
- }
58405
- entries.push({
58406
- statusCode,
58407
- path: recordPath
58408
- });
58409
- }
58410
- return entries;
58411
- };
58412
- const buildDirtyPathContentFingerprint = (absolutePath, statusCode) => {
58413
- let stats;
58414
- try {
58415
- stats = fs.lstatSync(absolutePath);
58416
- } catch {
58417
- return statusCode.includes("D") ? "deleted" : null;
58418
- }
58419
- if (stats.isSymbolicLink()) try {
58420
- return `link:${fs.readlinkSync(absolutePath)}`;
58421
- } catch {
58422
- return null;
58423
- }
58424
- if (!stats.isFile()) return null;
58425
- if (stats.size > 10485760) return `stat:${stats.mtimeMs}:${stats.size}`;
58426
- return hashFileContents(absolutePath);
58427
- };
58428
- const buildWorktreeFingerprint = (projectDirectory) => {
58429
- const statusOutput = runGit(projectDirectory, [
58430
- "status",
58431
- "--porcelain=v1",
58432
- "-z",
58433
- "--untracked-files=all"
58434
- ]);
58435
- if (statusOutput === null) return null;
58436
- if (statusOutput.length === 0) return [];
58437
- const statusEntries = parseWorktreeStatusRecords(statusOutput);
58438
- if (statusEntries === null) return null;
58439
- if (statusEntries.length > 300) return null;
58440
- const repositoryRoot = runGit(projectDirectory, ["rev-parse", "--show-toplevel"]);
58441
- if (repositoryRoot === null) return null;
58442
- const dirtyEntries = [];
58443
- for (const statusEntry of statusEntries) {
58444
- const contentFingerprint = buildDirtyPathContentFingerprint(path.join(repositoryRoot, statusEntry.path), statusEntry.statusCode);
58445
- if (contentFingerprint === null) return null;
58446
- dirtyEntries.push({
58447
- ...statusEntry,
58448
- contentFingerprint
58449
- });
58450
- }
58451
- return dirtyEntries.sort((firstEntry, secondEntry) => {
58452
- const firstSortKey = `${firstEntry.path}${firstEntry.statusCode}`;
58453
- const secondSortKey = `${secondEntry.path}${secondEntry.statusCode}`;
58454
- return firstSortKey < secondSortKey ? -1 : firstSortKey > secondSortKey ? 1 : 0;
58455
- });
58456
- };
58457
- const DOTENV_FILE_NAME_PATTERN = /^\.env(\.|$)/;
58458
- const resolveDotenvFingerprint = (projectDirectory) => {
58459
- try {
58460
- return fs.readdirSync(projectDirectory).filter((entryName) => DOTENV_FILE_NAME_PATTERN.test(entryName)).sort().map((entryName) => `${entryName}=${hashFileContents(path.join(projectDirectory, entryName)) ?? "unreadable"}`);
58461
- } catch {
58462
- return [];
58463
- }
58464
- };
58465
- const hasHiddenTrackedFileState = (projectDirectory) => {
58466
- const output = runGit(projectDirectory, ["ls-files", "-v"]);
58467
- if (output === null) return true;
58468
- return output.split("\n").some((line) => line.length > 0 && line[0] !== "H");
58469
- };
58470
- const resolveCacheFilePath = (projectDirectory) => path.join(resolveReactDoctorCacheDir(projectDirectory), SCAN_RESULT_CACHE_FILENAME);
58471
- const readPersistedCache = (cacheFilePath) => {
58472
- try {
58473
- const parsed = JSON.parse(fs.readFileSync(cacheFilePath, "utf8"));
58474
- if (!isRecord(parsed) || parsed.version !== 3) return {
58475
- version: 3,
58476
- entries: []
58477
- };
58478
- if (!Array.isArray(parsed.entries)) return {
58479
- version: 3,
58480
- entries: []
58481
- };
58482
- const entries = [];
58483
- for (const entry of parsed.entries) {
58484
- if (!isRecord(entry) || typeof entry.key !== "string" || typeof entry.createdAtMs !== "number") continue;
58485
- if (!isRecord(entry.payload) || !Array.isArray(entry.payload.diagnostics)) continue;
58486
- entries.push(entry);
58487
- }
58488
- return {
58489
- version: 3,
58490
- entries
58491
- };
58492
- } catch {
58493
- return {
58494
- version: 3,
58495
- entries: []
58496
- };
58497
- }
58498
- };
58499
- const writePersistedCache = (cacheFilePath, cache) => {
58500
- try {
58501
- fs.mkdirSync(path.dirname(cacheFilePath), { recursive: true });
58502
- const tempPath = `${cacheFilePath}.${process.pid}.tmp`;
58503
- fs.writeFileSync(tempPath, JSON.stringify(cache));
58504
- fs.renameSync(tempPath, cacheFilePath);
58505
- } catch {
58506
- return;
58507
- }
58508
- };
58509
- /**
58510
- * The global cache off-switch (`REACT_DOCTOR_NO_CACHE`), which disables every
58511
- * cache subsystem: this whole-repo scan cache plus core's per-file lint,
58512
- * sidecar, and dead-code caches (their `Context.Reference` defaults read the
58513
- * same variable). Exported for the wide event's `cache.temperature`
58514
- * derivation, which reports `"disabled"` instead of `"cold"` when the switch
58515
- * is on. Granular knobs (`REACT_DOCTOR_NO_FILE_CACHE`, …) are deliberately
58516
- * not consulted — they leave the other subsystems live.
58517
- */
58518
- const isCacheGloballyDisabled = () => CACHE_DISABLED_VALUES.has(process.env.REACT_DOCTOR_NO_CACHE?.toLowerCase() ?? "");
58519
- const resolveProjectIdentity = (projectDirectory) => {
58520
- try {
58521
- return fs.realpathSync.native(projectDirectory);
58522
- } catch {
58523
- return path.resolve(projectDirectory);
58524
- }
58525
- };
58526
- const fileFingerprint = (filePath) => {
58527
- try {
58528
- const stat = fs.statSync(filePath);
58529
- return `${filePath}:${stat.mtimeMs}:${stat.size}`;
58530
- } catch {
58531
- return null;
58532
- }
58533
- };
58534
- const resolveToolchainFingerprint = (nodeBinaryPath) => {
58535
- const fingerprints = [];
58536
- if (nodeBinaryPath !== null) fingerprints.push(nodeBinaryPath === process.execPath ? `node=${process.version}` : `node=${fileFingerprint(nodeBinaryPath) ?? "unreadable"}`);
58537
- for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS) try {
58538
- const packageJson = bundledRequire(specifier);
58539
- const version = typeof packageJson.version === "string" ? packageJson.version : "unknown";
58540
- fingerprints.push(`${specifier}=${version}`);
58541
- } catch {
58542
- fingerprints.push(`${specifier}=missing`);
58543
- }
58544
- return fingerprints;
58545
- };
58546
- const buildScanResultCacheKey = (input) => {
58547
- if (isCacheGloballyDisabled()) return null;
58548
- if (hasHiddenTrackedFileState(input.projectDirectory)) return null;
58549
- const worktreeFingerprint = buildWorktreeFingerprint(input.projectDirectory);
58550
- if (worktreeFingerprint === null) return null;
58551
- const headSha = readHeadSha(input.projectDirectory);
58552
- if (headSha === null) return null;
58553
- if (stringifyStableJson(input.userConfig) === null) return null;
58554
- const cacheKeyJson = stringifyStableJson({
58555
- schemaVersion: 3,
58556
- projectIdentity: resolveProjectIdentity(input.projectDirectory),
58557
- headSha,
58558
- worktreeFingerprint,
58559
- dotenvFingerprint: resolveDotenvFingerprint(input.projectDirectory),
58560
- reactDoctorVersion: input.version,
58561
- nodeVersion: process.version,
58562
- toolchainFingerprint: resolveToolchainFingerprint(input.nodeBinaryPath),
58563
- configFingerprint: computeConfigFingerprint(input.projectDirectory, input.version),
58564
- hasConfigOverride: input.hasConfigOverride,
58565
- configSourceDirectory: input.configSourceDirectory,
58566
- userConfig: input.userConfig,
58567
- engineOptions: {
58568
- lint: input.options.lint,
58569
- deadCode: input.options.deadCode,
58570
- includePaths: [...input.options.includePaths].sort(),
58571
- customRulesOnly: input.options.customRulesOnly,
58572
- respectInlineDisables: input.options.respectInlineDisables,
58573
- warnings: input.options.warnings,
58574
- adoptExistingLintConfig: input.options.adoptExistingLintConfig,
58575
- ignoredTags: [...input.options.ignoredTags].sort(),
58576
- concurrency: input.options.concurrency,
58577
- lintBatchOrdering: resolveLintBatchOrdering(),
58578
- baselineRef: input.options.baseline?.ref,
58579
- changedLineRanges: input.options.changedLineRanges ?? void 0,
58580
- noScore: input.options.noScore,
58581
- isCi: input.options.isCi,
58582
- suppressRendering: input.options.suppressRendering,
58583
- supplyChainManifestChanged: input.options.supplyChainManifestChanged
58584
- }
58585
- });
58586
- return cacheKeyJson === null ? null : hashString(cacheKeyJson);
58587
- };
58588
- const createScanResultCache = (projectDirectory) => {
58589
- const cacheFilePath = resolveCacheFilePath(projectDirectory);
58590
- const persistedCache = readPersistedCache(cacheFilePath);
58591
- const entries = /* @__PURE__ */ new Map();
58592
- for (const entry of persistedCache.entries) entries.set(entry.key, entry);
58593
- const persist = () => {
58594
- writePersistedCache(cacheFilePath, {
58595
- version: 3,
58596
- entries: [...entries.values()].sort((firstEntry, secondEntry) => secondEntry.createdAtMs - firstEntry.createdAtMs).slice(0, 20)
58597
- });
58598
- };
58599
- return {
58600
- lookup: (key) => entries.get(key)?.payload ?? null,
58601
- store: (key, payload) => {
58602
- entries.set(key, {
58603
- key,
58604
- createdAtMs: Date.now(),
58605
- payload
58606
- });
58607
- persist();
58608
- }
58609
- };
58610
- };
58611
- const shouldStoreScanPayload = (payload) => !payload.didLintFail && !payload.didDeadCodeFail && payload.lintPartialFailures.length === 0 && !payload.supplyChainOverlapTimedOut && payload.securityScanFailed !== true;
58612
- //#endregion
58613
57905
  //#region src/cli/utils/should-block-ci.ts
58614
57906
  const shouldBlockCi = (diagnostics, blockingLevel) => {
58615
57907
  if (blockingLevel === "none") return false;
@@ -58651,63 +57943,6 @@ const readEnvBoolean = (name) => {
58651
57943
  if (value === void 0) return null;
58652
57944
  return value.toLowerCase() === "true" || value === "1";
58653
57945
  };
58654
- const ratioOf = (numerator, denominator) => denominator != null && denominator > 0 ? (numerator ?? 0) / denominator : null;
58655
- const resolveDeadCodeReuseRatio = (result) => {
58656
- if (result.deadCodeCacheHit === true) return 1;
58657
- const summaryTotal = (result.deadCodeSummaryCacheHits ?? 0) + (result.deadCodeSummaryCacheMisses ?? 0);
58658
- if (summaryTotal > 0) return (result.deadCodeSummaryCacheHits ?? 0) / summaryTotal;
58659
- return result.deadCodeCacheHit === false ? 0 : null;
58660
- };
58661
- /**
58662
- * One queryable cache temperature per scan, derived from the whole stack:
58663
- *
58664
- * - `"turbo"` — whole-repo scan-result replay (the explicit
58665
- * `wholeRepoCacheHit` flag from the CLI's cachedPayload
58666
- * branch; no scan work ran).
58667
- * - `"warm"` — any incremental reuse: per-file lint hits, sidecar
58668
- * replays, a dead-code whole-result hit, or dead-code
58669
- * summary-cache hits.
58670
- * - `"disabled"` — zero reuse because the global `REACT_DOCTOR_NO_CACHE`
58671
- * off-switch is on. Granular per-cache opt-outs
58672
- * (`REACT_DOCTOR_NO_FILE_CACHE`, …) still read warm/cold,
58673
- * since the other subsystems stay live.
58674
- * - `"cold"` — caches on, zero reuse (first scan / everything changed).
58675
- *
58676
- * `cache.warmth` is the headline reuse magnitude in [0, 1]: the plain mean of
58677
- * the subsystem reuse fractions known this run (lint hit ratio, sidecar
58678
- * replay ratio, dead-code reuse), skipping subsystems that never consulted a
58679
- * cache; `1` on turbo, dropped when nothing consulted any cache. Deliberately
58680
- * unweighted — the per-subsystem dims stay the precise signal; warmth is the
58681
- * p50/p90-able summary. Emitted only on the success path.
58682
- */
58683
- const buildCacheAttributes = (input) => {
58684
- if (input.result === void 0) return {};
58685
- if (input.wholeRepoCacheHit) return withNamespace("cache", {
58686
- wholeRepoHit: true,
58687
- temperature: "turbo",
58688
- warmth: 1
58689
- });
58690
- const result = input.result;
58691
- const subsystemReuseRatios = [
58692
- ratioOf(result.lintCacheHitFileCount, result.lintCacheTotalFileCount),
58693
- ratioOf(result.lintSidecarReplayedFileCount, result.lintSidecarTotalFileCount),
58694
- resolveDeadCodeReuseRatio(result)
58695
- ];
58696
- let knownSubsystemCount = 0;
58697
- let reuseRatioSum = 0;
58698
- for (const reuseRatio of subsystemReuseRatios) {
58699
- if (reuseRatio === null) continue;
58700
- knownSubsystemCount += 1;
58701
- reuseRatioSum += reuseRatio;
58702
- }
58703
- const warmth = knownSubsystemCount > 0 ? reuseRatioSum / knownSubsystemCount : null;
58704
- const temperature = warmth !== null && warmth > 0 ? "warm" : isCacheGloballyDisabled() ? "disabled" : "cold";
58705
- return withNamespace("cache", {
58706
- wholeRepoHit: input.wholeRepoCacheHit ?? null,
58707
- temperature,
58708
- warmth
58709
- });
58710
- };
58711
57946
  const resolveVersionPin = (versionInput) => {
58712
57947
  if (versionInput === void 0 || versionInput.trim() === "") return null;
58713
57948
  if (versionInput === "latest") return "latest";
@@ -58813,17 +58048,12 @@ const buildOutcomeAttributes = (input) => {
58813
58048
  deadlineSkippedFileCount: input.lintDeadlineSkippedFileCount ?? null,
58814
58049
  cacheHitFiles: result.lintCacheHitFileCount ?? null,
58815
58050
  cacheTotalFiles: result.lintCacheTotalFileCount ?? null,
58816
- cacheHitRatio: ratioOf(result.lintCacheHitFileCount, result.lintCacheTotalFileCount),
58817
- sidecarReplayedFiles: result.lintSidecarReplayedFileCount ?? null,
58818
- sidecarTotalFiles: result.lintSidecarTotalFileCount ?? null,
58819
- sidecarReplayRatio: ratioOf(result.lintSidecarReplayedFileCount, result.lintSidecarTotalFileCount)
58051
+ cacheHitRatio: result.lintCacheTotalFileCount != null && result.lintCacheTotalFileCount > 0 ? (result.lintCacheHitFileCount ?? 0) / result.lintCacheTotalFileCount : null
58820
58052
  }),
58821
58053
  ...withNamespace("deadCode", {
58822
58054
  failed: input.didDeadCodeFail ?? null,
58823
58055
  overlapped: input.deadCodeOverlapped ?? null,
58824
- cacheHit: result.deadCodeCacheHit ?? null,
58825
- summaryCacheHits: result.deadCodeSummaryCacheHits ?? null,
58826
- summaryCacheMisses: result.deadCodeSummaryCacheMisses ?? null
58056
+ cacheHit: result.deadCodeCacheHit ?? null
58827
58057
  }),
58828
58058
  ...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut ?? null }),
58829
58059
  ...withNamespace("securityScan", { failed: input.securityScanFailed ?? null }),
@@ -58883,7 +58113,7 @@ const buildScanAttributes = (input) => {
58883
58113
  * Projects a scan into the namespaced attribute set for its root span — the
58884
58114
  * canonical per-scan "wide event". Every attribute carries a dotted namespace
58885
58115
  * that groups it by concept (`scan.*` config, `action.*` CI knobs, `outcome.*`
58886
- * verdict, `diag.*` findings, `score.*`, `lint.*`, `deadCode.*`, `cache.*`,
58116
+ * verdict, `diag.*` findings, `score.*`, `lint.*`, `deadCode.*`,
58887
58117
  * `supplyChain.*`, `timing.*`, `migration.*`, `baseline.*`) so the attributes
58888
58118
  * tree up in Sentry's attribute browser and stay filter-/group-/aggregate-able
58889
58119
  * in the Spans dataset. Pure and exported so the projection (outcome
@@ -58897,8 +58127,7 @@ const buildScanAttributes = (input) => {
58897
58127
  const buildRunEventAttributes = (input) => toSpanAttributes({
58898
58128
  ...buildScanAttributes(input),
58899
58129
  ...buildActionAttributes(),
58900
- ...buildOutcomeAttributes(input),
58901
- ...buildCacheAttributes(input)
58130
+ ...buildOutcomeAttributes(input)
58902
58131
  });
58903
58132
  /**
58904
58133
  * Stamps the wide-event attributes onto the run's root span. A guarded no-op
@@ -60108,6 +59337,196 @@ const resolveCliCategories = (categoryFlag) => {
60108
59337
  return resolvedCategories.length > 0 ? resolvedCategories : void 0;
60109
59338
  };
60110
59339
  //#endregion
59340
+ //#region src/cli/utils/scan-result-cache.ts
59341
+ const CACHE_DISABLED_VALUES = new Set(["1", "true"]);
59342
+ const TOOLCHAIN_PACKAGE_SPECIFIERS = [
59343
+ "oxlint",
59344
+ "oxlint/package.json",
59345
+ "oxlint-plugin-react-doctor",
59346
+ "deslop-js/package.json",
59347
+ "eslint-plugin-react-hooks/package.json"
59348
+ ];
59349
+ const bundledRequire = createRequire(import.meta.url);
59350
+ const normalizeForStableJson = (value) => {
59351
+ if (value === null) return null;
59352
+ if (value === void 0) return void 0;
59353
+ const valueType = typeof value;
59354
+ if (valueType === "string" || valueType === "number" || valueType === "boolean") return value;
59355
+ if (valueType === "bigint" || valueType === "function" || valueType === "symbol") throw new Error("Unsupported cache key value");
59356
+ if (Array.isArray(value)) return value.map((item) => {
59357
+ const normalized = normalizeForStableJson(item);
59358
+ return normalized === void 0 ? null : normalized;
59359
+ });
59360
+ const prototype = Object.getPrototypeOf(value);
59361
+ if (prototype !== Object.prototype && prototype !== null) throw new Error("Unsupported cache key object");
59362
+ if (!isRecord(value)) throw new Error("Unsupported cache key object");
59363
+ const normalizedRecord = {};
59364
+ for (const key of Object.keys(value).sort()) {
59365
+ const normalized = normalizeForStableJson(value[key]);
59366
+ if (normalized !== void 0) normalizedRecord[key] = normalized;
59367
+ }
59368
+ return normalizedRecord;
59369
+ };
59370
+ const stringifyStableJson = (value) => {
59371
+ try {
59372
+ return JSON.stringify(normalizeForStableJson(value));
59373
+ } catch {
59374
+ return null;
59375
+ }
59376
+ };
59377
+ const hashString = (value) => crypto.createHash("sha1").update(value).digest("hex");
59378
+ const readHeadSha = (projectDirectory) => runGit(projectDirectory, ["rev-parse", "HEAD"]);
59379
+ const isWorktreeClean = (projectDirectory) => {
59380
+ const status = runGit(projectDirectory, [
59381
+ "status",
59382
+ "--porcelain=v1",
59383
+ "--untracked-files=normal"
59384
+ ]);
59385
+ return status !== null && status.length === 0;
59386
+ };
59387
+ const hasHiddenTrackedFileState = (projectDirectory) => {
59388
+ const output = runGit(projectDirectory, ["ls-files", "-v"]);
59389
+ if (output === null) return true;
59390
+ return output.split("\n").some((line) => line.length > 0 && line[0] !== "H");
59391
+ };
59392
+ const resolveCacheFilePath = (projectDirectory) => {
59393
+ const nodeModulesDirectory = path.join(projectDirectory, "node_modules");
59394
+ if (fs.existsSync(nodeModulesDirectory)) return path.join(nodeModulesDirectory, ".cache", "react-doctor", "scan-cache.json");
59395
+ const projectHash = hashString(projectDirectory).slice(0, 16);
59396
+ return path.join(os.tmpdir(), "react-doctor-cache", `scan-${projectHash}.json`);
59397
+ };
59398
+ const readPersistedCache = (cacheFilePath) => {
59399
+ try {
59400
+ const parsed = JSON.parse(fs.readFileSync(cacheFilePath, "utf8"));
59401
+ if (!isRecord(parsed) || parsed.version !== 3) return {
59402
+ version: 3,
59403
+ entries: []
59404
+ };
59405
+ if (!Array.isArray(parsed.entries)) return {
59406
+ version: 3,
59407
+ entries: []
59408
+ };
59409
+ const entries = [];
59410
+ for (const entry of parsed.entries) {
59411
+ if (!isRecord(entry) || typeof entry.key !== "string" || typeof entry.createdAtMs !== "number") continue;
59412
+ if (!isRecord(entry.payload) || !Array.isArray(entry.payload.diagnostics)) continue;
59413
+ entries.push(entry);
59414
+ }
59415
+ return {
59416
+ version: 3,
59417
+ entries
59418
+ };
59419
+ } catch {
59420
+ return {
59421
+ version: 3,
59422
+ entries: []
59423
+ };
59424
+ }
59425
+ };
59426
+ const writePersistedCache = (cacheFilePath, cache) => {
59427
+ try {
59428
+ fs.mkdirSync(path.dirname(cacheFilePath), { recursive: true });
59429
+ const tempPath = `${cacheFilePath}.${process.pid}.tmp`;
59430
+ fs.writeFileSync(tempPath, JSON.stringify(cache));
59431
+ fs.renameSync(tempPath, cacheFilePath);
59432
+ } catch {
59433
+ return;
59434
+ }
59435
+ };
59436
+ const isScanResultCacheDisabled = () => CACHE_DISABLED_VALUES.has(process.env.REACT_DOCTOR_NO_CACHE?.toLowerCase() ?? "");
59437
+ const resolveProjectIdentity = (projectDirectory) => {
59438
+ try {
59439
+ return fs.realpathSync.native(projectDirectory);
59440
+ } catch {
59441
+ return path.resolve(projectDirectory);
59442
+ }
59443
+ };
59444
+ const fileFingerprint = (filePath) => {
59445
+ try {
59446
+ const stat = fs.statSync(filePath);
59447
+ return `${filePath}:${stat.mtimeMs}:${stat.size}`;
59448
+ } catch {
59449
+ return null;
59450
+ }
59451
+ };
59452
+ const resolveToolchainFingerprint = (nodeBinaryPath) => {
59453
+ const fingerprints = [];
59454
+ if (nodeBinaryPath !== null) {
59455
+ const fingerprint = fileFingerprint(nodeBinaryPath);
59456
+ if (fingerprint !== null) fingerprints.push(`node=${fingerprint}`);
59457
+ }
59458
+ for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS) try {
59459
+ const fingerprint = fileFingerprint(bundledRequire.resolve(specifier));
59460
+ if (fingerprint !== null) fingerprints.push(`${specifier}=${fingerprint}`);
59461
+ } catch {
59462
+ continue;
59463
+ }
59464
+ return fingerprints;
59465
+ };
59466
+ const buildScanResultCacheKey = (input) => {
59467
+ if (isScanResultCacheDisabled()) return null;
59468
+ if (!isWorktreeClean(input.projectDirectory)) return null;
59469
+ if (hasHiddenTrackedFileState(input.projectDirectory)) return null;
59470
+ const headSha = readHeadSha(input.projectDirectory);
59471
+ if (headSha === null) return null;
59472
+ if (stringifyStableJson(input.userConfig) === null) return null;
59473
+ const cacheKeyJson = stringifyStableJson({
59474
+ schemaVersion: 3,
59475
+ projectIdentity: resolveProjectIdentity(input.projectDirectory),
59476
+ headSha,
59477
+ reactDoctorVersion: input.version,
59478
+ nodeVersion: process.version,
59479
+ toolchainFingerprint: resolveToolchainFingerprint(input.nodeBinaryPath),
59480
+ configFingerprint: computeConfigFingerprint(input.projectDirectory, input.version),
59481
+ hasConfigOverride: input.hasConfigOverride,
59482
+ configSourceDirectory: input.configSourceDirectory,
59483
+ userConfig: input.userConfig,
59484
+ engineOptions: {
59485
+ lint: input.options.lint,
59486
+ deadCode: input.options.deadCode,
59487
+ includePaths: [...input.options.includePaths].sort(),
59488
+ customRulesOnly: input.options.customRulesOnly,
59489
+ respectInlineDisables: input.options.respectInlineDisables,
59490
+ warnings: input.options.warnings,
59491
+ adoptExistingLintConfig: input.options.adoptExistingLintConfig,
59492
+ ignoredTags: [...input.options.ignoredTags].sort(),
59493
+ concurrency: input.options.concurrency,
59494
+ lintBatchOrdering: resolveLintBatchOrdering(),
59495
+ baselineRef: input.options.baseline?.ref,
59496
+ changedLineRanges: input.options.changedLineRanges ?? void 0,
59497
+ noScore: input.options.noScore,
59498
+ isCi: input.options.isCi,
59499
+ suppressRendering: input.options.suppressRendering,
59500
+ supplyChainManifestChanged: input.options.supplyChainManifestChanged
59501
+ }
59502
+ });
59503
+ return cacheKeyJson === null ? null : hashString(cacheKeyJson);
59504
+ };
59505
+ const createScanResultCache = (projectDirectory) => {
59506
+ const cacheFilePath = resolveCacheFilePath(projectDirectory);
59507
+ const persistedCache = readPersistedCache(cacheFilePath);
59508
+ const entries = /* @__PURE__ */ new Map();
59509
+ for (const entry of persistedCache.entries) entries.set(entry.key, entry);
59510
+ const persist = () => {
59511
+ writePersistedCache(cacheFilePath, {
59512
+ version: 3,
59513
+ entries: [...entries.values()].sort((firstEntry, secondEntry) => secondEntry.createdAtMs - firstEntry.createdAtMs).slice(0, 20)
59514
+ });
59515
+ };
59516
+ return {
59517
+ lookup: (key) => entries.get(key)?.payload ?? null,
59518
+ store: (key, payload) => {
59519
+ entries.set(key, {
59520
+ key,
59521
+ createdAtMs: Date.now(),
59522
+ payload
59523
+ });
59524
+ persist();
59525
+ }
59526
+ };
59527
+ };
59528
+ const shouldStoreScanPayload = (payload) => !payload.didLintFail && !payload.didDeadCodeFail && payload.lintPartialFailures.length === 0 && !payload.supplyChainOverlapTimedOut && payload.securityScanFailed !== true;
59529
+ //#endregion
60111
59530
  //#region src/inspect.ts
60112
59531
  const silentConsole = makeNoopConsole();
60113
59532
  const runConsole = (effect) => {
@@ -60284,7 +59703,7 @@ const runBaselineComparison = async (params) => {
60284
59703
  suppressScanSummary: true,
60285
59704
  supplyChainManifestChanged: params.options.supplyChainManifestChanged,
60286
59705
  deadlineEpochMs: params.deadlineEpochMs ?? void 0
60287
- }, {}).pipe(provide(baseLayers), provideService$2(PerFileLintCacheEnabled, false), provideService$2(SidecarLintCacheEnabled, false), provideService$2(Console, silentConsole))));
59706
+ }, {}).pipe(provide(baseLayers), provideService$2(Console, silentConsole))));
60288
59707
  if (baseOutput.didLintFail || countIncompleteLintFiles(baseOutput.lintPartialFailures) > 0) return null;
60289
59708
  const delta = computeDiagnosticDelta({
60290
59709
  headDiagnostics: params.headDiagnostics,
@@ -60337,8 +59756,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
60337
59756
  startTime,
60338
59757
  rootSentrySpan,
60339
59758
  scanMode: cachedPayload.baselineDelta ? "baseline" : isDiffMode ? "diff" : "full",
60340
- baselineDegraded,
60341
- wholeRepoCacheHit: true
59759
+ baselineDegraded
60342
59760
  });
60343
59761
  recordOnboardingCompletion(options);
60344
59762
  return result;
@@ -60447,14 +59865,9 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
60447
59865
  rootSentrySpan,
60448
59866
  scanMode: baselineDelta ? "baseline" : isDiffMode ? "diff" : "full",
60449
59867
  baselineDegraded,
60450
- wholeRepoCacheHit: false,
60451
59868
  lintCacheHitFileCount: output.lintCacheHitFileCount,
60452
59869
  lintCacheTotalFileCount: output.lintCacheTotalFileCount,
60453
- lintSidecarReplayedFileCount: output.lintSidecarReplayedFileCount,
60454
- lintSidecarTotalFileCount: output.lintSidecarTotalFileCount,
60455
- deadCodeCacheHit: output.deadCodeCacheHit,
60456
- deadCodeSummaryCacheHits: output.deadCodeSummaryCacheHits,
60457
- deadCodeSummaryCacheMisses: output.deadCodeSummaryCacheMisses
59870
+ deadCodeCacheHit: output.deadCodeCacheHit
60458
59871
  });
60459
59872
  recordOnboardingCompletion(options);
60460
59873
  return result;
@@ -60489,11 +59902,7 @@ const renderAndRecordScan = async (input) => {
60489
59902
  scanElapsedMilliseconds: input.payload.scanElapsedMilliseconds,
60490
59903
  lintCacheHitFileCount: input.lintCacheHitFileCount ?? null,
60491
59904
  lintCacheTotalFileCount: input.lintCacheTotalFileCount ?? null,
60492
- lintSidecarReplayedFileCount: input.lintSidecarReplayedFileCount ?? null,
60493
- lintSidecarTotalFileCount: input.lintSidecarTotalFileCount ?? null,
60494
59905
  deadCodeCacheHit: input.deadCodeCacheHit ?? null,
60495
- deadCodeSummaryCacheHits: input.deadCodeSummaryCacheHits ?? null,
60496
- deadCodeSummaryCacheMisses: input.deadCodeSummaryCacheMisses ?? null,
60497
59906
  baselineDelta: input.payload.baselineDelta
60498
59907
  }), input.options.silent));
60499
59908
  const { workerCount: resolvedWorkerCount, parallel } = resolveWorkerTelemetry(input.payload.scanConcurrency, input.options.concurrency);
@@ -60518,7 +59927,6 @@ const renderAndRecordScan = async (input) => {
60518
59927
  result,
60519
59928
  mode: input.scanMode,
60520
59929
  gateExempt: input.baselineDegraded,
60521
- wholeRepoCacheHit: input.wholeRepoCacheHit,
60522
59930
  didLintFail: input.payload.didLintFail,
60523
59931
  lintFailureReasonKind: input.payload.lintFailureReasonKind,
60524
59932
  lintPartialFailureCount: input.payload.lintPartialFailures.length,
@@ -60533,7 +59941,7 @@ const renderAndRecordScan = async (input) => {
60533
59941
  return result;
60534
59942
  };
60535
59943
  const finalizeAndRender = (input) => gen(function* () {
60536
- const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, directory, scannedFileCount, scannedFilePaths, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount, lintSidecarReplayedFileCount, lintSidecarTotalFileCount, deadCodeCacheHit, deadCodeSummaryCacheHits, deadCodeSummaryCacheMisses, baselineDelta } = input;
59944
+ const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, directory, scannedFileCount, scannedFilePaths, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount, deadCodeCacheHit, baselineDelta } = input;
60537
59945
  const { skippedChecks, skippedCheckReasons } = buildSkippedChecks({
60538
59946
  didLintFail,
60539
59947
  lintFailureReason,
@@ -60557,15 +59965,7 @@ const finalizeAndRender = (input) => gen(function* () {
60557
59965
  lintCacheHitFileCount,
60558
59966
  lintCacheTotalFileCount
60559
59967
  } : {},
60560
- ...lintSidecarTotalFileCount !== null ? {
60561
- lintSidecarReplayedFileCount,
60562
- lintSidecarTotalFileCount
60563
- } : {},
60564
59968
  ...deadCodeCacheHit !== null ? { deadCodeCacheHit } : {},
60565
- ...deadCodeSummaryCacheHits !== null && deadCodeSummaryCacheMisses !== null ? {
60566
- deadCodeSummaryCacheHits,
60567
- deadCodeSummaryCacheMisses
60568
- } : {},
60569
59969
  ...baselineDelta ? { baselineDelta } : {}
60570
59970
  });
60571
59971
  if (options.suppressRendering) return buildResult();
@@ -64412,4 +63812,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
64412
63812
  export {};
64413
63813
 
64414
63814
  //# sourceMappingURL=cli.js.map
64415
- //# debugId=1722becf-6e91-56ae-afc9-1e1410647bc3
63815
+ //# debugId=82500030-a7a7-5da8-b18e-cbdb42102dc3