Skip to content

Commit

Permalink
chore: memoize clsx() (alternative) (#15456)
Browse files Browse the repository at this point in the history
* memoize clsx + directives

* changeset

* unused

* tweak

* tweak changeset

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
  • Loading branch information
adiguba and Rich-Harris authored Mar 6, 2025
1 parent 30562b8 commit ae615ae
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 16 deletions.
5 changes: 5 additions & 0 deletions .changeset/flat-jars-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

fix: memoize `clsx` calls
Original file line number Diff line number Diff line change
Expand Up @@ -511,22 +511,21 @@ function setup_select_synchronization(value_binding, context) {
/**
* @param {AST.ClassDirective[]} class_directives
* @param {ComponentContext} context
* @return {ObjectExpression}
* @return {ObjectExpression | Identifier}
*/
export function build_class_directives_object(class_directives, context) {
let properties = [];
let has_call_or_state = false;

for (const d of class_directives) {
let expression = /** @type Expression */ (context.visit(d.expression));

if (d.metadata.expression.has_call) {
expression = get_expression_id(context.state, expression);
}

const expression = /** @type Expression */ (context.visit(d.expression));
properties.push(b.init(d.name, expression));
has_call_or_state ||= d.metadata.expression.has_call || d.metadata.expression.has_state;
}

return b.object(properties);
const directives = b.object(properties);

return has_call_or_state ? get_expression_id(context.state, directives) : directives;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,21 +162,21 @@ export function get_attribute_name(element, attribute) {
* @param {boolean} is_html
*/
export function build_set_class(element, node_id, attribute, class_directives, context, is_html) {
let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) =>
metadata.has_call ? get_expression_id(context.state, value) : value
);
let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => {
if (attribute.metadata.needs_clsx) {
value = b.call('$.clsx', value);
}

if (attribute && attribute.metadata.needs_clsx) {
value = b.call('$.clsx', value);
}
return metadata.has_call ? get_expression_id(context.state, value) : value;
});

/** @type {Identifier | undefined} */
let previous_id;

/** @type {ObjectExpression | Identifier | undefined} */
let prev;

/** @type {ObjectExpression | undefined} */
/** @type {ObjectExpression | Identifier | undefined} */
let next;

if (class_directives.length) {
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte/src/internal/client/dom/elements/class.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function set_class(dom, is_html, value, hash, prev_classes, next_classes)

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

Expand Down

0 comments on commit ae615ae

Please # to comment.