Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 77 additions & 70 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,80 @@ export type ListenOptions =
& {
remoteAddress?: string;
};
function createOnListen(
basePath: string,
options: ListenOptions,
): (localAddr: Deno.NetAddr) => void {
return (params) => {
// Don't spam logs with this on live deployments
if (DENO_DEPLOYMENT_ID) return;

const pathname = basePath + "/";
const protocol = "key" in options && options.key && options.cert
? "https:"
: "http:";

let hostname = params.hostname;
// Windows being windows...
if (
Deno.build.os === "windows" &&
(hostname === "0.0.0.0" || hostname === "::")
) {
hostname = "localhost";
}
// Work around https://github.com/denoland/deno/issues/23650
hostname = hostname.startsWith("::") ? `[${hostname}]` : hostname;

// deno-lint-ignore no-console
console.log();
// deno-lint-ignore no-console
console.log(
colors.bgRgb8(colors.rgb8(" 🍋 Fresh ready ", 0), 121),
);
const sep = options.remoteAddress ? "" : "\n";
const space = options.remoteAddress ? " " : "";

const localLabel = colors.bold("Local:");
const address = colors.cyan(
`${protocol}//${hostname}:${params.port}${pathname}`,
);
// deno-lint-ignore no-console
console.log(` ${localLabel} ${space}${address}${sep}`);
if (options.remoteAddress) {
const remoteLabel = colors.bold("Remote:");
const remoteAddress = colors.cyan(options.remoteAddress);
// deno-lint-ignore no-console
console.log(` ${remoteLabel} ${remoteAddress}\n`);
}
};
}

Deno.serve;
async function listenOnFreePort(
options: ListenOptions,
handler: (
request: Request,
info?: Deno.ServeHandlerInfo,
) => Promise<Response>,
) {
// No port specified, check for a free port. Instead of picking just
// any port we'll check if the next one is free for UX reasons.
// That way the user only needs to increment a number when running
// multiple apps vs having to remember completely different ports.
let firstError = null;
for (let port = 8000; port < 8020; port++) {
try {
return await Deno.serve({ ...options, port }, handler);
} catch (err) {
if (err instanceof Deno.errors.AddrInUse) {
// Throw first EADDRINUSE error if no port is free
if (!firstError) firstError = err;
continue;
}
throw err;
}
}
throw firstError;
}

export let getRouter: <State>(app: App<State>) => Router<MiddlewareFn<State>>;
// deno-lint-ignore no-explicit-any
Expand Down Expand Up @@ -248,81 +320,16 @@ export class App<State> {

async listen(options: ListenOptions = {}): Promise<void> {
if (!options.onListen) {
options.onListen = (params) => {
const pathname = (this.config.basePath) + "/";
const protocol = "key" in options && options.key && options.cert
? "https:"
: "http:";

let hostname = params.hostname;
// Windows being windows...
if (
Deno.build.os === "windows" &&
(hostname === "0.0.0.0" || hostname === "::")
) {
hostname = "localhost";
}
// Work around https://github.com/denoland/deno/issues/23650
hostname = hostname.startsWith("::") ? `[${hostname}]` : hostname;
const address = colors.cyan(
`${protocol}//${hostname}:${params.port}${pathname}`,
);
const localLabel = colors.bold("Local:");

// Don't spam logs with this on live deployments
if (!DENO_DEPLOYMENT_ID) {
// deno-lint-ignore no-console
console.log();
// deno-lint-ignore no-console
console.log(
colors.bgRgb8(colors.rgb8(" 🍋 Fresh ready ", 0), 121),
);
const sep = options.remoteAddress ? "" : "\n";
const space = options.remoteAddress ? " " : "";
// deno-lint-ignore no-console
console.log(` ${localLabel} ${space}${address}${sep}`);
if (options.remoteAddress) {
const remoteLabel = colors.bold("Remote:");
const remoteAddress = colors.cyan(options.remoteAddress);
// deno-lint-ignore no-console
console.log(` ${remoteLabel} ${remoteAddress}\n`);
}
}
};
options.onListen = createOnListen(this.config.basePath, options);
}

const handler = await this.handler();
if (options.port) {
await Deno.serve(options, handler);
} else {
// No port specified, check for a free port. Instead of picking just
// any port we'll check if the next one is free for UX reasons.
// That way the user only needs to increment a number when running
// multiple apps vs having to remember completely different ports.
let firstError;
for (let port = 8000; port < 8020; port++) {
try {
await Deno.serve({ ...options, port }, handler);
firstError = undefined;
break;
} catch (err) {
if (err instanceof Deno.errors.AddrInUse) {
// Throw first EADDRINUSE error
// if no port is free
if (!firstError) {
firstError = err;
}
continue;
}

throw err;
}
}

if (firstError) {
throw firstError;
}
return;
}

await listenOnFreePort(options, handler);
}
}

Expand Down
Loading