Skip to content

Commit 7276e5c

Browse files
committed
use bracket matching
1 parent b5b6a69 commit 7276e5c

File tree

3 files changed

+62
-103
lines changed

3 files changed

+62
-103
lines changed

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

Lines changed: 30 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ import { regex_starts_with_newline, regex_whitespaces_strict } from '../../patte
2727
import { DOMBooleanAttributes } from '../../../../constants.js';
2828
import { sanitize_template_string } from '../../../utils/sanitize_template_string.js';
2929

30+
const block_open = t_string('<![>');
31+
const block_close = t_string('<!]>');
32+
3033
/**
3134
* @param {string} value
3235
* @returns {import('./types').TemplateString}
@@ -52,15 +55,6 @@ function t_statement(value) {
5255
return { type: 'statement', value };
5356
}
5457

55-
/**
56-
* @param {import('./types').ServerTransformState} state
57-
* @returns {[import('estree').VariableDeclaration, import('estree').Identifier]}
58-
*/
59-
function serialize_anchor(state) {
60-
const id = state.scope.root.unique('anchor');
61-
return [b.const(id, b.call('$.create_anchor', b.id('$$payload'))), id];
62-
}
63-
6458
/**
6559
* @param {import('./types').Template[]} template
6660
* @param {import('estree').Identifier} out
@@ -1237,12 +1231,10 @@ const template_visitors = {
12371231
},
12381232
HtmlTag(node, context) {
12391233
const state = context.state;
1240-
const [dec, id] = serialize_anchor(state);
1241-
state.init.push(dec);
1242-
state.template.push(t_expression(id));
1234+
state.template.push(block_open);
12431235
const raw = /** @type {import('estree').Expression} */ (context.visit(node.expression));
12441236
context.state.template.push(t_expression(raw));
1245-
state.template.push(t_expression(id));
1237+
state.template.push(block_close);
12461238
},
12471239
ConstTag(node, { state, visit }) {
12481240
const declaration = node.declaration.declarations[0];
@@ -1273,10 +1265,8 @@ const template_visitors = {
12731265
},
12741266
RenderTag(node, context) {
12751267
const state = context.state;
1276-
const [anchor, anchor_id] = serialize_anchor(state);
12771268

1278-
state.init.push(anchor);
1279-
state.template.push(t_expression(anchor_id));
1269+
state.template.push(block_open);
12801270

12811271
const callee = unwrap_optional(node.expression).callee;
12821272
const raw_args = unwrap_optional(node.expression).arguments;
@@ -1302,7 +1292,7 @@ const template_visitors = {
13021292
)
13031293
);
13041294

1305-
state.template.push(t_expression(anchor_id));
1295+
state.template.push(block_close);
13061296
},
13071297
ClassDirective(node) {
13081298
error(node, 'INTERNAL', 'Node should have been handled elsewhere');
@@ -1427,9 +1417,7 @@ const template_visitors = {
14271417
}
14281418
};
14291419

1430-
const [el_anchor, anchor_id] = serialize_anchor(context.state);
1431-
context.state.init.push(el_anchor);
1432-
context.state.template.push(t_expression(anchor_id));
1420+
context.state.template.push(block_open);
14331421

14341422
const main = create_block(node, node.fragment.nodes, {
14351423
...context,
@@ -1465,17 +1453,15 @@ const template_visitors = {
14651453
)
14661454
)
14671455
),
1468-
t_expression(anchor_id)
1456+
block_close
14691457
);
14701458
if (context.state.options.dev) {
14711459
context.state.template.push(t_statement(b.stmt(b.call('$.pop_element'))));
14721460
}
14731461
},
14741462
EachBlock(node, context) {
14751463
const state = context.state;
1476-
const [dec, id] = serialize_anchor(state);
1477-
state.init.push(dec);
1478-
state.template.push(t_expression(id));
1464+
state.template.push(block_open);
14791465

14801466
const each_node_meta = node.metadata;
14811467
const collection = /** @type {import('estree').Expression} */ (context.visit(node.expression));
@@ -1486,14 +1472,6 @@ const template_visitors = {
14861472
: b.id(node.index);
14871473
const children = node.body.nodes;
14881474

1489-
const [each_dec, each_id] = serialize_anchor(state);
1490-
1491-
/** @type {import('./types').Anchor} */
1492-
const anchor = {
1493-
type: 'Anchor',
1494-
id: each_id
1495-
};
1496-
14971475
const array_id = state.scope.root.unique('each_array');
14981476
state.init.push(b.const(array_id, b.call('$.ensure_array_like', collection)));
14991477

@@ -1507,11 +1485,14 @@ const template_visitors = {
15071485
each.push(b.let(node.index, index));
15081486
}
15091487

1488+
each.push(b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(block_open.value))));
1489+
15101490
each.push(
1511-
each_dec,
1512-
.../** @type {import('estree').Statement[]} */ (create_block(node, children, context, anchor))
1491+
.../** @type {import('estree').Statement[]} */ (create_block(node, children, context))
15131492
);
15141493

1494+
each.push(b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(block_close.value))));
1495+
15151496
const for_loop = b.for(
15161497
b.let(index, b.literal(0)),
15171498
b.binary('<', index, b.member(array_id, b.id('length'))),
@@ -1535,13 +1516,11 @@ const template_visitors = {
15351516
} else {
15361517
state.template.push(t_statement(for_loop));
15371518
}
1538-
state.template.push(t_expression(id));
1519+
state.template.push(block_close);
15391520
},
15401521
IfBlock(node, context) {
15411522
const state = context.state;
1542-
const [dec, id] = serialize_anchor(state);
1543-
state.init.push(dec);
1544-
state.template.push(t_expression(id));
1523+
state.template.push(block_open);
15451524

15461525
// Insert ssr:if:true/false anchors in addition to the other anchors so that
15471526
// the if block can catch hydration mismatches (false on the server, true on the client and vice versa)
@@ -1568,13 +1547,11 @@ const template_visitors = {
15681547
)
15691548
)
15701549
);
1571-
state.template.push(t_expression(id));
1550+
state.template.push(block_close);
15721551
},
15731552
AwaitBlock(node, context) {
15741553
const state = context.state;
1575-
const [dec, id] = serialize_anchor(state);
1576-
state.init.push(dec);
1577-
state.template.push(t_expression(id));
1554+
state.template.push(block_open);
15781555

15791556
state.template.push(
15801557
t_statement(
@@ -1608,16 +1585,14 @@ const template_visitors = {
16081585
)
16091586
);
16101587

1611-
state.template.push(t_expression(id));
1588+
state.template.push(block_close);
16121589
},
16131590
KeyBlock(node, context) {
16141591
const state = context.state;
1615-
const [dec, id] = serialize_anchor(state);
1616-
state.init.push(dec);
1617-
state.template.push(t_expression(id));
1592+
state.template.push(block_open);
16181593
const body = create_block(node, node.fragment.nodes, context);
16191594
state.template.push(t_statement(b.block(body)));
1620-
state.template.push(t_expression(id));
1595+
state.template.push(block_close);
16211596
},
16221597
SnippetBlock(node, context) {
16231598
// TODO hoist where possible
@@ -1635,34 +1610,28 @@ const template_visitors = {
16351610
},
16361611
Component(node, context) {
16371612
const state = context.state;
1638-
const [dec, id] = serialize_anchor(state);
1639-
state.init.push(dec);
1640-
state.template.push(t_expression(id));
1613+
state.template.push(block_open);
16411614
const call = serialize_inline_component(node, node.name, context);
16421615
state.template.push(t_statement(call));
1643-
state.template.push(t_expression(id));
1616+
state.template.push(block_close);
16441617
},
16451618
SvelteSelf(node, context) {
16461619
const state = context.state;
1647-
const [dec, id] = serialize_anchor(state);
1648-
state.init.push(dec);
1649-
state.template.push(t_expression(id));
1620+
state.template.push(block_open);
16501621
const call = serialize_inline_component(node, context.state.analysis.name, context);
16511622
state.template.push(t_statement(call));
1652-
state.template.push(t_expression(id));
1623+
state.template.push(block_close);
16531624
},
16541625
SvelteComponent(node, context) {
16551626
const state = context.state;
1656-
const [dec, id] = serialize_anchor(state);
1657-
state.init.push(dec);
1658-
state.template.push(t_expression(id));
1627+
state.template.push(block_open);
16591628
const call = serialize_inline_component(
16601629
node,
16611630
/** @type {import('estree').Expression} */ (context.visit(node.expression)),
16621631
context
16631632
);
16641633
state.template.push(t_statement(call));
1665-
state.template.push(t_expression(id));
1634+
state.template.push(block_close);
16661635
},
16671636
LetDirective(node, { state }) {
16681637
if (node.expression && node.expression.type !== 'Identifier') {
@@ -1745,9 +1714,7 @@ const template_visitors = {
17451714
},
17461715
SlotElement(node, context) {
17471716
const state = context.state;
1748-
const [dec, id] = serialize_anchor(state);
1749-
state.init.push(dec);
1750-
state.template.push(t_expression(id));
1717+
state.template.push(block_open);
17511718

17521719
/** @type {import('estree').Property[]} */
17531720
const props = [];
@@ -1794,7 +1761,7 @@ const template_visitors = {
17941761
const slot = b.call('$.slot', b.id('$$payload'), expression, props_expression, fallback);
17951762

17961763
state.template.push(t_statement(b.stmt(slot)));
1797-
state.template.push(t_expression(id));
1764+
state.template.push(block_close);
17981765
},
17991766
SvelteHead(node, context) {
18001767
const state = context.state;

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

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export function update_hydrate_nodes(first, insert_text) {
3939
}
4040

4141
/**
42-
* Returns all nodes between the first `<!--ssr:...-->` comment tag pair encountered.
42+
* Returns all nodes between the first `<![>...<!]>` comment tag pair encountered.
4343
* @param {Node | null} node
4444
* @param {boolean} [insert_text] Whether to insert an empty text node if `nodes` is empty
4545
* @returns {import('#client').TemplateNode[] | null}
@@ -50,34 +50,36 @@ function get_hydrate_nodes(node, insert_text = false) {
5050

5151
var current_node = /** @type {null | import('#client').TemplateNode} */ (node);
5252

53-
/** @type {null | string} */
54-
var target_depth = null;
53+
var depth = 0;
54+
55+
var will_start = false;
56+
var started = false;
5557

5658
while (current_node !== null) {
5759
if (current_node.nodeType === 8) {
5860
var data = /** @type {Comment} */ (current_node).data;
5961

60-
if (data.startsWith('ssr:')) {
61-
var depth = data.slice(4);
62-
63-
if (target_depth === null) {
64-
target_depth = depth;
65-
} else if (depth === target_depth) {
66-
if (insert_text && nodes.length === 0) {
67-
var text = empty();
68-
nodes.push(text);
69-
current_node.before(text);
70-
}
71-
return nodes;
72-
} else {
73-
nodes.push(current_node);
62+
if (data === '[') {
63+
depth += 1;
64+
will_start = true;
65+
} else if (data === ']' && --depth === 0) {
66+
if (insert_text && nodes.length === 0) {
67+
var text = empty();
68+
nodes.push(text);
69+
current_node.before(text);
7470
}
71+
72+
return nodes;
7573
}
76-
} else if (target_depth !== null) {
74+
}
75+
76+
if (started) {
7777
nodes.push(current_node);
7878
}
7979

8080
current_node = /** @type {null | import('#client').TemplateNode} */ (current_node.nextSibling);
81+
82+
started = will_start;
8183
}
8284

8385
return null;
@@ -103,7 +105,7 @@ export function hydrate_block_anchor(node) {
103105
export function capture_fragment_from_node(node) {
104106
if (
105107
node.nodeType === 8 &&
106-
/** @type {Comment} */ (node).data.startsWith('ssr:') &&
108+
/** @type {Comment} */ (node).data === '[' &&
107109
hydrate_nodes[hydrate_nodes.length - 1] !== node
108110
) {
109111
const nodes = /** @type {Node[]} */ (get_hydrate_nodes(node));

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

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,12 @@ export function element(payload, tag, attributes_fn, children_fn) {
162162
payload.out += `>`;
163163

164164
if (!VoidElements.has(tag)) {
165-
const anchor = tag !== 'textarea' ? create_anchor(payload) : null;
166-
if (anchor !== null) {
167-
payload.out += anchor;
165+
if (tag !== 'textarea') {
166+
payload.out += '<![>';
168167
}
169168
children_fn();
170-
if (anchor !== null) {
171-
payload.out += anchor;
169+
if (tag !== 'textarea') {
170+
payload.out += '<!]>';
172171
}
173172
payload.out += `</${tag}>`;
174173
}
@@ -187,12 +186,10 @@ export let on_destroy = [];
187186
*/
188187
export function render(component, options) {
189188
const payload = create_payload();
190-
const root_anchor = create_anchor(payload);
191-
const root_head_anchor = create_anchor(payload.head);
192189

193190
const prev_on_destroy = on_destroy;
194191
on_destroy = [];
195-
payload.out += root_anchor;
192+
payload.out += '<![>';
196193

197194
if (options.context) {
198195
$.push({});
@@ -203,14 +200,14 @@ export function render(component, options) {
203200
if (options.context) {
204201
$.pop();
205202
}
206-
payload.out += root_anchor;
203+
payload.out += '<!]>';
207204
for (const cleanup of on_destroy) cleanup();
208205
on_destroy = prev_on_destroy;
209206

210207
return {
211208
head:
212209
payload.head.out || payload.head.title
213-
? payload.head.title + root_head_anchor + payload.head.out + root_head_anchor
210+
? payload.head.title + '<![>' + payload.head.out + '<!]>'
214211
: '',
215212
html: payload.out
216213
};
@@ -284,17 +281,16 @@ export function attr(name, value, boolean) {
284281
*/
285282
export function css_props(payload, is_html, props, component) {
286283
const styles = style_object_to_string(props);
287-
const anchor = create_anchor(payload);
288284
if (is_html) {
289-
payload.out += `<div style="display: contents; ${styles}">${anchor}`;
285+
payload.out += `<div style="display: contents; ${styles}"><![>`;
290286
} else {
291-
payload.out += `<g style="${styles}">${anchor}`;
287+
payload.out += `<g style="${styles}"><![>`;
292288
}
293289
component();
294290
if (is_html) {
295-
payload.out += `${anchor}</div>`;
291+
payload.out += `<!]></div>`;
296292
} else {
297-
payload.out += `${anchor}</g>`;
293+
payload.out += `<!]></g>`;
298294
}
299295
}
300296

@@ -634,12 +630,6 @@ export function ensure_array_like(array_like_or_iterator) {
634630
: Array.from(array_like_or_iterator);
635631
}
636632

637-
/** @param {{ anchor: number }} payload */
638-
export function create_anchor(payload) {
639-
const depth = payload.anchor++;
640-
return `<!ssr:${depth}>`;
641-
}
642-
643633
/** @returns {[() => false, (value: boolean) => void]} */
644634
export function selector() {
645635
// Add SSR stubs

0 commit comments

Comments
 (0)