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 (8)
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! :-)
Lol, lmao, lol. Wow.
Finally a decent build system, and it turns out to be the same approach I was using 20 years ago for software lifestyles but in TS instead of batch which is a nice improvement.
By build system I mean a system for making software life cycles, not a predefined software lifestyle with potential for minimal plug-ins.
After all these years with gradle, maven, ant, Jenkins, and the rest of the junk, I've been missing my batch CLI approach for some time, I hate chaining commands in package.json, you can't even use a space in the command name... how is programming batch INSIDE a json string better than using real batch or maybe this approach?!
Anyway, excited to try this... it has this potential to really bring a monorepo together WITH a seamless platform independent pipeline (ci/cd, sorry, I use the older terms... I see this a lot in web tech, lots of wheel reinventing 30 times, then a decade later people realize, oh, other people had something better 20 years ago... e.g. agile vs. Real project management, build systems vs. Custom life cycles)
I think best case for this approach are manual tasks that don't need any bash commands triggered. (e.g. tasks you can run with just node.js)
If you need it to run same command in multiple sub-folders in monorepo, I would suggest checking turbo build workspaces. It allows for a similar but a bit easier approach.
And if it's lots of complicated steps usually bash scripts are used they also help automating it for CD/CI pipelines later in the process.
Not only useful for monorepos. Thanks for the blueprint!
That's a great thing! Thanks for sharing. Definitely gonna use it in fullstack projects.