Socket
Socket
Sign inDemoInstall

bunsai

Package Overview
Dependencies
31
Maintainers
1
Versions
71
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.0.0-preview.7 to 2.0.0

LICENSE

15

package.json
{
"name": "bunsai",
"version": "2.0.0-preview.7",
"version": "2.0.0",
"module": "./src/core/index.ts",
"repository": "levii-pires/bunsai2",
"devDependencies": {
"@types/bun": "latest"
"@types/bun": "latest",
"@types/urijs": "^1.19.25"
},

@@ -52,4 +53,4 @@ "exports": {

},
"./map-router": {
"default": "./src/extra/map-router.ts"
"./asset": {
"default": "./src/extra/asset.ts"
}

@@ -64,3 +65,3 @@ },

"engines": {
"bun": ">=1.1.6"
"bun": "1.1.7"
},

@@ -71,3 +72,5 @@ "files": [

"type": "module",
"dependencies": {},
"dependencies": {
"urijs": "1.19.11"
},
"license": "MIT",

@@ -74,0 +77,0 @@ "keywords": [

175

README.md

@@ -1,3 +0,174 @@

# bunsai2
<p align="center"><img style="width: 25%" src="https://github.com/levii-pires/bunsai2/blob/main/assets/logo.png?raw=true"></p>
todo: create a nice README
<h1 align="center">BunSai 2</h1>
> Bonsai is a japanese art of growing and shaping miniature trees in containers.
>
> Bun + Bonsai = BunSai
BunSai proposes to be the official SSR engine for [Bun](https://bun.sh).
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...
## Quick start
```bash
bun add bunsai@2 <the optional deps you are gonna use>
```
| Framework/API | Example |
| --------------------------------------------------- | ------------------------------------------------------------- |
| [Elysia](https://elysiajs.com/) | [`adapters/elysia.ts`](./examples/src/adapters/elysia.ts) |
| [Hono](https://hono.dev/) | [`adapters/hono.ts`](./examples/src/adapters/hono.ts) |
| [Bun.serve](https://bun.sh/docs/api/http#bun-serve) | [`adapters/manifest.ts`](./examples/src/adapters/manifest.ts) |
| [Bun.write](https://bun.sh/docs/api/file-io) | [`adapters/file.ts`](./examples/src/adapters/file.ts) |
| [Byte](https://bytejs.pages.dev/) | `Coming soon...` |
| Static Build | `Coming soon...` |
### Dev mode
To run on dev mode, just use Bun `--hot` flag and add:
```properties
# .env
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.
```ts
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:
```ts
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 */
```
```ts
// 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`.
```ts
import createAssetGetter from "bunsai/asset";
import myPng from "./asset.png";
const asset = createAssetGetter(import.meta);
asset(myPng);
```
## 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](#module).
Let's take the builtin Svelte Plugin as an example:
```toml
# bunfig.toml
preload = ["bunsai/svelte/preload.ts"]
```
```jsonc
// 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
### 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:
```ts
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](./src/core/module.ts) interface

@@ -41,6 +41,6 @@ type Reserved<K extends string | number | symbol> = {

return {
body_attrs: objToAttrs(attrs.body_attrs || defaults.body_attrs || {}),
body_attrs: objToAttrs({ ...defaults.body_attrs, ...attrs.body_attrs }),
html_lang: attrs.html_lang || defaults.html_lang || "",
root_attrs: objToAttrs(attrs.root_attrs || defaults.root_attrs || {}),
root_attrs: objToAttrs({ ...defaults.root_attrs, ...attrs.root_attrs }),
};
}

@@ -18,5 +18,5 @@ // import { mkdir } from "fs/promises"; unused

const dumpFolder = mkdtempSync(join(tmpdir(), "bunsai-temp-"));
export const dumpFolder = mkdtempSync(join(tmpdir(), "bunsai-temp-"));
export async function buildClient(prefix: string) {
export async function buildClient(prefix: string, root?: string) {
Util.log.debug("creating client build...");

@@ -34,2 +34,3 @@

const { logs, outputs, success } = await Bun.build({
root,
entrypoints: files,

@@ -40,2 +41,5 @@ minify: !IsDev(),

target: "browser",
naming: {
asset: "[dir]/[name].[ext]",
},
outdir: dumpFolder,

@@ -42,0 +46,0 @@ });

@@ -0,0 +0,0 @@ import { createPath } from "./build";

declare namespace NodeJS {
export interface ProcessEnv {
DEBUG?: "on" | "verbose" | "silent";
BUNSAI_USE_CONFIG?: string;
// BUNSAI_USE_CONFIG?: string; not used
}
}
import type { BunSai } from ".";
import type { BunPlugin } from "bun";
import { createHolder, type Holder } from "./util";
import { ModuleSymbol as $ms } from "./module";

@@ -20,2 +21,2 @@ // to avoid type errors

export { ModuleSymbol } from "./module";
export const ModuleSymbol: symbol = ($global.$$$bunsai_module_symbol ||= $ms);

@@ -11,6 +11,12 @@ /// <reference path="./global.d.ts"/>

import { CurrentBunSai } from "./globals";
import { resolve } from "path";
export interface BunSai {
prefix: string;
root: string;
declarations: { path: string; handle: () => Response }[];
render(module: Module, context: Record<string, any>): Response;
render<Context extends Record<string, any>>(
module: Module<Context>,
context: Context
): Response;
}

@@ -20,2 +26,10 @@

/**
* Root folder where all your files will be placed.
*
* This is used on `Bun.build` to ensure the correct path resolution.
*
* @default process.cwd()
*/
root?: string;
/**
* Where the client build will be served.

@@ -34,9 +48,16 @@ *

const $global: any = global;
export default async function bunsai(
config: BunsaiConfig = {}
): Promise<BunSai> {
const { prefix = "/__bunsai__/", defaults } = config;
const { prefix = "/__bunsai__/", defaults, root = process.cwd() } = config;
const result = await buildClient(prefix);
// deps: extra/asset.ts
$global.$$$bunsai_build_root = resolve(root);
// deps: extra/asset.ts
$global.$$$bunsai_build_prefix = prefix;
const result = await buildClient(prefix, root);
if (!result) {

@@ -46,2 +67,4 @@ Util.log.loud("empty client endpoints. No module was registered");

const retorno = {
prefix,
root,
declarations: [],

@@ -68,2 +91,4 @@ render() {

const retorno: BunSai = {
prefix,
root,
render: (module, context) => {

@@ -105,3 +130,3 @@ const { $m_meta: meta, $m_render, $m_gen_script } = module;

Util.log.verbose("client endpoints (", paths.join(" | "), ")");
Util.log.debug("client endpoints (", paths.join(" | "), ")");

@@ -108,0 +133,0 @@ CurrentBunSai(retorno);

@@ -18,7 +18,7 @@ import type { BunPlugin } from "bun";

bun: BP;
browser: BunPlugin;
browser?: BunPlugin;
}) {
BrowserBuildPlugins.push(browser);
if (browser) BrowserBuildPlugins.push(browser);
ServerBuildPlugins.push(bun);
return Bun.plugin(bun);
}

@@ -6,5 +6,13 @@ import type { Attributes } from "./attrs";

export interface ModuleProps {
jsMap: object | undefined;
jsMap: object | null | undefined;
/**
* Module scoped css
*/
css: string | null;
cssHash: string | null;
/**
*
*/
cssHash: string;
cssMap: object | null | undefined;

@@ -16,4 +24,4 @@ path: string;

export type ModuleRenderer = (props: {
context: Record<string, any>;
export type ModuleRenderer<Context extends Record<string, any>> = (props: {
context: Context;
attrs: Attributes;

@@ -26,11 +34,21 @@ isServer: boolean;

export interface Module {
export interface Module<
Context extends Record<string, any> = Record<string, any>
> {
$m_meta: ModuleProps;
$m_symbol: typeof ModuleSymbol;
$m_render: ModuleRenderer;
$m_render: ModuleRenderer<Context>;
/**
* Client side hydration script tag generator
*/
$m_gen_script(data: ScriptData): string;
}
render: StandaloneRenderer;
export interface StandaloneModule<
Context extends Record<string, any> = Record<string, any>
> extends Module<Context> {
render: StandaloneRenderer<Context>;
}
export const ModuleSymbol = Symbol("bunsai.module");

@@ -0,0 +0,0 @@ import { Util } from "./util";

import type { Module } from "./module";
import { CurrentBunSai } from "./globals";
export type StandaloneRenderer = (context: Record<string, any>) => Response;
export type StandaloneRenderer<Context extends Record<string, any>> = (
context: Context
) => Response;
export const registry = new Map<string, Module>();
export const registry = new Map<string, Module<any>>();

@@ -15,3 +17,5 @@ class StandaloneRendererError extends Error {

*/
export function register(component: Module): StandaloneRenderer {
export function register<
Context extends Record<string, any> = Record<string, any>
>(component: Module<Context>): StandaloneRenderer<Context> {
registry.set(component.$m_meta.path, component);

@@ -22,3 +26,3 @@

if (!bunsai)
throw new StandaloneRendererError("cannot render before bunsai()");
throw new StandaloneRendererError("cannot render before 'bunsai()'");

@@ -25,0 +29,0 @@ return bunsai.render(component, context);

@@ -0,0 +0,0 @@ import type { ProcessedRenderAttrs } from "./attrs";

@@ -0,0 +0,0 @@ import type { ModuleRenderer } from "./module";

@@ -107,5 +107,6 @@ export namespace Util {

name = "BunSaiLoadError";
constructor(options?: ErrorOptions) {
constructor(options?: ErrorOptions, noProvide = false) {
super(
"Could not get current bunsai from globals, and you did not provide one",
"Could not get current bunsai from globals" +
(noProvide ? "." : ", and you did not provide one"),
options

@@ -112,0 +113,0 @@ );

@@ -11,5 +11,6 @@ import { resolve } from "path";

Util.log.debug(err);
Util.log.loud("Using BunSai with default settings");
} finally {
if (!config) Util.log.loud("Using BunSai with default settings");
}
await bunsai(config);
export default await bunsai(config);

@@ -5,2 +5,5 @@ import Elysia from "elysia";

/**
* @returns An Elysia plugin to resolve the assets and browser modules + the 'render' decorator
*/
export function plug(result = CurrentBunSai()) {

@@ -19,3 +22,13 @@ if (!result) throw new BunSaiLoadError();

/**
* Automatically call `bunsai/with-config`,
* then call {@link plug}
*/
export async function plugged() {
const { default: b } = await import("../core/with-config");
return plug(b);
}
export { default as Elysia } from "elysia";
export { default as bunsai } from "../core";

@@ -1,18 +0,12 @@

import { join } from "path";
import { CurrentBunSai } from "../core/globals";
import { BunSaiLoadError } from "../core/util";
import { cp } from "fs/promises";
import { dumpFolder } from "../core/build";
export async function writeToDisk(
rootFolder: string,
result = CurrentBunSai()
) {
export function writeToDisk(outFolder: string, result = CurrentBunSai()) {
if (!result) throw new BunSaiLoadError();
for (const decl of result.declarations) {
const path = join(rootFolder, decl.path);
await Bun.write(path, await decl.handle().arrayBuffer());
}
return cp(dumpFolder, outFolder, { recursive: true });
}
export { default as bunsai } from "../core";

@@ -1,5 +0,6 @@

import { Hono, type Context as HonoContext } from "hono";
import { Hono, type Env, type Context as HonoContext, type Schema } from "hono";
import type { Module } from "../core/module";
import { CurrentBunSai } from "../core/globals";
import { BunSaiLoadError } from "../core/util";
import type { BlankSchema } from "hono/types";

@@ -10,3 +11,9 @@ export interface BunSaiHono {

*/
hono(...args: ConstructorParameters<typeof Hono>): Hono;
hono<
E extends Env = Env,
S extends Schema = BlankSchema,
BasePath extends string = "/"
>(
...args: ConstructorParameters<typeof Hono<E, S, BasePath>>
): Hono<E, S, BasePath>;

@@ -19,3 +26,3 @@ /**

/**
* For a given Module, crates an Hono method handler.
* For a given Module, creates an Hono method handler.
*/

@@ -25,7 +32,14 @@ handler(module: Module): (context: HonoContext) => Response;

export function create(result = CurrentBunSai()) {
export function plug(result = CurrentBunSai()) {
if (!result) throw new BunSaiLoadError();
const retorno: BunSaiHono = {
hono(...args) {
hono: function <
E extends Env = Env,
S extends Schema = BlankSchema,
BasePath extends string = "/"
>(
...args: ConstructorParameters<typeof Hono<E, S, BasePath>>
): Hono<E, S, BasePath> {
// @ts-ignore
return retorno.apply(new Hono(...args));

@@ -47,3 +61,18 @@ },

/**
* Automatically call `bunsai/with-config`,
* then call {@link plug}
*/
export async function plugged() {
const { default: b } = await import("../core/with-config");
return plug(b);
}
/**
* @deprecated Use {@link plug} instead
*/
export const create = plug;
export { Hono } from "hono";
export { default as bunsai } from "../core";
import { CurrentBunSai } from "../core/globals";
import { BunSaiLoadError } from "../core/util";
export function createManifest(result = CurrentBunSai()) {
export function plug(result = CurrentBunSai()) {
if (!result) throw new BunSaiLoadError();

@@ -19,2 +19,17 @@

/**
* Automatically call `bunsai/with-config`,
* then call {@link plug}
*/
export async function plugged() {
const { default: b } = await import("../core/with-config");
return plug(b);
}
/**
* @deprecated Use {@link plug} instead
*/
export const createManifest = plug;
export { default as bunsai } from "../core";

@@ -168,2 +168,8 @@ import type {

overrideIsDev?: boolean;
/**
* Add `bunsai/asset` as global.
*
* @default true
*/
useAsset?: boolean;
};

@@ -170,0 +176,0 @@ }

@@ -0,7 +1,27 @@

/// <reference path="../../core/global.d.ts"/>
/// <reference path="../../core/global.d.ts"/>
/// <reference path="../../core/global.d.ts"/>
declare module "*.svelte" {
const mod: import("../../core/module").Module;
type SvelteModule = import("../../core/module").StandaloneModule;
export = mod;
const module: SvelteModule;
export = module;
}
declare type SvelteConfig = import("./config").Config;
/**
* Must **NOT** be used in `context="module"` script
*
* Converts Bun asset import into a BunSai compatible URL.
*
* NOTE: Unavailable if `SvelteConfig.bunsai2.useAsset` is set to `false`.
*
* @example
* import logo from "./assets/logo.png";
*
* asset(logo); // /__bunsai__/assets/logo.png
*/
declare var asset: import("../asset").Asset;

@@ -0,0 +0,0 @@ import { createHolder, type Holder } from "../../core/util";

/// <reference path="./global.d.ts" />
export type { Config as SvelteConfig } from "./config";

@@ -9,3 +9,8 @@ import type { BunPlugin } from "bun";

export default function createPlugins(svelteConfig: ResolvedSvelteConfig) {
const { extensions, preprocess, compilerOptions } = svelteConfig;
const {
extensions,
preprocess,
compilerOptions,
bunsai2: { useAsset },
} = svelteConfig;

@@ -56,3 +61,7 @@ const rxExtensions = extensions

'import { genScript as $$$sv_gen_script } from "bunsai/svelte/script.ts";\n' +
js +
(useAsset == false
? ""
: 'import $$$create_asset_getter from "bunsai/asset";\n' +
"const asset = $$$create_asset_getter(import.meta);\n") +
js.replace("export default", "") +
`\nconst _css = ${JSON.stringify(css)}, path = ${JSON.stringify(

@@ -71,3 +80,4 @@ args.path

"\nexport const $m_gen_script = $$$sv_gen_script;" +
"\nexport const render = $$$sv_reg({$m_meta,$m_render,$m_symbol,$m_gen_script})",
"\nexport const render = $$$sv_reg({$m_meta,$m_render,$m_symbol,$m_gen_script});" +
"\nexport default {$m_meta,$m_render,$m_symbol,$m_gen_script,render}",
loader: "js",

@@ -91,3 +101,3 @@ };

const {
js: { code: contents },
js: { code: js },
warnings,

@@ -107,3 +117,8 @@ } = svelte.compile(code, {

return {
contents,
contents:
useAsset == false
? js
: 'import $$$create_asset_getter from "bunsai/asset";\n' +
js +
"\nconst asset = $$$create_asset_getter(import.meta);",
loader: "js",

@@ -110,0 +125,0 @@ };

@@ -0,0 +0,0 @@ /// <reference path="./global.d.ts" />

@@ -0,0 +0,0 @@ import type { ScriptData } from "../../core/script";

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc