All files / src/internal/client/dev hmr.js

95.5% Statements 85/89
60% Branches 6/10
66.66% Functions 2/3
95.18% Lines 79/83

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 842x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 10x 10x 10x 4x 4x 4x 4x 6x 6x 6x 6x 6x 6x 6x 6x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x         16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 16x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x  
/** @import { Effect } from '#client' */
import { FILENAME, ORIGINAL } from '../../../constants.js';
import { EFFECT_TRANSPARENT } from '../constants.js';
import { block, branch, destroy_effect } from '../reactivity/effects.js';
import { set, source } from '../reactivity/sources.js';
import { set_should_intro } from '../render.js';
import { get } from '../runtime.js';
 
/**
 * For each original component, store a persistent reference to the HMR wrapper
 * @type {Map<string, {wrapper: any, update: (update: any) => void}>}
 */
const registry = new Map();
 
/**
 * @template {(anchor: Comment, props: any) => any} Component
 * @param {Component} original
 */
export function hmr(original) {
	let result = registry.get(/** @type {any} */ (original)[FILENAME]);
 
	if (result) {
		// update the reference to the original component as it's now updated
		result.wrapper[ORIGINAL] = original;
		return result;
	}
 
	const component_source = source(original);
 
	/**
	 * @param {Comment} anchor
	 * @param {any} props
	 */
	const wrapper = function (anchor, props) {
		let instance = {};
 
		/** @type {Effect} */
		let effect;
 
		let ran = false;
 
		block(() => {
			const component = get(component_source);
 
			if (effect) {
				// @ts-ignore
				for (var k in instance) delete instance[k];
				destroy_effect(effect);
			}
 
			effect = branch(() => {
				// when the component is invalidated, replace it without transitions
				if (ran) set_should_intro(false);
 
				// preserve getters/setters
				Object.defineProperties(
					instance,
					Object.getOwnPropertyDescriptors(
						// @ts-expect-error
						new.target ? new component(anchor, props) : component(anchor, props)
					)
				);
 
				if (ran) set_should_intro(true);
			});
		}, EFFECT_TRANSPARENT);
 
		ran = true;
 
		return instance;
	};
 
	// stash a reference to the original component to avoid adding more and more wrappers per HMR update
	wrapper[ORIGINAL] = original;
 
	result = {
		wrapper,
		update: (update) => set(component_source, update[ORIGINAL])
	};
	registry.set(/** @type {any} */ (original)[FILENAME], result);
 
	return result;
}