preact-render-to-string 5.1.21 → 5.2.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/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 +5 -4
- package/src/index.js +375 -61
- package/src/util.js +28 -11
package/src/index.js
CHANGED
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
indent,
|
|
4
4
|
isLargeString,
|
|
5
5
|
styleObjToCss,
|
|
6
|
-
assign,
|
|
7
6
|
getChildren
|
|
8
7
|
} from './util';
|
|
9
8
|
import { options, Fragment } from 'preact';
|
|
@@ -18,8 +17,11 @@ const UNNAMED = [];
|
|
|
18
17
|
const VOID_ELEMENTS = /^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/;
|
|
19
18
|
|
|
20
19
|
const UNSAFE_NAME = /[\s\n\\/='"\0<>]/;
|
|
20
|
+
const XLINK = /^xlink:?./;
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
function markAsDirty() {
|
|
23
|
+
this.__d = true;
|
|
24
|
+
}
|
|
23
25
|
|
|
24
26
|
/** Render Preact JSX + Components to an HTML string.
|
|
25
27
|
* @name render
|
|
@@ -56,7 +58,12 @@ function renderToString(vnode, context, opts) {
|
|
|
56
58
|
const previousSkipEffects = options.__s;
|
|
57
59
|
options.__s = true;
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
let res;
|
|
62
|
+
if (opts.pretty || opts.sortAttributes) {
|
|
63
|
+
res = _renderToStringPretty(vnode, context, opts);
|
|
64
|
+
} else {
|
|
65
|
+
res = _renderToString(vnode, context, opts);
|
|
66
|
+
}
|
|
60
67
|
|
|
61
68
|
// options._commit, we don't schedule any effects in this library right now,
|
|
62
69
|
// so we can pass an empty queue to this hook.
|
|
@@ -66,6 +73,32 @@ function renderToString(vnode, context, opts) {
|
|
|
66
73
|
return res;
|
|
67
74
|
}
|
|
68
75
|
|
|
76
|
+
function createComponent(vnode, context) {
|
|
77
|
+
return {
|
|
78
|
+
__v: vnode,
|
|
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
|
+
}
|
|
89
|
+
|
|
90
|
+
// Necessary for createContext api. Setting this property will pass
|
|
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
|
+
}
|
|
101
|
+
|
|
69
102
|
/** The default export is an alias of `render()`. */
|
|
70
103
|
function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
71
104
|
if (vnode == null || typeof vnode === 'boolean') {
|
|
@@ -77,21 +110,309 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
77
110
|
return encodeEntities(vnode);
|
|
78
111
|
}
|
|
79
112
|
|
|
80
|
-
let pretty = opts.pretty,
|
|
81
|
-
indentChar = pretty && typeof pretty === 'string' ? pretty : '\t';
|
|
82
|
-
|
|
83
113
|
if (Array.isArray(vnode)) {
|
|
84
|
-
|
|
114
|
+
const rendered = [];
|
|
85
115
|
for (let i = 0; i < vnode.length; i++) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
116
|
+
rendered.push(
|
|
117
|
+
_renderToString(vnode[i], context, opts, inner, isSvgMode, selectValue)
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
return rendered.join('');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let nodeName = vnode.type,
|
|
124
|
+
props = vnode.props,
|
|
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,
|
|
89
137
|
context,
|
|
90
138
|
opts,
|
|
91
|
-
|
|
139
|
+
opts.shallowHighOrder !== false,
|
|
92
140
|
isSvgMode,
|
|
93
141
|
selectValue
|
|
94
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
|
+
}
|
|
217
|
+
|
|
218
|
+
if (options.diffed) options.diffed(vnode);
|
|
219
|
+
return _renderToString(
|
|
220
|
+
rendered,
|
|
221
|
+
context,
|
|
222
|
+
opts,
|
|
223
|
+
opts.shallowHighOrder !== false,
|
|
224
|
+
isSvgMode,
|
|
225
|
+
selectValue
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// render JSX to HTML
|
|
231
|
+
let s = `<${nodeName}`,
|
|
232
|
+
propChildren,
|
|
233
|
+
html;
|
|
234
|
+
|
|
235
|
+
if (props) {
|
|
236
|
+
for (let name in props) {
|
|
237
|
+
let v = props[name];
|
|
238
|
+
if (name === 'children') {
|
|
239
|
+
propChildren = v;
|
|
240
|
+
continue;
|
|
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
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
s = s + '>';
|
|
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];
|
|
343
|
+
|
|
344
|
+
if (child != null && child !== false) {
|
|
345
|
+
let childSvgMode =
|
|
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
|
+
);
|
|
359
|
+
|
|
360
|
+
// Skip if we received an empty string
|
|
361
|
+
if (ret) {
|
|
362
|
+
pieces = pieces + ret;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (pieces.length || html) {
|
|
369
|
+
s = s + pieces;
|
|
370
|
+
} else if (opts && opts.xml) {
|
|
371
|
+
return s.substring(0, s.length - 1) + ' />';
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (isVoid && !children && !html) {
|
|
375
|
+
return s.replace(/>$/, ' />');
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return `${s}</${nodeName}>`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/** The default export is an alias of `render()`. */
|
|
382
|
+
function _renderToStringPretty(
|
|
383
|
+
vnode,
|
|
384
|
+
context,
|
|
385
|
+
opts,
|
|
386
|
+
inner,
|
|
387
|
+
isSvgMode,
|
|
388
|
+
selectValue
|
|
389
|
+
) {
|
|
390
|
+
if (vnode == null || typeof vnode === 'boolean') {
|
|
391
|
+
return '';
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// #text nodes
|
|
395
|
+
if (typeof vnode !== 'object') {
|
|
396
|
+
return encodeEntities(vnode);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
let pretty = opts.pretty,
|
|
400
|
+
indentChar = pretty && typeof pretty === 'string' ? pretty : '\t';
|
|
401
|
+
|
|
402
|
+
if (Array.isArray(vnode)) {
|
|
403
|
+
let rendered = '';
|
|
404
|
+
for (let i = 0; i < vnode.length; i++) {
|
|
405
|
+
if (pretty && i > 0) rendered = rendered + '\n';
|
|
406
|
+
rendered =
|
|
407
|
+
rendered +
|
|
408
|
+
_renderToStringPretty(
|
|
409
|
+
vnode[i],
|
|
410
|
+
context,
|
|
411
|
+
opts,
|
|
412
|
+
inner,
|
|
413
|
+
isSvgMode,
|
|
414
|
+
selectValue
|
|
415
|
+
);
|
|
95
416
|
}
|
|
96
417
|
return rendered;
|
|
97
418
|
}
|
|
@@ -108,7 +429,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
108
429
|
} else if (nodeName === Fragment) {
|
|
109
430
|
const children = [];
|
|
110
431
|
getChildren(children, vnode.props.children);
|
|
111
|
-
return
|
|
432
|
+
return _renderToStringPretty(
|
|
112
433
|
children,
|
|
113
434
|
context,
|
|
114
435
|
opts,
|
|
@@ -119,50 +440,36 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
119
440
|
} else {
|
|
120
441
|
let rendered;
|
|
121
442
|
|
|
122
|
-
let c = (vnode.__c =
|
|
123
|
-
__v: vnode,
|
|
124
|
-
context,
|
|
125
|
-
props: vnode.props,
|
|
126
|
-
// silently drop state updates
|
|
127
|
-
setState: noop,
|
|
128
|
-
forceUpdate: noop,
|
|
129
|
-
// hooks
|
|
130
|
-
__h: []
|
|
131
|
-
});
|
|
443
|
+
let c = (vnode.__c = createComponent(vnode, context));
|
|
132
444
|
|
|
133
445
|
// options._diff
|
|
134
446
|
if (options.__b) options.__b(vnode);
|
|
135
447
|
|
|
136
448
|
// options._render
|
|
137
|
-
|
|
449
|
+
let renderHook = options.__r;
|
|
138
450
|
|
|
139
451
|
if (
|
|
140
452
|
!nodeName.prototype ||
|
|
141
453
|
typeof nodeName.prototype.render !== 'function'
|
|
142
454
|
) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
455
|
+
let cctx = getContext(nodeName, context);
|
|
456
|
+
|
|
457
|
+
// If a hook invokes setState() to invalidate the component during rendering,
|
|
458
|
+
// re-render it up to 25 times to allow "settling" of memoized states.
|
|
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;
|
|
465
|
+
|
|
466
|
+
if (renderHook) renderHook(vnode);
|
|
467
|
+
|
|
468
|
+
// stateless functional components
|
|
469
|
+
rendered = nodeName.call(vnode.__c, props, cctx);
|
|
470
|
+
}
|
|
156
471
|
} else {
|
|
157
|
-
|
|
158
|
-
let cxType = nodeName.contextType;
|
|
159
|
-
let provider = cxType && context[cxType.__c];
|
|
160
|
-
let cctx =
|
|
161
|
-
cxType != null
|
|
162
|
-
? provider
|
|
163
|
-
? provider.props.value
|
|
164
|
-
: cxType.__
|
|
165
|
-
: context;
|
|
472
|
+
let cctx = getContext(nodeName, context);
|
|
166
473
|
|
|
167
474
|
// c = new nodeName(props, context);
|
|
168
475
|
c = vnode.__c = new nodeName(props, cctx);
|
|
@@ -178,8 +485,9 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
178
485
|
|
|
179
486
|
c.context = cctx;
|
|
180
487
|
if (nodeName.getDerivedStateFromProps)
|
|
181
|
-
c.state = assign(
|
|
182
|
-
|
|
488
|
+
c.state = Object.assign(
|
|
489
|
+
{},
|
|
490
|
+
c.state,
|
|
183
491
|
nodeName.getDerivedStateFromProps(c.props, c.state)
|
|
184
492
|
);
|
|
185
493
|
else if (c.componentWillMount) {
|
|
@@ -195,15 +503,17 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
195
503
|
: c.state;
|
|
196
504
|
}
|
|
197
505
|
|
|
506
|
+
if (renderHook) renderHook(vnode);
|
|
507
|
+
|
|
198
508
|
rendered = c.render(c.props, c.state, c.context);
|
|
199
509
|
}
|
|
200
510
|
|
|
201
511
|
if (c.getChildContext) {
|
|
202
|
-
context = assign(
|
|
512
|
+
context = Object.assign({}, context, c.getChildContext());
|
|
203
513
|
}
|
|
204
514
|
|
|
205
515
|
if (options.diffed) options.diffed(vnode);
|
|
206
|
-
return
|
|
516
|
+
return _renderToStringPretty(
|
|
207
517
|
rendered,
|
|
208
518
|
context,
|
|
209
519
|
opts,
|
|
@@ -246,10 +556,14 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
246
556
|
|
|
247
557
|
if (name === 'defaultValue') {
|
|
248
558
|
name = 'value';
|
|
559
|
+
} else if (name === 'defaultChecked') {
|
|
560
|
+
name = 'checked';
|
|
561
|
+
} else if (name === 'defaultSelected') {
|
|
562
|
+
name = 'selected';
|
|
249
563
|
} else if (name === 'className') {
|
|
250
564
|
if (typeof props.class !== 'undefined') continue;
|
|
251
565
|
name = 'class';
|
|
252
|
-
} else if (isSvgMode &&
|
|
566
|
+
} else if (isSvgMode && XLINK.test(name)) {
|
|
253
567
|
name = name.toLowerCase().replace(/^xlink:?/, 'xlink:');
|
|
254
568
|
}
|
|
255
569
|
|
|
@@ -272,7 +586,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
272
586
|
opts.attributeHook &&
|
|
273
587
|
opts.attributeHook(name, v, context, opts, isComponent);
|
|
274
588
|
if (hooked || hooked === '') {
|
|
275
|
-
s
|
|
589
|
+
s = s + hooked;
|
|
276
590
|
continue;
|
|
277
591
|
}
|
|
278
592
|
|
|
@@ -286,7 +600,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
286
600
|
v = name;
|
|
287
601
|
// in non-xml mode, allow boolean attributes
|
|
288
602
|
if (!opts || !opts.xml) {
|
|
289
|
-
s
|
|
603
|
+
s = s + ' ' + name;
|
|
290
604
|
continue;
|
|
291
605
|
}
|
|
292
606
|
}
|
|
@@ -302,10 +616,10 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
302
616
|
// and the <option> doesn't already have a selected attribute on it
|
|
303
617
|
typeof props.selected === 'undefined'
|
|
304
618
|
) {
|
|
305
|
-
s
|
|
619
|
+
s = s + ` selected`;
|
|
306
620
|
}
|
|
307
621
|
}
|
|
308
|
-
s
|
|
622
|
+
s = s + ` ${name}="${encodeEntities(v)}"`;
|
|
309
623
|
}
|
|
310
624
|
}
|
|
311
625
|
}
|
|
@@ -314,10 +628,10 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
314
628
|
if (pretty) {
|
|
315
629
|
let sub = s.replace(/\n\s*/, ' ');
|
|
316
630
|
if (sub !== s && !~sub.indexOf('\n')) s = sub;
|
|
317
|
-
else if (pretty && ~s.indexOf('\n')) s
|
|
631
|
+
else if (pretty && ~s.indexOf('\n')) s = s + '\n';
|
|
318
632
|
}
|
|
319
633
|
|
|
320
|
-
s
|
|
634
|
+
s = s + '>';
|
|
321
635
|
|
|
322
636
|
if (UNSAFE_NAME.test(nodeName))
|
|
323
637
|
throw new Error(`${nodeName} is not a valid HTML tag name in ${s}`);
|
|
@@ -333,7 +647,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
333
647
|
if (pretty && isLargeString(html)) {
|
|
334
648
|
html = '\n' + indentChar + indent(html, indentChar);
|
|
335
649
|
}
|
|
336
|
-
s
|
|
650
|
+
s = s + html;
|
|
337
651
|
} else if (
|
|
338
652
|
propChildren != null &&
|
|
339
653
|
getChildren((children = []), propChildren).length
|
|
@@ -351,7 +665,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
351
665
|
: nodeName === 'foreignObject'
|
|
352
666
|
? false
|
|
353
667
|
: isSvgMode,
|
|
354
|
-
ret =
|
|
668
|
+
ret = _renderToStringPretty(
|
|
355
669
|
child,
|
|
356
670
|
context,
|
|
357
671
|
opts,
|
|
@@ -390,7 +704,7 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
390
704
|
}
|
|
391
705
|
|
|
392
706
|
if (pieces.length || html) {
|
|
393
|
-
s
|
|
707
|
+
s = s + pieces.join('');
|
|
394
708
|
} else if (opts && opts.xml) {
|
|
395
709
|
return s.substring(0, s.length - 1) + ' />';
|
|
396
710
|
}
|
|
@@ -398,8 +712,8 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) {
|
|
|
398
712
|
if (isVoid && !children && !html) {
|
|
399
713
|
s = s.replace(/>$/, ' />');
|
|
400
714
|
} else {
|
|
401
|
-
if (pretty && ~s.indexOf('\n')) s
|
|
402
|
-
s
|
|
715
|
+
if (pretty && ~s.indexOf('\n')) s = s + '\n';
|
|
716
|
+
s = s + `</${nodeName}>`;
|
|
403
717
|
}
|
|
404
718
|
|
|
405
719
|
return s;
|
package/src/util.js
CHANGED
|
@@ -3,16 +3,34 @@ export const IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine
|
|
|
3
3
|
|
|
4
4
|
const ENCODED_ENTITIES = /[&<>"]/;
|
|
5
5
|
|
|
6
|
-
export function encodeEntities(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
export function encodeEntities(str) {
|
|
7
|
+
// Ensure we're always parsing and returning a string:
|
|
8
|
+
str += '';
|
|
9
|
+
|
|
10
|
+
// Skip all work for strings with no entities needing encoding:
|
|
11
|
+
if (ENCODED_ENTITIES.test(str) === false) return str;
|
|
12
|
+
|
|
13
|
+
let last = 0,
|
|
14
|
+
i = 0,
|
|
15
|
+
out = '',
|
|
16
|
+
ch = '';
|
|
17
|
+
|
|
18
|
+
// Seek forward in str until the next entity char:
|
|
19
|
+
for (; i<str.length; i++) {
|
|
20
|
+
switch (str.charCodeAt(i)) {
|
|
21
|
+
case 60: ch = '<'; break;
|
|
22
|
+
case 62: ch = '>'; break;
|
|
23
|
+
case 34: ch = '"'; break;
|
|
24
|
+
case 38: ch = '&'; break;
|
|
25
|
+
default: continue;
|
|
26
|
+
}
|
|
27
|
+
// Append skipped/buffered characters and the encoded entity:
|
|
28
|
+
if (i > last) out += str.slice(last, i);
|
|
29
|
+
out += ch;
|
|
30
|
+
// Start the next seek/buffer after the entity's offset:
|
|
31
|
+
last = i + 1;
|
|
10
32
|
}
|
|
11
|
-
return
|
|
12
|
-
.replace(/&/g, '&')
|
|
13
|
-
.replace(/</g, '<')
|
|
14
|
-
.replace(/>/g, '>')
|
|
15
|
-
.replace(/"/g, '"');
|
|
33
|
+
return out + str.slice(last, i);
|
|
16
34
|
}
|
|
17
35
|
|
|
18
36
|
export let indent = (s, char) =>
|
|
@@ -57,8 +75,7 @@ export function styleObjToCss(s) {
|
|
|
57
75
|
* @private
|
|
58
76
|
*/
|
|
59
77
|
export function assign(obj, props) {
|
|
60
|
-
|
|
61
|
-
return obj;
|
|
78
|
+
return Object.assign(obj, props);
|
|
62
79
|
}
|
|
63
80
|
|
|
64
81
|
/**
|