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 +88 -40
- package/dist/index.js +85 -37
- package/dist/lsp.js +85 -37
- package/package.json +5 -5
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]="
|
|
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", (
|
|
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
|
-
|
|
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
|
|
43728
|
-
* fails with a resource-exhaustion error that's exclusive
|
|
43729
|
-
* many oxlint subprocesses at once (EAGAIN / EMFILE /
|
|
43730
|
-
* see `isParallelismRelatedSpawnError`), the whole
|
|
43731
|
-
* with a single worker. That's the only failure a
|
|
43732
|
-
* clear, so every other error class is left to
|
|
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
|
-
|
|
43743
|
-
const
|
|
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
|
-
|
|
43771
|
-
|
|
43772
|
-
|
|
43773
|
-
|
|
43774
|
-
|
|
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 =
|
|
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
|
-
|
|
43806
|
+
passOnFileProgress(displayedFileCount, totalFileCount);
|
|
43790
43807
|
}
|
|
43791
43808
|
}, 50) : null;
|
|
43792
43809
|
progressTimer?.unref?.();
|
|
43793
43810
|
try {
|
|
43794
|
-
const batchResults = await mapWithConcurrency(
|
|
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 (
|
|
43823
|
+
if (passOnFileProgress) {
|
|
43807
43824
|
displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
|
|
43808
|
-
|
|
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
|
-
|
|
43817
|
-
|
|
43818
|
-
|
|
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
|
|
43842
|
+
let outcome;
|
|
43843
|
+
let effectiveConcurrency = requestedConcurrency;
|
|
43826
43844
|
try {
|
|
43827
|
-
|
|
43845
|
+
outcome = await runBatchPass(requestedConcurrency, fileBatches, onFileProgress);
|
|
43828
43846
|
} catch (error) {
|
|
43829
43847
|
if (requestedConcurrency <= 1 || !isParallelismRelatedSpawnError(error)) throw error;
|
|
43830
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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=
|
|
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]="
|
|
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", (
|
|
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
|
-
|
|
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
|
|
40485
|
-
* fails with a resource-exhaustion error that's exclusive
|
|
40486
|
-
* many oxlint subprocesses at once (EAGAIN / EMFILE /
|
|
40487
|
-
* see `isParallelismRelatedSpawnError`), the whole
|
|
40488
|
-
* with a single worker. That's the only failure a
|
|
40489
|
-
* clear, so every other error class is left to
|
|
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
|
-
|
|
40500
|
-
const
|
|
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
|
-
|
|
40528
|
-
|
|
40529
|
-
|
|
40530
|
-
|
|
40531
|
-
|
|
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 =
|
|
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
|
-
|
|
40563
|
+
passOnFileProgress(displayedFileCount, totalFileCount);
|
|
40547
40564
|
}
|
|
40548
40565
|
}, 50) : null;
|
|
40549
40566
|
progressTimer?.unref?.();
|
|
40550
40567
|
try {
|
|
40551
|
-
const batchResults = await mapWithConcurrency(
|
|
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 (
|
|
40580
|
+
if (passOnFileProgress) {
|
|
40564
40581
|
displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
|
|
40565
|
-
|
|
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
|
-
|
|
40574
|
-
|
|
40575
|
-
|
|
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
|
|
40599
|
+
let outcome;
|
|
40600
|
+
let effectiveConcurrency = requestedConcurrency;
|
|
40583
40601
|
try {
|
|
40584
|
-
|
|
40602
|
+
outcome = await runBatchPass(requestedConcurrency, fileBatches, onFileProgress);
|
|
40585
40603
|
} catch (error) {
|
|
40586
40604
|
if (requestedConcurrency <= 1 || !isParallelismRelatedSpawnError(error)) throw error;
|
|
40587
|
-
|
|
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=
|
|
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", (
|
|
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
|
-
|
|
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
|
|
40470
|
-
* fails with a resource-exhaustion error that's exclusive
|
|
40471
|
-
* many oxlint subprocesses at once (EAGAIN / EMFILE /
|
|
40472
|
-
* see `isParallelismRelatedSpawnError`), the whole
|
|
40473
|
-
* with a single worker. That's the only failure a
|
|
40474
|
-
* clear, so every other error class is left to
|
|
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
|
-
|
|
40485
|
-
const
|
|
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
|
-
|
|
40513
|
-
|
|
40514
|
-
|
|
40515
|
-
|
|
40516
|
-
|
|
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 =
|
|
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
|
-
|
|
40548
|
+
passOnFileProgress(displayedFileCount, totalFileCount);
|
|
40532
40549
|
}
|
|
40533
40550
|
}, 50) : null;
|
|
40534
40551
|
progressTimer?.unref?.();
|
|
40535
40552
|
try {
|
|
40536
|
-
const batchResults = await mapWithConcurrency(
|
|
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 (
|
|
40565
|
+
if (passOnFileProgress) {
|
|
40549
40566
|
displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
|
|
40550
|
-
|
|
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
|
-
|
|
40559
|
-
|
|
40560
|
-
|
|
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
|
|
40584
|
+
let outcome;
|
|
40585
|
+
let effectiveConcurrency = requestedConcurrency;
|
|
40568
40586
|
try {
|
|
40569
|
-
|
|
40587
|
+
outcome = await runBatchPass(requestedConcurrency, fileBatches, onFileProgress);
|
|
40570
40588
|
} catch (error) {
|
|
40571
40589
|
if (requestedConcurrency <= 1 || !isParallelismRelatedSpawnError(error)) throw error;
|
|
40572
|
-
|
|
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]="
|
|
44619
|
-
//# debugId=
|
|
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.
|
|
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
|
-
"
|
|
69
|
-
"
|
|
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/
|
|
77
|
+
"@react-doctor/core": "0.7.2",
|
|
78
78
|
"@react-doctor/language-server": "0.7.2",
|
|
79
|
-
"@react-doctor/
|
|
79
|
+
"@react-doctor/api": "0.7.2"
|
|
80
80
|
},
|
|
81
81
|
"engines": {
|
|
82
82
|
"node": "^20.19.0 || >=22.13.0"
|