手把手教学拥有自己的CLI

news2024/11/25 20:45:34

随着开发时间的增长,你积累的模版需要管理,不能老是复制粘贴。那么一个小小的cli 可以帮助你这个问题。它是你进行管理分类的管家,替你管理仓库和翻东西。

 

技术选型

 

  • NodeJS
  • TS
  • pnpm
  • unbuild : unbuild 是基于rollup 配置更加单的打包工具
  • chalk : 输出颜色
  • commander:命令管理
  • inquirer:询问
  • figlet:输出数字化字体
  • standard-version: 版本管理

因为我是前端 node对于我来说比较友好,node 环境电脑一般都有,写这种cli js其实是比较好的选择,灵活高效。但是我还是想用TS 🐶。

 

思路

 

开发cli 的目的主要是为了管理模版 ,所以我们需要自定义输入模版,这就用到了本地存储,存储选用文件存储。

  1. 命名行输入创建命令
  2. 查询自己的模版
  3. 选择模版
  4. 利用git 命令clone
  5. node更改模版需要更改的名称和内容
  6. 完成

 

开发前准备工作

 

步骤如下

  1. 创建项目
  2. 设置入口,并已验证
  3. 添加依赖

创建项目

初始化项目package.json

在这里插入图片描述
在这里插入图片描述

设置入口

在这里插入图片描述
在这里插入图片描述

link 到全局验证测试

在这里插入图片描述

在这里插入图片描述

添加依赖

添加生产依赖

pnpm add chalk commander figlet inquirer

在这里插入图片描述

TS 配置

安装ts 和 类型等开发依赖:

  "devDependencies": {
    "@types/figlet": "^1.5.8",
    "@types/inquirer": "^9.0.7",
    "@types/node": "^20.9.5",
    "typescript": "^5.3.2"
  },

在这里插入图片描述

tsconfig 配置:

{
  "include": ["src"],
  "compilerOptions": {
    "outDir": "dist",
    "target": "ES2022",
    "module": "ES2020",
    "moduleResolution": "bundler",
    "strict": true,
    "skipLibCheck": true,
    "declaration": false,
    "sourceMap": false,
    "noUnusedLocals": true,
    "esModuleInterop": true
  }
}

打包工具

打包工具参考尤雨溪在vue-cli 使用的工具,unbuild 简单高效。相信尤大大没错。

安装依赖:

pnpm add unbuild -D

添加配置:

在这里插入图片描述

import { defineBuildConfig } from "unbuild";

export default defineBuildConfig({
  entries: ["./src/index"],
  clean: true,
  rollup: {
    inlineDependencies: true,
    esbuild: {
      target: 'node18',
      minify: true,
    },
  }
});

package.json 添加脚本:

  "scripts": {
    "dev": "unbuild --stub",
    "build": "unbuild"
  },

 

功能开发

 

目的是快捷的创建自己的模版库,主要是增删改查,理想情况是有用户系统,远程维护。这么依赖比较麻烦,电脑换的还是比较少的,所以文件存储了。

创建新应用


// 创建命令
program
  .command("create <project-name>")
  .description("创建一个新应用")
  .option("-f,--force", "强制覆盖已有项目")
  .action(async (_projectName, cmd) => {
    // 如果应用名称不规范,则提示用户输入
    if (!isValidPackageName(_projectName)) {
      console.log(chalk.red("应用名称不规范"));
      return;
    }
    appName = _projectName;
    // 如果强制覆盖且已存在相同命名工程,则提示用户输入
    if (!cmd.force && existsSync(resolve(`./${appName}`))) {
      console.log(chalk.red("已存在相同命名工程"));
      return;
    } else if (cmd.force && existsSync(resolve(`./${appName}`))) {
      //删除当前文件夹
      rmdirSync(resolve(`./${appName}`), { recursive: true });
    }
    // 获取默认模板列表
    const templateList = await getTemplateList();
    // 获取项目名称
    const topTemplateList = templateList?.map((item) => item.name);
    // 创建问题
    const question = [
      {
        type: "list",
        message: "请选择开发的应用类型:",
        name: "appType",
        default: "vue",
        choices: topTemplateList,
      },
    ];

    inquirer.prompt(question).then((answer) => {
      // 根据用户选择的模板,获取子模板列表
      const template = templateList?.find(
        (item) => item.name === answer.appType
      );
      if (!template) {
        return;
      }
      const choices = template.children?.map((item) => item.name);
      const question = [
        {
          type: template.type,
          name: "appSubType",
          message: template.message,
          choices,
        },
      ];
      inquirer.prompt(question).then((answer) => {
        // 根据用户选择的子模板,获取客户端列表
        const subTemplate = template.children?.find(
          (item) => item.name === answer.appSubType
        );
        if (!subTemplate || !subTemplate.git || !subTemplate.client) {
          return;
        }
        // 使用子模板的git仓库,创建应用
        cloneProject(subTemplate.git, appName, subTemplate.client);
      });
    });
  });

自定义模版新增

program
  .command("add <name> <gitUrl>")
  .description("新增自定义模版")
  .action(async (_name: string, _gitUrl: string) => {
    // 获取模板列表
    const tempList = await getTemplateList();
    // 获取自定义组
    const customGroup = tempList.pop();
    // 查找自定义组中是否已经存在同名模板
    const customTemplate = customGroup?.children?.find(
      (item) => item.name === _name
    );
    if (customTemplate) {
      // 如果存在,则返回错误信息
      return console.log(chalk.redBright("名称已存在"));
    }
    // 向自定义组中添加模板
    customGroup?.children?.push({
      code: `${(customGroup?.children?.length ?? 0) + 1}`,
      name: _name,
      git: _gitUrl,
      client: "pnpm",
    });

    if (customGroup) {
      tempList.push(customGroup);
    }

    console.dir(tempList, { depth: null });

    writeFile("repoList.txt", JSON.stringify(tempList), (err) => {
      if (err) {
        console.log(chalk.redBright("新增失败"), err);
        return;
      }
      console.log(chalk.greenBright("新增成功"));
    });
  });

自定义模版查询

// 查看所有模版树形列表
program
  .command("ls")
  .description("查看所有模版树形列表")
  .action(async () => {
    console.log(figlet.textSync("PAN CLI"));
    printTree(await getTemplateList());
  });

// 查看所有模版树形列表值和repo 地址
program
  .command("ll")
  .description("查看所有模版对象结构")
  .action(async () => {
    printTree(await getTemplateList(), 0, true);
  });

自定义模版删除

program
  .command("delete <name>")
  .description("删除自定义模版")
  .action(async (_name: string) => {
    // 获取模板列表
    const tempList = await getTemplateList();
    // 删除输入名称的模版
    const afterDelTempList = tempList[tempList.length - 1]?.children?.filter(
      (item) => item.name !== _name
    );
    tempList[tempList.length - 1].children = afterDelTempList;
    writeFile("repoList.txt", JSON.stringify(tempList), (err) => {
      if (err) {
        console.log(chalk.redBright("删除失败"), err);
        return;
      }
      console.log(chalk.greenBright("删除成功"));
    });
  });

自定义模版更新


program
  .command("update <name> <gitUrl>")
  .description("更新自定义模版")
  .action(async (_name: string, _gitUrl: string) => {
    // 获取模板列表
    const tempList = await getTemplateList();
    // 获取自定义组
    const customGroup = tempList.pop();
    // 查找自定义组中是否已经存在同名模板
    const customTemplate = customGroup?.children?.find(
      (item) => item.name === _name
    );
    if (customTemplate) {
      customTemplate.git = _gitUrl;
    }else{
      console.log(chalk.redBright("模板不存在"));
      return
    }

    if (customGroup) {
      tempList.push(customGroup);
    }

    writeFile("repoList.txt", JSON.stringify(tempList), (err) => {
      if (err) {
        console.log(chalk.redBright("更新失败"), err);
        return;
      }
      console.log(chalk.greenBright("更新成功"));
    });
  });

 

发布

使用standard-version 发布版本以及管理CHANGELOG

  • 安装依赖:pnpm add standard-version -D
  • 添加命令
  "scripts": {
    "dev": "unbuild --stub",
    "build": "unbuild",
    "release": "standard-version && npm publish"
  },

 

使用

全局安装使用,npm install -g @x-fe/cli

具体功能查看-h功能即可

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【开源】基于Vue.js的无代码动态表单系统的设计和实现

项目编号&#xff1a; S 026 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S026&#xff0c;文末获取源码。} 项目编号&#xff1a;S026&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 动态类型模块2.2 动态文件模块2.3 动…

2023-11-27操作系统---进程—线程—实验

目录 2023-11-27操作系统_进程—线程_实验 5-10&#xff1a; 代码&#xff1a; 运行结果: 5-11 代码&#xff1a; 运行结果&#xff1a;​编辑 2023-11-27操作系统实验 5-10&#xff1a; 代码&#xff1a; #include<unistd.h> #include<stdio.h> #include…

力扣hot100 滑动窗口最大值 单调队列

&#x1f468;‍&#x1f3eb; 题目地址 &#x1f37b; AC code class Solution {public int[] maxSlidingWindow(int[] nums, int k){int n nums.length;int[] res new int[n - k 1]; // 单调递减队列int[] q new int[n];// q数组维护的是元素在 nums 数组对应的下标int…

FFmepg 核心开发库及重要数据结构与API

文章目录 前言一、FFmpeg 核心开发库二、FFmpeg 重要数据结构与 API1、简介2、FFmpeg 解码流程①、FFmpeg2.x 解码流程②、FFmpeg4.x 解码流程 3、FFMpeg 中比较重要的函数以及数据结构①、数据结构②、初始化函数③、音视频解码函数④、文件操作⑤、其他函数 三、FFmpeg 流程1…

史上最细,2个半月从功能进阶自动化测试,进阶指南...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、学习自动化之前…

算法通关村第一关|链表基础

1. 单链表概念 对于所有的数据结构的基础都是创建增删改查&#xff0c;学习链表重点也是学习链表的五种基本操作。 单向链表就像一个铁链一样&#xff0c;元素之间相互连接&#xff0c;包含多个结点&#xff0c;每个结点有一个指向后继元素的next指针。表中最后一个元素的nex…

【文献阅读笔记】关于GANomaly的异常检测方法

文章目录 1、GANomaly: Semi-Supervised Anomaly Detection via Adversarial Training模型主要创新 2、Skip-GANomaly: Skip Connected and AdversariallyTrained Encoder-Decoder Anomaly Detection模型主要创新点 3、Industrial surface defect detection and localization u…

AMP State Evolution的计算:以伯努利高斯先验为例

AMP State Evolution (SE)的计算 t 1 t1 t1时&#xff0c; E ( t ) E [ X 2 ] \mathcal E^{(t)} \mathbb E [X^2] E(t)E[X2]&#xff0c;SE的迭代式为 τ r ( t ) σ 2 1 δ E ( t ) E ( t 1 ) E ∣ η ( t ) ( X Z ) − X ∣ 2 , Z ∼ N ( 0 , τ r ( t ) ) \begin{a…

京东秒杀之商品展示

1 在gitee上添加.yml文件 1.1 添加good-server.yml文件 server:port: 8084 spring:datasource:url: jdbc:mysql://localhost:3306/shop_goods?serverTimezoneGMT%2B8driverClassName: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourceusername: rootpa…

【JMeter】使用BeanShell写入内容到文件

一、前言 在我们日常工作中&#xff0c;可能会遇到需要将请求返回的数据写入到文件中。在我们使用JMeter进行性能测试时&#xff0c;就经常能够遇到这种情况。要想达到这种目的&#xff0c;我们一般采取BeanShell后置处理器来将内容写入到文件。 二、提取 在目前大多数的性能…

华为云CDN刷新与查询余量的Go实现及在Jenkins中的部署

引言 在华为云上&#xff0c;对CDN缓存内容进行刷新是一个常见的需求&#xff0c;以确保最新的内容能尽快被用户访问到。通过使用Go语言&#xff0c;我们可以开发一个自动化的工具来实现这一需求&#xff0c;并将其集成到Jenkins中以实现持续部署。下面我们将分步骤讲解如何实…

力扣hot100 和为 K 的子数组 前缀和

&#x1f468;‍&#x1f3eb; 题目地址 &#x1f37b; AC code class Solution {public int subarraySum(int[] nums, int k){int ans 0;int n nums.length;int[] s new int[n 1];// 前缀和s[0] 0;s[1] nums[0];for (int i 2; i < n; i)s[i] s[i - 1] nums[i - 1…

Termius 一款优秀的跨平台 SSH 客户端工具

&#x1f525;&#x1f525;&#x1f525; 作为程序员或者运维管理人员&#xff0c;我们经常需要使用终端工具来进行服务器管理及各种操作&#xff0c;比如部署项目、调试代码、查看/优化服务、管理服务器等。 而实现远程服务器连接需要借助 SSH 协议来进行&#xff0c;SSH&am…

Ubuntu知识积累

修改当前Ubuntu环境下时间 sudo date --set "2023-11-21 10:01:00"查看进程 过滤bs开头的进程 ps -aux|grep Bs查看ubuntu系统的磁盘大小 要查看Ubuntu系统的磁盘大小&#xff0c;可以使用df命令。df命令用于报告文件系统的磁盘空间使用情况&#xff0c;包括每个…

ESP32-Web-Server编程- JS 基础 4

ESP32-Web-Server编程- JS 基础 4 概述 HTML 内联事件处理器&#xff0c;你永远不应该使用 HTML 事件处理器属性——因为那些已经过时了&#xff0c;使用它们是不好的做法。 在前端编程中&#xff0c;除了将期望发生的事件写为 JS 文件外&#xff0c;还可以使用一些组件自带…

springboot+vue智能企业设备管理系统05k50

智能设备管理系统主要是为了提高工作人员的工作效率和更方便快捷的满足用户&#xff0c;更好存储所有数据信息及快速方便的检索功能&#xff0c;对系统的各个模块是通过许多今天的发达系统做出合理的分析来确定考虑用户的可操作性&#xff0c;遵循开发的系统优化的原则&#xf…

电子学会C/C++编程等级考试2021年12月(三级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:我家的门牌号 我家住在一条短胡同里,这条胡同的门牌号从1开始顺序编号。 若所有的门牌号之和减去我家门牌号的两倍,恰好等于n,求我家的门牌号及总共有多少家。 数据保证有唯一解。 时间限制:1000 内存限制:65536输入 一个…

艾森股份将上市在即:募资约6亿元,张兵、蔡卡敦夫妇为实控人

11月27日&#xff0c;江苏艾森半导体材料股份有限公司&#xff08;下称“艾森股份”&#xff0c;SH:688720&#xff09;开启申购&#xff0c;将在科创板上市。本次上市&#xff0c;艾森股份的发行价为28.03元/股&#xff0c;发行数量约2203万股&#xff0c;预计募资总额约6.18亿…

C++ :const修饰成员函数

常函数&#xff1a; 常函数&#xff1a; 成员函数后加const后我们称为这个函数为常函数 常函数内不可以修改成员属性 成员属性声明时加关键字mutable后&#xff0c;在常函数中依然可以修改 属性可修改&#xff1a; class Person { public: void showPerson() …

UI自动化测试工具有哪些优势?

UI自动化测试工具通过提高测试效率、覆盖率&#xff0c;减少测试时间和成本&#xff0c;以及支持持续集成等方式&#xff0c;为软件开发团队提供了一系列重要的优势&#xff0c;有助于提升软件质量和开发效率。 自动化执行&#xff1a;UI自动化测试工具可以模拟用户与应用程序的…