Import an array of dynamic modules with Webpack that are known during compilation

For my project, I have the requirement to import specific modules whose actual paths are only known during compile time.

Imagine having components/A.js, components/B.js, and components/C.js. In my App.js, I need to include a subset of these modules that will only be determined at webpack's compile-time, perhaps from a JSON file.

My initial attempt:

//App.js
compileData = await getCompileDataSomehow()

import("./components/" + compileData.component + ".js")
   .then( /* Do something with the component */);

While this approach worked, webpack created a chunk for every components/*.js file resulting in unnecessary network roundtrips. To avoid this overhead, I tried providing an array of components but ran into issues since require([...]) is considered dynamic.

What I eventually came up with:

//App.js
import("./components/ALL.js") //this file doesn't actually exist
//webpack.config.js

const fs = require('fs')

const componentsToInclude = ["A", "B"] //read from a json


fs.writeFileSync('./src/components/ALL.js',
  `
export default [
    ${componentsToInclude.map(comp => `require("./${comp}.js")` )}
].map(c => c.default);

`)

module.exports = { 
// webpack config 
}

This method works, however, it is not the most elegant solution and can lead to potential bugs. Does anyone have a better approach to handle this scenario?

Answer №1

For the purpose of this response, let's assume there is a primary App.js file and a directory named components which includes files like A.js, B.js, C.js, D.js, etc. The requirement is to include only A.js and B.js in the bundle, while excluding all other files at compile time.

Exploring require.context

Webpack offers a specific method called require.context to handle a group of dependencies. It requires four parameters: the path to the directory containing files to be included, a boolean value to indicate whether subdirectories should be considered, a regular expression for filtering files, and a loading mode directive.

To bundle all components, we would use the following code:

// App.js
const req = require.context('./components')

(While this goes beyond the initial query, you can then utilize the exported functions from these files using the req variable. For instance, if each file exports a default function, you can execute them as follows:

const req = require.context('./components')
req.keys().forEach( key => {
    req( key )?.default()
} )

Refer to this question and its answers for more on requiring with require.context)


However, if we need to load only A.js and B.js, we must apply a filter with a regular expression to require.context. Assuming that we know the required files upfront, we can hardcode the regular expression like so:

// App.js
const req = require.context('./components', true, /(A|B)\.js$/)

The /(A|B)\.js$/ regex filters input to include either A.js or B.js. It can be expanded to accommodate more files such as /(A|B|C|D)\.js$/ or to target specific subdirectories. Webpack evaluates this regex at compile time, ensuring only matching files are bundled.

But the challenge arises when the filtering criteria are unknown during compilation due to dynamically generated filenames.

Utilizing webpack.DefinePlugin

By leveraging webpack.DefinePlugin, we can set global variables in our webpack.config.js known at compile time. These variables become statically accessible across all files by Webpack. For example, we can assign our regex pattern to a global variable like __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 values defined via DefinePlugin must be stringified, hence raw RegExp objects are not supported. All backslashes must be properly escaped.

If the filenames are stored in an array, say fetched from a JSON file, we can dynamically construct the required regex in webpack.config.js. Here's a simple approach - concatenate filenames using the pipe symbol as a separator:

// webpack.config.js
const fileNames = ["A", "B"];

const importRegex = `/(${ fileNames.join("|") })\\.js$/`; // will return '/(A|B)\\.js$/'

module.exports = { ...

Bringing it 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__)

Lastly, Typescript users need to declare a type definition for __IMPORT_REGEX__.

// interface.d.ts
declare global {
    var __IMPORT_REGEX__: RegExp
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Error message: Electron is unable to read properties of undefined, specifically the property 'receive'. Furthermore, the IPC is unable to receive arguments that were sent through an HTML iframe

I am currently working on passing light mode and language data from ipcMain to ipcRenderer via my preload script: Preload.js: const { contextBridge, ipcRenderer } = require("electron"); const ipc = { render: { send: ["mainMenuUpdate& ...

Unique custom CSS and meta tag options for enhancing iPad/iPhone user experience

Currently, I am developing a web application that integrates Extjs components, PHP, and MySQL. My main goal is to ensure that my application looks correct on the iPad. Are there any specific CSS rules or meta tags that I should be implementing for optima ...

When errors occur while printing HTML through an Ajax request, it can hinder the functionality of other JavaScript code

I recently conducted an interesting experiment on my website. The concept involved sending an AJAX request to a PHP file, which then retrieved a random website by using various random words for search queries on Google. The retrieved website content was th ...

Do you know the method to make a Youtube iframe go fullscreen?

I am encountering an issue with developing an iframe where I am unable to make it go fullscreen from within the iframe. Fullscreen API works when both the iframe and hosting website are on the same domain, as well as triggering fullscreen from outside the ...

Obtaining a return value from a function that involves a series of chained Ajax requests in jQuery

I'm facing an issue with my function that involves chained Ajax requests. Function A and B are both Ajax requests, where A runs first and B runs after A returns its data. The problem arises when Function C executes Function B. Upon execution of Funct ...

Obtaining solely the words found within HTML documents

In my Python 2.7 project, I have a folder containing multiple HTML pages that I need to extract only words from. My current process involves opening the HTML file, using the Beautiful Soup library to extract text, and then writing it to a new file. However ...

Transferring cookies across subdomains

I am facing an issue with an ajax request going from one subdomain to another, for example from sub1.example.com to sub2.example.com. Despite having a cookie set for all domains (cookie domain='.example.com'), the cookie is not being sent to the ...

I am trying to include the Css Baseline from @mui/material in my project. However, even though it is present in my JSON file, I am encountering an error stating that '@mui/material' needs to be included in the project

Struggling to import Css Baseline from @mui/material, it's listed in my json but I keep getting an error saying '@mui/material' should be included in the project's dependencies. I've been stuck on this issue for a while now! { &q ...

Issue: The element [undefined] is not recognized as a valid child of the <Routes> component. Only <Route> or <React.Fragment> components are allowed as children of the <Routes

I am facing an issue while trying to upgrade react-router-dom from v5 to v6. The error message I receive is as follows: Error: [undefined] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragm ...

Incorporating a remote PHP file into your website using JavaScript

Is it feasible to utilize JS (jQuery) for executing a $.post from any website on any domain to a script on my server? This query stems from my reluctance to disclose my PHP files to clients (and avoid spending money on ionCube or similar solutions). By se ...

Is there an equivalent to Tomcat specifically designed for Node.js applications?

Looking for an application server that offers an administration interface to deploy node.js applications, access log files, and manage running applications with options to start, stop, restart, and monitor? ...

Issue with accessing form in Angular 6 Reactive forms for custom validator functionality

I am facing an issue with creating a password validation for reactive forms in Angular. Every time I try to verify the password, I get a “Cannot read property 'get' of undefined” error in the console. I have tried different methods to access ...

What is the best way to change function.bind(this) to an arrow function in a react native application?

I am attempting to convert my function into an arrow function, but I keep encountering an error of undefined when passing props. <TextInput style={styles.input} value={formState.inputValues.title} onChangeText={textCh ...

Tips for evaluating an array of objects in JavaScript

Welcome to the world of coding! Here's a scenario with an array: [ { "question1": "Apple", "question2": 5, "question3": "Item 1" }, { "question1": ...

Ways to accurately determine the size of an array

My issue revolves around an array of objects. When I log the array, everything appears as expected. However, when I use the .length function, it inexplicably returns a value of 0. Check out my code snippet: async fetchTicketType(updatedTicket) { awai ...

Updating a behavior object array in Angular 5 by appending data to the end

After creating a service to share data across my entire application, I'm wondering if it's possible to append new data to an array within the userDataSource. Here is how the service looks: user.service userDataSource = BehaviorSubject<Array& ...

Unable to assign value to Ref data property in Vue3 due to undefined item

Recently, I've been utilizing Vue3 and Nuxt3 to work on a project. My main task involves extracting the :id parameter from the URL and checking if it matches an ID in a JSON file. If there is a match, I update a reference data point called 'exist ...

Utilize node.js on your local machine and leverage gulp to monitor any modifications

I recently copied a repository from https://github.com/willianjusten/bootstrap-boilerplate and followed these steps. git clone git://github.com/willianjusten/bootstrap-boilerplate.git new_project cd bootstrap-boilerplate npm install gulp The gulp comman ...

Having trouble getting Laravel Full Calendar to function properly with a JQuery and Bootstrap theme

Using the Laravel full calendar package maddhatter/laravel-fullcalendar, I am facing an issue where the package is not recognizing my theme's jQuery, Bootstrap, and Moment. I have included all these in the master blade and extended it in this blade. ...

Dragging items in the horizontal list of Knockout-Sortable causes them to be pushed vertically

For my application development using knockout.js, I am implementing knockout-sortable to create drag-and-drop sortable lists. The setup involves a vertical list with each item containing a horizontal list. While the vertical lists are functioning properly, ...