Skip to content

Commit 9678faa

Browse files
committed
feat: add a startup event to ensure the WebAssembly binding can be used
Closes #788
1 parent c2e8f98 commit 9678faa

File tree

11 files changed

+166
-4
lines changed

11 files changed

+166
-4
lines changed

examples/initializer/index.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,10 @@
1111
<body>
1212
<link data-trunk rel="rust" href="Cargo.toml" data-wasm-opt="z" data-bin="initializer-example" data-initializer="src/initializer.mjs"/>
1313
</body>
14+
<script type="module">
15+
// you will still get the TrunkApplicationStarted event
16+
addEventListener("TrunkApplicationStarted", (event) => {
17+
console.log("application started - bindings:", window.wasmBindings, "event:", event);
18+
});
19+
</script>
1420
</html>

examples/vanilla/Cargo.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,11 @@ edition = "2018"
77
[dependencies]
88
console_error_panic_hook = "0.1"
99
wasm-bindgen = "0.2"
10-
web-sys = { version = "0.3", features = ["Window", "Document", "HtmlElement", "Node", "Text"] }
10+
web-sys = { version = "0.3", features = [
11+
"console",
12+
"Document",
13+
"HtmlElement",
14+
"Node",
15+
"Text",
16+
"Window",
17+
] }

examples/vanilla/index.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,14 @@
1616
<script data-trunk src="src/script.mjs" type="module"></script>
1717
</body>
1818
<script>testFromJavaScript();</script>
19+
20+
<script type="module">
21+
addEventListener("TrunkApplicationStarted", (event) => {
22+
console.log("application started - bindings:", window.wasmBindings, "WASM:", event.detail.wasm);
23+
window.wasmBindings.wasm_ffi();
24+
// You can also run this via the WASM instance in the details
25+
// event.detail.wasm.wasm_ffi();
26+
});
27+
</script>
28+
1929
</html>

examples/vanilla/src/main.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ extern "C" {
1919
fn snippetTest();
2020
}
2121

22+
#[wasm_bindgen]
23+
pub fn wasm_ffi() {
24+
web_sys::console::log_1(&"Hello from WASM!".into());
25+
}
26+
2227
fn main() {
2328
set_panic_hook();
2429
snippetTest();

guide/src/SUMMARY.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
* [Assets](assets/index.md)
1515
* [Minification](assets/minification.md)
1616
* [Sub-resource integrity](assets/sri.md)
17-
17+
* [Advanced](advanced/index.md)
18+
* [JavaScript interoperability](advanced/javascript_interop.md)
19+
* [Startup event](advanced/startup_event.md)
20+
* [Application initializer](advanced/initializer.md)
21+
* [Library crate](advanced/library.md)
1822
---
1923

2024
[Contributing](contributing.md)

guide/src/advanced/index.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Advanced
2+
3+
There are some more advanced topics, which will be described in the following subsections.

guide/src/advanced/initializer.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
# Initializer
3+
4+
Since: `0.19.0-alpha.1`.
5+
6+
Trunk supports tapping into the initialization process of the WebAssembly application. By
7+
default, this is not active and works the same way as with previous versions.
8+
9+
The default process is that trunk injects a small JavaScript snippet, which imports the JavaScript loader generated
10+
by `wasm_bindgen` and calls the `init` method. That will fetch the WASM blob and run it.
11+
12+
The downside of this is, that during this process, there's no feedback for the user. Neither when it takes a bit longer
13+
to load the WASM file, nor when something goes wrong.
14+
15+
Now it is possible to tap into this process by setting `data-initializer` to a JavaScript module file. This module file
16+
is required to (default) export a function, which returns the "initializer" instance. Here is an example:
17+
18+
```javascript
19+
export default function myInitializer () {
20+
return {
21+
onStart: () => {
22+
// called when the loading starts
23+
},
24+
onProgress: ({current, total}) => {
25+
// the progress while loading, will be called periodically.
26+
// "current" will contain the number of bytes of the WASM already loaded
27+
// "total" will either contain the total number of bytes expected for the WASM, or if the server did not provide
28+
// the content-length header it will contain 0.
29+
},
30+
onComplete: () => {
31+
// called when the initialization is complete (successfully or failed)
32+
},
33+
onSuccess: (wasm) => {
34+
// called when the initialization is completed successfully, receives the `wasm` instance
35+
},
36+
onFailure: (error) => {
37+
// called when the initialization is completed with an error, receives the `error`
38+
}
39+
}
40+
};
41+
```
42+
43+
For a full example, see: <https://github.com/trunk-rs/trunk/tree/main/examples/initializer>.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# JavaScript interoperability
2+
3+
Trunk will create the necessary JavaScript code to bootstrap and run the WebAssembly based application. It will also
4+
include all JavaScript snippets generated by `wasm-bindgen` for interfacing with JavaScript functionality.
5+
6+
By default, functions exported from Rust, using `wasm-bingen`, can be accessed in the JavaScript code through the global
7+
variable `window.wasmBindings`. This behavior can be disabled, and the name can be customized. For more information
8+
see the [`rust` asset type](@/assets.md#rust).
9+
10+
## Order of initialization
11+
12+
The bindings will only be available and working when the application initialization has been completed.
13+
14+
If your WebAssembly application renders code into the web page/DOM tree, which then calls from JavaScript into the
15+
WebAssembly application, then this will not be an issue, as the application is already initialized.
16+
17+
However, if you want to call into the WebAssembly application from, for example, the `index.html` file itself, then
18+
you must delay that call until the application is started.
19+
20+
This can be ensured by executing that code with the `TrunkApplicationStartup` event. Also see
21+
[Startup Event](startup_event.md).

guide/src/advanced/library.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
# Library crate
3+
4+
Aside from having a `main` function, it is also possible to up your project as a `cdylib` project. In order to do that,
5+
add the following to your `Cargo.toml`:
6+
7+
```toml
8+
[lib]
9+
crate-type = ["cdylib", "rlib"]
10+
```
11+
12+
And then, define the entrypoint in your `lib.rs` like (does not need to be `async`):
13+
14+
```rust
15+
#[wasm_bindgen(start)]
16+
pub async fn run() {}
17+
```

guide/src/advanced/startup_event.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Startup event
2+
3+
The initializer code snippet of Trunk will emit an event when the WebAssembly application has been loaded and started.
4+
5+
```admonish note
6+
This event is independent of the initializer functionality.
7+
```
8+
9+
## Definition
10+
11+
The event is called `TrunkApplicationStarted` and is executed after the WebAssembly has been loaded and initialized.
12+
13+
The event will have custom details:
14+
15+
```javascript
16+
{
17+
wasm // The web assembly instance
18+
}
19+
```
20+
21+
## Example
22+
23+
The following snippet can be used to run code after the initialization of the WebAssembly application:
24+
25+
```html
26+
<script type="module">
27+
addEventListener("TrunkApplicationStarted", (event) => {
28+
console.log("application started - bindings:", window.wasmBindings, "WASM:", event.detail.wasm);
29+
// wasm_ffi is a function exported from WASM to JavaScript
30+
window.wasmBindings.wasm_ffi();
31+
// You can also run this via the WASM instance in the details
32+
// event.detail.wasm.wasm_ffi();
33+
});
34+
</script>
35+
```
36+
37+
Also see the vanilla example: <https://github.com/trunk-rs/trunk/tree/main/examples/vanilla>.

src/pipelines/rust/output.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,20 @@ window.{bindings} = bindings;
126126
false => ("", String::new()),
127127
};
128128

129+
// the code to fire the `TrunkApplicationStarted` event
130+
let fire = r#"
131+
dispatchEvent(new CustomEvent("TrunkApplicationStarted", {detail: {wasm}}));
132+
"#;
133+
129134
match &self.initializer {
130135
None => format!(
131136
r#"
132137
<script type="module">
133138
import init{import} from '{base}{js}';
134-
init('{base}{wasm}');{bind}
139+
const wasm = await init('{base}{wasm}');
140+
141+
{bind}
142+
{fire}
135143
</script>"#
136144
),
137145
Some(initializer) => format!(
@@ -142,9 +150,10 @@ init('{base}{wasm}');{bind}
142150
import init{import} from '{base}{js}';
143151
import initializer from '{base}{initializer}';
144152
145-
await __trunkInitializer(init, '{base}{wasm}', {size}, initializer());
153+
const wasm = await __trunkInitializer(init, '{base}{wasm}', {size}, initializer());
146154
147155
{bind}
156+
{fire}
148157
</script>"#,
149158
init = include_str!("initializer.js"),
150159
size = self.wasm_size,

0 commit comments

Comments
 (0)