Let's say someone has implemented a module for sets that's being used in two modules.
a.js:
var set = require("set");
export.thing = set(1, 2, 3);
b.js:
var set = require("set");
export.thing = set(4, 5, 6);
You're using both:
var thing_a = require("a").thing
, thing_b = require("b").thing
Imagine that b
depends on a newer version of set
which has a completely new (faster!) internal structure. This structure uses a new (public) API for computing intersects/unions between sets. This means that's it's impossible to use thing_a
and thing_b
together:
thing_a.intersect(thing_b) // => Err!
thing_a.union(thing_b)) // => Err!
It's easy to to incidentally creates these sort of dependencies inside your app. If a
and b
did use the same version of set
, you might have used the code above. By upgrading either a
or b
there's a chance that your code might mysteriously break.
More concretely:
Every object that comes from
module O, version A
throughmodule A
can only be safely handled by amodule X
if (and only if)module X
also has a dependency onmodule O, version A
. (Byversion A
I don't necessarily mean the exact same version, but the same version of the public API).
This turns out to be the exact same constraint as single-namespace (Ruby, Python, Perl etc.) language imposes.
Conclusion:
For modules that are only used internally in a module and never used across modules: NPM trumps. There's no way your dependencies can interact with other modules.
For modules that are used across several modules: You need to (globally) use the same module. Technically, you don't have to, but it's going to be a lot of pain and horrible for encapsulation.
@jcoglan:
Me:
No. The problem is that two packages can say "I'm returning a
set
", and you need to worry about the differences. The package didn't "leak" anything. it specified clearly that it returns an object from another module. There are many cases where this is wanted (do you really want x different versions ofset
in your code?).