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

Support preferredRegion and Support runtime/preferredRegion on layouts #48959

Merged
Show file tree
Hide file tree
Changes from all 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
26 changes: 23 additions & 3 deletions packages/next/src/build/analysis/get-page-static-info.ts
Expand Up @@ -35,6 +35,7 @@ export interface MiddlewareMatcher {

export interface PageStaticInfo {
runtime?: ServerRuntime
preferredRegion?: string | string[]
ssg?: boolean
ssr?: boolean
rsc?: RSCModuleType
Expand Down Expand Up @@ -81,10 +82,12 @@ function checkExports(swcAST: any): {
ssr: boolean
ssg: boolean
runtime?: string
preferredRegion?: string | string[]
} {
if (Array.isArray(swcAST?.body)) {
try {
let runtime: string | undefined
let preferredRegion: string | string[] | undefined
let ssr: boolean = false
let ssg: boolean = false

Expand All @@ -97,6 +100,22 @@ function checkExports(swcAST: any): {
if (declaration.id.value === 'runtime') {
runtime = declaration.init.value
}

if (declaration.id.value === 'preferredRegion') {
if (declaration.init.type === 'ArrayExpression') {
const elements: string[] = []
for (const element of declaration.init.elements) {
const { expression } = element
if (expression.type !== 'StringLiteral') {
continue
}
elements.push(expression.value)
}
preferredRegion = elements
} else {
preferredRegion = declaration.init.value
}
}
}
}

Expand Down Expand Up @@ -137,7 +156,7 @@ function checkExports(swcAST: any): {
}
}

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

Expand Down Expand Up @@ -322,12 +341,12 @@ export async function getPageStaticInfo(params: {

const fileContent = (await tryToReadFile(pageFilePath, !isDev)) || ''
if (
/runtime|getStaticProps|getServerSideProps|export const config/.test(
/runtime|preferredRegion|getStaticProps|getServerSideProps|export const config/.test(
fileContent
)
) {
const swcAST = await parseModule(pageFilePath, fileContent)
const { ssg, ssr, runtime } = checkExports(swcAST)
const { ssg, ssr, runtime, preferredRegion } = checkExports(swcAST)
const rsc = getRSCModuleInformation(fileContent).type

// default / failsafe value for config
Expand Down Expand Up @@ -420,6 +439,7 @@ export async function getPageStaticInfo(params: {
rsc,
...(middlewareConfig && { middleware: middlewareConfig }),
...(resolvedRuntime && { runtime: resolvedRuntime }),
preferredRegion,
}
}

Expand Down
97 changes: 93 additions & 4 deletions packages/next/src/build/entries.ts
Expand Up @@ -7,12 +7,13 @@ import type { webpack } from 'next/dist/compiled/webpack/webpack'
import type {
MiddlewareConfig,
MiddlewareMatcher,
PageStaticInfo,
} from './analysis/get-page-static-info'
import type { LoadedEnvFiles } from '@next/env'
import type { AppLoaderOptions } from './webpack/loaders/next-app-loader'

import chalk from 'next/dist/compiled/chalk'
import { posix, join } from 'path'
import { posix, join, dirname } from 'path'
import { stringify } from 'querystring'
import {
PAGES_DIR_ALIAS,
Expand Down Expand Up @@ -50,6 +51,83 @@ import { encodeMatchers } from './webpack/loaders/next-middleware-loader'
import { EdgeFunctionLoaderOptions } from './webpack/loaders/next-edge-function-loader'
import { isAppRouteRoute } from '../lib/is-app-route-route'
import { normalizeMetadataRoute } from '../lib/metadata/get-metadata-route'
import { fileExists } from '../lib/file-exists'

export async function getStaticInfoIncludingLayouts({
isInsideAppDir,
pageExtensions,
pageFilePath,
appDir,
config,
isDev,
page,
}: {
isInsideAppDir: boolean
pageExtensions: string[]
pageFilePath: string
appDir: string | undefined
config: NextConfigComplete
isDev: boolean | undefined
page: string
}): Promise<PageStaticInfo> {
const pageStaticInfo = await getPageStaticInfo({
nextConfig: config,
pageFilePath,
isDev,
page,
pageType: isInsideAppDir ? 'app' : 'pages',
})

const staticInfo: PageStaticInfo = isInsideAppDir
? {
// TODO-APP: Remove the rsc key altogether. It's no longer required.
rsc: 'server',
}
: pageStaticInfo

if (isInsideAppDir) {
const layoutFiles = []
const potentialLayoutFiles = pageExtensions.map((ext) => 'layout.' + ext)
let dir = dirname(pageFilePath)
while (dir !== appDir) {
for (const potentialLayoutFile of potentialLayoutFiles) {
const layoutFile = join(dir, potentialLayoutFile)
if (!(await fileExists(layoutFile))) {
continue
}
layoutFiles.unshift(layoutFile)
}
// Walk up the directory tree
dir = join(dir, '..')
}

for (const layoutFile of layoutFiles) {
const layoutStaticInfo = await getPageStaticInfo({
nextConfig: config,
pageFilePath: layoutFile,
isDev,
page,
pageType: isInsideAppDir ? 'app' : 'pages',
})

// Only runtime is relevant here.
if (layoutStaticInfo.runtime) {
staticInfo.runtime = layoutStaticInfo.runtime
}
if (layoutStaticInfo.preferredRegion) {
staticInfo.preferredRegion = layoutStaticInfo.preferredRegion
}
}

if (pageStaticInfo.runtime) {
staticInfo.runtime = pageStaticInfo.runtime
}
if (pageStaticInfo.preferredRegion) {
staticInfo.preferredRegion = pageStaticInfo.preferredRegion
}
}
return staticInfo
}

type ObjectValue<T> = T extends { [key: string]: infer V } ? V : never

Expand Down Expand Up @@ -204,6 +282,7 @@ export function getEdgeServerEntry(opts: {
pagesType: 'app' | 'pages' | 'root'
appDirLoader?: string
hasInstrumentationHook?: boolean
preferredRegion: string | string[] | undefined
}) {
if (
opts.pagesType === 'app' &&
Expand All @@ -215,6 +294,7 @@ export function getEdgeServerEntry(opts: {
page: opts.page,
appDirLoader: Buffer.from(opts.appDirLoader || '').toString('base64'),
nextConfigOutput: opts.config.output,
preferredRegion: opts.preferredRegion,
}

return {
Expand All @@ -240,6 +320,7 @@ export function getEdgeServerEntry(opts: {
absolutePagePath: opts.absolutePagePath,
page: opts.page,
rootDir: opts.rootDir,
preferredRegion: opts.preferredRegion,
}

return `next-edge-function-loader?${stringify(loaderParams)}!`
Expand Down Expand Up @@ -268,6 +349,7 @@ export function getEdgeServerEntry(opts: {
sriEnabled: !opts.isDev && !!opts.config.experimental.sri?.algorithm,
incrementalCacheHandlerPath:
opts.config.experimental.incrementalCacheHandlerPath,
preferredRegion: opts.preferredRegion,
}

return {
Expand Down Expand Up @@ -426,12 +508,14 @@ export async function createEntrypoints(
(absolutePagePath.startsWith(APP_DIR_ALIAS) ||
absolutePagePath.startsWith(appDir))

const staticInfo = await getPageStaticInfo({
nextConfig: config,
const staticInfo: PageStaticInfo = await getStaticInfoIncludingLayouts({
isInsideAppDir,
pageExtensions,
pageFilePath,
appDir,
config,
isDev,
page,
pageType: isInsideAppDir ? 'app' : 'pages',
})

const isServerComponent =
Expand Down Expand Up @@ -470,6 +554,7 @@ export async function createEntrypoints(
pageExtensions,
assetPrefix: config.assetPrefix,
nextConfigOutput: config.output,
preferredRegion: staticInfo.preferredRegion,
})
} else {
if (isInstrumentationHookFile(page) && pagesType === 'root') {
Expand All @@ -496,6 +581,9 @@ export async function createEntrypoints(
pageExtensions,
assetPrefix: config.assetPrefix,
nextConfigOutput: config.output,
// This isn't used with edge as it needs to be set on the entry module, which will be the `edgeServerEntry` instead.
// Still passing it here for consistency.
preferredRegion: staticInfo.preferredRegion,
}).import
}
const normalizedServerBundlePath =
Expand All @@ -513,6 +601,7 @@ export async function createEntrypoints(
middleware: staticInfo?.middleware,
pagesType,
appDirLoader,
preferredRegion: staticInfo.preferredRegion,
})
},
})
Expand Down
Expand Up @@ -36,6 +36,7 @@ export interface RSCMeta {
export interface RouteMeta {
page: string
absolutePagePath: string
preferredRegion: string | string[] | undefined
}

export interface EdgeMiddlewareMeta {
Expand Down
3 changes: 3 additions & 0 deletions packages/next/src/build/webpack/loaders/next-app-loader.ts
Expand Up @@ -27,6 +27,7 @@ export type AppLoaderOptions = {
pagePath: string
appDir: string
appPaths: readonly string[] | null
preferredRegion: string | string[] | undefined
pageExtensions: string[]
assetPrefix: string
rootDir?: string
Expand Down Expand Up @@ -421,13 +422,15 @@ const nextAppLoader: AppLoader = async function nextAppLoader() {
tsconfigPath,
isDev,
nextConfigOutput,
preferredRegion,
} = loaderOptions

const buildInfo = getModuleBuildInfo((this as any)._module)
const page = name.replace(/^app/, '')
buildInfo.route = {
page,
absolutePagePath: createAbsolutePath(appDir, pagePath),
preferredRegion,
}

const extensions = pageExtensions.map((extension) => `.${extension}`)
Expand Down
Expand Up @@ -7,6 +7,7 @@ export type EdgeAppRouteLoaderQuery = {
absolutePagePath: string
page: string
appDirLoader: string
preferredRegion: string | string[] | undefined
nextConfigOutput: NextConfig['output']
}

Expand All @@ -15,6 +16,7 @@ const EdgeAppRouteLoader: webpack.LoaderDefinitionFunction<EdgeAppRouteLoaderQue
const {
page,
absolutePagePath,
preferredRegion,
appDirLoader: appDirLoaderBase64 = '',
} = this.getOptions()

Expand All @@ -33,6 +35,7 @@ const EdgeAppRouteLoader: webpack.LoaderDefinitionFunction<EdgeAppRouteLoaderQue
buildInfo.route = {
page,
absolutePagePath,
preferredRegion,
}

const stringifiedPagePath = stringifyRequest(this, absolutePagePath)
Expand Down
@@ -1,23 +1,35 @@
import type webpack from 'webpack'
import { getModuleBuildInfo } from './get-module-build-info'
import { stringifyRequest } from '../stringify-request'

export type EdgeFunctionLoaderOptions = {
absolutePagePath: string
page: string
rootDir: string
preferredRegion: string | string[] | undefined
}

export default function middlewareLoader(this: any) {
const { absolutePagePath, page, rootDir }: EdgeFunctionLoaderOptions =
this.getOptions()
const stringifiedPagePath = stringifyRequest(this, absolutePagePath)
const buildInfo = getModuleBuildInfo(this._module)
buildInfo.nextEdgeApiFunction = {
page: page || '/',
}
buildInfo.rootDir = rootDir
const nextEdgeFunctionLoader: webpack.LoaderDefinitionFunction<EdgeFunctionLoaderOptions> =
function nextEdgeFunctionLoader(this) {
const {
absolutePagePath,
page,
rootDir,
preferredRegion,
}: EdgeFunctionLoaderOptions = this.getOptions()
const stringifiedPagePath = stringifyRequest(this, absolutePagePath)
const buildInfo = getModuleBuildInfo(this._module as any)
buildInfo.route = {
page: page || '/',
absolutePagePath,
preferredRegion,
}
buildInfo.nextEdgeApiFunction = {
page: page || '/',
}
buildInfo.rootDir = rootDir

return `
return `
import { adapter, enhanceGlobals } from 'next/dist/esm/server/web/adapter'
import {IncrementalCache} from 'next/dist/esm/server/lib/incremental-cache'

Expand All @@ -39,4 +51,6 @@ export default function middlewareLoader(this: any) {
})
}
`
}
}

export default nextEdgeFunctionLoader