相信你一定用过vue-cli或者create-react-app或者你公司自己的脚手架。当我第一次用这些脚手架的时候,会觉得这一定是种很高级的玩意,其实了解过之后就发现,脚手架也并不是多么深奥的东西。怎么做一个脚手架呢?接下来我会一步步的教你如何完成一个自己的脚手架(源码:https://github.com/hui-fly/rv-cli,记得star哦)

1. 预备知识

本次教大家实现一个基础的cli工具,将会用到以下一些依赖,下面对这些依赖进行简单介绍:

Inquirer.js

Inquirer.js试图为NodeJs做一个可嵌入式的美观的命令行界面。它的功能主要是:

用法示例:
建立index.js,内容如下

let inquirer = require('inquirer')
inquirer.prompt([{type: 'confirm',name: 'handsome',message: '我是世界上最帅的男人吗?',default: true}
]).then((answers) => {console.log(answers)
})

在文件目录线执行node index.js得到结果如下:
{ handsome:true }

这里type的类型包括

想了解更多请参考:https://juejin.im/entry/5937c73cac502e0068cf1171

commander

官方解释:commander灵感来自 Ruby,它提供了用户命令行输入和参数解析的强大功能,可以帮助我们简化命令行开发,它提供的功能是:

  • 参数解析
  • 强制多态
  • 可变参数
  • Git 风格的子命令
  • 自动化帮助信息
  • 自定义帮助等

这么说想必大家也比较抽象,直接上例子:

const program = require("commander");
// 定义指令
program.version('0.0.1').usage('<command> [options]').command('init', 'Generate a new project from a template').action((option) => {// 回调函数console.log('Hello World')})
// 解析命令行参数
program.parse(process.argv);

同样在文件目录下执行node command.js init
然后就会发现输出  Hello World

如果你想了解更多,可以参考https://wangchujiang.com/wcj/#Commander

download-git-repo

这个看名字大概就能猜出它是干嘛的了,没错—它就是帮我们完成下载远程仓库的,它的用法也比较简单,示例如下:

const download = require('download-git-repo')
download(repository, destination, options, callback)

其中 repository 是远程仓库地址;destination是存放项目的文件夹,下载完之后会默认建立在本目录下;options 是一些选项,比如 { clone:boolean } 表示用 http download 还是 git clone 的形式下载,callback就是下载完成之后的回调函数了。

chalk

这个依赖可以让你输出的内容变得更好看

const chalk = require('chalk');
console.log(chalk.green('success'));
console.log(chalk.red('error'));

ora

这个依赖可以产生一个loading的效果,在进行下载的时候我们会用到它

OK,以上就是我们这次会用到的所有依赖了,接下里开始搞起我们的脚手架吧!


2. 脚手架开发

项目文件结构搭建

  1. 首先初始化我们的项目,建立一个文件夹,我这里就叫rv-cli,你也可以随意取.
  2. 建立好之后,在文件夹下执行npm init(相信你一定已经安装了node了),一路回车即可,然后就会帮你生成一个package.json文件,在package.json添加如下代码–即我们预备知识介绍的依赖
"dependencies": {"chalk": "^2.4.2","commander": "^2.19.0","download-git-repo": "^1.1.0","inquirer": "^6.2.2","ora": "^3.2.0"
}

然后执行npm i 安装这些依赖

  1. 建立bin文件夹,并在bin文件夹下建立以下文件,这些文件就是我们执行的脚本,其中rv-add用于添加项目,rv-delete用于删除一个项目,rv-init用于初始化一个项目,rv-list用于展示已添加的项目列表,而rv可以作为我们的入口文件。接下来我们一步步完善这些脚本

  2. 在建立的rv文件中添加一段熟悉的代码

#!/usr/bin/env node
console.log('hello world');

然后执行 node ./bin/rv 就会输出 hello world了,这里大家会发现头部有一行#!/usr/bin/env node,这个的      作用就是告诉系统脚本的执行程序是node,并且系统会自动寻找node的所在路径。有了这行代码我们可以直接执行 ./bin/rv而不用加node了

  1. 我们仿照大多数的脚手架的设计,实现以下需求:在命令行输入rv add 即执行rv-add,执行rv delete即执行rv-delete… 其他同理,以下以rv-add为例。
    首先我们先搞一下rv这个文件,话不多讲,直接贴代码:
#!/usr/bin/env node
const program = require('commander')// 定义当前版本
// 定义使用方法
// 定义四个指令
program.version(require('../package').version).usage('<command> [options]').command('add', 'add a new template').command('delete', 'delete a template').command('list', 'list all the templates').command('init', 'generate a new project from a template')// 解析命令行参数
program.parse(process.argv)

这里简单解释下:前面在定义的时候 program.command(‘init’).action(() => {}),其实本来还有个 action 回调,但是我们没写,commander 就会尝试在入口脚本的目录中搜索可执行文件,找到形如 program-command
     
然后再简单搞一下rv-add文件

#!/usr/bin/env node
console.log('hello rv-add!')

当我们执行./bin/rv时可以看到输出了以下内容
     
接下来验证一下执行 ./bin rv add 是否执行了rv-add,在命令行输入./bin rv add,如果输出 hello rv-add,说明我们成功了!

完善各个脚本文件

rv-add

接下来我们完善一下rv-add,执行它之后帮我们添加一个项目,在完善它之前,我们首先在根目录下建立一个template.json,内容就是一个空对象{}即可。接下来直接贴代码

#!/usr/bin/env node// 交互式命令行
const inquirer = require('inquirer')
// 修改控制台字符串的样式
const chalk = require('chalk')
// node 内置文件模块
const fs = require('fs')
// 读取根目录下的 template.json
const tplObj = require(`${__dirname}/../template.json`)// 自定义交互式命令行的问题及简单的校验
let question = [{name: "name",type: 'input',message: "请输入模板名称",validate (val) {if (val === '') {return 'Name is required!'} else if (tplObj[val]) {return 'Template has already existed!'} else {return true}}},{name: "url",type: 'input',message: "请输入模板地址",validate (val) {if (val === '') return 'The url is required!'return true}}
]inquirer.prompt(question).then(answers => {// answers 就是用户输入的内容,是个对象let { name, url } = answers;// 过滤 unicode 字符tplObj[name] = url.replace(/[\u0000-\u0019]/g, '')// 把模板信息写入 template.json 文件中fs.writeFile(`${__dirname}/../template.json`, JSON.stringify(tplObj), 'utf-8', err => {if (err) console.log(err)console.log('\n')console.log(chalk.green('Added successfully!\n'))console.log(chalk.grey('The latest template list is: \n'))console.log(tplObj)console.log('\n')})})

注释写的也比较清楚,我就不多解释啦
执行以下看看效果:

这里先说明一下这个模版地址就是download-git-repo要读取的git地址,这个地址 不需要完整的,只需
要贴上下边红框内的即可。

当然也可以写完整,其实这是 https://github.com:hui-fly/myTest 的简写,注意域名后面的 ‘/’ 要改成 ‘:’,gitlab仓库也是支持的,如果gitlab域名是https://gitlab.com/,则可以简写gitlab:[owner]/[projiectName],但是如果是公司搭建的git服务就不能简写了

rv-delete文件

删除一个项目

#!/usr/bin/env nodeconst inquirer = require('inquirer')
const chalk = require('chalk')
const fs = require('fs')
const tplObj = require(`${__dirname}/../template`)let question = [{name: "name",message: "请输入要删除的模板名称",validate (val) {if (val === '') {return 'Name is required!'} else if (!tplObj[val]) {return 'Template does not exist!'} else  {return true}}}
]inquirer.prompt(question).then(answers => {let { name } = answers;delete tplObj[name]// 更新 template.json 文件fs.writeFile(`${__dirname}/../template.json`, JSON.stringify(tplObj), 'utf-8', err => {if (err) console.log(err)console.log('\n')console.log(chalk.green('Deleted successfully!\n'))console.log(chalk.grey('The latest template list is: \n'))console.log(tplObj)console.log('\n')})})

接下来看看执行的效果

delete完之后,template就是{}了,因为之后我们还要用test模版,这里我们再把它add回去

rv-list

这个就比较简单了,就是输出项目列表

#!/usr/bin/env node
const tplObj = require(`${__dirname}/../template`)
console.log(tplObj)

执行结果如下:

rv-init

接下来是 init命令,这个要稍微麻烦一点,不过还好,代码走起

#!/usr/bin/env nodeconst program = require('commander')
const chalk = require('chalk')
const ora = require('ora')
const download = require('download-git-repo')
const tplObj = require(`${__dirname}/../template`)program.usage('<template-name> [project-name]')
program.parse(process.argv)
// 当没有输入参数的时候给个提示
if (program.args.length < 1) return program.help()// 好比 vue init webpack project-name 的命令一样,第一个参数是 webpack,第二个参数是 project-name
let templateName = program.args[0]
let projectName = program.args[1]
// 小小校验一下参数
if (!tplObj[templateName]) {console.log(chalk.red('\n Template does not exit! \n '))return
}
if (!projectName) {console.log(chalk.red('\n Project should not be empty! \n '))return
}url = tplObj[templateName]console.log(chalk.white('\n Start generating... \n'))
// 出现加载图标
const spinner = ora("Downloading...");
spinner.start();
// 执行下载方法并传入参数
download (url,projectName,err => {if (err) {spinner.fail();console.log(chalk.red(`Generation failed. ${err}`))return}// 结束加载图标spinner.succeed();console.log(chalk.green('\n Generation completed!'))console.log('\n To get started')console.log(`\n    cd ${projectName} \n`)}
)

这里便用到了下载远程仓库的依赖 download-git-repo,这里我仿照的是
vue init webpack project-name,webpack就是模版名,project-name就是项目名,也是项目的根目录,        结下来我们执行一下
rv init test demo 看看发生了什么吧
       
执行之后,就开始下载远程仓库并生成demo文件夹,此时就生成了一个demo项目了
    
但是大家有没有发现,我的init命令是 rv init test demo 而不是 ./bin/rv init test demo,如果你直接执行
rv init test demo一定会失败,因为我做了一个操作让 rv 指向了./bin/rv,其实很简单,就是配置一下            package.json,即配置一下bin字段

"bin": {"rv": "./bin/rv","rv-add": "bin/rv-add","rv-delete": "bin/rv-delete","rv-list": "bin/rv-list","rv-init": "bin/rv-init"},

然后在package.json所在目录下执行npm link,这样便完成了命令的指向而且是全局的
至此,我们的脚手架就基本完成了,给自己鼓个掌吧!!

3. 将脚手架发布到npm

  1. 要发布npm包,首先你得有一个npm账号,注册完成之后,执行npm login命令输入你的用户名、密码、邮箱,然后就可以执行npm publish发布你的npm包了。但是rv-cli这个名字已经存在了,在发布之前,你得换个名字了,然后在npm上搜一下验证下你起的名字是不是已经被占用。
  2. 建立.npmignore,这个和.gitignore一样,我们在.npmignore添加 /node_modules即可,哦对了,你还得把demo文件夹删掉,这个我们用来测试的就不要发上去了。

  1. 发布成功之后,你可以去npm上搜一下,就可以看到自己的包啦,鼓掌!!!
  2. 最后我们在验证一波,是不是可以安装下来,命令行执行 npm i rv-cli -g,安装完之后,执行一下几个命令,耶,成功啦!!

4. 总结

看到这里,你是不是也觉得一个脚手架也不过如此而已嘛,虽然这个还比较初级,但是其实也具备了基本的功能,好啦,我要去研究研究vue-cli了,等我研究完再来更新一篇

一步步教你完成一个自己的脚手架并且发布到npm相关推荐

  1. 手写一个自己的 cli 并发布到 npm 上

    手写一个自己的 cli 并发布到 npm 上 简介:大家平时肯定用过 vue-cli 或者 create-react-app,只需要敲简单的命令行,就可以生成一个完整的项目,非常好用.由于本人所在公司 ...

  2. 从零开始开发一个vue组件打包并发布到npm (把vue组件打包成一个可以直接引用的js文件)

    自己写的组件 有的也挺好的,为了方便以后用自己再用或者给别人用,把组件打包发布到npm是最好不过了,本次打包支持 支持正常的组件调用方式,也支持Vue.use, 也可以直接引用打包好的js文件, 配合 ...

  3. Python爬虫不会?戳一下,小编一步步教你写一个简单的爬虫

    写在前面 网络爬虫又称网络蜘蛛,是指按照某种规则在网络上爬取所需内容的脚本程序.众所周知,每个网页通常包含其他网页的入口,网络爬虫则通过一个网址依次进入其他网址获取所需内容. 优先申明:我们使用的py ...

  4. 人工智能离前端并不远 一步步教你开发一个机器学习APP(附源码)

    最近HBO电视网推出的美剧<硅谷Silicon Valley>席卷全球,里面有一个桥段介绍了超级有趣的iOS app- Not Hotdog.你甚至可以在APP Store上下载到它. 受 ...

  5. 手把手教你做一个新浪博客发布软件JAVA版本(5)--打开博客发布页面并解析博客内容

            前言:很多人用新浪博客引流,但是以前可以用api发布,但是现在已经行不通了,市面上也有诸如新浪博客批量发布软件啦,新浪博客批量发帖啦,新浪博客发布软件啊等等的各种工具,但是小心中枪,一 ...

  6. 手把手教你做一个新浪博客发布软件JAVA版本(4)--打开博客发布页面

           前言:很多人用新浪博客引流,但是以前可以用api发布,但是现在已经行不通了,市面上也有诸如新浪博客批量发布软件啦,新浪博客批量发帖啦,新浪博客发布软件啊等等的各种工具,但是小心中枪,一不 ...

  7. 手把手教你写一个生成对抗网络

    成对抗网络代码全解析, 详细代码解析(TensorFlow, numpy, matplotlib, scipy) 那么,什么是 GANs? 用 Ian Goodfellow 自己的话来说: " ...

  8. 一步步教你轻松学朴素贝叶斯模型算法理论篇1

    一步步教你轻松学朴素贝叶斯模型理论篇1 (白宁超2018年9月3日17:51:32) 导读:朴素贝叶斯模型是机器学习常用的模型算法之一,其在文本分类方面简单易行,且取得不错的分类效果.所以很受欢迎,对 ...

  9. [翻译]一步步教你配置SQL SERVER合并复制(五)配置Publisher(上)

    上一篇:一步步教你配置 SQL SERVER合并复制(四)设置Distributor的安全 (译者注:本篇内容较多也是合并复制最重要的一个环节之一,因此配置Publisher将分成两个章节来翻译)   ...

最新文章

  1. 【Unity】12.5 Navmesh Obstacle组件
  2. linux-epoll研究
  3. VC中获取窗口句柄的各种方法
  4. 太强了!用200个LED做一个智能手表
  5. Prometheus Alertmanager 报警模块
  6. cgi web页面传入命令
  7. php二叉排序树,二叉排序树(建树)
  8. Spring的注解 @Bean用法
  9. Servlet容器与Servlet的关系
  10. 用 Swift、Foursquare API 和 Realm 創建一個咖啡店 App
  11. 【HDL】Verilog HDL仿真工具iverilog(Icarus Verilog)
  12. freeswitch ws php,针对FreeSwitch的呼叫中心接口
  13. u2020 华为_顶级锐龙标压U加持!华为MateBook 13 2020锐龙版评测:轻薄本稳压40W
  14. 勇士大战恶魔?这款桌游明明是套高质量原创手办
  15. 自己电脑中安装黑群辉NAS
  16. 【Robot】Rookit简介
  17. Leetcode_191_Number of 1 Bits
  18. C++字符串转16进制
  19. maven 导出所有依赖jar到指定路径
  20. 太爽啦,GitHub网站1S变VS Code

热门文章

  1. python爬取软件数据_利用Python爬取爬取APP上面的数据
  2. 2022最新大厂高频微服务面试总结,java开发流程面试
  3. 知乎高赞,6款办公工具,小白逆袭成职场大神
  4. 华为OD机试题 - 能力组队(JavaScript)| 机考必刷
  5. GeoPandas入门 | 04-空间连接
  6. 小程序隐藏滚动条_透视智能小程序思享会,和隐藏起来的百度“新流量故事”...
  7. 一:项目需求分析;开发环境介绍;
  8. ADAMS中的过约束(超静定)问题
  9. 最新!大象机器人myCobot机械臂应用于Unity人工智能机器人编程实战课
  10. 音视频实现传输概念讲解