Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/graphql/src/schema/fieldToSchemaMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,7 @@ export const fieldToSchemaMap: FieldToSchemaMap = {
depth = args.depth
}
if (!field?.editor) {
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
throw new MissingEditorProp({ fieldName: field.name }) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
}

if (typeof field?.editor === 'function') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ const buildVersionField = ({
let CustomComponent = customDiffComponents?.[field.type]
if (field?.type === 'richText') {
if (!field?.editor) {
throw new MissingEditorProp(field) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
throw new MissingEditorProp({ fieldName: field.name }) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
}

if (typeof field?.editor === 'function') {
Expand Down
4 changes: 2 additions & 2 deletions packages/payload/src/errors/DuplicateFieldName.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { APIError } from './APIError.js'

export class DuplicateFieldName extends APIError {
constructor(fieldName: string) {
constructor(fieldPath: string) {
super(
`A field with the name '${fieldName}' was found multiple times on the same level. Field names must be unique.`,
`A field with path '${fieldPath}' was found multiple times on the same level. Field names must be unique.`,
)
}
}
4 changes: 2 additions & 2 deletions packages/payload/src/errors/InvalidFieldJoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import type { JoinField } from '../fields/config/types.js'
import { APIError } from './APIError.js'

export class InvalidFieldJoin extends APIError {
constructor(field: JoinField) {
constructor(field: JoinField, fieldPath: string) {
super(
`Invalid join field ${field.name}. The config does not have a field '${field.on}' in collection '${field.collection}'.`,
`Invalid join field with path ${fieldPath}. The config does not have a field '${field.on}' in collection '${field.collection}'.`,
)
}
}
6 changes: 2 additions & 4 deletions packages/payload/src/errors/InvalidFieldName.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import type { FieldAffectingData } from '../fields/config/types.js'

import { APIError } from './APIError.js'

export class InvalidFieldName extends APIError {
constructor(field: FieldAffectingData, fieldName: string) {
constructor(fieldPath: string, fieldName: string) {
super(
`Field ${field.label} has invalid name '${fieldName}'. Field names can not include periods (.) and must be alphanumeric.`,
`Field at path ${fieldPath} has invalid name '${fieldName}'. Field names can not include periods (.) and must be alphanumeric.`,
)
}
}
6 changes: 2 additions & 4 deletions packages/payload/src/errors/InvalidFieldRelationship.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import type { RelationshipField, UploadField } from '../fields/config/types.js'

import { APIError } from './APIError.js'

export class InvalidFieldRelationship extends APIError {
constructor(field: RelationshipField | UploadField, relationship: string) {
super(`Field ${field.label} has invalid relationship '${relationship}'.`)
constructor(fieldPath: string, relationship: string) {
super(`Field with path ${fieldPath} has invalid relationship '${relationship}'.`)
}
}
16 changes: 11 additions & 5 deletions packages/payload/src/errors/MissingEditorProp.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import type { Field } from '../fields/config/types.js'

import { fieldAffectsData } from '../fields/config/types.js'
import { APIError } from './APIError.js'

type MissingEditorPropArgs =
| {
fieldName: string
}
| {
fieldPath: string
}

export class MissingEditorProp extends APIError {
constructor(field: Field) {
constructor(args: MissingEditorPropArgs) {
const nameOrPath = 'fieldName' in args ? `name ${args.fieldName}` : `path ${args.fieldPath}`
super(
`RichText field${fieldAffectsData(field) ? ` "${field.name}"` : ''} is missing the editor prop. For sub-richText fields, the editor props is required, as it would otherwise create infinite recursion.`,
`RichText field with ${nameOrPath} is missing the editor prop. For sub-richText fields, the editor props is required, as it would otherwise create infinite recursion.`,
)
}
}
4 changes: 2 additions & 2 deletions packages/payload/src/errors/ReservedFieldName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { FieldAffectingData } from '../fields/config/types.js'
import { APIError } from './APIError.js'

export class ReservedFieldName extends APIError {
constructor(field: FieldAffectingData, fieldName: string) {
super(`Field ${field.label} has reserved name '${fieldName}'.`)
constructor(fieldPath: string, fieldName: string) {
super(`Field ${fieldPath} has reserved name '${fieldName}'.`)
}
}
2 changes: 1 addition & 1 deletion packages/payload/src/fields/config/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ export const createClientField = ({

case 'richText': {
if (!incomingField?.editor) {
throw new MissingEditorProp(incomingField) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
throw new MissingEditorProp({ fieldName: incomingField.name }) // while we allow disabling editor functionality, you should not have any richText fields defined if you do not have an editor
}

if (typeof incomingField?.editor === 'function') {
Expand Down
84 changes: 77 additions & 7 deletions packages/payload/src/fields/config/sanitize.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,17 @@ import {
MissingFieldType,
} from '../../errors/index.js'
import { sanitizeFields } from './sanitize.js'
import { CollectionConfig } from '../../index.js'
import { describe, it, expect } from 'vitest'
import { CollectionConfig, GlobalConfig } from '../../index.js'
import { describe, expect, it } from 'vitest'

describe('sanitizeFields', () => {
const config = {} as Config
const collectionConfig = {} as CollectionConfig
const collectionConfig = {
slug: 'example-collection',
} as CollectionConfig
const globalConfig = {
slug: 'example-global',
} as GlobalConfig

it('should throw on missing type field', async () => {
const fields: Field[] = [
Expand Down Expand Up @@ -75,14 +80,21 @@ describe('sanitizeFields', () => {
},
]

await expect(async () => {
let error: Error | null = null
try {
await sanitizeFields({
config,
collectionConfig,
fields,
validRelationships: [],
})
}).rejects.toThrow(DuplicateFieldName)
} catch (e) {
error = e
}
expect(error).toBeInstanceOf(DuplicateFieldName)
expect(error?.message).toBe(
"A field with path 'example-collection > someField' was found multiple times on the same level. Field names must be unique.",
)
})

it('should throw on duplicate block slug', async () => {
Expand Down Expand Up @@ -113,14 +125,72 @@ describe('sanitizeFields', () => {
},
]

await expect(async () => {
let error: Error | null = null
try {
await sanitizeFields({
config,
collectionConfig,
fields,
validRelationships: [],
})
}).rejects.toThrow(DuplicateFieldName)
} catch (e) {
error = e
}
expect(error).toBeInstanceOf(DuplicateFieldName)
expect(error?.message).toBe(
"A field with path 'example-collection > blocks > block' was found multiple times on the same level. Field names must be unique.",
)
})

it('should recursively build error message', async () => {
const fields: Field[] = [
{
name: 'blocks',
type: 'blocks',
blocks: [
{
slug: 'block',
fields: [
{
name: 'blockField',
type: 'blocks',
blocks: [
{
slug: 'innerBlock',
fields: [
{
name: 'innerField',
type: 'text',
},
{
name: 'innerField',
type: 'text',
},
],
},
],
},
],
},
],
},
]

let error: Error | null = null
try {
await sanitizeFields({
config,
globalConfig,
fields,
validRelationships: [],
})
} catch (e) {
error = e
}
expect(error).toBeInstanceOf(DuplicateFieldName)
expect(error?.message).toBe(
"A field with path 'example-global > blocks > block > blockField > innerBlock > innerField' was found multiple times on the same level. Field names must be unique.",
)
})

describe('auto-labeling', () => {
Expand Down
Loading