Skip to content

Commit bad909e

Browse files
shudingijjk
andauthored
Update page config APIs (vercel#41580)
This PR updates app configurations from `export const config = { ... }` to be directly exported. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md) Co-authored-by: JJ Kasper <jj@jjsweb.site>
1 parent d261e7c commit bad909e

File tree

37 files changed

+112
-131
lines changed

37 files changed

+112
-131
lines changed

packages/next/build/analysis/get-page-static-info.ts

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -48,22 +48,39 @@ export function getRSCModuleType(source: string): RSCModuleType {
4848
* requires a runtime to be specified. Those are:
4949
* - Modules with `export function getStaticProps | getServerSideProps`
5050
* - Modules with `export { getStaticProps | getServerSideProps } <from ...>`
51+
* - Modules with `export const runtime = ...`
5152
*/
52-
export function checkExports(swcAST: any): { ssr: boolean; ssg: boolean } {
53+
function checkExports(swcAST: any): {
54+
ssr: boolean
55+
ssg: boolean
56+
runtime?: string
57+
} {
5358
if (Array.isArray(swcAST?.body)) {
5459
try {
60+
let runtime: string | undefined
61+
let ssr: boolean = false
62+
let ssg: boolean = false
63+
5564
for (const node of swcAST.body) {
65+
if (
66+
node.type === 'ExportDeclaration' &&
67+
node.declaration?.type === 'VariableDeclaration'
68+
) {
69+
const id = node.declaration?.declarations[0]?.id.value
70+
if (id === 'runtime') {
71+
runtime = node.declaration?.declarations[0]?.init.value
72+
}
73+
}
74+
5675
if (
5776
node.type === 'ExportDeclaration' &&
5877
node.declaration?.type === 'FunctionDeclaration' &&
5978
['getStaticProps', 'getServerSideProps'].includes(
6079
node.declaration.identifier?.value
6180
)
6281
) {
63-
return {
64-
ssg: node.declaration.identifier.value === 'getStaticProps',
65-
ssr: node.declaration.identifier.value === 'getServerSideProps',
66-
}
82+
ssg = node.declaration.identifier.value === 'getStaticProps'
83+
ssr = node.declaration.identifier.value === 'getServerSideProps'
6784
}
6885

6986
if (
@@ -72,10 +89,8 @@ export function checkExports(swcAST: any): { ssr: boolean; ssg: boolean } {
7289
) {
7390
const id = node.declaration?.declarations[0]?.id.value
7491
if (['getStaticProps', 'getServerSideProps'].includes(id)) {
75-
return {
76-
ssg: id === 'getStaticProps',
77-
ssr: id === 'getServerSideProps',
78-
}
92+
ssg = id === 'getStaticProps'
93+
ssr = id === 'getServerSideProps'
7994
}
8095
}
8196

@@ -87,16 +102,14 @@ export function checkExports(swcAST: any): { ssr: boolean; ssg: boolean } {
87102
specifier.orig?.value
88103
)
89104

90-
return {
91-
ssg: values.some((value: any) =>
92-
['getStaticProps'].includes(value)
93-
),
94-
ssr: values.some((value: any) =>
95-
['getServerSideProps'].includes(value)
96-
),
97-
}
105+
ssg = values.some((value: any) => ['getStaticProps'].includes(value))
106+
ssr = values.some((value: any) =>
107+
['getServerSideProps'].includes(value)
108+
)
98109
}
99110
}
111+
112+
return { ssr, ssg, runtime }
100113
} catch (err) {}
101114
}
102115

@@ -270,7 +283,7 @@ export async function getPageStaticInfo(params: {
270283
)
271284
) {
272285
const swcAST = await parseModule(pageFilePath, fileContent)
273-
const { ssg, ssr } = checkExports(swcAST)
286+
const { ssg, ssr, runtime } = checkExports(swcAST)
274287
const rsc = getRSCModuleType(fileContent)
275288

276289
// default / failsafe value for config
@@ -284,12 +297,18 @@ export async function getPageStaticInfo(params: {
284297
// `export config` doesn't exist, or other unknown error throw by swc, silence them
285298
}
286299

300+
// Currently, we use `export const config = { runtime: '...' }` to specify the page runtime.
301+
// But in the new app directory, we prefer to use `export const runtime = '...'`
302+
// and deprecate the old way. To prevent breaking changes for `pages`, we use the exported config
303+
// as the fallback value.
304+
let resolvedRuntime = runtime || config.runtime
305+
287306
if (
288-
typeof config.runtime !== 'undefined' &&
289-
!isServerRuntime(config.runtime)
307+
typeof resolvedRuntime !== 'undefined' &&
308+
!isServerRuntime(resolvedRuntime)
290309
) {
291310
const options = Object.values(SERVER_RUNTIME).join(', ')
292-
if (typeof config.runtime !== 'string') {
311+
if (typeof resolvedRuntime !== 'string') {
293312
Log.error(
294313
`The \`runtime\` config must be a string. Please leave it empty or choose one of: ${options}`
295314
)
@@ -303,14 +322,14 @@ export async function getPageStaticInfo(params: {
303322
}
304323
}
305324

306-
let runtime =
307-
SERVER_RUNTIME.edge === config?.runtime
325+
resolvedRuntime =
326+
SERVER_RUNTIME.edge === resolvedRuntime
308327
? SERVER_RUNTIME.edge
309328
: ssr || ssg
310-
? config?.runtime || nextConfig.experimental?.runtime
329+
? resolvedRuntime || nextConfig.experimental?.runtime
311330
: undefined
312331

313-
if (runtime === SERVER_RUNTIME.edge) {
332+
if (resolvedRuntime === SERVER_RUNTIME.edge) {
314333
warnAboutExperimentalEdgeApiFunctions()
315334
}
316335

@@ -325,7 +344,7 @@ export async function getPageStaticInfo(params: {
325344
ssg,
326345
rsc,
327346
...(middlewareConfig && { middleware: middlewareConfig }),
328-
...(runtime && { runtime }),
347+
...(resolvedRuntime && { runtime: resolvedRuntime }),
329348
}
330349
}
331350

packages/next/build/utils.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,13 +1044,41 @@ export type AppConfig = {
10441044
preferredRegion?: string
10451045
}
10461046
type GenerateParams = Array<{
1047-
config: AppConfig
1047+
config?: AppConfig
10481048
segmentPath: string
10491049
getStaticPaths?: GetStaticPaths
10501050
generateStaticParams?: any
10511051
isLayout?: boolean
10521052
}>
10531053

1054+
export const collectAppConfig = (mod: any): AppConfig | undefined => {
1055+
let hasConfig = false
1056+
1057+
const config: AppConfig = {}
1058+
if (typeof mod?.revalidate !== 'undefined') {
1059+
config.revalidate = mod.revalidate
1060+
hasConfig = true
1061+
}
1062+
if (typeof mod?.dynamicParams !== 'undefined') {
1063+
config.dynamicParams = mod.dynamicParams
1064+
hasConfig = true
1065+
}
1066+
if (typeof mod?.dynamic !== 'undefined') {
1067+
config.dynamic = mod.dynamic
1068+
hasConfig = true
1069+
}
1070+
if (typeof mod?.fetchCache !== 'undefined') {
1071+
config.fetchCache = mod.fetchCache
1072+
hasConfig = true
1073+
}
1074+
if (typeof mod?.preferredRegion !== 'undefined') {
1075+
config.preferredRegion = mod.preferredRegion
1076+
hasConfig = true
1077+
}
1078+
1079+
return hasConfig ? config : undefined
1080+
}
1081+
10541082
export const collectGenerateParams = async (
10551083
segment: any,
10561084
parentSegments: string[] = [],
@@ -1059,13 +1087,14 @@ export const collectGenerateParams = async (
10591087
if (!Array.isArray(segment)) return generateParams
10601088
const isLayout = !!segment[2]?.layout
10611089
const mod = await (isLayout ? segment[2]?.layout?.() : segment[2]?.page?.())
1090+
const config = collectAppConfig(mod)
10621091

10631092
const result = {
10641093
isLayout,
10651094
segmentPath: `/${parentSegments.join('/')}${
10661095
segment[0] && parentSegments.length > 0 ? '/' : ''
10671096
}${segment[0]}`,
1068-
config: mod?.config,
1097+
config,
10691098
getStaticPaths: mod?.getStaticPaths,
10701099
generateStaticParams: mod?.generateStaticParams,
10711100
}

packages/next/build/webpack/plugins/flight-types-plugin.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,14 @@ interface IEntry {
3131
? `default: (props: { children: React.ReactNode; params?: any }) => React.ReactNode | null`
3232
: `default: (props: { params?: any }) => React.ReactNode | null`
3333
}
34+
config?: {}
3435
generateStaticParams?: (params?:any) => Promise<any[]>
35-
config?: {
36-
// TODO: remove revalidate here
37-
revalidate?: number | boolean
38-
${options.type === 'page' ? 'runtime?: string' : ''}
39-
}
4036
revalidate?: RevalidateRange<TEntry> | false
4137
dynamic?: 'auto' | 'force-dynamic' | 'error' | 'force-static'
4238
dynamicParams?: boolean
4339
fetchCache?: 'auto' | 'force-no-store' | 'only-no-store' | 'default-no-store' | 'default-cache' | 'only-cache' | 'force-cache'
4440
preferredRegion?: 'auto' | 'home' | 'edge'
41+
${options.type === 'page' ? "runtime?: 'nodejs' | 'experimental-edge'" : ''}
4542
}
4643
4744
// =============

packages/next/server/app-render.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -947,8 +947,8 @@ export async function renderToHTMLOrFlight(
947947
? await page()
948948
: undefined
949949

950-
if (layoutOrPageMod?.config) {
951-
defaultRevalidate = layoutOrPageMod.config.revalidate
950+
if (typeof layoutOrPageMod?.revalidate !== 'undefined') {
951+
defaultRevalidate = layoutOrPageMod.revalidate
952952

953953
if (isStaticGeneration && defaultRevalidate === 0) {
954954
const { DynamicServerError } =
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export default function Page() {
22
return <p>app-edge-ssr</p>
33
}
4-
export const config = { runtime: 'experimental-edge' }
4+
export const runtime = 'experimental-edge'

test/e2e/app-dir/app-prefetch/app/dashboard/page.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { experimental_use as use } from 'react'
22

3-
export const config = {
4-
revalidate: 0,
5-
}
3+
export const revalidate = 0
64

75
async function getData() {
86
await new Promise((resolve) => setTimeout(resolve, 3000))

test/e2e/app-dir/app-rendering/app/isr-multiple/nested/page.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { experimental_use as use } from 'react'
22

3-
export const config = {
4-
revalidate: 1,
5-
}
3+
export const revalidate = 1
64

75
async function getData() {
86
return {

test/e2e/app-dir/app-rendering/app/ssr-only/layout.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { experimental_use as use } from 'react'
22

3-
export const config = {
4-
revalidate: 0,
5-
}
3+
export const revalidate = 0
64

75
async function getData() {
86
return {

test/e2e/app-dir/app-rendering/app/static-only/nested/page.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { experimental_use as use } from 'react'
22

3-
export const config = {
4-
revalidate: false,
5-
}
3+
export const revalidate = false
64

75
async function getData() {
86
return {

test/e2e/app-dir/app-rendering/app/static-only/slow/page.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { experimental_use as use } from 'react'
22

3-
export const config = {
4-
revalidate: false,
5-
}
3+
export const revalidate = false
64

75
async function getData() {
86
await new Promise((resolve) => setTimeout(resolve, 5000))

0 commit comments

Comments
 (0)