从前面的一和二你会发现,这不就是最基本的git clone 的命令行嘛,这mcf-cli也不过如此,但是并没有实现vue-cli的功能啊
心急吃不着热豆腐,先学会爬,再学会走,下面我们就来看下,怎么实现和vue-cli一样的功能。
# 创建lib文件夹
/bin # ------ 命令执行文件
/mcf # -----mcf入口文件
/mcf-add # ----- 添加修改模板文件
/mcf-list # ----- 查看模板列表
/mcf-init # ----- 初始化模板项目
/mcf-delete # ----- 删除模板
/lib # ------ 工具模块
package.json
template.json # ------ 模板列表对象
template.json 模板列表对象为空
{}
# 在bin文件夹下创建 mcf-create-vue文件
#!/usr/bin/env node
const path = require('path')
const fs = require('fs')
const download = require('../lib/download')
const generator = require('../lib/generator')
// 命令行交互工具
const program = require('commander')
//使用shell使用的模式匹配文件,比如星号之类的
const glob = require('glob')
// 终端交互工具
const inquirer = require('inquirer')
// 终端字符串样式设置正确
const chalk = require('chalk')
// 各种日志级别的彩色符号
const logSymbols = require('log-symbols')
program.usage('<project-name>')
.option('-r, --repository [repository]', 'assign to repository', 'minchao920917/app-vue-template')
.parse(process.argv);
let projectName = program.args[0];
if (!projectName) { // project-name 必填
// 相当于执行命令的--help选项,显示help信息,这是commander内置的一个命令选项
program.help()
return
}
const list = glob.sync('*') // 遍历当前目录
const rootName = path.basename(process.cwd()) // 获取执行当前命令的文件夹名称字符串
let next = undefined
if (list.length) { // 如果当前目录不为空
if (list.filter(name => {
const fileName = path.resolve(process.cwd(), path.join('.', name))
const isDir = fs.statSync(fileName).isDirectory()
return name.indexOf(projectName) !== -1 && isDir
}).length !== 0) {
console.log(`项目${projectName}已经存在`)
return
}
next = Promise.resolve(projectName)
} else if (rootName === projectName) {
next = inquirer.prompt([
{
name: 'buildInCurrent',
message: '当前目录为空,且目录名称和项目名称相同,是否直接在当前目录下创建新项目?',
type: 'confirm',
default: true
}
]).then(answer => {
return Promise.resolve(answer.buildInCurrent ? projectName : '.')
})
} else {
next = Promise.resolve(projectName)
}
next && go()
function go() {
next.then(projectRoot => {
if (projectRoot !== '.') {
fs.mkdirSync(projectRoot)
}
return download(projectRoot, program.repository).then(target => {
return {
name: projectRoot,
root: projectRoot,
target: target
}
})
}).then(context => {
return inquirer.prompt([
{
name: 'projectName',
message: '项目的名称',
default: context.name
}, {
name: 'projectVersion',
message: '项目的版本号',
default: '1.0.0'
}, {
name: 'projectDescription',
message: '项目的简介',
default: `A project named ${context.name}`
}
, {
name: 'projectAuthor',
message: '项目的创建人',
default: `minchao`
}
]).then(answers => {
return {
...context,
metadata: {
...answers
}
}
})
}).then(context => {
// 添加生成的逻辑
return generator(context.metadata, context.target, path.parse(context.target).dir);
}).then((res) => {
// 成功用绿色显示,给出积极的反馈
console.log(logSymbols.success, chalk.green('创建成功:)'))
console.log(chalk.green(`cd ${projectName}\nnpm install\nnpm run dev`))
}).catch(err => {
// 失败了用红色,增强提示
console.error(logSymbols.error, chalk.red(`创建失败:${error.message}`))
})
}
# 在lib下创建download.js和generator.js
download.js
const path = require('path')
const ora = require('ora')
const download = require('download-git-repo')
module.exports = function (target, url) {
target = path.join(target || '.', '.download-temp')
return new Promise((resolve, reject) => {
const spinner = ora(`正在下载项目模板,源地址:https://github.com/${url}`)
spinner.start()
download(url, target, (err) => {
if (err) {
spinner.fail()
reject(err)
} else {
// 下载的模板存放在一个临时路径中,下载完成后,可以向下通知这个临时路径,以便后续处理
spinner.succeed()
resolve(target)
}
})
})
}
generator.js
// npm i handlebars metalsmith -D
const Metalsmith = require('metalsmith')
const Handlebars = require('handlebars')
const rm = require('rimraf').sync
module.exports = function (metadata = {}, src, dest = '.') {
if (!src) {
return Promise.reject(new Error(`无效的source:${src}`))
}
return new Promise((resolve, reject) => {
Metalsmith(process.cwd())
.metadata(metadata)
.clean(false)
.source(src)
.destination(dest)
.use((files, metalsmith, done) => {
const meta = metalsmith.metadata()
// 目前仅定义替换package.json文件
Object.keys(files).filter(x => x.includes('package.json')).forEach(fileName => {
const t = files[fileName].contents.toString()
files[fileName].contents = new Buffer(Handlebars.compile(t)(meta))
})
done()
}).build(err => {
rm(src)
err ? reject(err) : resolve()
})
})
}
# 在你的github上创建你的模板项目
例如 vue-template模板 如果你喜欢,请点个star鼓励下
# 相关地址
mcf-cli 源码地址 如果你喜欢,请点个star鼓励下
# 总结
各位你看到这里,你也许会说,这么简单吗?这里,我只能说,就是这么简单,先实现从模板复制一个项目到本地,然后再由命令行修改文件名,进而修改项目的配置信息。