preact-render-to-string 5.2.2 → 5.2.3
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/dist/commonjs.js +1 -1
- package/dist/commonjs.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.module.js +1 -1
- package/dist/index.module.js.map +1 -1
- package/dist/jsx-entry.js +1 -1
- package/dist/jsx-entry.js.map +1 -1
- package/dist/jsx.js.map +1 -1
- package/dist/jsx.mjs +1 -1
- package/dist/jsx.modern.js +1 -1
- package/dist/jsx.modern.js.map +1 -1
- package/dist/jsx.module.js +1 -1
- package/dist/jsx.module.js.map +1 -1
- package/package.json +6 -1
- package/src/constants.js +14 -0
- package/src/index.js +202 -595
- package/src/jsx.js +2 -2
- package/src/pretty.js +385 -0
- package/src/util.js +55 -25
package/src/index.js
CHANGED
|
@@ -1,28 +1,30 @@
|
|
|
1
1
|
import {
|
|
2
2
|
encodeEntities,
|
|
3
|
-
indent,
|
|
4
|
-
isLargeString,
|
|
5
3
|
styleObjToCss,
|
|
6
|
-
|
|
4
|
+
getContext,
|
|
5
|
+
createComponent,
|
|
6
|
+
UNSAFE_NAME,
|
|
7
|
+
XLINK,
|
|
8
|
+
VOID_ELEMENTS
|
|
7
9
|
} from './util';
|
|
8
10
|
import { options, Fragment } from 'preact';
|
|
11
|
+
import { _renderToStringPretty } from './pretty';
|
|
12
|
+
import {
|
|
13
|
+
COMMIT,
|
|
14
|
+
COMPONENT,
|
|
15
|
+
DIFF,
|
|
16
|
+
DIFFED,
|
|
17
|
+
DIRTY,
|
|
18
|
+
NEXT_STATE,
|
|
19
|
+
RENDER,
|
|
20
|
+
SKIP_EFFECTS,
|
|
21
|
+
VNODE
|
|
22
|
+
} from './constants';
|
|
9
23
|
|
|
10
24
|
/** @typedef {import('preact').VNode} VNode */
|
|
11
25
|
|
|
12
26
|
const SHALLOW = { shallow: true };
|
|
13
27
|
|
|
14
|
-
// components without names, kept as a hash for later comparison to return consistent UnnamedComponentXX names.
|
|
15
|
-
const UNNAMED = [];
|
|
16
|
-
|
|
17
|
-
const VOID_ELEMENTS = /^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/;
|
|
18
|
-
|
|
19
|
-
const UNSAFE_NAME = /[\s\n\\/='"\0<>]/;
|
|
20
|
-
const XLINK = /^xlink:?./;
|
|
21
|
-
|
|
22
|
-
function markAsDirty() {
|
|
23
|
-
this.__d = true;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
28
|
/** Render Preact JSX + Components to an HTML string.
|
|
27
29
|
* @name render
|
|
28
30
|
* @function
|
|
@@ -48,705 +50,310 @@ let shallowRender = (vnode, context) => renderToString(vnode, context, SHALLOW);
|
|
|
48
50
|
const EMPTY_ARR = [];
|
|
49
51
|
function renderToString(vnode, context, opts) {
|
|
50
52
|
context = context || {};
|
|
51
|
-
opts = opts || {};
|
|
52
53
|
|
|
53
54
|
// Performance optimization: `renderToString` is synchronous and we
|
|
54
55
|
// therefore don't execute any effects. To do that we pass an empty
|
|
55
56
|
// array to `options._commit` (`__c`). But we can go one step further
|
|
56
57
|
// and avoid a lot of dirty checks and allocations by setting
|
|
57
58
|
// `options._skipEffects` (`__s`) too.
|
|
58
|
-
const previousSkipEffects = options
|
|
59
|
-
options
|
|
59
|
+
const previousSkipEffects = options[SKIP_EFFECTS];
|
|
60
|
+
options[SKIP_EFFECTS] = true;
|
|
60
61
|
|
|
61
62
|
let res;
|
|
62
|
-
if (
|
|
63
|
+
if (
|
|
64
|
+
opts &&
|
|
65
|
+
(opts.pretty ||
|
|
66
|
+
opts.voidElements ||
|
|
67
|
+
opts.sortAttributes ||
|
|
68
|
+
opts.shallow ||
|
|
69
|
+
opts.allAttributes ||
|
|
70
|
+
opts.xml ||
|
|
71
|
+
opts.attributeHook)
|
|
72
|
+
) {
|
|
63
73
|
res = _renderToStringPretty(vnode, context, opts);
|
|
64
74
|
} else {
|
|
65
|
-
res = _renderToString(vnode, context,
|
|
75
|
+
res = _renderToString(vnode, context, false, undefined);
|
|
66
76
|
}
|
|
67
77
|
|
|
68
78
|
// options._commit, we don't schedule any effects in this library right now,
|
|
69
79
|
// so we can pass an empty queue to this hook.
|
|
70
|
-
if (options
|
|
80
|
+
if (options[COMMIT]) options[COMMIT](vnode, EMPTY_ARR);
|
|
81
|
+
options[SKIP_EFFECTS] = previousSkipEffects;
|
|
71
82
|
EMPTY_ARR.length = 0;
|
|
72
|
-
options.__s = previousSkipEffects;
|
|
73
83
|
return res;
|
|
74
84
|
}
|
|
75
85
|
|
|
76
|
-
function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
context
|
|
80
|
-
props: vnode.props,
|
|
81
|
-
// silently drop state updates
|
|
82
|
-
setState: markAsDirty,
|
|
83
|
-
forceUpdate: markAsDirty,
|
|
84
|
-
__d: true,
|
|
85
|
-
// hooks
|
|
86
|
-
__h: []
|
|
87
|
-
};
|
|
88
|
-
}
|
|
86
|
+
function renderFunctionComponent(vnode, context) {
|
|
87
|
+
let rendered,
|
|
88
|
+
c = createComponent(vnode, context),
|
|
89
|
+
cctx = getContext(vnode.type, context);
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
// the context value as `this.context` just for this component.
|
|
92
|
-
function getContext(nodeName, context) {
|
|
93
|
-
let cxType = nodeName.contextType;
|
|
94
|
-
let provider = cxType && context[cxType.__c];
|
|
95
|
-
return cxType != null
|
|
96
|
-
? provider
|
|
97
|
-
? provider.props.value
|
|
98
|
-
: cxType.__
|
|
99
|
-
: context;
|
|
100
|
-
}
|
|
91
|
+
vnode[COMPONENT] = c;
|
|
101
92
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
93
|
+
// If a hook invokes setState() to invalidate the component during rendering,
|
|
94
|
+
// re-render it up to 25 times to allow "settling" of memoized states.
|
|
95
|
+
// Note:
|
|
96
|
+
// This will need to be updated for Preact 11 to use internal.flags rather than component._dirty:
|
|
97
|
+
// https://github.com/preactjs/preact/blob/d4ca6fdb19bc715e49fd144e69f7296b2f4daa40/src/diff/component.js#L35-L44
|
|
98
|
+
let renderHook = options[RENDER];
|
|
99
|
+
let count = 0;
|
|
100
|
+
while (c[DIRTY] && count++ < 25) {
|
|
101
|
+
c[DIRTY] = false;
|
|
107
102
|
|
|
108
|
-
|
|
109
|
-
if (typeof vnode !== 'object') {
|
|
110
|
-
return encodeEntities(vnode);
|
|
111
|
-
}
|
|
103
|
+
if (renderHook) renderHook(vnode);
|
|
112
104
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
for (let i = 0; i < vnode.length; i++) {
|
|
116
|
-
rendered.push(
|
|
117
|
-
_renderToString(vnode[i], context, opts, inner, isSvgMode, selectValue)
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
return rendered.join('');
|
|
105
|
+
// stateless functional components
|
|
106
|
+
rendered = vnode.type.call(c, vnode.props, cctx);
|
|
121
107
|
}
|
|
122
108
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
isComponent = false;
|
|
126
|
-
|
|
127
|
-
// components
|
|
128
|
-
if (typeof nodeName === 'function') {
|
|
129
|
-
isComponent = true;
|
|
130
|
-
if (opts.shallow && (inner || opts.renderRootComponent === false)) {
|
|
131
|
-
nodeName = getComponentName(nodeName);
|
|
132
|
-
} else if (nodeName === Fragment) {
|
|
133
|
-
const children = [];
|
|
134
|
-
getChildren(children, vnode.props.children);
|
|
135
|
-
return _renderToString(
|
|
136
|
-
children,
|
|
137
|
-
context,
|
|
138
|
-
opts,
|
|
139
|
-
opts.shallowHighOrder !== false,
|
|
140
|
-
isSvgMode,
|
|
141
|
-
selectValue
|
|
142
|
-
);
|
|
143
|
-
} else {
|
|
144
|
-
let rendered;
|
|
145
|
-
|
|
146
|
-
let c = (vnode.__c = createComponent(vnode, context));
|
|
147
|
-
|
|
148
|
-
// options._diff
|
|
149
|
-
if (options.__b) options.__b(vnode);
|
|
150
|
-
|
|
151
|
-
// options._render
|
|
152
|
-
let renderHook = options.__r;
|
|
153
|
-
|
|
154
|
-
if (
|
|
155
|
-
!nodeName.prototype ||
|
|
156
|
-
typeof nodeName.prototype.render !== 'function'
|
|
157
|
-
) {
|
|
158
|
-
let cctx = getContext(nodeName, context);
|
|
159
|
-
|
|
160
|
-
// If a hook invokes setState() to invalidate the component during rendering,
|
|
161
|
-
// re-render it up to 25 times to allow "settling" of memoized states.
|
|
162
|
-
// Note:
|
|
163
|
-
// This will need to be updated for Preact 11 to use internal.flags rather than component._dirty:
|
|
164
|
-
// https://github.com/preactjs/preact/blob/d4ca6fdb19bc715e49fd144e69f7296b2f4daa40/src/diff/component.js#L35-L44
|
|
165
|
-
let count = 0;
|
|
166
|
-
while (c.__d && count++ < 25) {
|
|
167
|
-
c.__d = false;
|
|
168
|
-
|
|
169
|
-
if (renderHook) renderHook(vnode);
|
|
170
|
-
|
|
171
|
-
// stateless functional components
|
|
172
|
-
rendered = nodeName.call(vnode.__c, props, cctx);
|
|
173
|
-
}
|
|
174
|
-
} else {
|
|
175
|
-
let cctx = getContext(nodeName, context);
|
|
176
|
-
|
|
177
|
-
// c = new nodeName(props, context);
|
|
178
|
-
c = vnode.__c = new nodeName(props, cctx);
|
|
179
|
-
c.__v = vnode;
|
|
180
|
-
// turn off stateful re-rendering:
|
|
181
|
-
c._dirty = c.__d = true;
|
|
182
|
-
c.props = props;
|
|
183
|
-
if (c.state == null) c.state = {};
|
|
184
|
-
|
|
185
|
-
if (c._nextState == null && c.__s == null) {
|
|
186
|
-
c._nextState = c.__s = c.state;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
c.context = cctx;
|
|
190
|
-
if (nodeName.getDerivedStateFromProps)
|
|
191
|
-
c.state = Object.assign(
|
|
192
|
-
{},
|
|
193
|
-
c.state,
|
|
194
|
-
nodeName.getDerivedStateFromProps(c.props, c.state)
|
|
195
|
-
);
|
|
196
|
-
else if (c.componentWillMount) {
|
|
197
|
-
c.componentWillMount();
|
|
198
|
-
|
|
199
|
-
// If the user called setState in cWM we need to flush pending,
|
|
200
|
-
// state updates. This is the same behaviour in React.
|
|
201
|
-
c.state =
|
|
202
|
-
c._nextState !== c.state
|
|
203
|
-
? c._nextState
|
|
204
|
-
: c.__s !== c.state
|
|
205
|
-
? c.__s
|
|
206
|
-
: c.state;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
if (renderHook) renderHook(vnode);
|
|
210
|
-
|
|
211
|
-
rendered = c.render(c.props, c.state, c.context);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (c.getChildContext) {
|
|
215
|
-
context = Object.assign({}, context, c.getChildContext());
|
|
216
|
-
}
|
|
109
|
+
return rendered;
|
|
110
|
+
}
|
|
217
111
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
112
|
+
function renderClassComponent(vnode, context) {
|
|
113
|
+
let nodeName = vnode.type,
|
|
114
|
+
cctx = getContext(nodeName, context);
|
|
115
|
+
|
|
116
|
+
// c = new nodeName(props, context);
|
|
117
|
+
let c = new nodeName(vnode.props, cctx);
|
|
118
|
+
vnode[COMPONENT] = c;
|
|
119
|
+
c[VNODE] = vnode;
|
|
120
|
+
// turn off stateful re-rendering:
|
|
121
|
+
c[DIRTY] = true;
|
|
122
|
+
c.props = vnode.props;
|
|
123
|
+
if (c.state == null) c.state = {};
|
|
124
|
+
|
|
125
|
+
if (c[NEXT_STATE] == null) {
|
|
126
|
+
c[NEXT_STATE] = c.state;
|
|
228
127
|
}
|
|
229
128
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if (UNSAFE_NAME.test(name)) continue;
|
|
244
|
-
|
|
245
|
-
if (
|
|
246
|
-
!(opts && opts.allAttributes) &&
|
|
247
|
-
(name === 'key' ||
|
|
248
|
-
name === 'ref' ||
|
|
249
|
-
name === '__self' ||
|
|
250
|
-
name === '__source')
|
|
251
|
-
)
|
|
252
|
-
continue;
|
|
253
|
-
|
|
254
|
-
if (name === 'defaultValue') {
|
|
255
|
-
name = 'value';
|
|
256
|
-
} else if (name === 'defaultChecked') {
|
|
257
|
-
name = 'checked';
|
|
258
|
-
} else if (name === 'defaultSelected') {
|
|
259
|
-
name = 'selected';
|
|
260
|
-
} else if (name === 'className') {
|
|
261
|
-
if (typeof props.class !== 'undefined') continue;
|
|
262
|
-
name = 'class';
|
|
263
|
-
} else if (isSvgMode && XLINK.test(name)) {
|
|
264
|
-
name = name.toLowerCase().replace(/^xlink:?/, 'xlink:');
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
if (name === 'htmlFor') {
|
|
268
|
-
if (props.for) continue;
|
|
269
|
-
name = 'for';
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
if (name === 'style' && v && typeof v === 'object') {
|
|
273
|
-
v = styleObjToCss(v);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// always use string values instead of booleans for aria attributes
|
|
277
|
-
// also see https://github.com/preactjs/preact/pull/2347/files
|
|
278
|
-
if (name[0] === 'a' && name['1'] === 'r' && typeof v === 'boolean') {
|
|
279
|
-
v = String(v);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
let hooked =
|
|
283
|
-
opts.attributeHook &&
|
|
284
|
-
opts.attributeHook(name, v, context, opts, isComponent);
|
|
285
|
-
if (hooked || hooked === '') {
|
|
286
|
-
s = s + hooked;
|
|
287
|
-
continue;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (name === 'dangerouslySetInnerHTML') {
|
|
291
|
-
html = v && v.__html;
|
|
292
|
-
} else if (nodeName === 'textarea' && name === 'value') {
|
|
293
|
-
// <textarea value="a&b"> --> <textarea>a&b</textarea>
|
|
294
|
-
propChildren = v;
|
|
295
|
-
} else if ((v || v === 0 || v === '') && typeof v !== 'function') {
|
|
296
|
-
if (v === true || v === '') {
|
|
297
|
-
v = name;
|
|
298
|
-
// in non-xml mode, allow boolean attributes
|
|
299
|
-
if (!opts || !opts.xml) {
|
|
300
|
-
s = s + ' ' + name;
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
if (name === 'value') {
|
|
306
|
-
if (nodeName === 'select') {
|
|
307
|
-
selectValue = v;
|
|
308
|
-
continue;
|
|
309
|
-
} else if (
|
|
310
|
-
// If we're looking at an <option> and it's the currently selected one
|
|
311
|
-
nodeName === 'option' &&
|
|
312
|
-
selectValue == v &&
|
|
313
|
-
// and the <option> doesn't already have a selected attribute on it
|
|
314
|
-
typeof props.selected === 'undefined'
|
|
315
|
-
) {
|
|
316
|
-
s = `${s} selected`;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
s = s + ` ${name}="${encodeEntities(v)}"`;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
129
|
+
c.context = cctx;
|
|
130
|
+
if (nodeName.getDerivedStateFromProps) {
|
|
131
|
+
c.state = assign(
|
|
132
|
+
{},
|
|
133
|
+
c.state,
|
|
134
|
+
nodeName.getDerivedStateFromProps(c.props, c.state)
|
|
135
|
+
);
|
|
136
|
+
} else if (c.componentWillMount) {
|
|
137
|
+
c.componentWillMount();
|
|
138
|
+
|
|
139
|
+
// If the user called setState in cWM we need to flush pending,
|
|
140
|
+
// state updates. This is the same behaviour in React.
|
|
141
|
+
c.state = c[NEXT_STATE] !== c.state ? c[NEXT_STATE] : c.state;
|
|
322
142
|
}
|
|
323
143
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (UNSAFE_NAME.test(nodeName))
|
|
327
|
-
throw new Error(`${nodeName} is not a valid HTML tag name in ${s}`);
|
|
328
|
-
|
|
329
|
-
let isVoid =
|
|
330
|
-
VOID_ELEMENTS.test(nodeName) ||
|
|
331
|
-
(opts.voidElements && opts.voidElements.test(nodeName));
|
|
332
|
-
let pieces = '';
|
|
333
|
-
|
|
334
|
-
let children;
|
|
335
|
-
if (html) {
|
|
336
|
-
s = s + html;
|
|
337
|
-
} else if (
|
|
338
|
-
propChildren != null &&
|
|
339
|
-
getChildren((children = []), propChildren).length
|
|
340
|
-
) {
|
|
341
|
-
for (let i = 0; i < children.length; i++) {
|
|
342
|
-
let child = children[i];
|
|
144
|
+
let renderHook = options[RENDER];
|
|
145
|
+
if (renderHook) renderHook(vnode);
|
|
343
146
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
nodeName === 'svg'
|
|
347
|
-
? true
|
|
348
|
-
: nodeName === 'foreignObject'
|
|
349
|
-
? false
|
|
350
|
-
: isSvgMode,
|
|
351
|
-
ret = _renderToString(
|
|
352
|
-
child,
|
|
353
|
-
context,
|
|
354
|
-
opts,
|
|
355
|
-
true,
|
|
356
|
-
childSvgMode,
|
|
357
|
-
selectValue
|
|
358
|
-
);
|
|
147
|
+
return c.render(c.props, c.state, c.context);
|
|
148
|
+
}
|
|
359
149
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
150
|
+
function normalizePropName(name, isSvgMode) {
|
|
151
|
+
if (name === 'className') {
|
|
152
|
+
return 'class';
|
|
153
|
+
} else if (name === 'htmlFor') {
|
|
154
|
+
return 'for';
|
|
155
|
+
} else if (name === 'defaultValue') {
|
|
156
|
+
return 'value';
|
|
157
|
+
} else if (name === 'defaultChecked') {
|
|
158
|
+
return 'checked';
|
|
159
|
+
} else if (name === 'defaultSelected') {
|
|
160
|
+
return 'selected';
|
|
161
|
+
} else if (isSvgMode && XLINK.test(name)) {
|
|
162
|
+
return name.toLowerCase().replace(/^xlink:?/, 'xlink:');
|
|
366
163
|
}
|
|
367
164
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
} else if (opts && opts.xml) {
|
|
371
|
-
return s.substring(0, s.length - 1) + ' />';
|
|
372
|
-
}
|
|
165
|
+
return name;
|
|
166
|
+
}
|
|
373
167
|
|
|
374
|
-
|
|
375
|
-
|
|
168
|
+
function normalizePropValue(name, v) {
|
|
169
|
+
if (name === 'style' && v != null && typeof v === 'object') {
|
|
170
|
+
return styleObjToCss(v);
|
|
171
|
+
} else if (name[0] === 'a' && name[1] === 'r' && typeof v === 'boolean') {
|
|
172
|
+
// always use string values instead of booleans for aria attributes
|
|
173
|
+
// also see https://github.com/preactjs/preact/pull/2347/files
|
|
174
|
+
return String(v);
|
|
376
175
|
}
|
|
377
176
|
|
|
378
|
-
return
|
|
177
|
+
return v;
|
|
379
178
|
}
|
|
380
179
|
|
|
180
|
+
const isArray = Array.isArray;
|
|
181
|
+
const assign = Object.assign;
|
|
182
|
+
|
|
381
183
|
/** The default export is an alias of `render()`. */
|
|
382
|
-
function
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
opts,
|
|
386
|
-
inner,
|
|
387
|
-
isSvgMode,
|
|
388
|
-
selectValue
|
|
389
|
-
) {
|
|
390
|
-
if (vnode == null || typeof vnode === 'boolean') {
|
|
184
|
+
function _renderToString(vnode, context, isSvgMode, selectValue) {
|
|
185
|
+
// Ignore non-rendered VNodes/values
|
|
186
|
+
if (vnode == null || vnode === true || vnode === false || vnode === '') {
|
|
391
187
|
return '';
|
|
392
188
|
}
|
|
393
189
|
|
|
394
|
-
//
|
|
190
|
+
// Text VNodes: escape as HTML
|
|
395
191
|
if (typeof vnode !== 'object') {
|
|
396
192
|
return encodeEntities(vnode);
|
|
397
193
|
}
|
|
398
194
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
if (Array.isArray(vnode)) {
|
|
195
|
+
// Recurse into children / Arrays
|
|
196
|
+
if (isArray(vnode)) {
|
|
403
197
|
let rendered = '';
|
|
404
198
|
for (let i = 0; i < vnode.length; i++) {
|
|
405
|
-
if (pretty && i > 0) rendered = rendered + '\n';
|
|
406
199
|
rendered =
|
|
407
|
-
rendered +
|
|
408
|
-
_renderToStringPretty(
|
|
409
|
-
vnode[i],
|
|
410
|
-
context,
|
|
411
|
-
opts,
|
|
412
|
-
inner,
|
|
413
|
-
isSvgMode,
|
|
414
|
-
selectValue
|
|
415
|
-
);
|
|
200
|
+
rendered + _renderToString(vnode[i], context, isSvgMode, selectValue);
|
|
416
201
|
}
|
|
417
202
|
return rendered;
|
|
418
203
|
}
|
|
419
204
|
|
|
420
|
-
|
|
421
|
-
props = vnode.props,
|
|
422
|
-
isComponent = false;
|
|
423
|
-
|
|
424
|
-
// components
|
|
425
|
-
if (typeof nodeName === 'function') {
|
|
426
|
-
isComponent = true;
|
|
427
|
-
if (opts.shallow && (inner || opts.renderRootComponent === false)) {
|
|
428
|
-
nodeName = getComponentName(nodeName);
|
|
429
|
-
} else if (nodeName === Fragment) {
|
|
430
|
-
const children = [];
|
|
431
|
-
getChildren(children, vnode.props.children);
|
|
432
|
-
return _renderToStringPretty(
|
|
433
|
-
children,
|
|
434
|
-
context,
|
|
435
|
-
opts,
|
|
436
|
-
opts.shallowHighOrder !== false,
|
|
437
|
-
isSvgMode,
|
|
438
|
-
selectValue
|
|
439
|
-
);
|
|
440
|
-
} else {
|
|
441
|
-
let rendered;
|
|
442
|
-
|
|
443
|
-
let c = (vnode.__c = createComponent(vnode, context));
|
|
444
|
-
|
|
445
|
-
// options._diff
|
|
446
|
-
if (options.__b) options.__b(vnode);
|
|
447
|
-
|
|
448
|
-
// options._render
|
|
449
|
-
let renderHook = options.__r;
|
|
450
|
-
|
|
451
|
-
if (
|
|
452
|
-
!nodeName.prototype ||
|
|
453
|
-
typeof nodeName.prototype.render !== 'function'
|
|
454
|
-
) {
|
|
455
|
-
let cctx = getContext(nodeName, context);
|
|
205
|
+
if (options[DIFF]) options[DIFF](vnode);
|
|
456
206
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
// Note:
|
|
460
|
-
// This will need to be updated for Preact 11 to use internal.flags rather than component._dirty:
|
|
461
|
-
// https://github.com/preactjs/preact/blob/d4ca6fdb19bc715e49fd144e69f7296b2f4daa40/src/diff/component.js#L35-L44
|
|
462
|
-
let count = 0;
|
|
463
|
-
while (c.__d && count++ < 25) {
|
|
464
|
-
c.__d = false;
|
|
207
|
+
let type = vnode.type,
|
|
208
|
+
props = vnode.props;
|
|
465
209
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
let cctx = getContext(nodeName, context);
|
|
473
|
-
|
|
474
|
-
// c = new nodeName(props, context);
|
|
475
|
-
c = vnode.__c = new nodeName(props, cctx);
|
|
476
|
-
c.__v = vnode;
|
|
477
|
-
// turn off stateful re-rendering:
|
|
478
|
-
c._dirty = c.__d = true;
|
|
479
|
-
c.props = props;
|
|
480
|
-
if (c.state == null) c.state = {};
|
|
481
|
-
|
|
482
|
-
if (c._nextState == null && c.__s == null) {
|
|
483
|
-
c._nextState = c.__s = c.state;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
c.context = cctx;
|
|
487
|
-
if (nodeName.getDerivedStateFromProps)
|
|
488
|
-
c.state = Object.assign(
|
|
489
|
-
{},
|
|
490
|
-
c.state,
|
|
491
|
-
nodeName.getDerivedStateFromProps(c.props, c.state)
|
|
492
|
-
);
|
|
493
|
-
else if (c.componentWillMount) {
|
|
494
|
-
c.componentWillMount();
|
|
495
|
-
|
|
496
|
-
// If the user called setState in cWM we need to flush pending,
|
|
497
|
-
// state updates. This is the same behaviour in React.
|
|
498
|
-
c.state =
|
|
499
|
-
c._nextState !== c.state
|
|
500
|
-
? c._nextState
|
|
501
|
-
: c.__s !== c.state
|
|
502
|
-
? c.__s
|
|
503
|
-
: c.state;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
if (renderHook) renderHook(vnode);
|
|
507
|
-
|
|
508
|
-
rendered = c.render(c.props, c.state, c.context);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
if (c.getChildContext) {
|
|
512
|
-
context = Object.assign({}, context, c.getChildContext());
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
if (options.diffed) options.diffed(vnode);
|
|
516
|
-
return _renderToStringPretty(
|
|
517
|
-
rendered,
|
|
210
|
+
// Invoke rendering on Components
|
|
211
|
+
const isComponent = typeof type === 'function';
|
|
212
|
+
if (isComponent) {
|
|
213
|
+
if (type === Fragment) {
|
|
214
|
+
return _renderToString(
|
|
215
|
+
vnode.props.children,
|
|
518
216
|
context,
|
|
519
|
-
opts,
|
|
520
|
-
opts.shallowHighOrder !== false,
|
|
521
217
|
isSvgMode,
|
|
522
218
|
selectValue
|
|
523
219
|
);
|
|
524
220
|
}
|
|
525
|
-
}
|
|
526
221
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
222
|
+
let rendered;
|
|
223
|
+
if (type.prototype && typeof type.prototype.render === 'function') {
|
|
224
|
+
rendered = renderClassComponent(vnode, context);
|
|
225
|
+
} else {
|
|
226
|
+
rendered = renderFunctionComponent(vnode, context);
|
|
227
|
+
}
|
|
531
228
|
|
|
532
|
-
|
|
533
|
-
|
|
229
|
+
let component = vnode[COMPONENT];
|
|
230
|
+
if (component.getChildContext) {
|
|
231
|
+
context = assign({}, context, component.getChildContext());
|
|
232
|
+
}
|
|
534
233
|
|
|
535
|
-
//
|
|
536
|
-
|
|
234
|
+
// Recurse into children before invoking the after-diff hook
|
|
235
|
+
const str = _renderToString(rendered, context, isSvgMode, selectValue);
|
|
236
|
+
if (options[DIFFED]) options[DIFFED](vnode);
|
|
237
|
+
return str;
|
|
238
|
+
}
|
|
537
239
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
propChildren = v;
|
|
543
|
-
continue;
|
|
544
|
-
}
|
|
240
|
+
// Serialize Element VNodes to HTML
|
|
241
|
+
let s = '<',
|
|
242
|
+
children,
|
|
243
|
+
html;
|
|
545
244
|
|
|
546
|
-
|
|
245
|
+
s = s + type;
|
|
246
|
+
|
|
247
|
+
if (props) {
|
|
248
|
+
children = props.children;
|
|
249
|
+
for (let name in props) {
|
|
250
|
+
let v = props[name];
|
|
547
251
|
|
|
548
252
|
if (
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
253
|
+
name === 'key' ||
|
|
254
|
+
name === 'ref' ||
|
|
255
|
+
name === '__self' ||
|
|
256
|
+
name === '__source' ||
|
|
257
|
+
name === 'children' ||
|
|
258
|
+
(name === 'className' && 'class' in props) ||
|
|
259
|
+
(name === 'htmlFor' && 'for' in props)
|
|
260
|
+
) {
|
|
555
261
|
continue;
|
|
556
|
-
|
|
557
|
-
if (name === 'defaultValue') {
|
|
558
|
-
name = 'value';
|
|
559
|
-
} else if (name === 'defaultChecked') {
|
|
560
|
-
name = 'checked';
|
|
561
|
-
} else if (name === 'defaultSelected') {
|
|
562
|
-
name = 'selected';
|
|
563
|
-
} else if (name === 'className') {
|
|
564
|
-
if (typeof props.class !== 'undefined') continue;
|
|
565
|
-
name = 'class';
|
|
566
|
-
} else if (isSvgMode && XLINK.test(name)) {
|
|
567
|
-
name = name.toLowerCase().replace(/^xlink:?/, 'xlink:');
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
if (name === 'htmlFor') {
|
|
571
|
-
if (props.for) continue;
|
|
572
|
-
name = 'for';
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
if (name === 'style' && v && typeof v === 'object') {
|
|
576
|
-
v = styleObjToCss(v);
|
|
577
262
|
}
|
|
578
263
|
|
|
579
|
-
|
|
580
|
-
// also see https://github.com/preactjs/preact/pull/2347/files
|
|
581
|
-
if (name[0] === 'a' && name['1'] === 'r' && typeof v === 'boolean') {
|
|
582
|
-
v = String(v);
|
|
583
|
-
}
|
|
264
|
+
if (UNSAFE_NAME.test(name)) continue;
|
|
584
265
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
opts.attributeHook(name, v, context, opts, isComponent);
|
|
588
|
-
if (hooked || hooked === '') {
|
|
589
|
-
s = s + hooked;
|
|
590
|
-
continue;
|
|
591
|
-
}
|
|
266
|
+
name = normalizePropName(name, isSvgMode);
|
|
267
|
+
v = normalizePropValue(name, v);
|
|
592
268
|
|
|
593
269
|
if (name === 'dangerouslySetInnerHTML') {
|
|
594
270
|
html = v && v.__html;
|
|
595
|
-
} else if (
|
|
271
|
+
} else if (type === 'textarea' && name === 'value') {
|
|
596
272
|
// <textarea value="a&b"> --> <textarea>a&b</textarea>
|
|
597
|
-
|
|
273
|
+
children = v;
|
|
598
274
|
} else if ((v || v === 0 || v === '') && typeof v !== 'function') {
|
|
599
275
|
if (v === true || v === '') {
|
|
600
276
|
v = name;
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
s = s + ' ' + name;
|
|
604
|
-
continue;
|
|
605
|
-
}
|
|
277
|
+
s = s + ' ' + name;
|
|
278
|
+
continue;
|
|
606
279
|
}
|
|
607
280
|
|
|
608
281
|
if (name === 'value') {
|
|
609
|
-
if (
|
|
282
|
+
if (type === 'select') {
|
|
610
283
|
selectValue = v;
|
|
611
284
|
continue;
|
|
612
285
|
} else if (
|
|
613
286
|
// If we're looking at an <option> and it's the currently selected one
|
|
614
|
-
|
|
287
|
+
type === 'option' &&
|
|
615
288
|
selectValue == v &&
|
|
616
289
|
// and the <option> doesn't already have a selected attribute on it
|
|
617
|
-
|
|
290
|
+
!('selected' in props)
|
|
618
291
|
) {
|
|
619
|
-
s = s +
|
|
292
|
+
s = s + ' selected';
|
|
620
293
|
}
|
|
621
294
|
}
|
|
622
|
-
s = s +
|
|
295
|
+
s = s + ' ' + name + '="' + encodeEntities(v) + '"';
|
|
623
296
|
}
|
|
624
297
|
}
|
|
625
298
|
}
|
|
626
299
|
|
|
627
|
-
|
|
628
|
-
if (pretty) {
|
|
629
|
-
let sub = s.replace(/\n\s*/, ' ');
|
|
630
|
-
if (sub !== s && !~sub.indexOf('\n')) s = sub;
|
|
631
|
-
else if (pretty && ~s.indexOf('\n')) s = s + '\n';
|
|
632
|
-
}
|
|
633
|
-
|
|
300
|
+
let startElement = s;
|
|
634
301
|
s = s + '>';
|
|
635
302
|
|
|
636
|
-
if (UNSAFE_NAME.test(
|
|
637
|
-
throw new Error(`${
|
|
303
|
+
if (UNSAFE_NAME.test(type)) {
|
|
304
|
+
throw new Error(`${type} is not a valid HTML tag name in ${s}`);
|
|
305
|
+
}
|
|
638
306
|
|
|
639
|
-
let
|
|
640
|
-
|
|
641
|
-
(opts.voidElements && opts.voidElements.test(nodeName));
|
|
642
|
-
let pieces = [];
|
|
307
|
+
let pieces = '';
|
|
308
|
+
let hasChildren = false;
|
|
643
309
|
|
|
644
|
-
let children;
|
|
645
310
|
if (html) {
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
} else if (
|
|
652
|
-
propChildren != null &&
|
|
653
|
-
getChildren((children = []), propChildren).length
|
|
654
|
-
) {
|
|
655
|
-
let hasLarge = pretty && ~s.indexOf('\n');
|
|
656
|
-
let lastWasText = false;
|
|
657
|
-
|
|
311
|
+
pieces = pieces + html;
|
|
312
|
+
hasChildren = true;
|
|
313
|
+
} else if (typeof children === 'string') {
|
|
314
|
+
pieces = pieces + encodeEntities(children);
|
|
315
|
+
hasChildren = true;
|
|
316
|
+
} else if (isArray(children)) {
|
|
658
317
|
for (let i = 0; i < children.length; i++) {
|
|
659
318
|
let child = children[i];
|
|
660
319
|
|
|
661
320
|
if (child != null && child !== false) {
|
|
662
321
|
let childSvgMode =
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
: nodeName === 'foreignObject'
|
|
666
|
-
? false
|
|
667
|
-
: isSvgMode,
|
|
668
|
-
ret = _renderToStringPretty(
|
|
669
|
-
child,
|
|
670
|
-
context,
|
|
671
|
-
opts,
|
|
672
|
-
true,
|
|
673
|
-
childSvgMode,
|
|
674
|
-
selectValue
|
|
675
|
-
);
|
|
676
|
-
|
|
677
|
-
if (pretty && !hasLarge && isLargeString(ret)) hasLarge = true;
|
|
322
|
+
type === 'svg' || (type !== 'foreignObject' && isSvgMode);
|
|
323
|
+
let ret = _renderToString(child, context, childSvgMode, selectValue);
|
|
678
324
|
|
|
679
325
|
// Skip if we received an empty string
|
|
680
326
|
if (ret) {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
// We merge adjacent text nodes, otherwise each piece would be printed
|
|
685
|
-
// on a new line.
|
|
686
|
-
if (lastWasText && isText) {
|
|
687
|
-
pieces[pieces.length - 1] += ret;
|
|
688
|
-
} else {
|
|
689
|
-
pieces.push(ret);
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
lastWasText = isText;
|
|
693
|
-
} else {
|
|
694
|
-
pieces.push(ret);
|
|
695
|
-
}
|
|
327
|
+
pieces = pieces + ret;
|
|
328
|
+
hasChildren = true;
|
|
696
329
|
}
|
|
697
330
|
}
|
|
698
331
|
}
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
332
|
+
} else if (children != null && children !== false && children !== true) {
|
|
333
|
+
let childSvgMode =
|
|
334
|
+
type === 'svg' || (type !== 'foreignObject' && isSvgMode);
|
|
335
|
+
let ret = _renderToString(children, context, childSvgMode, selectValue);
|
|
336
|
+
|
|
337
|
+
// Skip if we received an empty string
|
|
338
|
+
if (ret) {
|
|
339
|
+
pieces = pieces + ret;
|
|
340
|
+
hasChildren = true;
|
|
703
341
|
}
|
|
704
342
|
}
|
|
705
343
|
|
|
706
|
-
if (
|
|
707
|
-
s = s + pieces.join('');
|
|
708
|
-
} else if (opts && opts.xml) {
|
|
709
|
-
return s.substring(0, s.length - 1) + ' />';
|
|
710
|
-
}
|
|
344
|
+
if (options[DIFFED]) options[DIFFED](vnode);
|
|
711
345
|
|
|
712
|
-
if (
|
|
713
|
-
s = s
|
|
714
|
-
} else {
|
|
715
|
-
|
|
716
|
-
s = s + `</${nodeName}>`;
|
|
346
|
+
if (hasChildren) {
|
|
347
|
+
s = s + pieces;
|
|
348
|
+
} else if (VOID_ELEMENTS.test(type)) {
|
|
349
|
+
return startElement + ' />';
|
|
717
350
|
}
|
|
718
351
|
|
|
719
|
-
return s;
|
|
352
|
+
return s + '</' + type + '>';
|
|
720
353
|
}
|
|
721
354
|
|
|
722
|
-
|
|
723
|
-
return (
|
|
724
|
-
component.displayName ||
|
|
725
|
-
(component !== Function && component.name) ||
|
|
726
|
-
getFallbackComponentName(component)
|
|
727
|
-
);
|
|
728
|
-
}
|
|
355
|
+
/** The default export is an alias of `render()`. */
|
|
729
356
|
|
|
730
|
-
function getFallbackComponentName(component) {
|
|
731
|
-
let str = Function.prototype.toString.call(component),
|
|
732
|
-
name = (str.match(/^\s*function\s+([^( ]+)/) || '')[1];
|
|
733
|
-
if (!name) {
|
|
734
|
-
// search for an existing indexed name for the given component:
|
|
735
|
-
let index = -1;
|
|
736
|
-
for (let i = UNNAMED.length; i--; ) {
|
|
737
|
-
if (UNNAMED[i] === component) {
|
|
738
|
-
index = i;
|
|
739
|
-
break;
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
// not found, create a new indexed name:
|
|
743
|
-
if (index < 0) {
|
|
744
|
-
index = UNNAMED.push(component) - 1;
|
|
745
|
-
}
|
|
746
|
-
name = `UnnamedComponent${index}`;
|
|
747
|
-
}
|
|
748
|
-
return name;
|
|
749
|
-
}
|
|
750
357
|
renderToString.shallowRender = shallowRender;
|
|
751
358
|
|
|
752
359
|
export default renderToString;
|