closure-webpack-plugin
closure-webpack-plugin copied to clipboard
Allow google closure self-managed js dynamic imports
Google Closure Library has a dynamic imports engine --- goog.module.ModuleManager, which can be used to lazy loading JS resource. This engine is different from webpack, thus it should be handled without webpackJsonp.
I have used ModuleManager successfully to realize JS lay loading with closure-webpack-plugin, only in case where entry chunks have no common dependence. I have to use CommonChunksPlugin When entry chunks have common dependence, as mentioned in Readme. But webpack tries to handle child chunk(lazy loading chunk) with webpackJsonp, which will cause child chunk lazy loading failure. The reason is below: https://github.com/webpack-contrib/closure-webpack-plugin/blob/2786b261abb06e427ee0d7c3c12f1f613322e8d2/src/index.js#L440-L446
I suggest adding an option: entryChunks
to use entryChunkWrapper
instead of webpackJsonp
.
Could you grant me pull request permission? I have done implemented it locally. @ChadKillingsworth
@jplaisted Can you weigh in on this? I've not used ModuleManager. Should this be recognized by webpack as a potential split point for code splitting?
Could you grant me pull request permission
Anyone can make a pull request. You need to fork this repo, push changes to a branch there, then you can make a pull request to the main repo.
Admittedly I know very little about this as well. AFAIK it is what we use for code splitting internally.
Rerouting to @concavelenz @shicks or @brad4d.
@MorganDream
The section of code you referenced is part of the closure-webpack-plugin
repository rather than the closure-compiler
one. Which one of these repositories would your PR affect?
I think submitting a PR will be your best bet, here, since it'll make it clear exactly what you're doing.
Unfortunately most of us working on closure-compiler don't use webpack, so we don't have a good understanding of how it interacts with stuff like ModuleManager
.
@ChadKillingsworth @brad4d Thanks for your response and instruction.
Yes I want to issue a PR, and my local solution is working for me.
But I am not sure if it is the best solution to add a config option.
ModuleManager
lazy loading is working in NONE
mode but not working in AggressiveBundle
mode, in later mode, webpackJsonp
is compiled to a function accepting 2 params instead of 3(I am using webpack 3). So I guess it is because webpackJsonp
is compiled too aggressively that it is not working.
Update:
In AggressiveBundle
mode, webpackJsonp is compiled to be
var a = window.webpackJsonp;
window.webpackJsonp = function(b, c) {
var d, e = [];
for (d = 0; d < b.length; d++)
aa[b[d]] && (e.push(aa[b[d]][0]),
aa[b[d]] = 0);
for (a && a(b, function() {}); e.length; )
e.shift()(c)
}
According to
https://github.com/webpack-contrib/closure-webpack-plugin/blob/2786b261abb06e427ee0d7c3c12f1f613322e8d2/src/index.js#L440-L444
A child chunk function will be never be executed if aa
is empty, which is my case.
Are you certain webpackJsonp will work in AggressiveBundle
mode?
That is why I want to abort webpackJsonp wrapper in my PR, and it works well just regarding child chunk as entry chunks.
Your entryChunks idea is creative - but not really in the spirit of how webpack works. Can you put up a demo using ModuleManager? That will help me understand exactly what the problem is.
I'm guessing it's because webpack doesn't recognize the call to lazy load a module. If that was recognized, it could be an automatic split point and then it would be clearer what needs to be fixed for webpack's runtime to load the module.
// A.js
TEST_MM = true;
goog.module.ModuleManager.getInstance().setLoaded('moduleA');
// main.js
var moduleManager = goog.module.ModuleManager.getInstance();
moduleManager.setLoader(new goog.module.ModuleLoader());
moduleManager.setAllModuleInfo({
'moduleA': []
});
moduleManager.setModuleUris({
'moduleA': 'http:url/to/your/compiled/A.js'
});
// lazy loading triggered by action
document.addEventListener('click', function(e){
// this success callback is called after module loaded successfully
// which is when moduleManager.setLoaded('moduleA') is executed
// below process will load compiled A.js by XHR and then execute it
moduleManager.execOnLoad('moduleA', function(){
console.log(window.TEST_MM); // true
});
});
// webpack.config.js
module.exports = {
entry: {
'main': 'path/to/main.js',
'moduleA': 'path/to/A.js'
}
...
plugins: [
new ClosurePlugin({
mode: 'AGGRESSIVE_BUNDLE', // in 'NONE' mode, this can work, but not in Aggressive_bundle
...
}),
// We extract common dependencies into main to avoid ClosurePlugin error, as you suggested
// In this case, they don't have common dependencies
// This will still establish a parent-child relationship between main and moduleA
new webpack.optimize.CommonsChunkPlugin({
name: 'main',
minChunks: 2
})
]
}
As moduleA is a child chunk, it will be wrapped with webpackJsonp
and never executed even lazy loaded successfully. if A.js is not executed, setLoaded
flag is never hitted, the success callback will never be executed.
After more investigation, I found that closure-webpack-plugin itself has a runtime file which will be included to compilation when chunk that has no runtime is included. Through this runtime, you can manage dynamic imports in webpack style compatible with closure compiler(which I guess). However, this is not compatible with goog.module.ModuleManager, which manage chunk info, url, downloading, executing and callback all by itself instead of webpack.
So I suggest adding an option runtimeNeedlessChunks to let ModuleManager manage the related chunks.