@@ -1028,190 +1028,6 @@ function serialize_locations(locations) {
1028
1028
) ;
1029
1029
}
1030
1030
1031
- /**
1032
- * Creates a new block which looks roughly like this:
1033
- * ```js
1034
- * // hoisted:
1035
- * const block_name = $.template(`...`);
1036
- *
1037
- * // for the main block:
1038
- * const id = block_name();
1039
- * // init stuff and possibly render effect
1040
- * $.append($$anchor, id);
1041
- * ```
1042
- * Adds the hoisted parts to `context.state.hoisted` and returns the statements of the main block.
1043
- * @param {import('#compiler').SvelteNode } parent
1044
- * @param {import('#compiler').Fragment } fragment
1045
- * @param {string } name
1046
- * @param {import('#compiler').SvelteNode[] } nodes
1047
- * @param {import('../types.js').ComponentContext } context
1048
- * @returns {import('estree').Statement[] }
1049
- */
1050
- function create_block ( parent , fragment , name , nodes , context ) {
1051
- const namespace = infer_namespace ( context . state . metadata . namespace , parent , nodes ) ;
1052
-
1053
- const { hoisted, trimmed } = clean_nodes (
1054
- parent ,
1055
- nodes ,
1056
- context . path ,
1057
- namespace ,
1058
- context . state ,
1059
- context . state . preserve_whitespace ,
1060
- context . state . options . preserveComments
1061
- ) ;
1062
-
1063
- if ( hoisted . length === 0 && trimmed . length === 0 ) {
1064
- return [ ] ;
1065
- }
1066
-
1067
- const is_single_element = trimmed . length === 1 && trimmed [ 0 ] . type === 'RegularElement' ;
1068
- const is_single_child_not_needing_template =
1069
- trimmed . length === 1 &&
1070
- ( trimmed [ 0 ] . type === 'SvelteFragment' || trimmed [ 0 ] . type === 'TitleElement' ) ;
1071
-
1072
- const template_name = context . state . scope . root . unique ( name ) ;
1073
-
1074
- /** @type {import('estree').Statement[] } */
1075
- const body = [ ] ;
1076
-
1077
- /** @type {import('estree').Statement | undefined } */
1078
- let close = undefined ;
1079
-
1080
- /** @type {import('../types').ComponentClientTransformState } */
1081
- const state = {
1082
- ...context . state ,
1083
- scope : context . state . scopes . get ( fragment ) ?? context . state . scope ,
1084
- before_init : [ ] ,
1085
- init : [ ] ,
1086
- update : [ ] ,
1087
- after_update : [ ] ,
1088
- template : [ ] ,
1089
- locations : [ ] ,
1090
- metadata : {
1091
- context : {
1092
- template_needs_import_node : false ,
1093
- template_contains_script_tag : false
1094
- } ,
1095
- namespace,
1096
- bound_contenteditable : context . state . metadata . bound_contenteditable
1097
- }
1098
- } ;
1099
-
1100
- for ( const node of hoisted ) {
1101
- context . visit ( node , state ) ;
1102
- }
1103
-
1104
- /**
1105
- * @param {import('estree').Identifier } template_name
1106
- * @param {import('estree').Expression[] } args
1107
- */
1108
- const add_template = ( template_name , args ) => {
1109
- let call = b . call ( get_template_function ( namespace , state ) , ...args ) ;
1110
- if ( context . state . options . dev ) {
1111
- call = b . call (
1112
- '$.add_locations' ,
1113
- call ,
1114
- b . member ( b . id ( context . state . analysis . name ) , b . id ( 'filename' ) ) ,
1115
- serialize_locations ( state . locations )
1116
- ) ;
1117
- }
1118
-
1119
- context . state . hoisted . push ( b . var ( template_name , call ) ) ;
1120
- } ;
1121
-
1122
- if ( is_single_element ) {
1123
- const element = /** @type {import('#compiler').RegularElement } */ ( trimmed [ 0 ] ) ;
1124
-
1125
- const id = b . id ( context . state . scope . generate ( element . name ) ) ;
1126
-
1127
- context . visit ( element , {
1128
- ...state ,
1129
- node : id
1130
- } ) ;
1131
-
1132
- /** @type {import('estree').Expression[] } */
1133
- const args = [ b . template ( [ b . quasi ( state . template . join ( '' ) , true ) ] , [ ] ) ] ;
1134
-
1135
- if ( state . metadata . context . template_needs_import_node ) {
1136
- args . push ( b . literal ( TEMPLATE_USE_IMPORT_NODE ) ) ;
1137
- }
1138
-
1139
- add_template ( template_name , args ) ;
1140
-
1141
- body . push ( b . var ( id , b . call ( template_name ) ) , ...state . before_init , ...state . init ) ;
1142
- close = b . stmt ( b . call ( '$.append' , b . id ( '$$anchor' ) , id ) ) ;
1143
- } else if ( is_single_child_not_needing_template ) {
1144
- context . visit ( trimmed [ 0 ] , state ) ;
1145
- body . push ( ...state . before_init , ...state . init ) ;
1146
- } else if ( trimmed . length > 0 ) {
1147
- const id = b . id ( context . state . scope . generate ( 'fragment' ) ) ;
1148
-
1149
- const use_space_template =
1150
- trimmed . some ( ( node ) => node . type === 'ExpressionTag' ) &&
1151
- trimmed . every ( ( node ) => node . type === 'Text' || node . type === 'ExpressionTag' ) ;
1152
-
1153
- if ( use_space_template ) {
1154
- // special case — we can use `$.text` instead of creating a unique template
1155
- const id = b . id ( context . state . scope . generate ( 'text' ) ) ;
1156
-
1157
- process_children ( trimmed , ( ) => id , false , {
1158
- ...context ,
1159
- state
1160
- } ) ;
1161
-
1162
- body . push ( b . var ( id , b . call ( '$.text' , b . id ( '$$anchor' ) ) ) , ...state . before_init , ...state . init ) ;
1163
- close = b . stmt ( b . call ( '$.append' , b . id ( '$$anchor' ) , id ) ) ;
1164
- } else {
1165
- /** @type {(is_text: boolean) => import('estree').Expression } */
1166
- const expression = ( is_text ) =>
1167
- is_text ? b . call ( '$.first_child' , id , b . true ) : b . call ( '$.first_child' , id ) ;
1168
-
1169
- process_children ( trimmed , expression , false , { ...context , state } ) ;
1170
-
1171
- const use_comment_template = state . template . length === 1 && state . template [ 0 ] === '<!>' ;
1172
-
1173
- if ( use_comment_template ) {
1174
- // special case — we can use `$.comment` instead of creating a unique template
1175
- body . push ( b . var ( id , b . call ( '$.comment' ) ) ) ;
1176
- } else {
1177
- let flags = TEMPLATE_FRAGMENT ;
1178
-
1179
- if ( state . metadata . context . template_needs_import_node ) {
1180
- flags |= TEMPLATE_USE_IMPORT_NODE ;
1181
- }
1182
-
1183
- add_template ( template_name , [
1184
- b . template ( [ b . quasi ( state . template . join ( '' ) , true ) ] , [ ] ) ,
1185
- b . literal ( flags )
1186
- ] ) ;
1187
-
1188
- body . push ( b . var ( id , b . call ( template_name ) ) ) ;
1189
- }
1190
-
1191
- body . push ( ...state . before_init , ...state . init ) ;
1192
-
1193
- close = b . stmt ( b . call ( '$.append' , b . id ( '$$anchor' ) , id ) ) ;
1194
- }
1195
- } else {
1196
- body . push ( ...state . before_init , ...state . init ) ;
1197
- }
1198
-
1199
- if ( state . update . length > 0 ) {
1200
- body . push ( serialize_render_stmt ( state ) ) ;
1201
- }
1202
-
1203
- body . push ( ...state . after_update ) ;
1204
-
1205
- if ( close !== undefined ) {
1206
- // It's important that close is the last statement in the block, as any previous statements
1207
- // could contain element insertions into the template, which the close statement needs to
1208
- // know of when constructing the list of current inner elements.
1209
- body . push ( close ) ;
1210
- }
1211
-
1212
- return body ;
1213
- }
1214
-
1215
1031
/**
1216
1032
*
1217
1033
* @param {import('#compiler').Namespace } namespace
@@ -1701,7 +1517,184 @@ function serialize_template_literal(values, visit, state) {
1701
1517
/** @type {import('../types').ComponentVisitors } */
1702
1518
export const template_visitors = {
1703
1519
Fragment ( node , context ) {
1704
- const body = create_block ( context . path . at ( - 1 ) ?? node , node , 'root' , node . nodes , context ) ;
1520
+ // Creates a new block which looks roughly like this:
1521
+ // ```js
1522
+ // // hoisted:
1523
+ // const block_name = $.template(`...`);
1524
+ //
1525
+ // // for the main block:
1526
+ // const id = block_name();
1527
+ // // init stuff and possibly render effect
1528
+ // $.append($$anchor, id);
1529
+ // ```
1530
+ // Adds the hoisted parts to `context.state.hoisted` and returns the statements of the main block.
1531
+
1532
+ const parent = context . path . at ( - 1 ) ?? node ;
1533
+
1534
+ const namespace = infer_namespace ( context . state . metadata . namespace , parent , node . nodes ) ;
1535
+
1536
+ const { hoisted, trimmed } = clean_nodes (
1537
+ parent ,
1538
+ node . nodes ,
1539
+ context . path ,
1540
+ namespace ,
1541
+ context . state ,
1542
+ context . state . preserve_whitespace ,
1543
+ context . state . options . preserveComments
1544
+ ) ;
1545
+
1546
+ if ( hoisted . length === 0 && trimmed . length === 0 ) {
1547
+ return b . block ( [ ] ) ;
1548
+ }
1549
+
1550
+ const is_single_element = trimmed . length === 1 && trimmed [ 0 ] . type === 'RegularElement' ;
1551
+ const is_single_child_not_needing_template =
1552
+ trimmed . length === 1 &&
1553
+ ( trimmed [ 0 ] . type === 'SvelteFragment' || trimmed [ 0 ] . type === 'TitleElement' ) ;
1554
+
1555
+ const template_name = context . state . scope . root . unique ( 'root' ) ; // TODO infer name from parent
1556
+
1557
+ /** @type {import('estree').Statement[] } */
1558
+ const body = [ ] ;
1559
+
1560
+ /** @type {import('estree').Statement | undefined } */
1561
+ let close = undefined ;
1562
+
1563
+ /** @type {import('../types').ComponentClientTransformState } */
1564
+ const state = {
1565
+ ...context . state ,
1566
+ before_init : [ ] ,
1567
+ init : [ ] ,
1568
+ update : [ ] ,
1569
+ after_update : [ ] ,
1570
+ template : [ ] ,
1571
+ locations : [ ] ,
1572
+ metadata : {
1573
+ context : {
1574
+ template_needs_import_node : false ,
1575
+ template_contains_script_tag : false
1576
+ } ,
1577
+ namespace,
1578
+ bound_contenteditable : context . state . metadata . bound_contenteditable
1579
+ }
1580
+ } ;
1581
+
1582
+ for ( const node of hoisted ) {
1583
+ context . visit ( node , state ) ;
1584
+ }
1585
+
1586
+ /**
1587
+ * @param {import('estree').Identifier } template_name
1588
+ * @param {import('estree').Expression[] } args
1589
+ */
1590
+ const add_template = ( template_name , args ) => {
1591
+ let call = b . call ( get_template_function ( namespace , state ) , ...args ) ;
1592
+ if ( context . state . options . dev ) {
1593
+ call = b . call (
1594
+ '$.add_locations' ,
1595
+ call ,
1596
+ b . member ( b . id ( context . state . analysis . name ) , b . id ( 'filename' ) ) ,
1597
+ serialize_locations ( state . locations )
1598
+ ) ;
1599
+ }
1600
+
1601
+ context . state . hoisted . push ( b . var ( template_name , call ) ) ;
1602
+ } ;
1603
+
1604
+ if ( is_single_element ) {
1605
+ const element = /** @type {import('#compiler').RegularElement } */ ( trimmed [ 0 ] ) ;
1606
+
1607
+ const id = b . id ( context . state . scope . generate ( element . name ) ) ;
1608
+
1609
+ context . visit ( element , {
1610
+ ...state ,
1611
+ node : id
1612
+ } ) ;
1613
+
1614
+ /** @type {import('estree').Expression[] } */
1615
+ const args = [ b . template ( [ b . quasi ( state . template . join ( '' ) , true ) ] , [ ] ) ] ;
1616
+
1617
+ if ( state . metadata . context . template_needs_import_node ) {
1618
+ args . push ( b . literal ( TEMPLATE_USE_IMPORT_NODE ) ) ;
1619
+ }
1620
+
1621
+ add_template ( template_name , args ) ;
1622
+
1623
+ body . push ( b . var ( id , b . call ( template_name ) ) , ...state . before_init , ...state . init ) ;
1624
+ close = b . stmt ( b . call ( '$.append' , b . id ( '$$anchor' ) , id ) ) ;
1625
+ } else if ( is_single_child_not_needing_template ) {
1626
+ context . visit ( trimmed [ 0 ] , state ) ;
1627
+ body . push ( ...state . before_init , ...state . init ) ;
1628
+ } else if ( trimmed . length > 0 ) {
1629
+ const id = b . id ( context . state . scope . generate ( 'fragment' ) ) ;
1630
+
1631
+ const use_space_template =
1632
+ trimmed . some ( ( node ) => node . type === 'ExpressionTag' ) &&
1633
+ trimmed . every ( ( node ) => node . type === 'Text' || node . type === 'ExpressionTag' ) ;
1634
+
1635
+ if ( use_space_template ) {
1636
+ // special case — we can use `$.text` instead of creating a unique template
1637
+ const id = b . id ( context . state . scope . generate ( 'text' ) ) ;
1638
+
1639
+ process_children ( trimmed , ( ) => id , false , {
1640
+ ...context ,
1641
+ state
1642
+ } ) ;
1643
+
1644
+ body . push (
1645
+ b . var ( id , b . call ( '$.text' , b . id ( '$$anchor' ) ) ) ,
1646
+ ...state . before_init ,
1647
+ ...state . init
1648
+ ) ;
1649
+ close = b . stmt ( b . call ( '$.append' , b . id ( '$$anchor' ) , id ) ) ;
1650
+ } else {
1651
+ /** @type {(is_text: boolean) => import('estree').Expression } */
1652
+ const expression = ( is_text ) =>
1653
+ is_text ? b . call ( '$.first_child' , id , b . true ) : b . call ( '$.first_child' , id ) ;
1654
+
1655
+ process_children ( trimmed , expression , false , { ...context , state } ) ;
1656
+
1657
+ const use_comment_template = state . template . length === 1 && state . template [ 0 ] === '<!>' ;
1658
+
1659
+ if ( use_comment_template ) {
1660
+ // special case — we can use `$.comment` instead of creating a unique template
1661
+ body . push ( b . var ( id , b . call ( '$.comment' ) ) ) ;
1662
+ } else {
1663
+ let flags = TEMPLATE_FRAGMENT ;
1664
+
1665
+ if ( state . metadata . context . template_needs_import_node ) {
1666
+ flags |= TEMPLATE_USE_IMPORT_NODE ;
1667
+ }
1668
+
1669
+ add_template ( template_name , [
1670
+ b . template ( [ b . quasi ( state . template . join ( '' ) , true ) ] , [ ] ) ,
1671
+ b . literal ( flags )
1672
+ ] ) ;
1673
+
1674
+ body . push ( b . var ( id , b . call ( template_name ) ) ) ;
1675
+ }
1676
+
1677
+ body . push ( ...state . before_init , ...state . init ) ;
1678
+
1679
+ close = b . stmt ( b . call ( '$.append' , b . id ( '$$anchor' ) , id ) ) ;
1680
+ }
1681
+ } else {
1682
+ body . push ( ...state . before_init , ...state . init ) ;
1683
+ }
1684
+
1685
+ if ( state . update . length > 0 ) {
1686
+ body . push ( serialize_render_stmt ( state ) ) ;
1687
+ }
1688
+
1689
+ body . push ( ...state . after_update ) ;
1690
+
1691
+ if ( close !== undefined ) {
1692
+ // It's important that close is the last statement in the block, as any previous statements
1693
+ // could contain element insertions into the template, which the close statement needs to
1694
+ // know of when constructing the list of current inner elements.
1695
+ body . push ( close ) ;
1696
+ }
1697
+
1705
1698
return b . block ( body ) ;
1706
1699
} ,
1707
1700
Comment ( node , context ) {
0 commit comments