DEV Community

Gergő Móricz
Gergő Móricz

Posted on

Making a CLI & Library fusion

So let's say you want to make a CLI. Great! You made it with yargs, or just straight process.argv. Anything works.

But now you suddenly want to make it a package too. Create a seperate package? No need to.

For example, here's our CLI code[1]:

var fs = require('fs');
var fileName = process.argv[2]; // get 1st argument (0 is node, 1 is script filename) 

var fileStats = fs.statSync(fileName); // get file stats
var fileSize = fileStats.size; // filesize in bytes

console.log(fileSize); // print fileSize
Enter fullscreen mode Exit fullscreen mode

This simple code gets the filesize of the file provided in the arguments.

Now, if you'd want to make a CLI, you'd name this index.js, put it as main file, and put it in "bin". But if you're making a fusion[2], you should name this cli.js[3], and put it into "bin", but do not make it main. We'll create a new main file.

var fs = require('fs');

function getFileSize(fileName) {
    var fileStats = fs.statSync(fileName); // get file stats
    var fileSize = fileStats.size; // filesize in bytes

    return fileSize;
}

module.exports = getFileSize;
Enter fullscreen mode Exit fullscreen mode

This is somewhat-same as the CLI code. You still require fs first, but then we create a function, with a fileName argument. You don't need to get the fileName from argv since it's supplied in the argument, so we just get the fileStats and fileSize, and then return it. Lastly, we export the function.

People can now use the main file in our own node scripts.

var cliApi = require('yourPackageName');

var fileSize = cliApi('example.txt'); //getFileSize(fileName)
Enter fullscreen mode Exit fullscreen mode

Now, we could just leave the CLI code alone and just ship it, but when you update the package, inconsistency could happen between the API and the CLI code. So you'd want to rewrite the CLI code to use the CLI API code[1].

var cliApi = require('./index.js'); // require the CLI API file

var fileName = process.argv[2]; // get 1st argument (0 is node, 1 is script filename)

var fileSize = cliApi(fileName); //getFileSize(fileName)

console.log(fileSize);
Enter fullscreen mode Exit fullscreen mode

First, we get the CLI API. Let's talk about this require statement for a bit. ./ is needed at the beginning. It means that node should require the file (that can be found at the path after ./) not from node_modules, but the actual folder that the script (that's being executed) is.

Next, we need to get the filename from the arguments, then we get the filesize from the CLI API, and we print it to the console. That's it!

Notes

[1]: Shebang (needed for npm link) not included.
[2]: Not a technical term.
[3]: Doesn't matter what you name it. Just reserve index.js for the main file.

Also, make sure you make it clear in your README that this package is both a package and a CLI.

Example files

index.js

var fs = require('fs');

function getFileSize(fileName) {
    var fileStats = fs.statSync(fileName); // get file stats
    var fileSize = fileStats.size; // filesize in bytes

    return fileSize;
}

module.exports = getFileSize;
Enter fullscreen mode Exit fullscreen mode

cli.js

#!/usr/bin/env node

var cliApi = require('./index.js'); // require the CLI API file

var fileName = process.argv[2]; // get 1st argument (0 is node, 1 is script filename)

var fileSize = cliApi(fileName); //getFileSize(fileName)

console.log(fileSize);
Enter fullscreen mode Exit fullscreen mode

package.json

{
    "name": "getfilesize",
    "version": "1.0.0",
    "description": "A CLI and package for getting file sizes.",
    "main": "index.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "keywords": [
        "fs"
    ],
    "bin": {
        "filesize": "cli.js"
    },
    "author": "skiilaa",
    "license": "MIT"
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)