development
tsx 71 lines 1.59 KB
Raw
1 import type { Metadata } from "next";
2 import siteMetadata from "@/content/siteMetadata";
3
4 interface PageSEOProps extends Omit<
5 Metadata,
6 "title" | "description" | "alternates" | "openGraph" | "twitter"
7 > {
8 title: string;
9 description?: string;
10 image?: string;
11 path?: string;
12 locale?: string;
13 type?: "website" | "article";
14 alternates?: Metadata["alternates"];
15 openGraph?: NonNullable<Metadata["openGraph"]>;
16 twitter?: NonNullable<Metadata["twitter"]>;
17 }
18
19 export function absoluteUrl(path = "/") {
20 return new URL(path, siteMetadata.siteUrl).toString();
21 }
22
23 function formatTitle(title: string) {
24 return title === siteMetadata.title
25 ? title
26 : `${title} | ${siteMetadata.title}`;
27 }
28
29 export function genPageMetadata({
30 title,
31 description,
32 image,
33 path = "/",
34 locale = "en_US",
35 type = "website",
36 alternates,
37 openGraph,
38 twitter,
39 ...rest
40 }: PageSEOProps): Metadata {
41 const pageDescription = description || siteMetadata.description;
42 const pageUrl = absoluteUrl(path);
43 const socialImage = absoluteUrl(image || siteMetadata.socialBanner);
44
45 return {
46 title,
47 description: pageDescription,
48 alternates: {
49 canonical: pageUrl,
50 ...alternates,
51 },
52 openGraph: {
53 title: formatTitle(title),
54 description: pageDescription,
55 url: pageUrl,
56 siteName: siteMetadata.title,
57 images: [socialImage],
58 locale,
59 type,
60 ...openGraph,
61 },
62 twitter: {
63 title: formatTitle(title),
64 description: pageDescription,
65 card: "summary_large_image",
66 images: [socialImage],
67 ...twitter,
68 },
69 ...rest,
70 };
71 }