Files
payload/templates/plugin/src/index.ts
Sasha d8a62b7022 feat: plugin template (#10150)
Updates the plugin template and adds it to the monorepo

Includes:
* Integration testing setup 
* Adding custom client / server components via a plugin
* The same building setup that we use for our plugins in the monorepo
* `create-payload-app` dynamically configures the project based on the
name:`dev/tsconfig.json`, `src/index.ts`, `dev/payload.config.ts`
For example, from project name: `payload-plugin-cool`
`src/index.ts`:
```ts
export type PayloadPluginCoolConfig = {
  /**
   * List of collections to add a custom field
   */
  collections?: Partial<Record<CollectionSlug, true>>
  disabled?: boolean
}

export const payloadPluginCool =
  (pluginOptions: PayloadPluginCoolConfig) =>
/// ...
```
`dev/tsconfig.json`:
```json
{
  "extends": "../tsconfig.json",
  "exclude": [],
  "include": [
    "**/*.ts",
    "**/*.tsx",
    "../src/**/*.ts",
    "../src/**/*.tsx",
    "next.config.mjs",
    ".next/types/**/*.ts"
  ],
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@payload-config": [
        "./payload.config.ts"
      ],
      "payload-plugin-cool": [
        "../src/index.ts"
      ],
      "payload-plugin-cool/client": [
        "../src/exports/client.ts"
      ],
      "payload-plugin-cool/rsc": [
        "../src/exports/rsc.ts"
      ]
    },
    "noEmit": true
  }
}

```

`./dev/payload.config.ts`
```
import { payloadPluginCool } from 'payload-plugin-cool'
///
 plugins: [
    payloadPluginCool({
      collections: {
        posts: true,
      },
    }),
  ],
```

Example of published plugin
https://www.npmjs.com/package/payload-plugin-cool
2024-12-27 14:25:08 +00:00

114 lines
2.7 KiB
TypeScript

import type { CollectionSlug, Config } from 'payload'
export type MyPluginConfig = {
/**
* List of collections to add a custom field
*/
collections?: Partial<Record<CollectionSlug, true>>
disabled?: boolean
}
export const myPlugin =
(pluginOptions: MyPluginConfig) =>
(config: Config): Config => {
if (!config.collections) {
config.collections = []
}
config.collections.push({
slug: 'plugin-collection',
fields: [
{
name: 'id',
type: 'text',
},
],
})
if (pluginOptions.collections) {
for (const collectionSlug in pluginOptions.collections) {
const collection = config.collections.find(
(collection) => collection.slug === collectionSlug,
)
if (collection) {
collection.fields.push({
name: 'addedByPlugin',
type: 'text',
admin: {
position: 'sidebar',
},
})
}
}
}
/**
* If the plugin is disabled, we still want to keep added collections/fields so the database schema is consistent which is important for migrations.
* If your plugin heavily modifies the database schema, you may want to remove this property.
*/
if (pluginOptions.disabled) {
return config
}
if (!config.endpoints) {
config.endpoints = []
}
if (!config.admin) {
config.admin = {}
}
if (!config.admin.components) {
config.admin.components = {}
}
if (!config.admin.components.beforeDashboard) {
config.admin.components.beforeDashboard = []
}
config.admin.components.beforeDashboard.push(
`plugin-package-name-placeholder/client#BeforeDashboardClient`,
)
config.admin.components.beforeDashboard.push(
`plugin-package-name-placeholder/rsc#BeforeDashboardServer`,
)
config.endpoints.push({
handler: () => {
return Response.json({ message: 'Hello from custom endpoint' })
},
method: 'get',
path: '/my-plugin-endpoint',
})
const incomingOnInit = config.onInit
config.onInit = async (payload) => {
// Ensure we are executing any existing onInit functions before running our own.
if (incomingOnInit) {
await incomingOnInit(payload)
}
const { totalDocs } = await payload.count({
collection: 'plugin-collection',
where: {
id: {
equals: 'seeded-by-plugin',
},
},
})
if (totalDocs === 0) {
await payload.create({
collection: 'plugin-collection',
data: {
id: 'seeded-by-plugin',
},
})
}
}
return config
}