code:
### Changelog

All notable changes to this project will be documented in this file. Dates are displayed in UTC.

Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).

#### [v6.5.1](https://github.com/neutrinojs/webpack-chain/compare/v6.5.0...v6.5.1)

> 25 July 2020

- Improve error message when .tap() used on an undefined plugin [`#271`](https://github.com/neutrinojs/webpack-chain/pull/271)
- Improve error message when .use() not called for a Plugin [`#270`](https://github.com/neutrinojs/webpack-chain/pull/270)
- Clean up linting configuration [`#268`](https://github.com/neutrinojs/webpack-chain/pull/268)
- Remove unused dev-dependency @types/node [`#269`](https://github.com/neutrinojs/webpack-chain/pull/269)
- Lock file maintenance [`#259`](https://github.com/neutrinojs/webpack-chain/pull/259)
- Travis: Test against Node 14 [`#267`](https://github.com/neutrinojs/webpack-chain/pull/267)
- Improve error message when .tap() used on an undefined plugin (#271) [`#125`](https://github.com/neutrinojs/webpack-chain/issues/125)

#### [v6.5.0](https://github.com/neutrinojs/webpack-chain/compare/v6.4.0...v6.5.0)

> 1 July 2020

- Add rule.resolve [`#265`](https://github.com/neutrinojs/webpack-chain/pull/265)
- Update dependency eslint to v7 [`#263`](https://github.com/neutrinojs/webpack-chain/pull/263)
- Switch from Ava to Jest [`#258`](https://github.com/neutrinojs/webpack-chain/pull/258)
- Update dependency prettier to v2 [`#250`](https://github.com/neutrinojs/webpack-chain/pull/250)
- Update dependency auto-changelog to v2 [`#257`](https://github.com/neutrinojs/webpack-chain/pull/257)
- Lock file maintenance [`#244`](https://github.com/neutrinojs/webpack-chain/pull/244)
- Test against Node 13 [`#254`](https://github.com/neutrinojs/webpack-chain/pull/254)
- Switch to a newer Travis base image [`#253`](https://github.com/neutrinojs/webpack-chain/pull/253)

#### [v6.4.0](https://github.com/neutrinojs/webpack-chain/compare/v6.3.1...v6.4.0)

> 3 February 2020

- Fix Rule.merge() when include or exclude are strings [`#243`](https://github.com/neutrinojs/webpack-chain/pull/243)
- Add Types to Plugin Arguments [`#241`](https://github.com/neutrinojs/webpack-chain/pull/241)
- Add devServer shorthands for sockHost, sockPort, sockPath [`#238`](https://github.com/neutrinojs/webpack-chain/pull/238)
- Add `devtoolNamespace` TypeScript declaration [`#232`](https://github.com/neutrinojs/webpack-chain/pull/232)
- Fix Rule.merge() when include or exclude are strings (#243) [`#228`](https://github.com/neutrinojs/webpack-chain/issues/228)
- Add devServer shorthands for sockHost, sockPort, sockPath (#238) [`#231`](https://github.com/neutrinojs/webpack-chain/issues/231)

#### [v6.3.1](https://github.com/neutrinojs/webpack-chain/compare/v6.3.0...v6.3.1)

> 28 January 2020

- Lock file maintenance [`#217`](https://github.com/neutrinojs/webpack-chain/pull/217)
- docs: Emphasise that merge() doesn't accept webpack config objects [`#225`](https://github.com/neutrinojs/webpack-chain/pull/225)
- Update types and documentation for DevServer [`#233`](https://github.com/neutrinojs/webpack-chain/pull/233)
- Validate that Plugin 'args' is an array [`#229`](https://github.com/neutrinojs/webpack-chain/pull/229)
- Improve error message when legacy minimizer syntax used [`#226`](https://github.com/neutrinojs/webpack-chain/pull/226)
- Add missing engines definition to package.json [`#227`](https://github.com/neutrinojs/webpack-chain/pull/227)
- docs: Correct the .merge() example for optimization.minimizer [`#224`](https://github.com/neutrinojs/webpack-chain/pull/224)
- docs: Fix formatting typos in README [`#223`](https://github.com/neutrinojs/webpack-chain/pull/223)
- Validate that Plugin 'args' is an array (#229) [`#121`](https://github.com/neutrinojs/webpack-chain/issues/121)

#### [v6.3.0](https://github.com/neutrinojs/webpack-chain/compare/v6.2.0...v6.3.0)

> 22 December 2019

- Add support for nested rules (rule.rules) [`#220`](https://github.com/neutrinojs/webpack-chain/pull/220)
- Fix missing type TypedChainedMap.getOrCompute [`#221`](https://github.com/neutrinojs/webpack-chain/pull/221)
- fix: fix type definition for Rule#oneOf [`#218`](https://github.com/neutrinojs/webpack-chain/pull/218)
- fix: fix type definition for Rule#oneOf (#218) [`#216`](https://github.com/neutrinojs/webpack-chain/issues/216)

#### [v6.2.0](https://github.com/neutrinojs/webpack-chain/compare/v6.1.0...v6.2.0)

> 22 December 2019

- Add support for module.strictExportPresence and output.futureEmitAssets [`#207`](https://github.com/neutrinojs/webpack-chain/pull/207)
- Add support for module.strictExportPresence and output.futureEmitAssets (#207) [`#205`](https://github.com/neutrinojs/webpack-chain/issues/205) [`#206`](https://github.com/neutrinojs/webpack-chain/issues/206)

#### [v6.1.0](https://github.com/neutrinojs/webpack-chain/compare/v6.0.0...v6.1.0)

> 13 December 2019

- README: Update Travis badge to point to travis-ci.com [`#215`](https://github.com/neutrinojs/webpack-chain/pull/215)
- Lock file maintenance [`#199`](https://github.com/neutrinojs/webpack-chain/pull/199)
- Fix types for Config.resolve plugins and improve Plugin types [`#213`](https://github.com/neutrinojs/webpack-chain/pull/213)
- Update dependency eslint-plugin-ava to v9 [`#203`](https://github.com/neutrinojs/webpack-chain/pull/203)
- Add Chinese docs link to README [`#136`](https://github.com/neutrinojs/webpack-chain/pull/136)
- Lock file maintenance [`#196`](https://github.com/neutrinojs/webpack-chain/pull/196)
- Update dependency eslint to v6 [`#186`](https://github.com/neutrinojs/webpack-chain/pull/186)
- Update dependency eslint-config-airbnb-base to v14 [`#192`](https://github.com/neutrinojs/webpack-chain/pull/192)
- Lock file maintenance [`#184`](https://github.com/neutrinojs/webpack-chain/pull/184)
- Update dependency eslint-config-prettier to v6 [`#187`](https://github.com/neutrinojs/webpack-chain/pull/187)
- Lock file maintenance [`#180`](https://github.com/neutrinojs/webpack-chain/pull/180)
- Update dependency eslint-plugin-ava to v7 [`#178`](https://github.com/neutrinojs/webpack-chain/pull/178)
- Lock file maintenance [`#177`](https://github.com/neutrinojs/webpack-chain/pull/177)
- Lock file maintenance [`#176`](https://github.com/neutrinojs/webpack-chain/pull/176)
- Lock file maintenance [`#174`](https://github.com/neutrinojs/webpack-chain/pull/174)
- feat: rule test supports function [`#172`](https://github.com/neutrinojs/webpack-chain/pull/172)
- Lock file maintenance [`#170`](https://github.com/neutrinojs/webpack-chain/pull/170)

### [v6.0.0](https://github.com/neutrinojs/webpack-chain/compare/v5.2.4...v6.0.0)

> 3 May 2019

- Lock file maintenance [`#169`](https://github.com/neutrinojs/webpack-chain/pull/169)
- Update linting configuration, support Node.js 12 in CI [`#168`](https://github.com/neutrinojs/webpack-chain/pull/168)
- Extended DevServer method [`#167`](https://github.com/neutrinojs/webpack-chain/pull/167)
- Lock file maintenance [`#165`](https://github.com/neutrinojs/webpack-chain/pull/165)
- Update dependency eslint-plugin-ava to v6 [`#161`](https://github.com/neutrinojs/webpack-chain/pull/161)
- Point docs to v6 [`37201a2`](https://github.com/neutrinojs/webpack-chain/commit/37201a2974c078c3494243ff03342ba16910db21)

#### [v5.2.4](https://github.com/neutrinojs/webpack-chain/compare/v5.2.3...v5.2.4)

> 25 March 2019

- fix Use#end return type in OneOf [`#158`](https://github.com/neutrinojs/webpack-chain/pull/158)
- make __expression property works in any object [`#157`](https://github.com/neutrinojs/webpack-chain/pull/157)
- Lock file maintenance [`#160`](https://github.com/neutrinojs/webpack-chain/pull/160)
- docs: Fix typo in config.optimization example [`#159`](https://github.com/neutrinojs/webpack-chain/pull/159)

#### [v5.2.3](https://github.com/neutrinojs/webpack-chain/compare/v5.2.2...v5.2.3)

> 22 March 2019

- optimize type definitions [`#156`](https://github.com/neutrinojs/webpack-chain/pull/156)
- Lock file maintenance [`#155`](https://github.com/neutrinojs/webpack-chain/pull/155)

#### [v5.2.2](https://github.com/neutrinojs/webpack-chain/compare/v5.2.1...v5.2.2)

> 12 March 2019

- Fix README comment rendering [`#154`](https://github.com/neutrinojs/webpack-chain/pull/154)
- Lock file maintenance [`#153`](https://github.com/neutrinojs/webpack-chain/pull/153)
- Update dependency javascript-stringify to v2 [`#151`](https://github.com/neutrinojs/webpack-chain/pull/151)
- Fix stringify master bustage [`55f6a5d`](https://github.com/neutrinojs/webpack-chain/commit/55f6a5d25b58d03fbc57a17d8c2e09de310fa068)

#### [v5.2.1](https://github.com/neutrinojs/webpack-chain/compare/v5.2.0...v5.2.1)

> 7 March 2019

- Lock file maintenance [`#145`](https://github.com/neutrinojs/webpack-chain/pull/145)
- Add `config.output.globalObject` type [`#147`](https://github.com/neutrinojs/webpack-chain/pull/147)
- add module-rule-type [`#148`](https://github.com/neutrinojs/webpack-chain/pull/148)
- Update `config.mode` type [`#146`](https://github.com/neutrinojs/webpack-chain/pull/146)
- Update dependency eslint-config-prettier to v4 [`75aa60b`](https://github.com/neutrinojs/webpack-chain/commit/75aa60b760947cd8eb438691158dd9db52f4f9f5)

#### [v5.2.0](https://github.com/neutrinojs/webpack-chain/compare/v5.1.0...v5.2.0)

> 23 January 2019

- Add `config.name` type [`#143`](https://github.com/neutrinojs/webpack-chain/pull/143)
- Add TypeScript type definitions [`#132`](https://github.com/neutrinojs/webpack-chain/pull/132)
- docs: Fix typo of 'optimization' [`#139`](https://github.com/neutrinojs/webpack-chain/pull/139)
- Add TypeScript type definitions (#132) [`#62`](https://github.com/neutrinojs/webpack-chain/issues/62)

#### [v5.1.0](https://github.com/neutrinojs/webpack-chain/compare/v5.0.1...v5.1.0)

> 16 January 2019

- Support config.name() setter [`#131`](https://github.com/neutrinojs/webpack-chain/pull/131)
- Allow use of before() and after() with oneOf rules [`#133`](https://github.com/neutrinojs/webpack-chain/pull/133)
- Travis: Test against Node 11 [`#118`](https://github.com/neutrinojs/webpack-chain/pull/118)
- docs: Fix typo in devServer options [`#117`](https://github.com/neutrinojs/webpack-chain/pull/117)
- Allow use of before() and after() with oneOf rules (#133) [`#119`](https://github.com/neutrinojs/webpack-chain/issues/119)
- Update dependency ava to v1 [`ce9e884`](https://github.com/neutrinojs/webpack-chain/commit/ce9e884f988a01ac6297d167dfd013cab8d8c24a)
- Lock file maintenance [`d124b5d`](https://github.com/neutrinojs/webpack-chain/commit/d124b5df6648257092becae81ceece9bef5485b8)

#### [v5.0.1](https://github.com/neutrinojs/webpack-chain/compare/v5.0.0...v5.0.1)

> 22 October 2018

- Fix toString() output for alternative types of plugin [`#116`](https://github.com/neutrinojs/webpack-chain/pull/116)
- Fix toString() output for alternative types of plugin (#116) [`#115`](https://github.com/neutrinojs/webpack-chain/issues/115)

### [v5.0.0](https://github.com/neutrinojs/webpack-chain/compare/v4.12.1...v5.0.0)

> 8 October 2018

- README: Add NPM/Travis badges [`#112`](https://github.com/neutrinojs/webpack-chain/pull/112)
- Provide the same API for config.optimization.minimizer as config.plugins [`#84`](https://github.com/neutrinojs/webpack-chain/pull/84)
- README: Add NPM/Travis badges (#112) [`#110`](https://github.com/neutrinojs/webpack-chain/issues/110)
- Provide the same API for config.optimization.minimizer as config.plugins (#84) [`#95`](https://github.com/neutrinojs/webpack-chain/issues/95)

#### [v4.12.1](https://github.com/neutrinojs/webpack-chain/compare/v4.12.0...v4.12.1)

> 3 October 2018

- Switch from changelog to auto-changelog [`#109`](https://github.com/neutrinojs/webpack-chain/pull/109)
- Allow passing entry as a string to config.merge() [`#107`](https://github.com/neutrinojs/webpack-chain/pull/107)
- Lock file maintenance [`#101`](https://github.com/neutrinojs/webpack-chain/pull/101)
- Update dependency eslint-plugin-prettier to v3 [`e42d8bd`](https://github.com/neutrinojs/webpack-chain/commit/e42d8bd2f6f70841c4d1ab7a2926c26d3ec828ed)

#### [v4.12.0](https://github.com/neutrinojs/webpack-chain/compare/v4.11.0...v4.12.0)

> 3 October 2018

- merge resolve plugins just like config [`c47ee2d`](https://github.com/neutrinojs/webpack-chain/commit/c47ee2d52c1be4fcdbe10adef865e45d6fd729ef)
- linted [`dff82f8`](https://github.com/neutrinojs/webpack-chain/commit/dff82f8494394dcf8c97bff324f877513f44fa56)
- Revert changes to gitignore [`c1250a0`](https://github.com/neutrinojs/webpack-chain/commit/c1250a0fe7ffafa82f529ac8e7262e2c3cdd4729)

#### [v4.11.0](https://github.com/neutrinojs/webpack-chain/compare/v4.10.0...v4.11.0)

> 13 September 2018

- Support specifying plugins by path [`#102`](https://github.com/neutrinojs/webpack-chain/pull/102)
- Lock file maintenance [`#100`](https://github.com/neutrinojs/webpack-chain/pull/100)
- Lock file maintenance [`#96`](https://github.com/neutrinojs/webpack-chain/pull/96)

#### [v4.10.0](https://github.com/neutrinojs/webpack-chain/compare/v4.9.0...v4.10.0)

> 6 September 2018

- Use the Resolve API to define ResolveLoader according to webpack [`#99`](https://github.com/neutrinojs/webpack-chain/pull/99)
- Migrate to new org [`#92`](https://github.com/neutrinojs/webpack-chain/pull/92)
- test: 'clean' in 'ChainedMap' [`#93`](https://github.com/neutrinojs/webpack-chain/pull/93)
- Lock file maintenance [`3a4b3e1`](https://github.com/neutrinojs/webpack-chain/commit/3a4b3e10032856ab7f01afa67a23dd9e4e68161a)
- Lock file maintenance [`815bfd1`](https://github.com/neutrinojs/webpack-chain/commit/815bfd173a2dc6f802b66a48cdb2c4d2ff47df9f)

#### [v4.9.0](https://github.com/neutrinojs/webpack-chain/compare/v4.8.0...v4.9.0)

> 15 August 2018

- Update to ESLint 5 [`#89`](https://github.com/neutrinojs/webpack-chain/pull/89)
- Lock file maintenance [`#85`](https://github.com/neutrinojs/webpack-chain/pull/85)
- Implement ChainedMap.getOrCompute [`#63`](https://github.com/neutrinojs/webpack-chain/pull/63)
- Support Object literal plugin usage [`#86`](https://github.com/neutrinojs/webpack-chain/pull/86)
- Lock file maintenance [`#61`](https://github.com/neutrinojs/webpack-chain/pull/61)
- Lock file maintenance [`#60`](https://github.com/neutrinojs/webpack-chain/pull/60)
- Update to ESLint 5 (#89) [`#69`](https://github.com/neutrinojs/webpack-chain/issues/69) [`#77`](https://github.com/neutrinojs/webpack-chain/issues/77) [`#87`](https://github.com/neutrinojs/webpack-chain/issues/87) [`#88`](https://github.com/neutrinojs/webpack-chain/issues/88)
- Update dependency eslint-config-airbnb-base to v13 [`7370962`](https://github.com/neutrinojs/webpack-chain/commit/73709628a6ff6661e478c652d0ff03b99b6c2abb)
- Fix linting :/ [`30cc11d`](https://github.com/neutrinojs/webpack-chain/commit/30cc11d0d35a5676069a623b180c8e7b00e099e4)
- Fix README bug, test in Node.js v6 [`4a37c74`](https://github.com/neutrinojs/webpack-chain/commit/4a37c74e1f790e118034154da9c32d0e36164f74)
- Run yarn lint --fix [`9384537`](https://github.com/neutrinojs/webpack-chain/commit/9384537269d60bb80b3330cf44ddbdd9d528c454)

#### [v4.8.0](https://github.com/neutrinojs/webpack-chain/compare/v4.7.0...v4.8.0)

> 16 May 2018

- Expose toString as a static method on Config [`#57`](https://github.com/neutrinojs/webpack-chain/pull/57)
- Add test for Config.toString, add README note [`0107aef`](https://github.com/neutrinojs/webpack-chain/commit/0107aef203202aef069190723b04ec4f6ac80b9f)

#### [v4.7.0](https://github.com/neutrinojs/webpack-chain/compare/v4.6.0...v4.7.0)

> 15 May 2018

- Lint with eslint, prettier, airbnb [`#52`](https://github.com/neutrinojs/webpack-chain/pull/52)
- Support Config.toString() with name hints [`#53`](https://github.com/neutrinojs/webpack-chain/pull/53)
- Configure Renovate [`#54`](https://github.com/neutrinojs/webpack-chain/pull/54)
- Lock file maintenance [`50d4db8`](https://github.com/neutrinojs/webpack-chain/commit/50d4db81ab5fe62a55435720f5c78ddc40309a88)

#### [v4.6.0](https://github.com/neutrinojs/webpack-chain/compare/v4.5.0...v4.6.0)

> 16 April 2018

- Support Webpack 4.x [`#51`](https://github.com/neutrinojs/webpack-chain/pull/51)
- Update devDependencies [`#50`](https://github.com/neutrinojs/webpack-chain/pull/50)

#### [v4.5.0](https://github.com/neutrinojs/webpack-chain/compare/v4.4.2...v4.5.0)

> 22 November 2017

- Introduce method for performing a batch of operations against a context [`#43`](https://github.com/neutrinojs/webpack-chain/pull/43)

#### [v4.4.2](https://github.com/neutrinojs/webpack-chain/compare/v4.4.1...v4.4.2)

> 10 October 2017

- Update changelog [`1bb3da1`](https://github.com/neutrinojs/webpack-chain/commit/1bb3da1fec04a158f68762e57aff33a0172a298f)
- Hotfix - guard against non-defined entries when ordering chainedmap [`76be81f`](https://github.com/neutrinojs/webpack-chain/commit/76be81f4509b9652bef25cc55747df87850b858e)
- Updating changelog [`a71fc4b`](https://github.com/neutrinojs/webpack-chain/commit/a71fc4b70ccce358aacc29ed7dc5d8cdacdd4cc1)

#### [v4.4.1](https://github.com/neutrinojs/webpack-chain/compare/v4.4.0...v4.4.1)

> 6 October 2017

- Updating changelog [`97a2fab`](https://github.com/neutrinojs/webpack-chain/commit/97a2fabf6e51f1b03cacb0991ba02e236be983fa)
- Missing schema before/after [`8d8f26d`](https://github.com/neutrinojs/webpack-chain/commit/8d8f26dd0e6db375dbabb8e8dfe784e6c50408d5)

#### [v4.4.0](https://github.com/neutrinojs/webpack-chain/compare/v4.3.0...v4.4.0)

> 6 October 2017

- Feature: allow specifying to use before or after other use [`#42`](https://github.com/neutrinojs/webpack-chain/pull/42)
- Docs: Upstream fixes made to Neutrino's webpack-chain docs [`#41`](https://github.com/neutrinojs/webpack-chain/pull/41)
- Improve documentation for plugin configuration [`#40`](https://github.com/neutrinojs/webpack-chain/pull/40)
- Allow omitting keys from source merge object [`fb6ea2f`](https://github.com/neutrinojs/webpack-chain/commit/fb6ea2fad931c13e7516a3e9354215a78cb5c4ff)
- Feature: allow specifying .before or .after to order plugins and uses [`b0040bf`](https://github.com/neutrinojs/webpack-chain/commit/b0040bff73b3b9e55d53192ac4a447a2ac8c02d1)
- Rename when arguments to be clearer [`d15e895`](https://github.com/neutrinojs/webpack-chain/commit/d15e895669ce0a44c704755af39290700e73e85f)
- Bumping deps [`c15be4a`](https://github.com/neutrinojs/webpack-chain/commit/c15be4ab99d232126bbf18666f4f20f80df21f90)
- Update changelog [`5aec63a`](https://github.com/neutrinojs/webpack-chain/commit/5aec63a424c71a9a603540b33e576999c839f074)

#### [v4.3.0](https://github.com/neutrinojs/webpack-chain/compare/v4.2.0...v4.3.0)

> 13 September 2017

- Update API for base config, dev server, and output [`#38`](https://github.com/neutrinojs/webpack-chain/pull/38)
- Update changelog [`6260f49`](https://github.com/neutrinojs/webpack-chain/commit/6260f49edbcab301988b7b2c6c8a77e07707c010)

#### [v4.2.0](https://github.com/neutrinojs/webpack-chain/compare/v4.1.0...v4.2.0)

> 13 September 2017

- Add new shorthands from resolve and output [`#37`](https://github.com/neutrinojs/webpack-chain/pull/37)
- changelog [`0374e51`](https://github.com/neutrinojs/webpack-chain/commit/0374e518a4b465c73b5097eff6e4c77768319e4f)
- Updating README with shorthands [`ae5e75a`](https://github.com/neutrinojs/webpack-chain/commit/ae5e75ae619d0399bcbf8e588a48759b0e590b6e)

#### [v4.1.0](https://github.com/neutrinojs/webpack-chain/compare/v4.0.0...v4.1.0)

> 12 September 2017

- Updating rule definition shortcuts, adding oneOf [`#36`](https://github.com/neutrinojs/webpack-chain/pull/36)

### [v4.0.0](https://github.com/neutrinojs/webpack-chain/compare/v3.3.0...v4.0.0)

> 3 October 2018

- Switch noParse to getter/setter to allow webpack v3 function argument [`#32`](https://github.com/neutrinojs/webpack-chain/pull/32)
- Serialize performance into config output [`#31`](https://github.com/neutrinojs/webpack-chain/pull/31)
- Release v4.0.0 [`e84b002`](https://github.com/neutrinojs/webpack-chain/commit/e84b00207f6d4f4dc37c43ffffc65d8a34f63a75)

#### [v3.3.0](https://github.com/neutrinojs/webpack-chain/compare/v3.2.0...v3.3.0)

> 3 October 2018

- Adding noParse on module [`#27`](https://github.com/neutrinojs/webpack-chain/pull/27)
- Releasing v3.3.0 [`4a59bef`](https://github.com/neutrinojs/webpack-chain/commit/4a59bef687f273503945e638fefe1f6ab29857d1)

#### [v3.2.0](https://github.com/neutrinojs/webpack-chain/compare/v3.1.0...v3.2.0)

> 3 October 2018

- Adding updated shorthand methods for devServer [`#23`](https://github.com/neutrinojs/webpack-chain/pull/23)

#### [v3.1.0](https://github.com/neutrinojs/webpack-chain/compare/v3.0.0...v3.1.0)

> 3 October 2018

- Allow conditional configuration via when [`#22`](https://github.com/neutrinojs/webpack-chain/pull/22)
- Update README with links to previous docs versions [`0dc3984`](https://github.com/neutrinojs/webpack-chain/commit/0dc39841b76c5e4d9493fa86d7f65e66145a6964)
- Update README with links to previous docs versions [`bcc2362`](https://github.com/neutrinojs/webpack-chain/commit/bcc2362d396cee736feaa5e4537150b4a1fa2d4a)

### [v3.0.0](https://github.com/neutrinojs/webpack-chain/compare/v2.0.1...v3.0.0)

> 3 October 2018

- Make rule.include, rule.exclude, loaders and plugins more extensible [`#16`](https://github.com/neutrinojs/webpack-chain/pull/16)

#### [v2.0.1](https://github.com/neutrinojs/webpack-chain/compare/v2.0.0...v2.0.1)

> 3 October 2018

- undefined plugin [`#17`](https://github.com/neutrinojs/webpack-chain/pull/17)

### [v2.0.0](https://github.com/neutrinojs/webpack-chain/compare/v1.4.3...v2.0.0)

> 3 October 2018

- Adding testing, which informed v2 API, updated docs to reflect [`#14`](https://github.com/neutrinojs/webpack-chain/pull/14)
- Make Plugin API consistent with Loader API [`#13`](https://github.com/neutrinojs/webpack-chain/pull/13)
- MPL license, moving to mozilla-neutrino [`f122edd`](https://github.com/neutrinojs/webpack-chain/commit/f122eddccb9f7af9742f5c447c651172700b4c50)

#### [v1.4.3](https://github.com/neutrinojs/webpack-chain/compare/v1.4.2...v1.4.3)

> 6 March 2017

- Adding ChainedMap and ChainedSet documentation [`b071f82`](https://github.com/neutrinojs/webpack-chain/commit/b071f82042c7806f6d2df412a0154c1b985c4763)
- Removing empty entities from cluttering configuration object [`b428e55`](https://github.com/neutrinojs/webpack-chain/commit/b428e55a671a033c133ba2e225796845307dee12)
- Docs: getConfig -> toConfig [`2468eaa`](https://github.com/neutrinojs/webpack-chain/commit/2468eaac7e4c2f74cae244a4af6d2a517483db7b)

#### [v1.4.2](https://github.com/neutrinojs/webpack-chain/compare/v1.4.1...v1.4.2)

> 3 October 2018

- Fix bug where `exclude` doesn't return `this` [`#7`](https://github.com/neutrinojs/webpack-chain/pull/7)
- Bumping to v1.4.2 [`38d1412`](https://github.com/neutrinojs/webpack-chain/commit/38d1412037780b815c537de2abd5f02443b80502)

#### [v1.4.1](https://github.com/neutrinojs/webpack-chain/compare/v1.4.0...v1.4.1)

> 3 October 2018

- Allowing config merge to append to existing rule loaders [`#3`](https://github.com/neutrinojs/webpack-chain/pull/3)
- docs(readme): fix typo in devtool option [`#1`](https://github.com/neutrinojs/webpack-chain/pull/1)

#### [v1.4.0](https://github.com/neutrinojs/webpack-chain/compare/v1.3.0...v1.4.0)

> 3 October 2018

- Adds rule test merge via string to regex, fixes externals not chainable [`a15b49e`](https://github.com/neutrinojs/webpack-chain/commit/a15b49ec28903708c04665d0c6cac3a956558a99)

#### [v1.3.0](https://github.com/neutrinojs/webpack-chain/compare/v1.2.0...v1.3.0)

> 3 October 2018

- Adding functionality for merging and object into a Config instance [`5f0b0c6`](https://github.com/neutrinojs/webpack-chain/commit/5f0b0c670e6ad946e0232a208abb667f749aeba4)

#### [v1.2.0](https://github.com/neutrinojs/webpack-chain/compare/v1.1.0...v1.2.0)

> 3 October 2018

- Adds hot flag for Config.DevServer [`c64a155`](https://github.com/neutrinojs/webpack-chain/commit/c64a1558188ab2a4982a7b3f2aba95ced50c9756)

#### [v1.1.0](https://github.com/neutrinojs/webpack-chain/compare/v1.0.3...v1.1.0)

> 3 October 2018

- Adding ChainedSet#prepend functionality [`cc86e7b`](https://github.com/neutrinojs/webpack-chain/commit/cc86e7bdbdcaca7610255039dcb31adfacb4952b)

#### [v1.0.3](https://github.com/neutrinojs/webpack-chain/compare/v1.0.2...v1.0.3)

> 3 October 2018

- Fixes exception with empty rule entries with loader only [`7964b34`](https://github.com/neutrinojs/webpack-chain/commit/7964b347ed613c7dc18e54f912467abad2956b8b)

#### [v1.0.2](https://github.com/neutrinojs/webpack-chain/compare/v1.0.1...v1.0.2)

> 3 October 2018

- Fixes plugin methods not chaining [`7cc56ed`](https://github.com/neutrinojs/webpack-chain/commit/7cc56ed5e331c7cc0ccd58f25feb3b1b0398829a)

#### [v1.0.1](https://github.com/neutrinojs/webpack-chain/compare/v1.0.0...v1.0.1)

> 3 October 2018

- Shared configuration documentation [`5c6a65b`](https://github.com/neutrinojs/webpack-chain/commit/5c6a65b344113ff3522d5f7d66dfacbc0ad7fa69)
- Avoid exceptions in empty config [`ab46ee0`](https://github.com/neutrinojs/webpack-chain/commit/ab46ee0234a04eb2b89190df32ea4287c499dd39)

#### v1.0.0

> 3 October 2018

- initial commit [`9e2a87c`](https://github.com/neutrinojs/webpack-chain/commit/9e2a87c5f6a1f1aac3eedbd4102e40dc47a8f7f4)


code:
# webpack-chain

[![NPM version][npm-image]][npm-url]
[![NPM downloads][npm-downloads]][npm-url]
[![Build Status][travis-image]][travis-url]

Use a chaining API to generate and simplify the modification of
webpack version 2-4 configurations.

This documentation corresponds to v6 of webpack-chain. For previous versions, see:

* [v5 docs](https://github.com/neutrinojs/webpack-chain/tree/v5)
* [v4 docs](https://github.com/neutrinojs/webpack-chain/tree/v4)
* [v3 docs](https://github.com/neutrinojs/webpack-chain/tree/v3)
* [v2 docs](https://github.com/neutrinojs/webpack-chain/tree/v2)
* [v1 docs](https://github.com/neutrinojs/webpack-chain/tree/v1)

_Note: while webpack-chain is utilized extensively in Neutrino, this package is
completely standalone and can be used by any project._

**[Chinese docs(中文文档)](https://github.com/Yatoo2018/webpack-chain/tree/zh-cmn-Hans)**

## Introduction

webpack's core configuration is based on creating and modifying a
potentially unwieldy JavaScript object. While this is OK for configurations
on individual projects, trying to share these objects across projects and
make subsequent modifications gets messy, as you need to have a deep
understanding of the underlying object structure to make those changes.

`webpack-chain` attempts to improve this process by providing a chainable or
fluent API for creating and modifying webpack configurations. Key portions
of the API can be referenced by user-specified names, which helps to
standardize how to modify a configuration across projects.

This is easier explained through the examples following.

## Installation

`webpack-chain` requires Node.js v6.9 and higher. `webpack-chain` also
only creates configuration objects designed for use in webpack versions 2, 3,
and 4.

You may install this package using either Yarn or npm (choose one):

**Yarn**

```bash
yarn add --dev webpack-chain
```

**npm**

```bash
npm install --save-dev webpack-chain
```

## Getting Started

Once you have `webpack-chain` installed, you can start creating a
webpack configuration. For this guide, our example base configuration will
be `webpack.config.js` in the root of our project directory.

```js
// Require the webpack-chain module. This module exports a single
// constructor function for creating a configuration API.
const Config = require('webpack-chain');

// Instantiate the configuration with a new API
const config = new Config();

// Make configuration changes using the chain API.
// Every API call tracks a change to the stored configuration.

config
  // Interact with entry points
  .entry('index')
    .add('src/index.js')
    .end()
  // Modify output settings
  .output
    .path('dist')
    .filename('[name].bundle.js');

// Create named rules which can be modified later
config.module
  .rule('lint')
    .test(/\.js$/)
    .pre()
    .include
      .add('src')
      .end()
    // Even create named uses (loaders)
    .use('eslint')
      .loader('eslint-loader')
      .options({
        rules: {
          semi: 'off'
        }
      });

config.module
  .rule('compile')
    .test(/\.js$/)
    .include
      .add('src')
      .add('test')
      .end()
    .use('babel')
      .loader('babel-loader')
      .options({
        presets: [
          ['@babel/preset-env', { modules: false }]
        ]
      });

// Create named plugins too!
config
  .plugin('clean')
    .use(CleanPlugin, [['dist'], { root: '/dir' }]);

// Export the completed configuration object to be consumed by webpack
module.exports = config.toConfig();
```

Having shared configurations is also simple. Just export the configuration
and call `.toConfig()` prior to passing to webpack.

```js
// webpack.core.js
const Config = require('webpack-chain');
const config = new Config();

// Make configuration shared across targets
// ...

module.exports = config;

// webpack.dev.js
const config = require('./webpack.core');

// Dev-specific configuration
// ...
module.exports = config.toConfig();

// webpack.prod.js
const config = require('./webpack.core');

// Production-specific configuration
// ...
module.exports = config.toConfig();
```

## ChainedMap

One of the core API interfaces in webpack-chain is a `ChainedMap`. A
`ChainedMap` operates similar to a JavaScript Map, with some conveniences for
chaining and generating configuration. If a property is marked as being a
`ChainedMap`, it will have an API and methods as described below:

**Unless stated otherwise, these methods will return the `ChainedMap`, allowing
you to chain these methods.**

```js
// Remove all entries from a Map.
clear()
```

```js
// Remove a single entry from a Map given its key.
// key: *
delete(key)
```

```js
// Fetch the value from a Map located at the corresponding key.
// key: *
// returns: value
get(key)
```

```js
// Fetch the value from a Map located at the corresponding key.
// If the key is missing, the key is set to the result of function fn.
// key: *
// fn: Function () -> value
// returns: value
getOrCompute(key, fn)
```

```js
// Set a value on the Map stored at the `key` location.
// key: *
// value: *
set(key, value)
```

```js
// Returns `true` or `false` based on whether a Map as has a value set at a
// particular key.
// key: *
// returns: Boolean
has(key)
```

```js
// Returns an array of all the values stored in the Map.
// returns: Array
values()
```

```js
// Returns an object of all the entries in the backing Map
// where the key is the object property, and the value
// corresponding to the key. Will return `undefined` if the backing
// Map is empty.
// This will order properties by their name if the value is
// a ChainedMap that used .before() or .after().
// returns: Object, undefined if empty
entries()
```

```js
// Provide an object which maps its properties and values
// into the backing Map as keys and values.
// You can also provide an array as the second argument
// for property names to omit from being merged.
// obj: Object
// omit: Optional Array
merge(obj, omit)
```

```js
// Execute a function against the current configuration context
// handler: Function -> ChainedMap
  // A function which is given a single argument of the ChainedMap instance
batch(handler)
```

```js
// Conditionally execute a function to continue configuration
// condition: Boolean
// whenTruthy: Function -> ChainedMap
  // invoked when condition is truthy, given a single argument of the ChainedMap instance
// whenFalsy: Optional Function -> ChainedMap
  // invoked when condition is falsy, given a single argument of the ChainedMap instance
when(condition, whenTruthy, whenFalsy)
```

## ChainedSet

Another of the core API interfaces in webpack-chain is a `ChainedSet`. A
`ChainedSet` operates similar to a JavaScript Set, with some conveniences for
chaining and generating configuration. If a property is marked as being a
`ChainedSet`, it will have an API and methods as described below:

**Unless stated otherwise, these methods will return the `ChainedSet`, allowing
you to chain these methods.**

```js
// Add/append a value to the end of a Set.
// value: *
add(value)
```

```js
// Add a value to the beginning of a Set.
// value: *
prepend(value)
```

```js
// Remove all values from a Set.
clear()
```

```js
// Remove a specific value from a Set.
// value: *
delete(value)
```

```js
// Returns `true` or `false` based on whether or not the
// backing Set contains the specified value.
// value: *
// returns: Boolean
has(value)
```

```js
// Returns an array of values contained in the backing Set.
// returns: Array
values()
```

```js
// Concatenates the given array to the end of the backing Set.
// arr: Array
merge(arr)
```

```js
// Execute a function against the current configuration context
// handler: Function -> ChainedSet
  // A function which is given a single argument of the ChainedSet instance
batch(handler)
```

```js
// Conditionally execute a function to continue configuration
// condition: Boolean
// whenTruthy: Function -> ChainedSet
  // invoked when condition is truthy, given a single argument of the ChainedSet instance
// whenFalsy: Optional Function -> ChainedSet
  // invoked when condition is falsy, given a single argument of the ChainedSet instance
when(condition, whenTruthy, whenFalsy)
```

## Shorthand methods

A number of shorthand methods exist for setting a value on a `ChainedMap`
with the same key as the shorthand method name.
For example, `devServer.hot` is a shorthand method, so it can be used as:

```js
// A shorthand method for setting a value on a ChainedMap
devServer.hot(true);

// This would be equivalent to:
devServer.set('hot', true);
```

A shorthand method is chainable, so calling it will return the original
instance, allowing you to continue to chain.

### Config

Create a new configuration object.

```js
const Config = require('webpack-chain');

const config = new Config();
```

Moving to deeper points in the API will change the context of what you
are modifying. You can move back to the higher context by either referencing
the top-level `config` again, or by calling `.end()` to move up one level.
If you are familiar with jQuery, `.end()` works similarly. All API calls
will return the API instance at the current context unless otherwise
specified. This is so you may chain API calls continuously if desired.

For details on the specific values that are valid for all shorthand and
low-level methods, please refer to their corresponding name in the
[webpack docs hierarchy](https://webpack.js.org/configuration/).

```js
Config : ChainedMap
```

#### Config shorthand methods

```js
config
  .amd(amd)
  .bail(bail)
  .cache(cache)
  .devtool(devtool)
  .context(context)
  .externals(externals)
  .loader(loader)
  .name(name)
  .mode(mode)
  .parallelism(parallelism)
  .profile(profile)
  .recordsPath(recordsPath)
  .recordsInputPath(recordsInputPath)
  .recordsOutputPath(recordsOutputPath)
  .stats(stats)
  .target(target)
  .watch(watch)
  .watchOptions(watchOptions)
```

#### Config entryPoints

```js
// Backed at config.entryPoints : ChainedMap
config.entry(name) : ChainedSet

config
  .entry(name)
    .add(value)
    .add(value)

config
  .entry(name)
    .clear()

// Using low-level config.entryPoints:

config.entryPoints
  .get(name)
    .add(value)
    .add(value)

config.entryPoints
  .get(name)
    .clear()
```

#### Config output: shorthand methods

```js
config.output : ChainedMap

config.output
  .auxiliaryComment(auxiliaryComment)
  .chunkFilename(chunkFilename)
  .chunkLoadTimeout(chunkLoadTimeout)
  .crossOriginLoading(crossOriginLoading)
  .devtoolFallbackModuleFilenameTemplate(devtoolFallbackModuleFilenameTemplate)
  .devtoolLineToLine(devtoolLineToLine)
  .devtoolModuleFilenameTemplate(devtoolModuleFilenameTemplate)
  .devtoolNamespace(devtoolNamespace)
  .filename(filename)
  .hashFunction(hashFunction)
  .hashDigest(hashDigest)
  .hashDigestLength(hashDigestLength)
  .hashSalt(hashSalt)
  .hotUpdateChunkFilename(hotUpdateChunkFilename)
  .hotUpdateFunction(hotUpdateFunction)
  .hotUpdateMainFilename(hotUpdateMainFilename)
  .jsonpFunction(jsonpFunction)
  .library(library)
  .libraryExport(libraryExport)
  .libraryTarget(libraryTarget)
  .path(path)
  .pathinfo(pathinfo)
  .publicPath(publicPath)
  .sourceMapFilename(sourceMapFilename)
  .sourcePrefix(sourcePrefix)
  .strictModuleExceptionHandling(strictModuleExceptionHandling)
  .umdNamedDefine(umdNamedDefine)
```

#### Config resolve: shorthand methods

```js
config.resolve : ChainedMap

config.resolve
  .cachePredicate(cachePredicate)
  .cacheWithContext(cacheWithContext)
  .enforceExtension(enforceExtension)
  .enforceModuleExtension(enforceModuleExtension)
  .unsafeCache(unsafeCache)
  .symlinks(symlinks)
```

#### Config resolve alias

```js
config.resolve.alias : ChainedMap

config.resolve.alias
  .set(key, value)
  .set(key, value)
  .delete(key)
  .clear()
```

#### Config resolve modules

```js
config.resolve.modules : ChainedSet

config.resolve.modules
  .add(value)
  .prepend(value)
  .clear()
```

#### Config resolve aliasFields

```js
config.resolve.aliasFields : ChainedSet

config.resolve.aliasFields
  .add(value)
  .prepend(value)
  .clear()
```

#### Config resolve descriptionFields

```js
config.resolve.descriptionFields : ChainedSet

config.resolve.descriptionFields
  .add(value)
  .prepend(value)
  .clear()
```

#### Config resolve extensions

```js
config.resolve.extensions : ChainedSet

config.resolve.extensions
  .add(value)
  .prepend(value)
  .clear()
```

#### Config resolve mainFields

```js
config.resolve.mainFields : ChainedSet

config.resolve.mainFields
  .add(value)
  .prepend(value)
  .clear()
```

#### Config resolve mainFiles

```js
config.resolve.mainFiles : ChainedSet

config.resolve.mainFiles
  .add(value)
  .prepend(value)
  .clear()
```

#### Config resolveLoader

The API for `config.resolveLoader` is identical to `config.resolve` with
the following additions:

#### Config resolveLoader moduleExtensions

```js
config.resolveLoader.moduleExtensions : ChainedSet

config.resolveLoader.moduleExtensions
  .add(value)
  .prepend(value)
  .clear()
```

#### Config resolveLoader packageMains

```js
config.resolveLoader.packageMains : ChainedSet

config.resolveLoader.packageMains
  .add(value)
  .prepend(value)
  .clear()
```

#### Config performance: shorthand methods

```js
config.performance : ChainedMap

config.performance
  .hints(hints)
  .maxEntrypointSize(maxEntrypointSize)
  .maxAssetSize(maxAssetSize)
  .assetFilter(assetFilter)
```

#### Configuring optimizations: shorthand methods

```js
config.optimization : ChainedMap

config.optimization
  .concatenateModules(concatenateModules)
  .flagIncludedChunks(flagIncludedChunks)
  .mergeDuplicateChunks(mergeDuplicateChunks)
  .minimize(minimize)
  .namedChunks(namedChunks)
  .namedModules(namedModules)
  .nodeEnv(nodeEnv)
  .noEmitOnErrors(noEmitOnErrors)
  .occurrenceOrder(occurrenceOrder)
  .portableRecords(portableRecords)
  .providedExports(providedExports)
  .removeAvailableModules(removeAvailableModules)
  .removeEmptyChunks(removeEmptyChunks)
  .runtimeChunk(runtimeChunk)
  .sideEffects(sideEffects)
  .splitChunks(splitChunks)
  .usedExports(usedExports)
```

#### Config optimization minimizers

```js
// Backed at config.optimization.minimizers
config.optimization
  .minimizer(name) : ChainedMap
```

#### Config optimization minimizers: adding

_NOTE: Do not use `new` to create the minimizer plugin, as this will be done for you._

```js
config.optimization
  .minimizer(name)
  .use(WebpackPlugin, args)

// Examples

config.optimization
  .minimizer('css')
  .use(OptimizeCSSAssetsPlugin, [{ cssProcessorOptions: { safe: true } }])

// Minimizer plugins can also be specified by their path, allowing the expensive require()s to be
// skipped in cases where the plugin or webpack configuration won't end up being used.
config.optimization
  .minimizer('css')
  .use(require.resolve('optimize-css-assets-webpack-plugin'), [{ cssProcessorOptions: { safe: true } }])

```

#### Config optimization minimizers: modify arguments

```js
config.optimization
  .minimizer(name)
  .tap(args => newArgs)

// Example
config.optimization
  .minimizer('css')
  .tap(args => [...args, { cssProcessorOptions: { safe: false } }])
```

#### Config optimization minimizers: modify instantiation

```js
config.optimization
  .minimizer(name)
  .init((Plugin, args) => new Plugin(...args));
```

#### Config optimization minimizers: removing

```js
config.optimization.minimizers.delete(name)
```

#### Config plugins

```js
// Backed at config.plugins
config.plugin(name) : ChainedMap
```

#### Config plugins: adding

_NOTE: Do not use `new` to create the plugin, as this will be done for you._

```js
config
  .plugin(name)
  .use(WebpackPlugin, args)

// Examples

config
  .plugin('hot')
  .use(webpack.HotModuleReplacementPlugin);

// Plugins can also be specified by their path, allowing the expensive require()s to be
// skipped in cases where the plugin or webpack configuration won't end up being used.
config
  .plugin('env')
  .use(require.resolve('webpack/lib/EnvironmentPlugin'), [{ 'VAR': false }]);
```

#### Config plugins: modify arguments

```js
config
  .plugin(name)
  .tap(args => newArgs)

// Example
config
  .plugin('env')
  .tap(args => [...args, 'SECRET_KEY']);
```

#### Config plugins: modify instantiation

```js
config
  .plugin(name)
  .init((Plugin, args) => new Plugin(...args));
```

#### Config plugins: removing

```js
config.plugins.delete(name)
```

#### Config plugins: ordering before

Specify that the current `plugin` context should operate before another named
`plugin`. You cannot use both `.before()` and `.after()` on the same plugin.

```js
config
  .plugin(name)
    .before(otherName)

// Example

config
  .plugin('html-template')
    .use(HtmlWebpackTemplate)
    .end()
  .plugin('script-ext')
    .use(ScriptExtWebpackPlugin)
    .before('html-template');
```

#### Config plugins: ordering after

Specify that the current `plugin` context should operate after another named
`plugin`. You cannot use both `.before()` and `.after()` on the same plugin.

```js
config
  .plugin(name)
    .after(otherName)

// Example

config
  .plugin('html-template')
    .after('script-ext')
    .use(HtmlWebpackTemplate)
    .end()
  .plugin('script-ext')
    .use(ScriptExtWebpackPlugin);
```

#### Config resolve plugins

```js
// Backed at config.resolve.plugins
config.resolve.plugin(name) : ChainedMap
```

#### Config resolve plugins: adding

_NOTE: Do not use `new` to create the plugin, as this will be done for you._

```js
config.resolve
  .plugin(name)
  .use(WebpackPlugin, args)
```

#### Config resolve plugins: modify arguments

```js
config.resolve
  .plugin(name)
  .tap(args => newArgs)
```

#### Config resolve plugins: modify instantiation

```js
config.resolve
  .plugin(name)
  .init((Plugin, args) => new Plugin(...args))
```

#### Config resolve plugins: removing

```js
config.resolve.plugins.delete(name)
```

#### Config resolve plugins: ordering before

Specify that the current `plugin` context should operate before another named
`plugin`. You cannot use both `.before()` and `.after()` on the same resolve
plugin.

```js
config.resolve
  .plugin(name)
    .before(otherName)

// Example

config.resolve
  .plugin('beta')
    .use(BetaWebpackPlugin)
    .end()
  .plugin('alpha')
    .use(AlphaWebpackPlugin)
    .before('beta');
```

#### Config resolve plugins: ordering after

Specify that the current `plugin` context should operate after another named
`plugin`. You cannot use both `.before()` and `.after()` on the same resolve
plugin.

```js
config.resolve
  .plugin(name)
    .after(otherName)

// Example

config.resolve
  .plugin('beta')
    .after('alpha')
    .use(BetaWebpackTemplate)
    .end()
  .plugin('alpha')
    .use(AlphaWebpackPlugin);
```

#### Config node

```js
config.node : ChainedMap

config.node
  .set('__dirname', 'mock')
  .set('__filename', 'mock');
```

#### Config devServer

```js
config.devServer : ChainedMap
```

#### Config devServer allowedHosts

```js
config.devServer.allowedHosts : ChainedSet

config.devServer.allowedHosts
  .add(value)
  .prepend(value)
  .clear()
```

#### Config devServer: shorthand methods

```js
config.devServer
  .after(after)
  .before(before)
  .bonjour(bonjour)
  .clientLogLevel(clientLogLevel)
  .color(color)
  .compress(compress)
  .contentBase(contentBase)
  .disableHostCheck(disableHostCheck)
  .filename(filename)
  .headers(headers)
  .historyApiFallback(historyApiFallback)
  .host(host)
  .hot(hot)
  .hotOnly(hotOnly)
  .http2(http2)
  .https(https)
  .index(index)
  .info(info)
  .inline(inline)
  .lazy(lazy)
  .mimeTypes(mimeTypes)
  .noInfo(noInfo)
  .open(open)
  .openPage(openPage)
  .overlay(overlay)
  .pfx(pfx)
  .pfxPassphrase(pfxPassphrase)
  .port(port)
  .progress(progress)
  .proxy(proxy)
  .public(public)
  .publicPath(publicPath)
  .quiet(quiet)
  .setup(setup)
  .socket(socket)
  .sockHost(sockHost)
  .sockPath(sockPath)
  .sockPort(sockPort)
  .staticOptions(staticOptions)
  .stats(stats)
  .stdin(stdin)
  .useLocalIp(useLocalIp)
  .watchContentBase(watchContentBase)
  .watchOptions(watchOptions)
  .writeToDisk(writeToDisk)
```

#### Config module

```js
config.module : ChainedMap
```

#### Config module: shorthand methods

```js
config.module : ChainedMap

config.module
  .noParse(noParse)
```

#### Config module rules: shorthand methods

```js
config.module.rules : ChainedMap

config.module
  .rule(name)
    .test(test)
    .pre()
    .post()
    .enforce(preOrPost)
```

#### Config module rules uses (loaders): creating

```js
config.module.rules{}.uses : ChainedMap

config.module
  .rule(name)
    .use(name)
      .loader(loader)
      .options(options)

// Example

config.module
  .rule('compile')
    .use('babel')
      .loader('babel-loader')
      .options({ presets: ['@babel/preset-env'] });
```

#### Config module rules uses (loaders): modifying options

```js
config.module
  .rule(name)
    .use(name)
      .tap(options => newOptions)

// Example

config.module
  .rule('compile')
    .use('babel')
      .tap(options => merge(options, {
        plugins: ['@babel/plugin-proposal-class-properties']
      }));
```

#### Config module rules nested rules:

```js
config.module.rules{}.rules : ChainedMap<Rule>

config.module
  .rule(name)
    .rule(name)

// Example

config.module
  .rule('css')
    .test(/\.css$/)
    .use('style')
      .loader('style-loader')
      .end()
    .rule('postcss')
      .resourceQuery(/postcss/)
      .use('postcss')
        .loader('postcss-loader')
```

#### Config module rules nested rules: ordering before
Specify that the current `rule` context should operate before another named
`rule`. You cannot use both `.before()` and `.after()` on the same `rule`.

```js
config.module.rules{}.rules : ChainedMap<Rule>

config.module
  .rule(name)
    .rule(name)
      .before(otherName)

// Example

config.module
  .rule('css')
    .use('style')
      .loader('style-loader')
      .end()
    .rule('postcss')
      .resourceQuery(/postcss/)
      .use('postcss')
        .loader('postcss-loader')
        .end()
      .end()
    .rule('css-loader')
      .resourceQuery(/css-loader/)
      .before('postcss')
      .use('css-loader')
        .loader('css-loader')
```

#### Config module rules nested rules: ordering after
Specify that the current `rule` context should operate after another named
`rule`. You cannot use both `.before()` and `.after()` on the same `rule`.

```js
config.module.rules{}.rules : ChainedMap<Rule>

config.module
  .rule(name)
    .rule(name)
      .after(otherName)

// Example

config.module
  .rule('css')
    .use('style')
      .loader('style-loader')
      .end()
    .rule('postcss')
      .resourceQuery(/postcss/)
      .after('css-loader')
      .use('postcss')
        .loader('postcss-loader')
        .end()
      .end()
    .rule('css-loader')
      .resourceQuery(/css-loader/)
      .use('css-loader')
        .loader('css-loader')
```

#### Config module rules oneOfs (conditional rules):

```js
config.module.rules{}.oneOfs : ChainedMap<Rule>

config.module
  .rule(name)
    .oneOf(name)

// Example

config.module
  .rule('css')
    .oneOf('inline')
      .resourceQuery(/inline/)
      .use('url')
        .loader('url-loader')
        .end()
      .end()
    .oneOf('external')
      .resourceQuery(/external/)
      .use('file')
        .loader('file-loader')
```

#### Config module rules oneOfs (conditional rules): ordering before
Specify that the current `oneOf` context should operate before another named
`oneOf`. You cannot use both `.before()` and `.after()` on the same `oneOf`.

```js
config.module
  .rule(name)
    .oneOf(name)
      .before()

// Example

config.module
  .rule('scss')
    .test(/\.scss$/)
    .oneOf('normal')
      .use('sass')
        .loader('sass-loader')
        .end()
      .end()
    .oneOf('sass-vars')
      .before('normal')
      .resourceQuery(/\?sassvars/)
      .use('sass-vars')
        .loader('sass-vars-to-js-loader')
```

#### Config module rules oneOfs (conditional rules): ordering after
Specify that the current `oneOf` context should operate after another named
`oneOf`. You cannot use both `.before()` and `.after()` on the same `oneOf`.

```js
config.module
  .rule(name)
    .oneOf(name)
      .after()

// Example

config.module
  .rule('scss')
    .test(/\.scss$/)
    .oneOf('vue')
      .resourceQuery(/\?vue/)
      .use('vue-style')
        .loader('vue-style-loader')
        .end()
      .end()
    .oneOf('normal')
      .use('sass')
        .loader('sass-loader')
        .end()
      .end()
    .oneOf('sass-vars')
      .after('vue')
      .resourceQuery(/\?sassvars/)
      .use('sass-vars')
        .loader('sass-vars-to-js-loader')
```

#### Config module rules resolve

Specify a resolve configuration to be merged over the default `config.resolve`
for modules that match the rule.

See "Config resolve" sections above for full syntax.

**Note:** This option is supported by webpack since 4.36.1.

```js
config.module
  .rule(name)
    .resolve

// Example

config.module
  .rule('scss')
    .test(/\.scss$/)
    .resolve
      .symlinks(true)
```

---

### Merging Config

webpack-chain supports merging in an object to the configuration instance which
matches a layout similar to how the webpack-chain schema is laid out.

**Note:** This object does not match the webpack configuration schema exactly
(for example the `[name]` keys for entry/rules/plugins), so you may need to transform
webpack configuration objects (such as those output by webpack-chain's `.toConfig()`)
to match the layout below prior to passing to `.merge()`.

```js
config.merge({ devtool: 'source-map' });

config.get('devtool') // "source-map"
```

```js
config.merge({
  [key]: value,

  amd,
  bail,
  cache,
  context,
  devtool,
  externals,
  loader,
  mode,
  parallelism,
  profile,
  recordsPath,
  recordsInputPath,
  recordsOutputPath,
  stats,
  target,
  watch,
  watchOptions,

  entry: {
    [name]: [...values]
  },

  plugin: {
    [name]: {
      plugin: WebpackPlugin,
      args: [...args],
      before,
      after
    }
  },

  devServer: {
    [key]: value,

    clientLogLevel,
    compress,
    contentBase,
    filename,
    headers,
    historyApiFallback,
    host,
    hot,
    hotOnly,
    https,
    inline,
    lazy,
    noInfo,
    overlay,
    port,
    proxy,
    quiet,
    setup,
    stats,
    watchContentBase
  },

  node: {
    [key]: value
  },

  optimization: {
    concatenateModules,
    flagIncludedChunks,
    mergeDuplicateChunks,
    minimize,
    minimizer: {
      [name]: {
        plugin: WebpackPlugin,
        args: [...args],
        before,
        after
      }
    },
    namedChunks,
    namedModules,
    nodeEnv,
    noEmitOnErrors,
    occurrenceOrder,
    portableRecords,
    providedExports,
    removeAvailableModules,
    removeEmptyChunks,
    runtimeChunk,
    sideEffects,
    splitChunks,
    usedExports,
  },

  performance: {
    [key]: value,

    hints,
    maxEntrypointSize,
    maxAssetSize,
    assetFilter
  },

  resolve: {
    [key]: value,

    alias: {
      [key]: value
    },
    aliasFields: [...values],
    descriptionFields: [...values],
    extensions: [...values],
    mainFields: [...values],
    mainFiles: [...values],
    modules: [...values],

    plugin: {
      [name]: {
        plugin: WebpackPlugin,
        args: [...args],
        before,
        after
      }
    }
  },

  resolveLoader: {
    [key]: value,

    alias: {
      [key]: value
    },
    aliasFields: [...values],
    descriptionFields: [...values],
    extensions: [...values],
    mainFields: [...values],
    mainFiles: [...values],
    modules: [...values],
    moduleExtensions: [...values],
    packageMains: [...values],

    plugin: {
      [name]: {
        plugin: WebpackPlugin,
        args: [...args],
        before,
        after
      }
    }
  },

  module: {
    [key]: value,

    rule: {
      [name]: {
        [key]: value,

        enforce,
        issuer,
        parser,
        resource,
        resourceQuery,
        test,

        include: [...paths],
        exclude: [...paths],

        rules: {
          [name]: Rule
        },

        oneOf: {
          [name]: Rule
        },

        use: {
          [name]: {
            loader: LoaderString,
            options: LoaderOptions,
            before,
            after
          }
        }
      }
    }
  }
})
```

### Conditional configuration

When working with instances of `ChainedMap` and `ChainedSet`, you can perform
conditional configuration using `when`. You must specify an expression to
`when()` which will be evaluated for truthiness or falsiness. If the expression
is truthy, the first function argument will be invoked with an instance of the
current chained instance. You can optionally provide a second function to be
invoked when the condition is falsy, which is also given the current chained
instance.

```js
// Example: Only add minify plugin during production
config
  .when(process.env.NODE_ENV === 'production', config => {
    config
      .plugin('minify')
      .use(BabiliWebpackPlugin);
  });
```

```js
// Example: Only add minify plugin during production,
// otherwise set devtool to source-map
config
  .when(process.env.NODE_ENV === 'production',
    config => config.plugin('minify').use(BabiliWebpackPlugin),
    config => config.devtool('source-map')
  );
```

### Inspecting generated configuration

You can inspect the generated webpack config using `config.toString()`. This
will generate a stringified version of the config with comment hints for named
rules, uses and plugins:

```js
config
  .module
    .rule('compile')
      .test(/\.js$/)
      .use('babel')
        .loader('babel-loader');

config.toString();

/*
{
  module: {
    rules: [
      /* config.module.rule('compile') */
      {
        test: /\.js$/,
        use: [
          /* config.module.rule('compile').use('babel') */
          {
            loader: 'babel-loader'
          }
        ]
      }
    ]
  }
}
*/
```

By default the generated string cannot be used directly as real webpack config
if it contains objects and plugins that need to be required. In order to
generate usable config, you can customize how objects and plugins are
stringified by setting a special `__expression` property on them:

```js
const sass = require('sass');
sass.__expression = `require('sass')`;

class MyPlugin {}
MyPlugin.__expression = `require('my-plugin')`;

function myFunction () {}
myFunction.__expression = `require('my-function')`;

config
  .plugin('example')
    .use(MyPlugin, [{ fn: myFunction, implementation: sass, }]);

config.toString();

/*
{
  plugins: [
    new (require('my-plugin'))({
      fn: require('my-function'),
      implementation: require('sass')
    })
  ]
}
*/
```

Plugins specified via their path will have their `require()` statement generated
automatically:

```js
config
  .plugin('env')
    .use(require.resolve('webpack/lib/ProvidePlugin'), [{ jQuery: 'jquery' }])

config.toString();

/*
{
  plugins: [
    new (require('/foo/bar/src/node_modules/webpack/lib/EnvironmentPlugin.js'))(
      {
        jQuery: 'jquery'
      }
    )
  ]
}
*/
```

You can also call `toString` as a static method on `Config` in order to
modify the configuration object prior to stringifying.

```js
Config.toString({
  ...config.toConfig(),
  module: {
    defaultRules: [
      {
        use: [
          {
            loader: 'banner-loader',
            options: { prefix: 'banner-prefix.txt' },
          },
        ],
      },
    ],
  },
})
```

```
{
  plugins: [
    /* config.plugin('foo') */
    new TestPlugin()
  ],
  module: {
    defaultRules: [
      {
        use: [
          {
            loader: 'banner-loader',
            options: {
              prefix: 'banner-prefix.txt'
            }
          }
        ]
      }
    ]
  }
}
```

[npm-image]: https://img.shields.io/npm/v/webpack-chain.svg
[npm-downloads]: https://img.shields.io/npm/dt/webpack-chain.svg
[npm-url]: https://www.npmjs.com/package/webpack-chain
[travis-image]: https://api.travis-ci.com/neutrinojs/webpack-chain.svg?branch=master
[travis-url]: https://travis-ci.com/neutrinojs/webpack-chain

license: mit


webpack-chain

repository: neutrinojs/webpack-chain

keywords: webpack config chain fluent api

author: Eli Perelman <eli@eliperelman.com>

fix: yarn lint:fix && yarn style:fix

lint: eslint --cache --max-warnings 0 --format codeframe .

lint:fix: yarn lint --fix

style: prettier --check .

style:fix: prettier --write .

test: jest

test:types: tsc -p ./types/test/tsconfig.json

changelog: auto-changelog --remote upstream --commit-limit false

version: yarn changelog --package && git add CHANGELOG.md


code:
module.exports = class {
  constructor(parent) {
    this.parent = parent;
  }

  batch(handler) {
    handler(this);
    return this;
  }

  end() {
    return this.parent;
  }
};


code:
const merge = require('deepmerge');
const Chainable = require('./Chainable');

module.exports = class extends Chainable {
  constructor(parent) {
    super(parent);
    this.store = new Map();
  }

  extend(methods) {
    this.shorthands = methods;
    methods.forEach((method) => {
      this[method] = (value) => this.set(method, value);
    });
    return this;
  }

  clear() {
    this.store.clear();
    return this;
  }

  delete(key) {
    this.store.delete(key);
    return this;
  }

  order() {
    const entries = [...this.store].reduce((acc, [key, value]) => {
      acc[key] = value;
      return acc;
    }, {});
    const names = Object.keys(entries);
    const order = [...names];

    names.forEach((name) => {
      if (!entries[name]) {
        return;
      }

      const { __before, __after } = entries[name];

      if (__before && order.includes(__before)) {
        order.splice(order.indexOf(name), 1);
        order.splice(order.indexOf(__before), 0, name);
      } else if (__after && order.includes(__after)) {
        order.splice(order.indexOf(name), 1);
        order.splice(order.indexOf(__after) + 1, 0, name);
      }
    });

    return { entries, order };
  }

  entries() {
    const { entries, order } = this.order();

    if (order.length) {
      return entries;
    }

    return undefined;
  }

  values() {
    const { entries, order } = this.order();

    return order.map((name) => entries[name]);
  }

  get(key) {
    return this.store.get(key);
  }

  getOrCompute(key, fn) {
    if (!this.has(key)) {
      this.set(key, fn());
    }
    return this.get(key);
  }

  has(key) {
    return this.store.has(key);
  }

  set(key, value) {
    this.store.set(key, value);
    return this;
  }

  merge(obj, omit = []) {
    Object.keys(obj).forEach((key) => {
      if (omit.includes(key)) {
        return;
      }

      const value = obj[key];

      if (
        (!Array.isArray(value) && typeof value !== 'object') ||
        value === null ||
        !this.has(key)
      ) {
        this.set(key, value);
      } else {
        this.set(key, merge(this.get(key), value));
      }
    });

    return this;
  }

  clean(obj) {
    return Object.keys(obj).reduce((acc, key) => {
      const value = obj[key];

      if (value === undefined) {
        return acc;
      }

      if (Array.isArray(value) && !value.length) {
        return acc;
      }

      if (
        Object.prototype.toString.call(value) === '[object Object]' &&
        !Object.keys(value).length
      ) {
        return acc;
      }

      acc[key] = value;

      return acc;
    }, {});
  }

  when(
    condition,
    whenTruthy = Function.prototype,
    whenFalsy = Function.prototype,
  ) {
    if (condition) {
      whenTruthy(this);
    } else {
      whenFalsy(this);
    }

    return this;
  }
};


code:
const Chainable = require('./Chainable');

module.exports = class extends Chainable {
  constructor(parent) {
    super(parent);
    this.store = new Set();
  }

  add(value) {
    this.store.add(value);
    return this;
  }

  prepend(value) {
    this.store = new Set([value, ...this.store]);
    return this;
  }

  clear() {
    this.store.clear();
    return this;
  }

  delete(value) {
    this.store.delete(value);
    return this;
  }

  values() {
    return [...this.store];
  }

  has(value) {
    return this.store.has(value);
  }

  merge(arr) {
    this.store = new Set([...this.store, ...arr]);
    return this;
  }

  when(
    condition,
    whenTruthy = Function.prototype,
    whenFalsy = Function.prototype,
  ) {
    if (condition) {
      whenTruthy(this);
    } else {
      whenFalsy(this);
    }

    return this;
  }
};


code:
const ChainedMap = require('./ChainedMap');
const ChainedSet = require('./ChainedSet');
const Resolve = require('./Resolve');
const ResolveLoader = require('./ResolveLoader');
const Output = require('./Output');
const DevServer = require('./DevServer');
const Plugin = require('./Plugin');
const Module = require('./Module');
const Optimization = require('./Optimization');
const Performance = require('./Performance');

module.exports = class extends ChainedMap {
  constructor() {
    super();
    this.devServer = new DevServer(this);
    this.entryPoints = new ChainedMap(this);
    this.module = new Module(this);
    this.node = new ChainedMap(this);
    this.optimization = new Optimization(this);
    this.output = new Output(this);
    this.performance = new Performance(this);
    this.plugins = new ChainedMap(this);
    this.resolve = new Resolve(this);
    this.resolveLoader = new ResolveLoader(this);
    this.extend([
      'amd',
      'bail',
      'cache',
      'context',
      'devtool',
      'externals',
      'loader',
      'mode',
      'name',
      'parallelism',
      'profile',
      'recordsInputPath',
      'recordsPath',
      'recordsOutputPath',
      'stats',
      'target',
      'watch',
      'watchOptions',
    ]);
  }

  static toString(config, { verbose = false, configPrefix = 'config' } = {}) {
    // eslint-disable-next-line global-require
    const { stringify } = require('javascript-stringify');

    return stringify(
      config,
      (value, indent, stringify) => {
        // improve plugin output
        if (value && value.__pluginName) {
          const prefix = `/* ${configPrefix}.${value.__pluginType}('${value.__pluginName}') */\n`;
          const constructorExpression = value.__pluginPath
            ? // The path is stringified to ensure special characters are escaped
              // (such as the backslashes in Windows-style paths).
              `(require(${stringify(value.__pluginPath)}))`
            : value.__pluginConstructorName;

          if (constructorExpression) {
            // get correct indentation for args by stringifying the args array and
            // discarding the square brackets.
            const args = stringify(value.__pluginArgs).slice(1, -1);
            return `${prefix}new ${constructorExpression}(${args})`;
          }
          return (
            prefix +
            stringify(
              value.__pluginArgs && value.__pluginArgs.length
                ? { args: value.__pluginArgs }
                : {},
            )
          );
        }

        // improve rule/use output
        if (value && value.__ruleNames) {
          const ruleTypes = value.__ruleTypes;
          const prefix = `/* ${configPrefix}.module${value.__ruleNames
            .map(
              (r, index) => `.${ruleTypes ? ruleTypes[index] : 'rule'}('${r}')`,
            )
            .join('')}${
            value.__useName ? `.use('${value.__useName}')` : ``
          } */\n`;
          return prefix + stringify(value);
        }

        if (value && value.__expression) {
          return value.__expression;
        }

        // shorten long functions
        if (typeof value === 'function') {
          if (!verbose && value.toString().length > 100) {
            return `function () { /* omitted long function */ }`;
          }
        }

        return stringify(value);
      },
      2,
    );
  }

  entry(name) {
    return this.entryPoints.getOrCompute(name, () => new ChainedSet(this));
  }

  plugin(name) {
    return this.plugins.getOrCompute(name, () => new Plugin(this, name));
  }

  toConfig() {
    const entryPoints = this.entryPoints.entries() || {};

    return this.clean(
      Object.assign(this.entries() || {}, {
        node: this.node.entries(),
        output: this.output.entries(),
        resolve: this.resolve.toConfig(),
        resolveLoader: this.resolveLoader.toConfig(),
        devServer: this.devServer.toConfig(),
        module: this.module.toConfig(),
        optimization: this.optimization.toConfig(),
        plugins: this.plugins.values().map((plugin) => plugin.toConfig()),
        performance: this.performance.entries(),
        entry: Object.keys(entryPoints).reduce(
          (acc, key) =>
            Object.assign(acc, { [key]: entryPoints[key].values() }),
          {},
        ),
      }),
    );
  }

  toString(options) {
    return module.exports.toString(this.toConfig(), options);
  }

  merge(obj = {}, omit = []) {
    const omissions = [
      'node',
      'output',
      'resolve',
      'resolveLoader',
      'devServer',
      'optimization',
      'performance',
      'module',
    ];

    if (!omit.includes('entry') && 'entry' in obj) {
      Object.keys(obj.entry).forEach((name) =>
        this.entry(name).merge([].concat(obj.entry[name])),
      );
    }

    if (!omit.includes('plugin') && 'plugin' in obj) {
      Object.keys(obj.plugin).forEach((name) =>
        this.plugin(name).merge(obj.plugin[name]),
      );
    }

    omissions.forEach((key) => {
      if (!omit.includes(key) && key in obj) {
        this[key].merge(obj[key]);
      }
    });

    return super.merge(obj, [...omit, ...omissions, 'entry', 'plugin']);
  }
};


code:
const ChainedMap = require('./ChainedMap');
const ChainedSet = require('./ChainedSet');

module.exports = class extends ChainedMap {
  constructor(parent) {
    super(parent);

    this.allowedHosts = new ChainedSet(this);

    this.extend([
      'after',
      'before',
      'bonjour',
      'clientLogLevel',
      'color',
      'compress',
      'contentBase',
      'disableHostCheck',
      'filename',
      'headers',
      'historyApiFallback',
      'host',
      'hot',
      'hotOnly',
      'http2',
      'https',
      'index',
      'info',
      'inline',
      'lazy',
      'mimeTypes',
      'noInfo',
      'open',
      'openPage',
      'overlay',
      'pfx',
      'pfxPassphrase',
      'port',
      'proxy',
      'progress',
      'public',
      'publicPath',
      'quiet',
      'setup',
      'socket',
      'sockHost',
      'sockPath',
      'sockPort',
      'staticOptions',
      'stats',
      'stdin',
      'useLocalIp',
      'watchContentBase',
      'watchOptions',
      'writeToDisk',
    ]);
  }

  toConfig() {
    return this.clean({
      allowedHosts: this.allowedHosts.values(),
      ...(this.entries() || {}),
    });
  }

  merge(obj, omit = []) {
    if (!omit.includes('allowedHosts') && 'allowedHosts' in obj) {
      this.allowedHosts.merge(obj.allowedHosts);
    }

    return super.merge(obj, ['allowedHosts']);
  }
};


code:
const ChainedMap = require('./ChainedMap');
const Rule = require('./Rule');

module.exports = class extends ChainedMap {
  constructor(parent) {
    super(parent);
    this.rules = new ChainedMap(this);
    this.defaultRules = new ChainedMap(this);
    this.extend(['noParse', 'strictExportPresence']);
  }

  defaultRule(name) {
    return this.defaultRules.getOrCompute(
      name,
      () => new Rule(this, name, 'defaultRule'),
    );
  }

  rule(name) {
    return this.rules.getOrCompute(name, () => new Rule(this, name, 'rule'));
  }

  toConfig() {
    return this.clean(
      Object.assign(this.entries() || {}, {
        defaultRules: this.defaultRules.values().map((r) => r.toConfig()),
        rules: this.rules.values().map((r) => r.toConfig()),
      }),
    );
  }

  merge(obj, omit = []) {
    if (!omit.includes('rule') && 'rule' in obj) {
      Object.keys(obj.rule).forEach((name) =>
        this.rule(name).merge(obj.rule[name]),
      );
    }

    if (!omit.includes('defaultRule') && 'defaultRule' in obj) {
      Object.keys(obj.defaultRule).forEach((name) =>
        this.defaultRule(name).merge(obj.defaultRule[name]),
      );
    }

    return super.merge(obj, ['rule', 'defaultRule']);
  }
};


code:
const ChainedMap = require('./ChainedMap');
const Plugin = require('./Plugin');

module.exports = class extends ChainedMap {
  constructor(parent) {
    super(parent);
    this.minimizers = new ChainedMap(this);
    this.extend([
      'concatenateModules',
      'flagIncludedChunks',
      'mergeDuplicateChunks',
      'minimize',
      'namedChunks',
      'namedModules',
      'nodeEnv',
      'noEmitOnErrors',
      'occurrenceOrder',
      'portableRecords',
      'providedExports',
      'removeAvailableModules',
      'removeEmptyChunks',
      'runtimeChunk',
      'sideEffects',
      'splitChunks',
      'usedExports',
    ]);
  }

  minimizer(name) {
    if (Array.isArray(name)) {
      throw new Error(
        'optimization.minimizer() no longer supports being passed an array. ' +
          'Either switch to the new syntax (https://github.com/neutrinojs/webpack-chain#config-optimization-minimizers-adding) or downgrade to webpack-chain 4. ' +
          'If using Vue this likely means a Vue plugin has not yet been updated to support Vue CLI 4+.',
      );
    }

    return this.minimizers.getOrCompute(
      name,
      () => new Plugin(this, name, 'optimization.minimizer'),
    );
  }

  toConfig() {
    return this.clean(
      Object.assign(this.entries() || {}, {
        minimizer: this.minimizers.values().map((plugin) => plugin.toConfig()),
      }),
    );
  }

  merge(obj, omit = []) {
    if (!omit.includes('minimizer') && 'minimizer' in obj) {
      Object.keys(obj.minimizer).forEach((name) =>
        this.minimizer(name).merge(obj.minimizer[name]),
      );
    }

    return super.merge(obj, [...omit, 'minimizer']);
  }
};


code:
module.exports = (Class) =>
  class extends Class {
    before(name) {
      if (this.__after) {
        throw new Error(
          `Unable to set .before(${JSON.stringify(
            name,
          )}) with existing value for .after()`,
        );
      }

      this.__before = name;
      return this;
    }

    after(name) {
      if (this.__before) {
        throw new Error(
          `Unable to set .after(${JSON.stringify(
            name,
          )}) with existing value for .before()`,
        );
      }

      this.__after = name;
      return this;
    }

    merge(obj, omit = []) {
      if (obj.before) {
        this.before(obj.before);
      }

      if (obj.after) {
        this.after(obj.after);
      }

      return super.merge(obj, [...omit, 'before', 'after']);
    }
  };


code:
const ChainedMap = require('./ChainedMap');

module.exports = class extends ChainedMap {
  constructor(parent) {
    super(parent);
    this.extend([
      'auxiliaryComment',
      'chunkCallbackName',
      'chunkFilename',
      'chunkLoadTimeout',
      'crossOriginLoading',
      'devtoolFallbackModuleFilenameTemplate',
      'devtoolLineToLine',
      'devtoolModuleFilenameTemplate',
      'devtoolNamespace',
      'filename',
      'futureEmitAssets',
      'globalObject',
      'hashDigest',
      'hashDigestLength',
      'hashFunction',
      'hashSalt',
      'hotUpdateChunkFilename',
      'hotUpdateFunction',
      'hotUpdateMainFilename',
      'jsonpFunction',
      'library',
      'libraryExport',
      'libraryTarget',
      'path',
      'pathinfo',
      'publicPath',
      'sourceMapFilename',
      'sourcePrefix',
      'strictModuleExceptionHandling',
      'umdNamedDefine',
      'webassemblyModuleFilename',
    ]);
  }
};


code:
const ChainedMap = require('./ChainedMap');

module.exports = class extends ChainedMap {
  constructor(parent) {
    super(parent);
    this.extend(['assetFilter', 'hints', 'maxAssetSize', 'maxEntrypointSize']);
  }
};


code:
const ChainedMap = require('./ChainedMap');
const Orderable = require('./Orderable');

module.exports = Orderable(
  class extends ChainedMap {
    constructor(parent, name, type = 'plugin') {
      super(parent);
      this.name = name;
      this.type = type;
      this.extend(['init']);

      this.init((Plugin, args = []) => {
        if (typeof Plugin === 'function') {
          return new Plugin(...args);
        }
        return Plugin;
      });
    }

    use(plugin, args = []) {
      return this.set('plugin', plugin).set('args', args);
    }

    tap(f) {
      if (!this.has('plugin')) {
        throw new Error(
          `Cannot call .tap() on a plugin that has not yet been defined. Call ${this.type}('${this.name}').use(<Plugin>) first.`,
        );
      }
      this.set('args', f(this.get('args') || []));
      return this;
    }

    set(key, value) {
      if (key === 'args' && !Array.isArray(value)) {
        throw new Error('args must be an array of arguments');
      }
      return super.set(key, value);
    }

    merge(obj, omit = []) {
      if ('plugin' in obj) {
        this.set('plugin', obj.plugin);
      }

      if ('args' in obj) {
        this.set('args', obj.args);
      }

      return super.merge(obj, [...omit, 'args', 'plugin']);
    }

    toConfig() {
      const init = this.get('init');
      let plugin = this.get('plugin');
      const args = this.get('args');
      let pluginPath = null;

      if (plugin === undefined) {
        throw new Error(
          `Invalid ${this.type} configuration: ${this.type}('${this.name}').use(<Plugin>) was not called to specify the plugin`,
        );
      }

      // Support using the path to a plugin rather than the plugin itself,
      // allowing expensive require()s to be skipped in cases where the plugin
      // or webpack configuration won't end up being used.
      if (typeof plugin === 'string') {
        pluginPath = plugin;
        // eslint-disable-next-line global-require, import/no-dynamic-require
        plugin = require(pluginPath);
      }

      const constructorName = plugin.__expression
        ? `(${plugin.__expression})`
        : plugin.name;

      const config = init(plugin, args);

      Object.defineProperties(config, {
        __pluginName: { value: this.name },
        __pluginType: { value: this.type },
        __pluginArgs: { value: args },
        __pluginConstructorName: { value: constructorName },
        __pluginPath: { value: pluginPath },
      });

      return config;
    }
  },
);


code:
const ChainedMap = require('./ChainedMap');
const ChainedSet = require('./ChainedSet');
const Plugin = require('./Plugin');

module.exports = class extends ChainedMap {
  constructor(parent) {
    super(parent);
    this.alias = new ChainedMap(this);
    this.aliasFields = new ChainedSet(this);
    this.descriptionFiles = new ChainedSet(this);
    this.extensions = new ChainedSet(this);
    this.mainFields = new ChainedSet(this);
    this.mainFiles = new ChainedSet(this);
    this.modules = new ChainedSet(this);
    this.plugins = new ChainedMap(this);
    this.extend([
      'cachePredicate',
      'cacheWithContext',
      'concord',
      'enforceExtension',
      'enforceModuleExtension',
      'symlinks',
      'unsafeCache',
    ]);
  }

  plugin(name) {
    return this.plugins.getOrCompute(
      name,
      () => new Plugin(this, name, 'resolve.plugin'),
    );
  }

  toConfig() {
    return this.clean(
      Object.assign(this.entries() || {}, {
        alias: this.alias.entries(),
        aliasFields: this.aliasFields.values(),
        descriptionFiles: this.descriptionFiles.values(),
        extensions: this.extensions.values(),
        mainFields: this.mainFields.values(),
        mainFiles: this.mainFiles.values(),
        modules: this.modules.values(),
        plugins: this.plugins.values().map((plugin) => plugin.toConfig()),
      }),
    );
  }

  merge(obj, omit = []) {
    const omissions = [
      'alias',
      'aliasFields',
      'descriptionFiles',
      'extensions',
      'mainFields',
      'mainFiles',
      'modules',
    ];

    if (!omit.includes('plugin') && 'plugin' in obj) {
      Object.keys(obj.plugin).forEach((name) =>
        this.plugin(name).merge(obj.plugin[name]),
      );
    }

    omissions.forEach((key) => {
      if (!omit.includes(key) && key in obj) {
        this[key].merge(obj[key]);
      }
    });

    return super.merge(obj, [...omit, ...omissions, 'plugin']);
  }
};


code:
const Resolve = require('./Resolve');
const ChainedSet = require('./ChainedSet');

module.exports = class extends Resolve {
  constructor(parent) {
    super(parent);
    this.moduleExtensions = new ChainedSet(this);
    this.packageMains = new ChainedSet(this);
  }

  toConfig() {
    return this.clean({
      moduleExtensions: this.moduleExtensions.values(),
      packageMains: this.packageMains.values(),
      ...super.toConfig(),
    });
  }

  merge(obj, omit = []) {
    const omissions = ['moduleExtensions', 'packageMains'];

    omissions.forEach((key) => {
      if (!omit.includes(key) && key in obj) {
        this[key].merge(obj[key]);
      }
    });

    return super.merge(obj, [...omit, ...omissions]);
  }
};


code:
const ChainedMap = require('./ChainedMap');
const ChainedSet = require('./ChainedSet');
const Orderable = require('./Orderable');
const Use = require('./Use');
const Resolve = require('./Resolve');

function toArray(arr) {
  return Array.isArray(arr) ? arr : [arr];
}

const Rule = Orderable(
  class extends ChainedMap {
    constructor(parent, name, ruleType = 'rule') {
      super(parent);
      this.name = name;
      this.names = [];
      this.ruleType = ruleType;
      this.ruleTypes = [];

      let rule = this;
      while (rule instanceof Rule) {
        this.names.unshift(rule.name);
        this.ruleTypes.unshift(rule.ruleType);
        rule = rule.parent;
      }

      this.uses = new ChainedMap(this);
      this.include = new ChainedSet(this);
      this.exclude = new ChainedSet(this);
      this.rules = new ChainedMap(this);
      this.oneOfs = new ChainedMap(this);
      this.resolve = new Resolve(this);
      this.extend([
        'enforce',
        'issuer',
        'parser',
        'resource',
        'resourceQuery',
        'sideEffects',
        'test',
        'type',
      ]);
    }

    use(name) {
      return this.uses.getOrCompute(name, () => new Use(this, name));
    }

    rule(name) {
      return this.rules.getOrCompute(name, () => new Rule(this, name, 'rule'));
    }

    oneOf(name) {
      return this.oneOfs.getOrCompute(
        name,
        () => new Rule(this, name, 'oneOf'),
      );
    }

    pre() {
      return this.enforce('pre');
    }

    post() {
      return this.enforce('post');
    }

    toConfig() {
      const config = this.clean(
        Object.assign(this.entries() || {}, {
          include: this.include.values(),
          exclude: this.exclude.values(),
          rules: this.rules.values().map((rule) => rule.toConfig()),
          oneOf: this.oneOfs.values().map((oneOf) => oneOf.toConfig()),
          use: this.uses.values().map((use) => use.toConfig()),
          resolve: this.resolve.toConfig(),
        }),
      );

      Object.defineProperties(config, {
        __ruleNames: { value: this.names },
        __ruleTypes: { value: this.ruleTypes },
      });

      return config;
    }

    merge(obj, omit = []) {
      if (!omit.includes('include') && 'include' in obj) {
        this.include.merge(toArray(obj.include));
      }

      if (!omit.includes('exclude') && 'exclude' in obj) {
        this.exclude.merge(toArray(obj.exclude));
      }

      if (!omit.includes('use') && 'use' in obj) {
        Object.keys(obj.use).forEach((name) =>
          this.use(name).merge(obj.use[name]),
        );
      }

      if (!omit.includes('rules') && 'rules' in obj) {
        Object.keys(obj.rules).forEach((name) =>
          this.rule(name).merge(obj.rules[name]),
        );
      }

      if (!omit.includes('oneOf') && 'oneOf' in obj) {
        Object.keys(obj.oneOf).forEach((name) =>
          this.oneOf(name).merge(obj.oneOf[name]),
        );
      }

      if (!omit.includes('resolve') && 'resolve' in obj) {
        this.resolve.merge(obj.resolve);
      }

      if (!omit.includes('test') && 'test' in obj) {
        this.test(
          obj.test instanceof RegExp || typeof obj.test === 'function'
            ? obj.test
            : new RegExp(obj.test),
        );
      }

      return super.merge(obj, [
        ...omit,
        'include',
        'exclude',
        'use',
        'rules',
        'oneOf',
        'resolve',
        'test',
      ]);
    }
  },
);

module.exports = Rule;


code:
const merge = require('deepmerge');
const ChainedMap = require('./ChainedMap');
const Orderable = require('./Orderable');

module.exports = Orderable(
  class extends ChainedMap {
    constructor(parent, name) {
      super(parent);
      this.name = name;
      this.extend(['loader', 'options']);
    }

    tap(f) {
      this.options(f(this.get('options')));
      return this;
    }

    merge(obj, omit = []) {
      if (!omit.includes('loader') && 'loader' in obj) {
        this.loader(obj.loader);
      }

      if (!omit.includes('options') && 'options' in obj) {
        this.options(merge(this.store.get('options') || {}, obj.options));
      }

      return super.merge(obj, [...omit, 'loader', 'options']);
    }

    toConfig() {
      const config = this.clean(this.entries() || {});

      Object.defineProperties(config, {
        __useName: { value: this.name },
        __ruleNames: { value: this.parent && this.parent.names },
        __ruleTypes: { value: this.parent && this.parent.ruleTypes },
      });

      return config;
    }
  },
);


code:
import { Tapable } from 'tapable';
import * as webpack from 'webpack';
import * as https from 'https';

export = Config;

declare namespace __Config {
  class Chained<Parent> {
    end(): Parent;
  }

  class TypedChainedMap<Parent, Value> extends Chained<Parent> {
    clear(): this;
    delete(key: string): this;
    has(key: string): boolean;
    get(key: string): Value;
    getOrCompute(key: string, compute: () => Value): Value;
    set(key: string, value: Value): this;
    merge(obj: { [key: string]: Value }): this;
    entries(): { [key: string]: Value };
    values(): Value[];
    when(
      condition: boolean,
      trueBrancher: (obj: this) => void,
      falseBrancher?: (obj: this) => void,
    ): this;
  }

  class ChainedMap<Parent> extends TypedChainedMap<Parent, any> {}

  class TypedChainedSet<Parent, Value> extends Chained<Parent> {
    add(value: Value): this;
    prepend(value: Value): this;
    clear(): this;
    delete(key: string): this;
    has(key: string): boolean;
    merge(arr: Value[]): this;
    values(): Value[];
    when(
      condition: boolean,
      trueBrancher: (obj: this) => void,
      falseBrancher?: (obj: this) => void,
    ): this;
  }

  class ChainedSet<Parent> extends TypedChainedSet<Parent, any> {}
}

declare class Config extends __Config.ChainedMap<void> {
  devServer: Config.DevServer;
  entryPoints: Config.TypedChainedMap<Config, Config.EntryPoint>;
  module: Config.Module;
  node: Config.ChainedMap<this>;
  output: Config.Output;
  optimization: Config.Optimization;
  performance: Config.Performance;
  plugins: Config.Plugins<this, webpack.Plugin>;
  resolve: Config.Resolve;
  resolveLoader: Config.ResolveLoader;

  amd(value: { [moduleName: string]: boolean }): this;
  bail(value: boolean): this;
  cache(value: boolean | any): this;
  devtool(value: Config.DevTool): this;
  context(value: string): this;
  externals(value: webpack.ExternalsElement | webpack.ExternalsElement[]): this;
  loader(value: any): this;
  name(value: string): this;
  mode(value: 'none' | 'development' | 'production'): this;
  parallelism(value: number): this;
  profile(value: boolean): this;
  recordsPath(value: string): this;
  recordsInputPath(value: string): this;
  recordsOutputPath(value: string): this;
  stats(value: webpack.Options.Stats): this;
  target(value: string): this;
  watch(value: boolean): this;
  watchOptions(value: webpack.Options.WatchOptions): this;

  entry(name: string): Config.EntryPoint;
  plugin(name: string): Config.Plugin<this, webpack.Plugin>;

  toConfig(): webpack.Configuration;
}

declare namespace Config {
  class Chained<Parent> extends __Config.Chained<Parent> {}
  class TypedChainedMap<Parent, Value> extends __Config.TypedChainedMap<
    Parent,
    Value
  > {}
  class ChainedMap<Parent> extends __Config.TypedChainedMap<Parent, any> {}
  class TypedChainedSet<Parent, Value> extends __Config.TypedChainedSet<
    Parent,
    Value
  > {}
  class ChainedSet<Parent> extends __Config.TypedChainedSet<Parent, any> {}

  class Plugins<
    Parent,
    PluginType extends Tapable.Plugin = webpack.Plugin
  > extends TypedChainedMap<Parent, Plugin<Parent, PluginType>> {}

  class Plugin<Parent, PluginType extends Tapable.Plugin = webpack.Plugin>
    extends ChainedMap<Parent>
    implements Orderable {
    init<P extends PluginType | PluginClass<PluginType>>(
      value: (
        plugin: P,
        args: P extends PluginClass ? ConstructorParameters<P> : any[],
      ) => PluginType,
    ): this;
    use<P extends string | PluginType | PluginClass<PluginType>>(
      plugin: P,
      args?: P extends PluginClass ? ConstructorParameters<P> : any[],
    ): this;
    tap<P extends PluginClass<PluginType>>(
      f: (args: ConstructorParameters<P>) => ConstructorParameters<P>,
    ): this;

    // Orderable
    before(name: string): this;
    after(name: string): this;
  }

  class Module extends ChainedMap<Config> {
    rules: TypedChainedMap<this, Rule>;
    rule(name: string): Rule;
    noParse(
      noParse: RegExp | RegExp[] | ((contentPath: string) => boolean),
    ): this;
    strictExportPresence(value: boolean): this;
  }

  class Output extends ChainedMap<Config> {
    auxiliaryComment(value: string | { [comment: string]: string }): this;
    chunkFilename(value: string): this;
    chunkLoadTimeout(value: number): this;
    crossOriginLoading(value: boolean | string): this;
    filename(value: string): this;
    library(value: string): this;
    libraryExport(value: string | string[]): this;
    libraryTarget(value: string): this;
    devtoolFallbackModuleFilenameTemplate(value: any): this;
    devtoolLineToLine(value: any): this;
    devtoolModuleFilenameTemplate(value: any): this;
    devtoolNamespace(value: string): this;
    globalObject(value: string): this;
    hashFunction(value: string): this;
    hashDigest(value: string): this;
    hashDigestLength(value: number): this;
    hashSalt(value: any): this;
    hotUpdateChunkFilename(value: string): this;
    hotUpdateFunction(value: any): this;
    hotUpdateMainFilename(value: string): this;
    jsonpFunction(value: string): this;
    path(value: string): this;
    pathinfo(value: boolean): this;
    publicPath(value: string): this;
    sourceMapFilename(value: string): this;
    sourcePrefix(value: string): this;
    strictModuleExceptionHandling(value: boolean): this;
    umdNamedDefine(value: boolean): this;
    futureEmitAssets(value: boolean): this;
  }

  class DevServer extends ChainedMap<Config> {
    allowedHosts: TypedChainedSet<this, string>;

    after(
      value: (app: any, server: any, compiler: webpack.Compiler) => void,
    ): this;
    before(
      value: (app: any, server: any, compiler: webpack.Compiler) => void,
    ): this;
    bonjour(value: boolean): this;
    clientLogLevel(value: 'none' | 'error' | 'warning' | 'info'): this;
    color(value: boolean): this;
    compress(value: boolean): this;
    contentBase(value: boolean | string | string[]): this;
    disableHostCheck(value: boolean): this;
    filename(value: string): this;
    headers(value: { [header: string]: string }): this;
    historyApiFallback(value: boolean | any): this;
    host(value: string): this;
    hot(value: boolean): this;
    hotOnly(value: boolean): this;
    http2(value: boolean): this;
    https(value: boolean | https.ServerOptions): this;
    index(value: string): this;
    info(value: boolean): this;
    inline(value: boolean): this;
    lazy(value: boolean): this;
    mimeTypes(value: Object): this;
    noInfo(value: boolean): this;
    open(value: boolean): this;
    openPage(value: string | string[]): this;
    overlay(value: boolean | { warnings?: boolean; errors?: boolean }): this;
    pfx(value: string): this;
    pfxPassphrase(value: string): this;
    port(value: number): this;
    progress(value: boolean): this;
    proxy(value: any): this;
    public(value: string): this;
    publicPath(publicPath: string): this;
    quiet(value: boolean): this;
    setup(value: (expressApp: any) => void): this;
    socket(value: string): this;
    sockHost(value: string): this;
    sockPath(value: string): this;
    sockPort(value: number): this;
    staticOptions(value: any): this;
    stats(value: webpack.Options.Stats): this;
    stdin(value: boolean): this;
    useLocalIp(value: boolean): this;
    watchContentBase(value: boolean): this;
    watchOptions(value: any): this;
    writeToDisk(value: boolean): this;
  }

  class Performance extends ChainedMap<Config> {
    hints(value: boolean | 'error' | 'warning'): this;
    maxEntrypointSize(value: number): this;
    maxAssetSize(value: number): this;
    assetFilter(value: (assetFilename: string) => boolean): this;
  }

  class EntryPoint extends TypedChainedSet<Config, string> {}

  class Resolve<T = Config> extends ChainedMap<T> {
    alias: TypedChainedMap<this, string>;
    aliasFields: TypedChainedSet<this, string>;
    descriptionFiles: TypedChainedSet<this, string>;
    extensions: TypedChainedSet<this, string>;
    mainFields: TypedChainedSet<this, string>;
    mainFiles: TypedChainedSet<this, string>;
    modules: TypedChainedSet<this, string>;
    plugins: TypedChainedMap<this, Plugin<this, webpack.ResolvePlugin>>;

    enforceExtension(value: boolean): this;
    enforceModuleExtension(value: boolean): this;
    unsafeCache(value: boolean | RegExp | RegExp[]): this;
    symlinks(value: boolean): this;
    cachePredicate(
      value: (data: { path: string; request: string }) => boolean,
    ): this;
    cacheWithContext(value: boolean): this;

    plugin(name: string): Plugin<this, webpack.ResolvePlugin>;
  }

  class ResolveLoader extends Resolve {
    moduleExtensions: ChainedSet<this>;
    packageMains: ChainedSet<this>;
  }

  class Rule<T = Module> extends ChainedMap<T> implements Orderable {
    rules: TypedChainedMap<this, Rule<Rule>>;
    oneOfs: TypedChainedMap<this, Rule<Rule>>;
    uses: TypedChainedMap<this, Use>;
    include: TypedChainedSet<this, webpack.Condition>;
    exclude: TypedChainedSet<this, webpack.Condition>;
    resolve: Resolve<Rule<T>>;

    parser(value: { [optName: string]: any }): this;
    test(value: webpack.Condition | webpack.Condition[]): this;
    type(
      value:
        | 'javascript/auto'
        | 'javascript/dynamic'
        | 'javascript/esm'
        | 'json'
        | 'webassembly/experimental',
    ): this;
    enforce(value: 'pre' | 'post'): this;

    use(name: string): Use<this>;
    rule(name: string): Rule<Rule>;
    oneOf(name: string): Rule<Rule>;
    pre(): this;
    post(): this;
    before(name: string): this;
    after(name: string): this;
    resourceQuery(value: webpack.Condition | webpack.Condition[]): this;
  }

  class Optimization extends ChainedMap<Config> {
    concatenateModules(value: boolean): this;
    flagIncludedChunks(value: boolean): this;
    mergeDuplicateChunks(value: boolean): this;
    minimize(value: boolean): this;
    minimizer(name: string): Config.Plugin<this, webpack.Plugin>;
    namedChunks(value: boolean): this;
    namedModules(value: boolean): this;
    nodeEnv(value: boolean | string): this;
    noEmitOnErrors(value: boolean): this;
    occurrenceOrder(value: boolean): this;
    portableRecords(value: boolean): this;
    providedExports(value: boolean): this;
    removeAvailableModules(value: boolean): this;
    removeEmptyChunks(value: boolean): this;
    runtimeChunk(value: boolean | 'single' | 'multiple' | RuntimeChunk): this;
    sideEffects(value: boolean): this;
    splitChunks(value: SplitChunksOptions): this;
    usedExports(value: boolean): this;
  }

  interface RuntimeChunk {
    name: string | RuntimeChunkFunction;
  }

  type RuntimeChunkFunction = (entryPoint: EntryPoint) => string;

  interface SplitChunksOptions {
    [name: string]: any;
  }

  interface LoaderOptions {
    [name: string]: any;
  }

  class Use<Parent = Rule> extends ChainedMap<Parent> implements Orderable {
    loader(value: string): this;
    options(value: LoaderOptions): this;

    tap(f: (options: LoaderOptions) => LoaderOptions): this;

    // Orderable
    before(name: string): this;
    after(name: string): this;
  }

  type DevTool =
    | 'eval'
    | 'inline-source-map'
    | 'cheap-eval-source-map'
    | 'cheap-source-map'
    | 'cheap-module-eval-source-map'
    | 'cheap-module-source-map'
    | 'eval-source-map'
    | 'source-map'
    | 'nosources-source-map'
    | 'hidden-source-map'
    | 'nosources-source-map'
    | '@eval'
    | '@inline-source-map'
    | '@cheap-eval-source-map'
    | '@cheap-source-map'
    | '@cheap-module-eval-source-map'
    | '@cheap-module-source-map'
    | '@eval-source-map'
    | '@source-map'
    | '@nosources-source-map'
    | '@hidden-source-map'
    | '@nosources-source-map'
    | '#eval'
    | '#inline-source-map'
    | '#cheap-eval-source-map'
    | '#cheap-source-map'
    | '#cheap-module-eval-source-map'
    | '#cheap-module-source-map'
    | '#eval-source-map'
    | '#source-map'
    | '#nosources-source-map'
    | '#hidden-source-map'
    | '#nosources-source-map'
    | '#@eval'
    | '#@inline-source-map'
    | '#@cheap-eval-source-map'
    | '#@cheap-source-map'
    | '#@cheap-module-eval-source-map'
    | '#@cheap-module-source-map'
    | '#@eval-source-map'
    | '#@source-map'
    | '#@nosources-source-map'
    | '#@hidden-source-map'
    | '#@nosources-source-map'
    | boolean;

  interface PluginClass<PluginType extends Tapable.Plugin = webpack.Plugin> {
    new (...opts: any[]): PluginType;
  }

  interface Orderable {
    before(name: string): this;
    after(name: string): this;
  }
}