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

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]="58e54b02-7ba2-5f13-a535-98c236467a86")}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]="4c1e681e-4dee-5ffd-a4ba-69b876f47147")}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,6 +39594,49 @@ 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
+ };
39597
39640
  const compareStrings = (left, right) => left < right ? -1 : left > right ? 1 : 0;
39598
39641
  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));
39599
39642
  const getDirectDependencyNames = (packageJson) => new Set([...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.devDependencies ?? {})]);
@@ -42735,13 +42778,13 @@ const encodeSidecarFileEntry = (entry) => ({
42735
42778
  diagnostics: entry.diagnostics
42736
42779
  });
42737
42780
  const emptyCache = () => ({
42738
- version: 1,
42781
+ version: 2,
42739
42782
  rulesets: {}
42740
42783
  });
42741
42784
  const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
42742
42785
  const entries = /* @__PURE__ */ new Map();
42743
42786
  const persisted = failOpenReadJson(cacheFilePath, emptyCache());
42744
- if (persisted.version !== 1 || !isRecord$1(persisted.rulesets)) return entries;
42787
+ if (persisted.version !== 2 || !isRecord$1(persisted.rulesets)) return entries;
42745
42788
  const bucket = persisted.rulesets[rulesetHash];
42746
42789
  if (!isRecord$1(bucket) || !isRecord$1(bucket.files)) return entries;
42747
42790
  for (const [fileKey, rawEntry] of Object.entries(bucket.files)) {
@@ -42761,7 +42804,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
42761
42804
  },
42762
42805
  persist: () => {
42763
42806
  const onDisk = failOpenReadJson(cacheFilePath, emptyCache());
42764
- const rulesets = onDisk.version === 1 && isRecord$1(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
42807
+ const rulesets = onDisk.version === 2 && isRecord$1(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
42765
42808
  const existingBucket = rulesets[rulesetHash];
42766
42809
  const existingFiles = isRecord$1(existingBucket) && isRecord$1(existingBucket.files) ? existingBucket.files : {};
42767
42810
  const ourFiles = {};
@@ -42775,7 +42818,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
42775
42818
  files: Object.fromEntries(cappedEntries)
42776
42819
  };
42777
42820
  atomicWriteJson(cacheFilePath, {
42778
- version: 1,
42821
+ version: 2,
42779
42822
  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))
42780
42823
  });
42781
42824
  }
@@ -42826,23 +42869,6 @@ const resolveTsConfigRelativePath = (rootDirectory) => {
42826
42869
  for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
42827
42870
  return null;
42828
42871
  };
42829
- const DERIVED_STATE_RULE_PRIORITY = new Map([
42830
- ["no-initialize-state", 0],
42831
- ["no-adjust-state-on-prop-change", 1],
42832
- ["no-derived-state", 2],
42833
- ["no-derived-state-effect", 3]
42834
- ]);
42835
- const doDiagnosticSpansOverlap = (first, second) => {
42836
- if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
42837
- if (first.offset !== void 0 && second.offset !== void 0) {
42838
- const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
42839
- const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
42840
- return first.offset < secondEnd && second.offset < firstEnd;
42841
- }
42842
- const firstEndLine = first.endLine ?? first.line;
42843
- const secondEndLine = second.endLine ?? second.line;
42844
- return first.line <= secondEndLine && second.line <= firstEndLine;
42845
- };
42846
42872
  const dedupeDiagnostics = (diagnostics) => {
42847
42873
  const seenKeys = /* @__PURE__ */ new Set();
42848
42874
  const uniqueDiagnostics = [];
@@ -42850,16 +42876,6 @@ const dedupeDiagnostics = (diagnostics) => {
42850
42876
  const key = `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}\u0000${diagnostic.plugin}\u0000${diagnostic.rule}\u0000${diagnostic.severity}\u0000${diagnostic.message}`;
42851
42877
  if (seenKeys.has(key)) continue;
42852
42878
  seenKeys.add(key);
42853
- const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
42854
- if (priority !== void 0) {
42855
- const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && candidate.severity === diagnostic.severity && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
42856
- if (overlappingDiagnosticIndex >= 0) {
42857
- const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
42858
- const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
42859
- if (existingPriority !== void 0 && priority < existingPriority) uniqueDiagnostics[overlappingDiagnosticIndex] = diagnostic;
42860
- continue;
42861
- }
42862
- }
42863
42879
  uniqueDiagnostics.push(diagnostic);
42864
42880
  }
42865
42881
  return uniqueDiagnostics;
@@ -45553,7 +45569,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
45553
45569
  respectInlineDisables: input.respectInlineDisables,
45554
45570
  showWarnings
45555
45571
  });
45556
- const applyPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(filterMapNullable(transform.apply)), tap((diagnostic) => reporterService.emit(diagnostic)));
45572
+ const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(filterMapNullable(transform.apply)));
45573
+ const applyPerElementPipeline = (rawStream) => filterPerElementPipeline(rawStream).pipe(tap((diagnostic) => reporterService.emit(diagnostic)));
45557
45574
  const envCollected = yield* runCollect(applyPerElementPipeline(fromIterable$1(isDiffMode ? [] : [
45558
45575
  ...checkReducedMotion(scanDirectory),
45559
45576
  ...checkPnpmHardening(scanDirectory),
@@ -45682,7 +45699,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
45682
45699
  });
45683
45700
  return empty$4;
45684
45701
  }))));
45685
- const lintCollected = yield* runCollect(applyPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream)).pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
45702
+ const lintCollected = dedupeRelatedDiagnostics(yield* runCollect(filterPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream)).pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
45686
45703
  onNone: () => set(lintFailure, {
45687
45704
  didFail: true,
45688
45705
  reason: `Lint analysis exceeded ${lintPhaseTimeoutMs / MILLISECONDS_PER_SECOND}s and was skipped.`,
@@ -45690,7 +45707,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
45690
45707
  reasonKind: null
45691
45708
  }).pipe(as([])),
45692
45709
  onSome: succeed$2
45693
- })));
45710
+ }))));
45711
+ yield* forEach$1(lintCollected, reporterService.emit, { discard: true });
45694
45712
  const lintFailureState = yield* get$2(lintFailure);
45695
45713
  yield* afterLint(lintFailureState.didFail);
45696
45714
  if (lintFailureState.didFail) yield* scanProgress.fail(formatLintFailText(lintFailureState.reasonTag, process.version));
@@ -46756,7 +46774,7 @@ const makeNoopConsole = () => ({
46756
46774
  });
46757
46775
  //#endregion
46758
46776
  //#region src/cli/utils/version.ts
46759
- const VERSION = "0.7.6-dev.b36e439";
46777
+ const VERSION = "0.7.6-dev.c1916d5";
46760
46778
  //#endregion
46761
46779
  //#region src/cli/utils/json-mode.ts
46762
46780
  let context = null;
@@ -47125,13 +47143,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
47125
47143
  * uploads source-map artifacts under, so stack frames symbolicate. Honors the
47126
47144
  * standard `SENTRY_RELEASE` override.
47127
47145
  */
47128
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.6-dev.b36e439`;
47146
+ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.6-dev.c1916d5`;
47129
47147
  /**
47130
47148
  * Deployment environment shown in Sentry's environment filter. Defaults to
47131
47149
  * `production` for tagged releases and `development` for dev/unbuilt versions,
47132
47150
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
47133
47151
  */
47134
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.6-dev.b36e439") ? "development" : "production");
47152
+ const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.6-dev.c1916d5") ? "development" : "production");
47135
47153
  /**
47136
47154
  * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
47137
47155
  * (set to `0` to disable tracing) and falls back to
@@ -65397,4 +65415,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
65397
65415
  export {};
65398
65416
 
65399
65417
  //# sourceMappingURL=cli.js.map
65400
- //# debugId=58e54b02-7ba2-5f13-a535-98c236467a86
65418
+ //# debugId=4c1e681e-4dee-5ffd-a4ba-69b876f47147
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]="2aa6fc3c-96ab-5a4c-8a52-e74d27fb4b5a")}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]="4da79386-f10f-5bae-95ac-7333b2d6c4e8")}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,6 +36366,49 @@ 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
+ };
36369
36412
  const compareStrings = (left, right) => left < right ? -1 : left > right ? 1 : 0;
36370
36413
  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));
36371
36414
  const getDirectDependencyNames = (packageJson) => new Set([...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.devDependencies ?? {})]);
@@ -39491,13 +39534,13 @@ const encodeSidecarFileEntry = (entry) => ({
39491
39534
  diagnostics: entry.diagnostics
39492
39535
  });
39493
39536
  const emptyCache = () => ({
39494
- version: 1,
39537
+ version: 2,
39495
39538
  rulesets: {}
39496
39539
  });
39497
39540
  const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
39498
39541
  const entries = /* @__PURE__ */ new Map();
39499
39542
  const persisted = failOpenReadJson(cacheFilePath, emptyCache());
39500
- if (persisted.version !== 1 || !isRecord(persisted.rulesets)) return entries;
39543
+ if (persisted.version !== 2 || !isRecord(persisted.rulesets)) return entries;
39501
39544
  const bucket = persisted.rulesets[rulesetHash];
39502
39545
  if (!isRecord(bucket) || !isRecord(bucket.files)) return entries;
39503
39546
  for (const [fileKey, rawEntry] of Object.entries(bucket.files)) {
@@ -39517,7 +39560,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
39517
39560
  },
39518
39561
  persist: () => {
39519
39562
  const onDisk = failOpenReadJson(cacheFilePath, emptyCache());
39520
- const rulesets = onDisk.version === 1 && isRecord(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
39563
+ const rulesets = onDisk.version === 2 && isRecord(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
39521
39564
  const existingBucket = rulesets[rulesetHash];
39522
39565
  const existingFiles = isRecord(existingBucket) && isRecord(existingBucket.files) ? existingBucket.files : {};
39523
39566
  const ourFiles = {};
@@ -39531,7 +39574,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
39531
39574
  files: Object.fromEntries(cappedEntries)
39532
39575
  };
39533
39576
  atomicWriteJson(cacheFilePath, {
39534
- version: 1,
39577
+ version: 2,
39535
39578
  rulesets: Object.fromEntries(Object.entries(rulesets).filter(([, bucket]) => isRecord(bucket) && typeof bucket.updatedAtMs === "number").sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8))
39536
39579
  });
39537
39580
  }
@@ -39582,23 +39625,6 @@ const resolveTsConfigRelativePath = (rootDirectory) => {
39582
39625
  for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
39583
39626
  return null;
39584
39627
  };
39585
- const DERIVED_STATE_RULE_PRIORITY = new Map([
39586
- ["no-initialize-state", 0],
39587
- ["no-adjust-state-on-prop-change", 1],
39588
- ["no-derived-state", 2],
39589
- ["no-derived-state-effect", 3]
39590
- ]);
39591
- const doDiagnosticSpansOverlap = (first, second) => {
39592
- if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
39593
- if (first.offset !== void 0 && second.offset !== void 0) {
39594
- const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
39595
- const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
39596
- return first.offset < secondEnd && second.offset < firstEnd;
39597
- }
39598
- const firstEndLine = first.endLine ?? first.line;
39599
- const secondEndLine = second.endLine ?? second.line;
39600
- return first.line <= secondEndLine && second.line <= firstEndLine;
39601
- };
39602
39628
  const dedupeDiagnostics = (diagnostics) => {
39603
39629
  const seenKeys = /* @__PURE__ */ new Set();
39604
39630
  const uniqueDiagnostics = [];
@@ -39606,16 +39632,6 @@ const dedupeDiagnostics = (diagnostics) => {
39606
39632
  const key = `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}\u0000${diagnostic.plugin}\u0000${diagnostic.rule}\u0000${diagnostic.severity}\u0000${diagnostic.message}`;
39607
39633
  if (seenKeys.has(key)) continue;
39608
39634
  seenKeys.add(key);
39609
- const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
39610
- if (priority !== void 0) {
39611
- const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && candidate.severity === diagnostic.severity && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
39612
- if (overlappingDiagnosticIndex >= 0) {
39613
- const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
39614
- const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
39615
- if (existingPriority !== void 0 && priority < existingPriority) uniqueDiagnostics[overlappingDiagnosticIndex] = diagnostic;
39616
- continue;
39617
- }
39618
- }
39619
39635
  uniqueDiagnostics.push(diagnostic);
39620
39636
  }
39621
39637
  return uniqueDiagnostics;
@@ -42309,7 +42325,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
42309
42325
  respectInlineDisables: input.respectInlineDisables,
42310
42326
  showWarnings
42311
42327
  });
42312
- const applyPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(filterMapNullable(transform.apply)), tap((diagnostic) => reporterService.emit(diagnostic)));
42328
+ const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(filterMapNullable(transform.apply)));
42329
+ const applyPerElementPipeline = (rawStream) => filterPerElementPipeline(rawStream).pipe(tap((diagnostic) => reporterService.emit(diagnostic)));
42313
42330
  const envCollected = yield* runCollect(applyPerElementPipeline(fromIterable$1(isDiffMode ? [] : [
42314
42331
  ...checkReducedMotion(scanDirectory),
42315
42332
  ...checkPnpmHardening(scanDirectory),
@@ -42438,7 +42455,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
42438
42455
  });
42439
42456
  return empty$4;
42440
42457
  }))));
42441
- const lintCollected = yield* runCollect(applyPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream)).pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
42458
+ const lintCollected = dedupeRelatedDiagnostics(yield* runCollect(filterPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream)).pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
42442
42459
  onNone: () => set(lintFailure, {
42443
42460
  didFail: true,
42444
42461
  reason: `Lint analysis exceeded ${lintPhaseTimeoutMs / MILLISECONDS_PER_SECOND}s and was skipped.`,
@@ -42446,7 +42463,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
42446
42463
  reasonKind: null
42447
42464
  }).pipe(as([])),
42448
42465
  onSome: succeed$2
42449
- })));
42466
+ }))));
42467
+ yield* forEach$1(lintCollected, reporterService.emit, { discard: true });
42450
42468
  const lintFailureState = yield* get$2(lintFailure);
42451
42469
  yield* afterLint(lintFailureState.didFail);
42452
42470
  if (lintFailureState.didFail) yield* scanProgress.fail(formatLintFailText(lintFailureState.reasonTag, process.version));
@@ -43260,4 +43278,4 @@ const toJsonReport = (result, options) => buildJsonReport({
43260
43278
  export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
43261
43279
 
43262
43280
  //# sourceMappingURL=index.js.map
43263
- //# debugId=2aa6fc3c-96ab-5a4c-8a52-e74d27fb4b5a
43281
+ //# debugId=4da79386-f10f-5bae-95ac-7333b2d6c4e8
package/dist/lsp.js CHANGED
@@ -36344,6 +36344,49 @@ 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
+ };
36347
36390
  const compareStrings = (left, right) => left < right ? -1 : left > right ? 1 : 0;
36348
36391
  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));
36349
36392
  const getDirectDependencyNames = (packageJson) => new Set([...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.devDependencies ?? {})]);
@@ -39469,13 +39512,13 @@ const encodeSidecarFileEntry = (entry) => ({
39469
39512
  diagnostics: entry.diagnostics
39470
39513
  });
39471
39514
  const emptyCache = () => ({
39472
- version: 1,
39515
+ version: 2,
39473
39516
  rulesets: {}
39474
39517
  });
39475
39518
  const loadRulesetEntries = (cacheFilePath, rulesetHash) => {
39476
39519
  const entries = /* @__PURE__ */ new Map();
39477
39520
  const persisted = failOpenReadJson(cacheFilePath, emptyCache());
39478
- if (persisted.version !== 1 || !isRecord(persisted.rulesets)) return entries;
39521
+ if (persisted.version !== 2 || !isRecord(persisted.rulesets)) return entries;
39479
39522
  const bucket = persisted.rulesets[rulesetHash];
39480
39523
  if (!isRecord(bucket) || !isRecord(bucket.files)) return entries;
39481
39524
  for (const [fileKey, rawEntry] of Object.entries(bucket.files)) {
@@ -39495,7 +39538,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
39495
39538
  },
39496
39539
  persist: () => {
39497
39540
  const onDisk = failOpenReadJson(cacheFilePath, emptyCache());
39498
- const rulesets = onDisk.version === 1 && isRecord(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
39541
+ const rulesets = onDisk.version === 2 && isRecord(onDisk.rulesets) ? { ...onDisk.rulesets } : {};
39499
39542
  const existingBucket = rulesets[rulesetHash];
39500
39543
  const existingFiles = isRecord(existingBucket) && isRecord(existingBucket.files) ? existingBucket.files : {};
39501
39544
  const ourFiles = {};
@@ -39509,7 +39552,7 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
39509
39552
  files: Object.fromEntries(cappedEntries)
39510
39553
  };
39511
39554
  atomicWriteJson(cacheFilePath, {
39512
- version: 1,
39555
+ version: 2,
39513
39556
  rulesets: Object.fromEntries(Object.entries(rulesets).filter(([, bucket]) => isRecord(bucket) && typeof bucket.updatedAtMs === "number").sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8))
39514
39557
  });
39515
39558
  }
@@ -39560,23 +39603,6 @@ const resolveTsConfigRelativePath = (rootDirectory) => {
39560
39603
  for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
39561
39604
  return null;
39562
39605
  };
39563
- const DERIVED_STATE_RULE_PRIORITY = new Map([
39564
- ["no-initialize-state", 0],
39565
- ["no-adjust-state-on-prop-change", 1],
39566
- ["no-derived-state", 2],
39567
- ["no-derived-state-effect", 3]
39568
- ]);
39569
- const doDiagnosticSpansOverlap = (first, second) => {
39570
- if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
39571
- if (first.offset !== void 0 && second.offset !== void 0) {
39572
- const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
39573
- const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
39574
- return first.offset < secondEnd && second.offset < firstEnd;
39575
- }
39576
- const firstEndLine = first.endLine ?? first.line;
39577
- const secondEndLine = second.endLine ?? second.line;
39578
- return first.line <= secondEndLine && second.line <= firstEndLine;
39579
- };
39580
39606
  const dedupeDiagnostics = (diagnostics) => {
39581
39607
  const seenKeys = /* @__PURE__ */ new Set();
39582
39608
  const uniqueDiagnostics = [];
@@ -39584,16 +39610,6 @@ const dedupeDiagnostics = (diagnostics) => {
39584
39610
  const key = `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}\u0000${diagnostic.plugin}\u0000${diagnostic.rule}\u0000${diagnostic.severity}\u0000${diagnostic.message}`;
39585
39611
  if (seenKeys.has(key)) continue;
39586
39612
  seenKeys.add(key);
39587
- const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
39588
- if (priority !== void 0) {
39589
- const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && candidate.severity === diagnostic.severity && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
39590
- if (overlappingDiagnosticIndex >= 0) {
39591
- const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
39592
- const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
39593
- if (existingPriority !== void 0 && priority < existingPriority) uniqueDiagnostics[overlappingDiagnosticIndex] = diagnostic;
39594
- continue;
39595
- }
39596
- }
39597
39613
  uniqueDiagnostics.push(diagnostic);
39598
39614
  }
39599
39615
  return uniqueDiagnostics;
@@ -42287,7 +42303,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
42287
42303
  respectInlineDisables: input.respectInlineDisables,
42288
42304
  showWarnings
42289
42305
  });
42290
- const applyPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(filterMapNullable(transform.apply)), tap((diagnostic) => reporterService.emit(diagnostic)));
42306
+ const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(filterMapNullable(transform.apply)));
42307
+ const applyPerElementPipeline = (rawStream) => filterPerElementPipeline(rawStream).pipe(tap((diagnostic) => reporterService.emit(diagnostic)));
42291
42308
  const envCollected = yield* runCollect(applyPerElementPipeline(fromIterable$1(isDiffMode ? [] : [
42292
42309
  ...checkReducedMotion(scanDirectory),
42293
42310
  ...checkPnpmHardening(scanDirectory),
@@ -42416,7 +42433,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
42416
42433
  });
42417
42434
  return empty$4;
42418
42435
  }))));
42419
- const lintCollected = yield* runCollect(applyPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream)).pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
42436
+ const lintCollected = dedupeRelatedDiagnostics(yield* runCollect(filterPerElementPipeline(shouldOverlapDeadCode ? baseLintStream.pipe(provideService(OxlintConcurrency, lintConcurrency)) : baseLintStream)).pipe(timeoutOption(lintPhaseTimeoutMs), flatMap$2(match$3({
42420
42437
  onNone: () => set(lintFailure, {
42421
42438
  didFail: true,
42422
42439
  reason: `Lint analysis exceeded ${lintPhaseTimeoutMs / MILLISECONDS_PER_SECOND}s and was skipped.`,
@@ -42424,7 +42441,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
42424
42441
  reasonKind: null
42425
42442
  }).pipe(as([])),
42426
42443
  onSome: succeed$2
42427
- })));
42444
+ }))));
42445
+ yield* forEach$1(lintCollected, reporterService.emit, { discard: true });
42428
42446
  const lintFailureState = yield* get$2(lintFailure);
42429
42447
  yield* afterLint(lintFailureState.didFail);
42430
42448
  if (lintFailureState.didFail) yield* scanProgress.fail(formatLintFailText(lintFailureState.reasonTag, process.version));
@@ -44897,5 +44915,5 @@ const startLanguageServer = () => {
44897
44915
  };
44898
44916
  //#endregion
44899
44917
  export { startLanguageServer };
44900
- !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]="8ed74459-2a69-556c-bb95-8cdebdb094cd")}catch(e){}}();
44901
- //# debugId=8ed74459-2a69-556c-bb95-8cdebdb094cd
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor",
3
- "version": "0.7.6-dev.b36e439",
3
+ "version": "0.7.6-dev.c1916d5",
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.b36e439"
69
+ "oxlint-plugin-react-doctor": "0.7.6-dev.c1916d5"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/babel__code-frame": "^7.27.0",