payloadcms blank init

This commit is contained in:
2025-10-08 14:21:29 -04:00
parent 0d96ac3ddd
commit b018742ebc
38 changed files with 3303 additions and 351 deletions

2
.env.example Normal file
View File

@@ -0,0 +1,2 @@
DATABASE_URI=mongodb://127.0.0.1/your-database-name
PAYLOAD_SECRET=YOUR_SECRET_HERE

57
.gitignore vendored
View File

@@ -1,27 +1,46 @@
build # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
dist / media
node_modules # dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
/.idea/*
!/.idea/runConfigurations
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store .DS_Store
.env *.pem
.next
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel .vercel
node_modules/ # typescript
.next/ *.tsbuildinfo
build/ next-env.d.ts
dist/
.env .env
.env.*
.DS_Store
*.log
postgres-data/
# Payload default media upload directory
public/media/
public/robots.txt
public/sitemap*.xml
/media
# Playwright # Playwright
node_modules/ node_modules/

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
legacy-peer-deps=true

6
.prettierrc.json Normal file
View File

@@ -0,0 +1,6 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"semi": false
}

3
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
}

24
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,24 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Next.js: debug full stack",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/next/dist/bin/next",
"runtimeArgs": ["--inspect"],
"skipFiles": ["<node_internals>/**"],
"serverReadyAction": {
"action": "debugWithChrome",
"killOnServerStop": true,
"pattern": "- Local:.+(https?://.+)",
"uriFormat": "%s",
"webRoot": "${workspaceFolder}"
},
"cwd": "${workspaceFolder}"
}
]
}

40
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,40 @@
{
"npm.packageManager": "pnpm",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"editor.formatOnSaveMode": "file",
"typescript.tsdk": "node_modules/typescript/lib",
"[javascript][typescript][typescriptreact]": {
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}
}

1
.yarnrc Normal file
View File

@@ -0,0 +1 @@
--install.ignore-engines true

71
Dockerfile Normal file
View File

@@ -0,0 +1,71 @@
# To use this Dockerfile, you have to set `output: 'standalone'` in your next.config.mjs file.
# From https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile
FROM node:22.17.0-alpine AS base
# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN \
if [ -f yarn.lock ]; then yarn run build; \
elif [ -f package-lock.json ]; then npm run build; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
else echo "Lockfile not found." && exit 1; \
fi
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Remove this line if you do not have this folder
COPY --from=builder /app/public ./public
# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
CMD HOSTNAME="0.0.0.0" node server.js

300
README.md
View File

@@ -1,65 +1,39 @@
# Payload Website Template # Payload Blank Template
This is the official [Payload Website Template](https://github.com/payloadcms/payload/blob/main/templates/website). Use it to power websites, blogs, or portfolios from small to enterprise. This repo includes a fully-working backend, enterprise-grade admin panel, and a beautifully designed, production-ready website. This template comes configured with the bare minimum to get started on anything you need.
This template is right for you if you are working on: ## Quick start
- A personal or enterprise-grade website, blog, or portfolio This template can be deployed directly from our Cloud hosting and it will setup MongoDB and cloud S3 object storage for media.
- A content publishing platform with a fully featured publication workflow
- Exploring the capabilities of Payload
Core features: ## Quick Start - local setup
- [Pre-configured Payload Config](#how-it-works) To spin up this template locally, follow these steps:
- [Authentication](#users-authentication)
- [Access Control](#access-control)
- [Layout Builder](#layout-builder)
- [Draft Preview](#draft-preview)
- [Live Preview](#live-preview)
- [On-demand Revalidation](#on-demand-revalidation)
- [SEO](#seo)
- [Search](#search)
- [Redirects](#redirects)
- [Jobs and Scheduled Publishing](#jobs-and-scheduled-publish)
- [Website](#website)
## Quick Start
To spin up this example locally, follow these steps:
### Clone ### Clone
If you have not done so already, you need to have standalone copy of this repo on your machine. If you've already cloned this repo, skip to [Development](#development). After you click the `Deploy` button above, you'll want to have standalone copy of this repo on your machine. If you've already cloned this repo, skip to [Development](#development).
#### Method 1 (recommended)
Go to Payload Cloud and [clone this template](https://payloadcms.com/new/clone/website). This will create a new repository on your GitHub account with this template's code which you can then clone to your own machine.
#### Method 2
Use the `create-payload-app` CLI to clone this template directly to your machine:
```bash
pnpx create-payload-app my-project -t website
```
#### Method 3
Use the `git` CLI to clone this template directly to your machine:
```bash
git clone -n --depth=1 --filter=tree:0 https://github.com/payloadcms/payload my-project && cd my-project && git sparse-checkout set --no-cone templates/website && git checkout && rm -rf .git && git init && git add . && git mv -f templates/website/{.,}* . && git add . && git commit -m "Initial commit"
```
### Development ### Development
1. First [clone the repo](#clone) if you have not done so already 1. First [clone the repo](#clone) if you have not done so already
1. `cd my-project && cp .env.example .env` to copy the example environment variables 2. `cd my-project && cp .env.example .env` to copy the example environment variables. You'll need to add the `MONGODB_URI` from your Cloud project to your `.env` if you want to use S3 storage and the MongoDB database that was created for you.
1. `pnpm install && pnpm dev` to install dependencies and start the dev server
1. open `http://localhost:3000` to open the app in your browser 3. `pnpm install && pnpm dev` to install dependencies and start the dev server
4. open `http://localhost:3000` to open the app in your browser
That's it! Changes made in `./src` will be reflected in your app. Follow the on-screen instructions to login and create your first admin user. Then check out [Production](#production) once you're ready to build and serve your app, and [Deployment](#deployment) when you're ready to go live. That's it! Changes made in `./src` will be reflected in your app. Follow the on-screen instructions to login and create your first admin user. Then check out [Production](#production) once you're ready to build and serve your app, and [Deployment](#deployment) when you're ready to go live.
#### Docker (Optional)
If you prefer to use Docker for local development instead of a local MongoDB instance, the provided docker-compose.yml file can be used.
To do so, follow these steps:
- Modify the `MONGODB_URI` in your `.env` file to `mongodb://127.0.0.1/<dbname>`
- Modify the `docker-compose.yml` file's `MONGODB_URI` to match the above `<dbname>`
- Run `docker-compose up` to start the database, optionally pass `-d` to run in the background.
## How it works ## How it works
The Payload config is tailored specifically to the needs of most websites. It is pre-configured in the following ways: The Payload config is tailored specifically to the needs of most websites. It is pre-configured in the following ways:
@@ -70,161 +44,13 @@ See the [Collections](https://payloadcms.com/docs/configuration/collections) doc
- #### Users (Authentication) - #### Users (Authentication)
Users are auth-enabled collections that have access to the admin panel and unpublished content. See [Access Control](#access-control) for more details. Users are auth-enabled collections that have access to the admin panel.
For additional help, see the official [Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth) or the [Authentication](https://payloadcms.com/docs/authentication/overview#authentication-overview) docs. For additional help, see the official [Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth) or the [Authentication](https://payloadcms.com/docs/authentication/overview#authentication-overview) docs.
- #### Posts
Posts are used to generate blog posts, news articles, or any other type of content that is published over time. All posts are layout builder enabled so you can generate unique layouts for each post using layout-building blocks, see [Layout Builder](#layout-builder) for more details. Posts are also draft-enabled so you can preview them before publishing them to your website, see [Draft Preview](#draft-preview) for more details.
- #### Pages
All pages are layout builder enabled so you can generate unique layouts for each page using layout-building blocks, see [Layout Builder](#layout-builder) for more details. Pages are also draft-enabled so you can preview them before publishing them to your website, see [Draft Preview](#draft-preview) for more details.
- #### Media - #### Media
This is the uploads enabled collection used by pages, posts, and projects to contain media like images, videos, downloads, and other assets. It features pre-configured sizes, focal point and manual resizing to help you manage your pictures. This is the uploads enabled collection. It features pre-configured sizes, focal point and manual resizing to help you manage your pictures.
- #### Categories
A taxonomy used to group posts together. Categories can be nested inside of one another, for example "News > Technology". See the official [Payload Nested Docs Plugin](https://payloadcms.com/docs/plugins/nested-docs) for more details.
### Globals
See the [Globals](https://payloadcms.com/docs/configuration/globals) docs for details on how to extend this functionality.
- `Header`
The data required by the header on your front-end like nav links.
- `Footer`
Same as above but for the footer of your site.
## Access control
Basic access control is setup to limit access to various content based based on publishing status.
- `users`: Users can access the admin panel and create or edit content.
- `posts`: Everyone can access published posts, but only users can create, update, or delete them.
- `pages`: Everyone can access published pages, but only users can create, update, or delete them.
For more details on how to extend this functionality, see the [Payload Access Control](https://payloadcms.com/docs/access-control/overview#access-control) docs.
## Layout Builder
Create unique page layouts for any type of content using a powerful layout builder. This template comes pre-configured with the following layout building blocks:
- Hero
- Content
- Media
- Call To Action
- Archive
Each block is fully designed and built into the front-end website that comes with this template. See [Website](#website) for more details.
## Lexical editor
A deep editorial experience that allows complete freedom to focus just on writing content without breaking out of the flow with support for Payload blocks, media, links and other features provided out of the box. See [Lexical](https://payloadcms.com/docs/rich-text/overview) docs.
## Draft Preview
All posts and pages are draft-enabled so you can preview them before publishing them to your website. To do this, these collections use [Versions](https://payloadcms.com/docs/configuration/collections#versions) with `drafts` set to `true`. This means that when you create a new post, project, or page, it will be saved as a draft and will not be visible on your website until you publish it. This also means that you can preview your draft before publishing it to your website. To do this, we automatically format a custom URL which redirects to your front-end to securely fetch the draft version of your content.
Since the front-end of this template is statically generated, this also means that pages, posts, and projects will need to be regenerated as changes are made to published documents. To do this, we use an `afterChange` hook to regenerate the front-end when a document has changed and its `_status` is `published`.
For more details on how to extend this functionality, see the official [Draft Preview Example](https://github.com/payloadcms/payload/tree/examples/draft-preview).
## Live preview
In addition to draft previews you can also enable live preview to view your end resulting page as you're editing content with full support for SSR rendering. See [Live preview docs](https://payloadcms.com/docs/live-preview/overview) for more details.
## On-demand Revalidation
We've added hooks to collections and globals so that all of your pages, posts, footer, or header changes will automatically be updated in the frontend via on-demand revalidation supported by Nextjs.
> Note: if an image has been changed, for example it's been cropped, you will need to republish the page it's used on in order to be able to revalidate the Nextjs image cache.
## SEO
This template comes pre-configured with the official [Payload SEO Plugin](https://payloadcms.com/docs/plugins/seo) for complete SEO control from the admin panel. All SEO data is fully integrated into the front-end website that comes with this template. See [Website](#website) for more details.
## Search
This template also pre-configured with the official [Payload Search Plugin](https://payloadcms.com/docs/plugins/search) to showcase how SSR search features can easily be implemented into Next.js with Payload. See [Website](#website) for more details.
## Redirects
If you are migrating an existing site or moving content to a new URL, you can use the `redirects` collection to create a proper redirect from old URLs to new ones. This will ensure that proper request status codes are returned to search engines and that your users are not left with a broken link. This template comes pre-configured with the official [Payload Redirects Plugin](https://payloadcms.com/docs/plugins/redirects) for complete redirect control from the admin panel. All redirects are fully integrated into the front-end website that comes with this template. See [Website](#website) for more details.
## Jobs and Scheduled Publish
We have configured [Scheduled Publish](https://payloadcms.com/docs/versions/drafts#scheduled-publish) which uses the [jobs queue](https://payloadcms.com/docs/jobs-queue/jobs) in order to publish or unpublish your content on a scheduled time. The tasks are run on a cron schedule and can also be run as a separate instance if needed.
> Note: When deployed on Vercel, depending on the plan tier, you may be limited to daily cron only.
## Website
This template includes a beautifully designed, production-ready front-end built with the [Next.js App Router](https://nextjs.org), served right alongside your Payload app in a instance. This makes it so that you can deploy both your backend and website where you need it.
Core features:
- [Next.js App Router](https://nextjs.org)
- [TypeScript](https://www.typescriptlang.org)
- [React Hook Form](https://react-hook-form.com)
- [Payload Admin Bar](https://github.com/payloadcms/payload/tree/main/packages/admin-bar)
- [TailwindCSS styling](https://tailwindcss.com/)
- [shadcn/ui components](https://ui.shadcn.com/)
- User Accounts and Authentication
- Fully featured blog
- Publication workflow
- Dark mode
- Pre-made layout building blocks
- SEO
- Search
- Redirects
- Live preview
### Cache
Although Next.js includes a robust set of caching strategies out of the box, Payload Cloud proxies and caches all files through Cloudflare using the [Official Cloud Plugin](https://www.npmjs.com/package/@payloadcms/payload-cloud). This means that Next.js caching is not needed and is disabled by default. If you are hosting your app outside of Payload Cloud, you can easily reenable the Next.js caching mechanisms by removing the `no-store` directive from all fetch requests in `./src/app/_api` and then removing all instances of `export const dynamic = 'force-dynamic'` from pages files, such as `./src/app/(pages)/[slug]/page.tsx`. For more details, see the official [Next.js Caching Docs](https://nextjs.org/docs/app/building-your-application/caching).
## Development
To spin up this example locally, follow the [Quick Start](#quick-start). Then [Seed](#seed) the database with a few pages, posts, and projects.
### Working with Postgres
Postgres and other SQL-based databases follow a strict schema for managing your data. In comparison to our MongoDB adapter, this means that there's a few extra steps to working with Postgres.
Note that often times when making big schema changes you can run the risk of losing data if you're not manually migrating it.
#### Local development
Ideally we recommend running a local copy of your database so that schema updates are as fast as possible. By default the Postgres adapter has `push: true` for development environments. This will let you add, modify and remove fields and collections without needing to run any data migrations.
If your database is pointed to production you will want to set `push: false` otherwise you will risk losing data or having your migrations out of sync.
#### Migrations
[Migrations](https://payloadcms.com/docs/database/migrations) are essentially SQL code versions that keeps track of your schema. When deploy with Postgres you will need to make sure you create and then run your migrations.
Locally create a migration
```bash
pnpm payload migrate:create
```
This creates the migration files you will need to push alongside with your new configuration.
On the server after building and before running `pnpm start` you will want to run your migrations
```bash
pnpm payload migrate
```
This command will check for any migrations that have not yet been run and try to run them and it will keep a record of migrations that have been run in the database.
### Docker ### Docker
@@ -236,86 +62,6 @@ Alternatively, you can use [Docker](https://www.docker.com) to spin up this temp
That's it! The Docker instance will help you get up and running quickly while also standardizing the development environment across your teams. That's it! The Docker instance will help you get up and running quickly while also standardizing the development environment across your teams.
### Seed
To seed the database with a few pages, posts, and projects you can click the 'seed database' link from the admin panel.
The seed script will also create a demo user for demonstration purposes only:
- Demo Author
- Email: `demo-author@payloadcms.com`
- Password: `password`
> NOTICE: seeding the database is destructive because it drops your current database to populate a fresh one from the seed template. Only run this command if you are starting a new project or can afford to lose your current data.
## Production
To run Payload in production, you need to build and start the Admin panel. To do so, follow these steps:
1. Invoke the `next build` script by running `pnpm build` or `npm run build` in your project root. This creates a `.next` directory with a production-ready admin bundle.
1. Finally run `pnpm start` or `npm run start` to run Node in production and serve Payload from the `.build` directory.
1. When you're ready to go live, see Deployment below for more details.
### Deploying to Payload Cloud
The easiest way to deploy your project is to use [Payload Cloud](https://payloadcms.com/new/import), a one-click hosting solution to deploy production-ready instances of your Payload apps directly from your GitHub repo.
### Deploying to Vercel
This template can also be deployed to Vercel for free. You can get started by choosing the Vercel DB adapter during the setup of the template or by manually installing and configuring it:
```bash
pnpm add @payloadcms/db-vercel-postgres
```
```ts
// payload.config.ts
import { vercelPostgresAdapter } from '@payloadcms/db-vercel-postgres'
export default buildConfig({
// ...
db: vercelPostgresAdapter({
pool: {
connectionString: process.env.POSTGRES_URL || '',
},
}),
// ...
```
We also support Vercel's blob storage:
```bash
pnpm add @payloadcms/storage-vercel-blob
```
```ts
// payload.config.ts
import { vercelBlobStorage } from '@payloadcms/storage-vercel-blob'
export default buildConfig({
// ...
plugins: [
vercelBlobStorage({
collections: {
[Media.slug]: true,
},
token: process.env.BLOB_READ_WRITE_TOKEN || '',
}),
],
// ...
```
There is also a simplified [one click deploy](https://github.com/payloadcms/payload/tree/templates/with-vercel-postgres) to Vercel should you need it.
### Self-hosting
Before deploying your app, you need to:
1. Ensure your app builds and serves in production. See [Production](#production) for more details.
2. You can then deploy Payload as you would any other Node.js or Next.js application either directly on a VPS, DigitalOcean's Apps Platform, via Coolify or more. More guides coming soon.
You can also deploy your app manually, check out the [deployment documentation](https://payloadcms.com/docs/production/deployment) for full details.
## Questions ## Questions
If you have any issues or questions, reach out to us on [Discord](https://discord.com/invite/payload) or start a [GitHub discussion](https://github.com/payloadcms/payload/discussions). If you have any issues or questions, reach out to us on [Discord](https://discord.com/invite/payload) or start a [GitHub discussion](https://github.com/payloadcms/payload/discussions).

2146
bun.lock Normal file

File diff suppressed because it is too large Load Diff

43
docker-compose.yml Normal file
View File

@@ -0,0 +1,43 @@
version: '3'
services:
payload:
image: node:18-alpine
ports:
- '3000:3000'
volumes:
- .:/home/node/app
- node_modules:/home/node/app/node_modules
working_dir: /home/node/app/
command: sh -c "corepack enable && corepack prepare pnpm@latest --activate && pnpm install && pnpm dev"
depends_on:
- mongo
# - postgres
env_file:
- .env
# Ensure your DATABASE_URI uses 'mongo' as the hostname ie. mongodb://mongo/my-db-name
mongo:
image: mongo:latest
ports:
- '27017:27017'
command:
- --storageEngine=wiredTiger
volumes:
- data:/data/db
logging:
driver: none
# Uncomment the following to use postgres
# postgres:
# restart: always
# image: postgres:latest
# volumes:
# - pgdata:/var/lib/postgresql/data
# ports:
# - "5432:5432"
volumes:
data:
# pgdata:
node_modules:

38
eslint.config.mjs Normal file
View File

@@ -0,0 +1,38 @@
import { dirname } from 'path'
import { fileURLToPath } from 'url'
import { FlatCompat } from '@eslint/eslintrc'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const compat = new FlatCompat({
baseDirectory: __dirname,
})
const eslintConfig = [
...compat.extends('next/core-web-vitals', 'next/typescript'),
{
rules: {
'@typescript-eslint/ban-ts-comment': 'warn',
'@typescript-eslint/no-empty-object-type': 'warn',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': [
'warn',
{
vars: 'all',
args: 'after-used',
ignoreRestSiblings: false,
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
destructuredArrayIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^(_|ignore)',
},
],
},
},
{
ignores: ['.next/'],
},
]
export default eslintConfig

17
next.config.mjs Normal file
View File

@@ -0,0 +1,17 @@
import { withPayload } from '@payloadcms/next/withPayload'
/** @type {import('next').NextConfig} */
const nextConfig = {
// Your Next.js config here
webpack: (webpackConfig) => {
webpackConfig.resolve.extensionAlias = {
'.cjs': ['.cts', '.cjs'],
'.js': ['.ts', '.tsx', '.js', '.jsx'],
'.mjs': ['.mts', '.mjs'],
}
return webpackConfig
},
}
export default withPayload(nextConfig, { devBundleServerPackages: false })

View File

@@ -1,81 +1,50 @@
{ {
"name": "", "name": "",
"version": "1.0.0", "version": "1.0.0",
"description": "Website template for Payload", "description": "A blank template to get started with Payload 3.0",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"scripts": { "scripts": {
"build": "cross-env NODE_OPTIONS=--no-deprecation next build", "build": "cross-env NODE_OPTIONS=\"--no-deprecation --max-old-space-size=8000\" next build",
"postbuild": "next-sitemap --config next-sitemap.config.cjs",
"dev": "cross-env NODE_OPTIONS=--no-deprecation next dev", "dev": "cross-env NODE_OPTIONS=--no-deprecation next dev",
"dev:prod": "cross-env NODE_OPTIONS=--no-deprecation rm -rf .next && pnpm build && pnpm start", "devsafe": "rm -rf .next && cross-env NODE_OPTIONS=--no-deprecation next dev",
"generate:importmap": "cross-env NODE_OPTIONS=--no-deprecation payload generate:importmap", "generate:importmap": "cross-env NODE_OPTIONS=--no-deprecation payload generate:importmap",
"generate:types": "cross-env NODE_OPTIONS=--no-deprecation payload generate:types", "generate:types": "cross-env NODE_OPTIONS=--no-deprecation payload generate:types",
"ii": "cross-env NODE_OPTIONS=--no-deprecation pnpm --ignore-workspace install",
"lint": "cross-env NODE_OPTIONS=--no-deprecation next lint", "lint": "cross-env NODE_OPTIONS=--no-deprecation next lint",
"lint:fix": "cross-env NODE_OPTIONS=--no-deprecation next lint --fix",
"payload": "cross-env NODE_OPTIONS=--no-deprecation payload", "payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
"reinstall": "cross-env NODE_OPTIONS=--no-deprecation rm -rf node_modules && rm pnpm-lock.yaml && pnpm --ignore-workspace install",
"start": "cross-env NODE_OPTIONS=--no-deprecation next start", "start": "cross-env NODE_OPTIONS=--no-deprecation next start",
"test": "pnpm run test:int && pnpm run test:e2e", "test": "pnpm run test:int && pnpm run test:e2e",
"test:e2e": "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" pnpm exec playwright test --config=playwright.config.ts", "test:e2e": "cross-env NODE_OPTIONS=\"--no-deprecation --no-experimental-strip-types\" pnpm exec playwright test",
"test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts" "test:int": "cross-env NODE_OPTIONS=--no-deprecation vitest run --config ./vitest.config.mts"
}, },
"dependencies": { "dependencies": {
"@payloadcms/admin-bar": "3.59.1",
"@payloadcms/live-preview-react": "3.59.1",
"@payloadcms/next": "3.59.1", "@payloadcms/next": "3.59.1",
"@payloadcms/payload-cloud": "3.59.1", "@payloadcms/payload-cloud": "3.59.1",
"@payloadcms/plugin-form-builder": "3.59.1",
"@payloadcms/plugin-nested-docs": "3.59.1",
"@payloadcms/plugin-redirects": "3.59.1",
"@payloadcms/plugin-search": "3.59.1",
"@payloadcms/plugin-seo": "3.59.1",
"@payloadcms/richtext-lexical": "3.59.1", "@payloadcms/richtext-lexical": "3.59.1",
"@payloadcms/ui": "3.59.1", "@payloadcms/ui": "3.59.1",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"dotenv": "16.4.7", "dotenv": "16.4.7",
"geist": "^1.3.0", "graphql": "^16.8.1",
"graphql": "^16.8.2",
"lucide-react": "^0.378.0",
"next": "15.4.4", "next": "15.4.4",
"next-sitemap": "^4.2.3",
"payload": "3.59.1", "payload": "3.59.1",
"prism-react-renderer": "^2.3.1",
"react": "19.1.0", "react": "19.1.0",
"react-dom": "19.1.0", "react-dom": "19.1.0",
"react-hook-form": "7.45.4",
"sharp": "0.34.2", "sharp": "0.34.2",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
"@payloadcms/db-postgres": "3.59.1" "@payloadcms/db-postgres": "3.59.1"
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "^3.2.0",
"@playwright/test": "1.54.1", "@playwright/test": "1.54.1",
"@tailwindcss/typography": "^0.5.13",
"@testing-library/react": "16.3.0", "@testing-library/react": "16.3.0",
"@types/escape-html": "^1.0.2", "@types/node": "^22.5.4",
"@types/node": "22.5.4",
"@types/react": "19.1.8", "@types/react": "19.1.8",
"@types/react-dom": "19.1.6", "@types/react-dom": "19.1.6",
"@vitejs/plugin-react": "4.5.2", "@vitejs/plugin-react": "4.5.2",
"autoprefixer": "^10.4.19",
"copyfiles": "^2.4.1",
"eslint": "^9.16.0", "eslint": "^9.16.0",
"eslint-config-next": "15.4.4", "eslint-config-next": "15.4.4",
"jsdom": "26.1.0", "jsdom": "26.1.0",
"playwright": "1.54.1", "playwright": "1.54.1",
"playwright-core": "1.54.1", "playwright-core": "1.54.1",
"postcss": "^8.4.38",
"prettier": "^3.4.2", "prettier": "^3.4.2",
"tailwindcss": "^3.4.3",
"typescript": "5.7.3", "typescript": "5.7.3",
"vite-tsconfig-paths": "5.1.4", "vite-tsconfig-paths": "5.1.4",
"vitest": "3.2.3" "vitest": "3.2.3"

41
playwright.config.ts Normal file
View File

@@ -0,0 +1,41 @@
import { defineConfig, devices } from '@playwright/test'
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
import 'dotenv/config'
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests/e2e',
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
// baseURL: 'http://localhost:3000',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
webServer: {
command: 'pnpm dev',
reuseExistingServer: true,
url: 'http://localhost:3000',
},
})

View File

@@ -0,0 +1,19 @@
import React from 'react'
import './styles.css'
export const metadata = {
description: 'A blank template using Payload in a Next.js app.',
title: 'Payload Blank Template',
}
export default async function RootLayout(props: { children: React.ReactNode }) {
const { children } = props
return (
<html lang="en">
<body>
<main>{children}</main>
</body>
</html>
)
}

View File

@@ -0,0 +1,59 @@
import { headers as getHeaders } from 'next/headers.js'
import Image from 'next/image'
import { getPayload } from 'payload'
import React from 'react'
import { fileURLToPath } from 'url'
import config from '@/payload.config'
import './styles.css'
export default async function HomePage() {
const headers = await getHeaders()
const payloadConfig = await config
const payload = await getPayload({ config: payloadConfig })
const { user } = await payload.auth({ headers })
const fileURL = `vscode://file/${fileURLToPath(import.meta.url)}`
return (
<div className="home">
<div className="content">
<picture>
<source srcSet="https://raw.githubusercontent.com/payloadcms/payload/main/packages/ui/src/assets/payload-favicon.svg" />
<Image
alt="Payload Logo"
height={65}
src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/ui/src/assets/payload-favicon.svg"
width={65}
/>
</picture>
{!user && <h1>Welcome to your new project.</h1>}
{user && <h1>Welcome back, {user.email}</h1>}
<div className="links">
<a
className="admin"
href={payloadConfig.routes.admin}
rel="noopener noreferrer"
target="_blank"
>
Go to admin panel
</a>
<a
className="docs"
href="https://payloadcms.com/docs"
rel="noopener noreferrer"
target="_blank"
>
Documentation
</a>
</div>
</div>
<div className="footer">
<p>Update this page by editing</p>
<a className="codeLink" href={fileURL}>
<code>app/(frontend)/page.tsx</code>
</a>
</div>
</div>
)
}

View File

@@ -0,0 +1,164 @@
:root {
--font-mono: 'Roboto Mono', monospace;
}
* {
box-sizing: border-box;
}
html {
font-size: 18px;
line-height: 32px;
background: rgb(0, 0, 0);
-webkit-font-smoothing: antialiased;
}
html,
body,
#app {
height: 100%;
}
body {
font-family: system-ui;
font-size: 18px;
line-height: 32px;
margin: 0;
color: rgb(1000, 1000, 1000);
@media (max-width: 1024px) {
font-size: 15px;
line-height: 24px;
}
}
img {
max-width: 100%;
height: auto;
display: block;
}
h1 {
margin: 40px 0;
font-size: 64px;
line-height: 70px;
font-weight: bold;
@media (max-width: 1024px) {
margin: 24px 0;
font-size: 42px;
line-height: 42px;
}
@media (max-width: 768px) {
font-size: 38px;
line-height: 38px;
}
@media (max-width: 400px) {
font-size: 32px;
line-height: 32px;
}
}
p {
margin: 24px 0;
@media (max-width: 1024px) {
margin: calc(var(--base) * 0.75) 0;
}
}
a {
color: currentColor;
&:focus {
opacity: 0.8;
outline: none;
}
&:active {
opacity: 0.7;
outline: none;
}
}
svg {
vertical-align: middle;
}
.home {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
height: 100vh;
padding: 45px;
max-width: 1024px;
margin: 0 auto;
overflow: hidden;
@media (max-width: 400px) {
padding: 24px;
}
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex-grow: 1;
h1 {
text-align: center;
}
}
.links {
display: flex;
align-items: center;
gap: 12px;
a {
text-decoration: none;
padding: 0.25rem 0.5rem;
border-radius: 4px;
}
.admin {
color: rgb(0, 0, 0);
background: rgb(1000, 1000, 1000);
border: 1px solid rgb(0, 0, 0);
}
.docs {
color: rgb(1000, 1000, 1000);
background: rgb(0, 0, 0);
border: 1px solid rgb(1000, 1000, 1000);
}
}
.footer {
display: flex;
align-items: center;
gap: 8px;
@media (max-width: 1024px) {
flex-direction: column;
gap: 6px;
}
p {
margin: 0;
}
.codeLink {
text-decoration: none;
padding: 0 0.5rem;
background: rgb(60, 60, 60);
border-radius: 4px;
}
}
}

View File

@@ -0,0 +1,24 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views'
import { importMap } from '../importMap'
type Args = {
params: Promise<{
segments: string[]
}>
searchParams: Promise<{
[key: string]: string | string[]
}>
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams })
const NotFound = ({ params, searchParams }: Args) =>
NotFoundPage({ config, params, searchParams, importMap })
export default NotFound

View File

@@ -0,0 +1,24 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import type { Metadata } from 'next'
import config from '@payload-config'
import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
import { importMap } from '../importMap'
type Args = {
params: Promise<{
segments: string[]
}>
searchParams: Promise<{
[key: string]: string | string[]
}>
}
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
generatePageMetadata({ config, params, searchParams })
const Page = ({ params, searchParams }: Args) =>
RootPage({ config, params, searchParams, importMap })
export default Page

View File

@@ -0,0 +1 @@
export const importMap = {}

View File

@@ -0,0 +1,19 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import '@payloadcms/next/css'
import {
REST_DELETE,
REST_GET,
REST_OPTIONS,
REST_PATCH,
REST_POST,
REST_PUT,
} from '@payloadcms/next/routes'
export const GET = REST_GET(config)
export const POST = REST_POST(config)
export const DELETE = REST_DELETE(config)
export const PATCH = REST_PATCH(config)
export const PUT = REST_PUT(config)
export const OPTIONS = REST_OPTIONS(config)

View File

@@ -0,0 +1,7 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import '@payloadcms/next/css'
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'
export const GET = GRAPHQL_PLAYGROUND_GET(config)

View File

@@ -0,0 +1,8 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import { GRAPHQL_POST, REST_OPTIONS } from '@payloadcms/next/routes'
export const POST = GRAPHQL_POST(config)
export const OPTIONS = REST_OPTIONS(config)

View File

View File

@@ -0,0 +1,31 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
import config from '@payload-config'
import '@payloadcms/next/css'
import type { ServerFunctionClient } from 'payload'
import { handleServerFunctions, RootLayout } from '@payloadcms/next/layouts'
import React from 'react'
import { importMap } from './admin/importMap.js'
import './custom.scss'
type Args = {
children: React.ReactNode
}
const serverFunction: ServerFunctionClient = async function (args) {
'use server'
return handleServerFunctions({
...args,
config,
importMap,
})
}
const Layout = ({ children }: Args) => (
<RootLayout config={config} importMap={importMap} serverFunction={serverFunction}>
{children}
</RootLayout>
)
export default Layout

12
src/app/my-route/route.ts Normal file
View File

@@ -0,0 +1,12 @@
import configPromise from '@payload-config'
import { getPayload } from 'payload'
export const GET = async (request: Request) => {
const payload = await getPayload({
config: configPromise,
})
return Response.json({
message: 'This is an example of a custom route.',
})
}

16
src/collections/Media.ts Normal file
View File

@@ -0,0 +1,16 @@
import type { CollectionConfig } from 'payload'
export const Media: CollectionConfig = {
slug: 'media',
access: {
read: () => true,
},
fields: [
{
name: 'alt',
type: 'text',
required: true,
},
],
upload: true,
}

13
src/collections/Users.ts Normal file
View File

@@ -0,0 +1,13 @@
import type { CollectionConfig } from 'payload'
export const Users: CollectionConfig = {
slug: 'users',
admin: {
useAsTitle: 'email',
},
auth: true,
fields: [
// Email added by default
// Add more fields as needed
],
}

299
src/payload-types.ts Normal file
View File

@@ -0,0 +1,299 @@
/* tslint:disable */
/* eslint-disable */
/**
* This file was automatically generated by Payload.
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
* and re-run `payload generate:types` to regenerate this file.
*/
/**
* Supported timezones in IANA format.
*
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "supportedTimezones".
*/
export type SupportedTimezones =
| 'Pacific/Midway'
| 'Pacific/Niue'
| 'Pacific/Honolulu'
| 'Pacific/Rarotonga'
| 'America/Anchorage'
| 'Pacific/Gambier'
| 'America/Los_Angeles'
| 'America/Tijuana'
| 'America/Denver'
| 'America/Phoenix'
| 'America/Chicago'
| 'America/Guatemala'
| 'America/New_York'
| 'America/Bogota'
| 'America/Caracas'
| 'America/Santiago'
| 'America/Buenos_Aires'
| 'America/Sao_Paulo'
| 'Atlantic/South_Georgia'
| 'Atlantic/Azores'
| 'Atlantic/Cape_Verde'
| 'Europe/London'
| 'Europe/Berlin'
| 'Africa/Lagos'
| 'Europe/Athens'
| 'Africa/Cairo'
| 'Europe/Moscow'
| 'Asia/Riyadh'
| 'Asia/Dubai'
| 'Asia/Baku'
| 'Asia/Karachi'
| 'Asia/Tashkent'
| 'Asia/Calcutta'
| 'Asia/Dhaka'
| 'Asia/Almaty'
| 'Asia/Jakarta'
| 'Asia/Bangkok'
| 'Asia/Shanghai'
| 'Asia/Singapore'
| 'Asia/Tokyo'
| 'Asia/Seoul'
| 'Australia/Brisbane'
| 'Australia/Sydney'
| 'Pacific/Guam'
| 'Pacific/Noumea'
| 'Pacific/Auckland'
| 'Pacific/Fiji';
export interface Config {
auth: {
users: UserAuthOperations;
};
blocks: {};
collections: {
users: User;
media: Media;
'payload-locked-documents': PayloadLockedDocument;
'payload-preferences': PayloadPreference;
'payload-migrations': PayloadMigration;
};
collectionsJoins: {};
collectionsSelect: {
users: UsersSelect<false> | UsersSelect<true>;
media: MediaSelect<false> | MediaSelect<true>;
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
};
db: {
defaultIDType: string;
};
globals: {};
globalsSelect: {};
locale: null;
user: User & {
collection: 'users';
};
jobs: {
tasks: unknown;
workflows: unknown;
};
}
export interface UserAuthOperations {
forgotPassword: {
email: string;
password: string;
};
login: {
email: string;
password: string;
};
registerFirstUser: {
email: string;
password: string;
};
unlock: {
email: string;
password: string;
};
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users".
*/
export interface User {
id: string;
updatedAt: string;
createdAt: string;
email: string;
resetPasswordToken?: string | null;
resetPasswordExpiration?: string | null;
salt?: string | null;
hash?: string | null;
loginAttempts?: number | null;
lockUntil?: string | null;
sessions?:
| {
id: string;
createdAt?: string | null;
expiresAt: string;
}[]
| null;
password?: string | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media".
*/
export interface Media {
id: string;
alt: string;
updatedAt: string;
createdAt: string;
url?: string | null;
thumbnailURL?: string | null;
filename?: string | null;
mimeType?: string | null;
filesize?: number | null;
width?: number | null;
height?: number | null;
focalX?: number | null;
focalY?: number | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-locked-documents".
*/
export interface PayloadLockedDocument {
id: string;
document?:
| ({
relationTo: 'users';
value: string | User;
} | null)
| ({
relationTo: 'media';
value: string | Media;
} | null);
globalSlug?: string | null;
user: {
relationTo: 'users';
value: string | User;
};
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences".
*/
export interface PayloadPreference {
id: string;
user: {
relationTo: 'users';
value: string | User;
};
key?: string | null;
value?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-migrations".
*/
export interface PayloadMigration {
id: string;
name?: string | null;
batch?: number | null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users_select".
*/
export interface UsersSelect<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
email?: T;
resetPasswordToken?: T;
resetPasswordExpiration?: T;
salt?: T;
hash?: T;
loginAttempts?: T;
lockUntil?: T;
sessions?:
| T
| {
id?: T;
createdAt?: T;
expiresAt?: T;
};
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media_select".
*/
export interface MediaSelect<T extends boolean = true> {
alt?: T;
updatedAt?: T;
createdAt?: T;
url?: T;
thumbnailURL?: T;
filename?: T;
mimeType?: T;
filesize?: T;
width?: T;
height?: T;
focalX?: T;
focalY?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-locked-documents_select".
*/
export interface PayloadLockedDocumentsSelect<T extends boolean = true> {
document?: T;
globalSlug?: T;
user?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences_select".
*/
export interface PayloadPreferencesSelect<T extends boolean = true> {
user?: T;
key?: T;
value?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-migrations_select".
*/
export interface PayloadMigrationsSelect<T extends boolean = true> {
name?: T;
batch?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "auth".
*/
export interface Auth {
[k: string]: unknown;
}
declare module 'payload' {
export interface GeneratedTypes extends Config {}
}

39
src/payload.config.ts Normal file
View File

@@ -0,0 +1,39 @@
// storage-adapter-import-placeholder
import { postgresAdapter } from '@payloadcms/db-postgres'
import { payloadCloudPlugin } from '@payloadcms/payload-cloud'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
import path from 'path'
import { buildConfig } from 'payload'
import { fileURLToPath } from 'url'
import sharp from 'sharp'
import { Users } from './collections/Users'
import { Media } from './collections/Media'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export default buildConfig({
admin: {
user: Users.slug,
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [Users, Media],
editor: lexicalEditor(),
secret: process.env.PAYLOAD_SECRET || '',
typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'),
},
db: postgresAdapter({
pool: {
connectionString: process.env.DATABASE_URI || '',
},
}),
sharp,
plugins: [
payloadCloudPlugin(),
// storage-adapter-placeholder
],
})

1
test.env Normal file
View File

@@ -0,0 +1 @@
NODE_OPTIONS="--no-deprecation --no-experimental-strip-types"

View File

@@ -0,0 +1,20 @@
import { test, expect, Page } from '@playwright/test'
test.describe('Frontend', () => {
let page: Page
test.beforeAll(async ({ browser }, testInfo) => {
const context = await browser.newContext()
page = await context.newPage()
})
test('can go on homepage', async ({ page }) => {
await page.goto('http://localhost:3000')
await expect(page).toHaveTitle(/Payload Blank Template/)
const heading = page.locator('h1').first()
await expect(heading).toHaveText('Welcome to your new project.')
})
})

20
tests/int/api.int.spec.ts Normal file
View File

@@ -0,0 +1,20 @@
import { getPayload, Payload } from 'payload'
import config from '@/payload.config'
import { describe, it, beforeAll, expect } from 'vitest'
let payload: Payload
describe('API', () => {
beforeAll(async () => {
const payloadConfig = await config
payload = await getPayload({ config: payloadConfig })
})
it('fetches users', async () => {
const users = await payload.find({
collection: 'users',
})
expect(users).toBeDefined()
})
})

View File

@@ -1,9 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"strict": true,
"baseUrl": ".", "baseUrl": ".",
"esModuleInterop": true,
"target": "ES2022",
"lib": [ "lib": [
"DOM", "DOM",
"DOM.Iterable", "DOM.Iterable",
@@ -11,39 +8,35 @@
], ],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": true,
"noEmit": true, "noEmit": true,
"incremental": true, "esModuleInterop": true,
"jsx": "preserve",
"module": "esnext", "module": "esnext",
"moduleResolution": "bundler", "moduleResolution": "bundler",
"resolveJsonModule": true, "resolveJsonModule": true,
"sourceMap": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [ "plugins": [
{ {
"name": "next" "name": "next"
} }
], ],
"paths": { "paths": {
"@payload-config": [
"./src/payload.config.ts"
],
"react": [
"./node_modules/@types/react"
],
"@/*": [ "@/*": [
"./src/*" "./src/*"
], ],
} "@payload-config": [
"./src/payload.config.ts"
]
},
"target": "ES2022",
}, },
"include": [ "include": [
"next-env.d.ts",
"**/*.ts", "**/*.ts",
"**/*.tsx", "**/*.tsx",
".next/types/**/*.ts", ".next/types/**/*.ts"
"redirects.js",
"next-env.d.ts",
"next.config.js",
"next-sitemap.config.cjs"
], ],
"exclude": [ "exclude": [
"node_modules" "node_modules"

12
vitest.config.mts Normal file
View File

@@ -0,0 +1,12 @@
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig({
plugins: [tsconfigPaths(), react()],
test: {
environment: 'jsdom',
setupFiles: ['./vitest.setup.ts'],
include: ['tests/int/**/*.int.spec.ts'],
},
})

4
vitest.setup.ts Normal file
View File

@@ -0,0 +1,4 @@
// Any setup scripts you might need go here
// Load .env files
import 'dotenv/config'