@@ -97,6 +97,7 @@ import {
97
97
printTreeView ,
98
98
copyTracedFiles ,
99
99
isReservedPage ,
100
+ AppConfig ,
100
101
} from './utils'
101
102
import getBaseWebpackConfig from './webpack-config'
102
103
import { PagesManifest } from './webpack/plugins/pages-manifest-plugin'
@@ -1065,6 +1066,11 @@ export default async function build(
1065
1066
const serverPropsPages = new Set < string > ( )
1066
1067
const additionalSsgPaths = new Map < string , Array < string > > ( )
1067
1068
const additionalSsgPathsEncoded = new Map < string , Array < string > > ( )
1069
+ const appStaticPaths = new Map < string , Array < string > > ( )
1070
+ const appStaticPathsEncoded = new Map < string , Array < string > > ( )
1071
+ const appNormalizedPaths = new Map < string , string > ( )
1072
+ const appDynamicParamPaths = new Set < string > ( )
1073
+ const appDefaultConfigs = new Map < string , AppConfig > ( )
1068
1074
const pageTraceIncludes = new Map < string , Array < string > > ( )
1069
1075
const pageTraceExcludes = new Map < string , Array < string > > ( )
1070
1076
const pageInfos = new Map < string , PageInfo > ( )
@@ -1087,6 +1093,26 @@ export default async function build(
1087
1093
: require . resolve ( './utils' )
1088
1094
let infoPrinted = false
1089
1095
1096
+ let appPathsManifest : Record < string , string > = { }
1097
+ const appPathRoutes : Record < string , string > = { }
1098
+
1099
+ if ( appDir ) {
1100
+ appPathsManifest = JSON . parse (
1101
+ await promises . readFile (
1102
+ path . join ( distDir , serverDir , APP_PATHS_MANIFEST ) ,
1103
+ 'utf8'
1104
+ )
1105
+ )
1106
+
1107
+ Object . keys ( appPathsManifest ) . forEach ( ( entry ) => {
1108
+ appPathRoutes [ entry ] = normalizeAppPath ( entry ) || '/'
1109
+ } )
1110
+ await promises . writeFile (
1111
+ path . join ( distDir , APP_PATH_ROUTES_MANIFEST ) ,
1112
+ JSON . stringify ( appPathRoutes , null , 2 )
1113
+ )
1114
+ }
1115
+
1090
1116
process . env . NEXT_PHASE = PHASE_PRODUCTION_BUILD
1091
1117
1092
1118
const staticWorkers = new Worker ( staticWorker , {
@@ -1255,33 +1281,49 @@ export default async function build(
1255
1281
let isHybridAmp = false
1256
1282
let ssgPageRoutes : string [ ] | null = null
1257
1283
1258
- const pagePath =
1259
- pageType === 'pages'
1260
- ? pagesPaths . find (
1261
- ( p ) =>
1262
- p . startsWith ( actualPage + '.' ) ||
1263
- p . startsWith ( actualPage + '/index.' )
1284
+ let pagePath = ''
1285
+
1286
+ if ( pageType === 'pages' ) {
1287
+ pagePath =
1288
+ pagesPaths . find (
1289
+ ( p ) =>
1290
+ p . startsWith ( actualPage + '.' ) ||
1291
+ p . startsWith ( actualPage + '/index.' )
1292
+ ) || ''
1293
+ }
1294
+ let originalAppPath : string | undefined
1295
+
1296
+ if ( pageType === 'app' && mappedAppPages ) {
1297
+ for ( const [ originalPath , normalizedPath ] of Object . entries (
1298
+ appPathRoutes
1299
+ ) ) {
1300
+ if ( normalizedPath === page ) {
1301
+ pagePath = mappedAppPages [ originalPath ] . replace (
1302
+ / ^ p r i v a t e - n e x t - a p p - d i r / ,
1303
+ ''
1264
1304
)
1265
- : appPaths ?. find ( ( p ) => p . startsWith ( actualPage + '/page.' ) )
1305
+ originalAppPath = originalPath
1306
+ break
1307
+ }
1308
+ }
1309
+ }
1266
1310
1267
- const staticInfo =
1268
- pagesDir && pageType === 'pages' && pagePath
1269
- ? await getPageStaticInfo ( {
1270
- pageFilePath : join ( pagesDir , pagePath ) ,
1271
- nextConfig : config ,
1272
- } )
1273
- : { }
1274
- const pageRuntime = staticInfo . runtime
1311
+ const staticInfo = pagePath
1312
+ ? await getPageStaticInfo ( {
1313
+ pageFilePath : join (
1314
+ ( pageType === 'pages' ? pagesDir : appDir ) || '' ,
1315
+ pagePath
1316
+ ) ,
1317
+ nextConfig : config ,
1318
+ } )
1319
+ : undefined
1320
+
1321
+ const pageRuntime = staticInfo ?. runtime
1275
1322
isServerComponent =
1276
1323
pageType === 'app' &&
1277
- staticInfo . rsc !== RSC_MODULE_TYPES . client
1324
+ staticInfo ? .rsc !== RSC_MODULE_TYPES . client
1278
1325
1279
- if (
1280
- // Only calculate page static information if the page is not an
1281
- // app page.
1282
- pageType !== 'app' &&
1283
- ! isReservedPage ( page )
1284
- ) {
1326
+ if ( ! isReservedPage ( page ) ) {
1285
1327
try {
1286
1328
let edgeInfo : any
1287
1329
@@ -1291,8 +1333,10 @@ export default async function build(
1291
1333
serverDir ,
1292
1334
MIDDLEWARE_MANIFEST
1293
1335
) )
1336
+ const manifestKey =
1337
+ pageType === 'pages' ? page : join ( page , 'page' )
1294
1338
1295
- edgeInfo = manifest . functions [ page ]
1339
+ edgeInfo = manifest . functions [ manifestKey ]
1296
1340
}
1297
1341
1298
1342
let isPageStaticSpan =
@@ -1301,6 +1345,7 @@ export default async function build(
1301
1345
( ) => {
1302
1346
return staticWorkers . isPageStatic ( {
1303
1347
page,
1348
+ originalAppPath,
1304
1349
distDir,
1305
1350
serverless : isLikeServerless ,
1306
1351
configFileName,
@@ -1311,10 +1356,50 @@ export default async function build(
1311
1356
parentId : isPageStaticSpan . id ,
1312
1357
pageRuntime,
1313
1358
edgeInfo,
1359
+ pageType,
1360
+ hasServerComponents,
1314
1361
} )
1315
1362
}
1316
1363
)
1317
1364
1365
+ if ( pageType === 'app' && originalAppPath ) {
1366
+ appNormalizedPaths . set ( originalAppPath , page )
1367
+
1368
+ // TODO-APP: handle prerendering with edge
1369
+ // runtime
1370
+ if ( pageRuntime === 'experimental-edge' ) {
1371
+ return
1372
+ }
1373
+
1374
+ if (
1375
+ workerResult . encodedPrerenderRoutes &&
1376
+ workerResult . prerenderRoutes
1377
+ ) {
1378
+ appStaticPaths . set (
1379
+ originalAppPath ,
1380
+ workerResult . prerenderRoutes
1381
+ )
1382
+ appStaticPathsEncoded . set (
1383
+ originalAppPath ,
1384
+ workerResult . encodedPrerenderRoutes
1385
+ )
1386
+ }
1387
+ if ( ! isDynamicRoute ( page ) ) {
1388
+ appStaticPaths . set ( originalAppPath , [ page ] )
1389
+ appStaticPathsEncoded . set ( originalAppPath , [ page ] )
1390
+ }
1391
+ if ( workerResult . prerenderFallback ) {
1392
+ // whether or not to allow requests for paths not
1393
+ // returned from generateStaticParams
1394
+ appDynamicParamPaths . add ( originalAppPath )
1395
+ }
1396
+ appDefaultConfigs . set (
1397
+ originalAppPath ,
1398
+ workerResult . appConfig || { }
1399
+ )
1400
+ return
1401
+ }
1402
+
1318
1403
if ( pageRuntime === SERVER_RUNTIME . edge ) {
1319
1404
if ( workerResult . hasStaticProps ) {
1320
1405
console . warn (
@@ -1821,24 +1906,6 @@ export default async function build(
1821
1906
'utf8'
1822
1907
)
1823
1908
1824
- if ( appDir ) {
1825
- const appPathsManifest = JSON . parse (
1826
- await promises . readFile (
1827
- path . join ( distDir , serverDir , APP_PATHS_MANIFEST ) ,
1828
- 'utf8'
1829
- )
1830
- )
1831
- const appPathRoutes : Record < string , string > = { }
1832
-
1833
- Object . keys ( appPathsManifest ) . forEach ( ( entry ) => {
1834
- appPathRoutes [ entry ] = normalizeAppPath ( entry ) || '/'
1835
- } )
1836
- await promises . writeFile (
1837
- path . join ( distDir , APP_PATH_ROUTES_MANIFEST ) ,
1838
- JSON . stringify ( appPathRoutes , null , 2 )
1839
- )
1840
- }
1841
-
1842
1909
const middlewareManifest : MiddlewareManifest = JSON . parse (
1843
1910
await promises . readFile (
1844
1911
path . join ( distDir , serverDir , MIDDLEWARE_MANIFEST ) ,
@@ -1865,6 +1932,7 @@ export default async function build(
1865
1932
}
1866
1933
1867
1934
const finalPrerenderRoutes : { [ route : string ] : SsgRoute } = { }
1935
+ const finalDynamicRoutes : PrerenderManifest [ 'dynamicRoutes' ] = { }
1868
1936
const tbdPrerenderRoutes : string [ ] = [ ]
1869
1937
let ssgNotFoundPaths : string [ ] = [ ]
1870
1938
@@ -1889,7 +1957,16 @@ export default async function build(
1889
1957
1890
1958
const combinedPages = [ ...staticPages , ...ssgPages ]
1891
1959
1892
- if ( combinedPages . length > 0 || useStatic404 || useDefaultStatic500 ) {
1960
+ // we need to trigger automatic exporting when we have
1961
+ // - static 404/500
1962
+ // - getStaticProps paths
1963
+ // - experimental app is enabled
1964
+ if (
1965
+ combinedPages . length > 0 ||
1966
+ useStatic404 ||
1967
+ useDefaultStatic500 ||
1968
+ config . experimental . appDir
1969
+ ) {
1893
1970
const staticGenerationSpan =
1894
1971
nextBuildSpan . traceChild ( 'static-generation' )
1895
1972
await staticGenerationSpan . traceAsyncFn ( async ( ) => {
@@ -1986,6 +2063,20 @@ export default async function build(
1986
2063
}
1987
2064
}
1988
2065
2066
+ // TODO: output manifest specific to app paths and their
2067
+ // revalidate periods and dynamicParams settings
2068
+ appStaticPaths . forEach ( ( routes , originalAppPath ) => {
2069
+ const encodedRoutes = appStaticPathsEncoded . get ( originalAppPath )
2070
+
2071
+ routes . forEach ( ( route , routeIdx ) => {
2072
+ defaultMap [ route ] = {
2073
+ page : originalAppPath ,
2074
+ query : { __nextSsgPath : encodedRoutes ?. [ routeIdx ] } ,
2075
+ _isAppDir : true ,
2076
+ }
2077
+ } )
2078
+ } )
2079
+
1989
2080
if ( i18n ) {
1990
2081
for ( const page of [
1991
2082
...staticPages ,
@@ -2035,6 +2126,58 @@ export default async function build(
2035
2126
await promises . unlink ( serverBundle )
2036
2127
}
2037
2128
2129
+ for ( const [ originalAppPath , routes ] of appStaticPaths ) {
2130
+ const page = appNormalizedPaths . get ( originalAppPath ) || ''
2131
+ const appConfig = appDefaultConfigs . get ( originalAppPath ) || { }
2132
+ let hasDynamicData = appConfig . revalidate === 0
2133
+
2134
+ routes . forEach ( ( route ) => {
2135
+ let revalidate = exportConfig . initialPageRevalidationMap [ route ]
2136
+
2137
+ if ( typeof revalidate === 'undefined' ) {
2138
+ revalidate =
2139
+ typeof appConfig . revalidate !== 'undefined'
2140
+ ? appConfig . revalidate
2141
+ : false
2142
+ }
2143
+ if ( revalidate !== 0 ) {
2144
+ const normalizedRoute = normalizePagePath ( route )
2145
+ const dataRoute = path . posix . join ( `${ normalizedRoute } .rsc` )
2146
+ finalPrerenderRoutes [ route ] = {
2147
+ initialRevalidateSeconds : revalidate ,
2148
+ srcRoute : page ,
2149
+ dataRoute,
2150
+ }
2151
+ } else {
2152
+ hasDynamicData = true
2153
+ }
2154
+ } )
2155
+
2156
+ if ( ! hasDynamicData && isDynamicRoute ( originalAppPath ) ) {
2157
+ const normalizedRoute = normalizePagePath ( page )
2158
+ const dataRoute = path . posix . join ( `${ normalizedRoute } .rsc` )
2159
+
2160
+ // TODO: create a separate manifest to allow enforcing
2161
+ // dynamicParams for non-static paths?
2162
+ finalDynamicRoutes [ page ] = {
2163
+ routeRegex : normalizeRouteRegex (
2164
+ getNamedRouteRegex ( page ) . re . source
2165
+ ) ,
2166
+ dataRoute,
2167
+ // if dynamicParams are enabled treat as fallback:
2168
+ // 'blocking' if not it's fallback: false
2169
+ fallback : appDynamicParamPaths . has ( originalAppPath )
2170
+ ? null
2171
+ : false ,
2172
+ dataRouteRegex : normalizeRouteRegex (
2173
+ getNamedRouteRegex (
2174
+ dataRoute . replace ( / \. r s c $ / , '' )
2175
+ ) . re . source . replace ( / \( \? : \\ \/ \) \? \$ $ / , '\\.rsc$' )
2176
+ ) ,
2177
+ }
2178
+ }
2179
+ }
2180
+
2038
2181
const moveExportedPage = async (
2039
2182
originPage : string ,
2040
2183
page : string ,
@@ -2347,8 +2490,7 @@ export default async function build(
2347
2490
telemetry . record ( eventPackageUsedInGetServerSideProps ( telemetryPlugin ) )
2348
2491
}
2349
2492
2350
- if ( ssgPages . size > 0 ) {
2351
- const finalDynamicRoutes : PrerenderManifest [ 'dynamicRoutes' ] = { }
2493
+ if ( ssgPages . size > 0 || appDir ) {
2352
2494
tbdPrerenderRoutes . forEach ( ( tbdRoute ) => {
2353
2495
const normalizedRoute = normalizePagePath ( tbdRoute )
2354
2496
const dataRoute = path . posix . join (
0 commit comments