UNPKG

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
/** * Based on Stylish reporter from Sindre Sorhus */ 'use strict'; 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 : ''; };