diff --git a/CHANGELOG.md b/CHANGELOG.md index cead3e09dfd..f52a9a9a22f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ - Adds (opt-out) experiment to disable cleaning up containers after a functions deploy (#6861) - Fix Next.js image optimization check in app directory for Windows (#6930) - Add support to next.config.mjs (#6933) +- Remove development-only files from Next.js deployments (#6731) diff --git a/scripts/webframeworks-deploy-tests/tests.ts b/scripts/webframeworks-deploy-tests/tests.ts index a078aa5caf1..9749aba128c 100644 --- a/scripts/webframeworks-deploy-tests/tests.ts +++ b/scripts/webframeworks-deploy-tests/tests.ts @@ -388,6 +388,23 @@ describe("webframeworks", function (this) { expect(unmatchedFiles, "matchedFiles").to.eql([]); expect(unmatchedExpectations, "unmatchedExpectations").to.eql([]); }); + + it("should not have development files to be deployed", async () => { + const distDir = ".next"; + + const UNEXPECTED_PATTERNS = [ + `${distDir}\/cache\/.*-development`, + `${distDir}\/cache\/eslint`, + ].map((it) => new RegExp(it)); + + const files = await getFilesListFromDir(`${NEXT_OUTPUT_PATH}/functions/${distDir}`); + + const filesContainingUnexpectedPatterns = UNEXPECTED_PATTERNS.filter((unexpectedPattern) => + files.some((file) => file.match(unexpectedPattern)), + ); + + expect(filesContainingUnexpectedPatterns.length).to.eql(0); + }); }); describe("angular", () => { diff --git a/src/frameworks/next/index.ts b/src/frameworks/next/index.ts index 635921093a4..b89b63983b7 100644 --- a/src/frameworks/next/index.ts +++ b/src/frameworks/next/index.ts @@ -54,6 +54,7 @@ import { getNextVersion, hasStaticAppNotFoundComponent, getRoutesWithServerAction, + getProductionDistDirFiles, whichNextConfigFile, } from "./utils"; import { NODE_VERSION, NPM_COMMAND_TIMEOUT_MILLIES, SHARP_VERSION, I18N_ROOT } from "../constants"; @@ -650,8 +651,17 @@ export async function ɵcodegenFunctionsDirectory( } } - await mkdirp(join(destDir, distDir)); - await copy(join(sourceDir, distDir), join(destDir, distDir)); + const [productionDistDirfiles] = await Promise.all([ + getProductionDistDirFiles(sourceDir, distDir), + mkdirp(join(destDir, distDir)), + ]); + + await Promise.all( + productionDistDirfiles.map((file) => + copy(file, file.replace(sourceDir, destDir), { recursive: true }), + ), + ); + return { packageJson, frameworksEntry: "next.js", dotEnv }; } diff --git a/src/frameworks/next/utils.ts b/src/frameworks/next/utils.ts index 4eb25777caf..a1aba6d4a91 100644 --- a/src/frameworks/next/utils.ts +++ b/src/frameworks/next/utils.ts @@ -3,6 +3,7 @@ import { pathExists } from "fs-extra"; import { basename, extname, join, posix, sep } from "path"; import { readFile } from "fs/promises"; import { sync as globSync } from "glob"; +import * as glob from "glob"; import type { PagesManifest } from "next/dist/build/webpack/plugins/pages-manifest-plugin"; import { coerce } from "semver"; @@ -461,6 +462,32 @@ export function getRoutesWithServerAction( return Array.from(routesWithServerAction); } +/** + * Get files in the dist directory to be deployed to Firebase, ignoring development files. + */ +export async function getProductionDistDirFiles( + sourceDir: string, + distDir: string, +): Promise { + const productionDistDirFiles = await new Promise((resolve, reject) => + glob( + "**", + { + ignore: [join("cache", "webpack", "*-development", "**"), join("cache", "eslint", "**")], + cwd: join(sourceDir, distDir), + nodir: true, + absolute: true, + }, + (err, matches) => { + if (err) reject(err); + resolve(matches); + }, + ), + ); + + return productionDistDirFiles; +} + /** * Get the Next.js config file name in the project directory, either * `next.config.js` or `next.config.mjs`. If none of them exist, return null.