Skip to content

Commit 75c3bf6

Browse files
authoredMay 20, 2022
feat(wasm): new wasm plugin (.wasm?init) (#8219)
1 parent a0ee4ff commit 75c3bf6

File tree

8 files changed

+87
-19
lines changed

8 files changed

+87
-19
lines changed
 

‎docs/guide/features.md

+9-4
Original file line numberDiff line numberDiff line change
@@ -435,13 +435,13 @@ Note that variables only represent file names one level deep. If `file` is `'foo
435435

436436
## WebAssembly
437437

438-
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:
438+
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:
439439

440440
```js
441-
import init from './example.wasm'
441+
import init from './example.wasm?init'
442442

443-
init().then((exports) => {
444-
exports.test()
443+
init().then((instance) => {
444+
instance.exports.test()
445445
})
446446
```
447447

@@ -461,6 +461,11 @@ init({
461461

462462
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.
463463

464+
::: warning
465+
[ES Module Integration Proposal for WebAssembly](https://github.com/WebAssembly/esm-integration) is not currently supported.
466+
Use [`vite-plugin-wasm`](https://github.com/Menci/vite-plugin-wasm) or other community plugins to handle this.
467+
:::
468+
464469
## Web Workers
465470

466471
### Import with Constructors

‎packages/vite/client.d.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,10 @@ declare module '*.otf' {
152152
}
153153

154154
// other
155-
declare module '*.wasm' {
156-
const initWasm: (options: WebAssembly.Imports) => Promise<WebAssembly.Exports>
155+
declare module '*.wasm?init' {
156+
const initWasm: (
157+
options: WebAssembly.Imports
158+
) => Promise<WebAssembly.Instance>
157159
export default initWasm
158160
}
159161
declare module '*.webmanifest' {

‎packages/vite/src/node/constants.ts

-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ export const KNOWN_ASSET_TYPES = [
8181
'otf',
8282

8383
// other
84-
'wasm',
8584
'webmanifest',
8685
'pdf',
8786
'txt'

‎packages/vite/src/node/plugins/index.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { cssPlugin, cssPostPlugin } from './css'
1010
import { assetPlugin } from './asset'
1111
import { clientInjectionsPlugin } from './clientInjections'
1212
import { buildHtmlPlugin, htmlInlineProxyPlugin } from './html'
13-
import { wasmPlugin } from './wasm'
13+
import { wasmFallbackPlugin, wasmHelperPlugin } from './wasm'
1414
import { modulePreloadPolyfillPlugin } from './modulePreloadPolyfill'
1515
import { webWorkerPlugin } from './worker'
1616
import { preAliasPlugin } from './preAlias'
@@ -64,10 +64,11 @@ export async function resolvePlugins(
6464
},
6565
isBuild
6666
),
67-
wasmPlugin(config),
67+
wasmHelperPlugin(config),
6868
webWorkerPlugin(config),
6969
assetPlugin(config),
7070
...normalPlugins,
71+
wasmFallbackPlugin(),
7172
definePlugin(config),
7273
cssPostPlugin(config),
7374
config.build.ssr ? ssrRequireHookPlugin(config) : null,

‎packages/vite/src/node/plugins/wasm.ts

+23-4
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,14 @@ const wasmHelper = async (opts = {}, url: string) => {
3737
result = await WebAssembly.instantiate(buffer, opts)
3838
}
3939
}
40-
return result.instance.exports
40+
return result.instance
4141
}
4242

4343
const wasmHelperCode = wasmHelper.toString()
4444

45-
export const wasmPlugin = (config: ResolvedConfig): Plugin => {
45+
export const wasmHelperPlugin = (config: ResolvedConfig): Plugin => {
4646
return {
47-
name: 'vite:wasm',
47+
name: 'vite:wasm-helper',
4848

4949
resolveId(id) {
5050
if (id === wasmHelperId) {
@@ -57,7 +57,7 @@ export const wasmPlugin = (config: ResolvedConfig): Plugin => {
5757
return `export default ${wasmHelperCode}`
5858
}
5959

60-
if (!id.endsWith('.wasm')) {
60+
if (!id.endsWith('.wasm?init')) {
6161
return
6262
}
6363

@@ -70,3 +70,22 @@ export default opts => initWasm(opts, ${JSON.stringify(url)})
7070
}
7171
}
7272
}
73+
74+
export const wasmFallbackPlugin = (): Plugin => {
75+
return {
76+
name: 'vite:wasm-fallback',
77+
78+
async load(id) {
79+
if (!id.endsWith('.wasm')) {
80+
return
81+
}
82+
83+
throw new Error(
84+
'"ESM integration proposal for Wasm" is not supported currently. ' +
85+
'Use vite-plugin-wasm or other community plugins to handle this. ' +
86+
'Alternatively, you can use `.wasm?init` or `.wasm?url`. ' +
87+
'See https://vitejs.dev/guide/features.html#webassembly for more details.'
88+
)
89+
}
90+
}
91+
}

‎playground/wasm/__tests__/wasm.spec.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { page, untilUpdated } from '~utils'
1+
import { isBuild, page, untilUpdated } from '~utils'
22

33
test('should work when inlined', async () => {
44
await page.click('.inline-wasm .run')
@@ -10,6 +10,20 @@ test('should work when output', async () => {
1010
await untilUpdated(() => page.textContent('.output-wasm .result'), '24')
1111
})
1212

13+
test('init function returns WebAssembly.Instance', async () => {
14+
await page.click('.init-returns-instance .run')
15+
await untilUpdated(
16+
() => page.textContent('.init-returns-instance .result'),
17+
'true'
18+
)
19+
})
20+
21+
test('?url', async () => {
22+
expect(await page.textContent('.url')).toMatch(
23+
isBuild ? 'data:application/wasm' : '/light.wasm'
24+
)
25+
})
26+
1327
test('should work when wasm in worker', async () => {
1428
await untilUpdated(() => page.textContent('.worker-wasm .result'), '3')
1529
})

‎playground/wasm/index.html

+31-3
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,25 @@ <h3>When wasm is output, result should be 24</h3>
1212
<span class="result"></span>
1313
</div>
1414

15+
<div class="init-returns-instance">
16+
<h3>init function returns WebAssembly.Instance</h3>
17+
<button class="run">Click to run</button>
18+
<span class="result"></span>
19+
</div>
20+
21+
<div>
22+
<h3>Importing as URL</h3>
23+
<span class="url"></span>
24+
</div>
25+
1526
<div class="worker-wasm">
1627
<h3>worker wasm</h3>
1728
<span class="result"></span>
1829
</div>
1930

2031
<script type="module">
21-
import light from './light.wasm'
22-
import heavy from './heavy.wasm'
32+
import light from './light.wasm?init'
33+
import heavy from './heavy.wasm?init'
2334
import myWorker from './worker?worker'
2435

2536
const w = new myWorker()
@@ -32,7 +43,7 @@ <h3>worker wasm</h3>
3243
imports: {
3344
imported_func: (res) => (resultElement.textContent = res)
3445
}
35-
})
46+
}).then((i) => i.exports)
3647
exported_func()
3748
}
3849

@@ -51,4 +62,21 @@ <h3>worker wasm</h3>
5162
.addEventListener('click', async () =>
5263
testWasm(heavy, document.querySelector('.output-wasm .result'))
5364
)
65+
66+
document
67+
.querySelector('.init-returns-instance .run')
68+
.addEventListener('click', async () => {
69+
const res = await light({
70+
imports: {
71+
imported_func: (res) => (resultElement.textContent = res)
72+
}
73+
})
74+
text(
75+
'.init-returns-instance .result',
76+
res instanceof WebAssembly.Instance
77+
)
78+
})
79+
80+
import lightUrl from './light.wasm?url'
81+
text('.url', lightUrl)
5482
</script>

‎playground/wasm/worker.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import init from './add.wasm'
2-
init().then((exports) => {
1+
import init from './add.wasm?init'
2+
init().then(({ exports }) => {
33
// eslint-disable-next-line no-undef
44
self.postMessage({ result: exports.add(1, 2) })
55
})

0 commit comments

Comments
 (0)
Please sign in to comment.