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
12 changes: 12 additions & 0 deletions .editorconfig
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will help automatically to deal with lint errors regarding "non empty last line in file"

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
root = true

[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf

[*.{md,ts.snap}]
trim_trailing_whitespace = false
3 changes: 3 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"recommendations": ["biomejs.biome"]
}
Comment on lines +1 to +3
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

plugin needed for correct work.
Without it nothing will correctly happened and it will be the same experience as before - only lints when you run bun run lint:fix

16 changes: 16 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
{
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.biome": "explicit",
"source.organizeImports.biome": "explicit"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[json]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[jsonc]": {
"editor.defaultFormatter": "biomejs.biome"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed it last time, should be explicit

}
}
668 changes: 421 additions & 247 deletions bun.lock

Large diffs are not rendered by default.

51 changes: 37 additions & 14 deletions infra/build.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { watch } from "node:fs";
import { copyFile } from "node:fs/promises";
import { join } from "node:path";
import chalk from "chalk";
import { BuildConfig } from "bun";

const runBuild = async () => {
const result = await Bun.build({
entrypoints: ["./src/cli/index.ts"],
outdir: "./dist/cli",
const runBuild = async (config: BuildConfig) => {
const defaultBuildOptions: Partial<BuildConfig> = {
target: "node",
format: "esm",
sourcemap: "external",
};

const result = await Bun.build({
...defaultBuildOptions,
...config,
});

if (!result.success) {
Expand All @@ -21,26 +27,43 @@ const runBuild = async () => {
return result;
};

const runAllBuilds = async () => {
const cli = await runBuild({
entrypoints: ["./src/cli/index.ts"],
outdir: "./dist/cli",
});
return {
cli,
};
};

const formatOutput = (outputs: { path: string }[]) => {
return outputs.map((o) => chalk.cyan(o.path)).join("\n ");
};

if (process.argv.includes("--watch")) {
console.log(chalk.yellow("Watching for changes..."));

const changeHandler = async (event: "rename" | "change", filename: string | null) => {
const changeHandler = async (
event: "rename" | "change",
filename: string | null
) => {
const time = new Date().toLocaleTimeString();
console.log(chalk.dim(`[${time}]`), chalk.gray(`${filename} ${event}d`));

const result = await runBuild();
console.log(
chalk.green(` ✓ Rebuilt`),
chalk.dim(`→`),
formatOutput(result.outputs)
);
const { cli } = await runAllBuilds();
for (const result of [cli]) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a preparation for additional entry

if (result.success && result.outputs.length > 0) {
console.log(
chalk.green(` ✓ Rebuilt`),
chalk.dim(`→`),
formatOutput(result.outputs)
);
}
}
};

await runBuild();
await runAllBuilds();

for (const dir of ["./src"]) {
watch(dir, { recursive: true }, changeHandler);
Expand All @@ -49,8 +72,8 @@ if (process.argv.includes("--watch")) {
// Keep process alive
await new Promise(() => {});
} else {
const result = await runBuild();
const { cli } = await runAllBuilds();
console.log(chalk.green.bold(`\n✓ Build complete\n`));
console.log(chalk.dim(" Output:"));
console.log(` ${formatOutput(result.outputs)}\n`);
console.log(` ${formatOutput(cli.outputs)}`);
}
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@
"@clack/prompts": "^0.11.0",
"@types/bun": "^1.2.15",
"@types/common-tags": "^1.8.4",
"@types/cors": "^2.8.19",
"@types/ejs": "^3.1.5",
"@types/express": "^5.0.6",
"@types/json-schema": "^7.0.15",
"@types/lodash.kebabcase": "^4.1.9",
"@types/node": "^22.10.5",
Expand All @@ -47,10 +49,14 @@
"chalk": "^5.6.2",
"commander": "^12.1.0",
"common-tags": "^1.8.2",
"cors": "^2.8.5",
"ejs": "^3.1.10",
"execa": "^9.6.1",
"express": "^5.0.1",
"front-matter": "^4.0.2",
"get-port": "^7.1.0",
"globby": "^16.1.0",
"http-proxy-middleware": "^3.0.5",
"json-schema-to-typescript": "^15.0.4",
"json5": "^2.2.3",
"ky": "^1.14.2",
Expand Down
31 changes: 31 additions & 0 deletions src/cli/commands/dev.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Command } from "commander";
import { createDevServer } from "@/cli/dev/dev-server/main";
import { runCommand, theme } from "@/cli/utils/index.js";
import type { RunCommandResult } from "@/cli/utils/runCommand.js";
import type { CLIContext } from "../types.js";

interface DevOptions {
port?: string;
}

async function devAction(options: DevOptions): Promise<RunCommandResult> {
const port = options.port ? Number(options.port) : undefined;
const { port: resolvedPort } = await createDevServer({ port });

return {
outroMessage: `Dev server is available at ${theme.colors.links(`http://localhost:${resolvedPort}`)}`,
};
}

export function getDevCommand(context: CLIContext): Command {
return new Command("dev")
.description("Start the development server")
.option("-p, --port <number>", "Port for the development server")
.action(async (options: DevOptions) => {
await runCommand(
() => devAction(options),
{ requireAuth: true },
context
);
});
}
73 changes: 73 additions & 0 deletions src/cli/dev/dev-server/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import type { Server } from "node:http";
import cors from "cors";
import express from "express";
import getPort from "get-port";
import { createProxyMiddleware } from "http-proxy-middleware";

const DEFAULT_PORT = 4400;
const BASE44_APP_URL = "https://base44.app";

interface DevServerOptions {
port?: number;
}

interface DevServerResult {
port: number;
server: Server;
}

export async function createDevServer(
options: DevServerOptions = {}
): Promise<DevServerResult> {
const port = options.port ?? (await getPort({ port: DEFAULT_PORT }));

const app = express();

const remoteProxy = createProxyMiddleware({
target: BASE44_APP_URL,
changeOrigin: true,
});

app.use(
cors({
origin: /^http:\/\/localhost(:\d+)?$/,
credentials: true,
})
);

// Redirect OAuth routes to base44.app directly — proxying breaks the
// redirect flow and session cookies set by the provider.
const AUTH_ROUTE_PATTERN = /^\/api\/apps\/auth(\/|$)/;
app.use((req, res, next) => {
if (AUTH_ROUTE_PATTERN.test(req.path)) {
const targetUrl = new URL(req.originalUrl, BASE44_APP_URL);
return res.redirect(targetUrl.toString());
}
next();
});

app.use((req, res, next) => {
return remoteProxy(req, res, next);
});

return new Promise((resolve, reject) => {
const server = app.listen(port, "127.0.0.1", (err) => {
if (err) {
if ("code" in err && err.code === "EADDRINUSE") {
reject(
new Error(
`Port ${port} is already in use. Stop the other process and try again.`
)
);
} else {
reject(err);
}
} else {
resolve({
port,
server,
});
}
});
});
Comment on lines 53 to 72
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case of error it will look like this:

Image

end process will exit

}
4 changes: 4 additions & 0 deletions src/cli/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { getLinkCommand } from "@/cli/commands/project/link.js";
import { getSiteCommand } from "@/cli/commands/site/index.js";
import { getTypesCommand } from "@/cli/commands/types/index.js";
import packageJson from "../../package.json";
import { getDevCommand } from "./commands/dev.js";
import { getEjectCommand } from "./commands/project/eject.js";
import type { CLIContext } from "./types.js";

Expand Down Expand Up @@ -56,5 +57,8 @@ export function createProgram(context: CLIContext): Command {
// Register types command
program.addCommand(getTypesCommand(context), { hidden: true });

// Register development commands
program.addCommand(getDevCommand(context), { hidden: true });

return program;
}
Loading