一次webpack编译成功却没有文件输出的问题跟踪

编译成功完成,但是没有编译后的文件输出到目标目录上。这个就逗了。

打日志发现webpack的stats的assets为空,filteredAssets显示63,也就是说全部被过滤掉了。从webpack源码Stats.js跟着找原因,为什么被filter了。createAssetFilter里被干掉了,发现这些assert的emitted字段都为false。emitted是在403行被加上的。compilation是在Compiler.js的onCompiled中实例化Stats时传入的。并且已排除happyPack的嫌疑(不启用happyPack也会有这个问题)。在Compiler.js的emitAssets方法里316行找下,发现在391行设置了compilation的emittedAsset。但发现并没有这里,走的是402行那个else分支,是直接给asset(source)设置了emitted。在414行,发现asset的emitted已被设置为true,这就蛋疼了。

后来又发现这个其实是DLL的compiler,在Primary compiler里,压根没进这里。发现进了316行emitAssets,也就是在243行调用this.emitAssets的callback里面,这个回调也进了。在这个回调的最下面的就是生成stats了。在325行执行emitFiles,发现emitFiles没有被执行,对比正常的DLL编译,这里就是差异的地方了,也就是这个地方没有去输出文件导致了问题产生。

继续深入这里查看,到450行的this.hooks.emit.callAsync方法的回调,发现没有进到回调这里,而且callAsync已经属于tapable的封装了,不会有什么问题。于是直接再往回看,发现是232行this.hooks.shouldEmit这个方法的调用结果为false,也就是说不emit直接输出stats,在241行直接return,就是这个判断为false导致的问题。这个判断的方法还传入了compilation,就是说是否emit和compilation和这个方法的内部逻辑有关。

继续深入,这个shouldEmit是tapable的SyncBailHook钩子的call方法返回的结果,call方法经过_createCall等最终调用了compile方法,compile调用的是HookCodeFactory的实例的create方法。也就是说调用shouldEmit的call方法以后,实际上是调用了compile返回的那个方法,但这个并不是最终影响到的地方,这个只是是tapable的流程,属于流程控制的东西,不是真正干活的东西,到这就走到死路上了。

后来反应过来真正干活的应该是某个插件,于是在插件有关的文件中找了下,发现在NoEmitOnErrorsPlugin这个插件里做了判断,如果compilation的Stats有Errors的话,就返回false不emit asset,也就是说在compilation是存在有错误的,这就很扯淡了,有错误缺没有在控制台上打印出来。再继续跟进,发现是eslint-loader这个loader和webpack之间的问题,错误出来了,但webpack那边构建完成后,没有报错,导致在build的过程中发生错误却无错误输出,但是最后编译好的assert因为有错,全部被filter掉了,导致无资源emit到编译输出目录上。

这个loader我用的是一个早期的版本,最新的版本比我用的高了2个大版本。按最新版本里的说法是错误和告警的上报都是自动根据是否有错误和告警来决定的,不需要特别配置。于是我将webpack更新至v4的最新版本,将eslint-loader更新至最新版本依旧如此。(在webpack v4的后期版本中,这个loader已被弃用,项目已迁移至eslint-webpack-plugin,建议使用这个插件替代,需要配合eslint >=7的版本。)我先把build脚本里加上了对fiterAssert数的判断,拦截住这种异常情况,如果有过滤掉的assert,直接reject build流程报错到终端上,免得出现这种没错误又没文件输出的模棱两可的情况。

接着回到之前跟进的进度,打印compilation的errors,没发现错误,打印compilation的所有child的errors,发现有错误,错误为eslint报的,xxx.worker.js中self变量未定义就使用了,这是使用了comlink后特别指定的global对象的alias,这个错误在child中,也就是子编译器爆出的错误。然后我试着在eslint-loader的配置中将emitError和failOnError都设置为true,但是在处理worker文件遇到语法问题后,语法错误都不会报上来,但是在非worker文件中,我人为写了一个语法错误,是直接报上来了的,这个问题恐怕就是worker-loader和eslint-loader之间的问题了,去worker-loader的issue里找了下,确实是worker-loader这边的问题,一个有隐患的解决方法是在optimization中设置noEmitOnErrors为false来禁用NoEmitOnErrorsPlugin插件,也就是说让weboack有error也照样emit,我是没有这样做的。最后在worker文件头部加上eslint的disable注释来忽略语法检测,问题解决,打包过程没有报错,所有资源正确输出,至此结束。

原文地址:https://www.cnblogs.com/rock-roll/p/14420155.html