• 使用 pnpm monorepo + ts 制作个功能完善的 CLI 命令行工具



    前言

    在卷卷的今天,我们可以看到前端框架和技术层出不穷,各种技术都伴有着一系列的生态诞生,说白了就是都有后台,有静态文档技术,有运行时依赖,有各种各样的轮子,当然也少不了 cli 这一套

    对于大部分人来说最开始接触这个东西的应该是 vue-cli ,当时确实感觉很不错,如今发现很多脚手架和命令行工具完全可以使用 js / ts 来开发,都依赖着 nodejs npm 来运行


    来吧:接下来我们也实践一个功能简单且常用的工具

    先把仓库地址放出来,因为之前很多博客很多人都希望放代码的

    github仓库

    gitee仓库(同步github)

    一、仓库搭建

    1. 介绍功能

    简单介绍下上面仓库这个 cli 工具的功能吧

    参数 Options

    -v, --vers: 查看版本号

    -h, --help: 查看自定义帮助信息

    命令 Commands

    init: 初始化一个模版(包含文档和博客风格的 vitepress 主题模版),也就是远程仓库模版下载和本地文件修改

    list: 展示模版仓库各个模版的最新提交信息,请求了 GitHub API

    git: 展示某人远程仓库的列表,请求了 Github API

    ver: 展示模版仓库分支的版本号,请求了 Github API

    -l: 多国语言切换

    2.开始搭建

    我们使用比较新的 pnpm 搭建 monorepo 工程

    先来看下项目目录

    在这里插入图片描述
    红框是我们必须要有的

    1. package.json

    这里就正常写就可以,不过 scripts 脚本可以变一下,使用 pnpm 支持的更多的脚本

    pnpm run -C packages/cli xxx

    上述命令表示执行子模块中的 xxx 命令,这样就可以再根目录操作子模块脚本了

    依赖说明:

    fs-extra: fs 模块升级版

    rimraf: 用来删除目录,忽略一些系统差异

    ts-node: 便于我们使用 node 直接执行 ts 文件

    {
      "name": "@vuetom-cli/root",
      "private": true,
      "workspaces": [
        "packages/*"
      ],
      "scripts": {
        "build": "pnpm run -C packages/cli tsc",
        "cli": "pnpm run -C packages/cli cli",
        "cli:init": "pnpm run -C packages/cli cli:init",
        "cli:list": "pnpm run -C packages/cli cli:list",
        "ts": "pnpm run -C packages/cli ts",
        "ts:init": "pnpm run -C packages/cli ts:init",
        "ts:list": "pnpm run -C packages/cli ts:list" 
      },
      "dependencies": {
      },
      "devDependencies": {
        "fs-extra": "^10.1.0",
        "rimraf": "^3.0.2",
        "ts-node": "^10.9.1",
        "typescript": "^4.8.2"
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    1. pnpm-workspace.yaml

    这里声明包含的模块

    packages:
      - 'packages/*'
    
    • 1
    • 2
    1. packages 包

    这里面我们再分 3 个目录,一个是 cli 主项目目录

    再搞两个模版(temp-docs / temp-blog)目录,用来模拟项目模版生成的功能

    1. 子模块结构

    在这里插入图片描述
    5. cli/package.json

    该文件下包含了cli自己模块本身要用到了依赖包以及命令,最主要的是要看编译命令:

    假设你以后的命令是 vuetom-cli

    cli: 主命令,执行 pnpm cli 就相当于执行了 vuetom-cli

    cli:init: command,相当于执行了 vuetom-cli init

    cli:list: command,相当于执行了 vuetom-cli list

    ts: 开发模式下执行的,方便变开发变测试

    copy: 移动模版文件或者其他一些不需要经过编译的文件

    tsc: 编译命令,将 ts 文件编译为 js

    {
      "name": "vuetom-cli",
      "version": "0.2.5",
      "description": "A simple CLI for vitepress-theme-vuetom",
      "scripts": {
        "cli": "node ./bin/vuetom-cli.js",
        "cli:init": "node ./bin/vuetom-cli.js init",
        "cli:list": "node ./bin/vuetom-cli.js list",
        "ts": "ts-node ./src/vuetom-cli.ts",
        "ts:init": "ts-node ./src/vuetom-cli-init.ts",
        "ts:list": "ts-node ./src/vuetom-cli-list.ts",
        "copy": "node ../../scripts/index",
        "tsc": "pnpm copy && pnpm clean && tsc",
        "clean": "rimraf bin"
      },
      "bin": {
        "vuetom-cli": "./bin/vuetom-cli.js"
      },
      "files": [
        "bin/",
        "media",
        "temp-blog",
        "temp-docs",
        ".env",
        "CHANGELOG.md",
        "README.md"
      ],
      "dependencies": {
        "chalk": "^4.1.2",
        "commander": "^9.4.0",
        "dotenv": "^16.0.1",
        "dotenv-expand": "^9.0.0",
        "download-git-repo": "^3.0.2",
        "envfile": "^6.17.0",
        "inquirer": "^8.0.0",
        "ora": "^5.4.1",
        "request": "^2.88.2"
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    1. temp-docs 和 temp-blog

    这两个目录下都各有一个 package.json 和 README.md ,就假装是两个不同的项目吧

    两个 package.json 简单书写一下就行

    {
      "name": "temp-blog",
      "version": "0.0.1",
      "description": "blog templates",
      "scripts": {
      },
      "author": "lauset",
      "license": "MIT"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    项目简单的搭建完成了,详细代码在 github


    二、开始编码

    1. 简单命令

    先列举一些简单的命令,比如版本、帮助、命令、参数判断等

    cli/src/vuetom-cli.ts 文件名可以随意起,跟自己的主命令一样也可以,ts 文件内可以使用 es import 语法,也可以使用 node require 语法,也可以直接获取到 json,可混搭

    #!/usr/bin/env node
    
    const chalk = require('chalk')
    const pkg = require('../package')
    
    // const program = require('commander')
    import { Command } from 'commander'
    const program = new Command()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    然后我们就获得了 program,这个就很无敌了已经,chalk 是控制台彩色输出,pkg 是你的 cli/package.json 文件内容

    展示主要信息、帮助信息

    先看效果

    在这里插入图片描述
    在 vuetom-cli.ts 内加入代码

    program
      .name(pkg.name)
      .description(chalk.hex('#FA8072')(pkg.description))
      .version(pkg.version, '-v, --vers', chalk.gray('output the current version'))
    
    program
      .helpOption('-h, --help', chalk.gray('display help for you'))
      .addHelpCommand(true, chalk.gray('display help for command'))
      .on('--help', () => require('./cmd/help').default())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这样 -v-h 就可以使用了

    require('./cmd/help').default() 是调用了一个文件内默认导出的方法,我们把命令都拆分到 cli/cmd 目录下,或者你替换成 console.log('help info') 也是可以的,只是展示的帮助信息多少的问题

    cmd/help.ts

    const chalk = require('chalk')
    
    function printHelp () {
      console.log()
      console.log(chalk.hex('#66CDAA')('  Examples:'))
      console.log()
      console.log(
        chalk.gray('    # create a new project with an official template')
      )
      console.log('    $ vuetom-cli init project-name')
      console.log()
      console.log(chalk.gray('    # show github branch information'))
      console.log('    $ vuetom-cli list temp-docs')
      console.log()
      console.log(chalk.gray('    # show github repository'))
      console.log('    $ vuetom-cli git vuejs')
      console.log()
    }
    
    export default printHelp
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.多国语言切换

    首先这里我们使用一个 -l (--lang) 参数来切换语言,大概就是 vuetom-cli -l zh 就可与切换成中文

    在 vuetom-cli.ts 里加入

    program
      .option('-l, --lang [language]', chalk.gray('change language'))
      .action((options) => {
        const { lang } = options
        require('./cmd/lang').default(lang)
      })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    cmd/lang.ts

    const logger = require('../logger')
    import { t, useLang } from '../lang'
    
    const { langs, showLang, changeLang } = useLang()
    
    function handleLang (lang: string) {
      if (lang) {
        if (typeof lang === 'boolean') {
          showLang()
          return
        }
        if (langs.includes(lang)) {
          changeLang(lang)
        } else {
          logger.error(
            `${t('error.lang')} ${langs} \n`
          )
        }
      }
    }
    
    export default handleLang
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    这里使用到了我们自己写的 useLang,lang 目录下代码较多,请跳转 代码

    我们单输入 -l 那么就会调用 showLang 展示当前语言的方法

    代码中所有的 t 函数效果就和 vue-i18n 中的 t 差不多一样

    上面还使用到了 .env 文件,这个文件里存入变量,我们改语言后可以直接修改文本内容以达到永久修改的目的

    cli/.env

    THEME_VERSION=2.2.0
    VUETOM_CLI_LANG=zh
    
    • 1
    • 2

    看下切换效果

    在这里插入图片描述

    3. 请求api数据

    这里我们做个展示某人远程仓库项目的功能,这样就用到了 api 的请求和响应数据的展示

    也就是自定义 git 命令,后加用户名或者组织名,然后看你 github/gitee 仓库,下面展示一下 vuetom 这个组织内的仓库列表

    看下效果

    在这里插入图片描述
    首先改 cli/vuetom-cli.ts 吧

    program
      .command('git')
      .description(chalk.gray('show the list of github/gitee repository of a user'))
      .argument('', "your github/gitee 's username")
      .argument('[type]', 'repo type: "github" or "gitee"', undefined)
      .action((user, type) => {
        require('./cmd/git').default(user, type)
      })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这样你 help git 命令就自动会生成一些帮助信息了,argument 里 表示必填,[type] 表示可选,默认的话 undefinedaction 里就是要调用的方法

    首先我们可以自定义参数校验

    cmd/git.ts

    function handleGit (user: string, type: string) {
      if (type === undefined || type === 'github') {
        type = `${user}'s github`
        githubList(user ?? 'lauset', type)
      } else if (type === 'gitee') {
        type = `${user}'s gitee `
        giteeList(user ?? 'lauset', type)
      } else {
        logger.error(
          `"${type}" ${t('error.gitType')} \n`
        )
      }
    }
    
    export default handleGit
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这样默认第二个参数就是 github 了,也就是说要展示 github 的仓库列表,完整代码

    4. 生成模版操作

    这个功能比较常见吧,就是很多工具里都有的 init 或 create 模版,其实模版可以从远程仓库拉取也可以内置本地工具里,这样速度就很快

    当然我们也会以提问的方式友好地去让用户输入或者选择来完成个性化,包括输入内容后我们可以将用户输入的内容替换掉拉取的项目下 package.json 里的内容

    看下效果

    在这里插入图片描述
    由于这个不是一个简单的命令,所以我们采用其他方式调用,单独写一个 vuetom-cli-init.ts 文件

    pnpm ts:init 就可以直接来调用执行

    在 vuetom-cli.ts 里加入以下内容

    program
      .usage(' [options]')
      // .option('-i, init [name]', 'init vitepress-theme-vuetom theme')
      .command(
        'init ',
        chalk.gray('generate a new project from a template')
      )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    代码实在是太多了,直接看 vuetom-cli-init 这个文件吧,大家有不懂的地方直接评论或者 issues 里发起提问就行

    三、打包发布

    首先打包来说,我们使用 tsc 将其输出到 cli/bin 目录下,之后发布就直接上传 bin 目录了,你也可以加 README.md 文件等等

    当你自己的 cli 做好以后,就可以发布至 npmjs 远程仓库,在你注册账号后,可以在个人电脑终端使用 npm login 登录,然后对包进行 npm publish 即可

    需要注意上传的文件,在 package.json 里需要注意 files 属性

     "files": [
        "bin/",
        "media",
        "temp-blog",
        "temp-docs",
        ".env",
        "CHANGELOG.md",
        "README.md"
      ],
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    总结

    由于代码过多,所以这里就只能让大家多去远程仓库看看代码,有什么问题大家可以随时留言提问的,希望可以帮到大家

  • 相关阅读:
    docker更换国内源
    git 提交时屏蔽本地无需上传的文件
    C++ 基础入门
    vite(setup语法糖)+ts+vant+axios入门教程
    10【DCL数据库控制语言】
    LeetCode算法题解(回溯)|LeetCode93. 复原 IP 地址、LeetCode78. 子集、LeetCode90. 子集 II
    我问老大:TCP 四次挥手,可以变成三次吗?
    Python安装与环境变量配置傻瓜式教程(2023年9月)
    vue+java实现简易AI问答组件(基于百度文心大模型)
    【升职加薪秘籍】我在服务监控方面的实践(3)-机器监控
  • 原文地址:https://blog.csdn.net/qq_44209274/article/details/126683921