one
Version:
One is a new React Framework that makes Vite serve both native and web.
82 lines (68 loc) • 2.06 kB
text/typescript
import { cloneElement, isValidElement } from 'react'
type Props = Record<string, any>
export type FoundRootHTML = {
children: React.ReactElement
htmlProps?: Props
bodyProps?: Props
head?: React.ReactElement
}
/**
* To enable custom <html> and other html-like stuff in the root _layout
* we are doing some fancy stuff, namely, just capturing the root layout return
* value and deep-mapping over it.
*
* On server, we filter it out and hoist it to the parent root html in createApp
*
* On client, we just filter it out completely as in One we don't hydrate html
*/
export function filterRootHTML(el: React.ReactNode): FoundRootHTML {
let htmlProps: Props | undefined
let bodyProps: React.ReactElement | undefined
let head: React.ReactElement | undefined
function traverse(element: React.ReactNode) {
if (!element || typeof element !== 'object') {
return element
}
if (Array.isArray(element)) {
return element.map(traverse)
}
const reactElement = element as React.ReactElement
const { type, props } = reactElement
if (type === 'html') {
const { children, ...restProps } = reactElement.props
htmlProps = restProps
return traverse(children)
}
if (type === 'head') {
head = reactElement
return null
}
if (type === 'body') {
const { children, ...restProps } = reactElement.props
bodyProps = restProps
return children
}
if (process.env.TAMAGUI_TARGET === 'native') {
if (
isValidElement(element) &&
typeof element.type === 'string' &&
element.type.toLowerCase() === element.type
) {
// filter out things like <meta /> etc on native
// because it could just be thown in <html> or a fragment to be hoisted on web
return null
}
}
return element
}
const children =
traverse(el) ||
// if none found, we assume they aren't returning any html so just pass it on
el
return {
children,
htmlProps,
bodyProps,
head,
}
}