背景
先来说说为什么我要开发一个 CLI 工具
之前因为业务需求,开发过一段时间的 Koa 项目,由于需要不停的在新老项目之间进行切换,也就经常不避免的会产生了一些重复性的工作,比如
- 创建新项目
- 集成项目所需依赖
- 添加各种项目规范
- 添加各种配置文件
- 编写大量重复代码
- 配置项目打包发布
- ...
结合以上 10086+ 个痛点,经过简单的构思和研究,我开发出了属于自己的 CLI
- nost-cli:基于 Koa 的 nodeJs 框架
目的很简单,就是为了简化开发流程,提高生产效率。
虽然现成的 node 框架也很香,但是我不管,我就要卷!!!
预览
现已发布至 npm
,可通过 npm install -g nost-cli
安装体验!!!
思路
通过 npm package 的形式进行安装使用
-
创建项目:可以快速帮我创建一个规范的 koa 项目,开箱即用
-
运行项目:可以帮我在开发环境下运行该项目并进行实时的热更新
-
打包项目:可以帮我在即将发布生产环境时进行代码生成和压缩
-
文件创建:
- 快速创建 router 模版进行请求和参数处理
- 快速创建 controller 模版进行内部逻辑处理
- 快速创建 service 模版进行数据库相关操作
- 快速创建 middleware 模版进行自定义中间件的编写
- 快速创建 error 模板进行自定义错误处理
- 快速创建 config 文件用于新增项目配置
-
信息查看:
- 版本信息
- 更多帮助
nost-cli
nost-cli核心步骤实现
第一步:初始化项目并实现通过命令运行该项目
- 使用
yarn init -y
初始化项目 - 配置
package.json
的bin
字段
- 新增 index.js 文件并在顶部写入
#!/usr/bin/env node
- 通过
npm link
实现软链 - 命令行中输入
nost
敲回车即可查看相关打印
第二步:添加工具的帮助信息和使用说明
- 安装
commander
工具库 - 完善
index.js
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]
就可以执行不同的命令
第三步:完善每一个命令对应的操作
1. init | i <project-name>
- 从
action callback
中的source
获取输入,通过fs
模块对进行边界判断 - 安装
chalk
工具库用来美化命令行的日志打印 - 安装
ora
工具库用来在命令行与用户进行交互 - 安装
download-git-repo
工具库用来拉取 Koa 项目模板 - 将
<project-name>
更新到package.json
中
2. start
- 通过
child_process
模块执行项目中的脚本用于在开发环境运行项目
3. build | b
- 通过
child_process
模块执行项目中的脚本通过打包工具生成生产代码
4. info
- 通过
figlet
在线生成"高大上"的命令行 Banner
5. config | cfg <config-filename>
- 通过
fs
模块在项目的config
目录中创建配置文件
6. controller <controller-filename>
- 通过
fs
模块在项目的controller
目录中根据控制器模板创建指定的控制器文件
js
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
`
7. error | e <error-types-filename>
- 通过
fs
模块在项目的error
目录中创建errorTypes
文件
js
const errorTypesTempalte = `
export const ERRORTYPE = 'error message'
`
8. middleware | m <middleware-filename>
- 通过
fs
模块在项目的middleware
目录中根据中间件模板创建指定的中间件文件
js
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()
}
`
9. router | r <router-filename>
- 通过 fs 模块在项目的 router 目录中根据路由模板创建指定的路由文件
js
const routerTemplate = `
import Router from '@koa/router'
const router = new Router({ prefix: '/${source}' })
router.get('/list')
router.post('/list/:id')
export default router
`
10. service | s <service-filename>
- 通过
fs
模块在项目的service
目录中根据模型模板创建指定的路由文件
js
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
`
第四步:编写一个简单的 markdown
并执行 npm publish
进行发布
Koa 项目模板
- 项目概述
- 包管理工具使用
yarn
- 默认使用
ts
进行编写 - 规范使用
eslint
- 格式化使用
prettier
- 打包使用
rollup
- 包管理工具使用
- 源码(
src
)目录结构main.ts
- 入口文件app.ts
- 创建 app 并导出router
- 路由目录controller
- 控制器目录service
- sql 目录config
- 配置目录middleware
- 中间件目录mysql
- sql-connection 目录error
- 错误处理目录
- 完整目录结构
md
.
├── .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
总结
有实践! 有思考! 有收获! 有提高!!