关于zhen-cli的介绍
开发目的
- 首先是方便自己快速创建项目工程的初始化模板;
- 其次真正实现日常维护的工程模板价值;
- 再次实现一个脚手架同时管理两种技术栈的目的:管理vue技术栈和react技术栈。
zhen-cli是一个脚手架。其功能类似vue-cli,相当于vue-cli的简化版,但功能又不全是vue-cli的子集。
目前发布在npm上的版本1.0.14实现的功能:
- 项目工程、版本号以及作者可以自定义;
- 可以选择是否安装vuex;
- 可以同时生成react技术栈的项目初始化工程模板。 最后这点算是区别于vue-cli的。
工作原理
整体工作流程如下。
整体上,zhen-cli是一个nodejs的应用,归属于命令行工具应用。
依赖的主要功能库:
- commander 负责命令行输入和参数解解析
以下是对想要注册命令的定义。
#!/usr/bin/env node
// commander是终端命令行命令注册库
// #!/usr/bin/env node 这行代表的是按照node的环境处理
const program = require("commander");
program
.version(require("../package").version)
.usage("<command> [options]")
.option('--no-sauce', 'Remove sauce')
.command("vue", "generate a new vue project from a template") // 注册vue命令
.command("react", "generate a new react project from a template") // 注意react命令
program.parse(process.argv); // 去掉后不管作用
- download-git-repo 负责下载gitlab或者github的仓库代码
const download = require("download-git-repo"); // 下载github下的仓库
/**
* 从模板仓库下载模板,并生成项目
*
* @param {String} template
*/
function downloadAndGenerate(template) {
const spinner = ora("模板下载中,请稍等···");
spinner.start();
// 如果存在本地模板,先删除
if (exists(tmp)) rm(tmp);
download(template, tmp, {
clone: false
}, err => {
spinner.stop();
if (err) {
logger.fatal("模板" + template + "下载失败" + ": " + err.message.trim());
}
// 生产项目方法 核心方法
// name为文件名 tmp数临时目录 生成的项目的目录
generate(name, tmp, to, err => {
if (err) logger.fatal(err);
logger.success('"%s" 创建成功.', name);
});
});
}
}
- metalsmith 串行执行各个方法直到下载项目依赖
读取文件之后链式调用中间件方法。
// Read all the files in a source directory.
// Invoke a series of plugins that manipulate the files.
// Write the results to a destination directory!
const metalsmith = Metalsmith(path.join(src, "template"));
// metalsmith.metadata() 这个默认是{}
// console.log(metalsmith.metadata(), 'metalsmith.metadata()')
const data = Object.assign(metalsmith.metadata(), {
destDirName: name,
inPlace: dest === process.cwd(),
noEscape: true
});
// 将模板中自定义的helper注册到handlebars中。
// helper实际就是模板可以自动获取到helper中的数据
// opts.helpers &&
// Object.keys(opts.helpers).map(key => {
// Handlebars.registerHelper(key, opts.helpers[key]);
// });
// 给metalsmith绑定插件,1.收集用户交互信息 2. 过滤需要渲染的文件 3. 渲染文件
// metalsmith Read all the files in a source directory.
// 将数据咋转为以路径为key的键值对形式
// 输出到一个指定的目录
metalsmith
.use(askQuestions(opts.prompts))
.use(filterFiles(opts.filters))
.use(renderTemplateFiles)
.clean(false)
.source(".")
.destination(dest)
.build(err => {
done(err);
console.log('\n正在下载依赖...\n')
executeCommand('npm install', path.join(process.cwd(), name)).then(()=>{
logMessage(opts.completeMessage, data);
})
});
- handlebars 负责渲染字符串为文件
const Handlebars = require("handlebars");
const async = require("async"); // async这是一个异步处理的库
// 这里是用的handlebars模板 也可以使用ejs模板语法
const render = require("consolidate").handlebars.render; //Template engine consolidation library. 模板引擎库 有多种模板渲染引擎
const metalsmithMetadata = metalsmith.metadata();
// metalsmithMetadata是meta里面的数据
// console.log(metalsmithMetadata, 'metalsmithMetadata')
async.each(
keys,
(file, next) => {
const str = files[file].contents.toString();
// 如果文件中没有模板语法,则不对该文件进行渲染,直接输出文件内容。
if (!/{{([^{}]+)}}/g.test(str) || file.indexOf('.vue') > -1) {
return next();
}
// 使用数据对象对模板进行渲染
render(str, metalsmithMetadata, (err, res) => {
if (err) {
err.message = `[${file}] ${err.message}`;
return next(err);
}
files[file].contents = Buffer.from(res);
next();
});
},
done
);
- inquirer 负责用户与命令行交互
inquirer
.prompt([
{
type: prompt.type,
name: key,
message: prompt.message || key,
default: promptDefault,
choices: prompt.choices || [],
validate: prompt.validate || (() => true)
}
])
.then(answers => {
if (Array.isArray(answers[key])) {
data[key] = {};
answers[key].forEach(multiChoiceAnswer => {
data[key][multiChoiceAnswer] = true;
});
} else if (typeof answers[key] === "string") {
data[key] = answers[key].replace(/"/g, '\\"');
} else {
data[key] = answers[key];
}
done();
})
.catch(done);
使用方法
执行命令npm i -g zhen-cli 安装
使用zhen vue/react project-name 创建项目
如果想研究源代码可以直接查看node modules里面的zhen-cli
后续工作
后续将继续优化原始模板。尤其react模板目前比较简单,后期将做出更加针对性的优化方案。
我站在山顶看风景!下面是我的家乡!