Skip to content

Commit 2d1e001

Browse files
fix: respect --watch-options-stdin
1 parent 05636b7 commit 2d1e001

24 files changed

Lines changed: 410 additions & 113 deletions

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
"strip-ansi": "^6.0.0",
9191
"ts-jest": "^26.4.3",
9292
"typescript": "^3.9.7",
93-
"webpack": "^5.10.0",
93+
"webpack": "^5.11.0",
9494
"webpack-bundle-analyzer": "^3.9.0",
9595
"webpack-dev-server": "^3.11.0",
9696
"yeoman-test": "^2.7.0"

packages/serve/src/index.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,38 @@ class ServeCommand {
8282

8383
const compiler = await cli.createCompiler(webpackOptions);
8484

85+
if (!compiler) {
86+
return;
87+
}
88+
89+
let servers;
90+
91+
if (cli.needWatchStdin(compiler) || devServerOptions.stdin) {
92+
// TODO
93+
// Compatibility with old `stdin` option for `webpack-dev-server`
94+
// Should be removed for the next major release on both sides
95+
if (devServerOptions.stdin) {
96+
delete devServerOptions.stdin;
97+
}
98+
99+
process.stdin.on('end', () => {
100+
Promise.all(
101+
servers.map((server) => {
102+
return new Promise((resolve) => {
103+
server.close(() => {
104+
resolve();
105+
});
106+
});
107+
}),
108+
).then(() => {
109+
process.exit(0);
110+
});
111+
});
112+
process.stdin.resume();
113+
}
114+
85115
try {
86-
await startDevServer(compiler, devServerOptions, logger);
116+
servers = await startDevServer(compiler, devServerOptions, logger);
87117
} catch (error) {
88118
if (error.name === 'ValidationError') {
89119
logger.error(error.message);

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const minimumHelpFlags = [
88
'env',
99
'mode',
1010
'watch',
11+
'watch-options-stdin',
1112
'stats',
1213
'devtool',
1314
'entry',
@@ -151,6 +152,13 @@ const builtInFlags = [
151152
description: 'Watch for files changes.',
152153
negatedDescription: 'Do not watch for file changes.',
153154
},
155+
{
156+
name: 'watch-options-stdin',
157+
usage: '--watch-options-stdin',
158+
type: Boolean,
159+
negative: true,
160+
description: 'Stop watching when stdin stream has ended.',
161+
},
154162
];
155163

156164
// Extract all the flags being exported from core.

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

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,13 @@ class WebpackCLI {
10211021
configOptions.watch = options.watch;
10221022
}
10231023

1024+
if (typeof options.watchOptionsStdin !== 'undefined') {
1025+
configOptions.watchOptions = {
1026+
...configOptions.watchOptions,
1027+
...{ stdin: options.watchOptionsStdin },
1028+
};
1029+
}
1030+
10241031
return configOptions;
10251032
};
10261033

@@ -1058,6 +1065,14 @@ class WebpackCLI {
10581065
return config;
10591066
}
10601067

1068+
needWatchStdin(compiler) {
1069+
if (compiler.compilers) {
1070+
return compiler.compilers.some((compiler) => compiler.options.watchOptions && compiler.options.watchOptions.stdin);
1071+
}
1072+
1073+
return compiler.options.watchOptions && compiler.options.watchOptions.stdin;
1074+
}
1075+
10611076
async createCompiler(options, callback) {
10621077
const isValidationError = (error) => {
10631078
// https://github.com/webpack/webpack/blob/master/lib/index.js#L267
@@ -1098,6 +1113,11 @@ class WebpackCLI {
10981113
process.exit(2);
10991114
}
11001115

1116+
// TODO webpack@4 return Watching and MultiWatching instead Compiler and MultiCompiler, remove this after drop webpack@4
1117+
if (compiler && compiler.compiler) {
1118+
compiler = compiler.compiler;
1119+
}
1120+
11011121
return compiler;
11021122
}
11031123

@@ -1114,9 +1134,11 @@ class WebpackCLI {
11141134
process.exitCode = 1;
11151135
}
11161136

1137+
// TODO remove after drop webpack@4
1138+
const statsForWebpack4 = webpack.Stats && webpack.Stats.presetToOptions;
1139+
11171140
const getStatsOptions = (stats) => {
1118-
// TODO remove after drop webpack@4
1119-
if (webpack.Stats && webpack.Stats.presetToOptions) {
1141+
if (statsForWebpack4) {
11201142
if (!stats) {
11211143
stats = {};
11221144
} else if (typeof stats === 'boolean' || typeof stats === 'string') {
@@ -1144,32 +1166,42 @@ class WebpackCLI {
11441166
return stats;
11451167
};
11461168

1147-
const getStatsOptionsFromCompiler = (compiler) => getStatsOptions(compiler.options ? compiler.options.stats : undefined);
1148-
11491169
if (!compiler) {
11501170
return;
11511171
}
11521172

11531173
const foundStats = compiler.compilers
1154-
? { children: compiler.compilers.map(getStatsOptionsFromCompiler) }
1155-
: getStatsOptionsFromCompiler(compiler);
1156-
const handleWriteError = (error) => {
1157-
logger.error(error);
1158-
process.exit(2);
1159-
};
1174+
? {
1175+
children: compiler.compilers.map((compiler) =>
1176+
getStatsOptions(compiler.options ? compiler.options.stats : undefined),
1177+
),
1178+
}
1179+
: getStatsOptions(compiler.options ? compiler.options.stats : undefined);
1180+
1181+
// TODO webpack@4 doesn't support `{ children: [{ colors: true }, { colors: true }] }` for stats
1182+
if (statsForWebpack4 && compiler.compilers) {
1183+
foundStats.colors = foundStats.children.some((child) => child.colors);
1184+
}
1185+
1186+
if (options.json) {
1187+
const handleWriteError = (error) => {
1188+
logger.error(error);
1189+
process.exit(2);
1190+
};
11601191

1161-
if (options.json === true) {
1162-
createJsonStringifyStream(stats.toJson(foundStats))
1163-
.on('error', handleWriteError)
1164-
.pipe(process.stdout)
1165-
.on('error', handleWriteError)
1166-
.on('close', () => process.stdout.write('\n'));
1167-
} else if (typeof options.json === 'string') {
1168-
createJsonStringifyStream(stats.toJson(foundStats))
1169-
.on('error', handleWriteError)
1170-
.pipe(createWriteStream(options.json))
1171-
.on('error', handleWriteError)
1172-
.on('close', () => logger.success(`stats are successfully stored as json to ${options.json}`));
1192+
if (options.json === true) {
1193+
createJsonStringifyStream(stats.toJson(foundStats))
1194+
.on('error', handleWriteError)
1195+
.pipe(process.stdout)
1196+
.on('error', handleWriteError)
1197+
.on('close', () => process.stdout.write('\n'));
1198+
} else {
1199+
createJsonStringifyStream(stats.toJson(foundStats))
1200+
.on('error', handleWriteError)
1201+
.pipe(createWriteStream(options.json))
1202+
.on('error', handleWriteError)
1203+
.on('close', () => logger.success(`stats are successfully stored as json to ${options.json}`));
1204+
}
11731205
} else {
11741206
const printedStats = stats.toString(foundStats);
11751207

@@ -1184,9 +1216,18 @@ class WebpackCLI {
11841216

11851217
compiler = await this.createCompiler(options, callback);
11861218

1187-
// TODO webpack@4 return Watching and MultiWathing instead Compiler and MultiCompiler, remove this after drop webpack@4
1188-
if (compiler && compiler.compiler) {
1189-
compiler = compiler.compiler;
1219+
if (!compiler) {
1220+
return;
1221+
}
1222+
1223+
const isWatch = (compiler) =>
1224+
compiler.compilers ? compiler.compilers.some((compiler) => compiler.options.watch) : compiler.options.watch;
1225+
1226+
if (isWatch(compiler) && this.needWatchStdin(compiler)) {
1227+
process.stdin.on('end', () => {
1228+
process.exit(0);
1229+
});
1230+
process.stdin.resume();
11901231
}
11911232
}
11921233
}

test/config/multiple/multiple-config.test.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const stripAnsi = require('strip-ansi');
2+
13
const { run } = require('../../utils/test-utils');
24

35
describe('Multiple config flag: ', () => {
@@ -8,7 +10,7 @@ describe('Multiple config flag: ', () => {
810
expect(exitCode).toEqual(0);
911
expect(stderr).toBeFalsy();
1012
// Should spawn multiple compilers
11-
expect(stdout).toContain('amd:');
12-
expect(stdout).toContain('commonjs:');
13+
expect(stripAnsi(stdout)).toContain('amd:');
14+
expect(stripAnsi(stdout)).toContain('commonjs:');
1315
});
1416
});

test/core-flags/watch-flags.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ describe('watch config related flag', () => {
1111
const property = flag.name.split('watch-options-')[1];
1212
const propName = hyphenToUpperCase(property);
1313

14-
if (flag.type === Boolean && flag.name !== 'watch') {
14+
if (flag.type === Boolean && flag.name !== 'watch' && flag.name !== 'watch-options-stdin') {
1515
it(`should config --${flag.name} correctly`, () => {
1616
const { exitCode, stderr, stdout } = run(__dirname, [`--${flag.name}`]);
1717

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'use strict';
2+
3+
const { run } = require('../../utils/test-utils');
4+
5+
describe('error', () => {
6+
it('should log error with stacktrace', async () => {
7+
const { exitCode, stderr, stdout } = await run(__dirname);
8+
9+
expect(exitCode).toBe(2);
10+
expect(stderr).toContain('Error: test');
11+
expect(stderr).toMatch(/at .+ (.+)/);
12+
expect(stdout).toBeFalsy();
13+
});
14+
15+
it('should log error with stacktrace using the "bundle" command', async () => {
16+
const { exitCode, stderr, stdout } = await run(__dirname, ['bundle']);
17+
18+
expect(exitCode).toBe(2);
19+
expect(stderr).toContain('Error: test');
20+
expect(stderr).toMatch(/at .+ (.+)/);
21+
expect(stdout).toBeFalsy();
22+
});
23+
24+
it('should log error with stacktrace using the "serve" command', async () => {
25+
const { exitCode, stderr, stdout } = await run(__dirname, ['serve']);
26+
27+
expect(exitCode).toBe(2);
28+
expect(stderr).toContain('Error: test');
29+
expect(stderr).toMatch(/at .+ (.+)/);
30+
expect(stdout).toBeFalsy();
31+
});
32+
});
File renamed without changes.

test/error/error.test.js

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

0 commit comments

Comments
 (0)