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 1:

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.