@withstudiocms/internal_helpers
Version:
Internal helper utilities for StudioCMS
81 lines (80 loc) • 2.3 kB
JavaScript
import { z } from "astro/zod";
const HeadConfigSchema = () => z.array(
z.object({
/** Name of the HTML tag to add to `<head>`, e.g. `'meta'`, `'link'`, or `'script'`. */
tag: z.enum(["title", "base", "link", "style", "meta", "script", "noscript", "template"]),
/** Attributes to set on the tag, e.g. `{ rel: 'stylesheet', href: '/custom.css' }`. */
attrs: z.record(z.union([z.string(), z.boolean(), z.undefined()])).default({}),
/** Content to place inside the tag (optional). */
content: z.string().default("")
})
).default([]);
const HeadSchema = HeadConfigSchema();
function createHead(defaults, ...heads) {
let head = HeadSchema.parse(defaults);
for (const next of heads) {
head = mergeHead(head, next);
}
return sortHead(head);
}
function hasTag(head, entry) {
switch (entry.tag) {
case "title":
return head.some(({ tag }) => tag === "title");
case "meta":
return hasOneOf(head, entry, ["name", "property", "http-equiv"]);
default:
return false;
}
}
function hasOneOf(head, entry, keys) {
const attr = getAttr(keys, entry);
if (!attr) return false;
const [key, val] = attr;
return head.some(({ tag, attrs }) => tag === entry.tag && attrs[key] === val);
}
function getAttr(keys, entry) {
let attr;
for (const key of keys) {
const val = entry.attrs[key];
if (val) {
attr = [key, val];
break;
}
}
return attr;
}
function mergeHead(oldHead, newHead) {
return [...oldHead.filter((tag) => !hasTag(newHead, tag)), ...newHead];
}
function sortHead(head) {
return head.sort((a, b) => {
const aImportance = getImportance(a);
const bImportance = getImportance(b);
return aImportance > bImportance ? -1 : bImportance > aImportance ? 1 : 0;
});
}
function getImportance(entry) {
if (entry.tag === "meta" && ("charset" in entry.attrs || "http-equiv" in entry.attrs || entry.attrs.name === "viewport")) {
return 100;
}
if (entry.tag === "title") return 90;
if (entry.tag !== "meta") {
if (entry.tag === "link" && "rel" in entry.attrs && entry.attrs.rel === "shortcut icon") {
return 70;
}
return 80;
}
return 0;
}
export {
HeadConfigSchema,
HeadSchema,
createHead,
getAttr,
getImportance,
hasOneOf,
hasTag,
mergeHead,
sortHead
};