one
Version:
One is a new React Framework that makes Vite serve both native and web.
95 lines (81 loc) • 2.65 kB
text/typescript
import { 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: Props | 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') {
if (props && typeof props === 'object' && 'children' in props) {
const { children, ...restProps } = props
htmlProps = restProps
return traverse(children as React.ReactNode)
}
return null
}
if (type === 'head') {
head = reactElement
return null
}
if (type === 'body') {
if (props && typeof props === 'object' && 'children' in props) {
const { children, ...restProps } = props
bodyProps = restProps
if (process.env.TAMAGUI_TARGET === 'native') {
// must traverse children so nested HTML elements (e.g. <div>) get filtered on native
return traverse(children as React.ReactNode)
}
return children as React.ReactNode
}
return null
}
if (process.env.TAMAGUI_TARGET === 'native') {
if (
isValidElement(element) &&
typeof element.type === 'string' &&
element.type.toLowerCase() === element.type
) {
// filter out HTML elements on native (e.g. <div>, <meta>)
// preserve children so <div><Slot/></div> renders <Slot/> instead of nothing
if (element.props && typeof element.props === 'object' && 'children' in element.props) {
return traverse(element.props.children as React.ReactNode)
}
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,
}
}