react-doctor 0.7.3 → 0.7.4-dev.11333b9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/lsp.js CHANGED
@@ -5,10 +5,10 @@ import * as OS from "node:os";
5
5
  import os from "node:os";
6
6
  import * as Path from "node:path";
7
7
  import path from "node:path";
8
+ 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";
8
9
  import * as NodeChildProcess from "node:child_process";
9
10
  import { execFileSync, spawn, spawnSync } from "node:child_process";
10
- import * as ts from "typescript";
11
- import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
11
+ import ts from "typescript";
12
12
  import { parseJSON5 } from "confbox";
13
13
  import * as NodeUrl from "node:url";
14
14
  import { fileURLToPath } from "node:url";
@@ -19339,6 +19339,31 @@ var Diagnostic = class extends Class("Diagnostic")({
19339
19339
  relatedLocations: optional(ArraySchema(DiagnosticRelatedLocation)),
19340
19340
  fixGroupId: optional(String$1)
19341
19341
  }) {};
19342
+ var JsonReportDiagnosticV3 = class extends Class("JsonReportDiagnosticV3")({
19343
+ id: String$1,
19344
+ normalizedFilePath: String$1,
19345
+ filePath: String$1,
19346
+ plugin: String$1,
19347
+ rule: String$1,
19348
+ severity: Severity,
19349
+ tags: ArraySchema(String$1),
19350
+ title: optional(String$1),
19351
+ message: String$1,
19352
+ help: String$1,
19353
+ url: optional(String$1),
19354
+ line: Number$1,
19355
+ column: Number$1,
19356
+ offset: optional(Number$1),
19357
+ length: optional(Number$1),
19358
+ endLine: optional(Number$1),
19359
+ endColumn: optional(Number$1),
19360
+ category: String$1,
19361
+ matchByOccurrence: optional(Boolean$1),
19362
+ fileContext: optional(Literals(["test", "story"])),
19363
+ suppressionHint: optional(String$1),
19364
+ relatedLocations: optional(ArraySchema(DiagnosticRelatedLocation)),
19365
+ fixGroupId: optional(String$1)
19366
+ }) {};
19342
19367
  /**
19343
19368
  * Deterministic identity string for a diagnostic. Same diagnostic
19344
19369
  * across two scans yields the same identity; lets baselines,
@@ -19352,6 +19377,7 @@ const JsonReportMode = Literals([
19352
19377
  "staged",
19353
19378
  "baseline"
19354
19379
  ]);
19380
+ const Framework = Literals(FRAMEWORK_TOKENS);
19355
19381
  var JsonReportSummary = class extends Class("JsonReportSummary")({
19356
19382
  errorCount: Number$1,
19357
19383
  warningCount: Number$1,
@@ -19389,13 +19415,22 @@ var JsonReportProjectEntry = class extends Class("JsonReportProjectEntry")({
19389
19415
  scannedFileCount: optional(Number$1),
19390
19416
  elapsedMilliseconds: Number$1
19391
19417
  }) {};
19392
- /**
19393
- * Versioned JsonReport schema. `JsonReport` is a `Schema.Union` so we
19394
- * can add `schemaVersion: 2` later as one new union member without
19395
- * breaking existing v1 consumers (the GitHub Action keys off the
19396
- * version literal). Today's union is single-arm; the shape is
19397
- * intentional.
19398
- */
19418
+ var JsonReportProjectEntryV3 = class extends Class("JsonReportProjectEntryV3")({
19419
+ directory: String$1,
19420
+ packageRoot: String$1,
19421
+ framework: Framework,
19422
+ project: Unknown,
19423
+ diagnostics: ArraySchema(JsonReportDiagnosticV3),
19424
+ score: Unknown,
19425
+ skippedChecks: ArraySchema(String$1),
19426
+ skippedCheckReasons: optional(Record$1(String$1, String$1)),
19427
+ analyzedFiles: ArraySchema(String$1),
19428
+ analyzedFileCount: Number$1,
19429
+ complete: Boolean$1,
19430
+ scannedFileCount: optional(Number$1),
19431
+ elapsedMilliseconds: Number$1
19432
+ }) {};
19433
+ /** Original full, diff, and staged report contract. */
19399
19434
  var JsonReportV1 = class extends Class("JsonReportV1")({
19400
19435
  schemaVersion: Literal(1),
19401
19436
  version: String$1,
@@ -19456,7 +19491,27 @@ var JsonReportV2 = class extends Class("JsonReportV2")({
19456
19491
  elapsedMilliseconds: Number$1,
19457
19492
  error: NullOr(JsonReportError)
19458
19493
  }) {};
19459
- Union([JsonReportV1, JsonReportV2]);
19494
+ var JsonReportV3 = class extends Class("JsonReportV3")({
19495
+ schemaVersion: Literal(3),
19496
+ version: String$1,
19497
+ ok: Boolean$1,
19498
+ directory: String$1,
19499
+ mode: JsonReportMode,
19500
+ baselineDegraded: optional(Boolean$1),
19501
+ reactDetected: optional(Boolean$1),
19502
+ diff: NullOr(JsonReportDiffInfo),
19503
+ baseline: optional(JsonReportBaseline),
19504
+ projects: ArraySchema(JsonReportProjectEntryV3),
19505
+ diagnostics: ArraySchema(JsonReportDiagnosticV3),
19506
+ summary: JsonReportSummary,
19507
+ elapsedMilliseconds: Number$1,
19508
+ error: NullOr(JsonReportError)
19509
+ }) {};
19510
+ Union([
19511
+ JsonReportV1,
19512
+ JsonReportV2,
19513
+ JsonReportV3
19514
+ ]);
19460
19515
  //#endregion
19461
19516
  //#region ../../node_modules/.pnpm/semver@7.7.4/node_modules/semver/internal/constants.js
19462
19517
  var require_constants$1 = /* @__PURE__ */ __commonJSMin$1(((exports, module) => {
@@ -33037,7 +33092,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
33037
33092
  const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
33038
33093
  const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
33039
33094
  const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
33040
- const CORE_PACKAGE_VERSION = "0.7.3";
33095
+ const CORE_PACKAGE_VERSION = "0.7.4";
33041
33096
  const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
33042
33097
  const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
33043
33098
  const SUPPLY_CHAIN_PLUGIN = "socket";
@@ -33949,6 +34004,22 @@ const isPackageJsonReanimatedAware = (packageJson) => {
33949
34004
  };
33950
34005
  return Object.hasOwn(allDependencies, REANIMATED_DEPENDENCY_NAME$1);
33951
34006
  };
34007
+ const SSR_DEPENDENCY_NAMES = new Set([
34008
+ "@react-router/cloudflare",
34009
+ "@react-router/node",
34010
+ "@react-router/serve",
34011
+ "vike",
34012
+ "vite-plugin-ssr"
34013
+ ]);
34014
+ const isPackageJsonSsrAware = (packageJson) => {
34015
+ const allDependencies = {
34016
+ ...packageJson.peerDependencies,
34017
+ ...packageJson.dependencies,
34018
+ ...packageJson.devDependencies,
34019
+ ...packageJson.optionalDependencies
34020
+ };
34021
+ return Object.keys(allDependencies).some((packageName) => SSR_DEPENDENCY_NAMES.has(packageName));
34022
+ };
33952
34023
  const getWorkspacePatterns = (rootDirectory, packageJson) => {
33953
34024
  const pnpmPatterns = parsePnpmWorkspacePatterns(rootDirectory);
33954
34025
  if (pnpmPatterns.length > 0) return pnpmPatterns;
@@ -34089,6 +34160,7 @@ const evaluateManifestFacts = (facts, packageJson, directory) => {
34089
34160
  }
34090
34161
  facts.hasReactNativeAwarePackage = facts.hasReactNativeAwarePackage || isPackageJsonReactNativeAware(packageJson);
34091
34162
  facts.hasReanimatedAwarePackage = facts.hasReanimatedAwarePackage || isPackageJsonReanimatedAware(packageJson);
34163
+ facts.hasSsrDependency = facts.hasSsrDependency || isPackageJsonSsrAware(packageJson);
34092
34164
  };
34093
34165
  const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGroup }) => {
34094
34166
  const facts = {
@@ -34110,6 +34182,7 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
34110
34182
  },
34111
34183
  hasReactNativeAwarePackage: false,
34112
34184
  hasReanimatedAwarePackage: false,
34185
+ hasSsrDependency: false,
34113
34186
  reanimatedVersion: null
34114
34187
  };
34115
34188
  evaluateManifestFacts(facts, rootPackageJson, rootDirectory);
@@ -34349,6 +34422,7 @@ const discoverProjectWithoutPackageJson = (directory) => {
34349
34422
  hasTypeScript: hasOwnTsConfig,
34350
34423
  hasReactCompiler: false,
34351
34424
  hasTanStackQuery: false,
34425
+ hasSsrDependency: false,
34352
34426
  preactVersion: null,
34353
34427
  preactMajorVersion: null,
34354
34428
  hasReactNativeWorkspace: false,
@@ -34468,6 +34542,7 @@ const discoverProject = (directory) => {
34468
34542
  hasTypeScript,
34469
34543
  hasReactCompiler: detectReactCompiler(directory, packageJson),
34470
34544
  hasTanStackQuery: hasTanStackQuery(packageJson),
34545
+ hasSsrDependency: workspaceFacts.hasSsrDependency,
34471
34546
  preactVersion,
34472
34547
  preactMajorVersion: parseReactMajor(preactVersion),
34473
34548
  hasReactNativeWorkspace,
@@ -35543,6 +35618,14 @@ var AmbiguousProject = class extends TaggedErrorClass()("AmbiguousProject", {
35543
35618
  return `Ambiguous project at ${this.directory}: found ${this.candidates.length} candidates (${this.candidates.join(", ")})`;
35544
35619
  }
35545
35620
  };
35621
+ var ProjectDiscoveryFailed = class extends TaggedErrorClass()("ProjectDiscoveryFailed", {
35622
+ directory: String$1,
35623
+ cause: Unknown
35624
+ }) {
35625
+ get message() {
35626
+ return `Project discovery failed for ${this.directory}: ${pretty(fail$6(this.cause))}`;
35627
+ }
35628
+ };
35546
35629
  var DeadCodeAnalysisFailed = class extends TaggedErrorClass()("DeadCodeAnalysisFailed", { cause: Unknown }) {
35547
35630
  get message() {
35548
35631
  return `Dead-code analysis failed: ${pretty(fail$6(this.cause))}`;
@@ -35577,6 +35660,7 @@ const ReactDoctorErrorReason = Union([
35577
35660
  ProjectNotFound,
35578
35661
  NoReactDependency,
35579
35662
  AmbiguousProject,
35663
+ ProjectDiscoveryFailed,
35580
35664
  DeadCodeAnalysisFailed,
35581
35665
  GitInvocationFailed,
35582
35666
  GitBaseBranchMissing,
@@ -37203,6 +37287,12 @@ const CLIENT_ONLY_FRAMEWORKS = new Set([
37203
37287
  "react-native",
37204
37288
  "expo"
37205
37289
  ]);
37290
+ const SSR_FRAMEWORKS = new Set([
37291
+ "nextjs",
37292
+ "remix",
37293
+ "gatsby",
37294
+ "tanstack-start"
37295
+ ]);
37206
37296
  const addMajorLadder = (capabilities, name, major, earliest, latest) => {
37207
37297
  if (major === null) return;
37208
37298
  const cappedMajor = Math.min(major, latest);
@@ -37215,6 +37305,7 @@ const buildCapabilities = (project) => {
37215
37305
  if (project.framework === "expo" || project.framework === "react-native" || project.hasReactNativeWorkspace) capabilities.add("react-native");
37216
37306
  if (project.expoVersion !== null) capabilities.add("expo");
37217
37307
  if ((project.framework === "nextjs" || project.framework === "tanstack-start" || project.framework === "remix") && !project.isStaticExport) capabilities.add("server-actions");
37308
+ if (SSR_FRAMEWORKS.has(project.framework) || project.hasSsrDependency) capabilities.add("ssr");
37218
37309
  if (CLIENT_ONLY_FRAMEWORKS.has(project.framework)) capabilities.add("client-only");
37219
37310
  if (project.isStaticExport) capabilities.add("nextjs:static-export");
37220
37311
  if (project.nextjsMajorVersion !== null && project.nextjsMajorVersion >= 15) capabilities.add("nextjs:15");
@@ -40619,6 +40710,8 @@ const spawnLintBatches = async (input) => {
40619
40710
  const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
40620
40711
  reportSkippedFiles(droppedFiles, (fileListPreview) => `${droppedFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
40621
40712
  reportSkippedFiles(outcome.deadlineSkippedFiles, (fileListPreview) => `${outcome.deadlineSkippedFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
40713
+ const unavailableFiles = new Set([...droppedFiles, ...outcome.deadlineSkippedFiles]);
40714
+ input.onAnalyzedFiles?.([...new Set(fileBatches.flat().filter((filePath) => !unavailableFiles.has(filePath)))].sort());
40622
40715
  return dedupeDiagnostics(diagnostics);
40623
40716
  };
40624
40717
  let didValidate = false;
@@ -40875,12 +40968,18 @@ const reactHooksJsPluginDropNote = (error) => {
40875
40968
  * 6. always restore disable directives + clean up the temp dir
40876
40969
  */
40877
40970
  const runOxlint = async (options) => {
40878
- const { rootDirectory, project, includePaths, nodeBinaryPath = process.execPath, customRulesOnly = false, respectInlineDisables = true, adoptExistingLintConfig = true, ignoredTags = /* @__PURE__ */ new Set(), userConfig, configSourceDirectory = rootDirectory, onPartialFailure, perFileLintCacheEnabled = false, sidecarLintCacheEnabled = false, onCacheStats, onSidecarStats, spawnTimeoutMs, outputMaxBytes, lintBatchOrdering = "cost" } = options;
40971
+ const { rootDirectory, project, includePaths, nodeBinaryPath = process.execPath, customRulesOnly = false, respectInlineDisables = true, adoptExistingLintConfig = true, ignoredTags = /* @__PURE__ */ new Set(), userConfig, configSourceDirectory = rootDirectory, onPartialFailure, onFileCoverage, perFileLintCacheEnabled = false, sidecarLintCacheEnabled = false, onCacheStats, onSidecarStats, spawnTimeoutMs, outputMaxBytes, lintBatchOrdering = "cost" } = options;
40879
40972
  const serverAuthFunctionNames = Array.isArray(userConfig?.serverAuthFunctionNames) ? userConfig.serverAuthFunctionNames.filter((entry) => typeof entry === "string" && entry.length > 0) : void 0;
40880
40973
  const severityControls = buildRuleSeverityControls(userConfig);
40881
40974
  validateRuleRegistration();
40882
40975
  resetManifestCaches();
40883
- if (includePaths !== void 0 && includePaths.length === 0) return [];
40976
+ if (includePaths !== void 0 && includePaths.length === 0) {
40977
+ onFileCoverage?.({
40978
+ candidateFiles: [],
40979
+ analyzedFiles: []
40980
+ });
40981
+ return [];
40982
+ }
40884
40983
  const pluginPath = resolvePluginPath();
40885
40984
  const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
40886
40985
  const userPlugins = resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
@@ -40941,6 +41040,7 @@ const runOxlint = async (options) => {
40941
41040
  const runConfigOverFiles = async (buildConfigForPass, configFileName, files, fileProgress) => {
40942
41041
  if (files.length === 0) return {
40943
41042
  diagnostics: [],
41043
+ analyzedFiles: [],
40944
41044
  didDropReactHooksJsPlugin: false,
40945
41045
  hadPartialFailure: false
40946
41046
  };
@@ -40952,24 +41052,32 @@ const runOxlint = async (options) => {
40952
41052
  const passConfigPath = Path.join(configDirectory, configFileName);
40953
41053
  const passBaseArgs = makeBaseArgs(passConfigPath);
40954
41054
  const passFileBatches = buildFileBatches(passBaseArgs, files);
40955
- const spawnPass = () => spawnLintBatches({
40956
- baseArgs: passBaseArgs,
40957
- fileBatches: passFileBatches,
40958
- rootDirectory,
40959
- nodeBinaryPath,
40960
- project,
40961
- onPartialFailure: reportPartialFailure,
40962
- onFileProgress: fileProgress,
40963
- spawnTimeoutMs,
40964
- outputMaxBytes,
40965
- concurrency: options.concurrency,
40966
- signal: options.signal,
40967
- deadlineEpochMs: options.deadlineEpochMs
40968
- });
41055
+ let analyzedFiles = [];
41056
+ const spawnPass = () => {
41057
+ analyzedFiles = [];
41058
+ return spawnLintBatches({
41059
+ baseArgs: passBaseArgs,
41060
+ fileBatches: passFileBatches,
41061
+ rootDirectory,
41062
+ nodeBinaryPath,
41063
+ project,
41064
+ onPartialFailure: reportPartialFailure,
41065
+ onAnalyzedFiles: (filePaths) => {
41066
+ analyzedFiles = filePaths;
41067
+ },
41068
+ onFileProgress: fileProgress,
41069
+ spawnTimeoutMs,
41070
+ outputMaxBytes,
41071
+ concurrency: options.concurrency,
41072
+ signal: options.signal,
41073
+ deadlineEpochMs: options.deadlineEpochMs
41074
+ });
41075
+ };
40969
41076
  writeOxlintConfig(passConfigPath, buildConfigForPass({}));
40970
41077
  try {
40971
41078
  return {
40972
41079
  diagnostics: await spawnPass(),
41080
+ analyzedFiles,
40973
41081
  didDropReactHooksJsPlugin: false,
40974
41082
  hadPartialFailure
40975
41083
  };
@@ -40981,6 +41089,7 @@ const runOxlint = async (options) => {
40981
41089
  reportPartialFailure(reactHooksJsDropNote);
40982
41090
  return {
40983
41091
  diagnostics,
41092
+ analyzedFiles,
40984
41093
  didDropReactHooksJsPlugin: true,
40985
41094
  hadPartialFailure
40986
41095
  };
@@ -41056,6 +41165,7 @@ const runOxlint = async (options) => {
41056
41165
  const missFileSet = new Set(missFiles);
41057
41166
  const hitFiles = candidateFiles.filter((candidateFile) => !missFileSet.has(candidateFile));
41058
41167
  const sidecarReplayedDiagnostics = [];
41168
+ const sidecarReplayedFiles = [];
41059
41169
  const sidecarLintFiles = [];
41060
41170
  if (sidecarCache === null) sidecarLintFiles.push(...hitFiles);
41061
41171
  else {
@@ -41063,8 +41173,10 @@ const runOxlint = async (options) => {
41063
41173
  for (const hitFile of hitFiles) {
41064
41174
  const cacheKey = cacheKeyByFile.get(hitFile);
41065
41175
  const entry = cacheKey === void 0 ? null : sidecarCache.lookup(cacheKey);
41066
- if (entry !== null && entry.probes.every((probe) => probeAnswers.answerFor(probe.kind, probe.path) === probe.answer)) sidecarReplayedDiagnostics.push(...entry.diagnostics);
41067
- else sidecarLintFiles.push(hitFile);
41176
+ if (entry !== null && entry.probes.every((probe) => probeAnswers.answerFor(probe.kind, probe.path) === probe.answer)) {
41177
+ sidecarReplayedFiles.push(hitFile);
41178
+ sidecarReplayedDiagnostics.push(...entry.diagnostics);
41179
+ } else sidecarLintFiles.push(hitFile);
41068
41180
  if (performance.now() - verifySliceStartedAt >= 12) {
41069
41181
  await yieldToEventLoop();
41070
41182
  verifySliceStartedAt = performance.now();
@@ -41090,6 +41202,7 @@ const runOxlint = async (options) => {
41090
41202
  sidecarRuleIdFilter: new Set(unboundedSidecarRuleIds)
41091
41203
  }), "oxlintrc.sidecar-unbounded.json", hitFiles, void 0) : {
41092
41204
  diagnostics: [],
41205
+ analyzedFiles: [],
41093
41206
  didDropReactHooksJsPlugin: false,
41094
41207
  hadPartialFailure: false
41095
41208
  };
@@ -41120,6 +41233,15 @@ const runOxlint = async (options) => {
41120
41233
  probesByFile: await missProbesTask
41121
41234
  }]
41122
41235
  });
41236
+ const analyzedFileSet = new Set(fullResult.analyzedFiles);
41237
+ const boundedSidecarAnalyzedFileSet = new Set(boundedSidecarResult.analyzedFiles);
41238
+ const unboundedSidecarAnalyzedFileSet = new Set(unboundedSidecarResult.analyzedFiles);
41239
+ const sidecarReplayedFileSet = new Set(sidecarReplayedFiles);
41240
+ for (const hitFile of hitFiles) if (useSidecarCache ? (sidecarReplayedFileSet.has(hitFile) || boundedSidecarAnalyzedFileSet.has(hitFile)) && (unboundedSidecarRuleIds.length === 0 || unboundedSidecarAnalyzedFileSet.has(hitFile)) : boundedSidecarAnalyzedFileSet.has(hitFile)) analyzedFileSet.add(hitFile);
41241
+ onFileCoverage?.({
41242
+ candidateFiles,
41243
+ analyzedFiles: candidateFiles.filter((candidateFile) => analyzedFileSet.has(candidateFile))
41244
+ });
41123
41245
  return dedupeDiagnostics([
41124
41246
  ...replayedDiagnostics,
41125
41247
  ...fullResult.diagnostics,
@@ -41130,6 +41252,7 @@ const runOxlint = async (options) => {
41130
41252
  }
41131
41253
  const baseArgs = makeBaseArgs(configPath);
41132
41254
  const fileBatches = buildFileBatches(baseArgs, candidateFiles);
41255
+ let analyzedFiles = [];
41133
41256
  const runBatches = () => spawnLintBatches({
41134
41257
  baseArgs,
41135
41258
  fileBatches,
@@ -41137,6 +41260,9 @@ const runOxlint = async (options) => {
41137
41260
  nodeBinaryPath,
41138
41261
  project,
41139
41262
  onPartialFailure,
41263
+ onAnalyzedFiles: (filePaths) => {
41264
+ analyzedFiles = filePaths;
41265
+ },
41140
41266
  onFileProgress: options.onFileProgress,
41141
41267
  spawnTimeoutMs,
41142
41268
  outputMaxBytes,
@@ -41146,7 +41272,12 @@ const runOxlint = async (options) => {
41146
41272
  });
41147
41273
  writeOxlintConfig(configPath, buildConfig({ extendsPaths }));
41148
41274
  try {
41149
- return await runBatches();
41275
+ const diagnostics = await runBatches();
41276
+ onFileCoverage?.({
41277
+ candidateFiles,
41278
+ analyzedFiles
41279
+ });
41280
+ return diagnostics;
41150
41281
  } catch (error) {
41151
41282
  const reactHooksJsDropNote = reactHooksJsPluginDropNote(error);
41152
41283
  if (reactHooksJsDropNote !== null) {
@@ -41156,11 +41287,20 @@ const runOxlint = async (options) => {
41156
41287
  }));
41157
41288
  const diagnostics = await runBatches();
41158
41289
  onPartialFailure?.(reactHooksJsDropNote);
41290
+ onFileCoverage?.({
41291
+ candidateFiles,
41292
+ analyzedFiles
41293
+ });
41159
41294
  return diagnostics;
41160
41295
  }
41161
41296
  if (extendsPaths.length === 0) throw error;
41162
41297
  writeOxlintConfig(configPath, buildConfig({ extendsPaths: [] }));
41163
- return await runBatches();
41298
+ const diagnostics = await runBatches();
41299
+ onFileCoverage?.({
41300
+ candidateFiles,
41301
+ analyzedFiles
41302
+ });
41303
+ return diagnostics;
41164
41304
  }
41165
41305
  } finally {
41166
41306
  restoreDisableDirectives();
@@ -41240,6 +41380,7 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
41240
41380
  onPartialFailure: (reason) => {
41241
41381
  collectedFailures.push(reason);
41242
41382
  },
41383
+ onFileCoverage: input.onFileCoverage,
41243
41384
  onFileProgress: input.onFileProgress,
41244
41385
  perFileLintCacheEnabled,
41245
41386
  sidecarLintCacheEnabled,
@@ -41332,7 +41473,10 @@ const translateProjectInfoError = (cause, directory) => {
41332
41473
  directory: cause.directory,
41333
41474
  candidates: cause.candidates
41334
41475
  }) });
41335
- return new ReactDoctorError({ reason: new ProjectNotFound({ directory }) });
41476
+ return new ReactDoctorError({ reason: new ProjectDiscoveryFailed({
41477
+ directory,
41478
+ cause
41479
+ }) });
41336
41480
  };
41337
41481
  var Project = class Project extends Service()("react-doctor/Project") {
41338
41482
  static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (directory) {
@@ -41944,6 +42088,7 @@ const resolveGithubActionsScoreMetadata = (environment = process.env) => {
41944
42088
  ...actorAssociation !== void 0 ? { githubActorAssociation: actorAssociation } : {}
41945
42089
  };
41946
42090
  };
42091
+ const toNormalizedRelativePath = (filePath, rootDirectory) => Path.relative(Path.resolve(rootDirectory), Path.resolve(rootDirectory, filePath)).replaceAll("\\", "/") || ".";
41947
42092
  const NO_HOOKS = {
41948
42093
  beforeLint: () => void_,
41949
42094
  afterLint: () => void_
@@ -42042,7 +42187,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
42042
42187
  repo
42043
42188
  }).pipe(orElseSucceed(() => null)) : succeed$2(null));
42044
42189
  const lintIncludePaths = (input.skipJsxIncludeFilter ? input.includePaths.length > 0 ? [...input.includePaths] : void 0 : computeExplicitLintIncludePaths([...input.includePaths], project)) ?? resolveLintIncludePaths(scanDirectory, resolvedConfig.config, project);
42045
- const scannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
42190
+ const fallbackScannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
42046
42191
  const beforeLint = hooks.beforeLint ?? NO_HOOKS.beforeLint;
42047
42192
  const afterLint = hooks.afterLint ?? NO_HOOKS.afterLint;
42048
42193
  yield* beforeLint(project, lintIncludePaths ?? void 0);
@@ -42147,6 +42292,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
42147
42292
  let deadCodeCacheHit = null;
42148
42293
  let deadCodeSummaryCacheHits = null;
42149
42294
  let deadCodeSummaryCacheMisses = null;
42295
+ const lintFileCoverageState = { value: null };
42150
42296
  const baseLintStream = linterService.run({
42151
42297
  rootDirectory: scanDirectory,
42152
42298
  project,
@@ -42162,6 +42308,9 @@ const runInspect = (input, hooks = {}) => gen(function* () {
42162
42308
  lastReportedTotalFileCount = totalFileCount;
42163
42309
  runSync(scanProgress.update(`Scanning files (${scannedFileCount}/${totalFileCount})${workerCountSuffix}...`));
42164
42310
  },
42311
+ onFileCoverage: (coverage) => {
42312
+ lintFileCoverageState.value = coverage;
42313
+ },
42165
42314
  onCacheStats: (cacheHitFileCount, totalConsideredFileCount) => {
42166
42315
  lintCacheHitFileCount = cacheHitFileCount;
42167
42316
  lintCacheTotalFileCount = totalConsideredFileCount;
@@ -42192,7 +42341,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
42192
42341
  const lintFailureState = yield* get$2(lintFailure);
42193
42342
  yield* afterLint(lintFailureState.didFail);
42194
42343
  if (lintFailureState.didFail) yield* scanProgress.fail(formatLintFailText(lintFailureState.reasonTag, process.version));
42195
- const totalFileCount = lastReportedTotalFileCount || (lintIncludePaths?.length ?? project.sourceFileCount);
42344
+ const candidateFiles = lintFileCoverageState.value === null ? [] : [...new Set(lintFileCoverageState.value.candidateFiles.map((filePath) => toNormalizedRelativePath(filePath, scanDirectory)))];
42345
+ const analyzedFiles = lintFileCoverageState.value === null ? [] : [...new Set(lintFileCoverageState.value.analyzedFiles.map((filePath) => toNormalizedRelativePath(filePath, scanDirectory)))].sort();
42346
+ const totalFileCount = candidateFiles.length || lastReportedTotalFileCount || (lintIncludePaths?.length ?? project.sourceFileCount);
42347
+ const scannedFilePaths = input.suppressScanSummary ? candidateFiles.length > 0 ? candidateFiles.map((filePath) => Path.resolve(scanDirectory, filePath)) : fallbackScannedFilePaths : [];
42196
42348
  const scannedFilesLabel = `${totalFileCount} ${totalFileCount === 1 ? "file" : "files"}`;
42197
42349
  let deadCodeCollected = [];
42198
42350
  if (lintFailureState.didFail) {
@@ -42274,6 +42426,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
42274
42426
  deadCodeOverlapped: shouldOverlapDeadCode,
42275
42427
  scannedFileCount: totalFileCount,
42276
42428
  scannedFilePaths,
42429
+ analyzedFiles,
42277
42430
  scanElapsedMilliseconds,
42278
42431
  scanConcurrency,
42279
42432
  supplyChainOverlapTimedOut: supplyChainResult.timedOut,
@@ -42612,7 +42765,7 @@ const computeConfigFingerprint = (projectDirectory, version) => {
42612
42765
  /** Display name used in client-facing messages and progress titles. */
42613
42766
  const SERVER_DISPLAY_NAME = "React Doctor";
42614
42767
  /** Server version reported in `serverInfo`; injected at build, `dev` from source. */
42615
- const SERVER_VERSION = "0.7.3";
42768
+ const SERVER_VERSION = "0.7.4";
42616
42769
  /** `Diagnostic.source` shown next to every published diagnostic. */
42617
42770
  const DIAGNOSTIC_SOURCE = "react-doctor";
42618
42771
  /**
@@ -44663,5 +44816,5 @@ const startLanguageServer = () => {
44663
44816
  };
44664
44817
  //#endregion
44665
44818
  export { startLanguageServer };
44666
- !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]="6105fd41-b8bf-5c49-b0bd-8979e737f179")}catch(e){}}();
44667
- //# debugId=6105fd41-b8bf-5c49-b0bd-8979e737f179
44819
+ !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]="c686dc30-13d8-539e-acb4-e8b997f967e6")}catch(e){}}();
44820
+ //# debugId=c686dc30-13d8-539e-acb4-e8b997f967e6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor",
3
- "version": "0.7.3",
3
+ "version": "0.7.4-dev.11333b9",
4
4
  "description": "Your agent writes bad React. This catches it",
5
5
  "keywords": [
6
6
  "accessibility",
@@ -65,8 +65,8 @@
65
65
  "vscode-languageserver-textdocument": "^1.0.12",
66
66
  "vscode-uri": "^3.1.0",
67
67
  "yaml": "^2.9.0",
68
- "deslop-js": "0.7.3",
69
- "oxlint-plugin-react-doctor": "0.7.3"
68
+ "deslop-js": "0.7.4",
69
+ "oxlint-plugin-react-doctor": "0.7.4-dev.11333b9"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/babel__code-frame": "^7.27.0",
@@ -74,9 +74,9 @@
74
74
  "@xterm/headless": "^6.0.0",
75
75
  "commander": "^14.0.3",
76
76
  "ora": "^9.4.0",
77
- "@react-doctor/core": "0.7.3",
78
- "@react-doctor/language-server": "0.7.3",
79
- "@react-doctor/api": "0.7.3"
77
+ "@react-doctor/core": "0.7.4",
78
+ "@react-doctor/api": "0.7.4",
79
+ "@react-doctor/language-server": "0.7.4"
80
80
  },
81
81
  "engines": {
82
82
  "node": "^20.19.0 || >=22.13.0"