Compare commits
74 Commits
v3.0.0-bet
...
v3.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e0dfd410d | ||
|
|
8506385ef9 | ||
|
|
e74952902e | ||
|
|
4a51f4d2c1 | ||
|
|
2c283bcc08 | ||
|
|
a2e9bcd333 | ||
|
|
33d53121a2 | ||
|
|
e0b201c810 | ||
|
|
a8000f644f | ||
|
|
7d0e909a30 | ||
|
|
b2662eeb1f | ||
|
|
0b274dd67e | ||
|
|
2ddd50edc4 | ||
|
|
0287acb8f0 | ||
|
|
10c94b3665 | ||
|
|
ea48ca377e | ||
|
|
6f5d86ed84 | ||
|
|
c383f391e3 | ||
|
|
8a91a7adbb | ||
|
|
96181d91a6 | ||
|
|
eff5129a5f | ||
|
|
38e5adc462 | ||
|
|
ff4ea1eecc | ||
|
|
dbfd1beed5 | ||
|
|
4b6774463e | ||
|
|
cb14b97a6e | ||
|
|
18bc4b708c | ||
|
|
6d951e6987 | ||
|
|
365660764d | ||
|
|
8b91af8a5b | ||
|
|
b4092f59ae | ||
|
|
7a768144ea | ||
|
|
3839eb5ab0 | ||
|
|
fd02bee0fe | ||
|
|
42222cd2f6 | ||
|
|
e3222f2ac3 | ||
|
|
35f961fecb | ||
|
|
85bfca79ef | ||
|
|
661a4a099d | ||
|
|
72c0534008 | ||
|
|
78579ed2bd | ||
|
|
7bcb4ba1cc | ||
|
|
6b45cf3197 | ||
|
|
73d0b209d7 | ||
|
|
c93752bdbb | ||
|
|
7a4dd5890e | ||
|
|
60ee55fcaa | ||
|
|
1fe9790d0d | ||
|
|
3c0853a675 | ||
|
|
2b941b7e2c | ||
|
|
db772a058c | ||
|
|
0bfbf9c750 | ||
|
|
5c7647f45b | ||
|
|
6c952875e8 | ||
|
|
af7e12aa2f | ||
|
|
bcc506b423 | ||
|
|
3d7c8277d7 | ||
|
|
a8a2dc2347 | ||
|
|
6c74b0326b | ||
|
|
f51af92491 | ||
|
|
77528a1e7d | ||
|
|
ba8b8e8330 | ||
|
|
23f9a32a99 | ||
|
|
0190eb8b28 | ||
|
|
f482fdcfd5 | ||
|
|
ed4766188d | ||
|
|
e682cb1b04 | ||
|
|
36fda30c61 | ||
|
|
fa7cc376d1 | ||
|
|
3fc2ff1ef9 | ||
|
|
1d81eef805 | ||
|
|
8fcfac61b5 | ||
|
|
0d544dacdb | ||
|
|
147b50e719 |
50
.github/workflows/label-author.yml
vendored
Normal file
50
.github/workflows/label-author.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
name: label-author
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
debug-context:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: View context attributes
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: console.log(context)
|
||||
|
||||
label-created-by:
|
||||
name: Label pr/issue on opening
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Tag with 'created-by'
|
||||
uses: actions/github-script@v7
|
||||
if: github.event.action == 'opened'
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const type = context.payload.pull_request ? 'pull_request' : 'issue';
|
||||
const association = context.payload[type].author_association;
|
||||
let label = ''
|
||||
if (association === 'MEMBER' || association === 'OWNER') {
|
||||
label = 'created-by: Payload team';
|
||||
} else if (association === 'CONTRIBUTOR') {
|
||||
label = 'created-by: Contributor';
|
||||
}
|
||||
|
||||
if (!label) return;
|
||||
|
||||
github.rest.issues.addLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: [label],
|
||||
});
|
||||
console.log('Added created-by: Payload team label');
|
||||
16
.github/workflows/main.yml
vendored
16
.github/workflows/main.yml
vendored
@@ -289,7 +289,8 @@ jobs:
|
||||
suite:
|
||||
- _community
|
||||
- access-control
|
||||
- admin
|
||||
- admin__e2e__1
|
||||
- admin__e2e__2
|
||||
- auth
|
||||
- field-error-states
|
||||
- fields-relationship
|
||||
@@ -298,7 +299,14 @@ jobs:
|
||||
- fields__collections__Array
|
||||
- fields__collections__Relationship
|
||||
- fields__collections__RichText
|
||||
- fields__collections__Lexical
|
||||
- fields__collections__Lexical__e2e__main
|
||||
- fields__collections__Lexical__e2e__blocks
|
||||
- fields__collections__Date
|
||||
- fields__collections__Number
|
||||
- fields__collections__Point
|
||||
- fields__collections__Tabs
|
||||
- fields__collections__Text
|
||||
- fields__collections__Upload
|
||||
- live-preview
|
||||
- localization
|
||||
- i18n
|
||||
@@ -419,8 +427,8 @@ jobs:
|
||||
cd templates/blank-3.0
|
||||
cp .env.example .env
|
||||
ls -la
|
||||
pnpm add ./*.tgz
|
||||
pnpm install --ignore-workspace
|
||||
pnpm add ./*.tgz --ignore-workspace
|
||||
pnpm install --ignore-workspace --no-frozen-lockfile
|
||||
cat package.json
|
||||
pnpm run build
|
||||
|
||||
|
||||
9
.swcrc
9
.swcrc
@@ -7,6 +7,15 @@
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
@@ -16,6 +16,9 @@ export default withBundleAnalyzer(
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
},
|
||||
experimental: {
|
||||
reactCompiler: false
|
||||
},
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
|
||||
22
package.json
22
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload-monorepo",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -102,7 +102,8 @@
|
||||
"@types/node": "20.12.5",
|
||||
"@types/prompts": "^2.4.5",
|
||||
"@types/qs": "6.9.14",
|
||||
"@types/react": "18.3.2",
|
||||
"@types/react": "npm:types-react@19.0.0-beta.2",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
|
||||
"@types/semver": "^7.5.3",
|
||||
"@types/shelljs": "0.8.15",
|
||||
"add-stream": "^1.0.0",
|
||||
@@ -128,11 +129,10 @@
|
||||
"jest": "29.7.0",
|
||||
"jest-environment-jsdom": "29.7.0",
|
||||
"jwt-decode": "4.0.0",
|
||||
"lexical": "0.15.0",
|
||||
"lint-staged": "^14.0.1",
|
||||
"minimist": "1.2.8",
|
||||
"mongodb-memory-server": "^9.0",
|
||||
"next": "14.3.0-canary.68",
|
||||
"next": "15.0.0-rc.0",
|
||||
"node-mocks-http": "^1.14.1",
|
||||
"nodemon": "3.0.3",
|
||||
"open": "^10.1.0",
|
||||
@@ -144,8 +144,8 @@
|
||||
"prettier": "^3.0.3",
|
||||
"prompts": "2.4.2",
|
||||
"qs": "6.11.2",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react": "^19.0.0-rc-f994737d14-20240522",
|
||||
"react-dom": "^19.0.0-rc-f994737d14-20240522",
|
||||
"read-stream": "^2.1.1",
|
||||
"rimraf": "3.0.2",
|
||||
"semver": "^7.5.4",
|
||||
@@ -166,8 +166,8 @@
|
||||
"yocto-queue": "^1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0 || ^19.0.0",
|
||||
"react-dom": "^18.2.0 || ^19.0.0"
|
||||
"react": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522",
|
||||
"react-dom": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.20.2",
|
||||
@@ -180,6 +180,8 @@
|
||||
"uuid": "3.4.0"
|
||||
},
|
||||
"overrides": {
|
||||
"@types/react": "npm:types-react@19.0.0-beta.2",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
|
||||
"copyfiles": "$copyfiles",
|
||||
"cross-env": "$cross-env",
|
||||
"dotenv": "$dotenv",
|
||||
@@ -194,6 +196,10 @@
|
||||
"playwright@1.43.0": "patches/playwright@1.43.0.patch"
|
||||
}
|
||||
},
|
||||
"overrides": {
|
||||
"@types/react": "npm:types-react@19.0.0-beta.2",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
|
||||
},
|
||||
"workspaces:": [
|
||||
"packages/*",
|
||||
"test/*"
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-payload-app",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -41,7 +41,6 @@
|
||||
"command-exists": "^1.2.9",
|
||||
"comment-json": "^4.2.3",
|
||||
"degit": "^2.8.4",
|
||||
"detect-package-manager": "^3.0.1",
|
||||
"esprima-next": "^6.0.3",
|
||||
"execa": "^5.0.0",
|
||||
"figures": "^6.1.0",
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('createProject', () => {
|
||||
const args = {
|
||||
_: ['project-name'],
|
||||
'--db': 'mongodb',
|
||||
'--local-template': 'blank',
|
||||
'--local-template': 'blank-3.0',
|
||||
'--no-deps': true,
|
||||
} as CliArgs
|
||||
const packageManager = 'yarn'
|
||||
|
||||
@@ -9,7 +9,7 @@ import path from 'path'
|
||||
import type { CliArgs, DbDetails, PackageManager, ProjectTemplate } from '../types.js'
|
||||
|
||||
import { tryInitRepoAndCommit } from '../utils/git.js'
|
||||
import { debug, error, warning } from '../utils/log.js'
|
||||
import { debug, error, info, warning } from '../utils/log.js'
|
||||
import { configurePayloadConfig } from './configure-payload-config.js'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
@@ -99,6 +99,7 @@ export async function createProject(args: {
|
||||
}
|
||||
|
||||
if (!cliArgs['--no-deps']) {
|
||||
info(`Using ${packageManager}.\n`)
|
||||
spinner.message('Installing dependencies...')
|
||||
const result = await installDeps({ cliArgs, packageManager, projectDir })
|
||||
if (result) {
|
||||
|
||||
29
packages/create-payload-app/src/lib/get-package-manager.ts
Normal file
29
packages/create-payload-app/src/lib/get-package-manager.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import commandExists from 'command-exists'
|
||||
import fse from 'fs-extra'
|
||||
|
||||
import type { CliArgs, PackageManager } from '../types.js'
|
||||
|
||||
export async function getPackageManager(args: {
|
||||
cliArgs?: CliArgs
|
||||
projectDir: string
|
||||
}): Promise<PackageManager> {
|
||||
const { cliArgs, projectDir } = args
|
||||
|
||||
// Check for yarn.lock, package-lock.json, or pnpm-lock.yaml
|
||||
let detected: PackageManager = 'npm'
|
||||
if (
|
||||
cliArgs?.['--use-pnpm'] ||
|
||||
fse.existsSync(`${projectDir}/pnpm-lock.yaml`) ||
|
||||
(await commandExists('pnpm'))
|
||||
) {
|
||||
detected = 'pnpm'
|
||||
} else if (
|
||||
(cliArgs?.['--use-yarn'] && fse.existsSync(`${projectDir}/yarn.lock`)) ||
|
||||
(await commandExists('yarn'))
|
||||
) {
|
||||
detected = 'yarn'
|
||||
} else if (cliArgs?.['--use-npm'] && fse.existsSync(`${projectDir}/package-lock.json`)) {
|
||||
detected = 'npm'
|
||||
}
|
||||
return detected || 'npm'
|
||||
}
|
||||
@@ -6,24 +6,24 @@ import execa from 'execa'
|
||||
import fs from 'fs'
|
||||
import fse from 'fs-extra'
|
||||
import globby from 'globby'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import path from 'path'
|
||||
import { promisify } from 'util'
|
||||
|
||||
import type { CliArgs, DbType, NextAppDetails, NextConfigType, PackageManager } from '../types.js'
|
||||
|
||||
import { copyRecursiveSync } from '../utils/copy-recursive-sync.js'
|
||||
import { debug as origDebug, warning } from '../utils/log.js'
|
||||
import { moveMessage } from '../utils/messages.js'
|
||||
import { installPackages } from './install-packages.js'
|
||||
import { wrapNextConfig } from './wrap-next-config.js'
|
||||
|
||||
const readFile = promisify(fs.readFile)
|
||||
const writeFile = promisify(fs.writeFile)
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
import type { CliArgs, DbType, PackageManager } from '../types.js'
|
||||
|
||||
import { copyRecursiveSync } from '../utils/copy-recursive-sync.js'
|
||||
import { debug as origDebug, warning } from '../utils/log.js'
|
||||
import { moveMessage } from '../utils/messages.js'
|
||||
import { wrapNextConfig } from './wrap-next-config.js'
|
||||
|
||||
type InitNextArgs = Pick<CliArgs, '--debug'> & {
|
||||
dbType: DbType
|
||||
nextAppDetails?: NextAppDetails
|
||||
@@ -32,8 +32,6 @@ type InitNextArgs = Pick<CliArgs, '--debug'> & {
|
||||
useDistFiles?: boolean
|
||||
}
|
||||
|
||||
type NextConfigType = 'cjs' | 'esm'
|
||||
|
||||
type InitNextResult =
|
||||
| {
|
||||
isSrcDir: boolean
|
||||
@@ -55,7 +53,8 @@ export async function initNext(args: InitNextArgs): Promise<InitNextResult> {
|
||||
nextAppDetails.nextAppDir = createdAppDir
|
||||
}
|
||||
|
||||
const { hasTopLevelLayout, isSrcDir, nextAppDir, nextConfigType } = nextAppDetails
|
||||
const { hasTopLevelLayout, isPayloadInstalled, isSrcDir, nextAppDir, nextConfigType } =
|
||||
nextAppDetails
|
||||
|
||||
if (!nextConfigType) {
|
||||
return {
|
||||
@@ -222,43 +221,19 @@ function installAndConfigurePayload(
|
||||
}
|
||||
|
||||
async function installDeps(projectDir: string, packageManager: PackageManager, dbType: DbType) {
|
||||
const packagesToInstall = ['payload', '@payloadcms/next', '@payloadcms/richtext-lexical'].map(
|
||||
(pkg) => `${pkg}@beta`,
|
||||
)
|
||||
const packagesToInstall = [
|
||||
'payload',
|
||||
'@payloadcms/next',
|
||||
'@payloadcms/richtext-lexical',
|
||||
'@payloadcms/plugin-cloud',
|
||||
].map((pkg) => `${pkg}@beta`)
|
||||
|
||||
packagesToInstall.push(`@payloadcms/db-${dbType}@beta`)
|
||||
|
||||
let exitCode = 0
|
||||
switch (packageManager) {
|
||||
case 'npm': {
|
||||
;({ exitCode } = await execa('npm', ['install', '--save', ...packagesToInstall], {
|
||||
cwd: projectDir,
|
||||
}))
|
||||
break
|
||||
}
|
||||
case 'yarn':
|
||||
case 'pnpm': {
|
||||
;({ exitCode } = await execa(packageManager, ['add', ...packagesToInstall], {
|
||||
cwd: projectDir,
|
||||
}))
|
||||
break
|
||||
}
|
||||
case 'bun': {
|
||||
warning('Bun support is untested.')
|
||||
;({ exitCode } = await execa('bun', ['add', ...packagesToInstall], { cwd: projectDir }))
|
||||
break
|
||||
}
|
||||
}
|
||||
// Match graphql version of @payloadcms/next
|
||||
packagesToInstall.push('graphql@^16.8.1')
|
||||
|
||||
return { success: exitCode === 0 }
|
||||
}
|
||||
|
||||
type NextAppDetails = {
|
||||
hasTopLevelLayout: boolean
|
||||
isSrcDir: boolean
|
||||
nextAppDir?: string
|
||||
nextConfigPath?: string
|
||||
nextConfigType?: NextConfigType
|
||||
return await installPackages({ packageManager, packagesToInstall, projectDir })
|
||||
}
|
||||
|
||||
export async function getNextAppDetails(projectDir: string): Promise<NextAppDetails> {
|
||||
@@ -267,6 +242,7 @@ export async function getNextAppDetails(projectDir: string): Promise<NextAppDeta
|
||||
const nextConfigPath: string | undefined = (
|
||||
await globby('next.config.*js', { absolute: true, cwd: projectDir })
|
||||
)?.[0]
|
||||
|
||||
if (!nextConfigPath || nextConfigPath.length === 0) {
|
||||
return {
|
||||
hasTopLevelLayout: false,
|
||||
@@ -275,6 +251,16 @@ export async function getNextAppDetails(projectDir: string): Promise<NextAppDeta
|
||||
}
|
||||
}
|
||||
|
||||
const packageObj = await fse.readJson(path.resolve(projectDir, 'package.json'))
|
||||
if (packageObj.dependencies?.payload) {
|
||||
return {
|
||||
hasTopLevelLayout: false,
|
||||
isPayloadInstalled: true,
|
||||
isSrcDir,
|
||||
nextConfigPath,
|
||||
}
|
||||
}
|
||||
|
||||
let nextAppDir: string | undefined = (
|
||||
await globby(['**/app'], {
|
||||
absolute: true,
|
||||
@@ -288,7 +274,7 @@ export async function getNextAppDetails(projectDir: string): Promise<NextAppDeta
|
||||
nextAppDir = undefined
|
||||
}
|
||||
|
||||
const configType = await getProjectType(projectDir, nextConfigPath)
|
||||
const configType = getProjectType({ nextConfigPath, packageObj })
|
||||
|
||||
const hasTopLevelLayout = nextAppDir
|
||||
? fs.existsSync(path.resolve(nextAppDir, 'layout.tsx'))
|
||||
@@ -297,7 +283,11 @@ export async function getNextAppDetails(projectDir: string): Promise<NextAppDeta
|
||||
return { hasTopLevelLayout, isSrcDir, nextAppDir, nextConfigPath, nextConfigType: configType }
|
||||
}
|
||||
|
||||
async function getProjectType(projectDir: string, nextConfigPath: string): Promise<'cjs' | 'esm'> {
|
||||
function getProjectType(args: {
|
||||
nextConfigPath: string
|
||||
packageObj: Record<string, unknown>
|
||||
}): 'cjs' | 'esm' {
|
||||
const { nextConfigPath, packageObj } = args
|
||||
if (nextConfigPath.endsWith('.mjs')) {
|
||||
return 'esm'
|
||||
}
|
||||
@@ -305,7 +295,6 @@ async function getProjectType(projectDir: string, nextConfigPath: string): Promi
|
||||
return 'cjs'
|
||||
}
|
||||
|
||||
const packageObj = await fse.readJson(path.resolve(projectDir, 'package.json'))
|
||||
const packageJsonType = packageObj.type
|
||||
if (packageJsonType === 'module') {
|
||||
return 'esm'
|
||||
|
||||
42
packages/create-payload-app/src/lib/install-packages.ts
Normal file
42
packages/create-payload-app/src/lib/install-packages.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import execa from 'execa'
|
||||
|
||||
import type { PackageManager } from '../types.js'
|
||||
|
||||
import { error, warning } from '../utils/log.js'
|
||||
|
||||
export async function installPackages(args: {
|
||||
packageManager: PackageManager
|
||||
packagesToInstall: string[]
|
||||
projectDir: string
|
||||
}) {
|
||||
const { packageManager, packagesToInstall, projectDir } = args
|
||||
|
||||
let exitCode = 0
|
||||
let stderr = ''
|
||||
|
||||
switch (packageManager) {
|
||||
case 'npm': {
|
||||
;({ exitCode, stderr } = await execa('npm', ['install', '--save', ...packagesToInstall], {
|
||||
cwd: projectDir,
|
||||
}))
|
||||
break
|
||||
}
|
||||
case 'yarn':
|
||||
case 'pnpm':
|
||||
case 'bun': {
|
||||
if (packageManager === 'bun') {
|
||||
warning('Bun support is untested.')
|
||||
}
|
||||
;({ exitCode, stderr } = await execa(packageManager, ['add', ...packagesToInstall], {
|
||||
cwd: projectDir,
|
||||
}))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (exitCode !== 0) {
|
||||
error(`Unable to install packages. Error: ${stderr}`)
|
||||
}
|
||||
|
||||
return { success: exitCode === 0 }
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import execa from 'execa'
|
||||
import fse from 'fs-extra'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import path from 'path'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
import type { NextAppDetails } from '../types.js'
|
||||
|
||||
import { copyRecursiveSync } from '../utils/copy-recursive-sync.js'
|
||||
import { info } from '../utils/log.js'
|
||||
import { getPackageManager } from './get-package-manager.js'
|
||||
import { installPackages } from './install-packages.js'
|
||||
|
||||
export async function updatePayloadInProject(
|
||||
appDetails: NextAppDetails,
|
||||
): Promise<{ message: string; success: boolean }> {
|
||||
if (!appDetails.nextConfigPath) return { message: 'No Next.js config found', success: false }
|
||||
|
||||
const projectDir = path.dirname(appDetails.nextConfigPath)
|
||||
|
||||
const packageObj = (await fse.readJson(path.resolve(projectDir, 'package.json'))) as {
|
||||
dependencies?: Record<string, string>
|
||||
}
|
||||
if (!packageObj?.dependencies) {
|
||||
throw new Error('No package.json found in this project')
|
||||
}
|
||||
|
||||
const payloadVersion = packageObj.dependencies?.payload
|
||||
if (!payloadVersion) {
|
||||
throw new Error('Payload is not installed in this project')
|
||||
}
|
||||
|
||||
const packageManager = await getPackageManager({ projectDir })
|
||||
|
||||
// Fetch latest Payload version from npm
|
||||
const { exitCode: getLatestVersionExitCode, stdout: latestPayloadVersion } = await execa('npm', [
|
||||
'show',
|
||||
'payload@beta',
|
||||
'version',
|
||||
])
|
||||
if (getLatestVersionExitCode !== 0) {
|
||||
throw new Error('Failed to fetch latest Payload version')
|
||||
}
|
||||
|
||||
if (payloadVersion === latestPayloadVersion) {
|
||||
return { message: `Payload v${payloadVersion} is already up to date.`, success: true }
|
||||
}
|
||||
|
||||
// Update all existing Payload packages
|
||||
const payloadPackages = Object.keys(packageObj.dependencies).filter((dep) =>
|
||||
dep.startsWith('@payloadcms/'),
|
||||
)
|
||||
|
||||
const packageNames = ['payload', ...payloadPackages]
|
||||
|
||||
const packagesToUpdate = packageNames.map((pkg) => `${pkg}@${latestPayloadVersion}`)
|
||||
|
||||
info(`Using ${packageManager}.\n`)
|
||||
info(
|
||||
`Updating ${packagesToUpdate.length} Payload packages to v${latestPayloadVersion}...\n\n${packageNames.map((p) => ` - ${p}`).join('\n')}`,
|
||||
)
|
||||
|
||||
const { success: updateSuccess } = await installPackages({
|
||||
packageManager,
|
||||
packagesToInstall: packagesToUpdate,
|
||||
projectDir,
|
||||
})
|
||||
|
||||
if (!updateSuccess) {
|
||||
throw new Error('Failed to update Payload packages')
|
||||
}
|
||||
info('Payload packages updated successfully.')
|
||||
|
||||
info(`Updating Payload Next.js files...`)
|
||||
const templateFilesPath = dirname.endsWith('dist')
|
||||
? path.resolve(dirname, '../..', 'dist/template')
|
||||
: path.resolve(dirname, '../../../../templates/blank-3.0')
|
||||
|
||||
const templateSrcDir = path.resolve(templateFilesPath, 'src/app/(payload)')
|
||||
|
||||
copyRecursiveSync(
|
||||
templateSrcDir,
|
||||
path.resolve(projectDir, appDetails.isSrcDir ? 'src/app' : 'app', '(payload)'),
|
||||
)
|
||||
|
||||
return { message: 'Payload updated successfully.', success: true }
|
||||
}
|
||||
@@ -2,21 +2,21 @@ import * as p from '@clack/prompts'
|
||||
import slugify from '@sindresorhus/slugify'
|
||||
import arg from 'arg'
|
||||
import chalk from 'chalk'
|
||||
// @ts-expect-error no types
|
||||
import { detect } from 'detect-package-manager'
|
||||
import figures from 'figures'
|
||||
import path from 'path'
|
||||
|
||||
import type { CliArgs, PackageManager } from './types.js'
|
||||
import type { CliArgs } from './types.js'
|
||||
|
||||
import { configurePayloadConfig } from './lib/configure-payload-config.js'
|
||||
import { createProject } from './lib/create-project.js'
|
||||
import { generateSecret } from './lib/generate-secret.js'
|
||||
import { getPackageManager } from './lib/get-package-manager.js'
|
||||
import { getNextAppDetails, initNext } from './lib/init-next.js'
|
||||
import { parseProjectName } from './lib/parse-project-name.js'
|
||||
import { parseTemplate } from './lib/parse-template.js'
|
||||
import { selectDb } from './lib/select-db.js'
|
||||
import { getValidTemplates, validateTemplate } from './lib/templates.js'
|
||||
import { updatePayloadInProject } from './lib/update-payload-in-project.js'
|
||||
import { writeEnvFile } from './lib/write-env-file.js'
|
||||
import { error, info } from './utils/log.js'
|
||||
import {
|
||||
@@ -85,7 +85,28 @@ export class Main {
|
||||
|
||||
// Detect if inside Next.js project
|
||||
const nextAppDetails = await getNextAppDetails(process.cwd())
|
||||
const { hasTopLevelLayout, nextAppDir, nextConfigPath } = nextAppDetails
|
||||
const { hasTopLevelLayout, isPayloadInstalled, nextAppDir, nextConfigPath } = nextAppDetails
|
||||
|
||||
// Upgrade Payload in existing project
|
||||
if (isPayloadInstalled && nextConfigPath) {
|
||||
p.log.warn(`Payload installation detected in current project.`)
|
||||
const shouldUpdate = await p.confirm({
|
||||
initialValue: false,
|
||||
message: chalk.bold(`Upgrade Payload in this project?`),
|
||||
})
|
||||
|
||||
if (!p.isCancel(shouldUpdate) || shouldUpdate) {
|
||||
const { message, success: updateSuccess } = await updatePayloadInProject(nextAppDetails)
|
||||
if (updateSuccess) {
|
||||
info(message)
|
||||
} else {
|
||||
error(message)
|
||||
}
|
||||
}
|
||||
|
||||
p.outro(feedbackOutro())
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
if (nextConfigPath) {
|
||||
this.args['--name'] = slugify(path.basename(path.dirname(nextConfigPath)))
|
||||
@@ -96,7 +117,7 @@ export class Main {
|
||||
? path.dirname(nextConfigPath)
|
||||
: path.resolve(process.cwd(), slugify(projectName))
|
||||
|
||||
const packageManager = await getPackageManager(this.args, projectDir)
|
||||
const packageManager = await getPackageManager({ cliArgs: this.args, projectDir })
|
||||
|
||||
if (nextConfigPath) {
|
||||
p.log.step(
|
||||
@@ -212,19 +233,3 @@ export class Main {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getPackageManager(args: CliArgs, projectDir: string): Promise<PackageManager> {
|
||||
let packageManager: PackageManager = 'npm'
|
||||
|
||||
if (args['--use-npm']) {
|
||||
packageManager = 'npm'
|
||||
} else if (args['--use-yarn']) {
|
||||
packageManager = 'yarn'
|
||||
} else if (args['--use-pnpm']) {
|
||||
packageManager = 'pnpm'
|
||||
} else {
|
||||
const detected = await detect({ cwd: projectDir })
|
||||
packageManager = detected || 'npm'
|
||||
}
|
||||
return packageManager
|
||||
}
|
||||
|
||||
@@ -65,3 +65,14 @@ export type DbDetails = {
|
||||
}
|
||||
|
||||
export type EditorType = 'lexical' | 'slate'
|
||||
|
||||
export type NextAppDetails = {
|
||||
hasTopLevelLayout: boolean
|
||||
isPayloadInstalled?: boolean
|
||||
isSrcDir: boolean
|
||||
nextAppDir?: string
|
||||
nextConfigPath?: string
|
||||
nextConfigType?: NextConfigType
|
||||
}
|
||||
|
||||
export type NextConfigType = 'cjs' | 'esm'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"description": "The officially supported MongoDB database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
@@ -9,7 +9,7 @@ It abstracts all of the email functionality that was in Payload by default in 2.
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/email-nodemailer` nodemailer
|
||||
pnpm add @payloadcms/email-nodemailer nodemailer
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-nodemailer",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"description": "Payload Nodemailer Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
@@ -11,6 +11,15 @@
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
@@ -5,7 +5,7 @@ This adapter allows you to send emails using the [Resend](https://resend.com) RE
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
pnpm add @payloadcms/email-resend`
|
||||
pnpm add @payloadcms/email-resend
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-resend",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"description": "Payload Resend Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/graphql",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Ivo Meißner
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
[Package Link](https://github.com/slicknode/graphql-query-complexity)
|
||||
23
packages/graphql/src/packages/graphql-type-json/LICENSE
Normal file
23
packages/graphql/src/packages/graphql-type-json/LICENSE
Normal file
@@ -0,0 +1,23 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Jimmy Jia
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
[Package Link](https://github.com/taion/graphql-type-json/tree/master)
|
||||
@@ -1,5 +1,5 @@
|
||||
import { GraphQLScalarType } from 'graphql'
|
||||
import { Kind, print } from 'graphql/language'
|
||||
import { Kind, print } from 'graphql/language/index.js'
|
||||
|
||||
function identity(value) {
|
||||
return value
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview-react",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"description": "The official live preview React SDK for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
@@ -35,12 +35,13 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/react": "18.3.2",
|
||||
"@types/react": "npm:types-react@19.0.0-beta.2",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0 || ^19.0.0",
|
||||
"react-dom": "^18.2.0 || ^19.0.0"
|
||||
"react": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522",
|
||||
"react-dom": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
@@ -53,5 +54,9 @@
|
||||
"main": "./dist/index.js",
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"overrides": {
|
||||
"@types/react": "npm:types-react@19.0.0-beta.2",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
||||
"rootDir": "./src" /* Specify the root folder within your source files. */,
|
||||
"jsx": "react"
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
||||
"rootDir": "./src" /* Specify the root folder within your source files. */,
|
||||
"jsx": "react"
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"description": "The official live preview JavaScript SDK for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -8,6 +8,15 @@
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
},
|
||||
"experimental": {
|
||||
"plugins": [
|
||||
[
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/next",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -49,7 +49,7 @@
|
||||
"@types/busboy": "^1.5.3",
|
||||
"busboy": "^1.6.0",
|
||||
"deep-equal": "2.2.2",
|
||||
"file-type": "19.0.0",
|
||||
"file-type": "19.0.0 || 19.0.0-rc-f994737d14-20240522",
|
||||
"graphql-http": "^1.22.0",
|
||||
"graphql-playground-html": "1.6.30",
|
||||
"http-status": "1.6.2",
|
||||
@@ -63,8 +63,8 @@
|
||||
"devDependencies": {
|
||||
"@next/eslint-plugin-next": "^14.1.0",
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/react": "18.3.2",
|
||||
"@types/react-dom": "18.3.0",
|
||||
"@types/react": "npm:types-react@19.0.0-beta.2",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
|
||||
"@types/ws": "^8.5.10",
|
||||
"css-loader": "^6.10.0",
|
||||
"css-minimizer-webpack-plugin": "^6.0.0",
|
||||
@@ -81,7 +81,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"graphql": "^16.8.1",
|
||||
"next": "^14.3.0-canary.68",
|
||||
"next": "^15.0.0-rc.0",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"engines": {
|
||||
@@ -107,5 +107,9 @@
|
||||
"main": "./dist/index.js",
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"overrides": {
|
||||
"@types/react": "npm:types-react@19.0.0-beta.2",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export { EditView } from '../views/Edit/index.js'
|
||||
export { DefaultEditView as EditView } from '../views/Edit/Default/index.js'
|
||||
export { DefaultListView as ListView } from '../views/List/Default/index.js'
|
||||
export { NotFoundPage } from '../views/NotFound/index.js'
|
||||
export { type GenerateViewMetadata, RootPage, generatePageMetadata } from '../views/Root/index.js'
|
||||
|
||||
@@ -152,7 +152,7 @@ type FetchAPIFileUpload = (args: {
|
||||
request: Request
|
||||
}) => Promise<FetchAPIFileUploadResponse>
|
||||
export const fetchAPIFileUpload: FetchAPIFileUpload = async ({ options, request }) => {
|
||||
const uploadOptions = { ...DEFAULT_OPTIONS, ...options }
|
||||
const uploadOptions: FetchAPIFileUploadOptions = { ...DEFAULT_OPTIONS, ...options }
|
||||
if (!isEligibleRequest(request)) {
|
||||
debugLog(uploadOptions, 'Request is not eligible for file upload!')
|
||||
return {
|
||||
|
||||
@@ -15,6 +15,7 @@ import 'react-toastify/dist/ReactToastify.css'
|
||||
|
||||
import { getPayloadHMR } from '../../utilities/getPayloadHMR.js'
|
||||
import { getRequestLanguage } from '../../utilities/getRequestLanguage.js'
|
||||
import { getRequestTheme } from '../../utilities/getRequestTheme.js'
|
||||
import { DefaultEditView } from '../../views/Edit/Default/index.js'
|
||||
import { DefaultListView } from '../../views/List/Default/index.js'
|
||||
|
||||
@@ -49,6 +50,12 @@ export const RootLayout = async ({
|
||||
headers,
|
||||
})
|
||||
|
||||
const theme = getRequestTheme({
|
||||
config,
|
||||
cookies,
|
||||
headers,
|
||||
})
|
||||
|
||||
const payload = await getPayloadHMR({ config })
|
||||
const i18n: I18nClient = await initI18n({
|
||||
config: config.i18n,
|
||||
@@ -94,7 +101,7 @@ export const RootLayout = async ({
|
||||
})
|
||||
|
||||
return (
|
||||
<html className={merriweather.variable} dir={dir} lang={languageCode}>
|
||||
<html className={merriweather.variable} data-theme={theme} dir={dir} lang={languageCode}>
|
||||
<body>
|
||||
<RootProvider
|
||||
componentMap={componentMap}
|
||||
@@ -105,6 +112,7 @@ export const RootLayout = async ({
|
||||
languageOptions={languageOptions}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
switchLanguageServerAction={switchLanguageServerAction}
|
||||
theme={theme}
|
||||
translations={i18n.translations}
|
||||
>
|
||||
{wrappedChildren}
|
||||
|
||||
@@ -6,15 +6,29 @@ import type { BaseRouteHandler } from '../types.js'
|
||||
import { headersWithCors } from '../../../utilities/headersWithCors.js'
|
||||
|
||||
export const access: BaseRouteHandler = async ({ req }) => {
|
||||
const results = await accessOperation({
|
||||
const headers = headersWithCors({
|
||||
headers: new Headers(),
|
||||
req,
|
||||
})
|
||||
|
||||
return Response.json(results, {
|
||||
headers: headersWithCors({
|
||||
headers: new Headers(),
|
||||
try {
|
||||
const results = await accessOperation({
|
||||
req,
|
||||
}),
|
||||
status: httpStatus.OK,
|
||||
})
|
||||
})
|
||||
|
||||
return Response.json(results, {
|
||||
headers,
|
||||
status: httpStatus.OK,
|
||||
})
|
||||
} catch (e: unknown) {
|
||||
return Response.json(
|
||||
{
|
||||
error: e,
|
||||
},
|
||||
{
|
||||
headers,
|
||||
status: httpStatus.INTERNAL_SERVER_ERROR,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,9 @@ export const reload = async (config: SanitizedConfig, payload: Payload): Promise
|
||||
// TODO: support HMR for other props in the future (see payload/src/index init()) hat may change on Payload singleton
|
||||
|
||||
await payload.db.init()
|
||||
await payload.db.connect({ hotReload: true })
|
||||
if (payload.db.connect) {
|
||||
await payload.db.connect({ hotReload: true })
|
||||
}
|
||||
}
|
||||
|
||||
export const getPayloadHMR = async (options: InitOptions): Promise<Payload> => {
|
||||
|
||||
@@ -18,17 +18,19 @@ export const getRequestLanguage = ({
|
||||
}: GetRequestLanguageArgs): AcceptedLanguages => {
|
||||
const supportedLanguageKeys = <AcceptedLanguages[]>Object.keys(config.i18n.supportedLanguages)
|
||||
const langCookie = cookies.get(`${config.cookiePrefix || 'payload'}-lng`)
|
||||
|
||||
const languageFromCookie: AcceptedLanguages = (
|
||||
typeof langCookie === 'string' ? langCookie : langCookie?.value
|
||||
) as AcceptedLanguages
|
||||
const languageFromHeader = headers.get('Accept-Language')
|
||||
? extractHeaderLanguage(headers.get('Accept-Language'))
|
||||
: undefined
|
||||
|
||||
if (languageFromCookie && supportedLanguageKeys.includes(languageFromCookie)) {
|
||||
return languageFromCookie
|
||||
}
|
||||
|
||||
const languageFromHeader = headers.get('Accept-Language')
|
||||
? extractHeaderLanguage(headers.get('Accept-Language'))
|
||||
: undefined
|
||||
|
||||
if (languageFromHeader && supportedLanguageKeys.includes(languageFromHeader)) {
|
||||
return languageFromHeader
|
||||
}
|
||||
|
||||
33
packages/next/src/utilities/getRequestTheme.ts
Normal file
33
packages/next/src/utilities/getRequestTheme.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { Theme } from '@payloadcms/ui/providers/Theme'
|
||||
import type { ReadonlyRequestCookies } from 'next/dist/server/web/spec-extension/adapters/request-cookies.js'
|
||||
import type { SanitizedConfig } from 'payload/config'
|
||||
|
||||
import { defaultTheme } from '@payloadcms/ui/providers/Theme'
|
||||
|
||||
type GetRequestLanguageArgs = {
|
||||
config: SanitizedConfig
|
||||
cookies: Map<string, string> | ReadonlyRequestCookies
|
||||
headers: Request['headers']
|
||||
}
|
||||
|
||||
const acceptedThemes: Theme[] = ['dark', 'light']
|
||||
|
||||
export const getRequestTheme = ({ config, cookies, headers }: GetRequestLanguageArgs): Theme => {
|
||||
const themeCookie = cookies.get(`${config.cookiePrefix || 'payload'}-theme`)
|
||||
|
||||
const themeFromCookie: Theme = (
|
||||
typeof themeCookie === 'string' ? themeCookie : themeCookie?.value
|
||||
) as Theme
|
||||
|
||||
if (themeFromCookie && acceptedThemes.includes(themeFromCookie)) {
|
||||
return themeFromCookie
|
||||
}
|
||||
|
||||
const themeFromHeader = headers.get('Sec-CH-Prefers-Color-Scheme') as Theme
|
||||
|
||||
if (themeFromHeader && acceptedThemes.includes(themeFromHeader)) {
|
||||
return themeFromHeader
|
||||
}
|
||||
|
||||
return defaultTheme
|
||||
}
|
||||
@@ -52,6 +52,7 @@ export const initPage = async ({
|
||||
fallbackLocale: null,
|
||||
locale: locale.code,
|
||||
req: {
|
||||
host: headers.get('host'),
|
||||
i18n,
|
||||
query: qs.parse(queryString, {
|
||||
depth: 10,
|
||||
|
||||
@@ -30,14 +30,14 @@ export const meta = async (args: MetaConfig & { serverURL: string }): Promise<an
|
||||
type: 'image/png',
|
||||
rel: 'icon',
|
||||
sizes: '32x32',
|
||||
url: payloadFaviconDark?.src,
|
||||
url: typeof payloadFaviconDark === 'object' ? payloadFaviconDark?.src : payloadFaviconDark,
|
||||
},
|
||||
{
|
||||
type: 'image/png',
|
||||
media: '(prefers-color-scheme: dark)',
|
||||
rel: 'icon',
|
||||
sizes: '32x32',
|
||||
url: payloadFaviconLight?.src,
|
||||
url: typeof payloadFaviconLight === 'object' ? payloadFaviconLight?.src : payloadFaviconLight,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -79,7 +79,7 @@ export const meta = async (args: MetaConfig & { serverURL: string }): Promise<an
|
||||
{
|
||||
alt: ogTitle,
|
||||
height: 480,
|
||||
url: staticOGImage.src,
|
||||
url: typeof staticOGImage === 'object' ? staticOGImage?.src : staticOGImage,
|
||||
width: 640,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -9,15 +9,21 @@ import { FormQueryParamsProvider } from '@payloadcms/ui/providers/FormQueryParam
|
||||
import { notFound } from 'next/navigation.js'
|
||||
import React from 'react'
|
||||
|
||||
import { getDocumentPermissions } from '../Document/getDocumentPermissions.js'
|
||||
import { EditView } from '../Edit/index.js'
|
||||
import { Settings } from './Settings/index.js'
|
||||
|
||||
export { generateAccountMetadata } from './meta.js'
|
||||
|
||||
export const Account: React.FC<AdminViewProps> = ({ initPageResult, params, searchParams }) => {
|
||||
export const Account: React.FC<AdminViewProps> = async ({
|
||||
initPageResult,
|
||||
params,
|
||||
searchParams,
|
||||
}) => {
|
||||
const {
|
||||
locale,
|
||||
permissions,
|
||||
req,
|
||||
req: {
|
||||
i18n,
|
||||
payload,
|
||||
@@ -32,11 +38,17 @@ export const Account: React.FC<AdminViewProps> = ({ initPageResult, params, sear
|
||||
serverURL,
|
||||
} = config
|
||||
|
||||
const collectionPermissions = permissions?.collections?.[userSlug]
|
||||
|
||||
const collectionConfig = config.collections.find((collection) => collection.slug === userSlug)
|
||||
|
||||
if (collectionConfig) {
|
||||
if (collectionConfig && user?.id) {
|
||||
const { docPermissions, hasPublishPermission, hasSavePermission } =
|
||||
await getDocumentPermissions({
|
||||
id: user.id,
|
||||
collectionConfig,
|
||||
data: user,
|
||||
req,
|
||||
})
|
||||
|
||||
const viewComponentProps: ServerSideEditViewProps = {
|
||||
initPageResult,
|
||||
params,
|
||||
@@ -50,9 +62,10 @@ export const Account: React.FC<AdminViewProps> = ({ initPageResult, params, sear
|
||||
action={`${serverURL}${api}/${userSlug}${user?.id ? `/${user.id}` : ''}`}
|
||||
apiURL={`${serverURL}${api}/${userSlug}${user?.id ? `/${user.id}` : ''}`}
|
||||
collectionSlug={userSlug}
|
||||
docPermissions={collectionPermissions}
|
||||
hasSavePermission={collectionPermissions?.update?.permission}
|
||||
id={user?.id}
|
||||
docPermissions={docPermissions}
|
||||
hasPublishPermission={hasPublishPermission}
|
||||
hasSavePermission={hasSavePermission}
|
||||
id={user?.id.toString()}
|
||||
isEditing
|
||||
>
|
||||
<DocumentHeader
|
||||
|
||||
41
packages/next/src/views/Document/getDocumentData.tsx
Normal file
41
packages/next/src/views/Document/getDocumentData.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import type {
|
||||
Data,
|
||||
Payload,
|
||||
PayloadRequest,
|
||||
SanitizedCollectionConfig,
|
||||
SanitizedGlobalConfig,
|
||||
} from 'payload/types'
|
||||
|
||||
export const getDocumentData = async (args: {
|
||||
collectionConfig?: SanitizedCollectionConfig
|
||||
globalConfig?: SanitizedGlobalConfig
|
||||
id?: number | string
|
||||
locale: Locale
|
||||
payload: Payload
|
||||
req: PayloadRequest
|
||||
}): Promise<Data> => {
|
||||
const { id, collectionConfig, globalConfig, locale, payload, req } = args
|
||||
|
||||
let data: Data
|
||||
|
||||
if (collectionConfig && id !== undefined && id !== null) {
|
||||
data = await payload.findByID({
|
||||
id,
|
||||
collection: collectionConfig.slug,
|
||||
depth: 0,
|
||||
locale: locale.code,
|
||||
req,
|
||||
})
|
||||
}
|
||||
|
||||
if (globalConfig) {
|
||||
data = await payload.findGlobal({
|
||||
slug: globalConfig.slug,
|
||||
depth: 0,
|
||||
locale: locale.code,
|
||||
req,
|
||||
})
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
105
packages/next/src/views/Document/getDocumentPermissions.tsx
Normal file
105
packages/next/src/views/Document/getDocumentPermissions.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import type { DocumentPermissions } from 'payload/auth'
|
||||
import type {
|
||||
Data,
|
||||
PayloadRequest,
|
||||
SanitizedCollectionConfig,
|
||||
SanitizedGlobalConfig,
|
||||
} from 'payload/types'
|
||||
|
||||
import { hasSavePermission as getHasSavePermission } from '@payloadcms/ui/utilities/hasSavePermission'
|
||||
import { isEditing as getIsEditing } from '@payloadcms/ui/utilities/isEditing'
|
||||
import { docAccessOperation, docAccessOperationGlobal } from 'payload/operations'
|
||||
|
||||
export const getDocumentPermissions = async (args: {
|
||||
collectionConfig?: SanitizedCollectionConfig
|
||||
data: Data
|
||||
globalConfig?: SanitizedGlobalConfig
|
||||
id?: number | string
|
||||
req: PayloadRequest
|
||||
}): Promise<{
|
||||
docPermissions: DocumentPermissions
|
||||
hasPublishPermission: boolean
|
||||
hasSavePermission: boolean
|
||||
}> => {
|
||||
const { id, collectionConfig, data = {}, globalConfig, req } = args
|
||||
|
||||
let docPermissions: DocumentPermissions
|
||||
let hasPublishPermission = false
|
||||
|
||||
if (collectionConfig) {
|
||||
try {
|
||||
docPermissions = await docAccessOperation({
|
||||
id: id?.toString(),
|
||||
collection: {
|
||||
config: collectionConfig,
|
||||
},
|
||||
req: {
|
||||
...req,
|
||||
data,
|
||||
},
|
||||
})
|
||||
|
||||
if (collectionConfig.versions?.drafts) {
|
||||
hasPublishPermission = await docAccessOperation({
|
||||
id: id?.toString(),
|
||||
collection: {
|
||||
config: collectionConfig,
|
||||
},
|
||||
req: {
|
||||
...req,
|
||||
data: {
|
||||
...data,
|
||||
_status: 'published',
|
||||
},
|
||||
},
|
||||
}).then(({ update }) => update?.permission)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error) // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
if (globalConfig) {
|
||||
try {
|
||||
docPermissions = await docAccessOperationGlobal({
|
||||
globalConfig,
|
||||
req: {
|
||||
...req,
|
||||
data,
|
||||
},
|
||||
})
|
||||
|
||||
if (globalConfig.versions?.drafts) {
|
||||
hasPublishPermission = await docAccessOperationGlobal({
|
||||
globalConfig,
|
||||
req: {
|
||||
...req,
|
||||
data: {
|
||||
...data,
|
||||
_status: 'published',
|
||||
},
|
||||
},
|
||||
}).then(({ update }) => update?.permission)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error) // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
const hasSavePermission = getHasSavePermission({
|
||||
collectionSlug: collectionConfig?.slug,
|
||||
docPermissions,
|
||||
globalSlug: globalConfig?.slug,
|
||||
isEditing: getIsEditing({
|
||||
id,
|
||||
collectionSlug: collectionConfig?.slug,
|
||||
globalSlug: globalConfig?.slug,
|
||||
}),
|
||||
})
|
||||
|
||||
return {
|
||||
docPermissions,
|
||||
hasPublishPermission,
|
||||
hasSavePermission,
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { EditViewComponent } from 'payload/config'
|
||||
import type { AdminViewComponent, ServerSideEditViewProps } from 'payload/types'
|
||||
import type { DocumentPermissions } from 'payload/types'
|
||||
import type { AdminViewProps } from 'payload/types'
|
||||
|
||||
import { DocumentHeader } from '@payloadcms/ui/elements/DocumentHeader'
|
||||
@@ -9,15 +8,15 @@ import { RenderCustomComponent } from '@payloadcms/ui/elements/RenderCustomCompo
|
||||
import { DocumentInfoProvider } from '@payloadcms/ui/providers/DocumentInfo'
|
||||
import { EditDepthProvider } from '@payloadcms/ui/providers/EditDepth'
|
||||
import { FormQueryParamsProvider } from '@payloadcms/ui/providers/FormQueryParams'
|
||||
import { hasSavePermission as getHasSavePermission } from '@payloadcms/ui/utilities/hasSavePermission'
|
||||
import { isEditing as getIsEditing } from '@payloadcms/ui/utilities/isEditing'
|
||||
import { notFound, redirect } from 'next/navigation.js'
|
||||
import { docAccessOperation } from 'payload/operations'
|
||||
import React from 'react'
|
||||
|
||||
import type { GenerateEditViewMetadata } from './getMetaBySegment.js'
|
||||
|
||||
import { NotFoundView } from '../NotFound/index.js'
|
||||
import { getDocumentData } from './getDocumentData.js'
|
||||
import { getDocumentPermissions } from './getDocumentPermissions.js'
|
||||
import { getMetaBySegment } from './getMetaBySegment.js'
|
||||
import { getViewsFromConfig } from './getViewsFromConfig.js'
|
||||
|
||||
@@ -61,32 +60,33 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
let DefaultView: EditViewComponent
|
||||
let ErrorView: AdminViewComponent
|
||||
|
||||
let docPermissions: DocumentPermissions
|
||||
let hasSavePermission: boolean
|
||||
let apiURL: string
|
||||
let action: string
|
||||
|
||||
const data = await getDocumentData({
|
||||
id,
|
||||
collectionConfig,
|
||||
globalConfig,
|
||||
locale,
|
||||
payload,
|
||||
req,
|
||||
})
|
||||
|
||||
const { docPermissions, hasPublishPermission, hasSavePermission } = await getDocumentPermissions({
|
||||
id,
|
||||
collectionConfig,
|
||||
data,
|
||||
globalConfig,
|
||||
req,
|
||||
})
|
||||
|
||||
if (collectionConfig) {
|
||||
if (!visibleEntities?.collections?.find((visibleSlug) => visibleSlug === collectionSlug)) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
try {
|
||||
docPermissions = await docAccessOperation({
|
||||
id,
|
||||
collection: {
|
||||
config: collectionConfig,
|
||||
},
|
||||
req,
|
||||
})
|
||||
} catch (error) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
action = `${serverURL}${apiRoute}/${collectionSlug}${isEditing ? `/${id}` : ''}`
|
||||
|
||||
hasSavePermission = getHasSavePermission({ collectionSlug, docPermissions, isEditing })
|
||||
|
||||
apiURL = `${serverURL}${apiRoute}/${collectionSlug}/${id}?locale=${locale.code}${
|
||||
collectionConfig.versions?.drafts ? '&draft=true' : ''
|
||||
}`
|
||||
@@ -117,9 +117,6 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
notFound()
|
||||
}
|
||||
|
||||
docPermissions = permissions?.globals?.[globalSlug]
|
||||
hasSavePermission = getHasSavePermission({ docPermissions, globalSlug, isEditing })
|
||||
|
||||
action = `${serverURL}${apiRoute}/globals/${globalSlug}`
|
||||
|
||||
apiURL = `${serverURL}${apiRoute}/${globalSlug}?locale=${locale.code}${
|
||||
@@ -191,6 +188,7 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
disableActions={false}
|
||||
docPermissions={docPermissions}
|
||||
globalSlug={globalConfig?.slug}
|
||||
hasPublishPermission={hasPublishPermission}
|
||||
hasSavePermission={hasSavePermission}
|
||||
id={id}
|
||||
isEditing={isEditing}
|
||||
|
||||
@@ -44,10 +44,10 @@ export const DefaultEditView: React.FC = () => {
|
||||
disableActions,
|
||||
disableLeaveWithoutSaving,
|
||||
docPermissions,
|
||||
getDocPermissions,
|
||||
getDocPreferences,
|
||||
getVersions,
|
||||
globalSlug,
|
||||
hasPublishPermission,
|
||||
hasSavePermission,
|
||||
initialData: data,
|
||||
initialState,
|
||||
@@ -115,7 +115,6 @@ export const DefaultEditView: React.FC = () => {
|
||||
}
|
||||
|
||||
void getVersions()
|
||||
void getDocPermissions()
|
||||
|
||||
if (typeof onSaveFromContext === 'function') {
|
||||
void onSaveFromContext({
|
||||
@@ -147,7 +146,6 @@ export const DefaultEditView: React.FC = () => {
|
||||
depth,
|
||||
collectionSlug,
|
||||
getVersions,
|
||||
getDocPermissions,
|
||||
isEditing,
|
||||
refreshCookieAsync,
|
||||
adminRoute,
|
||||
@@ -221,6 +219,7 @@ export const DefaultEditView: React.FC = () => {
|
||||
apiURL={apiURL}
|
||||
data={data}
|
||||
disableActions={disableActions}
|
||||
hasPublishPermission={hasPublishPermission}
|
||||
hasSavePermission={hasSavePermission}
|
||||
id={id}
|
||||
isEditing={isEditing}
|
||||
|
||||
@@ -13,7 +13,7 @@ export interface LivePreviewContextType {
|
||||
breakpoints: LivePreviewConfig['breakpoints']
|
||||
fieldSchemaJSON?: ReturnType<typeof fieldSchemaToJSON>
|
||||
iframeHasLoaded: boolean
|
||||
iframeRef: React.RefObject<HTMLIFrameElement>
|
||||
iframeRef: React.RefObject<HTMLIFrameElement | null>
|
||||
isPopupOpen: boolean
|
||||
measuredDeviceSize: {
|
||||
height: number
|
||||
|
||||
@@ -32,7 +32,7 @@ export const LivePreview: React.FC<EditViewProps> = (props) => {
|
||||
const { breakpoint, fieldSchemaJSON } = useLivePreviewContext()
|
||||
|
||||
const prevWindowType =
|
||||
React.useRef<ReturnType<typeof useLivePreviewContext>['previewWindowType']>()
|
||||
React.useRef<ReturnType<typeof useLivePreviewContext>['previewWindowType']>(undefined)
|
||||
|
||||
const [fields] = useAllFormFields()
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ const PreviewView: React.FC<Props> = ({
|
||||
docPermissions,
|
||||
getDocPreferences,
|
||||
globalSlug,
|
||||
hasPublishPermission,
|
||||
hasSavePermission,
|
||||
initialData,
|
||||
initialState,
|
||||
@@ -160,6 +161,7 @@ const PreviewView: React.FC<Props> = ({
|
||||
apiURL={apiURL}
|
||||
data={initialData}
|
||||
disableActions={disableActions}
|
||||
hasPublishPermission={hasPublishPermission}
|
||||
hasSavePermission={hasSavePermission}
|
||||
id={id}
|
||||
isEditing={isEditing}
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { I18n } from '@payloadcms/translations'
|
||||
import type { Metadata } from 'next'
|
||||
import type { SanitizedConfig } from 'payload/types'
|
||||
|
||||
import { WithServerSideProps } from '@payloadcms/ui/elements/WithServerSideProps'
|
||||
import { DefaultTemplate } from '@payloadcms/ui/templates/Default'
|
||||
import { MinimalTemplate } from '@payloadcms/ui/templates/Minimal'
|
||||
import { notFound, redirect } from 'next/navigation.js'
|
||||
@@ -82,7 +83,16 @@ export const RootPage = async ({
|
||||
}
|
||||
|
||||
const RenderedView = (
|
||||
<DefaultView initPageResult={initPageResult} params={params} searchParams={searchParams} />
|
||||
<WithServerSideProps
|
||||
Component={DefaultView}
|
||||
serverOnlyProps={
|
||||
{
|
||||
initPageResult,
|
||||
params,
|
||||
searchParams,
|
||||
} as any
|
||||
}
|
||||
/>
|
||||
)
|
||||
|
||||
return (
|
||||
@@ -100,7 +110,12 @@ export const RootPage = async ({
|
||||
permissions={initPageResult?.permissions}
|
||||
searchParams={searchParams}
|
||||
user={initPageResult?.req.user}
|
||||
visibleEntities={initPageResult.visibleEntities}
|
||||
visibleEntities={{
|
||||
// The reason we are not passing in initPageResult.visibleEntities directly is due to a "Cannot assign to read only property of object '#<Object>" error introduced in React 19
|
||||
// which this caused as soon as initPageResult.visibleEntities is passed in
|
||||
collections: initPageResult.visibleEntities?.collections,
|
||||
globals: initPageResult.visibleEntities?.globals,
|
||||
}}
|
||||
>
|
||||
{RenderedView}
|
||||
</DefaultTemplate>
|
||||
|
||||
@@ -17,6 +17,30 @@ export const withPayload = (nextConfig = {}) => {
|
||||
],
|
||||
},
|
||||
},
|
||||
headers: async () => {
|
||||
const headersFromConfig = 'headers' in nextConfig ? await nextConfig.headers() : []
|
||||
|
||||
return [
|
||||
...(headersFromConfig || []),
|
||||
{
|
||||
source: '/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'Accept-CH',
|
||||
value: 'Sec-CH-Prefers-Color-Scheme',
|
||||
},
|
||||
{
|
||||
key: 'Vary',
|
||||
value: 'Sec-CH-Prefers-Color-Scheme',
|
||||
},
|
||||
{
|
||||
key: 'Critical-CH',
|
||||
value: 'Sec-CH-Prefers-Color-Scheme',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
serverExternalPackages: [
|
||||
...(nextConfig?.serverExternalPackages || []),
|
||||
'drizzle-kit',
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"description": "Node, React, Headless CMS and Application Framework built on Next.js",
|
||||
"keywords": [
|
||||
"admin panel",
|
||||
@@ -108,7 +108,6 @@
|
||||
"pino-pretty": "10.2.0",
|
||||
"pluralize": "8.0.0",
|
||||
"sanitize-filename": "1.6.3",
|
||||
"scheduler": "0.23.0",
|
||||
"scmp": "2.1.0",
|
||||
"ts-essentials": "7.0.3",
|
||||
"uuid": "^9.0.1"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { I18nClient } from '@payloadcms/translations'
|
||||
import type { GenericLanguages, I18n, I18nClient } from '@payloadcms/translations'
|
||||
import type { JSONSchema4 } from 'json-schema'
|
||||
import type React from 'react'
|
||||
|
||||
@@ -28,11 +28,12 @@ type RichTextAdapterBase<
|
||||
}) => Map<string, React.ReactNode>
|
||||
generateSchemaMap?: (args: {
|
||||
config: SanitizedConfig
|
||||
i18n: I18nClient
|
||||
i18n: I18n<any, any>
|
||||
schemaMap: Map<string, Field[]>
|
||||
schemaPath: string
|
||||
}) => Map<string, Field[]>
|
||||
hooks?: FieldBase['hooks']
|
||||
i18n?: Partial<GenericLanguages>
|
||||
outputSchema?: ({
|
||||
collectionIDFieldTypes,
|
||||
config,
|
||||
|
||||
@@ -66,7 +66,7 @@ export async function sendVerificationEmail(args: Args): Promise<void> {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
email.sendEmail({
|
||||
from: `"${email.defaultFromName}" <${email.defaultFromName}>`,
|
||||
from: `"${email.defaultFromName}" <${email.defaultFromAddress}>`,
|
||||
html,
|
||||
subject,
|
||||
to: user.email,
|
||||
|
||||
@@ -121,6 +121,7 @@ export const createOperation = async <TSlug extends keyof GeneratedTypes['collec
|
||||
collection,
|
||||
config,
|
||||
data,
|
||||
operation: 'create',
|
||||
overwriteExistingFiles,
|
||||
req,
|
||||
throwOnMissingFile:
|
||||
|
||||
@@ -156,6 +156,7 @@ export const updateOperation = async <TSlug extends keyof GeneratedTypes['collec
|
||||
collection,
|
||||
config,
|
||||
data: bulkUpdateData,
|
||||
operation: 'update',
|
||||
overwriteExistingFiles,
|
||||
req,
|
||||
throwOnMissingFile: false,
|
||||
|
||||
@@ -147,6 +147,7 @@ export const updateByIDOperation = async <TSlug extends keyof GeneratedTypes['co
|
||||
collection,
|
||||
config,
|
||||
data,
|
||||
operation: 'update',
|
||||
overwriteExistingFiles,
|
||||
req,
|
||||
throwOnMissingFile: false,
|
||||
|
||||
@@ -17,6 +17,7 @@ import { InvalidConfiguration } from '../errors/index.js'
|
||||
import { sanitizeGlobals } from '../globals/config/sanitize.js'
|
||||
import getPreferencesCollection from '../preferences/preferencesCollection.js'
|
||||
import checkDuplicateCollections from '../utilities/checkDuplicateCollections.js'
|
||||
import { deepMerge } from '../utilities/deepMerge.js'
|
||||
import { isPlainObject } from '../utilities/isPlainObject.js'
|
||||
import { defaults } from './defaults.js'
|
||||
|
||||
@@ -154,6 +155,9 @@ export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedC
|
||||
config: config as SanitizedConfig,
|
||||
isRoot: true,
|
||||
})
|
||||
if (config.editor.i18n && Object.keys(config.editor.i18n).length >= 0) {
|
||||
config.i18n.translations = deepMerge(config.i18n.translations, config.editor.i18n)
|
||||
}
|
||||
}
|
||||
|
||||
const promises: Promise<void>[] = []
|
||||
|
||||
@@ -9,6 +9,7 @@ import type GraphQL from 'graphql'
|
||||
import type { Metadata as NextMetadata } from 'next'
|
||||
import type { DestinationStream, LoggerOptions } from 'pino'
|
||||
import type React from 'react'
|
||||
import type { JSX } from 'react'
|
||||
import type { default as sharp } from 'sharp'
|
||||
import type { DeepRequired } from 'ts-essentials'
|
||||
|
||||
|
||||
1
packages/payload/src/exports/i18n/he.ts
Normal file
1
packages/payload/src/exports/i18n/he.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { he } from '@payloadcms/translations/languages/he'
|
||||
@@ -12,7 +12,7 @@ export { traverseFields as beforeValidateTraverseFields } from '../fields/hooks/
|
||||
|
||||
export { formatFilesize } from '../uploads/formatFilesize.js'
|
||||
|
||||
export { default as isImage } from '../uploads/isImage.js'
|
||||
export { isImage } from '../uploads/isImage.js'
|
||||
|
||||
export { combineMerge } from '../utilities/combineMerge.js'
|
||||
export {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
InvalidFieldRelationship,
|
||||
MissingFieldType,
|
||||
} from '../../errors/index.js'
|
||||
import { deepMerge } from '../../utilities/deepMerge.js'
|
||||
import { formatLabels, toWords } from '../../utilities/formatLabels.js'
|
||||
import { baseBlockFields } from '../baseFields/baseBlockFields.js'
|
||||
import { baseIDField } from '../baseFields/baseIDField.js'
|
||||
@@ -168,6 +169,10 @@ export const sanitizeFields = async ({
|
||||
})
|
||||
}
|
||||
|
||||
if (field.editor.i18n && Object.keys(field.editor.i18n).length >= 0) {
|
||||
config.i18n.translations = deepMerge(config.i18n.translations, field.editor.i18n)
|
||||
}
|
||||
|
||||
// Add editor adapter hooks to field hooks
|
||||
if (!field.hooks) field.hooks = {}
|
||||
|
||||
|
||||
@@ -7,19 +7,6 @@ import type { GeneratedTypes } from '../index.js'
|
||||
import type { validOperators } from './constants.js'
|
||||
export type { Payload as Payload } from '../index.js'
|
||||
|
||||
export type UploadEdits = {
|
||||
crop?: {
|
||||
height?: number
|
||||
width?: number
|
||||
x?: number
|
||||
y?: number
|
||||
}
|
||||
focalPoint?: {
|
||||
x?: number
|
||||
y?: number
|
||||
}
|
||||
}
|
||||
|
||||
export type CustomPayloadRequestProperties = {
|
||||
context: RequestContext
|
||||
/** The locale that should be used for a field when it is not translated to the requested locale */
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export default function canResizeImage(mimeType: string): boolean {
|
||||
export function canResizeImage(mimeType: string): boolean {
|
||||
return ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/tiff'].indexOf(mimeType) > -1
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ export const percentToPixel = (value, dimension) => {
|
||||
return Math.floor((parseFloat(value) / 100) * dimension)
|
||||
}
|
||||
|
||||
export default async function cropImage({ cropData, dimensions, file, sharp }) {
|
||||
export async function cropImage({ cropData, dimensions, file, sharp }) {
|
||||
try {
|
||||
const { height, width, x, y } = cropData
|
||||
|
||||
|
||||
@@ -9,22 +9,24 @@ import sanitize from 'sanitize-filename'
|
||||
import type { Collection } from '../collections/config/types.js'
|
||||
import type { SanitizedConfig } from '../config/types.js'
|
||||
import type { PayloadRequestWithData } from '../types/index.js'
|
||||
import type { FileData, FileToSave, ProbedImageSize } from './types.js'
|
||||
import type { FileData, FileToSave, ProbedImageSize, UploadEdits } from './types.js'
|
||||
|
||||
import { FileRetrievalError, FileUploadError, MissingFile } from '../errors/index.js'
|
||||
import canResizeImage from './canResizeImage.js'
|
||||
import cropImage from './cropImage.js'
|
||||
import { canResizeImage } from './canResizeImage.js'
|
||||
import { cropImage } from './cropImage.js'
|
||||
import { getExternalFile } from './getExternalFile.js'
|
||||
import { getFileByPath } from './getFileByPath.js'
|
||||
import { getImageSize } from './getImageSize.js'
|
||||
import getSafeFileName from './getSafeFilename.js'
|
||||
import resizeAndTransformImageSizes from './imageResizer.js'
|
||||
import isImage from './isImage.js'
|
||||
import { getSafeFileName } from './getSafeFilename.js'
|
||||
import { resizeAndTransformImageSizes } from './imageResizer.js'
|
||||
import { isImage } from './isImage.js'
|
||||
|
||||
type Args<T> = {
|
||||
collection: Collection
|
||||
config: SanitizedConfig
|
||||
data: T
|
||||
operation: 'create' | 'update'
|
||||
originalDoc?: T
|
||||
overwriteExistingFiles?: boolean
|
||||
req: PayloadRequestWithData
|
||||
throwOnMissingFile?: boolean
|
||||
@@ -38,6 +40,8 @@ type Result<T> = Promise<{
|
||||
export const generateFileData = async <T>({
|
||||
collection: { config: collectionConfig },
|
||||
data,
|
||||
operation,
|
||||
originalDoc,
|
||||
overwriteExistingFiles,
|
||||
req,
|
||||
throwOnMissingFile,
|
||||
@@ -53,10 +57,22 @@ export const generateFileData = async <T>({
|
||||
|
||||
let file = req.file
|
||||
|
||||
const uploadEdits = req.query['uploadEdits'] || {}
|
||||
const uploadEdits = parseUploadEditsFromReqOrIncomingData({
|
||||
data,
|
||||
operation,
|
||||
originalDoc,
|
||||
req,
|
||||
})
|
||||
|
||||
const { disableLocalStorage, formatOptions, imageSizes, resizeOptions, staticDir, trimOptions } =
|
||||
collectionConfig.upload
|
||||
const {
|
||||
disableLocalStorage,
|
||||
focalPoint: focalPointEnabled = true,
|
||||
formatOptions,
|
||||
imageSizes,
|
||||
resizeOptions,
|
||||
staticDir,
|
||||
trimOptions,
|
||||
} = collectionConfig.upload
|
||||
|
||||
const staticPath = staticDir
|
||||
|
||||
@@ -228,9 +244,9 @@ export const generateFileData = async <T>({
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(imageSizes) && fileSupportsResize && sharp) {
|
||||
if (fileSupportsResize && (Array.isArray(imageSizes) || focalPointEnabled !== false)) {
|
||||
req.payloadUploadSizes = {}
|
||||
const { sizeData, sizesToSave } = await resizeAndTransformImageSizes({
|
||||
const { focalPoint, sizeData, sizesToSave } = await resizeAndTransformImageSizes({
|
||||
config: collectionConfig,
|
||||
dimensions: !cropData
|
||||
? dimensions
|
||||
@@ -245,13 +261,16 @@ export const generateFileData = async <T>({
|
||||
savedFilename: fsSafeName || file.name,
|
||||
sharp,
|
||||
staticPath,
|
||||
uploadEdits,
|
||||
})
|
||||
|
||||
fileData.sizes = sizeData
|
||||
fileData.focalX = focalPoint?.x
|
||||
fileData.focalY = focalPoint?.y
|
||||
filesToSave.push(...sizesToSave)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
req.payload.logger.error(err)
|
||||
throw new FileUploadError(req.t)
|
||||
}
|
||||
|
||||
@@ -265,3 +284,50 @@ export const generateFileData = async <T>({
|
||||
files: filesToSave,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse upload edits from req or incoming data
|
||||
*/
|
||||
function parseUploadEditsFromReqOrIncomingData(args: {
|
||||
data: unknown
|
||||
operation: 'create' | 'update'
|
||||
originalDoc: unknown
|
||||
req: PayloadRequestWithData
|
||||
}): UploadEdits {
|
||||
const { data, operation, originalDoc, req } = args
|
||||
|
||||
// Get intended focal point change from query string or incoming data
|
||||
const {
|
||||
uploadEdits = {},
|
||||
}: {
|
||||
uploadEdits?: UploadEdits
|
||||
} = req.query || {}
|
||||
|
||||
if (uploadEdits.focalPoint) return uploadEdits
|
||||
|
||||
const incomingData = data as FileData
|
||||
const origDoc = originalDoc as FileData
|
||||
|
||||
// If no change in focal point, return undefined.
|
||||
// This prevents a refocal operation triggered from admin, because it always sends the focal point.
|
||||
if (origDoc && incomingData.focalX === origDoc.focalX && incomingData.focalY === origDoc.focalY) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (incomingData?.focalX && incomingData?.focalY) {
|
||||
uploadEdits.focalPoint = {
|
||||
x: incomingData.focalX,
|
||||
y: incomingData.focalY,
|
||||
}
|
||||
return uploadEdits
|
||||
}
|
||||
|
||||
// If no focal point is set, default to center
|
||||
if (operation === 'create') {
|
||||
uploadEdits.focalPoint = {
|
||||
x: 50,
|
||||
y: 50,
|
||||
}
|
||||
}
|
||||
return uploadEdits
|
||||
}
|
||||
|
||||
@@ -149,6 +149,25 @@ export const getBaseUploadFields = ({ collection, config }: Options): Field[] =>
|
||||
height,
|
||||
]
|
||||
|
||||
// Add focal point fields if not disabled
|
||||
if (
|
||||
uploadOptions.focalPoint !== false ||
|
||||
uploadOptions.imageSizes ||
|
||||
uploadOptions.resizeOptions
|
||||
) {
|
||||
uploadFields = uploadFields.concat(
|
||||
['focalX', 'focalY'].map((name) => {
|
||||
return {
|
||||
name,
|
||||
type: 'number',
|
||||
admin: {
|
||||
hidden: true,
|
||||
},
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
if (uploadOptions.mimeTypes) {
|
||||
mimeType.validate = mimeTypeValidator(uploadOptions.mimeTypes)
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ type Args = {
|
||||
staticPath: string
|
||||
}
|
||||
|
||||
async function getSafeFileName({
|
||||
export async function getSafeFileName({
|
||||
collectionSlug,
|
||||
desiredFilename,
|
||||
req,
|
||||
@@ -51,5 +51,3 @@ async function getSafeFileName({
|
||||
}
|
||||
return modifiedFilename
|
||||
}
|
||||
|
||||
export default getSafeFileName
|
||||
|
||||
@@ -8,8 +8,15 @@ import sanitize from 'sanitize-filename'
|
||||
|
||||
import type { SanitizedCollectionConfig } from '../collections/config/types.js'
|
||||
import type { SharpDependency } from '../config/types.js'
|
||||
import type { PayloadRequestWithData, UploadEdits } from '../types/index.js'
|
||||
import type { FileSize, FileSizes, FileToSave, ImageSize, ProbedImageSize } from './types.js'
|
||||
import type { PayloadRequestWithData } from '../types/index.js'
|
||||
import type {
|
||||
FileSize,
|
||||
FileSizes,
|
||||
FileToSave,
|
||||
ImageSize,
|
||||
ProbedImageSize,
|
||||
UploadEdits,
|
||||
} from './types.js'
|
||||
|
||||
import { isNumber } from '../utilities/isNumber.js'
|
||||
import fileExists from './fileExists.js'
|
||||
@@ -19,18 +26,16 @@ type ResizeArgs = {
|
||||
dimensions: ProbedImageSize
|
||||
file: PayloadRequestWithData['file']
|
||||
mimeType: string
|
||||
req: PayloadRequestWithData & {
|
||||
query?: {
|
||||
uploadEdits?: UploadEdits
|
||||
}
|
||||
}
|
||||
req: PayloadRequestWithData
|
||||
savedFilename: string
|
||||
sharp: SharpDependency
|
||||
sharp?: SharpDependency
|
||||
staticPath: string
|
||||
uploadEdits?: UploadEdits
|
||||
}
|
||||
|
||||
/** Result from resizing and transforming the requested image sizes */
|
||||
type ImageSizesResult = {
|
||||
focalPoint?: UploadEdits['focalPoint']
|
||||
sizeData: FileSizes
|
||||
sizesToSave: FileToSave[]
|
||||
}
|
||||
@@ -71,6 +76,16 @@ const createImageName = (
|
||||
extension: string,
|
||||
) => `${outputImageName}-${width}x${height}.${extension}`
|
||||
|
||||
type CreateResultArgs = {
|
||||
filename?: FileSize['filename']
|
||||
filesize?: FileSize['filesize']
|
||||
height?: FileSize['height']
|
||||
mimeType?: FileSize['mimeType']
|
||||
name: string
|
||||
sizesToSave?: FileToSave[]
|
||||
width?: FileSize['width']
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the result object for the image resize operation based on the
|
||||
* provided parameters. If the name is not provided, an empty result object
|
||||
@@ -85,26 +100,28 @@ const createImageName = (
|
||||
* @param sizesToSave - the sizes to save
|
||||
* @returns the result object
|
||||
*/
|
||||
const createResult = (
|
||||
name: string,
|
||||
filename: FileSize['filename'] = null,
|
||||
width: FileSize['width'] = null,
|
||||
height: FileSize['height'] = null,
|
||||
filesize: FileSize['filesize'] = null,
|
||||
mimeType: FileSize['mimeType'] = null,
|
||||
sizesToSave: FileToSave[] = [],
|
||||
): ImageSizesResult => ({
|
||||
sizeData: {
|
||||
[name]: {
|
||||
filename,
|
||||
filesize,
|
||||
height,
|
||||
mimeType,
|
||||
width,
|
||||
const createResult = ({
|
||||
name,
|
||||
filename = null,
|
||||
filesize = null,
|
||||
height = null,
|
||||
mimeType = null,
|
||||
sizesToSave = [],
|
||||
width = null,
|
||||
}: CreateResultArgs): ImageSizesResult => {
|
||||
return {
|
||||
sizeData: {
|
||||
[name]: {
|
||||
filename,
|
||||
filesize,
|
||||
height,
|
||||
mimeType,
|
||||
width,
|
||||
},
|
||||
},
|
||||
},
|
||||
sizesToSave,
|
||||
})
|
||||
sizesToSave,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the image needs to be resized according to the requested dimensions
|
||||
@@ -208,7 +225,7 @@ const sanitizeResizeConfig = (resizeConfig: ImageSize): ImageSize => {
|
||||
* @param resizeConfig - the resize config
|
||||
* @returns the result of the resize operation(s)
|
||||
*/
|
||||
export default async function resizeAndTransformImageSizes({
|
||||
export async function resizeAndTransformImageSizes({
|
||||
config,
|
||||
dimensions,
|
||||
file,
|
||||
@@ -217,10 +234,27 @@ export default async function resizeAndTransformImageSizes({
|
||||
savedFilename,
|
||||
sharp,
|
||||
staticPath,
|
||||
uploadEdits,
|
||||
}: ResizeArgs): Promise<ImageSizesResult> {
|
||||
const { imageSizes } = config.upload
|
||||
// Noting to resize here so return as early as possible
|
||||
if (!imageSizes) return { sizeData: {}, sizesToSave: [] }
|
||||
const { focalPoint: focalPointEnabled = true, imageSizes } = config.upload
|
||||
|
||||
// Focal point adjustments
|
||||
const incomingFocalPoint = uploadEdits.focalPoint
|
||||
? {
|
||||
x: isNumber(uploadEdits.focalPoint.x) ? Math.round(uploadEdits.focalPoint.x) : 50,
|
||||
y: isNumber(uploadEdits.focalPoint.y) ? Math.round(uploadEdits.focalPoint.y) : 50,
|
||||
}
|
||||
: undefined
|
||||
|
||||
const defaultResult: ImageSizesResult = {
|
||||
...(focalPointEnabled && incomingFocalPoint && { focalPoint: incomingFocalPoint }),
|
||||
sizeData: {},
|
||||
sizesToSave: [],
|
||||
}
|
||||
|
||||
if (!imageSizes || !sharp) {
|
||||
return defaultResult
|
||||
}
|
||||
|
||||
const sharpBase = sharp(file.tempFilePath || file.data).rotate() // pass rotate() to auto-rotate based on EXIF data. https://github.com/payloadcms/payload/pull/3081
|
||||
|
||||
@@ -232,16 +266,13 @@ export default async function resizeAndTransformImageSizes({
|
||||
// skipped COMPLETELY and thus will not be included in the resulting images.
|
||||
// All further format/trim options will thus be skipped as well.
|
||||
if (preventResize(imageResizeConfig, dimensions)) {
|
||||
return createResult(imageResizeConfig.name)
|
||||
return createResult({ name: imageResizeConfig.name })
|
||||
}
|
||||
|
||||
const imageToResize = sharpBase.clone()
|
||||
let resized = imageToResize
|
||||
|
||||
if (
|
||||
req.query?.uploadEdits?.focalPoint &&
|
||||
applyPayloadAdjustments(imageResizeConfig, dimensions)
|
||||
) {
|
||||
if (incomingFocalPoint && applyPayloadAdjustments(imageResizeConfig, dimensions)) {
|
||||
const { height: resizeHeight, width: resizeWidth } = imageResizeConfig
|
||||
const resizeAspectRatio = resizeWidth / resizeHeight
|
||||
const originalAspectRatio = dimensions.width / dimensions.height
|
||||
@@ -254,27 +285,17 @@ export default async function resizeAndTransformImageSizes({
|
||||
})
|
||||
const { info: scaledImageInfo } = await scaledImage.toBuffer({ resolveWithObject: true })
|
||||
|
||||
// Focal point adjustments
|
||||
const focalPoint = {
|
||||
x: isNumber(req.query.uploadEdits.focalPoint?.x)
|
||||
? req.query.uploadEdits.focalPoint.x
|
||||
: 50,
|
||||
y: isNumber(req.query.uploadEdits.focalPoint?.y)
|
||||
? req.query.uploadEdits.focalPoint.y
|
||||
: 50,
|
||||
}
|
||||
|
||||
const safeResizeWidth = resizeWidth ?? scaledImageInfo.width
|
||||
const maxOffsetX = scaledImageInfo.width - safeResizeWidth
|
||||
const leftFocalEdge = Math.round(
|
||||
scaledImageInfo.width * (focalPoint.x / 100) - safeResizeWidth / 2,
|
||||
scaledImageInfo.width * (incomingFocalPoint.x / 100) - safeResizeWidth / 2,
|
||||
)
|
||||
const safeOffsetX = Math.min(Math.max(0, leftFocalEdge), maxOffsetX)
|
||||
|
||||
const safeResizeHeight = resizeHeight ?? scaledImageInfo.height
|
||||
const maxOffsetY = scaledImageInfo.height - safeResizeHeight
|
||||
const topFocalEdge = Math.round(
|
||||
scaledImageInfo.height * (focalPoint.y / 100) - safeResizeHeight / 2,
|
||||
scaledImageInfo.height * (incomingFocalPoint.y / 100) - safeResizeHeight / 2,
|
||||
)
|
||||
const safeOffsetY = Math.min(Math.max(0, topFocalEdge), maxOffsetY)
|
||||
|
||||
@@ -306,7 +327,9 @@ export default async function resizeAndTransformImageSizes({
|
||||
|
||||
const sanitizedImage = getSanitizedImageData(savedFilename)
|
||||
|
||||
req.payloadUploadSizes[imageResizeConfig.name] = bufferData
|
||||
if (req.payloadUploadSizes) {
|
||||
req.payloadUploadSizes[imageResizeConfig.name] = bufferData
|
||||
}
|
||||
|
||||
const mimeInfo = await fromBuffer(bufferData)
|
||||
|
||||
@@ -327,15 +350,15 @@ export default async function resizeAndTransformImageSizes({
|
||||
}
|
||||
|
||||
const { height, size, width } = bufferInfo
|
||||
return createResult(
|
||||
imageResizeConfig.name,
|
||||
imageNameWithDimensions,
|
||||
width,
|
||||
return createResult({
|
||||
name: imageResizeConfig.name,
|
||||
filename: imageNameWithDimensions,
|
||||
filesize: size,
|
||||
height,
|
||||
size,
|
||||
mimeInfo?.mime || mimeType,
|
||||
[{ buffer: bufferData, path: imagePath }],
|
||||
)
|
||||
mimeType: mimeInfo?.mime || mimeType,
|
||||
sizesToSave: [{ buffer: bufferData, path: imagePath }],
|
||||
width,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -345,6 +368,6 @@ export default async function resizeAndTransformImageSizes({
|
||||
acc.sizesToSave.push(...result.sizesToSave)
|
||||
return acc
|
||||
},
|
||||
{ sizeData: {}, sizesToSave: [] },
|
||||
{ ...defaultResult },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export default function isImage(mimeType: string): boolean {
|
||||
export function isImage(mimeType: string): boolean {
|
||||
return (
|
||||
['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/webp', 'image/avif'].indexOf(
|
||||
mimeType,
|
||||
|
||||
@@ -18,6 +18,8 @@ export type FileSizes = {
|
||||
export type FileData = {
|
||||
filename: string
|
||||
filesize: number
|
||||
focalX?: number
|
||||
focalY?: number
|
||||
height: number
|
||||
mimeType: string
|
||||
sizes: FileSizes
|
||||
@@ -117,3 +119,16 @@ export type FileToSave = {
|
||||
buffer: Buffer
|
||||
path: string
|
||||
}
|
||||
|
||||
export type UploadEdits = {
|
||||
crop?: {
|
||||
height?: number
|
||||
width?: number
|
||||
x?: number
|
||||
y?: number
|
||||
}
|
||||
focalPoint?: {
|
||||
x?: number
|
||||
y?: number
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-cloud-storage",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"description": "The official cloud storage plugin for Payload CMS",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -8,6 +8,15 @@
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-cloud",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"description": "The official Payload Cloud plugin",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
@@ -35,7 +35,7 @@
|
||||
"@aws-sdk/client-s3": "^3.525.0",
|
||||
"@aws-sdk/credential-providers": "^3.525.0",
|
||||
"@aws-sdk/lib-storage": "^3.525.0",
|
||||
"@payloadcms/email-nodemailer": "workspace:^",
|
||||
"@payloadcms/email-nodemailer": "workspace:*",
|
||||
"amazon-cognito-identity-js": "^6.1.2",
|
||||
"nodemailer": "6.9.10",
|
||||
"resend": "^0.17.2"
|
||||
|
||||
@@ -8,6 +8,15 @@
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-form-builder",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"description": "Form builder plugin for Payload CMS",
|
||||
"keywords": [
|
||||
"payload",
|
||||
@@ -55,7 +55,8 @@
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/escape-html": "^1.0.4",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/react": "18.3.2",
|
||||
"@types/react": "npm:types-react@19.0.0-beta.2",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
|
||||
"copyfiles": "^2.4.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"nodemon": "3.0.3",
|
||||
@@ -64,8 +65,8 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"payload": "workspace:*",
|
||||
"react": "^18.2.0 || ^19.0.0",
|
||||
"react-dom": "^18.2.0 || ^19.0.0"
|
||||
"react": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522",
|
||||
"react-dom": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
@@ -84,5 +85,9 @@
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"homepage:": "https://payloadcms.com"
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"overrides": {
|
||||
"@types/react": "npm:types-react@19.0.0-beta.2",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
import type { CollectionConfig, Field } from 'payload/types'
|
||||
|
||||
import type { FormBuilderPluginConfig } from '../../types.js'
|
||||
|
||||
@@ -11,6 +11,74 @@ export const generateSubmissionCollection = (
|
||||
): CollectionConfig => {
|
||||
const formSlug = formConfig?.formOverrides?.slug || 'forms'
|
||||
|
||||
const defaultFields: Field[] = [
|
||||
{
|
||||
name: 'form',
|
||||
type: 'relationship',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
relationTo: formSlug,
|
||||
required: true,
|
||||
validate: async (value, { req: { payload }, req }) => {
|
||||
/* Don't run in the client side */
|
||||
if (!payload) return true
|
||||
|
||||
if (payload) {
|
||||
let _existingForm
|
||||
|
||||
try {
|
||||
_existingForm = await payload.findByID({
|
||||
id: value,
|
||||
collection: formSlug,
|
||||
req,
|
||||
})
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
return 'Cannot create this submission because this form does not exist.'
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'submissionData',
|
||||
type: 'array',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'field',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
type: 'text',
|
||||
required: true,
|
||||
validate: (value: unknown) => {
|
||||
// TODO:
|
||||
// create a validation function that dynamically
|
||||
// relies on the field type and its options as configured.
|
||||
|
||||
// How to access sibling data from this field?
|
||||
// Need the `name` of the field in order to validate it.
|
||||
|
||||
// Might not be possible to use this validation function.
|
||||
// Instead, might need to do all validation in a `beforeValidate` collection hook.
|
||||
|
||||
if (typeof value !== 'undefined') {
|
||||
return true
|
||||
}
|
||||
|
||||
return 'This field is required.'
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const newConfig: CollectionConfig = {
|
||||
...(formConfig?.formSubmissionOverrides || {}),
|
||||
slug: formConfig?.formSubmissionOverrides?.slug || 'form-submissions',
|
||||
@@ -24,74 +92,11 @@ export const generateSubmissionCollection = (
|
||||
...(formConfig?.formSubmissionOverrides?.admin || {}),
|
||||
enableRichTextRelationship: false,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'form',
|
||||
type: 'relationship',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
relationTo: formSlug,
|
||||
required: true,
|
||||
validate: async (value, { req: { payload }, req }) => {
|
||||
/* Don't run in the client side */
|
||||
if (!payload) return true
|
||||
|
||||
if (payload) {
|
||||
let _existingForm
|
||||
|
||||
try {
|
||||
_existingForm = await payload.findByID({
|
||||
id: value,
|
||||
collection: formSlug,
|
||||
req,
|
||||
})
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
return 'Cannot create this submission because this form does not exist.'
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'submissionData',
|
||||
type: 'array',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'field',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
type: 'text',
|
||||
required: true,
|
||||
validate: (value: unknown) => {
|
||||
// TODO:
|
||||
// create a validation function that dynamically
|
||||
// relies on the field type and its options as configured.
|
||||
|
||||
// How to access sibling data from this field?
|
||||
// Need the `name` of the field in order to validate it.
|
||||
|
||||
// Might not be possible to use this validation function.
|
||||
// Instead, might need to do all validation in a `beforeValidate` collection hook.
|
||||
|
||||
if (typeof value !== 'undefined') {
|
||||
return true
|
||||
}
|
||||
|
||||
return 'This field is required.'
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
...(formConfig?.formSubmissionOverrides?.fields || []),
|
||||
],
|
||||
fields:
|
||||
formConfig?.formSubmissionOverrides?.fields &&
|
||||
typeof formConfig?.formSubmissionOverrides?.fields === 'function'
|
||||
? formConfig?.formSubmissionOverrides?.fields({ defaultFields })
|
||||
: defaultFields,
|
||||
hooks: {
|
||||
...(formConfig?.formSubmissionOverrides?.hooks || {}),
|
||||
beforeChange: [
|
||||
|
||||
@@ -64,6 +64,159 @@ export const generateFormCollection = (formConfig: FormBuilderPluginConfig): Col
|
||||
}
|
||||
}
|
||||
|
||||
const defaultFields: Field[] = [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'fields',
|
||||
type: 'blocks',
|
||||
blocks: Object.entries(formConfig?.fields || {})
|
||||
.map(([fieldKey, fieldConfig]) => {
|
||||
// let the config enable/disable fields with either boolean values or objects
|
||||
if (fieldConfig !== false) {
|
||||
const block = fields[fieldKey]
|
||||
|
||||
if (block === undefined && typeof fieldConfig === 'object') {
|
||||
return fieldConfig
|
||||
}
|
||||
|
||||
if (typeof block === 'object' && typeof fieldConfig === 'object') {
|
||||
return merge<FieldConfig>(block, fieldConfig, {
|
||||
arrayMerge: (_, sourceArray) => sourceArray,
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof block === 'function') {
|
||||
return block(fieldConfig)
|
||||
}
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
.filter(Boolean) as Block[],
|
||||
},
|
||||
{
|
||||
name: 'submitButtonLabel',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'confirmationType',
|
||||
type: 'radio',
|
||||
admin: {
|
||||
description:
|
||||
'Choose whether to display an on-page message or redirect to a different page after they submit the form.',
|
||||
layout: 'horizontal',
|
||||
},
|
||||
defaultValue: 'message',
|
||||
options: [
|
||||
{
|
||||
label: 'Message',
|
||||
value: 'message',
|
||||
},
|
||||
{
|
||||
label: 'Redirect',
|
||||
value: 'redirect',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'confirmationMessage',
|
||||
type: 'richText',
|
||||
admin: {
|
||||
condition: (_, siblingData) => siblingData?.confirmationType === 'message',
|
||||
},
|
||||
localized: true,
|
||||
required: true,
|
||||
},
|
||||
redirect,
|
||||
{
|
||||
name: 'emails',
|
||||
type: 'array',
|
||||
admin: {
|
||||
description:
|
||||
"Send custom emails when the form submits. Use comma separated lists to send the same email to multiple recipients. To reference a value from this form, wrap that field's name with double curly brackets, i.e. {{firstName}}.",
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'emailTo',
|
||||
type: 'text',
|
||||
admin: {
|
||||
placeholder: '"Email Sender" <sender@email.com>',
|
||||
width: '100%',
|
||||
},
|
||||
label: 'Email To',
|
||||
},
|
||||
{
|
||||
name: 'cc',
|
||||
type: 'text',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'CC',
|
||||
},
|
||||
{
|
||||
name: 'bcc',
|
||||
type: 'text',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'BCC',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'replyTo',
|
||||
type: 'text',
|
||||
admin: {
|
||||
placeholder: '"Reply To" <reply-to@email.com>',
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Reply To',
|
||||
},
|
||||
{
|
||||
name: 'emailFrom',
|
||||
type: 'text',
|
||||
admin: {
|
||||
placeholder: '"Email From" <email-from@email.com>',
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Email From',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'subject',
|
||||
type: 'text',
|
||||
defaultValue: "You've received a new message.",
|
||||
label: 'Subject',
|
||||
localized: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'message',
|
||||
type: 'richText',
|
||||
admin: {
|
||||
description: 'Enter the message that should be sent in this email.',
|
||||
},
|
||||
label: 'Message',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const config: CollectionConfig = {
|
||||
...(formConfig?.formOverrides || {}),
|
||||
slug: formConfig?.formOverrides?.slug || 'forms',
|
||||
@@ -76,159 +229,10 @@ export const generateFormCollection = (formConfig: FormBuilderPluginConfig): Col
|
||||
useAsTitle: 'title',
|
||||
...(formConfig?.formOverrides?.admin || {}),
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'fields',
|
||||
type: 'blocks',
|
||||
blocks: Object.entries(formConfig?.fields || {})
|
||||
.map(([fieldKey, fieldConfig]) => {
|
||||
// let the config enable/disable fields with either boolean values or objects
|
||||
if (fieldConfig !== false) {
|
||||
const block = fields[fieldKey]
|
||||
|
||||
if (block === undefined && typeof fieldConfig === 'object') {
|
||||
return fieldConfig
|
||||
}
|
||||
|
||||
if (typeof block === 'object' && typeof fieldConfig === 'object') {
|
||||
return merge<FieldConfig>(block, fieldConfig, {
|
||||
arrayMerge: (_, sourceArray) => sourceArray,
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof block === 'function') {
|
||||
return block(fieldConfig)
|
||||
}
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
.filter(Boolean) as Block[],
|
||||
},
|
||||
{
|
||||
name: 'submitButtonLabel',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'confirmationType',
|
||||
type: 'radio',
|
||||
admin: {
|
||||
description:
|
||||
'Choose whether to display an on-page message or redirect to a different page after they submit the form.',
|
||||
layout: 'horizontal',
|
||||
},
|
||||
defaultValue: 'message',
|
||||
options: [
|
||||
{
|
||||
label: 'Message',
|
||||
value: 'message',
|
||||
},
|
||||
{
|
||||
label: 'Redirect',
|
||||
value: 'redirect',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'confirmationMessage',
|
||||
type: 'richText',
|
||||
admin: {
|
||||
condition: (_, siblingData) => siblingData?.confirmationType === 'message',
|
||||
},
|
||||
localized: true,
|
||||
required: true,
|
||||
},
|
||||
redirect,
|
||||
{
|
||||
name: 'emails',
|
||||
type: 'array',
|
||||
admin: {
|
||||
description:
|
||||
"Send custom emails when the form submits. Use comma separated lists to send the same email to multiple recipients. To reference a value from this form, wrap that field's name with double curly brackets, i.e. {{firstName}}.",
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'emailTo',
|
||||
type: 'text',
|
||||
admin: {
|
||||
placeholder: '"Email Sender" <sender@email.com>',
|
||||
width: '100%',
|
||||
},
|
||||
label: 'Email To',
|
||||
},
|
||||
{
|
||||
name: 'cc',
|
||||
type: 'text',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'CC',
|
||||
},
|
||||
{
|
||||
name: 'bcc',
|
||||
type: 'text',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'BCC',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'replyTo',
|
||||
type: 'text',
|
||||
admin: {
|
||||
placeholder: '"Reply To" <reply-to@email.com>',
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Reply To',
|
||||
},
|
||||
{
|
||||
name: 'emailFrom',
|
||||
type: 'text',
|
||||
admin: {
|
||||
placeholder: '"Email From" <email-from@email.com>',
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Email From',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'subject',
|
||||
type: 'text',
|
||||
defaultValue: "You've received a new message.",
|
||||
label: 'Subject',
|
||||
localized: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'message',
|
||||
type: 'richText',
|
||||
admin: {
|
||||
description: 'Enter the message that should be sent in this email.',
|
||||
},
|
||||
label: 'Message',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
...(formConfig?.formOverrides?.fields || []),
|
||||
],
|
||||
fields:
|
||||
formConfig?.formOverrides.fields && typeof formConfig?.formOverrides.fields === 'function'
|
||||
? formConfig?.formOverrides.fields({ defaultFields })
|
||||
: defaultFields,
|
||||
}
|
||||
|
||||
return config
|
||||
|
||||
@@ -30,20 +30,6 @@ export const formBuilderPlugin =
|
||||
|
||||
return {
|
||||
...config,
|
||||
// admin: {
|
||||
// ...config.admin,
|
||||
// webpack: (webpackConfig) => ({
|
||||
// ...webpackConfig,
|
||||
// resolve: {
|
||||
// ...webpackConfig.resolve,
|
||||
// alias: {
|
||||
// ...webpackConfig.resolve.alias,
|
||||
// [path.resolve(__dirname, 'collections/FormSubmissions/hooks/sendEmail.ts')]: path.resolve(__dirname, 'mocks/serverModule.js'),
|
||||
// [path.resolve(__dirname, 'collections/FormSubmissions/hooks/createCharge.ts')]: path.resolve(__dirname, 'mocks/serverModule.js'),
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
// },
|
||||
collections: [
|
||||
...(config?.collections || []),
|
||||
generateFormCollection(formConfig),
|
||||
|
||||
@@ -39,12 +39,13 @@ export interface FieldsConfig {
|
||||
|
||||
export type BeforeEmail = (emails: FormattedEmail[]) => FormattedEmail[] | Promise<FormattedEmail[]>
|
||||
export type HandlePayment = (data: any) => void
|
||||
export type FieldsOverride = (args: { defaultFields: Field[] }) => Field[]
|
||||
|
||||
export type FormBuilderPluginConfig = {
|
||||
beforeEmail?: BeforeEmail
|
||||
fields?: FieldsConfig
|
||||
formOverrides?: Partial<CollectionConfig>
|
||||
formSubmissionOverrides?: Partial<CollectionConfig>
|
||||
formOverrides?: Partial<Omit<CollectionConfig, 'fields'>> & { fields: FieldsOverride }
|
||||
formSubmissionOverrides?: Partial<Omit<CollectionConfig, 'fields'>> & { fields: FieldsOverride }
|
||||
handlePayment?: HandlePayment
|
||||
redirectRelationships?: string[]
|
||||
}
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-nested-docs",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"description": "The official Nested Docs plugin for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
||||
"rootDir": "./src" /* Specify the root folder within your source files. */,
|
||||
"jsx": "react"
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-redirects",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"description": "Redirects plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
@@ -49,7 +49,8 @@
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/express": "^4.17.9",
|
||||
"@types/react": "18.3.2",
|
||||
"@types/react": "npm:types-react@19.0.0-beta.2",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -71,5 +72,9 @@
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"homepage:": "https://payloadcms.com"
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"overrides": {
|
||||
"@types/react": "npm:types-react@19.0.0-beta.2",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-relationship-object-ids",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"description": "A Payload plugin to store all relationship IDs as ObjectIDs",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-search",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"description": "Search plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
@@ -50,13 +50,14 @@
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/express": "^4.17.9",
|
||||
"@types/react": "18.3.2",
|
||||
"@types/react": "npm:types-react@19.0.0-beta.2",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"payload": "workspace:*",
|
||||
"react": "^18.2.0 || ^19.0.0",
|
||||
"react-dom": "^18.2.0 || ^19.0.0"
|
||||
"react": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522",
|
||||
"react-dom": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
@@ -75,5 +76,9 @@
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"homepage:": "https://payloadcms.com"
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"overrides": {
|
||||
"@types/react": "npm:types-react@19.0.0-beta.2",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,15 @@
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
@@ -47,7 +47,8 @@
|
||||
"@types/express": "^4.17.9",
|
||||
"@types/jest": "^29.5.2",
|
||||
"@types/node": "20.12.5",
|
||||
"@types/react": "18.3.2",
|
||||
"@types/react": "npm:types-react@19.0.0-beta.2",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
|
||||
"copyfiles": "^2.4.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"dotenv": "^8.2.0",
|
||||
@@ -59,8 +60,8 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"payload": "workspace:*",
|
||||
"react": "^18.2.0 || ^19.0.0",
|
||||
"react-dom": "^18.2.0 || ^19.0.0"
|
||||
"react": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522",
|
||||
"react-dom": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
@@ -73,5 +74,9 @@
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"homepage:": "https://payloadcms.com"
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"overrides": {
|
||||
"@types/react": "npm:types-react@19.0.0-beta.2",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-seo",
|
||||
"version": "3.0.0-beta.33",
|
||||
"version": "3.0.0-beta.37",
|
||||
"description": "SEO plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
@@ -51,15 +51,16 @@
|
||||
"@payloadcms/next": "workspace:*",
|
||||
"@payloadcms/translations": "workspace:*",
|
||||
"@payloadcms/ui": "workspace:*",
|
||||
"@types/react": "18.3.2",
|
||||
"@types/react": "npm:types-react@19.0.0-beta.2",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@payloadcms/translations": "workspace:*",
|
||||
"@payloadcms/ui": "workspace:*",
|
||||
"payload": "workspace:*",
|
||||
"react": "^18.2.0 || ^19.0.0",
|
||||
"react-dom": "^18.2.0 || ^19.0.0"
|
||||
"react": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522",
|
||||
"react-dom": "^19.0.0 || ^19.0.0-rc-f994737d14-20240522"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
@@ -78,5 +79,9 @@
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"homepage:": "https://payloadcms.com"
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"overrides": {
|
||||
"@types/react": "npm:types-react@19.0.0-beta.2",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0-beta.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ export const MetaImage: React.FC<MetaImageProps> = (props) => {
|
||||
CustomError={errorMessage}
|
||||
api={api}
|
||||
collection={collection}
|
||||
filterOptions={{}}
|
||||
filterOptions={field.filterOptions}
|
||||
label={undefined}
|
||||
onChange={(incomingImage) => {
|
||||
if (incomingImage !== null) {
|
||||
|
||||
@@ -7,6 +7,15 @@
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic",
|
||||
"pragmaFrag": "React.Fragment",
|
||||
"throwIfNamespace": true,
|
||||
"development": false,
|
||||
"useBuiltins": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user