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; | |