In this answer I'll assume that, similar to the question, there's a main App.js
file, and a directory called components
containing files A.js
, B.js
, C.js
, D.js
, etc. Only A.js
, B.js
need to be bundled, and all other files should be excluded from the bundle, but this is only known at compile time.
require.context
Webpack provides a way to specify a group of dependencies with the custom, Webpack-specific require.context method. require.context
takes four arguments: a path to a directory with the files to include, a boolean to determine whether subdirectories are included, a regular expression to filter files, and a directive for the loading mode.
To bundle all files in the components directory we'd run
// App.js
const req = require.context('./components')
(This is beyond the scope of the original question, but you'd then use req
to run any exported functions from those files. For example, if each .js
file had a default
function exported, to run each default
function you'd do:
const req = require.context('./components')
req.keys().forEach( key => {
req( key )?.default()
} )
For more information in requiring with require.context
see this question and answers)
Since we want to load only A.js
and B.js
we must send a regular expression to require.context
to filter the files. Let's suppose that we knew we'd only need A.js
and B.js
when writing App.js
. We could filter the files with a hardcoded regular expression like so:
// App.js
const req = require.context('./components', true, /(A|B)\.js$/)
/(A|B)\.js$/
is a simple regular expression that returns true if the input is A.js
or B.js
. It could easily be extended to allow more files: /(A|B|C|D)\.js$/
, or to only search specific subdirectories. Webpack will evaluate the regular expression at compile time and only include matching files in the bundle.
However, the problem remains: how do we determine what the filter is when we only know the filenames at compile time?
webpack.DefinePlugin
Using webpack.DefinePlugin we can define global variables in our webpack.config.js
that are defined at compile time, and that Webpack makes statically available to every file. For example, we could put our regex from before into a global variable called __IMPORT_REGEX__
:
//webpack.config.js
module.exports = {
...
plugins: [
new webpack.DefinePlugin( {
__IMPORT_REGEX__: "/(A|B)\\.js$/"
} )
]
}
// App.js
const req = require.context('./components', true, __IMPORT_REGEX__)
Note that the value of variables created via DefinePlugin
must be stringified; that is, we can't supply a raw RegExp value. This means that any backslashes must be escaped.
If we have the names of our files in an array, perhaps previously read from a JSON file, we can dynamically build the needed regular expression in our webpack.config.js
. In this example it's a simple case of join
ing the array with the |
character as a separator:
// webpack.config.js
const fileNames = ["A", "B"];
const importRegex = `/(${ fileNames.join("|") })\\.js$/`; // will return '/(A|B)\\.js$/'
module.exports = { ...
Putting it all together
// webpack.config.js
const fileNames = ["A", "B"];
const importRegex = `/(${ fileNames.join("|") })\\.js$/`;
module.exports = {
...
plugins: [
new webpack.DefinePlugin( {
__IMPORT_REGEX__: importRegex
} )
]
}
// App.js
const req = require.context('./components', true, __IMPORT_REGEX__)
One final note: Typescript users will need to declare a global for __IMPORT_REGEX__
// interface.d.ts
declare global {
var __IMPORT_REGEX__: RegExp
}