257

Does anyone know how to create multiple output paths in a webpack.config.js file? I'm using bootstrap-sass which comes with a few different font files, etc. For webpack to process these i've included file-loader which is working correctly, however the files it outputs are being saved to the output path i specified for the rest of my files:

    output: {
      path: __dirname + "/js",
      filename: "scripts.min.js"
    }

I'd like to achieve something where I can maybe look at the extension types for whatever webpack is outputting and for things ending in .woff .eot, etc, have them diverted to a different output path. Is this possible?

I did a little googling and came across this *issue on github where a couple of solutions are offered, edit:

but it looks as if you need to know the entry point in able to specify an output using the hash method eg:

var entryPointsPathPrefix = './src/javascripts/pages';
var WebpackConfig = {
  entry : {
    a: entryPointsPathPrefix + '/a.jsx',
    b: entryPointsPathPrefix + '/b.jsx',
    c: entryPointsPathPrefix + '/c.jsx',
    d: entryPointsPathPrefix + '/d.jsx'
  },

  // send to distribution
  output: {
    path: './dist/js',
    filename: '[name].js'
  }
}

*https://github.com/webpack/webpack/issues/1189

however in my case, as far as the font files are concerned, the input process is kind of abstracted away and all i know is the output. in the case of my other files undergoing transformations, there's a known point where i'm requiring them in to be then handled by my loaders. if there was a way of finding out where this step was happening, i could then use the hash method to customize output paths, but i don't know where these files are being required in.

Jérémie Bertrand
  • 3,025
  • 3
  • 44
  • 53
spb
  • 4,049
  • 6
  • 22
  • 30

13 Answers13

363

Webpack does support multiple output paths.

Set the output paths as the entry key. And use the name as output template.

webpack config:

entry: {
    'module/a/index': 'module/a/index.js',
    'module/b/index': 'module/b/index.js',
},
output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
}

generated:

└── module
    ├── a
    │   └── index.js
    └── b
        └── index.js
GijsjanB
  • 9,944
  • 4
  • 23
  • 27
zhengkenghong
  • 3,762
  • 1
  • 8
  • 6
  • 6
    In my case I want one output to not contain chunkhash, is there any simple solution to that? Thanks. – Jón Trausti Arason Dec 11 '17 at 11:42
  • 1
    @zhengkenghong I believe that the generated output path would need `dist` in it. So instead of `module/a/index.js` being an output path, it should be `module/a/dist/index.js` Or else, you are overriding your entry files. – dance2die Feb 07 '18 at 14:34
  • 2
    @Sung `dist` folder is already configured in output path. So the generated file would actually be `dist/module/a/index.js`, which i didn't mention. – zhengkenghong Feb 08 '18 at 07:44
  • I get: `ERROR in Entry module not found: Error: Can't resolve 'src/js/app.js'` when using: `entry: { 'src/js/app': "src/js/app.js"}` – user1063287 May 24 '18 at 01:45
  • 8
    I think this should be the accepted answer as it is the answer from the webpack 4 docs. -> https://webpack.js.org/concepts/output/#multiple-entry-points – Will Jul 05 '18 at 12:37
  • Is this feature documented anywhere?!? I had sort of gotten a non-flat output structure working by making the output path `__dirname + '/dist/js')`, and then in plugins doing things like `new CopyWebpackPlugin([{ from: "./src/img/", to: "../img" }])` to copy files to `/dist/img/`, but then that confused other plugins like `zip-webpack-plugin`. Adding the output path to the entry key seems to be the only thing that works reliably. – jdunning Aug 01 '18 at 18:16
  • 3
    @raRaRa Late to the party, but you can do that by using a function for `output.filename` as documented here: https://webpack.js.org/configuration/output/#outputfilename – Thomas Apr 19 '19 at 08:30
  • I was able to create multiple output paths. Nice answer but webpack do allow. – Kent Marete Jun 06 '20 at 16:35
  • 1
    It's worth checking out @Will s comment, the docs specify it's as simple as adding key:value pairs to your entry property. – Pieter De Bie Nov 01 '20 at 06:21
  • MAKE THIS THE ACCEPTED ANSWER! – Omid Ariyan Sep 08 '21 at 08:22
285

I'm not sure if we have the same problem since webpack only support one output per configuration as of Jun 2016. I guess you already seen the issue on Github.

But I separate the output path by using the multi-compiler. (i.e. separating the configuration object of webpack.config.js).

var config = {
    // TODO: Add common Configuration
    module: {},
};

var fooConfig = Object.assign({}, config, {
    name: "a",
    entry: "./a/app",
    output: {
       path: "./a",
       filename: "bundle.js"
    },
});
var barConfig = Object.assign({}, config,{
    name: "b",
    entry: "./b/app",
    output: {
       path: "./b",
       filename: "bundle.js"
    },
});

// Return Array of Configurations
module.exports = [
    fooConfig, barConfig,       
];

If you have common configuration among them, you could use the extend library or Object.assign in ES6 or {...} spread operator in ES7.

Yeo
  • 11,416
  • 6
  • 63
  • 90
  • 1
    I didn't run the snippet, some error or typo may occur – Yeo Jun 30 '16 at 20:20
  • 1
    I did run your snippet, works like charm... Surprised no one spotted this, eh frontend developers, no patience, always in hurry ;-). I export configs the same way, but my declaration is different/standard: var config = { entry: SOURCE_DIR + '/index.jsx', .... } Did not use any multi compiler as well :-\ – Aubergine Jul 20 '16 at 00:15
  • Or you can just do a **webpack && cp etc** in npm? – SuperUberDuper Jul 05 '17 at 16:33
  • 1
    That's very useful for me to deploy an npm package both in the original folder (automatic tests are there) but also in the folder of the app that implements the package. In this way I can skip the npm download step and live test my updated package code until the new version is stable and ready to be published on npm. – Adrian Moisa Jan 06 '18 at 10:57
  • var config = { // TODO: Add common Configuration module: {}, }; The `module{}` object is incorrect. It's not required. It will be extended/merged at the same level as keywords `name`, `entry`, `output` (from your example).
    
        {
            module: {
                mode: "development",
                devtool: "source-map" } 
            },
            name: "a",
            entry: "./a/app",
            output: {
                path: "/a",
                filename: "bundle.js"
            }
        }
    
    – Rob Waa Jul 06 '18 at 15:29
  • I can build it now but how can I start-dev-server for fooConfig or barConfig? – rendom Feb 28 '20 at 05:46
  • This was a fantastic example and genius. I had a similar implementation that worked perfectly well. ```module.exports = [ adminPanelConfig, flightEngineConfig, ];``` – Kent Marete Jun 06 '20 at 16:38
  • `Object.assign` overwrites the same config key from common config (if it exists), using https://www.npmjs.com/package/webpack-merge can handle merging perfectly. For example, we declare CompressionPlugin in common under `plugins` object key, if then we add another plugin for only specific config, the previously declared plugin in common config will be replaced. – fsevenm Jul 13 '20 at 03:39
  • I ended up with "Webpack has been initialized using a configuration object that does not match the API schema." cause I am using the webpack merge config thing for other wbpack files for other envs – Tanner Summers Sep 14 '22 at 17:15
65

You can now (as of Webpack v5.0.0) specify a unique output path for each entry using the new "descriptor" syntax (https://webpack.js.org/configuration/entry-context/#entry-descriptor) –

module.exports = {
  entry: {
    home: { import: './home.js', filename: 'unique/path/1/[name][ext]' },
    about: { import: './about.js', filename: 'unique/path/2/[name][ext]' }
  }
};
Ryan Waldon
  • 759
  • 6
  • 5
35

If you can live with multiple output paths having the same level of depth and folder structure there is a way to do this in webpack 2 (have yet to test with webpack 1.x)

Basically you don't follow the doc rules and you provide a path for the filename.

module.exports = {
    entry: {
      foo: 'foo.js',
      bar: 'bar.js'
    },

    output: {
      path: path.join(__dirname, 'components'),
      filename: '[name]/dist/[name].bundle.js', // Hacky way to force webpack   to have multiple output folders vs multiple files per one path
    }
};

That will take this folder structure

/-
  foo.js
  bar.js

And turn it into

/-
  foo.js
  bar.js
  components/foo/dist/foo.js
  components/bar/dist/bar.js
cmswalker
  • 371
  • 3
  • 4
  • @ccnixon it is documented here https://webpack.js.org/configuration/output/#outputfilename seach for "still allowed". – John Henckel Jun 06 '19 at 22:17
10

Please don't use any workaround because it will impact build performance.

Webpack File Manager Plugin

Easy to install copy this tag on top of the webpack.config.js

const FileManagerPlugin = require('filemanager-webpack-plugin');

Install

npm install filemanager-webpack-plugin --save-dev

Add the plugin

module.exports = {
    plugins: [
        new FileManagerPlugin({
            onEnd: {
                copy: [
                    {source: 'www', destination: './vinod test 1/'},
                    {source: 'www', destination: './vinod testing 2/'},
                    {source: 'www', destination: './vinod testing 3/'},
                ],
            },
        }),
    ],
};

Screenshot

enter image description here

RCRalph
  • 355
  • 6
  • 17
Vinod Kumar
  • 1,191
  • 14
  • 12
10

In my case I had this scenario

const config = {
    entry: {
    moduleA: './modules/moduleA/index.js',
    moduleB: './modules/moduleB/index.js',
    moduleC: './modules/moduleB/v1/index.js',
    moduleC: './modules/moduleB/v2/index.js',
  },
}

And I solve it like this (webpack4)

const config = {
entry: {
        moduleA: './modules/moduleA/index.js',
        moduleB: './modules/moduleB/index.js',
        'moduleC/v1/moduleC': './modules/moduleB/v1/index.js',
        'moduleC/v2/MoculeC': './modules/moduleB/v2/index.js',
      },
    }
7

You definitely can return array of configurations from your webpack.config file. But it's not an optimal solution if you just want a copy of artifacts to be in the folder of your project's documentation, since it makes webpack build your code twice doubling the overall time to build.

In this case I'd recommend to use the FileManagerWebpackPlugin plugin instead:

const FileManagerPlugin = require('filemanager-webpack-plugin');
// ...
plugins: [
    // ...
    new FileManagerPlugin({
      onEnd: {
        copy: [{
          source: './dist/*.*',
          destination: './public/',
        }],
      },
    }),
],
7

If it's not obvious after all the answers you can also output to a completely different directories (for example a directory outside your standard dist folder). You can do that by using your root as a path (because you only have one path) and by moving the full "directory part" of your path to the entry option (because you can have multiple entries):

entry: {
  'dist/main': './src/index.js',
  'docs/main': './src/index.js'
},
output: {
  filename: '[name].js',
  path: path.resolve(__dirname, './'),
}

This config results in the ./dist/main.js and ./docs/main.js being created.

ajobi
  • 2,996
  • 3
  • 13
  • 28
4

You can only have one output path.

from the docs https://github.com/webpack/docs/wiki/configuration#output

Options affecting the output of the compilation. output options tell Webpack how to write the compiled files to disk. Note, that while there can be multiple entry points, only one output configuration is specified.

If you use any hashing ([hash] or [chunkhash]) make sure to have a consistent ordering of modules. Use the OccurenceOrderPlugin or recordsPath.

Community
  • 1
  • 1
ex-zac-tly
  • 979
  • 1
  • 8
  • 24
  • thanks. i'm gonna leave the Q just in case someone might be able to come up with a workaround. – spb Mar 09 '16 at 23:20
  • what's your use case for requiring 2 output paths? It sounds like you want 2 applications or 1 application and 1 module. – ex-zac-tly Mar 11 '16 at 00:33
  • i thought i needed one that was dedicated to the output generated by the file-loader which was all going into the root of the project whereas I wanted it in its own folder. i wound up just redirecting the output path in the loader itself per my answer below. – spb Mar 11 '16 at 23:09
  • 1
    This isn't completely true. You can technically only specify one output path but it will apply for each key in an entry object, allowing you to have multiple outputs -- https://webpack.js.org/concepts/entry-points/ – sanjsanj Nov 30 '17 at 17:40
3

I wrote a plugin that can hopefully do what you want, you can specify known or unknown entry points (using glob) and specify exact outputs or dynamically generate them using the entry file path and name. https://www.npmjs.com/package/webpack-entry-plus

sanjsanj
  • 1,003
  • 9
  • 20
0

I actually wound up just going into index.js in the file-loader module and changing where the contents were emitted to. This is probably not the optimal solution, but until there's some other way, this is fine since I know exactly what's being handled by this loader, which is just fonts.

//index.js
var loaderUtils = require("loader-utils");
module.exports = function(content) {
    this.cacheable && this.cacheable();
    if(!this.emitFile) throw new Error("emitFile is required from module system");
    var query = loaderUtils.parseQuery(this.query);
    var url = loaderUtils.interpolateName(this, query.name || "[hash].[ext]", {
        context: query.context || this.options.context,
        content: content,
        regExp: query.regExp
    });
    this.emitFile("fonts/"+ url, content);//changed path to emit contents to "fonts" folder rather than project root
    return "module.exports = __webpack_public_path__ + " + JSON.stringify( url) + ";";
}
module.exports.raw = true;
spb
  • 4,049
  • 6
  • 22
  • 30
  • 1
    Don't know if this is still an issue for you but have a look at https://www.npmjs.com/package/webpack-entry-plus – sanjsanj Nov 30 '17 at 17:54
0

The problem is already in the language:

  • entry (which is a object (key/value) and is used to define the inputs*)
  • output (which is a object (key/value) and is used to define outputs*)

The idea to differentiate the output based on limited placeholder like '[name]' defines limitations.

I like the core functionality of webpack, but the usage requires a rewrite with abstract definitions which are based on logic and simplicity... the hardest thing in software-development... logic and simplicity.

All this could be solved by just providing a list of input/output definitions... A LIST INPUT/OUTPUT DEFINITIONS.

Vinod Kumar's good workaround is:

module.exports = {
   plugins: [
    new FileManagerPlugin({
      events: {
        onEnd: {
          copy: [
            {source: 'www', destination: './vinod test 1/'},
            {source: 'www', destination: './vinod testing 2/'},
            {source: 'www', destination: './vinod testing 3/'},
          ],
        },
      }
    }),
  ],
};

Nimantha
  • 6,405
  • 6
  • 28
  • 69
0

You can do like:

var config = {
    // TODO: Add common Configuration
    module: {},
};

var x= Object.assign({}, config, {
    name: "x",
    entry: "./public/x/js/x.js",
    output: {
       path: __dirname+"/public/x/jsbuild",
       filename: "xbundle.js"
    },
});
var y= Object.assign({}, config, {
    name: "y",
    entry: "./public/y/js/FBRscript.js",
    output: {
       path: __dirname+"/public/fbr/jsbuild",
       filename: "ybundle.js"
    },
});

let list=[x,y];

for(item of list){
    module.exports =item;
}
Ed de Almeida
  • 3,675
  • 4
  • 25
  • 57
  • 1
    Welcome to SO. Please review [how to write a good answer](https://stackoverflow.com/help/how-to-answer). Adding context and explanation will make your answer better. – displacedtexan Jan 12 '21 at 21:28