fix: ensure directives run in sequential order (#12591)

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/12597/head
Dominic Gannaway 1 year ago committed by GitHub
parent c173140969
commit 7a8cf3a9a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: ensure directives run in sequential order

@ -1233,8 +1233,8 @@ function serialize_event_handler(node, metadata, { state, visit }) {
function serialize_event(node, metadata, context) {
const state = context.state;
/** @type {Statement} */
let statement;
/** @type {Expression} */
let expression;
if (node.expression) {
let handler = serialize_event_handler(node, metadata, context);
@ -1303,19 +1303,23 @@ function serialize_event(node, metadata, context) {
}
// Events need to run in order with bindings/actions
statement = b.stmt(b.call('$.event', ...args));
expression = b.call('$.event', ...args);
} else {
statement = b.stmt(
b.call(
'$.event',
b.literal(node.name),
state.node,
serialize_event_handler(node, metadata, context)
)
expression = b.call(
'$.event',
b.literal(node.name),
state.node,
serialize_event_handler(node, metadata, context)
);
}
const parent = /** @type {SvelteNode} */ (context.path.at(-1));
const has_action_directive =
parent.type === 'RegularElement' && parent.attributes.find((a) => a.type === 'UseDirective');
const statement = b.stmt(
has_action_directive ? b.call('$.effect', b.thunk(expression)) : expression
);
if (
parent.type === 'SvelteDocument' ||
parent.type === 'SvelteWindow' ||
@ -3083,12 +3087,20 @@ export const template_visitors = {
}
}
const parent = /** @type {import('#compiler').SvelteNode} */ (context.path.at(-1));
const has_action_directive =
parent.type === 'RegularElement' && parent.attributes.find((a) => a.type === 'UseDirective');
// Bindings need to happen after attribute updates, therefore after the render effect, and in order with events/actions.
// bind:this is a special case as it's one-way and could influence the render effect.
if (node.name === 'this') {
state.init.push(b.stmt(call_expr));
state.init.push(
b.stmt(has_action_directive ? b.call('$.effect', b.thunk(call_expr)) : call_expr)
);
} else {
state.after_update.push(b.stmt(call_expr));
state.after_update.push(
b.stmt(has_action_directive ? b.call('$.effect', b.thunk(call_expr)) : call_expr)
);
}
},
Component(node, context) {

@ -101,6 +101,7 @@ export {
legacy_pre_effect_reset,
render_effect,
template_effect,
effect,
user_effect,
user_pre_effect
} from './reactivity/effects.js';

@ -21,18 +21,16 @@ export default test({
flushSync();
}
// Svelte 5 breaking change, use:action now fires
// in effect phase. So they will occur AFTER the others.
assert.deepEqual(value, [
'1',
'2',
'3',
'1',
'4',
'5',
'6',
'4',
'7',
'9',
'8',
'9',
'10',
'11',
'12',
@ -40,30 +38,8 @@ export default test({
'14',
'15',
'16',
'18',
'17'
'17',
'18'
]);
// Previously
// assert.deepEqual(value, [
// '1',
// '2',
// '3',
// '4',
// '5',
// '6',
// '7',
// '8',
// '9',
// '10',
// '11',
// '12',
// '13',
// '14',
// '15',
// '16',
// '17',
// '18',
// ]);
}
});

Loading…
Cancel
Save