Skip to content

Commit a7ce6bf

Browse files
feat(rfcs): add expressing npm deps rfc
1 parent a46b3f2 commit a7ce6bf

File tree

1 file changed

+117
-0
lines changed

1 file changed

+117
-0
lines changed

text/003-expressing-npm-deps.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# RFC: npm Dependency Expression
2+
3+
## Summary
4+
5+
Allow for the expression of npm dependencies in Rust-generated WebAssembly projects that use the `wasm-pack` workflow.
6+
7+
## Motivation
8+
9+
In keeping with the team’s goal to allow the surgical replacement of JS codepaths with Rust-generated WebAssembly, developers using the `wasm-pack` workflow should be able to express dependency on packages from the npm registry, other registry, or version-control repository.
10+
11+
## Guiding Values
12+
- Development on Rust-generated WebAssembly projects should allow developers to use the development environment they are most comfortable with. Developers writing Rust should get to use Rust, and developers using JavaScript should get to use a JS based runtime environment (Node.js, Chakra, etc).
13+
- JavaScript tooling and workflows should be usable with Rust-generated WebAssembly projects. For example, bundlers like WebPack and Parcel, or dependency management tools such as `npm audit` and GreenKeeper.
14+
- When possible, decisions should be made that allow the solution to be available to developers of not just Rust, but also C, and C++.
15+
- Decisions should be focused on creating workflows that allow developers an easy learning curve and productive development experience.
16+
17+
18+
19+
## Solutions
20+
21+
Any solution to a problem like this involves 2 steps:
22+
23+
1. How to index the third-party dependencies (in this case: npm packages), and
24+
2. How to "require" or "import" the packages into code.
25+
26+
The second of these is simpler than the first so let's start with that:
27+
28+
### Requiring an npm package
29+
30+
To require an npm package in your Rust code, you will use the `wasm-bindgen`
31+
attribute, passing in `module = "name-of-package"`.
32+
33+
```rs
34+
// src/foo.rs
35+
36+
#[wasm_bindgen(module = "moment")]
37+
extern {
38+
// imported from moment.js
39+
}
40+
```
41+
42+
## Indexing the npm packages
43+
44+
This question of how to index, or even if, to index, the npm packages is a large one with
45+
several considerations. The below options were all considered. We believe that the
46+
`package.json` solution is the best, at the moment.
47+
48+
`**package.json**`
49+
50+
*This is likely the best choice. Although it requires that Rust developers use a* `*package.json*` *file, it allows the best interoperability with existing JavaScript tooling and is agnostic to target language (Rust, C, C++).*
51+
52+
Create a file called `package.json` in the root of your Rust library. Fill out dependencies as per specification: https://docs.npmjs.com/files/package.json#dependencies. You can use `npm install` to add dependencies: Although npm will warn that your `package.json` is missing metadata, it will add the dependency entry.
53+
54+
Note: All meta-data in this file will be ignored during `wasm-pack build` in favor of metadata in the `Cargo.toml`. The potential duplication of metadata is a downside to this solution.
55+
56+
Note: semver expression in `package.json` are based on npm rules. This is counter to the implicit `^` in a `Cargo.toml`. This confusion is also a downside to this solution, but is difficult to avoid in any potential solution to this problem.
57+
58+
Example:
59+
60+
```json
61+
62+
{
63+
"dependencies": {
64+
"foo" : "1.0.0 - 2.9999.9999",
65+
"bar" : ">=1.0.2 <2.1.2",
66+
"baz" : ">1.0.2 <=2.3.4",
67+
"boo" : "2.0.1",
68+
"qux" : "<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0",
69+
"asd" : "http://asdf.com/asdf.tar.gz"
70+
},
71+
"devDependencies": {
72+
"til" : "~1.2",
73+
"elf" : "~1.2.3",
74+
"two" : "2.x",
75+
"thr" : "3.3.x",
76+
"lat" : "latest",
77+
"dyl" : "file:../dyl"
78+
},
79+
"optionalDependencies": {
80+
"express": "expressjs/express",
81+
"mocha": "mochajs/mocha#4727d357ea",
82+
"module": "user/repo#feature\/branch"
83+
}
84+
}
85+
```
86+
87+
`**Cargo.toml**`
88+
89+
*Ultimately this is not a good choice because it lacks interoperability with existing JavaScript tooling, but could be considered if we anticipate that we can get tooling to use this format.*
90+
91+
To express npm dependencies, add a table to your `Cargo.toml` called `npm`. This table will have a key-value store of the dependencies you would like to use. The key is the name of the dependency followed by a value that is consistent with dependency values as specc’d in [this document](https://docs.npmjs.com/files/package.json#dependencies), many of which are demonstrated in the below example.
92+
93+
```toml
94+
# Cargo.toml
95+
96+
[package]
97+
#...
98+
99+
[dependencies]
100+
#...
101+
102+
[npm]
103+
moment = "~2.22"
104+
#sugar for moment = { version: "~2.22", type: prod }
105+
mocha = { version: "mochajs/mocha#4727d357ea", type: dev }
106+
chai = { version: "^4", type: dev }
107+
optional = { version: "6.6.6", type: optional }
108+
git = { version: "http://asdf.com/asdf.tar.gz" }
109+
```
110+
111+
**New Manifest File Format**
112+
*We rejected this outright based on inherent complexity, community exhaustion, and it’s lack of interoperability with JavaScript tooling.*
113+
114+
**Inline**
115+
*This was the original solution that was implemented. It was good because it worked equally well with Rust and other languages such as C or C++. It was not good because it added high management complexity and lacked operability with JavaScript tooling.*
116+
117+

0 commit comments

Comments
 (0)