import { toChildArray, Component } from 'preact' import { mapKeys } from 'lowline' const CLASSNAME = '__preact_generated__' const DOMAttributeNames = { acceptCharset: 'accept-charset', className: 'class', htmlFor: 'for', httpEquiv: 'http-equiv', } const isBrowser = typeof window !== 'undefined' let mounted = [] function reducer(components) { return components .map((c) => toChildArray(c.props.children)) .reduce((result, c) => result.concat(c), []) .reverse() .filter(unique()) .reverse() .reduce( (result, c) => { if (c.type === 'title') { result.title += toChildArray(c.props.children).join('') } else { result.tags.push({ type: c.type, attributes: mapKeys(c.props, (_value, key) => DOMAttributeNames[key] || key), }) } return result }, { title: '', tags: [] }, ) } function updateClient({ title, tags }) { const head = document.head const prevElements = Array.from(head.getElementsByClassName(CLASSNAME)) for (const tag of tags) { const el = createDOMElement(tag) const prevIndex = prevElements.findIndex((prevEl) => prevEl.isEqualNode(el)) if (~prevIndex) { prevElements.splice(prevIndex, 1) } else { head.appendChild(el) } } prevElements.forEach((prevEl) => prevEl.remove()) document.title = title } function createDOMElement(tag) { const el = document.createElement(tag.type) const attributes = tag.attributes || {} el.setAttribute('class', CLASSNAME) for (const p in attributes || {}) { const attribute = DOMAttributeNames[p] || p.toLowerCase() el.setAttribute(attribute, attributes[p]) } return el } const METATYPES = ['name', 'httpEquiv', 'charSet', 'itemProp'] // returns a function for filtering head child elements // which shouldn't be duplicated, like