react-doctor 0.7.2-dev.82e0475 → 0.7.2-dev.9b59d96

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]="91ba16f6-8b1a-5cd9-a5d2-fc0b6fce2508")}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]="9aed4e70-dd94-51e8-9810-f8e8945d80a3")}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";
@@ -36131,6 +36131,8 @@ const OXLINT_SPAWN_TIMEOUT_MS = 6e4;
36131
36131
  const NODE_COMPILE_CACHE_DIR_NAME = "node-compile-cache";
36132
36132
  const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
36133
36133
  const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
36134
+ const ABORT_EXIT_CODES = new Set([134, 3221226505]);
36135
+ const OXLINT_OOM_RESCUE_BUDGET_MS = 6e4;
36134
36136
  const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
36135
36137
  const LINT_PHASE_TIMEOUT_MS = 3e5;
36136
36138
  const SCAN_TOTAL_DEADLINE_MS = 9e5;
@@ -43602,7 +43604,9 @@ const SANITIZED_ENV = buildOxlintChildEnv(process.env);
43602
43604
  * - `OxlintBatchExceeded { kind: "output-too-large" }` — stdout+stderr
43603
43605
  * crossed `OXLINT_OUTPUT_MAX_BYTES`.
43604
43606
  * - `OxlintBatchExceeded { kind: "oom" | "killed" }` — child exited
43605
- * on a signal (SIGABRT → OOM, others → generic kill).
43607
+ * on a signal (SIGABRT → OOM, others → generic kill), or with one
43608
+ * of the abort exit codes Windows reports instead of a signal
43609
+ * (`ABORT_EXIT_CODES` → OOM).
43606
43610
  * - `OxlintSpawnFailed { cause }` — `spawn` itself errored, or the
43607
43611
  * child exited successfully but printed only stderr.
43608
43612
  *
@@ -43668,7 +43672,7 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
43668
43672
  clearAbortListener();
43669
43673
  reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: error }) }));
43670
43674
  });
43671
- child.on("close", (_code, signal) => {
43675
+ child.on("close", (code, signal) => {
43672
43676
  clearTimeout(timeoutHandle);
43673
43677
  clearAbortListener();
43674
43678
  if (didKillForSize) {
@@ -43678,10 +43682,11 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
43678
43682
  }) }));
43679
43683
  return;
43680
43684
  }
43681
- if (signal) {
43685
+ const isAbortExitCode = code !== null && ABORT_EXIT_CODES.has(code);
43686
+ if (signal || isAbortExitCode) {
43682
43687
  const stderrOutput = Buffer.concat(stderrBuffers).toString("utf-8").trim();
43683
- const isOom = signal === "SIGABRT";
43684
- const detailParts = [`killed by ${signal}`];
43688
+ const isOom = signal === "SIGABRT" || isAbortExitCode;
43689
+ const detailParts = [signal ? `killed by ${signal}` : `aborted with exit code ${code}`];
43685
43690
  if (isOom) detailParts.push("try scanning fewer files with --diff");
43686
43691
  if (stderrOutput) detailParts.push(stderrOutput);
43687
43692
  reject(new ReactDoctorError({ reason: new OxlintBatchExceeded({
@@ -43724,12 +43729,19 @@ const isParallelismRelatedSpawnError = (error) => {
43724
43729
  * (surfaced via `onPartialFailure`) so JSON-mode consumers see WHICH
43725
43730
  * files were skipped instead of silently losing them.
43726
43731
  *
43727
- * Parallel runs (concurrency > 1) get one extra safety net: if the pass
43728
- * fails with a resource-exhaustion error that's exclusive to running
43729
- * many oxlint subprocesses at once (EAGAIN / EMFILE / ENFILE / ENOMEM —
43730
- * see `isParallelismRelatedSpawnError`), the whole pass replays once
43731
- * with a single worker. That's the only failure a serial replay can
43732
- * clear, so every other error class is left to propagate.
43732
+ * Parallel runs (concurrency > 1) get two extra safety nets:
43733
+ * - If the pass fails with a resource-exhaustion error that's exclusive
43734
+ * to running many oxlint subprocesses at once (EAGAIN / EMFILE /
43735
+ * ENFILE / ENOMEM — see `isParallelismRelatedSpawnError`), the whole
43736
+ * pass replays once with a single worker. That's the only failure a
43737
+ * serial replay can clear, so every other error class is left to
43738
+ * propagate.
43739
+ * - Files dropped because oxlint's native binding SIGABRT'd under
43740
+ * memory pressure (`OxlintBatchExceeded { kind: "oom" }`) get one
43741
+ * serial single-file-batch rescue pass: the OOM is usually a function
43742
+ * of N concurrent oxlint allocator arenas, not the file itself, so a
43743
+ * lone process with the machine to itself typically clears it —
43744
+ * turning a partial scan into a complete (if slower) one.
43733
43745
  *
43734
43746
  * Errors that aren't splittable and aren't parallelism-related (oxlint
43735
43747
  * config crash, JS plugin resolution failure, etc.) propagate to the
@@ -43739,13 +43751,16 @@ const isParallelismRelatedSpawnError = (error) => {
43739
43751
  const spawnLintBatches = async (input) => {
43740
43752
  const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal, deadlineEpochMs } = input;
43741
43753
  const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
43742
- const totalFileCount = fileBatches.reduce((sum, batch) => sum + batch.length, 0);
43743
- const runBatchPass = async (concurrency) => {
43754
+ let rescueDeadlineEpochMs;
43755
+ const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0 || rescueDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(rescueDeadlineEpochMs) === 0;
43756
+ const runBatchPass = async (concurrency, passBatches, passOnFileProgress) => {
43757
+ const totalFileCount = passBatches.reduce((sum, batch) => sum + batch.length, 0);
43744
43758
  const allDiagnostics = [];
43745
43759
  const droppedFiles = [];
43760
+ const oomDroppedFiles = [];
43746
43761
  const deadlineSkippedFiles = [];
43747
- const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0;
43748
43762
  let firstDropReason = null;
43763
+ let firstNonOomDropReason = null;
43749
43764
  const spawnLintBatch = async (batch, depth, batchState) => {
43750
43765
  if (isPastDeadline()) {
43751
43766
  deadlineSkippedFiles.push(...batch);
@@ -43767,12 +43782,14 @@ const spawnLintBatches = async (input) => {
43767
43782
  const isDepthCapReached = depth >= splitMaxDepth;
43768
43783
  if (batch.length <= 1 || isBudgetElapsed || isDepthCapReached) {
43769
43784
  droppedFiles.push(...batch);
43770
- if (firstDropReason === null) {
43771
- let limitHint = "";
43772
- if (isDepthCapReached) limitHint = ` (split depth cap of ${splitMaxDepth} levels reached)`;
43773
- else if (isBudgetElapsed) limitHint = ` (split budget of ${splitTotalBudgetMs / MILLISECONDS_PER_SECOND}s exhausted at depth ${depth})`;
43774
- firstDropReason = batch.length > 1 ? `${error.message}${limitHint}` : error.message;
43775
- }
43785
+ const isOomDrop = error.reason._tag === "OxlintBatchExceeded" && error.reason.kind === "oom";
43786
+ if (isOomDrop) oomDroppedFiles.push(...batch);
43787
+ let limitHint = "";
43788
+ if (isDepthCapReached) limitHint = ` (split depth cap of ${splitMaxDepth} levels reached)`;
43789
+ else if (isBudgetElapsed) limitHint = ` (split budget of ${splitTotalBudgetMs / MILLISECONDS_PER_SECOND}s exhausted at depth ${depth})`;
43790
+ const dropReason = batch.length > 1 ? `${error.message}${limitHint}` : error.message;
43791
+ firstDropReason ??= dropReason;
43792
+ if (!isOomDrop) firstNonOomDropReason ??= dropReason;
43776
43793
  return [];
43777
43794
  }
43778
43795
  const splitIndex = Math.ceil(batch.length / 2);
@@ -43782,16 +43799,16 @@ const spawnLintBatches = async (input) => {
43782
43799
  let startedFileCount = 0;
43783
43800
  let scannedFileCount = 0;
43784
43801
  let displayedFileCount = 0;
43785
- const progressTimer = onFileProgress && totalFileCount > 1 ? setInterval(() => {
43802
+ const progressTimer = passOnFileProgress && totalFileCount > 1 ? setInterval(() => {
43786
43803
  const ceiling = Math.min(startedFileCount, totalFileCount - 1);
43787
43804
  if (displayedFileCount < ceiling) {
43788
43805
  displayedFileCount += 1;
43789
- onFileProgress(displayedFileCount, totalFileCount);
43806
+ passOnFileProgress(displayedFileCount, totalFileCount);
43790
43807
  }
43791
43808
  }, 50) : null;
43792
43809
  progressTimer?.unref?.();
43793
43810
  try {
43794
- const batchResults = await mapWithConcurrency(fileBatches, concurrency, async (batch) => {
43811
+ const batchResults = await mapWithConcurrency(passBatches, concurrency, async (batch) => {
43795
43812
  if (isPastDeadline()) {
43796
43813
  deadlineSkippedFiles.push(...batch);
43797
43814
  return [];
@@ -43803,9 +43820,9 @@ const spawnLintBatches = async (input) => {
43803
43820
  };
43804
43821
  const batchDiagnostics = await spawnLintBatch(batch, 0, batchState);
43805
43822
  scannedFileCount += batch.length - batchState.deadlineSkippedFileCount;
43806
- if (onFileProgress) {
43823
+ if (passOnFileProgress) {
43807
43824
  displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
43808
- onFileProgress(displayedFileCount, totalFileCount);
43825
+ passOnFileProgress(displayedFileCount, totalFileCount);
43809
43826
  }
43810
43827
  return batchDiagnostics;
43811
43828
  });
@@ -43813,22 +43830,53 @@ const spawnLintBatches = async (input) => {
43813
43830
  } finally {
43814
43831
  if (progressTimer !== null) clearInterval(progressTimer);
43815
43832
  }
43816
- const reportSkippedFiles = (skippedFiles, buildMessage) => {
43817
- if (skippedFiles.length === 0 || !onPartialFailure) return;
43818
- onPartialFailure(buildMessage(`${skippedFiles.slice(0, 3).join(", ")}${skippedFiles.length > 3 ? `, +${skippedFiles.length - 3} more` : ""}`));
43833
+ return {
43834
+ diagnostics: allDiagnostics,
43835
+ droppedFiles,
43836
+ oomDroppedFiles,
43837
+ deadlineSkippedFiles,
43838
+ firstDropReason,
43839
+ firstNonOomDropReason
43819
43840
  };
43820
- const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
43821
- reportSkippedFiles(droppedFiles, (fileListPreview) => `${droppedFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
43822
- reportSkippedFiles(deadlineSkippedFiles, (fileListPreview) => `${deadlineSkippedFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
43823
- return allDiagnostics;
43824
43841
  };
43825
- let diagnostics;
43842
+ let outcome;
43843
+ let effectiveConcurrency = requestedConcurrency;
43826
43844
  try {
43827
- diagnostics = await runBatchPass(requestedConcurrency);
43845
+ outcome = await runBatchPass(requestedConcurrency, fileBatches, onFileProgress);
43828
43846
  } catch (error) {
43829
43847
  if (requestedConcurrency <= 1 || !isParallelismRelatedSpawnError(error)) throw error;
43830
- diagnostics = await runBatchPass(1);
43848
+ effectiveConcurrency = 1;
43849
+ outcome = await runBatchPass(1, fileBatches, onFileProgress);
43850
+ }
43851
+ let { droppedFiles, firstDropReason } = outcome;
43852
+ const diagnostics = outcome.diagnostics;
43853
+ if (outcome.oomDroppedFiles.length > 0 && effectiveConcurrency > 1 && !isPastDeadline()) {
43854
+ rescueDeadlineEpochMs = Date.now() + OXLINT_OOM_RESCUE_BUDGET_MS;
43855
+ const rescuedFiles = /* @__PURE__ */ new Set();
43856
+ let firstRescueFailureReason = null;
43857
+ for (const oomDroppedFile of outcome.oomDroppedFiles) {
43858
+ if (isPastDeadline()) break;
43859
+ try {
43860
+ const rescueOutcome = await runBatchPass(1, [[oomDroppedFile]], void 0);
43861
+ diagnostics.push(...rescueOutcome.diagnostics);
43862
+ if (rescueOutcome.droppedFiles.length > 0 || rescueOutcome.deadlineSkippedFiles.length > 0) firstRescueFailureReason ??= rescueOutcome.firstDropReason;
43863
+ else rescuedFiles.add(oomDroppedFile);
43864
+ } catch (error) {
43865
+ firstRescueFailureReason ??= error instanceof Error ? error.message : String(error);
43866
+ }
43867
+ }
43868
+ rescueDeadlineEpochMs = void 0;
43869
+ droppedFiles = droppedFiles.filter((filePath) => !rescuedFiles.has(filePath));
43870
+ if (droppedFiles.length === 0) firstDropReason = null;
43871
+ else firstDropReason = firstRescueFailureReason ?? outcome.firstNonOomDropReason ?? firstDropReason;
43831
43872
  }
43873
+ const reportSkippedFiles = (skippedFiles, buildMessage) => {
43874
+ if (skippedFiles.length === 0 || !onPartialFailure) return;
43875
+ onPartialFailure(buildMessage(`${skippedFiles.slice(0, 3).join(", ")}${skippedFiles.length > 3 ? `, +${skippedFiles.length - 3} more` : ""}`));
43876
+ };
43877
+ const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
43878
+ reportSkippedFiles(droppedFiles, (fileListPreview) => `${droppedFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
43879
+ reportSkippedFiles(outcome.deadlineSkippedFiles, (fileListPreview) => `${outcome.deadlineSkippedFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
43832
43880
  return dedupeDiagnostics(diagnostics);
43833
43881
  };
43834
43882
  let didValidate = false;
@@ -46386,7 +46434,7 @@ const makeNoopConsole = () => ({
46386
46434
  });
46387
46435
  //#endregion
46388
46436
  //#region src/cli/utils/version.ts
46389
- const VERSION = "0.7.2-dev.82e0475";
46437
+ const VERSION = "0.7.2-dev.9b59d96";
46390
46438
  //#endregion
46391
46439
  //#region src/cli/utils/json-mode.ts
46392
46440
  let context = null;
@@ -46755,13 +46803,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
46755
46803
  * uploads source-map artifacts under, so stack frames symbolicate. Honors the
46756
46804
  * standard `SENTRY_RELEASE` override.
46757
46805
  */
46758
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.2-dev.82e0475`;
46806
+ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.2-dev.9b59d96`;
46759
46807
  /**
46760
46808
  * Deployment environment shown in Sentry's environment filter. Defaults to
46761
46809
  * `production` for tagged releases and `development` for dev/unbuilt versions,
46762
46810
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
46763
46811
  */
46764
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.2-dev.82e0475") ? "development" : "production");
46812
+ const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.2-dev.9b59d96") ? "development" : "production");
46765
46813
  /**
46766
46814
  * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
46767
46815
  * (set to `0` to disable tracing) and falls back to
@@ -64936,4 +64984,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
64936
64984
  export {};
64937
64985
 
64938
64986
  //# sourceMappingURL=cli.js.map
64939
- //# debugId=91ba16f6-8b1a-5cd9-a5d2-fc0b6fce2508
64987
+ //# debugId=9aed4e70-dd94-51e8-9810-f8e8945d80a3
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]="63c65851-4fa5-5cd1-9337-0f2feefe37ad")}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]="3dc4a1ac-42dd-51e8-ba38-0260edf45659")}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 * as NFS from "node:fs";
@@ -32930,6 +32930,8 @@ const OXLINT_SPAWN_TIMEOUT_MS = 6e4;
32930
32930
  const NODE_COMPILE_CACHE_DIR_NAME = "node-compile-cache";
32931
32931
  const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
32932
32932
  const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
32933
+ const ABORT_EXIT_CODES = new Set([134, 3221226505]);
32934
+ const OXLINT_OOM_RESCUE_BUDGET_MS = 6e4;
32933
32935
  const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
32934
32936
  const LINT_PHASE_TIMEOUT_MS = 3e5;
32935
32937
  const SCAN_TOTAL_DEADLINE_MS = 9e5;
@@ -40359,7 +40361,9 @@ const SANITIZED_ENV = buildOxlintChildEnv(process.env);
40359
40361
  * - `OxlintBatchExceeded { kind: "output-too-large" }` — stdout+stderr
40360
40362
  * crossed `OXLINT_OUTPUT_MAX_BYTES`.
40361
40363
  * - `OxlintBatchExceeded { kind: "oom" | "killed" }` — child exited
40362
- * on a signal (SIGABRT → OOM, others → generic kill).
40364
+ * on a signal (SIGABRT → OOM, others → generic kill), or with one
40365
+ * of the abort exit codes Windows reports instead of a signal
40366
+ * (`ABORT_EXIT_CODES` → OOM).
40363
40367
  * - `OxlintSpawnFailed { cause }` — `spawn` itself errored, or the
40364
40368
  * child exited successfully but printed only stderr.
40365
40369
  *
@@ -40425,7 +40429,7 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
40425
40429
  clearAbortListener();
40426
40430
  reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: error }) }));
40427
40431
  });
40428
- child.on("close", (_code, signal) => {
40432
+ child.on("close", (code, signal) => {
40429
40433
  clearTimeout(timeoutHandle);
40430
40434
  clearAbortListener();
40431
40435
  if (didKillForSize) {
@@ -40435,10 +40439,11 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
40435
40439
  }) }));
40436
40440
  return;
40437
40441
  }
40438
- if (signal) {
40442
+ const isAbortExitCode = code !== null && ABORT_EXIT_CODES.has(code);
40443
+ if (signal || isAbortExitCode) {
40439
40444
  const stderrOutput = Buffer.concat(stderrBuffers).toString("utf-8").trim();
40440
- const isOom = signal === "SIGABRT";
40441
- const detailParts = [`killed by ${signal}`];
40445
+ const isOom = signal === "SIGABRT" || isAbortExitCode;
40446
+ const detailParts = [signal ? `killed by ${signal}` : `aborted with exit code ${code}`];
40442
40447
  if (isOom) detailParts.push("try scanning fewer files with --diff");
40443
40448
  if (stderrOutput) detailParts.push(stderrOutput);
40444
40449
  reject(new ReactDoctorError({ reason: new OxlintBatchExceeded({
@@ -40481,12 +40486,19 @@ const isParallelismRelatedSpawnError = (error) => {
40481
40486
  * (surfaced via `onPartialFailure`) so JSON-mode consumers see WHICH
40482
40487
  * files were skipped instead of silently losing them.
40483
40488
  *
40484
- * Parallel runs (concurrency > 1) get one extra safety net: if the pass
40485
- * fails with a resource-exhaustion error that's exclusive to running
40486
- * many oxlint subprocesses at once (EAGAIN / EMFILE / ENFILE / ENOMEM —
40487
- * see `isParallelismRelatedSpawnError`), the whole pass replays once
40488
- * with a single worker. That's the only failure a serial replay can
40489
- * clear, so every other error class is left to propagate.
40489
+ * Parallel runs (concurrency > 1) get two extra safety nets:
40490
+ * - If the pass fails with a resource-exhaustion error that's exclusive
40491
+ * to running many oxlint subprocesses at once (EAGAIN / EMFILE /
40492
+ * ENFILE / ENOMEM — see `isParallelismRelatedSpawnError`), the whole
40493
+ * pass replays once with a single worker. That's the only failure a
40494
+ * serial replay can clear, so every other error class is left to
40495
+ * propagate.
40496
+ * - Files dropped because oxlint's native binding SIGABRT'd under
40497
+ * memory pressure (`OxlintBatchExceeded { kind: "oom" }`) get one
40498
+ * serial single-file-batch rescue pass: the OOM is usually a function
40499
+ * of N concurrent oxlint allocator arenas, not the file itself, so a
40500
+ * lone process with the machine to itself typically clears it —
40501
+ * turning a partial scan into a complete (if slower) one.
40490
40502
  *
40491
40503
  * Errors that aren't splittable and aren't parallelism-related (oxlint
40492
40504
  * config crash, JS plugin resolution failure, etc.) propagate to the
@@ -40496,13 +40508,16 @@ const isParallelismRelatedSpawnError = (error) => {
40496
40508
  const spawnLintBatches = async (input) => {
40497
40509
  const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal, deadlineEpochMs } = input;
40498
40510
  const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
40499
- const totalFileCount = fileBatches.reduce((sum, batch) => sum + batch.length, 0);
40500
- const runBatchPass = async (concurrency) => {
40511
+ let rescueDeadlineEpochMs;
40512
+ const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0 || rescueDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(rescueDeadlineEpochMs) === 0;
40513
+ const runBatchPass = async (concurrency, passBatches, passOnFileProgress) => {
40514
+ const totalFileCount = passBatches.reduce((sum, batch) => sum + batch.length, 0);
40501
40515
  const allDiagnostics = [];
40502
40516
  const droppedFiles = [];
40517
+ const oomDroppedFiles = [];
40503
40518
  const deadlineSkippedFiles = [];
40504
- const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0;
40505
40519
  let firstDropReason = null;
40520
+ let firstNonOomDropReason = null;
40506
40521
  const spawnLintBatch = async (batch, depth, batchState) => {
40507
40522
  if (isPastDeadline()) {
40508
40523
  deadlineSkippedFiles.push(...batch);
@@ -40524,12 +40539,14 @@ const spawnLintBatches = async (input) => {
40524
40539
  const isDepthCapReached = depth >= splitMaxDepth;
40525
40540
  if (batch.length <= 1 || isBudgetElapsed || isDepthCapReached) {
40526
40541
  droppedFiles.push(...batch);
40527
- if (firstDropReason === null) {
40528
- let limitHint = "";
40529
- if (isDepthCapReached) limitHint = ` (split depth cap of ${splitMaxDepth} levels reached)`;
40530
- else if (isBudgetElapsed) limitHint = ` (split budget of ${splitTotalBudgetMs / MILLISECONDS_PER_SECOND}s exhausted at depth ${depth})`;
40531
- firstDropReason = batch.length > 1 ? `${error.message}${limitHint}` : error.message;
40532
- }
40542
+ const isOomDrop = error.reason._tag === "OxlintBatchExceeded" && error.reason.kind === "oom";
40543
+ if (isOomDrop) oomDroppedFiles.push(...batch);
40544
+ let limitHint = "";
40545
+ if (isDepthCapReached) limitHint = ` (split depth cap of ${splitMaxDepth} levels reached)`;
40546
+ else if (isBudgetElapsed) limitHint = ` (split budget of ${splitTotalBudgetMs / MILLISECONDS_PER_SECOND}s exhausted at depth ${depth})`;
40547
+ const dropReason = batch.length > 1 ? `${error.message}${limitHint}` : error.message;
40548
+ firstDropReason ??= dropReason;
40549
+ if (!isOomDrop) firstNonOomDropReason ??= dropReason;
40533
40550
  return [];
40534
40551
  }
40535
40552
  const splitIndex = Math.ceil(batch.length / 2);
@@ -40539,16 +40556,16 @@ const spawnLintBatches = async (input) => {
40539
40556
  let startedFileCount = 0;
40540
40557
  let scannedFileCount = 0;
40541
40558
  let displayedFileCount = 0;
40542
- const progressTimer = onFileProgress && totalFileCount > 1 ? setInterval(() => {
40559
+ const progressTimer = passOnFileProgress && totalFileCount > 1 ? setInterval(() => {
40543
40560
  const ceiling = Math.min(startedFileCount, totalFileCount - 1);
40544
40561
  if (displayedFileCount < ceiling) {
40545
40562
  displayedFileCount += 1;
40546
- onFileProgress(displayedFileCount, totalFileCount);
40563
+ passOnFileProgress(displayedFileCount, totalFileCount);
40547
40564
  }
40548
40565
  }, 50) : null;
40549
40566
  progressTimer?.unref?.();
40550
40567
  try {
40551
- const batchResults = await mapWithConcurrency(fileBatches, concurrency, async (batch) => {
40568
+ const batchResults = await mapWithConcurrency(passBatches, concurrency, async (batch) => {
40552
40569
  if (isPastDeadline()) {
40553
40570
  deadlineSkippedFiles.push(...batch);
40554
40571
  return [];
@@ -40560,9 +40577,9 @@ const spawnLintBatches = async (input) => {
40560
40577
  };
40561
40578
  const batchDiagnostics = await spawnLintBatch(batch, 0, batchState);
40562
40579
  scannedFileCount += batch.length - batchState.deadlineSkippedFileCount;
40563
- if (onFileProgress) {
40580
+ if (passOnFileProgress) {
40564
40581
  displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
40565
- onFileProgress(displayedFileCount, totalFileCount);
40582
+ passOnFileProgress(displayedFileCount, totalFileCount);
40566
40583
  }
40567
40584
  return batchDiagnostics;
40568
40585
  });
@@ -40570,22 +40587,53 @@ const spawnLintBatches = async (input) => {
40570
40587
  } finally {
40571
40588
  if (progressTimer !== null) clearInterval(progressTimer);
40572
40589
  }
40573
- const reportSkippedFiles = (skippedFiles, buildMessage) => {
40574
- if (skippedFiles.length === 0 || !onPartialFailure) return;
40575
- onPartialFailure(buildMessage(`${skippedFiles.slice(0, 3).join(", ")}${skippedFiles.length > 3 ? `, +${skippedFiles.length - 3} more` : ""}`));
40590
+ return {
40591
+ diagnostics: allDiagnostics,
40592
+ droppedFiles,
40593
+ oomDroppedFiles,
40594
+ deadlineSkippedFiles,
40595
+ firstDropReason,
40596
+ firstNonOomDropReason
40576
40597
  };
40577
- const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
40578
- reportSkippedFiles(droppedFiles, (fileListPreview) => `${droppedFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
40579
- reportSkippedFiles(deadlineSkippedFiles, (fileListPreview) => `${deadlineSkippedFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
40580
- return allDiagnostics;
40581
40598
  };
40582
- let diagnostics;
40599
+ let outcome;
40600
+ let effectiveConcurrency = requestedConcurrency;
40583
40601
  try {
40584
- diagnostics = await runBatchPass(requestedConcurrency);
40602
+ outcome = await runBatchPass(requestedConcurrency, fileBatches, onFileProgress);
40585
40603
  } catch (error) {
40586
40604
  if (requestedConcurrency <= 1 || !isParallelismRelatedSpawnError(error)) throw error;
40587
- diagnostics = await runBatchPass(1);
40605
+ effectiveConcurrency = 1;
40606
+ outcome = await runBatchPass(1, fileBatches, onFileProgress);
40607
+ }
40608
+ let { droppedFiles, firstDropReason } = outcome;
40609
+ const diagnostics = outcome.diagnostics;
40610
+ if (outcome.oomDroppedFiles.length > 0 && effectiveConcurrency > 1 && !isPastDeadline()) {
40611
+ rescueDeadlineEpochMs = Date.now() + OXLINT_OOM_RESCUE_BUDGET_MS;
40612
+ const rescuedFiles = /* @__PURE__ */ new Set();
40613
+ let firstRescueFailureReason = null;
40614
+ for (const oomDroppedFile of outcome.oomDroppedFiles) {
40615
+ if (isPastDeadline()) break;
40616
+ try {
40617
+ const rescueOutcome = await runBatchPass(1, [[oomDroppedFile]], void 0);
40618
+ diagnostics.push(...rescueOutcome.diagnostics);
40619
+ if (rescueOutcome.droppedFiles.length > 0 || rescueOutcome.deadlineSkippedFiles.length > 0) firstRescueFailureReason ??= rescueOutcome.firstDropReason;
40620
+ else rescuedFiles.add(oomDroppedFile);
40621
+ } catch (error) {
40622
+ firstRescueFailureReason ??= error instanceof Error ? error.message : String(error);
40623
+ }
40624
+ }
40625
+ rescueDeadlineEpochMs = void 0;
40626
+ droppedFiles = droppedFiles.filter((filePath) => !rescuedFiles.has(filePath));
40627
+ if (droppedFiles.length === 0) firstDropReason = null;
40628
+ else firstDropReason = firstRescueFailureReason ?? outcome.firstNonOomDropReason ?? firstDropReason;
40588
40629
  }
40630
+ const reportSkippedFiles = (skippedFiles, buildMessage) => {
40631
+ if (skippedFiles.length === 0 || !onPartialFailure) return;
40632
+ onPartialFailure(buildMessage(`${skippedFiles.slice(0, 3).join(", ")}${skippedFiles.length > 3 ? `, +${skippedFiles.length - 3} more` : ""}`));
40633
+ };
40634
+ const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
40635
+ reportSkippedFiles(droppedFiles, (fileListPreview) => `${droppedFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
40636
+ reportSkippedFiles(outcome.deadlineSkippedFiles, (fileListPreview) => `${outcome.deadlineSkippedFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
40589
40637
  return dedupeDiagnostics(diagnostics);
40590
40638
  };
40591
40639
  let didValidate = false;
@@ -42925,4 +42973,4 @@ const toJsonReport = (result, options) => buildJsonReport({
42925
42973
  export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
42926
42974
 
42927
42975
  //# sourceMappingURL=index.js.map
42928
- //# debugId=63c65851-4fa5-5cd1-9337-0f2feefe37ad
42976
+ //# debugId=3dc4a1ac-42dd-51e8-ba38-0260edf45659
package/dist/lsp.js CHANGED
@@ -32989,6 +32989,8 @@ const OXLINT_SPAWN_TIMEOUT_MS = 6e4;
32989
32989
  const NODE_COMPILE_CACHE_DIR_NAME = "node-compile-cache";
32990
32990
  const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
32991
32991
  const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
32992
+ const ABORT_EXIT_CODES = new Set([134, 3221226505]);
32993
+ const OXLINT_OOM_RESCUE_BUDGET_MS = 6e4;
32992
32994
  const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
32993
32995
  const LINT_PHASE_TIMEOUT_MS = 3e5;
32994
32996
  const SCAN_TOTAL_DEADLINE_MS = 9e5;
@@ -40344,7 +40346,9 @@ const SANITIZED_ENV = buildOxlintChildEnv(process.env);
40344
40346
  * - `OxlintBatchExceeded { kind: "output-too-large" }` — stdout+stderr
40345
40347
  * crossed `OXLINT_OUTPUT_MAX_BYTES`.
40346
40348
  * - `OxlintBatchExceeded { kind: "oom" | "killed" }` — child exited
40347
- * on a signal (SIGABRT → OOM, others → generic kill).
40349
+ * on a signal (SIGABRT → OOM, others → generic kill), or with one
40350
+ * of the abort exit codes Windows reports instead of a signal
40351
+ * (`ABORT_EXIT_CODES` → OOM).
40348
40352
  * - `OxlintSpawnFailed { cause }` — `spawn` itself errored, or the
40349
40353
  * child exited successfully but printed only stderr.
40350
40354
  *
@@ -40410,7 +40414,7 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
40410
40414
  clearAbortListener();
40411
40415
  reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: error }) }));
40412
40416
  });
40413
- child.on("close", (_code, signal) => {
40417
+ child.on("close", (code, signal) => {
40414
40418
  clearTimeout(timeoutHandle);
40415
40419
  clearAbortListener();
40416
40420
  if (didKillForSize) {
@@ -40420,10 +40424,11 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
40420
40424
  }) }));
40421
40425
  return;
40422
40426
  }
40423
- if (signal) {
40427
+ const isAbortExitCode = code !== null && ABORT_EXIT_CODES.has(code);
40428
+ if (signal || isAbortExitCode) {
40424
40429
  const stderrOutput = Buffer.concat(stderrBuffers).toString("utf-8").trim();
40425
- const isOom = signal === "SIGABRT";
40426
- const detailParts = [`killed by ${signal}`];
40430
+ const isOom = signal === "SIGABRT" || isAbortExitCode;
40431
+ const detailParts = [signal ? `killed by ${signal}` : `aborted with exit code ${code}`];
40427
40432
  if (isOom) detailParts.push("try scanning fewer files with --diff");
40428
40433
  if (stderrOutput) detailParts.push(stderrOutput);
40429
40434
  reject(new ReactDoctorError({ reason: new OxlintBatchExceeded({
@@ -40466,12 +40471,19 @@ const isParallelismRelatedSpawnError = (error) => {
40466
40471
  * (surfaced via `onPartialFailure`) so JSON-mode consumers see WHICH
40467
40472
  * files were skipped instead of silently losing them.
40468
40473
  *
40469
- * Parallel runs (concurrency > 1) get one extra safety net: if the pass
40470
- * fails with a resource-exhaustion error that's exclusive to running
40471
- * many oxlint subprocesses at once (EAGAIN / EMFILE / ENFILE / ENOMEM —
40472
- * see `isParallelismRelatedSpawnError`), the whole pass replays once
40473
- * with a single worker. That's the only failure a serial replay can
40474
- * clear, so every other error class is left to propagate.
40474
+ * Parallel runs (concurrency > 1) get two extra safety nets:
40475
+ * - If the pass fails with a resource-exhaustion error that's exclusive
40476
+ * to running many oxlint subprocesses at once (EAGAIN / EMFILE /
40477
+ * ENFILE / ENOMEM — see `isParallelismRelatedSpawnError`), the whole
40478
+ * pass replays once with a single worker. That's the only failure a
40479
+ * serial replay can clear, so every other error class is left to
40480
+ * propagate.
40481
+ * - Files dropped because oxlint's native binding SIGABRT'd under
40482
+ * memory pressure (`OxlintBatchExceeded { kind: "oom" }`) get one
40483
+ * serial single-file-batch rescue pass: the OOM is usually a function
40484
+ * of N concurrent oxlint allocator arenas, not the file itself, so a
40485
+ * lone process with the machine to itself typically clears it —
40486
+ * turning a partial scan into a complete (if slower) one.
40475
40487
  *
40476
40488
  * Errors that aren't splittable and aren't parallelism-related (oxlint
40477
40489
  * config crash, JS plugin resolution failure, etc.) propagate to the
@@ -40481,13 +40493,16 @@ const isParallelismRelatedSpawnError = (error) => {
40481
40493
  const spawnLintBatches = async (input) => {
40482
40494
  const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal, deadlineEpochMs } = input;
40483
40495
  const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
40484
- const totalFileCount = fileBatches.reduce((sum, batch) => sum + batch.length, 0);
40485
- const runBatchPass = async (concurrency) => {
40496
+ let rescueDeadlineEpochMs;
40497
+ const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0 || rescueDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(rescueDeadlineEpochMs) === 0;
40498
+ const runBatchPass = async (concurrency, passBatches, passOnFileProgress) => {
40499
+ const totalFileCount = passBatches.reduce((sum, batch) => sum + batch.length, 0);
40486
40500
  const allDiagnostics = [];
40487
40501
  const droppedFiles = [];
40502
+ const oomDroppedFiles = [];
40488
40503
  const deadlineSkippedFiles = [];
40489
- const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0;
40490
40504
  let firstDropReason = null;
40505
+ let firstNonOomDropReason = null;
40491
40506
  const spawnLintBatch = async (batch, depth, batchState) => {
40492
40507
  if (isPastDeadline()) {
40493
40508
  deadlineSkippedFiles.push(...batch);
@@ -40509,12 +40524,14 @@ const spawnLintBatches = async (input) => {
40509
40524
  const isDepthCapReached = depth >= splitMaxDepth;
40510
40525
  if (batch.length <= 1 || isBudgetElapsed || isDepthCapReached) {
40511
40526
  droppedFiles.push(...batch);
40512
- if (firstDropReason === null) {
40513
- let limitHint = "";
40514
- if (isDepthCapReached) limitHint = ` (split depth cap of ${splitMaxDepth} levels reached)`;
40515
- else if (isBudgetElapsed) limitHint = ` (split budget of ${splitTotalBudgetMs / MILLISECONDS_PER_SECOND}s exhausted at depth ${depth})`;
40516
- firstDropReason = batch.length > 1 ? `${error.message}${limitHint}` : error.message;
40517
- }
40527
+ const isOomDrop = error.reason._tag === "OxlintBatchExceeded" && error.reason.kind === "oom";
40528
+ if (isOomDrop) oomDroppedFiles.push(...batch);
40529
+ let limitHint = "";
40530
+ if (isDepthCapReached) limitHint = ` (split depth cap of ${splitMaxDepth} levels reached)`;
40531
+ else if (isBudgetElapsed) limitHint = ` (split budget of ${splitTotalBudgetMs / MILLISECONDS_PER_SECOND}s exhausted at depth ${depth})`;
40532
+ const dropReason = batch.length > 1 ? `${error.message}${limitHint}` : error.message;
40533
+ firstDropReason ??= dropReason;
40534
+ if (!isOomDrop) firstNonOomDropReason ??= dropReason;
40518
40535
  return [];
40519
40536
  }
40520
40537
  const splitIndex = Math.ceil(batch.length / 2);
@@ -40524,16 +40541,16 @@ const spawnLintBatches = async (input) => {
40524
40541
  let startedFileCount = 0;
40525
40542
  let scannedFileCount = 0;
40526
40543
  let displayedFileCount = 0;
40527
- const progressTimer = onFileProgress && totalFileCount > 1 ? setInterval(() => {
40544
+ const progressTimer = passOnFileProgress && totalFileCount > 1 ? setInterval(() => {
40528
40545
  const ceiling = Math.min(startedFileCount, totalFileCount - 1);
40529
40546
  if (displayedFileCount < ceiling) {
40530
40547
  displayedFileCount += 1;
40531
- onFileProgress(displayedFileCount, totalFileCount);
40548
+ passOnFileProgress(displayedFileCount, totalFileCount);
40532
40549
  }
40533
40550
  }, 50) : null;
40534
40551
  progressTimer?.unref?.();
40535
40552
  try {
40536
- const batchResults = await mapWithConcurrency(fileBatches, concurrency, async (batch) => {
40553
+ const batchResults = await mapWithConcurrency(passBatches, concurrency, async (batch) => {
40537
40554
  if (isPastDeadline()) {
40538
40555
  deadlineSkippedFiles.push(...batch);
40539
40556
  return [];
@@ -40545,9 +40562,9 @@ const spawnLintBatches = async (input) => {
40545
40562
  };
40546
40563
  const batchDiagnostics = await spawnLintBatch(batch, 0, batchState);
40547
40564
  scannedFileCount += batch.length - batchState.deadlineSkippedFileCount;
40548
- if (onFileProgress) {
40565
+ if (passOnFileProgress) {
40549
40566
  displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
40550
- onFileProgress(displayedFileCount, totalFileCount);
40567
+ passOnFileProgress(displayedFileCount, totalFileCount);
40551
40568
  }
40552
40569
  return batchDiagnostics;
40553
40570
  });
@@ -40555,22 +40572,53 @@ const spawnLintBatches = async (input) => {
40555
40572
  } finally {
40556
40573
  if (progressTimer !== null) clearInterval(progressTimer);
40557
40574
  }
40558
- const reportSkippedFiles = (skippedFiles, buildMessage) => {
40559
- if (skippedFiles.length === 0 || !onPartialFailure) return;
40560
- onPartialFailure(buildMessage(`${skippedFiles.slice(0, 3).join(", ")}${skippedFiles.length > 3 ? `, +${skippedFiles.length - 3} more` : ""}`));
40575
+ return {
40576
+ diagnostics: allDiagnostics,
40577
+ droppedFiles,
40578
+ oomDroppedFiles,
40579
+ deadlineSkippedFiles,
40580
+ firstDropReason,
40581
+ firstNonOomDropReason
40561
40582
  };
40562
- const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
40563
- reportSkippedFiles(droppedFiles, (fileListPreview) => `${droppedFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
40564
- reportSkippedFiles(deadlineSkippedFiles, (fileListPreview) => `${deadlineSkippedFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
40565
- return allDiagnostics;
40566
40583
  };
40567
- let diagnostics;
40584
+ let outcome;
40585
+ let effectiveConcurrency = requestedConcurrency;
40568
40586
  try {
40569
- diagnostics = await runBatchPass(requestedConcurrency);
40587
+ outcome = await runBatchPass(requestedConcurrency, fileBatches, onFileProgress);
40570
40588
  } catch (error) {
40571
40589
  if (requestedConcurrency <= 1 || !isParallelismRelatedSpawnError(error)) throw error;
40572
- diagnostics = await runBatchPass(1);
40590
+ effectiveConcurrency = 1;
40591
+ outcome = await runBatchPass(1, fileBatches, onFileProgress);
40592
+ }
40593
+ let { droppedFiles, firstDropReason } = outcome;
40594
+ const diagnostics = outcome.diagnostics;
40595
+ if (outcome.oomDroppedFiles.length > 0 && effectiveConcurrency > 1 && !isPastDeadline()) {
40596
+ rescueDeadlineEpochMs = Date.now() + OXLINT_OOM_RESCUE_BUDGET_MS;
40597
+ const rescuedFiles = /* @__PURE__ */ new Set();
40598
+ let firstRescueFailureReason = null;
40599
+ for (const oomDroppedFile of outcome.oomDroppedFiles) {
40600
+ if (isPastDeadline()) break;
40601
+ try {
40602
+ const rescueOutcome = await runBatchPass(1, [[oomDroppedFile]], void 0);
40603
+ diagnostics.push(...rescueOutcome.diagnostics);
40604
+ if (rescueOutcome.droppedFiles.length > 0 || rescueOutcome.deadlineSkippedFiles.length > 0) firstRescueFailureReason ??= rescueOutcome.firstDropReason;
40605
+ else rescuedFiles.add(oomDroppedFile);
40606
+ } catch (error) {
40607
+ firstRescueFailureReason ??= error instanceof Error ? error.message : String(error);
40608
+ }
40609
+ }
40610
+ rescueDeadlineEpochMs = void 0;
40611
+ droppedFiles = droppedFiles.filter((filePath) => !rescuedFiles.has(filePath));
40612
+ if (droppedFiles.length === 0) firstDropReason = null;
40613
+ else firstDropReason = firstRescueFailureReason ?? outcome.firstNonOomDropReason ?? firstDropReason;
40573
40614
  }
40615
+ const reportSkippedFiles = (skippedFiles, buildMessage) => {
40616
+ if (skippedFiles.length === 0 || !onPartialFailure) return;
40617
+ onPartialFailure(buildMessage(`${skippedFiles.slice(0, 3).join(", ")}${skippedFiles.length > 3 ? `, +${skippedFiles.length - 3} more` : ""}`));
40618
+ };
40619
+ const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
40620
+ reportSkippedFiles(droppedFiles, (fileListPreview) => `${droppedFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
40621
+ reportSkippedFiles(outcome.deadlineSkippedFiles, (fileListPreview) => `${outcome.deadlineSkippedFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
40574
40622
  return dedupeDiagnostics(diagnostics);
40575
40623
  };
40576
40624
  let didValidate = false;
@@ -44615,5 +44663,5 @@ const startLanguageServer = () => {
44615
44663
  };
44616
44664
  //#endregion
44617
44665
  export { startLanguageServer };
44618
- !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]="94aee89d-9f35-5b59-a911-566ac2ef5a0a")}catch(e){}}();
44619
- //# debugId=94aee89d-9f35-5b59-a911-566ac2ef5a0a
44666
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="fff2fb6a-e10e-51f4-b9c3-f531f8dab288")}catch(e){}}();
44667
+ //# debugId=fff2fb6a-e10e-51f4-b9c3-f531f8dab288
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor",
3
- "version": "0.7.2-dev.82e0475",
3
+ "version": "0.7.2-dev.9b59d96",
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.2",
69
- "oxlint-plugin-react-doctor": "0.7.2-dev.82e0475"
68
+ "oxlint-plugin-react-doctor": "0.7.2-dev.9b59d96",
69
+ "deslop-js": "0.7.2"
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.2",
77
+ "@react-doctor/core": "0.7.2",
78
78
  "@react-doctor/language-server": "0.7.2",
79
- "@react-doctor/core": "0.7.2"
79
+ "@react-doctor/api": "0.7.2"
80
80
  },
81
81
  "engines": {
82
82
  "node": "^20.19.0 || >=22.13.0"