blog icon indicating copy to clipboard operation
blog copied to clipboard

Node CLI 开发调试技巧总结

Open younth opened this issue 7 years ago • 5 comments

最近这段时间一直在折腾各种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/node-cli.js",
    "args": [
        "-v"
    ],
    "env": {
        "PLUGIN_CORE_PATH": "/Users/younth/node/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输出各种酷炫的效果,这里也把自己使用的模块分享给大家:

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

younth avatar Mar 24 '18 08:03 younth

vscode debug是需要F5启动的,但是往往cli程序是在命令行里输入并带有参数的,比如:webpack --env.platform=web,请问在vscode里打断点能拿到后面的命令行参数吗?

cike8899 avatar Mar 28 '18 15:03 cike8899

@cike8899 这个问题是调试中比较常见的,终端直接输入cli命令,且带参数,这时候用vscode调试是不方便的。目前我都是用 debug 输出日志调试。如果一定要断点调试,不能直接终端执行 webpack 需要用 node --debug ./bin/webpack 这种形式启动webpack

younth avatar Mar 29 '18 02:03 younth

可以

wython avatar Dec 01 '20 03:12 wython

适用 vscode 断点调试时, 有时有交互的输入, 这种情况下,断点是进不去的,这怎么解决。 例如 创建项目的 cli, 会让你输入项目相关的数据(项目名, 描述之类的)

Been101 avatar Feb 19 '21 06:02 Been101

适用 vscode 断点调试时, 有时有交互的输入, 这种情况下,断点是进不去的,这怎么解决。 例如 创建项目的 cli, 会让你输入项目相关的数据(项目名, 描述之类的)

找到方法了, 添加 console 配置可以解决

Been101 avatar Feb 19 '21 07:02 Been101