All files / src/internal/client/dom/legacy misc.js

55.19% Statements 101/183
100% Branches 17/17
62.5% Functions 5/8
54.18% Lines 97/179

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 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 1802x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 8x 8x 8x 86x 26x 26x 86x 60x 60x 60x 8x 8x 2x 2x 2x 2x 2x 2x 2x 2x 62x 56x 62x 62x 62x 62x 62x 56x 56x 56x 62x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 809x 809x 437x 809x 372x 372x 809x 2x 2x 2x 2x 2x               2x 2x 2x 2x 2x                                                                                                                                       2x 2x 2x 2x 2x 2x                  
import { set, source } from '../../reactivity/sources.js';
import { get } from '../../runtime.js';
import { is_array } from '../../../shared/utils.js';
import { invalid_default_snippet } from '../../../shared/errors.js';
 
/**
 * Under some circumstances, imports may be reactive in legacy mode. In that case,
 * they should be using `reactive_import` as part of the transformation
 * @param {() => any} fn
 */
export function reactive_import(fn) {
	var s = source(0);
 
	return function () {
		if (arguments.length === 1) {
			set(s, get(s) + 1);
			return arguments[0];
		} else {
			get(s);
			return fn();
		}
	};
}
 
/**
 * @this {any}
 * @param {Record<string, unknown>} $$props
 * @param {Event} event
 * @returns {void}
 */
export function bubble_event($$props, event) {
	var events = /** @type {Record<string, Function[] | Function>} */ ($$props.$$events)?.[
		event.type
	];
 
	var callbacks = is_array(events) ? events.slice() : events == null ? [] : [events];
 
	for (var fn of callbacks) {
		// Preserve "this" context
		fn.call(this, event);
	}
}
 
/**
 * Used to simulate `$on` on a component instance when `compatibility.componentApi === 4`
 * @param {Record<string, any>} $$props
 * @param {string} event_name
 * @param {Function} event_callback
 */
export function add_legacy_event_listener($$props, event_name, event_callback) {
	$$props.$$events ||= {};
	$$props.$$events[event_name] ||= [];
	$$props.$$events[event_name].push(event_callback);
}
 
/**
 * Used to simulate `$set` on a component instance when `compatibility.componentApi === 4`.
 * Needs component accessors so that it can call the setter of the prop. Therefore doesn't
 * work for updating props in `$$props` or `$$restProps`.
 * @this {Record<string, any>}
 * @param {Record<string, any>} $$new_props
 */
export function update_legacy_props($$new_props) {
	for (var key in $$new_props) {
		if (key in this) {
			this[key] = $$new_props[key];
		}
	}
}
 
/**
 * @param {Record<string, any>} $$props
 */
export function default_slot($$props) {
	var children = $$props.$$slots?.default;
	if (children === true) {
		return $$props.children;
	} else {
		return children;
	}
}
 
 
/**
 * @param {Record<string, any>} $$slots
 */
function throw_error_on_slots($$slots) {
	if ($$slots) {
		for (const name of Object.getOwnPropertyNames($$slots)) {
			throw new Error(`Illegal slot "${name}"`);
		}
	}
}
 
/**
 * @param {Record<string, any>} $$props
 * @param {Record<string, (boolean | string | string[] | {prop: string, args: string[]})>} metadata
 */
function legacy_slots($$props, metadata) {
	for (const name of Object.getOwnPropertyNames($$props.$$slots)) {


		if (name === 'default' && typeof $$props.$$slots[name] !== 'function') {
			continue;
		}


		const meta = metadata[name] ?? false;

		/** @type {string} */
		let prop = name === 'default' ? 'children' : name;
		/** @type {string[] | null} */
		let args = name === 'default' ? [] : null;
		switch (typeof meta) {
		case 'boolean':
			if (meta) {
				args = [];
			}
			break;
		case 'string':
			prop = meta;
			args = [];
			break;
		case 'object':
			if (Array.isArray(meta)) {
				args = meta;
			} else {
				prop = meta.prop;
				args = meta.args;
			}
		}


		if (args == null) {
			// no args : slot is invalid
			throw new Error(`Invalid slot="${name}"`);
		}

		if (prop in $$props) {
			if (name === 'default' && $$props['children'] === invalid_default_snippet) {
				// ok ?
			} else {
				// Conflict between slot and prop
				throw new Error(`Conflict between slot="${name}" and prop '${prop}'`);
			}
		}
		// TODO : warning ???
		
		const slot = $$props.$$slots[name];
		// @ts-ignore
		$$props[prop] = ($$anchor, ...params) => {
			/** @type {Record<string,any>} */
			const slot_props = {};
			args.forEach( (n, i) => {
				const get = params[i];
				if (get) {
					Object.defineProperty(slot_props, n, { get });
				}
			});
			slot($$anchor, slot_props);
		};

	}
	
}
 
/**
 * @param {Record<string, any>} $$props
 * @param {false | { slots?: any, events?: any}} metadata
 */
export function legacy($$props, metadata) {
	if ($$props.$$slots) {
		if (metadata === false || metadata.slots === false) {
			throw_error_on_slots($$props.$$slots);
		} else if (metadata.slots) {
			legacy_slots($$props, metadata.slots);
		}
	}
}