UNPKG

@withstudiocms/internal_helpers

Version:

Internal helper utilities for StudioCMS

81 lines (80 loc) 2.3 kB
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 };