Skip to content

Commit c9e9178

Browse files
John Richard Chipps-Hardingevilebottnawi
John Richard Chipps-Harding
authored andcommitted
feat(options): add multiple openPage support (#2266)
1 parent 06e014b commit c9e9178

File tree

12 files changed

+265
-21
lines changed

12 files changed

+265
-21
lines changed
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# CLI: Open Page Option (Multiple)
2+
3+
```console
4+
npm run webpack-dev-server -- --open-page example1.html,example2.html
5+
```
6+
7+
Some applications may consist of multiple pages. During development it may
8+
be useful to directly open multiple pages at the same time. The pages to open
9+
may be specified as the argument to the `open-page` option.
10+
11+
## What Should Happen
12+
13+
The script should open `http://localhost:8080/example1.html` and
14+
`http://localhost:8080/example2.html` in your default browser.
15+
You should see the text on the page itself change to read `Success!`.
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
'use strict';
2+
3+
const target = document.querySelector('#target');
4+
5+
if (window.location.href.endsWith('example1.html')) {
6+
target.classList.add('pass');
7+
target.innerHTML = 'Success!';
8+
} else {
9+
target.classList.add('fail');
10+
target.innerHTML = 'Houston, we have a problem.';
11+
}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
'use strict';
2+
3+
const target = document.querySelector('#target');
4+
5+
if (window.location.href.endsWith('example2.html')) {
6+
target.classList.add('pass');
7+
target.innerHTML = 'Success!';
8+
} else {
9+
target.classList.add('fail');
10+
target.innerHTML = 'Houston, we have a problem.';
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict';
2+
3+
const HtmlWebpackPlugin = require('html-webpack-plugin');
4+
// our setup function adds behind-the-scenes bits to the config that all of our
5+
// examples need
6+
const { setup } = require('../../util');
7+
8+
module.exports = [
9+
setup({
10+
context: __dirname,
11+
entry: './app1.js',
12+
output: {
13+
filename: 'app1.js',
14+
},
15+
plugins: [
16+
new HtmlWebpackPlugin({
17+
filename: 'example1.html',
18+
template: '../../.assets/layout.html',
19+
title: 'Open Page (Multiple) / Example / Page 1',
20+
}),
21+
],
22+
}),
23+
setup({
24+
context: __dirname,
25+
entry: './app2.js',
26+
output: {
27+
filename: 'app2.js',
28+
},
29+
plugins: [
30+
new HtmlWebpackPlugin({
31+
filename: 'example2.html',
32+
template: '../../.assets/layout.html',
33+
title: 'Open Page (Multiple) / Example / Page 2',
34+
}),
35+
],
36+
}),
37+
];

examples/cli/open-page/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ as the argument to the `open-page` option.
1010

1111
## What Should Happen
1212

13-
The script should open `http://localhost:8080/` in your default browser.
14-
You should see the text on the page itself change to read `Success!`.
13+
The script should open `http://localhost:8080/example.html#page1` in your
14+
default browser. You should see the text on the page itself change to read `Success!`.

lib/options.json

+13-2
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,18 @@
209209
]
210210
},
211211
"openPage": {
212-
"type": "string"
212+
"anyOf": [
213+
{
214+
"type": "string"
215+
},
216+
{
217+
"type": "array",
218+
"items": {
219+
"type": "string"
220+
},
221+
"minItems": 1
222+
}
223+
]
213224
},
214225
"overlay": {
215226
"anyOf": [
@@ -436,7 +447,7 @@
436447
"noInfo": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devservernoinfo-)",
437448
"onListening": "should be {Function} (https://webpack.js.org/configuration/dev-server/#onlistening)",
438449
"open": "should be {String|Boolean} (https://webpack.js.org/configuration/dev-server/#devserveropen)",
439-
"openPage": "should be {String} (https://webpack.js.org/configuration/dev-server/#devserveropenpage)",
450+
"openPage": "should be {String|Array} (https://webpack.js.org/configuration/dev-server/#devserveropenpage)",
440451
"overlay": "should be {Boolean|Object} (https://webpack.js.org/configuration/dev-server/#devserveroverlay)",
441452
"pfx": "should be {String|Buffer} (https://webpack.js.org/configuration/dev-server/#devserverpfx)",
442453
"pfxPassphrase": "should be {String} (https://webpack.js.org/configuration/dev-server/#devserverpfxpassphrase)",

lib/utils/createConfig.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ function createConfig(config, argv, { port }) {
203203

204204
if (argv.openPage) {
205205
options.open = true;
206-
options.openPage = argv.openPage;
206+
options.openPage = argv.openPage.split(',');
207207
}
208208

209209
if (typeof argv.open !== 'undefined') {

lib/utils/runOpen.js

+17-11
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,29 @@ const isAbsoluteUrl = require('is-absolute-url');
66
function runOpen(uri, options, log) {
77
// https://github.com/webpack/webpack-dev-server/issues/1990
88
let openOptions = { wait: false };
9-
let openMessage = 'Unable to open browser';
9+
let openOptionValue = '';
1010

1111
if (typeof options.open === 'string') {
1212
openOptions = Object.assign({}, openOptions, { app: options.open });
13-
openMessage += `: ${options.open}`;
13+
openOptionValue = `: "${options.open}"`;
1414
}
1515

16-
const pageUrl =
17-
options.openPage && isAbsoluteUrl(options.openPage)
18-
? options.openPage
19-
: `${uri}${options.openPage || ''}`;
16+
const pages =
17+
typeof options.openPage === 'string'
18+
? [options.openPage]
19+
: options.openPage || [''];
2020

21-
return open(pageUrl, openOptions).catch(() => {
22-
log.warn(
23-
`${openMessage}. If you are running in a headless environment, please do not use the --open flag`
24-
);
25-
});
21+
return Promise.all(
22+
pages.map((page) => {
23+
const pageUrl = page && isAbsoluteUrl(page) ? page : `${uri}${page}`;
24+
25+
return open(pageUrl, openOptions).catch(() => {
26+
log.warn(
27+
`Unable to open "${pageUrl}" in browser${openOptionValue}. If you are running in a headless environment, please do not use the --open flag`
28+
);
29+
});
30+
})
31+
);
2632
}
2733

2834
module.exports = runOpen;

test/server/utils/__snapshots__/createConfig.test.js.snap

+22-1
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,25 @@ Object {
818818
}
819819
`;
820820

821+
exports[`createConfig openPage multiple option (in devServer config) 1`] = `
822+
Object {
823+
"hot": true,
824+
"hotOnly": false,
825+
"noInfo": true,
826+
"open": true,
827+
"openPage": Array [
828+
"/different/page",
829+
"/different/page2",
830+
],
831+
"port": 8080,
832+
"publicPath": "/",
833+
"stats": Object {
834+
"cached": false,
835+
"cachedAssets": false,
836+
},
837+
}
838+
`;
839+
821840
exports[`createConfig openPage option (in devServer config) 1`] = `
822841
Object {
823842
"hot": true,
@@ -840,7 +859,9 @@ Object {
840859
"hotOnly": false,
841860
"noInfo": true,
842861
"open": true,
843-
"openPage": "/different/page",
862+
"openPage": Array [
863+
"/different/page",
864+
],
844865
"port": 8080,
845866
"publicPath": "/",
846867
"stats": Object {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`runOpen util on specify multiple absolute https URLs with pages in Google Chrome 1`] = `
4+
Array [
5+
"https://example2.com",
6+
Object {
7+
"app": "Google Chrome",
8+
"wait": false,
9+
},
10+
]
11+
`;
12+
13+
exports[`runOpen util on specify multiple absolute https URLs with pages in Google Chrome 2`] = `
14+
Array [
15+
"https://example3.com",
16+
Object {
17+
"app": "Google Chrome",
18+
"wait": false,
19+
},
20+
]
21+
`;
22+
23+
exports[`runOpen util on specify one relative URL and one absolute URL with pages in Google Chrome 1`] = `
24+
Array [
25+
"https://example.com/index.html",
26+
Object {
27+
"app": "Google Chrome",
28+
"wait": false,
29+
},
30+
]
31+
`;
32+
33+
exports[`runOpen util on specify one relative URL and one absolute URL with pages in Google Chrome 2`] = `
34+
Array [
35+
"https://example2.com",
36+
Object {
37+
"app": "Google Chrome",
38+
"wait": false,
39+
},
40+
]
41+
`;
42+
43+
exports[`runOpen util should open browser on specify URL with multiple pages inside array 1`] = `
44+
Array [
45+
"https://example.com/index.html",
46+
Object {
47+
"wait": false,
48+
},
49+
]
50+
`;
51+
52+
exports[`runOpen util should open browser on specify URL with multiple pages inside array 2`] = `
53+
Array [
54+
"https://example.com/index2.html",
55+
Object {
56+
"wait": false,
57+
},
58+
]
59+
`;
60+
61+
exports[`runOpen util should open browser on specify URL with page inside array 1`] = `
62+
Array [
63+
"https://example.com/index.html",
64+
Object {
65+
"wait": false,
66+
},
67+
]
68+
`;

test/server/utils/createConfig.test.js

+15
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,21 @@ describe('createConfig', () => {
887887
expect(config).toMatchSnapshot();
888888
});
889889

890+
it('openPage multiple option (in devServer config)', () => {
891+
const config = createConfig(
892+
Object.assign({}, webpackConfig, {
893+
devServer: {
894+
open: true,
895+
openPage: ['/different/page', '/different/page2'],
896+
},
897+
}),
898+
argv,
899+
{ port: 8080 }
900+
);
901+
902+
expect(config).toMatchSnapshot();
903+
});
904+
890905
it('useLocalIp option', () => {
891906
const config = createConfig(
892907
webpackConfig,

test/server/utils/runOpen.test.js

+53-4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,27 @@ describe('runOpen util', () => {
4545
});
4646
});
4747

48+
it('on specify URL with page inside array', () => {
49+
return runOpen(
50+
'https://example.com',
51+
{ openPage: ['/index.html'] },
52+
console
53+
).then(() => {
54+
expect(opn.mock.calls[0]).toMatchSnapshot();
55+
});
56+
});
57+
58+
it('on specify URL with multiple pages inside array', () => {
59+
return runOpen(
60+
'https://example.com',
61+
{ openPage: ['/index.html', '/index2.html'] },
62+
console
63+
).then(() => {
64+
expect(opn.mock.calls[0]).toMatchSnapshot();
65+
expect(opn.mock.calls[1]).toMatchSnapshot();
66+
});
67+
});
68+
4869
it('on specify URL in Google Chrome', () => {
4970
return runOpen(
5071
'https://example.com',
@@ -118,6 +139,34 @@ describe('runOpen util', () => {
118139
});
119140
});
120141

142+
it('on specify multiple absolute https URLs with pages in Google Chrome ', () => {
143+
return runOpen(
144+
'https://example.com',
145+
{
146+
open: 'Google Chrome',
147+
openPage: ['https://example2.com', 'https://example3.com'],
148+
},
149+
console
150+
).then(() => {
151+
expect(opn.mock.calls[0]).toMatchSnapshot();
152+
expect(opn.mock.calls[1]).toMatchSnapshot();
153+
});
154+
});
155+
156+
it('on specify one relative URL and one absolute URL with pages in Google Chrome ', () => {
157+
return runOpen(
158+
'https://example.com',
159+
{
160+
open: 'Google Chrome',
161+
openPage: ['/index.html', 'https://example2.com'],
162+
},
163+
console
164+
).then(() => {
165+
expect(opn.mock.calls[0]).toMatchSnapshot();
166+
expect(opn.mock.calls[1]).toMatchSnapshot();
167+
});
168+
});
169+
121170
describe('should not open browser', () => {
122171
const logMock = { warn: jest.fn() };
123172

@@ -132,7 +181,7 @@ describe('runOpen util', () => {
132181
it('on specify URL and log error', () => {
133182
return runOpen('https://example.com', {}, logMock).then(() => {
134183
expect(logMock.warn.mock.calls[0][0]).toMatchInlineSnapshot(
135-
`"Unable to open browser. If you are running in a headless environment, please do not use the --open flag"`
184+
`"Unable to open \\"https://example.com\\" in browser. If you are running in a headless environment, please do not use the --open flag"`
136185
);
137186
expect(opn.mock.calls[0]).toMatchInlineSnapshot(`
138187
Array [
@@ -152,7 +201,7 @@ describe('runOpen util', () => {
152201
logMock
153202
).then(() => {
154203
expect(logMock.warn.mock.calls[0][0]).toMatchInlineSnapshot(
155-
`"Unable to open browser. If you are running in a headless environment, please do not use the --open flag"`
204+
`"Unable to open \\"https://example.com/index.html\\" in browser. If you are running in a headless environment, please do not use the --open flag"`
156205
);
157206
expect(opn.mock.calls[0]).toMatchInlineSnapshot(`
158207
Array [
@@ -172,7 +221,7 @@ describe('runOpen util', () => {
172221
logMock
173222
).then(() => {
174223
expect(logMock.warn.mock.calls[0][0]).toMatchInlineSnapshot(
175-
`"Unable to open browser: Google Chrome. If you are running in a headless environment, please do not use the --open flag"`
224+
`"Unable to open \\"https://example.com\\" in browser: \\"Google Chrome\\". If you are running in a headless environment, please do not use the --open flag"`
176225
);
177226
expect(opn.mock.calls[0]).toMatchInlineSnapshot(`
178227
Array [
@@ -193,7 +242,7 @@ describe('runOpen util', () => {
193242
logMock
194243
).then(() => {
195244
expect(logMock.warn.mock.calls[0][0]).toMatchInlineSnapshot(
196-
`"Unable to open browser: Google Chrome. If you are running in a headless environment, please do not use the --open flag"`
245+
`"Unable to open \\"https://example.com/index.html\\" in browser: \\"Google Chrome\\". If you are running in a headless environment, please do not use the --open flag"`
197246
);
198247
expect(opn.mock.calls[0]).toMatchInlineSnapshot(`
199248
Array [

0 commit comments

Comments
 (0)