1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
//! This crate aims to provide an easy and customizable way to help you build
//! Wasm projects by extending them with custom subcommands, based on the
//! [`xtask` concept](https://github.com/matklad/cargo-xtask/), instead of using
//! external tooling like [`wasm-pack`](https://github.com/rustwasm/wasm-pack).
//!
//! **[Changelog](https://github.com/rustminded/xtask-wasm/blob/main/CHANGELOG.md)**
//!
//! # Why xtask-wasm?
//!
//! ## No external tools to install
//!
//! `wasm-pack` and `trunk` are separate binaries that must be installed outside
//! of Cargo — via `cargo install`, a shell script, or a system package manager.
//! This means every contributor and every CI machine needs an extra installation
//! step, and there is no built-in guarantee that everyone is running the same
//! version.
//!
//! With xtask-wasm, `cargo xtask` is all you need. The build tooling is a
//! regular Cargo dependency, versioned in your `Cargo.lock` and reproduced
//! exactly like every other dependency in your project.
//!
//! ## `wasm-bindgen` version is always in sync
//!
//! This is the most common source of pain with `wasm-pack` and `trunk`: the
//! `wasm-bindgen` CLI tool version must exactly match the `wasm-bindgen` library
//! version declared in your `Cargo.toml`. When they drift — after a `cargo
//! update`, a fresh clone, or a CI cache invalidation — you get a cryptic error
//! at runtime rather than a clear compile-time failure.
//!
//! xtask-wasm uses [`wasm-bindgen-cli-support`](https://crates.io/crates/wasm-bindgen-cli-support)
//! as a library dependency. The version is pinned in your `Cargo.lock` alongside
//! your `wasm-bindgen` library dependency and kept in sync automatically — no
//! manual version matching required.
//!
//! ## Fully customizable
//!
//! Because the build process is plain Rust code living inside your workspace,
//! you can extend, replace or wrap any step. `wasm-pack` and `trunk` are
//! opaque binaries driven by configuration files; xtask-wasm gives you the full
//! build logic as code, under your control.
//!
//! # Setup
//!
//! The best way to add xtask-wasm to your project is to create a workspace
//! with two packages: your project's package and the xtask package.
//!
//! ## Create a project using xtask
//!
//! * Create a new directory that will contains the two package of your project
//! and the workspace's `Cargo.toml`:
//!
//! ```console
//! mkdir my-project
//! cd my-project
//! touch Cargo.toml
//! ```
//!
//! * Create the project package and the xtask package using `cargo new`:
//!
//! ```console
//! cargo new my-project
//! cargo new xtask
//! ```
//!
//! * Open the workspace's `Cargo.toml` and add the following:
//!
//! ```toml
//! [workspace]
//! default-members = ["my-project"]
//! members = [
//! "my-project",
//! "xtask",
//! ]
//! resolver = "2"
//! ```
//!
//! * Create a `.cargo/config.toml` file and add the following content:
//!
//! ```toml
//! [alias]
//! xtask = "run --package xtask --"
//! ```
//!
//! The directory layout should look like this:
//!
//! ```console
//! project
//! ├── .cargo
//! │ └── config.toml
//! ├── Cargo.toml
//! ├── my-project
//! │ ├── Cargo.toml
//! │ └── src
//! │ └── ...
//! └── xtask
//! ├── Cargo.toml
//! └── src
//! └── main.rs
//! ```
//!
//! And now you can run your xtask package using:
//!
//! ```console
//! cargo xtask
//! ```
//!
//! You can find more informations about xtask
//! [here](https://github.com/matklad/cargo-xtask/).
//!
//! ## Use xtask-wasm as a dependency
//!
//! Finally, add `xtask-wasm` to your dependencies:
//!
//! ```console
//! cargo add -p xtask xtask-wasm
//! ```
//!
//! # Usage
//!
//! This library gives you three structs:
//!
//! * [`Dist`](https://docs.rs/xtask-wasm/latest/xtask_wasm/struct.Dist.html) - Generate a distributed package for Wasm.
//! * [`Watch`](https://docs.rs/xtask-watch/latest/xtask_watch/struct.Watch.html) -
//! Re-run a given command when changes are detected
//! (using [xtask-watch](https://github.com/rustminded/xtask-watch)).
//! * [`DevServer`](https://docs.rs/xtask-wasm/latest/xtask_wasm/struct.DevServer.html) - Serve your project at a given IP address.
//!
//! They all implement [`clap::Parser`](https://docs.rs/clap/latest/clap/trait.Parser.html)
//! allowing them to be added easily to an existing CLI implementation and are
//! flexible enough to be customized for most use-cases.
//!
//! The pre and post hooks of [`DevServer`](https://docs.rs/xtask-wasm/latest/xtask_wasm/struct.DevServer.html)
//! accept any type implementing the
//! [`Hook`](https://docs.rs/xtask-wasm/latest/xtask_wasm/trait.Hook.html) trait.
//! This lets you construct a [`std::process::Command`] based on the server's final configuration
//! — for example, to pass the resolved `dist_dir` or `port` as arguments to an external tool.
//! A blanket implementation is provided for [`std::process::Command`] itself, so no changes are
//! needed for simple use-cases.
//!
//! Asset files copied by [`Dist`](https://docs.rs/xtask-wasm/latest/xtask_wasm/struct.Dist.html)
//! can be processed by types implementing the
//! [`Transformer`](https://docs.rs/xtask-wasm/latest/xtask_wasm/trait.Transformer.html) trait.
//! Transformers are tried in order for each file; the first to return `Ok(true)` claims the file,
//! while unclaimed files are copied verbatim. When the `sass` feature is enabled,
//! [`SassTransformer`](https://docs.rs/xtask-wasm/latest/xtask_wasm/struct.SassTransformer.html)
//! is available to compile SASS/SCSS files to CSS.
//!
//! You can find further information for each type at their documentation level.
//!
//! # Examples
//!
//! ## A basic implementation
//!
//! ```rust,no_run
//! use std::process::Command;
//! use xtask_wasm::{anyhow::Result, clap};
//!
//! #[derive(clap::Parser)]
//! enum Opt {
//! Dist(xtask_wasm::Dist),
//! Watch(xtask_wasm::Watch),
//! Start(xtask_wasm::DevServer),
//! }
//!
//!
//! fn main() -> Result<()> {
//! env_logger::builder()
//! .filter_level(log::LevelFilter::Info)
//! .init();
//!
//! let opt: Opt = clap::Parser::parse();
//!
//! match opt {
//! Opt::Dist(dist) => {
//! log::info!("Generating package...");
//!
//! dist
//! .assets_dir("my-project/assets")
//! .app_name("my-project")
//! .build("my-project")?;
//! }
//! Opt::Watch(watch) => {
//! log::info!("Watching for changes and check...");
//!
//! let mut command = Command::new("cargo");
//! command.arg("check");
//!
//! watch.run(command)?;
//! }
//! Opt::Start(dev_server) => {
//! log::info!("Starting the development server...");
//!
//! dev_server
//! .xtask("dist")
//! .start()?;
//! }
//! }
//!
//! Ok(())
//! }
//! ```
//!
//! Note: this basic implementation uses `env_logger` and `log`. Add them to the `Cargo.toml` of
//! your `xtask` (or use your preferred logger).
//!
//! ## [`examples/demo`](https://github.com/rustminded/xtask-wasm/tree/main/examples/demo)
//!
//! Provides a basic implementation of xtask-wasm to generate the web app
//! package, an "hello world" app using [Yew](https://yew.rs/). This example
//! demonstrates a simple directory layout and a dist process that uses the
//! `wasm-opt` feature via [`Dist::optimize_wasm`].
//!
//! The available subcommands are:
//!
//! * Build and optimize the web app package (downloads
//! [`wasm-opt`](https://github.com/WebAssembly/binaryen#tools) if not cached).
//!
//! ```console
//! cargo xtask dist
//! ```
//!
//! * Build the web app package and watch for changes in the workspace root.
//!
//! ```console
//! cargo xtask watch
//! ```
//!
//! * Serve an optimized web app dist on `127.0.0.1:8000` and watch for
//! changes in the workspace root.
//!
//! ```console
//! cargo xtask start
//! ```
//!
//! Additional flags can be found using `cargo xtask <subcommand> --help`.
//!
//! This example also demonstrates the use of the `run-example` feature that allows you to use the
//! following:
//!
//! ```console
//! cargo run --example run_example
//! ```
//!
//! This command will run the code in `examples/run_example` using the development server.
//!
//! # Features
//!
//! * `wasm-opt`: enable the
//! [`WasmOpt`](https://docs.rs/xtask-wasm/latest/xtask_wasm/struct.WasmOpt.html) struct and
//! [`Dist::optimize_wasm`](https://docs.rs/xtask-wasm/latest/xtask_wasm/struct.Dist.html#method.optimize_wasm)
//! for downloading and running [`wasm-opt`](https://github.com/WebAssembly/binaryen#tools)
//! automatically as part of the dist build. This is the recommended way to integrate wasm-opt —
//! no custom wrapper struct or manual path computation needed:
//!
//! ```rust,ignore
//! // requires the `wasm-opt` feature
//! dist.optimize_wasm(WasmOpt::level(1).shrink(2))
//! .build("my-project")?;
//! ```
//!
//! * `run-example`: a helper to run examples from `examples/` directory using a development
//! server.
//! * `sass`: enable SASS/SCSS compilation via [`SassTransformer`](https://docs.rs/xtask-wasm/latest/xtask_wasm/struct.SassTransformer.html).
//! Add it to your [`Dist`](https://docs.rs/xtask-wasm/latest/xtask_wasm/struct.Dist.html) with `.transformer(SassTransformer::default())`.
//!
//! # Troubleshooting
//!
//! When using the re-export of [`clap`](https://docs.rs/clap/latest/clap), you
//! might encounter this error:
//!
//! ```console
//! error[E0433]: failed to resolve: use of undeclared crate or module `clap`
//! --> xtask/src/main.rs:4:10
//! |
//! 4 | #[derive(Parser)]
//! | ^^^^^^ use of undeclared crate or module `clap`
//! |
//! = note: this error originates in the derive macro `Parser` (in Nightly builds, run with -Z macro-backtrace for more info)
//! ```
//!
//! This occurs because you need to import clap in the scope too. This error can
//! be resolved like this:
//!
//! ```rust
//! use xtask_wasm::clap;
//!
//! #[derive(clap::Parser)]
//! struct MyStruct {}
//! ```
//!
//! Or like this:
//!
//! ```rust
//! use xtask_wasm::{clap, clap::Parser};
//!
//! #[derive(Parser)]
//! struct MyStruct {}
//! ```
use Command;
pub use ;
pub use *;
pub use *;
pub use *;
pub use *;
pub use sass_rs;
pub use env_logger;
pub use log;
/// Get the default command for the build in the dist process.
///
/// This is `cargo build --target wasm32-unknown-unknown`.
pub use console_error_panic_hook;
pub use wasm_bindgen;
pub use *;