148 lines
3.9 KiB
TypeScript
148 lines
3.9 KiB
TypeScript
|
import type { Metadata, ResolvedMetadata } from "../types/metadata-interface";
|
||
|
import type {
|
||
|
OpenGraph,
|
||
|
OpenGraphType,
|
||
|
ResolvedOpenGraph,
|
||
|
} from "../types/opengraph-types";
|
||
|
import type { FieldResolverWithMetadataBase } from "../types/resolvers";
|
||
|
import type { ResolvedTwitterMetadata, Twitter } from "../types/twitter-types";
|
||
|
import { resolveAsArrayOrUndefined } from "../generate/utils";
|
||
|
import { isStringOrURL, resolveUrl } from "./resolve-url";
|
||
|
|
||
|
const OgTypeFields = {
|
||
|
article: ["authors", "tags"],
|
||
|
song: ["albums", "musicians"],
|
||
|
playlist: ["albums", "musicians"],
|
||
|
radio: ["creators"],
|
||
|
video: ["actors", "directors", "writers", "tags"],
|
||
|
basic: [
|
||
|
"emails",
|
||
|
"phoneNumbers",
|
||
|
"faxNumbers",
|
||
|
"alternateLocale",
|
||
|
"audio",
|
||
|
"videos",
|
||
|
],
|
||
|
} as const;
|
||
|
|
||
|
function resolveImages(
|
||
|
images: Twitter["images"],
|
||
|
metadataBase: ResolvedMetadata["metadataBase"],
|
||
|
): NonNullable<ResolvedMetadata["twitter"]>["images"];
|
||
|
function resolveImages(
|
||
|
images: OpenGraph["images"],
|
||
|
metadataBase: ResolvedMetadata["metadataBase"],
|
||
|
): NonNullable<ResolvedMetadata["openGraph"]>["images"];
|
||
|
function resolveImages(
|
||
|
images: OpenGraph["images"] | Twitter["images"],
|
||
|
metadataBase: ResolvedMetadata["metadataBase"],
|
||
|
):
|
||
|
| NonNullable<ResolvedMetadata["twitter"]>["images"]
|
||
|
| NonNullable<ResolvedMetadata["openGraph"]>["images"] {
|
||
|
const resolvedImages = resolveAsArrayOrUndefined(images);
|
||
|
resolvedImages?.forEach((item, index, array) => {
|
||
|
if (isStringOrURL(item)) {
|
||
|
array[index] = {
|
||
|
url: resolveUrl(item, metadataBase)!,
|
||
|
};
|
||
|
} else {
|
||
|
// Update image descriptor url
|
||
|
item.url = resolveUrl(item.url, metadataBase)!;
|
||
|
}
|
||
|
});
|
||
|
return resolvedImages;
|
||
|
}
|
||
|
|
||
|
function getFieldsByOgType(ogType: OpenGraphType | undefined) {
|
||
|
switch (ogType) {
|
||
|
case "article":
|
||
|
case "book":
|
||
|
return OgTypeFields.article;
|
||
|
case "music.song":
|
||
|
case "music.album":
|
||
|
return OgTypeFields.song;
|
||
|
case "music.playlist":
|
||
|
return OgTypeFields.playlist;
|
||
|
case "music.radio_station":
|
||
|
return OgTypeFields.radio;
|
||
|
case "video.movie":
|
||
|
case "video.episode":
|
||
|
return OgTypeFields.video;
|
||
|
default:
|
||
|
return OgTypeFields.basic;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export const resolveOpenGraph: FieldResolverWithMetadataBase<"openGraph"> = (
|
||
|
openGraph: Metadata["openGraph"],
|
||
|
metadataBase: ResolvedMetadata["metadataBase"],
|
||
|
) => {
|
||
|
if (!openGraph) return null;
|
||
|
|
||
|
const url = resolveUrl(openGraph.url, metadataBase);
|
||
|
const resolved = { ...openGraph } as ResolvedOpenGraph;
|
||
|
|
||
|
function assignProps(og: OpenGraph) {
|
||
|
const ogType = og && "type" in og ? og.type : undefined;
|
||
|
const keys = getFieldsByOgType(ogType);
|
||
|
for (const k of keys) {
|
||
|
const key = k as keyof ResolvedOpenGraph;
|
||
|
if (key in og && key !== "url") {
|
||
|
const value = og[key];
|
||
|
if (value) {
|
||
|
const arrayValue = resolveAsArrayOrUndefined(value); /// TODO: improve typing inferring
|
||
|
(resolved as any)[key] = arrayValue;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
resolved.images = resolveImages(og.images, metadataBase);
|
||
|
}
|
||
|
|
||
|
assignProps(openGraph);
|
||
|
|
||
|
resolved.url = url;
|
||
|
|
||
|
return resolved;
|
||
|
};
|
||
|
|
||
|
const TwitterBasicInfoKeys = [
|
||
|
"site",
|
||
|
"siteId",
|
||
|
"creator",
|
||
|
"creatorId",
|
||
|
"description",
|
||
|
] as const;
|
||
|
|
||
|
export const resolveTwitter: FieldResolverWithMetadataBase<"twitter"> = (
|
||
|
twitter,
|
||
|
metadataBase,
|
||
|
) => {
|
||
|
if (!twitter) return null;
|
||
|
const resolved = {
|
||
|
...twitter,
|
||
|
card: "card" in twitter ? twitter.card : "summary",
|
||
|
} as ResolvedTwitterMetadata;
|
||
|
for (const infoKey of TwitterBasicInfoKeys) {
|
||
|
resolved[infoKey] = twitter[infoKey] || null;
|
||
|
}
|
||
|
resolved.images = resolveImages(twitter.images, metadataBase);
|
||
|
|
||
|
if ("card" in resolved) {
|
||
|
switch (resolved.card) {
|
||
|
case "player": {
|
||
|
resolved.players = resolveAsArrayOrUndefined(resolved.players) || [];
|
||
|
break;
|
||
|
}
|
||
|
case "app": {
|
||
|
resolved.app = resolved.app || {};
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return resolved;
|
||
|
};
|