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 e49475b52fb4c..14159ef2d3f5e 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', @@ -85,7 +106,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' @@ -96,6 +120,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() @@ -112,7 +137,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' @@ -124,6 +152,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 || {} @@ -162,10 +191,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') @@ -195,15 +220,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__: id, ...params } = ctx.params || {} @@ -253,11 +270,11 @@ const nextMetadataRouterLoader: webpack.LoaderDefinitionFunction + Open Graph + + ) + ) +} + +export const revalidate = 5 diff --git a/test/e2e/app-dir/metadata-revalidate/app/robots.ts b/test/e2e/app-dir/metadata-revalidate/app/robots.ts new file mode 100644 index 0000000000000..83daa5af5f719 --- /dev/null +++ b/test/e2e/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/e2e/app-dir/metadata-revalidate/app/sitemap.ts b/test/e2e/app-dir/metadata-revalidate/app/sitemap.ts new file mode 100644 index 0000000000000..8f301a61ef068 --- /dev/null +++ b/test/e2e/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/e2e/app-dir/metadata-revalidate/metadata-revalidate.test.ts b/test/e2e/app-dir/metadata-revalidate/metadata-revalidate.test.ts new file mode 100644 index 0000000000000..b046c05077d5d --- /dev/null +++ b/test/e2e/app-dir/metadata-revalidate/metadata-revalidate.test.ts @@ -0,0 +1,31 @@ +import { nextTestSetup } from 'e2e-utils' + +describe('app-dir - metadata-revalidate', () => { + const { next, isNextStart } = nextTestSetup({ + files: __dirname, + }) + + it('should contain the routes in prerender manifest', async () => { + if (isNextStart) { + 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) + } + }) +})