Skip to content

Commit cf56973

Browse files
authored
fix: class:directive not working with $$restProps (#15389)
* add spread to test * fix #15386 * do no set cssHash on non-scoped element * changeset
1 parent da98c89 commit cf56973

File tree

7 files changed

+119
-19
lines changed

7 files changed

+119
-19
lines changed

.changeset/lemon-cougars-buy.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: class:directive not working with $$restProps #15386
6+
fix: spread add an useless cssHash on non-scoped element

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ export function build_set_attributes(
8888
element_id,
8989
is_dynamic ? attributes_id : b.literal(null),
9090
b.object(values),
91-
context.state.analysis.css.hash !== '' && b.literal(context.state.analysis.css.hash),
91+
element.metadata.scoped &&
92+
context.state.analysis.css.hash !== '' &&
93+
b.literal(context.state.analysis.css.hash),
9294
preserve_attribute_case,
9395
is_custom_element,
9496
is_ignored(element, 'hydration_attribute_changed') && b.true

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -368,9 +368,10 @@ function build_element_spread_attributes(
368368
})
369369
);
370370

371-
const css_hash = context.state.analysis.css.hash
372-
? b.literal(context.state.analysis.css.hash)
373-
: b.null;
371+
const css_hash =
372+
element.metadata.scoped && context.state.analysis.css.hash
373+
? b.literal(context.state.analysis.css.hash)
374+
: b.null;
374375

375376
const args = [object, css_hash, classes, styles, flags ? b.literal(flags) : undefined];
376377
context.state.template.push(b.call('$.spread_attributes', ...args));

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -326,8 +326,16 @@ export function set_attributes(
326326
continue;
327327
}
328328

329+
if (key === 'class') {
330+
var is_html = element.namespaceURI === 'http://www.w3.org/1999/xhtml';
331+
set_class(element, is_html, value, css_hash, prev?.[CLASS], next[CLASS]);
332+
current[key] = value;
333+
current[CLASS] = next[CLASS];
334+
continue;
335+
}
336+
329337
var prev_value = current[key];
330-
if (value === prev_value && key !== 'class') continue;
338+
if (value === prev_value) continue;
331339

332340
current[key] = value;
333341

@@ -377,9 +385,6 @@ export function set_attributes(
377385
// @ts-ignore
378386
element[`__${event_name}`] = undefined;
379387
}
380-
} else if (key === 'class') {
381-
var is_html = element.namespaceURI === 'http://www.w3.org/1999/xhtml';
382-
set_class(element, is_html, value, css_hash, prev?.[CLASS], next[CLASS]);
383388
} else if (key === 'style' && value != null) {
384389
element.style.cssText = value + '';
385390
} else if (key === 'autofocus') {

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import { hydrating } from '../hydration.js';
66
* @param {boolean | number} is_html
77
* @param {string | null} value
88
* @param {string} [hash]
9-
* @param {Record<string, boolean>} [prev_classes]
10-
* @param {Record<string, boolean>} [next_classes]
9+
* @param {Record<string, any>} [prev_classes]
10+
* @param {Record<string, any>} [next_classes]
1111
* @returns {Record<string, boolean> | undefined}
1212
*/
1313
export function set_class(dom, is_html, value, hash, prev_classes, next_classes) {
@@ -34,12 +34,10 @@ export function set_class(dom, is_html, value, hash, prev_classes, next_classes)
3434
// @ts-expect-error need to add __className to patched prototype
3535
dom.__className = value;
3636
} else if (next_classes) {
37-
prev_classes ??= {};
38-
3937
for (var key in next_classes) {
4038
var is_present = !!next_classes[key];
4139

42-
if (is_present !== !!prev_classes[key]) {
40+
if (prev_classes == null || is_present !== !!prev_classes[key]) {
4341
dom.classList.toggle(key, is_present);
4442
}
4543
}

packages/svelte/tests/runtime-runes/samples/class-directive-mutations/_config.js

Lines changed: 88 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { flushSync } from 'svelte';
1+
import { flushSync, tick } from 'svelte';
22
import { test } from '../../test';
33

44
// This test counts mutations on hydration
@@ -16,7 +16,12 @@ export default test({
1616

1717
html: `
1818
<main id="main" class="browser">
19-
<div class="custom svelte-1cjqok6 foo bar"></div>
19+
<div class="custom svelte-1cjqok6 foo bar" title="a title"></div>
20+
<span class="svelte-1cjqok6 foo bar"></span>
21+
<b class="custom foo bar"></b>
22+
<i class="foo bar"></i>
23+
24+
<div class="custom svelte-1cjqok6 foo bar" title="a title"></div>
2025
<span class="svelte-1cjqok6 foo bar"></span>
2126
<b class="custom foo bar"></b>
2227
<i class="foo bar"></i>
@@ -25,19 +30,97 @@ export default test({
2530

2631
ssrHtml: `
2732
<main id="main">
28-
<div class="custom svelte-1cjqok6 foo bar"></div>
33+
<div class="custom svelte-1cjqok6 foo bar" title="a title"></div>
34+
<span class="svelte-1cjqok6 foo bar"></span>
35+
<b class="custom foo bar"></b>
36+
<i class="foo bar"></i>
37+
38+
<div class="custom svelte-1cjqok6 foo bar" title="a title"></div>
2939
<span class="svelte-1cjqok6 foo bar"></span>
3040
<b class="custom foo bar"></b>
3141
<i class="foo bar"></i>
3242
</main>
3343
`,
3444

35-
async test({ assert, component, instance }) {
45+
async test({ target, assert, component, instance }) {
3646
flushSync();
47+
tick();
3748
assert.deepEqual(instance.get_and_clear_mutations(), ['MAIN']);
3849

3950
component.foo = false;
4051
flushSync();
41-
assert.deepEqual(instance.get_and_clear_mutations(), ['DIV', 'SPAN', 'B', 'I']);
52+
tick();
53+
assert.deepEqual(
54+
instance.get_and_clear_mutations(),
55+
['DIV', 'SPAN', 'B', 'I', 'DIV', 'SPAN', 'B', 'I'],
56+
'first mutation'
57+
);
58+
59+
assert.htmlEqual(
60+
target.innerHTML,
61+
`
62+
<main id="main" class="browser">
63+
<div class="custom svelte-1cjqok6 bar" title="a title"></div>
64+
<span class="svelte-1cjqok6 bar"></span>
65+
<b class="custom bar"></b>
66+
<i class="bar"></i>
67+
68+
<div class="custom svelte-1cjqok6 bar" title="a title"></div>
69+
<span class="svelte-1cjqok6 bar"></span>
70+
<b class="custom bar"></b>
71+
<i class="bar"></i>
72+
</main>
73+
`
74+
);
75+
76+
component.foo = true;
77+
flushSync();
78+
assert.deepEqual(
79+
instance.get_and_clear_mutations(),
80+
['DIV', 'SPAN', 'B', 'I', 'DIV', 'SPAN', 'B', 'I'],
81+
'second mutation'
82+
);
83+
84+
assert.htmlEqual(
85+
target.innerHTML,
86+
`
87+
<main id="main" class="browser">
88+
<div class="custom svelte-1cjqok6 bar foo" title="a title"></div>
89+
<span class="svelte-1cjqok6 bar foo"></span>
90+
<b class="custom bar foo"></b>
91+
<i class="bar foo"></i>
92+
93+
<div class="custom svelte-1cjqok6 bar foo" title="a title"></div>
94+
<span class="svelte-1cjqok6 bar foo"></span>
95+
<b class="custom bar foo"></b>
96+
<i class="bar foo"></i>
97+
</main>
98+
`
99+
);
100+
101+
component.classname = 'another';
102+
flushSync();
103+
assert.deepEqual(
104+
instance.get_and_clear_mutations(),
105+
['DIV', 'B', 'DIV', 'B'],
106+
'class mutation'
107+
);
108+
109+
assert.htmlEqual(
110+
target.innerHTML,
111+
`
112+
<main id="main" class="browser">
113+
<div class="another svelte-1cjqok6 foo bar" title="a title"></div>
114+
<span class="svelte-1cjqok6 bar foo"></span>
115+
<b class="another foo bar"></b>
116+
<i class="bar foo"></i>
117+
118+
<div class="another svelte-1cjqok6 foo bar" title="a title"></div>
119+
<span class="svelte-1cjqok6 bar foo"></span>
120+
<b class="another foo bar"></b>
121+
<i class="bar foo"></i>
122+
</main>
123+
`
124+
);
42125
}
43126
});

packages/svelte/tests/runtime-runes/samples/class-directive-mutations/main.svelte

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,15 @@
3333
</script>
3434

3535
<main id="main" class:browser>
36-
<div class={classname} class:foo class:bar></div>
36+
<div class={classname} title="a title" class:foo class:bar></div>
3737
<span class:foo class:bar></span>
3838
<b class={classname} class:foo class:bar></b>
3939
<i class:foo class:bar></i>
40+
41+
<div {...{class:classname, title:"a title"}} class:foo class:bar></div>
42+
<span {...{}} class:foo class:bar></span>
43+
<b {...{class:classname}} class:foo class:bar></b>
44+
<i {...{}} class:foo class:bar></i>
4045
</main>
4146

4247
<style>

0 commit comments

Comments
 (0)