feat: add ability to serve static by using dynamic imports (#197)

* feat: add ability to serve static by using dynamic imports

* serveStaticViaImport: make manifest optional

* serveStaticViaImport: add error log
This commit is contained in:
dswbx
2025-07-22 20:13:10 +02:00
committed by GitHub
parent ea372a8125
commit 1ec3ba0e92
5 changed files with 62 additions and 4 deletions
+2 -1
View File
@@ -250,7 +250,8 @@
},
"./dist/main.css": "./dist/ui/main.css",
"./dist/styles.css": "./dist/ui/styles.css",
"./dist/manifest.json": "./dist/static/.vite/manifest.json"
"./dist/manifest.json": "./dist/static/.vite/manifest.json",
"./static/*": "./dist/static/*"
},
"typesVersions": {
"*": {
+3 -1
View File
@@ -21,8 +21,8 @@ export async function createApp<Env = BunEnv>(
return await createRuntimeApp(
{
...config,
serveStatic: serveStatic({ root }),
...config,
},
args ?? (process.env as Env),
opts,
@@ -53,6 +53,7 @@ export function serve<Env = BunEnv>(
onBuilt,
buildConfig,
adminOptions,
serveStatic,
...serveOptions
}: BunBkndConfig<Env> = {},
args: Env = {} as Env,
@@ -70,6 +71,7 @@ export function serve<Env = BunEnv>(
buildConfig,
adminOptions,
distPath,
serveStatic,
},
args,
opts,
+54 -1
View File
@@ -1,9 +1,11 @@
import { App, type CreateAppConfig } from "bknd";
import { config as $config } from "bknd/core";
import { $console } from "bknd/utils";
import type { MiddlewareHandler } from "hono";
import type { Context, MiddlewareHandler, Next } from "hono";
import type { AdminControllerOptions } from "modules/server/AdminController";
import { Connection } from "bknd/data";
import type { Manifest } from "vite";
import { guessMimeType } from "media";
export { Connection } from "bknd/data";
@@ -140,3 +142,54 @@ export async function createRuntimeApp<Args = DefaultArgs>(
return app;
}
/**
* Creates a middleware handler to serve static assets via dynamic imports.
* This is useful for environments where filesystem access is limited but bundled assets can be imported.
*
* @param manifest - Vite manifest object containing asset information
* @returns Hono middleware handler for serving static assets
*
* @example
* ```typescript
* import { serveStaticViaImport } from "bknd/adapter";
*
* serve({
* serveStatic: serveStaticViaImport(),
* });
* ```
*/
export function serveStaticViaImport(opts?: { manifest?: Manifest }) {
let files: string[] | undefined;
// @ts-ignore
return async (c: Context, next: Next) => {
if (!files) {
const manifest =
opts?.manifest || ((await import("bknd/dist/manifest.json")).default as Manifest);
files = Object.values(manifest).flatMap((asset) => [asset.file, ...(asset.css || [])]);
}
const path = c.req.path.substring(1);
if (files.includes(path)) {
try {
const content = await import(`bknd/static/${path}?raw`, {
assert: { type: "text" },
}).then((m) => m.default);
if (content) {
return c.body(content, {
headers: {
"Content-Type": guessMimeType(path),
"Cache-Control": "public, max-age=31536000, immutable",
},
});
}
} catch (e) {
console.error("Error serving static file:", e);
return c.text("File not found", 404);
}
}
await next();
};
}
+1 -1
View File
@@ -32,8 +32,8 @@ export async function createApp<Env = NodeEnv>(
registerLocalMediaAdapter();
return await createRuntimeApp(
{
...config,
serveStatic: serveStatic({ root }),
...config,
},
// @ts-ignore
args ?? { env: process.env },
+2
View File
@@ -26,9 +26,11 @@ export const M = new Map<string, string>([
["eps", c.a("postscript")],
["epub", c.a("epub+zip")],
["ini", c.t()],
["ico", c.i("vnd.microsoft.icon")],
["jar", c.a("java-archive")],
["jsonld", c.a("ld+json")],
["jpg", c.i("jpeg")],
["js", c.t("javascript")],
["log", c.t()],
["m3u", c.t()],
["m3u8", c.a("vnd.apple.mpegurl")],