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

95.29% Statements 81/85
55.55% Branches 5/9
100% Functions 2/2
94.87% Lines 74/78

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 792x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 10x 10x 10x 10x 10x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x         21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 4x 4x 21x 21x 21x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x  
/** @import { Source, Effect, TemplateNode } from '#client' */
import { FILENAME, HMR } from '../../../constants.js';
import { EFFECT_TRANSPARENT } from '../constants.js';
import { hydrate_node, hydrating } from '../dom/hydration.js';
import { block, branch, destroy_effect } from '../reactivity/effects.js';
import { source } from '../reactivity/sources.js';
import { set_should_intro } from '../render.js';
import { get } from '../runtime.js';
 
/**
 * @template {(anchor: Comment, props: any) => any} Component
 * @param {Component} original
 * @param {() => Source<Component>} get_source
 */
export function hmr(original, get_source) {
	/**
	 * @param {TemplateNode} anchor
	 * @param {any} props
	 */
	function wrapper(anchor, props) {
		let instance = {};
 
		/** @type {Effect} */
		let effect;
 
		let ran = false;
 
		block(() => {
			const source = get_source();
			const component = get(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;
 
		if (hydrating) {
			anchor = hydrate_node;
		}
 
		return instance;
	}
 
	// @ts-expect-error
	wrapper[FILENAME] = original[FILENAME];
 
	// @ts-expect-error
	wrapper[HMR] = {
		// When we accept an update, we set the original source to the new component
		original,
		// The `get_source` parameter reads `wrapper[HMR].source`, but in the `accept`
		// function we always replace it with `previous[HMR].source`, which in practice
		// means we only ever update the original
		source: source(original)
	};
 
	return wrapper;
}