自行搭建一个前端脚手架

70 阅读3分钟

第一步:创建文件夹CLI

第二步:在CLI目录终端执行名命令:npm init -y

第三步:在CLI目录下创建文件夹

创建一个bin文件夹,添加test-cli.js文件,在这个文件中写下#! /usr/bin/env node

第四步:

  • 在package.json中指定执行命令和执行的文件添加bin和min目录
  • { "name": "test-cli", "version": "1.0.0", "description": "", "main": "/bin/test-cli.js", "bin": "/bin/test-cli.js", "directories": { "lib": "lib" }, "scripts": { "test": "echo "Error: no test specified" && exit 1" }, "keywords": [], "author": "", "license": "ISC", }

第五步:在bin目录下执行npm link

第六步:在bin目录下执行test-cli,会输出你在test-cli文件中写下的打印信息

第七步:在package.json中添加"type": "module",然后安装所有依赖

  "name": "test-cli",
  "version": "1.0.0",
  "description": "",
  "main": "/bin/test-cli.js",
  "bin": "/bin/test-cli.js",
  "directories": {
    "lib": "lib"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "module",
  "dependencies": {
    "axios": "^1.5.0",
    "chalk": "^5.3.0",
    "commander": "^11.0.0",
    "download-git-repo": "^3.0.2",
    "fs-extra": "^11.1.1",
    "inquirer": "^9.2.10",
    "ora": "^7.0.1",
    "path": "^0.12.7",
    "util": "^0.12.5"
  }
}

8:新建lib文件夹,

import path from "path";
import Generator from "./creator.js";
import inquirer from "inquirer";
import fs from "fs-extra";
// 用于交互式询问用户问题
// 导出Generator类

//1. 抛出一个方法用来接收用户要创建的文件夹(项目)名 和 其他参数
const create = async function (name, options) {
  // 当前命令行选择的目录
  const cwd = process.cwd();
  // 需要创建的目录地址
  const targetAir = path.join(cwd, name);

  //2 判断是否存在相同的文件夹(项目)名
  // 目录是否已经存在?
  if (fs.existsSync(targetAir)) {
    // 是否为强制创建?
    if (options.force) {
      await fs.remove(targetAir);
    } else {
      // 询问用户是否确定要覆盖
      let { action } = await inquirer.prompt([
        {
          name: "action",
          type: "list",
          message: "Target directory already exists Pick an action:",
          choices: [
            {
              name: "Overwrite",
              value: "overwrite",
            },
            {
              name: "Cancel",
              value: false,
            },
          ],
        },
      ]);
      // 如果用户拒绝覆盖则停止剩余操作
      if (!action) {
        return;
      } else if (action === "overwrite") {
        // 移除已存在的目录
        console.log(`\r\nRemoving...`);
        await fs.remove(targetAir);
      }
    }
  }

  //3 新建generator类
  const generator = new Generator(name, targetAir);
  generator.create();
};

export default create;

import { getRepoList, getTagList } from "./request.js";
import { loading } from "./utils.js";
import downloadGitRepo from "download-git-repo";
import inquirer from "inquirer";
import chalk from "chalk";
import util from "util";
import path from "path";

class Generator {
  constructor(name, targetDir) {
    // 目录名称
    this.name = name;
    // 创建位置
    this.targetDir = targetDir;
    // 对 download-git-repo 进行 promise 化改造
    this.downloadGitRepo = util.promisify(downloadGitRepo);
  }

  // 获取用户选择的模板
  // 1)从远程拉取模板数据
  // 2)用户选择自己新下载的模板名称
  // 3)return 用户选择的名称

  async getRepo() {
    // 1)从远程拉取模板数据
    const repoList = await loading(getRepoList, "waiting fetch template");
    if (!repoList) return;
    // 过滤我们需要的模板名称
    const repos = repoList.map((item) => item.name);

    // 2)用户选择自己新下载的模板名称
    const { repo } = await inquirer.prompt({
      name: "repo",
      type: "list",
      choices: repos,
      message: "Please choose a template to create project",
    });

    // 3)return 用户选择的名称
    return repo;
  }

  // 获取用户选择的版本
  // 1)基于 repo 结果,远程拉取对应的 tag 列表
  // 2)自动选择最新版的 tag

  async getTag(repo) {
    // 1)基于 repo 结果,远程拉取对应的 tag 列表
    const tags = await loading(getTagList, "waiting fetch tag", repo);
    if (!tags) return;

    // 过滤我们需要的 tag 名称
    const tagsList = tags.map((item) => item.name);

    // 2)return 用户选择的 tag
    return tagsList[0];
  }

  // 下载远程模板
  // 1)拼接下载地址
  // 2)调用下载方法
  async download(repo, tag) {
    // 1)拼接下载地址
    const requestUrl = `taoTests/${repo}${tag ? "#" + tag : ""}`;

    // 2)调用下载方法
    await loading(
      this.downloadGitRepo, // 远程下载方法
      "waiting download template", // 加载提示信息
      requestUrl, // 参数1: 下载地址
      path.resolve(process.cwd(), this.targetDir) // 参数2: 创建位置
    );
  }

  // 核心创建逻辑
  // 1)获取模板名称
  // 2)获取 tag 名称
  // 3)下载模板到模板目录
  // 4) 对uniapp模板中部分文件进行读写
  // 5) 模板使用提示
  async create() {
    // 1)获取模板名称
    const repo = await this.getRepo();

    // 2) 获取 tag 名称
    const tag = await this.getTag(repo);

    // 3)下载模板到模板目录
    await this.download(repo, tag);

    // 5)模板使用提示
    console.log(`\r\nSuccessfully created project ${chalk.cyan(this.name)}`);
    console.log(`\r\n  cd ${chalk.cyan(this.name)}`);
    console.log(`\r\n  启动前请务必阅读 ${chalk.cyan("README.md")} 文件`);
  }
}

export default Generator;

// 通过 axios 处理请求
import axios from "axios";

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

/**
 * 获取模板列表
 * @returns Promise
 */
async function getRepoList() {
  // return axios.get("https://api.github.com/orgs/geeksTest/repos");
  return axios.get("https://api.github.com/orgs/taoTests/repos");
}

/**
 * 获取版本信息
 * @param {string} repo 模板名称
 * @returns Promise
 */
async function getTagList(repo) {
  return axios.get(`https://api.github.com/repos/taoTests/${repo}/tags`);
}

export { getRepoList, getTagList };

9:去github新建组织和仓库,增加tag,修改3处路径为自己的

https://api.github.com/orgs/taoTests/repos:taoTests修改为你的组织名称 https://api.github.com/repos/taoTests/${repo}/tags:taoTests修改为你的组织名称 const requestUrl = taoTests/repo{repo}{tag ? "#" + tag : ""};:taoTests修改为你的组织名称

可以在浏览器回车测试是否调通接口返回正确信息 此文章为9月Day08学习笔记,内容来源于极客时间《重学前端》,强烈推荐该课程