手写一个cli工具

497 阅读1分钟

cli 工具实现

1、新建文件夹 my-cli

执行以下命令生成package.json文件

npm init -y

2、新建 bin 文件

新建 hui.js文件

#!/usr/bin/env node

console.log('my-cli 测试')

// 定制命令
const program = require('commander')

const initFunc = require('../lib/init') // 初始化函数

program.version(require('../package.json').version)

program.command('init <name>').description('init project').action(initFunc)

// 解析
program.parse(process.argv)

// console.log('process.argv:')
// console.log(process.argv)
// console.log('params:')
// const params = program.parse(process.argv)
// console.log(params)

在package.json 添加

  "bin": {
    "hui": "./bin/hui.js"
  },

3、全局安装,或者模拟安装

当npm包发布后,可以通过 hui 全局执行,若是本地的话可以通过在 my-cli文件下执行npm link 命令

4、在lib新建init、download的文件

lib/init.js文件

const { promisify } = require('util')

const figlet = promisify(require('figlet')) // 大字

const clear = require('clear') // 清屏

const chalk = require('chalk') // 改色输出

const { clone } = require('./download.js')

const log = (content) => console.log(chalk.green(content))

module.exports = async (name) => {
  clear()
  console.log('执行')
  const data = await figlet('my-cli welcome')
  log('创建项目:' + name)
  log(data)
  const { clone } = require('./download.js')
  await clone('github:JeffyEvergarden/learn-ts', name)
}

download.js文件

const { promisify } = require('util')
module.exports.clone = async function (repo, desc) {
  const download = promisify(require('download-git-repo'))
  const ora = require('ora') // 显示loading
  const process = ora(`下载……${repo}`)
  try {
    process.start()
    await download(repo, desc)
    process.succeed()
  } catch (error) {
    console.log(error)
    process.succeed()
  }
}

5、安装依赖

注意的地方,如果运行环境是win32,需要

process.platform === 'win32' ? 'npm.cmd' : 'npm'

添加 install.js 文件

// 安装依赖

const spawn = async (...args) => {
  const { spawn } = require('child_process')

  return new Promise((resolve) => {
    args[0] = process.platform === 'win32' ? 'npm.cmd' : args[0]
    const proc = spawn(...args) // 得到命令流
    proc.stdout.pipe(process.stdout) // 导到控制台输出流
    proc.stderr.pipe(process.stderr) // 导到错误流
    proc.on('close', () => {
      resolve()
    })
  })
}

module.exports = spawn

主方法添加 这段代码

  // ...
  try {
    await spawn('npm', ['install', 'lodash'], { cwd: `./${name}` })
    log(chalk.green(`安装依赖完成`))
  } catch (err) {
    console.log(err)
    log(chalk.green(`安装依赖失败`))
  }
  // ...

6、默认打开浏览器,运行 npm run

const open = require("open")
open(`http://localhost:3000`);
await spawn('npm', ['run', 'serve'], { cwd: `./${name}` })

7、包的描述

A. 关于 download-git-repo 包

该包用来下载仓库的代码的

仓库使用方式

GitHub - github:owner/name or simply owner/name

GitLab - gitlab:owner/name

Bitbucket - bitbucket:owner/name

范例:

github:JeffyEvergarden/learn-ts

或者使用 direct 直接粘贴gitclone的地址

Direct - direct:url

direct:url#my-branch

B. 关于 spawn 包

出自child_process包,node自带

8、约定式路由

写界面,自定义生成route

abs模板库

const fs = require('fs')

const handlebars = require('handlebars')

const chalk = require('chalk')

function compile(meta, filePath, templatePath) {
  if (fs.existsSync(templatePath)) {
    const content = fs.readFileSync(templatePath).toString() // 得到模板
    const result = handlebars.compile(content)(meta) // 得到渲染函数
    fs.writeFileSync(filePath, result) // 写进文件
  }
}

module.exports = async () => {
  const list = fs.readdirSync('./views').map((v) => ({
    name: v.replace('.vue', '').toLowerCase(),
    file: v,
  }))

  compile({ list }, './src/router.js', './template/router.js.hbs')
}

发布

新建publish.sh

#!/usr/bin/env bash 
npm config get registry 
# 检查仓库镜像库 
npm config set registry=http://registry.npmjs.org 
echo '请进行登录相关操作:' npm login 
# 登陆 echo "-------publishing-------" 
npm publish 
# 发布 
npm config set registry=https://registry.npm.taobao.org 
# 设置为淘宝镜像 
echo "发布完成" exit

遇见的坑:

1、区别于mac系统,window运行spawn时,如果是win32,需要改为npm.cmd

2、spawn('npm', ['run', 'serve'], { cwd: ./${name} }), cwd表示的是当前目录

3、sh在window命令行上运行不了

4、npm发布的包只有在dependencies安装时才会有依赖,在dev下发布的包是不会安装这些依赖的。

5、需要关注的包去研究 handlebars(模板转渲染)、child_process(子进程的包研究)、download-git-repo (下载仓库的包,关于仓库的规则,不支持码云)