chore: reuse common templates (#9601)

#9589 - add comment and space as reusable templates to save a few bytes. We can definitely take this idea further, but this is a base to iterate from.

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
pull/9615/head
Rich Harris 2 years ago committed by GitHub
parent d83bd7f7c1
commit 0283e50070
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
---
'svelte': patch
---
chore: reuse common templates

@ -974,9 +974,6 @@ function create_block(parent, name, nodes, context) {
/** @type {import('estree').Statement | undefined} */ /** @type {import('estree').Statement | undefined} */
let close = undefined; let close = undefined;
/** @type {import('estree').Identifier | undefined} */
let id = undefined;
/** @type {import('../types').ComponentClientTransformState} */ /** @type {import('../types').ComponentClientTransformState} */
const state = { const state = {
...context.state, ...context.state,
@ -999,7 +996,7 @@ function create_block(parent, name, nodes, context) {
if (is_single_element) { if (is_single_element) {
const element = /** @type {import('#compiler').RegularElement} */ (trimmed[0]); const element = /** @type {import('#compiler').RegularElement} */ (trimmed[0]);
id = b.id(context.state.scope.generate(element.name)); const id = b.id(context.state.scope.generate(element.name));
context.visit(element, { context.visit(element, {
...state, ...state,
@ -1014,7 +1011,7 @@ function create_block(parent, name, nodes, context) {
body.push( body.push(
b.var( b.var(
id.name, id,
b.call( b.call(
'$.open', '$.open',
b.id('$$anchor'), b.id('$$anchor'),
@ -1028,15 +1025,30 @@ function create_block(parent, name, nodes, context) {
} else if (is_single_child_not_needing_template) { } else if (is_single_child_not_needing_template) {
context.visit(trimmed[0], state); context.visit(trimmed[0], state);
body.push(...state.init); body.push(...state.init);
} else { } else if (trimmed.length > 0) {
id = b.id(context.state.scope.generate('fragment')); const id = b.id(context.state.scope.generate('fragment'));
const node_id = b.id(context.state.scope.generate('node'));
process_children(trimmed, b.call('$.child_frag', id), { process_children(trimmed, node_id, {
...context, ...context,
state state
}); });
if (state.template.length > 0) { const template = state.template[0];
if (state.template.length === 1 && (template === ' ' || template === '<!>')) {
if (template === ' ') {
body.push(b.var(node_id, b.call('$.space', b.id('$$anchor'))), ...state.init);
close = b.stmt(b.call('$.close', b.id('$$anchor'), node_id));
} else {
body.push(
b.var(id, b.call('$.comment', b.id('$$anchor'))),
b.var(node_id, b.call('$.child_frag', id)),
...state.init
);
close = b.stmt(b.call('$.close_frag', b.id('$$anchor'), id));
}
} else {
const callee = namespace === 'svg' ? '$.svg_template' : '$.template'; const callee = namespace === 'svg' ? '$.svg_template' : '$.template';
state.hoisted.push( state.hoisted.push(
@ -1048,7 +1060,7 @@ function create_block(parent, name, nodes, context) {
body.push( body.push(
b.var( b.var(
id.name, id,
b.call( b.call(
'$.open_frag', '$.open_frag',
b.id('$$anchor'), b.id('$$anchor'),
@ -1056,12 +1068,14 @@ function create_block(parent, name, nodes, context) {
template_name template_name
) )
), ),
b.var(node_id, b.call('$.child_frag', id)),
...state.init ...state.init
); );
close = b.stmt(b.call('$.close_frag', b.id('$$anchor'), id)); close = b.stmt(b.call('$.close_frag', b.id('$$anchor'), id));
} else {
body.push(...state.init);
} }
} else {
body.push(...state.init);
} }
if (state.update.length > 0 || state.update_effects.length > 0) { if (state.update.length > 0 || state.update_effects.length > 0) {
@ -1359,13 +1373,11 @@ function process_children(nodes, parent, { visit, state }) {
state.template.push(' '); state.template.push(' ');
const name = state.scope.generate('text'); const text_id = get_node_id(expression, state, 'text');
state.init.push(b.var(name, expression));
const singular = b.stmt( const singular = b.stmt(
b.call( b.call(
'$.text_effect', '$.text_effect',
b.id(name), text_id,
b.thunk(/** @type {import('estree').Expression} */ (visit(node.expression))) b.thunk(/** @type {import('estree').Expression} */ (visit(node.expression)))
) )
); );
@ -1378,7 +1390,7 @@ function process_children(nodes, parent, { visit, state }) {
grouped: b.stmt( grouped: b.stmt(
b.call( b.call(
'$.text', '$.text',
b.id(name), text_id,
/** @type {import('estree').Expression} */ (visit(node.expression)) /** @type {import('estree').Expression} */ (visit(node.expression))
) )
) )
@ -1388,7 +1400,7 @@ function process_children(nodes, parent, { visit, state }) {
b.stmt( b.stmt(
b.assignment( b.assignment(
'=', '=',
b.id(`${name}.nodeValue`), b.member(text_id, b.id('nodeValue')),
b.call( b.call(
'$.stringify', '$.stringify',
/** @type {import('estree').Expression} */ (visit(node.expression)) /** @type {import('estree').Expression} */ (visit(node.expression))
@ -1403,17 +1415,16 @@ function process_children(nodes, parent, { visit, state }) {
state.template.push(' '); state.template.push(' ');
const name = state.scope.generate('text'); const text_id = get_node_id(expression, state, 'text');
const contains_call_expression = sequence.some( const contains_call_expression = sequence.some(
(n) => n.type === 'ExpressionTag' && n.metadata.contains_call_expression (n) => n.type === 'ExpressionTag' && n.metadata.contains_call_expression
); );
state.init.push(b.var(name, expression));
const assignment = serialize_template_literal(sequence, visit, state)[1]; const assignment = serialize_template_literal(sequence, visit, state)[1];
const init = b.stmt(b.assignment('=', b.id(`${name}.nodeValue`), assignment)); const init = b.stmt(b.assignment('=', b.member(text_id, b.id('nodeValue')), assignment));
const singular = b.stmt( const singular = b.stmt(
b.call( b.call(
'$.text_effect', '$.text_effect',
b.id(name), text_id,
b.thunk(serialize_template_literal(sequence, visit, state)[1]) b.thunk(serialize_template_literal(sequence, visit, state)[1])
) )
); );
@ -1426,13 +1437,13 @@ function process_children(nodes, parent, { visit, state }) {
) { ) {
state.update.push({ state.update.push({
singular, singular,
grouped: b.stmt(b.call('$.text', b.id(name), assignment)) grouped: b.stmt(b.call('$.text', text_id, assignment))
}); });
} else { } else {
state.init.push(init); state.init.push(init);
} }
expression = b.call('$.sibling', b.id(name)); expression = b.call('$.sibling', text_id);
} }
for (let i = 0; i < nodes.length; i += 1) { for (let i = 0; i < nodes.length; i += 1) {
@ -1456,9 +1467,6 @@ function process_children(nodes, parent, { visit, state }) {
// get hoisted inside clean_nodes? // get hoisted inside clean_nodes?
visit(node, state); visit(node, state);
} else { } else {
const name = state.scope.generate(node.type === 'RegularElement' ? node.name : 'node');
const id = b.id(name);
// Optimization path for each blocks. If the parent isn't a fragment and it only has // Optimization path for each blocks. If the parent isn't a fragment and it only has
// a single child, then we can classify the block as being "controlled". // a single child, then we can classify the block as being "controlled".
if ( if (
@ -1471,7 +1479,12 @@ function process_children(nodes, parent, { visit, state }) {
node.metadata.is_controlled = true; node.metadata.is_controlled = true;
visit(node, state); visit(node, state);
} else { } else {
state.init.push(b.var(name, expression)); const id = get_node_id(
expression,
state,
node.type === 'RegularElement' ? node.name : 'node'
);
expression = b.call('$.sibling', id); expression = b.call('$.sibling', id);
visit(node, { visit(node, {
@ -1488,6 +1501,22 @@ function process_children(nodes, parent, { visit, state }) {
} }
} }
/**
* @param {import('estree').Expression} expression
* @param {import('../types.js').ComponentClientTransformState} state
* @param {string} name
*/
function get_node_id(expression, state, name) {
let id = expression;
if (id.type !== 'Identifier') {
id = b.id(state.scope.generate(name));
state.init.push(b.var(id, expression));
}
return id;
}
/** /**
* @param {true | Array<import('#compiler').Text | import('#compiler').ExpressionTag>} attribute_value * @param {true | Array<import('#compiler').Text | import('#compiler').ExpressionTag>} attribute_value
* @param {import('../types').ComponentContext} context * @param {import('../types').ComponentContext} context

@ -2,6 +2,7 @@ import { DEV } from 'esm-env';
import { import {
append_child, append_child,
child, child,
child_frag,
clone_node, clone_node,
create_element, create_element,
init_operations, init_operations,
@ -133,7 +134,7 @@ export function svg_replace(node) {
* @param {boolean} is_fragment * @param {boolean} is_fragment
* @param {boolean} use_clone_node * @param {boolean} use_clone_node
* @param {null | Text | Comment | Element} anchor * @param {null | Text | Comment | Element} anchor
* @param {() => Element} [template_element_fn] * @param {() => Node} [template_element_fn]
* @returns {Element | DocumentFragment | Node[]} * @returns {Element | DocumentFragment | Node[]}
*/ */
function open_template(is_fragment, use_clone_node, anchor, template_element_fn) { function open_template(is_fragment, use_clone_node, anchor, template_element_fn) {
@ -156,7 +157,7 @@ function open_template(is_fragment, use_clone_node, anchor, template_element_fn)
/** /**
* @param {null | Text | Comment | Element} anchor * @param {null | Text | Comment | Element} anchor
* @param {boolean} use_clone_node * @param {boolean} use_clone_node
* @param {() => Element} [template_element_fn] * @param {() => Node} [template_element_fn]
* @returns {Element | DocumentFragment | Node[]} * @returns {Element | DocumentFragment | Node[]}
*/ */
/*#__NO_SIDE_EFFECTS__*/ /*#__NO_SIDE_EFFECTS__*/
@ -167,7 +168,7 @@ export function open(anchor, use_clone_node, template_element_fn) {
/** /**
* @param {null | Text | Comment | Element} anchor * @param {null | Text | Comment | Element} anchor
* @param {boolean} use_clone_node * @param {boolean} use_clone_node
* @param {() => Element} [template_element_fn] * @param {() => Node} [template_element_fn]
* @returns {Element | DocumentFragment | Node[]} * @returns {Element | DocumentFragment | Node[]}
*/ */
/*#__NO_SIDE_EFFECTS__*/ /*#__NO_SIDE_EFFECTS__*/
@ -175,6 +176,25 @@ export function open_frag(anchor, use_clone_node, template_element_fn) {
return open_template(true, use_clone_node, anchor, template_element_fn); return open_template(true, use_clone_node, anchor, template_element_fn);
} }
const space_template = template(' ', false);
const comment_template = template('<!>', true);
/**
* @param {null | Text | Comment | Element} anchor
*/
/*#__NO_SIDE_EFFECTS__*/
export function space(anchor) {
return open(anchor, true, space_template);
}
/**
* @param {null | Text | Comment | Element} anchor
*/
/*#__NO_SIDE_EFFECTS__*/
export function comment(anchor) {
return open_frag(anchor, true, comment_template);
}
/** /**
* @param {Element | Text} dom * @param {Element | Text} dom
* @param {boolean} is_fragment * @param {boolean} is_fragment

@ -3,9 +3,6 @@
import "svelte/internal/disclose-version"; import "svelte/internal/disclose-version";
import * as $ from "svelte/internal"; import * as $ from "svelte/internal";
var Button_default = $.template(` `, true);
var frag = $.template(`<!>`, true);
export default function Function_prop_no_getter($$anchor, $$props) { export default function Function_prop_no_getter($$anchor, $$props) {
$.push($$props, true); $.push($$props, true);
@ -16,7 +13,7 @@ export default function Function_prop_no_getter($$anchor, $$props) {
} }
/* Init */ /* Init */
var fragment = $.open_frag($$anchor, true, frag); var fragment = $.comment($$anchor);
var node = $.child_frag(fragment); var node = $.child_frag(fragment);
Button(node, { Button(node, {
@ -24,12 +21,11 @@ export default function Function_prop_no_getter($$anchor, $$props) {
onmouseup, onmouseup,
children: ($$anchor, $$slotProps) => { children: ($$anchor, $$slotProps) => {
/* Init */ /* Init */
var fragment_1 = $.open_frag($$anchor, true, Button_default); var node_1 = $.space($$anchor);
var text = $.child_frag(fragment_1);
/* Update */ /* Update */
$.text_effect(text, () => `clicks: ${$.stringify($.get(count))}`); $.text_effect(node_1, () => `clicks: ${$.stringify($.get(count))}`);
$.close_frag($$anchor, fragment_1); $.close($$anchor, node_1);
} }
}); });

Loading…
Cancel
Save