Skip to content

Commit 0cdf24c

Browse files
committed
Fixes for 3.0.0, add support for shallow rendering, and dangerouslySetInnerHTML.
1 parent 54426e2 commit 0cdf24c

File tree

1 file changed

+85
-19
lines changed

1 file changed

+85
-19
lines changed

src/index.js

Lines changed: 85 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
const NO_RENDER = { render: false };
1+
2+
const SHALLOW = { shallow: true };
23

34
const ESC = {
45
'<': '&lt;',
@@ -9,10 +10,45 @@ const ESC = {
910

1011
const HOP = Object.prototype.hasOwnProperty;
1112

12-
let escape = s => String(s).replace(/[<>"&]/, a => ESC[a] || a);
13+
let escape = s => String(s).replace(/[<>"&]/, escapeChar);
14+
15+
let escapeChar = a => ESC[a] || a;
16+
17+
18+
/** Render Preact JSX + Components to an HTML string.
19+
* @name render
20+
* @function
21+
* @param {VNode} vnode JSX VNode to render.
22+
* @param {Object} [options={}] Rendering options
23+
* @param {Boolean} [options.shallow=false] If `true`, renders nested Components as HTML elements (`<Foo a="b" />`).
24+
* @param {Boolean} [options.xml=false] If `true`, uses self-closing tags for elements without children.
25+
* @param {Object} [context={}] Optionally pass an initial context object through the render path.
26+
*/
27+
renderToString.render = renderToString;
28+
29+
30+
/** Only render elements, leaving Components inline as `<ComponentName ... />`.
31+
* This method is just a convenience alias for `render(vnode, context, { shallow:true })`
32+
* @name shallow
33+
* @function
34+
* @param {VNode} vnode JSX VNode to render.
35+
* @param {Object} [context={}] Optionally pass an initial context object through the render path.
36+
*/
37+
renderToString.shallowRender = (vnode, context) => renderToString(vnode, context, SHALLOW);
1338

14-
export default function renderToString(vnode) {
39+
40+
/** You can actually skip preact entirely and import this empty Component base class (or not use a base class at all).
41+
* preact-render-to-string doesn't use any of Preact's functionality to do its job.
42+
* @name Component
43+
* @class
44+
*/
45+
// renderToString.Component = function Component(){};
46+
47+
48+
/** The default export is an alias of `render()`. */
49+
export default function renderToString(vnode, context, opts, inner) {
1550
let { nodeName, attributes, children } = vnode || EMPTY;
51+
context = context || {};
1652

1753
// #text nodes
1854
if (!nodeName) {
@@ -21,40 +57,70 @@ export default function renderToString(vnode) {
2157

2258
// components
2359
if (typeof nodeName==='function') {
24-
let props = { children, ...attributes },
25-
rendered;
26-
27-
if (typeof nodeName.prototype.render!=='function') {
28-
// stateless functional components
29-
rendered = nodeName(props);
60+
if (opts && opts.shallow && inner) {
61+
nodeName = nodeName.prototype.displayName || nodeName.name;
3062
}
3163
else {
32-
// class-based components
33-
let c = new nodeName();
34-
c.setProps(props, NO_RENDER);
35-
rendered = c.render(c.props = props, c.state);
64+
let props = { children, ...attributes },
65+
rendered;
66+
67+
if (typeof nodeName.prototype.render!=='function') {
68+
// stateless functional components
69+
rendered = nodeName(props, context);
70+
}
71+
else {
72+
// class-based components
73+
let c = new nodeName(props, context);
74+
c.props = props;
75+
c.context = context;
76+
rendered = c.render(c.props, c.state, c.context);
77+
78+
if (c.getChildContext) {
79+
context = c.getChildContext();
80+
}
81+
}
82+
83+
return renderToString(rendered, context, opts, true);
3684
}
37-
return renderToString(rendered);
3885
}
3986

4087
// render JSX to HTML
41-
let s = `<${nodeName}`;
88+
let s = `<${nodeName}`,
89+
html;
90+
4291
for (let name in attributes) {
4392
if (HOP.call(attributes, name)) {
44-
let v = attributes[name];
93+
let v = attributes[name],
94+
type = typeof v;
4595
if (name==='className') {
4696
if (attributes['class']) continue;
4797
name = 'class';
4898
}
49-
if (v!==null && v!==undefined) {
99+
if (name==='dangerouslySetInnerHTML') {
100+
html = v && v.__html;
101+
}
102+
else if (v!==null && v!==undefined && type!=='function') {
50103
s += ` ${name}="${escape(v)}"`;
51104
}
52105
}
53106
}
54107
s += '>';
55-
if (children && children.length) {
56-
s += children.map(renderToString).join('');
108+
109+
if (html) {
110+
s += html;
57111
}
112+
else {
113+
let len = children && children.length;
114+
if (len) {
115+
for (let i=0; i<len; i++) {
116+
s += renderToString(children[i], context, opts, true);
117+
}
118+
}
119+
else if (opts && opts.xml) {
120+
return s.substring(0, s.length-1) + ' />';
121+
}
122+
}
123+
58124
s += `</${nodeName}>`
59125
return s;
60126
};

0 commit comments

Comments
 (0)