diff --git a/README.md b/README.md index cc3ba7a..84e2c38 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ [![npm downloads][npm-downloads-src]][npm-downloads-href] [![Unit Test][unit-test-src]][unit-test-href] -Rolldown plugin for WASM. This plugin is forked from [@rollup/plugin-wasm](https://github.com/rollup/plugins/tree/master/packages/wasm). +Rolldown plugin for WASM. + +This project is heavily referenced from [@rollup/plugin-wasm](https://github.com/rollup/plugins/tree/master/packages/wasm) and [vite-plugin-wasm](https://github.com/Menci/vite-plugin-wasm). ## Install @@ -28,37 +30,51 @@ export default defineConfig({ }) ``` -### Asynchronous (default) +### Importing WASM ```ts -import init from './add.wasm' - -const module = await init() -// ^? WebAssembly.Module +import { add } from './add.wasm' -const instance = await init(imports) -// ^? WebAssembly.Instance +console.log(add(1, 2)) ``` -### Synchronous +### Asynchronous Init ```ts -import initSync from './add.wasm?sync' +import init from './add.wasm?init' + +const instance = await init( + imports, // optional +) +console.log(instance.exports.add(1, 2)) +``` -const module = initSync() -// ^? WebAssembly.Module +### Synchronous Init -const instance = initSync(imports) -// ^? WebAssembly.Instance +```ts +import initSync from './add.wasm?init&sync' + +const instance = initSync( + imports, // optional +) +console.log(instance.exports.add(1, 2)) ``` ### `wasm-bindgen` Support -#### Target `web` (Recommended) +#### Target `bundler` (default, recommended) + +```ts +import { add } from 'some-pkg' + +add(1, 2) +``` + +#### Target `web` ```ts import init, { add } from 'some-pkg' -import wasm from 'some-pkg/add_bg.wasm' // import wasm file +import wasm from 'some-pkg/add_bg.wasm?init' // import wasm file await init({ module_or_path: await wasm(), @@ -70,20 +86,6 @@ add(1, 2) > [!NOTE] > Other targets such as `nodejs` and `no-modules` are not supported. -#### Target `bundler` (default) - -Enable `wasmBindgen` option in the plugin config. - -```ts -// rolldown.config.ts -import { defineConfig } from 'rolldown' -import { wasm } from 'rolldown-plugin-wasm' - -export default defineConfig({ - plugins: [wasm({ wasmBindgen: true })], -}) -``` - ### TypeScript Support ```jsonc @@ -107,19 +109,6 @@ export default defineConfig({ export type TargetEnv = 'auto' | 'auto-inline' | 'browser' | 'node' export interface Options { - /** - * A glob pattern, or array of patterns, which specifies the files in the build the plugin - * should operate on. - * - * @default /\.wasm(\?sync)?$/ - */ - include?: Arrayable - /** - * A glob pattern, or array of patterns, which specifies the files in the build the plugin - * should _ignore_. - * By default no files are ignored. - */ - exclude?: Arrayable /** * The maximum file size for inline files. If a file exceeds this limit, it will be copied to the destination folder and loaded from a separate file at runtime. * If `maxFileSize` is set to `0` all files will be copied. @@ -141,10 +130,6 @@ export interface Options { * Configures what code is emitted to instantiate the Wasm (both inline and separate) */ targetEnv?: TargetEnv - /** - * Enable `wasm-bindgen` support - */ - wasmBindgen?: boolean } ``` @@ -160,6 +145,8 @@ export interface Options { [MIT](./LICENSE) License © 2026-PRESENT [Kevin Deng](https://github.com/sxzz) +[MIT](https://github.com/Menci/vite-plugin-wasm/blob/main/LICENSE) Copyright (c) 2022 Menci + [MIT](https://github.com/rollup/plugins/blob/master/LICENSE) License Copyright (c) 2019 [RollupJS Plugin Contributors](https://github.com/rollup/plugins/graphs/contributors) diff --git a/package.json b/package.json index 821f9bf..86da35a 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,9 @@ "release": "bumpp", "prepublishOnly": "pnpm run build" }, + "peerDependencies": { + "rolldown": "^1.0.0-beta.58" + }, "devDependencies": { "@antfu/utils": "^9.3.0", "@swc/wasm": "^1.15.8", diff --git a/src/helper.ts b/src/helper.ts index 9b120d6..d5f72c2 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -8,7 +8,7 @@ const { readFile } = process.getBuiltinModule('fs/promises') const path = process.getBuiltinModule('path') return readFile(path.resolve(import.meta.dirname, filepath)).then( - (buffer) => instantiateOrCompile(buffer, imports) + (buffer) => instantiate(buffer, imports) ) ` @@ -18,7 +18,7 @@ buf = Buffer.from(src, 'base64') ` const browserFilePath = ` -return instantiateOrCompile(fetch(filepath), imports, true); +return instantiate(fetch(filepath), imports, true); ` const browserDecode = ` @@ -92,24 +92,18 @@ const envModule = (env: TargetEnv) => { export const getHelpersModule = (env: TargetEnv) => ` export function loadWasmModule(sync, filepath, src, imports) { - function instantiateOrCompile(source, imports, stream) { + function instantiate(source, imports, stream) { const instantiate = stream ? WebAssembly.instantiateStreaming : WebAssembly.instantiate; - const compile = stream ? WebAssembly.compileStreaming : WebAssembly.compile; - - if (imports) { - return instantiate(source, imports).then(({ instance }) => instance) - } else { - return compile(source) - } + return instantiate(source, imports).then(({ instance }) => instance) } ${envModule(env)} if (sync) { const mod = new WebAssembly.Module(buf) - return imports ? new WebAssembly.Instance(mod, imports) : mod + return new WebAssembly.Instance(mod, imports) } else { - return instantiateOrCompile(buf, imports) + return instantiate(buf, imports) } } ` diff --git a/src/index.ts b/src/index.ts index b146a0a..22d1437 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,8 +2,9 @@ import { Buffer } from 'node:buffer' import { createHash } from 'node:crypto' import { readFile } from 'node:fs/promises' import path from 'node:path' -import { toArray } from '@antfu/utils' +import { id, include, or } from 'rolldown/filter' import { getHelpersModule, HELPERS_ID, HELPERS_ID_RE } from './helper' +import { parseWasm, type WasmInfo } from './wasm-parser' import type { Options } from './options' import type { Plugin } from 'rolldown' @@ -12,17 +13,12 @@ function cleanUrl(url: string): string { return url.replace(postfixRE, '') } -const RE_SYNC = /\?sync$/ - export function wasm(options: Options = {}): Plugin { let { - include = [/\.wasm(\?sync)?$/], - exclude, maxFileSize = 14 * 1024, publicPath = '', targetEnv, fileName = '[hash][extname]', - wasmBindgen, } = options const copies: Record< @@ -53,40 +49,45 @@ export function wasm(options: Options = {}): Plugin { }, resolveId: { - filter: { id: [HELPERS_ID_RE, RE_SYNC] }, + filter: [ + include(or(id(HELPERS_ID_RE), id(/\.wasm$/, { cleanUrl: true }))), + ], + async handler(id, ...args) { - if (RE_SYNC.test(id)) { - const resolved = await this.resolve(cleanUrl(id), ...args) - if (resolved) { - resolved.meta.wasmSync = true - } - return resolved + if (id === HELPERS_ID) { + return id + } + + const query = id.split('?')[1] + if (!query) return + + const resolved = await this.resolve(cleanUrl(id), ...args) + if (resolved) { + resolved.id += `?${query}` } - return id + + return resolved }, }, load: { - filter: { - id: { - include: [HELPERS_ID_RE, ...toArray(include)], - exclude, - }, - }, + filter: [ + include(or(id(HELPERS_ID_RE), id(/\.wasm$/, { cleanUrl: true }))), + ], + async handler(id) { if (id === HELPERS_ID) { return getHelpersModule(targetEnv || 'auto') } - this.addWatchFile(id) + const file = cleanUrl(id) + const buffer = await readFile(file) + const params = new URLSearchParams(id.split('?')[1]) + const isInit = params.has('init') - const mod = this.getModuleInfo(id)! - const buffer = await readFile(id) - - if (wasmBindgen) { - mod.meta.wasmExportNames = WebAssembly.Module.exports( - await WebAssembly.compile(buffer), - ).map((e) => e.name) + this.addWatchFile(file) + if (!isInit) { + this.getModuleInfo(id)!.meta.wasmInfo = await parseWasm(buffer) } if (targetEnv === 'auto-inline') { @@ -109,7 +110,8 @@ export function wasm(options: Options = {}): Plugin { const publicFilepath = `${publicPath}${outputFileName}` // only copy if the file is not marked `sync`, `sync` files are always inlined - if (!mod.meta.wasmSync) { + const query = new URLSearchParams(id.split('?')[1]) + if (!query.has('sync')) { copies[id] = { filename: outputFileName, publicFilepath, @@ -123,10 +125,12 @@ export function wasm(options: Options = {}): Plugin { }, transform: { - filter: { id: { include, exclude } }, + filter: [include(id(/\.wasm$/, { cleanUrl: true }))], handler(code, id) { - const mod = this.getModuleInfo(id)! - const isSync = !!mod.meta.wasmSync + const params = new URLSearchParams(id.split('?')[1]) + const isSync = params.has('sync') + const isInit = params.has('init') + const publicFilepath = copies[id] ? `'${copies[id].publicFilepath}'` : null @@ -142,22 +146,38 @@ export function wasm(options: Options = {}): Plugin { } let codegen = `import { loadWasmModule } from ${JSON.stringify(HELPERS_ID)} -function init(imports) { - return loadWasmModule(${isSync}, ${publicFilepath}, ${src}, imports) -}\n` - - if (wasmBindgen) { - const names = mod.meta.wasmExportNames as string[] - codegen += 'const instance = /* @__PURE__ */ await init({})\n' - codegen += names + ${isInit ? 'export default ' : ''}function __wasm_init(imports) { + return loadWasmModule(${isSync}, ${publicFilepath}, ${src}, imports) + }\n` + + const mod = this.getModuleInfo(id)! + + if (!isInit) { + const wasmInfo = mod.meta.wasmInfo as WasmInfo + codegen += wasmInfo.imports.map(({ from }, i) => { + return `import * as _wasmImport_${i} from ${JSON.stringify(from)};` + }) + + const importObject = wasmInfo.imports.map(({ from, names }, i) => { + return { + key: JSON.stringify(from), + value: names.map((name) => { + return { + key: JSON.stringify(name), + value: `_wasmImport_${i}[${JSON.stringify(name)}]`, + } + }), + } + }) + codegen += `const instance = /* @__PURE__ */ await __wasm_init(${codegenSimpleObject(importObject)});` + + codegen += wasmInfo.exports .map((name) => { - return `export const ${name} = instance.exports[${JSON.stringify(name)}]` + return `export ${name === 'default' ? 'default' : `const ${name} =`} /* @__PURE__ */ instance.exports.${name}` }) .join('\n') } - codegen += `\nexport default init` - return { map: { mappings: '' }, code: codegen, @@ -177,3 +197,22 @@ function init(imports) { }, } } + +type SimpleObject = SimpleObjectKeyValue[] + +interface SimpleObjectKeyValue { + key: string + value: string | SimpleObject +} + +function codegenSimpleObject(obj: SimpleObject): string { + return `{ ${codegenSimpleObjectKeyValue(obj)} }` +} + +function codegenSimpleObjectKeyValue(obj: SimpleObject): string { + return obj + .map(({ key, value }) => { + return `${key}: ${typeof value === 'string' ? value : codegenSimpleObject(value)}` + }) + .join(',\n') +} diff --git a/src/options.ts b/src/options.ts index ec2fbd2..2870768 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1,5 +1,3 @@ -import type { Arrayable } from '@antfu/utils' - /** * - `"auto"` will determine the environment at runtime and invoke the correct methods accordingly * - `"auto-inline"` always inlines the WASM and will decode it according to the environment @@ -9,19 +7,6 @@ import type { Arrayable } from '@antfu/utils' export type TargetEnv = 'auto' | 'auto-inline' | 'browser' | 'node' export interface Options { - /** - * A glob pattern, or array of patterns, which specifies the files in the build the plugin - * should operate on. - * - * @default /\.wasm(\?sync)?$/ - */ - include?: Arrayable - /** - * A glob pattern, or array of patterns, which specifies the files in the build the plugin - * should _ignore_. - * By default no files are ignored. - */ - exclude?: Arrayable /** * The maximum file size for inline files. If a file exceeds this limit, it will be copied to the destination folder and loaded from a separate file at runtime. * If `maxFileSize` is set to `0` all files will be copied. @@ -43,8 +28,4 @@ export interface Options { * Configures what code is emitted to instantiate the Wasm (both inline and separate) */ targetEnv?: TargetEnv - /** - * Enable wasm-bindgen support - */ - wasmBindgen?: boolean } diff --git a/src/wasm-parser.ts b/src/wasm-parser.ts new file mode 100644 index 0000000..5cc4079 --- /dev/null +++ b/src/wasm-parser.ts @@ -0,0 +1,34 @@ +// MIT license - https://github.com/Menci/vite-plugin-wasm/blob/main/LICENSE + +export interface WasmInfo { + imports: { + from: string + names: string[] + }[] + exports: string[] +} + +export async function parseWasm(buffer: BufferSource): Promise { + try { + const wasmModule = await WebAssembly.compile(buffer) + const imports = Object.entries( + WebAssembly.Module.imports(wasmModule).reduce( + (result, item) => ({ + ...result, + [item.module]: [...(result[item.module] || []), item.name], + }), + {} as Record, + ), + ).map(([from, names]) => ({ from, names })) + + const exports = WebAssembly.Module.exports(wasmModule).map( + (item) => item.name, + ) + + return { imports, exports } + } catch (error: any) { + throw new Error(`Failed to parse WASM file: ${error.message}`, { + cause: error, + }) + } +} diff --git a/tests/__snapshots__/init/browser.snap b/tests/__snapshots__/init/browser.snap new file mode 100644 index 0000000..8417b50 --- /dev/null +++ b/tests/__snapshots__/init/browser.snap @@ -0,0 +1,39 @@ +// b62665e669ab1545.wasm?init +[BINARY] +// init.js +//#region \0wasm-helpers.js +function loadWasmModule(sync, filepath, src, imports) { + function instantiate(source, imports$1, stream) { + const instantiate$1 = stream ? WebAssembly.instantiateStreaming : WebAssembly.instantiate; + return instantiate$1(source, imports$1).then(({ instance }) => instance); + } + let buf = null; + if (filepath) { + return instantiate(fetch(filepath), imports, true); + } + const raw = globalThis.atob(src); + const len = raw.length; + buf = new Uint8Array(new ArrayBuffer(len)); + for (let i = 0; i < len; i++) { + buf[i] = raw.charCodeAt(i); + } + if (sync) { + const mod = new WebAssembly.Module(buf); + return new WebAssembly.Instance(mod, imports); + } else { + return instantiate(buf, imports); + } +} + +//#endregion +//#region tests/fixtures/add.wasm?init +function __wasm_init(imports) { + return loadWasmModule(false, "b62665e669ab1545.wasm?init", null, imports); +} + +//#endregion +//#region tests/fixtures/init.js +var init_default = __wasm_init; + +//#endregion +export { init_default as default }; \ No newline at end of file diff --git a/tests/__snapshots__/init/neutral.snap b/tests/__snapshots__/init/neutral.snap new file mode 100644 index 0000000..309d15d --- /dev/null +++ b/tests/__snapshots__/init/neutral.snap @@ -0,0 +1,49 @@ +// b62665e669ab1545.wasm?init +[BINARY] +// init.js +//#region \0wasm-helpers.js +function loadWasmModule(sync, filepath, src, imports) { + function instantiate(source, imports$1, stream) { + const instantiate$1 = stream ? WebAssembly.instantiateStreaming : WebAssembly.instantiate; + return instantiate$1(source, imports$1).then(({ instance }) => instance); + } + let buf = null; + const isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null; + if (filepath && isNode) { + const { readFile } = process.getBuiltinModule("fs/promises"); + const path = process.getBuiltinModule("path"); + return readFile(path.resolve(import.meta.dirname, filepath)).then((buffer) => instantiate(buffer, imports)); + } else if (filepath) { + return instantiate(fetch(filepath), imports, true); + } + if (isNode) { + const { Buffer } = process.getBuiltinModule("buffer"); + buf = Buffer.from(src, "base64"); + } else { + const raw = globalThis.atob(src); + const len = raw.length; + buf = new Uint8Array(new ArrayBuffer(len)); + for (let i = 0; i < len; i++) { + buf[i] = raw.charCodeAt(i); + } + } + if (sync) { + const mod = new WebAssembly.Module(buf); + return new WebAssembly.Instance(mod, imports); + } else { + return instantiate(buf, imports); + } +} + +//#endregion +//#region tests/fixtures/add.wasm?init +function __wasm_init(imports) { + return loadWasmModule(false, "b62665e669ab1545.wasm?init", null, imports); +} + +//#endregion +//#region tests/fixtures/init.js +var init_default = __wasm_init; + +//#endregion +export { init_default as default }; \ No newline at end of file diff --git a/tests/__snapshots__/init/node.snap b/tests/__snapshots__/init/node.snap new file mode 100644 index 0000000..897009e --- /dev/null +++ b/tests/__snapshots__/init/node.snap @@ -0,0 +1,37 @@ +// b62665e669ab1545.wasm?init +[BINARY] +// init.js +//#region \0wasm-helpers.js +function loadWasmModule(sync, filepath, src, imports) { + function instantiate(source, imports$1, stream) { + const instantiate$1 = stream ? WebAssembly.instantiateStreaming : WebAssembly.instantiate; + return instantiate$1(source, imports$1).then(({ instance }) => instance); + } + let buf = null; + if (filepath) { + const { readFile } = process.getBuiltinModule("fs/promises"); + const path = process.getBuiltinModule("path"); + return readFile(path.resolve(import.meta.dirname, filepath)).then((buffer) => instantiate(buffer, imports)); + } + const { Buffer } = process.getBuiltinModule("buffer"); + buf = Buffer.from(src, "base64"); + if (sync) { + const mod = new WebAssembly.Module(buf); + return new WebAssembly.Instance(mod, imports); + } else { + return instantiate(buf, imports); + } +} + +//#endregion +//#region tests/fixtures/add.wasm?init +function __wasm_init(imports) { + return loadWasmModule(false, "b62665e669ab1545.wasm?init", null, imports); +} + +//#endregion +//#region tests/fixtures/init.js +var init_default = __wasm_init; + +//#endregion +export { init_default as default }; \ No newline at end of file diff --git a/tests/__snapshots__/rolldown.test.ts.snap b/tests/__snapshots__/rolldown.test.ts.snap index d0d63a0..9ba8c34 100644 --- a/tests/__snapshots__/rolldown.test.ts.snap +++ b/tests/__snapshots__/rolldown.test.ts.snap @@ -1,380 +1,28 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`rolldown > basic.js > platform = browser 1`] = ` -"// b62665e669ab1545.wasm -[BINARY] -// basic.js -//#region \\0wasm-helpers.js -function loadWasmModule(sync, filepath, src, imports) { - function instantiateOrCompile(source, imports$1, stream) { - const instantiate = stream ? WebAssembly.instantiateStreaming : WebAssembly.instantiate; - const compile = stream ? WebAssembly.compileStreaming : WebAssembly.compile; - if (imports$1) { - return instantiate(source, imports$1).then(({ instance }) => instance); - } else { - return compile(source); - } - } - let buf = null; - if (filepath) { - return instantiateOrCompile(fetch(filepath), imports, true); - } - const raw = globalThis.atob(src); - const len = raw.length; - buf = new Uint8Array(new ArrayBuffer(len)); - for (let i = 0; i < len; i++) { - buf[i] = raw.charCodeAt(i); - } - if (sync) { - const mod = new WebAssembly.Module(buf); - return imports ? new WebAssembly.Instance(mod, imports) : mod; - } else { - return instantiateOrCompile(buf, imports); - } -} - -//#endregion -//#region tests/fixtures/add.wasm -function init(imports) { - return loadWasmModule(false, "b62665e669ab1545.wasm", null, imports); -} -var add_default = init; - -//#endregion -//#region tests/fixtures/basic.js -var basic_default = add_default; - -//#endregion -export { basic_default as default };" -`; - -exports[`rolldown > basic.js > platform = neutral 1`] = ` -"// b62665e669ab1545.wasm -[BINARY] -// basic.js -//#region \\0wasm-helpers.js -function loadWasmModule(sync, filepath, src, imports) { - function instantiateOrCompile(source, imports$1, stream) { - const instantiate = stream ? WebAssembly.instantiateStreaming : WebAssembly.instantiate; - const compile = stream ? WebAssembly.compileStreaming : WebAssembly.compile; - if (imports$1) { - return instantiate(source, imports$1).then(({ instance }) => instance); - } else { - return compile(source); - } - } - let buf = null; - const isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null; - if (filepath && isNode) { - const { readFile } = process.getBuiltinModule("fs/promises"); - const path = process.getBuiltinModule("path"); - return readFile(path.resolve(import.meta.dirname, filepath)).then((buffer) => instantiateOrCompile(buffer, imports)); - } else if (filepath) { - return instantiateOrCompile(fetch(filepath), imports, true); - } - if (isNode) { - const { Buffer } = process.getBuiltinModule("buffer"); - buf = Buffer.from(src, "base64"); - } else { - const raw = globalThis.atob(src); - const len = raw.length; - buf = new Uint8Array(new ArrayBuffer(len)); - for (let i = 0; i < len; i++) { - buf[i] = raw.charCodeAt(i); - } - } - if (sync) { - const mod = new WebAssembly.Module(buf); - return imports ? new WebAssembly.Instance(mod, imports) : mod; - } else { - return instantiateOrCompile(buf, imports); - } -} - -//#endregion -//#region tests/fixtures/add.wasm -function init(imports) { - return loadWasmModule(false, "b62665e669ab1545.wasm", null, imports); -} -var add_default = init; - -//#endregion -//#region tests/fixtures/basic.js -var basic_default = add_default; - -//#endregion -export { basic_default as default };" -`; - -exports[`rolldown > basic.js > platform = node 1`] = ` -"// b62665e669ab1545.wasm -[BINARY] -// basic.js -//#region \\0wasm-helpers.js -function loadWasmModule(sync, filepath, src, imports) { - function instantiateOrCompile(source, imports$1, stream) { - const instantiate = stream ? WebAssembly.instantiateStreaming : WebAssembly.instantiate; - const compile = stream ? WebAssembly.compileStreaming : WebAssembly.compile; - if (imports$1) { - return instantiate(source, imports$1).then(({ instance }) => instance); - } else { - return compile(source); - } - } - let buf = null; - if (filepath) { - const { readFile } = process.getBuiltinModule("fs/promises"); - const path = process.getBuiltinModule("path"); - return readFile(path.resolve(import.meta.dirname, filepath)).then((buffer) => instantiateOrCompile(buffer, imports)); - } - const { Buffer } = process.getBuiltinModule("buffer"); - buf = Buffer.from(src, "base64"); - if (sync) { - const mod = new WebAssembly.Module(buf); - return imports ? new WebAssembly.Instance(mod, imports) : mod; - } else { - return instantiateOrCompile(buf, imports); - } -} - -//#endregion -//#region tests/fixtures/add.wasm -function init(imports) { - return loadWasmModule(false, "b62665e669ab1545.wasm", null, imports); -} -var add_default = init; - -//#endregion -//#region tests/fixtures/basic.js -var basic_default = add_default; - -//#endregion -export { basic_default as default };" -`; - -exports[`rolldown > sync.js > platform = browser 1`] = ` -"// sync.js -//#region \\0wasm-helpers.js -function loadWasmModule(sync, filepath, src, imports) { - function instantiateOrCompile(source, imports$1, stream) { - const instantiate = stream ? WebAssembly.instantiateStreaming : WebAssembly.instantiate; - const compile = stream ? WebAssembly.compileStreaming : WebAssembly.compile; - if (imports$1) { - return instantiate(source, imports$1).then(({ instance }) => instance); - } else { - return compile(source); - } - } - let buf = null; - if (filepath) { - return instantiateOrCompile(fetch(filepath), imports, true); - } - const raw = globalThis.atob(src); - const len = raw.length; - buf = new Uint8Array(new ArrayBuffer(len)); - for (let i = 0; i < len; i++) { - buf[i] = raw.charCodeAt(i); - } - if (sync) { - const mod = new WebAssembly.Module(buf); - return imports ? new WebAssembly.Instance(mod, imports) : mod; - } else { - return instantiateOrCompile(buf, imports); - } -} - -//#endregion -//#region tests/fixtures/add.wasm -function init(imports) { - return loadWasmModule(true, null, "AGFzbQEAAAABh4CAgAABYAJ/fwF/A4KAgIAAAQAFg4CAgAABABEHkICAgAACBm1lbW9yeQIAA2FkZAAAComAgIAAAQcAIAEgAGoLC7aEgIAAAgBBgIDAAAsIAQAAAAIAAAAAQYiAwAALnAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkYCAgAAEbmFtZQGGgICAAAEAA2FkZAD+gICAAAlwcm9kdWNlcnMCCGxhbmd1YWdlAQRSdXN0BDIwMTgMcHJvY2Vzc2VkLWJ5AwVydXN0Yx0xLjM0LjEgKGZjNTBmMzI4YiAyMDE5LTA0LTI0KQZ3YWxydXMFMC43LjAMd2FzbS1iaW5kZ2VuEjAuMi40NSAoNmZhNmFmMjNiKQ==", imports); -} -var add_default = init; - -//#endregion -//#region tests/fixtures/sync.js -var sync_default = add_default; - -//#endregion -export { sync_default as default };" -`; +exports[`rolldown > init-sync.js 1`] = ` +"// init-sync.js +import { loadWasmModule } from "\\u0000wasm-helpers.js"; -exports[`rolldown > sync.js > platform = neutral 1`] = ` -"// sync.js -//#region \\0wasm-helpers.js -function loadWasmModule(sync, filepath, src, imports) { - function instantiateOrCompile(source, imports$1, stream) { - const instantiate = stream ? WebAssembly.instantiateStreaming : WebAssembly.instantiate; - const compile = stream ? WebAssembly.compileStreaming : WebAssembly.compile; - if (imports$1) { - return instantiate(source, imports$1).then(({ instance }) => instance); - } else { - return compile(source); - } - } - let buf = null; - const isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null; - if (filepath && isNode) { - const { readFile } = process.getBuiltinModule("fs/promises"); - const path = process.getBuiltinModule("path"); - return readFile(path.resolve(import.meta.dirname, filepath)).then((buffer) => instantiateOrCompile(buffer, imports)); - } else if (filepath) { - return instantiateOrCompile(fetch(filepath), imports, true); - } - if (isNode) { - const { Buffer } = process.getBuiltinModule("buffer"); - buf = Buffer.from(src, "base64"); - } else { - const raw = globalThis.atob(src); - const len = raw.length; - buf = new Uint8Array(new ArrayBuffer(len)); - for (let i = 0; i < len; i++) { - buf[i] = raw.charCodeAt(i); - } - } - if (sync) { - const mod = new WebAssembly.Module(buf); - return imports ? new WebAssembly.Instance(mod, imports) : mod; - } else { - return instantiateOrCompile(buf, imports); - } -} - -//#endregion -//#region tests/fixtures/add.wasm -function init(imports) { +//#region tests/fixtures/add.wasm?init&sync +function __wasm_init(imports) { return loadWasmModule(true, null, "AGFzbQEAAAABh4CAgAABYAJ/fwF/A4KAgIAAAQAFg4CAgAABABEHkICAgAACBm1lbW9yeQIAA2FkZAAAComAgIAAAQcAIAEgAGoLC7aEgIAAAgBBgIDAAAsIAQAAAAIAAAAAQYiAwAALnAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkYCAgAAEbmFtZQGGgICAAAEAA2FkZAD+gICAAAlwcm9kdWNlcnMCCGxhbmd1YWdlAQRSdXN0BDIwMTgMcHJvY2Vzc2VkLWJ5AwVydXN0Yx0xLjM0LjEgKGZjNTBmMzI4YiAyMDE5LTA0LTI0KQZ3YWxydXMFMC43LjAMd2FzbS1iaW5kZ2VuEjAuMi40NSAoNmZhNmFmMjNiKQ==", imports); } -var add_default = init; //#endregion -//#region tests/fixtures/sync.js -var sync_default = add_default; +//#region tests/fixtures/init-sync.js +var init_sync_default = __wasm_init; //#endregion -export { sync_default as default };" +export { init_sync_default as default };" `; -exports[`rolldown > sync.js > platform = node 1`] = ` -"// sync.js -//#region \\0wasm-helpers.js -function loadWasmModule(sync, filepath, src, imports) { - function instantiateOrCompile(source, imports$1, stream) { - const instantiate = stream ? WebAssembly.instantiateStreaming : WebAssembly.instantiate; - const compile = stream ? WebAssembly.compileStreaming : WebAssembly.compile; - if (imports$1) { - return instantiate(source, imports$1).then(({ instance }) => instance); - } else { - return compile(source); - } - } - let buf = null; - if (filepath) { - const { readFile } = process.getBuiltinModule("fs/promises"); - const path = process.getBuiltinModule("path"); - return readFile(path.resolve(import.meta.dirname, filepath)).then((buffer) => instantiateOrCompile(buffer, imports)); - } - const { Buffer } = process.getBuiltinModule("buffer"); - buf = Buffer.from(src, "base64"); - if (sync) { - const mod = new WebAssembly.Module(buf); - return imports ? new WebAssembly.Instance(mod, imports) : mod; - } else { - return instantiateOrCompile(buf, imports); - } -} - -//#endregion -//#region tests/fixtures/add.wasm -function init(imports) { - return loadWasmModule(true, null, "AGFzbQEAAAABh4CAgAABYAJ/fwF/A4KAgIAAAQAFg4CAgAABABEHkICAgAACBm1lbW9yeQIAA2FkZAAAComAgIAAAQcAIAEgAGoLC7aEgIAAAgBBgIDAAAsIAQAAAAIAAAAAQYiAwAALnAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkYCAgAAEbmFtZQGGgICAAAEAA2FkZAD+gICAAAlwcm9kdWNlcnMCCGxhbmd1YWdlAQRSdXN0BDIwMTgMcHJvY2Vzc2VkLWJ5AwVydXN0Yx0xLjM0LjEgKGZjNTBmMzI4YiAyMDE5LTA0LTI0KQZ3YWxydXMFMC43LjAMd2FzbS1iaW5kZ2VuEjAuMi40NSAoNmZhNmFmMjNiKQ==", imports); -} -var add_default = init; - -//#endregion -//#region tests/fixtures/sync.js -var sync_default = add_default; - -//#endregion -export { sync_default as default };" -`; - -exports[`rolldown > wasm-bindgen.js > platform = browser 1`] = ` +exports[`rolldown > wasm-module.js 1`] = ` "// b62665e669ab1545.wasm [BINARY] -// wasm-bindgen.js -//#region rolldown:runtime -var __defProp = Object.defineProperty; -var __exportAll = (all, symbols) => { - let target = {}; - for (var name in all) { - __defProp(target, name, { - get: all[name], - enumerable: true - }); - } - if (symbols) { - __defProp(target, Symbol.toStringTag, { value: "Module" }); - } - return target; -}; - -//#endregion -//#region \\0wasm-helpers.js -function loadWasmModule(sync, filepath, src, imports) { - function instantiateOrCompile(source, imports$1, stream) { - const instantiate = stream ? WebAssembly.instantiateStreaming : WebAssembly.instantiate; - const compile = stream ? WebAssembly.compileStreaming : WebAssembly.compile; - if (imports$1) { - return instantiate(source, imports$1).then(({ instance: instance$1 }) => instance$1); - } else { - return compile(source); - } - } - let buf = null; - if (filepath) { - return instantiateOrCompile(fetch(filepath), imports, true); - } - const raw = globalThis.atob(src); - const len = raw.length; - buf = new Uint8Array(new ArrayBuffer(len)); - for (let i = 0; i < len; i++) { - buf[i] = raw.charCodeAt(i); - } - if (sync) { - const mod = new WebAssembly.Module(buf); - return imports ? new WebAssembly.Instance(mod, imports) : mod; - } else { - return instantiateOrCompile(buf, imports); - } -} - -//#endregion -//#region tests/fixtures/add.wasm -var add_exports = /* @__PURE__ */ __exportAll({ - add: () => add, - default: () => add_default, - memory: () => memory -}); -function init(imports) { - return loadWasmModule(false, "b62665e669ab1545.wasm", null, imports); -} -const instance = await init({}); -const memory = instance.exports["memory"]; -const add = instance.exports["add"]; -var add_default = init; - -//#endregion -//#region tests/fixtures/wasm-bindgen.js -var wasm_bindgen_default = add_exports; - -//#endregion -export { wasm_bindgen_default as default };" -`; +// wasm-module.js +import { loadWasmModule } from "\\u0000wasm-helpers.js"; -exports[`rolldown > wasm-bindgen.js > platform = neutral 1`] = ` -"// b62665e669ab1545.wasm -[BINARY] -// wasm-bindgen.js //#region rolldown:runtime var __defProp = Object.defineProperty; var __exportAll = (all, symbols) => { @@ -391,138 +39,23 @@ var __exportAll = (all, symbols) => { return target; }; -//#endregion -//#region \\0wasm-helpers.js -function loadWasmModule(sync, filepath, src, imports) { - function instantiateOrCompile(source, imports$1, stream) { - const instantiate = stream ? WebAssembly.instantiateStreaming : WebAssembly.instantiate; - const compile = stream ? WebAssembly.compileStreaming : WebAssembly.compile; - if (imports$1) { - return instantiate(source, imports$1).then(({ instance: instance$1 }) => instance$1); - } else { - return compile(source); - } - } - let buf = null; - const isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null; - if (filepath && isNode) { - const { readFile } = process.getBuiltinModule("fs/promises"); - const path = process.getBuiltinModule("path"); - return readFile(path.resolve(import.meta.dirname, filepath)).then((buffer) => instantiateOrCompile(buffer, imports)); - } else if (filepath) { - return instantiateOrCompile(fetch(filepath), imports, true); - } - if (isNode) { - const { Buffer: Buffer$1 } = process.getBuiltinModule("buffer"); - buf = Buffer$1.from(src, "base64"); - } else { - const raw = globalThis.atob(src); - const len = raw.length; - buf = new Uint8Array(new ArrayBuffer(len)); - for (let i = 0; i < len; i++) { - buf[i] = raw.charCodeAt(i); - } - } - if (sync) { - const mod = new WebAssembly.Module(buf); - return imports ? new WebAssembly.Instance(mod, imports) : mod; - } else { - return instantiateOrCompile(buf, imports); - } -} - -//#endregion -//#region tests/fixtures/add.wasm -var add_exports = /* @__PURE__ */ __exportAll({ - add: () => add, - default: () => add_default, - memory: () => memory -}); -function init(imports) { - return loadWasmModule(false, "b62665e669ab1545.wasm", null, imports); -} -const instance = await init({}); -const memory = instance.exports["memory"]; -const add = instance.exports["add"]; -var add_default = init; - -//#endregion -//#region tests/fixtures/wasm-bindgen.js -var wasm_bindgen_default = add_exports; - -//#endregion -export { wasm_bindgen_default as default };" -`; - -exports[`rolldown > wasm-bindgen.js > platform = node 1`] = ` -"// b62665e669ab1545.wasm -[BINARY] -// wasm-bindgen.js -import "node:module"; - -//#region rolldown:runtime -var __defProp = Object.defineProperty; -var __exportAll = (all, symbols) => { - let target = {}; - for (var name in all) { - __defProp(target, name, { - get: all[name], - enumerable: true - }); - } - if (symbols) { - __defProp(target, Symbol.toStringTag, { value: "Module" }); - } - return target; -}; - -//#endregion -//#region \\0wasm-helpers.js -function loadWasmModule(sync, filepath, src, imports) { - function instantiateOrCompile(source, imports$1, stream) { - const instantiate = stream ? WebAssembly.instantiateStreaming : WebAssembly.instantiate; - const compile = stream ? WebAssembly.compileStreaming : WebAssembly.compile; - if (imports$1) { - return instantiate(source, imports$1).then(({ instance: instance$1 }) => instance$1); - } else { - return compile(source); - } - } - let buf = null; - if (filepath) { - const { readFile } = process.getBuiltinModule("fs/promises"); - const path = process.getBuiltinModule("path"); - return readFile(path.resolve(import.meta.dirname, filepath)).then((buffer) => instantiateOrCompile(buffer, imports)); - } - const { Buffer: Buffer$1 } = process.getBuiltinModule("buffer"); - buf = Buffer$1.from(src, "base64"); - if (sync) { - const mod = new WebAssembly.Module(buf); - return imports ? new WebAssembly.Instance(mod, imports) : mod; - } else { - return instantiateOrCompile(buf, imports); - } -} - //#endregion //#region tests/fixtures/add.wasm var add_exports = /* @__PURE__ */ __exportAll({ add: () => add, - default: () => add_default, memory: () => memory }); -function init(imports) { +function __wasm_init(imports) { return loadWasmModule(false, "b62665e669ab1545.wasm", null, imports); } -const instance = await init({}); -const memory = instance.exports["memory"]; -const add = instance.exports["add"]; -var add_default = init; +const instance = await __wasm_init({}); +const memory = instance.exports.memory; +const add = instance.exports.add; //#endregion -//#region tests/fixtures/wasm-bindgen.js -var wasm_bindgen_default = add_exports; +//#region tests/fixtures/wasm-module.js +var wasm_module_default = add_exports; //#endregion -export { wasm_bindgen_default as default };" +export { wasm_module_default as default };" `; diff --git a/tests/e2e.test.ts b/tests/e2e.test.ts index 9fec441..6bd084e 100644 --- a/tests/e2e.test.ts +++ b/tests/e2e.test.ts @@ -5,7 +5,7 @@ import { readFile } from 'node:fs/promises' import path from 'node:path' import process from 'node:process' import { createContext, SourceTextModule } from 'node:vm' -import { rolldownBuild } from '@sxzz/test-utils' +import { rolldownBuild, testFixtures } from '@sxzz/test-utils' import { describe, expect, test } from 'vitest' import { wasm } from '../src' @@ -13,44 +13,43 @@ const wasmPath = path.resolve(import.meta.dirname, 'fixtures/add.wasm') const atob = (str: string) => Buffer.from(str, 'base64').toString('binary') describe('e2e', () => { - describe.each(['node', 'browser'] as const)('platform: %s', (platform) => { - test.each([true, false] as const)('sync = %s', async (sync) => { - await e2e(platform, sync) - }) + describe.each(['node', 'browser'] as const)( + 'platform: %s', + async (platform) => { + await testFixtures( + '*.js', + async (args, id) => { + await e2e(id, platform, 0) + }, + { + cwd: path.resolve(import.meta.dirname, 'fixtures'), + promise: true, + snapshot: false, + }, + ) - test('maxFileSize = 0', async () => { - await e2e(platform, false, 0) - }) - - test('with wasmBindgen', async () => { - await e2e(platform, false, undefined, true) - }) - }) + test('maxFileSize = 0', async () => { + await e2e( + path.resolve(import.meta.dirname, 'fixtures/init.js'), + platform, + 0, + ) + }) + }, + ) }) async function e2e( + entry: string, platform: 'node' | 'browser', - sync: boolean, maxFileSize?: number, - wasmBindgen?: boolean, ) { - const { chunks } = await rolldownBuild( - path.resolve( - import.meta.dirname, - sync - ? 'fixtures/sync.js' - : wasmBindgen - ? 'fixtures/wasm-bindgen.js' - : 'fixtures/basic.js', - ), - [wasm({ maxFileSize, wasmBindgen })], - { - platform, - treeshake: { - moduleSideEffects: false, - }, + const { chunks } = await rolldownBuild(entry, [wasm({ maxFileSize })], { + platform, + treeshake: { + moduleSideEffects: false, }, - ) + }) const code = chunks[0].code const mod = new SourceTextModule(code, { @@ -90,32 +89,21 @@ async function e2e( await mod.evaluate() const exported = (mod.namespace as any).default - let init: (imports?: any) => any - if (wasmBindgen) { - const { add } = (mod.namespace as any).default - expect(add(1, 2)).toBe(3) - init = exported.default - } else { - init = exported - } - - { - // module - const ret = init() - expect(ret).a(sync ? 'WebAssembly.Module' : 'Promise') - - const wasmModule: WebAssembly.Module = await ret - const instance = await WebAssembly.instantiate(wasmModule) - expect((instance.exports as any).add(1, 2)).toBe(3) - } + const init = entry.includes('init') + const sync = entry.includes('sync') - { - // instance - const ret = init({}) - expect(ret).a(sync ? 'WebAssembly.Instance' : 'Promise') + if (init) { + const ret = exported() + if (sync) { + expect(ret).a('WebAssembly.Instance') + } else { + await expect(ret).resolves.a('WebAssembly.Instance') + } - const instance = await ret - expect(instance).a('WebAssembly.Instance') + const instance: WebAssembly.Instance = await ret expect((instance.exports as any).add(1, 2)).toBe(3) + } else { + expect(Object.keys(exported)).toEqual(['add', 'memory']) + expect(exported.add(1, 2)).toBe(3) } } diff --git a/tests/fixtures/basic.js b/tests/fixtures/basic.js deleted file mode 100644 index 0028a0d..0000000 --- a/tests/fixtures/basic.js +++ /dev/null @@ -1,2 +0,0 @@ -import wasm from './add.wasm' -export default wasm diff --git a/tests/fixtures/init-sync.js b/tests/fixtures/init-sync.js new file mode 100644 index 0000000..57c8d25 --- /dev/null +++ b/tests/fixtures/init-sync.js @@ -0,0 +1,2 @@ +import wasm from './add.wasm?init&sync' +export default wasm diff --git a/tests/fixtures/init.js b/tests/fixtures/init.js new file mode 100644 index 0000000..f43dc86 --- /dev/null +++ b/tests/fixtures/init.js @@ -0,0 +1,2 @@ +import wasm from './add.wasm?init' +export default wasm diff --git a/tests/fixtures/sync.js b/tests/fixtures/sync.js deleted file mode 100644 index dff50ab..0000000 --- a/tests/fixtures/sync.js +++ /dev/null @@ -1,2 +0,0 @@ -import wasm from './add.wasm?sync' -export default wasm diff --git a/tests/fixtures/wasm-bindgen.js b/tests/fixtures/wasm-module.js similarity index 100% rename from tests/fixtures/wasm-bindgen.js rename to tests/fixtures/wasm-module.js diff --git a/tests/import.test-d.ts b/tests/import.test-d.ts index 34160d3..c6d3106 100644 --- a/tests/import.test-d.ts +++ b/tests/import.test-d.ts @@ -1,21 +1,15 @@ /// import { expectTypeOf } from 'vitest' -import init from './add.wasm' -import initSync from './add.wasm?sync' +import init from './add.wasm?init' +import initSync from './add.wasm?init&sync' { - const module = init() - expectTypeOf(module).toEqualTypeOf>() - - const instance = init({}) + const instance = init() expectTypeOf(instance).toEqualTypeOf>() } { - const module = initSync() - expectTypeOf(module).toEqualTypeOf() - const instance = initSync({}) expectTypeOf(instance).toEqualTypeOf() } diff --git a/tests/rolldown.test.ts b/tests/rolldown.test.ts index c1a4aff..513ce16 100644 --- a/tests/rolldown.test.ts +++ b/tests/rolldown.test.ts @@ -1,23 +1,19 @@ import path from 'node:path' import { rolldownBuild, testFixtures } from '@sxzz/test-utils' -import { describe } from 'vitest' +import { describe, expect } from 'vitest' import { wasm } from '../src' const { dirname } = import.meta describe('rolldown', async () => { await testFixtures( - '*.js', + 'init.js', async (args, id) => { - const { snapshot } = await rolldownBuild( - id, - [ - wasm({ - maxFileSize: 0, - wasmBindgen: id.includes('wasm-bindgen'), - }), - ], - { platform: args.platform }, + const { snapshot } = await rolldownBuild(id, [wasm({ maxFileSize: 0 })], { + platform: args.platform, + }) + await expect(snapshot).toMatchFileSnapshot( + path.resolve(dirname, '__snapshots__/init/', `${args.platform}.snap`), ) return snapshot }, @@ -25,6 +21,22 @@ describe('rolldown', async () => { cwd: path.resolve(dirname, 'fixtures'), promise: true, params: [['platform', ['node', 'browser', 'neutral']]], + snapshot: false, + }, + ) + + await testFixtures( + ['*.js', '!init.js'], + async (args, id) => { + const { snapshot } = await rolldownBuild(id, [wasm({ maxFileSize: 0 })], { + platform: 'browser', + external: ['\0wasm-helpers.js'], + }) + return snapshot + }, + { + cwd: path.resolve(dirname, 'fixtures'), + promise: true, }, ) }) diff --git a/tsdown.config.ts b/tsdown.config.ts index 1631107..7f96a14 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -1,9 +1,7 @@ import { nodeLib } from 'tsdown-preset-sxzz' export default nodeLib( - { - inlineDeps: ['@antfu/utils'], - }, + {}, { exports: { customExports(exports) { diff --git a/types.d.ts b/types.d.ts index 51613f6..26c62a4 100644 --- a/types.d.ts +++ b/types.d.ts @@ -1,13 +1,13 @@ -declare module '*.wasm' { - export default function ( - imports: WebAssembly.Imports, - ): Promise - - export default function wasm(imports?: undefined): Promise +// wasm?init +declare module '*.wasm?init' { + const initWasm: ( + imports?: WebAssembly.Imports, + ) => Promise + export default initWasm } -declare module '*.wasm?sync' { - export default function wasm( - imports: WebAssembly.Imports, - ): WebAssembly.Instance - export default function wasm(imports?: undefined): WebAssembly.Module + +// wasm?init&sync +declare module '*.wasm?init&sync' { + const initWasm: (imports?: WebAssembly.Imports) => WebAssembly.Instance + export default initWasm }