Building Vue3 Component Library from Scratch #10 Create Cli Scaffold
Tags: vue
Published: 2024-05-29 09:57:08

This article will implement the development of a scaffold called `create-stellarnovaui`. With just one command, `npm init stellarnovaui`, you can pull the entire component library development framework to your local environment.

## Create Cli Package

Firstly, we create a new `cli` directory under the `packages` directory, initialize it by running `pnpm init`, and then change the package name to `create-stellarnovaui`.

> What needs to be known here is that when we execute `npm init xxx` or `npm create xxx`, we are actually executing `npx create-xxx`. So when we execute `npm init stellarnovaui`, we are actually executing `npx create-stellarnovaui`.

When we execute `create-stellarnovaui`, it will execute the path corresponding to `bin` in `package.json`. Therefore, we modify `package.json` as follows.

```json
{
  "name": "create-stellarnovaui",
  "version": "1.0.0",
  "description": "",
  "bin": "index.js",
  "keywords": [],
  "author": "",
  "license": "MIT"
}
```

At the same time, create `index.js` as the entry file. Note that you need to add `#! /usr/bin/env node` at the beginning.

```bash
#! /usr/bin/env node
```

# Processing User Input Commands with `command-line-args`

There are actually many plugins for handling user input parameters, such as CAC, Yargs, Commander.js, command-line-args, etc. However, in my view, command-line-args is the simplest to use, so here we use command-line-args for user parameter parsing.

```bash
pnpm add command-line-args
```

Create `cli.js` to handle the logic of our scaffold. Here, let's simply write a `-v` version command.

```javascript
import commandLineArgs from "command-line-args";
import { readFile } from "fs/promises";

const pkg = JSON.parse(
  await readFile(new URL("./package.json", import.meta.url))
);
//"Configure command parameters."
const optionDefinitions = [{ name: "version", alias: "v", type: Boolean }];
const options = commandLineArgs(optionDefinitions);
if (options.version) {
  console.log(`v${pkg.version}`);
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0gswlhzibp4wo1xvromf.png)

We can also use the `command-line-usage` plugin to provide us with help commands.

```bash
pnpm add command-line-usage
```

Only the relevant code is posted here.

```javascript
import commandLineArgs from "command-line-args"
import commandLineUsage from "command-line-usage"
...

//Help command
const helpSections = [
  {
    header: 'create-stellarnovaui',
    content: 'A scaffold for quickly setting up a component library environment'
  },
  {
    header: 'Options',
    optionList: [
      {
        name: 'version',
        alias: 'v',
        typeLabel: '{underline boolean}',
        description: 'Version number'
      },
      {
        name: 'help',
        alias: 'h',
        typeLabel: '{underline boolean}',
        description: 'Help'
      }
    ]
  }
];

if (options.help) {
  console.log(commandLineUsage(helpSections))
  return
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/us5cr6hocsmjsk447ffr.png)

## Interactive Commands

When we use some scaffolding, such as create-vite, it will give us some options to choose from.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wjwm710x4fkcxn7aeh2a.png)

So our scaffolding should also have this feature, but how should this be implemented?

It's actually quite simple, all we need is the `prompts` plugin. It can configure what the user inputs, whether it's single or multiple choices, etc.

Firstly, install `prompts`.

```bash
pnpm add prompts
```

Then in cli.js

```javascript
import prompts from "prompts";
const promptsOptions = [
  {
    type: "text",
    name: "user",
    message: "User",
  },
  {
    type: "password",
    name: "password",
    message: "Password",
  },
  {
    type: "select", //
    name: "gender",
    message: "Gender",
    choices: [
      { title: "Male", value: 0 },
      { title: "Female", value: 1 },
    ],
  },
  {
    type: "multiselect",
    name: "study",
    message: "Select Framework",
    choices: [
      { title: "Vue", value: 0 },
      { title: "React", value: 1 },
      { title: "Angular", value: 2 },
    ],
  },
];

const getUserInfo = async () => {
  const res = await prompts(promptsOptions);
  console.log(res);
};
getUserInfo();
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1qiny76nkld7hfetef1t.gif)

Then we can deal with different logic based on the corresponding values. Of course, our scaffolding doesn't need so many parameters, we can change it to the following options.

```javascript
const promptsOptions = [
  {
    type: 'text',
    name: 'name',
    message: 'Please enter the project name'
  },
  {
    type: 'select', //Single Choice
    name: 'template',
    message: 'Please select a template',
    choices: [
      { title: 'stellarnovaui', value: 1 },
      { title: 'stellarnovaui2', value: 2 }
    ]
  }
];
```

Then we can pull different repositories based on the user's choices.

## Pulling Remote Repository Templates

To pull remote repositories, we can use the `download-git-repo` tool and use its `clone` method. At the same time, we need to install a loading plugin `ora` and a log color plugin `chalk`.

```bash
pnpm add download-git-repo ora chalk
```

```javascript
//gitClone.js

import download from "download-git-repo";
import chalk from "chalk";
import ora from "ora";

export default (remote, name, option) => {
  const downSpinner = ora("Downloading template...").start();
  return new Promise((resolve, reject) => {
    download(remote, name, option, (err) => {
      if (err) {
        downSpinner.fail();
        console.log("err", chalk.red(err));
        reject(err);
        return;
      }
      downSpinner.succeed(chalk.green("Template downloaded successfully!"));
      resolve();
    });
  });
};

//cli.js
const remoteList = {
  1: "https://github.com/markliu2013/StellarNovaUI.git",
  2: "https://github.com/markliu2013/StellarNovaUI.git",
};
const getUserInfo = async () => {
  const res = await prompts(promptsOptions);
  if (!res.name || !res.template) return;
  gitClone(`direct:${remoteList[res.template]}`, res.name, {
    clone: true,
  });
};
```

After execution, the template we need is in the directory.

Finally, we can publish our 'create-stellarnovaui'. The publishing process has been introduced before, so I won't go into detail here. I have already published it, so let's try to run 'npm create stellarnovaui' in any folder. You can see stellarnovaui will be cloned.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/02lrwothnkfkdm8h21lm.png)

The final source code: https://github.com/markliu2013/StellarNovaUI