Sign in

    moon-embed

    Embed static files into MoonBit binaries

    Download zip
    Version
    0.1.2
    License
    MIT
    Last updated
    4 months ago
    Downloads
    38

    Dependencies

    #moon-embed

    将静态文件嵌入 MoonBit 二进制。

    #一分钟上手

    # 生成嵌入文件(默认输出到 src/embedded.mbt) moon run gen -- ./frontend/dist # 或指定输出路径 moon run gen -- ./frontend/dist -o my-embed.mbt # 在代码里使用
    let files = @embed.decode_all(EMBED_PATHS, EMBED_DATA)

    // HTTP 处理
    match files.get(request.path) {
    Some(bytes) => {
    sconn.send_response(200, "OK",
    extra_headers={ "Content-Type": @embed.content_type(request.path) })
    sconn.write(bytes)
    sconn.end_response()
    }
    None => { /* 404 */ }
    }

    #两种模式

    模式函数适用场景
    内存decode_all()小文件、性能敏感
    磁盘extract_all() + read_file()大文件、省内存

    // 内存模式
    let files = @embed.decode_all(EMBED_PATHS, EMBED_DATA)

    // 磁盘模式
    let dir = @embed.extract_all(EMBED_PATHS, EMBED_DATA)
    let html = @embed.read_file(dir, "/index.html")


    #这是什么

    把前端构建产物(或任意静态文件)直接编译进 MoonBit 二进制。类似 Go 的 //go:embed。

    #常见误解

    "那编译产物不还是两个文件吗?一个 .exe 还得带着一个 embedded.mbt?"

    不是。 embedded.mbt 在 moon build 阶段就被编译进二进制了,不是运行时加载的。

    make build → pnpm build → moon run gen -- ./frontend/dist ← 生成 src/embedded.mbt → moon build ← 编译进 .exe → 一个 .exe 文件 ← 这就是全部

    这个 .exe 拿到任何机器直接跑,不需要带着 embedded.mbt。

    #和纯 Bytes 字面量方案的区别

    社区已有的 tonyfettes/moon-embed 走的是另一条路:每个文件生成一个单独的 let / const 绑定,文本用 #| 多行字符串,二进制用 Bytes 十六进制字面量。

    纯 Bytes 字面量本方案(base64 + 运行时解码)
    源码可读性小文件好,大文件几千行 0xFF统一 base64 字符串
    编译速度大文件 Bytes 字面量拖慢编译器base64 字符串编译快
    运行时性能零解码启动时一次 decode
    目录支持手动,每个文件跑一次一次扫描整个目录
    运行时库无decode_all / content_type / extract_all

    大文件(JS/CSS)用 Bytes 字面量会导致生成的 .mbt 文件膨胀、编译变慢。本方案用 base64 更适合前端静态文件批量嵌入的场景。


    #用法

    #作为库

    moon add moonbitlang/x moon add moonbitlang/async moon add your-name/moon-embed

    let files = @embed.decode_all(EMBED_PATHS, EMBED_DATA)
    let html = files["/index.html"]

    #生成嵌入文件

    moon run gen -- ./frontend/dist

    输出默认到 src/embedded.mbt,可自定义:

    moon run gen -- ./frontend/dist -o path/to/embedded.mbt

    #API

    函数说明
    decode_all(paths, data)base64 解码全部文件,返回 Map[String, Bytes]
    extract_all(paths, data)解压到临时目录,返回目录路径
    read_file(dir, path)从解压目录读取文件
    content_type(path)根据扩展名猜测 Content-Type
    ensure_embedded(paths)检查嵌入数据是否为空

    #发布到 mooncakes

    moon login # 登录 mooncakes 账号 moon publish # 发布(包名为 your-name/moon-embed)

    包名以你的 mooncakes 用户名开头,不会和任何人冲突。

    content_type

    fn content_type(path : String) -> String
    根据文件路径猜测 Content-Type

    decode_all

    fn decode_all(paths : Array[String], data : Array[String]) -> Map[String, Bytes]
    将 base64 编码的嵌入数据解码为内存 Map

    let files = @embed.decode_all(MY_PATHS, MY_DATA) let html = files["/index.html"]

    ensure_embedded

    fn ensure_embedded(paths : Array[String]) -> Result[Unit, String]
    检查嵌入数据是否为空

    编译时:如果用户没生成 embedded.mbt,编译器会报 The value identifier EMBED_PATHS is unbound. 这已经是接近编译期检查的效果。

    运行时:如果生成的文件为空数组,返回 Err 提示。

    extract_all

    async fn extract_all(paths : Array[String], data : Array[String]) -> String
    将嵌入文件解压到临时目录,返回目录路径

    let dir = @embed.extract_all(MY_PATHS, MY_DATA) let html = @embed.read_file(dir, "/index.html")

    read_file

    async fn read_file(dir : String, path : String) -> Bytes?
    从解压目录中读取文件

    Source Files