Webpack5插件编写与Node的bug

1,100 阅读1分钟

记录一个问题的调研过程。

在研究webpack4和webpack5编译过程的不同,写了插件用于暴露整个编译过程,准备逐个研究hook的回调。插件代码如下:

const PLUGIN_NAME = 'TestPlugin'

const traverse = {
    contextModuleFactory: (arg) => {
        console.log(arg)
    }
}

module.exports = class TestPlugin {
    apply(compiler) {
        Object.keys(traverse).forEach(hook => {
            compiler.hooks[hook].tap(PLUGIN_NAME, traverse[hook])
        })
    }
}

在调用contextModuleFactory或者run之类的hooks时报错:

[webpack-cli] TypeError: Function has non-object prototype 'null' in instanceof check
    at Function.[Symbol.hasInstance] (<anonymous>)
    at getConstructorName (internal/util/inspect.js:535:13)
    at formatRaw (internal/util/inspect.js:803:23)
    at formatValue (internal/util/inspect.js:793:10)
    at formatProperty (internal/util/inspect.js:1679:11)
    at formatRaw (internal/util/inspect.js:1007:9)
    at formatValue (internal/util/inspect.js:793:10)
    at formatProperty (internal/util/inspect.js:1679:11)
    at formatRaw (internal/util/inspect.js:1007:9)
    at formatValue (internal/util/inspect.js:793:10)

全都是internal/*的错误。

但是当回调函数没有入参的时候,不会报错:

const traverse = {
    // 去掉入参,不报上述错误
    contextModuleFactory: () => {
        console.log()
    }
}

先上百度搜一下,没啥结果,这个得吐槽了。

然后翻墙用google搜,找到一条webpack的issue:github.com/webpack/web… ,情况描述一摸一样。有人认为是node的bug,并写了个demo复现:

function X () {}
X.prototype = null;
x = {};
x.constructor = X;
console.log(x);

我当时的node版本是14.9,会复现这个问题。浏览器没事。

结合这个例子,这个bug大概问题是,在node执行instanceof检查的时候,一个function类的prototype为null时,导致报错。

在调用compiler.hooks.contextModuleFactory.tap时,正常情况下会返回一个ContextModuleFactory对象入参。怀疑这个入参的构造过程有点问题,不过没看源码核实,因为讨论中都觉得这是node的锅。

好在后面的讨论里,有人说node修复了这个问题:github.com/nodejs/node…

通过brew把node版本升级到15,问题解决。