Introduction
Trunk is a WebAssembly (WASM) web application bundler for Rust. Trunk uses a simple, optional-config pattern for building & bundling WASM, JS snippets & other assets (images, css, scss) via a source HTML file.
Or in layman's terms: Rusty things go in, webby things come out.
Getting started
The following subsections explain what is required to install and use trunk
, and how you can start with a basic
project.
Pre-requisites
While trunk
tries to fetch tools automatically as needed (unless you're running with --offline
), some
pre-requisites may be required, depending on your environment.
Rust
It might be obvious, but trunk
requires an installation of Rust. Not only when installing trunk
itself from sources,
but also for compiling the Rust-based projects to WebAssembly.
The instructions of installing Rust may vary based on your operating system, a reasonable default comes from the Rust project: https://www.rust-lang.org/learn/get-started
Once installed, you should have the following tools available on your command line:
rustup
cargo
WebAssembly target
By default, the Rust installation will only install the target for your current machine. However, in this case, we want
to cross-compile to WebAssembly. Therefore, it is required to install the target wasm32-unknown-unknown
. Assuming
you have installed Rust using the standard process and can use rustup
, you can add the target using:
rustup target add wasm32-unknown-unknown
Installation
trunk
is a standard Rust command line tool and can be installed using standard Rust tooling (cargo
), by downloading
a pre-compiled binary, or through some distribution packagers.
Installing from source
As trunk
uses a standard Rust build and release process, you can install trunk
just the "standard way". The
following sections will give some examples.
trunk
supports a build time features, they are:
rustls
(default)- Use rustls for client and server sockets
native-tls
- Enable the use of the system native TLS stack for client sockets, and `openssl` for server sockets
update_check
(default)- Enable the update check on startup
Installing a release from crates.io
As trunk
is released on crates.io, it can be installed by simply executing:
cargo install --locked trunk
Installing from git directly
Using cargo
you can also install directly from git:
cargo install --git https://github.com/trunk-rs/trunk trunk
This will build and install the most recent commit from the main
branch. You can also select a specific commit:
cargo install --git https://github.com/trunk-rs/trunk trunk --rev <commit>
Or a specific tag:
cargo install --git https://github.com/trunk-rs/trunk trunk --tag <tag>
Installing from the local directory
Assuming you have checked out the trunk
repository, even with local changes, you can install a local build using:
cargo install --path . trunk
Installing a pre-compiled binary from trunk
Pre-compiled releases have the default
features enabled.
Download from GitHub releases
trunk
published compiled binaries for various platforms during the release process. They can be found in the
GitHub release section of trunk
. Just download and extract the binary
as you would normally do.
Using cargo binstall
cargo-binstall
allows to install pre-compiled binaries in a
more convenient way. Given a certain pattern, it can detect the version from crates.io and then fetch the matching
binary from a GitHub release. trunk
supports this pattern. So assuming you have installed cargo-binstall
already,
you can simpy run:
cargo binstall trunk
Distributions
Trunk is released by different distributions. In most cases, a distribution will build their own binaries and might not keep default feature flags. It might also be that an update to the most recent version might be delayed by the distribution's release process.
As distributions will have their own update management, most likely Trunk's update check is disabled.
Brew
trunk
is available using brew
and can be installed using:
brew install trunk
Fedora
Starting with Fedora 40, trunk
can be installed by executing:
sudo dnf install trunk
Nix OS
Using Nix, trunk
can be installed using:
nix-env -i trunk
Update check
Since: 0.19.0-alpha.2
.
Trunk has an update check built in. By default, it will check the trunk
crate on crates.io
for a newer
(non-pre-release) version. If one is found, the information will be shown in the command line.
This check can be disabled entirely, by not enabling the cargo feature update_check
. It can also be disabled during
runtime using the environment variable TRUNK_SKIP_VERSION_CHECK
, or using the command line switch
--skip-version-check
.
The actual check with crates.io
is only performed every 24 hours.
A basic project
For building the web application, trunk
is running a combination of tools, mainly cargo build
and wasm-bindgen
.
Therefore, having a simple cargo
project and an index.html
file as an entry-point is sufficient to get you started.
Creating a project
Start with creating a fresh Rust project and change into that folder:
cargo new trunk-hello-world
cd trunk-hello-world
Add some dependencies for the web:
cargo add wasm-bindgen console_error_panic_hook
cargo add web_sys -F Window,Document,HtmlElement,Text
Inside the newly created project, create a file src/main.rs
with the following content:
use web_sys::window; fn main() { console_error_panic_hook::set_once(); let document = window() .and_then(|win| win.document()) .expect("Could not access the document"); let body = document.body().expect("Could not access document.body"); let text_node = document.create_text_node("Hello, world from Vanilla Rust!"); body.append_child(text_node.as_ref()) .expect("Failed to append text"); }
Create an index.html
in the root of project:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Hello World</title>
</head>
<body>
</body>
</html>
Then, start trunk
inside that project to have it built and served:
trunk serve --open
This should compile the project, run wasm_bindgen
, create an index.html
based on the original file which loads and
initializes the application.
The application itself is pretty basic, simply getting the document's body and adding a note.
Next steps
Most likely, you do not want to manually update the DOM tree of your application. You might want to add some assets, tweak the build process, use some more browser APIs, perform some HTTP requests, use existing crates for the web, and maybe even interface with the JavaScript world. However, all of this is an extension to this basic project we just created.
Here are some pointers:
wasm_bindgen
documentation- HTML Frameworks (sorted by GitHub stars)
- More
trunk
examples
Commands
Trunk ships with a set of CLI commands to help you in your development workflows.
build
trunk build
runs a cargo build targeting the wasm32 instruction set, runs wasm-bindgen
on the built WASM, and spawns
asset build pipelines for any assets defined in the target index.html
.
Trunk leverages Rust's powerful concurrency primitives for maximum build speeds & throughput.
watch
trunk watch
does the same thing as trunk build
, but also watches the filesystem for changes, triggering new builds
as changes are detected.
serve
trunk serve
does the same thing as trunk watch
, but also spawns a web server.
clean
trunk clean
cleans up any build artifacts generated from earlier builds.
config show
trunk config show
prints out Trunk's current config, before factoring in CLI arguments. Nice for testing & debugging.
tools show
trunk tools show
prints out information about tools required by trunk and the project. It shows which tools are
expected and which are found.
Configuration
Trunk's configuration has massively changed in the 0.21.0 release. The goal was not to break anything, but it might have happened anyway. Also does the layering system work a bit different now.
It might also be that the documentation still mentions only Trunk.toml
. If that's the case, then this now includes
all other configuration file variants as well.
Trunk supports a layered configuration system. The base comes from a reasonable set of defaults, overridden by a configuration file, overridden command line arguments.
Technically speaking, there's a project configuration struct, which has reasonable defaults. Trunk will try to locate a configuration file and load if into this struct. It will then override this configuration with settings from the command line parser (which includes environment variables).
Configuration files
Trunk will try to locate a configuration file. Either in the local directory, or by using the global argument
--config
, which can accept either a file, or a directory. If the argument is a file, then this file will be
used directly. Otherwise, Trunk will load the first file found, searching for:
Trunk.toml
.trunk.toml
Trunk.yaml
.trunk.yaml
Trunk.json
.trunk.json
If neither of those files is found, Trunk will use the metadata from the Cargo.toml
, which defaults to an empty
set of metadata.
The directory of the configuration file will become the project root, and all relative files will be resolved based on that project root.
Formats
Trunk's configuration is limited to a JSON compatible model. This means you can easily translate between those different formats.
For example, having the following Trunk.toml
configuration:
[build]
dist = "dist"
[serve]
port = 8080
Would be the following in YAML:
build:
dist: "dist"
serve:
port: 8080
Also Cargo.toml
is based on that model. However, it moves that data down into the package.metadata.trunk
section.
The example above would become:
[package.metadata.trunk.build]
dist = "dist"
[package.metadata.trunk.serve]
port = 8080
Command line arguments (and environment variables)
Command line arguments can override part of the configuration. Not all configuration aspects can be overridden by the command line arguments though. Command line arguments include the use of environment variables.
Trunk supports --help
on all levels of commands and sub-commands. This will show you the available options, as well
as the names of the environment variables to use instead.
All relative paths will be resolved against the project root, as evaluated by loading the configuration.
Migration from pre 0.21.0 the best approach to moving forward
While the goal was to support all fields from Trunk.toml
, the command line arguments as well as the environment
variables, it still is a version breaking the API. In some cases, it just made little sense, and so those fields
got marked "deprecated". They trigger a warning today and might be removed in one of the next releases.
Ideally, you don't need to change anything. In some ideal cases, you don't even need any configuration. In case you do,
you now have some more choices. You can keep using TOML, you may hide it using .trunk.*
variant. You can use YAML or
JSON to leverage the JSON schema that is generated. Or if you're a fan of keeping everything in Cargo.toml
, that's
fine too. The choice is yours.
You need to take care when working with older versions of Trunk though. If you use an older version of Trunk
(before 0.21.0) with a project using the newer configuration files, then that version would not consider those files
and might consider default settings, due to the missing Trunk.toml
file.
Required version
Starting with 0.19.0-alpha.2
, it is possible to enforce having a certain version of trunk building the project.
As new features get added to trunk, this might be helpful to ensure that the version of trunk building the current
is actually capable of doing so. This can be done using the trunk-version
(or using the alias trunk_version
) on
the root level of the Trunk.toml
file.
The version format is a "version requirement", the same format you might know from Cargo's version field on dependencies.
This also supports pre-release requirements, which allows adopting upcoming features early.
Versions prior do 0.19.0-alpha.2
currently do not support this check, and so they will silently ignore
such an error for now.
Configuration Schema
Trunk provides a JSON schema for the configuration model. This can be added to e.g. a YAML file using the following syntax:
$schema: "./schema.json"
Obtaining the schema
You can generate the schema by running:
trunk config generate-schema
Or directly write it to a file:
trunk config generate-schema path/to/file
Editor/IDE support
Your editor/IDE needs to support this functionality. Trunk only provides the schema. The following sections provide some information on how to use this.
IntelliJ (and alike)
IntelliJ based IDEs (including Rust Rover) do support JSON schemas in YAML and JSON files. You only need to reference the schema, like:
$schema: "./schema.json"
Hooks
If you find that you need Trunk to perform an additional build action that isn't supported directly, then Trunk's flexible hooks system can be used to launch external processes at various stages in the pipeline.
Build steps
This is a brief overview of Trunk's build process for the purpose of describing when hooks are executed. Please note that the exact ordering may change in the future to add new features.
- Step 1 — Read and parse the HTML file.
- Step 2 — Produce a plan of all assets to be built.
- Step 3 — Build all assets in parallel.
- Step 4 — Finalize and write assets to staging directory.
- Step 5 — Write HTML to staging directory.
- Step 6 - Replace
dist
directory contents with staging directory contents.
The hook stages correspond to this as follows:
pre_build
: takes place before step 1.build
: takes place at the same time as step 3, executing in parallel with asset builds.post_build
: takes place after step 5 and before step 6.
Hook execution
Hooks can be declared exclusively in Trunk.toml
, and consist of a stage
, command
and command_arguments
:
stage
: (required) one ofpre_build
,build
orpost_build
. It specifies when in Trunk's build pipeline the hook is executed.command
: (required) the name or path to the desired executable.command_arguments
: (optional, defaults to none) any arguments to be passed, in the given order, to the executable.
At the relevant point for each stage, all hooks for that stage are spawned simultaneously. After this, Trunk immediately
waits for all the hooks to exit before proceeding, except in the case of the build
stage, described further below.
All hooks are executed using the same stdin
and stdout
as trunk. The executable is expected to return an error code
of 0
to indicate success. Any other code will be treated as an error and terminate the build process. Additionally,
the following environment variables are provided to the process:
TRUNK_PROFILE
: the build profile in use. Currently, eitherdebug
orrelease
.TRUNK_HTML_FILE
: the full path to the HTML file (typicallyindex.html
inTRUNK_SOURCE_DIR
) used by trunk.TRUNK_SOURCE_DIR
: the full path to the source directory in use by Trunk. This is always the directory in whichTRUNK_HTML_FILE
resides.TRUNK_STAGING_DIR
: the full path of the Trunk staging directory.TRUNK_DIST_DIR
: the full path of the Trunk dist directory.TRUNK_PUBLIC_URL
: the configured public URL for Trunk.
OS-specific overrides
Often times you will want to perform the same build step on different OSes, requiring different commands.
A typical example of this is using the sh
command on Linux, but cmd
on Windows.
To accomodate this, you can optionally create OS-specific overrides for each hook.
To do this, specify the default hook, then directly below it create a [hooks.<os>]
entry where <os>
can be one of windows
, macos
, or linux
. Within this entry you must specify only the command
and
command_argumnets
keys. You may provide multiple overrides for each hook. i.e.
One for windows
, one for macos
, and one for linux
.
Assets
Declaring assets to be processed by Trunk is simple and extensible.
Link Asset Types
All link assets to be processed by Trunk must follow these three rules:
- Must be declared as a valid HTML
link
tag. - Must have the attribute
data-trunk
. - Must have the attribute
rel="{type}"
, where{type}
is one of the asset types listed below.
This will typically look like: <link data-trunk rel="{type}" href="{path}" ..other options here.. />
. Each asset type described below specifies the required and optional attributes for its asset type. All <link data-trunk .../>
HTML elements will be replaced with the output HTML of the associated pipeline.
rust
✅ rel="rust"
: Trunk will compile the specified Cargo project as WASM and load it. This is optional. If not specified, Trunk will look for a Cargo.toml
in the parent directory of the source HTML file.
href
: (optional) the path to theCargo.toml
of the Rust project. If a directory is specified, then Trunk will look for theCargo.toml
in the given directory. If no value is specified, then Trunk will look for aCargo.toml
in the parent directory of the source HTML file.data-target-name
: (optional) the name of the target artifact to load. If the Cargo project has multiple targets (binaries and library), this value can be used to select which one should be used by trunk.data-bin
: (optional) the name of the binary to compile and load. If the Cargo project has multiple binaries, this value can be used to specify that a specific binary should be compiled (using--bin
) and used by trunk. This implicitly includesdata-target-name
.data-type
: (optional) specifies how the binary should be loaded into the project. Can be set tomain
orworker
.main
is the default. There can only be onemain
link. For workers a wasm-bindgen javascript wrapper and the wasm file (with_bg.wasm
suffix) is created, named after the binary name (if provided) or project name. See one of the webworker examples on how to load them.data-cargo-features
: (optional) Space or comma separated list of cargo features to activate.data-cargo-no-default-features
: (optional) Disables the default Cargo features.data-cargo-all-features
: (optional) Enables all Cargo features.- Neither compatible with
data-cargo-features
nordata-cargo-no-default-features
.
- Neither compatible with
data-wasm-opt
: (optional) run wasm-opt with the set optimization level. The possible values are0
,1
,2
,3
,4
,s
,z
or an empty value for wasm-opt's default. Set this option to0
to disable wasm-opt explicitly. The values1-4
are increasingly stronger optimization levels for speed.s
andz
(z means more optimization) optimize for binary size instead. Only used in--release
mode.data-wasm-opt-params
: (optional) run wasm-opt with the additional params. Only used in--release
mode.data-keep-debug
: (optional) instructwasm-bindgen
to preserve debug info in the final WASM output, even for--release
mode. This may conflict with the use of wasm-opt, so to be sure, it is recommended to setdata-wasm-opt="0"
when using this option.data-no-demangle
: (optional) instructwasm-bindgen
to not demangle Rust symbol names.data-reference-types
: (optional) instructwasm-bindgen
to enable reference types.data-weak-refs
: (optional) instructwasm-bindgen
to enable weak references.data-typescript
: (optional) instructwasm-bindgen
to output Typescript bindings. Defaults to false.data-bindgen-target
: (optional) specifies the value of thewasm-bindgen
flag--target
(see link for possible values). Defaults tono-modules
. The main use-case is to switch toweb
withdata-type="worker"
which reduces backwards compatibility but with some advantages.data-loader-shim
: (optional) instructtrunk
to create a loader shim for web workers. Defaults to false.data-cross-origin
: (optional) thecrossorigin
setting when loading the code & script resources. Defaults to plainanonymous
.data-integrity
: (optional) theintegrity
digest type for code & script resources. Defaults to plainsha384
.data-wasm-no-import
: (optional) by default, Trunk will generate an import of functions exported from Rust. Enabling this flag disables this feature. Defaults to false.data-wasm-import-name
: (optional) the name of the global variable where the functions imported from WASM will be available (under thewindow
object). Defaults towasmBindings
(which makes them available viawindow.wasmBindings.<functionName>
).data-target-path
: (optional) Path where the output is placed inside the dist dir. If not present, the directory is placed in the dist root. The path must be a relative path without..
.data-initializer
: (optional) Path to the (module) JavaScript file of the initializer.data-cargo-profile
: (optional) A cargo profile to use, instead of the default, for both release or dev mode.data-cargo-profile-release
: (optional) A cargo profile to use, instead of the default, for the release mode. Overrides thedata-cargo-profile
setting.data-cargo-profile-dev
: (optional) A cargo profile to use, instead of the default, for the dev mode. Overrides thedata-cargo-profile
setting.
sass/scss
✅ rel="sass"
or rel="scss"
: Trunk uses the official dart-sass for compilation. Just link to your sass files from your source HTML, and Trunk will handle the rest. This content is hashed for cache control. The href
attribute must be included in the link pointing to the sass/scss file to be processed.
data-inline
: (optional) this attribute will inline the compiled CSS from the SASS/SCSS file into a<style>
tag instead of using a<link rel="stylesheet">
tag.data-integrity
: (optional) theintegrity
digest type for code & script resources. Defaults to plainsha384
.data-target-path
: (optional) Path where the output is placed inside the dist dir. If not present, the directory is placed in the dist root. The path must be a relative path without..
.
css
✅ rel="css"
: Trunk will copy linked css files found in the source HTML without content modification. This content is hashed for cache control. The href
attribute must be included in the link pointing to the css file to be processed.
- In the future, Trunk will resolve local
@imports
, will handle minification (see trunk#7), and we may even look into a pattern where any CSS found in the source tree will be bundled, which would enable a nice zero-config "component styles" pattern. See trunk#3 for more details. data-integrity
: (optional) theintegrity
digest type for code & script resources. Defaults to plainsha384
.data-no-minify
: (optional) Opt-out of minification. Also see: Minification.data-target-path
: (optional) Path where the output is placed inside the dist dir. If not present, the directory is placed in the dist root. The path must be a relative path without..
.
tailwind
✅ rel="tailwind-css"
: Trunk uses the official tailwindcss cli for compilation. Just link to your tailwind css files from your source HTML, and Trunk will handle the rest. This content is hashed for cache control. The href
attribute must be included in the link pointing to the sass/scss file to be processed.
data-inline
: (optional) this attribute will inline the compiled CSS from the tailwind compilation into a<style>
tag instead of using a<link rel="stylesheet">
tag.data-integrity
: (optional) theintegrity
digest type for code & script resources. Defaults to plainsha384
.data-no-minify
: (optional) Opt-out of minification. Also see: Minification.data-target-path
: (optional) Path where the output is placed inside the dist dir. If not present, the directory is placed in the dist root. The path must be a relative path without..
.
icon
✅ rel="icon"
: Trunk will copy the icon image specified in the href
attribute to the dist
dir. This content is hashed for cache control.
data-integrity
: (optional) theintegrity
digest type for code & script resources. Defaults to plainsha384
.data-no-minify
: (optional) Opt-out of minification. Also see: Minification.data-target-path
: (optional) Path where the output is placed inside the dist dir. If not present, the directory is placed in the dist root. The path must be a relative path without..
.
inline
✅ rel="inline"
: Trunk will inline the content of the file specified in the href
attribute into index.html
. This content is copied exactly, no hashing is performed.
type
: (optional) – If not present, the type is inferred by the file extension.html
,svg
css
: CSS wrapped instyle
tagsjs
: JavaScript wrapped inscript
tagsmjs
,module
: JavaScript wrapped inscript
tags withtype="module"
copy-file
✅ rel="copy-file"
: Trunk will copy the file specified in the href
attribute to the dist
dir. This content is copied exactly, no hashing is performed.
data-target-path
: (optional) Path where the output is placed inside the dist dir. If not present, the directory is placed in the dist root. The path must be a relative path without..
.
copy-dir
✅ rel="copy-dir"
: Trunk will recursively copy the directory specified in the href
attribute to the dist
dir. This content is copied exactly, no hashing is performed.
data-target-path
: (optional) Path where the output is placed inside the dist dir. If not present, the directory is placed in the dist root. The path must be a relative path without..
.
Script Asset Types
Script assets are bit more diverse.
Script Assets
Classic script assets processed by Trunk must follow these three rules:
- Must be declared as a valid HTML
script
tag. - Must have the attribute
data-trunk
. - Must have the attribute
src
, pointing to a script file
A valid HTML script
tag always has an end tag (like <script></script>
). A self-closing script tag
(like <script />
) is not avalid HTML script tag and will trigger a warning an may create a non-working HTML file.
This will typically look like: <script data-trunk src="{path}" ..other options here..></script>
. All <script data-trunk ...></script>
HTML elements will be replaced with the output HTML of the associated pipeline.
Trunk will copy script files found in the source HTML without content modification. This content is hashed for cache control. The src
attribute must be included in the script pointing to the script file to be processed.
data-no-minify
: (optional) Opt-out of minification. Also see: Minification.data-target-path
: (optional) Path where the output is placed inside the dist dir. If not present, the directory is placed in the dist root. The path must be a relative path without..
.
JS Snippets
JS snippets generated from the wasm-bindgen JS snippets feature are automatically copied to the dist dir, hashed and ready to rock. No additional setup is required. Just use the feature in your application, and Trunk will take care of the rest.
Images & Other Resources
Images and other resource types can be copied into the dist
dir by adding a link like this to your source HTML: <link data-trunk rel="copy-file" href="path/to/image"/>
. Any normal file type is supported. This will cause Trunk to find the target resource, and copy it to the dist
dir unmodified. No hashing will be applied. The link itself will be removed from the HTML. To copy an entire directory of assets/images, you can use the following HTML: <link data-trunk rel="copy-dir" href="path/to/images-dir"/>
.
This will allow your WASM application to reference images directly from the dist
dir, and Trunk will ensure that the images are available in the dist
dir to be served.
As Trunk continues to mature, we will find better ways to include images and other resources. Hashing content for cache control is great, we just need to find a nice pattern to work with images referenced in Rust components. Please contribute to the discussion over in trunk#9! See you there.
Directives
You can instruct Trunk to write the URL passed to --public-url
to the HTML output by adding this to your <head>
: <base data-trunk-public-url/>
.
Trunk will set the href
attribute of the element to the public URL. This changes the behavior of relative URLs to be relative to the public URL instead of the current location.
You can also access this value at runtime using document.baseURI
which is useful for apps that need to know the base URL on which they're hosted (e.g. for routing).
Minification
Trunk supports minifying assets. This is disabled by default and can be controlled on various levels.
In any case, Trunk does not perform minification itself, but delegates the process to dependencies which do the actual implementation. In cases where minification breaks things, it will, most likely, be an issue with that dependency.
Starting with Trunk 0.20.0, minification is disabled by default. It can be turned on from the command line using the
--minify
(or -M
) switch. Alternatively, it can be controlled using the build.minify
field in the Trunk.toml
file. The value of this field is an enum, with the following possible values: never
(default, never minify),
on_release
(minify when running Trunk with --release
), always
(always minify).
When minification is enabled, all assets known to trunk (this excludes the copy-dir
and copy-file
opaque blobs to
Trunk), will get minified. It is possible to opt out of this process on a per-asset basis using the data-no-minify
attribute (see individual asset configuration). In this case, the asset will never get minified.
Sub-resource integrity
Trunk can automatically generate hashes of files and add the integrity
attribute for resources fetched by the web
application. This is enabled by default, but can be overridden using the data-integrity
attribute. See the different
asset types.
The following values are available:
none
sha256
sha384
(default)sha512
Advanced
There are some more advanced topics, which will be described in the following subsections.
JavaScript interoperability
Trunk will create the necessary JavaScript code to bootstrap and run the WebAssembly based application. It will also
include all JavaScript snippets generated by wasm-bindgen
for interfacing with JavaScript functionality.
By default, functions exported from Rust, using wasm-bingen
, can be accessed in the JavaScript code through the global
variable window.wasmBindings
. This behavior can be disabled, and the name can be customized. For more information
see the rust
asset type.
Order of initialization
The bindings will only be available and working when the application initialization has been completed.
If your WebAssembly application renders code into the web page/DOM tree, which then calls from JavaScript into the WebAssembly application, then this will not be an issue, as the application is already initialized.
However, if you want to call into the WebAssembly application from, for example, the index.html
file itself, then
you must delay that call until the application is started.
This can be ensured by executing that code with the TrunkApplicationStartup
event. Also see
Startup Event.
Startup event
The initializer code snippet of Trunk will emit an event when the WebAssembly application has been loaded and started.
Definition
The event is called TrunkApplicationStarted
and is executed after the WebAssembly has been loaded and initialized.
The event will have custom details:
{
wasm // The web assembly instance
}
Example
The following snippet can be used to run code after the initialization of the WebAssembly application:
<script type="module">
addEventListener("TrunkApplicationStarted", (event) => {
console.log("application started - bindings:", window.wasmBindings, "WASM:", event.detail.wasm);
// wasm_ffi is a function exported from WASM to JavaScript
window.wasmBindings.wasm_ffi();
// You can also run this via the WASM instance in the details
// event.detail.wasm.wasm_ffi();
});
</script>
Also see the vanilla example: https://github.com/trunk-rs/trunk/tree/main/examples/vanilla.
Initializer
Since: 0.19.0-alpha.1
.
Trunk supports tapping into the initialization process of the WebAssembly application. By default, this is not active and works the same way as with previous versions.
The default process is that trunk injects a small JavaScript snippet, which imports the JavaScript loader generated
by wasm_bindgen
and calls the init
method. That will fetch the WASM blob and run it.
The downside of this is, that during this process, there's no feedback for the user. Neither when it takes a bit longer to load the WASM file, nor when something goes wrong.
Now it is possible to tap into this process by setting data-initializer
to a JavaScript module file. This module file
is required to (default) export a function, which returns the "initializer" instance. Here is an example:
export default function myInitializer () {
return {
onStart: () => {
// called when the loading starts
},
onProgress: ({current, total}) => {
// the progress while loading, will be called periodically.
// "current" will contain the number of bytes of the WASM already loaded
// "total" will either contain the total number of bytes expected for the WASM, or if the server did not provide
// the content-length header it will contain 0.
},
onComplete: () => {
// called when the initialization is complete (successfully or failed)
},
onSuccess: (wasm) => {
// called when the initialization is completed successfully, receives the `wasm` instance
},
onFailure: (error) => {
// called when the initialization is completed with an error, receives the `error`
}
}
};
For a full example, see: https://github.com/trunk-rs/trunk/tree/main/examples/initializer.
Library crate
Aside from having a main
function, it is also possible to up your project as a cdylib
project. In order to do that,
add the following to your Cargo.toml
:
[lib]
crate-type = ["cdylib", "rlib"]
And then, define the entrypoint in your lib.rs
like (does not need to be async
):
#![allow(unused)] fn main() { #[wasm_bindgen(start)] pub async fn run() {} }
Base URLs, public URLs, paths & reverse proxies
Since: 0.19.0-alpha.3
.
Originally trunk
had a single --public-url
, which allowed to set the base URL of the hosted application.
Plain and simple. This was a prefix for all URLs generated and acted as a base for trunk serve
.
Unfortunately, life isn't that simple and naming is hard.
Today trunk
was three paths:
- The "public base URL": acting as a prefix for all generated URLs
- The "serve base": acting as a scope/prefix for all things served by
trunk serve
- The "websocket base": acting as a base path for the auto-reload websocket
All three can be configured, but there are reasonable defaults in place. By default, the serve base and websocket base default to the absolute path of the public base. The public base will have a slash appended if it doesn't have one. The public base can be one of:
- Unset/nothing/default (meaning
/
) - An absolute URL (e.g.
http://domain/path/app
) - An absolute path (e.g.
/path/app
) - A relative path (e.g.
foo
or./
)
If the public base is an absolute URL, then the path of that URL will be used as serve and websocket base. If the public base is a relative path, then it will be turned into an absolute one. Both approaches might result in a dysfunctional application, based on your environment. There will be a warning on the console. However, by providing an explicit value using serve-base or ws-base, this can be fixed.
Why is this necessary and when is it useful? It's mostly there to provide all the knobs/configurations for the case that weren't considered. The magic of public-url worked for many, but not for all. To support such cases, it is now possible to tweak all the settings, at the cost of more complexity. Having reasonable defaults should keep it simple for the simple cases.
An example use case is a reverse proxy in front of trunk serve
, which can't be configured to serve the trunk
websocket at the location trunk serve
expects it. Now, it is possible to have --public-url
to choose the base when
generating links, so that it looks correct when being served by the proxy. But also use --serve-base /
to keep
serving resource from the root.
Backend Proxy
Trunk ships with a built-in proxy which can be enabled when running trunk serve
. There are two ways to configure the
proxy, each discussed below. All Trunk proxies will transparently pass along the request body, headers, and query
parameters to the proxy backend.
Proxy CLI Flags
The trunk serve
command accepts two proxy related flags.
--proxy-backend
specifies the URL of the backend server to which requests should be proxied. The URI segment of the
given URL will be used as the path on the Trunk server to handle proxy requests.
E.G., trunk serve --proxy-backend=http://localhost:9000/api/
will proxy any requests received on the path /api/
to
the server listening at http://localhost:9000/api/
. Further path segments or query parameters will be seamlessly
passed along.
--proxy-rewrite
specifies an alternative URI on which the Trunk server is to listen for proxy requests. Any requests
received on the given URI will be rewritten to match the URI of the proxy backend, effectively stripping the rewrite
prefix. E.G., trunk serve --proxy-backend=http://localhost:9000/ --proxy-rewrite=/api/
will proxy any requests
received on /api/
over to http://localhost:9000/
with the /api/
prefix stripped from the request, while everything
following the /api/
prefix will be left unchanged.
--proxy-insecure
allows the --proxy-backend
url to use a self-signed certificate for https (or any
officially invalid
certs, including expired). This would be used when proxying to https such
as trunk serve --proxy-backend=https://localhost:3001/ --proxy-insecure
where the ssl cert was self-signed, such as
with mkcert, and routed through an https reverse proxy for the backend, such
as local-ssl-proxy
or caddy.
--proxy-no-sytem-proxy
bypasses the system proxy when contacting the proxy backend.
--proxy-ws
specifies that the proxy is for a WebSocket endpoint.
Config File
The Trunk.toml
config file accepts multiple [[proxy]]
sections, which allows for multiple proxies to be configured.
Each section requires at least the backend
field, and optionally accepts the rewrite
and ws
fields, both
corresponding to the --proxy-*
CLI flags discussed above.
As it is with other Trunk config, a proxy declared via CLI will take final precedence and will cause any config file proxies to be ignored, even if there are multiple proxies declared in the config file.
The following is a snippet from the Trunk.toml
file in the Trunk repo:
[[proxy]]
rewrite = "/api/v1/"
backend = "http://localhost:9000/"
Contributing
Anyone and everyone is welcome to contribute! Please review the CONTRIBUTING.md document for more details. The best way to get started is to find an open issue, and then start hacking on implementing it. Letting other folks know that you are working on it, and sharing progress is a great approach. Open pull requests early and often, and please use GitHub's draft pull request feature.