From dd9b086355c5c507d8700e50529fa6452588e182 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Wed, 3 Jan 2024 11:16:25 +0100 Subject: [PATCH] Add writeStandaloneDirectory --- packages/next/src/build/index.ts | 371 ++++++++++++++++++------------- 1 file changed, 211 insertions(+), 160 deletions(-) diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index 4df9b7c55806f..aa8a595a16868 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -9,7 +9,7 @@ import type { Revalidate } from '../server/lib/revalidate' import '../lib/setup-exception-listeners' -import { loadEnvConfig } from '@next/env' +import { loadEnvConfig, type LoadedEnvFiles } from '@next/env' import { bold, yellow, green } from '../lib/picocolors' import crypto from 'crypto' import { isMatch, makeRe } from 'next/dist/compiled/micromatch' @@ -105,7 +105,7 @@ 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 { trace, flushAllTraces, setGlobal, type Span } from '../trace' import { detectConflictingPaths, computeFromManifest, @@ -423,6 +423,107 @@ async function writeClientSsgManifest( ) } +interface RequiredServerFilesManifest { + version: number + config: NextConfigComplete + appDir: string + relativeAppDir: string + files: string[] + ignore: string[] +} + +async function writeRequiredServerFilesManifest( + distDir: string, + requiredServerFiles: RequiredServerFilesManifest +) { + await writeManifest( + path.join(distDir, SERVER_FILES_MANIFEST), + requiredServerFiles + ) +} + +const STANDALONE_DIRECTORY = 'standalone' as const +async function writeStandaloneDirectory( + nextBuildSpan: Span, + distDir: string, + pageKeys: { pages: string[]; app: string[] | undefined }, + denormalizedAppPages: string[] | undefined, + outputFileTracingRoot: string, + requiredServerFiles: RequiredServerFilesManifest, + middlewareManifest: MiddlewareManifest, + hasInstrumentationHook: boolean, + staticPages: Set, + loadedEnvFiles: LoadedEnvFiles, + appDir: string | undefined +) { + 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 } + ) + } + } + }) +} + export default async function build( dir: string, reactProductionProfiling = false, @@ -1000,93 +1101,98 @@ export default async function build( const { incrementalCacheHandlerPath } = config.experimental - const requiredServerFiles = nextBuildSpan + const requiredServerFilesManifest = nextBuildSpan .traceChild('generate-required-server-files') - .traceFn(() => ({ - version: 1, - config: { - ...config, - configFile: undefined, - ...(ciEnvironment.hasNextSupport - ? { - compress: false, - } - : {}), - experimental: { - ...config.experimental, - trustHostHeader: ciEnvironment.hasNextSupport, - incrementalCacheHandlerPath: incrementalCacheHandlerPath - ? path.relative(distDir, incrementalCacheHandlerPath) - : undefined, - - isExperimentalCompile: isCompileMode, + .traceFn(() => { + const serverFilesManifest: RequiredServerFilesManifest = { + version: 1, + config: { + ...config, + configFile: undefined, + ...(ciEnvironment.hasNextSupport + ? { + compress: false, + } + : {}), + experimental: { + ...config.experimental, + trustHostHeader: ciEnvironment.hasNextSupport, + incrementalCacheHandlerPath: incrementalCacheHandlerPath + ? path.relative(distDir, incrementalCacheHandlerPath) + : undefined, + + // @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, - PRERENDER_MANIFEST.replace(/\.json$/, '.js'), - 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, FONT_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: [] as string[], - })) + appDir: dir, + relativeAppDir: path.relative(outputFileTracingRoot, dir), + files: [ + ROUTES_MANIFEST, + path.relative(distDir, pagesManifestPath), + BUILD_MANIFEST, + PRERENDER_MANIFEST, + PRERENDER_MANIFEST.replace(/\.json$/, '.js'), + 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, FONT_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: [] as string[], + } + + return serverFilesManifest + }) async function turbopackBuild() { const turboNextBuildStart = process.hrtime() @@ -1940,7 +2046,7 @@ export default async function build( } if (!hasSsrAmpPages) { - requiredServerFiles.ignore.push( + requiredServerFilesManifest.ignore.push( path.relative( dir, path.join( @@ -2038,7 +2144,7 @@ export default async function build( ) }) - requiredServerFiles.files.push( + requiredServerFilesManifest.files.push( ...cssFilePaths.map((filePath) => path.join(config.distDir, 'static', filePath) ) @@ -2068,9 +2174,9 @@ export default async function build( }) ) - await writeManifest( - path.join(distDir, SERVER_FILES_MANIFEST), - requiredServerFiles + await writeRequiredServerFilesManifest( + distDir, + requiredServerFilesManifest ) const middlewareManifest: MiddlewareManifest = await readManifest( @@ -3016,74 +3122,19 @@ export default async function build( await buildTracesPromise if (config.output === 'standalone') { - await nextBuildSpan - .traceChild('copy-traced-files') - .traceAsyncFn(async () => { - await copyTracedFiles( - dir, - distDir, - pageKeys.pages, - denormalizedAppPages, - outputFileTracingRoot, - requiredServerFiles.config, - middlewareManifest, - hasInstrumentationHook, - staticPages - ) - - for (const file of [ - ...requiredServerFiles.files, - path.join(config.distDir, SERVER_FILES_MANIFEST), - ...loadedEnvFiles.reduce((acc, envFile) => { - if (['.env', '.env.production'].includes(envFile.path)) { - acc.push(envFile.path) - } - return acc - }, []), - ]) { - const filePath = path.join(dir, file) - const outputPath = path.join( - distDir, - 'standalone', - 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', - 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', - path.relative(outputFileTracingRoot, distDir), - SERVER_DIRECTORY, - 'app' - ), - { overwrite: true } - ) - } - } - }) + await writeStandaloneDirectory( + nextBuildSpan, + distDir, + pageKeys, + denormalizedAppPages, + outputFileTracingRoot, + requiredServerFilesManifest, + middlewareManifest, + hasInstrumentationHook, + staticPages, + loadedEnvFiles, + appDir + ) } if (buildTracesSpinner) {