-
Notifications
You must be signed in to change notification settings - Fork 28
Writing a custom eslint formatter
Writing an eslint reporter/formatter is simple. Just provide a module that exports a function that will receive the results from the execution of eslint.
so the simplest formatter will be something like:
//my-awesome-formatter.js
module.exports = function (results) {
console.log(JSON.stringify(results, null, 2));
}
So running eslint
with this custom formatter:
eslint -f './my-awesome-formatter' grunt-deps/
The output will be
[
{
"filePath": "path/to/file.js",
"messages": [
{
"ruleId": "curly",
"severity": 2,
"message": "Expected { after 'if' condition.",
"line": 41,
"column": 2,
"nodeType": "IfStatement",
"source": " if ( err ) console.log( 'failed tests: ' + err );"
},
{
"ruleId": "no-process-exit",
"severity": 2,
"message": "Don't use process.exit(); throw an error instead.",
"line": 42,
"column": 2,
"nodeType": "CallExpression",
"source": " process.exit( exitCode );"
}
],
"errorCount": 2,
"warningCount": 0
},
{
"filePath": "Gruntfile.js",
"messages": [],
"errorCount": 0,
"warningCount": 0
}
]
As you can see the passed argument is just a list of results.
You will receive an object result from each file eslint validates
- filePath: The path to the file (relative to the cwd)
- messages: An array of message objects. See below for more info about messages
- errorCount: The number of errors for the given file
- warningCount: the number of warnings for the give file
So if you're writing a formatter that only cares about errors and warnings count you can do something like this:
module.exports = function (results) {
var results = results || [];
var summary = results.reduce(function (seq, current) {
seq.errors+= current.errorCount;
seq.warnings+= current.warningCount;
return seq;
}, { errors: 0, warnings: 0});
if (summary.errors > 0 || summary.warnings > 0) {
console.log('Errors: '+ summary.errors + ', Warnings: ' + summary.warnings + '\n');
}
};
So running eslint
with this custom formatter:
eslint -f './my-awesome-formatter' grunt-deps/
The output will be
Errors: 2, Warnings: 4
- ruleId: the id of the rule being violated
-
severity: the severity of the failure,
1
for warnings and2
for errors - message: the human readable description of the error
- line: the line where where the issue is located
- column: the colum where the issue is located
- nodeType: the type of the node in the AST
- source: a extract of the code the line where the failure happened. Note to self: I wish we have at least a couple more of lines here both before and after... that will help to provide a bit more of context. But so far it is good enough.
So if you want to also show a more detailed report you can do something like this:
module.exports = function (results) {
var results = results || [];
var summary = results.reduce(function (seq, current) {
current.messages.forEach(function (msg) {
var logMessage = {
filePath: current.filePath,
ruleId: msg.ruleId,
message: msg.message,
line: msg.line,
column: msg.column,
source: msg.source
};
if (msg.severity === 1) {
logMessage.type = 'warning';
seq.warnings.push(logMessage);
}
if (msg.severity === 2) {
logMessage.type = 'error';
seq.errors.push(logMessage);
}
});
return seq;
}, { errors: [], warnings: []});
if (summary.errors.length > 0 || summary.warnings.length > 0) {
var lines = summary.errors.concat(summary.warnings).map(function (msg) {
return '\n' + msg.type + ' ' + msg.ruleId + '\n ' + msg.filePath + ':' + msg.line + ':' + msg.column;
} ).join('\n');
return lines + '\n';
}
};
So running eslint
with this custom formatter:
eslint -f './my-awesome-formatter' grunt-deps/
The output will be
error space-infix-ops
grunt-deps/configs/bundler.js:6:8
error semi
grunt-deps/configs/bundler.js:6:10
warning no-unused-vars
grunt-deps/configs/bundler.js:5:6
warning no-unused-vars
grunt-deps/configs/bundler.js:6:6
warning no-shadow
grunt-deps/configs/bundler.js:65:32
warning no-unused-vars
grunt-deps/configs/clean.js:3:6
More complex reporters could written by grouping differently the errors and warnings and/or grouping the ruleIds.
I find that a reporter is easier to use if:
-
the errors are reported at the end
-
the files are printed using the following format
file:line:colum
Since that allows modern fancy terminals to make them links to files that open in your favorite editor. Mine's
favorite is Sublime Text 3 but this should work on other editors and IDEs.
Other persons prefer to concentrate in a given error first, so you can group all the errors and warnings by the ruleId of the the rule they are violating. In the end it is a matter of preferences.
Passing arguments to your reporter. So far there is no way to pass arguments to the reporter itself when executed from the command line, but you can pass them directly to the reporter reading either from the environment variables or from the command line arguments Sadly command line arguments are discarded by the cli parser. Bummer :(
For example:
Let's say that you want to show only the messages that are errors (and filter those noise warnings)
module.exports = function (results) {
var skipWarnings = process.env.AF_SKIP_WARNINGS === 'true'; //af stands for awesome-formatter
var results = results || [];
var summary = results.reduce(function (seq, current) {
current.messages.forEach(function (msg) {
var logMessage = {
filePath: current.filePath,
ruleId: msg.ruleId,
message: msg.message,
line: msg.line,
column: msg.column,
source: msg.source
};
if (msg.severity === 1) {
logMessage.type = 'warning';
seq.warnings.push(logMessage);
}
if (msg.severity === 2) {
logMessage.type = 'error';
seq.errors.push(logMessage);
}
});
return seq;
}, { errors: [], warnings: []});
if (summary.errors.length > 0 || summary.warnings.length > 0) {
var warnings = !skipWarnings ? summary.warnings : []; // skip the warnings in that case
var lines = summary.errors.concat(warnings).map(function (msg) {
return '\n' + msg.type + ' ' + msg.ruleId + '\n ' + msg.filePath + ':' + msg.line + ':' + msg.column;
} ).join('\n');
return lines + '\n';
}
};
So running eslint
with this custom formatter:
AF_SKIP_WARNINGS=true eslint -f './my-awesome-formatter' grunt-deps/
The output will be
error space-infix-ops
grunt-deps/configs/bundler.js:6:8
error semi
grunt-deps/configs/bundler.js:6:10
I'm not a huge fan of environment variables, but until eslint allow passing parameters to formatters then this is the only solution so far.
Or you can simply use any of the nice formatters written by the community. Might I suggest eslint-friendly-formatter #shamelessSelfPromotion?