Vitest 对非循环 wasm-bindgen 产物的支持index_bg.js 外部化与双包危害规避【免费下载链接】vitestNext generation testing framework powered by Vite.项目地址: https://gitcode.com/GitHub_Trending/vi/vitest本文以 Vitest 单元测试套件中的 wasm 测试夹具为核心讲解新一代 wasm-bindgen 打包器产物的“非循环导入”结构如何接入 Vitest 模块执行体系为什么必须将index_bg.js与index_bg.wasm同时声明为 external 依赖不这么做会发生什么双包危害以及如何在测试中验证greet这类 wasm 导出函数可用。读完本文你将掌握 wasm-bindgen 胶水代码的运行原理、Vitestserver.deps.external在 wasm 场景下的精确配置方式以及完整的复现步骤。非循环no-cyclic产物的背景新旧两代 wasm-bindgen 输出Vitest 仓库的test/unit/src/wasm/目录下并排放置了两代 wasm-bindgen 产物作为对照夹具夹具结构特征来源wasm-bindgen旧版输出wasm 与 js 之间存在循环导入复刻自 Jest 的 e2e 夹具见 wasm-bindgen/README.mdwasm-bindgen-no-cyclic新版输出wasm 与 js 之间不再使用循环导入复刻自 wasm-bindgen 官方examples/hello_worldcommit8198d2d2…两者的差异决定了不同的加载约束。旧版夹具的入口 index.js 只有两行核心逻辑// folder source: wasm-bindgen examples/hello_world // eslint-disable-next-line unused-imports/no-unused-imports, import/newline-after-import, no-unused-vars import * as wasm from ./index_bg.wasm export * from ./index_bg.js即把 wasm 模块的命名空间导入后丢弃仅触发实例化再把胶水模块index_bg.js全部再导出——胶水模块内部持有对 wasm 模块的引用形成 wasm ↔ js 的循环导入关系。而非循环版本彻底消除了这个环。其入口 index.js 变为import * as wasm from ./index_bg.wasm; import { __wbg_set_wasm } from ./index_bg.js; __wbg_set_wasm(wasm); export * from ./index_bg.js;变化点在于胶水模块index_bg.js内部改为一个模块级变量let wasm;加一个注入函数__wbg_set_wasm(val) { wasm val; }见 index_bg.js由入口文件在导入时显式把 wasm 实例“推”进胶水模块。js 侧不再反向 import wasm循环被打破。对应的包元信息 package.json 声明了 ESM 语义这也是理解双包危害的前提{ type: module, name: hello_world, files: [index_bg.wasm, index.js, index_bg.js, index.d.ts], module: index.js, types: index.d.ts, sideEffects: [./index.js, ./snippets/*] }核心问题为什么必须外部化 index_bg.js双包危害README 对这一夹具给出的关键结论只有一句话但信息量极大对于这个非循环版本能够工作index_bg.js和index_bg.wasm都需要被外部化否则index_bg.js上的双包危害会使其无法工作。结合 Vitest 的执行模型可以推断其失效机理Vitest 的模块执行体系会把被内联inline处理过的模块交给自己的运行时转换执行。非循环产物的正确性完全依赖“__wbg_set_wasm注入的 wasm 实例与后续调用greet时读到的wasm变量必须是同一份模块状态”。如果index_bg.js被内联执行而入口index.js中的再导出链路又让它以另一个形态被加载就会出现同一模块存在“两份拷贝”的经典双包危害dual package hazard一份拷贝执行了__wbg_set_wasm(wasm)注入成功另一份拷贝导出的greet读取的却是自己那份let wasm此时它仍是undefined调用wasm.__wbindgen_malloc立即抛错。从源码结构看index_bg.js 中的greet正是如此实现的——它直接闭包引用模块顶层的wasm变量export function greet(name) { const ptr0 passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const len0 WASM_VECTOR_LEN; wasm.greet(ptr0, len0); }因此“注入”与“消费”落在不同模块实例上时功能必然失效。解法就是让index_bg.js连同index_bg.wasm走 Node 原生 ESM 加载、绕过 Vitest 的转换管线由 ESM 模块缓存保证全局唯一性双包危害自然消失。Vitest 中的配置证据单元测试套件在 test/unit/vite.config.ts 中给出了精确配置两条 external 规则缺一不可server: { deps: { external: [ tinyspy, /src\/external/, /esm\/esm/, /packages\/web-worker/, /\.wasm$/, // index_bg.wasm 原生加载 /\/wasm-bindgen-no-cyclic\/index_bg.js/, // 胶水模块保持单一实例 /dep-esm-non-existing/, ], inline: [inline-lib], }, },另外两处配套细节同样值得注意原生.wasm导入依赖 Node 实验特性该配置在 test/unit/vite.config.ts 中通过test.execArgv: [--experimental-wasm-modules]注入到 worker 进程wasm 加载过程会产生 “Importing WebAssembly …” 日志配置通过onConsoleLog钩子test/unit/vite.config.ts将其静默保持测试输出干净。测试验证greet 的端到端调用链对非循环产物的功能验证位于 test/unit/test/wasm.test.tstest(supports wasm-bindgen, async () { globalThis.alert vi.fn() const { greet } await import(../src/wasm/wasm-bindgen-no-cyclic/index.js) greet(No Cyclic) expect(globalThis.alert).toHaveBeenCalledWith(Hello, No Cyclic!) })这条断言背后是一条完整的跨语言调用链入口 index.js 导入index_bg.wasmexternalNode 原生实例化并调用__wbg_set_wasm注入胶水模块测试调用greet(No Cyclic)胶水层 passStringToWasm0 把字符串按 UTF-8 编码写入 wasm 线性内存先快速写入纯 ASCII 前缀遇到 0x7F的码点再通过__wbindgen_realloc扩容并用encodeInto补齐wasm 侧执行greet通过导入的__wbg_alert_9ea5a791b0d4c7a3回调 JSindex_bg.js从线性内存读回字符串后调用全局alert测试用vi.fn()替换globalThis.alert断言收到Hello, No Cyclic!。作为对照旧版循环产物有独立用例 supports wasm/js cyclic import (old wasm-bindgen output)同样通过 mockalert断言Hello, World!。两个用例同时通过说明 Vitest 对两代 wasm-bindgen 输出均保持兼容。同文件还覆盖了原生.wasm导入、动态导入以及data:application/wasm;base64,…URI 等场景test/unit/test/wasm.test.ts。复现该夹具从 wasm-bindgen 官方示例到测试目录README 保留了这套夹具的生成步骤源码取自 wasm-bindgen 官方仓库的examples/hello_world锁定在 commit8198d2d25920e1f4fc593e9f8eb9d199e004d731npm i npm run build # 然后 # 1. 将 examples/hello_world/pkg 复制到本目录 # 2. 在 package.json 中加入 { type: module } # wasm-pack PR #1061 合并后会默认包含该项需要注意的最后一条备注wasm-bindgen 早期生成的pkg/package.json不带type: module需要手工补上否则 ESM 胶水代码会被 Node 按 CJS 解析。当前夹具目录中的 package.json 第一行即为type: module正是这一步的落地结果。关键结论wasm-bindgen 新版打包器产物以“__wbg_set_wasm显式注入”替代了 wasm/js 循环导入其正确性依赖胶水模块状态的唯一性在 Vitest 中运行此类产物时index_bg.js与index_bg.wasm必须同时列入test.server.deps.external如 test/unit/vite.config.ts 所示否则双包危害导致注入与消费分属不同模块实例greet等导出函数不可用配套还需要execArgv: [--experimental-wasm-modules]以启用 Node 原生 wasm 模块导入验证方式是对 wasm 导入的 JS 回调如alert打桩后断言输出见 test/unit/test/wasm.test.ts。【免费下载链接】vitestNext generation testing framework powered by Vite.项目地址: https://gitcode.com/GitHub_Trending/vi/vitest创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
