项目创建前准备阶段架构设计:
prepare方法是有清空当前目录的操作的,所以新建一个文件夹/Users/Minjie/Documents/test/mj-cli-test
中去执行命令,因为mj-cli-dev
命令是全局的;
__dirname
它会去找当前执行时的代码,如果在另一个进程中执行,不能获取当前目录;可以用path.resolve(".")
js
mj-cli-dev init --targetPath /Users/Minjie/Documents/vue3/mj-cli-dev/commands/init --debug test-project
准备工作
强制清空当前目录
在commands/init
目录下安装inquirer
库;
js
lerna add inquirer@7.3.3 commands/init
lerna add fs-extra@9.0.1 commands/init
如果命令中有--force时会确认两次,如果命令中无--force时只会做一次确认;
js
async prepare() {
const localPath = process.cwd();
// 1.当前目录是否为空
if (!this.isCwdEmpty(localPath)) {
// 询问是否继续创建
// 没有--force的命令会进行这次确认
let ifContinue = false;
if (!this.force) {
ifContinue = (
await inquirer.prompt([
{
type: "confirm",
name: "ifContinue",
default: false,
message: "当前文件夹不为空,是否继续创建项目?",
},
])
).ifContinue;
if (!ifContinue) {
return;
}
}
// 2.是否为强制更新
if (ifContinue || this.force) {
// 清空当前目录,给用户做二次确认
const { secondContinue } = await inquirer.prompt([
{
type: "confirm",
name: "secondContinue",
default: false,
message: "是否确认清空当前目录下的文件?",
},
]);
if (secondContinue) {
fse.emptyDirSync(localPath);
} else {
return;
}
}
} else {
}
return this.getProjectInfo();
}
项目名称和版本号合法性校验
js
lerna add semver@7.3.4 commands/init
js
// 当选择时项目类型时
const o = await inquirer.prompt([
{
type: "input",
name: "projectName",
message: "请输入项目名称",
default: "",
validate: function (v) {
const done = this.async();
// Do async stuff
setTimeout(function () {
// 1.输入的首字符;
// 2.尾字符必须是英文字符或数字,不能为字符
// 3.字符近允许是"-_""
if (
!/^[a-zA-Z]+([-][a-zA-Z][a-zA-Z0-9]*|[_][a-zA-Z][a-zA-Z0-9]*|[a-zA-Z0-9])*$/.test(
v
)
) {
done("请输入合法的项目名称");
} else {
done(null, true);
}
}, 0);
},
filter: function (v) {
return v;
},
},
{
type: "input",
name: "projectVersion",
message: "请输入版本号",
default: "1.0.0",
validate: function (v) {
const done = this.async();
setTimeout(function () {
if (!!!semver.valid(v)) {
done("请输入合法的版本号");
} else {
done(null, true);
}
}, 0);
},
filter: function (v) {
if (!!semver.valid(v)) {
return semver.valid(v);
} else {
return v;
}
},
},
]);
下载模版
下载模版步骤:
1.通过项目模板api获取项目模板信息
1.1.通过egg.js搭建一套后端系统提供api
1.2.通过api的项目存储项目模板(vue-cli、vue-element-admin)
1.3.将项目模版信息存储到mongodb数据库中
1.4.通过egg.js获取mongodb中的数据并且通过API返回
通过api的项目存储项目模板
首先创建项目:
js
mkdir mj-cli-dev-template
cd mj-cli-dev-template
mkdir mj-cli-dev-template-vue3
cd mj-cli-dev-template-vue3
npm init -y
cnpm i -g @vue/cli
vue create vue-test
每次都因为node版本问题卡好久;
在vue-test中执行npm run serve
;vue3项目模板就创建好了;
接下来精简一下项目,删除一些不必要的文件,比如node_modolues等;然后移动到template的文件夹中; 在mj-cli-dev-template-vue3
目录下执行npm publish
发布这个包; 接着需要在mongodb中存储这个模版信息,实现一个API,让脚手架端能获取到这个模版;
在mongodb中写入一条数据:
js
db.project.insert({name: 'vue3标准模版',
npmName: 'mj-cli-dev-template-vue3',
version: '1.0.0',
})
此时在mj-cli-dev-server
服务中可以获取到mongodb
中的数据;
接着我们就要去脚手架mj-cli-dev
中请求数据;在utils目录下创建request包;
js
lerna create @mj-cli-dev/request
// 安装axios
lerna add axios@0.21.1 utils/request
request/lib/index.js文件;
js
"use strict";
const axios = require("axios");
const BASE_URL = process.env.MJ_CLI_BASE_URL
? process.env.MJ_CLI_BASE_URL
: "http://127.0.0.1:7002";
const request = axios.create({
baseURL: BASE_URL,
timeout: 5000,
});
request.interceptors.response.use(
(response) => {
if (response.status === 200) {
return response.data;
}
},
(error) => {
return Promise.reject(error);
}
);
module.exports = request;
在commands/init模块中引用utils/request模块; 新建commands/lib/getProjectTemplate
文件,在commands/lib/index的prepare
方法加入判断逻辑:
js
// 0.判断项目模版是否存在
const template = await getProjectTemplate();
if (!template || template.length == 0) {
this.template = template;
throw new Error("项目模版不存在!");
}
通过环境变量配置默认URL+选择项目模板功能开发
checkEnv方法还遗留了问题;
commands/init模块getProjectInfo中增加选择模板命令;
js
{
type: "list",
name: "projectTemplate",
message: "请选择项目模板",
choices: this.createTemplateChioice(),
},
// 生成模板选项
createTemplateChioice() {
const arr = this.template.map((item) => ({
name: item.npmName,
value: item.name,
}));
return arr;
}
下载一个新的模板vue-element-admin-master
,点击这里;将这个模版也放入之前mj-cli-dev-template中;为vue-element-admin-master
精简代码然后npm run dev
运行起来;新建一个目录,然后将下载的vue-element-admin-master
放到template
文件夹下;
接着进入mj-cli-dev-template-vue-element-admin
目录下执行发布npm publish
命令;
发布成功之后,我们也把这个模版的信息维护到mongodb中;
js
db.project.insert({name: 'vue-element-admin',
npmName: 'mj-cli-dev-template-vue-element-admin',
version: '1.0.0',
})
数据插入成功之后,再去执行脚手架命令,现在就显示出了两个模板信息;
脚手架下载项目模版
我们会把templage拷贝到用户的目录下;
首先在commands/init模块依赖models/command模块;
安装user-home包;
在commands/init模块增加下载模版的方法:
js
// 下载模版
async downloadTemplate() {
const { projectTemplate } = this.projectInfo;
const templateInfo = this.template.find(
(item) => item.npmName === projectTemplate
);
const targetPath = path.resolve(userHome, "mj-cli-dev", "template");
const storeDir = path.resolve(
userHome,
"mj-cli-dev",
"template",
"node_modules"
);
const { npmName, version } = templateInfo;
const templateNpm = new Package({
targetPath,
storeDir,
packageName: npmName,
packageVersion: version,
});
// 不存在时
if (!(await templateNpm.exists())) {
await templateNpm.install();
} else {
await templateNpm.update();
}
}
通过spinner实现命令行loading效果
人家自带loading了。。。这个功能略过。。。