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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
- `base44 deploy` (and `base44 site deploy`) can now build first: interactive runs ask, and `--build` / `--no-build` pre-answer the prompt.
- `base44 dev --remote` serves the frontend against the production backend: it runs `site.serveCommand` with `VITE_BASE44_APP_ID` and `VITE_BASE44_APP_BASE_URL` pointing at the app's own published URL, without starting the local backend. Fails if the app has no published URL.

### Changed

- The `backend-and-client` template now scaffolds the same client convention editor-created apps use: `@base44/vite-plugin` + `src/lib/app-params.js`, with the SDK client on same-origin `/api` (`serverUrl: ''`). Under `base44 dev` the plugin proxies `/api` to the local dev backend, so scaffolded apps get local entities and functions; the app id is injected via `VITE_BASE44_APP_ID` by `base44 dev`, `base44 dev --remote`, and the build/deploy commands instead of being baked into source.

### Fixed

- `base44 link` now lists editor-created apps; previously only apps created by the CLI could be linked.
Expand Down
6 changes: 5 additions & 1 deletion packages/cli/src/cli/commands/project/scaffold-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ export async function completeProjectSetup(
await execa({ cwd: resolvedPath, shell: true })`${installCommand}`;

updateMessage("Building project...");
await execa({ cwd: resolvedPath, shell: true })`${buildCommand}`;
await execa({
cwd: resolvedPath,
shell: true,
env: { VITE_BASE44_APP_ID: projectId },
})`${buildCommand}`;

updateMessage("Deploying site...");
return await deploySite(join(resolvedPath, outputDirectory));
Expand Down
10 changes: 8 additions & 2 deletions packages/cli/src/core/site/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ export async function deploySite(
`Output directory does not exist: ${siteOutputDir}. Make sure to build your project first.`,
{
hints: [
{ message: "Run your build command (e.g., 'npm run build') first" },
{
message:
"Run 'base44 build' first (it injects your app id; a bare 'npm run build' does not)",
},
],
},
);
Expand All @@ -29,7 +32,10 @@ export async function deploySite(
siteOutputDir,
{
hints: [
{ message: "Run your build command (e.g., 'npm run build') first" },
{
message:
"Run 'base44 build' first (it injects your app id; a bare 'npm run build' does not)",
},
],
},
);
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/templates/backend-and-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"preview": "vite preview"
},
"dependencies": {
"@base44/sdk": "^0.8.3",
"@base44/sdk": "^0.8.40",
"@base44/vite-plugin": "^1.0.30",
"lucide-react": "^0.475.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
Expand Down
12 changes: 12 additions & 0 deletions packages/cli/templates/backend-and-client/src/api/base44Client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createClient } from '@base44/sdk';
import { appParams } from '@/lib/app-params';

const { appId, token, functionsVersion, appBaseUrl } = appParams;

export const base44 = createClient({
appId,
token,
functionsVersion,
serverUrl: '',
appBaseUrl
});

This file was deleted.

28 changes: 28 additions & 0 deletions packages/cli/templates/backend-and-client/src/lib/app-params.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { getAccessToken } from '@base44/sdk';

const isNode = typeof window === 'undefined';

const isClearAccessTokenRequested = () =>
!isNode && new URLSearchParams(window.location.search).get("clear_access_token") === 'true';

const clearStoredAccessToken = () => {
window.localStorage.removeItem('base44_access_token');
window.localStorage.removeItem('token');
}

const getAppParams = () => {
if (isClearAccessTokenRequested()) {
clearStoredAccessToken();
}
return {
appId: import.meta.env.VITE_BASE44_APP_ID,
token: getAccessToken(),
Comment thread
davidsu marked this conversation as resolved.
functionsVersion: import.meta.env.VITE_BASE44_FUNCTIONS_VERSION,
Comment thread
davidsu marked this conversation as resolved.
appBaseUrl: import.meta.env.VITE_BASE44_APP_BASE_URL,
}
}


export const appParams = {
...getAppParams()
}
11 changes: 3 additions & 8 deletions packages/cli/templates/backend-and-client/vite.config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import { defineConfig } from 'vite';
import base44 from '@base44/vite-plugin';
import react from '@vitejs/plugin-react';
import path from 'path';
import { defineConfig } from 'vite';

export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
plugins: [base44(), react()],
});
46 changes: 46 additions & 0 deletions packages/cli/tests/cli/create.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,52 @@ describe("create command", () => {
expect(config).toContain('"visibility": "public"');
});

it("scaffolds backend-and-client with the editor-app client convention", async () => {
await t.givenLoggedIn({ email: "test@example.com", name: "Test User" });
t.api.mockCreateApp({ id: "convention-app-id", name: "Convention App" });

const projectPath = join(t.getTempDir(), "convention-app");

const result = await t.run(
"create",
"Convention App",
"--path",
projectPath,
"--template",
"backend-and-client",
"--no-skills",
);

t.expectResult(result).toSucceed();

const client = await readFile(
join(projectPath, "src", "api", "base44Client.js"),
"utf-8",
);
expect(client).toContain("serverUrl: ''");
expect(client).toContain("@/lib/app-params");
expect(client).not.toContain("convention-app-id");

const appParams = await readFile(
join(projectPath, "src", "lib", "app-params.js"),
"utf-8",
);
expect(appParams).toContain("VITE_BASE44_APP_ID");
expect(appParams).toContain("VITE_BASE44_APP_BASE_URL");

const viteConfig = await readFile(
join(projectPath, "vite.config.js"),
"utf-8",
);
expect(viteConfig).toContain("@base44/vite-plugin");

const appConfig = await readFile(
join(projectPath, "base44", ".app.jsonc"),
"utf-8",
);
expect(appConfig).toContain("convention-app-id");
});

it("infers path from name when --path is not provided", async () => {
await t.givenLoggedIn({ email: "test@example.com", name: "Test User" });
t.api.mockCreateApp({ id: "inferred-path-id", name: "My App" });
Expand Down