@n3okill/utils
Version:
Many javascript helpers
149 lines • 6.4 kB
JavaScript
/* 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