Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Javascript

A Note on Node-Modules

0.00/5 (No votes)
23 Oct 2020CPOL4 min read 4.1K   20  
Node-modules folder and its structure
Here, we try to address how the different versions of the same package are placed in the node-modules folder. We will also look at whether all the versions of the same package are invoked or just one version of the package is invoked when the application runs.

Introduction

This is a note on the node_modules folder and its structure.

Background

This is a note on the node_modules folder and its structure. In particular, I want to answer the following questions when a NODE project references different versions of the same package. These questions are simple questions, but they may give us a nasty version hell if not taken proper care.

  • How are the different versions of the same package placed in the node-modules folder?
  • When the application runs, are all the versions of the same package invoked or just one version of the package is invoked?

Image 1

To answer the questions, I created three NODE packages. The package-client project references two versions of the package-0 directly and indirectly.

  • We will publish two versions of package-0, version 1.0.0 and version 1.0.1.
  • The package-1 references package-0 version 1.0.0.
  • The package-client references package-1 and package-0 version 1.0.1. As a consequence, the package-client references package-0 for both version 1.0.0 and version 1.0.1.

The experiment is done with a private NPM registry. If you do not know how to setup a private NPM registry, you can take a look at my another note. In order to make my work easy, all the packages are scoped to "@example". For example, the full name of the package-0 is @example/package-0. I also put a .npmrc file under each package, so I do not need to specify the registry URL (--registry) every time I publish or install a package.

@example:registry=http://localhost:4873/

If you want all the packages scoped as @example to use the registry http://localhost:4873/, you can also put the entry in the .npmrc file in your home directory. We can confirm if the registry is up and running by curling its URL.

curl http://localhost:4873/

The "package-0"

The package.json of the @example/package-0 is the following:

JavaScript
{
  "name": "@example/package-0",
  "version": "1.0.0",
  "description": "The @example/package-0",
  "main": "index.js",
  "author": "Song Li",
  "license": "ISC"
}

The only functionality of the Index.js file is to export a string stating the version number.

JavaScript
module.exports = 'This is Version 1.0.0';

We can publish the package to the registry by the following command:

npm publish

We can then update the package.json and the index.js file to the version 1.0.1, and publish it.

JavaScript
{
  "name": "@example/package-0",
  "version": "1.0.1",
    ......
}
JavaScript
module.exports = 'This is Version 1.0.1';

We can confirm that both versions are published to the registry by the following command:

npm view @example/package-0 versions

Image 2

The "package-1"

The @example/package-1 references the @example/package-0.

JavaScript
{
  "name": "@example/package-1",
  "version": "1.0.0",
  "description": "The @example/package-1",
  "main": "index.js",
  "author": "Song Li",
  "license": "ISC",
  "dependencies": {
    "@example/package-0": "1.0.0"
  }
}

There are three ways to reference a package version.

  • ~version “Approximately equivalent to version” - It will update you to all future patch versions, without incrementing the minor version. ~1.2.3 will use releases from 1.2.3 to <1.3.0;
  • ^version “Compatible with version” - It will update you to all future minor/patch versions, without incrementing the major version. ^2.3.4 will use releases from 2.3.4 to <3.0.0;
  • Absolute version - In this note, I used the absolute version "1.0.0"

The index.js requires the @example/package-0 and re-exports the string from it. Since the @example/package-1 references the @example/package-0 version 1.0.0, ideally it should export the string "This is Version 1.0.0".

JavaScript
const msg = require('@example/package-0');
    
module.exports = msg;

We can then publish the package and confirm its availability in the registry.

npm publish
npm view @example/package-1 versions

Image 3

The "package-client"

With both @example/package-0 and @example/package-1 published to the registry, we can take a look at the package-client.

JavaScript
{
  "name": "package-client",
  "version": "1.0.0",
  "description": "The package-client ",
  "main": "index.js",
  "private": true,
  "keywords": [],
  "author": "Song Li",
  "license": "ISC",
  "dependencies": {
    "@example/package-1": "1.0.0",
    "@example/package-0": "1.0.1"
  }
}

It references both @example/package-1 and @example/package-0, the index.js prints out the string from both packages.

JavaScript
const msg_0 = require('@example/package-0');
const msg_1 = require('@example/package-1');
    
console.log(`Message from package-0: ${msg_0}`);
console.log(`Message from package-1: ${msg_1}`);

We can then install the referenced packages and take a look at the node_modules folder.

npm install
find . -name "package-0" -type d -prune -print | xargs du -chs

Image 4

If we now run the index.js, we will see the following result:

node index.js

Image 5

Conclusion

We can now come to the conclusion.

  • When multiple versions of a package are referenced by a NODE application, all the versions will be installed in the node_modules folder. In this example, the 1.0.1 is installed at the top level, and the 1.0.0 is installed under the @example/package-1 folder;
  • When multiple versions of a package are used in a program, all the versions will be invoked if actually used in the program.

We can further take a look at the package-lock.json file to better understand how the different versions of the package are installed.

Image 6

We need to share the package-lock.json file with the team, so all the people have the same node_modules folder when they npm install. If we want to clean up the experimental packages from the registry, we can use the following command:

JavaScript
npm cache clean -f
npm unpublish @example/package-0 -f
npm unpublish @example/package-1 -f

Points of Interest

  • This is a note on the node_modules folder and its structure.
  • It is not common that you will encounter problem with different versions of a package. But if you do, it is better to know that you may be running different versions of a dependency package simultaneously in the same program.
  • I hope you like my posts and I hope this note can help you one way or the other.

History

  • 16th October, 2020: First revision

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)