Node.js package management system: Packages that install commands

1. Packages that install commands

Some packages install command-line programs. A side effect of installing such packages is a new command that you can type at the shell prompt or use in shell scripts. An example is the hexy program that we briefly used in Chapter 2, Setting Up Node.js. Another example is the widely used Grunt or Babel build tools.

The recommendation to explicitly declare all dependencies in package.json applies to command-line tools as well as any other package. Therefore these packages will typically be installed locally. This requires special care in setting up the PATH environment variable correctly. As you should already be aware, the PATH variable is used on both Unix-like systems and Windows to list the directories in which the command-line shell searches for commands.

The command can be installed to one of two places:

Global install: It is installed either to a directory, such as /usr/local, or to the bin directory where Node.js was installed. The npm bin-g command tells you the absolute pathname for this directory. In this case, it’s unlikely you’ll have to modify the PATH environment variable.

Local install: Installs to node_modules/.bin in the package where the module is being installed, the npm bin command tells you the absolute pathname for the directory. Because the directory is inconveniently located to run commands, a change to the PATH variable is useful.

To run the command, simply type the command name at a shell prompt. This works correctly if the directory where the command is installed happens to be in the PATH variable. Let’s look at how to configure the PATH variable to handle locally installed commands.

1.1. Configuring the PATH variable to handle locally installed commands

Assume we have installed the hexy command like so:

$ npm install hexy 

As a local install, this creates a command as node_modules/.bin/hexy. We can attempt to use it as follows:

$ hexy package.json

-bash: hexy: command not found 

But this breaks because the command is not in a directory listed in the PATH. The workaround is to use the full pathname or relative pathname:

$ ./node_modules/.bin/hexy package.json

… hexy output 

But obviously typing the full or partial pathname is not a user-friendly way to execute the command. We want to use the commands installed by modules, and we want a simple process for doing so. This means, we must add an appropriate value in the PATH variable, but what is it?

For global package installations, the executable lands in a directory that is probably already in your PATH variable, like /usr/bin or /usr/local/bin. Local package installations require special handling. The full path for the node_modules/.bin directory varies for each project, and obviously it won’t work to add the full path for every node_modules/.bin directory to your PATH.

Adding ./node_modules/.bin to the PATH variable (or, on Windows, .\node_modules\.bin) works great. Any time your shell is in the root of a Node.js project, it will automatically find locally installed commands from Node.js packages.

How we do this depends on the command shell you use and your operating system.

On a Unix-like system, the command shells are bash and csh. Your PATH variable would be set up in one of these ways:

$ export PATH=./node_modules/.bin:${PATH}     # bash

$ setenv PATH ./node_modules/.bin:${PATH}     # csh 

The next step is adding the command to your login scripts so the variable is always set. On bash, add the corresponding line to ~/.bashrc, and on csh add it to ~/.cshrc.

Once this is accomplished the command-line tool executes correctly.

1.2. Configuring the PATH variable on Windows

On Windows, this task is handled through a system-wide settings panel:

This pane of the System Properties panel is found by searching for PATH in the Windows Settings screen. Click on the Environment Variables button, then select the Path variable, and finally click on the Edit button. On the screen here, click the New button to add an entry to this variable, and enter .\node_modules\.bin as shown. You’ll have to restart any open command shell windows. Once you do, the effect will be as shown previously.

As easy as it is to modify the PATH variable, we don’t want to do this in all circumstances.

1.3. Avoiding modifications to the PATH variable

What if you don’t want to add these variables to your PATH at all times? The npm- path module may be of interest. This is a small program that computes the correct PATH variable for your shell and operating system. See the package at https://www.npmjs.com/package/npm-path.

Another option is to use the npx command to execute such commands. This tool is automatically installed alongside the npm command. This command either executes commands from a locally installed package or it silently installs commands in a global cache:

$ npx hexy package.json

 Using npx is this easy.

Of course, once you’ve installed some packages, they’ll go out of date and need to be updated.

2. Updating packages you’ve installed when they’re outdated

The coder codes, updating their package, leaving you in the dust unless you keep up.

To find out whether your installed packages are out of date, use the following command:

$ npm outdated

 The report shows the current npm packages, the currently installed version, as well as the current version in the npm repository. Updating outdated packages is very simple:

$ npm update express

$ npm update 

Specifying a package name updates just the named package. Otherwise, it updates every package that would be printed by npm outdated.

Npm handles more than package management, it has a decent built-in task automation system.

3. Automating tasks with scripts in package.json

The npm command handles not just installing packages, it can also be used to automate running tasks related to the project. In package.json, we can add a field, scripts, containing one or more command strings. Originally scripts were meant to handle tasks related to installing an application, such as compiling native code, but they can be used for much more. For example, you might have a deployment task using rsync to copy files to a server. In package.json, you can add this:

{ …

“scripts: {

“deploy”: “rsync –archive –delete local-dir

user@host:/path/to/dest-dir

}

… }

What’s important here is that we can add any script we like, and the scripts entry records the command to run:

$ npm run deploy 

Once it has been recorded in scripts, running the command is this easy.

There is a long list of “lifecycle events” for which npm has defined script names. These include the following:

  • install, for when the package is installed
  • uninstall, for when it is uninstalled
  • test, for running a test suite
  • start and stop, for controlling a server defined by the package

Package authors are free to define any other script they like.

Npm also defines a pattern for scripts that run before or after another script, namely to prepend pre or post to the script name. Therefore the pretest script runs before the test script, and the posttest script runs afterward.

A practical example is to run a test script in a prepublish script to ensure the package is tested before publishing it to the npm repository:

{

“scripts”: {

“test”: “cd test && mocha”,

“prepublish”: “npm run test”

}

}

With this combination, if the test author types npm publish, the prepublish script will cause the test script to run, which in turn uses mocha to run the test suite.

It is a well-known best practice to automate all administrative tasks, if only so that you never forget how to run those tasks. Creating the scripts entries for every such task not only prevents you from forgetting how to do things, but it also documents the administrative tasks for the benefit of others.

Next, let’s talk about how to ensure the Node.js platform on which a package is executed supports the required features.

4. Declaring Node.js version compatibility

It’s important that your Node.js software runs on the correct version of Node.js. The primary reason being that the Node.js platform features required by your package are available every time your package is run. Therefore, the package author must know which Node.js releases are compatible with the package, and then describe in package.json that compatibility.

This dependency is declared in package.json using the engines tag:

“engines”: {

“node”: “>= 8.x <=10.x”

}

The version string is similar to what we can use in dependencies and devDependencies. In this case, we’ve defined that the package is compatible with Node.js 8.x, 9.x, and 10.x.

Now that we know how to construct a package, let’s talk about publishing packages.

5. Publishing an npm package

All those packages in the npm repository came from people like you with an idea of a better way of doing something. It is very easy to get started with publishing packages.

You first use the npm adduser command to register yourself with the npm repository. You can also sign up with the website. Next, you log in using the npm login command.

Finally, while sitting in the package root directory, use the npm publish command. Then, stand back so that you don’t get stampeded by the crush of thronging fans, or, maybe not. There are several zillion packages in the repository, with hundreds of packages added every day. To get yours to stand out, you will require some marketing skill, which is another topic beyond the scope of this book.

It is suggested that your first package be a scoped package, for example, @my-user- name/my-great-package.

We’ve learned a lot in this section about using npm to manage and publish packages. But npm is not the only game in town for managing Node.js packages.

Source: Herron David (2020), Node.js Web Development: Server-side web development made easy with Node 14 using practical examples, Packt Publishing.

Leave a Reply

Your email address will not be published. Required fields are marked *