@@ -4,14 +4,15 @@ import type { PagesManifest } from './webpack/plugins/pages-manifest-plugin'
4
4
import type { ExportPathMap , NextConfigComplete } from '../server/config-shared'
5
5
import type { MiddlewareManifest } from './webpack/plugins/middleware-plugin'
6
6
import type { ActionManifest } from './webpack/plugins/flight-client-entry-plugin'
7
- import type { ExportOptions } from '../export'
7
+ import type { ExportAppOptions , ExportAppWorker } from '../export/types '
8
8
9
9
import '../lib/setup-exception-listeners'
10
+
10
11
import { loadEnvConfig } from '@next/env'
11
12
import { bold , yellow , green } from '../lib/picocolors'
12
13
import crypto from 'crypto'
13
14
import { isMatch , makeRe } from 'next/dist/compiled/micromatch'
14
- import { promises as fs , existsSync as fsExistsSync } from 'fs'
15
+ import fs from 'fs/promises '
15
16
import os from 'os'
16
17
import { Worker } from '../lib/worker'
17
18
import { defaultConfig } from '../server/config-shared'
@@ -148,22 +149,31 @@ import { initialize as initializeIncrementalCache } from '../server/lib/incremen
148
149
import { nodeFs } from '../server/lib/node-fs-methods'
149
150
import { collectBuildTraces } from './collect-build-traces'
150
151
import { BuildTraceContext } from './webpack/plugins/next-trace-entrypoints-plugin'
152
+ import { formatManifest } from './manifests/formatter/format-manifest'
153
+
154
+ interface ExperimentalBypassForInfo {
155
+ experimentalBypassFor ?: RouteHas [ ]
156
+ }
157
+
158
+ interface DataRouteRouteInfo {
159
+ dataRoute : string | null
160
+ }
151
161
152
- export type SsgRoute = {
162
+ export interface SsgRoute
163
+ extends ExperimentalBypassForInfo ,
164
+ DataRouteRouteInfo {
153
165
initialRevalidateSeconds : number | false
154
166
srcRoute : string | null
155
- dataRoute : string | null
156
167
initialStatus ?: number
157
168
initialHeaders ?: Record < string , string >
158
- experimentalBypassFor ?: RouteHas [ ]
159
169
}
160
170
161
- export type DynamicSsgRoute = {
171
+ export interface DynamicSsgRoute
172
+ extends ExperimentalBypassForInfo ,
173
+ DataRouteRouteInfo {
162
174
routeRegex : string
163
175
fallback : string | null | false
164
- dataRoute : string | null
165
176
dataRouteRegex : string | null
166
- experimentalBypassFor ?: RouteHas [ ]
167
177
}
168
178
169
179
export type PrerenderManifest = {
@@ -880,7 +890,7 @@ export default async function build(
880
890
. traceAsyncFn ( ( ) =>
881
891
fs . writeFile (
882
892
routesManifestPath ,
883
- JSON . stringify ( routesManifest ) ,
893
+ formatManifest ( routesManifest ) ,
884
894
'utf8'
885
895
)
886
896
)
@@ -1095,7 +1105,8 @@ export default async function build(
1095
1105
} )
1096
1106
await fs . writeFile (
1097
1107
path . join ( distDir , APP_PATH_ROUTES_MANIFEST ) ,
1098
- JSON . stringify ( appPathRoutes , null , 2 )
1108
+ formatManifest ( appPathRoutes ) ,
1109
+ 'utf-8'
1099
1110
)
1100
1111
}
1101
1112
@@ -1757,7 +1768,8 @@ export default async function build(
1757
1768
1758
1769
await fs . writeFile (
1759
1770
path . join ( distDir , SERVER_DIRECTORY , FUNCTIONS_CONFIG_MANIFEST ) ,
1760
- JSON . stringify ( manifest , null , 2 )
1771
+ formatManifest ( manifest ) ,
1772
+ 'utf8'
1761
1773
)
1762
1774
}
1763
1775
@@ -1793,7 +1805,7 @@ export default async function build(
1793
1805
1794
1806
await fs . writeFile (
1795
1807
routesManifestPath ,
1796
- JSON . stringify ( routesManifest ) ,
1808
+ formatManifest ( routesManifest ) ,
1797
1809
'utf8'
1798
1810
)
1799
1811
}
@@ -1868,7 +1880,7 @@ export default async function build(
1868
1880
1869
1881
await fs . writeFile (
1870
1882
path . join ( distDir , SERVER_FILES_MANIFEST ) ,
1871
- JSON . stringify ( requiredServerFiles ) ,
1883
+ formatManifest ( requiredServerFiles ) ,
1872
1884
'utf8'
1873
1885
)
1874
1886
@@ -1927,15 +1939,10 @@ export default async function build(
1927
1939
ssgPages ,
1928
1940
additionalSsgPaths
1929
1941
)
1930
- const exportApp : typeof import ( '../export' ) . default =
1931
- require ( '../export' ) . default
1942
+ const exportApp : ExportAppWorker = require ( '../export' ) . default
1932
1943
1933
1944
const exportConfig : NextConfigComplete = {
1934
1945
...config ,
1935
- initialPageRevalidationMap : { } ,
1936
- initialPageMetaMap : { } ,
1937
- pageDurationMap : { } ,
1938
- ssgNotFoundPaths : [ ] as string [ ] ,
1939
1946
// Default map will be the collection of automatic statically exported
1940
1947
// pages and incremental pages.
1941
1948
// n.b. we cannot handle this above in combinedPages because the dynamic
@@ -2057,7 +2064,7 @@ export default async function build(
2057
2064
} ,
2058
2065
}
2059
2066
2060
- const exportOptions : ExportOptions = {
2067
+ const exportOptions : ExportAppOptions = {
2061
2068
isInvokedFromCli : false ,
2062
2069
nextConfig : exportConfig ,
2063
2070
hasAppDir,
@@ -2078,10 +2085,17 @@ export default async function build(
2078
2085
} ,
2079
2086
}
2080
2087
2081
- await exportApp ( dir , exportOptions , nextBuildSpan )
2088
+ const exportResult = await exportApp (
2089
+ dir ,
2090
+ exportOptions ,
2091
+ nextBuildSpan
2092
+ )
2093
+
2094
+ // If there was no result, there's nothing more to do.
2095
+ if ( ! exportResult ) return
2082
2096
2083
2097
const postBuildSpinner = createSpinner ( 'Finalizing page optimization' )
2084
- ssgNotFoundPaths = exportConfig . ssgNotFoundPaths
2098
+ ssgNotFoundPaths = Array . from ( exportResult . ssgNotFoundPaths )
2085
2099
2086
2100
// remove server bundles that were exported
2087
2101
for ( const page of staticPages ) {
@@ -2094,8 +2108,9 @@ export default async function build(
2094
2108
const appConfig = appDefaultConfigs . get ( originalAppPath ) || { }
2095
2109
let hasDynamicData =
2096
2110
appConfig . revalidate === 0 ||
2097
- exportConfig . initialPageRevalidationMap [ page ] === 0
2111
+ exportResult . byPath . get ( page ) ?. revalidate === 0
2098
2112
2113
+ // TODO: (wyattjoh) maybe change behavior for postpone?
2099
2114
if ( hasDynamicData && pageInfos . get ( page ) ?. static ) {
2100
2115
// if the page was marked as being static, but it contains dynamic data
2101
2116
// (ie, in the case of a static generation bailout), then it should be marked dynamic
@@ -2123,22 +2138,10 @@ export default async function build(
2123
2138
if ( isDynamicRoute ( page ) && route === page ) return
2124
2139
if ( route === '/_not-found' ) return
2125
2140
2126
- let revalidate = exportConfig . initialPageRevalidationMap [ route ]
2127
-
2128
- if ( typeof revalidate === 'undefined' ) {
2129
- revalidate =
2130
- typeof appConfig . revalidate !== 'undefined'
2131
- ? appConfig . revalidate
2132
- : false
2133
- }
2134
-
2135
- // ensure revalidate is normalized correctly
2136
- if (
2137
- typeof revalidate !== 'number' &&
2138
- typeof revalidate !== 'boolean'
2139
- ) {
2140
- revalidate = false
2141
- }
2141
+ const {
2142
+ revalidate = appConfig . revalidate ?? false ,
2143
+ metadata = { } ,
2144
+ } = exportResult . byPath . get ( route ) ?? { }
2142
2145
2143
2146
if ( revalidate !== 0 ) {
2144
2147
const normalizedRoute = normalizePagePath ( route )
@@ -2148,15 +2151,11 @@ export default async function build(
2148
2151
2149
2152
const routeMeta : Partial < SsgRoute > = { }
2150
2153
2151
- const exportRouteMeta : {
2152
- status ?: number
2153
- headers ?: Record < string , string >
2154
- } = exportConfig . initialPageMetaMap [ route ] || { }
2155
-
2156
- if ( exportRouteMeta . status !== 200 ) {
2157
- routeMeta . initialStatus = exportRouteMeta . status
2154
+ if ( metadata . status !== 200 ) {
2155
+ routeMeta . initialStatus = metadata . status
2158
2156
}
2159
- const exportHeaders = exportRouteMeta . headers
2157
+
2158
+ const exportHeaders = metadata . headers
2160
2159
const headerKeys = Object . keys ( exportHeaders || { } )
2161
2160
2162
2161
if ( exportHeaders && headerKeys . length ) {
@@ -2390,15 +2389,22 @@ export default async function build(
2390
2389
const file = normalizePagePath ( page )
2391
2390
2392
2391
const pageInfo = pageInfos . get ( page )
2393
- const durationInfo = exportConfig . pageDurationMap [ page ]
2392
+ const durationInfo = exportResult . byPage . get ( page )
2394
2393
if ( pageInfo && durationInfo ) {
2395
2394
// Set Build Duration
2396
2395
if ( pageInfo . ssgPageRoutes ) {
2397
2396
pageInfo . ssgPageDurations = pageInfo . ssgPageRoutes . map (
2398
- ( pagePath ) => durationInfo [ pagePath ]
2397
+ ( pagePath ) => {
2398
+ const duration = durationInfo . durationsByPath . get ( pagePath )
2399
+ if ( typeof duration === 'undefined' ) {
2400
+ throw new Error ( "Invariant: page wasn't built" )
2401
+ }
2402
+
2403
+ return duration
2404
+ }
2399
2405
)
2400
2406
}
2401
- pageInfo . pageDuration = durationInfo [ page ]
2407
+ pageInfo . pageDuration = durationInfo . durationsByPath . get ( page )
2402
2408
}
2403
2409
2404
2410
// The dynamic version of SSG pages are only prerendered if the
@@ -2432,7 +2438,8 @@ export default async function build(
2432
2438
2433
2439
finalPrerenderRoutes [ localePage ] = {
2434
2440
initialRevalidateSeconds :
2435
- exportConfig . initialPageRevalidationMap [ localePage ] ,
2441
+ exportResult . byPath . get ( localePage ) ?. revalidate ??
2442
+ false ,
2436
2443
srcRoute : null ,
2437
2444
dataRoute : path . posix . join (
2438
2445
'/_next/data' ,
@@ -2444,7 +2451,7 @@ export default async function build(
2444
2451
} else {
2445
2452
finalPrerenderRoutes [ page ] = {
2446
2453
initialRevalidateSeconds :
2447
- exportConfig . initialPageRevalidationMap [ page ] ,
2454
+ exportResult . byPath . get ( page ) ?. revalidate ?? false ,
2448
2455
srcRoute : null ,
2449
2456
dataRoute : path . posix . join (
2450
2457
'/_next/data' ,
@@ -2456,7 +2463,7 @@ export default async function build(
2456
2463
// Set Page Revalidation Interval
2457
2464
if ( pageInfo ) {
2458
2465
pageInfo . initialRevalidateSeconds =
2459
- exportConfig . initialPageRevalidationMap [ page ]
2466
+ exportResult . byPath . get ( page ) ?. revalidate ?? false
2460
2467
}
2461
2468
} else {
2462
2469
// For a dynamic SSG page, we did not copy its data exports and only
@@ -2503,9 +2510,15 @@ export default async function build(
2503
2510
)
2504
2511
}
2505
2512
2513
+ const initialRevalidateSeconds =
2514
+ exportResult . byPath . get ( route ) ?. revalidate ?? false
2515
+
2516
+ if ( typeof initialRevalidateSeconds === 'undefined' ) {
2517
+ throw new Error ( "Invariant: page wasn't built" )
2518
+ }
2519
+
2506
2520
finalPrerenderRoutes [ route ] = {
2507
- initialRevalidateSeconds :
2508
- exportConfig . initialPageRevalidationMap [ route ] ,
2521
+ initialRevalidateSeconds,
2509
2522
srcRoute : page ,
2510
2523
dataRoute : path . posix . join (
2511
2524
'/_next/data' ,
@@ -2516,8 +2529,7 @@ export default async function build(
2516
2529
2517
2530
// Set route Revalidation Interval
2518
2531
if ( pageInfo ) {
2519
- pageInfo . initialRevalidateSeconds =
2520
- exportConfig . initialPageRevalidationMap [ route ]
2532
+ pageInfo . initialRevalidateSeconds = initialRevalidateSeconds
2521
2533
}
2522
2534
}
2523
2535
}
@@ -2528,7 +2540,7 @@ export default async function build(
2528
2540
await fs . rm ( exportOptions . outdir , { recursive : true , force : true } )
2529
2541
await fs . writeFile (
2530
2542
manifestPath ,
2531
- JSON . stringify ( pagesManifest , null , 2 ) ,
2543
+ formatManifest ( pagesManifest ) ,
2532
2544
'utf8'
2533
2545
)
2534
2546
@@ -2622,7 +2634,7 @@ export default async function build(
2622
2634
2623
2635
await fs . writeFile (
2624
2636
path . join ( distDir , PRERENDER_MANIFEST ) ,
2625
- JSON . stringify ( prerenderManifest ) ,
2637
+ formatManifest ( prerenderManifest ) ,
2626
2638
'utf8'
2627
2639
)
2628
2640
await fs . writeFile (
@@ -2647,7 +2659,7 @@ export default async function build(
2647
2659
}
2648
2660
await fs . writeFile (
2649
2661
path . join ( distDir , PRERENDER_MANIFEST ) ,
2650
- JSON . stringify ( prerenderManifest ) ,
2662
+ formatManifest ( prerenderManifest ) ,
2651
2663
'utf8'
2652
2664
)
2653
2665
await fs . writeFile (
@@ -2674,15 +2686,15 @@ export default async function build(
2674
2686
2675
2687
await fs . writeFile (
2676
2688
path . join ( distDir , IMAGES_MANIFEST ) ,
2677
- JSON . stringify ( {
2689
+ formatManifest ( {
2678
2690
version : 1 ,
2679
2691
images,
2680
2692
} ) ,
2681
2693
'utf8'
2682
2694
)
2683
2695
await fs . writeFile (
2684
2696
path . join ( distDir , EXPORT_MARKER ) ,
2685
- JSON . stringify ( {
2697
+ formatManifest ( {
2686
2698
version : 1 ,
2687
2699
hasExportPathMap : typeof config . exportPathMap === 'function' ,
2688
2700
exportTrailingSlash : config . trailingSlash === true ,
@@ -2755,7 +2767,7 @@ export default async function build(
2755
2767
incrementalCacheIpcValidationKey
2756
2768
)
2757
2769
2758
- const options : ExportOptions = {
2770
+ const options : ExportAppOptions = {
2759
2771
isInvokedFromCli : false ,
2760
2772
buildExport : false ,
2761
2773
nextConfig : config ,
@@ -2834,7 +2846,7 @@ export default async function build(
2834
2846
SERVER_DIRECTORY ,
2835
2847
'app'
2836
2848
)
2837
- if ( fsExistsSync ( originalServerApp ) ) {
2849
+ if ( await fileExists ( originalServerApp ) ) {
2838
2850
await recursiveCopy (
2839
2851
originalServerApp ,
2840
2852
path . join (
0 commit comments