4

I have build_modules.json file, which contains information about modules should be required by WebPack, for example:

[
    'module.superman',
    'module.superwoman',
    'module.spiderman',
    'module.wolverine'
]

So, I need to require all those modules only once to compile everything to single bundle:

var _modules = require('./build_modules.json');

_modules.forEach(function(_module){
    require('./modules/' + _module);
});

Everything works pretty fine, but if I am having some other files in ./modules/ folder than listed in build_modules.json, all of them are included to bundle due to WebPack core mechanism, though, they are not required and will never be used in current build. I need to forcely exclude those excess files from bundle because, in praxis, my ./modules/ folder contains over 200 different modules and only modules that are in build_modules.json should be included to current bundle.

Maybe I can do this with some plugin, or maybe inside of webpack.config.js file to construct a list of needed modules and require them with node.js, then put all that stuff to WebPack as a precompiled context?

impulsgraw
  • 887
  • 3
  • 13
  • 34
  • What's the problem with the answer you already got from Pawel? – Louis Nov 13 '17 at 11:54
  • I still believe that there is a solution. That is very stupid, from the point of webpack, not to have any solution to create a context manually with a manual list of modules. May be there is some plugin or module, which allows to do that? – impulsgraw Nov 13 '17 at 16:17

2 Answers2

1

I had got similar problem. Webpack does not allow to create dynamic requires. It takes all modules before the code is executed. But you can use require.context to load necessary modules according to some regexp rules. You can read more here: webpack docs

You can name modules, that you want to load in some specific way, eg. module.* and use require.context("./modules/", false, /^module\..+$/). Then all modules that begin with module. will be required before the code execution.

I remember that I was struggling with that for a couple of hours and that was the best solution for me.

Paweł
  • 4,238
  • 4
  • 21
  • 40
1

In case you need just to execute your modules

You need to set entry property in webpack config. This property accepts array as value of files to include in bundle.

// webpack.config.js
const fs = require('fs');
const path = require('path');

const getModulePath = module => path.resolve(__dirname, './relative/path/to/modules/directory', module);

const fileContent = fs.readFileSync('./your.json');
const entries = JSON.parse(fileContent).map(getModulePath);

module.exports = {
  // all entries will be built during one build process
  // if you need you can split each entry to separate config file
  entry: {
    bundle1: entries, // build modules from JSON file to single bundle
    bundle2: [ './file1', './file2' ], // build static modules to single bundle
    bundle3: './sindle/file', // "classic" build
    all: [ ...entries, './file1', './file2', './sindle/file' ], // you can combine everything to 1 bundle
  },
};

In case you need to export something from your modules

Unfortunately webpack's entry as array won't help you because it will export only last file in array.

I didn't heard something about such plugins that can combine certan files and use it as entry point. But you can create such file by yourself.

// webpack.config.js
const fs = require('fs');
const path = require('path');

// saving tmp file location
const pathToCreatedFile = path.resolve(__dirname, './path/to/created/file');
// implement fn to get relative path to modules from tmp file
const getModulePath = module => path.relative(pathToCreatedFile, path.resolve(
  __dirname,
  './relative/path/to/modules/directory',
  module,
));

// read json
const fileContent = fs.readFileSync('./path/to/your.json');
// prepare modules for tmp file
const entries = JSON.parse(fileContent)
  .map(item => ({
    name: item.slice(7),
    relpath: getModulePath(item),
  }));

// here generate import directive for each module. I used `import * as modules_name from 'path';` format
// you can choose any
const importsString = entries.map(({ name, relpath }) => `import * as ${name} from '${relpath}';`).join('\n');
// here generate exports from tmp file. It contain of all imports
const exportsString = `export {${entries.map(({name}) => name).join(',\n')}};`;
// and here just concatenate imports ad exports
const content = [
  entries.map(({ name, relpath }) => `import * as ${name} from '${relpath}';`).join('\n'),
  `export {${entries.map(({name}) => name).join(',\n')}};`,
].join('\n');

// and save tmp file content to fs
fs.writeFileSync(pathToCreatedFile, content);

module.exports = {
  // and here use saved tmp file path as entry point
  entry: pathToCreatedFile,
};

After you import builded bundle it will return plain object with your modules. You can use Object.values or similar to get your dependencies as array.

And another aproach, you can format exportString to export array of imports.

I used import * as name from ... format to show you more common way to create file. If you don't have any default exports or you have only default export it's better to use export * from ... or export {default as name} from ... formats respectively.

  • You missed the _then put all that stuff to WebPack as a precompiled context?_ (especially, the _context_ keyword). I do not need a separate file with modules because I need to **require them all in a main file of my project**. To be more particular, saying `var marvel = [];` and then, in cycle, `marvel.push(require('./modules/' + _module));`. If following your suggestion, I can't require all the modules that way to the very one array `marvel`. They will unlikely be separated from each other, even though placed into a single final destination bundle. – impulsgraw Nov 18 '17 at 00:02