Spaces:
Sleeping
Sleeping
; | |
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}${nbErrors === 1 ? '' : 's'}` : | |
`Compiled with ${nbErrors} ${severity}${nbErrors === 1 ? '' : '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); | |
} | |
const findErrorsRecursive = (compilation) => { | |
const errors = compilation[type]; | |
if (errors.length === 0 && compilation.children) { | |
for (const child of compilation.children) { | |
errors.push(...findErrorsRecursive(child)); | |
} | |
} | |
return uniqueBy(errors, error => error.message); | |
}; | |
return findErrorsRecursive(stats.compilation); | |
} | |
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; | |