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
50 changes: 50 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ cli/
│ │ │ │ ├── resource.ts
│ │ │ │ └── index.ts
│ │ │ └── index.ts
│ │ ├── site/ # Site deployment (NOT a Resource)
│ │ │ ├── schema.ts # DeployResponse Zod schema
│ │ │ ├── config.ts # getSiteFilePaths() - glob files for validation
│ │ │ ├── api.ts # uploadSite() - reads archive, sends to API
│ │ │ ├── deploy.ts # deploySite() - validates, creates tar.gz, uploads
│ │ │ └── index.ts
│ │ ├── utils/
│ │ │ ├── fs.ts # File system utilities
│ │ │ └── index.ts
Expand All @@ -78,6 +84,8 @@ cli/
│ │ │ └── create.ts
│ │ └── entities/
│ │ └── push.ts
│ │ └── site/
│ │ └── deploy.ts
│ ├── utils/
│ │ ├── runCommand.ts # Command wrapper with branding
│ │ ├── runTask.ts # Spinner wrapper
Expand Down Expand Up @@ -231,6 +239,48 @@ export const entityResource: Resource<Entity> = {
8. Register in `project/config.ts` (add to `readProjectConfig`)
9. Add typed field to `ProjectData` interface

## Site Module

The site module (`src/core/site/`) handles deploying built frontend files to Base44 hosting. Unlike Resources, the site module:

- Reads built artifacts (JS, CSS, HTML) from the output directory
- Gets configuration from `site.outputDirectory` in project config
- Creates a tar.gz archive and uploads it to the API

### Architecture

```
site/
├── schema.ts # DeployResponse Zod schema
├── config.ts # getSiteFilePaths() - glob files for validation
├── api.ts # uploadSite() - reads archive, sends to API
├── deploy.ts # deploySite() - validates, creates archive, uploads
└── index.ts # Barrel exports
```

### Key Functions

```typescript
import { deploySite } from "@core/site/index.js";

// Deploy site from output directory (returns deployment details)
const { app_url, files_count } = await deploySite("./dist");
```

### Deploy Flow

1. Validate output directory exists and has files
2. Create temporary tar.gz archive using `tar` package
3. Upload archive to `POST /api/apps/{app_id}/deploy-dist`
4. Parse response with Zod schema
5. Clean up temporary archive file

### CLI Command

```bash
base44 site deploy
```

## Path Aliases

Single alias defined in `tsconfig.json`:
Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ base44 create

# 3. Push entities to Base44
base44 entities push

# 4. Build and deploy your site
npm run build
base44 site deploy
```

## Commands
Expand All @@ -49,6 +53,12 @@ base44 entities push
|---------|-------------|
| `base44 entities push` | Push local entity schemas to Base44 |

### Site Deployment

| Command | Description |
|---------|-------------|
| `base44 site deploy` | Deploy built site files to Base44 hosting |

## Configuration

### Project Configuration
Expand All @@ -61,7 +71,10 @@ Base44 projects are configured via a `config.jsonc` (or `config.json`) file in t
"id": "your-app-id", // Set after project creation
"name": "My Project",
"entitiesDir": "./entities", // Default: ./entities
"functionsDir": "./functions" // Default: ./functions
"functionsDir": "./functions", // Default: ./functions
"site": {
"outputDirectory": "../dist" // Path to built site files
}
}
```

Expand Down Expand Up @@ -91,6 +104,7 @@ my-project/
│ │ ├── user.jsonc
│ │ └── product.jsonc
├── src/ # Your frontend code
├── dist/ # Built site files (for deployment)
└── package.json
```

Expand Down
96 changes: 96 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@types/ejs": "^3.1.5",
"@types/lodash.kebabcase": "^4.1.9",
"@types/node": "^22.10.5",
"@types/tar": "^6.1.13",
"@typescript-eslint/eslint-plugin": "^8.51.0",
"@typescript-eslint/parser": "^8.51.0",
"chalk": "^5.6.2",
Expand All @@ -52,6 +53,7 @@
"ky": "^1.14.2",
"lodash.kebabcase": "^4.1.1",
"p-wait-for": "^6.0.0",
"tar": "^7.4.3",
"tsdown": "^0.12.4",
"tsx": "^4.19.2",
"typescript": "^5.7.2",
Expand Down
50 changes: 50 additions & 0 deletions src/cli/commands/site/deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { resolve } from "node:path";
import { Command } from "commander";
import { log, confirm, isCancel } from "@clack/prompts";
import { readProjectConfig } from "@core/project/index.js";
import { deploySite } from "@core/site/index.js";
import { runCommand, runTask } from "../../utils/index.js";

async function deployAction(): Promise<void> {
const { project } = await readProjectConfig();

if (!project.site?.outputDirectory) {
throw new Error(
"No site configuration found. Please add 'site.outputDirectory' to your config.jsonc"
);
}

const outputDir = resolve(project.root, project.site.outputDirectory);

const shouldDeploy = await confirm({
message: `Deploy site from ${project.site.outputDirectory}?`,
});

if (isCancel(shouldDeploy) || !shouldDeploy) {
log.warn("Deployment cancelled");
return;
}

const result = await runTask(
"Creating archive and deploying site...",
async () => {
return await deploySite(outputDir);
},
{
successMessage: "Site deployed successfully",
errorMessage: "Deployment failed",
}
);

log.success(`Site deployed to: ${result.app_url}`);
}

export const siteDeployCommand = new Command("site")
.description("Manage site deployments")
.addCommand(
new Command("deploy")
.description("Deploy built site files to Base44 hosting")
.action(async () => {
await runCommand(deployAction, { requireAuth: true });
})
);
4 changes: 4 additions & 0 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { whoamiCommand } from "./commands/auth/whoami.js";
import { logoutCommand } from "./commands/auth/logout.js";
import { entitiesPushCommand } from "./commands/entities/push.js";
import { createCommand } from "./commands/project/create.js";
import { siteDeployCommand } from "./commands/site/deploy.js";
import packageJson from "../../package.json";

const program = new Command();
Expand All @@ -28,5 +29,8 @@ program.addCommand(createCommand);
// Register entities commands
program.addCommand(entitiesPushCommand);

// Register site commands
program.addCommand(siteDeployCommand);

// Parse command line arguments
program.parse();
Loading