Compare commits

...

4 Commits

Author SHA1 Message Date
Jacob Fletcher
340089f483 migrates fieldSchemasToFormState 2025-01-28 17:41:13 -05:00
Jacob Fletcher
e8604bde6d fix build errors 2025-01-28 16:17:29 -05:00
Jacob Fletcher
0d49752c06 fixes field schema map, deprecates modified getFieldPaths fn 2025-01-28 16:03:23 -05:00
Jacob Fletcher
b187da4639 adds standalone test suite for field paths and adds failing test for field schema map 2025-01-28 14:05:52 -05:00
30 changed files with 1067 additions and 628 deletions

View File

@@ -1,7 +1,7 @@
import { type SupportedLanguages } from '@payloadcms/translations'
import type { SanitizedDocumentPermissions } from '../../auth/types.js'
import type { Field, Validate } from '../../fields/config/types.js'
import type { Field, TabAsField, Validate } from '../../fields/config/types.js'
import type { TypedLocale } from '../../index.js'
import type { DocumentPreferences } from '../../preferences/types.js'
import type { PayloadRequest, Where } from '../../types/index.js'
@@ -43,7 +43,7 @@ export type FieldState = {
* The fieldSchema may be part of the form state if `includeSchema: true` is passed to buildFormState.
* This will never be in the form state of the client.
*/
fieldSchema?: Field
fieldSchema?: Field | TabAsField
filterOptions?: FilterOptionsResult
initialValue?: unknown
passesCondition?: boolean

View File

@@ -1683,7 +1683,7 @@ export type FieldWithManyClient = RelationshipFieldClient | SelectFieldClient
export type FieldWithMaxDepth = RelationshipField | UploadField
export type FieldWithMaxDepthClient = JoinFieldClient | RelationshipFieldClient | UploadFieldClient
export function fieldHasSubFields<TField extends ClientField | Field>(
export function fieldHasSubFields<TField extends ClientField | Field | TabAsField>(
field: TField,
): field is TField & (TField extends ClientField ? FieldWithSubFieldsClient : FieldWithSubFields) {
return (

View File

@@ -31,30 +31,6 @@ export function getFieldPaths({
parentIndexPath,
parentPath,
parentSchemaPath,
}: Args): FieldPaths {
if ('name' in field) {
return {
indexPath: `${parentIndexPath ? parentIndexPath + '-' : ''}${index}`,
path: `${parentPath ? parentPath + '.' : ''}${field.name}`,
schemaPath: `${parentSchemaPath ? parentSchemaPath + '.' : ''}${field.name}`,
}
}
const indexSuffix = `_index-${`${parentIndexPath ? parentIndexPath + '-' : ''}${index}`}`
return {
indexPath: `${parentIndexPath ? parentIndexPath + '-' : ''}${index}`,
path: `${parentPath ? parentPath + '.' : ''}${indexSuffix}`,
schemaPath: `${parentSchemaPath ? parentSchemaPath + '.' : ''}${indexSuffix}`,
}
}
export function getFieldPathsModified({
field,
index,
parentIndexPath,
parentPath,
parentSchemaPath,
}: Args): FieldPaths {
const parentPathSegments = parentPath.split('.')

View File

@@ -7,7 +7,7 @@ import type { Field, TabAsField } from '../../config/types.js'
import { MissingEditorProp } from '../../../errors/index.js'
import { fieldAffectsData, tabHasName } from '../../config/types.js'
import { getFieldPathsModified as getFieldPaths } from '../../getFieldPaths.js'
import { getFieldPaths } from '../../getFieldPaths.js'
import { traverseFields } from './traverseFields.js'
type Args = {

View File

@@ -14,7 +14,7 @@ import type { Field, TabAsField } from '../../config/types.js'
import { MissingEditorProp } from '../../../errors/index.js'
import { fieldAffectsData, tabHasName } from '../../config/types.js'
import { getDefaultValue } from '../../getDefaultValue.js'
import { getFieldPathsModified as getFieldPaths } from '../../getFieldPaths.js'
import { getFieldPaths } from '../../getFieldPaths.js'
import { relationshipPopulationPromise } from './relationshipPopulationPromise.js'
import { traverseFields } from './traverseFields.js'

View File

@@ -11,7 +11,7 @@ import { deepMergeWithSourceArrays } from '../../../utilities/deepMerge.js'
import { getLabelFromPath } from '../../../utilities/getLabelFromPath.js'
import { getTranslatedLabel } from '../../../utilities/getTranslatedLabel.js'
import { fieldAffectsData, tabHasName } from '../../config/types.js'
import { getFieldPathsModified as getFieldPaths } from '../../getFieldPaths.js'
import { getFieldPaths } from '../../getFieldPaths.js'
import { getExistingRowDoc } from './getExistingRowDoc.js'
import { traverseFields } from './traverseFields.js'

View File

@@ -4,7 +4,7 @@ import type { JsonObject, PayloadRequest } from '../../../types/index.js'
import type { Field, FieldHookArgs, TabAsField } from '../../config/types.js'
import { fieldAffectsData } from '../../config/types.js'
import { getFieldPathsModified as getFieldPaths } from '../../getFieldPaths.js'
import { getFieldPaths } from '../../getFieldPaths.js'
import { runBeforeDuplicateHooks } from './runHook.js'
import { traverseFields } from './traverseFields.js'

View File

@@ -8,7 +8,7 @@ import type { Field, TabAsField } from '../../config/types.js'
import { MissingEditorProp } from '../../../errors/index.js'
import { fieldAffectsData, tabHasName, valueIsValueWithRelation } from '../../config/types.js'
import { getDefaultValue } from '../../getDefaultValue.js'
import { getFieldPathsModified as getFieldPaths } from '../../getFieldPaths.js'
import { getFieldPaths } from '../../getFieldPaths.js'
import { cloneDataFromOriginalDoc } from '../beforeChange/cloneDataFromOriginalDoc.js'
import { getExistingRowDoc } from '../beforeChange/getExistingRowDoc.js'
import { traverseFields } from './traverseFields.js'

View File

@@ -32,6 +32,7 @@ export const getGenerateSchemaMap =
fields: field.fields,
i18n,
parentIndexPath: '',
parentPath: '',
parentSchemaPath: `${schemaPath}.lexical_internal_feature.${featureKey}.${schemaKey}`,
schemaMap,
})

View File

@@ -34,6 +34,7 @@ export const getGenerateSchemaMap =
fields: args.admin?.link?.fields as Field[],
i18n,
parentIndexPath: '',
parentPath: '',
parentSchemaPath: `${schemaPath}.${linkFieldsSchemaPath}`,
schemaMap,
})
@@ -68,6 +69,7 @@ export const getGenerateSchemaMap =
fields: args?.admin?.upload?.collections[collection.slug]?.fields,
i18n,
parentIndexPath: '',
parentPath: '',
parentSchemaPath: `${schemaPath}.${uploadFieldsSchemaPath}.${collection.slug}`,
schemaMap,
})

View File

@@ -10,6 +10,7 @@ import type {
PayloadRequest,
SanitizedFieldPermissions,
SanitizedFieldsPermissions,
TabAsField,
Validate,
} from 'payload'
@@ -21,7 +22,6 @@ import {
fieldIsHiddenOrDisabled,
fieldIsID,
fieldIsLocalized,
getFieldPaths,
tabHasName,
} from 'payload/shared'
@@ -42,7 +42,7 @@ export type AddFieldStatePromiseArgs = {
clientFieldSchemaMap?: ClientFieldSchemaMap
collectionSlug?: string
data: Data
field: Field
field: Field | TabAsField
fieldIndex: number
fieldSchemaMap: FieldSchemaMap
/**
@@ -168,7 +168,7 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
return
}
const validate: Validate = field.validate
const validate: Validate = 'validate' in field ? field.validate : undefined
let validationResult: string | true = true
@@ -235,12 +235,13 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
const arrayValue = Array.isArray(data[field.name]) ? data[field.name] : []
const { promises, rows } = arrayValue.reduce(
(acc, row, i: number) => {
const parentPath = path + '.' + i
(acc, row, rowIndex: number) => {
const rowPath = path + '.' + rowIndex
row.id = row?.id || new ObjectId().toHexString()
if (!omitParents && (!filter || filter(args))) {
const idKey = parentPath + '.id'
const idKey = rowPath + '.id'
state[idKey] = {
initialValue: row.id,
@@ -270,7 +271,7 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
operation,
parentIndexPath: '',
parentPassesCondition: passesCondition,
parentPath,
parentPath: rowPath,
parentSchemaPath: schemaPath,
permissions:
fieldPermissions === true ? fieldPermissions : fieldPermissions?.fields || {},
@@ -357,22 +358,23 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
const blocksValue = Array.isArray(data[field.name]) ? data[field.name] : []
const { promises, rowMetadata } = blocksValue.reduce(
(acc, row, i: number) => {
(acc, row, rowIndex: number) => {
const block = field.blocks.find((blockType) => blockType.slug === row.blockType)
if (!block) {
throw new Error(
`Block with type "${row.blockType}" was found in block data, but no block with that type is defined in the config for field with schema path ${schemaPath}.`,
)
}
const parentPath = path + '.' + i
const rowPath = path + '.' + rowIndex
if (block) {
row.id = row?.id || new ObjectId().toHexString()
if (!omitParents && (!filter || filter(args))) {
// Handle block `id` field
const idKey = parentPath + '.id'
const idKey = rowPath + '.id'
state[idKey] = {
initialValue: row.id,
@@ -386,7 +388,7 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
}
// Handle `blockType` field
const fieldKey = parentPath + '.blockType'
const fieldKey = rowPath + '.blockType'
state[fieldKey] = {
initialValue: row.blockType,
@@ -400,7 +402,7 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
}
// Handle `blockName` field
const blockNameKey = parentPath + '.blockName'
const blockNameKey = rowPath + '.blockName'
state[blockNameKey] = {}
@@ -434,7 +436,7 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
operation,
parentIndexPath: '',
parentPassesCondition: passesCondition,
parentPath,
parentPath: rowPath,
parentSchemaPath: schemaPath + '.' + block.slug,
permissions:
fieldPermissions === true
@@ -546,6 +548,7 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
break
}
case 'relationship':
case 'upload': {
if (field.filterOptions) {
@@ -678,8 +681,8 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
operation,
parentIndexPath: indexPath,
parentPassesCondition: passesCondition,
parentPath,
parentSchemaPath,
parentPath: path,
parentSchemaPath: schemaPath,
permissions: parentPermissions, // TODO: Verify this is correct
preferences,
previousFormState,
@@ -690,74 +693,85 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
skipValidation,
state,
})
} else if (field.type === 'tabs') {
const promises = field.tabs.map((tab, tabIndex) => {
const isNamedTab = tabHasName(tab)
} else if (field.type === 'tab') {
const isNamedTab = tabHasName(field)
const {
indexPath: tabIndexPath,
path: tabPath,
schemaPath: tabSchemaPath,
} = getFieldPaths({
field: {
...tab,
type: 'tab',
},
index: tabIndex,
parentIndexPath: indexPath,
parentPath,
parentSchemaPath,
})
let childPermissions: SanitizedFieldsPermissions = undefined
let childPermissions: SanitizedFieldsPermissions = undefined
if (isNamedTab) {
if (parentPermissions === true) {
if (isNamedTab) {
if (parentPermissions === true) {
childPermissions = true
} else {
const tabPermissions = parentPermissions?.[field.name]
if (tabPermissions === true) {
childPermissions = true
} else {
const tabPermissions = parentPermissions?.[tab.name]
if (tabPermissions === true) {
childPermissions = true
} else {
childPermissions = tabPermissions?.fields
}
childPermissions = tabPermissions?.fields
}
} else {
childPermissions = parentPermissions
}
} else {
childPermissions = parentPermissions
}
return iterateFields({
id,
addErrorPathToParent: addErrorPathToParentArg,
anyParentLocalized: tab.localized || anyParentLocalized,
clientFieldSchemaMap,
collectionSlug,
data: isNamedTab ? data?.[tab.name] || {} : data,
fields: tab.fields,
fieldSchemaMap,
filter,
forceFullValue,
fullData,
includeSchema,
omitParents,
operation,
parentIndexPath: isNamedTab ? '' : tabIndexPath,
parentPassesCondition: passesCondition,
parentPath: isNamedTab ? tabPath : parentPath,
parentSchemaPath: isNamedTab ? tabSchemaPath : parentSchemaPath,
permissions: childPermissions,
preferences,
previousFormState,
renderAllFields,
renderFieldFn,
req,
skipConditionChecks,
skipValidation,
state,
})
return iterateFields({
id,
addErrorPathToParent: addErrorPathToParentArg,
anyParentLocalized: fieldIsLocalized(field) || anyParentLocalized,
clientFieldSchemaMap,
collectionSlug,
data: isNamedTab ? data?.[field.name] || {} : data,
fields: field.fields,
fieldSchemaMap,
filter,
forceFullValue,
fullData,
includeSchema,
omitParents,
operation,
parentIndexPath: isNamedTab ? '' : indexPath,
parentPassesCondition: passesCondition,
parentPath: isNamedTab ? path : parentPath,
parentSchemaPath: schemaPath,
permissions: childPermissions,
preferences,
previousFormState,
renderAllFields,
renderFieldFn,
req,
skipConditionChecks,
skipValidation,
state,
})
} else if (field.type === 'tabs') {
return iterateFields({
id,
addErrorPathToParent: addErrorPathToParentArg,
anyParentLocalized: fieldIsLocalized(field) || anyParentLocalized,
clientFieldSchemaMap,
collectionSlug,
data,
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
fieldSchemaMap,
filter,
forceFullValue,
fullData,
includeSchema,
omitParents,
operation,
parentIndexPath: indexPath,
parentPassesCondition: passesCondition,
parentPath: path,
parentSchemaPath: schemaPath,
permissions: parentPermissions,
preferences,
previousFormState,
renderAllFields,
renderFieldFn,
req,
skipConditionChecks,
skipValidation,
state,
})
await Promise.all(promises)
} else if (field.type === 'ui') {
if (!filter || filter(args)) {
state[path] = fieldState

View File

@@ -2,12 +2,13 @@ import type {
ClientFieldSchemaMap,
Data,
DocumentPreferences,
Field as FieldSchema,
Field,
FieldSchemaMap,
FormState,
FormStateWithoutComponents,
PayloadRequest,
SanitizedFieldsPermissions,
TabAsField,
} from 'payload'
import { getFieldPaths } from 'payload/shared'
@@ -26,7 +27,7 @@ type Args = {
clientFieldSchemaMap?: ClientFieldSchemaMap
collectionSlug?: string
data: Data
fields: FieldSchema[]
fields: (Field | TabAsField)[]
fieldSchemaMap: FieldSchemaMap
filter?: (args: AddFieldStatePromiseArgs) => boolean
/**
@@ -108,7 +109,7 @@ export const iterateFields = async ({
const { indexPath, path, schemaPath } = getFieldPaths({
field,
index: fieldIndex,
parentIndexPath: 'name' in field ? '' : parentIndexPath,
parentIndexPath,
parentPath,
parentSchemaPath,
})

View File

@@ -62,6 +62,7 @@ export const buildClientFieldSchemaMap = (args: {
fields: fieldsToSet,
i18n,
parentIndexPath: '',
parentPath: '',
parentSchemaPath: collectionSlug,
payload,
schemaMap,
@@ -81,6 +82,7 @@ export const buildClientFieldSchemaMap = (args: {
fields: matchedGlobal.fields,
i18n,
parentIndexPath: '',
parentPath: '',
parentSchemaPath: globalSlug,
payload,
schemaMap,

View File

@@ -1,22 +1,24 @@
import type { I18n } from '@payloadcms/translations'
import {
type ClientConfig,
type ClientField,
type ClientFieldSchemaMap,
createClientFields,
type Field,
type FieldSchemaMap,
type Payload,
import type {
ClientConfig,
ClientField,
ClientFieldSchemaMap,
Field,
FieldSchemaMap,
Payload,
TabAsFieldClient,
} from 'payload'
import { createClientFields } from 'payload'
import { getFieldPaths, tabHasName } from 'payload/shared'
type Args = {
clientSchemaMap: ClientFieldSchemaMap
config: ClientConfig
fields: ClientField[]
fields: (ClientField | TabAsFieldClient)[]
i18n: I18n<any, any>
parentIndexPath: string
parentPath: string
parentSchemaPath: string
payload: Payload
schemaMap: FieldSchemaMap
@@ -28,16 +30,17 @@ export const traverseFields = ({
fields,
i18n,
parentIndexPath,
parentPath,
parentSchemaPath,
payload,
schemaMap,
}: Args) => {
for (const [index, field] of fields.entries()) {
const { indexPath, schemaPath } = getFieldPaths({
const { indexPath, path, schemaPath } = getFieldPaths({
field,
index,
parentIndexPath: 'name' in field ? '' : parentIndexPath,
parentPath: '',
parentIndexPath,
parentPath,
parentSchemaPath,
})
@@ -45,21 +48,23 @@ export const traverseFields = ({
switch (field.type) {
case 'array':
case 'group':
case 'group': {
traverseFields({
clientSchemaMap,
config,
fields: field.fields,
i18n,
parentIndexPath: '',
parentPath: path + '.' + 0, // mock the row index
parentSchemaPath: schemaPath,
payload,
schemaMap,
})
break
}
case 'blocks':
case 'blocks': {
field.blocks.map((block) => {
const blockSchemaPath = `${schemaPath}.${block.slug}`
@@ -70,26 +75,31 @@ export const traverseFields = ({
fields: block.fields,
i18n,
parentIndexPath: '',
parentSchemaPath: blockSchemaPath,
parentPath: path + '.' + 0, // mock the row index
parentSchemaPath: schemaPath + '.' + block.slug,
payload,
schemaMap,
})
})
break
}
case 'collapsible':
case 'row':
case 'row': {
traverseFields({
clientSchemaMap,
config,
fields: field.fields,
i18n,
parentIndexPath: indexPath,
parentSchemaPath,
parentPath,
parentSchemaPath: schemaPath,
payload,
schemaMap,
})
break
}
case 'richText': {
// richText sub-fields are not part of the ClientConfig or the Config.
@@ -132,36 +142,39 @@ export const traverseFields = ({
break
}
case 'tabs':
field.tabs.map((tab, tabIndex) => {
const isNamedTab = tabHasName(tab)
case 'tab': {
const isNamedTab = tabHasName(field)
const { indexPath: tabIndexPath, schemaPath: tabSchemaPath } = getFieldPaths({
field: {
...tab,
type: 'tab',
},
index: tabIndex,
parentIndexPath: indexPath,
parentPath: '',
parentSchemaPath,
})
clientSchemaMap.set(tabSchemaPath, tab)
traverseFields({
clientSchemaMap,
config,
fields: tab.fields,
i18n,
parentIndexPath: isNamedTab ? '' : tabIndexPath,
parentSchemaPath: isNamedTab ? tabSchemaPath : parentSchemaPath,
payload,
schemaMap,
})
traverseFields({
clientSchemaMap,
config,
fields: field.fields,
i18n,
parentIndexPath: isNamedTab ? '' : indexPath,
parentPath: isNamedTab ? path : parentPath,
parentSchemaPath: schemaPath,
payload,
schemaMap,
})
break
}
case 'tabs': {
traverseFields({
clientSchemaMap,
config,
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
i18n,
parentIndexPath: indexPath,
parentPath: path,
parentSchemaPath: schemaPath,
payload,
schemaMap,
})
break
}
}
}
}

View File

@@ -56,6 +56,7 @@ export const buildFieldSchemaMap = (args: {
fields: fieldsToSet,
i18n,
parentIndexPath: '',
parentPath: '',
parentSchemaPath: collectionSlug,
schemaMap,
})
@@ -73,6 +74,7 @@ export const buildFieldSchemaMap = (args: {
fields: matchedGlobal.fields,
i18n,
parentIndexPath: '',
parentPath: '',
parentSchemaPath: globalSlug,
schemaMap,
})

View File

@@ -1,14 +1,15 @@
import type { I18n } from '@payloadcms/translations'
import type { Field, FieldSchemaMap, SanitizedConfig } from 'payload'
import type { Field, FieldSchemaMap, SanitizedConfig, TabAsField } from 'payload'
import { MissingEditorProp } from 'payload'
import { getFieldPaths, tabHasName } from 'payload/shared'
type Args = {
config: SanitizedConfig
fields: Field[]
fields: (Field | TabAsField)[]
i18n: I18n<any, any>
parentIndexPath: string
parentPath: string
parentSchemaPath: string
schemaMap: FieldSchemaMap
}
@@ -18,15 +19,16 @@ export const traverseFields = ({
fields,
i18n,
parentIndexPath,
parentPath,
parentSchemaPath,
schemaMap,
}: Args) => {
for (const [index, field] of fields.entries()) {
const { indexPath, schemaPath } = getFieldPaths({
const { indexPath, path, schemaPath } = getFieldPaths({
field,
index,
parentIndexPath: 'name' in field ? '' : parentIndexPath,
parentPath: '',
parentIndexPath,
parentPath,
parentSchemaPath,
})
@@ -40,13 +42,14 @@ export const traverseFields = ({
fields: field.fields,
i18n,
parentIndexPath: '',
parentPath: path + '.' + 0, // mock the row index
parentSchemaPath: schemaPath,
schemaMap,
})
break
case 'blocks':
case 'blocks': {
field.blocks.map((block) => {
const blockSchemaPath = `${schemaPath}.${block.slug}`
@@ -56,12 +59,15 @@ export const traverseFields = ({
fields: block.fields,
i18n,
parentIndexPath: '',
parentSchemaPath: blockSchemaPath,
parentPath: path + '.' + 0, // mock the row index
parentSchemaPath: schemaPath + '.' + block.slug,
schemaMap,
})
})
break
}
case 'collapsible':
case 'row':
traverseFields({
@@ -69,13 +75,14 @@ export const traverseFields = ({
fields: field.fields,
i18n,
parentIndexPath: indexPath,
parentSchemaPath,
parentPath,
parentSchemaPath: schemaPath,
schemaMap,
})
break
case 'richText':
case '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
}
@@ -95,35 +102,37 @@ export const traverseFields = ({
}
break
}
case 'tabs':
field.tabs.map((tab, tabIndex) => {
const isNamedTab = tabHasName(tab)
case 'tab': {
const isNamedTab = tabHasName(field)
const { indexPath: tabIndexPath, schemaPath: tabSchemaPath } = getFieldPaths({
field: {
...tab,
type: 'tab',
},
index: tabIndex,
parentIndexPath: indexPath,
parentPath: '',
parentSchemaPath,
})
schemaMap.set(tabSchemaPath, tab)
traverseFields({
config,
fields: tab.fields,
i18n,
parentIndexPath: isNamedTab ? '' : tabIndexPath,
parentSchemaPath: isNamedTab ? tabSchemaPath : parentSchemaPath,
schemaMap,
})
traverseFields({
config,
fields: field.fields,
i18n,
parentIndexPath: isNamedTab ? '' : indexPath,
parentPath: isNamedTab ? path : parentPath,
parentSchemaPath: schemaPath,
schemaMap,
})
break
}
case 'tabs': {
traverseFields({
config,
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
i18n,
parentIndexPath: indexPath,
parentPath: path,
parentSchemaPath: schemaPath,
schemaMap,
})
break
}
}
}
}

View File

@@ -131,6 +131,8 @@ export const buildFormState = async (
i18n,
})
console.log('schemaMap', schemaMap)
const clientSchemaMap = getClientSchemaMap({
collectionSlug,
config: getClientConfig({ config, i18n, importMap: req.payload.importMap }),

View File

@@ -0,0 +1,22 @@
import { fileURLToPath } from 'node:url'
import path from 'path'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
import type { SanitizedConfig } from 'payload'
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
import { FieldPaths } from './collections/FieldPaths/index.js'
export const HooksConfig: Promise<SanitizedConfig> = buildConfigWithDefaults({
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [FieldPaths],
typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'),
},
})
export default HooksConfig

View File

@@ -0,0 +1,49 @@
import type { Page } from '@playwright/test'
import { test } from '@playwright/test'
import path from 'path'
import { fileURLToPath } from 'url'
import type { PayloadTestSDK } from '../helpers/sdk/index.js'
import type { Config } from './payload-types.js'
import { ensureCompilationIsDone, initPageConsoleErrorCatch, saveDocAndAssert } from '../helpers.js'
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
import { TEST_TIMEOUT_LONG } from '../playwright.config.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
const { beforeAll, beforeEach, describe } = test
let payload: PayloadTestSDK<Config>
describe('Field Paths', () => {
let url: AdminUrlUtil
let beforeChangeURL: AdminUrlUtil
let page: Page
let serverURL: string
beforeAll(async ({ browser }, testInfo) => {
testInfo.setTimeout(TEST_TIMEOUT_LONG)
;({ payload, serverURL } = await initPayloadE2ENoConfig<Config>({ dirname }))
url = new AdminUrlUtil(serverURL, 'before-validate')
beforeChangeURL = new AdminUrlUtil(serverURL, 'before-change-hooks')
const context = await browser.newContext()
page = await context.newPage()
initPageConsoleErrorCatch(page)
await ensureCompilationIsDone({ page, serverURL })
})
beforeEach(async () => {
await ensureCompilationIsDone({ page, serverURL })
})
test('should replace value with before validate response', async () => {
expect(true).toBe(true)
})
})

View File

@@ -0,0 +1,19 @@
import { rootParserOptions } from '../../eslint.config.js'
import testEslintConfig from '../eslint.config.js'
/** @typedef {import('eslint').Linter.Config} Config */
/** @type {Config[]} */
export const index = [
...testEslintConfig,
{
languageOptions: {
parserOptions: {
...rootParserOptions,
tsconfigRootDir: import.meta.dirname,
},
},
},
]
export default index

View File

@@ -0,0 +1,177 @@
import type { Payload, SanitizedConfig } from 'payload'
import path from 'path'
import { fileURLToPath } from 'url'
import { buildFieldSchemaMap } from '../../packages/ui/src/utilities/buildFieldSchemaMap/index.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
import { fieldPathsSlug } from './shared.js'
import { initI18n } from '@payloadcms/translations'
let payload: Payload
let config: SanitizedConfig
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
describe('Field Paths', () => {
beforeAll(async () => {
const initResult = await initPayloadInt(dirname)
config = initResult.config
payload = initResult.payload
})
afterAll(async () => {
if (typeof payload.db.destroy === 'function') {
await payload.db.destroy()
}
})
describe('hooks', () => {
it('should pass correct field paths through field hooks', async () => {
const formatExpectedFieldPaths = (
fieldIdentifier: string,
{
path,
schemaPath,
}: {
path: string[]
schemaPath: string[]
},
) => ({
[`${fieldIdentifier}_beforeValidate_FieldPaths`]: {
path,
schemaPath,
},
[`${fieldIdentifier}_beforeChange_FieldPaths`]: {
path,
schemaPath,
},
[`${fieldIdentifier}_afterRead_FieldPaths`]: {
path,
schemaPath,
},
[`${fieldIdentifier}_beforeDuplicate_FieldPaths`]: {
path,
schemaPath,
},
})
const originalDoc = await payload.create({
collection: fieldPathsSlug,
data: {
topLevelNamedField: 'Test',
array: [
{
fieldWithinArray: 'Test',
nestedArray: [
{
fieldWithinNestedArray: 'Test',
fieldWithinNestedRow: 'Test',
},
],
},
],
fieldWithinRow: 'Test',
fieldWithinUnnamedTab: 'Test',
namedTab: {
fieldWithinNamedTab: 'Test',
},
fieldWithinNestedUnnamedTab: 'Test',
},
})
// duplicate the doc to ensure that the beforeDuplicate hook is run
const doc = await payload.duplicate({
id: originalDoc.id,
collection: fieldPathsSlug,
})
expect(doc).toMatchObject({
...formatExpectedFieldPaths('topLevelNamedField', {
path: ['topLevelNamedField'],
schemaPath: ['topLevelNamedField'],
}),
...formatExpectedFieldPaths('fieldWithinArray', {
path: ['array', '0', 'fieldWithinArray'],
schemaPath: ['array', 'fieldWithinArray'],
}),
...formatExpectedFieldPaths('fieldWithinNestedArray', {
path: ['array', '0', 'nestedArray', '0', 'fieldWithinNestedArray'],
schemaPath: ['array', 'nestedArray', 'fieldWithinNestedArray'],
}),
...formatExpectedFieldPaths('fieldWithinRowWithinArray', {
path: ['array', '0', 'fieldWithinRowWithinArray'],
schemaPath: ['array', '_index-2', 'fieldWithinRowWithinArray'],
}),
...formatExpectedFieldPaths('fieldWithinRow', {
path: ['fieldWithinRow'],
schemaPath: ['_index-2', 'fieldWithinRow'],
}),
...formatExpectedFieldPaths('fieldWithinUnnamedTab', {
path: ['fieldWithinUnnamedTab'],
schemaPath: ['_index-3-0', 'fieldWithinUnnamedTab'],
}),
...formatExpectedFieldPaths('fieldWithinNestedUnnamedTab', {
path: ['fieldWithinNestedUnnamedTab'],
schemaPath: ['_index-3-0-1-0', 'fieldWithinNestedUnnamedTab'],
}),
...formatExpectedFieldPaths('fieldWithinNamedTab', {
path: ['namedTab', 'fieldWithinNamedTab'],
schemaPath: ['_index-3', 'namedTab', 'fieldWithinNamedTab'],
}),
})
})
})
describe('field schema map', () => {
it('should build a field schema map with correct field paths', async () => {
const i18n = await initI18n({
config: config.i18n,
context: 'client',
language: 'en',
})
const { fieldSchemaMap } = await buildFieldSchemaMap({
collectionSlug: fieldPathsSlug,
config,
i18n,
})
const fieldSchemaKeys: string[] = []
fieldSchemaMap.forEach((value, key) => {
if (key === fieldPathsSlug || key.endsWith('_FieldPaths')) {
return
}
fieldSchemaKeys.push(key.replace(`${fieldPathsSlug}.`, ''))
})
expect(fieldSchemaKeys).toEqual([
'topLevelNamedField',
'array',
'array.fieldWithinArray',
'array.nestedArray',
'array.nestedArray.fieldWithinNestedArray',
'array.nestedArray.id',
'array._index-2',
'array._index-2.fieldWithinRowWithinArray', // THIS ONE IS WRONG!
'array.id',
'_index-2',
'_index-2.fieldWithinRow', // THIS ONE IS WRONG!
'_index-3',
'_index-3-0',
'_index-3-0.fieldWithinUnnamedTab', // THIS ONE IS WRONG!
'_index-3-0-1',
'_index-3-0-1-0',
'_index-3-0-1-0.fieldWithinNestedUnnamedTab', // THIS ONE IS WRONG
'_index-3.namedTab', // THIS ONE IS WRONG!
'_index-3.namedTab.fieldWithinNamedTab', // THIS ONE IS WRONG!
'updatedAt',
'createdAt',
])
})
})
})

View File

@@ -0,0 +1,572 @@
/* tslint:disable */
/* eslint-disable */
/**
* This file was automatically generated by Payload.
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
* and re-run `payload generate:types` to regenerate this file.
*/
export interface Config {
auth: {
users: UserAuthOperations;
};
collections: {
'field-paths': FieldPath;
users: User;
'payload-locked-documents': PayloadLockedDocument;
'payload-preferences': PayloadPreference;
'payload-migrations': PayloadMigration;
};
collectionsJoins: {};
collectionsSelect: {
'field-paths': FieldPathsSelect<false> | FieldPathsSelect<true>;
users: UsersSelect<false> | UsersSelect<true>;
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
};
db: {
defaultIDType: string;
};
globals: {};
globalsSelect: {};
locale: null;
user: User & {
collection: 'users';
};
jobs: {
tasks: unknown;
workflows: unknown;
};
}
export interface UserAuthOperations {
forgotPassword: {
email: string;
password: string;
};
login: {
email: string;
password: string;
};
registerFirstUser: {
email: string;
password: string;
};
unlock: {
email: string;
password: string;
};
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "field-paths".
*/
export interface FieldPath {
id: string;
topLevelNamedField?: string | null;
array?:
| {
fieldWithinArray?: string | null;
nestedArray?:
| {
fieldWithinNestedArray?: string | null;
id?: string | null;
}[]
| null;
fieldWithinRowWithinArray?: string | null;
id?: string | null;
}[]
| null;
fieldWithinRow?: string | null;
fieldWithinUnnamedTab?: string | null;
fieldWithinNestedUnnamedTab?: string | null;
namedTab?: {
fieldWithinNamedTab?: string | null;
};
topLevelNamedField_beforeValidate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
topLevelNamedField_beforeChange_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
topLevelNamedField_afterRead_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
topLevelNamedField_beforeDuplicate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinArray_beforeValidate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinArray_beforeChange_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinArray_afterRead_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinArray_beforeDuplicate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNestedArray_beforeValidate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNestedArray_beforeChange_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNestedArray_afterRead_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNestedArray_beforeDuplicate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinRowWithinArray_beforeValidate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinRowWithinArray_beforeChange_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinRowWithinArray_afterRead_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinRowWithinArray_beforeDuplicate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinRow_beforeValidate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinRow_beforeChange_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinRow_afterRead_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinRow_beforeDuplicate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinUnnamedTab_beforeValidate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinUnnamedTab_beforeChange_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinUnnamedTab_afterRead_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinUnnamedTab_beforeDuplicate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNestedUnnamedTab_beforeValidate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNestedUnnamedTab_beforeChange_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNestedUnnamedTab_afterRead_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNestedUnnamedTab_beforeDuplicate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNamedTab_beforeValidate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNamedTab_beforeChange_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNamedTab_afterRead_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNamedTab_beforeDuplicate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users".
*/
export interface User {
id: string;
updatedAt: string;
createdAt: string;
email: string;
resetPasswordToken?: string | null;
resetPasswordExpiration?: string | null;
salt?: string | null;
hash?: string | null;
loginAttempts?: number | null;
lockUntil?: string | null;
password?: string | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-locked-documents".
*/
export interface PayloadLockedDocument {
id: string;
document?:
| ({
relationTo: 'field-paths';
value: string | FieldPath;
} | null)
| ({
relationTo: 'users';
value: string | User;
} | null);
globalSlug?: string | null;
user: {
relationTo: 'users';
value: string | User;
};
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences".
*/
export interface PayloadPreference {
id: string;
user: {
relationTo: 'users';
value: string | User;
};
key?: string | null;
value?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-migrations".
*/
export interface PayloadMigration {
id: string;
name?: string | null;
batch?: number | null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "field-paths_select".
*/
export interface FieldPathsSelect<T extends boolean = true> {
topLevelNamedField?: T;
array?:
| T
| {
fieldWithinArray?: T;
nestedArray?:
| T
| {
fieldWithinNestedArray?: T;
id?: T;
};
fieldWithinRowWithinArray?: T;
id?: T;
};
fieldWithinRow?: T;
fieldWithinUnnamedTab?: T;
fieldWithinNestedUnnamedTab?: T;
namedTab?:
| T
| {
fieldWithinNamedTab?: T;
};
topLevelNamedField_beforeValidate_FieldPaths?: T;
topLevelNamedField_beforeChange_FieldPaths?: T;
topLevelNamedField_afterRead_FieldPaths?: T;
topLevelNamedField_beforeDuplicate_FieldPaths?: T;
fieldWithinArray_beforeValidate_FieldPaths?: T;
fieldWithinArray_beforeChange_FieldPaths?: T;
fieldWithinArray_afterRead_FieldPaths?: T;
fieldWithinArray_beforeDuplicate_FieldPaths?: T;
fieldWithinNestedArray_beforeValidate_FieldPaths?: T;
fieldWithinNestedArray_beforeChange_FieldPaths?: T;
fieldWithinNestedArray_afterRead_FieldPaths?: T;
fieldWithinNestedArray_beforeDuplicate_FieldPaths?: T;
fieldWithinRowWithinArray_beforeValidate_FieldPaths?: T;
fieldWithinRowWithinArray_beforeChange_FieldPaths?: T;
fieldWithinRowWithinArray_afterRead_FieldPaths?: T;
fieldWithinRowWithinArray_beforeDuplicate_FieldPaths?: T;
fieldWithinRow_beforeValidate_FieldPaths?: T;
fieldWithinRow_beforeChange_FieldPaths?: T;
fieldWithinRow_afterRead_FieldPaths?: T;
fieldWithinRow_beforeDuplicate_FieldPaths?: T;
fieldWithinUnnamedTab_beforeValidate_FieldPaths?: T;
fieldWithinUnnamedTab_beforeChange_FieldPaths?: T;
fieldWithinUnnamedTab_afterRead_FieldPaths?: T;
fieldWithinUnnamedTab_beforeDuplicate_FieldPaths?: T;
fieldWithinNestedUnnamedTab_beforeValidate_FieldPaths?: T;
fieldWithinNestedUnnamedTab_beforeChange_FieldPaths?: T;
fieldWithinNestedUnnamedTab_afterRead_FieldPaths?: T;
fieldWithinNestedUnnamedTab_beforeDuplicate_FieldPaths?: T;
fieldWithinNamedTab_beforeValidate_FieldPaths?: T;
fieldWithinNamedTab_beforeChange_FieldPaths?: T;
fieldWithinNamedTab_afterRead_FieldPaths?: T;
fieldWithinNamedTab_beforeDuplicate_FieldPaths?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users_select".
*/
export interface UsersSelect<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
email?: T;
resetPasswordToken?: T;
resetPasswordExpiration?: T;
salt?: T;
hash?: T;
loginAttempts?: T;
lockUntil?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-locked-documents_select".
*/
export interface PayloadLockedDocumentsSelect<T extends boolean = true> {
document?: T;
globalSlug?: T;
user?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences_select".
*/
export interface PayloadPreferencesSelect<T extends boolean = true> {
user?: T;
key?: T;
value?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-migrations_select".
*/
export interface PayloadMigrationsSelect<T extends boolean = true> {
name?: T;
batch?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "auth".
*/
export interface Auth {
[k: string]: unknown;
}
declare module 'payload' {
// @ts-ignore
export interface GeneratedTypes extends Config {}
}

View File

@@ -0,0 +1,2 @@
export const beforeValidateSlug = 'before-validate'
export const fieldPathsSlug = 'field-paths'

View File

@@ -0,0 +1,13 @@
{
// extend your base config to share compilerOptions, etc
//"extends": "./tsconfig.json",
"compilerOptions": {
// ensure that nobody can accidentally use this config for a build
"noEmit": true
},
"include": [
// whatever paths you intend to lint
"./**/*.ts",
"./**/*.tsx"
]
}

View File

@@ -0,0 +1,3 @@
{
"extends": "../tsconfig.json"
}

View File

@@ -13,7 +13,6 @@ import { BeforeValidateCollection } from './collections/BeforeValidate/index.js'
import ChainingHooks from './collections/ChainingHooks/index.js'
import ContextHooks from './collections/ContextHooks/index.js'
import { DataHooks } from './collections/Data/index.js'
import { FieldPaths } from './collections/FieldPaths/index.js'
import Hooks, { hooksSlug } from './collections/Hook/index.js'
import NestedAfterReadHooks from './collections/NestedAfterReadHooks/index.js'
import Relations from './collections/Relations/index.js'
@@ -39,7 +38,6 @@ export const HooksConfig: Promise<SanitizedConfig> = buildConfigWithDefaults({
Relations,
Users,
DataHooks,
FieldPaths,
],
globals: [DataHooksGlobal],
endpoints: [

View File

@@ -517,101 +517,6 @@ describe('Hooks', () => {
expect(doc.field_globalAndField).toStrictEqual(globalAndFieldString + globalAndFieldString)
})
it('should pass correct field paths through field hooks', async () => {
const formatExpectedFieldPaths = (
fieldIdentifier: string,
{
path,
schemaPath,
}: {
path: string[]
schemaPath: string[]
},
) => ({
[`${fieldIdentifier}_beforeValidate_FieldPaths`]: {
path,
schemaPath,
},
[`${fieldIdentifier}_beforeChange_FieldPaths`]: {
path,
schemaPath,
},
[`${fieldIdentifier}_afterRead_FieldPaths`]: {
path,
schemaPath,
},
[`${fieldIdentifier}_beforeDuplicate_FieldPaths`]: {
path,
schemaPath,
},
})
const originalDoc = await payload.create({
collection: fieldPathsSlug,
data: {
topLevelNamedField: 'Test',
array: [
{
fieldWithinArray: 'Test',
nestedArray: [
{
fieldWithinNestedArray: 'Test',
fieldWithinNestedRow: 'Test',
},
],
},
],
fieldWithinRow: 'Test',
fieldWithinUnnamedTab: 'Test',
namedTab: {
fieldWithinNamedTab: 'Test',
},
fieldWithinNestedUnnamedTab: 'Test',
},
})
// duplicate the doc to ensure that the beforeDuplicate hook is run
const doc = await payload.duplicate({
id: originalDoc.id,
collection: fieldPathsSlug,
})
expect(doc).toMatchObject({
...formatExpectedFieldPaths('topLevelNamedField', {
path: ['topLevelNamedField'],
schemaPath: ['topLevelNamedField'],
}),
...formatExpectedFieldPaths('fieldWithinArray', {
path: ['array', '0', 'fieldWithinArray'],
schemaPath: ['array', 'fieldWithinArray'],
}),
...formatExpectedFieldPaths('fieldWithinNestedArray', {
path: ['array', '0', 'nestedArray', '0', 'fieldWithinNestedArray'],
schemaPath: ['array', 'nestedArray', 'fieldWithinNestedArray'],
}),
...formatExpectedFieldPaths('fieldWithinRowWithinArray', {
path: ['array', '0', 'fieldWithinRowWithinArray'],
schemaPath: ['array', '_index-2', 'fieldWithinRowWithinArray'],
}),
...formatExpectedFieldPaths('fieldWithinRow', {
path: ['fieldWithinRow'],
schemaPath: ['_index-2', 'fieldWithinRow'],
}),
...formatExpectedFieldPaths('fieldWithinUnnamedTab', {
path: ['fieldWithinUnnamedTab'],
schemaPath: ['_index-3-0', 'fieldWithinUnnamedTab'],
}),
...formatExpectedFieldPaths('fieldWithinNestedUnnamedTab', {
path: ['fieldWithinNestedUnnamedTab'],
schemaPath: ['_index-3-0-1-0', 'fieldWithinNestedUnnamedTab'],
}),
...formatExpectedFieldPaths('fieldWithinNamedTab', {
path: ['namedTab', 'fieldWithinNamedTab'],
schemaPath: ['_index-3', 'namedTab', 'fieldWithinNamedTab'],
}),
})
})
})
describe('config level after error hook', () => {

View File

@@ -22,7 +22,6 @@ export interface Config {
relations: Relation;
'hooks-users': HooksUser;
'data-hooks': DataHook;
'field-paths': FieldPath;
'payload-locked-documents': PayloadLockedDocument;
'payload-preferences': PayloadPreference;
'payload-migrations': PayloadMigration;
@@ -40,7 +39,6 @@ export interface Config {
relations: RelationsSelect<false> | RelationsSelect<true>;
'hooks-users': HooksUsersSelect<false> | HooksUsersSelect<true>;
'data-hooks': DataHooksSelect<false> | DataHooksSelect<true>;
'field-paths': FieldPathsSelect<false> | FieldPathsSelect<true>;
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
@@ -238,286 +236,6 @@ export interface DataHook {
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "field-paths".
*/
export interface FieldPath {
id: string;
topLevelNamedField?: string | null;
array?:
| {
fieldWithinArray?: string | null;
nestedArray?:
| {
fieldWithinNestedArray?: string | null;
id?: string | null;
}[]
| null;
id?: string | null;
}[]
| null;
fieldWithinRow?: string | null;
fieldWithinUnnamedTab?: string | null;
fieldWithinNestedUnnamedTab?: string | null;
namedTab?: {
fieldWithinNamedTab?: string | null;
};
topLevelNamedField_beforeValidate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
topLevelNamedField_beforeChange_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
topLevelNamedField_afterRead_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
topLevelNamedField_beforeDuplicate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinArray_beforeValidate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinArray_beforeChange_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinArray_afterRead_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinArray_beforeDuplicate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNestedArray_beforeValidate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNestedArray_beforeChange_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNestedArray_afterRead_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNestedArray_beforeDuplicate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinRow_beforeValidate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinRow_beforeChange_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinRow_afterRead_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinRow_beforeDuplicate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinUnnamedTab_beforeValidate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinUnnamedTab_beforeChange_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinUnnamedTab_afterRead_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinUnnamedTab_beforeDuplicate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNestedUnnamedTab_beforeValidate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNestedUnnamedTab_beforeChange_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNestedUnnamedTab_afterRead_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNestedUnnamedTab_beforeDuplicate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNamedTab_beforeValidate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNamedTab_beforeChange_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNamedTab_afterRead_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
fieldWithinNamedTab_beforeDuplicate_FieldPaths?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-locked-documents".
@@ -568,10 +286,6 @@ export interface PayloadLockedDocument {
| ({
relationTo: 'data-hooks';
value: string | DataHook;
} | null)
| ({
relationTo: 'field-paths';
value: string | FieldPath;
} | null);
globalSlug?: string | null;
user: {
@@ -756,63 +470,6 @@ export interface DataHooksSelect<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "field-paths_select".
*/
export interface FieldPathsSelect<T extends boolean = true> {
topLevelNamedField?: T;
array?:
| T
| {
fieldWithinArray?: T;
nestedArray?:
| T
| {
fieldWithinNestedArray?: T;
id?: T;
};
id?: T;
};
fieldWithinRow?: T;
fieldWithinUnnamedTab?: T;
fieldWithinNestedUnnamedTab?: T;
namedTab?:
| T
| {
fieldWithinNamedTab?: T;
};
topLevelNamedField_beforeValidate_FieldPaths?: T;
topLevelNamedField_beforeChange_FieldPaths?: T;
topLevelNamedField_afterRead_FieldPaths?: T;
topLevelNamedField_beforeDuplicate_FieldPaths?: T;
fieldWithinArray_beforeValidate_FieldPaths?: T;
fieldWithinArray_beforeChange_FieldPaths?: T;
fieldWithinArray_afterRead_FieldPaths?: T;
fieldWithinArray_beforeDuplicate_FieldPaths?: T;
fieldWithinNestedArray_beforeValidate_FieldPaths?: T;
fieldWithinNestedArray_beforeChange_FieldPaths?: T;
fieldWithinNestedArray_afterRead_FieldPaths?: T;
fieldWithinNestedArray_beforeDuplicate_FieldPaths?: T;
fieldWithinRow_beforeValidate_FieldPaths?: T;
fieldWithinRow_beforeChange_FieldPaths?: T;
fieldWithinRow_afterRead_FieldPaths?: T;
fieldWithinRow_beforeDuplicate_FieldPaths?: T;
fieldWithinUnnamedTab_beforeValidate_FieldPaths?: T;
fieldWithinUnnamedTab_beforeChange_FieldPaths?: T;
fieldWithinUnnamedTab_afterRead_FieldPaths?: T;
fieldWithinUnnamedTab_beforeDuplicate_FieldPaths?: T;
fieldWithinNestedUnnamedTab_beforeValidate_FieldPaths?: T;
fieldWithinNestedUnnamedTab_beforeChange_FieldPaths?: T;
fieldWithinNestedUnnamedTab_afterRead_FieldPaths?: T;
fieldWithinNestedUnnamedTab_beforeDuplicate_FieldPaths?: T;
fieldWithinNamedTab_beforeValidate_FieldPaths?: T;
fieldWithinNamedTab_beforeChange_FieldPaths?: T;
fieldWithinNamedTab_afterRead_FieldPaths?: T;
fieldWithinNamedTab_beforeDuplicate_FieldPaths?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-locked-documents_select".

View File

@@ -31,7 +31,7 @@
}
],
"paths": {
"@payload-config": ["./test/versions/config.ts"],
"@payload-config": ["./test/hooks/config.ts"],
"@payloadcms/live-preview": ["./packages/live-preview/src"],
"@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"],
"@payloadcms/live-preview-vue": ["./packages/live-preview-vue/src/index.ts"],