No Description

Sven Slootweg ff2af85fb1 0.1.3 3 weeks ago
.eslintrc 6be177aaaf Initial commit 3 weeks ago
.gitignore 6be177aaaf Initial commit 3 weeks ago
README.md da7feeaab8 Improve README a bit 3 weeks ago
example.js 426b578acc Remove item count requirement 3 weeks ago
index.js 426b578acc Remove item count requirement 3 weeks ago
package.json ff2af85fb1 0.1.3 3 weeks ago
yarn.lock 6be177aaaf Initial commit 3 weeks ago

README.md

merge-by-template

A library for generating custom deep-merging functions, that merge data structures according to a set of (nested) rules that you specify.

This module is still a work-in-progress! It's already usable in production code, but the documentation is still rough, it may contain the occasional bug, and its API might still change in the future.

Example

A runnable version of this example (and more examples) can be found in example.js in this project's repository.

First, you define a merging template, which results in a custom merging function:

const mergeByTemplate = require("merge-by-template");

let mergeConfiguration = mergeByTemplate.createMerger({
	/* `null` makes it explicit that this property should be overridden as a single value,
	   despite being an object - but leaving the property out entirely would have had the
	   same result, so this is strictly for readability */
	database: null,
	scripts: {},
	accessList: [],
	powerLevel: (a, b) => a + b
});

Then, you use that with two or more input values (eg. objects):

let defaultConfiguration = {
	database: {
		type: "socket",
		path: "/default"
	},
	scripts: {
		test: "echo 'no test configured'",
		publish: "npm publish"
	},
	accessList: [
		"maintainer-bot"
	],
	powerLevel: 8999
};

let customConfiguration = {
	database: {
		hostname: "localhost",
		port: "1234",
		username: "hello",
		password: "world"
	},
	scripts: {
		test: "node test.js",
		build: "node build.js"
	},
	accessList: [
		"real-person"
	],
	powerLevel: 2
};

console.log(mergeConfiguration([ defaultConfiguration, customConfiguration ]));

... and the result of that is a value that's been deep-merged according to your specifications:

{
  database: {
    hostname: 'localhost',
    port: '1234',
    username: 'hello',
    password: 'world'
  },
  scripts: {
    test: 'node test.js',
    publish: 'npm publish',
    build: 'node build.js'
  },
  accessList: [ 'maintainer-bot', 'real-person' ],
  powerLevel: 9001
}

Rules

This section will be expanded in the future.

The basic principle: Regardless of how many values you pass into the custom merging function, it will always merge them per 2. So if you pass in [ a, b, c ] then it will first merge b onto a, and then merge c onto the result of the b -> a merger. The B side always takes precedence in the default merging strategies.

Rules can be nested to any depth (until you hit the runtime's stack size limit, anyway). This allows merging complex nested data structures.

For now, a quick listing of rule syntax:

  • No rule specified (or explicit null or undefined specified): One value overrides the other in full, even if that value is an object or array.
  • Object specified: The input values are expected to be plain objects, and each property will be merged/overridden individually.
    • Empty object: This means all properties are merged according to "No rule specified", ie. the value of one object's property overrides the other.
    • Object with rules: Each specified property is merged according to whatever rule syntax is used for that property. Unspecified properties are overridden according to "No rule specified".
  • Empty array specified: The input values are expected to be arrays, and they will be concatenated together.
  • Array with items specified: Each item is treated as a positional rule. So if you specify an array with two rules, the first item of each input array gets merged according to the first rule, the second item of each input array according to the second rule, and so on. Any surplus items for which no rule exists, are overridden according to "No rule specified".
  • Function specified: The function is called with (a, b) as arguments, and is expected to return whatever the merge result should be. This lets you implement any custom merging logic, at any level in the data structure.