Make it a habit to like it first

preface

NPM (Node Package Manager) is the default Package management tool written in JavaScript for Node.js. Although a tool in Node.js, it is now more commonly used in conjunction with front-end build tools for front-end package management.

As a package manager, the most important thing is to manage dependencies. NPM handles complex dependency trees differently than other package managers, and this article will cover these details

Npm2 and NPM3 + versions handle dependencies differently, but few projects now use versions below NPM3, and all of the introductions in this article are based on versions above NPM3

NPM relies on the management mechanism

In general, NPM, like other package managers, is package dependent and declares those dependent packages with version numbers.

Semantic version number

NPM uses semantic versions to control the version of versiondependent packages, such as range symbols such as ^~>=<. However, the resolution of version numbers in this article is irrelevant, except that NPM will install the latest version available in the scope if you use a range version number

Here is a joke about the NPM documentation. It took a long time to find the specific version policy used for this range version number. The documentation is not clear… Finally, I found a little introduction on the NPM Update page

If the app ‘s package. Json contains:

“Dependencies” : {” dep1 “:” ^ 1.1.1 “}

Then NPM update will install [email protected], because 1.2.2 is latest and 1.2.2 distribution ^1.1.1.

The concept of NPM’s scope version design is quite advanced. The scope version number allows users to automatically update the minor version in a timely manner. The update may fix some bugs, but there are also many risks caused by the update. After all, version numbers are controlled by humans, and human controls can make mistakes, such as an update to a revision number that removes some API, resulting in incompatibility

Personally, this package management mechanism of range version numbers does more harm than good and is too risky. If you sneak in a (minor) version without changing anything in a server scenario, you’re likely to have some serious accidents. As a general rule, any change needs to be tested, especially with dependency upgrades, which can be a risky business. If it’s a generic base package, the risk is even greater, with too many references and incompatibilities.

Dependency trees and transitive dependencies

NPM installs dependencies flat by default, and also installs them at the root of node_modules. For example, if there is A module A that depends on module B:

Version conflict

Now add a module C, C also depends on B, but C depends on the higher version of B, V2.0, when the NPM processing is a little different; Since the B version that C depends on is incompatible with the B version that A depends on, NPM first installs THE B1.0 that A depends on to the root directory, and then installs the B2.0 that C depends on to its own node_modules, as shown in the following figure

The directory structure

| - [email protected] | -- - [email protected] | -- - [email protected] | -- - [email protected]Copy the code

For the dependency tree with incompatible versions, NPM will first check whether the version is compatible. If the version is compatible, the installation will not be repeated. If the version is incompatible with the previous dependency package version, ** NPM’s solution to package version conflicts creates redundancy in package files, but resolves the conflict perfectly

Is this version conflict resolution really perfect?

As you can see from the previous section, NPM will install the dependent package to the node_modules of the current package when version incompatibility occurs.

For example, B v1.0 registered an attribute to the Window object, and B v2.0 also registered an attribute to the Window object. Due to the large gap between B V1.0 and V2.0, although the same object was registered, there was A large gap between the attribute and its function. When A page introduces both A and C modules, both B v1.0 and B v2.0 are loaded, and some unexpected errors may occur. Unacceptable to the user

The above example may not be appropriate because registering Windows is inherently risky. Now imagine another common scenario, such as Angular (2), where two Angular-based components depend on different larger versions of Angular (Core). When a page uses two components and the two components need to interact on the current page, such as assignment or function calls, It is easy to have the problem shown above.

This problem applies to package management in the Java ecosystem as well, but in a different form:

In Maven (the package management tool for the Java ecosystem), the dependencies are tree-structured, but the result of the build is flat. If multiple versions of jars are present, the runtime typically loads all jars; The same Class is loaded only once, and the next N Jar classes with the same name (package name + Class name) are ignored. Conflicts need to be handled by users themselves.

Maven builds rely on multi-version processing of packages (delivery), as shown below:

NPM also provides a solution to this possible version conflict problem: peerDependencies

peerDependencies

PeerDependencies are similar to provide Scope in Maven. When a dependency module X is defined in peerDependencies instead of devDependencies or dependencies, Projects that depend on the module will not automatically download the dependency.

Direct dependencies are declared in devDependencies or dependencies, and indirect dependencies are declared in devDependencies where other modules of the current project depend on X modules of the version range. If neither of these modules is specified, NPM install generates an alarm such as:

NPM WARN [email protected] requires a peer of Lodash @~1.3.1 but none is installed. You must install peer dependencies yourself.Copy the code

npm & webpack

Many projects now use WebPack as a build tool for their projects, but unlike Maven in Java, WebPack and NPM are two separate tools that keep build and package management separate

That is, even if NPM installs conflicting packages as “submodules” in the current package, WebPack doesn’t necessarily recognize them

For example, in the ABC three modules above, if A module’s code import BObj from B mod, then after webpack is built, which B version will A refer to? V1.0 or V2.0?

Finding and Fixing Duplicates in Webpack with Inspectpack this scenario is quite complicated and will not be introduced in this article, which details processing techniques and test scenarios under Webpack:

conclusion

NPM package management is a good concept, but it is not suitable for all scenarios. For example, the subModule pattern is not feasible in Java, and the subModule pattern still has some risks, but the risks are reduced. Once you have multiple dependent code working or interacting on a page, it’s easy to get into trouble.

Whatever the package management tool, the safest thing to do is to avoid duplication. After adding a new dependency or creating a new project, use a dependency analysis check tool to check for duplicate/conflicting dependencies.

reference

  • Finding and fixing duplicates in webpack with Inspectpack
  • Github.com/formidablel…
  • Understanding the npm dependency model
  • www.reddit.com/r/haskell/c…
  • Stackoverflow.com/questions/2…
  • How npm3 Works
  • Nodejs.org/es/blog/npm…
  • Docs.npmjs.com/packages-an…
  • Details that are overlooked in NPM dependency management
  • How does npm handle conflicting package versions?

Original is not easy, reprint please contact the author. If my article helped you, please like/bookmark/follow it ❤❤❤❤❤❤