happypack有点问题

happypack4.0.0@beta1版本以及以前的版本,并不能很好的兼容webpack2以上的版本。

这个问题并不大,但是目前来说比较影响观瞻。

这个问题实际反映在哪里呢?

当我使用webpack3的时候,经常发现happypack报两个重复的错误。

./static/client/js/tool/app.js
(Emitted value instead of an instance of Error)
/Users/lipenghui/works/qietv-mobile/static/client/js/tool/app.js
   6:5   warning  'downloadbtn' is never reassigned. Use 'const' instead  prefer-const
   8:5   warning  'url' is never reassigned. Use 'const' instead          prefer-const
  10:5   warning  'mask' is never reassigned. Use 'const' instead         prefer-const
  15:14  error    Missing semicolon                                       semi
  24:7   error    Missing semicolon                                       semi

✖ 5 problems (2 errors, 3 warnings)
  2 errors, 3 warnings potentially fixable with the `--fix` option.

NonErrorEmittedError: (Emitted value instead of an instance of Error)
/Users/lipenghui/works/qietv-mobile/static/client/js/tool/app.js
   6:5   warning  'downloadbtn' is never reassigned. Use 'const' instead  prefer-const
   8:5   warning  'url' is never reassigned. Use 'const' instead          prefer-const
  10:5   warning  'mask' is never reassigned. Use 'const' instead         prefer-const
  15:14  error    Missing semicolon                                       semi
  24:7   error    Missing semicolon                                       semi

✖ 5 problems (2 errors, 3 warnings)
  2 errors, 3 warnings potentially fixable with the `--fix` option.

    at Object.emitError (/Users/lipenghui/works/qietv-mobile/node_modules/webpack/lib/NormalModule.js:120:14)
    at Object.emitError (/Users/lipenghui/works/qietv-mobile/node_modules/happypack/lib/HappyRPCHandler.js:85:12)
    at HappyRPCHandler.execute (/Users/lipenghui/works/qietv-mobile/node_modules/happypack/lib/HappyRPCHandler.js:58:22)
    at ChildProcess.acceptMessageFromWorker (/Users/lipenghui/works/qietv-mobile/node_modules/happypack/lib/HappyThread.js:80:27)
    at emitTwo (events.js:125:13)
    at ChildProcess.emit (events.js:213:7)
    at handleMessage (internal/child_process.js:753:14)
    at Pipe.channel.onread (internal/child_process.js:472:11)

从报错分析,我们的其中一个错误是经过Object.emitError时报出的NonErrorEmittedError: (Emitted value instead of an instance of Error)。 查阅webpack/lib/NormalModule.js的源代码可知,在webpack2及以上的版本中,emitError和emitWarning在接受到的消息不是一个错误的时候,会重新生成一条错误记录,扔进errorStack里。。。。

emitWarning: (warning) => {
    if(!(warning instanceof Error))
        warning = new NonErrorEmittedError(warning);
    this.warnings.push(new ModuleWarning(this, warning));
},
emitError: (error) => {
    if(!(error instanceof Error))
        error = new NonErrorEmittedError(error);
    this.errors.push(new ModuleError(this, error));
},

在现有loader的代码中,很多对LoaderContext版本进行了处理,或者干脆新版本里传过去的错误消息就是继承于Error,比如eslint-loader,源代码中有一句

// 注,该webpack为LoaderContext实例,非webpack实例
emitter(webpack.version === 2 ? new ESLintError(messages) : messages)

如果LoaderContext的版本是2,就扔出一个继承自Error的消息

// webpack/lib/NormalModule.js中创建loaderContext的方法
// version === 2
createLoaderContext(resolver, options, compilation, fs) {
        const loaderContext = {
            version: 2,
            emitWarning: (warning) => {
                if(!(warning instanceof Error))
                    warning = new NonErrorEmittedError(warning);
                this.warnings.push(new ModuleWarning(this, warning));
            },
            emitError: (error) => {
                if(!(error instanceof Error))
                    error = new NonErrorEmittedError(error);
                this.errors.push(new ModuleError(this, error));
            },
            exec: (code, filename) => {
                const module = new NativeModule(filename, this);
                module.paths = NativeModule._nodeModulePaths(this.context);
                module.filename = filename;
                module._compile(code, filename);
                return module.exports;
            },
                。。。

高潮来了。

happypack本身会建立一个假的loaderContext用以分线程处理任务,这个loaderContext的version是1。。。

unction HappyFakeLoaderContext(initialValues) {
  var loader = {};

  // for compiler RPCs, like this.resolve()
  loader._compiler = null;

  // for loader RPCs, like this.emitWarning()
  loader._remoteLoaderId = null;

  loader.version = 1; // https://webpack.github.io/docs/loaders.html#version
  loader.webpack = false;
  loader.request = null;
  loader.query = null;

  loader.context = null;
  loader.resource = null;
  loader.resourcePath = null;
  loader.resourceQuery = null

于是在接洽真正的loader并获得errorMessage抛给主进程的时候,我们的happypack就跪了,于是我们有了两个报错。。。

然后,就有人修复了是不是~

对,有人修复了并给作者push了一下。兼容方案如下

 HappyRPCHandler.prototype.execute = function(type, payload, done) {
    var compiler, loader;
  
     if (payload.message.happyPackIsError) {
         var oldMessage = payload.message;
         payload.message = new Error(oldMessage.message);
         delete payload.message.stack;
         for (var i in oldMessage) {
             if (i !== 'message' && i !== 'happyPackIsError') {
                 payload.message[i] = oldMessage[i];
             }
         }
     }

完全无视了version,这样也好。。也好,毕竟我们npm是可以指定模块版本的。。

然鹅。。在本篇文章发表12小时前。。。

clipboard.png

修复代码没有通过ci的构建,并没有解决该问题。。。


geeeger
459 声望35 粉丝

我是一个智障,你害怕了吗?我的破文章如果对您有用的话,请赏一个,感谢