脚手架功能模块图
需要注意的是不支持 windows 系统
因为内部是使用#!/usr/bin/env node 来执行命令的
脚手架流程图
安装lerna
lerna是一个可以在管理多个npm包,所以对于多个npm包管理很方便。
npm install lerna -g
建立项目文件夹
mkdir xi-cli
cd xi-cli
mkdir command
mkdir core
mkdir utils
lerna初始化
lerna init
npm包主要是分功能放在command、core、utils三个文件下面,所以需要删除packages,修改lerna.json
"packages": ["command/*", "core/*", "utils/*"]
实现个模块功能
添加core模块
lerna create @xi-cli/core
给core/index.js 添加#!usr/bin/env node,让系统执行xi-cl的时候,去环境变量里面去找node来执行
// core/index.js
#!usr/bin/env node
//core/package.json
"bin": {
"xi-cli": "lib/index.js"
},
执行npm link
npm link
测试命令
xi-cli
添加日志模块
//添加日志
lerna create @xi-cli/log
//安装npmlog
lerna add npmlog --scope=@xi-cli/log
在log/index.js里面,初始化npmlog的基本配置
"use strict";
const npmlog = require("npmlog");
//定义npmlog的level
npmlog.level = process.env.LOG_LEVEL ? process.env.LOG_LEVEL : "info";
npmlog.heading = "xi-cli";
module.exports = npmlog;
core模块功能开发
core模块主要实现init项目命令
//给core模块添加 log模块
lerna add @xi-cli/log --scope=@xi-cli/core
安装commander
lerna add commander --scope=@xi-cli/core
注册init命令
program
.name("xi-cli")
.command("init [name]")
.option("-f,--force", "是否强制初始化项目")
.action((name) => {
console.log("name")
});
//重点 不能忘记
program.parse(process.argv);
测试命令
xi-cli init projectname
实现init项目command的逻辑
这里主要实现的功能就是下载模板,render模板,安装依赖,启动项目这几项功能
"use strict";
const fs = require("fs");
const inquier = require("inquirer");
const fse = require("fs-extra");
const {execSync} = require("child_process");
const path = require("path");
const glob = require("glob");
const ejs = require("ejs");
const log = require("@xi-cli/log");
const {resolve} = require("path");
//__dirname 执行文件的目录 这里是init/lib/index.js
//需要的程序执行的当前目录
const localPath = process.cwd();
class Init {
constructor(name) {
this.propjectName = name;
this.force = true;
this.configInfo = {
projectName: name,
};
this.exec();
}
async exec() {
try {
// 准备阶段
await this.prepare();
//下载模块
this.downloadTemplate();
//匹配文件
let files = await this.getGlobFile();
//ejsrender模板
let res = await this.ejsRender(files);
let tempalteDir = await this.moveFile();
//删除模板文件夹
fse.removeSync(path.join(localPath, tempalteDir));
//安装依赖 并启动
this.installDepend();
this.initStartProject();
} catch (e) {
log.error(e);
}
}
async prepare() {
//判断当前目录是否为空
if (this.isCewEmpty()) {
//询问是否创建
const answer = await inquier.prompt([
{
type: "confrim",
name: "isContinue",
message: "当前文件不为空,是否继续?",
default: "xi-project",
},
]);
if (answer.isContinue) {
//是否启动强制更新
fse.emptyDirSync(localPath);
}
}
}
isCewEmpty() {
let fileList = fs.readdirSync(localPath);
//文件过滤
fileList = fileList.filter((file) => {
return !file.startsWith(".") && ["node_modules"].indexOf(file) < 0;
});
return fileList.length > 0 && fileList ? true : false;
}
downloadTemplate() {
log.info("开始下载模板");
//git模块的地址
execSync("git clone https://gitee.com/rainbowChenhong/xi-template.git", {
stdio: [0, 1, 2], // we need this so node will print the command output
cwd: localPath, // path to where you want to save the file
});
}
async getGlobFile() {
const dir = process.cwd();
return new Promise((resolve, reject) => {
glob(
"**",
{
cwd: dir,
//忽略文件
ignore: ["**/**.html"],
//匹配文件夹
nodir: true,
},
(err, files) => {
if (err) reject(err);
resolve(files);
}
);
});
}
ejsRender(files) {
let filesPromise = [];
files.map((file) => {
const filePath = path.join(localPath, file);
filesPromise.push(
new Promise((resolve, reject) => {
ejs.renderFile(filePath, this.configInfo, (err, result) => {
if (err) {
reject(err);
}
if (result) {
//写入
fse.writeFileSync(filePath, result);
}
resolve(result);
});
})
);
});
return Promise.all(filesPromise);
}
moveFile() {
return new Promise((resolve, reject) => {
glob(
"**",
{
cwd: localPath,
//忽略文件
ignore: [],
//匹配文件夹
nodir: true,
},
(err, files) => {
let curentDir = "";
files.map((file) => {
if (err) reject(err);
const filePath = path.join(localPath, file);
let fileAr = file.split("/");
curentDir = fileAr.shift();
let newPath = path.join(localPath, fileAr.join("/"));
//移动文件
fse.moveSync(filePath, newPath);
});
resolve(curentDir);
}
);
});
}
installDepend() {
execSync("npm install ", {
stdio: [0, 1, 2],
cwd: localPath,
});
}
initStartProject() {
execSync("npm run start ", {
stdio: [0, 1, 2],
cwd: localPath,
});
}
}
module.exports = Init;
修改core/lib/index.js
//安装init模块
lerna add @xi-cli/init --scope=@xi-cli/core
//core/lib/index.js 修改命令
const Init = require("@xi-cli/init");
program
.command("init [name]")
.option("-f,--force", "是否强制初始化项目")
.action((name) => {
new Init(name);
});
测试执行
xi-cli init projectName
效果: