Node CLI 开发调试技巧总结

5,554 阅读4分钟

原文地址:github.com/younth/blog…

最近这段时间一直在折腾各种Node CLI,把个人的一些开发,调试技巧分享给大家~

一般情况下,一套CLI工具集合,包括CLI运行核心模块及各种插件体系。拿 webpack 来说,其由核心模块 webpack 及各种 loader plugin 组成其强大的构建生态体系。

单一NPM包的调试

npm link 到本地进行开发调试

  • 先进入模块目录执行 npm link
  • 再去项目目录通过包名来 link npm link my-plugin
  • 删除link: npm unlink my-plugin

npm link 的原理其实就是建立软连接,省去了自己建立软链的麻烦了

单一的npm包,调试方法比较多,vscode node-debug 或者 chrome devtool 调试node 都可以。如果是vscode ,launch.json 里面可以配置 env 与 args 也比较方便。单一模块开发推荐这种方式。

{
    "type": "node",
    "request": "launch",
    "name": "Launch Program",
    "program": "${workspaceRoot}/bin/def.js",
    "args": [
        "-v"
    ],
    "env": {
        "DEF_CORE_PATH": "/Users/younth/node-cli/def/node-cli"
    }
}

入口CLI的开发调试

入口cli模块一般是在 package.json 里面申明的:

"bin": {
    "webpack": "./bin/webpack.js"
}

bin 字段主要用于npm i -g时候可以将脚本添加到可执行路径中,之后可以在命令行中直接执行。例如npm i -g webpack后就可以全局使用webpack命令。

同样的我们在 CLI 的项目目录下执行 npm link 将 webpack 放入全局,这样后面我们就可以在命令行中直接使用了。对于CLI模块的调试仍然可以使用单一模块调试方法。

但是当 webpack 再去调用 loader 或者 plugin 的时候,我们想完整的调试整个过程,就不太方便了。尝试过用 node --debug 去启动 CLI 下面的 bin/webpack ,然后在调试具体执行模块,感觉开发起来很别扭,体验不好。而且如果是模块是通过child\_process.spawn 执行的,那真就没啥办法了。最终选择了以日志的形式进行开发调试。

说起日志,最简单的就是 console.log 调试了,但问题也很明显:

  • 模块本身需要输出一些信息,需要做到debug信息在正常启动模式下是不显示的
  • 涉及模块越来越多的时候,console 输出的信息会多而乱。这样增加了调试的复杂度,迫切需要 进行分模块块打印日志调试

我们引入了 debug 模块。使用也非常方便,通常我们只需要给每个模块指定特定的__命名空间__即可:

// app.js
var debug = require('debug')('app');

// 运行 DEBUG=app node app.js
// 输出:app hello
debug('hello');
debug('this is %s', string);
debug('this is %o', obj);

使用的时候,可以传参,自定义格式:

  • %s: string
  • %o: object

debug 同时支持命名空间:

  • DEBUG=app,api:表示同时打印出命名空间为app、api的调试日志。
  • DEBUG=a\*:支持通配符,所有命名空间为a开头的调试日志都打印出来。

关于守护程序(daemon)模块的调试

在开发Node Cli的过程中,我们经常需要执行一些 后台程序 , 比如 检测CLI及模块的版本更新情况,其本质就是利用 child\_process.spawn 以守护进程的方式执行一个js文件。我们可以通过设定 spawnstdout 参数来将输出指向某个文件。

const fs = require('fs-extra');
let stdout = fs.openSync(path.join(__dirname, 'check_out.log'), 'w')
let stderr = fs.openSync(path.join(__dirname, 'check_error.log'), 'w')
const p = child_process.spawn(
  'node', ['check.js', 'a', 'b'],
  {
    'stdio': ['ignore', stdout, stderr],
    'detached': true   // 让子进程能在父进程退出后继续运行
  }
);

如上述代码所示,在 check.js 里面进行 console.log 或者 debug 的,将会在 check\_out.log 里面看到结果。

正式环境的日志显示

以上都是开发时候的调试,在发布之后,有时候我们也需要一些信息输出给用户。我们经常用 --verbose 查看详细的运行情况。对于正式环境的错误提示,信息输出等,我们把日志级别分为:

  • verbose:详细日志,一般是通过 --verbose 参数查看
  • info:日常的输出
  • warn:警告日志
  • error:错误日志
  • debug:  这里不需要了,因为debug已经单独拿出来用 debug模块进行输出

可以考虑用原生util模块,这里推荐使用 npmlog ,功能也比较完善。

// additional stuff ---------------------------+
// message ----------+                         |
// prefix ----+      |                         |
// level -+   |      |                         |
//        v   v      v                         v
    log.info('[core]', 'node version is : %j', nodeVersion)

其他小技巧

有时候我们也想让自己的CLI输出各种酷炫的效果,这里也把自己使用的模块分享给大家:

最近也在梳理前端工程化的全链路开发的解决方案,看了业界很多工具,慢慢融化贯通后再尝试分享。