feat: deprecates getPayloadHMR in favor of simpler getPayload (#9249)

Deprecates `getPayloadHMR` and simplifies this pattern into a single
`import { getPayload } from 'payload'`.

We will still retain the exported `getPayloadHMR` but it now will throw
a deprecation warning with instructions for how to migrate.
This commit is contained in:
James Mikrut
2024-11-16 15:30:05 -05:00
committed by GitHub
parent 67ff23a6e2
commit 31b32ef941
58 changed files with 264 additions and 304 deletions

View File

@@ -68,15 +68,10 @@ Here's a quick example of a React Server Component fetching data using the Local
```tsx ```tsx
import React from 'react' import React from 'react'
import config from '@payload-config' import config from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
const MyServerComponent: React.FC = () => { const MyServerComponent: React.FC = () => {
// If you're working in Next.js, and you want HMR, const payload = await getPayload({ config })
// you should get Payload via the `getPayloadHMR` function.
const payload = await getPayloadHMR({ config })
// If you are writing a standalone script and do not need HMR,
// you can get Payload via import { getPayload } from 'payload' instead.
// The `findResult` here will be fully typed as `PaginatedDocs<Page>`, // The `findResult` here will be fully typed as `PaginatedDocs<Page>`,
// where you will have the `docs` that are returned as well as // where you will have the `docs` that are returned as well as

View File

@@ -34,11 +34,11 @@ Then, render the `RefreshRouteOnSave` component anywhere in your `page.tsx`. Her
```tsx ```tsx
import { RefreshRouteOnSave } from './RefreshRouteOnSave.tsx' import { RefreshRouteOnSave } from './RefreshRouteOnSave.tsx'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import config from '../payload.config' import config from '../payload.config'
export default async function Page() { export default async function Page() {
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
const page = await payload.findByID({ const page = await payload.findByID({
collection: 'pages', collection: 'pages',

View File

@@ -18,12 +18,9 @@ Payload can be used completely outside of Next.js which is helpful in cases like
Payload provides a convenient way to run standalone scripts, which can be useful for tasks like seeding your database or performing one-off operations. Payload provides a convenient way to run standalone scripts, which can be useful for tasks like seeding your database or performing one-off operations.
In standalone scripts, can simply import the Payload Config and use it right away. If you need an initialized copy of Payload, you can then use the `getPayload` function. This can be useful for tasks like seeding your database or performing other one-off operations. In standalone scripts, you can simply import the Payload Config and use it right away. If you need an initialized copy of Payload, you can then use the `getPayload` function. This can be useful for tasks like seeding your database or performing other one-off operations.
```ts ```ts
// We are importing `getPayload` because we don't need HMR
// for a standalone script. For usage of Payload inside Next.js,
// you should always use `import { getPayloadHMR } from '@payloadcms/next/utilities'` instead.
import { getPayload } from 'payload' import { getPayload } from 'payload'
import config from '@payload-config' import config from '@payload-config'

View File

@@ -43,27 +43,6 @@ const afterChangeHook: CollectionAfterChangeHook = async ({ req: { payload } })
If you want to import Payload in places where you don't have the option to access it from function arguments or `req`, you can import it and initialize it. If you want to import Payload in places where you don't have the option to access it from function arguments or `req`, you can import it and initialize it.
There are two places to import Payload:
**Option 1 - using HMR, within Next.js**
```ts
import { getPayloadHMR } from '@payloadcms/next/utilities'
import config from '@payload-config'
const payload = await getPayloadHMR({ config })
```
You should import Payload using the first option (`getPayloadHMR`) if you are using Payload inside of Next.js (like route handlers, server components, and similar.)
This way, in Next.js development mode, Payload will work with Hot Module Replacement (HMR), and as you make changes to your Payload Config, your usage of Payload will always be in sync with your changes. In production, `getPayloadHMR` simply disables all HMR functionality so you don't need to write your code any differently. We handle optimization for you in production mode.
If you are accessing Payload via function arguments or `req.payload`, HMR is automatically supported if you are using it within Next.js.
**Option 2 - outside of Next.js**
If you are using Payload outside of Next.js, for example in standalone scripts or in other frameworks, you can import Payload with no HMR functionality. Instead of using `getPayloadHMR`, you can use `getPayload`.
```ts ```ts
import { getPayload } from 'payload' import { getPayload } from 'payload'
import config from '@payload-config' import config from '@payload-config'
@@ -71,7 +50,11 @@ import config from '@payload-config'
const payload = await getPayload({ config }) const payload = await getPayload({ config })
``` ```
Both options function in exactly the same way outside of one having HMR support and the other not. For more information about using Payload outside of Next.js, [click here](./outside-nextjs). If you're working in Next.js' development mode, Payload will work with Hot Module Replacement (HMR), and as you make changes to your Payload Config, your usage of Payload will always be in sync with your changes. In production, `getPayload` simply disables all HMR functionality so you don't need to write your code any differently. We handle optimization for you in production mode.
If you are accessing Payload via function arguments or `req.payload`, HMR is automatically supported if you are using it within Next.js.
For more information about using Payload outside of Next.js, [click here](./outside-nextjs).
## Local options available ## Local options available

View File

@@ -48,12 +48,12 @@ See the [Collections](https://payloadcms.com/docs/configuration/collections) doc
```ts ```ts
import { headers as getHeaders } from 'next/headers.js' import { headers as getHeaders } from 'next/headers.js'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import config from '../../payload.config' import config from '../../payload.config'
export default async function AccountPage({ searchParams }) { export default async function AccountPage({ searchParams }) {
const headers = getHeaders() const headers = getHeaders()
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const { permissions, user } = await payload.auth({ headers }) const { permissions, user } = await payload.auth({ headers })
if (!user) { if (!user) {

View File

@@ -1,7 +1,7 @@
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { headers as getHeaders } from 'next/headers.js' import { headers as getHeaders } from 'next/headers.js'
import Link from 'next/link' import Link from 'next/link'
import { redirect } from 'next/navigation' import { redirect } from 'next/navigation'
import { getPayload } from 'payload'
import React, { Fragment } from 'react' import React, { Fragment } from 'react'
import config from '../../../payload.config' import config from '../../../payload.config'
@@ -14,7 +14,7 @@ import classes from './index.module.scss'
export default async function Account() { export default async function Account() {
const headers = getHeaders() const headers = getHeaders()
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
const { permissions, user } = await payload.auth({ headers }) const { permissions, user } = await payload.auth({ headers })
if (!user) { if (!user) {

View File

@@ -1,6 +1,6 @@
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { headers as getHeaders } from 'next/headers.js' import { headers as getHeaders } from 'next/headers.js'
import { redirect } from 'next/navigation' import { redirect } from 'next/navigation'
import { getPayload } from 'payload'
import React from 'react' import React from 'react'
import config from '../../../payload.config' import config from '../../../payload.config'
@@ -11,7 +11,7 @@ import classes from './index.module.scss'
export default async function CreateAccount() { export default async function CreateAccount() {
const headers = getHeaders() const headers = getHeaders()
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
const { user } = await payload.auth({ headers }) const { user } = await payload.auth({ headers })
if (user) { if (user) {

View File

@@ -1,17 +1,17 @@
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { headers as getHeaders } from 'next/headers.js' import { headers as getHeaders } from 'next/headers.js'
import { redirect } from 'next/navigation' import { redirect } from 'next/navigation'
import { getPayload } from 'payload'
import React from 'react' import React from 'react'
import config from '../../../payload.config' import config from '../../../payload.config'
import { Gutter } from '../_components/Gutter' import { Gutter } from '../_components/Gutter'
import { RenderParams } from '../_components/RenderParams' import { RenderParams } from '../_components/RenderParams'
import { LoginForm } from './LoginForm'
import classes from './index.module.scss' import classes from './index.module.scss'
import { LoginForm } from './LoginForm'
export default async function Login() { export default async function Login() {
const headers = getHeaders() const headers = getHeaders()
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
const { user } = await payload.auth({ headers }) const { user } = await payload.auth({ headers })
if (user) { if (user) {

View File

@@ -1,16 +1,16 @@
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { headers as getHeaders } from 'next/headers.js' import { headers as getHeaders } from 'next/headers.js'
import Link from 'next/link' import Link from 'next/link'
import { getPayload } from 'payload'
import React from 'react' import React from 'react'
import config from '../../../payload.config' import config from '../../../payload.config'
import { Gutter } from '../_components/Gutter' import { Gutter } from '../_components/Gutter'
import { LogoutPage } from './LogoutPage'
import classes from './index.module.scss' import classes from './index.module.scss'
import { LogoutPage } from './LogoutPage'
export default async function Logout() { export default async function Logout() {
const headers = getHeaders() const headers = getHeaders()
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
const { user } = await payload.auth({ headers }) const { user } = await payload.auth({ headers })
if (!user) { if (!user) {

View File

@@ -1,6 +1,6 @@
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { headers as getHeaders } from 'next/headers.js' import { headers as getHeaders } from 'next/headers.js'
import Link from 'next/link' import Link from 'next/link'
import { getPayload } from 'payload'
import React, { Fragment } from 'react' import React, { Fragment } from 'react'
import config from '../../payload.config' import config from '../../payload.config'
@@ -9,7 +9,7 @@ import { HydrateClientUser } from './_components/HydrateClientUser'
export default async function HomePage() { export default async function HomePage() {
const headers = getHeaders() const headers = getHeaders()
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
const { permissions, user } = await payload.auth({ headers }) const { permissions, user } = await payload.auth({ headers })
return ( return (

View File

@@ -1,16 +1,16 @@
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { headers as getHeaders } from 'next/headers.js' import { headers as getHeaders } from 'next/headers.js'
import { redirect } from 'next/navigation' import { redirect } from 'next/navigation'
import { getPayload } from 'payload'
import React from 'react' import React from 'react'
import config from '../../../payload.config' import config from '../../../payload.config'
import { Gutter } from '../_components/Gutter' import { Gutter } from '../_components/Gutter'
import { RecoverPasswordForm } from './RecoverPasswordForm'
import classes from './index.module.scss' import classes from './index.module.scss'
import { RecoverPasswordForm } from './RecoverPasswordForm'
export default async function RecoverPassword() { export default async function RecoverPassword() {
const headers = getHeaders() const headers = getHeaders()
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
const { user } = await payload.auth({ headers }) const { user } = await payload.auth({ headers })
if (user) { if (user) {

View File

@@ -1,16 +1,16 @@
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { headers as getHeaders } from 'next/headers.js' import { headers as getHeaders } from 'next/headers.js'
import { redirect } from 'next/navigation' import { redirect } from 'next/navigation'
import { getPayload } from 'payload'
import React from 'react' import React from 'react'
import config from '../../../payload.config' import config from '../../../payload.config'
import { Gutter } from '../_components/Gutter' import { Gutter } from '../_components/Gutter'
import { ResetPasswordForm } from './ResetPasswordForm'
import classes from './index.module.scss' import classes from './index.module.scss'
import { ResetPasswordForm } from './ResetPasswordForm'
export default async function ResetPassword() { export default async function ResetPassword() {
const headers = getHeaders() const headers = getHeaders()
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
const { user } = await payload.auth({ headers }) const { user } = await payload.auth({ headers })
if (user) { if (user) {

View File

@@ -107,11 +107,11 @@ Then, render `RefreshRouteOnSave` anywhere in your `page.tsx`. Here's an example
```tsx ```tsx
import { RefreshRouteOnSave } from './RefreshRouteOnSave.tsx' import { RefreshRouteOnSave } from './RefreshRouteOnSave.tsx'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import config from '../payload.config' import config from '../payload.config'
export default async function Page() { export default async function Page() {
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
const page = await payload.find({ const page = await payload.find({
collection: 'pages', collection: 'pages',

View File

@@ -1,23 +1,22 @@
/* eslint-disable no-restricted-exports */
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { notFound } from 'next/navigation' import { notFound } from 'next/navigation'
import React from 'react' /* eslint-disable no-restricted-exports */
import { Fragment } from 'react' import { getPayload } from 'payload'
import React, { Fragment } from 'react'
import type { Page as PageType } from '../../../payload-types' import type { Page as PageType } from '../../../payload-types'
import config from '../../../payload.config' import config from '../../../payload.config'
import { Gutter } from '../_components/Gutter' import { Gutter } from '../_components/Gutter'
import RichText from '../_components/RichText' import RichText from '../_components/RichText'
import { RefreshRouteOnSave } from './RefreshRouteOnSave'
import classes from './index.module.scss' import classes from './index.module.scss'
import { RefreshRouteOnSave } from './RefreshRouteOnSave'
interface PageParams { interface PageParams {
params: { slug: string } params: { slug: string }
} }
export default async function Page({ params: { slug = 'home' } }: PageParams) { export default async function Page({ params: { slug = 'home' } }: PageParams) {
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
const pageRes = await payload.find({ const pageRes = await payload.find({
collection: 'pages', collection: 'pages',
@@ -30,7 +29,7 @@ export default async function Page({ params: { slug = 'home' } }: PageParams) {
}, },
}) })
const data = pageRes?.docs?.[0] as PageType | null const data = pageRes?.docs?.[0] as null | PageType
if (data === null) { if (data === null) {
return notFound() return notFound()
@@ -49,7 +48,7 @@ export default async function Page({ params: { slug = 'home' } }: PageParams) {
} }
export async function generateStaticParams() { export async function generateStaticParams() {
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
const pagesRes = await payload.find({ const pagesRes = await payload.find({
collection: 'pages', collection: 'pages',
@@ -66,5 +65,5 @@ export async function generateStaticParams() {
slug, slug,
} }
: {}, : {},
) // eslint-disable-line function-paren-newline )
} }

View File

@@ -1,6 +1,6 @@
import { getPayloadHMR } from '@payloadcms/next/utilities'
import Image from 'next/image' import Image from 'next/image'
import Link from 'next/link' import Link from 'next/link'
import { getPayload } from 'payload'
import React from 'react' import React from 'react'
import config from '../../../../payload.config' import config from '../../../../payload.config'
@@ -9,7 +9,7 @@ import { Gutter } from '../Gutter'
import classes from './index.module.scss' import classes from './index.module.scss'
export const Header = async () => { export const Header = async () => {
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
const mainMenu = await payload.findGlobal({ const mainMenu = await payload.findGlobal({
slug: 'main-menu', slug: 'main-menu',

View File

@@ -1,16 +1,16 @@
import type { Where } from 'payload' import type { Where } from 'payload'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { headers as getHeaders } from 'next/headers' import { headers as getHeaders } from 'next/headers'
import { notFound, redirect } from 'next/navigation' import { notFound, redirect } from 'next/navigation'
import { getPayload } from 'payload'
import React from 'react' import React from 'react'
import { RenderPage } from '../../../components/RenderPage' import { RenderPage } from '../../../components/RenderPage'
export default async function Page({ params }: { params: { slug?: string[]; tenant: string } }) { export default async function Page({ params }: { params: { slug?: string[]; tenant: string } }) {
const headers = await getHeaders() const headers = await getHeaders()
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const { user } = await payload.auth({ headers }) const { user } = await payload.auth({ headers })
const tenantsQuery = await payload.find({ const tenantsQuery = await payload.find({

View File

@@ -96,8 +96,7 @@
"react-diff-viewer-continued": "3.2.6", "react-diff-viewer-continued": "3.2.6",
"sass": "1.77.4", "sass": "1.77.4",
"sonner": "^1.5.0", "sonner": "^1.5.0",
"uuid": "10.0.0", "uuid": "10.0.0"
"ws": "^8.16.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.24.5", "@babel/cli": "^7.24.5",
@@ -111,7 +110,6 @@
"@types/react": "npm:types-react@19.0.0-rc.1", "@types/react": "npm:types-react@19.0.0-rc.1",
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1", "@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
"@types/uuid": "10.0.0", "@types/uuid": "10.0.0",
"@types/ws": "^8.5.10",
"babel-plugin-react-compiler": "0.0.0-experimental-24ec0eb-20240918", "babel-plugin-react-compiler": "0.0.0-experimental-24ec0eb-20240918",
"esbuild": "0.23.1", "esbuild": "0.23.1",
"esbuild-sass-plugin": "3.3.1", "esbuild-sass-plugin": "3.3.1",

View File

@@ -3,6 +3,6 @@ export { addDataAndFileToRequest } from '../utilities/addDataAndFileToRequest.js
export { addLocalesToRequestFromData, sanitizeLocales } from '../utilities/addLocalesToRequest.js' export { addLocalesToRequestFromData, sanitizeLocales } from '../utilities/addLocalesToRequest.js'
export { createPayloadRequest } from '../utilities/createPayloadRequest.js' export { createPayloadRequest } from '../utilities/createPayloadRequest.js'
export { getNextRequestI18n } from '../utilities/getNextRequestI18n.js' export { getNextRequestI18n } from '../utilities/getNextRequestI18n.js'
export { getPayloadHMR, reload } from '../utilities/getPayloadHMR.js' export { getPayloadHMR } from '../utilities/getPayloadHMR.js'
export { headersWithCors } from '../utilities/headersWithCors.js' export { headersWithCors } from '../utilities/headersWithCors.js'
export { mergeHeaders } from '../utilities/mergeHeaders.js' export { mergeHeaders } from '../utilities/mergeHeaders.js'

View File

@@ -5,12 +5,11 @@ import { rtlLanguages } from '@payloadcms/translations'
import { RootProvider } from '@payloadcms/ui' import { RootProvider } from '@payloadcms/ui'
import '@payloadcms/ui/scss/app.scss' import '@payloadcms/ui/scss/app.scss'
import { headers as getHeaders, cookies as nextCookies } from 'next/headers.js' import { headers as getHeaders, cookies as nextCookies } from 'next/headers.js'
import { checkDependencies, parseCookies } from 'payload' import { checkDependencies, getPayload, parseCookies } from 'payload'
import React from 'react' import React from 'react'
import { getNavPrefs } from '../../elements/Nav/getNavPrefs.js' import { getNavPrefs } from '../../elements/Nav/getNavPrefs.js'
import { getClientConfig } from '../../utilities/getClientConfig.js' import { getClientConfig } from '../../utilities/getClientConfig.js'
import { getPayloadHMR } from '../../utilities/getPayloadHMR.js'
import { getRequestLanguage } from '../../utilities/getRequestLanguage.js' import { getRequestLanguage } from '../../utilities/getRequestLanguage.js'
import { getRequestTheme } from '../../utilities/getRequestTheme.js' import { getRequestTheme } from '../../utilities/getRequestTheme.js'
import { initReq } from '../../utilities/initReq.js' import { initReq } from '../../utilities/initReq.js'
@@ -100,7 +99,7 @@ export const RootLayout = async ({
headers, headers,
}) })
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
const { i18n, permissions, req, user } = await initReq(config) const { i18n, permissions, req, user } = await initReq(config)

View File

@@ -1,9 +1,8 @@
import type { Collection, ErrorResult, PayloadRequest, SanitizedConfig } from 'payload' import type { Collection, ErrorResult, PayloadRequest, SanitizedConfig } from 'payload'
import httpStatus from 'http-status' import httpStatus from 'http-status'
import { APIError, formatErrors } from 'payload' import { APIError, formatErrors, getPayload } from 'payload'
import { getPayloadHMR } from '../../utilities/getPayloadHMR.js'
import { headersWithCors } from '../../utilities/headersWithCors.js' import { headersWithCors } from '../../utilities/headersWithCors.js'
import { mergeHeaders } from '../../utilities/mergeHeaders.js' import { mergeHeaders } from '../../utilities/mergeHeaders.js'
@@ -22,7 +21,7 @@ export const routeError = async ({
if (!payload) { if (!payload) {
try { try {
payload = await getPayloadHMR({ config: configArg }) payload = await getPayload({ config: configArg })
} catch (e) { } catch (e) {
return Response.json( return Response.json(
{ {

View File

@@ -1,12 +1,17 @@
import type { CustomPayloadRequestProperties, PayloadRequest, SanitizedConfig } from 'payload' import type { CustomPayloadRequestProperties, PayloadRequest, SanitizedConfig } from 'payload'
import { initI18n } from '@payloadcms/translations' import { initI18n } from '@payloadcms/translations'
import { executeAuthStrategies, getDataLoader, parseCookies, sanitizeFallbackLocale } from 'payload' import {
executeAuthStrategies,
getDataLoader,
getPayload,
parseCookies,
sanitizeFallbackLocale,
} from 'payload'
import * as qs from 'qs-esm' import * as qs from 'qs-esm'
import { URL } from 'url' import { URL } from 'url'
import { sanitizeLocales } from './addLocalesToRequest.js' import { sanitizeLocales } from './addLocalesToRequest.js'
import { getPayloadHMR } from './getPayloadHMR.js'
import { getRequestLanguage } from './getRequestLanguage.js' import { getRequestLanguage } from './getRequestLanguage.js'
type Args = { type Args = {
@@ -23,7 +28,7 @@ export const createPayloadRequest = async ({
request, request,
}: Args): Promise<PayloadRequest> => { }: Args): Promise<PayloadRequest> => {
const cookies = parseCookies(request.headers) const cookies = parseCookies(request.headers)
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const { config } = payload const { config } = payload
const localization = config.localization const localization = config.localization

View File

@@ -1,140 +1,15 @@
import type { InitOptions, Payload, SanitizedConfig } from 'payload' import type { InitOptions, Payload } from 'payload'
import { BasePayload, generateImportMap } from 'payload' import { getPayload } from 'payload'
import WebSocket from 'ws'
let cached: {
payload: null | Payload
promise: null | Promise<Payload>
reload: boolean | Promise<void>
ws: null | WebSocket
} = global._payload
if (!cached) {
cached = global._payload = { payload: null, promise: null, reload: false, ws: null }
}
export const reload = async (
config: SanitizedConfig,
payload: Payload,
skipImportMapGeneration?: boolean,
): Promise<void> => {
if (typeof payload.db.destroy === 'function') {
await payload.db.destroy()
}
payload.config = config
payload.collections = config.collections.reduce((collections, collection) => {
collections[collection.slug] = {
config: collection,
customIDType: payload.collections[collection.slug]?.customIDType,
}
return collections
}, {})
payload.globals = {
config: config.globals,
}
// TODO: support HMR for other props in the future (see payload/src/index init()) hat may change on Payload singleton
// Generate types
if (config.typescript.autoGenerate !== false) {
// We cannot run it directly here, as generate-types imports json-schema-to-typescript, which breaks on turbopack.
// see: https://github.com/vercel/next.js/issues/66723
void payload.bin({
args: ['generate:types'],
log: false,
})
}
// Generate component map
if (skipImportMapGeneration !== true && config.admin?.importMap?.autoGenerate !== false) {
await generateImportMap(config, {
log: true,
})
}
await payload.db.init()
if (payload.db.connect) {
await payload.db.connect({ hotReload: true })
}
}
export const getPayloadHMR = async ( export const getPayloadHMR = async (
options: Pick<InitOptions, 'config' | 'importMap'>, options: Pick<InitOptions, 'config' | 'importMap'>,
): Promise<Payload> => { ): Promise<Payload> => {
if (!options?.config) { const result = await getPayload(options)
throw new Error('Error: the payload config is required for getPayloadHMR to work.')
}
if (cached.payload) { result.logger.error(
if (cached.reload === true) { "Deprecation warning: getPayloadHMR is no longer preferred. You can now use `import { getPayload } from 'payload' in all contexts.",
let resolve: () => void )
// getPayloadHMR is called multiple times, in parallel. However, we only want to run `await reload` once. By immediately setting cached.reload to a promise, return result
// we can ensure that all subsequent calls will wait for the first reload to finish. So if we set it here, the 2nd call of getPayloadHMR
// will reach `if (cached.reload instanceof Promise) {` which then waits for the first reload to finish.
cached.reload = new Promise((res) => (resolve = res))
const config = await options.config
await reload(config, cached.payload)
resolve()
}
if (cached.reload instanceof Promise) {
await cached.reload
}
if (options?.importMap) {
cached.payload.importMap = options.importMap
}
return cached.payload
}
// eslint-disable-next-line @typescript-eslint/no-misused-promises
if (!cached.promise) {
// no need to await options.config here, as it's already awaited in the BasePayload.init
cached.promise = new BasePayload().init(options)
}
try {
cached.payload = await cached.promise
if (
!cached.ws &&
process.env.NODE_ENV !== 'production' &&
process.env.NODE_ENV !== 'test' &&
process.env.DISABLE_PAYLOAD_HMR !== 'true'
) {
try {
const port = process.env.PORT || '3000'
cached.ws = new WebSocket(
`ws://localhost:${port}${process.env.NEXT_BASE_PATH ?? ''}/_next/webpack-hmr`,
)
cached.ws.onmessage = (event) => {
if (typeof event.data === 'string') {
const data = JSON.parse(event.data)
if ('action' in data && data.action === 'serverComponentChanges') {
cached.reload = true
}
}
}
} catch (_) {
// swallow e
}
}
} catch (e) {
cached.promise = null
throw e
}
if (options?.importMap) {
cached.payload.importMap = options.importMap
}
return cached.payload
} }

View File

@@ -4,12 +4,11 @@ import type { InitPageResult, Locale, VisibleEntities } from 'payload'
import { findLocaleFromCode } from '@payloadcms/ui/shared' import { findLocaleFromCode } from '@payloadcms/ui/shared'
import { headers as getHeaders } from 'next/headers.js' import { headers as getHeaders } from 'next/headers.js'
import { notFound } from 'next/navigation.js' import { notFound } from 'next/navigation.js'
import { createLocalReq, isEntityHidden, parseCookies } from 'payload' import { createLocalReq, getPayload, isEntityHidden, parseCookies } from 'payload'
import * as qs from 'qs-esm' import * as qs from 'qs-esm'
import type { Args } from './types.js' import type { Args } from './types.js'
import { getPayloadHMR } from '../getPayloadHMR.js'
import { initReq } from '../initReq.js' import { initReq } from '../initReq.js'
import { getRouteInfo } from './handleAdminPage.js' import { getRouteInfo } from './handleAdminPage.js'
import { handleAuthRedirect } from './handleAuthRedirect.js' import { handleAuthRedirect } from './handleAuthRedirect.js'
@@ -23,7 +22,7 @@ export const initPage = async ({
searchParams, searchParams,
}: Args): Promise<InitPageResult> => { }: Args): Promise<InitPageResult> => {
const headers = await getHeaders() const headers = await getHeaders()
const payload = await getPayloadHMR({ config: configPromise, importMap }) const payload = await getPayload({ config: configPromise, importMap })
const queryString = `${qs.stringify(searchParams ?? {}, { addQueryPrefix: true })}` const queryString = `${qs.stringify(searchParams ?? {}, { addQueryPrefix: true })}`
const { const {

View File

@@ -3,10 +3,9 @@ import type { PayloadRequest, SanitizedConfig, SanitizedPermissions, User } from
import { initI18n } from '@payloadcms/translations' import { initI18n } from '@payloadcms/translations'
import { headers as getHeaders } from 'next/headers.js' import { headers as getHeaders } from 'next/headers.js'
import { createLocalReq, parseCookies } from 'payload' import { createLocalReq, getPayload, parseCookies } from 'payload'
import { cache } from 'react' import { cache } from 'react'
import { getPayloadHMR } from './getPayloadHMR.js'
import { getRequestLanguage } from './getRequestLanguage.js' import { getRequestLanguage } from './getRequestLanguage.js'
type Result = { type Result = {
@@ -20,7 +19,7 @@ export const initReq = cache(async function (
configPromise: Promise<SanitizedConfig> | SanitizedConfig, configPromise: Promise<SanitizedConfig> | SanitizedConfig,
): Promise<Result> { ): Promise<Result> {
const config = await configPromise const config = await configPromise
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
const headers = await getHeaders() const headers = await getHeaders()
const cookies = parseCookies(headers) const cookies = parseCookies(headers)

View File

@@ -109,7 +109,8 @@
"scmp": "2.1.0", "scmp": "2.1.0",
"ts-essentials": "10.0.3", "ts-essentials": "10.0.3",
"tsx": "4.19.2", "tsx": "4.19.2",
"uuid": "10.0.0" "uuid": "10.0.0",
"ws": "^8.16.0"
}, },
"devDependencies": { "devDependencies": {
"@hyrious/esbuild-plugin-commonjs": "^0.2.4", "@hyrious/esbuild-plugin-commonjs": "^0.2.4",
@@ -120,6 +121,7 @@
"@types/pluralize": "0.0.33", "@types/pluralize": "0.0.33",
"@types/react-datepicker": "6.2.0", "@types/react-datepicker": "6.2.0",
"@types/uuid": "10.0.0", "@types/uuid": "10.0.0",
"@types/ws": "^8.5.10",
"copyfiles": "2.4.1", "copyfiles": "2.4.1",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"esbuild": "0.23.1", "esbuild": "0.23.1",

View File

@@ -6,6 +6,7 @@ import { spawn } from 'child_process'
import crypto from 'crypto' import crypto from 'crypto'
import { fileURLToPath } from 'node:url' import { fileURLToPath } from 'node:url'
import path from 'path' import path from 'path'
import WebSocket from 'ws'
import type { AuthArgs } from './auth/operations/auth.js' import type { AuthArgs } from './auth/operations/auth.js'
import type { Result as ForgotPasswordResult } from './auth/operations/forgotPassword.js' import type { Result as ForgotPasswordResult } from './auth/operations/forgotPassword.js'
@@ -17,7 +18,6 @@ import type { Options as VerifyEmailOptions } from './auth/operations/local/veri
import type { Result as LoginResult } from './auth/operations/login.js' import type { Result as LoginResult } from './auth/operations/login.js'
import type { Result as ResetPasswordResult } from './auth/operations/resetPassword.js' import type { Result as ResetPasswordResult } from './auth/operations/resetPassword.js'
import type { AuthStrategy, User } from './auth/types.js' import type { AuthStrategy, User } from './auth/types.js'
import type { ImportMap } from './bin/generateImportMap/index.js'
import type { import type {
BulkOperationResult, BulkOperationResult,
Collection, Collection,
@@ -25,6 +25,8 @@ import type {
SelectFromCollectionSlug, SelectFromCollectionSlug,
TypeWithID, TypeWithID,
} from './collections/config/types.js' } from './collections/config/types.js'
import { generateImportMap, type ImportMap } from './bin/generateImportMap/index.js'
export type { FieldState } from './admin/forms/Form.js' export type { FieldState } from './admin/forms/Form.js'
export type * from './admin/types.js' export type * from './admin/types.js'
import type { NonNever } from 'ts-essentials' import type { NonNever } from 'ts-essentials'
@@ -726,32 +728,139 @@ const initialized = new BasePayload()
export default initialized export default initialized
let cached = global._payload let cached: {
payload: null | Payload
promise: null | Promise<Payload>
reload: boolean | Promise<void>
ws: null | WebSocket
} = global._payload
if (!cached) { if (!cached) {
cached = global._payload = { payload: null, promise: null } cached = global._payload = { payload: null, promise: null, reload: false, ws: null }
} }
export const getPayload = async (options: InitOptions): Promise<BasePayload> => { export const reload = async (
config: SanitizedConfig,
payload: Payload,
skipImportMapGeneration?: boolean,
): Promise<void> => {
if (typeof payload.db.destroy === 'function') {
await payload.db.destroy()
}
payload.config = config
payload.collections = config.collections.reduce((collections, collection) => {
collections[collection.slug] = {
config: collection,
customIDType: payload.collections[collection.slug]?.customIDType,
}
return collections
}, {})
payload.globals = {
config: config.globals,
}
// TODO: support HMR for other props in the future (see payload/src/index init()) that may change on Payload singleton
// Generate types
if (config.typescript.autoGenerate !== false) {
// We cannot run it directly here, as generate-types imports json-schema-to-typescript, which breaks on turbopack.
// see: https://github.com/vercel/next.js/issues/66723
void payload.bin({
args: ['generate:types'],
log: false,
})
}
// Generate component map
if (skipImportMapGeneration !== true && config.admin?.importMap?.autoGenerate !== false) {
await generateImportMap(config, {
log: true,
})
}
await payload.db.init()
if (payload.db.connect) {
await payload.db.connect({ hotReload: true })
}
}
export const getPayload = async (
options: Pick<InitOptions, 'config' | 'importMap'>,
): Promise<Payload> => {
if (!options?.config) { if (!options?.config) {
throw new Error('Error: the payload config is required for getPayload to work.') throw new Error('Error: the payload config is required for getPayload to work.')
} }
if (cached.payload) { if (cached.payload) {
if (cached.reload === true) {
let resolve: () => void
// getPayload is called multiple times, in parallel. However, we only want to run `await reload` once. By immediately setting cached.reload to a promise,
// we can ensure that all subsequent calls will wait for the first reload to finish. So if we set it here, the 2nd call of getPayload
// will reach `if (cached.reload instanceof Promise) {` which then waits for the first reload to finish.
cached.reload = new Promise((res) => (resolve = res))
const config = await options.config
await reload(config, cached.payload)
resolve()
}
if (cached.reload instanceof Promise) {
await cached.reload
}
if (options?.importMap) {
cached.payload.importMap = options.importMap
}
return cached.payload return cached.payload
} }
// eslint-disable-next-line @typescript-eslint/no-misused-promises
if (!cached.promise) { if (!cached.promise) {
// no need to await options.config here, as it's already awaited in the BasePayload.init
cached.promise = new BasePayload().init(options) cached.promise = new BasePayload().init(options)
} }
try { try {
cached.payload = await cached.promise cached.payload = await cached.promise
if (
!cached.ws &&
process.env.NODE_ENV !== 'production' &&
process.env.NODE_ENV !== 'test' &&
process.env.DISABLE_PAYLOAD_HMR !== 'true'
) {
try {
const port = process.env.PORT || '3000'
cached.ws = new WebSocket(
`ws://localhost:${port}${process.env.NEXT_BASE_PATH ?? ''}/_next/webpack-hmr`,
)
cached.ws.onmessage = (event) => {
if (typeof event.data === 'string') {
const data = JSON.parse(event.data)
if ('action' in data && data.action === 'serverComponentChanges') {
cached.reload = true
}
}
}
} catch (_) {
// swallow e
}
}
} catch (e) { } catch (e) {
cached.promise = null cached.promise = null
throw e throw e
} }
if (options?.importMap) {
cached.payload.importMap = options.importMap
}
return cached.payload return cached.payload
} }

12
pnpm-lock.yaml generated
View File

@@ -739,9 +739,6 @@ importers:
uuid: uuid:
specifier: 10.0.0 specifier: 10.0.0
version: 10.0.0 version: 10.0.0
ws:
specifier: ^8.16.0
version: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.5)
devDependencies: devDependencies:
'@babel/cli': '@babel/cli':
specifier: ^7.24.5 specifier: ^7.24.5
@@ -776,9 +773,6 @@ importers:
'@types/uuid': '@types/uuid':
specifier: 10.0.0 specifier: 10.0.0
version: 10.0.0 version: 10.0.0
'@types/ws':
specifier: ^8.5.10
version: 8.5.13
babel-plugin-react-compiler: babel-plugin-react-compiler:
specifier: 0.0.0-experimental-24ec0eb-20240918 specifier: 0.0.0-experimental-24ec0eb-20240918
version: 0.0.0-experimental-24ec0eb-20240918 version: 0.0.0-experimental-24ec0eb-20240918
@@ -881,6 +875,9 @@ importers:
uuid: uuid:
specifier: 10.0.0 specifier: 10.0.0
version: 10.0.0 version: 10.0.0
ws:
specifier: ^8.16.0
version: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.5)
devDependencies: devDependencies:
'@hyrious/esbuild-plugin-commonjs': '@hyrious/esbuild-plugin-commonjs':
specifier: ^0.2.4 specifier: ^0.2.4
@@ -906,6 +903,9 @@ importers:
'@types/uuid': '@types/uuid':
specifier: 10.0.0 specifier: 10.0.0
version: 10.0.0 version: 10.0.0
'@types/ws':
specifier: ^8.5.10
version: 8.5.13
copyfiles: copyfiles:
specifier: 2.4.1 specifier: 2.4.1
version: 2.4.1 version: 2.4.1

View File

@@ -2,7 +2,7 @@ import type { Metadata } from 'next'
import { PayloadRedirects } from '@/components/PayloadRedirects' import { PayloadRedirects } from '@/components/PayloadRedirects'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import { draftMode } from 'next/headers' import { draftMode } from 'next/headers'
import React, { cache } from 'react' import React, { cache } from 'react'
import { homeStatic } from '@/endpoints/seed/home-static' import { homeStatic } from '@/endpoints/seed/home-static'
@@ -15,7 +15,7 @@ import { generateMeta } from '@/utilities/generateMeta'
import PageClient from './page.client' import PageClient from './page.client'
export async function generateStaticParams() { export async function generateStaticParams() {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const pages = await payload.find({ const pages = await payload.find({
collection: 'pages', collection: 'pages',
draft: false, draft: false,
@@ -85,7 +85,7 @@ export async function generateMetadata({ params: paramsPromise }): Promise<Metad
const queryPageBySlug = cache(async ({ slug }: { slug: string }) => { const queryPageBySlug = cache(async ({ slug }: { slug: string }) => {
const { isEnabled: draft } = await draftMode() const { isEnabled: draft } = await draftMode()
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const result = await payload.find({ const result = await payload.find({
collection: 'pages', collection: 'pages',

View File

@@ -1,7 +1,7 @@
import jwt from 'jsonwebtoken' import jwt from 'jsonwebtoken'
import { draftMode } from 'next/headers' import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation' import { redirect } from 'next/navigation'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { CollectionSlug } from 'payload' import { CollectionSlug } from 'payload'
@@ -16,7 +16,7 @@ export async function GET(
} }
}, },
): Promise<Response> { ): Promise<Response> {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const token = req.cookies.get(payloadToken)?.value const token = req.cookies.get(payloadToken)?.value
const { searchParams } = new URL(req.url) const { searchParams } = new URL(req.url)
const path = searchParams.get('path') const path = searchParams.get('path')

View File

@@ -3,7 +3,7 @@ import type { Metadata } from 'next'
import { RelatedPosts } from '@/blocks/RelatedPosts/Component' import { RelatedPosts } from '@/blocks/RelatedPosts/Component'
import { PayloadRedirects } from '@/components/PayloadRedirects' import { PayloadRedirects } from '@/components/PayloadRedirects'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import { draftMode } from 'next/headers' import { draftMode } from 'next/headers'
import React, { cache } from 'react' import React, { cache } from 'react'
import RichText from '@/components/RichText' import RichText from '@/components/RichText'
@@ -15,7 +15,7 @@ import { generateMeta } from '@/utilities/generateMeta'
import PageClient from './page.client' import PageClient from './page.client'
export async function generateStaticParams() { export async function generateStaticParams() {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const posts = await payload.find({ const posts = await payload.find({
collection: 'posts', collection: 'posts',
draft: false, draft: false,
@@ -82,7 +82,7 @@ export async function generateMetadata({ params: paramsPromise }: Args): Promise
const queryPostBySlug = cache(async ({ slug }: { slug: string }) => { const queryPostBySlug = cache(async ({ slug }: { slug: string }) => {
const { isEnabled: draft } = await draftMode() const { isEnabled: draft } = await draftMode()
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const result = await payload.find({ const result = await payload.find({
collection: 'posts', collection: 'posts',

View File

@@ -4,7 +4,7 @@ import { CollectionArchive } from '@/components/CollectionArchive'
import { PageRange } from '@/components/PageRange' import { PageRange } from '@/components/PageRange'
import { Pagination } from '@/components/Pagination' import { Pagination } from '@/components/Pagination'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import React from 'react' import React from 'react'
import PageClient from './page.client' import PageClient from './page.client'
@@ -12,7 +12,7 @@ export const dynamic = 'force-static'
export const revalidate = 600 export const revalidate = 600
export default async function Page() { export default async function Page() {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const posts = await payload.find({ const posts = await payload.find({
collection: 'posts', collection: 'posts',

View File

@@ -4,7 +4,7 @@ import { CollectionArchive } from '@/components/CollectionArchive'
import { PageRange } from '@/components/PageRange' import { PageRange } from '@/components/PageRange'
import { Pagination } from '@/components/Pagination' import { Pagination } from '@/components/Pagination'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import React from 'react' import React from 'react'
import PageClient from './page.client' import PageClient from './page.client'
import { notFound } from 'next/navigation' import { notFound } from 'next/navigation'
@@ -19,7 +19,7 @@ type Args = {
export default async function Page({ params: paramsPromise }: Args) { export default async function Page({ params: paramsPromise }: Args) {
const { pageNumber } = await paramsPromise const { pageNumber } = await paramsPromise
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const sanitizedPageNumber = Number(pageNumber) const sanitizedPageNumber = Number(pageNumber)
@@ -70,7 +70,7 @@ export async function generateMetadata({ params: paramsPromise }: Args): Promise
} }
export async function generateStaticParams() { export async function generateStaticParams() {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const posts = await payload.find({ const posts = await payload.find({
collection: 'posts', collection: 'posts',
depth: 0, depth: 0,

View File

@@ -2,7 +2,7 @@ import type { Metadata } from 'next/types'
import { CollectionArchive } from '@/components/CollectionArchive' import { CollectionArchive } from '@/components/CollectionArchive'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import React from 'react' import React from 'react'
import { Post } from '@/payload-types' import { Post } from '@/payload-types'
import { Search } from '@/search/Component' import { Search } from '@/search/Component'
@@ -15,7 +15,7 @@ type Args = {
} }
export default async function Page({ searchParams: searchParamsPromise }: Args) { export default async function Page({ searchParams: searchParamsPromise }: Args) {
const { q: query } = await searchParamsPromise const { q: query } = await searchParamsPromise
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const posts = await payload.find({ const posts = await payload.find({
collection: 'search', collection: 'search',

View File

@@ -1,7 +1,7 @@
import type { Post, ArchiveBlock as ArchiveBlockProps } from '@/payload-types' import type { Post, ArchiveBlock as ArchiveBlockProps } from '@/payload-types'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import React from 'react' import React from 'react'
import RichText from '@/components/RichText' import RichText from '@/components/RichText'
@@ -19,7 +19,7 @@ export const ArchiveBlock: React.FC<
let posts: Post[] = [] let posts: Post[] = []
if (populateBy === 'collection') { if (populateBy === 'collection') {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const flattenedCategories = categories?.map((category) => { const flattenedCategories = categories?.map((category) => {
if (typeof category === 'object') return category.id if (typeof category === 'object') return category.id

View File

@@ -1,13 +1,13 @@
import type { Config } from 'src/payload-types' import type { Config } from 'src/payload-types'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import { unstable_cache } from 'next/cache' import { unstable_cache } from 'next/cache'
type Collection = keyof Config['collections'] type Collection = keyof Config['collections']
async function getDocument(collection: Collection, slug: string, depth = 0) { async function getDocument(collection: Collection, slug: string, depth = 0) {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const page = await payload.find({ const page = await payload.find({
collection, collection,

View File

@@ -1,13 +1,13 @@
import type { Config } from 'src/payload-types' import type { Config } from 'src/payload-types'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import { unstable_cache } from 'next/cache' import { unstable_cache } from 'next/cache'
type Global = keyof Config['globals'] type Global = keyof Config['globals']
async function getGlobal(slug: Global, depth = 0) { async function getGlobal(slug: Global, depth = 0) {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const global = await payload.findGlobal({ const global = await payload.findGlobal({
slug, slug,

View File

@@ -1,9 +1,9 @@
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import { unstable_cache } from 'next/cache' import { unstable_cache } from 'next/cache'
export async function getRedirects(depth = 1) { export async function getRedirects(depth = 1) {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const { docs: redirects } = await payload.find({ const { docs: redirects } = await payload.find({
collection: 'redirects', collection: 'redirects',

View File

@@ -2,7 +2,7 @@ import type { Metadata } from 'next'
import { PayloadRedirects } from '@/components/PayloadRedirects' import { PayloadRedirects } from '@/components/PayloadRedirects'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import { draftMode } from 'next/headers' import { draftMode } from 'next/headers'
import React, { cache } from 'react' import React, { cache } from 'react'
import { homeStatic } from '@/endpoints/seed/home-static' import { homeStatic } from '@/endpoints/seed/home-static'
@@ -15,7 +15,7 @@ import { generateMeta } from '@/utilities/generateMeta'
import PageClient from './page.client' import PageClient from './page.client'
export async function generateStaticParams() { export async function generateStaticParams() {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const pages = await payload.find({ const pages = await payload.find({
collection: 'pages', collection: 'pages',
draft: false, draft: false,
@@ -85,7 +85,7 @@ export async function generateMetadata({ params: paramsPromise }): Promise<Metad
const queryPageBySlug = cache(async ({ slug }: { slug: string }) => { const queryPageBySlug = cache(async ({ slug }: { slug: string }) => {
const { isEnabled: draft } = await draftMode() const { isEnabled: draft } = await draftMode()
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const result = await payload.find({ const result = await payload.find({
collection: 'pages', collection: 'pages',

View File

@@ -1,7 +1,7 @@
import jwt from 'jsonwebtoken' import jwt from 'jsonwebtoken'
import { draftMode } from 'next/headers' import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation' import { redirect } from 'next/navigation'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { CollectionSlug } from 'payload' import { CollectionSlug } from 'payload'
@@ -16,7 +16,7 @@ export async function GET(
} }
}, },
): Promise<Response> { ): Promise<Response> {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const token = req.cookies.get(payloadToken)?.value const token = req.cookies.get(payloadToken)?.value
const { searchParams } = new URL(req.url) const { searchParams } = new URL(req.url)
const path = searchParams.get('path') const path = searchParams.get('path')

View File

@@ -3,7 +3,7 @@ import type { Metadata } from 'next'
import { RelatedPosts } from '@/blocks/RelatedPosts/Component' import { RelatedPosts } from '@/blocks/RelatedPosts/Component'
import { PayloadRedirects } from '@/components/PayloadRedirects' import { PayloadRedirects } from '@/components/PayloadRedirects'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import { draftMode } from 'next/headers' import { draftMode } from 'next/headers'
import React, { cache } from 'react' import React, { cache } from 'react'
import RichText from '@/components/RichText' import RichText from '@/components/RichText'
@@ -15,7 +15,7 @@ import { generateMeta } from '@/utilities/generateMeta'
import PageClient from './page.client' import PageClient from './page.client'
export async function generateStaticParams() { export async function generateStaticParams() {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const posts = await payload.find({ const posts = await payload.find({
collection: 'posts', collection: 'posts',
draft: false, draft: false,
@@ -82,7 +82,7 @@ export async function generateMetadata({ params: paramsPromise }: Args): Promise
const queryPostBySlug = cache(async ({ slug }: { slug: string }) => { const queryPostBySlug = cache(async ({ slug }: { slug: string }) => {
const { isEnabled: draft } = await draftMode() const { isEnabled: draft } = await draftMode()
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const result = await payload.find({ const result = await payload.find({
collection: 'posts', collection: 'posts',

View File

@@ -4,7 +4,7 @@ import { CollectionArchive } from '@/components/CollectionArchive'
import { PageRange } from '@/components/PageRange' import { PageRange } from '@/components/PageRange'
import { Pagination } from '@/components/Pagination' import { Pagination } from '@/components/Pagination'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import React from 'react' import React from 'react'
import PageClient from './page.client' import PageClient from './page.client'
@@ -12,7 +12,7 @@ export const dynamic = 'force-static'
export const revalidate = 600 export const revalidate = 600
export default async function Page() { export default async function Page() {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const posts = await payload.find({ const posts = await payload.find({
collection: 'posts', collection: 'posts',

View File

@@ -4,7 +4,7 @@ import { CollectionArchive } from '@/components/CollectionArchive'
import { PageRange } from '@/components/PageRange' import { PageRange } from '@/components/PageRange'
import { Pagination } from '@/components/Pagination' import { Pagination } from '@/components/Pagination'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import React from 'react' import React from 'react'
import PageClient from './page.client' import PageClient from './page.client'
import { notFound } from 'next/navigation' import { notFound } from 'next/navigation'
@@ -19,7 +19,7 @@ type Args = {
export default async function Page({ params: paramsPromise }: Args) { export default async function Page({ params: paramsPromise }: Args) {
const { pageNumber } = await paramsPromise const { pageNumber } = await paramsPromise
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const sanitizedPageNumber = Number(pageNumber) const sanitizedPageNumber = Number(pageNumber)
@@ -70,7 +70,7 @@ export async function generateMetadata({ params: paramsPromise }: Args): Promise
} }
export async function generateStaticParams() { export async function generateStaticParams() {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const posts = await payload.find({ const posts = await payload.find({
collection: 'posts', collection: 'posts',
depth: 0, depth: 0,

View File

@@ -2,7 +2,7 @@ import type { Metadata } from 'next/types'
import { CollectionArchive } from '@/components/CollectionArchive' import { CollectionArchive } from '@/components/CollectionArchive'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import React from 'react' import React from 'react'
import { Post } from '@/payload-types' import { Post } from '@/payload-types'
import { Search } from '@/search/Component' import { Search } from '@/search/Component'
@@ -15,7 +15,7 @@ type Args = {
} }
export default async function Page({ searchParams: searchParamsPromise }: Args) { export default async function Page({ searchParams: searchParamsPromise }: Args) {
const { q: query } = await searchParamsPromise const { q: query } = await searchParamsPromise
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const posts = await payload.find({ const posts = await payload.find({
collection: 'search', collection: 'search',

View File

@@ -1,7 +1,7 @@
import type { Post, ArchiveBlock as ArchiveBlockProps } from '@/payload-types' import type { Post, ArchiveBlock as ArchiveBlockProps } from '@/payload-types'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import React from 'react' import React from 'react'
import RichText from '@/components/RichText' import RichText from '@/components/RichText'
@@ -19,7 +19,7 @@ export const ArchiveBlock: React.FC<
let posts: Post[] = [] let posts: Post[] = []
if (populateBy === 'collection') { if (populateBy === 'collection') {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const flattenedCategories = categories?.map((category) => { const flattenedCategories = categories?.map((category) => {
if (typeof category === 'object') return category.id if (typeof category === 'object') return category.id

View File

@@ -1,13 +1,13 @@
import type { Config } from 'src/payload-types' import type { Config } from 'src/payload-types'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import { unstable_cache } from 'next/cache' import { unstable_cache } from 'next/cache'
type Collection = keyof Config['collections'] type Collection = keyof Config['collections']
async function getDocument(collection: Collection, slug: string, depth = 0) { async function getDocument(collection: Collection, slug: string, depth = 0) {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const page = await payload.find({ const page = await payload.find({
collection, collection,

View File

@@ -1,13 +1,13 @@
import type { Config } from 'src/payload-types' import type { Config } from 'src/payload-types'
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import { unstable_cache } from 'next/cache' import { unstable_cache } from 'next/cache'
type Global = keyof Config['globals'] type Global = keyof Config['globals']
async function getGlobal(slug: Global, depth = 0) { async function getGlobal(slug: Global, depth = 0) {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const global = await payload.findGlobal({ const global = await payload.findGlobal({
slug, slug,

View File

@@ -1,9 +1,9 @@
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
import { unstable_cache } from 'next/cache' import { unstable_cache } from 'next/cache'
export async function getRedirects(depth = 1) { export async function getRedirects(depth = 1) {
const payload = await getPayloadHMR({ config: configPromise }) const payload = await getPayload({ config: configPromise })
const { docs: redirects } = await payload.find({ const { docs: redirects } = await payload.find({
collection: 'redirects', collection: 'redirects',

View File

@@ -1,8 +1,8 @@
import configPromise from '@payload-config' import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities' import { getPayload } from 'payload'
export const Page = async ({ params, searchParams }) => { export const Page = async ({ params, searchParams }) => {
const payload = await getPayloadHMR({ const payload = await getPayload({
config: configPromise, config: configPromise,
}) })
return <div>test ${payload?.config?.collections?.length}</div> return <div>test ${payload?.config?.collections?.length}</div>

View File

@@ -1,9 +1,8 @@
import type { MongooseAdapter } from '@payloadcms/db-mongodb' import type { MongooseAdapter } from '@payloadcms/db-mongodb'
import type { IndexDirection, IndexOptions } from 'mongoose' import type { IndexDirection, IndexOptions } from 'mongoose'
import { reload } from '@payloadcms/next/utilities'
import path from 'path' import path from 'path'
import { type PaginatedDocs, type Payload, ValidationError } from 'payload' import { type PaginatedDocs, type Payload, reload, ValidationError } from 'payload'
import { fileURLToPath } from 'url' import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../helpers/NextRESTClient.js' import type { NextRESTClient } from '../helpers/NextRESTClient.js'

View File

@@ -1,7 +1,7 @@
import type { Payload, SanitizedConfig } from 'payload' import type { Payload, SanitizedConfig } from 'payload'
import { getPayloadHMR } from '@payloadcms/next/utilities'
import path from 'path' import path from 'path'
import { getPayload } from 'payload'
import { runInit } from '../runInit.js' import { runInit } from '../runInit.js'
import { NextRESTClient } from './NextRESTClient.js' import { NextRESTClient } from './NextRESTClient.js'
@@ -19,9 +19,7 @@ export async function initPayloadInt(
const { default: config } = await import(path.resolve(dirname, 'config.ts')) const { default: config } = await import(path.resolve(dirname, 'config.ts'))
console.log('starting payload') console.log('starting payload')
// need to use getPayloadHMR and not getPayload, as getPayloadHMR will be used in next handlers. If we use getPayload const payload = await getPayload({ config })
// here, payload would be cached somewhere else
const payload = await getPayloadHMR({ config })
console.log('initializing rest client') console.log('initializing rest client')
const restClient = new NextRESTClient(payload.config) const restClient = new NextRESTClient(payload.config)
console.log('initPayloadInt done') console.log('initPayloadInt done')

View File

@@ -1,7 +1,7 @@
import type { CollectionSlug, Where } from 'payload' import type { CollectionSlug, Where } from 'payload'
import config from '@payload-config' import config from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities/getPayloadHMR.js' import { getPayload } from 'payload'
export const getDoc = async <T>(args: { export const getDoc = async <T>(args: {
collection: CollectionSlug collection: CollectionSlug
@@ -9,7 +9,7 @@ export const getDoc = async <T>(args: {
draft?: boolean draft?: boolean
slug?: string slug?: string
}): Promise<T> => { }): Promise<T> => {
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
const { slug, collection, depth = 2, draft } = args || {} const { slug, collection, depth = 2, draft } = args || {}
const where: Where = {} const where: Where = {}
@@ -28,7 +28,9 @@ export const getDoc = async <T>(args: {
draft, draft,
}) })
if (docs[0]) return docs[0] as T if (docs[0]) {
return docs[0] as T
}
} catch (err) { } catch (err) {
console.log('Error getting doc', err) console.log('Error getting doc', err)
} }

View File

@@ -1,8 +1,8 @@
import config from '@payload-config' import config from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities/getPayloadHMR.js' import { getPayload } from 'payload'
export const getDocs = async <T>(collection: string): Promise<T[]> => { export const getDocs = async <T>(collection: string): Promise<T[]> => {
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
try { try {
const { docs } = await payload.find({ const { docs } = await payload.find({

View File

@@ -1,10 +1,10 @@
import config from '@payload-config' import config from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities/getPayloadHMR.js' import { getPayload } from 'payload'
import type { Footer } from '../../../payload-types.js' import type { Footer } from '../../../payload-types.js'
export async function getFooter(): Promise<Footer> { export async function getFooter(): Promise<Footer> {
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
try { try {
const footer = await payload.findGlobal({ const footer = await payload.findGlobal({

View File

@@ -1,10 +1,10 @@
import config from '@payload-config' import config from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities/getPayloadHMR.js' import { getPayload } from 'payload'
import type { Header } from '../../../payload-types.js' import type { Header } from '../../../payload-types.js'
export async function getHeader(): Promise<Header> { export async function getHeader(): Promise<Header> {
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
try { try {
const header = await payload.findGlobal({ const header = await payload.findGlobal({

View File

@@ -1,7 +1,7 @@
import type { CollectionSlug, Where } from 'payload' import type { CollectionSlug, Where } from 'payload'
import config from '@payload-config' import config from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities/getPayloadHMR.js' import { getPayload } from 'payload'
export const getDoc = async <T>(args: { export const getDoc = async <T>(args: {
collection: CollectionSlug collection: CollectionSlug
@@ -9,7 +9,7 @@ export const getDoc = async <T>(args: {
draft?: boolean draft?: boolean
slug?: string slug?: string
}): Promise<T> => { }): Promise<T> => {
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
const { slug, collection, depth = 2, draft } = args || {} const { slug, collection, depth = 2, draft } = args || {}
const where: Where = {} const where: Where = {}
@@ -28,7 +28,9 @@ export const getDoc = async <T>(args: {
draft, draft,
}) })
if (docs[0]) return docs[0] as T if (docs[0]) {
return docs[0] as T
}
} catch (err) { } catch (err) {
console.log('Error getting doc', err) console.log('Error getting doc', err)
} }

View File

@@ -1,8 +1,8 @@
import config from '@payload-config' import config from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities/getPayloadHMR.js' import { getPayload } from 'payload'
export const getDocs = async <T>(collection: string): Promise<T[]> => { export const getDocs = async <T>(collection: string): Promise<T[]> => {
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
try { try {
const { docs } = await payload.find({ const { docs } = await payload.find({

View File

@@ -1,10 +1,10 @@
import config from '@payload-config' import config from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities/getPayloadHMR.js' import { getPayload } from 'payload'
import type { Footer } from '../../../../payload-types.js' import type { Footer } from '../../../../payload-types.js'
export async function getFooter(): Promise<Footer> { export async function getFooter(): Promise<Footer> {
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
try { try {
const footer = await payload.findGlobal({ const footer = await payload.findGlobal({

View File

@@ -1,10 +1,10 @@
import config from '@payload-config' import config from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities/getPayloadHMR.js' import { getPayload } from 'payload'
import type { Header } from '../../../../payload-types.js' import type { Header } from '../../../../payload-types.js'
export async function getHeader(): Promise<Header> { export async function getHeader(): Promise<Header> {
const payload = await getPayloadHMR({ config }) const payload = await getPayload({ config })
try { try {
const header = await payload.findGlobal({ const header = await payload.findGlobal({