The Anatomy of package.json
Tags: javascript, webdev, node, beginners
Published: 2020-12-02 15:54:37

---
title: The Anatomy of package.json
published: true
description: Package.json does a lot more than list your project's dependencies. This is a guide to help you understand the clues and instructions it gives you.
tags: javascript, webdev, node, beginners
cover_image: https://images.unsplash.com/photo-1591117752671-541f3495dc93?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1050&q=80
canonical_url: https://laurieontech.com/posts/package-json/
---

If you work with JavaScript code, you come across a `package.json` file in every project. Every time you run `npm install` or `yarn` those package managers look through that file and grab the dependencies you need. However, these files are chockfull of valuable information and powerful features, let's dive in!

We'll work off of this example as a reference point.

```json
{
  "name": "example-package",
  "description": "A package that does a thing",
  "version": "1.0.0",
  "author": "laurieontech",
  "repository": {
    "type": "git",
    "url": "https://github.com/some-project-here"
  },
  "dependencies": {
    "react": "16.8.6"
  },
  "devDependencies": {
    "prettier": "^1.18.2"
  },
  "keywords": ["react"],
  "license": "MIT",
  "main": "index.js",
  "scripts": {
    "test": "jest"
  },
  "bin": "./bin/executable.js"
}
```

## Metadata

The first few items in a `package.json` are descriptive. `description`, `repository`, and `author` (or `contributors` if there are multiple) are there to provide context about the project. If you publish the package on npm, that information is available on the package page. `name` and `version` do a bit more.

`name` is a kebab-case package name. This is the name you'll find it under in [npm](https://www.npmjs.com/), this is the name you'll use to install the package, etc. If you're used to using packages you're likely familiar with syntax like this `"react": "16.8.6"`. This is a name and a version number.

Most JavaScript projects follow [semver](https://semver.org/) as a way to intuitively increment the package version. Every time the package is published to npm, the version should increase. Whether the first, last, or middle number increments is based on the significance of the changes and their impact on everyone else.

## Dependencies

Dependencies are a list of runtime packages that your project depends on. They are installed when you run `npm install`, or similar.

Let's talk about `"react": "16.8.6"` again. Each dependency is listed as a key-value pair using the name and version of the package. However, there are some extra characters you can add in front of the version.

- `~`: if you add a tilde, your package manager will install the version you listed or any newer patch version. E.g. `~16.8.6` means you will get the latest version of `16.8.x`, but not `16.9.0`.
- `^`: If you add a caret your package manager will install the version you listed or any newer patch or minor version, but not a major version. E.g. `^16.8.6` means you will get the latest version of `16.x.y`, but not `17.0.0`.

There are additional supported characters as well, allowing you to specify [ranges](https://docs.npmjs.com/cli/v6/using-npm/semver#ranges). All of these are parsed using the [semver package](https://www.npmjs.com/package/semver). This gets a bit confusing, so let me clarify. Semver is a set of guidelines for versioning your packages. Since npm follows it and uses those guidelines as a basis for its package manager, it named the semantic versioning package it uses accordingly.

### devDependencies

Slightly different are `devDependencies`. These are dependencies that are required for developers working on the package, e.g. testing libraries. However, end users don't need them, so they're included separately. They're included when you run `npm install` inside `example-package`, but not when you `npm install example-package` inside another project.

### peerDependencies

This is yet another type of dependencies. It's mostly there for package authors to prevent conflicts when they're using a package that other dependencies you have are also using. E.g. making sure the package is using the Babel version from your project and not a local one that might not be compatible.

## keywords

Keywords are a helper for the npm search function.

## license

Mandatory "I am not a lawyer" comment here. Licenses are a topic on which there are experts and I am not one of them. The license(s) listed are the terms under which you are permitted to use the project. You can read more on the [various licenses](https://spdx.org/licenses/).

## main entry point

This is the file that is referenced when someone imports a package. Given `"main": "index.js"`, `const example = require("example-package")` will grab the `example` export from the `index.js`.

## scripts

This is where we get into the meat of the file. The scripts section includes more key-value pairs. The key is the name of the command and the value is the command line instructions that run when you call it.

Let's start with a straightforward example.

```json
{
  "test": "npm run jest"
}
```

This is more of an alias than anything. It allows us to run `npm test` in our command line and it will actually run `npm run jest`.

What about something a bit more complex?

```json
{
  "lint": "eslint --cache --ext .js,.jsx,.ts,.tsx ."
}
```

This runs eslint against the entire project directory with some specific flags.

Nothing prevents you from running these scripts yourself. Giving you a shorter command with the correct configuration is just a better experience.

However, there are some scripts that are meant to build the project so that it can be published and installed in other projects as a package. There are [special keys](https://docs.npmjs.com/cli/v6/using-npm/scripts#pre--post-scripts) that execute scripts at specified times but we're not going to dive into that here.

Instead, we're going to look at a couple types of scripts you might see that bundle up a project and prepare it for installation.

## Babel example

```json
{
  "build": "babel src --out-dir . --ignore \"**/__tests__\""
}
```

This first script is using [babel](https://babeljs.io/). Using a configuration file in the root of the project, this takes all of the files in the `src` directory and compiles them into the root directory. It also includes a flag to ignore the files in `src/__tests__`.

## Microbundle example

```json
{
  "build": "microbundle -i src/example.js"
}
```

This script uses [microbundle](https://github.com/developit/microbundle) to bundle up the project. In this case, we're specifying a `src/example.js` as the entry point for building.

## Running scripts

Scripts are runnable. I mentioned above that `npm test` runs `npm jest` and it does. However, that's because `test` is an alias for `npm run test`. There are [a few of these](https://docs.npmjs.com/cli/v6/commands/npm-run-script#see-also).

For any other custom scripts you specify, a user needs to to run `npm run <script>`.

## bin

One more fun thing! In addition to the `npm` command, there is now an `npx` command. `npx` allows you to run commands without installing the package first. 🤯

Package authors enable this by using the `bin` section of the `package.json` file. It can be written as a key-value pair or using the below syntax.

```json
{
  "bin": "./bin/executable.js"
}
```

In this case, the `./bin` and extension get stripped and a user can run `npx executable`. If you ever decide to write a package that implements this, note that the relative file path is based on the bundled version of the project. This makes sense since it's being executed directly from the published package.

## Isn't there more?

Yes, [a lot more](https://docs.npmjs.com/cli/v6/configuring-npm/package-json) actually. But this is a solid start, so we'll stop here for now.