UNPKG

3.48 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
7const progress = require('./progress');
8
9function makeCallback(options) {
10 const { compiler, config, reporter, resolve, reject } = options;
11 const { watch } = config;
12
13 return (error, stats) => {
14 const log = weblog({ name: 'webpack', id: 'webpack-command' });
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 = reporter.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.progress;
63 delete result.reporter;
64 delete result.watchStdin;
65
66 // TODO: remove the need for this
67 for (const property of ['entry', 'output']) {
68 const target = result[property];
69 if (target && Object.keys(target).length === 0) {
70 delete result[property];
71 }
72 }
73
74 return result;
75 });
76
77 // if we always return an array, every compilation will be a MultiCompiler
78 return configs.length > 1 ? configs : configs[0];
79}
80
81module.exports = (config) => {
82 const log = weblog({ name: 'webpack', id: 'webpack-command' });
83 const target = sanitize(config);
84 const compiler = webpack(target);
85 const { done, run } = compiler.hooks;
86 const configs = [].concat(config);
87 const [first] = configs;
88 const { reporter: reporterName, watchOptions } = first;
89 const ReporterClass = requireReporter(reporterName || 'stylish');
90 const reporter = new ReporterClass({ compiler, config });
91
92 if (first.progress) {
93 progress.apply(first, compiler, reporter);
94 }
95
96 run.tap('WebpackCommand', () => {
97 log.info('Starting Build');
98 });
99
100 done.tap('WebpackCommand', () => {
101 log.info('Build Finished');
102 });
103
104 return {
105 run() {
106 return new Promise((resolve, reject) => {
107 const { stdin } = watchOptions || {};
108 const callback = makeCallback({
109 compiler,
110 config: first,
111 reporter,
112 resolve,
113 reject,
114 });
115
116 if (first.watch) {
117 log.info('Watching enabled');
118 compiler.watch(watchOptions, callback);
119 } else {
120 compiler.run(callback);
121 }
122
123 /* istanbul ignore if */
124 if (stdin) {
125 process.stdin.on('end', () => {
126 process.exit();
127 });
128
129 process.stdin.resume();
130 }
131 });
132 },
133 };
134};