Fix css applying for root not found (#47992)
### What This issue is introduced in #47688, we need to do the same work for rendering single component which collecting the assets and then render with root layout + root not found Fix #47970 Related #47862 (partially fix the css issue but not link issue) ### How This PR encapsulates the preload and stylesheets assets collection and rendering process, and move them into a helper, and share between the component rendering and the root not found rendering
This commit is contained in:
parent
9b0af04649
commit
e4e5c1674a
6 changed files with 131 additions and 78 deletions
|
@ -2541,11 +2541,14 @@ export default async function build(
|
|||
const updatedRelativeDest = path
|
||||
.join('pages', '404.html')
|
||||
.replace(/\\/g, '/')
|
||||
await promises.copyFile(
|
||||
orig,
|
||||
path.join(distDir, 'server', updatedRelativeDest)
|
||||
)
|
||||
pagesManifest['/404'] = updatedRelativeDest
|
||||
|
||||
if (await fileExists(orig)) {
|
||||
await promises.copyFile(
|
||||
orig,
|
||||
path.join(distDir, 'server', updatedRelativeDest)
|
||||
)
|
||||
pagesManifest['/404'] = updatedRelativeDest
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ type AppRouterProps = Omit<
|
|||
assetPrefix: string
|
||||
// Top level boundaries props
|
||||
notFound: React.ReactNode | undefined
|
||||
notFoundStyles: React.ReactNode | undefined
|
||||
notFoundStyles?: React.ReactNode | undefined
|
||||
asNotFound?: boolean
|
||||
}
|
||||
|
||||
|
|
|
@ -459,6 +459,89 @@ export async function renderToHTMLOrFlight(
|
|||
return [Comp, styles]
|
||||
}
|
||||
|
||||
const createStaticAssets = async ({
|
||||
layoutOrPagePath,
|
||||
injectedCSS: injectedCSSWithCurrentLayout,
|
||||
injectedFontPreloadTags: injectedFontPreloadTagsWithCurrentLayout,
|
||||
}: {
|
||||
layoutOrPagePath: string | undefined
|
||||
injectedCSS: Set<string>
|
||||
injectedFontPreloadTags: Set<string>
|
||||
}) => {
|
||||
const stylesheets: string[] = layoutOrPagePath
|
||||
? getCssInlinedLinkTags(
|
||||
clientReferenceManifest,
|
||||
serverCSSManifest!,
|
||||
layoutOrPagePath,
|
||||
serverCSSForEntries,
|
||||
injectedCSSWithCurrentLayout,
|
||||
true
|
||||
)
|
||||
: []
|
||||
|
||||
const preloadedFontFiles = layoutOrPagePath
|
||||
? getPreloadedFontFilesInlineLinkTags(
|
||||
serverCSSManifest!,
|
||||
nextFontManifest,
|
||||
serverCSSForEntries,
|
||||
layoutOrPagePath,
|
||||
injectedFontPreloadTagsWithCurrentLayout
|
||||
)
|
||||
: []
|
||||
|
||||
return (
|
||||
<>
|
||||
{preloadedFontFiles?.length === 0 ? (
|
||||
<link
|
||||
data-next-font={
|
||||
nextFontManifest?.appUsingSizeAdjust ? 'size-adjust' : ''
|
||||
}
|
||||
rel="preconnect"
|
||||
href="/"
|
||||
crossOrigin="anonymous"
|
||||
/>
|
||||
) : null}
|
||||
{preloadedFontFiles
|
||||
? preloadedFontFiles.map((fontFile) => {
|
||||
const ext = /\.(woff|woff2|eot|ttf|otf)$/.exec(fontFile)![1]
|
||||
return (
|
||||
<link
|
||||
key={fontFile}
|
||||
rel="preload"
|
||||
href={`${assetPrefix}/_next/${fontFile}`}
|
||||
as="font"
|
||||
type={`font/${ext}`}
|
||||
crossOrigin="anonymous"
|
||||
data-next-font={
|
||||
fontFile.includes('-s') ? 'size-adjust' : ''
|
||||
}
|
||||
/>
|
||||
)
|
||||
})
|
||||
: null}
|
||||
{stylesheets
|
||||
? stylesheets.map((href, index) => (
|
||||
<link
|
||||
rel="stylesheet"
|
||||
// In dev, Safari will wrongly cache the resource if you preload it:
|
||||
// - https://github.com/vercel/next.js/issues/5860
|
||||
// - https://bugs.webkit.org/show_bug.cgi?id=187726
|
||||
// We used to add a `?ts=` query for resources in `pages` to bypass it,
|
||||
// but in this case it is fine as we don't need to preload the styles.
|
||||
href={`${assetPrefix}/_next/${href}`}
|
||||
// `Precedence` is an opt-in signal for React to handle
|
||||
// resource loading and deduplication, etc:
|
||||
// https://github.com/facebook/react/pull/25060
|
||||
// @ts-ignore
|
||||
precedence="next.js"
|
||||
key={index}
|
||||
/>
|
||||
))
|
||||
: null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the provided loader tree to create the React Component tree.
|
||||
*/
|
||||
|
@ -498,29 +581,15 @@ export async function renderToHTMLOrFlight(
|
|||
const layoutOrPagePath = layout?.[1] || page?.[1]
|
||||
|
||||
const injectedCSSWithCurrentLayout = new Set(injectedCSS)
|
||||
const stylesheets: string[] = layoutOrPagePath
|
||||
? getCssInlinedLinkTags(
|
||||
clientReferenceManifest,
|
||||
serverCSSManifest!,
|
||||
layoutOrPagePath,
|
||||
serverCSSForEntries,
|
||||
injectedCSSWithCurrentLayout,
|
||||
true
|
||||
)
|
||||
: []
|
||||
|
||||
const injectedFontPreloadTagsWithCurrentLayout = new Set(
|
||||
injectedFontPreloadTags
|
||||
)
|
||||
const preloadedFontFiles = layoutOrPagePath
|
||||
? getPreloadedFontFilesInlineLinkTags(
|
||||
serverCSSManifest!,
|
||||
nextFontManifest,
|
||||
serverCSSForEntries,
|
||||
layoutOrPagePath,
|
||||
injectedFontPreloadTagsWithCurrentLayout
|
||||
)
|
||||
: []
|
||||
|
||||
const assets = createStaticAssets({
|
||||
layoutOrPagePath,
|
||||
injectedCSS: injectedCSSWithCurrentLayout,
|
||||
injectedFontPreloadTags: injectedFontPreloadTagsWithCurrentLayout,
|
||||
})
|
||||
|
||||
const [Template, templateStyles] = template
|
||||
? await createComponentAndStyles({
|
||||
|
@ -855,53 +924,7 @@ export async function renderToHTMLOrFlight(
|
|||
) : (
|
||||
<Component {...props} />
|
||||
)}
|
||||
{preloadedFontFiles?.length === 0 ? (
|
||||
<link
|
||||
data-next-font={
|
||||
nextFontManifest?.appUsingSizeAdjust ? 'size-adjust' : ''
|
||||
}
|
||||
rel="preconnect"
|
||||
href="/"
|
||||
crossOrigin="anonymous"
|
||||
/>
|
||||
) : null}
|
||||
{preloadedFontFiles
|
||||
? preloadedFontFiles.map((fontFile) => {
|
||||
const ext = /\.(woff|woff2|eot|ttf|otf)$/.exec(fontFile)![1]
|
||||
return (
|
||||
<link
|
||||
key={fontFile}
|
||||
rel="preload"
|
||||
href={`${assetPrefix}/_next/${fontFile}`}
|
||||
as="font"
|
||||
type={`font/${ext}`}
|
||||
crossOrigin="anonymous"
|
||||
data-next-font={
|
||||
fontFile.includes('-s') ? 'size-adjust' : ''
|
||||
}
|
||||
/>
|
||||
)
|
||||
})
|
||||
: null}
|
||||
{stylesheets
|
||||
? stylesheets.map((href, index) => (
|
||||
<link
|
||||
rel="stylesheet"
|
||||
// In dev, Safari will wrongly cache the resource if you preload it:
|
||||
// - https://github.com/vercel/next.js/issues/5860
|
||||
// - https://bugs.webkit.org/show_bug.cgi?id=187726
|
||||
// We used to add a `?ts=` query for resources in `pages` to bypass it,
|
||||
// but in this case it is fine as we don't need to preload the styles.
|
||||
href={`${assetPrefix}/_next/${href}`}
|
||||
// `Precedence` is an opt-in signal for React to handle
|
||||
// resource loading and deduplication, etc:
|
||||
// https://github.com/facebook/react/pull/25060
|
||||
// @ts-ignore
|
||||
precedence="next.js"
|
||||
key={index}
|
||||
/>
|
||||
))
|
||||
: null}
|
||||
{assets}
|
||||
</>
|
||||
)
|
||||
},
|
||||
|
@ -1201,13 +1224,15 @@ export async function renderToHTMLOrFlight(
|
|||
async (props) => {
|
||||
// Create full component tree from root to leaf.
|
||||
const injectedCSS = new Set<string>()
|
||||
const injectedFontPreloadTags = new Set<string>()
|
||||
|
||||
const { Component: ComponentTree } = await createComponentTree({
|
||||
createSegmentPath: (child) => child,
|
||||
loaderTree,
|
||||
parentParams: {},
|
||||
firstItem: true,
|
||||
injectedCSS,
|
||||
injectedFontPreloadTags: new Set(),
|
||||
injectedFontPreloadTags,
|
||||
rootLayoutIncluded: false,
|
||||
asNotFound: props.asNotFound,
|
||||
})
|
||||
|
@ -1229,6 +1254,12 @@ export async function renderToHTMLOrFlight(
|
|||
? [DefaultNotFound]
|
||||
: []
|
||||
|
||||
const assets = createStaticAssets({
|
||||
layoutOrPagePath: layout?.[1],
|
||||
injectedCSS,
|
||||
injectedFontPreloadTags,
|
||||
})
|
||||
|
||||
const initialTree = createFlightRouterStateFromLoaderTree(
|
||||
loaderTree,
|
||||
getDynamicParamFromSegment,
|
||||
|
@ -1255,12 +1286,15 @@ export async function renderToHTMLOrFlight(
|
|||
globalErrorComponent={GlobalError}
|
||||
notFound={
|
||||
NotFound && RootLayout ? (
|
||||
<RootLayout params={{}}>
|
||||
<NotFound />
|
||||
</RootLayout>
|
||||
<>
|
||||
{assets}
|
||||
<RootLayout params={{}}>
|
||||
{notFoundStyles}
|
||||
<NotFound />
|
||||
</RootLayout>
|
||||
</>
|
||||
) : undefined
|
||||
}
|
||||
notFoundStyles={notFoundStyles}
|
||||
asNotFound={props.asNotFound}
|
||||
>
|
||||
<ComponentTree />
|
||||
|
|
3
test/e2e/app-dir/app-css/app/not-found.js
Normal file
3
test/e2e/app-dir/app-css/app/not-found.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default function RootNotFound() {
|
||||
return <h1 className="not-found">Root not found</h1>
|
||||
}
|
|
@ -1,3 +1,7 @@
|
|||
body {
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.not-found {
|
||||
color: rgb(210, 105, 30); /* chocolate */
|
||||
}
|
||||
|
|
|
@ -194,6 +194,15 @@ createNextDescribe(
|
|||
).toBe('rgb(255, 0, 0)')
|
||||
})
|
||||
|
||||
it('should include root layout css for root not-found.js', async () => {
|
||||
const browser = await next.browser('/this-path-does-not-exist')
|
||||
expect(
|
||||
await browser.eval(
|
||||
`window.getComputedStyle(document.querySelector('h1')).color`
|
||||
)
|
||||
).toBe('rgb(210, 105, 30)')
|
||||
})
|
||||
|
||||
it('should include css imported in error.js', async () => {
|
||||
const browser = await next.browser('/error/client-component')
|
||||
await browser.elementByCss('button').click()
|
||||
|
|
Loading…
Reference in a new issue