Repository: ashleygwilliams/wasm-pack (rust)

Issue:
--scope flag on init command
@mgattozzi brought this up while working on the tutorial. we should support namespaces (which are called scopes in npm land)! this way people taking the tutorial won't all attempt to publish a pkg with the same name.

`wasm-pack init --scope MYSCOPE` should write to the package.json:

```json
{
   "name": "@MYSCOPE/cratename"
}
```

Pull request:
mgattozzi scopes rollup
fixes #45 

Patch:
diff --git a/README.md b/README.md
index 612a98d..2780d29 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,16 @@ this project is written in rust. [get rust] to work on this project.
 - `help`: display available commands
 - 🐣  `init`: create necessary files for js interop and npm publishing
   - optionally pass a path to a dir that contains a `Cargo.toml`, e.g.:
-    `wasm-pack init examples/js-hello-world`
+    ```
+    wasm-pack init examples/js-hello-world
+    ```
+  - optionally pass a scope name to generate a `package.json` for a scoped pkg, e.g.:
+    ```
+    wasm-pack init examples/scopes-hello-world --scope test
+    ```
+    generates a `package.json` for an npm package called `@test/scopes-hello-world`
+
+#### to be implemented
 - 🍱  `pack`: create a tarball but don't push to the npm registry [NOT IMPLEMENTED]
 - 🎆  `publish`: create a tarball and publish to the npm registry [NOT IMPLEMENTED]
 
@@ -72,7 +81,7 @@ this project is written in rust. [get rust] to work on this project.
   ```
 
 5. install this tool: `cargo install wasm-pack`
-6. run `wasm-pack init`, optionally, pass a path to a dir that contains your `Cargo.toml`
+6. run `wasm-pack init`, optionally, pass a path to a dir or a scope (see above for details)
 7. this tool generates files in a `pkg` dir. to publish to npm, `cd pkg` and then `npm publish` 
   (in the future you'll be able to use this tool to publish)
 
diff --git a/src/main.rs b/src/main.rs
index b4dd5eb..9afeabe 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -24,7 +24,11 @@ struct Cli {
 enum Command {
     #[structopt(name = "init")]
     /// 🐣  initialize a package.json based on your compiled wasm
-    Init { path: Option<String> },
+    Init {
+        path: Option<String>,
+        #[structopt(long = "scope", short = "s")]
+        scope: Option<String>,
+    },
     #[structopt(name = "pack")]
     /// 🍱  create a tar of your npm package but don't publish! [NOT IMPLEMENTED]
     Pack {},
@@ -34,7 +38,7 @@ enum Command {
 }
 
 main!(|args: Cli, log_level: verbosity| match args.cmd {
-    Command::Init { path } => {
+    Command::Init { path, scope } => {
         let started = Instant::now();
 
         let crate_path = match path {
@@ -45,7 +49,7 @@ main!(|args: Cli, log_level: verbosity| match args.cmd {
         build::rustup_add_wasm_target();
         build::cargo_build_wasm(&crate_path);
         wasm_pack::create_pkg_dir(&crate_path)?;
-        manifest::write_package_json(&crate_path)?;
+        manifest::write_package_json(&crate_path, scope)?;
         readme::copy_from_crate(&crate_path)?;
         bindgen::cargo_install_wasm_bindgen();
         let name = manifest::get_crate_name(&crate_path)?;
diff --git a/src/manifest.rs b/src/manifest.rs
index 99d36e1..da5308d 100644
--- a/src/manifest.rs
+++ b/src/manifest.rs
@@ -49,10 +49,13 @@ fn read_cargo_toml(path: &str) -> Result<CargoManifest, Error> {
 }
 
 impl CargoManifest {
-    fn into_npm(self) -> NpmPackage {
+    fn into_npm(mut self, scope: Option<String>) -> NpmPackage {
         let filename = self.package.name.replace("-", "_");
         let js_file = format!("{}.js", filename);
         let wasm_file = format!("{}_bg.wasm", filename);
+        if let Some(s) = scope {
+            self.package.name = format!("@{}/{}", s, self.package.name);
+        }
         NpmPackage {
             name: self.package.name,
             description: self.package.description,
@@ -68,7 +71,7 @@ impl CargoManifest {
 }
 
 /// Generate a package.json file inside in `./pkg`.
-pub fn write_package_json(path: &str) -> Result<(), Error> {
+pub fn write_package_json(path: &str, scope: Option<String>) -> Result<(), Error> {
     let step = format!(
         "{} {}Writing a package.json...",
         style("[4/7]").bold().dim(),
@@ -78,7 +81,7 @@ pub fn write_package_json(path: &str) -> Result<(), Error> {
     let pkg_file_path = format!("{}/pkg/package.json", path);
     let mut pkg_file = File::create(pkg_file_path)?;
     let crate_data = read_cargo_toml(path)?;
-    let npm_data = crate_data.into_npm();
+    let npm_data = crate_data.into_npm(scope);
     let npm_json = serde_json::to_string(&npm_data)?;
     pkg_file.write_all(npm_json.as_bytes())?;
     pb.finish();


Test patch:
diff --git a/tests/fixtures/scopes/Cargo.lock b/tests/fixtures/scopes/Cargo.lock
new file mode 100644
index 0000000..95ea282
--- /dev/null
+++ b/tests/fixtures/scopes/Cargo.lock
@@ -0,0 +1,140 @@
+[[package]]
+name = "dtoa"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "fnv"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "itoa"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "num-traits"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "proc-macro2"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "quote"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "scopes-hello-world"
+version = "0.1.0"
+dependencies = [
+ "wasm-bindgen 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "serde_derive"
+version = "1.0.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive_internals 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde_derive_internals"
+version = "0.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "syn"
+version = "0.12.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-xid"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "wasm-bindgen-macro 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasm-bindgen-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[metadata]
+"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
+"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
+"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
+"checksum num-traits 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3c2bd9b9d21e48e956b763c9f37134dc62d9e95da6edb3f672cacb6caf3cd3"
+"checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0"
+"checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408"
+"checksum serde 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "c73f63e08b33f6e59dfb3365b009897ebc3a3edc4af6e4f3ce8e483cf3d80ce7"
+"checksum serde_derive 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9cd9e89b8be5b611971734eaf887f1da0ce1a5b51491f04b09fe855649a84f3b"
+"checksum serde_derive_internals 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a79b781fe5c4a7037a10a485249a499ea02927046360afe7e04885aad2f9c10c"
+"checksum serde_json 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "fab6c4d75bedcf880711c85e39ebf8ccc70d0eba259899047ec5d7436643ee17"
+"checksum syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8c5bc2d6ff27891209efa5f63e9de78648d7801f085e4653701a692ce938d6fd"
+"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
+"checksum wasm-bindgen 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "717507a3c7ef09d740c6d781bda57ba054d7b776f3e27d4478e25a773afd2e4b"
+"checksum wasm-bindgen-macro 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbc1a194a8fe6319f9f326f0c03d2083d61727ace574f9b1df083bad79a314ad"
+"checksum wasm-bindgen-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8b7cf730a826f1ec1ea2143d8582d5d2d27c0775035652daf0e0337225e8566b"
diff --git a/tests/fixtures/scopes/Cargo.toml b/tests/fixtures/scopes/Cargo.toml
new file mode 100644
index 0000000..d70f9b6
--- /dev/null
+++ b/tests/fixtures/scopes/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "scopes-hello-world"
+description = "an example rust->wasm crate"
+version = "0.1.0"
+authors = ["Michael Gattozzi <mgattozzi@gmail.com>"]
+license = "WTFPL"
+repository = "https://github.com/ashleygwilliams/wasm-pack"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+wasm-bindgen = "0.1.0"
diff --git a/tests/fixtures/scopes/README.md b/tests/fixtures/scopes/README.md
new file mode 100644
index 0000000..97b39af
--- /dev/null
+++ b/tests/fixtures/scopes/README.md
@@ -0,0 +1,2 @@
+# scopes-hello-world
+> an example rust -> wasm project
diff --git a/tests/fixtures/scopes/src/lib.rs b/tests/fixtures/scopes/src/lib.rs
new file mode 100644
index 0000000..d4fb8e9
--- /dev/null
+++ b/tests/fixtures/scopes/src/lib.rs
@@ -0,0 +1,15 @@
+#![feature(proc_macro)]
+
+extern crate wasm_bindgen;
+
+use wasm_bindgen::prelude::*;
+
+#[wasm_bindgen]
+extern {
+    fn alert(s: &str);
+}
+
+#[wasm_bindgen]
+pub fn greet(name: &str) {
+    alert(&format!("Hello, {}!", name));
+}
diff --git a/tests/manifest/main.rs b/tests/manifest/main.rs
index 711c445..22a2f16 100644
--- a/tests/manifest/main.rs
+++ b/tests/manifest/main.rs
@@ -29,7 +29,7 @@ fn it_gets_the_crate_name_provided_path() {
 fn it_creates_a_package_json_default_path() {
     let path = ".".to_string();
     wasm_pack::create_pkg_dir(&path).unwrap();
-    assert!(manifest::write_package_json(&path).is_ok());
+    assert!(manifest::write_package_json(&path, None).is_ok());
     let package_json_path = format!("{}/pkg/package.json", &path);
     assert!(fs::metadata(package_json_path).is_ok());
     assert!(utils::read_package_json(&path).is_ok());
@@ -47,7 +47,7 @@ fn it_creates_a_package_json_default_path() {
 fn it_creates_a_package_json_provided_path() {
     let path = "tests/fixtures/js-hello-world".to_string();
     wasm_pack::create_pkg_dir(&path).unwrap();
-    assert!(manifest::write_package_json(&path).is_ok());
+    assert!(manifest::write_package_json(&path, None).is_ok());
     let package_json_path = format!("{}/pkg/package.json", &path);
     assert!(fs::metadata(package_json_path).is_ok());
     assert!(utils::read_package_json(&path).is_ok());
@@ -55,3 +55,19 @@ fn it_creates_a_package_json_provided_path() {
     assert_eq!(pkg.name, "js-hello-world");
     assert_eq!(pkg.files, ["js_hello_world.js", "js_hello_world_bg.wasm"]);
 }
+
+#[test]
+fn it_creates_a_package_json_provided_path_with_scope() {
+    let path = "tests/fixtures/scopes".to_string();
+    wasm_pack::create_pkg_dir(&path).unwrap();
+    assert!(manifest::write_package_json(&path, Some("test".to_string())).is_ok());
+    let package_json_path = format!("{}/pkg/package.json", &path);
+    assert!(fs::metadata(package_json_path).is_ok());
+    assert!(utils::read_package_json(&path).is_ok());
+    let pkg = utils::read_package_json(&path).unwrap();
+    assert_eq!(pkg.name, "@test/scopes-hello-world");
+    assert_eq!(
+        pkg.files,
+        ["scopes_hello_world.js", "scopes_hello_world_bg.wasm"]
+    );
+}