-
Notifications
You must be signed in to change notification settings - Fork 409
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
Generate a single JavaScript file #699
Comments
@kellytk What is your use case? Wasm-pack currently supports the following targets:
|
Option 4 may be what I want however I can't get it to run successfully. When I run [INFO]: 🎯 Checking for the Wasm target... Do you know what mistake I'm making? |
@kellytk That's because |
I've got a use case where my final deliverable is a single html file. Currently I'm using parcel to inline all the js and css. If wasm-pack could generate a single js along with the ts declarations it would work flawlessly for what I need. |
@EliSnow I made https://github.com/yewstack/yew-wasm-pack-minimal and used |
I tried to use wasm-pack with rollup but did not manage to create a minimal example. After reading https://rustwasm.github.io/docs/wasm-pack/commands/build.html?highlight=bundler#target I thought that wasm-pack is supposed to work with different bundlers, not just webpack but as @Pauan mentioned this might not be the case? It would be helpfull to specify which requirement the bundler has to fulfill. |
@benmkw Webpack is the only bundler that wasm-pack natively supports. This is because Webpack supports importing However, even though wasm-pack doesn't natively support other bundlers, you can still use them with plugins. For example, you can use the |
This comment has been minimized.
This comment has been minimized.
Sorry I was a bit quick/ tired to respond, it appears that https://github.com/wasm-tool/rollup-plugin-rust is the same as https://github.com/rollup/plugins/tree/master/packages/wasm in that it needs all code that interacts with wasm to be async. As far as I see this is a big drawback compared to the approach by webpack https://github.com/webpack/webpack/tree/master/examples/wasm-simple because it makes it much harder to use wasm functions as callbacks/ simple functions together with the dom. It is not clear to me how to structure the program in face of async wasm loading such that it does not leak all over the code so this makes these two plugins hard to use for me. I may want to avoid reloading the same module (without relying on correct browser and server configuration) and persist state in the same wasm module over multiple calls to it, both of which is harder using the async api not handled by the bundler. The benefit of exposing the async nature of wasm to the programmer seems to me that it makes it easier to avoid the page from blocking but the page may only usefull with the wasm loaded anyway. (I also probably made a mistake while I tried https://github.com/wasm-tool/rollup-plugin-rust because the wasm file is not served correctly cause its in the wrong dir but the example was not using rust from js but just using rust intead of js so this may be why it was unclear https://github.com/benmkw/svelte_rust_test) Back to my first investigaton of bundlers, my hope was to get something like: import some_fn from "some_package"
// just use some_fn Possibly at the expense of a little more loading time upfront. This appears to be only possible using webpack/ it would be interesting if/ when/ how this would be possible in other bundlers/ if this is a supported approach or discouraged for other reasons (and what the suggested alternative for interacting with non async code is) |
That's not because of wasm-pack (or the plugins), it's fundamentally how Wasm works. All of the Wasm APIs (such as Until browsers get native
Note that Webpack also requires async: you must use dynamic Bundlers and plugins cannot make Wasm synchronous, because the browser itself requires Wasm to be loaded asynchronously.
That is impossible until browsers get native
We can't really do anything about that: if you want Webpack-style importing of
No, the "benefit" of making it async is that the browser forces us to make it async. We have no choice. The browser simply does not allow you to load Wasm synchronously.
This is very easy to accomplish, since ES6 modules are never reloaded: // wrapper.js
import wasm from "./path/to/Cargo.toml";
export default wasm(); Now you can import that ES6 module and it's guaranteed to only load the Wasm file once: import wasm from "./path/to/wrapper.js";
async function foo() {
let exports = await wasm;
}
Normally you would structure your app like this: async function main() {
let wasm = await loadWasmAsyncSomehow();
// The rest of your app's initialization code
}
main().catch(console.error); In other words, your app's main entry point is async. It will asynchronously load the Wasm, and then afterwards everything else (including the DOM) can be fully synchronous. So you only need a single async function, and you only need to deal with the asyncness in one place (it does not leak throughout your app). This also guarantees that the Wasm is only loaded once. For libraries it is trickier, but your library can export a function which initializes the library, and then you leave it up to the application to call the init function: // Library
export default async function () {
let wasm = await loadWasmAsyncSomehow();
return {
// Your library's API goes here...
};
} // Application
import init from "some-library";
async function main() {
let library = await init();
// Rest of app code...
}
main().catch(console.error); |
P.S. You can combine the async // wrapper.js
import wasm from "./Cargo.toml";
export let exports = null;
export async function init() {
exports = await wasm();
} // some-file.js
import { exports } from "./wrapper.js";
// You can use exports synchronously! // main.js
import { init } from "./wrapper.js";
async function main() {
await init();
let someFile = await import("./some-file.js");
// Run rest of initialization code
}
main().catch(console.error); Now you can import and use the Wasm exports synchronously, as long as you make sure that your application is initialized after You can compare that to the Webpack approach: // some-file.js
import * as exports from "./foo.wasm";
// You can use exports synchronously! // main.js
async function main() {
let someFile = await import("./some-file.js");
// Run rest of initialization code
}
main().catch(console.error); As you can see, it's very similar, the only real difference is that you needed to create a small wrapper file. The reason why the Rollup plugin doesn't create this wrapper file for you is because it's very inflexible: what if you want to initialize the Wasm multiple times? There are use cases for that. |
Thanks for your explanations. <script>
import { onMount } from "svelte";
import wasm from './../../rust_wasm/Cargo.toml';
let add = null;
onMount(async () => {
let wasmer = await wasm();
add = wasmer.add;
});
</script>
<main>
<!-- does work -->
<p> {add ? add(1,2) : "not yet" } </p>
<!-- does not work because onMount is called after first render-->
<!-- <p> {add(1,2)} </p> -->
</main>
Apparently in webpack you can now do import await { add as mathAdd, factorial, factorialJavascript, fibonacci, fibonacciJavascript } from "./math";
console.log(add(22, 2200)); so the import sort of blocks until the promise resolves and thus the rest of the file can treat the function from the async import as if they are coming from a non async import. (I have not tried it actually but as far as I see)
That's good to know!
yeah that's kind of what I meant with leak (but viewed this way its not really the right word), in the sense that you have to put it all into the function and can't communicate with the outside (or have to use the thing where you put a dummy value in and then hope the async code resolves quickly as I did in the example now) Sorry for derailing this issue 😃 |
That is an experimental syntax which is not enabled by default. And it will likely never be standardized. It also "taints" the module, so now you need to import that module with The top level await proposal will fix this problem, but it's not implemented in any browsers. In any case, that's Webpack specific, so you would need to ask other bundlers to implement it as well.
In the above post I showed a way to get the exact same behavior as Webpack: there are no race conditions, and it is guaranteed that you can fully access the Wasm synchronously inside of your rendering code, without any hacks. |
Basically I did the same as they did for react https://www.telerik.com/blogs/using-webassembly-with-react here but upon closer inspection how others deal with this I also found https://github.com/BrockReece/vue-wasm which is what you mean (I hope) and it works in svelte as well, I was just to focussed to see the bigger picture. // main.js
import App from './App.svelte';
import wasm from './../../rust_wasm/Cargo.toml';
const init = async () => {
const wasmer = await wasm();
const w_add = wasmer.add;
const app = new App({
target: document.body,
props: {
add: w_add
}
});
};
init(); // App.svelte
<script>
export let add; // the wasm function
</script>
<main>
<p> {add(1,2)} </p>
</main>= Thanks for insisting that my solution was not proper 👍 |
https://rustwasm.github.io/wasm-bindgen/examples/without-a-bundler.html mentions a number of limitations with the |
It would be much much more easier if we could bundle wasm binary into js file. In many cases, we cannot load resources from node_modules directly, but we can import the package which only contains js files. The default init function will not necessarily work, especially when you have many different bundle methods, so many configure options... EditFor people who are looking for solutions, checkout this post |
Due to this limitation, js-tiktoken seems to have hacked together it's own solution so it can create an hybrid package that can be imported from both node and bundlers using See https://github.com/dqbd/tiktoken/blob/main/wasm/scripts/postprocess.ts |
I'm guessing that what people need is something that can be used on both web and node platforms with the same api. const base64 = "wasm_base64"
export default async function (env: Env = {}) {
const buffer = decode(base64);
const wasmModule = await WebAssembly.compile(buffer)
const instance = (await WebAssembly.instantiate(wasmModule, env)) as Instance
function fib(n: number): number {
const fn = instance.exports.fib
return fn(n)
}
return {
instance,
fib
}
} |
💡 Feature description
It would be very useful for
wasm-pack
to provide an option to generate a single amalgamated JavaScript file.The text was updated successfully, but these errors were encountered: