{"id":28545130,"url":"https://github.com/moxystudio/js-chained-config","last_synced_at":"2026-03-10T04:31:48.562Z","repository":{"id":57196683,"uuid":"126465598","full_name":"moxystudio/js-chained-config","owner":"moxystudio","description":"Use a chaining API to generate and simplify the modification of configs","archived":false,"fork":false,"pushed_at":"2020-01-03T10:08:35.000Z","size":440,"stargazers_count":5,"open_issues_count":0,"forks_count":3,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-10-18T16:19:32.975Z","etag":null,"topics":["chain","chainable","config","configuration"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moxystudio.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-03-23T09:51:49.000Z","updated_at":"2020-01-03T10:08:38.000Z","dependencies_parsed_at":"2022-09-15T15:41:09.939Z","dependency_job_id":null,"html_url":"https://github.com/moxystudio/js-chained-config","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/moxystudio/js-chained-config","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moxystudio%2Fjs-chained-config","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moxystudio%2Fjs-chained-config/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moxystudio%2Fjs-chained-config/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moxystudio%2Fjs-chained-config/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moxystudio","download_url":"https://codeload.github.com/moxystudio/js-chained-config/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moxystudio%2Fjs-chained-config/sbom","scorecard":{"id":661817,"data":{"date":"2025-08-11","repo":{"name":"github.com/moxystudio/js-chained-config","commit":"b73312cd8d5673f214e37edd4eccacff3e5d1a06"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 2/26 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 6 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"58 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-6chw-6frg-f759","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-fwr7-v2mv-hh25","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-ff7x-qrg7-qggm","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-62gr-4qp9-h98f","Warn: Project is vulnerable to: GHSA-f52g-6jhx-586p","Warn: Project is vulnerable to: GHSA-2cf5-4w76-r9qv","Warn: Project is vulnerable to: GHSA-3cqr-58rm-57f8","Warn: Project is vulnerable to: GHSA-g9r4-xpmj-mj65","Warn: Project is vulnerable to: GHSA-q2c6-c6pm-g3gh","Warn: Project is vulnerable to: GHSA-765h-qjxv-5f44","Warn: Project is vulnerable to: GHSA-f2jv-r9rf-7988","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-2pr6-76vf-7546","Warn: Project is vulnerable to: GHSA-8j8c-7jfh-h6hx","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-6c8f-qphg-qjgp","Warn: Project is vulnerable to: GHSA-4xc9-xhrj-v574","Warn: Project is vulnerable to: GHSA-x5rq-j2xg-h7qm","Warn: Project is vulnerable to: GHSA-jf85-cpcp-j695","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-h726-x36v-rx45","Warn: Project is vulnerable to: GHSA-779f-wgxg-qr8f","Warn: Project is vulnerable to: GHSA-4xcv-9jjx-gfj3","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-5fw9-fq32-wv5p","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-4g88-fppr-53pp","Warn: Project is vulnerable to: GHSA-4jqc-8m5r-9rpr","Warn: Project is vulnerable to: GHSA-7xcx-6wjh-7xp2","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-jgrx-mgxx-jf9v","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-7p7h-4mm5-852v","Warn: Project is vulnerable to: GHSA-38fc-wpqx-33j7","Warn: Project is vulnerable to: GHSA-6fc8-4gx4-v693","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh","Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-21T16:26:08.507Z","repository_id":57196683,"created_at":"2025-08-21T16:26:08.507Z","updated_at":"2025-08-21T16:26:08.507Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30324411,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T01:36:58.598Z","status":"online","status_checked_at":"2026-03-10T02:00:06.579Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chain","chainable","config","configuration"],"created_at":"2025-06-09T23:06:39.604Z","updated_at":"2026-03-10T04:31:48.549Z","avatar_url":"https://github.com/moxystudio.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# chained-config\n\n[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Coverage Status][codecov-image]][codecov-url] [![Dependency status][david-dm-image]][david-dm-url] [![Dev Dependency status][david-dm-dev-image]][david-dm-dev-url]\n\n[npm-url]:https://npmjs.org/package/chained-config\n[downloads-image]:https://img.shields.io/npm/dm/chained-config.svg\n[npm-image]:https://img.shields.io/npm/v/chained-config.svg\n[travis-url]:https://travis-ci.org/moxystudio/js-chained-config\n[travis-image]:https://img.shields.io/travis/moxystudio/js-chained-config/master.svg\n[codecov-url]:https://codecov.io/gh/moxystudio/js-chained-config\n[codecov-image]:https://img.shields.io/codecov/c/github/moxystudio/js-chained-config/master.svg\n[david-dm-url]:https://david-dm.org/moxystudio/js-chained-config\n[david-dm-image]:https://img.shields.io/david/moxystudio/js-chained-config.svg\n[david-dm-dev-url]:https://david-dm.org/moxystudio/js-chained-config?type=dev\n[david-dm-dev-image]:https://img.shields.io/david/dev/moxystudio/js-chained-config.svg\n\nUse a chaining API to generate and simplify the modification of configs.\n\n\n## Installation\n\n`$ npm install chained-config`\n\n\n## Motivation\n\nWith `webpack-chain`, manipulating the webpack config is a breeze. This is possible thanks to [ChainedMap](https://github.com/mozilla-neutrino/webpack-chain/blob/2445476d0d7f444c34060f383c890597ca0d2e8d/src/ChainedMap.js) and [ChainedSet](https://github.com/mozilla-neutrino/webpack-chain/blob/2445476d0d7f444c34060f383c890597ca0d2e8d/src/ChainedSet.js).\n\n`chained-config` offers those constructors in a separate package so that we can apply the same concepts to other configurations other than Webpack. There are small differences that are noted in each API section.\n\n\n## Usage\n\n```js\nconst {\n    ChainedMap,\n    OrderableChainedMap,\n    ChainedSet,\n} = require('chained-config');\n```\n\nEither you may use these directly or extend them, just like [DevServer](https://github.com/mozilla-neutrino/webpack-chain/blob/2445476d0d7f444c34060f383c890597ca0d2e8d/src/DevServer.js) from `webpack-chain` extends `ChainedMap`.\n\n```js\nconst chainedMap = new ChainedMap();\n\n// ..or\n\nclass MyConfig extends ChainedMap {\n    // your own methods here..\n}\n\nconst myConfig = new MyConfig();\n```\n\n\n## API\n\n### Chainable\n\nThis is the base class that others inherit from, it's not meant to be used directly.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eOpen to see the differences relative to \u003ccode\u003ewebpack-chain\u003c/code\u003e\u003c/b\u003e\u003c/summary\u003e\n  \u003cul\u003e\n    \u003cli\u003eMoved \u003ccode\u003e.when(condition, whenTruthy, whenFalsy)\u003c/code\u003e from ChainedMap and ChainedSet to Chainable to avoid having them replicated\u003c/li\u003e\n    \u003cli\u003eAdded \u003ccode\u003e#isChainable\u003c/code\u003e static method to test if a value is an instance of Chainable\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/details\u003e\n\n\n#### constructor(parent)\n\nThe constructor expects a `parent` pointing to the parent chain, if any.\n\n#### .end()\n\nEnds the current chain, going back to the parent chain.\n\nReturns the parent.\n\n#### .batch(fn)\n\nExecute a function against the current chain.   \nThis is useful to execute various operations while not breaking the chain.\n\nReturns itself to allow chaining.\n\n#### .when(condition, whenTruthy, whenFalsy)\n\nConditionally execute a function on the current chain.   \n`whenTruthy` and `whenFalsy` are called if condition is truthy or falsy respectively.\n\nReturns itself to allow chaining.\n\n#### #isChainable(value)\n\nTest if `value` is an instance of Chainable.\n\nReturns true if it is, false otherwise.\n\n\n### ChainedMap\n\nA ChainedMap operates similarly to a JavaScript Map, with some conveniences for chaining and generating configs. It extends [Chainable](#chainable) which means that all its methods are also available.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eOpen to see the differences relative to \u003ccode\u003ewebpack-chain\u003c/code\u003e\u003c/b\u003e\u003c/summary\u003e\n  \u003cul\u003e\n    \u003cli\u003eRemoved \u003ccode\u003eorder()\u003c/code\u003e because it's an internal method, reducing the number of conflicts in case you create a class that inherits from \u003ccode\u003eChainedMap\u003c/code\u003e\u003c/li\u003e\n    \u003cli\u003eRemoved \u003ccode\u003e.clean(obj)\u003c/code\u003e because it was used as a helper (it didn't use \u003ccode\u003ethis.store\u003c/code\u003e whatsoever)\u003c/li\u003e\n    \u003cli\u003eRenamed \u003ccode\u003e.extend(shorthands)\u003c/code\u003e to \u003ccode\u003e.shorthands(keys)\u003c/code\u003e\u003c/li\u003e\n    \u003cli\u003eChanged \u003ccode\u003e.entries(obj)\u003c/code\u003e to return an array of key and value pairs just like the native \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys\"\u003eMap\u003c/a\u003e\n    \u003cli\u003eChanged \u003ccode\u003e.set(key, value)\u003c/code\u003e to automatically create a getter \u0026 setter if \u003ccode\u003evalue\u003c/code\u003e is a Chainable\u003c/li\u003e\n    \u003cli\u003eAdded \u003ccode\u003e.tap(key, fn)\u003c/code\u003e\u003c/li\u003e\n    \u003cli\u003eAdded \u003ccode\u003e.keys()\u003c/code\u003e just like the native \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys\"\u003eMap\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003eAdded \u003ccode\u003e.forEach()\u003c/code\u003e just like the native \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach\"\u003eMap\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003eAdded \u003ccode\u003e.toConfig()\u003c/code\u003e which returns an object representation of the config, calling \u003ccode\u003e.toConfig()\u003c/code\u003e recursively for each item\u003c/li\u003e\n  \u003c/ul\u003e\n\n  Some of the changes detailed above are *breaking* but they will mostly affect developers implementing the configs and not the consumers.\n\u003c/details\u003e\n\n\n#### constructor(parent, [options])\n\nAt the moment, ChainedMap accepts a single option named `asArray`.\n\nA Set (and ChainedSet) is very limited in its capacities. With `asArray`, we can leverage ChainedMap features while keeping the return type of `toConfig` as an array. In this model, the keys on the backing map act as labels that identify items, making it easier for consumers to perform manipulations. Moreover, when using OrderableChainedMap, consumers may even change the items' order.\n\n#### .get(key)\n\nRetrieve the value of the item corresponding to `key`.\n\n#### .set(key, value)\n\nSet the value of the item corresponding to `key`.\n\nIf `value` is a `Chainable`, a getter/setter will automatically be created with the name `key`:\n\n```js\nclass MyConfig extends ChainedMap {\n    constructor(parent) {\n        super(parent);\n\n        this.set('foo', new ChainedMap(this));\n    }\n}\n\nconst myConfig = new MyConfig();\n\n// myConfig.foo is a `ChainedMap` instance\n```\n\nReturns itself to allow chaining.\n\n#### .tap(key, fn)\n\nExecute `fn` with the current value of the item corresponding to `key`.   \nThe return value of `fn` will replace the current value.\n\nReturns itself to allow chaining.\n\n#### .delete(key)\n\nRemove the item corresponding to `key`.\n\nReturns itself to allow chaining.\n\n#### .has(key)\n\nCheck if there's an item corresponding to `key`.\n\nReturns `true` if it exists, `false` otherwise.\n\n#### .clear()\n\nRemove all items from the Map.\n\nReturns itself to allow chaining.\n\n#### .keys()\n\nReturn an array of the keys stored in the Map.\n\n#### .values()\n\nReturn an array of the values stored in the Map.\n\n#### .entries()\n\nReturn an array of all the `[key, value]` pairs for each element stored in the Map.\n\n#### .forEach(fn, [thisArg])\n\nExecute `fn` once per each key/value pair in the Map.\n\n#### .merge(ob, [omit])\n\nProvide an object which maps its properties and values into the backing Map as keys and values.   \nYou can also provide an array as the second argument for property names to omit from being merged.\n\nReturns itself to allow chaining.\n\n#### .shorthands(keys)\n\nAdd shorthands for every key specified in `keys` array.\n\nEssentially, this method is a sugar for:\n\n```js\nclass MyConfig extends ChainedMap {\n    foo(value) {\n        return this.set('foo', value);\n    }\n\n    bar(value) {\n        return this.set('bar', value);\n    }\n};\n```\n\nwhich can be written as:\n\n```js\nclass MyConfig extends ChainedMap {\n    constructor(parent) {\n        super(parent);\n\n        this.shorthands(['foo', 'bar']);\n    }\n};\n```\n\nReturns itself to allow chaining.\n\n#### .toConfig()\n\nReturn the plain object representation of the config.   \nCalls `.toConfig()` recursively for each item that is a chainable.\n\n\n#### Example usage of ChainedMap\n\n```js\nclass Jest extends ChainedMap {\n    constructor(parent) {\n        super(parent);\n\n        this.shorthands([\n            'rootDir',\n            'runner',\n        ]);\n\n        this.set('coverageThresholds', new ChainedMap(this));\n        this.set('coveragePathIgnorePatterns', new ChainedMap(this, { asArray: true }));\n    }\n}\n\n// Then use it like so:\nconst jestConfig = (new Jest())\n    .rootDir('my-project')\n    .runner('jest-runner-mocha')\n    .coverageThresholds\n        .set('global', {\n            branches: 80,\n            functions: 80,\n            lines: 80,\n            statements: -10\n        })\n        .tap('global', (thresholds) =\u003e Object.assign(thresholds, { branches: 100 }))\n        .end()\n    .coveragePathIgnorePatterns\n        .set('node_modules', '/node_modules/')\n        .end()\n    .toConfig();\n\n// {\n//     rootDir: 'my-project',\n//     runner: 'jest-runner-mocha',\n//     coverageThresholds: {\n//         global: {\n//             branches: 100,\n//             functions: 80,\n//             lines: 80,\n//             statements: -10,\n//         },\n//     },\n//     coveragePathIgnorePatterns: ['/node_modules/'],\n// }\n```\n\n\n### OrderableChainedMap\n\nOrderableChainedMap extends [Chainable](#chained-map) and allows to re-order keys via the `move(key, specifier)` method. It also adds `before(key)` and `after(key)` methods on Chainables added via `set`. This allows an item to declare that it comes before or after another by calling the aforementioned methods on itself.\n\nConsequently, `keys()`, `values()`, `entries()`, `forEach()` and `toConfig()` methods of OrderableChainedMap will have into consideration any changes to the items order.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eOpen to see the differences relative to \u003ccode\u003ewebpack-chain\u003c/code\u003e\u003c/b\u003e\u003c/summary\u003e\n  \u003cul\u003e\n    \u003cli\u003e\u003ccode\u003ewebpack-chain\u003c/code\u003e has Orderable which is a function that receives a Chainable and adds the \u003ccode\u003ebefore\u003c/code\u003e and \u003ccode\u003eafter\u003c/code\u003e methods. OrderableChainedMap is more flexible since it allows ordering items whose values are primitives\u003c/li\u003e\n    \u003cli\u003eRemoved weird treatment on \u003ccode\u003e.merge(obj, [omit])\u003c/code\u003e of \u003ccode\u003eafter\u003c/code\u003e and \u003ccode\u003ebefore\u003c/code\u003e keys in \u003ccode\u003eobj\u003c/code\u003e\u003c/li\u003e\n    \u003cli\u003eFixed minor bugs that caused \u003ccode\u003e.keys()\u003c/code\u003e, \u003ccode\u003e.values()\u003c/code\u003e and \u003ccode\u003e.entries()\u003c/code\u003e to not respect the order specified with \u003ccode\u003e.before(key)\u003c/code\u003e and \u003ccode\u003e.after(key)\u003c/code\u003e\n  \u003c/ul\u003e\n\u003c/details\u003e\n\n\n#### .move(key, specifier)\n\nMoves the item corresponding to `key` before of after another, according to the `specifier`.\n\n`specifier` is a function that will be called with a single argument which is an object with `before(relativeKey)` and `after(relativeKey)` functions.\n\nReturns itself to allow chaining.\n\n\n#### Methods added to Chainables included via `set`\n\n##### .before(key)\n\nMarks the item to be positioned before `key` on the OrderableChainedMap they belong.\n\nReturns itself to allow chaining.\n\n##### .after(key)\n\nMarks the item to be positioned after `key` on the OrderableChainedMap they belong.\n\nReturns itself to allow chaining.\n\n\n#### Example usage of OrderableChainedMap\n\n```js\nclass Jest extends ChainedMap {\n    constructor(parent) {\n        super(parent);\n\n        this.shorthands([\n            'rootDir',\n            'runner',\n        ]);\n\n        this.set('setupFiles', new OrderableChainedMap(this, { asArray: true }));\n        this.set('projects', new OrderableChainedMap(this, { asArray: true }));\n    }\n\n    project(name) {\n        if (!this.projects.has(name)) {\n            this.projects.set(name, new ChainedMap(this));\n        }\n\n        return this.projects.get(name);\n    }\n}\n\n// Then use it like so:\nconst jestConfig = (new Jest())\n    // Using `move` to re-order items\n    .setupFiles\n        .set('setup-foo', '/path/to/setup/foo.js')\n        .set('setup-bar', '/path/to/setup/bar.js')\n        .set('setup-baz', '/path/to/setup/baz.js')\n        .move('setup-bar', ({ before }) =\u003e before('setup-foo'))\n        .move('setup-baz', ({ after }) =\u003e after('setup-foo'))\n        .end()\n    // Using `before` and `after` which where added automatically by\n    // OrderableChainedMap to items added via `set`\n    .project('examples')\n        .set('displayName', 'lint')\n        .set('runner', 'jest-runner-eslint')\n        .set('testMatch', ['\u003crootDir\u003e/**/*.js'])\n        .end()\n    .project('test')\n        .set('displayName', 'test')\n        .before('examples')\n        .end()\n    .toConfig();\n\n// {\n//     setupFiles: [\n//         '/path/to/setup/bar.js',\n//         '/path/to/setup/foo.js',\n//         '/path/to/setup/baz.js',\n//     ],\n//     projects: [\n//         {\n//             displayName: 'test',\n//         },\n//         {\n//             displayName: 'lint',\n//             runner: 'jest-runner-eslint',\n//             testMatch: ['\u003crootDir\u003e/**/*.js'],\n//         },\n//     ],\n// }\n```\n\n\n### ChainedSet\n\nA ChainedSet operates similarly to a JavaScript Set, with some conveniences for chaining and generating configs. It extends [Chainable](#chainable) which means that all its methods are also available.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003eOpen to see the differences relative to \u003ccode\u003ewebpack-chain\u003c/code\u003e\u003c/b\u003e\u003c/summary\u003e\n  \u003cul\u003e\n    \u003cli\u003eAdded \u003ccode\u003e.forEach()\u003c/code\u003e just like the native \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/forEach\"\u003eSet\u003c/a\u003e\u003c/li\u003e\n    \u003cli\u003eAdded \u003ccode\u003e.toConfig()\u003c/code\u003e which returns an array representation of the config, calling \u003ccode\u003e.toConfig()\u003c/code\u003e recursively for each stored item\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/details\u003e\n\n\n#### .add(value)\n\nAdds `value` to the end of the Set.\n\nReturns itself to allow chaining.\n\n#### .prepend(value)\n\nAdds `value` to the start of the Set.\n\nReturns itself to allow chaining.\n\n#### .delete(value)\n\nRemoves `value` from the Set.\n\nReturns itself to allow chaining.\n\n#### .has(value)\n\nCheck if `value` is within the Set.\n\nReturns `true` if it exists, `false` otherwise.\n\n#### .clear()\n\nRemove all items from the Set.\n\nReturns itself to allow chaining.\n\n#### .values()\n\nReturns an array of the values stored in the Set.\n\n#### .forEach(fn, [thisArg])\n\nExecute `fn` once per each value in the Set.\n\n#### .merge(arr)\n\nProvide an array whose items will be added to the Set.\n\nReturns itself to allow chaining.\n\n#### .toConfig()\n\nReturn the array representation of the config.   \nCalls `.toConfig()` recursively for each item that is a chainable.\n\n\n#### Example usage of ChainedSet\n\n```js\nclass Jest extends ChainedMap {\n    constructor(parent) {\n        super(parent);\n\n        this.shorthands([\n            'rootDir',\n            'runner',\n            // ...\n        ]);\n\n        this.set('moduleFileExtensions', new ChainedSet(this));\n    }\n}\n\n// Then use it like so:\nconst jestConfig = (new Jest())\n    .rootDir('my-project')\n    .runner('jest-runner-mocha')\n    .moduleFileExtensions\n        .add('ts')\n        .add('tsx')\n        .end()\n    .toConfig();\n\n// {\n//     rootDir: 'my-project',\n//     runner: 'jest-runner-mocha',\n//     moduleFileExtensions: ['ts', 'tsx'],\n// }\n```\n\n\n## Tests\n\n`$ npm test`   \n`$ npm test -- --watch` during development\n\n\n## License\n\nReleased under the [MIT License](https://www.opensource.org/licenses/mit-license.php).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoxystudio%2Fjs-chained-config","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoxystudio%2Fjs-chained-config","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoxystudio%2Fjs-chained-config/lists"}