Skip to content

levii-pires/bunsai2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

82 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BunSai 2

Bonsai is a japanese art of growing and shaping miniature trees in containers.

Bun + Bonsai = BunSai

BunSai is a SSR Engine for Bun.

It leverages some of the powers of Bun (like Bundler, preloading and plugin system) to create a fast, DX friendly library.

NOTE: the full documentation is coming soon...

Known bugs

  • At the current version of Bun (v1.1.9) on Windows, the asset path on the browser version is not resolved correctly, throwing a hydration warning on React and completly breaking the asset path on Svelte. (Github issue)

Quick start

bun add bunsai@2 <the optional deps you are gonna use>

Adapters

Framework/API Example
Elysia adapters/elysia.ts
Hono adapters/hono.ts
Bun.serve adapters/manifest.ts
Bun.write adapters/file.ts
Byte adapters/byte.ts
Static Build Coming soon...

Web libraries and plugins

Currently BunSai supports Svelte and React. Check examples.

Svelte is a framework and has a compiler, thus having a higher level DX. That means you have to use plugins, write "Svelte code" and you have access to global injected variables.

On the other hand, React is a library and have a much lower level DX. Its just Typescript (or Javascript, but who uses plain JS these days?).

To avoid having a README full of information and difficult to understand, I will put all the information on the (coming soon) BunSai documentation web page.

Dev mode

To run on dev mode, just use Bun --hot flag and add:

# .env
# possible values: 'on', 'verbose' or 'silent'
DEBUG=on

NOTE: Browser HMR is not yet supported

BunSai entrypoint

BunSai has an entrypoint (a function) that must be called after all modules are imported and before begining the use of those modules.

import A from "./module-a";
import B from "./module-b";
import C from "./module-c";

import bunsai from "bunsai";
// or
import { bunsai } from "bunsai/<adapter>";

const result = await bunsai(/* config here */);

/* your code here */

BunSai also offers a convenience tool:

import "bunsai/with-config"; // WRONG
import A from "./module-a";
import B from "./module-b";
import "bunsai/with-config"; // WRONG
import C from "./module-c";

import "bunsai/with-config"; // RIGHT

/* your code here */
// bunsai.config.ts

import type { BunsaiConfig } from "bunsai";

const config: BunsaiConfig = {
  /* ... */
};

export default config;

Helpers

BunSai has (currently) 1 helper: asset.

Asset

No big deal, just a helper function to allow you to use Bun asset import across BunSai (both server and client side):

NOTE: Svelte plugin automatically injects it and makes it available module scope as asset. You can opt out by setting SvelteConfig.bunsai2.useAsset to false.

import createAssetGetter from "bunsai/asset";
import myPng from "./asset.png";

const asset = createAssetGetter(import.meta);

asset(myPng);

NOTE: If your asset is imported by only one file, use asset.abs() instead.

How it works

BunSai is divided in 3 parts: Plugin, Adapter and Module.

Plugin

A Plugin is the part of BunSai that converts things like Svelte code into a BunSai Module.

Let's take the builtin Svelte Plugin as an example:

# bunfig.toml
preload = ["bunsai/svelte/preload.ts"]
// tsconfig.json
{
  // Svelte global references 'bunsai/global.d.ts',
  // so if you are not using Svelte, you should include it yourself
  "types": ["bunsai/svelte/global.d.ts"]
}

NOTE: if you use the Svelte Extension for VSCode, make sure the Enable-TS-plugin option is disabled

NOTE: The Svelte plugin uses config file (svelte.config.ts). For proper typing, please use the global SvelteConfig type

NOTE: React, being a library, has an library approach. That means no plugin.

Adapter

As the name implies, an adapter adapts the BunSai Result (a.k.a. build) to be consumed by another Framework/API.

This time we'll take the Elysia adapter:

import { plug, plugged, bunsai, Elysia } from "bunsai/elysia";
import { render } from "./module.svelte";

const result = await bunsai();
// or
import "bunsai/with-config";

/* 
  If you are going to use 'bunsai/with-config',
  the adapter will try to get the result from the global scope.
  You dont even need to store the 'bunsai()' returned value,
  nor pass it to the adapter entrypoint ('plug' in this case).
*/

/*
  Or you can use 'plugged' and "ignore" both 'bunsai()' and 'bunsai/with-config'
*/
await plugged();

// as plugin
const app = new Elysia().use(plug(result)).get("/", render).listen(3000);

// as the main instance
const app = plug(result).get("/", render).listen(3000);

console.log("Elysia Ready!");

Module

The module is just an Svelte file (in our case). It is the plugin's sole responsibility to transpile/adapt the source code into a BunSai Module.

The Svelte plugin uses svelte/compiler to transpile our source code into vanilla javascript, then it injects BunSai's register function and makes changes to exports so it can be consumed as an out-of-the-box BunSai Module.

Check the StandaloneModule interface