DEV Community

Mario
Mario

Posted on • Originally published at mariokandut.com on

The Basics of Package.json explained

The package.json file is a core element in the Node.js ecosystem and is basic for understanding and working with Node.js, NPM, modern JavaScript and JavaScript frameworks and libraries. The package.json is used as a manifest about applications, modules and packages. It's a tool to make modular and efficient applications.

Understanding the basics of package.json is essential, so I outline in the following article the most common and important properties of the package.json file.

Let's start with initiating a project to create a basic package.json.

Create Folder mkdir super-mario and run npm init (option with the -y flag to answer yes to every question).

This command will create this package.json:

{
  "name": "super-mario",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}
Enter fullscreen mode Exit fullscreen mode

Properties in package.json

Let's go over the properties from the generated package.json.

name

This is the name for the project and is optional if the project is private. When a npm package is published, the name will be used as an URl. Hence, when the package gets published the name is required and must be unique on the npm repository. It has requirements to be URL-safe. It should be:

  • less than 214 characters
  • should not include spaces
  • can only contain lowercase letters, hyphens (-) or underscores (_)
  • The name can be prefixed with a scope (i.e.: @angular/angular-cli).

A good value if you have publicly published this package on Github is the repository name.

version

A version number that should be understandable by node-semver. The version property is optional for private and required for public modules.

description

A brief description for the project, optional for both, private and public, but useful if you publish your package to npm to provide more information.

main

The entry file/entry point for the package. When you import this package in an application, that’s where the application will search for the module exports.

scripts

The scripts key expects an object with script names as keys and commands as values, so it basically defines a set of node scripts you can execute.

These scripts are command line applications. You can run them by calling npm run COMMAND or yarn COMMAND.

When we have a look at the package.json above, we see the test key in the scripts.

    "test": "echo \"Error: no test specified\" && exit 1"
Enter fullscreen mode Exit fullscreen mode

So we can run npm run test in the commandline and would get Error: no test specified logged.

There are no limitations in the command name and scripts can do things like starting your project on a local server, building for production or running your tests.

Typically, in the scripts commands you'll make the most manual changes in your package.json file.

keywords

An array of keywords to helps with finding the module on the npm repository.

author

This property lists the package author name and expects an object with keys for the name, email and url.

Possible formats are:

"author": Mario Kandut <mario@mariokandut.com> (https://www.mariokandut.com)"

"author": {
    "name": "Mario Kandut",
    "email": "mario@mariokandut.com",
    "url": "https://www.mariokandut.com"
  }
Enter fullscreen mode Exit fullscreen mode

license

Indicates the license of the package and expects a license name using its SPDX identifier. It defaults to the ISC license, and MIT would be another popular license choice. You can also use UNLICENSED for projects that are private and closed-source.

There are many more properties in the package.json than generated by npm init.

repository

This property defines where the source code of the package repository is located. Typically, this would be a public GitHub/Gitlab repo, with the repository array noting that the type of version control is git, and the URL of the repo itself.

The repository property would look like this:

  "repository": {
      "type": "git",
      "url": "https://github.com/mariokandut/REPOSITORY-NAME.git"
  }
Enter fullscreen mode Exit fullscreen mode

You can also use the prefix for github or gitlab.

"repository": "github:mariokandut/REPOSITORY-NAME"

"repository": "gitlab:mariokandut/REPOSITORY-NAME"
Enter fullscreen mode Exit fullscreen mode

Managing Dependencies

One of npm’s main strength is the ability to easily manage project’s dependencies. Hence, the package.json centers mostly around specifying the dependencies for a project. There are the regular dependencies, but there can also be devDependencies, peerDependencies, optionalDependencies and bundledDependencies.

dependencies

The dependencies property is where dependencies, the other modules that this module uses, are defined. It takes an object that has the name and version at which each dependency should be used. You'll frequently find carets (^) and tildes (~) included with package versions, these are the notation for version range.

The dependencies property could look something like this:

  "dependencies": {
    "async": "^0.2.10",
    "npm2es": "~0.4.2",
    "optimist": "~0.6.0",
    "request": "~2.30.0",
    "skateboard": "^1.5.1",
    "split": "^0.3.0",
    "weld": "^0.2.2"
  }
Enter fullscreen mode Exit fullscreen mode

When you install a package using npm or yarn, npm install <PACKAGENAME> or yarn add <PACKAGENAME>, that package is automatically inserted in this list.

"dependencies": {
    "react": "^16.10.2",
    "react-dom": "^16.10.2",
    "react-helmet": "^5.2.1",
}
Enter fullscreen mode Exit fullscreen mode

devDependencies

The devDependencies property is almost identical to the dependencies property, when it comes to structure. There is one key difference. The dependencies property is used to define the dependencies that a module needs to run in production. The devDependencies property is used to define the dependencies the module needs to run in development.

Example:

"devDependencies": {
    "@types/react-helmet": "^6.1.0",
    "@types/react-typist": "^2.0.1",
    "@typescript-eslint/eslint-plugin": "^4.1.0",
}
Enter fullscreen mode Exit fullscreen mode

When you install a package using npm or yarn, npm install --dev <PACKAGENAME> or yarn add --dev <PACKAGENAME>, that package is automatically inserted in this list.

peerDependencies

In some cases, you want to express the compatibility of your package with a host tool or library, while not necessarily doing a require of this host. This is usually referred to as a plugin. Notably, your module may be exposing a specific interface, expected and specified by the host documentation.

For example:

{
  "name": "tea-latte",
  "version": "1.3.5",
  "peerDependencies": {
    "tea": "2.x"
  }
}
Enter fullscreen mode Exit fullscreen mode

This ensures your package tea-latte can be installed along with the second major version of the host package tea only.

optionalDependencies

If a dependency can be used, but you would like npm to proceed if it cannot be found or fails to install, then you may put it in the optionalDependencies object. This is a map of package name to version or url, just like the dependencies object. The difference is that build failures do not cause installation to fail. Running npm install --no-optional will prevent these dependencies from being installed.

It is still your program's responsibility to handle the lack of the dependency. For example, something like this:

try {
  var foo = require('foo')
  var fooVersion = require('foo/package.json').version
} catch (er) {
  foo = null
}
if ( notGoodFooVersion(fooVersion) ) {
  foo = null
}
// .. then later in your program ..
if (foo) {
  foo.doFooThings()
}
Enter fullscreen mode Exit fullscreen mode

Entries in optionalDependencies will override entries of the same name in dependencies, so it's usually best to only put in one place.

bundledDependencies

This defines an array of package names that will be bundled when publishing the package.

In cases where you need to preserve npm packages locally or have them available through a single file download, you can bundle the packages in a tarball file by specifying the package names in the bundledDependencies array and executing npm pack.

For example:

{
  "name": "awesome-web-framework",
  "version": "1.0.0",
  "bundledDependencies": [
    "renderized",
    "super-streams"
  ]
}
Enter fullscreen mode Exit fullscreen mode

💰: $100 (credits) for you to start your cloud journey with DigitalOcean!

More Useful Configuration Keys

There are more configurations that can optionally go into your project’s package.json file:

engines

You can specify the version of node that your stuff works on:

  "engines": {
    "node": ">=0.10.3 <15"
  }
Enter fullscreen mode Exit fullscreen mode

And, like with dependencies, if you don't specify the version (or if you specify "*" as the version), then any version of node will do.

You can also use the "engines" field to specify which versions of npm are capable of properly installing your program. For example:

  "engines": {
    "npm": "~1.0.20"
  }
Enter fullscreen mode Exit fullscreen mode

Unless the user has set the engine-strict config flag, this field is advisory only and will only produce warnings when your package is installed as a dependency.

config

A config object can be used to set configuration parameters used in package scripts that persist across upgrades. For instance, if a package had the following:

{
  "name": "foo",
  "config": {
    "port": "8080"
  }
}
Enter fullscreen mode Exit fullscreen mode

Then with a start command that referenced the npm_package_config_port environment variable. A user could override that by doing npm config set foo:port 8001.

private

If you set "private": true in your package.json, then npm will refuse to publish it. This is a way to prevent accidental publication of private repositories.

browser

If your module is meant to be used client-side the browser field should be used instead of the main field. This is helpful to hint users that it might rely on primitives that aren't available in Node.js modules. (e.g. window)

homepage

The URL for the home page of the project.

bugs

A URL where issues and bugs can be reported. This will often be an URL to the Github issues page for a project.

TL;DR

  • The package.json file acts like a manifest for your application.
  • The strength is in managing dependencies (prod, dev, peer, optional or bundled).
  • Read the article carefully, so you don't publish your package publicly.
  • There are many configuration options, have a look at the official documentation for all the possible options.

Thanks for reading and if you have any questions , use the comment function or send me a message @mariokandut.

If you want to know more about Node, have a look at these Node Tutorials.

References (and Big thanks):

NPM, Node, NodeJS dev, NodeSource

Discussion (0)