All files / src/internal/shared clone.js

86.3% Statements 63/73
89.47% Branches 17/19
100% Functions 2/2
85.71% Lines 60/70

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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 712x 2x 2x 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 185x       70x 70x 70x 185x               185x  
/** @import { Snapshot } from './types' */
import { DEV } from 'esm-env';
import * as w from './warnings.js';
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);
		}
	}
 
	if (value instanceof EventTarget) {
		// can't be cloned
		return /** @type {Snapshot<T>} */ (value);
	}
 
	try {
		return /** @type {Snapshot<T>} */ (structuredClone(value));
	} catch (e) {
		if (DEV) {
			w.state_snapshot_uncloneable();
			// eslint-disable-next-line no-console
			console.warn(e);
		}
		return /** @type {Snapshot<T>} */ (value);
	}
}