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

Respect reexports from metadata API routes #70508

Merged
merged 3 commits into from
Sep 26, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,27 @@ function errorOnBadHandler(resourcePath: string) {
`
}

/* re-export the userland route configs */
async function createReExportsCode(
resourcePath: string,
loaderContext: webpack.LoaderContext<any>
) {
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',
Expand Down Expand Up @@ -85,7 +106,10 @@ export const dynamic = 'force-static'
return code
}

function getDynamicTextRouteCode(resourcePath: string) {
async function getDynamicTextRouteCode(
resourcePath: string,
loaderContext: webpack.LoaderContext<any>
) {
return `\
/* dynamic asset route */
import { NextResponse } from 'next/server'
Expand All @@ -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()
Expand All @@ -112,7 +137,10 @@ export async function GET() {
}

// <metadata-image>/[id]/route.js
function getDynamicImageRouteCode(resourcePath: string) {
async function getDynamicImageRouteCode(
resourcePath: string,
loaderContext: webpack.LoaderContext<any>
) {
return `\
/* dynamic image route */
import { NextResponse } from 'next/server'
Expand All @@ -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 || {}
Expand Down Expand Up @@ -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')

Expand Down Expand Up @@ -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 || {}
Expand Down Expand Up @@ -253,11 +270,11 @@ const nextMetadataRouterLoader: webpack.LoaderDefinitionFunction<MetadataRouteLo
let code = ''
if (isDynamicRouteExtension === '1') {
if (fileBaseName === 'robots' || fileBaseName === 'manifest') {
code = getDynamicTextRouteCode(filePath)
code = await getDynamicTextRouteCode(filePath, this)
} else if (fileBaseName === 'sitemap') {
code = await getDynamicSitemapRouteCode(filePath, this)
} else {
code = getDynamicImageRouteCode(filePath)
code = await getDynamicImageRouteCode(filePath, this)
}
} else {
code = await getStaticAssetRouteCode(filePath, fileBaseName)
Expand Down
22 changes: 22 additions & 0 deletions test/production/app-dir/metadata-revalidate/app/manifest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { MetadataRoute } from 'next'

export default function manifest(): MetadataRoute.Manifest {
return {
name: 'Next.js App',
short_name: 'Next.js App',
description: 'Next.js App',
start_url: '/',
display: 'standalone',
background_color: '#fff',
theme_color: '#fff',
icons: [
{
src: '/favicon.ico',
sizes: 'any',
type: 'image/x-icon',
},
],
}
}

export const revalidate = 5
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ImageResponse } from 'next/og'

/* without generateImageMetadata */
export default function og() {
return new ImageResponse(
(
<div
style={{
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 128,
background: 'lavender',
}}
>
Open Graph
</div>
)
)
}

export const revalidate = 5
14 changes: 14 additions & 0 deletions test/production/app-dir/metadata-revalidate/app/robots.ts
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions test/production/app-dir/metadata-revalidate/app/sitemap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default function sitemap() {
return [
{
url: 'https://acme.com',
lastModified: new Date(),
changeFrequency: 'yearly',
priority: 1,
},
]
}

export const revalidate = 5
Original file line number Diff line number Diff line change
@@ -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)
})
})
9 changes: 9 additions & 0 deletions test/turbopack-build-tests-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -16300,6 +16300,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": ["middleware-typescript should have built and started"],
"failed": [],
Expand Down
Loading