From 868fb234edfe2aefc27caf93b3439c6cda72e25f Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 11 Apr 2023 10:11:05 +0200 Subject: [PATCH] mount and render after a tick --- .../compile/render_dom/wrappers/Slot.ts | 4 --- src/runtime/internal/Component.ts | 36 +++++++------------ test/custom-elements/samples/$$props/test.js | 4 ++- .../samples/$$slot-dynamic-content/test.js | 13 ++++--- test/custom-elements/samples/$$slot/test.js | 4 ++- test/custom-elements/samples/action/test.js | 3 +- .../samples/camel-case-attribute/test.js | 4 ++- .../samples/ce-options-valid/test.js | 4 ++- .../samples/custom-method/test.js | 2 ++ .../samples/escaped-css/test.js | 4 ++- test/custom-elements/samples/events/test.js | 4 ++- .../samples/extended-builtin/test.js | 4 ++- .../samples/html-slots/test.js | 4 ++- test/custom-elements/samples/html/test.js | 4 ++- test/custom-elements/samples/nested/test.js | 2 ++ .../samples/new-styled/test.js | 4 ++- .../samples/no-missing-prop-warnings/test.js | 4 ++- .../samples/no-svelte-options/test.js | 4 ++- .../samples/no-tag-warning/test.js | 4 ++- test/custom-elements/samples/no-tag/test.js | 4 ++- test/custom-elements/samples/oncreate/test.js | 1 + .../custom-elements/samples/ondestroy/test.js | 1 + test/custom-elements/samples/props/test.js | 6 +++- .../samples/reflect-attributes/test.js | 5 ++- 24 files changed, 79 insertions(+), 50 deletions(-) diff --git a/src/compiler/compile/render_dom/wrappers/Slot.ts b/src/compiler/compile/render_dom/wrappers/Slot.ts index d0cb2c318d..0a589e3394 100644 --- a/src/compiler/compile/render_dom/wrappers/Slot.ts +++ b/src/compiler/compile/render_dom/wrappers/Slot.ts @@ -132,10 +132,6 @@ export default class SlotWrapper extends Wrapper { const ${slot_definition} = ${renderer.reference('#slots')}.${slot_name}; const ${slot} = @create_slot(${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${get_slot_context_fn}); ${has_fallback ? b`const ${slot_or_fallback} = ${slot} || ${this.fallback.name}(#ctx);` : null} - ${has_fallback && this.renderer.options.customElement && this.renderer.component.component_options.tag - // This ensures that fallback content is rendered into the element given by the custom element wrapper - ? b`if (${slot_or_fallback}.$$c_e) { ${slot_or_fallback}.$$c_e = ${this.fallback.name}(#ctx); }` - : null} `); block.chunks.create.push( diff --git a/src/runtime/internal/Component.ts b/src/runtime/internal/Component.ts index 5d2850a65f..9bab5c7575 100644 --- a/src/runtime/internal/Component.ts +++ b/src/runtime/internal/Component.ts @@ -1,4 +1,4 @@ -import { add_render_callback, flush, flush_render_callbacks, schedule_update, dirty_components, tick } from './scheduler'; +import { add_render_callback, flush, flush_render_callbacks, schedule_update, dirty_components } from './scheduler'; import { current_component, set_current_component } from './lifecycle'; import { blank_object, is_empty, is_function, run, run_all, noop } from './utils'; import { children, detach, start_hydrating, end_hydrating, get_custom_elements_slots, insert } from './dom'; @@ -169,10 +169,17 @@ if (typeof HTMLElement === 'function') { super.addEventListener(type, listener, options); } - connectedCallback() { + async connectedCallback() { this.$$connected = true; if (!this.$$component) { - function create_slot(name: string, $$c_e = false) { + // We wait one tick to let possible child slot elements be created/mounted + await Promise.resolve(); + + if (!this.$$connected) { + return; + } + + function create_slot(name: string) { return () => { let node: HTMLSlotElement; const obj = { @@ -181,25 +188,15 @@ if (typeof HTMLElement === 'function') { if (name !== 'default') { node.setAttribute('name', name); } - if (typeof obj.$$c_e === 'object') { - (obj.$$c_e as any).c() - } }, m: function mount(target: HTMLElement, anchor?: HTMLElement) { insert(target, node, anchor); - if (typeof obj.$$c_e === 'object') { - (obj.$$c_e as any).m(node, anchor) - } }, d: function destroy(detaching: boolean) { if (detaching) { - if (typeof obj.$$c_e === 'object') { - (obj.$$c_e as any).d(detaching) - } detach(node); } - }, - $$c_e + } }; return obj; }; @@ -213,15 +210,6 @@ if (typeof HTMLElement === 'function') { } } - if (!Object.keys($$slots).length && this.$$slots.length) { - // There are potentially slots, but we didn't find any. This could be due to Svelte adding the child nodes - // only after the component is created (in the mount phase). In this case, pass in placeholder slots. - // The drawback is that all $$slots properties will be `true`, even if the component doesn't use them. - for (const name of this.$$slots) { - $$slots[name] = [create_slot(name, true)]; - } - } - for (const attribute of this.attributes) { // this.$$data takes precedence over this.attributes const name = this.$$get_prop_name(attribute.name); @@ -256,7 +244,7 @@ if (typeof HTMLElement === 'function') { disconnectedCallback() { this.$$connected = false; // In a microtask, because this could be a move within the DOM - tick().then(() => { + Promise.resolve().then(() => { if (!this.$$connected) { this.$$component!.$destroy(); this.$$component = undefined; diff --git a/test/custom-elements/samples/$$props/test.js b/test/custom-elements/samples/$$props/test.js index 94cad86577..844390acc9 100644 --- a/test/custom-elements/samples/$$props/test.js +++ b/test/custom-elements/samples/$$props/test.js @@ -1,8 +1,10 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import './main.svelte'; -export default function (target) { +export default async function (target) { target.innerHTML = ''; + await tick(); const el = target.querySelector('custom-element'); assert.htmlEqual(el.shadowRoot.innerHTML, ` diff --git a/test/custom-elements/samples/$$slot-dynamic-content/test.js b/test/custom-elements/samples/$$slot-dynamic-content/test.js index 348cd5aa86..629e6f5eb7 100644 --- a/test/custom-elements/samples/$$slot-dynamic-content/test.js +++ b/test/custom-elements/samples/$$slot-dynamic-content/test.js @@ -1,19 +1,22 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import Component from './main.svelte'; -export default function (target) { +export default async function (target) { const component = new Component({ target, props: { name: 'slot' } }); + await tick(); + await tick(); const ce = target.querySelector('my-widget'); assert.htmlEqual(ce.shadowRoot.innerHTML, ` - fallback -

named fallback

+ +

named fallback

`); component.name = 'slot2'; assert.htmlEqual(ce.shadowRoot.innerHTML, ` - fallback -

named fallback

+ +

named fallback

`); } diff --git a/test/custom-elements/samples/$$slot/test.js b/test/custom-elements/samples/$$slot/test.js index 567e93f509..59c6ba8b22 100644 --- a/test/custom-elements/samples/$$slot/test.js +++ b/test/custom-elements/samples/$$slot/test.js @@ -1,11 +1,13 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import './main.svelte'; -export default function (target) { +export default async function (target) { target.innerHTML = ` hello worldbyeworld hello worldhello worldbye world `; + await tick(); const [a, b] = target.querySelectorAll('custom-element'); diff --git a/test/custom-elements/samples/action/test.js b/test/custom-elements/samples/action/test.js index e3fa0a808f..e57be4059e 100644 --- a/test/custom-elements/samples/action/test.js +++ b/test/custom-elements/samples/action/test.js @@ -1,9 +1,10 @@ -import { tick } from 'svelte'; import * as assert from 'assert'; +import { tick } from 'svelte'; import './main.svelte'; export default async function (target) { target.innerHTML = ''; + await tick(); const el = target.querySelector('custom-element'); const events = el.events; // need to get the array reference, else it's gone when destroyed assert.deepEqual(events, ['foo']); diff --git a/test/custom-elements/samples/camel-case-attribute/test.js b/test/custom-elements/samples/camel-case-attribute/test.js index 9b0c35d9d9..e8933e2c1f 100644 --- a/test/custom-elements/samples/camel-case-attribute/test.js +++ b/test/custom-elements/samples/camel-case-attribute/test.js @@ -1,8 +1,10 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import './main.svelte'; -export default function (target) { +export default async function (target) { target.innerHTML = ''; + await tick(); const el = target.querySelector('custom-element'); assert.equal(el.shadowRoot.innerHTML, '

Hello world!

1

2

'); diff --git a/test/custom-elements/samples/ce-options-valid/test.js b/test/custom-elements/samples/ce-options-valid/test.js index 75de66d5c6..9fa19e53a2 100644 --- a/test/custom-elements/samples/ce-options-valid/test.js +++ b/test/custom-elements/samples/ce-options-valid/test.js @@ -1,8 +1,10 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import './main.svelte'; -export default function (target) { +export default async function (target) { target.innerHTML = ''; + await tick(); const el = target.querySelector('custom-element'); const h1 = el.shadowRoot.querySelector('h1'); diff --git a/test/custom-elements/samples/custom-method/test.js b/test/custom-elements/samples/custom-method/test.js index 08c58c3383..ba1ca25176 100644 --- a/test/custom-elements/samples/custom-method/test.js +++ b/test/custom-elements/samples/custom-method/test.js @@ -1,8 +1,10 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import './main.svelte'; export default async function (target) { target.innerHTML = ''; + await tick(); const el = target.querySelector('custom-element'); await el.updateFoo(42); diff --git a/test/custom-elements/samples/escaped-css/test.js b/test/custom-elements/samples/escaped-css/test.js index e7df08da1c..1b7e2ea7ce 100644 --- a/test/custom-elements/samples/escaped-css/test.js +++ b/test/custom-elements/samples/escaped-css/test.js @@ -1,8 +1,10 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import './main.svelte'; -export default function (target) { +export default async function (target) { target.innerHTML = ''; + await tick(); const icon = target.querySelector('custom-element').shadowRoot.querySelector('.icon'); const before = getComputedStyle(icon, '::before'); diff --git a/test/custom-elements/samples/events/test.js b/test/custom-elements/samples/events/test.js index 2c8b7832d0..ec4fcd2796 100644 --- a/test/custom-elements/samples/events/test.js +++ b/test/custom-elements/samples/events/test.js @@ -1,8 +1,10 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import './main.svelte'; -export default function (target) { +export default async function (target) { target.innerHTML = ''; + await tick(); const el = target.querySelector('custom-element'); const events = []; diff --git a/test/custom-elements/samples/extended-builtin/test.js b/test/custom-elements/samples/extended-builtin/test.js index 1eac8d852e..ba5d27ea6d 100644 --- a/test/custom-elements/samples/extended-builtin/test.js +++ b/test/custom-elements/samples/extended-builtin/test.js @@ -1,8 +1,10 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import './main.svelte'; -export default function (target) { +export default async function (target) { target.innerHTML = ''; + await tick(); assert.equal(target.innerHTML, ''); const el = target.querySelector('custom-element'); diff --git a/test/custom-elements/samples/html-slots/test.js b/test/custom-elements/samples/html-slots/test.js index c82a2d24ad..c8e09347db 100644 --- a/test/custom-elements/samples/html-slots/test.js +++ b/test/custom-elements/samples/html-slots/test.js @@ -1,11 +1,13 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import './main.svelte'; -export default function (target) { +export default async function (target) { target.innerHTML = ` slotted `; + await tick(); const el = target.querySelector('custom-element'); diff --git a/test/custom-elements/samples/html/test.js b/test/custom-elements/samples/html/test.js index 4e38fd6c2d..d7764af397 100644 --- a/test/custom-elements/samples/html/test.js +++ b/test/custom-elements/samples/html/test.js @@ -1,8 +1,10 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import './main.svelte'; -export default function (target) { +export default async function (target) { target.innerHTML = ''; + await tick(); const el = target.querySelector('custom-element'); assert.equal(el.name, 'world'); diff --git a/test/custom-elements/samples/nested/test.js b/test/custom-elements/samples/nested/test.js index 29029f3d2c..0493e58bf0 100644 --- a/test/custom-elements/samples/nested/test.js +++ b/test/custom-elements/samples/nested/test.js @@ -1,8 +1,10 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import './main.svelte'; export default async function (target) { target.innerHTML = ''; + await tick(); const el = target.querySelector('my-app'); const button = el.shadowRoot.querySelector('button'); const span = el.shadowRoot.querySelector('span'); diff --git a/test/custom-elements/samples/new-styled/test.js b/test/custom-elements/samples/new-styled/test.js index 3433451900..bf7abb449e 100644 --- a/test/custom-elements/samples/new-styled/test.js +++ b/test/custom-elements/samples/new-styled/test.js @@ -1,9 +1,11 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import './main.svelte'; -export default function (target) { +export default async function (target) { target.innerHTML = '

unstyled

'; target.appendChild(document.createElement('custom-element')); + await tick(); const unstyled = target.querySelector('p'); const styled = target.querySelector('custom-element').shadowRoot.querySelector('p'); diff --git a/test/custom-elements/samples/no-missing-prop-warnings/test.js b/test/custom-elements/samples/no-missing-prop-warnings/test.js index 6f15b63920..32c447e1c1 100644 --- a/test/custom-elements/samples/no-missing-prop-warnings/test.js +++ b/test/custom-elements/samples/no-missing-prop-warnings/test.js @@ -1,7 +1,8 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import './main.svelte'; -export default function (target) { +export default async function (target) { const warnings = []; const warn = console.warn; @@ -10,6 +11,7 @@ export default function (target) { }; target.innerHTML = ''; + await tick(); assert.deepEqual(warnings, [ " was created without expected prop 'bar'" diff --git a/test/custom-elements/samples/no-svelte-options/test.js b/test/custom-elements/samples/no-svelte-options/test.js index ebb7db4d51..0ff4e7c7d2 100644 --- a/test/custom-elements/samples/no-svelte-options/test.js +++ b/test/custom-elements/samples/no-svelte-options/test.js @@ -1,10 +1,12 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import CustomElement from './main.svelte'; import { create_custom_element } from 'svelte/internal'; -export default function (target) { +export default async function (target) { customElements.define('no-tag', create_custom_element(CustomElement, {name: {}}, [], [])); target.innerHTML = ''; + await tick(); const el = target.querySelector('no-tag'); const h1 = el.shadowRoot.querySelector('h1'); diff --git a/test/custom-elements/samples/no-tag-warning/test.js b/test/custom-elements/samples/no-tag-warning/test.js index 170fbb4f9d..e6b82e8ec8 100644 --- a/test/custom-elements/samples/no-tag-warning/test.js +++ b/test/custom-elements/samples/no-tag-warning/test.js @@ -1,10 +1,12 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import CustomElement from './main.svelte'; import { create_custom_element } from 'svelte/internal'; -export default function (target) { +export default async function (target) { customElements.define('no-tag', create_custom_element(CustomElement, { name: {}}, [], [])); target.innerHTML = ''; + await tick(); const el = target.querySelector('no-tag'); const h1 = el.shadowRoot.querySelector('h1'); diff --git a/test/custom-elements/samples/no-tag/test.js b/test/custom-elements/samples/no-tag/test.js index ddf193bc1c..4967b31fba 100644 --- a/test/custom-elements/samples/no-tag/test.js +++ b/test/custom-elements/samples/no-tag/test.js @@ -1,10 +1,12 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import CustomElement from './main.svelte'; import { create_custom_element } from 'svelte/internal'; -export default function (target) { +export default async function (target) { customElements.define('no-tag', create_custom_element(CustomElement, { name: {} }, [], [])); target.innerHTML = ''; + await tick(); const el = target.querySelector('no-tag'); const h1 = el.shadowRoot.querySelector('h1'); diff --git a/test/custom-elements/samples/oncreate/test.js b/test/custom-elements/samples/oncreate/test.js index cd27d9de86..d377efe156 100644 --- a/test/custom-elements/samples/oncreate/test.js +++ b/test/custom-elements/samples/oncreate/test.js @@ -4,6 +4,7 @@ import './main.svelte'; export default async function (target) { target.innerHTML = ''; + await tick(); const el = target.querySelector('my-app'); await tick(); diff --git a/test/custom-elements/samples/ondestroy/test.js b/test/custom-elements/samples/ondestroy/test.js index 3092613fc3..62ec07a419 100644 --- a/test/custom-elements/samples/ondestroy/test.js +++ b/test/custom-elements/samples/ondestroy/test.js @@ -4,6 +4,7 @@ import './main.svelte'; export default async function (target) { target.innerHTML = ''; + await tick(); const el = target.querySelector('my-app'); target.removeChild(el); diff --git a/test/custom-elements/samples/props/test.js b/test/custom-elements/samples/props/test.js index 3d93c60b83..e3824582e1 100644 --- a/test/custom-elements/samples/props/test.js +++ b/test/custom-elements/samples/props/test.js @@ -1,8 +1,11 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import './main.svelte'; -export default function (target) { +export default async function (target) { target.innerHTML = ''; + await tick(); + await tick(); assert.equal(target.innerHTML, ''); @@ -10,6 +13,7 @@ export default function (target) { const widget = el.shadowRoot.querySelector('my-widget'); const [p1, p2, p3, p4] = widget.shadowRoot.querySelectorAll('p'); + console.log('goooo', !!p1, !!p2, !!p3, !!p4) assert.equal(p1.textContent, '3 items'); assert.equal(p2.textContent, 'a, b, c'); diff --git a/test/custom-elements/samples/reflect-attributes/test.js b/test/custom-elements/samples/reflect-attributes/test.js index 4b392868c6..dfa925403d 100644 --- a/test/custom-elements/samples/reflect-attributes/test.js +++ b/test/custom-elements/samples/reflect-attributes/test.js @@ -1,8 +1,11 @@ import * as assert from 'assert'; +import { tick } from 'svelte'; import './main.svelte'; -export default function (target) { +export default async function (target) { target.innerHTML = ''; + await tick(); + await tick(); const ceRoot = target.querySelector('custom-element').shadowRoot; const div = ceRoot.querySelector('div'); const p = ceRoot.querySelector('p');