eslint-formatter-friendly
Version:
simple formatter/reporter for eslint that's friendly with Sublime Text and iterm2 'click to open file' functionality
285 lines (233 loc) • 7.47 kB
JavaScript
/**
* Based on Stylish reporter from Sindre Sorhus
*/
;
var chalk = require('chalk'),
stripAnsi = require('strip-ansi'),
table = require('text-table'),
extend = require('extend');
var path = require('path');
var process = require('./process');
var fs = require('fs');
var codeFrameColumns = require('@babel/code-frame').codeFrameColumns;
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Given a word and a count, append an s if count is not one.
* @param {string} word A word in its singular form.
* @param {int} count A number controlling whether word should be pluralized.
* @returns {string} The original word with an s on the end if count is not one.
*/
function pluralize(word, count) {
return (count === 1 ? word : word + 's');
}
var parseBoolEnvVar = function(varName) {
var env = process.env || { };
return env[varName] === 'true';
};
var parseEnvVal = function(varName) {
var env = process.env || { };
return env[varName];
};
var subtleLog = function(args) {
return parseBoolEnvVar('EFF_NO_GRAY') ? args : chalk.gray(args);
};
var getEnvVar = function(varName) {
var env = process.env || { };
return env[varName] || false;
};
var getFileLink = function(_path, line, column) {
var scheme = getEnvVar('EFF_EDITOR_SCHEME');
if (scheme === false) {
return false;
}
return scheme.replace('{file}', encodeURIComponent(_path)).replace('{line}', line).replace('{column}', column);
};
var getKeyLink = function(key) {
var noLinkRules = parseBoolEnvVar('EFF_NO_LINK_RULES');
var url = key.indexOf('/') > -1 ? 'https://google.com/#q=' : 'http://eslint.org/docs/rules/';
return (!noLinkRules) ? chalk.underline(subtleLog(url + chalk.white(encodeURIComponent(key)))) : chalk.white(key);
};
var printSummary = function(hash, title, method) {
var res = '\n\n' + chalk[method](title + ':') + chalk.white('\n');
res += table(
Object.keys(hash).sort(function(a, b) {
return hash[a] > hash[b] ? -1 : 1;
}).map(function(key) {
return [
'',
hash[key],
getKeyLink(key)
];
}), {
align: [
'',
'r',
'l'
],
stringLength: function(str) {
return stripAnsi(str).length;
}
});
return res;
};
//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
module.exports = function(results) {
var output = '\n',
total = 0,
errors = 0,
warnings = 0,
summaryColor = 'yellow';
results = results || [];
var entries = [];
var absolutePathsToFile = parseBoolEnvVar('EFF_ABSOLUTE_PATHS');
var groupByIssue = parseBoolEnvVar('EFF_BY_ISSUE');
var filterRule = parseEnvVal('EFF_FILTER');
var showSource = !parseBoolEnvVar('EFF_NO_SOURCE');
var errorsHash = { };
var warningsHash = { };
results.forEach(function(result) {
var messages = result.messages || [];
const fileSource = result.source || fs.readFileSync(result.filePath, 'utf8');
entries = entries.concat(messages.map(function(message) {
return extend({
filePath: absolutePathsToFile ? path.resolve(result.filePath) : path.relative('.', result.filePath)
}, message, {
fileSource
});
}));
});
entries.sort(function(a, b) {
if (a.severity > b.severity) {
return 1;
}
if (a.severity < b.severity) {
return -1;
}
if (groupByIssue) {
if (a.ruleId > b.ruleId) {
return 1;
}
if (a.ruleId < b.ruleId) {
return -1;
}
}
var pathSort = a.filePath.localeCompare(b.filePath);
if (pathSort) {
return pathSort;
}
if (a.line > b.line) {
return 1;
}
if (a.line < b.line) {
return -1;
}
if (a.column > b.column) {
return 1;
}
if (a.column < b.column) {
return -1;
}
return 0;
});
var lastRuleId;
output += entries.reduce(function(seq, message) {
var messageType;
if (filterRule) {
if (message.ruleId !== filterRule) {
return seq;
}
}
if (message.fatal || message.severity === 2) {
messageType = chalk.red('✘');
summaryColor = 'red';
errorsHash[message.ruleId] = (errorsHash[message.ruleId] || 0) + 1;
errors++;
} else {
messageType = chalk.yellow('⚠');
warningsHash[message.ruleId] = (warningsHash[message.ruleId] || 0) + 1;
warnings++;
}
var line = message.line || 0;
var column = message.column || 0;
var filePath = message.filePath;
var link = getFileLink(filePath, line, column);
var filename = subtleLog(filePath + ':' + line + ':' + column);
function renderTitle() {
return '\n ' + messageType + ' ' + getKeyLink(message.ruleId || '');
}
function renderLink() {
return (link === false ? '' : ' ' + chalk.underline(subtleLog(link)));
}
function renderDescription() {
return '\n ' + message.message.replace(/\.$/, '') + '\n';
}
function renderFileLink() {
return (showSource ? '\n' : '') + ' ' + (link === false ? chalk.underline(filename) : filename);
}
function renderSourceCode() {
const location = {
start: {
line: message.line,
column: message.column
}
};
return showSource ? codeFrameColumns(message.fileSource, location, {
highlightCode: true
}).split('\n').map(l => ' ' + l).join('\n') : '';
}
function createLine(arr) {
return arr.filter(function(l) {
return !!(l || '').trim();
}).join('\n');
}
if (groupByIssue) {
var isSameIssueAsLastOne = lastRuleId === message.ruleId;
lastRuleId = message.ruleId;
seq.push(createLine([
!isSameIssueAsLastOne ? renderTitle() : '',
!isSameIssueAsLastOne ? renderLink() : '',
!isSameIssueAsLastOne ? renderDescription() : '',
renderFileLink(),
renderSourceCode()
]));
} else {
seq.push(createLine([
renderTitle(),
renderLink(),
renderDescription(),
renderFileLink(),
renderSourceCode()
]));
}
return seq;
}, []).join('\n') + '\n\n';
total = entries.length;
if (total > 0) {
output += chalk[summaryColor].bold([
'✘ ',
total,
pluralize(' problem', total),
' (',
errors,
pluralize(' error', errors),
', ',
warnings,
pluralize(' warning', warnings),
')'
].join('')) + chalk.white('\n');
if (errors > 0) {
output += printSummary(errorsHash, 'Errors', 'red');
}
if (warnings > 0) {
output += printSummary(warningsHash, 'Warnings', 'yellow') + '\n';
}
}
if (process.env.FORCE_ITERM_HINT === 'true' || (process.stdout.isTTY && !process.env.CI)) {
output = '\u001B]1337;CurrentDir=' + process.cwd() + '\u0007' + output;
}
return total > 0 ? output : '';
};