UNPKG

parallel-webpack

Version:

Builds multiple webpack configurations in parallel and allows you to easily create variants to those configurations.

213 lines (196 loc) 8.07 kB
var Promise = require('bluebird'), chalk = require('chalk'), loadConfigurationFile = require('./loadConfigurationFile').default, notifyIPCWatchCompileDone = require('./watchModeIPC').notifyIPCWatchCompileDone, presetToOptions = require('webpack/lib/Stats').presetToOptions; /** * Choose the most correct version of webpack, prefer locally installed version, * fallback to the own dependency if there's none. * @returns {*} */ function getWebpack() { try { return require(process.cwd() + '/node_modules/webpack'); } catch(e) { return require('webpack'); } } function getAppName(webpackConfig) { var appName = webpackConfig.name || webpackConfig.output && webpackConfig.output.filename || String(process.pid); if(~appName.indexOf('[name]') && typeof webpackConfig.entry === 'object') { var entryNames = Object.keys(webpackConfig.entry); if(entryNames.length === 1) { // we can only replace [name] with the entry point if there is only one entry point appName = appName.replace(/\[name]/g, entryNames[0]); } } return appName; } function getOutputOptions(webpackConfig, options) { var stats = webpackConfig.stats; // @see https://webpack.js.org/configuration/stats/ if (typeof stats === 'string') { stats = presetToOptions(stats); } var outputOptions = Object.create(stats || {}); if(typeof options.modulesSort !== 'undefined') { outputOptions.modulesSort = options.modulesSort; } if(typeof options.chunksSort !== 'undefined') { outputOptions.chunksSort = options.chunksSort; } if(typeof options.assetsSort !== 'undefined') { outputOptions.assetsSort = options.assetsSort; } if(typeof options.exclude !== 'undefined') { outputOptions.exclude = options.exclude; } if(typeof options.colors !== 'undefined') { outputOptions.colors = options.colors; } return outputOptions; } /** * Create a single webpack build using the specified configuration. * Calls the done callback once it has finished its work. * * @param {string} configuratorFileName The app configuration filename * @param {Object} options The build options * @param {boolean} options.watch If `true`, then the webpack watcher is being run; if `false`, runs only ones * @param {boolean} options.json If `true`, then the webpack watcher will only report the result as JSON but not produce any other output * @param {number} index The configuration index * @param {number} expectedConfigLength * @param {Function} done The callback that should be invoked once this worker has finished the build. */ module.exports = function(configuratorFileName, options, index, expectedConfigLength, done) { if(options.argv) { process.argv = options.argv; } chalk.enabled = options.colors; var config = loadConfigurationFile(configuratorFileName) Promise.resolve(config).then(function(config) { var watch = !!options.watch, silent = !!options.json; if(expectedConfigLength !== 1 && !Array.isArray(config) || (Array.isArray(config) && config.length !== expectedConfigLength)) { if(config.length !== expectedConfigLength) { var errorMessage = '[WEBPACK] There is a difference between the amount of the' + ' provided configs. Maybe you where expecting command line' + ' arguments to be passed to your webpack.config.js. If so,' + " you'll need to separate them with a -- from the parallel-webpack options."; console.error(errorMessage); throw Error(errorMessage); } } var webpackConfig; if(Array.isArray(config)) { webpackConfig = config[index]; } else { webpackConfig = config } var MSG_ERROR = chalk.red('[WEBPACK]'); var MSG_SUCCESS = chalk.blue('[WEBPACK]'); var MSG_APP = chalk.yellow(getAppName(webpackConfig)); var watcher; var webpack = getWebpack(); var hasCompletedOneCompile = false; var outputOptions = getOutputOptions(webpackConfig, options); var disconnected = false; if(!silent) { console.log('%s Started %s %s', MSG_SUCCESS, watch ? 'watching' : 'building', MSG_APP); } var compiler = webpack(webpackConfig); if(watch || webpack.watch) { watcher = compiler.watch(webpackConfig.watchOptions, finishedCallback); } else { compiler.run(finishedCallback); } process.on('SIGINT', shutdownCallback); process.on('exit', exitCallback); process.on('unhandledRejection', unhandledRejectionCallback); process.on('disconnect', disconnectCallback); function cleanup() { process.removeListener('SIGINT', shutdownCallback); process.removeListener('exit', exitCallback); process.removeListener('unhandledRejection', unhandledRejectionCallback); process.removeListener('disconnect', disconnectCallback); } function shutdownCallback() { if(watcher) { watcher.close(done); } done({ message: MSG_ERROR + ' Forcefully shut down ' + MSG_APP }); process.exit(0); } function unhandledRejectionCallback(error) { console.log(MSG_ERROR + 'Build child process error:', error); process.exit(1); } function exitCallback(code) { cleanup(); if (code === 0) { return; } if(watcher) { watcher.close(done); } done({ message: MSG_ERROR + ' Exit ' + MSG_APP + ' with code ' + code }); } function disconnectCallback(){ disconnected = true; console.log('%s Parent process terminated, exit building %s', MSG_ERROR, MSG_APP); process.exit(1); } function finishedCallback(err, stats) { if(err) { console.error('%s fatal error occured', MSG_ERROR); console.error(err); cleanup(); return done(err); } if(stats.compilation.errors && stats.compilation.errors.length) { var message = MSG_ERROR + ' Errors building ' + MSG_APP + "\n" + stats.compilation.errors.map(function(error) { return error.message; }).join("\n"); if(watch) { console.log(message); } else { cleanup(); if (disconnected) { return; } return done({ message: message, stats: JSON.stringify(stats.toJson(outputOptions), null, 2) }); } } if(!silent) { if(options.stats) { console.log(stats.toString(outputOptions)); } var timeStamp = watch ? ' ' + chalk.yellow(new Date().toTimeString().split(/ +/)[0]) : ''; console.log('%s Finished building %s within %s seconds', chalk.blue('[WEBPACK' + timeStamp + ']'), MSG_APP, chalk.blue((stats.endTime - stats.startTime) / 1000)); } if(!watch) { cleanup(); if (disconnected) { return; } done(null, options.stats ? JSON.stringify(stats.toJson(outputOptions), null, 2) : ''); } else if (!hasCompletedOneCompile) { notifyIPCWatchCompileDone(index); hasCompletedOneCompile = true; } } }); };