All files / src/compiler/phases/3-transform/server/visitors RegularElement.js

100% Statements 110/110
100% Branches 19/19
100% Functions 1/1
100% Lines 104/104

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 1052x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 4054x 4054x 4054x 4054x 4054x 4054x 4054x 4054x 4054x 4054x 4054x 4054x 4054x 4054x 4054x 6x 6x 6x 6x 6x 6x 6x 4048x 4048x 4048x 4048x 4048x 4048x 4048x 4048x 4048x 4048x 4048x 4048x 4048x 4048x 4054x 10x 10x 4048x 4054x 117x 117x 117x 117x 117x 117x 117x 117x 117x 117x 117x 117x 117x 4048x 4053x 4029x 4054x 19x 19x 19x 13x 13x 13x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 19x 4048x 4054x 3715x 3715x 4048x 4054x 117x 117x 4054x  
/** @import { Location } from 'locate-character' */
/** @import { AST } from '#compiler' */
/** @import { ComponentContext, ComponentServerTransformState } from '../types.js' */
/** @import { Scope } from '../../../scope.js' */
import { is_void } from '../../../../../utils.js';
import { dev, locator } from '../../../../state.js';
import * as b from '../../../../utils/builders.js';
import { clean_nodes, determine_namespace_for_children } from '../../utils.js';
import { build_element_attributes } from './shared/element.js';
import { process_children, build_template } from './shared/utils.js';
 
/**
 * @param {AST.RegularElement} node
 * @param {ComponentContext} context
 */
export function RegularElement(node, context) {
	const namespace = determine_namespace_for_children(node, context.state.namespace);
 
	/** @type {ComponentServerTransformState} */
	const state = {
		...context.state,
		namespace,
		preserve_whitespace:
			context.state.preserve_whitespace || node.name === 'pre' || node.name === 'textarea'
	};
 
	context.state.template.push(b.literal(`<${node.name}`));
	const body = build_element_attributes(node, { ...context, state });
	context.state.template.push(b.literal('>'));
 
	if ((node.name === 'script' || node.name === 'style') && node.fragment.nodes.length === 1) {
		context.state.template.push(
			b.literal(/** @type {AST.Text} */ (node.fragment.nodes[0]).data),
			b.literal(`</${node.name}>`)
		);
 
		return;
	}
 
	const { hoisted, trimmed } = clean_nodes(
		node,
		node.fragment.nodes,
		context.path,
		namespace,
		{
			...state,
			scope: /** @type {Scope} */ (state.scopes.get(node.fragment))
		},
		state.preserve_whitespace,
		state.options.preserveComments
	);
 
	for (const node of hoisted) {
		context.visit(node, state);
	}
 
	if (dev) {
		const location = /** @type {Location} */ (locator(node.start));
		state.template.push(
			b.stmt(
				b.call(
					'$.push_element',
					b.id('$$payload'),
					b.literal(node.name),
					b.literal(location.line),
					b.literal(location.column)
				)
			)
		);
	}
 
	if (body === null) {
		process_children(trimmed, { ...context, state });
	} else {
		let id = body;
 
		if (body.type !== 'Identifier') {
			id = b.id(state.scope.generate('$$body'));
			state.template.push(b.const(id, body));
		}
 
		// if this is a `<textarea>` value or a contenteditable binding, we only add
		// the body if the attribute/binding is falsy
		const inner_state = { ...state, template: [], init: [] };
		process_children(trimmed, { ...context, state: inner_state });
 
		// Use the body expression as the body if it's truthy, otherwise use the inner template
		state.template.push(
			b.if(
				id,
				b.block(build_template([id])),
				b.block([...inner_state.init, ...build_template(inner_state.template)])
			)
		);
	}
 
	if (!is_void(node.name)) {
		state.template.push(b.literal(`</${node.name}>`));
	}
 
	if (dev) {
		state.template.push(b.stmt(b.call('$.pop_element')));
	}
}