react-doctor 0.6.3-dev.bdf8074 → 0.6.3-dev.ce49250

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/lsp.js CHANGED
@@ -8,7 +8,7 @@ import path from "node:path";
8
8
  import * as NodeChildProcess from "node:child_process";
9
9
  import { execFileSync, spawn, spawnSync } from "node:child_process";
10
10
  import * as ts from "typescript";
11
- 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";
11
+ 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";
12
12
  import { parseJSON5 } from "confbox";
13
13
  import * as NodeUrl from "node:url";
14
14
  import { fileURLToPath } from "node:url";
@@ -32987,7 +32987,6 @@ const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
32987
32987
  const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
32988
32988
  const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
32989
32989
  const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
32990
- const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
32991
32990
  const CORE_PACKAGE_VERSION = "0.6.3";
32992
32991
  const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
32993
32992
  const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
@@ -35652,29 +35651,6 @@ var PerFileLintCacheEnabled = class extends Reference("react-doctor/PerFileLintC
35652
35651
  return true;
35653
35652
  } }) {};
35654
35653
  /**
35655
- * Whether the sidecar lint cache (`runners/oxlint/sidecar-lint-cache.ts`) is
35656
- * active. Defaults ON — warm rescans replay the cross-file rules' cached
35657
- * diagnostics for every file whose dependency fingerprint (the probe set the
35658
- * plugin's collectors recorded) still matches the tree, and re-lint only the
35659
- * rest. Only reachable when the per-file lint cache itself is on (the
35660
- * sidecar covers its cache hits), so `REACT_DOCTOR_NO_CACHE` /
35661
- * `REACT_DOCTOR_NO_FILE_CACHE` implicitly disable it too. Opt-OUT knobs:
35662
- *
35663
- * - `REACT_DOCTOR_NO_CACHE` — the global off-switch.
35664
- * - `REACT_DOCTOR_NO_SIDECAR_CACHE` — granular rollback hatch: keep the
35665
- * per-file cache but run the always-fresh sidecar over every hit
35666
- * (the pre-cache behavior).
35667
- *
35668
- * Tests override via `Layer.succeed(SidecarLintCacheEnabled, false)`.
35669
- */
35670
- var SidecarLintCacheEnabled = class extends Reference("react-doctor/SidecarLintCacheEnabled", { defaultValue: () => {
35671
- const noCache = process.env["REACT_DOCTOR_NO_CACHE"]?.toLowerCase() ?? "";
35672
- const noSidecarCache = process.env["REACT_DOCTOR_NO_SIDECAR_CACHE"]?.toLowerCase() ?? "";
35673
- if (CACHE_DISABLED_VALUES.has(noCache)) return false;
35674
- if (CACHE_DISABLED_VALUES.has(noSidecarCache)) return false;
35675
- return true;
35676
- } }) {};
35677
- /**
35678
35654
  * Whether the whole-project dead-code result cache
35679
35655
  * (`dead-code/dead-code-result-cache.ts`) is active. Defaults ON — a rescan
35680
35656
  * whose inputs (source tree, manifests, configs, analyzer version) are
@@ -37386,10 +37362,10 @@ const computeDeadCodeCacheKey = (input) => crypto.createHash("sha1").update(JSON
37386
37362
  tsConfigFile: input.tsConfigPath === void 0 ? null : Path.relative(input.rootDirectory, input.tsConfigPath).replace(/\\/g, "/"),
37387
37363
  files: collectAnalyzedFileFingerprints(input.rootDirectory)
37388
37364
  })).digest("hex");
37389
- const validateDiagnostic$2 = decodeUnknownSync(Diagnostic);
37365
+ const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
37390
37366
  const decodeCachedDiagnostics = (raw) => {
37391
37367
  try {
37392
- for (const entry of raw) validateDiagnostic$2(entry);
37368
+ for (const entry of raw) validateDiagnostic$1(entry);
37393
37369
  return raw;
37394
37370
  } catch {
37395
37371
  return null;
@@ -38879,7 +38855,7 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
38879
38855
  }
38880
38856
  return enabled;
38881
38857
  };
38882
- const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection, sidecarRuleIdFilter }) => {
38858
+ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection }) => {
38883
38859
  const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
38884
38860
  const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
38885
38861
  const jsPlugins = [];
@@ -38891,7 +38867,6 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
38891
38867
  if (!rule) continue;
38892
38868
  if (ruleSelection === "cacheable" && CROSS_FILE_RULE_IDS.has(registryEntry.id)) continue;
38893
38869
  if (ruleSelection === "sidecar" && !CROSS_FILE_RULE_IDS.has(registryEntry.id)) continue;
38894
- if (ruleSelection === "sidecar" && sidecarRuleIdFilter !== void 0 && !sidecarRuleIdFilter.has(registryEntry.id)) continue;
38895
38870
  if (rule.scan !== void 0) continue;
38896
38871
  if (customRulesOnly && registryEntry.originallyExternal) continue;
38897
38872
  if (rule.framework !== "global" && !rule.requires) continue;
@@ -38936,23 +38911,23 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
38936
38911
  }
38937
38912
  };
38938
38913
  };
38939
- const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
38914
+ const validateDiagnostic = decodeUnknownSync(Diagnostic);
38940
38915
  const decodeFileDiagnostics = (raw) => {
38941
38916
  if (!Array.isArray(raw)) return null;
38942
38917
  try {
38943
- for (const entry of raw) validateDiagnostic$1(entry);
38918
+ for (const entry of raw) validateDiagnostic(entry);
38944
38919
  return raw;
38945
38920
  } catch {
38946
38921
  return null;
38947
38922
  }
38948
38923
  };
38949
- const emptyCache$1 = () => ({
38924
+ const emptyCache = () => ({
38950
38925
  version: 1,
38951
38926
  rulesets: {}
38952
38927
  });
38953
- const loadRulesetEntries$1 = (cacheFilePath, rulesetHash) => {
38928
+ const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
38954
38929
  const entries = /* @__PURE__ */ new Map();
38955
- const persisted = failOpenReadJson(cacheFilePath, emptyCache$1());
38930
+ const persisted = failOpenReadJson(cacheFilePath, emptyCache());
38956
38931
  if (persisted.version !== 1 || !isRecord(persisted.rulesets)) return entries;
38957
38932
  const bucket = persisted.rulesets[rulesetHash];
38958
38933
  if (!isRecord(bucket) || !isRecord(bucket.files)) return entries;
@@ -38964,156 +38939,20 @@ const loadRulesetEntries$1 = (cacheFilePath, rulesetHash) => {
38964
38939
  };
38965
38940
  const createFileLintCache = (cacheDirectory, rulesetHash) => {
38966
38941
  const cacheFilePath = Path.join(cacheDirectory, FILE_LINT_CACHE_FILENAME);
38967
- const entries = loadRulesetEntries$1(cacheFilePath, rulesetHash);
38942
+ const entries = loadRulesetEntries(cacheFilePath, rulesetHash);
38968
38943
  return {
38969
38944
  lookup: (fileKey) => entries.get(fileKey) ?? null,
38970
38945
  store: (fileKey, diagnostics) => {
38971
38946
  entries.delete(fileKey);
38972
38947
  entries.set(fileKey, diagnostics);
38973
38948
  },
38974
- persist: () => {
38975
- const onDisk = failOpenReadJson(cacheFilePath, emptyCache$1());
38976
- const rulesets = onDisk.version === 1 && isRecord(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
38977
- const existingBucket = rulesets[rulesetHash];
38978
- const existingFiles = isRecord(existingBucket) && isRecord(existingBucket.files) ? existingBucket.files : {};
38979
- const ourFiles = {};
38980
- for (const [fileKey, diagnostics] of entries) ourFiles[fileKey] = diagnostics;
38981
- const cappedEntries = Object.entries({
38982
- ...existingFiles,
38983
- ...ourFiles
38984
- }).slice(-FILE_LINT_CACHE_MAX_FILE_COUNT);
38985
- rulesets[rulesetHash] = {
38986
- updatedAtMs: Date.now(),
38987
- files: Object.fromEntries(cappedEntries)
38988
- };
38989
- atomicWriteJson(cacheFilePath, {
38990
- version: 1,
38991
- rulesets: Object.fromEntries(Object.entries(rulesets).filter(([, bucket]) => isRecord(bucket) && typeof bucket.updatedAtMs === "number").sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8))
38992
- });
38993
- }
38994
- };
38995
- };
38996
- const hashFileContents = (filePath) => {
38997
- try {
38998
- return crypto.createHash("sha1").update(NFS.readFileSync(filePath)).digest("hex");
38999
- } catch {
39000
- return null;
39001
- }
39002
- };
39003
- const ABSENT_CONTENT_ANSWER = "absent";
39004
- /**
39005
- * Answers dependency probes for the sidecar lint cache — used both when
39006
- * STORING an entry (turn the collector's probe paths into answers) and when
39007
- * VERIFYING one (re-answer each stored probe against the current tree).
39008
- * Store and verify must agree exactly, so both go through this one resolver.
39009
- *
39010
- * - `content` — SHA-1 of the file's bytes, `"absent"` when unreadable
39011
- * (missing, a directory, permission failure). Source files reuse the
39012
- * hashes the cache-partition loop already computed
39013
- * (`contentHashByRelativePath`); everything else (package.json,
39014
- * tsconfig, ignored files) is hashed on demand.
39015
- * - `exists` — the existence class a module-resolution candidate probe
39016
- * sees: `"file"`, `"dir"`, or `"none"` (missing or an exotic entry —
39017
- * `statSync` follows symlinks, matching the resolvers).
39018
- *
39019
- * Answers are memoized per scan: the filesystem is frozen for the run, and
39020
- * hot dependency paths (a barrel imported by hundreds of files, the shared
39021
- * tsconfig chain) repeat across many files' probe sets.
39022
- */
39023
- const createSidecarProbeAnswerResolver = (input) => {
39024
- const contentAnswerByPath = /* @__PURE__ */ new Map();
39025
- const existsAnswerByPath = /* @__PURE__ */ new Map();
39026
- const toRelativePath = (absolutePath) => Path.relative(input.rootDirectory, absolutePath).replaceAll("\\", "/");
39027
- const contentAnswer = (relativePath) => {
39028
- const memoized = contentAnswerByPath.get(relativePath);
39029
- if (memoized !== void 0) return memoized;
39030
- const answer = input.contentHashByRelativePath.get(relativePath) ?? hashFileContents(Path.resolve(input.rootDirectory, relativePath)) ?? ABSENT_CONTENT_ANSWER;
39031
- contentAnswerByPath.set(relativePath, answer);
39032
- return answer;
39033
- };
39034
- const existsAnswer = (relativePath) => {
39035
- const memoized = existsAnswerByPath.get(relativePath);
39036
- if (memoized !== void 0) return memoized;
39037
- let answer;
39038
- try {
39039
- const stat = NFS.statSync(Path.resolve(input.rootDirectory, relativePath));
39040
- answer = stat.isFile() ? "file" : stat.isDirectory() ? "dir" : "none";
39041
- } catch {
39042
- answer = "none";
39043
- }
39044
- existsAnswerByPath.set(relativePath, answer);
39045
- return answer;
39046
- };
39047
- return {
39048
- answerFor: (kind, relativePath) => kind === "content" ? contentAnswer(relativePath) : existsAnswer(relativePath),
39049
- toRelativePath
39050
- };
39051
- };
39052
- const validateDiagnostic = decodeUnknownSync(Diagnostic);
39053
- const isProbeKind = (value) => value === "content" || value === "exists";
39054
- const decodeSidecarFileEntry = (raw) => {
39055
- if (!isRecord(raw) || !Array.isArray(raw.probes) || !Array.isArray(raw.diagnostics)) return null;
39056
- const probes = [];
39057
- for (const rawProbe of raw.probes) {
39058
- if (!Array.isArray(rawProbe) || rawProbe.length !== 3) return null;
39059
- const [kind, probePath, answer] = rawProbe;
39060
- if (!isProbeKind(kind) || typeof probePath !== "string" || typeof answer !== "string") return null;
39061
- probes.push({
39062
- kind,
39063
- path: probePath,
39064
- answer
39065
- });
39066
- }
39067
- try {
39068
- for (const entry of raw.diagnostics) validateDiagnostic(entry);
39069
- } catch {
39070
- return null;
39071
- }
39072
- return {
39073
- probes,
39074
- diagnostics: raw.diagnostics
39075
- };
39076
- };
39077
- const encodeSidecarFileEntry = (entry) => ({
39078
- probes: entry.probes.map((probe) => [
39079
- probe.kind,
39080
- probe.path,
39081
- probe.answer
39082
- ]),
39083
- diagnostics: entry.diagnostics
39084
- });
39085
- const emptyCache = () => ({
39086
- version: 1,
39087
- rulesets: {}
39088
- });
39089
- const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
39090
- const entries = /* @__PURE__ */ new Map();
39091
- const persisted = failOpenReadJson(cacheFilePath, emptyCache());
39092
- if (persisted.version !== 1 || !isRecord(persisted.rulesets)) return entries;
39093
- const bucket = persisted.rulesets[rulesetHash];
39094
- if (!isRecord(bucket) || !isRecord(bucket.files)) return entries;
39095
- for (const [fileKey, rawEntry] of Object.entries(bucket.files)) {
39096
- const decoded = decodeSidecarFileEntry(rawEntry);
39097
- if (decoded !== null) entries.set(fileKey, decoded);
39098
- }
39099
- return entries;
39100
- };
39101
- const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
39102
- const cacheFilePath = Path.join(cacheDirectory, SIDECAR_LINT_CACHE_FILENAME);
39103
- const entries = loadRulesetEntries(cacheFilePath, rulesetHash);
39104
- return {
39105
- lookup: (fileKey) => entries.get(fileKey) ?? null,
39106
- store: (fileKey, entry) => {
39107
- entries.delete(fileKey);
39108
- entries.set(fileKey, entry);
39109
- },
39110
38949
  persist: () => {
39111
38950
  const onDisk = failOpenReadJson(cacheFilePath, emptyCache());
39112
38951
  const rulesets = onDisk.version === 1 && isRecord(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
39113
38952
  const existingBucket = rulesets[rulesetHash];
39114
38953
  const existingFiles = isRecord(existingBucket) && isRecord(existingBucket.files) ? existingBucket.files : {};
39115
38954
  const ourFiles = {};
39116
- for (const [fileKey, entry] of entries) ourFiles[fileKey] = encodeSidecarFileEntry(entry);
38955
+ for (const [fileKey, diagnostics] of entries) ourFiles[fileKey] = diagnostics;
39117
38956
  const cappedEntries = Object.entries({
39118
38957
  ...existingFiles,
39119
38958
  ...ourFiles
@@ -40128,6 +39967,13 @@ const validateRuleRegistration = () => {
40128
39967
  ].filter((entry) => entry !== null).join("; ");
40129
39968
  console.warn(`[react-doctor] rule-registration drift: ${detail}`);
40130
39969
  };
39970
+ const hashFileContents = (filePath) => {
39971
+ try {
39972
+ return crypto.createHash("sha1").update(NFS.readFileSync(filePath)).digest("hex");
39973
+ } catch {
39974
+ return null;
39975
+ }
39976
+ };
40131
39977
  /**
40132
39978
  * Balanced LPT (longest-processing-time-first) lint batch planner. Where
40133
39979
  * `batchIncludePaths` greedily fills 100-file batches and leaves a small
@@ -40209,106 +40055,6 @@ const writeOxlintConfig = (configPath, configToWrite) => {
40209
40055
  NFS.closeSync(fileHandle);
40210
40056
  }
40211
40057
  };
40212
- /**
40213
- * Attributes diagnostics back to the file that produced them by the
40214
- * normalized path oxlint echoes. Returns `null` when ANY diagnostic can't be
40215
- * attributed — the path forms don't align, so the caller must skip its cache
40216
- * store rather than risk caching a wrong empty result for a file that
40217
- * actually had diagnostics.
40218
- */
40219
- const attributeDiagnosticsToFiles = (diagnostics, files) => {
40220
- const fileByNormalizedPath = /* @__PURE__ */ new Map();
40221
- for (const file of files) fileByNormalizedPath.set(file.replaceAll("\\", "/"), file);
40222
- const diagnosticsByFile = /* @__PURE__ */ new Map();
40223
- for (const diagnostic of diagnostics) {
40224
- const file = fileByNormalizedPath.get(diagnostic.filePath);
40225
- if (file === void 0) return null;
40226
- const fileDiagnostics = diagnosticsByFile.get(file) ?? [];
40227
- fileDiagnostics.push(diagnostic);
40228
- diagnosticsByFile.set(file, fileDiagnostics);
40229
- }
40230
- return diagnosticsByFile;
40231
- };
40232
- /**
40233
- * Collects each file's cross-file dependency probes (via the plugin's
40234
- * collectors) with their current answers. Fail-open per file: an unreadable /
40235
- * unparseable / collector-failing file maps to `null` (no entry stored — it
40236
- * re-lints next scan). The filesystem is frozen for the scan, so this can run
40237
- * CONCURRENTLY with an oxlint child-process await — the caller starts it
40238
- * unawaited before a lint pass and awaits it at store time, hiding the
40239
- * in-process parse + resolution cost under the subprocess wait. The loop
40240
- * yields on the cooperative time budget so the child's stdout handling (and
40241
- * sibling fibers) keep flowing; each per-file collection is synchronous, so
40242
- * the module-level probe recorder never sees interleaved use. Never rejects.
40243
- */
40244
- const collectSidecarProbesForFiles = async (input) => {
40245
- const buildProbes = (file) => {
40246
- const absoluteFilePath = Path.resolve(input.rootDirectory, file);
40247
- let trace;
40248
- try {
40249
- trace = collectCrossFileDependencyProbes({
40250
- absoluteFilePath,
40251
- sourceText: NFS.readFileSync(absoluteFilePath, "utf8"),
40252
- ruleIds: input.boundedSidecarRuleIds
40253
- });
40254
- } catch {
40255
- return null;
40256
- }
40257
- if (trace === null) return null;
40258
- const probes = [];
40259
- for (const contentPath of trace.contentPaths) {
40260
- const relativePath = input.probeAnswers.toRelativePath(contentPath);
40261
- probes.push({
40262
- kind: "content",
40263
- path: relativePath,
40264
- answer: input.probeAnswers.answerFor("content", relativePath)
40265
- });
40266
- }
40267
- for (const existencePath of trace.existencePaths) {
40268
- const relativePath = input.probeAnswers.toRelativePath(existencePath);
40269
- probes.push({
40270
- kind: "exists",
40271
- path: relativePath,
40272
- answer: input.probeAnswers.answerFor("exists", relativePath)
40273
- });
40274
- }
40275
- return probes;
40276
- };
40277
- const probesByFile = /* @__PURE__ */ new Map();
40278
- let collectSliceStartedAt = performance.now();
40279
- for (const file of input.files) {
40280
- probesByFile.set(file, buildProbes(file));
40281
- if (performance.now() - collectSliceStartedAt >= 12) {
40282
- await yieldToEventLoop();
40283
- collectSliceStartedAt = performance.now();
40284
- }
40285
- }
40286
- return probesByFile;
40287
- };
40288
- /**
40289
- * Stores each freshly-linted file's probe set + cross-file diagnostics so
40290
- * the next scan can replay instead of re-linting.
40291
- */
40292
- const storeSidecarEntries = (input) => {
40293
- let didStoreAnyEntry = false;
40294
- for (const pass of input.storablePasses) {
40295
- if (!pass.isTrusted) continue;
40296
- const diagnosticsByFile = attributeDiagnosticsToFiles(pass.diagnostics, pass.files);
40297
- if (diagnosticsByFile === null) continue;
40298
- for (const file of pass.files) {
40299
- const cacheKey = input.cacheKeyByFile.get(file);
40300
- if (cacheKey === void 0) continue;
40301
- const probes = pass.probesByFile.get(file);
40302
- if (probes === void 0 || probes === null) continue;
40303
- input.sidecarCache.store(cacheKey, {
40304
- probes,
40305
- diagnostics: diagnosticsByFile.get(file) ?? []
40306
- });
40307
- didStoreAnyEntry = true;
40308
- }
40309
- }
40310
- if (didStoreAnyEntry) input.sidecarCache.persist();
40311
- };
40312
40058
  const REACT_HOOKS_JS_DROP_PREFIX = "React Compiler rules (react-hooks-js/*) skipped — eslint-plugin-react-hooks failed to load in this environment";
40313
40059
  /**
40314
40060
  * Detects an oxlint config-load crash caused by the optional
@@ -40350,7 +40096,7 @@ const reactHooksJsPluginDropNote = (error) => {
40350
40096
  * 6. always restore disable directives + clean up the temp dir
40351
40097
  */
40352
40098
  const runOxlint = async (options) => {
40353
- 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;
40099
+ 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;
40354
40100
  const serverAuthFunctionNames = Array.isArray(userConfig?.serverAuthFunctionNames) ? userConfig.serverAuthFunctionNames.filter((entry) => typeof entry === "string" && entry.length > 0) : void 0;
40355
40101
  const severityControls = buildRuleSeverityControls(userConfig);
40356
40102
  validateRuleRegistration();
@@ -40368,8 +40114,7 @@ const runOxlint = async (options) => {
40368
40114
  severityControls,
40369
40115
  userPlugins,
40370
40116
  disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
40371
- ruleSelection: overrides.ruleSelection,
40372
- sidecarRuleIdFilter: overrides.sidecarRuleIdFilter
40117
+ ruleSelection: overrides.ruleSelection
40373
40118
  });
40374
40119
  const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
40375
40120
  const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
@@ -40472,18 +40217,15 @@ const runOxlint = async (options) => {
40472
40217
  });
40473
40218
  const cache = createFileLintCache(resolveReactDoctorCacheDir(rootDirectory), rulesetHash);
40474
40219
  const cacheKeyByFile = /* @__PURE__ */ new Map();
40475
- const contentHashByRelativePath = /* @__PURE__ */ new Map();
40476
40220
  const missFiles = [];
40477
40221
  const replayedDiagnostics = [];
40478
40222
  let hashSliceStartedAt = performance.now();
40479
40223
  for (const candidateFile of candidateFiles) {
40480
- const absoluteCandidatePath = Path.resolve(rootDirectory, candidateFile);
40481
- const contentHash = hashFileContents(absoluteCandidatePath);
40224
+ const contentHash = hashFileContents(Path.resolve(rootDirectory, candidateFile));
40482
40225
  if (contentHash === null) {
40483
40226
  missFiles.push(candidateFile);
40484
40227
  continue;
40485
40228
  }
40486
- contentHashByRelativePath.set(Path.relative(rootDirectory, absoluteCandidatePath).replaceAll("\\", "/"), contentHash);
40487
40229
  const cacheKey = `${candidateFile.replaceAll("\\", "/")}\u0000${contentHash}`;
40488
40230
  cacheKeyByFile.set(candidateFile, cacheKey);
40489
40231
  const cachedDiagnostics = cache.lookup(cacheKey);
@@ -40495,111 +40237,43 @@ const runOxlint = async (options) => {
40495
40237
  }
40496
40238
  }
40497
40239
  const cacheHitFileCount = candidateFiles.length - missFiles.length;
40498
- const enabledSidecarRuleIds = Object.keys(buildConfig({
40499
- extendsPaths: [],
40500
- ruleSelection: "sidecar"
40501
- }).rules).map((ruleKey) => ruleKey.slice(ruleKey.lastIndexOf("/") + 1)).filter((ruleId) => CROSS_FILE_RULE_IDS.has(ruleId));
40502
- const boundedSidecarRuleIds = enabledSidecarRuleIds.filter((ruleId) => CROSS_FILE_DEPENDENCY_COLLECTORS.has(ruleId));
40503
- const boundedSidecarRuleIdSet = new Set(boundedSidecarRuleIds);
40504
- const unboundedSidecarRuleIds = enabledSidecarRuleIds.filter((ruleId) => !CROSS_FILE_DEPENDENCY_COLLECTORS.has(ruleId));
40505
- const useSidecarCache = sidecarLintCacheEnabled && boundedSidecarRuleIds.length > 0;
40506
- const sidecarCache = useSidecarCache ? createSidecarLintCache(resolveReactDoctorCacheDir(rootDirectory), computeRulesetHash({
40507
- config: buildConfig({
40508
- extendsPaths: [],
40509
- ruleSelection: "sidecar",
40510
- sidecarRuleIdFilter: boundedSidecarRuleIdSet
40511
- }),
40512
- toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
40513
- ignorePatterns: combinedPatterns,
40514
- tsconfigContent
40515
- })) : null;
40516
- const probeAnswers = createSidecarProbeAnswerResolver({
40517
- rootDirectory,
40518
- contentHashByRelativePath
40519
- });
40520
- const missProbesTask = sidecarCache === null ? null : collectSidecarProbesForFiles({
40521
- files: missFiles,
40522
- rootDirectory,
40523
- boundedSidecarRuleIds,
40524
- probeAnswers
40525
- });
40526
40240
  const fullResult = await runConfigOverFiles((overrides) => buildConfig({
40527
40241
  extendsPaths: [],
40528
40242
  disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin
40529
40243
  }), "oxlintrc.full.json", missFiles, options.onFileProgress && ((scannedFileCount) => options.onFileProgress?.(scannedFileCount, candidateFiles.length)));
40530
40244
  const missFileSet = new Set(missFiles);
40531
- const hitFiles = candidateFiles.filter((candidateFile) => !missFileSet.has(candidateFile));
40532
- const sidecarReplayedDiagnostics = [];
40533
- const sidecarLintFiles = [];
40534
- if (sidecarCache === null) sidecarLintFiles.push(...hitFiles);
40535
- else {
40536
- let verifySliceStartedAt = performance.now();
40537
- for (const hitFile of hitFiles) {
40538
- const cacheKey = cacheKeyByFile.get(hitFile);
40539
- const entry = cacheKey === void 0 ? null : sidecarCache.lookup(cacheKey);
40540
- if (entry !== null && entry.probes.every((probe) => probeAnswers.answerFor(probe.kind, probe.path) === probe.answer)) sidecarReplayedDiagnostics.push(...entry.diagnostics);
40541
- else sidecarLintFiles.push(hitFile);
40542
- if (performance.now() - verifySliceStartedAt >= 12) {
40543
- await yieldToEventLoop();
40544
- verifySliceStartedAt = performance.now();
40545
- }
40546
- }
40547
- }
40548
- const sidecarProbesTask = sidecarCache === null ? null : collectSidecarProbesForFiles({
40549
- files: sidecarLintFiles,
40550
- rootDirectory,
40551
- boundedSidecarRuleIds,
40552
- probeAnswers
40553
- });
40554
- const sidecarReplayedFileCount = hitFiles.length - sidecarLintFiles.length;
40555
- if (sidecarReplayedFileCount > 0) options.onFileProgress?.(missFiles.length + sidecarReplayedFileCount, candidateFiles.length);
40556
- const boundedSidecarResult = await runConfigOverFiles(() => buildConfig({
40557
- extendsPaths: [],
40558
- ruleSelection: "sidecar",
40559
- ...useSidecarCache ? { sidecarRuleIdFilter: boundedSidecarRuleIdSet } : {}
40560
- }), "oxlintrc.sidecar.json", sidecarLintFiles, options.onFileProgress && ((scannedFileCount) => options.onFileProgress?.(missFiles.length + sidecarReplayedFileCount + scannedFileCount, candidateFiles.length)));
40561
- const unboundedSidecarResult = useSidecarCache && unboundedSidecarRuleIds.length > 0 ? await runConfigOverFiles(() => buildConfig({
40245
+ const sidecarResult = await runConfigOverFiles(() => buildConfig({
40562
40246
  extendsPaths: [],
40563
- ruleSelection: "sidecar",
40564
- sidecarRuleIdFilter: new Set(unboundedSidecarRuleIds)
40565
- }), "oxlintrc.sidecar-unbounded.json", hitFiles, void 0) : {
40566
- diagnostics: [],
40567
- didDropReactHooksJsPlugin: false,
40568
- hadPartialFailure: false
40569
- };
40247
+ ruleSelection: "sidecar"
40248
+ }), "oxlintrc.sidecar.json", candidateFiles.filter((candidateFile) => !missFileSet.has(candidateFile)), options.onFileProgress && ((scannedFileCount) => options.onFileProgress?.(missFiles.length + scannedFileCount, candidateFiles.length)));
40570
40249
  const freshCacheableDiagnostics = [];
40571
40250
  for (const diagnostic of fullResult.diagnostics) if (!CROSS_FILE_RULE_IDS.has(diagnostic.rule)) freshCacheableDiagnostics.push(diagnostic);
40572
40251
  onCacheStats?.(cacheHitFileCount, candidateFiles.length);
40573
- if (sidecarCache !== null) onSidecarStats?.(hitFiles.length - sidecarLintFiles.length, hitFiles.length);
40574
- const freshDiagnosticsByFile = attributeDiagnosticsToFiles(freshCacheableDiagnostics, missFiles);
40575
- if (!fullResult.didDropReactHooksJsPlugin && !fullResult.hadPartialFailure && freshDiagnosticsByFile !== null) {
40252
+ const missFileByNormalizedPath = /* @__PURE__ */ new Map();
40253
+ for (const missFile of missFiles) missFileByNormalizedPath.set(missFile.replaceAll("\\", "/"), missFile);
40254
+ const freshDiagnosticsByFile = /* @__PURE__ */ new Map();
40255
+ let isAttributionSound = true;
40256
+ for (const diagnostic of freshCacheableDiagnostics) {
40257
+ const missFile = missFileByNormalizedPath.get(diagnostic.filePath);
40258
+ if (missFile === void 0) {
40259
+ isAttributionSound = false;
40260
+ break;
40261
+ }
40262
+ const fileDiagnostics = freshDiagnosticsByFile.get(missFile) ?? [];
40263
+ fileDiagnostics.push(diagnostic);
40264
+ freshDiagnosticsByFile.set(missFile, fileDiagnostics);
40265
+ }
40266
+ if (!fullResult.didDropReactHooksJsPlugin && !fullResult.hadPartialFailure && isAttributionSound) {
40576
40267
  for (const missFile of missFiles) {
40577
40268
  const cacheKey = cacheKeyByFile.get(missFile);
40578
40269
  if (cacheKey !== void 0) cache.store(cacheKey, freshDiagnosticsByFile.get(missFile) ?? []);
40579
40270
  }
40580
40271
  cache.persist();
40581
40272
  }
40582
- if (sidecarCache !== null && missProbesTask !== null && sidecarProbesTask !== null) storeSidecarEntries({
40583
- sidecarCache,
40584
- cacheKeyByFile,
40585
- storablePasses: [{
40586
- files: sidecarLintFiles,
40587
- diagnostics: boundedSidecarResult.diagnostics,
40588
- isTrusted: !boundedSidecarResult.hadPartialFailure,
40589
- probesByFile: await sidecarProbesTask
40590
- }, {
40591
- files: missFiles,
40592
- diagnostics: fullResult.diagnostics.filter((diagnostic) => boundedSidecarRuleIdSet.has(diagnostic.rule)),
40593
- isTrusted: !fullResult.didDropReactHooksJsPlugin && !fullResult.hadPartialFailure,
40594
- probesByFile: await missProbesTask
40595
- }]
40596
- });
40597
40273
  return dedupeDiagnostics([
40598
40274
  ...replayedDiagnostics,
40599
40275
  ...fullResult.diagnostics,
40600
- ...sidecarReplayedDiagnostics,
40601
- ...boundedSidecarResult.diagnostics,
40602
- ...unboundedSidecarResult.diagnostics
40276
+ ...sidecarResult.diagnostics
40603
40277
  ]);
40604
40278
  }
40605
40279
  const baseArgs = makeBaseArgs(configPath);
@@ -40697,7 +40371,6 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
40697
40371
  const concurrency = yield* OxlintConcurrency;
40698
40372
  const lintBatchOrdering = yield* LintBatchOrdering;
40699
40373
  const perFileLintCacheEnabled = yield* PerFileLintCacheEnabled;
40700
- const sidecarLintCacheEnabled = yield* SidecarLintCacheEnabled;
40701
40374
  const collectedFailures = [];
40702
40375
  const diagnostics = yield* tryPromise({
40703
40376
  try: (signal) => runOxlint({
@@ -40716,9 +40389,7 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
40716
40389
  },
40717
40390
  onFileProgress: input.onFileProgress,
40718
40391
  perFileLintCacheEnabled,
40719
- sidecarLintCacheEnabled,
40720
40392
  onCacheStats: input.onCacheStats,
40721
- onSidecarStats: input.onSidecarStats,
40722
40393
  spawnTimeoutMs,
40723
40394
  outputMaxBytes,
40724
40395
  concurrency,
@@ -41528,8 +41199,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
41528
41199
  let lastReportedTotalFileCount = 0;
41529
41200
  let lintCacheHitFileCount = null;
41530
41201
  let lintCacheTotalFileCount = null;
41531
- let lintSidecarReplayedFileCount = null;
41532
- let lintSidecarTotalFileCount = null;
41533
41202
  let deadCodeCacheHit = null;
41534
41203
  let deadCodeSummaryCacheHits = null;
41535
41204
  let deadCodeSummaryCacheMisses = null;
@@ -41552,10 +41221,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
41552
41221
  lintCacheHitFileCount = cacheHitFileCount;
41553
41222
  lintCacheTotalFileCount = totalConsideredFileCount;
41554
41223
  },
41555
- onSidecarStats: (sidecarReplayedFileCount, sidecarConsideredFileCount) => {
41556
- lintSidecarReplayedFileCount = sidecarReplayedFileCount;
41557
- lintSidecarTotalFileCount = sidecarConsideredFileCount;
41558
- },
41559
41224
  deadlineEpochMs: input.deadlineEpochMs
41560
41225
  }).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
41561
41226
  yield* set(lintFailure, {
@@ -41666,8 +41331,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
41666
41331
  securityScanFailed,
41667
41332
  lintCacheHitFileCount,
41668
41333
  lintCacheTotalFileCount,
41669
- lintSidecarReplayedFileCount,
41670
- lintSidecarTotalFileCount,
41671
41334
  deadCodeCacheHit: lintFailureState.didFail ? null : deadCodeCacheHit,
41672
41335
  deadCodeSummaryCacheHits: lintFailureState.didFail ? null : deadCodeSummaryCacheHits,
41673
41336
  deadCodeSummaryCacheMisses: lintFailureState.didFail ? null : deadCodeSummaryCacheMisses,
@@ -44050,5 +43713,5 @@ const startLanguageServer = () => {
44050
43713
  };
44051
43714
  //#endregion
44052
43715
  export { startLanguageServer };
44053
- !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]="17a3f533-8248-511a-859b-584197cefba6")}catch(e){}}();
44054
- //# debugId=17a3f533-8248-511a-859b-584197cefba6
43716
+ !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]="c5ee1532-72b6-5d6f-83ae-7bb4dccf2226")}catch(e){}}();
43717
+ //# debugId=c5ee1532-72b6-5d6f-83ae-7bb4dccf2226
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor",
3
- "version": "0.6.3-dev.bdf8074",
3
+ "version": "0.6.3-dev.ce49250",
4
4
  "description": "Your agent writes bad React. This catches it",
5
5
  "keywords": [
6
6
  "accessibility",
@@ -66,7 +66,7 @@
66
66
  "vscode-uri": "^3.1.0",
67
67
  "yaml": "^2.9.0",
68
68
  "deslop-js": "0.6.3",
69
- "oxlint-plugin-react-doctor": "0.6.3-dev.bdf8074"
69
+ "oxlint-plugin-react-doctor": "0.6.3-dev.ce49250"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/babel__code-frame": "^7.27.0",