bunsai
Advanced tools
Comparing version 2.3.0 to 2.3.1
{ | ||
"name": "bunsai", | ||
"version": "2.3.0", | ||
"version": "2.3.1", | ||
"module": "./src/core/index.ts", | ||
@@ -5,0 +5,0 @@ "description": "SSR Engine for Bun", |
@@ -15,2 +15,6 @@ <p align="center"><img width="300rem" height="300rem" src="https://github.com/levii-pires/bunsai2/blob/main/assets/logo.png?raw=true"></p> | ||
## Known bugs | ||
- At the current version of Bun (v1.1.8) 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](https://github.com/oven-sh/bun/issues/11181)) | ||
## Quick start | ||
@@ -118,2 +122,4 @@ | ||
> NOTE: If your asset is imported by only one file, use `asset.abs()` instead. | ||
## How it works | ||
@@ -120,0 +126,0 @@ |
@@ -1,7 +0,6 @@ | ||
// import { mkdir } from "fs/promises"; unused | ||
// import { existsSync } from "fs"; unused | ||
import { getCSSArtifactPath } from "./css"; | ||
import { BrowserBuildPlugins, IsDev } from "./globals"; | ||
import { registry } from "./register"; | ||
import { Util } from "./util"; | ||
import { log, time } from "./util"; | ||
import { getSource } from "./source"; | ||
import { mkdtempSync } from "fs"; | ||
@@ -14,3 +13,3 @@ import { join, relative } from "path"; | ||
path: string; | ||
object: BuildArtifact | Blob; | ||
object: () => BuildArtifact | Blob; | ||
} | ||
@@ -31,3 +30,3 @@ | ||
): Promise<ClientBuild | undefined> { | ||
Util.log.debug("creating client build..."); | ||
log.debug("creating client build..."); | ||
@@ -37,7 +36,7 @@ const files = Array.from(registry.keys()); | ||
if (files.length == 0) { | ||
Util.log.debug("empty registry. Skiping build"); | ||
log.debug("empty registry. Skiping build"); | ||
return; | ||
} | ||
const cbt = Util.time.debug("client build time"); | ||
const cbt = time.debug("client build time"); | ||
@@ -54,2 +53,3 @@ const { logs, outputs, success } = await Bun.build({ | ||
}, | ||
sourcemap: "external", | ||
outdir: dumpFolder, | ||
@@ -64,27 +64,31 @@ }); | ||
const entriesTup = outputs | ||
const entriesTup = await outputs | ||
.filter((o) => o.kind == "entry-point") | ||
.map( | ||
(object, i) => | ||
[ | ||
files[i], | ||
{ | ||
path: createPath({ | ||
prefix, | ||
artifactPath: relative(dumpFolder, object.path), | ||
}), | ||
object, | ||
}, | ||
] as const | ||
); | ||
.mapAsync(async (object) => { | ||
const source = await getSource(object); | ||
if (!source) throw new Error("bunsai bug; check debug logs"); | ||
return [ | ||
source, | ||
{ | ||
path: createPath({ | ||
prefix, | ||
artifactPath: relative(dumpFolder, object.path), | ||
}), | ||
object: () => object, | ||
}, | ||
] as const; | ||
}); | ||
const extra = Array.from(registry.values()) | ||
.filter(({ $m_meta: $sv_meta }) => $sv_meta.css) | ||
.map(({ $m_meta: $sv_meta }) => ({ | ||
object: new Blob([$sv_meta.css!], { | ||
type: "text/css;charset=utf-8", | ||
}), | ||
.filter(({ $m_meta }) => $m_meta.css) | ||
.map(({ $m_meta }) => ({ | ||
object: () => | ||
new Blob([$m_meta.css!], { | ||
type: "text/css;charset=utf-8", | ||
}), | ||
path: createPath({ | ||
prefix, | ||
artifactPath: getCSSArtifactPath($sv_meta), | ||
artifactPath: getCSSArtifactPath($m_meta), | ||
}), | ||
@@ -96,11 +100,20 @@ })); | ||
extra: extra.concat( | ||
outputs | ||
.filter((o) => o.kind != "entry-point") | ||
.map((object) => ({ | ||
path: createPath({ | ||
await outputs | ||
.filter((o) => | ||
(IsDev() | ||
? ["sourcemap", "chunk", "asset"] | ||
: ["chunk", "asset"] | ||
).includes(o.kind) | ||
) | ||
.mapAsync(async (object) => { | ||
const path = createPath({ | ||
prefix: prefix, | ||
artifactPath: relative(dumpFolder, object.path), | ||
}), | ||
object, | ||
})) | ||
}); | ||
return { | ||
path, | ||
object: () => object, | ||
}; | ||
}) | ||
) as BuildResult[], | ||
@@ -107,0 +120,0 @@ }; |
@@ -24,7 +24,10 @@ import type { Attributes } from "./attrs"; | ||
path, | ||
handle: () => | ||
new Response(object, { headers: { "content-type": object.type } }), | ||
handle: () => { | ||
const obj = object(); | ||
return new Response(obj, { headers: { "content-type": obj.type } }); | ||
}, | ||
}); | ||
} | ||
return retorno; | ||
} |
@@ -7,1 +7,8 @@ declare namespace NodeJS { | ||
} | ||
declare interface Array<T> { | ||
mapAsync<R = T>( | ||
callbackfn: (value: T, index: number, array: T[]) => Promise<R> | R, | ||
thisArg?: any | ||
): Promise<Array<R>>; | ||
} |
@@ -27,1 +27,5 @@ import type { BunSai } from "."; | ||
$ms); | ||
Array.prototype.mapAsync = function mapAsync(callbackFn, thisArg) { | ||
return Promise.all(this.map(callbackFn, thisArg)); | ||
}; |
@@ -5,3 +5,3 @@ /// <reference path="./global.d.ts"/> | ||
import { buildClient } from "./build"; | ||
import { Util } from "./util"; | ||
import { log } from "./util"; | ||
import { CurrentBunSai, CurrentClientBuild } from "./globals"; | ||
@@ -50,3 +50,3 @@ import { type Renderer } from "./create-renderer"; | ||
if (!build) { | ||
Util.log.loud("empty client endpoints. No module was registered"); | ||
log.loud("empty client endpoints. No module was registered"); | ||
@@ -80,3 +80,3 @@ const retorno = { | ||
Util.log.debug("client endpoints (", paths.join(" | "), ")"); | ||
log.debug("client endpoints (", paths.join(" | "), ")"); | ||
@@ -83,0 +83,0 @@ return result; |
@@ -1,4 +0,4 @@ | ||
import { Util } from "./util"; | ||
import { log } from "./util"; | ||
Util.log.loud( | ||
log.loud( | ||
"Preloading BunSai is not needed since v2.0.0-preview.5.", | ||
@@ -5,0 +5,0 @@ "\nUnless you want to load BunSai as standalone;", |
import type { ModuleRenderer } from "./module"; | ||
export interface ScriptData { | ||
export interface ScriptData< | ||
Context extends Record<string, any> = Record<string, any> | ||
> { | ||
clientPath: string; | ||
props: Parameters<ModuleRenderer>[0]; | ||
props: Parameters<ModuleRenderer<Context>>[0]; | ||
} |
@@ -1,88 +0,86 @@ | ||
export namespace Util { | ||
/** | ||
* `console.log` according to `DEBUG` settings | ||
*/ | ||
export namespace log { | ||
/** | ||
* `console.log` according to `DEBUG` settings | ||
* log if DEBUG env is set | ||
*/ | ||
export namespace log { | ||
/** | ||
* log if DEBUG env is set | ||
*/ | ||
export function debug(...data: any[]) { | ||
if (Bun.env.DEBUG) console.log("[bunsai2]:", ...data); | ||
} | ||
export function debug(...data: any[]) { | ||
if (Bun.env.DEBUG) console.log("[bunsai2]:", ...data); | ||
} | ||
/** | ||
* log if DEBUG env is equal to `verbose` | ||
*/ | ||
export function verbose(...data: any[]) { | ||
if (Bun.env.DEBUG == "verbose") console.log("[bunsai2]:", ...data); | ||
} | ||
/** | ||
* log if DEBUG env is equal to `verbose` | ||
*/ | ||
export function verbose(...data: any[]) { | ||
if (Bun.env.DEBUG == "verbose") console.log("[bunsai2]:", ...data); | ||
} | ||
/** | ||
* log if DEBUG env is **not** equal to `silent` | ||
*/ | ||
export function loud(...data: any[]) { | ||
if (Bun.env.DEBUG != "silent") console.log("[bunsai2]:", ...data); | ||
} | ||
/** | ||
* log if DEBUG env is **not** equal to `silent` | ||
*/ | ||
export function loud(...data: any[]) { | ||
if (Bun.env.DEBUG != "silent") console.log("[bunsai2]:", ...data); | ||
} | ||
} | ||
/** | ||
* `console.time` according to `DEBUG` settings | ||
*/ | ||
export namespace time { | ||
/** | ||
* `console.time` according to `DEBUG` settings | ||
* time if DEBUG env is set | ||
*/ | ||
export namespace time { | ||
/** | ||
* time if DEBUG env is set | ||
*/ | ||
export function debug(label?: string) { | ||
if (Bun.env.DEBUG) { | ||
const initial = performance.now(); | ||
export function debug(label?: string) { | ||
if (Bun.env.DEBUG) { | ||
const initial = performance.now(); | ||
return () => { | ||
const diff = performance.now() - initial; | ||
const mdiff = diff / 1000; | ||
const time = | ||
mdiff >= 1.0 ? mdiff.toFixed(3) + "s" : diff.toFixed(0) + "ms"; | ||
console.log("[bunsai2]:", label, "[" + time + "]"); | ||
}; | ||
} | ||
return () => {}; | ||
return () => { | ||
const diff = performance.now() - initial; | ||
const mdiff = diff / 1000; | ||
const time = | ||
mdiff >= 1.0 ? mdiff.toFixed(3) + "s" : diff.toFixed(0) + "ms"; | ||
console.log("[bunsai2]:", label, "[" + time + "]"); | ||
}; | ||
} | ||
/** | ||
* time if DEBUG env is equal to `verbose` | ||
*/ | ||
export function verbose(label?: string) { | ||
if (Bun.env.DEBUG == "verbose") { | ||
const initial = performance.now(); | ||
return () => {}; | ||
} | ||
return () => { | ||
const diff = performance.now() - initial; | ||
const mdiff = diff / 1000; | ||
const time = | ||
mdiff >= 1.0 ? mdiff.toFixed(2) + "s" : diff.toFixed(0) + "ms"; | ||
console.log("[bunsai2]:", label, "[" + time + "]"); | ||
}; | ||
} | ||
/** | ||
* time if DEBUG env is equal to `verbose` | ||
*/ | ||
export function verbose(label?: string) { | ||
if (Bun.env.DEBUG == "verbose") { | ||
const initial = performance.now(); | ||
return () => {}; | ||
return () => { | ||
const diff = performance.now() - initial; | ||
const mdiff = diff / 1000; | ||
const time = | ||
mdiff >= 1.0 ? mdiff.toFixed(2) + "s" : diff.toFixed(0) + "ms"; | ||
console.log("[bunsai2]:", label, "[" + time + "]"); | ||
}; | ||
} | ||
/** | ||
* time if DEBUG env is **not** equal to `silent` | ||
*/ | ||
export function loud(label: string) { | ||
if (Bun.env.DEBUG != "silent") { | ||
const initial = performance.now(); | ||
return () => {}; | ||
} | ||
return () => { | ||
const diff = performance.now() - initial; | ||
const mdiff = diff / 1000; | ||
const time = | ||
mdiff >= 1.0 ? mdiff.toFixed(2) + "s" : diff.toFixed(0) + "ms"; | ||
console.log("[bunsai2]:", label, "[" + time + "]"); | ||
}; | ||
} | ||
/** | ||
* time if DEBUG env is **not** equal to `silent` | ||
*/ | ||
export function loud(label: string) { | ||
if (Bun.env.DEBUG != "silent") { | ||
const initial = performance.now(); | ||
return () => {}; | ||
return () => { | ||
const diff = performance.now() - initial; | ||
const mdiff = diff / 1000; | ||
const time = | ||
mdiff >= 1.0 ? mdiff.toFixed(2) + "s" : diff.toFixed(0) + "ms"; | ||
console.log("[bunsai2]:", label, "[" + time + "]"); | ||
}; | ||
} | ||
return () => {}; | ||
} | ||
@@ -89,0 +87,0 @@ } |
import { resolve } from "path"; | ||
import { Util } from "./util"; | ||
import { log } from "./util"; | ||
import bunsai from "."; | ||
@@ -10,7 +10,7 @@ | ||
} catch (err) { | ||
Util.log.debug(err); | ||
log.debug(err); | ||
} finally { | ||
if (!config) Util.log.loud("Using BunSai with default settings"); | ||
if (!config) log.loud("Using BunSai with default settings"); | ||
} | ||
export default await bunsai(config); |
@@ -6,3 +6,3 @@ import type { | ||
} from "svelte/compiler"; | ||
import { Util } from "../../core/util"; | ||
import { log } from "../../core/util"; | ||
@@ -184,3 +184,3 @@ export interface CompileOptions { | ||
for await (const file of configFileGlob.scan({ absolute: true })) { | ||
Util.log.debug("[svelte]: loading config from", file); | ||
log.debug("[svelte]: loading config from", file); | ||
@@ -200,3 +200,3 @@ const config = (await import(file)).default as ResolvedSvelteConfig; | ||
Util.log.loud( | ||
log.loud( | ||
"[svelte]: config file was not found. Using default svelte settings." | ||
@@ -203,0 +203,0 @@ ); |
@@ -5,8 +5,9 @@ /// <reference path="../../core/global.d.ts"/> | ||
declare type SvelteModule = import("../../core/module").StandaloneModule & | ||
import("svelte").SvelteComponent; | ||
declare module "*.svelte" { | ||
type SvelteModule = import("../../core/module").StandaloneModule; | ||
const module: SvelteModule; | ||
export = module; | ||
export default module; | ||
} | ||
@@ -13,0 +14,0 @@ |
import type { BunPlugin } from "bun"; | ||
import * as svelte from "svelte/compiler"; | ||
import type { ResolvedSvelteConfig } from "./config"; | ||
import { Util } from "../../core/util"; | ||
import { log } from "../../core/util"; | ||
import { IsDev } from "../../core/globals"; | ||
@@ -36,4 +36,6 @@ import { SvelteHydratable } from "./globals"; | ||
Util.log.verbose(preprocess, code); | ||
log.verbose(preprocess, code); | ||
const name = "$" + Bun.hash(args.path, 0).toString(36); | ||
const { | ||
@@ -50,24 +52,24 @@ css: { code: css, map: cssMap }, | ||
css: "external", | ||
name: "$$$sv_comp", | ||
name, | ||
}); | ||
warnings.forEach((w) => Util.log.loud("[svelte]:", w)); | ||
warnings.forEach((w) => log.loud("[svelte]:", w.filename, w.message)); | ||
return { | ||
contents: | ||
'import { register as $$$sv_reg } from "bunsai/register";\n' + | ||
'import { register as $sv_reg } from "bunsai/register";\n' + | ||
'import { ModuleSymbol } from "bunsai/globals";\n' + | ||
'import { genScript as $$$sv_gen_script } from "bunsai/svelte/script.ts";\n' + | ||
'import { genScript as $sv_gen_script } from "bunsai/svelte/script.ts";\n' + | ||
(useAsset == false | ||
? "" | ||
: 'import $$$create_asset_getter from "bunsai/asset";\n' + | ||
"const asset = $$$create_asset_getter(import.meta);\n") + | ||
js.replace("export default", "") + | ||
: 'import $create_asset_getter from "bunsai/asset";\n' + | ||
"const asset = $create_asset_getter(import.meta);\n") + | ||
js + | ||
`\nconst _rr = ${name}.render` + | ||
`\n${name}.render=(...args)=>{const r =_rr(...args);$m_meta.css = r.css.code||$m_meta.css;return r}` + | ||
`\nconst _css = ${JSON.stringify(css)}, path = ${JSON.stringify( | ||
args.path | ||
)};` + | ||
`\nexport const $m_meta = {` + | ||
(IsDev() | ||
? `jsMap: ${JSON.stringify(jsMap)},` | ||
: "jsMap: void 0,") + | ||
`\nconst $m_meta = {` + | ||
(IsDev() ? `jsMap: ${JSON.stringify(jsMap)},` : "jsMap: null") + | ||
"css: _css," + | ||
@@ -77,10 +79,10 @@ "cssHash: _css && Bun.hash(path + _css, 1).toString(36)," + | ||
? `cssMap: ${JSON.stringify(cssMap)},` | ||
: "cssMap: void 0,") + | ||
: "cssMap: null") + | ||
"path," + | ||
"};" + | ||
"\nexport const $m_symbol = ModuleSymbol;" + | ||
"\nexport const $m_render = $$$sv_comp.render;" + | ||
"\nexport const $m_gen_script = $$$sv_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}", | ||
"\nconst $m_symbol = ModuleSymbol;" + | ||
`\nconst $m_render = ${name}.render;` + | ||
"\nconst $m_gen_script = $sv_gen_script;" + | ||
"\nconst render = $sv_reg({$m_meta,$m_render,$m_symbol,$m_gen_script});" + | ||
`\nObject.assign(${name},{$m_meta,$m_render,$m_symbol,$m_gen_script,render})`, | ||
loader: "js", | ||
@@ -98,7 +100,12 @@ }; | ||
await Bun.file(args.path).text(), | ||
preprocess | ||
preprocess, | ||
{ | ||
filename: args.path, | ||
} | ||
); | ||
Util.log.verbose(code); | ||
log.verbose(code); | ||
const name = "$" + Bun.hash(args.path, 0).toString(36); | ||
const { | ||
@@ -114,8 +121,6 @@ js: { code: js }, | ||
generate: "dom", | ||
name: "$$$sv_comp", | ||
name, | ||
}); | ||
warnings.forEach((w) => | ||
Util.log.loud("[svelte]:", w.filename, w.message) | ||
); | ||
warnings.forEach((w) => log.loud("[svelte]:", w.filename, w.message)); | ||
@@ -126,5 +131,5 @@ return { | ||
? js | ||
: 'import $$$create_asset_getter from "bunsai/asset";\n' + | ||
: 'import $create_asset_getter from "bunsai/asset";\n' + | ||
js + | ||
"\nconst asset = $$$create_asset_getter(import.meta);", | ||
"\nconst asset = $create_asset_getter(import.meta);", | ||
loader: "js", | ||
@@ -131,0 +136,0 @@ }; |
import type { ScriptData } from "../../core/script"; | ||
import { SvelteHydratable } from "./globals"; | ||
@@ -7,3 +8,3 @@ export function genScript(data: ScriptData) { | ||
`import Component from "${data.clientPath}";` + | ||
`window.$sv_instance = new Component({hydrate:true,target:window.$root,props:${JSON.stringify( | ||
`window.$sv_instance = new Component({hydrate:${SvelteHydratable()},target:window.$root,props:${JSON.stringify( | ||
data.props | ||
@@ -10,0 +11,0 @@ )}})` + |
50415
37
1310
195