Skip to content

Commit 7a32960

Browse files
committed
Load dependencies from wasm-bindgen
This commit updates `wasm-pack` to read the `__wasm_pack_unstable` section of wasm binaries that it exeutes over. This section is then parsed and NPM dependencies are extracted, if any, and inserted into the generated `package.json`. Closes #101
1 parent 26c8b8d commit 7a32960

14 files changed

+384
-19
lines changed

Cargo.lock

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ readme = "README.md"
1111
console = "0.6.1"
1212
failure = "0.1.1"
1313
indicatif = "0.9.0"
14+
parity-wasm = "0.28"
1415
quicli = "0.2"
1516
serde = "1.0"
1617
serde_derive = "1.0"

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ extern crate failure;
44
extern crate indicatif;
55
#[macro_use]
66
extern crate lazy_static;
7+
extern crate parity_wasm;
78
extern crate quicli;
89
#[macro_use]
910
extern crate serde_derive;

src/manifest.rs

Lines changed: 97 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
use std::collections::BTreeMap;
12
use std::fs::File;
23
use std::io::prelude::*;
4+
use std::path::Path;
35

46
use console::style;
57
use emoji;
6-
use failure::Error;
8+
use failure::{Error, ResultExt};
9+
use parity_wasm;
10+
use parity_wasm::elements::*;
711
use serde_json;
812
use toml;
913
use PBAR;
@@ -33,6 +37,7 @@ struct NpmPackage {
3337
repository: Option<Repository>,
3438
files: Vec<String>,
3539
main: String,
40+
dependencies: Option<BTreeMap<String, String>>,
3641
}
3742

3843
#[derive(Serialize)]
@@ -42,24 +47,100 @@ struct Repository {
4247
url: String,
4348
}
4449

45-
fn read_cargo_toml(path: &str) -> Result<CargoManifest, Error> {
46-
let manifest_path = format!("{}/Cargo.toml", path);
47-
let mut cargo_file = File::open(manifest_path)?;
50+
fn read_cargo_toml(path: &Path) -> Result<CargoManifest, Error> {
51+
let mut cargo_file = File::open(path.join("Cargo.toml"))?;
4852
let mut cargo_contents = String::new();
4953
cargo_file.read_to_string(&mut cargo_contents)?;
5054

5155
Ok(toml::from_str(&cargo_contents)?)
5256
}
5357

58+
/// Locates the `__wasm_pack_unstable` module section inside the wasm file
59+
/// specified, parsing it and returning dependencies found.
60+
///
61+
/// Crates compiled with `wasm-bindgen` can declare dependencies on NPM packages
62+
/// in their code and this is communicated to us, `wasm-pack`, via a custom
63+
/// section in the final binary.
64+
fn read_npm_dependencies(wasm: &Path) -> Result<BTreeMap<String, String>, Error> {
65+
#[derive(Deserialize)]
66+
#[serde(untagged)]
67+
enum Schema<'a> {
68+
V1 {
69+
version: &'a str,
70+
modules: Vec<(String, String)>,
71+
},
72+
Unknown {
73+
version: &'a str,
74+
},
75+
}
76+
77+
let mut module = parity_wasm::deserialize_file(wasm)
78+
.with_context(|_| format!("failed to parse `{}` as wasm", wasm.display()))?;
79+
let wasm_pack_module;
80+
let deps = {
81+
let result = module
82+
.sections()
83+
.iter()
84+
.enumerate()
85+
.filter_map(|(i, s)| match *s {
86+
Section::Custom(ref cs) => Some((i, cs)),
87+
_ => None,
88+
})
89+
.find(|&(_i, section)| section.name() == "__wasm_pack_unstable");
90+
let data = match result {
91+
Some((i, section)) => {
92+
wasm_pack_module = i;
93+
section.payload()
94+
}
95+
None => return Ok(BTreeMap::new()),
96+
};
97+
let schema = serde_json::from_slice(data).with_context(|_| {
98+
"the wasm file emitted by `wasm-bindgen` contains a \
99+
`__wasm_pack_unstable` section which should describe \
100+
js dependencies, but it's in a format that this \
101+
`wasm-pack` tool does not understand; does `wasm-pack` \
102+
need to be updated?"
103+
})?;
104+
let modules = match schema {
105+
Schema::V1 {
106+
version,
107+
ref modules,
108+
} if version == "0.0.1" =>
109+
{
110+
modules.clone()
111+
}
112+
Schema::Unknown { version } | Schema::V1 { version, .. } => bail!(
113+
"the wasm file emitted by `wasm-bindgen` contains a \
114+
`__wasm_pack_unstable` section which should describe \
115+
js dependencies, but it's schema version is `{}` \
116+
while this `wasm-pack` tool only understands the \
117+
schema version 0.0.1; does `wasm-pack` need to be updated?",
118+
version
119+
),
120+
};
121+
122+
modules.into_iter().collect()
123+
};
124+
125+
// Delete the `__wasm_pack_unstable` custom section and rewrite the wasm
126+
// file that we're emitting.
127+
module.sections_mut().remove(wasm_pack_module);
128+
parity_wasm::serialize_to_file(wasm, module)
129+
.with_context(|_| format!("failed to write wasm to `{}`", wasm.display()))?;
130+
131+
Ok(deps)
132+
}
133+
54134
impl CargoManifest {
55-
fn into_npm(mut self, scope: Option<String>) -> NpmPackage {
135+
fn into_npm(mut self, pkg: &Path, scope: Option<String>) -> Result<NpmPackage, Error> {
56136
let filename = self.package.name.replace("-", "_");
57137
let wasm_file = format!("{}_bg.wasm", filename);
58138
let js_file = format!("{}.js", filename);
139+
let dependencies = read_npm_dependencies(&pkg.join(&wasm_file))?;
59140
if let Some(s) = scope {
60141
self.package.name = format!("@{}/{}", s, self.package.name);
61142
}
62-
NpmPackage {
143+
Ok(NpmPackage {
63144
name: self.package.name,
64145
collaborators: self.package.authors,
65146
description: self.package.description,
@@ -71,12 +152,18 @@ impl CargoManifest {
71152
}),
72153
files: vec![wasm_file],
73154
main: js_file,
74-
}
155+
dependencies: if dependencies.len() == 0 {
156+
None
157+
} else {
158+
Some(dependencies)
159+
},
160+
})
75161
}
76162
}
77163

78164
/// Generate a package.json file inside in `./pkg`.
79165
pub fn write_package_json(path: &str, scope: Option<String>) -> Result<(), Error> {
166+
let path = Path::new(path);
80167
let step = format!(
81168
"{} {}Writing a package.json...",
82169
style("[4/7]").bold().dim(),
@@ -91,10 +178,10 @@ pub fn write_package_json(path: &str, scope: Option<String>) -> Result<(), Error
91178
};
92179

93180
let pb = PBAR.message(&step);
94-
let pkg_file_path = format!("{}/pkg/package.json", path);
181+
let pkg_file_path = path.join("pkg/package.json");
95182
let mut pkg_file = File::create(pkg_file_path)?;
96183
let crate_data = read_cargo_toml(path)?;
97-
let npm_data = crate_data.into_npm(scope);
184+
let npm_data = crate_data.into_npm(&path.join("pkg"), scope)?;
98185

99186
if npm_data.description.is_none() {
100187
PBAR.warn(&warn_fmt("description"));
@@ -113,5 +200,5 @@ pub fn write_package_json(path: &str, scope: Option<String>) -> Result<(), Error
113200
}
114201

115202
pub fn get_crate_name(path: &str) -> Result<String, Error> {
116-
Ok(read_cargo_toml(path)?.package.name)
203+
Ok(read_cargo_toml(Path::new(path))?.package.name)
117204
}

0 commit comments

Comments
 (0)