Join egghead, unlock knowledge.

Want more egghead?

This lesson is for members. Join us? Get access to all 3,000+ tutorials + a community with expert developers around the world.

Unlock This Lesson
Become a member
to unlock all features

Level Up!

Access all courses & lessons on egghead today and lock-in your price for life.


    Convert a Single Command CLI into a Multi Command CLI with Oclif and TypeScript

    Shawn WangShawn Wang

    Heavy duty CLI's like gatsby and npm do more than one thing. The convention is to namespace them with a command name after the CLI name, like gatsby new or gatsby build or npm install or npm uninstall. We should be comfortable converting our single purpose CLI's into multi command CLI's as our needs grow, as well as understand how to share logic between commands to keep code DRY.



    Become a Member to view code

    You must be a Member to view code

    Access all courses and lessons, track your progress, gain confidence and expertise.

    Become a Member
    and unlock code for this lesson




    Instructor: 0:00 Many commands you use are single-purpose commands like echo or cat, but some of the more heavy-duty commands out there are multi-commands.

    0:16 For example, the Gatsby CLI has the gatsby new command for scaffolding new sites, gatsby develop for serving a local directory, gatsby build for a production build, gatsby serve for serving just the production build, or info for reporting information, and so on and so forth.

    0:39 Some of the very, very heavy-duty commands, like npm, have a huge offering of other commands that you might want to choose from. Multi-commands are essentially namespaced CLIs within a CLI. You might as well have a framework to organize them and share code between them.

    1:01 Oclif has a good solution for this. You can run npx oclif multi mycli to start a new CLI that is multi by default. I can take away some of that magic by showing you how to convert a single-command CLI into a multi-command CLI.

    1:20 First, we're going to make a new folder in our source directory, called commands. Then we move our existing command that we've been working on into the commands directory. I'll call it something different, like init, for example.

    1:37 I still need an index.ts at the top level. I will type in index.ts over here and paste in this pre-prepared code. This is essentially a helper from oclif for helping to go through the commands folder and initialize each of these commands based on their file name. That's very easy and intuitive.

    2:01 There's one more bit of prep that we need to do, which is to head into package.json and look for the special oclif field. This tells oclif some information about what you want to do with this CLI. We're just going to tell it that there is a multi-commands folder. It's going to point to the lib commands folder.

    2:20 Notice that this doesn't actually exist inside of the source code, but it's the compiled output of the TypeScript into JavaScript. When we compile this, we'll have lib commands, init.ts, init.js, and everything else that you might possibly want.

    2:35 Now that we've refactored our single command into a multi command, we can now try it out, like saying, "yarn mycli init." That should find this init command and run it accordingly. We can now also scaffold out some other commands. Like let's have a build command over here.

    3:00 We'll say, "Hello." We'll have some sort of different visual input so that we can see the difference. Hello from build. Serve.ts. Then we'll also make a serve command. This is just illustrative. We're not actually going to use this. Just to show you that you can very quickly scaffold different commands that say different things, like "yarn cli build" or "yarn cli serve."

    3:41 Sometimes, we want to share logic or share initialization code for more than one command. One pattern that you can do is to have a common base command at the top level where you import that command. You use a base command that you extend directly.

    4:02 This is documented inside of the docs as a custom base class. It shows how you can add extra methods or do standard initialization in all commands that you run. This can be imported by specific commands. You're using logic. You're only typing out what's different in every single command.