mirror of
https://github.com/imezx/Warp.git
synced 2025-04-25 15:40:02 +00:00
81 lines
3.1 KiB
JavaScript
81 lines
3.1 KiB
JavaScript
import { watchEffect } from 'vue';
|
|
import { createTitle, mergeHead } from '../../shared';
|
|
export function useUpdateHead(route, siteDataByRouteRef) {
|
|
let isFirstUpdate = true;
|
|
let managedHeadElements = [];
|
|
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;
|
|
newTags.forEach((tag) => {
|
|
const headEl = createHeadElement(tag);
|
|
for (const el of document.head.children) {
|
|
if (el.isEqualNode(headEl)) {
|
|
managedHeadElements.push(el);
|
|
return;
|
|
}
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
const newElements = newTags.map(createHeadElement);
|
|
managedHeadElements.forEach((oldEl, oldIndex) => {
|
|
const matchedIndex = newElements.findIndex((newEl) => newEl?.isEqualNode(oldEl ?? null));
|
|
if (matchedIndex !== -1) {
|
|
delete newElements[matchedIndex];
|
|
}
|
|
else {
|
|
oldEl?.remove();
|
|
delete managedHeadElements[oldIndex];
|
|
}
|
|
});
|
|
newElements.forEach((el) => el && document.head.appendChild(el));
|
|
managedHeadElements = [...managedHeadElements, ...newElements].filter(Boolean);
|
|
};
|
|
watchEffect(() => {
|
|
const pageData = route.data;
|
|
const siteData = siteDataByRouteRef.value;
|
|
const pageDescription = pageData && pageData.description;
|
|
const frontmatterHead = (pageData && pageData.frontmatter.head) || [];
|
|
// update title and description
|
|
const title = createTitle(siteData, pageData);
|
|
if (title !== document.title) {
|
|
document.title = title;
|
|
}
|
|
const description = pageDescription || siteData.description;
|
|
let metaDescriptionElement = document.querySelector(`meta[name=description]`);
|
|
if (metaDescriptionElement) {
|
|
if (metaDescriptionElement.getAttribute('content') !== description) {
|
|
metaDescriptionElement.setAttribute('content', description);
|
|
}
|
|
}
|
|
else {
|
|
createHeadElement(['meta', { name: 'description', content: description }]);
|
|
}
|
|
updateHeadTags(mergeHead(siteData.head, 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;
|
|
}
|
|
if (tag === 'script' && !attrs.async) {
|
|
// async is true by default for dynamically created scripts
|
|
;
|
|
el.async = false;
|
|
}
|
|
return el;
|
|
}
|
|
function isMetaDescription(headConfig) {
|
|
return (headConfig[0] === 'meta' &&
|
|
headConfig[1] &&
|
|
headConfig[1].name === 'description');
|
|
}
|
|
function filterOutHeadDescription(head) {
|
|
return head.filter((h) => !isMetaDescription(h));
|
|
}
|