Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 63 additions & 13 deletions packages/cli/src/cli/commands/dev.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type { Command } from "commander";
import { createDevLogger } from "@/cli/dev/createDevLogger.js";
import { createDevServer } from "@/cli/dev/dev-server/main.js";
import type { ServeRunner } from "@/cli/dev/dev-server/serve-runner.js";
import { createServeCommandRunner } from "@/cli/dev/serve-command-runner.js";
import type { CLIContext, RunCommandResult } from "@/cli/types.js";
import { type AppIdOptions, Base44Command, theme } from "@/cli/utils/index.js";
import { getDenoWrapperPath } from "@/core/assets.js";
Expand All @@ -12,6 +15,11 @@ interface DevOptions {
port?: string;
}

interface LinkedApp {
id: string;
projectRoot: string;
}

function localServerUrl(port: number): string {
return `http://localhost:${port}`;
}
Expand All @@ -25,36 +33,78 @@ function validateDevOptions(command: Command): void {
}
}

async function devAction(
ctx: CLIContext,
options: DevOptions,
): Promise<RunCommandResult> {
const { log, app } = ctx;
function requireLinkedProject({ app }: CLIContext): LinkedApp {
if (!app?.projectRoot) {
throw new ConfigInvalidError(
"base44 dev requires a linked local project. Run it from a project with base44/.app.jsonc.",
);
}
return { id: app.id, projectRoot: app.projectRoot };
}

async function createConfiguredServeRunner(
app: LinkedApp,
backendUrl: string,
): Promise<ServeRunner | undefined> {
const { project } = await readProjectConfig(app.projectRoot);
const serveCommand = project.site?.serveCommand;
if (!serveCommand) {
return undefined;
}
return createServeCommandRunner({
serveCommand,
projectRoot: project.root,
appId: app.id,
appBaseUrl: backendUrl,
});
}

const port = options.port ? Number(options.port) : undefined;
const appId = app.id;
function startServeCommand(
runner: ServeRunner,
backend: { url: string; shutdown: () => Promise<void> },
): void {
const stop = () => void runner.stop();
process.on("SIGINT", stop);
process.on("SIGTERM", stop);

// If the frontend dies, tear the whole dev environment down.
runner.onExit(() => {
void backend.shutdown().finally(() => process.exit(1));
});

createDevLogger("backend", theme.styles.info).log(
`Backend running on ${backend.url}`,
);
runner.start();
}

async function devAction(
ctx: CLIContext,
options: DevOptions,
): Promise<RunCommandResult> {
const app = requireLinkedProject(ctx);
const siteUrlPromise = getSiteUrl().catch(() => undefined);

const { port: resolvedPort, isServingFrontend } = await createDevServer({
log,
port,
appId,
const backend = await createDevServer({
log: ctx.log,
port: options.port ? Number(options.port) : undefined,
denoWrapperPath: getDenoWrapperPath(),
loadResources: async () => {
const { functions, entities, project } = await readProjectConfig();
const siteUrl = await siteUrlPromise;
return { functions, entities, project, siteUrl };
},
});
const backendUrl = localServerUrl(backend.port);

const runner = await createConfiguredServeRunner(app, backendUrl);
if (runner) {
startServeCommand(runner, { url: backendUrl, shutdown: backend.shutdown });
}

const outroMessage = isServingFrontend
const outroMessage = runner
? "Open your app using the frontend dev server URL"
: `Dev server is available at ${theme.colors.links(localServerUrl(resolvedPort))}`;
: `Dev server is available at ${theme.colors.links(backendUrl)}`;

return { outroMessage };
}
Expand Down
33 changes: 2 additions & 31 deletions packages/cli/src/cli/dev/dev-server/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
createFileToken,
createIntegrationRoutes,
} from "./routes/integrations.js";
import { ServeRunner } from "./serve-runner.js";
import { WatchBase44 } from "./watcher.js";

const DEFAULT_PORT = 4400;
Expand All @@ -34,7 +33,6 @@ interface DevServerOptions {
log: Logger;
port?: number;
denoWrapperPath: string;
appId?: string;
loadResources: () => Promise<{
functions: ProjectData["functions"];
entities: ProjectData["entities"];
Expand All @@ -46,7 +44,7 @@ interface DevServerOptions {
interface DevServerResult {
port: number;
server: Server;
isServingFrontend: boolean;
shutdown: () => Promise<void>;
}

export async function createDevServer(
Expand Down Expand Up @@ -245,22 +243,6 @@ export async function createDevServer(
});
await base44ConfigWatcher.start();

// Run the frontend dev server when the project configures a `site.serveCommand`
// and we have an app id to inject. It runs from the project root.
const serveCommand = project.site?.serveCommand;
let serveRunner: ServeRunner | undefined;
if (options.appId && serveCommand) {
serveRunner = new ServeRunner({
command: serveCommand,
cwd: project.root,
env: {
VITE_BASE44_APP_ID: options.appId,
VITE_BASE44_APP_BASE_URL: baseUrl,
},
logger: createDevLogger("frontend", theme.colors.base44Orange),
});
}

const handleShutdownError = (error: unknown) => {
const errorMessage = error instanceof Error ? error.message : String(error);
devLogger.error(`Failed to shut down dev server: ${errorMessage}`);
Expand All @@ -287,7 +269,6 @@ export async function createDevServer(
base44ConfigWatcher.close();
await io.close();
await functionManager.stopAll();
await serveRunner?.stop();
await closeServerIfRunning();
};

Expand All @@ -299,15 +280,5 @@ export async function createDevServer(
process.on("SIGINT", shutdown);
process.on("SIGTERM", shutdown);

// If the frontend dies, tear the whole dev environment down.
serveRunner?.onExit(() => {
void shutdown().finally(() => process.exit(1));
});

if (serveRunner) {
devLogger.log(`Backend running on ${baseUrl}`);
serveRunner.start();
}

return { port, server, isServingFrontend: serveRunner !== undefined };
return { port, server, shutdown };
}
27 changes: 27 additions & 0 deletions packages/cli/src/cli/dev/serve-command-runner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { createDevLogger } from "@/cli/dev/createDevLogger.js";
import { ServeRunner } from "@/cli/dev/dev-server/serve-runner.js";
import { theme } from "@/cli/utils/index.js";

interface ServeCommandRunnerOptions {
serveCommand: string;
projectRoot: string;
appId: string;
appBaseUrl: string;
}

export function createServeCommandRunner({
serveCommand,
projectRoot,
appId,
appBaseUrl,
}: ServeCommandRunnerOptions): ServeRunner {
return new ServeRunner({
command: serveCommand,
cwd: projectRoot,
env: {
VITE_BASE44_APP_ID: appId,
VITE_BASE44_APP_BASE_URL: appBaseUrl,
},
logger: createDevLogger("frontend", theme.colors.base44Orange),
});
}
Loading