Skip to content

Instantly share code, notes, and snippets.

@jaredatron
Last active June 9, 2020 01:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaredatron/2a13228d4aaa6e8ee53a9e510e860355 to your computer and use it in GitHub Desktop.
Save jaredatron/2a13228d4aaa6e8ee53a9e510e860355 to your computer and use it in GitHub Desktop.
Rendering raw HTML as Preact components
import { h, Fragment } from 'preact'
import { memo } from 'preact/compat'
import PropTypes from 'prop-types'
import Link from 'components/Link'
const RawHTML = memo(({source = '', ignoreNodes = []}) => {
const parser = new global.DOMParser()
const doc = parser.parseFromString(source, "text/html")
const options = {
ignoreNodes: [...DEFAULT_INGNORED_NODES, ...ignoreNodes],
}
return h(Fragment, {}, ...renderChildren(doc.body, options))
})
RawHTML.propTypes = {
source: PropTypes.string.isRequired,
ignoreNodes: PropTypes.arrayOf(PropTypes.string),
}
export default RawHTML
const DEFAULT_INGNORED_NODES = (
'#comment style script body html meta title'
).split(' ')
function renderChildren(node, options){
const children = []
Array.from(node.childNodes).forEach(childNode => {
if (options.ignoreNodes.includes(childNode.nodeName.toLowerCase()))
return
if (childNode.nodeName === "#text"){
children.push(childNode.nodeValue)
return
}
let component = childNode.localName
const props = attributesToProps(childNode)
if (component === 'a' || 'href' in props){
component = Link
props.type = 'link'
}
children.push(
h(component, props, ...renderChildren(childNode, options))
)
})
return children
}
function attributesToProps(node){
const props = {}
Array.from(node.attributes).forEach(attribute => {
let key = attribute.name
let value = attribute.nodeValue
if (value === '' && typeof node[key] === 'boolean')
value = node[key]
props[key] = value
})
return props
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment