diff --git a/packages/runtime/src/helpers/config.ts b/packages/runtime/src/helpers/config.ts index da916c2447..596a610dec 100644 --- a/packages/runtime/src/helpers/config.ts +++ b/packages/runtime/src/helpers/config.ts @@ -8,7 +8,6 @@ import slash from 'slash' import { HANDLER_FUNCTION_NAME, IMAGE_FUNCTION_NAME, ODB_FUNCTION_NAME } from '../constants' -import { splitApiRoutes } from './flags' import type { APILambda } from './functions' import type { RoutesManifest } from './types' import { escapeStringRegexp } from './utils' @@ -102,13 +101,13 @@ export const configureHandlerFunctions = async ({ publish, ignore = [], apiLambdas, - featureFlags, + splitApiRoutes, }: { netlifyConfig: NetlifyConfig publish: string ignore: Array apiLambdas: APILambda[] - featureFlags: Record + splitApiRoutes: boolean }) => { const config = await getRequiredServerFiles(publish) const files = config.files || [] @@ -168,7 +167,7 @@ export const configureHandlerFunctions = async ({ configureFunction(HANDLER_FUNCTION_NAME) configureFunction(ODB_FUNCTION_NAME) - if (splitApiRoutes(featureFlags)) { + if (splitApiRoutes) { for (const apiLambda of apiLambdas) { const { functionName, includedFiles } = apiLambda netlifyConfig.functions[functionName] ||= { included_files: [] } diff --git a/packages/runtime/src/helpers/flags.ts b/packages/runtime/src/helpers/flags.ts index fe0755fa32..69f633ee03 100644 --- a/packages/runtime/src/helpers/flags.ts +++ b/packages/runtime/src/helpers/flags.ts @@ -1,4 +1,6 @@ import destr from 'destr' +import { existsSync } from 'fs-extra' +import { join } from 'pathe' /** * If this flag is enabled, we generate individual Lambda functions for API Routes. @@ -11,7 +13,19 @@ import destr from 'destr' * If disabled, we bundle all API Routes into a single function. * This is can lead to large bundle sizes. * + * Relies on `next-server.js.nft.json`, which is only supported in Next.js 12+. + * * Disabled by default. Can be overriden using the NEXT_SPLIT_API_ROUTES env var. */ -export const splitApiRoutes = (featureFlags: Record): boolean => - destr(process.env.NEXT_SPLIT_API_ROUTES) ?? featureFlags.next_split_api_routes ?? false +export const splitApiRoutes = (featureFlags: Record, publish: string): boolean => { + const isEnabled = destr(process.env.NEXT_SPLIT_API_ROUTES) ?? featureFlags.next_split_api_routes ?? false + + if (isEnabled && !existsSync(join(publish, 'next-server.js.nft.json'))) { + console.warn( + 'Trace-based bundling not possible on this version of Next.js. Speed up your builds significantly by upgrading to Next.js v12 or newer.', + ) + return false + } + + return isEnabled +} diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index e823eeebbd..a1a17b5ab1 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -166,7 +166,7 @@ const plugin: NetlifyPlugin = { const buildId = readFileSync(join(publish, 'BUILD_ID'), 'utf8').trim() - const apiLambdas: APILambda[] = splitApiRoutes(featureFlags) + const apiLambdas: APILambda[] = splitApiRoutes(featureFlags, publish) ? await getAPILambdas(publish, appDir, pageExtensions) : await getExtendedApiRouteConfigs(publish, appDir, pageExtensions).then((extendedRoutes) => extendedRoutes.map(packSingleFunction), @@ -180,7 +180,7 @@ const plugin: NetlifyPlugin = { ignore, publish: relative(process.cwd(), publish), apiLambdas, - featureFlags, + splitApiRoutes: splitApiRoutes(featureFlags, publish), }) await movePublicFiles({ appDir, outdir, publish, basePath }) diff --git a/test/index.spec.ts b/test/index.spec.ts index d18baa8685..87cc555267 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -727,6 +727,16 @@ describe('onBuild()', () => { expect(existsSync(publicFile)).toBe(true) expect(await readJson(publicFile)).toMatchObject(expect.any(Array)) }) + + it('does not split APIs when .nft.json files are unavailable', async () => { + await moveNextDist() + + await unlink(path.join(process.cwd(), '.next', 'next-server.js.nft.json')) + + await nextRuntime.onBuild(defaultArgs) + + expect(netlifyConfig.functions['_api_*'].node_bundler).toEqual('nft') + }) }) describe('onPostBuild', () => {