diff --git a/src/preact-dom-renderer.js b/src/preact-dom-renderer.js index 10042e3..c5349d2 100644 --- a/src/preact-dom-renderer.js +++ b/src/preact-dom-renderer.js @@ -1,54 +1,71 @@ -var preact = require('preact'); -var undom = require('undom'); -var h = preact.h; - -/** - * Prototype stateful server renderer. - */ -var doc; -module.exports = function createRenderer() { +import {h, render} from 'preact'; +import undom from 'undom'; + +const VOID_ELEMENTS = [ + 'area', + 'base', + 'br', + 'col', + 'embed', + 'hr', + 'img', + 'input', + 'link', + 'meta', + 'param', + 'source', + 'track', + 'wbr', +]; + +const ESC = { + '&': 'amp', + '<': 'lt', + '>': 'gt', + '"': 'quot', + "'": 'apos', +}; + +const enc = s => s.replace(/[&'"<>]/g, a => `&${ESC[a]};`); +const attr = (a) => { + if (a.name === 'class' && a.value === '') { + return ''; + } + return ` ${a.name.replace(/^html/, '')}${a.value === 'true' || a.value === '' ? '' : `="${enc(a.value)}"`}`; +}; + +const serializeHtml = (el) => { + const {nodeType, normalizedNodeName, textContent, attributes, childNodes, innerHTML} = el; + if (nodeType === 3) { + return enc(textContent); + } + const start = `<${normalizedNodeName}${attributes.map(attr).join('')}`; + if (VOID_ELEMENTS.includes(normalizedNodeName)) { + return `${start} />`; + } + return `${start}>${innerHTML || childNodes.map(serializeHtml).join('')}`; +}; + +let doc; +const preactDomRenderer = () => { if (!doc) { doc = undom(); Object.assign(global, doc.defaultView); } - var root, parent = doc.createElement('x-root'); + let root; + const parent = doc.createElement('x-root'); doc.body.appendChild(parent); - return { - render: function(jsx) { - root = preact.render(jsx, parent, root); - return this; - }, - html: function() { - return serializeHtml(root); + const renderer = { + render: (jsx) => { + root = render(jsx, parent, root); + return renderer; }, - tearDown: function() { - preact.render(, parent, root).remove(); - } + html: () => serializeHtml(root), + tearDown: () => render(, parent, root).remove(), }; -} - -function serializeHtml(el) { - if (el.nodeType===3) return esc(el.nodeValue); - var name = String(el.nodeName).toLowerCase(), - str = '<'+name, - hasClass = false, - c, i; - for (i=0; i'; -} + return renderer; +}; -function esc(str) { return String(str).replace(/[&<>"']/g, escMap); } -function escMap(s) { return '&'+map[s]+';'; } -var map = {'&':'amp','<':'lt','>':'gt','"':'quot',"'":'apos'}; +export default preactDomRenderer;