Files
payloadcms/packages/plugin-cloud-storage/src/fields/getFields.ts
Dallas Opelt 285fc8cf7e fix: presentational-field types incorrectly exposing hooks (#11514)
### What?

Presentational Fields such as
[Row](https://payloadcms.com/docs/fields/row) are described as only
effecting the admin panel. If they do not impact data, their types
should not include hooks in the fields config.

### Why?

Developers can currently assign hooks to these fields, expecting them to
work, when in reality they are not called.

### How?

Omit `hooks` from `FieldBase`

Fixes #11507

---------

Co-authored-by: German Jablonski <43938777+GermanJablo@users.noreply.github.com>
2025-09-10 18:05:59 +00:00

162 lines
4.0 KiB
TypeScript

import type { CollectionConfig, Field, GroupField, TextField } from 'payload'
import path from 'path'
import type { GeneratedAdapter, GenerateFileURL } from '../types.js'
import { getAfterReadHook } from '../hooks/afterRead.js'
interface Args {
adapter: GeneratedAdapter
collection: CollectionConfig
disablePayloadAccessControl?: true
generateFileURL?: GenerateFileURL
prefix?: string
}
export const getFields = ({
adapter,
collection,
disablePayloadAccessControl,
generateFileURL,
prefix,
}: Args): Field[] => {
const baseURLField: TextField = {
name: 'url',
type: 'text',
admin: {
hidden: true,
readOnly: true,
},
label: 'URL',
}
const basePrefixField: TextField = {
name: 'prefix',
type: 'text',
admin: {
hidden: true,
readOnly: true,
},
}
const fields = [...collection.fields, ...(adapter.fields || [])]
// Inject a hook into all URL fields to generate URLs
let existingURLFieldIndex = -1
const existingURLField = fields.find((existingField, i) => {
if ('name' in existingField && existingField.name === 'url') {
existingURLFieldIndex = i
return true
}
return false
}) as TextField
if (existingURLFieldIndex > -1) {
fields.splice(existingURLFieldIndex, 1)
}
fields.push({
...baseURLField,
...(existingURLField || {}),
hooks: {
afterRead: [
getAfterReadHook({ adapter, collection, disablePayloadAccessControl, generateFileURL }),
...(existingURLField?.hooks?.afterRead || []),
],
},
} as TextField)
if (typeof collection.upload === 'object' && collection.upload.imageSizes) {
let existingSizesFieldIndex = -1
const existingSizesField = fields.find((existingField, i) => {
if ('name' in existingField && existingField.name === 'sizes') {
existingSizesFieldIndex = i
return true
}
return false
}) as GroupField
if (existingSizesFieldIndex > -1) {
fields.splice(existingSizesFieldIndex, 1)
}
const sizesField: Field = {
...(existingSizesField || {}),
name: 'sizes',
type: 'group',
admin: {
hidden: true,
},
fields: collection.upload.imageSizes.map((size) => {
const existingSizeField = existingSizesField?.fields.find(
(existingField) => 'name' in existingField && existingField.name === size.name,
) as GroupField
const existingSizeURLField = existingSizeField?.fields.find(
(existingField) => 'name' in existingField && existingField.name === 'url',
) as GroupField
return {
...existingSizeField,
name: size.name,
type: 'group',
fields: [
...(adapter.fields || []),
{
...(existingSizeURLField || {}),
...baseURLField,
hooks: {
afterRead: [
getAfterReadHook({
adapter,
collection,
disablePayloadAccessControl,
generateFileURL,
size,
}),
...((typeof existingSizeURLField === 'object' &&
'hooks' in existingSizeURLField &&
existingSizeURLField?.hooks?.afterRead) ||
[]),
],
},
},
],
} as Field
}),
}
fields.push(sizesField)
}
// If prefix is enabled, save it to db
if (typeof prefix !== 'undefined') {
let existingPrefixFieldIndex = -1
const existingPrefixField = fields.find((existingField, i) => {
if ('name' in existingField && existingField.name === 'prefix') {
existingPrefixFieldIndex = i
return true
}
return false
}) as TextField
if (existingPrefixFieldIndex > -1) {
fields.splice(existingPrefixFieldIndex, 1)
}
fields.push({
...basePrefixField,
...(existingPrefixField || {}),
defaultValue: path.posix.join(prefix),
} as TextField)
}
return fields
}