Skip to content

Commit 3ce74e4

Browse files
Rich-Harristrueadm
andauthored
chore: update sequencing inside blocks (#10939)
* WIP * fix timing issue * compromise * fix * add missing before_init * lint --------- Co-authored-by: Dominic Gannaway <[email protected]>
1 parent 9bbc332 commit 3ce74e4

File tree

6 files changed

+49
-28
lines changed

6 files changed

+49
-28
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ export function client_component(source, analysis, options) {
4949
hoisted: [b.import_all('$', 'svelte/internal')],
5050
node: /** @type {any} */ (null), // populated by the root node
5151
// these should be set by create_block - if they're called outside, it's a bug
52+
get before_init() {
53+
/** @type {any[]} */
54+
const a = [];
55+
a.push = () =>
56+
error(null, 'INTERNAL', 'before_init.push should not be called outside create_block');
57+
return a;
58+
},
5259
get init() {
5360
/** @type {any[]} */
5461
const a = [];

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export interface ComponentClientTransformState extends ClientTransformState {
2929
readonly hoisted: Array<Statement | ModuleDeclaration>;
3030
readonly events: Set<string>;
3131

32+
/** Stuff that happens before the render effect(s) */
33+
readonly before_init: Statement[];
3234
/** Stuff that happens before the render effect(s) */
3335
readonly init: Statement[];
3436
/** Stuff that happens inside the render effect */

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

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,6 +1005,7 @@ function create_block(parent, name, nodes, context) {
10051005
/** @type {import('../types').ComponentClientTransformState} */
10061006
const state = {
10071007
...context.state,
1008+
before_init: [],
10081009
init: [],
10091010
update: [],
10101011
after_update: [],
@@ -1050,11 +1051,11 @@ function create_block(parent, name, nodes, context) {
10501051
args.push(b.false);
10511052
}
10521053

1053-
body.push(b.var(id, b.call('$.open', ...args)), ...state.init);
1054+
body.push(b.var(id, b.call('$.open', ...args)), ...state.before_init, ...state.init);
10541055
close = b.stmt(b.call('$.close', b.id('$$anchor'), id));
10551056
} else if (is_single_child_not_needing_template) {
10561057
context.visit(trimmed[0], state);
1057-
body.push(...state.init);
1058+
body.push(...state.before_init, ...state.init);
10581059
} else if (trimmed.length > 0) {
10591060
const id = b.id(context.state.scope.generate('fragment'));
10601061

@@ -1071,7 +1072,11 @@ function create_block(parent, name, nodes, context) {
10711072
state
10721073
});
10731074

1074-
body.push(b.var(id, b.call('$.space_frag', b.id('$$anchor'))), ...state.init);
1075+
body.push(
1076+
b.var(id, b.call('$.space_frag', b.id('$$anchor'))),
1077+
...state.before_init,
1078+
...state.init
1079+
);
10751080
close = b.stmt(b.call('$.close', b.id('$$anchor'), id));
10761081
} else {
10771082
/** @type {(is_text: boolean) => import('estree').Expression} */
@@ -1107,12 +1112,12 @@ function create_block(parent, name, nodes, context) {
11071112
body.push(b.var(id, b.call('$.open_frag', ...args)));
11081113
}
11091114

1110-
body.push(...state.init);
1115+
body.push(...state.before_init, ...state.init);
11111116

11121117
close = b.stmt(b.call('$.close_frag', b.id('$$anchor'), id));
11131118
}
11141119
} else {
1115-
body.push(...state.init);
1120+
body.push(...state.before_init, ...state.init);
11161121
}
11171122

11181123
if (state.update.length > 0) {
@@ -1256,6 +1261,9 @@ function serialize_event_handler(node, { state, visit }) {
12561261
function serialize_event(node, context) {
12571262
const state = context.state;
12581263

1264+
/** @type {import('estree').Statement} */
1265+
let statement;
1266+
12591267
if (node.expression) {
12601268
let handler = serialize_event_handler(node, context);
12611269
const event_name = node.name;
@@ -1291,7 +1299,7 @@ function serialize_event(node, context) {
12911299
delegated_assignment = handler;
12921300
}
12931301

1294-
state.after_update.push(
1302+
state.init.push(
12951303
b.stmt(
12961304
b.assignment(
12971305
'=',
@@ -1323,14 +1331,19 @@ function serialize_event(node, context) {
13231331
}
13241332

13251333
// Events need to run in order with bindings/actions
1326-
state.after_update.push(b.stmt(b.call('$.event', ...args)));
1334+
statement = b.stmt(b.call('$.event', ...args));
13271335
} else {
1328-
state.after_update.push(
1329-
b.stmt(
1330-
b.call('$.event', b.literal(node.name), state.node, serialize_event_handler(node, context))
1331-
)
1336+
statement = b.stmt(
1337+
b.call('$.event', b.literal(node.name), state.node, serialize_event_handler(node, context))
13321338
);
13331339
}
1340+
1341+
const parent = /** @type {import('#compiler').SvelteNode} */ (context.path.at(-1));
1342+
if (parent.type === 'SvelteDocument' || parent.type === 'SvelteWindow') {
1343+
state.before_init.push(statement);
1344+
} else {
1345+
state.after_update.push(statement);
1346+
}
13341347
}
13351348

13361349
/**
@@ -1720,11 +1733,9 @@ export const template_visitors = {
17201733
}
17211734

17221735
if (is_reactive) {
1723-
context.state.after_update.push(
1724-
b.stmt(b.call('$.snippet', b.thunk(snippet_function), ...args))
1725-
);
1736+
context.state.init.push(b.stmt(b.call('$.snippet', b.thunk(snippet_function), ...args)));
17261737
} else {
1727-
context.state.after_update.push(
1738+
context.state.init.push(
17281739
b.stmt(
17291740
(node.expression.type === 'CallExpression' ? b.call : b.maybe_call)(
17301741
snippet_function,
@@ -2029,6 +2040,7 @@ export const template_visitors = {
20292040
state: {
20302041
...context.state,
20312042
node: element_id,
2043+
before_init: [],
20322044
init: [],
20332045
update: [],
20342046
after_update: []
@@ -2091,7 +2103,7 @@ export const template_visitors = {
20912103
}
20922104
})
20932105
);
2094-
context.state.after_update.push(
2106+
context.state.init.push(
20952107
b.stmt(
20962108
b.call(
20972109
'$.element',
@@ -2372,7 +2384,7 @@ export const template_visitors = {
23722384
);
23732385
}
23742386

2375-
context.state.after_update.push(b.stmt(b.call(callee, ...args)));
2387+
context.state.init.push(b.stmt(b.call(callee, ...args)));
23762388
},
23772389
IfBlock(node, context) {
23782390
context.state.template.push('<!>');
@@ -2423,12 +2435,12 @@ export const template_visitors = {
24232435
args.push(b.literal(true));
24242436
}
24252437

2426-
context.state.after_update.push(b.stmt(b.call('$.if', ...args)));
2438+
context.state.init.push(b.stmt(b.call('$.if', ...args)));
24272439
},
24282440
AwaitBlock(node, context) {
24292441
context.state.template.push('<!>');
24302442

2431-
context.state.after_update.push(
2443+
context.state.init.push(
24322444
b.stmt(
24332445
b.call(
24342446
'$.await',
@@ -2470,7 +2482,7 @@ export const template_visitors = {
24702482
context.state.template.push('<!>');
24712483
const key = /** @type {import('estree').Expression} */ (context.visit(node.expression));
24722484
const body = /** @type {import('estree').Expression} */ (context.visit(node.fragment));
2473-
context.state.after_update.push(
2485+
context.state.init.push(
24742486
b.stmt(b.call('$.key', context.state.node, b.thunk(key), b.arrow([b.id('$$anchor')], body)))
24752487
);
24762488
},
@@ -2791,7 +2803,7 @@ export const template_visitors = {
27912803
if (binding !== null && binding.kind !== 'normal') {
27922804
// Handle dynamic references to what seems like static inline components
27932805
const component = serialize_inline_component(node, '$$component', context);
2794-
context.state.after_update.push(
2806+
context.state.init.push(
27952807
b.stmt(
27962808
b.call(
27972809
'$.component',
@@ -2808,12 +2820,12 @@ export const template_visitors = {
28082820
return;
28092821
}
28102822
const component = serialize_inline_component(node, node.name, context);
2811-
context.state.after_update.push(component);
2823+
context.state.init.push(component);
28122824
},
28132825
SvelteSelf(node, context) {
28142826
context.state.template.push('<!>');
28152827
const component = serialize_inline_component(node, context.state.analysis.name, context);
2816-
context.state.after_update.push(component);
2828+
context.state.init.push(component);
28172829
},
28182830
SvelteComponent(node, context) {
28192831
context.state.template.push('<!>');
@@ -2822,7 +2834,7 @@ export const template_visitors = {
28222834
if (context.state.options.dev) {
28232835
component = b.stmt(b.call('$.validate_dynamic_component', b.thunk(b.block([component]))));
28242836
}
2825-
context.state.after_update.push(
2837+
context.state.init.push(
28262838
b.stmt(
28272839
b.call(
28282840
'$.component',
@@ -2974,7 +2986,7 @@ export const template_visitors = {
29742986
: b.member(b.member(b.id('$$props'), b.id('$$slots')), name, true, true);
29752987

29762988
const slot = b.call('$.slot', context.state.node, expression, props_expression, fallback);
2977-
context.state.after_update.push(b.stmt(slot));
2989+
context.state.init.push(b.stmt(slot));
29782990
},
29792991
SvelteHead(node, context) {
29802992
// TODO attributes?

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export function capture_fragment_from_node(node) {
113113
if (
114114
node.nodeType === 8 &&
115115
/** @type {Comment} */ (node).data === '[' &&
116-
hydrate_nodes[hydrate_nodes.length - 1] !== node
116+
hydrate_nodes?.[hydrate_nodes.length - 1] !== node
117117
) {
118118
const nodes = /** @type {Node[]} */ (get_hydrate_nodes(node));
119119
const last_child = nodes[nodes.length - 1] || node;

packages/svelte/tests/runtime-legacy/samples/deconflict-contextual-action/_config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ export default test({
1414
};
1515
},
1616
test({ assert }) {
17-
assert.deepEqual(result, ['import_action', 'each_action']);
17+
assert.deepEqual(result, ['each_action', 'import_action']); // ideally this would be reversed, but it doesn't matter a whole lot
1818
}
1919
});

packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ export default function State_proxy_literal($$anchor, $$props) {
2828

2929
var button = $.sibling($.sibling(input_1, true));
3030

31+
button.__click = [reset, str, tpl];
3132
$.bind_value(input, () => $.get(str), ($$value) => $.set(str, $$value));
3233
$.bind_value(input_1, () => $.get(tpl), ($$value) => $.set(tpl, $$value));
33-
button.__click = [reset, str, tpl];
3434
$.close_frag($$anchor, fragment);
3535
$.pop();
3636
}

0 commit comments

Comments
 (0)