merge local branches (#16757)

* use fragment as async hoist boundary

* remove async_hoist_boundary

* only dewaterfall when necessary

* unused

* simplify/fix

* de-waterfall awaits in separate elements

* update snapshots

* remove unnecessary wrapper

* fix

* fix

* remove suspends_without_fallback

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/16762/head
Elliott Johnson 6 days ago committed by GitHub
parent 127c5a73cb
commit 49241764bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,4 +1,5 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
import * as b from '#compiler/builders';
/** /**
* @param {any} transparent * @param {any} transparent
@ -13,7 +14,7 @@ export function create_fragment(transparent = false) {
dynamic: false, dynamic: false,
has_await: false, has_await: false,
// name is added later, after we've done scope analysis // name is added later, after we've done scope analysis
hoisted_promises: { name: '', promises: [] } hoisted_promises: { id: b.id('$$promises'), promises: [] }
} }
}; };
} }

@ -131,9 +131,6 @@ const visitors = {
ignore_map.set(node, structuredClone(ignore_stack)); ignore_map.set(node, structuredClone(ignore_stack));
const scope = state.scopes.get(node); const scope = state.scopes.get(node);
if (node.type === 'Fragment') {
node.metadata.hoisted_promises.name = state.scope.generate('promises');
}
next(scope !== undefined && scope !== state.scope ? { ...state, scope } : state); next(scope !== undefined && scope !== state.scope ? { ...state, scope } : state);
if (ignores.length > 0) { if (ignores.length > 0) {
@ -309,8 +306,7 @@ export function analyze_module(source, options) {
title: null, title: null,
boundary: null, boundary: null,
parent_element: null, parent_element: null,
reactive_statement: null, reactive_statement: null
async_hoist_boundary: null
}, },
visitors visitors
); );
@ -547,7 +543,6 @@ export function analyze_component(root, source, options) {
snippet_renderers: new Map(), snippet_renderers: new Map(),
snippets: new Set(), snippets: new Set(),
async_deriveds: new Set(), async_deriveds: new Set(),
suspends_without_fallback: false,
hoisted_promises: new Map() hoisted_promises: new Map()
}; };
@ -709,8 +704,7 @@ export function analyze_component(root, source, options) {
expression: null, expression: null,
state_fields: new Map(), state_fields: new Map(),
function_depth: scope.function_depth, function_depth: scope.function_depth,
reactive_statement: null, reactive_statement: null
async_hoist_boundary: ast === template.ast ? ast : null
}; };
walk(/** @type {AST.SvelteNode} */ (ast), state, visitors); walk(/** @type {AST.SvelteNode} */ (ast), state, visitors);
@ -779,8 +773,7 @@ export function analyze_component(root, source, options) {
component_slots: new Set(), component_slots: new Set(),
expression: null, expression: null,
state_fields: new Map(), state_fields: new Map(),
function_depth: scope.function_depth, function_depth: scope.function_depth
async_hoist_boundary: ast === template.ast ? ast : null
}; };
walk(/** @type {AST.SvelteNode} */ (ast), state, visitors); walk(/** @type {AST.SvelteNode} */ (ast), state, visitors);

@ -11,14 +11,6 @@ export interface AnalysisState {
fragment: AST.Fragment | null; fragment: AST.Fragment | null;
title: AST.TitleElement | null; title: AST.TitleElement | null;
boundary: AST.SvelteBoundary | null; boundary: AST.SvelteBoundary | null;
/**
* The "anchor" fragment for any hoisted promises. This is the root fragment when
* walking starts and until another boundary fragment is encountered, like a
* consequent or alternate of an `#if` or `#each` block. When this fragment is emitted
* during server transformation, the promise expressions will be hoisted out of the fragment
* and placed right above it in an array.
*/
async_hoist_boundary: AST.Fragment | null;
/** /**
* Tag name of the parent element. `null` if the parent is `svelte:element`, `#snippet`, a component or the root. * Tag name of the parent element. `null` if the parent is `svelte:element`, `#snippet`, a component or the root.
* Parent doesn't necessarily mean direct path predecessor because there could be `#each`, `#if` etc in-between. * Parent doesn't necessarily mean direct path predecessor because there could be `#each`, `#if` etc in-between.

@ -44,22 +44,7 @@ export function AwaitBlock(node, context) {
// this one doesn't get the new state because it still hoists to the existing scope // this one doesn't get the new state because it still hoists to the existing scope
context.visit(node.expression, { ...context.state, expression: node.metadata.expression }); context.visit(node.expression, { ...context.state, expression: node.metadata.expression });
if (node.pending) { if (node.pending) context.visit(node.pending);
context.visit(node.pending, { if (node.then) context.visit(node.then);
...context.state, if (node.catch) context.visit(node.catch);
async_hoist_boundary: node.pending
});
}
if (node.then) {
context.visit(node.then, {
...context.state,
async_hoist_boundary: node.then
});
}
if (node.catch) {
context.visit(node.catch, {
...context.state,
async_hoist_boundary: node.catch
});
}
} }

@ -17,17 +17,11 @@ export function AwaitExpression(node, context) {
context.state.fragment.metadata.has_await = true; context.state.fragment.metadata.has_await = true;
} }
if (context.state.async_hoist_boundary) { if (context.state.fragment) {
const len = context.state.async_hoist_boundary.metadata.hoisted_promises.promises.push( const len = context.state.fragment.metadata.hoisted_promises.promises.push(node.argument);
node.argument
);
context.state.analysis.hoisted_promises.set( context.state.analysis.hoisted_promises.set(
node.argument, node.argument,
b.member( b.member(context.state.fragment.metadata.hoisted_promises.id, b.literal(len - 1), true)
b.id(context.state.async_hoist_boundary.metadata.hoisted_promises.name),
b.literal(len - 1),
true
)
); );
} }
@ -48,8 +42,6 @@ export function AwaitExpression(node, context) {
if (!context.state.analysis.runes) { if (!context.state.analysis.runes) {
e.legacy_await_invalid(node); e.legacy_await_invalid(node);
} }
context.state.analysis.suspends_without_fallback ||= !context.state.boundary?.metadata.pending;
} }
context.next(); context.next();

@ -35,16 +35,10 @@ export function EachBlock(node, context) {
scope: /** @type {Scope} */ (context.state.scope.parent) scope: /** @type {Scope} */ (context.state.scope.parent)
}); });
context.visit(node.body, { context.visit(node.body);
...context.state,
async_hoist_boundary: node.body
});
if (node.key) context.visit(node.key); if (node.key) context.visit(node.key);
if (node.fallback) { if (node.fallback) {
context.visit(node.fallback, { context.visit(node.fallback);
...context.state,
async_hoist_boundary: node.fallback
});
} }
if (!context.state.analysis.runes) { if (!context.state.analysis.runes) {

@ -7,4 +7,16 @@
*/ */
export function Fragment(node, context) { export function Fragment(node, context) {
context.next({ ...context.state, fragment: node }); context.next({ ...context.state, fragment: node });
// TODO this indicates whether the fragment contains an `await` expression (not inside
// a child fragment), which is necessary for ensuring that a `SnippetBlock` creates an
// async function in SSR. It feels like this is probably duplicative, but it's late
// and it works, so for now I'm doing it like this
node.metadata.is_async = node.metadata.hoisted_promises.promises.length > 0;
if (node.metadata.hoisted_promises.promises.length === 1) {
// if there's only one promise in this fragment, we don't need to de-waterfall it
context.state.analysis.hoisted_promises.delete(node.metadata.hoisted_promises.promises[0]);
node.metadata.hoisted_promises.promises.length = 0;
}
} }

@ -22,14 +22,8 @@ export function IfBlock(node, context) {
expression: node.metadata.expression expression: node.metadata.expression
}); });
context.visit(node.consequent, { context.visit(node.consequent);
...context.state,
async_hoist_boundary: node.consequent
});
if (node.alternate) { if (node.alternate) {
context.visit(node.alternate, { context.visit(node.alternate);
...context.state,
async_hoist_boundary: node.alternate
});
} }
} }

@ -17,9 +17,5 @@ export function KeyBlock(node, context) {
mark_subtree_dynamic(context.path); mark_subtree_dynamic(context.path);
context.visit(node.expression, { ...context.state, expression: node.metadata.expression }); context.visit(node.expression, { ...context.state, expression: node.metadata.expression });
context.visit(node.fragment);
context.visit(node.fragment, {
...context.state,
async_hoist_boundary: node.fragment
});
} }

@ -23,11 +23,7 @@ export function SnippetBlock(node, context) {
} }
} }
context.next({ context.next({ ...context.state, parent_element: null });
...context.state,
parent_element: null,
async_hoist_boundary: node.body
});
const can_hoist = const can_hoist =
context.path.length === 1 && context.path.length === 1 &&

@ -34,9 +34,5 @@ export function SvelteBoundary(node, context) {
) )
) ?? null; ) ?? null;
context.next({ context.next({ ...context.state, boundary: node });
...context.state,
boundary: node,
async_hoist_boundary: node.fragment
});
} }

@ -238,16 +238,15 @@ export function server_component(analysis, options) {
template.body.push(b.stmt(b.call('$.bind_props', b.id('$$props'), b.object(props)))); template.body.push(b.stmt(b.call('$.bind_props', b.id('$$props'), b.object(props))));
} }
const component_block = b.block([ let component_block = b.block([
call_child_payload( .../** @type {Statement[]} */ (instance.body),
b.block([ .../** @type {Statement[]} */ (template.body)
.../** @type {Statement[]} */ (instance.body),
.../** @type {Statement[]} */ (template.body)
]),
analysis.suspends_without_fallback
)
]); ]);
if (analysis.instance.has_await) {
component_block = b.block([call_child_payload(component_block, true)]);
}
// trick esrap into including comments // trick esrap into including comments
component_block.loc = instance.loc; component_block.loc = instance.loc;

@ -1,4 +1,5 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { Statement } from 'estree' */
/** @import { ComponentContext, ComponentServerTransformState } from '../types.js' */ /** @import { ComponentContext, ComponentServerTransformState } from '../types.js' */
import { clean_nodes, infer_namespace } from '../../utils.js'; import { clean_nodes, infer_namespace } from '../../utils.js';
import * as b from '#compiler/builders'; import * as b from '#compiler/builders';
@ -47,15 +48,20 @@ export function Fragment(node, context) {
process_children(trimmed, { ...context, state }); process_children(trimmed, { ...context, state });
if (node.metadata.hoisted_promises.promises.length > 0) { if (node.metadata.is_async) {
return b.block([ /** @type {Statement[]} */
b.const( const statements = [];
node.metadata.hoisted_promises.name,
b.array(node.metadata.hoisted_promises.promises) if (node.metadata.hoisted_promises.promises.length > 0) {
), statements.push(
...state.init, b.const(node.metadata.hoisted_promises.id, b.array(node.metadata.hoisted_promises.promises))
call_child_payload(b.block(build_template(state.template)), true) );
]); }
statements.push(...state.init);
statements.push(...build_template(state.template));
return b.block([call_child_payload(b.block(statements), true)]);
} }
return b.block([...state.init, ...build_template(state.template)]); return b.block([...state.init, ...build_template(state.template)]);

@ -78,6 +78,7 @@ export function RegularElement(node, context) {
} }
let select_with_value = false; let select_with_value = false;
let select_with_value_async = false;
const template_start = state.template.length; const template_start = state.template.length;
if (node.name === 'select') { if (node.name === 'select') {
@ -86,8 +87,12 @@ export function RegularElement(node, context) {
(attribute.type === 'Attribute' || attribute.type === 'BindDirective') && (attribute.type === 'Attribute' || attribute.type === 'BindDirective') &&
attribute.name === 'value' attribute.name === 'value'
); );
if (node.attributes.some((attribute) => attribute.type === 'SpreadAttribute')) {
const spread = node.attributes.find((attribute) => attribute.type === 'SpreadAttribute');
if (spread) {
select_with_value = true; select_with_value = true;
select_with_value_async ||= spread.metadata.expression.has_await;
state.template.push( state.template.push(
b.stmt( b.stmt(
b.assignment( b.assignment(
@ -113,6 +118,13 @@ export function RegularElement(node, context) {
); );
} else if (value) { } else if (value) {
select_with_value = true; select_with_value = true;
if (value.type === 'Attribute' && value.value !== true) {
select_with_value_async ||= (Array.isArray(value.value) ? value.value : [value.value]).some(
(tag) => tag.type === 'ExpressionTag' && tag.metadata.expression.has_await
);
}
const left = b.id('$$payload.local.select_value'); const left = b.id('$$payload.local.select_value');
if (value.type === 'Attribute') { if (value.type === 'Attribute') {
state.template.push( state.template.push(
@ -164,7 +176,7 @@ export function RegularElement(node, context) {
b.arrow( b.arrow(
[b.id('$$payload')], [b.id('$$payload')],
b.block([...inner_state.init, ...build_template(inner_state.template)]), b.block([...inner_state.init, ...build_template(inner_state.template)]),
context.state.analysis.suspends_without_fallback node.fragment.metadata.is_async
) )
) )
) )
@ -192,7 +204,11 @@ export function RegularElement(node, context) {
) )
); );
} else { } else {
process_children(trimmed, { ...context, state }); if (node.fragment.metadata.is_async) {
state.template.push(/** @type {Statement} */ (context.visit(node.fragment)));
} else {
process_children(trimmed, { ...context, state });
}
} }
if (select_with_value) { if (select_with_value) {
@ -208,7 +224,7 @@ export function RegularElement(node, context) {
// TODO this will always produce correct results (because it will produce an async function if the surrounding component is async) // TODO this will always produce correct results (because it will produce an async function if the surrounding component is async)
// but it will false-positive and create unnecessary async functions (eg. when the component is async but the select element is not) // but it will false-positive and create unnecessary async functions (eg. when the component is async but the select element is not)
// we could probably optimize by checking if the select element is async. Might be worth it. // we could probably optimize by checking if the select element is async. Might be worth it.
context.state.analysis.suspends_without_fallback select_with_value_async
) )
); );
} }

@ -12,7 +12,8 @@ export function SnippetBlock(node, context) {
let fn = b.function_declaration( let fn = b.function_declaration(
node.expression, node.expression,
[b.id('$$payload'), ...node.parameters], [b.id('$$payload'), ...node.parameters],
/** @type {BlockStatement} */ (context.visit(node.body)) /** @type {BlockStatement} */ (context.visit(node.body)),
node.body.metadata.is_async
); );
// @ts-expect-error - TODO remove this hack once $$render_inner for legacy bindings is gone // @ts-expect-error - TODO remove this hack once $$render_inner for legacy bindings is gone

@ -15,9 +15,7 @@ export function SvelteHead(node, context) {
b.call( b.call(
'$.head', '$.head',
b.id('$$payload'), b.id('$$payload'),
// same thing as elsewhere; this will create more async functions than necessary but should never be _wrong_ b.arrow([b.id('$$payload')], block, node.fragment.metadata.is_sync)
// because the component rendering this head block will always be async if the head block is async
b.arrow([b.id('$$payload')], block, context.state.analysis.suspends_without_fallback)
) )
) )
); );

@ -8,6 +8,15 @@ import { process_children, build_template, call_child_payload } from './shared/u
* @param {ComponentContext} context * @param {ComponentContext} context
*/ */
export function TitleElement(node, context) { export function TitleElement(node, context) {
if (node.fragment.metadata.hoisted_promises.promises.length > 0) {
context.state.init.push(
b.const(
node.fragment.metadata.hoisted_promises.name,
b.array(node.fragment.metadata.hoisted_promises.promises)
)
);
}
// title is guaranteed to contain only text/expression tag children // title is guaranteed to contain only text/expression tag children
const template = [b.literal('<title>')]; const template = [b.literal('<title>')];
process_children(node.fragment.nodes, { ...context, state: { ...context.state, template } }); process_children(node.fragment.nodes, { ...context, state: { ...context.state, template } });

@ -233,9 +233,7 @@ export function build_inline_component(node, expression, context) {
// if the current component is an async component, but it may produce async functions where they're // if the current component is an async component, but it may produce async functions where they're
// not necessary -- eg. when the component is asynchronous but the child content is not. // not necessary -- eg. when the component is asynchronous but the child content is not.
// May or may not be worth optimizing. // May or may not be worth optimizing.
b.block([ b.block([call_child_payload(b.block(block.body), node.fragment.metadata.is_async)])
call_child_payload(b.block(block.body), context.state.analysis.suspends_without_fallback)
])
); );
if (slot_name === 'default' && !has_children_prop) { if (slot_name === 'default' && !has_children_prop) {

@ -107,8 +107,6 @@ export interface ComponentAnalysis extends Analysis {
* Every snippet that is declared locally * Every snippet that is declared locally
*/ */
snippets: Set<AST.SnippetBlock>; snippets: Set<AST.SnippetBlock>;
/** Whether the component uses `await` in a context that causes suspense outside of any boundary with a pending snippet. */
suspends_without_fallback: boolean;
hoisted_promises: Map<Expression, MemberExpression>; hoisted_promises: Map<Expression, MemberExpression>;
} }

@ -57,7 +57,7 @@ export namespace AST {
*/ */
dynamic: boolean; dynamic: boolean;
has_await: boolean; has_await: boolean;
hoisted_promises: { name: string; promises: Expression[] }; hoisted_promises: { id: Identifier; promises: Expression[] };
}; };
} }

@ -2,32 +2,26 @@ import * as $ from 'svelte/internal/server';
export default function Async_each_fallback_hoisting($$payload) { export default function Async_each_fallback_hoisting($$payload) {
$$payload.child(async ($$payload) => { $$payload.child(async ($$payload) => {
const promises = [Promise.resolve([])]; const each_array = $.ensure_array_like(await Promise.resolve([]));
const each_array = $.ensure_array_like(await promises[0]);
$$payload.child(async ($$payload) => { if (each_array.length !== 0) {
if (each_array.length !== 0) { $$payload.push('<!--[-->');
$$payload.push('<!--[-->');
for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
let item = each_array[$$index]; let item = each_array[$$index];
const promises_1 = [Promise.reject('This should never be reached')];
$$payload.child(async ($$payload) => {
$$payload.push(`<!---->${$.escape(await promises_1[0])}`);
});
}
} else {
$$payload.push('<!--[!-->');
const promises_2 = [Promise.resolve(4)];
$$payload.child(async ($$payload) => { $$payload.child(async ($$payload) => {
$$payload.push(`<!---->${$.escape(await promises_2[0])}`); $$payload.push(`<!---->${$.escape(await Promise.reject('This should never be reached'))}`);
}); });
} }
} else {
$$payload.push('<!--[!-->');
$$payload.child(async ($$payload) => {
$$payload.push(`<!---->${$.escape(await Promise.resolve(4))}`);
});
}
$$payload.push(`<!--]-->`); $$payload.push(`<!--]-->`);
});
}); });
} }

@ -1,26 +1,23 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
export default function Async_each_hoisting($$payload) { export default function Async_each_hoisting($$payload) {
const first = Promise.resolve(1);
const second = Promise.resolve(2);
const third = Promise.resolve(3);
$$payload.child(async ($$payload) => { $$payload.child(async ($$payload) => {
const first = Promise.resolve(1); const each_array = $.ensure_array_like(await Promise.resolve([first, second, third]));
const second = Promise.resolve(2);
const third = Promise.resolve(3);
const promises = [Promise.resolve([first, second, third])];
const each_array = $.ensure_array_like(await promises[0]);
$$payload.child(async ($$payload) => { $$payload.push(`<!--[-->`);
$$payload.push(`<!--[-->`);
for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
let item = each_array[$$index]; let item = each_array[$$index];
const promises_1 = [item];
$$payload.child(async ($$payload) => { $$payload.child(async ($$payload) => {
$$payload.push(`<!---->${$.escape(await promises_1[0])}`); $$payload.push(`<!---->${$.escape(await item)}`);
}); });
} }
$$payload.push(`<!--]-->`); $$payload.push(`<!--]-->`);
});
}); });
} }

@ -2,28 +2,20 @@ import * as $ from 'svelte/internal/server';
export default function Async_if_alternate_hoisting($$payload) { export default function Async_if_alternate_hoisting($$payload) {
$$payload.child(async ($$payload) => { $$payload.child(async ($$payload) => {
const promises = [Promise.resolve(false)]; if (await Promise.resolve(false)) {
$$payload.push('<!--[-->');
$$payload.child(async ($$payload) => { $$payload.child(async ($$payload) => {
if (await promises[0]) { $$payload.push(`${$.escape(await Promise.reject('no no no'))}`);
$$payload.push('<!--[-->'); });
} else {
$$payload.push('<!--[!-->');
const promises_1 = [Promise.reject('no no no')]; $$payload.child(async ($$payload) => {
$$payload.push(`${$.escape(await Promise.resolve('yes yes yes'))}`);
});
}
$$payload.child(async ($$payload) => { $$payload.push(`<!--]-->`);
$$payload.push(`${$.escape(await promises_1[0])}`);
});
} else {
$$payload.push('<!--[!-->');
const promises_2 = [Promise.resolve('yes yes yes')];
$$payload.child(async ($$payload) => {
$$payload.push(`${$.escape(await promises_2[0])}`);
});
}
$$payload.push(`<!--]-->`);
});
}); });
} }

@ -2,28 +2,20 @@ import * as $ from 'svelte/internal/server';
export default function Async_if_hoisting($$payload) { export default function Async_if_hoisting($$payload) {
$$payload.child(async ($$payload) => { $$payload.child(async ($$payload) => {
const promises = [Promise.resolve(true)]; if (await Promise.resolve(true)) {
$$payload.push('<!--[-->');
$$payload.child(async ($$payload) => { $$payload.child(async ($$payload) => {
if (await promises[0]) { $$payload.push(`${$.escape(await Promise.resolve('yes yes yes'))}`);
$$payload.push('<!--[-->'); });
} else {
$$payload.push('<!--[!-->');
const promises_1 = [Promise.resolve('yes yes yes')]; $$payload.child(async ($$payload) => {
$$payload.push(`${$.escape(await Promise.reject('no no no'))}`);
});
}
$$payload.child(async ($$payload) => { $$payload.push(`<!--]-->`);
$$payload.push(`${$.escape(await promises_1[0])}`);
});
} else {
$$payload.push('<!--[!-->');
const promises_2 = [Promise.reject('no no no')];
$$payload.child(async ($$payload) => {
$$payload.push(`${$.escape(await promises_2[0])}`);
});
}
$$payload.push(`<!--]-->`);
});
}); });
} }

@ -1,16 +1,14 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
export default function Await_block_scope($$payload) { export default function Await_block_scope($$payload) {
$$payload.child(($$payload) => { let counter = { count: 0 };
let counter = { count: 0 }; const promise = Promise.resolve(counter);
const promise = Promise.resolve(counter);
function increment() { function increment() {
counter.count += 1; counter.count += 1;
} }
$$payload.push(`<button>clicks: ${$.escape(counter.count)}</button> `); $$payload.push(`<button>clicks: ${$.escape(counter.count)}</button> `);
$.await($$payload, promise, () => {}, (counter) => {}); $.await($$payload, promise, () => {}, (counter) => {});
$$payload.push(`<!--]--> ${$.escape(counter.count)}`); $$payload.push(`<!--]--> ${$.escape(counter.count)}`);
});
} }

@ -6,33 +6,31 @@ function snippet($$payload) {
} }
export default function Bind_component_snippet($$payload) { export default function Bind_component_snippet($$payload) {
$$payload.child(($$payload) => { let value = '';
let value = ''; const _snippet = snippet;
const _snippet = snippet; let $$settled = true;
let $$settled = true; let $$inner_payload;
let $$inner_payload;
function $$render_inner($$payload) { function $$render_inner($$payload) {
TextInput($$payload, { TextInput($$payload, {
get value() { get value() {
return value; return value;
}, },
set value($$value) { set value($$value) {
value = $$value; value = $$value;
$$settled = false; $$settled = false;
} }
}); });
$$payload.push(`<!----> value: ${$.escape(value)}`); $$payload.push(`<!----> value: ${$.escape(value)}`);
} }
do { do {
$$settled = true; $$settled = true;
$$inner_payload = $$payload.copy(); $$inner_payload = $$payload.copy();
$$render_inner($$inner_payload); $$render_inner($$inner_payload);
} while (!$$settled); } while (!$$settled);
$$payload.subsume($$inner_payload); $$payload.subsume($$inner_payload);
});
} }

@ -1,7 +1,5 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
export default function Bind_this($$payload) { export default function Bind_this($$payload) {
$$payload.child(($$payload) => { Foo($$payload, {});
Foo($$payload, {});
});
} }

@ -3,38 +3,36 @@ import * as $ from 'svelte/internal/server';
export default function Class_state_field_constructor_assignment($$payload, $$props) { export default function Class_state_field_constructor_assignment($$payload, $$props) {
$.push(); $.push();
$$payload.child(($$payload) => { class Foo {
class Foo { a = 0;
a = 0; #b;
#b; #foo = $.derived(() => ({ bar: this.a * 2 }));
#foo = $.derived(() => ({ bar: this.a * 2 }));
get foo() {
get foo() { return this.#foo();
return this.#foo(); }
}
set foo($$value) {
set foo($$value) { return this.#foo($$value);
return this.#foo($$value); }
}
#bar = $.derived(() => ({ baz: this.foo }));
#bar = $.derived(() => ({ baz: this.foo }));
get bar() {
get bar() { return this.#bar();
return this.#bar(); }
}
set bar($$value) {
set bar($$value) { return this.#bar($$value);
return this.#bar($$value); }
}
constructor() {
constructor() { this.a = 1;
this.a = 1; this.#b = 2;
this.#b = 2; this.foo.bar = 3;
this.foo.bar = 3; this.bar = 4;
this.bar = 4;
}
} }
}); }
$.pop(); $.pop();
} }

@ -1,15 +1,13 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
export default function Delegated_locally_declared_shadowed($$payload) { export default function Delegated_locally_declared_shadowed($$payload) {
$$payload.child(($$payload) => { const each_array = $.ensure_array_like({ length: 1 });
const each_array = $.ensure_array_like({ length: 1 });
$$payload.push(`<!--[-->`); $$payload.push(`<!--[-->`);
for (let index = 0, $$length = each_array.length; index < $$length; index++) { for (let index = 0, $$length = each_array.length; index < $$length; index++) {
$$payload.push(`<button type="button"${$.attr('data-index', index)}>B</button>`); $$payload.push(`<button type="button"${$.attr('data-index', index)}>B</button>`);
} }
$$payload.push(`<!--]-->`); $$payload.push(`<!--]-->`);
});
} }

@ -1,10 +1,10 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
export default function Main($$payload) { export default function Main($$payload) {
$$payload.child(($$payload) => { // needs to be a snapshot test because jsdom does auto-correct the attribute casing
let x = 'test'; let x = 'test';
let y = () => 'test';
$$payload.push(`<div${$.attr('foobar', x)}></div> <svg${$.attr('viewBox', x)}></svg> <custom-element${$.attr('foobar', x)}></custom-element> <div${$.attr('foobar', y())}></div> <svg${$.attr('viewBox', y())}></svg> <custom-element${$.attr('foobar', y())}></custom-element>`); let y = () => 'test';
});
$$payload.push(`<div${$.attr('foobar', x)}></div> <svg${$.attr('viewBox', x)}></svg> <custom-element${$.attr('foobar', x)}></custom-element> <div${$.attr('foobar', y())}></div> <svg${$.attr('viewBox', y())}></svg> <custom-element${$.attr('foobar', y())}></custom-element>`);
} }

@ -1,15 +1,13 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
export default function Each_index_non_null($$payload) { export default function Each_index_non_null($$payload) {
$$payload.child(($$payload) => { const each_array = $.ensure_array_like(Array(10));
const each_array = $.ensure_array_like(Array(10));
$$payload.push(`<!--[-->`); $$payload.push(`<!--[-->`);
for (let i = 0, $$length = each_array.length; i < $$length; i++) { for (let i = 0, $$length = each_array.length; i < $$length; i++) {
$$payload.push(`<p>index: ${$.escape(i)}</p>`); $$payload.push(`<p>index: ${$.escape(i)}</p>`);
} }
$$payload.push(`<!--]-->`); $$payload.push(`<!--]-->`);
});
} }

@ -1,17 +1,15 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
export default function Each_string_template($$payload) { export default function Each_string_template($$payload) {
$$payload.child(($$payload) => { const each_array = $.ensure_array_like(['foo', 'bar', 'baz']);
const each_array = $.ensure_array_like(['foo', 'bar', 'baz']);
$$payload.push(`<!--[-->`); $$payload.push(`<!--[-->`);
for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
let thing = each_array[$$index]; let thing = each_array[$$index];
$$payload.push(`<!---->${$.escape(thing)}, `); $$payload.push(`<!---->${$.escape(thing)}, `);
} }
$$payload.push(`<!--]-->`); $$payload.push(`<!--]-->`);
});
} }

@ -1,27 +1,25 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
export default function Function_prop_no_getter($$payload) { export default function Function_prop_no_getter($$payload) {
$$payload.child(($$payload) => { let count = 0;
let count = 0;
function onmouseup() { function onmouseup() {
count += 2; count += 2;
} }
const plusOne = (num) => num + 1; const plusOne = (num) => num + 1;
Button($$payload, { Button($$payload, {
onmousedown: () => count += 1, onmousedown: () => count += 1,
onmouseup, onmouseup,
onmouseenter: () => count = plusOne(count), onmouseenter: () => count = plusOne(count),
children: ($$payload) => { children: ($$payload) => {
$$payload.child(($$payload) => { $$payload.child(($$payload) => {
$$payload.push(`<!---->clicks: ${$.escape(count)}`); $$payload.push(`<!---->clicks: ${$.escape(count)}`);
}); });
}, },
$$slots: { default: true } $$slots: { default: true }
});
}); });
} }

@ -1,7 +1,5 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
export default function Functional_templating($$payload) { export default function Functional_templating($$payload) {
$$payload.child(($$payload) => { $$payload.push(`<h1>hello</h1> <div class="potato"><p>child element</p> <p>another child element</p></div>`);
$$payload.push(`<h1>hello</h1> <div class="potato"><p>child element</p> <p>another child element</p></div>`);
});
} }

@ -1,7 +1,5 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
export default function Hello_world($$payload) { export default function Hello_world($$payload) {
$$payload.child(($$payload) => { $$payload.push(`<h1>hello world</h1>`);
$$payload.push(`<h1>hello world</h1>`);
});
} }

@ -1,7 +1,5 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
export default function Hmr($$payload) { export default function Hmr($$payload) {
$$payload.child(($$payload) => { $$payload.push(`<h1>hello world</h1>`);
$$payload.push(`<h1>hello world</h1>`);
});
} }

@ -1,6 +1,4 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
import { random } from './module.svelte'; import { random } from './module.svelte';
export default function Imports_in_modules($$payload) { export default function Imports_in_modules($$payload) {}
$$payload.child(($$payload) => {});
}

@ -1,10 +1,8 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
export default function Nullish_coallescence_omittance($$payload) { export default function Nullish_coallescence_omittance($$payload) {
$$payload.child(($$payload) => { let name = 'world';
let name = 'world'; let count = 0;
let count = 0;
$$payload.push(`<h1>Hello, world!</h1> <b>123</b> <button>Count is ${$.escape(count)}</button> <h1>Hello, world</h1>`); $$payload.push(`<h1>Hello, world!</h1> <b>123</b> <button>Count is ${$.escape(count)}</button> <h1>Hello, world</h1>`);
});
} }

@ -3,17 +3,14 @@ import * as $ from 'svelte/internal/server';
export default function Props_identifier($$payload, $$props) { export default function Props_identifier($$payload, $$props) {
$.push(); $.push();
$$payload.child(($$payload) => { let { $$slots, $$events, ...props } = $$props;
let { $$slots, $$events, ...props } = $$props;
props.a;
props[a];
props.a.b;
props.a.b = true;
props.a = true;
props[a] = true;
props;
});
props.a;
props[a];
props.a.b;
props.a.b = true;
props.a = true;
props[a] = true;
props;
$.pop(); $.pop();
} }

@ -1,9 +1,7 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
export default function Purity($$payload) { export default function Purity($$payload) {
$$payload.child(($$payload) => { $$payload.push(`<p>0</p> <p>${$.escape(location.href)}</p> `);
$$payload.push(`<p>0</p> <p>${$.escape(location.href)}</p> `); Child($$payload, { prop: encodeURIComponent('hello') });
Child($$payload, { prop: encodeURIComponent('hello') }); $$payload.push(`<!---->`);
$$payload.push(`<!---->`);
});
} }

@ -1,9 +1,7 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
export default function Skip_static_subtree($$payload, $$props) { export default function Skip_static_subtree($$payload, $$props) {
$$payload.child(($$payload) => { let { title, content } = $$props;
let { title, content } = $$props;
$$payload.push(`<header><nav><a href="/">Home</a> <a href="/away">Away</a></nav></header> <main><h1>${$.escape(title)}</h1> <div class="static"><p>we don't need to traverse these nodes</p></div> <p>or</p> <p>these</p> <p>ones</p> ${$.html(content)} <p>these</p> <p>trailing</p> <p>nodes</p> <p>can</p> <p>be</p> <p>completely</p> <p>ignored</p></main> <cant-skip><custom-elements with="attributes"></custom-elements></cant-skip> <div><input autofocus/></div> <div><source muted/></div> <select><option value="a"${$.maybe_selected($$payload, 'a')}>a</option></select> <img src="..." alt="" loading="lazy"/> <div><img src="..." alt="" loading="lazy"/></div>`); $$payload.push(`<header><nav><a href="/">Home</a> <a href="/away">Away</a></nav></header> <main><h1>${$.escape(title)}</h1> <div class="static"><p>we don't need to traverse these nodes</p></div> <p>or</p> <p>these</p> <p>ones</p> ${$.html(content)} <p>these</p> <p>trailing</p> <p>nodes</p> <p>can</p> <p>be</p> <p>completely</p> <p>ignored</p></main> <cant-skip><custom-elements with="attributes"></custom-elements></cant-skip> <div><input autofocus/></div> <div><source muted/></div> <select><option value="a"${$.maybe_selected($$payload, 'a')}>a</option></select> <img src="..." alt="" loading="lazy"/> <div><img src="..." alt="" loading="lazy"/></div>`);
});
} }

@ -1,17 +1,15 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
export default function State_proxy_literal($$payload) { export default function State_proxy_literal($$payload) {
$$payload.child(($$payload) => { let str = '';
let str = ''; let tpl = ``;
let tpl = ``;
function reset() { function reset() {
str = ''; str = '';
str = ``; str = ``;
tpl = ''; tpl = '';
tpl = ``; tpl = ``;
} }
$$payload.push(`<input${$.attr('value', str)}/> <input${$.attr('value', tpl)}/> <button>reset</button>`); $$payload.push(`<input${$.attr('value', str)}/> <input${$.attr('value', tpl)}/> <button>reset</button>`);
});
} }

@ -1,9 +1,7 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
export default function Svelte_element($$payload, $$props) { export default function Svelte_element($$payload, $$props) {
$$payload.child(($$payload) => { let { tag = 'hr' } = $$props;
let { tag = 'hr' } = $$props;
$.element($$payload, tag); $.element($$payload, tag);
});
} }

@ -1,18 +1,16 @@
import * as $ from 'svelte/internal/server'; import * as $ from 'svelte/internal/server';
export default function Text_nodes_deriveds($$payload) { export default function Text_nodes_deriveds($$payload) {
$$payload.child(($$payload) => { let count1 = 0;
let count1 = 0; let count2 = 0;
let count2 = 0;
function text1() { function text1() {
return count1; return count1;
} }
function text2() { function text2() {
return count2; return count2;
} }
$$payload.push(`<p>${$.escape(text1())}${$.escape(text2())}</p>`); $$payload.push(`<p>${$.escape(text1())}${$.escape(text2())}</p>`);
});
} }
Loading…
Cancel
Save