From bcb10b52b344e785b57affca6695a46e1aa7be21 Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Thu, 26 Jun 2025 14:05:10 -0400 Subject: [PATCH] fix: restore missing properties to live preview client config (#12904) Needed for #12860. The client config unnecessarily omits the `livePreview.collections` and `livePreview.globals` properties. This is because the root live preview config extends the type with these two additional properties without sharing it elsewhere. To led to the client sanitization function overlooking these additional properties, as there was no type indication that they exist. The `collections` and `globals` properties are now appended to the client config as expected, and the root live preview is standardized behind the `RootLivePreviewConfig` type to ensure no properties are lost. --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1210628466702823 --- .../payload/src/collections/config/client.ts | 36 ++++++++++++++++-- packages/payload/src/config/client.ts | 38 +++++++++++++++++-- packages/payload/src/config/types.ts | 12 +++--- 3 files changed, 75 insertions(+), 11 deletions(-) diff --git a/packages/payload/src/collections/config/client.ts b/packages/payload/src/collections/config/client.ts index 80fb81fa5..ffd29798a 100644 --- a/packages/payload/src/collections/config/client.ts +++ b/packages/payload/src/collections/config/client.ts @@ -122,7 +122,9 @@ export const createClientCollectionConfig = ({ if (!collection.admin) { break } + clientCollection.admin = {} as ClientCollectionConfig['admin'] + for (const adminKey in collection.admin) { if (serverOnlyCollectionAdminProperties.includes(adminKey as any)) { continue @@ -139,56 +141,72 @@ export const createClientCollectionConfig = ({ } } else if (typeof collection.admin.description === 'function') { const description = collection.admin.description({ t: i18n.t as TFunction }) + if (description) { clientCollection.admin.description = description } } break + case 'livePreview': - clientCollection.admin.livePreview = - {} as ClientCollectionConfig['admin']['livePreview'] + clientCollection.admin.livePreview = {} + if (collection.admin.livePreview?.breakpoints) { - clientCollection.admin.livePreview!.breakpoints = + clientCollection.admin.livePreview.breakpoints = collection.admin.livePreview.breakpoints } + break + case 'preview': if (collection.admin.preview) { clientCollection.admin.preview = true } + break + default: ;(clientCollection as any).admin[adminKey] = collection.admin[adminKey as keyof SanitizedCollectionConfig['admin']] } } + break + case 'auth': if (!collection.auth) { break } + clientCollection.auth = {} as { verify?: true } & SanitizedCollectionConfig['auth'] + if (collection.auth.cookies) { clientCollection.auth.cookies = collection.auth.cookies } + if (collection.auth.depth !== undefined) { // Check for undefined as it can be a number (0) clientCollection.auth.depth = collection.auth.depth } + if (collection.auth.disableLocalStrategy) { clientCollection.auth.disableLocalStrategy = collection.auth.disableLocalStrategy } + if (collection.auth.lockTime !== undefined) { // Check for undefined as it can be a number (0) clientCollection.auth.lockTime = collection.auth.lockTime } + if (collection.auth.loginWithUsername) { clientCollection.auth.loginWithUsername = collection.auth.loginWithUsername } + if (collection.auth.maxLoginAttempts !== undefined) { // Check for undefined as it can be a number (0) clientCollection.auth.maxLoginAttempts = collection.auth.maxLoginAttempts } + if (collection.auth.removeTokenFromResponses) { clientCollection.auth.removeTokenFromResponses = collection.auth.removeTokenFromResponses } @@ -196,13 +214,17 @@ export const createClientCollectionConfig = ({ if (collection.auth.useAPIKey) { clientCollection.auth.useAPIKey = collection.auth.useAPIKey } + if (collection.auth.tokenExpiration) { clientCollection.auth.tokenExpiration = collection.auth.tokenExpiration } + if (collection.auth.verify) { clientCollection.auth.verify = true } + break + case 'fields': clientCollection.fields = createClientFields({ defaultIDType, @@ -210,7 +232,9 @@ export const createClientCollectionConfig = ({ i18n, importMap, }) + break + case 'labels': clientCollection.labels = { plural: @@ -222,16 +246,21 @@ export const createClientCollectionConfig = ({ ? collection.labels.singular({ i18n, t: i18n.t as TFunction }) : collection.labels.singular, } + break + case 'upload': if (!collection.upload) { break } + clientCollection.upload = {} as SanitizedUploadConfig + for (const uploadKey in collection.upload) { if (serverOnlyUploadProperties.includes(uploadKey as any)) { continue } + if (uploadKey === 'imageSizes') { clientCollection.upload.imageSizes = collection.upload.imageSizes?.map((size) => { const sanitizedSize = { ...size } @@ -245,6 +274,7 @@ export const createClientCollectionConfig = ({ collection.upload[uploadKey as keyof SanitizedUploadConfig] } } + break default: diff --git a/packages/payload/src/config/client.ts b/packages/payload/src/config/client.ts index 9d0d696be..f6fa1266d 100644 --- a/packages/payload/src/config/client.ts +++ b/packages/payload/src/config/client.ts @@ -5,7 +5,7 @@ import type { ImportMap } from '../bin/generateImportMap/index.js' import type { ClientBlock } from '../fields/config/types.js' import type { BlockSlug } from '../index.js' import type { - LivePreviewConfig, + RootLivePreviewConfig, SanitizedConfig, ServerOnlyLivePreviewProperties, } from './types.js' @@ -44,7 +44,7 @@ export type ServerOnlyRootAdminProperties = keyof Pick + livePreview?: Omit } & Omit blocks: ClientBlock[] collections: ClientCollectionConfig[] @@ -54,7 +54,7 @@ export type UnsanitizedClientConfig = { export type ClientConfig = { admin: { - livePreview?: Omit + livePreview?: Omit } & Omit blocks: ClientBlock[] blocksMap: Record @@ -103,6 +103,7 @@ export const createClientConfig = ({ if (serverOnlyConfigProperties.includes(key as any)) { continue } + switch (key) { case 'admin': clientConfig.admin = { @@ -117,14 +118,25 @@ export const createClientConfig = ({ timezones: config.admin.timezones, user: config.admin.user, } + if (config.admin.livePreview) { clientConfig.admin.livePreview = {} if (config.admin.livePreview.breakpoints) { clientConfig.admin.livePreview.breakpoints = config.admin.livePreview.breakpoints } + + if (config.admin.livePreview.collections) { + clientConfig.admin.livePreview.collections = config.admin.livePreview.collections + } + + if (config.admin.livePreview.globals) { + clientConfig.admin.livePreview.globals = config.admin.livePreview.globals + } } + break + case 'blocks': { ;(clientConfig.blocks as ClientBlock[]) = createClientBlocks({ blocks: config.blocks!, @@ -135,6 +147,7 @@ export const createClientConfig = ({ break } + case 'collections': ;(clientConfig.collections as ClientCollectionConfig[]) = createClientCollectionConfigs({ collections: config.collections, @@ -142,7 +155,9 @@ export const createClientConfig = ({ i18n, importMap, }) + break + case 'folders': if (config.folders) { clientConfig.folders = { @@ -152,6 +167,7 @@ export const createClientConfig = ({ fieldName: config.folders.fieldName, } } + break case 'globals': @@ -161,49 +177,65 @@ export const createClientConfig = ({ i18n, importMap, }) + break + case 'localization': if (typeof config.localization === 'object' && config.localization) { clientConfig.localization = {} + if (config.localization.defaultLocale) { clientConfig.localization.defaultLocale = config.localization.defaultLocale } + if (config.localization.defaultLocalePublishOption) { clientConfig.localization.defaultLocalePublishOption = config.localization.defaultLocalePublishOption } + if (config.localization.fallback) { clientConfig.localization.fallback = config.localization.fallback } + if (config.localization.localeCodes) { clientConfig.localization.localeCodes = config.localization.localeCodes } + if (config.localization.locales) { clientConfig.localization.locales = [] + for (const locale of config.localization.locales) { if (locale) { const clientLocale: Partial<(typeof config.localization.locales)[0]> = {} + if (locale.code) { clientLocale.code = locale.code } + if (locale.fallbackLocale) { clientLocale.fallbackLocale = locale.fallbackLocale } + if (locale.label) { clientLocale.label = locale.label } + if (locale.rtl) { clientLocale.rtl = locale.rtl } + clientConfig.localization.locales.push(clientLocale) } } } } + break + default: ;(clientConfig as any)[key] = config[key as keyof SanitizedConfig] } } + return clientConfig as ClientConfig } diff --git a/packages/payload/src/config/types.ts b/packages/payload/src/config/types.ts index 346cce47d..7aa042bd8 100644 --- a/packages/payload/src/config/types.ts +++ b/packages/payload/src/config/types.ts @@ -172,6 +172,11 @@ export type LivePreviewConfig = { | string } +export type RootLivePreviewConfig = { + collections?: string[] + globals?: string[] +} & LivePreviewConfig + export type OGImageConfig = { alt?: string height?: number | string @@ -202,7 +207,7 @@ export type MetaConfig = { titleSuffix?: string } & DeepClone -export type ServerOnlyLivePreviewProperties = keyof Pick +export type ServerOnlyLivePreviewProperties = keyof Pick type GeneratePreviewURLOptions = { locale: string @@ -861,10 +866,7 @@ export type Config = { */ importMapFile?: string } - livePreview?: { - collections?: string[] - globals?: string[] - } & LivePreviewConfig + livePreview?: RootLivePreviewConfig /** Base meta data to use for the Admin Panel. Included properties are titleSuffix, ogImage, and favicon. */ meta?: MetaConfig routes?: {