Creating an NPM package that runs on command line with npx

nausaf - Oct 2 - - Dev Community

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:

  1. 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.

  2. In package.json, place the following attributes:

    "bin": "./cli.js",
    "type": "module",
    

    "bin": "./cli.js" tells npx that when the package is run from the command line using npx <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 (using import statement). Otherwise you would only be able to import Common JS modules (using require) which, this being the tail-end of 2024, you probably don't want to do.

  3. On top of the file declared in "bin" in package.json, place the line #!/usr/bin/env node. This allows the cli.js to run using the Node.js executable when it is launched by npx.

    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:

Image description

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:

Image description

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");
Enter fullscreen mode Exit fullscreen mode

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" };
Enter fullscreen mode Exit fullscreen mode

or using the assert keyword as:

import packageJson from "./package.json" assert { type: "json" };
Enter fullscreen mode Exit fullscreen mode

In either case, I got the following warning when I ran the code:

Image description

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.

. . . . . .
Terabox Video Player