typescriptwebpack

Webpack compile all files in one folder into a file per folder


The title might seem confusing, but I have a structure a bit like this:

src
|-- project one
|-- project two

When I run npm run build I want webpack to take all the files in project one and bundle them together, then do the same for project two and put them into the dist folder, but keeping the folder structure:

dist
|-- project one
|-- project two

I have setup my webpack like this:

const glob = require("glob");
const path = require("path");

const entryFiles = glob
.sync("./src/**/*.ts")
.reduce((previousValue, currentValue, currentIndex, array) => {
    return typeof previousValue === "string"
    ? {
        [path.basename(previousValue, path.extname(previousValue))]:
            previousValue,
        [path.basename(currentValue, path.extname(currentValue))]:
            currentValue,
        }
    : {
        ...previousValue,
        [path.basename(currentValue, path.extname(currentValue))]:
            currentValue,
        };
});

module.exports = {
    entry: entryFiles,
    resolve: {
        extensions: [".ts"],
    },
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "[name].js",
    },
    module: {
        rules: [
        {
            test: /^(?!.*\.spec\.ts$).*\.ts$/,
            loader: "ts-loader",
            exclude: /node_modules/,
        },
        ],
    },
};

But that doesn't work, they don't bundle or keep the folder structure.


Solution

  • The key to achieve this lies in the entry object. Each key in your entry object should contain the desired target folder path. For example, in your case, you need the entry object to be like this:

    module.exports = {
      entry: {
        'project1/main': './src/project1/main.ts',
        'project2/main': './src/project2/main.ts',
      },
    
      output: {
        filename: '[name].bundle.js'
      }
    };
    
    

    This should maintain the required directory structure in the dist folder:

    dist
    |--- project1
    |    |--- main.bundle.js
    |--- project2
    |    |--- main.bundle.js
    

    Since, you are generating your entry object dynamically using the glob pattern, you would need to maintain the structure in the each key of the entry object. So, you need:

    const glob = require('glob');
    const path = require('path');
    
    const entryFiles = glob
      .sync('./src/**/*.ts')
      .reduce((entryObj, currentValue) => {
    
        const parsedPath = path.parse(path.relative('./src', currentValue));
    
        // e.g. `project1/main`
        const entryValue = `${parsedPath.dir}${path.sep}${parsedPath.name}`;
    
        return {
          ...entryObj,
          [entryValue]: currentValue
        };
      }, {});
    
    
    module.exports = {
      entry: entryFiles,
    
      output: {
        filename: '[name].bundle.js'
      },
    
      // Rest of the configuration...
    };