diff --git a/src/lib/apply-website-info-head.ts b/src/lib/apply-website-info-head.ts new file mode 100644 index 0000000..82da842 --- /dev/null +++ b/src/lib/apply-website-info-head.ts @@ -0,0 +1,89 @@ +/** + * IMAGINE-MANAGED FILE. + * Do not edit this file manually or with AI. + * Changes may be overwritten by Imagine. + */ + +import { websiteInfo } from './website-info' + +type HeadMetaEntry = Record +type HeadLinkEntry = Record + +function withoutWebsiteInfoMeta(meta: HeadMetaEntry[]) { + return meta.filter((entry) => { + if (typeof entry.title === 'string') { + return false + } + + const key = + typeof entry.name === 'string' + ? entry.name + : typeof entry.property === 'string' + ? entry.property + : '' + return ![ + 'description', + 'og:title', + 'og:description', + 'og:image', + 'og:type', + 'twitter:card', + 'twitter:title', + 'twitter:description', + 'twitter:image', + ].includes(key) + }) +} + +function withoutWebsiteInfoLinks(links: HeadLinkEntry[]) { + return links.filter((entry) => { + const rel = typeof entry.rel === 'string' ? entry.rel : '' + return rel !== 'icon' && rel !== 'shortcut icon' + }) +} + +export function applyWebsiteInfoHead< + T extends { + meta?: HeadMetaEntry[] + links?: HeadLinkEntry[] + [key: string]: unknown + }, +>(input: T) { + const meta = withoutWebsiteInfoMeta(input.meta ?? []) + const links = withoutWebsiteInfoLinks(input.links ?? []) + const title = websiteInfo.title ?? 'Imagine App' + + return { + ...input, + meta: [ + ...meta, + { title }, + { property: 'og:title', content: title }, + { property: 'og:type', content: 'website' }, + { + name: 'twitter:card', + content: websiteInfo.ogImageUrl ? 'summary_large_image' : 'summary', + }, + { name: 'twitter:title', content: title }, + ...(websiteInfo.description + ? [ + { name: 'description', content: websiteInfo.description }, + { property: 'og:description', content: websiteInfo.description }, + { name: 'twitter:description', content: websiteInfo.description }, + ] + : []), + ...(websiteInfo.ogImageUrl + ? [ + { property: 'og:image', content: websiteInfo.ogImageUrl }, + { name: 'twitter:image', content: websiteInfo.ogImageUrl }, + ] + : []), + ], + links: [ + ...links, + ...(websiteInfo.faviconUrl + ? [{ rel: 'icon', href: websiteInfo.faviconUrl }] + : []), + ], + } satisfies T +} diff --git a/src/lib/website-info.ts b/src/lib/website-info.ts new file mode 100644 index 0000000..8272764 --- /dev/null +++ b/src/lib/website-info.ts @@ -0,0 +1,20 @@ +/** + * IMAGINE-MANAGED FILE. + * Do not edit this file manually or with AI. + * Update Website Info from the Imagine Studio settings. + * Changes may be overwritten by Imagine. + */ + +export type WebsiteInfo = { + title: string | null + description: string | null + faviconUrl: string | null + ogImageUrl: string | null +} + +export const websiteInfo: WebsiteInfo = { + title: 'Imagine App', + description: null, + faviconUrl: null, + ogImageUrl: null, +} diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index 0e3a570..976521b 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -16,6 +16,7 @@ import { OGImageConfig, OGMetaTags, } from '@/lib/og-config' +import { applyWebsiteInfoHead } from '@/lib/apply-website-info-head' interface MyRouterContext { queryClient: QueryClient @@ -43,6 +44,7 @@ export const Route = createRootRouteWithContext()({ baseUrl, } }, + head: ({ loaderData }) => { const baseUrl = typeof window !== 'undefined' @@ -64,7 +66,7 @@ export const Route = createRootRouteWithContext()({ const ogTags = createOGMetaTags(metadata) - return { + return applyWebsiteInfoHead({ meta: [ { charSet: 'utf-8', @@ -85,7 +87,7 @@ export const Route = createRootRouteWithContext()({ }, ], scripts: [...scripts], - } + }) }, shellComponent: RootDocument,