diff --git a/.changeset/warm-eyes-protect.md b/.changeset/warm-eyes-protect.md new file mode 100644 index 0000000000..fbb971f314 --- /dev/null +++ b/.changeset/warm-eyes-protect.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: recognize all custom element prop definitions diff --git a/documentation/docs/07-misc/04-custom-elements.md b/documentation/docs/07-misc/04-custom-elements.md index 83874f2f5b..71c66f7edc 100644 --- a/documentation/docs/07-misc/04-custom-elements.md +++ b/documentation/docs/07-misc/04-custom-elements.md @@ -49,6 +49,8 @@ console.log(el.name); el.name = 'everybody'; ``` +Note that you need to list out all properties explicitly, i.e. doing `let props = $props()` without declaring `props` in the [component options](#Component-options) means that Svelte can't know which props to expose as properties on the DOM element. + ## Component lifecycle Custom elements are created from Svelte components using a wrapper approach. This means the inner Svelte component has no knowledge that it is a custom element. The custom element wrapper takes care of handling its lifecycle appropriately. diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 8369842e13..4daff53efb 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -561,17 +561,19 @@ export function client_component(analysis, options) { if (analysis.custom_element) { const ce = analysis.custom_element; + const ce_props = typeof ce === 'boolean' ? {} : ce.props || {}; /** @type {ESTree.Property[]} */ const props_str = []; - for (const [name, binding] of properties) { - const key = binding.prop_alias ?? name; - const prop_def = typeof ce === 'boolean' ? {} : ce.props?.[key] || {}; + for (const [name, prop_def] of Object.entries(ce_props)) { + const binding = analysis.instance.scope.get(name); + const key = binding?.prop_alias ?? name; + if ( !prop_def.type && - binding.initial?.type === 'Literal' && - typeof binding.initial.value === 'boolean' + binding?.initial?.type === 'Literal' && + typeof binding?.initial.value === 'boolean' ) { prop_def.type = 'Boolean'; } @@ -585,9 +587,17 @@ export function client_component(analysis, options) { ].filter(Boolean) ) ); + props_str.push(b.init(key, value)); } + for (const [name, binding] of properties) { + const key = binding.prop_alias ?? name; + if (ce_props[key]) continue; + + props_str.push(b.init(key, b.object([]))); + } + const slots_str = b.array([...analysis.slot_names.keys()].map((name) => b.literal(name))); const accessors_str = b.array( analysis.exports.map(({ name, alias }) => b.literal(alias ?? name)) diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/_config.js b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/_config.js new file mode 100644 index 0000000000..04b49c5c00 --- /dev/null +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/_config.js @@ -0,0 +1,21 @@ +import { test } from '../../assert'; +const tick = () => Promise.resolve(); + +export default test({ + async test({ assert, target }) { + target.innerHTML = ''; + await tick(); + + /** @type {any} */ + const el = target.querySelector('custom-element'); + + assert.htmlEqual( + el.shadowRoot.innerHTML, + ` +

1

+

2

+

3

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/main.svelte b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/main.svelte new file mode 100644 index 0000000000..bea04b1248 --- /dev/null +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/main.svelte @@ -0,0 +1,14 @@ + + + + +

{rest.foo}

+

{bar}

+

{baz}

diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/my-widget.svelte b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/my-widget.svelte new file mode 100644 index 0000000000..cdba7491f9 --- /dev/null +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/props-rune-attributes/my-widget.svelte @@ -0,0 +1,14 @@ + + + + +

{rest.foo}

+

{bar}

+

{baz}