All files / src/compiler/phases/3-transform/client/visitors IfBlock.js

100% Statements 69/69
100% Branches 18/18
100% Functions 1/1
100% Lines 68/68

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 692x 2x 2x 2x 2x 2x 2x 2x 2x 2x 335x 335x 335x 335x 335x 335x 335x 31x 31x 1x 1x 1x 1x 31x 333x 333x 333x 333x 333x 333x 333x 333x 333x 335x 102x 102x 102x 102x 102x 102x 333x 334x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 333x 333x 333x  
/** @import { BlockStatement, Expression } from 'estree' */
/** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */
import * as b from '../../../../utils/builders.js';
 
/**
 * @param {AST.IfBlock} node
 * @param {ComponentContext} context
 */
export function IfBlock(node, context) {
	const { state } = context;
 
	const consequent = /** @type {BlockStatement} */ (context.visit(node.consequent));
 
	// compile to if rather than $.if for non-reactive if statements
	// NOTE: this only handles expressions that are simple identifiers and doesn't handle else/elseif yet
	if (state.analysis.runes && node.test.type === 'Identifier' && !(node.alternate || node.elseif)) {
		const binding = state.scope.owner(node.test.name)?.declarations.get(node.test.name);
		if (binding?.kind === 'normal') {
			consequent.body.unshift(b.var(b.id('$$anchor'), state.node));
			state.init.push(b.if(node.test, consequent));
			return;
		}
	}
 
	state.template.push_quasi('<!>');
 
	const args = [
		state.node,
		b.thunk(/** @type {Expression} */ (context.visit(node.test))),
		b.arrow([b.id('$$anchor')], consequent)
	];
 
	if (node.alternate || node.elseif) {
		args.push(
			node.alternate
				? b.arrow([b.id('$$anchor')], /** @type {BlockStatement} */ (context.visit(node.alternate)))
				: b.literal(null)
		);
	}
 
	if (node.elseif) {
		// We treat this...
		//
		//   {#if x}
		//     ...
		//   {:else}
		//     {#if y}
		//       <div transition:foo>...</div>
		//     {/if}
		//   {/if}
		//
		// ...slightly differently to this...
		//
		//   {#if x}
		//     ...
		//   {:else if y}
		//     <div transition:foo>...</div>
		//   {/if}
		//
		// ...even though they're logically equivalent. In the first case, the
		// transition will only play when `y` changes, but in the second it
		// should play when `x` or `y` change — both are considered 'local'
		args.push(b.literal(true));
	}
 
	state.init.push(b.stmt(b.call('$.if', ...args)));
}