Search code examples
webpacklaravel-mixcode-splitting-async

In Laravel Mix, is it possible to extract all vendors except a list of packages?


Normally in Laravel Mix, we can extract all vendors automatically using mix.extract() or extract a list of vendors using mix.extract(['vue','lodash','axios']). Is there a way to extract all vendors except a few?

For example, I load Pusher, Echo, Chart.js, etc. when they're needed importing dynamic chunks. However, they're still getting added to my vendor.js file.

Also, when extracting a list of specific vendors, I end up with about 20 extra chunks due to sharing common code with names like vendors~js/mychunk1~js/mychunk2~js/mychunk3.


Solution

  • By inserting a console.log in the Extract.js component inside Laravel Mix, I've found that in my current settings the plugin was just inserting these lines in the optimization field of the Webpack Configuration.

    optimization: {
      runtimeChunk: { name: '/js/manifest' },
      splitChunks: {
        cacheGroups: {},
        chunks: 'all',
        name: '/js/vendor',
      },
    },
    

    In order to filter what was going inside the vendor file I had to edit splitChunks like that:

    splitChunks: {
      cacheGroups: {
        vendor: {
          // moved name and chunks here
          name: 'js/vendor',
          chunks: 'all',
            // you can pass a RegExp to test, but also a function
            test(module/* , chunk */) {
              if (module.context) {
                // node_modules are needed
                const isNodeModule = module.context.includes('node_modules');
                // but only specific node_modules
                const nodesToBundle = [
                 'vue',
                 'lodash',
                 'axios',
                ].some(str => module.context.includes(str));
                if (isNodeModule && nodesToBundle) {
                  return true;
                }
             }
             return false;
           },
         }
      },
      // removed name and chunks from here  
      // chunks: 'all',
      // name: '/js/vendor',
    },
    

    After those changes I still had the weird naming you are experiencing, but thanks to this GitHub repo I discovered that I could remove this behavior by adding:

    default: false, // disable default groups
    vendors: false, // disable vendors group
    

    to the CacheGroups field.

    So my complete webpackConfig function argument was:

    .webpackConfig({
      output: {
        // with other settings
      },
      optimization: {
        runtimeChunk: { name: '/js/manifest' },
        splitChunks: {
          cacheGroups: {
            default: false,
            vendors: false,
            vendor: {
              name: 'js/vendor',
              chunks: 'all',
              // you can pass a RegExp to test, but also a function
              test(module/* , chunk */) {
                if (module.context) {
                  // node_modules are needed
                  const isNodeModule = module.context.includes('node_modules');
                  // but only specific node_modules
                  const nodesToBundle = [
                    'vue',
                    'lodash',
                    'axios',
                  ].some(str => module.context.includes(str));
                  if (isNodeModule && nodesToBundle) {
                    return true;
                  }
               }
               return false;
             },
           }
         },
       },
     }
    })
    

    Be sure to also remove the extract() call in you mix file.