-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
changed SSR preactjs/preact-render-to-string#30 (comment)
- Loading branch information
1 parent
b77c430
commit 49bd7eb
Showing
1 changed file
with
61 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(<nothing/>, parent, root).remove(); | ||
} | ||
html: () => serializeHtml(root), | ||
tearDown: () => render(<nothing />, 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<el.attributes.length; i++) { | ||
let name = el.attributes[i].name; | ||
if (name==='class') hasClass = true; | ||
str += ' '+name+'="'+esc(el.attributes[i].value)+'"'; | ||
} | ||
if (el.className && !hasClass) str += ' class="'+el.className+'"'; | ||
str += '>'; | ||
for (i=0; i<el.childNodes.length; i++) { | ||
c = serializeHtml(el.childNodes[i]); | ||
if (c) str += c; | ||
} | ||
return str + '</'+name+'>'; | ||
} | ||
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; |