Skip to content

Commit b4b3926

Browse files
authored
Merge pull request #1305 from alexcrichton/npm-dependencies
Implement transitive support for NPM dependencies
2 parents 433a8d8 + faf49c7 commit b4b3926

File tree

18 files changed

+432
-13
lines changed

18 files changed

+432
-13
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ members = [
6666
"examples/fetch",
6767
"examples/guide-supported-types-examples",
6868
"examples/hello_world",
69-
"examples/import_js",
69+
"examples/import_js/crate",
7070
"examples/julia_set",
7171
"examples/paint",
7272
"examples/performance",
File renamed without changes.

azure-pipelines.yml

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,17 @@ jobs:
6262
- template: ci/azure-install-sccache.yml
6363
- script: cargo test --target wasm32-unknown-unknown --features nightly --test wasm
6464

65-
- job: test_cli_support
66-
displayName: "Run wasm-bindgen-cli-support crate tests"
65+
- job: test_cli
66+
displayName: "Run wasm-bindgen-cli crate tests"
6767
steps:
6868
- template: ci/azure-install-rust.yml
6969
- template: ci/azure-install-sccache.yml
70+
- script: rustup target add wasm32-unknown-unknown
71+
displayName: "install wasm target"
7072
- script: cargo test -p wasm-bindgen-cli-support
73+
displayName: "wasm-bindgen-cli-support tests"
74+
- script: cargo test -p wasm-bindgen-cli
75+
displayName: "wasm-bindgen-cli tests"
7176

7277
- job: test_web_sys
7378
displayName: "Run web-sys crate tests"
@@ -139,16 +144,22 @@ jobs:
139144
steps:
140145
- template: ci/azure-install-rust.yml
141146
- template: ci/azure-install-sccache.yml
142-
- script: npm install
147+
- script: mv _package.json package.json && npm install && rm package.json
148+
displayName: "run npm install"
143149
- script: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f
144-
- script: cargo build -p wasm-bindgen-cli
145-
- script: ln -snf `pwd`/target/debug/wasm-bindgen $HOME/.cargo/bin/wasm-bindgen
150+
displayName: "install wasm-pack"
151+
- script: |
152+
set -ex
153+
cargo build -p wasm-bindgen-cli
154+
ln -snf `pwd`/target/debug/wasm-bindgen $HOME/.cargo/bin/wasm-bindgen
155+
displayName: "install wasm-bindgen for `wasm-pack` to use"
146156
- script: |
147157
for dir in `ls examples | grep -v README | grep -v asm.js | grep -v raytrace | grep -v without-a-bundler`; do
148158
(cd examples/$dir &&
149159
ln -fs ../../node_modules . &&
150160
npm run build -- --output-path $BUILD_ARTIFACTSTAGINGDIRECTORY/exbuild/$dir) || exit 1;
151161
done
162+
displayName: "build examples"
152163
- task: PublishPipelineArtifact@0
153164
inputs:
154165
artifactName: examples1

crates/backend/src/encode.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use proc_macro2::{Ident, Span};
2-
use std::cell::RefCell;
2+
use std::cell::{RefCell, Cell};
33
use std::collections::HashMap;
44
use std::env;
55
use std::fs;
@@ -28,6 +28,7 @@ struct Interner {
2828
files: RefCell<HashMap<String, LocalFile>>,
2929
root: PathBuf,
3030
crate_name: String,
31+
has_package_json: Cell<bool>,
3132
}
3233

3334
struct LocalFile {
@@ -43,6 +44,7 @@ impl Interner {
4344
files: RefCell::new(HashMap::new()),
4445
root: env::var_os("CARGO_MANIFEST_DIR").unwrap().into(),
4546
crate_name: env::var("CARGO_PKG_NAME").unwrap(),
47+
has_package_json: Cell::new(false),
4648
}
4749
}
4850

@@ -67,6 +69,7 @@ impl Interner {
6769
if let Some(file) = files.get(id) {
6870
return Ok(self.intern_str(&file.new_identifier))
6971
}
72+
self.check_for_package_json();
7073
let path = if id.starts_with("/") {
7174
self.root.join(&id[1..])
7275
} else if id.starts_with("./") || id.starts_with("../") {
@@ -92,6 +95,16 @@ impl Interner {
9295
fn unique_crate_identifier(&self) -> String {
9396
format!("{}-{}", self.crate_name, ShortHash(0))
9497
}
98+
99+
fn check_for_package_json(&self) {
100+
if self.has_package_json.get() {
101+
return
102+
}
103+
let path = self.root.join("package.json");
104+
if path.exists() {
105+
self.has_package_json.set(true);
106+
}
107+
}
95108
}
96109

97110
fn shared_program<'a>(
@@ -144,6 +157,11 @@ fn shared_program<'a>(
144157
.map(|js| intern.intern_str(js))
145158
.collect(),
146159
unique_crate_identifier: intern.intern_str(&intern.unique_crate_identifier()),
160+
package_json: if intern.has_package_json.get() {
161+
Some(intern.intern_str(intern.root.join("package.json").to_str().unwrap()))
162+
} else {
163+
None
164+
},
147165
})
148166
}
149167

crates/cli-support/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ base64 = "0.9"
1616
failure = "0.1.2"
1717
log = "0.4"
1818
rustc-demangle = "0.1.13"
19+
serde_json = "1.0"
1920
tempfile = "3.0"
2021
walrus = "0.5.0"
2122
wasm-bindgen-anyref-xform = { path = '../anyref-xform', version = '=0.2.40' }

crates/cli-support/src/js/mod.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::{Bindgen, EncodeInto, OutputMode};
44
use failure::{bail, Error, ResultExt};
55
use std::collections::{HashMap, HashSet, BTreeMap};
66
use std::env;
7+
use std::fs;
78
use walrus::{MemoryId, Module};
89
use wasm_bindgen_wasm_interpreter::Interpreter;
910

@@ -64,6 +65,13 @@ pub struct Context<'a> {
6465
/// the same `Program`.
6566
pub snippet_offsets: HashMap<&'a str, usize>,
6667

68+
/// All package.json dependencies we've learned about so far
69+
pub package_json_read: HashSet<&'a str>,
70+
71+
/// A map of the name of npm dependencies we've loaded so far to the path
72+
/// they're defined in as well as their version specification.
73+
pub npm_dependencies: HashMap<String, (&'a str, String)>,
74+
6775
pub anyref: wasm_bindgen_anyref_xform::Context,
6876
}
6977

@@ -2509,6 +2517,10 @@ impl<'a, 'b> SubContext<'a, 'b> {
25092517
self.cx.typescript.push_str("\n\n");
25102518
}
25112519

2520+
if let Some(path) = self.program.package_json {
2521+
self.add_package_json(path)?;
2522+
}
2523+
25122524
Ok(())
25132525
}
25142526

@@ -2980,6 +2992,66 @@ impl<'a, 'b> SubContext<'a, 'b> {
29802992
let import = self.determine_import(import, item)?;
29812993
Ok(self.cx.import_identifier(import))
29822994
}
2995+
2996+
fn add_package_json(&mut self, path: &'b str) -> Result<(), Error> {
2997+
if !self.cx.package_json_read.insert(path) {
2998+
return Ok(());
2999+
}
3000+
if !self.cx.config.mode.nodejs() && !self.cx.config.mode.bundler() {
3001+
bail!("NPM dependencies have been specified in `{}` but \
3002+
this is only compatible with the `bundler` and `nodejs` targets");
3003+
}
3004+
let contents = fs::read_to_string(path).context(format!("failed to read `{}`", path))?;
3005+
let json: serde_json::Value = serde_json::from_str(&contents)?;
3006+
let object = match json.as_object() {
3007+
Some(s) => s,
3008+
None => bail!(
3009+
"expected `package.json` to have an JSON object in `{}`",
3010+
path
3011+
),
3012+
};
3013+
let mut iter = object.iter();
3014+
let (key, value) = match iter.next() {
3015+
Some(pair) => pair,
3016+
None => return Ok(()),
3017+
};
3018+
if key != "dependencies" || iter.next().is_some() {
3019+
bail!(
3020+
"NPM manifest found at `{}` can currently only have one key, \
3021+
`dependencies`, and no other fields",
3022+
path
3023+
);
3024+
}
3025+
let value = match value.as_object() {
3026+
Some(s) => s,
3027+
None => bail!("expected `dependencies` to be a JSON object in `{}`", path),
3028+
};
3029+
3030+
for (name, value) in value.iter() {
3031+
let value = match value.as_str() {
3032+
Some(s) => s,
3033+
None => bail!(
3034+
"keys in `dependencies` are expected to be strings in `{}`",
3035+
path
3036+
),
3037+
};
3038+
if let Some((prev, _prev_version)) = self.cx.npm_dependencies.get(name) {
3039+
bail!(
3040+
"dependency on NPM package `{}` specified in two `package.json` files, \
3041+
which at the time is not allowed:\n * {}\n * {}",
3042+
name,
3043+
path,
3044+
prev
3045+
)
3046+
}
3047+
3048+
self.cx
3049+
.npm_dependencies
3050+
.insert(name.to_string(), (path, value.to_string()));
3051+
}
3052+
3053+
Ok(())
3054+
}
29833055
}
29843056

29853057
#[derive(Hash, Eq, PartialEq)]

crates/cli-support/src/lib.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![doc(html_root_url = "https://docs.rs/wasm-bindgen-cli-support/0.2")]
22

33
use failure::{bail, Error, ResultExt};
4-
use std::collections::BTreeSet;
4+
use std::collections::{BTreeSet, BTreeMap};
55
use std::env;
66
use std::fs;
77
use std::mem;
@@ -329,6 +329,8 @@ impl Bindgen {
329329
start: None,
330330
anyref: Default::default(),
331331
snippet_offsets: Default::default(),
332+
npm_dependencies: Default::default(),
333+
package_json_read: Default::default(),
332334
};
333335
cx.anyref.enabled = self.anyref;
334336
cx.anyref.prepare(cx.module)?;
@@ -366,6 +368,16 @@ impl Bindgen {
366368
.with_context(|_| format!("failed to write `{}`", path.display()))?;
367369
}
368370

371+
if cx.npm_dependencies.len() > 0 {
372+
let map = cx
373+
.npm_dependencies
374+
.iter()
375+
.map(|(k, v)| (k, &v.1))
376+
.collect::<BTreeMap<_, _>>();
377+
let json = serde_json::to_string_pretty(&map)?;
378+
fs::write(out_dir.join("package.json"), json)?;
379+
}
380+
369381
cx.finalize(stem)?
370382
};
371383

@@ -701,4 +713,11 @@ impl OutputMode {
701713
_ => false,
702714
}
703715
}
716+
717+
fn bundler(&self) -> bool {
718+
match self {
719+
OutputMode::Bundler { .. } => true,
720+
_ => false,
721+
}
722+
}
704723
}

crates/cli/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,9 @@ walrus = "0.5"
2828
wasm-bindgen-cli-support = { path = "../cli-support", version = "=0.2.40" }
2929
wasm-bindgen-shared = { path = "../shared", version = "=0.2.40" }
3030

31+
[dev-dependencies]
32+
assert_cmd = "0.11"
33+
predicates = "1.0.0"
34+
3135
[features]
3236
vendored-openssl = ['openssl/vendored']

crates/cli/src/bin/wasm-bindgen.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ fn main() {
7979
};
8080
eprintln!("error: {}", err);
8181
for cause in err.iter_causes() {
82-
eprintln!("\tcaused by: {}", cause);
82+
eprintln!(" caused by: {}", cause);
8383
}
8484
process::exit(1);
8585
}

0 commit comments

Comments
 (0)