@feugene/mu
Version:
Helpful TS utilities without dependencies
79 lines • 2.78 kB
JavaScript
import isDate from '../is/isDate.mjs';
// Helpers
function isPlainObject(val) {
if (val === null || typeof val !== 'object')
return false;
const proto = Object.getPrototypeOf(val);
return proto === Object.prototype || proto === null;
}
function ownEnumerableKeys(obj) {
const keys = Object.keys(obj);
const symbols = Object.getOwnPropertySymbols(obj).filter(sym => {
const desc = Object.getOwnPropertyDescriptor(obj, sym);
return !!desc?.enumerable;
});
return keys.concat(symbols);
}
/**
* Deep clone with Node 22 strategy.
* - Plain objects and arrays: recursively clone over own enumerable string and symbol keys only.
* - Date: cloned by value via new Date(time).
* - DOM nodes (when cloneDom=true): uses node.cloneNode(true).
* - Other objects (Map, Set, RegExp, TypedArrays, ArrayBuffer, URL, etc.):
* attempted via structuredClone; on failure, return original reference.
* - Functions/classes/instances with custom prototypes: returned as-is (by reference).
*
* Limitations:
* - Accessors/descriptors are not preserved; enumerable data properties only for plain objects.
* - Prototypes for plain objects are normalized to Object.prototype (or null if origin had null-proto).
*/
export default function clone(item, cloneDom = true) {
// Nullish or primitive
if (item == null || (typeof item !== 'object' && typeof item !== 'function')) {
return item;
}
// DOM Node clone (duck-typed)
// @ts-ignore
if (cloneDom && item.nodeType && typeof item.cloneNode === 'function') {
// @ts-ignore
return item.cloneNode(true);
}
// Date
if (isDate(item)) {
// @ts-ignore
return new Date(item.getTime());
}
// Array
if (Array.isArray(item)) {
const src = item;
const out = new Array(src.length);
for (let i = 0; i < src.length; i++) {
out[i] = clone(src[i], cloneDom);
}
return out;
}
// Plain Object
if (isPlainObject(item)) {
const src = item;
const out = Object.create(Object.getPrototypeOf(src));
for (const key of ownEnumerableKeys(src)) {
out[key] = clone(src[key], cloneDom);
}
return out;
}
// For other complex objects (Map, Set, RegExp, TypedArrays, etc.)
// try structuredClone if available; fall back to reference if it throws.
try {
// @ts-ignore Node 22 global
if (typeof structuredClone === 'function') {
// @ts-ignore
return structuredClone(item);
}
}
catch (_) {
// fallthrough to return original item
}
// As-is for functions/classes and unsupported cases
return item;
}
//# sourceMappingURL=clone.mjs.map