Skip to content
Open
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
8 changes: 6 additions & 2 deletions packages/cli/src/cli/dev/dev-server/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { createFunctionRuntime } from "@/cli/dev/dev-server/function-runtime.js"
import { createFunctionRouter } from "@/cli/dev/dev-server/routes/functions.js";
import { theme } from "@/cli/utils/index.js";
import type { ProjectData } from "@/core/project/types.js";
import { getSharedDir } from "@/core/resources/function/config.js";
import { Database } from "./db/database.js";
import {
type BroadcastEntityEvent,
Expand Down Expand Up @@ -205,9 +206,11 @@ export async function createDevServer(
broadcastEntityEvent(io, appId, entityName, event);
};

const functionsDir = join(dirname(project.configPath), project.functionsDir);
const base44ConfigWatcher = new WatchBase44(
{
functions: join(dirname(project.configPath), project.functionsDir),
functions: functionsDir,
shared: getSharedDir(functionsDir),
entities: join(dirname(project.configPath), project.entitiesDir),
},
devLogger,
Expand All @@ -216,7 +219,8 @@ export async function createDevServer(
try {
const { functions, entities } = await options.loadResources();

if (name === "functions") {
// Functions import base44/shared/, so a change there restarts them too.
if (name === "functions" || name === "shared") {
const previousFunctionCount = functionManager.getFunctionNames().length;
await functionManager.reload(functions);

Expand Down
7 changes: 6 additions & 1 deletion packages/cli/src/core/resources/function/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,18 @@ import type {
import { FunctionConfigSchema } from "@/core/resources/function/schema.js";
import { pathExists, readJsonFile } from "@/core/utils/fs.js";

/** The shared-code directory, `base44/shared/`: the sibling of the functions dir. */
export function getSharedDir(functionsDir: string): string {
return resolve(functionsDir, "..", "shared");
}

/**
* Collect every file under `base44/shared/`. These are uploaded alongside each
* function so shared modules imported via `../../shared/...` bundle server-side.
* The bundler tree-shakes anything a given function doesn't import.
*/
async function readSharedFiles(functionsDir: string): Promise<string[]> {
const sharedDir = resolve(functionsDir, "..", "shared");
const sharedDir = getSharedDir(functionsDir);
if (!(await pathExists(sharedDir))) {
return [];
}
Expand Down
30 changes: 30 additions & 0 deletions packages/cli/tests/cli/dev.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import jwt from "jsonwebtoken";
import { describe, expect, it } from "vitest";
import {
Expand Down Expand Up @@ -368,6 +370,34 @@ describe("dev command", () => {
});
});

it("serves a function importing base44/shared/ and reloads when it changes", async () => {
await t.givenLoggedInWithProject(fixture("with-shared-code"));

const handle = await t.runLive("dev");
const devServerUrl = await waitForDevServer(handle);
const callGreet = async () => {
const response = await fetch(
`${devServerUrl}/api/apps/${t.api.appId}/functions/greet`,
{ headers: { "X-App-Id": t.api.appId } },
);
expect(response.status).toBe(200);
return (await response.json()) as Record<string, unknown>;
};

expect(await callGreet()).toEqual({ greeting: "hello" });

await writeFile(
join(t.getTempDir(), "project", "base44", "shared", "greeting.ts"),
'export const GREETING = "changed";\n',
);
await handle.waitForOutput(/Reloaded functions: greet/, 10000);

expect(await callGreet()).toEqual({ greeting: "changed" });

const result = await handle.stop();
t.expectResult(result).toSucceed();
});

it("allows service-role JWTs to bypass denied entity create RLS", async () => {
await t.givenLoggedInWithProject(fixture("with-private-note-entity"));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Base44 App Configuration
{
"id": "test-app-id"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "Shared Code App"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { GREETING } from "../../shared/greeting.ts";

Deno.serve(() => Response.json({ greeting: GREETING }));
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const GREETING = "hello";
Loading