Skip to content

Commit cc12853

Browse files
johnjenkinsJohn Jenkins
andauthored
feat(dev-server): new strictPort property (#6523)
Co-authored-by: John Jenkins <john.jenkins@nanoporetech.com>
1 parent e243c6f commit cc12853

File tree

6 files changed

+108
-3
lines changed

6 files changed

+108
-3
lines changed

src/compiler/config/test/validate-dev-server.spec.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,23 @@ describe('validateDevServer', () => {
162162
expect(config.devServer.port).toBe(4444);
163163
});
164164

165+
it('should default strictPort to false', () => {
166+
const { config } = validateConfig(inputConfig, mockLoadConfigInit());
167+
expect(config.devServer.strictPort).toBe(false);
168+
});
169+
170+
it('should set strictPort to true', () => {
171+
inputConfig.devServer = { ...inputDevServerConfig, strictPort: true };
172+
const { config } = validateConfig(inputConfig, mockLoadConfigInit());
173+
expect(config.devServer.strictPort).toBe(true);
174+
});
175+
176+
it('should set strictPort to false', () => {
177+
inputConfig.devServer = { ...inputDevServerConfig, strictPort: false };
178+
const { config } = validateConfig(inputConfig, mockLoadConfigInit());
179+
expect(config.devServer.strictPort).toBe(false);
180+
});
181+
165182
it('should default historyApiFallback', () => {
166183
const { config } = validateConfig(inputConfig, mockLoadConfigInit());
167184
expect(config.devServer.historyApiFallback).toBeDefined();

src/compiler/config/validate-dev-server.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ export const validateDevServer = (config: d.ValidatedConfig, diagnostics: d.Diag
8787
devServer.websocket = true;
8888
}
8989

90+
if (!isBoolean(devServer.strictPort)) {
91+
devServer.strictPort = false;
92+
}
93+
9094
if (flags.ssr) {
9195
devServer.ssr = true;
9296
} else {

src/declarations/stencil-public-compiler.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,12 @@ export interface StencilDevServerConfig {
658658
* Sets the server's port. Defaults to `3333`.
659659
*/
660660
port?: number;
661+
/**
662+
* When set to `true`, the dev server will exit with an error if the specified port is already in use.
663+
* When set to `false`, the dev server will automatically try the next available port.
664+
* Defaults to `false`.
665+
*/
666+
strictPort?: boolean;
661667
/**
662668
* When files are watched and updated, by default the dev server will use `hmr` (Hot Module Replacement)
663669
* to update the page without a full page refresh. To have the page do a full refresh use `pageReload`.

src/dev-server/server-http.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,18 @@ export function createHttpServer(devServerConfig: d.DevServerConfig, serverCtx:
1414
return credentials ? https.createServer(credentials, reqHandler) : http.createServer(reqHandler);
1515
}
1616

17-
export async function findClosestOpenPort(host: string, port: number): Promise<number> {
17+
export async function findClosestOpenPort(host: string, port: number, strictPort = false): Promise<number> {
18+
const isTaken = await isPortTaken(host, port);
19+
20+
if (!isTaken) {
21+
return port;
22+
}
23+
24+
if (strictPort) {
25+
throw new Error(`Port ${port} is already in use. Please specify a different port or set strictPort to false.`);
26+
}
27+
28+
// If strictPort is false, recursively find the next available port
1829
async function t(portToCheck: number): Promise<number> {
1930
const isTaken = await isPortTaken(host, portToCheck);
2031
if (!isTaken) {
@@ -23,7 +34,7 @@ export async function findClosestOpenPort(host: string, port: number): Promise<n
2334
return t(portToCheck + 1);
2435
}
2536

26-
return t(port);
37+
return t(port + 1);
2738
}
2839

2940
function isPortTaken(host: string, port: number): Promise<boolean> {

src/dev-server/server-process.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ export function initServerProcess(sendMsg: d.DevServerSendMessage) {
2020

2121
const startServer = async (msg: d.DevServerMessage) => {
2222
const devServerConfig = msg.startServer;
23-
devServerConfig.port = await findClosestOpenPort(devServerConfig.address, devServerConfig.port);
23+
devServerConfig.port = await findClosestOpenPort(
24+
devServerConfig.address,
25+
devServerConfig.port,
26+
devServerConfig.strictPort,
27+
);
2428
devServerConfig.browserUrl = getBrowserUrl(
2529
devServerConfig.protocol,
2630
devServerConfig.address,
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import * as net from 'net';
2+
3+
import { findClosestOpenPort } from '../server-http';
4+
5+
describe('server-http', () => {
6+
describe('findClosestOpenPort', () => {
7+
let testServer: net.Server;
8+
const TEST_HOST = '127.0.0.1';
9+
const TEST_PORT = 9876;
10+
11+
afterEach(async () => {
12+
if (testServer) {
13+
await new Promise<void>((resolve) => {
14+
testServer.close(() => resolve());
15+
});
16+
}
17+
});
18+
19+
it('should return the same port if it is available', async () => {
20+
const port = await findClosestOpenPort(TEST_HOST, TEST_PORT);
21+
expect(port).toBe(TEST_PORT);
22+
});
23+
24+
it('should find the next available port when strictPort is false', async () => {
25+
// Occupy the test port
26+
testServer = net.createServer();
27+
await new Promise<void>((resolve) => {
28+
testServer.listen(TEST_PORT, TEST_HOST, () => resolve());
29+
});
30+
31+
const port = await findClosestOpenPort(TEST_HOST, TEST_PORT, false);
32+
expect(port).toBe(TEST_PORT + 1);
33+
});
34+
35+
it('should find the next available port when strictPort is not provided (defaults to false)', async () => {
36+
// Occupy the test port
37+
testServer = net.createServer();
38+
await new Promise<void>((resolve) => {
39+
testServer.listen(TEST_PORT, TEST_HOST, () => resolve());
40+
});
41+
42+
const port = await findClosestOpenPort(TEST_HOST, TEST_PORT);
43+
expect(port).toBe(TEST_PORT + 1);
44+
});
45+
46+
it('should throw an error when port is taken and strictPort is true', async () => {
47+
// Occupy the test port
48+
testServer = net.createServer();
49+
await new Promise<void>((resolve) => {
50+
testServer.listen(TEST_PORT, TEST_HOST, () => resolve());
51+
});
52+
53+
await expect(findClosestOpenPort(TEST_HOST, TEST_PORT, true)).rejects.toThrow(
54+
`Port ${TEST_PORT} is already in use. Please specify a different port or set strictPort to false.`,
55+
);
56+
});
57+
58+
it('should return the port when available and strictPort is true', async () => {
59+
const port = await findClosestOpenPort(TEST_HOST, TEST_PORT, true);
60+
expect(port).toBe(TEST_PORT);
61+
});
62+
});
63+
});

0 commit comments

Comments
 (0)