New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(webpack-cli): infrastructure logging #2036
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,63 +1,120 @@ | ||
const packageExists = require('../utils/package-exists'); | ||
const webpack = packageExists('webpack') ? require('webpack') : undefined; | ||
const logger = require('../utils/logger'); | ||
|
||
const PluginName = 'webpack-cli'; | ||
const { getStatsOptions } = require('../utils/stats-options'); | ||
const { PluginName } = require('../utils/name'); | ||
|
||
class WebpackCLIPlugin { | ||
constructor(options) { | ||
this.options = options; | ||
|
||
this.isWatch = false; | ||
} | ||
|
||
async apply(compiler) { | ||
const compilers = compiler.compilers || [compiler]; | ||
const { progress } = this.options; | ||
if (progress) { | ||
this.appendProgressPlugin(compiler, progress); | ||
} | ||
|
||
for (const compiler of compilers) { | ||
if (this.options.progress) { | ||
const { ProgressPlugin } = compiler.webpack || webpack; | ||
const isWebpack5 = Boolean(compiler.webpack); | ||
|
||
let progressPluginExists; | ||
const logger = compiler.getInfrastructureLogger(PluginName); | ||
|
||
if (compiler.options.plugins) { | ||
progressPluginExists = Boolean(compiler.options.plugins.find((plugin) => plugin instanceof ProgressPlugin)); | ||
} | ||
const resolveName = (obj) => (obj.name ? `${obj.name} ` : ''); | ||
|
||
if (!progressPluginExists) { | ||
if (typeof this.options.progress === 'string' && this.options.progress !== 'profile') { | ||
logger.error( | ||
`'${this.options.progress}' is an invalid value for the --progress option. Only 'profile' is allowed.`, | ||
); | ||
process.exit(2); | ||
} | ||
const done = (stats) => { | ||
const printStats = (childCompiler, childStats) => { | ||
const name = resolveName(childCompiler); | ||
|
||
const status = childStats.toString({ | ||
all: false, | ||
version: true, | ||
timings: true, | ||
}); | ||
|
||
const normalizedStatus = (() => { | ||
if (isWebpack5) { | ||
return status; | ||
} else { | ||
const [version, time] = status.split('\n').map((line) => { | ||
const value = line.split(':')[1] || ''; | ||
return value.trim(); | ||
}); | ||
|
||
const isProfile = this.options.progress === 'profile'; | ||
return `${name ? name + `(${version})` : version} compiled in ${time}`; | ||
} | ||
})(); | ||
|
||
new ProgressPlugin({ profile: isProfile }).apply(compiler); | ||
if (childStats.hasErrors()) { | ||
logger.error(normalizedStatus); | ||
} else if (childStats.hasWarnings()) { | ||
logger.warn(normalizedStatus); | ||
} else { | ||
logger.info(normalizedStatus); | ||
} | ||
} | ||
} | ||
|
||
const compilationName = (compilation) => (compilation.name ? ` ${compilation.name}` : ''); | ||
const statsString = childStats.toString(getStatsOptions(childCompiler)); | ||
process.stdout.write(statsString + '\n'); | ||
|
||
compiler.hooks.watchRun.tap(PluginName, (compilation) => { | ||
const { bail, watch } = compilation.options; | ||
if (bail && watch) { | ||
logger.warn('You are using "bail" with "watch". "bail" will still exit webpack when the first error is found.'); | ||
if (this.isWatch) { | ||
logger.info(name + 'watching files for updates...'); | ||
} | ||
}; | ||
|
||
if (compiler.compilers) { | ||
compiler.compilers.forEach((compilerFromMultiCompileMode, index) => { | ||
printStats(compilerFromMultiCompileMode, stats.stats[index]); | ||
}); | ||
} else { | ||
printStats(compiler, stats); | ||
} | ||
}; | ||
|
||
logger.success(`Compilation${compilationName(compilation)} starting...`); | ||
compiler.hooks.run.tap(PluginName, (compilation) => { | ||
logger.info(resolveName(compilation) + 'compilation starting...'); | ||
}); | ||
compiler.hooks.watchRun.tap(PluginName, (compilation) => { | ||
logger.info(resolveName(compilation) + 'compilation starting...'); | ||
|
||
compiler.hooks.done.tap(PluginName, (compilation) => { | ||
logger.success(`Compilation${compilationName(compilation)} finished`); | ||
|
||
if (compilation.options.bail) { | ||
logger.warn('you are using "bail" with "watch". "bail" will still exit webpack when the first error is found.'); | ||
} | ||
this.isWatch = true; | ||
}); | ||
compiler.hooks.done.tap(PluginName, (stats) => { | ||
process.nextTick(() => { | ||
if (compiler.watchMode) { | ||
logger.success('watching files for updates...'); | ||
} | ||
done(stats); | ||
}); | ||
}); | ||
} | ||
|
||
appendProgressPlugin(compiler, progress) { | ||
const { ProgressPlugin } = compiler.webpack || webpack; | ||
|
||
const logger = compiler.getInfrastructureLogger(PluginName); | ||
|
||
const compilers = compiler.compilers || [compiler]; | ||
|
||
for (const compiler of compilers) { | ||
let progressPluginExists; | ||
|
||
if (compiler.options.plugins) { | ||
progressPluginExists = Boolean(compiler.options.plugins.find((plugin) => plugin instanceof ProgressPlugin)); | ||
} | ||
|
||
if (!progressPluginExists) { | ||
if (typeof progress === 'string' && progress !== 'profile') { | ||
logger.error(`'${progress}' is an invalid value for the --progress option. Only 'profile' is allowed.`); | ||
|
||
process.exit(2); | ||
} | ||
|
||
const isProfile = progress === 'profile'; | ||
|
||
new ProgressPlugin({ profile: isProfile }).apply(compiler); | ||
} | ||
} | ||
} | ||
} | ||
|
||
module.exports = WebpackCLIPlugin; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
const PluginName = 'webpack-cli'; | ||
|
||
module.exports = { | ||
PluginName, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
const packageExists = require('./package-exists'); | ||
const webpack = packageExists('webpack') ? require('webpack') : undefined; | ||
const { options: coloretteOptions } = require('colorette'); | ||
|
||
const getStatsOptions = (compiler) => { | ||
let options = compiler.options | ||
? typeof compiler.options.stats === 'object' | ||
? Object.assign({}, compiler.options.stats) | ||
: compiler.options.stats | ||
: undefined; | ||
|
||
// TODO remove after drop webpack@4 | ||
if (webpack.Stats && webpack.Stats.presetToOptions) { | ||
if (!options) { | ||
options = {}; | ||
} else if (typeof options === 'boolean' || typeof options === 'string') { | ||
options = webpack.Stats.presetToOptions(options); | ||
} | ||
} | ||
|
||
options.colors = typeof options.colors !== 'undefined' ? options.colors : coloretteOptions.enabled; | ||
|
||
return options; | ||
}; | ||
|
||
module.exports = { getStatsOptions }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No utils relates to complation, use methods classes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was execrated to utils as, moved stats display to WebpackCLIPlugin, but wanted to keep |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should set exitCode = 1 for errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exit code is set in callback in webpack-cli.js this wasn't changed https://github.com/webpack/webpack-cli/pull/2036/files#diff-7c711e2a4ef4b16d2cbbe1658495723402f9692da654cb6fa959127f3dc779f2R357-R359
Wondering if we should reset it if next complication is has no errors 🤔