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
import React from 'react'
import config from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { getPayload } from 'payload'
const MyServerComponent: React.FC = () => {
// If you're working in Next.js, and you want HMR,
// 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.
const payload = await getPayload({ config })
// The `findResult` here will be fully typed as `PaginatedDocs<Page>`,
// 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
import { RefreshRouteOnSave } from './RefreshRouteOnSave.tsx'
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { getPayload } from 'payload'
import config from '../payload.config'
export default async function Page() {
const payload = await getPayloadHMR({ config })
const payload = await getPayload({ config })
const page = await payload.findByID({
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.
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
// 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 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.
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
import { getPayload } from 'payload'
import config from '@payload-config'
@@ -71,7 +50,11 @@ import config from '@payload-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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -107,11 +107,11 @@ Then, render `RefreshRouteOnSave` anywhere in your `page.tsx`. Here's an example
```tsx
import { RefreshRouteOnSave } from './RefreshRouteOnSave.tsx'
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { getPayload } from 'payload'
import config from '../payload.config'
export default async function Page() {
const payload = await getPayloadHMR({ config })
const payload = await getPayload({ config })
const page = await payload.find({
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 React from 'react'
import { Fragment } from 'react'
/* eslint-disable no-restricted-exports */
import { getPayload } from 'payload'
import React, { Fragment } from 'react'
import type { Page as PageType } from '../../../payload-types'
import config from '../../../payload.config'
import { Gutter } from '../_components/Gutter'
import RichText from '../_components/RichText'
import { RefreshRouteOnSave } from './RefreshRouteOnSave'
import classes from './index.module.scss'
import { RefreshRouteOnSave } from './RefreshRouteOnSave'
interface PageParams {
params: { slug: string }
}
export default async function Page({ params: { slug = 'home' } }: PageParams) {
const payload = await getPayloadHMR({ config })
const payload = await getPayload({ config })
const pageRes = await payload.find({
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) {
return notFound()
@@ -49,7 +48,7 @@ export default async function Page({ params: { slug = 'home' } }: PageParams) {
}
export async function generateStaticParams() {
const payload = await getPayloadHMR({ config })
const payload = await getPayload({ config })
const pagesRes = await payload.find({
collection: 'pages',
@@ -66,5 +65,5 @@ export async function generateStaticParams() {
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 Link from 'next/link'
import { getPayload } from 'payload'
import React from 'react'
import config from '../../../../payload.config'
@@ -9,7 +9,7 @@ import { Gutter } from '../Gutter'
import classes from './index.module.scss'
export const Header = async () => {
const payload = await getPayloadHMR({ config })
const payload = await getPayload({ config })
const mainMenu = await payload.findGlobal({
slug: 'main-menu',

View File

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

View File

@@ -96,8 +96,7 @@
"react-diff-viewer-continued": "3.2.6",
"sass": "1.77.4",
"sonner": "^1.5.0",
"uuid": "10.0.0",
"ws": "^8.16.0"
"uuid": "10.0.0"
},
"devDependencies": {
"@babel/cli": "^7.24.5",
@@ -111,7 +110,6 @@
"@types/react": "npm:types-react@19.0.0-rc.1",
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
"@types/uuid": "10.0.0",
"@types/ws": "^8.5.10",
"babel-plugin-react-compiler": "0.0.0-experimental-24ec0eb-20240918",
"esbuild": "0.23.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 { createPayloadRequest } from '../utilities/createPayloadRequest.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 { mergeHeaders } from '../utilities/mergeHeaders.js'

View File

@@ -5,12 +5,11 @@ import { rtlLanguages } from '@payloadcms/translations'
import { RootProvider } from '@payloadcms/ui'
import '@payloadcms/ui/scss/app.scss'
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 { getNavPrefs } from '../../elements/Nav/getNavPrefs.js'
import { getClientConfig } from '../../utilities/getClientConfig.js'
import { getPayloadHMR } from '../../utilities/getPayloadHMR.js'
import { getRequestLanguage } from '../../utilities/getRequestLanguage.js'
import { getRequestTheme } from '../../utilities/getRequestTheme.js'
import { initReq } from '../../utilities/initReq.js'
@@ -100,7 +99,7 @@ export const RootLayout = async ({
headers,
})
const payload = await getPayloadHMR({ config })
const payload = await getPayload({ config })
const { i18n, permissions, req, user } = await initReq(config)

View File

@@ -1,9 +1,8 @@
import type { Collection, ErrorResult, PayloadRequest, SanitizedConfig } from 'payload'
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 { mergeHeaders } from '../../utilities/mergeHeaders.js'
@@ -22,7 +21,7 @@ export const routeError = async ({
if (!payload) {
try {
payload = await getPayloadHMR({ config: configArg })
payload = await getPayload({ config: configArg })
} catch (e) {
return Response.json(
{

View File

@@ -1,12 +1,17 @@
import type { CustomPayloadRequestProperties, PayloadRequest, SanitizedConfig } from 'payload'
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 { URL } from 'url'
import { sanitizeLocales } from './addLocalesToRequest.js'
import { getPayloadHMR } from './getPayloadHMR.js'
import { getRequestLanguage } from './getRequestLanguage.js'
type Args = {
@@ -23,7 +28,7 @@ export const createPayloadRequest = async ({
request,
}: Args): Promise<PayloadRequest> => {
const cookies = parseCookies(request.headers)
const payload = await getPayloadHMR({ config: configPromise })
const payload = await getPayload({ config: configPromise })
const { config } = payload
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 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 })
}
}
import { getPayload } from 'payload'
export const getPayloadHMR = async (
options: Pick<InitOptions, 'config' | 'importMap'>,
): Promise<Payload> => {
if (!options?.config) {
throw new Error('Error: the payload config is required for getPayloadHMR to work.')
}
const result = await getPayload(options)
if (cached.payload) {
if (cached.reload === true) {
let resolve: () => void
result.logger.error(
"Deprecation warning: getPayloadHMR is no longer preferred. You can now use `import { getPayload } from 'payload' in all contexts.",
)
// getPayloadHMR 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 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
return result
}

View File

@@ -4,12 +4,11 @@ import type { InitPageResult, Locale, VisibleEntities } from 'payload'
import { findLocaleFromCode } from '@payloadcms/ui/shared'
import { headers as getHeaders } from 'next/headers.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 type { Args } from './types.js'
import { getPayloadHMR } from '../getPayloadHMR.js'
import { initReq } from '../initReq.js'
import { getRouteInfo } from './handleAdminPage.js'
import { handleAuthRedirect } from './handleAuthRedirect.js'
@@ -23,7 +22,7 @@ export const initPage = async ({
searchParams,
}: Args): Promise<InitPageResult> => {
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 {

View File

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

View File

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

View File

@@ -6,6 +6,7 @@ import { spawn } from 'child_process'
import crypto from 'crypto'
import { fileURLToPath } from 'node:url'
import path from 'path'
import WebSocket from 'ws'
import type { AuthArgs } from './auth/operations/auth.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 ResetPasswordResult } from './auth/operations/resetPassword.js'
import type { AuthStrategy, User } from './auth/types.js'
import type { ImportMap } from './bin/generateImportMap/index.js'
import type {
BulkOperationResult,
Collection,
@@ -25,6 +25,8 @@ import type {
SelectFromCollectionSlug,
TypeWithID,
} from './collections/config/types.js'
import { generateImportMap, type ImportMap } from './bin/generateImportMap/index.js'
export type { FieldState } from './admin/forms/Form.js'
export type * from './admin/types.js'
import type { NonNever } from 'ts-essentials'
@@ -726,32 +728,139 @@ const initialized = new BasePayload()
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) {
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) {
throw new Error('Error: the payload config is required for getPayload to work.')
}
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
}
// 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
}

12
pnpm-lock.yaml generated
View File

@@ -739,9 +739,6 @@ importers:
uuid:
specifier: 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:
'@babel/cli':
specifier: ^7.24.5
@@ -776,9 +773,6 @@ importers:
'@types/uuid':
specifier: 10.0.0
version: 10.0.0
'@types/ws':
specifier: ^8.5.10
version: 8.5.13
babel-plugin-react-compiler:
specifier: 0.0.0-experimental-24ec0eb-20240918
version: 0.0.0-experimental-24ec0eb-20240918
@@ -881,6 +875,9 @@ importers:
uuid:
specifier: 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:
'@hyrious/esbuild-plugin-commonjs':
specifier: ^0.2.4
@@ -906,6 +903,9 @@ importers:
'@types/uuid':
specifier: 10.0.0
version: 10.0.0
'@types/ws':
specifier: ^8.5.10
version: 8.5.13
copyfiles:
specifier: 2.4.1
version: 2.4.1

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,13 @@
import type { Config } from 'src/payload-types'
import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { getPayload } from 'payload'
import { unstable_cache } from 'next/cache'
type Collection = keyof Config['collections']
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({
collection,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,13 @@
import type { Config } from 'src/payload-types'
import configPromise from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities'
import { getPayload } from 'payload'
import { unstable_cache } from 'next/cache'
type Collection = keyof Config['collections']
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({
collection,

View File

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

View File

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

View File

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

View File

@@ -1,9 +1,8 @@
import type { MongooseAdapter } from '@payloadcms/db-mongodb'
import type { IndexDirection, IndexOptions } from 'mongoose'
import { reload } from '@payloadcms/next/utilities'
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 type { NextRESTClient } from '../helpers/NextRESTClient.js'

View File

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

View File

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

View File

@@ -1,8 +1,8 @@
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[]> => {
const payload = await getPayloadHMR({ config })
const payload = await getPayload({ config })
try {
const { docs } = await payload.find({

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,8 @@
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[]> => {
const payload = await getPayloadHMR({ config })
const payload = await getPayload({ config })
try {
const { docs } = await payload.find({

View File

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

View File

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