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' */
@@ -20,9 +20,9 @@ import { 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,13 +223,13 @@ export function RegularElement(node, context) {
223223
224224 build_set_attributes (
225225 attributes ,
226+ class_directives ,
226227 context ,
227228 node ,
228229 node_id ,
229230 attributes_id ,
230231 ( node . metadata . svg || node . metadata . mathml || is_custom_element_node ( node ) ) && b . true ,
231- is_custom_element_node ( node ) && b . true ,
232- context . state
232+ is_custom_element_node ( node ) && b . true
233233 ) ;
234234
235235 // If value binding exists, that one takes care of calling $.init_select
@@ -270,13 +270,22 @@ export function RegularElement(node, context) {
270270 continue ;
271271 }
272272
273+ const name = get_attribute_name ( node , attribute ) ;
273274 if (
274275 ! is_custom_element &&
275276 ! cannot_be_set_statically ( attribute . name ) &&
276- ( attribute . value === true || is_text_attribute ( attribute ) )
277+ ( attribute . value === true || is_text_attribute ( attribute ) ) &&
278+ ( name !== 'class' || class_directives . length === 0 )
277279 ) {
278- const name = get_attribute_name ( node , attribute ) ;
279- const value = is_text_attribute ( attribute ) ? attribute . value [ 0 ] . data : true ;
280+ let value = is_text_attribute ( attribute ) ? attribute . value [ 0 ] . data : true ;
281+
282+ if ( name === 'class' && node . metadata . scoped && context . state . analysis . css . hash ) {
283+ if ( value === true || value === '' ) {
284+ value = context . state . analysis . css . hash ;
285+ } else {
286+ value += ' ' + context . state . analysis . css . hash ;
287+ }
288+ }
280289
281290 if ( name !== 'class' || value ) {
282291 context . state . template . push (
@@ -290,15 +299,22 @@ export function RegularElement(node, context) {
290299 continue ;
291300 }
292301
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 ) ;
302+ const is =
303+ is_custom_element && name !== 'class'
304+ ? build_custom_element_attribute_update_assignment ( node_id , attribute , context )
305+ : build_element_attribute_update_assignment (
306+ node ,
307+ node_id ,
308+ attribute ,
309+ attributes ,
310+ class_directives ,
311+ context
312+ ) ;
296313 if ( is ) is_attributes_reactive = true ;
297314 }
298315 }
299316
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 ) ;
317+ // style directives must be applied last since they could override class/style attributes
302318 build_style_directives ( style_directives , node_id , context , is_attributes_reactive ) ;
303319
304320 if (
@@ -491,6 +507,27 @@ function setup_select_synchronization(value_binding, context) {
491507 ) ;
492508}
493509
510+ /**
511+ * @param {AST.ClassDirective[] } class_directives
512+ * @param {ComponentContext } context
513+ * @return {ObjectExpression }
514+ */
515+ export function build_class_directives_object ( class_directives , context ) {
516+ let properties = [ ] ;
517+
518+ for ( const d of class_directives ) {
519+ let expression = /** @type Expression */ ( context . visit ( d . expression ) ) ;
520+
521+ if ( d . metadata . expression . has_call ) {
522+ expression = get_expression_id ( context . state , expression ) ;
523+ }
524+
525+ properties . push ( b . init ( d . name , expression ) ) ;
526+ }
527+
528+ return b . object ( properties ) ;
529+ }
530+
494531/**
495532 * Serializes an assignment to an element property by adding relevant statements to either only
496533 * the init or the the init and update arrays, depending on whether or not the value is dynamic.
@@ -517,6 +554,7 @@ function setup_select_synchronization(value_binding, context) {
517554 * @param {Identifier } node_id
518555 * @param {AST.Attribute } attribute
519556 * @param {Array<AST.Attribute | AST.SpreadAttribute> } attributes
557+ * @param {AST.ClassDirective[] } class_directives
520558 * @param {ComponentContext } context
521559 * @returns {boolean }
522560 */
@@ -525,6 +563,7 @@ function build_element_attribute_update_assignment(
525563 node_id ,
526564 attribute ,
527565 attributes ,
566+ class_directives ,
528567 context
529568) {
530569 const state = context . state ;
@@ -563,19 +602,15 @@ function build_element_attribute_update_assignment(
563602 let update ;
564603
565604 if ( name === 'class' ) {
566- if ( attribute . metadata . needs_clsx ) {
567- value = b . call ( '$.clsx' , value ) ;
568- }
569-
570- update = b . stmt (
571- b . call (
572- is_svg ? '$.set_svg_class' : is_mathml ? '$.set_mathml_class' : '$.set_class' ,
573- node_id ,
574- value ,
575- attribute . metadata . needs_clsx && context . state . analysis . css . hash
576- ? b . literal ( context . state . analysis . css . hash )
577- : undefined
578- )
605+ return build_set_class (
606+ element ,
607+ node_id ,
608+ attribute ,
609+ value ,
610+ has_state ,
611+ class_directives ,
612+ context ,
613+ ! is_svg && ! is_mathml
579614 ) ;
580615 } else if ( name === 'value' ) {
581616 update = b . stmt ( b . call ( '$.set_value' , node_id , value ) ) ;
@@ -639,14 +674,6 @@ function build_custom_element_attribute_update_assignment(node_id, attribute, co
639674 const name = attribute . name ; // don't lowercase, as we set the element's property, which might be case sensitive
640675 let { value, has_state } = build_attribute_value ( attribute . value , context ) ;
641676
642- // 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
643- if ( name === 'class' && attribute . metadata . needs_clsx ) {
644- if ( context . state . analysis . css . hash ) {
645- value = b . array ( [ value , b . literal ( context . state . analysis . css . hash ) ] ) ;
646- }
647- value = b . call ( '$.clsx' , value ) ;
648- }
649-
650677 const update = b . stmt ( b . call ( '$.set_custom_element_data' , node_id , b . literal ( name ) , value ) ) ;
651678
652679 if ( has_state ) {
0 commit comments