5 ms·
There are two things going on here: 1. the desire to allow every package to install its dependencies separately to guarantee deterministic behavior, and 2. not
by tolmasky 10y ago
There are two things going on here: 1. the desire to allow every package to install its dependencies separately to guarantee deterministic behavior, and 2. not too take up a lot of disk space. The desired result in this SPECIFIC scenario is simple:
1. Package B contains the faulty Dependency 1 1.0.0.
2. Package A contains the correct Dependency 1 1.0.1.
This is the best case scenario in this situation: Package A is not subject to the security problems of Dependency 1 1.0.0. Package B IS of course, but critical parts of your code are not exposed. If these were the only two packages involved, it would be simple. However the problem arrises from having LOTS of packages, and thus wanting to dedupe, which then leads to having to resolve these semver ranges, or being left with the unfortunate indeterministic behavior of sometimes these being distinct installations and sometimes not.
Thus, to achieve the desired result above AND ensure packages are only installed once on disk, you want to dedupe at the FILESYSTEM level. This is how ied behaves (and I encourage everyone to take a look at its implementation, it is very well done). Essentially, every package is installed and contains symlinks to their dependencies, that way it only takes up space on the hard disk once. However, thanks to node's treat-symlinks-as-real-filepaths option, as far as node is concerned these are distinct installations (as if you had done no deduping at all). Now you have the result from above AND the minimum space on disk.
- bassislife 10y agoYour proposal only works if you don't need ABI-compatibility, especially relevant if the build process needs to be parallelizable. It all depends on what sense you provide to the terms: "minor version" and how you expect your software to be built.
- tolmasky 10y agoYes this proposal is for "functional" languages (where libraries provide functions, and records). JavaScript is a good fit here. When you need "identical" objects across different dependencies, the idea is to specify it as such (npm has a notion of a "peerDependency" <-- this is not a black box object but the requirement that this "exist"). Plugins are a good example of this. If you are building a React plugin, you don't want to require your own React (and then hope the build system dedupes it!), instead you state "I can build when React at this version is present". Once you have this explicit requirement, it is much easier to reason about the (few) "shared" objects. Here most users would raise an eyebrow that for some reason the "resolved" React is a very low version for example.
- Dylan16807 10y agoI would not say the main reason for wildcards is to save disk space. It's to allow updates to happen automatically. > Package A contains the correct Dependency 1 1.0.1. What happened to "Package A was written when Dependency 1 was at version 1.0.0."? If someone in the pinned-version universe was able to update package A to include 1.0.1, the equivalent coder in the semver universe should have updated package A to require "<2 >=1.0.1". Semver dependencies should never lag in necessary updates vs. pinned dependencies in a fair comparison, because they can set an exact minimum.
- tolmasky 10y agoI'm saying deduping is to save disk space. Sennmver ranges combined with deduping generate this behavior.