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
importprocessfrom'node:process'
1111

12-
import{checkPort,getPort}from'get-port-please'
12+
import{getPort}from'get-port-please'
1313
importcolorsfrom'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
awaitnewPromise<void>((resolve,reject)=>{
120-
server.once('error',reject)
120+
constonError=(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-
constdesiredPort=requestedPort??3000
236-
if(awaitcheckPort(desiredPort,hostname||undefined)===false){
237-
thrownewError(`Port ${desiredPort} is already in use (\`--strictPort\` is enabled).`)
238-
}
239-
returndesiredPort
236+
returnrequestedPort??3000
240237
}
241238

242239
constport=awaitgetPort({
@@ -250,6 +247,27 @@ async function resolvePort(requestedPort: number | undefined, hostname: string,
250247
returnport
251248
}
252249

250+
/**
251+
* Turn a `listen()` failure into actionable advice, leaving anything we have
252+
* nothing better to say about untouched.
253+
*/
254+
functiondescribeBindError(error: NodeJS.ErrnoException,port: number,hostname: string,strictPort?: boolean): Error{
255+
switch(error.code){
256+
case'EADDRINUSE':
257+
returnnewError(
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+
returnnewError(`Port ${port} requires elevated privileges. Pass \`--port\` with a port above 1023.`,{cause: error})
265+
case'EADDRNOTAVAIL':
266+
returnnewError(`\`${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+
returnerror
269+
}
270+
253271
exportasyncfunctionprintQRCode(url: string,{ showURL =false}: {showURL?: boolean}={}): Promise<void>{
254272
const{ renderUnicodeCompact }=awaitimport('uqr')
255273
constcaption=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
constfirst=awaitstart({port: 0})
120120

121-
awaitexpect(start({port: first.address.port,strictPort: true})).rejects.toThrow(/alreadyinuse/)
121+
awaitexpect(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+
awaitexpect(start({port: 34567,hostname: '203.0.113.1',strictPort: true})).rejects.toThrow(/isnotanaddressofthismachine/)
122126
})
123127

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

0 commit comments

Comments
 (0)