preact-render-to-string 6.0.0 → 6.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.js CHANGED
@@ -1,381 +1,385 @@
1
- import { encodeEntities, styleObjToCss, UNSAFE_NAME, XLINK } from './util';
2
- import { options, h, Fragment } from 'preact';
3
- import {
4
- CHILDREN,
5
- COMMIT,
6
- COMPONENT,
7
- DIFF,
8
- DIFFED,
9
- DIRTY,
10
- NEXT_STATE,
11
- PARENT,
12
- RENDER,
13
- SKIP_EFFECTS,
14
- VNODE
15
- } from './constants';
16
-
17
- /** @typedef {import('preact').VNode} VNode */
18
-
19
- const EMPTY_ARR = [];
20
- const isArray = Array.isArray;
21
- const assign = Object.assign;
22
-
23
- // Global state for the current render pass
24
- let beforeDiff, afterDiff, renderHook, ummountHook;
25
-
26
- /**
27
- * Render Preact JSX + Components to an HTML string.
28
- * @param {VNode} vnode JSX Element / VNode to render
29
- * @param {Object} [context={}] Initial root context object
30
- * @returns {string} serialized HTML
31
- */
32
- export default function renderToString(vnode, context) {
33
- // Performance optimization: `renderToString` is synchronous and we
34
- // therefore don't execute any effects. To do that we pass an empty
35
- // array to `options._commit` (`__c`). But we can go one step further
36
- // and avoid a lot of dirty checks and allocations by setting
37
- // `options._skipEffects` (`__s`) too.
38
- const previousSkipEffects = options[SKIP_EFFECTS];
39
- options[SKIP_EFFECTS] = true;
40
-
41
- // store options hooks once before each synchronous render call
42
- beforeDiff = options[DIFF];
43
- afterDiff = options[DIFFED];
44
- renderHook = options[RENDER];
45
- ummountHook = options.unmount;
46
-
47
- const parent = h(Fragment, null);
48
- parent[CHILDREN] = [vnode];
49
-
50
- try {
51
- return _renderToString(
52
- vnode,
53
- context || EMPTY_OBJ,
54
- false,
55
- undefined,
56
- parent
57
- );
58
- } finally {
59
- // options._commit, we don't schedule any effects in this library right now,
60
- // so we can pass an empty queue to this hook.
61
- if (options[COMMIT]) options[COMMIT](vnode, EMPTY_ARR);
62
- options[SKIP_EFFECTS] = previousSkipEffects;
63
- EMPTY_ARR.length = 0;
64
- }
65
- }
66
-
67
- // Installed as setState/forceUpdate for function components
68
- function markAsDirty() {
69
- this.__d = true;
70
- }
71
-
72
- const EMPTY_OBJ = {};
73
-
74
- /**
75
- * @param {VNode} vnode
76
- * @param {Record<string, unknown>} context
77
- */
78
- function renderClassComponent(vnode, context) {
79
- let type = /** @type {import("preact").ComponentClass<typeof vnode.props>} */ (vnode.type);
80
-
81
- let c = new type(vnode.props, context);
82
-
83
- vnode[COMPONENT] = c;
84
- c[VNODE] = vnode;
85
-
86
- c.props = vnode.props;
87
- c.context = context;
88
- // turn off stateful re-rendering:
89
- c[DIRTY] = true;
90
-
91
- if (c.state == null) c.state = EMPTY_OBJ;
92
-
93
- if (c[NEXT_STATE] == null) {
94
- c[NEXT_STATE] = c.state;
95
- }
96
-
97
- if (type.getDerivedStateFromProps) {
98
- c.state = assign(
99
- {},
100
- c.state,
101
- type.getDerivedStateFromProps(c.props, c.state)
102
- );
103
- } else if (c.componentWillMount) {
104
- c.componentWillMount();
105
-
106
- // If the user called setState in cWM we need to flush pending,
107
- // state updates. This is the same behaviour in React.
108
- c.state = c[NEXT_STATE] !== c.state ? c[NEXT_STATE] : c.state;
109
- }
110
-
111
- if (renderHook) renderHook(vnode);
112
-
113
- return c.render(c.props, c.state, context);
114
- }
115
-
116
- /**
117
- * Recursively render VNodes to HTML.
118
- * @param {VNode|any} vnode
119
- * @param {any} context
120
- * @param {boolean} isSvgMode
121
- * @param {any} selectValue
122
- * @param {VNode} parent
123
- * @returns {string}
124
- */
125
- function _renderToString(vnode, context, isSvgMode, selectValue, parent) {
126
- // Ignore non-rendered VNodes/values
127
- if (vnode == null || vnode === true || vnode === false || vnode === '') {
128
- return '';
129
- }
130
-
131
- // Text VNodes: escape as HTML
132
- if (typeof vnode !== 'object') {
133
- if (typeof vnode === 'function') return '';
134
- return encodeEntities(vnode + '');
135
- }
136
-
137
- // Recurse into children / Arrays
138
- if (isArray(vnode)) {
139
- let rendered = '';
140
- parent[CHILDREN] = vnode;
141
- for (let i = 0; i < vnode.length; i++) {
142
- let child = vnode[i];
143
- if (child == null || typeof child === 'boolean') continue;
144
-
145
- rendered =
146
- rendered +
147
- _renderToString(child, context, isSvgMode, selectValue, parent);
148
- }
149
- return rendered;
150
- }
151
-
152
- // VNodes have {constructor:undefined} to prevent JSON injection:
153
- if (vnode.constructor !== undefined) return '';
154
-
155
- vnode[PARENT] = parent;
156
- if (beforeDiff) beforeDiff(vnode);
157
-
158
- let type = vnode.type,
159
- props = vnode.props,
160
- cctx = context,
161
- contextType,
162
- rendered,
163
- component;
164
-
165
- // Invoke rendering on Components
166
- if (typeof type === 'function') {
167
- if (type === Fragment) {
168
- rendered = props.children;
169
- } else {
170
- contextType = type.contextType;
171
- if (contextType != null) {
172
- let provider = context[contextType.__c];
173
- cctx = provider ? provider.props.value : contextType.__;
174
- }
175
-
176
- if (type.prototype && typeof type.prototype.render === 'function') {
177
- rendered = /**#__NOINLINE__**/ renderClassComponent(vnode, cctx);
178
- component = vnode[COMPONENT];
179
- } else {
180
- component = {
181
- __v: vnode,
182
- props,
183
- context: cctx,
184
- // silently drop state updates
185
- setState: markAsDirty,
186
- forceUpdate: markAsDirty,
187
- __d: true,
188
- // hooks
189
- __h: []
190
- };
191
- vnode[COMPONENT] = component;
192
-
193
- // If a hook invokes setState() to invalidate the component during rendering,
194
- // re-render it up to 25 times to allow "settling" of memoized states.
195
- // Note:
196
- // This will need to be updated for Preact 11 to use internal.flags rather than component._dirty:
197
- // https://github.com/preactjs/preact/blob/d4ca6fdb19bc715e49fd144e69f7296b2f4daa40/src/diff/component.js#L35-L44
198
- let count = 0;
199
- while (component[DIRTY] && count++ < 25) {
200
- component[DIRTY] = false;
201
-
202
- if (renderHook) renderHook(vnode);
203
-
204
- rendered = type.call(component, props, cctx);
205
- }
206
- component[DIRTY] = true;
207
- }
208
-
209
- if (component.getChildContext != null) {
210
- context = assign({}, context, component.getChildContext());
211
- }
212
- }
213
-
214
- // When a component returns a Fragment node we flatten it in core, so we
215
- // need to mirror that logic here too
216
- let isTopLevelFragment =
217
- rendered != null && rendered.type === Fragment && rendered.key == null;
218
- rendered = isTopLevelFragment ? rendered.props.children : rendered;
219
-
220
- // Recurse into children before invoking the after-diff hook
221
- const str = _renderToString(
222
- rendered,
223
- context,
224
- isSvgMode,
225
- selectValue,
226
- vnode
227
- );
228
- if (afterDiff) afterDiff(vnode);
229
- vnode[PARENT] = undefined;
230
-
231
- if (ummountHook) ummountHook(vnode);
232
-
233
- return str;
234
- }
235
-
236
- // Serialize Element VNodes to HTML
237
- let s = '<' + type,
238
- html = '',
239
- children;
240
-
241
- for (let name in props) {
242
- let v = props[name];
243
-
244
- switch (name) {
245
- case 'children':
246
- children = v;
247
- continue;
248
-
249
- // VDOM-specific props
250
- case 'key':
251
- case 'ref':
252
- case '__self':
253
- case '__source':
254
- continue;
255
-
256
- // prefer for/class over htmlFor/className
257
- case 'htmlFor':
258
- if ('for' in props) continue;
259
- name = 'for';
260
- break;
261
- case 'className':
262
- if ('class' in props) continue;
263
- name = 'class';
264
- break;
265
-
266
- // Form element reflected properties
267
- case 'defaultChecked':
268
- name = 'checked';
269
- break;
270
- case 'defaultSelected':
271
- name = 'selected';
272
- break;
273
-
274
- // Special value attribute handling
275
- case 'defaultValue':
276
- case 'value':
277
- name = 'value';
278
- switch (type) {
279
- // <textarea value="a&b"> --> <textarea>a&amp;b</textarea>
280
- case 'textarea':
281
- children = v;
282
- continue;
283
-
284
- // <select value> is serialized as a selected attribute on the matching option child
285
- case 'select':
286
- selectValue = v;
287
- continue;
288
-
289
- // Add a selected attribute to <option> if its value matches the parent <select> value
290
- case 'option':
291
- if (selectValue == v && !('selected' in props)) {
292
- s = s + ' selected';
293
- }
294
- break;
295
- }
296
- break;
297
-
298
- case 'dangerouslySetInnerHTML':
299
- html = v && v.__html;
300
- continue;
301
-
302
- // serialize object styles to a CSS string
303
- case 'style':
304
- if (typeof v === 'object') {
305
- v = styleObjToCss(v);
306
- }
307
- break;
308
-
309
- default: {
310
- if (isSvgMode && XLINK.test(name)) {
311
- name = name.toLowerCase().replace(XLINK_REPLACE_REGEX, 'xlink:');
312
- } else if (UNSAFE_NAME.test(name)) {
313
- continue;
314
- } else if ((name[4] === '-' || name === 'draggable') && v != null) {
315
- // serialize boolean aria-xyz or draggable attribute values as strings
316
- // `draggable` is an enumerated attribute and not Boolean. A value of `true` or `false` is mandatory
317
- // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable
318
- v += '';
319
- }
320
- }
321
- }
322
-
323
- // write this attribute to the buffer
324
- if (v != null && v !== false && typeof v !== 'function') {
325
- if (v === true || v === '') {
326
- s = s + ' ' + name;
327
- } else {
328
- s = s + ' ' + name + '="' + encodeEntities(v + '') + '"';
329
- }
330
- }
331
- }
332
-
333
- if (UNSAFE_NAME.test(type)) {
334
- // this seems to performs a lot better than throwing
335
- // return '<!-- -->';
336
- throw new Error(`${type} is not a valid HTML tag name in ${s}>`);
337
- }
338
-
339
- if (html) {
340
- // dangerouslySetInnerHTML defined this node's contents
341
- } else if (typeof children === 'string') {
342
- // single text child
343
- html = encodeEntities(children);
344
- } else if (children != null && children !== false && children !== true) {
345
- // recurse into this element VNode's children
346
- let childSvgMode =
347
- type === 'svg' || (type !== 'foreignObject' && isSvgMode);
348
- html = _renderToString(children, context, childSvgMode, selectValue, vnode);
349
- }
350
-
351
- if (afterDiff) afterDiff(vnode);
352
- vnode[PARENT] = undefined;
353
- if (ummountHook) ummountHook(vnode);
354
-
355
- // Emit self-closing tag for empty void elements:
356
- if (!html && SELF_CLOSING.has(type)) {
357
- return s + '/>';
358
- }
359
-
360
- return s + '>' + html + '</' + type + '>';
361
- }
362
-
363
- const XLINK_REPLACE_REGEX = /^xlink:?/;
364
- const SELF_CLOSING = new Set([
365
- 'area',
366
- 'base',
367
- 'br',
368
- 'col',
369
- 'command',
370
- 'embed',
371
- 'hr',
372
- 'img',
373
- 'input',
374
- 'keygen',
375
- 'link',
376
- 'meta',
377
- 'param',
378
- 'source',
379
- 'track',
380
- 'wbr'
381
- ]);
1
+ import { encodeEntities, styleObjToCss, UNSAFE_NAME, XLINK } from './util';
2
+ import { options, h, Fragment } from 'preact';
3
+ import {
4
+ CHILDREN,
5
+ COMMIT,
6
+ COMPONENT,
7
+ DIFF,
8
+ DIFFED,
9
+ DIRTY,
10
+ NEXT_STATE,
11
+ PARENT,
12
+ RENDER,
13
+ SKIP_EFFECTS,
14
+ VNODE
15
+ } from './constants';
16
+
17
+ /** @typedef {import('preact').VNode} VNode */
18
+
19
+ const EMPTY_ARR = [];
20
+ const isArray = Array.isArray;
21
+ const assign = Object.assign;
22
+
23
+ // Global state for the current render pass
24
+ let beforeDiff, afterDiff, renderHook, ummountHook;
25
+
26
+ /**
27
+ * Render Preact JSX + Components to an HTML string.
28
+ * @param {VNode} vnode JSX Element / VNode to render
29
+ * @param {Object} [context={}] Initial root context object
30
+ * @returns {string} serialized HTML
31
+ */
32
+ export function renderToString(vnode, context) {
33
+ // Performance optimization: `renderToString` is synchronous and we
34
+ // therefore don't execute any effects. To do that we pass an empty
35
+ // array to `options._commit` (`__c`). But we can go one step further
36
+ // and avoid a lot of dirty checks and allocations by setting
37
+ // `options._skipEffects` (`__s`) too.
38
+ const previousSkipEffects = options[SKIP_EFFECTS];
39
+ options[SKIP_EFFECTS] = true;
40
+
41
+ // store options hooks once before each synchronous render call
42
+ beforeDiff = options[DIFF];
43
+ afterDiff = options[DIFFED];
44
+ renderHook = options[RENDER];
45
+ ummountHook = options.unmount;
46
+
47
+ const parent = h(Fragment, null);
48
+ parent[CHILDREN] = [vnode];
49
+
50
+ try {
51
+ return _renderToString(
52
+ vnode,
53
+ context || EMPTY_OBJ,
54
+ false,
55
+ undefined,
56
+ parent
57
+ );
58
+ } finally {
59
+ // options._commit, we don't schedule any effects in this library right now,
60
+ // so we can pass an empty queue to this hook.
61
+ if (options[COMMIT]) options[COMMIT](vnode, EMPTY_ARR);
62
+ options[SKIP_EFFECTS] = previousSkipEffects;
63
+ EMPTY_ARR.length = 0;
64
+ }
65
+ }
66
+
67
+ // Installed as setState/forceUpdate for function components
68
+ function markAsDirty() {
69
+ this.__d = true;
70
+ }
71
+
72
+ const EMPTY_OBJ = {};
73
+
74
+ /**
75
+ * @param {VNode} vnode
76
+ * @param {Record<string, unknown>} context
77
+ */
78
+ function renderClassComponent(vnode, context) {
79
+ let type = /** @type {import("preact").ComponentClass<typeof vnode.props>} */ (vnode.type);
80
+
81
+ let c = new type(vnode.props, context);
82
+
83
+ vnode[COMPONENT] = c;
84
+ c[VNODE] = vnode;
85
+
86
+ c.props = vnode.props;
87
+ c.context = context;
88
+ // turn off stateful re-rendering:
89
+ c[DIRTY] = true;
90
+
91
+ if (c.state == null) c.state = EMPTY_OBJ;
92
+
93
+ if (c[NEXT_STATE] == null) {
94
+ c[NEXT_STATE] = c.state;
95
+ }
96
+
97
+ if (type.getDerivedStateFromProps) {
98
+ c.state = assign(
99
+ {},
100
+ c.state,
101
+ type.getDerivedStateFromProps(c.props, c.state)
102
+ );
103
+ } else if (c.componentWillMount) {
104
+ c.componentWillMount();
105
+
106
+ // If the user called setState in cWM we need to flush pending,
107
+ // state updates. This is the same behaviour in React.
108
+ c.state = c[NEXT_STATE] !== c.state ? c[NEXT_STATE] : c.state;
109
+ }
110
+
111
+ if (renderHook) renderHook(vnode);
112
+
113
+ return c.render(c.props, c.state, context);
114
+ }
115
+
116
+ /**
117
+ * Recursively render VNodes to HTML.
118
+ * @param {VNode|any} vnode
119
+ * @param {any} context
120
+ * @param {boolean} isSvgMode
121
+ * @param {any} selectValue
122
+ * @param {VNode} parent
123
+ * @returns {string}
124
+ */
125
+ function _renderToString(vnode, context, isSvgMode, selectValue, parent) {
126
+ // Ignore non-rendered VNodes/values
127
+ if (vnode == null || vnode === true || vnode === false || vnode === '') {
128
+ return '';
129
+ }
130
+
131
+ // Text VNodes: escape as HTML
132
+ if (typeof vnode !== 'object') {
133
+ if (typeof vnode === 'function') return '';
134
+ return encodeEntities(vnode + '');
135
+ }
136
+
137
+ // Recurse into children / Arrays
138
+ if (isArray(vnode)) {
139
+ let rendered = '';
140
+ parent[CHILDREN] = vnode;
141
+ for (let i = 0; i < vnode.length; i++) {
142
+ let child = vnode[i];
143
+ if (child == null || typeof child === 'boolean') continue;
144
+
145
+ rendered =
146
+ rendered +
147
+ _renderToString(child, context, isSvgMode, selectValue, parent);
148
+ }
149
+ return rendered;
150
+ }
151
+
152
+ // VNodes have {constructor:undefined} to prevent JSON injection:
153
+ if (vnode.constructor !== undefined) return '';
154
+
155
+ vnode[PARENT] = parent;
156
+ if (beforeDiff) beforeDiff(vnode);
157
+
158
+ let type = vnode.type,
159
+ props = vnode.props,
160
+ cctx = context,
161
+ contextType,
162
+ rendered,
163
+ component;
164
+
165
+ // Invoke rendering on Components
166
+ if (typeof type === 'function') {
167
+ if (type === Fragment) {
168
+ rendered = props.children;
169
+ } else {
170
+ contextType = type.contextType;
171
+ if (contextType != null) {
172
+ let provider = context[contextType.__c];
173
+ cctx = provider ? provider.props.value : contextType.__;
174
+ }
175
+
176
+ if (type.prototype && typeof type.prototype.render === 'function') {
177
+ rendered = /**#__NOINLINE__**/ renderClassComponent(vnode, cctx);
178
+ component = vnode[COMPONENT];
179
+ } else {
180
+ component = {
181
+ __v: vnode,
182
+ props,
183
+ context: cctx,
184
+ // silently drop state updates
185
+ setState: markAsDirty,
186
+ forceUpdate: markAsDirty,
187
+ __d: true,
188
+ // hooks
189
+ __h: []
190
+ };
191
+ vnode[COMPONENT] = component;
192
+
193
+ // If a hook invokes setState() to invalidate the component during rendering,
194
+ // re-render it up to 25 times to allow "settling" of memoized states.
195
+ // Note:
196
+ // This will need to be updated for Preact 11 to use internal.flags rather than component._dirty:
197
+ // https://github.com/preactjs/preact/blob/d4ca6fdb19bc715e49fd144e69f7296b2f4daa40/src/diff/component.js#L35-L44
198
+ let count = 0;
199
+ while (component[DIRTY] && count++ < 25) {
200
+ component[DIRTY] = false;
201
+
202
+ if (renderHook) renderHook(vnode);
203
+
204
+ rendered = type.call(component, props, cctx);
205
+ }
206
+ component[DIRTY] = true;
207
+ }
208
+
209
+ if (component.getChildContext != null) {
210
+ context = assign({}, context, component.getChildContext());
211
+ }
212
+ }
213
+
214
+ // When a component returns a Fragment node we flatten it in core, so we
215
+ // need to mirror that logic here too
216
+ let isTopLevelFragment =
217
+ rendered != null && rendered.type === Fragment && rendered.key == null;
218
+ rendered = isTopLevelFragment ? rendered.props.children : rendered;
219
+
220
+ // Recurse into children before invoking the after-diff hook
221
+ const str = _renderToString(
222
+ rendered,
223
+ context,
224
+ isSvgMode,
225
+ selectValue,
226
+ vnode
227
+ );
228
+ if (afterDiff) afterDiff(vnode);
229
+ vnode[PARENT] = undefined;
230
+
231
+ if (ummountHook) ummountHook(vnode);
232
+
233
+ return str;
234
+ }
235
+
236
+ // Serialize Element VNodes to HTML
237
+ let s = '<' + type,
238
+ html = '',
239
+ children;
240
+
241
+ for (let name in props) {
242
+ let v = props[name];
243
+
244
+ switch (name) {
245
+ case 'children':
246
+ children = v;
247
+ continue;
248
+
249
+ // VDOM-specific props
250
+ case 'key':
251
+ case 'ref':
252
+ case '__self':
253
+ case '__source':
254
+ continue;
255
+
256
+ // prefer for/class over htmlFor/className
257
+ case 'htmlFor':
258
+ if ('for' in props) continue;
259
+ name = 'for';
260
+ break;
261
+ case 'className':
262
+ if ('class' in props) continue;
263
+ name = 'class';
264
+ break;
265
+
266
+ // Form element reflected properties
267
+ case 'defaultChecked':
268
+ name = 'checked';
269
+ break;
270
+ case 'defaultSelected':
271
+ name = 'selected';
272
+ break;
273
+
274
+ // Special value attribute handling
275
+ case 'defaultValue':
276
+ case 'value':
277
+ name = 'value';
278
+ switch (type) {
279
+ // <textarea value="a&b"> --> <textarea>a&amp;b</textarea>
280
+ case 'textarea':
281
+ children = v;
282
+ continue;
283
+
284
+ // <select value> is serialized as a selected attribute on the matching option child
285
+ case 'select':
286
+ selectValue = v;
287
+ continue;
288
+
289
+ // Add a selected attribute to <option> if its value matches the parent <select> value
290
+ case 'option':
291
+ if (selectValue == v && !('selected' in props)) {
292
+ s = s + ' selected';
293
+ }
294
+ break;
295
+ }
296
+ break;
297
+
298
+ case 'dangerouslySetInnerHTML':
299
+ html = v && v.__html;
300
+ continue;
301
+
302
+ // serialize object styles to a CSS string
303
+ case 'style':
304
+ if (typeof v === 'object') {
305
+ v = styleObjToCss(v);
306
+ }
307
+ break;
308
+
309
+ default: {
310
+ if (isSvgMode && XLINK.test(name)) {
311
+ name = name.toLowerCase().replace(XLINK_REPLACE_REGEX, 'xlink:');
312
+ } else if (UNSAFE_NAME.test(name)) {
313
+ continue;
314
+ } else if ((name[4] === '-' || name === 'draggable') && v != null) {
315
+ // serialize boolean aria-xyz or draggable attribute values as strings
316
+ // `draggable` is an enumerated attribute and not Boolean. A value of `true` or `false` is mandatory
317
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable
318
+ v += '';
319
+ }
320
+ }
321
+ }
322
+
323
+ // write this attribute to the buffer
324
+ if (v != null && v !== false && typeof v !== 'function') {
325
+ if (v === true || v === '') {
326
+ s = s + ' ' + name;
327
+ } else {
328
+ s = s + ' ' + name + '="' + encodeEntities(v + '') + '"';
329
+ }
330
+ }
331
+ }
332
+
333
+ if (UNSAFE_NAME.test(type)) {
334
+ // this seems to performs a lot better than throwing
335
+ // return '<!-- -->';
336
+ throw new Error(`${type} is not a valid HTML tag name in ${s}>`);
337
+ }
338
+
339
+ if (html) {
340
+ // dangerouslySetInnerHTML defined this node's contents
341
+ } else if (typeof children === 'string') {
342
+ // single text child
343
+ html = encodeEntities(children);
344
+ } else if (children != null && children !== false && children !== true) {
345
+ // recurse into this element VNode's children
346
+ let childSvgMode =
347
+ type === 'svg' || (type !== 'foreignObject' && isSvgMode);
348
+ html = _renderToString(children, context, childSvgMode, selectValue, vnode);
349
+ }
350
+
351
+ if (afterDiff) afterDiff(vnode);
352
+ vnode[PARENT] = undefined;
353
+ if (ummountHook) ummountHook(vnode);
354
+
355
+ // Emit self-closing tag for empty void elements:
356
+ if (!html && SELF_CLOSING.has(type)) {
357
+ return s + '/>';
358
+ }
359
+
360
+ return s + '>' + html + '</' + type + '>';
361
+ }
362
+
363
+ const XLINK_REPLACE_REGEX = /^xlink:?/;
364
+ const SELF_CLOSING = new Set([
365
+ 'area',
366
+ 'base',
367
+ 'br',
368
+ 'col',
369
+ 'command',
370
+ 'embed',
371
+ 'hr',
372
+ 'img',
373
+ 'input',
374
+ 'keygen',
375
+ 'link',
376
+ 'meta',
377
+ 'param',
378
+ 'source',
379
+ 'track',
380
+ 'wbr'
381
+ ]);
382
+
383
+ export default renderToString;
384
+ export const render = renderToString;
385
+ export const renderToStaticMarkup = renderToString;