[FE] webpack群侠传(三):log + debug

学习源码的一个好办法就是,跟进源码的逻辑中,看看流程是怎么样流转的,
这需要我们有直接debug代码的能力,
有时候还需要我们在某些关键位置写入log

下面我们从npm run build 命令行工具开始,想办法debug进webpack中,
然后在关键位置写入log。

1. npm scripts

上一篇中,我们在命令行中调用npm run build
源码就被自动的编译打包,然后结果输出到了 ./dist/index.js 文件中了。

$ npm run build

> [email protected] build ~/Test/debug-webpack
> webpack

Hash: 2e91628041d9a877f709
Version: webpack 4.20.2
Time: 639ms
Built at: 2018-10-09 09:25:24
   Asset       Size  Chunks             Chunk Names
index.js  937 bytes       0  [emitted]  index
Entrypoint index = index.js
[0] ./src/index.js 8 bytes {0} [built]

可是,这到底发生了什么呢?

1.1 npm run build

上一篇中我们在npm scripts配置了npm run build命令,

{
    ...,
    "scripts:": {
        ...,
        "build": "webpack"
    }
    ...,
}

通过查看npm-run-script文档,我们知道,
npm run会自动添加node_module/.bin 到当前命令所用的PATH变量中,
因此,npm run build 实际会调用 node_modules/.bin/webpack

$ node_modules/.bin/webpack
Hash: 2070b107dceedfc63c72
Version: webpack 4.20.2
Time: 334ms
Built at: 2018-10-09 10:13:05
   Asset       Size  Chunks             Chunk Names
index.js  930 bytes       0  [emitted]  index
Entrypoint index = index.js
[0] ./src/index.js 10 bytes {0} [built]

与执行npm run build 效果一样。

1.2 显示原身

[FE] webpack群侠传(三):log + debug_第1张图片

我在Finder中打开这个文件看了下,发现它是一个软链接(symbolic link),
于是,我们还要看看它的原身在哪里。

$ l ~/.nvm/versions/node/v8.12.0/bin/webpack
lrwxr-xr-x  1 用户名  staff    41B 10 24 09:50 node_modules/.bin/webpack -> ../[email protected]@webpack/bin/webpack.js

我们看到它的原身在这里,

../[email protected]@webpack/bin/webpack.js

完整路径如下,

~/Test/debug-webpack/node_modules/[email protected]@webpack/bin/webpack.js

这就是我们在node_modules中安装的webpack模块的文件地址。

我们来看看代码,
https://github.com/webpack/webpack/blob/v4.20.2/bin/webpack.js

#!/usr/bin/env node

process.exitCode = 0;

/**
 * @param {string} command process to run
 * @param {string[]} args commandline arguments
 * @returns {Promise} promise
 */
const runCommand = 
...

以上链接是webpack github仓库的地址,我已经找到了tag为4.20.2的版本位置,
它展示了4.20.2版本的webpack,./bin/webpack.js的源代码。
后文中我们可以使用这样的方式展示源代码了。

1.3 Shebang

我们注意到了,./bin/webpack.js 文件头有一行这样的代码,

#!/usr/bin/env node

它被称为 Shebang。

在类Unix系统中,包含Shebang的文本,如果作为可执行文件调用,
#!后面指定的解释器将会被调用,用来执行后面的代码。

Shebang 语法如下,

#!interpreter [optional-arg]

注:
/usr/bin/env 不是一个路径,而是一个命令,
后面跟node 参数,就会找到node并调用它,
我们来试试,

$ /usr/bin/env node --version
v8.12.0

2. 写入日志

上文中,我们了解到,
npm run build最终导致node解释执行了 ./bin/webpack.js 文件。
由于Node.js是解释型语言,所以,我们可以直接修改源码,来查看更改效果。

一般而言,最常见的写日志的方法是直接使用console.log
但是我们发现,控制台还输出了其他的文本,
我们很难找到自己输出的信息。

为了展示方便,我决定使用 debug 模块来输出信息,
它是一个日志库,可以用颜色来区分不同的日志,
看看github仓库中的官方截图,

[FE] webpack群侠传(三):log + debug_第2张图片

2.1 安装debug为devDependencies

./bin/webpack.js 位于 ~/Test/debug-webpack/node_modules/[email protected]@webpack 文件夹中,
我们进入这个文件夹,然后安装debug,

$ cd ~/Test/debug-webpack/node_modules/[email protected]@webpack
$ npm i -D debug

2.2 使用debug

在 ./bin/webpack.js 文件头部调用debug,这里我们创建了一个log变量。

#!/usr/bin/env node
const log = require('debug')('debug-webpack webpack webpack.js');

...

记得要放到 #!/usr/bin/env node 后面,
其中第二个参数debug-webpack webpack webpack.js 称为namespace ,可用于区分日志的颜色
这里我们为整个文件使用了相同的namespace。

2.3 bin/webpack.js 代码逻辑

[FE] webpack群侠传(三):log + debug_第3张图片

通过阅读 ./bin/webpack.js 源码,我们发现,
它首先会对已安装的CLI进行检查,然后会载入安装的CLI工具。
webpack要求我们必须安装webpack-cli 或 webpack-command 之一,否则就会报错。

if (installedClis.length === 0) {
    // 报错
}

源码位置如下:https://github.com/webpack/webpack/blob/v4.20.2/bin/webpack.js#L84

如果我们已经安装了某一个CLI的话,就会加载这个CLI,源码第149-159行,

else if (installedClis.length === 1) {
    const path = require("path");
    const pkgPath = require.resolve(`${installedClis[0].package}/package.json`);
    // eslint-disable-next-line node/no-missing-require
    const pkg = require(pkgPath);
    // eslint-disable-next-line node/no-missing-require
    require(path.resolve(
        path.dirname(pkgPath),
        pkg.bin[installedClis[0].binName]
    ));
}

注意以上代码第7行,webpack动态 require了一个地址,
这时候我们的log 工具就有用武之地了。

const cliPath = path.resolve(path.dirname(pkgPath), pkg.bin[installedClis[0].binName]);
log('cliPath: %s', cliPath);
require(cliPath);

2.3 查看日志

直接按原样调用npm run build是看不到刚才写入的日志信息的,
我们还需要传入前置参数

$ DEBUG=debug-webpack* npm run build

The environment for any simple command or function may be augmented temporarily by prefixing it with parameter assignments, as described > in Shell Parameters. These assignment statements affect only the environment seen by that command.
—— Bash Reference Manual - 3.7.4 Environment

其中名为DEBUG的前置参数,是 debug 模块所需要的,
debug-webpack* 表示我们要输出所有以debug-webpack 开头namespace中的日志。
我们示例中,namespacedebug-webpack webpack webpack.js

运行结果如下,

debug-webpack webpack webpack.js cliPath: ~/Test/debug-webpack/node_modules/[email protected]@webpack-cli/bin/cli.js +0ms

3. webpack-cli/bin/cli.js

[FE] webpack群侠传(三):log + debug_第4张图片

上文中我们得到了webpack require的CLI地址,

~/Test/debug-webpack/node_modules/[email protected]@webpack-cli/bin/cli.js

源码位于,https://github.com/webpack/webpack-cli/blob/v3.1.2/bin/cli.js
webpack-cli版本为 v3.1.2

分析源码我们发现,代码中第436行requirewebpack模块,
https://github.com/webpack/webpack-cli/blob/v3.1.2/bin/cli.js#L436

const webpack = require("webpack");

随后在第441行,调用webpack,返回了一个compiler
https://github.com/webpack/webpack-cli/blob/v3.1.2/bin/cli.js#L441

compiler = webpack(options);

最后,在第533行,调用了compiler.run
https://github.com/webpack/webpack-cli/blob/v3.1.2/bin/cli.js#L533

} else compiler.run(compilerCallback);
[FE] webpack群侠传(三):log + debug_第5张图片

4. 开始debug

知道了webpack-cli的代码逻辑之后,我们就可以创建一个debug.js脚本来模拟webpack-cli调用了,
在我们上一篇debug-webpack示例项目中,添加一个./debug.js 文件,

const webpack = require('webpack');
const options = require('./webpack.config');

const compiler = webpack(options);

compiler.run((...args) => {
    console.log(...args);
});

保持这个文件打开状态,在以上代码第6行位置打个断点,
然后在vscode中按 F5(或者点击左侧调试面板,再点击调试)。

[FE] webpack群侠传(三):log + debug_第6张图片

代码就停在我们的断点位置上了。

然后我们可以点击左数第3个按钮,进行单步调试,就可以进入compiler.run方法中了。


参考

github: debug
github: webpack v4.20.2 ./bin/webpack.js
github: webpack-cli v3.1.2 ./bin/cli.js
Debugging in Visual Studio Code

你可能感兴趣的:([FE] webpack群侠传(三):log + debug)