next
Version:
The React Framework
898 lines • 134 kB
JavaScript
import '../lib/setup-exception-listeners';
import { loadEnvConfig } from '@next/env';
import { bold, yellow } from '../lib/picocolors';
import crypto from 'crypto';
import { makeRe } from 'next/dist/compiled/picomatch';
import { existsSync, promises as fs } from 'fs';
import os from 'os';
import { Worker } from '../lib/worker';
import { defaultConfig } from '../server/config-shared';
import devalue from 'next/dist/compiled/devalue';
import findUp from 'next/dist/compiled/find-up';
import { nanoid } from 'next/dist/compiled/nanoid/index.cjs';
import path from 'path';
import { STATIC_STATUS_PAGE_GET_INITIAL_PROPS_ERROR, PUBLIC_DIR_MIDDLEWARE_CONFLICT, MIDDLEWARE_FILENAME, PAGES_DIR_ALIAS, INSTRUMENTATION_HOOK_FILENAME, RSC_PREFETCH_SUFFIX, RSC_SUFFIX, NEXT_RESUME_HEADER, PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, NEXT_CACHE_REVALIDATE_TAG_TOKEN_HEADER, NEXT_CACHE_REVALIDATED_TAGS_HEADER, MATCHED_PATH_HEADER, RSC_SEGMENTS_DIR_SUFFIX, RSC_SEGMENT_SUFFIX } from '../lib/constants';
import { FileType, fileExists } from '../lib/file-exists';
import { findPagesDir } from '../lib/find-pages-dir';
import loadCustomRoutes, { normalizeRouteRegex } from '../lib/load-custom-routes';
import { nonNullable } from '../lib/non-nullable';
import { recursiveDelete } from '../lib/recursive-delete';
import { verifyPartytownSetup } from '../lib/verify-partytown-setup';
import { BUILD_ID_FILE, BUILD_MANIFEST, CLIENT_STATIC_FILES_PATH, EXPORT_DETAIL, EXPORT_MARKER, IMAGES_MANIFEST, PAGES_MANIFEST, PHASE_PRODUCTION_BUILD, PRERENDER_MANIFEST, REACT_LOADABLE_MANIFEST, ROUTES_MANIFEST, SERVER_DIRECTORY, SERVER_FILES_MANIFEST, STATIC_STATUS_PAGES, MIDDLEWARE_MANIFEST, APP_PATHS_MANIFEST, APP_PATH_ROUTES_MANIFEST, APP_BUILD_MANIFEST, RSC_MODULE_TYPES, NEXT_FONT_MANIFEST, SUBRESOURCE_INTEGRITY_MANIFEST, MIDDLEWARE_BUILD_MANIFEST, MIDDLEWARE_REACT_LOADABLE_MANIFEST, SERVER_REFERENCE_MANIFEST, FUNCTIONS_CONFIG_MANIFEST, UNDERSCORE_NOT_FOUND_ROUTE_ENTRY, UNDERSCORE_NOT_FOUND_ROUTE, DYNAMIC_CSS_MANIFEST, TURBOPACK_CLIENT_MIDDLEWARE_MANIFEST } from '../shared/lib/constants';
import { getSortedRoutes, isDynamicRoute, getSortedRouteObjects } from '../shared/lib/router/utils';
import loadConfig from '../server/config';
import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path';
import { getPagePath } from '../server/require';
import * as ciEnvironment from '../server/ci-info';
import { turborepoTraceAccess, TurborepoAccessTraceResult, writeTurborepoAccessTraceResult } from './turborepo-access-trace';
import { eventBuildOptimize, eventCliSession, eventBuildFeatureUsage, eventNextPlugins, EVENT_BUILD_FEATURE_USAGE, eventPackageUsedInGetServerSideProps, eventBuildCompleted } from '../telemetry/events';
import { Telemetry } from '../telemetry/storage';
import { hadUnsupportedValue } from './analysis/get-page-static-info';
import { createPagesMapping, getStaticInfoIncludingLayouts, sortByPageExts } from './entries';
import { PAGE_TYPES } from '../lib/page-types';
import { generateBuildId } from './generate-build-id';
import { isWriteable } from './is-writeable';
import * as Log from './output/log';
import createSpinner from './spinner';
import { trace, flushAllTraces, setGlobal } from '../trace';
import { detectConflictingPaths, computeFromManifest, getJsPageSizeInKb, printCustomRoutes, printTreeView, copyTracedFiles, isReservedPage, isAppBuiltinNotFoundPage, collectRoutesUsingEdgeRuntime, collectMeta } from './utils';
import { writeBuildId } from './write-build-id';
import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path';
import isError from '../lib/is-error';
import { isEdgeRuntime } from '../lib/is-edge-runtime';
import { recursiveCopy } from '../lib/recursive-copy';
import { recursiveReadDir } from '../lib/recursive-readdir';
import { lockfilePatchPromise, teardownTraceSubscriber } from './swc';
import { getNamedRouteRegex } from '../shared/lib/router/utils/route-regex';
import { getFilesInDir } from '../lib/get-files-in-dir';
import { eventSwcPlugins } from '../telemetry/events/swc-plugins';
import { normalizeAppPath } from '../shared/lib/router/utils/app-paths';
import { ACTION_HEADER, NEXT_ROUTER_PREFETCH_HEADER, RSC_HEADER, RSC_CONTENT_TYPE_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_DID_POSTPONE_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_REWRITTEN_PATH_HEADER, NEXT_REWRITTEN_QUERY_HEADER } from '../client/components/app-router-headers';
import { webpackBuild } from './webpack-build';
import { NextBuildContext } from './build-context';
import { normalizePathSep } from '../shared/lib/page-path/normalize-path-sep';
import { isAppRouteRoute } from '../lib/is-app-route-route';
import { createClientRouterFilter } from '../lib/create-client-router-filter';
import { createValidFileMatcher } from '../server/lib/find-page-file';
import { startTypeChecking } from './type-check';
import { generateInterceptionRoutesRewrites } from '../lib/generate-interception-routes-rewrites';
import { buildDataRoute } from '../server/lib/router-utils/build-data-route';
import { collectBuildTraces } from './collect-build-traces';
import { formatManifest } from './manifests/formatter/format-manifest';
import { recordFrameworkVersion, updateBuildDiagnostics, recordFetchMetrics } from '../diagnostics/build-diagnostics';
import { getStartServerInfo, logStartInfo } from '../server/lib/app-info-log';
import { hasCustomExportOutput } from '../export/utils';
import { buildCustomRoute } from '../lib/build-custom-route';
import { traceMemoryUsage } from '../lib/memory/trace';
import { generateEncryptionKeyBase64 } from '../server/app-render/encryption-utils-server';
import uploadTrace from '../trace/upload-trace';
import { checkIsAppPPREnabled, checkIsRoutePPREnabled } from '../server/lib/experimental/ppr';
import { FallbackMode, fallbackModeToFallbackField } from '../lib/fallback';
import { RenderingMode } from './rendering-mode';
import { getParamKeys } from '../server/request/fallback-params';
import { formatNodeOptions, getParsedNodeOptionsWithoutInspect } from '../server/lib/utils';
import { InvariantError } from '../shared/lib/invariant-error';
import { HTML_LIMITED_BOT_UA_RE_STRING } from '../shared/lib/router/utils/is-bot';
import { buildPrefetchSegmentDataRoute } from '../server/lib/router-utils/build-prefetch-segment-data-route';
import { turbopackBuild } from './turbopack-build';
/**
* The headers that are allowed to be used when revalidating routes. Currently
* this includes both headers used by the pages and app routers.
*/ const ALLOWED_HEADERS = [
'host',
MATCHED_PATH_HEADER,
PRERENDER_REVALIDATE_HEADER,
PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER,
NEXT_CACHE_REVALIDATED_TAGS_HEADER,
NEXT_CACHE_REVALIDATE_TAG_TOKEN_HEADER
];
function pageToRoute(page) {
const routeRegex = getNamedRouteRegex(page, {
prefixRouteKeys: true
});
return {
page,
regex: normalizeRouteRegex(routeRegex.re.source),
routeKeys: routeRegex.routeKeys,
namedRegex: routeRegex.namedRegex
};
}
function getCacheDir(distDir) {
const cacheDir = path.join(distDir, 'cache');
if (ciEnvironment.isCI && !ciEnvironment.hasNextSupport) {
const hasCache = existsSync(cacheDir);
if (!hasCache) {
// Intentionally not piping to stderr which is what `Log.warn` does in case people fail in CI when
// stderr is detected.
console.log(`${Log.prefixes.warn} No build cache found. Please configure build caching for faster rebuilds. Read more: https://nextjs.org/docs/messages/no-cache`);
}
}
return cacheDir;
}
async function writeFileUtf8(filePath, content) {
await fs.writeFile(filePath, content, 'utf-8');
}
function readFileUtf8(filePath) {
return fs.readFile(filePath, 'utf8');
}
async function writeManifest(filePath, manifest) {
await writeFileUtf8(filePath, formatManifest(manifest));
}
async function readManifest(filePath) {
return JSON.parse(await readFileUtf8(filePath));
}
async function writePrerenderManifest(distDir, manifest) {
await writeManifest(path.join(distDir, PRERENDER_MANIFEST), manifest);
}
async function writeClientSsgManifest(prerenderManifest, { buildId, distDir, locales }) {
const ssgPages = new Set([
...Object.entries(prerenderManifest.routes)// Filter out dynamic routes
.filter(([, { srcRoute }])=>srcRoute == null).map(([route])=>normalizeLocalePath(route, locales).pathname),
...Object.keys(prerenderManifest.dynamicRoutes)
].sort());
const clientSsgManifestContent = `self.__SSG_MANIFEST=${devalue(ssgPages)};self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()`;
await writeFileUtf8(path.join(distDir, CLIENT_STATIC_FILES_PATH, buildId, '_ssgManifest.js'), clientSsgManifestContent);
}
async function writeFunctionsConfigManifest(distDir, manifest) {
await writeManifest(path.join(distDir, SERVER_DIRECTORY, FUNCTIONS_CONFIG_MANIFEST), manifest);
}
async function writeRequiredServerFilesManifest(distDir, requiredServerFiles) {
await writeManifest(path.join(distDir, SERVER_FILES_MANIFEST), requiredServerFiles);
}
async function writeImagesManifest(distDir, config) {
var _config_images, _config_images1;
const images = {
...config.images
};
const { deviceSizes, imageSizes } = images;
images.sizes = [
...deviceSizes,
...imageSizes
];
// By default, remotePatterns will allow no remote images ([])
images.remotePatterns = ((config == null ? void 0 : (_config_images = config.images) == null ? void 0 : _config_images.remotePatterns) || []).map((p)=>({
// Modifying the manifest should also modify matchRemotePattern()
protocol: p.protocol,
hostname: makeRe(p.hostname).source,
port: p.port,
pathname: makeRe(p.pathname ?? '**', {
dot: true
}).source,
search: p.search
}));
// By default, localPatterns will allow all local images (undefined)
if (config == null ? void 0 : (_config_images1 = config.images) == null ? void 0 : _config_images1.localPatterns) {
images.localPatterns = config.images.localPatterns.map((p)=>({
// Modifying the manifest should also modify matchLocalPattern()
pathname: makeRe(p.pathname ?? '**', {
dot: true
}).source,
search: p.search
}));
}
await writeManifest(path.join(distDir, IMAGES_MANIFEST), {
version: 1,
images
});
}
const STANDALONE_DIRECTORY = 'standalone';
async function writeStandaloneDirectory(nextBuildSpan, distDir, pageKeys, denormalizedAppPages, outputFileTracingRoot, requiredServerFiles, middlewareManifest, hasNodeMiddleware, hasInstrumentationHook, staticPages, loadedEnvFiles, appDir) {
await nextBuildSpan.traceChild('write-standalone-directory').traceAsyncFn(async ()=>{
await copyTracedFiles(// requiredServerFiles.appDir Refers to the application directory, not App Router.
requiredServerFiles.appDir, distDir, pageKeys.pages, denormalizedAppPages, outputFileTracingRoot, requiredServerFiles.config, middlewareManifest, hasNodeMiddleware, hasInstrumentationHook, staticPages);
for (const file of [
...requiredServerFiles.files,
path.join(requiredServerFiles.config.distDir, SERVER_FILES_MANIFEST),
...loadedEnvFiles.reduce((acc, envFile)=>{
if ([
'.env',
'.env.production'
].includes(envFile.path)) {
acc.push(envFile.path);
}
return acc;
}, [])
]){
// requiredServerFiles.appDir Refers to the application directory, not App Router.
const filePath = path.join(requiredServerFiles.appDir, file);
const outputPath = path.join(distDir, STANDALONE_DIRECTORY, path.relative(outputFileTracingRoot, filePath));
await fs.mkdir(path.dirname(outputPath), {
recursive: true
});
await fs.copyFile(filePath, outputPath);
}
if (hasNodeMiddleware) {
const middlewareOutput = path.join(distDir, STANDALONE_DIRECTORY, path.relative(outputFileTracingRoot, distDir), SERVER_DIRECTORY, 'middleware.js');
await fs.mkdir(path.dirname(middlewareOutput), {
recursive: true
});
await fs.copyFile(path.join(distDir, SERVER_DIRECTORY, 'middleware.js'), middlewareOutput);
}
await recursiveCopy(path.join(distDir, SERVER_DIRECTORY, 'pages'), path.join(distDir, STANDALONE_DIRECTORY, path.relative(outputFileTracingRoot, distDir), SERVER_DIRECTORY, 'pages'), {
overwrite: true
});
if (appDir) {
const originalServerApp = path.join(distDir, SERVER_DIRECTORY, 'app');
if (existsSync(originalServerApp)) {
await recursiveCopy(originalServerApp, path.join(distDir, STANDALONE_DIRECTORY, path.relative(outputFileTracingRoot, distDir), SERVER_DIRECTORY, 'app'), {
overwrite: true
});
}
}
});
}
function getNumberOfWorkers(config) {
if (config.experimental.cpus && config.experimental.cpus !== defaultConfig.experimental.cpus) {
return config.experimental.cpus;
}
if (config.experimental.memoryBasedWorkersCount) {
return Math.max(Math.min(config.experimental.cpus || 1, Math.floor(os.freemem() / 1e9)), // enforce a minimum of 4 workers
4);
}
if (config.experimental.cpus) {
return config.experimental.cpus;
}
// Fall back to 4 workers if a count is not specified
return 4;
}
const staticWorkerPath = require.resolve('./worker');
const staticWorkerExposedMethods = [
'hasCustomGetInitialProps',
'isPageStatic',
'getDefinedNamedExports',
'exportPages'
];
export function createStaticWorker(config, progress) {
// Get the node options without inspect and also remove the
// --max-old-space-size flag as it can cause memory issues.
const nodeOptions = getParsedNodeOptionsWithoutInspect();
delete nodeOptions['max-old-space-size'];
delete nodeOptions['max_old_space_size'];
return new Worker(staticWorkerPath, {
logger: Log,
numWorkers: getNumberOfWorkers(config),
onActivity: ()=>{
progress == null ? void 0 : progress.run();
},
onActivityAbort: ()=>{
progress == null ? void 0 : progress.clear();
},
forkOptions: {
env: {
...process.env,
NODE_OPTIONS: formatNodeOptions(nodeOptions)
}
},
enableWorkerThreads: config.experimental.workerThreads,
exposedMethods: staticWorkerExposedMethods
});
}
async function writeFullyStaticExport(config, dir, enabledDirectories, configOutDir, nextBuildSpan) {
const exportApp = require('../export').default;
const pagesWorker = createStaticWorker(config);
const appWorker = createStaticWorker(config);
await exportApp(dir, {
buildExport: false,
nextConfig: config,
enabledDirectories,
silent: true,
outdir: path.join(dir, configOutDir),
numWorkers: getNumberOfWorkers(config)
}, nextBuildSpan);
pagesWorker.end();
appWorker.end();
}
async function getBuildId(isGenerateMode, distDir, nextBuildSpan, config) {
if (isGenerateMode) {
return await fs.readFile(path.join(distDir, 'BUILD_ID'), 'utf8');
}
return await nextBuildSpan.traceChild('generate-buildid').traceAsyncFn(()=>generateBuildId(config.generateBuildId, nanoid));
}
export default async function build(dir, reactProductionProfiling = false, debugOutput = false, runLint = true, noMangling = false, appDirOnly = false, turboNextBuild = false, experimentalBuildMode, traceUploadUrl) {
const isCompileMode = experimentalBuildMode === 'compile';
const isGenerateMode = experimentalBuildMode === 'generate';
let loadedConfig;
try {
const nextBuildSpan = trace('next-build', undefined, {
buildMode: experimentalBuildMode,
isTurboBuild: String(turboNextBuild),
version: "15.2.4"
});
NextBuildContext.nextBuildSpan = nextBuildSpan;
NextBuildContext.dir = dir;
NextBuildContext.appDirOnly = appDirOnly;
NextBuildContext.reactProductionProfiling = reactProductionProfiling;
NextBuildContext.noMangling = noMangling;
await nextBuildSpan.traceAsyncFn(async ()=>{
var _mappedPages_404;
// attempt to load global env values so they are available in next.config.js
const { loadedEnvFiles } = nextBuildSpan.traceChild('load-dotenv').traceFn(()=>loadEnvConfig(dir, false, Log));
NextBuildContext.loadedEnvFiles = loadedEnvFiles;
const turborepoAccessTraceResult = new TurborepoAccessTraceResult();
const config = await nextBuildSpan.traceChild('load-next-config').traceAsyncFn(()=>turborepoTraceAccess(()=>loadConfig(PHASE_PRODUCTION_BUILD, dir, {
// Log for next.config loading process
silent: false,
reactProductionProfiling
}), turborepoAccessTraceResult));
loadedConfig = config;
process.env.NEXT_DEPLOYMENT_ID = config.deploymentId || '';
NextBuildContext.config = config;
let configOutDir = 'out';
if (hasCustomExportOutput(config)) {
configOutDir = config.distDir;
config.distDir = '.next';
}
const distDir = path.join(dir, config.distDir);
NextBuildContext.distDir = distDir;
setGlobal('phase', PHASE_PRODUCTION_BUILD);
setGlobal('distDir', distDir);
const buildId = await getBuildId(isGenerateMode, distDir, nextBuildSpan, config);
NextBuildContext.buildId = buildId;
const customRoutes = await nextBuildSpan.traceChild('load-custom-routes').traceAsyncFn(()=>loadCustomRoutes(config));
const { headers, rewrites, redirects } = customRoutes;
const combinedRewrites = [
...rewrites.beforeFiles,
...rewrites.afterFiles,
...rewrites.fallback
];
const hasRewrites = combinedRewrites.length > 0;
NextBuildContext.hasRewrites = hasRewrites;
NextBuildContext.originalRewrites = config._originalRewrites;
NextBuildContext.originalRedirects = config._originalRedirects;
const cacheDir = getCacheDir(distDir);
const telemetry = new Telemetry({
distDir
});
setGlobal('telemetry', telemetry);
const publicDir = path.join(dir, 'public');
const { pagesDir, appDir } = findPagesDir(dir);
NextBuildContext.pagesDir = pagesDir;
NextBuildContext.appDir = appDir;
const enabledDirectories = {
app: typeof appDir === 'string',
pages: typeof pagesDir === 'string'
};
// Generate a random encryption key for this build.
// This key is used to encrypt cross boundary values and can be used to generate hashes.
const encryptionKey = await generateEncryptionKeyBase64({
isBuild: true,
distDir
});
NextBuildContext.encryptionKey = encryptionKey;
const isSrcDir = path.relative(dir, pagesDir || appDir || '').startsWith('src');
const hasPublicDir = existsSync(publicDir);
telemetry.record(eventCliSession(dir, config, {
webpackVersion: 5,
cliCommand: 'build',
isSrcDir,
hasNowJson: !!await findUp('now.json', {
cwd: dir
}),
isCustomServer: null,
turboFlag: false,
pagesDir: !!pagesDir,
appDir: !!appDir
}));
eventNextPlugins(path.resolve(dir)).then((events)=>telemetry.record(events));
eventSwcPlugins(path.resolve(dir), config).then((events)=>telemetry.record(events));
// Always log next version first then start rest jobs
const { envInfo, experimentalFeatures } = await getStartServerInfo(dir, false);
logStartInfo({
networkUrl: null,
appUrl: null,
envInfo,
experimentalFeatures
});
const ignoreESLint = Boolean(config.eslint.ignoreDuringBuilds);
const shouldLint = !ignoreESLint && runLint;
const typeCheckingOptions = {
dir,
appDir,
pagesDir,
runLint,
shouldLint,
ignoreESLint,
telemetry,
nextBuildSpan,
config,
cacheDir
};
const distDirCreated = await nextBuildSpan.traceChild('create-dist-dir').traceAsyncFn(async ()=>{
try {
await fs.mkdir(distDir, {
recursive: true
});
return true;
} catch (err) {
if (isError(err) && err.code === 'EPERM') {
return false;
}
throw err;
}
});
if (!distDirCreated || !await isWriteable(distDir)) {
throw Object.defineProperty(new Error('> Build directory is not writeable. https://nextjs.org/docs/messages/build-dir-not-writeable'), "__NEXT_ERROR_CODE", {
value: "E202",
enumerable: false,
configurable: true
});
}
if (config.cleanDistDir && !isGenerateMode) {
await recursiveDelete(distDir, /^cache/);
}
// For app directory, we run type checking after build. That's because
// we dynamically generate types for each layout and page in the app
// directory.
if (!appDir && !isCompileMode) await startTypeChecking(typeCheckingOptions);
if (appDir && 'exportPathMap' in config) {
Log.error('The "exportPathMap" configuration cannot be used with the "app" directory. Please use generateStaticParams() instead.');
await telemetry.flush();
process.exit(1);
}
const buildLintEvent = {
featureName: 'build-lint',
invocationCount: shouldLint ? 1 : 0
};
telemetry.record({
eventName: EVENT_BUILD_FEATURE_USAGE,
payload: buildLintEvent
});
const validFileMatcher = createValidFileMatcher(config.pageExtensions, appDir);
const providedPagePaths = JSON.parse(process.env.NEXT_PRIVATE_PAGE_PATHS || '[]');
let pagesPaths = Boolean(process.env.NEXT_PRIVATE_PAGE_PATHS) ? providedPagePaths : !appDirOnly && pagesDir ? await nextBuildSpan.traceChild('collect-pages').traceAsyncFn(()=>recursiveReadDir(pagesDir, {
pathnameFilter: validFileMatcher.isPageFile
})) : [];
const middlewareDetectionRegExp = new RegExp(`^${MIDDLEWARE_FILENAME}\\.(?:${config.pageExtensions.join('|')})$`);
const instrumentationHookDetectionRegExp = new RegExp(`^${INSTRUMENTATION_HOOK_FILENAME}\\.(?:${config.pageExtensions.join('|')})$`);
const rootDir = path.join(pagesDir || appDir, '..');
const includes = [
middlewareDetectionRegExp,
instrumentationHookDetectionRegExp
];
const rootPaths = Array.from(await getFilesInDir(rootDir)).filter((file)=>includes.some((include)=>include.test(file))).sort(sortByPageExts(config.pageExtensions)).map((file)=>path.join(rootDir, file).replace(dir, ''));
const hasInstrumentationHook = rootPaths.some((p)=>p.includes(INSTRUMENTATION_HOOK_FILENAME));
const hasMiddlewareFile = rootPaths.some((p)=>p.includes(MIDDLEWARE_FILENAME));
NextBuildContext.hasInstrumentationHook = hasInstrumentationHook;
const previewProps = {
previewModeId: crypto.randomBytes(16).toString('hex'),
previewModeSigningKey: crypto.randomBytes(32).toString('hex'),
previewModeEncryptionKey: crypto.randomBytes(32).toString('hex')
};
NextBuildContext.previewProps = previewProps;
const mappedPages = await nextBuildSpan.traceChild('create-pages-mapping').traceAsyncFn(()=>createPagesMapping({
isDev: false,
pageExtensions: config.pageExtensions,
pagesType: PAGE_TYPES.PAGES,
pagePaths: pagesPaths,
pagesDir,
appDir
}));
NextBuildContext.mappedPages = mappedPages;
let mappedAppPages;
let denormalizedAppPages;
if (appDir) {
const providedAppPaths = JSON.parse(process.env.NEXT_PRIVATE_APP_PATHS || '[]');
let appPaths = Boolean(process.env.NEXT_PRIVATE_APP_PATHS) ? providedAppPaths : await nextBuildSpan.traceChild('collect-app-paths').traceAsyncFn(()=>recursiveReadDir(appDir, {
pathnameFilter: (absolutePath)=>validFileMatcher.isAppRouterPage(absolutePath) || // For now we only collect the root /not-found page in the app
// directory as the 404 fallback
validFileMatcher.isRootNotFound(absolutePath),
ignorePartFilter: (part)=>part.startsWith('_')
}));
mappedAppPages = await nextBuildSpan.traceChild('create-app-mapping').traceAsyncFn(()=>createPagesMapping({
pagePaths: appPaths,
isDev: false,
pagesType: PAGE_TYPES.APP,
pageExtensions: config.pageExtensions,
pagesDir,
appDir
}));
NextBuildContext.mappedAppPages = mappedAppPages;
}
const mappedRootPaths = await createPagesMapping({
isDev: false,
pageExtensions: config.pageExtensions,
pagePaths: rootPaths,
pagesType: PAGE_TYPES.ROOT,
pagesDir: pagesDir,
appDir
});
NextBuildContext.mappedRootPaths = mappedRootPaths;
const pagesPageKeys = Object.keys(mappedPages);
const conflictingAppPagePaths = [];
const appPageKeys = new Set();
if (mappedAppPages) {
denormalizedAppPages = Object.keys(mappedAppPages);
for (const appKey of denormalizedAppPages){
const normalizedAppPageKey = normalizeAppPath(appKey);
const pagePath = mappedPages[normalizedAppPageKey];
if (pagePath) {
const appPath = mappedAppPages[appKey];
conflictingAppPagePaths.push([
pagePath.replace(/^private-next-pages/, 'pages'),
appPath.replace(/^private-next-app-dir/, 'app')
]);
}
appPageKeys.add(normalizedAppPageKey);
}
}
const appPaths = Array.from(appPageKeys);
// Interception routes are modelled as beforeFiles rewrites
rewrites.beforeFiles.push(...generateInterceptionRoutesRewrites(appPaths, config.basePath));
NextBuildContext.rewrites = rewrites;
const totalAppPagesCount = appPaths.length;
const pageKeys = {
pages: pagesPageKeys,
app: appPaths.length > 0 ? appPaths : undefined
};
// Turbopack already handles conflicting app and page routes.
if (!turboNextBuild) {
const numConflictingAppPaths = conflictingAppPagePaths.length;
if (mappedAppPages && numConflictingAppPaths > 0) {
Log.error(`Conflicting app and page file${numConflictingAppPaths === 1 ? ' was' : 's were'} found, please remove the conflicting files to continue:`);
for (const [pagePath, appPath] of conflictingAppPagePaths){
Log.error(` "${pagePath}" - "${appPath}"`);
}
await telemetry.flush();
process.exit(1);
}
}
const conflictingPublicFiles = [];
const hasPages404 = (_mappedPages_404 = mappedPages['/404']) == null ? void 0 : _mappedPages_404.startsWith(PAGES_DIR_ALIAS);
const hasApp404 = !!(mappedAppPages == null ? void 0 : mappedAppPages[UNDERSCORE_NOT_FOUND_ROUTE_ENTRY]);
const hasCustomErrorPage = mappedPages['/_error'].startsWith(PAGES_DIR_ALIAS);
if (hasPublicDir) {
const hasPublicUnderScoreNextDir = existsSync(path.join(publicDir, '_next'));
if (hasPublicUnderScoreNextDir) {
throw Object.defineProperty(new Error(PUBLIC_DIR_MIDDLEWARE_CONFLICT), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
});
}
}
await nextBuildSpan.traceChild('public-dir-conflict-check').traceAsyncFn(async ()=>{
// Check if pages conflict with files in `public`
// Only a page of public file can be served, not both.
for(const page in mappedPages){
const hasPublicPageFile = await fileExists(path.join(publicDir, page === '/' ? '/index' : page), FileType.File);
if (hasPublicPageFile) {
conflictingPublicFiles.push(page);
}
}
const numConflicting = conflictingPublicFiles.length;
if (numConflicting) {
throw Object.defineProperty(new Error(`Conflicting public and page file${numConflicting === 1 ? ' was' : 's were'} found. https://nextjs.org/docs/messages/conflicting-public-file-page\n${conflictingPublicFiles.join('\n')}`), "__NEXT_ERROR_CODE", {
value: "E270",
enumerable: false,
configurable: true
});
}
});
const nestedReservedPages = pageKeys.pages.filter((page)=>{
return page.match(/\/(_app|_document|_error)$/) && path.dirname(page) !== '/';
});
if (nestedReservedPages.length) {
Log.warn(`The following reserved Next.js pages were detected not directly under the pages directory:\n` + nestedReservedPages.join('\n') + `\nSee more info here: https://nextjs.org/docs/messages/nested-reserved-page\n`);
}
const restrictedRedirectPaths = [
'/_next'
].map((p)=>config.basePath ? `${config.basePath}${p}` : p);
const isAppDynamicIOEnabled = Boolean(config.experimental.dynamicIO);
const isAuthInterruptsEnabled = Boolean(config.experimental.authInterrupts);
const isAppPPREnabled = checkIsAppPPREnabled(config.experimental.ppr);
const routesManifestPath = path.join(distDir, ROUTES_MANIFEST);
const routesManifest = nextBuildSpan.traceChild('generate-routes-manifest').traceFn(()=>{
const sortedRoutes = getSortedRoutes([
...pageKeys.pages,
...pageKeys.app ?? []
]);
const dynamicRoutes = [];
const staticRoutes = [];
for (const route of sortedRoutes){
if (isDynamicRoute(route)) {
dynamicRoutes.push(pageToRoute(route));
} else if (!isReservedPage(route)) {
staticRoutes.push(pageToRoute(route));
}
}
return {
version: 3,
pages404: true,
caseSensitive: !!config.experimental.caseSensitiveRoutes,
basePath: config.basePath,
redirects: redirects.map((r)=>buildCustomRoute('redirect', r, restrictedRedirectPaths)),
headers: headers.map((r)=>buildCustomRoute('header', r)),
dynamicRoutes,
staticRoutes,
dataRoutes: [],
i18n: config.i18n || undefined,
rsc: {
header: RSC_HEADER,
// This vary header is used as a default. It is technically re-assigned in `base-server`,
// and may include an additional Vary option for `Next-URL`.
varyHeader: `${RSC_HEADER}, ${NEXT_ROUTER_STATE_TREE_HEADER}, ${NEXT_ROUTER_PREFETCH_HEADER}, ${NEXT_ROUTER_SEGMENT_PREFETCH_HEADER}`,
prefetchHeader: NEXT_ROUTER_PREFETCH_HEADER,
didPostponeHeader: NEXT_DID_POSTPONE_HEADER,
contentTypeHeader: RSC_CONTENT_TYPE_HEADER,
suffix: RSC_SUFFIX,
prefetchSuffix: RSC_PREFETCH_SUFFIX,
prefetchSegmentHeader: NEXT_ROUTER_SEGMENT_PREFETCH_HEADER,
prefetchSegmentSuffix: RSC_SEGMENT_SUFFIX,
prefetchSegmentDirSuffix: RSC_SEGMENTS_DIR_SUFFIX
},
rewriteHeaders: {
pathHeader: NEXT_REWRITTEN_PATH_HEADER,
queryHeader: NEXT_REWRITTEN_QUERY_HEADER
},
skipMiddlewareUrlNormalize: config.skipMiddlewareUrlNormalize,
ppr: isAppPPREnabled ? {
chain: {
headers: {
[NEXT_RESUME_HEADER]: '1'
}
}
} : undefined
};
});
if (rewrites.beforeFiles.length === 0 && rewrites.fallback.length === 0) {
routesManifest.rewrites = rewrites.afterFiles.map((r)=>buildCustomRoute('rewrite', r));
} else {
routesManifest.rewrites = {
beforeFiles: rewrites.beforeFiles.map((r)=>buildCustomRoute('rewrite', r)),
afterFiles: rewrites.afterFiles.map((r)=>buildCustomRoute('rewrite', r)),
fallback: rewrites.fallback.map((r)=>buildCustomRoute('rewrite', r))
};
}
let clientRouterFilters;
if (config.experimental.clientRouterFilter) {
const nonInternalRedirects = (config._originalRedirects || []).filter((r)=>!r.internal);
clientRouterFilters = createClientRouterFilter([
...appPaths
], config.experimental.clientRouterFilterRedirects ? nonInternalRedirects : [], config.experimental.clientRouterFilterAllowedRate);
NextBuildContext.clientRouterFilters = clientRouterFilters;
}
// Ensure commonjs handling is used for files in the distDir (generally .next)
// Files outside of the distDir can be "type": "module"
await writeFileUtf8(path.join(distDir, 'package.json'), '{"type": "commonjs"}');
// These are written to distDir, so they need to come after creating and cleaning distDr.
await recordFrameworkVersion("15.2.4");
await updateBuildDiagnostics({
buildStage: 'start'
});
const outputFileTracingRoot = config.outputFileTracingRoot || dir;
const pagesManifestPath = path.join(distDir, SERVER_DIRECTORY, PAGES_MANIFEST);
let buildTraceContext;
let buildTracesPromise = undefined;
// If there's has a custom webpack config and disable the build worker.
// Otherwise respect the option if it's set.
const useBuildWorker = config.experimental.webpackBuildWorker || config.experimental.webpackBuildWorker === undefined && !config.webpack;
const runServerAndEdgeInParallel = config.experimental.parallelServerCompiles;
const collectServerBuildTracesInParallel = config.experimental.parallelServerBuildTraces || config.experimental.parallelServerBuildTraces === undefined && isCompileMode;
nextBuildSpan.setAttribute('has-custom-webpack-config', String(!!config.webpack));
nextBuildSpan.setAttribute('use-build-worker', String(useBuildWorker));
if (!useBuildWorker && (runServerAndEdgeInParallel || collectServerBuildTracesInParallel)) {
throw Object.defineProperty(new Error('The "parallelServerBuildTraces" and "parallelServerCompiles" options may only be used when build workers can be used. Read more: https://nextjs.org/docs/messages/parallel-build-without-worker'), "__NEXT_ERROR_CODE", {
value: "E101",
enumerable: false,
configurable: true
});
}
Log.info('Creating an optimized production build ...');
traceMemoryUsage('Starting build', nextBuildSpan);
await updateBuildDiagnostics({
buildStage: 'compile',
buildOptions: {
useBuildWorker: String(useBuildWorker)
}
});
let shutdownPromise = Promise.resolve();
if (!isGenerateMode) {
if (turboNextBuild) {
const { duration: compilerDuration, shutdownPromise: p, ...rest } = await turbopackBuild(process.env.NEXT_TURBOPACK_USE_WORKER === undefined || process.env.NEXT_TURBOPACK_USE_WORKER !== '0');
shutdownPromise = p;
traceMemoryUsage('Finished build', nextBuildSpan);
buildTraceContext = rest.buildTraceContext;
const durationString = durationToString(compilerDuration);
Log.event(`Compiled successfully in ${durationString}`);
telemetry.record(eventBuildCompleted(pagesPaths, {
durationInSeconds: Math.round(compilerDuration),
totalAppPagesCount
}));
} else {
if (runServerAndEdgeInParallel || collectServerBuildTracesInParallel) {
let durationInSeconds = 0;
await updateBuildDiagnostics({
buildStage: 'compile-server'
});
const serverBuildPromise = webpackBuild(useBuildWorker, [
'server'
]).then((res)=>{
traceMemoryUsage('Finished server compilation', nextBuildSpan);
buildTraceContext = res.buildTraceContext;
durationInSeconds += res.duration;
if (collectServerBuildTracesInParallel) {
const buildTraceWorker = new Worker(require.resolve('./collect-build-traces'), {
numWorkers: 1,
exposedMethods: [
'collectBuildTraces'
]
});
buildTracesPromise = buildTraceWorker.collectBuildTraces({
dir,
config,
distDir,
// Serialize Map as this is sent to the worker.
edgeRuntimeRoutes: collectRoutesUsingEdgeRuntime(new Map()),
staticPages: [],
hasSsrAmpPages: false,
buildTraceContext,
outputFileTracingRoot
}).catch((err)=>{
console.error(err);
process.exit(1);
});
}
});
if (!runServerAndEdgeInParallel) {
await serverBuildPromise;
await updateBuildDiagnostics({
buildStage: 'webpack-compile-edge-server'
});
}
const edgeBuildPromise = webpackBuild(useBuildWorker, [
'edge-server'
]).then((res)=>{
durationInSeconds += res.duration;
traceMemoryUsage('Finished edge-server compilation', nextBuildSpan);
});
if (runServerAndEdgeInParallel) {
await serverBuildPromise;
await updateBuildDiagnostics({
buildStage: 'webpack-compile-edge-server'
});
}
await edgeBuildPromise;
await updateBuildDiagnostics({
buildStage: 'webpack-compile-client'
});
await webpackBuild(useBuildWorker, [
'client'
]).then((res)=>{
durationInSeconds += res.duration;
traceMemoryUsage('Finished client compilation', nextBuildSpan);
});
const durationString = durationToString(durationInSeconds);
Log.event(`Compiled successfully in ${durationString}`);
telemetry.record(eventBuildCompleted(pagesPaths, {
durationInSeconds,
totalAppPagesCount
}));
} else {
const { duration: compilerDuration, ...rest } = await webpackBuild(useBuildWorker, null);
traceMemoryUsage('Finished build', nextBuildSpan);
buildTraceContext = rest.buildTraceContext;
telemetry.record(eventBuildCompleted(pagesPaths, {
durationInSeconds: compilerDuration,
totalAppPagesCount
}));
}
}
}
// For app directory, we run type checking after build.
if (appDir && !isCompileMode && !isGenerateMode) {
await updateBuildDiagnostics({
buildStage: 'type-checking'
});
await startTypeChecking(typeCheckingOptions);
traceMemoryUsage('Finished type checking', nextBuildSpan);
}
const postCompileSpinner = createSpinner('Collecting page data');
const buildManifestPath = path.join(distDir, BUILD_MANIFEST);
const appBuildManifestPath = path.join(distDir, APP_BUILD_MANIFEST);
let staticAppPagesCount = 0;
let serverAppPagesCount = 0;
let edgeRuntimeAppCount = 0;
let edgeRuntimePagesCount = 0;
const ssgPages = new Set();
const ssgStaticFallbackPages = new Set();
const ssgBlockingFallbackPages = new Set();
const staticPages = new Set();
const invalidPages = new Set();
const hybridAmpPages = new Set();
const serverPropsPages = new Set();
const additionalPaths = new Map();
const staticPaths = new Map();
const prospectiveRenders = new Map();
const appNormalizedPaths = new Map();
const fallbackModes = new Map();
const appDefaultConfigs = new Map();
const pageInfos = new Map();
let pagesManifest = await readManifest(pagesManifestPath);
const buildManifest = await readManifest(buildManifestPath);
const appBuildManifest = appDir ? await readManifest(appBuildManifestPath) : undefined;
const appPathRoutes = {};
if (appDir) {
const appPathsManifest = await readManifest(path.join(distDir, SERVER_DIRECTORY, APP_PATHS_MANIFEST));
for(const key in appPathsManifest){
appPathRoutes[key] = normalizeAppPath(key);
}
await writeManifest(path.join(distDir, APP_PATH_ROUTES_MANIFEST), appPathRoutes);
}
process.env.NEXT_PHASE = PHASE_PRODUCTION_BUILD;
const worker = createStaticWorker(config);
const analysisBegin = process.hrtime();
const staticCheckSpan = nextBuildSpan.traceChild('static-check');
const functionsConfigManifest = {
version: 1,
functions: {}
};
const { customAppGetInitialProps, namedExports, isNextImageImported, hasSsrAmpPages, hasNonStaticErrorPage } = await staticCheckSpan.traceAsyncFn(async ()=>{
var _config_experimental_sri;
if (isCompileMode) {
return {
customAppGetInitialProps: false,
namedExports: [],
isNextImageImported: true,
hasSsrAmpPages: !!pagesDir,
hasNonStaticErrorPage: true
};
}
const { configFileName, publicRuntimeConfig, serverRuntimeConfig } = config;
const runtimeEnvConfig = {
publicRuntimeConfig,
serverRuntimeConfig
};
const sriEnabled = Boolean((_config_experimental_sri = config.experimental.sri) == null ? void 0 : _config_experimental_sri.algorithm);
const nonStaticErrorPageSpan = staticCheckSpan.traceChild('check-static-error-page');
const errorPageHasCustomGetInitialProps = nonStaticErrorPageSpan.traceAsyncFn(async ()=>hasCustomErrorPage && await worker.hasCustomGetInitialProps({
page: '/_error',
distDir,
runtimeEnvConfig,
checkingApp: false,
sriEnabled
}));
const errorPageStaticResult = nonStaticErrorPageSpan.traceAsyncFn(async ()=>{
var _config_i18n, _config_i18n1;
return hasCustomErrorPage && worker.isPageStatic({
dir,
page: '/_error',
distDir,
configFileName,
runtimeEnvConfig,
dynamicIO: isAppDynamicIOEnabled,
authInterrupts: isAuthInterruptsEnabled,
httpAgentOptions: config.httpAgentOptions,
locales: (_config_i18n = config.i18n) == null ? void 0 : _config_i18n.locales,
defaultLocale: (_config_i18n1 = config.i18n) == null ? void 0 : _config_i18n1.defaultLocale,
nextConfigOutput: config.output,
pprConfig: config.experimental.ppr,
cacheLifeProfiles: config.experimental.cacheLife,
buildId,
sriEnabled
});
});
const appPageToCheck = '/_app';
const customAppGetInitialPropsPromise = worker.hasCustomGetInitialProps({
page: appPageToCheck,
distDir,
runtimeEnvConfig,
checkingApp: true,
sriEnabled
});
const namedExportsPromise = worker.getDefinedNamedExports({
page: appPageToCheck,
distDir,
runtimeEnvConfig,
sriEnabled
});
// eslint-disable-next-line @typescript-eslint/no-shadow
let isNextImageImported;
// eslint-disable-next-line @typescript-eslint/no-shadow
let hasSsrAmpPages = false;
const computedManifestData = await computeFromManifest({
build: buildManifest,
app: appBuildManifest
}, distDir, config.experimental.gzipSize);
const middlewareManifest = require(path.join(distDir, SERVER_DIRECTORY, MIDDLEWARE_MANIFEST));
const actionManifest = appDir ? require(path.join(distDir, SERVER_DIRECTORY, SERVER_REFERENCE_MANIFEST + '.json')) : null;
const entriesWithAction = a