In Progress
Unit 1, Lesson 1
In Progress

NPM for Dev Dependencies

No matter what language you program in for a living, these days being able to work with JavaScript tools and libraries has become an essential skill for any developer. Join guest chef Jessica Kerr for a quick intro in how to use NPM to document project dev dependencies in runnable code, instead of mistake-prone READMEs!

Video transcript & code

NPM Essentials

Let's pretend that one day, you find a project you want to contribute to.

Maybe your friend has this beautiful web site, and you think you can make it better.

You’re submitting updates to a static site project

Like, this site that Avdi and I put together just today. I'm not sure how it could be any better, but maybe you can think of something.

You clone the repository.

git clone

The project lead recommends live-server for local development

In the README, your friend has left a suggestion for a development process. It says, use live-server to serve the site locally, and it'll automatically reload in the browser whenever you change it.

Let's try it!

> live-server --verbose --ignore .
live-server: command not found

It doesn't work. Back to the README.

Oh look, it says we should use npm install -g live-server. Hmm. What will that do?

npm is a package manager for JavaScript. It ships with node.js. It does not stand for 'node package manager.' No no no.

The web site will tell you a different initialism every time you click it.

These days, npm distributes all kinds of things, include command-line utilities.

live-server is distributed by npm, and runs in node.

live-server is a "little http server with live reload capability." For development, not production. Cool.

Given that I have npm on my laptop, if we follow the README, and type

npm install -g live-server

this will install live-server on my laptop. That '-g' stands for 'global'.

which live-server

Now live-server is available everywhere.

live-server -v

The same version everywhere, regardless of what this project or another project expects.

OK. First, I don't want working on your project to permanenently change the state of my computer. Rude.

Also, I don't think it's fair to have a dependency on a tool that isn't documented in a way that's reproducible. READMEs aren't executable -- except by humans, and there's nothing reproducible about that. Also, It has to be specific about the version.

Let's do something else instead. I'll uninstall this now.

npm uninstall -g live-server

You want to document this project dependency

What I want is for live-server to be installed within this project, in a way that I can execute it with the right arguments. (I could use docker, but this episode is about npm.) Also, I want that important command documented in some sort of script, where I can execute it reproducibly instead of copy and paste.

npm documents dependencies in package.json.

npm is all about documenting dependencies, including development-time tools like this one.

It does this using a file called package.json. Let's create one.

It contains json.


There's usually a bunch of stuff in here, most of which is important for publishing packages, which we are not going to do. Let's include the absolute minimum for what we are trying to accomplish.

I want to document this dependency. I don't have to do this by hand; npm will do it for me.

npm install --save-dev live-server

I ask npm to install this dependency with --save-dev meaning this is development-time tool, not a runtime dependency.

npm downloads the live-server package and all the packages it depends on, and all the packages they depend on, and so on, forever, 191 packages later...

"devDependencies": {
        "live-server": "^1.2.1"

and then it records this dependency in package.json. the "dev" part in devDependencies means that this is for development-time use.

what else did it do?

git status

Here's our package.json, we want to commit that. It is documenting the dependency.

git add package.json

Here is package-lock.json. What is that?

cat package-lock.json

Yuck. This file has all the super-specific version info about the exact packages that it downloaded. You want to commit this. You don't want to look at it, but committing it enables reproducible builds. Your friend can be sure to get the same version of live-server and all its dependencies that you have now. This is not super important for this dev tool, but it is very important for runtime JavaScript dependencies.

It is good practice to commit package-lock.json.

git add package-lock.json

What about this node_modules directory? Do we want to commit that?

tree node_modules


This is where npm downloaded all that stuff to. The package-lock.json means we can always download this same stuff again. npm no longer lets anyone delete published modules, so it'll be there in the future. We do not need this mess in our repository.

Instead, we want to tell git to ignore node_modules.


npm can also document important commands

Now that we've documented the dependency, we want to encode how to use it. npm can work as a rudimentary build tool for using these dependencies.

With the subcommand run we can tell npm what command we want to run, and we get to name that command whatever we want. I would like to type npm run serve and get this web site served up.

> npm run serve
npm ERR! missing script: serve

Ah yes, we must give it the script.

We can put a "scripts" property in package.json, and map script names to shell commands.

"scripts": {}

We call our script serve and paste in the command from the README.

"serve": "live-server --verbose --ignore ."

Now npm can run the script.

npm run serve

It runs! We can click on the link and see...

oh no! wah-wah. Cannot even. What is wrong?

Oh no, it is trying to serve --ignore. It is supposed to ignore changes to the README and serve the current directory.

This command is wrong! The README is wrong! That's what happens when you put code in text files! It doesn't work and nobody realizes it.

Fortunately, we have put this command in a place that is executable. When we change it, it will change for every person who types 'npm run serve.'

If we check the documentation, we learn that --ignore wants an equal sign, and update the command.

"serve": "live-server --verbose ."

Try it now.

> npm run serve
Serving "." at

Good, serving the current directory.

Behold, the royal imperial golden yak!

It is working!

Now we can develop! Open the html in vscode and change something.

... of Doom

Yay it updated and we didn't have to do anything. Live-server is a success.

Conclusion of Part 1

Okay. So far, we’ve seen an NPM script work; we’ve documented a dev dependency; and we’ve learned about package-lock.json and node_modules (don’t forget to put it in your .gitignore!) But! How do npm scripts work? What do we need to put in the README in place of that broken command? Tune in next time to delve into the dangers of npm install!