@knightly/vitepress
Version:
Vite & Vue powered static site generator
86 lines (85 loc) • 3.23 kB
JavaScript
import { watchEffect } from 'vue';
export function useUpdateHead(route, siteDataByRouteRef) {
let managedHeadTags = [];
let isFirstUpdate = true;
const updateHeadTags = (newTags) => {
if (import.meta.env.PROD && isFirstUpdate) {
// in production, the initial meta tags are already pre-rendered so we
// skip the first update.
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;
// update title and description
document.title = (pageTitle ? pageTitle + ` | ` : ``) + siteData.title;
document
.querySelector(`meta[name=description]`)
.setAttribute('content', pageDescription || siteData.description);
updateHeadTags([
// site head can only change during dev
...(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));
}