DEV Community

Cover image for shadcn-ui/ui codebase analysis: How does shadcn-ui CLI work? — Part 3.0
Ramu Narasinga
Ramu Narasinga

Posted on • Updated on

shadcn-ui/ui codebase analysis: How does shadcn-ui CLI work? — Part 3.0

I wanted to find out how shadcn-ui CLI works. In this article, I discuss the code used to build the shadcn-ui/ui CLI.

In part 2.0 to 2.15, I discussed how npx shadcn-ui init works under the hood.

We will look at how npx shadcn-ui add works in this part 3.x.

Since the packages/cli/src/commands/add.ts file is large, I will break this analysis down into parts and talk about code snippets and explain how stuff works.

In this article, we will look the concepts:

  1. Add command.
  2. Commander.js package
  3. How add command is registered?
  4. Argument and options
  5. addOptionsSchema

add command

export const add = new Command()
  .name("add")
  .description("add a component to your project")
  .argument("\[components...\]", "the components to add")
  .option("-y, --yes", "skip confirmation prompt.", true)
  .option("-o, --overwrite", "overwrite existing files.", false)
  .option(
    "-c, --cwd <cwd>",
    "the working directory. defaults to the current directory.",
    process.cwd()
  )
  .option("-a, --all", "add all available components", false)
  .option("-p, --path <path>", "the path to add the component to.")
  .action(async (components, opts) => {
    try {
      const options = addOptionsSchema.parse({
        components,
        ...opts,
      })
Enter fullscreen mode Exit fullscreen mode

We will begin with how add command is added. The above code snippet is picked from packages/cli/src/commands/add.ts

Commander.js package:

Command is imported from commander.js, a complete solution for node.js command-line interfaces.

How add command is registered?

The way add command is registered is that, if you open this src/commands/index.ts in a new tab, you will find this code as shown below

Commands are created separately in the folder named commands for maintainability purposes. If you were to fork this shadcn-ui/ui repo and want to add your own command, this is one way to do it.

Argument and options

When you write something like npx shadcn-ui add Button, Button here is an argument.

.argument("\[components...\]", "the components to add")
Enter fullscreen mode Exit fullscreen mode

The above code snippet is picked from here.

You also have options that go with your add command as shown below:

.option("-y, --yes", "skip confirmation prompt.", true)
.option("-o, --overwrite", "overwrite existing files.", false)
.option(
  "-c, --cwd <cwd>",
  "the working directory. defaults to the current directory.",
  process.cwd()
)
.option("-a, --all", "add all available components", false)
.option("-p, --path <path>", "the path to add the component to.")
Enter fullscreen mode Exit fullscreen mode

and then you have action

.action(async (components, opts) => {
Enter fullscreen mode Exit fullscreen mode

addOptionsSchema

 const options = addOptionsSchema.parse({
  components,
  ...opts,
})

const cwd = path.resolve(options.cwd)

if (!existsSync(cwd)) {
  logger.error(\`The path ${cwd} does not exist. Please try again.\`)
  process.exit(1)
}
Enter fullscreen mode Exit fullscreen mode

The above snippet is picked from add.ts

addOptionsSchema is declared just above the add function as shown below:

const addOptionsSchema = z.object({
  components: z.array(z.string()).optional(),
  yes: z.boolean(),
  overwrite: z.boolean(),
  cwd: z.string(),
  all: z.boolean(),
  path: z.string().optional(),
})
Enter fullscreen mode Exit fullscreen mode

This schema basically ensures all the options and arguments are valid before processing them further.

Conclusion:

In Part 2.0 to 2.15, I discussed how npx shadcn-ui init works under the hood. It is time for a version bump to my articles. In 3.x articles, I will write about how npx shadcn-ui add works under the hood. Please note that semver is not applicable to my articles lol.

Command is imported from commander.js, a complete solution for node.js command-line interfaces. The way add command is registered with npx shadcn-ui CLI is that, if you open this src/commands/index.ts in a new tab, you will find this code as shown below:

program.addCommand(init).addCommand(add).addCommand(diff)
Enter fullscreen mode Exit fullscreen mode

There’s an argument that accepts a single component name or an array of component names, that is why you would write something like npx shadcn-ui add Button

Buttonhere is an argument. add command also has few options such -y, -o, -c, -a, -p. Read more about these in the shadcn-ui CLI documentation.

addOptionsSchema ensures that arguments and the options passed to the add command are valid using zod

Get free courses inspired by the best practices used in open source.

About me:

Website: https://ramunarasinga.com/

Linkedin: https://www.linkedin.com/in/ramu-narasinga-189361128/

Github: https://github.com/Ramu-Narasinga

Email: ramu.narasinga@gmail.com

Learn the best practices used in open source.

References

  1. https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/commands/add.ts
  2. https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/commands/add.ts#L31C1-L49C9
  3. https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/index.ts#L24
  4. https://www.npmjs.com/package/commander
  5. https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/commands/add.ts#L22

Top comments (0)