有了上一篇文章的的基础准备知识,下面我们开始动手做吧
# 创建你的脚手架文件夹及名称
mkdir mcf-cli && cd mcf-cli
npm init
此时,你的文件夹下回自动生成一个package.json,里面的基本信息就是你在终端输入的基本信息
# 创建bin文件夹和tempalte.json
此时基本的文件目录如下
/bin # ------ 命令执行文件
package.json #------ 配置文件
template.json # ------ 模板列表对象
template.json 为空对象
{}
# 在bin目录下创建mcf
#!/usr/bin/env node
//定义执行解释器
const program = require('commander')
const chalk = require('chalk')
console.log('**********************************************************************');
console.log('* '+chalk.red('mcf-cli(闵长峰脚手架) 为自己写的一个脚手架工具:')+' *');
console.log('* '+chalk.green('1、让项目从"搭建-开发-部署"更加快速以及规范')+' *');
console.log('* '+chalk.green('2、避免创建项目时手动复制不全的尴尬')+' *');
console.log('* '+chalk.green('3、多了解点原理,多学点东西总是好的')+' *');
console.log('**********************************************************************');
program
.version(require('../package').version)
.usage('<command> [options]')
.command('add', '添加新的源 mcf add')
.command('delete', '删除源 mcf list')
.command('list', '展示源列表 mcf list')
.command('init', '从源初始化项目 mcf init <模板名> 项目名')
.command('create-vue', '快速构建新vue项目 mcf create-vue <项目名>')
program.parse(process.argv)
version('0.0.1') 版本号;
usage('') 名字
command('') 定义子命令;
parse(process.argv); 至于末尾,解析命令行输入的命令;
其他的还包括:
- description(‘hello ,I\’m zhao’) 描述
- allowUnknownOption 取消接收到未定义option时报错的机制,不报错;
- alias(‘a’) 定义子命令的短命令;
- action(cb) 回调
- option(‘-p, –peppers’,’Add oeooers’) 自定义选项参数和描述
# 将mcf命令行配置到package中
在 package.json中,添加bin属性
{
"bin":{
"mcf":"bin/mcf"
}
...
"dependencies": {
"commander": "^2.19.0",
"metalsmith": "^2.3.0",
"download-git-repo": "^1.1.0",
"inquirer": "^6.2.2",
"ora": "^3.2.0",
"chalk": "^2.4.2",
"latest-version": "^3.1.0",
"log-symbols": "^2.2.0",
"glob": "^7.1.2",
"handlebars": "^4.0.11"
}
...
}
# npm link
将当前开发的npm关联到node环境中
打开cmd,输入mcf 回车查看你的mcf是否是你想要的结果
# 依次创建mcf-add、mcf-init、mcf-list、mcf-delete
# mcf-add文件
#!/usr/bin/env node
// 交互式命令行
const inquirer = require('inquirer')
// 修改控制台字符串的样式
const chalk = require('chalk')
// node 内置文件模块
const fs = require('fs')
// 读取根目录下的 template.json
const tplObj = require(`${__dirname}/../template`)
// 自定义交互式命令行的问题及简单的校验
let question = [
{
name: "name",
type: 'input',
message: "请输入模板名称",
validate (val) {
if (val === '') {
return 'Name is required!'
} else if (tplObj[val]) {
return 'Template name 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('O(∩_∩)~ Added successfully!\n'))
console.log(chalk.grey('The latest template list is: \n'))
console.log(tplObj)
console.log('\n')
})
})
mcf-add命令行的作用主要在于,添加一个模板到脚手架本地库中,输入用模板名以及模板对应的url(github上只需写到用户名/项目名)
# mcf-list文件
#!/usr/bin/env node
const tplObj = require(`${__dirname}/../template`)
console.log(tplObj)
查看当地的模板列表
# mcf-delete文件
#!/usr/bin/env node
const 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')
})
})
mcf-delete <模板名>
从本地mcf脚手架模板列表中删除你要删除的模板
# mcf-init文件
#!/usr/bin/env node
const 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`)
}
)
mcf-init <模板名> <项目名>
将模板copy到本地
# 添加进package.json
{
...
"bin":{
"mcf-add": "bin/mcf-add",
"mcf-delete": "bin/mcf-delete",
"mcf-list": "bin/mcf-list",
"mcf-init": "bin/mcf-init",
}
...
}
# 总结
到这里你会发现,其实这只是基础的git clone的方法,你也许会说,写这么多,还不如我以一句git clone命令行来得实在