1- /** @import { Expression, ExpressionStatement, Identifier, MemberExpression, Statement } from 'estree' */
1+ /** @import { Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression, Statement } from 'estree' */
22/** @import { AST } from '#compiler' */
33/** @import { SourceLocation } from '#shared' */
44/** @import { ComponentClientTransformState, ComponentContext } from '../types' */
@@ -14,15 +14,15 @@ import { escape_html } from '../../../../../escaping.js';
1414import { dev , is_ignored , locator } from '../../../../state.js' ;
1515import { is_event_attribute , is_text_attribute } from '../../../../utils/ast.js' ;
1616import * as b from '../../../../utils/builders.js' ;
17- import { is_custom_element_node } from '../../../nodes.js' ;
17+ import { create_expression_metadata , is_custom_element_node } from '../../../nodes.js' ;
1818import { clean_nodes , determine_namespace_for_children } from '../../utils.js' ;
1919import { build_getter } from '../utils.js' ;
2020import {
2121 get_attribute_name ,
2222 build_attribute_value ,
23- build_class_directives ,
2423 build_style_directives ,
25- build_set_attributes
24+ build_set_attributes ,
25+ build_set_class
2626} from './shared/element.js' ;
2727import { process_children } from './shared/fragment.js' ;
2828import {
@@ -223,6 +223,8 @@ export function RegularElement(node, context) {
223223
224224 build_set_attributes (
225225 attributes ,
226+ class_directives ,
227+ style_directives ,
226228 context ,
227229 node ,
228230 node_id ,
@@ -270,13 +272,22 @@ export function RegularElement(node, context) {
270272 continue ;
271273 }
272274
275+ const name = get_attribute_name ( node , attribute ) ;
273276 if (
274277 ! is_custom_element &&
275278 ! cannot_be_set_statically ( attribute . name ) &&
276- ( attribute . value === true || is_text_attribute ( attribute ) )
279+ ( attribute . value === true || is_text_attribute ( attribute ) ) &&
280+ ( name !== 'class' || class_directives . length === 0 )
277281 ) {
278- const name = get_attribute_name ( node , attribute ) ;
279- const value = is_text_attribute ( attribute ) ? attribute . value [ 0 ] . data : true ;
282+ let value = is_text_attribute ( attribute ) ? attribute . value [ 0 ] . data : true ;
283+
284+ if ( name === 'class' && node . metadata . scoped && context . state . analysis . css . hash ) {
285+ if ( value === true || value === '' ) {
286+ value = context . state . analysis . css . hash ;
287+ } else {
288+ value += ' ' + context . state . analysis . css . hash ;
289+ }
290+ }
280291
281292 if ( name !== 'class' || value ) {
282293 context . state . template . push (
@@ -290,15 +301,23 @@ export function RegularElement(node, context) {
290301 continue ;
291302 }
292303
293- const is = is_custom_element
294- ? build_custom_element_attribute_update_assignment ( node_id , attribute , context )
295- : build_element_attribute_update_assignment ( node , node_id , attribute , attributes , context ) ;
304+ const is =
305+ is_custom_element && name !== 'class'
306+ ? build_custom_element_attribute_update_assignment ( node_id , attribute , context )
307+ : build_element_attribute_update_assignment (
308+ node ,
309+ node_id ,
310+ attribute ,
311+ attributes ,
312+ class_directives ,
313+ style_directives ,
314+ context
315+ ) ;
296316 if ( is ) is_attributes_reactive = true ;
297317 }
298318 }
299319
300- // class/style directives must be applied last since they could override class/style attributes
301- build_class_directives ( class_directives , node_id , context , is_attributes_reactive ) ;
320+ // style directives must be applied last since they could override class/style attributes
302321 build_style_directives ( style_directives , node_id , context , is_attributes_reactive ) ;
303322
304323 if (
@@ -492,6 +511,23 @@ function setup_select_synchronization(value_binding, context) {
492511 ) ;
493512}
494513
514+ /**
515+ * @param {AST.ClassDirective[] } class_directives
516+ * @param {ComponentContext } context
517+ * @return {ObjectExpression }
518+ */
519+ export function build_class_directives_object ( class_directives , context ) {
520+ let properties = [ ] ;
521+ for ( const d of class_directives ) {
522+ let expression = /** @type Expression */ ( context . visit ( d . expression ) ) ;
523+ if ( d . metadata . expression . has_call ) {
524+ expression = get_expression_id ( context . state , expression ) ;
525+ }
526+ properties . push ( b . init ( d . name , expression ) ) ;
527+ }
528+ return b . object ( properties ) ;
529+ }
530+
495531/**
496532 * Serializes an assignment to an element property by adding relevant statements to either only
497533 * the init or the the init and update arrays, depending on whether or not the value is dynamic.
@@ -518,6 +554,8 @@ function setup_select_synchronization(value_binding, context) {
518554 * @param {Identifier } node_id
519555 * @param {AST.Attribute } attribute
520556 * @param {Array<AST.Attribute | AST.SpreadAttribute> } attributes
557+ * @param {AST.ClassDirective[] } class_directives
558+ * @param {AST.StyleDirective[] } style_directives
521559 * @param {ComponentContext } context
522560 * @returns {boolean }
523561 */
@@ -526,6 +564,8 @@ function build_element_attribute_update_assignment(
526564 node_id ,
527565 attribute ,
528566 attributes ,
567+ class_directives ,
568+ style_directives ,
529569 context
530570) {
531571 const state = context . state ;
@@ -564,19 +604,15 @@ function build_element_attribute_update_assignment(
564604 let update ;
565605
566606 if ( name === 'class' ) {
567- if ( attribute . metadata . needs_clsx ) {
568- value = b . call ( '$.clsx' , value ) ;
569- }
570-
571- update = b . stmt (
572- b . call (
573- is_svg ? '$.set_svg_class' : is_mathml ? '$.set_mathml_class' : '$.set_class' ,
574- node_id ,
575- value ,
576- attribute . metadata . needs_clsx && context . state . analysis . css . hash
577- ? b . literal ( context . state . analysis . css . hash )
578- : undefined
579- )
607+ return build_set_class (
608+ element ,
609+ node_id ,
610+ attribute ,
611+ value ,
612+ has_state ,
613+ class_directives ,
614+ context ,
615+ ! is_svg && ! is_mathml
580616 ) ;
581617 } else if ( name === 'value' ) {
582618 update = b . stmt ( b . call ( '$.set_value' , node_id , value ) ) ;
@@ -640,14 +676,6 @@ function build_custom_element_attribute_update_assignment(node_id, attribute, co
640676 const name = attribute . name ; // don't lowercase, as we set the element's property, which might be case sensitive
641677 let { value, has_state } = build_attribute_value ( attribute . value , context ) ;
642678
643- // We assume that noone's going to redefine the semantics of the class attribute on custom elements, i.e. it's still used for CSS classes
644- if ( name === 'class' && attribute . metadata . needs_clsx ) {
645- if ( context . state . analysis . css . hash ) {
646- value = b . array ( [ value , b . literal ( context . state . analysis . css . hash ) ] ) ;
647- }
648- value = b . call ( '$.clsx' , value ) ;
649- }
650-
651679 const update = b . stmt ( b . call ( '$.set_custom_element_data' , node_id , b . literal ( name ) , value ) ) ;
652680
653681 if ( has_state ) {
0 commit comments