Published:
I've been curious about this topic for quite some time. As a Sitecore developer, I knew it was easy to build a .Net core CLI (Command Line Interface) application, and in fact, there is already one available from Sitecore that was extensible. Technically I would say JSS falls into this category as well and is Node-based. But before we get into this topic at a deeper level, I wanted to briefly talk about why I wanted to learn this and potentially build my own Node-based CLI.
So for Sitecore development in general, everything has always been done in .Net. For a .Net developer wanting to run a CLI, it seemed only natural for them to either already have the .Net runtime to run the CLI or for them to download the required framework. But I wondered is that feasible for a frontend developer? Now sure to run a Node CLI, you have to have node, but I would argue that there are way more developers in general running Node today than running .Net runtime. And especially if you are targeting the typical Frontend developer, they might not even know much about .Net at all. So it just seemed like a good idea to build a CLI, potentially for Sitecore (but I might take it further in the future) using Node so that more Frontend developers don't have to jump through the .Net hoops. Frontend developers used to the Javascript Frameworks of today, can stick to the tools they are used to, while .Net developers could theoretically continue to use the .Net CLI.
So that is the premise behind this curiosity, but I wasn't sure it was feasible yet, and probably still won't know in the immediate term how feasible it really is, but I figured I'd write about my journey nonetheless.
So before I start building a CLI, I wanted to talk a little bit about the nuances of running a CLI. If you are a .Net developer, or not that familiar with NPM, this section is for you. Now first off it's important to know the differences between Node and NPM. Node is simply the runtime, allowing you to write Javascript and have that run on your machine (similar to how the .Net runtime works for .Net), while NPM is nothing more than a place to store your node scripts and allow users to consume them (similar to Nuget). We are going to use NPM to distribute and run our Node CLI, which we’ll cover how to take our simple CLI and use Github Actions to deploy that out to NPM. There are a couple of ways a user can run a CLI with NPM. They could install an NPM package globally and run it with a simple npm command:
1> npm i speed-test -g23> speed-test
Now that it's been installed locally you can simply just run speed-test
in your command line to run a speed test (this is just an example of a CLI you can use). This method of running a CLI is great if you need to run this CLI often. An example of a CLI you may run multiple times could be yo
which runs Yeoman which is a Node/Javascript-based templating engine. But what if you just want to run something once? Well as of version 5.2.0+ of npm, it now includes something called npx
, which just gives you the ability to run any npm package one time. This is quite useful for running CLI commands. An example of this is when you want to create a React
project, you might run the following command:
1> npx create-react-app my-react-project
So this npx command will execute the npm package called create-react-app
which will be useful for generating the needed starter kit template to run React.
So now that you know how you can run a CLI application based on Node, let's explore how you would create your first node CLI application. It’s quite easy to get started, you’ll likely need a GitHub repository (especially if you want to use Github actions to deploy to npm covered in a future article), or if not, you’ll need a folder for where your application will begin to be built. If using a repository, clone this repository locally, this is where I’ll begin this topic. If you are not sure how to clone a repository, definitely check out this topic where I covered how to use the CLI to run common git actions. If you want to skip ahead, you can navigate directly to the repository here.
So once we have a blank repository, we need to start by initializing Node by running the following command:
1> npm init -y
And then that will create a package.json file with all the default options. If you would like to choose options as you go, just leave off the -y
flag, and then you’ll be required to answer the questions, or just press enter to go with the default options.
We next should install some common CLI packages that might be useful in our CLI application. Obviously, not all of these are required, but most with be helpful.
To install these, just run the following command:
1> npm i commander chalk
Once those are installed, I’ll go ahead and create a src
folder that will contain our code for the CLI application. I also plan to use typescript for this application, so to initialize typescript, in the root, also run the following commands to get setup:
1> npm i typescript@latest @types/node23> npx tsc --init
These steps will create a tsconfig.json in your root directory and tells typescript how to compile the typescript and output javascript. Now let's jump into some of the changes you’ll need to make to your package.json
file. At any point, you can refer to this repository on GitHub to see the final product.
1{2 "name": "first-node-cli",3 "version": "1.0.0",4 "description": "Just a play repo that I'll use in my blog series",5 "main": "index.js",6 "scripts": {7 "build": "tsc",8 "start": "ts-node ./src/bin/index.ts",9 "test": "echo \"Error: no test specified\" && exit 1"10 },11 "repository": {12 "type": "git",13 "url": "git+https://github.com/dylanyoung-dev/first-node-cli.git"14 },15 "bin": {16 "test-cli": "./src/bin/index.ts"17 },18 "author": "",19 "license": "ISC",20 "bugs": {21 "url": "https://github.com/dylanyoung-dev/first-node-cli/issues"22 },23 "homepage": "https://github.com/dylanyoung-dev/first-node-cli#readme",24 "dependencies": {25 "@types/node": "^18.6.1",26 "chalk": "4.1.2",27 "commander": "^9.4.0",28 "typescript": "^4.7.4"29 }30}
You’ll need to add two scripts
that are missing, one for the Typescript build "build": "tsc"
and then another one for start "start": "ts-node ./src/bin/index.ts"
these we’ll use later. Then you’ll also need to create a bin definition:
1"bin": {2 "test-cli": "./src/bin/index.ts"3}
This is so that when we type in a command into the command line, it’ll know what phrase to use to trigger the command test-cli
and then what file to run.
Now that’s it for the package.json, now let's create two files. One called index.ts (/src/bin/index.ts
) with the contents below. This is the primary starting point for the application, where you can place other imports if you were building more than a simple “Hello World” repository.
1#!/usr/bin/env node2import { Command } from 'commander';3import * as packageJson from '../../package.json';4import { sayHello } from '../test';56const program = new Command();78program.version(packageJson.version)9 .command('say-hello')10 .description('A simple CLI tool to say hello')11 .action(() => {12 sayHello();13 }14 )15 .parse(process.argv);
This code really shows how the commander
npm package really helps with the Node CLI, because you can specify a command, in this case, test-cli say-hello
would be used to now run the action sayHello();
. You can also provide options or arguments here as well if your CLI needs user-provided data.
You’ll also need to create an /src/test.ts
file, which will contain our sayHello function which our commander function calls into. At this point, we just output our Hello World, using the npm package for chalk which allows us to use different colors in our command line.
1import chalk from 'chalk';23const sayHello = () => {4 console.log(chalk.green('Hello World!'));5}
Once you’ve completed writing your application, there are a couple of different wants to test it out, but I found the following one of the better ways, plus if you are creating some form of CLI that works with a separate folder on your machine, I found this is the closest to the experience you would have if you pulled the package from NPM directly.
You’ll first need to build your NPM package, and in a previous step, we created an NPM script called build
which we can use to facilitate the Typescript build process. We can run the following command below to run the build script.
1> npm run build
Then once it’s built, you can then run an NPM link command, which basically links the global npm command for this NPM package to this local folder, so that we can run the test-cli
anywhere, and in any folder.
1> npm link
So once you’ve run that command, you should now be able to run the following command anywhere else globally that you would like, and your output should be a green Hello World
message.
1> test-cli say-hello
Well that completes today’s content. I will continue this post in my next article, where I will cover how to create a basic CI/CD process using Github Actions to publish our CLI out to NPM. This will then allow us to run npx test-cli or to install it globally or locally for various needs in a specific application.