@frontity/yoast
Version:
Integrate your Frontity site with Yoast SEO plugin.
107 lines (92 loc) • 2.97 kB
text/typescript
import { useMemo } from "react";
import { decode, useConnect } from "frontity";
import { transformAllLinks } from "../utils";
import { Packages, WithYoastHead } from "../../types";
import {
isPostType,
isTerm,
isAuthor,
isPostTypeArchive,
} from "@frontity/source";
/**
* Object returned for {@link useYoastHead} hook.
*/
interface UseYoastHeadResult {
/**
* The <title> tag content.
*/
title?: string;
/**
* All the head tags in string format.
*/
head?: string;
}
/**
* Hook that returns either the content of the <title> tag or all the head tags.
*
* @param link - Link pointing to a page in Frontity.
* @returns Object of type {@link UseYoastHeadResult}.
*/
export const useYoastHead = (link: string): UseYoastHeadResult => {
const { state } = useConnect<Packages>();
// Get the data object associated to link.
const data = state.source.get(link);
// Get the entity pointed by the given link.
let entity: WithYoastHead = null;
// Entities are stored in different places depending on their type.
if (isPostType(data)) {
const { type, id } = data;
entity = state.source[type][id];
} else if (isTerm(data)) {
const { taxonomy, id } = data;
entity = state.source[taxonomy][id];
} else if (isAuthor(data)) {
const { id } = data;
entity = state.source.author[id];
} else if (isPostTypeArchive(data)) {
const { type } = data;
entity = state.source.type[type];
}
// Get the `yoast_head` field from entity.
const html = entity?.yoast_head || "";
const shouldUseTitle =
state.yoast.renderTags === "server" && state.frontity.rendering === "csr";
const shouldTransformLinks = !!state.yoast.transformLinks;
let ignore = "";
let base = "";
let newBase = "";
// Props to replace all links present in `yoast_head`.
if (state.yoast.transformLinks) {
ignore = state.yoast.transformLinks.ignore;
base =
state.yoast.transformLinks.base || state.source.url.replace(/\/?$/, "/");
newBase = state.frontity.url;
}
// Return a memoized object depending on the previously generated values.
return useMemo(() => {
/**
* Extract the title string from the `yoast_head` field and return it if
* found.
*/
if (shouldUseTitle) {
const titleMatch = /<title>(.+)<\/title>/.exec(html);
if (titleMatch) {
return { title: decode(titleMatch[1]) };
}
}
// Transform links in the `yoast_head` string and return it.
if (html && shouldTransformLinks) {
const head = transformAllLinks({ html, ignore, base, newBase });
return { head };
}
// Return just the `yoast_head` string.
if (html && !shouldTransformLinks) {
return { head: html };
}
/**
* Return an empty object if neither the title nor the head tags are going
* to be rendered for this link.
*/
return {};
}, [shouldUseTitle, shouldTransformLinks, html, ignore, base, newBase]);
};