perf: fold SSR block-open markers into the branch's first push (#18712)

One fewer `$$renderer.push` per rendered branch
pull/18721/head
Mathias Picker 4 days ago committed by GitHub
parent c575b07bca
commit 14ec75c5fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
perf: fold SSR block-open markers into the branch's first push

@ -2,7 +2,13 @@
/** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */
import * as b from '#compiler/builders';
import { block_close, block_open, block_open_else, create_child_block } from './shared/utils.js';
import {
block_close,
block_open,
block_open_else,
create_child_block,
prepend_block_marker
} from './shared/utils.js';
/**
* @param {AST.EachBlock} node
@ -51,7 +57,7 @@ export function EachBlock(node, context) {
const fallback = /** @type {BlockStatement} */ (context.visit(node.fallback));
fallback.body.unshift(b.stmt(b.call(b.id('$$renderer.push'), block_open_else)));
prepend_block_marker(fallback, /** @type {string} */ (block_open_else.value));
statements.push(
b.if(

@ -2,7 +2,7 @@
/** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */
import * as b from '#compiler/builders';
import { block_close, create_child_block } from './shared/utils.js';
import { block_close, create_child_block, prepend_block_marker } from './shared/utils.js';
/**
* @param {AST.IfBlock} node
@ -10,7 +10,7 @@ import { block_close, create_child_block } from './shared/utils.js';
*/
export function IfBlock(node, context) {
const consequent = /** @type {BlockStatement} */ (context.visit(node.consequent));
consequent.body.unshift(b.stmt(b.call(b.id('$$renderer.push'), b.literal(`<!--[0-->`))));
prepend_block_marker(consequent, `<!--[0-->`);
/** @type {IfStatement} */
let if_statement = b.if(/** @type {Expression} */ (context.visit(node.test)), consequent);
@ -22,7 +22,7 @@ export function IfBlock(node, context) {
// Walk the else-if chain, flattening branches
for (const elseif of node.metadata.flattened ?? []) {
const branch = /** @type {BlockStatement} */ (context.visit(elseif.consequent));
branch.body.unshift(b.stmt(b.call(b.id('$$renderer.push'), b.literal(`<!--[${index++}-->`))));
prepend_block_marker(branch, `<!--[${index++}-->`);
current_if = current_if.alternate = b.if(
/** @type {Expression} */ (context.visit(elseif.test)),
@ -34,7 +34,7 @@ export function IfBlock(node, context) {
// Handle final else (or remaining async chain)
const final_alternate = alt ? /** @type {BlockStatement} */ (context.visit(alt)) : b.block([]);
final_alternate.body.unshift(b.stmt(b.call(b.id('$$renderer.push'), b.literal(`<!--[-1-->`))));
prepend_block_marker(final_alternate, `<!--[-1-->`);
current_if.alternate = final_alternate;
context.state.template.push(

@ -180,6 +180,36 @@ export function build_template(template) {
return statements;
}
/**
* Prepends a hydration marker (e.g. `<!--[0-->`) to a branch. The branch has already been
* turned into statements by `build_template`, so if it happens to start with a static
* `$$renderer.push(...)` we fold the marker into that call rather than emitting a second one.
* @param {BlockStatement} block
* @param {string} marker
*/
export function prepend_block_marker(block, marker) {
const first = block.body[0];
if (
first?.type === 'ExpressionStatement' &&
first.expression.type === 'CallExpression' &&
first.expression.callee.type === 'Identifier' &&
first.expression.callee.name === '$$renderer.push' &&
first.expression.arguments.length === 1 &&
first.expression.arguments[0].type === 'TemplateLiteral'
) {
const quasi = first.expression.arguments[0].quasis[0];
// markers never contain characters that need escaping in a template literal
quasi.value.cooked = marker + quasi.value.cooked;
quasi.value.raw = marker + quasi.value.raw;
return;
}
block.body.unshift(b.stmt(b.call(b.id('$$renderer.push'), b.literal(marker))));
}
/**
*
* @param {AST.Attribute['value']} value

@ -15,8 +15,7 @@ export default function Async_each_fallback_hoisting($$renderer) {
$$renderer.push(async () => $.escape(await Promise.reject('This should never be reached')));
}
} else {
$$renderer.push('<!--[!-->');
$$renderer.push(`<!---->`);
$$renderer.push(`<!--[!--><!---->`);
$$renderer.push(async () => $.escape(await Promise.resolve(4)));
}
});

@ -12,14 +12,11 @@ export default function Async_if_chain($$renderer) {
$$renderer.async_block([$$promises[0]], ($$renderer) => {
if (foo) {
$$renderer.push('<!--[0-->');
$$renderer.push(`foo`);
$$renderer.push(`<!--[0-->foo`);
} else if (bar) {
$$renderer.push('<!--[1-->');
$$renderer.push(`bar`);
$$renderer.push(`<!--[1-->bar`);
} else {
$$renderer.push('<!--[-1-->');
$$renderer.push(`else`);
$$renderer.push(`<!--[-1-->else`);
}
});
@ -27,21 +24,17 @@ export default function Async_if_chain($$renderer) {
$$renderer.async_block([$$promises[0]], async ($$renderer) => {
if ((await $.save(foo))()) {
$$renderer.push('<!--[0-->');
$$renderer.push(`foo`);
$$renderer.push(`<!--[0-->foo`);
} else if (bar) {
$$renderer.push('<!--[1-->');
$$renderer.push(`bar`);
$$renderer.push(`<!--[1-->bar`);
} else {
$$renderer.push('<!--[-1-->');
$$renderer.child_block(async ($$renderer) => {
if ((await $.save(baz))()) {
$$renderer.push('<!--[0-->');
$$renderer.push(`baz`);
$$renderer.push(`<!--[0-->baz`);
} else {
$$renderer.push('<!--[-1-->');
$$renderer.push(`else`);
$$renderer.push(`<!--[-1-->else`);
}
});
@ -53,21 +46,17 @@ export default function Async_if_chain($$renderer) {
$$renderer.async_block([$$promises[0]], async ($$renderer) => {
if ((await $.save(foo))() > 10) {
$$renderer.push('<!--[0-->');
$$renderer.push(`foo`);
$$renderer.push(`<!--[0-->foo`);
} else if (bar) {
$$renderer.push('<!--[1-->');
$$renderer.push(`bar`);
$$renderer.push(`<!--[1-->bar`);
} else {
$$renderer.push('<!--[-1-->');
$$renderer.async_block([$$promises[0]], async ($$renderer) => {
if ((await $.save(foo))() > 5) {
$$renderer.push('<!--[0-->');
$$renderer.push(`baz`);
$$renderer.push(`<!--[0-->baz`);
} else {
$$renderer.push('<!--[-1-->');
$$renderer.push(`else`);
$$renderer.push(`<!--[-1-->else`);
}
});
@ -78,31 +67,24 @@ export default function Async_if_chain($$renderer) {
$$renderer.push(`<!--]--> `);
if (simple1) {
$$renderer.push('<!--[0-->');
$$renderer.push(`foo`);
$$renderer.push(`<!--[0-->foo`);
} else if (simple2 > 10) {
$$renderer.push('<!--[1-->');
$$renderer.push(`bar`);
$$renderer.push(`<!--[1-->bar`);
} else if (complex1() * complex2 > 100) {
$$renderer.push('<!--[2-->');
$$renderer.push(`baz`);
$$renderer.push(`<!--[2-->baz`);
} else {
$$renderer.push('<!--[-1-->');
$$renderer.push(`else`);
$$renderer.push(`<!--[-1-->else`);
}
$$renderer.push(`<!--]--> `);
$$renderer.async_block([$$promises[0]], ($$renderer) => {
if (blocking() > 10) {
$$renderer.push('<!--[0-->');
$$renderer.push(`foo`);
$$renderer.push(`<!--[0-->foo`);
} else if (blocking() > 5) {
$$renderer.push('<!--[1-->');
$$renderer.push(`bar`);
$$renderer.push(`<!--[1-->bar`);
} else {
$$renderer.push('<!--[-1-->');
$$renderer.push(`else`);
$$renderer.push(`<!--[-1-->else`);
}
});

@ -4,18 +4,15 @@ export default function Dedupe_templates($$renderer, $$props) {
let { a, b } = $$props;
if (a) {
$$renderer.push('<!--[0-->');
$$renderer.push(`<p class="x">hello</p>`);
$$renderer.push(`<!--[0--><p class="x">hello</p>`);
} else {
$$renderer.push('<!--[-1-->');
$$renderer.push(`<p class="x">hello</p>`);
$$renderer.push(`<!--[-1--><p class="x">hello</p>`);
}
$$renderer.push(`<!--]--> `);
if (b) {
$$renderer.push('<!--[0-->');
$$renderer.push(`<p class="x">hello</p>`);
$$renderer.push(`<!--[0--><p class="x">hello</p>`);
} else {
$$renderer.push('<!--[-1-->');
}

@ -148,8 +148,7 @@ export default function Select_with_rich_content($$renderer) {
$$renderer.push(`<!--]--></select> <select>`);
if (show) {
$$renderer.push('<!--[0-->');
$$renderer.push(`<!--[-->`);
$$renderer.push(`<!--[0--><!--[-->`);
const each_array_4 = $.ensure_array_like(items);

Loading…
Cancel
Save