10分钟快速搭建出提高生产效率的 CLI 工具

660 阅读4分钟

背景

先来说说为什么我要开发一个 CLI 工具

之前因为业务需求,开发过一段时间的 Koa 项目,由于需要不停的在新老项目之间进行切换,也就经常不避免的会产生了一些重复性的工作,比如

  • 创建新项目
  • 集成项目所需依赖
  • 添加各种项目规范
  • 添加各种配置文件
  • 编写大量重复代码
  • 配置项目打包发布
  • ...

结合以上 10086+ 个痛点,经过简单的构思和研究,我开发出了属于自己的 CLI

  • nost-cli:基于 Koa 的 nodeJs 框架

目的很简单,就是为了简化开发流程,提高生产效率。

虽然现成的 node 框架也很香,但是我不管,我就要卷!!!

预览

nost-2222.gif

现已发布至 npm,可通过 npm install -g nost-cli 安装体验!!!

思路

通过 npm package 的形式进行安装使用

  • 创建项目:可以快速帮我创建一个规范的 koa 项目,开箱即用

  • 运行项目:可以帮我在开发环境下运行该项目并进行实时的热更新

  • 打包项目:可以帮我在即将发布生产环境时进行代码生成和压缩

  • 文件创建:

    • 快速创建 router 模版进行请求和参数处理
    • 快速创建 controller 模版进行内部逻辑处理
    • 快速创建 service 模版进行数据库相关操作
    • 快速创建 middleware 模版进行自定义中间件的编写
    • 快速创建 error 模板进行自定义错误处理
    • 快速创建 config 文件用于新增项目配置
  • 信息查看:

    • 版本信息
    • 更多帮助

nost-cli

nost-cli核心步骤实现

第一步:初始化项目并实现通过命令运行该项目

  • 使用 yarn init -y 初始化项目
  • 配置 package.jsonbin 字段

image.png

  • 新增 index.js 文件并在顶部写入 #!/usr/bin/env node

image.png

  • 通过 npm link 实现软链
  • 命令行中输入 nost 敲回车即可查看相关打印

第二步:添加工具的帮助信息和使用说明

  • 安装 commander 工具库
  • 完善 index.js
#!/usr/bin/env node

const { program } = require('commander')
const { version } = require('./package.json')

program.version(version, '-v, --version', 'Output the current version.')
program.option('-h, --help', 'Output usage information.')

program
  .command('init <project-name>')
  .alias('i')
  .description('Initialization Nost application.')
  .action(async (source, destination) => {})

program
  .command('start')
  .description('Run Nost application.')
  .action(async (source, destination) => {})

program
  .command('build')
  .alias('b')
  .description('Build Nost application.')
  .action(async (source, destination) => {})

program
  .command('info')
  .description('Display Nost project details.')
  .action(async (source, destination) => {})

program
  .command('config <config-filename>')
  .alias('cfg')
  .description('Generate a configuration file.')
  .action(async (source, destination) => {})

program
  .command('controller <controller-filename>')
  .alias('c')
  .description('Generate a controller declaration.')
  .action(async (source, destination) => {})

program
  .command('error <error-types-filename>')
  .alias('e')
  .description('Generate a error declaration.')
  .action(async (source, destination) => {})

program
  .command('middleware <middleware-filename>')
  .alias('m')
  .description('Generate a middleware declaration.')
  .action(async (source, destination) => {})

program
  .command('router <router-filename>')
  .alias('r')
  .description('Generate a router declaration.')
  .action(async (source, destination) => {})

program
  .command('service <service-filename>')
  .alias('s')
  .description('Generate a service declaration.')
  .action(async (source, destination) => {})

program.parseAsync(process.argv)
  • 执行 nost [options] [command] 就可以执行不同的命令

image.png

第三步:完善每一个命令对应的操作

1. init | i <project-name>

  • action callback 中的 source 获取输入,通过 fs 模块对进行边界判断
  • 安装 chalk 工具库用来美化命令行的日志打印
  • 安装 ora 工具库用来在命令行与用户进行交互
  • 安装 download-git-repo 工具库用来拉取 Koa 项目模板
  • <project-name> 更新到 package.json

image.png

2. start

  • 通过 child_process 模块执行项目中的脚本用于在开发环境运行项目

image.png

3. build | b

  • 通过 child_process模块执行项目中的脚本通过打包工具生成生产代码 image.png

4. info

  • 通过 figlet 在线生成“高大上”的命令行 Banner

image.png

5. config | cfg <config-filename>

  • 通过 fs 模块在项目的 config 目录中创建配置文件

image.png

6. controller <controller-filename>

  • 通过 fs 模块在项目的 controller 目录中根据控制器模板创建指定的控制器文件
const controllerName = source[0].toUpperCase()}${source.substring(1)
const controllerTemplate = `
import { Context, Next } from 'koa'
class ${controllerName}Controller {
  async list(ctx: Context, next: Next) {
    ctx.body = 'list'
    await next()
  }
    
  async info(ctx: Context, next: Next) {
    ctx.body = 'info'
    await next()
  }
}

const ${source}Controller = new ${controllerName}Controller()
export default ${source}Controller
`  

image.png

7. error | e <error-types-filename>

  • 通过 fs 模块在项目的 error 目录中创建 errorTypes 文件
const errorTypesTempalte = `
export const ERRORTYPE = 'error message'
`

image.png

8. middleware | m <middleware-filename>

  • 通过 fs 模块在项目的 middleware 目录中根据中间件模板创建指定的中间件文件
const middlewareTemplate = `
import { Context, Next } from 'koa'

export const customMiddleware = async (ctx: Context, next: Next) => {
  // do something...
  console.log(ctx.request.params, ctx.request.body)
  await next()
}
`

image.png

9. router | r <router-filename>

  • 通过 fs 模块在项目的 router 目录中根据路由模板创建指定的路由文件
const routerTemplate = `
import Router from '@koa/router'

const router = new Router({ prefix: '/${source}' })

router.get('/list')
router.post('/list/:id')

export default router
`    

image.png

10. service | s <service-filename>

  • 通过 fs 模块在项目的 service 目录中根据模型模板创建指定的路由文件
const serviceName = source[0].toUpperCase()}${source.substring(1)
const serviceTemplate = `
// import connection from "../mysql"
class ${serviceName}Service {
  // 操作 mysql 获取数据
  // async list() {
  //     const statement = 'SELECT * FROM ${source};'
  //     const [result] = await connection.execute(statement)
  //     return result
  // }
  async list() {
    return []
  }
}
const ${source}Service = new ${serviceName}Service()
export default ${source}Service
`

image.png

第四步:编写一个简单的 markdown 并执行 npm publish 进行发布

image.png

Koa 项目模板

  • 项目概述
    • 包管理工具使用 yarn
    • 默认使用 ts 进行编写
    • 规范使用 eslint
    • 格式化使用 prettier
    • 打包使用 rollup
  • 源码(src)目录结构
    • main.ts - 入口文件
    • app.ts - 创建 app 并导出
    • router - 路由目录
    • controller - 控制器目录
    • service - sql 目录
    • config - 配置目录
    • middleware - 中间件目录
    • mysql - sql-connection 目录
    • error - 错误处理目录
  • 完整目录结构
.
├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── env.d.ts
├── package-lock.json
├── package.json
├── rollup.config.mjs
├── src
│   ├── app.ts
│   ├── config
│   │   ├── db.config.ts
│   │   └── server.config.ts
│   ├── controller
│   │   └── user.controller.ts
│   ├── error
│   │   ├── index.ts
│   │   └── user.errorTypes.ts
│   ├── main.ts
│   ├── middleware
│   │   ├── user.middleware.ts
│   │   └── wrapper.middle.ts
│   ├── mysql
│   │   └── index.ts
│   ├── router
│   │   ├── index.ts
│   │   └── user.router.ts
│   └── service
│       └── user.service.ts
├── tsconfig.json
└── yarn.lock

总结

有实践! 有思考! 有收获! 有提高!!