59 lines
2.0 KiB
TypeScript
59 lines
2.0 KiB
TypeScript
import type { CollectionBeforeChangeHook } from 'payload'
|
|
|
|
/**
|
|
* Maps document fields to SEO meta fields for auto-fill.
|
|
* Defaults match the Payload website template (title → meta.title).
|
|
*/
|
|
export type AutoFillMapping = {
|
|
/** Document field used to fill meta.description when empty. */
|
|
description?: string
|
|
/** Document field used to fill meta.title when empty. Default: 'title'. */
|
|
title?: string
|
|
}
|
|
|
|
/**
|
|
* Reads a possibly-localized field value into a plain string.
|
|
* With localized fields at this hook stage the value is the active-locale
|
|
* string, so we just coerce defensively.
|
|
*/
|
|
function readString(value: unknown): string | undefined {
|
|
if (typeof value === 'string' && value.trim()) {return value.trim()}
|
|
return undefined
|
|
}
|
|
|
|
/**
|
|
* Builds a beforeChange hook that fills empty SEO meta fields from document
|
|
* content. Only fills when the meta field is blank — never overwrites what an
|
|
* editor typed. This runs server-side on every create/update, so editors get
|
|
* sensible meta without clicking "auto-generate".
|
|
*
|
|
* The plugin doesn't know the client's collection shape, so the field mapping
|
|
* is configurable; defaults follow the website template.
|
|
*/
|
|
export function buildAutoFillMetaHook(mapping: AutoFillMapping = {}): CollectionBeforeChangeHook {
|
|
const titleField = mapping.title ?? 'title'
|
|
const descriptionField = mapping.description
|
|
|
|
return ({ data }) => {
|
|
if (!data) {return data}
|
|
|
|
// plugin-seo stores meta as a group under `meta`
|
|
const meta = (data.meta as Record<string, unknown> | undefined) ?? {}
|
|
|
|
// Fill meta.title from the document title when empty
|
|
if (!readString(meta.title)) {
|
|
const sourceTitle = readString(data[titleField])
|
|
if (sourceTitle) {meta.title = sourceTitle}
|
|
}
|
|
|
|
// Fill meta.description from a configured field when empty
|
|
if (descriptionField && !readString(meta.description)) {
|
|
const sourceDescription = readString(data[descriptionField])
|
|
if (sourceDescription) {meta.description = sourceDescription}
|
|
}
|
|
|
|
data.meta = meta
|
|
return data
|
|
}
|
|
}
|