@@ -27,9 +27,13 @@ export function RenderTag(node, context) {
2727 */
2828 let resolved = callee . type === 'Identifier' && is_resolved_snippet ( binding ) ;
2929
30+ /** @type {AST.SnippetBlock | undefined } */
31+ let snippet
32+
3033 if ( binding ?. initial ?. type === 'SnippetBlock' ) {
3134 // if this render tag unambiguously references a local snippet, our job is easy
32- node . metadata . snippets . add ( binding . initial ) ;
35+ snippet = binding . initial
36+ node . metadata . snippets . add ( snippet ) ;
3337 }
3438
3539 context . state . analysis . snippet_renderers . set ( node , resolved ) ;
@@ -49,8 +53,31 @@ export function RenderTag(node, context) {
4953 ) {
5054 e . render_tag_invalid_call_expression ( node ) ;
5155 }
56+
57+ const parent = context . path . at ( - 2 ) ;
58+ const is_animated = snippet ?. body . nodes . some ( is_animate_directive ) ;
59+
60+ if ( is_animated ) {
61+ if ( parent ?. type !== 'EachBlock' ) {
62+ e . animation_invalid_placement ( node ) ;
63+ }
64+ else if ( ! parent . key ) {
65+ e . animation_missing_key ( parent ) ;
66+ }
67+ }
5268
5369 mark_subtree_dynamic ( context . path ) ;
5470
5571 context . next ( { ...context . state , render_tag : node } ) ;
5672}
73+
74+ /** @param {AST.Text | AST.Tag | AST.ElementLike | AST.Comment | AST.Block } child */
75+ function is_animate_directive ( child ) {
76+ if ( child . type === 'RenderTag' ) {
77+ for ( const snippet_block of child . metadata . snippets ) {
78+ return snippet_block . body . nodes . some ( is_animate_directive ) ;
79+ }
80+ }
81+ if ( child . type !== 'RegularElement' && child . type !== 'SvelteElement' ) return false ;
82+ return child . attributes . some ( ( attr ) => attr . type === 'AnimateDirective' ) ;
83+ }
0 commit comments