preact-render-to-string 6.4.2 → 6.5.1

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.
Files changed (43) hide show
  1. package/dist/commonjs.js +1 -1
  2. package/dist/commonjs.js.map +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +1 -1
  5. package/dist/index.module.js +1 -1
  6. package/dist/index.module.js.map +1 -1
  7. package/dist/index.umd.js +1 -1
  8. package/dist/index.umd.js.map +1 -1
  9. package/dist/internal.d.ts +35 -0
  10. package/dist/jsx-entry.js +1 -1
  11. package/dist/jsx-entry.js.map +1 -1
  12. package/dist/jsx.js.map +1 -1
  13. package/dist/jsx.mjs +1 -1
  14. package/dist/jsx.mjs.map +1 -1
  15. package/dist/jsx.module.js +1 -1
  16. package/dist/jsx.module.js.map +1 -1
  17. package/dist/jsx.umd.js +1 -1
  18. package/dist/jsx.umd.js.map +1 -1
  19. package/dist/stream-node.js +786 -0
  20. package/dist/stream-node.js.map +1 -0
  21. package/dist/stream-node.module.js +786 -0
  22. package/dist/stream-node.module.js.map +1 -0
  23. package/dist/stream-node.umd.js +790 -0
  24. package/dist/stream-node.umd.js.map +1 -0
  25. package/dist/stream.js +2 -0
  26. package/dist/stream.js.map +1 -0
  27. package/dist/stream.module.js +2 -0
  28. package/dist/stream.module.js.map +1 -0
  29. package/dist/stream.umd.js +2 -0
  30. package/dist/stream.umd.js.map +1 -0
  31. package/jsx.d.ts +1 -1
  32. package/package.json +172 -157
  33. package/src/index.js +54 -18
  34. package/src/internal.d.ts +35 -0
  35. package/src/jsx.js +2 -4
  36. package/src/lib/chunked.js +97 -0
  37. package/src/lib/client.js +61 -0
  38. package/src/{constants.js → lib/constants.js} +3 -0
  39. package/src/{util.js → lib/util.js} +13 -1
  40. package/src/pretty.js +13 -13
  41. package/src/stream-node.js +60 -0
  42. package/src/stream.js +43 -0
  43. /package/src/{polyfills.js → lib/polyfills.js} +0 -0
package/package.json CHANGED
@@ -1,159 +1,174 @@
1
1
  {
2
- "name": "preact-render-to-string",
3
- "amdName": "preactRenderToString",
4
- "version": "6.4.2",
5
- "description": "Render JSX to an HTML string, with support for Preact components.",
6
- "main": "dist/index.js",
7
- "umd:main": "dist/index.umd.js",
8
- "module": "dist/index.module.js",
9
- "jsnext:main": "dist/index.module.js",
10
- "types": "src/index.d.ts",
11
- "exports": {
12
- ".": {
13
- "types": "./src/index.d.ts",
14
- "browser": "./dist/index.module.js",
15
- "umd": "./dist/index.umd.js",
16
- "import": "./dist/index.mjs",
17
- "require": "./dist/index.js"
18
- },
19
- "./jsx": {
20
- "types": "./jsx.d.ts",
21
- "browser": "./dist/jsx.module.js",
22
- "umd": "./dist/jsx.umd.js",
23
- "import": "./dist/jsx.mjs",
24
- "require": "./dist/jsx.js"
25
- },
26
- "./package.json": "./package.json"
27
- },
28
- "scripts": {
29
- "bench": "BABEL_ENV=test node -r @babel/register benchmarks index.js",
30
- "bench:v8": "BABEL_ENV=test microbundle benchmarks/index.js -f modern --alias benchmarkjs-pretty=benchmarks/lib/benchmark-lite.js --external none --target node --no-compress --no-sourcemap --raw -o benchmarks/.v8.mjs && v8 --module benchmarks/.v8.mjs",
31
- "build": "npm run -s transpile && npm run -s transpile:jsx && npm run -s copy-typescript-definition",
32
- "postbuild": "node ./config/node-13-exports.js && node ./config/node-commonjs.js && node ./config/node-verify-exports.js",
33
- "transpile": "microbundle src/index.js -f es,cjs,umd --target web --external preact",
34
- "transpile:jsx": "microbundle src/jsx.js -o dist/jsx.js --target web --external preact && microbundle dist/jsx.js -o dist/jsx.js -f cjs --external preact",
35
- "copy-typescript-definition": "copyfiles -f src/*.d.ts dist",
36
- "test": "eslint src test && tsc && npm run test:mocha && npm run test:mocha:compat && npm run test:mocha:debug && npm run bench",
37
- "test:mocha": "BABEL_ENV=test mocha -r @babel/register -r test/setup.js test/*.test.js test/*.test.jsx",
38
- "test:mocha:compat": "BABEL_ENV=test mocha -r @babel/register -r test/setup.js 'test/compat/*.test.js' 'test/compat/*.test.jsx'",
39
- "test:mocha:debug": "BABEL_ENV=test mocha -r @babel/register -r test/setup.js 'test/debug/index.test.js' 'test/debug/index.test.jsx'",
40
- "format": "prettier src/**/*.{d.ts,js,jsx} test/**/*.js test/**/*.jsx --write",
41
- "prepublishOnly": "npm run build",
42
- "release": "npm run build && git commit -am $npm_package_version && git tag $npm_package_version && git push && git push --tags && npm publish"
43
- },
44
- "keywords": [
45
- "preact",
46
- "render",
47
- "universal",
48
- "isomorphic"
49
- ],
50
- "files": [
51
- "src",
52
- "dist",
53
- "jsx.js",
54
- "jsx.d.ts",
55
- "typings.json"
56
- ],
57
- "eslintConfig": {
58
- "extends": "developit",
59
- "rules": {
60
- "react/prefer-stateless-function": 0,
61
- "react/jsx-no-bind": 0,
62
- "react/no-danger": 0,
63
- "jest/valid-expect": 0,
64
- "new-cap": 0,
65
- "curly": "off",
66
- "brace-style": "off",
67
- "indent": "off"
68
- },
69
- "settings": {
70
- "react": {
71
- "version": "16.8"
72
- }
73
- }
74
- },
75
- "babel": {
76
- "env": {
77
- "test": {
78
- "presets": [
79
- [
80
- "@babel/preset-env",
81
- {
82
- "targets": {
83
- "node": true
84
- }
85
- }
86
- ]
87
- ],
88
- "plugins": [
89
- [
90
- "@babel/plugin-transform-react-jsx",
91
- {
92
- "pragma": "h"
93
- }
94
- ]
95
- ]
96
- }
97
- }
98
- },
99
- "minify": {
100
- "compress": {
101
- "reduce_funcs": false
102
- }
103
- },
104
- "author": "Jason Miller <jason@developit.ca>",
105
- "license": "MIT",
106
- "repository": {
107
- "type": "git",
108
- "url": "https://github.com/preactjs/preact-render-to-string"
109
- },
110
- "bugs": "https://github.com/developit/preact-render-to-string/issues",
111
- "homepage": "https://github.com/developit/preact-render-to-string",
112
- "peerDependencies": {
113
- "preact": ">=10"
114
- },
115
- "devDependencies": {
116
- "@babel/plugin-transform-react-jsx": "^7.22.15",
117
- "@babel/preset-env": "^7.23.2",
118
- "@babel/register": "^7.22.15",
119
- "@changesets/changelog-github": "^0.4.1",
120
- "@changesets/cli": "^2.18.0",
121
- "benchmarkjs-pretty": "^2.0.1",
122
- "chai": "^4.3.10",
123
- "copyfiles": "^2.4.1",
124
- "eslint": "^7.16.0",
125
- "eslint-config-developit": "^1.2.0",
126
- "husky": "^4.3.6",
127
- "lint-staged": "^10.5.3",
128
- "microbundle": "^0.15.1",
129
- "mocha": "^10.2.0",
130
- "baseline-rts": "npm:preact-render-to-string@latest",
131
- "preact": "^10.13.0",
132
- "prettier": "^2.2.1",
133
- "sinon": "^17.0.1",
134
- "sinon-chai": "^3.7.0",
135
- "typescript": "^5.0.0"
136
- },
137
- "dependencies": {
138
- "pretty-format": "^3.8.0"
139
- },
140
- "prettier": {
141
- "singleQuote": true,
142
- "trailingComma": "none",
143
- "useTabs": true,
144
- "tabWidth": 2
145
- },
146
- "lint-staged": {
147
- "**/*.{js,jsx,ts,tsx,yml}": [
148
- "prettier --write"
149
- ]
150
- },
151
- "husky": {
152
- "hooks": {
153
- "pre-commit": "lint-staged"
154
- }
155
- },
156
- "publishConfig": {
157
- "provenance": true
158
- }
2
+ "name": "preact-render-to-string",
3
+ "amdName": "preactRenderToString",
4
+ "version": "6.5.1",
5
+ "description": "Render JSX to an HTML string, with support for Preact components.",
6
+ "main": "dist/index.js",
7
+ "umd:main": "dist/index.umd.js",
8
+ "module": "dist/index.module.js",
9
+ "jsnext:main": "dist/index.module.js",
10
+ "types": "src/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./src/index.d.ts",
14
+ "browser": "./dist/index.module.js",
15
+ "umd": "./dist/index.umd.js",
16
+ "import": "./dist/index.mjs",
17
+ "require": "./dist/index.js"
18
+ },
19
+ "./jsx": {
20
+ "types": "./jsx.d.ts",
21
+ "browser": "./dist/jsx.module.js",
22
+ "umd": "./dist/jsx.umd.js",
23
+ "import": "./dist/jsx.mjs",
24
+ "require": "./dist/jsx.js"
25
+ },
26
+ "./stream": {
27
+ "types": "./stream.d.ts",
28
+ "import": "./dist/stream.mjs",
29
+ "browser": "./dist/stream.module.js",
30
+ "require": "./dist/stream.js"
31
+ },
32
+ "./stream-node": {
33
+ "types": "./stream-node.d.ts",
34
+ "import": "./dist/stream-node.mjs",
35
+ "browser": "./dist/stream-node.module.js",
36
+ "require": "./dist/stream-node.js"
37
+ },
38
+ "./package.json": "./package.json"
39
+ },
40
+ "scripts": {
41
+ "bench": "BABEL_ENV=test node -r @babel/register benchmarks index.js",
42
+ "bench:v8": "BABEL_ENV=test microbundle benchmarks/index.js -f modern --alias benchmarkjs-pretty=benchmarks/lib/benchmark-lite.js --external none --target node --no-compress --no-sourcemap --raw -o benchmarks/.v8.mjs && v8 --module benchmarks/.v8.modern.js",
43
+ "build": "npm run -s transpile && npm run -s transpile:jsx && npm run -s transpile:stream && npm run -s copy-typescript-definition",
44
+ "postbuild": "node ./config/node-13-exports.js && node ./config/node-commonjs.js && node ./config/node-verify-exports.js",
45
+ "transpile": "microbundle src/index.js -f es,cjs,umd --target web --external preact",
46
+ "transpile:stream": "microbundle src/stream.js -o dist/stream.js -f es,cjs,umd --external preact && microbundle src/stream-node.js -o dist/stream-node.js -f es,cjs,umd --target node --external preact",
47
+ "transpile:jsx": "microbundle src/jsx.js -o dist/jsx.js --target web --external preact && microbundle dist/jsx.js -o dist/jsx.js -f cjs --external preact",
48
+ "copy-typescript-definition": "copyfiles -f src/*.d.ts dist",
49
+ "test": "eslint src test && tsc && npm run test:mocha && npm run test:mocha:compat && npm run test:mocha:debug && npm run bench",
50
+ "test:mocha": "BABEL_ENV=test mocha -r @babel/register -r test/setup.js test/*.test.jsx",
51
+ "test:mocha:compat": "BABEL_ENV=test mocha -r @babel/register -r test/setup.js 'test/compat/*.test.js' 'test/compat/*.test.jsx'",
52
+ "test:mocha:debug": "BABEL_ENV=test mocha -r @babel/register -r test/setup.js 'test/debug/*.test.jsx'",
53
+ "format": "prettier src/**/*.{d.ts,js} test/**/*.js --write",
54
+ "prepublishOnly": "npm run build",
55
+ "release": "npm run build && git commit -am $npm_package_version && git tag $npm_package_version && git push && git push --tags && npm publish"
56
+ },
57
+ "keywords": [
58
+ "preact",
59
+ "render",
60
+ "universal",
61
+ "isomorphic"
62
+ ],
63
+ "files": [
64
+ "src",
65
+ "dist",
66
+ "jsx.js",
67
+ "jsx.d.ts",
68
+ "typings.json"
69
+ ],
70
+ "eslintConfig": {
71
+ "extends": "developit",
72
+ "rules": {
73
+ "react/prefer-stateless-function": 0,
74
+ "react/jsx-no-bind": 0,
75
+ "react/no-danger": 0,
76
+ "jest/valid-expect": 0,
77
+ "new-cap": 0,
78
+ "curly": "off",
79
+ "brace-style": "off",
80
+ "indent": "off",
81
+ "lines-around-comment": "off"
82
+ },
83
+ "settings": {
84
+ "react": {
85
+ "version": "16.8"
86
+ }
87
+ }
88
+ },
89
+ "babel": {
90
+ "env": {
91
+ "test": {
92
+ "presets": [
93
+ [
94
+ "@babel/preset-env",
95
+ {
96
+ "targets": {
97
+ "node": true
98
+ }
99
+ }
100
+ ]
101
+ ],
102
+ "plugins": [
103
+ [
104
+ "@babel/plugin-transform-react-jsx",
105
+ {
106
+ "pragma": "h"
107
+ }
108
+ ]
109
+ ]
110
+ }
111
+ }
112
+ },
113
+ "minify": {
114
+ "compress": {
115
+ "reduce_funcs": false
116
+ }
117
+ },
118
+ "author": "The Preact Authors (https://github.com/preactjs/preact/contributors)",
119
+ "license": "MIT",
120
+ "repository": {
121
+ "type": "git",
122
+ "url": "https://github.com/preactjs/preact-render-to-string"
123
+ },
124
+ "bugs": "https://github.com/preactjs/preact-render-to-string/issues",
125
+ "homepage": "https://github.com/preactjs/preact-render-to-string",
126
+ "peerDependencies": {
127
+ "preact": ">=10"
128
+ },
129
+ "devDependencies": {
130
+ "@babel/plugin-transform-react-jsx": "^7.12.12",
131
+ "@babel/preset-env": "^7.12.11",
132
+ "@babel/register": "^7.12.10",
133
+ "@changesets/changelog-github": "^0.4.1",
134
+ "@changesets/cli": "^2.18.0",
135
+ "benchmarkjs-pretty": "^2.0.1",
136
+ "chai": "^4.2.0",
137
+ "copyfiles": "^2.4.1",
138
+ "eslint": "^7.16.0",
139
+ "eslint-config-developit": "^1.2.0",
140
+ "husky": "^4.3.6",
141
+ "lint-staged": "^10.5.3",
142
+ "microbundle": "^0.15.1",
143
+ "mocha": "^8.2.1",
144
+ "baseline-rts": "npm:preact-render-to-string@latest",
145
+ "preact": "^10.13.0",
146
+ "prettier": "^2.2.1",
147
+ "sinon": "^9.2.2",
148
+ "sinon-chai": "^3.5.0",
149
+ "typescript": "^5.0.0",
150
+ "web-streams-polyfill": "^3.2.1"
151
+ },
152
+ "dependencies": {
153
+ "pretty-format": "^3.8.0"
154
+ },
155
+ "prettier": {
156
+ "singleQuote": true,
157
+ "trailingComma": "none",
158
+ "useTabs": true,
159
+ "tabWidth": 2
160
+ },
161
+ "lint-staged": {
162
+ "**/*.{js,jsx,ts,tsx,yml}": [
163
+ "prettier --write"
164
+ ]
165
+ },
166
+ "husky": {
167
+ "hooks": {
168
+ "pre-commit": "lint-staged"
169
+ }
170
+ },
171
+ "publishConfig": {
172
+ "provenance": true
173
+ }
159
174
  }
package/src/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  NAMESPACE_REPLACE_REGEX,
6
6
  HTML_LOWER_CASE,
7
7
  SVG_CAMEL_CASE
8
- } from './util.js';
8
+ } from './lib/util.js';
9
9
  import { options, h, Fragment } from 'preact';
10
10
  import {
11
11
  CHILDREN,
@@ -18,10 +18,9 @@ import {
18
18
  PARENT,
19
19
  RENDER,
20
20
  SKIP_EFFECTS,
21
- VNODE
22
- } from './constants.js';
23
-
24
- /** @typedef {import('preact').VNode} VNode */
21
+ VNODE,
22
+ CATCH_ERROR
23
+ } from './lib/constants.js';
25
24
 
26
25
  const EMPTY_ARR = [];
27
26
  const isArray = Array.isArray;
@@ -34,9 +33,10 @@ let beforeDiff, afterDiff, renderHook, ummountHook;
34
33
  * Render Preact JSX + Components to an HTML string.
35
34
  * @param {VNode} vnode JSX Element / VNode to render
36
35
  * @param {Object} [context={}] Initial root context object
36
+ * @param {RendererState} [_rendererState] for internal use
37
37
  * @returns {string} serialized HTML
38
38
  */
39
- export function renderToString(vnode, context) {
39
+ export function renderToString(vnode, context, _rendererState) {
40
40
  // Performance optimization: `renderToString` is synchronous and we
41
41
  // therefore don't execute any effects. To do that we pass an empty
42
42
  // array to `options._commit` (`__c`). But we can go one step further
@@ -61,7 +61,8 @@ export function renderToString(vnode, context) {
61
61
  false,
62
62
  undefined,
63
63
  parent,
64
- false
64
+ false,
65
+ _rendererState
65
66
  );
66
67
  } catch (e) {
67
68
  if (e.then) {
@@ -109,7 +110,8 @@ export async function renderToStringAsync(vnode, context) {
109
110
  false,
110
111
  undefined,
111
112
  parent,
112
- true
113
+ true,
114
+ undefined
113
115
  );
114
116
 
115
117
  if (Array.isArray(rendered)) {
@@ -204,6 +206,7 @@ function renderClassComponent(vnode, context) {
204
206
  * @param {any} selectValue
205
207
  * @param {VNode} parent
206
208
  * @param {boolean} asyncMode
209
+ * @param {RendererState | undefined} [renderer]
207
210
  * @returns {string | Promise<string> | (string | Promise<string>)[]}
208
211
  */
209
212
  function _renderToString(
@@ -212,7 +215,8 @@ function _renderToString(
212
215
  isSvgMode,
213
216
  selectValue,
214
217
  parent,
215
- asyncMode
218
+ asyncMode,
219
+ renderer
216
220
  ) {
217
221
  // Ignore non-rendered VNodes/values
218
222
  if (vnode == null || vnode === true || vnode === false || vnode === '') {
@@ -240,7 +244,8 @@ function _renderToString(
240
244
  isSvgMode,
241
245
  selectValue,
242
246
  parent,
243
- asyncMode
247
+ asyncMode,
248
+ renderer
244
249
  );
245
250
 
246
251
  if (typeof childRender === 'string') {
@@ -305,7 +310,8 @@ function _renderToString(
305
310
  isSvgMode,
306
311
  selectValue,
307
312
  vnode,
308
- asyncMode
313
+ asyncMode,
314
+ renderer
309
315
  );
310
316
  } else {
311
317
  // Values are pre-escaped by the JSX transform
@@ -386,7 +392,8 @@ function _renderToString(
386
392
  isSvgMode,
387
393
  selectValue,
388
394
  vnode,
389
- asyncMode
395
+ asyncMode,
396
+ renderer
390
397
  );
391
398
  return str;
392
399
  } catch (err) {
@@ -418,7 +425,8 @@ function _renderToString(
418
425
  isSvgMode,
419
426
  selectValue,
420
427
  vnode,
421
- asyncMode
428
+ asyncMode,
429
+ renderer
422
430
  );
423
431
  }
424
432
 
@@ -449,16 +457,38 @@ function _renderToString(
449
457
  isSvgMode,
450
458
  selectValue,
451
459
  vnode,
452
- asyncMode
460
+ asyncMode,
461
+ renderer
453
462
  );
454
463
 
455
464
  if (afterDiff) afterDiff(vnode);
465
+ // when we are dealing with suspense we can't do this...
456
466
  vnode[PARENT] = null;
457
467
 
458
- if (ummountHook) ummountHook(vnode);
468
+ if (options.unmount) options.unmount(vnode);
459
469
 
460
470
  return str;
461
471
  } catch (error) {
472
+ if (!asyncMode && renderer && renderer.onError) {
473
+ let res = renderer.onError(error, vnode, (child) =>
474
+ _renderToString(
475
+ child,
476
+ context,
477
+ isSvgMode,
478
+ selectValue,
479
+ vnode,
480
+ asyncMode,
481
+ renderer
482
+ )
483
+ );
484
+
485
+ if (res !== undefined) return res;
486
+
487
+ let errorHook = options[CATCH_ERROR];
488
+ if (errorHook) errorHook(error, vnode);
489
+ return '';
490
+ }
491
+
462
492
  if (!asyncMode) throw error;
463
493
 
464
494
  if (!error || typeof error.then !== 'function') throw error;
@@ -471,7 +501,8 @@ function _renderToString(
471
501
  isSvgMode,
472
502
  selectValue,
473
503
  vnode,
474
- asyncMode
504
+ asyncMode,
505
+ renderer
475
506
  );
476
507
  } catch (e) {
477
508
  if (!e || typeof e.then !== 'function') throw e;
@@ -484,7 +515,8 @@ function _renderToString(
484
515
  isSvgMode,
485
516
  selectValue,
486
517
  vnode,
487
- asyncMode
518
+ asyncMode,
519
+ renderer
488
520
  ),
489
521
  () => renderNestedChildren()
490
522
  );
@@ -628,12 +660,16 @@ function _renderToString(
628
660
  childSvgMode,
629
661
  selectValue,
630
662
  vnode,
631
- asyncMode
663
+ asyncMode,
664
+ renderer
632
665
  );
633
666
  }
634
667
 
635
668
  if (afterDiff) afterDiff(vnode);
669
+
670
+ // TODO: this was commented before
636
671
  vnode[PARENT] = null;
672
+
637
673
  if (ummountHook) ummountHook(vnode);
638
674
 
639
675
  // Emit self-closing tag for empty void elements:
@@ -0,0 +1,35 @@
1
+ import { ComponentChildren, VNode } from 'preact';
2
+
3
+ interface Suspended {
4
+ id: string;
5
+ promise: Promise<any>;
6
+ context: any;
7
+ isSvgMode: boolean;
8
+ selectValue: any;
9
+ vnode: VNode;
10
+ parent: VNode | null;
11
+ }
12
+
13
+ interface RendererErrorHandler {
14
+ (
15
+ this: RendererState,
16
+ error: any,
17
+ vnode: VNode<{ fallback: any }>,
18
+ renderChild: (child: ComponentChildren) => string
19
+ ): string | undefined;
20
+ }
21
+
22
+ interface RendererState {
23
+ start: number;
24
+ suspended: Suspended[];
25
+ abortSignal?: AbortSignal | undefined;
26
+ onWrite: (str: string) => void;
27
+ onError?: RendererErrorHandler;
28
+ }
29
+
30
+ interface RenderToChunksOptions {
31
+ context?: any;
32
+ onError?: (error: any) => void;
33
+ onWrite: (str: string) => void;
34
+ abortSignal?: AbortSignal;
35
+ }
package/src/jsx.js CHANGED
@@ -1,10 +1,8 @@
1
- import './polyfills.js';
1
+ import './lib/polyfills.js';
2
2
  import renderToString from './pretty.js';
3
- import { indent, encodeEntities } from './util.js';
3
+ import { indent, encodeEntities } from './lib/util.js';
4
4
  import prettyFormat from 'pretty-format';
5
5
 
6
- /** @typedef {import('preact').VNode} VNode */
7
-
8
6
  // we have to patch in Array support, Possible issue in npm.im/pretty-format
9
7
  let preactPlugin = {
10
8
  test(object) {
@@ -0,0 +1,97 @@
1
+ import { renderToString } from '../index.js';
2
+ import { CHILD_DID_SUSPEND, COMPONENT, PARENT } from './constants.js';
3
+ import { Deferred } from './util.js';
4
+ import { createInitScript, createSubtree } from './client.js';
5
+
6
+ /**
7
+ * @param {VNode} vnode
8
+ * @param {RenderToChunksOptions} options
9
+ * @returns {Promise<void>}
10
+ */
11
+ export async function renderToChunks(vnode, { context, onWrite, abortSignal }) {
12
+ context = context || {};
13
+
14
+ /** @type {RendererState} */
15
+ const renderer = {
16
+ start: Date.now(),
17
+ abortSignal,
18
+ onWrite,
19
+ onError: handleError,
20
+ suspended: []
21
+ };
22
+
23
+ // Synchronously render the shell
24
+ // @ts-ignore - using third internal RendererState argument
25
+ const shell = renderToString(vnode, context, renderer);
26
+ onWrite(shell);
27
+
28
+ // Wait for any suspended sub-trees if there are any
29
+ const len = renderer.suspended.length;
30
+ if (len > 0) {
31
+ onWrite('<div hidden>');
32
+ onWrite(createInitScript(len));
33
+ // We should keep checking all promises
34
+ await forkPromises(renderer);
35
+ onWrite('</div>');
36
+ }
37
+ }
38
+
39
+ async function forkPromises(renderer) {
40
+ if (renderer.suspended.length > 0) {
41
+ const suspensions = [...renderer.suspended];
42
+ await Promise.all(renderer.suspended.map((s) => s.promise));
43
+ renderer.suspended = renderer.suspended.filter(
44
+ (s) => !suspensions.includes(s)
45
+ );
46
+ await forkPromises(renderer);
47
+ }
48
+ }
49
+
50
+ /** @type {RendererErrorHandler} */
51
+ function handleError(error, vnode, renderChild) {
52
+ if (!error || !error.then) return;
53
+
54
+ // walk up to the Suspense boundary
55
+ while ((vnode = vnode[PARENT])) {
56
+ let component = vnode[COMPONENT];
57
+ if (component && component[CHILD_DID_SUSPEND]) {
58
+ break;
59
+ }
60
+ }
61
+
62
+ if (!vnode) return;
63
+
64
+ const id = vnode.__v;
65
+ const found = this.suspended.find((x) => x.id === id);
66
+ const race = new Deferred();
67
+
68
+ const abortSignal = this.abortSignal;
69
+ if (abortSignal) {
70
+ // @ts-ignore 2554 - implicit undefined arg
71
+ if (abortSignal.aborted) race.resolve();
72
+ else abortSignal.addEventListener('abort', race.resolve);
73
+ }
74
+
75
+ const promise = error.then(
76
+ () => {
77
+ if (abortSignal && abortSignal.aborted) return;
78
+ const child = renderChild(vnode.props.children);
79
+ if (child) this.onWrite(createSubtree(id, child));
80
+ },
81
+ // TODO: Abort and send hydration code snippet to client
82
+ // to attempt to recover during hydration
83
+ this.onError
84
+ );
85
+
86
+ this.suspended.push({
87
+ id,
88
+ vnode,
89
+ promise: Promise.race([promise, race.promise])
90
+ });
91
+
92
+ const fallback = renderChild(vnode.props.fallback);
93
+
94
+ return found
95
+ ? ''
96
+ : `<!--preact-island:${id}-->${fallback}<!--/preact-island:${id}-->`;
97
+ }