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); + }); });