webpack-merge
    Overview
    Documentation
    Insights
    Code
    Contributors
    Dependencies
    Alternatives

webpack-merge

Variant of merge that's useful for webpack configuration

4.2.2  •  Published 3 months ago  •  by survivejs  •  MIT License

build status codecov

webpack-merge - Merge designed for Webpack

webpack-merge provides a merge function that concatenates arrays and merges objects creating a new object. If functions are encountered, it will execute them, run the results through the algorithm, and then wrap the returned values within a function again.

This behavior is particularly useful in configuring webpack although it has uses beyond it. Whenever you need to merge configuration objects, webpack-merge can come in handy.

There’s also a webpack specific merge variant known as merge.smart that’s able to take webpack specifics into account (i.e., it can flatten loader definitions).

Standard Merging

merge(...configuration | [...configuration])

merge is the core, and the most important idea, of the API. Often this is all you need unless you want further customization.

// Default API
var output = merge(object1, object2, object3, ...);

// You can pass an array of objects directly.
// This works with all available functions.
var output = merge([object1, object2, object3]);

// Please note that where keys match,
// the objects to the right take precedence:
var output = merge(
  { fruit: "apple", color: "red" },
  { fruit: "strawberries" }
);
console.log(output);
// { color: "red", fruit: "strawberries"}

merge({ customizeArray, customizeObject })(...configuration | [...configuration])

merge behavior can be customized per field through a curried customization API.

// Customizing array/object behavior
var output = merge(
  {
    customizeArray(a, b, key) {
      if (key === 'extensions') {
        return _.uniq([...a, ...b]);
      }

      // Fall back to default merging
      return undefined;
    },
    customizeObject(a, b, key) {
      if (key === 'module') {
        // Custom merging
        return _.merge({}, a, b);
      }

      // Fall back to default merging
      return undefined;
    }
  }
)(object1, object2, object3, ...);

For example, if the previous code was invoked with only object1 and object2 with object1 as:

{
    foo1: ['object1'],
    foo2: ['object1'],
    bar1: { object1: {} },
    bar2: { object1: {} },
}

and object2 as:

{
    foo1: ['object2'],
    foo2: ['object2'],
    bar1: { object2: {} },
    bar2: { object2: {} },
}

then customizeArray will be invoked for each property of Array type, i.e:

customizeArray(['object1'], ['object2'], 'foo1');
customizeArray(['object1'], ['object2'], 'foo2');

and customizeObject will be invoked for each property of Object type, i.e:

customizeObject({ object1: {} }, { object2: {} }, bar1);
customizeObject({ object1: {} }, { object2: {} }, bar2);

merge.unique(<field>, <fields>, field => field)

The first is the config property to look through for duplicates.

represents the values that should be unique when you run the field => field function on each duplicate.

const output = merge({
  customizeArray: merge.unique(
    'plugins',
    ['HotModuleReplacementPlugin'],
    plugin => plugin.constructor && plugin.constructor.name
  )
})({
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ]
}, {
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ]
});

// Output contains only single HotModuleReplacementPlugin now.

Merging with Strategies

merge.strategy({ <field>: '<prepend|append|replace>''})(...configuration | [...configuration])

Given you may want to configure merging behavior per field, there’s a strategy variant:

// Merging with a specific merge strategy
var output = merge.strategy(
  {
    entry: 'prepend', // or 'replace', defaults to 'append'
    'module.rules': 'prepend'
  }
)(object1, object2, object3, ...);

merge.smartStrategy({ <key>: '<prepend|append|replace>''})(...configuration | [...configuration])

The same idea works with smart merging too (described below in greater detail).

var output = merge.smartStrategy(
  {
    entry: 'prepend', // or 'replace'
    'module.rules': 'prepend'
  }
)(object1, object2, object3, ...);

Smart Merging

merge.smart(...configuration | [...configuration])

webpack-merge tries to be smart about merging loaders when merge.smart is used. Loaders with matching tests will be merged into a single loader value.

Note that the logic picks up webpack 2 rules kind of syntax as well. The examples below have been written in webpack 1 syntax.

package.json

{
  "scripts": {
    "start": "webpack-dev-server",
    "build": "webpack"
  },
  // ...
}

webpack.config.js

var path = require('path');
var merge = require('webpack-merge');

var TARGET = process.env.npm_lifecycle_event;

var common = {
  entry: path.join(__dirname, 'app'),
  ...
  module: {
    loaders: [
      {
        test: /\.css$/,
        loaders: ['style', 'css'],
      },
    ],
  },
};

if(TARGET === 'start') {
  module.exports = merge(common, {
    module: {
      // loaders will get concatenated!
      loaders: [
        {
          test: /\.jsx?$/,
          loader: 'babel?stage=1',
          include: path.join(ROOT_PATH, 'app'),
        },
      ],
    },
    ...
  });
}

if(TARGET === 'build') {
  module.exports = merge(common, {
    ...
  });
}

...

Loader string values loader: 'babel' override each other.

merge.smart({
  loaders: [{
    test: /\.js$/,
    loader: 'babel'
  }]
}, {
  loaders: [{
    test: /\.js$/,
    loader: 'coffee'
  }]
});
// will become
{
  loaders: [{
    test: /\.js$/,
    loader: 'coffee'
  }]
}

Loader array values loaders: ['babel'] will be merged, without duplication.

merge.smart({
  loaders: [{
    test: /\.js$/,
    loaders: ['babel']
  }]
}, {
  loaders: [{
    test: /\.js$/,
    loaders: ['coffee']
  }]
});
// will become
{
  loaders: [{
    test: /\.js$/,
    // appended because Webpack evaluated these from right to left
    // this way you can specialize behavior and build the loader chain
    loaders: ['babel', 'coffee']
  }]
}

Loader array values loaders: ['babel'] can be reordered by including original loaders.

merge.smart({
  loaders: [{
    test: /\.js$/,
    loaders: ['babel']
  }]
}, {
  loaders: [{
    test: /\.js$/,
    loaders: ['react-hot', 'babel']
  }]
});
// will become
{
  loaders: [{
    test: /\.js$/,
    // order of second argument is respected
    loaders: ['react-hot', 'babel']
  }]
}

This also works in reverse - the existing order will be maintained if possible:

merge.smart({
  loaders: [{
    test: /\.css$/,
    use: [
      { loader: 'css-loader', options: { myOptions: true } },
      { loader: 'style-loader' }
    ]
  }]
}, {
  loaders: [{
    test: /\.css$/,
    use: [
      { loader: 'style-loader', options: { someSetting: true } }
    ]
  }]
});
// will become
{
  loaders: [{
    test: /\.css$/,
    use: [
      { loader: 'css-loader', options: { myOptions: true } },
      { loader: 'style-loader', options: { someSetting: true } }
    ]
  }]
}

In the case of an order conflict, the second order wins:

merge.smart({
  loaders: [{
    test: /\.css$/,
    use: [
      { loader: 'css-loader' },
      { loader: 'style-loader' }
    ]
  }]
}, {
  loaders: [{
    test: /\.css$/,
    use: [
      { loader: 'style-loader' },
      { loader: 'css-loader' }
    ]
  }]
});
// will become
{
  loaders: [{
    test: /\.css$/,
    use: [
      { loader: 'style-loader' }
      { loader: 'css-loader' },
    ]
  }]
}

Loader query strings loaders: ['babel?plugins[]=object-assign'] will be overridden.

merge.smart({
  loaders: [{
    test: /\.js$/,
    loaders: ['babel?plugins[]=object-assign']
  }]
}, {
  loaders: [{
    test: /\.js$/,
    loaders: ['babel', 'coffee']
  }]
});
// will become
{
  loaders: [{
    test: /\.js$/,
    loaders: ['babel', 'coffee']
  }]
}

Loader arrays in source values will have loader strings merged into them.

merge.smart({
  loaders: [{
    test: /\.js$/,
    loader: 'babel'
  }]
}, {
  loaders: [{
    test: /\.js$/,
    loaders: ['coffee']
  }]
});
// will become
{
  loaders: [{
    test: /\.js$/,
    // appended because Webpack evaluated these from right to left!
    loaders: ['babel', 'coffee']
  }]
}

Loader strings in source values will always override.

merge.smart({
  loaders: [{
    test: /\.js$/,
    loaders: ['babel']
  }]
}, {
  loaders: [{
    test: /\.js$/,
    loader: 'coffee'
  }]
});
// will become
{
  loaders: [{
    test: /\.js$/,
    loader: 'coffee'
  }]
}

Multiple Merging

merge.multiple(...configuration | [...configuration])

Sometimes you may need to support multiple targets, webpack-merge will accept an object where each key represents the target configuration. The output becomes an array of configurations where matching keys are merged and non-matching keys are added.

var path = require('path');
var baseConfig = {
    server: {
      target: 'node',
      output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'lib.node.js'
      }
    },
    client: {
      output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'lib.js'
      }
    }
  };

// specialized configuration
var production = {
    client: {
      output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].[hash].js'
      }
    }
  }

module.exports = merge.multiple(baseConfig, production)

Check out SurviveJS - Webpack and React to dig deeper into the topic.

Development

  1. npm i
  2. npm run build
  3. npm run watch

Before contributing, please open an issue where to discuss.

License

webpack-merge is available under MIT. See LICENSE for more details.

How do you feel about the name Devstore for this site?

Popularity

Weekly Downloads
2.6M
Stars
1.9K

Maintenance

Development

Last ver 3 months ago
Created 4 years ago
Last commit 3 months ago
4 days between commits

Technology

Node version: 10.15.1
52.4K unpacked

Compliance

MIT License
OSI Approved
0 vulnerabilities

Contributors

26 contributors
Juho Vepsalainen
Maintainer, 303 commits, 27 merges, 2 PRs
Works at SurviveJS
Jonathan
16 commits, 2 PRs
Works at EnergySavvy
Kyle Herock
7 commits, 3 PRs
Matt Shwery
5 commits
Artem Zakharchenko
4 commits, 1 PRs
Works at Pics.io
herecydev
4 commits, 1 PRs

Tags

webpack
merge
Ready for the next level?
Join Devstore's founding team to help us build the ultimate open-source app store, work with the latest technologies, and enjoy great culture, impact and autonomy
© 2019 Devstore, Inc.
Devstore helps developers find and use open-source packages, so they can focus on building amazing things