「这是我参与2022首次更文挑战的第32天,活动详情查看:2022首次更文挑战」。
一、背景
预祝点赞的看官老爷成为尊贵的奥迪车主!
年初刚开工,由于年前啥都不发,年后连个开工红包也没有,我表示摸鱼愿望强烈,要不怎么对得起摸鱼专家的 title 呢?所以需求一个都不写,就是摸鱼,什么?上线怎么办?我也不知道怎么办,在线等,挺急的。。。
需求不写是可以的,但是代码不能停啊。
事情是这样的,我司有个用于开发环境查询短信验证码的平台,每天查询验证码需要一下几步:
- 打开平台网址;
- 登录你的账号;
- 接着进入到短信验证码查询;
- 接着输入测试号,当然可以从
input的输入历史中选择一下或者贴进去; - 再按下回车,然后等
loading结束; - 最后从页面上文字用鼠标选中、复制到剪贴板;
- 最后去
CV;
这是以前的操作路数,后来不知道这个脑血栓晚期的平台发了什么并发症,他居然又在这之上加了一个选择业务的过程,而且没办法定制默认业务,每次打开都是人家的业务,更坑爹的是,选完业务它又要全局刷新页面,内部工具速度懂的都懂,慢的不要不要的。。。。
对于我这种急性子(摸鱼时从来都不),咋办?赞扬这平台的产品和开发都大聪明(MDZZ)?
这我已经做了好多次,但是并不会加速我验证码的获取速速。所以解决问题还得靠摸鱼!
恰逢我们做 E2E 测试时接入了这个平台的 open-api,当时就闪现了一个想法,何不写个命令行工具通过 terminal 去获取验证码,这不就可以绕开这些 GUI 界面,省下的时间不就可以用来摸鱼了吗。
二、需求
有了上面的背景,大家对需求也已经很清楚了,但是作为一个有追求的摸鱼专家,怎么能用 axios 发个请求行了呢?
- 写个全局的
CLI命令,我们叫他joymax(为啥joymax,超喜欢三阳joymaxZ300,上次写的一个工具名 rs7,源自奥迪RS7),调用后可以从工具平台拉取验证码; - 得到验证码后直接写入到
系统剪贴板,可以直接CV(CMD + V)去 - 拉取验证码时可以通过手机号,也可以提前调用命令为手机号设置的别名,比如我把
7384311234设置为tf; - 第二步骤需要一个设置别名的需要,所以还得有个命令设置别名,当然还得有地方保存;
- 我们团队内部业务很多,卷起来,当然得能够复用到别人家的业务,所以得命令要支持配置业务类型等参数;
三、技术点准备
先搞个技术选型吧,我选 TS + esbuild,选型过程:
- 举起右手;
- 朝自己的额头轻拍三下;
好了,选型完毕😂 😂,开玩笑的哈,前面我们搞
E2E时写SDK同事推荐用TS写Node.js,用TS写Node.js调试不方便,要实时编译成js。至于用esbuild,就是制造用的机会,我司业务用webpack打包,用esbuild属于没有机会就制造机会。
3.1 全局安装的命令
这个好解决,如果你读过我前面的 来吧,撸一个快速 git 提交工具 ,你就知道答案了。想让命令全局安装,只需要在 package.json 中正确配置 bin 字段,bin 字段对应可执行文件,如下:
{
"name": "some-smscode",
"version": "0.0.4",
"description": "a cmd for challenge code",
"main": "bin/joymax.js",
"scripts": {
"test": "bin/joymax.js"
},
"bin": {
"joymax": "bin/joymax.js" // 这里
},
"keywords": [
"challenge code"
],
"author": "TouchFish",
"license": "ISC",
"dependencies": {
"@xxxxx/some-tasks": "^1.0.3",
"commander": "^9.0.0"
},
"devDependencies": {
"chalk": "^5.0.0",
"esbuild": "^0.14.21"
}
}
当 some-smscode 被 npm i -g some-smscode,全局安装后就可以全局执行 joymax,如下:
$ joymax --help
3.2 命令设计
3.2.1 查询验证码的 q 命令
# 用手机号查
$ joymax q 7384311234
# 用别名查
$ joymax q moyu
3.2.2 添加别名 add 命令
- 参数如下:
- 2.1
-n,--name电话号码别名 - 2.2
-p,--phone电话号码
- 2.1
$ joymax add -n moyu -p 7384311234
3.2.3 修改配置命令 ccfg 命令
- 参数如下(这部分参数涉密了,我胆子小就瞎写几个了):
- 3.1
-f, --fid, 平台fid - 3.2
-k, --key, 平台kid - 3.3
-a, --appid, 平台appid
- 3.1
$ joymax ccfg -f 250
$ joymax ccfg -a f**k
3.2.4 查看配置 ls 命令
我们目前把所有的配置写到这个包的 cfg.json 中,这么做是有隐患的,这里先不说,看官老爷可以思考一下;
命令时通用的,能修改配置,配置多了谁知道现在是啥,所以就需要个 npm config ls 一样的一个命令,列出 cfg.json 的文件内容即可
$ joymax ls
3.3 解析命令和参数
这里我们使用开源的命令行支持工具 commander,这个包包是个好东西,他可以注册命令,解析命令行参数。
接着呢我们简单说一种生产环境常用的用法,这个简单粗暴,不过还是是推荐你去看官方文档。
3.3.1 命令组织
首先我们的上面的命令一个命令对应一个文件,其中主命令为 joymax,子命令 joymax q, joymax add, joymax ccfg, joymax ls;
主命令的名字将作为子命令的文件前缀,所以最终的命令一共有以下几个文件:
- 对应
joymax命令的joymax.js,这个相当于是个入口,其余的子命令都在这里注册; - 对应
joymax add的joymax-add.js; - 对应
joymax q的 `joymax-q.js; - 对应
joymax ccfg的joymax-ccfg.js; - 对应
joymax ls的joymax-ls.js;
当然,这些 js 文件都要是可执行文件,并且都是由 ts 编译而来,这个是个后话;
3.4 esbuild
esbuild 是个新兴的打包工具,api 形式的调用,支持 ts,相对简单;
3.4.1 安装 esbuild
$ npm install esbuild --save-dev
3.4.2 esbuild 的示例配置如下
#!/usr/bin/env node
let fs = require('fs');
let child_process = require('child_process');
// 跑命令用的,这个方法来自 webpack,我超级喜欢这个方法
const runCommand = async (cmd, args, needReturn = false) => {
return new Promise((resolve, reject) => {
if (!needReturn) {
let execCmd = child_process.spawn(cmd, args, {
shell: true,
stdio: 'inherit'
});
execCmd.on('error', reject);
execCmd.on('exit', (code) => +code === 0 ? resolve(code) : reject(code))
} else {
child_process.exec(cmd, args, (err, stdo, stde) => {
if (err && err.code !== 0) reject({ code: err.code, data: null })
else resolve({ code: 0, data: stdo })
})
}
})
};
let esbuild = require('esbuild');
// 入口文件名
let entries = [
'joymax',
'joymax-q',
'joymax-add',
'joymax-ccfg',
'joymax-ls'
];
let p = (item) => `./src/${item}.ts`
esbuild.build({
entryPoints: entries.map(p),
platform: 'node',
target: 'es2015',
bundle: true,
banner: {
js: '#!/usr/bin/env node' // 给每个输出文件增加脚本解析头,告诉系统该脚本由 node 解析执行
},
outdir: 'bin'
// minify: true, // 压缩
// sourcemap: true,
}).then(() => {
// 打包结束会执行这个回调,在这里我们对所有输出 js 文件进行 +x 可执行权限的授权
entries.forEach(async (item) => {
await runCommand(`chmod +x ./bin/${item}.js`);
console.log(`chmod + x ${item}.js done!`)
});
console.log('打包完成!')
}).catch(() => process.exit(1));
3.5 复制到剪贴板
pbcopy命令可以复制所有写入到std.in中的内容
这里我就偷懒了,我搜了一下可以跨平台的剪贴板库,但是多少有点复杂,所以我选择放弃😂。所以我这里用了 MacOS 原生操作剪贴板的命令 pbcopy;简单粗暴,一行命令就搞定了。
但是,但是这里这个做法就不支持跨平台了,只能给 MacOS用;当然为了降低损失,我会把最后的验证码输出到命令行,给用户自己复制;
$ echo 010021 | pbcopy
四、总结
本篇小作文给大家描述了一个背景,分析了需求,最后给大家准备了实现这些功能的技术点:
package.json的bin字段与去全局安装;- 命令设计和
commander组织命令并且解析命令参数; esbuild的打包脚本;- 复制到剪贴板的实现;
本篇为姊妹篇,每个命令实现请看下一篇:# 不想写需求?摸鱼也要写 CLI 命令!(2)
最后恭祝各位看官老爷新年发财,再次预祝点赞的老爷成为尊贵的奥迪车主;