All files / src/internal/client validate.js

84.61% Statements 88/104
73.68% Branches 14/19
66.66% Functions 4/6
84.31% Lines 86/102

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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 1032x 2x 2x 2x 2x 2x 2x 2x       2x 2x 2x 2x 2x 2x 7x 1x 1x 7x 2x 2x 2x 2x 2x 2x 7x 7x 7x 7x     4x 7x 3x 3x 3x 3x     3x 7x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x   2x 2x 2x 8x 8x 2x 2x 2x 2x 2x 2x 6x 6x   2x 2x 2x 2x 2x 2x               2x 2x 2x 2x 2x 2x 75x 71x 63x 63x 63x 4x 4x 4x 4x 4x 63x 71x 71x  
import { untrack } from './runtime.js';
import { get_descriptor, is_array } from './utils.js';
 
/** regex of all html void element names */
const void_element_names =
	/^(?:area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/;
 
/** @param {string} tag */
function is_void(tag) {
	return void_element_names.test(tag) || tag.toLowerCase() === '!doctype';
}
 
/**
 * @param {any} store
 * @param {string} name
 */
export function validate_store(store, name) {
	if (store != null && typeof store.subscribe !== 'function') {
		throw new Error(`'${name}' is not a store with a 'subscribe' method`);
	}
}
 
/**
 * @param {() => any} component_fn
 * @returns {any}
 */
export function validate_dynamic_component(component_fn) {
	const error_message = 'this={...} of <svelte:component> should specify a Svelte component.';
	try {
		const instance = component_fn();
		if (instance !== undefined && typeof instance !== 'object') {
			throw new Error(error_message);
		}
		return instance;
	} catch (err) {
		const { message } = /** @type {Error} */ (err);
		if (typeof message === 'string' && message.indexOf('is not a function') !== -1) {
			throw new Error(error_message);
		} else {
			throw err;
		}
	}
}
 
/**
 * @param {() => any} collection
 * @param {(item: any, index: number) => string} key_fn
 * @returns {void}
 */
export function validate_each_keys(collection, key_fn) {
	const keys = new Map();
	const maybe_array = untrack(() => collection());
	const array = is_array(maybe_array)
		? maybe_array
		: maybe_array == null
			? []
			: Array.from(maybe_array);
	const length = array.length;
	for (let i = 0; i < length; i++) {
		const key = key_fn(array[i], i);
		if (keys.has(key)) {
			throw new Error(
				`Cannot have duplicate keys in a keyed each: Keys at index ${keys.get(
					key
				)} and ${i} with value '${array[i]}' are duplicates`
			);
		}
		keys.set(key, i);
	}
}
 
/**
 * @param {number} timeout
 * @returns {() => void}
 * */
export function loop_guard(timeout) {
	const start = Date.now();
	return () => {
		if (Date.now() - start > timeout) {
			throw new Error('Infinite loop detected');
		}
	};
}
 
/**
 * @param {Record<string, any>} $$props
 * @param {string[]} bindable
 */
export function validate_prop_bindings($$props, bindable) {
	for (const key in $$props) {
		if (!bindable.includes(key)) {
			var setter = get_descriptor($$props, key)?.set;
 
			if (setter) {
				throw new Error(
					`Cannot use bind:${key} on this component because the property was not declared as bindable. ` +
						`To mark a property as bindable, use the $bindable() rune like this: \`let { ${key} = $bindable() } = $props()\``
				);
			}
		}
	}
}