From 955b9f07d6d3e97bd8fb8cbc96c92433d7f968b4 Mon Sep 17 00:00:00 2001 From: Mario Reder Date: Fri, 25 Oct 2019 17:42:50 +0200 Subject: [PATCH] feat(build): is-child argument is-child can be passed as argument to wasm-pack build for generating packages with multiple build targets --- docs/src/commands/build.md | 17 +++++++ src/command/build.rs | 14 ++++++ src/manifest/mod.rs | 40 ++++++++++++++-- src/manifest/npm/commonjs.rs | 2 +- src/manifest/npm/esmodules.rs | 2 +- src/manifest/npm/mod.rs | 12 ++++- src/manifest/npm/nomodules.rs | 2 +- src/manifest/npm/repository.rs | 2 +- tests/all/manifest.rs | 88 +++++++++++++++++++++++++++++----- 9 files changed, 158 insertions(+), 21 deletions(-) diff --git a/docs/src/commands/build.md b/docs/src/commands/build.md index 247ac7160..fcc9e99f6 100644 --- a/docs/src/commands/build.md +++ b/docs/src/commands/build.md @@ -125,6 +125,23 @@ wasm-pack build examples/js-hello-world --mode no-install | `no-install` | `wasm-pack init` implicitly and create wasm binding without installing `wasm-bindgen`. | | `normal` | do all the stuffs of `no-install` with installed `wasm-bindgen`. | +## Multiple build targets per package + +The `build` command accepts an optional `--is-child` argument. +``` +wasm-pack build examples/js-hello-world --is-child --out-name child +``` + +This command will extend a previously generated package, which makes it possible to have multiple build targets per package. +For example it would be feasible to have an optimized build for web and Node. + +Following is an example which will use the wee_alloc feature for web, because size is more crucial whereas Node should be optimized for speed. + +``` +wasm-pack build examples/js-hello-world --out-dir pkg -- --features "wee_alloc" +wasm-pack build examples/js-hello-world --out-dir pkg --out-name js-hello-node --is-child +``` + ## Extra options The `build` command can pass extra options straight to `cargo build` even if they are not diff --git a/src/command/build.rs b/src/command/build.rs index 723f50746..6783f3dd8 100644 --- a/src/command/build.rs +++ b/src/command/build.rs @@ -32,6 +32,7 @@ pub struct Build { pub mode: InstallMode, pub out_dir: PathBuf, pub out_name: Option, + pub is_child: bool, pub bindgen: Option, pub cache: Cache, pub extra_options: Vec, @@ -148,6 +149,10 @@ pub struct BuildOptions { /// Sets the output file names. Defaults to package name. pub out_name: Option, + #[structopt(long = "is-child")] + /// Won't generate package metadata. Instead adds output to existing package given by `out-dir`. + pub is_child: bool, + #[structopt(last = true)] /// List of extra options to pass to `cargo build` pub extra_options: Vec, @@ -167,6 +172,7 @@ impl Default for BuildOptions { profiling: false, out_dir: String::new(), out_name: None, + is_child: false, extra_options: Vec::new(), } } @@ -201,6 +207,7 @@ impl Build { mode: build_opts.mode, out_dir, out_name: build_opts.out_name, + is_child: build_opts.is_child, bindgen: None, cache: cache::get_wasm_pack_cache()?, extra_options: build_opts.extra_options, @@ -324,6 +331,7 @@ impl Build { &self.scope, self.disable_dts, self.target, + self.is_child, )?; info!( "Wrote a package.json at {:#?}.", @@ -333,6 +341,9 @@ impl Build { } fn step_copy_readme(&mut self) -> Result<(), Error> { + if self.is_child { + return Ok(()); + } info!("Copying readme from crate..."); readme::copy_from_crate(&self.crate_path, &self.out_dir)?; info!("Copied readme from crate to {:#?}.", &self.out_dir); @@ -340,6 +351,9 @@ impl Build { } fn step_copy_license(&mut self) -> Result<(), failure::Error> { + if self.is_child { + return Ok(()); + } info!("Copying license from crate..."); license::copy_from_crate(&self.crate_data, &self.crate_path, &self.out_dir)?; info!("Copied license from crate to {:#?}.", &self.out_dir); diff --git a/src/manifest/mod.rs b/src/manifest/mod.rs index 0caaf420c..e02511708 100644 --- a/src/manifest/mod.rs +++ b/src/manifest/mod.rs @@ -560,13 +560,43 @@ impl CrateData { scope: &Option, disable_dts: bool, target: Target, + is_child: bool, ) -> Result<(), Error> { let pkg_file_path = out_dir.join("package.json"); - let npm_data = match target { - Target::Nodejs => self.to_commonjs(scope, disable_dts, out_dir), - Target::NoModules => self.to_nomodules(scope, disable_dts, out_dir), - Target::Bundler => self.to_esmodules(scope, disable_dts, out_dir), - Target::Web => self.to_web(scope, disable_dts, out_dir), + let npm_data = if is_child { + let npm_json = match fs::read_to_string(&pkg_file_path) { + Ok(file) => file, + Err(_) => bail!( + "--is-child was provided, but given --out-dir path does not contain a package.json. \ + Please make sure, that you compile the parent directory to the same --out-dir"), + }; + let mut npm_data: NpmPackage = serde_json::from_str(&npm_json)?; + let name_prefix = self.name_prefix(); + + let wasm_file = format!("{}_bg.wasm", name_prefix); + npm_data.add_file(wasm_file); + + let js_file = format!("{}.js", name_prefix); + npm_data.add_file(js_file); + + if let Target::Nodejs = target { + let js_bg_file = format!("{}_bg.js", name_prefix); + npm_data.add_file(js_bg_file); + } + + if !disable_dts { + let dts_file = format!("{}.d.ts", name_prefix); + npm_data.add_file(dts_file); + } + + npm_data + } else { + match target { + Target::Nodejs => self.to_commonjs(scope, disable_dts, out_dir), + Target::NoModules => self.to_nomodules(scope, disable_dts, out_dir), + Target::Bundler => self.to_esmodules(scope, disable_dts, out_dir), + Target::Web => self.to_web(scope, disable_dts, out_dir), + } }; let npm_json = serde_json::to_string_pretty(&npm_data)?; diff --git a/src/manifest/npm/commonjs.rs b/src/manifest/npm/commonjs.rs index 8d8ce8bd4..6d056a2fc 100644 --- a/src/manifest/npm/commonjs.rs +++ b/src/manifest/npm/commonjs.rs @@ -1,6 +1,6 @@ use manifest::npm::repository::Repository; -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct CommonJSPackage { pub name: String, #[serde(skip_serializing_if = "Vec::is_empty")] diff --git a/src/manifest/npm/esmodules.rs b/src/manifest/npm/esmodules.rs index b16c5c662..0f2fa42ab 100644 --- a/src/manifest/npm/esmodules.rs +++ b/src/manifest/npm/esmodules.rs @@ -1,6 +1,6 @@ use manifest::npm::repository::Repository; -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct ESModulesPackage { pub name: String, #[serde(skip_serializing_if = "Vec::is_empty")] diff --git a/src/manifest/npm/mod.rs b/src/manifest/npm/mod.rs index c8d658210..295dceade 100644 --- a/src/manifest/npm/mod.rs +++ b/src/manifest/npm/mod.rs @@ -7,10 +7,20 @@ pub use self::commonjs::CommonJSPackage; pub use self::esmodules::ESModulesPackage; pub use self::nomodules::NoModulesPackage; -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] #[serde(untagged)] pub enum NpmPackage { CommonJSPackage(CommonJSPackage), ESModulesPackage(ESModulesPackage), NoModulesPackage(NoModulesPackage), } + +impl NpmPackage { + pub fn add_file(&mut self, file: String) { + match self { + Self::CommonJSPackage(pkg) => pkg.files.push(file), + Self::ESModulesPackage(pkg) => pkg.files.push(file), + Self::NoModulesPackage(pkg) => pkg.files.push(file), + } + } +} diff --git a/src/manifest/npm/nomodules.rs b/src/manifest/npm/nomodules.rs index 913e97e72..600298c7b 100644 --- a/src/manifest/npm/nomodules.rs +++ b/src/manifest/npm/nomodules.rs @@ -1,6 +1,6 @@ use manifest::npm::repository::Repository; -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct NoModulesPackage { pub name: String, #[serde(skip_serializing_if = "Vec::is_empty")] diff --git a/src/manifest/npm/repository.rs b/src/manifest/npm/repository.rs index 04fe8d1cc..909552e33 100644 --- a/src/manifest/npm/repository.rs +++ b/src/manifest/npm/repository.rs @@ -1,4 +1,4 @@ -#[derive(Serialize)] +#[derive(Deserialize, Serialize)] pub struct Repository { #[serde(rename = "type")] pub ty: String, diff --git a/tests/all/manifest.rs b/tests/all/manifest.rs index 1f202a209..27916ddc0 100644 --- a/tests/all/manifest.rs +++ b/tests/all/manifest.rs @@ -79,7 +79,7 @@ fn it_creates_a_package_json_default_path() { let crate_data = manifest::CrateData::new(&fixture.path, None).unwrap(); wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap(); assert!(crate_data - .write_package_json(&out_dir, &None, false, Target::Bundler) + .write_package_json(&out_dir, &None, false, Target::Bundler, false) .is_ok()); let package_json_path = &fixture.path.join("pkg").join("package.json"); fs::metadata(package_json_path).unwrap(); @@ -114,7 +114,7 @@ fn it_creates_a_package_json_provided_path() { let crate_data = manifest::CrateData::new(&fixture.path, None).unwrap(); wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap(); assert!(crate_data - .write_package_json(&out_dir, &None, false, Target::Bundler) + .write_package_json(&out_dir, &None, false, Target::Bundler, false) .is_ok()); let package_json_path = &fixture.path.join("pkg").join("package.json"); fs::metadata(package_json_path).unwrap(); @@ -142,7 +142,13 @@ fn it_creates_a_package_json_provided_path_with_scope() { let crate_data = manifest::CrateData::new(&fixture.path, None).unwrap(); wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap(); assert!(crate_data - .write_package_json(&out_dir, &Some("test".to_string()), false, Target::Bundler,) + .write_package_json( + &out_dir, + &Some("test".to_string()), + false, + Target::Bundler, + false + ) .is_ok()); let package_json_path = &fixture.path.join("pkg").join("package.json"); fs::metadata(package_json_path).unwrap(); @@ -170,7 +176,7 @@ fn it_creates_a_pkg_json_with_correct_files_on_node() { let crate_data = manifest::CrateData::new(&fixture.path, None).unwrap(); wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap(); assert!(crate_data - .write_package_json(&out_dir, &None, false, Target::Nodejs) + .write_package_json(&out_dir, &None, false, Target::Nodejs, false) .is_ok()); let package_json_path = &out_dir.join("package.json"); fs::metadata(package_json_path).unwrap(); @@ -205,7 +211,7 @@ fn it_creates_a_pkg_json_with_correct_files_on_nomodules() { let crate_data = manifest::CrateData::new(&fixture.path, None).unwrap(); wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap(); assert!(crate_data - .write_package_json(&out_dir, &None, false, Target::NoModules) + .write_package_json(&out_dir, &None, false, Target::NoModules, false) .is_ok()); let package_json_path = &out_dir.join("package.json"); fs::metadata(package_json_path).unwrap(); @@ -239,7 +245,7 @@ fn it_creates_a_package_json_with_correct_files_when_out_name_is_provided() { let crate_data = manifest::CrateData::new(&fixture.path, Some("index".to_owned())).unwrap(); wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap(); assert!(crate_data - .write_package_json(&out_dir, &None, false, Target::Bundler) + .write_package_json(&out_dir, &None, false, Target::Bundler, false) .is_ok()); let package_json_path = &fixture.path.join("pkg").join("package.json"); fs::metadata(package_json_path).unwrap(); @@ -270,7 +276,7 @@ fn it_creates_a_pkg_json_in_out_dir() { let crate_data = manifest::CrateData::new(&fixture.path, None).unwrap(); wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap(); assert!(crate_data - .write_package_json(&out_dir, &None, false, Target::Bundler) + .write_package_json(&out_dir, &None, false, Target::Bundler, false) .is_ok()); let package_json_path = &fixture.path.join(&out_dir).join("package.json"); @@ -285,7 +291,7 @@ fn it_creates_a_package_json_with_correct_keys_when_types_are_skipped() { let crate_data = manifest::CrateData::new(&fixture.path, None).unwrap(); wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap(); assert!(crate_data - .write_package_json(&out_dir, &None, true, Target::Bundler) + .write_package_json(&out_dir, &None, true, Target::Bundler, false) .is_ok()); let package_json_path = &out_dir.join("package.json"); fs::metadata(package_json_path).unwrap(); @@ -307,6 +313,49 @@ fn it_creates_a_package_json_with_correct_keys_when_types_are_skipped() { assert_eq!(actual_files, expected_files); } +#[test] +fn it_creates_a_package_json_with_correct_files_when_is_child_is_provided() { + let fixture = fixture::js_hello_world(); + let out_dir = fixture.path.join("pkg"); + let crate_data = manifest::CrateData::new(&fixture.path, Some("index".to_owned())).unwrap(); + let crate_data_child = + manifest::CrateData::new(&fixture.path, Some("child".to_owned())).unwrap(); + wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap(); + assert!(crate_data + .write_package_json(&out_dir, &None, false, Target::Web, false) + .is_ok()); + assert!(crate_data_child + .write_package_json(&out_dir, &None, false, Target::Web, true) + .is_ok()); + let package_json_path = &fixture.path.join("pkg").join("package.json"); + fs::metadata(package_json_path).unwrap(); + utils::manifest::read_package_json(&fixture.path, &out_dir).unwrap(); + let pkg = utils::manifest::read_package_json(&fixture.path, &out_dir).unwrap(); + assert_eq!(pkg.name, "js-hello-world"); + assert_eq!(pkg.repository.ty, "git"); + assert_eq!( + pkg.repository.url, + "https://github.com/rustwasm/wasm-pack.git" + ); + assert_eq!(pkg.module, "index.js"); + assert_eq!(pkg.types, "index.d.ts"); + assert_eq!(pkg.side_effects, false); + + let actual_files: HashSet = pkg.files.into_iter().collect(); + let expected_files: HashSet = [ + "index_bg.wasm", + "index.d.ts", + "index.js", + "child_bg.wasm", + "child.d.ts", + "child.js", + ] + .iter() + .map(|&s| String::from(s)) + .collect(); + assert_eq!(actual_files, expected_files); +} + #[test] fn it_errors_when_wasm_bindgen_is_not_declared() { let fixture = fixture::bad_cargo_toml(); @@ -314,6 +363,23 @@ fn it_errors_when_wasm_bindgen_is_not_declared() { assert!(crate_data.check_crate_config().is_err()); } +#[test] +fn it_errors_when_out_dir_of_child_does_not_exist() { + let fixture = fixture::js_hello_world(); + let out_dir = fixture.path.join("pkg"); + let out_dir_child = fixture.path.join("pkg-child"); + let crate_data = manifest::CrateData::new(&fixture.path, Some("index".to_owned())).unwrap(); + let crate_data_child = + manifest::CrateData::new(&fixture.path, Some("child".to_owned())).unwrap(); + wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap(); + assert!(crate_data + .write_package_json(&out_dir, &None, false, Target::Web, false) + .is_ok()); + assert!(crate_data_child + .write_package_json(&out_dir_child, &None, false, Target::Web, true) + .is_err()); +} + #[test] fn it_sets_homepage_field_if_available_in_cargo_toml() { // When 'homepage' is available @@ -346,7 +412,7 @@ fn it_sets_homepage_field_if_available_in_cargo_toml() { wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap(); crate_data - .write_package_json(&out_dir, &None, true, Target::Bundler) + .write_package_json(&out_dir, &None, true, Target::Bundler, false) .unwrap(); let pkg = utils::manifest::read_package_json(&fixture.path, &out_dir).unwrap(); @@ -362,7 +428,7 @@ fn it_sets_homepage_field_if_available_in_cargo_toml() { wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap(); crate_data - .write_package_json(&out_dir, &None, true, Target::Bundler) + .write_package_json(&out_dir, &None, true, Target::Bundler, false) .unwrap(); let pkg = utils::manifest::read_package_json(&fixture.path, &out_dir).unwrap(); @@ -463,7 +529,7 @@ fn it_lists_license_files_in_files_field_of_package_json() { wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap(); license::copy_from_crate(&crate_data, &fixture.path, &out_dir).unwrap(); crate_data - .write_package_json(&out_dir, &None, false, Target::Bundler) + .write_package_json(&out_dir, &None, false, Target::Bundler, false) .unwrap(); let package_json_path = &fixture.path.join("pkg").join("package.json");