diff --git a/packages/app-store-cli/src/build.ts b/packages/app-store-cli/src/build.ts index 6469db035c..900ee25b56 100644 --- a/packages/app-store-cli/src/build.ts +++ b/packages/app-store-cli/src/build.ts @@ -40,6 +40,7 @@ type App = Partial & { function generateFiles() { const browserOutput = [`import dynamic from "next/dynamic"`]; const metadataOutput = []; + const bookerMetadataOutput = []; const schemasOutput = []; const appKeysSchemasOutput = []; const serverOutput = []; @@ -70,22 +71,30 @@ function generateFiles() { } }); - function forEachAppDir(callback: (arg: App) => void) { + function forEachAppDir(callback: (arg: App) => void, filter: (arg: App) => boolean = () => true) { for (let i = 0; i < appDirs.length; i++) { const configPath = path.join(APP_STORE_PATH, appDirs[i].path, "config.json"); + const metadataPath = path.join(APP_STORE_PATH, appDirs[i].path, "_metadata.ts"); let app; if (fs.existsSync(configPath)) { app = JSON.parse(fs.readFileSync(configPath).toString()); + } else if (fs.existsSync(metadataPath)) { + // eslint-disable-next-line @typescript-eslint/no-var-requires + app = require(metadataPath).metadata; } else { app = {}; } - callback({ + const finalApp = { ...app, name: appDirs[i].name, path: appDirs[i].path, - }); + }; + + if (filter(finalApp)) { + callback(finalApp); + } } } @@ -130,7 +139,8 @@ function generateFiles() { lazyImport?: boolean; importConfig: ImportConfig; entryObjectKeyGetter?: (arg: App, importName?: string) => string; - } + }, + filter?: (arg: App) => boolean ) { const output: string[] = []; @@ -174,7 +184,7 @@ function generateFiles() { } } } - }); + }, filter); } function createExportObject() { @@ -201,7 +211,7 @@ function generateFiles() { } } } - }); + }, filter); output.push(`};`); } @@ -247,6 +257,25 @@ function generateFiles() { }) ); + bookerMetadataOutput.push( + ...getExportedObject( + "appStoreMetadata", + { + // Try looking for config.json and if it's not found use _metadata.ts to generate appStoreMetadata + importConfig: [ + { + fileToBeImported: "config.json", + importName: "default", + }, + { + fileToBeImported: "_metadata.ts", + importName: "metadata", + }, + ], + }, + isBookerApp + ) + ); schemasOutput.push( ...getExportedObject("appDataSchemas", { // Import path must have / even for windows and not \ @@ -312,6 +341,7 @@ function generateFiles() { ["apps.browser.generated.tsx", browserOutput], ["apps.schemas.generated.ts", schemasOutput], ["apps.keys-schemas.generated.ts", appKeysSchemasOutput], + ["bookerApps.metadata.generated.ts", bookerMetadataOutput], ]; filesToGenerate.forEach(([fileName, output]) => { fs.writeFileSync(`${APP_STORE_PATH}/${fileName}`, formatOutput(`${banner}${output.join("\n")}`)); @@ -347,3 +377,11 @@ if (isInWatchMode) { } else { generateFiles(); } + +function isBookerApp(app: App) { + // Right now there are only two types of Apps that booker needs. + // Note that currently payment apps' meta don't need to be accessed on booker. We just access from DB eventType.metadata + // 1. It is a location app(e.g. any Conferencing App) + // 2. It is a tag manager app(e.g. Google Analytics, GTM, Fathom) + return !!(app.appData?.location || app.appData?.tag); +} diff --git a/packages/app-store/BookingPageTagManager.tsx b/packages/app-store/BookingPageTagManager.tsx index db333ec0a9..e8349e80f7 100644 --- a/packages/app-store/BookingPageTagManager.tsx +++ b/packages/app-store/BookingPageTagManager.tsx @@ -1,7 +1,7 @@ import Script from "next/script"; import { getEventTypeAppData } from "@calcom/app-store/_utils/getEventTypeAppData"; -import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData"; +import { appStoreMetadata } from "@calcom/app-store/bookerAppsMetaData"; import type { appDataSchemas } from "./apps.schemas.generated"; diff --git a/packages/app-store/appStoreMetaData.ts b/packages/app-store/appStoreMetaData.ts index 9410aa54d2..74f6fdb95d 100644 --- a/packages/app-store/appStoreMetaData.ts +++ b/packages/app-store/appStoreMetaData.ts @@ -1,7 +1,7 @@ import type { AppMeta } from "@calcom/types/App"; import { appStoreMetadata as rawAppStoreMetadata } from "./apps.metadata.generated"; -import { getAppAssetFullPath } from "./getAppAssetFullPath"; +import { getNormalizedAppMetadata } from "./getNormalizedAppMetadata"; type RawAppStoreMetaData = typeof rawAppStoreMetadata; type AppStoreMetaData = { @@ -10,18 +10,5 @@ type AppStoreMetaData = { export const appStoreMetadata = {} as AppStoreMetaData; for (const [key, value] of Object.entries(rawAppStoreMetadata)) { - const dirName = "dirName" in value ? value.dirName : value.slug; - if (!dirName) { - throw new Error(`Couldn't derive dirName for app ${key}`); - } - const metadata = (appStoreMetadata[key as keyof typeof appStoreMetadata] = { - appData: null, - dirName, - __template: "", - ...value, - } as AppStoreMetaData[keyof AppStoreMetaData]); - metadata.logo = getAppAssetFullPath(metadata.logo, { - dirName, - isTemplate: metadata.isTemplate, - }); + appStoreMetadata[key as keyof typeof appStoreMetadata] = getNormalizedAppMetadata(value); } diff --git a/packages/app-store/bookerApps.metadata.generated.ts b/packages/app-store/bookerApps.metadata.generated.ts new file mode 100644 index 0000000000..9ac85a9b1e --- /dev/null +++ b/packages/app-store/bookerApps.metadata.generated.ts @@ -0,0 +1,67 @@ +/** + This file is autogenerated using the command `yarn app-store:build --watch`. + Don't modify this file manually. +**/ +import around_config_json from "./around/config.json"; +import campfire_config_json from "./campfire/config.json"; +import { metadata as dailyvideo__metadata_ts } from "./dailyvideo/_metadata"; +import discord_config_json from "./discord/config.json"; +import eightxeight_config_json from "./eightxeight/config.json"; +import element_call_config_json from "./element-call/config.json"; +import facetime_config_json from "./facetime/config.json"; +import fathom_config_json from "./fathom/config.json"; +import ga4_config_json from "./ga4/config.json"; +import { metadata as googlevideo__metadata_ts } from "./googlevideo/_metadata"; +import gtm_config_json from "./gtm/config.json"; +import { metadata as huddle01video__metadata_ts } from "./huddle01video/_metadata"; +import { metadata as jitsivideo__metadata_ts } from "./jitsivideo/_metadata"; +import metapixel_config_json from "./metapixel/config.json"; +import mirotalk_config_json from "./mirotalk/config.json"; +import office365video_config_json from "./office365video/config.json"; +import ping_config_json from "./ping/config.json"; +import plausible_config_json from "./plausible/config.json"; +import riverside_config_json from "./riverside/config.json"; +import signal_config_json from "./signal/config.json"; +import sirius_video_config_json from "./sirius_video/config.json"; +import sylapsvideo_config_json from "./sylapsvideo/config.json"; +import { metadata as tandemvideo__metadata_ts } from "./tandemvideo/_metadata"; +import telegram_config_json from "./telegram/config.json"; +import booking_pages_tag_config_json from "./templates/booking-pages-tag/config.json"; +import event_type_location_video_static_config_json from "./templates/event-type-location-video-static/config.json"; +import webex_config_json from "./webex/config.json"; +import whatsapp_config_json from "./whatsapp/config.json"; +import whereby_config_json from "./whereby/config.json"; +import { metadata as zoomvideo__metadata_ts } from "./zoomvideo/_metadata"; + +export const appStoreMetadata = { + around: around_config_json, + campfire: campfire_config_json, + dailyvideo: dailyvideo__metadata_ts, + discord: discord_config_json, + eightxeight: eightxeight_config_json, + "element-call": element_call_config_json, + facetime: facetime_config_json, + fathom: fathom_config_json, + ga4: ga4_config_json, + googlevideo: googlevideo__metadata_ts, + gtm: gtm_config_json, + huddle01video: huddle01video__metadata_ts, + jitsivideo: jitsivideo__metadata_ts, + metapixel: metapixel_config_json, + mirotalk: mirotalk_config_json, + office365video: office365video_config_json, + ping: ping_config_json, + plausible: plausible_config_json, + riverside: riverside_config_json, + signal: signal_config_json, + sirius_video: sirius_video_config_json, + sylapsvideo: sylapsvideo_config_json, + tandemvideo: tandemvideo__metadata_ts, + telegram: telegram_config_json, + "booking-pages-tag": booking_pages_tag_config_json, + "event-type-location-video-static": event_type_location_video_static_config_json, + webex: webex_config_json, + whatsapp: whatsapp_config_json, + whereby: whereby_config_json, + zoomvideo: zoomvideo__metadata_ts, +}; diff --git a/packages/app-store/bookerAppsMetaData.ts b/packages/app-store/bookerAppsMetaData.ts new file mode 100644 index 0000000000..6752c20985 --- /dev/null +++ b/packages/app-store/bookerAppsMetaData.ts @@ -0,0 +1,16 @@ +import type { AppMeta } from "@calcom/types/App"; + +// We have to import all the booker-apps config/metadata in here as without importing that we can't dig into their config and read their props +// It isn't a significant bundle-size impact as we are only importing the metadata of only the booker apps, but when it becomes a problem we can figure out a solution +import { appStoreMetadata as rawBookerAppsMetadata } from "./bookerApps.metadata.generated"; +import { getNormalizedAppMetadata } from "./getNormalizedAppMetadata"; + +type RawAppStoreMetaData = typeof rawBookerAppsMetadata; +type AppStoreMetaData = { + [key in keyof RawAppStoreMetaData]: AppMeta; +}; + +export const appStoreMetadata = {} as AppStoreMetaData; +for (const [key, value] of Object.entries(rawBookerAppsMetadata)) { + appStoreMetadata[key as keyof typeof appStoreMetadata] = getNormalizedAppMetadata(value); +} diff --git a/packages/app-store/getNormalizedAppMetadata.ts b/packages/app-store/getNormalizedAppMetadata.ts new file mode 100644 index 0000000000..b3dec5fe78 --- /dev/null +++ b/packages/app-store/getNormalizedAppMetadata.ts @@ -0,0 +1,28 @@ +import type { AppMeta } from "@calcom/types/App"; + +// We have to import all the booker-apps config/metadata in here as without that we couldn't +import type { appStoreMetadata as rawAppStoreMetadata } from "./apps.metadata.generated"; +import { getAppAssetFullPath } from "./getAppAssetFullPath"; + +type RawAppStoreMetaData = typeof rawAppStoreMetadata; +type AppStoreMetaData = { + [key in keyof RawAppStoreMetaData]: AppMeta; +}; + +export const getNormalizedAppMetadata = (appMeta: RawAppStoreMetaData[keyof RawAppStoreMetaData]) => { + const dirName = "dirName" in appMeta ? appMeta.dirName : appMeta.slug; + if (!dirName) { + throw new Error(`Couldn't derive dirName for app ${appMeta.name}`); + } + const metadata = { + appData: null, + dirName, + __template: "", + ...appMeta, + } as AppStoreMetaData[keyof AppStoreMetaData]; + metadata.logo = getAppAssetFullPath(metadata.logo, { + dirName, + isTemplate: metadata.isTemplate, + }); + return metadata; +}; diff --git a/packages/app-store/locations.ts b/packages/app-store/locations.ts index cf92a5bdd0..0692043266 100644 --- a/packages/app-store/locations.ts +++ b/packages/app-store/locations.ts @@ -1,7 +1,7 @@ import type { TFunction } from "next-i18next"; import { z } from "zod"; -import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData"; +import { appStoreMetadata } from "@calcom/app-store/bookerAppsMetaData"; import logger from "@calcom/lib/logger"; import { BookingStatus } from "@calcom/prisma/enums"; import type { Ensure, Optional } from "@calcom/types/utils";