123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102 |
- 'use strict';
- const Assert = require('./assert');
- const Clone = require('./clone');
- const Merge = require('./merge');
- const Reach = require('./reach');
- const internals = {};
- module.exports = function (defaults, source, options = {}) {
- Assert(defaults && typeof defaults === 'object', 'Invalid defaults value: must be an object');
- Assert(!source || source === true || typeof source === 'object', 'Invalid source value: must be true, falsy or an object');
- Assert(typeof options === 'object', 'Invalid options: must be an object');
- if (!source) { // If no source, return null
- return null;
- }
- if (options.shallow) {
- return internals.applyToDefaultsWithShallow(defaults, source, options);
- }
- const copy = Clone(defaults);
- if (source === true) { // If source is set to true, use defaults
- return copy;
- }
- const nullOverride = options.nullOverride !== undefined ? options.nullOverride : false;
- return Merge(copy, source, { nullOverride, mergeArrays: false });
- };
- internals.applyToDefaultsWithShallow = function (defaults, source, options) {
- const keys = options.shallow;
- Assert(Array.isArray(keys), 'Invalid keys');
- const seen = new Map();
- const merge = source === true ? null : new Set();
- for (let key of keys) {
- key = Array.isArray(key) ? key : key.split('.'); // Pre-split optimization
- const ref = Reach(defaults, key);
- if (ref &&
- typeof ref === 'object') {
- seen.set(ref, merge && Reach(source, key) || ref);
- }
- else if (merge) {
- merge.add(key);
- }
- }
- const copy = Clone(defaults, {}, seen);
- if (!merge) {
- return copy;
- }
- for (const key of merge) {
- internals.reachCopy(copy, source, key);
- }
- const nullOverride = options.nullOverride !== undefined ? options.nullOverride : false;
- return Merge(copy, source, { nullOverride, mergeArrays: false });
- };
- internals.reachCopy = function (dst, src, path) {
- for (const segment of path) {
- if (!(segment in src)) {
- return;
- }
- const val = src[segment];
- if (typeof val !== 'object' || val === null) {
- return;
- }
- src = val;
- }
- const value = src;
- let ref = dst;
- for (let i = 0; i < path.length - 1; ++i) {
- const segment = path[i];
- if (typeof ref[segment] !== 'object') {
- ref[segment] = {};
- }
- ref = ref[segment];
- }
- ref[path[path.length - 1]] = value;
- };
|