From eb3a1a2b34bda45fb0bb8b37eb56758674f1ce5b Mon Sep 17 00:00:00 2001
From: JJ Kasper
Date: Fri, 19 Nov 2021 11:50:46 -0600
Subject: [PATCH 1/5] Fix non-concurrent function _document
---
packages/next/server/render.tsx | 33 ++++++++++--------
test/e2e/next-head/app/components/meta.js | 12 +++++++
test/e2e/next-head/app/pages/_document.js | 13 +++++++
test/e2e/next-head/app/pages/index.js | 15 +++++++++
test/e2e/next-head/index.test.ts | 41 +++++++++++++++++++++++
5 files changed, 100 insertions(+), 14 deletions(-)
create mode 100644 test/e2e/next-head/app/components/meta.js
create mode 100644 test/e2e/next-head/app/pages/_document.js
create mode 100644 test/e2e/next-head/app/pages/index.js
create mode 100644 test/e2e/next-head/index.test.ts
diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx
index 91fdb62bd369..d5673177ce02 100644
--- a/packages/next/server/render.tsx
+++ b/packages/next/server/render.tsx
@@ -1148,24 +1148,29 @@ export async function renderToHTML(
styles: docProps.styles,
}
} else {
- const bodyResult = async () => {
- const content = (
-
- {ctx.err && ErrorDebug ? (
-
- ) : (
- getWrappedApp(
-
- )
- )}
-
+ const content =
+ ctx.err && ErrorDebug ? (
+
+ ) : (
+ getWrappedApp(
+
+ )
)
- return concurrentFeatures
- ? process.browser
+ let bodyResult
+
+ if (concurrentFeatures) {
+ bodyResult = async () => {
+ return process.browser
? await renderToWebStream(content)
: await renderToNodeStream(content, generateStaticHTML)
- : piperFromArray([ReactDOMServer.renderToString(content)])
+ }
+ } else {
+ // for non-concurrent rendering we need to ensure App is rendered
+ // before _document so that updateHead is called/collected before
+ // rendering _document's head
+ const result = piperFromArray([ReactDOMServer.renderToString(content)])
+ bodyResult = () => result
}
return {
diff --git a/test/e2e/next-head/app/components/meta.js b/test/e2e/next-head/app/components/meta.js
new file mode 100644
index 000000000000..3a121375fd3f
--- /dev/null
+++ b/test/e2e/next-head/app/components/meta.js
@@ -0,0 +1,12 @@
+import Head from 'next/head'
+
+export function Meta(props) {
+ return (
+ <>
+
+
+
+
+ >
+ )
+}
diff --git a/test/e2e/next-head/app/pages/_document.js b/test/e2e/next-head/app/pages/_document.js
new file mode 100644
index 000000000000..7ee4a282756f
--- /dev/null
+++ b/test/e2e/next-head/app/pages/_document.js
@@ -0,0 +1,13 @@
+import { Html, Head, Main, NextScript } from 'next/document'
+
+export default function MyDocument() {
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/test/e2e/next-head/app/pages/index.js b/test/e2e/next-head/app/pages/index.js
new file mode 100644
index 000000000000..c887c8738691
--- /dev/null
+++ b/test/e2e/next-head/app/pages/index.js
@@ -0,0 +1,15 @@
+import Head from 'next/head'
+import { Meta } from '../components/meta'
+
+export default function Page(props) {
+ return (
+ <>
+
+
+
+
+
+ index page
+ >
+ )
+}
diff --git a/test/e2e/next-head/index.test.ts b/test/e2e/next-head/index.test.ts
new file mode 100644
index 000000000000..479a67a63bc7
--- /dev/null
+++ b/test/e2e/next-head/index.test.ts
@@ -0,0 +1,41 @@
+import { createNext, FileRef } from 'e2e-utils'
+import { renderViaHTTP } from 'next-test-utils'
+import cheerio from 'cheerio'
+import webdriver from 'next-webdriver'
+import { NextInstance } from 'test/lib/next-modes/base'
+import { join } from 'path'
+
+describe('should set-up next', () => {
+ let next: NextInstance
+
+ beforeAll(async () => {
+ next = await createNext({
+ files: {
+ pages: new FileRef(join(__dirname, 'app/pages')),
+ components: new FileRef(join(__dirname, 'app/components')),
+ },
+ })
+ })
+ afterAll(() => next.destroy())
+
+ it('should have correct head tags in initial document', async () => {
+ const html = await renderViaHTTP(next.url, '/')
+ const $ = cheerio.load(html)
+
+ for (let i = 1; i < 5; i++) {
+ expect($(`meta[name="test-head-${i}"]`).attr()['content']).toBe('hello')
+ }
+ })
+
+ it('should have correct head tags after hydration', async () => {
+ const browser = await webdriver(next.url, '/')
+
+ for (let i = 1; i < 5; i++) {
+ expect(
+ await browser
+ .elementByCss(`meta[name="test-head-${i}"]`)
+ .getAttribute('content')
+ ).toBe('hello')
+ }
+ })
+})
From 0c637558c4cddbdc41b869c398bab3cf21eb4bc6 Mon Sep 17 00:00:00 2001
From: JJ Kasper
Date: Fri, 19 Nov 2021 12:01:19 -0600
Subject: [PATCH 2/5] ensure wrapper is added for non-concurrent
---
packages/next/server/render.tsx | 30 +++++++++++++++++++++---------
1 file changed, 21 insertions(+), 9 deletions(-)
diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx
index d5673177ce02..c77ef5085487 100644
--- a/packages/next/server/render.tsx
+++ b/packages/next/server/render.tsx
@@ -1148,24 +1148,36 @@ export async function renderToHTML(
styles: docProps.styles,
}
} else {
- const content =
- ctx.err && ErrorDebug ? (
-
- ) : (
- getWrappedApp(
-
- )
- )
-
let bodyResult
if (concurrentFeatures) {
bodyResult = async () => {
+ const content =
+ ctx.err && ErrorDebug ? (
+
+ ) : (
+ getWrappedApp(
+
+ )
+ )
+
return process.browser
? await renderToWebStream(content)
: await renderToNodeStream(content, generateStaticHTML)
}
} else {
+ const content =
+ ctx.err && ErrorDebug ? (
+
+
+
+ ) : (
+
+ {getWrappedApp(
+
+ )}
+
+ )
// for non-concurrent rendering we need to ensure App is rendered
// before _document so that updateHead is called/collected before
// rendering _document's head
From d967c9920915feab5cfa14c225aff029da038327 Mon Sep 17 00:00:00 2001
From: JJ Kasper
Date: Fri, 19 Nov 2021 12:04:22 -0600
Subject: [PATCH 3/5] ensure __next div is present
---
packages/next/server/render.tsx | 33 ++++++++++++---------------------
1 file changed, 12 insertions(+), 21 deletions(-)
diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx
index c77ef5085487..d6de774a554c 100644
--- a/packages/next/server/render.tsx
+++ b/packages/next/server/render.tsx
@@ -1149,35 +1149,26 @@ export async function renderToHTML(
}
} else {
let bodyResult
+ const content =
+ ctx.err && ErrorDebug ? (
+
+
+
+ ) : (
+
+ {getWrappedApp(
+
+ )}
+
+ )
if (concurrentFeatures) {
bodyResult = async () => {
- const content =
- ctx.err && ErrorDebug ? (
-
- ) : (
- getWrappedApp(
-
- )
- )
-
return process.browser
? await renderToWebStream(content)
: await renderToNodeStream(content, generateStaticHTML)
}
} else {
- const content =
- ctx.err && ErrorDebug ? (
-
-
-
- ) : (
-
- {getWrappedApp(
-
- )}
-
- )
// for non-concurrent rendering we need to ensure App is rendered
// before _document so that updateHead is called/collected before
// rendering _document's head
From 491dcf5de12c3c3c966d78cebfe36b89520f3410 Mon Sep 17 00:00:00 2001
From: JJ Kasper
Date: Fri, 19 Nov 2021 12:18:42 -0600
Subject: [PATCH 4/5] lint-fix
---
packages/next/server/render.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx
index d6de774a554c..0f29fc05f020 100644
--- a/packages/next/server/render.tsx
+++ b/packages/next/server/render.tsx
@@ -1179,7 +1179,7 @@ export async function renderToHTML(
return {
bodyResult,
documentElement: () => (Document as any)(),
- useMainContent: (fn?: (content: JSX.Element) => JSX.Element) => {
+ useMainContent: (fn?: (_content: JSX.Element) => JSX.Element) => {
if (fn) {
appWrappers.push(fn)
}
From 3c256defa4d7ea7fcb339cc8827b0cbd0caaff88 Mon Sep 17 00:00:00 2001
From: JJ Kasper
Date: Fri, 19 Nov 2021 12:58:42 -0600
Subject: [PATCH 5/5] Update previous test
---
packages/next/server/render.tsx | 38 +++++++++++++------
.../{ => app}/lib/context.js | 0
.../app/next.config.js | 19 ++++++++++
.../app/package.json | 12 ++++++
.../{ => app}/pages/_document.js | 0
.../{ => app}/pages/index.js | 0
.../tests/index.test.js | 5 ++-
7 files changed, 60 insertions(+), 14 deletions(-)
rename test/integration/document-functional-render-prop/{ => app}/lib/context.js (100%)
create mode 100644 test/integration/document-functional-render-prop/app/next.config.js
create mode 100644 test/integration/document-functional-render-prop/app/package.json
rename test/integration/document-functional-render-prop/{ => app}/pages/_document.js (100%)
rename test/integration/document-functional-render-prop/{ => app}/pages/index.js (100%)
diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx
index 0f29fc05f020..c5af9cdc527e 100644
--- a/packages/next/server/render.tsx
+++ b/packages/next/server/render.tsx
@@ -1149,26 +1149,40 @@ export async function renderToHTML(
}
} else {
let bodyResult
- const content =
- ctx.err && ErrorDebug ? (
-
-
-
- ) : (
-
- {getWrappedApp(
-
- )}
-
- )
if (concurrentFeatures) {
bodyResult = async () => {
+ // this must be called inside bodyResult so appWrappers is
+ // up to date when getWrappedApp is called
+ const content =
+ ctx.err && ErrorDebug ? (
+
+
+
+ ) : (
+
+ {getWrappedApp(
+
+ )}
+
+ )
return process.browser
? await renderToWebStream(content)
: await renderToNodeStream(content, generateStaticHTML)
}
} else {
+ const content =
+ ctx.err && ErrorDebug ? (
+
+
+
+ ) : (
+
+ {getWrappedApp(
+
+ )}
+
+ )
// for non-concurrent rendering we need to ensure App is rendered
// before _document so that updateHead is called/collected before
// rendering _document's head
diff --git a/test/integration/document-functional-render-prop/lib/context.js b/test/integration/document-functional-render-prop/app/lib/context.js
similarity index 100%
rename from test/integration/document-functional-render-prop/lib/context.js
rename to test/integration/document-functional-render-prop/app/lib/context.js
diff --git a/test/integration/document-functional-render-prop/app/next.config.js b/test/integration/document-functional-render-prop/app/next.config.js
new file mode 100644
index 000000000000..a866ec0085c1
--- /dev/null
+++ b/test/integration/document-functional-render-prop/app/next.config.js
@@ -0,0 +1,19 @@
+module.exports = {
+ experimental: {
+ reactRoot: true,
+ concurrentFeatures: true,
+ },
+ webpack(config) {
+ const { alias } = config.resolve
+ // FIXME: resolving react/jsx-runtime https://github.com/facebook/react/issues/20235
+ alias['react/jsx-dev-runtime'] = 'react/jsx-dev-runtime.js'
+ alias['react/jsx-runtime'] = 'react/jsx-runtime.js'
+
+ // Use react 18
+ alias['react'] = 'react-18'
+ alias['react-dom'] = 'react-dom-18'
+ alias['react-dom/server'] = 'react-dom-18/server'
+
+ return config
+ },
+}
diff --git a/test/integration/document-functional-render-prop/app/package.json b/test/integration/document-functional-render-prop/app/package.json
new file mode 100644
index 000000000000..f9dafc993a79
--- /dev/null
+++ b/test/integration/document-functional-render-prop/app/package.json
@@ -0,0 +1,12 @@
+{
+ "scripts": {
+ "next": "node -r ../test/require-hook.js ../../../../packages/next/dist/bin/next",
+ "dev": "yarn next dev",
+ "build": "yarn next build",
+ "start": "yarn next start"
+ },
+ "dependencies": {
+ "react": "*",
+ "react-dom": "*"
+ }
+}
diff --git a/test/integration/document-functional-render-prop/pages/_document.js b/test/integration/document-functional-render-prop/app/pages/_document.js
similarity index 100%
rename from test/integration/document-functional-render-prop/pages/_document.js
rename to test/integration/document-functional-render-prop/app/pages/_document.js
diff --git a/test/integration/document-functional-render-prop/pages/index.js b/test/integration/document-functional-render-prop/app/pages/index.js
similarity index 100%
rename from test/integration/document-functional-render-prop/pages/index.js
rename to test/integration/document-functional-render-prop/app/pages/index.js
diff --git a/test/integration/document-functional-render-prop/tests/index.test.js b/test/integration/document-functional-render-prop/tests/index.test.js
index d7cb027d41f7..c843082feed9 100644
--- a/test/integration/document-functional-render-prop/tests/index.test.js
+++ b/test/integration/document-functional-render-prop/tests/index.test.js
@@ -3,7 +3,8 @@
import { join } from 'path'
import { findPort, launchApp, killApp, renderViaHTTP } from 'next-test-utils'
-const appDir = join(__dirname, '..')
+const nodeArgs = ['-r', join(__dirname, '../../react-18/test/require-hook.js')]
+const appDir = join(__dirname, '../app')
let appPort
let app
@@ -11,7 +12,7 @@ describe('Functional Custom Document', () => {
describe('development mode', () => {
beforeAll(async () => {
appPort = await findPort()
- app = await launchApp(appDir, appPort)
+ app = await launchApp(appDir, appPort, { nodeArgs })
})
afterAll(() => killApp(app))