{"id":16208475,"url":"https://github.com/ehmicky/declarative-merge","last_synced_at":"2025-03-19T08:30:48.054Z","repository":{"id":40602310,"uuid":"494066701","full_name":"ehmicky/declarative-merge","owner":"ehmicky","description":"Merge objects/arrays declaratively","archived":false,"fork":false,"pushed_at":"2025-03-13T05:04:08.000Z","size":9512,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-13T06:19:06.948Z","etag":null,"topics":["algorithm","append","array","clone","data-structures","deep","functional","functional-programming","insert","javascript","library","merge","node","nodejs","object","omit","override","patch","shallow","typescript"],"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/ehmicky.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-19T12:41:50.000Z","updated_at":"2025-03-13T05:04:12.000Z","dependencies_parsed_at":"2023-09-21T20:39:53.554Z","dependency_job_id":"86c322da-a36e-497c-a9a5-f68e8d3291a5","html_url":"https://github.com/ehmicky/declarative-merge","commit_stats":{"total_commits":343,"total_committers":2,"mean_commits":171.5,"dds":"0.011661807580174877","last_synced_commit":"0357b2f2f047a2f5af7af07203074d6d656aa599"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehmicky%2Fdeclarative-merge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehmicky%2Fdeclarative-merge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehmicky%2Fdeclarative-merge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ehmicky%2Fdeclarative-merge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ehmicky","download_url":"https://codeload.github.com/ehmicky/declarative-merge/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243976471,"owners_count":20377691,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["algorithm","append","array","clone","data-structures","deep","functional","functional-programming","insert","javascript","library","merge","node","nodejs","object","omit","override","patch","shallow","typescript"],"created_at":"2024-10-10T10:17:10.826Z","updated_at":"2025-03-19T08:30:47.538Z","avatar_url":"https://github.com/ehmicky.png","language":"JavaScript","readme":"\u003cimg alt=\"declarative-merge logo\" src=\"https://raw.githubusercontent.com/ehmicky/design/main/declarative-merge/declarative-merge.svg\" width=\"600\"/\u003e\n\n[![Node](https://img.shields.io/badge/-Node.js-808080?logo=node.js\u0026colorA=404040\u0026logoColor=66cc33)](https://www.npmjs.com/package/declarative-merge)\n[![Browsers](https://img.shields.io/badge/-Browsers-808080?logo=firefox\u0026colorA=404040)](https://unpkg.com/declarative-merge?module)\n[![TypeScript](https://img.shields.io/badge/-Typed-808080?logo=typescript\u0026colorA=404040\u0026logoColor=0096ff)](/src/main.d.ts)\n[![Codecov](https://img.shields.io/badge/-Tested%20100%25-808080?logo=codecov\u0026colorA=404040)](https://codecov.io/gh/ehmicky/declarative-merge)\n[![Minified size](https://img.shields.io/bundlephobia/minzip/declarative-merge?label\u0026colorA=404040\u0026colorB=808080\u0026logo=webpack)](https://bundlephobia.com/package/declarative-merge)\n[![Mastodon](https://img.shields.io/badge/-Mastodon-808080.svg?logo=mastodon\u0026colorA=404040\u0026logoColor=9590F9)](https://fosstodon.org/@ehmicky)\n[![Medium](https://img.shields.io/badge/-Medium-808080.svg?logo=medium\u0026colorA=404040)](https://medium.com/@ehmicky)\n\nMerge objects/arrays declaratively.\n\n- Objects: [deeply](#deep-merge), [shallowly](#shallow-merge), or\n  [both](#nesting)\n- Arrays: items can be [updated](#update), [merged](#merge), [added](#add),\n  [inserted](#insert), [appended](#append), [prepended](#prepend),\n  [deleted](#delete-1) or [set](#set)\n\n# Hire me\n\nPlease\n[reach out](https://www.linkedin.com/feed/update/urn:li:activity:7117265228068716545/)\nif you're looking for a Node.js API or CLI engineer (11 years of experience).\nMost recently I have been [Netlify Build](https://github.com/netlify/build)'s\nand [Netlify Plugins](https://www.netlify.com/products/build/plugins/)'\ntechnical lead for 2.5 years. I am available for full-time remote positions.\n\n# Use cases\n\nThis is intended for cases where objects/arrays manipulation in JavaScript is\nnot available.\n\nFor example, a library where shared configuration files can be extended.\n\n```yml\nextend: my-shared-config\n\n# Deep merge\nlog:\n  verbosity: silent\n  # Shallow merge\n  provider:\n    _merge: shallow\n    name: redis\n    type: local\n  # Delete properties\n  output:\n    _merge: delete\n\nrules:\n  # Update arrays deeply\n  1:\n    level: silent\n  # Append arrays\n  '-0':\n    name: appendedRule\n```\n\nOr a server receiving network patch requests.\n\n```http\nPATCH /pets/0\n```\n\n\u003c!-- prettier-ignore --\u003e\n```json5\n{\n  // Deep merge\n  \"capabilities\": { \"play\": true },\n  // Shallow merge\n  \"title\": { \"name\": \"felix\", \"_merge\": \"shallow\" },\n  // Append arrays\n  \"toys\": { \"-0\": \"newToy\" }\n}\n```\n\n# Examples\n\n## Objects\n\n### Deep merge\n\n```js\nimport declarativeMerge from 'declarative-merge'\n\ndeclarativeMerge({ a: 1, b: { c: 2 }, d: 3 }, { a: 10, b: { e: 20 } })\n// { a: 10, b: { c: 2, e: 20 }, d: 3 }\n```\n\n### Shallow merge\n\n```js\ndeclarativeMerge(\n  { a: 1, b: { c: 2 }, d: 3 },\n  { a: 10, b: { e: 20 }, _merge: 'shallow' },\n)\n// { a: 10, b: { e: 20 }, d: 3 }\n```\n\n### No merge\n\n```js\ndeclarativeMerge(\n  { a: 1, b: { c: 2 }, d: 3 },\n  { a: 10, b: { e: 20 }, _merge: 'set' },\n)\n// { a: 10, b: { e: 20 } }\n```\n\n### Nesting\n\n```js\n// `_merge` can be specified in nested objects\ndeclarativeMerge(\n  { a: 1, b: { c: 2 }, d: 3 },\n  { a: 10, b: { e: 20, _merge: 'set' } },\n)\n// { a: 10, b: { e: 20 }, d: 3 }\n\ndeclarativeMerge(\n  { a: 1, b: { c: 2 }, d: 3 },\n  { a: 10, b: { e: 20, _merge: 'deep' }, _merge: 'set' },\n)\n// { a: 10, b: { c: 2, e: 20 } }\n```\n\n### Delete\n\n```js\ndeclarativeMerge(\n  { a: 1, b: { c: 2 }, d: 3 },\n  { a: 10, b: { e: 20, _merge: 'delete' } },\n)\n// { a: 10, d: 3 }\n```\n\n## Arrays\n\n### Update\n\n```js\n// By default, arrays override each other\ndeclarativeMerge({ one: ['a', 'b', 'c'] }, { one: ['X', 'Y'] }) // { one: ['X', 'Y'] }\n\n// They can be updated instead using an object where the keys are the array\n// indices (before any updates).\ndeclarativeMerge(\n  { one: ['a', 'b', 'c'], two: 2 },\n  { one: { 1: 'X' }, three: 3 },\n)\n// { one: ['a', 'X', 'c'], two: 2, three: 3 }\n\n// This works on top-level arrays too\ndeclarativeMerge(['a', 'b', 'c'], { 1: 'X', 2: 'Y' }) // ['a', 'X', 'Y']\n```\n\n### Merge\n\n```js\n// If the new array items are objects, they are merged\ndeclarativeMerge(\n  [{ id: 'a' }, { id: 'b', value: { name: 'Ann' } }, { id: 'c' }],\n  { 1: { value: { color: 'red' } } },\n)\n// [{ id: 'a' }, { id: 'b', value: { name: 'Ann', color: 'red' } }, { id: 'c' }]\n\ndeclarativeMerge(\n  [{ id: 'a' }, { id: 'b', value: { name: 'Ann' } }, { id: 'c' }],\n  { 1: { value: { color: 'red' }, _merge: 'shallow' } },\n)\n// [{ id: 'a' }, { id: 'b', value: { color: 'red' } }, { id: 'c' }]\n```\n\n### Indices\n\n```js\ndeclarativeMerge(['a', 'b', 'c'], { '*': 'X' }) // ['X', 'X', 'X']\ndeclarativeMerge(['a', 'b', 'c'], { '-1': 'X' }) // ['a', 'b', 'X']\ndeclarativeMerge(['a', 'b', 'c'], { 4: 'X' }) // ['a', 'b', 'c', undefined, 'X']\n```\n\n### Add\n\n```js\n// Array of items can be used\ndeclarativeMerge(['a', 'b', 'c'], { 1: ['X', 'Y'] }) // ['a', 'X', 'Y', 'c']\ndeclarativeMerge(['a', 'b', 'c'], { 1: ['X'] }) // ['a', 'X', 'c']\ndeclarativeMerge(['a', 'b', 'c'], { 1: [['X']] }) // ['a', ['X'], 'c']\n```\n\n### Insert\n\n```js\n// If the key ends with +, items are prepended, not replaced\ndeclarativeMerge(['a', 'b', 'c'], { '1+': 'X' }) // ['a', 'X', 'b', 'c']\n```\n\n### Append\n\n```js\ndeclarativeMerge(['a', 'b', 'c'], { '-0': 'X' }) // ['a', 'b', 'c', 'X']\ndeclarativeMerge(['a', 'b', 'c'], { '-0': ['X', 'Y'] }) // ['a', 'b', 'c', 'X', 'Y']\n```\n\n### Prepend\n\n```js\ndeclarativeMerge(['a', 'b', 'c'], { '0+': ['X', 'Y'] }) // ['X', 'Y', 'a', 'b', 'c']\n```\n\n### Delete\n\n```js\ndeclarativeMerge(['a', 'b', 'c'], { 1: [] }) // ['a', 'c']\n```\n\n### Set\n\n```js\ndeclarativeMerge({}, { one: { 0: 'X', 2: 'Z' } }) // { one: ['X', undefined, 'Z'] }\ndeclarativeMerge({ one: true }, { one: { 0: 'X', 2: 'Z' } }) // { one: ['X', undefined, 'Z'] }\n```\n\n# Install\n\n```bash\nnpm install declarative-merge\n```\n\nThis package works in both Node.js \u003e=18.18.0 and\n[browsers](https://raw.githubusercontent.com/ehmicky/dev-tasks/main/src/browserslist).\n\nThis is an ES module. It must be loaded using\n[an `import` or `import()` statement](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c),\nnot `require()`. If TypeScript is used, it must be configured to\n[output ES modules](https://www.typescriptlang.org/docs/handbook/esm-node.html),\nnot CommonJS.\n\n# API\n\n## declarativeMerge(firstValue, secondValue, options?)\n\n`firstValue` `any`\\\n`secondValue` `any`\\\n`options` [`Options`](#options)\\\n_Return value_: `any`\n\nMerge `firstValue` and `secondValue`.\n\n### Merge mode\n\n[Any object can change](#nesting) the merge mode using a `_merge` property with\nvalue [`\"deep\"`](#deep-merge) (default), [`\"shallow\"`](#shallow-merge),\n[`\"set\"`](#no-merge) or [`\"delete\"`](#delete).\n\nArrays [can be merged using objects](#arrays) where the keys are the\n[array indices](#update). Items can be [updated](#update), [merged](#merge),\n[added](#add), [inserted](#insert), [appended](#append), [prepended](#prepend),\n[deleted](#delete-1) or [set](#set).\n\nThe `_merge` property and array updates objects can only be used in\n`secondValue`. They are left as is in `firstValue`.\n\n### Cloning\n\n`firstValue` and `secondValue` are not modified. Plain objects and arrays are\ndeeply cloned.\n\n[Inherited](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)\nand\n[non-enumerable properties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties)\nare ignored.\n\n### Options\n\nOptions are an optional object.\n\n#### key\n\n_Type_: `string | symbol`\\\n_Default_: `\"_merge\"`\n\nName of the property used to specify the [merge mode](#merge-mode).\n\n```js\ndeclarativeMerge({ a: 1 }, { b: 2, _mergeMode: 'set' }, { key: '_mergeMode' }) // { b: 2 }\n```\n\nSymbols can be useful to prevent injections when the input is user-provided.\n\n```js\nconst mergeMode = Symbol('mergeMode')\ndeclarativeMerge({ a: 1 }, { b: 2, [mergeMode]: 'set' }, { key: mergeMode }) // { b: 2 }\n```\n\n# Related projects\n\n- [`set-array`](https://github.com/ehmicky/set-array): underlying module to\n  [merge arrays](#arrays)\n- [`wild-wild-utils`](https://github.com/ehmicky/wild-wild-utils): apply\n  `declarative-merge` on multiple properties at once using this module's\n  [`merge()` method](https://github.com/ehmicky/wild-wild-utils#mergetarget-query-value-options)\n\n# Support\n\nFor any question, _don't hesitate_ to [submit an issue on GitHub](../../issues).\n\nEveryone is welcome regardless of personal background. We enforce a\n[Code of conduct](CODE_OF_CONDUCT.md) in order to promote a positive and\ninclusive environment.\n\n# Contributing\n\nThis project was made with ❤️. The simplest way to give back is by starring and\nsharing it online.\n\nIf the documentation is unclear or has a typo, please click on the page's `Edit`\nbutton (pencil icon) and suggest a correction.\n\nIf you would like to help us fix a bug or add a new feature, please check our\n[guidelines](CONTRIBUTING.md). Pull requests are welcome!\n\n\u003c!-- Thanks go to our wonderful contributors: --\u003e\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START --\u003e\n\u003c!-- prettier-ignore --\u003e\n\u003c!--\n\u003ctable\u003e\u003ctr\u003e\u003ctd align=\"center\"\u003e\u003ca href=\"https://fosstodon.org/@ehmicky\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/8136211?v=4\" width=\"100px;\" alt=\"ehmicky\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eehmicky\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/ehmicky/declarative-merge/commits?author=ehmicky\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"#design-ehmicky\" title=\"Design\"\u003e🎨\u003c/a\u003e \u003ca href=\"#ideas-ehmicky\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e \u003ca href=\"https://github.com/ehmicky/declarative-merge/commits?author=ehmicky\" title=\"Documentation\"\u003e📖\u003c/a\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n --\u003e\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fehmicky%2Fdeclarative-merge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fehmicky%2Fdeclarative-merge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fehmicky%2Fdeclarative-merge/lists"}