@@ -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'
@@ -147,22 +148,31 @@ import { initialize as initializeIncrementalCache } from '../server/lib/incremen
147
148
import { nodeFs } from '../server/lib/node-fs-methods'
148
149
import { collectBuildTraces } from './collect-build-traces'
149
150
import { BuildTraceContext } from './webpack/plugins/next-trace-entrypoints-plugin'
151
+ import { formatManifest } from './manifests/formatter/format-manifest'
152
+
153
+ interface ExperimentalBypassForInfo {
154
+ experimentalBypassFor ?: RouteHas [ ]
155
+ }
156
+
157
+ interface DataRouteRouteInfo {
158
+ dataRoute : string | null
159
+ }
150
160
151
- export type SsgRoute = {
161
+ export interface SsgRoute
162
+ extends ExperimentalBypassForInfo ,
163
+ DataRouteRouteInfo {
152
164
initialRevalidateSeconds : number | false
153
165
srcRoute : string | null
154
- dataRoute : string | null
155
166
initialStatus ?: number
156
167
initialHeaders ?: Record < string , string >
157
- experimentalBypassFor ?: RouteHas [ ]
158
168
}
159
169
160
- export type DynamicSsgRoute = {
170
+ export interface DynamicSsgRoute
171
+ extends ExperimentalBypassForInfo ,
172
+ DataRouteRouteInfo {
161
173
routeRegex : string
162
174
fallback : string | null | false
163
- dataRoute : string | null
164
175
dataRouteRegex : string | null
165
- experimentalBypassFor ?: RouteHas [ ]
166
176
}
167
177
168
178
export type PrerenderManifest = {
@@ -877,7 +887,7 @@ export default async function build(
877
887
. traceAsyncFn ( ( ) =>
878
888
fs . writeFile (
879
889
routesManifestPath ,
880
- JSON . stringify ( routesManifest ) ,
890
+ formatManifest ( routesManifest ) ,
881
891
'utf8'
882
892
)
883
893
)
@@ -1091,7 +1101,8 @@ export default async function build(
1091
1101
} )
1092
1102
await fs . writeFile (
1093
1103
path . join ( distDir , APP_PATH_ROUTES_MANIFEST ) ,
1094
- JSON . stringify ( appPathRoutes , null , 2 )
1104
+ formatManifest ( appPathRoutes ) ,
1105
+ 'utf-8'
1095
1106
)
1096
1107
}
1097
1108
@@ -1745,7 +1756,8 @@ export default async function build(
1745
1756
1746
1757
await fs . writeFile (
1747
1758
path . join ( distDir , SERVER_DIRECTORY , FUNCTIONS_CONFIG_MANIFEST ) ,
1748
- JSON . stringify ( manifest , null , 2 )
1759
+ formatManifest ( manifest ) ,
1760
+ 'utf8'
1749
1761
)
1750
1762
}
1751
1763
@@ -1781,7 +1793,7 @@ export default async function build(
1781
1793
1782
1794
await fs . writeFile (
1783
1795
routesManifestPath ,
1784
- JSON . stringify ( routesManifest ) ,
1796
+ formatManifest ( routesManifest ) ,
1785
1797
'utf8'
1786
1798
)
1787
1799
}
@@ -1856,7 +1868,7 @@ export default async function build(
1856
1868
1857
1869
await fs . writeFile (
1858
1870
path . join ( distDir , SERVER_FILES_MANIFEST ) ,
1859
- JSON . stringify ( requiredServerFiles ) ,
1871
+ formatManifest ( requiredServerFiles ) ,
1860
1872
'utf8'
1861
1873
)
1862
1874
@@ -1915,15 +1927,10 @@ export default async function build(
1915
1927
ssgPages ,
1916
1928
additionalSsgPaths
1917
1929
)
1918
- const exportApp : typeof import ( '../export' ) . default =
1919
- require ( '../export' ) . default
1930
+ const exportApp : ExportAppWorker = require ( '../export' ) . default
1920
1931
1921
1932
const exportConfig : NextConfigComplete = {
1922
1933
...config ,
1923
- initialPageRevalidationMap : { } ,
1924
- initialPageMetaMap : { } ,
1925
- pageDurationMap : { } ,
1926
- ssgNotFoundPaths : [ ] as string [ ] ,
1927
1934
// Default map will be the collection of automatic statically exported
1928
1935
// pages and incremental pages.
1929
1936
// n.b. we cannot handle this above in combinedPages because the dynamic
@@ -2036,7 +2043,7 @@ export default async function build(
2036
2043
} ,
2037
2044
}
2038
2045
2039
- const exportOptions : ExportOptions = {
2046
+ const exportOptions : ExportAppOptions = {
2040
2047
isInvokedFromCli : false ,
2041
2048
nextConfig : exportConfig ,
2042
2049
hasAppDir,
@@ -2057,10 +2064,17 @@ export default async function build(
2057
2064
} ,
2058
2065
}
2059
2066
2060
- await exportApp ( dir , exportOptions , nextBuildSpan )
2067
+ const exportResult = await exportApp (
2068
+ dir ,
2069
+ exportOptions ,
2070
+ nextBuildSpan
2071
+ )
2072
+
2073
+ // If there was no result, there's nothing more to do.
2074
+ if ( ! exportResult ) return
2061
2075
2062
2076
const postBuildSpinner = createSpinner ( 'Finalizing page optimization' )
2063
- ssgNotFoundPaths = exportConfig . ssgNotFoundPaths
2077
+ ssgNotFoundPaths = Array . from ( exportResult . ssgNotFoundPaths )
2064
2078
2065
2079
// remove server bundles that were exported
2066
2080
for ( const page of staticPages ) {
@@ -2073,8 +2087,9 @@ export default async function build(
2073
2087
const appConfig = appDefaultConfigs . get ( originalAppPath ) || { }
2074
2088
let hasDynamicData =
2075
2089
appConfig . revalidate === 0 ||
2076
- exportConfig . initialPageRevalidationMap [ page ] === 0
2090
+ exportResult . byPath . get ( page ) ?. revalidate === 0
2077
2091
2092
+ // TODO: (wyattjoh) maybe change behavior for postpone?
2078
2093
if ( hasDynamicData && pageInfos . get ( page ) ?. static ) {
2079
2094
// if the page was marked as being static, but it contains dynamic data
2080
2095
// (ie, in the case of a static generation bailout), then it should be marked dynamic
@@ -2102,22 +2117,10 @@ export default async function build(
2102
2117
if ( isDynamicRoute ( page ) && route === page ) return
2103
2118
if ( route === '/_not-found' ) return
2104
2119
2105
- let revalidate = exportConfig . initialPageRevalidationMap [ route ]
2106
-
2107
- if ( typeof revalidate === 'undefined' ) {
2108
- revalidate =
2109
- typeof appConfig . revalidate !== 'undefined'
2110
- ? appConfig . revalidate
2111
- : false
2112
- }
2113
-
2114
- // ensure revalidate is normalized correctly
2115
- if (
2116
- typeof revalidate !== 'number' &&
2117
- typeof revalidate !== 'boolean'
2118
- ) {
2119
- revalidate = false
2120
- }
2120
+ const {
2121
+ revalidate = appConfig . revalidate ?? false ,
2122
+ metadata = { } ,
2123
+ } = exportResult . byPath . get ( route ) ?? { }
2121
2124
2122
2125
if ( revalidate !== 0 ) {
2123
2126
const normalizedRoute = normalizePagePath ( route )
@@ -2127,15 +2130,11 @@ export default async function build(
2127
2130
2128
2131
const routeMeta : Partial < SsgRoute > = { }
2129
2132
2130
- const exportRouteMeta : {
2131
- status ?: number
2132
- headers ?: Record < string , string >
2133
- } = exportConfig . initialPageMetaMap [ route ] || { }
2134
-
2135
- if ( exportRouteMeta . status !== 200 ) {
2136
- routeMeta . initialStatus = exportRouteMeta . status
2133
+ if ( metadata . status !== 200 ) {
2134
+ routeMeta . initialStatus = metadata . status
2137
2135
}
2138
- const exportHeaders = exportRouteMeta . headers
2136
+
2137
+ const exportHeaders = metadata . headers
2139
2138
const headerKeys = Object . keys ( exportHeaders || { } )
2140
2139
2141
2140
if ( exportHeaders && headerKeys . length ) {
@@ -2369,15 +2368,22 @@ export default async function build(
2369
2368
const file = normalizePagePath ( page )
2370
2369
2371
2370
const pageInfo = pageInfos . get ( page )
2372
- const durationInfo = exportConfig . pageDurationMap [ page ]
2371
+ const durationInfo = exportResult . byPage . get ( page )
2373
2372
if ( pageInfo && durationInfo ) {
2374
2373
// Set Build Duration
2375
2374
if ( pageInfo . ssgPageRoutes ) {
2376
2375
pageInfo . ssgPageDurations = pageInfo . ssgPageRoutes . map (
2377
- ( pagePath ) => durationInfo [ pagePath ]
2376
+ ( pagePath ) => {
2377
+ const duration = durationInfo . durationsByPath . get ( pagePath )
2378
+ if ( typeof duration === 'undefined' ) {
2379
+ throw new Error ( "Invariant: page wasn't built" )
2380
+ }
2381
+
2382
+ return duration
2383
+ }
2378
2384
)
2379
2385
}
2380
- pageInfo . pageDuration = durationInfo [ page ]
2386
+ pageInfo . pageDuration = durationInfo . durationsByPath . get ( page )
2381
2387
}
2382
2388
2383
2389
// The dynamic version of SSG pages are only prerendered if the
@@ -2411,7 +2417,8 @@ export default async function build(
2411
2417
2412
2418
finalPrerenderRoutes [ localePage ] = {
2413
2419
initialRevalidateSeconds :
2414
- exportConfig . initialPageRevalidationMap [ localePage ] ,
2420
+ exportResult . byPath . get ( localePage ) ?. revalidate ??
2421
+ false ,
2415
2422
srcRoute : null ,
2416
2423
dataRoute : path . posix . join (
2417
2424
'/_next/data' ,
@@ -2423,7 +2430,7 @@ export default async function build(
2423
2430
} else {
2424
2431
finalPrerenderRoutes [ page ] = {
2425
2432
initialRevalidateSeconds :
2426
- exportConfig . initialPageRevalidationMap [ page ] ,
2433
+ exportResult . byPath . get ( page ) ?. revalidate ?? false ,
2427
2434
srcRoute : null ,
2428
2435
dataRoute : path . posix . join (
2429
2436
'/_next/data' ,
@@ -2435,7 +2442,7 @@ export default async function build(
2435
2442
// Set Page Revalidation Interval
2436
2443
if ( pageInfo ) {
2437
2444
pageInfo . initialRevalidateSeconds =
2438
- exportConfig . initialPageRevalidationMap [ page ]
2445
+ exportResult . byPath . get ( page ) ?. revalidate ?? false
2439
2446
}
2440
2447
} else {
2441
2448
// For a dynamic SSG page, we did not copy its data exports and only
@@ -2482,9 +2489,15 @@ export default async function build(
2482
2489
)
2483
2490
}
2484
2491
2492
+ const initialRevalidateSeconds =
2493
+ exportResult . byPath . get ( route ) ?. revalidate ?? false
2494
+
2495
+ if ( typeof initialRevalidateSeconds === 'undefined' ) {
2496
+ throw new Error ( "Invariant: page wasn't built" )
2497
+ }
2498
+
2485
2499
finalPrerenderRoutes [ route ] = {
2486
- initialRevalidateSeconds :
2487
- exportConfig . initialPageRevalidationMap [ route ] ,
2500
+ initialRevalidateSeconds,
2488
2501
srcRoute : page ,
2489
2502
dataRoute : path . posix . join (
2490
2503
'/_next/data' ,
@@ -2495,8 +2508,7 @@ export default async function build(
2495
2508
2496
2509
// Set route Revalidation Interval
2497
2510
if ( pageInfo ) {
2498
- pageInfo . initialRevalidateSeconds =
2499
- exportConfig . initialPageRevalidationMap [ route ]
2511
+ pageInfo . initialRevalidateSeconds = initialRevalidateSeconds
2500
2512
}
2501
2513
}
2502
2514
}
@@ -2507,7 +2519,7 @@ export default async function build(
2507
2519
await fs . rm ( exportOptions . outdir , { recursive : true , force : true } )
2508
2520
await fs . writeFile (
2509
2521
manifestPath ,
2510
- JSON . stringify ( pagesManifest , null , 2 ) ,
2522
+ formatManifest ( pagesManifest ) ,
2511
2523
'utf8'
2512
2524
)
2513
2525
@@ -2601,7 +2613,7 @@ export default async function build(
2601
2613
2602
2614
await fs . writeFile (
2603
2615
path . join ( distDir , PRERENDER_MANIFEST ) ,
2604
- JSON . stringify ( prerenderManifest ) ,
2616
+ formatManifest ( prerenderManifest ) ,
2605
2617
'utf8'
2606
2618
)
2607
2619
await fs . writeFile (
@@ -2626,7 +2638,7 @@ export default async function build(
2626
2638
}
2627
2639
await fs . writeFile (
2628
2640
path . join ( distDir , PRERENDER_MANIFEST ) ,
2629
- JSON . stringify ( prerenderManifest ) ,
2641
+ formatManifest ( prerenderManifest ) ,
2630
2642
'utf8'
2631
2643
)
2632
2644
await fs . writeFile (
@@ -2653,15 +2665,15 @@ export default async function build(
2653
2665
2654
2666
await fs . writeFile (
2655
2667
path . join ( distDir , IMAGES_MANIFEST ) ,
2656
- JSON . stringify ( {
2668
+ formatManifest ( {
2657
2669
version : 1 ,
2658
2670
images,
2659
2671
} ) ,
2660
2672
'utf8'
2661
2673
)
2662
2674
await fs . writeFile (
2663
2675
path . join ( distDir , EXPORT_MARKER ) ,
2664
- JSON . stringify ( {
2676
+ formatManifest ( {
2665
2677
version : 1 ,
2666
2678
hasExportPathMap : typeof config . exportPathMap === 'function' ,
2667
2679
exportTrailingSlash : config . trailingSlash === true ,
@@ -2734,7 +2746,7 @@ export default async function build(
2734
2746
incrementalCacheIpcValidationKey
2735
2747
)
2736
2748
2737
- const options : ExportOptions = {
2749
+ const options : ExportAppOptions = {
2738
2750
isInvokedFromCli : false ,
2739
2751
buildExport : false ,
2740
2752
nextConfig : config ,
@@ -2813,7 +2825,7 @@ export default async function build(
2813
2825
SERVER_DIRECTORY ,
2814
2826
'app'
2815
2827
)
2816
- if ( fsExistsSync ( originalServerApp ) ) {
2828
+ if ( await fileExists ( originalServerApp ) ) {
2817
2829
await recursiveCopy (
2818
2830
originalServerApp ,
2819
2831
path . join (
0 commit comments