From 75c3bf65694bc89b395e03dabb81721871d24a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Fri, 20 May 2022 17:05:37 +0900 Subject: [PATCH] feat(wasm): new wasm plugin (`.wasm?init`) (#8219) --- docs/guide/features.md | 13 +++++++--- packages/vite/client.d.ts | 6 +++-- packages/vite/src/node/constants.ts | 1 - packages/vite/src/node/plugins/index.ts | 5 ++-- packages/vite/src/node/plugins/wasm.ts | 27 +++++++++++++++++--- playground/wasm/__tests__/wasm.spec.ts | 16 +++++++++++- playground/wasm/index.html | 34 ++++++++++++++++++++++--- playground/wasm/worker.js | 4 +-- 8 files changed, 87 insertions(+), 19 deletions(-) diff --git a/docs/guide/features.md b/docs/guide/features.md index bb8179e75073f5..ef0610111b9709 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -435,13 +435,13 @@ Note that variables only represent file names one level deep. If `file` is `'foo ## WebAssembly -Pre-compiled `.wasm` files can be directly imported - the default export will be an initialization function that returns a Promise of the exports object of the wasm instance: +Pre-compiled `.wasm` files can be imported with `?init` - the default export will be an initialization function that returns a Promise of the wasm instance: ```js -import init from './example.wasm' +import init from './example.wasm?init' -init().then((exports) => { - exports.test() +init().then((instance) => { + instance.exports.test() }) ``` @@ -461,6 +461,11 @@ init({ In the production build, `.wasm` files smaller than `assetInlineLimit` will be inlined as base64 strings. Otherwise, they will be copied to the dist directory as an asset and fetched on-demand. +::: warning +[ES Module Integration Proposal for WebAssembly](https://github.com/WebAssembly/esm-integration) is not currently supported. +Use [`vite-plugin-wasm`](https://github.com/Menci/vite-plugin-wasm) or other community plugins to handle this. +::: + ## Web Workers ### Import with Constructors diff --git a/packages/vite/client.d.ts b/packages/vite/client.d.ts index af8e6a6f9d1494..948d66d1d7ad74 100644 --- a/packages/vite/client.d.ts +++ b/packages/vite/client.d.ts @@ -152,8 +152,10 @@ declare module '*.otf' { } // other -declare module '*.wasm' { - const initWasm: (options: WebAssembly.Imports) => Promise +declare module '*.wasm?init' { + const initWasm: ( + options: WebAssembly.Imports + ) => Promise export default initWasm } declare module '*.webmanifest' { diff --git a/packages/vite/src/node/constants.ts b/packages/vite/src/node/constants.ts index 9612cd8c96460d..8f673c72f30fba 100644 --- a/packages/vite/src/node/constants.ts +++ b/packages/vite/src/node/constants.ts @@ -81,7 +81,6 @@ export const KNOWN_ASSET_TYPES = [ 'otf', // other - 'wasm', 'webmanifest', 'pdf', 'txt' diff --git a/packages/vite/src/node/plugins/index.ts b/packages/vite/src/node/plugins/index.ts index b4a298bc334d41..ac6cface14fbeb 100644 --- a/packages/vite/src/node/plugins/index.ts +++ b/packages/vite/src/node/plugins/index.ts @@ -10,7 +10,7 @@ import { cssPlugin, cssPostPlugin } from './css' import { assetPlugin } from './asset' import { clientInjectionsPlugin } from './clientInjections' import { buildHtmlPlugin, htmlInlineProxyPlugin } from './html' -import { wasmPlugin } from './wasm' +import { wasmFallbackPlugin, wasmHelperPlugin } from './wasm' import { modulePreloadPolyfillPlugin } from './modulePreloadPolyfill' import { webWorkerPlugin } from './worker' import { preAliasPlugin } from './preAlias' @@ -64,10 +64,11 @@ export async function resolvePlugins( }, isBuild ), - wasmPlugin(config), + wasmHelperPlugin(config), webWorkerPlugin(config), assetPlugin(config), ...normalPlugins, + wasmFallbackPlugin(), definePlugin(config), cssPostPlugin(config), config.build.ssr ? ssrRequireHookPlugin(config) : null, diff --git a/packages/vite/src/node/plugins/wasm.ts b/packages/vite/src/node/plugins/wasm.ts index 1f64434a5b9781..56aa56402df8e2 100644 --- a/packages/vite/src/node/plugins/wasm.ts +++ b/packages/vite/src/node/plugins/wasm.ts @@ -37,14 +37,14 @@ const wasmHelper = async (opts = {}, url: string) => { result = await WebAssembly.instantiate(buffer, opts) } } - return result.instance.exports + return result.instance } const wasmHelperCode = wasmHelper.toString() -export const wasmPlugin = (config: ResolvedConfig): Plugin => { +export const wasmHelperPlugin = (config: ResolvedConfig): Plugin => { return { - name: 'vite:wasm', + name: 'vite:wasm-helper', resolveId(id) { if (id === wasmHelperId) { @@ -57,7 +57,7 @@ export const wasmPlugin = (config: ResolvedConfig): Plugin => { return `export default ${wasmHelperCode}` } - if (!id.endsWith('.wasm')) { + if (!id.endsWith('.wasm?init')) { return } @@ -70,3 +70,22 @@ export default opts => initWasm(opts, ${JSON.stringify(url)}) } } } + +export const wasmFallbackPlugin = (): Plugin => { + return { + name: 'vite:wasm-fallback', + + async load(id) { + if (!id.endsWith('.wasm')) { + return + } + + throw new Error( + '"ESM integration proposal for Wasm" is not supported currently. ' + + 'Use vite-plugin-wasm or other community plugins to handle this. ' + + 'Alternatively, you can use `.wasm?init` or `.wasm?url`. ' + + 'See https://vitejs.dev/guide/features.html#webassembly for more details.' + ) + } + } +} diff --git a/playground/wasm/__tests__/wasm.spec.ts b/playground/wasm/__tests__/wasm.spec.ts index 9a23378ecbf8dc..ad4438992609f9 100644 --- a/playground/wasm/__tests__/wasm.spec.ts +++ b/playground/wasm/__tests__/wasm.spec.ts @@ -1,4 +1,4 @@ -import { page, untilUpdated } from '~utils' +import { isBuild, page, untilUpdated } from '~utils' test('should work when inlined', async () => { await page.click('.inline-wasm .run') @@ -10,6 +10,20 @@ test('should work when output', async () => { await untilUpdated(() => page.textContent('.output-wasm .result'), '24') }) +test('init function returns WebAssembly.Instance', async () => { + await page.click('.init-returns-instance .run') + await untilUpdated( + () => page.textContent('.init-returns-instance .result'), + 'true' + ) +}) + +test('?url', async () => { + expect(await page.textContent('.url')).toMatch( + isBuild ? 'data:application/wasm' : '/light.wasm' + ) +}) + test('should work when wasm in worker', async () => { await untilUpdated(() => page.textContent('.worker-wasm .result'), '3') }) diff --git a/playground/wasm/index.html b/playground/wasm/index.html index ecb0b66e913fbb..2af0fda6847f2f 100644 --- a/playground/wasm/index.html +++ b/playground/wasm/index.html @@ -12,14 +12,25 @@

When wasm is output, result should be 24

+
+

init function returns WebAssembly.Instance

+ + +
+ +
+

Importing as URL

+ +
+

worker wasm

diff --git a/playground/wasm/worker.js b/playground/wasm/worker.js index a483865bd42bff..dbbc0640226406 100644 --- a/playground/wasm/worker.js +++ b/playground/wasm/worker.js @@ -1,5 +1,5 @@ -import init from './add.wasm' -init().then((exports) => { +import init from './add.wasm?init' +init().then(({ exports }) => { // eslint-disable-next-line no-undef self.postMessage({ result: exports.add(1, 2) }) })