diff --git a/packages/@apphosting/adapter-nextjs/src/bin/build.spec.ts b/packages/@apphosting/adapter-nextjs/src/bin/build.spec.ts index eb81b226..8fb1177f 100644 --- a/packages/@apphosting/adapter-nextjs/src/bin/build.spec.ts +++ b/packages/@apphosting/adapter-nextjs/src/bin/build.spec.ts @@ -32,6 +32,10 @@ describe("build commands", () => { it("expects all output bundle files to be generated", async () => { const { generateBuildOutput, validateOutputDirectory } = await importUtils; const files = { + // .next/standalone/.next/ must be created beforehand otherwise + // generateBuildOutput will attempt to copy + // .next/ into .next/standalone/.next + ".next/standalone/.next/package.json": "", ".next/standalone/server.js": "", ".next/static/staticfile": "", ".next/routes-manifest.json": `{ @@ -53,7 +57,13 @@ describe("build commands", () => { const expectedFiles = { ".next/standalone/.next/static/staticfile": "", + ".next/static/staticfile": "", ".next/standalone/server.js": "", + ".next/routes-manifest.json": `{ + "headers":[], + "rewrites":[], + "redirects":[] + }`, ".apphosting/bundle.yaml": `version: v1 runConfig: runCommand: node .next/standalone/server.js @@ -71,7 +81,7 @@ outputFiles: validateTestFiles(tmpDir, expectedFiles); }); - it("moves files into correct location in a monorepo setup", async () => { + it("copies files into correct location in a monorepo setup", async () => { const { generateBuildOutput } = await importUtils; const files = { ".next/standalone/apps/next-app/standalonefile": "", @@ -113,6 +123,7 @@ outputFiles: const expectedFiles = { ".next/standalone/apps/next-app/.next/static/staticfile": "", ".next/standalone/apps/next-app/standalonefile": "", + ".next/static/staticfile": "", }; const expectedPartialYaml = { version: "v1", @@ -125,6 +136,10 @@ outputFiles: it("test failed validateOutputDirectory", async () => { const { generateBuildOutput, validateOutputDirectory } = await importUtils; const files = { + // .next/standalone/.next/ must be created beforehand otherwise + // generateBuildOutput will attempt to copy + // .next/ into .next/standalone/.next + ".next/standalone/.next/package.json": "", ".next/standalone/notserver.js": "", ".next/static/staticfile": "", ".next/routes-manifest.json": `{ @@ -152,6 +167,10 @@ outputFiles: it("expects directories and other files to be copied over", async () => { const { generateBuildOutput, validateOutputDirectory } = await importUtils; const files = { + // .next/standalone/.next/ must be created beforehand otherwise + // generateBuildOutput will attempt to copy + // .next/ into .next/standalone/.next + ".next/standalone/.next/package.json": "", ".next/standalone/server.js": "", ".next/static/staticfile": "", "public/publicfile": "", @@ -178,9 +197,15 @@ outputFiles: const expectedFiles = { ".next/standalone/.next/static/staticfile": "", + ".next/static/staticfile": "", ".next/standalone/server.js": "", ".next/standalone/public/publicfile": "", ".next/standalone/extrafile": "", + ".next/routes-manifest.json": `{ + "headers":[], + "rewrites":[], + "redirects":[] + }`, }; validateTestFiles(tmpDir, expectedFiles); }); diff --git a/packages/@apphosting/adapter-nextjs/src/utils.ts b/packages/@apphosting/adapter-nextjs/src/utils.ts index aaf5ead8..ed6cbc0a 100644 --- a/packages/@apphosting/adapter-nextjs/src/utils.ts +++ b/packages/@apphosting/adapter-nextjs/src/utils.ts @@ -15,7 +15,7 @@ import { NextConfigComplete } from "next/dist/server/config-shared.js"; import { OutputBundleConfig } from "@apphosting/common"; // fs-extra is CJS, readJson can't be imported using shorthand -export const { move, exists, writeFile, readJson, readdir, readFileSync, existsSync, mkdir } = +export const { copy, exists, writeFile, readJson, readdir, readFileSync, existsSync, mkdir } = fsExtra; // Loads the user's next.config.js file. @@ -116,7 +116,7 @@ export function populateOutputBundleOptions( } /** - * Moves static assets and other resources into the standlone directory, also generates the bundle.yaml + * Copy static assets and other resources into the standlone directory, also generates the bundle.yaml * @param rootDir The root directory of the uploaded source code. * @param outputBundleOptions The target location of built artifacts in the output bundle. * @param nextBuildDirectory The location of the .next directory. @@ -131,30 +131,30 @@ export async function generateBuildOutput( ): Promise { const staticDirectory = join(nextBuildDirectory, "static"); await Promise.all([ - move(staticDirectory, opts.outputStaticDirectoryPath, { overwrite: true }), - moveResources(appDir, opts.outputDirectoryAppPath, opts.bundleYamlPath), + copy(staticDirectory, opts.outputStaticDirectoryPath, { overwrite: true }), + copyResources(appDir, opts.outputDirectoryAppPath, opts.bundleYamlPath), generateBundleYaml(opts, rootDir, nextVersion, adapterMetadata), ]); return; } -// Move all files and directories to apphosting output directory. +// Copy all files and directories to apphosting output directory. // Files are skipped if there is already a file with the same name in the output directory -async function moveResources( +async function copyResources( appDir: string, outputBundleAppDir: string, bundleYamlPath: string, ): Promise { const appDirExists = await exists(appDir); if (!appDirExists) return; - const pathsToMove = await readdir(appDir); - for (const path of pathsToMove) { + const pathsToCopy = await readdir(appDir); + for (const path of pathsToCopy) { const isbundleYamlDir = join(appDir, path) === dirname(bundleYamlPath); const existsInOutputBundle = await exists(join(outputBundleAppDir, path)); // Keep apphosting.yaml files in the root directory still, as later steps expect them to be there const isApphostingYaml = path === "apphosting_preprocessed" || path === "apphosting.yaml"; if (!isbundleYamlDir && !existsInOutputBundle && !isApphostingYaml) { - await move(join(appDir, path), join(outputBundleAppDir, path)); + await copy(join(appDir, path), join(outputBundleAppDir, path)); } } return;