前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >手把手教学拥有自己的CLI

手把手教学拥有自己的CLI

作者头像
星宇大前端
发布2023-11-28 11:01:47
发布2023-11-28 11:01:47
53900
代码可运行
举报
文章被收录于专栏:大宇笔记大宇笔记
运行总次数:0
代码可运行

随着开发时间的增长,你积累的模版需要管理,不能老是复制粘贴。那么一个小小的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 和 类型等开发依赖:

代码语言:javascript
代码运行次数:0
复制
  "devDependencies": {
    "@types/figlet": "^1.5.8",
    "@types/inquirer": "^9.0.7",
    "@types/node": "^20.9.5",
    "typescript": "^5.3.2"
  },

tsconfig 配置:

代码语言:javascript
代码运行次数:0
复制
{
  "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 简单高效。相信尤大大没错。

安装依赖:

代码语言:javascript
代码运行次数:0
复制
pnpm add unbuild -D

添加配置:

代码语言:javascript
代码运行次数:0
复制
import { defineBuildConfig } from "unbuild";

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

package.json 添加脚本:

代码语言:javascript
代码运行次数:0
复制
  "scripts": {
    "dev": "unbuild --stub",
    "build": "unbuild"
  },

功能开发

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

创建新应用
代码语言:javascript
代码运行次数:0
复制
// 创建命令
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);
      });
    });
  });
自定义模版新增
代码语言:javascript
代码运行次数:0
复制
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("新增成功"));
    });
  });
自定义模版查询
代码语言:javascript
代码运行次数:0
复制
// 查看所有模版树形列表
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);
  });
自定义模版删除
代码语言:javascript
代码运行次数:0
复制
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("删除成功"));
    });
  });
自定义模版更新
代码语言:javascript
代码运行次数:0
复制
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
  • 添加命令
代码语言:javascript
代码运行次数:0
复制
  "scripts": {
    "dev": "unbuild --stub",
    "build": "unbuild",
    "release": "standard-version && npm publish"
  },

使用

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

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-11-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 技术选型
  • 思路
  • 开发前准备工作
    • 创建项目
    • 初始化项目package.json
      • 设置入口
      • link 到全局验证测试
    • 添加依赖
    • TS 配置
    • 打包工具
  • 功能开发
    • 创建新应用
    • 自定义模版新增
    • 自定义模版查询
    • 自定义模版删除
    • 自定义模版更新
  • 发布
  • 使用
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档