Skip to content

Commit a88e2dc

Browse files
authored
chore: tweak template generation logic (#13283)
* chore: tweak template generation logic * collapse * simplify
1 parent 2a1d1c1 commit a88e2dc

File tree

14 files changed

+51
-61
lines changed

14 files changed

+51
-61
lines changed

packages/svelte/src/compiler/phases/3-transform/client/types.d.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,7 @@ export interface ComponentClientTransformState extends ClientTransformState {
5454
/** Stuff that happens after the render effect (control blocks, dynamic elements, bindings, actions, etc) */
5555
readonly after_update: Statement[];
5656
/** The HTML template string */
57-
readonly template: {
58-
push_quasi: (q: string) => void;
59-
push_expression: (e: Expression) => void;
60-
};
57+
readonly template: Array<string | Expression>;
6158
readonly locations: SourceLocation[];
6259
readonly metadata: {
6360
namespace: Namespace;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { create_derived_block_argument } from '../utils.js';
99
* @param {ComponentContext} context
1010
*/
1111
export function AwaitBlock(node, context) {
12-
context.state.template.push_quasi('<!>');
12+
context.state.template.push('<!>');
1313

1414
// Visit {#await <expression>} first to ensure that scopes are in the correct order
1515
const expression = b.thunk(/** @type {Expression} */ (context.visit(node.expression)));

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@
77
*/
88
export function Comment(node, context) {
99
// We'll only get here if comments are not filtered out, which they are unless preserveComments is true
10-
context.state.template.push_quasi(`<!--${node.data}-->`);
10+
context.state.template.push(`<!--${node.data}-->`);
1111
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export function EachBlock(node, context) {
3232
);
3333

3434
if (!each_node_meta.is_controlled) {
35-
context.state.template.push_quasi('<!>');
35+
context.state.template.push('<!>');
3636
}
3737

3838
if (each_node_meta.array_name !== null) {

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

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import { TEMPLATE_FRAGMENT, TEMPLATE_USE_IMPORT_NODE } from '../../../../../constants.js';
66
import { dev } from '../../../../state.js';
77
import * as b from '../../../../utils/builders.js';
8+
import { sanitize_template_string } from '../../../../utils/sanitize_template_string.js';
89
import { clean_nodes, infer_namespace } from '../../utils.js';
910
import { process_children } from './shared/fragment.js';
1011
import { build_render_statement } from './shared/utils.js';
@@ -57,34 +58,14 @@ export function Fragment(node, context) {
5758
/** @type {Statement | undefined} */
5859
let close = undefined;
5960

60-
/** @type {string[]} */
61-
const quasi = [];
62-
/** @type {Expression[]} */
63-
const expressions = [];
64-
6561
/** @type {ComponentClientTransformState} */
6662
const state = {
6763
...context.state,
6864
before_init: [],
6965
init: [],
7066
update: [],
7167
after_update: [],
72-
template: {
73-
push_quasi: (/** @type {string} */ quasi_to_add) => {
74-
if (quasi.length === 0) {
75-
quasi.push(quasi_to_add);
76-
return;
77-
}
78-
quasi[quasi.length - 1] = quasi[quasi.length - 1].concat(quasi_to_add);
79-
},
80-
push_expression: (/** @type {Expression} */ expression_to_add) => {
81-
if (quasi.length === 0) {
82-
quasi.push('');
83-
}
84-
expressions.push(expression_to_add);
85-
quasi.push('');
86-
}
87-
},
68+
template: [],
8869
locations: [],
8970
transform: { ...context.state.transform },
9071
metadata: {
@@ -135,12 +116,7 @@ export function Fragment(node, context) {
135116
});
136117

137118
/** @type {Expression[]} */
138-
const args = [
139-
b.template(
140-
quasi.map((q) => b.quasi(q, true)),
141-
expressions
142-
)
143-
];
119+
const args = [join_template(state.template)];
144120

145121
if (state.metadata.context.template_needs_import_node) {
146122
args.push(b.literal(TEMPLATE_USE_IMPORT_NODE));
@@ -195,17 +171,11 @@ export function Fragment(node, context) {
195171
flags |= TEMPLATE_USE_IMPORT_NODE;
196172
}
197173

198-
if (quasi.length === 1 && quasi[0] === '<!>') {
174+
if (state.template.length === 1 && state.template[0] === '<!>') {
199175
// special case — we can use `$.comment` instead of creating a unique template
200176
body.push(b.var(id, b.call('$.comment')));
201177
} else {
202-
add_template(template_name, [
203-
b.template(
204-
quasi.map((q) => b.quasi(q, true)),
205-
expressions
206-
),
207-
b.literal(flags)
208-
]);
178+
add_template(template_name, [join_template(state.template), b.literal(flags)]);
209179

210180
body.push(b.var(id, b.call(template_name)));
211181
}
@@ -235,6 +205,31 @@ export function Fragment(node, context) {
235205
return b.block(body);
236206
}
237207

208+
/**
209+
* @param {Array<string | Expression>} items
210+
*/
211+
function join_template(items) {
212+
let quasi = b.quasi('');
213+
const template = b.template([quasi], []);
214+
215+
for (const item of items) {
216+
if (typeof item === 'string') {
217+
quasi.value.cooked += item;
218+
} else {
219+
template.expressions.push(item);
220+
template.quasis.push((quasi = b.quasi('')));
221+
}
222+
}
223+
224+
for (const quasi of template.quasis) {
225+
quasi.value.raw = sanitize_template_string(/** @type {string} */ (quasi.value.cooked));
226+
}
227+
228+
quasi.tail = true;
229+
230+
return template;
231+
}
232+
238233
/**
239234
*
240235
* @param {Namespace} namespace

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as b from '../../../../utils/builders.js';
99
* @param {ComponentContext} context
1010
*/
1111
export function HtmlTag(node, context) {
12-
context.state.template.push_quasi('<!>');
12+
context.state.template.push('<!>');
1313

1414
// push into init, so that bindings run afterwards, which might trigger another run and override hydration
1515
context.state.init.push(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import * as b from '../../../../utils/builders.js';
88
* @param {ComponentContext} context
99
*/
1010
export function IfBlock(node, context) {
11-
context.state.template.push_quasi('<!>');
11+
context.state.template.push('<!>');
1212

1313
const consequent = /** @type {BlockStatement} */ (context.visit(node.consequent));
1414

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import * as b from '../../../../utils/builders.js';
88
* @param {ComponentContext} context
99
*/
1010
export function KeyBlock(node, context) {
11-
context.state.template.push_quasi('<!>');
11+
context.state.template.push('<!>');
1212

1313
const key = /** @type {Expression} */ (context.visit(node.expression));
1414
const body = /** @type {Expression} */ (context.visit(node.fragment));

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

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export function RegularElement(node, context) {
5454
}
5555

5656
if (node.name === 'noscript') {
57-
context.state.template.push_quasi('<noscript></noscript>');
57+
context.state.template.push('<noscript></noscript>');
5858
return;
5959
}
6060

@@ -68,7 +68,7 @@ export function RegularElement(node, context) {
6868
namespace: determine_namespace_for_children(node, context.state.metadata.namespace)
6969
};
7070

71-
context.state.template.push_quasi(`<${node.name}`);
71+
context.state.template.push(`<${node.name}`);
7272

7373
/** @type {Array<AST.Attribute | AST.SpreadAttribute>} */
7474
const attributes = [];
@@ -242,7 +242,7 @@ export function RegularElement(node, context) {
242242
const value = is_text_attribute(attribute) ? attribute.value[0].data : true;
243243

244244
if (name !== 'class' || value) {
245-
context.state.template.push_quasi(
245+
context.state.template.push(
246246
` ${attribute.name}${
247247
is_boolean_attribute(name) && value === true
248248
? ''
@@ -279,7 +279,7 @@ export function RegularElement(node, context) {
279279
context.state.after_update.push(b.stmt(b.call('$.replay_events', node_id)));
280280
}
281281

282-
context.state.template.push_quasi('>');
282+
context.state.template.push('>');
283283

284284
/** @type {SourceLocation[]} */
285285
const child_locations = [];
@@ -384,7 +384,7 @@ export function RegularElement(node, context) {
384384
}
385385

386386
if (!is_void(node.name)) {
387-
context.state.template.push_quasi(`</${node.name}>`);
387+
context.state.template.push(`</${node.name}>`);
388388
}
389389
}
390390

@@ -472,7 +472,7 @@ function build_element_spread_attributes(
472472
value.type === 'Literal' &&
473473
context.state.metadata.namespace === 'html'
474474
) {
475-
context.state.template.push_quasi(` is="${escape_html(value.value, true)}"`);
475+
context.state.template.push(` is="${escape_html(value.value, true)}"`);
476476
continue;
477477
}
478478

@@ -630,9 +630,7 @@ function build_element_attribute_update_assignment(element, node_id, attribute,
630630
return true;
631631
} else {
632632
if (inlinable_expression) {
633-
context.state.template.push_quasi(` ${name}="`);
634-
context.state.template.push_expression(value);
635-
context.state.template.push_quasi('"');
633+
context.state.template.push(` ${name}="`, value, '"');
636634
} else {
637635
state.init.push(update);
638636
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as b from '../../../../utils/builders.js';
99
* @param {ComponentContext} context
1010
*/
1111
export function RenderTag(node, context) {
12-
context.state.template.push_quasi('<!>');
12+
context.state.template.push('<!>');
1313
const callee = unwrap_optional(node.expression).callee;
1414
const raw_args = unwrap_optional(node.expression).arguments;
1515

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { build_attribute_value } from './shared/element.js';
1010
*/
1111
export function SlotElement(node, context) {
1212
// <slot {a}>fallback</slot> --> $.slot($$slots.default, { get a() { .. } }, () => ...fallback);
13-
context.state.template.push_quasi('<!>');
13+
context.state.template.push('<!>');
1414

1515
/** @type {Property[]} */
1616
const props = [];

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { build_render_statement, build_update } from './shared/utils.js';
2121
* @param {ComponentContext} context
2222
*/
2323
export function SvelteElement(node, context) {
24-
context.state.template.push_quasi(`<!>`);
24+
context.state.template.push(`<!>`);
2525

2626
/** @type {Array<AST.Attribute | AST.SpreadAttribute>} */
2727
const attributes = [];

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ export function build_component(node, component_name, context, anchor = context.
357357
}
358358

359359
if (Object.keys(custom_css_props).length > 0) {
360-
context.state.template.push_quasi(
360+
context.state.template.push(
361361
context.state.metadata.namespace === 'svg'
362362
? '<g><!></g>'
363363
: '<div style="display: contents"><!></div>'
@@ -369,7 +369,7 @@ export function build_component(node, component_name, context, anchor = context.
369369
b.stmt(b.call('$.reset', anchor))
370370
);
371371
} else {
372-
context.state.template.push_quasi('<!>');
372+
context.state.template.push('<!>');
373373
statements.push(b.stmt(fn(anchor)));
374374
}
375375

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@ export function process_children(nodes, initial, is_element, { visit, state }) {
6262
function flush_sequence(sequence) {
6363
if (sequence.every((node) => node.type === 'Text')) {
6464
skipped += 1;
65-
state.template.push_quasi(sequence.map((node) => node.raw).join(''));
65+
state.template.push(sequence.map((node) => node.raw).join(''));
6666
return;
6767
}
6868

69-
state.template.push_quasi(' ');
69+
state.template.push(' ');
7070

7171
const { has_state, has_call, value } = build_template_literal(sequence, visit, state);
7272

0 commit comments

Comments
 (0)