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
#![deny(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]

//! 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).
//!
//! # Minimum Supported Rust Version
//!
//! This crate requires **Rust 1.58.1** at a minimum because there is a security
//! issue on a function we use from std in previous version
//! (see [cve-2022-21658](https://groups.google.com/g/rustlang-security-announcements/c/R1fZFDhnJVQ)).
//!
//! # 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",
//!   ]
//!   ```
//!
//! * 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 the following to the xtask package's `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! xtask-wasm = "0.1.0"
//! ```
//!
//! # Usage
//!
//! This library gives you three structs:
//!
//! * [`Dist`](crate::dist::Dist) - 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`](crate::dev_server::DevServer) - 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.
//!
//! 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, default_dist_dir};
//!
//! #[derive(clap::Parser)]
//! enum Opt {
//!     Dist(xtask_wasm::Dist),
//!     Watch(xtask_wasm::Watch),
//!     Start(xtask_wasm::DevServer),
//! }
//!
//!
//! fn main() -> Result<()> {
//!     let opt: Opt = clap::Parser::parse();
//!
//!     match opt {
//!         Opt::Dist(dist) => {
//!             log::info!("Generating package...");
//!
//!             dist
//!                 .dist_dir_path("dist")
//!                 .static_dir_path("my-project/static")
//!                 .app_name("my-project")
//!                 .run_in_workspace(true)
//!                 .run("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(mut dev_server) => {
//!             log::info!("Starting the development server...");
//!
//!             dev_server.arg("dist").start(default_dist_dir(false))?;
//!         }
//!     }
//!
//!     Ok(())
//! }
//! ```
//!
//! ## [`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 customized dist process
//! that use the `wasm-opt` feature.
//!
//! The available subcommands are:
//!
//! * Build the web app package.
//!
//!   ```console
//!   cargo xtask dist
//!   ```
//!   * Build the web app package, download the [`wasm-opt`](https://github.com/WebAssembly/binaryen#tools)
//!     binary and optimize the Wasm generated by the dist process.
//!
//!     ```console
//!     cargo xtask dist --optimize
//!     ```
//!
//! * 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`](crate::wasm_opt::WasmOpt) struct that helps downloading
//!     and using [`wasm-opt`](https://github.com/WebAssembly/binaryen#tools) very easily.
//! * `run-example`: a helper to run examples from `examples/` directory using a development
//!     server.
//! * `sass`: allow the use of SASS/SCSS in your project.
//!
//! # 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 {}
//! ```

#[macro_use]
mod cfg;

cfg_not_wasm32! {
    use std::process::Command;

    pub use xtask_watch::{
        anyhow, cargo_metadata, cargo_metadata::camino, clap, metadata, package, xtask_command,
        Watch,
    };

    mod dev_server;
    mod dist;

    pub use dev_server::*;
    pub use dist::*;

    cfg_run_example! {
        pub use env_logger;
        pub use log;
    }

    cfg_wasm_opt! {
        mod wasm_opt;
        pub use wasm_opt::*;
    }

    cfg_sass! {
        pub use sass_rs;
    }

    /// Get the default command for the build in the dist process.
    ///
    /// This is `cargo build --target wasm32-unknown-unknown`.
    pub fn default_build_command() -> Command {
        let mut command = Command::new("cargo");
        command.args(["build", "--target", "wasm32-unknown-unknown"]);
        command
    }
}

cfg_wasm32! {
    cfg_run_example! {
        pub use console_error_panic_hook;
        pub use wasm_bindgen;
    }
}

cfg_run_example! {
    pub use xtask_wasm_run_example::*;
}