脚手架拉取模板文件的新思路

时间:2020-7-27 作者:admin

前言

通过编写脚手架,可以有效提高开发效率,减少一些重复性的工作,项目初始化时拉取远端模板就是一个比较好的方向。

之前有些大佬们(基于node.js的脚手架工具开发经历【中高级前端必备】手摸手教你撸一个脚手架)已经给出了拉取远端模板的思路,美中不足的地方是都使用的download-git-repo这个依赖库,这个库只能从项目根目录拉取项目模板,如果我们想在某一个仓库中集中管理这些模板,将模板放在仓库的一个目录下,这个库就无能为力了,这里参考搞一个Node Cli来提升工作效率的思路,从指定目录开始递归遍历获取文件并拉取到本地。

准备

获取项目文件信息通过github/gitlab API来完成,其实也就是两个API,获取目录下文件信息和获取文件。

gitlab

// 获取指定目录下的文件信息
GET /projects/:id/repository/tree
// 获取指定路径的文件,需要仓库是公开的public
GET /projects/:id/repository/files/:file_path/raw

具体的大家可以前往gitlab API中查看,由于不在公司,没有权限访问gitlab,所以本文的示例代码是基于github编写的,gitlab API的使用和github的类似。

github

// 获取指定目录下的文件信息
GET /repos/{owner}/{repo}/contents/{path}

这个API的返回中可获取到文件的下载链接。具体的大家可以前往github API中查看。

简单实现

使用DFS(深度优先搜索)来进行模板文件的遍历,从模板所在目录开始遍历,遇到目录继续递归,遇到文件执行写入本地操作。

  1. 首先进行一些变量的定义,包括模板所在仓库的名称、用户名、模板所在目录等(脚手架所需要的一些依赖安装和配置之前的文章已经描述的比较详细了,这里就不再赘述,具体可查看【中高级前端必备】手摸手教你撸一个脚手架):
import got from 'got';
import fs from 'fs';

const baseUrl = 'https://api.github.com'
const user = 'xxx';  // 替换为自己的github用户名
const repositry = 'xxx'; // 自己github里面一个仓库的名称
const basePath = 'templates/'; // 模板所在路径
const getRootList = `${baseUrl}/repos/${user}/${repositry}/contents`; // 获取文件列表
  1. 获取模板目录下文件列表,参数是本地目录名和文件相对仓库根目录的路径
  1. 首先进行请求地址的拼装,项目根目录 + 传入的路径即可

  2. 因为文件所在路径是从仓库根目录到模板文件的整个路径,本地路径拼接时需要注意将模板所在路径去掉

    如:path = ‘/templates/a.js’,拷贝到本地demo目录下应该是’demo/a.js’

  3. 判断本地拼接目录是否存在,如果不存在,则要创建该目录,这一步是为了之后写入文件做准备

  4. 解析获取的数据,判断数组各项的type字段,file就执行写入文件操作,dir则继续递归处理

const getList = async function (root, path) {
    let url = getRootList + path;
    let list = await got(url);
    let targetDir = root + path.replace(basePath, '');
    // 判断是否存在当前目录,不存在就新建
    if (!fs.existsSync(targetDir)) {
        fs.mkdirSync(targetDir);
    }

    for (let e of JSON.parse(list.body)) {
        if (e.type === 'file') {
            await getFile(root, e)
        } else if (e.type === 'dir') {
            await getList(root, '/' + e.path);
        }
    }
}
  1. 获取文件并写入本地,这里我在最开始时没有限制新的目录必须不存在,因此在这里也没有对目录下的文件进行覆盖操作
  1. 拼装本地文件所在路径,同样的,需要将接口中返回的path中的模板所在路径去掉
  2. 检测文件是否存在,如果在开始时限制了本地文件夹必须是空的这里就不需要写了
  3. 获取文件内容
  4. 进行一些占位符的替换操作
  5. 将文件写入本地
const getFile = async (root, info) => {
    let target = `${root}/${info.path.replace(basePath, '')}`;
    if (fs.existsSync(target)) {
        return;
    }
    let { body } = await got(info.download_url);
    // 这里可进行一些文件内容的改写操作 如 if(info.path = 'xxx') body = body.replace(/$[^$]+$/, root)
    fs.writeFileSync(target, body);
}
  1. 出口函数,这里只是简单的接受一个参数,如果没有输入就会提示报错
export default function (...args) {
    if (!args.length) {
        console.log(chalk.red('请输入项目名'))
        return
    }
    if (!fs.existsSync(args[0])) fs.mkdirSync(args[0])
    // 初始目录即为模板所在目录
    return getList(args[0], '/' + basePath)
}

至此,就完成了一个简单的拉取仓库指定目录下文件的操作。

一些需要注意的点

  1. 在上面拉取文件列表函数getList中会进行大量的请求操作,我使用的async/await单线操作,这样做确实会导致文件拉取效率变慢,这样做的目的主要是为了方便拉取模板操作完成后进行一些后续操作,如:
let fn = require('./init.js')
fn.default().then(() => {
    console.log(chalk.green('completed'))
})
  1. github API有坑,速度慢,需要自己配置相关的host(可参考这篇文章一文入门DNS?从访问GitHub开始),有时候会拉取到一半卡住。如果文件数量过大,还会被限制,一小时内未授权的请求只能请求60次,授权后限制在5000次,所以模板放在github上的不建议用这个方法。在公司写的请求gitlab的API倒是没这个限制。
  2. 本文只是实现了脚手架的拉取远端模板的简单操作init.js,脚手架完整配置建议参考文章开头的几篇文章,写的都比较详细。本文主要目的是使用gitlab/github API完成一些骚操作,其实创建仓库、删除或更改仓库内文件也可以通过API实现。
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。