【npm】搞个自己的CLI拉取基础工程

2019-12-16  本文已影响0人  前端菜篮子

一、CLI原理

CLIcommand-line interface — 命令行界面):commander 这个node库,有很多cli使用了它,它可以帮助开发者简化实现命令流程。不过我们的第一步还是应该先搞清楚,如何通过npm如何实现命令行。(其实就和普通的bat命令一样)

一个简单的cli
package.json 中有一个 bin 字段,指定各个内部命令对应的可执行文件的位置。
②在包安装时,如果是全局安装,npm 将会把 package.json 里定义的 bin 文件软连接到全局 node_modules/bin
③如果是非全局安装,会软链接到项目文件夹./node_modules/.bin/
④根据下面代码的配置,当我们全局安装此包后,在任意位置运行 cli-test,都会执行全局 node_modules 中的 cli-test 文件。

/*cli-test 的 package.json*/
"bin": {
    "cli-test": "./bin/cli-test"
}

cli文件:
下面的第一行必写,是告诉操作系统这个文件中的代码用node可执行程序去运行它。 后面就做我们要做的事情就行了 。

#!/usr/bin/env node
//do something

一个简单的cli就完成了
其实有很多三方cli也并没有用类似 commandernode 库,如果我们的 cli 足够简单,以上这样就可以了。


二、commandernode.js命令行界面的完整解决方案

1. Commander.js中文文档链接一
2. Commander.js中文文档链接二
3. 仿vue的前端自定义cmd命令拉取项目脚手架

那下面开练吧! 我们先来看到脚手架工程中可能会用到的js~

A. commander.js

//安装
npm install commander --save
//调用
const program = require("commander");

B. chalk.js:修改控制台中字符串的样式【打印日志的时候,根据日志级别的不同,显示不同的颜色】,包括:①字体样式(加粗、隐藏等);②字体颜色;③背景颜色。 创建log.js如下:


//安装
npm install chalk --save-dev

//调用
const chalk = require('chalk');
const log = console.log;

const success = function (msg) {
  log(chalk.bgGreen(' SUCCESS ') + chalk.green(' ' + msg));
}

const warn = function (msg) {
  log(chalk.bgYellow(' WARN ') + chalk.yellow(' ' + msg));
}

const error= function (msg) {
  log(chalk.bgRed(' ERROR') + chalk.red(' ' + msg));
}

const info = function (msg) {
  log(chalk.bgBlackBright(' INFO ') + chalk.gray(' ' + msg));
}

const tip = function (msg) {
  log(chalk.bgBlue(' TIP') + chalk.blue(' ' + msg));
}

module.exports = { success, warn, error, info, tip }

C. fs-extra.js:文件操作相关工具库,该模块是系统fs模块的扩展,提供了更多便利的 API,并继承了fs模块的 API

//安装
npm install --save-dev fs-extra
//调用
const fs = require("fs-extra");

相关API了解下:


a. 文件拷贝:copy(src, dest, [option],callback);【copySync()】
/**
option对应:
  ①clobber (boolean): 覆盖现有的文件或目录,默认true
  ②dereference (boolean): dereference symlinks, default is false
  ③preserveTimestamps (boolean): 最后修改和访问时间和原始的源文件一致,默认为false
  ④filter: 函数或正则表达式过滤复制文件,返回true包含,否则排除
*/
fs.copy('/tmp/myfile', '/tmp/mynewfile', function (err) {
   if (err) return console.error(err); 
   console.log("success!")
}) //拷贝文件
fs.copy('/tmp/mydir', '/tmp/mynewdir', function (err) {
   if (err) return console.error(err) 
   console.log('success!')
}) //拷贝目录

============================================

b. 清空目录:emptydir() 【emptyDirSync(), emptydirSync()】
/**
确保一个目录是空的。如果目录非空删除目录内容。
如果目录不存在,就创建一个。目录本身并不是删除。
*/
fs.emptyDir('/tmp/some/dir', function (err) {
  if (!err) console.log('success!')
})

============================================

c. 创建文件:ensureFile() 【createFileSync(),ensureFileSync()】
/**
确保文件存在。如果被请求的文件的目录不存在,创建这些目录。
如果文件已经存在,它不修改。
*/
var file = '/tmp/this/path/does/not/exist/file.txt';
fs.ensureFile(file, function (err) { 
   console.log(err) ;
})

=============================================

d.创建目录:ensureDir()  【ensureDirSync()】
/**
确保目录的存在。如果目录结构不存在,就创建一个
*/
var dir = '/tmp/this/path/does/not/exist';
fs.ensureDir(dir, function (err) {
   console.log(err);
})

D. inquirer.js如果想自己做一个脚手架或者在某些时候要与用户进行交互,这个时候就不得不提到了这个库了。

由于交互的问题种类不同,inquirer为每个问题提供很多参数:
type:表示提问的类型,包括:input, confirm, list, rawlist, 
                      expand, checkbox, password, editor
name: 存储当前问题回答的变量;
message:问题的描述;
default:默认值;
choices:列表选项,在某些type下可用,并且包含一个分隔符(separator);
validate:对用户的回答进行校验;
filter:对用户的回答进行过滤处理,返回处理后的值;
transformer:对用户回答的显示效果进行处理
    (如:修改回答的字体或背景颜色),但不会影响最终的答案的内容;
when:根据前面问题的回答,判断当前问题是否需要被回答;
pageSize:修改某些type类型下的渲染行数;
prefix:修改message默认前缀;
suffix:修改message默认后缀。

案例如下:

const promptList = [{
    type: 'input',
    message: '设置一个用户名:',
    name: 'name',
    default: "test_user" // 默认值
},{
    type: 'input',
    message: '请输入手机号:',
    name: 'phone',
    validate: function(val) {
        if(val.match(/\d{11}/g)) { // 校验位数
            return val;
        }
        return "请输入11位数字";
    }
},{
    type: "confirm",
    message: "是否使用监听?",
    name: "watch",
    prefix: "前缀"
},{
    type: "confirm",
    message: "是否进行文件过滤?",
    name: "filter",
    suffix: "后缀",
    when: function(answers) { // 当watch为true的时候才会提问当前问题
        return answers.watch
    }
},{
    type: 'list',
    message: '请选择一种水果:',
    name: 'fruit',
    choices: [
        "Apple",
        "Pear",
        "Banana"
    ],
    filter: function (val) { // 使用filter将回答变为小写
        return val.toLowerCase();
    }
},{
    type: "expand",
    message: "请选择一种水果:",
    name: "fruit",
    choices: [
        {
            key: "a",
            name: "Apple",
            value: "apple"
        },
        {
            key: "O",
            name: "Orange",
            value: "orange"
        },
        {
            key: "p",
            name: "Pear",
            value: "pear"
        }
    ]
},{
    type: "checkbox",
    message: "选择颜色:",
    name: "color",
    choices: [
        "red",
        "blur",
        "green",
        "yellow"
    ],
    pageSize: 2 // 设置行数
},{
    type: "password", // 密码为密文输入
    message: "请输入密码:",
    name: "pwd"
},{
    type: "editor",
    message: "请输入备注:",
    name: "editor"
}]

E. execa:据称是更好的子进程管理工具。执行后续的示例,先执行npm install --save execa

const execa = require("execa");
execa("ls").then(result => console.log(result.stdout));

F.get-stream:Get a stream as a string, buffer, or array先执行npm install get-stream

//检查NodeJs版本
const execa = require('execa');
const getStream = require('get-stream');
const stream = execa('node', ['--version']).stdout;
return getStream(stream).then(data => {

 })

G. download-git-repo:Download and extract a git repository (GitHub, GitLab, Bitbucket) from node. 若我们CLI中的具体基础工程是从git上下载的,那就需要用到该库了。
Vue-CLIvue-init中就有调用了该函数

image.png

若我们自己的脚手架中包含了基础工程,则执行命令时拷贝基础工程到我们创建的初始工程中即可。

image.png

H. vue-cli 中用到的其他一些库
user-home:【Get the path to the user home directory — 获取用户主目录的路径】

image.png

tildifyConvert an absolute path to a tilde path: /Users/sindresorhus/dev → ~/dev — 将绝对路径转换为波形路径】

image.png

ora:【Elegant terminal spinner — 在终端里有显示载入动画】

image.png

vue-cli其他js中还引用了其他一些库,这里就先不介绍了,后面再将vue-cli源码 Vue-cli 原理分析好好解读下。下图是vue-cli的一个基本结构。

image.png

小结
搭建一个简易的cli需要:
① 检查node 等的版本号,一般校验不能低于某个版本
② 日志的打印:chalk
③ 搭建过程中的一些提问: inquirer
④ 工程包的拷贝fs-extra 或下载 download-git-repo
pack.json的配置

来看下这个最简易cli的结构吧:

image.png

具体的创建过程,上面已提到过,即基础工程的拷贝过程。这里再一次贴一下相关代码吧

image.png

相关package.json也看下吧:

image.png

将该cli发布到npm中,具体发布过程见将自己的vue组件发布为npm包 & npm私有仓库搭建

image.png

发布成功后,安装cli

npm install eui2-cli -g

按装成功后,执行命令

//projectName: 你的工程名
eui2 create <projectName>

创建工程

image.png

打开工程目录查看

image.png

没错,就是它了

最简易的cli 在git上的地址


三、yeoman自行了解下

大前端的自动化工厂(1)——Yeoman

上一篇下一篇

猜你喜欢

热点阅读