164 lines
4.8 KiB
Java
164 lines
4.8 KiB
Java
![]() |
'use strict';
|
||
|
|
||
|
const path = require('path');
|
||
|
const chalk = require('chalk');
|
||
|
const os = require('os');
|
||
|
const transformErrors = require('./core/transformErrors');
|
||
|
const formatErrors = require('./core/formatErrors');
|
||
|
const output = require('./output');
|
||
|
const utils = require('./utils');
|
||
|
|
||
|
const concat = utils.concat;
|
||
|
const uniqueBy = utils.uniqueBy;
|
||
|
|
||
|
const defaultTransformers = [
|
||
|
require('./transformers/babelSyntax'),
|
||
|
require('./transformers/moduleNotFound'),
|
||
|
require('./transformers/esLintError'),
|
||
|
];
|
||
|
|
||
|
const defaultFormatters = [
|
||
|
require('./formatters/moduleNotFound'),
|
||
|
require('./formatters/eslintError'),
|
||
|
require('./formatters/defaultError'),
|
||
|
];
|
||
|
|
||
|
class FriendlyErrorsWebpackPlugin {
|
||
|
|
||
|
constructor(options) {
|
||
|
options = options || {};
|
||
|
this.compilationSuccessInfo = options.compilationSuccessInfo || {};
|
||
|
this.onErrors = options.onErrors;
|
||
|
this.shouldClearConsole = options.clearConsole == null ? true : Boolean(options.clearConsole);
|
||
|
this.formatters = concat(defaultFormatters, options.additionalFormatters);
|
||
|
this.transformers = concat(defaultTransformers, options.additionalTransformers);
|
||
|
this.previousEndTimes = {};
|
||
|
}
|
||
|
|
||
|
apply(compiler) {
|
||
|
|
||
|
const doneFn = stats => {
|
||
|
this.clearConsole();
|
||
|
|
||
|
const hasErrors = stats.hasErrors();
|
||
|
const hasWarnings = stats.hasWarnings();
|
||
|
|
||
|
if (!hasErrors && !hasWarnings) {
|
||
|
this.displaySuccess(stats);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (hasErrors) {
|
||
|
this.displayErrors(extractErrorsFromStats(stats, 'errors'), 'error');
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (hasWarnings) {
|
||
|
this.displayErrors(extractErrorsFromStats(stats, 'warnings'), 'warning');
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const invalidFn = () => {
|
||
|
this.clearConsole();
|
||
|
output.title('info', 'WAIT', 'Compiling...');
|
||
|
};
|
||
|
|
||
|
if (compiler.hooks) {
|
||
|
const plugin = { name: 'FriendlyErrorsWebpackPlugin' };
|
||
|
|
||
|
compiler.hooks.done.tap(plugin, doneFn);
|
||
|
compiler.hooks.invalid.tap(plugin, invalidFn);
|
||
|
} else {
|
||
|
compiler.plugin('done', doneFn);
|
||
|
compiler.plugin('invalid', invalidFn);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
clearConsole() {
|
||
|
if (this.shouldClearConsole) {
|
||
|
output.clearConsole();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
displaySuccess(stats) {
|
||
|
const time = isMultiStats(stats) ? this.getMultiStatsCompileTime(stats) : this.getStatsCompileTime(stats);
|
||
|
output.title('success', 'DONE', 'Compiled successfully in ' + time + 'ms');
|
||
|
|
||
|
if (this.compilationSuccessInfo.messages) {
|
||
|
this.compilationSuccessInfo.messages.forEach(message => output.info(message));
|
||
|
}
|
||
|
if (this.compilationSuccessInfo.notes) {
|
||
|
output.log();
|
||
|
this.compilationSuccessInfo.notes.forEach(note => output.note(note));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
displayErrors(errors, severity) {
|
||
|
const processedErrors = transformErrors(errors, this.transformers);
|
||
|
|
||
|
const topErrors = getMaxSeverityErrors(processedErrors);
|
||
|
const nbErrors = topErrors.length;
|
||
|
|
||
|
const subtitle = severity === 'error' ?
|
||
|
`Failed to compile with ${nbErrors} ${severity}s` :
|
||
|
`Compiled with ${nbErrors} ${severity}s`;
|
||
|
output.title(severity, severity.toUpperCase(), subtitle);
|
||
|
|
||
|
if (this.onErrors) {
|
||
|
this.onErrors(severity, topErrors);
|
||
|
}
|
||
|
|
||
|
formatErrors(topErrors, this.formatters, severity)
|
||
|
.forEach(chunk => output.log(chunk));
|
||
|
}
|
||
|
|
||
|
getStatsCompileTime(stats, statsIndex) {
|
||
|
// When we have multi compilations but only one of them is rebuilt, we need to skip the
|
||
|
// unchanged compilers to report the true rebuild time.
|
||
|
if (statsIndex !== undefined) {
|
||
|
if (this.previousEndTimes[statsIndex] === stats.endTime) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
this.previousEndTimes[statsIndex] = stats.endTime;
|
||
|
}
|
||
|
|
||
|
return stats.endTime - stats.startTime;
|
||
|
}
|
||
|
|
||
|
getMultiStatsCompileTime(stats) {
|
||
|
// Webpack multi compilations run in parallel so using the longest duration.
|
||
|
// https://webpack.github.io/docs/configuration.html#multiple-configurations
|
||
|
return stats.stats
|
||
|
.reduce((time, stats, index) => Math.max(time, this.getStatsCompileTime(stats, index)), 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function extractErrorsFromStats(stats, type) {
|
||
|
if (isMultiStats(stats)) {
|
||
|
const errors = stats.stats
|
||
|
.reduce((errors, stats) => errors.concat(extractErrorsFromStats(stats, type)), []);
|
||
|
// Dedupe to avoid showing the same error many times when multiple
|
||
|
// compilers depend on the same module.
|
||
|
return uniqueBy(errors, error => error.message);
|
||
|
}
|
||
|
return stats.compilation[type];
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
}
|
||
|
|
||
|
module.exports = FriendlyErrorsWebpackPlugin;
|