preact-render-to-string 6.5.13 → 6.6.0

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
@@ -6,7 +6,10 @@ import {
6
6
  HTML_LOWER_CASE,
7
7
  HTML_ENUMERATED,
8
8
  SVG_CAMEL_CASE,
9
- createComponent
9
+ createComponent,
10
+ setDirty,
11
+ unsetDirty,
12
+ isDirty
10
13
  } from './lib/util.js';
11
14
  import { options, h, Fragment } from 'preact';
12
15
  import {
@@ -15,7 +18,6 @@ import {
15
18
  COMPONENT,
16
19
  DIFF,
17
20
  DIFFED,
18
- DIRTY,
19
21
  NEXT_STATE,
20
22
  PARENT,
21
23
  RENDER,
@@ -29,6 +31,8 @@ const EMPTY_ARR = [];
29
31
  const isArray = Array.isArray;
30
32
  const assign = Object.assign;
31
33
  const EMPTY_STR = '';
34
+ const BEGIN_SUSPENSE_DENOMINATOR = '<!--$s-->';
35
+ const END_SUSPENSE_DENOMINATOR = '<!--/$s-->';
32
36
 
33
37
  // Global state for the current render pass
34
38
  let beforeDiff, afterDiff, renderHook, ummountHook;
@@ -172,8 +176,9 @@ function renderClassComponent(vnode, context) {
172
176
 
173
177
  c.props = vnode.props;
174
178
  c.context = context;
175
- // turn off stateful re-rendering:
176
- c[DIRTY] = true;
179
+
180
+ // Turn off stateful rendering
181
+ setDirty(c);
177
182
 
178
183
  if (c.state == null) c.state = EMPTY_OBJ;
179
184
 
@@ -263,6 +268,7 @@ function _renderToString(
263
268
  rendered = rendered + childRender;
264
269
  } else {
265
270
  if (!renderArray) {
271
+ // oxlint-disable-next-line no-new-array
266
272
  renderArray = new Array(vnodeLength);
267
273
  }
268
274
 
@@ -367,14 +373,22 @@ function _renderToString(
367
373
  // This will need to be updated for Preact 11 to use internal.flags rather than component._dirty:
368
374
  // https://github.com/preactjs/preact/blob/d4ca6fdb19bc715e49fd144e69f7296b2f4daa40/src/diff/component.js#L35-L44
369
375
  let count = 0;
370
- while (component[DIRTY] && count++ < 25) {
371
- component[DIRTY] = false;
376
+ while (isDirty(component) && count++ < 25) {
377
+ unsetDirty(component);
372
378
 
373
379
  if (renderHook) renderHook(vnode);
374
380
 
375
- rendered = type.call(component, props, cctx);
381
+ try {
382
+ rendered = type.call(component, props, cctx);
383
+ } catch (e) {
384
+ if (asyncMode) {
385
+ vnode._suspended = true;
386
+ }
387
+ throw e;
388
+ }
376
389
  }
377
- component[DIRTY] = true;
390
+
391
+ setDirty(component);
378
392
  }
379
393
 
380
394
  if (component.getChildContext != null) {
@@ -403,6 +417,7 @@ function _renderToString(
403
417
  selectValue,
404
418
  vnode,
405
419
  asyncMode,
420
+ false,
406
421
  renderer
407
422
  );
408
423
  } catch (err) {
@@ -414,7 +429,7 @@ function _renderToString(
414
429
  component.componentDidCatch(err, EMPTY_OBJ);
415
430
  }
416
431
 
417
- if (component[DIRTY]) {
432
+ if (isDirty(component)) {
418
433
  rendered = renderClassComponent(vnode, context);
419
434
  component = vnode[COMPONENT];
420
435
 
@@ -475,6 +490,21 @@ function _renderToString(
475
490
 
476
491
  if (options.unmount) options.unmount(vnode);
477
492
 
493
+ if (vnode._suspended) {
494
+ if (typeof str === 'string') {
495
+ return BEGIN_SUSPENSE_DENOMINATOR + str + END_SUSPENSE_DENOMINATOR;
496
+ } else if (isArray(str)) {
497
+ str.unshift(BEGIN_SUSPENSE_DENOMINATOR);
498
+ str.push(END_SUSPENSE_DENOMINATOR);
499
+ return str;
500
+ }
501
+
502
+ return str.then(
503
+ (resolved) =>
504
+ BEGIN_SUSPENSE_DENOMINATOR + resolved + END_SUSPENSE_DENOMINATOR
505
+ );
506
+ }
507
+
478
508
  return str;
479
509
  } catch (error) {
480
510
  if (!asyncMode && renderer && renderer.onError) {
@@ -503,7 +533,7 @@ function _renderToString(
503
533
 
504
534
  const renderNestedChildren = () => {
505
535
  try {
506
- return _renderToString(
536
+ const result = _renderToString(
507
537
  rendered,
508
538
  context,
509
539
  isSvgMode,
@@ -512,22 +542,13 @@ function _renderToString(
512
542
  asyncMode,
513
543
  renderer
514
544
  );
545
+ return vnode._suspended
546
+ ? BEGIN_SUSPENSE_DENOMINATOR + result + END_SUSPENSE_DENOMINATOR
547
+ : result;
515
548
  } catch (e) {
516
549
  if (!e || typeof e.then != 'function') throw e;
517
550
 
518
- return e.then(
519
- () =>
520
- _renderToString(
521
- rendered,
522
- context,
523
- isSvgMode,
524
- selectValue,
525
- vnode,
526
- asyncMode,
527
- renderer
528
- ),
529
- renderNestedChildren
530
- );
551
+ return e.then(renderNestedChildren);
531
552
  }
532
553
  };
533
554
 
@@ -542,6 +563,7 @@ function _renderToString(
542
563
 
543
564
  for (let name in props) {
544
565
  let v = props[name];
566
+ v = isSignal(v) ? v.value : v;
545
567
 
546
568
  if (typeof v == 'function' && name !== 'class' && name !== 'className') {
547
569
  continue;
@@ -723,3 +745,12 @@ const SELF_CLOSING = new Set([
723
745
  export default renderToString;
724
746
  export const render = renderToString;
725
747
  export const renderToStaticMarkup = renderToString;
748
+
749
+ function isSignal(x) {
750
+ return (
751
+ x !== null &&
752
+ typeof x === 'object' &&
753
+ typeof x.peek === 'function' &&
754
+ 'value' in x
755
+ );
756
+ }
package/src/jsx.js CHANGED
@@ -14,7 +14,7 @@ let preactPlugin = {
14
14
  'key' in object
15
15
  );
16
16
  },
17
- print(val, print, indent) {
17
+ print(val) {
18
18
  return renderToString(val, preactPlugin.context, preactPlugin.opts, true);
19
19
  }
20
20
  };
@@ -15,5 +15,6 @@ export const MASK = '__m';
15
15
  // Component properties
16
16
  export const VNODE = '__v';
17
17
  export const DIRTY = '__d';
18
+ export const BITS = '__g';
18
19
  export const NEXT_STATE = '__s';
19
20
  export const CHILD_DID_SUSPEND = '__c';
@@ -1,6 +1,6 @@
1
1
  if (typeof Symbol !== 'function') {
2
2
  let c = 0;
3
- // eslint-disable-next-line
3
+ // oxlint-disable-next-line no-global-assign
4
4
  Symbol = function (s) {
5
5
  return `@@${s}${++c}`;
6
6
  };
package/src/lib/util.js CHANGED
@@ -1,4 +1,7 @@
1
+ import { DIRTY, BITS } from './constants';
2
+
1
3
  export const VOID_ELEMENTS = /^(?:area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/;
4
+ // oxlint-disable-next-line no-control-regex
2
5
  export const UNSAFE_NAME = /[\s\n\\/='"\0<>]/;
3
6
  export const NAMESPACE_REPLACE_REGEX = /^(xlink|xmlns|xml)([A-Z])/;
4
7
  export 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|src[A-Z]|tabI|useM|item[A-Z])/;
@@ -7,6 +10,30 @@ export const SVG_CAMEL_CASE = /^ac|^ali|arabic|basel|cap|clipPath$|clipRule$|col
7
10
  // Boolean DOM properties that translate to enumerated ('true'/'false') attributes
8
11
  export const HTML_ENUMERATED = new Set(['draggable', 'spellcheck']);
9
12
 
13
+ export const COMPONENT_DIRTY_BIT = 1 << 3;
14
+ export function setDirty(component) {
15
+ if (component[BITS] !== undefined) {
16
+ component[BITS] |= COMPONENT_DIRTY_BIT;
17
+ } else {
18
+ component[DIRTY] = true;
19
+ }
20
+ }
21
+
22
+ export function unsetDirty(component) {
23
+ if (component.__g !== undefined) {
24
+ component.__g &= ~COMPONENT_DIRTY_BIT;
25
+ } else {
26
+ component[DIRTY] = false;
27
+ }
28
+ }
29
+
30
+ export function isDirty(component) {
31
+ if (component.__g !== undefined) {
32
+ return (component.__g & COMPONENT_DIRTY_BIT) !== 0;
33
+ }
34
+ return component[DIRTY] === true;
35
+ }
36
+
10
37
  // DOM properties that should NOT have "px" added when numeric
11
38
  const ENCODED_ENTITIES = /["&<]/;
12
39
 
@@ -150,6 +177,7 @@ export function createComponent(vnode, context) {
150
177
  forceUpdate: markAsDirty,
151
178
  __d: true,
152
179
  // hooks
180
+ // oxlint-disable-next-line no-new-array
153
181
  __h: new Array(0)
154
182
  };
155
183
  }
@@ -171,7 +199,6 @@ export function getContext(nodeName, context) {
171
199
  */
172
200
  export class Deferred {
173
201
  constructor() {
174
- // eslint-disable-next-line lines-around-comment
175
202
  /** @type {Promise<T>} */
176
203
  this.promise = new Promise((resolve, reject) => {
177
204
  this.resolve = resolve;
package/src/pretty.js CHANGED
@@ -10,7 +10,10 @@ import {
10
10
  NAMESPACE_REPLACE_REGEX,
11
11
  SVG_CAMEL_CASE,
12
12
  HTML_LOWER_CASE,
13
- getContext
13
+ getContext,
14
+ setDirty,
15
+ isDirty,
16
+ unsetDirty
14
17
  } from './lib/util.js';
15
18
  import { COMMIT, DIFF, DIFFED, RENDER, SKIP_EFFECTS } from './lib/constants.js';
16
19
  import { options, Fragment } from 'preact';
@@ -139,8 +142,8 @@ function _renderToStringPretty(
139
142
  // This will need to be updated for Preact 11 to use internal.flags rather than component._dirty:
140
143
  // https://github.com/preactjs/preact/blob/d4ca6fdb19bc715e49fd144e69f7296b2f4daa40/src/diff/component.js#L35-L44
141
144
  let count = 0;
142
- while (c.__d && count++ < 25) {
143
- c.__d = false;
145
+ while (isDirty(c) && count++ < 25) {
146
+ unsetDirty(c);
144
147
 
145
148
  if (renderHook) renderHook(vnode);
146
149
 
@@ -154,7 +157,7 @@ function _renderToStringPretty(
154
157
  c = vnode.__c = new nodeName(props, cctx);
155
158
  c.__v = vnode;
156
159
  // turn off stateful re-rendering:
157
- c._dirty = c.__d = true;
160
+ setDirty(c);
158
161
  c.props = props;
159
162
  if (c.state == null) c.state = {};
160
163
 
@@ -60,7 +60,14 @@ export function renderToPipeableStream(vnode, options, context) {
60
60
  /**
61
61
  * @param {unknown} [reason]
62
62
  */
63
- abort(reason = new Error('The render was aborted by the server without a reason.')) {
63
+ abort(
64
+ reason = new Error(
65
+ 'The render was aborted by the server without a reason.'
66
+ )
67
+ ) {
68
+ // Remix/React-Router will always call abort after a timeout, even on success
69
+ if (stream.closed) return;
70
+
64
71
  controller.abort();
65
72
  stream.destroy();
66
73
  if (options.onError) {