Every now and then I need to create an NPM package that runs on the command line, as a CLI (Command Line Interace).
For example, npx eslint
runs and lints code in a directory and can accept command line arguments also. Similarly npx create-next-app --eslint --tailwind
would scaffold a Next.js app that has eslint and tailwind configured.
I want my package similarly from the command line using npx <my package name>
.
There are three things you need to do to turn an NPM package into one that can be run from the command line:
Create a file that contains the code that would run when the package is run using
npx
from the command line, e.g.cli.js
.-
In package.json, place the following attributes:
"bin": "./cli.js", "type": "module",
"bin": "./cli.js"
tellsnpx
that when the package is run from the command line usingnpx <package name>
, then./cli.js
should be run."type": "module"
tells NPM utilities that the type of the modules which would be imported in the code files in this package would ES6 (usingimport
statement). Otherwise you would only be able to import Common JS modules (usingrequire
) which, this being the tail-end of 2024, you probably don't want to do. -
On top of the file declared in
"bin"
in package.json, place the line#!/usr/bin/env node
. This allows thecli.js
to run using the Node.js executable when it is launched bynpx
.For example, my
cli.js
in package root would look like this:
#!/usr/bin/env node import { createRequire } from "module"; const require = createRequire(import.meta.url); const packageJson = require("./package.json"); console.log("Hello World!"); console.log(`Version number of the package is ${packageJson.version}`);
Now, on the terminal, in the root folder of my package, I can run npx .
and would get the following output:
If I publish the package to NPM then go to a completely different folder on the terminal and execute npx show-version-number
(where show-version-number
is the name of the package in package.json
and therefore in NPM), it would still run:
I checked that npx
downloaded and stored the package in C:\Users\{My User Name}\AppData\Local\npm-cache\_npx\
on my Windows machine.
Code is in this GitHub repo.
ASIDE: I had to import package.json
using the following lines in cli.js
:
import { createRequire } from "module";
const require = createRequire(import.meta.url);
const packageJson = require("./package.json");
instead of import packageJson from "package.json"
because in my version of Node (v20+), this latter import throws a ERR_IMPORT_ASSERTION_TYPE_MISSING
error when I try to run the package using npx .
.
To fix the error I had to either rewrite the import as:
import packageJson from "./package.json" with { type: "json" };
or using the assert
keyword as:
import packageJson from "./package.json" assert { type: "json" };
In either case, I got the following warning when I ran the code:
However, the three lines I use to import package.json
instead, clunky as they are, get rid of the warning. See this StackOverflow thread for more details.
Top comments (0)