【懒人系列】从零折腾一个自己都不想用的Cli工具:组件自动生成

221 阅读3分钟

一、需求

开发新需求,一般都要创建好多新文件

唔想鼠标点点点而系通过命令生成新文件

效果预览 效果预览.png

二、用户画像、文档、原型

三、设计

1. 输入命令

如输入 mycli c src/app然后开始

2. 读取命令

逻辑有诸如以下情况按需处理:

  1. 名称喺未合法
  2. 文件喺未已经存在
  3. 如果喺多层路由点处理;
  4. 如果喺组件类喺未要大/小驼峰命名
  5. 喺未有文件后缀
  6. 喺未需要插入模板
  7. 如果只喺一个简单咖字符串,喺未想喺某个路径下面直接创建--然后重复1-6咖问题;
  8. ...

3. 生成文件

如生成src/app.vue文件

四、准备

交互处理

  • commander:命令行交互
  • inquirer:选择式指令
  • path:路径处理
  • url:路径处理
  • fs-extra:文件处理
  • ...

优化体验

  • ora:loading
  • chalk:输入文字美化
  • ...

五、开发

1. mycli

首先喺实现命令mycli咖有效触发。

  1. 打开cmd敲命令
mkdir mycli && cd mycli && npm init -y && code .
  1. 修改配置,用import语法
  "name": "mycli",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
  1. 创建主要文件
mkdir bin && touch bin/index.js
  1. bin/index.js
#! /usr/bin/env node

console.log("Hello world !");

然后执行node bin/index.js,就可以睇到控制台输出Hello world !

  1. package.json
  "bin": {
    "mycli": "./bin/index.js"
  },
  1. 生成命令
npm link
  1. 测试命令
mycli

如果提示命令唔存在,重启cmd再次执行mcli,可以睇到控制台成功咁输出Hello world !

2. mycli c

然后喺识别命令参数mycli c,需要

  1. 安装依赖
yarn add commander fs-extra inquirer path url
  1. --help生效
#! /usr/bin/env node

import { program } from "commander";

program.option("-d,--debug", "whether to enable debug mode?", false);

program.on("option:debug", () => {
  console.log("开启了debug模式");
});
program.on("command:*", (obj) => {
  console.error(`未知命令:${obj[0]}`);
});

// 监听 --help 指令
program.on("--help", function () {
  // 前后两个空行调整格式,更舒适
  console.log("\n", `输入 "cli <command> --help" 查看命令详情.`, "\n");
});

// 必须,让命令生效
program.parse(process.argv);

输入mycli -h就可以睇到提示内容

  1. c咖创建

a. 首先喺新增内容

program
  // 命令: mycli create
  .command("create ")
  // 别名 mycli c 相当于 mycli create
  .alias("c")
  // 描述信息
  .description("创建文件")
  .action((createName) => {
    console.log("创建命令触发:", createName);
  });

b. 然后敲mycli -h可以睇到多咗提示信息

c. mycli c可以见到创建命令触发:{}噶输出

3. mycli c src/app

而家输入mycli c src/app仲唔可以识别到后面咖字符串,继续走

1. argument识别

  1. 先更新program代码
program
  // 命令: mycli create
  .command("create ")
  // 别名 mycli c 相当于 mycli create
  .alias("c")
  // 描述信息
  .description("创建文件")
  //   .usage("[name]")
  // <>表示是必输字符,[]表示可选输入字符
  .argument("[name]", "文件名称")
  .action((createName) => {
    console.log("字符串识别:", createName);
  });
  1. 然后敲mycli c src/app就可以睇到字符串识别: src/app咖输出

2. 直接创建文件

  1. 需要引入fs-extra文件模块处理器编写创建文件咖代码,例如:
import fs from "fs-extra";

// ...代码省略
console.log("字符串识别:", createName);
fs.writeFileSync(createName, "哈哈哈");
  1. 然后执行mycli c src/app

/ 符号喺会自动识别为文件夹路径,所以毫无疑问会报错

3. 问题或者喺流程处理

行到呢度已经完成咗一个cli工具最基本亦都喺最主要咖步骤。 下面进入上面1~7个问题噶处理,然后重点喺:

  1. 路径名称喺未合法
  2. /识别问题
  3. 路径重复问题
  4. 询问式交互都喺进阶操作,情况比较多,谂太多最后就喺写出来自己都唔想用

下面喺关键节点:

1. 路径名称喺未已经存在

呢个比较好处理,都喺用得最多咖地方,大部分节点都要呢个判断

// root是否已存在文件/夹
export const existsPathSync = (path = "") => {
  const root = process.cwd();
  let src = path.startsWith("/") ? path : `/${path}`;
  if (src.indexOf(root) === -1) {
    src += root;
  }
  const exist = fs.existsSync(src);
  if (exist) {
    chalk.error(`${src} 已存在`);
  }
  return exist;
};
2. 路径名称喺未合法

情况诸如喺未中文、首字母喺未英文等等

// name校验
export function patternName(str = "") {
  if (!str || !str.trim()) {
    chalk.error("不能为空");
    return false;
  }
  // 中文检测
  const isChinese =
    /[\u4e00-\u9fa5|\u3002|\uff1f|\uff01|\uff0c|\u3001|\uff1b|\uff1a|\u201c|\u201d|\u2018|\u2019|\uff08|\uff09|\u300a|\u300b|\u3008|\u3009|\u3010|\u3011|\u300e|\u300f|\u300c|\u300d|\ufe43|\ufe44|\u3014|\u3015|\u2026|\u2014|\uff5e|\ufe4f|\uffe5]/.test(
      str
    );
  // console.log('是否包含中文:' + isChinese);
  if (isChinese) {
    chalk.error("名称不应该包含中文");
    return false;
  }

  // 首字母 英文开头
  const firstWord = str.substring(0, 1);
  const isEn = /^[a-zA-Z]+$/.test(firstWord);
  // console.log('是否英文开头:' + isEn);
  if (!isEn) {
    chalk.error("首写字母必须是英文");
    return false;
  }

  return true;
}
3. 文件夹符 / 识别

主要就喺识别字符串中咖/然后处理

// 创建对应文件夹
export function createDirSync(prevDir = "", fileName = "") {
  // 如果有文件夹路径,遍历生成文件夹
  if (fileName.split("/").length > 1) {
    const names = fileName.split("/").filter((src) => src && src !== "/");
    // console.log(names);
    // 生成的文件名
    const __filename = names[names.length - 1];
    let str = prevDir;
    for (const src of names) {
      if (src === __filename) {
        break;
      }
      str = str + "/" + src;
      // 不存在,创建文件夹
      if (!fs.existsSync(str) && src !== __filename) {
        // 创建文件夹
        fs.mkdirSync(str);
      }
    }
  }
}

4. app.vue

如果上面咖问题你都已经处理好

输入mycli c src/app喺没有文件后缀咖,想要生成对应类型噶文件,系创建文件咖步骤直接拼接.vue就可以了。

但喺呢种限制比较大,万一我想创建一个js文件,或者我想直接生成模板代码呢,又或者...我想自动识别手机壳颜色变换主题上天呢?所以接下来进入自由发挥模式

1. inquirer

  1. 询问式交互,引入inquirer然后新增代码
.action(async (createName) => {
    console.log("字符串识别:", createName);

    const input = await inquirer.prompt([
      {
        type: "list",
        name: "value",
        message: "选择创建的文件后缀",
        choices: [".vue", ".js"],
      },
    ]);
    console.log(input);
});
  1. 再次执行命令mycli c src/app会需要你选择文件后缀,按回车键确认选择

文件后缀.png

2. 模板

既然喺生成组件,系唔系应该有模板一起生成会比较好,所以要准备模板

首先创建templates目录,然后新增模板文件

// templates/vue.js
const template_vue2 = `
<template>
  <div class="">
    
  </div>
</template>

<script>

export default {
  name: '',
  components: {
  },
  props: {
  },
  data() {
    return {};
  }
};
</script>

<style lang='less' scoped>
</style>
`;

export default template_vue2

3. 生成组件

得到文件后缀,亦都摞到模板代码,接落黎可以生成带模板代码噶vue文件

  1. 先更新代码
import template from "../templates/vue.js";


.action(async (createName) => {
    console.log("字符串识别:", createName);

    const input = await inquirer.prompt([
      {
        type: "list",
        name: "value",
        message: "选择创建的文件后缀",
        choices: [".vue", ".js"],
      },
    ]);
    console.log(input.value);

    // 方便演示把src/去掉
    fs.writeFileSync(`app${input.value}`, template);

    console.log("app.vue 组件生成成功!");
  });
  1. 继续执行命令mycli c src/app
  2. 生成组件,Bingo~! 生成组件.png 至此一个通过命令生成包含模板代码的组件文件咖脚本就基本完成。

六、发布、提测、迭代

最后系项目研发咖常规流程,项目周期取决于你想呢个工具为你做D咩

搞掂,收工~