preact-render-to-string 6.5.7 → 6.5.9

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.
@@ -6,7 +6,7 @@
6
6
  const UNSAFE_NAME = /[\s\n\\/='"\0<>]/;
7
7
  const NAMESPACE_REPLACE_REGEX = /^(xlink|xmlns|xml)([A-Z])/;
8
8
  const HTML_LOWER_CASE = /^accessK|^auto[A-Z]|^cell|^ch|^col|cont|cross|dateT|encT|form[A-Z]|frame|hrefL|inputM|maxL|minL|noV|playsI|popoverT|readO|rowS|spellC|src[A-Z]|tabI|useM|item[A-Z]/;
9
- const SVG_CAMEL_CASE = /^ac|^ali|arabic|basel|cap|clipPath$|clipRule$|color|dominant|enable|fill|flood|font|glyph[^R]|horiz|image|letter|lighting|marker[^WUH]|overline|panose|pointe|paint|rendering|shape|stop|strikethrough|stroke|text[^L]|transform|underline|unicode|units|^v[^i]|^w|^xH/; // DOM properties that should NOT have "px" added when numeric
9
+ const SVG_CAMEL_CASE = /^ac|^ali|arabic|basel|cap|clipPath$|clipRule$|color|dominant|enable|fill|flood|font|glyph[^R]|horiz|image|letter|lighting|marker[^WUH]|overline|panose|pointe|paint|rendering|shape|stop|strikethrough|stroke|spel|text[^L]|transform|underline|unicode|units|^v[^i]|^w|^xH/; // DOM properties that should NOT have "px" added when numeric
10
10
 
11
11
  const ENCODED_ENTITIES = /["&<]/;
12
12
  /** @param {string} str */
@@ -38,13 +38,13 @@
38
38
  } // Append skipped/buffered characters and the encoded entity:
39
39
 
40
40
 
41
- if (i !== last) out += str.slice(last, i);
42
- out += ch; // Start the next seek/buffer after the entity's offset:
41
+ if (i !== last) out = out + str.slice(last, i);
42
+ out = out + ch; // Start the next seek/buffer after the entity's offset:
43
43
 
44
44
  last = i + 1;
45
45
  }
46
46
 
47
- if (i !== last) out += str.slice(last, i);
47
+ if (i !== last) out = out + str.slice(last, i);
48
48
  return out;
49
49
  }
50
50
  const JS_TO_CSS = {};
@@ -72,6 +72,24 @@
72
72
 
73
73
  return str || undefined;
74
74
  }
75
+
76
+ function markAsDirty() {
77
+ this.__d = true;
78
+ }
79
+
80
+ function createComponent(vnode, context) {
81
+ return {
82
+ __v: vnode,
83
+ context,
84
+ props: vnode.props,
85
+ // silently drop state updates
86
+ setState: markAsDirty,
87
+ forceUpdate: markAsDirty,
88
+ __d: true,
89
+ // hooks
90
+ __h: new Array(0)
91
+ };
92
+ } // Necessary for createContext api. Setting this property will pass
75
93
  /**
76
94
  * @template T
77
95
  */
@@ -106,9 +124,11 @@
106
124
  const NEXT_STATE = '__s';
107
125
  const CHILD_DID_SUSPEND = '__c';
108
126
 
127
+ const EMPTY_OBJ = {};
109
128
  const EMPTY_ARR = [];
110
129
  const isArray = Array.isArray;
111
- const assign = Object.assign; // Global state for the current render pass
130
+ const assign = Object.assign;
131
+ const EMPTY_STR = ''; // Global state for the current render pass
112
132
 
113
133
  let beforeDiff, afterDiff, renderHook, ummountHook;
114
134
  /**
@@ -138,8 +158,8 @@
138
158
  try {
139
159
  const rendered = _renderToString(vnode, context || EMPTY_OBJ, false, undefined, parent, false, _rendererState);
140
160
 
141
- if (Array.isArray(rendered)) {
142
- return rendered.join('');
161
+ if (isArray(rendered)) {
162
+ return rendered.join(EMPTY_STR);
143
163
  }
144
164
 
145
165
  return rendered;
@@ -157,12 +177,6 @@
157
177
  EMPTY_ARR.length = 0;
158
178
  }
159
179
  }
160
-
161
- function markAsDirty() {
162
- this.__d = true;
163
- }
164
-
165
- const EMPTY_OBJ = {};
166
180
  /**
167
181
  * @param {VNode} vnode
168
182
  * @param {Record<string, unknown>} context
@@ -224,36 +238,40 @@
224
238
 
225
239
  function _renderToString(vnode, context, isSvgMode, selectValue, parent, asyncMode, renderer) {
226
240
  // Ignore non-rendered VNodes/values
227
- if (vnode == null || vnode === true || vnode === false || vnode === '') {
228
- return '';
229
- } // Text VNodes: escape as HTML
241
+ if (vnode == null || vnode === true || vnode === false || vnode === EMPTY_STR) {
242
+ return EMPTY_STR;
243
+ }
230
244
 
245
+ let vnodeType = typeof vnode; // Text VNodes: escape as HTML
231
246
 
232
- if (typeof vnode !== 'object') {
233
- if (typeof vnode === 'function') return '';
234
- return encodeEntities(vnode + '');
247
+ if (vnodeType != 'object') {
248
+ if (vnodeType == 'function') return EMPTY_STR;
249
+ return vnodeType == 'string' ? encodeEntities(vnode) : vnode + EMPTY_STR;
235
250
  } // Recurse into children / Arrays
236
251
 
237
252
 
238
253
  if (isArray(vnode)) {
239
- let rendered = '',
254
+ let rendered = EMPTY_STR,
240
255
  renderArray;
241
256
  parent[CHILDREN] = vnode;
242
257
 
243
258
  for (let i = 0; i < vnode.length; i++) {
244
259
  let child = vnode[i];
245
- if (child == null || typeof child === 'boolean') continue;
260
+ if (child == null || typeof child == 'boolean') continue;
246
261
 
247
262
  const childRender = _renderToString(child, context, isSvgMode, selectValue, parent, asyncMode, renderer);
248
263
 
249
- if (typeof childRender === 'string') {
250
- rendered += childRender;
264
+ if (typeof childRender == 'string') {
265
+ rendered = rendered + childRender;
251
266
  } else {
252
- renderArray = renderArray || [];
267
+ if (!renderArray) {
268
+ renderArray = [];
269
+ }
270
+
253
271
  if (rendered) renderArray.push(rendered);
254
- rendered = '';
272
+ rendered = EMPTY_STR;
255
273
 
256
- if (Array.isArray(childRender)) {
274
+ if (isArray(childRender)) {
257
275
  renderArray.push(...childRender);
258
276
  } else {
259
277
  renderArray.push(childRender);
@@ -270,43 +288,44 @@
270
288
  } // VNodes have {constructor:undefined} to prevent JSON injection:
271
289
 
272
290
 
273
- if (vnode.constructor !== undefined) return '';
291
+ if (vnode.constructor !== undefined) return EMPTY_STR;
274
292
  vnode[PARENT] = parent;
275
293
  if (beforeDiff) beforeDiff(vnode);
276
294
  let type = vnode.type,
277
- props = vnode.props,
278
- cctx = context,
279
- contextType,
280
- rendered,
281
- component; // Invoke rendering on Components
295
+ props = vnode.props; // Invoke rendering on Components
296
+
297
+ if (typeof type == 'function') {
298
+ let cctx = context,
299
+ contextType,
300
+ rendered,
301
+ component;
282
302
 
283
- if (typeof type === 'function') {
284
303
  if (type === preact.Fragment) {
285
304
  // Serialized precompiled JSX.
286
- if (props.tpl) {
287
- let out = '';
305
+ if ('tpl' in props) {
306
+ let out = EMPTY_STR;
288
307
 
289
308
  for (let i = 0; i < props.tpl.length; i++) {
290
- out += props.tpl[i];
309
+ out = out + props.tpl[i];
291
310
 
292
311
  if (props.exprs && i < props.exprs.length) {
293
312
  const value = props.exprs[i];
294
313
  if (value == null) continue; // Check if we're dealing with a vnode or an array of nodes
295
314
 
296
- if (typeof value === 'object' && (value.constructor === undefined || isArray(value))) {
297
- out += _renderToString(value, context, isSvgMode, selectValue, vnode, asyncMode, renderer);
315
+ if (typeof value == 'object' && (value.constructor === undefined || isArray(value))) {
316
+ out = out + _renderToString(value, context, isSvgMode, selectValue, vnode, asyncMode, renderer);
298
317
  } else {
299
318
  // Values are pre-escaped by the JSX transform
300
- out += value;
319
+ out = out + value;
301
320
  }
302
321
  }
303
322
  }
304
323
 
305
324
  return out;
306
- } else if (props.UNSTABLE_comment) {
325
+ } else if ('UNSTABLE_comment' in props) {
307
326
  // Fragments are the least used components of core that's why
308
327
  // branching here for comments has the least effect on perf.
309
- return '<!--' + encodeEntities(props.UNSTABLE_comment || '') + '-->';
328
+ return '<!--' + encodeEntities(props.UNSTABLE_comment) + '-->';
310
329
  }
311
330
 
312
331
  rendered = props.children;
@@ -318,24 +337,17 @@
318
337
  cctx = provider ? provider.props.value : contextType.__;
319
338
  }
320
339
 
321
- if (type.prototype && typeof type.prototype.render === 'function') {
340
+ let isClassComponent = type.prototype && typeof type.prototype.render == 'function';
341
+
342
+ if (isClassComponent) {
322
343
  rendered =
323
344
  /**#__NOINLINE__**/
324
345
  renderClassComponent(vnode, cctx);
325
346
  component = vnode[COMPONENT];
326
347
  } else {
327
- component = {
328
- __v: vnode,
329
- props,
330
- context: cctx,
331
- // silently drop state updates
332
- setState: markAsDirty,
333
- forceUpdate: markAsDirty,
334
- __d: true,
335
- // hooks
336
- __h: []
337
- };
338
- vnode[COMPONENT] = component; // If a hook invokes setState() to invalidate the component during rendering,
348
+ vnode[COMPONENT] = component =
349
+ /**#__NOINLINE__**/
350
+ createComponent(vnode, cctx); // If a hook invokes setState() to invalidate the component during rendering,
339
351
  // re-render it up to 25 times to allow "settling" of memoized states.
340
352
  // Note:
341
353
  // This will need to be updated for Preact 11 to use internal.flags rather than component._dirty:
@@ -356,23 +368,21 @@
356
368
  context = assign({}, context, component.getChildContext());
357
369
  }
358
370
 
359
- if ((type.getDerivedStateFromError || component.componentDidCatch) && preact.options.errorBoundaries) {
360
- let str = ''; // When a component returns a Fragment node we flatten it in core, so we
371
+ if (isClassComponent && preact.options.errorBoundaries && (type.getDerivedStateFromError || component.componentDidCatch)) {
372
+ // When a component returns a Fragment node we flatten it in core, so we
361
373
  // need to mirror that logic here too
362
-
363
- let isTopLevelFragment = rendered != null && rendered.type === preact.Fragment && rendered.key == null;
374
+ let isTopLevelFragment = rendered != null && rendered.type === preact.Fragment && rendered.key == null && rendered.props.tpl == null;
364
375
  rendered = isTopLevelFragment ? rendered.props.children : rendered;
365
376
 
366
377
  try {
367
- str = _renderToString(rendered, context, isSvgMode, selectValue, vnode, asyncMode, renderer);
368
- return str;
378
+ return _renderToString(rendered, context, isSvgMode, selectValue, vnode, asyncMode, renderer);
369
379
  } catch (err) {
370
380
  if (type.getDerivedStateFromError) {
371
381
  component[NEXT_STATE] = type.getDerivedStateFromError(err);
372
382
  }
373
383
 
374
384
  if (component.componentDidCatch) {
375
- component.componentDidCatch(err, {});
385
+ component.componentDidCatch(err, EMPTY_OBJ);
376
386
  }
377
387
 
378
388
  if (component[DIRTY]) {
@@ -383,12 +393,12 @@
383
393
  context = assign({}, context, component.getChildContext());
384
394
  }
385
395
 
386
- let isTopLevelFragment = rendered != null && rendered.type === preact.Fragment && rendered.key == null;
396
+ let isTopLevelFragment = rendered != null && rendered.type === preact.Fragment && rendered.key == null && rendered.props.tpl == null;
387
397
  rendered = isTopLevelFragment ? rendered.props.children : rendered;
388
- str = _renderToString(rendered, context, isSvgMode, selectValue, vnode, asyncMode, renderer);
398
+ return _renderToString(rendered, context, isSvgMode, selectValue, vnode, asyncMode, renderer);
389
399
  }
390
400
 
391
- return str;
401
+ return EMPTY_STR;
392
402
  } finally {
393
403
  if (afterDiff) afterDiff(vnode);
394
404
  vnode[PARENT] = null;
@@ -417,32 +427,33 @@
417
427
  if (res !== undefined) return res;
418
428
  let errorHook = preact.options[CATCH_ERROR];
419
429
  if (errorHook) errorHook(error, vnode);
420
- return '';
430
+ return EMPTY_STR;
421
431
  }
422
432
 
423
433
  if (!asyncMode) throw error;
424
- if (!error || typeof error.then !== 'function') throw error;
434
+ if (!error || typeof error.then != 'function') throw error;
425
435
 
426
436
  const renderNestedChildren = () => {
427
437
  try {
428
438
  return _renderToString(rendered, context, isSvgMode, selectValue, vnode, asyncMode, renderer);
429
439
  } catch (e) {
430
- if (!e || typeof e.then !== 'function') throw e;
431
- return e.then(() => _renderToString(rendered, context, isSvgMode, selectValue, vnode, asyncMode, renderer), () => renderNestedChildren());
440
+ if (!e || typeof e.then != 'function') throw e;
441
+ return e.then(() => _renderToString(rendered, context, isSvgMode, selectValue, vnode, asyncMode, renderer), renderNestedChildren);
432
442
  }
433
443
  };
434
444
 
435
- return error.then(() => renderNestedChildren());
445
+ return error.then(renderNestedChildren);
436
446
  }
437
447
  } // Serialize Element VNodes to HTML
438
448
 
439
449
 
440
450
  let s = '<' + type,
441
- html = '',
451
+ html = EMPTY_STR,
442
452
  children;
443
453
 
444
454
  for (let name in props) {
445
455
  let v = props[name];
456
+ if (typeof v == 'function') continue;
446
457
 
447
458
  switch (name) {
448
459
  case 'children':
@@ -533,23 +544,27 @@
533
544
  // serialize boolean aria-xyz or draggable attribute values as strings
534
545
  // `draggable` is an enumerated attribute and not Boolean. A value of `true` or `false` is mandatory
535
546
  // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable
536
- v += '';
547
+ v = v + EMPTY_STR;
537
548
  } else if (isSvgMode) {
538
549
  if (SVG_CAMEL_CASE.test(name)) {
539
550
  name = name === 'panose1' ? 'panose-1' : name.replace(/([A-Z])/g, '-$1').toLowerCase();
540
551
  }
541
552
  } else if (HTML_LOWER_CASE.test(name)) {
542
553
  name = name.toLowerCase();
554
+
555
+ if (name === 'spellcheck') {
556
+ v = '' + v;
557
+ }
543
558
  }
544
559
  }
545
560
  } // write this attribute to the buffer
546
561
 
547
562
 
548
- if (v != null && v !== false && typeof v !== 'function') {
549
- if (v === true || v === '') {
563
+ if (v != null && v !== false) {
564
+ if (v === true || v === EMPTY_STR) {
550
565
  s = s + ' ' + name;
551
566
  } else {
552
- s = s + ' ' + name + '="' + encodeEntities(v + '') + '"';
567
+ s = s + ' ' + name + '="' + (typeof v == 'string' ? encodeEntities(v) : v + EMPTY_STR) + '"';
553
568
  }
554
569
  }
555
570
  }
@@ -580,7 +595,7 @@
580
595
 
581
596
  const endTag = '</' + type + '>';
582
597
  const startTag = s + '>';
583
- if (Array.isArray(html)) return [startTag, ...html, endTag];else if (typeof html !== 'string') return [startTag, html, endTag];
598
+ if (isArray(html)) return [startTag, ...html, endTag];else if (typeof html != 'string') return [startTag, html, endTag];
584
599
  return startTag + html + endTag;
585
600
  }
586
601