diff --git a/package-lock.json b/package-lock.json index ba54fea6..4d26bb77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "preact-render-to-string", - "version": "6.2.2", + "version": "6.3.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "preact-render-to-string", - "version": "6.2.2", + "version": "6.3.1", "license": "MIT", "dependencies": { "pretty-format": "^3.8.0" diff --git a/src/index.js b/src/index.js index 19567c65..debfa618 100644 --- a/src/index.js +++ b/src/index.js @@ -84,7 +84,7 @@ export function renderToString(vnode, context) { * @param {Object} [context={}] Initial root context object * @returns {string} serialized HTML */ -export function renderToStringAsync(vnode, context) { +export async function renderToStringAsync(vnode, context) { // Performance optimization: `renderToString` is synchronous and we // therefore don't execute any effects. To do that we pass an empty // array to `options._commit` (`__c`). But we can go one step further @@ -113,7 +113,18 @@ export function renderToStringAsync(vnode, context) { ); if (Array.isArray(rendered)) { - return Promise.all(rendered).then((rendered) => rendered.join('')); + let count = 0; + let resolved = rendered; + + // Resolving nested Promises with a maximum depth of 25 + while ( + resolved.some((element) => typeof element.then === 'function') && + count++ < 25 + ) { + resolved = (await Promise.all(resolved)).flat(); + } + + return resolved.join(''); } return rendered; diff --git a/test/compat/async.test.js b/test/compat/async.test.js index 00d4ab04..8a9d8299 100644 --- a/test/compat/async.test.js +++ b/test/compat/async.test.js @@ -25,7 +25,7 @@ describe('Async renderToString', () => { expect(rendered).to.equal(expected); }); - it('should render JSX with nested suspense boundary', async () => { + it('should render JSX with nested suspended components', async () => { const { Suspender: SuspenderOne, suspended: suspendedOne @@ -58,4 +58,40 @@ describe('Async renderToString', () => { expect(rendered).to.equal(expected); }); + + it('should render JSX with nested suspense boundaries', async () => { + const { + Suspender: SuspenderOne, + suspended: suspendedOne + } = createSuspender(); + const { + Suspender: SuspenderTwo, + suspended: suspendedTwo + } = createSuspender(); + + const promise = renderToStringAsync( + + ); + + const expected = ``; + + suspendedOne.resolve(); + suspendedTwo.resolve(); + + const rendered = await promise; + + expect(rendered).to.equal(expected); + }); });