From 899b1bb6903849d3763dfc50c9944200ff048327 Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Thu, 29 Sep 2022 15:56:51 +0200 Subject: [PATCH] make sure polyfill is added in app --- packages/next/server/app-render.tsx | 27 +++++++++++++------ .../next/server/node-web-streams-helper.ts | 12 ++++++++- test/e2e/app-dir/index.test.ts | 7 +++++ 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/packages/next/server/app-render.tsx b/packages/next/server/app-render.tsx index 4975b86ba9a00..e2f6f072797ae 100644 --- a/packages/next/server/app-render.tsx +++ b/packages/next/server/app-render.tsx @@ -1287,6 +1287,13 @@ export async function renderToHTMLOrFlight( return flushed } + const polyfills = buildManifest.polyfillFiles + .filter( + (polyfill) => + polyfill.endsWith('.js') && !polyfill.endsWith('.module.js') + ) + .map((polyfill) => `${renderOpts.assetPrefix || ''}/_next/${polyfill}`) + try { const renderStream = await renderToInitialStream({ ReactDOMServer, @@ -1295,14 +1302,16 @@ export async function renderToHTMLOrFlight( onError: htmlRendererErrorHandler, nonce, // Include hydration scripts in the HTML - bootstrapScripts: subresourceIntegrityManifest - ? buildManifest.rootMainFiles.map((src) => ({ - src: `${renderOpts.assetPrefix || ''}/_next/` + src, - integrity: subresourceIntegrityManifest[src], - })) - : buildManifest.rootMainFiles.map( - (src) => `${renderOpts.assetPrefix || ''}/_next/` + src - ), + bootstrapScripts: [ + ...(subresourceIntegrityManifest + ? buildManifest.rootMainFiles.map((src) => ({ + src: `${renderOpts.assetPrefix || ''}/_next/` + src, + integrity: subresourceIntegrityManifest[src], + })) + : buildManifest.rootMainFiles.map( + (src) => `${renderOpts.assetPrefix || ''}/_next/` + src + )), + ], }, }) @@ -1311,6 +1320,7 @@ export async function renderToHTMLOrFlight( generateStaticHTML: generateStaticHTML, flushEffectHandler, flushEffectsToHead: true, + polyfills, }) } catch (err: any) { // TODO-APP: show error overlay in development. `element` should probably be wrapped in AppRouter for this case. @@ -1341,6 +1351,7 @@ export async function renderToHTMLOrFlight( generateStaticHTML: generateStaticHTML, flushEffectHandler, flushEffectsToHead: true, + polyfills, }) } } diff --git a/packages/next/server/node-web-streams-helper.ts b/packages/next/server/node-web-streams-helper.ts index 9e6bdfb203d7e..77b759f2ecf4f 100644 --- a/packages/next/server/node-web-streams-helper.ts +++ b/packages/next/server/node-web-streams-helper.ts @@ -265,12 +265,14 @@ export async function continueFromInitialStream( generateStaticHTML, flushEffectHandler, flushEffectsToHead, + polyfills, }: { suffix?: string dataStream?: ReadableStream generateStaticHTML: boolean flushEffectHandler?: () => Promise flushEffectsToHead: boolean + polyfills?: string[] } ): Promise> { const closeTag = '' @@ -289,13 +291,21 @@ export async function continueFromInitialStream( dataStream ? createInlineDataStream(dataStream) : null, suffixUnclosed != null ? createSuffixStream(closeTag) : null, createHeadInjectionTransformStream(async () => { + // Inject polyfills for browsers that don't support modules. It has to be + // blocking here and can't be `defer` because other scripts have `async`. + const polyfillScripts = polyfills + ? polyfills + .map((src) => ``) + .join('') + : '' + // TODO-APP: Inject flush effects to end of head in app layout rendering, to avoid // hydration errors. Remove this once it's ready to be handled by react itself. const flushEffectsContent = flushEffectHandler && flushEffectsToHead ? await flushEffectHandler() : '' - return flushEffectsContent + return polyfillScripts + flushEffectsContent }), ].filter(nonNullable) diff --git a/test/e2e/app-dir/index.test.ts b/test/e2e/app-dir/index.test.ts index c67d345bb0d35..9b88201286c8b 100644 --- a/test/e2e/app-dir/index.test.ts +++ b/test/e2e/app-dir/index.test.ts @@ -99,6 +99,13 @@ describe('app dir', () => { expect(html).toContain('hello from dynamic on client') }) + it('should serve polyfills for browsers that do not support modules', async () => { + const html = await renderViaHTTP(next.url, '/dashboard/index') + expect(html).toMatch( + /