diff --git a/src/context.ts b/src/context.ts index e3dbc3c5c..00e9fb0d0 100644 --- a/src/context.ts +++ b/src/context.ts @@ -9,6 +9,7 @@ import type { RouterRoute, TypedResponse, } from './types' +import type { HTMLInput } from './utils/html' import { HtmlEscapedCallbackPhase, resolveCallback } from './utils/html' import type { RedirectStatusCode, StatusCode } from './utils/http-status' import type { @@ -192,7 +193,7 @@ type JSONRespondReturn< /** * Interface representing a function that responds with HTML content. * - * @param html - The HTML content to respond with, which can be a string or a Promise that resolves to a string. + * @param html - The HTML content to respond with, which can be anything with a `toString` method or a Promise that resolves to anything with a `toString` method. * @param status - (Optional) The HTTP status code for the response. * @param headers - (Optional) A record of headers to include in the response. * @param init - (Optional) The response initialization object. @@ -200,12 +201,12 @@ type JSONRespondReturn< * @returns A Response object or a Promise that resolves to a Response object. */ interface HTMLRespond { - >( + >( html: T, status?: StatusCode, headers?: HeaderRecord - ): T extends string ? Response : Promise - >(html: T, init?: ResponseInit): T extends string + ): T extends HTMLInput ? Response : Promise + >(html: T, init?: ResponseInit): T extends HTMLInput ? Response : Promise } @@ -836,7 +837,7 @@ export class Context< } html: HTMLRespond = ( - html: string | Promise, + html: HTMLInput | Promise, arg?: StatusCode | ResponseInit, headers?: HeaderRecord ): Response | Promise => { @@ -845,10 +846,10 @@ export class Context< if (typeof html === 'object') { if (!(html instanceof Promise)) { - html = (html as string).toString() // HtmlEscapedString object to string + html = html.toString() // HtmlEscapedString object to string } - if ((html as string | Promise) instanceof Promise) { - return (html as unknown as Promise) + if (html instanceof Promise) { + return html .then((html) => resolveCallback(html, HtmlEscapedCallbackPhase.Stringify, false, {})) .then((html) => { return typeof arg === 'number' diff --git a/src/utils/html.ts b/src/utils/html.ts index d35572634..d7bc87a01 100644 --- a/src/utils/html.ts +++ b/src/utils/html.ts @@ -20,6 +20,15 @@ export type HtmlEscaped = { } export type HtmlEscapedString = string & HtmlEscaped +/** + * Anything that has a `toString` method. + */ +type HasToString = { + toString(): string +} + +export type HTMLInput = string | HtmlEscapedString | HasToString + /** * StringBuffer contains string and Promise alternately * The length of the array will be odd, the odd numbered element will be a string, @@ -140,7 +149,7 @@ export const resolveCallbackSync = (str: string | HtmlEscapedString): string => } export const resolveCallback = async ( - str: string | HtmlEscapedString, + str: HTMLInput, phase: (typeof HtmlEscapedCallbackPhase)[keyof typeof HtmlEscapedCallbackPhase], preserveCallbacks: boolean, context: object, @@ -148,12 +157,12 @@ export const resolveCallback = async ( ): Promise => { const callbacks = (str as HtmlEscapedString).callbacks as HtmlEscapedCallback[] if (!callbacks?.length) { - return Promise.resolve(str) + return Promise.resolve(str.toString()) } if (buffer) { buffer[0] += str } else { - buffer = [str] + buffer = [str.toString()] } const resStr = Promise.all(callbacks.map((c) => c({ phase, buffer, context }))).then((res) =>