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 { Sema } from "next/dist/compiled/async-sema"; 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 } 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 { validateTurboNextConfig } from "../lib/turbopack-warning"; import { BUILD_ID_FILE, BUILD_MANIFEST, CLIENT_STATIC_FILES_PATH, EXPORT_DETAIL, EXPORT_MARKER, AUTOMATIC_FONT_OPTIMIZATION_MANIFEST, 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 } from "../shared/lib/constants"; import { getSortedRoutes, isDynamicRoute } 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 "../telemetry/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 { isDynamicMetadataRoute, getPageStaticInfo } from "./analysis/get-page-static-info"; import { createPagesMapping, getPageFilePath, 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, serializePageInfos } 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 { loadBindings, lockfilePatchPromise, teardownTraceSubscriber, teardownHeapProfiler, createDefineEnv } 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, NEXT_ROUTER_PREFETCH_HEADER, RSC_HEADER, RSC_CONTENT_TYPE_HEADER, NEXT_ROUTER_STATE_TREE, NEXT_DID_POSTPONE_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 { initialize as initializeIncrementalCache } from "../server/lib/incremental-cache-server"; import { nodeFs } from "../server/lib/node-fs-methods"; import { collectBuildTraces } from "./collect-build-traces"; import { formatManifest } from "./manifests/formatter/format-manifest"; import { getStartServerInfo, logStartInfo } from "../server/lib/app-info-log"; import { hasCustomExportOutput } from "../export/utils"; import { interopDefault } from "../lib/interop-default"; import { formatDynamicImportPath } from "../lib/format-dynamic-import-path"; import { isInterceptionRouteAppPath } from "../server/future/helpers/interception-routes"; import { getTurbopackJsConfig, handleEntrypoints, handleRouteType, handlePagesErrorRoute, formatIssue, isRelevantWarning } from "../server/dev/turbopack-utils"; import { TurbopackManifestLoader } from "../server/dev/turbopack/manifest-loader"; import { buildCustomRoute } from "../lib/build-custom-route"; import { createProgress } from "./progress"; import { traceMemoryUsage } from "../lib/memory/trace"; import { generateEncryptionKeyBase64 } from "../server/app-render/encryption-utils"; function pageToRoute(page) { const routeRegex = getNamedRouteRegex(page, 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; const images = { ...config.images }; const { deviceSizes, imageSizes } = images; images.sizes = [ ...deviceSizes, ...imageSizes ]; images.remotePatterns = ((config == null ? void 0 : (_config_images = config.images) == null ? void 0 : _config_images.remotePatterns) || []).map((p)=>({ // Should be the same as matchRemotePattern() protocol: p.protocol, hostname: makeRe(p.hostname).source, port: p.port, pathname: makeRe(p.pathname ?? "**", { dot: true }).source })); await writeManifest(path.join(distDir, IMAGES_MANIFEST), { version: 1, images }); } const STANDALONE_DIRECTORY = "standalone"; async function writeStandaloneDirectory(nextBuildSpan, distDir, pageKeys, denormalizedAppPages, outputFileTracingRoot, requiredServerFiles, middlewareManifest, 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, 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); } 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", "exportPage" ]; function createStaticWorker(config, incrementalCacheIpcPort, incrementalCacheIpcValidationKey) { let infoPrinted = false; const timeout = config.staticPageGenerationTimeout || 0; return new Worker(staticWorkerPath, { timeout: timeout * 1000, logger: Log, onRestart: (method, args, attempts)=>{ if (method === "exportPage") { const [arg] = args; const pagePath = arg.path; if (attempts >= 3) { throw new Error(`Static page generation for ${pagePath} is still timing out after 3 attempts. See more info here https://nextjs.org/docs/messages/static-page-generation-timeout`); } Log.warn(`Restarted static page generation for ${pagePath} because it took more than ${timeout} seconds`); } else { const [arg] = args; const pagePath = arg.page; if (attempts >= 2) { throw new Error(`Collecting page data for ${pagePath} is still timing out after 2 attempts. See more info here https://nextjs.org/docs/messages/page-data-collection-timeout`); } Log.warn(`Restarted collecting page data for ${pagePath} because it took more than ${timeout} seconds`); } if (!infoPrinted) { Log.warn("See more info here https://nextjs.org/docs/messages/static-page-generation-timeout"); infoPrinted = true; } }, numWorkers: getNumberOfWorkers(config), forkOptions: { env: { ...process.env, __NEXT_INCREMENTAL_CACHE_IPC_PORT: incrementalCacheIpcPort ? incrementalCacheIpcPort + "" : undefined, __NEXT_INCREMENTAL_CACHE_IPC_KEY: incrementalCacheIpcValidationKey } }, enableWorkerThreads: config.experimental.workerThreads, exposedMethods: staticWorkerExposedMethods }); } async function writeFullyStaticExport(config, incrementalCacheIpcPort, incrementalCacheIpcValidationKey, dir, enabledDirectories, configOutDir, nextBuildSpan) { const exportApp = require("../export").default; const pagesWorker = createStaticWorker(config, incrementalCacheIpcPort, incrementalCacheIpcValidationKey); const appWorker = createStaticWorker(config, incrementalCacheIpcPort, incrementalCacheIpcValidationKey); await exportApp(dir, { buildExport: false, nextConfig: config, enabledDirectories, silent: true, threads: config.experimental.cpus, outdir: path.join(dir, configOutDir), // The worker already explicitly binds `this` to each of the // exposed methods. exportAppPageWorker: appWorker == null ? void 0 : appWorker.exportPage, exportPageWorker: pagesWorker == null ? void 0 : pagesWorker.exportPage, endWorker: async ()=>{ await pagesWorker.end(); await appWorker.end(); } }, nextBuildSpan); // ensure the worker is not left hanging pagesWorker.close(); appWorker.close(); } 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)); } const IS_TURBOPACK_BUILD = process.env.TURBOPACK && process.env.TURBOPACK_BUILD; export default async function build(dir, reactProductionProfiling = false, debugOutput = false, runLint = true, noMangling = false, appDirOnly = false, turboNextBuild = false, experimentalBuildMode) { const isCompileMode = experimentalBuildMode === "compile"; const isGenerateMode = experimentalBuildMode === "generate"; try { const nextBuildSpan = trace("next-build", undefined, { buildMode: experimentalBuildMode, isTurboBuild: String(turboNextBuild), version: "14.2.13" }); 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 }), turborepoAccessTraceResult)); 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); 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.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(); 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, expFeatureInfo } = await getStartServerInfo(dir, false); logStartInfo({ networkUrl: null, appUrl: null, envInfo, expFeatureInfo }); const ignoreESLint = Boolean(config.eslint.ignoreDuringBuilds); const shouldLint = !ignoreESLint && runLint; const typeCheckingOptions = { dir, appDir, pagesDir, runLint, shouldLint, ignoreESLint, telemetry, nextBuildSpan, config, cacheDir }; // 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 pagesPaths = !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 instrumentationHookEnabled = Boolean(config.experimental.instrumentationHook); const includes = [ middlewareDetectionRegExp, ...instrumentationHookEnabled ? [ instrumentationHookDetectionRegExp ] : [] ]; const rootPaths = (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 = nextBuildSpan.traceChild("create-pages-mapping").traceFn(()=>createPagesMapping({ isDev: false, pageExtensions: config.pageExtensions, pagesType: PAGE_TYPES.PAGES, pagePaths: pagesPaths, pagesDir })); NextBuildContext.mappedPages = mappedPages; let mappedAppPages; let denormalizedAppPages; if (appDir) { const appPaths = 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 = nextBuildSpan.traceChild("create-app-mapping").traceFn(()=>createPagesMapping({ pagePaths: appPaths, isDev: false, pagesType: PAGE_TYPES.APP, pageExtensions: config.pageExtensions, pagesDir: pagesDir })); // If the metadata route doesn't contain generating dynamic exports, // we can replace the dynamic catch-all route and use the static route instead. for (const [pageKey, pagePath] of Object.entries(mappedAppPages)){ if (pageKey.includes("[[...__metadata_id__]]")) { const pageFilePath = getPageFilePath({ absolutePagePath: pagePath, pagesDir, appDir, rootDir }); const isDynamic = await isDynamicMetadataRoute(pageFilePath); if (!isDynamic) { delete mappedAppPages[pageKey]; mappedAppPages[pageKey.replace("[[...__metadata_id__]]/", "")] = pagePath; } if (pageKey.includes("sitemap.xml/[[...__metadata_id__]]") && isDynamic) { delete mappedAppPages[pageKey]; mappedAppPages[pageKey.replace("sitemap.xml/[[...__metadata_id__]]", "sitemap/[__metadata_id__]")] = pagePath; } } } NextBuildContext.mappedAppPages = mappedAppPages; } const mappedRootPaths = createPagesMapping({ isDev: false, pageExtensions: config.pageExtensions, pagePaths: rootPaths, pagesType: PAGE_TYPES.ROOT, pagesDir: pagesDir }); 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 (!IS_TURBOPACK_BUILD) { 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 new Error(PUBLIC_DIR_MIDDLEWARE_CONFLICT); } } 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 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")}`); } }); 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 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}, ${NEXT_ROUTER_PREFETCH_HEADER}`, prefetchHeader: NEXT_ROUTER_PREFETCH_HEADER, didPostponeHeader: NEXT_DID_POSTPONE_HEADER, contentTypeHeader: RSC_CONTENT_TYPE_HEADER, suffix: RSC_SUFFIX, prefetchSuffix: RSC_PREFETCH_SUFFIX }, skipMiddlewareUrlNormalize: config.skipMiddlewareUrlNormalize }; }); 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)) }; } if (config.experimental.clientRouterFilter) { const nonInternalRedirects = (config._originalRedirects || []).filter((r)=>!r.internal); const clientRouterFilters = createClientRouterFilter(appPaths, config.experimental.clientRouterFilterRedirects ? nonInternalRedirects : [], config.experimental.clientRouterFilterAllowedRate); NextBuildContext.clientRouterFilters = clientRouterFilters; } 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 new Error("> Build directory is not writeable. https://nextjs.org/docs/messages/build-dir-not-writeable"); } if (config.cleanDistDir && !isGenerateMode) { await recursiveDelete(distDir, /^cache/); } // 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"}'); // We need to write the manifest with rewrites before build await nextBuildSpan.traceChild("write-routes-manifest").traceAsyncFn(()=>writeManifest(routesManifestPath, routesManifest)); const outputFileTracingRoot = config.experimental.outputFileTracingRoot || dir; const pagesManifestPath = path.join(distDir, SERVER_DIRECTORY, PAGES_MANIFEST); const { cacheHandler } = config; const requiredServerFilesManifest = nextBuildSpan.traceChild("generate-required-server-files").traceFn(()=>{ const serverFilesManifest = { version: 1, config: { ...config, configFile: undefined, ...ciEnvironment.hasNextSupport ? { compress: false } : {}, cacheHandler: cacheHandler ? path.relative(distDir, cacheHandler) : config.cacheHandler, experimental: { ...config.experimental, trustHostHeader: ciEnvironment.hasNextSupport, // @ts-expect-error internal field TODO: fix this, should use a separate mechanism to pass the info. isExperimentalCompile: isCompileMode } }, appDir: dir, relativeAppDir: path.relative(outputFileTracingRoot, dir), files: [ ROUTES_MANIFEST, path.relative(distDir, pagesManifestPath), BUILD_MANIFEST, PRERENDER_MANIFEST, path.join(SERVER_DIRECTORY, MIDDLEWARE_MANIFEST), path.join(SERVER_DIRECTORY, MIDDLEWARE_BUILD_MANIFEST + ".js"), path.join(SERVER_DIRECTORY, MIDDLEWARE_REACT_LOADABLE_MANIFEST + ".js"), ...appDir ? [ ...config.experimental.sri ? [ path.join(SERVER_DIRECTORY, SUBRESOURCE_INTEGRITY_MANIFEST + ".js"), path.join(SERVER_DIRECTORY, SUBRESOURCE_INTEGRITY_MANIFEST + ".json") ] : [], path.join(SERVER_DIRECTORY, APP_PATHS_MANIFEST), path.join(APP_PATH_ROUTES_MANIFEST), APP_BUILD_MANIFEST, path.join(SERVER_DIRECTORY, SERVER_REFERENCE_MANIFEST + ".js"), path.join(SERVER_DIRECTORY, SERVER_REFERENCE_MANIFEST + ".json") ] : [], REACT_LOADABLE_MANIFEST, config.optimizeFonts ? path.join(SERVER_DIRECTORY, AUTOMATIC_FONT_OPTIMIZATION_MANIFEST) : null, BUILD_ID_FILE, path.join(SERVER_DIRECTORY, NEXT_FONT_MANIFEST + ".js"), path.join(SERVER_DIRECTORY, NEXT_FONT_MANIFEST + ".json"), ...hasInstrumentationHook ? [ path.join(SERVER_DIRECTORY, `${INSTRUMENTATION_HOOK_FILENAME}.js`), path.join(SERVER_DIRECTORY, `edge-${INSTRUMENTATION_HOOK_FILENAME}.js`) ] : [] ].filter(nonNullable).map((file)=>path.join(config.distDir, file)), ignore: [] }; return serverFilesManifest; }); async function turbopackBuild() { var _config_experimental; if (!IS_TURBOPACK_BUILD) { throw new Error("next build doesn't support turbopack yet"); } await validateTurboNextConfig({ dir, isDev: false }); const startTime = process.hrtime(); const bindings = await loadBindings(config == null ? void 0 : (_config_experimental = config.experimental) == null ? void 0 : _config_experimental.useWasmBinary); const dev = false; const project = await bindings.turbo.createProject({ projectPath: dir, rootPath: config.experimental.outputFileTracingRoot || dir, nextConfig: config, jsConfig: await getTurbopackJsConfig(dir, config), watch: false, dev, env: process.env, defineEnv: createDefineEnv({ isTurbopack: true, clientRouterFilters: NextBuildContext.clientRouterFilters, config, dev, distDir, fetchCacheKeyPrefix: config.experimental.fetchCacheKeyPrefix, hasRewrites, // TODO: Implement middlewareMatchers: undefined }), buildId: NextBuildContext.buildId, encryptionKey: NextBuildContext.encryptionKey, previewProps: NextBuildContext.previewProps }); await fs.mkdir(path.join(distDir, "server"), { recursive: true }); await fs.mkdir(path.join(distDir, "static", buildId), { recursive: true }); await fs.writeFile(path.join(distDir, "package.json"), JSON.stringify({ type: "commonjs" }, null, 2)); // eslint-disable-next-line @typescript-eslint/no-unused-vars const entrypointsSubscription = project.entrypointsSubscribe(); const currentEntrypoints = { global: { app: undefined, document: undefined, error: undefined, middleware: undefined, instrumentation: undefined }, app: new Map(), page: new Map() }; const currentEntryIssues = new Map(); const manifestLoader = new TurbopackManifestLoader({ buildId, distDir, encryptionKey }); // TODO: implement this const emptyRewritesObjToBeImplemented = { beforeFiles: [], afterFiles: [], fallback: [] }; const entrypointsResult = await entrypointsSubscription.next(); if (entrypointsResult.done) { throw new Error("Turbopack did not return any entrypoints"); } entrypointsSubscription.return == null ? void 0 : entrypointsSubscription.return.call(entrypointsSubscription).catch(()=>{}); const entrypoints = entrypointsResult.value; const topLevelErrors = []; for (const issue of entrypoints.issues){ topLevelErrors.push({ message: formatIssue(issue) }); } if (topLevelErrors.length > 0) { throw new Error(`Turbopack build failed with ${topLevelErrors.length} issues:\n${topLevelErrors.map((e)=>e.message).join("\n")}`); } await handleEntrypoints({ entrypoints, currentEntrypoints, currentEntryIssues, manifestLoader, nextConfig: config, rewrites: emptyRewritesObjToBeImplemented, logErrors: false }); const progress = createProgress(currentEntrypoints.page.size + currentEntrypoints.app.size + 1, "Building"); const promises = []; const sema = new Sema(10); const enqueue = (fn)=>{ promises.push((async ()=>{ await sema.acquire(); try { await fn(); } finally{ sema.release(); progress(); } })()); }; for (const [page, route] of currentEntrypoints.page){ enqueue(()=>handleRouteType({ dev, page, pathname: page, route, currentEntryIssues, entrypoints: currentEntrypoints, manifestLoader, rewrites: emptyRewritesObjToBeImplemented, logErrors: false })); } for (const [page, route] of currentEntrypoints.app){ enqueue(()=>handleRouteType({ page, dev: false, pathname: normalizeAppPath(page), route, currentEntryIssues, entrypoints: currentEntrypoints, manifestLoader, rewrites: emptyRewritesObjToBeImplemented, logErrors: false })); } enqueue(()=>handlePagesErrorRoute({ currentEntryIssues, entrypoints: currentEntrypoints, manifestLoader, rewrites: emptyRewritesObjToBeImplemented, logErrors: false })); await Promise.all(promises); await manifestLoader.writeManifests({ rewrites: emptyRewritesObjToBeImplemented, pageEntrypoints: currentEntrypoints.page }); const errors = []; const warnings = []; for (const [page, entryIssues] of currentEntryIssues){ for (const issue of entryIssues.values()){ if (issue.severity !== "warning") { errors.push({ page, message: formatIssue(issue) }); } else { if (isRelevantWarning(issue)) { warnings.push({ page, message: formatIssue(issue) }); } } } } if (warnings.length > 0) { Log.warn(`Turbopack build collected ${warnings.length} warnings:\n${warnings.map((e)=>{ return "Page: " + e.page + "\n" + e.message; }).join("\n")}`); } if (errors.length > 0) { throw new Error(`Turbopack build failed with ${errors.length} errors:\n${errors.map((e)=>{ return "Page: " + e.page + "\n" + e.message; }).join("\n")}`); } return { duration: process.hrtime(startTime)[0], buildTraceContext: undefined }; } 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 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'); } Log.info("Creating an optimized production build ..."); traceMemoryUsage("Starting build", nextBuildSpan); if (!isGenerateMode) { if (runServerAndEdgeInParallel || collectServerBuildTracesInParallel) { let durationInSeconds = 0; 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. pageInfos: serializePageInfos(new Map()), staticPages: [], hasSsrAmpPages: false, buildTraceContext, outputFileTracingRoot }).catch((err)=>{ console.error(err); process.exit(1); }); } }); if (!runServerAndEdgeInParallel) { await serverBuildPromise; } const edgeBuildPromise = webpackBuild(useBuildWorker, [ "edge-server" ]).then((res)=>{ durationInSeconds += res.duration; traceMemoryUsage("Finished edge-server compilation", nextBuildSpan); }); if (runServerAndEdgeInParallel) { await serverBuildPromise; } await edgeBuildPromise; await webpackBuild(useBuildWorker, [ "client" ]).then((res)=>{ durationInSeconds += res.duration; traceMemoryUsage("Finished client compilation", nextBuildSpan); }); Log.event("Compiled successfully"); telemetry.record(eventBuildCompleted(pagesPaths, { durationInSeconds, totalAppPagesCount })); } else { const { duration: compilerDuration, ...rest } = turboNextBuild ? await turbopackBuild() : 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 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 additionalSsgPaths = new Map(); const additionalSsgPathsEncoded = new Map(); const appStaticPaths = new Map(); const appPrefetchPaths = new Map(); const appStaticPathsEncoded = new Map(); const appNormalizedPaths = new Map(); const appDynamicParamPaths = new Set(); const appDefaultConfigs = new Map(); const pageInfos = new Map(); const 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; let incrementalCacheIpcPort; let incrementalCacheIpcValidationKey; if (config.experimental.staticWorkerRequestDeduping) { let CacheHandler; if (cacheHandler) { CacheHandler = interopDefault(await import(formatDynamicImportPath(dir, cacheHandler)).then((mod)=>mod.default || mod)); } const cacheInitialization = await initializeIncrementalCache({ fs: nodeFs, dev: false, pagesDir: true, appDir: true, fetchCache: true, flushToDisk: ciEnvironment.hasNextSupport ? false : config.experimental.isrFlushToDisk, serverDistDir: path.join(distDir, "server"), fetchCacheKeyPrefix: config.experimental.fetchCacheKeyPrefix, maxMemoryCacheSize: config.cacheMaxMemorySize, getPrerenderManifest: ()=>({ version: -1, routes: {}, dynamicRoutes: {}, notFoundRoutes: [], preview: null }), requestHeaders: {}, CurCacheHandler: CacheHandler, minimalMode: ciEnvironment.hasNextSupport, allowedRevalidateHeaderKeys: config.experimental.allowedRevalidateHeaderKeys, experimental: { ppr: config.experimental.ppr === true } }); incrementalCacheIpcPort = cacheInitialization.ipcPort; incrementalCacheIpcValidationKey = cacheInitialization.ipcValidationKey; } const pagesStaticWorkers = createStaticWorker(config, incrementalCacheIpcPort, incrementalCacheIpcValidationKey); const appStaticWorkers = appDir ? createStaticWorker(config, incrementalCacheIpcPort, incrementalCacheIpcValidationKey) : undefined; 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 ()=>{ if (isCompileMode) { return { customAppGetInitialProps: false, namedExports: [], isNextImageImported: true, hasSsrAmpPages: !!pagesDir, hasNonStaticErrorPage: true }; } const { configFileName, publicRuntimeConfig, serverRuntimeConfig } = config; const runtimeEnvConfig = { publicRuntimeConfig, serverRuntimeConfig }; const nonStaticErrorPageSpan = staticCheckSpan.traceChild("check-static-error-page"); const errorPageHasCustomGetInitialProps = nonStaticErrorPageSpan.traceAsyncFn(async ()=>hasCustomErrorPage && await pagesStaticWorkers.hasCustomGetInitialProps({ page: "/_error", distDir, runtimeEnvConfig, checkingApp: false })); const errorPageStaticResult = nonStaticErrorPageSpan.traceAsyncFn(async ()=>{ var _config_i18n, _config_i18n1; return hasCustomErrorPage && pagesStaticWorkers.isPageStatic({ dir, page: "/_error", distDir, configFileName, runtimeEnvConfig, 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, ppr: config.experimental.ppr === true }); }); const appPageToCheck = "/_app"; const customAppGetInitialPropsPromise = pagesStaticWorkers.hasCustomGetInitialProps({ page: appPageToCheck, distDir, runtimeEnvConfig, checkingApp: true }); const namedExportsPromise = pagesStaticWorkers.getDefinedNamedExports({ page: appPageToCheck, distDir, runtimeEnvConfig }); // 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 = actionManifest ? new Set() : null; if (actionManifest && entriesWithAction) { for(const id in actionManifest.node){ for(const entry in actionManifest.node[id].workers){ entriesWithAction.add(entry); } } for(const id in actionManifest.edge){ for(const entry in actionManifest.edge[id].workers){ entriesWithAction.add(entry); } } } for (const key of Object.keys(middlewareManifest == null ? void 0 : middlewareManifest.functions)){ if (key.startsWith("/api")) { edgeRuntimePagesCount++; } } await Promise.all(Object.entries(pageKeys).reduce((acc, [key, files])=>{ if (!files) { return acc; } const pageType = key; for (const page of files){ acc.push({ pageType, page }); } return acc; }, []).map(({ pageType, page })=>{ const checkPageSpan = staticCheckSpan.traceChild("check-page", { page }); return checkPageSpan.traceAsyncFn(async ()=>{ const actualPage = normalizePagePath(page); const [size, totalSize] = await getJsPageSizeInKb(pageType, actualPage, distDir, buildManifest, appBuildManifest, config.experimental.gzipSize, computedManifestData); let isPPR = false; let isSSG = false; let isStatic = false; let isServerComponent = false; let isHybridAmp = false; let ssgPageRoutes = null; let pagePath = ""; if (pageType === "pages") { pagePath = pagesPaths.find((p)=>{ p = normalizePathSep(p); return p.startsWith(actualPage + ".") || p.startsWith(actualPage + "/index."); }) || ""; } let originalAppPath; if (pageType === "app" && mappedAppPages) { for (const [originalPath, normalizedPath] of Object.entries(appPathRoutes)){ if (normalizedPath === page) { pagePath = mappedAppPages[originalPath].replace(/^private-next-app-dir/, ""); originalAppPath = originalPath; break; } } } const pageFilePath = isAppBuiltinNotFoundPage(pagePath) ? require.resolve("next/dist/client/components/not-found-error") : path.join((pageType === "pages" ? pagesDir : appDir) || "", pagePath); const staticInfo = pagePath ? await getPageStaticInfo({ pageFilePath, nextConfig: config, // TODO: fix type mismatch pageType: pageType === "app" ? PAGE_TYPES.APP : PAGE_TYPES.PAGES }) : undefined; if (staticInfo == null ? void 0 : staticInfo.extraConfig) { functionsConfigManifest.functions[page] = staticInfo.extraConfig; } const pageRuntime = middlewareManifest.functions[originalAppPath || page] ? "edge" : staticInfo == null ? void 0 : staticInfo.runtime; if (!isCompileMode) { isServerComponent = pageType === "app" && (staticInfo == null ? void 0 : staticInfo.rsc) !== RSC_MODULE_TYPES.client; if (pageType === "app" || !isReservedPage(page)) { try { let edgeInfo; if (isEdgeRuntime(pageRuntime)) { if (pageType === "app") { edgeRuntimeAppCount++; } else { edgeRuntimePagesCount++; } const manifestKey = pageType === "pages" ? page : originalAppPath || ""; edgeInfo = middlewareManifest.functions[manifestKey]; } let isPageStaticSpan = checkPageSpan.traceChild("is-page-static"); let workerResult = await isPageStaticSpan.traceAsyncFn(()=>{ var _config_i18n, _config_i18n1; return (pageType === "app" ? appStaticWorkers : pagesStaticWorkers).isPageStatic({ dir, page, originalAppPath, distDir, configFileName, runtimeEnvConfig, 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, parentId: isPageStaticSpan.getId(), pageRuntime, edgeInfo, pageType, cacheHandler: config.cacheHandler, isrFlushToDisk: ciEnvironment.hasNextSupport ? false : config.experimental.isrFlushToDisk, maxMemoryCacheSize: config.cacheMaxMemorySize, nextConfigOutput: config.output, ppr: config.experimental.ppr === true }); }); if (pageType === "app" && originalAppPath) { appNormalizedPaths.set(originalAppPath, page); // TODO-APP: handle prerendering with edge if (isEdgeRuntime(pageRuntime)) { isStatic = false; isSSG = false; Log.warnOnce(`Using edge runtime on a page currently disables static generation for that page`); } else { // If this route can be partially pre-rendered, then // mark it as such and mark that it can be // generated server-side. if (workerResult.isPPR) { isPPR = workerResult.isPPR; isSSG = true; isStatic = true; appStaticPaths.set(originalAppPath, []); appStaticPathsEncoded.set(originalAppPath, []); } if (workerResult.encodedPrerenderRoutes && workerResult.prerenderRoutes) { appStaticPaths.set(originalAppPath, workerResult.prerenderRoutes); appStaticPathsEncoded.set(originalAppPath, workerResult.encodedPrerenderRoutes); ssgPageRoutes = workerResult.prerenderRoutes; isSSG = true; } const appConfig = workerResult.appConfig || {}; const isInterceptionRoute = isInterceptionRouteAppPath(page); if (appConfig.revalidate !== 0) { var _workerResult_prerenderRoutes; const isDynamic = isDynamicRoute(page); const hasGenerateStaticParams = !!((_workerResult_prerenderRoutes = workerResult.prerenderRoutes) == null ? void 0 : _workerResult_prerenderRoutes.length); if (config.output === "export" && isDynamic && !hasGenerateStaticParams) { throw new Error(`Page "${page}" is missing "generateStaticParams()" so it cannot be used with "output: export" config.`); } // Mark the app as static if: // - It's not an interception route (these currently depend on request headers and cannot be computed at build) // - It has no dynamic param // - It doesn't have generateStaticParams but `dynamic` is set to // `error` or `force-static` if (!isInterceptionRoute) { if (!isDynamic) { appStaticPaths.set(originalAppPath, [ page ]); appStaticPathsEncoded.set(originalAppPath, [ page ]); isStatic = true; } else if (isDynamic && !hasGenerateStaticParams && (appConfig.dynamic === "error" || appConfig.dynamic === "force-static")) { appStaticPaths.set(originalAppPath, []); appStaticPathsEncoded.set(originalAppPath, []); isStatic = true; isPPR = false; } } } if (workerResult.prerenderFallback) { // whether or not to allow requests for paths not // returned from generateStaticParams appDynamicParamPaths.add(originalAppPath); } appDefaultConfigs.set(originalAppPath, appConfig); // Only generate the app prefetch rsc if the route is // an app page. if (!isStatic && !isAppRouteRoute(originalAppPath) && !isDynamicRoute(originalAppPath) && !isPPR && !isInterceptionRoute) { appPrefetchPaths.set(originalAppPath, page); } } } else { if (isEdgeRuntime(pageRuntime)) { if (workerResult.hasStaticProps) { console.warn(`"getStaticProps" is not yet supported fully with "experimental-edge", detected on ${page}`); } // TODO: add handling for statically rendering edge // pages and allow edge with Prerender outputs workerResult.isStatic = false; workerResult.hasStaticProps = false; } if (workerResult.isStatic === false && (workerResult.isHybridAmp || workerResult.isAmpOnly)) { hasSsrAmpPages = true; } if (workerResult.isHybridAmp) { isHybridAmp = true; hybridAmpPages.add(page); } if (workerResult.isNextImageImported) { isNextImageImported = true; } if (workerResult.hasStaticProps) { ssgPages.add(page); isSSG = true; if (workerResult.prerenderRoutes && workerResult.encodedPrerenderRoutes) { additionalSsgPaths.set(page, workerResult.prerenderRoutes); additionalSsgPathsEncoded.set(page, workerResult.encodedPrerenderRoutes); ssgPageRoutes = workerResult.prerenderRoutes; } if (workerResult.prerenderFallback === "blocking") { ssgBlockingFallbackPages.add(page); } else if (workerResult.prerenderFallback === true) { ssgStaticFallbackPages.add(page); } } else if (workerResult.hasServerProps) { serverPropsPages.add(page); } else if (workerResult.isStatic && !isServerComponent && await customAppGetInitialPropsPromise === false) { staticPages.add(page); isStatic = true; } else if (isServerComponent) { // This is a static server component page that doesn't have // gSP or gSSP. We still treat it as a SSG page. ssgPages.add(page); isSSG = true; } if (hasPages404 && page === "/404") { if (!workerResult.isStatic && !workerResult.hasStaticProps) { throw new Error(`\`pages/404\` ${STATIC_STATUS_PAGE_GET_INITIAL_PROPS_ERROR}`); } // we need to ensure the 404 lambda is present since we use // it when _app has getInitialProps if (await customAppGetInitialPropsPromise && !workerResult.hasStaticProps) { staticPages.delete(page); } } if (STATIC_STATUS_PAGES.includes(page) && !workerResult.isStatic && !workerResult.hasStaticProps) { throw new Error(`\`pages${page}\` ${STATIC_STATUS_PAGE_GET_INITIAL_PROPS_ERROR}`); } } } catch (err) { if (!isError(err) || err.message !== "INVALID_DEFAULT_EXPORT") throw err; invalidPages.add(page); } } if (pageType === "app") { if (isSSG || isStatic) { staticAppPagesCount++; } else { serverAppPagesCount++; } } } pageInfos.set(page, { size, totalSize, isStatic, isSSG, isPPR, isHybridAmp, ssgPageRoutes, initialRevalidateSeconds: false, runtime: pageRuntime, pageDuration: undefined, ssgPageDurations: undefined, hasEmptyPrelude: undefined }); }); })); const errorPageResult = await errorPageStaticResult; const nonStaticErrorPage = await errorPageHasCustomGetInitialProps || errorPageResult && errorPageResult.hasServerProps; const returnValue = { customAppGetInitialProps: await customAppGetInitialPropsPromise, namedExports: await namedExportsPromise, isNextImageImported, hasSsrAmpPages, hasNonStaticErrorPage: nonStaticErrorPage }; return returnValue; }); if (postCompileSpinner) postCompileSpinner.stopAndPersist(); traceMemoryUsage("Finished collecting page data", nextBuildSpan); if (customAppGetInitialProps) { console.warn(bold(yellow(`Warning: `)) + yellow(`You have opted-out of Automatic Static Optimization due to \`getInitialProps\` in \`pages/_app\`. This does not opt-out pages with \`getStaticProps\``)); console.warn("Read more: https://nextjs.org/docs/messages/opt-out-auto-static-optimization\n"); } if (!hasSsrAmpPages) { requiredServerFilesManifest.ignore.push(path.relative(dir, path.join(path.dirname(require.resolve("next/dist/compiled/@ampproject/toolbox-optimizer")), "**/*"))); } await writeFunctionsConfigManifest(distDir, functionsConfigManifest); if (!isGenerateMode && config.outputFileTracing && !buildTracesPromise) { buildTracesPromise = collectBuildTraces({ dir, config, distDir, pageInfos, staticPages: [ ...staticPages ], nextBuildSpan, hasSsrAmpPages, buildTraceContext, outputFileTracingRoot }).catch((err)=>{ console.error(err); process.exit(1); }); } if (serverPropsPages.size > 0 || ssgPages.size > 0) { // We update the routes manifest after the build with the // data routes since we can't determine these until after build routesManifest.dataRoutes = getSortedRoutes([ ...serverPropsPages, ...ssgPages ]).map((page)=>{ return buildDataRoute(page, buildId); }); await writeManifest(routesManifestPath, routesManifest); } // Since custom _app.js can wrap the 404 page we have to opt-out of static optimization if it has getInitialProps // Only export the static 404 when there is no /_error present const useStaticPages404 = !customAppGetInitialProps && (!hasNonStaticErrorPage || hasPages404); if (invalidPages.size > 0) { const err = new Error(`Build optimization failed: found page${invalidPages.size === 1 ? "" : "s"} without a React Component as default export in \n${[ ...invalidPages ].map((pg)=>`pages${pg}`).join("\n")}\n\nSee https://nextjs.org/docs/messages/page-without-valid-component for more info.\n`); err.code = "BUILD_OPTIMIZATION_FAILED"; throw err; } await writeBuildId(distDir, buildId); if (config.experimental.optimizeCss) { const globOrig = require("next/dist/compiled/glob"); const cssFilePaths = await new Promise((resolve, reject)=>{ globOrig("**/*.css", { cwd: path.join(distDir, "static") }, (err, files)=>{ if (err) { return reject(err); } resolve(files); }); }); requiredServerFilesManifest.files.push(...cssFilePaths.map((filePath)=>path.join(config.distDir, "static", filePath))); } const features = [ { featureName: "experimental/optimizeCss", invocationCount: config.experimental.optimizeCss ? 1 : 0 }, { featureName: "experimental/nextScriptWorkers", invocationCount: config.experimental.nextScriptWorkers ? 1 : 0 }, { featureName: "optimizeFonts", invocationCount: config.optimizeFonts ? 1 : 0 }, { featureName: "experimental/ppr", invocationCount: config.experimental.ppr ? 1 : 0 } ]; telemetry.record(features.map((feature)=>{ return { eventName: EVENT_BUILD_FEATURE_USAGE, payload: feature }; })); await writeRequiredServerFilesManifest(distDir, requiredServerFilesManifest); const middlewareManifest = await readManifest(path.join(distDir, SERVER_DIRECTORY, MIDDLEWARE_MANIFEST)); const finalPrerenderRoutes = {}; const finalDynamicRoutes = {}; const tbdPrerenderRoutes = []; let ssgNotFoundPaths = []; const { i18n } = config; const usedStaticStatusPages = STATIC_STATUS_PAGES.filter((page)=>mappedPages[page] && mappedPages[page].startsWith("private-next-pages")); usedStaticStatusPages.forEach((page)=>{ if (!ssgPages.has(page) && !customAppGetInitialProps) { staticPages.add(page); } }); const hasPages500 = usedStaticStatusPages.includes("/500"); const useDefaultStatic500 = !hasPages500 && !hasNonStaticErrorPage && !customAppGetInitialProps; const combinedPages = [ ...staticPages, ...ssgPages ]; const isApp404Static = appStaticPaths.has(UNDERSCORE_NOT_FOUND_ROUTE_ENTRY); const hasStaticApp404 = hasApp404 && isApp404Static; // we need to trigger automatic exporting when we have // - static 404/500 // - getStaticProps paths // - experimental app is enabled if (!isCompileMode && (combinedPages.length > 0 || useStaticPages404 || useDefaultStatic500 || appDir)) { const staticGenerationSpan = nextBuildSpan.traceChild("static-generation"); await staticGenerationSpan.traceAsyncFn(async ()=>{ detectConflictingPaths([ ...combinedPages, ...pageKeys.pages.filter((page)=>!combinedPages.includes(page)) ], ssgPages, additionalSsgPaths); const exportApp = require("../export").default; const exportConfig = { ...config, // Default map will be the collection of automatic statically exported // pages and incremental pages. // n.b. we cannot handle this above in combinedPages because the dynamic // page must be in the `pages` array, but not in the mapping. exportPathMap: (defaultMap)=>{ // Dynamically routed pages should be prerendered to be used as // a client-side skeleton (fallback) while data is being fetched. // This ensures the end-user never sees a 500 or slow response from the // server. // // Note: prerendering disables automatic static optimization. ssgPages.forEach((page)=>{ if (isDynamicRoute(page)) { tbdPrerenderRoutes.push(page); if (ssgStaticFallbackPages.has(page)) { // Override the rendering for the dynamic page to be treated as a // fallback render. if (i18n) { defaultMap[`/${i18n.defaultLocale}${page}`] = { page, query: { __nextFallback: "true" } }; } else { defaultMap[page] = { page, query: { __nextFallback: "true" } }; } } else { // Remove dynamically routed pages from the default path map when // fallback behavior is disabled. delete defaultMap[page]; } } }); // Append the "well-known" routes we should prerender for, e.g. blog // post slugs. additionalSsgPaths.forEach((routes, page)=>{ const encodedRoutes = additionalSsgPathsEncoded.get(page); routes.forEach((route, routeIdx)=>{ defaultMap[route] = { page, query: { __nextSsgPath: encodedRoutes == null ? void 0 : encodedRoutes[routeIdx] } }; }); }); if (useStaticPages404) { defaultMap["/404"] = { page: hasPages404 ? "/404" : "/_error" }; } if (useDefaultStatic500) { defaultMap["/500"] = { page: "/_error" }; } // TODO: output manifest specific to app paths and their // revalidate periods and dynamicParams settings appStaticPaths.forEach((routes, originalAppPath)=>{ const encodedRoutes = appStaticPathsEncoded.get(originalAppPath); const appConfig = appDefaultConfigs.get(originalAppPath) || {}; routes.forEach((route, routeIdx)=>{ defaultMap[route] = { page: originalAppPath, query: { __nextSsgPath: encodedRoutes == null ? void 0 : encodedRoutes[routeIdx] }, _isDynamicError: appConfig.dynamic === "error", _isAppDir: true }; }); }); // Ensure we don't generate explicit app prefetches while in PPR. if (config.experimental.ppr && appPrefetchPaths.size > 0) { throw new Error("Invariant: explicit app prefetches shouldn't generated with PPR"); } for (const [originalAppPath, page] of appPrefetchPaths){ defaultMap[page] = { page: originalAppPath, query: {}, _isAppDir: true, _isAppPrefetch: true }; } if (i18n) { for (const page of [ ...staticPages, ...ssgPages, ...useStaticPages404 ? [ "/404" ] : [], ...useDefaultStatic500 ? [ "/500" ] : [] ]){ const isSsg = ssgPages.has(page); const isDynamic = isDynamicRoute(page); const isFallback = isSsg && ssgStaticFallbackPages.has(page); for (const locale of i18n.locales){ var _defaultMap_page; // skip fallback generation for SSG pages without fallback mode if (isSsg && isDynamic && !isFallback) continue; const outputPath = `/${locale}${page === "/" ? "" : page}`; defaultMap[outputPath] = { page: ((_defaultMap_page = defaultMap[page]) == null ? void 0 : _defaultMap_page.page) || page, query: { __nextLocale: locale, __nextFallback: isFallback ? "true" : undefined } }; } if (isSsg) { // remove non-locale prefixed variant from defaultMap delete defaultMap[page]; } } } return defaultMap; } }; const exportOptions = { nextConfig: exportConfig, enabledDirectories, silent: false, buildExport: true, debugOutput, threads: config.experimental.cpus, pages: combinedPages, outdir: path.join(distDir, "export"), statusMessage: "Generating static pages", // The worker already explicitly binds `this` to each of the // exposed methods. exportAppPageWorker: appStaticWorkers == null ? void 0 : appStaticWorkers.exportPage, exportPageWorker: pagesStaticWorkers == null ? void 0 : pagesStaticWorkers.exportPage, endWorker: async ()=>{ await pagesStaticWorkers.end(); await (appStaticWorkers == null ? void 0 : appStaticWorkers.end()); } }; const exportResult = await exportApp(dir, exportOptions, nextBuildSpan); // If there was no result, there's nothing more to do. if (!exportResult) return; writeTurborepoAccessTraceResult({ distDir: config.distDir, traces: [ turborepoAccessTraceResult, ...exportResult.turborepoAccessTraceResults.values() ] }); ssgNotFoundPaths = Array.from(exportResult.ssgNotFoundPaths); // remove server bundles that were exported for (const page of staticPages){ const serverBundle = getPagePath(page, distDir, undefined, false); await fs.unlink(serverBundle); } for (const [originalAppPath, routes] of appStaticPaths){ var _exportResult_byPath_get, _pageInfos_get; const page = appNormalizedPaths.get(originalAppPath) || ""; const appConfig = appDefaultConfigs.get(originalAppPath) || {}; let hasDynamicData = appConfig.revalidate === 0 || ((_exportResult_byPath_get = exportResult.byPath.get(page)) == null ? void 0 : _exportResult_byPath_get.revalidate) === 0; if (hasDynamicData && ((_pageInfos_get = pageInfos.get(page)) == null ? void 0 : _pageInfos_get.isStatic)) { // if the page was marked as being static, but it contains dynamic data // (ie, in the case of a static generation bailout), then it should be marked dynamic pageInfos.set(page, { ...pageInfos.get(page), isStatic: false, isSSG: false }); } const isRouteHandler = isAppRouteRoute(originalAppPath); // When this is an app page and PPR is enabled, the route supports // partial pre-rendering. const experimentalPPR = !isRouteHandler && config.experimental.ppr === true ? true : undefined; // this flag is used to selectively bypass the static cache and invoke the lambda directly // to enable server actions on static routes const bypassFor = [ { type: "header", key: ACTION }, { type: "header", key: "content-type", value: "multipart/form-data;.*" } ]; // Always sort the routes to get consistent output in manifests getSortedRoutes(routes).forEach((route)=>{ if (isDynamicRoute(page) && route === page) return; if (route === UNDERSCORE_NOT_FOUND_ROUTE) return; const { revalidate = appConfig.revalidate ?? false, metadata = {}, hasEmptyPrelude, hasPostponed } = exportResult.byPath.get(route) ?? {}; pageInfos.set(route, { ...pageInfos.get(route), hasPostponed, hasEmptyPrelude }); // update the page (eg /blog/[slug]) to also have the postpone metadata pageInfos.set(page, { ...pageInfos.get(page), hasPostponed, hasEmptyPrelude }); if (revalidate !== 0) { const normalizedRoute = normalizePagePath(route); let dataRoute; if (isRouteHandler) { dataRoute = null; } else { dataRoute = path.posix.join(`${normalizedRoute}${RSC_SUFFIX}`); } let prefetchDataRoute; if (experimentalPPR) { prefetchDataRoute = path.posix.join(`${normalizedRoute}${RSC_PREFETCH_SUFFIX}`); } const routeMeta = {}; if (metadata.status !== 200) { routeMeta.initialStatus = metadata.status; } const exportHeaders = metadata.headers; const headerKeys = Object.keys(exportHeaders || {}); if (exportHeaders && headerKeys.length) { routeMeta.initialHeaders = {}; // normalize header values as initialHeaders // must be Record for (const key of headerKeys){ // set-cookie is already handled - the middleware cookie setting case // isn't needed for the prerender manifest since it can't read cookies if (key === "x-middleware-set-cookie") continue; let value = exportHeaders[key]; if (Array.isArray(value)) { if (key === "set-cookie") { value = value.join(","); } else { value = value[value.length - 1]; } } if (typeof value === "string") { routeMeta.initialHeaders[key] = value; } } } finalPrerenderRoutes[route] = { ...routeMeta, experimentalPPR, experimentalBypassFor: bypassFor, initialRevalidateSeconds: revalidate, srcRoute: page, dataRoute, prefetchDataRoute }; } else { hasDynamicData = true; // we might have determined during prerendering that this page // used dynamic data pageInfos.set(route, { ...pageInfos.get(route), isSSG: false, isStatic: false }); } }); if (!hasDynamicData && isDynamicRoute(originalAppPath)) { const normalizedRoute = normalizePagePath(page); const dataRoute = path.posix.join(`${normalizedRoute}${RSC_SUFFIX}`); let prefetchDataRoute; if (experimentalPPR) { prefetchDataRoute = path.posix.join(`${normalizedRoute}${RSC_PREFETCH_SUFFIX}`); } pageInfos.set(page, { ...pageInfos.get(page), isDynamicAppRoute: true, // if PPR is turned on and the route contains a dynamic segment, // we assume it'll be partially prerendered hasPostponed: experimentalPPR }); // TODO: create a separate manifest to allow enforcing // dynamicParams for non-static paths? finalDynamicRoutes[page] = { experimentalPPR, experimentalBypassFor: bypassFor, routeRegex: normalizeRouteRegex(getNamedRouteRegex(page, false).re.source), dataRoute, // if dynamicParams are enabled treat as fallback: // 'blocking' if not it's fallback: false fallback: appDynamicParamPaths.has(originalAppPath) ? null : false, dataRouteRegex: isRouteHandler ? null : normalizeRouteRegex(getNamedRouteRegex(dataRoute.replace(/\.rsc$/, ""), false).re.source.replace(/\(\?:\\\/\)\?\$$/, "\\.rsc$")), prefetchDataRoute, prefetchDataRouteRegex: isRouteHandler || !prefetchDataRoute ? undefined : normalizeRouteRegex(getNamedRouteRegex(prefetchDataRoute.replace(/\.prefetch\.rsc$/, ""), false).re.source.replace(/\(\?:\\\/\)\?\$$/, "\\.prefetch\\.rsc$")) }; } } const moveExportedPage = async (originPage, page, file, isSsg, ext, additionalSsgFile = false)=>{ return staticGenerationSpan.traceChild("move-exported-page").traceAsyncFn(async ()=>{ file = `${file}.${ext}`; const orig = path.join(exportOptions.outdir, file); const pagePath = getPagePath(originPage, distDir, undefined, false); const relativeDest = path.relative(path.join(distDir, SERVER_DIRECTORY), path.join(path.join(pagePath, // strip leading / and then recurse number of nested dirs // to place from base folder originPage.slice(1).split("/").map(()=>"..").join("/")), file)).replace(/\\/g, "/"); if (!isSsg && !// don't add static status page to manifest if it's // the default generated version e.g. no pages/500 (STATIC_STATUS_PAGES.includes(page) && !usedStaticStatusPages.includes(page))) { pagesManifest[page] = relativeDest; } const dest = path.join(distDir, SERVER_DIRECTORY, relativeDest); const isNotFound = ssgNotFoundPaths.includes(page); // for SSG files with i18n the non-prerendered variants are // output with the locale prefixed so don't attempt moving // without the prefix if ((!i18n || additionalSsgFile) && !isNotFound) { await fs.mkdir(path.dirname(dest), { recursive: true }); await fs.rename(orig, dest); } else if (i18n && !isSsg) { // this will be updated with the locale prefixed variant // since all files are output with the locale prefix delete pagesManifest[page]; } if (i18n) { if (additionalSsgFile) return; const localeExt = page === "/" ? path.extname(file) : ""; const relativeDestNoPages = relativeDest.slice("pages/".length); for (const locale of i18n.locales){ const curPath = `/${locale}${page === "/" ? "" : page}`; if (isSsg && ssgNotFoundPaths.includes(curPath)) { continue; } const updatedRelativeDest = path.join("pages", locale + localeExt, // if it's the top-most index page we want it to be locale.EXT // instead of locale/index.html page === "/" ? "" : relativeDestNoPages).replace(/\\/g, "/"); const updatedOrig = path.join(exportOptions.outdir, locale + localeExt, page === "/" ? "" : file); const updatedDest = path.join(distDir, SERVER_DIRECTORY, updatedRelativeDest); if (!isSsg) { pagesManifest[curPath] = updatedRelativeDest; } await fs.mkdir(path.dirname(updatedDest), { recursive: true }); await fs.rename(updatedOrig, updatedDest); } } }); }; async function moveExportedAppNotFoundTo404() { return staticGenerationSpan.traceChild("move-exported-app-not-found-").traceAsyncFn(async ()=>{ const orig = path.join(distDir, "server", "app", "_not-found.html"); const updatedRelativeDest = path.join("pages", "404.html").replace(/\\/g, "/"); if (existsSync(orig)) { await fs.copyFile(orig, path.join(distDir, "server", updatedRelativeDest)); pagesManifest["/404"] = updatedRelativeDest; } }); } // If there's /not-found inside app, we prefer it over the pages 404 if (hasStaticApp404) { await moveExportedAppNotFoundTo404(); } else { // Only move /404 to /404 when there is no custom 404 as in that case we don't know about the 404 page if (!hasPages404 && !hasApp404 && useStaticPages404) { await moveExportedPage("/_error", "/404", "/404", false, "html"); } } if (useDefaultStatic500) { await moveExportedPage("/_error", "/500", "/500", false, "html"); } for (const page of combinedPages){ const isSsg = ssgPages.has(page); const isStaticSsgFallback = ssgStaticFallbackPages.has(page); const isDynamic = isDynamicRoute(page); const hasAmp = hybridAmpPages.has(page); const file = normalizePagePath(page); const pageInfo = pageInfos.get(page); const durationInfo = exportResult.byPage.get(page); if (pageInfo && durationInfo) { // Set Build Duration if (pageInfo.ssgPageRoutes) { pageInfo.ssgPageDurations = pageInfo.ssgPageRoutes.map((pagePath)=>{ const duration = durationInfo.durationsByPath.get(pagePath); if (typeof duration === "undefined") { throw new Error("Invariant: page wasn't built"); } return duration; }); } pageInfo.pageDuration = durationInfo.durationsByPath.get(page); } // The dynamic version of SSG pages are only prerendered if the // fallback is enabled. Below, we handle the specific prerenders // of these. const hasHtmlOutput = !(isSsg && isDynamic && !isStaticSsgFallback); if (hasHtmlOutput) { await moveExportedPage(page, page, file, isSsg, "html"); } if (hasAmp && (!isSsg || isSsg && !isDynamic)) { const ampPage = `${file}.amp`; await moveExportedPage(page, ampPage, ampPage, isSsg, "html"); if (isSsg) { await moveExportedPage(page, ampPage, ampPage, isSsg, "json"); } } if (isSsg) { // For a non-dynamic SSG page, we must copy its data file // from export, we already moved the HTML file above if (!isDynamic) { await moveExportedPage(page, page, file, isSsg, "json"); if (i18n) { // TODO: do we want to show all locale variants in build output for (const locale of i18n.locales){ var _exportResult_byPath_get1; const localePage = `/${locale}${page === "/" ? "" : page}`; finalPrerenderRoutes[localePage] = { initialRevalidateSeconds: ((_exportResult_byPath_get1 = exportResult.byPath.get(localePage)) == null ? void 0 : _exportResult_byPath_get1.revalidate) ?? false, experimentalPPR: undefined, srcRoute: null, dataRoute: path.posix.join("/_next/data", buildId, `${file}.json`), prefetchDataRoute: undefined }; } } else { var _exportResult_byPath_get2; finalPrerenderRoutes[page] = { initialRevalidateSeconds: ((_exportResult_byPath_get2 = exportResult.byPath.get(page)) == null ? void 0 : _exportResult_byPath_get2.revalidate) ?? false, experimentalPPR: undefined, srcRoute: null, dataRoute: path.posix.join("/_next/data", buildId, `${file}.json`), // Pages does not have a prefetch data route. prefetchDataRoute: undefined }; } // Set Page Revalidation Interval if (pageInfo) { var _exportResult_byPath_get3; pageInfo.initialRevalidateSeconds = ((_exportResult_byPath_get3 = exportResult.byPath.get(page)) == null ? void 0 : _exportResult_byPath_get3.revalidate) ?? false; } } else { // For a dynamic SSG page, we did not copy its data exports and only // copy the fallback HTML file (if present). // We must also copy specific versions of this page as defined by // `getStaticPaths` (additionalSsgPaths). const extraRoutes = additionalSsgPaths.get(page) || []; for (const route of extraRoutes){ var _exportResult_byPath_get4; const pageFile = normalizePagePath(route); await moveExportedPage(page, route, pageFile, isSsg, "html", true); await moveExportedPage(page, route, pageFile, isSsg, "json", true); if (hasAmp) { const ampPage = `${pageFile}.amp`; await moveExportedPage(page, ampPage, ampPage, isSsg, "html", true); await moveExportedPage(page, ampPage, ampPage, isSsg, "json", true); } const initialRevalidateSeconds = ((_exportResult_byPath_get4 = exportResult.byPath.get(route)) == null ? void 0 : _exportResult_byPath_get4.revalidate) ?? false; if (typeof initialRevalidateSeconds === "undefined") { throw new Error("Invariant: page wasn't built"); } finalPrerenderRoutes[route] = { initialRevalidateSeconds, experimentalPPR: undefined, srcRoute: page, dataRoute: path.posix.join("/_next/data", buildId, `${normalizePagePath(route)}.json`), // Pages does not have a prefetch data route. prefetchDataRoute: undefined }; // Set route Revalidation Interval if (pageInfo) { pageInfo.initialRevalidateSeconds = initialRevalidateSeconds; } } } } } // remove temporary export folder await fs.rm(exportOptions.outdir, { recursive: true, force: true }); await writeManifest(pagesManifestPath, pagesManifest); }); } const postBuildSpinner = createSpinner("Finalizing page optimization"); let buildTracesSpinner = createSpinner(`Collecting build traces`); // ensure the worker is not left hanging pagesStaticWorkers.close(); appStaticWorkers == null ? void 0 : appStaticWorkers.close(); const analysisEnd = process.hrtime(analysisBegin); telemetry.record(eventBuildOptimize(pagesPaths, { durationInSeconds: analysisEnd[0], staticPageCount: staticPages.size, staticPropsPageCount: ssgPages.size, serverPropsPageCount: serverPropsPages.size, ssrPageCount: pagesPaths.length - (staticPages.size + ssgPages.size + serverPropsPages.size), hasStatic404: useStaticPages404, hasReportWebVitals: (namedExports == null ? void 0 : namedExports.includes("reportWebVitals")) ?? false, rewritesCount: combinedRewrites.length, headersCount: headers.length, redirectsCount: redirects.length - 1, headersWithHasCount: headers.filter((r)=>!!r.has).length, rewritesWithHasCount: combinedRewrites.filter((r)=>!!r.has).length, redirectsWithHasCount: redirects.filter((r)=>!!r.has).length, middlewareCount: hasMiddlewareFile ? 1 : 0, totalAppPagesCount, staticAppPagesCount, serverAppPagesCount, edgeRuntimeAppCount, edgeRuntimePagesCount })); if (NextBuildContext.telemetryState) { const events = eventBuildFeatureUsage(NextBuildContext.telemetryState.usages); telemetry.record(events); telemetry.record(eventPackageUsedInGetServerSideProps(NextBuildContext.telemetryState.packagesUsedInServerSideProps)); } if (ssgPages.size > 0 || appDir) { var _config_i18n; tbdPrerenderRoutes.forEach((tbdRoute)=>{ const normalizedRoute = normalizePagePath(tbdRoute); const dataRoute = path.posix.join("/_next/data", buildId, `${normalizedRoute}.json`); finalDynamicRoutes[tbdRoute] = { routeRegex: normalizeRouteRegex(getNamedRouteRegex(tbdRoute, false).re.source), experimentalPPR: undefined, dataRoute, fallback: ssgBlockingFallbackPages.has(tbdRoute) ? null : ssgStaticFallbackPages.has(tbdRoute) ? `${normalizedRoute}.html` : false, dataRouteRegex: normalizeRouteRegex(getNamedRouteRegex(dataRoute.replace(/\.json$/, ""), false).re.source.replace(/\(\?:\\\/\)\?\$$/, "\\.json$")), // Pages does not have a prefetch data route. prefetchDataRoute: undefined, prefetchDataRouteRegex: undefined }; }); NextBuildContext.previewModeId = previewProps.previewModeId; NextBuildContext.fetchCacheKeyPrefix = config.experimental.fetchCacheKeyPrefix; NextBuildContext.allowedRevalidateHeaderKeys = config.experimental.allowedRevalidateHeaderKeys; const prerenderManifest = { version: 4, routes: finalPrerenderRoutes, dynamicRoutes: finalDynamicRoutes, notFoundRoutes: ssgNotFoundPaths, preview: previewProps }; await writePrerenderManifest(distDir, prerenderManifest); await writeClientSsgManifest(prerenderManifest, { distDir, buildId, locales: ((_config_i18n = config.i18n) == null ? void 0 : _config_i18n.locales) || [] }); } else { await writePrerenderManifest(distDir, { version: 4, routes: {}, dynamicRoutes: {}, preview: previewProps, notFoundRoutes: [] }); } await writeImagesManifest(distDir, config); await writeManifest(path.join(distDir, EXPORT_MARKER), { version: 1, hasExportPathMap: typeof config.exportPathMap === "function", exportTrailingSlash: config.trailingSlash === true, isNextImageImported: isNextImageImported === true }); await fs.unlink(path.join(distDir, EXPORT_DETAIL)).catch((err)=>{ if (err.code === "ENOENT") { return Promise.resolve(); } return Promise.reject(err); }); // TODO: remove in the next major version if (config.analyticsId) { Log.warn(`\`config.analyticsId\` is deprecated and will be removed in next major version. Read more: https://nextjs.org/docs/messages/deprecated-analyticsid`); } if (Boolean(config.experimental.nextScriptWorkers)) { await nextBuildSpan.traceChild("verify-partytown-setup").traceAsyncFn(async ()=>{ await verifyPartytownSetup(dir, path.join(distDir, CLIENT_STATIC_FILES_PATH)); }); } await buildTracesPromise; if (buildTracesSpinner) { buildTracesSpinner.stopAndPersist(); buildTracesSpinner = undefined; } if (config.output === "export") { await writeFullyStaticExport(config, incrementalCacheIpcPort, incrementalCacheIpcValidationKey, dir, enabledDirectories, configOutDir, nextBuildSpan); } if (config.output === "standalone") { await writeStandaloneDirectory(nextBuildSpan, distDir, pageKeys, denormalizedAppPages, outputFileTracingRoot, requiredServerFilesManifest, middlewareManifest, hasInstrumentationHook, staticPages, loadedEnvFiles, appDir); } if (postBuildSpinner) postBuildSpinner.stopAndPersist(); console.log(); if (debugOutput) { nextBuildSpan.traceChild("print-custom-routes").traceFn(()=>printCustomRoutes({ redirects, rewrites, headers })); } await nextBuildSpan.traceChild("print-tree-view").traceAsyncFn(()=>printTreeView(pageKeys, pageInfos, { distPath: distDir, buildId: buildId, pagesDir, useStaticPages404, pageExtensions: config.pageExtensions, appBuildManifest, buildManifest, middlewareManifest, gzipSize: config.experimental.gzipSize })); await nextBuildSpan.traceChild("telemetry-flush").traceAsyncFn(()=>telemetry.flush()); }); } finally{ // Ensure we wait for lockfile patching if present await lockfilePatchPromise.cur; // Ensure all traces are flushed before finishing the command await flushAllTraces(); teardownTraceSubscriber(); teardownHeapProfiler(); } } //# sourceMappingURL=index.js.map