diff --git a/.changeset/tricky-planets-rush.md b/.changeset/tricky-planets-rush.md new file mode 100644 index 000000000000..aa93098b9f55 --- /dev/null +++ b/.changeset/tricky-planets-rush.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: recognize option value on spread attribute diff --git a/packages/svelte/src/compiler/compile/nodes/Element.js b/packages/svelte/src/compiler/compile/nodes/Element.js index 0ee4f26d5f74..248f831f66ea 100644 --- a/packages/svelte/src/compiler/compile/nodes/Element.js +++ b/packages/svelte/src/compiler/compile/nodes/Element.js @@ -1,5 +1,6 @@ import { is_html, is_svg, is_void } from '../../../shared/utils/names.js'; import Node from './shared/Node.js'; +import { walk } from 'estree-walker'; import Attribute from './Attribute.js'; import Binding from './Binding.js'; import EventHandler from './EventHandler.js'; @@ -430,7 +431,7 @@ export default class Element extends Node { } if (this.name === 'textarea') { if (info.children.length > 0) { - const value_attribute = info.attributes.find((node) => node.name === 'value'); + const value_attribute = get_value_attribute(info.attributes); if (value_attribute) { component.error(value_attribute, compiler_errors.textarea_duplicate_value); return; @@ -449,7 +450,7 @@ export default class Element extends Node { // Special case — treat these the same way: // // - const value_attribute = info.attributes.find((attribute) => attribute.name === 'value'); + const value_attribute = get_value_attribute(info.attributes); if (!value_attribute) { info.attributes.push({ type: 'Attribute', @@ -1420,3 +1421,30 @@ function within_custom_element(parent) { } return false; } + +/** + * @param {any[]} attributes + */ +function get_value_attribute(attributes) { + let node_value; + attributes.forEach((node) => { + if (node.type !== 'Spread' && node.name.toLowerCase() === 'value') { + node_value = node; + } + if (node.type === 'Spread') { + walk(/** @type {any} */ (node.expression), { + enter(/** @type {import('estree').Node} */ node) { + if (node_value) { + this.skip(); + } + if (node.type === 'Identifier') { + if (/** @type {import('estree').Identifier} */ (node).name.toLowerCase() === 'value') { + node_value = node; + } + } + } + }); + } + }); + return node_value; +} diff --git a/packages/svelte/test/runtime/samples/select-options-spread-attributes/_config.js b/packages/svelte/test/runtime/samples/select-options-spread-attributes/_config.js new file mode 100644 index 000000000000..be4faba61202 --- /dev/null +++ b/packages/svelte/test/runtime/samples/select-options-spread-attributes/_config.js @@ -0,0 +1,7 @@ +export default { + html: ` + + ` +}; diff --git a/packages/svelte/test/runtime/samples/select-options-spread-attributes/main.svelte b/packages/svelte/test/runtime/samples/select-options-spread-attributes/main.svelte new file mode 100644 index 000000000000..cabaf2ea0a7f --- /dev/null +++ b/packages/svelte/test/runtime/samples/select-options-spread-attributes/main.svelte @@ -0,0 +1,3 @@ +