Command line apps are a neat way to package repetitive tasks. This will walk you through some tools that are useful to build CLI apps.

Channel your inner Sindre Sohrus and ship a beautifully simple CLI app using Node.

The idea 💡

When merging/rebasing, the file that always seems to cause trouble is the package-lock. We’ll go through how to make a simple utility that deletes the package-lock.json file, regenerates it (npm install) and adds it to the git index.

You can find it here: https://github.com/HugoDF/fix-package-lock and run it using npx fix-package-lock .

Piping to the command line 🚇

To start off, we’ll leverage a package from Sindre Sohrus, execa , which is described as “a better child_process ”. For the following snippet to work, run npm install --save execa :

index.js

const execa = require ( 'execa' ); execa ( 'ls' ). then ( result => console . log ( result . stdout ));

node index.js index.js node_modules package-lock.json package.json

Dealing with sequential actions ✨

To re-generate the package-lock we’ll need to first delete it, then run an npm install .

To this end, we can use Listr, it allows us to do things that look like:

Run npm install --save listr and add leverage Listr as follows:

index.js :

const execa = require ( 'execa' ); const Listr = require ( 'listr' ); new Listr ([ { title : 'Removing package-lock' , task : () => execa ( 'rm' , [ 'package-lock.json' ]) }, { title : 'Running npm install' , task : () => execa ( 'npm' , [ 'install' ]) }, { title : 'Adding package-lock to git' , task : ( ctx , task ) => execa ( 'git' , [ 'add' , 'package-lock.json' ]) . catch (() => task . skip ()) } ]). run ();

Now the output of node index.js looks like the following:

Listr gives you a loading state when you have a long-running task that returns a Promise (like the execa invocation of npm install ).

It’s also possible to display a message that changes using Observables, for more information see the Listr docs

Executable JavaScript files 🦅

It’s ideal to be able to execute our script using ./index.js instead of node index.js .

To do this, we need the file to be executable on UNIX systems that’s: chmod +x . So

chmod +x index.js

We then need to inform the system how it should attempt to run the file, that’s using the following hashbang:

#!/usr/bin/env node

If we add it to index.js we get:

#!/usr/bin/env node const execa = require ( 'execa' ); const Listr = require ( 'listr' ); new Listr ([ { title : 'Removing package-lock' , task : () => execa ( 'rm' , [ 'package-lock.json' ]) }, { title : 'Running npm install' , task : () => execa ( 'npm' , [ 'install' ]) }, { title : 'Adding package-lock to git' , task : ( ctx , task ) => execa ( 'git' , [ 'add' , 'package-lock.json' ]) . catch (() => task . skip ()) } ]). run ();

Which we can now run using:

./index.js

Adding package binaries

npm has a bin field which we can use like the following (in package.json ):

{ "name": "beautiful-cli", "version": "1.0.0", "description": "A simple CLI", "main": "index.js", "bin": { "fix-package-json": "./index.js" } "dependencies": { "execa": "^0.10.0", "listr": "^0.14.1" } }

Publishing to npm 🚀

This is left to the reader as an exercise, although using the np package, it’s super straightforward.

Hint: run npx np in whatever package you’re trying to publish