搭建自己的脚手架(四)---创建下载模板(完结)

76 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 16 天,点击查看活动详情

前言

上一篇文章完成了初始化命令和帮助信息等选项的创建,初始化命令的实现我们也写了好多,今天这一篇文章,我们讲继续完善init函数

当前痛点

  • github在国内访问非常不稳定,github api请求每次都是超时
  • 请求模板和创建目录的时候都比较耗时,这时就需要在命令行显示loading效果,告诉用户再进行操作
  • spwan命令在mac很好使用,但是在windows上有兼容性问题存在

痛点解决方案

  • 使用gitee仓库代替github仓库, 使请求稳定
  • ora命令行loading效果
  • 使用cross-spawn解决spwan的操作系统兼容性问题

新建请求工具模块

请求gitee中的所有模板仓库,供用户选择想要的模板

创建请求库

lerna create @wson-koa2-cli/request utils

utils/request/lib/index.js

'use strict';
const axios = require('axios');
const giteeRepo = 'https://gitee.com/api/v5/users/Yjust4U/repos';


axios.interceptions.response.use(res => {
    return res.data;
})

async function getTemplateList() {
    // 获取所有模板
    const repos = axios.get(giteeRepo);
    // 筛选name属性中包含template的仓库
    const templates = repos.filter((repo) => repo.name.includes('template'));
    return templates;
}

module.exports = {
    getTemplateList
};

新建generator类来处理创建逻辑

commands/init/lib/generator.js

const { getTemplateList } = require("@wson-koa2-cli/request");
const inquirer = require("inquirer");

class Generator{ 
  constructor(projectName, targetDir) {
    // 项目名称
    this.projectName = projectName;
    // 项目目录
    this.targetDir = targetDir;
  }

  // 获取用户选择的模板
  
  
  // 3)return 用户选择的名称
  async getRepo() {
    // 1)从远程拉取模板数据
    const templateList = await getTemplateList();
    if (!templateList.length) return;

    // 2)用户选择自己要下载的模板名称
    const { template } = await inquirer.prompt([
      {
        type: 'list',
        name: 'template',
        message: "请选择模板来初始化项目",
        choices: templateList
      }
    ])
    
    return template;
  }

  // 核心创建逻辑
  async create() {
    // 1)获取模板名称
    const repo = await this.getRepo()
    
    console.log('用户选择了,repo=' + repo)
  }
}


module.exports = Generator;

其他代码不变

下载远程模板

创建utils库,用来存放工具函数

lerna create @wson-koa2-cli/utils utils

utils/utils/lib/index.js 封装spawn异步请求

'use strict';

const spawn = require('cross-spawn'); // 用来解决操作系统兼容性问题

// 异步执行子进程命令
function execAsync(commands, args, options = {}) {
    return new Promise((resolve, reject) => {
        const p = spawn(commands, args, options);
        p.on('error', e => {
            reject(e)
        })
        p.on('exit', c => {
            resolve(c)
        })
    })
}

module.exports = {
    execAsync
}

继续完善generator类

commands/init/lib/generator.js

const { getTemplateList } = require("@wson-koa2-cli/request");
const { execAsync } = require("@wson-koa2-cli/utils");
const inquirer = require("inquirer");
const path = require('path');
const fs = require('fs-extra');

class Generator{ 
  constructor(projectName, targetDir) {
    // 项目名称
    this.projectName = projectName;
    // 项目目录
    this.targetDir = targetDir;
  }


  
  // return 用户选择的模板名称
  async getRepo() {
    // 1)从远程拉取模板数据
    const templateList = await getTemplateList();
    if (!templateList.length) return;

    // 2)用户选择自己要下载的模板名称
    const { template } = await inquirer.prompt([
      {
        type: 'list',
        name: 'template',
        message: "请选择模板来初始化项目",
        choices: templateList
      }
    ])
    
    return template;
  }

  // clone远程模板
  async download(repo){

    // 1)拼接下载地址
    const repoUrl = `https://gitee.com/Yjust4U/${repo}.git`;

    // 把安装后的模板放到targetDir中
    await execAsync('git', ['clone', repoUrl], {
      stdio: 'inherit',
      cwd: this.targetDir,
    })
  }

  async toTargetDir(repo) {
    // 获取模板目录
    const templatePath = path.join(this.targetDir, repo);
    // .git文件所在位置
    const gitFile = path.join(this.targetDir, '/.git');
    // 赋值到targetDir中
    await fs.copy(templatePath, this.targetDir);
    // 删除模板文件
    await fs.remove(templatePath);
    // 删除.git文件
    await fs.remove(gitFile);
  }

  // 核心创建逻辑
  async create() {
    // 1)获取模板名称
    const repo = await this.getRepo();
    // 2)下载模板到模板目录
    await this.download(repo);

    // 3) 把模板拷贝到项目目录
    await this.toTargetDir(repo);
    console.log('下载完成');
  }
}


module.exports = Generator;

ora 命令行loading动效

首先我们得使用ora库封装一下loading动效

utils/utils/lib/index.js
// 添加加载动画
async function wrapLoading(fn, message, failMessage, ...args) {
    // 使用 ora 初始化,传入提示信息 message
    const spinner = ora(message);
    // 开始加载动画
    spinner.start();
  
    try {
      // 执行传入方法 fn
      const result = await fn.apply(null, ...args);
      // 状态为修改为成功
      spinner.succeed();
      return result; 
    } catch (error) {
      // 状态为修改为失败
      spinner.fail(failMessage)
    } 
}

封装好以后了,两个地方需要使用,第一个是

commands/init/lib/generator.js

中的获取仓库模板

  // return 用户选择的模板名称
  async getRepo() {
    // 1)从远程拉取模板数据
    const templateList = await wrapLoading(getTemplateList, '获取仓库模板中....', '获取仓库模板失败');
    if (!templateList.length) return;

    // 2)用户选择自己要下载的模板名称
    const { template } = await inquirer.prompt([
      {
        type: 'list',
        name: 'template',
        message: "请选择模板来初始化项目",
        choices: templateList
      }
    ])
    
    return template;
  }

还有一处是下载模板

  // clone远程模板
  async download(repo){

    // 1)拼接下载地址
    const repoUrl = `https://gitee.com/Yjust4U/${repo}.git`;

    // 把安装后的模板放到targetDir中
    const result = await wrapLoading(execAsync, '下载模板中....', '下载模板失败', ['git', ['clone', repoUrl], {
      cwd: this.targetDir,
    }]);
    return result;
  }

两处都搞定以后我们的项目就完美了

测试

接下来测试一下

image.png

good

最后

我的项目已经上传到npm上了, 大家也可以全局npm安装来把玩,package名称为@wson-koa2-cli/core

参考