Skip to content

Commit 44f9a89

Browse files
committed
feat(build): multiple build targets per package
is-child can be passed as argument to wasm-pack build for generating packages with multiple build targets
1 parent eceb5df commit 44f9a89

File tree

9 files changed

+160
-23
lines changed

9 files changed

+160
-23
lines changed

docs/src/commands/build.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,23 @@ wasm-pack build examples/js-hello-world --mode no-install
125125
| `no-install` | `wasm-pack init` implicitly and create wasm binding without installing `wasm-bindgen`. |
126126
| `normal` | do all the stuffs of `no-install` with installed `wasm-bindgen`. |
127127

128+
## Multiple build targets per package
129+
130+
The `build` command accepts an optional `--is-child` argument.
131+
```
132+
wasm-pack build examples/js-hello-world --is-child --out-name child
133+
```
134+
135+
This command will extend a previously generated package, which makes it possible to have multiple build targets per package.
136+
For example it would be feasible to have an optimized build for web and Node.
137+
138+
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.
139+
140+
```
141+
wasm-pack build examples/js-hello-world --out-dir pkg -- --features "wee_alloc"
142+
wasm-pack build examples/js-hello-world --out-dir pkg --out-name js-hello-node --is-child
143+
```
144+
128145
## Extra options
129146

130147
The `build` command can pass extra options straight to `cargo build` even if they are not

src/command/build.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub struct Build {
3232
pub mode: InstallMode,
3333
pub out_dir: PathBuf,
3434
pub out_name: Option<String>,
35+
pub is_child: bool,
3536
pub bindgen: Option<install::Status>,
3637
pub cache: Cache,
3738
pub extra_options: Vec<String>,
@@ -148,6 +149,10 @@ pub struct BuildOptions {
148149
/// Sets the output file names. Defaults to package name.
149150
pub out_name: Option<String>,
150151

152+
#[structopt(long = "is-child")]
153+
/// Won't generate package metadata. Instead adds output to existing package given by `out-dir`.
154+
pub is_child: bool,
155+
151156
#[structopt(last = true)]
152157
/// List of extra options to pass to `cargo build`
153158
pub extra_options: Vec<String>,
@@ -167,6 +172,7 @@ impl Default for BuildOptions {
167172
profiling: false,
168173
out_dir: String::new(),
169174
out_name: None,
175+
is_child: false,
170176
extra_options: Vec::new(),
171177
}
172178
}
@@ -201,6 +207,7 @@ impl Build {
201207
mode: build_opts.mode,
202208
out_dir,
203209
out_name: build_opts.out_name,
210+
is_child: build_opts.is_child,
204211
bindgen: None,
205212
cache: cache::get_wasm_pack_cache()?,
206213
extra_options: build_opts.extra_options,
@@ -324,6 +331,7 @@ impl Build {
324331
&self.scope,
325332
self.disable_dts,
326333
self.target,
334+
self.is_child,
327335
)?;
328336
info!(
329337
"Wrote a package.json at {:#?}.",
@@ -333,13 +341,19 @@ impl Build {
333341
}
334342

335343
fn step_copy_readme(&mut self) -> Result<(), Error> {
344+
if self.is_child {
345+
return Ok(());
346+
}
336347
info!("Copying readme from crate...");
337348
readme::copy_from_crate(&self.crate_path, &self.out_dir)?;
338349
info!("Copied readme from crate to {:#?}.", &self.out_dir);
339350
Ok(())
340351
}
341352

342353
fn step_copy_license(&mut self) -> Result<(), failure::Error> {
354+
if self.is_child {
355+
return Ok(());
356+
}
343357
info!("Copying license from crate...");
344358
license::copy_from_crate(&self.crate_data, &self.crate_path, &self.out_dir)?;
345359
info!("Copied license from crate to {:#?}.", &self.out_dir);

src/manifest/mod.rs

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -573,13 +573,43 @@ impl CrateData {
573573
scope: &Option<String>,
574574
disable_dts: bool,
575575
target: Target,
576+
is_child: bool,
576577
) -> Result<(), Error> {
577578
let pkg_file_path = out_dir.join("package.json");
578-
let npm_data = match target {
579-
Target::Nodejs => self.to_commonjs(scope, disable_dts, out_dir),
580-
Target::NoModules => self.to_nomodules(scope, disable_dts, out_dir),
581-
Target::Bundler => self.to_esmodules(scope, disable_dts, out_dir),
582-
Target::Web => self.to_web(scope, disable_dts, out_dir),
579+
let npm_data = if is_child {
580+
let npm_json = match fs::read_to_string(&pkg_file_path) {
581+
Ok(file) => file,
582+
Err(_) => bail!(
583+
"--is-child was provided, but given --out-dir path does not contain a package.json. \
584+
Please make sure, that you compile the parent directory to the same --out-dir"),
585+
};
586+
let mut npm_data: NpmPackage = serde_json::from_str(&npm_json)?;
587+
let name_prefix = self.name_prefix();
588+
589+
let wasm_file = format!("{}_bg.wasm", name_prefix);
590+
npm_data.add_file(wasm_file);
591+
592+
let js_file = format!("{}.js", name_prefix);
593+
npm_data.add_file(js_file);
594+
595+
if let Target::Nodejs = target {
596+
let js_bg_file = format!("{}_bg.js", name_prefix);
597+
npm_data.add_file(js_bg_file);
598+
}
599+
600+
if !disable_dts {
601+
let dts_file = format!("{}.d.ts", name_prefix);
602+
npm_data.add_file(dts_file);
603+
}
604+
605+
npm_data
606+
} else {
607+
match target {
608+
Target::Nodejs => self.to_commonjs(scope, disable_dts, out_dir),
609+
Target::NoModules => self.to_nomodules(scope, disable_dts, out_dir),
610+
Target::Bundler => self.to_esmodules(scope, disable_dts, out_dir),
611+
Target::Web => self.to_web(scope, disable_dts, out_dir),
612+
}
583613
};
584614

585615
let npm_json = serde_json::to_string_pretty(&npm_data)?;

src/manifest/npm/commonjs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use manifest::npm::repository::Repository;
22

3-
#[derive(Serialize)]
3+
#[derive(Deserialize, Serialize)]
44
pub struct CommonJSPackage {
55
pub name: String,
66
#[serde(skip_serializing_if = "Vec::is_empty")]

src/manifest/npm/esmodules.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use manifest::npm::repository::Repository;
22

3-
#[derive(Serialize)]
3+
#[derive(Deserialize, Serialize)]
44
pub struct ESModulesPackage {
55
pub name: String,
66
#[serde(skip_serializing_if = "Vec::is_empty")]

src/manifest/npm/mod.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,20 @@ pub use self::commonjs::CommonJSPackage;
77
pub use self::esmodules::ESModulesPackage;
88
pub use self::nomodules::NoModulesPackage;
99

10-
#[derive(Serialize)]
10+
#[derive(Deserialize, Serialize)]
1111
#[serde(untagged)]
1212
pub enum NpmPackage {
1313
CommonJSPackage(CommonJSPackage),
1414
ESModulesPackage(ESModulesPackage),
1515
NoModulesPackage(NoModulesPackage),
1616
}
17+
18+
impl NpmPackage {
19+
pub fn add_file(&mut self, file: String) {
20+
match self {
21+
Self::CommonJSPackage(pkg) => pkg.files.push(file),
22+
Self::ESModulesPackage(pkg) => pkg.files.push(file),
23+
Self::NoModulesPackage(pkg) => pkg.files.push(file),
24+
}
25+
}
26+
}

src/manifest/npm/nomodules.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use manifest::npm::repository::Repository;
22

3-
#[derive(Serialize)]
3+
#[derive(Deserialize, Serialize)]
44
pub struct NoModulesPackage {
55
pub name: String,
66
#[serde(skip_serializing_if = "Vec::is_empty")]

src/manifest/npm/repository.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#[derive(Serialize)]
1+
#[derive(Deserialize, Serialize)]
22
pub struct Repository {
33
#[serde(rename = "type")]
44
pub ty: String,

tests/all/manifest.rs

Lines changed: 79 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ fn it_creates_a_package_json_default_path() {
7979
let crate_data = manifest::CrateData::new(&fixture.path, None).unwrap();
8080
wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap();
8181
assert!(crate_data
82-
.write_package_json(&out_dir, &None, false, Target::Bundler)
82+
.write_package_json(&out_dir, &None, false, Target::Bundler, false)
8383
.is_ok());
8484
let package_json_path = &fixture.path.join("pkg").join("package.json");
8585
fs::metadata(package_json_path).unwrap();
@@ -115,7 +115,7 @@ fn it_creates_a_package_json_provided_path() {
115115
let crate_data = manifest::CrateData::new(&fixture.path, None).unwrap();
116116
wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap();
117117
assert!(crate_data
118-
.write_package_json(&out_dir, &None, false, Target::Bundler)
118+
.write_package_json(&out_dir, &None, false, Target::Bundler, false)
119119
.is_ok());
120120
let package_json_path = &fixture.path.join("pkg").join("package.json");
121121
fs::metadata(package_json_path).unwrap();
@@ -144,7 +144,13 @@ fn it_creates_a_package_json_provided_path_with_scope() {
144144
let crate_data = manifest::CrateData::new(&fixture.path, None).unwrap();
145145
wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap();
146146
assert!(crate_data
147-
.write_package_json(&out_dir, &Some("test".to_string()), false, Target::Bundler,)
147+
.write_package_json(
148+
&out_dir,
149+
&Some("test".to_string()),
150+
false,
151+
Target::Bundler,
152+
false
153+
)
148154
.is_ok());
149155
let package_json_path = &fixture.path.join("pkg").join("package.json");
150156
fs::metadata(package_json_path).unwrap();
@@ -173,7 +179,7 @@ fn it_creates_a_pkg_json_with_correct_files_on_node() {
173179
let crate_data = manifest::CrateData::new(&fixture.path, None).unwrap();
174180
wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap();
175181
assert!(crate_data
176-
.write_package_json(&out_dir, &None, false, Target::Nodejs)
182+
.write_package_json(&out_dir, &None, false, Target::Nodejs, false)
177183
.is_ok());
178184
let package_json_path = &out_dir.join("package.json");
179185
fs::metadata(package_json_path).unwrap();
@@ -207,7 +213,7 @@ fn it_creates_a_pkg_json_with_correct_files_on_nomodules() {
207213
let crate_data = manifest::CrateData::new(&fixture.path, None).unwrap();
208214
wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap();
209215
assert!(crate_data
210-
.write_package_json(&out_dir, &None, false, Target::NoModules)
216+
.write_package_json(&out_dir, &None, false, Target::NoModules, false)
211217
.is_ok());
212218
let package_json_path = &out_dir.join("package.json");
213219
fs::metadata(package_json_path).unwrap();
@@ -241,7 +247,7 @@ fn it_creates_a_package_json_with_correct_files_when_out_name_is_provided() {
241247
let crate_data = manifest::CrateData::new(&fixture.path, Some("index".to_owned())).unwrap();
242248
wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap();
243249
assert!(crate_data
244-
.write_package_json(&out_dir, &None, false, Target::Bundler)
250+
.write_package_json(&out_dir, &None, false, Target::Bundler, false)
245251
.is_ok());
246252
let package_json_path = &fixture.path.join("pkg").join("package.json");
247253
fs::metadata(package_json_path).unwrap();
@@ -273,7 +279,7 @@ fn it_creates_a_pkg_json_in_out_dir() {
273279
let crate_data = manifest::CrateData::new(&fixture.path, None).unwrap();
274280
wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap();
275281
assert!(crate_data
276-
.write_package_json(&out_dir, &None, false, Target::Bundler)
282+
.write_package_json(&out_dir, &None, false, Target::Bundler, false)
277283
.is_ok());
278284

279285
let package_json_path = &fixture.path.join(&out_dir).join("package.json");
@@ -288,7 +294,7 @@ fn it_creates_a_package_json_with_correct_keys_when_types_are_skipped() {
288294
let crate_data = manifest::CrateData::new(&fixture.path, None).unwrap();
289295
wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap();
290296
assert!(crate_data
291-
.write_package_json(&out_dir, &None, true, Target::Bundler)
297+
.write_package_json(&out_dir, &None, true, Target::Bundler, false)
292298
.is_ok());
293299
let package_json_path = &out_dir.join("package.json");
294300
fs::metadata(package_json_path).unwrap();
@@ -314,13 +320,73 @@ fn it_creates_a_package_json_with_correct_keys_when_types_are_skipped() {
314320
assert_eq!(actual_files, expected_files);
315321
}
316322

323+
#[test]
324+
fn it_creates_a_package_json_with_correct_files_when_is_child_is_provided() {
325+
let fixture = fixture::js_hello_world();
326+
let out_dir = fixture.path.join("pkg");
327+
let crate_data = manifest::CrateData::new(&fixture.path, Some("index".to_owned())).unwrap();
328+
let crate_data_child =
329+
manifest::CrateData::new(&fixture.path, Some("child".to_owned())).unwrap();
330+
wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap();
331+
assert!(crate_data
332+
.write_package_json(&out_dir, &None, false, Target::Web, false)
333+
.is_ok());
334+
assert!(crate_data_child
335+
.write_package_json(&out_dir, &None, false, Target::Web, true)
336+
.is_ok());
337+
let package_json_path = &fixture.path.join("pkg").join("package.json");
338+
fs::metadata(package_json_path).unwrap();
339+
utils::manifest::read_package_json(&fixture.path, &out_dir).unwrap();
340+
let pkg = utils::manifest::read_package_json(&fixture.path, &out_dir).unwrap();
341+
assert_eq!(pkg.name, "js-hello-world");
342+
assert_eq!(pkg.repository.ty, "git");
343+
assert_eq!(
344+
pkg.repository.url,
345+
"https://github.com/rustwasm/wasm-pack.git"
346+
);
347+
assert_eq!(pkg.module, "index.js");
348+
assert_eq!(pkg.types, "index.d.ts");
349+
assert_eq!(pkg.side_effects, false);
350+
351+
let actual_files: HashSet<String> = pkg.files.into_iter().collect();
352+
let expected_files: HashSet<String> = [
353+
"index_bg.wasm",
354+
"index.d.ts",
355+
"index.js",
356+
"child_bg.wasm",
357+
"child.d.ts",
358+
"child.js",
359+
]
360+
.iter()
361+
.map(|&s| String::from(s))
362+
.collect();
363+
assert_eq!(actual_files, expected_files);
364+
}
365+
317366
#[test]
318367
fn it_errors_when_wasm_bindgen_is_not_declared() {
319368
let fixture = fixture::bad_cargo_toml();
320369
let crate_data = manifest::CrateData::new(&fixture.path, None).unwrap();
321370
assert!(crate_data.check_crate_config().is_err());
322371
}
323372

373+
#[test]
374+
fn it_errors_when_out_dir_of_child_does_not_exist() {
375+
let fixture = fixture::js_hello_world();
376+
let out_dir = fixture.path.join("pkg");
377+
let out_dir_child = fixture.path.join("pkg-child");
378+
let crate_data = manifest::CrateData::new(&fixture.path, Some("index".to_owned())).unwrap();
379+
let crate_data_child =
380+
manifest::CrateData::new(&fixture.path, Some("child".to_owned())).unwrap();
381+
wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap();
382+
assert!(crate_data
383+
.write_package_json(&out_dir, &None, false, Target::Web, false)
384+
.is_ok());
385+
assert!(crate_data_child
386+
.write_package_json(&out_dir_child, &None, false, Target::Web, true)
387+
.is_err());
388+
}
389+
324390
#[test]
325391
fn it_sets_homepage_field_if_available_in_cargo_toml() {
326392
// When 'homepage' is available
@@ -353,7 +419,7 @@ fn it_sets_homepage_field_if_available_in_cargo_toml() {
353419

354420
wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap();
355421
crate_data
356-
.write_package_json(&out_dir, &None, true, Target::Bundler)
422+
.write_package_json(&out_dir, &None, true, Target::Bundler, false)
357423
.unwrap();
358424

359425
let pkg = utils::manifest::read_package_json(&fixture.path, &out_dir).unwrap();
@@ -369,7 +435,7 @@ fn it_sets_homepage_field_if_available_in_cargo_toml() {
369435

370436
wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap();
371437
crate_data
372-
.write_package_json(&out_dir, &None, true, Target::Bundler)
438+
.write_package_json(&out_dir, &None, true, Target::Bundler, false)
373439
.unwrap();
374440

375441
let pkg = utils::manifest::read_package_json(&fixture.path, &out_dir).unwrap();
@@ -408,7 +474,7 @@ fn it_sets_keywords_field_if_available_in_cargo_toml() {
408474

409475
wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap();
410476
crate_data
411-
.write_package_json(&out_dir, &None, true, Target::Bundler)
477+
.write_package_json(&out_dir, &None, true, Target::Bundler, false)
412478
.unwrap();
413479

414480
let pkg = utils::manifest::read_package_json(&fixture.path, &out_dir).unwrap();
@@ -426,7 +492,7 @@ fn it_sets_keywords_field_if_available_in_cargo_toml() {
426492

427493
wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap();
428494
crate_data
429-
.write_package_json(&out_dir, &None, true, Target::Bundler)
495+
.write_package_json(&out_dir, &None, true, Target::Bundler, false)
430496
.unwrap();
431497

432498
let pkg = utils::manifest::read_package_json(&fixture.path, &out_dir).unwrap();
@@ -527,7 +593,7 @@ fn it_lists_license_files_in_files_field_of_package_json() {
527593
wasm_pack::command::utils::create_pkg_dir(&out_dir).unwrap();
528594
license::copy_from_crate(&crate_data, &fixture.path, &out_dir).unwrap();
529595
crate_data
530-
.write_package_json(&out_dir, &None, false, Target::Bundler)
596+
.write_package_json(&out_dir, &None, false, Target::Bundler, false)
531597
.unwrap();
532598

533599
let package_json_path = &fixture.path.join("pkg").join("package.json");

0 commit comments

Comments
 (0)