@@ -37,6 +37,7 @@ import { getDocument } from '../util/dom';
3737import { directGetAttribute , directSetAttribute } from './fast-calls' ;
3838import { HOST_TYPE , SKIP_RENDER_TYPE } from './jsx/jsx-runtime' ;
3939import { assertQrl } from '../import/qrl-class' ;
40+ import { isElement } from '../util/element' ;
4041
4142export const SVG_NS = 'http://www.w3.org/2000/svg' ;
4243
@@ -60,7 +61,7 @@ export interface RenderOperation {
6061 $fn$ : ( ) => void ;
6162}
6263
63- export type ChildrenMode = 'root' | 'slot' | 'fallback' | 'default' ;
64+ export type ChildrenMode = 'root' | 'slot' | 'fallback' | 'default' | 'head' ;
6465
6566/**
6667 * @alpha
@@ -97,6 +98,10 @@ export const smartUpdateChildren = (
9798 }
9899 ch = ch [ 0 ] . $children$ ;
99100 }
101+ const isHead = elm . nodeName === 'HEAD' ;
102+ if ( isHead ) {
103+ mode = 'head' ;
104+ }
100105 const oldCh = getChildren ( elm , mode ) ;
101106 if ( qDev ) {
102107 if ( elm . nodeType === 9 ) {
@@ -110,9 +115,9 @@ export const smartUpdateChildren = (
110115 }
111116 }
112117 if ( oldCh . length > 0 && ch . length > 0 ) {
113- return updateChildren ( ctx , elm , oldCh , ch , isSvg ) ;
118+ return updateChildren ( ctx , elm , oldCh , ch , isSvg , isHead ) ;
114119 } else if ( ch . length > 0 ) {
115- return addVnodes ( ctx , elm , null , ch , 0 , ch . length - 1 , isSvg ) ;
120+ return addVnodes ( ctx , elm , null , ch , 0 , ch . length - 1 , isSvg , isHead ) ;
116121 } else if ( oldCh . length > 0 ) {
117122 return removeVnodes ( ctx , oldCh , 0 , oldCh . length - 1 ) ;
118123 }
@@ -123,7 +128,8 @@ export const updateChildren = (
123128 parentElm : Node ,
124129 oldCh : Node [ ] ,
125130 newCh : ProcessedJSXNode [ ] ,
126- isSvg : boolean
131+ isSvg : boolean ,
132+ isHead : boolean
127133) : ValueOrPromise < void > => {
128134 let oldStartIdx = 0 ;
129135 let newStartIdx = 0 ;
@@ -176,7 +182,7 @@ export const updateChildren = (
176182 idxInOld = oldKeyToIdx [ newStartVnode . $key$ as string ] ;
177183 if ( idxInOld === undefined ) {
178184 // New element
179- const newElm = createElm ( ctx , newStartVnode , isSvg ) ;
185+ const newElm = createElm ( ctx , newStartVnode , isSvg , isHead ) ;
180186 results . push (
181187 then ( newElm , ( newElm ) => {
182188 insertBefore ( ctx , parentElm , newElm , oldStartVnode ) ;
@@ -185,7 +191,7 @@ export const updateChildren = (
185191 } else {
186192 elmToMove = oldCh [ idxInOld ] ;
187193 if ( ! isTagName ( elmToMove , newStartVnode . $type$ ) ) {
188- const newElm = createElm ( ctx , newStartVnode , isSvg ) ;
194+ const newElm = createElm ( ctx , newStartVnode , isSvg , isHead ) ;
189195 results . push (
190196 then ( newElm , ( newElm ) => {
191197 insertBefore ( ctx , parentElm , newElm , oldStartVnode ) ;
@@ -203,7 +209,7 @@ export const updateChildren = (
203209
204210 if ( newStartIdx <= newEndIdx ) {
205211 const before = newCh [ newEndIdx + 1 ] == null ? null : newCh [ newEndIdx + 1 ] . $elm$ ;
206- results . push ( addVnodes ( ctx , parentElm , before , newCh , newStartIdx , newEndIdx , isSvg ) ) ;
212+ results . push ( addVnodes ( ctx , parentElm , before , newCh , newStartIdx , newEndIdx , isSvg , isHead ) ) ;
207213 }
208214
209215 let wait = promiseAll ( results ) as any ;
@@ -236,6 +242,8 @@ export const getChildren = (elm: Node, mode: ChildrenMode): Node[] => {
236242 return getCh ( elm , isChildComponent ) ;
237243 case 'fallback' :
238244 return getCh ( elm , isFallback ) ;
245+ case 'head' :
246+ return getCh ( elm , isHeadChildren ) ;
239247 }
240248} ;
241249export const isNode = ( elm : Node ) : boolean => {
@@ -247,6 +255,10 @@ const isFallback = (node: Node): boolean => {
247255 return node . nodeName === 'Q:FALLBACK' ;
248256} ;
249257
258+ const isHeadChildren = ( node : Node ) : boolean => {
259+ return isElement ( node ) && ( node . hasAttribute ( 'q:head' ) || node . nodeName === 'TITLE' ) ;
260+ } ;
261+
250262const isChildSlot = ( node : Node ) : boolean => {
251263 return isNode ( node ) && node . nodeName !== 'Q:FALLBACK' && node . nodeName !== 'Q:TEMPLATE' ;
252264} ;
@@ -370,13 +382,14 @@ const addVnodes = (
370382 vnodes : ProcessedJSXNode [ ] ,
371383 startIdx : number ,
372384 endIdx : number ,
373- isSvg : boolean
385+ isSvg : boolean ,
386+ isHead : boolean
374387) : ValueOrPromise < void > => {
375388 const promises = [ ] ;
376389 for ( ; startIdx <= endIdx ; ++ startIdx ) {
377390 const ch = vnodes [ startIdx ] ;
378391 assertDefined ( ch , 'render: node must be defined at index' , startIdx , vnodes ) ;
379- promises . push ( createElm ( ctx , ch , isSvg ) ) ;
392+ promises . push ( createElm ( ctx , ch , isSvg , isHead ) ) ;
380393 }
381394 return then ( promiseAll ( promises ) , ( children ) => {
382395 for ( const child of children ) {
@@ -501,7 +514,8 @@ const getSlotName = (node: ProcessedJSXNode): string => {
501514const createElm = (
502515 rctx : RenderContext ,
503516 vnode : ProcessedJSXNode ,
504- isSvg : boolean
517+ isSvg : boolean ,
518+ isHead : boolean
505519) : ValueOrPromise < Node > => {
506520 rctx . $perf$ . $visited$ ++ ;
507521 const tag = vnode . $type$ ;
@@ -523,6 +537,9 @@ const createElm = (
523537 const ctx = getContext ( elm ) ;
524538 setKey ( elm , vnode . $key$ ) ;
525539 updateProperties ( rctx , ctx , props , isSvg , false ) ;
540+ if ( isHead ) {
541+ directSetAttribute ( elm as Element , 'q:head' , '' ) ;
542+ }
526543
527544 if ( isSvg && tag === 'foreignObject' ) {
528545 isSvg = false ;
@@ -565,7 +582,7 @@ const createElm = (
565582 const slotRctx = copyRenderContext ( rctx ) ;
566583 slotRctx . $contexts$ . push ( ctx ) ;
567584 const slotMap = isComponent ? getSlots ( ctx . $component$ , elm ) : undefined ;
568- const promises = children . map ( ( ch ) => createElm ( slotRctx , ch , isSvg ) ) ;
585+ const promises = children . map ( ( ch ) => createElm ( slotRctx , ch , isSvg , false ) ) ;
569586 return then ( promiseAll ( promises ) , ( ) => {
570587 let parent = elm ;
571588 for ( const node of children ) {
@@ -845,12 +862,15 @@ const insertBefore = <T extends Node>(
845862export const appendStyle = ( ctx : RenderContext , hostElement : Element , styleTask : StyleAppend ) => {
846863 const fn = ( ) => {
847864 const containerEl = ctx . $containerEl$ ;
848- const stylesParent =
849- ctx . $doc$ . documentElement === containerEl ? ctx . $doc$ . head ?? containerEl : containerEl ;
865+ const isDoc = ctx . $doc$ . documentElement === containerEl && ! ! ctx . $doc$ . head ;
850866 const style = ctx . $doc$ . createElement ( 'style' ) ;
851867 directSetAttribute ( style , 'q:style' , styleTask . styleId ) ;
852868 style . textContent = styleTask . content ;
853- stylesParent . insertBefore ( style , stylesParent . firstChild ) ;
869+ if ( isDoc ) {
870+ ctx . $doc$ . head . appendChild ( style ) ;
871+ } else {
872+ containerEl . insertBefore ( style , containerEl . firstChild ) ;
873+ }
854874 } ;
855875 ctx . $operations$ . push ( {
856876 $el$ : hostElement ,
0 commit comments