close

WebAssembly

Rslib 支持构建引用 WebAssembly(.wasm)文件的库。

该能力主要面向会被 Node.js 或 Rsbuild、Vite 等现代 ESM 应用打包工具消费的库产物。

Warning

WASM 支持目前仅适用于 format 设置为 'esm' 的产物。

这个限制只针对 Rslib 的输出格式。你的源码仍然可以照常使用 TypeScript 或 JavaScript 编写。

启用 WASM 支持

lib 配置中设置 wasm: true

rslib.config.ts
import { defineConfig } from '@rslib/core';

export default defineConfig({
  lib: [
    {
      format: 'esm',
      wasm: true,
      source: {
        entry: {
          index: './src/index.ts',
        },
      },
    },
  ],
});

支持的输入形式

Rslib 支持以下输入形式。

new URL() 引用

当你的代码需要拿到 .wasm 文件的 URL 或文件位置时,可以使用标准的 new URL('./file.wasm', import.meta.url) 写法:

src/index.ts
export const wasmUrl = new URL('./add.wasm', import.meta.url);

默认情况下,Rslib 会在 JavaScript 产物中保留这个表达式,并将引用到的 .wasm 文件输出到对应位置:

dist/index.js
const wasmUrl = new URL('./add.wasm', import.meta.url);
export { wasmUrl };
dist
dist/
  index.js
  add.wasm

这种方式适合你希望自己加载或传递 wasm 文件的场景。

ESM .wasm 导入

你可以直接从 .wasm 文件中导入导出项:

src/index.ts
import { add } from './add.wasm';

export const sum = add(1, 2);
export { add };

Rslib 会将 .wasm 模块转换成一个 JavaScript facade。这个 facade 会实例化 WebAssembly 模块,并将 wasm exports 作为 ESM exports 重新导出。

默认情况下,较小的 .wasm 文件会被内联到生成的 JavaScript facade 中:

dist/index.js
const __rslib_wasm_base64 = '...';
const { instance: __rslib_wasm_instance } = await WebAssembly.instantiate(...);
const __rslib_wasm_export_add = __rslib_wasm_instance.exports['add'];
export { __rslib_wasm_export_add as add };

内联可以避免运行时资源路径问题,适合体积较小的 wasm 模块。

你也可以重新导出 wasm 模块:

src/index.ts
export { add } from './add.wasm';
export * from './add.wasm';

TypeScript 类型声明

TypeScript 默认无法知道任意 .wasm 文件的导出名称。如果你的源码直接导入原始 .wasm 文件,需要添加和 wasm exports 匹配的类型声明:

src/wasm.d.ts
declare module '*.wasm' {
  export function add(left: number, right: number): number;
}

如果 wasm 由 wasm-pack 等工具生成,建议优先使用这些工具生成的 .d.ts 文件。

控制内联输出

Rslib 默认会内联较小的 ESM .wasm 导入。默认内联阈值是 14 * 1024 字节。

你可以通过 inlineinlineLimit 控制这个行为:

rslib.config.ts
export default {
  lib: [
    {
      format: 'esm',
      wasm: {
        inline: 'auto',
        inlineLimit: 14 * 1024,
      },
    },
  ],
};

如果希望强制内联:

rslib.config.ts
export default {
  lib: [
    {
      format: 'esm',
      wasm: {
        inline: true,
      },
    },
  ],
};

如果希望 ESM .wasm 导入以独立资源文件形式输出:

rslib.config.ts
export default {
  lib: [
    {
      format: 'esm',
      wasm: {
        mode: 'asset',
        assetFileName: 'static/wasm/[name].[contenthash:8][ext]',
      },
    },
  ],
};

对于 Node.js 目标,生成的 facade 会通过 node:fs/promises 读取输出的 .wasm 文件。对于 web 目标,则会通过 fetch(new URL(..., import.meta.url)) 加载输出的 .wasm 文件。

wasm-pack / wasm-bindgen

Rslib 支持使用标准 new URL('./file.wasm', import.meta.url) 引用 companion .wasm 资源的本地 JavaScript glue 文件。只要 wasm-pack / wasm-bindgen 等工具生成的 ESM glue 使用这种标准写法,也会被这套通用机制覆盖。

对于 wasm-pack,目前推荐使用 wasm-pack --target web。这个 target 会生成如下形式的 ESM glue:

pkg/my_lib.js
if (module_or_path === undefined) {
  module_or_path = new URL('my_lib_bg.wasm', import.meta.url);
}

你可以在库入口中导入生成的 glue:

src/index.ts
import init, { add, initSync } from './pkg/my_lib.js';

export { init, initSync, add };

在 bundled 产物中,glue 文件会和其他 JavaScript 模块一样走通用的 new URL() 处理。在 bundleless 产物中,Rslib 会额外扫描本地 JavaScript glue 文件,并将 companion .wasm 文件输出到它旁边:

dist
dist/
  index.js
  pkg/
    my_lib.js
    my_lib_bg.wasm

Rslib 不会 patch 这些工具生成的初始化协议。这样可以保留 wasm-bindgen 生成的 init()initSync() 以及自定义初始化输入等 API 行为。

Warning

wasm-pack --target bundler 会使用 ESM .wasm 导入,并且可能在 wasm 模块和生成的 JavaScript glue 之间形成循环初始化关系。请将它视为高级场景,并在目标运行时中充分测试最终产物。

wasm-pack --target nodejswasm-pack --target no-modules 不推荐用于目前这套 ESM-only WASM 流程。

高级选项

你可以分别启用或关闭每一种支持的输入形式:

rslib.config.ts
export default {
  lib: [
    {
      format: 'esm',
      wasm: {
        // 支持 new URL('./foo.wasm', import.meta.url)
        url: true,
        // 支持 import { foo } from './foo.wasm'
        module: true,
        // 在 bundleless 产物中扫描本地 JS glue,并复制 companion wasm 资源
        glue: true,
      },
    },
  ],
};

对于 new URL() 引用,Rslib 默认会保留原始相对路径。你也可以将 URL 重写到输出的资源文件名:

rslib.config.ts
export default {
  lib: [
    {
      format: 'esm',
      wasm: {
        url: {
          mode: 'rewrite',
        },
        assetFileName: 'static/wasm/[name].[contenthash:8][ext]',
      },
    },
  ],
};

Rspack runtime 模式

如果你明确希望使用 Rspack 的 WebAssembly runtime 处理能力,可以显式开启:

rslib.config.ts
export default {
  lib: [
    {
      format: 'esm',
      wasm: {
        mode: 'rspack-runtime',
      },
    },
  ],
};

这个模式会启用 Rspack 的 asyncWebAssembly experiment。它适合你能够控制消费侧应用环境的场景,但不建议作为可移植 library 的默认输出策略。