If, module1 depends on button-component@1.0.2
and module2 depends on button-component@1.5.0
Which version of button-component is loaded in final bundle?
What steps can we take to avoid duplicate modules in final build?
TL;DR: It is not the bundler. It is Mostly the package manager (NPM, etc.) that is responsible for providing dependencies which bundlers simply follow.
It is not really the bundler itself that working here. For third party dependencies, we are generally using NPM and thus node_modules
in general.
Now job of the NPM is to keep dependency tree as flat as possible. Imagine following graph where your-code
depends on module1
and module2
. And, module1
itself is internally dependent on module2
.
your-code <-- module1@1.0.0 <--- module2@1.0.5
<-- module2@1.0.5
If your-code
and module1
uses same dependency, then npm
will keep it flat under top-level node_modules
folder and as such bundler (Webpack/Rollup) will pick single version of the module
Now imagine the other scenario:
your-code <-- module1@1.0.0 <--- module2@1.0.5
<-- module2@2.0.0
Here your-code
is using module2
with version 2.0.0 but module1
is using different version. That is a breaking change. In this case, npm will not keep it flat and install module2@2.0.0
in you node_modules
folder while module2@1.0.5
will be installed in a node_modules
folder of module1
. The Bundler will pick both the version when it is bundling the code.
But it is not all that script. Usually if dependencies only differ at patch
version i.e. last digit of semver, then npm
will pick only one and ignore the other. Further, this resolution of dependencies also depends upon the NPM versioning model. You can specify lenient version of dependency using ^1.0.5
or ~1.0.5
. That also affect if NPM will install modules separately or keep it flat.
You can use bundler analyzer or equivalent to detect duplicate dependencies. When NPM was first released, it could not handle this and thus Bower was born to help developers with flat dependencies so that bundlers would pick only one version of dependency. But latest NPM is reasonably good enough for most cases and bower is no longer used.
Finally, I said Mostly because it is generally the package manager. But, in case of bare imports, you can teach/override bundler to specifically resolve from one and only one version. For example, consider Webpack resolution config below:
resolve: {
// see below for an explanation
alias: {
preact: path.resolve('node_modules', 'preact')
}
}
So, if you accidentally have preact
more than once in your dependencies graph, then you can force Webpack to always use specific preact
import from one specific folder.