I like to create local CLIs for my Monorepo to automate tasks like build
and deploy
. These tasks often require more than just chaining a few commands in an npm script (like rimraf dist && tsc
).
Using commander.js and tsx, we can create executable programs written in TypeScript that run from the command line like any other CLI tool.
#!/usr/bin/env -S pnpm tsx
import { Command } from 'commander';
const program = new Command()
.name('monorepo')
.description('CLI for Monorepo')
.version('1.0.0');
program
.command('build')
.description('Build the monorepo')
.action(async () => {
console.log('Building...');
// run your build steps ...
});
program
.command('deploy')
.description('Deploy the monorepo')
.action(async () => {
console.log('Deploying...');
// run your deploy steps ...
});
await program.parseAsync(process.argv);
Save this script as cli
(or any name you prefer) in your project root and make it executable with chmod +x cli
. You can then run it directly using ./cli
:
$ ./cli
Usage: monorepo [options] [command]
CLI for Monorepo
Options:
-V, --version output the version number
-h, --help display help for command
Commands:
build Build the monorepo
deploy Deploy the monorepo
help [command] display help for command
The magic that allows you to run this without node
, npx
, or even a .ts
extension is in the first line - the shebang:
#!/usr/bin/env -S pnpm tsx
This shebang tells your shell which program should execute this file. Behind the scenes, it translates your ./cli
command into pnpm tsx cli
. This works with other package managers too - you can use npm
or yarn
instead of pnpm
.
Top comments (4)
I can see myself doing this when build gets complicated. Thanks for the informative post. keep writing
I have a built a CLI for building and deploying my Shopify app. It executes multiple CLI commands (vite build, serverless deploy, etc.) via execa.
Maybe I'll make a short follow up post on this :-)
This is great @zirkelc, we need more of these.
Thanks! :-)