From 35e5b6e0b69cb338d5002c9c737ea3776decce4c Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Thu, 30 Nov 2023 19:31:17 -0300 Subject: [PATCH 1/5] check whether app `not-found` is static --- src/frameworks/next/index.ts | 11 ++++++++++- src/frameworks/next/utils.ts | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/frameworks/next/index.ts b/src/frameworks/next/index.ts index 852e9329755..62467d353bf 100644 --- a/src/frameworks/next/index.ts +++ b/src/frameworks/next/index.ts @@ -47,6 +47,7 @@ import { getHeadersFromMetaFiles, cleanI18n, getNextVersion, + hasStaticAppNotFoundComponent, } from "./utils"; import { NODE_VERSION, NPM_COMMAND_TIMEOUT_MILLIES, SHARP_VERSION, I18N_ROOT } from "../constants"; import type { @@ -208,13 +209,21 @@ export async function build(dir: string): Promise { headers.push(...headersFromMetaFiles); if (appPathsManifest) { - const unrenderedServerComponents = getNonStaticServerComponents( + let unrenderedServerComponents = getNonStaticServerComponents( appPathsManifest, appPathRoutesManifest, prerenderedRoutes, dynamicRoutes ); + if (unrenderedServerComponents.includes("/_not-found")) { + if (await hasStaticAppNotFoundComponent(dir, distDir)) { + unrenderedServerComponents = unrenderedServerComponents.filter( + (path) => path !== "/_not-found" + ); + } + } + for (const key of unrenderedServerComponents) { reasonsForBackend.add(`non-static component ${key}`); } diff --git a/src/frameworks/next/utils.ts b/src/frameworks/next/utils.ts index d625c105a84..1a095771582 100644 --- a/src/frameworks/next/utils.ts +++ b/src/frameworks/next/utils.ts @@ -407,3 +407,17 @@ export function getNextVersion(cwd: string): string | undefined { return nextVersionSemver.toString(); } + +/** + * Whether the Next.js project has a static `not-found` page in the app directory. + * + * The Next.js build manifests are misleading regarding the existence of a static + * `not-found` component. Therefore, we check if a `_not-found.html` file exists + * in the generated app directory files to know whether `not-found` is static. + */ +export async function hasStaticAppNotFoundComponent( + sourceDir: string, + distDir: string +): Promise { + return pathExists(join(sourceDir, distDir, "server", "app", "_not-found.html")); +} From 4458a176064442b6629c39b20c6f02ceeeb10175 Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Fri, 1 Dec 2023 17:17:43 -0300 Subject: [PATCH 2/5] use set, const --- src/frameworks/next/index.ts | 8 +++----- src/frameworks/next/utils.ts | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/frameworks/next/index.ts b/src/frameworks/next/index.ts index 62467d353bf..c3fa99c25fc 100644 --- a/src/frameworks/next/index.ts +++ b/src/frameworks/next/index.ts @@ -209,18 +209,16 @@ export async function build(dir: string): Promise { headers.push(...headersFromMetaFiles); if (appPathsManifest) { - let unrenderedServerComponents = getNonStaticServerComponents( + const unrenderedServerComponents = getNonStaticServerComponents( appPathsManifest, appPathRoutesManifest, prerenderedRoutes, dynamicRoutes ); - if (unrenderedServerComponents.includes("/_not-found")) { + if (unrenderedServerComponents.has("/_not-found")) { if (await hasStaticAppNotFoundComponent(dir, distDir)) { - unrenderedServerComponents = unrenderedServerComponents.filter( - (path) => path !== "/_not-found" - ); + unrenderedServerComponents.delete("/_not-found"); } } diff --git a/src/frameworks/next/utils.ts b/src/frameworks/next/utils.ts index 1a095771582..94b23b40a25 100644 --- a/src/frameworks/next/utils.ts +++ b/src/frameworks/next/utils.ts @@ -338,7 +338,7 @@ export function getNonStaticServerComponents( appPathRoutesManifest: AppPathRoutesManifest, prerenderedRoutes: string[], dynamicRoutes: string[] -): string[] { +): Set { const nonStaticServerComponents = Object.entries(appPathsManifest) .filter(([it, src]) => { if (extname(src) !== ".js") return; @@ -347,7 +347,7 @@ export function getNonStaticServerComponents( }) .map(([it]) => it); - return nonStaticServerComponents; + return new Set(nonStaticServerComponents); } /** From baf4b1981ab38648ed318c74f03574e281744025 Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Fri, 1 Dec 2023 17:20:34 -0300 Subject: [PATCH 3/5] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ae0f507107..065a1de7870 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ - Added the ability to deploy Angular apps using [the new application-builder](https://angular.dev/tools/cli/esbuild). (#6480) - Fixed an issue where `--non-interactive` flag is not respected in Firestore indexes deploys. (#6539) - Fixed an issue where `login:use` would not work outside of a Firebase project directory. (#6526) +- Prevent app router static `not-found` requiring a Cloud Function in Next.js deployments. (#6558) From 1e720b18dd97aad1d57b2f5eda959b131f23685d Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Fri, 1 Dec 2023 17:26:30 -0300 Subject: [PATCH 4/5] use set in unit test --- src/test/frameworks/next/utils.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/frameworks/next/utils.spec.ts b/src/test/frameworks/next/utils.spec.ts index 0e5fa2163e2..6097523bb30 100644 --- a/src/test/frameworks/next/utils.spec.ts +++ b/src/test/frameworks/next/utils.spec.ts @@ -461,7 +461,7 @@ describe("Next.js utils", () => { Object.keys(prerenderManifest.routes), Object.keys(prerenderManifest.dynamicRoutes) ) - ).to.deep.equal(["/api/test/route"]); + ).to.deep.equal(new Set(["/api/test/route"])); }); }); From 995bcefd3c4ef62a08bb3f229954acdcfa560fc6 Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Fri, 1 Dec 2023 17:51:28 -0300 Subject: [PATCH 5/5] only one if --- src/frameworks/next/index.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/frameworks/next/index.ts b/src/frameworks/next/index.ts index c3fa99c25fc..ccdcd36eced 100644 --- a/src/frameworks/next/index.ts +++ b/src/frameworks/next/index.ts @@ -216,10 +216,11 @@ export async function build(dir: string): Promise { dynamicRoutes ); - if (unrenderedServerComponents.has("/_not-found")) { - if (await hasStaticAppNotFoundComponent(dir, distDir)) { - unrenderedServerComponents.delete("/_not-found"); - } + if ( + unrenderedServerComponents.has("/_not-found") && + (await hasStaticAppNotFoundComponent(dir, distDir)) + ) { + unrenderedServerComponents.delete("/_not-found"); } for (const key of unrenderedServerComponents) {