diff --git a/packages/next/src/build/webpack/loaders/next-metadata-route-loader.ts b/packages/next/src/build/webpack/loaders/next-metadata-route-loader.ts index 6cbc1e6480232..508a50956d1e1 100644 --- a/packages/next/src/build/webpack/loaders/next-metadata-route-loader.ts +++ b/packages/next/src/build/webpack/loaders/next-metadata-route-loader.ts @@ -1,4 +1,5 @@ import type webpack from 'webpack' +import fs from 'fs' import path from 'path' import { METADATA_RESOURCE_QUERY } from './metadata/discover' import { imageExtMimeTypeMap } from '../../../lib/mime-type' @@ -36,27 +37,29 @@ function getContentType(resourcePath: string) { } // Strip metadata resource query string from `import.meta.url` to make sure the fs.readFileSync get the right path. -function getStaticRouteCode(resourcePath: string, fileBaseName: string) { +async function getStaticRouteCode(resourcePath: string, fileBaseName: string) { const cache = fileBaseName === 'favicon' ? 'public, max-age=0, must-revalidate' : process.env.NODE_ENV !== 'production' ? cacheHeader.none : cacheHeader.longCache - return `\ -import fs from 'fs' -import { fileURLToPath } from 'url' + const code = `\ import { NextResponse } from 'next/server' const contentType = ${JSON.stringify(getContentType(resourcePath))} -const resourceUrl = new URL(import.meta.url) -const filePath = fileURLToPath(resourceUrl).replace(${JSON.stringify( - METADATA_RESOURCE_QUERY - )}, '') +const buffer = Buffer.from(${JSON.stringify( + ( + await fs.promises.readFile( + resourcePath.replace(METADATA_RESOURCE_QUERY, ''), + { + encoding: 'utf-8', + } + ) + ).toString() + )}) -let buffer export function GET() { - if (!buffer) { buffer = fs.readFileSync(filePath) } return new NextResponse(buffer, { headers: { 'Content-Type': contentType, @@ -67,6 +70,7 @@ export function GET() { export const dynamic = 'force-static' ` + return code } function getDynamicTextRouteCode(resourcePath: string) { @@ -201,7 +205,7 @@ ${staticGenerationCode} // When it's static route, it could be favicon.ico, sitemap.xml, robots.txt etc. // TODO-METADATA: improve the cache control strategy const nextMetadataRouterLoader: webpack.LoaderDefinitionFunction = - function () { + async function () { const { resourcePath } = this const { pageExtensions, page } = this.getOptions() @@ -218,7 +222,7 @@ const nextMetadataRouterLoader: webpack.LoaderDefinitionFunction { const getTitle = (browser: BrowserInterface) => @@ -603,6 +602,15 @@ createNextDescribe( // No apple icon if it's not provided const $appleIcon = $('head > link[rel="apple-touch-icon"]') expect($appleIcon.length).toBe(0) + + const $dynamic = await next.render$('/icons/static/dynamic-routes/123') + const $dynamicIcon = $dynamic('head > link[rel="icon"]') + const dynamicIconHref = $dynamicIcon.attr('href') + expect(dynamicIconHref).toMatch( + /\/icons\/static\/dynamic-routes\/123\/icon\.png\?b76e8f0282c93c8e/ + ) + const dynamicIconRes = await next.fetch(dynamicIconHref) + expect(dynamicIconRes.status).toBe(200) }) if (isNextDev) {