Require a set of modules based on a DefinePlugin constant

I'm trying to build a web application using Webpack, and I've hit a bit of a wall with a particular part of the design - hopefully someone on here will have experience doing similar stuff (or know enough to tell me I'm doing it completely wrong)!

Basically, we have an Angular dashboard app that consists of a 'shell' (the common UI, session management services, etc) and a set of modules (self-contained sections of the app that can be plugged into the shell). The tricky bit is that this app is going to be distributed to multiple customers, each of whom will want a different set of modules and will need slightly different configuration options.

I've made some headway with this so far using DefinePlugin to load in a JSON config file as a constant at build-time:

const envConfig = require(`./config/${yargs.argv.env || "dev"}`);

...

new webpack.DefinePlugin({        
    __ENV__: Object.keys(envConfig).reduce((obj, key) => {
        obj[key] = JSON.stringify(envConfig[key]);
        return obj;
    }, {})
})

I then tried to use a dynamic require to include just the specified modules, like so:

__ENV__.modules.map((id) => require(`./modules/${id}/index.ts`).default)

This sort of worked - the right modules get loaded, but Webpack doesn't pick up on the fact that __ENV__ is never going to change at runtime, so the build file itself includes all of the modules, the disabled ones included. Obviously this would ramp up the file size unnecessarily for clients that just want a single module, so I'm not really happy with it.

So, to reduce it down to a simple question - is there a simple way to load a specific set of modules based on a DefinePlugin constant (or is there an alternative way to achieve what I'm trying to achieve)? I feel like there's something obvious I must be missing.

Answers:

Answer

You could set up a resolve.alias and configure that per customer. That would give you the kind of setup you want.

Answer

I came up with a decent(?) solution to this problem after taking bebraw's advice - thought I'd post it as an additional answer in case anyone runs into the same issue or wants to implement something similar. I'm using TypeScript, but the same approach would probably work fine in JavaScript, with or without ES6.

Basically, I now have a directory called config in my src which contains a subdirectory for each customer, like so:

config
??? dev
??? cust1
??? cust2

Each one of these contains an info.ts file, which exports constants such as REST endpoint URLs:

export default {
    name: "Company Name",
    server: "http://localhost:8810",
    service: "ServiceName"
}

And a modules.ts file, which exports the array of modules to pass into my app's bootstrapper function:

import util from "../../modules/util";
import session from "../../modules/session";
import shell from "../../modules/shell";
import stock from "../../modules/stock";

export default [
    util,
    session,
    shell,
    stock,
];

These have to be separate - my initial implementation had one .ts file for each config, but this meant I ended up with a circular dependency whenever I tried to import the constants from within the modules.

Then I just added a resolve.alias based on a command line argument:

alias: {
    "config": path.join(__dirname, `src/config/${yargs.argv.env || "dev"}/`)
}

Now I can switch out what config/info and config/modules refer to just by calling:

webpack --env dev
webpack --env cust1
webpack --env cust2

I still need to see how this works out in practice, and maybe refine the interface for the different exports, but as a starting point I'm liking it so far!

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.