65.9K
CodeProject is changing. Read more.
Home

A Note on Node-Modules

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0 vote)

Oct 23, 2020

CPOL

4 min read

viewsIcon

4052

downloadIcon

20

Node-modules folder and its structure

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?

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:

{
  "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.

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.

{
  "name": "@example/package-0",
  "version": "1.0.1",
    ......
}
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

The "package-1"

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

{
  "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".

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

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.

{
  "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.

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

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

node index.js

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.

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:

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
A Note on Node-Modules - CodeProject