|
| 1 | +- Start Date: 2019-03-06 |
| 2 | +- RFC PR: https://github.com/rustwasm/rfcs/pull/10 |
| 3 | +- Tracking Issue: (leave this empty) |
| 4 | + |
| 5 | +# Summary |
| 6 | +[summary]: #summary |
| 7 | + |
| 8 | +Add two new sub-commands to `wasm-pack`: |
| 9 | + |
| 10 | +1. `watch`: watch the crate's files and automatically rebuild whenever they |
| 11 | + change. |
| 12 | +2. `serve`: start an HTTP server for the crate directory on `localhost`. |
| 13 | + |
| 14 | +# Motivation |
| 15 | +[motivation]: #motivation |
| 16 | + |
| 17 | +Enable a smooth and complete local development experience for users who are not |
| 18 | +using a JavaScript bundler. |
| 19 | + |
| 20 | +In particular, we would like to [remove the `npm` and bundler usage from our |
| 21 | +Game of Life tutorial][no-bundler-in-docs] without requiring readers to install |
| 22 | +any additional tools other than the Rust toolchain and `wasm-pack` when setting |
| 23 | +up their development environment. The goal here being less moving parts to learn |
| 24 | +and tools to wrangle when first exploring Rust and WebAssembly. |
| 25 | + |
| 26 | +Additionally, we would like to make sure that the local development server uses |
| 27 | +the `application/wasm` MIME type when serving Wasm binaries. This enables the |
| 28 | +`WebAssembly.instantiateStreaming` fast-path for users. |
| 29 | + |
| 30 | +[no-bundler-in-docs]: https://github.com/rustwasm/book/issues/150 |
| 31 | + |
| 32 | +# Stakeholders |
| 33 | +[stakeholders]: #stakeholders |
| 34 | + |
| 35 | +The stakeholders are primarily people that are doing Rust and Wasm development |
| 36 | +without a JavaScript bundler, which potentially will include all of our new |
| 37 | +users who are just starting the Game of Life tutorial. Most of this demographic |
| 38 | +is probably not watching the RFCs repository, and only some are regularly coming |
| 39 | +to working group meetings, so it makes sense to advertise this RFC in TWiRaWA |
| 40 | +and on our @rustwasm Twitter account. |
| 41 | + |
| 42 | +# Detailed Explanation |
| 43 | +[detailed-explanation]: #detailed-explanation |
| 44 | + |
| 45 | +We will add two new subcommands to `wasm-pack`: |
| 46 | + |
| 47 | +1. `watch` |
| 48 | +2. `serve` |
| 49 | + |
| 50 | +## `wasm-pack watch` |
| 51 | + |
| 52 | +The `watch` subcommand will observe the crate directory for changes and |
| 53 | +automatically re-build when files change. |
| 54 | + |
| 55 | +### Command Line Interface |
| 56 | + |
| 57 | +The `watch` command takes an identical set of command line arguments as |
| 58 | +`wasm-pack build` does. |
| 59 | + |
| 60 | +``` |
| 61 | +USAGE: |
| 62 | + wasm-pack watch [FLAGS] [OPTIONS] [path] [-- <extra_options>...] |
| 63 | +
|
| 64 | +FLAGS: |
| 65 | + --dev Create a development build. Enable debug info, and disable optimizations. |
| 66 | + --no-typescript By default a *.d.ts file is generated for the generated JS file, but this flag will disable |
| 67 | + generating this TypeScript file. |
| 68 | + -h, --help Prints help information |
| 69 | + --profiling Create a profiling build. Enable optimizations and debug info. |
| 70 | + --release Create a release build. Enable optimizations and disable debug info. |
| 71 | + -V, --version Prints version information |
| 72 | +
|
| 73 | +OPTIONS: |
| 74 | + -m, --mode <mode> Sets steps to be run. [possible values: no-install, normal, force] [default: normal] |
| 75 | + -d, --out-dir <out_dir> Sets the output directory with a relative path. [default: pkg] |
| 76 | + -s, --scope <scope> The npm scope to use in package.json, if any. |
| 77 | + -t, --target <target> Sets the target environment. [possible values: browser, nodejs, no-modules] [default: |
| 78 | + browser] |
| 79 | +
|
| 80 | +ARGS: |
| 81 | + <path> The path to the Rust crate. |
| 82 | + <extra_options>... List of extra options to pass to `cargo build` |
| 83 | +``` |
| 84 | + |
| 85 | +### Implementation |
| 86 | + |
| 87 | +We can use [the `notify` crate][notify] to watch the filesystem for |
| 88 | +changes. Initially, we will just watch the crate directory for |
| 89 | +changes. Eventually, we can use [`cargo build --build-plan`][build-plan] to get |
| 90 | +a list of files that we should be watching. |
| 91 | + |
| 92 | +[notify]: https://crates.io/crates/notify |
| 93 | +[build-plan]: https://github.com/rust-lang/cargo/issues/5579 |
| 94 | + |
| 95 | +## `wasm-pack serve` |
| 96 | + |
| 97 | +The `serve` subcommand starts a local Web server in the crate directory, watches |
| 98 | +for file changes, and re-builds the crate on changes. |
| 99 | + |
| 100 | +### Command Line Interface |
| 101 | + |
| 102 | +The `serve` subcommand takes a superset of the arguments that `wasm-pack build` |
| 103 | +takes. Like other `wasm-pack` subcommands, it takes an optional path to the |
| 104 | +crate directory as a positional argument, but if that is missing, then it |
| 105 | +defaults to the current directory. It has the usual flags for controlling the |
| 106 | +build profile, and the target environment. It takes extra options after `--` |
| 107 | +that get passed through straight to `cargo build`. |
| 108 | + |
| 109 | +Additionally, it has a `--no-watch` flag to disable watching the crate for |
| 110 | +changes to kick off automatic re-builds, and a `--port` option to specifiy the |
| 111 | +port the local server should bind to. |
| 112 | + |
| 113 | +``` |
| 114 | +USAGE: |
| 115 | + wasm-pack serve [FLAGS] [OPTIONS] [path] [-- <extra_options>...] |
| 116 | +
|
| 117 | +FLAGS: |
| 118 | + --dev Create a development build. Enable debug info, and disable optimizations. |
| 119 | + --no-typescript By default a *.d.ts file is generated for the generated JS file, but this flag will disable |
| 120 | + generating this TypeScript file. |
| 121 | + -h, --help Prints help information |
| 122 | + --profiling Create a profiling build. Enable optimizations and debug info. |
| 123 | + --release Create a release build. Enable optimizations and disable debug info. |
| 124 | + -V, --version Prints version information |
| 125 | + --no-watch Do not watch the crate for changes to automaticaly rebuild. |
| 126 | +
|
| 127 | +OPTIONS: |
| 128 | + -m, --mode <mode> Sets steps to be run. [possible values: no-install, normal, force] [default: normal] |
| 129 | + -d, --out-dir <out_dir> Sets the output directory with a relative path. [default: pkg] |
| 130 | + -s, --scope <scope> The npm scope to use in package.json, if any. |
| 131 | + -t, --target <target> Sets the target environment. [possible values: browser, nodejs, no-modules] [default: |
| 132 | + browser] |
| 133 | + -p, --port <port> Bind to this port number with the local development HTTP server. [default: 8000] |
| 134 | +
|
| 135 | +ARGS: |
| 136 | + <path> The path to the Rust crate. |
| 137 | + <extra_options>... List of extra options to pass to `cargo build` |
| 138 | +``` |
| 139 | + |
| 140 | +### Implementation |
| 141 | + |
| 142 | +Rather than integrating an HTTP server into the `wasm-pack` binary itself, we |
| 143 | +should leverage its ability to download and run other binaries. Exactly *which* |
| 144 | +local HTTP server binary is left as an unresolved question. |
| 145 | + |
| 146 | +The subcommand will start the local HTTP server and then execute the `watch` |
| 147 | +subcommand (unless `--no-watch` is supplied). |
| 148 | + |
| 149 | +# Drawbacks, Rationale, and Alternatives |
| 150 | + |
| 151 | +The primary drawbacks are authoring and maintaining two more subcommands to |
| 152 | +implement functionality that is already available in external, third-party |
| 153 | +tools. We try to mitigate this downside by using existing local HTTP server |
| 154 | +binaries rather than building an HTTP server directly into `wasm-pack` itself. |
| 155 | + |
| 156 | +## Alternative: Build the HTTP Server into `wasm-pack` |
| 157 | + |
| 158 | +An alternative design would be to build the HTTP server into `wasm-pack` itself, |
| 159 | +using one of the existing crates for building Web servers like rocket, actix, |
| 160 | +hyper, tide, etc: |
| 161 | + |
| 162 | +* **Pros:** |
| 163 | + * The `wasm-pack serve` command is ready to roll immediately after `wasm-pack` |
| 164 | + is installed, and doesn't need to hit the network the first time you run |
| 165 | + it. This situation only arises when folks who already downloaded `wasm-pack` |
| 166 | + do their first build/serve offline, disconnected from the internet. This |
| 167 | + seems like a niche enough situation that we can take the trade off. |
| 168 | +* **Cons:** |
| 169 | + * Building the HTTP server into the `wasm-pack` binary is less robust: by |
| 170 | + shelling out to an external binary for our local server, we get process |
| 171 | + isolation, which makes error recovery simpler. |
| 172 | + * More work to implement. `wasm-pack` already has plenty of infrastructure for |
| 173 | + downloading and working with external binaries, so using external HTTP |
| 174 | + servers should be fairly easy to implement. |
| 175 | + |
| 176 | +## Alternative: Only `serve` and Don't `watch` |
| 177 | + |
| 178 | +We could only implement the `serve` subcommand and not the `watch` subcommand or |
| 179 | +the filesystem watching functionality. |
| 180 | + |
| 181 | +My original motivation when writing this RFC was just to get a local server |
| 182 | +story sorted out. But I realized that for most users, if their build tool starts |
| 183 | +a local server, they expect new, post-`serve` file changes to be automatically |
| 184 | +rebuilt and reflected in the served things. This is what `webpack`, `jekyll`, |
| 185 | +`elm`, and `ember` CLIs do for a small selection. I think that this expectation |
| 186 | +is large enough that we have to support file watching and automatic rebuild if |
| 187 | +we support `serve` at all. |
| 188 | + |
| 189 | +That said, we could avoid having a `watch` *subcommand* and only expose the file |
| 190 | +watching functionality through the `serve` subcommand. At this point though, |
| 191 | +supporting the `watch` subcommand is a trivial amount of work, and there are use |
| 192 | +cases where it could be useful without the server: for example, JS bundler |
| 193 | +plugins could use it to re-build wasm packages, but then they are already |
| 194 | +running their own local server and are doing bundler-y stuff in-between the wasm |
| 195 | +package build and serving the newest wasm, like minifying some JS. |
| 196 | + |
| 197 | +## Alternative: Don't. |
| 198 | + |
| 199 | +Are we fine with the status quo where `wasm-pack` can neither locally serve |
| 200 | +files nor watch for file changes and auto rebuild? I think that *if* we want to |
| 201 | +remove the npm and bundler usage from the Game of Life tutorial, then the answer |
| 202 | +is "no". But maybe it isn't worth the hassle to port the Game of Life tutorial |
| 203 | +and add these `wasm-pack` features? |
| 204 | + |
| 205 | +# Unresolved Questions |
| 206 | +[unresolved]: #unresolved-questions |
| 207 | + |
| 208 | +* Which local HTTP server binary should we use? |
| 209 | + |
| 210 | + | Server | Windows/macOs/Linux Binary Releases? | `application/wasm` MIME type? | |
| 211 | + |-------------------------------------|--------------------------------------|-------------------------------| |
| 212 | + | [`miniserve`][miniserve] | Yes | Yes | |
| 213 | + | [`httplz`][https] | No | Yes | |
| 214 | + | [`simple-http-server`][simple-http] | Yes | No | |
| 215 | + |
| 216 | + There are probably even more options on crates.io than this, but these were |
| 217 | + the ones that I found in a quick search and seemed actively maintained. |
| 218 | + |
| 219 | + Given these results, I think we should pursue `miniserve` further: reach out |
| 220 | + to the maintainers to make sure they are cool with it and don't intend on |
| 221 | + ditching the project any time soon. |
| 222 | + |
| 223 | + We *could* also write our own local server binary using existing crates for |
| 224 | + writing Web servers, which shouldn't be *too* hard given our limited need for |
| 225 | + features. But ideally we shouldn't need to do this given that `miniserve` |
| 226 | + seems to fulfill all of our requirements. |
| 227 | + |
| 228 | +[https]: https://crates.io/crates/https |
| 229 | +[miniserve]: https://crates.io/crates/miniserve |
| 230 | +[simple-http]: https://crates.io/crates/simple-http-server |
0 commit comments