Skip to content

Commit 0342906

Browse files
committed
refactor: add more typings and clean up export/build flows
1 parent 7df92b8 commit 0342906

File tree

17 files changed

+1151
-921
lines changed

17 files changed

+1151
-921
lines changed

packages/next/src/build/index.ts

Lines changed: 76 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ import type { PagesManifest } from './webpack/plugins/pages-manifest-plugin'
44
import type { ExportPathMap, NextConfigComplete } from '../server/config-shared'
55
import type { MiddlewareManifest } from './webpack/plugins/middleware-plugin'
66
import type { ActionManifest } from './webpack/plugins/flight-client-entry-plugin'
7-
import type { ExportOptions } from '../export'
7+
import type { ExportAppOptions, ExportAppWorker } from '../export/types'
88

99
import '../lib/setup-exception-listeners'
10+
1011
import { loadEnvConfig } from '@next/env'
1112
import { bold, yellow, green } from '../lib/picocolors'
1213
import crypto from 'crypto'
1314
import { isMatch, makeRe } from 'next/dist/compiled/micromatch'
14-
import { promises as fs, existsSync as fsExistsSync } from 'fs'
15+
import fs from 'fs/promises'
1516
import os from 'os'
1617
import { Worker } from '../lib/worker'
1718
import { defaultConfig } from '../server/config-shared'
@@ -148,22 +149,31 @@ import { initialize as initializeIncrementalCache } from '../server/lib/incremen
148149
import { nodeFs } from '../server/lib/node-fs-methods'
149150
import { collectBuildTraces } from './collect-build-traces'
150151
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+
}
151161

152-
export type SsgRoute = {
162+
export interface SsgRoute
163+
extends ExperimentalBypassForInfo,
164+
DataRouteRouteInfo {
153165
initialRevalidateSeconds: number | false
154166
srcRoute: string | null
155-
dataRoute: string | null
156167
initialStatus?: number
157168
initialHeaders?: Record<string, string>
158-
experimentalBypassFor?: RouteHas[]
159169
}
160170

161-
export type DynamicSsgRoute = {
171+
export interface DynamicSsgRoute
172+
extends ExperimentalBypassForInfo,
173+
DataRouteRouteInfo {
162174
routeRegex: string
163175
fallback: string | null | false
164-
dataRoute: string | null
165176
dataRouteRegex: string | null
166-
experimentalBypassFor?: RouteHas[]
167177
}
168178

169179
export type PrerenderManifest = {
@@ -880,7 +890,7 @@ export default async function build(
880890
.traceAsyncFn(() =>
881891
fs.writeFile(
882892
routesManifestPath,
883-
JSON.stringify(routesManifest),
893+
formatManifest(routesManifest),
884894
'utf8'
885895
)
886896
)
@@ -1095,7 +1105,8 @@ export default async function build(
10951105
})
10961106
await fs.writeFile(
10971107
path.join(distDir, APP_PATH_ROUTES_MANIFEST),
1098-
JSON.stringify(appPathRoutes, null, 2)
1108+
formatManifest(appPathRoutes),
1109+
'utf-8'
10991110
)
11001111
}
11011112

@@ -1757,7 +1768,8 @@ export default async function build(
17571768

17581769
await fs.writeFile(
17591770
path.join(distDir, SERVER_DIRECTORY, FUNCTIONS_CONFIG_MANIFEST),
1760-
JSON.stringify(manifest, null, 2)
1771+
formatManifest(manifest),
1772+
'utf8'
17611773
)
17621774
}
17631775

@@ -1793,7 +1805,7 @@ export default async function build(
17931805

17941806
await fs.writeFile(
17951807
routesManifestPath,
1796-
JSON.stringify(routesManifest),
1808+
formatManifest(routesManifest),
17971809
'utf8'
17981810
)
17991811
}
@@ -1868,7 +1880,7 @@ export default async function build(
18681880

18691881
await fs.writeFile(
18701882
path.join(distDir, SERVER_FILES_MANIFEST),
1871-
JSON.stringify(requiredServerFiles),
1883+
formatManifest(requiredServerFiles),
18721884
'utf8'
18731885
)
18741886

@@ -1927,15 +1939,10 @@ export default async function build(
19271939
ssgPages,
19281940
additionalSsgPaths
19291941
)
1930-
const exportApp: typeof import('../export').default =
1931-
require('../export').default
1942+
const exportApp: ExportAppWorker = require('../export').default
19321943

19331944
const exportConfig: NextConfigComplete = {
19341945
...config,
1935-
initialPageRevalidationMap: {},
1936-
initialPageMetaMap: {},
1937-
pageDurationMap: {},
1938-
ssgNotFoundPaths: [] as string[],
19391946
// Default map will be the collection of automatic statically exported
19401947
// pages and incremental pages.
19411948
// n.b. we cannot handle this above in combinedPages because the dynamic
@@ -2057,7 +2064,7 @@ export default async function build(
20572064
},
20582065
}
20592066

2060-
const exportOptions: ExportOptions = {
2067+
const exportOptions: ExportAppOptions = {
20612068
isInvokedFromCli: false,
20622069
nextConfig: exportConfig,
20632070
hasAppDir,
@@ -2078,10 +2085,17 @@ export default async function build(
20782085
},
20792086
}
20802087

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
20822096

20832097
const postBuildSpinner = createSpinner('Finalizing page optimization')
2084-
ssgNotFoundPaths = exportConfig.ssgNotFoundPaths
2098+
ssgNotFoundPaths = Array.from(exportResult.ssgNotFoundPaths)
20852099

20862100
// remove server bundles that were exported
20872101
for (const page of staticPages) {
@@ -2094,8 +2108,9 @@ export default async function build(
20942108
const appConfig = appDefaultConfigs.get(originalAppPath) || {}
20952109
let hasDynamicData =
20962110
appConfig.revalidate === 0 ||
2097-
exportConfig.initialPageRevalidationMap[page] === 0
2111+
exportResult.byPath.get(page)?.revalidate === 0
20982112

2113+
// TODO: (wyattjoh) maybe change behavior for postpone?
20992114
if (hasDynamicData && pageInfos.get(page)?.static) {
21002115
// if the page was marked as being static, but it contains dynamic data
21012116
// (ie, in the case of a static generation bailout), then it should be marked dynamic
@@ -2123,22 +2138,10 @@ export default async function build(
21232138
if (isDynamicRoute(page) && route === page) return
21242139
if (route === '/_not-found') return
21252140

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) ?? {}
21422145

21432146
if (revalidate !== 0) {
21442147
const normalizedRoute = normalizePagePath(route)
@@ -2148,15 +2151,11 @@ export default async function build(
21482151

21492152
const routeMeta: Partial<SsgRoute> = {}
21502153

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
21582156
}
2159-
const exportHeaders = exportRouteMeta.headers
2157+
2158+
const exportHeaders = metadata.headers
21602159
const headerKeys = Object.keys(exportHeaders || {})
21612160

21622161
if (exportHeaders && headerKeys.length) {
@@ -2390,15 +2389,22 @@ export default async function build(
23902389
const file = normalizePagePath(page)
23912390

23922391
const pageInfo = pageInfos.get(page)
2393-
const durationInfo = exportConfig.pageDurationMap[page]
2392+
const durationInfo = exportResult.byPage.get(page)
23942393
if (pageInfo && durationInfo) {
23952394
// Set Build Duration
23962395
if (pageInfo.ssgPageRoutes) {
23972396
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+
}
23992405
)
24002406
}
2401-
pageInfo.pageDuration = durationInfo[page]
2407+
pageInfo.pageDuration = durationInfo.durationsByPath.get(page)
24022408
}
24032409

24042410
// The dynamic version of SSG pages are only prerendered if the
@@ -2432,7 +2438,8 @@ export default async function build(
24322438

24332439
finalPrerenderRoutes[localePage] = {
24342440
initialRevalidateSeconds:
2435-
exportConfig.initialPageRevalidationMap[localePage],
2441+
exportResult.byPath.get(localePage)?.revalidate ??
2442+
false,
24362443
srcRoute: null,
24372444
dataRoute: path.posix.join(
24382445
'/_next/data',
@@ -2444,7 +2451,7 @@ export default async function build(
24442451
} else {
24452452
finalPrerenderRoutes[page] = {
24462453
initialRevalidateSeconds:
2447-
exportConfig.initialPageRevalidationMap[page],
2454+
exportResult.byPath.get(page)?.revalidate ?? false,
24482455
srcRoute: null,
24492456
dataRoute: path.posix.join(
24502457
'/_next/data',
@@ -2456,7 +2463,7 @@ export default async function build(
24562463
// Set Page Revalidation Interval
24572464
if (pageInfo) {
24582465
pageInfo.initialRevalidateSeconds =
2459-
exportConfig.initialPageRevalidationMap[page]
2466+
exportResult.byPath.get(page)?.revalidate ?? false
24602467
}
24612468
} else {
24622469
// For a dynamic SSG page, we did not copy its data exports and only
@@ -2503,9 +2510,15 @@ export default async function build(
25032510
)
25042511
}
25052512

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+
25062520
finalPrerenderRoutes[route] = {
2507-
initialRevalidateSeconds:
2508-
exportConfig.initialPageRevalidationMap[route],
2521+
initialRevalidateSeconds,
25092522
srcRoute: page,
25102523
dataRoute: path.posix.join(
25112524
'/_next/data',
@@ -2516,8 +2529,7 @@ export default async function build(
25162529

25172530
// Set route Revalidation Interval
25182531
if (pageInfo) {
2519-
pageInfo.initialRevalidateSeconds =
2520-
exportConfig.initialPageRevalidationMap[route]
2532+
pageInfo.initialRevalidateSeconds = initialRevalidateSeconds
25212533
}
25222534
}
25232535
}
@@ -2528,7 +2540,7 @@ export default async function build(
25282540
await fs.rm(exportOptions.outdir, { recursive: true, force: true })
25292541
await fs.writeFile(
25302542
manifestPath,
2531-
JSON.stringify(pagesManifest, null, 2),
2543+
formatManifest(pagesManifest),
25322544
'utf8'
25332545
)
25342546

@@ -2622,7 +2634,7 @@ export default async function build(
26222634

26232635
await fs.writeFile(
26242636
path.join(distDir, PRERENDER_MANIFEST),
2625-
JSON.stringify(prerenderManifest),
2637+
formatManifest(prerenderManifest),
26262638
'utf8'
26272639
)
26282640
await fs.writeFile(
@@ -2647,7 +2659,7 @@ export default async function build(
26472659
}
26482660
await fs.writeFile(
26492661
path.join(distDir, PRERENDER_MANIFEST),
2650-
JSON.stringify(prerenderManifest),
2662+
formatManifest(prerenderManifest),
26512663
'utf8'
26522664
)
26532665
await fs.writeFile(
@@ -2674,15 +2686,15 @@ export default async function build(
26742686

26752687
await fs.writeFile(
26762688
path.join(distDir, IMAGES_MANIFEST),
2677-
JSON.stringify({
2689+
formatManifest({
26782690
version: 1,
26792691
images,
26802692
}),
26812693
'utf8'
26822694
)
26832695
await fs.writeFile(
26842696
path.join(distDir, EXPORT_MARKER),
2685-
JSON.stringify({
2697+
formatManifest({
26862698
version: 1,
26872699
hasExportPathMap: typeof config.exportPathMap === 'function',
26882700
exportTrailingSlash: config.trailingSlash === true,
@@ -2755,7 +2767,7 @@ export default async function build(
27552767
incrementalCacheIpcValidationKey
27562768
)
27572769

2758-
const options: ExportOptions = {
2770+
const options: ExportAppOptions = {
27592771
isInvokedFromCli: false,
27602772
buildExport: false,
27612773
nextConfig: config,
@@ -2834,7 +2846,7 @@ export default async function build(
28342846
SERVER_DIRECTORY,
28352847
'app'
28362848
)
2837-
if (fsExistsSync(originalServerApp)) {
2849+
if (await fileExists(originalServerApp)) {
28382850
await recursiveCopy(
28392851
originalServerApp,
28402852
path.join(
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Formats the manifest depending on the environment variable
3+
* `NODE_ENV`. If it's set to `development`, it will return a pretty printed
4+
* JSON string, otherwise it will return a minified JSON string.
5+
*/
6+
export function formatManifest<T extends object>(manifest: T): string {
7+
if (process.env.NODE_ENV === 'development') {
8+
return JSON.stringify(manifest, null, 2)
9+
}
10+
11+
return JSON.stringify(manifest)
12+
}

packages/next/src/cli/next-export.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#!/usr/bin/env node
2+
import type { ExportAppOptions } from '../export/types'
3+
24
import { resolve, join } from 'path'
35
import { existsSync } from 'fs'
46
import { cyan } from '../lib/picocolors'
5-
import exportApp, { ExportError, ExportOptions } from '../export'
7+
import exportApp, { ExportError } from '../export'
68
import * as Log from '../build/output/log'
79
import { printAndExit } from '../server/lib/utils'
810
import { CliCommand } from '../lib/commands'
@@ -43,7 +45,7 @@ const nextExport: CliCommand = (args) => {
4345
printAndExit(`> No such directory exists as the project root: ${dir}`)
4446
}
4547

46-
const options: ExportOptions = {
48+
const options: ExportAppOptions = {
4749
silent: args['--silent'] || false,
4850
threads: args['--threads'],
4951
outdir: args['--outdir'] ? resolve(args['--outdir']) : join(dir, 'out'),

0 commit comments

Comments
 (0)