Skip to content

Commit 3f75ea6

Browse files
authored
fix: allow stores in transition,animation,use directives (#10481)
* fix: allow stores in `transition`,`animation`,`use` directives * ssr * naming * linting is broken * hope it works * style * desc
1 parent 9e98bb6 commit 3f75ea6

File tree

6 files changed

+96
-7
lines changed

6 files changed

+96
-7
lines changed

.changeset/stale-jeans-refuse.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: subscribe to stores in `transition`,`animation`,`use` directives

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

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,25 @@ function is_store_name(name) {
362362
return name[0] === '$' && /[A-Za-z_]/.test(name[1]);
363363
}
364364

365+
/**
366+
*
367+
* @param {Iterable<import('#compiler').Binding>} bindings
368+
*/
369+
function store_sub_exist(bindings) {
370+
for (const binding of bindings) {
371+
if (binding.kind === 'store_sub') {
372+
for (const reference of binding.references) {
373+
const node = reference.path.at(-1);
374+
375+
// hacky way to ensure the sub is not in a directive e.g. use:$store as it is unneeded
376+
if (node?.type !== 'RegularElement') {
377+
return true;
378+
}
379+
}
380+
}
381+
}
382+
}
383+
365384
/**
366385
* @param {import('estree').AssignmentExpression} node
367386
* @param {import('zimmerframe').Context<import('#compiler').SvelteNode, import('./types').ServerTransformState>} context
@@ -2089,15 +2108,10 @@ export function server_component(analysis, options) {
20892108
];
20902109
}
20912110

2092-
if (
2093-
[...analysis.instance.scope.declarations.values()].some(
2094-
(binding) => binding.kind === 'store_sub'
2095-
)
2096-
) {
2111+
if (store_sub_exist(analysis.instance.scope.declarations.values())) {
20972112
instance.body.unshift(b.const('$$store_subs', b.object([])));
20982113
template.body.push(b.stmt(b.call('$.unsubscribe_stores', b.id('$$store_subs'))));
20992114
}
2100-
21012115
// Propagate values of bound props upwards if they're undefined in the parent and have a value.
21022116
// Don't do this as part of the props retrieval because people could eagerly mutate the prop in the instance script.
21032117
/** @type {import('estree').Property[]} */

packages/svelte/src/compiler/phases/scope.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ export class Scope {
185185
reference(node, path) {
186186
path = [...path]; // ensure that mutations to path afterwards don't affect this reference
187187
let references = this.references.get(node.name);
188+
188189
if (!references) this.references.set(node.name, (references = []));
189190

190191
references.push({ node, path });
@@ -329,6 +330,18 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
329330
return /** @type {const} */ ([scope, is_default_slot]);
330331
}
331332

333+
/**
334+
* Reference store in transion:, use:, animate: directives
335+
* @type {import('zimmerframe').Visitor<import('#compiler').Directive, State, import('#compiler').SvelteNode>}
336+
*/
337+
const SvelteDirective = (node, context) => {
338+
const name = node.name;
339+
340+
if (name[0] === '$') {
341+
context.state.scope.reference(b.id(name), context.path);
342+
}
343+
};
344+
332345
walk(ast, state, {
333346
// references
334347
Identifier(node, { path, state }) {
@@ -625,7 +638,11 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
625638
)
626639
]);
627640
context.next();
628-
}
641+
},
642+
643+
TransitionDirective: SvelteDirective,
644+
AnimateDirective: SvelteDirective,
645+
UseDirective: SvelteDirective
629646

630647
// TODO others
631648
});
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// main.svelte (Svelte VERSION)
2+
// Note: compiler output will change before 5.0 is released!
3+
import "svelte/internal/disclose-version";
4+
import * as $ from "svelte/internal";
5+
import { writable } from 'svelte/store';
6+
7+
var frag = $.template(`<div>Hello!</div> <div>Hello!</div>`, true);
8+
9+
export default function Main($$anchor, $$props) {
10+
$.push($$props, false);
11+
12+
const $$subscriptions = {};
13+
14+
$.unsubscribe_on_destroy($$subscriptions);
15+
16+
const $animate = () => $.store_get(animate, "$animate", $$subscriptions);
17+
const animate = writable();
18+
19+
$.init();
20+
21+
/* Init */
22+
var fragment = $.open_frag($$anchor, true, frag);
23+
var div = $.child_frag(fragment);
24+
25+
$.in(div, $animate, () => ({ duration: 750, x: 0, y: -200 }), false);
26+
27+
var div_1 = $.sibling($.sibling(div, true));
28+
29+
$.action(div_1, ($$node) => $animate()($$node));
30+
$.close_frag($$anchor, fragment);
31+
$.pop();
32+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// main.svelte (Svelte VERSION)
2+
// Note: compiler output will change before 5.0 is released!
3+
import * as $ from "svelte/internal/server";
4+
import { writable } from 'svelte/store';
5+
6+
export default function Main($$payload, $$props) {
7+
$.push(false);
8+
9+
const animate = writable();
10+
11+
$$payload.out += `<div>Hello!</div> <div>Hello!</div>`;
12+
$.pop();
13+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<script>
2+
import { writable, } from 'svelte/store';
3+
4+
const animate = writable();
5+
</script>
6+
7+
<div in:$animate={{ duration: 750, x:0, y: -200}}>Hello!</div>
8+
<div use:$animate>Hello!</div>

0 commit comments

Comments
 (0)