跳到内容
配置next.config.js 选项experimental.adapterPath

experimental.adapterPath

Next.js 提供了一个实验性 API,允许您创建自定义适配器以介入构建过程。这对于需要修改 Next.js 配置或处理构建输出的部署平台或自定义构建集成非常有用。

配置

要使用适配器,请在 experimental.adapterPath 中指定适配器模块的路径

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    adapterPath: require.resolve('./my-adapter.js'),
  },
}
 
module.exports = nextConfig

创建适配器

适配器是一个模块,它导出一个实现了 NextAdapter 接口的对象

export interface NextAdapter {
  name: string
  modifyConfig?: (
    config: NextConfigComplete,
    ctx: {
      phase: PHASE_TYPE
    }
  ) => Promise<NextConfigComplete> | NextConfigComplete
  onBuildComplete?: (ctx: {
    routes: {
      headers: Array<ManifestHeaderRoute>
      redirects: Array<ManifestRedirectRoute>
      rewrites: {
        beforeFiles: Array<ManifestRewriteRoute>
        afterFiles: Array<ManifestRewriteRoute>
        fallback: Array<ManifestRewriteRoute>
      }
      dynamicRoutes: ReadonlyArray<ManifestRoute>
    }
    outputs: AdapterOutputs
    projectDir: string
    repoRoot: string
    distDir: string
    config: NextConfigComplete
    nextVersion: string
  }) => Promise<void> | void
}

基本适配器结构

这是一个最小的适配器示例

my-adapter.js
/** @type {import('next').NextAdapter} */
const adapter = {
  name: 'my-custom-adapter',
 
  async modifyConfig(config, { phase }) {
    // Modify the Next.js config based on the build phase
    if (phase === 'phase-production-build') {
      return {
        ...config,
        // Add your modifications
      }
    }
    return config
  },
 
  async onBuildComplete({
    routes,
    outputs,
    projectDir,
    repoRoot,
    distDir,
    config,
    nextVersion,
  }) {
    // Process the build output
    console.log('Build completed with', outputs.pages.length, 'pages')
 
    // Access different output types
    for (const page of outputs.pages) {
      console.log('Page:', page.pathname, 'at', page.filePath)
    }
 
    for (const apiRoute of outputs.pagesApi) {
      console.log('API Route:', apiRoute.pathname, 'at', apiRoute.filePath)
    }
 
    for (const appPage of outputs.appPages) {
      console.log('App Page:', appPage.pathname, 'at', appPage.filePath)
    }
 
    for (const prerender of outputs.prerenders) {
      console.log('Prerendered:', prerender.pathname)
    }
  },
}
 
module.exports = adapter

API 参考

modifyConfig(config, context)

对于任何加载 next.config 的 CLI 命令都会调用此函数,以允许修改配置。

参数

  • config: 完整的 Next.js 配置对象
  • context.phase: 当前构建阶段(参阅阶段

返回: 修改后的配置对象(可以是异步的)

onBuildComplete(context)

在构建过程完成后调用,其中包含有关路由和输出的详细信息。

参数

  • routes: 包含用于标头、重定向、重写和动态路由的路由清单的对象
    • routes.headers: 标头路由对象数组,包含 sourcesourceRegexheadershasmissing 和可选的 priority 字段
    • routes.redirects: 重定向路由对象数组,包含 sourcesourceRegexdestinationstatusCodehasmissing 和可选的 priority 字段
    • routes.rewrites: 包含 beforeFilesafterFilesfallback 数组的对象,每个数组都包含重写路由对象,其中包含 sourcesourceRegexdestinationhasmissing 字段
    • routes.dynamicRoutes: 动态路由对象数组,包含 sourcesourceRegexdestinationhasmissing 字段
  • outputs: 按类型组织的详细构建输出信息
  • projectDir: Next.js 项目目录的绝对路径
  • repoRoot: 检测到的仓库根目录的绝对路径
  • distDir: 构建输出目录的绝对路径
  • config: 最终的 Next.js 配置(已应用 modifyConfig
  • nextVersion: 正在使用的 Next.js 版本
  • buildId: 当前构建的唯一标识符

输出类型

outputs 对象包含不同输出类型的数组

页面 (outputs.pages)

来自 pages/ 目录的 React 页面

{
  type: 'PAGES'
  id: string           // Route identifier
  filePath: string     // Path to the built file
  pathname: string     // URL pathname
  sourcePage: string   // Original source file path in pages/ directory
  runtime: 'nodejs' | 'edge'
  assets: Record<string, string>  // Traced dependencies (key: relative path from repo root, value: absolute path)
  wasmAssets?: Record<string, string>  // Bundled wasm files (key: name, value: absolute path)
  config: {
    maxDuration?: number
    preferredRegion?: string | string[]
    env?: Record<string, string>  // Environment variables (edge runtime only)
  }
}

API 路由 (outputs.pagesApi)

来自 pages/api/ 的 API 路由

{
  type: 'PAGES_API'
  id: string
  filePath: string
  pathname: string
  sourcePage: string   // Original relative source file path
  runtime: 'nodejs' | 'edge'
  assets: Record<string, string>
  wasmAssets?: Record<string, string>
  config: {
    maxDuration?: number
    preferredRegion?: string | string[]
    env?: Record<string, string>
  }
}

App 页面 (outputs.appPages)

来自 app/ 目录的 React 页面,文件名为 page.{js,ts,jsx,tsx}

{
  type: 'APP_PAGE'
  id: string
  filePath: string
  pathname: string     // Includes .rsc suffix for RSC routes
  sourcePage: string   // Original relative source file path
  runtime: 'nodejs' | 'edge'
  assets: Record<string, string>
  wasmAssets?: Record<string, string>
  config: {
    maxDuration?: number
    preferredRegion?: string | string[]
    env?: Record<string, string>
  }
}

App 路由 (outputs.appRoutes)

来自 app/ 的 API 和元数据路由,文件名为 route.{js,ts,jsx,tsx}

{
  type: 'APP_ROUTE'
  id: string
  filePath: string
  pathname: string
  sourcePage: string
  runtime: 'nodejs' | 'edge'
  assets: Record<string, string>
  wasmAssets?: Record<string, string>
  config: {
    maxDuration?: number
    preferredRegion?: string | string[]
    env?: Record<string, string>
  }
}

预渲染 (outputs.prerenders)

启用 ISR 的路由和静态预渲染

{
  type: 'PRERENDER'
  id: string
  pathname: string
  parentOutputId: string  // ID of the source page/route
  groupId: number        // Revalidation group identifier (prerenders with same groupId revalidate together)
  pprChain?: {
    headers: Record<string, string>  // PPR chain headers (e.g., 'x-nextjs-resume': '1')
  }
  parentFallbackMode?: 'blocking' | false | null  // Fallback mode from getStaticPaths
  fallback?: {
    filePath: string
    initialStatus?: number
    initialHeaders?: Record<string, string | string[]>
    initialExpiration?: number
    initialRevalidate?: number
    postponedState?: string  // PPR postponed state
  }
  config: {
    allowQuery?: string[]     // Allowed query parameters
    allowHeader?: string[]    // Allowed headers for ISR
    bypassFor?: RouteHas[]    // Cache bypass conditions
    renderingMode?: RenderingMode
    bypassToken?: string
  }
}

静态文件 (outputs.staticFiles)

静态资产和自动静态优化页面

{
  type: 'STATIC_FILE'
  id: string
  filePath: string
  pathname: string
}

中间件 (outputs.middleware)

中间件函数(如果存在)

{
  type: 'MIDDLEWARE'
  id: string
  filePath: string
  pathname: string      // Always '/_middleware'
  sourcePage: string    // Always 'middleware'
  runtime: 'nodejs' | 'edge'
  assets: Record<string, string>
  wasmAssets?: Record<string, string>
  config: {
    maxDuration?: number
    preferredRegion?: string | string[]
    env?: Record<string, string>
    matchers?: Array<{
      source: string
      sourceRegex: string
      has: RouteHas[] | undefined
      missing: RouteHas[] | undefined
    }>
  }
}

路由信息

onBuildComplete 中的 routes 对象提供了完整的路由信息,其中包含已处理的模式,可用于部署

标头

每个标头路由包括

  • source: 原始路由模式(例如,/about
  • sourceRegex: 用于匹配请求的编译正则表达式
  • headers: 要应用的标头键值对
  • has: 必须满足的可选条件
  • missing: 必须不满足的可选条件
  • priority: 内部路由的可选标志

重定向

每个重定向路由包括

  • source: 原始路由模式
  • sourceRegex: 用于匹配的编译正则表达式
  • destination: 目标 URL(可包含捕获组)
  • statusCode: HTTP 状态码(301、302、307、308)
  • has: 可选的肯定条件
  • missing: 可选的否定条件
  • priority: 内部路由的可选标志

重写

重写分为三个阶段

  • beforeFiles: 在文件系统(包括页面和公共文件)之前检查
  • afterFiles: 在页面/公共文件之后但在动态路由之前检查
  • fallback: 在所有其他路由之后检查

每个重写包括 sourcesourceRegexdestinationhasmissing

动态路由

从动态路由段(例如,[slug][...path])生成。每个路由都包括

  • source: 路由模式
  • sourceRegex: 带有命名捕获组的编译正则表达式
  • destination: 带有参数替换的内部目标
  • has: 可选的肯定条件
  • missing: 可选的否定条件

用例

适配器的常见用例包括

  • 部署平台集成:自动为特定托管平台配置构建输出
  • 资产处理:转换或优化构建输出
  • 监控集成:收集构建指标和路由信息
  • 自定义打包:以平台特定格式打包输出
  • 构建验证:确保输出满足特定要求
  • 路由生成:使用已处理的路由信息生成平台特定路由配置