Skip to content

Commit

Permalink
Merge branch 'canary' into shu/6379
Browse files Browse the repository at this point in the history
  • Loading branch information
shuding authored Nov 8, 2021
2 parents 9f95ae0 + 9e03c8d commit 385144b
Show file tree
Hide file tree
Showing 14 changed files with 138 additions and 48 deletions.
8 changes: 4 additions & 4 deletions bench/nested-deps/next.config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const idx = process.execArgv.indexOf('--cpu-prof')
if (idx >= 0) process.execArgv.splice(idx, 1)

module.exports = {
eslint: {
ignoreDuringBuilds: true,
},
experimental: {
swcLoader: true,
swcMinify: true,
},
swcMinify: true,
}
14 changes: 7 additions & 7 deletions bench/nested-deps/package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"scripts": {
"prepare": "rm -rf components && mkdir components && node ./fuzzponent.js -d 2 -s 206 -o components",
"dev": "cross-env NEXT_PRIVATE_LOCAL_WEBPACK5=1 ../../node_modules/.bin/next dev",
"build": "cross-env NEXT_PRIVATE_LOCAL_WEBPACK5=1 ../../node_modules/.bin/next build",
"start": "cross-env NEXT_PRIVATE_LOCAL_WEBPACK5=1 ../../node_modules/.bin/next start",
"dev-nocache": "rm -rf .next && yarn dev",
"dev-cpuprofile-nocache": "rm -rf .next && cross-env NEXT_PRIVATE_LOCAL_WEBPACK5=1 node --cpu-prof ../../node_modules/.bin/next",
"build-nocache": "rm -rf .next && yarn build"
"prepare": "rimraf components && mkdir components && node ./fuzzponent.js -d 2 -s 206 -o components",
"dev": "cross-env NEXT_PRIVATE_LOCAL_WEBPACK5=1 node ../../node_modules/next/dist/bin/next dev",
"build": "cross-env NEXT_PRIVATE_LOCAL_WEBPACK5=1 node ../../node_modules/next/dist/bin/next build",
"start": "cross-env NEXT_PRIVATE_LOCAL_WEBPACK5=1 node ../../node_modules/next/dist/bin/next start",
"dev-nocache": "rimraf .next && yarn dev",
"dev-cpuprofile-nocache": "rimraf .next && cross-env NEXT_PRIVATE_LOCAL_WEBPACK5=1 node --cpu-prof ../../node_modules/next/dist/bin/next",
"build-nocache": "rimraf .next && yarn build"
}
}
2 changes: 1 addition & 1 deletion packages/next/build/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export function createPagesMapping(
// we alias these in development and allow webpack to
// allow falling back to the correct source file so
// that HMR can work properly when a file is added/removed
const documentPage = `_document${hasServerComponents ? '.web' : ''}`
const documentPage = `_document${hasServerComponents ? '-web' : ''}`
if (isDev) {
pages['/_app'] = `${PAGES_DIR_ALIAS}/_app`
pages['/_error'] = `${PAGES_DIR_ALIAS}/_error`
Expand Down
2 changes: 1 addition & 1 deletion packages/next/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@ export default async function getBaseWebpackConfig(
prev.push(path.join(pagesDir, `_document.${ext}`))
return prev
}, [] as string[]),
`next/dist/pages/_document${hasServerComponents ? '.web' : ''}.js`,
`next/dist/pages/_document${hasServerComponents ? '-web' : ''}.js`,
]
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Default _document page for web runtime

import React from 'react'
import { Html, Head, Main, NextScript } from './_document'

Expand Down
20 changes: 11 additions & 9 deletions packages/next/pages/_document.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import React, { Component, ReactElement, ReactNode, useContext } from 'react'
import {
BODY_RENDER_TARGET,
OPTIMIZED_FONT_PROVIDERS,
} from '../shared/lib/constants'
import { OPTIMIZED_FONT_PROVIDERS } from '../shared/lib/constants'
import {
DocumentContext,
DocumentInitialProps,
Expand Down Expand Up @@ -763,13 +760,18 @@ export class Head extends Component<
}
}

export function Main() {
const { inAmpMode, docComponentsRendered } = useContext(HtmlContext)

export function Main({
children,
}: {
children?: (content: JSX.Element) => JSX.Element
}) {
const { inAmpMode, docComponentsRendered, useMainContent } =
useContext(HtmlContext)
const content = useMainContent(children)
docComponentsRendered.Main = true

if (inAmpMode) return <>{BODY_RENDER_TARGET}</>
return <div id="__next">{BODY_RENDER_TARGET}</div>
if (inAmpMode) return content
return <div id="__next">{content}</div>
}

export class NextScript extends Component<OriginProps> {
Expand Down
80 changes: 56 additions & 24 deletions packages/next/server/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { GetServerSideProps, GetStaticProps, PreviewData } from '../types'
import { isInAmpMode } from '../shared/lib/amp'
import { AmpStateContext } from '../shared/lib/amp-context'
import {
BODY_RENDER_TARGET,
SERVER_PROPS_ID,
STATIC_PROPS_ID,
STATIC_STATUS_PAGES,
Expand Down Expand Up @@ -935,6 +934,20 @@ export async function renderToHTML(
}
}

const appWrappers: Array<(content: JSX.Element) => JSX.Element> = []
const getWrappedApp = (app: JSX.Element) => {
// Prevent wrappers from reading/writing props by rendering inside an
// opaque component. Wrappers should use context instead.
const InnerApp = () => app
return (
<AppContainer>
{appWrappers.reduce((innerContent, fn) => {
return fn(innerContent)
}, <InnerApp />)}
</AppContainer>
)
}

/**
* Rules of Static & Dynamic HTML:
*
Expand Down Expand Up @@ -976,13 +989,13 @@ export async function renderToHTML(
enhanceComponents(options, App, Component)

const html = ReactDOMServer.renderToString(
<AppContainer>
getWrappedApp(
<EnhancedApp
Component={EnhancedComponent}
router={router}
{...props}
/>
</AppContainer>
)
)
return { html, head }
}
Expand All @@ -1002,33 +1015,51 @@ export async function renderToHTML(
}

return {
bodyResult: piperFromArray([docProps.html]),
bodyResult: () => piperFromArray([docProps.html]),
documentElement: (htmlProps: HtmlProps) => (
<Document {...htmlProps} {...docProps} />
),
useMainContent: (fn?: (content: JSX.Element) => JSX.Element) => {
if (fn) {
throw new Error(
'The `children` property is not supported by non-functional custom Document components'
)
}
// @ts-ignore
return <next-js-internal-body-render-target />
},
head: docProps.head,
headTags: await headTags(documentCtx),
styles: docProps.styles,
}
} else {
const content =
ctx.err && ErrorDebug ? (
<ErrorDebug error={ctx.err} />
) : (
<AppContainer>
<App {...props} Component={Component} router={router} />
</AppContainer>
)
const bodyResult = async () => {
const content =
ctx.err && ErrorDebug ? (
<ErrorDebug error={ctx.err} />
) : (
getWrappedApp(
<App {...props} Component={Component} router={router} />
)
)

const bodyResult = concurrentFeatures
? process.browser
? await renderToReadableStream(content)
: await renderToNodeStream(content, generateStaticHTML)
: piperFromArray([ReactDOMServer.renderToString(content)])
return concurrentFeatures
? process.browser
? await renderToReadableStream(content)
: await renderToNodeStream(content, generateStaticHTML)
: piperFromArray([ReactDOMServer.renderToString(content)])
}

return {
bodyResult,
documentElement: () => (Document as any)(),
useMainContent: (fn?: (content: JSX.Element) => JSX.Element) => {
if (fn) {
appWrappers.push(fn)
}
// @ts-ignore
return <next-js-internal-body-render-target />
},
head,
headTags: [],
styles: jsxStyleRegistry.styles(),
Expand Down Expand Up @@ -1056,8 +1087,8 @@ export async function renderToHTML(
}

const hybridAmp = ampState.hybrid

const docComponentsRendered: DocumentProps['docComponentsRendered'] = {}

const {
assetPrefix,
buildId,
Expand Down Expand Up @@ -1123,6 +1154,7 @@ export async function renderToHTML(
head: documentResult.head,
headTags: documentResult.headTags,
styles: documentResult.styles,
useMainContent: documentResult.useMainContent,
useMaybeDeferContent,
}

Expand Down Expand Up @@ -1181,20 +1213,20 @@ export async function renderToHTML(
}
}

const renderTargetIdx = documentHTML.indexOf(BODY_RENDER_TARGET)
const [renderTargetPrefix, renderTargetSuffix] = documentHTML.split(
/<next-js-internal-body-render-target><\/next-js-internal-body-render-target>/
)
const prefix: Array<string> = []
prefix.push('<!DOCTYPE html>')
prefix.push(documentHTML.substring(0, renderTargetIdx))
prefix.push(renderTargetPrefix)
if (inAmpMode) {
prefix.push('<!-- __NEXT_DATA__ -->')
}

let pipers: Array<NodeWritablePiper> = [
piperFromArray(prefix),
documentResult.bodyResult,
piperFromArray([
documentHTML.substring(renderTargetIdx + BODY_RENDER_TARGET.length),
]),
await documentResult.bodyResult(),
piperFromArray([renderTargetSuffix]),
]

const postProcessors: Array<((html: string) => Promise<string>) | null> = (
Expand Down
1 change: 0 additions & 1 deletion packages/next/shared/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export const BLOCKED_PAGES = ['/_document', '/_app', '/_error']
export const CLIENT_PUBLIC_FILES_PATH = 'public'
export const CLIENT_STATIC_FILES_PATH = 'static'
export const CLIENT_STATIC_FILES_RUNTIME = 'runtime'
export const BODY_RENDER_TARGET = '__NEXT_BODY_RENDER_TARGET__'
export const STRING_LITERAL_DROP_BUNDLE = '__NEXT_DROP_CLIENT_FILE__'

// server/middleware-flight-manifest.js
Expand Down
1 change: 1 addition & 0 deletions packages/next/shared/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ export type HtmlProps = {
styles?: React.ReactElement[] | React.ReactFragment
head?: Array<JSX.Element | null>
useMaybeDeferContent: MaybeDeferContentHook
useMainContent: (fn?: (content: JSX.Element) => JSX.Element) => JSX.Element
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/next/taskfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,7 @@ export async function pages_document(task, opts) {

export async function pages_document_server(task, opts) {
await task
.source('pages/_document.web.tsx')
.source('pages/_document-web.tsx')
.swc('client', { dev: opts.dev })
.target('dist/pages')
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createContext } from 'react'

export default createContext(null)
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Html, Head, Main, NextScript } from 'next/document'
import Context from '../lib/context'

export default function Document() {
return (
<Html>
<Head />
<body>
<Main>
{(children) => (
<Context.Provider value="from render prop">
{children}
</Context.Provider>
)}
</Main>
<NextScript />
</body>
</Html>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useContext } from 'react'
import Context from '../lib/context'

export default function MainRenderProp() {
const value = useContext(Context)
return <span>{value}</span>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* eslint-env jest */

import { join } from 'path'
import { findPort, launchApp, killApp, renderViaHTTP } from 'next-test-utils'

const appDir = join(__dirname, '..')
let appPort
let app

describe('Functional Custom Document', () => {
describe('development mode', () => {
beforeAll(async () => {
appPort = await findPort()
app = await launchApp(appDir, appPort)
})

afterAll(() => killApp(app))

it('supports render props', async () => {
const html = await renderViaHTTP(appPort, '/')
expect(html).toMatch(/<span>from render prop<\/span>/)
})
})
})

0 comments on commit 385144b

Please sign in to comment.