Skip to content

Commit 039e6c9

Browse files
refactor(plugin-mcp): reduced code and de-duplication (#15705)
This is a simple refactor to clean up code. - No core CRUD tools were touched by this refactor other than a small change to `convertCollectionSchemaToZod` - Primarily experimental tools needing tidying that are used only when experiments are enabled, and only in development node environments.
1 parent 7e20b5c commit 039e6c9

File tree

13 files changed

+137
-475
lines changed

13 files changed

+137
-475
lines changed

packages/plugin-mcp/src/mcp/helpers/config.ts

Lines changed: 1 addition & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
1-
import type {
2-
AdminConfig,
3-
CollectionConfigUpdates,
4-
DatabaseConfig,
5-
GeneralConfig,
6-
PluginUpdates,
7-
} from '../../types.js'
1+
import type { AdminConfig, DatabaseConfig, PluginUpdates } from '../../types.js'
82

93
/**
104
* Adds a collection to the payload.config.ts file
@@ -217,110 +211,3 @@ export function updatePluginsConfig(content: string, pluginUpdates: PluginUpdate
217211

218212
return content
219213
}
220-
221-
/**
222-
* Updates general configuration options in payload.config.ts
223-
*/
224-
export function updateGeneralConfig(content: string, generalConfig: GeneralConfig): string {
225-
// Update various general configuration options
226-
Object.entries(generalConfig).forEach(([key, value]) => {
227-
if (value !== undefined && value !== null) {
228-
const configRegex = new RegExp(`${key}:\\s*[^,}]*`, 'g')
229-
230-
if (content.match(configRegex)) {
231-
if (typeof value === 'string') {
232-
content = content.replace(configRegex, `${key}: '${value}'`)
233-
} else if (typeof value === 'boolean') {
234-
content = content.replace(configRegex, `${key}: ${value}`)
235-
} else if (typeof value === 'object') {
236-
content = content.replace(configRegex, `${key}: ${JSON.stringify(value, null, 2)}`)
237-
}
238-
} else {
239-
// Add new config option
240-
const configValue =
241-
typeof value === 'string'
242-
? `'${value}'`
243-
: typeof value === 'object'
244-
? JSON.stringify(value, null, 2)
245-
: value
246-
content = content.replace(
247-
/export default buildConfig\(\{/,
248-
`export default buildConfig({\n ${key}: ${configValue},`,
249-
)
250-
}
251-
}
252-
})
253-
254-
return content
255-
}
256-
257-
/**
258-
* Updates collection-level configuration in a collection file
259-
*/
260-
export function updateCollectionConfig(
261-
content: string,
262-
updates: CollectionConfigUpdates,
263-
collectionName: string,
264-
): string {
265-
let updatedContent = content
266-
267-
if (updates.slug) {
268-
updatedContent = updatedContent.replace(/slug:\s*'[^']*'/, `slug: '${updates.slug}'`)
269-
}
270-
271-
if (updates.access) {
272-
const accessRegex = /access:\s*\{[^}]*\}/
273-
if (updatedContent.match(accessRegex)) {
274-
// Update existing access config
275-
Object.entries(updates.access).forEach(([key, value]) => {
276-
if (value !== undefined) {
277-
updatedContent = updatedContent.replace(
278-
new RegExp(`${key}:\\s*[^,}]*`),
279-
`${key}: ${value}`,
280-
)
281-
}
282-
})
283-
} else {
284-
// Add access config
285-
const accessConfig = Object.entries(updates.access)
286-
.filter(([, value]) => value !== undefined)
287-
.map(([key, value]) => ` ${key}: ${value}`)
288-
.join(',\n')
289-
290-
updatedContent = updatedContent.replace(
291-
/slug:\s*'[^']*',/,
292-
`slug: '${collectionName}',\n access: {\n${accessConfig}\n },`,
293-
)
294-
}
295-
}
296-
297-
if (updates.timestamps !== undefined) {
298-
if (updatedContent.includes('timestamps:')) {
299-
updatedContent = updatedContent.replace(
300-
/timestamps:[^,}]*/,
301-
`timestamps: ${updates.timestamps}`,
302-
)
303-
} else {
304-
updatedContent = updatedContent.replace(
305-
/fields:\s*\[/,
306-
`timestamps: ${updates.timestamps},\n fields: [`,
307-
)
308-
}
309-
}
310-
311-
if (updates.versioning !== undefined) {
312-
if (updatedContent.includes('versioning:')) {
313-
updatedContent = updatedContent.replace(
314-
/versioning:[^,}]*/,
315-
`versioning: ${updates.versioning}`,
316-
)
317-
} else {
318-
updatedContent = updatedContent.replace(
319-
/fields:\s*\[/,
320-
`versioning: ${updates.versioning},\n fields: [`,
321-
)
322-
}
323-
}
324-
325-
return updatedContent
326-
}

packages/plugin-mcp/src/mcp/helpers/conversion.ts

Lines changed: 0 additions & 3 deletions
This file was deleted.

packages/plugin-mcp/src/mcp/helpers/fields.ts

Lines changed: 46 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,51 @@
1-
type FieldDefinition = {
2-
description?: string
3-
name: string
4-
options?: { label: string; value: string }[]
5-
position?: 'main' | 'sidebar'
6-
required?: boolean
7-
type: string
1+
import type { FieldDefinition, FieldModification } from '../../types.js'
2+
3+
/** Escapes a value for safe embedding inside a single-quoted TypeScript string literal. */
4+
function escapeSingleQuotedString(value: string): string {
5+
return value
6+
.replace(/\\/g, '\\\\')
7+
.replace(/'/g, "\\'")
8+
.replace(/\r/g, '\\r')
9+
.replace(/\n/g, '\\n')
810
}
911

10-
type FieldModification = {
11-
changes: {
12-
description?: string
13-
options?: { label: string; value: string }[]
14-
position?: 'main' | 'sidebar'
15-
required?: boolean
16-
type?: string
12+
/**
13+
* Generates the TypeScript source string for a single field definition block.
14+
* Used when writing collection files to disk.
15+
*/
16+
export function generateFieldDefinitionString(field: FieldDefinition): string {
17+
const lines: string[] = []
18+
lines.push(` {`)
19+
lines.push(` name: '${escapeSingleQuotedString(field.name)}',`)
20+
lines.push(` type: '${escapeSingleQuotedString(field.type)}',`)
21+
22+
if (field.required) {
23+
lines.push(` required: true,`)
24+
}
25+
26+
if (field.description || field.position) {
27+
lines.push(` admin: {`)
28+
if (field.description) {
29+
lines.push(` description: '${escapeSingleQuotedString(field.description)}',`)
30+
}
31+
if (field.position) {
32+
lines.push(` position: '${escapeSingleQuotedString(field.position)}',`)
33+
}
34+
lines.push(` },`)
1735
}
18-
fieldName: string
36+
37+
if (field.options && field.type === 'select') {
38+
lines.push(` options: [`)
39+
field.options.forEach((option) => {
40+
lines.push(
41+
` { label: '${escapeSingleQuotedString(option.label)}', value: '${escapeSingleQuotedString(option.value)}' },`,
42+
)
43+
})
44+
lines.push(` ],`)
45+
}
46+
47+
lines.push(` },`)
48+
return lines.join('\n')
1949
}
2050

2151
/**
@@ -30,41 +60,7 @@ export function addFieldsToCollection(content: string, newFields: FieldDefinitio
3060
throw new Error('Could not find fields array in collection file')
3161
}
3262

33-
// Generate new field definitions
34-
const newFieldDefinitions = newFields
35-
.map((field) => {
36-
const fieldConfig = []
37-
fieldConfig.push(` {`)
38-
fieldConfig.push(` name: '${field.name}',`)
39-
fieldConfig.push(` type: '${field.type}',`)
40-
41-
if (field.required) {
42-
fieldConfig.push(` required: true,`)
43-
}
44-
45-
if (field.description || field.position) {
46-
fieldConfig.push(` admin: {`)
47-
if (field.description) {
48-
fieldConfig.push(` description: '${field.description}',`)
49-
}
50-
if (field.position) {
51-
fieldConfig.push(` position: '${field.position}',`)
52-
}
53-
fieldConfig.push(` },`)
54-
}
55-
56-
if (field.options && field.type === 'select') {
57-
fieldConfig.push(` options: [`)
58-
field.options.forEach((option: { label: string; value: string }) => {
59-
fieldConfig.push(` { label: '${option.label}', value: '${option.value}' },`)
60-
})
61-
fieldConfig.push(` ],`)
62-
}
63-
64-
fieldConfig.push(` },`)
65-
return fieldConfig.join('\n')
66-
})
67-
.join('\n')
63+
const newFieldDefinitions = newFields.map(generateFieldDefinitionString).join('\n')
6864

6965
// Add new fields before the closing bracket
7066
const existingFields = match[1] || ''

0 commit comments

Comments
 (0)