From f1b7499a5162d3be918328ce496eb80692353a5a Mon Sep 17 00:00:00 2001 From: MO <9125255+fengzilong@users.noreply.github.com> Date: Wed, 24 Nov 2021 21:09:23 +0800 Subject: [PATCH] feat: support async formatters (#15243) * feat: support async formatter Fixes #15242 * test: add test for async formatter * test: tweak async formatter fixture * docs: support async formatter * docs: fix formatter type * test: move async formatter to formatters * chore: add Formatter typedef * style: remove extra space * docs: update ESLint version for async formatter Co-authored-by: Milos Djermanovic * chore: format is not optional Co-authored-by: Milos Djermanovic * Update docs/developer-guide/working-with-custom-formatters.md Co-authored-by: Milos Djermanovic Co-authored-by: Nicholas C. Zakas --- docs/developer-guide/nodejs-api.md | 2 +- .../developer-guide/working-with-custom-formatters.md | 10 ++++++++++ lib/cli.js | 2 +- lib/eslint/eslint.js | 9 +++++++-- tests/fixtures/formatters/async.js | 4 ++++ tests/lib/cli-engine/cli-engine.js | 10 +++++++--- tests/lib/cli.js | 11 +++++++++++ tests/lib/eslint/eslint.js | 2 +- 8 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 tests/fixtures/formatters/async.js diff --git a/docs/developer-guide/nodejs-api.md b/docs/developer-guide/nodejs-api.md index 378b9c16b8d..e4a3a1e42c1 100644 --- a/docs/developer-guide/nodejs-api.md +++ b/docs/developer-guide/nodejs-api.md @@ -404,7 +404,7 @@ This edit information means replacing the range of the `range` property by the ` The `Formatter` value is the object to convert the [LintResult] objects to text. The [eslint.loadFormatter()][eslint-loadformatter] method returns it. It has the following method: -* `format` (`(results: LintResult[]) => string`)
+* `format` (`(results: LintResult[]) => string | Promise`)
The method to convert the [LintResult] objects to text. --- diff --git a/docs/developer-guide/working-with-custom-formatters.md b/docs/developer-guide/working-with-custom-formatters.md index a5e9ade81bf..181aa69cec5 100644 --- a/docs/developer-guide/working-with-custom-formatters.md +++ b/docs/developer-guide/working-with-custom-formatters.md @@ -11,6 +11,16 @@ module.exports = function(results) { }; ``` +Formatter can also be an async function (from ESLint v8.4.0), the following shows a simple example: + +```js +//my-awesome-formatter.js +module.exports = async function(results) { + const formatted = await asyncTask(); + return formatted; +}; +``` + To run ESLint with this formatter, you can use the `-f` (or `--format`) command line flag: ```bash diff --git a/lib/cli.js b/lib/cli.js index 477310da585..f09d143d255 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -178,7 +178,7 @@ async function printResults(engine, results, format, outputFile) { return false; } - const output = formatter.format(results); + const output = await formatter.format(results); if (output) { if (outputFile) { diff --git a/lib/eslint/eslint.js b/lib/eslint/eslint.js index 8886d45ab43..29d7e5cbc92 100644 --- a/lib/eslint/eslint.js +++ b/lib/eslint/eslint.js @@ -34,7 +34,12 @@ const { version } = require("../../package.json"); /** @typedef {import("../shared/types").LintMessage} LintMessage */ /** @typedef {import("../shared/types").Plugin} Plugin */ /** @typedef {import("../shared/types").Rule} Rule */ -/** @typedef {import("./load-formatter").Formatter} Formatter */ + +/** + * The main formatter object. + * @typedef Formatter + * @property {function(LintResult[]): string | Promise} format format function. + */ /** * The options with which to configure the ESLint instance. @@ -629,7 +634,7 @@ class ESLint { /** * The main formatter method. * @param {LintResults[]} results The lint results to format. - * @returns {string} The formatted lint results. + * @returns {string | Promise} The formatted lint results. */ format(results) { let rulesMeta = null; diff --git a/tests/fixtures/formatters/async.js b/tests/fixtures/formatters/async.js new file mode 100644 index 00000000000..b5651a697a4 --- /dev/null +++ b/tests/fixtures/formatters/async.js @@ -0,0 +1,4 @@ +/*global module*/ +module.exports = function(results) { + return Promise.resolve('from async formatter'); +}; diff --git a/tests/lib/cli-engine/cli-engine.js b/tests/lib/cli-engine/cli-engine.js index 31f59bf51b4..16beb92b87b 100644 --- a/tests/lib/cli-engine/cli-engine.js +++ b/tests/lib/cli-engine/cli-engine.js @@ -1160,7 +1160,7 @@ describe("CLIEngine", () => { const report = engine.executeOnFiles([getFixturePath("formatters")]); - assert.strictEqual(report.results.length, 3); + assert.strictEqual(report.results.length, 4); assert.strictEqual(report.errorCount, 0); assert.strictEqual(report.warningCount, 0); assert.strictEqual(report.fixableErrorCount, 0); @@ -1200,14 +1200,18 @@ describe("CLIEngine", () => { assert.strictEqual(report.results[0].warningCount, 0); assert.strictEqual(report.results[0].fixableErrorCount, 0); assert.strictEqual(report.results[0].fixableWarningCount, 0); - assert.strictEqual(report.results[1].errorCount, 3); + assert.strictEqual(report.results[1].errorCount, 0); assert.strictEqual(report.results[1].warningCount, 0); - assert.strictEqual(report.results[1].fixableErrorCount, 3); + assert.strictEqual(report.results[1].fixableErrorCount, 0); assert.strictEqual(report.results[1].fixableWarningCount, 0); assert.strictEqual(report.results[2].errorCount, 3); assert.strictEqual(report.results[2].warningCount, 0); assert.strictEqual(report.results[2].fixableErrorCount, 3); assert.strictEqual(report.results[2].fixableWarningCount, 0); + assert.strictEqual(report.results[3].errorCount, 3); + assert.strictEqual(report.results[3].warningCount, 0); + assert.strictEqual(report.results[3].fixableErrorCount, 3); + assert.strictEqual(report.results[3].fixableWarningCount, 0); }); it("should process when file is given by not specifying extensions", () => { diff --git a/tests/lib/cli.js b/tests/lib/cli.js index 1b3828b4090..143a3ac1efc 100644 --- a/tests/lib/cli.js +++ b/tests/lib/cli.js @@ -287,6 +287,17 @@ describe("cli", () => { }); }); + describe("when given an async formatter path", () => { + it("should execute without any errors", async () => { + const formatterPath = getFixturePath("formatters", "async.js"); + const filePath = getFixturePath("passing.js"); + const exit = await cli.execute(`-f ${formatterPath} ${filePath}`); + + assert.strictEqual(log.info.getCall(0).args[0], "from async formatter"); + assert.strictEqual(exit, 0); + }); + }); + describe("when executing a file with a lint error", () => { it("should exit with error", async () => { const filePath = getFixturePath("undef.js"); diff --git a/tests/lib/eslint/eslint.js b/tests/lib/eslint/eslint.js index eaf4aaaaf05..d984f5a3082 100644 --- a/tests/lib/eslint/eslint.js +++ b/tests/lib/eslint/eslint.js @@ -1212,7 +1212,7 @@ describe("ESLint", () => { }); const results = await eslint.lintFiles([getFixturePath("formatters")]); - assert.strictEqual(results.length, 3); + assert.strictEqual(results.length, 4); assert.strictEqual(results[0].messages.length, 0); assert.strictEqual(results[1].messages.length, 0); assert.strictEqual(results[2].messages.length, 0);