merge.js 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. 'use strict';
  2. const Assert = require('./assert');
  3. const Clone = require('./clone');
  4. const Utils = require('./utils');
  5. const internals = {};
  6. module.exports = internals.merge = function (target, source, options) {
  7. Assert(target && typeof target === 'object', 'Invalid target value: must be an object');
  8. Assert(source === null || source === undefined || typeof source === 'object', 'Invalid source value: must be null, undefined, or an object');
  9. if (!source) {
  10. return target;
  11. }
  12. options = Object.assign({ nullOverride: true, mergeArrays: true }, options);
  13. if (Array.isArray(source)) {
  14. Assert(Array.isArray(target), 'Cannot merge array onto an object');
  15. if (!options.mergeArrays) {
  16. target.length = 0; // Must not change target assignment
  17. }
  18. for (let i = 0; i < source.length; ++i) {
  19. target.push(Clone(source[i], { symbols: options.symbols }));
  20. }
  21. return target;
  22. }
  23. const keys = Utils.keys(source, options);
  24. for (let i = 0; i < keys.length; ++i) {
  25. const key = keys[i];
  26. if (key === '__proto__' ||
  27. !Object.prototype.propertyIsEnumerable.call(source, key)) {
  28. continue;
  29. }
  30. const value = source[key];
  31. if (value &&
  32. typeof value === 'object') {
  33. if (target[key] === value) {
  34. continue; // Can occur for shallow merges
  35. }
  36. if (!target[key] ||
  37. typeof target[key] !== 'object' ||
  38. (Array.isArray(target[key]) !== Array.isArray(value)) ||
  39. value instanceof Date ||
  40. (Buffer && Buffer.isBuffer(value)) || // $lab:coverage:ignore$
  41. value instanceof RegExp) {
  42. target[key] = Clone(value, { symbols: options.symbols });
  43. }
  44. else {
  45. internals.merge(target[key], value, options);
  46. }
  47. }
  48. else {
  49. if (value !== null &&
  50. value !== undefined) { // Explicit to preserve empty strings
  51. target[key] = value;
  52. }
  53. else if (options.nullOverride) {
  54. target[key] = value;
  55. }
  56. }
  57. }
  58. return target;
  59. };