@@ -14,6 +14,7 @@ import colors from 'picocolors'
1414
1515import { debug , logger } from '../utils/logger'
1616import { resolveCertificate } from './cert'
17+ import { resolvePortlessURLs } from './portless'
1718import { startTunnel } from './tunnel'
1819
1920export interface ListenOptions {
@@ -32,7 +33,7 @@ export interface ListenOptions {
3233
3334interface ListenURL {
3435 url : string
35- type : 'local' | 'network' | 'tunnel'
36+ type : 'local' | 'network' | 'tunnel' | 'public'
3637}
3738
3839/**
@@ -122,11 +123,18 @@ export async function listen(handler: RequestListener, options: ListenOptions =
122123 tunnel = await startTunnel ( `${ protocol } ://localhost:${ address . port } ` , ! ! certificate )
123124 }
124125
126+ const portless = resolvePortlessURLs ( )
127+ const portlessURL = portless . url && portless . url + baseURL
128+ const portlessShareURL = portless . shareURL && portless . shareURL + baseURL
129+
125130 function getURLs ( ) : ListenURL [ ] {
126131 const urls : ListenURL [ ] = [ ]
127132 if ( tunnel ) {
128133 urls . push ( { url : tunnel . url , type : 'tunnel' } )
129134 }
135+ for ( const portlessURL of portless . all ) {
136+ urls . push ( { url : portlessURL + baseURL , type : 'public' } )
137+ }
130138 if ( anyHost ) {
131139 urls . push ( { url : formatURL ( 'localhost' ) , type : 'local' } )
132140 for ( const info of Object . values ( networkInterfaces ( ) ) . flat ( ) ) {
@@ -142,7 +150,7 @@ export async function listen(handler: RequestListener, options: ListenOptions =
142150 return urls
143151 }
144152
145- const publicURL = options . publicURL || tunnel ?. url
153+ const publicURL = options . publicURL || tunnel ?. url || portlessShareURL || portlessURL
146154
147155 if ( options . showURL !== false ) {
148156 const urls = getURLs ( )
@@ -158,19 +166,19 @@ export async function listen(handler: RequestListener, options: ListenOptions =
158166 console . log ( `\n${ centerBlock ( renderUnicodeCompact ( qrURL ) ) } \n` )
159167 }
160168
161- const labels = { local : 'Local:' , network : 'Network:' , tunnel : 'Tunnel:' } as const
162- const labelColors = { local : colors . green , network : colors . magenta , tunnel : colors . cyan } as const
169+ const labels = { local : 'Local:' , network : 'Network:' , tunnel : 'Tunnel:' , public : 'Public:' } as const
170+ const labelColors = { local : colors . green , network : colors . magenta , tunnel : colors . cyan , public : colors . magenta } as const
163171 const lines : string [ ] = [ ]
164172 const line = ( color : ( text : string ) => string , label : string , value : string , isQR : boolean ) =>
165173 ` ${ color ( '➜' ) } ${ colors . bold ( color ( label . padEnd ( 10 ) ) ) } ${ value } ${ isQR ? colors . gray ( ' [QR code]' ) : '' } `
166174 for ( const { url : displayURL , type } of urls ) {
167175 lines . push ( line ( labelColors [ type ] , labels [ type ] , colors . cyan ( displayURL ) , displayURL === qrURL ) )
168176 }
169- if ( ! anyHost && ! tunnel ) {
177+ if ( ! anyHost && ! tunnel && ! portless . url ) {
170178 lines . push ( line ( colors . magenta , 'Network:' , colors . gray ( `use ${ colors . white ( '--host' ) } to expose` ) , false ) )
171179 }
172- if ( options . publicURL && options . publicURL !== url ) {
173- lines . push ( line ( colors . magenta , 'Public:' , colors . cyan ( options . publicURL ) , options . publicURL === qrURL ) )
180+ if ( publicURL && publicURL !== url && ! urls . some ( entry => entry . url === publicURL ) ) {
181+ lines . push ( line ( colors . magenta , 'Public:' , colors . cyan ( publicURL ) , publicURL === qrURL ) )
174182 }
175183 // eslint-disable-next-line no-console
176184 console . log ( `${ qrURL ? '' : '\n' } ${ lines . join ( '\n' ) } \n` )
0 commit comments