从0-1实现一个自己的脚手架

当我们在开发的时候,时常会在创建项目的时候重复很多操作,比如下载相同的依赖(锁定依赖版本)封装网络请求封装路由配置代码提示/检查配置vite/webpack打包配置自动导入(auto-import)配置项目基础结构注册全局指令等,有非常多重复的步骤其实都是可以被省略的,或者说开发者应该专注于业务的实现,而不是在这些上浪费大量的时间,所以我们可以将以上重复且关键的步骤提炼出来放在一个仓库里,然后创建一个属于我们自己的脚手架并放在npm上,通过全局安装这个脚手架,使用脚手架指令就可以直接下载我们存在仓库里面的基础模版了。

 一、创建脚手架

1、初始化脚手架

npm init -y

2、创建入口文件

mkdir bin

cd bin

touch www.js

//然后进入 www.js 文件 ,将下列代码粘贴上去

#! /usr/bin/env node

require('../index.js')

3、配置package.json的入口文件

//将以下代码贴入 package.json中

  "bin": "bin/www",

4、在根目录创建index.js并软链接到全局

touch index.js

cnpm link

5、安装依赖(工具)

    "axios": "^0.26.1",
    "chalk": "^4.0.0",
    "commander": "^9.0.0",
    "download-git-repo": "^3.0.2",
    "figlet": "^1.5.2",
    "fs-extra": "^10.0.1",
    "gitee-repo": "^0.0.2",
    "inquirer": "^8.2.1",
    "ora": "^5.0.0"

6、创建配置文件

        我已经将所要配置的文件都放在代码里面的,直接贴进去就行,当然也可以在我的基础上加以改造,可以让脚手架变的更加完善。

mkdir lib

touch api.js create.js Creator.js util.js

//index.js
const program = require("commander");
const chalk = require("chalk");
const figlet = require("figlet");
program
  .command("create <project-name>") //增加创建指令
  .description("create a new project") //添加描述信息
  .option("-f, --force", "overwrite target directory if it exists") //强制覆盖
  .action((projectName, cmd) => {
    // 处理用户输入create 指令附加的参数
    require("./lib/create")(projectName, cmd);
  });

program
  .command("config [value]")
  .description("inspect and modify the config")
  .option("-g, --get <key>", "get value by key")
  .option("-s, --set <key> <value>", "set option[key] is value")
  .option("-d, --detede <key>", "delete option by key")
  .action((value, keys) => {
    // 处理用户输入create 指令附加的参数
    console.log(value, keys);
  });

program.on("--help", function () {
  console.log(
    "\r\n" +
      figlet.textSync("当输入 --help 所展示的颜文字,可以自定义", {
        font: "3D-ASCII",
        horizontalLayout: "default",
        verticalLayout: "default",
        width: 80,
        whitespaceBreak: true,
      })
  );
  // 前后两个空行调整格式,更舒适
  console.log();
  console.log(
    `Run ${chalk.cyan(
      "自定义颜文字 <command> --help"
    )} for detailed usage of given command.`
  );
  console.log();
});

program
  .name("自定义脚手架命名")
  .usage(`<command> [option]`)
  .version(`dragon-cli-frontend ${require("./package.json").version}`);

// 解析用户执行时输入的参数
// process.argv 是 nodejs 提供的属性
// npm run server --port 3000
// 后面的 --port 3000 就是用户输入的参数
program.parse(process.argv);
********************************************************************************

//api.js
const axios = require("axios");

// 拦截全局请求响应
axios.interceptors.response.use((res) => {
  return res.data;
});

/**
 * 获取模版
 * @returns Promise
 */

async function getZhuRongRepo() {
  //这里我填写是gitee的
  return axios.get("https://gitee.com/api/v5/users/**这是你gitee的用户名**/repos");
}

/**
 * 获取仓库下的版本
 * @param {String} repo 仓库名称
 * @returns Promise
 */

async function getTagsByRepo(repo) {
  return axios.get(`https://gitee.com/api/v5/repos/**这是你gitee的用户名**/${repo}/tags`);
}

module.exports = {
    getZhuRongRepo,
    getTagsByRepo
}
********************************************************************************

//create.js
const path = require("path");
const fs = require("fs-extra");
const Inquirer = require("inquirer");
const Creator = require("./Creator");
const { loading } = require("./util");
module.exports = async function (projectName, options) {
  // 获取当前工作目录
  const cwd = process.cwd();
  const targetDirectory = path.join(cwd, projectName);

  if (fs.existsSync(targetDirectory)) {
    if (options.force) {
      // 删除重名目录
      await fs.remove(targetDirectory);
    } else {
      let { isOverwrite } = await new Inquirer.prompt([
        // 返回值为promise
        {
          name: "isOverwrite", // 与返回值对应
          type: "list", // list 类型
          message: "Target directory exists, Please choose an action",
          choices: [
            { name: "Overwrite", value: true },
            { name: "Cancel", value: false },
          ],
        },
      ]);
      if (!isOverwrite) {
        console.log("Cancel");
        return;
      } else {
        await loading(
          `Removing ${projectName}, please wait a minute`,
          fs.remove,
          targetDirectory
        );
      }
    }
  }

  // 创建项目
  const creator = new Creator(projectName, targetDirectory);

  creator.create();
};
********************************************************************************

//Creator.js
const inquirer = require("inquirer");
const downloadGitRepo = require("gitee-repo");
const chalk = require("chalk");
const fs = require("fs-extra");
const util = require("util");
const path = require("path");
const { loading } = require("./util");
const { getZhuRongRepo, getTagsByRepo } = require("./api");

class Creator {
  constructor(name, target) {
    this.name = name;
    this.target = target;
    // 转化为 promise 方法
    this.downloadGitRepo = util.promisify(downloadGitRepo);
  }
  // 创建项目部分
  async create() {
    // 仓库信息 —— 模板信息
    let repo = await this.getRepoInfo();

    // 标签信息 —— 版本信息
    console.log("我是版本信息", repo);
    let tag = await this.getTagInfo(repo);

    // 下载模板
    await this.download(repo, tag);
    // 模板使用提示
    console.log(`\r\nSuccessfully created project ${chalk.cyan(this.name)}`);
    console.log(`\r\n  cd ${chalk.cyan(this.name)}`);
    console.log("  npm install");
    console.log("  npm run serve\r\n");
  }
  // 获取模板信息及用户选择的模板
  async getRepoInfo() {
    // 获取组织下的仓库信息
    let repoList = await loading(
      "waiting for fetching template",
      getZhuRongRepo
    );
    if (!repoList) return;
    // 提取仓库名
    const repos = repoList.map((item) => item.name);
    // 选取模板信息
    let { repo } = await new inquirer.prompt([
      {
        name: "repo",
        type: "list",
        message: "Please choose a template to create project",
        choices: repos,
      },
    ]);
    return repo;
  }
  // 获取版本信息及用户选择的版本
  async getTagInfo(repo) {
    console.log(repo);
    let tagList = await loading(
      "waiting for fetching version",
      getTagsByRepo,
      repo
    );
    if (!tagList) return;
    const tags = tagList.map((item) => item.name);
    // 选取模板信息
    let { tag } = await new inquirer.prompt([
      {
        name: "tag",
        type: "list",
        message: "Please choose a version to create project",
        choices: tags,
      },
    ]);
    return tag;
  }
  // 下载git仓库
  async download(repo, tag) {
    // 模板下载地址
    const templateUrl = `gitee:***gitee的用户名***/${repo}${tag ? "#" + tag : ""}`;
    // 调用 downloadGitRepo 方法将对应模板下载到指定目录
    await loading(
      "downloading template, please wait",
      this.downloadGitRepo,
      templateUrl,
      path.resolve(process.cwd(), this.target) // 项目创建位置
    );
    const packageJsonPath = path.join(
      process.cwd() + `/${this.name}`,
      "package.json"
    );
    const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
    packageJson.name = this.name;
    fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
  }
}

module.exports = Creator;
********************************************************************************

//utils.js
const ora = require("ora");

/**
 * 睡觉函数
 * @param {Number} n 睡眠时间
 */
function sleep(n) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve();
    }, n);
  });
}

/**
 * loading加载效果
 * @param {String} message 加载信息
 * @param {Function} fn 加载函数
 * @param {List} args fn 函数执行的参数
 * @returns 异步调用返回值
 */
async function loading(message, fn, ...args) {
  const spinner = ora(message);
  spinner.start(); // 开启加载
  try {
    let executeRes = await fn(...args);
    spinner.succeed();
    return executeRes;
  } catch (error) {
    spinner.fail("request fail, reTrying");
    await sleep(1000);
    return loading(message, fn, ...args);
  }
}

module.exports = {
  loading,
};

7、发布脚手架到npm上

1、先 npm login 登陆npm(需要先有npm账号,可以先去npm上创建一个账号)

2、发布npm包 使用 npm push 将当前包传上去(切记npm的源要是国外哪个,不能是国内的)

完成后就可以登陆npm上看见了。

二、发布gitee基础模版标签(否则在创建的时候会失败)

        在我们使用脚手架的时候有一步就是选择要下载基础模版的版本,所以我们需要给gitee上的项目打一个标签推上去

//查看标签

git tag

//创建标签

git tag v1.0.0
//或
git tag v1.0.0 -m "添加新功能"

//查看标签
git tag

//v1.0.0

git show v1.0.0
//将标签推送到远程

三、源码地址

dragom-cli-frontend:dragon-cli-frontend - npm

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/726842.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

连锁餐厅降低员工离职率:发誓!绝不是靠“舌尖上的诱惑”

员工社交与成长&#xff0c;企业福利与文化&#xff0c;沃可趣多维度优化员工体验。 连锁餐饮业在全球范围内迅速发展&#xff0c;要为消费者提供更多便利&#xff0c;2023年中国餐饮市场连锁化率达到21%。 然而&#xff0c;这些分散式门店为企业运营创造了挑战。Black Box I…

爬山算法优点

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

前端练习小项目——视觉冲击卡片

前言&#xff1a; 前言&#xff1a;在学习完HTML和CSS之后&#xff0c;我们就可以开始做一些小项目了&#xff0c;本篇文章所讲的小项目为——视觉冲击卡片 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客 先让我们看一下效果&a…

美国铁路客运巨头Amtrak泄漏旅客数据,数据销毁 硬盘销毁 文件销毁

旅客的Guest Rewards常旅客积分账户的个人信息被大量窃取。 美国国家客运铁路公司&#xff08;Amtrak&#xff09;近日披露了一起数据泄露事件&#xff0c;旅客的Guest Rewards常旅客积分账户的个人信息被大量窃取。 根据Amtrak向马萨诸塞州提交的泄露通知&#xff0c;5月15日…

微信小程序navigateTo异常(APP-SERVICE-SDK:Unknown URL)

背景 在开发小程序时&#xff0c;可能会用到banner&#xff0c;通过banner跳转至各种子页面。但是因为小程序自身的因素&#xff0c;有些是不允许的&#xff0c;比如通过banner跳转一个http/https链接。如果使用 wx.navigateTo完成跳转时&#xff0c;就会发生异常。 navigate…

HarmonyOS模拟器(phone-x86-api9)一直卡顿的解决方法

在DevEco Studio 3.1.1 Release版本中的Device Manager中创建本地的模拟器&#xff0c;创建phone-x86-api9模拟器成功&#xff0c;但是启动该新建的模拟器一直显示"HarmonyOS"logo图片&#xff0c;然后一直卡在这里&#xff0c;运行结果如下所示&#xff1a; 检查模…

分布式锁三种方案

基于数据库的分布式锁&#xff08;基于主键id和唯一索引&#xff09; 1基于主键实现分布式锁 2基于唯一索引实现分布式锁 其实原理一致&#xff0c;都是采用一个唯一的标识进行判断是否加锁。 原理&#xff1a;通过主键或者唯一索性两者都是唯一的特性&#xff0c;如果多个…

Ike-scan一键发现通过互联网的IPsec VPN服务器(KALI工具系列二十八)

目录 1、KALI LINUX 简介 2、Ike-scan工具简介 3、信息收集 3.1 目标主机IP&#xff08;服务器&#xff09; 3.2 KALI的IP 4、操作示例 4.1 简单扫描 4.2 范围扫描 4.3 扫描多个目标 4.4 输出扫描结果 4.5 特殊扫描 5、总结 1、KALI LINUX 简介 Kali Linux 是一个功…

区块链技术:探索7个物联网应用的潜力

在当今数字化时代&#xff0c;区块链技术逐渐成为一种受到广泛关注的技术创新。本文将以《区块链技术&#xff1a;探索7个物联网应用的潜力》为题&#xff0c;介绍区块链技术在物联网领域的七个应用潜力。物联网作为未来发展的重要方向&#xff0c;结合区块链技术的应用将为我们…

SAP PI/PO获取文件名及路径

Sender Adapter设置如下&#xff1a; UDF定义如下&#xff1a; DynamicConfiguration conf (DynamicConfiguration) container.getTransformationParameters().get(StreamTransformationConstants.DYNAMIC_CONFIGURATION); //get file name DynamicConfigurationKey keyFile…

r2frida:基于Frida的远程进程安全检测和通信工具

关于r2frida r2frida是一款能够将Radare2和Frida的功能合二为一的强大工具&#xff0c;该工具本质上是一个Radare2的自包含插件&#xff0c;可以帮助广大研究人员利用Frida的功能实现对目标进程的远程安全检测和通信管理。 Radare2项目提供了针对逆向工程分析的完整工具链&…

[Shell编程学习路线]——for循环应用技巧 语法和案例

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f6e0;️Shell编程专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年6月20日16点21分 &#x1f004;️文章质量&#xff1a;96分 目录 ————前言———— for 循环语句 基本结构 图示原理…

设计程序,实现高精度圆周率的计算和存储,使用线性表突破程序设计语言内置变量的数值和有效数字范围限制

一、使用线性表突破程序设计语言内置变量的数值和有效数字范围的限制&#xff0c;为了实现高精度圆周率的计算&#xff0c;先根据数学公式进行对PI高精度运算&#xff0c;如图1-1。根据这个数学公式 π2 0nn!2n1‼ 即 Rn1Rn*n2n1&#xff0c;R11&#xff0c;sum π2* n1∞Rn 来…

02 Pytorch_NLP

1. N-gram n决定关联信息 2. TF____IDF TF&#xff1a;词频 IDF&#xff1a;逆向序列 假如&#xff1a;TF * IDF 就是当前的文件&#xff0c;那么乘积反而更大&#xff01; 因为它只出现在 特定的文章中&#xff01; TF-IDF 简介 TF-IDF&#xff08;Term Frequency-Inverse…

Ansys Mechanical|学习方法

Ansys Mechanical是Ansys的旗舰产品之一&#xff0c;涉及的学科体系全面丰富&#xff0c;包括的力学分支主要有理论力学&#xff0c;振动理论&#xff0c;连续介质力学&#xff0c;固态力学&#xff0c;物理力学&#xff0c;爆炸力学及应用力学等。 在自媒体及数字经济飞速发展…

【CSS in Depth2精译】1.2 继承~1.3 特殊值

文章目录 1.2 继承1.3 特殊值1.3.1 inherit 关键字1.3.2 initial 关键字1.3.3 unset 关键字1.3.4 revert 关键字 1.2 继承 除了层叠&#xff0c;还有一种给元素设置样式的方式&#xff1a;继承。经常有人把层叠与继承的概念弄混淆。它们虽然有关联&#xff0c;但也应该分辨清楚…

react实现窗口悬浮框,可拖拽、折叠、滚动

1、效果如下 2、如下两个文件不需要修改 drag.js import React from "react"; import PropTypes from "prop-types";export default class DragM extends React.Component {static propTypes {children: PropTypes.element.isRequired};static defaultP…

什么是片上端接校准(On Die Termination Calibration)技术?

On Die Termination Calibration 随着对于数字系统性能要求的不断提高&#xff0c;对信号完整性的要求也越来越高&#xff0c;从而能够在更高的速率下可靠运行。信号线端接是信号完整性管理中的有用元件&#xff0c;可以在memory外部或memory内部使用。在DRAM器件中加入电阻端接…

ChatmoneyAI如狂风般席卷广告创意舞台,轻松闯荡财富之海!

本文由 ChatMoney团队出品 引言 在广告创意行业&#xff0c;创新和高效是赢得市场的关键。而我今天要分享的就是如何利用ChatmoneyAI这款强大的人工智能工具&#xff0c;打破创新难题&#xff0c;赚取丰厚收益。 让我告诉你一个小秘密&#xff0c;有客户曾在一个月内&#xf…

React Native性能优化红宝书

一、React Native介绍 React Native 是Facebook在React.js Conf2015 推出的开源框架&#xff0c;使用React和应用平台的原生功能来构建 Android 和 iOS 应用。通过 React Native&#xff0c;可以使用 JavaScript 来访问移动平台的 API&#xff0c;使用 React 组件来描述 UI 的…