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) { function serialize_event(node, metadata, context) {
const state = context.state; const state = context.state;
/** @type {Statement} */ /** @type {Expression} */
let statement; let expression;
if (node.expression) { if (node.expression) {
let handler = serialize_event_handler(node, metadata, context); 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 // Events need to run in order with bindings/actions
statement = b.stmt(b.call('$.event', ...args)); expression = b.call('$.event', ...args);
} else { } else {
statement = b.stmt( expression = b.call(
b.call( '$.event',
'$.event', b.literal(node.name),
b.literal(node.name), state.node,
state.node, serialize_event_handler(node, metadata, context)
serialize_event_handler(node, metadata, context)
)
); );
} }
const parent = /** @type {SvelteNode} */ (context.path.at(-1)); 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 ( if (
parent.type === 'SvelteDocument' || parent.type === 'SvelteDocument' ||
parent.type === 'SvelteWindow' || 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. // 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. // bind:this is a special case as it's one-way and could influence the render effect.
if (node.name === 'this') { 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 { } 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) { Component(node, context) {

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

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