sitegen/framework/meta/merge.ts
chloe caruso af60d1172f i accidentally deleted the repo, but recovered it. i'll start committing
it was weird. i pressed delete on a subfolder, i think one of the
pages.off folders that i was using. and then, suddenly, nvim on windows
7 decided to delete every file in the directory. they weren't shred off
the space time continuum, but just marked deleted. i had to pay $80 to
get access to a software that could see them. bleh!

just seeing all my work, a little over a week, was pretty heart
shattering. but i remembered that long ago, a close friend said i could
call them whenever i was feeling sad. i finally took them up on that
offer. the first time i've ever called someone for emotional support.
but it's ok. i got it back. and the site framework is better than ever.

i'm gonna commit and push more often. the repo is private anyways.
2025-06-06 23:38:02 -07:00

154 lines
4.2 KiB
TypeScript

import { createDefaultMetadata } from "./nextjs/default-metadata";
import { resolveAsArrayOrUndefined } from "./nextjs/generate/utils";
import {
resolveAlternates,
resolveAppleWebApp,
resolveAppLinks,
resolveRobots,
resolveThemeColor,
resolveVerification,
resolveViewport,
} from "./nextjs/resolvers/resolve-basics";
import { resolveIcons } from "./nextjs/resolvers/resolve-icons";
import {
resolveOpenGraph,
resolveTwitter,
} from "./nextjs/resolvers/resolve-opengraph";
import { resolveTitle } from "./nextjs/resolvers/resolve-title";
import type {
Metadata,
ResolvedMetadata,
} from "./nextjs/types/metadata-interface";
type MetadataAccumulationOptions = {
pathname: string;
};
// Merge the source metadata into the resolved target metadata.
function merge(
target: ResolvedMetadata,
source: Metadata | null,
titleTemplates: {
title?: string | null;
twitter?: string | null;
openGraph?: string | null;
} = {},
) {
const metadataBase = source?.metadataBase || target.metadataBase;
for (const key_ in source) {
const key = key_ as keyof Metadata;
switch (key) {
case "title": {
target.title = resolveTitle(source.title, titleTemplates.title);
break;
}
case "alternates": {
target.alternates = resolveAlternates(source.alternates, metadataBase, {
pathname: (source as any)._pathname ?? "/",
});
break;
}
case "openGraph": {
target.openGraph = resolveOpenGraph(source.openGraph, metadataBase);
if (target.openGraph) {
target.openGraph.title = resolveTitle(
target.openGraph.title,
titleTemplates.openGraph,
);
}
break;
}
case "twitter": {
target.twitter = resolveTwitter(source.twitter, metadataBase);
if (target.twitter) {
target.twitter.title = resolveTitle(
target.twitter.title,
titleTemplates.twitter,
);
}
break;
}
case "verification":
target.verification = resolveVerification(source.verification);
break;
case "viewport": {
target.viewport = resolveViewport(source.viewport);
break;
}
case "icons": {
target.icons = resolveIcons(source.icons);
break;
}
case "appleWebApp":
target.appleWebApp = resolveAppleWebApp(source.appleWebApp);
break;
case "appLinks":
target.appLinks = resolveAppLinks(source.appLinks);
break;
case "robots": {
target.robots = resolveRobots(source.robots);
break;
}
case "themeColor": {
target.themeColor = resolveThemeColor(source.themeColor);
break;
}
case "archives":
case "assets":
case "bookmarks":
case "keywords":
case "authors": {
// FIXME: type inferring
// @ts-ignore
target[key] = resolveAsArrayOrUndefined(source[key]) || null;
break;
}
// directly assign fields that fallback to null
case "applicationName":
case "description":
case "generator":
case "creator":
case "publisher":
case "category":
case "classification":
case "referrer":
case "colorScheme":
case "itunes":
case "formatDetection":
case "manifest":
// @ts-ignore TODO: support inferring
target[key] = source[key] || null;
break;
case "other":
target.other = Object.assign({}, target.other, source.other);
break;
case "metadataBase":
target.metadataBase = metadataBase;
break;
default:
break;
}
}
return target;
}
export interface MetadataWithPathname extends Metadata {
/** Set by framework author to the pathname of the page defining this metadata. */
_pathname?: string;
}
export function resolveMetadata(
...metadata: [MetadataWithPathname, ...MetadataWithPathname[]]
) {
const base = createDefaultMetadata();
for (const item of metadata) {
merge(base, item, {
title: base.title?.template,
twitter: base.twitter?.title?.template,
openGraph: base.openGraph?.title?.template,
});
}
return base;
}