All files / src/internal/client/dom/blocks snippet.js

100% Statements 117/117
100% Branches 17/17
100% Functions 3/3
100% Lines 108/108

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 1092x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 93x 93x 93x 93x 93x 93x 93x 93x 93x 111x 107x 111x 14x 14x 14x 107x 107x 107x 107x 93x 93x 93x 44x 44x 93x 2x 2x 2x 2x 2x 2x 2x 2x 37x 36x 36x 36x 36x 36x 36x 36x 36x 37x 37x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 8x 8x 8x 8x 8x 8x 8x 8x 4x 4x 4x 4x 4x 4x 4x 1x 1x 4x 4x 8x 8x 8x 8x 8x 2x 2x 8x 8x 8x  
/** @import { Snippet } from 'svelte' */
/** @import { Effect, TemplateNode } from '#client' */
/** @import { Getters } from '#shared' */
import { add_snippet_symbol } from '../../../shared/validate.js';
import { EFFECT_TRANSPARENT } from '../../constants.js';
import { branch, block, destroy_effect, teardown } from '../../reactivity/effects.js';
import {
	dev_current_component_function,
	set_dev_current_component_function
} from '../../runtime.js';
import { hydrate_next, hydrate_node, hydrating } from '../hydration.js';
import { create_fragment_from_html } from '../reconciler.js';
import { assign_nodes } from '../template.js';
import * as w from '../../warnings.js';
import { DEV } from 'esm-env';
 
/**
 * @template {(node: TemplateNode, ...args: any[]) => void} SnippetFn
 * @param {TemplateNode} node
 * @param {() => SnippetFn | null | undefined} get_snippet
 * @param {(() => any)[]} args
 * @returns {void}
 */
export function snippet(node, get_snippet, ...args) {
	var anchor = node;
 
	/** @type {SnippetFn | null | undefined} */
	var snippet;
 
	/** @type {Effect | null} */
	var snippet_effect;
 
	block(() => {
		if (snippet === (snippet = get_snippet())) return;
 
		if (snippet_effect) {
			destroy_effect(snippet_effect);
			snippet_effect = null;
		}
 
		if (snippet) {
			snippet_effect = branch(() => /** @type {SnippetFn} */ (snippet)(anchor, ...args));
		}
	}, EFFECT_TRANSPARENT);
 
	if (hydrating) {
		anchor = hydrate_node;
	}
}
 
/**
 * In development, wrap the snippet function so that it passes validation, and so that the
 * correct component context is set for ownership checks
 * @param {any} component
 * @param {(node: TemplateNode, ...args: any[]) => void} fn
 */
export function wrap_snippet(component, fn) {
	return add_snippet_symbol((/** @type {TemplateNode} */ node, /** @type {any[]} */ ...args) => {
		var previous_component_function = dev_current_component_function;
		set_dev_current_component_function(component);
 
		try {
			return fn(node, ...args);
		} finally {
			set_dev_current_component_function(previous_component_function);
		}
	});
}
 
/**
 * Create a snippet programmatically
 * @template {unknown[]} Params
 * @param {(...params: Getters<Params>) => {
 *   render: () => string
 *   setup?: (element: Element) => void
 * }} fn
 * @returns {Snippet<Params>}
 */
export function createRawSnippet(fn) {
	return add_snippet_symbol(
		(/** @type {TemplateNode} */ anchor, /** @type {Getters<Params>} */ ...params) => {
			var snippet = fn(...params);
 
			/** @type {Element} */
			var element;
 
			if (hydrating) {
				element = /** @type {Element} */ (hydrate_node);
				hydrate_next();
			} else {
				var html = snippet.render().trim();
				var fragment = create_fragment_from_html(html);
				element = /** @type {Element} */ (fragment.firstChild);
				if (DEV && (element.nextSibling !== null || element.nodeType !== 1)) {
					w.invalid_raw_snippet_render();
				}
				anchor.before(element);
			}
 
			const result = snippet.setup?.(element);
			assign_nodes(element, element);
 
			if (typeof result === 'function') {
				teardown(result);
			}
		}
	);
}