Merge pull request #181 from ashleygwilliams/migerh-fix-spinners

Let the spinners spin again
master
ashley williams 7 years ago committed by GitHub
commit 1c50b12b8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      src/bindgen.rs
  2. 7
      src/build.rs
  3. 516
      src/command.rs
  4. 278
      src/command/init.rs
  5. 31
      src/command/login.rs
  6. 154
      src/command/mod.rs
  7. 20
      src/command/pack.rs
  8. 18
      src/command/publish.rs
  9. 8
      src/command/utils.rs
  10. 10
      src/error.rs
  11. 38
      src/manifest.rs
  12. 71
      src/progressbar.rs
  13. 5
      src/readme.rs
  14. 21
      tests/manifest/main.rs

@ -6,17 +6,16 @@ use PBAR;
pub fn cargo_install_wasm_bindgen(step: &Step) -> Result<(), Error> { pub fn cargo_install_wasm_bindgen(step: &Step) -> Result<(), Error> {
let msg = format!("{}Installing WASM-bindgen...", emoji::DOWN_ARROW); let msg = format!("{}Installing WASM-bindgen...", emoji::DOWN_ARROW);
let pb = PBAR.step(step, &msg); PBAR.step(step, &msg)?;
let output = Command::new("cargo") let output = Command::new("cargo")
.arg("install") .arg("install")
.arg("wasm-bindgen-cli") .arg("wasm-bindgen-cli")
.arg("--force") .arg("--force")
.output()?; .output()?;
pb.finish();
if !output.status.success() { if !output.status.success() {
let s = String::from_utf8_lossy(&output.stderr); let s = String::from_utf8_lossy(&output.stderr);
if s.contains("already exists") { if s.contains("already exists") {
PBAR.info("wasm-bindgen already installed"); PBAR.info("wasm-bindgen already installed")?;
return Ok(()); return Ok(());
} }
Error::cli("Installing wasm-bindgen failed", s) Error::cli("Installing wasm-bindgen failed", s)
@ -34,7 +33,7 @@ pub fn wasm_bindgen_build(
step: &Step, step: &Step,
) -> Result<(), Error> { ) -> Result<(), Error> {
let msg = format!("{}Running WASM-bindgen...", emoji::RUNNER); let msg = format!("{}Running WASM-bindgen...", emoji::RUNNER);
let pb = PBAR.step(step, &msg); PBAR.step(step, &msg)?;
let binary_name = name.replace("-", "_"); let binary_name = name.replace("-", "_");
let release_or_debug = if debug { "debug" } else { "release" }; let release_or_debug = if debug { "debug" } else { "release" };
let wasm_path = format!( let wasm_path = format!(
@ -60,7 +59,6 @@ pub fn wasm_bindgen_build(
.arg(dts_arg) .arg(dts_arg)
.arg(target_arg) .arg(target_arg)
.output()?; .output()?;
pb.finish();
if !output.status.success() { if !output.status.success() {
let s = String::from_utf8_lossy(&output.stderr); let s = String::from_utf8_lossy(&output.stderr);
Error::cli("wasm-bindgen failed to execute properly", s) Error::cli("wasm-bindgen failed to execute properly", s)

@ -6,7 +6,7 @@ use PBAR;
pub fn rustup_add_wasm_target(step: &Step) -> Result<(), Error> { pub fn rustup_add_wasm_target(step: &Step) -> Result<(), Error> {
let msg = format!("{}Adding WASM target...", emoji::TARGET); let msg = format!("{}Adding WASM target...", emoji::TARGET);
let pb = PBAR.step(step, &msg); PBAR.step(step, &msg)?;
ensure_nightly()?; ensure_nightly()?;
let output = Command::new("rustup") let output = Command::new("rustup")
.arg("target") .arg("target")
@ -15,7 +15,6 @@ pub fn rustup_add_wasm_target(step: &Step) -> Result<(), Error> {
.arg("--toolchain") .arg("--toolchain")
.arg("nightly") .arg("nightly")
.output()?; .output()?;
pb.finish();
if !output.status.success() { if !output.status.success() {
let s = String::from_utf8_lossy(&output.stderr); let s = String::from_utf8_lossy(&output.stderr);
Error::cli("Adding the wasm32-unknown-unknown target failed", s) Error::cli("Adding the wasm32-unknown-unknown target failed", s)
@ -42,7 +41,7 @@ fn ensure_nightly() -> Result<(), Error> {
pub fn cargo_build_wasm(path: &str, debug: bool, step: &Step) -> Result<(), Error> { pub fn cargo_build_wasm(path: &str, debug: bool, step: &Step) -> Result<(), Error> {
let msg = format!("{}Compiling to WASM...", emoji::CYCLONE); let msg = format!("{}Compiling to WASM...", emoji::CYCLONE);
let pb = PBAR.step(step, &msg); PBAR.step(step, &msg)?;
let output = { let output = {
let mut cmd = Command::new("cargo"); let mut cmd = Command::new("cargo");
cmd.current_dir(path).arg("+nightly").arg("build"); cmd.current_dir(path).arg("+nightly").arg("build");
@ -52,7 +51,7 @@ pub fn cargo_build_wasm(path: &str, debug: bool, step: &Step) -> Result<(), Erro
cmd.arg("--target").arg("wasm32-unknown-unknown"); cmd.arg("--target").arg("wasm32-unknown-unknown");
cmd.output()? cmd.output()?
}; };
pb.finish();
if !output.status.success() { if !output.status.success() {
let s = String::from_utf8_lossy(&output.stderr); let s = String::from_utf8_lossy(&output.stderr);
Error::cli("Compilation of your program failed", s) Error::cli("Compilation of your program failed", s)

@ -1,516 +0,0 @@
use bindgen;
use build;
use emoji;
use error::Error;
use indicatif::HumanDuration;
use manifest;
use npm;
use progressbar::Step;
#[allow(unused)]
use readme;
use slog::Logger;
use std::fs;
use std::result;
use std::time::Instant;
use PBAR;
#[derive(Debug, StructOpt)]
pub enum Command {
#[structopt(name = "init")]
/// 🐣 initialize a package.json based on your compiled wasm!
Init {
path: Option<String>,
#[structopt(long = "scope", short = "s")]
scope: Option<String>,
#[structopt(long = "--skip-build")]
/// Do not build, only update metadata
skip_build: bool,
#[structopt(long = "no-typescript")]
/// By default a *.d.ts file is generated for the generated JS file, but
/// this flag will disable generating this TypeScript file.
disable_dts: bool,
#[structopt(long = "target", short = "t", default_value = "browser")]
/// Sets the target environment. [possible values: browser, nodejs]
target: String,
#[structopt(long = "debug")]
/// Build without --release.
debug: bool,
},
#[structopt(name = "pack")]
/// 🍱 create a tar of your npm package but don't publish!
Pack { path: Option<String> },
#[structopt(name = "publish")]
/// 🎆 pack up your npm package and publish!
Publish { path: Option<String> },
#[structopt(name = "login", alias = "adduser", alias = "add-user")]
/// 👤 Add a registry user account! (aliases: adduser, add-user)
Login {
#[structopt(long = "registry", short = "r")]
/// Default: 'https://registry.npmjs.org/'.
/// The base URL of the npm package registry. If scope is also
/// specified, this registry will only be used for packages with that
/// scope. scope defaults to the scope of the project directory you're
/// currently in, if any.
registry: Option<String>,
#[structopt(long = "scope", short = "s")]
/// Default: none.
/// If specified, the user and login credentials given will be
/// associated with the specified scope.
scope: Option<String>,
#[structopt(long = "always-auth", short = "a")]
/// If specified, save configuration indicating that all requests to the
/// given registry should include authorization information. Useful for
/// private registries. Can be used with --registry and / or --scope
always_auth: bool,
#[structopt(long = "auth-type", short = "t")]
/// Default: 'legacy'.
/// Type: 'legacy', 'sso', 'saml', 'oauth'.
/// What authentication strategy to use with adduser/login. Some npm
/// registries (for example, npmE) might support alternative auth
/// strategies besides classic username/password entry in legacy npm.
auth_type: Option<String>,
},
}
pub fn run_wasm_pack(command: Command, log: &Logger) -> result::Result<(), Error> {
// Run the correct command based off input and store the result of it so that we can clear
// the progress bar then return it
let status = match command {
Command::Init {
path,
scope,
skip_build,
disable_dts,
target,
debug,
} => {
info!(&log, "Running init command...");
info!(
&log,
"Path: {:?}, Scope: {:?}, Skip build: {}, Disable Dts: {}, Target: {}, Debug: {}",
&path,
&scope,
&skip_build,
&disable_dts,
&target,
debug
);
let mode = if skip_build {
InitMode::Nobuild
} else {
InitMode::Normal
};
Init::new(path, scope, disable_dts, target, debug).process(&log, mode)
}
Command::Pack { path } => {
info!(&log, "Running pack command...");
info!(&log, "Path: {:?}", &path);
pack(path, &log)
}
Command::Publish { path } => {
info!(&log, "Running publish command...");
info!(&log, "Path: {:?}", &path);
publish(path, &log)
}
Command::Login {
registry,
scope,
always_auth,
auth_type,
} => {
info!(&log, "Running login command...");
info!(
&log,
"Registry: {:?}, Scope: {:?}, Always Auth: {}, Auth Type: {:?}",
&registry,
&scope,
&always_auth,
&auth_type
);
login(registry, scope, always_auth, auth_type, &log)
}
};
match status {
Ok(_) => {}
Err(ref e) => {
error!(&log, "{}", e);
PBAR.error(e.error_type());
}
}
// Make sure we always clear the progress bar before we abort the program otherwise
// stderr and stdout output get eaten up and nothing will work. If this part fails
// to work and clear the progress bars then you're really having a bad day with your tools.
PBAR.done()?;
// Return the actual status of the program to the main function
status
}
// quicli::prelude::* imports a different result struct which gets
// precedence over the std::result::Result, so have had to specify
// the correct type here.
pub fn create_pkg_dir(path: &str, step: &Step) -> result::Result<(), Error> {
let msg = format!("{}Creating a pkg directory...", emoji::FOLDER);
let pb = PBAR.step(step, &msg);
let pkg_dir_path = format!("{}/pkg", path);
fs::create_dir_all(pkg_dir_path)?;
pb.finish();
Ok(())
}
enum InitMode {
Normal,
Nobuild,
}
struct Init {
crate_path: String,
scope: Option<String>,
disable_dts: bool,
target: String,
debug: bool,
crate_name: Option<String>,
}
type InitStep = fn(&mut Init, &Step, &Logger) -> result::Result<(), Error>;
impl Init {
pub fn new(
path: Option<String>,
scope: Option<String>,
disable_dts: bool,
target: String,
debug: bool,
) -> Init {
Init {
crate_path: set_crate_path(path),
scope,
disable_dts,
target,
debug,
crate_name: None,
}
}
fn get_process_steps(mode: InitMode) -> Vec<(&'static str, InitStep)> {
macro_rules! steps {
($($name:ident),+) => {
{
let mut steps: Vec<(&'static str, InitStep)> = Vec::new();
$(steps.push((stringify!($name), Init::$name));)*
steps
}
};
($($name:ident,)*) => (steps![$($name),*])
}
match mode {
InitMode::Normal => steps![
step_check_dependency,
step_add_wasm_target,
step_build_wasm,
step_create_dir,
step_create_json,
step_copy_readme,
step_check_create_type,
step_install_wasm_bindgen,
step_running_wasm_bindgen,
],
InitMode::Nobuild => steps![
step_check_dependency,
step_create_dir,
step_create_json,
step_copy_readme,
],
}
}
pub fn process(&mut self, log: &Logger, mode: InitMode) -> result::Result<(), Error> {
let process_steps = Init::get_process_steps(mode);
let mut step_counter = Step::new(process_steps.len());
let started = Instant::now();
for (_, process_step) in process_steps {
process_step(self, &step_counter, log)?;
step_counter.inc();
}
let duration = HumanDuration(started.elapsed());
info!(&log, "Done in {}.", &duration);
info!(
&log,
"Your WASM pkg is ready to publish at {}/pkg.", &self.crate_path
);
PBAR.message(&format!("{} Done in {}", emoji::SPARKLE, &duration));
PBAR.message(&format!(
"{} Your WASM pkg is ready to publish at {}/pkg.",
emoji::PACKAGE,
&self.crate_path
));
Ok(())
}
fn step_check_dependency(&mut self, _step: &Step, log: &Logger) -> result::Result<(), Error> {
info!(&log, "Checking wasm-bindgen dependency...");
manifest::check_wasm_bindgen(&self.crate_path)?;
info!(&log, "wasm-bindgen dependency is correctly declared.");
Ok(())
}
fn step_add_wasm_target(&mut self, step: &Step, log: &Logger) -> result::Result<(), Error> {
info!(&log, "Adding wasm-target...");
build::rustup_add_wasm_target(step)?;
info!(&log, "Adding wasm-target was successful.");
Ok(())
}
fn step_build_wasm(&mut self, step: &Step, log: &Logger) -> result::Result<(), Error> {
info!(&log, "Building wasm...");
build::cargo_build_wasm(&self.crate_path, self.debug, step)?;
#[cfg(not(target_os = "windows"))]
info!(
&log,
"wasm built at {}/target/wasm32-unknown-unknown/release.", &self.crate_path
);
#[cfg(target_os = "windows")]
info!(
&log,
"wasm built at {}\\target\\wasm32-unknown-unknown\\release.", &self.crate_path
);
Ok(())
}
fn step_create_dir(&mut self, step: &Step, log: &Logger) -> result::Result<(), Error> {
info!(&log, "Creating a pkg directory...");
create_pkg_dir(&self.crate_path, step)?;
info!(&log, "Created a pkg directory at {}.", &self.crate_path);
Ok(())
}
fn step_create_json(&mut self, step: &Step, log: &Logger) -> result::Result<(), Error> {
info!(&log, "Writing a package.json...");
manifest::write_package_json(&self.crate_path, &self.scope, self.disable_dts, step)?;
#[cfg(not(target_os = "windows"))]
info!(
&log,
"Wrote a package.json at {}/pkg/package.json.", &self.crate_path
);
#[cfg(target_os = "windows")]
info!(
&log,
"Wrote a package.json at {}\\pkg\\package.json.", &self.crate_path
);
Ok(())
}
fn step_copy_readme(&mut self, step: &Step, log: &Logger) -> result::Result<(), Error> {
info!(&log, "Copying readme from crate...");
readme::copy_from_crate(&self.crate_path, step)?;
#[cfg(not(target_os = "windows"))]
info!(
&log,
"Copied readme from crate to {}/pkg.", &self.crate_path
);
#[cfg(target_os = "windows")]
info!(
&log,
"Copied readme from crate to {}\\pkg.", &self.crate_path
);
Ok(())
}
fn step_check_create_type(&mut self, _step: &Step, log: &Logger) -> result::Result<(), Error> {
info!(&log, "Checking the crate type from the manifest...");
manifest::check_crate_type(&self.crate_path)?;
#[cfg(not(target_os = "windows"))]
info!(
&log,
"Checked crate type from the manifest at {}/Cargo.toml.", &self.crate_path
);
#[cfg(target_os = "windows")]
info!(
&log,
"Checked crate type from the manifest at {}\\Cargo.toml.", &self.crate_path
);
Ok(())
}
fn step_install_wasm_bindgen(
&mut self,
step: &Step,
log: &Logger,
) -> result::Result<(), Error> {
info!(&log, "Installing wasm-bindgen-cli...");
bindgen::cargo_install_wasm_bindgen(step)?;
info!(&log, "Installing wasm-bindgen-cli was successful.");
info!(&log, "Getting the crate name from the manifest...");
self.crate_name = Some(manifest::get_crate_name(&self.crate_path)?);
#[cfg(not(target_os = "windows"))]
info!(
&log,
"Got crate name {} from the manifest at {}/Cargo.toml.",
&self.crate_name.as_ref().unwrap(),
&self.crate_path
);
#[cfg(target_os = "windows")]
info!(
&log,
"Got crate name {} from the manifest at {}\\Cargo.toml.",
&self.crate_name.as_ref().unwrap(),
&self.crate_path
);
Ok(())
}
fn step_running_wasm_bindgen(
&mut self,
step: &Step,
log: &Logger,
) -> result::Result<(), Error> {
info!(&log, "Building the wasm bindings...");
bindgen::wasm_bindgen_build(
&self.crate_path,
&self.crate_name.as_ref().unwrap(),
self.disable_dts,
&self.target,
self.debug,
step,
)?;
#[cfg(not(target_os = "windows"))]
info!(
&log,
"wasm bindings were built at {}/pkg.", &self.crate_path
);
#[cfg(target_os = "windows")]
info!(
&log,
"wasm bindings were built at {}\\pkg.", &self.crate_path
);
Ok(())
}
}
fn pack(path: Option<String>, log: &Logger) -> result::Result<(), Error> {
let crate_path = set_crate_path(path);
info!(&log, "Packing up the npm package...");
npm::npm_pack(&crate_path)?;
#[cfg(not(target_os = "windows"))]
info!(&log, "Your package is located at {}/pkg", &crate_path);
#[cfg(target_os = "windows")]
info!(&log, "Your package is located at {}\\pkg", &crate_path);
PBAR.message("🎒 packed up your package!");
Ok(())
}
fn publish(path: Option<String>, log: &Logger) -> result::Result<(), Error> {
let crate_path = set_crate_path(path);
info!(&log, "Publishing the npm package...");
info!(&log, "npm info located in the npm debug log");
npm::npm_publish(&crate_path)?;
info!(&log, "Published your package!");
PBAR.message("💥 published your package!");
Ok(())
}
fn login(
registry: Option<String>,
scope: Option<String>,
always_auth: bool,
auth_type: Option<String>,
log: &Logger,
) -> result::Result<(), Error> {
let registry = registry.unwrap_or(npm::DEFAULT_NPM_REGISTRY.to_string());
info!(&log, "Logging in to npm...");
info!(
&log,
"Scope: {:?} Registry: {}, Always Auth: {}, Auth Type: {:?}.",
&scope,
&registry,
always_auth,
&auth_type
);
info!(&log, "npm info located in the npm debug log");
npm::npm_login(&registry, &scope, always_auth, &auth_type)?;
info!(&log, "Logged you in!");
PBAR.message(&format!("👋 logged you in!"));
Ok(())
}
fn set_crate_path(path: Option<String>) -> String {
let crate_path = match path {
Some(p) => p,
None => ".".to_string(),
};
crate_path
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn init_normal_build() {
let steps: Vec<&str> = Init::get_process_steps(InitMode::Normal)
.into_iter()
.map(|(n, _)| n)
.collect();
assert_eq!(
steps,
[
"step_check_dependency",
"step_add_wasm_target",
"step_build_wasm",
"step_create_dir",
"step_create_json",
"step_copy_readme",
"step_check_create_type",
"step_install_wasm_bindgen",
"step_running_wasm_bindgen"
]
);
}
#[test]
fn init_skip_build() {
let steps: Vec<&str> = Init::get_process_steps(InitMode::Nobuild)
.into_iter()
.map(|(n, _)| n)
.collect();
assert_eq!(
steps,
[
"step_check_dependency",
"step_create_dir",
"step_create_json",
"step_copy_readme"
]
);
}
}

@ -0,0 +1,278 @@
use bindgen;
use build;
use command::utils::set_crate_path;
use emoji;
use error::Error;
use indicatif::HumanDuration;
use manifest;
use progressbar::Step;
use readme;
use slog::Logger;
use std::time::Instant;
use std::{fs, result};
use PBAR;
// quicli::prelude::* imports a different result struct which gets
// precedence over the std::result::Result, so have had to specify
// the correct type here.
pub fn create_pkg_dir(path: &str, step: &Step) -> result::Result<(), Error> {
let msg = format!("{}Creating a pkg directory...", emoji::FOLDER);
PBAR.step(step, &msg)?;
let pkg_dir_path = format!("{}/pkg", path);
fs::create_dir_all(pkg_dir_path)?;
Ok(())
}
pub enum InitMode {
Normal,
Nobuild,
}
pub struct Init {
crate_path: String,
scope: Option<String>,
disable_dts: bool,
target: String,
debug: bool,
crate_name: Option<String>,
}
type InitStep = fn(&mut Init, &Step, &Logger) -> result::Result<(), Error>;
impl Init {
pub fn new(
path: Option<String>,
scope: Option<String>,
disable_dts: bool,
target: String,
debug: bool,
) -> Init {
Init {
crate_path: set_crate_path(path),
scope,
disable_dts,
target,
debug,
crate_name: None,
}
}
fn get_process_steps(mode: InitMode) -> Vec<(&'static str, InitStep)> {
macro_rules! steps {
($($name:ident),+) => {
{
let mut steps: Vec<(&'static str, InitStep)> = Vec::new();
$(steps.push((stringify!($name), Init::$name));)*
steps
}
};
($($name:ident,)*) => (steps![$($name),*])
}
match mode {
InitMode::Normal => steps![
step_check_crate_config,
step_add_wasm_target,
step_build_wasm,
step_create_dir,
step_create_json,
step_copy_readme,
step_install_wasm_bindgen,
step_running_wasm_bindgen,
],
InitMode::Nobuild => steps![step_create_dir, step_create_json, step_copy_readme,],
}
}
pub fn process(&mut self, log: &Logger, mode: InitMode) -> result::Result<(), Error> {
let process_steps = Init::get_process_steps(mode);
let mut step_counter = Step::new(process_steps.len());
let started = Instant::now();
for (_, process_step) in process_steps {
process_step(self, &step_counter, log)?;
step_counter.inc();
}
let duration = HumanDuration(started.elapsed());
info!(&log, "Done in {}.", &duration);
info!(
&log,
"Your WASM pkg is ready to publish at {}/pkg.", &self.crate_path
);
PBAR.message(&format!("{} Done in {}", emoji::SPARKLE, &duration))?;
PBAR.message(&format!(
"{} Your WASM pkg is ready to publish at {}/pkg.",
emoji::PACKAGE,
&self.crate_path
))?;
Ok(())
}
fn step_check_crate_config(&mut self, step: &Step, log: &Logger) -> result::Result<(), Error> {
info!(&log, "Checking crate configuration...");
manifest::check_crate_config(&self.crate_path, step)?;
info!(&log, "Crate is correctly configured.");
Ok(())
}
fn step_add_wasm_target(&mut self, step: &Step, log: &Logger) -> result::Result<(), Error> {
info!(&log, "Adding wasm-target...");
build::rustup_add_wasm_target(step)?;
info!(&log, "Adding wasm-target was successful.");
Ok(())
}
fn step_build_wasm(&mut self, step: &Step, log: &Logger) -> result::Result<(), Error> {
info!(&log, "Building wasm...");
build::cargo_build_wasm(&self.crate_path, self.debug, step)?;
#[cfg(not(target_os = "windows"))]
info!(
&log,
"wasm built at {}/target/wasm32-unknown-unknown/release.", &self.crate_path
);
#[cfg(target_os = "windows")]
info!(
&log,
"wasm built at {}\\target\\wasm32-unknown-unknown\\release.", &self.crate_path
);
Ok(())
}
fn step_create_dir(&mut self, step: &Step, log: &Logger) -> result::Result<(), Error> {
info!(&log, "Creating a pkg directory...");
create_pkg_dir(&self.crate_path, step)?;
info!(&log, "Created a pkg directory at {}.", &self.crate_path);
Ok(())
}
fn step_create_json(&mut self, step: &Step, log: &Logger) -> result::Result<(), Error> {
info!(&log, "Writing a package.json...");
manifest::write_package_json(&self.crate_path, &self.scope, self.disable_dts, step)?;
#[cfg(not(target_os = "windows"))]
info!(
&log,
"Wrote a package.json at {}/pkg/package.json.", &self.crate_path
);
#[cfg(target_os = "windows")]
info!(
&log,
"Wrote a package.json at {}\\pkg\\package.json.", &self.crate_path
);
Ok(())
}
fn step_copy_readme(&mut self, step: &Step, log: &Logger) -> result::Result<(), Error> {
info!(&log, "Copying readme from crate...");
readme::copy_from_crate(&self.crate_path, step)?;
#[cfg(not(target_os = "windows"))]
info!(
&log,
"Copied readme from crate to {}/pkg.", &self.crate_path
);
#[cfg(target_os = "windows")]
info!(
&log,
"Copied readme from crate to {}\\pkg.", &self.crate_path
);
Ok(())
}
fn step_install_wasm_bindgen(
&mut self,
step: &Step,
log: &Logger,
) -> result::Result<(), Error> {
info!(&log, "Installing wasm-bindgen-cli...");
bindgen::cargo_install_wasm_bindgen(step)?;
info!(&log, "Installing wasm-bindgen-cli was successful.");
info!(&log, "Getting the crate name from the manifest...");
self.crate_name = Some(manifest::get_crate_name(&self.crate_path)?);
#[cfg(not(target_os = "windows"))]
info!(
&log,
"Got crate name {} from the manifest at {}/Cargo.toml.",
&self.crate_name.as_ref().unwrap(),
&self.crate_path
);
#[cfg(target_os = "windows")]
info!(
&log,
"Got crate name {} from the manifest at {}\\Cargo.toml.",
&self.crate_name.as_ref().unwrap(),
&self.crate_path
);
Ok(())
}
fn step_running_wasm_bindgen(
&mut self,
step: &Step,
log: &Logger,
) -> result::Result<(), Error> {
info!(&log, "Building the wasm bindings...");
bindgen::wasm_bindgen_build(
&self.crate_path,
&self.crate_name.as_ref().unwrap(),
self.disable_dts,
&self.target,
self.debug,
step,
)?;
#[cfg(not(target_os = "windows"))]
info!(
&log,
"wasm bindings were built at {}/pkg.", &self.crate_path
);
#[cfg(target_os = "windows")]
info!(
&log,
"wasm bindings were built at {}\\pkg.", &self.crate_path
);
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn init_normal_build() {
let steps: Vec<&str> = Init::get_process_steps(InitMode::Normal)
.into_iter()
.map(|(n, _)| n)
.collect();
assert_eq!(
steps,
[
"step_check_crate_config",
"step_add_wasm_target",
"step_build_wasm",
"step_create_dir",
"step_create_json",
"step_copy_readme",
"step_install_wasm_bindgen",
"step_running_wasm_bindgen"
]
);
}
#[test]
fn init_skip_build() {
let steps: Vec<&str> = Init::get_process_steps(InitMode::Nobuild)
.into_iter()
.map(|(n, _)| n)
.collect();
assert_eq!(
steps,
["step_create_dir", "step_create_json", "step_copy_readme"]
);
}
}

@ -0,0 +1,31 @@
use error::Error;
use npm;
use slog::Logger;
use std::result;
use PBAR;
pub fn login(
registry: Option<String>,
scope: Option<String>,
always_auth: bool,
auth_type: Option<String>,
log: &Logger,
) -> result::Result<(), Error> {
let registry = registry.unwrap_or(npm::DEFAULT_NPM_REGISTRY.to_string());
info!(&log, "Logging in to npm...");
info!(
&log,
"Scope: {:?} Registry: {}, Always Auth: {}, Auth Type: {:?}.",
&scope,
&registry,
always_auth,
&auth_type
);
info!(&log, "npm info located in the npm debug log");
npm::npm_login(&registry, &scope, always_auth, &auth_type)?;
info!(&log, "Logged you in!");
PBAR.message(&format!("👋 logged you in!"))?;
Ok(())
}

@ -0,0 +1,154 @@
pub mod init;
mod login;
mod pack;
mod publish;
pub mod utils;
use self::init::{Init, InitMode};
use self::login::login;
use self::pack::pack;
use self::publish::publish;
use error::Error;
use slog::Logger;
use std::result;
use PBAR;
#[derive(Debug, StructOpt)]
pub enum Command {
#[structopt(name = "init")]
/// 🐣 initialize a package.json based on your compiled wasm!
Init {
path: Option<String>,
#[structopt(long = "scope", short = "s")]
scope: Option<String>,
#[structopt(long = "--skip-build")]
/// Do not build, only update metadata
skip_build: bool,
#[structopt(long = "no-typescript")]
/// By default a *.d.ts file is generated for the generated JS file, but
/// this flag will disable generating this TypeScript file.
disable_dts: bool,
#[structopt(long = "target", short = "t", default_value = "browser")]
/// Sets the target environment. [possible values: browser, nodejs]
target: String,
#[structopt(long = "debug")]
/// Build without --release.
debug: bool,
},
#[structopt(name = "pack")]
/// 🍱 create a tar of your npm package but don't publish!
Pack { path: Option<String> },
#[structopt(name = "publish")]
/// 🎆 pack up your npm package and publish!
Publish { path: Option<String> },
#[structopt(name = "login", alias = "adduser", alias = "add-user")]
/// 👤 Add a registry user account! (aliases: adduser, add-user)
Login {
#[structopt(long = "registry", short = "r")]
/// Default: 'https://registry.npmjs.org/'.
/// The base URL of the npm package registry. If scope is also
/// specified, this registry will only be used for packages with that
/// scope. scope defaults to the scope of the project directory you're
/// currently in, if any.
registry: Option<String>,
#[structopt(long = "scope", short = "s")]
/// Default: none.
/// If specified, the user and login credentials given will be
/// associated with the specified scope.
scope: Option<String>,
#[structopt(long = "always-auth", short = "a")]
/// If specified, save configuration indicating that all requests to the
/// given registry should include authorization information. Useful for
/// private registries. Can be used with --registry and / or --scope
always_auth: bool,
#[structopt(long = "auth-type", short = "t")]
/// Default: 'legacy'.
/// Type: 'legacy', 'sso', 'saml', 'oauth'.
/// What authentication strategy to use with adduser/login. Some npm
/// registries (for example, npmE) might support alternative auth
/// strategies besides classic username/password entry in legacy npm.
auth_type: Option<String>,
},
}
pub fn run_wasm_pack(command: Command, log: &Logger) -> result::Result<(), Error> {
// Run the correct command based off input and store the result of it so that we can clear
// the progress bar then return it
let status = match command {
Command::Init {
path,
scope,
skip_build,
disable_dts,
target,
debug,
} => {
info!(&log, "Running init command...");
info!(
&log,
"Path: {:?}, Scope: {:?}, Skip build: {}, Disable Dts: {}, Target: {}, Debug: {}",
&path,
&scope,
&skip_build,
&disable_dts,
&target,
debug
);
let mode = if skip_build {
InitMode::Nobuild
} else {
InitMode::Normal
};
Init::new(path, scope, disable_dts, target, debug).process(&log, mode)
}
Command::Pack { path } => {
info!(&log, "Running pack command...");
info!(&log, "Path: {:?}", &path);
pack(path, &log)
}
Command::Publish { path } => {
info!(&log, "Running publish command...");
info!(&log, "Path: {:?}", &path);
publish(path, &log)
}
Command::Login {
registry,
scope,
always_auth,
auth_type,
} => {
info!(&log, "Running login command...");
info!(
&log,
"Registry: {:?}, Scope: {:?}, Always Auth: {}, Auth Type: {:?}",
&registry,
&scope,
&always_auth,
&auth_type
);
login(registry, scope, always_auth, auth_type, &log)
}
};
match status {
Ok(_) => {}
Err(ref e) => {
error!(&log, "{}", e);
PBAR.error(e.error_type())?;
}
}
// Return the actual status of the program to the main function
status
}

@ -0,0 +1,20 @@
use command::utils::set_crate_path;
use error::Error;
use npm;
use slog::Logger;
use std::result;
use PBAR;
pub fn pack(path: Option<String>, log: &Logger) -> result::Result<(), Error> {
let crate_path = set_crate_path(path);
info!(&log, "Packing up the npm package...");
npm::npm_pack(&crate_path)?;
#[cfg(not(target_os = "windows"))]
info!(&log, "Your package is located at {}/pkg", &crate_path);
#[cfg(target_os = "windows")]
info!(&log, "Your package is located at {}\\pkg", &crate_path);
PBAR.message("🎒 packed up your package!")?;
Ok(())
}

@ -0,0 +1,18 @@
use command::utils::set_crate_path;
use error::Error;
use npm;
use slog::Logger;
use std::result;
use PBAR;
pub fn publish(path: Option<String>, log: &Logger) -> result::Result<(), Error> {
let crate_path = set_crate_path(path);
info!(&log, "Publishing the npm package...");
info!(&log, "npm info located in the npm debug log");
npm::npm_publish(&crate_path)?;
info!(&log, "Published your package!");
PBAR.message("💥 published your package!")?;
Ok(())
}

@ -0,0 +1,8 @@
pub fn set_crate_path(path: Option<String>) -> String {
let crate_path = match path {
Some(p) => p,
None => ".".to_string(),
};
crate_path
}

@ -2,6 +2,7 @@
use serde_json; use serde_json;
use std::borrow::Cow; use std::borrow::Cow;
use std::io; use std::io;
use std::sync::PoisonError;
use toml; use toml;
#[derive(Debug, Fail)] #[derive(Debug, Fail)]
@ -13,6 +14,8 @@ pub enum Error {
SerdeJson(#[cause] serde_json::Error), SerdeJson(#[cause] serde_json::Error),
#[fail(display = "{}", _0)] #[fail(display = "{}", _0)]
SerdeToml(#[cause] toml::de::Error), SerdeToml(#[cause] toml::de::Error),
#[fail(display = "Acquiring lock failed")]
PoisonedLockError,
#[fail(display = "{}. stderr:\n\n{}", message, stderr)] #[fail(display = "{}. stderr:\n\n{}", message, stderr)]
Cli { message: String, stderr: String }, Cli { message: String, stderr: String },
#[fail(display = "{}", message)] #[fail(display = "{}", message)]
@ -38,6 +41,7 @@ impl Error {
Error::Io(_) => "There was an I/O error. Details:\n\n", Error::Io(_) => "There was an I/O error. Details:\n\n",
Error::SerdeJson(_) => "There was an JSON error. Details:\n\n", Error::SerdeJson(_) => "There was an JSON error. Details:\n\n",
Error::SerdeToml(_) => "There was an TOML error. Details:\n\n", Error::SerdeToml(_) => "There was an TOML error. Details:\n\n",
Error::PoisonedLockError => "There was an RwLock error. Details: \n\n",
Error::Cli { Error::Cli {
message: _, message: _,
stderr: _, stderr: _,
@ -66,3 +70,9 @@ impl From<toml::de::Error> for Error {
Error::SerdeToml(e) Error::SerdeToml(e)
} }
} }
impl<T> From<PoisonError<T>> for Error {
fn from(_: PoisonError<T>) -> Self {
Error::PoisonedLockError
}
}

@ -123,25 +123,24 @@ pub fn write_package_json(
) )
}; };
let pb = PBAR.step(step, &msg); PBAR.step(step, &msg)?;
let pkg_file_path = format!("{}/pkg/package.json", path); let pkg_file_path = format!("{}/pkg/package.json", path);
let mut pkg_file = File::create(pkg_file_path)?; let mut pkg_file = File::create(pkg_file_path)?;
let crate_data = read_cargo_toml(path)?; let crate_data = read_cargo_toml(path)?;
let npm_data = crate_data.into_npm(scope, disable_dts); let npm_data = crate_data.into_npm(scope, disable_dts);
if npm_data.description.is_none() { if npm_data.description.is_none() {
PBAR.warn(&warn_fmt("description")); PBAR.warn(&warn_fmt("description"))?;
} }
if npm_data.repository.is_none() { if npm_data.repository.is_none() {
PBAR.warn(&warn_fmt("repository")); PBAR.warn(&warn_fmt("repository"))?;
} }
if npm_data.license.is_none() { if npm_data.license.is_none() {
PBAR.warn(&warn_fmt("license")); PBAR.warn(&warn_fmt("license"))?;
} }
let npm_json = serde_json::to_string_pretty(&npm_data)?; let npm_json = serde_json::to_string_pretty(&npm_data)?;
pkg_file.write_all(npm_json.as_bytes())?; pkg_file.write_all(npm_json.as_bytes())?;
pb.finish();
Ok(()) Ok(())
} }
@ -149,7 +148,15 @@ pub fn get_crate_name(path: &str) -> Result<String, Error> {
Ok(read_cargo_toml(path)?.package.name) Ok(read_cargo_toml(path)?.package.name)
} }
pub fn check_wasm_bindgen(path: &str) -> Result<(), Error> { pub fn check_crate_config(path: &str, step: &Step) -> Result<(), Error> {
let msg = format!("{}Checking crate configuration...", emoji::WRENCH);
PBAR.step(&step, &msg)?;
check_wasm_bindgen(path)?;
check_crate_type(path)?;
Ok(())
}
fn check_wasm_bindgen(path: &str) -> Result<(), Error> {
if read_cargo_toml(path)?.dependencies.map_or(false, |x| { if read_cargo_toml(path)?.dependencies.map_or(false, |x| {
!x.wasm_bindgen.unwrap_or("".to_string()).is_empty() !x.wasm_bindgen.unwrap_or("".to_string()).is_empty()
}) { }) {
@ -161,19 +168,14 @@ pub fn check_wasm_bindgen(path: &str) -> Result<(), Error> {
)) ))
} }
fn has_cdylib(path: &str) -> Result<bool, Error> { fn check_crate_type(path: &str) -> Result<(), Error> {
Ok(read_cargo_toml(path)?.lib.map_or(false, |lib| { if read_cargo_toml(path)?.lib.map_or(false, |lib| {
lib.crate_type lib.crate_type
.map_or(false, |types| types.iter().any(|s| s == "cdylib")) .map_or(false, |types| types.iter().any(|s| s == "cdylib"))
})) }) {
} return Ok(());
pub fn check_crate_type(path: &str) -> Result<(), Error> {
if !has_cdylib(path)? {
Error::crate_config(
"crate-type must include cdylib to compile to wasm32-unknown-unknown. Add the following to your Cargo.toml file:\n\n[lib]\ncrate-type = [\"cdylib\"]"
)
} else {
Ok(())
} }
Error::crate_config(
"crate-type must be cdylib to compile to wasm32-unknown-unknown. Add the following to your Cargo.toml file:\n\n[lib]\ncrate-type = [\"cdylib\"]"
)
} }

@ -1,60 +1,90 @@
use console::style; use console::style;
use emoji; use emoji;
use error::Error; use error::Error;
use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use indicatif::{ProgressBar, ProgressStyle};
use std::fmt; use std::fmt;
use std::sync::RwLock;
pub struct ProgressOutput { pub struct ProgressOutput {
bar: MultiProgress, spinner: RwLock<ProgressBar>,
messages: RwLock<String>,
} }
impl ProgressOutput { impl ProgressOutput {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
bar: MultiProgress::new(), spinner: RwLock::new(ProgressBar::new_spinner()),
messages: RwLock::new(String::from("")),
} }
} }
pub fn step(&self, step: &Step, message: &str) -> ProgressBar { pub fn step(&self, step: &Step, message: &str) -> Result<(), Error> {
let msg = format!("{} {}", style(step).bold().dim(), message); let msg = format!("{} {}", style(step).bold().dim(), message);
self.bar.add(Self::progressbar(&msg)) self.message(&msg)
} }
pub fn message(&self, message: &str) -> ProgressBar { fn finish(&self) -> Result<(), Error> {
self.bar.add(Self::progressbar(message)) let spinner = self.spinner.read()?;
spinner.finish();
let mut message = self.messages.write()?;
print!("{}", message);
message.clear();
Ok(())
}
pub fn message(&self, message: &str) -> Result<(), Error> {
self.finish()?;
let mut spinner = self.spinner.write()?;
*spinner = Self::progressbar(message);
Ok(())
}
fn add_message(&self, msg: &str) -> Result<(), Error> {
let mut message = self.messages.write()?;
message.push_str(" ");
message.push_str(msg);
message.push('\n');
Ok(())
} }
pub fn info(&self, message: &str) { pub fn info(&self, message: &str) -> Result<(), Error> {
let info = format!( let info = format!(
"{} {}: {}", "{} {}: {}",
emoji::INFO, emoji::INFO,
style("[INFO]").bold().dim(), style("[INFO]").bold().dim(),
message message
); );
let bar = self.bar.add(Self::progressbar(&info)); self.add_message(&info)?;
bar.finish();
Ok(())
} }
pub fn warn(&self, message: &str) { pub fn warn(&self, message: &str) -> Result<(), Error> {
let warn = format!( let warn = format!(
"{} {}: {}", "{} {}: {}",
emoji::WARN, emoji::WARN,
style("[WARN]").bold().dim(), style("[WARN]").bold().dim(),
message message
); );
let bar = self.bar.add(Self::progressbar(&warn)); self.add_message(&warn)?;
bar.finish();
Ok(())
} }
pub fn error(&self, message: String) { pub fn error(&self, message: String) -> Result<(), Error> {
let err = format!( let err = format!(
"{} {}: {}", "{} {}: {}",
emoji::ERROR, emoji::ERROR,
style("[ERR]").bold().dim(), style("[ERR]").bold().dim(),
message message
); );
let bar = self.bar.add(Self::progressbar(&err)); self.add_message(&err)?;
bar.finish();
Ok(())
} }
fn progressbar(msg: &str) -> ProgressBar { fn progressbar(msg: &str) -> ProgressBar {
@ -70,7 +100,8 @@ impl ProgressOutput {
} }
pub fn done(&self) -> Result<(), Error> { pub fn done(&self) -> Result<(), Error> {
self.bar.join_and_clear().map_err(|e| Error::from(e)) self.finish()?;
Ok(())
} }
} }
@ -93,3 +124,9 @@ impl fmt::Display for Step {
write!(f, "[{}/{}]", self.current, self.total) write!(f, "[{}/{}]", self.current, self.total)
} }
} }
impl Drop for ProgressOutput {
fn drop(&mut self) {
self.done().unwrap();
}
}

@ -7,12 +7,11 @@ use PBAR;
pub fn copy_from_crate(path: &str, step: &Step) -> Result<(), Error> { pub fn copy_from_crate(path: &str, step: &Step) -> Result<(), Error> {
let msg = format!("{}Copying over your README...", emoji::DANCERS); let msg = format!("{}Copying over your README...", emoji::DANCERS);
let pb = PBAR.step(step, &msg); PBAR.step(step, &msg)?;
let crate_readme_path = format!("{}/README.md", path); let crate_readme_path = format!("{}/README.md", path);
let new_readme_path = format!("{}/pkg/README.md", path); let new_readme_path = format!("{}/pkg/README.md", path);
if let Err(_) = fs::copy(&crate_readme_path, &new_readme_path) { if let Err(_) = fs::copy(&crate_readme_path, &new_readme_path) {
PBAR.warn("origin crate has no README"); PBAR.warn("origin crate has no README")?;
}; };
pb.finish();
Ok(()) Ok(())
} }

@ -27,24 +27,27 @@ fn it_gets_the_crate_name_provided_path() {
#[test] #[test]
fn it_checks_has_cdylib_default_path() { fn it_checks_has_cdylib_default_path() {
assert!(manifest::check_crate_type(".").is_err()); let step = wasm_pack::progressbar::Step::new(1);
assert!(manifest::check_crate_config(".", &step).is_err());
} }
#[test] #[test]
fn it_checks_has_cdylib_provided_path() { fn it_checks_has_cdylib_provided_path() {
assert!(manifest::check_crate_type("tests/fixtures/js-hello-world").is_ok()); let step = wasm_pack::progressbar::Step::new(1);
assert!(manifest::check_crate_config("tests/fixtures/js-hello-world", &step).is_ok());
} }
#[test] #[test]
fn it_checks_has_cdylib_wrong_crate_type() { fn it_checks_has_cdylib_wrong_crate_type() {
assert!(manifest::check_crate_type("tests/fixtures/bad-cargo-toml").is_err()); let step = wasm_pack::progressbar::Step::new(1);
assert!(manifest::check_crate_config("tests/fixtures/bad-cargo-toml", &step).is_err());
} }
#[test] #[test]
fn it_creates_a_package_json_default_path() { fn it_creates_a_package_json_default_path() {
let step = wasm_pack::progressbar::Step::new(1); let step = wasm_pack::progressbar::Step::new(1);
let path = ".".to_string(); let path = ".".to_string();
wasm_pack::command::create_pkg_dir(&path, &step).unwrap(); wasm_pack::command::init::create_pkg_dir(&path, &step).unwrap();
assert!(manifest::write_package_json(&path, &None, false, &step).is_ok()); assert!(manifest::write_package_json(&path, &None, false, &step).is_ok());
let package_json_path = format!("{}/pkg/package.json", &path); let package_json_path = format!("{}/pkg/package.json", &path);
assert!(fs::metadata(package_json_path).is_ok()); assert!(fs::metadata(package_json_path).is_ok());
@ -66,7 +69,7 @@ fn it_creates_a_package_json_default_path() {
fn it_creates_a_package_json_provided_path() { fn it_creates_a_package_json_provided_path() {
let step = wasm_pack::progressbar::Step::new(1); let step = wasm_pack::progressbar::Step::new(1);
let path = "tests/fixtures/js-hello-world".to_string(); let path = "tests/fixtures/js-hello-world".to_string();
wasm_pack::command::create_pkg_dir(&path, &step).unwrap(); wasm_pack::command::init::create_pkg_dir(&path, &step).unwrap();
assert!(manifest::write_package_json(&path, &None, false, &step).is_ok()); assert!(manifest::write_package_json(&path, &None, false, &step).is_ok());
let package_json_path = format!("{}/pkg/package.json", &path); let package_json_path = format!("{}/pkg/package.json", &path);
assert!(fs::metadata(package_json_path).is_ok()); assert!(fs::metadata(package_json_path).is_ok());
@ -79,7 +82,7 @@ fn it_creates_a_package_json_provided_path() {
fn it_creates_a_package_json_provided_path_with_scope() { fn it_creates_a_package_json_provided_path_with_scope() {
let step = wasm_pack::progressbar::Step::new(1); let step = wasm_pack::progressbar::Step::new(1);
let path = "tests/fixtures/scopes".to_string(); let path = "tests/fixtures/scopes".to_string();
wasm_pack::command::create_pkg_dir(&path, &step).unwrap(); wasm_pack::command::init::create_pkg_dir(&path, &step).unwrap();
assert!(manifest::write_package_json(&path, &Some("test".to_string()), false, &step).is_ok()); assert!(manifest::write_package_json(&path, &Some("test".to_string()), false, &step).is_ok());
let package_json_path = format!("{}/pkg/package.json", &path); let package_json_path = format!("{}/pkg/package.json", &path);
assert!(fs::metadata(package_json_path).is_ok()); assert!(fs::metadata(package_json_path).is_ok());
@ -90,10 +93,12 @@ fn it_creates_a_package_json_provided_path_with_scope() {
#[test] #[test]
fn it_errors_when_wasm_bindgen_is_not_declared() { fn it_errors_when_wasm_bindgen_is_not_declared() {
assert!(manifest::check_wasm_bindgen("tests/fixtures/bad-cargo-toml").is_err()); let step = wasm_pack::progressbar::Step::new(1);
assert!(manifest::check_crate_config("tests/fixtures/bad-cargo-toml", &step).is_err());
} }
#[test] #[test]
fn it_does_not_error_when_wasm_bindgen_is_declared() { fn it_does_not_error_when_wasm_bindgen_is_declared() {
assert!(manifest::check_wasm_bindgen("tests/fixtures/js-hello-world").is_ok()); let step = wasm_pack::progressbar::Step::new(1);
assert!(manifest::check_crate_config("tests/fixtures/js-hello-world", &step).is_ok());
} }

Loading…
Cancel
Save