9bb9c3cc by Adam Heath

Add react rendering.

1 parent e01f913d
import React from 'react'
import { ELEMENT_NODE, TEXT_NODE } from 'ultrahtml'
import { parseHtml, createMatcher, findNode } from 'astro-wt/html'
import { decode } from 'html-entities'
export const ReparseStaticChildren = (replacements, Component) => (props) => {
const { children = {}, ...rest } = props
const { props: { value: staticHtml } = {} } = children
if (!staticHtml) return <Component {...props}/>
const bodyToParse = String(staticHtml)
const ultra = Replace({
debug: 2,
html: bodyToParse,
replacements,
})
return <Component {...rest}>{ultra}</Component>
}
export const fixAttributes = (attributes) => {
const { class: className, srcset: srcSet, maxlength: maxLength, for: htmlFor, ...rest } = attributes
const result = rest
if (className !== undefined) result.className = className
if (srcSet !== undefined) result.srcSet = srcSet
if (maxLength !== undefined) result.maxLength = maxLength
if (htmlFor !== undefined) result.htmlFor = htmlFor
return result
return Object.fromEntries(Object.entries(result).map(([ key, value ]) => {
return [ key, decode(value) ]
}))
}
export const CreateReplacement = (Element, propsOrCreator) => (matchOptions) => {
const { debug, parent, node, index, replacers } = matchOptions
const props = propsOrCreator instanceof Function ? propsOrCreator(matchOptions) : propsOrCreator
return (
<Element key={index} {...fixAttributes({...node.attributes, ...props})}>
{Children({ debug, parent, children: node.children, replacers})}
</Element>
)
}
export const DeleteHandler = () => null
export const Replace = (props) => {
const { debug = 0, html, xpath, replacements } = props
const doc = props.node ? props.node : parseHtml(html, { react: false })
const node = xpath ? findNode(doc, xpath) : doc
const replacers = Object.entries(replacements).map(([ selector, Handler ]) => [ createMatcher(selector), Handler ])
const nextDebug = debug ? debug - 1 : 0
if (debug) console.log('Replace', { html, replacers, node })
const result = Match({ debug: nextDebug, node, replacers })
if (debug) console.log('Replace result', result)
return result
}
export const Match = (props) => {
const { debug = 0, parent = null, node, index = 0, replacers } = props
const nextDebug = debug ? debug - 1 : 0
for (const [ matcher, Handler ] of replacers) {
if (matcher(node, parent, index, false)) {
return Handler({ debug: nextDebug, parent, node, index, replacers })
}
}
if (debug) console.log('Match:nothing', {node})
return Node({ debug: nextDebug, parent, node, index, replacers })
}
export const RAW_TAGS = new Set(['script', 'style'])
export const Node = (props) => {
const { debug = 0, parent, node, index, replacers } = props
const { name: Name, attributes } = node
const nextDebug = debug ? debug - 1 : 0
if (node.type === TEXT_NODE) {
if (debug) console.log('Node:text', { value: node.value })
return node.value ? decode(node.value) : null
//return <React.Fragment dangerouslySetInnerHTML={{ __html: node.value }}/>
} else if (node.type === ELEMENT_NODE) {
const elementProps = fixAttributes(attributes)
if (node.isSelfClosingTag) {
if (debug) console.log('Node:selfClosing', { node })
return <Name key={index} {...elementProps}/>
} else {
if (debug) console.log('Node:element', { node, children: node.children })
let children = node.children
if (children.length === 1) {
const [ firstChild ] = children
if (firstChild.type === TEXT_NODE && !firstChild.value) children = null
}
if (Name === 'astro-island') console.log('astro-island', { attributes, elementProps })
if (Name === 'script') console.log('script', { node, children })
if (RAW_TAGS.has(Name) && children) {
if (Array.isArray(children) && children.length === 1) {
return <Name key={index} {...elementProps} dangerouslySetInnerHTML={{ __html: children[ 0 ].value } }/>
}
}
return (
<Name key={index} {...elementProps} children={children && Children({ debug: nextDebug, parent: node, children: node.children, replacers})}/>
)
}
} else {
if (debug) console.log('Node:children', { children: node.children })
return Children({ debug: nextDebug, parent: node, children: node.children, replacers })
}
}
export const Children = (props) => {
const { debug = 0, parent, children, replacers } = props
const nextDebug = debug ? debug - 1 : 0
if (Array.isArray(children)) {
const result = children.map((child, index) => {
return Match({ debug: nextDebug, parent, node: child, index, replacers })
})
if (result.length === 1) return result[0]
if (result.length === 0) return null
return result
} else if (!children) {
return ''
} else {
return Node({ debug: nextDebug, parent, node: children, index: 0, replacers })
}
}
......@@ -4,6 +4,7 @@
"exports": {
"./astro": "./lib/astro.ts",
"./html": "./lib/html.ts",
"./react": "./lib/react.jsx",
"./remote-content": "./lib/remote-content.ts"
},
"scripts": {
......