@@ -44,8 +44,80 @@ export type ListenOptions =
4444 & {
4545 remoteAddress ?: string ;
4646 } ;
47+ function createOnListen (
48+ basePath : string ,
49+ options : ListenOptions ,
50+ ) : ( localAddr : Deno . NetAddr ) => void {
51+ return ( params ) => {
52+ // Don't spam logs with this on live deployments
53+ if ( DENO_DEPLOYMENT_ID ) return ;
54+
55+ const pathname = basePath + "/" ;
56+ const protocol = "key" in options && options . key && options . cert
57+ ? "https:"
58+ : "http:" ;
59+
60+ let hostname = params . hostname ;
61+ // Windows being windows...
62+ if (
63+ Deno . build . os === "windows" &&
64+ ( hostname === "0.0.0.0" || hostname === "::" )
65+ ) {
66+ hostname = "localhost" ;
67+ }
68+ // Work around https://github.com/denoland/deno/issues/23650
69+ hostname = hostname . startsWith ( "::" ) ? `[${ hostname } ]` : hostname ;
70+
71+ // deno-lint-ignore no-console
72+ console . log ( ) ;
73+ // deno-lint-ignore no-console
74+ console . log (
75+ colors . bgRgb8 ( colors . rgb8 ( " 🍋 Fresh ready " , 0 ) , 121 ) ,
76+ ) ;
77+ const sep = options . remoteAddress ? "" : "\n" ;
78+ const space = options . remoteAddress ? " " : "" ;
79+
80+ const localLabel = colors . bold ( "Local:" ) ;
81+ const address = colors . cyan (
82+ `${ protocol } //${ hostname } :${ params . port } ${ pathname } ` ,
83+ ) ;
84+ // deno-lint-ignore no-console
85+ console . log ( ` ${ localLabel } ${ space } ${ address } ${ sep } ` ) ;
86+ if ( options . remoteAddress ) {
87+ const remoteLabel = colors . bold ( "Remote:" ) ;
88+ const remoteAddress = colors . cyan ( options . remoteAddress ) ;
89+ // deno-lint-ignore no-console
90+ console . log ( ` ${ remoteLabel } ${ remoteAddress } \n` ) ;
91+ }
92+ } ;
93+ }
4794
48- Deno . serve ;
95+ async function listenOnFreePort (
96+ options : ListenOptions ,
97+ handler : (
98+ request : Request ,
99+ info ?: Deno . ServeHandlerInfo ,
100+ ) => Promise < Response > ,
101+ ) {
102+ // No port specified, check for a free port. Instead of picking just
103+ // any port we'll check if the next one is free for UX reasons.
104+ // That way the user only needs to increment a number when running
105+ // multiple apps vs having to remember completely different ports.
106+ let firstError = null ;
107+ for ( let port = 8000 ; port < 8020 ; port ++ ) {
108+ try {
109+ return await Deno . serve ( { ...options , port } , handler ) ;
110+ } catch ( err ) {
111+ if ( err instanceof Deno . errors . AddrInUse ) {
112+ // Throw first EADDRINUSE error if no port is free
113+ if ( ! firstError ) firstError = err ;
114+ continue ;
115+ }
116+ throw err ;
117+ }
118+ }
119+ throw firstError ;
120+ }
49121
50122export let getRouter : < State > ( app : App < State > ) => Router < MiddlewareFn < State > > ;
51123// deno-lint-ignore no-explicit-any
@@ -248,81 +320,16 @@ export class App<State> {
248320
249321 async listen ( options : ListenOptions = { } ) : Promise < void > {
250322 if ( ! options . onListen ) {
251- options . onListen = ( params ) => {
252- const pathname = ( this . config . basePath ) + "/" ;
253- const protocol = "key" in options && options . key && options . cert
254- ? "https:"
255- : "http:" ;
256-
257- let hostname = params . hostname ;
258- // Windows being windows...
259- if (
260- Deno . build . os === "windows" &&
261- ( hostname === "0.0.0.0" || hostname === "::" )
262- ) {
263- hostname = "localhost" ;
264- }
265- // Work around https://github.com/denoland/deno/issues/23650
266- hostname = hostname . startsWith ( "::" ) ? `[${ hostname } ]` : hostname ;
267- const address = colors . cyan (
268- `${ protocol } //${ hostname } :${ params . port } ${ pathname } ` ,
269- ) ;
270- const localLabel = colors . bold ( "Local:" ) ;
271-
272- // Don't spam logs with this on live deployments
273- if ( ! DENO_DEPLOYMENT_ID ) {
274- // deno-lint-ignore no-console
275- console . log ( ) ;
276- // deno-lint-ignore no-console
277- console . log (
278- colors . bgRgb8 ( colors . rgb8 ( " 🍋 Fresh ready " , 0 ) , 121 ) ,
279- ) ;
280- const sep = options . remoteAddress ? "" : "\n" ;
281- const space = options . remoteAddress ? " " : "" ;
282- // deno-lint-ignore no-console
283- console . log ( ` ${ localLabel } ${ space } ${ address } ${ sep } ` ) ;
284- if ( options . remoteAddress ) {
285- const remoteLabel = colors . bold ( "Remote:" ) ;
286- const remoteAddress = colors . cyan ( options . remoteAddress ) ;
287- // deno-lint-ignore no-console
288- console . log ( ` ${ remoteLabel } ${ remoteAddress } \n` ) ;
289- }
290- }
291- } ;
323+ options . onListen = createOnListen ( this . config . basePath , options ) ;
292324 }
293325
294326 const handler = await this . handler ( ) ;
295327 if ( options . port ) {
296328 await Deno . serve ( options , handler ) ;
297- } else {
298- // No port specified, check for a free port. Instead of picking just
299- // any port we'll check if the next one is free for UX reasons.
300- // That way the user only needs to increment a number when running
301- // multiple apps vs having to remember completely different ports.
302- let firstError ;
303- for ( let port = 8000 ; port < 8020 ; port ++ ) {
304- try {
305- await Deno . serve ( { ...options , port } , handler ) ;
306- firstError = undefined ;
307- break ;
308- } catch ( err ) {
309- if ( err instanceof Deno . errors . AddrInUse ) {
310- // Throw first EADDRINUSE error
311- // if no port is free
312- if ( ! firstError ) {
313- firstError = err ;
314- }
315- continue ;
316- }
317-
318- throw err ;
319- }
320- }
321-
322- if ( firstError ) {
323- throw firstError ;
324- }
329+ return ;
325330 }
331+
332+ await listenOnFreePort ( options , handler ) ;
326333 }
327334}
328335
0 commit comments