wasmer-js icon indicating copy to clipboard operation
wasmer-js copied to clipboard

How to use in browsers?

Open michaelfranzl opened this issue 3 years ago • 10 comments

The README of v1.0.2 states that browsers are supported. However, neither the README nor the example directory contain usage examples for the browser. Using the best guess in the browser,

import { init, WASI } from '@wasmer/wasi';
await init()

gives the following error:

Library.esm.min.js:25 Uncaught (in promise) ReferenceError: Buffer is not defined
    at Z (Library.esm.min.js:25:15897)

This comes from https://github.com/wasmerio/wasmer-js/blob/8f3f221a5431eff5c5370f48d9189eba3547f451/lib.ts#L62 Indeed there is no Buffer global in browsers.

How to use @wasmer/wasi in the browser? If Buffer is supposed to be polyfilled, could you add this to the README and refer to a library which fulfills the required API? Thanks.

michaelfranzl avatar Sep 18 '22 15:09 michaelfranzl

I was probably about to encounter the same problem. I'd like to use this library in the browser as well.

Jack-Edwards avatar Sep 18 '22 16:09 Jack-Edwards

Hi! Currently the Wasmer-JS libraries rely on a bundler for browser integration. Webpack and Rollup work well. Notice #290 and the comments below about defining wasmer_wasi_js_bg.wasm as external; it is actually not needed as its content is inlined in Library.esm.min.js (as a data URL).

corwin-of-amber avatar Sep 19 '22 12:09 corwin-of-amber

@corwin-of-amber When you say that it "relies on a bundler", do you mean that a bundler has to be configured to polyfill Buffer? Like from rollup-plugin-polyfill-node or @esbuild-plugins/node-modules-polyfill?

In contrast, in v0.12.0, Buffer was included in wasmer-js/wasi (imported from buffer-es6 and injected as a dependency): https://github.com/wasmerio/wasmer-js/blob/v0.12.0/packages/wasi/src/index.ts#L11

michaelfranzl avatar Sep 19 '22 17:09 michaelfranzl

Yes, exactly. I am pasting the contents of webpack.config.js from a hello-world project that I did to learn how to use it; if you find it useful then perhaps I can make a PR to add it to the WasmerJS docs.

const path = require('path');
const webpack = require('webpack');

module.exports = {
    name: 'hello',
    entry: './hello.js',
    mode: 'development',
    target: 'webworker',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'hello.js'
    },
    module: {
        rules: [
            {
                test: /\.wasm$/,
                type: 'asset/resource'
            }
        ]
    },
    externals: {
        'wasmer_wasi_js_bg.wasm': true
    },
    resolve: {
        fallback: {buffer: require.resolve('buffer/')}
    },
    plugins: [new webpack.ProvidePlugin({
        process: 'process/browser.js',
        Buffer: ['buffer', 'Buffer']
    })]
}

It is true that previous versions were bundled with Buffer. IMO this is now old-style; having your dependencies bundled hinders inter-operability esp. if types from the bundled package are part of the API. It makes passing the "right" objects quite tricky. Forcing the user to provide their own polyfills is certainly more cumbersome and goes against the OOP encapsulation concepts, but pragmatically is much more flexible.

corwin-of-amber avatar Sep 19 '22 21:09 corwin-of-amber

I'd also like to use the library on the browser, also note that in parcel doesn't work out of the box sadly

cdecompilador avatar Oct 02 '22 17:10 cdecompilador

Here is an esbuild configuration I am using successfully in the browser: https://github.com/guregu/trealla-js/blob/9dfaf8b886033ea339cd36d005b2555091c56efa/make.cjs#L17-L19

The important bit:

const stdLibBrowser = require('node-stdlib-browser');

(async () => {
	await esbuild.build({
		entryPoints: ['index.js'],
// ...
		inject: [require.resolve('node-stdlib-browser/helpers/esbuild/shim')],
		define: {
			Buffer: 'Buffer'
		},
		plugins: [plugin(stdLibBrowser)]

guregu avatar Oct 06 '22 14:10 guregu

After polyfilling buffer I'm getting Uncaught (in promise) Error: Failed to instantiate WASI: RuntimeError: JsValue(TypeError: import object field 'env' is not an Object

EDIT: After compiling in all the functions that weren't in wasi-libc ... would be nice if you could provide env through JS too in case you want to implement the functions there, now I get this:

Error while running start function: RuntimeError: JsValue(Object({"ptr":1122200}))

Works in wasmtime though :/

EDIT2: prob this issue https://github.com/wasmerio/wasmer/issues/2568 the app calls exit() It's dumb but this workaround works

var exitCode = 1;
try { exitCode = wasi.start(); } catch (e) {}

Cloudef avatar Nov 23 '22 20:11 Cloudef

One thing I hate( so far) is everything i can find requires vite Wich indirectly uses node. One of the cool things about wasm(if i not mistaken) looks like wasm should not need a bundler or anything other than a web browser ;i am reading wasm are binary(all though it could be text). I would like to use wapm without node i am surprised they have no instructions un using it with unpkg or skypack and no install IMHO it should need wasmer.

frog-o avatar Mar 25 '23 13:03 frog-o

If you're using vite you can use this plugin to polyfill for node: https://github.com/sodatea/vite-plugin-node-stdlib-browser

// vite.config.ts
import { defineConfig } from 'vite';
import nodePolyfills from 'vite-plugin-node-stdlib-browser'

export default defineConfig({
  server: {
    port: 8080,
  },
  plugins: [nodePolyfills()]
});

zees-dev avatar Aug 22 '23 13:08 zees-dev

After polyfilling buffer I'm getting Uncaught (in promise) Error: Failed to instantiate WASI: RuntimeError: JsValue(TypeError: import object field 'env' is not an Object

I had the same error. My problems were the imports. I've used:

const imports = {
  myfun: () => {
    console.log("my function")
  }
}

But when I looked at the WAT output (thanks to https://uptointerpretation.com/posts/webassembly-woes/, pointing to https://stackoverflow.com/questions/54598317/rust-wasm-module-not-found-error-cant-resolve-env-in, where the answer suggests running wasm2wat), I saw that myfun is imported under env.

So the correct import is:

const imports = {
  env: {
    myfun: () => {
      console.log("my function")
    }
  }
}

I've been using this from Rust, the extern there is just:

extern "C" {
    fn myfun();
}

vmx avatar Sep 14 '23 23:09 vmx