[
  {
    "path": ".eslintrc.js",
    "content": "module.exports = {\n  root: true,\n  extends: [\n    'eslint-config-airbnb-base',\n    'eslint-config-prettier',\n    'plugin:jest/recommended',\n    'plugin:jest/style',\n  ],\n  // Force dotfiles to be checked, since by default ESLint ignores them.\n  ignorePatterns: ['!.*.js'],\n  reportUnusedDisableDirectives: true,\n  rules: {\n    'class-methods-use-this': 'off',\n    'no-shadow': 'off',\n    'no-underscore-dangle': 'off',\n  },\n};\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: 'ci'\non: [push, pull_request]\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: bahmutov/npm-install@v1\n      - name: Run lint\n        run: yarn lint\n  style:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: bahmutov/npm-install@v1\n      - name: Run code style lint\n        run: yarn style\n  test:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        node: ['12', '14', '16']\n    steps:\n      - uses: actions/checkout@v2\n      - uses: actions/setup-node@v2\n        with:\n          node-version: ${{ matrix.node }}\n      - uses: bahmutov/npm-install@v1\n      - name: Run unit tests\n        run: yarn test\n  test-types:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: bahmutov/npm-install@v1\n      - name: Run type declaration tests\n        run: yarn test:types\n"
  },
  {
    "path": ".gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nlerna-debug.log\n\n# Build directories\nbuild\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# node-waf configuration\n.lock-wscript\n\n# Dependency directories\nnode_modules\njspm_packages\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Webstorm project metadata\n.idea\n"
  },
  {
    "path": ".prettierignore",
    "content": "# Since autogenerated file\nCHANGELOG.md\n\n# Since it formats the inline code blocks, worsening readability\nREADME.md\n"
  },
  {
    "path": ".prettierrc.js",
    "content": "module.exports = {\n  singleQuote: true,\n  trailingComma: 'all',\n};\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "### Changelog\n\nAll notable changes to this project will be documented in this file. Dates are displayed in UTC.\n\nGenerated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).\n\n#### [v6.5.1](https://github.com/neutrinojs/webpack-chain/compare/v6.5.0...v6.5.1)\n\n> 25 July 2020\n\n- Improve error message when .tap() used on an undefined plugin [`#271`](https://github.com/neutrinojs/webpack-chain/pull/271)\n- Improve error message when .use() not called for a Plugin [`#270`](https://github.com/neutrinojs/webpack-chain/pull/270)\n- Clean up linting configuration [`#268`](https://github.com/neutrinojs/webpack-chain/pull/268)\n- Remove unused dev-dependency @types/node [`#269`](https://github.com/neutrinojs/webpack-chain/pull/269)\n- Lock file maintenance [`#259`](https://github.com/neutrinojs/webpack-chain/pull/259)\n- Travis: Test against Node 14 [`#267`](https://github.com/neutrinojs/webpack-chain/pull/267)\n- Improve error message when .tap() used on an undefined plugin (#271) [`#125`](https://github.com/neutrinojs/webpack-chain/issues/125)\n\n#### [v6.5.0](https://github.com/neutrinojs/webpack-chain/compare/v6.4.0...v6.5.0)\n\n> 1 July 2020\n\n- Add rule.resolve [`#265`](https://github.com/neutrinojs/webpack-chain/pull/265)\n- Update dependency eslint to v7 [`#263`](https://github.com/neutrinojs/webpack-chain/pull/263)\n- Switch from Ava to Jest [`#258`](https://github.com/neutrinojs/webpack-chain/pull/258)\n- Update dependency prettier to v2 [`#250`](https://github.com/neutrinojs/webpack-chain/pull/250)\n- Update dependency auto-changelog to v2 [`#257`](https://github.com/neutrinojs/webpack-chain/pull/257)\n- Lock file maintenance [`#244`](https://github.com/neutrinojs/webpack-chain/pull/244)\n- Test against Node 13 [`#254`](https://github.com/neutrinojs/webpack-chain/pull/254)\n- Switch to a newer Travis base image [`#253`](https://github.com/neutrinojs/webpack-chain/pull/253)\n\n#### [v6.4.0](https://github.com/neutrinojs/webpack-chain/compare/v6.3.1...v6.4.0)\n\n> 3 February 2020\n\n- Fix Rule.merge() when include or exclude are strings [`#243`](https://github.com/neutrinojs/webpack-chain/pull/243)\n- Add Types to Plugin Arguments [`#241`](https://github.com/neutrinojs/webpack-chain/pull/241)\n- Add devServer shorthands for sockHost, sockPort, sockPath [`#238`](https://github.com/neutrinojs/webpack-chain/pull/238)\n- Add `devtoolNamespace` TypeScript declaration [`#232`](https://github.com/neutrinojs/webpack-chain/pull/232)\n- Fix Rule.merge() when include or exclude are strings (#243) [`#228`](https://github.com/neutrinojs/webpack-chain/issues/228)\n- Add devServer shorthands for sockHost, sockPort, sockPath (#238) [`#231`](https://github.com/neutrinojs/webpack-chain/issues/231)\n\n#### [v6.3.1](https://github.com/neutrinojs/webpack-chain/compare/v6.3.0...v6.3.1)\n\n> 28 January 2020\n\n- Lock file maintenance [`#217`](https://github.com/neutrinojs/webpack-chain/pull/217)\n- docs: Emphasise that merge() doesn't accept webpack config objects [`#225`](https://github.com/neutrinojs/webpack-chain/pull/225)\n- Update types and documentation for DevServer [`#233`](https://github.com/neutrinojs/webpack-chain/pull/233)\n- Validate that Plugin 'args' is an array [`#229`](https://github.com/neutrinojs/webpack-chain/pull/229)\n- Improve error message when legacy minimizer syntax used [`#226`](https://github.com/neutrinojs/webpack-chain/pull/226)\n- Add missing engines definition to package.json [`#227`](https://github.com/neutrinojs/webpack-chain/pull/227)\n- docs: Correct the .merge() example for optimization.minimizer [`#224`](https://github.com/neutrinojs/webpack-chain/pull/224)\n- docs: Fix formatting typos in README [`#223`](https://github.com/neutrinojs/webpack-chain/pull/223)\n- Validate that Plugin 'args' is an array (#229) [`#121`](https://github.com/neutrinojs/webpack-chain/issues/121)\n\n#### [v6.3.0](https://github.com/neutrinojs/webpack-chain/compare/v6.2.0...v6.3.0)\n\n> 22 December 2019\n\n- Add support for nested rules (rule.rules) [`#220`](https://github.com/neutrinojs/webpack-chain/pull/220)\n- Fix missing type TypedChainedMap.getOrCompute [`#221`](https://github.com/neutrinojs/webpack-chain/pull/221)\n- fix: fix type definition for Rule#oneOf [`#218`](https://github.com/neutrinojs/webpack-chain/pull/218)\n- fix: fix type definition for Rule#oneOf (#218) [`#216`](https://github.com/neutrinojs/webpack-chain/issues/216)\n\n#### [v6.2.0](https://github.com/neutrinojs/webpack-chain/compare/v6.1.0...v6.2.0)\n\n> 22 December 2019\n\n- Add support for module.strictExportPresence and output.futureEmitAssets [`#207`](https://github.com/neutrinojs/webpack-chain/pull/207)\n- 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)\n\n#### [v6.1.0](https://github.com/neutrinojs/webpack-chain/compare/v6.0.0...v6.1.0)\n\n> 13 December 2019\n\n- README: Update Travis badge to point to travis-ci.com [`#215`](https://github.com/neutrinojs/webpack-chain/pull/215)\n- Lock file maintenance [`#199`](https://github.com/neutrinojs/webpack-chain/pull/199)\n- Fix types for Config.resolve plugins and improve Plugin types [`#213`](https://github.com/neutrinojs/webpack-chain/pull/213)\n- Update dependency eslint-plugin-ava to v9 [`#203`](https://github.com/neutrinojs/webpack-chain/pull/203)\n- Add Chinese docs link to README [`#136`](https://github.com/neutrinojs/webpack-chain/pull/136)\n- Lock file maintenance [`#196`](https://github.com/neutrinojs/webpack-chain/pull/196)\n- Update dependency eslint to v6 [`#186`](https://github.com/neutrinojs/webpack-chain/pull/186)\n- Update dependency eslint-config-airbnb-base to v14 [`#192`](https://github.com/neutrinojs/webpack-chain/pull/192)\n- Lock file maintenance [`#184`](https://github.com/neutrinojs/webpack-chain/pull/184)\n- Update dependency eslint-config-prettier to v6 [`#187`](https://github.com/neutrinojs/webpack-chain/pull/187)\n- Lock file maintenance [`#180`](https://github.com/neutrinojs/webpack-chain/pull/180)\n- Update dependency eslint-plugin-ava to v7 [`#178`](https://github.com/neutrinojs/webpack-chain/pull/178)\n- Lock file maintenance [`#177`](https://github.com/neutrinojs/webpack-chain/pull/177)\n- Lock file maintenance [`#176`](https://github.com/neutrinojs/webpack-chain/pull/176)\n- Lock file maintenance [`#174`](https://github.com/neutrinojs/webpack-chain/pull/174)\n- feat: rule test supports function [`#172`](https://github.com/neutrinojs/webpack-chain/pull/172)\n- Lock file maintenance [`#170`](https://github.com/neutrinojs/webpack-chain/pull/170)\n\n### [v6.0.0](https://github.com/neutrinojs/webpack-chain/compare/v5.2.4...v6.0.0)\n\n> 3 May 2019\n\n- Lock file maintenance [`#169`](https://github.com/neutrinojs/webpack-chain/pull/169)\n- Update linting configuration, support Node.js 12 in CI [`#168`](https://github.com/neutrinojs/webpack-chain/pull/168)\n- Extended DevServer method [`#167`](https://github.com/neutrinojs/webpack-chain/pull/167)\n- Lock file maintenance [`#165`](https://github.com/neutrinojs/webpack-chain/pull/165)\n- Update dependency eslint-plugin-ava to v6 [`#161`](https://github.com/neutrinojs/webpack-chain/pull/161)\n- Point docs to v6 [`37201a2`](https://github.com/neutrinojs/webpack-chain/commit/37201a2974c078c3494243ff03342ba16910db21)\n\n#### [v5.2.4](https://github.com/neutrinojs/webpack-chain/compare/v5.2.3...v5.2.4)\n\n> 25 March 2019\n\n- fix Use#end return type in OneOf [`#158`](https://github.com/neutrinojs/webpack-chain/pull/158)\n- make __expression property works in any object [`#157`](https://github.com/neutrinojs/webpack-chain/pull/157)\n- Lock file maintenance [`#160`](https://github.com/neutrinojs/webpack-chain/pull/160)\n- docs: Fix typo in config.optimization example [`#159`](https://github.com/neutrinojs/webpack-chain/pull/159)\n\n#### [v5.2.3](https://github.com/neutrinojs/webpack-chain/compare/v5.2.2...v5.2.3)\n\n> 22 March 2019\n\n- optimize type definitions [`#156`](https://github.com/neutrinojs/webpack-chain/pull/156)\n- Lock file maintenance [`#155`](https://github.com/neutrinojs/webpack-chain/pull/155)\n\n#### [v5.2.2](https://github.com/neutrinojs/webpack-chain/compare/v5.2.1...v5.2.2)\n\n> 12 March 2019\n\n- Fix README comment rendering [`#154`](https://github.com/neutrinojs/webpack-chain/pull/154)\n- Lock file maintenance [`#153`](https://github.com/neutrinojs/webpack-chain/pull/153)\n- Update dependency javascript-stringify to v2 [`#151`](https://github.com/neutrinojs/webpack-chain/pull/151)\n- Fix stringify master bustage [`55f6a5d`](https://github.com/neutrinojs/webpack-chain/commit/55f6a5d25b58d03fbc57a17d8c2e09de310fa068)\n\n#### [v5.2.1](https://github.com/neutrinojs/webpack-chain/compare/v5.2.0...v5.2.1)\n\n> 7 March 2019\n\n- Lock file maintenance [`#145`](https://github.com/neutrinojs/webpack-chain/pull/145)\n- Add `config.output.globalObject` type [`#147`](https://github.com/neutrinojs/webpack-chain/pull/147)\n- add module-rule-type [`#148`](https://github.com/neutrinojs/webpack-chain/pull/148)\n- Update `config.mode` type [`#146`](https://github.com/neutrinojs/webpack-chain/pull/146)\n- Update dependency eslint-config-prettier to v4 [`75aa60b`](https://github.com/neutrinojs/webpack-chain/commit/75aa60b760947cd8eb438691158dd9db52f4f9f5)\n\n#### [v5.2.0](https://github.com/neutrinojs/webpack-chain/compare/v5.1.0...v5.2.0)\n\n> 23 January 2019\n\n- Add `config.name` type [`#143`](https://github.com/neutrinojs/webpack-chain/pull/143)\n- Add TypeScript type definitions [`#132`](https://github.com/neutrinojs/webpack-chain/pull/132)\n- docs: Fix typo of 'optimization' [`#139`](https://github.com/neutrinojs/webpack-chain/pull/139)\n- Add TypeScript type definitions (#132) [`#62`](https://github.com/neutrinojs/webpack-chain/issues/62)\n\n#### [v5.1.0](https://github.com/neutrinojs/webpack-chain/compare/v5.0.1...v5.1.0)\n\n> 16 January 2019\n\n- Support config.name() setter [`#131`](https://github.com/neutrinojs/webpack-chain/pull/131)\n- Allow use of before() and after() with oneOf rules [`#133`](https://github.com/neutrinojs/webpack-chain/pull/133)\n- Travis: Test against Node 11 [`#118`](https://github.com/neutrinojs/webpack-chain/pull/118)\n- docs: Fix typo in devServer options [`#117`](https://github.com/neutrinojs/webpack-chain/pull/117)\n- Allow use of before() and after() with oneOf rules (#133) [`#119`](https://github.com/neutrinojs/webpack-chain/issues/119)\n- Update dependency ava to v1 [`ce9e884`](https://github.com/neutrinojs/webpack-chain/commit/ce9e884f988a01ac6297d167dfd013cab8d8c24a)\n- Lock file maintenance [`d124b5d`](https://github.com/neutrinojs/webpack-chain/commit/d124b5df6648257092becae81ceece9bef5485b8)\n\n#### [v5.0.1](https://github.com/neutrinojs/webpack-chain/compare/v5.0.0...v5.0.1)\n\n> 22 October 2018\n\n- Fix toString() output for alternative types of plugin [`#116`](https://github.com/neutrinojs/webpack-chain/pull/116)\n- Fix toString() output for alternative types of plugin (#116) [`#115`](https://github.com/neutrinojs/webpack-chain/issues/115)\n\n### [v5.0.0](https://github.com/neutrinojs/webpack-chain/compare/v4.12.1...v5.0.0)\n\n> 8 October 2018\n\n- README: Add NPM/Travis badges [`#112`](https://github.com/neutrinojs/webpack-chain/pull/112)\n- Provide the same API for config.optimization.minimizer as config.plugins [`#84`](https://github.com/neutrinojs/webpack-chain/pull/84)\n- README: Add NPM/Travis badges (#112) [`#110`](https://github.com/neutrinojs/webpack-chain/issues/110)\n- Provide the same API for config.optimization.minimizer as config.plugins (#84) [`#95`](https://github.com/neutrinojs/webpack-chain/issues/95)\n\n#### [v4.12.1](https://github.com/neutrinojs/webpack-chain/compare/v4.12.0...v4.12.1)\n\n> 3 October 2018\n\n- Switch from changelog to auto-changelog [`#109`](https://github.com/neutrinojs/webpack-chain/pull/109)\n- Allow passing entry as a string to config.merge() [`#107`](https://github.com/neutrinojs/webpack-chain/pull/107)\n- Lock file maintenance [`#101`](https://github.com/neutrinojs/webpack-chain/pull/101)\n- Update dependency eslint-plugin-prettier to v3 [`e42d8bd`](https://github.com/neutrinojs/webpack-chain/commit/e42d8bd2f6f70841c4d1ab7a2926c26d3ec828ed)\n\n#### [v4.12.0](https://github.com/neutrinojs/webpack-chain/compare/v4.11.0...v4.12.0)\n\n> 3 October 2018\n\n- merge resolve plugins just like config [`c47ee2d`](https://github.com/neutrinojs/webpack-chain/commit/c47ee2d52c1be4fcdbe10adef865e45d6fd729ef)\n- linted [`dff82f8`](https://github.com/neutrinojs/webpack-chain/commit/dff82f8494394dcf8c97bff324f877513f44fa56)\n- Revert changes to gitignore [`c1250a0`](https://github.com/neutrinojs/webpack-chain/commit/c1250a0fe7ffafa82f529ac8e7262e2c3cdd4729)\n\n#### [v4.11.0](https://github.com/neutrinojs/webpack-chain/compare/v4.10.0...v4.11.0)\n\n> 13 September 2018\n\n- Support specifying plugins by path [`#102`](https://github.com/neutrinojs/webpack-chain/pull/102)\n- Lock file maintenance [`#100`](https://github.com/neutrinojs/webpack-chain/pull/100)\n- Lock file maintenance [`#96`](https://github.com/neutrinojs/webpack-chain/pull/96)\n\n#### [v4.10.0](https://github.com/neutrinojs/webpack-chain/compare/v4.9.0...v4.10.0)\n\n> 6 September 2018\n\n- Use the Resolve API to define ResolveLoader according to webpack [`#99`](https://github.com/neutrinojs/webpack-chain/pull/99)\n- Migrate to new org [`#92`](https://github.com/neutrinojs/webpack-chain/pull/92)\n- test: 'clean' in 'ChainedMap' [`#93`](https://github.com/neutrinojs/webpack-chain/pull/93)\n- Lock file maintenance [`3a4b3e1`](https://github.com/neutrinojs/webpack-chain/commit/3a4b3e10032856ab7f01afa67a23dd9e4e68161a)\n- Lock file maintenance [`815bfd1`](https://github.com/neutrinojs/webpack-chain/commit/815bfd173a2dc6f802b66a48cdb2c4d2ff47df9f)\n\n#### [v4.9.0](https://github.com/neutrinojs/webpack-chain/compare/v4.8.0...v4.9.0)\n\n> 15 August 2018\n\n- Update to ESLint 5 [`#89`](https://github.com/neutrinojs/webpack-chain/pull/89)\n- Lock file maintenance [`#85`](https://github.com/neutrinojs/webpack-chain/pull/85)\n- Implement ChainedMap.getOrCompute [`#63`](https://github.com/neutrinojs/webpack-chain/pull/63)\n- Support Object literal plugin usage [`#86`](https://github.com/neutrinojs/webpack-chain/pull/86)\n- Lock file maintenance [`#61`](https://github.com/neutrinojs/webpack-chain/pull/61)\n- Lock file maintenance [`#60`](https://github.com/neutrinojs/webpack-chain/pull/60)\n- 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)\n- Update dependency eslint-config-airbnb-base to v13 [`7370962`](https://github.com/neutrinojs/webpack-chain/commit/73709628a6ff6661e478c652d0ff03b99b6c2abb)\n- Fix linting :/ [`30cc11d`](https://github.com/neutrinojs/webpack-chain/commit/30cc11d0d35a5676069a623b180c8e7b00e099e4)\n- Fix README bug, test in Node.js v6 [`4a37c74`](https://github.com/neutrinojs/webpack-chain/commit/4a37c74e1f790e118034154da9c32d0e36164f74)\n- Run yarn lint --fix [`9384537`](https://github.com/neutrinojs/webpack-chain/commit/9384537269d60bb80b3330cf44ddbdd9d528c454)\n\n#### [v4.8.0](https://github.com/neutrinojs/webpack-chain/compare/v4.7.0...v4.8.0)\n\n> 16 May 2018\n\n- Expose toString as a static method on Config [`#57`](https://github.com/neutrinojs/webpack-chain/pull/57)\n- Add test for Config.toString, add README note [`0107aef`](https://github.com/neutrinojs/webpack-chain/commit/0107aef203202aef069190723b04ec4f6ac80b9f)\n\n#### [v4.7.0](https://github.com/neutrinojs/webpack-chain/compare/v4.6.0...v4.7.0)\n\n> 15 May 2018\n\n- Lint with eslint, prettier, airbnb [`#52`](https://github.com/neutrinojs/webpack-chain/pull/52)\n- Support Config.toString() with name hints [`#53`](https://github.com/neutrinojs/webpack-chain/pull/53)\n- Configure Renovate [`#54`](https://github.com/neutrinojs/webpack-chain/pull/54)\n- Lock file maintenance [`50d4db8`](https://github.com/neutrinojs/webpack-chain/commit/50d4db81ab5fe62a55435720f5c78ddc40309a88)\n\n#### [v4.6.0](https://github.com/neutrinojs/webpack-chain/compare/v4.5.0...v4.6.0)\n\n> 16 April 2018\n\n- Support Webpack 4.x [`#51`](https://github.com/neutrinojs/webpack-chain/pull/51)\n- Update devDependencies [`#50`](https://github.com/neutrinojs/webpack-chain/pull/50)\n\n#### [v4.5.0](https://github.com/neutrinojs/webpack-chain/compare/v4.4.2...v4.5.0)\n\n> 22 November 2017\n\n- Introduce method for performing a batch of operations against a context [`#43`](https://github.com/neutrinojs/webpack-chain/pull/43)\n\n#### [v4.4.2](https://github.com/neutrinojs/webpack-chain/compare/v4.4.1...v4.4.2)\n\n> 10 October 2017\n\n- Update changelog [`1bb3da1`](https://github.com/neutrinojs/webpack-chain/commit/1bb3da1fec04a158f68762e57aff33a0172a298f)\n- Hotfix - guard against non-defined entries when ordering chainedmap [`76be81f`](https://github.com/neutrinojs/webpack-chain/commit/76be81f4509b9652bef25cc55747df87850b858e)\n- Updating changelog [`a71fc4b`](https://github.com/neutrinojs/webpack-chain/commit/a71fc4b70ccce358aacc29ed7dc5d8cdacdd4cc1)\n\n#### [v4.4.1](https://github.com/neutrinojs/webpack-chain/compare/v4.4.0...v4.4.1)\n\n> 6 October 2017\n\n- Updating changelog [`97a2fab`](https://github.com/neutrinojs/webpack-chain/commit/97a2fabf6e51f1b03cacb0991ba02e236be983fa)\n- Missing schema before/after [`8d8f26d`](https://github.com/neutrinojs/webpack-chain/commit/8d8f26dd0e6db375dbabb8e8dfe784e6c50408d5)\n\n#### [v4.4.0](https://github.com/neutrinojs/webpack-chain/compare/v4.3.0...v4.4.0)\n\n> 6 October 2017\n\n- Feature: allow specifying to use before or after other use [`#42`](https://github.com/neutrinojs/webpack-chain/pull/42)\n- Docs: Upstream fixes made to Neutrino's webpack-chain docs [`#41`](https://github.com/neutrinojs/webpack-chain/pull/41)\n- Improve documentation for plugin configuration [`#40`](https://github.com/neutrinojs/webpack-chain/pull/40)\n- Allow omitting keys from source merge object [`fb6ea2f`](https://github.com/neutrinojs/webpack-chain/commit/fb6ea2fad931c13e7516a3e9354215a78cb5c4ff)\n- Feature: allow specifying .before or .after to order plugins and uses [`b0040bf`](https://github.com/neutrinojs/webpack-chain/commit/b0040bff73b3b9e55d53192ac4a447a2ac8c02d1)\n- Rename when arguments to be clearer [`d15e895`](https://github.com/neutrinojs/webpack-chain/commit/d15e895669ce0a44c704755af39290700e73e85f)\n- Bumping deps [`c15be4a`](https://github.com/neutrinojs/webpack-chain/commit/c15be4ab99d232126bbf18666f4f20f80df21f90)\n- Update changelog [`5aec63a`](https://github.com/neutrinojs/webpack-chain/commit/5aec63a424c71a9a603540b33e576999c839f074)\n\n#### [v4.3.0](https://github.com/neutrinojs/webpack-chain/compare/v4.2.0...v4.3.0)\n\n> 13 September 2017\n\n- Update API for base config, dev server, and output [`#38`](https://github.com/neutrinojs/webpack-chain/pull/38)\n- Update changelog [`6260f49`](https://github.com/neutrinojs/webpack-chain/commit/6260f49edbcab301988b7b2c6c8a77e07707c010)\n\n#### [v4.2.0](https://github.com/neutrinojs/webpack-chain/compare/v4.1.0...v4.2.0)\n\n> 13 September 2017\n\n- Add new shorthands from resolve and output [`#37`](https://github.com/neutrinojs/webpack-chain/pull/37)\n- changelog [`0374e51`](https://github.com/neutrinojs/webpack-chain/commit/0374e518a4b465c73b5097eff6e4c77768319e4f)\n- Updating README with shorthands [`ae5e75a`](https://github.com/neutrinojs/webpack-chain/commit/ae5e75ae619d0399bcbf8e588a48759b0e590b6e)\n\n#### [v4.1.0](https://github.com/neutrinojs/webpack-chain/compare/v4.0.0...v4.1.0)\n\n> 12 September 2017\n\n- Updating rule definition shortcuts, adding oneOf [`#36`](https://github.com/neutrinojs/webpack-chain/pull/36)\n\n### [v4.0.0](https://github.com/neutrinojs/webpack-chain/compare/v3.3.0...v4.0.0)\n\n> 3 October 2018\n\n- Switch noParse to getter/setter to allow webpack v3 function argument [`#32`](https://github.com/neutrinojs/webpack-chain/pull/32)\n- Serialize performance into config output [`#31`](https://github.com/neutrinojs/webpack-chain/pull/31)\n- Release v4.0.0 [`e84b002`](https://github.com/neutrinojs/webpack-chain/commit/e84b00207f6d4f4dc37c43ffffc65d8a34f63a75)\n\n#### [v3.3.0](https://github.com/neutrinojs/webpack-chain/compare/v3.2.0...v3.3.0)\n\n> 3 October 2018\n\n- Adding noParse on module [`#27`](https://github.com/neutrinojs/webpack-chain/pull/27)\n- Releasing v3.3.0 [`4a59bef`](https://github.com/neutrinojs/webpack-chain/commit/4a59bef687f273503945e638fefe1f6ab29857d1)\n\n#### [v3.2.0](https://github.com/neutrinojs/webpack-chain/compare/v3.1.0...v3.2.0)\n\n> 3 October 2018\n\n- Adding updated shorthand methods for devServer [`#23`](https://github.com/neutrinojs/webpack-chain/pull/23)\n\n#### [v3.1.0](https://github.com/neutrinojs/webpack-chain/compare/v3.0.0...v3.1.0)\n\n> 3 October 2018\n\n- Allow conditional configuration via when [`#22`](https://github.com/neutrinojs/webpack-chain/pull/22)\n- Update README with links to previous docs versions [`0dc3984`](https://github.com/neutrinojs/webpack-chain/commit/0dc39841b76c5e4d9493fa86d7f65e66145a6964)\n- Update README with links to previous docs versions [`bcc2362`](https://github.com/neutrinojs/webpack-chain/commit/bcc2362d396cee736feaa5e4537150b4a1fa2d4a)\n\n### [v3.0.0](https://github.com/neutrinojs/webpack-chain/compare/v2.0.1...v3.0.0)\n\n> 3 October 2018\n\n- Make rule.include, rule.exclude, loaders and plugins more extensible [`#16`](https://github.com/neutrinojs/webpack-chain/pull/16)\n\n#### [v2.0.1](https://github.com/neutrinojs/webpack-chain/compare/v2.0.0...v2.0.1)\n\n> 3 October 2018\n\n- undefined plugin [`#17`](https://github.com/neutrinojs/webpack-chain/pull/17)\n\n### [v2.0.0](https://github.com/neutrinojs/webpack-chain/compare/v1.4.3...v2.0.0)\n\n> 3 October 2018\n\n- Adding testing, which informed v2 API, updated docs to reflect [`#14`](https://github.com/neutrinojs/webpack-chain/pull/14)\n- Make Plugin API consistent with Loader API [`#13`](https://github.com/neutrinojs/webpack-chain/pull/13)\n- MPL license, moving to mozilla-neutrino [`f122edd`](https://github.com/neutrinojs/webpack-chain/commit/f122eddccb9f7af9742f5c447c651172700b4c50)\n\n#### [v1.4.3](https://github.com/neutrinojs/webpack-chain/compare/v1.4.2...v1.4.3)\n\n> 6 March 2017\n\n- Adding ChainedMap and ChainedSet documentation [`b071f82`](https://github.com/neutrinojs/webpack-chain/commit/b071f82042c7806f6d2df412a0154c1b985c4763)\n- Removing empty entities from cluttering configuration object [`b428e55`](https://github.com/neutrinojs/webpack-chain/commit/b428e55a671a033c133ba2e225796845307dee12)\n- Docs: getConfig -&gt; toConfig [`2468eaa`](https://github.com/neutrinojs/webpack-chain/commit/2468eaac7e4c2f74cae244a4af6d2a517483db7b)\n\n#### [v1.4.2](https://github.com/neutrinojs/webpack-chain/compare/v1.4.1...v1.4.2)\n\n> 3 October 2018\n\n- Fix bug where `exclude` doesn't return `this` [`#7`](https://github.com/neutrinojs/webpack-chain/pull/7)\n- Bumping to v1.4.2 [`38d1412`](https://github.com/neutrinojs/webpack-chain/commit/38d1412037780b815c537de2abd5f02443b80502)\n\n#### [v1.4.1](https://github.com/neutrinojs/webpack-chain/compare/v1.4.0...v1.4.1)\n\n> 3 October 2018\n\n- Allowing config merge to append to existing rule loaders [`#3`](https://github.com/neutrinojs/webpack-chain/pull/3)\n- docs(readme): fix typo in devtool option [`#1`](https://github.com/neutrinojs/webpack-chain/pull/1)\n\n#### [v1.4.0](https://github.com/neutrinojs/webpack-chain/compare/v1.3.0...v1.4.0)\n\n> 3 October 2018\n\n- Adds rule test merge via string to regex, fixes externals not chainable [`a15b49e`](https://github.com/neutrinojs/webpack-chain/commit/a15b49ec28903708c04665d0c6cac3a956558a99)\n\n#### [v1.3.0](https://github.com/neutrinojs/webpack-chain/compare/v1.2.0...v1.3.0)\n\n> 3 October 2018\n\n- Adding functionality for merging and object into a Config instance [`5f0b0c6`](https://github.com/neutrinojs/webpack-chain/commit/5f0b0c670e6ad946e0232a208abb667f749aeba4)\n\n#### [v1.2.0](https://github.com/neutrinojs/webpack-chain/compare/v1.1.0...v1.2.0)\n\n> 3 October 2018\n\n- Adds hot flag for Config.DevServer [`c64a155`](https://github.com/neutrinojs/webpack-chain/commit/c64a1558188ab2a4982a7b3f2aba95ced50c9756)\n\n#### [v1.1.0](https://github.com/neutrinojs/webpack-chain/compare/v1.0.3...v1.1.0)\n\n> 3 October 2018\n\n- Adding ChainedSet#prepend functionality [`cc86e7b`](https://github.com/neutrinojs/webpack-chain/commit/cc86e7bdbdcaca7610255039dcb31adfacb4952b)\n\n#### [v1.0.3](https://github.com/neutrinojs/webpack-chain/compare/v1.0.2...v1.0.3)\n\n> 3 October 2018\n\n- Fixes exception with empty rule entries with loader only [`7964b34`](https://github.com/neutrinojs/webpack-chain/commit/7964b347ed613c7dc18e54f912467abad2956b8b)\n\n#### [v1.0.2](https://github.com/neutrinojs/webpack-chain/compare/v1.0.1...v1.0.2)\n\n> 3 October 2018\n\n- Fixes plugin methods not chaining [`7cc56ed`](https://github.com/neutrinojs/webpack-chain/commit/7cc56ed5e331c7cc0ccd58f25feb3b1b0398829a)\n\n#### [v1.0.1](https://github.com/neutrinojs/webpack-chain/compare/v1.0.0...v1.0.1)\n\n> 3 October 2018\n\n- Shared configuration documentation [`5c6a65b`](https://github.com/neutrinojs/webpack-chain/commit/5c6a65b344113ff3522d5f7d66dfacbc0ad7fa69)\n- Avoid exceptions in empty config [`ab46ee0`](https://github.com/neutrinojs/webpack-chain/commit/ab46ee0234a04eb2b89190df32ea4287c499dd39)\n\n#### v1.0.0\n\n> 3 October 2018\n\n- initial commit [`9e2a87c`](https://github.com/neutrinojs/webpack-chain/commit/9e2a87c5f6a1f1aac3eedbd4102e40dc47a8f7f4)\n"
  },
  {
    "path": "LICENSE",
    "content": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\"\n    means each individual or legal entity that creates, contributes to\n    the creation of, or owns Covered Software.\n\n1.2. \"Contributor Version\"\n    means the combination of the Contributions of others (if any) used\n    by a Contributor and that particular Contributor's Contribution.\n\n1.3. \"Contribution\"\n    means Covered Software of a particular Contributor.\n\n1.4. \"Covered Software\"\n    means Source Code Form to which the initial Contributor has attached\n    the notice in Exhibit A, the Executable Form of such Source Code\n    Form, and Modifications of such Source Code Form, in each case\n    including portions thereof.\n\n1.5. \"Incompatible With Secondary Licenses\"\n    means\n\n    (a) that the initial Contributor has attached the notice described\n        in Exhibit B to the Covered Software; or\n\n    (b) that the Covered Software was made available under the terms of\n        version 1.1 or earlier of the License, but not also under the\n        terms of a Secondary License.\n\n1.6. \"Executable Form\"\n    means any form of the work other than Source Code Form.\n\n1.7. \"Larger Work\"\n    means a work that combines Covered Software with other material, in\n    a separate file or files, that is not Covered Software.\n\n1.8. \"License\"\n    means this document.\n\n1.9. \"Licensable\"\n    means having the right to grant, to the maximum extent possible,\n    whether at the time of the initial grant or subsequently, any and\n    all of the rights conveyed by this License.\n\n1.10. \"Modifications\"\n    means any of the following:\n\n    (a) any file in Source Code Form that results from an addition to,\n        deletion from, or modification of the contents of Covered\n        Software; or\n\n    (b) any new file in Source Code Form that contains any Covered\n        Software.\n\n1.11. \"Patent Claims\" of a Contributor\n    means any patent claim(s), including without limitation, method,\n    process, and apparatus claims, in any patent Licensable by such\n    Contributor that would be infringed, but for the grant of the\n    License, by the making, using, selling, offering for sale, having\n    made, import, or transfer of either its Contributions or its\n    Contributor Version.\n\n1.12. \"Secondary License\"\n    means either the GNU General Public License, Version 2.0, the GNU\n    Lesser General Public License, Version 2.1, the GNU Affero General\n    Public License, Version 3.0, or any later versions of those\n    licenses.\n\n1.13. \"Source Code Form\"\n    means the form of the work preferred for making modifications.\n\n1.14. \"You\" (or \"Your\")\n    means an individual or a legal entity exercising rights under this\n    License. For legal entities, \"You\" includes any entity that\n    controls, is controlled by, or is under common control with You. For\n    purposes of this definition, \"control\" means (a) the power, direct\n    or indirect, to cause the direction or management of such entity,\n    whether by contract or otherwise, or (b) ownership of more than\n    fifty percent (50%) of the outstanding shares or beneficial\n    ownership of such entity.\n\n2. License Grants and Conditions\n--------------------------------\n\n2.1. Grants\n\nEach Contributor hereby grants You a world-wide, royalty-free,\nnon-exclusive license:\n\n(a) under intellectual property rights (other than patent or trademark)\n    Licensable by such Contributor to use, reproduce, make available,\n    modify, display, perform, distribute, and otherwise exploit its\n    Contributions, either on an unmodified basis, with Modifications, or\n    as part of a Larger Work; and\n\n(b) under Patent Claims of such Contributor to make, use, sell, offer\n    for sale, have made, import, and otherwise transfer either its\n    Contributions or its Contributor Version.\n\n2.2. Effective Date\n\nThe licenses granted in Section 2.1 with respect to any Contribution\nbecome effective for each Contribution on the date the Contributor first\ndistributes such Contribution.\n\n2.3. Limitations on Grant Scope\n\nThe licenses granted in this Section 2 are the only rights granted under\nthis License. No additional rights or licenses will be implied from the\ndistribution or licensing of Covered Software under this License.\nNotwithstanding Section 2.1(b) above, no patent license is granted by a\nContributor:\n\n(a) for any code that a Contributor has removed from Covered Software;\n    or\n\n(b) for infringements caused by: (i) Your and any other third party's\n    modifications of Covered Software, or (ii) the combination of its\n    Contributions with other software (except as part of its Contributor\n    Version); or\n\n(c) under Patent Claims infringed by Covered Software in the absence of\n    its Contributions.\n\nThis License does not grant any rights in the trademarks, service marks,\nor logos of any Contributor (except as may be necessary to comply with\nthe notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\nNo Contributor makes additional grants as a result of Your choice to\ndistribute the Covered Software under a subsequent version of this\nLicense (see Section 10.2) or under the terms of a Secondary License (if\npermitted under the terms of Section 3.3).\n\n2.5. Representation\n\nEach Contributor represents that the Contributor believes its\nContributions are its original creation(s) or it has sufficient rights\nto grant the rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\nThis License is not intended to limit any rights You have under\napplicable copyright doctrines of fair use, fair dealing, or other\nequivalents.\n\n2.7. Conditions\n\nSections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted\nin Section 2.1.\n\n3. Responsibilities\n-------------------\n\n3.1. Distribution of Source Form\n\nAll distribution of Covered Software in Source Code Form, including any\nModifications that You create or to which You contribute, must be under\nthe terms of this License. You must inform recipients that the Source\nCode Form of the Covered Software is governed by the terms of this\nLicense, and how they can obtain a copy of this License. You may not\nattempt to alter or restrict the recipients' rights in the Source Code\nForm.\n\n3.2. Distribution of Executable Form\n\nIf You distribute Covered Software in Executable Form then:\n\n(a) such Covered Software must also be made available in Source Code\n    Form, as described in Section 3.1, and You must inform recipients of\n    the Executable Form how they can obtain a copy of such Source Code\n    Form by reasonable means in a timely manner, at a charge no more\n    than the cost of distribution to the recipient; and\n\n(b) You may distribute such Executable Form under the terms of this\n    License, or sublicense it under different terms, provided that the\n    license for the Executable Form does not attempt to limit or alter\n    the recipients' rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\nYou may create and distribute a Larger Work under terms of Your choice,\nprovided that You also comply with the requirements of this License for\nthe Covered Software. If the Larger Work is a combination of Covered\nSoftware with a work governed by one or more Secondary Licenses, and the\nCovered Software is not Incompatible With Secondary Licenses, this\nLicense permits You to additionally distribute such Covered Software\nunder the terms of such Secondary License(s), so that the recipient of\nthe Larger Work may, at their option, further distribute the Covered\nSoftware under the terms of either this License or such Secondary\nLicense(s).\n\n3.4. Notices\n\nYou may not remove or alter the substance of any license notices\n(including copyright notices, patent notices, disclaimers of warranty,\nor limitations of liability) contained within the Source Code Form of\nthe Covered Software, except that You may alter any license notices to\nthe extent required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\nYou may choose to offer, and to charge a fee for, warranty, support,\nindemnity or liability obligations to one or more recipients of Covered\nSoftware. However, You may do so only on Your own behalf, and not on\nbehalf of any Contributor. You must make it absolutely clear that any\nsuch warranty, support, indemnity, or liability obligation is offered by\nYou alone, and You hereby agree to indemnify every Contributor for any\nliability incurred by such Contributor as a result of warranty, support,\nindemnity or liability terms You offer. You may include additional\ndisclaimers of warranty and limitations of liability specific to any\njurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n---------------------------------------------------\n\nIf it is impossible for You to comply with any of the terms of this\nLicense with respect to some or all of the Covered Software due to\nstatute, judicial order, or regulation then You must: (a) comply with\nthe terms of this License to the maximum extent possible; and (b)\ndescribe the limitations and the code they affect. Such description must\nbe placed in a text file included with all distributions of the Covered\nSoftware under this License. Except to the extent prohibited by statute\nor regulation, such description must be sufficiently detailed for a\nrecipient of ordinary skill to be able to understand it.\n\n5. Termination\n--------------\n\n5.1. The rights granted under this License will terminate automatically\nif You fail to comply with any of its terms. However, if You become\ncompliant, then the rights granted under this License from a particular\nContributor are reinstated (a) provisionally, unless and until such\nContributor explicitly and finally terminates Your grants, and (b) on an\nongoing basis, if such Contributor fails to notify You of the\nnon-compliance by some reasonable means prior to 60 days after You have\ncome back into compliance. Moreover, Your grants from a particular\nContributor are reinstated on an ongoing basis if such Contributor\nnotifies You of the non-compliance by some reasonable means, this is the\nfirst time You have received notice of non-compliance with this License\nfrom such Contributor, and You become compliant prior to 30 days after\nYour receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\ninfringement claim (excluding declaratory judgment actions,\ncounter-claims, and cross-claims) alleging that a Contributor Version\ndirectly or indirectly infringes any patent, then the rights granted to\nYou by any and all Contributors for the Covered Software under Section\n2.1 of this License shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all\nend user license agreements (excluding distributors and resellers) which\nhave been validly granted by You or Your distributors under this License\nprior to termination shall survive termination.\n\n************************************************************************\n*                                                                      *\n*  6. Disclaimer of Warranty                                           *\n*  -------------------------                                           *\n*                                                                      *\n*  Covered Software is provided under this License on an \"as is\"       *\n*  basis, without warranty of any kind, either expressed, implied, or  *\n*  statutory, including, without limitation, warranties that the       *\n*  Covered Software is free of defects, merchantable, fit for a        *\n*  particular purpose or non-infringing. The entire risk as to the     *\n*  quality and performance of the Covered Software is with You.        *\n*  Should any Covered Software prove defective in any respect, You     *\n*  (not any Contributor) assume the cost of any necessary servicing,   *\n*  repair, or correction. This disclaimer of warranty constitutes an   *\n*  essential part of this License. No use of any Covered Software is   *\n*  authorized under this License except under this disclaimer.         *\n*                                                                      *\n************************************************************************\n\n************************************************************************\n*                                                                      *\n*  7. Limitation of Liability                                          *\n*  --------------------------                                          *\n*                                                                      *\n*  Under no circumstances and under no legal theory, whether tort      *\n*  (including negligence), contract, or otherwise, shall any           *\n*  Contributor, or anyone who distributes Covered Software as          *\n*  permitted above, be liable to You for any direct, indirect,         *\n*  special, incidental, or consequential damages of any character      *\n*  including, without limitation, damages for lost profits, loss of    *\n*  goodwill, work stoppage, computer failure or malfunction, or any    *\n*  and all other commercial damages or losses, even if such party      *\n*  shall have been informed of the possibility of such damages. This   *\n*  limitation of liability shall not apply to liability for death or   *\n*  personal injury resulting from such party's negligence to the       *\n*  extent applicable law prohibits such limitation. Some               *\n*  jurisdictions do not allow the exclusion or limitation of           *\n*  incidental or consequential damages, so this exclusion and          *\n*  limitation may not apply to You.                                    *\n*                                                                      *\n************************************************************************\n\n8. Litigation\n-------------\n\nAny litigation relating to this License may be brought only in the\ncourts of a jurisdiction where the defendant maintains its principal\nplace of business and such litigation shall be governed by laws of that\njurisdiction, without reference to its conflict-of-law provisions.\nNothing in this Section shall prevent a party's ability to bring\ncross-claims or counter-claims.\n\n9. Miscellaneous\n----------------\n\nThis License represents the complete agreement concerning the subject\nmatter hereof. If any provision of this License is held to be\nunenforceable, such provision shall be reformed only to the extent\nnecessary to make it enforceable. Any law or regulation which provides\nthat the language of a contract shall be construed against the drafter\nshall not be used to construe this License against a Contributor.\n\n10. Versions of the License\n---------------------------\n\n10.1. New Versions\n\nMozilla Foundation is the license steward. Except as provided in Section\n10.3, no one other than the license steward has the right to modify or\npublish new versions of this License. Each version will be given a\ndistinguishing version number.\n\n10.2. Effect of New Versions\n\nYou may distribute the Covered Software under the terms of the version\nof the License under which You originally received the Covered Software,\nor under the terms of any subsequent version published by the license\nsteward.\n\n10.3. Modified Versions\n\nIf you create software not governed by this License, and you want to\ncreate a new license for such software, you may create and use a\nmodified version of this License if you rename the license and remove\nany references to the name of the license steward (except to note that\nsuch modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary\nLicenses\n\nIf You choose to distribute Source Code Form that is Incompatible With\nSecondary Licenses under the terms of this version of the License, the\nnotice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n-------------------------------------------\n\n  This Source Code Form is subject to the terms of the Mozilla Public\n  License, v. 2.0. If a copy of the MPL was not distributed with this\n  file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular\nfile, then You may include the notice in a location (such as a LICENSE\nfile in a relevant directory) where a recipient would be likely to look\nfor such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - \"Incompatible With Secondary Licenses\" Notice\n---------------------------------------------------------\n\n  This Source Code Form is \"Incompatible With Secondary Licenses\", as\n  defined by the Mozilla Public License, v. 2.0."
  },
  {
    "path": "README.md",
    "content": "# webpack-chain\n\n[![NPM version][npm-image]][npm-url]\n[![NPM downloads][npm-downloads]][npm-url]\n[![CI Status][ci-image]][ci-url]\n\nUse a chaining API to generate and simplify the modification of webpack 4 configurations.\n\nThis documentation corresponds to v7 of webpack-chain. For previous versions, see:\n\n* [v6 docs](https://github.com/neutrinojs/webpack-chain/tree/v6)\n* [v5 docs](https://github.com/neutrinojs/webpack-chain/tree/v5)\n* [v4 docs](https://github.com/neutrinojs/webpack-chain/tree/v4)\n* [v3 docs](https://github.com/neutrinojs/webpack-chain/tree/v3)\n* [v2 docs](https://github.com/neutrinojs/webpack-chain/tree/v2)\n* [v1 docs](https://github.com/neutrinojs/webpack-chain/tree/v1)\n\n_Note: while webpack-chain is utilized extensively in Neutrino, this package is\ncompletely standalone and can be used by any project._\n\n**[Chinese docs(中文文档)](https://github.com/Yatoo2018/webpack-chain/tree/zh-cmn-Hans)**\n\n## Introduction\n\nwebpack's core configuration is based on creating and modifying a\npotentially unwieldy JavaScript object. While this is OK for configurations\non individual projects, trying to share these objects across projects and\nmake subsequent modifications gets messy, as you need to have a deep\nunderstanding of the underlying object structure to make those changes.\n\n`webpack-chain` attempts to improve this process by providing a chainable or\nfluent API for creating and modifying webpack configurations. Key portions\nof the API can be referenced by user-specified names, which helps to\nstandardize how to modify a configuration across projects.\n\nThis is easier explained through the examples following.\n\n## Installation\n\n`webpack-chain` requires Node.js 12 or higher. `webpack-chain` also only creates\nconfiguration objects designed for use with webpack 4.\n\nYou may install this package using either Yarn or npm (choose one):\n\n**Yarn**\n\n```bash\nyarn add --dev webpack-chain\n```\n\n**npm**\n\n```bash\nnpm install --save-dev webpack-chain\n```\n\n## Getting Started\n\nOnce you have `webpack-chain` installed, you can start creating a\nwebpack configuration. For this guide, our example base configuration will\nbe `webpack.config.js` in the root of our project directory.\n\n```js\n// Require the webpack-chain module. This module exports a single\n// constructor function for creating a configuration API.\nconst Config = require('webpack-chain');\n\n// Instantiate the configuration with a new API\nconst config = new Config();\n\n// Make configuration changes using the chain API.\n// Every API call tracks a change to the stored configuration.\n\nconfig\n  // Interact with entry points\n  .entry('index')\n    .add('src/index.js')\n    .end()\n  // Modify output settings\n  .output\n    .path('dist')\n    .filename('[name].bundle.js');\n\n// Create named rules which can be modified later\nconfig.module\n  .rule('lint')\n    .test(/\\.js$/)\n    .pre()\n    .include\n      .add('src')\n      .end()\n    // Even create named uses (loaders)\n    .use('eslint')\n      .loader('eslint-loader')\n      .options({\n        rules: {\n          semi: 'off'\n        }\n      });\n\nconfig.module\n  .rule('compile')\n    .test(/\\.js$/)\n    .include\n      .add('src')\n      .add('test')\n      .end()\n    .use('babel')\n      .loader('babel-loader')\n      .options({\n        presets: [\n          ['@babel/preset-env', { modules: false }]\n        ]\n      });\n\n// Create named plugins too!\nconfig\n  .plugin('clean')\n    .use(CleanPlugin, [['dist'], { root: '/dir' }]);\n\n// Export the completed configuration object to be consumed by webpack\nmodule.exports = config.toConfig();\n```\n\nHaving shared configurations is also simple. Just export the configuration\nand call `.toConfig()` prior to passing to webpack.\n\n```js\n// webpack.core.js\nconst Config = require('webpack-chain');\nconst config = new Config();\n\n// Make configuration shared across targets\n// ...\n\nmodule.exports = config;\n\n// webpack.dev.js\nconst config = require('./webpack.core');\n\n// Dev-specific configuration\n// ...\nmodule.exports = config.toConfig();\n\n// webpack.prod.js\nconst config = require('./webpack.core');\n\n// Production-specific configuration\n// ...\nmodule.exports = config.toConfig();\n```\n\n## ChainedMap\n\nOne of the core API interfaces in webpack-chain is a `ChainedMap`. A\n`ChainedMap` operates similar to a JavaScript Map, with some conveniences for\nchaining and generating configuration. If a property is marked as being a\n`ChainedMap`, it will have an API and methods as described below:\n\n**Unless stated otherwise, these methods will return the `ChainedMap`, allowing\nyou to chain these methods.**\n\n```js\n// Remove all entries from a Map.\nclear()\n```\n\n```js\n// Remove a single entry from a Map given its key.\n// key: *\ndelete(key)\n```\n\n```js\n// Fetch the value from a Map located at the corresponding key.\n// key: *\n// returns: value\nget(key)\n```\n\n```js\n// Fetch the value from a Map located at the corresponding key.\n// If the key is missing, the key is set to the result of function fn.\n// key: *\n// fn: Function () -> value\n// returns: value\ngetOrCompute(key, fn)\n```\n\n```js\n// Set a value on the Map stored at the `key` location.\n// key: *\n// value: *\nset(key, value)\n```\n\n```js\n// Returns `true` or `false` based on whether a Map as has a value set at a\n// particular key.\n// key: *\n// returns: Boolean\nhas(key)\n```\n\n```js\n// Returns an array of all the values stored in the Map.\n// returns: Array\nvalues()\n```\n\n```js\n// Returns an object of all the entries in the backing Map\n// where the key is the object property, and the value\n// corresponding to the key. Will return `undefined` if the backing\n// Map is empty.\n// This will order properties by their name if the value is\n// a ChainedMap that used .before() or .after().\n// returns: Object, undefined if empty\nentries()\n```\n\n```js\n// Provide an object which maps its properties and values\n// into the backing Map as keys and values.\n// You can also provide an array as the second argument\n// for property names to omit from being merged.\n// obj: Object\n// omit: Optional Array\nmerge(obj, omit)\n```\n\n```js\n// Execute a function against the current configuration context\n// handler: Function -> ChainedMap\n  // A function which is given a single argument of the ChainedMap instance\nbatch(handler)\n```\n\n```js\n// Conditionally execute a function to continue configuration\n// condition: Boolean\n// whenTruthy: Function -> ChainedMap\n  // invoked when condition is truthy, given a single argument of the ChainedMap instance\n// whenFalsy: Optional Function -> ChainedMap\n  // invoked when condition is falsy, given a single argument of the ChainedMap instance\nwhen(condition, whenTruthy, whenFalsy)\n```\n\n## ChainedSet\n\nAnother of the core API interfaces in webpack-chain is a `ChainedSet`. A\n`ChainedSet` operates similar to a JavaScript Set, with some conveniences for\nchaining and generating configuration. If a property is marked as being a\n`ChainedSet`, it will have an API and methods as described below:\n\n**Unless stated otherwise, these methods will return the `ChainedSet`, allowing\nyou to chain these methods.**\n\n```js\n// Add/append a value to the end of a Set.\n// value: *\nadd(value)\n```\n\n```js\n// Add a value to the beginning of a Set.\n// value: *\nprepend(value)\n```\n\n```js\n// Remove all values from a Set.\nclear()\n```\n\n```js\n// Remove a specific value from a Set.\n// value: *\ndelete(value)\n```\n\n```js\n// Returns `true` or `false` based on whether or not the\n// backing Set contains the specified value.\n// value: *\n// returns: Boolean\nhas(value)\n```\n\n```js\n// Returns an array of values contained in the backing Set.\n// returns: Array\nvalues()\n```\n\n```js\n// Concatenates the given array to the end of the backing Set.\n// arr: Array\nmerge(arr)\n```\n\n```js\n// Execute a function against the current configuration context\n// handler: Function -> ChainedSet\n  // A function which is given a single argument of the ChainedSet instance\nbatch(handler)\n```\n\n```js\n// Conditionally execute a function to continue configuration\n// condition: Boolean\n// whenTruthy: Function -> ChainedSet\n  // invoked when condition is truthy, given a single argument of the ChainedSet instance\n// whenFalsy: Optional Function -> ChainedSet\n  // invoked when condition is falsy, given a single argument of the ChainedSet instance\nwhen(condition, whenTruthy, whenFalsy)\n```\n\n## Shorthand methods\n\nA number of shorthand methods exist for setting a value on a `ChainedMap`\nwith the same key as the shorthand method name.\nFor example, `devServer.hot` is a shorthand method, so it can be used as:\n\n```js\n// A shorthand method for setting a value on a ChainedMap\ndevServer.hot(true);\n\n// This would be equivalent to:\ndevServer.set('hot', true);\n```\n\nA shorthand method is chainable, so calling it will return the original\ninstance, allowing you to continue to chain.\n\n### Config\n\nCreate a new configuration object.\n\n```js\nconst Config = require('webpack-chain');\n\nconst config = new Config();\n```\n\nMoving to deeper points in the API will change the context of what you\nare modifying. You can move back to the higher context by either referencing\nthe top-level `config` again, or by calling `.end()` to move up one level.\nIf you are familiar with jQuery, `.end()` works similarly. All API calls\nwill return the API instance at the current context unless otherwise\nspecified. This is so you may chain API calls continuously if desired.\n\nFor details on the specific values that are valid for all shorthand and\nlow-level methods, please refer to their corresponding name in the\n[webpack docs hierarchy](https://webpack.js.org/configuration/).\n\n```js\nConfig : ChainedMap\n```\n\n#### Config shorthand methods\n\n```js\nconfig\n  .amd(amd)\n  .bail(bail)\n  .cache(cache)\n  .devtool(devtool)\n  .context(context)\n  .externals(externals)\n  .loader(loader)\n  .name(name)\n  .mode(mode)\n  .parallelism(parallelism)\n  .profile(profile)\n  .recordsPath(recordsPath)\n  .recordsInputPath(recordsInputPath)\n  .recordsOutputPath(recordsOutputPath)\n  .stats(stats)\n  .target(target)\n  .watch(watch)\n  .watchOptions(watchOptions)\n```\n\n#### Config entryPoints\n\n```js\n// Backed at config.entryPoints : ChainedMap\nconfig.entry(name) : ChainedSet\n\nconfig\n  .entry(name)\n    .add(value)\n    .add(value)\n\nconfig\n  .entry(name)\n    .clear()\n\n// Using low-level config.entryPoints:\n\nconfig.entryPoints\n  .get(name)\n    .add(value)\n    .add(value)\n\nconfig.entryPoints\n  .get(name)\n    .clear()\n```\n\n#### Config output: shorthand methods\n\n```js\nconfig.output : ChainedMap\n\nconfig.output\n  .auxiliaryComment(auxiliaryComment)\n  .chunkFilename(chunkFilename)\n  .chunkLoadTimeout(chunkLoadTimeout)\n  .crossOriginLoading(crossOriginLoading)\n  .devtoolFallbackModuleFilenameTemplate(devtoolFallbackModuleFilenameTemplate)\n  .devtoolLineToLine(devtoolLineToLine)\n  .devtoolModuleFilenameTemplate(devtoolModuleFilenameTemplate)\n  .devtoolNamespace(devtoolNamespace)\n  .filename(filename)\n  .hashFunction(hashFunction)\n  .hashDigest(hashDigest)\n  .hashDigestLength(hashDigestLength)\n  .hashSalt(hashSalt)\n  .hotUpdateChunkFilename(hotUpdateChunkFilename)\n  .hotUpdateFunction(hotUpdateFunction)\n  .hotUpdateMainFilename(hotUpdateMainFilename)\n  .jsonpFunction(jsonpFunction)\n  .library(library)\n  .libraryExport(libraryExport)\n  .libraryTarget(libraryTarget)\n  .path(path)\n  .pathinfo(pathinfo)\n  .publicPath(publicPath)\n  .sourceMapFilename(sourceMapFilename)\n  .sourcePrefix(sourcePrefix)\n  .strictModuleExceptionHandling(strictModuleExceptionHandling)\n  .umdNamedDefine(umdNamedDefine)\n```\n\n#### Config resolve: shorthand methods\n\n```js\nconfig.resolve : ChainedMap\n\nconfig.resolve\n  .cachePredicate(cachePredicate)\n  .cacheWithContext(cacheWithContext)\n  .enforceExtension(enforceExtension)\n  .enforceModuleExtension(enforceModuleExtension)\n  .unsafeCache(unsafeCache)\n  .symlinks(symlinks)\n```\n\n#### Config resolve alias\n\n```js\nconfig.resolve.alias : ChainedMap\n\nconfig.resolve.alias\n  .set(key, value)\n  .set(key, value)\n  .delete(key)\n  .clear()\n```\n\n#### Config resolve modules\n\n```js\nconfig.resolve.modules : ChainedSet\n\nconfig.resolve.modules\n  .add(value)\n  .prepend(value)\n  .clear()\n```\n\n#### Config resolve aliasFields\n\n```js\nconfig.resolve.aliasFields : ChainedSet\n\nconfig.resolve.aliasFields\n  .add(value)\n  .prepend(value)\n  .clear()\n```\n\n#### Config resolve descriptionFields\n\n```js\nconfig.resolve.descriptionFields : ChainedSet\n\nconfig.resolve.descriptionFields\n  .add(value)\n  .prepend(value)\n  .clear()\n```\n\n#### Config resolve extensions\n\n```js\nconfig.resolve.extensions : ChainedSet\n\nconfig.resolve.extensions\n  .add(value)\n  .prepend(value)\n  .clear()\n```\n\n#### Config resolve mainFields\n\n```js\nconfig.resolve.mainFields : ChainedSet\n\nconfig.resolve.mainFields\n  .add(value)\n  .prepend(value)\n  .clear()\n```\n\n#### Config resolve mainFiles\n\n```js\nconfig.resolve.mainFiles : ChainedSet\n\nconfig.resolve.mainFiles\n  .add(value)\n  .prepend(value)\n  .clear()\n```\n\n#### Config resolveLoader\n\nThe API for `config.resolveLoader` is identical to `config.resolve` with\nthe following additions:\n\n#### Config resolveLoader moduleExtensions\n\n```js\nconfig.resolveLoader.moduleExtensions : ChainedSet\n\nconfig.resolveLoader.moduleExtensions\n  .add(value)\n  .prepend(value)\n  .clear()\n```\n\n#### Config resolveLoader packageMains\n\n```js\nconfig.resolveLoader.packageMains : ChainedSet\n\nconfig.resolveLoader.packageMains\n  .add(value)\n  .prepend(value)\n  .clear()\n```\n\n#### Config performance: shorthand methods\n\n```js\nconfig.performance : ChainedMap\n\nconfig.performance\n  .hints(hints)\n  .maxEntrypointSize(maxEntrypointSize)\n  .maxAssetSize(maxAssetSize)\n  .assetFilter(assetFilter)\n```\n\n#### Configuring optimizations: shorthand methods\n\n```js\nconfig.optimization : ChainedMap\n\nconfig.optimization\n  .concatenateModules(concatenateModules)\n  .flagIncludedChunks(flagIncludedChunks)\n  .mergeDuplicateChunks(mergeDuplicateChunks)\n  .minimize(minimize)\n  .namedChunks(namedChunks)\n  .namedModules(namedModules)\n  .nodeEnv(nodeEnv)\n  .noEmitOnErrors(noEmitOnErrors)\n  .occurrenceOrder(occurrenceOrder)\n  .portableRecords(portableRecords)\n  .providedExports(providedExports)\n  .removeAvailableModules(removeAvailableModules)\n  .removeEmptyChunks(removeEmptyChunks)\n  .runtimeChunk(runtimeChunk)\n  .sideEffects(sideEffects)\n  .splitChunks(splitChunks)\n  .usedExports(usedExports)\n```\n\n#### Config optimization minimizers\n\n```js\n// Backed at config.optimization.minimizers\nconfig.optimization\n  .minimizer(name) : ChainedMap\n```\n\n#### Config optimization minimizers: adding\n\n_NOTE: Do not use `new` to create the minimizer plugin, as this will be done for you._\n\n```js\nconfig.optimization\n  .minimizer(name)\n  .use(WebpackPlugin, args)\n\n// Examples\n\nconfig.optimization\n  .minimizer('css')\n  .use(OptimizeCSSAssetsPlugin, [{ cssProcessorOptions: { safe: true } }])\n\n// Minimizer plugins can also be specified by their path, allowing the expensive require()s to be\n// skipped in cases where the plugin or webpack configuration won't end up being used.\nconfig.optimization\n  .minimizer('css')\n  .use(require.resolve('optimize-css-assets-webpack-plugin'), [{ cssProcessorOptions: { safe: true } }])\n\n```\n\n#### Config optimization minimizers: modify arguments\n\n```js\nconfig.optimization\n  .minimizer(name)\n  .tap(args => newArgs)\n\n// Example\nconfig.optimization\n  .minimizer('css')\n  .tap(args => [...args, { cssProcessorOptions: { safe: false } }])\n```\n\n#### Config optimization minimizers: modify instantiation\n\n```js\nconfig.optimization\n  .minimizer(name)\n  .init((Plugin, args) => new Plugin(...args));\n```\n\n#### Config optimization minimizers: removing\n\n```js\nconfig.optimization.minimizers.delete(name)\n```\n\n#### Config plugins\n\n```js\n// Backed at config.plugins\nconfig.plugin(name) : ChainedMap\n```\n\n#### Config plugins: adding\n\n_NOTE: Do not use `new` to create the plugin, as this will be done for you._\n\n```js\nconfig\n  .plugin(name)\n  .use(WebpackPlugin, args)\n\n// Examples\n\nconfig\n  .plugin('hot')\n  .use(webpack.HotModuleReplacementPlugin);\n\n// Plugins can also be specified by their path, allowing the expensive require()s to be\n// skipped in cases where the plugin or webpack configuration won't end up being used.\nconfig\n  .plugin('env')\n  .use(require.resolve('webpack/lib/EnvironmentPlugin'), [{ 'VAR': false }]);\n```\n\n#### Config plugins: modify arguments\n\n```js\nconfig\n  .plugin(name)\n  .tap(args => newArgs)\n\n// Example\nconfig\n  .plugin('env')\n  .tap(args => [...args, 'SECRET_KEY']);\n```\n\n#### Config plugins: modify instantiation\n\n```js\nconfig\n  .plugin(name)\n  .init((Plugin, args) => new Plugin(...args));\n```\n\n#### Config plugins: removing\n\n```js\nconfig.plugins.delete(name)\n```\n\n#### Config plugins: ordering before\n\nSpecify that the current `plugin` context should operate before another named\n`plugin`. You cannot use both `.before()` and `.after()` on the same plugin.\n\n```js\nconfig\n  .plugin(name)\n    .before(otherName)\n\n// Example\n\nconfig\n  .plugin('html-template')\n    .use(HtmlWebpackTemplate)\n    .end()\n  .plugin('script-ext')\n    .use(ScriptExtWebpackPlugin)\n    .before('html-template');\n```\n\n#### Config plugins: ordering after\n\nSpecify that the current `plugin` context should operate after another named\n`plugin`. You cannot use both `.before()` and `.after()` on the same plugin.\n\n```js\nconfig\n  .plugin(name)\n    .after(otherName)\n\n// Example\n\nconfig\n  .plugin('html-template')\n    .after('script-ext')\n    .use(HtmlWebpackTemplate)\n    .end()\n  .plugin('script-ext')\n    .use(ScriptExtWebpackPlugin);\n```\n\n#### Config resolve plugins\n\n```js\n// Backed at config.resolve.plugins\nconfig.resolve.plugin(name) : ChainedMap\n```\n\n#### Config resolve plugins: adding\n\n_NOTE: Do not use `new` to create the plugin, as this will be done for you._\n\n```js\nconfig.resolve\n  .plugin(name)\n  .use(WebpackPlugin, args)\n```\n\n#### Config resolve plugins: modify arguments\n\n```js\nconfig.resolve\n  .plugin(name)\n  .tap(args => newArgs)\n```\n\n#### Config resolve plugins: modify instantiation\n\n```js\nconfig.resolve\n  .plugin(name)\n  .init((Plugin, args) => new Plugin(...args))\n```\n\n#### Config resolve plugins: removing\n\n```js\nconfig.resolve.plugins.delete(name)\n```\n\n#### Config resolve plugins: ordering before\n\nSpecify that the current `plugin` context should operate before another named\n`plugin`. You cannot use both `.before()` and `.after()` on the same resolve\nplugin.\n\n```js\nconfig.resolve\n  .plugin(name)\n    .before(otherName)\n\n// Example\n\nconfig.resolve\n  .plugin('beta')\n    .use(BetaWebpackPlugin)\n    .end()\n  .plugin('alpha')\n    .use(AlphaWebpackPlugin)\n    .before('beta');\n```\n\n#### Config resolve plugins: ordering after\n\nSpecify that the current `plugin` context should operate after another named\n`plugin`. You cannot use both `.before()` and `.after()` on the same resolve\nplugin.\n\n```js\nconfig.resolve\n  .plugin(name)\n    .after(otherName)\n\n// Example\n\nconfig.resolve\n  .plugin('beta')\n    .after('alpha')\n    .use(BetaWebpackTemplate)\n    .end()\n  .plugin('alpha')\n    .use(AlphaWebpackPlugin);\n```\n\n#### Config node\n\n```js\nconfig.node : ChainedMap\n\nconfig.node\n  .set('__dirname', 'mock')\n  .set('__filename', 'mock');\n```\n\n#### Config devServer\n\n```js\nconfig.devServer : ChainedMap\n```\n\n#### Config devServer allowedHosts\n\n```js\nconfig.devServer.allowedHosts : ChainedSet\n\nconfig.devServer.allowedHosts\n  .add(value)\n  .prepend(value)\n  .clear()\n```\n\n#### Config devServer: shorthand methods\n\n```js\nconfig.devServer\n  .after(after)\n  .before(before)\n  .bonjour(bonjour)\n  .clientLogLevel(clientLogLevel)\n  .color(color)\n  .compress(compress)\n  .contentBase(contentBase)\n  .disableHostCheck(disableHostCheck)\n  .filename(filename)\n  .headers(headers)\n  .historyApiFallback(historyApiFallback)\n  .host(host)\n  .hot(hot)\n  .hotOnly(hotOnly)\n  .http2(http2)\n  .https(https)\n  .index(index)\n  .info(info)\n  .inline(inline)\n  .lazy(lazy)\n  .mimeTypes(mimeTypes)\n  .noInfo(noInfo)\n  .open(open)\n  .openPage(openPage)\n  .overlay(overlay)\n  .pfx(pfx)\n  .pfxPassphrase(pfxPassphrase)\n  .port(port)\n  .progress(progress)\n  .proxy(proxy)\n  .public(public)\n  .publicPath(publicPath)\n  .quiet(quiet)\n  .setup(setup)\n  .socket(socket)\n  .sockHost(sockHost)\n  .sockPath(sockPath)\n  .sockPort(sockPort)\n  .staticOptions(staticOptions)\n  .stats(stats)\n  .stdin(stdin)\n  .useLocalIp(useLocalIp)\n  .watchContentBase(watchContentBase)\n  .watchOptions(watchOptions)\n  .writeToDisk(writeToDisk)\n```\n\n#### Config module\n\n```js\nconfig.module : ChainedMap\n```\n\n#### Config module: shorthand methods\n\n```js\nconfig.module : ChainedMap\n\nconfig.module\n  .noParse(noParse)\n```\n\n#### Config module rules: shorthand methods\n\n```js\nconfig.module.rules : ChainedMap\n\nconfig.module\n  .rule(name)\n    .test(test)\n    .pre()\n    .post()\n    .enforce(preOrPost)\n```\n\n#### Config module rules uses (loaders): creating\n\n```js\nconfig.module.rules{}.uses : ChainedMap\n\nconfig.module\n  .rule(name)\n    .use(name)\n      .loader(loader)\n      .options(options)\n\n// Example\n\nconfig.module\n  .rule('compile')\n    .use('babel')\n      .loader('babel-loader')\n      .options({ presets: ['@babel/preset-env'] });\n```\n\n#### Config module rules uses (loaders): modifying options\n\n```js\nconfig.module\n  .rule(name)\n    .use(name)\n      .tap(options => newOptions)\n\n// Example\n\nconfig.module\n  .rule('compile')\n    .use('babel')\n      .tap(options => merge(options, {\n        plugins: ['@babel/plugin-proposal-class-properties']\n      }));\n```\n\n#### Config module rules nested rules:\n\n```js\nconfig.module.rules{}.rules : ChainedMap<Rule>\n\nconfig.module\n  .rule(name)\n    .rule(name)\n\n// Example\n\nconfig.module\n  .rule('css')\n    .test(/\\.css$/)\n    .use('style')\n      .loader('style-loader')\n      .end()\n    .rule('postcss')\n      .resourceQuery(/postcss/)\n      .use('postcss')\n        .loader('postcss-loader')\n```\n\n#### Config module rules nested rules: ordering before\nSpecify that the current `rule` context should operate before another named\n`rule`. You cannot use both `.before()` and `.after()` on the same `rule`.\n\n```js\nconfig.module.rules{}.rules : ChainedMap<Rule>\n\nconfig.module\n  .rule(name)\n    .rule(name)\n      .before(otherName)\n\n// Example\n\nconfig.module\n  .rule('css')\n    .use('style')\n      .loader('style-loader')\n      .end()\n    .rule('postcss')\n      .resourceQuery(/postcss/)\n      .use('postcss')\n        .loader('postcss-loader')\n        .end()\n      .end()\n    .rule('css-loader')\n      .resourceQuery(/css-loader/)\n      .before('postcss')\n      .use('css-loader')\n        .loader('css-loader')\n```\n\n#### Config module rules nested rules: ordering after\nSpecify that the current `rule` context should operate after another named\n`rule`. You cannot use both `.before()` and `.after()` on the same `rule`.\n\n```js\nconfig.module.rules{}.rules : ChainedMap<Rule>\n\nconfig.module\n  .rule(name)\n    .rule(name)\n      .after(otherName)\n\n// Example\n\nconfig.module\n  .rule('css')\n    .use('style')\n      .loader('style-loader')\n      .end()\n    .rule('postcss')\n      .resourceQuery(/postcss/)\n      .after('css-loader')\n      .use('postcss')\n        .loader('postcss-loader')\n        .end()\n      .end()\n    .rule('css-loader')\n      .resourceQuery(/css-loader/)\n      .use('css-loader')\n        .loader('css-loader')\n```\n\n#### Config module rules oneOfs (conditional rules):\n\n```js\nconfig.module.rules{}.oneOfs : ChainedMap<Rule>\n\nconfig.module\n  .rule(name)\n    .oneOf(name)\n\n// Example\n\nconfig.module\n  .rule('css')\n    .oneOf('inline')\n      .resourceQuery(/inline/)\n      .use('url')\n        .loader('url-loader')\n        .end()\n      .end()\n    .oneOf('external')\n      .resourceQuery(/external/)\n      .use('file')\n        .loader('file-loader')\n```\n\n#### Config module rules oneOfs (conditional rules): ordering before\nSpecify that the current `oneOf` context should operate before another named\n`oneOf`. You cannot use both `.before()` and `.after()` on the same `oneOf`.\n\n```js\nconfig.module\n  .rule(name)\n    .oneOf(name)\n      .before()\n\n// Example\n\nconfig.module\n  .rule('scss')\n    .test(/\\.scss$/)\n    .oneOf('normal')\n      .use('sass')\n        .loader('sass-loader')\n        .end()\n      .end()\n    .oneOf('sass-vars')\n      .before('normal')\n      .resourceQuery(/\\?sassvars/)\n      .use('sass-vars')\n        .loader('sass-vars-to-js-loader')\n```\n\n#### Config module rules oneOfs (conditional rules): ordering after\nSpecify that the current `oneOf` context should operate after another named\n`oneOf`. You cannot use both `.before()` and `.after()` on the same `oneOf`.\n\n```js\nconfig.module\n  .rule(name)\n    .oneOf(name)\n      .after()\n\n// Example\n\nconfig.module\n  .rule('scss')\n    .test(/\\.scss$/)\n    .oneOf('vue')\n      .resourceQuery(/\\?vue/)\n      .use('vue-style')\n        .loader('vue-style-loader')\n        .end()\n      .end()\n    .oneOf('normal')\n      .use('sass')\n        .loader('sass-loader')\n        .end()\n      .end()\n    .oneOf('sass-vars')\n      .after('vue')\n      .resourceQuery(/\\?sassvars/)\n      .use('sass-vars')\n        .loader('sass-vars-to-js-loader')\n```\n\n#### Config module rules resolve\n\nSpecify a resolve configuration to be merged over the default `config.resolve`\nfor modules that match the rule.\n\nSee \"Config resolve\" sections above for full syntax.\n\n**Note:** This option is supported by webpack since 4.36.1.\n\n```js\nconfig.module\n  .rule(name)\n    .resolve\n\n// Example\n\nconfig.module\n  .rule('scss')\n    .test(/\\.scss$/)\n    .resolve\n      .symlinks(true)\n```\n\n---\n\n### Merging Config\n\nwebpack-chain supports merging in an object to the configuration instance which\nmatches a layout similar to how the webpack-chain schema is laid out.\n\n**Note:** This object does not match the webpack configuration schema exactly\n(for example the `[name]` keys for entry/rules/plugins), so you may need to transform\nwebpack configuration objects (such as those output by webpack-chain's `.toConfig()`)\nto match the layout below prior to passing to `.merge()`.\n\n```js\nconfig.merge({ devtool: 'source-map' });\n\nconfig.get('devtool') // \"source-map\"\n```\n\n```js\nconfig.merge({\n  [key]: value,\n\n  amd,\n  bail,\n  cache,\n  context,\n  devtool,\n  externals,\n  loader,\n  mode,\n  parallelism,\n  profile,\n  recordsPath,\n  recordsInputPath,\n  recordsOutputPath,\n  stats,\n  target,\n  watch,\n  watchOptions,\n\n  entry: {\n    [name]: [...values]\n  },\n\n  plugin: {\n    [name]: {\n      plugin: WebpackPlugin,\n      args: [...args],\n      before,\n      after\n    }\n  },\n\n  devServer: {\n    [key]: value,\n\n    clientLogLevel,\n    compress,\n    contentBase,\n    filename,\n    headers,\n    historyApiFallback,\n    host,\n    hot,\n    hotOnly,\n    https,\n    inline,\n    lazy,\n    noInfo,\n    overlay,\n    port,\n    proxy,\n    quiet,\n    setup,\n    stats,\n    watchContentBase\n  },\n\n  node: {\n    [key]: value\n  },\n\n  optimization: {\n    concatenateModules,\n    flagIncludedChunks,\n    mergeDuplicateChunks,\n    minimize,\n    minimizer: {\n      [name]: {\n        plugin: WebpackPlugin,\n        args: [...args],\n        before,\n        after\n      }\n    },\n    namedChunks,\n    namedModules,\n    nodeEnv,\n    noEmitOnErrors,\n    occurrenceOrder,\n    portableRecords,\n    providedExports,\n    removeAvailableModules,\n    removeEmptyChunks,\n    runtimeChunk,\n    sideEffects,\n    splitChunks,\n    usedExports,\n  },\n\n  performance: {\n    [key]: value,\n\n    hints,\n    maxEntrypointSize,\n    maxAssetSize,\n    assetFilter\n  },\n\n  resolve: {\n    [key]: value,\n\n    alias: {\n      [key]: value\n    },\n    aliasFields: [...values],\n    descriptionFields: [...values],\n    extensions: [...values],\n    mainFields: [...values],\n    mainFiles: [...values],\n    modules: [...values],\n\n    plugin: {\n      [name]: {\n        plugin: WebpackPlugin,\n        args: [...args],\n        before,\n        after\n      }\n    }\n  },\n\n  resolveLoader: {\n    [key]: value,\n\n    alias: {\n      [key]: value\n    },\n    aliasFields: [...values],\n    descriptionFields: [...values],\n    extensions: [...values],\n    mainFields: [...values],\n    mainFiles: [...values],\n    modules: [...values],\n    moduleExtensions: [...values],\n    packageMains: [...values],\n\n    plugin: {\n      [name]: {\n        plugin: WebpackPlugin,\n        args: [...args],\n        before,\n        after\n      }\n    }\n  },\n\n  module: {\n    [key]: value,\n\n    rule: {\n      [name]: {\n        [key]: value,\n\n        enforce,\n        issuer,\n        parser,\n        resource,\n        resourceQuery,\n        test,\n\n        include: [...paths],\n        exclude: [...paths],\n\n        rules: {\n          [name]: Rule\n        },\n\n        oneOf: {\n          [name]: Rule\n        },\n\n        use: {\n          [name]: {\n            loader: LoaderString,\n            options: LoaderOptions,\n            before,\n            after\n          }\n        }\n      }\n    }\n  }\n})\n```\n\n### Conditional configuration\n\nWhen working with instances of `ChainedMap` and `ChainedSet`, you can perform\nconditional configuration using `when`. You must specify an expression to\n`when()` which will be evaluated for truthiness or falsiness. If the expression\nis truthy, the first function argument will be invoked with an instance of the\ncurrent chained instance. You can optionally provide a second function to be\ninvoked when the condition is falsy, which is also given the current chained\ninstance.\n\n```js\n// Example: Only add minify plugin during production\nconfig\n  .when(process.env.NODE_ENV === 'production', config => {\n    config\n      .plugin('minify')\n      .use(BabiliWebpackPlugin);\n  });\n```\n\n```js\n// Example: Only add minify plugin during production,\n// otherwise set devtool to source-map\nconfig\n  .when(process.env.NODE_ENV === 'production',\n    config => config.plugin('minify').use(BabiliWebpackPlugin),\n    config => config.devtool('source-map')\n  );\n```\n\n### Inspecting generated configuration\n\nYou can inspect the generated webpack config using `config.toString()`. This\nwill generate a stringified version of the config with comment hints for named\nrules, uses and plugins:\n\n```js\nconfig\n  .module\n    .rule('compile')\n      .test(/\\.js$/)\n      .use('babel')\n        .loader('babel-loader');\n\nconfig.toString();\n\n/*\n{\n  module: {\n    rules: [\n      /* config.module.rule('compile') */\n      {\n        test: /\\.js$/,\n        use: [\n          /* config.module.rule('compile').use('babel') */\n          {\n            loader: 'babel-loader'\n          }\n        ]\n      }\n    ]\n  }\n}\n*/\n```\n\nBy default the generated string cannot be used directly as real webpack config\nif it contains objects and plugins that need to be required. In order to\ngenerate usable config, you can customize how objects and plugins are\nstringified by setting a special `__expression` property on them:\n\n```js\nconst sass = require('sass');\nsass.__expression = `require('sass')`;\n\nclass MyPlugin {}\nMyPlugin.__expression = `require('my-plugin')`;\n\nfunction myFunction () {}\nmyFunction.__expression = `require('my-function')`;\n\nconfig\n  .plugin('example')\n    .use(MyPlugin, [{ fn: myFunction, implementation: sass, }]);\n\nconfig.toString();\n\n/*\n{\n  plugins: [\n    new (require('my-plugin'))({\n      fn: require('my-function'),\n      implementation: require('sass')\n    })\n  ]\n}\n*/\n```\n\nPlugins specified via their path will have their `require()` statement generated\nautomatically:\n\n```js\nconfig\n  .plugin('env')\n    .use(require.resolve('webpack/lib/ProvidePlugin'), [{ jQuery: 'jquery' }])\n\nconfig.toString();\n\n/*\n{\n  plugins: [\n    new (require('/foo/bar/src/node_modules/webpack/lib/EnvironmentPlugin.js'))(\n      {\n        jQuery: 'jquery'\n      }\n    )\n  ]\n}\n*/\n```\n\nYou can also call `toString` as a static method on `Config` in order to\nmodify the configuration object prior to stringifying.\n\n```js\nConfig.toString({\n  ...config.toConfig(),\n  module: {\n    defaultRules: [\n      {\n        use: [\n          {\n            loader: 'banner-loader',\n            options: { prefix: 'banner-prefix.txt' },\n          },\n        ],\n      },\n    ],\n  },\n})\n```\n\n```\n{\n  plugins: [\n    /* config.plugin('foo') */\n    new TestPlugin()\n  ],\n  module: {\n    defaultRules: [\n      {\n        use: [\n          {\n            loader: 'banner-loader',\n            options: {\n              prefix: 'banner-prefix.txt'\n            }\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\n[npm-image]: https://img.shields.io/npm/v/webpack-chain.svg\n[npm-downloads]: https://img.shields.io/npm/dt/webpack-chain.svg\n[npm-url]: https://www.npmjs.com/package/webpack-chain\n[ci-image]: https://github.com/neutrinojs/webpack-chain/actions/workflows/ci.yml/badge.svg\n[ci-url]: https://github.com/neutrinojs/webpack-chain/actions/workflows/ci.yml\n"
  },
  {
    "path": "jest.config.js",
    "content": "module.exports = {\n  testEnvironment: 'node',\n  testMatch: ['**/test/**/*.js'],\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"webpack-chain\",\n  \"version\": \"7.0.0-dev\",\n  \"main\": \"src/Config.js\",\n  \"typings\": \"types/index.d.ts\",\n  \"repository\": \"neutrinojs/webpack-chain\",\n  \"keywords\": [\n    \"webpack\",\n    \"config\",\n    \"chain\",\n    \"fluent\",\n    \"api\"\n  ],\n  \"engines\": {\n    \"node\": \">=12\"\n  },\n  \"files\": [\n    \"src\",\n    \"types/*.d.ts\"\n  ],\n  \"author\": \"Eli Perelman <eli@eliperelman.com>\",\n  \"license\": \"MPL-2.0\",\n  \"scripts\": {\n    \"fix\": \"yarn lint:fix && yarn style:fix\",\n    \"lint\": \"eslint --cache --max-warnings 0 --format codeframe .\",\n    \"lint:fix\": \"yarn lint --fix\",\n    \"style\": \"prettier --check .\",\n    \"style:fix\": \"prettier --write .\",\n    \"test\": \"jest\",\n    \"test:types\": \"tsc -p ./types/test/tsconfig.json\",\n    \"changelog\": \"auto-changelog --remote upstream --commit-limit false\",\n    \"version\": \"yarn changelog --package && git add CHANGELOG.md\"\n  },\n  \"dependencies\": {\n    \"deepmerge\": \"^1.5.2\",\n    \"javascript-stringify\": \"^2.0.1\"\n  },\n  \"devDependencies\": {\n    \"@types/enhanced-resolve\": \"^3.0.6\",\n    \"@types/tapable\": \"^1.0.6\",\n    \"@types/webpack\": \"^4.41.21\",\n    \"auto-changelog\": \"^2.2.0\",\n    \"eslint\": \"^8.0.0\",\n    \"eslint-config-airbnb-base\": \"^14.2.0\",\n    \"eslint-config-prettier\": \"^8.0.0\",\n    \"eslint-formatter-codeframe\": \"^7.32.1\",\n    \"eslint-plugin-import\": \"^2.22.0\",\n    \"eslint-plugin-jest\": \"^25.0.0\",\n    \"jest\": \"^27.0.0\",\n    \"prettier\": \"^2.0.5\",\n    \"typescript\": \"^4.0.0\",\n    \"webpack\": \"^4.43.0\"\n  }\n}\n"
  },
  {
    "path": "renovate.json",
    "content": "{\n  \"extends\": [\n    \"config:base\",\n    \":maintainLockFilesWeekly\",\n    \":prNotPending\",\n    \":semanticCommitsDisabled\",\n    \":unpublishSafe\"\n  ],\n  \"reviewers\": [\"neutrinojs/core-contributors\"],\n  \"pinVersions\": false\n}\n"
  },
  {
    "path": "src/Chainable.js",
    "content": "module.exports = class {\n  constructor(parent) {\n    this.parent = parent;\n  }\n\n  batch(handler) {\n    handler(this);\n    return this;\n  }\n\n  end() {\n    return this.parent;\n  }\n};\n"
  },
  {
    "path": "src/ChainedMap.js",
    "content": "const merge = require('deepmerge');\nconst Chainable = require('./Chainable');\n\nmodule.exports = class extends Chainable {\n  constructor(parent) {\n    super(parent);\n    this.store = new Map();\n  }\n\n  extend(methods) {\n    this.shorthands = methods;\n    methods.forEach((method) => {\n      this[method] = (value) => this.set(method, value);\n    });\n    return this;\n  }\n\n  clear() {\n    this.store.clear();\n    return this;\n  }\n\n  delete(key) {\n    this.store.delete(key);\n    return this;\n  }\n\n  order() {\n    const entries = [...this.store].reduce((acc, [key, value]) => {\n      acc[key] = value;\n      return acc;\n    }, {});\n    const names = Object.keys(entries);\n    const order = [...names];\n\n    names.forEach((name) => {\n      if (!entries[name]) {\n        return;\n      }\n\n      const { __before, __after } = entries[name];\n\n      if (__before && order.includes(__before)) {\n        order.splice(order.indexOf(name), 1);\n        order.splice(order.indexOf(__before), 0, name);\n      } else if (__after && order.includes(__after)) {\n        order.splice(order.indexOf(name), 1);\n        order.splice(order.indexOf(__after) + 1, 0, name);\n      }\n    });\n\n    return { entries, order };\n  }\n\n  entries() {\n    const { entries, order } = this.order();\n\n    if (order.length) {\n      return entries;\n    }\n\n    return undefined;\n  }\n\n  values() {\n    const { entries, order } = this.order();\n\n    return order.map((name) => entries[name]);\n  }\n\n  get(key) {\n    return this.store.get(key);\n  }\n\n  getOrCompute(key, fn) {\n    if (!this.has(key)) {\n      this.set(key, fn());\n    }\n    return this.get(key);\n  }\n\n  has(key) {\n    return this.store.has(key);\n  }\n\n  set(key, value) {\n    this.store.set(key, value);\n    return this;\n  }\n\n  merge(obj, omit = []) {\n    Object.keys(obj).forEach((key) => {\n      if (omit.includes(key)) {\n        return;\n      }\n\n      const value = obj[key];\n\n      if (\n        (!Array.isArray(value) && typeof value !== 'object') ||\n        value === null ||\n        !this.has(key)\n      ) {\n        this.set(key, value);\n      } else {\n        this.set(key, merge(this.get(key), value));\n      }\n    });\n\n    return this;\n  }\n\n  clean(obj) {\n    return Object.keys(obj).reduce((acc, key) => {\n      const value = obj[key];\n\n      if (value === undefined) {\n        return acc;\n      }\n\n      if (Array.isArray(value) && !value.length) {\n        return acc;\n      }\n\n      if (\n        Object.prototype.toString.call(value) === '[object Object]' &&\n        !Object.keys(value).length\n      ) {\n        return acc;\n      }\n\n      acc[key] = value;\n\n      return acc;\n    }, {});\n  }\n\n  when(\n    condition,\n    whenTruthy = Function.prototype,\n    whenFalsy = Function.prototype,\n  ) {\n    if (condition) {\n      whenTruthy(this);\n    } else {\n      whenFalsy(this);\n    }\n\n    return this;\n  }\n};\n"
  },
  {
    "path": "src/ChainedSet.js",
    "content": "const Chainable = require('./Chainable');\n\nmodule.exports = class extends Chainable {\n  constructor(parent) {\n    super(parent);\n    this.store = new Set();\n  }\n\n  add(value) {\n    this.store.add(value);\n    return this;\n  }\n\n  prepend(value) {\n    this.store = new Set([value, ...this.store]);\n    return this;\n  }\n\n  clear() {\n    this.store.clear();\n    return this;\n  }\n\n  delete(value) {\n    this.store.delete(value);\n    return this;\n  }\n\n  values() {\n    return [...this.store];\n  }\n\n  has(value) {\n    return this.store.has(value);\n  }\n\n  merge(arr) {\n    this.store = new Set([...this.store, ...arr]);\n    return this;\n  }\n\n  when(\n    condition,\n    whenTruthy = Function.prototype,\n    whenFalsy = Function.prototype,\n  ) {\n    if (condition) {\n      whenTruthy(this);\n    } else {\n      whenFalsy(this);\n    }\n\n    return this;\n  }\n};\n"
  },
  {
    "path": "src/Config.js",
    "content": "const ChainedMap = require('./ChainedMap');\nconst ChainedSet = require('./ChainedSet');\nconst Resolve = require('./Resolve');\nconst ResolveLoader = require('./ResolveLoader');\nconst Output = require('./Output');\nconst DevServer = require('./DevServer');\nconst Plugin = require('./Plugin');\nconst Module = require('./Module');\nconst Optimization = require('./Optimization');\nconst Performance = require('./Performance');\n\nmodule.exports = class extends ChainedMap {\n  constructor() {\n    super();\n    this.devServer = new DevServer(this);\n    this.entryPoints = new ChainedMap(this);\n    this.module = new Module(this);\n    this.node = new ChainedMap(this);\n    this.optimization = new Optimization(this);\n    this.output = new Output(this);\n    this.performance = new Performance(this);\n    this.plugins = new ChainedMap(this);\n    this.resolve = new Resolve(this);\n    this.resolveLoader = new ResolveLoader(this);\n    this.extend([\n      'amd',\n      'bail',\n      'cache',\n      'context',\n      'devtool',\n      'externals',\n      'loader',\n      'mode',\n      'name',\n      'parallelism',\n      'profile',\n      'recordsInputPath',\n      'recordsPath',\n      'recordsOutputPath',\n      'stats',\n      'target',\n      'watch',\n      'watchOptions',\n    ]);\n  }\n\n  static toString(config, { verbose = false, configPrefix = 'config' } = {}) {\n    // eslint-disable-next-line global-require\n    const { stringify } = require('javascript-stringify');\n\n    return stringify(\n      config,\n      (value, indent, stringify) => {\n        // improve plugin output\n        if (value && value.__pluginName) {\n          const prefix = `/* ${configPrefix}.${value.__pluginType}('${value.__pluginName}') */\\n`;\n          const constructorExpression = value.__pluginPath\n            ? // The path is stringified to ensure special characters are escaped\n              // (such as the backslashes in Windows-style paths).\n              `(require(${stringify(value.__pluginPath)}))`\n            : value.__pluginConstructorName;\n\n          if (constructorExpression) {\n            // get correct indentation for args by stringifying the args array and\n            // discarding the square brackets.\n            const args = stringify(value.__pluginArgs).slice(1, -1);\n            return `${prefix}new ${constructorExpression}(${args})`;\n          }\n          return (\n            prefix +\n            stringify(\n              value.__pluginArgs && value.__pluginArgs.length\n                ? { args: value.__pluginArgs }\n                : {},\n            )\n          );\n        }\n\n        // improve rule/use output\n        if (value && value.__ruleNames) {\n          const ruleTypes = value.__ruleTypes;\n          const prefix = `/* ${configPrefix}.module${value.__ruleNames\n            .map(\n              (r, index) => `.${ruleTypes ? ruleTypes[index] : 'rule'}('${r}')`,\n            )\n            .join('')}${\n            value.__useName ? `.use('${value.__useName}')` : ``\n          } */\\n`;\n          return prefix + stringify(value);\n        }\n\n        if (value && value.__expression) {\n          return value.__expression;\n        }\n\n        // shorten long functions\n        if (typeof value === 'function') {\n          if (!verbose && value.toString().length > 100) {\n            return `function () { /* omitted long function */ }`;\n          }\n        }\n\n        return stringify(value);\n      },\n      2,\n    );\n  }\n\n  entry(name) {\n    return this.entryPoints.getOrCompute(name, () => new ChainedSet(this));\n  }\n\n  plugin(name) {\n    return this.plugins.getOrCompute(name, () => new Plugin(this, name));\n  }\n\n  toConfig() {\n    const entryPoints = this.entryPoints.entries() || {};\n\n    return this.clean(\n      Object.assign(this.entries() || {}, {\n        node: this.node.entries(),\n        output: this.output.entries(),\n        resolve: this.resolve.toConfig(),\n        resolveLoader: this.resolveLoader.toConfig(),\n        devServer: this.devServer.toConfig(),\n        module: this.module.toConfig(),\n        optimization: this.optimization.toConfig(),\n        plugins: this.plugins.values().map((plugin) => plugin.toConfig()),\n        performance: this.performance.entries(),\n        entry: Object.keys(entryPoints).reduce(\n          (acc, key) =>\n            Object.assign(acc, { [key]: entryPoints[key].values() }),\n          {},\n        ),\n      }),\n    );\n  }\n\n  toString(options) {\n    return module.exports.toString(this.toConfig(), options);\n  }\n\n  merge(obj = {}, omit = []) {\n    const omissions = [\n      'node',\n      'output',\n      'resolve',\n      'resolveLoader',\n      'devServer',\n      'optimization',\n      'performance',\n      'module',\n    ];\n\n    if (!omit.includes('entry') && 'entry' in obj) {\n      Object.keys(obj.entry).forEach((name) =>\n        this.entry(name).merge([].concat(obj.entry[name])),\n      );\n    }\n\n    if (!omit.includes('plugin') && 'plugin' in obj) {\n      Object.keys(obj.plugin).forEach((name) =>\n        this.plugin(name).merge(obj.plugin[name]),\n      );\n    }\n\n    omissions.forEach((key) => {\n      if (!omit.includes(key) && key in obj) {\n        this[key].merge(obj[key]);\n      }\n    });\n\n    return super.merge(obj, [...omit, ...omissions, 'entry', 'plugin']);\n  }\n};\n"
  },
  {
    "path": "src/DevServer.js",
    "content": "const ChainedMap = require('./ChainedMap');\nconst ChainedSet = require('./ChainedSet');\n\nmodule.exports = class extends ChainedMap {\n  constructor(parent) {\n    super(parent);\n\n    this.allowedHosts = new ChainedSet(this);\n\n    this.extend([\n      'after',\n      'before',\n      'bonjour',\n      'clientLogLevel',\n      'color',\n      'compress',\n      'contentBase',\n      'disableHostCheck',\n      'filename',\n      'headers',\n      'historyApiFallback',\n      'host',\n      'hot',\n      'hotOnly',\n      'http2',\n      'https',\n      'index',\n      'info',\n      'inline',\n      'lazy',\n      'mimeTypes',\n      'noInfo',\n      'open',\n      'openPage',\n      'overlay',\n      'pfx',\n      'pfxPassphrase',\n      'port',\n      'proxy',\n      'progress',\n      'public',\n      'publicPath',\n      'quiet',\n      'setup',\n      'socket',\n      'sockHost',\n      'sockPath',\n      'sockPort',\n      'staticOptions',\n      'stats',\n      'stdin',\n      'useLocalIp',\n      'watchContentBase',\n      'watchOptions',\n      'writeToDisk',\n    ]);\n  }\n\n  toConfig() {\n    return this.clean({\n      allowedHosts: this.allowedHosts.values(),\n      ...(this.entries() || {}),\n    });\n  }\n\n  merge(obj, omit = []) {\n    if (!omit.includes('allowedHosts') && 'allowedHosts' in obj) {\n      this.allowedHosts.merge(obj.allowedHosts);\n    }\n\n    return super.merge(obj, ['allowedHosts']);\n  }\n};\n"
  },
  {
    "path": "src/Module.js",
    "content": "const ChainedMap = require('./ChainedMap');\nconst Rule = require('./Rule');\n\nmodule.exports = class extends ChainedMap {\n  constructor(parent) {\n    super(parent);\n    this.rules = new ChainedMap(this);\n    this.defaultRules = new ChainedMap(this);\n    this.extend(['noParse', 'strictExportPresence']);\n  }\n\n  defaultRule(name) {\n    return this.defaultRules.getOrCompute(\n      name,\n      () => new Rule(this, name, 'defaultRule'),\n    );\n  }\n\n  rule(name) {\n    return this.rules.getOrCompute(name, () => new Rule(this, name, 'rule'));\n  }\n\n  toConfig() {\n    return this.clean(\n      Object.assign(this.entries() || {}, {\n        defaultRules: this.defaultRules.values().map((r) => r.toConfig()),\n        rules: this.rules.values().map((r) => r.toConfig()),\n      }),\n    );\n  }\n\n  merge(obj, omit = []) {\n    if (!omit.includes('rule') && 'rule' in obj) {\n      Object.keys(obj.rule).forEach((name) =>\n        this.rule(name).merge(obj.rule[name]),\n      );\n    }\n\n    if (!omit.includes('defaultRule') && 'defaultRule' in obj) {\n      Object.keys(obj.defaultRule).forEach((name) =>\n        this.defaultRule(name).merge(obj.defaultRule[name]),\n      );\n    }\n\n    return super.merge(obj, ['rule', 'defaultRule']);\n  }\n};\n"
  },
  {
    "path": "src/Optimization.js",
    "content": "const ChainedMap = require('./ChainedMap');\nconst Plugin = require('./Plugin');\n\nmodule.exports = class extends ChainedMap {\n  constructor(parent) {\n    super(parent);\n    this.minimizers = new ChainedMap(this);\n    this.extend([\n      'concatenateModules',\n      'flagIncludedChunks',\n      'mergeDuplicateChunks',\n      'minimize',\n      'namedChunks',\n      'namedModules',\n      'nodeEnv',\n      'noEmitOnErrors',\n      'occurrenceOrder',\n      'portableRecords',\n      'providedExports',\n      'removeAvailableModules',\n      'removeEmptyChunks',\n      'runtimeChunk',\n      'sideEffects',\n      'splitChunks',\n      'usedExports',\n    ]);\n  }\n\n  minimizer(name) {\n    if (Array.isArray(name)) {\n      throw new Error(\n        'optimization.minimizer() no longer supports being passed an array. ' +\n          'Either switch to the new syntax (https://github.com/neutrinojs/webpack-chain#config-optimization-minimizers-adding) or downgrade to webpack-chain 4. ' +\n          'If using Vue this likely means a Vue plugin has not yet been updated to support Vue CLI 4+.',\n      );\n    }\n\n    return this.minimizers.getOrCompute(\n      name,\n      () => new Plugin(this, name, 'optimization.minimizer'),\n    );\n  }\n\n  toConfig() {\n    return this.clean(\n      Object.assign(this.entries() || {}, {\n        minimizer: this.minimizers.values().map((plugin) => plugin.toConfig()),\n      }),\n    );\n  }\n\n  merge(obj, omit = []) {\n    if (!omit.includes('minimizer') && 'minimizer' in obj) {\n      Object.keys(obj.minimizer).forEach((name) =>\n        this.minimizer(name).merge(obj.minimizer[name]),\n      );\n    }\n\n    return super.merge(obj, [...omit, 'minimizer']);\n  }\n};\n"
  },
  {
    "path": "src/Orderable.js",
    "content": "module.exports = (Class) =>\n  class extends Class {\n    before(name) {\n      if (this.__after) {\n        throw new Error(\n          `Unable to set .before(${JSON.stringify(\n            name,\n          )}) with existing value for .after()`,\n        );\n      }\n\n      this.__before = name;\n      return this;\n    }\n\n    after(name) {\n      if (this.__before) {\n        throw new Error(\n          `Unable to set .after(${JSON.stringify(\n            name,\n          )}) with existing value for .before()`,\n        );\n      }\n\n      this.__after = name;\n      return this;\n    }\n\n    merge(obj, omit = []) {\n      if (obj.before) {\n        this.before(obj.before);\n      }\n\n      if (obj.after) {\n        this.after(obj.after);\n      }\n\n      return super.merge(obj, [...omit, 'before', 'after']);\n    }\n  };\n"
  },
  {
    "path": "src/Output.js",
    "content": "const ChainedMap = require('./ChainedMap');\n\nmodule.exports = class extends ChainedMap {\n  constructor(parent) {\n    super(parent);\n    this.extend([\n      'auxiliaryComment',\n      'chunkCallbackName',\n      'chunkFilename',\n      'chunkLoadTimeout',\n      'crossOriginLoading',\n      'devtoolFallbackModuleFilenameTemplate',\n      'devtoolLineToLine',\n      'devtoolModuleFilenameTemplate',\n      'devtoolNamespace',\n      'filename',\n      'futureEmitAssets',\n      'globalObject',\n      'hashDigest',\n      'hashDigestLength',\n      'hashFunction',\n      'hashSalt',\n      'hotUpdateChunkFilename',\n      'hotUpdateFunction',\n      'hotUpdateMainFilename',\n      'jsonpFunction',\n      'library',\n      'libraryExport',\n      'libraryTarget',\n      'path',\n      'pathinfo',\n      'publicPath',\n      'sourceMapFilename',\n      'sourcePrefix',\n      'strictModuleExceptionHandling',\n      'umdNamedDefine',\n      'webassemblyModuleFilename',\n    ]);\n  }\n};\n"
  },
  {
    "path": "src/Performance.js",
    "content": "const ChainedMap = require('./ChainedMap');\n\nmodule.exports = class extends ChainedMap {\n  constructor(parent) {\n    super(parent);\n    this.extend(['assetFilter', 'hints', 'maxAssetSize', 'maxEntrypointSize']);\n  }\n};\n"
  },
  {
    "path": "src/Plugin.js",
    "content": "const ChainedMap = require('./ChainedMap');\nconst Orderable = require('./Orderable');\n\nmodule.exports = Orderable(\n  class extends ChainedMap {\n    constructor(parent, name, type = 'plugin') {\n      super(parent);\n      this.name = name;\n      this.type = type;\n      this.extend(['init']);\n\n      this.init((Plugin, args = []) => {\n        if (typeof Plugin === 'function') {\n          return new Plugin(...args);\n        }\n        return Plugin;\n      });\n    }\n\n    use(plugin, args = []) {\n      return this.set('plugin', plugin).set('args', args);\n    }\n\n    tap(f) {\n      if (!this.has('plugin')) {\n        throw new Error(\n          `Cannot call .tap() on a plugin that has not yet been defined. Call ${this.type}('${this.name}').use(<Plugin>) first.`,\n        );\n      }\n      this.set('args', f(this.get('args') || []));\n      return this;\n    }\n\n    set(key, value) {\n      if (key === 'args' && !Array.isArray(value)) {\n        throw new Error('args must be an array of arguments');\n      }\n      return super.set(key, value);\n    }\n\n    merge(obj, omit = []) {\n      if ('plugin' in obj) {\n        this.set('plugin', obj.plugin);\n      }\n\n      if ('args' in obj) {\n        this.set('args', obj.args);\n      }\n\n      return super.merge(obj, [...omit, 'args', 'plugin']);\n    }\n\n    toConfig() {\n      const init = this.get('init');\n      let plugin = this.get('plugin');\n      const args = this.get('args');\n      let pluginPath = null;\n\n      if (plugin === undefined) {\n        throw new Error(\n          `Invalid ${this.type} configuration: ${this.type}('${this.name}').use(<Plugin>) was not called to specify the plugin`,\n        );\n      }\n\n      // Support using the path to a plugin rather than the plugin itself,\n      // allowing expensive require()s to be skipped in cases where the plugin\n      // or webpack configuration won't end up being used.\n      if (typeof plugin === 'string') {\n        pluginPath = plugin;\n        // eslint-disable-next-line global-require, import/no-dynamic-require\n        plugin = require(pluginPath);\n      }\n\n      const constructorName = plugin.__expression\n        ? `(${plugin.__expression})`\n        : plugin.name;\n\n      const config = init(plugin, args);\n\n      Object.defineProperties(config, {\n        __pluginName: { value: this.name },\n        __pluginType: { value: this.type },\n        __pluginArgs: { value: args },\n        __pluginConstructorName: { value: constructorName },\n        __pluginPath: { value: pluginPath },\n      });\n\n      return config;\n    }\n  },\n);\n"
  },
  {
    "path": "src/Resolve.js",
    "content": "const ChainedMap = require('./ChainedMap');\nconst ChainedSet = require('./ChainedSet');\nconst Plugin = require('./Plugin');\n\nmodule.exports = class extends ChainedMap {\n  constructor(parent) {\n    super(parent);\n    this.alias = new ChainedMap(this);\n    this.aliasFields = new ChainedSet(this);\n    this.descriptionFiles = new ChainedSet(this);\n    this.extensions = new ChainedSet(this);\n    this.mainFields = new ChainedSet(this);\n    this.mainFiles = new ChainedSet(this);\n    this.modules = new ChainedSet(this);\n    this.plugins = new ChainedMap(this);\n    this.extend([\n      'cachePredicate',\n      'cacheWithContext',\n      'concord',\n      'enforceExtension',\n      'enforceModuleExtension',\n      'symlinks',\n      'unsafeCache',\n    ]);\n  }\n\n  plugin(name) {\n    return this.plugins.getOrCompute(\n      name,\n      () => new Plugin(this, name, 'resolve.plugin'),\n    );\n  }\n\n  toConfig() {\n    return this.clean(\n      Object.assign(this.entries() || {}, {\n        alias: this.alias.entries(),\n        aliasFields: this.aliasFields.values(),\n        descriptionFiles: this.descriptionFiles.values(),\n        extensions: this.extensions.values(),\n        mainFields: this.mainFields.values(),\n        mainFiles: this.mainFiles.values(),\n        modules: this.modules.values(),\n        plugins: this.plugins.values().map((plugin) => plugin.toConfig()),\n      }),\n    );\n  }\n\n  merge(obj, omit = []) {\n    const omissions = [\n      'alias',\n      'aliasFields',\n      'descriptionFiles',\n      'extensions',\n      'mainFields',\n      'mainFiles',\n      'modules',\n    ];\n\n    if (!omit.includes('plugin') && 'plugin' in obj) {\n      Object.keys(obj.plugin).forEach((name) =>\n        this.plugin(name).merge(obj.plugin[name]),\n      );\n    }\n\n    omissions.forEach((key) => {\n      if (!omit.includes(key) && key in obj) {\n        this[key].merge(obj[key]);\n      }\n    });\n\n    return super.merge(obj, [...omit, ...omissions, 'plugin']);\n  }\n};\n"
  },
  {
    "path": "src/ResolveLoader.js",
    "content": "const Resolve = require('./Resolve');\nconst ChainedSet = require('./ChainedSet');\n\nmodule.exports = class extends Resolve {\n  constructor(parent) {\n    super(parent);\n    this.moduleExtensions = new ChainedSet(this);\n    this.packageMains = new ChainedSet(this);\n  }\n\n  toConfig() {\n    return this.clean({\n      moduleExtensions: this.moduleExtensions.values(),\n      packageMains: this.packageMains.values(),\n      ...super.toConfig(),\n    });\n  }\n\n  merge(obj, omit = []) {\n    const omissions = ['moduleExtensions', 'packageMains'];\n\n    omissions.forEach((key) => {\n      if (!omit.includes(key) && key in obj) {\n        this[key].merge(obj[key]);\n      }\n    });\n\n    return super.merge(obj, [...omit, ...omissions]);\n  }\n};\n"
  },
  {
    "path": "src/Rule.js",
    "content": "const ChainedMap = require('./ChainedMap');\nconst ChainedSet = require('./ChainedSet');\nconst Orderable = require('./Orderable');\nconst Use = require('./Use');\nconst Resolve = require('./Resolve');\n\nfunction toArray(arr) {\n  return Array.isArray(arr) ? arr : [arr];\n}\n\nconst Rule = Orderable(\n  class extends ChainedMap {\n    constructor(parent, name, ruleType = 'rule') {\n      super(parent);\n      this.name = name;\n      this.names = [];\n      this.ruleType = ruleType;\n      this.ruleTypes = [];\n\n      let rule = this;\n      while (rule instanceof Rule) {\n        this.names.unshift(rule.name);\n        this.ruleTypes.unshift(rule.ruleType);\n        rule = rule.parent;\n      }\n\n      this.uses = new ChainedMap(this);\n      this.include = new ChainedSet(this);\n      this.exclude = new ChainedSet(this);\n      this.rules = new ChainedMap(this);\n      this.oneOfs = new ChainedMap(this);\n      this.resolve = new Resolve(this);\n      this.extend([\n        'enforce',\n        'issuer',\n        'parser',\n        'resource',\n        'resourceQuery',\n        'sideEffects',\n        'test',\n        'type',\n      ]);\n    }\n\n    use(name) {\n      return this.uses.getOrCompute(name, () => new Use(this, name));\n    }\n\n    rule(name) {\n      return this.rules.getOrCompute(name, () => new Rule(this, name, 'rule'));\n    }\n\n    oneOf(name) {\n      return this.oneOfs.getOrCompute(\n        name,\n        () => new Rule(this, name, 'oneOf'),\n      );\n    }\n\n    pre() {\n      return this.enforce('pre');\n    }\n\n    post() {\n      return this.enforce('post');\n    }\n\n    toConfig() {\n      const config = this.clean(\n        Object.assign(this.entries() || {}, {\n          include: this.include.values(),\n          exclude: this.exclude.values(),\n          rules: this.rules.values().map((rule) => rule.toConfig()),\n          oneOf: this.oneOfs.values().map((oneOf) => oneOf.toConfig()),\n          use: this.uses.values().map((use) => use.toConfig()),\n          resolve: this.resolve.toConfig(),\n        }),\n      );\n\n      Object.defineProperties(config, {\n        __ruleNames: { value: this.names },\n        __ruleTypes: { value: this.ruleTypes },\n      });\n\n      return config;\n    }\n\n    merge(obj, omit = []) {\n      if (!omit.includes('include') && 'include' in obj) {\n        this.include.merge(toArray(obj.include));\n      }\n\n      if (!omit.includes('exclude') && 'exclude' in obj) {\n        this.exclude.merge(toArray(obj.exclude));\n      }\n\n      if (!omit.includes('use') && 'use' in obj) {\n        Object.keys(obj.use).forEach((name) =>\n          this.use(name).merge(obj.use[name]),\n        );\n      }\n\n      if (!omit.includes('rules') && 'rules' in obj) {\n        Object.keys(obj.rules).forEach((name) =>\n          this.rule(name).merge(obj.rules[name]),\n        );\n      }\n\n      if (!omit.includes('oneOf') && 'oneOf' in obj) {\n        Object.keys(obj.oneOf).forEach((name) =>\n          this.oneOf(name).merge(obj.oneOf[name]),\n        );\n      }\n\n      if (!omit.includes('resolve') && 'resolve' in obj) {\n        this.resolve.merge(obj.resolve);\n      }\n\n      if (!omit.includes('test') && 'test' in obj) {\n        this.test(\n          obj.test instanceof RegExp || typeof obj.test === 'function'\n            ? obj.test\n            : new RegExp(obj.test),\n        );\n      }\n\n      return super.merge(obj, [\n        ...omit,\n        'include',\n        'exclude',\n        'use',\n        'rules',\n        'oneOf',\n        'resolve',\n        'test',\n      ]);\n    }\n  },\n);\n\nmodule.exports = Rule;\n"
  },
  {
    "path": "src/Use.js",
    "content": "const merge = require('deepmerge');\nconst ChainedMap = require('./ChainedMap');\nconst Orderable = require('./Orderable');\n\nmodule.exports = Orderable(\n  class extends ChainedMap {\n    constructor(parent, name) {\n      super(parent);\n      this.name = name;\n      this.extend(['loader', 'options']);\n    }\n\n    tap(f) {\n      this.options(f(this.get('options')));\n      return this;\n    }\n\n    merge(obj, omit = []) {\n      if (!omit.includes('loader') && 'loader' in obj) {\n        this.loader(obj.loader);\n      }\n\n      if (!omit.includes('options') && 'options' in obj) {\n        this.options(merge(this.store.get('options') || {}, obj.options));\n      }\n\n      return super.merge(obj, [...omit, 'loader', 'options']);\n    }\n\n    toConfig() {\n      const config = this.clean(this.entries() || {});\n\n      Object.defineProperties(config, {\n        __useName: { value: this.name },\n        __ruleNames: { value: this.parent && this.parent.names },\n        __ruleTypes: { value: this.parent && this.parent.ruleTypes },\n      });\n\n      return config;\n    }\n  },\n);\n"
  },
  {
    "path": "test/Chainable.js",
    "content": "const Chainable = require('../src/Chainable');\n\ntest('calling .end() returns parent', () => {\n  const parent = { parent: true };\n  const chain = new Chainable(parent);\n\n  expect(chain.end()).toBe(parent);\n});\n\ntest('using .batch() receives context', () => {\n  const chain = new Chainable();\n  const context = chain.batch((current) => {\n    expect(current).toBe(chain);\n  });\n\n  expect(context).toBe(chain);\n});\n"
  },
  {
    "path": "test/ChainedMap.js",
    "content": "const ChainedMap = require('../src/ChainedMap');\n\ntest('is Chainable', () => {\n  const parent = { parent: true };\n  const map = new ChainedMap(parent);\n\n  expect(map.end()).toBe(parent);\n});\n\ntest('creates a backing Map', () => {\n  const map = new ChainedMap();\n\n  expect(map.store instanceof Map).toBe(true);\n});\n\ntest('set', () => {\n  const map = new ChainedMap();\n\n  expect(map.set('a', 'alpha')).toBe(map);\n  expect(map.store.get('a')).toBe('alpha');\n});\n\ntest('get', () => {\n  const map = new ChainedMap();\n\n  expect(map.set('a', 'alpha')).toBe(map);\n  expect(map.get('a')).toBe('alpha');\n});\n\ntest('getOrCompute', () => {\n  const map = new ChainedMap();\n\n  expect(map.get('a')).toBeUndefined();\n  expect(map.getOrCompute('a', () => 'alpha')).toBe('alpha');\n  expect(map.get('a')).toBe('alpha');\n});\n\ntest('clear', () => {\n  const map = new ChainedMap();\n\n  map.set('a', 'alpha');\n  map.set('b', 'beta');\n  map.set('c', 'gamma');\n\n  expect(map.store.size).toBe(3);\n  expect(map.clear()).toBe(map);\n  expect(map.store.size).toBe(0);\n});\n\ntest('delete', () => {\n  const map = new ChainedMap();\n\n  map.set('a', 'alpha');\n  map.set('b', 'beta');\n  map.set('c', 'gamma');\n\n  expect(map.delete('b')).toBe(map);\n  expect(map.store.size).toBe(2);\n  expect(map.store.has('b')).toBe(false);\n});\n\ntest('has', () => {\n  const map = new ChainedMap();\n\n  map.set('a', 'alpha');\n  map.set('b', 'beta');\n  map.set('c', 'gamma');\n\n  expect(map.has('b')).toBe(true);\n  expect(map.has('d')).toBe(false);\n  expect(map.has('b')).toBe(map.store.has('b'));\n});\n\ntest('values', () => {\n  const map = new ChainedMap();\n\n  map.set('a', 'alpha');\n  map.set('b', 'beta');\n  map.set('c', 'gamma');\n\n  expect(map.values()).toStrictEqual(['alpha', 'beta', 'gamma']);\n});\n\ntest('entries with values', () => {\n  const map = new ChainedMap();\n\n  map.set('a', 'alpha');\n  map.set('b', 'beta');\n  map.set('c', 'gamma');\n\n  expect(map.entries()).toStrictEqual({ a: 'alpha', b: 'beta', c: 'gamma' });\n});\n\ntest('entries with no values', () => {\n  const map = new ChainedMap();\n\n  expect(map.entries()).toBeUndefined();\n});\n\ntest('merge with no values', () => {\n  const map = new ChainedMap();\n  const obj = { a: 'alpha', b: 'beta', c: 'gamma' };\n\n  expect(map.merge(obj)).toBe(map);\n  expect(map.entries()).toStrictEqual(obj);\n});\n\ntest('merge with existing values', () => {\n  const map = new ChainedMap();\n  const obj = { a: 'alpha', b: 'beta', c: 'gamma' };\n\n  map.set('d', 'delta');\n\n  expect(map.merge(obj)).toBe(map);\n  expect(map.entries()).toStrictEqual({\n    a: 'alpha',\n    b: 'beta',\n    c: 'gamma',\n    d: 'delta',\n  });\n});\n\ntest('merge with overriding values', () => {\n  const map = new ChainedMap();\n  const obj = { a: 'alpha', b: 'beta', c: 'gamma' };\n\n  map.set('b', 'delta');\n\n  expect(map.merge(obj)).toBe(map);\n  expect(map.entries()).toStrictEqual({ a: 'alpha', b: 'beta', c: 'gamma' });\n});\n\ntest('merge with omitting keys', () => {\n  const map = new ChainedMap();\n  const obj = { a: 'alpha', b: 'beta', c: 'gamma' };\n\n  map.merge(obj, ['b']);\n\n  expect(map.entries()).toStrictEqual({ a: 'alpha', c: 'gamma' });\n});\n\ntest('when true', () => {\n  const map = new ChainedMap();\n  const right = (instance) => {\n    expect(instance).toBe(map);\n    instance.set('alpha', 'a');\n  };\n  const left = (instance) => {\n    instance.set('beta', 'b');\n  };\n\n  expect(map.when(true, right, left)).toBe(map);\n  expect(map.has('alpha')).toBe(true);\n  expect(map.has('beta')).toBe(false);\n});\n\ntest('when false', () => {\n  const map = new ChainedMap();\n  const right = (instance) => {\n    instance.set('alpha', 'a');\n  };\n  const left = (instance) => {\n    expect(instance).toBe(map);\n    instance.set('beta', 'b');\n  };\n\n  expect(map.when(false, right, left)).toBe(map);\n  expect(map.has('alpha')).toBe(false);\n  expect(map.has('beta')).toBe(true);\n});\n\ntest('clean undefined', () => {\n  const map = new ChainedMap();\n  map.set('alpha', undefined);\n  map.set('beta', 'b');\n  expect('alpha' in map.entries()).toBe(true);\n  expect('alpha' in map.clean(map.entries())).toBe(false);\n  expect('beta' in map.clean(map.entries())).toBe(true);\n});\n\ntest('clean empty array', () => {\n  const map = new ChainedMap();\n  map.set('alpha', []);\n  expect('alpha' in map.entries()).toBe(true);\n  expect('alpha' in map.clean(map.entries())).toBe(false);\n});\n\ntest('clean empty object', () => {\n  const map = new ChainedMap();\n  map.set('alpha', {});\n  expect('alpha' in map.entries()).toBe(true);\n  expect('alpha' in map.clean(map.entries())).toBe(false);\n});\n"
  },
  {
    "path": "test/ChainedSet.js",
    "content": "const ChainedSet = require('../src/ChainedSet');\n\ntest('is Chainable', () => {\n  const parent = { parent: true };\n  const set = new ChainedSet(parent);\n\n  expect(set.end()).toBe(parent);\n});\n\ntest('creates a backing Set', () => {\n  const set = new ChainedSet();\n\n  expect(set.store instanceof Set).toBe(true);\n});\n\ntest('add', () => {\n  const set = new ChainedSet();\n\n  expect(set.add('alpha')).toBe(set);\n  expect(set.store.has('alpha')).toBe(true);\n  expect(set.store.size).toBe(1);\n});\n\ntest('prepend', () => {\n  const set = new ChainedSet();\n\n  set.add('alpha');\n\n  expect(set.prepend('beta')).toBe(set);\n  expect(set.store.has('beta')).toBe(true);\n  expect([...set.store]).toStrictEqual(['beta', 'alpha']);\n});\n\ntest('clear', () => {\n  const set = new ChainedSet();\n\n  set.add('alpha');\n  set.add('beta');\n  set.add('gamma');\n\n  expect(set.store.size).toBe(3);\n  expect(set.clear()).toBe(set);\n  expect(set.store.size).toBe(0);\n});\n\ntest('delete', () => {\n  const set = new ChainedSet();\n\n  set.add('alpha');\n  set.add('beta');\n  set.add('gamma');\n\n  expect(set.delete('beta')).toBe(set);\n  expect(set.store.size).toBe(2);\n  expect(set.store.has('beta')).toBe(false);\n});\n\ntest('has', () => {\n  const set = new ChainedSet();\n\n  set.add('alpha');\n  set.add('beta');\n  set.add('gamma');\n\n  expect(set.has('beta')).toBe(true);\n  expect(set.has('delta')).toBe(false);\n  expect(set.has('beta')).toBe(set.store.has('beta'));\n});\n\ntest('values', () => {\n  const set = new ChainedSet();\n\n  set.add('alpha');\n  set.add('beta');\n  set.add('gamma');\n\n  expect(set.values()).toStrictEqual(['alpha', 'beta', 'gamma']);\n});\n\ntest('merge with no values', () => {\n  const set = new ChainedSet();\n  const arr = ['alpha', 'beta', 'gamma'];\n\n  expect(set.merge(arr)).toBe(set);\n  expect(set.values()).toStrictEqual(arr);\n});\n\ntest('merge with existing values', () => {\n  const set = new ChainedSet();\n  const arr = ['alpha', 'beta', 'gamma'];\n\n  set.add('delta');\n\n  expect(set.merge(arr)).toBe(set);\n  expect(set.values()).toStrictEqual(['delta', 'alpha', 'beta', 'gamma']);\n});\n\ntest('when true', () => {\n  const set = new ChainedSet();\n  const right = (instance) => {\n    expect(instance).toBe(set);\n    instance.add('alpha');\n  };\n  const left = (instance) => {\n    instance.add('beta');\n  };\n\n  expect(set.when(true, right, left)).toBe(set);\n  expect(set.has('alpha')).toBe(true);\n  expect(set.has('beta')).toBe(false);\n});\n\ntest('when false', () => {\n  const set = new ChainedSet();\n  const right = (instance) => {\n    instance.add('alpha');\n  };\n  const left = (instance) => {\n    expect(instance).toBe(set);\n    instance.add('beta');\n  };\n\n  expect(set.when(false, right, left)).toBe(set);\n  expect(set.has('alpha')).toBe(false);\n  expect(set.has('beta')).toBe(true);\n});\n"
  },
  {
    "path": "test/Config.js",
    "content": "/* eslint-disable max-classes-per-file */\nconst { validate } = require('webpack');\nconst { stringify } = require('javascript-stringify');\nconst EnvironmentPlugin = require('webpack/lib/EnvironmentPlugin');\nconst Config = require('../src/Config');\n\nclass StringifyPlugin {\n  constructor(...args) {\n    this.values = args;\n  }\n\n  apply() {\n    return JSON.stringify(this.values);\n  }\n}\n\ntest('is ChainedMap', () => {\n  const config = new Config();\n\n  config.set('a', 'alpha');\n\n  expect(config.store.get('a')).toBe('alpha');\n});\n\ntest('shorthand methods', () => {\n  const config = new Config();\n  const obj = {};\n\n  config.shorthands.forEach((method) => {\n    obj[method] = 'alpha';\n    expect(config[method]('alpha')).toBe(config);\n  });\n\n  expect(config.entries()).toStrictEqual(obj);\n});\n\ntest('node', () => {\n  const config = new Config();\n  const instance = config.node\n    .set('__dirname', 'mock')\n    .set('__filename', 'mock')\n    .end();\n\n  expect(instance).toBe(config);\n  expect(config.node.entries()).toStrictEqual({\n    __dirname: 'mock',\n    __filename: 'mock',\n  });\n});\n\ntest('entry', () => {\n  const config = new Config();\n\n  config.entry('index').add('babel-polyfill').add('src/index.js');\n\n  expect(config.entryPoints.has('index')).toBe(true);\n  expect(config.entryPoints.get('index').values()).toStrictEqual([\n    'babel-polyfill',\n    'src/index.js',\n  ]);\n});\n\ntest('plugin empty', () => {\n  const config = new Config();\n  const instance = config.plugin('stringify').use(StringifyPlugin).end();\n\n  expect(instance).toBe(config);\n  expect(config.plugins.has('stringify')).toBe(true);\n  expect(config.plugins.get('stringify').get('args')).toStrictEqual([]);\n});\n\ntest('plugin with args', () => {\n  const config = new Config();\n\n  config.plugin('stringify').use(StringifyPlugin, ['alpha', 'beta']);\n\n  expect(config.plugins.has('stringify')).toBe(true);\n  expect(config.plugins.get('stringify').get('args')).toStrictEqual([\n    'alpha',\n    'beta',\n  ]);\n});\n\ntest('toConfig empty', () => {\n  const config = new Config();\n\n  expect(config.toConfig()).toStrictEqual({});\n});\n\ntest('toConfig with values', () => {\n  const config = new Config();\n\n  config.output\n    .path('build')\n    .end()\n    .mode('development')\n    .node.set('__dirname', 'mock')\n    .end()\n    .optimization.nodeEnv('PRODUCTION')\n    .minimizer('stringify')\n    .use(StringifyPlugin)\n    .end()\n    .end()\n    .target('node')\n    .plugin('stringify')\n    .use(StringifyPlugin)\n    .end()\n    .plugin('env')\n    .use(require.resolve('webpack/lib/EnvironmentPlugin'))\n    .end()\n    .module.defaultRule('inline')\n    .use('banner')\n    .loader('banner-loader')\n    .options({ prefix: 'banner-prefix.txt' })\n    .end()\n    .end()\n    .rule('compile')\n    .include.add('alpha')\n    .add('beta')\n    .end()\n    .exclude.add('alpha')\n    .add('beta')\n    .end()\n    .post()\n    .pre()\n    .test(/\\.js$/)\n    .use('babel')\n    .loader('babel-loader')\n    .options({ presets: ['alpha'] });\n\n  expect(config.toConfig()).toStrictEqual({\n    mode: 'development',\n    node: {\n      __dirname: 'mock',\n    },\n    optimization: {\n      nodeEnv: 'PRODUCTION',\n      minimizer: [new StringifyPlugin()],\n    },\n    output: {\n      path: 'build',\n    },\n    target: 'node',\n    plugins: [new StringifyPlugin(), new EnvironmentPlugin()],\n    module: {\n      defaultRules: [\n        {\n          use: [\n            {\n              loader: 'banner-loader',\n              options: { prefix: 'banner-prefix.txt' },\n            },\n          ],\n        },\n      ],\n      rules: [\n        {\n          include: ['alpha', 'beta'],\n          exclude: ['alpha', 'beta'],\n          enforce: 'pre',\n          test: /\\.js$/,\n          use: [\n            {\n              loader: 'babel-loader',\n              options: { presets: ['alpha'] },\n            },\n          ],\n        },\n      ],\n    },\n  });\n});\n\ntest('merge empty', () => {\n  const config = new Config();\n\n  const obj = {\n    mode: 'development',\n    node: {\n      __dirname: 'mock',\n    },\n    optimization: {\n      nodeEnv: 'PRODUCTION',\n    },\n    output: {\n      path: 'build',\n    },\n    target: 'node',\n    entry: {\n      index: ['babel-polyfill', 'src/index.js'],\n    },\n    plugin: { stringify: { plugin: new StringifyPlugin(), args: [] } },\n  };\n\n  const instance = config.merge(obj);\n\n  expect(instance).toBe(config);\n\n  expect(config.toConfig()).toStrictEqual({\n    mode: 'development',\n    node: {\n      __dirname: 'mock',\n    },\n    optimization: {\n      nodeEnv: 'PRODUCTION',\n    },\n    output: {\n      path: 'build',\n    },\n    target: 'node',\n    entry: {\n      index: ['babel-polyfill', 'src/index.js'],\n    },\n    plugins: [new StringifyPlugin()],\n  });\n});\n\ntest('merge with values', () => {\n  const config = new Config();\n\n  config.output\n    .path('build')\n    .end()\n    .mode('development')\n    .node.set('__dirname', 'mock')\n    .end()\n    .optimization.nodeEnv('PRODUCTION')\n    .end()\n    .entry('index')\n    .add('babel-polyfill')\n    .end()\n    .target('node')\n    .plugin('stringify')\n    .use(StringifyPlugin)\n    .end();\n\n  const obj = {\n    mode: 'production',\n    output: {\n      path: 'build',\n    },\n    target: 'browser',\n    entry: {\n      index: 'src/index.js',\n    },\n    plugin: { env: { plugin: new EnvironmentPlugin() } },\n  };\n\n  const instance = config.merge(obj);\n\n  expect(instance).toBe(config);\n\n  expect(config.toConfig()).toStrictEqual({\n    mode: 'production',\n    node: {\n      __dirname: 'mock',\n    },\n    optimization: {\n      nodeEnv: 'PRODUCTION',\n    },\n    output: {\n      path: 'build',\n    },\n    target: 'browser',\n    entry: {\n      index: ['babel-polyfill', 'src/index.js'],\n    },\n    plugins: [new StringifyPlugin(), new EnvironmentPlugin()],\n  });\n});\n\ntest('merge with omit', () => {\n  const config = new Config();\n\n  config.output\n    .path('build')\n    .end()\n    .mode('development')\n    .node.set('__dirname', 'mock')\n    .end()\n    .optimization.nodeEnv('PRODUCTION')\n    .end()\n    .entry('index')\n    .add('babel-polyfill')\n    .end()\n    .target('node')\n    .plugin('stringify')\n    .use(StringifyPlugin)\n    .end();\n\n  const obj = {\n    mode: 'production',\n    output: {\n      path: 'build',\n    },\n    target: 'browser',\n    entry: {\n      index: 'src/index.js',\n    },\n    plugin: { env: { plugin: new EnvironmentPlugin() } },\n  };\n\n  const instance = config.merge(obj, ['target']);\n\n  expect(instance).toBe(config);\n\n  expect(config.toConfig()).toStrictEqual({\n    mode: 'production',\n    node: {\n      __dirname: 'mock',\n    },\n    optimization: {\n      nodeEnv: 'PRODUCTION',\n    },\n    output: {\n      path: 'build',\n    },\n    target: 'node',\n    entry: {\n      index: ['babel-polyfill', 'src/index.js'],\n    },\n    plugins: [new StringifyPlugin(), new EnvironmentPlugin()],\n  });\n});\n\ntest('validate empty', () => {\n  const config = new Config();\n\n  const errors = validate(config.toConfig());\n\n  expect(errors).toHaveLength(0);\n});\n\ntest('validate with entry', () => {\n  const config = new Config();\n\n  config.entry('index').add('src/index.js');\n\n  const errors = validate(config.toConfig());\n\n  expect(errors).toHaveLength(0);\n});\n\ntest('validate with values', () => {\n  const config = new Config();\n\n  config\n    .entry('index')\n    .add('babel-polyfill')\n    .add('src/index.js')\n    .end()\n    .output.path('/build')\n    .end()\n    .mode('development')\n    .optimization.nodeEnv('PRODUCTION')\n    .end()\n    .node.set('__dirname', 'mock')\n    .end()\n    .target('node')\n    .plugin('stringify')\n    .use(StringifyPlugin)\n    .end()\n    .plugin('env')\n    .use(require.resolve('webpack/lib/EnvironmentPlugin'), [{ VAR: false }])\n    .end()\n    .module.rule('compile')\n    .include.add('/alpha')\n    .add('/beta')\n    .end()\n    .exclude.add('/alpha')\n    .add('/beta')\n    .end()\n    .sideEffects(false)\n    .post()\n    .pre()\n    .test(/\\.js$/)\n    .use('babel')\n    .loader('babel-loader')\n    .options({ presets: ['alpha'] });\n\n  const errors = validate(config.toConfig());\n\n  expect(errors).toHaveLength(0);\n});\n\ntest('toString', () => {\n  const config = new Config();\n\n  config.module.rule('alpha').oneOf('beta').use('babel').loader('babel-loader');\n\n  // Nested rules\n  config.module\n    .rule('alpha')\n    .rule('nested')\n    .use('babel')\n    .loader('babel-loader');\n\n  // Default rules\n  config.module\n    .defaultRule('default')\n    .rule('nested')\n    .use('babel')\n    .loader('babel-loader');\n\n  const envPluginPath = require.resolve('webpack/lib/EnvironmentPlugin');\n  const stringifiedEnvPluginPath = stringify(envPluginPath);\n\n  class FooPlugin {}\n  FooPlugin.__expression = `require('foo-plugin')`;\n\n  config\n    .plugin('env')\n    .use(envPluginPath, [{ VAR: false }])\n    .end()\n    .plugin('gamma')\n    .use(FooPlugin)\n    .end()\n    .plugin('delta')\n    .use(class BarPlugin {}, ['bar'])\n    .end()\n    .plugin('epsilon')\n    .use(class BazPlugin {}, [{ n: 1 }, [2, 3]]);\n\n  config.resolve.plugin('resolver').use(FooPlugin);\n  config.optimization.minimizer('minifier').use(FooPlugin);\n\n  expect(config.toString().trim()).toBe(\n    `\n{\n  resolve: {\n    plugins: [\n      /* config.resolve.plugin('resolver') */\n      new (require('foo-plugin'))()\n    ]\n  },\n  module: {\n    defaultRules: [\n      /* config.module.defaultRule('default') */\n      {\n        rules: [\n          /* config.module.defaultRule('default').rule('nested') */\n          {\n            use: [\n              /* config.module.defaultRule('default').rule('nested').use('babel') */\n              {\n                loader: 'babel-loader'\n              }\n            ]\n          }\n        ]\n      }\n    ],\n    rules: [\n      /* config.module.rule('alpha') */\n      {\n        rules: [\n          /* config.module.rule('alpha').rule('nested') */\n          {\n            use: [\n              /* config.module.rule('alpha').rule('nested').use('babel') */\n              {\n                loader: 'babel-loader'\n              }\n            ]\n          }\n        ],\n        oneOf: [\n          /* config.module.rule('alpha').oneOf('beta') */\n          {\n            use: [\n              /* config.module.rule('alpha').oneOf('beta').use('babel') */\n              {\n                loader: 'babel-loader'\n              }\n            ]\n          }\n        ]\n      }\n    ]\n  },\n  optimization: {\n    minimizer: [\n      /* config.optimization.minimizer('minifier') */\n      new (require('foo-plugin'))()\n    ]\n  },\n  plugins: [\n    /* config.plugin('env') */\n    new (require(${stringifiedEnvPluginPath}))(\n      {\n        VAR: false\n      }\n    ),\n    /* config.plugin('gamma') */\n    new (require('foo-plugin'))(),\n    /* config.plugin('delta') */\n    new BarPlugin(\n      'bar'\n    ),\n    /* config.plugin('epsilon') */\n    new BazPlugin(\n      {\n        n: 1\n      },\n      [\n        2,\n        3\n      ]\n    )\n  ]\n}\n  `.trim(),\n  );\n});\n\ntest('toString for functions with custom expression', () => {\n  const fn = function foo() {};\n  fn.__expression = `require('foo')`;\n\n  const config = new Config();\n\n  config.module.rule('alpha').include.add(fn);\n\n  expect(config.toString().trim()).toBe(\n    `\n{\n  module: {\n    rules: [\n      /* config.module.rule('alpha') */\n      {\n        include: [\n          require('foo')\n        ]\n      }\n    ]\n  }\n}\n  `.trim(),\n  );\n});\n\ntest('toString with custom prefix', () => {\n  const config = new Config();\n\n  config.plugin('foo').use(class TestPlugin {});\n\n  expect(config.toString({ configPrefix: 'neutrino.config' }).trim()).toBe(\n    `\n{\n  plugins: [\n    /* neutrino.config.plugin('foo') */\n    new TestPlugin()\n  ]\n}\n  `.trim(),\n  );\n});\n\ntest('static Config.toString', () => {\n  const config = new Config();\n  const sass = {\n    __expression: `require('sass')`,\n    render() {},\n  };\n\n  config.plugin('foo').use(class TestPlugin {});\n\n  expect(\n    Config.toString(\n      Object.assign(config.toConfig(), {\n        module: {\n          defaultRules: [\n            {\n              use: [\n                {\n                  loader: 'banner-loader',\n                  options: {\n                    prefix: 'banner-prefix.txt',\n                    implementation: sass,\n                  },\n                },\n              ],\n            },\n          ],\n        },\n      }),\n    ).trim(),\n  ).toBe(\n    `\n{\n  plugins: [\n    /* config.plugin('foo') */\n    new TestPlugin()\n  ],\n  module: {\n    defaultRules: [\n      {\n        use: [\n          {\n            loader: 'banner-loader',\n            options: {\n              prefix: 'banner-prefix.txt',\n              implementation: require('sass')\n            }\n          }\n        ]\n      }\n    ]\n  }\n}\n  `.trim(),\n  );\n});\n"
  },
  {
    "path": "test/DevServer.js",
    "content": "const DevServer = require('../src/DevServer');\n\ntest('is Chainable', () => {\n  const parent = { parent: true };\n  const devServer = new DevServer(parent);\n\n  expect(devServer.end()).toBe(parent);\n});\n\ntest('sets allowed hosts', () => {\n  const devServer = new DevServer();\n  const instance = devServer.allowedHosts.add('https://github.com').end();\n\n  expect(instance).toBe(devServer);\n  expect(devServer.toConfig()).toStrictEqual({\n    allowedHosts: ['https://github.com'],\n  });\n});\n\ntest('shorthand methods', () => {\n  const devServer = new DevServer();\n  const obj = {};\n\n  devServer.shorthands.forEach((method) => {\n    obj[method] = 'alpha';\n    expect(devServer[method]('alpha')).toBe(devServer);\n  });\n\n  expect(devServer.entries()).toStrictEqual(obj);\n});\n"
  },
  {
    "path": "test/Module.js",
    "content": "const Module = require('../src/Module');\n\ntest('is Chainable', () => {\n  const parent = { parent: true };\n  const module = new Module(parent);\n\n  expect(module.end()).toBe(parent);\n});\n\ntest('is ChainedMap', () => {\n  const module = new Module();\n\n  module.set('a', 'alpha');\n\n  expect(module.get('a')).toBe('alpha');\n});\n\ntest('rule', () => {\n  const module = new Module();\n  const instance = module.rule('compile').end();\n\n  expect(instance).toBe(module);\n  expect(module.rules.has('compile')).toBe(true);\n});\n\ntest('defaultRule', () => {\n  const module = new Module();\n  const instance = module.defaultRule('banner').end();\n\n  expect(instance).toBe(module);\n  expect(module.defaultRules.has('banner')).toBe(true);\n});\n\ntest('toConfig empty', () => {\n  const module = new Module();\n\n  expect(module.toConfig()).toStrictEqual({});\n});\n\ntest('toConfig with values', () => {\n  const module = new Module();\n\n  module.rule('compile').test(/\\.js$/);\n  module.noParse(/.min.js/);\n\n  expect(module.toConfig()).toStrictEqual({\n    rules: [{ test: /\\.js$/ }],\n    noParse: /.min.js/,\n  });\n});\n"
  },
  {
    "path": "test/Optimization.js",
    "content": "const Optimization = require('../src/Optimization');\n\nclass StringifyPlugin {\n  constructor(...args) {\n    this.values = args;\n  }\n\n  apply() {\n    return JSON.stringify(this.values);\n  }\n}\n\ntest('is Chainable', () => {\n  const parent = { parent: true };\n  const optimization = new Optimization(parent);\n\n  expect(optimization.end()).toBe(parent);\n});\n\ntest('shorthand methods', () => {\n  const optimization = new Optimization();\n  const obj = {};\n\n  optimization.shorthands.forEach((method) => {\n    obj[method] = 'alpha';\n    expect(optimization[method]('alpha')).toBe(optimization);\n  });\n\n  expect(optimization.entries()).toStrictEqual(obj);\n});\n\ntest('minimizer plugin with name', () => {\n  const optimization = new Optimization();\n  optimization.minimizer('alpha');\n\n  expect(optimization.minimizers.get('alpha').name).toBe('alpha');\n  expect(optimization.minimizers.get('alpha').type).toBe(\n    'optimization.minimizer',\n  );\n});\n\ntest('minimizer plugin empty', () => {\n  const optimization = new Optimization();\n  const instance = optimization\n    .minimizer('stringify')\n    .use(StringifyPlugin)\n    .end();\n\n  expect(instance).toBe(optimization);\n  expect(optimization.minimizers.has('stringify')).toBe(true);\n  expect(optimization.minimizers.get('stringify').get('args')).toStrictEqual(\n    [],\n  );\n});\n\ntest('minimizer plugin with args', () => {\n  const optimization = new Optimization();\n\n  optimization.minimizer('stringify').use(StringifyPlugin, ['alpha', 'beta']);\n\n  expect(optimization.minimizers.has('stringify')).toBe(true);\n  expect(optimization.minimizers.get('stringify').get('args')).toStrictEqual([\n    'alpha',\n    'beta',\n  ]);\n});\n\ntest('minimizer plugin legacy syntax', () => {\n  const optimization = new Optimization();\n  expect(() => optimization.minimizer([new StringifyPlugin()])).toThrow(\n    /optimization.minimizer\\(\\) no longer supports being passed an array/,\n  );\n});\n\ntest('optimization merge', () => {\n  const optimization = new Optimization();\n  const obj = {\n    minimizer: {\n      stringify: {\n        plugin: StringifyPlugin,\n        args: ['alpha', 'beta'],\n      },\n    },\n  };\n\n  expect(optimization.merge(obj)).toBe(optimization);\n  expect(optimization.minimizers.has('stringify')).toBe(true);\n  expect(optimization.minimizers.get('stringify').get('args')).toStrictEqual([\n    'alpha',\n    'beta',\n  ]);\n});\n\ntest('toConfig empty', () => {\n  const optimization = new Optimization();\n\n  expect(optimization.toConfig()).toStrictEqual({});\n});\n\ntest('toConfig with values', () => {\n  const optimization = new Optimization();\n\n  optimization.minimizer('foo').use(StringifyPlugin).end().splitChunks({\n    chunks: 'all',\n  });\n\n  expect(optimization.toConfig()).toStrictEqual({\n    minimizer: [new StringifyPlugin()],\n    splitChunks: {\n      chunks: 'all',\n    },\n  });\n});\n"
  },
  {
    "path": "test/Orderable.js",
    "content": "const Orderable = require('../src/Orderable');\nconst ChainedMap = require('../src/ChainedMap');\n\nconst Ordered = Orderable(class Test extends ChainedMap {});\n\ntest('before', () => {\n  const ordered = new Ordered();\n  const instance = ordered.set('gamma').before('beta');\n\n  expect(instance).toBe(ordered);\n  expect(ordered.__before).toBe('beta');\n});\n\ntest('after', () => {\n  const ordered = new Ordered();\n  const instance = ordered.set('gamma').after('alpha');\n\n  expect(instance).toBe(ordered);\n  expect(ordered.__after).toBe('alpha');\n});\n\ntest('before throws with after', () => {\n  const ordered = new Ordered();\n\n  expect(() => ordered.after('alpha').before('beta')).toThrow();\n});\n\ntest('after throws with before', () => {\n  const ordered = new Ordered();\n\n  expect(() => ordered.before('beta').after('alpha')).toThrow();\n});\n\ntest('ordering before', () => {\n  const map = new ChainedMap();\n\n  map.set('beta', new Ordered().set('beta', 'beta'));\n  map.set('alpha', new Ordered().set('alpha', 'alpha').before('beta'));\n\n  expect(map.values().map((o) => o.values())).toStrictEqual([\n    ['alpha'],\n    ['beta'],\n  ]);\n});\n\ntest('ordering after', () => {\n  const map = new ChainedMap();\n\n  map.set('beta', new Ordered().set('beta', 'beta').after('alpha'));\n  map.set('alpha', new Ordered().set('alpha', 'alpha'));\n\n  expect(map.values().map((o) => o.values())).toStrictEqual([\n    ['alpha'],\n    ['beta'],\n  ]);\n});\n\ntest('ordering before and after', () => {\n  const map = new ChainedMap();\n\n  map.set('beta', new Ordered().set('beta', 'beta'));\n  map.set('gamma', new Ordered().set('gamma', 'gamma').after('beta'));\n  map.set('alpha', new Ordered().set('alpha', 'alpha').before('beta'));\n\n  expect(map.values().map((o) => o.values())).toStrictEqual([\n    ['alpha'],\n    ['beta'],\n    ['gamma'],\n  ]);\n});\n\ntest('merge with before', () => {\n  const ordered = new Ordered();\n  const instance = ordered.set('gamma').merge({\n    before: 'beta',\n  });\n\n  expect(instance).toBe(ordered);\n  expect(ordered.__before).toBe('beta');\n});\n\ntest('merge with after', () => {\n  const ordered = new Ordered();\n  const instance = ordered.set('gamma').merge({\n    after: 'alpha',\n  });\n\n  expect(instance).toBe(ordered);\n  expect(ordered.__after).toBe('alpha');\n});\n\ntest('merging throws using before with after', () => {\n  expect(() =>\n    new Ordered().merge({ before: 'beta', after: 'alpha' }),\n  ).toThrow();\n});\n"
  },
  {
    "path": "test/Output.js",
    "content": "const Output = require('../src/Output');\n\ntest('is Chainable', () => {\n  const parent = { parent: true };\n  const output = new Output(parent);\n\n  expect(output.end()).toBe(parent);\n});\n\ntest('shorthand methods', () => {\n  const output = new Output();\n  const obj = {};\n\n  output.shorthands.forEach((method) => {\n    obj[method] = 'alpha';\n    expect(output[method]('alpha')).toBe(output);\n  });\n\n  expect(output.entries()).toStrictEqual(obj);\n});\n"
  },
  {
    "path": "test/Performance.js",
    "content": "const Performance = require('../src/Performance');\n\ntest('is Chainable', () => {\n  const parent = { parent: true };\n  const performance = new Performance(parent);\n\n  expect(performance.end()).toBe(parent);\n});\n\ntest('shorthand methods', () => {\n  const performance = new Performance();\n  const obj = {};\n\n  performance.shorthands.forEach((method) => {\n    obj[method] = 'alpha';\n    expect(performance[method]('alpha')).toBe(performance);\n  });\n\n  expect(performance.entries()).toStrictEqual(obj);\n});\n"
  },
  {
    "path": "test/Plugin.js",
    "content": "/* eslint-disable max-classes-per-file */\nconst EnvironmentPlugin = require('webpack/lib/EnvironmentPlugin');\nconst Plugin = require('../src/Plugin');\n\nclass StringifyPlugin {\n  constructor(...args) {\n    this.values = args;\n  }\n\n  apply() {\n    return JSON.stringify(this.values);\n  }\n}\n\ntest('is Chainable', () => {\n  const parent = { parent: true };\n  const plugin = new Plugin(parent);\n\n  expect(plugin.end()).toBe(parent);\n});\n\ntest('use', () => {\n  const plugin = new Plugin();\n  const instance = plugin.use(StringifyPlugin, ['alpha', 'beta']);\n\n  expect(instance).toBe(plugin);\n  expect(plugin.get('plugin')).toBe(StringifyPlugin);\n  expect(plugin.get('args')).toStrictEqual(['alpha', 'beta']);\n});\n\ntest('tap', () => {\n  const plugin = new Plugin();\n\n  plugin.use(StringifyPlugin, ['alpha', 'beta']);\n\n  const instance = plugin.tap(() => ['gamma', 'delta']);\n\n  expect(instance).toBe(plugin);\n  expect(plugin.get('args')).toStrictEqual(['gamma', 'delta']);\n});\n\ntest('init', () => {\n  const plugin = new Plugin();\n\n  plugin.use(StringifyPlugin);\n\n  const instance = plugin.init((Plugin, args) => {\n    expect(args).toStrictEqual([]);\n    return new Plugin('gamma', 'delta');\n  });\n  const initialized = plugin.get('init')(\n    plugin.get('plugin'),\n    plugin.get('args'),\n  );\n\n  expect(instance).toBe(plugin);\n  expect(initialized instanceof StringifyPlugin).toBe(true);\n  expect(initialized.values).toStrictEqual(['gamma', 'delta']);\n});\n\ntest('args is validated as being an array', () => {\n  const plugin = new Plugin();\n\n  expect(() => plugin.use(StringifyPlugin, { foo: true })).toThrow(\n    'args must be an array of arguments',\n  );\n\n  plugin.use(StringifyPlugin);\n\n  expect(() => plugin.tap(() => ({ foo: true }))).toThrow(\n    'args must be an array of arguments',\n  );\n  expect(() => plugin.merge({ args: 5000 })).toThrow(\n    'args must be an array of arguments',\n  );\n  expect(() => plugin.set('args', null)).toThrow(\n    'args must be an array of arguments',\n  );\n});\n\ntest('toConfig', () => {\n  const plugin = new Plugin(null, 'gamma');\n\n  plugin.use(StringifyPlugin, ['delta']);\n\n  const initialized = plugin.toConfig();\n\n  expect(initialized instanceof StringifyPlugin).toBe(true);\n  expect(initialized.values).toStrictEqual(['delta']);\n  expect(initialized.__pluginName).toBe('gamma');\n  expect(initialized.__pluginType).toBe('plugin');\n  expect(initialized.__pluginArgs).toStrictEqual(['delta']);\n  expect(initialized.__pluginConstructorName).toBe('StringifyPlugin');\n});\n\ntest('toConfig with custom type', () => {\n  const plugin = new Plugin(null, 'gamma', 'optimization.minimizer');\n  plugin.use(StringifyPlugin);\n\n  expect(plugin.toConfig().__pluginType).toBe('optimization.minimizer');\n});\n\ntest('toConfig with custom expression', () => {\n  const plugin = new Plugin(null, 'gamma');\n\n  class TestPlugin {}\n  TestPlugin.__expression = `require('my-plugin')`;\n\n  plugin.use(TestPlugin);\n\n  const initialized = plugin.toConfig();\n\n  expect(initialized.__pluginConstructorName).toBe(`(require('my-plugin'))`);\n});\n\ntest('toConfig with object literal plugin', () => {\n  const plugin = new Plugin(null, 'gamma');\n\n  const TestPlugin = {\n    apply() {},\n  };\n\n  plugin.use(TestPlugin);\n\n  const initialized = plugin.toConfig();\n\n  expect(initialized).toBe(TestPlugin);\n});\n\ntest('toConfig with plugin as path', () => {\n  const plugin = new Plugin(null, 'gamma');\n  const envPluginPath = require.resolve('webpack/lib/EnvironmentPlugin');\n\n  plugin.use(envPluginPath);\n\n  const initialized = plugin.toConfig();\n\n  expect(initialized instanceof EnvironmentPlugin).toBe(true);\n  expect(initialized.__pluginConstructorName).toBe('EnvironmentPlugin');\n  expect(initialized.__pluginPath).toBe(envPluginPath);\n});\n\ntest('toConfig without having called use()', () => {\n  const plugin = new Plugin(null, 'gamma', 'optimization.minimizer');\n\n  expect(() => plugin.toConfig()).toThrow(\n    \"Invalid optimization.minimizer configuration: optimization.minimizer('gamma').use(<Plugin>) was not called to specify the plugin\",\n  );\n});\n\ntest('tap() without having called use()', () => {\n  const plugin = new Plugin(null, 'gamma', 'optimization.minimizer');\n\n  expect(() => plugin.tap(() => [])).toThrow(\n    \"Cannot call .tap() on a plugin that has not yet been defined. Call optimization.minimizer('gamma').use(<Plugin>) first.\",\n  );\n});\n"
  },
  {
    "path": "test/Resolve.js",
    "content": "const Resolve = require('../src/Resolve');\n\nclass StringifyPlugin {\n  constructor(...args) {\n    this.values = args;\n  }\n\n  apply() {\n    return JSON.stringify(this.values);\n  }\n}\n\ntest('is Chainable', () => {\n  const parent = { parent: true };\n  const resolve = new Resolve(parent);\n\n  expect(resolve.end()).toBe(parent);\n});\n\ntest('shorthand methods', () => {\n  const resolve = new Resolve();\n  const obj = {};\n\n  resolve.shorthands.forEach((method) => {\n    obj[method] = 'alpha';\n    expect(resolve[method]('alpha')).toBe(resolve);\n  });\n\n  expect(resolve.entries()).toStrictEqual(obj);\n});\n\ntest('sets methods', () => {\n  const resolve = new Resolve();\n  const instance = resolve.modules.add('src').end().extensions.add('.js').end();\n\n  expect(instance).toBe(resolve);\n});\n\ntest('toConfig empty', () => {\n  const resolve = new Resolve();\n\n  expect(resolve.toConfig()).toStrictEqual({});\n});\n\ntest('toConfig with values', () => {\n  const resolve = new Resolve();\n\n  resolve\n    .plugin('stringify')\n    .use(StringifyPlugin)\n    .end()\n    .modules.add('src')\n    .end()\n    .extensions.add('.js')\n    .end()\n    .alias.set('React', 'src/react');\n\n  expect(resolve.toConfig()).toStrictEqual({\n    plugins: [new StringifyPlugin()],\n    modules: ['src'],\n    extensions: ['.js'],\n    alias: { React: 'src/react' },\n  });\n});\n\ntest('merge empty', () => {\n  const resolve = new Resolve();\n  const obj = {\n    modules: ['src'],\n    extensions: ['.js'],\n    alias: { React: 'src/react' },\n  };\n  const instance = resolve.merge(obj);\n\n  expect(instance).toBe(resolve);\n  expect(resolve.toConfig()).toStrictEqual(obj);\n});\n\ntest('merge with values', () => {\n  const resolve = new Resolve();\n\n  resolve.modules\n    .add('src')\n    .end()\n    .extensions.add('.js')\n    .end()\n    .alias.set('React', 'src/react');\n\n  resolve.merge({\n    modules: ['dist'],\n    extensions: ['.jsx'],\n    alias: { ReactDOM: 'src/react-dom' },\n  });\n\n  expect(resolve.toConfig()).toStrictEqual({\n    modules: ['src', 'dist'],\n    extensions: ['.js', '.jsx'],\n    alias: { React: 'src/react', ReactDOM: 'src/react-dom' },\n  });\n});\n\ntest('merge with omit', () => {\n  const resolve = new Resolve();\n\n  resolve.modules\n    .add('src')\n    .end()\n    .extensions.add('.js')\n    .end()\n    .alias.set('React', 'src/react');\n\n  resolve.merge(\n    {\n      modules: ['dist'],\n      extensions: ['.jsx'],\n      alias: { ReactDOM: 'src/react-dom' },\n    },\n    ['alias'],\n  );\n\n  expect(resolve.toConfig()).toStrictEqual({\n    modules: ['src', 'dist'],\n    extensions: ['.js', '.jsx'],\n    alias: { React: 'src/react' },\n  });\n});\n\ntest('plugin with name', () => {\n  const resolve = new Resolve();\n\n  resolve.plugin('alpha');\n\n  expect(resolve.plugins.get('alpha').name).toBe('alpha');\n  expect(resolve.plugins.get('alpha').type).toBe('resolve.plugin');\n});\n\ntest('plugin empty', () => {\n  const resolve = new Resolve();\n  const instance = resolve.plugin('stringify').use(StringifyPlugin).end();\n\n  expect(instance).toBe(resolve);\n  expect(resolve.plugins.has('stringify')).toBe(true);\n  expect(resolve.plugins.get('stringify').get('args')).toStrictEqual([]);\n});\n\ntest('plugin with args', () => {\n  const resolve = new Resolve();\n\n  resolve.plugin('stringify').use(StringifyPlugin, ['alpha', 'beta']);\n\n  expect(resolve.plugins.has('stringify')).toBe(true);\n  expect(resolve.plugins.get('stringify').get('args')).toStrictEqual([\n    'alpha',\n    'beta',\n  ]);\n});\n"
  },
  {
    "path": "test/ResolveLoader.js",
    "content": "const ResolveLoader = require('../src/ResolveLoader');\n\ntest('is Chainable', () => {\n  const parent = { parent: true };\n  const resolveLoader = new ResolveLoader(parent);\n\n  expect(resolveLoader.end()).toBe(parent);\n});\n\ntest('shorthand methods', () => {\n  const resolveLoader = new ResolveLoader();\n  const obj = {};\n\n  resolveLoader.shorthands.forEach((method) => {\n    obj[method] = 'alpha';\n    expect(resolveLoader[method]('alpha')).toBe(resolveLoader);\n  });\n\n  expect(resolveLoader.entries()).toStrictEqual(obj);\n});\n\ntest('sets methods', () => {\n  const resolveLoader = new ResolveLoader();\n  const instance = resolveLoader.modules.add('src').end();\n\n  expect(instance).toBe(resolveLoader);\n  expect(resolveLoader.toConfig()).toStrictEqual({ modules: ['src'] });\n});\n\ntest('toConfig empty', () => {\n  const resolveLoader = new ResolveLoader();\n\n  expect(resolveLoader.toConfig()).toStrictEqual({});\n});\n\ntest('toConfig with values', () => {\n  const resolveLoader = new ResolveLoader();\n\n  resolveLoader.modules.add('src').end().set('moduleExtensions', ['-loader']);\n\n  expect(resolveLoader.toConfig()).toStrictEqual({\n    modules: ['src'],\n    moduleExtensions: ['-loader'],\n  });\n});\n\ntest('merge empty', () => {\n  const resolveLoader = new ResolveLoader();\n  const obj = {\n    modules: ['src'],\n    moduleExtensions: ['-loader'],\n  };\n  const instance = resolveLoader.merge(obj);\n\n  expect(instance).toBe(resolveLoader);\n  expect(resolveLoader.toConfig()).toStrictEqual(obj);\n});\n\ntest('merge with values', () => {\n  const resolveLoader = new ResolveLoader();\n\n  resolveLoader.modules.add('src').end().moduleExtensions.add('-loader');\n\n  resolveLoader.merge({\n    modules: ['dist'],\n    moduleExtensions: ['-fake'],\n  });\n\n  expect(resolveLoader.toConfig()).toStrictEqual({\n    modules: ['src', 'dist'],\n    moduleExtensions: ['-loader', '-fake'],\n  });\n});\n\ntest('merge with omit', () => {\n  const resolveLoader = new ResolveLoader();\n\n  resolveLoader.modules.add('src').end().moduleExtensions.add('-loader');\n\n  resolveLoader.merge(\n    {\n      modules: ['dist'],\n      moduleExtensions: ['-fake'],\n    },\n    ['moduleExtensions'],\n  );\n\n  expect(resolveLoader.toConfig()).toStrictEqual({\n    modules: ['src', 'dist'],\n    moduleExtensions: ['-loader'],\n  });\n});\n\ntest('plugin with name', () => {\n  const resolveLoader = new ResolveLoader();\n\n  resolveLoader.plugin('alpha');\n\n  expect(resolveLoader.plugins.get('alpha').name).toBe('alpha');\n});\n"
  },
  {
    "path": "test/Rule.js",
    "content": "const Rule = require('../src/Rule');\n\ntest('is Chainable', () => {\n  const parent = { parent: true };\n  const rule = new Rule(parent);\n\n  expect(rule.end()).toBe(parent);\n});\n\ntest('shorthand methods', () => {\n  const rule = new Rule();\n  const obj = {};\n\n  rule.shorthands.forEach((method) => {\n    obj[method] = 'alpha';\n    expect(rule[method]('alpha')).toBe(rule);\n  });\n\n  expect(rule.entries()).toStrictEqual(obj);\n});\n\ntest('use', () => {\n  const rule = new Rule();\n  const instance = rule.use('babel').end();\n\n  expect(instance).toBe(rule);\n  expect(rule.uses.has('babel')).toBe(true);\n});\n\ntest('rule', () => {\n  const rule = new Rule();\n  const instance = rule.rule('babel').end();\n\n  expect(instance).toBe(rule);\n  expect(rule.rules.has('babel')).toBe(true);\n});\n\ntest('oneOf', () => {\n  const rule = new Rule();\n  const instance = rule.oneOf('babel').end();\n\n  expect(instance).toBe(rule);\n  expect(rule.oneOfs.has('babel')).toBe(true);\n});\n\ntest('resolve', () => {\n  const rule = new Rule();\n  const instance = rule.resolve.alias.set('foo', 'bar').end().end();\n\n  expect(instance).toBe(rule);\n  expect(rule.resolve.alias.has('foo')).toBe(true);\n});\n\ntest('pre', () => {\n  const rule = new Rule();\n  const instance = rule.pre();\n\n  expect(instance).toBe(rule);\n  expect(rule.get('enforce')).toBe('pre');\n});\n\ntest('post', () => {\n  const rule = new Rule();\n  const instance = rule.post();\n\n  expect(instance).toBe(rule);\n  expect(rule.get('enforce')).toBe('post');\n});\n\ntest('sets methods', () => {\n  const rule = new Rule();\n  const instance = rule.include\n    .add('alpha')\n    .add('beta')\n    .end()\n    .exclude.add('alpha')\n    .add('beta')\n    .end();\n\n  expect(instance).toBe(rule);\n  expect(rule.include.values()).toStrictEqual(['alpha', 'beta']);\n  expect(rule.exclude.values()).toStrictEqual(['alpha', 'beta']);\n});\n\ntest('toConfig empty', () => {\n  const rule = new Rule();\n\n  expect(rule.toConfig()).toStrictEqual({});\n});\n\ntest('toConfig with name', () => {\n  const parent = new Rule(null, 'alpha');\n  const child = parent.oneOf('beta');\n  const grandChild = child.oneOf('gamma');\n  const ruleChild = parent.rule('delta');\n\n  expect(parent.toConfig().__ruleNames).toStrictEqual(['alpha']);\n  expect(parent.toConfig().__ruleTypes).toStrictEqual(['rule']);\n  expect(child.toConfig().__ruleNames).toStrictEqual(['alpha', 'beta']);\n  expect(child.toConfig().__ruleTypes).toStrictEqual(['rule', 'oneOf']);\n  expect(grandChild.toConfig().__ruleNames).toStrictEqual([\n    'alpha',\n    'beta',\n    'gamma',\n  ]);\n  expect(grandChild.toConfig().__ruleTypes).toStrictEqual([\n    'rule',\n    'oneOf',\n    'oneOf',\n  ]);\n  expect(ruleChild.toConfig().__ruleNames).toStrictEqual(['alpha', 'delta']);\n  expect(ruleChild.toConfig().__ruleTypes).toStrictEqual(['rule', 'rule']);\n});\n\ntest('toConfig with values', () => {\n  const rule = new Rule();\n\n  rule.include\n    .add('alpha')\n    .add('beta')\n    .end()\n    .exclude.add('alpha')\n    .add('beta')\n    .end()\n    .post()\n    .pre()\n    .test(/\\.js$/)\n    .use('babel')\n    .loader('babel-loader')\n    .options({ presets: ['alpha'] })\n    .end()\n    .rule('minifier')\n    .resourceQuery(/minify/)\n    .use('minifier')\n    .loader('minifier-loader')\n    .end()\n    .end()\n    .oneOf('inline')\n    .resourceQuery(/inline/)\n    .use('url')\n    .loader('url-loader');\n\n  expect(rule.toConfig()).toStrictEqual({\n    test: /\\.js$/,\n    enforce: 'pre',\n    include: ['alpha', 'beta'],\n    exclude: ['alpha', 'beta'],\n    rules: [\n      {\n        resourceQuery: /minify/,\n        use: [\n          {\n            loader: 'minifier-loader',\n          },\n        ],\n      },\n    ],\n    oneOf: [\n      {\n        resourceQuery: /inline/,\n        use: [\n          {\n            loader: 'url-loader',\n          },\n        ],\n      },\n    ],\n    use: [\n      {\n        loader: 'babel-loader',\n        options: {\n          presets: ['alpha'],\n        },\n      },\n    ],\n  });\n});\n\ntest('toConfig with test function', () => {\n  const rule = new Rule();\n  const test = (s) => s.includes('.js');\n\n  rule.test(test);\n\n  expect(rule.toConfig()).toStrictEqual({ test });\n});\n\ntest('merge empty', () => {\n  const rule = new Rule();\n  const obj = {\n    enforce: 'pre',\n    test: /\\.js$/,\n    include: ['alpha', 'beta'],\n    exclude: ['alpha', 'beta'],\n    rules: {\n      minifier: {\n        resourceQuery: /minify/,\n        use: {\n          minifier: {\n            loader: 'minifier-loader',\n          },\n        },\n      },\n    },\n    oneOf: {\n      inline: {\n        resourceQuery: /inline/,\n        use: {\n          url: {\n            loader: 'url-loader',\n          },\n        },\n      },\n    },\n    use: {\n      babel: {\n        loader: 'babel-loader',\n        options: {\n          presets: ['alpha'],\n        },\n      },\n    },\n  };\n  const instance = rule.merge(obj);\n\n  expect(instance).toBe(rule);\n  expect(rule.toConfig()).toStrictEqual({\n    enforce: 'pre',\n    test: /\\.js$/,\n    include: ['alpha', 'beta'],\n    exclude: ['alpha', 'beta'],\n    rules: [\n      {\n        resourceQuery: /minify/,\n        use: [\n          {\n            loader: 'minifier-loader',\n          },\n        ],\n      },\n    ],\n    oneOf: [\n      {\n        resourceQuery: /inline/,\n        use: [\n          {\n            loader: 'url-loader',\n          },\n        ],\n      },\n    ],\n    use: [\n      {\n        loader: 'babel-loader',\n        options: {\n          presets: ['alpha'],\n        },\n      },\n    ],\n  });\n});\n\ntest('merge with values', () => {\n  const rule = new Rule();\n\n  rule\n    .test(/\\.js$/)\n    .post()\n    .include.add('gamma')\n    .add('delta')\n    .end()\n    .use('babel')\n    .loader('babel-loader')\n    .options({ presets: ['alpha'] });\n\n  rule.merge({\n    test: /\\.jsx$/,\n    enforce: 'pre',\n    include: ['alpha', 'beta'],\n    exclude: ['alpha', 'beta'],\n    rules: {\n      minifier: {\n        resourceQuery: /minify/,\n        use: {\n          minifier: {\n            loader: 'minifier-loader',\n          },\n        },\n      },\n    },\n    oneOf: {\n      inline: {\n        resourceQuery: /inline/,\n        use: {\n          url: {\n            loader: 'url-loader',\n          },\n        },\n      },\n    },\n    use: {\n      babel: {\n        options: {\n          presets: ['beta'],\n        },\n      },\n    },\n  });\n\n  expect(rule.toConfig()).toStrictEqual({\n    test: /\\.jsx$/,\n    enforce: 'pre',\n    include: ['gamma', 'delta', 'alpha', 'beta'],\n    exclude: ['alpha', 'beta'],\n    rules: [\n      {\n        resourceQuery: /minify/,\n        use: [\n          {\n            loader: 'minifier-loader',\n          },\n        ],\n      },\n    ],\n    oneOf: [\n      {\n        resourceQuery: /inline/,\n        use: [\n          {\n            loader: 'url-loader',\n          },\n        ],\n      },\n    ],\n    use: [\n      {\n        loader: 'babel-loader',\n        options: {\n          presets: ['alpha', 'beta'],\n        },\n      },\n    ],\n  });\n});\n\ntest('merge with omit', () => {\n  const rule = new Rule();\n\n  rule\n    .test(/\\.js$/)\n    .post()\n    .include.add('gamma')\n    .add('delta')\n    .end()\n    .use('babel')\n    .loader('babel-loader')\n    .options({ presets: ['alpha'] });\n\n  rule.merge(\n    {\n      test: /\\.jsx$/,\n      enforce: 'pre',\n      include: ['alpha', 'beta'],\n      exclude: ['alpha', 'beta'],\n      rules: {\n        minifier: {\n          resourceQuery: /minify/,\n          use: {\n            minifier: {\n              loader: 'minifier-loader',\n            },\n          },\n        },\n      },\n      oneOf: {\n        inline: {\n          resourceQuery: /inline/,\n          use: {\n            url: {\n              loader: 'url-loader',\n            },\n          },\n        },\n      },\n      use: {\n        babel: {\n          options: {\n            presets: ['beta'],\n          },\n        },\n      },\n    },\n    ['use', 'oneOf', 'rules'],\n  );\n\n  expect(rule.toConfig()).toStrictEqual({\n    test: /\\.jsx$/,\n    enforce: 'pre',\n    include: ['gamma', 'delta', 'alpha', 'beta'],\n    exclude: ['alpha', 'beta'],\n    use: [\n      {\n        loader: 'babel-loader',\n        options: {\n          presets: ['alpha'],\n        },\n      },\n    ],\n  });\n});\n\ntest('merge with include and exclude not of array type', () => {\n  const rule = new Rule();\n\n  rule.merge({\n    test: /\\.jsx$/,\n    include: 'alpha',\n    exclude: 'alpha',\n  });\n\n  expect(rule.toConfig()).toStrictEqual({\n    test: /\\.jsx$/,\n    include: ['alpha'],\n    exclude: ['alpha'],\n  });\n});\n\ntest('merge with resolve', () => {\n  const rule = new Rule();\n\n  rule.merge({\n    resolve: {\n      alias: { foo: 'bar' },\n    },\n  });\n\n  rule.merge({\n    resolve: {\n      extensions: ['.js', '.mjs'],\n    },\n  });\n\n  expect(rule.toConfig()).toStrictEqual({\n    resolve: {\n      alias: { foo: 'bar' },\n      extensions: ['.js', '.mjs'],\n    },\n  });\n});\n\ntest('ordered rules', () => {\n  const rule = new Rule();\n  rule\n    .rule('first')\n    .test(/\\.first$/)\n    .end()\n    .rule('second')\n    .test(/\\.second$/)\n    .end()\n    .rule('third')\n    .test(/\\.third$/)\n    .end()\n    .rule('alpha')\n    .test(/\\.alpha$/)\n    .before('first')\n    .end()\n    .rule('beta')\n    .test(/\\.beta$/)\n    .after('second');\n\n  expect(rule.toConfig().rules.map((o) => o.test)).toStrictEqual([\n    /\\.alpha$/,\n    /\\.first$/,\n    /\\.second$/,\n    /\\.beta$/,\n    /\\.third$/,\n  ]);\n});\n\ntest('ordered oneOfs', () => {\n  const rule = new Rule();\n  rule\n    .oneOf('first')\n    .test(/\\.first$/)\n    .end()\n    .oneOf('second')\n    .test(/\\.second$/)\n    .end()\n    .oneOf('third')\n    .test(/\\.third$/)\n    .end()\n    .oneOf('alpha')\n    .test(/\\.alpha$/)\n    .before('first')\n    .end()\n    .oneOf('beta')\n    .test(/\\.beta$/)\n    .after('second');\n\n  expect(rule.toConfig().oneOf.map((o) => o.test)).toStrictEqual([\n    /\\.alpha$/,\n    /\\.first$/,\n    /\\.second$/,\n    /\\.beta$/,\n    /\\.third$/,\n  ]);\n});\n"
  },
  {
    "path": "test/Use.js",
    "content": "const Rule = require('../src/Rule');\nconst Use = require('../src/Use');\n\ntest('is Chainable', () => {\n  const parent = { parent: true };\n  const use = new Use(parent);\n\n  expect(use.end()).toBe(parent);\n});\n\ntest('shorthand methods', () => {\n  const use = new Use();\n  const obj = {};\n\n  use.shorthands.forEach((method) => {\n    obj[method] = 'alpha';\n    expect(use[method]('alpha')).toBe(use);\n  });\n\n  expect(use.entries()).toStrictEqual(obj);\n});\n\ntest('tap', () => {\n  const use = new Use();\n\n  use.loader('babel-loader').options({ presets: ['alpha'] });\n\n  use.tap((options) => {\n    expect(options).toStrictEqual({ presets: ['alpha'] });\n    return { presets: ['beta'] };\n  });\n\n  expect(use.store.get('options')).toStrictEqual({ presets: ['beta'] });\n});\n\ntest('toConfig', () => {\n  const rule = new Rule(null, 'alpha');\n  const use = rule\n    .use('beta')\n    .loader('babel-loader')\n    .options({ presets: ['alpha'] });\n\n  const config = use.toConfig();\n\n  expect(config).toStrictEqual({\n    loader: 'babel-loader',\n    options: { presets: ['alpha'] },\n  });\n\n  expect(config.__ruleNames).toStrictEqual(['alpha']);\n  expect(config.__ruleTypes).toStrictEqual(['rule']);\n  expect(config.__useName).toBe('beta');\n});\n"
  },
  {
    "path": "types/index.d.ts",
    "content": "import { Tapable } from 'tapable';\nimport * as webpack from 'webpack';\nimport * as https from 'https';\n\nexport = Config;\n\ndeclare namespace __Config {\n  class Chained<Parent> {\n    end(): Parent;\n  }\n\n  class TypedChainedMap<Parent, Value> extends Chained<Parent> {\n    clear(): this;\n    delete(key: string): this;\n    has(key: string): boolean;\n    get(key: string): Value;\n    getOrCompute(key: string, compute: () => Value): Value;\n    set(key: string, value: Value): this;\n    merge(obj: { [key: string]: Value }): this;\n    entries(): { [key: string]: Value };\n    values(): Value[];\n    when(\n      condition: boolean,\n      trueBrancher: (obj: this) => void,\n      falseBrancher?: (obj: this) => void,\n    ): this;\n  }\n\n  class ChainedMap<Parent> extends TypedChainedMap<Parent, any> {}\n\n  class TypedChainedSet<Parent, Value> extends Chained<Parent> {\n    add(value: Value): this;\n    prepend(value: Value): this;\n    clear(): this;\n    delete(key: string): this;\n    has(key: string): boolean;\n    merge(arr: Value[]): this;\n    values(): Value[];\n    when(\n      condition: boolean,\n      trueBrancher: (obj: this) => void,\n      falseBrancher?: (obj: this) => void,\n    ): this;\n  }\n\n  class ChainedSet<Parent> extends TypedChainedSet<Parent, any> {}\n}\n\ndeclare class Config extends __Config.ChainedMap<void> {\n  devServer: Config.DevServer;\n  entryPoints: Config.TypedChainedMap<Config, Config.EntryPoint>;\n  module: Config.Module;\n  node: Config.ChainedMap<this>;\n  output: Config.Output;\n  optimization: Config.Optimization;\n  performance: Config.Performance;\n  plugins: Config.Plugins<this, webpack.Plugin>;\n  resolve: Config.Resolve;\n  resolveLoader: Config.ResolveLoader;\n\n  amd(value: { [moduleName: string]: boolean }): this;\n  bail(value: boolean): this;\n  cache(value: boolean | any): this;\n  devtool(value: Config.DevTool): this;\n  context(value: string): this;\n  externals(value: webpack.ExternalsElement | webpack.ExternalsElement[]): this;\n  loader(value: any): this;\n  name(value: string): this;\n  mode(value: 'none' | 'development' | 'production'): this;\n  parallelism(value: number): this;\n  profile(value: boolean): this;\n  recordsPath(value: string): this;\n  recordsInputPath(value: string): this;\n  recordsOutputPath(value: string): this;\n  stats(value: webpack.Options.Stats): this;\n  target(value: string): this;\n  watch(value: boolean): this;\n  watchOptions(value: webpack.Options.WatchOptions): this;\n\n  entry(name: string): Config.EntryPoint;\n  plugin(name: string): Config.Plugin<this, webpack.Plugin>;\n\n  toConfig(): webpack.Configuration;\n}\n\ndeclare namespace Config {\n  class Chained<Parent> extends __Config.Chained<Parent> {}\n  class TypedChainedMap<Parent, Value> extends __Config.TypedChainedMap<\n    Parent,\n    Value\n  > {}\n  class ChainedMap<Parent> extends __Config.TypedChainedMap<Parent, any> {}\n  class TypedChainedSet<Parent, Value> extends __Config.TypedChainedSet<\n    Parent,\n    Value\n  > {}\n  class ChainedSet<Parent> extends __Config.TypedChainedSet<Parent, any> {}\n\n  class Plugins<\n    Parent,\n    PluginType extends Tapable.Plugin = webpack.Plugin,\n  > extends TypedChainedMap<Parent, Plugin<Parent, PluginType>> {}\n\n  class Plugin<Parent, PluginType extends Tapable.Plugin = webpack.Plugin>\n    extends ChainedMap<Parent>\n    implements Orderable\n  {\n    init<P extends PluginType | PluginClass<PluginType>>(\n      value: (\n        plugin: P,\n        args: P extends PluginClass ? ConstructorParameters<P> : any[],\n      ) => PluginType,\n    ): this;\n    use<P extends string | PluginType | PluginClass<PluginType>>(\n      plugin: P,\n      args?: P extends PluginClass ? ConstructorParameters<P> : any[],\n    ): this;\n    tap<P extends PluginClass<PluginType>>(\n      f: (args: ConstructorParameters<P>) => ConstructorParameters<P>,\n    ): this;\n\n    // Orderable\n    before(name: string): this;\n    after(name: string): this;\n  }\n\n  class Module extends ChainedMap<Config> {\n    rules: TypedChainedMap<this, Rule>;\n    rule(name: string): Rule;\n    noParse(\n      noParse: RegExp | RegExp[] | ((contentPath: string) => boolean),\n    ): this;\n    strictExportPresence(value: boolean): this;\n  }\n\n  class Output extends ChainedMap<Config> {\n    auxiliaryComment(value: string | { [comment: string]: string }): this;\n    chunkFilename(value: string): this;\n    chunkLoadTimeout(value: number): this;\n    crossOriginLoading(value: boolean | string): this;\n    filename(value: string): this;\n    library(value: string): this;\n    libraryExport(value: string | string[]): this;\n    libraryTarget(value: string): this;\n    devtoolFallbackModuleFilenameTemplate(value: any): this;\n    devtoolLineToLine(value: any): this;\n    devtoolModuleFilenameTemplate(value: any): this;\n    devtoolNamespace(value: string): this;\n    globalObject(value: string): this;\n    hashFunction(value: string): this;\n    hashDigest(value: string): this;\n    hashDigestLength(value: number): this;\n    hashSalt(value: any): this;\n    hotUpdateChunkFilename(value: string): this;\n    hotUpdateFunction(value: any): this;\n    hotUpdateMainFilename(value: string): this;\n    jsonpFunction(value: string): this;\n    path(value: string): this;\n    pathinfo(value: boolean): this;\n    publicPath(value: string): this;\n    sourceMapFilename(value: string): this;\n    sourcePrefix(value: string): this;\n    strictModuleExceptionHandling(value: boolean): this;\n    umdNamedDefine(value: boolean): this;\n    futureEmitAssets(value: boolean): this;\n  }\n\n  class DevServer extends ChainedMap<Config> {\n    allowedHosts: TypedChainedSet<this, string>;\n\n    after(\n      value: (app: any, server: any, compiler: webpack.Compiler) => void,\n    ): this;\n    before(\n      value: (app: any, server: any, compiler: webpack.Compiler) => void,\n    ): this;\n    bonjour(value: boolean): this;\n    clientLogLevel(value: 'none' | 'error' | 'warning' | 'info'): this;\n    color(value: boolean): this;\n    compress(value: boolean): this;\n    contentBase(value: boolean | string | string[]): this;\n    disableHostCheck(value: boolean): this;\n    filename(value: string): this;\n    headers(value: { [header: string]: string }): this;\n    historyApiFallback(value: boolean | any): this;\n    host(value: string): this;\n    hot(value: boolean): this;\n    hotOnly(value: boolean): this;\n    http2(value: boolean): this;\n    https(value: boolean | https.ServerOptions): this;\n    index(value: string): this;\n    info(value: boolean): this;\n    inline(value: boolean): this;\n    lazy(value: boolean): this;\n    mimeTypes(value: Object): this;\n    noInfo(value: boolean): this;\n    open(value: boolean): this;\n    openPage(value: string | string[]): this;\n    overlay(value: boolean | { warnings?: boolean; errors?: boolean }): this;\n    pfx(value: string): this;\n    pfxPassphrase(value: string): this;\n    port(value: number): this;\n    progress(value: boolean): this;\n    proxy(value: any): this;\n    public(value: string): this;\n    publicPath(publicPath: string): this;\n    quiet(value: boolean): this;\n    setup(value: (expressApp: any) => void): this;\n    socket(value: string): this;\n    sockHost(value: string): this;\n    sockPath(value: string): this;\n    sockPort(value: number): this;\n    staticOptions(value: any): this;\n    stats(value: webpack.Options.Stats): this;\n    stdin(value: boolean): this;\n    useLocalIp(value: boolean): this;\n    watchContentBase(value: boolean): this;\n    watchOptions(value: any): this;\n    writeToDisk(value: boolean): this;\n  }\n\n  class Performance extends ChainedMap<Config> {\n    hints(value: boolean | 'error' | 'warning'): this;\n    maxEntrypointSize(value: number): this;\n    maxAssetSize(value: number): this;\n    assetFilter(value: (assetFilename: string) => boolean): this;\n  }\n\n  class EntryPoint extends TypedChainedSet<Config, string> {}\n\n  class Resolve<T = Config> extends ChainedMap<T> {\n    alias: TypedChainedMap<this, string>;\n    aliasFields: TypedChainedSet<this, string>;\n    descriptionFiles: TypedChainedSet<this, string>;\n    extensions: TypedChainedSet<this, string>;\n    mainFields: TypedChainedSet<this, string>;\n    mainFiles: TypedChainedSet<this, string>;\n    modules: TypedChainedSet<this, string>;\n    plugins: TypedChainedMap<this, Plugin<this, webpack.ResolvePlugin>>;\n\n    enforceExtension(value: boolean): this;\n    enforceModuleExtension(value: boolean): this;\n    unsafeCache(value: boolean | RegExp | RegExp[]): this;\n    symlinks(value: boolean): this;\n    cachePredicate(\n      value: (data: { path: string; request: string }) => boolean,\n    ): this;\n    cacheWithContext(value: boolean): this;\n\n    plugin(name: string): Plugin<this, webpack.ResolvePlugin>;\n  }\n\n  class ResolveLoader extends Resolve {\n    moduleExtensions: ChainedSet<this>;\n    packageMains: ChainedSet<this>;\n  }\n\n  class Rule<T = Module> extends ChainedMap<T> implements Orderable {\n    rules: TypedChainedMap<this, Rule<Rule>>;\n    oneOfs: TypedChainedMap<this, Rule<Rule>>;\n    uses: TypedChainedMap<this, Use>;\n    include: TypedChainedSet<this, webpack.Condition>;\n    exclude: TypedChainedSet<this, webpack.Condition>;\n    resolve: Resolve<Rule<T>>;\n\n    parser(value: { [optName: string]: any }): this;\n    test(value: webpack.Condition | webpack.Condition[]): this;\n    type(\n      value:\n        | 'javascript/auto'\n        | 'javascript/dynamic'\n        | 'javascript/esm'\n        | 'json'\n        | 'webassembly/experimental',\n    ): this;\n    enforce(value: 'pre' | 'post'): this;\n\n    use(name: string): Use<this>;\n    rule(name: string): Rule<Rule>;\n    oneOf(name: string): Rule<Rule>;\n    pre(): this;\n    post(): this;\n    before(name: string): this;\n    after(name: string): this;\n    resourceQuery(value: webpack.Condition | webpack.Condition[]): this;\n  }\n\n  class Optimization extends ChainedMap<Config> {\n    concatenateModules(value: boolean): this;\n    flagIncludedChunks(value: boolean): this;\n    mergeDuplicateChunks(value: boolean): this;\n    minimize(value: boolean): this;\n    minimizer(name: string): Config.Plugin<this, webpack.Plugin>;\n    namedChunks(value: boolean): this;\n    namedModules(value: boolean): this;\n    nodeEnv(value: boolean | string): this;\n    noEmitOnErrors(value: boolean): this;\n    occurrenceOrder(value: boolean): this;\n    portableRecords(value: boolean): this;\n    providedExports(value: boolean): this;\n    removeAvailableModules(value: boolean): this;\n    removeEmptyChunks(value: boolean): this;\n    runtimeChunk(value: boolean | 'single' | 'multiple' | RuntimeChunk): this;\n    sideEffects(value: boolean): this;\n    splitChunks(value: SplitChunksOptions): this;\n    usedExports(value: boolean): this;\n  }\n\n  interface RuntimeChunk {\n    name: string | RuntimeChunkFunction;\n  }\n\n  type RuntimeChunkFunction = (entryPoint: EntryPoint) => string;\n\n  interface SplitChunksOptions {\n    [name: string]: any;\n  }\n\n  interface LoaderOptions {\n    [name: string]: any;\n  }\n\n  class Use<Parent = Rule> extends ChainedMap<Parent> implements Orderable {\n    loader(value: string): this;\n    options(value: LoaderOptions): this;\n\n    tap(f: (options: LoaderOptions) => LoaderOptions): this;\n\n    // Orderable\n    before(name: string): this;\n    after(name: string): this;\n  }\n\n  type DevTool =\n    | 'eval'\n    | 'inline-source-map'\n    | 'cheap-eval-source-map'\n    | 'cheap-source-map'\n    | 'cheap-module-eval-source-map'\n    | 'cheap-module-source-map'\n    | 'eval-source-map'\n    | 'source-map'\n    | 'nosources-source-map'\n    | 'hidden-source-map'\n    | 'nosources-source-map'\n    | '@eval'\n    | '@inline-source-map'\n    | '@cheap-eval-source-map'\n    | '@cheap-source-map'\n    | '@cheap-module-eval-source-map'\n    | '@cheap-module-source-map'\n    | '@eval-source-map'\n    | '@source-map'\n    | '@nosources-source-map'\n    | '@hidden-source-map'\n    | '@nosources-source-map'\n    | '#eval'\n    | '#inline-source-map'\n    | '#cheap-eval-source-map'\n    | '#cheap-source-map'\n    | '#cheap-module-eval-source-map'\n    | '#cheap-module-source-map'\n    | '#eval-source-map'\n    | '#source-map'\n    | '#nosources-source-map'\n    | '#hidden-source-map'\n    | '#nosources-source-map'\n    | '#@eval'\n    | '#@inline-source-map'\n    | '#@cheap-eval-source-map'\n    | '#@cheap-source-map'\n    | '#@cheap-module-eval-source-map'\n    | '#@cheap-module-source-map'\n    | '#@eval-source-map'\n    | '#@source-map'\n    | '#@nosources-source-map'\n    | '#@hidden-source-map'\n    | '#@nosources-source-map'\n    | boolean;\n\n  interface PluginClass<PluginType extends Tapable.Plugin = webpack.Plugin> {\n    new (...opts: any[]): PluginType;\n  }\n\n  interface Orderable {\n    before(name: string): this;\n    after(name: string): this;\n  }\n}\n"
  },
  {
    "path": "types/test/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"commonjs\",\n    \"lib\": [\"es6\"],\n    \"noImplicitAny\": true,\n    \"noImplicitThis\": true,\n    \"strictNullChecks\": true,\n    \"strictFunctionTypes\": true,\n    \"noEmit\": true,\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"webpack-chain\": [\"../index.d.ts\"]\n    }\n  },\n  \"files\": [\"../index.d.ts\", \"webpack-chain-tests.ts\"],\n  \"compileOnSave\": false\n}\n"
  },
  {
    "path": "types/test/webpack-chain-tests.ts",
    "content": "/**\n * Notes: The order structure of the type check follows the order\n * of this document: https://github.com/neutrinojs/webpack-chain#config\n */\nimport Resolver = require('enhanced-resolve/lib/Resolver');\nimport Config = require('webpack-chain');\nimport * as webpack from 'webpack';\n\nclass ResolvePluginImpl extends webpack.ResolvePlugin {\n  apply(resolver: Resolver): void {}\n}\n\nfunction expectType<T>(value: T) {}\n\nconst config = new Config();\n\nconfig\n  .amd({ foo: true })\n  .bail(true)\n  .cache(false)\n  .cache({})\n  .devtool('hidden-source-map')\n  .devtool(false)\n  .context('')\n  .externals('foo')\n  .externals(/node_modules/)\n  .externals({ test: false, foo: 'bar' })\n  .externals(['foo', 'bar'])\n  .externals((context, request, cb) => cb(null, 'foo'))\n  .loader({})\n  .name('config-name')\n  .mode('none')\n  .mode('development')\n  .mode('production')\n  .profile(false)\n  .parallelism(2)\n  .recordsPath('')\n  .recordsInputPath('')\n  .recordsOutputPath('')\n  .stats({\n    assets: false,\n    publicPath: true,\n    modules: false,\n  })\n  .target('web')\n  .watch(true)\n  .watchOptions({})\n  .when(\n    false,\n    (config) => config.watch(true),\n    (config) => config.watch(false),\n  )\n\n  .entry('main')\n  .add('index.js')\n  .delete('index.js')\n  .clear()\n  .when(\n    false,\n    (entry) => entry.clear(),\n    (entry) => entry.clear(),\n  )\n  .end()\n\n  .entryPoints.delete('main')\n  .end()\n\n  .output.futureEmitAssets(true)\n  .auxiliaryComment('Test Comment')\n  .auxiliaryComment({\n    root: 'Root Comment',\n  })\n  .chunkFilename('')\n  .chunkLoadTimeout(1000)\n  .crossOriginLoading(true)\n  .devtoolFallbackModuleFilenameTemplate('')\n  .devtoolNamespace('')\n  .devtoolLineToLine('')\n  .devtoolModuleFilenameTemplate('')\n  .filename('main.js')\n  .globalObject('global')\n  .hashFunction('md5')\n  .hashDigest('md5')\n  .hashDigestLength(15)\n  .hashSalt('')\n  .hotUpdateChunkFilename('update')\n  .hotUpdateFunction(() => {})\n  .hotUpdateMainFilename('main')\n  .jsonpFunction('callback')\n  .library('var')\n  .libraryExport(['MyModule', 'MySubModule'])\n  .libraryTarget('var')\n  .path('/')\n  .pathinfo(true)\n  .publicPath('/')\n  .sourceMapFilename('index.js.map')\n  .sourcePrefix('~')\n  .strictModuleExceptionHandling(true)\n  .umdNamedDefine(true)\n  .end()\n\n  .resolve.cachePredicate(({ path, request }) => true)\n  .cacheWithContext(true)\n  .enforceExtension(true)\n  .enforceModuleExtension(true)\n  .unsafeCache(false)\n  .unsafeCache(/foo/)\n  .symlinks(true)\n  .alias.set('foo', 'bar')\n  .end()\n  .modules.add('index.js')\n  .end()\n  .aliasFields.add('foo')\n  .end()\n  .descriptionFiles.add('foo')\n  .end()\n  .extensions.add('.js')\n  .end()\n  .mainFields.add('browser')\n  .end()\n  .mainFiles.add('index.js')\n  .end()\n  .plugin('foo')\n  .use(ResolvePluginImpl, [])\n  .end()\n  .plugins.delete('foo')\n  .end()\n  .end()\n\n  .resolveLoader.moduleExtensions.add('.js')\n  .end()\n  .packageMains.add('index.js')\n  .end()\n  .plugin('foo')\n  .use(webpack.DefinePlugin)\n  .end()\n  .end()\n\n  .performance.hints(true)\n  .hints('warning')\n  .maxEntrypointSize(20000)\n  .maxAssetSize(20000)\n  .assetFilter((filename) => true)\n  .end()\n\n  .optimization.concatenateModules(true)\n  .flagIncludedChunks(true)\n  .mergeDuplicateChunks(true)\n  .minimize(true)\n  .namedChunks(true)\n  .namedModules(true)\n  .nodeEnv(true)\n  .noEmitOnErrors(true)\n  .occurrenceOrder(true)\n  .portableRecords(true)\n  .providedExports(true)\n  .removeAvailableModules(true)\n  .removeEmptyChunks(true)\n  .runtimeChunk('single')\n  .runtimeChunk({ name: ({}) => 'hello' })\n  .sideEffects(true)\n  .splitChunks({})\n  .usedExports(true)\n  .minimizer('foo')\n  .use(webpack.DefinePlugin)\n  .tap((config) => [])\n  .end()\n  .end()\n\n  .plugin('foo')\n  .use(webpack.DefinePlugin, [\n    {\n      'process.env.NODE_ENV': '',\n    },\n  ])\n  .end()\n\n  .plugin('bar')\n  .use(webpack.DefinePlugin, [\n    {\n      'process.env.NODE_ENV': '',\n    },\n  ])\n  .before('foo')\n  .end()\n\n  .plugin('baz')\n  .use(webpack.DefinePlugin, [\n    {\n      'process.env.NODE_ENV': '',\n    },\n  ])\n  .after('bar')\n  .end()\n\n  .plugin('asString')\n  .use('package-name-or-path')\n  .end()\n\n  .plugin('asObject')\n  .use({ apply: (compiler: webpack.Compiler) => {} })\n  .end()\n\n  .plugins.delete('foo')\n  .delete('bar')\n  .delete('baz')\n  .delete('asString')\n  .delete('asObject')\n  .end()\n\n  .node.set('__dirname', true)\n  .delete('__dirname')\n  .clear()\n  .end()\n\n  .devServer.allowedHosts.add('host.com')\n  .clear()\n  .end()\n  .after(() => {})\n  .before(() => {})\n  .bonjour(true)\n  .clientLogLevel('error')\n  .color(true)\n  .compress(false)\n  .contentBase('/')\n  .contentBase(['foo', 'bar'])\n  .disableHostCheck(true)\n  .filename('hello')\n  .headers({\n    'Content-Type': 'text/css',\n  })\n  .historyApiFallback(true)\n  .host('localhost')\n  .hot(true)\n  .hotOnly(true)\n  .http2(true)\n  .https(true)\n  .index('test.html')\n  .info(true)\n  .inline(true)\n  .lazy(true)\n  .mimeTypes({ 'text/html': ['phtml'] })\n  .noInfo(true)\n  .open(true)\n  .openPage('/foo')\n  .openPage(['/foo', '/bar'])\n  .overlay(true)\n  .overlay({\n    warnings: true,\n    errors: true,\n  })\n  .pfx('/path/to/file.pfx')\n  .pfxPassphrase('passphrase')\n  .port(8080)\n  .progress(true)\n  .proxy({})\n  .public('foo')\n\n  .publicPath('bar')\n  .quiet(false)\n  .setup((app) => {})\n  .socket('socket')\n  .sockHost('localhost')\n  .sockPath('/sockpath/')\n  .sockPort(8080)\n  .staticOptions({})\n  .stats({\n    reasons: true,\n    errors: true,\n    warnings: false,\n  })\n  .stdin(true)\n  .useLocalIp(true)\n  .watchContentBase(true)\n  .watchOptions({})\n  .writeToDisk(true)\n  .end()\n\n  .module.noParse(/.min.js$/)\n  .strictExportPresence(true)\n  .rule('compile')\n  .test(/.js$/)\n  .include.add(/.js$/)\n  .end()\n  .exclude.add(/node_modules/)\n  .end()\n  .parser({\n    opt: 'foo',\n  })\n  .enforce('pre')\n  .use('babel')\n  .tap((config) => [])\n  .loader('babel-loader')\n  .options({})\n  .end()\n  .use('eslint')\n  .loader('eslint-loader')\n  .options({})\n  .after('babel')\n  .end()\n  .uses.delete('babel')\n  .delete('eslint')\n  .end()\n  .pre()\n  .post()\n  .rule('inline')\n  .after('vue')\n  .resourceQuery(/inline/)\n  .use('url')\n  .loader('url-loader')\n  .end()\n  .resolve.symlinks(true)\n  .end()\n  .end()\n  .rules.delete('inline')\n  .end()\n  .oneOf('inline')\n  .after('vue')\n  .uses.delete('babel')\n  .end()\n  .resourceQuery(/inline/)\n  .use('url')\n  .loader('url-loader')\n  .end()\n  .end()\n  .oneOfs.delete('inline')\n  .end()\n  .resolve.symlinks(true)\n  .end()\n  .end()\n  .rules.delete('compile')\n  .end()\n  .end()\n\n  //** support https://webpack.js.org/configuration/module/#ruletype  */\n  .module.rule('mjs-compile')\n  .test(/\\.mjs$/)\n  .type('javascript/auto')\n  .end()\n  .end()\n\n  .merge({})\n  .toConfig();\n\n// Test TypedChainedMap\nconst entryPoints = config.entryPoints;\n\nexpectType<typeof entryPoints>(entryPoints.clear());\nexpectType<typeof entryPoints>(entryPoints.delete('key'));\nexpectType<boolean>(entryPoints.has('key'));\nexpectType<Config.EntryPoint>(entryPoints.get('key'));\nexpectType<Config.EntryPoint>(\n  entryPoints.getOrCompute('key', () => new Config.EntryPoint()),\n);\nexpectType<typeof entryPoints>(entryPoints.set('key', new Config.EntryPoint()));\nexpectType<typeof entryPoints>(\n  entryPoints.merge({\n    key: new Config.EntryPoint(),\n  }),\n);\nexpectType<Record<string, Config.EntryPoint>>(entryPoints.entries());\nexpectType<typeof entryPoints>(\n  entryPoints.when(\n    true,\n    (val) => {\n      expectType<typeof entryPoints>(val);\n    },\n    (val) => {\n      expectType<typeof entryPoints>(val);\n    },\n  ),\n);\n\n// Test TypedChainedSet\nconst extensions = config.resolve.extensions;\n\nexpectType<typeof extensions>(extensions.add('.txt'));\nexpectType<typeof extensions>(extensions.prepend('.txt'));\nexpectType<typeof extensions>(extensions.clear());\nexpectType<typeof extensions>(extensions.delete('.txt'));\nexpectType<boolean>(extensions.has('.txt'));\nexpectType<typeof extensions>(extensions.merge(['.txt']));\nexpectType<string[]>(extensions.values());\nexpectType<typeof extensions>(\n  extensions.when(\n    true,\n    (val) => {\n      expectType<typeof extensions>(val);\n    },\n    (val) => {\n      expectType<typeof extensions>(val);\n    },\n  ),\n);\n"
  },
  {
    "path": "types/typings.json",
    "content": "{\n  \"name\": \"webpack-chain\",\n  \"main\": \"index.d.ts\"\n}\n"
  }
]