Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix not found hangs the build with overridden node env #53106

Merged
merged 10 commits into from
Jul 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
170 changes: 91 additions & 79 deletions packages/next/src/server/app-render/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ function ErrorHtml({
)
}

function createNotFoundLoaderTree(loaderTree: LoaderTree): LoaderTree {
// Align the segment with parallel-route-default in next-app-loader
return ['__DEFAULT__', {}, loaderTree[2]]
}

// Find the closest matched component in the loader tree for a given component type
function findMatchedComponent(
loaderTree: LoaderTree,
Expand Down Expand Up @@ -614,7 +619,7 @@ export async function renderToHTMLOrFlight(
firstItem?: boolean
injectedCSS: Set<string>
injectedFontPreloadTags: Set<string>
asNotFound?: boolean | 'force'
asNotFound?: boolean
}): Promise<{
Component: React.ComponentType
styles: React.ReactNode
Expand Down Expand Up @@ -944,18 +949,15 @@ export async function renderToHTMLOrFlight(

// If it's a not found route, and we don't have any matched parallel
// routes, we try to render the not found component if it exists.
let isLeaf =
process.env.NODE_ENV === 'production'
? !segment && !rootLayoutIncluded
: !parallelRouteMap.length && segment === '__DEFAULT__' // hit parallel-route-default

let notFoundComponent = {}
if (
NotFound &&
// For action not-found we force render the NotFound and stop checking the parallel routes.
(asNotFound === 'force' ||
// For normal case where we should look up for not-found, keep checking the parallel routes.
(asNotFound && isLeaf))
asNotFound &&
// In development, it could hit the parallel-route-default not found, so we only need to check the segment.
// Or if there's no parallel routes means it reaches the end.
((segment === '__DEFAULT__' && !parallelRouteMap.length) ||
// For production build the original pathname is /_not-found, always render not-found component.
renderOpts.originalPathname === '/_not-found')
) {
notFoundComponent = {
children: (
Expand Down Expand Up @@ -1382,69 +1384,70 @@ export async function renderToHTMLOrFlight(
* A new React Component that renders the provided React Component
* using Flight which can then be rendered to HTML.
*/
const ServerComponentsRenderer = createServerComponentRenderer<{
asNotFound: boolean | 'force'
}>(
async (props) => {
// Create full component tree from root to leaf.
const injectedCSS = new Set<string>()
const injectedFontPreloadTags = new Set<string>()

const { Component: ComponentTree, styles } = await createComponentTree({
createSegmentPath: (child) => child,
loaderTree,
parentParams: {},
firstItem: true,
injectedCSS,
injectedFontPreloadTags,
rootLayoutIncluded: false,
asNotFound: props.asNotFound,
})
const createServerComponentsRenderer = (loaderTreeToRender: LoaderTree) =>
createServerComponentRenderer<{
asNotFound: boolean
}>(
async (props) => {
// Create full component tree from root to leaf.
const injectedCSS = new Set<string>()
const injectedFontPreloadTags = new Set<string>()

const createMetadata = (tree: LoaderTree, errorType?: 'not-found') => (
// Adding key={requestId} to make metadata remount for each render
// @ts-expect-error allow to use async server component
<MetadataTree
key={requestId}
tree={tree}
errorType={errorType}
pathname={pathname}
searchParams={providedSearchParams}
getDynamicParamFromSegment={getDynamicParamFromSegment}
appUsingSizeAdjust={appUsingSizeAdjust}
/>
)
const { Component: ComponentTree, styles } =
await createComponentTree({
createSegmentPath: (child) => child,
loaderTree: loaderTreeToRender,
parentParams: {},
firstItem: true,
injectedCSS,
injectedFontPreloadTags,
rootLayoutIncluded: false,
asNotFound: props.asNotFound,
})

const initialTree = createFlightRouterStateFromLoaderTree(
loaderTree,
getDynamicParamFromSegment,
query
)
const createMetadata = (errorType?: 'not-found') => (
// Adding key={requestId} to make metadata remount for each render
// @ts-expect-error allow to use async server component
<MetadataTree
key={requestId}
tree={loaderTreeToRender}
errorType={errorType}
pathname={pathname}
searchParams={providedSearchParams}
getDynamicParamFromSegment={getDynamicParamFromSegment}
appUsingSizeAdjust={appUsingSizeAdjust}
/>
)

return (
<>
{styles}
<AppRouter
buildId={renderOpts.buildId}
assetPrefix={assetPrefix}
initialCanonicalUrl={pathname}
initialTree={initialTree}
initialHead={createMetadata(
loaderTree,
props.asNotFound ? 'not-found' : undefined
)}
globalErrorComponent={GlobalError}
>
<ComponentTree />
</AppRouter>
</>
)
},
ComponentMod,
serverComponentsRenderOpts,
serverComponentsErrorHandler,
nonce
)
const initialTree = createFlightRouterStateFromLoaderTree(
loaderTreeToRender,
getDynamicParamFromSegment,
query
)

return (
<>
{styles}
<AppRouter
buildId={renderOpts.buildId}
assetPrefix={assetPrefix}
initialCanonicalUrl={pathname}
initialTree={initialTree}
initialHead={createMetadata(
props.asNotFound ? 'not-found' : undefined
)}
globalErrorComponent={GlobalError}
>
<ComponentTree />
</AppRouter>
</>
)
},
ComponentMod,
serverComponentsRenderOpts,
serverComponentsErrorHandler,
nonce
)

const { HeadManagerContext } =
require('../../shared/lib/head-manager-context') as typeof import('../../shared/lib/head-manager-context')
Expand All @@ -1465,15 +1468,16 @@ export async function renderToHTMLOrFlight(
},
async ({
asNotFound,
tree,
}: {
/**
* This option is used to indicate that the page should be rendered as
* if it was not found. When it's enabled, instead of rendering the
* page component, it renders the not-found segment.
*
* If it's 'force', we don't traverse the tree and directly render the NotFound.
*/
asNotFound: boolean | 'force'
asNotFound: boolean
tree: LoaderTree
}) => {
const polyfills = buildManifest.polyfillFiles
.filter(
Expand All @@ -1487,6 +1491,7 @@ export async function renderToHTMLOrFlight(
integrity: subresourceIntegrityManifest?.[polyfill],
}))

const ServerComponentsRenderer = createServerComponentsRenderer(tree)
const content = (
<HeadManagerContext.Provider
value={{
Expand Down Expand Up @@ -1643,12 +1648,12 @@ export async function renderToHTMLOrFlight(
const injectedCSS = new Set<string>()
const injectedFontPreloadTags = new Set<string>()
const [RootLayout, rootStyles] = await getRootLayout(
loaderTree,
tree,
injectedCSS,
injectedFontPreloadTags
)
const [NotFound, notFoundStyles] = await getNotFound(
loaderTree,
tree,
injectedCSS,
pathname
)
Expand Down Expand Up @@ -1690,7 +1695,7 @@ export async function renderToHTMLOrFlight(
{/* @ts-expect-error allow to use async server component */}
<MetadataTree
key={requestId}
tree={loaderTree}
tree={tree}
pathname={pathname}
errorType={errorType}
searchParams={providedSearchParams}
Expand All @@ -1702,8 +1707,8 @@ export async function renderToHTMLOrFlight(
)

const notFoundLoaderTree: LoaderTree = is404
? ['__DEFAULT__', {}, loaderTree[2]]
: loaderTree
? createNotFoundLoaderTree(tree)
: tree

const initialTree = createFlightRouterStateFromLoaderTree(
notFoundLoaderTree,
Expand Down Expand Up @@ -1777,7 +1782,7 @@ export async function renderToHTMLOrFlight(
})
} catch (finalErr: any) {
if (
process.env.NODE_ENV !== 'production' &&
process.env.NODE_ENV === 'development' &&
isNotFoundError(finalErr)
) {
const bailOnNotFound: typeof import('../../client/components/dev-root-not-found-boundary').bailOnNotFound =
Expand All @@ -1804,14 +1809,21 @@ export async function renderToHTMLOrFlight(
})

if (actionRequestResult === 'not-found') {
return new RenderResult(await bodyResult({ asNotFound: 'force' }))
const notFoundLoaderTree = createNotFoundLoaderTree(loaderTree)
return new RenderResult(
await bodyResult({
asNotFound: true,
tree: notFoundLoaderTree,
})
)
} else if (actionRequestResult) {
return actionRequestResult
}

const renderResult = new RenderResult(
await bodyResult({
asNotFound: pagePath === '/404',
tree: loaderTree,
})
)

Expand Down