ag-grid is proud to partner with webpack

ModuleConcatenationPlugin

In the past, one of webpack’s trade-offs when bundling was that each module in your bundle would be wrapped in individual function closures. These wrapper functions made it slower for your JavaScript to execute in the browser. In comparison, tools like Closure Compiler and RollupJS ‘hoist’ or concatenate the scope of all your modules into one closure and allow for your code to have a faster execution time in the browser.

This plugin will enable the same concatenation behavior in webpack.

new webpack.optimize.ModuleConcatenationPlugin()

This concatenation behavior is called “scope hoisting.”

Scope hoisting is specifically a feature made possible by ECMAScript Module syntax. Because of this webpack may fallback to normal bundling based on what kind of modules you are using, and other conditions.

Keep in mind that this plugin will only be applied to ES6 modules processed directly by webpack. When using a transpiler, you'll need to disable module processing (e.g. the modules option in Babel).

Optimization Bailouts

As the article explains, webpack attempts to achieve partial scope hoisting. It will merge modules into a single scope but cannot do so in every case. If webpack cannot merge a module, the two alternatives are Prevent and Root. Prevent means the module must be in its own scope. Root means a new module group will be created. The following conditions determine the outcome:

Condition
Outcome
Condition
Non ES6 Module
Outcome
Prevent
Condition
Imported By Non Import
Outcome
Root
Condition
Imported From Other Chunk
Outcome
Root
Condition
Imported By Multiple Other Module Groups
Outcome
Root
Condition
Imported With import()
Outcome
Root
Condition
Affected By ProvidePlugin Or Using module
Outcome
Prevent
Condition
HMR Accepted
Outcome
Root
Condition
Using eval()
Outcome
Prevent
Condition
In Multiple Chunks
Outcome
Prevent
Condition
export * from "cjs-module"
Outcome
Prevent

Module Grouping Algorithm

The following pseudo JavaScript explains the algorithm:

modules.forEach(module => {
  const group = new ModuleGroup({
    root: module
  });
  module.dependencies.forEach(dependency => {
    tryToAdd(group, dependency);
  });
  if (group.modules.length > 1) {
    orderedModules = topologicalSort(group.modules);
    concatenatedModule = new ConcatenatedModule(orderedModules);
    chunk.add(concatenatedModule);
    orderedModules.forEach(groupModule => {
      chunk.remove(groupModule);
    });
  }
});

function tryToAdd(group, module) {
  if (group.has(module)) {
    return true;
  }
  if (!hasPreconditions(module)) {
    return false;
  }
  const nextGroup = group;
  const result = module.dependents.reduce((check, dependent) => {
    return check && tryToAdd(nextGroup, dependent);
  }, true);
  if (!result) {
    return false;
  }
  module.dependencies.forEach(dependency => {
    tryToAdd(group, dependency);
  });
  group.merge(nextGroup);
  return true;
}

Debugging Optimization Bailouts

When using the webpack CLI, the --display-optimization-bailout flag will display bailout reasons. When using the webpack config, just add the following to the stats object:

{
  ...stats,
  // Examine all modules
  maxModules: Infinity,
  // Display bailout reasons
  optimizationBailout: true
}

Further Reading


Contributors