fix: de-duplicate head tags while merging (#975) (#976)

pull/977/head
Divyansh Singh 2 years ago committed by GitHub
parent 65ec9d740b
commit f7e9cfeb3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,5 @@
import { watchEffect, Ref } from 'vue' import { watchEffect, Ref } from 'vue'
import { HeadConfig, SiteData, createTitle } from '../../shared' import { HeadConfig, SiteData, createTitle, mergeHead } from '../../shared'
import { Route } from '../router' import { Route } from '../router'
export function useUpdateHead(route: Route, siteDataByRouteRef: Ref<SiteData>) { export function useUpdateHead(route: Route, siteDataByRouteRef: Ref<SiteData>) {
@ -14,50 +14,20 @@ export function useUpdateHead(route: Route, siteDataByRouteRef: Ref<SiteData>) {
return return
} }
const newEls: HTMLElement[] = [] managedHeadTags.forEach((el) => document.head.removeChild(el))
const commonLength = Math.min(managedHeadTags.length, newTags.length) managedHeadTags = []
for (let i = 0; i < commonLength; i++) { newTags.forEach((headConfig) => {
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) const el = createHeadElement(headConfig)
document.head.appendChild(el) document.head.appendChild(el)
newEls.push(el) managedHeadTags.push(el)
}) })
managedHeadTags = newEls
} }
watchEffect(() => { watchEffect(() => {
const pageData = route.data const pageData = route.data
const siteData = siteDataByRouteRef.value const siteData = siteDataByRouteRef.value
const pageDescription = pageData && pageData.description const pageDescription = pageData && pageData.description
const frontmatterHead = pageData && pageData.frontmatter.head const frontmatterHead = (pageData && pageData.frontmatter.head) || []
// update title and description // update title and description
document.title = createTitle(siteData, pageData) document.title = createTitle(siteData, pageData)
@ -66,11 +36,9 @@ export function useUpdateHead(route: Route, siteDataByRouteRef: Ref<SiteData>) {
.querySelector(`meta[name=description]`)! .querySelector(`meta[name=description]`)!
.setAttribute('content', pageDescription || siteData.description) .setAttribute('content', pageDescription || siteData.description)
updateHeadTags([ updateHeadTags(
// site head can only change during dev mergeHead(siteData.head, filterOutHeadDescription(frontmatterHead))
...(import.meta.env.DEV ? siteData.head : []), )
...(frontmatterHead ? filterOutHeadDescription(frontmatterHead) : [])
])
}) })
} }

@ -5,7 +5,13 @@ import { pathToFileURL } from 'url'
import escape from 'escape-html' import escape from 'escape-html'
import { normalizePath, transformWithEsbuild } from 'vite' import { normalizePath, transformWithEsbuild } from 'vite'
import { RollupOutput, OutputChunk, OutputAsset } from 'rollup' import { RollupOutput, OutputChunk, OutputAsset } from 'rollup'
import { HeadConfig, PageData, createTitle, notFoundPageData } from '../shared' import {
HeadConfig,
PageData,
createTitle,
notFoundPageData,
mergeHead
} from '../shared'
import { slash } from '../utils/slash' import { slash } from '../utils/slash'
import { SiteConfig, resolveSiteDataByRoute } from '../config' import { SiteConfig, resolveSiteDataByRoute } from '../config'
@ -115,10 +121,10 @@ export async function renderPage(
const title: string = createTitle(siteData, pageData) const title: string = createTitle(siteData, pageData)
const description: string = pageData.description || siteData.description const description: string = pageData.description || siteData.description
const head = [ const head = mergeHead(
...siteData.head, siteData.head,
...filterOutHeadDescription(pageData.frontmatter.head) filterOutHeadDescription(pageData.frontmatter.head)
] )
let inlinedScript = '' let inlinedScript = ''
if (config.mpa && result) { if (config.mpa && result) {

@ -285,7 +285,7 @@ function resolveSiteDataHead(userConfig?: UserConfig): HeadConfig[] {
if (userConfig?.appearance ?? true) { if (userConfig?.appearance ?? true) {
head.push([ head.push([
'script', 'script',
{}, { id: 'check-dark-light' },
` `
;(() => { ;(() => {
const saved = localStorage.getItem('${APPEARANCE_KEY}') const saved = localStorage.getItem('${APPEARANCE_KEY}')

@ -1,4 +1,9 @@
import { SiteData, PageData, LocaleConfig } from '../../types/shared' import {
SiteData,
PageData,
LocaleConfig,
HeadConfig
} from '../../types/shared'
export type { export type {
SiteData, SiteData,
@ -136,3 +141,16 @@ function cleanRoute(siteData: SiteData, route: string): string {
return route.slice(baseWithoutSuffix.length) return route.slice(baseWithoutSuffix.length)
} }
function hasTag(head: HeadConfig[], tag: HeadConfig) {
const [tagType, tagAttrs] = tag
const keyAttr = Object.entries(tagAttrs)[0] // First key
if (keyAttr == null) return false
return head.some(
([type, attrs]) => type === tagType && attrs[keyAttr[0]] === keyAttr[1]
)
}
export function mergeHead(prev: HeadConfig[], curr: HeadConfig[]) {
return [...prev.filter((tagAttrs) => !hasTag(curr, tagAttrs)), ...curr]
}

Loading…
Cancel
Save