Skip to content

Commit 682c773

Browse files
committed
fix(dev): omit default ports and decode urls in the dev url block
1 parent 62a030f commit 682c773

2 files changed

Lines changed: 45 additions & 2 deletions

File tree

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,24 @@ export interface Listener {
6767

6868
constANY_HOSTS=newSet(['','0.0.0.0','::'])
6969
constLOOPBACK_HOSTS=newSet(['localhost','127.0.0.1','::1'])
70+
constDEFAULT_PORTS={http: 80,https: 443}asconst
71+
72+
/**
73+
* Render a URL for display: the port is omitted when it is the protocol
74+
* default, and percent-encoding is decoded so a non-ASCII `baseURL` is
75+
* readable. A malformed sequence is left as-is rather than throwing.
76+
*/
77+
exportfunctionformatDisplayURL(protocol: 'http'|'https',host: string,port: number,baseURL: string): string{
78+
consthostname=host.includes(':') ? `[${host}]` : host
79+
constsuffix=port===DEFAULT_PORTS[protocol] ? '' : `:${port}`
80+
consturl=`${protocol}://${hostname}${suffix}${baseURL}`
81+
try{
82+
returndecodeURI(url)
83+
}
84+
catch{
85+
returnurl
86+
}
87+
}
7088

7189
constHOSTNAME_RE=/^(?!-)[\d.:a-z-]{1,253}(?<!-)$/i
7290

@@ -152,7 +170,7 @@ export async function listen(handler: RequestListener, options: ListenOptions =
152170
constaddress=server.address()asAddressInfo
153171
constprotocol=certificate ? 'https' : 'http'
154172
constbaseURL=options.baseURL||'/'
155-
constformatURL=(host: string)=>`${protocol}://${host.includes(':') ? `[${host}]` : host}:${address.port}${baseURL}`
173+
constformatURL=(host: string)=>formatDisplayURL(protocol,host,address.port,baseURL)
156174

157175
constanyHost=ANY_HOSTS.has(hostname)
158176
consturl=formatURL(anyHost ? 'localhost' : hostname)

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { networkInterfaces } from 'node:os'
44

55
import{afterEach,beforeEach,describe,expect,it,vi}from'vitest'
66

7-
import{copyURL,getNetworkAddresses,listen,openBrowser,resolveOpenCommand,validateHostname}from'../../src/dev/listen'
7+
import{copyURL,formatDisplayURL,getNetworkAddresses,listen,openBrowser,resolveOpenCommand,validateHostname}from'../../src/dev/listen'
88

99
constwriteText=vi.hoisted(()=>vi.fn())
1010

@@ -62,6 +62,31 @@ describe('getNetworkAddresses', () => {
6262
})
6363
})
6464

65+
describe('formatDisplayURL',()=>{
66+
it('should include a non-default port',()=>{
67+
expect(formatDisplayURL('http','localhost',3000,'/')).toBe('http://localhost:3000/')
68+
expect(formatDisplayURL('https','localhost',8443,'/')).toBe('https://localhost:8443/')
69+
})
70+
71+
it('should omit the default port for the protocol',()=>{
72+
expect(formatDisplayURL('http','localhost',80,'/')).toBe('http://localhost/')
73+
expect(formatDisplayURL('https','localhost',443,'/')).toBe('https://localhost/')
74+
expect(formatDisplayURL('https','localhost',80,'/')).toBe('https://localhost:80/')
75+
})
76+
77+
it('should bracket ipv6 hosts',()=>{
78+
expect(formatDisplayURL('http','::1',3000,'/')).toBe('http://[::1]:3000/')
79+
})
80+
81+
it('should decode a percent-encoded base url',()=>{
82+
expect(formatDisplayURL('http','localhost',3000,'/%C3%A9t%C3%A9/')).toBe('http://localhost:3000/été/')
83+
})
84+
85+
it('should leave a malformed encoding alone',()=>{
86+
expect(formatDisplayURL('http','localhost',3000,'/%E0%A4%A/')).toBe('http://localhost:3000/%E0%A4%A/')
87+
})
88+
})
89+
6590
describe('validateHostname',()=>{
6691
it('should pass through valid hosts',()=>{
6792
expect(validateHostname('localhost')).toBe('localhost')

0 commit comments

Comments
 (0)