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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="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]="9a5c1a3c-7706-5a23-bca4-ddddd2f085af")}catch(e){}}();
3
3
  import { createRequire } from "node:module";
4
4
  import * as NodeChildProcess from "node:child_process";
5
5
  import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
@@ -41823,7 +41823,6 @@ const parseGithubViewerPermission = (stdout) => {
41823
41823
  return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
41824
41824
  };
41825
41825
  const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
41826
- const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
41827
41826
  /**
41828
41827
  * `Git` wraps every `git`-via-subprocess call react-doctor makes
41829
41828
  * behind a `Context.Service`. The production layer (`layerNode`)
@@ -41894,14 +41893,6 @@ var Git = class Git extends Service()("react-doctor/Git") {
41894
41893
  args,
41895
41894
  directory
41896
41895
  });
41897
- const listUntrackedFilePaths = (directory, includePaths = []) => runGit(directory, [
41898
- "ls-files",
41899
- "-z",
41900
- "--others",
41901
- "--exclude-standard",
41902
- ...includePaths.length > 0 ? ["--", ...includePaths] : []
41903
- ]).pipe(map$3((result) => result.status === 0 ? splitNullSeparated(result.stdout) : null));
41904
- const mergeUntracked = (directory, trackedFilePaths, includeUntracked) => includeUntracked ? listUntrackedFilePaths(directory).pipe(map$3((untracked) => untracked === null ? trackedFilePaths : [...new Set([...trackedFilePaths, ...untracked])])) : succeed$2(trackedFilePaths);
41905
41896
  const currentBranch = (directory) => runGit(directory, [
41906
41897
  "rev-parse",
41907
41898
  "--abbrev-ref",
@@ -42024,7 +42015,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
42024
42015
  githubViewerPermission,
42025
42016
  branchExists,
42026
42017
  mergeBase,
42027
- diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
42018
+ diffSelection: ({ directory, explicitBaseBranch }) => gen(function* () {
42028
42019
  if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
42029
42020
  if (explicitBaseBranch !== void 0) {
42030
42021
  const range = parseGitDiffRange(explicitBaseBranch);
@@ -42053,7 +42044,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
42053
42044
  "HEAD"
42054
42045
  ]);
42055
42046
  if (uncommitted.status !== 0) return null;
42056
- const files = yield* mergeUntracked(directory, splitNullSeparated(uncommitted.stdout), includeUntracked);
42047
+ const files = splitNullSeparated(uncommitted.stdout);
42057
42048
  if (files.length === 0) return null;
42058
42049
  return {
42059
42050
  currentBranch: resolvedCurrentBranch,
@@ -42083,7 +42074,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
42083
42074
  currentBranch: resolvedCurrentBranch,
42084
42075
  baseBranch,
42085
42076
  diffBaseRef: mergeBaseRef,
42086
- changedFiles: yield* mergeUntracked(directory, splitNullSeparated(diff.stdout), includeUntracked),
42077
+ changedFiles: splitNullSeparated(diff.stdout),
42087
42078
  isCurrentChanges: false
42088
42079
  };
42089
42080
  }).pipe(withSpan("Git.diffSelection")),
@@ -42129,7 +42120,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
42129
42120
  stdout: result.stdout
42130
42121
  };
42131
42122
  }).pipe(withSpan("Git.grep")),
42132
- changedLineRanges: ({ directory, baseRef, cached, files, includeUntracked = false }) => gen(function* () {
42123
+ changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
42133
42124
  if (files.length === 0) return [];
42134
42125
  if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
42135
42126
  const result = yield* runGit(directory, [
@@ -42143,14 +42134,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
42143
42134
  ...files
42144
42135
  ]);
42145
42136
  if (result.status !== 0) return null;
42146
- const changedLineRanges = parseChangedLineRanges(result.stdout);
42147
- if (cached || !includeUntracked) return changedLineRanges;
42148
- const untrackedFilePaths = yield* listUntrackedFilePaths(directory, files);
42149
- if (untrackedFilePaths === null) return changedLineRanges;
42150
- return [...changedLineRanges, ...untrackedFilePaths.map((file) => ({
42151
- file,
42152
- ranges: [[1, UNTRACKED_FILE_LAST_LINE]]
42153
- }))];
42137
+ return parseChangedLineRanges(result.stdout);
42154
42138
  }).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
42155
42139
  });
42156
42140
  })).pipe(provide$2(layer$3.pipe(provide$2(mergeAll$1(layer$2, layer$1)))));
@@ -43659,11 +43643,11 @@ const buildRelatedLocations = (labels, filePath) => {
43659
43643
  }
43660
43644
  return related;
43661
43645
  };
43662
- const isOxlintSpan = (value) => isRecord$1(value) && typeof value.offset === "number" && typeof value.length === "number" && typeof value.line === "number" && typeof value.column === "number";
43663
- const isOxlintLabel = (value) => isRecord$1(value) && isOxlintSpan(value.span);
43664
- const isMappableOxlintDiagnostic = (value) => isRecord$1(value) && typeof value.code === "string" && value.code.length > 0 && typeof value.filename === "string" && value.filename.length > 0 && (value.severity === "warning" || value.severity === "error") && Array.isArray(value.labels) && value.labels.every(isOxlintLabel);
43665
- const isEngineFailureDiagnostic = (value) => !isRecord$1(value) || typeof value.filename !== "string" || value.filename.length === 0;
43666
- const isOxlintOutput = (value) => isRecord$1(value) && Array.isArray(value.diagnostics);
43646
+ const isOxlintOutput = (value) => {
43647
+ if (typeof value !== "object" || value === null) return false;
43648
+ const candidate = value;
43649
+ return Array.isArray(candidate.diagnostics);
43650
+ };
43667
43651
  /**
43668
43652
  * Parses one oxlint subprocess's stdout into a flat `Diagnostic[]`.
43669
43653
  * Tolerates the leading-notice-line shape oxlint sometimes prints
@@ -43682,8 +43666,6 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
43682
43666
  throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
43683
43667
  }
43684
43668
  if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
43685
- const engineFailureDiagnostic = parsed.diagnostics.find(isEngineFailureDiagnostic);
43686
- if (engineFailureDiagnostic !== void 0) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: JSON.stringify(engineFailureDiagnostic).slice(0, 600) }) });
43687
43669
  const minifiedFileCache = /* @__PURE__ */ new Map();
43688
43670
  const sourceBufferCache = /* @__PURE__ */ new Map();
43689
43671
  const sourceLinesCache = /* @__PURE__ */ new Map();
@@ -43718,7 +43700,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
43718
43700
  minifiedFileCache.set(absolutePath, minified);
43719
43701
  return minified;
43720
43702
  };
43721
- return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
43703
+ return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => diagnostic.code && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
43722
43704
  const { plugin, rule } = parseRuleCode(diagnostic.code);
43723
43705
  const primaryLabel = diagnostic.labels[0];
43724
43706
  const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
@@ -46284,11 +46266,10 @@ const computeDiagnosticDelta = (input) => {
46284
46266
  * render diff-base mistakes (`GitBaseBranchInvalid` /
46285
46267
  * `GitBaseBranchMissing`) as clean user errors instead of crashes.
46286
46268
  */
46287
- const getDiffInfo = (directory, explicitBaseBranch, includeUntracked) => runPromise(gen(function* () {
46269
+ const getDiffInfo = (directory, explicitBaseBranch) => runPromise(gen(function* () {
46288
46270
  const selection = yield* (yield* Git).diffSelection({
46289
46271
  directory,
46290
- explicitBaseBranch,
46291
- includeUntracked
46272
+ explicitBaseBranch
46292
46273
  });
46293
46274
  if (selection === null) return null;
46294
46275
  return {
@@ -46624,43 +46605,6 @@ const isNonInteractiveEnvironment = () => NON_INTERACTIVE_ENVIRONMENT_VARIABLES.
46624
46605
  //#region src/cli/utils/constants.ts
46625
46606
  const REACT_DOCTOR_CONFIG_PROJECT_NAME = "react-doctor";
46626
46607
  const STAGED_FILES_TEMP_DIR_PREFIX = "react-doctor-staged-";
46627
- const STAGED_SNAPSHOT_ADDITIONAL_CONFIG_FILENAMES = [
46628
- ".babelrc",
46629
- ".babelrc.json",
46630
- ".eslintignore",
46631
- ".eslintrc.json",
46632
- ".oxlintignore",
46633
- "app.config.cjs",
46634
- "app.config.js",
46635
- "app.config.json",
46636
- "app.config.mjs",
46637
- "app.config.ts",
46638
- "app.json",
46639
- "babel.config.cjs",
46640
- "babel.config.js",
46641
- "babel.config.json",
46642
- "babel.config.mjs",
46643
- "eslint.config.cjs",
46644
- "eslint.config.cts",
46645
- "eslint.config.js",
46646
- "eslint.config.mjs",
46647
- "eslint.config.mts",
46648
- "eslint.config.ts",
46649
- "next.config.cjs",
46650
- "next.config.js",
46651
- "next.config.mjs",
46652
- "next.config.ts",
46653
- "oxlint.config.json",
46654
- "react-doctor.config.json",
46655
- "vite.config.cjs",
46656
- "vite.config.cts",
46657
- "vite.config.js",
46658
- "vite.config.mjs",
46659
- "vite.config.mts",
46660
- "vite.config.ts",
46661
- "vitest.config.js",
46662
- "vitest.config.ts"
46663
- ];
46664
46608
  const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
46665
46609
  const SCAN_RESULT_CACHE_FILENAME = "scan-cache.json";
46666
46610
  const RUN_GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
@@ -46675,7 +46619,6 @@ const METRIC = {
46675
46619
  cliInvoked: "cli.invoked",
46676
46620
  cliError: "cli.error",
46677
46621
  cliEnvironmentError: "cli.env_error",
46678
- stagedSnapshotDivergence: "staged.snapshot_divergence",
46679
46622
  projectDetected: "project.detected",
46680
46623
  projectPathSelected: "project.path_selected",
46681
46624
  projectConfigSelected: "project.config_selected",
@@ -46756,7 +46699,7 @@ const makeNoopConsole = () => ({
46756
46699
  });
46757
46700
  //#endregion
46758
46701
  //#region src/cli/utils/version.ts
46759
- const VERSION = "0.7.6-dev.b36e439";
46702
+ const VERSION = "0.7.6-dev.c5a06bd";
46760
46703
  //#endregion
46761
46704
  //#region src/cli/utils/json-mode.ts
46762
46705
  let context = null;
@@ -47125,13 +47068,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
47125
47068
  * uploads source-map artifacts under, so stack frames symbolicate. Honors the
47126
47069
  * standard `SENTRY_RELEASE` override.
47127
47070
  */
47128
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.6-dev.b36e439`;
47071
+ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.6-dev.c5a06bd`;
47129
47072
  /**
47130
47073
  * Deployment environment shown in Sentry's environment filter. Defaults to
47131
47074
  * `production` for tagged releases and `development` for dev/unbuilt versions,
47132
47075
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
47133
47076
  */
47134
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.6-dev.b36e439") ? "development" : "production");
47077
+ const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.6-dev.c5a06bd") ? "development" : "production");
47135
47078
  /**
47136
47079
  * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
47137
47080
  * (set to `0` to disable tracing) and falls back to
@@ -47323,7 +47266,7 @@ const NON_BLOCKING_REACT_DOCTOR_COMMAND = [
47323
47266
  "fi"
47324
47267
  ].join(" ");
47325
47268
  const PACKAGE_JSON_FILE_NAME = "package.json";
47326
- const runGitRaw = (projectRoot, args) => {
47269
+ const runGit = (projectRoot, args) => {
47327
47270
  try {
47328
47271
  return execFileSync("git", [...args], {
47329
47272
  cwd: projectRoot,
@@ -47334,12 +47277,11 @@ const runGitRaw = (projectRoot, args) => {
47334
47277
  "ignore"
47335
47278
  ],
47336
47279
  maxBuffer: RUN_GIT_MAX_BUFFER_BYTES
47337
- });
47280
+ }).trim();
47338
47281
  } catch {
47339
47282
  return null;
47340
47283
  }
47341
47284
  };
47342
- const runGit = (projectRoot, args) => runGitRaw(projectRoot, args)?.trim() ?? null;
47343
47285
  const resolveGitPath = (baseDirectory, value) => Path.isAbsolute(value) ? value : Path.resolve(baseDirectory, value);
47344
47286
  const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
47345
47287
  const getPackageJsonPath = (projectRoot) => Path.join(projectRoot, PACKAGE_JSON_FILE_NAME);
@@ -59210,17 +59152,6 @@ const buildRuleBlastRadii = (diagnostics) => buildSortedRuleGroups(diagnostics).
59210
59152
  })).toSorted((left, right) => right.fileCount - left.fileCount);
59211
59153
  const findMigrationScaleBuckets = (diagnostics) => buildRuleBlastRadii(diagnostics).filter((bucket) => bucket.fileCount >= 40);
59212
59154
  //#endregion
59213
- //#region src/cli/utils/has-lint-hard-failure.ts
59214
- const hasLintHardFailure = (result) => result.skippedChecks.includes("lint");
59215
- //#endregion
59216
- //#region src/cli/utils/is-inspect-result-complete.ts
59217
- const isInspectResultComplete = (result) => isScanComplete({
59218
- analyzedFileCount: result.analyzedFiles?.length,
59219
- scannedFileCount: result.scannedFileCount,
59220
- skippedCheckCount: result.skippedChecks.length,
59221
- skippedCheckReasonCount: Object.keys(result.skippedCheckReasons ?? {}).length
59222
- });
59223
- //#endregion
59224
59155
  //#region src/cli/utils/scan-result-cache.ts
59225
59156
  const CACHE_DISABLED_VALUES = new Set(["1", "true"]);
59226
59157
  const TOOLCHAIN_PACKAGE_SPECIFIERS = [
@@ -59626,12 +59557,15 @@ const buildOutcomeAttributes = (input) => {
59626
59557
  const summary = summarizeDiagnostics(result.diagnostics);
59627
59558
  const blockingLevel = resolveTelemetryBlocking(input.userConfig);
59628
59559
  const gateDiagnostics = filterDiagnosticsForSurface(result.diagnostics, "ciFailure", input.userConfig);
59629
- const complete = isInspectResultComplete(result);
59630
- const didLintHardFail = hasLintHardFailure(result);
59631
- const failsOnHardFailure = didLintHardFail && blockingLevel !== "none";
59632
59560
  const wouldBlock = !input.scoreOnly && !input.gateExempt && shouldBlockCi(gateDiagnostics, blockingLevel);
59561
+ const complete = isScanComplete({
59562
+ analyzedFileCount: result.analyzedFiles?.length,
59563
+ scannedFileCount: result.scannedFileCount,
59564
+ skippedCheckCount: result.skippedChecks.length,
59565
+ skippedCheckReasonCount: Object.keys(result.skippedCheckReasons ?? {}).length
59566
+ });
59633
59567
  const isClean = result.diagnostics.length === 0 && complete;
59634
- const outcome = didLintHardFail ? "error" : wouldBlock ? "blocked" : isClean ? "clean" : "ok";
59568
+ const outcome = wouldBlock ? "blocked" : isClean ? "clean" : "ok";
59635
59569
  const firings = summarizeRuleFirings(result.diagnostics);
59636
59570
  const countByRule = /* @__PURE__ */ new Map();
59637
59571
  const countByCategory = /* @__PURE__ */ new Map();
@@ -59676,7 +59610,7 @@ const buildOutcomeAttributes = (input) => {
59676
59610
  const attributes = {
59677
59611
  ...withNamespace("outcome", {
59678
59612
  status: outcome,
59679
- exitCode: failsOnHardFailure || wouldBlock ? 1 : 0,
59613
+ exitCode: wouldBlock ? 1 : 0,
59680
59614
  wouldBlock,
59681
59615
  blocking: blockingLevel,
59682
59616
  clean: isClean,
@@ -63858,15 +63792,10 @@ const warnIfAiTrainingEnvironment = () => {
63858
63792
  //#region src/cli/utils/validate-mode-flags.ts
63859
63793
  const usedDiffAlias = (flags) => flags.diff !== void 0 && flags.diff !== false && flags.diff !== "false" && flags.diff !== "";
63860
63794
  const usedScope = (flags) => typeof flags.scope === "string" && flags.scope.length > 0;
63861
- const validateIncludeUntrackedScope = (includeUntracked, scope) => {
63862
- if (!includeUntracked || scope !== void 0 && scope !== "full") return;
63863
- throw new CliInputError("--include-untracked requires the files, changed, or lines scope (via --scope, --diff, or config).");
63864
- };
63865
63795
  const validateModeFlags = (flags) => {
63866
63796
  if (usedScope(flags) && usedDiffAlias(flags)) throw new CliInputError("Cannot combine --scope and --diff; --diff is the deprecated alias.");
63867
63797
  if (flags.staged && usedDiffAlias(flags)) throw new CliInputError("Cannot combine --staged and --diff; pick one mode.");
63868
63798
  if (flags.staged && (flags.scope === "full" || flags.scope === "changed")) throw new CliInputError(`Cannot combine --staged with --scope ${flags.scope}; use --scope files or --scope lines, or drop --scope.`);
63869
- if (flags.includeUntracked && flags.staged) throw new CliInputError("Cannot combine --include-untracked with --staged; the git index never holds untracked files.");
63870
63799
  if (flags.score && flags.json) throw new CliInputError("Cannot combine --score and --json; pick one output mode.");
63871
63800
  if (flags.score && flags.telemetry === false) throw new CliInputError("Cannot combine --score with --no-telemetry; --score prints the score that --no-telemetry disables.");
63872
63801
  if (flags.debug && (flags.score === false || flags.telemetry === false)) {
@@ -63875,39 +63804,6 @@ const validateModeFlags = (flags) => {
63875
63804
  }
63876
63805
  };
63877
63806
  //#endregion
63878
- //#region src/cli/utils/parse-staged-snapshot-divergences.ts
63879
- const SNAPSHOT_CONFIG_FILENAMES = new Set([...STAGED_FILES_PROJECT_CONFIG_FILENAMES, ...STAGED_SNAPSHOT_ADDITIONAL_CONFIG_FILENAMES]);
63880
- const parseStagedSnapshotDivergences = (statusOutput) => {
63881
- const divergentConfigFiles = /* @__PURE__ */ new Set();
63882
- const statusEntries = statusOutput.split("\0").filter((entry) => entry.length > 0);
63883
- for (let entryIndex = 0; entryIndex < statusEntries.length; entryIndex += 1) {
63884
- const entry = statusEntries[entryIndex];
63885
- const [indexStatus, worktreeStatus] = entry;
63886
- const filePath = entry.slice(3);
63887
- const hasRenameOrCopySource = indexStatus === "R" || indexStatus === "C" || worktreeStatus === "R" || worktreeStatus === "C";
63888
- const sourcePath = hasRenameOrCopySource ? statusEntries[entryIndex + 1] : void 0;
63889
- const worktreePaths = worktreeStatus === "R" || worktreeStatus === "C" ? [filePath, sourcePath] : [filePath];
63890
- if (worktreeStatus !== " ") {
63891
- for (const worktreePath of worktreePaths) if (worktreePath && SNAPSHOT_CONFIG_FILENAMES.has(Path.basename(worktreePath))) divergentConfigFiles.add(worktreePath);
63892
- }
63893
- if (hasRenameOrCopySource) entryIndex += 1;
63894
- }
63895
- return [...divergentConfigFiles].sort();
63896
- };
63897
- //#endregion
63898
- //#region src/cli/utils/find-staged-snapshot-divergences.ts
63899
- const findStagedSnapshotDivergences = (directory) => {
63900
- const statusOutput = runGitRaw(directory, [
63901
- "status",
63902
- "--porcelain=v1",
63903
- "-z",
63904
- "--untracked-files=all",
63905
- "--ignored=matching"
63906
- ]);
63907
- if (statusOutput === null) return null;
63908
- return parseStagedSnapshotDivergences(statusOutput);
63909
- };
63910
- //#endregion
63911
63807
  //#region src/cli/commands/inspect.ts
63912
63808
  const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
63913
63809
  if (categoryFilters.size === 0) return [...completedScans];
@@ -63922,14 +63818,9 @@ const filterCompletedScansByCategories = (completedScans, categoryFilters) => {
63922
63818
  /**
63923
63819
  * Post-scan finalization shared by the staged-arm and project-loop
63924
63820
  * paths of `inspectAction`: emit the JSON report (when in JSON mode)
63925
- * and set `process.exitCode = 1` when any scan's lint pass hard-failed
63926
- * (an engine/plugin/binding failure destroys the findings, so success
63927
- * would be a false clean) or a diagnostic at or above the `--blocking`
63928
- * threshold (default `"error"`) reaches the `ciFailure` surface.
63929
- * `--blocking none` keeps the scan advisory (always exits 0), and
63930
- * fail-open degradations — `--no-lint`, `--max-duration` truncation,
63931
- * supply-chain/security skips — stay advisory too, surfaced through
63932
- * `complete: false` in the JSON report.
63821
+ * and set `process.exitCode = 1` when a diagnostic at or above the
63822
+ * `--blocking` threshold (default `"error"`) reaches the `ciFailure`
63823
+ * surface. `--blocking none` keeps the scan advisory (always exits 0).
63933
63824
  */
63934
63825
  const finalizeScans = (input) => {
63935
63826
  const baselineDeltas = input.completedScans.flatMap((scan) => scan.result.baselineDelta ? [scan.result.baselineDelta] : []);
@@ -63959,13 +63850,8 @@ const finalizeScans = (input) => {
63959
63850
  baselineDegraded
63960
63851
  }));
63961
63852
  }
63962
- const blockingLevel = resolveBlockingLevel(input.flags, input.userConfig);
63963
- if (input.completedScans.some(({ result }) => hasLintHardFailure(result)) && blockingLevel !== "none") {
63964
- process.exitCode = 1;
63965
- return;
63966
- }
63967
63853
  if (input.isScoreOnly || baselineDegraded) return;
63968
- if (shouldBlockCi(filterScansForSurface(input.completedScans, "ciFailure"), blockingLevel)) process.exitCode = 1;
63854
+ if (shouldBlockCi(filterScansForSurface(input.completedScans, "ciFailure"), resolveBlockingLevel(input.flags, input.userConfig))) process.exitCode = 1;
63969
63855
  };
63970
63856
  const buildChangedFilesDiffInfo = (changedFiles) => ({
63971
63857
  currentBranch: process.env.GITHUB_HEAD_REF?.trim() || null,
@@ -64003,7 +63889,6 @@ const inspectAction = async (directory, flags) => {
64003
63889
  recordCount(METRIC.cliInvoked, 1, { command: "inspect" });
64004
63890
  try {
64005
63891
  validateModeFlags(flags);
64006
- if (flags.staged) setJsonReportMode("staged");
64007
63892
  await maybeMigrateLegacyConfig(requestedDirectory, {
64008
63893
  isQuiet,
64009
63894
  isStaged: Boolean(flags.staged)
@@ -64019,14 +63904,6 @@ const inspectAction = async (directory, flags) => {
64019
63904
  cliLogger.dim(`Redirected to ${highlighter.info(toRelativePath(resolvedDirectory, requestedDirectory))} via react-doctor config "rootDir".`);
64020
63905
  cliLogger.break();
64021
63906
  }
64022
- if (flags.staged) {
64023
- const divergentConfigFiles = findStagedSnapshotDivergences(resolvedDirectory);
64024
- if (divergentConfigFiles === null) throw new CliInputError("Could not verify that staged configuration matches the worktree. Run the command from a Git worktree with Git available.");
64025
- if (divergentConfigFiles.length > 0) {
64026
- recordCount(METRIC.stagedSnapshotDivergence, 1, { divergentInputCount: divergentConfigFiles.length });
64027
- throw new CliInputError(`Cannot scan staged files while configuration differs between the index and worktree: ${divergentConfigFiles.join(", ")}. Stage or restore those files, then rerun react-doctor --staged.`);
64028
- }
64029
- }
64030
63907
  const explainArgument = flags.explain;
64031
63908
  if (explainArgument !== void 0) {
64032
63909
  await runExplain(explainArgument, {
@@ -64047,6 +63924,7 @@ const inspectAction = async (directory, flags) => {
64047
63924
  json: flags.json
64048
63925
  });
64049
63926
  if (flags.staged) {
63927
+ setJsonReportMode("staged");
64050
63928
  const stagedFiles = await getStagedSourceFiles(resolvedDirectory);
64051
63929
  if (stagedFiles.length === 0) {
64052
63930
  if (isJsonMode) writeJsonReport(buildJsonReport({
@@ -64127,15 +64005,13 @@ const inspectAction = async (directory, flags) => {
64127
64005
  const projectDirectories = await selectProjects(resolvedDirectory, flags.project, skipPrompts, userConfig?.projects);
64128
64006
  const changedFilesDiffInfo = flags.changedFilesFrom ? buildChangedFilesDiffInfo(readChangedFilesFrom(Path.resolve(flags.changedFilesFrom))) : null;
64129
64007
  const requestedScope = resolveScope(flags, userConfig);
64130
- const includeUntracked = flags.includeUntracked ?? false;
64131
64008
  const scopeRequest = requestedScope.scope === void 0 && changedFilesDiffInfo !== null ? {
64132
64009
  ...requestedScope,
64133
64010
  scope: "changed"
64134
64011
  } : requestedScope;
64135
- validateIncludeUntrackedScope(includeUntracked, scopeRequest.scope);
64136
64012
  const wantsDiffMode = scopeRequest.scope !== void 0 && scopeRequest.scope !== "full";
64137
64013
  const shouldDetectDiff = changedFilesDiffInfo === null && (wantsDiffMode || scopeRequest.scope === void 0 && !skipPrompts && !isQuiet);
64138
- const diffInfo = changedFilesDiffInfo ?? (shouldDetectDiff ? await getDiffInfo(resolvedDirectory, scopeRequest.base, includeUntracked) : null);
64014
+ const diffInfo = changedFilesDiffInfo ?? (shouldDetectDiff ? await getDiffInfo(resolvedDirectory, scopeRequest.base) : null);
64139
64015
  const scope = await finalizeScope({
64140
64016
  requested: scopeRequest,
64141
64017
  diffInfo,
@@ -64149,8 +64025,7 @@ const inspectAction = async (directory, flags) => {
64149
64025
  const changedLineRanges = scope === "lines" && diffInfo !== null && (diffInfo.isCurrentChanges || linesBaseRef !== null) && diffInfo !== null ? await getChangedLineRanges({
64150
64026
  directory: resolvedDirectory,
64151
64027
  baseRef: linesBaseRef ?? void 0,
64152
- files: [...diffInfo.changedFiles],
64153
- includeUntracked
64028
+ files: [...diffInfo.changedFiles]
64154
64029
  }) : null;
64155
64030
  if (scope === "lines" && changedLineRanges === null && !isQuiet) {
64156
64031
  cliLogger.warn("Could not determine changed lines (no base ref or git diff failed); reporting all issues in changed files.");
@@ -65083,7 +64958,6 @@ const ROOT_FLAG_SPEC = {
65083
64958
  "--dead-code",
65084
64959
  "--debug",
65085
64960
  "--help",
65086
- "--include-untracked",
65087
64961
  "--json",
65088
64962
  "--json-compact",
65089
64963
  "--lint",
@@ -65347,7 +65221,7 @@ ${highlighter.dim("Learn more:")}
65347
65221
  ${highlighter.info(CI_URL)}
65348
65222
  `;
65349
65223
  const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
65350
- const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much supported JS/TS source to scan/report: full (default), files, changed (only new issues vs base), or lines (issues whose source spans touch changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").option("--include-untracked", "with --scope files/changed/lines, also scan ordinary untracked files (respects .gitignore)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks").option("--max-duration <seconds>", "scan time budget for the whole run, shared across workspace projects: past it, remaining lint batches and dead-code are skipped and partial results are reported (skipped files are listed in the JSON report)").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
65224
+ const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much supported JS/TS source to scan/report: full (default), files, changed (only new issues vs base), or lines (issues whose source spans touch changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks").option("--max-duration <seconds>", "scan time budget for the whole run, shared across workspace projects: past it, remaining lint batches and dead-code are skipped and partial results are reported (skipped files are listed in the JSON report)").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
65351
65225
  program.action(inspectAction);
65352
65226
  program.command("why <location>").description("Explain why a rule fired (or why a suppression didn't apply) at a file:line").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple)").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").action((location, options) => whyAction(location, options));
65353
65227
  program.command("install").alias("setup").description("Install the react-doctor skill into your coding agents and optional git hook").option("-y, --yes", "skip prompts, install for all detected agents").option("--dry-run", "show what would be installed without writing files").option("--agent-hooks", "install native non-blocking agent hooks for Claude Code and Cursor").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderInstallHelpEpilog).action(installAction);
@@ -65397,4 +65271,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
65397
65271
  export {};
65398
65272
 
65399
65273
  //# sourceMappingURL=cli.js.map
65400
- //# debugId=58e54b02-7ba2-5f13-a535-98c236467a86
65274
+ //# debugId=9a5c1a3c-7706-5a23-bca4-ddddd2f085af
package/dist/index.d.ts CHANGED
@@ -10256,7 +10256,7 @@ declare const buildJsonReport: (input: BuildJsonReportInput) => JsonReportV3$1;
10256
10256
  * render diff-base mistakes (`GitBaseBranchInvalid` /
10257
10257
  * `GitBaseBranchMissing`) as clean user errors instead of crashes.
10258
10258
  */
10259
- declare const getDiffInfo: (directory: string, explicitBaseBranch?: string, includeUntracked?: boolean) => Promise<DiffInfo | null>;
10259
+ declare const getDiffInfo: (directory: string, explicitBaseBranch?: string) => Promise<DiffInfo | null>;
10260
10260
  declare const filterSourceFiles: (filePaths: string[]) => string[];
10261
10261
  /**
10262
10262
  * Programmatic façade over `Git.changedLineRanges` (the `lines` scope). Diffs
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="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]="11364a34-6c8b-5bd3-b9e8-aaa7c0ea264c")}catch(e){}}();
3
3
  import { r as __toESM$1, t as __commonJSMin$1 } from "./chunk-N93fKeF6.js";
4
4
  import { createRequire } from "node:module";
5
5
  import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, FRAMEWORK_TOKENS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
@@ -38579,7 +38579,6 @@ const parseGithubViewerPermission = (stdout) => {
38579
38579
  return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
38580
38580
  };
38581
38581
  const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
38582
- const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
38583
38582
  /**
38584
38583
  * `Git` wraps every `git`-via-subprocess call react-doctor makes
38585
38584
  * behind a `Context.Service`. The production layer (`layerNode`)
@@ -38650,14 +38649,6 @@ var Git = class Git extends Service()("react-doctor/Git") {
38650
38649
  args,
38651
38650
  directory
38652
38651
  });
38653
- const listUntrackedFilePaths = (directory, includePaths = []) => runGit(directory, [
38654
- "ls-files",
38655
- "-z",
38656
- "--others",
38657
- "--exclude-standard",
38658
- ...includePaths.length > 0 ? ["--", ...includePaths] : []
38659
- ]).pipe(map$3((result) => result.status === 0 ? splitNullSeparated(result.stdout) : null));
38660
- const mergeUntracked = (directory, trackedFilePaths, includeUntracked) => includeUntracked ? listUntrackedFilePaths(directory).pipe(map$3((untracked) => untracked === null ? trackedFilePaths : [...new Set([...trackedFilePaths, ...untracked])])) : succeed$2(trackedFilePaths);
38661
38652
  const currentBranch = (directory) => runGit(directory, [
38662
38653
  "rev-parse",
38663
38654
  "--abbrev-ref",
@@ -38780,7 +38771,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38780
38771
  githubViewerPermission,
38781
38772
  branchExists,
38782
38773
  mergeBase,
38783
- diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
38774
+ diffSelection: ({ directory, explicitBaseBranch }) => gen(function* () {
38784
38775
  if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
38785
38776
  if (explicitBaseBranch !== void 0) {
38786
38777
  const range = parseGitDiffRange(explicitBaseBranch);
@@ -38809,7 +38800,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38809
38800
  "HEAD"
38810
38801
  ]);
38811
38802
  if (uncommitted.status !== 0) return null;
38812
- const files = yield* mergeUntracked(directory, splitNullSeparated(uncommitted.stdout), includeUntracked);
38803
+ const files = splitNullSeparated(uncommitted.stdout);
38813
38804
  if (files.length === 0) return null;
38814
38805
  return {
38815
38806
  currentBranch: resolvedCurrentBranch,
@@ -38839,7 +38830,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38839
38830
  currentBranch: resolvedCurrentBranch,
38840
38831
  baseBranch,
38841
38832
  diffBaseRef: mergeBaseRef,
38842
- changedFiles: yield* mergeUntracked(directory, splitNullSeparated(diff.stdout), includeUntracked),
38833
+ changedFiles: splitNullSeparated(diff.stdout),
38843
38834
  isCurrentChanges: false
38844
38835
  };
38845
38836
  }).pipe(withSpan("Git.diffSelection")),
@@ -38885,7 +38876,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38885
38876
  stdout: result.stdout
38886
38877
  };
38887
38878
  }).pipe(withSpan("Git.grep")),
38888
- changedLineRanges: ({ directory, baseRef, cached, files, includeUntracked = false }) => gen(function* () {
38879
+ changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
38889
38880
  if (files.length === 0) return [];
38890
38881
  if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
38891
38882
  const result = yield* runGit(directory, [
@@ -38899,14 +38890,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38899
38890
  ...files
38900
38891
  ]);
38901
38892
  if (result.status !== 0) return null;
38902
- const changedLineRanges = parseChangedLineRanges(result.stdout);
38903
- if (cached || !includeUntracked) return changedLineRanges;
38904
- const untrackedFilePaths = yield* listUntrackedFilePaths(directory, files);
38905
- if (untrackedFilePaths === null) return changedLineRanges;
38906
- return [...changedLineRanges, ...untrackedFilePaths.map((file) => ({
38907
- file,
38908
- ranges: [[1, UNTRACKED_FILE_LAST_LINE]]
38909
- }))];
38893
+ return parseChangedLineRanges(result.stdout);
38910
38894
  }).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
38911
38895
  });
38912
38896
  })).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
@@ -40415,11 +40399,11 @@ const buildRelatedLocations = (labels, filePath) => {
40415
40399
  }
40416
40400
  return related;
40417
40401
  };
40418
- const isOxlintSpan = (value) => isRecord(value) && typeof value.offset === "number" && typeof value.length === "number" && typeof value.line === "number" && typeof value.column === "number";
40419
- const isOxlintLabel = (value) => isRecord(value) && isOxlintSpan(value.span);
40420
- const isMappableOxlintDiagnostic = (value) => isRecord(value) && typeof value.code === "string" && value.code.length > 0 && typeof value.filename === "string" && value.filename.length > 0 && (value.severity === "warning" || value.severity === "error") && Array.isArray(value.labels) && value.labels.every(isOxlintLabel);
40421
- const isEngineFailureDiagnostic = (value) => !isRecord(value) || typeof value.filename !== "string" || value.filename.length === 0;
40422
- const isOxlintOutput = (value) => isRecord(value) && Array.isArray(value.diagnostics);
40402
+ const isOxlintOutput = (value) => {
40403
+ if (typeof value !== "object" || value === null) return false;
40404
+ const candidate = value;
40405
+ return Array.isArray(candidate.diagnostics);
40406
+ };
40423
40407
  /**
40424
40408
  * Parses one oxlint subprocess's stdout into a flat `Diagnostic[]`.
40425
40409
  * Tolerates the leading-notice-line shape oxlint sometimes prints
@@ -40438,8 +40422,6 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
40438
40422
  throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
40439
40423
  }
40440
40424
  if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
40441
- const engineFailureDiagnostic = parsed.diagnostics.find(isEngineFailureDiagnostic);
40442
- if (engineFailureDiagnostic !== void 0) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: JSON.stringify(engineFailureDiagnostic).slice(0, 600) }) });
40443
40425
  const minifiedFileCache = /* @__PURE__ */ new Map();
40444
40426
  const sourceBufferCache = /* @__PURE__ */ new Map();
40445
40427
  const sourceLinesCache = /* @__PURE__ */ new Map();
@@ -40474,7 +40456,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
40474
40456
  minifiedFileCache.set(absolutePath, minified);
40475
40457
  return minified;
40476
40458
  };
40477
- return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
40459
+ return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => diagnostic.code && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
40478
40460
  const { plugin, rule } = parseRuleCode(diagnostic.code);
40479
40461
  const primaryLabel = diagnostic.labels[0];
40480
40462
  const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
@@ -42966,11 +42948,10 @@ const buildSkippedChecks = (input) => {
42966
42948
  * render diff-base mistakes (`GitBaseBranchInvalid` /
42967
42949
  * `GitBaseBranchMissing`) as clean user errors instead of crashes.
42968
42950
  */
42969
- const getDiffInfo = (directory, explicitBaseBranch, includeUntracked) => runPromise(gen(function* () {
42951
+ const getDiffInfo = (directory, explicitBaseBranch) => runPromise(gen(function* () {
42970
42952
  const selection = yield* (yield* Git).diffSelection({
42971
42953
  directory,
42972
- explicitBaseBranch,
42973
- includeUntracked
42954
+ explicitBaseBranch
42974
42955
  });
42975
42956
  if (selection === null) return null;
42976
42957
  return {
@@ -43260,4 +43241,4 @@ const toJsonReport = (result, options) => buildJsonReport({
43260
43241
  export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
43261
43242
 
43262
43243
  //# sourceMappingURL=index.js.map
43263
- //# debugId=2aa6fc3c-96ab-5a4c-8a52-e74d27fb4b5a
43244
+ //# debugId=11364a34-6c8b-5bd3-b9e8-aaa7c0ea264c
package/dist/lsp.js CHANGED
@@ -38557,7 +38557,6 @@ const parseGithubViewerPermission = (stdout) => {
38557
38557
  return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
38558
38558
  };
38559
38559
  const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
38560
- const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
38561
38560
  /**
38562
38561
  * `Git` wraps every `git`-via-subprocess call react-doctor makes
38563
38562
  * behind a `Context.Service`. The production layer (`layerNode`)
@@ -38628,14 +38627,6 @@ var Git = class Git extends Service()("react-doctor/Git") {
38628
38627
  args,
38629
38628
  directory
38630
38629
  });
38631
- const listUntrackedFilePaths = (directory, includePaths = []) => runGit(directory, [
38632
- "ls-files",
38633
- "-z",
38634
- "--others",
38635
- "--exclude-standard",
38636
- ...includePaths.length > 0 ? ["--", ...includePaths] : []
38637
- ]).pipe(map$3((result) => result.status === 0 ? splitNullSeparated(result.stdout) : null));
38638
- const mergeUntracked = (directory, trackedFilePaths, includeUntracked) => includeUntracked ? listUntrackedFilePaths(directory).pipe(map$3((untracked) => untracked === null ? trackedFilePaths : [...new Set([...trackedFilePaths, ...untracked])])) : succeed$2(trackedFilePaths);
38639
38630
  const currentBranch = (directory) => runGit(directory, [
38640
38631
  "rev-parse",
38641
38632
  "--abbrev-ref",
@@ -38758,7 +38749,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38758
38749
  githubViewerPermission,
38759
38750
  branchExists,
38760
38751
  mergeBase,
38761
- diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
38752
+ diffSelection: ({ directory, explicitBaseBranch }) => gen(function* () {
38762
38753
  if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
38763
38754
  if (explicitBaseBranch !== void 0) {
38764
38755
  const range = parseGitDiffRange(explicitBaseBranch);
@@ -38787,7 +38778,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38787
38778
  "HEAD"
38788
38779
  ]);
38789
38780
  if (uncommitted.status !== 0) return null;
38790
- const files = yield* mergeUntracked(directory, splitNullSeparated(uncommitted.stdout), includeUntracked);
38781
+ const files = splitNullSeparated(uncommitted.stdout);
38791
38782
  if (files.length === 0) return null;
38792
38783
  return {
38793
38784
  currentBranch: resolvedCurrentBranch,
@@ -38817,7 +38808,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38817
38808
  currentBranch: resolvedCurrentBranch,
38818
38809
  baseBranch,
38819
38810
  diffBaseRef: mergeBaseRef,
38820
- changedFiles: yield* mergeUntracked(directory, splitNullSeparated(diff.stdout), includeUntracked),
38811
+ changedFiles: splitNullSeparated(diff.stdout),
38821
38812
  isCurrentChanges: false
38822
38813
  };
38823
38814
  }).pipe(withSpan("Git.diffSelection")),
@@ -38863,7 +38854,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38863
38854
  stdout: result.stdout
38864
38855
  };
38865
38856
  }).pipe(withSpan("Git.grep")),
38866
- changedLineRanges: ({ directory, baseRef, cached, files, includeUntracked = false }) => gen(function* () {
38857
+ changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
38867
38858
  if (files.length === 0) return [];
38868
38859
  if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
38869
38860
  const result = yield* runGit(directory, [
@@ -38877,14 +38868,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
38877
38868
  ...files
38878
38869
  ]);
38879
38870
  if (result.status !== 0) return null;
38880
- const changedLineRanges = parseChangedLineRanges(result.stdout);
38881
- if (cached || !includeUntracked) return changedLineRanges;
38882
- const untrackedFilePaths = yield* listUntrackedFilePaths(directory, files);
38883
- if (untrackedFilePaths === null) return changedLineRanges;
38884
- return [...changedLineRanges, ...untrackedFilePaths.map((file) => ({
38885
- file,
38886
- ranges: [[1, UNTRACKED_FILE_LAST_LINE]]
38887
- }))];
38871
+ return parseChangedLineRanges(result.stdout);
38888
38872
  }).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
38889
38873
  });
38890
38874
  })).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
@@ -40393,11 +40377,11 @@ const buildRelatedLocations = (labels, filePath) => {
40393
40377
  }
40394
40378
  return related;
40395
40379
  };
40396
- const isOxlintSpan = (value) => isRecord(value) && typeof value.offset === "number" && typeof value.length === "number" && typeof value.line === "number" && typeof value.column === "number";
40397
- const isOxlintLabel = (value) => isRecord(value) && isOxlintSpan(value.span);
40398
- const isMappableOxlintDiagnostic = (value) => isRecord(value) && typeof value.code === "string" && value.code.length > 0 && typeof value.filename === "string" && value.filename.length > 0 && (value.severity === "warning" || value.severity === "error") && Array.isArray(value.labels) && value.labels.every(isOxlintLabel);
40399
- const isEngineFailureDiagnostic = (value) => !isRecord(value) || typeof value.filename !== "string" || value.filename.length === 0;
40400
- const isOxlintOutput = (value) => isRecord(value) && Array.isArray(value.diagnostics);
40380
+ const isOxlintOutput = (value) => {
40381
+ if (typeof value !== "object" || value === null) return false;
40382
+ const candidate = value;
40383
+ return Array.isArray(candidate.diagnostics);
40384
+ };
40401
40385
  /**
40402
40386
  * Parses one oxlint subprocess's stdout into a flat `Diagnostic[]`.
40403
40387
  * Tolerates the leading-notice-line shape oxlint sometimes prints
@@ -40416,8 +40400,6 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
40416
40400
  throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
40417
40401
  }
40418
40402
  if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
40419
- const engineFailureDiagnostic = parsed.diagnostics.find(isEngineFailureDiagnostic);
40420
- if (engineFailureDiagnostic !== void 0) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: JSON.stringify(engineFailureDiagnostic).slice(0, 600) }) });
40421
40403
  const minifiedFileCache = /* @__PURE__ */ new Map();
40422
40404
  const sourceBufferCache = /* @__PURE__ */ new Map();
40423
40405
  const sourceLinesCache = /* @__PURE__ */ new Map();
@@ -40452,7 +40434,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
40452
40434
  minifiedFileCache.set(absolutePath, minified);
40453
40435
  return minified;
40454
40436
  };
40455
- return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
40437
+ return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => diagnostic.code && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
40456
40438
  const { plugin, rule } = parseRuleCode(diagnostic.code);
40457
40439
  const primaryLabel = diagnostic.labels[0];
40458
40440
  const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
@@ -44542,7 +44524,6 @@ const METRIC = {
44542
44524
  cliInvoked: "cli.invoked",
44543
44525
  cliError: "cli.error",
44544
44526
  cliEnvironmentError: "cli.env_error",
44545
- stagedSnapshotDivergence: "staged.snapshot_divergence",
44546
44527
  projectDetected: "project.detected",
44547
44528
  projectPathSelected: "project.path_selected",
44548
44529
  projectConfigSelected: "project.config_selected",
@@ -44897,5 +44878,5 @@ const startLanguageServer = () => {
44897
44878
  };
44898
44879
  //#endregion
44899
44880
  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
44881
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f9337714-b38f-5f28-ac00-c9d7d79ebaea")}catch(e){}}();
44882
+ //# debugId=f9337714-b38f-5f28-ac00-c9d7d79ebaea
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor",
3
- "version": "0.7.6-dev.b36e439",
3
+ "version": "0.7.6-dev.c5a06bd",
4
4
  "description": "Your agent writes bad React. This catches it",
5
5
  "keywords": [
6
6
  "accessibility",
@@ -66,7 +66,7 @@
66
66
  "vscode-uri": "^3.1.0",
67
67
  "yaml": "^2.9.0",
68
68
  "deslop-js": "0.7.6",
69
- "oxlint-plugin-react-doctor": "0.7.6-dev.b36e439"
69
+ "oxlint-plugin-react-doctor": "0.7.6-dev.c5a06bd"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/babel__code-frame": "^7.27.0",