commit
88f45fe963
@ -1,12 +1,10 @@ |
|||||||
Make sure these boxes are checked! 📦✅ |
Make sure these boxes are checked! 📦✅ |
||||||
|
|
||||||
- [ ] You have the latest version of `rustfmt` installed and have your |
- [ ] You have the latest version of `rustfmt` installed |
||||||
cloned directory set to nightly |
|
||||||
```bash |
```bash |
||||||
$ rustup override set nightly |
|
||||||
$ rustup component add rustfmt-preview --toolchain nightly |
$ rustup component add rustfmt-preview --toolchain nightly |
||||||
``` |
``` |
||||||
- [ ] You ran `rustfmt` on the code base before submitting |
- [ ] You ran `cargo fmt` on the code base before submitting |
||||||
- [ ] You reference which issue is being closed in the PR text |
- [ ] You reference which issue is being closed in the PR text |
||||||
|
|
||||||
✨✨ 😄 Thanks so much for contributing to wasm-pack! 😄 ✨✨ |
✨✨ 😄 Thanks so much for contributing to wasm-pack! 😄 ✨✨ |
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,23 @@ |
|||||||
|
[package] |
||||||
|
name = "binary-install" |
||||||
|
description = "install a binary from a path to a global cache" |
||||||
|
authors = ["The wasm-pack team"] |
||||||
|
repository = "https://github.com/rustwasm/wasm-pack/tree/master/binary-install" |
||||||
|
license = "MIT/Apache-2.0" |
||||||
|
version = "0.0.2" |
||||||
|
documentation = "https://docs.rs/binary-install" |
||||||
|
readme = "./README.md" |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
curl = "0.4.13" |
||||||
|
dirs = "1.0.4" |
||||||
|
failure = "0.1.2" |
||||||
|
flate2 = "1.0.2" |
||||||
|
hex = "0.3" |
||||||
|
is_executable = "0.1.2" |
||||||
|
siphasher = "0.2.3" |
||||||
|
tar = "0.4.16" |
||||||
|
zip = "0.5.0" |
||||||
|
|
||||||
|
[dev-dependencies] |
||||||
|
tempfile = "3.0.5" |
@ -0,0 +1,2 @@ |
|||||||
|
# `binary-install` |
||||||
|
> install a binary from a path to a global cache |
@ -0,0 +1,142 @@ |
|||||||
|
use binary_install::Cache; |
||||||
|
use std::path::Path; |
||||||
|
use utils; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn it_returns_none_if_install_is_not_permitted() { |
||||||
|
let binary_name = "wasm-pack"; |
||||||
|
let binaries = vec![binary_name]; |
||||||
|
|
||||||
|
let dir = tempfile::TempDir::new().unwrap(); |
||||||
|
let cache = Cache::at(dir.path()); |
||||||
|
|
||||||
|
let dl = cache.download( |
||||||
|
false, |
||||||
|
binary_name, |
||||||
|
&binaries, |
||||||
|
&format!("{}/{}.tar.gz", "", binary_name), |
||||||
|
); |
||||||
|
|
||||||
|
assert!(dl.is_ok()); |
||||||
|
assert!(dl.unwrap().is_none()) |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn it_downloads_tarball() { |
||||||
|
let binary_name = "wasm-pack"; |
||||||
|
let binaries = vec![binary_name]; |
||||||
|
|
||||||
|
// Create a temporary tarball.
|
||||||
|
let tarball = utils::create_tarball(binary_name).ok(); |
||||||
|
|
||||||
|
// Spin up a local TcpListener.
|
||||||
|
let server_port = utils::start_server(tarball, None).recv().unwrap(); |
||||||
|
|
||||||
|
let url = format!("http://{}:{}", utils::TEST_SERVER_HOST, server_port); |
||||||
|
|
||||||
|
let dir = tempfile::TempDir::new().unwrap(); |
||||||
|
let cache = Cache::at(dir.path()); |
||||||
|
|
||||||
|
let dl = cache.download( |
||||||
|
true, |
||||||
|
binary_name, |
||||||
|
&binaries, |
||||||
|
&format!("{}/{}.tar.gz", &url, binary_name), |
||||||
|
); |
||||||
|
|
||||||
|
assert!(dl.is_ok()); |
||||||
|
assert!(dl.unwrap().is_some()) |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn it_returns_error_when_it_failed_to_download() { |
||||||
|
let server_port = 7881; |
||||||
|
let url = format!("http://{}:{}", utils::TEST_SERVER_HOST, server_port); |
||||||
|
let binary_name = "wasm-pack"; |
||||||
|
let binaries = vec![binary_name]; |
||||||
|
|
||||||
|
let dir = tempfile::TempDir::new().unwrap(); |
||||||
|
let cache = Cache::at(dir.path()); |
||||||
|
let full_url = &format!("{}/{}.tar.gz", &url, binary_name); |
||||||
|
|
||||||
|
let dl = cache.download(true, binary_name, &binaries, full_url); |
||||||
|
|
||||||
|
assert!(dl.is_err()); |
||||||
|
assert_eq!( |
||||||
|
&format!("failed to download from {}", full_url), |
||||||
|
&format!("{}", dl.unwrap_err()) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn it_returns_error_when_it_failed_to_extract_tarball() { |
||||||
|
let binary_name = "wasm-pack"; |
||||||
|
let binaries = vec![binary_name]; |
||||||
|
|
||||||
|
let dir = tempfile::TempDir::new().unwrap(); |
||||||
|
let cache = Cache::at(dir.path()); |
||||||
|
|
||||||
|
// Spin up a local TcpListener.
|
||||||
|
let server_port = utils::start_server(None, None).recv().unwrap(); |
||||||
|
|
||||||
|
let url = format!("http://{}:{}", utils::TEST_SERVER_HOST, server_port); |
||||||
|
let full_url = &format!("{}/{}.tar.gz", &url, binary_name); |
||||||
|
|
||||||
|
let dl = cache.download(true, binary_name, &binaries, full_url); |
||||||
|
|
||||||
|
assert!(dl.is_err()); |
||||||
|
assert_eq!( |
||||||
|
&format!("failed to extract tarball from {}", full_url), |
||||||
|
&format!("{}", dl.unwrap_err()) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn it_returns_error_when_it_failed_to_extract_zip() { |
||||||
|
let binary_name = "wasm-pack"; |
||||||
|
let binaries = vec![binary_name]; |
||||||
|
|
||||||
|
let dir = tempfile::TempDir::new().unwrap(); |
||||||
|
let cache = Cache::at(dir.path()); |
||||||
|
|
||||||
|
// Spin up a local TcpListener.
|
||||||
|
let server_port = utils::start_server(None, None).recv().unwrap(); |
||||||
|
|
||||||
|
let url = format!("http://{}:{}", utils::TEST_SERVER_HOST, server_port); |
||||||
|
let full_url = &format!("{}/{}.zip", &url, binary_name); |
||||||
|
|
||||||
|
let dl = cache.download(true, binary_name, &binaries, full_url); |
||||||
|
|
||||||
|
assert!(dl.is_err()); |
||||||
|
assert_eq!( |
||||||
|
&format!("failed to extract zip from {}", full_url), |
||||||
|
&format!("{}", dl.unwrap_err()) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
#[should_panic(expected = "don't know how to extract http://localhost:7884/wasm-pack.bin")] |
||||||
|
fn it_panics_if_not_tarball_or_zip() { |
||||||
|
let server_port = 7884; |
||||||
|
let binary_name = "wasm-pack"; |
||||||
|
let binaries = vec![binary_name]; |
||||||
|
|
||||||
|
let dir = tempfile::TempDir::new().unwrap(); |
||||||
|
let cache = Cache::at(dir.path()); |
||||||
|
|
||||||
|
// Spin up a local TcpListener.
|
||||||
|
utils::start_server(None, Some(server_port)).recv().unwrap(); |
||||||
|
|
||||||
|
let url = format!("http://{}:{}", utils::TEST_SERVER_HOST, server_port); |
||||||
|
let full_url = &format!("{}/{}.bin", &url, binary_name); |
||||||
|
|
||||||
|
let _ = cache.download(true, binary_name, &binaries, full_url); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn it_joins_path_with_destination() { |
||||||
|
let dir = tempfile::TempDir::new().unwrap(); |
||||||
|
let cache = Cache::at(dir.path()); |
||||||
|
|
||||||
|
assert_eq!(dir.path().join("hello"), cache.join(Path::new("hello"))); |
||||||
|
} |
@ -0,0 +1,125 @@ |
|||||||
|
use binary_install::Download; |
||||||
|
use std::fs::OpenOptions; |
||||||
|
|
||||||
|
#[test] |
||||||
|
#[cfg(unix)] |
||||||
|
fn it_returns_binary_name_for_unix() { |
||||||
|
use std::os::unix::fs::OpenOptionsExt; |
||||||
|
|
||||||
|
let binary_name = "wasm-pack"; |
||||||
|
|
||||||
|
let dir = tempfile::TempDir::new().unwrap(); |
||||||
|
let download = Download::at(dir.path()); |
||||||
|
|
||||||
|
let full_path = dir.path().join(binary_name); |
||||||
|
|
||||||
|
let mut options = OpenOptions::new(); |
||||||
|
options.create(true); |
||||||
|
options.write(true); |
||||||
|
|
||||||
|
// Make the "binary" an executable.
|
||||||
|
options.mode(0o755); |
||||||
|
|
||||||
|
options.open(&full_path).unwrap(); |
||||||
|
|
||||||
|
let binary = download.binary(binary_name); |
||||||
|
|
||||||
|
assert!(binary.is_ok()); |
||||||
|
assert_eq!(full_path, binary.unwrap()); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
#[cfg(not(windows))] |
||||||
|
fn it_bails_if_not_file_for_unix() { |
||||||
|
let binary_name = "wasm-pack"; |
||||||
|
|
||||||
|
let dir = tempfile::TempDir::new().unwrap(); |
||||||
|
let download = Download::at(dir.path()); |
||||||
|
|
||||||
|
let full_path = dir.path().join(binary_name); |
||||||
|
|
||||||
|
let mut options = OpenOptions::new(); |
||||||
|
options.create(true); |
||||||
|
options.write(true); |
||||||
|
|
||||||
|
let binary = download.binary(binary_name); |
||||||
|
|
||||||
|
assert!(binary.is_err()); |
||||||
|
assert_eq!( |
||||||
|
format!("{} binary does not exist", full_path.to_str().unwrap()), |
||||||
|
binary.unwrap_err().to_string() |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
#[cfg(windows)] |
||||||
|
fn it_bails_if_not_file_for_windows() { |
||||||
|
let binary_name = "wasm-pack.exe"; |
||||||
|
|
||||||
|
let dir = tempfile::TempDir::new().unwrap(); |
||||||
|
let download = Download::at(dir.path()); |
||||||
|
|
||||||
|
let full_path = dir.path().join(binary_name); |
||||||
|
|
||||||
|
let mut options = OpenOptions::new(); |
||||||
|
options.create(true); |
||||||
|
options.write(true); |
||||||
|
|
||||||
|
let binary = download.binary(binary_name); |
||||||
|
|
||||||
|
assert!(binary.is_err()); |
||||||
|
assert_eq!( |
||||||
|
format!("{} binary does not exist", full_path.to_str().unwrap()), |
||||||
|
binary.unwrap_err().to_string() |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
#[cfg(not(windows))] |
||||||
|
fn it_bails_if_not_executable_for_unix() { |
||||||
|
let binary_name = "wasm-pack"; |
||||||
|
|
||||||
|
let dir = tempfile::TempDir::new().unwrap(); |
||||||
|
let download = Download::at(dir.path()); |
||||||
|
|
||||||
|
let full_path = dir.path().join(binary_name); |
||||||
|
|
||||||
|
let mut options = OpenOptions::new(); |
||||||
|
options.create(true); |
||||||
|
options.write(true); |
||||||
|
|
||||||
|
options.open(&full_path).unwrap(); |
||||||
|
|
||||||
|
let binary = download.binary(binary_name); |
||||||
|
|
||||||
|
assert!(binary.is_err()); |
||||||
|
assert_eq!( |
||||||
|
format!("{} is not executable", full_path.to_str().unwrap()), |
||||||
|
binary.unwrap_err().to_string() |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
#[cfg(windows)] |
||||||
|
fn it_bails_if_not_executable_for_windows() { |
||||||
|
let binary_name = "wasm-pack.exe"; |
||||||
|
|
||||||
|
let dir = tempfile::TempDir::new().unwrap(); |
||||||
|
let download = Download::at(dir.path()); |
||||||
|
|
||||||
|
let full_path = dir.path().join(binary_name); |
||||||
|
|
||||||
|
let mut options = OpenOptions::new(); |
||||||
|
options.create(true); |
||||||
|
options.write(true); |
||||||
|
|
||||||
|
options.open(&full_path).unwrap(); |
||||||
|
|
||||||
|
let binary = download.binary(binary_name); |
||||||
|
|
||||||
|
assert!(binary.is_err()); |
||||||
|
assert_eq!( |
||||||
|
format!("{} is not executable", full_path.to_str().unwrap()), |
||||||
|
binary.unwrap_err().to_string() |
||||||
|
); |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
extern crate binary_install; |
||||||
|
extern crate flate2; |
||||||
|
extern crate tar; |
||||||
|
|
||||||
|
mod cache; |
||||||
|
mod download; |
||||||
|
mod utils; |
@ -0,0 +1,79 @@ |
|||||||
|
use flate2::write::GzEncoder; |
||||||
|
use flate2::Compression; |
||||||
|
use std::fs::{File, OpenOptions}; |
||||||
|
use std::io::{self, Read, Write}; |
||||||
|
use std::net::TcpListener; |
||||||
|
use std::sync::mpsc::{channel, Receiver}; |
||||||
|
use std::thread; |
||||||
|
|
||||||
|
pub const TEST_SERVER_HOST: &'static str = "localhost"; |
||||||
|
|
||||||
|
pub fn start_server(tarball: Option<Vec<u8>>, server_port: Option<u16>) -> Receiver<u16> { |
||||||
|
let (sender, receiver) = channel(); |
||||||
|
|
||||||
|
thread::spawn(move || { |
||||||
|
TcpListener::bind(format!( |
||||||
|
"{}:{}", |
||||||
|
TEST_SERVER_HOST, |
||||||
|
server_port.unwrap_or_else(|| 0) |
||||||
|
)) |
||||||
|
.map(|listener| { |
||||||
|
sender.send(listener.local_addr().unwrap().port()).unwrap(); |
||||||
|
|
||||||
|
for stream in listener.incoming() { |
||||||
|
let mut stream = stream.unwrap(); |
||||||
|
|
||||||
|
let mut buffer = [0; 512]; |
||||||
|
|
||||||
|
stream.read(&mut buffer).unwrap(); |
||||||
|
|
||||||
|
let response = "HTTP/1.1 200 OK\r\n\r\n"; |
||||||
|
|
||||||
|
stream.write(response.as_bytes()).unwrap(); |
||||||
|
|
||||||
|
match tarball.to_owned() { |
||||||
|
Some(tar) => { |
||||||
|
stream.write(tar.as_ref()).unwrap(); |
||||||
|
} |
||||||
|
None => {} |
||||||
|
} |
||||||
|
|
||||||
|
stream.flush().unwrap(); |
||||||
|
} |
||||||
|
}) |
||||||
|
.unwrap(); |
||||||
|
}); |
||||||
|
|
||||||
|
receiver |
||||||
|
} |
||||||
|
|
||||||
|
pub fn create_tarball(binary_name: &str) -> Result<Vec<u8>, io::Error> { |
||||||
|
let temp_dir = tempfile::TempDir::new().unwrap(); |
||||||
|
let full_path = temp_dir.path().join(binary_name.to_owned() + ".tar.gz"); |
||||||
|
|
||||||
|
let tar = OpenOptions::new() |
||||||
|
.create(true) |
||||||
|
.read(true) |
||||||
|
.write(true) |
||||||
|
.open(&full_path)?; |
||||||
|
|
||||||
|
let mut file = OpenOptions::new() |
||||||
|
.create(true) |
||||||
|
.read(true) |
||||||
|
.write(true) |
||||||
|
.open(temp_dir.path().join(binary_name))?; |
||||||
|
|
||||||
|
let mut encoder = GzEncoder::new(tar, Compression::default()); |
||||||
|
{ |
||||||
|
let mut archive = tar::Builder::new(&mut encoder); |
||||||
|
archive.append_file(binary_name, &mut file)?; |
||||||
|
} |
||||||
|
|
||||||
|
let mut contents = vec![]; |
||||||
|
|
||||||
|
encoder.finish()?; |
||||||
|
|
||||||
|
File::open(temp_dir.path().join(&full_path))?.read_to_end(&mut contents)?; |
||||||
|
|
||||||
|
Ok(contents) |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
too-many-arguments-threshold = 8 |
@ -0,0 +1,44 @@ |
|||||||
|
<style> |
||||||
|
header.warning { |
||||||
|
background-color: rgb(242, 222, 222); |
||||||
|
border-bottom-color: rgb(238, 211, 215); |
||||||
|
border-bottom-left-radius: 4px; |
||||||
|
border-bottom-right-radius: 4px; |
||||||
|
border-bottom-style: solid; |
||||||
|
border-bottom-width: 0.666667px; |
||||||
|
border-image-outset: 0 0 0 0; |
||||||
|
border-image-repeat: stretch stretch; |
||||||
|
border-image-slice: 100% 100% 100% 100%; |
||||||
|
border-image-source: none; |
||||||
|
border-image-width: 1 1 1 1; |
||||||
|
border-left-color: rgb(238, 211, 215); |
||||||
|
border-left-style: solid; |
||||||
|
border-left-width: 0.666667px; |
||||||
|
border-right-color: rgb(238, 211, 215); |
||||||
|
border-right-style: solid; |
||||||
|
border-right-width: 0.666667px; |
||||||
|
border-top-color: rgb(238, 211, 215); |
||||||
|
border-top-left-radius: 4px; |
||||||
|
border-top-right-radius: 4px; |
||||||
|
border-top-style: solid; |
||||||
|
border-top-width: 0.666667px; |
||||||
|
color: rgb(185, 74, 72); |
||||||
|
margin-bottom: 0px; |
||||||
|
margin-left: 0px; |
||||||
|
margin-right: 0px; |
||||||
|
margin-top: 30px; |
||||||
|
padding-bottom: 8px; |
||||||
|
padding-left: 14px; |
||||||
|
padding-right: 35px; |
||||||
|
padding-top: 8px; |
||||||
|
text-align: center; |
||||||
|
} |
||||||
|
</style> |
||||||
|
<header class='warning'> |
||||||
|
This is the <strong>unpublished</strong> documentation of |
||||||
|
<code>wasm-pack</code>, the published documentation is available |
||||||
|
<a href="https://rustwasm.github.io/docs/wasm-pack/"> |
||||||
|
on the main Rust and WebAssembly documentation site |
||||||
|
</a>. Features documented here may not be available in released versions of |
||||||
|
<code>wasm-pack</code>. |
||||||
|
</header> |
@ -1,22 +1,28 @@ |
|||||||
# Summary |
# Summary |
||||||
|
|
||||||
- [Prerequisites](./prerequisites/index.md) |
- [Prerequisites](./prerequisites/index.md) |
||||||
- [Rust](./prerequisites/rust.md) |
- [npm (optional)](./prerequisites/npm.md) |
||||||
- [npm](./prerequisites/npm.md) |
|
||||||
- [Project Setup](./project-setup/index.md) |
|
||||||
- [Using a Template](./project-setup/using-a-template.md) |
|
||||||
- [Manual Setup](./project-setup/manual-setup.md) |
|
||||||
- [Commands](./commands/index.md) |
- [Commands](./commands/index.md) |
||||||
- [`init` (DEPRECATED)](./commands/init.md) |
- [`init` (DEPRECATED)](./commands/init.md) |
||||||
- [`build`](./commands/build.md) |
- [`build`](./commands/build.md) |
||||||
|
- [`test`](./commands/test.md) |
||||||
- [`pack` and `publish`](./commands/pack-and-publish.md) |
- [`pack` and `publish`](./commands/pack-and-publish.md) |
||||||
- [Tutorial](./tutorial/index.md) |
- [Tutorials](./tutorials/index.md) |
||||||
- [Getting Started](./tutorial/getting-started.md) |
- [Hybrid applications with Webpack](./tutorials/hybrid-applications-with-webpack/index.md) |
||||||
- [Template Deep Dive](./tutorial/template-deep-dive/index.md) |
- [Getting started](./tutorials/hybrid-applications-with-webpack/getting-started.md) |
||||||
- [`Cargo.toml`](./tutorial/template-deep-dive/cargo-toml.md) |
- [Using your library](./tutorials/hybrid-applications-with-webpack/using-your-library.md) |
||||||
- [`src/lib.rs`](./tutorial/template-deep-dive/src-lib-rs.md) |
- [npm browser packages](./tutorials/npm-browser-packages/index.md) |
||||||
- [`src/utils.rs`](./tutorial/template-deep-dive/src-utils-rs.md) |
- [Getting started](./tutorials/npm-browser-packages/getting-started.md) |
||||||
- [Packaging and Publishing](./tutorial/packaging-and-publishing.md) |
- [Project setup](./tutorials/npm-browser-packages/project-setup/index.md) |
||||||
- [Using your Library](./tutorial/using-your-library.md) |
- [Using a Template](./tutorials/npm-browser-packages/project-setup/using-a-template.md) |
||||||
|
- [Manual Setup](./tutorials/npm-browser-packages/project-setup/manual-setup.md) |
||||||
|
- [Template deep dive](./tutorials/npm-browser-packages/template-deep-dive/index.md) |
||||||
|
- [`Cargo.toml`](./tutorials/npm-browser-packages/template-deep-dive/cargo-toml.md) |
||||||
|
- [`src/lib.rs`](./tutorials/npm-browser-packages/template-deep-dive/src-lib-rs.md) |
||||||
|
- [`src/utils.rs`](./tutorials/npm-browser-packages/template-deep-dive/src-utils-rs.md) |
||||||
|
- [`wee_alloc`](./tutorials/npm-browser-packages/template-deep-dive/wee_alloc.md) |
||||||
|
- [Building your project](./tutorials/npm-browser-packages/template-deep-dive/building-your-project.md) |
||||||
|
- [Packaging and publishing](./tutorials/npm-browser-packages/packaging-and-publishing.md) |
||||||
|
- [Using your library](./tutorials/npm-browser-packages/using-your-library.md) |
||||||
- [`Cargo.toml` Configuration](./cargo-toml-configuration.md) |
- [`Cargo.toml` Configuration](./cargo-toml-configuration.md) |
||||||
- [Contributing](./contributing.md) |
- [Contributing](./contributing.md) |
||||||
|
@ -1 +0,0 @@ |
|||||||
# pack and publish |
|
@ -1,10 +1,19 @@ |
|||||||
# Prerequisites |
# Prerequisites |
||||||
|
|
||||||
To run `wasm-pack` you'll need to have both Rust and npm installed and configured. |
First you'll want to [install the `wasm-pack` CLI][wasm-pack], and `wasm-pack |
||||||
|
-V` should print the version that you just installed. |
||||||
|
|
||||||
- [Rust](./rust.html) |
[wasm-pack]: https://rustwasm.github.io/wasm-pack/installer/ |
||||||
- [npm](./npm.html) |
|
||||||
|
|
||||||
In the future, we intend to rewrite the npm registry client bits so that the need |
Next, since `wasm-pack` is a build tool, you'll want to make sure you have |
||||||
for a Node runtime is eliminated. If you're excited about that work- you should |
[Rust][rust] installed. Make sure `rustc -V` prints out at least 1.30.0. |
||||||
reach out to the maintainers and get involved! |
|
||||||
|
[rust]: https://www.rust-lang.org/tools/install |
||||||
|
|
||||||
|
Finally, if you're using `wasm-pack` to install to publish to NPM, you'll want |
||||||
|
to [install and configure `npm`][npm]. In the future, we intend to rewrite the |
||||||
|
npm registry client bits so that the need for a Node runtime is eliminated. If |
||||||
|
you're excited about that work- you should reach out to the maintainers and get |
||||||
|
involved! |
||||||
|
|
||||||
|
[npm]: npm.html |
||||||
|
@ -1,35 +0,0 @@ |
|||||||
# Rust |
|
||||||
|
|
||||||
`wasm-pack` is a Command Line Interface tool written in Rust, and distributed with `cargo`. |
|
||||||
As a result, you'll need Rust and `cargo` to use `wasm-pack`. |
|
||||||
|
|
||||||
### Installing Rust and Cargo |
|
||||||
|
|
||||||
To install Rust, visit this [page](https://www.rust-lang.org/en-US/install.html), which will |
|
||||||
walk you through installing Rust and `cargo` on your machine using a tool called `rustup`. |
|
||||||
|
|
||||||
To confirm you have Rust and `cargo` installed, run: |
|
||||||
|
|
||||||
``` |
|
||||||
rustc --version |
|
||||||
cargo --version |
|
||||||
``` |
|
||||||
|
|
||||||
### Rust Versions |
|
||||||
|
|
||||||
`wasm-pack` depends on a library called `wasm-bindgen`. `wasm-bindgen` requires that you use |
|
||||||
Rust 1.30.0 or higher. This version is currently only available on the `nightly` or `beta` |
|
||||||
channels. |
|
||||||
|
|
||||||
To get the correct version of Rust, you'll use `rustup` a Rust version manager that comes |
|
||||||
bundled with Rust. Run this command to install the latest Rust on the `beta` channel: |
|
||||||
|
|
||||||
``` |
|
||||||
rustup install beta |
|
||||||
``` |
|
||||||
|
|
||||||
You can set your project directory to always use this version of Rust by running: |
|
||||||
|
|
||||||
``` |
|
||||||
rustup override set beta |
|
||||||
``` |
|
@ -1,3 +0,0 @@ |
|||||||
# src/lib.rs |
|
||||||
|
|
||||||
🚧 COMING SOON 🚧 |
|
@ -1,3 +0,0 @@ |
|||||||
# src/utils.rs |
|
||||||
|
|
||||||
🚧 COMING SOON 🚧 |
|
@ -0,0 +1,15 @@ |
|||||||
|
# Getting Started |
||||||
|
|
||||||
|
You can create a new Rust-WebAssembly webpack project by using the [rustwasm webpack-template]. |
||||||
|
|
||||||
|
Run: |
||||||
|
|
||||||
|
``` |
||||||
|
npm init rust-webpack my-app |
||||||
|
``` |
||||||
|
|
||||||
|
The last argument will be your project name. After you run the command, you will have a |
||||||
|
directory with a new project, ready to go. We'll talk about what's been included in this |
||||||
|
template further in this guide. |
||||||
|
|
||||||
|
[rustwasm webpack-template]: https://github.com/rustwasm/rust-webpack-template |
@ -0,0 +1,14 @@ |
|||||||
|
# Hybrid Applications with Webpack |
||||||
|
|
||||||
|
The goal of this tutorial is to introduce you to the [`rust-webpack-template`] |
||||||
|
and the `wasm-pack` workflow by building the example app in the template. |
||||||
|
|
||||||
|
This tutorial is aimed at folks who are both beginners to WebAssembly and Rust- you don't need |
||||||
|
much Rust knowledge to complete this tutorial. |
||||||
|
|
||||||
|
Be sure to have read and followed the [Prerequisites](../prerequisites/index.html). |
||||||
|
|
||||||
|
[Rust]: https://www.rust-lang.org |
||||||
|
[Node.js]: https://nodejs.org |
||||||
|
[npm]: https://npmjs.com |
||||||
|
[`rust-webpack-template`]: https://github.com/rustwasm/rust-webpack-template |
@ -0,0 +1,85 @@ |
|||||||
|
# Run The Code |
||||||
|
|
||||||
|
The Rust Webpack template is designed for creating monorepo-style Web applications with |
||||||
|
Rust-generated WebAssembly and Webpack without publishing your wasm to NPM. |
||||||
|
This portion of the tutorial will explain how to build a [Webpack] JavaScript project |
||||||
|
that will run your WebAssembly code in the browser. |
||||||
|
|
||||||
|
[Webpack]: https://webpack.js.org/ |
||||||
|
|
||||||
|
## Scaffold a JavaScript Project |
||||||
|
|
||||||
|
To generate a new Rust Webpack project, we've used the [`rust-webpack`] npm template. |
||||||
|
|
||||||
|
[`rust-webpack`]: https://github.com/rustwasm/rust-webpack-template |
||||||
|
|
||||||
|
``` |
||||||
|
npm init rust-webpack your-package-name |
||||||
|
``` |
||||||
|
|
||||||
|
A new project folder will be created with the name you supply. |
||||||
|
|
||||||
|
If we look in the project, we'll see the following: |
||||||
|
|
||||||
|
- `.gitignore`: ignores `node_modules` |
||||||
|
- `LICENSE-APACHE` and `LICENSE-MIT`: most Rust projects are licensed this way, so these are included for you |
||||||
|
- `README.md`: the file you are reading now! |
||||||
|
- `index.html`: a bare bones html document that includes the webpack bundle |
||||||
|
- `js/index.js`: example JS file with a comment showing how to import and use a wasm pkg |
||||||
|
- `package.json` and `package-lock.json`: |
||||||
|
- pulls in devDependencies for using webpack: |
||||||
|
- [`webpack`](https://www.npmjs.com/package/webpack) |
||||||
|
- [`webpack-cli`](https://www.npmjs.com/package/webpack-cli) |
||||||
|
- [`webpack-dev-server`](https://www.npmjs.com/package/webpack-dev-server) |
||||||
|
- defines a `start` script to run `webpack-dev-server` |
||||||
|
- `webpack.config.js`: configuration file for bundling your JS with webpack |
||||||
|
- `crate/src/lib.rs`: your Rust crate code! |
||||||
|
|
||||||
|
## Your Rust Crate |
||||||
|
|
||||||
|
The scaffolded project includes an example Rust WebAssembly webpack crate. |
||||||
|
|
||||||
|
Inside the `crate/src/lib.rs` file we see a `run` function that's callable from our JS file: |
||||||
|
```rust |
||||||
|
// Called by our JS entry point to run the example. |
||||||
|
#[wasm_bindgen] |
||||||
|
pub fn run() -> Result<(), JsValue> { |
||||||
|
set_panic_hook(); |
||||||
|
|
||||||
|
// ... |
||||||
|
let p: web_sys::Node = document.create_element("p")?.into(); |
||||||
|
p.set_text_content(Some("Hello from Rust, WebAssembly, and Webpack!")); |
||||||
|
// ... |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
Now, open up the `js/index.js` file. We see our Rust-generated wasm `run` function being |
||||||
|
called inside our JS file. |
||||||
|
|
||||||
|
```js |
||||||
|
import("../crate/pkg").then(module => { |
||||||
|
module.run(); |
||||||
|
}); |
||||||
|
``` |
||||||
|
|
||||||
|
## Run The Project |
||||||
|
|
||||||
|
To generate our Rust-compiled to wasm code, in the root directory we run: |
||||||
|
```bash |
||||||
|
npm run build |
||||||
|
``` |
||||||
|
This will create our bundled JavaScript module in a new directory `dist`. |
||||||
|
|
||||||
|
We should be ready to run our project now! |
||||||
|
In the root directory, we'll run: |
||||||
|
|
||||||
|
```bash |
||||||
|
npm start |
||||||
|
``` |
||||||
|
|
||||||
|
Then in a web browser navigate to `http://localhost:8080` and you should be greeted |
||||||
|
with text in the body of the page that says "Hello from Rust, WebAssembly, and Webpack!" |
||||||
|
|
||||||
|
If you did congrats! You've successfully used the rust-webpack template! |
@ -0,0 +1,9 @@ |
|||||||
|
# Tutorials |
||||||
|
|
||||||
|
We have two tutorials that help you get started with `wasm-pack`: |
||||||
|
|
||||||
|
- If you want to create and publish a package: [npm browser packages] |
||||||
|
- If you'd like to develop a Wasm library alongside a JavaScript application using Webpack: [Hybrid applications with Webpack] |
||||||
|
|
||||||
|
[npm browser packages]: npm-browser-packages/index.html |
||||||
|
[Hybrid applications with Webpack]: hybrid-applications-with-webpack/index.html |
@ -0,0 +1,24 @@ |
|||||||
|
# Building your package |
||||||
|
|
||||||
|
We've written our code so now we need to package it all up. |
||||||
|
|
||||||
|
We are writing a package that should be used in the browser, so we run this in our terminal: |
||||||
|
|
||||||
|
```bash |
||||||
|
$ wasm-pack build --scope MYSCOPE |
||||||
|
``` |
||||||
|
|
||||||
|
If you were writing a package that should be used in Node.js (with CommonJS modules, e.g. `require`), |
||||||
|
you would run this in your terminal: |
||||||
|
|
||||||
|
```bash |
||||||
|
$ wasm-pack build --scope MYSCOPE --target nodejs |
||||||
|
``` |
||||||
|
|
||||||
|
where `MYSCOPE` is your npm username. Normally you could just type `wasm-pack init` but since |
||||||
|
other people are doing this tutorial as well we don't want conflicts with the `wasm-add` package |
||||||
|
name! This command when run does a few things: |
||||||
|
|
||||||
|
1. It'll compile your code to wasm if you haven't already |
||||||
|
2. It'll generate a pkg folder with the wasm file, a JS wrapper file around the wasm, your README, |
||||||
|
and a `package.json` file. |
@ -0,0 +1 @@ |
|||||||
|
# Building your project |
@ -0,0 +1,99 @@ |
|||||||
|
# src/lib.rs |
||||||
|
|
||||||
|
`lib.rs` is the template's main source file. The name `lib.rs` commonly implies that this Rust project will be compiled as a library. |
||||||
|
|
||||||
|
It contains three key parts: |
||||||
|
|
||||||
|
1. [`#[wasm_bindgen] functions`](#a1-wasm_bindgen-functions) |
||||||
|
2. [Crate imports](#a2-crate-imports) |
||||||
|
3. [`wee_alloc` optional dependecy](#a3-wee_alloc-optional-dependecy) |
||||||
|
- [What is `wee_alloc`?](#what-is-wee_alloc) |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
We'll start with the most important part of `lib.rs` -- the two `#[wasm_bindgen]` functions (which you can find at the bottom of the file). In many cases, this is the only part of `lib.rs` you will need to modify. |
||||||
|
|
||||||
|
## 1. `#[wasm_bindgen]` functions |
||||||
|
|
||||||
|
The `#[wasm_bindgen]` attribute indicates that the function below it will be accessible both in JavaScript and Rust. |
||||||
|
|
||||||
|
```rust |
||||||
|
#[wasm_bindgen] |
||||||
|
extern { |
||||||
|
fn alert(s: &str); |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
The `extern` block imports the external JavaScript function `alert` into Rust. This declaration is required to call `alert` from Rust. By declaring it in this way, `wasm-bindgen` will create JavaScript stubs for `alert` which allow us to pass strings back and forth between Rust and JavaScript. |
||||||
|
|
||||||
|
We can see that the `alert` function requires a single parameter `s` of type `&str`, a string. In Rust, any string literal such as `"Hello, test-wasm!"` is of type `&str`. So, `alert` could be called by writing `alert("Hello, test-wasm!");`. |
||||||
|
|
||||||
|
We knew to declare `alert` in this way because it is how we would call `alert` in JavaScript -- by passing it a string argument. |
||||||
|
|
||||||
|
```rust |
||||||
|
#[wasm_bindgen] |
||||||
|
pub fn greet() { |
||||||
|
alert("Hello, test-wasm!"); |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
If we were to write the `greet` function without the `#[wasm_bindgen]` attribute, then `greet` would not be easily accessible within JavaScript. Furthermore, we wouldn't be able to natively convert certain types such as `&str` between JavaScript and Rust. So, both the `#[wasm_bindgen]` attribute and the prior import of `alert` allow `greet` to be called from JavaScript. |
||||||
|
|
||||||
|
This is all you need to know to interface with JavaScript, at least to start! You can learn a bunch more by reading the |
||||||
|
[`wasm-bindgen` documentation]! |
||||||
|
|
||||||
|
[`wasm-bindgen` documentation]: https://rustwasm.github.io/docs/wasm-bindgen/ |
||||||
|
|
||||||
|
If you are curious about the rest, read on. |
||||||
|
|
||||||
|
## 2. Crate imports |
||||||
|
|
||||||
|
```rust |
||||||
|
mod utils; |
||||||
|
``` |
||||||
|
This statement declares a new module named `utils` that is defined by the contents of `utils.rs`. Equivalently, we could place the contents of `utils.rs` inside the `utils` declaration, replacing the line with: |
||||||
|
|
||||||
|
```rust |
||||||
|
mod utils { |
||||||
|
// contents of utils.rs |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
Either way, the contents of `utils.rs` define a single public function `set_panic_hook`. Because we are placing it inside the `utils` module, we will be able to call the function directly by writing `utils::set_panic_hook()`. We will discuss how and why to use this function in `src/utils.rs`. |
||||||
|
|
||||||
|
|
||||||
|
```rust |
||||||
|
use cfg_if::cfg_if; |
||||||
|
``` |
||||||
|
|
||||||
|
`use` allows us to conveniently refer to parts of a crate or module. For example, suppose the crate `cfg_if` contains a function `func`. It is always possible to call this function directly by writing `cfg_if::func()`. However, this is often tedious to write. If we first specify `use cfg_if::func;`, then `func` can be called by just writing `func()` instead. You can learn more about how Rust let's you |
||||||
|
write modular code in [this chapter of the book](https://doc.rust-lang.org/book/ch07-02-modules-and-use-to-control-scope-and-privacy.html). |
||||||
|
|
||||||
|
With this in mind, this `use` allows us to call the macro `cfg_if!` inside the crate `cfg_if` without writing `cfg_if::cfg_if!`. We use `cfg_if!` to configure `wee_alloc`, which we will talk more about in a [separate section](./wee_alloc.md): |
||||||
|
|
||||||
|
```rust |
||||||
|
cfg_if! { |
||||||
|
if #[cfg(feature = "wee_alloc")] { |
||||||
|
extern crate wee_alloc; |
||||||
|
#[global_allocator] |
||||||
|
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
We immediately notice that `cfg_if!` is a macro because it ends in `!`, similarly to other Rust macros such as `println!` and `vec!`. A macro is directly replaced by other code during compile time. |
||||||
|
|
||||||
|
During compile time, `cfg_if!` evaluates the `if` statement. This tests whether the feature `wee_alloc` is present in the `[features]` section of `Cargo.toml` (among other possible ways to set it). |
||||||
|
|
||||||
|
As we saw earlier, the `default` vector in `[features]` only contains `"console_error_panic_hook"` and not `"wee_alloc"`. So, in this case, the `cfg_if!` block will be replaced by no code at all, and hence the default memory allocator will be used instead of `wee_alloc`. |
||||||
|
|
||||||
|
```rust |
||||||
|
use wasm_bindgen::prelude::*; |
||||||
|
``` |
||||||
|
|
||||||
|
Many modules contain a prelude, a list of things that should be automatically imported. This allows common features of the module to be conveniently accessed without a lengthy prefix. For example, in this file we can use `#[wasm_bindgen]` only because it is brought into scope by the prelude. |
||||||
|
|
||||||
|
The asterisk at the end of this `use` indicates that everything inside the module `wasm_bindgen::prelude` (i.e. the module `prelude` inside the crate `wasm_bindgen`) can be referred to without prefixing it with `wasm_bindgen::prelude`. |
||||||
|
|
||||||
|
For example, `#[wasm_bindgen]` could also be written as `#[wasm_bindgen::prelude::wasm_bindgen]`, although this is not recommended. |
||||||
|
|
@ -0,0 +1,66 @@ |
|||||||
|
# src/utils.rs |
||||||
|
|
||||||
|
The purpose of `utils.rs` is to define the `utils` module, which contains a single function `set_panic_hook`. This function becomes part of the `utils` module in `lib.rs`, as described in the preceding section. |
||||||
|
|
||||||
|
If the `console_error_panic_hook` feature is not enabled, then `set_panic_hook` is defined to be an inlined empty function. So, there is no run-time performance or code-size penalty incurred by its use. |
||||||
|
|
||||||
|
We will discuss: |
||||||
|
1. [Defining `set_panic_hook`](#a1-defining-set_panic_hook) |
||||||
|
2. [What is `console_error_panic_hook`?](#a2-what-is-console_error_panic_hook) |
||||||
|
|
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 1. Defining `set_panic_hook` |
||||||
|
|
||||||
|
```rust |
||||||
|
use cfg_if::cfg_if; |
||||||
|
``` |
||||||
|
|
||||||
|
This allows us to write `cfg_if!` instead of `cfg_if::cfg_if!`, identically to the line in `src/lib.rs`. |
||||||
|
|
||||||
|
```rust |
||||||
|
cfg_if! { |
||||||
|
if #[cfg(feature = "console_error_panic_hook")] { |
||||||
|
extern crate console_error_panic_hook; |
||||||
|
pub use self::console_error_panic_hook::set_once as set_panic_hook; |
||||||
|
} else { |
||||||
|
#[inline] |
||||||
|
pub fn set_panic_hook() {} |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
As described in the preceding section, the macro `cfg_if!` evaluates the `if` statement during compile time. This is possible because it is essentially testing whether `"console_error_panic_hook"` is defined in the `[features]` section of `Cargo.toml`, which is available during compile time. |
||||||
|
|
||||||
|
The entire macro block will either be replaced with the statements in the `if` block or with those in the `else` block. These two cases are now described in turn: |
||||||
|
|
||||||
|
```rust |
||||||
|
extern crate console_error_panic_hook; |
||||||
|
pub use self::console_error_panic_hook::set_once as set_panic_hook; |
||||||
|
``` |
||||||
|
|
||||||
|
Due to the `use` statement, the function `self::console_error_panic_hook::set_once` can now be accessed more conveniently as `set_panic_hook`. Due to `pub`, this function will be publicly accessible outside of the `utils` module as `utils::set_panic_hook`. |
||||||
|
|
||||||
|
```rust |
||||||
|
#[inline] |
||||||
|
pub fn set_panic_hook() {} |
||||||
|
``` |
||||||
|
|
||||||
|
An inline function replaces the function call with the contents of the function during compile time. Here, `set_panic_hook` is defined to be an empty inline function. This allows the use of `set_panic_hook` without any run-time or code-size performance penalty if the feature is not enabled. |
||||||
|
|
||||||
|
## 2. What is `console_error_panic_hook`? |
||||||
|
|
||||||
|
The crate `console_error_panic_hook` enhances error messages in the web browser. This allows you to easily debug WebAssembly code. |
||||||
|
|
||||||
|
Let's compare error messages before and after enabling the feature: |
||||||
|
|
||||||
|
**Before:** `"RuntimeError: Unreachable executed"` |
||||||
|
|
||||||
|
**After:** `"panicked at 'index out of bounds: the len is 3 but the index is 4', libcore/slice/mod.rs:2046:10"` |
||||||
|
|
||||||
|
To do this, a panic hook for WebAssembly is provided that logs panics to the developer console via the JavaScript `console.error` function. |
||||||
|
|
||||||
|
Note that although the template sets up the function, your error messages will not automatically be enhanced. To enable the enhanced errors, call the function `utils::set_panic_hook()` in `lib.rs` when your code first runs. The function may be called multiple times if needed. |
||||||
|
|
||||||
|
For more details, see the [`console_error_panic_hook` repository](https://github.com/rustwasm/console_error_panic_hook). |
@ -0,0 +1,56 @@ |
|||||||
|
# wee_alloc |
||||||
|
|
||||||
|
1. [What is `wee_alloc`?](#what-is-wee_alloc) |
||||||
|
2. [Enabling `wee_alloc`](#enabling-wee_alloc) |
||||||
|
3. [Rust nightly](#rust-nightly) |
||||||
|
|
||||||
|
## What is `wee_alloc`? |
||||||
|
|
||||||
|
Reducing the size of compiled WebAssembly code is important, since it is often transmitted over the Internet or placed on embedded devices. |
||||||
|
|
||||||
|
*Want to learn more about code sizein the rustwasm toolchain? Check out this [documentation](https://rustwasm.github.io/docs/book/reference/code-size.html). |
||||||
|
|
||||||
|
> `wee_alloc` is a tiny allocator designed for WebAssembly that has a (pre-compression) code-size footprint of only a single kilobyte. |
||||||
|
|
||||||
|
[An analysis](http://fitzgeraldnick.com/2018/02/09/wee-alloc.html) suggests that over half of the bare minimum WebAssembly memory footprint is required by Rust's default memory allocator. Yet, WebAssembly code often does not require a sophisticated allocator, since it often just requests a couple of large initial allocations. |
||||||
|
|
||||||
|
`wee_alloc` trades off size for speed. Although it has a tiny code-size footprint, it is relatively slow if additional allocations are needed. |
||||||
|
|
||||||
|
For even more details, see the [`wee_alloc` repository](https://github.com/rustwasm/wee_alloc). |
||||||
|
|
||||||
|
## Enabling `wee_alloc` |
||||||
|
|
||||||
|
In `lib.rs`, we have the configuration for `wee_alloc` inside a `cfg_if!` macro: |
||||||
|
|
||||||
|
```rust |
||||||
|
cfg_if! { |
||||||
|
if #[cfg(feature = "wee_alloc")] { |
||||||
|
extern crate wee_alloc; |
||||||
|
#[global_allocator] |
||||||
|
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
This code block is intended to initialize `wee_alloc` as the global memory allocator, but only if the `wee_alloc` feature is enabled in `Cargo.toml`. |
||||||
|
|
||||||
|
To do so we need to append `"wee_alloc"` to the `default` vector in `Cargo.toml`. Then, the `cfg_if!` block is replaced with the contents of the `if` block, shown above. |
||||||
|
|
||||||
|
```toml |
||||||
|
[features] |
||||||
|
default = ["console_error_panic_hook", "wee_alloc"] |
||||||
|
``` |
||||||
|
|
||||||
|
## Rust nightly |
||||||
|
|
||||||
|
`wee_alloc` currently relies on features only available in Rust nightly. As such it requires you to use the nightly toolchain for compilation. If you have [Rustup](https://rustup.rs/) set up, you can install the nightly toolchain as follows: |
||||||
|
|
||||||
|
``` |
||||||
|
rustup toolchain add nightly |
||||||
|
``` |
||||||
|
|
||||||
|
To use `wasm-pack` with Rust nightly run: |
||||||
|
|
||||||
|
``` |
||||||
|
rustup run nightly wasm-pack build |
||||||
|
``` |
@ -0,0 +1 @@ |
|||||||
|
# Standalone WASM Binaries |
@ -0,0 +1,14 @@ |
|||||||
|
//! Getting and configuring wasm-pack's binary cache.
|
||||||
|
|
||||||
|
use binary_install::Cache; |
||||||
|
use std::env; |
||||||
|
use std::path::Path; |
||||||
|
|
||||||
|
/// Get wasm-pack's binary cache.
|
||||||
|
pub fn get_wasm_pack_cache() -> Result<Cache, failure::Error> { |
||||||
|
if let Ok(path) = env::var("WASM_PACK_CACHE") { |
||||||
|
Ok(Cache::at(Path::new(&path))) |
||||||
|
} else { |
||||||
|
Cache::new("wasm-pack") |
||||||
|
} |
||||||
|
} |
@ -1,30 +1,25 @@ |
|||||||
|
use log::info; |
||||||
use npm; |
use npm; |
||||||
use slog::Logger; |
|
||||||
use std::result; |
use std::result; |
||||||
use PBAR; |
use PBAR; |
||||||
|
|
||||||
pub fn login( |
pub fn login( |
||||||
registry: Option<String>, |
registry: Option<String>, |
||||||
scope: Option<String>, |
scope: &Option<String>, |
||||||
always_auth: bool, |
always_auth: bool, |
||||||
auth_type: Option<String>, |
auth_type: &Option<String>, |
||||||
log: &Logger, |
|
||||||
) -> result::Result<(), failure::Error> { |
) -> result::Result<(), failure::Error> { |
||||||
let registry = registry.unwrap_or(npm::DEFAULT_NPM_REGISTRY.to_string()); |
let registry = registry.unwrap_or_else(|| npm::DEFAULT_NPM_REGISTRY.to_string()); |
||||||
|
|
||||||
info!(&log, "Logging in to npm..."); |
info!("Logging in to npm..."); |
||||||
info!( |
info!( |
||||||
&log, |
|
||||||
"Scope: {:?} Registry: {}, Always Auth: {}, Auth Type: {:?}.", |
"Scope: {:?} Registry: {}, Always Auth: {}, Auth Type: {:?}.", |
||||||
&scope, |
&scope, ®istry, always_auth, &auth_type |
||||||
®istry, |
|
||||||
always_auth, |
|
||||||
&auth_type |
|
||||||
); |
); |
||||||
info!(&log, "npm info located in the npm debug log"); |
info!("npm info located in the npm debug log"); |
||||||
npm::npm_login(log, ®istry, &scope, always_auth, &auth_type)?; |
npm::npm_login(®istry, &scope, always_auth, &auth_type)?; |
||||||
info!(&log, "Logged you in!"); |
info!("Logged you in!"); |
||||||
|
|
||||||
PBAR.message(&format!("👋 logged you in!")); |
PBAR.info(&"👋 logged you in!".to_string()); |
||||||
Ok(()) |
Ok(()) |
||||||
} |
} |
||||||
|
@ -0,0 +1,89 @@ |
|||||||
|
//! Copy `LICENSE` file(s) for the packaged wasm.
|
||||||
|
|
||||||
|
use failure; |
||||||
|
use std::fs; |
||||||
|
use std::path::Path; |
||||||
|
|
||||||
|
use glob::glob; |
||||||
|
use manifest::CrateData; |
||||||
|
use PBAR; |
||||||
|
|
||||||
|
fn glob_license_files(path: &Path) -> Result<Vec<String>, failure::Error> { |
||||||
|
let mut license_files: Vec<String> = Vec::new(); |
||||||
|
let path_string = match path.join("LICENSE*").to_str() { |
||||||
|
Some(path_string) => path_string.to_owned(), |
||||||
|
None => { |
||||||
|
return Err(format_err!( |
||||||
|
"Could not convert joined license path to String" |
||||||
|
)); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
for entry in glob(&path_string)? { |
||||||
|
match entry { |
||||||
|
Ok(globed_path) => { |
||||||
|
let file_name = match globed_path.file_name() { |
||||||
|
Some(file_name) => file_name, |
||||||
|
None => return Err(format_err!("Could not get file name from path")), |
||||||
|
}; |
||||||
|
let file_name_string = match file_name.to_str() { |
||||||
|
Some(file_name_string) => file_name_string.to_owned(), |
||||||
|
None => return Err(format_err!("Could not convert filename to String")), |
||||||
|
}; |
||||||
|
license_files.push(file_name_string); |
||||||
|
} |
||||||
|
Err(e) => println!("{:?}", e), |
||||||
|
} |
||||||
|
} |
||||||
|
Ok(license_files) |
||||||
|
} |
||||||
|
|
||||||
|
/// Copy the crate's license into the `pkg` directory.
|
||||||
|
pub fn copy_from_crate( |
||||||
|
crate_data: &CrateData, |
||||||
|
path: &Path, |
||||||
|
out_dir: &Path, |
||||||
|
) -> Result<(), failure::Error> { |
||||||
|
assert!( |
||||||
|
fs::metadata(path).ok().map_or(false, |m| m.is_dir()), |
||||||
|
"crate directory should exist" |
||||||
|
); |
||||||
|
|
||||||
|
assert!( |
||||||
|
fs::metadata(&out_dir).ok().map_or(false, |m| m.is_dir()), |
||||||
|
"crate's pkg directory should exist" |
||||||
|
); |
||||||
|
|
||||||
|
match (crate_data.crate_license(), crate_data.crate_license_file()) { |
||||||
|
(Some(_), _) => { |
||||||
|
let license_files = glob_license_files(path); |
||||||
|
|
||||||
|
match license_files { |
||||||
|
Ok(files) => { |
||||||
|
if files.is_empty() { |
||||||
|
PBAR.info("License key is set in Cargo.toml but no LICENSE file(s) were found; Please add the LICENSE file(s) to your project directory"); |
||||||
|
return Ok(()); |
||||||
|
} |
||||||
|
for license_file in files { |
||||||
|
let crate_license_path = path.join(&license_file); |
||||||
|
let new_license_path = out_dir.join(&license_file); |
||||||
|
if fs::copy(&crate_license_path, &new_license_path).is_err() { |
||||||
|
PBAR.info("origin crate has no LICENSE"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
Err(_) => PBAR.info("origin crate has no LICENSE"), |
||||||
|
} |
||||||
|
} |
||||||
|
(None, Some(license_file)) => { |
||||||
|
let crate_license_path = path.join(&license_file); |
||||||
|
let new_license_path = out_dir.join(&license_file); |
||||||
|
if fs::copy(&crate_license_path, &new_license_path).is_err() { |
||||||
|
PBAR.info("origin crate has no LICENSE"); |
||||||
|
} |
||||||
|
} |
||||||
|
(None, None) => {} |
||||||
|
}; |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
@ -1,72 +0,0 @@ |
|||||||
//! Logging facilities for `wasm-pack`.
|
|
||||||
|
|
||||||
use command::Command; |
|
||||||
use failure; |
|
||||||
use slog::{Drain, Level, Logger}; |
|
||||||
use slog_async::Async; |
|
||||||
use slog_term::{FullFormat, PlainDecorator}; |
|
||||||
use std::fs::OpenOptions; |
|
||||||
use std::path::PathBuf; |
|
||||||
|
|
||||||
/// Create the logger for wasm-pack that will output any info warning or errors we encounter
|
|
||||||
pub fn new(cmd: &Command, verbosity: u8) -> Result<Logger, failure::Error> { |
|
||||||
let log_path = log_file_path(&cmd); |
|
||||||
let file = OpenOptions::new() |
|
||||||
.create(true) |
|
||||||
.append(true) |
|
||||||
.open(log_path)?; |
|
||||||
|
|
||||||
let decorator = PlainDecorator::new(file); |
|
||||||
let drain = FullFormat::new(decorator).build().fuse(); |
|
||||||
|
|
||||||
// Set the log level based off the number of v passed in to the command line args.
|
|
||||||
// Level level means only messages of that level and higher are logged. If we have
|
|
||||||
// an error then we'll log it unconditionally, but extra levels are only available
|
|
||||||
// with extra v
|
|
||||||
let log_level = match verbosity { |
|
||||||
0 => Level::Error, |
|
||||||
1 => Level::Info, |
|
||||||
2 => Level::Debug, |
|
||||||
_ => Level::Trace, |
|
||||||
}; |
|
||||||
let drain = Async::new(drain).build().filter_level(log_level).fuse(); |
|
||||||
Ok(Logger::root(drain, o!())) |
|
||||||
} |
|
||||||
|
|
||||||
/// Figure out where to stick the log based off the command arguments given
|
|
||||||
fn log_file_path(cmd: &Command) -> PathBuf { |
|
||||||
let path = match cmd { |
|
||||||
Command::Build(build_opts) => &build_opts.path, |
|
||||||
Command::Pack { path } => path, |
|
||||||
Command::Publish { path, access: _ } => path, |
|
||||||
Command::Test(test_opts) => &test_opts.path, |
|
||||||
Command::Login { .. } => &None, |
|
||||||
}; |
|
||||||
|
|
||||||
// If the path exists attempt to use it, if not default to the current directory
|
|
||||||
if let Some(ref path) = path { |
|
||||||
let mut path_buf = PathBuf::from(path); |
|
||||||
path_buf.push("Cargo.toml"); |
|
||||||
|
|
||||||
// If the manifest file exists put the log in that directory otherwise default
|
|
||||||
// to the current directory.
|
|
||||||
if path_buf.exists() { |
|
||||||
path_buf.pop(); |
|
||||||
path_buf.push("wasm-pack.log"); |
|
||||||
path_buf |
|
||||||
} else { |
|
||||||
let mut path_buf = this_dir(); |
|
||||||
path_buf.push("wasm-pack.log"); |
|
||||||
path_buf |
|
||||||
} |
|
||||||
} else { |
|
||||||
let mut path_buf = this_dir(); |
|
||||||
path_buf.push("wasm-pack.log"); |
|
||||||
path_buf |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// Return a `PathBuf` for the current directory
|
|
||||||
fn this_dir() -> PathBuf { |
|
||||||
PathBuf::from(".") |
|
||||||
} |
|
@ -0,0 +1,152 @@ |
|||||||
|
extern crate failure; |
||||||
|
extern crate wasm_pack; |
||||||
|
|
||||||
|
use std::fs; |
||||||
|
|
||||||
|
use utils::{self, fixture}; |
||||||
|
use wasm_pack::license; |
||||||
|
use wasm_pack::manifest::CrateData; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn it_copies_a_license_default_path() { |
||||||
|
let fixture = fixture::single_license(); |
||||||
|
let out_dir = fixture.path.join("pkg"); |
||||||
|
fs::create_dir(&out_dir).expect("should create pkg directory OK"); |
||||||
|
let crate_data = CrateData::new(&fixture.path); |
||||||
|
|
||||||
|
assert!(license::copy_from_crate(&crate_data.unwrap(), &fixture.path, &out_dir).is_ok()); |
||||||
|
|
||||||
|
let crate_license_path = fixture.path.join("LICENSE"); |
||||||
|
let pkg_license_path = out_dir.join("LICENSE"); |
||||||
|
println!( |
||||||
|
"wasm-pack: should have copied LICENSE from '{}' to '{}'", |
||||||
|
crate_license_path.display(), |
||||||
|
pkg_license_path.display() |
||||||
|
); |
||||||
|
assert!(fs::metadata(&crate_license_path).is_ok()); |
||||||
|
|
||||||
|
assert!(fs::metadata(&pkg_license_path).is_ok()); |
||||||
|
|
||||||
|
let crate_license = utils::file::read_file(&crate_license_path).unwrap(); |
||||||
|
let pkg_license = utils::file::read_file(&pkg_license_path).unwrap(); |
||||||
|
assert_eq!(crate_license, pkg_license); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn it_copies_a_license_provided_path() { |
||||||
|
let fixture = fixture::single_license(); |
||||||
|
let out_dir = fixture.path.join("pkg"); |
||||||
|
fs::create_dir(&out_dir).expect("should create pkg directory OK"); |
||||||
|
let crate_data = CrateData::new(&fixture.path); |
||||||
|
|
||||||
|
assert!(license::copy_from_crate(&crate_data.unwrap(), &fixture.path, &out_dir).is_ok()); |
||||||
|
let crate_license_path = fixture.path.join("LICENSE"); |
||||||
|
let pkg_license_path = out_dir.join("LICENSE"); |
||||||
|
println!( |
||||||
|
"wasm-pack: should have copied LICENSE from '{}' to '{}'", |
||||||
|
crate_license_path.display(), |
||||||
|
pkg_license_path.display() |
||||||
|
); |
||||||
|
assert!(fs::metadata(&crate_license_path).is_ok()); |
||||||
|
assert!(fs::metadata(&pkg_license_path).is_ok()); |
||||||
|
|
||||||
|
let crate_license = utils::file::read_file(&crate_license_path).unwrap(); |
||||||
|
let pkg_license = utils::file::read_file(&pkg_license_path).unwrap(); |
||||||
|
assert_eq!(crate_license, pkg_license); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn it_copies_all_licenses_default_path() { |
||||||
|
let fixture = fixture::dual_license(); |
||||||
|
let out_dir = fixture.path.join("pkg"); |
||||||
|
fs::create_dir(&out_dir).expect("should create pkg directory OK"); |
||||||
|
let crate_data = CrateData::new(&fixture.path); |
||||||
|
|
||||||
|
assert!(license::copy_from_crate(&crate_data.unwrap(), &fixture.path, &out_dir).is_ok()); |
||||||
|
|
||||||
|
let crate_license_path = fixture.path.join("LICENSE-WTFPL"); |
||||||
|
let pkg_license_path = out_dir.join("LICENSE-WTFPL"); |
||||||
|
|
||||||
|
let crate_license_path_2 = fixture.path.join("LICENSE-MIT"); |
||||||
|
let pkg_license_path_2 = out_dir.join("LICENSE-MIT"); |
||||||
|
|
||||||
|
println!( |
||||||
|
"wasm-pack: should have copied LICENSE from '{}' to '{}'", |
||||||
|
crate_license_path.display(), |
||||||
|
pkg_license_path.display() |
||||||
|
); |
||||||
|
assert!(fs::metadata(&crate_license_path).is_ok()); |
||||||
|
assert!(fs::metadata(&pkg_license_path).is_ok()); |
||||||
|
|
||||||
|
assert!(fs::metadata(&crate_license_path_2).is_ok()); |
||||||
|
assert!(fs::metadata(&pkg_license_path_2).is_ok()); |
||||||
|
|
||||||
|
let crate_license = utils::file::read_file(&crate_license_path).unwrap(); |
||||||
|
let pkg_license = utils::file::read_file(&pkg_license_path).unwrap(); |
||||||
|
assert_eq!(crate_license, pkg_license); |
||||||
|
|
||||||
|
let crate_license_2 = utils::file::read_file(&crate_license_path_2).unwrap(); |
||||||
|
let pkg_license_2 = utils::file::read_file(&pkg_license_path_2).unwrap(); |
||||||
|
assert_eq!(crate_license_2, pkg_license_2); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn it_copies_all_licenses_provided_path() { |
||||||
|
let fixture = fixture::dual_license(); |
||||||
|
let out_dir = fixture.path.join("pkg"); |
||||||
|
fs::create_dir(&out_dir).expect("should create pkg directory OK"); |
||||||
|
let crate_data = CrateData::new(&fixture.path); |
||||||
|
|
||||||
|
assert!(license::copy_from_crate(&crate_data.unwrap(), &fixture.path, &out_dir).is_ok()); |
||||||
|
|
||||||
|
let crate_license_path = fixture.path.join("LICENSE-WTFPL"); |
||||||
|
let pkg_license_path = out_dir.join("LICENSE-WTFPL"); |
||||||
|
|
||||||
|
let crate_license_path_2 = fixture.path.join("LICENSE-MIT"); |
||||||
|
let pkg_license_path_2 = out_dir.join("LICENSE-MIT"); |
||||||
|
|
||||||
|
println!( |
||||||
|
"wasm-pack: should have copied LICENSE from '{}' to '{}'", |
||||||
|
crate_license_path.display(), |
||||||
|
pkg_license_path.display() |
||||||
|
); |
||||||
|
assert!(fs::metadata(&crate_license_path).is_ok()); |
||||||
|
assert!(fs::metadata(&pkg_license_path).is_ok()); |
||||||
|
|
||||||
|
assert!(fs::metadata(&crate_license_path_2).is_ok()); |
||||||
|
assert!(fs::metadata(&pkg_license_path_2).is_ok()); |
||||||
|
|
||||||
|
let crate_license = utils::file::read_file(&crate_license_path).unwrap(); |
||||||
|
let pkg_license = utils::file::read_file(&pkg_license_path).unwrap(); |
||||||
|
assert_eq!(crate_license, pkg_license); |
||||||
|
|
||||||
|
let crate_license_2 = utils::file::read_file(&crate_license_path_2).unwrap(); |
||||||
|
let pkg_license_2 = utils::file::read_file(&pkg_license_path_2).unwrap(); |
||||||
|
assert_eq!(crate_license_2, pkg_license_2); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn it_copies_a_non_standard_license_provided_path() { |
||||||
|
let license_file = "NON-STANDARD-LICENSE"; |
||||||
|
let fixture = fixture::non_standard_license(license_file); |
||||||
|
let out_dir = fixture.path.join("pkg"); |
||||||
|
fs::create_dir(&out_dir).expect("should create pkg directory OK"); |
||||||
|
let crate_data = CrateData::new(&fixture.path); |
||||||
|
|
||||||
|
assert!(license::copy_from_crate(&crate_data.unwrap(), &fixture.path, &out_dir).is_ok()); |
||||||
|
|
||||||
|
let crate_license_path = fixture.path.join(license_file); |
||||||
|
let pkg_license_path = out_dir.join(license_file); |
||||||
|
println!( |
||||||
|
"wasm-pack: should have copied LICENSE from '{}' to '{}'", |
||||||
|
crate_license_path.display(), |
||||||
|
pkg_license_path.display() |
||||||
|
); |
||||||
|
assert!(fs::metadata(&crate_license_path).is_ok()); |
||||||
|
|
||||||
|
assert!(fs::metadata(&pkg_license_path).is_ok()); |
||||||
|
|
||||||
|
let crate_license = utils::file::read_file(&crate_license_path).unwrap(); |
||||||
|
let pkg_license = utils::file::read_file(&pkg_license_path).unwrap(); |
||||||
|
assert_eq!(crate_license, pkg_license); |
||||||
|
} |
@ -1,6 +0,0 @@ |
|||||||
use slog::Logger; |
|
||||||
|
|
||||||
// Create a logger that ignores log messages for testing.
|
|
||||||
pub fn null_logger() -> Logger { |
|
||||||
Logger::root(slog::Discard, o!()) |
|
||||||
} |
|
@ -1,4 +1,3 @@ |
|||||||
pub mod file; |
pub mod file; |
||||||
pub mod fixture; |
pub mod fixture; |
||||||
pub mod logger; |
|
||||||
pub mod manifest; |
pub mod manifest; |
||||||
|
Loading…
Reference in new issue