import { watchEffect , Ref } from 'vue' ; import { HeadConfig , SiteData } from '../../shared' ; import { Route } from '../router' ; export function useUpdateHead ( route , siteDataByRouteRef ) { let managedHeadTags = [ ] ; let isFirstUpdate = true ; const updateHeadTags = ( newTags ) => { if ( import . meta . env . PROD && isFirstUpdate ) { isFirstUpdate = false ; return ; } const newEls = [ ] ; const commonLength = Math . min ( managedHeadTags . length , newTags . length ) ; for ( let i = 0 ; i < commonLength ; i ++ ) { let el = managedHeadTags [ i ] ; const [ tag , attrs , innerHTML = '' ] = newTags [ i ] ; if ( el . tagName . toLocaleLowerCase ( ) === tag ) { for ( const key in attrs ) { if ( el . getAttribute ( key ) !== attrs [ key ] ) { el . setAttribute ( key , attrs [ key ] ) ; } } for ( let i = 0 ; i < el . attributes . length ; i ++ ) { const name = el . attributes [ i ] . name ; if ( ! ( name in attrs ) ) { el . removeAttribute ( name ) ; } } if ( el . innerHTML !== innerHTML ) { el . innerHTML = innerHTML ; } } else { document . head . removeChild ( el ) ; el = createHeadElement ( newTags [ i ] ) ; document . head . append ( el ) ; } newEls . push ( el ) ; } managedHeadTags . slice ( commonLength ) . forEach ( ( el ) => document . head . removeChild ( el ) ) ; newTags . slice ( commonLength ) . forEach ( ( headConfig ) => { const el = createHeadElement ( headConfig ) ; document . head . appendChild ( el ) ; newEls . push ( el ) ; } ) ; managedHeadTags = newEls ; } ; watchEffect ( ( ) => { const pageData = route . data ; const siteData = siteDataByRouteRef . value ; const pageTitle = pageData && pageData . title ; const pageDescription = pageData && pageData . description ; const frontmatterHead = pageData && pageData . frontmatter . head ; document . title = ( pageTitle ? pageTitle + ` | ` : `` ) + siteData . title ; document . querySelector ( `meta[name=description]` ) ! . setAttribute ( 'content' , pageDescription || siteData . description ) ; updateHeadTags ( [ ... ( import . meta . env . DEV ? siteData . head : [ ] ) , ... ( frontmatterHead ? filterOutHeadDescription ( frontmatterHead ) : [ ] ) ] ) ; } ) ; } function createHeadElement ( [ tag , attrs , innerHTML ] ) { const el = document . createElement ( tag ) ; for ( const key in attrs ) { el . setAttribute ( key , attrs [ key ] ) ; } if ( innerHTML ) { el . innerHTML = innerHTML ; } return el ; } function isMetaDescription ( headConfig ) { return ( headConfig [ 0 ] === 'meta' && headConfig [ 1 ] && headConfig [ 1 ] . name === 'description' ) ; } function filterOutHeadDescription ( head ) { return head . filter ( ( h ) => ! isMetaDescription ( h ) ) ; }