#100DaysOfMERN - Day 2

Subscribe to my newsletter and never miss my upcoming articles

✏ What is npm?

In short, npm stands for Node Package Manager. Very simply put, a "package" is a piece of code that makes our lives easier.

There's a vast amount of packages available. Quoting nodejs.dev/learn again:

In January 2017 over 350000 packages were reported being listed in the npm registry, making it the biggest single language code repository on Earth, and you can be sure there is a package for (almost!) everything.

And that was 2017.

In order to use a package, we have to install it, and npm is happy to help us. To install a package, move to your project folder and run

npm install <package-name>

// short version:

npm i <package-name>

This will download the package and put it into the node_modules folder (if this doesn't exist yet, it will create it). It'll also create a package-lock.json file, or add an entry to the existing one, but more about that later.


✏ The node_modules folder

Many jokes have been made about the size of this folder, although the problem is rather the sheer amount of files and subfolders within it. It takes ages to move or remove it.

When I started learning React, and initialised my first application with CRA (create-react-app), I was mildly shocked to see how much that blows up my projects. I was used to having a few kB of HTML, CSS and JS files, all of which written by myself. I was used to having complete control over my code. Now there's this monster living inside my project. Why would I need all of that?

The answer is "dependencies". A package isn't always a stand-alone-package, it might be built on top of another. Installing a package with a lot of dependencies can trigger an avalanche of stuff that needs to be installed along with it.

The files that keep track of all the dependencies of a project are package.json and package-lock.json.

✏ package.json and package-lock.json

As soon as you install your first package, it'll create a package-lock.json. This file has a complete list of all dependencies and their exact versions, and it can get quite long as the project grows.

If you run the command npm init in the terminal, it'll create a package.json. It includes similar information, just less detailed and more descriptive/human-readable.

Looking at the package.json of a very small React project that I initialised with create-react-app and stripped of everything until it stopped working - it contains the following entries for dependencies:

"dependencies": {
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "react-scripts": "4.0.1"
  }

That doesn't look so complicated, why am I even using create-react-app if I can just install those three little packages myself? Let's see what happens.


✏ Create a React app without create-react-app (?)

I'm starting with an empty project folder again. cd into it and run:

npm install react

Well that was quick. What has happened now?

  1. I find a node_modules folder in my project, which at this point consists of 38 files in 7 subfolders, so that's pretty lean

  2. I find a package-lock.json file

Taking a closer look at it:

{
  "requires": true,
  "lockfileVersion": 1,
  "dependencies": {
    "js-tokens": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
    },
    "loose-envify": {
      "version": "1.4.0",
      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
      "requires": {
        "js-tokens": "^3.0.0 || ^4.0.0"
      }
    },
    "object-assign": {
      "version": "4.1.1",
      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
    },
    "react": {
      "version": "17.0.1",
      "resolved": "https://registry.npmjs.org/react/-/react-17.0.1.tgz",
      "integrity": "sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==",
      "requires": {
        "loose-envify": "^1.1.0",
        "object-assign": "^4.1.1"
      }
    }
  }
}

If I look under the "required" entries of "react", it shows me that it needs two packages called "loose-envify" and "object-assign". "object-assign" is stand-alone, but "loose-envify" requires another stand-alone package called "js-tokens".

Now for the second package:

npm install react-dom

Again, very quick. Status of my node_modules folder: 90 files in 13 subfolders, that's still not much. There's also two new entries in the package-lock.json.

And now for the third, prepare for escalation:

npm install react-scripts

My node_modules folder has exploded and now contains 35.922 files in 5.307 subfolders. The package-lock.json has grown from around 50 lines to 16177. The monster is back.


I should note that I didn't just initialise a React project without using create-react-app, because that third dependency "react-scripts" on the list is a package used by create-react-app. It installs a massive list of stuff that I'll probably never need, or do I? How am I supposed to tell? The loss of control is unbearable.

Tomorrow, I'm going to find out how to initialise a React project with the absolute minimum of packages.


But before, to wrap this post up, here's a few more things that are important to know when installing packages with npm:

✏ Local vs. global installation

Per default, npm installs a package in the folder you're currently working in, so it is only available in that particular project. There's some cases where you want to install a package globally though, to make it accessible for all projects.

You do so by adding a -g flag:

npm install -g <package-name>

To locate the global node_modules folder on your harddrive:

npm root -g

I've been more or less cluelessly working with npm for a while now, installing stuff here and there without knowing what I was doing, so I was curious which packages are installed globally on my machine. You can check that with:

npm list -g

My first reaction was something like "WTF who installed all that", but taking a closer look revealed that the only thing I had installed globally was node-sass. Which makes sense, I'd certainly want that to be available in all projects. All the other stuff on that list is... the dependency-monster that comes with node-sass.

I'm beginning to accept that nobody can control this anymore.

And what would be the alternative? Should every package be stand-alone? Wouldn't that sort of sabotage the whole idea of modular packages, with the possibility to import only the ones you need for your project (... and the packages those packages need...) - maybe I'm a bit overwhelmed, and the feeling that we're all living in a massive skyscaper that's standing on a few tiny little matches has no foundation, and is just fear of the unknown.


✏ Recap

I've learned

  • some background info about npm
  • how to install packages locally or globally with npm install <package-name> or npm install -g <package-name>
  • what the node_modules_monster folder is and why it's so fat
  • the react and react-dom packages are surprisingly small, the folder only explodes when installing react-scripts (as used by create-react-app)
  • the dependencies of a project are stored in a package.json and (more detailed) in a package-lock.json file
  • how to list all dependencies of a project (npm list)
  • how to list all globally installed dependencies (npm list -g)
  • how to locate the global node_modules folder (npm root -g)

✏ Next:

  • how to initialise a React app without create-react-app and with minimum dependencies

✏ Thanks for reading!

I do my best to thoroughly research the things I learn, but if you find any errors or have additions, please leave a comment below, or @ me on Twitter. If you liked this post, I invite you to subsribe to my newsletter. Until next time 👋

No Comments Yet