11/**
22 * @import { TemplateOperations } from "../types.js"
33 * @import { Namespace } from "#compiler"
4- * @import { Statement } from "estree"
4+ * @import { CallExpression, Statement } from "estree"
55 */
66import { NAMESPACE_SVG } from 'svelte/internal/client' ;
7- import * as b from '../../../../utils/builders.js' ;
87import { NAMESPACE_MATHML } from '../../../../../constants.js' ;
8+ import * as b from '../../../../utils/builders.js' ;
9+ import fix_attribute_casing from './fix-attribute-casing.js' ;
910
1011class Scope {
1112 declared = new Map ( ) ;
@@ -28,9 +29,8 @@ class Scope {
2829/**
2930 * @param {TemplateOperations } items
3031 * @param {Namespace } namespace
31- * @param {boolean } use_fragment
3232 */
33- export function template_to_functions ( items , namespace , use_fragment = false ) {
33+ export function template_to_functions ( items , namespace ) {
3434 let elements = [ ] ;
3535
3636 let body = [ ] ;
@@ -42,26 +42,61 @@ export function template_to_functions(items, namespace, use_fragment = false) {
4242 */
4343 let elements_stack = [ ] ;
4444
45+ /**
46+ * @type {Array<string> }
47+ */
48+ let namespace_stack = [ ] ;
49+
50+ /**
51+ * @type {number }
52+ */
53+ let foreign_object_count = 0 ;
54+
4555 /**
4656 * @type {Element | undefined }
4757 */
4858 let last_current_element ;
4959
60+ if ( items [ 0 ] . kind === 'create_anchor' ) {
61+ items . unshift ( { kind : 'create_anchor' } ) ;
62+ }
63+
5064 for ( let instruction of items ) {
5165 if ( instruction . kind === 'push_element' && last_current_element ) {
5266 elements_stack . push ( last_current_element ) ;
5367 continue ;
5468 }
5569 if ( instruction . kind === 'pop_element' ) {
56- elements_stack . pop ( ) ;
70+ const removed = elements_stack . pop ( ) ;
71+ if ( removed ?. namespaced ) {
72+ namespace_stack . pop ( ) ;
73+ }
74+ if ( removed ?. element === 'foreignObject' ) {
75+ foreign_object_count -- ;
76+ }
5777 continue ;
5878 }
5979
80+ if ( instruction . metadata ?. svg || instruction . metadata ?. mathml ) {
81+ namespace_stack . push ( instruction . metadata . svg ? NAMESPACE_SVG : NAMESPACE_MATHML ) ;
82+ }
83+
6084 // @ts -expect-error we can't be here if `swap_current_element` but TS doesn't know that
6185 const value = map [ instruction . kind ] (
6286 ...[
6387 ...( instruction . kind === 'set_prop' ? [ last_current_element ] : [ scope ] ) ,
64- ...( instruction . kind === 'create_element' ? [ namespace ] : [ ] ) ,
88+ ...( instruction . kind === 'create_element'
89+ ? [
90+ foreign_object_count > 0
91+ ? undefined
92+ : namespace_stack . at ( - 1 ) ??
93+ ( namespace === 'svg'
94+ ? NAMESPACE_SVG
95+ : namespace === 'mathml'
96+ ? NAMESPACE_MATHML
97+ : undefined )
98+ ]
99+ : [ ] ) ,
65100 ...( instruction . args ?? [ ] )
66101 ]
67102 ) ;
@@ -79,23 +114,22 @@ export function template_to_functions(items, namespace, use_fragment = false) {
79114 }
80115 if ( instruction . kind === 'create_element' ) {
81116 last_current_element = /** @type {Element } */ ( value ) ;
117+ if ( last_current_element . element === 'foreignObject' ) {
118+ foreign_object_count ++ ;
119+ }
82120 }
83121 }
84122 }
85- if ( elements . length > 1 || use_fragment ) {
86- const fragment = scope . generate ( 'fragment' ) ;
87- body . push ( b . var ( fragment , b . call ( 'document.createDocumentFragment' ) ) ) ;
88- body . push ( b . call ( fragment + '.append' , ...elements ) ) ;
89- body . push ( b . return ( b . id ( fragment ) ) ) ;
90- } else {
91- body . push ( b . return ( elements [ 0 ] ) ) ;
92- }
123+ const fragment = scope . generate ( 'fragment' ) ;
124+ body . push ( b . var ( fragment , b . call ( 'document.createDocumentFragment' ) ) ) ;
125+ body . push ( b . call ( fragment + '.append' , ...elements ) ) ;
126+ body . push ( b . return ( b . id ( fragment ) ) ) ;
93127
94128 return b . arrow ( [ ] , b . block ( body ) ) ;
95129}
96130
97131/**
98- * @typedef {{ call: Statement, name: string } } Element
132+ * @typedef {{ call: Statement, name: string, add_is: (value: string)=>void, namespaced: boolean; element: string; } } Element
99133 */
100134
101135/**
@@ -118,14 +152,26 @@ export function template_to_functions(items, namespace, use_fragment = false) {
118152 */
119153function create_element ( scope , namespace , element ) {
120154 const name = scope . generate ( element ) ;
121- let fn = namespace !== 'html' ? 'document.createElementNS' : 'document.createElement' ;
155+ let fn = namespace != null ? 'document.createElementNS' : 'document.createElement' ;
122156 let args = [ b . literal ( element ) ] ;
123- if ( namespace !== 'html' ) {
124- args . unshift ( namespace === 'svg' ? b . literal ( NAMESPACE_SVG ) : b . literal ( NAMESPACE_MATHML ) ) ;
157+ if ( namespace != null ) {
158+ args . unshift ( b . literal ( namespace ) ) ;
159+ }
160+ const call = b . var ( name , b . call ( fn , ...args ) ) ;
161+ /**
162+ * @param {string } value
163+ */
164+ function add_is ( value ) {
165+ /** @type {CallExpression } */ ( call . declarations [ 0 ] . init ) . arguments . push (
166+ b . object ( [ b . prop ( 'init' , b . literal ( 'is' ) , b . literal ( value ) ) ] )
167+ ) ;
125168 }
126169 return {
127- call : b . var ( name , b . call ( fn , ...args ) ) ,
128- name
170+ call,
171+ name,
172+ element,
173+ add_is,
174+ namespaced : namespace != null
129175 } ;
130176}
131177
@@ -162,8 +208,21 @@ function create_text(scope, value) {
162208 * @param {string } value
163209 */
164210function set_prop ( el , prop , value ) {
211+ if ( prop === 'is' ) {
212+ el . add_is ( value ) ;
213+ return ;
214+ }
215+
216+ const [ namespace ] = prop . split ( ':' ) ;
217+ let fn = namespace !== prop ? '.setAttributeNS' : '.setAttribute' ;
218+ let args = [ b . literal ( fix_attribute_casing ( prop ) ) , b . literal ( value ?? '' ) ] ;
219+
220+ if ( namespace === 'xlink' ) {
221+ args . unshift ( b . literal ( 'http://www.w3.org/1999/xlink' ) ) ;
222+ }
223+
165224 return {
166- call : b . call ( el . name + '.setAttribute' , b . literal ( prop ) , b . literal ( value ) )
225+ call : b . call ( el . name + fn , ... args )
167226 } ;
168227}
169228
@@ -175,7 +234,11 @@ function set_prop(el, prop, value) {
175234 */
176235function insert ( el , child , anchor ) {
177236 return {
178- call : b . call ( el . name + '.insertBefore' , b . id ( child . name ) , b . id ( anchor ?. name ?? 'undefined' ) )
237+ call : b . call (
238+ el . name + ( el . element === 'template' ? '.content' : '' ) + '.insertBefore' ,
239+ b . id ( child . name ) ,
240+ b . id ( anchor ?. name ?? 'undefined' )
241+ )
179242 } ;
180243}
181244
0 commit comments