版本信息
bun-1.3.0、svelte-5.41.1、macos-15.6.1。
% sw_vers
ProductName: macOS
ProductVersion: 15.6.1
BuildVersion: 24G90
% bun --version
1.3.0
概述
利用 bun 的插件机制,自行编写 sveltePlugin 插件,在打包期使用 svelte/compiler 编译 .svelte 文件为 js 模块(实测在运行期无法做到)。
代码
package.json
build.ts
svelte_plugin.ts
server.ts
src/index.html
src/index.ts
src/App.svelte
package.json:
{
"name": "bun_svelte",
"type": "module",
"version": "0.0.1",
"scripts": {
"build": "bun build.ts",
},
"devDependencies": {
"@types/bun": "^1.3.0",
"svelte": "^5.41.1"
},
"peerDependencies": {
"typescript": "^5.9.3"
}
}
build.ts:
import sveltePlugin from "./svelte_plugin.ts";
// 编译 server.ts 并打包成单一可执行文件
const result = await Bun.build({
entrypoints: ["server.ts"],
outdir: './dist',
minify: false,
format: "esm",
target: "bun",
plugins: [sveltePlugin()], // 必须的,否则无法编译 svelte 文件
sourcemap: true,
compile: true
});
if (!result.success) throw new Error('build "./dist/server" failed');
else console.log(`Create a standalone executable file "./dist/server". Can run with "./dist/server"`)
svelte_plugin.ts:
import type { BunPlugin } from "bun";
import { compile } from "svelte/compiler";
export default function sveltePlugin(): BunPlugin {
return {
name: "bun-plugin-svelte",
async setup(build) {
build.onLoad({ filter: /\.svelte$/ }, async ({ path }) => {
const source = await Bun.file(path).text();
const { js } = compile(source, { filename: path, generate: "client" });
console.log(`sveltePlugin: compiled svelte source code to js code`)
return { contents: js.code, loader: "js" };
});
},
};
}
server.ts:
import { serve } from "bun";
import index from "./src/index.html";
const port = 3000
serve({
development: true, // 可以避免 minify 方便观看源代码
port,
// `routes` requires Bun v1.2.3+
routes: {
// SPA
"/": index,
// Static route - content is buffered in memory at startup
"/favicon.ico": new Response(await Bun.file("./src/favicon.ico").bytes()),
"/api/status": new Response("OK"),
// File route - content is read from filesystem on each request
// "/download.zip": new Response(Bun.file("./download.zip")),
// Dynamic routes
"/users/:id": req => new Response(`Hello User ${req.params.id}!`),
},
// (optional) fallback for unmatched routes:
// Required if Bun's version < 1.2.3
fetch(req) {
return new Response("Not Found", { status: 404 });
},
});
console.log(`Listening http://localhost:${port}`);
src/index.html:
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="./styles.css" />
<script src="./index.ts" type="module"></script>
</head>
<body></body>
</html>
src/index.ts:
import { mount } from 'svelte';
import App from "./App.svelte";
// mount App to body
const app = mount(App, {
target: document.body,
props: { name: "RJ" }
});
export default app;
src/App.svelte:
<script>
let { name } = $props();
let count = $state(0);
function onclick() {
count += 1;
}
</script>
<main>
<h1>Hello {name}!</h1>
<button {onclick}>Clicked {count} {count > 1 ? 'times' : 'time'}</button>
</main>
编译打包
% bun build
Create a standalone executable file "./dist/server". Can run with "./dist/server"
or
% bun ./build.ts
% ls -alh ./dist
total 130760
drwxr-xr-x 3 rj staff 96B Oct 22 11:18 .
drwxr-xr-x 20 rj staff 640B Oct 22 11:18 ..
-rwxr-xr-x 1 rj staff 64M Oct 22 11:18 server
打包成功后将生成单一可执行文件 ./dist/server,直接执行这个文件就可以启动服务:
% ./dist/server
Listening http://localhost:3000
运行效果

image.png

image.png