Repository: sveltejs/svelte
File: packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js (JavaScript)

Code before:
	if (node.name.startsWith('on')) {
		mark_subtree_dynamic(context.path);
	}

	if (parent.type === 'RegularElement' && is_boolean_attribute(node.name.toLowerCase())) {
		node.metadata.expression.can_inline = false;
	}

	if (node.value !== true) {
		for (const chunk of get_attribute_chunks(node.value)) {
			if (chunk.type !== 'ExpressionTag') continue;

			if (
				chunk.expression.type === 'FunctionExpression' ||
				chunk.expression.type === 'ArrowFunctionExpression'
			) {
				continue;
			}

			node.metadata.expression.has_state ||= chunk.metadata.expression.has_state;
			node.metadata.expression.has_call ||= chunk.metadata.expression.has_call;
			node.metadata.expression.can_inline &&= chunk.metadata.expression.can_inline;

			if (!node.metadata.expression.can_inline) {
				mark_subtree_dynamic(context.path);
			}
		}

		if (is_event_attribute(node)) {
			const parent = context.path.at(-1);
			if (parent?.type === 'RegularElement' || parent?.type === 'SvelteElement') {
				context.state.analysis.uses_event_attributes = true;
			}

			const expression = get_attribute_expression(node);
			const delegated_event = get_delegated_event(node.name.slice(2), expression, context);

			if (delegated_event !== null) {
				if (delegated_event.hoisted) {
					delegated_event.function.metadata.hoisted = true;
				}

				node.metadata.delegated = delegated_event;
			}
		}
	}
}

/** @type {DelegatedEvent} */
const unhoisted = { hoisted: false };


Review comment (suggestion):
I actually think this belongs in the `MemberExpression` visitor — we don't need to treat attributes differently from text nodes AFAICT. commit incoming

Code after:
	if (node.name.startsWith('on')) {
		mark_subtree_dynamic(context.path);
	}

	if (parent.type === 'RegularElement' && is_boolean_attribute(node.name.toLowerCase())) {
		node.metadata.expression.can_inline = false;
	}

	if (node.value !== true) {
		for (const chunk of get_attribute_chunks(node.value)) {
			if (chunk.type !== 'ExpressionTag') continue;

			if (
				chunk.expression.type === 'FunctionExpression' ||
				chunk.expression.type === 'ArrowFunctionExpression'
			) {
				continue;
			}

			node.metadata.expression.has_state ||= chunk.metadata.expression.has_state;
			node.metadata.expression.has_call ||= chunk.metadata.expression.has_call;
			node.metadata.expression.can_inline &&= chunk.metadata.expression.can_inline;
		}

		if (is_event_attribute(node)) {
			const parent = context.path.at(-1);
			if (parent?.type === 'RegularElement' || parent?.type === 'SvelteElement') {
				context.state.analysis.uses_event_attributes = true;
			}

			const expression = get_attribute_expression(node);
			const delegated_event = get_delegated_event(node.name.slice(2), expression, context);

			if (delegated_event !== null) {
				if (delegated_event.hoisted) {
					delegated_event.function.metadata.hoisted = true;
				}

				node.metadata.delegated = delegated_event;
			}
		}
	}
}

/** @type {DelegatedEvent} */
const unhoisted = { hoisted: false };

/**
 * Checks if given event attribute can be delegated/hoisted and returns the corresponding info if so
 * @param {string} event_name
 * @param {Expression | null} handler