UNPKG

3.31 kBJavaScriptView Raw
1const camelcase = require('camelcase');
2const capitalize = require('titleize');
3const merge = require('merge-options');
4const webpack = require('webpack');
5const weblog = require('webpack-log');
6
7function makeCallback(options) {
8 const { compiler, config, resolve, reject } = options;
9 const { reporter, watch } = config;
10
11 return (error, stats) => {
12 const log = weblog({ name: 'webpack', id: 'webpack-command' });
13 const Reporter = requireReporter(reporter || 'stylish');
14 const actualReporter = new Reporter({ compiler, config });
15
16 if (error || !watch) {
17 // for some reason webpack-cli invalidates the cache here. there's no
18 // documentation as to why, so we're going to assume there's a reason.
19 compiler.purgeInputFileSystem();
20 log.debug('Input File System Purged');
21 }
22
23 if (error) {
24 reject(error);
25 return;
26 }
27
28 const result = actualReporter.render(error, stats);
29
30 resolve(result);
31 };
32}
33
34/**
35 * Attempts to require a specified reporter. Tries the local built-ins first,
36 * and if that fails, attempts to load an npm module that exports a reporter
37 * handler function.
38 */
39function requireReporter(name, local = true) {
40 const prefix = capitalize(camelcase(name));
41 const target = local ? `./reporters/${prefix}Reporter` : name;
42
43 try {
44 // eslint-disable-next-line import/no-dynamic-require, global-require
45 const result = require(target);
46 return result;
47 } catch (e) {
48 if (local) {
49 return requireReporter(name, false);
50 }
51
52 return null;
53 }
54}
55
56/**
57 * Removes invalid webpack config properties leftover from applying flags
58 */
59function sanitize(config) {
60 const configs = [].concat(config).map((conf) => {
61 const result = merge({}, conf);
62 delete result.reporter;
63 delete result.watchStdin;
64
65 // TODO: remove the need for this
66 for (const property of ['entry', 'output']) {
67 const target = result[property];
68 if (target && Object.keys(target).length === 0) {
69 delete result[property];
70 }
71 }
72
73 return result;
74 });
75
76 // if we always return an array, every compilation will be a MultiCompiler
77 return configs.length > 1 ? configs : configs[0];
78}
79
80module.exports = (config) => {
81 const log = weblog({ name: 'webpack', id: 'webpack-command' });
82 const target = sanitize(config);
83 const compiler = webpack(target);
84 const { done, run } = compiler.hooks;
85
86 run.tap('WebpackCommand', () => {
87 log.info('Starting Build');
88 });
89
90 done.tap('WebpackCommand', () => {
91 log.info('Build Finished');
92 });
93
94 return {
95 run() {
96 return new Promise((resolve, reject) => {
97 const configs = [].concat(config);
98 const [first] = configs;
99 const { watchOptions } = first;
100 const { stdin } = watchOptions || {};
101 const callback = makeCallback({
102 compiler,
103 config: first,
104 resolve,
105 reject,
106 });
107
108 if (first.watch) {
109 log.info('Watching enabled');
110 compiler.watch(watchOptions, callback);
111 } else {
112 compiler.run(callback);
113 }
114
115 /* istanbul ignore if */
116 if (stdin) {
117 process.stdin.on('end', () => {
118 process.exit();
119 });
120
121 process.stdin.resume();
122 }
123 });
124 },
125 };
126};