UNPKG

@n3okill/utils

Version:
149 lines 6.4 kB
/* global NodeJS */ import { _checkTransform } from "../_internal"; import { cloneBuffer } from "../other/cloneBuffer"; import { cloneDate } from "../other/cloneDate"; import { cloneError } from "../other/cloneError"; import { clonePrimitive } from "../other/clonePrimitive"; import { cloneRegExp } from "../other/cloneRegExp"; import { cloneSymbol } from "../other/cloneSymbol"; import { cloneTypedArray } from "../other/cloneTypedArray"; import { EnumTypes } from "../type/_types"; import { getEnumType } from "../type/getEnumType"; import { isArray } from "../type/isArray"; import { isMap } from "../type/isMap"; import { isObject } from "../type/isObject"; import { isPlainObject } from "../type/isPlainObject"; import { isSet } from "../type/isSet"; /** * Clone any argument type * @param arg The argument to clone * @param deep If true will deep clone values in multiple argument types * @returns New cloned object */ export function clone(source, deep = false, transform) { const parentStack = []; const parentCloned = []; const _clone = (_source) => { let target; const parentIndex = parentStack.indexOf(_source); if (parentIndex !== -1) { // eslint-disable-next-line security/detect-object-injection return parentCloned[parentIndex]; } switch (getEnumType(_source)) { case EnumTypes.Array: target = new (_source.constructor || Array)(_source.length); break; case EnumTypes.Map: target = new (_source.constructor || Map)(); break; case EnumTypes.Set: target = new (_source.constructor || Set)(); break; case EnumTypes.Promise: target = new _source.constructor( // eslint-disable-next-line @typescript-eslint/no-explicit-any (resolve, reject) => { _source.then((value) => resolve(_checkTransform(_clone(value), transform)), (err) => reject(_checkTransform(_clone(err), transform))); }); break; case EnumTypes.Object: case EnumTypes.PlainObject: if (isPlainObject(_source) || !("constructor" in _source)) { target = {}; } else { target = new _source.constructor(); } break; case EnumTypes.Buffer: return cloneBuffer(_source, transform); case EnumTypes.Date: return cloneDate(_source, transform); case EnumTypes.Error: target = cloneError(_source, transform); break; case EnumTypes.RegExp: return cloneRegExp(_source, transform); case EnumTypes.TypedArray: return cloneTypedArray(_source, transform); case EnumTypes.Function: case EnumTypes.AsyncFunction: return _source.bind(null); case EnumTypes.Symbol: return cloneSymbol(_source, transform); case EnumTypes.Boolean: case EnumTypes.Number: case EnumTypes.String: return clonePrimitive(_source, transform); case EnumTypes.Null: case EnumTypes.Undefined: return _source; } parentStack.push(_source); parentCloned.push(target); if (isArray(_source)) { let length = _source.length; while (length--) { // eslint-disable-next-line security/detect-object-injection target[length] = _checkTransform( // eslint-disable-next-line security/detect-object-injection deep ? _clone(_source[length]) : _source[length], transform, length); } } else if (isMap(_source)) { for (const [key, value] of _source.entries()) { target.set(key, _checkTransform(deep ? _clone(value) : value, transform, key)); } } else if (isSet(_source)) { for (const val of _source.values()) { target.add(_checkTransform(deep ? _clone(val) : val, transform)); } } const cloneDescriptor = (original, name, cloneObj) => { if (!(name in cloneObj) || cloneObj[name] !== original[name]) { const descriptor = Object.getOwnPropertyDescriptor(original, name); const desc = {}; desc.writable = descriptor.writable === true; desc.configurable = descriptor.configurable === true; desc.enumerable = descriptor.enumerable === true; if (descriptor.set) { // eslint-disable-next-line @typescript-eslint/unbound-method desc.set = (deep ? _clone(descriptor.set) : descriptor.set); delete desc.writable; } if (descriptor.get) { // eslint-disable-next-line @typescript-eslint/unbound-method desc.get = (deep ? _clone(descriptor.get) : descriptor.get); delete desc.writable; } else { desc.value = (deep ? _checkTransform(_clone(descriptor.value), transform, name) : descriptor.value); } try { Object.defineProperty(cloneObj, name, desc); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (err) { throw new Error(`Error defining descriptor for '${name}'`); } } }; const ownPropertyNames = Object.getOwnPropertyNames(_source); for (const name of ownPropertyNames) { cloneDescriptor(_source, name, target); } const ownPropertySymbols = Object.getOwnPropertySymbols(_source); for (const symbol of ownPropertySymbols) { cloneDescriptor(_source, symbol, target); } if (isObject(_source) || isPlainObject(_source)) { target = _checkTransform(target, transform); } return target; }; return _clone(source); } //# sourceMappingURL=clone.js.map