react-doctor 0.7.6-dev.c1916d5 → 0.7.6-dev.c5a06bd

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]="4c1e681e-4dee-5ffd-a4ba-69b876f47147")}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]="9a5c1a3c-7706-5a23-bca4-ddddd2f085af")}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";
@@ -39594,49 +39594,6 @@ const assignFixGroups = (diagnostics) => {
39594
39594
  };
39595
39595
  });
39596
39596
  };
39597
- const DERIVED_STATE_RULE_PRIORITY = new Map([
39598
- ["no-initialize-state", 0],
39599
- ["no-adjust-state-on-prop-change", 1],
39600
- ["no-derived-state", 2],
39601
- ["no-derived-state-effect", 3]
39602
- ]);
39603
- const buildDiagnosticSiteKey = (diagnostic) => `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}`;
39604
- const isReactDoctorRulesOfHooksDiagnostic = (diagnostic) => diagnostic.plugin === "react-doctor" && diagnostic.rule === "rules-of-hooks";
39605
- const isReactCompilerHooksDiagnostic = (diagnostic) => diagnostic.plugin === "react-hooks-js" && diagnostic.rule === "hooks";
39606
- const doDiagnosticSpansOverlap = (first, second) => {
39607
- if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
39608
- if (first.offset !== void 0 && second.offset !== void 0) {
39609
- const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
39610
- const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
39611
- return first.offset < secondEnd && second.offset < firstEnd;
39612
- }
39613
- const firstEndLine = first.endLine ?? first.line;
39614
- const secondEndLine = second.endLine ?? second.line;
39615
- return first.line <= secondEndLine && second.line <= firstEndLine;
39616
- };
39617
- const keepHighestSeverity = (survivingDiagnostic, collapsedDiagnostic) => collapsedDiagnostic.severity === "error" && survivingDiagnostic.severity !== "error" ? {
39618
- ...survivingDiagnostic,
39619
- severity: "error"
39620
- } : survivingDiagnostic;
39621
- const dedupeRelatedDiagnostics = (diagnostics) => {
39622
- const reactDoctorHookSites = new Set(diagnostics.filter(isReactDoctorRulesOfHooksDiagnostic).map(buildDiagnosticSiteKey));
39623
- const uniqueDiagnostics = [];
39624
- for (const diagnostic of diagnostics) {
39625
- if (isReactCompilerHooksDiagnostic(diagnostic) && reactDoctorHookSites.has(buildDiagnosticSiteKey(diagnostic))) continue;
39626
- const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
39627
- if (priority !== void 0) {
39628
- const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
39629
- if (overlappingDiagnosticIndex >= 0) {
39630
- const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
39631
- const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
39632
- if (existingDiagnostic && existingPriority !== void 0) uniqueDiagnostics[overlappingDiagnosticIndex] = priority < existingPriority ? keepHighestSeverity(diagnostic, existingDiagnostic) : keepHighestSeverity(existingDiagnostic, diagnostic);
39633
- continue;
39634
- }
39635
- }
39636
- uniqueDiagnostics.push(diagnostic);
39637
- }
39638
- return uniqueDiagnostics;
39639
- };
39640
39597
  const compareStrings = (left, right) => left < right ? -1 : left > right ? 1 : 0;
39641
39598
  const sortDiagnosticsStable = (diagnostics) => [...diagnostics].sort((left, right) => compareStrings(left.filePath, right.filePath) || left.line - right.line || left.column - right.column || compareStrings(left.plugin, right.plugin) || compareStrings(left.rule, right.rule) || compareStrings(left.severity, right.severity) || compareStrings(left.message, right.message));
39642
39599
  const getDirectDependencyNames = (packageJson) => new Set([...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.devDependencies ?? {})]);
@@ -41866,7 +41823,6 @@ const parseGithubViewerPermission = (stdout) => {
41866
41823
  return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
41867
41824
  };
41868
41825
  const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
41869
- const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
41870
41826
  /**
41871
41827
  * `Git` wraps every `git`-via-subprocess call react-doctor makes
41872
41828
  * behind a `Context.Service`. The production layer (`layerNode`)
@@ -41937,14 +41893,6 @@ var Git = class Git extends Service()("react-doctor/Git") {
41937
41893
  args,
41938
41894
  directory
41939
41895
  });
41940
- const listUntrackedFilePaths = (directory, includePaths = []) => runGit(directory, [
41941
- "ls-files",
41942
- "-z",
41943
- "--others",
41944
- "--exclude-standard",
41945
- ...includePaths.length > 0 ? ["--", ...includePaths] : []
41946
- ]).pipe(map$3((result) => result.status === 0 ? splitNullSeparated(result.stdout) : null));
41947
- const mergeUntracked = (directory, trackedFilePaths, includeUntracked) => includeUntracked ? listUntrackedFilePaths(directory).pipe(map$3((untracked) => untracked === null ? trackedFilePaths : [...new Set([...trackedFilePaths, ...untracked])])) : succeed$2(trackedFilePaths);
41948
41896
  const currentBranch = (directory) => runGit(directory, [
41949
41897
  "rev-parse",
41950
41898
  "--abbrev-ref",
@@ -42067,7 +42015,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
42067
42015
  githubViewerPermission,
42068
42016
  branchExists,
42069
42017
  mergeBase,
42070
- diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
42018
+ diffSelection: ({ directory, explicitBaseBranch }) => gen(function* () {
42071
42019
  if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
42072
42020
  if (explicitBaseBranch !== void 0) {
42073
42021
  const range = parseGitDiffRange(explicitBaseBranch);
@@ -42096,7 +42044,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
42096
42044
  "HEAD"
42097
42045
  ]);
42098
42046
  if (uncommitted.status !== 0) return null;
42099
- const files = yield* mergeUntracked(directory, splitNullSeparated(uncommitted.stdout), includeUntracked);
42047
+ const files = splitNullSeparated(uncommitted.stdout);
42100
42048
  if (files.length === 0) return null;
42101
42049
  return {
42102
42050
  currentBranch: resolvedCurrentBranch,
@@ -42126,7 +42074,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
42126
42074
  currentBranch: resolvedCurrentBranch,
42127
42075
  baseBranch,
42128
42076
  diffBaseRef: mergeBaseRef,
42129
- changedFiles: yield* mergeUntracked(directory, splitNullSeparated(diff.stdout), includeUntracked),
42077
+ changedFiles: splitNullSeparated(diff.stdout),
42130
42078
  isCurrentChanges: false
42131
42079
  };
42132
42080
  }).pipe(withSpan("Git.diffSelection")),
@@ -42172,7 +42120,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
42172
42120
  stdout: result.stdout
42173
42121
  };
42174
42122
  }).pipe(withSpan("Git.grep")),
42175
- changedLineRanges: ({ directory, baseRef, cached, files, includeUntracked = false }) => gen(function* () {
42123
+ changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
42176
42124
  if (files.length === 0) return [];
42177
42125
  if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
42178
42126
  const result = yield* runGit(directory, [
@@ -42186,14 +42134,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
42186
42134
  ...files
42187
42135
  ]);
42188
42136
  if (result.status !== 0) return null;
42189
- const changedLineRanges = parseChangedLineRanges(result.stdout);
42190
- if (cached || !includeUntracked) return changedLineRanges;
42191
- const untrackedFilePaths = yield* listUntrackedFilePaths(directory, files);
42192
- if (untrackedFilePaths === null) return changedLineRanges;
42193
- return [...changedLineRanges, ...untrackedFilePaths.map((file) => ({
42194
- file,
42195
- ranges: [[1, UNTRACKED_FILE_LAST_LINE]]
42196
- }))];
42137
+ return parseChangedLineRanges(result.stdout);
42197
42138
  }).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
42198
42139
  });
42199
42140
  })).pipe(provide$2(layer$3.pipe(provide$2(mergeAll$1(layer$2, layer$1)))));
@@ -42778,13 +42719,13 @@ const encodeSidecarFileEntry = (entry) => ({
42778
42719
  diagnostics: entry.diagnostics
42779
42720
  });
42780
42721
  const emptyCache = () => ({
42781
- version: 2,
42722
+ version: 1,
42782
42723
  rulesets: {}
42783
42724
  });
42784
42725
  const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
42785
42726
  const entries = /* @__PURE__ */ new Map();
42786
42727
  const persisted = failOpenReadJson(cacheFilePath, emptyCache());
42787
- if (persisted.version !== 2 || !isRecord$1(persisted.rulesets)) return entries;
42728
+ if (persisted.version !== 1 || !isRecord$1(persisted.rulesets)) return entries;
42788
42729
  const bucket = persisted.rulesets[rulesetHash];
42789
42730
  if (!isRecord$1(bucket) || !isRecord$1(bucket.files)) return entries;
42790
42731
  for (const [fileKey, rawEntry] of Object.entries(bucket.files)) {
@@ -42804,7 +42745,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
42804
42745
  },
42805
42746
  persist: () => {
42806
42747
  const onDisk = failOpenReadJson(cacheFilePath, emptyCache());
42807
- const rulesets = onDisk.version === 2 && isRecord$1(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
42748
+ const rulesets = onDisk.version === 1 && isRecord$1(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
42808
42749
  const existingBucket = rulesets[rulesetHash];
42809
42750
  const existingFiles = isRecord$1(existingBucket) && isRecord$1(existingBucket.files) ? existingBucket.files : {};
42810
42751
  const ourFiles = {};
@@ -42818,7 +42759,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
42818
42759
  files: Object.fromEntries(cappedEntries)
42819
42760
  };
42820
42761
  atomicWriteJson(cacheFilePath, {
42821
- version: 2,
42762
+ version: 1,
42822
42763
  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))
42823
42764
  });
42824
42765
  }
@@ -42869,6 +42810,23 @@ const resolveTsConfigRelativePath = (rootDirectory) => {
42869
42810
  for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
42870
42811
  return null;
42871
42812
  };
42813
+ const DERIVED_STATE_RULE_PRIORITY = new Map([
42814
+ ["no-initialize-state", 0],
42815
+ ["no-adjust-state-on-prop-change", 1],
42816
+ ["no-derived-state", 2],
42817
+ ["no-derived-state-effect", 3]
42818
+ ]);
42819
+ const doDiagnosticSpansOverlap = (first, second) => {
42820
+ if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
42821
+ if (first.offset !== void 0 && second.offset !== void 0) {
42822
+ const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
42823
+ const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
42824
+ return first.offset < secondEnd && second.offset < firstEnd;
42825
+ }
42826
+ const firstEndLine = first.endLine ?? first.line;
42827
+ const secondEndLine = second.endLine ?? second.line;
42828
+ return first.line <= secondEndLine && second.line <= firstEndLine;
42829
+ };
42872
42830
  const dedupeDiagnostics = (diagnostics) => {
42873
42831
  const seenKeys = /* @__PURE__ */ new Set();
42874
42832
  const uniqueDiagnostics = [];
@@ -42876,6 +42834,16 @@ const dedupeDiagnostics = (diagnostics) => {
42876
42834
  const key = `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}\u0000${diagnostic.plugin}\u0000${diagnostic.rule}\u0000${diagnostic.severity}\u0000${diagnostic.message}`;
42877
42835
  if (seenKeys.has(key)) continue;
42878
42836
  seenKeys.add(key);
42837
+ const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
42838
+ if (priority !== void 0) {
42839
+ const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && candidate.severity === diagnostic.severity && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
42840
+ if (overlappingDiagnosticIndex >= 0) {
42841
+ const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
42842
+ const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
42843
+ if (existingPriority !== void 0 && priority < existingPriority) uniqueDiagnostics[overlappingDiagnosticIndex] = diagnostic;
42844
+ continue;
42845
+ }
42846
+ }
42879
42847
  uniqueDiagnostics.push(diagnostic);
42880
42848
  }
42881
42849
  return uniqueDiagnostics;
@@ -43675,11 +43643,11 @@ const buildRelatedLocations = (labels, filePath) => {
43675
43643
  }
43676
43644
  return related;
43677
43645
  };
43678
- const isOxlintSpan = (value) => isRecord$1(value) && typeof value.offset === "number" && typeof value.length === "number" && typeof value.line === "number" && typeof value.column === "number";
43679
- const isOxlintLabel = (value) => isRecord$1(value) && isOxlintSpan(value.span);
43680
- const isMappableOxlintDiagnostic = (value) => isRecord$1(value) && typeof value.code === "string" && value.code.length > 0 && typeof value.filename === "string" && value.filename.length > 0 && (value.severity === "warning" || value.severity === "error") && Array.isArray(value.labels) && value.labels.every(isOxlintLabel);
43681
- const isEngineFailureDiagnostic = (value) => !isRecord$1(value) || typeof value.filename !== "string" || value.filename.length === 0;
43682
- const isOxlintOutput = (value) => isRecord$1(value) && Array.isArray(value.diagnostics);
43646
+ const isOxlintOutput = (value) => {
43647
+ if (typeof value !== "object" || value === null) return false;
43648
+ const candidate = value;
43649
+ return Array.isArray(candidate.diagnostics);
43650
+ };
43683
43651
  /**
43684
43652
  * Parses one oxlint subprocess's stdout into a flat `Diagnostic[]`.
43685
43653
  * Tolerates the leading-notice-line shape oxlint sometimes prints
@@ -43698,8 +43666,6 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
43698
43666
  throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
43699
43667
  }
43700
43668
  if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
43701
- const engineFailureDiagnostic = parsed.diagnostics.find(isEngineFailureDiagnostic);
43702
- if (engineFailureDiagnostic !== void 0) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: JSON.stringify(engineFailureDiagnostic).slice(0, 600) }) });
43703
43669
  const minifiedFileCache = /* @__PURE__ */ new Map();
43704
43670
  const sourceBufferCache = /* @__PURE__ */ new Map();
43705
43671
  const sourceLinesCache = /* @__PURE__ */ new Map();
@@ -43734,7 +43700,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
43734
43700
  minifiedFileCache.set(absolutePath, minified);
43735
43701
  return minified;
43736
43702
  };
43737
- return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
43703
+ return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => diagnostic.code && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
43738
43704
  const { plugin, rule } = parseRuleCode(diagnostic.code);
43739
43705
  const primaryLabel = diagnostic.labels[0];
43740
43706
  const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
@@ -45569,8 +45535,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
45569
45535
  respectInlineDisables: input.respectInlineDisables,
45570
45536
  showWarnings
45571
45537
  });
45572
- const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(filterMapNullable(transform.apply)));
45573
- const applyPerElementPipeline = (rawStream) => filterPerElementPipeline(rawStream).pipe(tap((diagnostic) => reporterService.emit(diagnostic)));
45538
+ const applyPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(filterMapNullable(transform.apply)), tap((diagnostic) => reporterService.emit(diagnostic)));
45574
45539
  const envCollected = yield* runCollect(applyPerElementPipeline(fromIterable$1(isDiffMode ? [] : [
45575
45540
  ...checkReducedMotion(scanDirectory),
45576
45541
  ...checkPnpmHardening(scanDirectory),
@@ -45699,7 +45664,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
45699
45664
  });
45700
45665
  return empty$4;
45701
45666
  }))));
45702
- const lintCollected = dedupeRelatedDiagnostics(yield* runCollect(filterPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream)).pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
45667
+ const lintCollected = yield* runCollect(applyPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream)).pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
45703
45668
  onNone: () => set(lintFailure, {
45704
45669
  didFail: true,
45705
45670
  reason: `Lint analysis exceeded ${lintPhaseTimeoutMs / MILLISECONDS_PER_SECOND}s and was skipped.`,
@@ -45707,8 +45672,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
45707
45672
  reasonKind: null
45708
45673
  }).pipe(as([])),
45709
45674
  onSome: succeed$2
45710
- }))));
45711
- yield* forEach$1(lintCollected, reporterService.emit, { discard: true });
45675
+ })));
45712
45676
  const lintFailureState = yield* get$2(lintFailure);
45713
45677
  yield* afterLint(lintFailureState.didFail);
45714
45678
  if (lintFailureState.didFail) yield* scanProgress.fail(formatLintFailText(lintFailureState.reasonTag, process.version));
@@ -46302,11 +46266,10 @@ const computeDiagnosticDelta = (input) => {
46302
46266
  * render diff-base mistakes (`GitBaseBranchInvalid` /
46303
46267
  * `GitBaseBranchMissing`) as clean user errors instead of crashes.
46304
46268
  */
46305
- const getDiffInfo = (directory, explicitBaseBranch, includeUntracked) => runPromise(gen(function* () {
46269
+ const getDiffInfo = (directory, explicitBaseBranch) => runPromise(gen(function* () {
46306
46270
  const selection = yield* (yield* Git).diffSelection({
46307
46271
  directory,
46308
- explicitBaseBranch,
46309
- includeUntracked
46272
+ explicitBaseBranch
46310
46273
  });
46311
46274
  if (selection === null) return null;
46312
46275
  return {
@@ -46642,43 +46605,6 @@ const isNonInteractiveEnvironment = () => NON_INTERACTIVE_ENVIRONMENT_VARIABLES.
46642
46605
  //#region src/cli/utils/constants.ts
46643
46606
  const REACT_DOCTOR_CONFIG_PROJECT_NAME = "react-doctor";
46644
46607
  const STAGED_FILES_TEMP_DIR_PREFIX = "react-doctor-staged-";
46645
- const STAGED_SNAPSHOT_ADDITIONAL_CONFIG_FILENAMES = [
46646
- ".babelrc",
46647
- ".babelrc.json",
46648
- ".eslintignore",
46649
- ".eslintrc.json",
46650
- ".oxlintignore",
46651
- "app.config.cjs",
46652
- "app.config.js",
46653
- "app.config.json",
46654
- "app.config.mjs",
46655
- "app.config.ts",
46656
- "app.json",
46657
- "babel.config.cjs",
46658
- "babel.config.js",
46659
- "babel.config.json",
46660
- "babel.config.mjs",
46661
- "eslint.config.cjs",
46662
- "eslint.config.cts",
46663
- "eslint.config.js",
46664
- "eslint.config.mjs",
46665
- "eslint.config.mts",
46666
- "eslint.config.ts",
46667
- "next.config.cjs",
46668
- "next.config.js",
46669
- "next.config.mjs",
46670
- "next.config.ts",
46671
- "oxlint.config.json",
46672
- "react-doctor.config.json",
46673
- "vite.config.cjs",
46674
- "vite.config.cts",
46675
- "vite.config.js",
46676
- "vite.config.mjs",
46677
- "vite.config.mts",
46678
- "vite.config.ts",
46679
- "vitest.config.js",
46680
- "vitest.config.ts"
46681
- ];
46682
46608
  const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
46683
46609
  const SCAN_RESULT_CACHE_FILENAME = "scan-cache.json";
46684
46610
  const RUN_GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
@@ -46693,7 +46619,6 @@ const METRIC = {
46693
46619
  cliInvoked: "cli.invoked",
46694
46620
  cliError: "cli.error",
46695
46621
  cliEnvironmentError: "cli.env_error",
46696
- stagedSnapshotDivergence: "staged.snapshot_divergence",
46697
46622
  projectDetected: "project.detected",
46698
46623
  projectPathSelected: "project.path_selected",
46699
46624
  projectConfigSelected: "project.config_selected",
@@ -46774,7 +46699,7 @@ const makeNoopConsole = () => ({
46774
46699
  });
46775
46700
  //#endregion
46776
46701
  //#region src/cli/utils/version.ts
46777
- const VERSION = "0.7.6-dev.c1916d5";
46702
+ const VERSION = "0.7.6-dev.c5a06bd";
46778
46703
  //#endregion
46779
46704
  //#region src/cli/utils/json-mode.ts
46780
46705
  let context = null;
@@ -47143,13 +47068,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
47143
47068
  * uploads source-map artifacts under, so stack frames symbolicate. Honors the
47144
47069
  * standard `SENTRY_RELEASE` override.
47145
47070
  */
47146
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.6-dev.c1916d5`;
47071
+ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.6-dev.c5a06bd`;
47147
47072
  /**
47148
47073
  * Deployment environment shown in Sentry's environment filter. Defaults to
47149
47074
  * `production` for tagged releases and `development` for dev/unbuilt versions,
47150
47075
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
47151
47076
  */
47152
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.6-dev.c1916d5") ? "development" : "production");
47077
+ const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.6-dev.c5a06bd") ? "development" : "production");
47153
47078
  /**
47154
47079
  * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
47155
47080
  * (set to `0` to disable tracing) and falls back to
@@ -47341,7 +47266,7 @@ const NON_BLOCKING_REACT_DOCTOR_COMMAND = [
47341
47266
  "fi"
47342
47267
  ].join(" ");
47343
47268
  const PACKAGE_JSON_FILE_NAME = "package.json";
47344
- const runGitRaw = (projectRoot, args) => {
47269
+ const runGit = (projectRoot, args) => {
47345
47270
  try {
47346
47271
  return execFileSync("git", [...args], {
47347
47272
  cwd: projectRoot,
@@ -47352,12 +47277,11 @@ const runGitRaw = (projectRoot, args) => {
47352
47277
  "ignore"
47353
47278
  ],
47354
47279
  maxBuffer: RUN_GIT_MAX_BUFFER_BYTES
47355
- });
47280
+ }).trim();
47356
47281
  } catch {
47357
47282
  return null;
47358
47283
  }
47359
47284
  };
47360
- const runGit = (projectRoot, args) => runGitRaw(projectRoot, args)?.trim() ?? null;
47361
47285
  const resolveGitPath = (baseDirectory, value) => Path.isAbsolute(value) ? value : Path.resolve(baseDirectory, value);
47362
47286
  const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
47363
47287
  const getPackageJsonPath = (projectRoot) => Path.join(projectRoot, PACKAGE_JSON_FILE_NAME);
@@ -59228,17 +59152,6 @@ const buildRuleBlastRadii = (diagnostics) => buildSortedRuleGroups(diagnostics).
59228
59152
  })).toSorted((left, right) => right.fileCount - left.fileCount);
59229
59153
  const findMigrationScaleBuckets = (diagnostics) => buildRuleBlastRadii(diagnostics).filter((bucket) => bucket.fileCount >= 40);
59230
59154
  //#endregion
59231
- //#region src/cli/utils/has-lint-hard-failure.ts
59232
- const hasLintHardFailure = (result) => result.skippedChecks.includes("lint");
59233
- //#endregion
59234
- //#region src/cli/utils/is-inspect-result-complete.ts
59235
- const isInspectResultComplete = (result) => isScanComplete({
59236
- analyzedFileCount: result.analyzedFiles?.length,
59237
- scannedFileCount: result.scannedFileCount,
59238
- skippedCheckCount: result.skippedChecks.length,
59239
- skippedCheckReasonCount: Object.keys(result.skippedCheckReasons ?? {}).length
59240
- });
59241
- //#endregion
59242
59155
  //#region src/cli/utils/scan-result-cache.ts
59243
59156
  const CACHE_DISABLED_VALUES = new Set(["1", "true"]);
59244
59157
  const TOOLCHAIN_PACKAGE_SPECIFIERS = [
@@ -59644,12 +59557,15 @@ const buildOutcomeAttributes = (input) => {
59644
59557
  const summary = summarizeDiagnostics(result.diagnostics);
59645
59558
  const blockingLevel = resolveTelemetryBlocking(input.userConfig);
59646
59559
  const gateDiagnostics = filterDiagnosticsForSurface(result.diagnostics, "ciFailure", input.userConfig);
59647
- const complete = isInspectResultComplete(result);
59648
- const didLintHardFail = hasLintHardFailure(result);
59649
- const failsOnHardFailure = didLintHardFail && blockingLevel !== "none";
59650
59560
  const wouldBlock = !input.scoreOnly && !input.gateExempt && shouldBlockCi(gateDiagnostics, blockingLevel);
59561
+ const complete = isScanComplete({
59562
+ analyzedFileCount: result.analyzedFiles?.length,
59563
+ scannedFileCount: result.scannedFileCount,
59564
+ skippedCheckCount: result.skippedChecks.length,
59565
+ skippedCheckReasonCount: Object.keys(result.skippedCheckReasons ?? {}).length
59566
+ });
59651
59567
  const isClean = result.diagnostics.length === 0 && complete;
59652
- const outcome = didLintHardFail ? "error" : wouldBlock ? "blocked" : isClean ? "clean" : "ok";
59568
+ const outcome = wouldBlock ? "blocked" : isClean ? "clean" : "ok";
59653
59569
  const firings = summarizeRuleFirings(result.diagnostics);
59654
59570
  const countByRule = /* @__PURE__ */ new Map();
59655
59571
  const countByCategory = /* @__PURE__ */ new Map();
@@ -59694,7 +59610,7 @@ const buildOutcomeAttributes = (input) => {
59694
59610
  const attributes = {
59695
59611
  ...withNamespace("outcome", {
59696
59612
  status: outcome,
59697
- exitCode: failsOnHardFailure || wouldBlock ? 1 : 0,
59613
+ exitCode: wouldBlock ? 1 : 0,
59698
59614
  wouldBlock,
59699
59615
  blocking: blockingLevel,
59700
59616
  clean: isClean,
@@ -63876,15 +63792,10 @@ const warnIfAiTrainingEnvironment = () => {
63876
63792
  //#region src/cli/utils/validate-mode-flags.ts
63877
63793
  const usedDiffAlias = (flags) => flags.diff !== void 0 && flags.diff !== false && flags.diff !== "false" && flags.diff !== "";
63878
63794
  const usedScope = (flags) => typeof flags.scope === "string" && flags.scope.length > 0;
63879
- const validateIncludeUntrackedScope = (includeUntracked, scope) => {
63880
- if (!includeUntracked || scope !== void 0 && scope !== "full") return;
63881
- throw new CliInputError("--include-untracked requires the files, changed, or lines scope (via --scope, --diff, or config).");
63882
- };
63883
63795
  const validateModeFlags = (flags) => {
63884
63796
  if (usedScope(flags) && usedDiffAlias(flags)) throw new CliInputError("Cannot combine --scope and --diff; --diff is the deprecated alias.");
63885
63797
  if (flags.staged && usedDiffAlias(flags)) throw new CliInputError("Cannot combine --staged and --diff; pick one mode.");
63886
63798
  if (flags.staged && (flags.scope === "full" || flags.scope === "changed")) throw new CliInputError(`Cannot combine --staged with --scope ${flags.scope}; use --scope files or --scope lines, or drop --scope.`);
63887
- if (flags.includeUntracked && flags.staged) throw new CliInputError("Cannot combine --include-untracked with --staged; the git index never holds untracked files.");
63888
63799
  if (flags.score && flags.json) throw new CliInputError("Cannot combine --score and --json; pick one output mode.");
63889
63800
  if (flags.score && flags.telemetry === false) throw new CliInputError("Cannot combine --score with --no-telemetry; --score prints the score that --no-telemetry disables.");
63890
63801
  if (flags.debug && (flags.score === false || flags.telemetry === false)) {
@@ -63893,39 +63804,6 @@ const validateModeFlags = (flags) => {
63893
63804
  }
63894
63805
  };
63895
63806
  //#endregion
63896
- //#region src/cli/utils/parse-staged-snapshot-divergences.ts
63897
- const SNAPSHOT_CONFIG_FILENAMES = new Set([...STAGED_FILES_PROJECT_CONFIG_FILENAMES, ...STAGED_SNAPSHOT_ADDITIONAL_CONFIG_FILENAMES]);
63898
- const parseStagedSnapshotDivergences = (statusOutput) => {
63899
- const divergentConfigFiles = /* @__PURE__ */ new Set();
63900
- const statusEntries = statusOutput.split("\0").filter((entry) => entry.length > 0);
63901
- for (let entryIndex = 0; entryIndex < statusEntries.length; entryIndex += 1) {
63902
- const entry = statusEntries[entryIndex];
63903
- const [indexStatus, worktreeStatus] = entry;
63904
- const filePath = entry.slice(3);
63905
- const hasRenameOrCopySource = indexStatus === "R" || indexStatus === "C" || worktreeStatus === "R" || worktreeStatus === "C";
63906
- const sourcePath = hasRenameOrCopySource ? statusEntries[entryIndex + 1] : void 0;
63907
- const worktreePaths = worktreeStatus === "R" || worktreeStatus === "C" ? [filePath, sourcePath] : [filePath];
63908
- if (worktreeStatus !== " ") {
63909
- for (const worktreePath of worktreePaths) if (worktreePath && SNAPSHOT_CONFIG_FILENAMES.has(Path.basename(worktreePath))) divergentConfigFiles.add(worktreePath);
63910
- }
63911
- if (hasRenameOrCopySource) entryIndex += 1;
63912
- }
63913
- return [...divergentConfigFiles].sort();
63914
- };
63915
- //#endregion
63916
- //#region src/cli/utils/find-staged-snapshot-divergences.ts
63917
- const findStagedSnapshotDivergences = (directory) => {
63918
- const statusOutput = runGitRaw(directory, [
63919
- "status",
63920
- "--porcelain=v1",
63921
- "-z",
63922
- "--untracked-files=all",
63923
- "--ignored=matching"
63924
- ]);
63925
- if (statusOutput === null) return null;
63926
- return parseStagedSnapshotDivergences(statusOutput);
63927
- };
63928
- //#endregion
63929
63807
  //#region src/cli/commands/inspect.ts
63930
63808
  const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
63931
63809
  if (categoryFilters.size === 0) return [...completedScans];
@@ -63940,14 +63818,9 @@ const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
63940
63818
  /**
63941
63819
  * Post-scan finalization shared by the staged-arm and project-loop
63942
63820
  * paths of `inspectAction`: emit the JSON report (when in JSON mode)
63943
- * and set `process.exitCode = 1` when any scan's lint pass hard-failed
63944
- * (an engine/plugin/binding failure destroys the findings, so success
63945
- * would be a false clean) or a diagnostic at or above the `--blocking`
63946
- * threshold (default `"error"`) reaches the `ciFailure` surface.
63947
- * `--blocking none` keeps the scan advisory (always exits 0), and
63948
- * fail-open degradations — `--no-lint`, `--max-duration` truncation,
63949
- * supply-chain/security skips — stay advisory too, surfaced through
63950
- * `complete: false` in the JSON report.
63821
+ * and set `process.exitCode = 1` when a diagnostic at or above the
63822
+ * `--blocking` threshold (default `"error"`) reaches the `ciFailure`
63823
+ * surface. `--blocking none` keeps the scan advisory (always exits 0).
63951
63824
  */
63952
63825
  const finalizeScans = (input) => {
63953
63826
  const baselineDeltas = input.completedScans.flatMap((scan) => scan.result.baselineDelta ? [scan.result.baselineDelta] : []);
@@ -63977,13 +63850,8 @@ const finalizeScans = (input) => {
63977
63850
  baselineDegraded
63978
63851
  }));
63979
63852
  }
63980
- const blockingLevel = resolveBlockingLevel(input.flags, input.userConfig);
63981
- if (input.completedScans.some(({ result }) => hasLintHardFailure(result)) && blockingLevel !== "none") {
63982
- process.exitCode = 1;
63983
- return;
63984
- }
63985
63853
  if (input.isScoreOnly || baselineDegraded) return;
63986
- if (shouldBlockCi(filterScansForSurface(input.completedScans, "ciFailure"), blockingLevel)) process.exitCode = 1;
63854
+ if (shouldBlockCi(filterScansForSurface(input.completedScans, "ciFailure"), resolveBlockingLevel(input.flags, input.userConfig))) process.exitCode = 1;
63987
63855
  };
63988
63856
  const buildChangedFilesDiffInfo = (changedFiles) => ({
63989
63857
  currentBranch: process.env.GITHUB_HEAD_REF?.trim() || null,
@@ -64021,7 +63889,6 @@ const inspectAction = async (directory, flags) => {
64021
63889
  recordCount(METRIC.cliInvoked, 1, { command: "inspect" });
64022
63890
  try {
64023
63891
  validateModeFlags(flags);
64024
- if (flags.staged) setJsonReportMode("staged");
64025
63892
  await maybeMigrateLegacyConfig(requestedDirectory, {
64026
63893
  isQuiet,
64027
63894
  isStaged: Boolean(flags.staged)
@@ -64037,14 +63904,6 @@ const inspectAction = async (directory, flags) => {
64037
63904
  cliLogger.dim(`Redirected to ${highlighter.info(toRelativePath(resolvedDirectory, requestedDirectory))} via react-doctor config "rootDir".`);
64038
63905
  cliLogger.break();
64039
63906
  }
64040
- if (flags.staged) {
64041
- const divergentConfigFiles = findStagedSnapshotDivergences(resolvedDirectory);
64042
- if (divergentConfigFiles === null) throw new CliInputError("Could not verify that staged configuration matches the worktree. Run the command from a Git worktree with Git available.");
64043
- if (divergentConfigFiles.length > 0) {
64044
- recordCount(METRIC.stagedSnapshotDivergence, 1, { divergentInputCount: divergentConfigFiles.length });
64045
- throw new CliInputError(`Cannot scan staged files while configuration differs between the index and worktree: ${divergentConfigFiles.join(", ")}. Stage or restore those files, then rerun react-doctor --staged.`);
64046
- }
64047
- }
64048
63907
  const explainArgument = flags.explain;
64049
63908
  if (explainArgument !== void 0) {
64050
63909
  await runExplain(explainArgument, {
@@ -64065,6 +63924,7 @@ const inspectAction = async (directory, flags) => {
64065
63924
  json: flags.json
64066
63925
  });
64067
63926
  if (flags.staged) {
63927
+ setJsonReportMode("staged");
64068
63928
  const stagedFiles = await getStagedSourceFiles(resolvedDirectory);
64069
63929
  if (stagedFiles.length === 0) {
64070
63930
  if (isJsonMode) writeJsonReport(buildJsonReport({
@@ -64145,15 +64005,13 @@ const inspectAction = async (directory, flags) => {
64145
64005
  const projectDirectories = await selectProjects(resolvedDirectory, flags.project, skipPrompts, userConfig?.projects);
64146
64006
  const changedFilesDiffInfo = flags.changedFilesFrom ? buildChangedFilesDiffInfo(readChangedFilesFrom(Path.resolve(flags.changedFilesFrom))) : null;
64147
64007
  const requestedScope = resolveScope(flags, userConfig);
64148
- const includeUntracked = flags.includeUntracked ?? false;
64149
64008
  const scopeRequest = requestedScope.scope === void 0 && changedFilesDiffInfo !== null ? {
64150
64009
  ...requestedScope,
64151
64010
  scope: "changed"
64152
64011
  } : requestedScope;
64153
- validateIncludeUntrackedScope(includeUntracked, scopeRequest.scope);
64154
64012
  const wantsDiffMode = scopeRequest.scope !== void 0 && scopeRequest.scope !== "full";
64155
64013
  const shouldDetectDiff = changedFilesDiffInfo === null && (wantsDiffMode || scopeRequest.scope === void 0 && !skipPrompts && !isQuiet);
64156
- const diffInfo = changedFilesDiffInfo ?? (shouldDetectDiff ? await getDiffInfo(resolvedDirectory, scopeRequest.base, includeUntracked) : null);
64014
+ const diffInfo = changedFilesDiffInfo ?? (shouldDetectDiff ? await getDiffInfo(resolvedDirectory, scopeRequest.base) : null);
64157
64015
  const scope = await finalizeScope({
64158
64016
  requested: scopeRequest,
64159
64017
  diffInfo,
@@ -64167,8 +64025,7 @@ const inspectAction = async (directory, flags) => {
64167
64025
  const changedLineRanges = scope === "lines" && diffInfo !== null && (diffInfo.isCurrentChanges || linesBaseRef !== null) && diffInfo !== null ? await getChangedLineRanges({
64168
64026
  directory: resolvedDirectory,
64169
64027
  baseRef: linesBaseRef ?? void 0,
64170
- files: [...diffInfo.changedFiles],
64171
- includeUntracked
64028
+ files: [...diffInfo.changedFiles]
64172
64029
  }) : null;
64173
64030
  if (scope === "lines" && changedLineRanges === null && !isQuiet) {
64174
64031
  cliLogger.warn("Could not determine changed lines (no base ref or git diff failed); reporting all issues in changed files.");
@@ -65101,7 +64958,6 @@ const ROOT_FLAG_SPEC = {
65101
64958
  "--dead-code",
65102
64959
  "--debug",
65103
64960
  "--help",
65104
- "--include-untracked",
65105
64961
  "--json",
65106
64962
  "--json-compact",
65107
64963
  "--lint",
@@ -65365,7 +65221,7 @@ ${highlighter.dim("Learn more:")}
65365
65221
  ${highlighter.info(CI_URL)}
65366
65222
  `;
65367
65223
  const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
65368
- const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much supported JS/TS source to scan/report: full (default), files, changed (only new issues vs base), or lines (issues whose source spans touch changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").option("--include-untracked", "with --scope files/changed/lines, also scan ordinary untracked files (respects .gitignore)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks").option("--max-duration <seconds>", "scan time budget for the whole run, shared across workspace projects: past it, remaining lint batches and dead-code are skipped and partial results are reported (skipped files are listed in the JSON report)").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
65224
+ const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much supported JS/TS source to scan/report: full (default), files, changed (only new issues vs base), or lines (issues whose source spans touch changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks").option("--max-duration <seconds>", "scan time budget for the whole run, shared across workspace projects: past it, remaining lint batches and dead-code are skipped and partial results are reported (skipped files are listed in the JSON report)").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
65369
65225
  program.action(inspectAction);
65370
65226
  program.command("why <location>").description("Explain why a rule fired (or why a suppression didn't apply) at a file:line").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple)").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").action((location, options) => whyAction(location, options));
65371
65227
  program.command("install").alias("setup").description("Install the react-doctor skill into your coding agents and optional git hook").option("-y, --yes", "skip prompts, install for all detected agents").option("--dry-run", "show what would be installed without writing files").option("--agent-hooks", "install native non-blocking agent hooks for Claude Code and Cursor").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderInstallHelpEpilog).action(installAction);
@@ -65415,4 +65271,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
65415
65271
  export {};
65416
65272
 
65417
65273
  //# sourceMappingURL=cli.js.map
65418
- //# debugId=4c1e681e-4dee-5ffd-a4ba-69b876f47147
65274
+ //# debugId=9a5c1a3c-7706-5a23-bca4-ddddd2f085af
package/dist/index.d.ts CHANGED
@@ -10256,7 +10256,7 @@ declare const buildJsonReport: (input: BuildJsonReportInput) => JsonReportV3$1;
10256
10256
  * render diff-base mistakes (`GitBaseBranchInvalid` /
10257
10257
  * `GitBaseBranchMissing`) as clean user errors instead of crashes.
10258
10258
  */
10259
- declare const getDiffInfo: (directory: string, explicitBaseBranch?: string, includeUntracked?: boolean) => Promise<DiffInfo | null>;
10259
+ declare const getDiffInfo: (directory: string, explicitBaseBranch?: string) => Promise<DiffInfo | null>;
10260
10260
  declare const filterSourceFiles: (filePaths: string[]) => string[];
10261
10261
  /**
10262
10262
  * Programmatic façade over `Git.changedLineRanges` (the `lines` scope). Diffs
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]="4da79386-f10f-5bae-95ac-7333b2d6c4e8")}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]="11364a34-6c8b-5bd3-b9e8-aaa7c0ea264c")}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 reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, FRAMEWORK_TOKENS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
@@ -36366,49 +36366,6 @@ const assignFixGroups = (diagnostics) => {
36366
36366
  };
36367
36367
  });
36368
36368
  };
36369
- const DERIVED_STATE_RULE_PRIORITY = new Map([
36370
- ["no-initialize-state", 0],
36371
- ["no-adjust-state-on-prop-change", 1],
36372
- ["no-derived-state", 2],
36373
- ["no-derived-state-effect", 3]
36374
- ]);
36375
- const buildDiagnosticSiteKey = (diagnostic) => `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}`;
36376
- const isReactDoctorRulesOfHooksDiagnostic = (diagnostic) => diagnostic.plugin === "react-doctor" && diagnostic.rule === "rules-of-hooks";
36377
- const isReactCompilerHooksDiagnostic = (diagnostic) => diagnostic.plugin === "react-hooks-js" && diagnostic.rule === "hooks";
36378
- const doDiagnosticSpansOverlap = (first, second) => {
36379
- if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
36380
- if (first.offset !== void 0 && second.offset !== void 0) {
36381
- const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
36382
- const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
36383
- return first.offset < secondEnd && second.offset < firstEnd;
36384
- }
36385
- const firstEndLine = first.endLine ?? first.line;
36386
- const secondEndLine = second.endLine ?? second.line;
36387
- return first.line <= secondEndLine && second.line <= firstEndLine;
36388
- };
36389
- const keepHighestSeverity = (survivingDiagnostic, collapsedDiagnostic) => collapsedDiagnostic.severity === "error" && survivingDiagnostic.severity !== "error" ? {
36390
- ...survivingDiagnostic,
36391
- severity: "error"
36392
- } : survivingDiagnostic;
36393
- const dedupeRelatedDiagnostics = (diagnostics) => {
36394
- const reactDoctorHookSites = new Set(diagnostics.filter(isReactDoctorRulesOfHooksDiagnostic).map(buildDiagnosticSiteKey));
36395
- const uniqueDiagnostics = [];
36396
- for (const diagnostic of diagnostics) {
36397
- if (isReactCompilerHooksDiagnostic(diagnostic) && reactDoctorHookSites.has(buildDiagnosticSiteKey(diagnostic))) continue;
36398
- const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
36399
- if (priority !== void 0) {
36400
- const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
36401
- if (overlappingDiagnosticIndex >= 0) {
36402
- const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
36403
- const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
36404
- if (existingDiagnostic && existingPriority !== void 0) uniqueDiagnostics[overlappingDiagnosticIndex] = priority < existingPriority ? keepHighestSeverity(diagnostic, existingDiagnostic) : keepHighestSeverity(existingDiagnostic, diagnostic);
36405
- continue;
36406
- }
36407
- }
36408
- uniqueDiagnostics.push(diagnostic);
36409
- }
36410
- return uniqueDiagnostics;
36411
- };
36412
36369
  const compareStrings = (left, right) => left < right ? -1 : left > right ? 1 : 0;
36413
36370
  const sortDiagnosticsStable = (diagnostics) => [...diagnostics].sort((left, right) => compareStrings(left.filePath, right.filePath) || left.line - right.line || left.column - right.column || compareStrings(left.plugin, right.plugin) || compareStrings(left.rule, right.rule) || compareStrings(left.severity, right.severity) || compareStrings(left.message, right.message));
36414
36371
  const getDirectDependencyNames = (packageJson) => new Set([...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.devDependencies ?? {})]);
@@ -38622,7 +38579,6 @@ const parseGithubViewerPermission = (stdout) => {
38622
38579
  return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
38623
38580
  };
38624
38581
  const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
38625
- const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
38626
38582
  /**
38627
38583
  * `Git` wraps every `git`-via-subprocess call react-doctor makes
38628
38584
  * behind a `Context.Service`. The production layer (`layerNode`)
@@ -38693,14 +38649,6 @@ var Git = class Git extends Service()("react-doctor/Git") {
38693
38649
  args,
38694
38650
  directory
38695
38651
  });
38696
- const listUntrackedFilePaths = (directory, includePaths = []) => runGit(directory, [
38697
- "ls-files",
38698
- "-z",
38699
- "--others",
38700
- "--exclude-standard",
38701
- ...includePaths.length > 0 ? ["--", ...includePaths] : []
38702
- ]).pipe(map$3((result) => result.status === 0 ? splitNullSeparated(result.stdout) : null));
38703
- const mergeUntracked = (directory, trackedFilePaths, includeUntracked) => includeUntracked ? listUntrackedFilePaths(directory).pipe(map$3((untracked) => untracked === null ? trackedFilePaths : [...new Set([...trackedFilePaths, ...untracked])])) : succeed$2(trackedFilePaths);
38704
38652
  const currentBranch = (directory) => runGit(directory, [
38705
38653
  "rev-parse",
38706
38654
  "--abbrev-ref",
@@ -38823,7 +38771,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38823
38771
  githubViewerPermission,
38824
38772
  branchExists,
38825
38773
  mergeBase,
38826
- diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
38774
+ diffSelection: ({ directory, explicitBaseBranch }) => gen(function* () {
38827
38775
  if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
38828
38776
  if (explicitBaseBranch !== void 0) {
38829
38777
  const range = parseGitDiffRange(explicitBaseBranch);
@@ -38852,7 +38800,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38852
38800
  "HEAD"
38853
38801
  ]);
38854
38802
  if (uncommitted.status !== 0) return null;
38855
- const files = yield* mergeUntracked(directory, splitNullSeparated(uncommitted.stdout), includeUntracked);
38803
+ const files = splitNullSeparated(uncommitted.stdout);
38856
38804
  if (files.length === 0) return null;
38857
38805
  return {
38858
38806
  currentBranch: resolvedCurrentBranch,
@@ -38882,7 +38830,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38882
38830
  currentBranch: resolvedCurrentBranch,
38883
38831
  baseBranch,
38884
38832
  diffBaseRef: mergeBaseRef,
38885
- changedFiles: yield* mergeUntracked(directory, splitNullSeparated(diff.stdout), includeUntracked),
38833
+ changedFiles: splitNullSeparated(diff.stdout),
38886
38834
  isCurrentChanges: false
38887
38835
  };
38888
38836
  }).pipe(withSpan("Git.diffSelection")),
@@ -38928,7 +38876,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38928
38876
  stdout: result.stdout
38929
38877
  };
38930
38878
  }).pipe(withSpan("Git.grep")),
38931
- changedLineRanges: ({ directory, baseRef, cached, files, includeUntracked = false }) => gen(function* () {
38879
+ changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
38932
38880
  if (files.length === 0) return [];
38933
38881
  if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
38934
38882
  const result = yield* runGit(directory, [
@@ -38942,14 +38890,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38942
38890
  ...files
38943
38891
  ]);
38944
38892
  if (result.status !== 0) return null;
38945
- const changedLineRanges = parseChangedLineRanges(result.stdout);
38946
- if (cached || !includeUntracked) return changedLineRanges;
38947
- const untrackedFilePaths = yield* listUntrackedFilePaths(directory, files);
38948
- if (untrackedFilePaths === null) return changedLineRanges;
38949
- return [...changedLineRanges, ...untrackedFilePaths.map((file) => ({
38950
- file,
38951
- ranges: [[1, UNTRACKED_FILE_LAST_LINE]]
38952
- }))];
38893
+ return parseChangedLineRanges(result.stdout);
38953
38894
  }).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
38954
38895
  });
38955
38896
  })).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
@@ -39534,13 +39475,13 @@ const encodeSidecarFileEntry = (entry) => ({
39534
39475
  diagnostics: entry.diagnostics
39535
39476
  });
39536
39477
  const emptyCache = () => ({
39537
- version: 2,
39478
+ version: 1,
39538
39479
  rulesets: {}
39539
39480
  });
39540
39481
  const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
39541
39482
  const entries = /* @__PURE__ */ new Map();
39542
39483
  const persisted = failOpenReadJson(cacheFilePath, emptyCache());
39543
- if (persisted.version !== 2 || !isRecord(persisted.rulesets)) return entries;
39484
+ if (persisted.version !== 1 || !isRecord(persisted.rulesets)) return entries;
39544
39485
  const bucket = persisted.rulesets[rulesetHash];
39545
39486
  if (!isRecord(bucket) || !isRecord(bucket.files)) return entries;
39546
39487
  for (const [fileKey, rawEntry] of Object.entries(bucket.files)) {
@@ -39560,7 +39501,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
39560
39501
  },
39561
39502
  persist: () => {
39562
39503
  const onDisk = failOpenReadJson(cacheFilePath, emptyCache());
39563
- const rulesets = onDisk.version === 2 && isRecord(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
39504
+ const rulesets = onDisk.version === 1 && isRecord(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
39564
39505
  const existingBucket = rulesets[rulesetHash];
39565
39506
  const existingFiles = isRecord(existingBucket) && isRecord(existingBucket.files) ? existingBucket.files : {};
39566
39507
  const ourFiles = {};
@@ -39574,7 +39515,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
39574
39515
  files: Object.fromEntries(cappedEntries)
39575
39516
  };
39576
39517
  atomicWriteJson(cacheFilePath, {
39577
- version: 2,
39518
+ version: 1,
39578
39519
  rulesets: Object.fromEntries(Object.entries(rulesets).filter(([, bucket]) => isRecord(bucket) && typeof bucket.updatedAtMs === "number").sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8))
39579
39520
  });
39580
39521
  }
@@ -39625,6 +39566,23 @@ const resolveTsConfigRelativePath = (rootDirectory) => {
39625
39566
  for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
39626
39567
  return null;
39627
39568
  };
39569
+ const DERIVED_STATE_RULE_PRIORITY = new Map([
39570
+ ["no-initialize-state", 0],
39571
+ ["no-adjust-state-on-prop-change", 1],
39572
+ ["no-derived-state", 2],
39573
+ ["no-derived-state-effect", 3]
39574
+ ]);
39575
+ const doDiagnosticSpansOverlap = (first, second) => {
39576
+ if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
39577
+ if (first.offset !== void 0 && second.offset !== void 0) {
39578
+ const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
39579
+ const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
39580
+ return first.offset < secondEnd && second.offset < firstEnd;
39581
+ }
39582
+ const firstEndLine = first.endLine ?? first.line;
39583
+ const secondEndLine = second.endLine ?? second.line;
39584
+ return first.line <= secondEndLine && second.line <= firstEndLine;
39585
+ };
39628
39586
  const dedupeDiagnostics = (diagnostics) => {
39629
39587
  const seenKeys = /* @__PURE__ */ new Set();
39630
39588
  const uniqueDiagnostics = [];
@@ -39632,6 +39590,16 @@ const dedupeDiagnostics = (diagnostics) => {
39632
39590
  const key = `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}\u0000${diagnostic.plugin}\u0000${diagnostic.rule}\u0000${diagnostic.severity}\u0000${diagnostic.message}`;
39633
39591
  if (seenKeys.has(key)) continue;
39634
39592
  seenKeys.add(key);
39593
+ const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
39594
+ if (priority !== void 0) {
39595
+ const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && candidate.severity === diagnostic.severity && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
39596
+ if (overlappingDiagnosticIndex >= 0) {
39597
+ const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
39598
+ const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
39599
+ if (existingPriority !== void 0 && priority < existingPriority) uniqueDiagnostics[overlappingDiagnosticIndex] = diagnostic;
39600
+ continue;
39601
+ }
39602
+ }
39635
39603
  uniqueDiagnostics.push(diagnostic);
39636
39604
  }
39637
39605
  return uniqueDiagnostics;
@@ -40431,11 +40399,11 @@ const buildRelatedLocations = (labels, filePath) => {
40431
40399
  }
40432
40400
  return related;
40433
40401
  };
40434
- const isOxlintSpan = (value) => isRecord(value) && typeof value.offset === "number" && typeof value.length === "number" && typeof value.line === "number" && typeof value.column === "number";
40435
- const isOxlintLabel = (value) => isRecord(value) && isOxlintSpan(value.span);
40436
- const isMappableOxlintDiagnostic = (value) => isRecord(value) && typeof value.code === "string" && value.code.length > 0 && typeof value.filename === "string" && value.filename.length > 0 && (value.severity === "warning" || value.severity === "error") && Array.isArray(value.labels) && value.labels.every(isOxlintLabel);
40437
- const isEngineFailureDiagnostic = (value) => !isRecord(value) || typeof value.filename !== "string" || value.filename.length === 0;
40438
- const isOxlintOutput = (value) => isRecord(value) && Array.isArray(value.diagnostics);
40402
+ const isOxlintOutput = (value) => {
40403
+ if (typeof value !== "object" || value === null) return false;
40404
+ const candidate = value;
40405
+ return Array.isArray(candidate.diagnostics);
40406
+ };
40439
40407
  /**
40440
40408
  * Parses one oxlint subprocess's stdout into a flat `Diagnostic[]`.
40441
40409
  * Tolerates the leading-notice-line shape oxlint sometimes prints
@@ -40454,8 +40422,6 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
40454
40422
  throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
40455
40423
  }
40456
40424
  if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
40457
- const engineFailureDiagnostic = parsed.diagnostics.find(isEngineFailureDiagnostic);
40458
- if (engineFailureDiagnostic !== void 0) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: JSON.stringify(engineFailureDiagnostic).slice(0, 600) }) });
40459
40425
  const minifiedFileCache = /* @__PURE__ */ new Map();
40460
40426
  const sourceBufferCache = /* @__PURE__ */ new Map();
40461
40427
  const sourceLinesCache = /* @__PURE__ */ new Map();
@@ -40490,7 +40456,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
40490
40456
  minifiedFileCache.set(absolutePath, minified);
40491
40457
  return minified;
40492
40458
  };
40493
- return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
40459
+ return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => diagnostic.code && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
40494
40460
  const { plugin, rule } = parseRuleCode(diagnostic.code);
40495
40461
  const primaryLabel = diagnostic.labels[0];
40496
40462
  const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
@@ -42325,8 +42291,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
42325
42291
  respectInlineDisables: input.respectInlineDisables,
42326
42292
  showWarnings
42327
42293
  });
42328
- const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(filterMapNullable(transform.apply)));
42329
- const applyPerElementPipeline = (rawStream) => filterPerElementPipeline(rawStream).pipe(tap((diagnostic) => reporterService.emit(diagnostic)));
42294
+ const applyPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(filterMapNullable(transform.apply)), tap((diagnostic) => reporterService.emit(diagnostic)));
42330
42295
  const envCollected = yield* runCollect(applyPerElementPipeline(fromIterable$1(isDiffMode ? [] : [
42331
42296
  ...checkReducedMotion(scanDirectory),
42332
42297
  ...checkPnpmHardening(scanDirectory),
@@ -42455,7 +42420,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
42455
42420
  });
42456
42421
  return empty$4;
42457
42422
  }))));
42458
- const lintCollected = dedupeRelatedDiagnostics(yield* runCollect(filterPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream)).pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
42423
+ const lintCollected = yield* runCollect(applyPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream)).pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
42459
42424
  onNone: () => set(lintFailure, {
42460
42425
  didFail: true,
42461
42426
  reason: `Lint analysis exceeded ${lintPhaseTimeoutMs / MILLISECONDS_PER_SECOND}s and was skipped.`,
@@ -42463,8 +42428,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
42463
42428
  reasonKind: null
42464
42429
  }).pipe(as([])),
42465
42430
  onSome: succeed$2
42466
- }))));
42467
- yield* forEach$1(lintCollected, reporterService.emit, { discard: true });
42431
+ })));
42468
42432
  const lintFailureState = yield* get$2(lintFailure);
42469
42433
  yield* afterLint(lintFailureState.didFail);
42470
42434
  if (lintFailureState.didFail) yield* scanProgress.fail(formatLintFailText(lintFailureState.reasonTag, process.version));
@@ -42984,11 +42948,10 @@ const buildSkippedChecks = (input) => {
42984
42948
  * render diff-base mistakes (`GitBaseBranchInvalid` /
42985
42949
  * `GitBaseBranchMissing`) as clean user errors instead of crashes.
42986
42950
  */
42987
- const getDiffInfo = (directory, explicitBaseBranch, includeUntracked) => runPromise(gen(function* () {
42951
+ const getDiffInfo = (directory, explicitBaseBranch) => runPromise(gen(function* () {
42988
42952
  const selection = yield* (yield* Git).diffSelection({
42989
42953
  directory,
42990
- explicitBaseBranch,
42991
- includeUntracked
42954
+ explicitBaseBranch
42992
42955
  });
42993
42956
  if (selection === null) return null;
42994
42957
  return {
@@ -43278,4 +43241,4 @@ const toJsonReport = (result, options) => buildJsonReport({
43278
43241
  export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
43279
43242
 
43280
43243
  //# sourceMappingURL=index.js.map
43281
- //# debugId=4da79386-f10f-5bae-95ac-7333b2d6c4e8
43244
+ //# debugId=11364a34-6c8b-5bd3-b9e8-aaa7c0ea264c
package/dist/lsp.js CHANGED
@@ -36344,49 +36344,6 @@ const assignFixGroups = (diagnostics) => {
36344
36344
  };
36345
36345
  });
36346
36346
  };
36347
- const DERIVED_STATE_RULE_PRIORITY = new Map([
36348
- ["no-initialize-state", 0],
36349
- ["no-adjust-state-on-prop-change", 1],
36350
- ["no-derived-state", 2],
36351
- ["no-derived-state-effect", 3]
36352
- ]);
36353
- const buildDiagnosticSiteKey = (diagnostic) => `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}`;
36354
- const isReactDoctorRulesOfHooksDiagnostic = (diagnostic) => diagnostic.plugin === "react-doctor" && diagnostic.rule === "rules-of-hooks";
36355
- const isReactCompilerHooksDiagnostic = (diagnostic) => diagnostic.plugin === "react-hooks-js" && diagnostic.rule === "hooks";
36356
- const doDiagnosticSpansOverlap = (first, second) => {
36357
- if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
36358
- if (first.offset !== void 0 && second.offset !== void 0) {
36359
- const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
36360
- const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
36361
- return first.offset < secondEnd && second.offset < firstEnd;
36362
- }
36363
- const firstEndLine = first.endLine ?? first.line;
36364
- const secondEndLine = second.endLine ?? second.line;
36365
- return first.line <= secondEndLine && second.line <= firstEndLine;
36366
- };
36367
- const keepHighestSeverity = (survivingDiagnostic, collapsedDiagnostic) => collapsedDiagnostic.severity === "error" && survivingDiagnostic.severity !== "error" ? {
36368
- ...survivingDiagnostic,
36369
- severity: "error"
36370
- } : survivingDiagnostic;
36371
- const dedupeRelatedDiagnostics = (diagnostics) => {
36372
- const reactDoctorHookSites = new Set(diagnostics.filter(isReactDoctorRulesOfHooksDiagnostic).map(buildDiagnosticSiteKey));
36373
- const uniqueDiagnostics = [];
36374
- for (const diagnostic of diagnostics) {
36375
- if (isReactCompilerHooksDiagnostic(diagnostic) && reactDoctorHookSites.has(buildDiagnosticSiteKey(diagnostic))) continue;
36376
- const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
36377
- if (priority !== void 0) {
36378
- const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
36379
- if (overlappingDiagnosticIndex >= 0) {
36380
- const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
36381
- const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
36382
- if (existingDiagnostic && existingPriority !== void 0) uniqueDiagnostics[overlappingDiagnosticIndex] = priority < existingPriority ? keepHighestSeverity(diagnostic, existingDiagnostic) : keepHighestSeverity(existingDiagnostic, diagnostic);
36383
- continue;
36384
- }
36385
- }
36386
- uniqueDiagnostics.push(diagnostic);
36387
- }
36388
- return uniqueDiagnostics;
36389
- };
36390
36347
  const compareStrings = (left, right) => left < right ? -1 : left > right ? 1 : 0;
36391
36348
  const sortDiagnosticsStable = (diagnostics) => [...diagnostics].sort((left, right) => compareStrings(left.filePath, right.filePath) || left.line - right.line || left.column - right.column || compareStrings(left.plugin, right.plugin) || compareStrings(left.rule, right.rule) || compareStrings(left.severity, right.severity) || compareStrings(left.message, right.message));
36392
36349
  const getDirectDependencyNames = (packageJson) => new Set([...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.devDependencies ?? {})]);
@@ -38600,7 +38557,6 @@ const parseGithubViewerPermission = (stdout) => {
38600
38557
  return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
38601
38558
  };
38602
38559
  const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
38603
- const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
38604
38560
  /**
38605
38561
  * `Git` wraps every `git`-via-subprocess call react-doctor makes
38606
38562
  * behind a `Context.Service`. The production layer (`layerNode`)
@@ -38671,14 +38627,6 @@ var Git = class Git extends Service()("react-doctor/Git") {
38671
38627
  args,
38672
38628
  directory
38673
38629
  });
38674
- const listUntrackedFilePaths = (directory, includePaths = []) => runGit(directory, [
38675
- "ls-files",
38676
- "-z",
38677
- "--others",
38678
- "--exclude-standard",
38679
- ...includePaths.length > 0 ? ["--", ...includePaths] : []
38680
- ]).pipe(map$3((result) => result.status === 0 ? splitNullSeparated(result.stdout) : null));
38681
- const mergeUntracked = (directory, trackedFilePaths, includeUntracked) => includeUntracked ? listUntrackedFilePaths(directory).pipe(map$3((untracked) => untracked === null ? trackedFilePaths : [...new Set([...trackedFilePaths, ...untracked])])) : succeed$2(trackedFilePaths);
38682
38630
  const currentBranch = (directory) => runGit(directory, [
38683
38631
  "rev-parse",
38684
38632
  "--abbrev-ref",
@@ -38801,7 +38749,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38801
38749
  githubViewerPermission,
38802
38750
  branchExists,
38803
38751
  mergeBase,
38804
- diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
38752
+ diffSelection: ({ directory, explicitBaseBranch }) => gen(function* () {
38805
38753
  if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
38806
38754
  if (explicitBaseBranch !== void 0) {
38807
38755
  const range = parseGitDiffRange(explicitBaseBranch);
@@ -38830,7 +38778,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38830
38778
  "HEAD"
38831
38779
  ]);
38832
38780
  if (uncommitted.status !== 0) return null;
38833
- const files = yield* mergeUntracked(directory, splitNullSeparated(uncommitted.stdout), includeUntracked);
38781
+ const files = splitNullSeparated(uncommitted.stdout);
38834
38782
  if (files.length === 0) return null;
38835
38783
  return {
38836
38784
  currentBranch: resolvedCurrentBranch,
@@ -38860,7 +38808,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38860
38808
  currentBranch: resolvedCurrentBranch,
38861
38809
  baseBranch,
38862
38810
  diffBaseRef: mergeBaseRef,
38863
- changedFiles: yield* mergeUntracked(directory, splitNullSeparated(diff.stdout), includeUntracked),
38811
+ changedFiles: splitNullSeparated(diff.stdout),
38864
38812
  isCurrentChanges: false
38865
38813
  };
38866
38814
  }).pipe(withSpan("Git.diffSelection")),
@@ -38906,7 +38854,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38906
38854
  stdout: result.stdout
38907
38855
  };
38908
38856
  }).pipe(withSpan("Git.grep")),
38909
- changedLineRanges: ({ directory, baseRef, cached, files, includeUntracked = false }) => gen(function* () {
38857
+ changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
38910
38858
  if (files.length === 0) return [];
38911
38859
  if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
38912
38860
  const result = yield* runGit(directory, [
@@ -38920,14 +38868,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38920
38868
  ...files
38921
38869
  ]);
38922
38870
  if (result.status !== 0) return null;
38923
- const changedLineRanges = parseChangedLineRanges(result.stdout);
38924
- if (cached || !includeUntracked) return changedLineRanges;
38925
- const untrackedFilePaths = yield* listUntrackedFilePaths(directory, files);
38926
- if (untrackedFilePaths === null) return changedLineRanges;
38927
- return [...changedLineRanges, ...untrackedFilePaths.map((file) => ({
38928
- file,
38929
- ranges: [[1, UNTRACKED_FILE_LAST_LINE]]
38930
- }))];
38871
+ return parseChangedLineRanges(result.stdout);
38931
38872
  }).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
38932
38873
  });
38933
38874
  })).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
@@ -39512,13 +39453,13 @@ const encodeSidecarFileEntry = (entry) => ({
39512
39453
  diagnostics: entry.diagnostics
39513
39454
  });
39514
39455
  const emptyCache = () => ({
39515
- version: 2,
39456
+ version: 1,
39516
39457
  rulesets: {}
39517
39458
  });
39518
39459
  const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
39519
39460
  const entries = /* @__PURE__ */ new Map();
39520
39461
  const persisted = failOpenReadJson(cacheFilePath, emptyCache());
39521
- if (persisted.version !== 2 || !isRecord(persisted.rulesets)) return entries;
39462
+ if (persisted.version !== 1 || !isRecord(persisted.rulesets)) return entries;
39522
39463
  const bucket = persisted.rulesets[rulesetHash];
39523
39464
  if (!isRecord(bucket) || !isRecord(bucket.files)) return entries;
39524
39465
  for (const [fileKey, rawEntry] of Object.entries(bucket.files)) {
@@ -39538,7 +39479,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
39538
39479
  },
39539
39480
  persist: () => {
39540
39481
  const onDisk = failOpenReadJson(cacheFilePath, emptyCache());
39541
- const rulesets = onDisk.version === 2 && isRecord(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
39482
+ const rulesets = onDisk.version === 1 && isRecord(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
39542
39483
  const existingBucket = rulesets[rulesetHash];
39543
39484
  const existingFiles = isRecord(existingBucket) && isRecord(existingBucket.files) ? existingBucket.files : {};
39544
39485
  const ourFiles = {};
@@ -39552,7 +39493,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
39552
39493
  files: Object.fromEntries(cappedEntries)
39553
39494
  };
39554
39495
  atomicWriteJson(cacheFilePath, {
39555
- version: 2,
39496
+ version: 1,
39556
39497
  rulesets: Object.fromEntries(Object.entries(rulesets).filter(([, bucket]) => isRecord(bucket) && typeof bucket.updatedAtMs === "number").sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8))
39557
39498
  });
39558
39499
  }
@@ -39603,6 +39544,23 @@ const resolveTsConfigRelativePath = (rootDirectory) => {
39603
39544
  for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
39604
39545
  return null;
39605
39546
  };
39547
+ const DERIVED_STATE_RULE_PRIORITY = new Map([
39548
+ ["no-initialize-state", 0],
39549
+ ["no-adjust-state-on-prop-change", 1],
39550
+ ["no-derived-state", 2],
39551
+ ["no-derived-state-effect", 3]
39552
+ ]);
39553
+ const doDiagnosticSpansOverlap = (first, second) => {
39554
+ if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
39555
+ if (first.offset !== void 0 && second.offset !== void 0) {
39556
+ const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
39557
+ const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
39558
+ return first.offset < secondEnd && second.offset < firstEnd;
39559
+ }
39560
+ const firstEndLine = first.endLine ?? first.line;
39561
+ const secondEndLine = second.endLine ?? second.line;
39562
+ return first.line <= secondEndLine && second.line <= firstEndLine;
39563
+ };
39606
39564
  const dedupeDiagnostics = (diagnostics) => {
39607
39565
  const seenKeys = /* @__PURE__ */ new Set();
39608
39566
  const uniqueDiagnostics = [];
@@ -39610,6 +39568,16 @@ const dedupeDiagnostics = (diagnostics) => {
39610
39568
  const key = `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}\u0000${diagnostic.plugin}\u0000${diagnostic.rule}\u0000${diagnostic.severity}\u0000${diagnostic.message}`;
39611
39569
  if (seenKeys.has(key)) continue;
39612
39570
  seenKeys.add(key);
39571
+ const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
39572
+ if (priority !== void 0) {
39573
+ const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && candidate.severity === diagnostic.severity && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
39574
+ if (overlappingDiagnosticIndex >= 0) {
39575
+ const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
39576
+ const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
39577
+ if (existingPriority !== void 0 && priority < existingPriority) uniqueDiagnostics[overlappingDiagnosticIndex] = diagnostic;
39578
+ continue;
39579
+ }
39580
+ }
39613
39581
  uniqueDiagnostics.push(diagnostic);
39614
39582
  }
39615
39583
  return uniqueDiagnostics;
@@ -40409,11 +40377,11 @@ const buildRelatedLocations = (labels, filePath) => {
40409
40377
  }
40410
40378
  return related;
40411
40379
  };
40412
- const isOxlintSpan = (value) => isRecord(value) && typeof value.offset === "number" && typeof value.length === "number" && typeof value.line === "number" && typeof value.column === "number";
40413
- const isOxlintLabel = (value) => isRecord(value) && isOxlintSpan(value.span);
40414
- const isMappableOxlintDiagnostic = (value) => isRecord(value) && typeof value.code === "string" && value.code.length > 0 && typeof value.filename === "string" && value.filename.length > 0 && (value.severity === "warning" || value.severity === "error") && Array.isArray(value.labels) && value.labels.every(isOxlintLabel);
40415
- const isEngineFailureDiagnostic = (value) => !isRecord(value) || typeof value.filename !== "string" || value.filename.length === 0;
40416
- const isOxlintOutput = (value) => isRecord(value) && Array.isArray(value.diagnostics);
40380
+ const isOxlintOutput = (value) => {
40381
+ if (typeof value !== "object" || value === null) return false;
40382
+ const candidate = value;
40383
+ return Array.isArray(candidate.diagnostics);
40384
+ };
40417
40385
  /**
40418
40386
  * Parses one oxlint subprocess's stdout into a flat `Diagnostic[]`.
40419
40387
  * Tolerates the leading-notice-line shape oxlint sometimes prints
@@ -40432,8 +40400,6 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
40432
40400
  throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
40433
40401
  }
40434
40402
  if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
40435
- const engineFailureDiagnostic = parsed.diagnostics.find(isEngineFailureDiagnostic);
40436
- if (engineFailureDiagnostic !== void 0) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: JSON.stringify(engineFailureDiagnostic).slice(0, 600) }) });
40437
40403
  const minifiedFileCache = /* @__PURE__ */ new Map();
40438
40404
  const sourceBufferCache = /* @__PURE__ */ new Map();
40439
40405
  const sourceLinesCache = /* @__PURE__ */ new Map();
@@ -40468,7 +40434,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
40468
40434
  minifiedFileCache.set(absolutePath, minified);
40469
40435
  return minified;
40470
40436
  };
40471
- return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
40437
+ return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => diagnostic.code && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
40472
40438
  const { plugin, rule } = parseRuleCode(diagnostic.code);
40473
40439
  const primaryLabel = diagnostic.labels[0];
40474
40440
  const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
@@ -42303,8 +42269,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
42303
42269
  respectInlineDisables: input.respectInlineDisables,
42304
42270
  showWarnings
42305
42271
  });
42306
- const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(filterMapNullable(transform.apply)));
42307
- const applyPerElementPipeline = (rawStream) => filterPerElementPipeline(rawStream).pipe(tap((diagnostic) => reporterService.emit(diagnostic)));
42272
+ const applyPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(filterMapNullable(transform.apply)), tap((diagnostic) => reporterService.emit(diagnostic)));
42308
42273
  const envCollected = yield* runCollect(applyPerElementPipeline(fromIterable$1(isDiffMode ? [] : [
42309
42274
  ...checkReducedMotion(scanDirectory),
42310
42275
  ...checkPnpmHardening(scanDirectory),
@@ -42433,7 +42398,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
42433
42398
  });
42434
42399
  return empty$4;
42435
42400
  }))));
42436
- const lintCollected = dedupeRelatedDiagnostics(yield* runCollect(filterPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream)).pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
42401
+ const lintCollected = yield* runCollect(applyPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream)).pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
42437
42402
  onNone: () => set(lintFailure, {
42438
42403
  didFail: true,
42439
42404
  reason: `Lint analysis exceeded ${lintPhaseTimeoutMs / MILLISECONDS_PER_SECOND}s and was skipped.`,
@@ -42441,8 +42406,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
42441
42406
  reasonKind: null
42442
42407
  }).pipe(as([])),
42443
42408
  onSome: succeed$2
42444
- }))));
42445
- yield* forEach$1(lintCollected, reporterService.emit, { discard: true });
42409
+ })));
42446
42410
  const lintFailureState = yield* get$2(lintFailure);
42447
42411
  yield* afterLint(lintFailureState.didFail);
42448
42412
  if (lintFailureState.didFail) yield* scanProgress.fail(formatLintFailText(lintFailureState.reasonTag, process.version));
@@ -44560,7 +44524,6 @@ const METRIC = {
44560
44524
  cliInvoked: "cli.invoked",
44561
44525
  cliError: "cli.error",
44562
44526
  cliEnvironmentError: "cli.env_error",
44563
- stagedSnapshotDivergence: "staged.snapshot_divergence",
44564
44527
  projectDetected: "project.detected",
44565
44528
  projectPathSelected: "project.path_selected",
44566
44529
  projectConfigSelected: "project.config_selected",
@@ -44915,5 +44878,5 @@ const startLanguageServer = () => {
44915
44878
  };
44916
44879
  //#endregion
44917
44880
  export { startLanguageServer };
44918
- !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]="b75c36e9-f19d-5127-be1a-89ac93910ff0")}catch(e){}}();
44919
- //# debugId=b75c36e9-f19d-5127-be1a-89ac93910ff0
44881
+ !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]="f9337714-b38f-5f28-ac00-c9d7d79ebaea")}catch(e){}}();
44882
+ //# debugId=f9337714-b38f-5f28-ac00-c9d7d79ebaea
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor",
3
- "version": "0.7.6-dev.c1916d5",
3
+ "version": "0.7.6-dev.c5a06bd",
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.7.6",
69
- "oxlint-plugin-react-doctor": "0.7.6-dev.c1916d5"
69
+ "oxlint-plugin-react-doctor": "0.7.6-dev.c5a06bd"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/babel__code-frame": "^7.27.0",