mirror of
https://github.com/honojs/hono.git
synced 2024-12-01 10:51:01 +00:00
fedeb3d696
* feat(jsx): Support async component. * chore: denoify * feat: Support nested async components. * chore: denoify * Remove unintended file from commit. * test(jsx): Add test for html tagged template strings. * feat: Introduce streaming API with `Suspense` and `use`. * chore: denoify * "use" receives only Promise. * feat: Support multiple calls and nested calls to "use". * refactor: tweaks replacement script. * test: Add test for replacement result of streaming * chore: denoify * test: Add test "Complex fallback content" * refactor: Add "typescript-eslint/no-explicit-any". * Use jsdom instead of happy-dom due to ci failure. * test: update test data for suspense. * refactor: Remove excessive exports * refactor: Changed initialization of `useContexts[]` to clarify intent. * perf: improve `renderToReadableStream()` performance. * chore: denoify * pref: Shortened the output JS a bit. * pref: Delete unneeded condition * docs(jsx/streaming): Add `@experimental` flag to streaming API. * fix(jsx/streadming): fix loop when using fullfilled Promise with null or undefined. * fix(jsx/streaming): Catch unhandledRejection to avoid streaming not being closed. * chore(jsx/streaming): Add entries for jsx/streaming to package.json. * chore: denoify * feat(jsx/streaming): Support the Async Component inside Suspense. * chore: denoify * feat(jsx/streaming): remove implementation of `use()`.
48 lines
1.6 KiB
TypeScript
48 lines
1.6 KiB
TypeScript
import { escapeToBuffer, stringBufferToString } from '../../utils/html.ts'
|
|
import type { StringBuffer, HtmlEscaped, HtmlEscapedString } from '../../utils/html.ts'
|
|
|
|
export const raw = (value: unknown): HtmlEscapedString => {
|
|
const escapedString = new String(value) as HtmlEscapedString
|
|
escapedString.isEscaped = true
|
|
|
|
return escapedString
|
|
}
|
|
|
|
export const html = (
|
|
strings: TemplateStringsArray,
|
|
...values: unknown[]
|
|
): HtmlEscapedString | Promise<HtmlEscapedString> => {
|
|
const buffer: StringBuffer = ['']
|
|
|
|
for (let i = 0, len = strings.length - 1; i < len; i++) {
|
|
buffer[0] += strings[i]
|
|
|
|
const children =
|
|
values[i] instanceof Array ? (values[i] as Array<unknown>).flat(Infinity) : [values[i]]
|
|
for (let i = 0, len = children.length; i < len; i++) {
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const child = children[i] as any
|
|
if (typeof child === 'string') {
|
|
escapeToBuffer(child, buffer)
|
|
} else if (typeof child === 'boolean' || child === null || child === undefined) {
|
|
continue
|
|
} else if (
|
|
(typeof child === 'object' && (child as HtmlEscaped).isEscaped) ||
|
|
typeof child === 'number'
|
|
) {
|
|
const tmp = child.toString()
|
|
if (tmp instanceof Promise) {
|
|
buffer.unshift('', tmp)
|
|
} else {
|
|
buffer[0] += tmp
|
|
}
|
|
} else {
|
|
escapeToBuffer(child.toString(), buffer)
|
|
}
|
|
}
|
|
}
|
|
buffer[0] += strings[strings.length - 1]
|
|
|
|
return buffer.length === 1 ? raw(buffer[0]) : stringBufferToString(buffer).then((str) => raw(str))
|
|
}
|