Repository: vercel/next.js
Pull request: [metadata] remove falsy dynamicParams approach

Do not use `dynamicParams = false` in metadata to handle 404, which will be incompatible with cache components. Hence we remove it and use the manual 404 check and response to handle unmatched ids returned from `generateSitemaps` and `generateImageMetadata`

Recommend to review with enabling "hide the whitespace"

Changed files:
filename: crates/next-core/src/next_app/metadata/route.rs
patch: @@ -291,26 +291,24 @@ async fn dynamic_sitemap_route_with_generate_source(
                 const paramsPromise = ctx.params
                 const idPromise = paramsPromise.then(params => params?.__metadata_id__)
 
-                if (process.env.NODE_ENV !== 'production') {{
-                    const id = await idPromise
-                    const hasXmlExtension = id ? id.endsWith('.xml') : false
-                    const sitemaps = await generateSitemaps()
-                    let foundId
-                    for (const item of sitemaps) {{
-                        if (item?.id == null) {{
-                            throw new Error('id property is required for every item returned from generateSitemaps')
-                        }}
-                        const baseId = id && hasXmlExtension ? id.slice(0, -4) : undefined
-                        if (item.id.toString() === baseId) {{
-                            foundId = item.id
-                        }}
+                const id = await idPromise
+                const hasXmlExtension = id ? id.endsWith('.xml') : false
+                const sitemaps = await generateSitemaps()
+                let foundId
+                for (const item of sitemaps) {{
+                    if (item?.id == null) {{
+                        throw new Error('id property is required for every item returned from generateSitemaps')
                     }}
-                    if (foundId == null) {{
-                        return new NextResponse('Not Found', {{
-                            status: 404,
-                        }})
+                    const baseId = id && hasXmlExtension ? id.slice(0, -4) : undefined
+                    if (item.id.toString() === baseId) {{
+                        foundId = item.id
                     }}
                 }}
+                if (foundId == null) {{
+                    return new NextResponse('Not Found', {{
+                        status: 404,
+                    }})
+                }}
                 
                 const targetIdPromise = idPromise.then(id => {{
                     const hasXmlExtension = id ? id.endsWith('.xml') : false
@@ -329,7 +327,6 @@ async fn dynamic_sitemap_route_with_generate_source(
 
             export * from {resource_path}
 
-            export const dynamicParams = false
             export async function generateStaticParams() {{
                 const sitemaps = await generateSitemaps()
                 const params = []
@@ -445,32 +442,29 @@ async fn dynamic_image_route_with_metadata_source(
                     const {{ __metadata_id__, ...rest }} = params
                     return rest
                 }})
-                
-                if (process.env.NODE_ENV !== 'production') {{
-                    const restParams = await restParamsPromise
-                    const __metadata_id__ = await idPromise
-                    const imageMetadata = await generateImageMetadata({{ params: restParams }})
-                    const id = imageMetadata.find((item) => {{
-                        if (item?.id == null) {{
-                            throw new Error('id property is required for every item returned from generateImageMetadata')
-                        }}
-
-                        return item.id.toString() === __metadata_id__
-                    }})?.id
-
-                    if (id == null) {{
-                        return new NextResponse('Not Found', {{
-                            status: 404,
-                        }})
+
+                const restParams = await restParamsPromise
+                const __metadata_id__ = await idPromise
+                const imageMetadata = await generateImageMetadata({{ params: restParams }})
+                const id = imageMetadata.find((item) => {{
+                    if (item?.id == null) {{
+                        throw new Error('id property is required for every item returned from generateImageMetadata')
                     }}
+
+                    return item.id.toString() === __metadata_id__
+                }})?.id
+
+                if (id == null) {{
+                    return new NextResponse('Not Found', {{
+                        status: 404,
+                    }})
                 }}
 
                 return handler({{ params: restParamsPromise, id: idPromise }})
             }}
 
             export * from {resource_path}
 
-            export const dynamicParams = false
             export async function generateStaticParams({{ params }}) {{
                 const imageMetadata = await generateImageMetadata({{ params }})
                 const staticParams = []

filename: packages/next/src/build/webpack/loaders/next-metadata-route-loader.ts
patch: @@ -176,31 +176,27 @@ export async function GET(_, ctx) {
     const { __metadata_id__, ...rest } = params
     return rest
   })
-  
-  ${/* we need a dev assertion for id since dynamicParams=false won't work well in dev */ ''}
-  if (process.env.NODE_ENV !== 'production') {
-    const restParams = await restParamsPromise
-    const __metadata_id__ = await idPromise
-    const imageMetadata = await generateImageMetadata({ params: restParams })
-    const id = imageMetadata.find((item) => {
-      if (item?.id == null) {
-        throw new Error('id property is required for every item returned from generateImageMetadata')
-      }
-
-      return item.id.toString() === __metadata_id__
-    })?.id
 
-    if (id == null) {
-      return new NextResponse('Not Found', {
-        status: 404,
-      })
+  const restParams = await restParamsPromise
+  const __metadata_id__ = await idPromise
+  const imageMetadata = await generateImageMetadata({ params: restParams })
+  const id = imageMetadata.find((item) => {
+    if (item?.id == null) {
+      throw new Error('id property is required for every item returned from generateImageMetadata')
     }
+
+    return item.id.toString() === __metadata_id__
+  })?.id
+
+  if (id == null) {
+    return new NextResponse('Not Found', {
+      status: 404,
+    })
   }
 
   return handler({ params: restParamsPromise, id: idPromise })
 }
 
-export const dynamicParams = false
 export async function generateStaticParams({ params }) {
   const imageMetadata = await generateImageMetadata({ params })
   const staticParams = []
@@ -303,27 +299,25 @@ export async function GET(_, ctx) {
   const paramsPromise = ctx.params
   const idPromise = paramsPromise.then(params => params?.__metadata_id__)
 
-  if (process.env.NODE_ENV !== 'production') {
-    const id = await idPromise
-    const hasXmlExtension = id ? id.endsWith('.xml') : false
-    const sitemaps = await generateSitemaps()
-    let foundId
-    for (const item of sitemaps) {
-      if (item?.id == null) {
-        throw new Error('id property is required for every item returned from generateSitemaps')
-      }
-      
-      const baseId = id && hasXmlExtension ? id.slice(0, -4) : undefined
-      if (item.id.toString() === baseId) {
-        foundId = item.id
-      }
+  const id = await idPromise
+  const hasXmlExtension = id ? id.endsWith('.xml') : false
+  const sitemaps = await generateSitemaps()
+  let foundId
+  for (const item of sitemaps) {
+    if (item?.id == null) {
+      throw new Error('id property is required for every item returned from generateSitemaps')
     }
-    if (foundId == null) {
-      return new NextResponse('Not Found', {
-        status: 404,
-      })
+
+    const baseId = id && hasXmlExtension ? id.slice(0, -4) : undefined
+    if (item.id.toString() === baseId) {
+      foundId = item.id
     }
   }
+  if (foundId == null) {
+    return new NextResponse('Not Found', {
+      status: 404,
+    })
+  }
 
   const targetIdPromise = idPromise.then(id => {
     const hasXmlExtension = id ? id.endsWith('.xml') : false
@@ -340,7 +334,6 @@ export async function GET(_, ctx) {
   })
 }
 
-export const dynamicParams = false
 export async function generateStaticParams() {
   const sitemaps = await generateSitemaps()
   const params = []