Files
RenewCMS/web/src/lib/seoApiPreview.ts
T

106 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { t, locale } from '@/i18n'
import { formatAbsoluteDateFromIso } from '@/lib/relativeDate'
import type { ResolvedSeo } from '@/lib/seoTypes'
const MONTH_NAMES = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
]
const EMPTY_VALUE = '–'
export interface ApiPreviewRow {
key: string
value: string
chip: string | null
}
function pad(value: number) {
return value.toString().padStart(2, '0')
}
export function formatApiDate(iso: string | null) {
if (!iso) {
return EMPTY_VALUE
}
const date = new Date(iso)
const clock = `${pad(date.getHours())}:${pad(date.getMinutes())}`
return locale.value === 'fr'
? `${formatAbsoluteDateFromIso(iso)}, ${clock}`
: `${date.getDate()} ${MONTH_NAMES[date.getMonth()]} ${date.getFullYear()}, ${clock}`
}
const FALLBACK_CHIPS: Record<string, Record<string, () => string>> = {
title: { title: () => t('article title + template') },
description: {
body: () => t('first sentence of the body'),
excerpt: () => t('excerpt'),
site: () => t('site default description'),
},
image: {
cover: () => t('cover image'),
site: () => t('site social image'),
none: () => t('No image'),
},
canonical: { site: () => t('site address + slug') },
}
function fallbackChip(field: string, source: string) {
return FALLBACK_CHIPS[field]?.[source]?.() ?? null
}
function authorLine(author: ResolvedSeo['authors'][number]) {
const links = author.same_as.length
return [
author.name,
author.url ? author.url.replace(/^https?:\/\//, '') : '',
links === 0 ? '' : links === 1 ? t('1 social link') : t('{0} social links', [links]),
]
.filter(Boolean)
.join(' · ')
}
function jsonLdSummary(jsonLd: Record<string, unknown>) {
const fields = Object.keys(jsonLd).filter((key) => !key.startsWith('@'))
return [String(jsonLd['@type'] ?? ''), fields.join(', ')].filter(Boolean).join(' · ')
}
export function apiPreviewRows(resolved: ResolvedSeo): ApiPreviewRow[] {
const { fallbacks } = resolved
return [
{ key: 'title', value: resolved.title, chip: fallbackChip('title', fallbacks.title) },
{
key: 'description',
value: resolved.description,
chip: fallbackChip('description', fallbacks.description),
},
{
key: 'canonical_url',
value: resolved.canonical_url,
chip: fallbackChip('canonical', fallbacks.canonical),
},
{ key: 'robots', value: resolved.robots, chip: null },
{ key: 'og.type', value: resolved.og.type, chip: null },
{ key: 'og.image', value: resolved.og.image, chip: fallbackChip('image', fallbacks.image) },
{ key: 'og.image_alt', value: resolved.og.image_alt, chip: null },
{ key: 'twitter.card', value: resolved.twitter.card, chip: null },
{
key: 'authors',
value: resolved.authors.map(authorLine).join('; ') || EMPTY_VALUE,
chip: null,
},
{ key: 'published_at', value: formatApiDate(resolved.published_at), chip: null },
{ key: 'modified_at', value: formatApiDate(resolved.modified_at), chip: null },
{ key: 'json_ld', value: jsonLdSummary(resolved.json_ld), chip: null },
]
}