脚手架下载项目模版功能实现流程

项目创建前准备阶段架构设计:

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了。。。这个功能略过。。。

相关推荐
持久的棒棒君11 分钟前
ElementUI 2.x 输入框回车后在调用接口进行远程搜索功能
前端·javascript·elementui
2401_8572979121 分钟前
秋招内推2025-招联金融
java·前端·算法·金融·求职招聘
undefined&&懒洋洋1 小时前
Web和UE5像素流送、通信教程
前端·ue5
VXbishe3 小时前
(附源码)基于springboot的“我来找房”微信小程序的设计与实现-计算机毕设 23157
java·python·微信小程序·node.js·c#·php·课程设计
IT小白33 小时前
node启动websocket保持后台一直运行
websocket·node.js
大前端爱好者3 小时前
React 19 新特性详解
前端
小程xy3 小时前
react 知识点汇总(非常全面)
前端·javascript·react.js
随云6323 小时前
WebGL编程指南之着色器语言GLSL ES(入门GLSL ES这篇就够了)
前端·webgl
随云6323 小时前
WebGL编程指南之进入三维世界
前端·webgl
寻找09之夏4 小时前
【Vue3实战】:用导航守卫拦截未保存的编辑,提升用户体验
前端·vue.js