记录了我使用nodejs编写自己的cli工具,入门级别的,大神勿喷,谢谢。
创建cli工具项目
- 创建一个文件夹,我就起名叫
demo-cli
吧,然后进入这个文件夹,使用npm
命令创建一个package.json
文件,并安装工具包
mkdir demo-cli
cd demo-cli
npm init -y
# 工具包可根据个人喜好选择安装,后边会对每个包的用处做解释
npm install commander figlet chalk clear ora download-git-repo open handlebars watch --save
- 创建
bin
目录,然后在bin
目录中创建一个入口文件
mkdir bin
cd bin
touch index.js
- 在
index.js
文件中先打印个hello demo-cli
console.log('hello demo-cli')
- 在
package.json
文件中增加
"demo": "./bin/index.js"
- 然后在终端使用npm命令,将cli工具link到全局
npm link
- 链接成功后在终端输入
demo
回车,如在终端输出hello demo-cli字样,说明demo-cli
的命令就成功了
demo
hello demo-cli
配置cli版本号
- 没有安装
commander
工具包的需要安装一下,这个工具包就是用来配置cli
命令,我们下边先弄个cli版本号感受一下
npm i commander --save
- 在
bin/index.js
文件中,删掉之前打印hello demo-cli的测试代码,引入commander
包,配置版本号,终端使用demo --version
可查看
// 导入package.json文件为了取其中的version
const pkg = require('./../package.json')
const commander = require('commander')
commander.version(pkg.version) // 这就配置了版本号
配置cli的init命令
commander
.command('init <name>') // init命令后边name就是项目名称
.description('init project') // init命令的描述
.action(name => { // init命令具体要做的事就写在这里
console.log('init ' + name)
})
- 后边我们可以新建一个
init.js
文件,将action的回调抽离到init.js
中编写,那么action就变成下边这样写
.action(require(./init.js))
配置cli的欢迎语(可选)
- 这里会用到的工具包是
figlet
(个性字体)、chalk
(字体颜色)、clear
(清屏) 我们将
// 创建bin/init.js文件
const { promisify } = require('util') // node.js API直接用,不需要单独安装工具包
// figlet是用callback异步返回的,这里我们将它包装成返回一个promise
const figlet = promisify(require('figlet'))
const clear = require('clear')
const chalk = require('chalk')
// 重写一个log方法用来打印,是对console.log的包装,文本颜色这里用的是绿色
const log = (content) => console.log(chalk.green(content))
const init = async (name) => {
// 清屏(可选),是否清屏幕个人喜好决定
clear()
// 使用figlet改变得到个性化字体文本
const welcome = await figlet('demo-cli welcome')
// 打印欢迎语
log(welcome)
log('init ' + name)
}
module.exports = init
- 来看下效果吧,终端输入命令
demo init hehe
这样我们就看到了一个比较漂亮的欢迎界面
下载项目模板
- 需要使用的工具包
download-git-repo
(从GitHub
下载模板项目) - 新建
bin/download.js
文件
const { promisify } = require('util')
const clone = async (repo, name) => {
// 使用promisify包装一下
const download = promisify(require('download-git-repo'))
const ora = require('ora') // 类似loading的状态提示
const p = ora(`下载:${repo}`) // 定义文本内容
p.start() // 开始转
await download(repo, name) // 从GitHub下载项目到指定{name}目录下
p.succeed() // 变对号✅
}
module.exports = { clone }
- 在
init.js
中导入download.js
中的clone
方法,在init
方法中调用,并传入GitHub
的repo
地址和name
项目名称
const { clone } = require('./download')
const init = async (name) => {
// 清屏(可选)
clear()
// 使用figlet得到个性化字体文本
const welcome = await figlet('demo-cli welcome')
// 打印欢迎语
log(welcome)
log(`🚗 创建项目:${name} \n`)
// 从GitHub下载项目模板,举个栗子🌰
// 项目地址是这样:https://github.com/vipsunwei/cnode-react-demo
// 我们需要的是去掉前边协议和域名后剩下的部分
// 那么clone方法需要传的repo应该等于
// github:vipsunwei/cnode-react-demo
await clone('github:vipsunwei/cnode-react-demo', name)
log(`\n🚗 创建完成`)
// 这里可以做个小提示,进入项目所在目录进行安装项目依赖
console.log(`-------------- \n`)
log(`cd ${name} \n`)
log(`npm install`)
log('or')
log(`yarn install \n`)
console.log(`--------------`)
}
- 好了我们试一下,执行命令我们看到了正在从
GitHub
上clone
项目模板下来
demo init hehe
- clone完成后会看到下图:
自动安装项目依赖
- 用
node.js
提供的child_process
进行安装项目依赖
// 由于spawn是异步的,先用promise封装一下
const spawn = async (...args) => {
const {spawn} = require('child_process')
return new Promise(resolve => {
const child = spawn(...args)
// 将子进程的输出接到主进程上
child.stdout.pipe(process.stdout)
// 将子进程的错误接到主进程上
child.stderr.pipe(process.stderr)
// 当子进程关闭说明子进程执行完毕,执行resolve()
child.on('close', () => resolve())
})
}
- 修改
init
方法的代码,在init
方法中执行spawn
进行安装项目依赖
const init = async (name) => {
...
...
log(`🚗 创建项目:${name} \n`)
// 从GitHub下载项目模板
await clone('github:vipsunwei/cnode-react-demo', name)
log(`\n🚗 创建完成`)
log(`🔨 安装依赖`)
await spawn('yarn', ['install'], {cwd: `./${name}`})
// 这里的提示语根据你自己的模板项目使用的包管理器自行修改,我的包管理器使用的是 yarn
// 执行启动命令根据你自己的模板项目 package.json -> scripts 中的定义自行修改
log(`
👌 安装完成
➡️ 启动项目
----------------
cd ${name}
yarn run start
----------------
`)
}
- 我们试一下看看效果,记得把之前测试生成的项目先删掉
demo init hehe
自动启动项目并且打开浏览器
- 上面我们给了手动启动项目的提示,我们也可以使用程序自动启动项目,并且也可以使用
open
自动打开浏览器(可选)
// 继续修改init方法
const init = async (name) => {
...
...
log(`🔨 安装依赖`)
await spawn('yarn', ['install'], {cwd: `./${name}`})
log(`
👌 安装完成
➡️ 启动项目
----------------
cd ${name}
yarn run start
----------------
`)
// 自动启动项目
await spawn('yarn', ['run', 'start'], {cwd: `./${name}`})
// 自动打开浏览器(可选),如果你使用webpack也可以在模板项目的webpack-dev-server中配置
require('open')()
}
- 创建个项目下面看下效果
demo init hehe
- 代码编译的同时浏览器也自动打开了,浏览器打开后会白屏
loading
,这是项目代码还没有完全编译完毕,编译完毕提示项目已经成功的在3000
端口上运行起来了,浏览器会自动更新出首页内容