react-doctor 0.7.4 → 0.7.5-dev.037bd56

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]="ed5ebcb9-a117-5978-b792-fec60473d806")}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]="ed6bfef3-a274-5972-833b-77462a1156e2")}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";
@@ -8,8 +8,8 @@ import path from "node:path";
8
8
  import * as NFS from "node:fs";
9
9
  import fs, { mkdtempSync, rmSync } from "node:fs";
10
10
  import process$1 from "node:process";
11
- import * as ts from "typescript";
12
- import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
11
+ 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";
12
+ import ts from "typescript";
13
13
  import * as OS from "node:os";
14
14
  import os, { tmpdir } from "node:os";
15
15
  import { parseJSON5 } from "confbox";
@@ -22411,12 +22411,45 @@ var Diagnostic = class extends Class("Diagnostic")({
22411
22411
  relatedLocations: optional(ArraySchema(DiagnosticRelatedLocation)),
22412
22412
  fixGroupId: optional(String$1)
22413
22413
  }) {};
22414
+ var JsonReportDiagnosticV3 = class extends Class("JsonReportDiagnosticV3")({
22415
+ id: String$1,
22416
+ normalizedFilePath: String$1,
22417
+ filePath: String$1,
22418
+ plugin: String$1,
22419
+ rule: String$1,
22420
+ severity: Severity,
22421
+ tags: ArraySchema(String$1),
22422
+ title: optional(String$1),
22423
+ message: String$1,
22424
+ help: String$1,
22425
+ url: optional(String$1),
22426
+ line: Number$1,
22427
+ column: Number$1,
22428
+ offset: optional(Number$1),
22429
+ length: optional(Number$1),
22430
+ endLine: optional(Number$1),
22431
+ endColumn: optional(Number$1),
22432
+ category: String$1,
22433
+ matchByOccurrence: optional(Boolean$1),
22434
+ fileContext: optional(Literals(["test", "story"])),
22435
+ suppressionHint: optional(String$1),
22436
+ relatedLocations: optional(ArraySchema(DiagnosticRelatedLocation)),
22437
+ fixGroupId: optional(String$1)
22438
+ }) {};
22439
+ /**
22440
+ * Deterministic identity string for a diagnostic. Same diagnostic
22441
+ * across two scans yields the same identity; lets baselines,
22442
+ * suppression files, content-hash caches, and IDE "ignore this"
22443
+ * actions all key off one shared shape.
22444
+ */
22445
+ const buildDiagnosticIdentity = (input) => `${input.filePath}::${input.line}:${input.column}::${input.plugin}/${input.rule}`;
22414
22446
  const JsonReportMode = Literals([
22415
22447
  "full",
22416
22448
  "diff",
22417
22449
  "staged",
22418
22450
  "baseline"
22419
22451
  ]);
22452
+ const Framework = Literals(FRAMEWORK_TOKENS);
22420
22453
  var JsonReportSummary = class extends Class("JsonReportSummary")({
22421
22454
  errorCount: Number$1,
22422
22455
  warningCount: Number$1,
@@ -22454,13 +22487,22 @@ var JsonReportProjectEntry = class extends Class("JsonReportProjectEntry")({
22454
22487
  scannedFileCount: optional(Number$1),
22455
22488
  elapsedMilliseconds: Number$1
22456
22489
  }) {};
22457
- /**
22458
- * Versioned JsonReport schema. `JsonReport` is a `Schema.Union` so we
22459
- * can add `schemaVersion: 2` later as one new union member without
22460
- * breaking existing v1 consumers (the GitHub Action keys off the
22461
- * version literal). Today's union is single-arm; the shape is
22462
- * intentional.
22463
- */
22490
+ var JsonReportProjectEntryV3 = class extends Class("JsonReportProjectEntryV3")({
22491
+ directory: String$1,
22492
+ packageRoot: String$1,
22493
+ framework: Framework,
22494
+ project: Unknown,
22495
+ diagnostics: ArraySchema(JsonReportDiagnosticV3),
22496
+ score: Unknown,
22497
+ skippedChecks: ArraySchema(String$1),
22498
+ skippedCheckReasons: optional(Record$1(String$1, String$1)),
22499
+ analyzedFiles: ArraySchema(String$1),
22500
+ analyzedFileCount: Number$1,
22501
+ complete: Boolean$1,
22502
+ scannedFileCount: optional(Number$1),
22503
+ elapsedMilliseconds: Number$1
22504
+ }) {};
22505
+ /** Original full, diff, and staged report contract. */
22464
22506
  var JsonReportV1 = class extends Class("JsonReportV1")({
22465
22507
  schemaVersion: Literal(1),
22466
22508
  version: String$1,
@@ -22521,7 +22563,27 @@ var JsonReportV2 = class extends Class("JsonReportV2")({
22521
22563
  elapsedMilliseconds: Number$1,
22522
22564
  error: NullOr(JsonReportError)
22523
22565
  }) {};
22524
- Union([JsonReportV1, JsonReportV2]);
22566
+ var JsonReportV3 = class extends Class("JsonReportV3")({
22567
+ schemaVersion: Literal(3),
22568
+ version: String$1,
22569
+ ok: Boolean$1,
22570
+ directory: String$1,
22571
+ mode: JsonReportMode,
22572
+ baselineDegraded: optional(Boolean$1),
22573
+ reactDetected: optional(Boolean$1),
22574
+ diff: NullOr(JsonReportDiffInfo),
22575
+ baseline: optional(JsonReportBaseline),
22576
+ projects: ArraySchema(JsonReportProjectEntryV3),
22577
+ diagnostics: ArraySchema(JsonReportDiagnosticV3),
22578
+ summary: JsonReportSummary,
22579
+ elapsedMilliseconds: Number$1,
22580
+ error: NullOr(JsonReportError)
22581
+ }) {};
22582
+ Union([
22583
+ JsonReportV1,
22584
+ JsonReportV2,
22585
+ JsonReportV3
22586
+ ]);
22525
22587
  //#endregion
22526
22588
  //#region ../../node_modules/.pnpm/semver@7.7.4/node_modules/semver/internal/constants.js
22527
22589
  var require_constants$1 = /* @__PURE__ */ __commonJSMin$1(((exports, module) => {
@@ -36179,7 +36241,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
36179
36241
  const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
36180
36242
  const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
36181
36243
  const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
36182
- const CORE_PACKAGE_VERSION = "0.7.4";
36244
+ const CORE_PACKAGE_VERSION = "0.7.5";
36183
36245
  const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
36184
36246
  const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
36185
36247
  const SUPPLY_CHAIN_PLUGIN = "socket";
@@ -37085,6 +37147,22 @@ const isPackageJsonReanimatedAware = (packageJson) => {
37085
37147
  };
37086
37148
  return Object.hasOwn(allDependencies, REANIMATED_DEPENDENCY_NAME$1);
37087
37149
  };
37150
+ const SSR_DEPENDENCY_NAMES = new Set([
37151
+ "@react-router/cloudflare",
37152
+ "@react-router/node",
37153
+ "@react-router/serve",
37154
+ "vike",
37155
+ "vite-plugin-ssr"
37156
+ ]);
37157
+ const isPackageJsonSsrAware = (packageJson) => {
37158
+ const allDependencies = {
37159
+ ...packageJson.peerDependencies,
37160
+ ...packageJson.dependencies,
37161
+ ...packageJson.devDependencies,
37162
+ ...packageJson.optionalDependencies
37163
+ };
37164
+ return Object.keys(allDependencies).some((packageName) => SSR_DEPENDENCY_NAMES.has(packageName));
37165
+ };
37088
37166
  const getWorkspacePatterns = (rootDirectory, packageJson) => {
37089
37167
  const pnpmPatterns = parsePnpmWorkspacePatterns(rootDirectory);
37090
37168
  if (pnpmPatterns.length > 0) return pnpmPatterns;
@@ -37225,6 +37303,7 @@ const evaluateManifestFacts = (facts, packageJson, directory) => {
37225
37303
  }
37226
37304
  facts.hasReactNativeAwarePackage = facts.hasReactNativeAwarePackage || isPackageJsonReactNativeAware(packageJson);
37227
37305
  facts.hasReanimatedAwarePackage = facts.hasReanimatedAwarePackage || isPackageJsonReanimatedAware(packageJson);
37306
+ facts.hasSsrDependency = facts.hasSsrDependency || isPackageJsonSsrAware(packageJson);
37228
37307
  };
37229
37308
  const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGroup }) => {
37230
37309
  const facts = {
@@ -37246,6 +37325,7 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
37246
37325
  },
37247
37326
  hasReactNativeAwarePackage: false,
37248
37327
  hasReanimatedAwarePackage: false,
37328
+ hasSsrDependency: false,
37249
37329
  reanimatedVersion: null
37250
37330
  };
37251
37331
  evaluateManifestFacts(facts, rootPackageJson, rootDirectory);
@@ -37482,6 +37562,7 @@ const discoverProjectWithoutPackageJson = (directory) => {
37482
37562
  hasTypeScript: hasOwnTsConfig,
37483
37563
  hasReactCompiler: false,
37484
37564
  hasTanStackQuery: false,
37565
+ hasSsrDependency: false,
37485
37566
  preactVersion: null,
37486
37567
  preactMajorVersion: null,
37487
37568
  hasReactNativeWorkspace: false,
@@ -37601,6 +37682,7 @@ const discoverProject = (directory) => {
37601
37682
  hasTypeScript,
37602
37683
  hasReactCompiler: detectReactCompiler(directory, packageJson),
37603
37684
  hasTanStackQuery: hasTanStackQuery(packageJson),
37685
+ hasSsrDependency: workspaceFacts.hasSsrDependency,
37604
37686
  preactVersion,
37605
37687
  preactMajorVersion: parseReactMajor(preactVersion),
37606
37688
  hasReactNativeWorkspace,
@@ -38687,6 +38769,14 @@ var AmbiguousProject = class extends TaggedErrorClass()("AmbiguousProject", {
38687
38769
  return `Ambiguous project at ${this.directory}: found ${this.candidates.length} candidates (${this.candidates.join(", ")})`;
38688
38770
  }
38689
38771
  };
38772
+ var ProjectDiscoveryFailed = class extends TaggedErrorClass()("ProjectDiscoveryFailed", {
38773
+ directory: String$1,
38774
+ cause: Unknown
38775
+ }) {
38776
+ get message() {
38777
+ return `Project discovery failed for ${this.directory}: ${pretty(fail$6(this.cause))}`;
38778
+ }
38779
+ };
38690
38780
  var DeadCodeAnalysisFailed = class extends TaggedErrorClass()("DeadCodeAnalysisFailed", { cause: Unknown }) {
38691
38781
  get message() {
38692
38782
  return `Dead-code analysis failed: ${pretty(fail$6(this.cause))}`;
@@ -38721,6 +38811,7 @@ const ReactDoctorErrorReason = Union([
38721
38811
  ProjectNotFound,
38722
38812
  NoReactDependency,
38723
38813
  AmbiguousProject,
38814
+ ProjectDiscoveryFailed,
38724
38815
  DeadCodeAnalysisFailed,
38725
38816
  GitInvocationFailed,
38726
38817
  GitBaseBranchMissing,
@@ -40445,6 +40536,12 @@ const CLIENT_ONLY_FRAMEWORKS = new Set([
40445
40536
  "react-native",
40446
40537
  "expo"
40447
40538
  ]);
40539
+ const SSR_FRAMEWORKS = new Set([
40540
+ "nextjs",
40541
+ "remix",
40542
+ "gatsby",
40543
+ "tanstack-start"
40544
+ ]);
40448
40545
  const addMajorLadder = (capabilities, name, major, earliest, latest) => {
40449
40546
  if (major === null) return;
40450
40547
  const cappedMajor = Math.min(major, latest);
@@ -40457,6 +40554,7 @@ const buildCapabilities = (project) => {
40457
40554
  if (project.framework === "expo" || project.framework === "react-native" || project.hasReactNativeWorkspace) capabilities.add("react-native");
40458
40555
  if (project.expoVersion !== null) capabilities.add("expo");
40459
40556
  if ((project.framework === "nextjs" || project.framework === "tanstack-start" || project.framework === "remix") && !project.isStaticExport) capabilities.add("server-actions");
40557
+ if (SSR_FRAMEWORKS.has(project.framework) || project.hasSsrDependency) capabilities.add("ssr");
40460
40558
  if (CLIENT_ONLY_FRAMEWORKS.has(project.framework)) capabilities.add("client-only");
40461
40559
  if (project.isStaticExport) capabilities.add("nextjs:static-export");
40462
40560
  if (project.nextjsMajorVersion !== null && project.nextjsMajorVersion >= 15) capabilities.add("nextjs:15");
@@ -42270,7 +42368,7 @@ const normalizeConfigForHash = (config) => {
42270
42368
  if (Array.isArray(clone?.jsPlugins)) clone.jsPlugins = clone.jsPlugins.map((_, index) => `<plugin:${index}>`);
42271
42369
  return clone;
42272
42370
  };
42273
- const computeRulesetHash = (input) => crypto.createHash("sha1").update(JSON.stringify(normalizeConfigForHash(input.config))).update("\0").update([...input.toolchainVersions].join("\0")).update("\0").update([...input.ignorePatterns].join("\n")).update("\0").update(input.tsconfigContent ?? "").digest("hex");
42371
+ const computeRulesetHash = (input) => crypto.createHash("sha1").update(JSON.stringify(normalizeConfigForHash(input.config))).update("\0").update([...input.toolchainVersions].join("\0")).update("\0").update([...input.ignorePatterns].join("\n")).update("\0").update(input.tsconfigContent ?? "").update("\0").update(input.respectInlineDisables ? "respect-inline-disables" : "audit-mode").digest("hex");
42274
42372
  /**
42275
42373
  * Loads a plugin module via the local require resolver and extracts
42276
42374
  * `(name, ruleNames)` from either `module.exports.meta + rules` or
@@ -42306,7 +42404,7 @@ const resolveReactHooksJsPlugin = (hasReactCompiler, customRulesOnly) => {
42306
42404
  } catch {
42307
42405
  return null;
42308
42406
  }
42309
- const { ruleNames } = readPluginShape(pluginSpecifier, (spec) => bundledRequire$1(spec));
42407
+ const { ruleNames } = readPluginShape(pluginSpecifier, (specifier) => bundledRequire$1(specifier));
42310
42408
  return {
42311
42409
  entry: {
42312
42410
  name: "react-hooks-js",
@@ -42714,6 +42812,23 @@ const resolveTsConfigRelativePath = (rootDirectory) => {
42714
42812
  for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
42715
42813
  return null;
42716
42814
  };
42815
+ const DERIVED_STATE_RULE_PRIORITY = new Map([
42816
+ ["no-initialize-state", 0],
42817
+ ["no-adjust-state-on-prop-change", 1],
42818
+ ["no-derived-state", 2],
42819
+ ["no-derived-state-effect", 3]
42820
+ ]);
42821
+ const doDiagnosticSpansOverlap = (first, second) => {
42822
+ if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
42823
+ if (first.offset !== void 0 && second.offset !== void 0) {
42824
+ const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
42825
+ const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
42826
+ return first.offset < secondEnd && second.offset < firstEnd;
42827
+ }
42828
+ const firstEndLine = first.endLine ?? first.line;
42829
+ const secondEndLine = second.endLine ?? second.line;
42830
+ return first.line <= secondEndLine && second.line <= firstEndLine;
42831
+ };
42717
42832
  const dedupeDiagnostics = (diagnostics) => {
42718
42833
  const seenKeys = /* @__PURE__ */ new Set();
42719
42834
  const uniqueDiagnostics = [];
@@ -42721,6 +42836,16 @@ const dedupeDiagnostics = (diagnostics) => {
42721
42836
  const key = `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}\u0000${diagnostic.plugin}\u0000${diagnostic.rule}\u0000${diagnostic.severity}\u0000${diagnostic.message}`;
42722
42837
  if (seenKeys.has(key)) continue;
42723
42838
  seenKeys.add(key);
42839
+ const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
42840
+ if (priority !== void 0) {
42841
+ const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && candidate.severity === diagnostic.severity && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
42842
+ if (overlappingDiagnosticIndex >= 0) {
42843
+ const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
42844
+ const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
42845
+ if (existingPriority !== void 0 && priority < existingPriority) uniqueDiagnostics[overlappingDiagnosticIndex] = diagnostic;
42846
+ continue;
42847
+ }
42848
+ }
42724
42849
  uniqueDiagnostics.push(diagnostic);
42725
42850
  }
42726
42851
  return uniqueDiagnostics;
@@ -43594,6 +43719,11 @@ const buildOxlintChildEnv = (sourceEnv) => {
43594
43719
  if (!isCompileCacheDisabled && !isCompileCacheAlreadySet) childEnv.NODE_COMPILE_CACHE = Path.join(os.tmpdir(), NODE_COMPILE_CACHE_DIR_NAME);
43595
43720
  return childEnv;
43596
43721
  };
43722
+ const buildProfiledNodeArguments = (input) => [
43723
+ ...input.cpuProfileDirectory ? ["--cpu-prof", `--cpu-prof-dir=${input.cpuProfileDirectory}`] : [],
43724
+ ...input.heapProfileDirectory ? ["--heap-prof", `--heap-prof-dir=${input.heapProfileDirectory}`] : [],
43725
+ ...input.argumentsList
43726
+ ];
43597
43727
  const SANITIZED_ENV = buildOxlintChildEnv(process.env);
43598
43728
  /**
43599
43729
  * Spawn one oxlint subprocess with hard ceilings on wall time and
@@ -43618,7 +43748,11 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
43618
43748
  reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) }));
43619
43749
  return;
43620
43750
  }
43621
- const child = spawn(nodeBinaryPath, args, {
43751
+ const child = spawn(nodeBinaryPath, buildProfiledNodeArguments({
43752
+ argumentsList: args,
43753
+ cpuProfileDirectory: SANITIZED_ENV.REACT_DOCTOR_CPU_PROFILE_DIR,
43754
+ heapProfileDirectory: SANITIZED_ENV.REACT_DOCTOR_HEAP_PROFILE_DIR
43755
+ }), {
43622
43756
  cwd: rootDirectory,
43623
43757
  env: SANITIZED_ENV,
43624
43758
  stdio: [
@@ -43877,6 +44011,8 @@ const spawnLintBatches = async (input) => {
43877
44011
  const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
43878
44012
  reportSkippedFiles(droppedFiles, (fileListPreview) => `${droppedFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
43879
44013
  reportSkippedFiles(outcome.deadlineSkippedFiles, (fileListPreview) => `${outcome.deadlineSkippedFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
44014
+ const unavailableFiles = new Set([...droppedFiles, ...outcome.deadlineSkippedFiles]);
44015
+ input.onAnalyzedFiles?.([...new Set(fileBatches.flat().filter((filePath) => !unavailableFiles.has(filePath)))].sort());
43880
44016
  return dedupeDiagnostics(diagnostics);
43881
44017
  };
43882
44018
  let didValidate = false;
@@ -44133,12 +44269,18 @@ const reactHooksJsPluginDropNote = (error) => {
44133
44269
  * 6. always restore disable directives + clean up the temp dir
44134
44270
  */
44135
44271
  const runOxlint = async (options) => {
44136
- 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;
44272
+ 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;
44137
44273
  const serverAuthFunctionNames = Array.isArray(userConfig?.serverAuthFunctionNames) ? userConfig.serverAuthFunctionNames.filter((entry) => typeof entry === "string" && entry.length > 0) : void 0;
44138
44274
  const severityControls = buildRuleSeverityControls(userConfig);
44139
44275
  validateRuleRegistration();
44140
44276
  resetManifestCaches();
44141
- if (includePaths !== void 0 && includePaths.length === 0) return [];
44277
+ if (includePaths !== void 0 && includePaths.length === 0) {
44278
+ onFileCoverage?.({
44279
+ candidateFiles: [],
44280
+ analyzedFiles: []
44281
+ });
44282
+ return [];
44283
+ }
44142
44284
  const pluginPath = resolvePluginPath();
44143
44285
  const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
44144
44286
  const userPlugins = resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
@@ -44199,6 +44341,7 @@ const runOxlint = async (options) => {
44199
44341
  const runConfigOverFiles = async (buildConfigForPass, configFileName, files, fileProgress) => {
44200
44342
  if (files.length === 0) return {
44201
44343
  diagnostics: [],
44344
+ analyzedFiles: [],
44202
44345
  didDropReactHooksJsPlugin: false,
44203
44346
  hadPartialFailure: false
44204
44347
  };
@@ -44210,24 +44353,32 @@ const runOxlint = async (options) => {
44210
44353
  const passConfigPath = Path.join(configDirectory, configFileName);
44211
44354
  const passBaseArgs = makeBaseArgs(passConfigPath);
44212
44355
  const passFileBatches = buildFileBatches(passBaseArgs, files);
44213
- const spawnPass = () => spawnLintBatches({
44214
- baseArgs: passBaseArgs,
44215
- fileBatches: passFileBatches,
44216
- rootDirectory,
44217
- nodeBinaryPath,
44218
- project,
44219
- onPartialFailure: reportPartialFailure,
44220
- onFileProgress: fileProgress,
44221
- spawnTimeoutMs,
44222
- outputMaxBytes,
44223
- concurrency: options.concurrency,
44224
- signal: options.signal,
44225
- deadlineEpochMs: options.deadlineEpochMs
44226
- });
44356
+ let analyzedFiles = [];
44357
+ const spawnPass = () => {
44358
+ analyzedFiles = [];
44359
+ return spawnLintBatches({
44360
+ baseArgs: passBaseArgs,
44361
+ fileBatches: passFileBatches,
44362
+ rootDirectory,
44363
+ nodeBinaryPath,
44364
+ project,
44365
+ onPartialFailure: reportPartialFailure,
44366
+ onAnalyzedFiles: (filePaths) => {
44367
+ analyzedFiles = filePaths;
44368
+ },
44369
+ onFileProgress: fileProgress,
44370
+ spawnTimeoutMs,
44371
+ outputMaxBytes,
44372
+ concurrency: options.concurrency,
44373
+ signal: options.signal,
44374
+ deadlineEpochMs: options.deadlineEpochMs
44375
+ });
44376
+ };
44227
44377
  writeOxlintConfig(passConfigPath, buildConfigForPass({}));
44228
44378
  try {
44229
44379
  return {
44230
44380
  diagnostics: await spawnPass(),
44381
+ analyzedFiles,
44231
44382
  didDropReactHooksJsPlugin: false,
44232
44383
  hadPartialFailure
44233
44384
  };
@@ -44239,12 +44390,13 @@ const runOxlint = async (options) => {
44239
44390
  reportPartialFailure(reactHooksJsDropNote);
44240
44391
  return {
44241
44392
  diagnostics,
44393
+ analyzedFiles,
44242
44394
  didDropReactHooksJsPlugin: true,
44243
44395
  hadPartialFailure
44244
44396
  };
44245
44397
  }
44246
44398
  };
44247
- if (perFileLintCacheEnabled && respectInlineDisables && !project.hasReactCompiler && extendsPaths.length === 0 && userPlugins.length === 0) {
44399
+ if (perFileLintCacheEnabled && !project.hasReactCompiler && extendsPaths.length === 0 && userPlugins.length === 0) {
44248
44400
  const rulesetHash = computeRulesetHash({
44249
44401
  config: buildConfig({
44250
44402
  extendsPaths: [],
@@ -44252,7 +44404,8 @@ const runOxlint = async (options) => {
44252
44404
  }),
44253
44405
  toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
44254
44406
  ignorePatterns: combinedPatterns,
44255
- tsconfigContent
44407
+ tsconfigContent,
44408
+ respectInlineDisables
44256
44409
  });
44257
44410
  const cache = createFileLintCache(resolveReactDoctorCacheDir(rootDirectory), rulesetHash);
44258
44411
  const cacheKeyByFile = /* @__PURE__ */ new Map();
@@ -44295,7 +44448,8 @@ const runOxlint = async (options) => {
44295
44448
  }),
44296
44449
  toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
44297
44450
  ignorePatterns: combinedPatterns,
44298
- tsconfigContent
44451
+ tsconfigContent,
44452
+ respectInlineDisables
44299
44453
  })) : null;
44300
44454
  const probeAnswers = createSidecarProbeAnswerResolver({
44301
44455
  rootDirectory,
@@ -44314,6 +44468,7 @@ const runOxlint = async (options) => {
44314
44468
  const missFileSet = new Set(missFiles);
44315
44469
  const hitFiles = candidateFiles.filter((candidateFile) => !missFileSet.has(candidateFile));
44316
44470
  const sidecarReplayedDiagnostics = [];
44471
+ const sidecarReplayedFiles = [];
44317
44472
  const sidecarLintFiles = [];
44318
44473
  if (sidecarCache === null) sidecarLintFiles.push(...hitFiles);
44319
44474
  else {
@@ -44321,8 +44476,10 @@ const runOxlint = async (options) => {
44321
44476
  for (const hitFile of hitFiles) {
44322
44477
  const cacheKey = cacheKeyByFile.get(hitFile);
44323
44478
  const entry = cacheKey === void 0 ? null : sidecarCache.lookup(cacheKey);
44324
- if (entry !== null && entry.probes.every((probe) => probeAnswers.answerFor(probe.kind, probe.path) === probe.answer)) sidecarReplayedDiagnostics.push(...entry.diagnostics);
44325
- else sidecarLintFiles.push(hitFile);
44479
+ if (entry !== null && entry.probes.every((probe) => probeAnswers.answerFor(probe.kind, probe.path) === probe.answer)) {
44480
+ sidecarReplayedFiles.push(hitFile);
44481
+ sidecarReplayedDiagnostics.push(...entry.diagnostics);
44482
+ } else sidecarLintFiles.push(hitFile);
44326
44483
  if (performance.now() - verifySliceStartedAt >= 12) {
44327
44484
  await yieldToEventLoop();
44328
44485
  verifySliceStartedAt = performance.now();
@@ -44348,6 +44505,7 @@ const runOxlint = async (options) => {
44348
44505
  sidecarRuleIdFilter: new Set(unboundedSidecarRuleIds)
44349
44506
  }), "oxlintrc.sidecar-unbounded.json", hitFiles, void 0) : {
44350
44507
  diagnostics: [],
44508
+ analyzedFiles: [],
44351
44509
  didDropReactHooksJsPlugin: false,
44352
44510
  hadPartialFailure: false
44353
44511
  };
@@ -44378,6 +44536,15 @@ const runOxlint = async (options) => {
44378
44536
  probesByFile: await missProbesTask
44379
44537
  }]
44380
44538
  });
44539
+ const analyzedFileSet = new Set(fullResult.analyzedFiles);
44540
+ const boundedSidecarAnalyzedFileSet = new Set(boundedSidecarResult.analyzedFiles);
44541
+ const unboundedSidecarAnalyzedFileSet = new Set(unboundedSidecarResult.analyzedFiles);
44542
+ const sidecarReplayedFileSet = new Set(sidecarReplayedFiles);
44543
+ 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);
44544
+ onFileCoverage?.({
44545
+ candidateFiles,
44546
+ analyzedFiles: candidateFiles.filter((candidateFile) => analyzedFileSet.has(candidateFile))
44547
+ });
44381
44548
  return dedupeDiagnostics([
44382
44549
  ...replayedDiagnostics,
44383
44550
  ...fullResult.diagnostics,
@@ -44388,6 +44555,7 @@ const runOxlint = async (options) => {
44388
44555
  }
44389
44556
  const baseArgs = makeBaseArgs(configPath);
44390
44557
  const fileBatches = buildFileBatches(baseArgs, candidateFiles);
44558
+ let analyzedFiles = [];
44391
44559
  const runBatches = () => spawnLintBatches({
44392
44560
  baseArgs,
44393
44561
  fileBatches,
@@ -44395,6 +44563,9 @@ const runOxlint = async (options) => {
44395
44563
  nodeBinaryPath,
44396
44564
  project,
44397
44565
  onPartialFailure,
44566
+ onAnalyzedFiles: (filePaths) => {
44567
+ analyzedFiles = filePaths;
44568
+ },
44398
44569
  onFileProgress: options.onFileProgress,
44399
44570
  spawnTimeoutMs,
44400
44571
  outputMaxBytes,
@@ -44404,7 +44575,12 @@ const runOxlint = async (options) => {
44404
44575
  });
44405
44576
  writeOxlintConfig(configPath, buildConfig({ extendsPaths }));
44406
44577
  try {
44407
- return await runBatches();
44578
+ const diagnostics = await runBatches();
44579
+ onFileCoverage?.({
44580
+ candidateFiles,
44581
+ analyzedFiles
44582
+ });
44583
+ return diagnostics;
44408
44584
  } catch (error) {
44409
44585
  const reactHooksJsDropNote = reactHooksJsPluginDropNote(error);
44410
44586
  if (reactHooksJsDropNote !== null) {
@@ -44414,11 +44590,20 @@ const runOxlint = async (options) => {
44414
44590
  }));
44415
44591
  const diagnostics = await runBatches();
44416
44592
  onPartialFailure?.(reactHooksJsDropNote);
44593
+ onFileCoverage?.({
44594
+ candidateFiles,
44595
+ analyzedFiles
44596
+ });
44417
44597
  return diagnostics;
44418
44598
  }
44419
44599
  if (extendsPaths.length === 0) throw error;
44420
44600
  writeOxlintConfig(configPath, buildConfig({ extendsPaths: [] }));
44421
- return await runBatches();
44601
+ const diagnostics = await runBatches();
44602
+ onFileCoverage?.({
44603
+ candidateFiles,
44604
+ analyzedFiles
44605
+ });
44606
+ return diagnostics;
44422
44607
  }
44423
44608
  } finally {
44424
44609
  restoreDisableDirectives();
@@ -44498,6 +44683,7 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
44498
44683
  onPartialFailure: (reason) => {
44499
44684
  collectedFailures.push(reason);
44500
44685
  },
44686
+ onFileCoverage: input.onFileCoverage,
44501
44687
  onFileProgress: input.onFileProgress,
44502
44688
  perFileLintCacheEnabled,
44503
44689
  sidecarLintCacheEnabled,
@@ -44590,7 +44776,10 @@ const translateProjectInfoError = (cause, directory) => {
44590
44776
  directory: cause.directory,
44591
44777
  candidates: cause.candidates
44592
44778
  }) });
44593
- return new ReactDoctorError({ reason: new ProjectNotFound({ directory }) });
44779
+ return new ReactDoctorError({ reason: new ProjectDiscoveryFailed({
44780
+ directory,
44781
+ cause
44782
+ }) });
44594
44783
  };
44595
44784
  var Project = class Project extends Service()("react-doctor/Project") {
44596
44785
  static layerNode = succeed$3(Project, Project.of({ discover: fn("Project.discover")(function* (directory) {
@@ -45202,6 +45391,7 @@ const resolveGithubActionsScoreMetadata = (environment = process.env) => {
45202
45391
  ...actorAssociation !== void 0 ? { githubActorAssociation: actorAssociation } : {}
45203
45392
  };
45204
45393
  };
45394
+ const toNormalizedRelativePath = (filePath, rootDirectory) => Path.relative(Path.resolve(rootDirectory), Path.resolve(rootDirectory, filePath)).replaceAll("\\", "/") || ".";
45205
45395
  const NO_HOOKS = {
45206
45396
  beforeLint: () => void_,
45207
45397
  afterLint: () => void_
@@ -45300,7 +45490,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
45300
45490
  repo
45301
45491
  }).pipe(orElseSucceed(() => null)) : succeed$2(null));
45302
45492
  const lintIncludePaths = (input.skipJsxIncludeFilter ? input.includePaths.length > 0 ? [...input.includePaths] : void 0 : computeExplicitLintIncludePaths([...input.includePaths], project)) ?? resolveLintIncludePaths(scanDirectory, resolvedConfig.config, project);
45303
- const scannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
45493
+ const fallbackScannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
45304
45494
  const beforeLint = hooks.beforeLint ?? NO_HOOKS.beforeLint;
45305
45495
  const afterLint = hooks.afterLint ?? NO_HOOKS.afterLint;
45306
45496
  yield* beforeLint(project, lintIncludePaths ?? void 0);
@@ -45405,6 +45595,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
45405
45595
  let deadCodeCacheHit = null;
45406
45596
  let deadCodeSummaryCacheHits = null;
45407
45597
  let deadCodeSummaryCacheMisses = null;
45598
+ const lintFileCoverageState = { value: null };
45408
45599
  const baseLintStream = linterService.run({
45409
45600
  rootDirectory: scanDirectory,
45410
45601
  project,
@@ -45420,6 +45611,9 @@ const runInspect = (input, hooks = {}) => gen(function* () {
45420
45611
  lastReportedTotalFileCount = totalFileCount;
45421
45612
  runSync(scanProgress.update(`Scanning files (${scannedFileCount}/${totalFileCount})${workerCountSuffix}...`));
45422
45613
  },
45614
+ onFileCoverage: (coverage) => {
45615
+ lintFileCoverageState.value = coverage;
45616
+ },
45423
45617
  onCacheStats: (cacheHitFileCount, totalConsideredFileCount) => {
45424
45618
  lintCacheHitFileCount = cacheHitFileCount;
45425
45619
  lintCacheTotalFileCount = totalConsideredFileCount;
@@ -45450,7 +45644,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
45450
45644
  const lintFailureState = yield* get$2(lintFailure);
45451
45645
  yield* afterLint(lintFailureState.didFail);
45452
45646
  if (lintFailureState.didFail) yield* scanProgress.fail(formatLintFailText(lintFailureState.reasonTag, process.version));
45453
- const totalFileCount = lastReportedTotalFileCount || (lintIncludePaths?.length ?? project.sourceFileCount);
45647
+ const candidateFiles = lintFileCoverageState.value === null ? [] : [...new Set(lintFileCoverageState.value.candidateFiles.map((filePath) => toNormalizedRelativePath(filePath, scanDirectory)))];
45648
+ const analyzedFiles = lintFileCoverageState.value === null ? [] : [...new Set(lintFileCoverageState.value.analyzedFiles.map((filePath) => toNormalizedRelativePath(filePath, scanDirectory)))].sort();
45649
+ const totalFileCount = candidateFiles.length || lastReportedTotalFileCount || (lintIncludePaths?.length ?? project.sourceFileCount);
45650
+ const scannedFilePaths = input.suppressScanSummary ? candidateFiles.length > 0 ? candidateFiles.map((filePath) => Path.resolve(scanDirectory, filePath)) : fallbackScannedFilePaths : [];
45454
45651
  const scannedFilesLabel = `${totalFileCount} ${totalFileCount === 1 ? "file" : "files"}`;
45455
45652
  let deadCodeCollected = [];
45456
45653
  if (lintFailureState.didFail) {
@@ -45532,6 +45729,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
45532
45729
  deadCodeOverlapped: shouldOverlapDeadCode,
45533
45730
  scannedFileCount: totalFileCount,
45534
45731
  scannedFilePaths,
45732
+ analyzedFiles,
45535
45733
  scanElapsedMilliseconds,
45536
45734
  scanConcurrency,
45537
45735
  supplyChainOverlapTimedOut: supplyChainResult.timedOut,
@@ -45807,7 +46005,7 @@ const buildJsonReportError = (input) => {
45807
46005
  sentryEventId
45808
46006
  };
45809
46007
  return {
45810
- schemaVersion: 1,
46008
+ schemaVersion: 3,
45811
46009
  version: input.version,
45812
46010
  ok: false,
45813
46011
  directory: input.directory,
@@ -45852,6 +46050,7 @@ const summarizeDiagnostics = (diagnostics, worstScore = null, worstScoreLabel =
45852
46050
  * React-runtime rule family was gated off for the project.
45853
46051
  */
45854
46052
  const hasReactRuntime = (project) => project.reactVersion !== null || project.preactVersion !== null;
46053
+ const isScanComplete = (input) => input.analyzedFileCount !== void 0 && input.scannedFileCount !== void 0 && input.skippedCheckCount === 0 && input.skippedCheckReasonCount === 0 && input.analyzedFileCount === input.scannedFileCount;
45855
46054
  const toJsonDiff = (diff) => {
45856
46055
  if (!diff) return null;
45857
46056
  return {
@@ -45874,21 +46073,68 @@ const findWorstScoredProject = (projects) => {
45874
46073
  }
45875
46074
  return worst;
45876
46075
  };
46076
+ const toJsonReportDiagnostic = (diagnostic, projectRoot, reportRoot) => {
46077
+ const normalizedFilePath = toNormalizedRelativePath(diagnostic.filePath, projectRoot);
46078
+ const reportRelativeFilePath = toNormalizedRelativePath(Path.resolve(projectRoot, diagnostic.filePath), reportRoot);
46079
+ const ruleIdentity = getDiagnosticRuleIdentity(diagnostic);
46080
+ return {
46081
+ ...diagnostic,
46082
+ id: buildDiagnosticIdentity({
46083
+ filePath: reportRelativeFilePath,
46084
+ line: diagnostic.line,
46085
+ column: diagnostic.column,
46086
+ plugin: diagnostic.plugin,
46087
+ rule: diagnostic.rule
46088
+ }),
46089
+ normalizedFilePath,
46090
+ category: ruleIdentity.category,
46091
+ tags: [...new Set(ruleIdentity.tags)].sort()
46092
+ };
46093
+ };
45877
46094
  const buildJsonReport = (input) => {
45878
- const projects = input.scans.map(({ directory, result }) => ({
45879
- directory,
45880
- project: result.project,
45881
- diagnostics: result.diagnostics,
45882
- score: result.score,
45883
- skippedChecks: result.skippedChecks,
45884
- ...result.skippedCheckReasons ? { skippedCheckReasons: result.skippedCheckReasons } : {},
45885
- ...typeof result.scannedFileCount === "number" ? { scannedFileCount: result.scannedFileCount } : {},
45886
- elapsedMilliseconds: result.elapsedMilliseconds
45887
- }));
46095
+ const projects = input.scans.map(({ directory, result }) => {
46096
+ const skippedChecks = result.skippedChecks ?? [];
46097
+ const analyzedFiles = [...new Set((result.analyzedFiles ?? []).map((filePath) => toNormalizedRelativePath(filePath, result.project.rootDirectory)))].sort();
46098
+ const scannedFileCount = result.scannedFileCount ?? result.project.sourceFileCount;
46099
+ return {
46100
+ directory,
46101
+ packageRoot: result.project.rootDirectory,
46102
+ framework: result.project.framework,
46103
+ project: result.project,
46104
+ diagnostics: result.diagnostics.map((diagnostic) => toJsonReportDiagnostic(diagnostic, result.project.rootDirectory, input.directory)),
46105
+ score: result.score,
46106
+ skippedChecks,
46107
+ ...result.skippedCheckReasons ? { skippedCheckReasons: result.skippedCheckReasons } : {},
46108
+ analyzedFiles,
46109
+ analyzedFileCount: analyzedFiles.length,
46110
+ complete: isScanComplete({
46111
+ analyzedFileCount: result.analyzedFiles === void 0 ? void 0 : analyzedFiles.length,
46112
+ scannedFileCount,
46113
+ skippedCheckCount: skippedChecks.length,
46114
+ skippedCheckReasonCount: Object.keys(result.skippedCheckReasons ?? {}).length
46115
+ }),
46116
+ ...typeof result.scannedFileCount === "number" ? { scannedFileCount } : {},
46117
+ elapsedMilliseconds: result.elapsedMilliseconds
46118
+ };
46119
+ });
45888
46120
  const flattenedDiagnostics = projects.flatMap((entry) => entry.diagnostics);
45889
46121
  const worstScoredProject = findWorstScoredProject(projects);
45890
- const summary = summarizeDiagnostics(flattenedDiagnostics, worstScoredProject?.score?.score ?? null, worstScoredProject?.score?.label ?? null);
45891
- const shared = {
46122
+ const diagnosticSummary = summarizeDiagnostics(flattenedDiagnostics, worstScoredProject?.score?.score ?? null, worstScoredProject?.score?.label ?? null);
46123
+ const affectedFileCount = projects.reduce((totalAffectedFileCount, project) => totalAffectedFileCount + new Set(project.diagnostics.map((diagnostic) => diagnostic.normalizedFilePath)).size, 0);
46124
+ const summary = {
46125
+ ...diagnosticSummary,
46126
+ affectedFileCount
46127
+ };
46128
+ return {
46129
+ schemaVersion: 3,
46130
+ mode: input.baseline !== void 0 ? "baseline" : input.baselineDegraded && input.mode === "baseline" ? "diff" : input.mode,
46131
+ ...input.baseline ? { baseline: {
46132
+ baseRef: input.baseline.baseRef,
46133
+ newCount: summary.totalDiagnosticCount,
46134
+ fixedCount: input.baseline.fixedCount,
46135
+ baseTotalCount: input.baseline.baseTotalCount
46136
+ } } : {},
46137
+ ...input.baselineDegraded ? { baselineDegraded: true } : {},
45892
46138
  ...input.scans.length > 0 ? { reactDetected: input.scans.some((scan) => hasReactRuntime(scan.result.project)) } : {},
45893
46139
  version: input.version,
45894
46140
  ok: true,
@@ -45900,39 +46146,26 @@ const buildJsonReport = (input) => {
45900
46146
  elapsedMilliseconds: input.totalElapsedMilliseconds,
45901
46147
  error: null
45902
46148
  };
45903
- if (input.baseline) return {
45904
- schemaVersion: 2,
45905
- mode: "baseline",
45906
- baseline: {
45907
- baseRef: input.baseline.baseRef,
45908
- newCount: summary.totalDiagnosticCount,
45909
- fixedCount: input.baseline.fixedCount,
45910
- baseTotalCount: input.baseline.baseTotalCount
45911
- },
45912
- ...shared
45913
- };
45914
- return {
45915
- schemaVersion: 1,
45916
- mode: input.mode,
45917
- ...input.baselineDegraded ? { baselineDegraded: true } : {},
45918
- ...shared
45919
- };
45920
46149
  };
45921
46150
  /**
45922
46151
  * Single source of truth for the skipped-check accounting shared by the
45923
46152
  * CLI renderer (`react-doctor/src/inspect.ts → finalizeAndRender`) and the
45924
46153
  * programmatic shell (`@react-doctor/api → diagnose()`). Both surface a
45925
- * failed lint / dead-code pass instead of a false "all clear", so the
46154
+ * failed or partial analysis pass instead of a false "all clear", so the
45926
46155
  * branch logic lives here once.
45927
46156
  */
45928
46157
  const buildSkippedChecks = (input) => {
45929
46158
  const skippedChecks = [];
45930
46159
  if (input.didLintFail) skippedChecks.push("lint");
45931
46160
  if (input.didDeadCodeFail) skippedChecks.push("dead-code");
46161
+ if (input.supplyChainOverlapTimedOut) skippedChecks.push("supply-chain");
46162
+ if (input.securityScanFailed) skippedChecks.push("security-scan");
45932
46163
  const skippedCheckReasons = {};
45933
46164
  if (input.didLintFail && input.lintFailureReason !== null) skippedCheckReasons.lint = input.lintFailureReason;
45934
46165
  else if (input.lintPartialFailures.length > 0) skippedCheckReasons["lint:partial"] = input.lintPartialFailures.join("; ");
45935
46166
  if (input.didDeadCodeFail && input.deadCodeFailureReason !== null) skippedCheckReasons["dead-code"] = input.deadCodeFailureReason;
46167
+ if (input.supplyChainOverlapTimedOut) skippedCheckReasons["supply-chain"] = "Supply-chain analysis timed out and was skipped.";
46168
+ if (input.securityScanFailed) skippedCheckReasons["security-scan"] = "Security scan failed and was skipped.";
45936
46169
  return {
45937
46170
  skippedChecks,
45938
46171
  skippedCheckReasons
@@ -46347,7 +46580,7 @@ const GH_DEFAULT_BRANCH_PROBE_TIMEOUT_MS = 5e3;
46347
46580
  const CI_TRUST_COMPANIES = "PayPal, Rippling, and Alibaba";
46348
46581
  const WELCOME_EXPLANATION_HOLD_MS = 1e3;
46349
46582
  const CATEGORY_COUNTUP_SETTLE_HOLD_MS = 1e3;
46350
- const INTERNAL_ERROR_JSON_FALLBACK = "{\"schemaVersion\":1,\"ok\":false,\"error\":{\"message\":\"Internal error\",\"name\":\"Error\",\"chain\":[]}}\n";
46583
+ const INTERNAL_ERROR_JSON_FALLBACK = "{\"schemaVersion\":3,\"version\":\"unknown\",\"ok\":false,\"directory\":\"\",\"mode\":\"full\",\"diff\":null,\"projects\":[],\"diagnostics\":[],\"summary\":{\"errorCount\":0,\"warningCount\":0,\"affectedFileCount\":0,\"totalDiagnosticCount\":0,\"score\":null,\"scoreLabel\":null},\"elapsedMilliseconds\":0,\"error\":{\"message\":\"Internal error\",\"name\":\"Error\",\"chain\":[]}}\n";
46351
46584
  const SENTRY_FLUSH_TIMEOUT_MS = 2e3;
46352
46585
  const NANOSECONDS_PER_SECOND = 1000000000n;
46353
46586
  const METRIC = {
@@ -46434,7 +46667,7 @@ const makeNoopConsole = () => ({
46434
46667
  });
46435
46668
  //#endregion
46436
46669
  //#region src/cli/utils/version.ts
46437
- const VERSION = "0.7.4";
46670
+ const VERSION = "0.7.5-dev.037bd56";
46438
46671
  //#endregion
46439
46672
  //#region src/cli/utils/json-mode.ts
46440
46673
  let context = null;
@@ -46803,13 +47036,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
46803
47036
  * uploads source-map artifacts under, so stack frames symbolicate. Honors the
46804
47037
  * standard `SENTRY_RELEASE` override.
46805
47038
  */
46806
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.4`;
47039
+ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.5-dev.037bd56`;
46807
47040
  /**
46808
47041
  * Deployment environment shown in Sentry's environment filter. Defaults to
46809
47042
  * `production` for tagged releases and `development` for dev/unbuilt versions,
46810
47043
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
46811
47044
  */
46812
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.4") ? "development" : "production");
47045
+ const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.5-dev.037bd56") ? "development" : "production");
46813
47046
  /**
46814
47047
  * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
46815
47048
  * (set to `0` to disable tracing) and falls back to
@@ -59019,12 +59252,12 @@ const resolveCacheFilePath = (projectDirectory) => path.join(resolveReactDoctorC
59019
59252
  const readPersistedCache = (cacheFilePath) => {
59020
59253
  try {
59021
59254
  const parsed = JSON.parse(fs.readFileSync(cacheFilePath, "utf8"));
59022
- if (!isRecord(parsed) || parsed.version !== 4) return {
59023
- version: 4,
59255
+ if (!isRecord(parsed) || parsed.version !== 5) return {
59256
+ version: 5,
59024
59257
  entries: []
59025
59258
  };
59026
59259
  if (!Array.isArray(parsed.entries)) return {
59027
- version: 4,
59260
+ version: 5,
59028
59261
  entries: []
59029
59262
  };
59030
59263
  const entries = [];
@@ -59034,12 +59267,12 @@ const readPersistedCache = (cacheFilePath) => {
59034
59267
  entries.push(entry);
59035
59268
  }
59036
59269
  return {
59037
- version: 4,
59270
+ version: 5,
59038
59271
  entries
59039
59272
  };
59040
59273
  } catch {
59041
59274
  return {
59042
- version: 4,
59275
+ version: 5,
59043
59276
  entries: []
59044
59277
  };
59045
59278
  }
@@ -59101,7 +59334,7 @@ const buildScanResultCacheKey = (input) => {
59101
59334
  if (headSha === null) return null;
59102
59335
  if (stringifyStableJson(input.userConfig) === null) return null;
59103
59336
  const cacheKeyJson = stringifyStableJson({
59104
- schemaVersion: 4,
59337
+ schemaVersion: 5,
59105
59338
  projectIdentity: resolveProjectIdentity(input.projectDirectory),
59106
59339
  headSha,
59107
59340
  worktreeFingerprint,
@@ -59142,7 +59375,7 @@ const createScanResultCache = (projectDirectory) => {
59142
59375
  for (const entry of persistedCache.entries) entries.set(entry.key, entry);
59143
59376
  const persist = () => {
59144
59377
  writePersistedCache(cacheFilePath, {
59145
- version: 4,
59378
+ version: 5,
59146
59379
  entries: [...entries.values()].sort((firstEntry, secondEntry) => secondEntry.createdAtMs - firstEntry.createdAtMs).slice(0, 20)
59147
59380
  });
59148
59381
  };
@@ -59283,6 +59516,7 @@ const buildOutcomeAttributes = (input) => {
59283
59516
  return withNamespace("outcome", {
59284
59517
  status: "error",
59285
59518
  exitCode: 1,
59519
+ complete: false,
59286
59520
  knownError: known,
59287
59521
  errorTag: known ? error.reason._tag : error instanceof Error ? error.name : null
59288
59522
  });
@@ -59292,8 +59526,13 @@ const buildOutcomeAttributes = (input) => {
59292
59526
  const blockingLevel = resolveTelemetryBlocking(input.userConfig);
59293
59527
  const gateDiagnostics = filterDiagnosticsForSurface(result.diagnostics, "ciFailure", input.userConfig);
59294
59528
  const wouldBlock = !input.scoreOnly && !input.gateExempt && shouldBlockCi(gateDiagnostics, blockingLevel);
59295
- const hasSkippedChecks = result.skippedChecks.length > 0;
59296
- const isClean = result.diagnostics.length === 0 && !hasSkippedChecks;
59529
+ const complete = isScanComplete({
59530
+ analyzedFileCount: result.analyzedFiles?.length,
59531
+ scannedFileCount: result.scannedFileCount,
59532
+ skippedCheckCount: result.skippedChecks.length,
59533
+ skippedCheckReasonCount: Object.keys(result.skippedCheckReasons ?? {}).length
59534
+ });
59535
+ const isClean = result.diagnostics.length === 0 && complete;
59297
59536
  const outcome = wouldBlock ? "blocked" : isClean ? "clean" : "ok";
59298
59537
  const firings = summarizeRuleFirings(result.diagnostics);
59299
59538
  const countByRule = /* @__PURE__ */ new Map();
@@ -59343,6 +59582,7 @@ const buildOutcomeAttributes = (input) => {
59343
59582
  wouldBlock,
59344
59583
  blocking: blockingLevel,
59345
59584
  clean: isClean,
59585
+ complete,
59346
59586
  skippedChecks: result.skippedChecks.length
59347
59587
  }),
59348
59588
  ...withNamespace("diag", {
@@ -60993,6 +61233,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
60993
61233
  directory: output.resolvedDirectory,
60994
61234
  scannedFileCount: output.scannedFileCount,
60995
61235
  scannedFilePaths: output.scannedFilePaths,
61236
+ analyzedFiles: output.analyzedFiles,
60996
61237
  scanElapsedMilliseconds: output.scanElapsedMilliseconds,
60997
61238
  scanConcurrency: output.scanConcurrency,
60998
61239
  baselineDelta,
@@ -61047,9 +61288,12 @@ const renderAndRecordScan = async (input) => {
61047
61288
  lintPartialFailures: input.payload.lintPartialFailures,
61048
61289
  didDeadCodeFail: input.payload.didDeadCodeFail,
61049
61290
  deadCodeFailureReason: input.payload.deadCodeFailureReason,
61291
+ supplyChainOverlapTimedOut: input.payload.supplyChainOverlapTimedOut,
61292
+ securityScanFailed: input.payload.securityScanFailed ?? false,
61050
61293
  directory: input.payload.directory,
61051
61294
  scannedFileCount: input.payload.scannedFileCount,
61052
61295
  scannedFilePaths: input.payload.scannedFilePaths,
61296
+ analyzedFiles: input.payload.analyzedFiles ?? [],
61053
61297
  scanElapsedMilliseconds: input.payload.scanElapsedMilliseconds,
61054
61298
  lintCacheHitFileCount: input.lintCacheHitFileCount ?? null,
61055
61299
  lintCacheTotalFileCount: input.lintCacheTotalFileCount ?? null,
@@ -61097,13 +61341,15 @@ const renderAndRecordScan = async (input) => {
61097
61341
  return result;
61098
61342
  };
61099
61343
  const finalizeAndRender = (input) => gen(function* () {
61100
- const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, directory, scannedFileCount, scannedFilePaths, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount, lintSidecarReplayedFileCount, lintSidecarTotalFileCount, deadCodeCacheHit, deadCodeSummaryCacheHits, deadCodeSummaryCacheMisses, baselineDelta } = input;
61344
+ const { options, elapsedMilliseconds, diagnostics, score, project, userConfig, didLintFail, lintFailureReason, lintPartialFailures, didDeadCodeFail, deadCodeFailureReason, supplyChainOverlapTimedOut, securityScanFailed, directory, scannedFileCount, scannedFilePaths, analyzedFiles, scanElapsedMilliseconds, lintCacheHitFileCount, lintCacheTotalFileCount, lintSidecarReplayedFileCount, lintSidecarTotalFileCount, deadCodeCacheHit, deadCodeSummaryCacheHits, deadCodeSummaryCacheMisses, baselineDelta } = input;
61101
61345
  const { skippedChecks, skippedCheckReasons } = buildSkippedChecks({
61102
61346
  didLintFail,
61103
61347
  lintFailureReason,
61104
61348
  lintPartialFailures,
61105
61349
  didDeadCodeFail,
61106
- deadCodeFailureReason
61350
+ deadCodeFailureReason,
61351
+ supplyChainOverlapTimedOut,
61352
+ securityScanFailed
61107
61353
  });
61108
61354
  const hasSkippedChecks = skippedChecks.length > 0;
61109
61355
  const noScoreMessage = buildNoScoreMessage(options.noScore);
@@ -61116,6 +61362,7 @@ const finalizeAndRender = (input) => gen(function* () {
61116
61362
  elapsedMilliseconds,
61117
61363
  scannedFileCount,
61118
61364
  scannedFilePaths,
61365
+ analyzedFiles,
61119
61366
  scanElapsedMilliseconds,
61120
61367
  ...lintCacheTotalFileCount !== null ? {
61121
61368
  lintCacheHitFileCount,
@@ -64984,4 +65231,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
64984
65231
  export {};
64985
65232
 
64986
65233
  //# sourceMappingURL=cli.js.map
64987
- //# debugId=ed5ebcb9-a117-5978-b792-fec60473d806
65234
+ //# debugId=ed6bfef3-a274-5972-833b-77462a1156e2