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('')}${normalizedNodeName}>`;
+};
+
+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';
- 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;