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

100% Statements 122/122
100% Branches 19/19
100% Functions 3/3
100% Lines 111/111

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 1152x 2x 2x 2x 2x       2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 104x 104x 104x 104x 104x 104x 104x 104x 104x 104x 124x 120x 124x 16x 16x 16x 120x 124x 2x 2x 118x 118x 104x 104x 104x 49x 49x 104x 2x 2x 2x 2x 2x 2x 2x 2x 43x 45x 45x 45x 45x 45x 45x 45x 45x 43x 43x 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 4x 1x 1x 4x 4x 4x 8x 8x 8x 8x 8x 2x 2x 8x 8x  
/** @import { Snippet } from 'svelte' */
/** @import { Effect, TemplateNode } from '#client' */
/** @import { Getters } from '#shared' */
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 * as e from '../../errors.js';
import { DEV } from 'esm-env';
import { get_first_child, get_next_sibling } from '../operations.js';
import { noop } from '../../../shared/utils.js';
 
/**
 * @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} */
	// @ts-ignore
	var snippet = noop;
 
	/** @type {Effect | null} */
	var snippet_effect;
 
	block(() => {
		if (snippet === (snippet = get_snippet())) return;
 
		if (snippet_effect) {
			destroy_effect(snippet_effect);
			snippet_effect = null;
		}
 
		if (DEV && snippet == null) {
			e.invalid_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 (/** @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 | (() => void)
 * }} fn
 * @returns {Snippet<Params>}
 */
export function createRawSnippet(fn) {
	// @ts-expect-error the types are a lie
	return (/** @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} */ (get_first_child(fragment));
 
			if (DEV && (get_next_sibling(element) !== 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);
		}
	};
}