From 28f79dc194bd48da74ff7419e73469fa73c2ef14 Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Thu, 7 Dec 2023 14:50:00 -0300 Subject: [PATCH 01/14] add function to get deployment domain --- src/hosting/api.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/hosting/api.ts b/src/hosting/api.ts index 2299552f868..746d97740c9 100644 --- a/src/hosting/api.ts +++ b/src/hosting/api.ts @@ -751,3 +751,20 @@ export async function getAllSiteDomains(projectId: string, siteId: string): Prom return Array.from(allSiteDomains); } + +/** + * Get the deployment domain. + * If hostingChannel is provided, get the channel url, otherwise get the + * default site url. + */ +export async function getDeploymentDomain( + projectId: string, + siteId: string, + hostingChannel?: string | undefined +): Promise { + const deploymentUrl = hostingChannel + ? await getChannel(projectId, siteId, hostingChannel).then((channel) => channel?.url) + : await getSite(projectId, siteId).then((site) => site.defaultUrl); + + return deploymentUrl?.replace(/^https?:\/\//, ""); +} From eb1823f269593b9996df125dbb5b9bd5c167b49d Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Thu, 7 Dec 2023 14:52:21 -0300 Subject: [PATCH 02/14] add deployment domain to VERCEL_URL env var --- src/frameworks/index.ts | 6 +++++- src/frameworks/interfaces.ts | 4 +++- src/frameworks/next/index.ts | 34 ++++++++++++++++++++++++++++++---- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/frameworks/index.ts b/src/frameworks/index.ts index 0bbbb4f1241..353c5c480ee 100644 --- a/src/frameworks/index.ts +++ b/src/frameworks/index.ts @@ -397,7 +397,11 @@ export async function prepareFrameworks( frameworksEntry = framework, dotEnv = {}, rewriteSource, - } = await codegenFunctionsDirectory(getProjectPath(), functionsDist, frameworksBuildTarget); + } = await codegenFunctionsDirectory(getProjectPath(), functionsDist, frameworksBuildTarget, { + projectId: project, + site: options.site, + hostingChannel: context?.hostingChannel, + }); const rewrite = { source: rewriteSource || posix.join(baseUrl, "**"), diff --git a/src/frameworks/interfaces.ts b/src/frameworks/interfaces.ts index f3825c0660a..6c9941f2964 100644 --- a/src/frameworks/interfaces.ts +++ b/src/frameworks/interfaces.ts @@ -52,6 +52,7 @@ export type FrameworksOptions = HostingOptions & export type FrameworkContext = { projectId?: string; hostingChannel?: string; + site?: string; }; export interface Framework { @@ -80,7 +81,8 @@ export interface Framework { ɵcodegenFunctionsDirectory?: ( dir: string, dest: string, - target: string + target: string, + context?: FrameworkContext ) => Promise<{ bootstrapScript?: string; packageJson: any; diff --git a/src/frameworks/next/index.ts b/src/frameworks/next/index.ts index 0368e6b28b6..68fec6bb140 100644 --- a/src/frameworks/next/index.ts +++ b/src/frameworks/next/index.ts @@ -30,7 +30,13 @@ import { validateLocales, getNodeModuleBin, } from "../utils"; -import { BuildResult, FrameworkType, SupportLevel } from "../interfaces"; +import { + BuildResult, + Framework, + FrameworkContext, + FrameworkType, + SupportLevel, +} from "../interfaces"; import { cleanEscapedChars, @@ -67,7 +73,7 @@ import { APP_PATHS_MANIFEST, ESBUILD_VERSION, } from "./constants"; -import { getAllSiteDomains } from "../../hosting/api"; +import { getAllSiteDomains, getDeploymentDomain } from "../../hosting/api"; import { logger } from "../../logger"; const DEFAULT_BUILD_SCRIPT = ["next build"]; @@ -488,7 +494,12 @@ export async function ɵcodegenPublicDirectory( /** * Create a directory for SSR content. */ -export async function ɵcodegenFunctionsDirectory(sourceDir: string, destDir: string) { +export async function ɵcodegenFunctionsDirectory( + sourceDir: string, + destDir: string, + target: string, + context?: FrameworkContext +): ReturnType> { const { distDir } = await getConfig(sourceDir); const packageJson = await readJSON(join(sourceDir, "package.json")); // Bundle their next.config.js with esbuild via NPX, pinned version was having troubles on m1 @@ -558,9 +569,24 @@ export async function ɵcodegenFunctionsDirectory(sourceDir: string, destDir: st packageJson.dependencies["sharp"] = SHARP_VERSION; } + const dotEnv: Record = {}; + if (context?.projectId && context?.site) { + const deploymentDomain = await getDeploymentDomain( + context.projectId, + context.site, + context.hostingChannel + ); + + if (deploymentDomain) { + // Add the deployment domain to VERCEL_URL env variable, which is + // required for dynamic OG images to work. + dotEnv["VERCEL_URL"] = deploymentDomain; + } + } + await mkdirp(join(destDir, distDir)); await copy(join(sourceDir, distDir), join(destDir, distDir)); - return { packageJson, frameworksEntry: "next.js" }; + return { packageJson, frameworksEntry: "next.js", dotEnv }; } /** From 8fa2d497e7c25d2cb38280dd4519c3915bc30b68 Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Thu, 7 Dec 2023 15:03:55 -0300 Subject: [PATCH 03/14] add tests for getDeploymentDomain --- src/test/hosting/api.spec.ts | 49 ++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/test/hosting/api.spec.ts b/src/test/hosting/api.spec.ts index 8e0b34329d3..095d7090e80 100644 --- a/src/test/hosting/api.spec.ts +++ b/src/test/hosting/api.spec.ts @@ -821,6 +821,55 @@ describe("hosting", () => { expect(nock.isDone()).to.be.true; }); }); + + describe("getDeploymentDomain", () => { + afterEach(nock.cleanAll); + + it("should get the default site domain when hostingChannel is omitted", async () => { + const defaultUrl = `https://${ + EXPECTED_DOMAINS_RESPONSE[EXPECTED_DOMAINS_RESPONSE.length - 1] + }`; + + nock(hostingApiOrigin) + .get(`/v1beta1/projects/${PROJECT_ID}/sites/${SITE}`) + .reply(200, { defaultUrl }); + + expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE)).to.be(defaultUrl); + }); + + it("should get the default site domain when hostingChannel is undefined", async () => { + const defaultUrl = `https://${ + EXPECTED_DOMAINS_RESPONSE[EXPECTED_DOMAINS_RESPONSE.length - 1] + }`; + + nock(hostingApiOrigin) + .get(`/v1beta1/projects/${PROJECT_ID}/sites/${SITE}`) + .reply(200, { defaultUrl }); + + expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE, undefined)).to.be(defaultUrl); + }); + + it("should get the channel domain", async () => { + const channelId = "my-channel"; + const channel = { url: `https://${PROJECT_ID}--${channelId}-123123.web.app` }; + + nock(hostingApiOrigin) + .get(`/v1beta1/projects/${PROJECT_ID}/sites/${SITE}/channels/${channelId}`) + .reply(200, channel); + + expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE, channelId)).to.be(channel.url); + }); + + it("should return undefined if channel not found", async () => { + const channelId = "my-channel"; + + nock(hostingApiOrigin) + .get(`/v1beta1/projects/${PROJECT_ID}/sites/${SITE}/channels/${channelId}`) + .reply(404, {}); + + expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE, channelId)).to.be.undefined; + }); + }); }); describe("normalizeName", () => { From 70ab21e469f8f7577a9712d08e169f9eeaa47847 Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Thu, 7 Dec 2023 15:07:40 -0300 Subject: [PATCH 04/14] link Next.js docs --- src/frameworks/next/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/frameworks/next/index.ts b/src/frameworks/next/index.ts index 68fec6bb140..faa1a1cb791 100644 --- a/src/frameworks/next/index.ts +++ b/src/frameworks/next/index.ts @@ -579,7 +579,8 @@ export async function ɵcodegenFunctionsDirectory( if (deploymentDomain) { // Add the deployment domain to VERCEL_URL env variable, which is - // required for dynamic OG images to work. + // required for dynamic OG images to work without manual configuration. + // See: https://nextjs.org/docs/app/api-reference/functions/generate-metadata#default-value dotEnv["VERCEL_URL"] = deploymentDomain; } } From a60fce1ee034d85c3fc38f177a621ac0e18ef027 Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Thu, 7 Dec 2023 15:10:08 -0300 Subject: [PATCH 05/14] to.be > to.equal --- src/test/hosting/api.spec.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/test/hosting/api.spec.ts b/src/test/hosting/api.spec.ts index 095d7090e80..d40ca30950b 100644 --- a/src/test/hosting/api.spec.ts +++ b/src/test/hosting/api.spec.ts @@ -834,7 +834,7 @@ describe("hosting", () => { .get(`/v1beta1/projects/${PROJECT_ID}/sites/${SITE}`) .reply(200, { defaultUrl }); - expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE)).to.be(defaultUrl); + expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE)).to.equal(defaultUrl); }); it("should get the default site domain when hostingChannel is undefined", async () => { @@ -846,7 +846,9 @@ describe("hosting", () => { .get(`/v1beta1/projects/${PROJECT_ID}/sites/${SITE}`) .reply(200, { defaultUrl }); - expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE, undefined)).to.be(defaultUrl); + expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE, undefined)).to.equal( + defaultUrl + ); }); it("should get the channel domain", async () => { @@ -857,7 +859,9 @@ describe("hosting", () => { .get(`/v1beta1/projects/${PROJECT_ID}/sites/${SITE}/channels/${channelId}`) .reply(200, channel); - expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE, channelId)).to.be(channel.url); + expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE, channelId)).to.equal( + channel.url + ); }); it("should return undefined if channel not found", async () => { From 0b98afc947dd408bd053ee17b08030322dfd4804 Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Thu, 7 Dec 2023 15:17:35 -0300 Subject: [PATCH 06/14] ops, test domain instead of url --- src/test/hosting/api.spec.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/test/hosting/api.spec.ts b/src/test/hosting/api.spec.ts index d40ca30950b..dc719e4636c 100644 --- a/src/test/hosting/api.spec.ts +++ b/src/test/hosting/api.spec.ts @@ -826,41 +826,40 @@ describe("hosting", () => { afterEach(nock.cleanAll); it("should get the default site domain when hostingChannel is omitted", async () => { - const defaultUrl = `https://${ - EXPECTED_DOMAINS_RESPONSE[EXPECTED_DOMAINS_RESPONSE.length - 1] - }`; + const defaultDomain = EXPECTED_DOMAINS_RESPONSE[EXPECTED_DOMAINS_RESPONSE.length - 1]; + const defaultUrl = `https://${defaultDomain}`; nock(hostingApiOrigin) .get(`/v1beta1/projects/${PROJECT_ID}/sites/${SITE}`) .reply(200, { defaultUrl }); - expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE)).to.equal(defaultUrl); + expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE)).to.equal(defaultDomain); }); it("should get the default site domain when hostingChannel is undefined", async () => { - const defaultUrl = `https://${ - EXPECTED_DOMAINS_RESPONSE[EXPECTED_DOMAINS_RESPONSE.length - 1] - }`; + const defaultDomain = EXPECTED_DOMAINS_RESPONSE[EXPECTED_DOMAINS_RESPONSE.length - 1]; + const defaultUrl = `https://${defaultDomain}`; nock(hostingApiOrigin) .get(`/v1beta1/projects/${PROJECT_ID}/sites/${SITE}`) .reply(200, { defaultUrl }); expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE, undefined)).to.equal( - defaultUrl + defaultDomain ); }); it("should get the channel domain", async () => { const channelId = "my-channel"; - const channel = { url: `https://${PROJECT_ID}--${channelId}-123123.web.app` }; + const channelDomain = `${PROJECT_ID}--${channelId}-123123.web.app`; + const channel = { url: `https://${channelDomain}` }; nock(hostingApiOrigin) .get(`/v1beta1/projects/${PROJECT_ID}/sites/${SITE}/channels/${channelId}`) .reply(200, channel); expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE, channelId)).to.equal( - channel.url + channelDomain ); }); From c34a472e97d2b5903dd9b613d98a6765048dd894 Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Thu, 7 Dec 2023 16:24:41 -0300 Subject: [PATCH 07/14] add frameworkContext param to `build` --- src/frameworks/index.ts | 31 ++++++++++++++++++++----------- src/frameworks/interfaces.ts | 2 +- src/frameworks/next/index.ts | 6 +++++- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/frameworks/index.ts b/src/frameworks/index.ts index 353c5c480ee..f5dc59b59d4 100644 --- a/src/frameworks/index.ts +++ b/src/frameworks/index.ts @@ -89,17 +89,18 @@ const BUILD_MEMO = new Map>(); // Memoize the build based on both the dir and the environment variables function memoizeBuild( dir: string, - build: (dir: string, target: string) => Promise, + build: Framework["build"], deps: any[], - target: string -) { + target: string, + context: FrameworkContext +): ReturnType { const key = [dir, ...deps]; for (const existingKey of BUILD_MEMO.keys()) { if (isDeepStrictEqual(existingKey, key)) { - return BUILD_MEMO.get(existingKey); + return BUILD_MEMO.get(existingKey) as ReturnType; } } - const value = build(dir, target); + const value = build(dir, target, context); BUILD_MEMO.set(key, value); return value; } @@ -286,6 +287,12 @@ export async function prepareFrameworks( purpose !== "deploy" && (await shouldUseDevModeHandle(frameworksBuildTarget, getProjectPath())); + const frameworkContext: FrameworkContext = { + projectId: project, + site: options.site, + hostingChannel: context?.hostingChannel, + }; + let codegenFunctionsDirectory: Framework["ɵcodegenFunctionsDirectory"]; let baseUrl = ""; const rewrites = []; @@ -309,7 +316,8 @@ export async function prepareFrameworks( getProjectPath(), build, [firebaseDefaults, frameworksBuildTarget], - frameworksBuildTarget + frameworksBuildTarget, + frameworkContext ); const { wantsBackend = false, trailingSlash, i18n = false }: BuildResult = buildResult || {}; @@ -397,11 +405,12 @@ export async function prepareFrameworks( frameworksEntry = framework, dotEnv = {}, rewriteSource, - } = await codegenFunctionsDirectory(getProjectPath(), functionsDist, frameworksBuildTarget, { - projectId: project, - site: options.site, - hostingChannel: context?.hostingChannel, - }); + } = await codegenFunctionsDirectory( + getProjectPath(), + functionsDist, + frameworksBuildTarget, + frameworkContext + ); const rewrite = { source: rewriteSource || posix.join(baseUrl, "**"), diff --git a/src/frameworks/interfaces.ts b/src/frameworks/interfaces.ts index 6c9941f2964..0ea91951bc3 100644 --- a/src/frameworks/interfaces.ts +++ b/src/frameworks/interfaces.ts @@ -60,7 +60,7 @@ export interface Framework { discover: (dir: string) => Promise; type: FrameworkType; name: string; - build: (dir: string, target: string) => Promise; + build: (dir: string, target: string, context?: FrameworkContext) => Promise; support: SupportLevel; docsUrl?: string; init?: (setup: any, config: any) => Promise; diff --git a/src/frameworks/next/index.ts b/src/frameworks/next/index.ts index faa1a1cb791..22977094a2a 100644 --- a/src/frameworks/next/index.ts +++ b/src/frameworks/next/index.ts @@ -107,7 +107,11 @@ export async function discover(dir: string) { /** * Build a next.js application. */ -export async function build(dir: string): Promise { +export async function build( + dir: string, + target: string, + context?: FrameworkContext +): Promise { await warnIfCustomBuildScript(dir, name, DEFAULT_BUILD_SCRIPT); const reactVersion = getReactVersion(dir); From 314216d939278ec873ca5cffdbad76ce4dec3994 Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Thu, 7 Dec 2023 16:24:57 -0300 Subject: [PATCH 08/14] add VERCEL_URL to Next.js build env --- src/frameworks/next/index.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/frameworks/next/index.ts b/src/frameworks/next/index.ts index 22977094a2a..2092bebafec 100644 --- a/src/frameworks/next/index.ts +++ b/src/frameworks/next/index.ts @@ -120,10 +120,27 @@ export async function build( process.env.__NEXT_REACT_ROOT = "true"; } + const env = { ...process.env }; + + if (context?.projectId && context?.site) { + const deploymentDomain = await getDeploymentDomain( + context.projectId, + context.site, + context.hostingChannel + ); + + if (deploymentDomain) { + // Add the deployment domain to VERCEL_URL env variable, which is + // required for dynamic OG images to work without manual configuration. + // See: https://nextjs.org/docs/app/api-reference/functions/generate-metadata#default-value + env["VERCEL_URL"] = deploymentDomain; + } + } + const cli = getNodeModuleBin("next", dir); const nextBuild = new Promise((resolve, reject) => { - const buildProcess = spawn(cli, ["build"], { cwd: dir }); + const buildProcess = spawn(cli, ["build"], { cwd: dir, env }); buildProcess.stdout?.on("data", (data) => logger.info(data.toString())); buildProcess.stderr?.on("data", (data) => logger.info(data.toString())); buildProcess.on("error", (err) => { From 782598d72475d1ffa160a519df7ef35280005045 Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Thu, 7 Dec 2023 16:52:00 -0300 Subject: [PATCH 09/14] undefined site when 404 --- src/hosting/api.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/hosting/api.ts b/src/hosting/api.ts index 746d97740c9..2a39057edce 100644 --- a/src/hosting/api.ts +++ b/src/hosting/api.ts @@ -764,7 +764,16 @@ export async function getDeploymentDomain( ): Promise { const deploymentUrl = hostingChannel ? await getChannel(projectId, siteId, hostingChannel).then((channel) => channel?.url) - : await getSite(projectId, siteId).then((site) => site.defaultUrl); + : await getSite(projectId, siteId) + .then((site) => site.defaultUrl) + .catch((e: unknown) => { + // return undefined if the site doesn't exist + if (e instanceof FirebaseError && e.status === 404) { + return undefined; + } + + throw e; + }); return deploymentUrl?.replace(/^https?:\/\//, ""); } From 54f36facd6fa3979ec3a57d751c56bd1ba0ae84c Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Thu, 7 Dec 2023 16:53:31 -0300 Subject: [PATCH 10/14] add unit test for site not found --- src/test/hosting/api.spec.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/hosting/api.spec.ts b/src/test/hosting/api.spec.ts index dc719e4636c..3fc71eb8f80 100644 --- a/src/test/hosting/api.spec.ts +++ b/src/test/hosting/api.spec.ts @@ -872,6 +872,12 @@ describe("hosting", () => { expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE, channelId)).to.be.undefined; }); + + it("should return undefined if site not found", async () => { + nock(hostingApiOrigin).get(`/v1beta1/projects/${PROJECT_ID}/sites/${SITE}`).reply(404, {}); + + expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE)).to.be.undefined; + }); }); }); From b56b9b8064c38ec33cb2db498c4f977f2bd3ee81 Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Thu, 7 Dec 2023 17:07:34 -0300 Subject: [PATCH 11/14] 404 status from e.original instead --- src/hosting/api.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/hosting/api.ts b/src/hosting/api.ts index 2a39057edce..20a4a4e9462 100644 --- a/src/hosting/api.ts +++ b/src/hosting/api.ts @@ -768,7 +768,11 @@ export async function getDeploymentDomain( .then((site) => site.defaultUrl) .catch((e: unknown) => { // return undefined if the site doesn't exist - if (e instanceof FirebaseError && e.status === 404) { + if ( + e instanceof FirebaseError && + e.original instanceof FirebaseError && + e.original.status === 404 + ) { return undefined; } From ace5a87b4f7f5ab70b5c1a72a47911082b45de99 Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Thu, 7 Dec 2023 17:26:14 -0300 Subject: [PATCH 12/14] refactor nit --- src/hosting/api.ts | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/hosting/api.ts b/src/hosting/api.ts index 20a4a4e9462..53629edfb99 100644 --- a/src/hosting/api.ts +++ b/src/hosting/api.ts @@ -551,6 +551,7 @@ export async function getSite(project: string, site: string): Promise { if (e instanceof FirebaseError && e.status === 404) { throw new FirebaseError(`could not find site "${site}" for project "${project}"`, { original: e, + status: e.status, }); } throw e; @@ -762,22 +763,23 @@ export async function getDeploymentDomain( siteId: string, hostingChannel?: string | undefined ): Promise { - const deploymentUrl = hostingChannel - ? await getChannel(projectId, siteId, hostingChannel).then((channel) => channel?.url) - : await getSite(projectId, siteId) - .then((site) => site.defaultUrl) - .catch((e: unknown) => { - // return undefined if the site doesn't exist - if ( - e instanceof FirebaseError && - e.original instanceof FirebaseError && - e.original.status === 404 - ) { - return undefined; - } - - throw e; - }); + const channel = hostingChannel ? await getChannel(projectId, siteId, hostingChannel) : undefined; + + const site = channel + ? undefined + : await getSite(projectId, siteId).catch((e: unknown) => { + if ( + e instanceof FirebaseError && + e.original instanceof FirebaseError && + e.original.status === 404 + ) { + return undefined; + } + + throw e; + }); + + const deploymentUrl = channel?.url || site?.defaultUrl; return deploymentUrl?.replace(/^https?:\/\//, ""); } From 531f33b92b2181ac126300b3c9c17b06742aa7ec Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Thu, 7 Dec 2023 18:01:44 -0300 Subject: [PATCH 13/14] hopefully last refactor --- src/hosting/api.ts | 38 +++++++++++++++++++----------------- src/test/hosting/api.spec.ts | 8 ++++---- src/utils.ts | 11 +++++++++++ 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/hosting/api.ts b/src/hosting/api.ts index 53629edfb99..c469e0af4fb 100644 --- a/src/hosting/api.ts +++ b/src/hosting/api.ts @@ -5,6 +5,7 @@ import * as operationPoller from "../operation-poller"; import { DEFAULT_DURATION } from "../hosting/expireUtils"; import { getAuthDomains, updateAuthDomains } from "../gcp/auth"; import * as proto from "../gcp/proto"; +import { getHostnameFromUrl } from "../utils"; const ONE_WEEK_MS = 604800000; // 7 * 24 * 60 * 60 * 1000 @@ -762,24 +763,25 @@ export async function getDeploymentDomain( projectId: string, siteId: string, hostingChannel?: string | undefined -): Promise { - const channel = hostingChannel ? await getChannel(projectId, siteId, hostingChannel) : undefined; - - const site = channel - ? undefined - : await getSite(projectId, siteId).catch((e: unknown) => { - if ( - e instanceof FirebaseError && - e.original instanceof FirebaseError && - e.original.status === 404 - ) { - return undefined; - } - - throw e; - }); +): Promise { + if (hostingChannel) { + const channel = await getChannel(projectId, siteId, hostingChannel); + + return channel && getHostnameFromUrl(channel?.url); + } + + const site = await getSite(projectId, siteId).catch((e: unknown) => { + // return null if the site doesn't exist + if ( + e instanceof FirebaseError && + e.original instanceof FirebaseError && + e.original.status === 404 + ) { + return null; + } - const deploymentUrl = channel?.url || site?.defaultUrl; + throw e; + }); - return deploymentUrl?.replace(/^https?:\/\//, ""); + return site && getHostnameFromUrl(site?.defaultUrl); } diff --git a/src/test/hosting/api.spec.ts b/src/test/hosting/api.spec.ts index 3fc71eb8f80..8a25ede2147 100644 --- a/src/test/hosting/api.spec.ts +++ b/src/test/hosting/api.spec.ts @@ -863,20 +863,20 @@ describe("hosting", () => { ); }); - it("should return undefined if channel not found", async () => { + it("should return null if channel not found", async () => { const channelId = "my-channel"; nock(hostingApiOrigin) .get(`/v1beta1/projects/${PROJECT_ID}/sites/${SITE}/channels/${channelId}`) .reply(404, {}); - expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE, channelId)).to.be.undefined; + expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE, channelId)).to.be.null; }); - it("should return undefined if site not found", async () => { + it("should return null if site not found", async () => { nock(hostingApiOrigin).get(`/v1beta1/projects/${PROJECT_ID}/sites/${SITE}`).reply(404, {}); - expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE)).to.be.undefined; + expect(await hostingApi.getDeploymentDomain(PROJECT_ID, SITE)).to.be.null; }); }); }); diff --git a/src/utils.ts b/src/utils.ts index cf33dd3e9ac..93ea9e005f0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -799,3 +799,14 @@ export async function openInBrowserPopup( }, }; } + +/** + * Get hostname from a given url or null if the url is invalid + */ +export function getHostnameFromUrl(url: string): string | null { + try { + return new URL(url).hostname; + } catch (e: unknown) { + return null; + } +} From fdb3ba455865b21164c483ce594bcc64a1603008 Mon Sep 17 00:00:00 2001 From: Leonardo Ortiz Date: Thu, 7 Dec 2023 18:11:53 -0300 Subject: [PATCH 14/14] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80b2f725d68..d9886224c97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,3 +6,4 @@ - Permit use of more SSR regions in Web Frameworks deploys. (#6086) - Limit Web Framework's generated Cloud Function name to 23 characters, fixing deploys for some. (#6260) - Allow Nuxt as an option during `firebase init hosting`. (#6309) +- Fix Next.js dynamic and static OG images. (#6592)