Skip to content

Commit dfde459

Browse files
committed
deduplicate stuff
1 parent fa8e1ef commit dfde459

File tree

19 files changed

+119
-161
lines changed

19 files changed

+119
-161
lines changed

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

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,10 @@
22
/** @import { Namespace } from '#compiler' */
33
/** @import { ComponentClientTransformState } from '../types.js' */
44
/** @import { Node } from './types.js' */
5+
import { TEMPLATE_USE_MATHML, TEMPLATE_USE_SVG } from '../../../../../constants.js';
56
import { dev, locator } from '../../../../state.js';
67
import * as b from '../../../../utils/builders.js';
78

8-
/**
9-
*
10-
* @param {Namespace} namespace
11-
* @param {ComponentClientTransformState} state
12-
* @returns
13-
*/
14-
function get_template_function(namespace, state) {
15-
return (
16-
namespace === 'svg'
17-
? '$.ns_template'
18-
: namespace === 'mathml'
19-
? '$.mathml_template'
20-
: '$.template'
21-
).concat(state.options.templatingMode === 'functional' ? '_fn' : '');
22-
}
23-
249
/**
2510
* @param {Node[]} nodes
2611
*/
@@ -50,14 +35,18 @@ function build_locations(nodes) {
5035
* @param {Namespace} namespace
5136
* @param {number} [flags]
5237
*/
53-
export function transform_template(state, namespace, flags) {
54-
const expression =
55-
state.options.templatingMode === 'functional'
56-
? state.template.as_objects()
57-
: state.template.as_string();
38+
export function transform_template(state, namespace, flags = 0) {
39+
const tree = state.options.templatingMode === 'functional';
40+
41+
const expression = tree ? state.template.as_tree() : state.template.as_html();
42+
43+
if (tree) {
44+
if (namespace === 'svg') flags |= TEMPLATE_USE_SVG;
45+
if (namespace === 'mathml') flags |= TEMPLATE_USE_MATHML;
46+
}
5847

5948
let call = b.call(
60-
get_template_function(namespace, state),
49+
tree ? `$.from_tree` : `$.from_${namespace}`,
6150
expression,
6251
flags ? b.literal(flags) : undefined
6352
);

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,11 @@ export class Template {
6969
/** @type {Element} */ (this.#element).attributes[key] = value;
7070
}
7171

72-
as_string() {
72+
as_html() {
7373
return b.template([b.quasi(this.nodes.map(stringify).join(''), true)], []);
7474
}
7575

76-
as_objects() {
76+
as_tree() {
7777
// if the first item is a comment we need to add another comment for effect.start
7878
if (this.nodes[0].type === 'comment') {
7979
this.nodes.unshift({ type: 'comment', data: undefined });

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export function Fragment(node, context) {
1717
// Creates a new block which looks roughly like this:
1818
// ```js
1919
// // hoisted:
20-
// const block_name = $.template(`...`);
20+
// const block_name = $.from_html(`...`);
2121
//
2222
// // for the main block:
2323
// const id = block_name();

packages/svelte/src/compiler/phases/3-transform/utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ export function clean_nodes(
324324
}
325325

326326
/**
327-
* Infers the namespace for the children of a node that should be used when creating the `$.template(...)`.
327+
* Infers the namespace for the children of a node that should be used when creating the fragment
328328
* @param {Namespace} namespace
329329
* @param {AST.SvelteNode} parent
330330
* @param {AST.SvelteNode[]} nodes

packages/svelte/src/constants.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export const TRANSITION_GLOBAL = 1 << 2;
1717

1818
export const TEMPLATE_FRAGMENT = 1;
1919
export const TEMPLATE_USE_IMPORT_NODE = 1 << 1;
20+
export const TEMPLATE_USE_SVG = 1 << 2;
21+
export const TEMPLATE_USE_MATHML = 1 << 3;
2022

2123
export const HYDRATION_START = '[';
2224
/** used to indicate that an `{:else}...` block was rendered */

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

Lines changed: 86 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ import {
1616
NAMESPACE_MATHML,
1717
NAMESPACE_SVG,
1818
TEMPLATE_FRAGMENT,
19-
TEMPLATE_USE_IMPORT_NODE
19+
TEMPLATE_USE_IMPORT_NODE,
20+
TEMPLATE_USE_MATHML,
21+
TEMPLATE_USE_SVG
2022
} from '../../../constants.js';
2123

2224
/**
@@ -37,7 +39,7 @@ export function assign_nodes(start, end) {
3739
* @returns {() => Node | Node[]}
3840
*/
3941
/*#__NO_SIDE_EFFECTS__*/
40-
export function template(content, flags) {
42+
export function from_html(content, flags) {
4143
var is_fragment = (flags & TEMPLATE_FRAGMENT) !== 0;
4244
var use_import_node = (flags & TEMPLATE_USE_IMPORT_NODE) !== 0;
4345

@@ -77,99 +79,6 @@ export function template(content, flags) {
7779
return clone;
7880
};
7981
}
80-
/**
81-
* @param {TemplateStructure[]} structure
82-
* @param {NAMESPACE_SVG | NAMESPACE_MATHML | undefined} [ns]
83-
*/
84-
function structure_to_fragment(structure, ns) {
85-
var fragment = create_fragment();
86-
87-
for (var item of structure) {
88-
if (typeof item === 'string') {
89-
fragment.append(create_text(item));
90-
continue;
91-
}
92-
93-
// if `preserveComments === true`, comments are represented as `['// <data>']`
94-
if (item === undefined || item[0][0] === '/') {
95-
fragment.append(create_comment(item ? item[0].slice(3) : ''));
96-
continue;
97-
}
98-
99-
const [name, attributes, ...children] = item;
100-
101-
const namespace = name === 'svg' ? NAMESPACE_SVG : name === 'math' ? NAMESPACE_MATHML : ns;
102-
103-
var element = create_element(name, namespace, attributes?.is);
104-
105-
for (var key in attributes) {
106-
set_attribute(element, key, attributes[key]);
107-
}
108-
109-
if (children.length > 0) {
110-
var target =
111-
element.tagName === 'TEMPLATE'
112-
? /** @type {HTMLTemplateElement} */ (element).content
113-
: element;
114-
115-
target.append(
116-
structure_to_fragment(children, element.tagName === 'foreignObject' ? undefined : namespace)
117-
);
118-
}
119-
120-
fragment.append(element);
121-
}
122-
123-
return fragment;
124-
}
125-
126-
/**
127-
* @param {Array<TemplateStructure>} structure
128-
* @param {number} flags
129-
* @returns {() => Node | Node[]}
130-
*/
131-
/*#__NO_SIDE_EFFECTS__*/
132-
export function template_fn(structure, flags) {
133-
var is_fragment = (flags & TEMPLATE_FRAGMENT) !== 0;
134-
var use_import_node = (flags & TEMPLATE_USE_IMPORT_NODE) !== 0;
135-
136-
/** @type {Node} */
137-
var node;
138-
139-
return () => {
140-
if (hydrating) {
141-
assign_nodes(hydrate_node, null);
142-
return hydrate_node;
143-
}
144-
145-
if (node === undefined) {
146-
node = structure_to_fragment(structure);
147-
if (!is_fragment) node = /** @type {Node} */ (get_first_child(node));
148-
}
149-
150-
var clone = /** @type {TemplateNode} */ (
151-
use_import_node || is_firefox ? document.importNode(node, true) : node.cloneNode(true)
152-
);
153-
154-
if (is_fragment) {
155-
var start = /** @type {TemplateNode} */ (get_first_child(clone));
156-
var end = /** @type {TemplateNode} */ (clone.lastChild);
157-
158-
assign_nodes(start, end);
159-
} else {
160-
assign_nodes(clone, clone);
161-
}
162-
163-
return clone;
164-
};
165-
}
166-
167-
/**
168-
* @param {() => Element | DocumentFragment} fn
169-
*/
170-
export function with_script(fn) {
171-
return () => run_scripts(fn());
172-
}
17382

17483
/**
17584
* @param {string} content
@@ -178,7 +87,7 @@ export function with_script(fn) {
17887
* @returns {() => Node | Node[]}
17988
*/
18089
/*#__NO_SIDE_EFFECTS__*/
181-
export function ns_template(content, flags, ns = 'svg') {
90+
function from_namespace(content, flags, ns = 'svg') {
18291
/**
18392
* Whether or not the first item is a text/element node. If not, we need to
18493
* create an additional comment node to act as `effect.nodes.start`
@@ -227,14 +136,78 @@ export function ns_template(content, flags, ns = 'svg') {
227136
}
228137

229138
/**
230-
* @param {Array<TemplateStructure>} structure
139+
* @param {string} content
140+
* @param {number} flags
141+
*/
142+
/*#__NO_SIDE_EFFECTS__*/
143+
export function from_svg(content, flags) {
144+
return from_namespace(content, flags, 'svg');
145+
}
146+
147+
/**
148+
* @param {string} content
149+
* @param {number} flags
150+
*/
151+
/*#__NO_SIDE_EFFECTS__*/
152+
export function from_mathml(content, flags) {
153+
return from_namespace(content, flags, 'math');
154+
}
155+
156+
/**
157+
* @param {TemplateStructure[]} structure
158+
* @param {NAMESPACE_SVG | NAMESPACE_MATHML | undefined} [ns]
159+
*/
160+
function fragment_from_tree(structure, ns) {
161+
var fragment = create_fragment();
162+
163+
for (var item of structure) {
164+
if (typeof item === 'string') {
165+
fragment.append(create_text(item));
166+
continue;
167+
}
168+
169+
// if `preserveComments === true`, comments are represented as `['// <data>']`
170+
if (item === undefined || item[0][0] === '/') {
171+
fragment.append(create_comment(item ? item[0].slice(3) : ''));
172+
continue;
173+
}
174+
175+
const [name, attributes, ...children] = item;
176+
177+
const namespace = name === 'svg' ? NAMESPACE_SVG : name === 'math' ? NAMESPACE_MATHML : ns;
178+
179+
var element = create_element(name, namespace, attributes?.is);
180+
181+
for (var key in attributes) {
182+
set_attribute(element, key, attributes[key]);
183+
}
184+
185+
if (children.length > 0) {
186+
var target =
187+
element.tagName === 'TEMPLATE'
188+
? /** @type {HTMLTemplateElement} */ (element).content
189+
: element;
190+
191+
target.append(
192+
fragment_from_tree(children, element.tagName === 'foreignObject' ? undefined : namespace)
193+
);
194+
}
195+
196+
fragment.append(element);
197+
}
198+
199+
return fragment;
200+
}
201+
202+
/**
203+
* @param {TemplateStructure[]} structure
231204
* @param {number} flags
232-
* @param {'svg' | 'math'} ns
233205
* @returns {() => Node | Node[]}
234206
*/
235207
/*#__NO_SIDE_EFFECTS__*/
236-
export function ns_template_fn(structure, flags, ns = 'svg') {
208+
export function from_tree(structure, flags) {
237209
var is_fragment = (flags & TEMPLATE_FRAGMENT) !== 0;
210+
var use_import_node = (flags & TEMPLATE_USE_IMPORT_NODE) !== 0;
238211

239212
/** @type {Node} */
240213
var node;
@@ -246,11 +219,20 @@ export function ns_template_fn(structure, flags, ns = 'svg') {
246219
}
247220

248221
if (node === undefined) {
249-
node = structure_to_fragment(structure, ns === 'svg' ? NAMESPACE_SVG : NAMESPACE_MATHML);
222+
const ns =
223+
(flags & TEMPLATE_USE_SVG) !== 0
224+
? NAMESPACE_SVG
225+
: (flags & TEMPLATE_USE_MATHML) !== 0
226+
? NAMESPACE_MATHML
227+
: undefined;
228+
229+
node = fragment_from_tree(structure, ns);
250230
if (!is_fragment) node = /** @type {Node} */ (get_first_child(node));
251231
}
252232

253-
var clone = /** @type {TemplateNode} */ (node.cloneNode(true));
233+
var clone = /** @type {TemplateNode} */ (
234+
use_import_node || is_firefox ? document.importNode(node, true) : node.cloneNode(true)
235+
);
254236

255237
if (is_fragment) {
256238
var start = /** @type {TemplateNode} */ (get_first_child(clone));
@@ -266,23 +248,10 @@ export function ns_template_fn(structure, flags, ns = 'svg') {
266248
}
267249

268250
/**
269-
* @param {string} content
270-
* @param {number} flags
271-
* @returns {() => Node | Node[]}
272-
*/
273-
/*#__NO_SIDE_EFFECTS__*/
274-
export function mathml_template(content, flags) {
275-
return ns_template(content, flags, 'math');
276-
}
277-
278-
/**
279-
* @param {Array<TemplateStructure>} structure
280-
* @param {number} flags
281-
* @returns {() => Node | Node[]}
251+
* @param {() => Element | DocumentFragment} fn
282252
*/
283-
/*#__NO_SIDE_EFFECTS__*/
284-
export function mathml_template_fn(structure, flags) {
285-
return ns_template_fn(structure, flags, 'math');
253+
export function with_script(fn) {
254+
return () => run_scripts(fn());
286255
}
287256

288257
/**

packages/svelte/src/internal/client/index.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,10 @@ export {
8888
export {
8989
append,
9090
comment,
91-
ns_template,
92-
ns_template_fn,
93-
mathml_template,
94-
mathml_template_fn,
95-
template,
96-
template_fn,
91+
from_html,
92+
from_mathml,
93+
from_svg,
94+
from_tree,
9795
text,
9896
props_id,
9997
with_script

packages/svelte/tests/snapshot/samples/await-block-scope/_expected/client/index.svelte.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ function increment(_, counter) {
55
counter.count += 1;
66
}
77

8-
var root = $.template(`<button> </button> <!> `, 1);
8+
var root = $.from_html(`<button> </button> <!> `, 1);
99

1010
export default function Await_block_scope($$anchor) {
1111
let counter = $.proxy({ count: 0 });

packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const snippet = ($$anchor) => {
1010
$.append($$anchor, text);
1111
};
1212

13-
var root = $.template(`<!> `, 1);
13+
var root = $.from_html(`<!> `, 1);
1414

1515
export default function Bind_component_snippet($$anchor) {
1616
let value = $.state('');

packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'svelte/internal/disclose-version';
22
import * as $ from 'svelte/internal/client';
33

4-
var root = $.template(`<div></div> <svg></svg> <custom-element></custom-element> <div></div> <svg></svg> <custom-element></custom-element>`, 3);
4+
var root = $.from_html(`<div></div> <svg></svg> <custom-element></custom-element> <div></div> <svg></svg> <custom-element></custom-element>`, 3);
55

66
export default function Main($$anchor) {
77
// needs to be a snapshot test because jsdom does auto-correct the attribute casing

0 commit comments

Comments
 (0)