@@ -57,15 +57,20 @@ export function template_to_functions(items, namespace) {
57
57
*/
58
58
let last_current_element ;
59
59
60
+ // if the first item is a comment we need to add another comment for effect.start
60
61
if ( items [ 0 ] . kind === 'create_anchor' ) {
61
62
items . unshift ( { kind : 'create_anchor' } ) ;
62
63
}
63
64
64
65
for ( let instruction of items ) {
66
+ // on push element we add the element to the stack, from this moment on every insert will
67
+ // happen on the last element in the stack
65
68
if ( instruction . kind === 'push_element' && last_current_element ) {
66
69
elements_stack . push ( last_current_element ) ;
67
70
continue ;
68
71
}
72
+ // we closed one element, we remove it from the stack and eventually revert back
73
+ // the namespace to the previous one
69
74
if ( instruction . kind === 'pop_element' ) {
70
75
const removed = elements_stack . pop ( ) ;
71
76
if ( removed ?. namespaced ) {
@@ -77,14 +82,21 @@ export function template_to_functions(items, namespace) {
77
82
continue ;
78
83
}
79
84
85
+ // if the inserted node is in the svg/mathml we push the namespace to the stack because we need to
86
+ // create with createElementNS
80
87
if ( instruction . metadata ?. svg || instruction . metadata ?. mathml ) {
81
88
namespace_stack . push ( instruction . metadata . svg ? NAMESPACE_SVG : NAMESPACE_MATHML ) ;
82
89
}
83
90
84
91
// @ts -expect-error we can't be here if `swap_current_element` but TS doesn't know that
85
92
const value = map [ instruction . kind ] (
86
93
...[
94
+ // for set prop we need to send the last element (not the one in the stack since
95
+ // it get's added to the stack only after the push_element instruction)...for all the rest
96
+ // the first prop is a the scope to generate the name of the variable
87
97
...( instruction . kind === 'set_prop' ? [ last_current_element ] : [ scope ] ) ,
98
+ // for create element we also need to add the namespace...namespaces in the stack get's precedence over
99
+ // the "global" namespace (and if we are in a foreignObject we default to html)
88
100
...( instruction . kind === 'create_element'
89
101
? [
90
102
foreign_object_count > 0
@@ -102,16 +114,20 @@ export function template_to_functions(items, namespace) {
102
114
) ;
103
115
104
116
if ( value ) {
117
+ // this will compose the body of the function
105
118
body . push ( value . call ) ;
106
119
}
107
120
121
+ // with set_prop we don't need to do anything else, in all other cases we also need to
122
+ // append the element/node/anchor to the current active element or push it in the elements array
108
123
if ( instruction . kind !== 'set_prop' ) {
109
124
if ( elements_stack . length >= 1 && value ) {
110
125
const { call } = map . insert ( /** @type {Element } */ ( elements_stack . at ( - 1 ) ) , value ) ;
111
126
body . push ( call ) ;
112
127
} else if ( value ) {
113
128
elements . push ( b . id ( value . name ) ) ;
114
129
}
130
+ // keep track of the last created element (it will be pushed to the stack after the props are set)
115
131
if ( instruction . kind === 'create_element' ) {
116
132
last_current_element = /** @type {Element } */ ( value ) ;
117
133
if ( last_current_element . element === 'foreignObject' ) {
@@ -120,6 +136,7 @@ export function template_to_functions(items, namespace) {
120
136
}
121
137
}
122
138
}
139
+ // every function needs to return a fragment so we create one and push all the elements there
123
140
const fragment = scope . generate ( 'fragment' ) ;
124
141
body . push ( b . var ( fragment , b . call ( 'document.createDocumentFragment' ) ) ) ;
125
142
body . push ( b . call ( fragment + '.append' , ...elements ) ) ;
@@ -159,6 +176,11 @@ function create_element(scope, namespace, element) {
159
176
}
160
177
const call = b . var ( name , b . call ( fn , ...args ) ) ;
161
178
/**
179
+ * if there's an "is" attribute we can't just add it as a property, it needs to be
180
+ * specified on creation like this `document.createElement('button', { is: 'my-button' })`
181
+ *
182
+ * Since the props are appended after the creation we change the generated call arguments and we push
183
+ * the is attribute later on on `set_prop`
162
184
* @param {string } value
163
185
*/
164
186
function add_is ( value ) {
@@ -208,6 +230,7 @@ function create_text(scope, value) {
208
230
* @param {string } value
209
231
*/
210
232
function set_prop ( el , prop , value ) {
233
+ // see comment above about the "is" attribute
211
234
if ( prop === 'is' ) {
212
235
el . add_is ( value ) ;
213
236
return ;
@@ -217,6 +240,7 @@ function set_prop(el, prop, value) {
217
240
let fn = namespace !== prop ? '.setAttributeNS' : '.setAttribute' ;
218
241
let args = [ b . literal ( fix_attribute_casing ( prop ) ) , b . literal ( value ?? '' ) ] ;
219
242
243
+ // attributes like `xlink:href` need to be set with the `xlink` namespace
220
244
if ( namespace === 'xlink' ) {
221
245
args . unshift ( b . literal ( 'http://www.w3.org/1999/xlink' ) ) ;
222
246
}
@@ -235,6 +259,7 @@ function set_prop(el, prop, value) {
235
259
function insert ( el , child , anchor ) {
236
260
return {
237
261
call : b . call (
262
+ // if we have a template element we need to push into it's content rather than the element itself
238
263
el . name + ( el . element === 'template' ? '.content' : '' ) + '.insertBefore' ,
239
264
b . id ( child . name ) ,
240
265
b . id ( anchor ?. name ?? 'undefined' )
0 commit comments