Skip to content

Commit 6fa3e91

Browse files
authored
support $$props and $$restProps for custom elements (#5608)
1 parent 0ca36a1 commit 6fa3e91

File tree

7 files changed

+67
-2
lines changed

7 files changed

+67
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Unreleased
44

5+
* Fix `$$props` and `$$restProps` when compiling to a custom element ([#5482](https://github.com/sveltejs/svelte/issues/5482))
56
* Add `Element` and `Node` to known globals ([#5586](https://github.com/sveltejs/svelte/issues/5586))
67

78
## 3.29.4

src/compiler/compile/render_dom/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ export default function dom(
474474
475475
${css.code && b`this.shadowRoot.innerHTML = \`<style>${css.code.replace(/\\/g, '\\\\')}${options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`}
476476
477-
@init(this, { target: this.shadowRoot }, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});
477+
@init(this, { target: this.shadowRoot, props: @attribute_to_object(this.attributes) }, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});
478478
479479
${dev_props_check}
480480

src/runtime/internal/dom.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,3 +360,11 @@ export class HtmlTag {
360360
this.n.forEach(detach);
361361
}
362362
}
363+
364+
export function attribute_to_object(attributes) {
365+
const result = {};
366+
for (const attribute of attributes) {
367+
result[attribute.name] = attribute.value;
368+
}
369+
return result;
370+
}

test/custom-elements/assert.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,24 @@ export function equal(a, b, message) {
3131
export function ok(condition, message) {
3232
if (!condition) throw new Error(message || `Expected ${condition} to be truthy`);
3333
}
34+
35+
export function htmlEqual(actual, expected, message) {
36+
return deepEqual(
37+
normalizeHtml(window, actual),
38+
normalizeHtml(window, expected),
39+
message
40+
);
41+
}
42+
43+
function normalizeHtml(window, html) {
44+
try {
45+
const node = window.document.createElement('div');
46+
node.innerHTML = html
47+
.replace(/<!--.*?-->/g, '')
48+
.replace(/>[\s\r\n]+</g, '><')
49+
.trim();
50+
return node.innerHTML.replace(/<\/?noscript\/?>/g, '');
51+
} catch (err) {
52+
throw new Error(`Failed to normalize HTML:\n${html}`);
53+
}
54+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<svelte:options tag="custom-element"/>
2+
3+
<script>
4+
export let name;
5+
</script>
6+
7+
<p>name: {name}</p>
8+
<p>$$props: {JSON.stringify($$props)}</p>
9+
<p>$$restProps: {JSON.stringify($$restProps)}</p>
10+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as assert from 'assert';
2+
import './main.svelte';
3+
4+
export default function (target) {
5+
target.innerHTML = '<custom-element name="world" answer="42" test="svelte"></custom-element>';
6+
const el = target.querySelector('custom-element');
7+
8+
assert.htmlEqual(el.shadowRoot.innerHTML, `
9+
<p>name: world</p>
10+
<p>$$props: {"name":"world","answer":"42","test":"svelte"}</p>
11+
<p>$$restProps: {"answer":"42","test":"svelte"}</p>
12+
`);
13+
}

test/js/samples/css-shadow-dom-keyframes/expected.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* generated by Svelte vX.Y.Z */
22
import {
33
SvelteElement,
4+
attribute_to_object,
45
detach,
56
element,
67
init,
@@ -34,7 +35,18 @@ class Component extends SvelteElement {
3435
constructor(options) {
3536
super();
3637
this.shadowRoot.innerHTML = `<style>div{animation:foo 1s}@keyframes foo{0%{opacity:0}100%{opacity:1}}</style>`;
37-
init(this, { target: this.shadowRoot }, null, create_fragment, safe_not_equal, {});
38+
39+
init(
40+
this,
41+
{
42+
target: this.shadowRoot,
43+
props: attribute_to_object(this.attributes)
44+
},
45+
null,
46+
create_fragment,
47+
safe_not_equal,
48+
{}
49+
);
3850

3951
if (options) {
4052
if (options.target) {

0 commit comments

Comments
 (0)