diff --git a/.changeset/orange-beds-pay.md b/.changeset/orange-beds-pay.md
new file mode 100644
index 00000000..3c193ec5
--- /dev/null
+++ b/.changeset/orange-beds-pay.md
@@ -0,0 +1,5 @@
+---
+'preact-render-to-string': major
+---
+
+stream Suspense chunks via declarative shadow dom
diff --git a/src/lib/chunked.js b/src/lib/chunked.js
index cd1686d1..9a932e18 100644
--- a/src/lib/chunked.js
+++ b/src/lib/chunked.js
@@ -1,7 +1,15 @@
import { renderToString } from '../index.js';
import { CHILD_DID_SUSPEND, COMPONENT, PARENT } from './constants.js';
import { Deferred } from './util.js';
-import { createInitScript, createSubtree } from './client.js';
+
+/**
+ * @param {string} id
+ * @param {string} content
+ * @returns {string}
+ */
+export function createSubtree(id, content) {
+ return `${content}`;
+}
/**
* @param {VNode} vnode
@@ -28,11 +36,8 @@ export async function renderToChunks(vnode, { context, onWrite, abortSignal }) {
// Wait for any suspended sub-trees if there are any
const len = renderer.suspended.length;
if (len > 0) {
- onWrite('
');
- onWrite(createInitScript(len));
// We should keep checking all promises
await forkPromises(renderer);
- onWrite('
');
}
}
@@ -93,5 +98,5 @@ function handleError(error, vnode, renderChild) {
return found
? ''
- : `${fallback}`;
+ : `${fallback}`;
}
diff --git a/src/lib/client.js b/src/lib/client.js
deleted file mode 100644
index c0f5cb72..00000000
--- a/src/lib/client.js
+++ /dev/null
@@ -1,62 +0,0 @@
-/* eslint-disable no-var, key-spacing, object-curly-spacing, prefer-arrow-callback, semi, keyword-spacing */
-
-// function initPreactIslandElement() {
-// class PreactIslandElement extends HTMLElement {
-// connectedCallback() {
-// var d = this;
-// if (!d.isConnected) return;
-
-// let i = this.getAttribute('data-target');
-// if (!i) return;
-
-// var s,
-// e,
-// c = document.createNodeIterator(document, 128);
-// while (c.nextNode()) {
-// let n = c.referenceNode;
-
-// if (n.data == 'preact-island:' + i) s = n;
-// else if (n.data == '/preact-island:' + i) e = n;
-// if (s && e) break;
-// }
-// if (s && e) {
-// requestAnimationFrame(() => {
-// var p = e.previousSibling;
-// while (p != s) {
-// if (!p || p == s) break;
-// e.parentNode.removeChild(p);
-// p = e.previousSibling;
-// }
-
-// c = s;
-// while (d.firstChild) {
-// s = d.firstChild;
-// d.removeChild(s);
-// c.after(s);
-// c = s;
-// }
-
-// d.parentNode.removeChild(d);
-// });
-// }
-// }
-// }
-
-// customElements.define('preact-island', PreactIslandElement);
-// }
-
-// To modify the INIT_SCRIPT, uncomment the above code, modify it, and paste it into https://try.terser.org/.
-const INIT_SCRIPT = `class e extends HTMLElement{connectedCallback(){var e=this;if(!e.isConnected)return;let t=this.getAttribute("data-target");if(t){for(var r,a,i=document.createNodeIterator(document,128);i.nextNode();){let e=i.referenceNode;if(e.data=="preact-island:"+t?r=e:e.data=="/preact-island:"+t&&(a=e),r&&a)break}r&&a&&requestAnimationFrame((()=>{for(var t=a.previousSibling;t!=r&&t&&t!=r;)a.parentNode.removeChild(t),t=a.previousSibling;for(i=r;e.firstChild;)r=e.firstChild,e.removeChild(r),i.after(r),i=r;e.parentNode.removeChild(e)}))}}}customElements.define("preact-island",e);`;
-
-export function createInitScript() {
- return ``;
-}
-
-/**
- * @param {string} id
- * @param {string} content
- * @returns {string}
- */
-export function createSubtree(id, content) {
- return `${content}`;
-}
diff --git a/src/lib/util.js b/src/lib/util.js
index 4be4e192..7487bfcd 100644
--- a/src/lib/util.js
+++ b/src/lib/util.js
@@ -1,4 +1,4 @@
-import { DIRTY, BITS } from './constants';
+import { DIRTY, BITS } from './constants.js';
export const VOID_ELEMENTS = /^(?:area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/;
// oxlint-disable-next-line no-control-regex
diff --git a/test/compat/render-chunked.test.jsx b/test/compat/render-chunked.test.jsx
index 3f20e517..5381273c 100644
--- a/test/compat/render-chunked.test.jsx
+++ b/test/compat/render-chunked.test.jsx
@@ -2,8 +2,7 @@ import { h } from 'preact';
import { expect, describe, it } from 'vitest';
import { Suspense } from 'preact/compat';
import { useId } from 'preact/hooks';
-import { renderToChunks } from '../../src/lib/chunked';
-import { createSubtree, createInitScript } from '../../src/lib/client';
+import { createSubtree, renderToChunks } from '../../src/lib/chunked';
import { createSuspender } from '../utils';
import { VNODE, PARENT } from '../../src/lib/constants';
@@ -34,11 +33,8 @@ describe('renderToChunks', () => {
await promise;
expect(result).to.deep.equal([
- 'loading...
',
- '',
- createInitScript(),
- createSubtree('5', '
it works
'),
- '
'
+ 'loading...
',
+ createSubtree('5', 'it works
')
]);
});
@@ -62,10 +58,7 @@ describe('renderToChunks', () => {
suspended.resolve();
expect(result).to.deep.equal([
- 'loading...
',
- '',
- createInitScript(1),
- '
'
+ 'loading...
'
]);
});
@@ -109,11 +102,8 @@ describe('renderToChunks', () => {
}
expect(result).to.deep.equal([
- 'loading...
',
- '',
- createInitScript(1),
- createSubtree('16', '
it works
'),
- '
'
+ 'loading...
',
+ createSubtree('16', 'it works
')
]);
});
@@ -142,11 +132,8 @@ describe('renderToChunks', () => {
await promise;
expect(result).to.deep.equal([
- '',
- '',
- createInitScript(1),
- createSubtree('24', '
id: P0-1
'),
- '
'
+ '',
+ createSubtree('24', 'id: P0-1
')
]);
});
@@ -182,12 +169,9 @@ describe('renderToChunks', () => {
await promise;
expect(result).toEqual([
- 'id: P0-0
loading...loading...
',
- '',
- createInitScript(1),
+ '
id: P0-0
loading...loading...',
createSubtree('33', '
id: P0-1
'),
- createSubtree('36', '
id: P0-2
'),
- '
'
+ createSubtree('36', 'id: P0-2
')
]);
});
@@ -217,11 +201,8 @@ describe('renderToChunks', () => {
await promise;
expect(result).to.deep.equal([
- 'loading part 1...
',
- '',
- createInitScript(1),
- createSubtree('49', '
it works
it works
'),
- '
'
+ 'loading part 1...
',
+ createSubtree('49', 'it works
it works
')
]);
});
});
diff --git a/test/compat/stream-node.test.jsx b/test/compat/stream-node.test.jsx
index b22cb839..d70acc0c 100644
--- a/test/compat/stream-node.test.jsx
+++ b/test/compat/stream-node.test.jsx
@@ -2,7 +2,7 @@ import { PassThrough } from 'node:stream';
import { h } from 'preact';
import { expect, describe, it } from 'vitest';
import { Suspense } from 'preact/compat';
-import { createSubtree, createInitScript } from '../../src/lib/client';
+import { createSubtree } from '../../src/lib/chunked';
import { renderToPipeableStream } from '../../src/stream-node';
import { Deferred } from '../../src/lib/util';
import { createSuspender } from '../utils';
@@ -66,11 +66,8 @@ describe('renderToPipeableStream', () => {
const result = await sink.promise;
expect(result).to.deep.equal([
- 'loading...
',
- '',
- createInitScript(),
- createSubtree('5', '
it works
'),
- '
'
+ 'loading...
',
+ createSubtree('5', 'it works
')
]);
});
diff --git a/test/compat/stream.test.jsx b/test/compat/stream.test.jsx
index 202c26aa..65d7b079 100644
--- a/test/compat/stream.test.jsx
+++ b/test/compat/stream.test.jsx
@@ -2,7 +2,7 @@
import { h } from 'preact';
import { expect, beforeAll, describe, it } from 'vitest';
import { Suspense } from 'preact/compat';
-import { createSubtree, createInitScript } from '../../src/lib/client';
+import { createSubtree } from '../../src/lib/chunked';
import { renderToReadableStream } from '../../src/stream';
import { Deferred } from '../../src/lib/util';
import { createSuspender } from '../utils';
@@ -82,11 +82,8 @@ describe('renderToReadableStream', () => {
const result = await sink.promise;
expect(result).toEqual([
- 'loading...
',
- '',
- createInitScript(),
- createSubtree('5', '
it works
'),
- '
'
+ 'loading...
',
+ createSubtree('5', 'it works
')
]);
});
});