[FE] uglify-es需要的innerSourceMap
1. LOADER_EXECUTION
webpack在载入资源的时候,会先加载loader,然后再使用loader加载资源。
loader加载资源的过程,是由图中LOADER_EXECUTION
函数完成的。
如果用户没有配置source map,
则加载资源的时候,context.sourceMap
的值就是false
了。
这个值会影响资源的载入过程,
下图是babel-loader加载ES6资源时的转换过程,
其中source为待加载的ES6代码,options.sourceMaps
为false
。
此时加载的结果,就已经不带source map了,
2. createSource
babel-loader载入资源后没有生成source map会影响到,webpack模块的doBuild
结果。
首先,runLoaders即使用loader载入资源,result.result[1]
为null
,表示没有生成source map。
然后在doBuild
的后续操作中,createSource
的参数sourceMap
也为空。
结果,如果用户没有配置source map,则最终生成数据结构就不是SourceMapSource
了,
而是最后的OriginalSource
,其中只包含loader转换之后的ES5代码。
3. uglifyjs-webpack-plugin
资源载入之后,webpack将加载的资源放到了compilation.assets中,
然后调用uglifyjs-webpack-plugin进行代码压缩。
这时候需要传入babel-loader转译后的代码,以及转译时生成的source map,给uglify-es用于minify,
但是,如果用户没有配置source map的话,
uglify-es minify需要的inputSourceMap就没有了。
因此,即使在这里通过插件的方式获取到了待压缩的代码,手动minify,
所生成的最终source map,也只能定位到babel-loader压缩前的ES5代码位置了,
不能定位到babel-loader之前的ES6代码。
这是由于当时在资源载入阶段,存入compilation.assets
中的数据结构是OriginalSource
导致的。
下面我们再来看一下,当用户配置了source map之后,这里的值是什么。
我们看到这里变成了SourceMapSource
了,
其中包含的_sourceMap
,正是从babel-loader转换后的ES5,定位到转换之前的ES6所必需的。
4. 如何强制开启
class WebpackPlugin {
apply(compiler) {
tapHook(compiler, 'compilation', compilation => {
// 调用loader-runner的runLoaders之前触发的hook
tapHook(compilation, 'normalModuleLoader', (loaderContext, normalModule) => {
// 让loader以带source map的方式转译代码
loaderContext.sourceMap = true;
// createSource时生成SourceMapSource
normalModule.useSourceMap = true;
});
});
}
}
module.exports = WebpackPlugin;
以上webpack插件可以当用户不配置source map的情况下,强制开启babel的innerSourceMap。
这样做有一个缺点,就是延长了webpack的资源载入时间。
另一个办法,可能是手动实现runLoaders,把innerSourceMap额外生成出来。
这个办法还有待探索。