BunSai
Bonsai is a japanese art of growing and shaping miniature trees in containers
Quick start
BunSai is a full-stack agnostic framework for the web, built upon Bun and Elysia. You can install it:
bun add bunsai
And use it as a command:
bun run bunsai
How it works?
Powered by Bun.build
, Elysia's routing system and some fancy tricks, BunSai takes an approach where each plugin declares which file extensions they want to work with. Then the app
folder is scanned using new Bun.Glob('app/**/*{<extensions>}')
. The matched files pass through building
and scripting
both for Bun and for the web. Finally, BunSai generates a script that exports an Elysia instance. You can consume it like so:
import plugin from "./.bunsai";
new Elysia().use(await plugin({ name: "BunSai" }));
(await plugin()).listen(3000);
Rules
Most of the rules are dictated by the plugins, but there are a few "global" rules.
CLI
The bunsai
command requires a file named bunsai.config.ts
to be placed at cwd.
Example:
import { type BunSaiArgs, staticPlugin, elysiaPlugin } from "bunsai";
import sveltePlugin from "@bunsai/svelte";
import typescript from "@bunsai/svelte/preprocessors/typescript";
import postcss from "@bunsai/svelte/preprocessors/postcss";
const config: BunSaiArgs = {
programEntrypoint: "./test.ts",
plugins: [
sveltePlugin([typescript(), postcss()]),
staticPlugin([".txt", ".ico", ".webp"]),
elysiaPlugin(),
],
};
export default config;
Dynamic path
- Path parameters must use
[foo]
sintax (e.g. app/[foo].svelte
) - Wildcards must use
[...]
syntax (e.g. app/bar/[...].svelte
)
Index
The index
keyword is ommited from the route path, so /foo/index.ts
becomes /foo
. This is also true for /bar/index/baz.ts
=> /bar/baz
.
Hidden resources
You can hide a folder or a file by prepending a dot on the filename. This is due to glob behaviour, where any file or folder that starts with a dot (e.g. .lib
) is ignored.
You can also hide a resource using the globHideToken
option.
Builtin plugins
Elysia
You can have an Elysia-compatible route within BunSai.
Usage:
import { elysiaPlugin, type BunSaiArgs } from "bunsai";
const config: BunSaiArgs = {
plugins: [elysiaPlugin()],
};
export function GET(ctx: ElysiaContext) {}
export function POST(ctx: ElysiaContext) {}
GET.hook = {};
export const WS = {
message() {},
};
Static
You can declare static files to be served by file extension under assetsPath
.
Usage:
import { staticPlugin, type BunSaiArgs } from "bunsai";
const config: BunSaiArgs = {
assetsPath: "/assets/",
plugins: [staticPlugin([".txt", ".ico", ".webp"])],
};
API
The API documentation is a work in progress and will likely be published alongside the official website.
The code itself is documented, but I intend to make improvements to the documentation over time, as well as the README.