Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update page config APIs #41580

Merged
merged 8 commits into from Oct 20, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
71 changes: 45 additions & 26 deletions packages/next/build/analysis/get-page-static-info.ts
Expand Up @@ -48,22 +48,39 @@ export function getRSCModuleType(source: string): RSCModuleType {
* requires a runtime to be specified. Those are:
* - Modules with `export function getStaticProps | getServerSideProps`
* - Modules with `export { getStaticProps | getServerSideProps } <from ...>`
* - Modules with `export const runtime = ...`
*/
export function checkExports(swcAST: any): { ssr: boolean; ssg: boolean } {
function checkExports(swcAST: any): {
ssr: boolean
ssg: boolean
runtime?: string
} {
if (Array.isArray(swcAST?.body)) {
try {
let runtime: string | undefined
let ssr: boolean = false
let ssg: boolean = false

for (const node of swcAST.body) {
if (
node.type === 'ExportDeclaration' &&
node.declaration?.type === 'VariableDeclaration'
) {
const id = node.declaration?.declarations[0]?.id.value
if (id === 'runtime') {
runtime = node.declaration?.declarations[0]?.init.value
}
}

if (
node.type === 'ExportDeclaration' &&
node.declaration?.type === 'FunctionDeclaration' &&
['getStaticProps', 'getServerSideProps'].includes(
node.declaration.identifier?.value
)
) {
return {
ssg: node.declaration.identifier.value === 'getStaticProps',
ssr: node.declaration.identifier.value === 'getServerSideProps',
}
ssg = node.declaration.identifier.value === 'getStaticProps'
ssr = node.declaration.identifier.value === 'getServerSideProps'
}

if (
Expand All @@ -72,10 +89,8 @@ export function checkExports(swcAST: any): { ssr: boolean; ssg: boolean } {
) {
const id = node.declaration?.declarations[0]?.id.value
if (['getStaticProps', 'getServerSideProps'].includes(id)) {
return {
ssg: id === 'getStaticProps',
ssr: id === 'getServerSideProps',
}
ssg = id === 'getStaticProps'
ssr = id === 'getServerSideProps'
}
}

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

return {
ssg: values.some((value: any) =>
['getStaticProps'].includes(value)
),
ssr: values.some((value: any) =>
['getServerSideProps'].includes(value)
),
}
ssg = values.some((value: any) => ['getStaticProps'].includes(value))
ssr = values.some((value: any) =>
['getServerSideProps'].includes(value)
)
}
}

return { ssr, ssg, runtime }
} catch (err) {}
}

Expand Down Expand Up @@ -270,7 +283,7 @@ export async function getPageStaticInfo(params: {
)
) {
const swcAST = await parseModule(pageFilePath, fileContent)
const { ssg, ssr } = checkExports(swcAST)
const { ssg, ssr, runtime } = checkExports(swcAST)
const rsc = getRSCModuleType(fileContent)

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

// Currently, we use `export const config = { runtime: '...' }` to specify the page runtime.
// But in the new app directory, we prefer to use `export const runtime = '...'`
// and deprecate the old way. To prevent breaking changes for `pages`, we use the exported config
// as the fallback value.
let resolvedRuntime = runtime || config.runtime

if (
typeof config.runtime !== 'undefined' &&
!isServerRuntime(config.runtime)
typeof resolvedRuntime !== 'undefined' &&
!isServerRuntime(resolvedRuntime)
) {
const options = Object.values(SERVER_RUNTIME).join(', ')
if (typeof config.runtime !== 'string') {
if (typeof resolvedRuntime !== 'string') {
Log.error(
`The \`runtime\` config must be a string. Please leave it empty or choose one of: ${options}`
)
Expand All @@ -303,14 +322,14 @@ export async function getPageStaticInfo(params: {
}
}

let runtime =
SERVER_RUNTIME.edge === config?.runtime
resolvedRuntime =
SERVER_RUNTIME.edge === resolvedRuntime
? SERVER_RUNTIME.edge
: ssr || ssg
? config?.runtime || nextConfig.experimental?.runtime
? resolvedRuntime || nextConfig.experimental?.runtime
: undefined

if (runtime === SERVER_RUNTIME.edge) {
if (resolvedRuntime === SERVER_RUNTIME.edge) {
warnAboutExperimentalEdgeApiFunctions()
}

Expand All @@ -325,7 +344,7 @@ export async function getPageStaticInfo(params: {
ssg,
rsc,
...(middlewareConfig && { middleware: middlewareConfig }),
...(runtime && { runtime }),
...(resolvedRuntime && { runtime: resolvedRuntime }),
}
}

Expand Down
33 changes: 31 additions & 2 deletions packages/next/build/utils.ts
Expand Up @@ -1044,13 +1044,41 @@ export type AppConfig = {
preferredRegion?: string
}
type GenerateParams = Array<{
config: AppConfig
config?: AppConfig
segmentPath: string
getStaticPaths?: GetStaticPaths
generateStaticParams?: any
isLayout?: boolean
}>

export const collectAppConfig = (mod: any): AppConfig | undefined => {
let hasConfig = false

const config: AppConfig = {}
if (typeof mod?.revalidate !== 'undefined') {
config.revalidate = mod.revalidate
hasConfig = true
}
if (typeof mod?.dynamicParams !== 'undefined') {
config.dynamicParams = mod.dynamicParams
hasConfig = true
}
if (typeof mod?.dynamic !== 'undefined') {
config.dynamic = mod.dynamic
hasConfig = true
}
if (typeof mod?.fetchCache !== 'undefined') {
config.fetchCache = mod.fetchCache
hasConfig = true
}
if (typeof mod?.preferredRegion !== 'undefined') {
config.preferredRegion = mod.preferredRegion
hasConfig = true
}

return hasConfig ? config : undefined
}

export const collectGenerateParams = async (
segment: any,
parentSegments: string[] = [],
Expand All @@ -1059,13 +1087,14 @@ export const collectGenerateParams = async (
if (!Array.isArray(segment)) return generateParams
const isLayout = !!segment[2]?.layout
const mod = await (isLayout ? segment[2]?.layout?.() : segment[2]?.page?.())
const config = collectAppConfig(mod)

const result = {
isLayout,
segmentPath: `/${parentSegments.join('/')}${
segment[0] && parentSegments.length > 0 ? '/' : ''
}${segment[0]}`,
config: mod?.config,
config,
getStaticPaths: mod?.getStaticPaths,
generateStaticParams: mod?.generateStaticParams,
}
Expand Down
Expand Up @@ -32,16 +32,12 @@ interface IEntry {
: `default: (props: { params?: any }) => React.ReactNode | null`
}
generateStaticParams?: (params?:any) => Promise<any[]>
config?: {
// TODO: remove revalidate here
revalidate?: number | boolean
${options.type === 'page' ? 'runtime?: string' : ''}
}
revalidate?: RevalidateRange<TEntry> | false
dynamic?: 'auto' | 'force-dynamic' | 'error' | 'force-static'
dynamicParams?: boolean
fetchCache?: 'auto' | 'force-no-store' | 'only-no-store' | 'default-no-store' | 'default-cache' | 'only-cache' | 'force-cache'
preferredRegion?: 'auto' | 'home' | 'edge'
${options.type === 'page' ? "runtime?: 'nodejs' | 'experimental-edge'" : ''}
}

// =============
Expand Down
4 changes: 1 addition & 3 deletions test/e2e/app-dir/app-prefetch/app/dashboard/page.js
@@ -1,8 +1,6 @@
import { experimental_use as use } from 'react'

export const config = {
revalidate: 0,
}
export const revalidate = 0

async function getData() {
await new Promise((resolve) => setTimeout(resolve, 3000))
Expand Down
@@ -1,8 +1,6 @@
import { experimental_use as use } from 'react'

export const config = {
revalidate: 1,
}
export const revalidate = 1

async function getData() {
return {
Expand Down
4 changes: 1 addition & 3 deletions test/e2e/app-dir/app-rendering/app/ssr-only/layout.js
@@ -1,8 +1,6 @@
import { experimental_use as use } from 'react'

export const config = {
revalidate: 0,
}
export const revalidate = 0

async function getData() {
return {
Expand Down
@@ -1,8 +1,6 @@
import { experimental_use as use } from 'react'

export const config = {
revalidate: false,
}
export const revalidate = false

async function getData() {
return {
Expand Down
@@ -1,8 +1,6 @@
import { experimental_use as use } from 'react'

export const config = {
revalidate: false,
}
export const revalidate = false

async function getData() {
await new Promise((resolve) => setTimeout(resolve, 5000))
Expand Down
@@ -1,6 +1,4 @@
export const config = {
dynamicParams: true,
}
export const dynamicParams = false

export default function Page({ params }) {
return (
Expand Down
4 changes: 1 addition & 3 deletions test/e2e/app-dir/app-static/app/blog/[author]/page.js
@@ -1,8 +1,6 @@
import Link from 'next/link'

export const config = {
dynamicParams: false,
}
export const dynamicParams = false

export default function Page({ params }) {
return (
Expand Down
@@ -1,6 +1,4 @@
export const config = {
revalidate: 0,
}
export const revalidate = 0

export default function Page({ params }) {
return (
Expand Down
Expand Up @@ -20,6 +20,4 @@ export default function Page() {
}

// TODO-APP: remove revalidate config once next.revalidate is supported
export const config = {
revalidate: 0,
}
export const revalidate = 0
4 changes: 1 addition & 3 deletions test/e2e/app-dir/app-static/app/ssr-forced/page.js
@@ -1,6 +1,4 @@
export const config = {
revalidate: 0,
}
export const revalidate = 0

export default function Page() {
return (
Expand Down
4 changes: 0 additions & 4 deletions test/e2e/app-dir/app-typescript/app/layout.tsx
@@ -1,9 +1,5 @@
/* eslint-disable */

export const config = {
revalidate: 0,
}

export const revalidate = -1

export default function Root({ children }) {
Expand Down
Expand Up @@ -6,6 +6,4 @@ export default function HelloPage(props) {
)
}

export const config = {
runtime: 'experimental-edge',
}
export const runtime = 'experimental-edge'
4 changes: 1 addition & 3 deletions test/e2e/app-dir/app/app/dashboard/page.js
Expand Up @@ -13,6 +13,4 @@ export default function DashboardPage(props) {
)
}

export const config = {
runtime: 'experimental-edge',
}
export const runtime = 'experimental-edge'
@@ -1,6 +1,4 @@
export const config = {
revalidate: 0,
}
export const revalidate = 0

export default function Page() {
const err = new Error('this is a test')
Expand Down
4 changes: 1 addition & 3 deletions test/e2e/app-dir/app/app/error/server-component/page.js
@@ -1,6 +1,4 @@
export const config = {
revalidate: 0,
}
export const revalidate = 0

export default function Page() {
throw new Error('this is a test')
Expand Down
4 changes: 1 addition & 3 deletions test/e2e/app-dir/app/app/layout.js
Expand Up @@ -3,9 +3,7 @@ import { experimental_use as use } from 'react'
import '../styles/global.css'
import './style.css'

export const config = {
revalidate: 0,
}
export const revalidate = 0

async function getData() {
return {
Expand Down
4 changes: 1 addition & 3 deletions test/e2e/app-dir/app/app/slow-page-no-loading/page.js
Expand Up @@ -13,6 +13,4 @@ export default function SlowPage(props) {
return <h1 id="slow-page-message">{data.message}</h1>
}

export const config = {
runtime: 'experimental-edge',
}
export const runtime = 'experimental-edge'
4 changes: 1 addition & 3 deletions test/e2e/app-dir/app/app/slow-page-with-loading/page.js
Expand Up @@ -13,6 +13,4 @@ export default function SlowPage(props) {
return <h1 id="slow-page-message">{data.message}</h1>
}

export const config = {
runtime: 'experimental-edge',
}
export const runtime = 'experimental-edge'
@@ -1,7 +1,5 @@
// TODO-APP: remove after fixing filtering static flight data
export const config = {
revalidate: 0,
}
export const revalidate = 0

export default function Root({ children }) {
return (
Expand Down
@@ -1,7 +1,5 @@
// TODO-APP: remove after fixing filtering static flight data
export const config = {
revalidate: 0,
}
export const revalidate = 0

export default function Root({ children }) {
return (
Expand Down