forked from crowetic/commerce
* replace next-seo with custom solution * Updated check Co-authored-by: LFades <luis@vercel.com>
158 lines
3.9 KiB
TypeScript
158 lines
3.9 KiB
TypeScript
import Head from 'next/head'
|
|
import { FC, Fragment, ReactNode } from 'react'
|
|
import config from '@config/seo_meta.json'
|
|
|
|
const storeUrl =
|
|
process.env.NEXT_PUBLIC_STORE_URL || process.env.NEXT_PUBLIC_VERCEL_URL
|
|
const storeBaseUrl = storeUrl ? `https://${storeUrl}` : null
|
|
|
|
interface OgImage {
|
|
url?: string
|
|
width?: string
|
|
height?: string
|
|
alt?: string
|
|
}
|
|
|
|
interface Props {
|
|
title?: string
|
|
description?: string
|
|
robots?: string
|
|
openGraph?: {
|
|
title?: string
|
|
type?: string
|
|
locale?: string
|
|
description?: string
|
|
site_name?: string
|
|
url?: string
|
|
images?: OgImage[]
|
|
}
|
|
children?: ReactNode
|
|
}
|
|
|
|
const ogImage = ({ url, width, height, alt }: OgImage, index: number) => {
|
|
// generate full URL for OG image url with store base URL
|
|
const imgUrl = storeBaseUrl ? new URL(url!, storeBaseUrl).toString() : url
|
|
return (
|
|
<Fragment key={`og:image:${index}`}>
|
|
<meta
|
|
key={`og:image:url:${index}`}
|
|
property="og:image"
|
|
content={imgUrl}
|
|
/>
|
|
<meta
|
|
key={`og:image:width:${index}`}
|
|
property="og:image:width"
|
|
content={width}
|
|
/>
|
|
<meta
|
|
key={`og:image:height:${index}`}
|
|
property="og:image:height"
|
|
content={height}
|
|
/>
|
|
<meta
|
|
key={`og:image:alt:${index}`}
|
|
property="og:image:alt"
|
|
content={alt}
|
|
/>
|
|
</Fragment>
|
|
)
|
|
}
|
|
|
|
const SEO: FC<Props> = ({
|
|
title,
|
|
description,
|
|
openGraph,
|
|
robots,
|
|
children,
|
|
}) => {
|
|
/**
|
|
* @see https://nextjs.org/docs/api-reference/next/head
|
|
*
|
|
* meta or any other elements need to be contained as direct children of the Head element,
|
|
* or wrapped into maximum one level of <React.Fragment> or arrays
|
|
* otherwise the tags won't be correctly picked up on client-side navigations.
|
|
*
|
|
* The `key` property makes the tag is only rendered once,
|
|
*/
|
|
return (
|
|
<Head>
|
|
<title key="title">
|
|
{title ? `${config.titleTemplate.replace(/%s/g, title)}` : config.title}
|
|
</title>
|
|
<meta
|
|
key="description"
|
|
name="description"
|
|
content={description || config.description}
|
|
/>
|
|
<meta
|
|
key="og:type"
|
|
property="og:type"
|
|
content={openGraph?.type ?? config.openGraph.type}
|
|
/>
|
|
<meta
|
|
key="og:title"
|
|
property="og:title"
|
|
content={
|
|
openGraph?.title ?? config.openGraph.title ?? title ?? config.title
|
|
}
|
|
/>
|
|
<meta
|
|
key="og:description"
|
|
property="og:description"
|
|
content={
|
|
openGraph?.description ??
|
|
config.openGraph.description ??
|
|
description ??
|
|
config.description
|
|
}
|
|
/>
|
|
<meta
|
|
key="og:site_name"
|
|
property="og:site_name"
|
|
content={openGraph?.site_name ?? config.openGraph.site_name}
|
|
/>
|
|
<meta
|
|
key="og:url"
|
|
property="og:url"
|
|
content={openGraph?.url ?? config.openGraph.url}
|
|
></meta>
|
|
{openGraph?.locale && (
|
|
<meta key="og:locale" property="og:locale" content={openGraph.locale} />
|
|
)}
|
|
{openGraph?.images?.length
|
|
? openGraph.images.map((img, index) => ogImage(img, index))
|
|
: ogImage(config.openGraph.images[0], 0)}
|
|
{config.twitter.cardType && (
|
|
<meta
|
|
key="twitter:card"
|
|
name="twitter:card"
|
|
content={config.twitter.cardType}
|
|
/>
|
|
)}
|
|
{config.twitter.site && (
|
|
<meta
|
|
key="twitter:site"
|
|
name="twitter:site"
|
|
content={config.twitter.site}
|
|
/>
|
|
)}
|
|
{config.twitter.handle && (
|
|
<meta
|
|
key="twitter:creator"
|
|
name="twitter:creator"
|
|
content={config.twitter.handle}
|
|
/>
|
|
)}
|
|
<meta key="robots" name="robots" content={robots ?? 'index,follow'} />
|
|
<meta
|
|
key="googlebot"
|
|
name="googlebot"
|
|
content={robots ?? 'index,follow'}
|
|
></meta>
|
|
{children}
|
|
</Head>
|
|
)
|
|
}
|
|
|
|
export default SEO
|