Skip to content

Commit 9a421be

Browse files
committed
refactor: add more typings and clean up export/build flows
1 parent f0f8762 commit 9a421be

File tree

17 files changed

+1135
-915
lines changed

17 files changed

+1135
-915
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'
@@ -147,22 +148,31 @@ import { initialize as initializeIncrementalCache } from '../server/lib/incremen
147148
import { nodeFs } from '../server/lib/node-fs-methods'
148149
import { collectBuildTraces } from './collect-build-traces'
149150
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+
}
150160

151-
export type SsgRoute = {
161+
export interface SsgRoute
162+
extends ExperimentalBypassForInfo,
163+
DataRouteRouteInfo {
152164
initialRevalidateSeconds: number | false
153165
srcRoute: string | null
154-
dataRoute: string | null
155166
initialStatus?: number
156167
initialHeaders?: Record<string, string>
157-
experimentalBypassFor?: RouteHas[]
158168
}
159169

160-
export type DynamicSsgRoute = {
170+
export interface DynamicSsgRoute
171+
extends ExperimentalBypassForInfo,
172+
DataRouteRouteInfo {
161173
routeRegex: string
162174
fallback: string | null | false
163-
dataRoute: string | null
164175
dataRouteRegex: string | null
165-
experimentalBypassFor?: RouteHas[]
166176
}
167177

168178
export type PrerenderManifest = {
@@ -877,7 +887,7 @@ export default async function build(
877887
.traceAsyncFn(() =>
878888
fs.writeFile(
879889
routesManifestPath,
880-
JSON.stringify(routesManifest),
890+
formatManifest(routesManifest),
881891
'utf8'
882892
)
883893
)
@@ -1091,7 +1101,8 @@ export default async function build(
10911101
})
10921102
await fs.writeFile(
10931103
path.join(distDir, APP_PATH_ROUTES_MANIFEST),
1094-
JSON.stringify(appPathRoutes, null, 2)
1104+
formatManifest(appPathRoutes),
1105+
'utf-8'
10951106
)
10961107
}
10971108

@@ -1745,7 +1756,8 @@ export default async function build(
17451756

17461757
await fs.writeFile(
17471758
path.join(distDir, SERVER_DIRECTORY, FUNCTIONS_CONFIG_MANIFEST),
1748-
JSON.stringify(manifest, null, 2)
1759+
formatManifest(manifest),
1760+
'utf8'
17491761
)
17501762
}
17511763

@@ -1781,7 +1793,7 @@ export default async function build(
17811793

17821794
await fs.writeFile(
17831795
routesManifestPath,
1784-
JSON.stringify(routesManifest),
1796+
formatManifest(routesManifest),
17851797
'utf8'
17861798
)
17871799
}
@@ -1856,7 +1868,7 @@ export default async function build(
18561868

18571869
await fs.writeFile(
18581870
path.join(distDir, SERVER_FILES_MANIFEST),
1859-
JSON.stringify(requiredServerFiles),
1871+
formatManifest(requiredServerFiles),
18601872
'utf8'
18611873
)
18621874

@@ -1915,15 +1927,10 @@ export default async function build(
19151927
ssgPages,
19161928
additionalSsgPaths
19171929
)
1918-
const exportApp: typeof import('../export').default =
1919-
require('../export').default
1930+
const exportApp: ExportAppWorker = require('../export').default
19201931

19211932
const exportConfig: NextConfigComplete = {
19221933
...config,
1923-
initialPageRevalidationMap: {},
1924-
initialPageMetaMap: {},
1925-
pageDurationMap: {},
1926-
ssgNotFoundPaths: [] as string[],
19271934
// Default map will be the collection of automatic statically exported
19281935
// pages and incremental pages.
19291936
// n.b. we cannot handle this above in combinedPages because the dynamic
@@ -2036,7 +2043,7 @@ export default async function build(
20362043
},
20372044
}
20382045

2039-
const exportOptions: ExportOptions = {
2046+
const exportOptions: ExportAppOptions = {
20402047
isInvokedFromCli: false,
20412048
nextConfig: exportConfig,
20422049
hasAppDir,
@@ -2057,10 +2064,17 @@ export default async function build(
20572064
},
20582065
}
20592066

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
20612075

20622076
const postBuildSpinner = createSpinner('Finalizing page optimization')
2063-
ssgNotFoundPaths = exportConfig.ssgNotFoundPaths
2077+
ssgNotFoundPaths = Array.from(exportResult.ssgNotFoundPaths)
20642078

20652079
// remove server bundles that were exported
20662080
for (const page of staticPages) {
@@ -2073,8 +2087,9 @@ export default async function build(
20732087
const appConfig = appDefaultConfigs.get(originalAppPath) || {}
20742088
let hasDynamicData =
20752089
appConfig.revalidate === 0 ||
2076-
exportConfig.initialPageRevalidationMap[page] === 0
2090+
exportResult.byPath.get(page)?.revalidate === 0
20772091

2092+
// TODO: (wyattjoh) maybe change behavior for postpone?
20782093
if (hasDynamicData && pageInfos.get(page)?.static) {
20792094
// if the page was marked as being static, but it contains dynamic data
20802095
// (ie, in the case of a static generation bailout), then it should be marked dynamic
@@ -2102,22 +2117,10 @@ export default async function build(
21022117
if (isDynamicRoute(page) && route === page) return
21032118
if (route === '/_not-found') return
21042119

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

21222125
if (revalidate !== 0) {
21232126
const normalizedRoute = normalizePagePath(route)
@@ -2127,15 +2130,11 @@ export default async function build(
21272130

21282131
const routeMeta: Partial<SsgRoute> = {}
21292132

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
21372135
}
2138-
const exportHeaders = exportRouteMeta.headers
2136+
2137+
const exportHeaders = metadata.headers
21392138
const headerKeys = Object.keys(exportHeaders || {})
21402139

21412140
if (exportHeaders && headerKeys.length) {
@@ -2369,15 +2368,22 @@ export default async function build(
23692368
const file = normalizePagePath(page)
23702369

23712370
const pageInfo = pageInfos.get(page)
2372-
const durationInfo = exportConfig.pageDurationMap[page]
2371+
const durationInfo = exportResult.byPage.get(page)
23732372
if (pageInfo && durationInfo) {
23742373
// Set Build Duration
23752374
if (pageInfo.ssgPageRoutes) {
23762375
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+
}
23782384
)
23792385
}
2380-
pageInfo.pageDuration = durationInfo[page]
2386+
pageInfo.pageDuration = durationInfo.durationsByPath.get(page)
23812387
}
23822388

23832389
// The dynamic version of SSG pages are only prerendered if the
@@ -2411,7 +2417,8 @@ export default async function build(
24112417

24122418
finalPrerenderRoutes[localePage] = {
24132419
initialRevalidateSeconds:
2414-
exportConfig.initialPageRevalidationMap[localePage],
2420+
exportResult.byPath.get(localePage)?.revalidate ??
2421+
false,
24152422
srcRoute: null,
24162423
dataRoute: path.posix.join(
24172424
'/_next/data',
@@ -2423,7 +2430,7 @@ export default async function build(
24232430
} else {
24242431
finalPrerenderRoutes[page] = {
24252432
initialRevalidateSeconds:
2426-
exportConfig.initialPageRevalidationMap[page],
2433+
exportResult.byPath.get(page)?.revalidate ?? false,
24272434
srcRoute: null,
24282435
dataRoute: path.posix.join(
24292436
'/_next/data',
@@ -2435,7 +2442,7 @@ export default async function build(
24352442
// Set Page Revalidation Interval
24362443
if (pageInfo) {
24372444
pageInfo.initialRevalidateSeconds =
2438-
exportConfig.initialPageRevalidationMap[page]
2445+
exportResult.byPath.get(page)?.revalidate ?? false
24392446
}
24402447
} else {
24412448
// For a dynamic SSG page, we did not copy its data exports and only
@@ -2482,9 +2489,15 @@ export default async function build(
24822489
)
24832490
}
24842491

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+
24852499
finalPrerenderRoutes[route] = {
2486-
initialRevalidateSeconds:
2487-
exportConfig.initialPageRevalidationMap[route],
2500+
initialRevalidateSeconds,
24882501
srcRoute: page,
24892502
dataRoute: path.posix.join(
24902503
'/_next/data',
@@ -2495,8 +2508,7 @@ export default async function build(
24952508

24962509
// Set route Revalidation Interval
24972510
if (pageInfo) {
2498-
pageInfo.initialRevalidateSeconds =
2499-
exportConfig.initialPageRevalidationMap[route]
2511+
pageInfo.initialRevalidateSeconds = initialRevalidateSeconds
25002512
}
25012513
}
25022514
}
@@ -2507,7 +2519,7 @@ export default async function build(
25072519
await fs.rm(exportOptions.outdir, { recursive: true, force: true })
25082520
await fs.writeFile(
25092521
manifestPath,
2510-
JSON.stringify(pagesManifest, null, 2),
2522+
formatManifest(pagesManifest),
25112523
'utf8'
25122524
)
25132525

@@ -2601,7 +2613,7 @@ export default async function build(
26012613

26022614
await fs.writeFile(
26032615
path.join(distDir, PRERENDER_MANIFEST),
2604-
JSON.stringify(prerenderManifest),
2616+
formatManifest(prerenderManifest),
26052617
'utf8'
26062618
)
26072619
await fs.writeFile(
@@ -2626,7 +2638,7 @@ export default async function build(
26262638
}
26272639
await fs.writeFile(
26282640
path.join(distDir, PRERENDER_MANIFEST),
2629-
JSON.stringify(prerenderManifest),
2641+
formatManifest(prerenderManifest),
26302642
'utf8'
26312643
)
26322644
await fs.writeFile(
@@ -2653,15 +2665,15 @@ export default async function build(
26532665

26542666
await fs.writeFile(
26552667
path.join(distDir, IMAGES_MANIFEST),
2656-
JSON.stringify({
2668+
formatManifest({
26572669
version: 1,
26582670
images,
26592671
}),
26602672
'utf8'
26612673
)
26622674
await fs.writeFile(
26632675
path.join(distDir, EXPORT_MARKER),
2664-
JSON.stringify({
2676+
formatManifest({
26652677
version: 1,
26662678
hasExportPathMap: typeof config.exportPathMap === 'function',
26672679
exportTrailingSlash: config.trailingSlash === true,
@@ -2734,7 +2746,7 @@ export default async function build(
27342746
incrementalCacheIpcValidationKey
27352747
)
27362748

2737-
const options: ExportOptions = {
2749+
const options: ExportAppOptions = {
27382750
isInvokedFromCli: false,
27392751
buildExport: false,
27402752
nextConfig: config,
@@ -2813,7 +2825,7 @@ export default async function build(
28132825
SERVER_DIRECTORY,
28142826
'app'
28152827
)
2816-
if (fsExistsSync(originalServerApp)) {
2828+
if (await fileExists(originalServerApp)) {
28172829
await recursiveCopy(
28182830
originalServerApp,
28192831
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)