Skip to content

Commit aeab7b7

Browse files
committed
fix spreading twice
1 parent 2d17f4a commit aeab7b7

File tree

3 files changed

+54
-23
lines changed

3 files changed

+54
-23
lines changed

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

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export function RegularElement(node, context) {
7272

7373
let has_spread = node.metadata.has_spread;
7474
let has_use = false;
75+
let should_remove_defaults = false;
7576

7677
for (const attribute of node.attributes) {
7778
switch (attribute.type) {
@@ -172,16 +173,12 @@ export function RegularElement(node, context) {
172173
bindings.has('group') ||
173174
(!bindings.has('group') && has_value_attribute))
174175
) {
175-
const spreads = has_spread
176-
? b.object(
177-
attributes
178-
.filter((attr) => attr.type === 'SpreadAttribute')
179-
.map((attr) => b.spread(attr.expression))
180-
)
181-
: null;
182-
context.state.init.push(
183-
b.stmt(b.call('$.remove_input_defaults', context.state.node, spreads))
184-
);
176+
if (has_spread) {
177+
// remove_input_defaults will be called inside set_attributes
178+
should_remove_defaults = true;
179+
} else {
180+
context.state.init.push(b.stmt(b.call('$.remove_input_defaults', context.state.node)));
181+
}
185182
}
186183
}
187184

@@ -211,7 +208,15 @@ export function RegularElement(node, context) {
211208
bindings.has('checked');
212209

213210
if (has_spread) {
214-
build_attribute_effect(attributes, class_directives, style_directives, context, node, node_id);
211+
build_attribute_effect(
212+
attributes,
213+
class_directives,
214+
style_directives,
215+
context,
216+
node,
217+
node_id,
218+
should_remove_defaults
219+
);
215220
} else {
216221
for (const attribute of /** @type {AST.Attribute[]} */ (attributes)) {
217222
if (is_event_attribute(attribute)) {

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@ import { build_expression, build_template_chunk, Memoizer } from './utils.js';
1616
* @param {ComponentContext} context
1717
* @param {AST.RegularElement | AST.SvelteElement} element
1818
* @param {Identifier} element_id
19+
* @param {boolean} [should_remove_defaults]
1920
*/
2021
export function build_attribute_effect(
2122
attributes,
2223
class_directives,
2324
style_directives,
2425
context,
2526
element,
26-
element_id
27+
element_id,
28+
should_remove_defaults = false
2729
) {
2830
/** @type {ObjectExpression['properties']} */
2931
const values = [];
@@ -91,6 +93,7 @@ export function build_attribute_effect(
9193
element.metadata.scoped &&
9294
context.state.analysis.css.hash !== '' &&
9395
b.literal(context.state.analysis.css.hash),
96+
should_remove_defaults && b.true,
9497
is_ignored(element, 'hydration_attribute_changed') && b.true
9598
)
9699
)

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

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,14 @@ const IS_CUSTOM_ELEMENT = Symbol('is custom element');
3131
const IS_HTML = Symbol('is html');
3232

3333
/**
34-
* The value/checked attribute in the template actually corresponds to the defaultValue property,
35-
* so we need to remove it upon hydration to avoid a bug when someone resets the form value,
36-
* unless the property is presented in the spreaded objects and is handled by `set_attributes()`
34+
* The value/checked attribute in the template actually corresponds to the defaultValue property, so we need to remove
35+
* it upon hydration to avoid a bug when someone resets the form value
3736
* @param {HTMLInputElement} input
38-
* @param {Record<string, any>} [spread]
3937
* @returns {void}
4038
*/
41-
export function remove_input_defaults(input, spread) {
39+
export function remove_input_defaults(input) {
4240
if (!hydrating) return;
4341

44-
if (spread && (input.type === 'checkbox' ? 'defaultChecked' : 'defaultValue') in spread) {
45-
return;
46-
}
47-
4842
var already_removed = false;
4943

5044
// We try and remove the default attributes later, rather than sync during hydration.
@@ -274,10 +268,30 @@ export function set_custom_element_data(node, prop, value) {
274268
* @param {Record<string | symbol, any> | undefined} prev
275269
* @param {Record<string | symbol, any>} next New attributes - this function mutates this object
276270
* @param {string} [css_hash]
271+
* @param {boolean} [should_remove_defaults]
277272
* @param {boolean} [skip_warning]
278273
* @returns {Record<string, any>}
279274
*/
280-
export function set_attributes(element, prev, next, css_hash, skip_warning = false) {
275+
export function set_attributes(
276+
element,
277+
prev,
278+
next,
279+
css_hash,
280+
should_remove_defaults = false,
281+
skip_warning = false
282+
) {
283+
// prettier-ignore
284+
if (
285+
hydrating &&
286+
should_remove_defaults &&
287+
element.tagName === 'INPUT' &&
288+
(/** @type {HTMLInputElement} */ (element).type === 'checkbox'
289+
? !('defaultChecked' in next)
290+
: !('defaultValue' in next))
291+
) {
292+
remove_input_defaults(/** @type {HTMLInputElement} */ (element));
293+
}
294+
281295
var attributes = get_attributes(element);
282296

283297
var is_custom_element = attributes[IS_CUSTOM_ELEMENT];
@@ -471,6 +485,7 @@ export function set_attributes(element, prev, next, css_hash, skip_warning = fal
471485
* @param {Array<() => any>} sync
472486
* @param {Array<() => Promise<any>>} async
473487
* @param {string} [css_hash]
488+
* @param {boolean} [should_remove_defaults]
474489
* @param {boolean} [skip_warning]
475490
*/
476491
export function attribute_effect(
@@ -479,6 +494,7 @@ export function attribute_effect(
479494
sync = [],
480495
async = [],
481496
css_hash,
497+
should_remove_defaults = false,
482498
skip_warning = false
483499
) {
484500
flatten(sync, async, (values) => {
@@ -494,7 +510,14 @@ export function attribute_effect(
494510
block(() => {
495511
var next = fn(...values.map(get));
496512
/** @type {Record<string | symbol, any>} */
497-
var current = set_attributes(element, prev, next, css_hash, skip_warning);
513+
var current = set_attributes(
514+
element,
515+
prev,
516+
next,
517+
css_hash,
518+
should_remove_defaults,
519+
skip_warning
520+
);
498521

499522
if (inited && is_select && 'value' in next) {
500523
select_option(/** @type {HTMLSelectElement} */ (element), next.value);

0 commit comments

Comments
 (0)