✏ 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?
I find a
node_modules
folder in my project, which at this point consists of 38 files in 7 subfolders, so that's pretty leanI 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>
ornpm install -g <package-name>
- what the
node_modules_monster
folder is and why it's so fat - the
react
andreact-dom
packages are surprisingly small, the folder only explodes when installingreact-scripts
(as used by create-react-app) - the dependencies of a project are stored in a
package.json
and (more detailed) in apackage-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 👋