cli搭建

作者: yonglei_shang | 来源:发表于2020-12-08 11:17 被阅读0次

我们都知道 vue-cli 可是这cli 是怎么实现的呢,下面我们来实现一个自己的 cli

解决痛点:

  • 项目配置繁琐、耗时,重复无意义的工作
  • 项目结构不统一、不规范
  • 前端项目类型繁多,不同项目不同配置,管理成本高
  • 脚手架也可以是一套命令集,不只用来创建项目

用到的 node 知识

process.argv 接收命令行参数process.cwd()获取当前文件绝对地址

// 终端执行
my-cli init projectName

process.argv
/* [
  'D:\\Program Files\\nodejs\\node.exe',
  'C:\\Users\\USER\\AppData\\Roaming\\npm\\node_modules\\s-cli\\bin\\cli',
  'init',
  'projectName'
] */
process.cwd()
/*
  E:\study\node\template
*/

常用到的几个库

commander命令行界面
用来编写指令和处理命令行参数的

const program = require('commander')
// 程序命令分析、处理
program.version('1.0.0')
    .command('init <name>')
    .description('generate a new project')
    .action(name => {
        console.log(name)
    })
program.parse(process.argv)

yargs处理命令行参数

类似 commander,还有minimist库,都是处理命令行参数

var argv = require('yargs').argv;
console.log('hello ', argv.name);

// 测试
s-cli --name=woniu    // hello  woniu

inquirer

输入交互库,就是你在创建项目的时候,输入项目名,项目描述等等

const inquirer = require('inquirer')
const promptList = [
    {
        type: 'input', // type: 默认 input,还可以是:number, confirm, list, rawlist, expand, checkbox, password, editor
        name: 'name',
        default: 'vue2-m-template',
        message: 'project name:'
    },
    {
        type: 'input',
        name: 'version',
        default: '1.0.0',
        message: 'version:'
    },
    {
        type: 'input',
        name: 'description',
        message: 'project description:'
    },
    {
        type: 'input',
        name: 'author',
        message: 'author:'
    }
]
inquirer.prompt(promptList).then(async answer => {
            console.log(answer)
        })

参考

shelljs

-f:强制删除文件;
-i:删除之前先询问用户;
-r:递归处理目录;
-v:显示处理过程;
echo:在控制台输出指定内容
exit(code):以退出码为code退出当前进程

const shell = require('shelljs')
shell.rm('-rf','out/test');//强制递归删除out/Release目录
shell.cp('-r','stuff/','out/test');//将`stuff/`中所有内容拷贝至`out/Release`目录
shell.mv(`./ssr-with-${language}`, `./${option.appName}`) // 把文件移到文件

参考

ora加载工具
显示loading动画

const ora = require('ora');
const ora1 = ora('正在创建...')
ora1.start()
setTimeout(function(){
    ora1.succeed('构建成功');
}, 1000)

chalk
用来修改控制台输出内容样式的,比如颜色

const chalk = require('chalk')
const glog = data => console.log(chalk.green(data))
const rlog = data => console.log(chalk.red(data))
glog('green word')
rlog('red word')

git-clone
用来下载远程模板,支持 github、gitlab等。

const gitClone = require('git-clone')
// 拉取模板
const cloneRp = (answer) => {
    return new Promise((resolve, reject) => {
        gitClone('https://github.com/woniu1112/vue2-m-template.git', path.resolve(process.cwd(), answer.name), null, () => {
            // glog('拉取完成')
            ora1.succeed(`创建完成,项目地址:${path.resolve(process.cwd(), answer.name)}`)
            resolve()
        })
    })
}

扩展:

Github API 接口

// RC 配置下载模板的地方,给 github 的 api 使用
https://api.github.com/users/xxx/repos // https://api.github.com/${type}/${registry}/repos
curl -u name:secret https://api.github.com/user/repos
https://api.github.com/users/xxx // xxx的用户信息,包含粉丝数
https://api.github.com/users/xxx/keys  // xxx的 ssh key
https://api.github.com/users/xxx/gpg_keys // xxx的gpg
https://api.github.com/users/xxx/followers?page=2 // xxx的粉丝
https://api.github.com/users/xxx/following?page=2 // xxx关注谁
https://api.github.com/orgs/bitcoin bitcoin项目的信息
https://api.github.com/orgs/bitcoin/repos bitcoin项目的repos
https://api.github.com/repos/bitcoin/bitcoin
https://api.github.com/repos/bitcoin/bitcoin/contributors bitcoin的贡献者

github API 相关

Handlebars模板引擎

const Handlebars = require('handlebars')
var template = Handlebars.compile("Handlebars <b>{{doesWhat}}</b>");
console.log(template({ doesWhat: "rocks!" }));  // 进行编译并输出

ini 格式转换

const fs = require('fs'), { decode, encode } = require('ini')
const config = decode.parse(fs.readFileSync('./config.ini', 'utf-8'))
/*
{
  '<!DOCTYPE html>': true,
  '<html lang': '"en">',
  '<head>': true,
  '<meta charset': '"UTF-8">',
  '<meta name': '"viewport" content="width=device-width, initial-scale=1.0">',
  '<title>Document</title>': true,
  '</head>': true,
  '<body>': true,
  '<p>ddddddddddd</p>': true,
  '</body>': true,
  '</html>': true
}
*/

简单 的cli 示例

#!/usr/bin/env node

const program = require('commander')
const chalk = require('chalk')
const path = require('path')
const inquirer = require('inquirer')
const ora = require('ora')
const gitClone = require('git-clone')
const fs = require('fs')
const shelljs = require('shelljs')

// console.log(process.argv)
const glog = data => console.log(chalk.green(data))
const rlog = data => console.log(chalk.red(data))

const ora1 = ora('正在创建...')

const promptList = [
    {
        type: 'input', // type: 默认 input,还可以是:number, confirm, list, rawlist, expand, checkbox, password, editor
        name: 'name',
        default: 'vue2-m-template',
        message: 'project name:'
    },
    {
        type: 'input',
        name: 'version',
        default: '1.0.0',
        message: 'version:'
    },
    {
        type: 'input',
        name: 'description',
        message: 'project description:'
    },
    {
        type: 'input',
        name: 'author',
        message: 'author:'
    }
]

// 拉取模板
const cloneRp = (answer) => {
    return new Promise((resolve, reject) => {
        gitClone('https://github.com/woniu1112/vue2-m-template.git', path.resolve(process.cwd(), answer.name), null, () => {
            // glog('拉取完成')
            ora1.succeed(`创建完成,项目地址:${path.resolve(process.cwd(), answer.name)}`)
            resolve()
        })
    })
}

// 读取package.json 文件并修改部分内容
const readPackageFile = (answer) => {
    return new Promise((resolve, reject) => {
        fs.readFile(path.resolve(process.cwd(), `${answer.name}/package.json`),'utf-8', async (err, result) => {
            result = JSON.parse(result)
            result = Object.assign(result, answer)
            await writePackageFile(result)
            resolve()
        })
    })
}

// 写入 package.json 文件
const writePackageFile = (content) => {
    return new Promise((resolve, reject) => {
        fs.writeFile(path.resolve(process.cwd(), `${content.name}/package.json`), JSON.stringify(content, null , 4), 'utf-8', (err, result) => {
            rmGitFile(content.name)
            glog(`you can do
=========================
cd ${content.name}
npm install
npm run dev
=========================
                `)
            resolve()
        })
    })
}

// 删除 .git  文件
const rmGitFile =  function (projectName) {
    shelljs.rm('-rf', path.resolve(process.cwd(), `${projectName}/.git`))
}

// 程序命令分析、处理
program.version('1.0.0')
    .command('init <name>')
    .description('generate a new project')
    .action(name => {
        promptList[0].default = name
        inquirer.prompt(promptList).then(async answer => {
            console.log(answer)
            ora1.start()
            await cloneRp(answer)
            await readPackageFile(answer)
        })
    })
program.parse(process.argv)

扩展
扩展:npm link把my-cli注册到全局,然后链接到工程目录,可以直接去执行(把当前目录放到当前环境变量)

// package.json bin
{
  "name": "syl-cli",
  "version": "1.0.7",
  "description": "",
  "main": "index.js",
  "bin": {
    "syl-cli": "./bin/index.js" //告诉package.json,我的bin叫q-cli,它可执行的文件路径是bin/q-cli.js
  },
  "dependencies": {
    "chalk": "^4.1.0",
    "commander": "^6.2.0",
    "download-git-repo": "^3.0.2",
    "git-clone": "^0.1.0",
    "inquirer": "^7.3.3",
    "ora": "^5.1.0",
    "shelljs": "^0.8.4"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "syl",
  "license": "ISC"
}

you can do

npm install syl-cli -g

这是我的一个简单的cli

相关文章

网友评论

    本文标题:cli搭建

    本文链接:https://www.haomeiwen.com/subject/ayrfgktx.html