Skip to content

Commit

Permalink
Remove the special _document-concurrent component (vercel#35242)
Browse files Browse the repository at this point in the history
This PR removes the `` const documentPage = `_document${globalRuntime ? '-concurrent' : ''}` `` condition from the config resolution phrase, and only use the built-in one. And later when rendering, we can conditionally convert the default class component into a function component. This change is necessary for the switchable runtime feature (vercel#31506).

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`

- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`

- [ ] Make sure the linting passes by running `yarn lint`
  • Loading branch information
shuding committed Mar 11, 2022
1 parent 63f1f3d commit 379bc50
Show file tree
Hide file tree
Showing 10 changed files with 49 additions and 43 deletions.
6 changes: 1 addition & 5 deletions packages/next/build/entries.ts
Expand Up @@ -39,11 +39,9 @@ export function createPagesMapping(
{
isDev,
hasServerComponents,
globalRuntime,
}: {
isDev: boolean
hasServerComponents: boolean
globalRuntime?: 'nodejs' | 'edge'
}
): PagesMapping {
const previousPages: PagesMapping = {}
Expand Down Expand Up @@ -83,16 +81,14 @@ 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${globalRuntime ? '-concurrent' : ''}`
if (isDev) {
pages['/_app'] = `${PAGES_DIR_ALIAS}/_app`
pages['/_error'] = `${PAGES_DIR_ALIAS}/_error`
pages['/_document'] = `${PAGES_DIR_ALIAS}/_document`
} else {
pages['/_app'] = pages['/_app'] || 'next/dist/pages/_app'
pages['/_error'] = pages['/_error'] || 'next/dist/pages/_error'
pages['/_document'] =
pages['/_document'] || `next/dist/pages/${documentPage}`
pages['/_document'] = pages['/_document'] || `next/dist/pages/_document`
}
return pages
}
Expand Down
1 change: 0 additions & 1 deletion packages/next/build/index.ts
Expand Up @@ -301,7 +301,6 @@ export default async function build(
createPagesMapping(pagePaths, config.pageExtensions, {
isDev: false,
hasServerComponents,
globalRuntime: runtime,
})
)

Expand Down
4 changes: 1 addition & 3 deletions packages/next/build/webpack-config.ts
Expand Up @@ -559,9 +559,7 @@ export default async function getBaseWebpackConfig(
prev.push(path.join(pagesDir, `_document.${ext}`))
return prev
}, [] as string[]),
`next/dist/pages/_document${
hasConcurrentFeatures ? '-concurrent' : ''
}.js`,
`next/dist/pages/_document.js`,
]
}

Expand Down
16 changes: 0 additions & 16 deletions packages/next/pages/_document-concurrent.tsx

This file was deleted.

15 changes: 15 additions & 0 deletions packages/next/pages/_document.tsx
Expand Up @@ -184,6 +184,21 @@ export default class Document<P = {}> extends Component<DocumentProps & P> {
}
}

// Add a speical property to the built-in `Document` component so later we can
// identify if a user customized `Document` is used or not.
;(Document as any).__next_internal_document =
function InternalFunctionDocument() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}

export function Html(
props: React.DetailedHTMLProps<
React.HtmlHTMLAttributes<HTMLHtmlElement>,
Expand Down
9 changes: 8 additions & 1 deletion packages/next/server/base-server.ts
Expand Up @@ -1176,14 +1176,21 @@ export default abstract class Server {

if (opts.supportsDynamicHTML === true) {
const isBotRequest = isBot(req.headers['user-agent'] || '')
const isSupportedDocument =
typeof components.Document?.getInitialProps !== 'function' ||
// When concurrent features is enabled, the built-in `Document`
// component also supports dynamic HTML.
(this.renderOpts.reactRoot &&
!!(components.Document as any)?.__next_internal_document)

// Disable dynamic HTML in cases that we know it won't be generated,
// so that we can continue generating a cache key when possible.
opts.supportsDynamicHTML =
!isSSG &&
!isLikeServerless &&
!isBotRequest &&
!query.amp &&
typeof components.Document?.getInitialProps !== 'function'
isSupportedDocument
}

const defaultLocale = isSSG
Expand Down
1 change: 0 additions & 1 deletion packages/next/server/dev/hot-reloader.ts
Expand Up @@ -324,7 +324,6 @@ export default class HotReloader {
this.config.pageExtensions,
{
isDev: true,
globalRuntime: this.runtime,
hasServerComponents: this.hasServerComponents,
}
)
Expand Down
4 changes: 1 addition & 3 deletions packages/next/server/dev/next-dev-server.ts
Expand Up @@ -950,9 +950,7 @@ export default class DevServer extends Server {
// Build the error page to ensure the fallback is built too.
// TODO: See if this can be moved into hotReloader or removed.
await this.hotReloader!.ensurePage('/_error')
return await loadDefaultErrorComponents(this.distDir, {
hasConcurrentFeatures: shouldUseReactRoot(),
})
return await loadDefaultErrorComponents(this.distDir)
}

protected setImmutableAssetCacheControl(res: BaseNextResponse): void {
Expand Down
10 changes: 2 additions & 8 deletions packages/next/server/load-components.ts
Expand Up @@ -39,14 +39,8 @@ export type LoadComponentsReturnType = {
AppMod: any
}

export async function loadDefaultErrorComponents(
distDir: string,
{ hasConcurrentFeatures }: { hasConcurrentFeatures: boolean }
) {
const Document = interopDefault(
require(`next/dist/pages/_document` +
(hasConcurrentFeatures ? '-concurrent' : ''))
)
export async function loadDefaultErrorComponents(distDir: string) {
const Document = interopDefault(require('next/dist/pages/_document'))
const AppMod = require('next/dist/pages/_app')
const App = interopDefault(AppMod)
const ComponentMod = require('next/dist/pages/_error')
Expand Down
26 changes: 21 additions & 5 deletions packages/next/server/render.tsx
Expand Up @@ -431,7 +431,6 @@ export async function renderToHTML(
dev = false,
ampPath = '',
App,
Document,
pageConfig = {},
buildManifest,
fontManifest,
Expand All @@ -457,6 +456,7 @@ export async function renderToHTML(

const hasConcurrentFeatures = reactRoot

let Document = renderOpts.Document
const OriginalComponent = renderOpts.Component

// We don't need to opt-into the flight inlining logic if the page isn't a RSC.
Expand Down Expand Up @@ -1234,11 +1234,27 @@ export async function renderToHTML(
*/
const generateStaticHTML = supportsDynamicHTML !== true
const renderDocument = async () => {
// For `Document`, there are two cases that we don't support:
// 1. Using `Document.getInitialProps` in the Edge runtime.
// 2. Using the class component `Document` with concurrent features.

const builtinDocument = (Document as any).__next_internal_document as
| typeof Document
| undefined

if (runtime === 'edge' && Document.getInitialProps) {
// In the Edge runtime, Document.getInitialProps isn't supported.
throw new Error(
'`getInitialProps` in Document component is not supported with the Edge Runtime.'
)
// In the Edge runtime, `Document.getInitialProps` isn't supported.
// We throw an error here if it's customized.
if (!builtinDocument) {
throw new Error(
'`getInitialProps` in Document component is not supported with the Edge Runtime.'
)
}
}

// We make it a function component to enable streaming.
if (hasConcurrentFeatures && builtinDocument) {
Document = builtinDocument
}

if (!hasConcurrentFeatures && Document.getInitialProps) {
Expand Down

0 comments on commit 379bc50

Please sign in to comment.