Skip to content

Commit 479724c

Browse files
authored
Merge branch 'master' into ws-string-serverMode-option
2 parents 34f3219 + 56274e4 commit 479724c

12 files changed

+286
-27
lines changed

bin/options.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ const options = {
3737
describe:
3838
'Inline mode (set to false to disable including client scripts like livereload)',
3939
},
40+
profile: {
41+
type: 'boolean',
42+
describe: 'Print compilation profile data for progress steps',
43+
},
4044
progress: {
4145
type: 'boolean',
4246
describe: 'Print compilation progress in percentage',

bin/webpack-dev-server.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,6 @@ function startDevServer(config, options) {
9797
throw err;
9898
}
9999

100-
if (options.progress) {
101-
new webpack.ProgressPlugin({
102-
profile: argv.profile,
103-
}).apply(compiler);
104-
}
105-
106100
try {
107101
server = new Server(compiler, options, log);
108102
} catch (err) {

lib/Server.js

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const serveIndex = require('serve-index');
2323
const webpack = require('webpack');
2424
const webpackDevMiddleware = require('webpack-dev-middleware');
2525
const validateOptions = require('schema-utils');
26+
const isAbsoluteUrl = require('is-absolute-url');
2627
const updateCompiler = require('./utils/updateCompiler');
2728
const createLogger = require('./utils/createLogger');
2829
const getCertificate = require('./utils/getCertificate');
@@ -153,23 +154,25 @@ class Server {
153154
}
154155

155156
setupProgressPlugin() {
156-
const progressPlugin = new webpack.ProgressPlugin(
157-
(percent, msg, addInfo) => {
158-
percent = Math.floor(percent * 100);
157+
// for CLI output
158+
new webpack.ProgressPlugin({
159+
profile: !!this.options.profile,
160+
}).apply(this.compiler);
159161

160-
if (percent === 100) {
161-
msg = 'Compilation completed';
162-
}
162+
// for browser console output
163+
new webpack.ProgressPlugin((percent, msg, addInfo) => {
164+
percent = Math.floor(percent * 100);
163165

164-
if (addInfo) {
165-
msg = `${msg} (${addInfo})`;
166-
}
166+
if (percent === 100) {
167+
msg = 'Compilation completed';
168+
}
167169

168-
this.sockWrite(this.sockets, 'progress-update', { percent, msg });
170+
if (addInfo) {
171+
msg = `${msg} (${addInfo})`;
169172
}
170-
);
171173

172-
progressPlugin.apply(this.compiler);
174+
this.sockWrite(this.sockets, 'progress-update', { percent, msg });
175+
}).apply(this.compiler);
173176
}
174177

175178
setupApp() {
@@ -356,7 +359,7 @@ class Server {
356359
contentBase.forEach((item) => {
357360
this.app.get('*', express.static(item));
358361
});
359-
} else if (/^(https?:)?\/\//.test(contentBase)) {
362+
} else if (isAbsoluteUrl(String(contentBase))) {
360363
this.log.warn(
361364
'Using a URL as contentBase is deprecated and will be removed in the next major version. Please use the proxy option instead.'
362365
);
@@ -408,8 +411,8 @@ class Server {
408411
this.app.get('*', serveIndex(item));
409412
});
410413
} else if (
411-
!/^(https?:)?\/\//.test(contentBase) &&
412-
typeof contentBase !== 'number'
414+
typeof contentBase !== 'number' &&
415+
!isAbsoluteUrl(String(contentBase))
413416
) {
414417
this.app.get('*', serveIndex(contentBase));
415418
}
@@ -418,13 +421,13 @@ class Server {
418421
setupWatchStaticFeature() {
419422
const contentBase = this.options.contentBase;
420423

421-
if (
422-
/^(https?:)?\/\//.test(contentBase) ||
423-
typeof contentBase === 'number'
424-
) {
424+
if (isAbsoluteUrl(String(contentBase)) || typeof contentBase === 'number') {
425425
throw new Error('Watching remote files is not supported.');
426426
} else if (Array.isArray(contentBase)) {
427427
contentBase.forEach((item) => {
428+
if (isAbsoluteUrl(String(item))) {
429+
throw new Error('Watching remote files is not supported.');
430+
}
428431
this._watch(item);
429432
});
430433
} else {

lib/options.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,9 @@
258258
}
259259
]
260260
},
261+
"profile": {
262+
"type": "boolean"
263+
},
261264
"progress": {
262265
"type": "boolean"
263266
},
@@ -426,6 +429,7 @@
426429
"pfx": "should be {String|Buffer} (https://webpack.js.org/configuration/dev-server/#devserverpfx)",
427430
"pfxPassphrase": "should be {String} (https://webpack.js.org/configuration/dev-server/#devserverpfxpassphrase)",
428431
"port": "should be {Number|String|Null} (https://webpack.js.org/configuration/dev-server/#devserverport)",
432+
"profile": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverprofile)",
429433
"progress": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverprogress---cli-only)",
430434
"proxy": "should be {Object|Array} (https://webpack.js.org/configuration/dev-server/#devserverproxy)",
431435
"public": "should be {String} (https://webpack.js.org/configuration/dev-server/#devserverpublic)",

lib/utils/createConfig.js

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

33
const path = require('path');
4+
const isAbsoluteUrl = require('is-absolute-url');
45
const defaultTo = require('./defaultTo');
56

67
function createConfig(config, argv, { port }) {
@@ -46,6 +47,10 @@ function createConfig(config, argv, { port }) {
4647
options.liveReload = false;
4748
}
4849

50+
if (argv.profile) {
51+
options.profile = argv.profile;
52+
}
53+
4954
if (argv.progress) {
5055
options.progress = argv.progress;
5156
}
@@ -60,7 +65,7 @@ function createConfig(config, argv, { port }) {
6065
(firstWpOpt.output && firstWpOpt.output.publicPath) || '';
6166

6267
if (
63-
!/^(https?:)?\/\//.test(options.publicPath) &&
68+
!isAbsoluteUrl(String(options.publicPath)) &&
6469
options.publicPath[0] !== '/'
6570
) {
6671
options.publicPath = `/${options.publicPath}`;
@@ -109,7 +114,7 @@ function createConfig(config, argv, { port }) {
109114
options.contentBase = options.contentBase.map((p) => path.resolve(p));
110115
} else if (/^[0-9]$/.test(options.contentBase)) {
111116
options.contentBase = +options.contentBase;
112-
} else if (!/^(https?:)?\/\//.test(options.contentBase)) {
117+
} else if (!isAbsoluteUrl(String(options.contentBase))) {
113118
options.contentBase = path.resolve(options.contentBase);
114119
}
115120
}

test/cli/cli.test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,23 @@ describe('CLI', () => {
2020
.then((output) => {
2121
expect(output.code).toEqual(0);
2222
expect(output.stderr.includes('0% compiling')).toBe(true);
23+
// should not profile
24+
expect(
25+
output.stderr.includes('ms after chunk modules optimization')
26+
).toBe(false);
27+
done();
28+
})
29+
.catch(done);
30+
});
31+
32+
it('--progress --profile', (done) => {
33+
testBin('--progress --profile')
34+
.then((output) => {
35+
expect(output.code).toEqual(0);
36+
// should profile
37+
expect(
38+
output.stderr.includes('ms after chunk modules optimization')
39+
).toBe(true);
2340
done();
2441
})
2542
.catch(done);

test/e2e/Progress.test.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
'use strict';
2+
3+
/* eslint-disable
4+
no-undef
5+
*/
6+
const fs = require('fs');
7+
const { resolve } = require('path');
8+
const testServer = require('../helpers/test-server');
9+
const reloadConfig = require('../fixtures/reload-config/webpack.config');
10+
const runBrowser = require('../helpers/run-browser');
11+
const port = require('../ports-map').Progress;
12+
13+
const cssFilePath = resolve(__dirname, '../fixtures/reload-config/main.css');
14+
15+
describe('client progress', () => {
16+
describe('using hot', () => {
17+
beforeAll((done) => {
18+
fs.writeFileSync(
19+
cssFilePath,
20+
'body { background-color: rgb(0, 0, 255); }'
21+
);
22+
const options = {
23+
port,
24+
host: '0.0.0.0',
25+
inline: true,
26+
hot: true,
27+
progress: true,
28+
watchOptions: {
29+
poll: 500,
30+
},
31+
};
32+
testServer.startAwaitingCompilation(reloadConfig, options, done);
33+
});
34+
35+
afterAll((done) => {
36+
fs.unlinkSync(cssFilePath);
37+
testServer.close(done);
38+
});
39+
40+
describe('on browser client', () => {
41+
it('should console.log progress', (done) => {
42+
runBrowser().then(({ page, browser }) => {
43+
const res = [];
44+
page.waitForNavigation({ waitUntil: 'load' }).then(() => {
45+
fs.writeFileSync(
46+
cssFilePath,
47+
'body { background-color: rgb(255, 0, 0); }'
48+
);
49+
page.waitFor(10000).then(() => {
50+
browser.close().then(() => {
51+
// check that there is some percentage progress output
52+
const regExp = /^\[WDS\] [0-9]{1,3}% - /;
53+
const match = res.find((line) => {
54+
return regExp.test(line);
55+
});
56+
// eslint-disable-next-line no-undefined
57+
expect(match).not.toEqual(undefined);
58+
done();
59+
});
60+
});
61+
});
62+
63+
page.goto(`http://localhost:${port}/main`);
64+
page.on('console', (data) => {
65+
res.push(data.text());
66+
});
67+
});
68+
});
69+
});
70+
});
71+
});

test/options.test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,10 @@ describe('options', () => {
309309
success: ['', 0, null],
310310
failure: [false],
311311
},
312+
profile: {
313+
success: [false],
314+
failure: [''],
315+
},
312316
progress: {
313317
success: [false],
314318
failure: [''],

test/ports-map.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ const portsList = {
4040
WebsocketServer: 1,
4141
ClientMode: 1,
4242
'clientMode-option': 1,
43+
Progress: 1,
44+
'progress-option': 1,
45+
'profile-option': 1,
4346
};
4447

4548
let startPort = 8079;

test/server/contentBase-option.test.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,56 @@ describe('contentBase option', () => {
267267
});
268268
});
269269

270+
describe('testing single & multiple external paths', () => {
271+
afterAll((done) => {
272+
testServer.close(() => {
273+
done();
274+
});
275+
});
276+
it('Should throw exception (string)', (done) => {
277+
try {
278+
// eslint-disable-next-line no-unused-vars
279+
server = testServer.start(config, {
280+
contentBase: 'https://example.com/',
281+
watchContentBase: true,
282+
});
283+
284+
expect(true).toBe(false);
285+
} catch (e) {
286+
expect(e.message).toBe('Watching remote files is not supported.');
287+
done();
288+
}
289+
});
290+
it('Should throw exception (number)', (done) => {
291+
try {
292+
// eslint-disable-next-line no-unused-vars
293+
server = testServer.start(config, {
294+
contentBase: 2,
295+
watchContentBase: true,
296+
});
297+
298+
expect(true).toBe(false);
299+
} catch (e) {
300+
expect(e.message).toBe('Watching remote files is not supported.');
301+
done();
302+
}
303+
});
304+
it('Should throw exception (array)', (done) => {
305+
try {
306+
// eslint-disable-next-line no-unused-vars
307+
server = testServer.start(config, {
308+
contentBase: [contentBasePublic, 'https://example.com/'],
309+
watchContentBase: true,
310+
});
311+
312+
expect(true).toBe(false);
313+
} catch (e) {
314+
expect(e.message).toBe('Watching remote files is not supported.');
315+
done();
316+
}
317+
});
318+
});
319+
270320
describe('default to PWD', () => {
271321
beforeAll((done) => {
272322
jest.spyOn(process, 'cwd').mockImplementation(() => contentBasePublic);

test/server/profile-option.test.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict';
2+
3+
const webpack = require('webpack');
4+
const Server = require('../../lib/Server');
5+
const config = require('../fixtures/simple-config/webpack.config');
6+
const port = require('../ports-map')['profile-option'];
7+
8+
describe('profile', () => {
9+
describe('output', () => {
10+
let mockStderr;
11+
12+
beforeAll(() => {
13+
mockStderr = jest
14+
.spyOn(process.stderr, 'write')
15+
.mockImplementation(() => {});
16+
});
17+
18+
it('should show percentage progress with profile data', (done) => {
19+
const compiler = webpack(config);
20+
const server = new Server(compiler, {
21+
port,
22+
// profile will only have an effect when progress is enabled
23+
progress: true,
24+
profile: true,
25+
});
26+
27+
compiler.hooks.done.tap('webpack-dev-server', () => {
28+
const calls = mockStderr.mock.calls;
29+
mockStderr.mockRestore();
30+
let foundProgress = false;
31+
let foundProfile = false;
32+
calls.forEach((call) => {
33+
if (call[0].includes('0% compiling')) {
34+
foundProgress = true;
35+
}
36+
37+
// this is an indicator that the profile option is enabled,
38+
// so we should expect to find it in stderr since profile is enabled
39+
if (call[0].includes('ms after chunk modules optimization')) {
40+
foundProfile = true;
41+
}
42+
});
43+
expect(foundProgress).toBeTruthy();
44+
expect(foundProfile).toBeTruthy();
45+
46+
server.close(done);
47+
});
48+
49+
compiler.run(() => {});
50+
server.listen(port, 'localhost');
51+
});
52+
});
53+
});

0 commit comments

Comments
 (0)