All files / src/internal/shared clone.js

100% Statements 55/55
100% Branches 16/16
100% Functions 2/2
100% Lines 54/54

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 552x 2x 2x 2x 2x 2x 2x 2x 2x 60x 60x 2x 2x 2x 2x 2x 2x 2x 185x 185x 117x 117x 112x 117x 71x 71x 71x 71x 79x 79x 71x 71x 71x 41x 43x 38x 38x 38x 38x 38x 45x 45x 45x 38x 38x 38x 3x 11x 1x 1x 117x 70x 70x 70x  
/** @import { Snapshot } from './types' */
import { get_prototype_of, is_array, object_prototype } from './utils.js';
 
/**
 * @template T
 * @param {T} value
 * @returns {Snapshot<T>}
 */
export function snapshot(value) {
	return clone(value, new Map());
}
 
/**
 * @template T
 * @param {T} value
 * @param {Map<T, Snapshot<T>>} cloned
 * @returns {Snapshot<T>}
 */
function clone(value, cloned) {
	if (typeof value === 'object' && value !== null) {
		const unwrapped = cloned.get(value);
		if (unwrapped !== undefined) return unwrapped;
 
		if (is_array(value)) {
			const copy = /** @type {Snapshot<any>} */ ([]);
			cloned.set(value, copy);
 
			for (const element of value) {
				copy.push(clone(element, cloned));
			}
 
			return copy;
		}
 
		if (get_prototype_of(value) === object_prototype) {
			/** @type {Snapshot<any>} */
			const copy = {};
			cloned.set(value, copy);
 
			for (var key in value) {
				// @ts-expect-error
				copy[key] = clone(value[key], cloned);
			}
 
			return copy;
		}
 
		if (typeof (/** @type {T & { toJSON?: any } } */ (value).toJSON) === 'function') {
			return clone(/** @type {T & { toJSON(): any } } */ (value).toJSON(), cloned);
		}
	}
 
	return /** @type {Snapshot<T>} */ (structuredClone(value));
}