npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

webpack-merge

v6.0.1

Published

Variant of merge that's useful for webpack configuration

Downloads

59,543,757

Readme

Financial Contributors on Open Collective Test 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.

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.

const { merge } = require('webpack-merge');

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

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

// Keys matching to the right take precedence:
const output = merge(
  { fruit: "apple", color: "red" },
  { fruit: "strawberries" }
);
console.log(output);
// { color: "red", fruit: "strawberries"}

Limitations

Note that Promises are not supported! If you want to return a configuration wrapped within a Promise, merge inside one. Example: Promise.resolve(merge({ ... }, { ... })).

The same goes for configuration level functions as in the example below:

webpack.config.js

const commonConfig = { ... };

const productionConfig = { ... };

const developmentConfig = { ... };

module.exports = (env, args) => {
  switch(args.mode) {
    case 'development':
      return merge(commonConfig, developmentConfig);
    case 'production':
      return merge(commonConfig, productionConfig);
    default:
      throw new Error('No matching configuration was found!');
  }
}

You can choose the configuration you want by using webpack --mode development assuming you are using webpack-cli.

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

In case you need more flexibility, merge behavior can be customized per field as below:

const { mergeWithCustomize } = require('webpack-merge');

const output = mergeWithCustomize(
  {
    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);

customizeArray and customizeObject

customizeArray and customizeObject provide small strategies to for mergeWithCustomize. They support append, prepend, replace, and wildcards for field names.

const { mergeWithCustomize, customizeArray, customizeObject } = require('webpack-merge');

const output = mergeWithCustomize({
  customizeArray: customizeArray({
    'entry.*': 'prepend'
  }),
  customizeObject: customizeObject({
    entry: 'prepend'
  })
})(object1, object2, object3, ...);

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

unique is a strategy used for forcing uniqueness within configuration. It's most useful with plugins when you want to make sure there's only one in place.

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

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

When the order of elements of the <field> in the first configuration differs from the order in the second configuration, the latter is preserved.

const { mergeWithCustomize, unique } = require("webpack-merge");

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

// Output contains only single HotModuleReplacementPlugin now and it's
// going to be the last plugin instance.

mergeWithRules

To support advanced merging needs (i.e. merging within loaders), mergeWithRules includes additional syntax that allows you to match fields and apply strategies to match. Consider the full example below:

const a = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [{ loader: "style-loader" }, { loader: "sass-loader" }],
      },
    ],
  },
};
const b = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: "style-loader",
            options: {
              modules: true,
            },
          },
        ],
      },
    ],
  },
};
const result = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: "style-loader",
            options: {
              modules: true,
            },
          },
          { loader: "sass-loader" },
        ],
      },
    ],
  },
};

assert.deepStrictEqual(
  mergeWithRules({
    module: {
      rules: {
        test: "match",
        use: {
          loader: "match",
          options: "replace",
        },
      },
    },
  })(a, b),
  result,
);

The way it works is that you should annotate fields to match using match (or CustomizeRule.Match if you are using TypeScript) matching your configuration structure and then use specific strategies to define how particular fields should be transformed. If a match doesn't exist above a rule, then it will apply the rule automatically.

Supported annotations:

  • match (CustomizeRule.Match) - Optional matcher that scopes merging behavior to a specific part based on similarity (think DOM or jQuery selectors)
  • append (CustomizeRule.Append) - Appends items
  • prepend (CustomizeRule.Prepend) - Prepends items
  • replace (CustomizeRule.Replace) - Replaces items
  • merge (CustomizeRule.Merge) - Merges objects (shallow merge)

Using with TypeScript

webpack-merge supports TypeScript out of the box. You should pass Configuration type from webpack to it as follows:

import { Configuration } from "webpack";
import { merge } from "webpack-merge";

const config = merge<Configuration>({...}, {...});

...

Development

  1. nvm use
  2. npm i
  3. npm run build -- --watch in one terminal
  4. npm t -- --watch in another one

Before contributing, please open an issue where to discuss.

Further Information and Support

Check out SurviveJS - Webpack 5 to dig deeper into webpack. The free book uses webpack-merge extensively and shows you how to compose your configuration to keep it maintainable.

I am also available as a consultant in case you require specific assistance. I can contribute particularly in terms of improving maintainability of the setup while speeding it up and pointing out better practices. In addition to improving developer productivity, the work has impact on the end users of the product in terms of reduced application size and loading times.

Contributors

Code Contributors

This project exists thanks to all the people who contribute. [Contribute].

Financial Contributors

Become a financial contributor and help us sustain our community. [Contribute]

Individuals

Organizations

Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]

License

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