Skip to content

Commit f5e7f8f

Browse files
authored
feat: allow string value for client.webSocketURL.port (#3354)
1 parent 7389612 commit f5e7f8f

9 files changed

+481
-10
lines changed

lib/options.json

+9-1
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,15 @@
296296
"description": "Tells clients connected to devServer to use the provided host."
297297
},
298298
"port": {
299-
"type": "number",
299+
"anyOf": [
300+
{
301+
"type": "number"
302+
},
303+
{
304+
"type": "string",
305+
"minLength": 1
306+
}
307+
],
300308
"description": "Tells clients connected to devServer to use the provided port."
301309
},
302310
"path": {

lib/utils/normalizeOptions.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ function normalizeOptions(compiler, options, logger) {
7474
options.liveReload =
7575
typeof options.liveReload !== 'undefined' ? options.liveReload : true;
7676

77+
if (typeof options.port === 'string' && options.port !== 'auto') {
78+
options.port = Number(options.port);
79+
}
80+
7781
const defaultWebSocketServerType = 'ws';
7882
const defaultWebSocketServerOptions = { path: '/ws' };
7983

@@ -98,6 +102,12 @@ function normalizeOptions(compiler, options, logger) {
98102
...options.webSocketServer.options,
99103
},
100104
};
105+
106+
if (typeof options.webSocketServer.options.port === 'string') {
107+
options.webSocketServer.options.port = Number(
108+
options.webSocketServer.options.port
109+
);
110+
}
101111
}
102112

103113
if (!options.client) {
@@ -111,9 +121,11 @@ function normalizeOptions(compiler, options, logger) {
111121

112122
options.client.webSocketURL = {
113123
host: parsedURL.hostname,
114-
port: parsedURL.port,
124+
port: Number(parsedURL.port),
115125
path: parsedURL.pathname,
116126
};
127+
} else if (typeof options.client.webSocketURL.port === 'string') {
128+
options.client.webSocketURL.port = Number(options.client.webSocketURL.port);
117129
}
118130

119131
// Enable client overlay by default

test/__snapshots__/validate-options.test.js.snap.webpack4

+11-4
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,21 @@ exports[`options validate should throw an error on the "client" option with '{"w
150150

151151
exports[`options validate should throw an error on the "client" option with '{"webSocketURL":{"port":""}}' value 1`] = `
152152
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
153-
- configuration.client.webSocketURL.port should be a number.
154-
-> Tells clients connected to devServer to use the provided port."
153+
- configuration.client.webSocketURL.port should be an non-empty string."
155154
`;
156155

157156
exports[`options validate should throw an error on the "client" option with '{"webSocketURL":{"port":true}}' value 1`] = `
158157
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
159-
- configuration.client.webSocketURL.port should be a number.
160-
-> Tells clients connected to devServer to use the provided port."
158+
- configuration.client.webSocketURL should be one of these:
159+
non-empty string | object { host?, port?, path? }
160+
-> When using dev server and you're proxying dev-server, the client script does not always know where to connect to.
161+
Details:
162+
* configuration.client.webSocketURL.port should be one of these:
163+
number | non-empty string
164+
-> Tells clients connected to devServer to use the provided port.
165+
Details:
166+
* configuration.client.webSocketURL.port should be a number.
167+
* configuration.client.webSocketURL.port should be a non-empty string."
161168
`;
162169

163170
exports[`options validate should throw an error on the "client" option with 'whoops!' value 1`] = `

test/__snapshots__/validate-options.test.js.snap.webpack5

+11-4
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,21 @@ exports[`options validate should throw an error on the "client" option with '{"w
150150

151151
exports[`options validate should throw an error on the "client" option with '{"webSocketURL":{"port":""}}' value 1`] = `
152152
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
153-
- configuration.client.webSocketURL.port should be a number.
154-
-> Tells clients connected to devServer to use the provided port."
153+
- configuration.client.webSocketURL.port should be an non-empty string."
155154
`;
156155

157156
exports[`options validate should throw an error on the "client" option with '{"webSocketURL":{"port":true}}' value 1`] = `
158157
"ValidationError: Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema.
159-
- configuration.client.webSocketURL.port should be a number.
160-
-> Tells clients connected to devServer to use the provided port."
158+
- configuration.client.webSocketURL should be one of these:
159+
non-empty string | object { host?, port?, path? }
160+
-> When using dev server and you're proxying dev-server, the client script does not always know where to connect to.
161+
Details:
162+
* configuration.client.webSocketURL.port should be one of these:
163+
number | non-empty string
164+
-> Tells clients connected to devServer to use the provided port.
165+
Details:
166+
* configuration.client.webSocketURL.port should be a number.
167+
* configuration.client.webSocketURL.port should be a non-empty string."
161168
`;
162169

163170
exports[`options validate should throw an error on the "client" option with 'whoops!' value 1`] = `

test/e2e/web-socket-server-and-url.test.js

+76
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,82 @@ for (const webSocketServerType of webSocketServerTypes) {
316316
});
317317
});
318318

319+
describe('should work with custom client port as string', () => {
320+
beforeAll((done) => {
321+
const options = {
322+
webSocketServer: webSocketServerType,
323+
port: port2,
324+
host: '0.0.0.0',
325+
client: {
326+
webSocketURL: {
327+
port: `${port3}`,
328+
},
329+
},
330+
};
331+
332+
testServer.startAwaitingCompilation(config, options, done);
333+
});
334+
335+
afterAll(testServer.close);
336+
337+
describe('browser client', () => {
338+
it('uses correct port and path', (done) => {
339+
runBrowser().then(({ page, browser }) => {
340+
waitForTest(browser, page, /ws/, (websocketUrl) => {
341+
expect(websocketUrl).toContain(
342+
`${websocketUrlProtocol}://localhost:${port3}/ws`
343+
);
344+
345+
done();
346+
});
347+
348+
page.goto(`http://localhost:${port2}/main`);
349+
});
350+
});
351+
});
352+
});
353+
354+
describe('should work with custom client.webSocketURL.port and webSocketServer.options.port both as string', () => {
355+
beforeAll((done) => {
356+
const options = {
357+
webSocketServer: {
358+
type: webSocketServerType,
359+
options: {
360+
host: '0.0.0.0',
361+
port: `${port2}`,
362+
},
363+
},
364+
port: port2,
365+
host: '0.0.0.0',
366+
client: {
367+
webSocketURL: {
368+
port: `${port3}`,
369+
},
370+
},
371+
};
372+
373+
testServer.startAwaitingCompilation(config, options, done);
374+
});
375+
376+
afterAll(testServer.close);
377+
378+
describe('browser client', () => {
379+
it('uses correct port and path', (done) => {
380+
runBrowser().then(({ page, browser }) => {
381+
waitForTest(browser, page, /ws/, (websocketUrl) => {
382+
expect(websocketUrl).toContain(
383+
`${websocketUrlProtocol}://localhost:${port3}/ws`
384+
);
385+
386+
done();
387+
});
388+
389+
page.goto(`http://localhost:${port2}/main`);
390+
});
391+
});
392+
});
393+
});
394+
319395
describe('should work with custom client host', () => {
320396
beforeAll((done) => {
321397
const options = {

test/server/utils/__snapshots__/normalizeOptions.test.js.snap.webpack4

+150
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,44 @@ Object {
109109
}
110110
`;
111111

112+
exports[`normalizeOptions client host and string port should set correct options 1`] = `
113+
Object {
114+
"allowedHosts": "auto",
115+
"client": Object {
116+
"hotEntry": true,
117+
"overlay": true,
118+
"webSocketURL": Object {
119+
"host": "my.host",
120+
"port": 9000,
121+
},
122+
},
123+
"compress": true,
124+
"devMiddleware": Object {},
125+
"hot": true,
126+
"liveReload": true,
127+
"setupExitSignals": true,
128+
"static": Array [
129+
Object {
130+
"directory": "CWD",
131+
"publicPath": Array [
132+
"/",
133+
],
134+
"serveIndex": Object {
135+
"icons": true,
136+
},
137+
"staticOptions": Object {},
138+
"watch": Object {},
139+
},
140+
],
141+
"webSocketServer": Object {
142+
"options": Object {
143+
"path": "/ws",
144+
},
145+
"type": "ws",
146+
},
147+
}
148+
`;
149+
112150
exports[`normalizeOptions client path should set correct options 1`] = `
113151
Object {
114152
"allowedHosts": "auto",
@@ -219,6 +257,82 @@ Object {
219257
}
220258
`;
221259

260+
exports[`normalizeOptions client.transport ws string and webSocketServer object should set correct options 1`] = `
261+
Object {
262+
"allowedHosts": "auto",
263+
"client": Object {
264+
"hotEntry": true,
265+
"overlay": true,
266+
"transport": "ws",
267+
"webSocketURL": Object {},
268+
},
269+
"compress": true,
270+
"devMiddleware": Object {},
271+
"hot": true,
272+
"liveReload": true,
273+
"setupExitSignals": true,
274+
"static": Array [
275+
Object {
276+
"directory": "CWD",
277+
"publicPath": Array [
278+
"/",
279+
],
280+
"serveIndex": Object {
281+
"icons": true,
282+
},
283+
"staticOptions": Object {},
284+
"watch": Object {},
285+
},
286+
],
287+
"webSocketServer": Object {
288+
"options": Object {
289+
"host": "myhost",
290+
"path": "/ws",
291+
"port": 8080,
292+
},
293+
"type": "ws",
294+
},
295+
}
296+
`;
297+
298+
exports[`normalizeOptions client.transport ws string and webSocketServer object with port as string should set correct options 1`] = `
299+
Object {
300+
"allowedHosts": "auto",
301+
"client": Object {
302+
"hotEntry": true,
303+
"overlay": true,
304+
"transport": "ws",
305+
"webSocketURL": Object {},
306+
},
307+
"compress": true,
308+
"devMiddleware": Object {},
309+
"hot": true,
310+
"liveReload": true,
311+
"setupExitSignals": true,
312+
"static": Array [
313+
Object {
314+
"directory": "CWD",
315+
"publicPath": Array [
316+
"/",
317+
],
318+
"serveIndex": Object {
319+
"icons": true,
320+
},
321+
"staticOptions": Object {},
322+
"watch": Object {},
323+
},
324+
],
325+
"webSocketServer": Object {
326+
"options": Object {
327+
"host": "myhost",
328+
"path": "/ws",
329+
"port": 8080,
330+
},
331+
"type": "ws",
332+
},
333+
}
334+
`;
335+
222336
exports[`normalizeOptions client.transport ws string and webSocketServer ws string should set correct options 1`] = `
223337
Object {
224338
"allowedHosts": "auto",
@@ -575,6 +689,42 @@ Object {
575689
}
576690
`;
577691

692+
exports[`normalizeOptions port string should set correct options 1`] = `
693+
Object {
694+
"allowedHosts": "auto",
695+
"client": Object {
696+
"hotEntry": true,
697+
"overlay": true,
698+
"webSocketURL": Object {},
699+
},
700+
"compress": true,
701+
"devMiddleware": Object {},
702+
"hot": true,
703+
"liveReload": true,
704+
"port": 9000,
705+
"setupExitSignals": true,
706+
"static": Array [
707+
Object {
708+
"directory": "CWD",
709+
"publicPath": Array [
710+
"/",
711+
],
712+
"serveIndex": Object {
713+
"icons": true,
714+
},
715+
"staticOptions": Object {},
716+
"watch": Object {},
717+
},
718+
],
719+
"webSocketServer": Object {
720+
"options": Object {
721+
"path": "/ws",
722+
},
723+
"type": "ws",
724+
},
725+
}
726+
`;
727+
578728
exports[`normalizeOptions single compiler watchOptions is object should set correct options 1`] = `
579729
Object {
580730
"allowedHosts": "auto",

0 commit comments

Comments
 (0)