lib/config/Matryoshka/index.js
function merge(a, b) {
	// If a is not a matryoshka, then return a copy of b (override).
	if (!(a instanceof Matryoshka)) {
		return b.copy();
	}

	// If b is not a matryoshka, then just keep a.
	if (!(b instanceof Matryoshka)) {
		return a.copy();
	}

	// If we reached here, both a and b are matryoshkas.

	// Types are 'object' (not including array or null) and 'scalar' (everything else).


	// If a field is empty, merge with the other object
	// Ex:
	//		logging:
	//			server:
	//			||
	//			\/
	//		logging: {
	//			server: null
	//		}
	if (a.value === null) {
		return b.copy();
	}

	if (b.value === null) {
		return a.copy();
	}

	// Different types means that no merge is required, and we can just copy b.
	if (a.type !== b.type) {
		return b.copy();
	}

	// Scalar types are shallow, so a merge is really just an override.
	if (b.type === 'scalar') {
		return b.copy();
	}

	// If we reached here, then both a and b contain objects to be compared key-by-key.

	// First assemble a list of keys in one or both objects.
	const aKeys = Object.keys(a.value);
	const bKeys = Object.keys(b.value);
	const uniqueKeys = new Set(aKeys.concat(bKeys));

	const merged = {};

	// Merge key-by-key.
	for (const key of uniqueKeys) {
		merged[key] = merge(a.value[key], b.value[key]);
	}

	// Wrap the merged object in a Matryoshka.
	return new Matryoshka(merged, a.source, true);
}
function merge(a, b) {
	// If a is not a matryoshka, then return a copy of b (override).
	if (!(a instanceof Matryoshka)) {
		return b.copy();
	}

	// If b is not a matryoshka, then just keep a.
	if (!(b instanceof Matryoshka)) {
		return a.copy();
	}

	// If we reached here, both a and b are matryoshkas.

	// Types are 'object' (not including array or null) and 'scalar' (everything else).


	// If a field is empty, merge with the other object
	// Ex:
	//		logging:
	//			server:
	//			||
	//			\/
	//		logging: {
	//			server: null
	//		}
	if (a.value === null) {
		return b.copy();
	}

	if (b.value === null) {
		return a.copy();
	}

	// Different types means that no merge is required, and we can just copy b.
	if (a.type !== b.type) {
		return b.copy();
	}

	// Scalar types are shallow, so a merge is really just an override.
	if (b.type === 'scalar') {
		return b.copy();
	}

	// If we reached here, then both a and b contain objects to be compared key-by-key.

	// First assemble a list of keys in one or both objects.
	const aKeys = Object.keys(a.value);
	const bKeys = Object.keys(b.value);
	const uniqueKeys = new Set(aKeys.concat(bKeys));

	const merged = {};

	// Merge key-by-key.
	for (const key of uniqueKeys) {
		merged[key] = merge(a.value[key], b.value[key]);
	}

	// Wrap the merged object in a Matryoshka.
	return new Matryoshka(merged, a.source, true);
}
function merge ( a , b ) { // If a is not a matryoshka, then return a copy of b (override). if ( ! ( a instanceof Matryoshka ) ) { return b . copy ( ) ; } // If b is not a matryoshka, then just keep a. if ( ! ( b instanceof Matryoshka ) ) { return a . copy ( ) ; } // If we reached here, both a and b are matryoshkas. // Types are 'object' (not including array or null) and 'scalar' (everything else). // If a field is empty, merge with the other object // Ex: //		logging: //			server: //			|| //			\/ //		logging: { //			server: null //		} if ( a . value === null ) { return b . copy ( ) ; } if ( b . value === null ) { return a . copy ( ) ; } // Different types means that no merge is required, and we can just copy b. if ( a . type !== b . type ) { return b . copy ( ) ; } // Scalar types are shallow, so a merge is really just an override. if ( b . type === 'scalar' ) { return b . copy ( ) ; } // If we reached here, then both a and b contain objects to be compared key-by-key. // First assemble a list of keys in one or both objects. const aKeys = Object . keys ( a . value ) ; const bKeys = Object . keys ( b . value ) ; const uniqueKeys = new Set ( aKeys . concat ( bKeys ) ) ; const merged = { } ; // Merge key-by-key. for ( const key of uniqueKeys ) { merged [ key ] = merge ( a . value [ key ] , b . value [ key ] ) ; } // Wrap the merged object in a Matryoshka. return new Matryoshka ( merged , a . source , true ) ; }
Merges a and b together into a new Matryoshka. Does not affect the state of a or b, and b
overrides a. At least one is guaranteed to be a Matryoshka.

@param  {Matryoshka} a Matryoshka of lesser importance.
@param  {Matryoshka} b Matryoshka of greater importance.
@return {Matryoshka}   Resultant merge of a and b.
Merges a and b together into a new Matryoshka . Does not affect the state of a or b and b overrides a . At least one is guaranteed to be a Matryoshka .