开发一个脚手架工具
本文介绍开发一个脚手架流程。实现类似vue-cli的初始化项目的功能
- 初始化项目
执行npm init初始化一个项目
- 安装依赖包
包名 | 用途 |
---|---|
chalk | 控制台输出显示,比如加颜色啥的 |
commander | 命令行工具,有了它我们就可以读取命令行命令,知道用户想要做什么了 |
inquirer | 交互式命令行工具,给用户提供一个漂亮的界面和提出问题流的方式 |
download-git-repo | 下载远程模板工具,负责下载远程仓库的模板项目 |
ora | 用于显示加载中的效果,类似于前端页面的 loading 效果,像下载模板这种耗时的操作,有了 loading 效果可以提示用户正在进行中,请耐心等待 |
metalsmith | copy下载后的文件以及对文件做一些处理 |
- 设置脚手架命令
如下图所示:在项目的package.json文件增加bin属性配置命令,比如mkq-cli指向bin/mkq-cli.js 当我们输入命令mkq-cli的时候就会执行bin/mkq-cli.js。
{
"name": "mkq-cli",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"bin": {
"mkq-cli": "bin/mkq-cli.js",
"mkq-cli-init": "bin/mkq-cli-init.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"chalk": "^4.1.1",
"commander": "^8.0.0",
"download-git-repo": "^3.0.2",
"inquirer": "^8.1.2",
"metalsmith": "^2.3.0",
"ora": "^5.4.1"
},
"devDependencies": {
"rimraf": "^3.0.2"
}
}
- 编写脚手架功能
如下是mkq-cli-init的实现
#!/usr/bin/env node //此行表示node可执行该文件
const program = require("commander");
const chalk = require("chalk");
const download = require("download-git-repo");
const path = require("path");
const ora = require("ora");
const inquirer = require("inquirer");
const generate = require('../lib/generate-template')
/**
* Usage.
*/
program
.usage("<template-name> [project-name]")
.option("-c, --clone", "use git clone");
/**
* Help.
*/
program.on("--help", () => {
console.log(" Examples:");
console.log();
console.log(
chalk.gray(" # create a new project with an official template")
);
console.log(" $ vue init webpack my-project");
console.log();
console.log(
chalk.gray(" # create a new project straight from a github template")
);
console.log(" $ vue init username/repo my-project");
console.log();
});
/**
* Help.
*/
function help() {
program.parse(process.argv);
if (program.args.length < 1) return program.help();
}
help();
/**
* Settings.
*/
let template = program.args[0];
const hasSlash = template.indexOf("/") > -1;
const rawName = program.args[1];
const inPlace = !rawName || rawName === ".";
const name = inPlace ? path.relative("../", process.cwd()) : rawName;
const to = path.resolve(rawName || "test-pro");
const clone = program.clone || false;
const tmp = path.join(".vue-templates", template.replace(/[\/:]/g, "-"));
if (program.offline) {
console.log(`> Use cached template at ${chalk.yellow(tildify(tmp))}`);
template = tmp;
}
const question = [
{
name: "name",
message: "请输入项目名称?",
},
{
name: "author",
message: "请输入作者?",
when: (res) => Boolean(res.name),
},
{
name: "description",
message: "请输入项目描述?",
when: (res) => Boolean(res.name),
},
];
/**
* Download a generate from a template repo.
*
* @param {String} template
*/
function run() {
inquirer.prompt(question).then((answer) => {
console.log(answer);
downloadAndGenerate(template,{
name:answer.name,
author:answer.author,
description:answer.description
})
});
}
run();
function downloadAndGenerate(template, conf) {
const spinner = ora("downloading template");
spinner.start();
// Remove if local template exists
// if (exists(tmp)) rm(tmp)
download("github:mokq2016/my-ui#main", tmp, { clone }, (err) => {
spinner.stop();
console.log(err);
// if (err) logger.fatal('Failed to download repo ' + template + ': ' + err.message.trim())
generate(name, tmp, to, conf, (err) => {
console.log(err)
});
});
}
输入命令mkq-cli-init init newPro 之后就会执行该文件,运行run方法,通过inquirer插件与用户输入交互。得到用户输入,使用download下载github上的模板。然后通过generate生成项目
- 生成项目
var Metalsmith = require("metalsmith");
const rm = require('rimraf').sync
module.exports = function generate(name, src, dest,conf, done) {
Metalsmith(process.cwd())
.source(src)
.destination(dest)
.use((files, metalsmith, done) => {
Object.keys(files).forEach(fileName => {
//根据输入替换package.json内容
if(fileName === 'package.json'){
const t = files[fileName].contents.toString()
const json = JSON.parse(t)
Object.keys(conf).forEach(key=>{
json[key] = conf[key]
})
files[fileName].contents = new Buffer(JSON.stringify(json),'utf8')
}
})
done()
})
.build((err) => {
rm('.vue-templates')
done(err);
if (!err) {
console.log("success");
} else {
console.log(err);
}
});
};
这样便实现了一个小型脚手架初始化项目的功能。在开发时,可通过npm link把项目链接到全局模块,这样我们就可以测试我们的命令了。