preact-render-to-string 6.5.7 → 6.5.8

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
@@ -4,7 +4,8 @@ import {
4
4
  UNSAFE_NAME,
5
5
  NAMESPACE_REPLACE_REGEX,
6
6
  HTML_LOWER_CASE,
7
- SVG_CAMEL_CASE
7
+ SVG_CAMEL_CASE,
8
+ createComponent
8
9
  } from './lib/util.js';
9
10
  import { options, h, Fragment } from 'preact';
10
11
  import {
@@ -22,9 +23,11 @@ import {
22
23
  CATCH_ERROR
23
24
  } from './lib/constants.js';
24
25
 
26
+ const EMPTY_OBJ = {};
25
27
  const EMPTY_ARR = [];
26
28
  const isArray = Array.isArray;
27
29
  const assign = Object.assign;
30
+ const EMPTY_STR = '';
28
31
 
29
32
  // Global state for the current render pass
30
33
  let beforeDiff, afterDiff, renderHook, ummountHook;
@@ -65,8 +68,8 @@ export function renderToString(vnode, context, _rendererState) {
65
68
  _rendererState
66
69
  );
67
70
 
68
- if (Array.isArray(rendered)) {
69
- return rendered.join('');
71
+ if (isArray(rendered)) {
72
+ return rendered.join(EMPTY_STR);
70
73
  }
71
74
  return rendered;
72
75
  } catch (e) {
@@ -119,7 +122,7 @@ export async function renderToStringAsync(vnode, context) {
119
122
  undefined
120
123
  );
121
124
 
122
- if (Array.isArray(rendered)) {
125
+ if (isArray(rendered)) {
123
126
  let count = 0;
124
127
  let resolved = rendered;
125
128
 
@@ -133,7 +136,7 @@ export async function renderToStringAsync(vnode, context) {
133
136
  resolved = (await Promise.all(resolved)).flat();
134
137
  }
135
138
 
136
- return resolved.join('');
139
+ return resolved.join(EMPTY_STR);
137
140
  }
138
141
 
139
142
  return rendered;
@@ -146,13 +149,6 @@ export async function renderToStringAsync(vnode, context) {
146
149
  }
147
150
  }
148
151
 
149
- // Installed as setState/forceUpdate for function components
150
- function markAsDirty() {
151
- this.__d = true;
152
- }
153
-
154
- const EMPTY_OBJ = {};
155
-
156
152
  /**
157
153
  * @param {VNode} vnode
158
154
  * @param {Record<string, unknown>} context
@@ -226,24 +222,30 @@ function _renderToString(
226
222
  renderer
227
223
  ) {
228
224
  // Ignore non-rendered VNodes/values
229
- if (vnode == null || vnode === true || vnode === false || vnode === '') {
230
- return '';
225
+ if (
226
+ vnode == null ||
227
+ vnode === true ||
228
+ vnode === false ||
229
+ vnode === EMPTY_STR
230
+ ) {
231
+ return EMPTY_STR;
231
232
  }
232
233
 
234
+ let vnodeType = typeof vnode;
233
235
  // Text VNodes: escape as HTML
234
- if (typeof vnode !== 'object') {
235
- if (typeof vnode === 'function') return '';
236
- return encodeEntities(vnode + '');
236
+ if (vnodeType != 'object') {
237
+ if (vnodeType == 'function') return EMPTY_STR;
238
+ return vnodeType == 'string' ? encodeEntities(vnode) : vnode + EMPTY_STR;
237
239
  }
238
240
 
239
241
  // Recurse into children / Arrays
240
242
  if (isArray(vnode)) {
241
- let rendered = '',
243
+ let rendered = EMPTY_STR,
242
244
  renderArray;
243
245
  parent[CHILDREN] = vnode;
244
246
  for (let i = 0; i < vnode.length; i++) {
245
247
  let child = vnode[i];
246
- if (child == null || typeof child === 'boolean') continue;
248
+ if (child == null || typeof child == 'boolean') continue;
247
249
 
248
250
  const childRender = _renderToString(
249
251
  child,
@@ -255,16 +257,18 @@ function _renderToString(
255
257
  renderer
256
258
  );
257
259
 
258
- if (typeof childRender === 'string') {
259
- rendered += childRender;
260
+ if (typeof childRender == 'string') {
261
+ rendered = rendered + childRender;
260
262
  } else {
261
- renderArray = renderArray || [];
263
+ if (!renderArray) {
264
+ renderArray = [];
265
+ }
262
266
 
263
267
  if (rendered) renderArray.push(rendered);
264
268
 
265
- rendered = '';
269
+ rendered = EMPTY_STR;
266
270
 
267
- if (Array.isArray(childRender)) {
271
+ if (isArray(childRender)) {
268
272
  renderArray.push(...childRender);
269
273
  } else {
270
274
  renderArray.push(childRender);
@@ -281,26 +285,26 @@ function _renderToString(
281
285
  }
282
286
 
283
287
  // VNodes have {constructor:undefined} to prevent JSON injection:
284
- if (vnode.constructor !== undefined) return '';
288
+ if (vnode.constructor !== undefined) return EMPTY_STR;
285
289
 
286
290
  vnode[PARENT] = parent;
287
291
  if (beforeDiff) beforeDiff(vnode);
288
292
 
289
293
  let type = vnode.type,
290
- props = vnode.props,
291
- cctx = context,
292
- contextType,
293
- rendered,
294
- component;
294
+ props = vnode.props;
295
295
 
296
296
  // Invoke rendering on Components
297
- if (typeof type === 'function') {
297
+ if (typeof type == 'function') {
298
+ let cctx = context,
299
+ contextType,
300
+ rendered,
301
+ component;
298
302
  if (type === Fragment) {
299
303
  // Serialized precompiled JSX.
300
- if (props.tpl) {
301
- let out = '';
304
+ if ('tpl' in props) {
305
+ let out = EMPTY_STR;
302
306
  for (let i = 0; i < props.tpl.length; i++) {
303
- out += props.tpl[i];
307
+ out = out + props.tpl[i];
304
308
 
305
309
  if (props.exprs && i < props.exprs.length) {
306
310
  const value = props.exprs[i];
@@ -308,30 +312,32 @@ function _renderToString(
308
312
 
309
313
  // Check if we're dealing with a vnode or an array of nodes
310
314
  if (
311
- typeof value === 'object' &&
315
+ typeof value == 'object' &&
312
316
  (value.constructor === undefined || isArray(value))
313
317
  ) {
314
- out += _renderToString(
315
- value,
316
- context,
317
- isSvgMode,
318
- selectValue,
319
- vnode,
320
- asyncMode,
321
- renderer
322
- );
318
+ out =
319
+ out +
320
+ _renderToString(
321
+ value,
322
+ context,
323
+ isSvgMode,
324
+ selectValue,
325
+ vnode,
326
+ asyncMode,
327
+ renderer
328
+ );
323
329
  } else {
324
330
  // Values are pre-escaped by the JSX transform
325
- out += value;
331
+ out = out + value;
326
332
  }
327
333
  }
328
334
  }
329
335
 
330
336
  return out;
331
- } else if (props.UNSTABLE_comment) {
337
+ } else if ('UNSTABLE_comment' in props) {
332
338
  // Fragments are the least used components of core that's why
333
339
  // branching here for comments has the least effect on perf.
334
- return '<!--' + encodeEntities(props.UNSTABLE_comment || '') + '-->';
340
+ return '<!--' + encodeEntities(props.UNSTABLE_comment) + '-->';
335
341
  }
336
342
 
337
343
  rendered = props.children;
@@ -342,22 +348,16 @@ function _renderToString(
342
348
  cctx = provider ? provider.props.value : contextType.__;
343
349
  }
344
350
 
345
- if (type.prototype && typeof type.prototype.render === 'function') {
351
+ let isClassComponent =
352
+ type.prototype && typeof type.prototype.render == 'function';
353
+ if (isClassComponent) {
346
354
  rendered = /**#__NOINLINE__**/ renderClassComponent(vnode, cctx);
347
355
  component = vnode[COMPONENT];
348
356
  } else {
349
- component = {
350
- __v: vnode,
351
- props,
352
- context: cctx,
353
- // silently drop state updates
354
- setState: markAsDirty,
355
- forceUpdate: markAsDirty,
356
- __d: true,
357
- // hooks
358
- __h: []
359
- };
360
- vnode[COMPONENT] = component;
357
+ vnode[COMPONENT] = component = /**#__NOINLINE__**/ createComponent(
358
+ vnode,
359
+ cctx
360
+ );
361
361
 
362
362
  // If a hook invokes setState() to invalidate the component during rendering,
363
363
  // re-render it up to 25 times to allow "settling" of memoized states.
@@ -380,20 +380,21 @@ function _renderToString(
380
380
  }
381
381
 
382
382
  if (
383
- (type.getDerivedStateFromError || component.componentDidCatch) &&
384
- options.errorBoundaries
383
+ isClassComponent &&
384
+ options.errorBoundaries &&
385
+ (type.getDerivedStateFromError || component.componentDidCatch)
385
386
  ) {
386
- let str = '';
387
387
  // When a component returns a Fragment node we flatten it in core, so we
388
388
  // need to mirror that logic here too
389
389
  let isTopLevelFragment =
390
390
  rendered != null &&
391
391
  rendered.type === Fragment &&
392
- rendered.key == null;
392
+ rendered.key == null &&
393
+ rendered.props.tpl == null;
393
394
  rendered = isTopLevelFragment ? rendered.props.children : rendered;
394
395
 
395
396
  try {
396
- str = _renderToString(
397
+ return _renderToString(
397
398
  rendered,
398
399
  context,
399
400
  isSvgMode,
@@ -402,14 +403,13 @@ function _renderToString(
402
403
  asyncMode,
403
404
  renderer
404
405
  );
405
- return str;
406
406
  } catch (err) {
407
407
  if (type.getDerivedStateFromError) {
408
408
  component[NEXT_STATE] = type.getDerivedStateFromError(err);
409
409
  }
410
410
 
411
411
  if (component.componentDidCatch) {
412
- component.componentDidCatch(err, {});
412
+ component.componentDidCatch(err, EMPTY_OBJ);
413
413
  }
414
414
 
415
415
  if (component[DIRTY]) {
@@ -423,10 +423,11 @@ function _renderToString(
423
423
  let isTopLevelFragment =
424
424
  rendered != null &&
425
425
  rendered.type === Fragment &&
426
- rendered.key == null;
426
+ rendered.key == null &&
427
+ rendered.props.tpl == null;
427
428
  rendered = isTopLevelFragment ? rendered.props.children : rendered;
428
429
 
429
- str = _renderToString(
430
+ return _renderToString(
430
431
  rendered,
431
432
  context,
432
433
  isSvgMode,
@@ -437,7 +438,7 @@ function _renderToString(
437
438
  );
438
439
  }
439
440
 
440
- return str;
441
+ return EMPTY_STR;
441
442
  } finally {
442
443
  if (afterDiff) afterDiff(vnode);
443
444
  vnode[PARENT] = null;
@@ -493,12 +494,12 @@ function _renderToString(
493
494
 
494
495
  let errorHook = options[CATCH_ERROR];
495
496
  if (errorHook) errorHook(error, vnode);
496
- return '';
497
+ return EMPTY_STR;
497
498
  }
498
499
 
499
500
  if (!asyncMode) throw error;
500
501
 
501
- if (!error || typeof error.then !== 'function') throw error;
502
+ if (!error || typeof error.then != 'function') throw error;
502
503
 
503
504
  const renderNestedChildren = () => {
504
505
  try {
@@ -512,7 +513,7 @@ function _renderToString(
512
513
  renderer
513
514
  );
514
515
  } catch (e) {
515
- if (!e || typeof e.then !== 'function') throw e;
516
+ if (!e || typeof e.then != 'function') throw e;
516
517
 
517
518
  return e.then(
518
519
  () =>
@@ -525,23 +526,25 @@ function _renderToString(
525
526
  asyncMode,
526
527
  renderer
527
528
  ),
528
- () => renderNestedChildren()
529
+ renderNestedChildren
529
530
  );
530
531
  }
531
532
  };
532
533
 
533
- return error.then(() => renderNestedChildren());
534
+ return error.then(renderNestedChildren);
534
535
  }
535
536
  }
536
537
 
537
538
  // Serialize Element VNodes to HTML
538
539
  let s = '<' + type,
539
- html = '',
540
+ html = EMPTY_STR,
540
541
  children;
541
542
 
542
543
  for (let name in props) {
543
544
  let v = props[name];
544
545
 
546
+ if (typeof v == 'function') continue;
547
+
545
548
  switch (name) {
546
549
  case 'children':
547
550
  children = v;
@@ -622,7 +625,7 @@ function _renderToString(
622
625
  // serialize boolean aria-xyz or draggable attribute values as strings
623
626
  // `draggable` is an enumerated attribute and not Boolean. A value of `true` or `false` is mandatory
624
627
  // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable
625
- v += '';
628
+ v = v + EMPTY_STR;
626
629
  } else if (isSvgMode) {
627
630
  if (SVG_CAMEL_CASE.test(name)) {
628
631
  name =
@@ -637,11 +640,17 @@ function _renderToString(
637
640
  }
638
641
 
639
642
  // write this attribute to the buffer
640
- if (v != null && v !== false && typeof v !== 'function') {
641
- if (v === true || v === '') {
643
+ if (v != null && v !== false) {
644
+ if (v === true || v === EMPTY_STR) {
642
645
  s = s + ' ' + name;
643
646
  } else {
644
- s = s + ' ' + name + '="' + encodeEntities(v + '') + '"';
647
+ s =
648
+ s +
649
+ ' ' +
650
+ name +
651
+ '="' +
652
+ (typeof v == 'string' ? encodeEntities(v) : v + EMPTY_STR) +
653
+ '"';
645
654
  }
646
655
  }
647
656
  }
@@ -687,8 +696,8 @@ function _renderToString(
687
696
  const endTag = '</' + type + '>';
688
697
  const startTag = s + '>';
689
698
 
690
- if (Array.isArray(html)) return [startTag, ...html, endTag];
691
- else if (typeof html !== 'string') return [startTag, html, endTag];
699
+ if (isArray(html)) return [startTag, ...html, endTag];
700
+ else if (typeof html != 'string') return [startTag, html, endTag];
692
701
  return startTag + html + endTag;
693
702
  }
694
703
 
package/src/lib/util.js CHANGED
@@ -33,12 +33,12 @@ export function encodeEntities(str) {
33
33
  continue;
34
34
  }
35
35
  // Append skipped/buffered characters and the encoded entity:
36
- if (i !== last) out += str.slice(last, i);
37
- out += ch;
36
+ if (i !== last) out = out + str.slice(last, i);
37
+ out = out + ch;
38
38
  // Start the next seek/buffer after the entity's offset:
39
39
  last = i + 1;
40
40
  }
41
- if (i !== last) out += str.slice(last, i);
41
+ if (i !== last) out = out + str.slice(last, i);
42
42
  return out;
43
43
  }
44
44
 
@@ -147,7 +147,7 @@ export function createComponent(vnode, context) {
147
147
  forceUpdate: markAsDirty,
148
148
  __d: true,
149
149
  // hooks
150
- __h: []
150
+ __h: new Array(0)
151
151
  };
152
152
  }
153
153