]> git.proxmox.com Git - pve-eslint.git/blame - eslint/docs/developer-guide/working-with-custom-formatters.md
bump version to 8.4.0-3
[pve-eslint.git] / eslint / docs / developer-guide / working-with-custom-formatters.md
CommitLineData
eb39fafa
DC
1# Working with Custom Formatters
2
3While ESLint has some built-in formatters available to format the linting results, it's also possible to create and distribute your own custom formatters. You can include custom formatters in your project directly or create an npm package to distribute them separately.
4
5Each formatter is just a function that receives a `results` object and returns a string. For example, the following is how the `json` built-in formatter is implemented:
6
7```js
8//my-awesome-formatter.js
34eeec05 9module.exports = function(results, context) {
eb39fafa
DC
10 return JSON.stringify(results, null, 2);
11};
12```
13
34eeec05
TL
14Formatter can also be an async function (from ESLint v8.4.0), the following shows a simple example:
15
16```js
17//my-awesome-formatter.js
18module.exports = async function(results) {
19 const formatted = await asyncTask();
20 return formatted;
21};
22```
23
6f036462 24To run ESLint with this formatter, you can use the `-f` (or `--format`) command line flag:
eb39fafa
DC
25
26```bash
27eslint -f ./my-awesome-formatter.js src/
28```
29
30In order to use a local file as a custom formatter, you must begin the filename with a dot (such as `./my-awesome-formatter.js` or `../formatters/my-awesome-formatter.js`).
31
eb39fafa
DC
32## Packaging the Custom Formatter
33
6f036462 34Custom formatters can also be distributed through npm packages. To do so, create an npm package with a name in the format of `eslint-formatter-*`, where `*` is the name of your formatter (such as `eslint-formatter-awesome`). Projects should then install the package and can use the custom formatter with the `-f` (or `--format`) flag like this:
eb39fafa
DC
35
36```bash
37eslint -f awesome src/
38```
39
40Because ESLint knows to look for packages beginning with `eslint-formatter-` when the specified formatter doesn't begin with a dot, there is no need to type `eslint-formatter-` when using a packaged custom formatter.
41
42Tips for `package.json`:
43
44* The `main` entry should be the JavaScript file implementing your custom formatter.
45* Add these `keywords` to help users find your formatter:
46 * `"eslint"`
47 * `"eslint-formatter"`
48 * `"eslintformatter"`
49
50See all [formatters on npm](https://www.npmjs.com/search?q=eslint-formatter);
51
34eeec05 52## The `results` Argument
eb39fafa
DC
53
54The `results` object passed into a formatter is an array of objects containing the lint results for individual files. Here's some example output:
55
56```js
57[
58 {
34eeec05 59 filePath: "/path/to/a/file.js",
eb39fafa
DC
60 messages: [
61 {
62 ruleId: "curly",
63 severity: 2,
64 message: "Expected { after 'if' condition.",
65 line: 2,
66 column: 1,
67 nodeType: "IfStatement"
68 },
69 {
70 ruleId: "no-process-exit",
71 severity: 2,
72 message: "Don't use process.exit(); throw an error instead.",
73 line: 3,
74 column: 1,
75 nodeType: "CallExpression"
76 }
77 ],
78 errorCount: 2,
79 warningCount: 0,
80 fixableErrorCount: 0,
81 fixableWarningCount: 0,
82 source:
83 "var err = doStuff();\nif (err) console.log('failed tests: ' + err);\nprocess.exit(1);\n"
84 },
85 {
34eeec05 86 filePath: "/path/to/Gruntfile.js",
eb39fafa
DC
87 messages: [],
88 errorCount: 0,
89 warningCount: 0,
90 fixableErrorCount: 0,
91 fixableWarningCount: 0
92 }
93]
94```
95
96### The `result` Object
97
98<!-- This section is copied from the "Node.js API" page. Changes to this section should
99also be manually applied to that page. -->
100
101Each object in the `results` array is a `result` object. Each `result` object contains the path of the file that was linted and information about linting issues that were encountered. Here are the properties available on each `result` object:
102
103* **filePath**: The absolute path to the file that was linted.
104* **messages**: An array of `message` objects. See below for more info about messages.
105* **errorCount**: The number of errors for the given file.
106* **warningCount**: The number of warnings for the given file.
107* **source**: The source code for the given file. This property is omitted if this file has no errors/warnings or if the `output` property is present.
108* **output**: The source code for the given file with as many fixes applied as possible. This property is omitted if no fix is available.
109
110### The `message` Object
111
112Each `message` object contains information about the ESLint rule that was triggered by some source code. The properties available on each `message` object are:
113
114* **ruleId**: the ID of the rule that produced the error or warning.
115* **severity**: the severity of the failure, `1` for warnings and `2` for errors.
116* **message**: the human readable description of the error.
117* **line**: the line where the issue is located.
118* **column**: the column where the issue is located.
119* **nodeType**: the type of the node in the [AST](https://github.com/estree/estree/blob/master/spec.md#node-objects)
120
34eeec05
TL
121## The `context` Argument
122
123The formatter function receives an object as the second argument. The object has two properties:
124
125* `cwd` ... The current working directory. This value comes from the `cwd` constructor option of the [ESLint](nodejs-api.md#-new-eslintoptions) class.
126* `rulesMeta` ... The `meta` property values of rules. See the [Working with Rules](working-with-rules.md) page for more information about rules.
127
128For example, here's what the object would look like if one rule, `no-extra-semi`, had been run:
129
130```js
131{
132 cwd: "/path/to/cwd",
133 rulesMeta: {
134 "no-extra-semi": {
135 type: "suggestion",
136 docs: {
137 description: "disallow unnecessary semicolons",
138 recommended: true,
139 url: "https://eslint.org/docs/rules/no-extra-semi"
140 },
141 fixable: "code",
142 schema: [],
143 messages: {
144 unexpected: "Unnecessary semicolon."
145 }
146 }
147 }
148}
149```
150
151**Note:** if a linting is executed by deprecated `CLIEngine` class, the `context` argument may be a different value because it is up to the API users. Please check whether the `context` argument is an expected value or not if you want to support legacy environments.
152
eb39fafa
DC
153## Examples
154
155### Summary formatter
156
157A formatter that only cares about the total count of errors and warnings will look like this:
158
159```javascript
34eeec05 160module.exports = function(results, context) {
eb39fafa
DC
161 // accumulate the errors and warnings
162 var summary = results.reduce(
163 function(seq, current) {
164 seq.errors += current.errorCount;
165 seq.warnings += current.warningCount;
166 return seq;
167 },
168 { errors: 0, warnings: 0 }
169 );
170
171 if (summary.errors > 0 || summary.warnings > 0) {
172 return (
173 "Errors: " +
174 summary.errors +
175 ", Warnings: " +
176 summary.warnings +
177 "\n"
178 );
179 }
180
181 return "";
182};
183```
184
185Running `eslint` with the previous custom formatter,
186
187```bash
188eslint -f ./my-awesome-formatter.js src/
189```
190
191Will produce the following output:
192
193```bash
194Errors: 2, Warnings: 4
195```
196
197### Detailed formatter
198
199A more complex report will look something like this:
200
201```javascript
34eeec05 202module.exports = function(results, context) {
eb39fafa
DC
203 var results = results || [];
204
205 var summary = results.reduce(
206 function(seq, current) {
207 current.messages.forEach(function(msg) {
208 var logMessage = {
209 filePath: current.filePath,
210 ruleId: msg.ruleId,
34eeec05 211 ruleUrl: context.rulesMeta[msg.ruleId].docs.url,
eb39fafa
DC
212 message: msg.message,
213 line: msg.line,
214 column: msg.column
215 };
216
217 if (msg.severity === 1) {
218 logMessage.type = "warning";
219 seq.warnings.push(logMessage);
220 }
221 if (msg.severity === 2) {
222 logMessage.type = "error";
223 seq.errors.push(logMessage);
224 }
225 });
226 return seq;
227 },
228 {
229 errors: [],
230 warnings: []
231 }
232 );
233
234 if (summary.errors.length > 0 || summary.warnings.length > 0) {
235 var lines = summary.errors
236 .concat(summary.warnings)
237 .map(function(msg) {
238 return (
239 "\n" +
240 msg.type +
241 " " +
456be15e 242 msg.ruleId + (msg.ruleUrl ? " (" + msg.ruleUrl + ")" : "") +
eb39fafa
DC
243 "\n " +
244 msg.filePath +
245 ":" +
246 msg.line +
247 ":" +
248 msg.column
249 );
250 })
251 .join("\n");
252
253 return lines + "\n";
254 }
255};
256```
257
258So running `eslint` with this custom formatter:
259
260```bash
261eslint -f ./my-awesome-formatter.js src/
262```
263
264The output will be
265
266```bash
267error space-infix-ops (https://eslint.org/docs/rules/space-infix-ops)
268 src/configs/bundler.js:6:8
269error semi (https://eslint.org/docs/rules/semi)
270 src/configs/bundler.js:6:10
271warning no-unused-vars (https://eslint.org/docs/rules/no-unused-vars)
272 src/configs/bundler.js:5:6
273warning no-unused-vars (https://eslint.org/docs/rules/no-unused-vars)
274 src/configs/bundler.js:6:6
275warning no-shadow (https://eslint.org/docs/rules/no-shadow)
276 src/configs/bundler.js:65:32
277warning no-unused-vars (https://eslint.org/docs/rules/no-unused-vars)
278 src/configs/clean.js:3:6
279```
280
281## Passing Arguments to Formatters
282
283While custom formatter do not receive arguments in addition to the results object, it is possible to pass additional data into formatters.
284
285## Using Environment Variables
286
287Custom formatters have access to environment variables and so can change their behavior based on environment variable data. Here's an example that uses a `AF_SKIP_WARNINGS` environment variable to determine whether or not to show warnings in the results:
288
289```js
290module.exports = function(results) {
291 var skipWarnings = process.env.AF_SKIP_WARNINGS === "true"; //af stands for awesome-formatter
292
293 var results = results || [];
294 var summary = results.reduce(
295 function(seq, current) {
296 current.messages.forEach(function(msg) {
297 var logMessage = {
298 filePath: current.filePath,
299 ruleId: msg.ruleId,
300 message: msg.message,
301 line: msg.line,
302 column: msg.column
303 };
304
305 if (msg.severity === 1) {
306 logMessage.type = "warning";
307 seq.warnings.push(logMessage);
308 }
309 if (msg.severity === 2) {
310 logMessage.type = "error";
311 seq.errors.push(logMessage);
312 }
313 });
314 return seq;
315 },
316 {
317 errors: [],
318 warnings: []
319 }
320 );
321
322 if (summary.errors.length > 0 || summary.warnings.length > 0) {
323 var warnings = !skipWarnings ? summary.warnings : []; // skip the warnings in that case
324
325 var lines = summary.errors
326 .concat(warnings)
327 .map(function(msg) {
328 return (
329 "\n" +
330 msg.type +
331 " " +
332 msg.ruleId +
333 "\n " +
334 msg.filePath +
335 ":" +
336 msg.line +
337 ":" +
338 msg.column
339 );
340 })
341 .join("\n");
342
343 return lines + "\n";
344 }
345};
346```
347
348You would run ESLint with this custom formatter and an environment variable set like this:
349
350```bash
351AF_SKIP_WARNINGS=true eslint -f ./my-awesome-formatter.js src/
352```
353
354The output would be:
355
356```bash
357error space-infix-ops
358 src/configs/bundler.js:6:8
359
360error semi
361 src/configs/bundler.js:6:10
362```
363
eb39fafa
DC
364### Complex Argument Passing
365
366If you find the custom formatter pattern doesn't provide enough options for the way you'd like to format ESLint results, the best option is to use ESLint's built-in [JSON formatter](https://eslint.org/docs/user-guide/formatters/) and pipe the output to a second program. For example:
367
368```bash
369eslint -f json src/ | your-program-that-reads-JSON --option
370```
371
372In this example, the `your-program-that-reads-json` program can accept the raw JSON of ESLint results and process it before outputting its own format of the results. You can pass as many command line arguments to that program as are necessary to customize the output.
373
374## Note: Formatting for Terminals
375
376Modern terminals like [iTerm2](https://www.iterm2.com/) or [Guake](http://guake-project.org/) expect a specific results format to automatically open filenames when they are clicked. Most terminals support this format for that purpose:
377
378```bash
379file:line:column
380```