Skip to content

Commit 747c898

Browse files
committed
fix(dev): bind directly with --strictPort and translate bind errors
1 parent 654cef3 commit 747c898

2 files changed

Lines changed: 31 additions & 9 deletions

File tree

packages/nuxt-cli/src/dev/listen.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { createServer as createHttpsServer } from 'node:https'
99
import { networkInterfaces, release } from 'node:os'
1010
import process from 'node:process'
1111

12-
import { checkPort, getPort } from 'get-port-please'
12+
import { getPort } from 'get-port-please'
1313
import colors from 'picocolors'
1414

1515
import { debug, logger } from '../utils/logger'
@@ -117,9 +117,10 @@ export async function listen(handler: RequestListener, options: ListenOptions =
117117
: createHttpServer(handler)
118118

119119
await new Promise<void>((resolve, reject) => {
120-
server.once('error', reject)
120+
const onError = (error: NodeJS.ErrnoException) => reject(describeBindError(error, port, hostname, options.strictPort))
121+
server.once('error', onError)
121122
server.listen(port, hostname || undefined, () => {
122-
server.removeListener('error', reject)
123+
server.removeListener('error', onError)
123124
// Without a persistent handler, any later `error` event is unhandled and
124125
// takes the whole dev process down.
125126
server.on('error', error => logger.error(`Dev server error: ${error.message}`))
@@ -232,11 +233,7 @@ async function resolvePort(requestedPort: number | undefined, hostname: string,
232233
}
233234

234235
if (strictPort) {
235-
const desiredPort = requestedPort ?? 3000
236-
if (await checkPort(desiredPort, hostname || undefined) === false) {
237-
throw new Error(`Port ${desiredPort} is already in use (\`--strictPort\` is enabled).`)
238-
}
239-
return desiredPort
236+
return requestedPort ?? 3000
240237
}
241238

242239
const port = await getPort({
@@ -250,6 +247,27 @@ async function resolvePort(requestedPort: number | undefined, hostname: string,
250247
return port
251248
}
252249

250+
/**
251+
* Turn a `listen()` failure into actionable advice, leaving anything we have
252+
* nothing better to say about untouched.
253+
*/
254+
function describeBindError(error: NodeJS.ErrnoException, port: number, hostname: string, strictPort?: boolean): Error {
255+
switch (error.code) {
256+
case 'EADDRINUSE':
257+
return new Error(
258+
strictPort
259+
? `Port ${port} is already in use (\`--strictPort\` is enabled).`
260+
: `Port ${port} is already in use.`,
261+
{ cause: error },
262+
)
263+
case 'EACCES':
264+
return new Error(`Port ${port} requires elevated privileges. Pass \`--port\` with a port above 1023.`, { cause: error })
265+
case 'EADDRNOTAVAIL':
266+
return new Error(`\`${hostname}\` is not an address of this machine. Pass \`--host\` with a local address, or omit it to listen on localhost.`, { cause: error })
267+
}
268+
return error
269+
}
270+
253271
export async function printQRCode(url: string, { showURL = false }: { showURL?: boolean } = {}): Promise<void> {
254272
const { renderUnicodeCompact } = await import('uqr')
255273
const caption = showURL ? `\n${centerBlock(colors.cyan(url), url.length)}` : ''

packages/nuxt-cli/test/unit/listen.spec.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,11 @@ describe('listen', () => {
118118
it('should throw for a busy port with `strictPort`', async () => {
119119
const first = await start({ port: 0 })
120120

121-
await expect(start({ port: first.address.port, strictPort: true })).rejects.toThrow(/already in use/)
121+
await expect(start({ port: first.address.port, strictPort: true })).rejects.toThrow(`Port ${first.address.port} is already in use (\`--strictPort\` is enabled).`)
122+
})
123+
124+
it('should explain an unavailable host', async () => {
125+
await expect(start({ port: 34567, hostname: '203.0.113.1', strictPort: true })).rejects.toThrow(/is not an address of this machine/)
122126
})
123127

124128
it('should open the dev server URL', async () => {

0 commit comments

Comments
 (0)