Webpack 4 doesn’t support sharing modules from entries

Do you want to request a feature or report a bug?

Bug

What is the current behavior?

Webpack 4.5 cannot be configured to create JavaScript files that both: a) bundle shared modules and b) runs them.

If the current behavior is a bug, please provide the steps to reproduce.

The following markup shows the use case of a large app that has pages that assumes a babel-polyfilled environment and may depend on either React or jQuery or both. A page often has a corresponding Webpack entry that shouldn’t redundantly bundle babel-polyfill, React, jQuery, or the Webpack runtime.

<html>
  <body>
    <!-- Download and run babel-polyfill, fetch polyfill modules, and webpack runtime. -->
    <script src="environment.js"></script>
    <!-- An ES6 environment should polyfilled -->
    <script>console.assert('Map exists.', typeof Map === 'function');</script>
    <script>console.assert('Set exists.', typeof Set === 'function');</script>

    <!-- Download react, react-dom -->
    <script src="vendor-react.js"></script>

    <!-- Download and run jquery, and a module that sets it on window -->
    <script src="vendor-jquery.js"></script>
    <script>console.assert('jQuery exists.', typeof jQuery === 'function');</script>

    <!-- Download and run page-specific code that may import react or jquery
      without bundling react or jquery in page-a.js -->
    <script src="page-a.js"></script>
  </body>
</html>

What is the expected behavior?

Webpack 3 and it’s CommonsChunkPlugin supported this feature.

Please mention other relevant information such as the browser version, Node.js version, webpack version, and Operating System.

Webpack 4.5, Node 8.2, OS X 10.12.6.

Author: Fantashit

12 thoughts on “Webpack 4 doesn’t support sharing modules from entries

  1. I have a vendor bundle, and my own commons module. With libraryTarget: 'var' I used to configure variables in my commons module (it has an options variable within, which is exported for the modules importing common.js).

    Before 4.0, using CommonsChunkPlugin I could import the common.js module, then configure it from inlined code in my pages, and then use the options configured from the inline code in my modules. After 4.0, I can’t get it working. It seems as the options within the same common.js is two separate variables: one which is configured via the libraryTarget behavior from the inlined code, and another which is basically an the original state of the defined options variable in common.js, imported by the underlying modules.

    Reading the new configuration docs so far with SplitChunks, I could not find ways as to how to keep the ‘one parent’-‘many children’ relation, so that my modules would see the same inline configured common.js option variable everywhere.

    Am I missing something, or has this become completely unavailable with V4? Can someone enlighten me as to how I can achieve what was available with V3?

  2. It sounds like you are trying to synthetically make certain modules and things available for other bundles not created from the same compilation?

  3. @TheLarkInn When you say “available for other bundles not created from the same compilation?”

    Does that apply to this line in the original description?

    <script>console.assert('Map exists.', typeof Map === 'function');</script>

    If so, do you think that this use case is unreasonable or unrealistic?

    It seems to me that Webpack 4 assumes that it is responsible for the loading of all JavaScript on a site. While this may be true with code splits in a SPA, this is not always true for a site composed of html pages with script tags.

    The hueristics system to determine what commons files to generate is a nice feature for SPA, but it is not practical for an html page app with script tags, where each script tag must be deliberately added.

    I’m trying to avoid:

    • Bundling jQuery or React for pages that do not need it.
    • Downloading common modules more than once.
    • Downloading the Webpack runtime more than once.
    • Having common modules pulled into new, unexpected files (often named something like a~b~c.js).

    I’m trying to preserve:

    • My existing script tags throughout a large app.
    • The ability to include babel-polyfill for any JavaScript that runs after it.
    • The optimization of downloading React or jQuery once, in their own common script files, that are made available to other Webpack modules or to the global window.

    Is this something that Webpack 4 supports?

  4. This is actually blocking update to webpack 4 for us. Some background: we have a pretty large app (4500+ modules, ~400 entry points). App is a mix of legacy and modern, webpack-built JS code. Every page could be a mix of legacy and modern widgets. For this cases, we create a separate webpack entry point for every modern-inside-legacy widget. Some global initialization is then put into another init.js entry point:

    import 'babel-polyfill';
    import SomeLib from 'some-internal-library';
    
    SomeLib.configure({ ...options });

    This init.js is executed before widget entry points. Widgets expect 'some-internal-library' to be configured. This worked us for every webpack version since 0.x. On webpack 4 it broke, because it seems like every widget entry point now re-executes some-internal-module on the first import and configure call in init.js does not affect the rest.

    Is there something we can do to make it work?

  5. If I understand your problem correctly, you are trying to load multiple entry points on a single page. This should work if you create one runtimeChunk for all entry points.

    optimization: {
      runtimeChunk: 'single'
    }

    This creates a “runtime.js”, which you need to load as first script on the page. After that, you can load multiple entry points on your page.

  6. To expand on my last comment, by the way, there’s a slightly undocumented syntax for optimization.runtimeChunk: You can actually set it to

    optimization: {
      runtimeChunk: {
        name: 'shared',
      },
    }
    

    to have Webpack inject the runtime into that chunk; this interoperates with optimization.cacheGroups.***.name, so if you have a shared chunk of dependencies that you swear every page of yours will load, you can use the above configuration to have Webpack inject the runtime there, to avoid the extra runtime-only chunk.

  7. This is really a nightmare for me, I nearly spent around 30 hours to make it work somehow – and it’s still not working. I’m using rails webpacker and either the chunks are loaded multiple times or the JS code is not executing at all – even though I can see the “working” javascript include tags in the html. Until now webpack is absolutely killing my nerves & productivty 🙁

    Basically I would like to have 1 base js file (imports polyfill, jquery,..), and several other js entry files which are included if one of our subpages is opened (different react components, depending on which site is opened)).

  8. This issue is more than a year old, and gets extremely frustrating for many devs trying to use a singleton-like module usage.

    Webpack devs, is there not a solution in sight? As it is now, webpack 4 does not have an upgrade path for us.

Comments are closed.