diff --git a/docs/src/integrate/nodejs-api.md b/docs/src/integrate/nodejs-api.md
index 5d081df3d3b..cc01388f852 100644
--- a/docs/src/integrate/nodejs-api.md
+++ b/docs/src/integrate/nodejs-api.md
@@ -128,7 +128,7 @@ The `ESLint` constructor takes an `options` object. If you omit the `options` ob
* `options.globInputPaths` (`boolean`)
Default is `true`. If `false` is present, the [`eslint.lintFiles()`][eslint-lintfiles] method doesn't interpret glob patterns.
* `options.ignore` (`boolean`)
- Default is `true`. If `false` is present, the [`eslint.lintFiles()`][eslint-lintfiles] method doesn't respect `.eslintignore` files or `ignorePatterns` in your configuration.
+ Default is `true`. If `false` is present, the [`eslint.lintFiles()`][eslint-lintfiles] method doesn't respect `ignorePatterns` in your configuration.
* `options.ignorePatterns` (`string[] | null`)
Default is `null`. Ignore file patterns to use in addition to config ignores. These patterns are relative to `cwd`.
* `options.passOnNoPatterns` (`boolean`)
diff --git a/lib/eslint/eslint.js b/lib/eslint/eslint.js
index 5bb2e3fec66..af156526689 100644
--- a/lib/eslint/eslint.js
+++ b/lib/eslint/eslint.js
@@ -879,6 +879,7 @@ class ESLint {
configs,
errorOnUnmatchedPattern
});
+ const controller = new AbortController();
debug(`${filePaths.length} files found in: ${Date.now() - startTime}ms`);
@@ -947,9 +948,12 @@ class ESLint {
fixer = message => shouldMessageBeFixed(message, config, fixTypesSet) && originalFix(message);
}
- return fs.readFile(filePath, "utf8")
+ return fs.readFile(filePath, { encoding: "utf8", signal: controller.signal })
.then(text => {
+ // fail immediately if an error occurred in another file
+ controller.signal.throwIfAborted();
+
// do the linting
const result = verifyText({
text,
@@ -973,6 +977,9 @@ class ESLint {
}
return result;
+ }).catch(error => {
+ controller.abort(error);
+ throw error;
});
})
diff --git a/tests/lib/eslint/eslint.js b/tests/lib/eslint/eslint.js
index 9360d39449d..a00efc2afad 100644
--- a/tests/lib/eslint/eslint.js
+++ b/tests/lib/eslint/eslint.js
@@ -16,6 +16,7 @@ const fs = require("fs");
const fsp = fs.promises;
const os = require("os");
const path = require("path");
+const timers = require("node:timers/promises");
const escapeStringRegExp = require("escape-string-regexp");
const fCache = require("file-entry-cache");
const sinon = require("sinon");
@@ -4445,6 +4446,40 @@ describe("ESLint", () => {
});
});
+ it("should stop linting files if a rule crashes", async () => {
+
+ const cwd = getFixturePath("files");
+ let createCallCount = 0;
+
+ eslint = new ESLint({
+ cwd,
+ plugins: {
+ boom: {
+ rules: {
+ boom: {
+ create() {
+ createCallCount++;
+ throw Error("Boom!");
+ }
+ }
+ }
+ }
+ },
+ baseConfig: {
+ rules: {
+ "boom/boom": "error"
+ }
+ }
+ });
+
+ await assert.rejects(eslint.lintFiles("*.js"));
+
+ // Wait until all files have been closed.
+ while (process.getActiveResourcesInfo().includes("CloseReq")) {
+ await timers.setImmediate();
+ }
+ assert.strictEqual(createCallCount, 1);
+ });
});