1
1
/** @import { AssignmentExpression, BinaryOperator, BlockStatement, CallExpression, Expression, ExpressionStatement, Identifier, MethodDefinition, Node, Pattern, Program, Property, PropertyDefinition, Statement, VariableDeclarator } from 'estree' */
2
- /** @import { Attribute, Binding, Component, Namespace, SvelteComponent, SvelteNode, SvelteSelf, TemplateNode, Text , ValidatedCompileOptions, ValidatedModuleCompileOptions } from '#compiler' */
3
- /** @import { ComponentContext, ComponentServerTransformState, ComponentVisitors, ServerTransformState, Visitors } from './types.js' */
2
+ /** @import { Binding, Namespace, SvelteNode, ValidatedCompileOptions, ValidatedModuleCompileOptions } from '#compiler' */
3
+ /** @import { ComponentServerTransformState, ComponentVisitors, ServerTransformState, Visitors } from './types.js' */
4
4
/** @import { Analysis, ComponentAnalysis } from '../../types.js' */
5
5
/** @import { Scope } from '../../scope.js' */
6
6
/** @import { StateField } from '../../3-transform/client/types.js' */ // TODO move this type
@@ -10,18 +10,23 @@ import { extract_identifiers, extract_paths, is_expression_async } from '../../.
10
10
import * as b from '../../../utils/builders.js' ;
11
11
import is_reference from 'is-reference' ;
12
12
import { transform_inspect_rune } from '../utils.js' ;
13
- import { is_element_node } from '../../nodes.js' ;
14
13
import { filename } from '../../../state.js' ;
15
14
import { render_stylesheet } from '../css/index.js' ;
15
+ import { AwaitBlock } from './visitors/template/AwaitBlock.js' ;
16
+ import { Component } from './visitors/template/Component.js' ;
16
17
import { ConstTag } from './visitors/template/ConstTag.js' ;
17
18
import { DebugTag } from './visitors/template/DebugTag.js' ;
18
19
import { EachBlock } from './visitors/template/EachBlock.js' ;
19
20
import { Fragment } from './visitors/template/Fragment.js' ;
20
21
import { HtmlTag } from './visitors/template/HtmlTag.js' ;
21
22
import { IfBlock } from './visitors/template/IfBlock.js' ;
23
+ import { KeyBlock } from './visitors/template/KeyBlock.js' ;
22
24
import { RegularElement } from './visitors/template/RegularElement.js' ;
23
25
import { RenderTag } from './visitors/template/RenderTag.js' ;
26
+ import { SnippetBlock } from './visitors/template/SnippetBlock.js' ;
27
+ import { SvelteComponent } from './visitors/template/SvelteComponent.js' ;
24
28
import { SvelteElement } from './visitors/template/SvelteElement.js' ;
29
+ import { SvelteSelf } from './visitors/template/SvelteSelf.js' ;
25
30
import {
26
31
empty_comment ,
27
32
process_children ,
@@ -502,235 +507,6 @@ const javascript_visitors_runes = {
502
507
}
503
508
} ;
504
509
505
- /**
506
- * @param {Component | SvelteComponent | SvelteSelf } node
507
- * @param {Expression } expression
508
- * @param {ComponentContext } context
509
- */
510
- function serialize_inline_component ( node , expression , context ) {
511
- /** @type {Array<Property[] | Expression> } */
512
- const props_and_spreads = [ ] ;
513
-
514
- /** @type {Property[] } */
515
- const custom_css_props = [ ] ;
516
-
517
- /** @type {ExpressionStatement[] } */
518
- const lets = [ ] ;
519
-
520
- /** @type {Record<string, TemplateNode[]> } */
521
- const children = { } ;
522
-
523
- /**
524
- * If this component has a slot property, it is a named slot within another component. In this case
525
- * the slot scope applies to the component itself, too, and not just its children.
526
- */
527
- let slot_scope_applies_to_itself = false ;
528
-
529
- /**
530
- * Components may have a children prop and also have child nodes. In this case, we assume
531
- * that the child component isn't using render tags yet and pass the slot as $$slots.default.
532
- * We're not doing it for spread attributes, as this would result in too many false positives.
533
- */
534
- let has_children_prop = false ;
535
-
536
- /**
537
- * @param {Property } prop
538
- */
539
- function push_prop ( prop ) {
540
- const current = props_and_spreads . at ( - 1 ) ;
541
- const current_is_props = Array . isArray ( current ) ;
542
- const props = current_is_props ? current : [ ] ;
543
- props . push ( prop ) ;
544
- if ( ! current_is_props ) {
545
- props_and_spreads . push ( props ) ;
546
- }
547
- }
548
- for ( const attribute of node . attributes ) {
549
- if ( attribute . type === 'LetDirective' ) {
550
- lets . push ( /** @type {ExpressionStatement } */ ( context . visit ( attribute ) ) ) ;
551
- } else if ( attribute . type === 'SpreadAttribute' ) {
552
- props_and_spreads . push ( /** @type {Expression } */ ( context . visit ( attribute ) ) ) ;
553
- } else if ( attribute . type === 'Attribute' ) {
554
- if ( attribute . name . startsWith ( '--' ) ) {
555
- const value = serialize_attribute_value ( attribute . value , context , false , true ) ;
556
- custom_css_props . push ( b . init ( attribute . name , value ) ) ;
557
- continue ;
558
- }
559
-
560
- if ( attribute . name === 'slot' ) {
561
- slot_scope_applies_to_itself = true ;
562
- }
563
-
564
- if ( attribute . name === 'children' ) {
565
- has_children_prop = true ;
566
- }
567
-
568
- const value = serialize_attribute_value ( attribute . value , context , false , true ) ;
569
- push_prop ( b . prop ( 'init' , b . key ( attribute . name ) , value ) ) ;
570
- } else if ( attribute . type === 'BindDirective' && attribute . name !== 'this' ) {
571
- // TODO this needs to turn the whole thing into a while loop because the binding could be mutated eagerly in the child
572
- push_prop (
573
- b . get ( attribute . name , [
574
- b . return ( /** @type {Expression } */ ( context . visit ( attribute . expression ) ) )
575
- ] )
576
- ) ;
577
- push_prop (
578
- b . set ( attribute . name , [
579
- b . stmt (
580
- /** @type {Expression } */ (
581
- context . visit ( b . assignment ( '=' , attribute . expression , b . id ( '$$value' ) ) )
582
- )
583
- ) ,
584
- b . stmt ( b . assignment ( '=' , b . id ( '$$settled' ) , b . false ) )
585
- ] )
586
- ) ;
587
- }
588
- }
589
-
590
- if ( slot_scope_applies_to_itself ) {
591
- context . state . init . push ( ...lets ) ;
592
- }
593
-
594
- /** @type {Statement[] } */
595
- const snippet_declarations = [ ] ;
596
-
597
- // Group children by slot
598
- for ( const child of node . fragment . nodes ) {
599
- if ( child . type === 'SnippetBlock' ) {
600
- // the SnippetBlock visitor adds a declaration to `init`, but if it's directly
601
- // inside a component then we want to hoist them into a block so that they
602
- // can be used as props without creating conflicts
603
- context . visit ( child , {
604
- ...context . state ,
605
- init : snippet_declarations
606
- } ) ;
607
-
608
- push_prop ( b . prop ( 'init' , child . expression , child . expression ) ) ;
609
-
610
- continue ;
611
- }
612
-
613
- let slot_name = 'default' ;
614
- if ( is_element_node ( child ) ) {
615
- const attribute = /** @type {Attribute | undefined } */ (
616
- child . attributes . find (
617
- ( attribute ) => attribute . type === 'Attribute' && attribute . name === 'slot'
618
- )
619
- ) ;
620
- if ( attribute !== undefined ) {
621
- slot_name = /** @type {Text[] } */ ( attribute . value ) [ 0 ] . data ;
622
- }
623
- }
624
-
625
- children [ slot_name ] = children [ slot_name ] || [ ] ;
626
- children [ slot_name ] . push ( child ) ;
627
- }
628
-
629
- // Serialize each slot
630
- /** @type {Property[] } */
631
- const serialized_slots = [ ] ;
632
-
633
- for ( const slot_name of Object . keys ( children ) ) {
634
- const block = /** @type {BlockStatement } */ (
635
- context . visit (
636
- {
637
- ...node . fragment ,
638
- // @ts -expect-error
639
- nodes : children [ slot_name ]
640
- } ,
641
- {
642
- ...context . state ,
643
- scope :
644
- context . state . scopes . get ( slot_name === 'default' ? children [ slot_name ] [ 0 ] : node ) ??
645
- context . state . scope
646
- }
647
- )
648
- ) ;
649
-
650
- if ( block . body . length === 0 ) continue ;
651
-
652
- const slot_fn = b . arrow (
653
- [ b . id ( '$$payload' ) , b . id ( '$$slotProps' ) ] ,
654
- b . block ( [
655
- ...( slot_name === 'default' && ! slot_scope_applies_to_itself ? lets : [ ] ) ,
656
- ...block . body
657
- ] )
658
- ) ;
659
-
660
- if ( slot_name === 'default' && ! has_children_prop ) {
661
- if ( lets . length === 0 && children . default . every ( ( node ) => node . type !== 'SvelteFragment' ) ) {
662
- // create `children` prop...
663
- push_prop ( b . prop ( 'init' , b . id ( 'children' ) , slot_fn ) ) ;
664
-
665
- // and `$$slots.default: true` so that `<slot>` on the child works
666
- serialized_slots . push ( b . init ( slot_name , b . true ) ) ;
667
- } else {
668
- // create `$$slots.default`...
669
- serialized_slots . push ( b . init ( slot_name , slot_fn ) ) ;
670
-
671
- // and a `children` prop that errors
672
- push_prop ( b . init ( 'children' , b . id ( '$.invalid_default_snippet' ) ) ) ;
673
- }
674
- } else {
675
- serialized_slots . push ( b . init ( slot_name , slot_fn ) ) ;
676
- }
677
- }
678
-
679
- if ( serialized_slots . length > 0 ) {
680
- push_prop ( b . prop ( 'init' , b . id ( '$$slots' ) , b . object ( serialized_slots ) ) ) ;
681
- }
682
-
683
- const props_expression =
684
- props_and_spreads . length === 0 ||
685
- ( props_and_spreads . length === 1 && Array . isArray ( props_and_spreads [ 0 ] ) )
686
- ? b . object ( /** @type {Property[] } */ ( props_and_spreads [ 0 ] || [ ] ) )
687
- : b . call (
688
- '$.spread_props' ,
689
- b . array ( props_and_spreads . map ( ( p ) => ( Array . isArray ( p ) ? b . object ( p ) : p ) ) )
690
- ) ;
691
-
692
- /** @type {Statement } */
693
- let statement = b . stmt (
694
- ( node . type === 'SvelteComponent' ? b . maybe_call : b . call ) (
695
- expression ,
696
- b . id ( '$$payload' ) ,
697
- props_expression
698
- )
699
- ) ;
700
-
701
- if ( snippet_declarations . length > 0 ) {
702
- statement = b . block ( [ ...snippet_declarations , statement ] ) ;
703
- }
704
-
705
- const dynamic =
706
- node . type === 'SvelteComponent' || ( node . type === 'Component' && node . metadata . dynamic ) ;
707
-
708
- if ( custom_css_props . length > 0 ) {
709
- context . state . template . push (
710
- b . stmt (
711
- b . call (
712
- '$.css_props' ,
713
- b . id ( '$$payload' ) ,
714
- b . literal ( context . state . namespace === 'svg' ? false : true ) ,
715
- b . object ( custom_css_props ) ,
716
- b . thunk ( b . block ( [ statement ] ) ) ,
717
- dynamic && b . true
718
- )
719
- )
720
- ) ;
721
- } else {
722
- if ( dynamic ) {
723
- context . state . template . push ( empty_comment ) ;
724
- }
725
-
726
- context . state . template . push ( statement ) ;
727
-
728
- if ( ! context . state . skip_hydration_boundaries ) {
729
- context . state . template . push ( empty_comment ) ;
730
- }
731
- }
732
- }
733
-
734
510
/** @type {Visitors } */
735
511
const javascript_visitors_legacy = {
736
512
VariableDeclaration ( node , { state, visit } ) {
@@ -834,57 +610,12 @@ const template_visitors = {
834
610
SvelteElement,
835
611
EachBlock,
836
612
IfBlock,
837
- AwaitBlock ( node , context ) {
838
- context . state . template . push (
839
- empty_comment ,
840
- b . stmt (
841
- b . call (
842
- '$.await' ,
843
- /** @type {Expression } */ ( context . visit ( node . expression ) ) ,
844
- b . thunk (
845
- node . pending ? /** @type {BlockStatement } */ ( context . visit ( node . pending ) ) : b . block ( [ ] )
846
- ) ,
847
- b . arrow (
848
- node . value ? [ /** @type {Pattern } */ ( context . visit ( node . value ) ) ] : [ ] ,
849
- node . then ? /** @type {BlockStatement } */ ( context . visit ( node . then ) ) : b . block ( [ ] )
850
- ) ,
851
- b . arrow (
852
- node . error ? [ /** @type {Pattern } */ ( context . visit ( node . error ) ) ] : [ ] ,
853
- node . catch ? /** @type {BlockStatement } */ ( context . visit ( node . catch ) ) : b . block ( [ ] )
854
- )
855
- )
856
- ) ,
857
- empty_comment
858
- ) ;
859
- } ,
860
- KeyBlock ( node , context ) {
861
- const block = /** @type {BlockStatement } */ ( context . visit ( node . fragment ) ) ;
862
- context . state . template . push ( empty_comment , block , empty_comment ) ;
863
- } ,
864
- SnippetBlock ( node , context ) {
865
- const fn = b . function_declaration (
866
- node . expression ,
867
- [ b . id ( '$$payload' ) , ...node . parameters ] ,
868
- /** @type {BlockStatement } */ ( context . visit ( node . body ) )
869
- ) ;
870
- // @ts -expect-error - TODO remove this hack once $$render_inner for legacy bindings is gone
871
- fn . ___snippet = true ;
872
- // TODO hoist where possible
873
- context . state . init . push ( fn ) ;
874
- } ,
875
- Component ( node , context ) {
876
- serialize_inline_component ( node , b . id ( node . name ) , context ) ;
877
- } ,
878
- SvelteSelf ( node , context ) {
879
- serialize_inline_component ( node , b . id ( context . state . analysis . name ) , context ) ;
880
- } ,
881
- SvelteComponent ( node , context ) {
882
- serialize_inline_component (
883
- node ,
884
- /** @type {Expression } */ ( context . visit ( node . expression ) ) ,
885
- context
886
- ) ;
887
- } ,
613
+ AwaitBlock,
614
+ KeyBlock,
615
+ SnippetBlock,
616
+ Component,
617
+ SvelteSelf,
618
+ SvelteComponent,
888
619
LetDirective ( node , { state } ) {
889
620
if ( node . expression === null || node . expression . type === 'Identifier' ) {
890
621
const name = node . expression === null ? node . name : node . expression . name ;
0 commit comments