认识 cli 工具

Node.js 命令行工具(CLI)是一种常用于开发和部署应用程序的工具。与图形化界面不同,命令行工具通常以文本模式进行操作,通过输入命令和参数来完成特定的任务。

下面是 Node.js 命令行工具的一些特点:

  1. 命令行工具基于 Node.js 平台,因此具有跨平台的优势,可以在 WindowsMac OSLinux 上运行。

  2. 命令行工具通常使用命令行参数来接收输入,并且执行效率比图形化界面高。

  3. 命令行工具通常可以很容易地集成到工作流程中,并可以通过自定义脚本轻松批量处理任务。

  4. 命令行工具可以通过标准输出(STDOUT)将输出信息传递给用户,例如成功或错误消息、进度说明等,并可以轻松将输出内容重定向到文件或其他程序中进行后续处理。

  5. 命令行工具通常可以与其他工具或服务集成,例如版本控制系统、云平台服务等,以提高生产力。

总之,Node.js 命令行工具是一种强大而灵活的工具,可以帮助开发人员进行自动化配置和任务处理,并提供了更高效、更灵活的工作方式。

初始化 cli 项目

  1. 在项目根目录下执行 npm init 命令,创建 package.json 文件

  2. 在项目根目录下创建 bin 文件夹,内部创建 index.js 文件

mycli/
├── bin/
│   └── index.js
└── package.json
  1. package.json 文件中添加如下配置:
"bin": {
    "mycli": "./bin/index.js"
 },
  1. bin/index.js 中添加如下代码:
// 告诉操作系统用 Node 来运行此文件
#!/usr/bin/env node 

console.log('hello CLI');
  1. 在项目根目录下执行 npm link 命令,将当前 cli 挂载到全局中,此后,在任意命令行窗口执行 mycli 命令,即可打开该 cli ,并执行其中代码

commander 工具使用

修改 bin/index.js 中内容如下:

#!/usr/bin/env node
console.log(process.argv);

在任意命令行窗口执行 mycli 命令结果如下:

在这里插入图片描述

其中第一个元素值为执行当前 cli 文件的环境位置,第二个元素值为当前 cli 文件所在位置

可以带参数执行,例如执行 mycli --help ,结果如下:

在这里插入图片描述

可以看出,多了第三个元素,元素值即为执行 cli 是所带的参数,于是我们可以通过判断参数值来使得 cli 执行不同的任务,但是如果仅通过 if~else 或者 swicth~case 来控制的话,分支未免太多,太复杂,所以就出现了Commander工具

介绍

commander 是一个基于 Node.js 的命令行界面,用于快速开发命令行工具。通过使用 commander,开发者可以更加方便地实现命令行程序的处理和交互。

使用 commander 可以轻松定义命令及选项,并为每个命令添加自定义回调函数,命令行工具的参数解析等都可以通过 commander 来完成。同时,commander 还提供了丰富的 API,可以用于输出帮助信息、管理版本号等。

具体用法参考官方文档:https://github.com/tj/commander.js/blob/HEAD/Readme_zh-CN.md

基础使用

首先在项目根目录下执行 npm install commander 命令安装 commander,然后修改bin/index.js如下:

#!/usr/bin/env node
const program = require('commander');

program.parse(process.argv); // 表示使用 Commander 来处理命令行参数

有了上面代码后,commander 会默认配置 --help 参数,执行 mycli --help ,结果如下:

自定义命令

然后修改bin/index.js如下:

#!/usr/bin/env node
const program = require('commander');

program.command('create <project> [other...]') // 自定义 create 命令,接收一个必填参数 project,[other...] 表示其他参数
  .alias('crt') // 别名,之后运行 mycli create ... 和 mycli crt ... 效果是一样的
  .description('创建项目') // 描述
  .action((project, args) => { // 命令行的执行逻辑代码
    console.log(project)
    console.log(args)
  })

program.parse(process.argv);

执行效果如下:

在这里插入图片描述

同时,commander 默认会将该命令添加到 --help 的显示结果中:

在这里插入图片描述

逻辑封装

接下来可以按照下面方式将代码封装一下,从而将 cli--help 的处理、对命令的定义和命令所要执行的方法拆分开来,更符合模块化的开发思想:

mycli/
├── node_modules/ 
├── bin/
│   └── index.js
├── lib/
│   └── core/
│       ├── action.js(内部定义cli为命令提供的处理逻辑)
│       ├── commander.js(内部定义cli支持的命令)
│       └── help.js(内部定义cli对--help命令的处理逻辑)      
├── package.json
└── package-lock.json
#!/usr/bin/env node
const program = require('commander');
const myhelp = require('../lib/core/help')
const myCommander = require('../lib/core/commander')

myhelp(program)
myCommander(program)

program.parse(process.argv);
const myhelp = function (program) {
  program.version('1.0.0') // 这里可以顺便添加版本信息
  program.option('-f --framework <framework>', '设置框架')
}

module.exports = myhelp

版本信息打印效果:

在这里插入图片描述

const myAction = require('./action')

const myCommander = function (program) {
  program.command('create <project> [other...]') // 自定义 create 命令,接收一个必填参数 project,[other...] 表示其他参数
    .alias('crt') // 别名,之后运行 mycli create ... 和 mycli crt ... 效果是一样的
    .description('创建项目') // 描述
    .action(myAction)
}

module.exports = myCommander
const myAction = (project, args) => { // 命令行的执行逻辑代码
  console.log(project)
  console.log(args)
}

module.exports = myAction

inquirer 工具使用

我们知道,很多 cli 工具是可以与用户进行交互的,最基本的例子就是 npm 在执行 npm init 之后,会在命令行窗口显示很多问题,而只有用户在回答了这些问题之后,npm 才会根据用户回答,来创建 package.json 文件。而要实现这种效果,要借助一个工具:inquirer

介绍

inquirer 是一个基于 Node.js 的交互式命令行工具,可以用于收集用户的输入信息。用户可以通过选择列表、输入文本、确认等方式来提供信息,使得命令行交互的过程更加友好和高效。具体用法可参考其官方文档:https://github.com/SBoudrias/Inquirer.js/blob/master/packages/inquirer/README.md

基础使用

首先我们需要执行 npm install inquirer 将其安装到项目中,然后我们修改 lib/action.js 如下:

const inquirer = require('inquirer')

const myAction = (project, args) => {
  // 命令行的执行逻辑代码
  inquirer.prompt([{ // 数组中每一个对象表示一个问题
    type: 'list', // 问题类型,list表示选择列表
    name: 'framework', // 用于接收答案的键值
    choices: ['express', 'koa', 'egg'], // 选项
    message: '请选择你所使用的框架' // 问题
  }]).then(answer => {
    console.log(answer)
  })
}

module.exports = myAction

接下来在命令行中输入 mycli create projectName 就会出现这种效果:

在这里插入图片描述

选择 koa 后:

在这里插入图片描述

逻辑封装

为了更好的模块化,可以将答案封装在一个单独的配置文件中,在项目根目录下新增 config.js 文件:

mycli/
├── node_modules/ 
├── bin/
│   └── index.js
├── lib/
│   └── core/
│       ├── action.js(内部定义cli为命令提供的处理逻辑)
│       ├── commander.js(内部定义cli支持的命令)
│       └── help.js(内部定义cli对--help命令的处理逻辑) 
├── config.js
├── package.json
└── package-lock.json
module.exports = {
  framework: ['express', 'koa', 'egg']
}
const inquirer = require('inquirer')
const config = require('../../config')

const myAction = (project, args) => {
  // 命令行的执行逻辑代码
  inquirer.prompt([{ // 数组中每一个对象表示一个问题
    type: 'list', // 问题类型,list表示选择列表
    name: 'framework', // 用于接收答案的键值
    choices: config.framework, // 选项
    message: '请选择你所使用的框架' // 问题
  }]).then(answer => {
    console.log(answer)
  })
}

module.exports = myAction

download-git-repo 工具使用

接下来我们需要根据用户选择去为用户下载对应的框架模板项目,那么下载这个动作改怎么做呢?这就引出了远程仓库下载工具 download-git-repo

介绍

download-git-repo 是一个 Node.js 模块,用于从 Git 仓库中下载代码或文件。该模块可以下载 GitHub、GitLab 和 Bitbucket 等平台上的 Git 仓库,支持使用 HTTPS 或 SSH 协议进行下载。具体用法可参考官方文档:https://gitlab.com/flippidippi/download-git-repo

基础使用 + 逻辑封装

修改 lib/core/action.jsconfig.js 如下:

module.exports = {
  // 可选择的框架
  framework: ['express', 'koa', 'egg'],
  // 框架对应的仓库地址
  frameworkUrls: {
    express: 'git@gitee.com:gao-shunpeng/express-template.git',
    koa: 'git@gitee.com:gao-shunpeng/koa-template.git',
    egg: 'git@gitee.com:gao-shunpeng/egg-template.git'
  }
}
const inquirer = require('inquirer')
const config = require('../../config')
const download = require('download-git-repo')

const myAction = async (project, args) => {
  // 命令行的执行逻辑代码
  const answer = await inquirer.prompt([{ // 数组中每一个对象表示一个问题
    type: 'list', // 问题类型,list表示选择列表
    name: 'framework', // 用于接收答案的键值
    choices: config.framework, // 选项
    message: '请选择你所使用的框架' // 问题
  }])
  // console.log(answer)
  // 下载代码模板
  
  /* 参数一是要下载的仓库对应的远程地址
   * 参数二是下载后保存的目录路径,可以是相对路径或绝对路径。如果目录不存在,则会自动创建。这里使用用户执行   
   * mycli create projectName 时输入的 projectName 值作为保存目录名
   * 参数三是下载选项,可以不传递,默认为 { clone: true },表示使用 Git 命令进行克隆。如果设置为 { clone: 
   * false },则表示直接从 Git 仓库中下载。
   * 参数四是一个回调函数,
   */
  download(`direct:${config.frameworkUrls[answer.framework]}`, project, { clone: true }, err => {
    console.log(err)
  })

}

module.exports = myAction

这样,在命令行中执行 mycli create projectName 后,就会自动在命令行坐在目录创建名为 ${ projectName } 的目录,并在其中下载用户选择的对应模板项目

ora 工具使用

这时候我们希望在仓库代码下载过程中,给使用者一些提示信息,如 loading 状态等,这时候我们就可以通过加载交互工具 ora 来实现

介绍

ora 是一个命令行加载动画效果的工具,可以在命令行中展示加载动画,增强用户体验,它的特点是易使用,配置简单,支持多种加载动画效果如显示进度条、旋转动画和状态文本等等。具体用法参考官方文档:https://github.com/sindresorhus/ora

基础使用

首先我们需要下载 ora ,因为其在 6.x.x 版本之后不再使用 CommonJS 模块化规范而转为使用的 ES6 的模块化规范,所以我们要在项目根目录执行 npm install ora@5 来下载对应的最新 5.x.x 版本

然后修改 lib/core/action.js 如下:

const inquirer = require('inquirer')
const config = require('../../config')
const download = require('download-git-repo')
const ora = require('ora')

const myAction = async (project, args) => {
  // 命令行的执行逻辑代码
  const answer = await inquirer.prompt([{ // 数组中每一个对象表示一个问题
    type: 'list', // 问题类型,list表示选择列表
    name: 'framework', // 用于接收答案的键值
    choices: config.framework, // 选项
    message: '请选择你所使用的框架' // 问题
  }])
  // console.log(answer)
  // 下载代码模板
  const spinner = ora().start() // 创建实例并启动加载指示器
  spinner.text = '代码正在下载……' // 下载过程中在命令行展示的 loading 信息
  download(`direct:${config.frameworkUrls[answer.framework]}`, project, { clone: true }, err => {
    if (!err) {
      spinner.succeed('代码下载成功')
      // 下载成功后,提示使用者j接下来可执行的操作
      console.log('Done! you run:')
      console.log('cd ' + project);
      console.log('npm install ');
      console.log('npm run dev ');
    } else {
      spinner.fail('代码下载失败')
    }
  })

}

module.exports = myAction

这样就可以在下载模板的过程中,展示一个动态的加载状态如下了:
在这里插入图片描述

chalk 工具使用

现在如果我们想搞得再花里胡哨点,比如使用各种颜色来展示之前的那些提示信息,我们可以使用命令行渲染工具 chalk 来实现

介绍

chalk 是一个 Node.js 模块,用于在控制台输出带有颜色的文本。它可以使控制台输出更加美观和易读。

使用 chalk 可以很方便地设置输出的文本的颜色、背景色等样式。常见的样式包括:

  • 字体颜色:.red.green.yellow.blue.magenta.cyan.gray 等;

  • 背景颜色:.bgRed.bgGreen.bgYellow.bgBlue.bgMagenta.bgCyan.bgGray 等;

  • 其他样式:.bold.underline.italic 等。

例如,以下代码可以将输出的文本设置为黄色加粗字体:

const chalk = require('chalk');
console.log(chalk.yellow.bold('Hello, world!'));

需要注意的是,使用 chalk 时应该避免过于花哨的样式,否则会影响阅读体验。具体用法参考官方文档:https://github.com/chalk/chalk

基础使用

首先我们需要下载 chalk ,因为其在 5.x.x 版本之后不再使用 CommonJS 模块化规范而转为使用的 ES6 的模块化规范,所以我们要在项目根目录执行 npm install chalk@4 来下载对应的最新 4.x.x 版本

然后修改 lib/core/action.js 如下:

const inquirer = require('inquirer')
const config = require('../../config')
const download = require('download-git-repo')
const ora = require('ora')
const chalk = require('chalk')

const myAction = async (project, args) => {
  // 命令行的执行逻辑代码
  const answer = await inquirer.prompt([{ // 数组中每一个对象表示一个问题
    type: 'list', // 问题类型,list表示选择列表
    name: 'framework', // 用于接收答案的键值
    choices: config.framework, // 选项
    message: '请选择你所使用的框架' // 问题
  }])
  // console.log(answer)
  // 下载代码模板
  const spinner = ora({ color: 'yellow' }).start()
  spinner.text = '代码正在下载……'
  download(`direct:${config.frameworkUrls[answer.framework]}`, project, { clone: true }, err => {
    if (!err) {
      spinner.succeed('代码下载成功')
      console.log(chalk.green.bold('Done!'), chalk.yellow('you run:'));
      console.log(chalk.blue.bold('cd ') + chalk.yellow(project));
      console.log(chalk.blue.bold('npm install'));
      console.log(chalk.blue.bold('npm run dev '));
    } else {
      spinner.fail('代码下载失败')
    }
  })

}

module.exports = myAction

这样就可以在命令行中展示五颜六色的提示信息如下:
在这里插入图片描述

逻辑封装

我们可以把下载远程库的具体操作单独封装起来,封装后的项目文件目录结构如下:

mycli/
├── node_modules/ 
├── bin/
│   └── index.js
├── lib/
│   └── core/
│       ├── action.js(内部定义cli为命令提供的处理逻辑)
│       ├── commander.js(内部定义cli支持的命令)
│       ├── download.js(内部定义下载远程代码库的具体处理逻辑)
│       └── help.js(内部定义cli对--help命令的处理逻辑) 
├── config.js
├── package.json
└── package-lock.json

lib/core 下新建 download.js 文件,内容如下:

const download = require('download-git-repo')
const ora = require('ora')
const chalk = require('chalk')

const downloadFun = function (url, project) {
  const spinner = ora({ color: 'yellow' }).start()
  spinner.text = '代码正在下载……'
  download(`direct:${config.frameworkUrls[answer.framework]}`, project, { clone: true }, err => {
    if (!err) {
      spinner.succeed('代码下载成功')
      console.log(chalk.green.bold('Done!'), chalk.yellow('you run:'));
      console.log(chalk.blue.bold('cd ') + chalk.yellow(project));
      console.log(chalk.blue.bold('npm install'));
      console.log(chalk.blue.bold('npm run dev '));
    } else {
      spinner.fail('代码下载失败')
    }
  })
}

module.exports = downloadFun

修改 lib/core/action.js 如下:

const inquirer = require('inquirer')
const config = require('../../config')
const downloadFun = require('./download')


const myAction = async (project, args) => {
  // 命令行的执行逻辑代码
  const answer = await inquirer.prompt([{ // 数组中每一个对象表示一个问题
    type: 'list', // 问题类型,list表示选择列表
    name: 'framework', // 用于接收答案的键值
    choices: config.framework, // 选项
    message: '请选择你所使用的框架' // 问题
  }])

  // 下载代码模板
  downloadFun(config.foramworkUrl[answer.framwork], project)
}

module.exports = myAction
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐