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 3f67dfe1b3360..f8eb52016d18c 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 @@ -14,6 +14,27 @@ function errorOnBadHandler(resourcePath: string) { ` } +/* re-export the userland route configs */ +async function createReExportsCode( + resourcePath: string, + loaderContext: webpack.LoaderContext +) { + const exportNames = await getLoaderModuleNamedExports( + resourcePath, + loaderContext + ) + // Re-export configs but avoid conflicted exports + const reExportNames = exportNames.filter( + (name) => name !== 'default' && name !== 'generateSitemaps' + ) + + return reExportNames.length > 0 + ? `export { ${reExportNames.join(', ')} } from ${JSON.stringify( + resourcePath + )}\n` + : '' +} + const cacheHeader = { none: 'no-cache, no-store', longCache: 'public, immutable, no-transform, max-age=31536000', @@ -83,7 +104,10 @@ export const dynamic = 'force-static' return code } -function getDynamicTextRouteCode(resourcePath: string) { +async function getDynamicTextRouteCode( + resourcePath: string, + loaderContext: webpack.LoaderContext +) { return `\ /* dynamic asset route */ import { NextResponse } from 'next/server' @@ -94,6 +118,7 @@ const contentType = ${JSON.stringify(getContentType(resourcePath))} const fileType = ${JSON.stringify(getFilenameAndExtension(resourcePath).name)} ${errorOnBadHandler(resourcePath)} +${await createReExportsCode(resourcePath, loaderContext)} export async function GET() { const data = await handler() @@ -110,7 +135,10 @@ export async function GET() { } // /[id]/route.js -function getDynamicImageRouteCode(resourcePath: string) { +async function getDynamicImageRouteCode( + resourcePath: string, + loaderContext: webpack.LoaderContext +) { return `\ /* dynamic image route */ import { NextResponse } from 'next/server' @@ -122,6 +150,7 @@ const handler = imageModule.default const generateImageMetadata = imageModule.generateImageMetadata ${errorOnBadHandler(resourcePath)} +${await createReExportsCode(resourcePath, loaderContext)} export async function GET(_, ctx) { const { __metadata_id__, ...params } = ctx.params || {} @@ -160,10 +189,6 @@ async function getDynamicSiteMapRouteCode( resourcePath, loaderContext ) - // Re-export configs but avoid conflicted exports - const reExportNames = exportNames.filter( - (name) => name !== 'default' && name !== 'generateSitemaps' - ) const hasGenerateSiteMaps = exportNames.includes('generateSitemaps') if ( @@ -197,15 +222,7 @@ const contentType = ${JSON.stringify(getContentType(resourcePath))} const fileType = ${JSON.stringify(getFilenameAndExtension(resourcePath).name)} ${errorOnBadHandler(resourcePath)} - -${'' /* re-export the userland route configs */} -${ - reExportNames.length > 0 - ? `export { ${reExportNames.join(', ')} } from ${JSON.stringify( - resourcePath - )}\n` - : '' -} +${await createReExportsCode(resourcePath, loaderContext)} export async function GET(_, ctx) { const { __metadata_id__, ...params } = ctx.params || {} @@ -266,11 +283,11 @@ const nextMetadataRouterLoader: webpack.LoaderDefinitionFunction + Open Graph + + ) + ) +} + +export const revalidate = 5 diff --git a/test/production/app-dir/metadata-revalidate/app/robots.ts b/test/production/app-dir/metadata-revalidate/app/robots.ts new file mode 100644 index 0000000000000..83daa5af5f719 --- /dev/null +++ b/test/production/app-dir/metadata-revalidate/app/robots.ts @@ -0,0 +1,14 @@ +import type { MetadataRoute } from 'next' + +export default function robots(): MetadataRoute.Robots { + return { + rules: { + userAgent: '*', + allow: '/', + disallow: '/private/', + }, + sitemap: 'https://acme.com/sitemap.xml', + } +} + +export const revalidate = 5 diff --git a/test/production/app-dir/metadata-revalidate/app/sitemap.ts b/test/production/app-dir/metadata-revalidate/app/sitemap.ts new file mode 100644 index 0000000000000..8f301a61ef068 --- /dev/null +++ b/test/production/app-dir/metadata-revalidate/app/sitemap.ts @@ -0,0 +1,12 @@ +export default function sitemap() { + return [ + { + url: 'https://acme.com', + lastModified: new Date(), + changeFrequency: 'yearly', + priority: 1, + }, + ] +} + +export const revalidate = 5 diff --git a/test/production/app-dir/metadata-revalidate/metadata-revalidate.test.ts b/test/production/app-dir/metadata-revalidate/metadata-revalidate.test.ts new file mode 100644 index 0000000000000..e4a0517d274d1 --- /dev/null +++ b/test/production/app-dir/metadata-revalidate/metadata-revalidate.test.ts @@ -0,0 +1,26 @@ +import { nextTestSetup } from 'e2e-utils' + +describe('app-dir - metadata-revalidate', () => { + const { next } = nextTestSetup({ + files: __dirname, + }) + + it('should contain the routes in prerender manifest', async () => { + const manifestContent = await next.readFile('.next/prerender-manifest.json') + const prerenderManifest = JSON.parse(manifestContent) + + expect( + prerenderManifest.routes['/revalidate/og/opengraph-image'] + .initialRevalidateSeconds + ).toBe(5) + expect( + prerenderManifest.routes['/manifest.webmanifest'].initialRevalidateSeconds + ).toBe(5) + expect( + prerenderManifest.routes['/robots.txt'].initialRevalidateSeconds + ).toBe(5) + expect( + prerenderManifest.routes['/sitemap.xml'].initialRevalidateSeconds + ).toBe(5) + }) +}) diff --git a/test/turbopack-build-tests-manifest.json b/test/turbopack-build-tests-manifest.json index 59c96caf3710a..f9bec72fc790b 100644 --- a/test/turbopack-build-tests-manifest.json +++ b/test/turbopack-build-tests-manifest.json @@ -15075,6 +15075,15 @@ "flakey": [], "runtimeError": false }, + "test/production/app-dir/metadata-revalidate/metadata-revalidate.test.ts": { + "passed": [], + "failed": [ + "app-dir - metadata-revalidate should contain the routes in prerender manifest" + ], + "pending": [], + "flakey": [], + "runtimeError": false + }, "test/production/middleware-typescript/test/index.test.ts": { "passed": [], "failed": ["middleware-typescript should have built and started"],