SEO: favicon metadata, Organization JSON-LD, favicon validation; R2 media (preconnect, generateFileURL per-collection), filename normalization
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import type { FieldHook } from 'payload'
|
||||
|
||||
/**
|
||||
* Field validation for the favicon upload: Google rejects favicons under 48×48,
|
||||
* so warn the editor at save time if the uploaded icon is too small or not
|
||||
* square. This ENFORCES the requirement instead of silently shipping a favicon
|
||||
* Google won't display.
|
||||
*
|
||||
* Attach to the favicon field's validate (or as a beforeValidate hook on the
|
||||
* Media relationship). Non-blocking by default — returns a warning string that
|
||||
* Payload surfaces; make it throw if you want a hard block.
|
||||
*
|
||||
* Note: dimensions come from the related Media doc (Payload stores width/height
|
||||
* for image uploads), so this checks the resolved upload, not the raw file.
|
||||
*/
|
||||
export const validateFaviconField: FieldHook = async ({ req, value }) => {
|
||||
if (!value) {return value} // no favicon set → nothing to validate (optional field)
|
||||
|
||||
try {
|
||||
const media = await req.payload.findByID({
|
||||
id: typeof value === 'object' ? (value as { id: string }).id : value,
|
||||
collection: 'media',
|
||||
depth: 0,
|
||||
})
|
||||
|
||||
const width = (media as { width?: number }).width
|
||||
const height = (media as { height?: number }).height
|
||||
const mimeType = (media as { mimeType?: string }).mimeType
|
||||
|
||||
// SVG scales infinitely — skip size checks.
|
||||
if (mimeType === 'image/svg+xml') {return value}
|
||||
|
||||
if (typeof width === 'number' && typeof height === 'number') {
|
||||
if (width < 48 || height < 48) {
|
||||
req.payload.logger.warn(
|
||||
`[ipal] Favicon is ${width}×${height}px. Google requires ≥48×48 to ` +
|
||||
`display it in search results. Upload a larger square icon (96 or 192px).`,
|
||||
)
|
||||
}
|
||||
if (width !== height) {
|
||||
req.payload.logger.warn(
|
||||
`[ipal] Favicon is not square (${width}×${height}). Use a square icon ` +
|
||||
`so it isn't cropped in the browser tab or search results.`,
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Media lookup failed — don't block the save over a validation warning.
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
Reference in New Issue
Block a user