vite
vite copied to clipboard
Support wasm in SSR
Description
Currently, the wasm plugin usually get the .wasm
file using fetch
. This approach doesn't work in SSR. Even if fetch
were available, fetching a relative path would not work. This results in errors such as ERR_INVALID_URL
when in SSR.
Suggested solution
By adding an extra if cause to the wasm helper, we could check the import.meta.env.SSR
to see if we're in SSR mode. If we are, then we could load the file from disk instead of fetching it over the network. Something like this:
const wasmHelper = async (opts = {}, url: string) => {
if (url.startsWith('data:')) {
// load from inline base64 string
} else if (import.meta.env.SSR) {
// load from disk
} else {
// fetch over the network
}
}
However, there might be problems to using import.meta
that I'm not aware of. It seems to work fine for me when I'm testing it, but I've only tested it with SvelteKit.
If using the SSR environment variable isn't an option, then we could of course use some typeof window === "undefined"
or perhaps process
would be better. But there might be several pitfalls with that, mainly other plugins that polyfills window, process or whatever thing we might use. Perhaps a better approach would be to add the file system logic in a .catch
block on the fetch
itself, something like:
const response = await fetch(url).catch(async (err) => {
if (!(err && err.code === "ERR_INVALID_URL")) throw err;
// load from disk and return new Response(fileBuffer)
}
Another problem with loading from disk is that the URL provided is a URL, and not a file path. Fortunately it contains the file path, but I'm not sure if that would always be the case, or if it might be a better way of resolving the file path. Using the provided url
string, it is possible to decode the path using something like url.replace("/@fs", "")
.
Alternative
A workaround is to use another wasm plugin. I haven't found a vite wasm plugin that works with SSR, so currently we have created our own just to make it work (using the import.meta.env.SSR
technique mentioned above). That works great with SvelteKit, but might not work with everything. If it's of any interest to someone else, just let me know and I can see if we could push it to NPM.
I've also raised an issue (https://github.com/Menci/vite-plugin-wasm/issues/4) in the vite-pugin-wasm repo to make SSR work, but it has some challenges regarding ESM to utilize import.meta
.
Additional context
No response
Validations
- [X] Follow our Code of Conduct
- [X] Read the Contributing Guidelines.
- [X] Read the docs.
- [X] Check that there isn't already an issue that request the same feature to avoid creating a duplicate.
If anyone finds this issue and needs a quick fix, we've created a vite plugin to use your WASM packages as regular ES modules that also supports SSR. Only supports wasm-pack generated modules at the moment.
I found this issue because I was trying to understand a regression I found that's blocking me updating from Vite 4.5.0 to Vite 5.2.11. I have a WASM library I built as an ES module with Emscripten, so I have manifold.js
and manifold.wasm
in my built
directory. In a worker I call it like import Module from './built/manifold';
In Vite 4 this was working just fine both in the browser and in my node-based testing with npm test
(what I believe you're referring to as SSR). In Vite 5, everything in the browser still works just fine, but npm test
gives:
stderr | worker.test.js > Examples > Rounded Frame
TypeError: Invalid URL
at new URL (node:internal/url:787:36)
at Module.default (/Users/elalish/Code/manifold/bindings/wasm/examples/built/manifold.js:8:20330)
at /Users/elalish/Code/manifold/bindings/wasm/examples/worker.ts:17:16
at InlineWorkerRunner.runModule (file:///Users/elalish/Code/manifold/bindings/wasm/examples/node_modules/vite-node/dist/client.mjs:362:5)
at InlineWorkerRunner.directRequest (file:///Users/elalish/Code/manifold/bindings/wasm/examples/node_modules/vite-node/dist/client.mjs:346:5)
at InlineWorkerRunner.cachedRequest (file:///Users/elalish/Code/manifold/bindings/wasm/examples/node_modules/vite-node/dist/client.mjs:189:14)
at InlineWorkerRunner.executeFile (file:///Users/elalish/Code/manifold/bindings/wasm/examples/node_modules/vite-node/dist/client.mjs:161:12) {
code: 'ERR_INVALID_URL',
input: '/built/manifold.wasm'
}
So even though manifold.js
is calling manifold.wasm
in its own directory, it can't seem to resolve the URL. But this used to work, which makes me wonder if it's actually related to this issue after all. I don't see anything obviously related in the Vite 5 migration guide. Any thoughts?
Also, the Vite docs seem to say a lot about needing plugins for loading WASM, but I'm not using any plugins with Vite 4. Has functionality been removed from core?
in my node-based testing with
npm test
(what I believe you're referring to as SSR)
@elalish from the error message, it looks like you're referring to Vitest with @vitest/web-worker
and that's way more moving parts than just Vite SSR. Can you report on Vitest with https://github.com/vitest-dev/vitest with a reproduction? It's also possible that you're getting a new error due to some change on Vitest.
Ah, correct you are! Okay, Vite 5.2.11 is working fine - it must have been the Vitest update that did it. I'll report over there instead - had sort of thought that was all part of the same project.
Done: https://github.com/vitest-dev/vitest/issues/5704