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