Skip to content

Commit 48d2e83

Browse files
committed
WIP
1 parent 45fa678 commit 48d2e83

File tree

11 files changed

+52
-7
lines changed

11 files changed

+52
-7
lines changed

packages/svelte/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@
142142
"acorn-typescript": "^1.4.13",
143143
"aria-query": "^5.3.1",
144144
"axobject-query": "^4.1.0",
145+
"clsx": "^2.1.1",
145146
"esm-env": "^1.0.0",
146147
"esrap": "^1.2.2",
147148
"is-reference": "^3.0.2",

packages/svelte/src/compiler/phases/2-analyze/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,8 @@ export function analyze_component(root, source, options) {
739739

740740
if (attribute.type !== 'Attribute') continue;
741741
if (attribute.name.toLowerCase() !== 'class') continue;
742+
// The dynamic class method appends the hash to the end of the class attribute on its own
743+
if (attribute.metadata.is_dynamic_class) continue outer;
742744

743745
class_attribute = attribute;
744746
}

packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ export function Attribute(node, context) {
2424
}
2525
}
2626

27+
// class={[...]} or class={{...}} or `class={x}` need clsx to resolve the classes
28+
if (node.name === 'class' && !Array.isArray(node.value) && node.value !== true) {
29+
mark_subtree_dynamic(context.path);
30+
node.metadata.is_dynamic_class = true;
31+
}
32+
2733
if (node.value !== true) {
2834
for (const chunk of get_attribute_chunks(node.value)) {
2935
if (chunk.type !== 'ExpressionTag') continue;

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,10 @@ function build_element_attribute_update_assignment(element, node_id, attribute,
544544
let update;
545545

546546
if (name === 'class') {
547+
if (attribute.metadata.is_dynamic_class) {
548+
value = b.call('$.clsx', value);
549+
}
550+
547551
if (attribute.metadata.expression.has_state && has_call) {
548552
// ensure we're not creating a separate template effect for this so that
549553
// potential class directives are added to the same effect and therefore always apply
@@ -552,11 +556,13 @@ function build_element_attribute_update_assignment(element, node_id, attribute,
552556
value = b.call('$.get', id);
553557
has_call = false;
554558
}
559+
555560
update = b.stmt(
556561
b.call(
557562
is_svg ? '$.set_svg_class' : is_mathml ? '$.set_mathml_class' : '$.set_class',
558563
node_id,
559-
value
564+
value,
565+
attribute.metadata.is_dynamic_class ? b.literal(context.state.analysis.css.hash) : undefined
560566
)
561567
);
562568
} else if (name === 'value') {

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,25 @@ export function build_element_attributes(node, context) {
8585
} else {
8686
if (attribute.name === 'class') {
8787
class_index = attributes.length;
88-
} else if (attribute.name === 'style') {
89-
style_index = attributes.length;
88+
if (attribute.metadata.is_dynamic_class) {
89+
attributes.push({
90+
...attribute,
91+
value: {
92+
.../** @type {AST.ExpressionTag} */ (attribute.value),
93+
expression: b.call(
94+
'$.clsx',
95+
/** @type {AST.ExpressionTag} */ (attribute.value).expression,
96+
b.literal(context.state.analysis.css.hash)
97+
)
98+
}
99+
});
100+
}
101+
} else {
102+
if (attribute.name === 'style') {
103+
style_index = attributes.length;
104+
}
105+
attributes.push(attribute);
90106
}
91-
attributes.push(attribute);
92107
}
93108
} else if (attribute.type === 'BindDirective') {
94109
if (attribute.name === 'value' && node.name === 'select') continue;

packages/svelte/src/compiler/phases/nodes.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ export function create_attribute(name, start, end, value) {
4646
parent: null,
4747
metadata: {
4848
expression: create_expression_metadata(),
49-
delegated: null
49+
delegated: null,
50+
is_dynamic_class: false
5051
}
5152
};
5253
}

packages/svelte/src/compiler/types/template.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,8 @@ export namespace AST {
452452
expression: ExpressionMetadata;
453453
/** May be set if this is an event attribute */
454454
delegated: null | DelegatedEvent;
455+
/** May be `true` if this is a `class` attribute that needs `clsx` */
456+
is_dynamic_class: boolean;
455457
};
456458
}
457459

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,13 @@ export function set_mathml_class(dom, value) {
6161
/**
6262
* @param {HTMLElement} dom
6363
* @param {string} value
64+
* @param {string} [hash]
6465
* @returns {void}
6566
*/
66-
export function set_class(dom, value) {
67+
export function set_class(dom, value, hash) {
6768
// @ts-expect-error need to add __className to patched prototype
6869
var prev_class_name = dom.__className;
69-
var next_class_name = to_class(value);
70+
var next_class_name = to_class(value) + (hash != null ? ' ' + hash : '');
7071

7172
if (hydrating && dom.className === next_class_name) {
7273
// In case of hydration don't reset the class as it's already correct.

packages/svelte/src/internal/client/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export { clsx } from 'clsx';
12
export { FILENAME, HMR, NAMESPACE_SVG } from '../../constants.js';
23
export { cleanup_styles } from './dev/css.js';
34
export { add_locations } from './dev/elements.js';

packages/svelte/src/internal/server/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/** @import { ComponentType, SvelteComponent } from 'svelte' */
22
/** @import { Component, Payload, RenderOutput } from '#server' */
33
/** @import { Store } from '#shared' */
4+
export { clsx } from 'clsx';
45
export { FILENAME, HMR } from '../../constants.js';
56
import { is_promise, noop } from '../shared/utils.js';
67
import { subscribe_to_store } from '../../store/utils.js';

0 commit comments

Comments
 (0)