Search code examples
gulpglobwebpack-4

Creating dynamically named outputs for wildcarded entry files


How can I keep folder structures and filenames?

The preservation of the wildcard **/*.js is important. I want to keep it dynamic and not add new components manually.

Folder structure

|-- src
    |-- components
        |-- lightbox
            |-- index.js
        |-- carousel
            |-- index.js
        |-- modal
            |-- index.js

Expected output of folders and files:
The folder structure and original filenames should be preserved!

|-- build
    |-- components
        |-- lightbox
            |-- index.js
        |-- carousel
            |-- index.js
        |-- modal
            |-- index.js   

With the current webpack config, I get the following result:
All components have been put together in a single .js file, which should not be.

|-- build
    |-- components
        |-- main.js     

My webpack config

entry: $.glob.sync('./src/components/**/*.js'),
mode: 'development',
output: {
  filename: '[name].js'
},
module: {
  rules: [{
    test: /\.js$/,
    exclude: /node_modules/,
    use: {
      loader: 'babel-loader',
    }
  }]
}

Solution

  • The webpack docs state that:

    Note this option is called filename but you are still allowed to use something like 'js/[name]/bundle.js' to create a folder structure.

    So if your webpack's output filename is dirA/fileA, it'll be output to

    |-- dist
        |-- dirA
             |-- fileA.js
    

    But you also state that

    The preservation of the wildcard **/*.js is important

    We can take advantage of the fact that you can name your file by passing an object to webpack's entry.

    I assume you're using node's glob (the $.glob.sync confused me a bit), whose output is an array of paths that match the glob patterns. Then, we simply need to modify this array into an object with this format:

    //from    
    ["./src/dirA/fileA.js", "./src/dirB/fileB.js"]
    // to
    { 
      "dirA/fileA": "./src/dirA/fileA.js",
      "dirB/fileB": "./src/dirB/fileB.js",
    }
    

    Here's a simple example:

    const glob = require('glob');
    
    const entry = glob.sync('./src/**/*.js').reduce((entry, path) => {
      const pathName = path.replace(/(\.\/src\/|\.js)/g, ''); // remove `./src/`, `.js`
      entry[pathName] = path;
      return entry;
    }, {});
    
    module.exports = {
      mode: 'development',
      entry,
      output: {
        filename: '[name].js'
      }
    }