Skip to content

Commit 326e2b4

Browse files
authored
chore: simplify templates (#10954)
* WIP * WIP * fix * simplify * rename close to append * appease typescript * simplify * simplify * frag -> root * move logic to where it's used
1 parent faf838c commit 326e2b4

File tree

14 files changed

+168
-216
lines changed

14 files changed

+168
-216
lines changed

packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js

Lines changed: 23 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import {
3333
EACH_IS_STRICT_EQUALS,
3434
EACH_ITEM_REACTIVE,
3535
EACH_KEYED,
36+
TEMPLATE_FRAGMENT,
37+
TEMPLATE_USE_IMPORT_NODE,
3638
TRANSITION_GLOBAL,
3739
TRANSITION_IN,
3840
TRANSITION_OUT
@@ -929,9 +931,9 @@ function serialize_bind_this(bind_this, context, node) {
929931
* const block_name = $.template(`...`);
930932
*
931933
* // for the main block:
932-
* const id = $.open(block_name);
934+
* const id = block_name();
933935
* // init stuff and possibly render effect
934-
* $.close(id);
936+
* $.append($$anchor, id);
935937
* ```
936938
* Adds the hoisted parts to `context.state.hoisted` and returns the statements of the main block.
937939
* @param {import('#compiler').SvelteNode} parent
@@ -1001,25 +1003,19 @@ function create_block(parent, name, nodes, context) {
10011003
node: id
10021004
});
10031005

1004-
context.state.hoisted.push(
1005-
b.var(
1006-
template_name,
1007-
b.call(
1008-
get_template_function(namespace, state),
1009-
b.template([b.quasi(state.template.join(''), true)], [])
1010-
)
1011-
)
1012-
);
1013-
10141006
/** @type {import('estree').Expression[]} */
1015-
const args = [template_name];
1007+
const args = [b.template([b.quasi(state.template.join(''), true)], [])];
10161008

10171009
if (state.metadata.context.template_needs_import_node) {
1018-
args.push(b.false);
1010+
args.push(b.literal(TEMPLATE_USE_IMPORT_NODE));
10191011
}
10201012

1021-
body.push(b.var(id, b.call('$.open', ...args)), ...state.before_init, ...state.init);
1022-
close = b.stmt(b.call('$.close', b.id('$$anchor'), id));
1013+
context.state.hoisted.push(
1014+
b.var(template_name, b.call(get_template_function(namespace, state), ...args))
1015+
);
1016+
1017+
body.push(b.var(id, b.call(template_name)), ...state.before_init, ...state.init);
1018+
close = b.stmt(b.call('$.append', b.id('$$anchor'), id));
10231019
} else if (is_single_child_not_needing_template) {
10241020
context.visit(trimmed[0], state);
10251021
body.push(...state.before_init, ...state.init);
@@ -1040,7 +1036,7 @@ function create_block(parent, name, nodes, context) {
10401036
});
10411037

10421038
body.push(b.var(id, b.call('$.text', b.id('$$anchor'))), ...state.before_init, ...state.init);
1043-
close = b.stmt(b.call('$.close', b.id('$$anchor'), id));
1039+
close = b.stmt(b.call('$.append', b.id('$$anchor'), id));
10441040
} else {
10451041
/** @type {(is_text: boolean) => import('estree').Expression} */
10461042
const expression = (is_text) =>
@@ -1054,30 +1050,29 @@ function create_block(parent, name, nodes, context) {
10541050
// special case — we can use `$.comment` instead of creating a unique template
10551051
body.push(b.var(id, b.call('$.comment')));
10561052
} else {
1053+
let flags = TEMPLATE_FRAGMENT;
1054+
1055+
if (state.metadata.context.template_needs_import_node) {
1056+
flags |= TEMPLATE_USE_IMPORT_NODE;
1057+
}
1058+
10571059
state.hoisted.push(
10581060
b.var(
10591061
template_name,
10601062
b.call(
10611063
get_template_function(namespace, state),
10621064
b.template([b.quasi(state.template.join(''), true)], []),
1063-
b.true
1065+
b.literal(flags)
10641066
)
10651067
)
10661068
);
10671069

1068-
/** @type {import('estree').Expression[]} */
1069-
const args = [template_name];
1070-
1071-
if (state.metadata.context.template_needs_import_node) {
1072-
args.push(b.false);
1073-
}
1074-
1075-
body.push(b.var(id, b.call('$.open_frag', ...args)));
1070+
body.push(b.var(id, b.call(template_name)));
10761071
}
10771072

10781073
body.push(...state.before_init, ...state.init);
10791074

1080-
close = b.stmt(b.call('$.close_frag', b.id('$$anchor'), id));
1075+
close = b.stmt(b.call('$.append', b.id('$$anchor'), id));
10811076
}
10821077
} else {
10831078
body.push(...state.before_init, ...state.init);
@@ -1093,12 +1088,6 @@ function create_block(parent, name, nodes, context) {
10931088
// It's important that close is the last statement in the block, as any previous statements
10941089
// could contain element insertions into the template, which the close statement needs to
10951090
// know of when constructing the list of current inner elements.
1096-
1097-
if (context.path.length > 0) {
1098-
// this is a block — return DOM so it can be attached directly to the effect
1099-
close = b.return(close.expression);
1100-
}
1101-
11021091
body.push(close);
11031092
}
11041093

@@ -1566,7 +1555,7 @@ function serialize_template_literal(values, visit) {
15661555
/** @type {import('../types').ComponentVisitors} */
15671556
export const template_visitors = {
15681557
Fragment(node, context) {
1569-
const body = create_block(node, 'frag', node.nodes, context);
1558+
const body = create_block(node, 'root', node.nodes, context);
15701559
return b.block(body);
15711560
},
15721561
Comment(node, context) {

packages/svelte/src/constants.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ export const TRANSITION_IN = 1;
1616
export const TRANSITION_OUT = 1 << 1;
1717
export const TRANSITION_GLOBAL = 1 << 2;
1818

19+
export const TEMPLATE_FRAGMENT = 1;
20+
export const TEMPLATE_USE_IMPORT_NODE = 1 << 1;
21+
1922
/** List of Element events that will be delegated */
2023
export const DelegatedEvents = [
2124
'beforeinput',

packages/svelte/src/internal/client/dom/blocks/each.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
} from '../../../../constants.js';
99
import { hydrate_anchor, hydrate_nodes, hydrating, set_hydrating } from '../hydration.js';
1010
import { empty } from '../operations.js';
11-
import { insert, remove } from '../reconciler.js';
11+
import { remove } from '../reconciler.js';
1212
import { untrack } from '../../runtime.js';
1313
import {
1414
block,
@@ -389,7 +389,7 @@ function reconcile_tracked_array(array, state, anchor, render_fn, flags, keys) {
389389

390390
if (moved && index !== LIS_ITEM) {
391391
if (last_item !== undefined) anchor = get_first_child(last_item);
392-
insert(/** @type {import('#client').Dom} */ (item.e.dom), anchor);
392+
move(/** @type {import('#client').Dom} */ (item.e.dom), anchor);
393393
}
394394
}
395395

@@ -560,3 +560,17 @@ function create_item(anchor, value, key, index, render_fn, flags) {
560560
current_each_item = previous_each_item;
561561
}
562562
}
563+
564+
/**
565+
* @param {import('#client').Dom} current
566+
* @param {Text | Element | Comment} anchor
567+
*/
568+
function move(current, anchor) {
569+
if (is_array(current)) {
570+
for (var i = 0; i < current.length; i++) {
571+
anchor.before(current[i]);
572+
}
573+
} else {
574+
anchor.before(current);
575+
}
576+
}
Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import { render_effect } from '../../reactivity/effects.js';
2-
import { remove } from '../reconciler.js';
3-
import { untrack } from '../../runtime.js';
1+
import { branch, render_effect } from '../../reactivity/effects.js';
42

53
/**
64
* @template {(node: import('#client').TemplateNode, ...args: any[]) => import('#client').Dom} SnippetFn
@@ -11,19 +9,13 @@ import { untrack } from '../../runtime.js';
119
*/
1210
export function snippet(get_snippet, node, ...args) {
1311
/** @type {SnippetFn | null | undefined} */
14-
var snippet_fn;
12+
var snippet;
1513

1614
render_effect(() => {
17-
if (snippet_fn === (snippet_fn = get_snippet())) return;
15+
if (snippet === (snippet = get_snippet())) return;
1816

19-
if (snippet_fn) {
20-
// Untrack so we only rerender when the snippet function itself changes,
21-
// not when an eagerly-read prop inside the snippet function changes
22-
var dom = untrack(() => /** @type {SnippetFn} */ (snippet_fn)(node, ...args));
23-
24-
if (dom !== undefined) {
25-
return () => remove(dom);
26-
}
17+
if (snippet) {
18+
branch(() => /** @type {SnippetFn} */ (snippet)(node, ...args));
2719
}
2820
});
2921
}

packages/svelte/src/internal/client/dom/elements/custom-element.js

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createClassComponent } from '../../../../legacy/legacy-client.js';
22
import { destroy_effect, render_effect } from '../../reactivity/effects.js';
3-
import { open, close } from '../template.js';
3+
import { append } from '../template.js';
44
import { define_property } from '../../utils.js';
55

66
/**
@@ -98,14 +98,10 @@ if (typeof HTMLElement === 'function') {
9898
* @param {Element} anchor
9999
*/
100100
return (anchor) => {
101-
const node = open(() => {
102-
const slot = document.createElement('slot');
103-
if (name !== 'default') {
104-
slot.name = name;
105-
}
106-
return slot;
107-
});
108-
close(anchor, /** @type {Element} */ (node));
101+
const slot = document.createElement('slot');
102+
if (name !== 'default') slot.name = name;
103+
104+
append(anchor, slot);
109105
};
110106
}
111107
/** @type {Record<string, any>} */

packages/svelte/src/internal/client/dom/reconciler.js

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,41 +8,6 @@ export function create_fragment_from_html(html) {
88
return elem.content;
99
}
1010

11-
/**
12-
* Creating a document fragment from HTML that contains script tags will not execute
13-
* the scripts. We need to replace the script tags with new ones so that they are executed.
14-
* @param {string} html
15-
*/
16-
export function create_fragment_with_script_from_html(html) {
17-
var content = create_fragment_from_html(html);
18-
var scripts = content.querySelectorAll('script');
19-
for (const script of scripts) {
20-
var new_script = document.createElement('script');
21-
for (var i = 0; i < script.attributes.length; i++) {
22-
new_script.setAttribute(script.attributes[i].name, script.attributes[i].value);
23-
}
24-
new_script.textContent = script.textContent;
25-
/** @type {Node} */ (script.parentNode).replaceChild(new_script, script);
26-
}
27-
return content;
28-
}
29-
30-
/**
31-
* @param {import('#client').Dom} current
32-
* @param {Text | Element | Comment} sibling
33-
*/
34-
export function insert(current, sibling) {
35-
if (!current) return sibling;
36-
37-
if (is_array(current)) {
38-
for (var i = 0; i < current.length; i++) {
39-
sibling.before(/** @type {Node} */ (current[i]));
40-
}
41-
} else {
42-
sibling.before(/** @type {Node} */ (current));
43-
}
44-
}
45-
4611
/**
4712
* @param {import('#client').Dom} current
4813
*/

0 commit comments

Comments
 (0)