|
| 1 | +# Dependency vendoring |
| 2 | + |
| 3 | +## Summary |
| 4 | + |
| 5 | +Provide a mechanism to vendor all dependencies for a project in a more reproducible form than adding `node_modules` to version control. |
| 6 | + |
| 7 | +## Motivation |
| 8 | + |
| 9 | +A `node_modules` directory can differ between operating systems(/npm versions?), which presents pitfalls for projects that want to ensure full reproducibility of a given dependency tree, such as [Nixpkgs](https://github.com/NixOS/nixpkgs). |
| 10 | + |
| 11 | +## Detailed Explanation |
| 12 | + |
| 13 | +This would add a new command to the npm CLI, `npm vendor`. This command results in a reproducible {archive, directory} that contains all dependencies of the given project. Additionally, a configuration value will be added. When `npm install` and `npm ci` are ran with this value set to a valid path, they will pull from the vendored copies of dependencies. |
| 14 | + |
| 15 | +## Rationale and Alternatives |
| 16 | + |
| 17 | +To my knowledge, there are two alternatives: |
| 18 | +1. Ship `node_modules`. This would work a good percent of the time, assuming `npm ci --ignore-scripts` is used, and all packages are installed across all operating systems. |
| 19 | +2. Generate a cache, and point npm to that. Given that the cacache format hasn't changed in a number of years, nor has what npm stored in cache entry metadata, I'd say this is a pretty safe bet. However, I feel uneasy about relying on it, as it could break at any time. |
| 20 | + |
| 21 | +Given the pitfalls to these alternatives, I think having an official, sanctioned vendoring mechanism is the best solution. |
| 22 | + |
| 23 | +## Implementation |
| 24 | + |
| 25 | +Pacote will need to be changed to support pulling from vendored copies of dependencies, similar to how `make-fetch-happen` pulls from cacache when possible. |
| 26 | + |
| 27 | +A command will need to be added to facilitate the generation of vendored dependencies. |
| 28 | + |
| 29 | +This will also require a new module to be created to share schemas between the two entrypoints -- unless it fits in an existing one? |
| 30 | + |
| 31 | +## Prior Art |
| 32 | + |
| 33 | +### Go's [`go mod vendor`](https://go.dev/ref/mod#go-mod-vendor) |
| 34 | + |
| 35 | +Go's dependency vendoring solution creates a directory. The contents of directory, for the same project (which means same `go.sum`, their lockfile), has changed between major Go versions. This is something we'd ideally avoid. |
| 36 | + |
| 37 | +### Cargo's [`cargo vendor`](https://doc.rust-lang.org/cargo/commands/cargo-vendor.html) |
| 38 | + |
| 39 | +As a contrast to Go, Cargo creates a tarball with the vendored dependencies. To my knowledge, these tarballs have never changed between Cargo versions. |
| 40 | + |
| 41 | +## Unresolved Questions and Bikeshedding |
| 42 | + |
| 43 | +- What format should we use (tarball or directory)? |
0 commit comments