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 a566eb3d2e7bc..f66bd2d3912c7 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 @@ -3,6 +3,16 @@ import fs from 'fs' import path from 'path' import { imageExtMimeTypeMap } from '../../../lib/mime-type' +function errorOnBadHandler(resourcePath: string) { + return ` + if (typeof handler !== 'function') { + throw new Error('Default export is missing in ${JSON.stringify( + resourcePath + )}') + } + ` +} + const cacheHeader = { none: 'no-cache, no-store', longCache: 'public, immutable, no-transform, max-age=31536000', @@ -78,6 +88,8 @@ import { resolveRouteData } from 'next/dist/build/webpack/loaders/metadata/resol const contentType = ${JSON.stringify(getContentType(resourcePath))} const fileType = ${JSON.stringify(getFilenameAndExtension(resourcePath).name)} +${errorOnBadHandler(resourcePath)} + export async function GET() { const data = await handler() const content = resolveRouteData(data, fileType) @@ -103,6 +115,8 @@ const imageModule = { ...userland } const handler = imageModule.default const generateImageMetadata = imageModule.generateImageMetadata +${errorOnBadHandler(resourcePath)} + export async function GET(_, ctx) { const { __metadata_id__ = [], ...params } = ctx.params || {} const targetId = __metadata_id__[0] @@ -160,6 +174,8 @@ const generateSitemaps = sitemapModule.generateSitemaps const contentType = ${JSON.stringify(getContentType(resourcePath))} const fileType = ${JSON.stringify(getFilenameAndExtension(resourcePath).name)} +${errorOnBadHandler(resourcePath)} + ${'' /* re-export the userland route configs */} export * from ${JSON.stringify(resourcePath)} diff --git a/test/e2e/app-dir/metadata-dynamic-routes/index.test.ts b/test/e2e/app-dir/metadata-dynamic-routes/index.test.ts index 60c77d18241b3..188575f9ae6d0 100644 --- a/test/e2e/app-dir/metadata-dynamic-routes/index.test.ts +++ b/test/e2e/app-dir/metadata-dynamic-routes/index.test.ts @@ -497,6 +497,33 @@ createNextDescribe( await next.fetch('/metadata-base/unset/sitemap.xml/0') } }) + + it('should error if the default export of dynamic image is missing', async () => { + const ogImageFilePath = 'app/opengraph-image.tsx' + const ogImageFileContent = await next.readFile(ogImageFilePath) + const ogImageFileContentWithoutDefaultExport = + ogImageFileContent.replace( + 'export default function', + 'export function' + ) + + try { + await next.patchFile( + ogImageFilePath, + ogImageFileContentWithoutDefaultExport + ) + const currentNextCliOutputLength = next.cliOutput.length + + await check(async () => { + await next.fetch('/opengraph-image') + const output = next.cliOutput.slice(currentNextCliOutputLength) + expect(output).toContain(`Default export is missing in`) + return 'success' + }, /success/) + } finally { + await next.patchFile(ogImageFilePath, ogImageFileContent) + } + }) } if (isNextStart) {