loading...

Webpack: How to Create Dynamic Entry & Output paths

bbenefield89 profile image Brandon Benefield Updated on ・3 min read

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)
    }
}

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'
  • string[]
entry: ['./my/first/path/index.js', './my/second/path/index.js']
  • object
entry: {
    'my/first/path': './my/first/path/index.js',
    'my/second/path': './my/second/path/index.js'
}
  • Function => string | string [] | object
entry: () => './my/path/index.js'

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)
    }
}

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

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

Discussion

pic
Editor guide