Compare commits

...

1 Commits

Author SHA1 Message Date
Dan Ribbens
3dd28e41d8 POC infinite recursion of blocks 2024-04-11 12:28:20 -04:00
5 changed files with 87 additions and 11 deletions

View File

@@ -1,5 +1,5 @@
import type { Config } from '../../config/types' import type { Config } from '../../config/types'
import type { Field } from './types' import type { Block, Field } from './types'
import withCondition from '../../admin/components/forms/withCondition' import withCondition from '../../admin/components/forms/withCondition'
import { import {
@@ -18,6 +18,7 @@ type Args = {
config: Config config: Config
existingFieldNames?: Set<string> existingFieldNames?: Set<string>
fields: Field[] fields: Field[]
sanitizedBlocksMap?: Record<string, Block>
/** /**
* If not null, will validate that upload and relationship fields do not relate to a collection that is not in this array. * If not null, will validate that upload and relationship fields do not relate to a collection that is not in this array.
* This validation will be skipped if validRelationships is null. * This validation will be skipped if validRelationships is null.
@@ -29,6 +30,7 @@ export const sanitizeFields = ({
config, config,
existingFieldNames = new Set(), existingFieldNames = new Set(),
fields, fields,
sanitizedBlocksMap = {},
validRelationships, validRelationships,
}: Args): Field[] => { }: Args): Field[] => {
if (!fields) return [] if (!fields) return []
@@ -96,10 +98,16 @@ export const sanitizeFields = ({
} }
if (field.type === 'blocks' && field.blocks) { if (field.type === 'blocks' && field.blocks) {
field.blocks = field.blocks.map((block) => ({ field.blocks = field.blocks.map((block) => {
...block, // break recursion
fields: block.fields.concat(baseBlockFields), if (!block.slug && Object.keys(block)[0]) {
})) return sanitizedBlocksMap[Object.keys(block)[0]]
}
return {
...block,
fields: block.fields.concat(baseBlockFields),
}
})
} }
if (field.type === 'array' && field.fields) { if (field.type === 'array' && field.fields) {
@@ -113,7 +121,7 @@ export const sanitizeFields = ({
if (fieldAffectsData(field)) { if (fieldAffectsData(field)) {
if (existingFieldNames.has(field.name)) { if (existingFieldNames.has(field.name)) {
throw new DuplicateFieldName(field.name) throw new DuplicateFieldName(field.name)
} else if (!['id', 'blockName'].includes(field.name)) { } else if (!['blockName', 'id'].includes(field.name)) {
existingFieldNames.add(field.name) existingFieldNames.add(field.name)
} }
@@ -169,16 +177,29 @@ export const sanitizeFields = ({
if ('blocks' in field && field.blocks) { if ('blocks' in field && field.blocks) {
field.blocks = field.blocks.map((block) => { field.blocks = field.blocks.map((block) => {
const unsanitizedBlock = { ...block } if (sanitizedBlocksMap[block?.interfaceName || block.slug]) {
return sanitizedBlocksMap[block?.interfaceName || block.slug]
}
// break recursion
if (!block.slug && Object.keys(block)[0]) {
return sanitizedBlocksMap[Object.keys(block)[0]]
}
sanitizedBlocksMap[block?.interfaceName || block.slug] = { ...block }
const unsanitizedBlock = sanitizedBlocksMap[block?.interfaceName || block.slug]
unsanitizedBlock.labels = !unsanitizedBlock.labels unsanitizedBlock.labels = !unsanitizedBlock.labels
? formatLabels(unsanitizedBlock.slug) ? formatLabels(unsanitizedBlock.slug)
: unsanitizedBlock.labels : unsanitizedBlock.labels
unsanitizedBlock.fields = sanitizeFields({ unsanitizedBlock.fields = sanitizeFields({
config, config,
fields: block.fields,
validRelationships,
existingFieldNames: new Set(), existingFieldNames: new Set(),
fields: block.fields,
sanitizedBlocksMap,
validRelationships,
}) })
return unsanitizedBlock return unsanitizedBlock

19
test/_community/blockA.ts Normal file
View File

@@ -0,0 +1,19 @@
import type { Block } from 'payload/dist/fields/config/types'
import { BlockB } from './blockB'
export const BlockA: Block = {
slug: 'block-a',
fields: [
{
name: 'nestedBlocks',
type: 'blocks',
blocks: [BlockB],
},
{
name: 'title',
type: 'text',
},
],
interfaceName: 'BlockA',
}

15
test/_community/blockB.ts Normal file
View File

@@ -0,0 +1,15 @@
import type { Block } from 'payload/dist/fields/config/types'
import { BlockC } from './blockC'
export const BlockB: Block = {
slug: 'block-b',
fields: [
{
name: 'nestedBlocks',
type: 'blocks',
blocks: [BlockC],
},
],
interfaceName: 'BlockB',
}

15
test/_community/blockC.ts Normal file
View File

@@ -0,0 +1,15 @@
import type { Block } from 'payload/dist/fields/config/types'
const Recurse = require('./blockA')
export const BlockC: Block = {
slug: 'block-C',
fields: [
{
name: 'nestedBlocks',
type: 'blocks',
blocks: [Recurse],
},
],
interfaceName: 'BlockC',
}

View File

@@ -1,24 +1,30 @@
import type { CollectionConfig } from '../../../../packages/payload/src/collections/config/types' import type { CollectionConfig } from '../../../../packages/payload/src/collections/config/types'
import { BlockA } from '../../blockA'
import { mediaSlug } from '../Media' import { mediaSlug } from '../Media'
export const postsSlug = 'posts' export const postsSlug = 'posts'
export const PostsCollection: CollectionConfig = { export const PostsCollection: CollectionConfig = {
slug: postsSlug,
fields: [ fields: [
{ {
name: 'text', name: 'text',
type: 'text', type: 'text',
}, },
{
name: 'blocks',
type: 'blocks',
blocks: [BlockA],
},
{ {
name: 'associatedMedia', name: 'associatedMedia',
type: 'upload',
access: { access: {
create: () => true, create: () => true,
update: () => false, update: () => false,
}, },
relationTo: mediaSlug, relationTo: mediaSlug,
type: 'upload',
}, },
], ],
slug: postsSlug,
} }