webpack-contrib/webpack-bundle-analyzer

Chunk trees become opaque with ModuleConcatenationPlugin in Webpack 3

fruchtose opened this issue ยท 16 comments

Issue description

Webpack 3introduces the plugin webpack.optimize.ModuleConcatenationPlugin for bundle optimizations. When used in conjunction with BundleAnalyzerPlugin, some chunks do not show their dependencies. Where there would once be a tree, there instead is an opaque label " + modules". When the module concatenation plugin is removed, the tree becomes traceable again as expected.

Technical info

  • Webpack Bundle Analyzer version: 2.9.0
  • Webpack version: 3.5.6

Hi! Thanks for opening an issue :). Would you be able to send a screenshot or even better, create a small reproduction repository for this case?

Also, if at all possible, would you be able to zip your bundles and stats.json?

I fear that there's little we can do with this. Although I've been wrong before about how ModuleConcatenationPlugin and webpack bundle analyzer work together ๐Ÿ˜…. I don't (yet) use module concat in my day job code, so I haven't come across this issue myself yet.

th0r commented

Well, that's how ModuleConcatenationPlugin works. It concatenates (merges) multiple modules into a single one so they don't have "egdes" anymore - they don't have stat size, they can't be parsed to get real size etc.

What to you want to see inside of this concatenated module?

At the very least, it would be nice for the documentation to mention that this plugin is incompatible with ModuleConcatenationPlugin. People who are not so familiar with Webpack's internals (like myself) will not assume that the two are incompatible.

th0r commented

What do you mean by "incompatible"? It shows you that there is a merged module in your bundle that contains N other modules. It's an expected result if you understand what ModuleConcatenationPlugin does.

By "incompatible" I mean that this plugin prevents the user from exploring the full dependency tree when included. Sorry, I should have used different wording.

Generally, my point is that users may include the ModuleConcatenationPlugin without realizing it will prevent a full visualization of the dependency tree.

It's an expected result if you understand what ModuleConcatenationPlugin does.

It's not really obvious to everyone that ModuleConcatenationPlugin will interfere with this plugin. It's a lot to expect every Webpack programmer to both understand the internals of Webpack, the internals of ModuleConcatenationPlugin, and the internals of this tool. Since the maintainers of Webpack now recommend using that plugin to all users, I imagine that I am not the first user to run into this issue, and I won't be the last. Mentioning in the README that there are other plugins that interfere with this tool would go a long way to alleviating this issue.

Hey, great idea! Would you be open to craft a PR to modify the README to contain information about this?

Would there be any way for the bundle analyzer to at least list out the files that are included in the module? Currently I just seeing an index.js + 77 module file name. Even without the file sizes, knowing what files are included would be helpful.

bundle analyzer output showing "index.js + 77 modules file name in rxjs

If not maybe an issue should be opened upstream on webpack/webpack so see if a solution could be implemented in the ModuleConcatenationPlugin.

It depends on whether we have that information in the webpack generated stats.json. Care to look for any possible data we could use?

Looks like the moduleIdentifier has a list of the included file paths

"moduleId": "riZV",
"moduleIdentifier": "[fullpath]\\operators\\buffer.js [fullpath]\\operators\\bufferCount.js [fullpath]\\operators\\bufferTime.js [fullpath]\\operators\\bufferToggle.js [fullpath]\\operators\\bufferWhen.js [fullpath]\\operators\\combineAll.js [fullpath]\\operators\\concat.js [fullpath]\\operators\\concatMapTo.js [fullpath]\\operators\\count.js [fullpath]\\operators\\debounce.js [fullpath]\\operators\\delayWhen.js [fullpath]\\operators\\dematerialize.js [fullpath]\\util\\Set.js [fullpath]\\operators\\distinct.js [fullpath]\\operators\\distinctUntilChanged.js [fullpath]\\operators\\distinctUntilKeyChanged.js [fullpath]\\operators\\elementAt.js [fullpath]\\operators\\exhaust.js [fullpath]\\operators\\exhaustMap.js [fullpath]\\operators\\expand.js [fullpath]\\operators\\find.js [fullpath]\\operators\\findIndex.js [fullpath]\\util\\MapPolyfill.js [fullpath]\\util\\Map.js [fullpath]\\util\\FastMap.js [fullpath]\\operators\\groupBy.js [fullpath]\\operators\\ignoreElements.js [fullpath]\\operators\\isEmpty.js [fullpath]\\operators\\mapTo.js [fullpath]\\operators\\materialize.js [fullpath]\\operators\\max.js [fullpath]\\operators\\mergeMapTo.js [fullpath]\\operators\\mergeScan.js [fullpath]\\operators\\min.js [fullpath]\\operators\\onErrorResumeNext.js [fullpath]\\util\\not.js [fullpath]\\operators\\partition.js [fullpath]\\operators\\pluck.js [fullpath]\\operators\\publish.js [fullpath]\\operators\\publishBehavior.js [fullpath]\\AsyncSubject.js [fullpath]\\operators\\publishLast.js [fullpath]\\operators\\publishReplay.js [fullpath]\\observable\\race.js [fullpath]\\operators\\race.js [fullpath]\\operators\\repeat.js [fullpath]\\operators\\repeatWhen.js [fullpath]\\operators\\retry.js [fullpath]\\operators\\retryWhen.js [fullpath]\\operators\\sample.js [fullpath]\\operators\\sampleTime.js [fullpath]\\operators\\sequenceEqual.js [fullpath]\\operators\\shareReplay.js [fullpath]\\operators\\single.js [fullpath]\\operators\\skipLast.js [fullpath]\\operators\\skipUntil.js [fullpath]\\operators\\skipWhile.js [fullpath]\\operators\\switchAll.js [fullpath]\\operators\\switchMapTo.js [fullpath]\\operators\\take.js [fullpath]\\operators\\takeWhile.js [fullpath]\\operators\\throttle.js [fullpath]\\operators\\throttleTime.js [fullpath]\\operators\\timeInterval.js [fullpath]\\util\\TimeoutError.js [fullpath]\\operators\\timeout.js [fullpath]\\operators\\timeoutWith.js [fullpath]\\operators\\timestamp.js [fullpath]\\operators\\toArray.js [fullpath]\\operators\\window.js [fullpath]\\operators\\windowCount.js [fullpath]\\operators\\windowTime.js [fullpath]\\operators\\windowToggle.js [fullpath]\\operators\\windowWhen.js [fullpath]\\operators\\withLatestFrom.js [fullpath]\\operators\\zip.js [fullpath]\\operators\\zipAll.js [fullpath]\\operators\\index.js",
"module": "./node_modules/rxjs/_esm5/operators/index.js + 77 modules",
"moduleName": "./node_modules/rxjs/_esm5/operators/index.js + 77 modules",
"type": "harmony import",
"userRequest": "./pairwise",
"loc": "54:0-38"

Hmm interesting. Does the modules listed in the moduleIdentifier appear anywhere else in the stats.json?

Closing due to inactivity. Feel free to comment if you or anyone else is willing to dig deeper or create a proof of concept.

webpack 4 has added additional information to the stats object and we can display an approximation of module sizes for webpack 4 ๐ŸŽ‰ #158

For others having problems getting good results with angular (5.2.10) and angualr-cli(1.7.4), this is what helped to generate stats that resulted in pretty good outputs:

yarn build --stats-json --build-optimizer=false --vendor-chunk=true --named-chunks=true --output-hashing=media

@pfeigl seeing as you have knowledge on how webpack-bundle-analyzer works with Angular CLI, would you be able to contribute a tiny "Troubleshooting" section for Angular CLI in the README?

I'd really need the help, as I haven't used Angular CLI and there's lots of people having with trouble with it and this tool!

Sure, in the end I just used https://github.com/angular/angular-cli/wiki/build#bundling--tree-shaking to find out how to disable all the build optimization and the usage of ModuleConcatenationPlugin.

I'll send a PR for the README

(Also Angular6 and the respecitve CLI will bring webpack4, which will probably solve the problems over time automatically)

I wasn't using ModuleConcatenationPlugin, but found i had to set
optimization.concatenateModules = false
https://webpack.js.org/configuration/optimization/#optimizationconcatenatemodules