How to split dynamically by directories with Webpack/SplitChunks plugin?

2.1k Views Asked by At

I'm trying to split my React code (created with create-react-app) with the splitChunks plugin in the following way :

I have following components (JSX) structure :

  • services
    • serviceA
      • ComponentA1
      • ComponentA2
      • subFolder
        • ComponentA3
      • ...
    • serviceB
      • ComponentB1
      • ComponentB2
      • ...
    • serviceC
      • ComponentB1
      • ComponentB2
      • ...
    • ...

and I want to have following output (build) :

  • static/js
    • serviceA
      • serviceA.bundle.chunkhash.js
    • serviceB
      • serviceB.bundle.chunkhash.js
    • serviceC
      • serviceC.bundle.chunkhash.js

(other runtimes / mains are at the root of /static/js)

Another restriction is that components are loaded dynamically with

const Component = lazy(() => import(componentPath));
...
<Suspense fallback={..}>Component</suspense>

"componentPath" is determined on the fly (when a user clicks on an icon then it opens a given service).

The reason for this is that I want to include each bundle into a separate Docker image running the backend. Then each Docker image is reachable thanks to Application routing :

static/js/serviceA/  ==> js served by Docker container running service A
static/js/serviceB/  ==> js served by Docker container running service B
static/js/serviceC/  ==> js served by Docker container running service C

So far, I'v tried to:

  • set the output.chunkFilename to [name]/[name].[chunkhash].js
  • use the webpackChunkName with [name] and [request]:

    • [name] doesn't seem to work (got just litterally "[name]" as part of my directory name).

    • [request] flattens the name of the directories:

      serviceA-ComponentA1 serviceA-ComponentA2 serviceA-subFolder-ComponentA3 serviceB-componentB1 serviceB-componentB2 ...

Then I tried to use the splitChunks plugin with following :

  splitChunks: {
    chunks: 'all',
    name: function(module) {
      let serviceName = module.rawRequest ? module.rawRequest : 'default';
      serviceName = serviceName.replace('../', '').replace('./', '');
      serviceName = serviceName.split('/')[0];
      return serviceName;
    },
    cacheGroups: {
      vendors: {
        test: /[\\/]node_modules[\\/]/,
        priority: -10
      },
      default: {
        minChunks: 2,
        priority: -20,
        reuseExistingChunk: true
      },
      serviceA: {
        test: /[\\/]serviceA[\\/]/,
        priority: -10
      },
      serviceB: {
        test: /[\\/]serviceB[\\/]/,
        priority: -10
      },
      serviceC: {
        test: /[\\/]serviceC[\\/]/,
        priority: -10
      },              
    }
  },

This approach looks like working as all my services are in their own directories. But I still have some additional directories as numbers (bundle ID probably) that I would have expect to be rather included into the default.

So the question is : is my approach correct ?

1

There are 1 best solutions below

0
On

I'm not sure if the following option would work for you. I had a similar problem, where I needed different folders to be outputed on different bundles.

In my case, I started with the glob solution, suggested here. Then, knowing that I needed an array of inputs for each desired output, I came up with this:

const path = require('path');
const glob = require('glob');

const plugins = [...];

module.exports = {
    entry: glob.sync('./src/**/*.js').reduce((acc, item) => {
        const path = item.split('/');
        path.pop();
        const rootFolder = path[2] ? `${path[0]}/${path[2]}` : path[0];
        if (acc[rootFolder]) {
            acc[rootFolder].push(item);
        } else {
            acc[rootFolder] = [item];
        }
        return acc;
    }, {}),
    output: {
        filename: '[name]/main.js',
        path: path.resolve(__dirname, 'dist'),
    },
  module: { ... },
    plugins,
};

This is a simplified version of my config and it could probably be improved, but it works fine for my needs. :)

More info on glob library: https://github.com/isaacs/node-glob