0

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.

Taxel
  • 3,859
  • 1
  • 18
  • 40

1 Answers1

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,
  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.

Grzegorz T.
  • 3,903
  • 2
  • 11
  • 24
  • Thanks for your answer, but after some trying out I could not get it to work. I upgraded to Webpack 5, since the import and dependOn always threw an error in 4, created a new (empty) polyfills file, added it as dependOn in my js entries and imported it in some of my files. I then looked at the file sizes and for example two files in one folder stay the same size when importing the polyfills file, yet the polyfills file stays at 202 bytes which leads me to believe that it does not work. Any pointers on how I could debug this? – Taxel Feb 16 '21 at 09:01
  • Take a look at it [code-splitting](https://webpack.js.org/guides/code-splitting/) and especially at this `SplitChunksPlugin` solution – Grzegorz T. Feb 16 '21 at 10:58
  • I made an update of the example above. Good luck. – Grzegorz T. Feb 16 '21 at 11:45
  • Okay so I tried using `useBuiltIns: "entry"` and it looked like it built more polyfills into the bundles than `"usage"`, so I stayed with `"entry". The `optimization` part was the real answer that extracts the polyfills into a single file. Since the common entry I added is empty this is just the solution to get rid of the module wrapper code in each module, have I understood that correctly? Thanks for all the help so far, my bundles are now considerably smaller. – Taxel Feb 17 '21 at 08:07
  • As for entry vs usage, this page describes well [confused-about-usebuiltins-option-of-babel-preset-env-using-browserslist-integ](https://stackoverflow.com/questions/52625979/confused-about-usebuiltins-option-of-babel-preset-env-using-browserslist-integ) splitChunks extracts common libraries and puts them into a separate file. For example, if you use `async/await` `corejs` pollyfill would normally append them to all input files, this solution extracts them into one file. – Grzegorz T. Feb 17 '21 at 11:06