| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 | 'use strict';const Types = require('./types');const Utils = require('./utils');const internals = {    needsProtoHack: new Set([Types.set, Types.map, Types.weakSet, Types.weakMap])};module.exports = internals.clone = function (obj, options = {}, _seen = null) {    if (typeof obj !== 'object' ||        obj === null) {        return obj;    }    let clone = internals.clone;    let seen = _seen;    if (options.shallow) {        if (options.shallow !== true) {            return internals.cloneWithShallow(obj, options);        }        clone = (value) => value;    }    else {        seen = seen || new Map();        const lookup = seen.get(obj);        if (lookup) {            return lookup;        }    }    // Built-in object types    const baseProto = Types.getInternalProto(obj);    if (baseProto === Types.buffer) {        return Buffer && Buffer.from(obj);              // $lab:coverage:ignore$    }    if (baseProto === Types.date) {        return new Date(obj.getTime());    }    if (baseProto === Types.regex) {        return new RegExp(obj);    }    // Generic objects    const newObj = internals.base(obj, baseProto, options);    if (newObj === obj) {        return obj;    }    if (seen) {        seen.set(obj, newObj);                              // Set seen, since obj could recurse    }    if (baseProto === Types.set) {        for (const value of obj) {            newObj.add(clone(value, options, seen));        }    }    else if (baseProto === Types.map) {        for (const [key, value] of obj) {            newObj.set(key, clone(value, options, seen));        }    }    const keys = Utils.keys(obj, options);    for (const key of keys) {        if (key === '__proto__') {            continue;        }        if (baseProto === Types.array &&            key === 'length') {            newObj.length = obj.length;            continue;        }        const descriptor = Object.getOwnPropertyDescriptor(obj, key);        if (descriptor) {            if (descriptor.get ||                descriptor.set) {                Object.defineProperty(newObj, key, descriptor);            }            else if (descriptor.enumerable) {                newObj[key] = clone(obj[key], options, seen);            }            else {                Object.defineProperty(newObj, key, { enumerable: false, writable: true, configurable: true, value: clone(obj[key], options, seen) });            }        }        else {            Object.defineProperty(newObj, key, {                enumerable: true,                writable: true,                configurable: true,                value: clone(obj[key], options, seen)            });        }    }    return newObj;};internals.cloneWithShallow = function (source, options) {    const keys = options.shallow;    options = Object.assign({}, options);    options.shallow = false;    const storage = Utils.store(source, keys);    // Move shallow copy items to storage    const copy = internals.clone(source, options);      // Deep copy the rest    Utils.restore(copy, source, storage);         // Shallow copy the stored items and restore    return copy;};internals.base = function (obj, baseProto, options) {    if (baseProto === Types.array) {        return [];    }    if (options.prototype === false) {                  // Defaults to true        if (internals.needsProtoHack.has(baseProto)) {            return new baseProto.constructor();        }        return {};    }    const proto = Object.getPrototypeOf(obj);    if (proto &&        proto.isImmutable) {        return obj;    }    if (internals.needsProtoHack.has(baseProto)) {        const newObj = new proto.constructor();        if (proto !== baseProto) {            Object.setPrototypeOf(newObj, proto);        }        return newObj;    }    return Object.create(proto);};
 |