Skip to content

Commit f26229d

Browse files
committed
fix: ensure directives run in sequential order
1 parent 621eb76 commit f26229d

File tree

4 files changed

+36
-42
lines changed

4 files changed

+36
-42
lines changed

.changeset/plenty-turkeys-raise.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: ensure directives run in sequential order

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

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/** @import { BlockStatement, CallExpression, Expression, ExpressionStatement, Identifier, Literal, MemberExpression, ObjectExpression, Pattern, Property, Statement, Super, TemplateElement, TemplateLiteral } from 'estree' */
2-
/** @import { BindDirective } from '#compiler' */
2+
/** @import { BindDirective, RegularElement } from '#compiler' */
33
import {
44
extract_identifiers,
55
extract_paths,
@@ -1232,8 +1232,8 @@ function serialize_event_handler(node, metadata, { state, visit }) {
12321232
function serialize_event(node, metadata, context) {
12331233
const state = context.state;
12341234

1235-
/** @type {Statement} */
1236-
let statement;
1235+
/** @type {Expression} */
1236+
let expression;
12371237

12381238
if (node.expression) {
12391239
let handler = serialize_event_handler(node, metadata, context);
@@ -1302,19 +1302,23 @@ function serialize_event(node, metadata, context) {
13021302
}
13031303

13041304
// Events need to run in order with bindings/actions
1305-
statement = b.stmt(b.call('$.event', ...args));
1305+
expression = b.call('$.event', ...args);
13061306
} else {
1307-
statement = b.stmt(
1308-
b.call(
1309-
'$.event',
1310-
b.literal(node.name),
1311-
state.node,
1312-
serialize_event_handler(node, metadata, context)
1313-
)
1307+
expression = b.call(
1308+
'$.event',
1309+
b.literal(node.name),
1310+
state.node,
1311+
serialize_event_handler(node, metadata, context)
13141312
);
13151313
}
13161314

13171315
const parent = /** @type {import('#compiler').SvelteNode} */ (context.path.at(-1));
1316+
const has_action_directive =
1317+
parent.type === 'RegularElement' && parent.attributes.find((a) => a.type === 'UseDirective');
1318+
const statement = b.stmt(
1319+
has_action_directive ? b.call('$.effect', b.thunk(expression)) : expression
1320+
);
1321+
13181322
if (
13191323
parent.type === 'SvelteDocument' ||
13201324
parent.type === 'SvelteWindow' ||
@@ -3084,12 +3088,20 @@ export const template_visitors = {
30843088
}
30853089
}
30863090

3091+
const parent = /** @type {import('#compiler').SvelteNode} */ (context.path.at(-1));
3092+
const has_action_directive =
3093+
parent.type === 'RegularElement' && parent.attributes.find((a) => a.type === 'UseDirective');
3094+
30873095
// Bindings need to happen after attribute updates, therefore after the render effect, and in order with events/actions.
30883096
// bind:this is a special case as it's one-way and could influence the render effect.
30893097
if (node.name === 'this') {
3090-
state.init.push(b.stmt(call_expr));
3098+
state.init.push(
3099+
b.stmt(has_action_directive ? b.call('$.effect', b.thunk(call_expr)) : call_expr)
3100+
);
30913101
} else {
3092-
state.after_update.push(b.stmt(call_expr));
3102+
state.after_update.push(
3103+
b.stmt(has_action_directive ? b.call('$.effect', b.thunk(call_expr)) : call_expr)
3104+
);
30933105
}
30943106
},
30953107
Component(node, context) {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ export {
101101
legacy_pre_effect_reset,
102102
render_effect,
103103
template_effect,
104+
effect,
104105
user_effect,
105106
user_pre_effect
106107
} from './reactivity/effects.js';

packages/svelte/tests/runtime-legacy/samples/apply-directives-in-order-2/_config.js

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,49 +21,25 @@ export default test({
2121
flushSync();
2222
}
2323

24-
// Svelte 5 breaking change, use:action now fires
25-
// in effect phase. So they will occur AFTER the others.
2624
assert.deepEqual(value, [
25+
'1',
2726
'2',
2827
'3',
29-
'1',
28+
'4',
3029
'5',
3130
'6',
32-
'4',
3331
'7',
34-
'9',
3532
'8',
33+
'9',
3634
'10',
3735
'11',
3836
'12',
3937
'13',
4038
'14',
4139
'15',
4240
'16',
43-
'18',
44-
'17'
41+
'17',
42+
'18'
4543
]);
46-
47-
// Previously
48-
// assert.deepEqual(value, [
49-
// '1',
50-
// '2',
51-
// '3',
52-
// '4',
53-
// '5',
54-
// '6',
55-
// '7',
56-
// '8',
57-
// '9',
58-
// '10',
59-
// '11',
60-
// '12',
61-
// '13',
62-
// '14',
63-
// '15',
64-
// '16',
65-
// '17',
66-
// '18',
67-
// ]);
6844
}
6945
});

0 commit comments

Comments
 (0)