Babel 的 @babel/plugin-proposal-import-wasm-source:让 WebAssembly 源码阶段导入在浏览器与 Node.js 中通用

Babel 的 @babel/plugin-proposal-import-wasm-source:让 WebAssembly 源码阶段导入在浏览器与 Node.js 中通用 Babel 的 babel/plugin-proposal-import-wasm-source让 WebAssembly 源码阶段导入在浏览器与 Node.js 中通用【免费下载链接】babel Babel is a compiler for writing next generation JavaScript.项目地址: https://gitcode.com/gh_mirrors/ba/babel导读babel/plugin-proposal-import-wasm-source是 Babel 仓库gh_mirrors/ba/babel中负责转换“源码阶段导入source phase imports”即import source ... from ...语法的插件其前提是导入的资源是 WebAssembly 模块。本文以该插件的 README.md 为主线结合 插件实现源码、平台适配辅助模块 与完整测试用例讲解其安装方式、转换行为、基于targets的分平台代码生成原理、并行加载优化以及使用限制。读完本文你将掌握如何把基于 WebAssembly 的源码阶段导入代码编译为浏览器与 Node.js 均可运行的等价实现并理解 Babel 如何根据目标环境动态选择底层的 fetch / fs 读取方案。什么是源码阶段导入Source Phase Imports传统 ES 模块导入在加载时会执行模块求值而“源码阶段导入”是 ECMAScript 模块系统的一个演进提案它允许以import source x from ...的语法只取回某个模块的源码本身而不执行它。这一特性与 WebAssembly 高度契合——调用方可以先拿到.wasm文件的原始字节再通过WebAssembly.Module/WebAssembly.compile自行编译与实例化。在babel/plugin-proposal-import-wasm-source中它处理的语法包括两种形态静态声明import source s from ./x.wasm;默认导入绑定绑定的是编译后的 WebAssembly 模块动态表达式let promise import.source(./x.wasm);对应 AST 中的ImportExpression且phase source对应的语法支持由 babel/plugin-syntax-import-source 提供该语法插件通过manipulateOptions向解析器注入sourcePhaseImports插件并开启createImportExpressions使解析器能够产出带有phase: source的导入节点。而本插件的inherits: syntaxImportSourcePhase声明保证仅安装本插件时语法层也能正常解析。安装与基本配置根据 README安装方式与大多数 Babel 插件一致npm install --save-dev babel/plugin-proposal-import-wasm-source或使用 yarnyarn add babel/plugin-proposal-import-wasm-source --dev随后在 Babel 配置.babelrc或babel.config.json中启用{ plugins: [babel/plugin-proposal-import-wasm-source] }从 package.json 可以确认该插件的关键元数据与约束版本8.0.0系列当前仓库中为8.0.1peerDependencies要求babel/core: ^8.0.0运行环境engines.node为^22.18.0 || 24.11.0说明其依赖较新的 Node.js 运行时能力运行时依赖babel/helper-import-to-platform-api平台适配、babel/helper-plugin-utils插件声明工具、babel/plugin-syntax-import-source语法支持开发依赖中包含babel/plugin-transform-json-modules用于验证与其他模块类转换插件的协同见下文“并行 JSON 导入”。需要注意的是插件源码中api.assertVersion(^7.23.0 || ^8.0.0)表示它同样兼容 Babel 7.23 的 API 面对应 npm 上 Babel 7 发行线的用法但本仓库的 package.json 面向 Babel 8。插件核心实现两条转换路径src/index.ts 中定义了transformers这一组“原子片段”它们是所有平台实现的公共拼图const transformers: Pieces { webFetch: (fetch) template.expression.astWebAssembly.compileStreaming(${fetch}), nodeFsSync: (read) template.expression.astnew WebAssembly.Module(${read}), nodeFsAsync: () template.expression.astWebAssembly.compile, };可以看到三条策略浏览器流式编译WebAssembly.compileStreaming(fetch(url))边下载边编译性能最优Node.js 同步new WebAssembly.Module(buffer)直接基于读取到的字节构造模块对象Node.js 异步WebAssembly.compile配合fs.promises.readFile异步读取字节后编译。静态声明的转换Programvisitor 遍历程序体收集所有phase source的ImportDeclaration若声明带有 import attributeswith { type: ... }或 assertions直接抛出buildCodeFrameError校验 specifier 必须是默认导入assertImportDefaultSpecifier记录{ id, fetch }后删除原声明通过injectParallelStaticImports将转换结果注入程序体顶部。以targets: { firefox: 110 }的浏览器场景为例对应测试 的输入import source s from ./x.wasm;转换输出为见 output.mjsconst s await WebAssembly.compileStreaming(fetch(import.meta.resolve(./x.wasm)));动态导入表达式的转换ImportExpressionvisitor 处理import.source(./x.wasm)。若传入 options bag 则报错否则替换为异步取回并编译的表达式。浏览器场景下 input.jslet promise import.source(./x.wasm);转换为见 output.mjslet promise Promise.resolve().then(() WebAssembly.compileStreaming(fetch(import.meta.resolve(./x.wasm))));Promise.resolve().then(...)的包装来自 helper-import-to-platform-api 的 buildFetchAsyncWrapped对于字符串字面量直接包装为Promise.resolve().then(() ...)对于动态字符串则先求值模板串再传入从而保证动态导入天然返回 Promise。分平台代码生成以 targets 为核心的决策树该插件的核心价值在于同一份源码按编译目标输出不同实现。模块系统与目标环境共同决定生成代码插件通过file.get(babel/plugin-transform-modules-*)判断模块转换结果值为commonjs时走 CommonJS helper为null即不转换模块时走 ESM helper其他模块系统如 AMD、UMD、SystemJS会直接抛错——“proposal-import-wasm-source只能在未编译模块或编译为 CommonJS 时使用”平台能力判断由 platforms-support.ts 依据targets完成关键能力点包括webIMRimport.meta.resolve的浏览器支持Chrome/Edge 105、Firefox 106、Safari 16.4、Deno 1.24 等nodeIMRNode.js 20.6.0nodeFSPrequire(fs).promisesNode.js 10.0.0。基于这些开关helper 的模式匹配分支 会生成不同代码下面结合测试 fixtures 逐一说明。纯浏览器场景目标不含 node 且支持import.meta.resolve时直接使用流式编译见上文 Firefox 110 示例。当浏览器不支持import.meta.resolve时会退化为import.meta.resolve?.(spec) ?? new URL(spec, import.meta.url)回退形态参见 import-meta-resolve-fallback 用例。Node.js CommonJS 场景import-declaration 的 node-cjs 输出 展示了同步实现use strict; const s new WebAssembly.Module(require(fs).readFileSync(require.resolve(./x.wasm)));require.resolve完成模块解析readFileSync同步读字节new WebAssembly.Module同步构造模块对象——整个过程无需顶层 await因此在 CommonJS 下完全同步可行。Node.js ESM 场景import-declaration 的 node-esm 输出import { readFileSync as _readFileSync } from fs; const s new WebAssembly.Module(_readFileSync(new URL(import.meta.resolve(./x.wasm))));这里利用import.meta.resolve把模块说明符解析为文件 URL再交给fs.readFileSync若目标 Node 版本低于 20.6.0不支持import.meta.resolve则退化为createRequire(import.meta.url).resolve(spec)见 node-esm 的 import-meta-resolve-fallback 用例且异步路径会退化为fs.promises.readFile(...).then(WebAssembly.compile)。浏览器 Node 同构场景当targets同时覆盖浏览器与 Node 时helper 生成带运行时分流的代码先判断环境再选择实现const s await (typeof process object process.versions?.node ? import(fs).then(fs fs.promises.readFile(new URL(import.meta.resolve(./x.wasm)))).then(WebAssembly.compile) : WebAssembly.compileStreaming(fetch(import.meta.resolve(./x.wasm))));该输出与 browser-and-node-esm/import-declaration 测试 完全一致process.versions?.node存在则走 Node 的文件系统路径否则走浏览器 fetch 路径。同构场景还细分了“两端都无import.meta.resolve”“仅浏览器不支持”“仅 Node 不支持”三种回退组合见 browser-and-node-esm 目录下的对应 fixtures以及 Node 不支持fs.promises时的 Promise 包装回退no-fs-promises-support。并行加载优化合并多个静态导入当同一模块存在多条import source声明时插件会将它们合并为一次Promise.all并行发起避免串行等待。import-declaration-multiple 用例import source s from ./x.wasm; someBody; import source s2 from ./x2.wasm;输出见 output.mjsconst [s, s2] await Promise.all([WebAssembly.compileStreaming(fetch(import.meta.resolve(./x.wasm))), WebAssembly.compileStreaming(fetch(import.meta.resolve(./x2.wasm)))]); someBody;该逻辑实现在 injectParallelStaticImports 与 buildParallelStaticImports单条导入const s await fetchExprneedsAwait时多条导入且需要 await解构Promise.all([...])多条导入但无需 await如 CommonJS 同步路径展开为多条独立const声明使用WeakMapProgram, ...记录上一次注入的声明节点当其他插件如transform-json-modules也调用同一注入函数时会把新数据合并进既有声明中prev.data.concat(data)实现跨插件的声明合并——这正是 parallel-json-import 用例 验证的行为。使用限制与报错场景基于 errors fixtures以下写法无法被该插件编译带 import attributes 的静态导入import source s from ./x.wasm with { type: wasm }→ 报错import source with import attributes cannot be compiled.带 assertions 的静态导入import source s from ./x.wasm assert { type: wasm }→ 同样拒绝attributes 与 assertions 均不被支持因为源码阶段导入本身就隐式保证“只取源码”无需再显式声明类型带 options bag 的动态导入import.source(./x.wasm, { with: { type: wasm } })→ 报错import.source with an options bag cannot be compiled.模块系统限制当模块被编译为 AMD / UMD / SystemJS 等非 CommonJS 形式时插件直接抛错CommonJS 下需要顶层 await 的场景若 helper 判定同步构建不可用而目标又是 CommonJS会抛出 “Cannot compile to CommonJS, since it would require top-level await.”见 helper 源码。此外静态导入要求使用默认导入 specifierassertImportDefaultSpecifier命名导入import source { x } from ...不被支持。与其他插件协同与验证方式该插件在 Babel 编译流水线中通常与其他插件搭配使用例如配合babel/plugin-transform-json-modules处理 JSON 模块的源码阶段导入配合babel/plugin-transform-modules-commonjs产出 CommonJS 目标此时插件自动走helperCJS同步路径。仓库内置的 fixture 测试覆盖了browser、node-esm、node-cjs、browser-and-node-esm四类目标组合及全部回退分支读者可在 test/fixtures 目录中对照输入输出验证本文所述的各种转换行为。安装与使用方面请严格遵循 README 的安装命令并在安装后通过npx babel或项目构建脚本对包含import source的源文件执行编译即可得到上述与目标环境匹配的输出代码。小结babel/plugin-proposal-import-wasm-source把仍处于提案阶段的源码阶段导入语法落地为可运行的跨平台代码浏览器端采用WebAssembly.compileStreaming(fetch(...))流式编译Node.js 端按 CJS/ESM 与 Node 版本选择require.resolve/import.meta.resolve/createRequire的解析组合与同步/异步读取方案并自动合并多条静态导入以并行加载。其“基于 targets 的能力感知 模式匹配生成平台实现 跨插件声明合并”的设计见 helper-import-to-platform-api 与 platforms-support也为编写同类“一套源码、多平台输出”的转换插件提供了可借鉴的范本。【免费下载链接】babel Babel is a compiler for writing next generation JavaScript.项目地址: https://gitcode.com/gh_mirrors/ba/babel创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考