react-doctor 0.7.6-dev.47beb25 → 0.7.6-dev.4fb3694

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]="5dcb5b0b-ca1e-5655-8f52-3c22b421256d")}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]="bc56f5c4-5b95-5727-8e86-d43a534abbbc")}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";
@@ -36341,12 +36341,10 @@ const listSourceFilesViaFilesystem = (rootDirectory) => {
36341
36341
  const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort());
36342
36342
  const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
36343
36343
  const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
36344
- const UTF8_BOM_CHARACTER = "";
36345
- const stripUtf8Bom = (content) => content.startsWith(UTF8_BOM_CHARACTER) ? content.slice(1) : content;
36346
36344
  const cachedPackageJsons = /* @__PURE__ */ new Map();
36347
36345
  const readPackageJsonUncached = (packageJsonPath) => {
36348
36346
  try {
36349
- return JSON.parse(stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8")));
36347
+ return JSON.parse(NFS.readFileSync(packageJsonPath, "utf-8"));
36350
36348
  } catch (error) {
36351
36349
  if (error instanceof SyntaxError) return {};
36352
36350
  if (isErrnoException(error)) {
@@ -40019,6 +40017,7 @@ const PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
40019
40017
  const PNPM_LOCKFILE = "pnpm-lock.yaml";
40020
40018
  const PACKAGE_JSON_FILE = "package.json";
40021
40019
  const PNPM_HARDENING_RULE_KEY = "require-pnpm-hardening";
40020
+ const UTF8_BOM_CHAR = "";
40022
40021
  const HARDENING_SETTING_KEYS = new Set([
40023
40022
  "minimumReleaseAge",
40024
40023
  "blockExoticSubdeps",
@@ -40043,11 +40042,12 @@ const stripInlineComment = (rawValue) => {
40043
40042
  return rawValue;
40044
40043
  };
40045
40044
  const unquote = (rawValue) => rawValue.replace(/^["']|["']$/g, "");
40045
+ const stripBom = (rawContent) => rawContent.startsWith(UTF8_BOM_CHAR) ? rawContent.slice(1) : rawContent;
40046
40046
  const parseHardeningSettings = (content) => {
40047
40047
  let minimumReleaseAge = null;
40048
40048
  let blockExoticSubdeps = null;
40049
40049
  let trustPolicy = null;
40050
- const lines = stripUtf8Bom(content).split(/\r?\n/);
40050
+ const lines = stripBom(content).split(/\r?\n/);
40051
40051
  for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
40052
40052
  const lineText = lines[lineIndex];
40053
40053
  if (lineText === void 0) continue;
@@ -40081,7 +40081,7 @@ const isPnpmManagedProject = (rootDirectory) => {
40081
40081
  const packageJsonPath = Path.join(rootDirectory, PACKAGE_JSON_FILE);
40082
40082
  if (!isFile(packageJsonPath)) return false;
40083
40083
  try {
40084
- const packageJsonRaw = stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8"));
40084
+ const packageJsonRaw = NFS.readFileSync(packageJsonPath, "utf-8");
40085
40085
  const packageJson = JSON.parse(packageJsonRaw);
40086
40086
  if (packageJson !== null && typeof packageJson === "object" && "packageManager" in packageJson && typeof packageJson.packageManager === "string" && packageJson.packageManager.startsWith("pnpm@")) return true;
40087
40087
  } catch {
@@ -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)))));
@@ -42902,12 +42886,6 @@ const mapWithConcurrency = async (items, concurrency, task) => {
42902
42886
  if (errors.length > 0) throw errors[0];
42903
42887
  return results;
42904
42888
  };
42905
- const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
42906
- let lineNumber = 1;
42907
- const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
42908
- for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === 10) lineNumber++;
42909
- return lineNumber;
42910
- };
42911
42889
  const REANIMATED_SHARED_VALUE_HINT = "If this is a Reanimated shared value, prefer its React Compiler-compatible `.get()` / `.set()` accessors over `.value` — https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue/#react-compiler-support";
42912
42890
  const ACCESSORS_MIN_VERSION = "3.15.0";
42913
42891
  const supportsAccessors = (reanimatedVersion) => {
@@ -43479,6 +43457,13 @@ const findOutermostEnclosingFunctionRange = (sourceFile, targetLine) => {
43479
43457
  visit(sourceFile);
43480
43458
  return foundRange;
43481
43459
  };
43460
+ const NEWLINE_BYTE = 10;
43461
+ const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
43462
+ let lineNumber = 1;
43463
+ const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
43464
+ for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === NEWLINE_BYTE) lineNumber++;
43465
+ return lineNumber;
43466
+ };
43482
43467
  /**
43483
43468
  * Drops `react-doctor/react-compiler-no-manual-memoization` diagnostics
43484
43469
  * whose enclosing function also carries a `react-hooks-js` (React
@@ -43683,33 +43668,8 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
43683
43668
  }
43684
43669
  if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
43685
43670
  const minifiedFileCache = /* @__PURE__ */ new Map();
43686
- const sourceBufferCache = /* @__PURE__ */ new Map();
43687
- const sourceLinesCache = /* @__PURE__ */ new Map();
43688
- const resolveAbsolutePath = (filename) => Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
43689
- const readSourceBuffer = (filename) => {
43690
- const absolutePath = resolveAbsolutePath(filename);
43691
- const cached = sourceBufferCache.get(absolutePath);
43692
- if (cached !== void 0) return cached;
43693
- let sourceBuffer;
43694
- try {
43695
- sourceBuffer = NFS.readFileSync(absolutePath);
43696
- } catch {
43697
- sourceBuffer = null;
43698
- }
43699
- sourceBufferCache.set(absolutePath, sourceBuffer);
43700
- return sourceBuffer;
43701
- };
43702
- const readSourceLines = (filename) => {
43703
- const absolutePath = resolveAbsolutePath(filename);
43704
- const cached = sourceLinesCache.get(absolutePath);
43705
- if (cached !== void 0) return cached;
43706
- const sourceBuffer = readSourceBuffer(filename);
43707
- const sourceLines = sourceBuffer ? sourceBuffer.toString("utf8").split("\n") : null;
43708
- sourceLinesCache.set(absolutePath, sourceLines);
43709
- return sourceLines;
43710
- };
43711
43671
  const isMinifiedDiagnosticFile = (filename) => {
43712
- const absolutePath = resolveAbsolutePath(filename);
43672
+ const absolutePath = Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
43713
43673
  const cached = minifiedFileCache.get(absolutePath);
43714
43674
  if (cached !== void 0) return cached;
43715
43675
  const minified = isMinifiedSource(absolutePath);
@@ -43722,17 +43682,8 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
43722
43682
  const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
43723
43683
  const normalizedFilePath = diagnostic.filename.replaceAll("\\", "/");
43724
43684
  const primarySpan = primaryLabel?.span;
43725
- const sourceBuffer = primarySpan ? readSourceBuffer(diagnostic.filename) : null;
43726
43685
  const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath);
43727
43686
  const category = resolveDiagnosticCategory(plugin, rule);
43728
- const matchByOccurrence = resolveMatchByOccurrence(rule, category);
43729
- const sourceLines = primarySpan ? readSourceLines(diagnostic.filename) : null;
43730
- const primaryLineIndex = primarySpan ? primarySpan.line - 1 : -1;
43731
- const primaryLine = sourceLines?.[primaryLineIndex];
43732
- const primaryColumnIndex = primarySpan ? primarySpan.column - 1 : -1;
43733
- const isJsxTagLabel = primaryLine !== void 0 && primaryColumnIndex >= 0 && (primaryLine[primaryColumnIndex] === "<" || primaryLine[primaryColumnIndex - 1] === "<");
43734
- const jsxOpenerEndLineIndex = sourceLines && isJsxTagLabel ? findJsxOpenerSpan(sourceLines, primaryLineIndex) : null;
43735
- const primarySpanEndLine = jsxOpenerEndLineIndex !== null ? jsxOpenerEndLineIndex + 1 : primarySpan && sourceBuffer ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset + Math.max(primarySpan.length - 1, 0)) : void 0;
43736
43687
  return {
43737
43688
  filePath: normalizedFilePath,
43738
43689
  plugin,
@@ -43746,11 +43697,10 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
43746
43697
  column: primarySpan?.column ?? 0,
43747
43698
  ...primarySpan ? {
43748
43699
  offset: primarySpan.offset,
43749
- length: primarySpan.length,
43750
- ...primarySpanEndLine !== void 0 ? { endLine: primarySpanEndLine } : {}
43700
+ length: primarySpan.length
43751
43701
  } : {},
43752
43702
  category,
43753
- ...matchByOccurrence ? { matchByOccurrence: true } : {},
43703
+ ...resolveMatchByOccurrence(rule, category) ? { matchByOccurrence: true } : {},
43754
43704
  ...relatedLocations.length > 0 ? { relatedLocations } : {}
43755
43705
  };
43756
43706
  }), rootDirectory);
@@ -46282,11 +46232,10 @@ const computeDiagnosticDelta = (input) => {
46282
46232
  * render diff-base mistakes (`GitBaseBranchInvalid` /
46283
46233
  * `GitBaseBranchMissing`) as clean user errors instead of crashes.
46284
46234
  */
46285
- const getDiffInfo = (directory, explicitBaseBranch, includeUntracked) => runPromise(gen(function* () {
46235
+ const getDiffInfo = (directory, explicitBaseBranch) => runPromise(gen(function* () {
46286
46236
  const selection = yield* (yield* Git).diffSelection({
46287
46237
  directory,
46288
- explicitBaseBranch,
46289
- includeUntracked
46238
+ explicitBaseBranch
46290
46239
  });
46291
46240
  if (selection === null) return null;
46292
46241
  return {
@@ -46716,7 +46665,7 @@ const makeNoopConsole = () => ({
46716
46665
  });
46717
46666
  //#endregion
46718
46667
  //#region src/cli/utils/version.ts
46719
- const VERSION = "0.7.6-dev.47beb25";
46668
+ const VERSION = "0.7.6-dev.4fb3694";
46720
46669
  //#endregion
46721
46670
  //#region src/cli/utils/json-mode.ts
46722
46671
  let context = null;
@@ -47085,13 +47034,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
47085
47034
  * uploads source-map artifacts under, so stack frames symbolicate. Honors the
47086
47035
  * standard `SENTRY_RELEASE` override.
47087
47036
  */
47088
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.6-dev.47beb25`;
47037
+ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.6-dev.4fb3694`;
47089
47038
  /**
47090
47039
  * Deployment environment shown in Sentry's environment filter. Defaults to
47091
47040
  * `production` for tagged releases and `development` for dev/unbuilt versions,
47092
47041
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
47093
47042
  */
47094
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.6-dev.47beb25") ? "development" : "production");
47043
+ const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.6-dev.4fb3694") ? "development" : "production");
47095
47044
  /**
47096
47045
  * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
47097
47046
  * (set to `0` to disable tracing) and falls back to
@@ -50856,7 +50805,7 @@ const BLOCKING_SUMMARY = {
50856
50805
  const SCOPE_SUMMARY = {
50857
50806
  full: "Scan the whole project on every run",
50858
50807
  files: "Report every issue in changed files",
50859
- lines: "Report issues whose source spans touch changed lines",
50808
+ lines: "Report issues on changed lines",
50860
50809
  changed: "Report only the issues a change introduces"
50861
50810
  };
50862
50811
  const gatesEqual = (left, right) => left.blocking === right.blocking && left.scope === right.scope && left.comment === right.comment && left.reviewComments === right.reviewComments && left.commitStatus === right.commitStatus;
@@ -59726,8 +59675,7 @@ const buildScanAttributes = (input) => {
59726
59675
  rulesConfigured: ruleKeys.length,
59727
59676
  rulesDisabled: ruleKeys.filter((key) => ruleOverrides[key] === "off").length,
59728
59677
  fileCount: input.result?.scannedFileCount ?? null,
59729
- nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length ?? null,
59730
- multilineDiagnosticCount: input.result?.diagnostics.filter((diagnostic) => (diagnostic.endLine ?? diagnostic.line) > diagnostic.line).length ?? null
59678
+ nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length ?? null
59731
59679
  });
59732
59680
  };
59733
59681
  /**
@@ -59796,12 +59744,6 @@ const countDeadlineSkippedFiles = (lintPartialFailures) => sumFileCountsMatching
59796
59744
  const DROPPED_FILES_MESSAGE_PATTERN = /^(\d+) file\(s\) failed to lint and were skipped/;
59797
59745
  const countDroppedLintFiles = (lintPartialFailures) => sumFileCountsMatching(lintPartialFailures, DROPPED_FILES_MESSAGE_PATTERN);
59798
59746
  //#endregion
59799
- //#region src/cli/utils/diagnostic-intersects-line-ranges.ts
59800
- const diagnosticIntersectsLineRanges = (diagnostic, lineRanges) => {
59801
- const diagnosticEndLine = Math.max(diagnostic.line, diagnostic.endLine ?? diagnostic.line);
59802
- return lineRanges.some(([rangeStartLine, rangeEndLine]) => diagnostic.line <= rangeEndLine && diagnosticEndLine >= rangeStartLine);
59803
- };
59804
- //#endregion
59805
59747
  //#region src/cli/utils/materialize-baseline-files.ts
59806
59748
  /**
59807
59749
  * Materializes the `ref` version of `files` into `tempDirectory` (via
@@ -60987,7 +60929,7 @@ const buildChangedLineMatcher = (directory, changedLineRanges) => {
60987
60929
  const relativePath = toForwardSlashes(path.isAbsolute(diagnostic.filePath) ? path.relative(directory, diagnostic.filePath) : diagnostic.filePath);
60988
60930
  const ranges = rangesByFile.get(relativePath);
60989
60931
  if (ranges === void 0) return false;
60990
- return diagnosticIntersectsLineRanges(diagnostic, ranges);
60932
+ return ranges.some(([start, end]) => diagnostic.line >= start && diagnostic.line <= end);
60991
60933
  };
60992
60934
  };
60993
60935
  const buildIgnoredTags = (userConfig) => {
@@ -63809,15 +63751,10 @@ const warnIfAiTrainingEnvironment = () => {
63809
63751
  //#region src/cli/utils/validate-mode-flags.ts
63810
63752
  const usedDiffAlias = (flags) => flags.diff !== void 0 && flags.diff !== false && flags.diff !== "false" && flags.diff !== "";
63811
63753
  const usedScope = (flags) => typeof flags.scope === "string" && flags.scope.length > 0;
63812
- const validateIncludeUntrackedScope = (includeUntracked, scope) => {
63813
- if (!includeUntracked || scope !== void 0 && scope !== "full") return;
63814
- throw new CliInputError("--include-untracked requires the files, changed, or lines scope (via --scope, --diff, or config).");
63815
- };
63816
63754
  const validateModeFlags = (flags) => {
63817
63755
  if (usedScope(flags) && usedDiffAlias(flags)) throw new CliInputError("Cannot combine --scope and --diff; --diff is the deprecated alias.");
63818
63756
  if (flags.staged && usedDiffAlias(flags)) throw new CliInputError("Cannot combine --staged and --diff; pick one mode.");
63819
63757
  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.`);
63820
- if (flags.includeUntracked && flags.staged) throw new CliInputError("Cannot combine --include-untracked with --staged; the git index never holds untracked files.");
63821
63758
  if (flags.score && flags.json) throw new CliInputError("Cannot combine --score and --json; pick one output mode.");
63822
63759
  if (flags.score && flags.telemetry === false) throw new CliInputError("Cannot combine --score with --no-telemetry; --score prints the score that --no-telemetry disables.");
63823
63760
  if (flags.debug && (flags.score === false || flags.telemetry === false)) {
@@ -64027,15 +63964,13 @@ const inspectAction = async (directory, flags) => {
64027
63964
  const projectDirectories = await selectProjects(resolvedDirectory, flags.project, skipPrompts, userConfig?.projects);
64028
63965
  const changedFilesDiffInfo = flags.changedFilesFrom ? buildChangedFilesDiffInfo(readChangedFilesFrom(Path.resolve(flags.changedFilesFrom))) : null;
64029
63966
  const requestedScope = resolveScope(flags, userConfig);
64030
- const includeUntracked = flags.includeUntracked ?? false;
64031
63967
  const scopeRequest = requestedScope.scope === void 0 && changedFilesDiffInfo !== null ? {
64032
63968
  ...requestedScope,
64033
63969
  scope: "changed"
64034
63970
  } : requestedScope;
64035
- validateIncludeUntrackedScope(includeUntracked, scopeRequest.scope);
64036
63971
  const wantsDiffMode = scopeRequest.scope !== void 0 && scopeRequest.scope !== "full";
64037
63972
  const shouldDetectDiff = changedFilesDiffInfo === null && (wantsDiffMode || scopeRequest.scope === void 0 && !skipPrompts && !isQuiet);
64038
- const diffInfo = changedFilesDiffInfo ?? (shouldDetectDiff ? await getDiffInfo(resolvedDirectory, scopeRequest.base, includeUntracked) : null);
63973
+ const diffInfo = changedFilesDiffInfo ?? (shouldDetectDiff ? await getDiffInfo(resolvedDirectory, scopeRequest.base) : null);
64039
63974
  const scope = await finalizeScope({
64040
63975
  requested: scopeRequest,
64041
63976
  diffInfo,
@@ -64049,8 +63984,7 @@ const inspectAction = async (directory, flags) => {
64049
63984
  const changedLineRanges = scope === "lines" && diffInfo !== null && (diffInfo.isCurrentChanges || linesBaseRef !== null) && diffInfo !== null ? await getChangedLineRanges({
64050
63985
  directory: resolvedDirectory,
64051
63986
  baseRef: linesBaseRef ?? void 0,
64052
- files: [...diffInfo.changedFiles],
64053
- includeUntracked
63987
+ files: [...diffInfo.changedFiles]
64054
63988
  }) : null;
64055
63989
  if (scope === "lines" && changedLineRanges === null && !isQuiet) {
64056
63990
  cliLogger.warn("Could not determine changed lines (no base ref or git diff failed); reporting all issues in changed files.");
@@ -64983,7 +64917,6 @@ const ROOT_FLAG_SPEC = {
64983
64917
  "--dead-code",
64984
64918
  "--debug",
64985
64919
  "--help",
64986
- "--include-untracked",
64987
64920
  "--json",
64988
64921
  "--json-compact",
64989
64922
  "--lint",
@@ -65247,7 +65180,7 @@ ${highlighter.dim("Learn more:")}
65247
65180
  ${highlighter.info(CI_URL)}
65248
65181
  `;
65249
65182
  const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
65250
- 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);
65183
+ 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 (only 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);
65251
65184
  program.action(inspectAction);
65252
65185
  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));
65253
65186
  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);
@@ -65297,4 +65230,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
65297
65230
  export {};
65298
65231
 
65299
65232
  //# sourceMappingURL=cli.js.map
65300
- //# debugId=5dcb5b0b-ca1e-5655-8f52-3c22b421256d
65233
+ //# debugId=bc56f5c4-5b95-5727-8e86-d43a534abbbc
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]="80f981d4-ebf0-5bc0-9643-8a68d27626a5")}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]="3e7262c1-34a6-5fd7-a4a2-5e7a15ca735d")}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";
@@ -33142,15 +33142,13 @@ const listSourceFilesViaFilesystem = (rootDirectory) => {
33142
33142
  const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort());
33143
33143
  const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
33144
33144
  const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
33145
- const UTF8_BOM_CHARACTER = "";
33146
- const stripUtf8Bom = (content) => content.startsWith(UTF8_BOM_CHARACTER) ? content.slice(1) : content;
33147
33145
  const cachedPackageJsons = /* @__PURE__ */ new Map();
33148
33146
  const clearPackageJsonCache = () => {
33149
33147
  cachedPackageJsons.clear();
33150
33148
  };
33151
33149
  const readPackageJsonUncached = (packageJsonPath) => {
33152
33150
  try {
33153
- return JSON.parse(stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8")));
33151
+ return JSON.parse(NFS.readFileSync(packageJsonPath, "utf-8"));
33154
33152
  } catch (error) {
33155
33153
  if (error instanceof SyntaxError) return {};
33156
33154
  if (isErrnoException(error)) {
@@ -36791,6 +36789,7 @@ const PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
36791
36789
  const PNPM_LOCKFILE = "pnpm-lock.yaml";
36792
36790
  const PACKAGE_JSON_FILE = "package.json";
36793
36791
  const PNPM_HARDENING_RULE_KEY = "require-pnpm-hardening";
36792
+ const UTF8_BOM_CHAR = "";
36794
36793
  const HARDENING_SETTING_KEYS = new Set([
36795
36794
  "minimumReleaseAge",
36796
36795
  "blockExoticSubdeps",
@@ -36815,11 +36814,12 @@ const stripInlineComment = (rawValue) => {
36815
36814
  return rawValue;
36816
36815
  };
36817
36816
  const unquote = (rawValue) => rawValue.replace(/^["']|["']$/g, "");
36817
+ const stripBom = (rawContent) => rawContent.startsWith(UTF8_BOM_CHAR) ? rawContent.slice(1) : rawContent;
36818
36818
  const parseHardeningSettings = (content) => {
36819
36819
  let minimumReleaseAge = null;
36820
36820
  let blockExoticSubdeps = null;
36821
36821
  let trustPolicy = null;
36822
- const lines = stripUtf8Bom(content).split(/\r?\n/);
36822
+ const lines = stripBom(content).split(/\r?\n/);
36823
36823
  for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
36824
36824
  const lineText = lines[lineIndex];
36825
36825
  if (lineText === void 0) continue;
@@ -36853,7 +36853,7 @@ const isPnpmManagedProject = (rootDirectory) => {
36853
36853
  const packageJsonPath = Path.join(rootDirectory, PACKAGE_JSON_FILE);
36854
36854
  if (!isFile(packageJsonPath)) return false;
36855
36855
  try {
36856
- const packageJsonRaw = stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8"));
36856
+ const packageJsonRaw = NFS.readFileSync(packageJsonPath, "utf-8");
36857
36857
  const packageJson = JSON.parse(packageJsonRaw);
36858
36858
  if (packageJson !== null && typeof packageJson === "object" && "packageManager" in packageJson && typeof packageJson.packageManager === "string" && packageJson.packageManager.startsWith("pnpm@")) return true;
36859
36859
  } catch {
@@ -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)))));
@@ -39658,12 +39642,6 @@ const mapWithConcurrency = async (items, concurrency, task) => {
39658
39642
  if (errors.length > 0) throw errors[0];
39659
39643
  return results;
39660
39644
  };
39661
- const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
39662
- let lineNumber = 1;
39663
- const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
39664
- for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === 10) lineNumber++;
39665
- return lineNumber;
39666
- };
39667
39645
  const REANIMATED_SHARED_VALUE_HINT = "If this is a Reanimated shared value, prefer its React Compiler-compatible `.get()` / `.set()` accessors over `.value` — https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue/#react-compiler-support";
39668
39646
  const ACCESSORS_MIN_VERSION = "3.15.0";
39669
39647
  const supportsAccessors = (reanimatedVersion) => {
@@ -40235,6 +40213,13 @@ const findOutermostEnclosingFunctionRange = (sourceFile, targetLine) => {
40235
40213
  visit(sourceFile);
40236
40214
  return foundRange;
40237
40215
  };
40216
+ const NEWLINE_BYTE = 10;
40217
+ const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
40218
+ let lineNumber = 1;
40219
+ const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
40220
+ for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === NEWLINE_BYTE) lineNumber++;
40221
+ return lineNumber;
40222
+ };
40238
40223
  /**
40239
40224
  * Drops `react-doctor/react-compiler-no-manual-memoization` diagnostics
40240
40225
  * whose enclosing function also carries a `react-hooks-js` (React
@@ -40439,33 +40424,8 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
40439
40424
  }
40440
40425
  if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
40441
40426
  const minifiedFileCache = /* @__PURE__ */ new Map();
40442
- const sourceBufferCache = /* @__PURE__ */ new Map();
40443
- const sourceLinesCache = /* @__PURE__ */ new Map();
40444
- const resolveAbsolutePath = (filename) => Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
40445
- const readSourceBuffer = (filename) => {
40446
- const absolutePath = resolveAbsolutePath(filename);
40447
- const cached = sourceBufferCache.get(absolutePath);
40448
- if (cached !== void 0) return cached;
40449
- let sourceBuffer;
40450
- try {
40451
- sourceBuffer = NFS.readFileSync(absolutePath);
40452
- } catch {
40453
- sourceBuffer = null;
40454
- }
40455
- sourceBufferCache.set(absolutePath, sourceBuffer);
40456
- return sourceBuffer;
40457
- };
40458
- const readSourceLines = (filename) => {
40459
- const absolutePath = resolveAbsolutePath(filename);
40460
- const cached = sourceLinesCache.get(absolutePath);
40461
- if (cached !== void 0) return cached;
40462
- const sourceBuffer = readSourceBuffer(filename);
40463
- const sourceLines = sourceBuffer ? sourceBuffer.toString("utf8").split("\n") : null;
40464
- sourceLinesCache.set(absolutePath, sourceLines);
40465
- return sourceLines;
40466
- };
40467
40427
  const isMinifiedDiagnosticFile = (filename) => {
40468
- const absolutePath = resolveAbsolutePath(filename);
40428
+ const absolutePath = Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
40469
40429
  const cached = minifiedFileCache.get(absolutePath);
40470
40430
  if (cached !== void 0) return cached;
40471
40431
  const minified = isMinifiedSource(absolutePath);
@@ -40478,17 +40438,8 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
40478
40438
  const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
40479
40439
  const normalizedFilePath = diagnostic.filename.replaceAll("\\", "/");
40480
40440
  const primarySpan = primaryLabel?.span;
40481
- const sourceBuffer = primarySpan ? readSourceBuffer(diagnostic.filename) : null;
40482
40441
  const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath);
40483
40442
  const category = resolveDiagnosticCategory(plugin, rule);
40484
- const matchByOccurrence = resolveMatchByOccurrence(rule, category);
40485
- const sourceLines = primarySpan ? readSourceLines(diagnostic.filename) : null;
40486
- const primaryLineIndex = primarySpan ? primarySpan.line - 1 : -1;
40487
- const primaryLine = sourceLines?.[primaryLineIndex];
40488
- const primaryColumnIndex = primarySpan ? primarySpan.column - 1 : -1;
40489
- const isJsxTagLabel = primaryLine !== void 0 && primaryColumnIndex >= 0 && (primaryLine[primaryColumnIndex] === "<" || primaryLine[primaryColumnIndex - 1] === "<");
40490
- const jsxOpenerEndLineIndex = sourceLines && isJsxTagLabel ? findJsxOpenerSpan(sourceLines, primaryLineIndex) : null;
40491
- const primarySpanEndLine = jsxOpenerEndLineIndex !== null ? jsxOpenerEndLineIndex + 1 : primarySpan && sourceBuffer ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset + Math.max(primarySpan.length - 1, 0)) : void 0;
40492
40443
  return {
40493
40444
  filePath: normalizedFilePath,
40494
40445
  plugin,
@@ -40502,11 +40453,10 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
40502
40453
  column: primarySpan?.column ?? 0,
40503
40454
  ...primarySpan ? {
40504
40455
  offset: primarySpan.offset,
40505
- length: primarySpan.length,
40506
- ...primarySpanEndLine !== void 0 ? { endLine: primarySpanEndLine } : {}
40456
+ length: primarySpan.length
40507
40457
  } : {},
40508
40458
  category,
40509
- ...matchByOccurrence ? { matchByOccurrence: true } : {},
40459
+ ...resolveMatchByOccurrence(rule, category) ? { matchByOccurrence: true } : {},
40510
40460
  ...relatedLocations.length > 0 ? { relatedLocations } : {}
40511
40461
  };
40512
40462
  }), rootDirectory);
@@ -42964,11 +42914,10 @@ const buildSkippedChecks = (input) => {
42964
42914
  * render diff-base mistakes (`GitBaseBranchInvalid` /
42965
42915
  * `GitBaseBranchMissing`) as clean user errors instead of crashes.
42966
42916
  */
42967
- const getDiffInfo = (directory, explicitBaseBranch, includeUntracked) => runPromise(gen(function* () {
42917
+ const getDiffInfo = (directory, explicitBaseBranch) => runPromise(gen(function* () {
42968
42918
  const selection = yield* (yield* Git).diffSelection({
42969
42919
  directory,
42970
- explicitBaseBranch,
42971
- includeUntracked
42920
+ explicitBaseBranch
42972
42921
  });
42973
42922
  if (selection === null) return null;
42974
42923
  return {
@@ -43258,4 +43207,4 @@ const toJsonReport = (result, options) => buildJsonReport({
43258
43207
  export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
43259
43208
 
43260
43209
  //# sourceMappingURL=index.js.map
43261
- //# debugId=80f981d4-ebf0-5bc0-9643-8a68d27626a5
43210
+ //# debugId=3e7262c1-34a6-5fd7-a4a2-5e7a15ca735d
package/dist/lsp.js CHANGED
@@ -33194,15 +33194,13 @@ const listSourceFilesViaFilesystem = (rootDirectory) => {
33194
33194
  const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort());
33195
33195
  const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
33196
33196
  const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
33197
- const UTF8_BOM_CHARACTER = "";
33198
- const stripUtf8Bom = (content) => content.startsWith(UTF8_BOM_CHARACTER) ? content.slice(1) : content;
33199
33197
  const cachedPackageJsons = /* @__PURE__ */ new Map();
33200
33198
  const clearPackageJsonCache = () => {
33201
33199
  cachedPackageJsons.clear();
33202
33200
  };
33203
33201
  const readPackageJsonUncached = (packageJsonPath) => {
33204
33202
  try {
33205
- return JSON.parse(stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8")));
33203
+ return JSON.parse(NFS.readFileSync(packageJsonPath, "utf-8"));
33206
33204
  } catch (error) {
33207
33205
  if (error instanceof SyntaxError) return {};
33208
33206
  if (isErrnoException(error)) {
@@ -36769,6 +36767,7 @@ const PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
36769
36767
  const PNPM_LOCKFILE = "pnpm-lock.yaml";
36770
36768
  const PACKAGE_JSON_FILE = "package.json";
36771
36769
  const PNPM_HARDENING_RULE_KEY = "require-pnpm-hardening";
36770
+ const UTF8_BOM_CHAR = "";
36772
36771
  const HARDENING_SETTING_KEYS = new Set([
36773
36772
  "minimumReleaseAge",
36774
36773
  "blockExoticSubdeps",
@@ -36793,11 +36792,12 @@ const stripInlineComment = (rawValue) => {
36793
36792
  return rawValue;
36794
36793
  };
36795
36794
  const unquote = (rawValue) => rawValue.replace(/^["']|["']$/g, "");
36795
+ const stripBom = (rawContent) => rawContent.startsWith(UTF8_BOM_CHAR) ? rawContent.slice(1) : rawContent;
36796
36796
  const parseHardeningSettings = (content) => {
36797
36797
  let minimumReleaseAge = null;
36798
36798
  let blockExoticSubdeps = null;
36799
36799
  let trustPolicy = null;
36800
- const lines = stripUtf8Bom(content).split(/\r?\n/);
36800
+ const lines = stripBom(content).split(/\r?\n/);
36801
36801
  for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
36802
36802
  const lineText = lines[lineIndex];
36803
36803
  if (lineText === void 0) continue;
@@ -36831,7 +36831,7 @@ const isPnpmManagedProject = (rootDirectory) => {
36831
36831
  const packageJsonPath = Path.join(rootDirectory, PACKAGE_JSON_FILE);
36832
36832
  if (!isFile(packageJsonPath)) return false;
36833
36833
  try {
36834
- const packageJsonRaw = stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8"));
36834
+ const packageJsonRaw = NFS.readFileSync(packageJsonPath, "utf-8");
36835
36835
  const packageJson = JSON.parse(packageJsonRaw);
36836
36836
  if (packageJson !== null && typeof packageJson === "object" && "packageManager" in packageJson && typeof packageJson.packageManager === "string" && packageJson.packageManager.startsWith("pnpm@")) return true;
36837
36837
  } catch {
@@ -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)))));
@@ -39636,12 +39620,6 @@ const mapWithConcurrency = async (items, concurrency, task) => {
39636
39620
  if (errors.length > 0) throw errors[0];
39637
39621
  return results;
39638
39622
  };
39639
- const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
39640
- let lineNumber = 1;
39641
- const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
39642
- for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === 10) lineNumber++;
39643
- return lineNumber;
39644
- };
39645
39623
  const REANIMATED_SHARED_VALUE_HINT = "If this is a Reanimated shared value, prefer its React Compiler-compatible `.get()` / `.set()` accessors over `.value` — https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue/#react-compiler-support";
39646
39624
  const ACCESSORS_MIN_VERSION = "3.15.0";
39647
39625
  const supportsAccessors = (reanimatedVersion) => {
@@ -40213,6 +40191,13 @@ const findOutermostEnclosingFunctionRange = (sourceFile, targetLine) => {
40213
40191
  visit(sourceFile);
40214
40192
  return foundRange;
40215
40193
  };
40194
+ const NEWLINE_BYTE = 10;
40195
+ const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
40196
+ let lineNumber = 1;
40197
+ const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
40198
+ for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === NEWLINE_BYTE) lineNumber++;
40199
+ return lineNumber;
40200
+ };
40216
40201
  /**
40217
40202
  * Drops `react-doctor/react-compiler-no-manual-memoization` diagnostics
40218
40203
  * whose enclosing function also carries a `react-hooks-js` (React
@@ -40417,33 +40402,8 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
40417
40402
  }
40418
40403
  if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
40419
40404
  const minifiedFileCache = /* @__PURE__ */ new Map();
40420
- const sourceBufferCache = /* @__PURE__ */ new Map();
40421
- const sourceLinesCache = /* @__PURE__ */ new Map();
40422
- const resolveAbsolutePath = (filename) => Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
40423
- const readSourceBuffer = (filename) => {
40424
- const absolutePath = resolveAbsolutePath(filename);
40425
- const cached = sourceBufferCache.get(absolutePath);
40426
- if (cached !== void 0) return cached;
40427
- let sourceBuffer;
40428
- try {
40429
- sourceBuffer = NFS.readFileSync(absolutePath);
40430
- } catch {
40431
- sourceBuffer = null;
40432
- }
40433
- sourceBufferCache.set(absolutePath, sourceBuffer);
40434
- return sourceBuffer;
40435
- };
40436
- const readSourceLines = (filename) => {
40437
- const absolutePath = resolveAbsolutePath(filename);
40438
- const cached = sourceLinesCache.get(absolutePath);
40439
- if (cached !== void 0) return cached;
40440
- const sourceBuffer = readSourceBuffer(filename);
40441
- const sourceLines = sourceBuffer ? sourceBuffer.toString("utf8").split("\n") : null;
40442
- sourceLinesCache.set(absolutePath, sourceLines);
40443
- return sourceLines;
40444
- };
40445
40405
  const isMinifiedDiagnosticFile = (filename) => {
40446
- const absolutePath = resolveAbsolutePath(filename);
40406
+ const absolutePath = Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
40447
40407
  const cached = minifiedFileCache.get(absolutePath);
40448
40408
  if (cached !== void 0) return cached;
40449
40409
  const minified = isMinifiedSource(absolutePath);
@@ -40456,17 +40416,8 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
40456
40416
  const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
40457
40417
  const normalizedFilePath = diagnostic.filename.replaceAll("\\", "/");
40458
40418
  const primarySpan = primaryLabel?.span;
40459
- const sourceBuffer = primarySpan ? readSourceBuffer(diagnostic.filename) : null;
40460
40419
  const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath);
40461
40420
  const category = resolveDiagnosticCategory(plugin, rule);
40462
- const matchByOccurrence = resolveMatchByOccurrence(rule, category);
40463
- const sourceLines = primarySpan ? readSourceLines(diagnostic.filename) : null;
40464
- const primaryLineIndex = primarySpan ? primarySpan.line - 1 : -1;
40465
- const primaryLine = sourceLines?.[primaryLineIndex];
40466
- const primaryColumnIndex = primarySpan ? primarySpan.column - 1 : -1;
40467
- const isJsxTagLabel = primaryLine !== void 0 && primaryColumnIndex >= 0 && (primaryLine[primaryColumnIndex] === "<" || primaryLine[primaryColumnIndex - 1] === "<");
40468
- const jsxOpenerEndLineIndex = sourceLines && isJsxTagLabel ? findJsxOpenerSpan(sourceLines, primaryLineIndex) : null;
40469
- const primarySpanEndLine = jsxOpenerEndLineIndex !== null ? jsxOpenerEndLineIndex + 1 : primarySpan && sourceBuffer ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset + Math.max(primarySpan.length - 1, 0)) : void 0;
40470
40421
  return {
40471
40422
  filePath: normalizedFilePath,
40472
40423
  plugin,
@@ -40480,11 +40431,10 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
40480
40431
  column: primarySpan?.column ?? 0,
40481
40432
  ...primarySpan ? {
40482
40433
  offset: primarySpan.offset,
40483
- length: primarySpan.length,
40484
- ...primarySpanEndLine !== void 0 ? { endLine: primarySpanEndLine } : {}
40434
+ length: primarySpan.length
40485
40435
  } : {},
40486
40436
  category,
40487
- ...matchByOccurrence ? { matchByOccurrence: true } : {},
40437
+ ...resolveMatchByOccurrence(rule, category) ? { matchByOccurrence: true } : {},
40488
40438
  ...relatedLocations.length > 0 ? { relatedLocations } : {}
40489
40439
  };
40490
40440
  }), rootDirectory);
@@ -44894,5 +44844,5 @@ const startLanguageServer = () => {
44894
44844
  };
44895
44845
  //#endregion
44896
44846
  export { startLanguageServer };
44897
- !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]="288f7240-2ee1-5bcb-a56e-bca2b972818c")}catch(e){}}();
44898
- //# debugId=288f7240-2ee1-5bcb-a56e-bca2b972818c
44847
+ !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]="a3135dfe-cc85-5d34-adea-93f5c29d9bcb")}catch(e){}}();
44848
+ //# debugId=a3135dfe-cc85-5d34-adea-93f5c29d9bcb
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor",
3
- "version": "0.7.6-dev.47beb25",
3
+ "version": "0.7.6-dev.4fb3694",
4
4
  "description": "Your agent writes bad React. This catches it",
5
5
  "keywords": [
6
6
  "accessibility",
@@ -65,8 +65,8 @@
65
65
  "vscode-languageserver-textdocument": "^1.0.12",
66
66
  "vscode-uri": "^3.1.0",
67
67
  "yaml": "^2.9.0",
68
- "deslop-js": "0.7.6",
69
- "oxlint-plugin-react-doctor": "0.7.6-dev.47beb25"
68
+ "oxlint-plugin-react-doctor": "0.7.6-dev.4fb3694",
69
+ "deslop-js": "0.7.6"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/babel__code-frame": "^7.27.0",
@@ -74,9 +74,9 @@
74
74
  "@xterm/headless": "^6.0.0",
75
75
  "commander": "^14.0.3",
76
76
  "ora": "^9.4.0",
77
- "@react-doctor/api": "0.7.6",
77
+ "@react-doctor/core": "0.7.6",
78
78
  "@react-doctor/language-server": "0.7.6",
79
- "@react-doctor/core": "0.7.6"
79
+ "@react-doctor/api": "0.7.6"
80
80
  },
81
81
  "engines": {
82
82
  "node": "^20.19.0 || >=22.13.0"