diff --git a/src/emulator/apphosting/serve.ts b/src/emulator/apphosting/serve.ts index f894cb99fe9..de8eedb102f 100644 --- a/src/emulator/apphosting/serve.ts +++ b/src/emulator/apphosting/serve.ts @@ -5,14 +5,13 @@ import { isIPv4 } from "net"; import { checkListenable } from "../portUtils"; -import { PackageManager, discoverPackageManager } from "./utils"; +import { detectStartCommand } from "./utils"; import { DEFAULT_HOST, DEFAULT_PORTS } from "../constants"; -import { spawnWithCommandString, wrapSpawn } from "../../init/spawn"; +import { spawnWithCommandString } from "../../init/spawn"; import { logger } from "./utils"; import { Emulators } from "../types"; import { getLocalAppHostingConfiguration } from "./config"; import { resolveProjectPath } from "../../projectPath"; -import { FirebaseError } from "../../error"; interface StartOptions { startCommand?: string; @@ -69,21 +68,9 @@ async function serve( return; } - let packageManager: PackageManager = "npm"; - try { - packageManager = await discoverPackageManager(backendRoot); - } catch (e) { - throw new FirebaseError( - "Failed to detect your project's package manager, consider manually setting the start command with the `startCommandOverride` config. ", - ); - } - - logger.logLabeled( - "BULLET", - Emulators.APPHOSTING, - `starting app with: '${packageManager} run dev'`, - ); - await wrapSpawn(packageManager, ["run", "dev"], backendRoot, environmentVariablesToInject); + const detectedStartCommand = await detectStartCommand(backendRoot); + logger.logLabeled("BULLET", Emulators.APPHOSTING, `starting app with: '${detectStartCommand}`); + await spawnWithCommandString(detectedStartCommand, backendRoot, environmentVariablesToInject); } function availablePort(host: string, port: number): Promise { diff --git a/src/emulator/apphosting/utils.ts b/src/emulator/apphosting/utils.ts index ab8b087c6ba..cf0c22feae0 100644 --- a/src/emulator/apphosting/utils.ts +++ b/src/emulator/apphosting/utils.ts @@ -16,7 +16,7 @@ export type PackageManager = "npm" | "yarn" | "pnpm"; * @param rootdir project's root directory * @returns PackageManager */ -export async function discoverPackageManager(rootdir: string): Promise { +async function detectPackageManager(rootdir: string): Promise { if (await pathExists(join(rootdir, "pnpm-lock.yaml"))) { return "pnpm"; } @@ -31,3 +31,16 @@ export async function discoverPackageManager(rootdir: string): Promise Promise | null>; -} = { - apphosting: async () => { - // Auto-detect package manager and set startCommandOverride +type InitFn = () => Promise | null>; +type AdditionalInitFnsType = Partial>; + +export const AdditionalInitFns: AdditionalInitFnsType = { + [Emulators.APPHOSTING]: async () => { + const additionalConfigs = new Map(); const logger = EmulatorLogger.forEmulator(Emulators.APPHOSTING); logger.log("BULLET", "Initializing App Hosting Emulator"); + // get root directory const rootDirectory = await promptOnce({ name: "rootDir", type: "input", default: "./", message: "Specify your app's root directory relative to your repository", }); + additionalConfigs.set("rootDirectory", rootDirectory); + + // Auto-detect package manager and set startCommandOverride + // TODO: don't use cwd, instead try to find project root + const backendRoot = process.cwd(); + try { + const startCommand = await detectStartCommand(backendRoot); + additionalConfigs.set("startCommandOverride", startCommand); + } catch (e) { + logger.log( + "WARN", + "failed to auto-detect your project's start command, consider manually setting the start command by setting the startCommandOverride config", + ); + } - return { - rootDirectory, - }; // prompt for apphosting yaml to export - }, - auth: async () => { - return null; - }, - hub: async () => { - return null; - }, - functions: async () => { - return null; - }, - firestore: async () => { - return null; - }, - database: async () => { - return null; - }, - hosting: async () => { - return null; - }, - pubsub: async () => { - return null; - }, - ui: async () => { - return null; - }, - logging: async () => { - return null; - }, - storage: async () => { - return null; - }, - extensions: async () => { - return null; - }, - eventarc: async () => { - return null; - }, - dataconnect: async () => { - return null; - }, - tasks: async () => { - return null; + + return mapToObject(additionalConfigs); }, }; + +function mapToObject(map: Map): Record { + let newObject: Record = {}; + for (let [key, value] of map) { + newObject[key] = value; + } + return newObject; +} diff --git a/src/init/features/emulators.ts b/src/init/features/emulators.ts index 278493d9fee..2ac7a8ad628 100644 --- a/src/init/features/emulators.ts +++ b/src/init/features/emulators.ts @@ -56,12 +56,15 @@ export async function doSetup(setup: Setup, config: any) { } //TODO: Can add some logic here where each emulator can run their own inits - const additionalOptions = await AdditionalInitFns[selected](); - if (additionalOptions) { - setup.config.emulators[selected] = { - ...setup.config.emulators[selected], - ...additionalOptions, - }; + const additionalInitFn = AdditionalInitFns[selected]; + if (additionalInitFn) { + const additionalOptions = await additionalInitFn(); + if (additionalOptions) { + setup.config.emulators[selected] = { + ...setup.config.emulators[selected], + ...additionalOptions, + }; + } } }