Skip to content

Commit 258219a

Browse files
fix: watch mode and options (#1931)
1 parent 6f95b26 commit 258219a

18 files changed

+550
-912
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
"strip-ansi": "^6.0.0",
9090
"ts-jest": "^25.5.1",
9191
"typescript": "^3.9.7",
92-
"webpack": "^4.44.2",
92+
"webpack": "^5.0.0",
9393
"webpack-bundle-analyzer": "^3.9.0",
9494
"webpack-dev-server": "3.10.3",
9595
"yeoman-test": "^2.7.0"

packages/webpack-cli/lib/utils/Compiler.js

Lines changed: 100 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -2,134 +2,29 @@ const { packageExists } = require('./package-exists');
22
const webpack = packageExists('webpack') ? require('webpack') : undefined;
33
const logger = require('./logger');
44
const { writeFileSync } = require('fs');
5-
const bailAndWatchWarning = require('./warnings/bailAndWatchWarning');
6-
7-
const assignWatchHooks = (compiler) => {
8-
compiler.hooks.watchRun.tap('watchInfo', (compilation) => {
9-
const compilationName = compilation.name || '';
10-
logger.raw(`\nCompilation ${compilationName} starting…\n`);
11-
});
12-
compiler.hooks.done.tap('watchInfo', (compilation) => {
13-
const compilationName = compilation.name || '';
14-
logger.raw(`\nCompilation ${compilationName} finished\n`);
15-
});
16-
};
17-
18-
const watchInfo = (compiler) => {
19-
if (compiler.compilers) {
20-
compiler.compilers.map((comp) => {
21-
assignWatchHooks(comp);
22-
});
23-
} else {
24-
assignWatchHooks(compiler);
25-
}
26-
};
275

286
class Compiler {
297
constructor() {
308
this.compilerOptions = {};
319
}
32-
setUpHookForCompilation(compilation, outputOptions, options) {
33-
const { ProgressPlugin } = webpack;
34-
let progressPluginExists;
35-
if (options.plugins) {
36-
progressPluginExists = options.plugins.find((e) => e instanceof ProgressPlugin);
37-
}
38-
39-
compilation.hooks.beforeRun.tap('webpackProgress', () => {
40-
if (outputOptions.progress) {
41-
if (!progressPluginExists) {
42-
new ProgressPlugin().apply(compilation);
43-
} else {
44-
if (!progressPluginExists.handler) {
45-
options.plugins = options.plugins.filter((e) => e !== progressPluginExists);
46-
Object.keys(progressPluginExists).map((opt) => {
47-
ProgressPlugin.defaultOptions[opt] = progressPluginExists[opt];
48-
});
49-
new ProgressPlugin().apply(compilation);
50-
} else {
51-
progressPluginExists.apply(compilation);
52-
}
53-
}
54-
}
55-
});
56-
}
57-
58-
compilerCallback(error, stats, lastHash, options, outputOptions) {
59-
if (error) {
60-
lastHash = null;
61-
logger.error(error);
62-
process.exit(1);
63-
}
64-
65-
if (!outputOptions.watch && stats.hasErrors()) {
66-
process.exitCode = 1;
67-
}
68-
69-
if (stats.hash !== lastHash) {
70-
lastHash = stats.hash;
71-
72-
if (outputOptions.json === true) {
73-
process.stdout.write(JSON.stringify(stats.toJson(outputOptions), null, 2) + '\n');
74-
} else if (typeof outputOptions.json === 'string') {
75-
const JSONStats = JSON.stringify(stats.toJson(outputOptions), null, 2);
76-
77-
try {
78-
writeFileSync(outputOptions.json, JSONStats);
79-
logger.success(`stats are successfully stored as json to ${outputOptions.json}`);
80-
} catch (err) {
81-
logger.error(err);
82-
}
83-
} else {
84-
logger.raw(`${stats.toString(this.compilerOptions.stats)}\n`);
85-
}
86-
87-
if (outputOptions.watch) {
88-
logger.info('watching files for updates...');
89-
}
90-
}
91-
}
92-
93-
async invokeCompilerInstance(lastHash, options, outputOptions) {
94-
// eslint-disable-next-line no-async-promise-executor
95-
return new Promise(async (resolve) => {
96-
await this.compiler.run((err, stats) => {
97-
if (this.compiler.close) {
98-
this.compiler.close(() => {
99-
this.compilerCallback(err, stats, lastHash, options, outputOptions);
100-
101-
resolve();
102-
});
103-
} else {
104-
this.compilerCallback(err, stats, lastHash, options, outputOptions);
105-
106-
resolve();
107-
}
108-
});
109-
});
110-
}
111-
112-
async invokeWatchInstance(lastHash, options, outputOptions, watchOptions) {
113-
return this.compiler.watch(watchOptions, (err, stats) => {
114-
this.compilerCallback(err, stats, lastHash, options, outputOptions);
115-
});
116-
}
11710

11811
async createCompiler(options) {
11912
try {
12013
this.compiler = await webpack(options);
12114
this.compilerOptions = options;
122-
} catch (err) {
15+
} catch (error) {
12316
// https://github.com/webpack/webpack/blob/master/lib/index.js#L267
12417
// https://github.com/webpack/webpack/blob/v4.44.2/lib/webpack.js#L90
12518
const ValidationError = webpack.ValidationError ? webpack.ValidationError : webpack.WebpackOptionsValidationError;
19+
12620
// In case of schema errors print and exit process
12721
// For webpack@4 and webpack@5
128-
if (err instanceof ValidationError) {
129-
logger.error(`\n${err.message}`);
22+
if (error instanceof ValidationError) {
23+
logger.error(error.message);
13024
} else {
131-
logger.error(`\n${err}`);
25+
logger.error(error);
13226
}
27+
13328
process.exit(2);
13429
}
13530
}
@@ -140,39 +35,115 @@ class Compiler {
14035

14136
async webpackInstance(opts) {
14237
const { outputOptions, options } = opts;
143-
const lastHash = null;
144-
145-
const { ProgressPlugin } = webpack;
146-
if (options.plugins) {
147-
options.plugins = options.plugins.filter((e) => e instanceof ProgressPlugin);
148-
}
14938

15039
if (outputOptions.interactive) {
15140
const interactive = require('./interactive');
41+
15242
return interactive(options, outputOptions);
15343
}
154-
if (this.compiler.compilers) {
155-
this.compiler.compilers.forEach((comp, idx) => {
156-
bailAndWatchWarning(comp); //warn the user if bail and watch both are used together
157-
this.setUpHookForCompilation(comp, outputOptions, options[idx]);
44+
45+
const compilers = this.compiler.compilers ? this.compiler.compilers : [this.compiler];
46+
const isWatchMode = Boolean(compilers.find((compiler) => compiler.options.watch));
47+
const isRawOutput = typeof outputOptions.json === 'undefined';
48+
49+
if (isRawOutput) {
50+
for (const compiler of compilers) {
51+
if (outputOptions.progress) {
52+
const { ProgressPlugin } = webpack;
53+
54+
let progressPluginExists;
55+
56+
if (compiler.options.plugins) {
57+
progressPluginExists = Boolean(compiler.options.plugins.find((e) => e instanceof ProgressPlugin));
58+
}
59+
60+
if (!progressPluginExists) {
61+
new ProgressPlugin().apply(compiler);
62+
}
63+
}
64+
}
65+
66+
this.compiler.hooks.watchRun.tap('watchInfo', (compilation) => {
67+
if (compilation.options.bail && isWatchMode) {
68+
logger.warn('You are using "bail" with "watch". "bail" will still exit webpack when the first error is found.');
69+
}
70+
71+
logger.success(`Compilation${compilation.name ? `${compilation.name}` : ''} starting...`);
72+
});
73+
this.compiler.hooks.done.tap('watchInfo', (compilation) => {
74+
logger.success(`Compilation${compilation.name ? `${compilation.name}` : ''} finished`);
15875
});
159-
} else {
160-
bailAndWatchWarning(this.compiler);
161-
this.setUpHookForCompilation(this.compiler, outputOptions, options);
16276
}
16377

164-
if (outputOptions.watch) {
165-
const watchOptions = outputOptions.watchOptions || {};
78+
const callback = (error, stats) => {
79+
if (error) {
80+
logger.error(error);
81+
process.exit(1);
82+
}
83+
84+
if (stats.hasErrors()) {
85+
process.exitCode = 1;
86+
}
87+
88+
const foundStats = this.compiler.compilers
89+
? { children: this.compiler.compilers.map((compiler) => compiler.options.stats) }
90+
: this.compiler.options.stats;
91+
92+
if (outputOptions.json === true) {
93+
process.stdout.write(JSON.stringify(stats.toJson(foundStats), null, 2) + '\n');
94+
} else if (typeof outputOptions.json === 'string') {
95+
const JSONStats = JSON.stringify(stats.toJson(foundStats), null, 2);
96+
97+
try {
98+
writeFileSync(outputOptions.json, JSONStats);
99+
logger.success(`stats are successfully stored as json to ${outputOptions.json}`);
100+
} catch (error) {
101+
logger.error(error);
102+
103+
process.exit(2);
104+
}
105+
} else {
106+
logger.raw(`${stats.toString(foundStats)}`);
107+
}
108+
109+
if (isWatchMode) {
110+
logger.success('watching files for updates...');
111+
}
112+
};
113+
114+
if (isWatchMode) {
115+
const watchOptions = (this.compiler.options && this.compiler.options.watchOptions) || {};
116+
166117
if (watchOptions.stdin) {
167118
process.stdin.on('end', function () {
168119
process.exit();
169120
});
170121
process.stdin.resume();
171122
}
172-
watchInfo(this.compiler);
173-
await this.invokeWatchInstance(lastHash, options, outputOptions, watchOptions);
123+
124+
return new Promise((resolve) => {
125+
this.compiler.watch(watchOptions, (error, stats) => {
126+
callback(error, stats);
127+
128+
resolve();
129+
});
130+
});
174131
} else {
175-
return await this.invokeCompilerInstance(lastHash, options, outputOptions);
132+
return new Promise((resolve) => {
133+
this.compiler.run((error, stats) => {
134+
if (this.compiler.close) {
135+
this.compiler.close(() => {
136+
callback(error, stats);
137+
138+
resolve();
139+
});
140+
} else {
141+
callback(error, stats);
142+
143+
resolve();
144+
}
145+
});
146+
});
176147
}
177148
}
178149
}

packages/webpack-cli/lib/utils/warnings/bailAndWatchWarning.js

Lines changed: 0 additions & 14 deletions
This file was deleted.

test/analyze/analyze-flag.test.js

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,19 @@
11
'use strict';
22

33
const { runAndGetWatchProc } = require('../utils/test-utils');
4-
const { writeFileSync } = require('fs');
5-
const { resolve } = require('path');
64

75
describe('--analyze flag', () => {
86
it('should load webpack-bundle-analyzer plugin with --analyze flag', (done) => {
9-
const proc = runAndGetWatchProc(__dirname, ['--analyze'], false, '', true);
10-
let semaphore = 1;
7+
const proc = runAndGetWatchProc(__dirname, ['--analyze', '--watch'], false, '', true);
8+
119
proc.stdout.on('data', (chunk) => {
1210
const data = chunk.toString();
13-
if (semaphore === 1 && data.includes('BundleAnalyzerPlugin')) {
14-
writeFileSync(resolve(__dirname, './src/main.js'), `console.log('analyze flag test');`);
15-
semaphore--;
16-
return;
17-
}
18-
if (semaphore === 0) {
19-
expect(data).toContain('Webpack Bundle Analyzer is started at http://127.0.0.1:8888');
20-
semaphore--;
11+
12+
if (data.includes('Webpack Bundle Analyzer is started at')) {
13+
expect(data).toContain('Webpack Bundle Analyzer is started at');
14+
2115
proc.kill();
2216
done();
23-
return;
2417
}
2518
});
2619
});

test/bail/bail-and-watch-warning.test.js

Lines changed: 0 additions & 26 deletions
This file was deleted.
File renamed without changes.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module.exports = [
2+
{
3+
output: {
4+
filename: './dist-first.js',
5+
},
6+
name: 'first',
7+
entry: './src/first.js',
8+
mode: 'development',
9+
bail: true,
10+
},
11+
{
12+
output: {
13+
filename: './dist-second.js',
14+
},
15+
name: 'second',
16+
entry: './src/second.js',
17+
mode: 'production',
18+
},
19+
];

0 commit comments

Comments
 (0)