How can I dynamically output JavaScript files individually with Webpack based on a folder?

59 views Asked by At

I am putting together a new webpack build for my project and it's actually working pretty well. I have one problem that I'd like some help on if anyone has an idea. I have to retype all of this from my work computer so please forgive any typos.

Package.json

{
    "name": "test_webpack",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    scripts: {
        "build": "webpack --config webpack.production.js"
        "dev": "webpack serve --config webpack.development.js"
    },
    "devDependencies": {
        "@babel/core": "^7.22.9",
        "@babel/preset-env": "^7.22.9",
        "babel-loader": "^9.1.3",
        "css-loader": "^6.8.1",
        "html-webpack-plugin": "^5.5.3",
        "mini-css-extract-plugin": "^2.7.6",
        "sass": "^1.64.1",
        "sass-loader": "^13.3.2",
        "style-loader": "^3.3.3",
        "webpack": "^5.88.2",
        "webpack-cli": "^5.1.4",
        "webpack-dev-server": "^4.15.1"
    }
}

webpack.production.js

const path = reqire('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    mode: "production",
    entry: {
        "script.min": path.resolve(_dirname, 'src/js/index.js'),
        "search": path.resolve(_dirname, 'src/js/functional/modules.js'),
        "modules/carousel": path.resolve(_dirname, 'src/js/modules/carousel.js'),
        "modules/handleEmoji": path.resolve(_dirname, 'src/js/modules/handleEmoji.js'),
        "modules/modalAccordion": path.resolve(_dirname, 'src/js/modules/modalAccordion.js'),
        "modules/sliders": path.resolve(_dirname, 'src/js/modules/sliders.js'),
        "modules/tourVideo": path.resolve(_dirname, 'src/js/modules/tourVideo.js'),
        "modules/vectorSwap": path.resolve(_dirname, 'src/js/modules/vectorSwap.js')
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js',
        clean: true,
        assetModuleFilename: '[name].[ext]'
    },
    module: {
        rules: [
            {
                est:/\.scss$/i,
                use: [
                    MiniCssExtractPlugin.loader, 
                    {
                        loader:'css-loader',
                        options: {url: false}
                    },
                    {loader: 'sass-loader'}
                ]
            },
            {
                test:/\.js$/,
                exclude:/node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env']
                    }
                }
            },
            {
                test:/\.(png|svg|jpg|jpeg|gif)$/i,
                type: 'asset/resource'
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: "shared.min.css"
       })
    ]
}

question Is there a way I can replace this...with something dynamic?:

        "modules/carousel": path.resolve(_dirname, 'src/js/modules/carousel.js'),
        "modules/handleEmoji": path.resolve(_dirname, 'src/js/modules/handleEmoji.js'),
        "modules/modalAccordion": path.resolve(_dirname, 'src/js/modules/modalAccordion.js'),
        "modules/sliders": path.resolve(_dirname, 'src/js/modules/sliders.js'),
        "modules/tourVideo": path.resolve(_dirname, 'src/js/modules/tourVideo.js'),
        "modules/vectorSwap": path.resolve(_dirname, 'src/js/modules/vectorSwap.js')

That way every file in my modules folder will individually be compiled and output. I figured out how to bundle them all together, but I need to keep them separate. For now every time we add a module, we have to add it to the webpack configuration files. Right now it outputs to a folder "modules" in the "dist" folder.

2

There are 2 answers

2
Andrei Gătej On

It seems the entry option can also take in a function that returns a promise:

{
    /**
     * The entry point(s) of the compilation.
     */
    entry: EntryNormalized;
    /**
}
/**
 * A Function returning a Promise resolving to a normalized entry.
 */
export type EntryDynamicNormalized = () => Promise<EntryStaticNormalized>;
/**
 * The entry point(s) of the compilation.
 */
export type EntryNormalized = EntryDynamicNormalized | EntryStaticNormalized;

In that function, you could read the files in the modules directory and generate the entry object accordingly.

1
murtuza hussain On

You can create an array of module names and then use Array.reduce() to create an object with the entry points.

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const modules = [
  'carousel',
  'handleEmoji',
  'modalAccordion',
  'sliders',
  'tourVideo',
  'vectorSwap',
];

const entryPoints = modules.reduce((entries, module) => {
  entries[`modules/${module}`] = path.resolve(__dirname, `src/js/modules/${module}.js`);
  return entries;
}, {});

module.exports = {
  mode: 'production',
  entry: {
    'script.min': path.resolve(__dirname, 'src/js/index.js'),
    'search': path.resolve(__dirname, 'src/js/functional/modules.js'),
    ...entryPoints, // Spread the dynamically generated entry points
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
    clean: true,
    assetModuleFilename: '[name].[ext]',
  },
  // Rest of the configuration remains unchanged
};

The resulting entryPoints object will look like:

{
  "modules/carousel": "/full/path/to/src/js/modules/carousel.js",
  "modules/handleEmoji": "/full/path/to/src/js/modules/handleEmoji.js",
  "modules/modalAccordion": "/full/path/to/src/js/modules/modalAccordion.js",
  "modules/sliders": "/full/path/to/src/js/modules/sliders.js",
  "modules/tourVideo": "/full/path/to/src/js/modules/tourVideo.js",
  "modules/vectorSwap": "/full/path/to/src/js/modules/vectorSwap.js"
}

This way, you don't have to repeat the same entry points manually, and the build configuration becomes more scalable.