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小时前。。。
修复代码没有通过ci的构建,并没有解决该问题。。。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。