gulp-reporter
Version:
Error report for: CSSLint/EditorConfig/ESLint/HTMLHint/JSCS/JSHint/PostCSS/Standard/TSLint/XO
290 lines (266 loc) • 7 kB
JavaScript
;
const chalk = require('chalk');
const path = require('path');
const emphasize = require('emphasize');
const cliTruncate = require('cli-truncate');
const stringWidth = require('string-width');
const ansiEscapes = require('ansi-escapes');
const isCI = require('ci-info').isCI;
const icons = require('./icons');
let fixIcon;
if (!isCI && process.stdout.isTTY && require('is-windows')()) {
if (process.env.VSCODE_PID || process.env.TERM_PROGRAM === 'Hyper') {
fixIcon = str => {
if (str === icons.error) {
str = str[0] + ' ';
} else if (str !== icons.info) {
str += ' ';
}
return str;
};
} else if (process.env.ConEmuPID) {
fixIcon = str => {
if (str === icons.info) {
str = '‼';
}
return str;
};
} else {
fixIcon = str => {
str = str[0];
return str;
};
}
} else {
fixIcon = str => {
if (str === icons.error) {
str = str[0];
}
return str;
};
}
const msgColor = {
success: chalk.green,
info: chalk.blue,
warn: chalk.yellow,
error: chalk.red,
};
let padNum;
if (String.prototype.padStart) {
padNum = (number, targetLength) => {
return String(number).padStart(targetLength, '0');
};
} else {
padNum = (number, targetLength) => {
number = String(number);
return ('0').repeat(Math.max(targetLength - number.length, 0)) + number;
};
}
function justify (left, right, width) {
if (left) {
width -= stringWidth(left);
} else {
left = '';
}
if (right) {
width -= stringWidth(right);
} else {
right = '';
}
return left + ' '.repeat(Math.max(width, left && right ? 1 : 0)) + right;
}
function timeFormat (timestamp) {
timestamp = new Date(timestamp * 1000);
return [
timestamp.getFullYear(),
padNum(timestamp.getMonth() + 1, 2),
padNum(timestamp.getDate(), 2),
].join('-') + ' ' + [
padNum(timestamp.getHours(), 2),
padNum(timestamp.getMinutes(), 2),
padNum(timestamp.getSeconds(), 2),
].join(':');
}
function highlight (error) {
let source = error.source.replace(/\t/g, ' ');
if (chalk.enabled) {
const extname = path.extname(error.fileName).slice(1);
if (extname) {
try {
return emphasize.highlight(extname.slice(1), source).value;
} catch (ex) {
//
}
}
source = emphasize.highlightAuto(source).value;
}
return source;
}
function addLineNumbers (code, line, fmtOpts) {
function add (s) {
let prefix = ' '.repeat(fmtOpts.columnNumberWidth + 1);
prefix += chalk.yellow(padNum(line, fmtOpts.lineNumberWidth) + ' | ');
line++;
return prefix + s;
}
return code.replace(/^.*$/gm, add);
}
function formatError (error, fmtOpts) {
const message = [];
let severity = error.severity;
if (error.demote || !severity || !icons[severity]) {
severity = 'success';
}
const mainMessage = chalk.gray(`${
padNum(error.lineNumber || 1, fmtOpts.lineNumberWidth)
}:${
padNum(error.columnNumber || 1, fmtOpts.columnNumberWidth)
}`) + ' ' + msgColor[severity](icons[severity] + ' ' + error.message);
let subMsg = [];
if (error.plugin) {
subMsg.push(justify(error.plugin, null, fmtOpts.maxPluginNameWidth));
}
if (error.rule) {
subMsg.push(justify(error.rule, null, fmtOpts.maxRuleNameWidth));
}
if (subMsg.length === 1) {
subMsg[0] = justify(subMsg[0], null, fmtOpts.maxPluginNameWidth + fmtOpts.maxRuleNameWidth + 1);
}
const link = error.docShort || error.doc;
if (link) {
subMsg.push(chalk.underline(link));
}
if (subMsg.length) {
subMsg = `(${subMsg.join(' ').trim()})`;
if (stringWidth(mainMessage) + stringWidth(subMsg) >= fmtOpts.termColumns) {
message.push(mainMessage, justify(
null,
subMsg,
fmtOpts.termColumns
));
} else {
message.push(
justify(
mainMessage,
subMsg,
fmtOpts.termColumns
)
);
}
} else {
message.push(mainMessage);
}
message[0] = message[0].replace(/\S\uFE0F/g, fixIcon);
if (fmtOpts.source && error.source) {
let source = fmtOpts.sourceCache[error.source];
if (!source) {
source = highlight(error);
source = addLineNumbers(source, error.lineNumber || 1, fmtOpts);
source = cliTruncate(source, fmtOpts.termColumns);
fmtOpts.sourceCache[error.source] = source;
}
message.push(source);
}
if (error.blame) {
let blame = error.blame._string;
if (!blame) {
const author = `(${
justify(
error.blame.author.name,
null,
fmtOpts.maxNameWidth
)
} ${
justify(
`<${error.blame.author.mail}>`,
null,
fmtOpts.maxEmailWidth + 2
)
} ${
timeFormat(error.blame.author.time)
})`;
blame = chalk.gray(justify(
cliTruncate(
error.blame.hash,
Math.max(fmtOpts.termColumns - stringWidth(author) - 1, 9)
),
author,
fmtOpts.termColumns
));
error.blame._string = blame;
}
message.push(blame);
}
return message.join('\n');
}
function formatter (file, options) {
const sourceCache = {};
let maxLineNumber = 0;
let maxColumnNumber = 0;
let maxNameWidth = 0;
let maxEmailWidth = 0;
let maxPluginNameWidth = 0;
let maxRuleNameWidth = 0;
let maxMessageWidth = 0;
let errors = file.report.errors;
errors.forEach(error => {
if (error.lineNumber > maxLineNumber) {
maxLineNumber = error.lineNumber;
}
if (error.columnNumber > maxColumnNumber) {
maxColumnNumber = error.columnNumber;
}
if (error.blame && !/^0+$/.test(error.blame.hash)) {
const nameWidth = stringWidth(error.blame.author.name);
if (nameWidth > maxNameWidth) {
maxNameWidth = nameWidth;
}
const emailWidth = stringWidth(error.blame.author.mail);
if (emailWidth > maxEmailWidth) {
maxEmailWidth = emailWidth;
}
}
const pluginNameWidth = error.plugin && stringWidth(error.plugin);
if (pluginNameWidth > maxPluginNameWidth) {
maxPluginNameWidth = pluginNameWidth;
}
const ruleNameWidth = error.rule && stringWidth(error.rule);
if (ruleNameWidth > maxRuleNameWidth) {
maxRuleNameWidth = ruleNameWidth;
}
const messageWidth = stringWidth(error.message);
if (messageWidth > maxMessageWidth) {
maxMessageWidth = messageWidth;
}
});
const lineNumberWidth = Math.max(String(maxLineNumber).length, 2);
const columnNumberWidth = Math.max(String(maxColumnNumber).length, 2);
const fmtOpts = {
sourceCache,
maxNameWidth,
maxEmailWidth,
maxPluginNameWidth,
maxRuleNameWidth,
lineNumberWidth,
columnNumberWidth,
termColumns: Math.min(
options._termColumns - 6,
Math.max(
31 + lineNumberWidth + columnNumberWidth + maxMessageWidth + maxPluginNameWidth + maxRuleNameWidth,
options.blame ? 66 + maxNameWidth + maxEmailWidth : 0
)
),
source: options.blame,
};
errors = errors.map(error => (
formatError(error, fmtOpts).replace(/^/gm, ' ')
));
let head = chalk.cyan.underline(path.relative(file.cwd, file.path).replace(/\\/g, '/'));
if (process.stdout.isTTY && !isCI) {
// make relative paths Cmd+click'able in iTerm
head = ansiEscapes.iTerm.setCwd(file.cwd) + head;
}
errors.unshift(head);
return errors.join('\n');
}
module.exports = formatter;