Repository: withastro/astro
File: packages/astro/src/runtime/server/render/util.ts (TypeScript)

Code before:
	if (values.length === 1) {
		return values[0];
	}
	return `${values.slice(0, -1).join(', ')} or ${values[values.length - 1]}`;
}

function isCustomElement(tagName: string) {
	return tagName.includes('-');
}

function handleBooleanAttribute(
	key: string,
	value: any,
	shouldEscape: boolean,
	tagName?: string
): string {
	// For custom elements, always render as string attributes
	if (tagName && isCustomElement(tagName)) {
		return markHTMLString(` ${key}="${toAttributeString(value, shouldEscape)}"`);
	}
	// For regular HTML elements, use boolean attribute logic
	return markHTMLString(value ? ` ${key}` : '');
}

// A helper used to turn expressions into attribute key/value
export function addAttribute(value: any, key: string, shouldEscape = true, tagName: string='') {
	if (value == null) {
		return '';
	}

	

	// compiler directives cannot be applied dynamically, log a warning and ignore.
	if (STATIC_DIRECTIVES.has(key)) {
		console.warn(`[astro] The "${key}" directive cannot be applied dynamically at runtime. It will not be rendered as an attribute.

Make sure to use the static attribute syntax (\`${key}={value}\`) instead of the dynamic spread syntax (\`{...{ "${key}": value }}\`).`);
		return '';
	}

	// support "class" from an expression passed into an element (#782)
	if (key === 'class:list') {
		const listValue = toAttributeString(clsx(value), shouldEscape);
		if (listValue === '') {
			return '';
		}
		return markHTMLString(` ${key.slice(0, -5)}="${listValue}"`);
	}

	// support object styles for better JSX compat
	if (key === 'style' && !(value instanceof HTMLString)) {

Review comment (security):
```suggestion
// In the compiler, addAttribute is only printed to process attributes of elements
// that may contain dynamic values. We don't need to pass tagName to addAttribute
// on the compiler side because it is used only for custom elements
export function addAttribute(value: any, key: string, shouldEscape = true, tagName = '') {
```

Code after:
	if (values.length === 1) {
		return values[0];
	}
	return `${values.slice(0, -1).join(', ')} or ${values[values.length - 1]}`;
}

function isCustomElement(tagName: string) {
	return tagName.includes('-');
}

function handleBooleanAttribute(
	key: string,
	value: any,
	shouldEscape: boolean,
	tagName?: string
): string {
	// For custom elements, always render as string attributes
	if (tagName && isCustomElement(tagName)) {
		return markHTMLString(` ${key}="${toAttributeString(value, shouldEscape)}"`);
	}
	// For regular HTML elements, use boolean attribute logic
	return markHTMLString(value ? ` ${key}` : '');
}

// A helper used to turn expressions into attribute key/value
// In the compiler, addAttribute is only printed to process attributes of elements
// that may contain dynamic values. We don't need to pass tagName to addAttribute
// on the compiler side because it is used only for custom elements
export function addAttribute(value: any, key: string, shouldEscape = true, tagName = '') {
	if (value == null) {
		return '';
	}

	

	// compiler directives cannot be applied dynamically, log a warning and ignore.
	if (STATIC_DIRECTIVES.has(key)) {
		console.warn(`[astro] The "${key}" directive cannot be applied dynamically at runtime. It will not be rendered as an attribute.

Make sure to use the static attribute syntax (\`${key}={value}\`) instead of the dynamic spread syntax (\`{...{ "${key}": value }}\`).`);
		return '';
	}

	// support "class" from an expression passed into an element (#782)
	if (key === 'class:list') {
		const listValue = toAttributeString(clsx(value), shouldEscape);
		if (listValue === '') {
			return '';
		}
		return markHTMLString(` ${key.slice(0, -5)}="${listValue}"`);
	}