Skip to content

Commit 156a14c

Browse files
committed
memoize clsx + directives
1 parent 2d38184 commit 156a14c

File tree

4 files changed

+35
-23
lines changed

4 files changed

+35
-23
lines changed

packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression, Statement } from 'estree' */
2-
/** @import { AST } from '#compiler' */
2+
/** @import { AST, ExpressionMetadata } from '#compiler' */
33
/** @import { SourceLocation } from '#shared' */
44
/** @import { ComponentClientTransformState, ComponentContext } from '../types' */
55
/** @import { Scope } from '../../../scope' */
@@ -501,22 +501,23 @@ function setup_select_synchronization(value_binding, context) {
501501
/**
502502
* @param {AST.ClassDirective[]} class_directives
503503
* @param {ComponentContext} context
504-
* @return {ObjectExpression}
504+
* @return {ObjectExpression | Identifier}
505505
*/
506506
export function build_class_directives_object(class_directives, context) {
507507
let properties = [];
508508

509+
let has_call_or_state = false;
510+
509511
for (const d of class_directives) {
510512
let expression = /** @type Expression */ (context.visit(d.expression));
511-
512-
if (d.metadata.expression.has_call) {
513-
expression = get_expression_id(context.state, expression);
514-
}
515-
516513
properties.push(b.init(d.name, expression));
514+
has_call_or_state ||= d.metadata.expression.has_call || d.metadata.expression.has_state;
517515
}
518-
519-
return b.object(properties);
516+
let directives = b.object(properties);
517+
if (has_call_or_state) {
518+
return get_expression_id(context.state, directives);
519+
}
520+
return directives;
520521
}
521522

522523
/**
@@ -564,15 +565,21 @@ function build_element_attribute_update_assignment(
564565

565566
const is_autofocus = name === 'autofocus';
566567

567-
let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) =>
568-
metadata.has_call
569-
? // if it's autofocus we will not add this to a template effect so we don't want to get the expression id
570-
// but separately memoize the expression
571-
is_autofocus
572-
? memoize_expression(state, value)
573-
: get_expression_id(state, value)
574-
: value
575-
);
568+
/** @type {(value: Expression, metadata: ExpressionMetadata) => Expression} */
569+
let memoize;
570+
if (is_autofocus) {
571+
// if it's autofocus we will not add this to a template effect so we don't want to get the expression id
572+
// but separately memoize the expression
573+
memoize = (value, metadata) => (metadata.has_call ? memoize_expression(state, value) : value);
574+
} else if (attribute.metadata.needs_clsx && name === 'class') {
575+
// if class needs clsx() we do nothing here !
576+
// => The entire value will be memoized inside build_set_class()
577+
memoize = (value) => value;
578+
} else {
579+
memoize = (value, metadata) => (metadata.has_call ? get_expression_id(state, value) : value);
580+
}
581+
582+
let { value, has_state } = build_attribute_value(attribute.value, context, memoize);
576583

577584
if (is_autofocus) {
578585
state.init.push(b.stmt(b.call('$.autofocus', node_id, value)));

packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ export function SvelteElement(node, context) {
9090
let { value, has_state } = build_attribute_value(
9191
attributes[0].value,
9292
context,
93-
(value, metadata) => (metadata.has_call ? get_expression_id(context.state, value) : value)
93+
attributes[0].metadata.needs_clsx
94+
? (value) => value
95+
: (value, metadata) => (metadata.has_call ? get_expression_id(context.state, value) : value)
9496
);
9597

9698
is_attributes_reactive = build_set_class(

packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ export function get_attribute_name(element, attribute) {
189189
/**
190190
* @param {AST.RegularElement | AST.SvelteElement} element
191191
* @param {Identifier} node_id
192-
* @param {AST.Attribute | null} attribute
192+
* @param {AST.Attribute} attribute
193193
* @param {Expression} value
194194
* @param {boolean} has_state
195195
* @param {AST.ClassDirective[]} class_directives
@@ -207,8 +207,11 @@ export function build_set_class(
207207
context,
208208
is_html
209209
) {
210-
if (attribute && attribute.metadata.needs_clsx) {
210+
if (attribute.metadata.needs_clsx) {
211211
value = b.call('$.clsx', value);
212+
if (has_state) {
213+
value = get_expression_id(context.state, value);
214+
}
212215
}
213216

214217
/** @type {Identifier | undefined} */
@@ -217,7 +220,7 @@ export function build_set_class(
217220
/** @type {ObjectExpression | Identifier | undefined} */
218221
let prev;
219222

220-
/** @type {ObjectExpression | undefined} */
223+
/** @type {ObjectExpression | Identifier | undefined} */
221224
let next;
222225

223226
if (class_directives.length) {

packages/svelte/src/internal/client/dom/elements/class.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export function set_class(dom, is_html, value, hash, prev_classes, next_classes)
3333

3434
// @ts-expect-error need to add __className to patched prototype
3535
dom.__className = value;
36-
} else if (next_classes) {
36+
} else if (next_classes && prev_classes !== next_classes) {
3737
for (var key in next_classes) {
3838
var is_present = !!next_classes[key];
3939

0 commit comments

Comments
 (0)