pull/16762/head
S. Elliott Johnson 1 week ago
parent a30de03242
commit 41e26b5a8b

@ -275,10 +275,10 @@ export class Boundary {
if (!this.has_pending_snippet()) { if (!this.has_pending_snippet()) {
if (this.parent) { if (this.parent) {
this.parent.#update_pending_count(d); this.parent.#update_pending_count(d);
return;
} }
e.await_outside_boundary(); // if there's no parent, we're in a scope with no pending snippet
return;
} }
this.#pending_count += d; this.#pending_count += d;

@ -31,6 +31,7 @@ import * as e from './errors.js';
import { assign_nodes } from './dom/template.js'; import { assign_nodes } from './dom/template.js';
import { is_passive_event } from '../../utils.js'; import { is_passive_event } from '../../utils.js';
import { COMMENT_NODE } from './constants.js'; import { COMMENT_NODE } from './constants.js';
import { boundary } from './dom/blocks/boundary.js';
/** /**
* This is normally true block effects should run their intro transitions * This is normally true block effects should run their intro transitions
@ -218,35 +219,37 @@ function _mount(Component, { target, anchor, props = {}, events, context, intro
var unmount = component_root(() => { var unmount = component_root(() => {
var anchor_node = anchor ?? target.appendChild(create_text()); var anchor_node = anchor ?? target.appendChild(create_text());
branch(() => { boundary(anchor_node, {}, (anchor_node) =>
if (context) { branch(() => {
push({}); if (context) {
var ctx = /** @type {ComponentContext} */ (component_context); push({});
ctx.c = context; var ctx = /** @type {ComponentContext} */ (component_context);
} ctx.c = context;
}
if (events) { if (events) {
// We can't spread the object or else we'd lose the state proxy stuff, if it is one // We can't spread the object or else we'd lose the state proxy stuff, if it is one
/** @type {any} */ (props).$$events = events; /** @type {any} */ (props).$$events = events;
} }
if (hydrating) { if (hydrating) {
assign_nodes(/** @type {TemplateNode} */ (anchor_node), null); assign_nodes(/** @type {TemplateNode} */ (anchor_node), null);
} }
should_intro = intro; should_intro = intro;
// @ts-expect-error the public typings are not what the actual function looks like // @ts-expect-error the public typings are not what the actual function looks like
component = Component(anchor_node, props) || {}; component = Component(anchor_node, props) || {};
should_intro = true; should_intro = true;
if (hydrating) { if (hydrating) {
/** @type {Effect} */ (active_effect).nodes_end = hydrate_node; /** @type {Effect} */ (active_effect).nodes_end = hydrate_node;
} }
if (context) { if (context) {
pop(); pop();
} }
}); })
);
return () => { return () => {
for (var event_name of registered_events) { for (var event_name of registered_events) {

@ -104,6 +104,8 @@ export function render(component, options = {}) {
let { head, body } = payload.collect(); let { head, body } = payload.collect();
head += payload.global.head.title.value; head += payload.global.head.title.value;
body = BLOCK_OPEN + body + BLOCK_CLOSE; // this inserts a fake boundary so hydration matches
for (const { hash, code } of payload.global.css) { for (const { hash, code } of payload.global.css) {
head += `<style id="${hash}">${code}</style>`; head += `<style id="${hash}">${code}</style>`;
} }
@ -171,6 +173,8 @@ export async function render_async(component, options = {}) {
let { head, body } = await payload; let { head, body } = await payload;
head += payload.global.head.title.value; head += payload.global.head.title.value;
body = BLOCK_OPEN + body + BLOCK_CLOSE; // this inserts a fake boundary so hydration matches
for (const { hash, code } of payload.global.css) { for (const { hash, code } of payload.global.css) {
head += `<style id="${hash}">${code}</style>`; head += `<style id="${hash}">${code}</style>`;
} }

@ -398,12 +398,17 @@ async function run_test_variant(
}; };
} else { } else {
const render = variant === 'hydrate' ? hydrate : mount; const render = variant === 'hydrate' ? hydrate : mount;
instance = render(mod.default, { // catch and log error from this function
target, try {
props, instance = render(mod.default, {
intro: config.intro, target,
recover: config.recover ?? false props,
}); intro: config.intro,
recover: config.recover ?? false
});
} catch (error) {
console.log(error);
}
} }
} else { } else {
instance = createClassComponent({ instance = createClassComponent({

@ -1,14 +1,23 @@
import { flushSync, settled } from 'svelte';
import { ok, test } from '../../test'; import { ok, test } from '../../test';
export default test({ export default test({
skip: true, skip_mode: ['hydrate', 'server'],
html: ` html: `
<p>hello</p> <p>hello</p>
`, `,
async test({ assert, target }) { async test({ assert, target, variant }) {
if (variant === 'dom') {
await settled();
}
const p = target.querySelector('p'); const p = target.querySelector('p');
ok(p); ok(p);
assert.htmlEqual(p.outerHTML, '<p>Loading...</p>');
await settled();
flushSync();
assert.htmlEqual(p.outerHTML, '<p>hello</p>'); assert.htmlEqual(p.outerHTML, '<p>hello</p>');
} }
}); });

@ -1,2 +1,7 @@
<svelte:boundary>
<p>{await Promise.resolve('hello')}</p>
<p>{await Promise.resolve('hello')}</p> {#snippet pending()}
<p>Loading...</p>
{/snippet}
</svelte:boundary>
Loading…
Cancel
Save