@@ -283,6 +283,7 @@ function formatRouteToRouteType(route: string) {
283283
284284// Whether redirects and rewrites have been converted into routeTypes or not. 
285285let  redirectsRewritesTypesProcessed  =  false 
286+ let  collectedRootParams : Map < string ,  string [ ] >  =  new  Map ( ) 
286287
287288// Convert redirects and rewrites into routeTypes. 
288289function  addRedirectsRewritesRouteTypes ( 
@@ -584,6 +585,63 @@ function formatTimespanWithSeconds(seconds: undefined | number): string {
584585  return  text  +  ' ('  +  descriptive  +  ')' 
585586} 
586587
588+ function  getRootParamsFromLayouts ( layoutsMap : Map < string ,  string [ ] > )  { 
589+   let  shortestLayoutLength  =  Infinity 
590+   const  rootLayouts : {  route : string ;  params : string [ ]  } [ ]  =  [ ] 
591+   const  allRootParams  =  new  Set < string > ( ) 
592+ 
593+   for  ( const  [ route ,  params ]  of  layoutsMap . entries ( ) )  { 
594+     const  segments  =  route . split ( '/' ) 
595+     if  ( segments . length  <=  shortestLayoutLength )  { 
596+       if  ( segments . length  <  shortestLayoutLength )  { 
597+         rootLayouts . length  =  0  // Clear previous layouts if we found a shorter one 
598+         shortestLayoutLength  =  segments . length 
599+       } 
600+       rootLayouts . push ( {  route,  params } ) 
601+       params . forEach ( ( param )  =>  allRootParams . add ( param ) ) 
602+     } 
603+   } 
604+ 
605+   const  result  =  Array . from ( allRootParams ) . map ( ( param )  =>  ( { 
606+     param, 
607+     // if we detected multiple root layouts and not all of them have the param, 
608+     // then it needs to be marked optional in the type. 
609+     optional : ! rootLayouts . every ( ( layout )  =>  layout . params . includes ( param ) ) , 
610+   } ) ) 
611+ 
612+   return  result 
613+ } 
614+ 
615+ function  createServerDefinitions ( 
616+   rootParams : {  param : string ;  optional : boolean  } [ ] 
617+ )  { 
618+   return  ` 
619+   declare module 'next/server' { 
620+ 
621+     import type { AsyncLocalStorage as NodeAsyncLocalStorage } from 'async_hooks' 
622+     declare global { 
623+       var AsyncLocalStorage: typeof NodeAsyncLocalStorage 
624+     } 
625+     export { NextFetchEvent } from 'next/dist/server/web/spec-extension/fetch-event' 
626+     export { NextRequest } from 'next/dist/server/web/spec-extension/request' 
627+     export { NextResponse } from 'next/dist/server/web/spec-extension/response' 
628+     export { NextMiddleware, MiddlewareConfig } from 'next/dist/server/web/types' 
629+     export { userAgentFromString } from 'next/dist/server/web/spec-extension/user-agent' 
630+     export { userAgent } from 'next/dist/server/web/spec-extension/user-agent' 
631+     export { URLPattern } from 'next/dist/compiled/@edge-runtime/primitives/url' 
632+     export { ImageResponse } from 'next/dist/server/web/spec-extension/image-response' 
633+     export type { ImageResponseOptions } from 'next/dist/compiled/@vercel/og/types' 
634+     export { unstable_after } from 'next/dist/server/after' 
635+     export { connection } from 'next/dist/server/request/connection' 
636+     export type { UnsafeUnwrappedSearchParams } from 'next/dist/server/request/search-params' 
637+     export type { UnsafeUnwrappedParams } from 'next/dist/server/request/params' 
638+     export function unstable_rootParams(): Promise<{ ${ rootParams  
639+       . map ( ( {  param,  optional } )  =>  `${ param } ${ optional  ? '?'  : '' }  )  
640+       . join ( ', ' ) }   }>
641+   } 
642+   ` 
643+ } 
644+ 
587645function  createCustomCacheLifeDefinitions ( cacheLife : { 
588646  [ profile : string ] : CacheLife 
589647} )  { 
@@ -855,6 +913,22 @@ export class NextTypesPlugin {
855913      if  ( ! IS_IMPORTABLE )  return 
856914
857915      if  ( IS_LAYOUT )  { 
916+         const  rootLayoutPath  =  normalizeAppPath ( 
917+           ensureLeadingSlash ( 
918+             getPageFromPath ( 
919+               path . relative ( this . appDir ,  mod . resource ) , 
920+               this . pageExtensions 
921+             ) 
922+           ) 
923+         ) 
924+ 
925+         const  foundParams  =  Array . from ( 
926+           rootLayoutPath . matchAll ( / \[ ( .* ?) \] / g) , 
927+           ( match )  =>  match [ 1 ] 
928+         ) 
929+ 
930+         collectedRootParams . set ( rootLayoutPath ,  foundParams ) 
931+ 
858932        const  slots  =  await  collectNamedSlots ( mod . resource ) 
859933        assets [ assetPath ]  =  new  sources . RawSource ( 
860934          createTypeGuardFile ( mod . resource ,  relativeImportPath ,  { 
@@ -933,6 +1007,20 @@ export class NextTypesPlugin {
9331007
9341008          await  Promise . all ( promises ) 
9351009
1010+           const  rootParams  =  getRootParamsFromLayouts ( collectedRootParams ) 
1011+           // If we discovered rootParams, we'll override the `next/server` types 
1012+           // since we're able to determine the root params at build time. 
1013+           if  ( rootParams . length  >  0 )  { 
1014+             const  serverTypesPath  =  path . join ( 
1015+               assetDirRelative , 
1016+               'types/server.d.ts' 
1017+             ) 
1018+ 
1019+             assets [ serverTypesPath ]  =  new  sources . RawSource ( 
1020+               createServerDefinitions ( rootParams ) 
1021+             )  as  unknown  as  webpack . sources . RawSource 
1022+           } 
1023+ 
9361024          // Support `"moduleResolution": "Node16" | "NodeNext"` with `"type": "module"` 
9371025
9381026          const  packageJsonAssetPath  =  path . join ( 
0 commit comments