zarbis
Version:
Configuration-less build tool
169 lines • 6.82 kB
JavaScript
import { codeFrameColumns } from '@babel/code-frame';
const PLUGIN_NAME = 'ErrorFormatter';
const SHIT = 'SHIT ';
const HINT = 'HINT ';
export default class ErrorFormatter {
logger;
doConsoleClear;
constructor(logger, doConsoleClear, explain = true) {
this.logger = logger;
this.doConsoleClear = doConsoleClear;
}
apply(compiler) {
this.clearConsole();
this.logger.info(`Initial compilation in progress...`);
compiler.hooks.emit.tapAsync(PLUGIN_NAME, (stats, callback) => {
this.clearConsole();
this.logger.info(`Emitting...`);
callback();
});
let self = this;
compiler.hooks.compile.tap(PLUGIN_NAME, (params) => {
self.clearConsole();
this.logger.info(`The compiler is starting to compile...`);
});
compiler.hooks.compilation.tap(PLUGIN_NAME, function (compilation) {
self.clearConsole();
self.logger.info(`The compiler is starting a new compilation...`);
compilation.hooks.optimize.tap(PLUGIN_NAME, function () {
self.logger.info(`The compilation is starting to optimize files...`);
});
});
compiler.hooks.done.tap(PLUGIN_NAME, (stats) => {
this.clearConsole();
const hasErrors = stats.hasErrors();
const hasWarnings = stats.hasWarnings();
if (!hasErrors && !hasWarnings) {
this.displaySuccess(stats);
return true;
}
if (hasErrors) {
this.displayErrors(extractErrorsFromStats(stats, 'errors'), 'error');
return true;
}
if (hasWarnings) {
this.displayErrors(extractErrorsFromStats(stats, 'warnings'), 'warning');
return true;
}
});
compiler.hooks.invalid.tap(PLUGIN_NAME, (fileName) => {
this.clearConsole();
this.logger.info(`Invalid (${fileName} changed), compilation in progress...`);
});
}
clearConsole() {
}
displaySuccess(stats) {
const time = getCompileTime(stats);
this.logger.info(`Compiled successfully in ${time}ms`);
}
displayErrors(errors, severity) {
const nbErrors = errors.length;
const isError = severity === 'error';
const subtitle = isError ?
`Failed to compile with ${nbErrors} ${severity}s` :
`Compiled with ${nbErrors} ${severity}s`;
if (isError)
this.logger.error(subtitle);
else
this.logger.warn(subtitle);
let missingModules = [];
errors.forEach(error => {
if (error.error === undefined)
error.error = error;
if (error.error.message.startsWith('Critical dependency:')) {
this.logger.warn(`Critical dependency near ${error.module.userRequest}:${error.loc.end.line}`);
this.logger.warn(codeFrameColumns(error.module._source._value, error.loc, {
message: error.error.message.replace('Critical dependency:', '').trim(),
highlightCode: true,
}));
//at ${error.module.userRequest}:${error.loc.end.line}:${error.loc.end.column}
}
else if (error.error.message.startsWith('Can\'t resolve \'')) {
if (error.dependencies instanceof Array) {
missingModules.push(...error.dependencies.map(dep => dep.request));
}
else {
const dep = error.error.message.match(/Can't resolve '(.+?)'/);
if (dep !== null) {
missingModules.push(dep[1]);
}
}
this.logger.error(SHIT + error.error.message);
}
else if (error.error.stack === undefined) {
this.logger.error(error.message);
}
else if (error.message.startsWith('SyntaxError: ')) {
const lines = error.stack.toString().split('\n');
let found = false;
const firstStackLine = lines.length - lines.slice().reverse().filter(line => {
if (line.trim().startsWith('at ') && !found) {
return true;
}
else {
found = true;
return false;
}
}).length;
this.logger.error(lines.slice(0, firstStackLine).join('\n'));
}
else {
if (isError)
this.logger.error(error);
else
this.logger.warn(error.message);
}
});
missingModules = [...new Set(missingModules.map(e => {
if (e.startsWith('@'))
return e.split('/').slice(0, 2).join('/');
return e.split('/')[0];
}))].sort().filter(e => !e.startsWith('.') && !e.startsWith('/') && !e.startsWith('~'));
const modules = missingModules.join(' ');
if (missingModules.length > 0 && modules.trim() != '') {
this.logger.warn(`${HINT}Found ${missingModules.length} missing dependencies, you can try to install them with`);
this.logger.warn(`${HINT}yarn add ${modules}`);
}
}
}
function extractErrorsFromStats(stats, type) {
if (isMultiStats(stats)) {
const errors = stats.stats
.reduce((errors, stats) => errors.concat(extractErrorsFromStats(stats, type)), []);
return uniqueBy(errors, error => error.message);
}
return stats.compilation[type];
}
function getCompileTime(stats) {
if (isMultiStats(stats)) {
return stats.stats
.reduce((time, stats) => Math.max(time, getCompileTime(stats)), 0);
}
return stats.endTime - stats.startTime;
}
function isMultiStats(stats) {
return stats.stats;
}
function getMaxSeverityErrors(errors) {
const maxSeverity = getMaxInt(errors, 'severity');
return errors.filter(e => e.severity === maxSeverity);
}
function getMaxInt(collection, propertyName) {
return collection.reduce((res, curr) => {
return curr[propertyName] > res ? curr[propertyName] : res;
}, 0);
}
function concat() {
const args = Array.from(arguments).filter(e => e != null);
const baseArray = Array.isArray(args[0]) ? args[0] : [args[0]];
return Array.prototype.concat.apply(baseArray, args.slice(1));
}
function uniqueBy(arr, fun) {
const seen = {};
return arr.filter(el => {
const e = fun(el);
return !(e in seen) && (seen[e] = 1);
});
}
//# sourceMappingURL=ErrorFormatter.js.map