chore: heygermany customer project
This commit is contained in:
161
apps/website/lib/head.ts
Normal file
161
apps/website/lib/head.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
type MetadataLike = {
|
||||
title?: string
|
||||
description?: string
|
||||
manifest?: string
|
||||
alternates?: {
|
||||
canonical?: string
|
||||
languages?: Record<string, string>
|
||||
}
|
||||
icons?: {
|
||||
icon?: Array<{ url: string; sizes?: string; type?: string }>
|
||||
apple?: Array<{ url: string; sizes?: string }>
|
||||
}
|
||||
openGraph?: {
|
||||
title?: string
|
||||
description?: string
|
||||
url?: string
|
||||
siteName?: string
|
||||
type?: string
|
||||
images?: Array<{ url: string; alt?: string }>
|
||||
}
|
||||
twitter?: {
|
||||
card?: string
|
||||
title?: string
|
||||
description?: string
|
||||
images?: string[]
|
||||
}
|
||||
robots?: {
|
||||
index?: boolean
|
||||
follow?: boolean
|
||||
nocache?: boolean
|
||||
googleBot?: {
|
||||
index?: boolean
|
||||
follow?: boolean
|
||||
noimageindex?: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pushMeta = (
|
||||
meta: Array<Record<string, string>>,
|
||||
key: string,
|
||||
value?: string
|
||||
) => {
|
||||
if (!value) return
|
||||
meta.push({ [key]: value })
|
||||
}
|
||||
|
||||
export function metadataToHead(metadata: MetadataLike) {
|
||||
const meta: Array<Record<string, string>> = [
|
||||
{ charSet: 'utf-8' },
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
|
||||
]
|
||||
const links: Array<Record<string, string>> = []
|
||||
|
||||
if (metadata.title) {
|
||||
meta.push({ title: metadata.title })
|
||||
meta.push({ property: 'og:title', content: metadata.title })
|
||||
meta.push({ name: 'twitter:title', content: metadata.title })
|
||||
}
|
||||
|
||||
pushMeta(meta, 'name', metadata.description ? 'description' : undefined)
|
||||
if (metadata.description) {
|
||||
meta.push({ name: 'description', content: metadata.description })
|
||||
meta.push({ property: 'og:description', content: metadata.description })
|
||||
meta.push({ name: 'twitter:description', content: metadata.description })
|
||||
}
|
||||
|
||||
if (metadata.manifest) {
|
||||
links.push({ rel: 'manifest', href: metadata.manifest })
|
||||
}
|
||||
|
||||
for (const icon of metadata.icons?.icon ?? []) {
|
||||
links.push({
|
||||
rel: 'icon',
|
||||
href: icon.url,
|
||||
...(icon.sizes ? { sizes: icon.sizes } : {}),
|
||||
...(icon.type ? { type: icon.type } : {}),
|
||||
})
|
||||
}
|
||||
|
||||
for (const icon of metadata.icons?.apple ?? []) {
|
||||
links.push({
|
||||
rel: 'apple-touch-icon',
|
||||
href: icon.url,
|
||||
...(icon.sizes ? { sizes: icon.sizes } : {}),
|
||||
})
|
||||
}
|
||||
|
||||
if (metadata.alternates?.canonical) {
|
||||
links.push({
|
||||
rel: 'canonical',
|
||||
href: metadata.alternates.canonical,
|
||||
})
|
||||
}
|
||||
|
||||
for (const [hreflang, href] of Object.entries(
|
||||
metadata.alternates?.languages ?? {}
|
||||
)) {
|
||||
links.push({
|
||||
rel: 'alternate',
|
||||
hrefLang: hreflang,
|
||||
href,
|
||||
})
|
||||
}
|
||||
|
||||
if (metadata.openGraph?.url) {
|
||||
meta.push({ property: 'og:url', content: metadata.openGraph.url })
|
||||
}
|
||||
if (metadata.openGraph?.siteName) {
|
||||
meta.push({ property: 'og:site_name', content: metadata.openGraph.siteName })
|
||||
}
|
||||
if (metadata.openGraph?.type) {
|
||||
meta.push({ property: 'og:type', content: metadata.openGraph.type })
|
||||
}
|
||||
for (const image of metadata.openGraph?.images ?? []) {
|
||||
meta.push({ property: 'og:image', content: image.url })
|
||||
if (image.alt) {
|
||||
meta.push({ property: 'og:image:alt', content: image.alt })
|
||||
}
|
||||
}
|
||||
|
||||
if (metadata.twitter?.card) {
|
||||
meta.push({ name: 'twitter:card', content: metadata.twitter.card })
|
||||
}
|
||||
for (const image of metadata.twitter?.images ?? []) {
|
||||
meta.push({ name: 'twitter:image', content: image })
|
||||
}
|
||||
|
||||
if (metadata.robots) {
|
||||
const robots = [
|
||||
metadata.robots.index === false ? 'noindex' : 'index',
|
||||
metadata.robots.follow === false ? 'nofollow' : 'follow',
|
||||
metadata.robots.nocache ? 'nocache' : undefined,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(', ')
|
||||
|
||||
if (robots) {
|
||||
meta.push({ name: 'robots', content: robots })
|
||||
}
|
||||
|
||||
if (metadata.robots.googleBot) {
|
||||
const googleBot = [
|
||||
metadata.robots.googleBot.index === false ? 'noindex' : 'index',
|
||||
metadata.robots.googleBot.follow === false ? 'nofollow' : 'follow',
|
||||
metadata.robots.googleBot.noimageindex ? 'noimageindex' : undefined,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(', ')
|
||||
|
||||
if (googleBot) {
|
||||
meta.push({ name: 'googlebot', content: googleBot })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
meta,
|
||||
links,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user