2022-06-07 10:03:42 +00:00
|
|
|
import type { Context } from '../../context'
|
|
|
|
import type { Next } from '../../hono'
|
|
|
|
import { escape } from '../../utils/html'
|
|
|
|
|
|
|
|
declare global {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
|
|
namespace h.JSX {
|
|
|
|
interface IntrinsicElements {
|
2022-06-10 01:19:04 +00:00
|
|
|
[tagName: string]: Record<string, any>
|
2022-06-07 10:03:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const jsx = () => {
|
|
|
|
return async (c: Context, next: Next) => {
|
|
|
|
c.render = (content: string) => {
|
|
|
|
const output = `<!doctype html>${content.toString()}`
|
|
|
|
return c.html(output)
|
|
|
|
}
|
|
|
|
await next()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type EscapedString = string & { isEscaped: true }
|
|
|
|
|
|
|
|
export const h = (
|
|
|
|
tag: string | Function,
|
|
|
|
props: Record<string, any>,
|
|
|
|
...children: (string | EscapedString)[]
|
|
|
|
): EscapedString => {
|
|
|
|
if (typeof tag === 'function') {
|
2022-06-10 01:19:04 +00:00
|
|
|
return tag.call(null, { ...props, children: children.length <= 1 ? children[0] : children })
|
2022-06-07 10:03:42 +00:00
|
|
|
}
|
2022-06-10 01:19:04 +00:00
|
|
|
|
|
|
|
let result = `<${tag}`
|
|
|
|
|
2022-06-07 10:03:42 +00:00
|
|
|
const propsKeys = Object.keys(props || {})
|
|
|
|
for (let i = 0, len = propsKeys.length; i < len; i++) {
|
2022-06-10 01:19:04 +00:00
|
|
|
const v = props[propsKeys[i]]
|
|
|
|
if (propsKeys[i] === 'dangerouslySetInnerHTML') {
|
|
|
|
if (children.length > 0) {
|
|
|
|
throw 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.'
|
|
|
|
}
|
|
|
|
|
|
|
|
const escapedString = new String(v.__html) as EscapedString
|
|
|
|
escapedString.isEscaped = true
|
|
|
|
children = [escapedString]
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
else if (v === null || v === undefined) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
result += ` ${propsKeys[i]}="${escape(v.toString())}"`
|
2022-06-07 10:03:42 +00:00
|
|
|
}
|
|
|
|
|
2022-06-10 01:19:04 +00:00
|
|
|
result += '>'
|
|
|
|
|
|
|
|
const flattenChildren = children.flat(Infinity)
|
|
|
|
for (let i = 0, len = flattenChildren.length; i < len; i++) {
|
|
|
|
const child = flattenChildren[i]
|
|
|
|
if (typeof child === 'boolean' || child === null || child === undefined) {
|
|
|
|
continue
|
|
|
|
} else if (typeof child === 'object' && (child as any).isEscaped) {
|
|
|
|
result += child
|
|
|
|
} else {
|
|
|
|
result += escape(child.toString())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result += `</${tag}>`
|
|
|
|
|
|
|
|
const escapedString = new String(result) as EscapedString
|
|
|
|
escapedString.isEscaped = true
|
2022-06-07 10:03:42 +00:00
|
|
|
|
2022-06-10 01:19:04 +00:00
|
|
|
return escapedString
|
2022-06-07 10:03:42 +00:00
|
|
|
}
|