Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support async formatter #15243

Merged
merged 12 commits into from Nov 24, 2021
2 changes: 1 addition & 1 deletion docs/developer-guide/nodejs-api.md
Expand Up @@ -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`)<br>
* `format` (`(results: LintResult[]) => string | Promise<string>`)<br>
The method to convert the [LintResult] objects to text.

---
Expand Down
10 changes: 10 additions & 0 deletions docs/developer-guide/working-with-custom-formatters.md
Expand Up @@ -11,6 +11,16 @@ module.exports = function(results) {
};
```

Formatter can also be an async function (from ESLint 8.2.0), the following shows a simple example:
fengzilong marked this conversation as resolved.
Show resolved Hide resolved

```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
Expand Down
2 changes: 1 addition & 1 deletion lib/cli.js
Expand Up @@ -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) {
Expand Down
9 changes: 7 additions & 2 deletions lib/eslint/eslint.js
Expand Up @@ -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<string>} [format] format function.
fengzilong marked this conversation as resolved.
Show resolved Hide resolved
*/

/**
* The options with which to configure the ESLint instance.
Expand Down Expand Up @@ -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<string>} The formatted lint results.
*/
format(results) {
let rulesMeta = null;
Expand Down
4 changes: 4 additions & 0 deletions tests/fixtures/formatters/async.js
@@ -0,0 +1,4 @@
/*global module*/
module.exports = function(results) {
return Promise.resolve('from async formatter');
};
10 changes: 7 additions & 3 deletions tests/lib/cli-engine/cli-engine.js
Expand Up @@ -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);
Expand Down Expand Up @@ -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", () => {
Expand Down
11 changes: 11 additions & 0 deletions tests/lib/cli.js
Expand Up @@ -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");
Expand Down
2 changes: 1 addition & 1 deletion tests/lib/eslint/eslint.js
Expand Up @@ -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);
Expand Down