UNPKG

8.07 kBJavaScriptView Raw
1var Promise = require('bluebird'),
2 chalk = require('chalk'),
3 loadConfigurationFile = require('./loadConfigurationFile').default,
4 notifyIPCWatchCompileDone = require('./watchModeIPC').notifyIPCWatchCompileDone,
5 presetToOptions = require('webpack/lib/Stats').presetToOptions;
6/**
7 * Choose the most correct version of webpack, prefer locally installed version,
8 * fallback to the own dependency if there's none.
9 * @returns {*}
10 */
11function getWebpack() {
12 try {
13 return require(process.cwd() + '/node_modules/webpack');
14 } catch(e) {
15 return require('webpack');
16 }
17}
18
19function getAppName(webpackConfig) {
20 var appName = webpackConfig.name
21 || webpackConfig.output && webpackConfig.output.filename
22 || String(process.pid);
23 if(~appName.indexOf('[name]') && typeof webpackConfig.entry === 'object') {
24 var entryNames = Object.keys(webpackConfig.entry);
25 if(entryNames.length === 1) {
26 // we can only replace [name] with the entry point if there is only one entry point
27 appName = appName.replace(/\[name]/g, entryNames[0]);
28 }
29 }
30 return appName;
31}
32
33function getOutputOptions(webpackConfig, options) {
34 var stats = webpackConfig.stats;
35 // @see https://webpack.js.org/configuration/stats/
36 if (typeof stats === 'string') {
37 stats = presetToOptions(stats);
38 }
39 var outputOptions = Object.create(stats || {});
40 if(typeof options.modulesSort !== 'undefined') {
41 outputOptions.modulesSort = options.modulesSort;
42 }
43 if(typeof options.chunksSort !== 'undefined') {
44 outputOptions.chunksSort = options.chunksSort;
45 }
46 if(typeof options.assetsSort !== 'undefined') {
47 outputOptions.assetsSort = options.assetsSort;
48 }
49 if(typeof options.exclude !== 'undefined') {
50 outputOptions.exclude = options.exclude;
51 }
52 if(typeof options.colors !== 'undefined') {
53 outputOptions.colors = options.colors;
54 }
55 return outputOptions;
56}
57
58/**
59 * Create a single webpack build using the specified configuration.
60 * Calls the done callback once it has finished its work.
61 *
62 * @param {string} configuratorFileName The app configuration filename
63 * @param {Object} options The build options
64 * @param {boolean} options.watch If `true`, then the webpack watcher is being run; if `false`, runs only ones
65 * @param {boolean} options.json If `true`, then the webpack watcher will only report the result as JSON but not produce any other output
66 * @param {number} index The configuration index
67 * @param {number} expectedConfigLength
68 * @param {Function} done The callback that should be invoked once this worker has finished the build.
69 */
70module.exports = function(configuratorFileName, options, index, expectedConfigLength, done) {
71 if(options.argv) {
72 process.argv = options.argv;
73 }
74 chalk.enabled = options.colors;
75 var config = loadConfigurationFile(configuratorFileName)
76
77 Promise.resolve(config).then(function(config) {
78 var watch = !!options.watch,
79 silent = !!options.json;
80 if(expectedConfigLength !== 1 && !Array.isArray(config)
81 || (Array.isArray(config) && config.length !== expectedConfigLength)) {
82 if(config.length !== expectedConfigLength) {
83 var errorMessage = '[WEBPACK] There is a difference between the amount of the'
84 + ' provided configs. Maybe you where expecting command line'
85 + ' arguments to be passed to your webpack.config.js. If so,'
86 + " you'll need to separate them with a -- from the parallel-webpack options.";
87 console.error(errorMessage);
88 throw Error(errorMessage);
89 }
90 }
91 var webpackConfig;
92 if(Array.isArray(config)) {
93 webpackConfig = config[index];
94 } else {
95 webpackConfig = config
96 }
97
98 var MSG_ERROR = chalk.red('[WEBPACK]');
99 var MSG_SUCCESS = chalk.blue('[WEBPACK]');
100 var MSG_APP = chalk.yellow(getAppName(webpackConfig));
101
102 var watcher;
103 var webpack = getWebpack();
104 var hasCompletedOneCompile = false;
105 var outputOptions = getOutputOptions(webpackConfig, options);
106 var disconnected = false;
107
108 if(!silent) {
109 console.log('%s Started %s %s', MSG_SUCCESS, watch ? 'watching' : 'building', MSG_APP);
110 }
111
112 var compiler = webpack(webpackConfig);
113
114 if(watch || webpack.watch) {
115 watcher = compiler.watch(webpackConfig.watchOptions, finishedCallback);
116 } else {
117 compiler.run(finishedCallback);
118 }
119
120 process.on('SIGINT', shutdownCallback);
121 process.on('exit', exitCallback);
122 process.on('unhandledRejection', unhandledRejectionCallback);
123 process.on('disconnect', disconnectCallback);
124
125 function cleanup() {
126 process.removeListener('SIGINT', shutdownCallback);
127 process.removeListener('exit', exitCallback);
128 process.removeListener('unhandledRejection', unhandledRejectionCallback);
129 process.removeListener('disconnect', disconnectCallback);
130 }
131
132 function shutdownCallback() {
133 if(watcher) {
134 watcher.close(done);
135 }
136 done({
137 message: MSG_ERROR + ' Forcefully shut down ' + MSG_APP
138 });
139 process.exit(0);
140 }
141
142 function unhandledRejectionCallback(error) {
143 console.log(MSG_ERROR + 'Build child process error:', error);
144 process.exit(1);
145 }
146
147 function exitCallback(code) {
148 cleanup();
149 if (code === 0) {
150 return;
151 }
152 if(watcher) {
153 watcher.close(done);
154 }
155 done({
156 message: MSG_ERROR + ' Exit ' + MSG_APP + ' with code ' + code
157 });
158 }
159
160 function disconnectCallback(){
161 disconnected = true;
162 console.log('%s Parent process terminated, exit building %s', MSG_ERROR, MSG_APP);
163 process.exit(1);
164 }
165
166 function finishedCallback(err, stats) {
167 if(err) {
168 console.error('%s fatal error occured', MSG_ERROR);
169 console.error(err);
170 cleanup();
171 return done(err);
172 }
173 if(stats.compilation.errors && stats.compilation.errors.length) {
174 var message = MSG_ERROR + ' Errors building ' + MSG_APP + "\n"
175 + stats.compilation.errors.map(function(error) {
176 return error.message;
177 }).join("\n");
178 if(watch) {
179 console.log(message);
180 } else {
181 cleanup();
182 if (disconnected) {
183 return;
184 }
185 return done({
186 message: message,
187 stats: JSON.stringify(stats.toJson(outputOptions), null, 2)
188 });
189 }
190 }
191 if(!silent) {
192 if(options.stats) {
193 console.log(stats.toString(outputOptions));
194 }
195 var timeStamp = watch
196 ? ' ' + chalk.yellow(new Date().toTimeString().split(/ +/)[0])
197 : '';
198 console.log('%s Finished building %s within %s seconds', chalk.blue('[WEBPACK' + timeStamp + ']'), MSG_APP, chalk.blue((stats.endTime - stats.startTime) / 1000));
199 }
200 if(!watch) {
201 cleanup();
202 if (disconnected) {
203 return;
204 }
205 done(null, options.stats ? JSON.stringify(stats.toJson(outputOptions), null, 2) : '');
206 } else if (!hasCompletedOneCompile) {
207 notifyIPCWatchCompileDone(index);
208 hasCompletedOneCompile = true;
209 }
210 }
211 });
212};