@@ -25,6 +25,7 @@ import {
25
25
const EMPTY_ARR = [ ] ;
26
26
const isArray = Array . isArray ;
27
27
const assign = Object . assign ;
28
+ const EMPTY_STR = '' ;
28
29
29
30
// Global state for the current render pass
30
31
let beforeDiff , afterDiff , renderHook , ummountHook ;
@@ -65,8 +66,8 @@ export function renderToString(vnode, context, _rendererState) {
65
66
_rendererState
66
67
) ;
67
68
68
- if ( Array . isArray ( rendered ) ) {
69
- return rendered . join ( '' ) ;
69
+ if ( isArray ( rendered ) ) {
70
+ return rendered . join ( EMPTY_STR ) ;
70
71
}
71
72
return rendered ;
72
73
} catch ( e ) {
@@ -119,7 +120,7 @@ export async function renderToStringAsync(vnode, context) {
119
120
undefined
120
121
) ;
121
122
122
- if ( Array . isArray ( rendered ) ) {
123
+ if ( isArray ( rendered ) ) {
123
124
let count = 0 ;
124
125
let resolved = rendered ;
125
126
@@ -133,7 +134,7 @@ export async function renderToStringAsync(vnode, context) {
133
134
resolved = ( await Promise . all ( resolved ) ) . flat ( ) ;
134
135
}
135
136
136
- return resolved . join ( '' ) ;
137
+ return resolved . join ( EMPTY_STR ) ;
137
138
}
138
139
139
140
return rendered ;
@@ -226,19 +227,26 @@ function _renderToString(
226
227
renderer
227
228
) {
228
229
// Ignore non-rendered VNodes/values
229
- if ( vnode == null || vnode === true || vnode === false || vnode === '' ) {
230
- return '' ;
230
+ if (
231
+ vnode == null ||
232
+ vnode === true ||
233
+ vnode === false ||
234
+ vnode === EMPTY_STR
235
+ ) {
236
+ return EMPTY_STR ;
231
237
}
232
238
233
239
// Text VNodes: escape as HTML
234
240
if ( typeof vnode !== 'object' ) {
235
- if ( typeof vnode === 'function' ) return '' ;
236
- return encodeEntities ( vnode + '' ) ;
241
+ if ( typeof vnode === 'function' ) return EMPTY_STR ;
242
+ return typeof vnode === 'string'
243
+ ? encodeEntities ( vnode )
244
+ : vnode + EMPTY_STR ;
237
245
}
238
246
239
247
// Recurse into children / Arrays
240
248
if ( isArray ( vnode ) ) {
241
- let rendered = '' ,
249
+ let rendered = EMPTY_STR ,
242
250
renderArray ;
243
251
parent [ CHILDREN ] = vnode ;
244
252
for ( let i = 0 ; i < vnode . length ; i ++ ) {
@@ -256,15 +264,15 @@ function _renderToString(
256
264
) ;
257
265
258
266
if ( typeof childRender === 'string' ) {
259
- rendered += childRender ;
267
+ rendered = rendered + childRender ;
260
268
} else {
261
269
renderArray = renderArray || [ ] ;
262
270
263
271
if ( rendered ) renderArray . push ( rendered ) ;
264
272
265
- rendered = '' ;
273
+ rendered = EMPTY_STR ;
266
274
267
- if ( Array . isArray ( childRender ) ) {
275
+ if ( isArray ( childRender ) ) {
268
276
renderArray . push ( ...childRender ) ;
269
277
} else {
270
278
renderArray . push ( childRender ) ;
@@ -281,7 +289,7 @@ function _renderToString(
281
289
}
282
290
283
291
// VNodes have {constructor:undefined} to prevent JSON injection:
284
- if ( vnode . constructor !== undefined ) return '' ;
292
+ if ( vnode . constructor !== undefined ) return EMPTY_STR ;
285
293
286
294
vnode [ PARENT ] = parent ;
287
295
if ( beforeDiff ) beforeDiff ( vnode ) ;
@@ -298,9 +306,9 @@ function _renderToString(
298
306
if ( type === Fragment ) {
299
307
// Serialized precompiled JSX.
300
308
if ( props . tpl ) {
301
- let out = '' ;
309
+ let out = EMPTY_STR ;
302
310
for ( let i = 0 ; i < props . tpl . length ; i ++ ) {
303
- out += props . tpl [ i ] ;
311
+ out = out + props . tpl [ i ] ;
304
312
305
313
if ( props . exprs && i < props . exprs . length ) {
306
314
const value = props . exprs [ i ] ;
@@ -311,18 +319,20 @@ function _renderToString(
311
319
typeof value === 'object' &&
312
320
( value . constructor === undefined || isArray ( value ) )
313
321
) {
314
- out += _renderToString (
315
- value ,
316
- context ,
317
- isSvgMode ,
318
- selectValue ,
319
- vnode ,
320
- asyncMode ,
321
- renderer
322
- ) ;
322
+ out =
323
+ out +
324
+ _renderToString (
325
+ value ,
326
+ context ,
327
+ isSvgMode ,
328
+ selectValue ,
329
+ vnode ,
330
+ asyncMode ,
331
+ renderer
332
+ ) ;
323
333
} else {
324
334
// Values are pre-escaped by the JSX transform
325
- out += value ;
335
+ out = out + value ;
326
336
}
327
337
}
328
338
}
@@ -331,7 +341,9 @@ function _renderToString(
331
341
} else if ( props . UNSTABLE_comment ) {
332
342
// Fragments are the least used components of core that's why
333
343
// branching here for comments has the least effect on perf.
334
- return '<!--' + encodeEntities ( props . UNSTABLE_comment || '' ) + '-->' ;
344
+ return (
345
+ '<!--' + encodeEntities ( props . UNSTABLE_comment || EMPTY_STR ) + '-->'
346
+ ) ;
335
347
}
336
348
337
349
rendered = props . children ;
@@ -342,11 +354,13 @@ function _renderToString(
342
354
cctx = provider ? provider . props . value : contextType . __ ;
343
355
}
344
356
345
- if ( type . prototype && typeof type . prototype . render === 'function' ) {
357
+ let isClassComponent =
358
+ type . prototype && typeof type . prototype . render === 'function' ;
359
+ if ( isClassComponent ) {
346
360
rendered = /**#__NOINLINE__**/ renderClassComponent ( vnode , cctx ) ;
347
361
component = vnode [ COMPONENT ] ;
348
362
} else {
349
- component = {
363
+ vnode [ COMPONENT ] = component = {
350
364
__v : vnode ,
351
365
props,
352
366
context : cctx ,
@@ -357,7 +371,6 @@ function _renderToString(
357
371
// hooks
358
372
__h : [ ]
359
373
} ;
360
- vnode [ COMPONENT ] = component ;
361
374
362
375
// If a hook invokes setState() to invalidate the component during rendering,
363
376
// re-render it up to 25 times to allow "settling" of memoized states.
@@ -380,10 +393,10 @@ function _renderToString(
380
393
}
381
394
382
395
if (
383
- ( type . getDerivedStateFromError || component . componentDidCatch ) &&
384
- options . errorBoundaries
396
+ isClassComponent &&
397
+ options . errorBoundaries &&
398
+ ( type . getDerivedStateFromError || component . componentDidCatch )
385
399
) {
386
- let str = '' ;
387
400
// When a component returns a Fragment node we flatten it in core, so we
388
401
// need to mirror that logic here too
389
402
let isTopLevelFragment =
@@ -393,7 +406,7 @@ function _renderToString(
393
406
rendered = isTopLevelFragment ? rendered . props . children : rendered ;
394
407
395
408
try {
396
- str = _renderToString (
409
+ return _renderToString (
397
410
rendered ,
398
411
context ,
399
412
isSvgMode ,
@@ -402,14 +415,15 @@ function _renderToString(
402
415
asyncMode ,
403
416
renderer
404
417
) ;
405
- return str ;
406
418
} catch ( err ) {
419
+ let str = EMPTY_STR ;
420
+
407
421
if ( type . getDerivedStateFromError ) {
408
422
component [ NEXT_STATE ] = type . getDerivedStateFromError ( err ) ;
409
423
}
410
424
411
425
if ( component . componentDidCatch ) {
412
- component . componentDidCatch ( err , { } ) ;
426
+ component . componentDidCatch ( err , EMPTY_OBJ ) ;
413
427
}
414
428
415
429
if ( component [ DIRTY ] ) {
@@ -493,7 +507,7 @@ function _renderToString(
493
507
494
508
let errorHook = options [ CATCH_ERROR ] ;
495
509
if ( errorHook ) errorHook ( error , vnode ) ;
496
- return '' ;
510
+ return EMPTY_STR ;
497
511
}
498
512
499
513
if ( ! asyncMode ) throw error ;
@@ -525,23 +539,25 @@ function _renderToString(
525
539
asyncMode ,
526
540
renderer
527
541
) ,
528
- ( ) => renderNestedChildren ( )
542
+ renderNestedChildren
529
543
) ;
530
544
}
531
545
} ;
532
546
533
- return error . then ( ( ) => renderNestedChildren ( ) ) ;
547
+ return error . then ( renderNestedChildren ) ;
534
548
}
535
549
}
536
550
537
551
// Serialize Element VNodes to HTML
538
552
let s = '<' + type ,
539
- html = '' ,
553
+ html = EMPTY_STR ,
540
554
children ;
541
555
542
556
for ( let name in props ) {
543
557
let v = props [ name ] ;
544
558
559
+ if ( typeof v === 'function' ) continue ;
560
+
545
561
switch ( name ) {
546
562
case 'children' :
547
563
children = v ;
@@ -622,7 +638,7 @@ function _renderToString(
622
638
// serialize boolean aria-xyz or draggable attribute values as strings
623
639
// `draggable` is an enumerated attribute and not Boolean. A value of `true` or `false` is mandatory
624
640
// https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable
625
- v += '' ;
641
+ v = v + EMPTY_STR ;
626
642
} else if ( isSvgMode ) {
627
643
if ( SVG_CAMEL_CASE . test ( name ) ) {
628
644
name =
@@ -637,11 +653,17 @@ function _renderToString(
637
653
}
638
654
639
655
// write this attribute to the buffer
640
- if ( v != null && v !== false && typeof v !== 'function' ) {
641
- if ( v === true || v === '' ) {
656
+ if ( v != null && v !== false ) {
657
+ if ( v === true || v === EMPTY_STR ) {
642
658
s = s + ' ' + name ;
643
659
} else {
644
- s = s + ' ' + name + '="' + encodeEntities ( v + '' ) + '"' ;
660
+ s =
661
+ s +
662
+ ' ' +
663
+ name +
664
+ '="' +
665
+ ( typeof v === 'string' ? encodeEntities ( v ) : v + EMPTY_STR ) +
666
+ '"' ;
645
667
}
646
668
}
647
669
}
@@ -687,7 +709,7 @@ function _renderToString(
687
709
const endTag = '</' + type + '>' ;
688
710
const startTag = s + '>' ;
689
711
690
- if ( Array . isArray ( html ) ) return [ startTag , ...html , endTag ] ;
712
+ if ( isArray ( html ) ) return [ startTag , ...html , endTag ] ;
691
713
else if ( typeof html !== 'string' ) return [ startTag , html , endTag ] ;
692
714
return startTag + html + endTag ;
693
715
}
0 commit comments