UNPKG

@qntm-code/utils

Version:

A collection of useful utility functions with associated TypeScript types. All functions have been unit tested.

149 lines (148 loc) 5.23 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.clone = void 0; const isPlainObject_js_1 = require("../type-predicates/isPlainObject.js"); const typeOf_js_1 = require("../type-predicates/typeOf.js"); /** * Recursively (deep) clones native types, like Object, Array, RegExp, Date, Map, Set, Symbol, Error as well as primitives. */ function clone(value, instanceClone = false) { switch ((0, typeOf_js_1.typeOf)(value)) { case typeOf_js_1.ValueType.object: { return cloneObjectDeep(value, instanceClone); } case typeOf_js_1.ValueType.map: { return cloneMapDeep(value, instanceClone); } case typeOf_js_1.ValueType.set: { return cloneSetDeep(value, instanceClone); } case typeOf_js_1.ValueType.array: case typeOf_js_1.ValueType.int8array: case typeOf_js_1.ValueType.uint8array: case typeOf_js_1.ValueType.uint8clampedarray: case typeOf_js_1.ValueType.int16array: case typeOf_js_1.ValueType.uint16array: case typeOf_js_1.ValueType.int32array: case typeOf_js_1.ValueType.uint32array: case typeOf_js_1.ValueType.float32array: case typeOf_js_1.ValueType.float64array: case typeOf_js_1.ValueType.bigint64array: case typeOf_js_1.ValueType.biguint64array: { return cloneArrayDeep(value, instanceClone); } default: { return cloneShallow(value); } } } exports.clone = clone; function cloneShallow(value) { switch ((0, typeOf_js_1.typeOf)(value)) { case typeOf_js_1.ValueType.buffer: { return cloneBuffer(value); } case typeOf_js_1.ValueType.symbol: { return cloneSymbol(value); } case typeOf_js_1.ValueType.error: { return Object.create(value); } case typeOf_js_1.ValueType.date: { return new Date(value); } case typeOf_js_1.ValueType.regexp: { return cloneRegExp(value); } case typeOf_js_1.ValueType.moment: { return value.clone(); } } return value; } function cloneObjectDeep(value, instanceClone) { if (typeof instanceClone === 'function') { return instanceClone(value); } if (instanceClone || (0, isPlainObject_js_1.isPlainObject)(value)) { const source = value; // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access const ctor = value.constructor; const cloned = (ctor === undefined ? Object.create(null) : new ctor()); // Only clone own enumerable properties (matches `isEqual` semantics) for (const key of Object.keys(source)) { cloned[key] = clone(source[key], instanceClone); } return cloned; } return value; } function cloneArrayDeep(value, instanceClone) { const length = value.length; // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access const cloned = new value.constructor(length); for (let i = 0; i < length; i++) { cloned[i] = clone(value[i], instanceClone); } return cloned; } function cloneMapDeep(value, instanceClone) { const cloned = new Map(); const nestedInstanceClone = instanceClone; for (const [key, item] of value.entries()) { const clonedKey = clone(key, nestedInstanceClone); const clonedValue = clone(item, nestedInstanceClone); cloned.set(clonedKey, clonedValue); } return cloned; } function cloneSetDeep(value, instanceClone) { const cloned = new Set(); const nestedInstanceClone = instanceClone; for (const item of value.values()) { cloned.add(clone(item, nestedInstanceClone)); } return cloned; } function cloneRegExp(value) { // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access const cloned = new value.constructor(value.source, value.flags); cloned.lastIndex = value.lastIndex; return cloned; } function cloneBuffer(value) { const length = value.length; // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const buffer = Buffer.allocUnsafe ? Buffer.allocUnsafe(length) : Buffer.from(length); value.copy(buffer); return buffer; } function cloneSymbol(value) { // Symbols are primitives and cannot be truly cloned; create a new symbol with the same description. const description = value.description; // Preserve well-known symbols (they cannot be recreated) const wellKnownSymbolKeys = [ 'asyncIterator', 'hasInstance', 'isConcatSpreadable', 'iterator', 'match', 'matchAll', 'replace', 'search', 'species', 'split', 'toPrimitive', 'toStringTag', 'unscopables', 'dispose', 'asyncDispose', ]; for (const key of wellKnownSymbolKeys) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access if (Symbol[key] === value) { return value; } } return Symbol(description); }