function build_attribute_effect(
	attributes,
	class_directives,
	style_directives,
	context,
	element,
	element_id
) {
	/** @type {ObjectExpression['properties']} */
	const values = [];

	/** @type {Expression[]} */
	const expressions = [];

	/** @param {Expression} value */
	function memoize(value) {
		return b.id(`$${expressions.push(value) - 1}`);
	}

	for (const attribute of attributes) {
		if (attribute.type === 'Attribute') {
			const { value } = build_attribute_value(attribute.value, context, (value, metadata) =>
				metadata.has_call ? memoize(value) : value
			);

			if (
				is_event_attribute(attribute) &&
				(value.type === 'ArrowFunctionExpression' || value.type === 'FunctionExpression')
			) {
				// Give the event handler a stable ID so it isn't removed and readded on every update
				const id = context.state.scope.generate('event_handler');
				context.state.init.push(b.var(id, value));
				values.push(b.init(attribute.name, b.id(id)));
			} else {
				values.push(b.init(attribute.name, value));
			}
		} else {
			let value = /** @type {Expression} */ (context.visit(attribute));

			if (attribute.metadata.expression.has_call) {
				value = memoize(value);
			}

			values.push(b.spread(value));
		}
	}

	if (class_directives.length) {
		values.push(
			b.prop(
				'init',
				b.array([b.id('$.CLASS')]),
				build_class_directives_object(class_directives, expressions, context)
			)
		);
	}

	if (style_directives.length) {
		values.push(
			b.prop(
				'init',
				b.array([b.id('$.STYLE')]),
				build_style_directives_object(style_directives, expressions, context)
			)
		);
	}

	context.state.init.push(
		b.stmt(
			b.call(
				'$.attribute_effect',
				element_id,
				b.arrow(
					expressions.map((_, i) => b.id(`$${i}`)),
					b.object(values)
				),
				expressions.length > 0 && b.array(expressions.map((expression) => b.thunk(expression))),
				element.metadata.scoped &&
					context.state.analysis.css.hash !== '' &&
					b.literal(context.state.analysis.css.hash),
				is_ignored(element, 'hydration_attribute_changed') && b.true
			)
		)
	);
}
@param {Array<AST.Attribute | AST.SpreadAttribute>} attributes
@param {AST.ClassDirective[]} class_directives
@param {AST.StyleDirective[]} style_directives
@param {ComponentContext} context
@param {AST.RegularElement | AST.SvelteElement} element
@param {Identifier} element_id