From 5b8078e002e8a6d42bfc5ca72863bb11373441cb Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Thu, 10 Mar 2022 19:50:57 +0100 Subject: [PATCH 1/6] Render resolved streaming content for static render result --- packages/next/server/web-server.ts | 1 - .../server/web/sandbox/readable-stream.ts | 28 ++++++++++++++----- .../test/rsc.js | 9 +----- .../test/streaming.js | 5 ++++ .../test/utils.js | 6 ++++ 5 files changed, 33 insertions(+), 16 deletions(-) diff --git a/packages/next/server/web-server.ts b/packages/next/server/web-server.ts index 32be4de48606..d910c4882a7a 100644 --- a/packages/next/server/web-server.ts +++ b/packages/next/server/web-server.ts @@ -131,7 +131,6 @@ export default class NextWebServer extends BaseServer { query, { ...renderOpts, - // supportsDynamicHTML: true, disableOptimizedLoading: true, runtime: 'edge', } diff --git a/packages/next/server/web/sandbox/readable-stream.ts b/packages/next/server/web/sandbox/readable-stream.ts index 824260922a62..21fd2b0e7b2c 100644 --- a/packages/next/server/web/sandbox/readable-stream.ts +++ b/packages/next/server/web/sandbox/readable-stream.ts @@ -39,17 +39,17 @@ class ReadableStream { const pull = () => { if (opts.pull) { - if (!pullPromise) { + const shouldPull = + controller.desiredSize !== null && controller.desiredSize > 0 + if (!pullPromise && shouldPull) { pullPromise = Promise.resolve().then(() => { - pullPromise = 0 + pullPromise = null opts.pull!(controller) }) + return pullPromise } } - } - - if (opts.start) { - opts.start(controller) + return Promise.resolve() } if (opts.cancel) { @@ -59,7 +59,21 @@ class ReadableStream { } } - pull() + function startPull() { + const getReader = readable.getReader.bind(readable) + readable.getReader = () => { + pull() + return getReader() + } + } + + if (opts.start) { + opts.start(controller).then(() => { + startPull() + }) + } else { + startPull() + } return readable } diff --git a/test/integration/react-streaming-and-server-components/test/rsc.js b/test/integration/react-streaming-and-server-components/test/rsc.js index 3b8ed678e33f..5e7c60d3f4f5 100644 --- a/test/integration/react-streaming-and-server-components/test/rsc.js +++ b/test/integration/react-streaming-and-server-components/test/rsc.js @@ -1,16 +1,9 @@ /* eslint-env jest */ import webdriver from 'next-webdriver' -import cheerio from 'cheerio' import { renderViaHTTP, check } from 'next-test-utils' import { join } from 'path' import fs from 'fs-extra' - -import { distDir } from './utils' - -function getNodeBySelector(html, selector) { - const $ = cheerio.load(html) - return $(selector) -} +import { distDir, getNodeBySelector } from './utils' export default function (context, { runtime, env }) { it('should render server components correctly', async () => { diff --git a/test/integration/react-streaming-and-server-components/test/streaming.js b/test/integration/react-streaming-and-server-components/test/streaming.js index f60fa1300720..8f983029f183 100644 --- a/test/integration/react-streaming-and-server-components/test/streaming.js +++ b/test/integration/react-streaming-and-server-components/test/streaming.js @@ -1,6 +1,7 @@ /* eslint-env jest */ import webdriver from 'next-webdriver' import { fetchViaHTTP, waitFor } from 'next-test-utils' +import { getNodeBySelector } from './utils' async function resolveStreamResponse(response, onData) { let result = '' @@ -218,6 +219,10 @@ export default function (context, { env, runtime }) { flushCount++ }) expect(flushCount).toBe(1) + const html = await res1.text() + const body = await getNodeBySelector(html, '#__next') + // Resolve data instead of fallback + expect(body.text()).toContain('next_streaming_data') if (runtime === 'nodejs') { expect(res1.headers.get('etag')).toBeDefined() diff --git a/test/integration/react-streaming-and-server-components/test/utils.js b/test/integration/react-streaming-and-server-components/test/utils.js index 4e51c063272c..56e3e4527ee1 100644 --- a/test/integration/react-streaming-and-server-components/test/utils.js +++ b/test/integration/react-streaming-and-server-components/test/utils.js @@ -5,6 +5,7 @@ import { nextBuild as _nextBuild, nextStart as _nextStart, } from 'next-test-utils' +import cheerio from 'cheerio' const nodeArgs = ['-r', join(__dirname, '../../react-18/test/require-hook.js')] @@ -44,3 +45,8 @@ export async function nextDev(dir, port) { nodeArgs, }) } + +export function getNodeBySelector(html, selector) { + const $ = cheerio.load(html) + return $(selector) +} From 04ebb859c797c45f7565777ec1976a49985f2b8c Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Thu, 10 Mar 2022 20:15:00 +0100 Subject: [PATCH 2/6] update tests --- .../next/server/web/sandbox/readable-stream.ts | 9 ++++----- .../test/basic.js | 9 +++++++++ .../test/index.test.js | 14 -------------- 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/packages/next/server/web/sandbox/readable-stream.ts b/packages/next/server/web/sandbox/readable-stream.ts index 21fd2b0e7b2c..434042ddccd3 100644 --- a/packages/next/server/web/sandbox/readable-stream.ts +++ b/packages/next/server/web/sandbox/readable-stream.ts @@ -43,7 +43,7 @@ class ReadableStream { controller.desiredSize !== null && controller.desiredSize > 0 if (!pullPromise && shouldPull) { pullPromise = Promise.resolve().then(() => { - pullPromise = null + pullPromise = 0 opts.pull!(controller) }) return pullPromise @@ -67,10 +67,9 @@ class ReadableStream { } } - if (opts.start) { - opts.start(controller).then(() => { - startPull() - }) + const started = opts.start && opts.start(controller) + if (typeof started.then === 'function') { + started.then(() => startPull()) } else { startPull() } diff --git a/test/integration/react-streaming-and-server-components/test/basic.js b/test/integration/react-streaming-and-server-components/test/basic.js index 2c7a9eea4ad3..c46f5c428364 100644 --- a/test/integration/react-streaming-and-server-components/test/basic.js +++ b/test/integration/react-streaming-and-server-components/test/basic.js @@ -54,4 +54,13 @@ export default async function basic(context, { env }) { const html = await renderViaHTTP(context.appPort, '/err/suspense') expect(html).toContain('error-fallback') }) + + it('should support React.lazy and dynamic imports', async () => { + const html = await renderViaHTTP(context.appPort, '/dynamic-imports') + expect(html).toContain('foo.client') + + const browser = await webdriver(context.appPort, '/dynamic-imports') + const content = await browser.eval(`window.document.body.innerText`) + expect(content).toMatchInlineSnapshot('"foo.client"') + }) } diff --git a/test/integration/react-streaming-and-server-components/test/index.test.js b/test/integration/react-streaming-and-server-components/test/index.test.js index 0f5f7a498e04..a12971f8bbef 100644 --- a/test/integration/react-streaming-and-server-components/test/index.test.js +++ b/test/integration/react-streaming-and-server-components/test/index.test.js @@ -146,11 +146,6 @@ describe('Edge runtime - prod', () => { expect(content.clientInfo).not.toContainEqual([['/404', true]]) }) - it('should support React.lazy and dynamic imports', async () => { - const html = await renderViaHTTP(context.appPort, '/dynamic-imports') - expect(html).toContain('foo.client') - }) - const options = { runtime: 'edge', env: 'prod' } basic(context, options) streaming(context, options) @@ -171,15 +166,6 @@ describe('Edge runtime - dev', () => { await killApp(context.server) }) - it('should support React.lazy and dynamic imports', async () => { - const html = await renderViaHTTP(context.appPort, '/dynamic-imports') - expect(html).toContain('loading...') - - const browser = await webdriver(context.appPort, '/dynamic-imports') - const content = await browser.eval(`window.document.body.innerText`) - expect(content).toMatchInlineSnapshot('"foo.client"') - }) - it('should have content-type and content-encoding headers', async () => { const res = await fetchViaHTTP(context.appPort, '/') expect(res.headers.get('content-type')).toBe('text/html; charset=utf-8') From 0d2968e46e89a444dff6740af7b2d5b4946d57cb Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Thu, 10 Mar 2022 20:23:39 +0100 Subject: [PATCH 3/6] rename --- packages/next/server/web/sandbox/readable-stream.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/next/server/web/sandbox/readable-stream.ts b/packages/next/server/web/sandbox/readable-stream.ts index 434042ddccd3..7a845444b81d 100644 --- a/packages/next/server/web/sandbox/readable-stream.ts +++ b/packages/next/server/web/sandbox/readable-stream.ts @@ -59,7 +59,7 @@ class ReadableStream { } } - function startPull() { + function registerPull() { const getReader = readable.getReader.bind(readable) readable.getReader = () => { pull() @@ -69,9 +69,9 @@ class ReadableStream { const started = opts.start && opts.start(controller) if (typeof started.then === 'function') { - started.then(() => startPull()) + started.then(() => registerPull()) } else { - startPull() + registerPull() } return readable From c67aee521b0a353988976bf93880ecf563ee326e Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Thu, 10 Mar 2022 20:25:14 +0100 Subject: [PATCH 4/6] use toBe in test --- .../react-streaming-and-server-components/test/streaming.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/react-streaming-and-server-components/test/streaming.js b/test/integration/react-streaming-and-server-components/test/streaming.js index 8f983029f183..3954db13e242 100644 --- a/test/integration/react-streaming-and-server-components/test/streaming.js +++ b/test/integration/react-streaming-and-server-components/test/streaming.js @@ -222,7 +222,7 @@ export default function (context, { env, runtime }) { const html = await res1.text() const body = await getNodeBySelector(html, '#__next') // Resolve data instead of fallback - expect(body.text()).toContain('next_streaming_data') + expect(body.text()).toBe('next_streaming_data') if (runtime === 'nodejs') { expect(res1.headers.get('etag')).toBeDefined() From 35bc9e428e9f68876cb89a67b077cc11420fcf1c Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Thu, 10 Mar 2022 20:33:54 +0100 Subject: [PATCH 5/6] fix lint --- .../react-streaming-and-server-components/test/index.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/integration/react-streaming-and-server-components/test/index.test.js b/test/integration/react-streaming-and-server-components/test/index.test.js index a12971f8bbef..4bdd329708b9 100644 --- a/test/integration/react-streaming-and-server-components/test/index.test.js +++ b/test/integration/react-streaming-and-server-components/test/index.test.js @@ -2,7 +2,6 @@ import { join } from 'path' import fs from 'fs-extra' -import webdriver from 'next-webdriver' import { fetchViaHTTP, findPort, killApp, renderViaHTTP } from 'next-test-utils' From a28c30fbf9205f7e8ea19902b87141f492e5384a Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Thu, 10 Mar 2022 22:14:56 +0100 Subject: [PATCH 6/6] check return value --- packages/next/server/web/sandbox/readable-stream.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/server/web/sandbox/readable-stream.ts b/packages/next/server/web/sandbox/readable-stream.ts index 7a845444b81d..7c0a1a43bd48 100644 --- a/packages/next/server/web/sandbox/readable-stream.ts +++ b/packages/next/server/web/sandbox/readable-stream.ts @@ -68,7 +68,7 @@ class ReadableStream { } const started = opts.start && opts.start(controller) - if (typeof started.then === 'function') { + if (started && typeof started.then === 'function') { started.then(() => registerPull()) } else { registerPull()