Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

--browser doesn’t work for browsers #1326

Closed
surma opened this issue Mar 7, 2019 · 4 comments
Closed

--browser doesn’t work for browsers #1326

surma opened this issue Mar 7, 2019 · 4 comments

Comments

@surma
Copy link

surma commented Mar 7, 2019

Motivation

The JavaScript generated when using --browser is actually not usable in a browser directly, but has to be processed by a bundler. They problem lies in this import in the JavaScript:

import * as wasm from './mymodule';

Firstly, the imported file is missing the .wasm file extension, which is required for import statements in the browser. Secondly, importing Wasm modules via ES6 import statements is not support (but there is a proposal). Out of the box, only Webpack supports this kind of import. This has lead to the impression that wasm-bindgen & wasm-pack require Webpack.

If I don’t use Webpack I either have to teach my bundler about this kind of non-standard import or switch to --no-modules, which generates code that works on a global variable. Globals are notoriously messy to reconcile with bundlers.

If I want to work without a bundler, --no-modules is a decent choice, but if I have multiple, independent Wasm modules generated with wasm_bindgen, these variables are going to clash.

I propose that the introduction of a new target --esm.

(I am assuming that the browser target cannot be renamed at this point. If it can, I’d rename --browser to --webpack in addition to introducing --esm.)

Proposed Solution

There is a middle ground in the approaches between the browser and the no-modules target. The --esm code could expose an async factory function that instantiates the Wasm module with the vanilla Wasm APIs and then returns an object with the wrapped functions. The code could look something like this (I’m obviously skipping and missing some details here, but happy to work on that with y’all if desired):

// mymodule.mjs
/*
 * ...
 * Unexported utility functions like 
 * `passArray8ToWasm`, `getArrayU8FromWasm` etc etc 
 * ...
 */

export function async instantiate(pathOrBuffer) {
  let instance;
  const imports = /* create imports object */

  // Feature detection of loading mechanism
  if(typeof pathOrBuffer === 'string' && WebAssembly.instantiateStreaming) {
	({instance} = await WebAssembly.instantiateStreaming(fetch(pathOrBuffer), imports));
  } else {
    if(typeof pathOrBuffer === 'string') {
      pathOrBuffer = await fetch(pathOrBuffer).then(r => r.arrayBuffer());
    } 
    ({instance} = await WebAssembly.instantiate(pathOrBuffer, imports));
  }

  return {
    /* 
     * ...
     * wrapped module functions 
     * ...
     */
    // This is just an idea how the raw instance could be exposed
    // without the risk of name clashes.
    [rawInstance]: instance, 
  }
}

const rawInstance = new Symbol();
export rawInstance;

The upside of this code is that it works in browsers and Node out of the box. Also note the .mjs file extension which is necessary for module code in Node.

cc @flaki @ashleygwilliams

@limira
Copy link
Contributor

limira commented Mar 7, 2019

What version (of wasm-bindgen) do you use? If you use v0.2.38, please note that the git master version has a merge that breaks the --browser flag. Hence, you may want to try the master instead of v0.2.38.

@alexcrichton
Copy link
Contributor

Thanks for the report @surma!

I think the root problem here is that the --browser flag is sort of terribly named. It, in reality, is a tiny flag which simply says "when generating bundler-compatible output optimize away a few checks for node.js". To add on top of this the --no-modules flag is a pretty bad name as well, which I think is roughly what you want in terms of "natively runnable in a browser".

It's definitely well-known that browser don't support native ES imports of wasm modules. It's something we want to coerce all output towards though! Like with the original ES modules proposal bundlers provide the shim necessary to translate future-looking code to actual code that runs in a browser, so bundlers are the only parts of the stack that implement "import this wasm file as an es module directly" today. This is also why it's the default output mode as well because eventually it'll work in web browsers!

I think that the desired output mode here is actually directly addressed by the proposed --browser in RFC 6 (which is likely to get renamed to --web here).

To address some of your other points though:

Firstly, the imported file is missing the .wasm file extension, which is required for import statements in the browser.

Indeed! This has had attempts to be fixed - #1059 - but it's tricker than expected. There's a number of idioms in use which rely on bundlers performing resolution to figure out if js or wasm is there and loading the right one. There's also proposals for "import maps" in browsers, so this may one day indeed work.

This has lead to the impression that wasm-bindgen & wasm-pack require Webpack.

Agreed, and this is unfortunate! We are doing our best to provide ample documentation and support saying that this is not the case, for example with the recently rewritten page on deployment. We're also hoping that --web and being prominently documented will help here.

One thing we've also noticed is that our introductory documentation shouldn't use npm which is where the impression is often coming from as well. In short, we're working really hard on this!

I propose that the introduction of a new target --esm.

Naming is really hard :(. The current default output is an ES module (and pretends wasm is as well), which means the name --esm isn't quite appropriate in my mind. I'm hoping that --web can help bridge the gap, but even that is still pretty ambiguous.


@surma want to give #1328 a spin and see if --web satisfies the use case you're thinking of?

@surma
Copy link
Author

surma commented Mar 7, 2019

Yup, I’m decently happy with --web :D Thanks for the detailed insight.

@surma surma closed this as completed Mar 7, 2019
@wangjia184
Copy link

it cost me several hours to find out browser is not a target for web browser :(

wasm-pack build --target web --no-typescript

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants