Webpack – build multiple entries but only include polyfills and module wrapper once

I’ve got the following webpack configuration that traverses multiple PHP packages, looks for .js and .scss files in their respective /resources directory, builds them and puts them into the /out/src directory of the module. This allows me to write modern ES6 belonging to a package without creating a webpack config for it. When in watch mode, it has to be restarted if I add a new file but otherwise it is working out great.

const getEntries = () => {
  let js = glob.sync("./vendor/myvendor/**/resources/**/!(node_modules)/**/*.js");
  let css = glob.sync("./vendor/myvendor/**/resources/**/!(node_modules)/**/*.scss");
  if (js.length === 0 && css.length === 0) {
    return "./noop.js";
  }
  let entries = {};
  for (let i = 0; i < js.length; i++) {
    const p = js[i];
    const outPath = p.replace("/resources/", "/out/src/");
    entries[outPath] = p;
  }
  for (let i = 0; i < css.length; i++) {
    const p = css[i];
    const outPath = p.replace("/resources/", "/out/src/").replace(".scss", "");
    entries[outPath] = p;
  }
  return entries;
};

module.exports = {
  mode: "production",
  entry: getEntries(),
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          // Compiles Sass to CSS
          MiniCssExtractPlugin.loader,
          "css-loader",
          "sass-loader",
        ],
      },
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: "babel-loader",
          options: {
            presets: [["@babel/preset-env", { debug: true, useBuiltIns: "usage", corejs: 3 }]],
            plugins: [["@babel/plugin-transform-runtime", { corejs: 3 }]],
          },
        },
      },
    ],
  },
  output: { path: __dirname, filename: "[name]" },
  plugins: [new MiniCssExtractPlugin()],
  watchOptions: {
    ignored: ["node_modules", "**/Tests/**"],
  },
};

There are some caveats regarding file size, however: I have to add polyfills with corejs so the generated code works in older browsers and it looks like webpack is adding the necessary polyfills per file, so many of them are included multiple times. Also, webpack is bundling each file with its module wrapper code so it can be executed on its own.

So here are my questions:

  • is it possible to build one js file with polyfills that is included in all pages so they are no longer included in each file but also keep the "usage" param so only used features are polyfilled?
  • can I make this central dependency also be the only one that includes the webpack module wrapper code to save even more on filesize? The other javascript files are included in PHP templates dynamically (and most scripts are only used on few pages) so building a huge chunked bundle containing everything is not a good option.

EDIT:
after trying out the answer below, the entry looks like this:

{
    './vendor/myVendor/myModule0/out/src/js/countdown.js': {
        import: './vendor/myVendor/myModule0/resources/js/countdown.js',
        dependOn: 'polyfills.min.js'
    },
    './vendor/myVendor/myModule0/out/src/js/paymentSelect.js': {
        import: './vendor/myVendor/myModule0/resources/js/paymentSelect.js',
        dependOn: 'polyfills.min.js'
    },
    './vendor/myVendor/myModule0/out/src/js/shippingAddressSelect.js': {
        import: './vendor/myVendor/myModule0/resources/js/shippingAddressSelect.js',
        dependOn: 'polyfills.min.js'
    },
    './vendor/myVendor/myModule0/out/src/js/modal.js': {
        import: './vendor/myVendor/myModule0/resources/js/modal.js',
        dependOn: 'polyfills.min.js'
    },
    './vendor/myVendor/oxid-factfinder-extends/out/src/js/custom.js': {
        import: './vendor/myVendor/oxid-factfinder-extends/resources/js/custom.js',
        dependOn: 'polyfills.min.js'
    },
    './vendor/myVendor/oxid-faq/out/src/js/faqForm.js': {
        import: './vendor/myVendor/oxid-faq/resources/js/faqForm.js', 
        dependOn: 'polyfills.min.js'
    },
    './vendor/myVendor/oxid-klarna-extends/out/src/js/payments_handler.js': {
        import: './vendor/myVendor/oxid-klarna-extends/resources/js/payments_handler.js',
        dependOn: 'polyfills.min.js'
    },
    './vendor/myVendor/oxid-mobile-menu/out/src/js/filter-drawer.js': {
        import: './vendor/myVendor/oxid-mobile-menu/resources/js/filter-drawer.js',
        dependOn: 'polyfills.min.js'
    },
    './vendor/myVendor/oxid-mobile-menu/out/src/js/mobile-menu.js': {
        import: './vendor/myVendor/oxid-mobile-menu/resources/js/mobile-menu.js',
        dependOn: 'polyfills.min.js'
    },
    './vendor/myVendor/oxid-storefinder/out/src/js/store_request.js': {
        import: './vendor/myVendor/oxid-storefinder/resources/js/store_request.js',
        dependOn: 'polyfills.min.js'
    },
    'polyfills.min.js': './polyfills.js'
}

(this is the return value of the modified getEntries function)

I added an empty polyfills.js and included it in the individual js files with import '../../../../../polyfills'; at the top of each, yet the polyfills file stays at 202 bytes of filesize and the individual modules don’t get smaller.

3 thoughts on “Webpack – build multiple entries but only include polyfills and module wrapper once”

  1. You can use one polyfill file that you can share files among others.

    entry: {
        index: {
          import: './sources/js/index.js',
          dependOn: 'share'
        },
        about: {
          import: './sources/js/about.js',
          dependOn: 'share'
        },
        contact: {
          import: './sources/js/contact.js'
        },
        share: './sources/js/module/share.js'
      },
    

    Take a look at my example webpack-boilerplate. Of course, this share file must be included in each file that is to use it.

    See also documentation entry-context

    ——– UPDATE ——–

    In part, it’s also my fault, if you ask for a webpack, please tell me what version it is 🙂

    1. Try reducing the size by changing seBuiltIns: "usage" to useBuiltIns: "entry"
    2. I see you don’t have an optimization section:
    optimization: {
      minimize: true,
      minimizer: [new TerserPlugin()],
      splitChunks: {
        cacheGroups: {
          commons: {
            test: /[\\/]node_modules[\\/]/,
            name: 'vendors',
            chunks: 'all',
          },
        },
      },
    }
    

    Just like that at the beginning. Unfortunately, this is a very large area and cannot be described in a few sentences. The best way is to go to github and analyze webpack-boilerplate and its documentation. That’s many hours of reading and testing.

    Reply

Leave a Comment