DEV Community

Brandon Benefield
Brandon Benefield

Posted on • Updated on

Webpack: How to Create Dynamic Entry & Output paths

TLDR;

For those that don't want to read and just want the config file then here you go. All you need to do is change the path string passed to the glob.sync(...) method to match your needs.

const glob = require('glob')
const path = require('path')

module.exports = {
    entry: glob.sync('./Projects/**/index.js').reduce((acc, path) => {
        const entry = path.replace('/index.js', '')
        acc[entry] = path
        return acc
    }, {}),

    output: {
        filename: './[name]/main.js',
        path: path.resolve(__dirname)
    }
}
Enter fullscreen mode Exit fullscreen mode

The idea behind creating dynamic entry and output paths with Webpack might seem confusing. When I was trying to figure out the situation and began to ask around, I received a lot of "Why do you need that?".

The "why" is pretty simple. I've found myself in a situation where I'm writing a lot of one-off scripts, each doing something of their own and typically have nothing to do with another script. Because of this, I had some very niche needs for my project. Mainly, having the ability to build and minify each script into their own subdirectory.

The real magic of this Webpack config file is in the entry property. The entry property is capable of taking:

  • string
entry: './my/path/index.js'
Enter fullscreen mode Exit fullscreen mode
  • string[]
entry: ['./my/first/path/index.js', './my/second/path/index.js']
Enter fullscreen mode Exit fullscreen mode
  • object
entry: {
    'my/first/path': './my/first/path/index.js',
    'my/second/path': './my/second/path/index.js'
}
Enter fullscreen mode Exit fullscreen mode
  • Function => string | string [] | object
entry: () => './my/path/index.js'
Enter fullscreen mode Exit fullscreen mode

Now, let's dive into what's actually happening in this config file. Make sure to read the comments as I'm going to attempt to explain everything going on in the code itself as comments.

webpack.config.js

/**
 * When passed a string, Glob will attempt to find each file that matches the
 * path given and return each path to the file as string[]
 */
const glob = require('glob')

/**
 * The Path API will be used to get the absolute path to the directory where we
 * plan to run Webpack
 */
const path = require('path')

module.exports = {
    /**
     * Pass Glob a relative path to each of our entry points
     * We will have different subdirectories inside of the Project directory so
     * we need to replace any of the directory names with a wildcard, **, which 
     * will recursively match any combination of directory names inside of any
     * number of subdirectories until it finds the index.js entry.
     * Then we use the Array.prototype.reduce method to iterate through the array
     * and return an object containing a path to each of our entry files
     * (index.js)
     */
    entry: glob.sync('./Projects/**/index.js').reduce((acc, path) => {
        /**
         * The "[name]" placeholder in the "output" property will be replaced
         * with each key name in our "entry" object. We need to make sure the
         * keys are a path to the "index.js" file but without the actual file
         * name. This is why we replace the file name, "index.js", with a string
         */
        const entry = path.replace('/index.js', '')
        /**
         * Here we start building our object by placing the "entry" variable from
         * the previous line as a key and the entire path including the file name
         * as the value
         */
        acc[entry] = path
        return acc
    }, {}),

    /**
     * The "output" property is what our build files will be named and where the
     * build file will be placed
     */
    output: {
        /**
         * Again, the "[name]" place holder will be replaced with each key in our
         * "entry" object and will name the build file "main.js"
         */
        filename: './[name]/main.js',
        /**         
         * We need to provide an absolute path to the root of our project and
         * thats exactly what this line is doing
         */
        path: path.resolve(__dirname)
    }
}
Enter fullscreen mode Exit fullscreen mode

Result

This is all you need to create dynamic entry & output paths with Webpack. The question now is what kind of project architecture does this example work with?

> node_modules
  package.json
  package-lock.json
> Projects
  ---- > Proj_1
         ---- index.js
  ---- > Proj_2
         ---- index.js
  webpack.config.js
Enter fullscreen mode Exit fullscreen mode

After running our Webpack and building our files our project will then look like

> node_modules
  package.json
  package-lock.json
> Projects
  ---- > Proj_1
         ---- index.js
         ---- main.js  // new build file
  ---- > Proj_2
         ---- index.js
         ---- main.js  // new build file
  webpack.config.js
Enter fullscreen mode Exit fullscreen mode

Top comments (5)

Collapse
 
drehere profile image
Andrey

Thanks this is awesome! Helped me with my multiple config setup for Gutenberg development

Collapse
 
dubst3pp4 profile image
Marc Hanisch

Great explanation, work also great with the ts-loader. Thank you very much!

Collapse
 
rjbullock profile image
Robert Bullock

This was really useful. Helped me setup my monorepo! :-)

Collapse
 
rsanath profile image
Ram Sanath Kumar

sweet! ❤️

Collapse
 
joacimnilsson profile image
Joacim Nilsson

Kudos!