Compare commits
98 Commits
v3.0.0-alp
...
v3.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba2702cfca | ||
|
|
eba1f6327d | ||
|
|
1ba0b4037c | ||
|
|
49daa75bc4 | ||
|
|
fea1eb1149 | ||
|
|
9a58f6c454 | ||
|
|
66cefe6bb5 | ||
|
|
23510acf40 | ||
|
|
faa49f36e7 | ||
|
|
a5fb8b20a1 | ||
|
|
e14b303609 | ||
|
|
2ec3df6680 | ||
|
|
1f4b6001ef | ||
|
|
6e7c9cc6a3 | ||
|
|
2db385c6a5 | ||
|
|
bd27f48eae | ||
|
|
98a33250f6 | ||
|
|
8f9729a928 | ||
|
|
1a8564bc35 | ||
|
|
76c9632417 | ||
|
|
2370361b43 | ||
|
|
cb63095d3f | ||
|
|
601f2fb450 | ||
|
|
e746f17167 | ||
|
|
5f76097562 | ||
|
|
03756c4210 | ||
|
|
5e1e158414 | ||
|
|
76d2525fd2 | ||
|
|
ec8c7e5c2c | ||
|
|
0a4cbe1a08 | ||
|
|
c228421a38 | ||
|
|
a742f82370 | ||
|
|
213678ca3e | ||
|
|
e1b7ad6a71 | ||
|
|
8b9985a92c | ||
|
|
f276826b09 | ||
|
|
8c1df551ef | ||
|
|
b62cb157e1 | ||
|
|
045c74ce67 | ||
|
|
911e902da4 | ||
|
|
4e0d90d720 | ||
|
|
7b62705cc0 | ||
|
|
bdf02bebaa | ||
|
|
94aa309910 | ||
|
|
499a0a782a | ||
|
|
1e5a531a83 | ||
|
|
3e6a4073c4 | ||
|
|
e7cb6abd1f | ||
|
|
abfd8841a5 | ||
|
|
5e385fa33b | ||
|
|
95688c7e30 | ||
|
|
b041d3e70e | ||
|
|
0e91cddab9 | ||
|
|
d3856693ce | ||
|
|
40f36104f3 | ||
|
|
7215edb784 | ||
|
|
be015320de | ||
|
|
a37d53b2e7 | ||
|
|
f43de11121 | ||
|
|
4c1129188b | ||
|
|
34e04f5251 | ||
|
|
b613d65b36 | ||
|
|
66aec63f6b | ||
|
|
5b0d18bb3b | ||
|
|
1c5b43c218 | ||
|
|
c78cfb9fcf | ||
|
|
927d4dd06f | ||
|
|
afb75ef75a | ||
|
|
92181866a4 | ||
|
|
c97805c7f1 | ||
|
|
c20a139e58 | ||
|
|
4287d1032f | ||
|
|
8b784d7c10 | ||
|
|
8895f6420f | ||
|
|
bb10ed5b7d | ||
|
|
9dc315dbf3 | ||
|
|
677531531f | ||
|
|
70c89b14a9 | ||
|
|
7f7c94e0d5 | ||
|
|
e6f09e42a1 | ||
|
|
f0419b7502 | ||
|
|
f01072eb11 | ||
|
|
c88102d6cc | ||
|
|
847a2994f9 | ||
|
|
ab186c0608 | ||
|
|
349ae8ed27 | ||
|
|
0066b858d6 | ||
|
|
e12e720a99 | ||
|
|
c17f2e2560 | ||
|
|
d75bf235bb | ||
|
|
881d1e9594 | ||
|
|
45a443989a | ||
|
|
7880fb402a | ||
|
|
ac2f8c9141 | ||
|
|
e36e774382 | ||
|
|
b1be2dfbf4 | ||
|
|
32f3a11bf7 | ||
|
|
6b28e72686 |
5
.github/workflows/main.yml
vendored
5
.github/workflows/main.yml
vendored
@@ -4,7 +4,7 @@ on:
|
||||
pull_request:
|
||||
types: [ opened, reopened, synchronize ]
|
||||
push:
|
||||
branches: ['main', 'feat/next-poc']
|
||||
branches: ['main', 'alpha']
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
@@ -196,9 +196,6 @@ jobs:
|
||||
echo "POSTGRES_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres" >> $GITHUB_ENV
|
||||
if: matrix.database == 'supabase'
|
||||
|
||||
- name: Component Tests
|
||||
run: pnpm test:components
|
||||
|
||||
- name: Integration Tests
|
||||
run: pnpm test:int
|
||||
env:
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -8,12 +8,15 @@ test-results
|
||||
.devcontainer
|
||||
.localstack
|
||||
/migrations
|
||||
/media
|
||||
.localstack
|
||||
.turbo
|
||||
|
||||
.turbo
|
||||
|
||||
# Ignore test directory media folder/files
|
||||
/media
|
||||
/versions
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node,macos,windows,webstorm,sublimetext,visualstudiocode
|
||||
|
||||
@@ -285,3 +288,4 @@ $RECYCLE.BIN/
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
|
||||
|
||||
/build
|
||||
.swc
|
||||
15
.idea/runConfigurations/Playwright_test.xml
generated
Normal file
15
.idea/runConfigurations/Playwright_test.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Playwright test" type="JavaScriptTestRunnerPlaywright">
|
||||
<node-interpreter value="project" />
|
||||
<playwright-package value="$PROJECT_DIR$/node_modules/playwright" />
|
||||
<working-dir value="$PROJECT_DIR$" />
|
||||
<envs />
|
||||
<scope-kind value="TEST" />
|
||||
<test-file value="$PROJECT_DIR$/test/_community/e2e.spec.ts" />
|
||||
<test-names>
|
||||
<test-name value="Admin Panel" />
|
||||
<test-name value="example test" />
|
||||
</test-names>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
15
.swcrc
Normal file
15
.swcrc
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"type": "es6"
|
||||
}
|
||||
}
|
||||
18
.vscode/launch.json
vendored
18
.vscode/launch.json
vendored
@@ -2,6 +2,13 @@
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
"configurations": [
|
||||
{
|
||||
"command": "pnpm generate:types",
|
||||
"name": "Generate Types CLI",
|
||||
"request": "launch",
|
||||
"type": "node-terminal",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev _community -- --no-turbo",
|
||||
"cwd": "${workspaceFolder}",
|
||||
@@ -102,17 +109,6 @@
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "ts-node ./packages/payload/src/bin/index.ts generate:types",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "test/_community/config.ts",
|
||||
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
|
||||
},
|
||||
"name": "Generate Types CLI",
|
||||
"outputCapture": "std",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "ts-node ./packages/payload/src/bin/index.ts migrate:status",
|
||||
"env": {
|
||||
|
||||
22
.vscode/settings.json
vendored
22
.vscode/settings.json
vendored
@@ -40,25 +40,5 @@
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
}
|
||||
},
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.activeBackground": "#8cb5b6",
|
||||
"activityBar.background": "#8cb5b6",
|
||||
"activityBar.foreground": "#15202b",
|
||||
"activityBar.inactiveForeground": "#15202b99",
|
||||
"activityBarBadge.background": "#9c639b",
|
||||
"activityBarBadge.foreground": "#e7e7e7",
|
||||
"commandCenter.border": "#15202b99",
|
||||
"sash.hoverBorder": "#8cb5b6",
|
||||
"statusBar.background": "#6da1a2",
|
||||
"statusBar.foreground": "#15202b",
|
||||
"statusBarItem.hoverBackground": "#568586",
|
||||
"statusBarItem.remoteBackground": "#6da1a2",
|
||||
"statusBarItem.remoteForeground": "#15202b",
|
||||
"titleBar.activeBackground": "#6da1a2",
|
||||
"titleBar.activeForeground": "#15202b",
|
||||
"titleBar.inactiveBackground": "#6da1a299",
|
||||
"titleBar.inactiveForeground": "#15202b99"
|
||||
},
|
||||
"peacock.color": "#6da1a2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,13 +292,11 @@ For auth-enabled Collections, this hook runs after successful `forgotPassword` o
|
||||
```ts
|
||||
import { CollectionAfterForgotPasswordHook } from 'payload/types'
|
||||
|
||||
const afterLoginHook: CollectionAfterForgotPasswordHook = async ({
|
||||
req, // full express request
|
||||
user, // user being logged in
|
||||
token, // user token
|
||||
}) => {
|
||||
return user
|
||||
}
|
||||
const afterForgotPasswordHook: CollectionAfterForgotPasswordHook = async ({
|
||||
args, // arguments passed into the operation
|
||||
context,
|
||||
collection, // The collection which this hook is being run on
|
||||
}) => {...}
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
@@ -290,5 +290,5 @@ If you've configured tests for your package, integrate them into your workf
|
||||
The best way to share and allow others to use your plugin once it is complete is to publish an NPM package. This process is straightforward and well documented, find out more about [creating and publishing a NPM package here](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/).
|
||||
##### Add payload-plugin topic tag:
|
||||
Apply the tag **payload-plugin** to your GitHub repository. This will boost the visibility of your plugin and ensure it gets listed with [existing payload plugins](https://github.com/topics/payload-plugin).
|
||||
##### Use [Semantic Versioning](https://semver.org/) (SemVar):
|
||||
With the SemVar system you release version numbers that reflect the nature of changes (major, minor, patch). Ensure all major versions reference their Payload compatibility.
|
||||
##### Use [Semantic Versioning](https://semver.org/) (SemVer):
|
||||
With the SemVer system you release version numbers that reflect the nature of changes (major, minor, patch). Ensure all major versions reference their Payload compatibility.
|
||||
|
||||
1
emptyModule.js
Normal file
1
emptyModule.js
Normal file
@@ -0,0 +1 @@
|
||||
export default {}
|
||||
@@ -1,16 +0,0 @@
|
||||
module.exports = {
|
||||
moduleNameMapper: {
|
||||
'\\.(css|scss)$': '<rootDir>/packages/payload/src/bundlers/mocks/emptyModule.js',
|
||||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
|
||||
'<rootDir>/packages/payload/src/bundlers/mocks/fileMock.js',
|
||||
},
|
||||
setupFilesAfterEnv: ['./test/componentsSetup.js'],
|
||||
testEnvironment: 'jsdom',
|
||||
testPathIgnorePatterns: ['node_modules', 'dist'],
|
||||
testRegex: '(/src/admin/.*\\.(test|spec))\\.[jt]sx?$',
|
||||
testTimeout: 15000,
|
||||
transform: {
|
||||
'^.+\\.(t|j)sx?$': ['@swc/jest'],
|
||||
},
|
||||
verbose: true,
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import withPayload from './packages/next/src/withPayload.js'
|
||||
import bundleAnalyzer from '@next/bundle-analyzer'
|
||||
|
||||
import withPayload from './packages/next/src/withPayload.js'
|
||||
|
||||
const withBundleAnalyzer = bundleAnalyzer({
|
||||
enabled: process.env.ANALYZE === 'true',
|
||||
})
|
||||
@@ -8,14 +9,6 @@ const withBundleAnalyzer = bundleAnalyzer({
|
||||
export default withBundleAnalyzer(
|
||||
withPayload({
|
||||
reactStrictMode: false,
|
||||
webpack: (webpackConfig) => {
|
||||
webpackConfig.resolve.extensionAlias = {
|
||||
'.js': ['.ts', '.tsx', '.js', '.jsx'],
|
||||
'.mjs': ['.mts', '.mjs'],
|
||||
'.cjs': ['.cts', '.cjs'],
|
||||
}
|
||||
return webpackConfig
|
||||
},
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
@@ -25,5 +18,13 @@ export default withBundleAnalyzer(
|
||||
},
|
||||
]
|
||||
},
|
||||
webpack: (webpackConfig) => {
|
||||
webpackConfig.resolve.extensionAlias = {
|
||||
'.cjs': ['.cts', '.cjs'],
|
||||
'.js': ['.ts', '.tsx', '.js', '.jsx'],
|
||||
'.mjs': ['.mts', '.mjs'],
|
||||
}
|
||||
return webpackConfig
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
23
package.json
23
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload-monorepo",
|
||||
"version": "3.0.0-alpha.34",
|
||||
"version": "3.0.0-alpha.45",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"workspaces:": [
|
||||
@@ -8,9 +8,10 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build": "pnpm run build:core",
|
||||
"obliterate-playwright-cache": "rm -rf ~/Library/Caches/ms-playwright && find /System/Volumes/Data/private/var/folders -type d -name 'playwright*' -exec rm -rf {} ++ && npx playwright install",
|
||||
"build:all": "turbo build",
|
||||
"build:core": "turbo build --filter \"!@payloadcms/plugin-*\"",
|
||||
"build:plugins": "turbo build --filter \"@payloadcms/plugin-*\" --filter \"!@payloadcms/plugin-search\" --filter \"!@payloadcms/plugin-redirects\" --filter \"!@payloadcms/plugin-nested-docs\"",
|
||||
"build:plugins": "turbo build --filter \"@payloadcms/plugin-*\"",
|
||||
"build:app": "next build",
|
||||
"build:app:analyze": "cross-env ANALYZE=true next build",
|
||||
"build:create-payload-app": "turbo build --filter create-payload-app",
|
||||
@@ -40,13 +41,15 @@
|
||||
"clean:build": "find . \\( -type d \\( -name dist -o -name .cache -o -name .next -o -name .turbo \\) -o -type f -name tsconfig.tsbuildinfo \\) -not -path '*/node_modules/*' -exec rm -rf {} +",
|
||||
"clean:all": "find . \\( -type d \\( -name node_modules -o -name dist -o -name .cache -o -name .next -o -name .turbo \\) -o -type f -name tsconfig.tsbuildinfo \\) -exec rm -rf {} +",
|
||||
"dev": "cross-env node --no-deprecation ./test/dev.js",
|
||||
"dev:generate-graphql-schema": "NODE_OPTIONS=--no-deprecation tsx ./test/generateGraphQLSchema.ts",
|
||||
"dev:generate-types": "NODE_OPTIONS=--no-deprecation tsx ./test/generateTypes.ts",
|
||||
"devsafe": "rm -rf .next && cross-env node --no-deprecation ./test/dev.js",
|
||||
"dev:generate-graphql-schema": "cross-env NODE_OPTIONS=--no-deprecation tsx ./test/generateGraphQLSchema.ts",
|
||||
"dev:generate-types": "cross-env NODE_OPTIONS=--no-deprecation tsx ./test/generateTypes.ts",
|
||||
"dev:postgres": "pnpm --filter payload run dev:postgres",
|
||||
"docker:restart": "pnpm docker:stop --remove-orphans && pnpm docker:start",
|
||||
"docker:start": "docker-compose -f packages/plugin-cloud-storage/docker-compose.yml up -d",
|
||||
"docker:stop": "docker-compose -f packages/plugin-cloud-storage/docker-compose.yml down",
|
||||
"fix": "eslint \"packages/**/*.ts\" --fix",
|
||||
"generate:types": "PAYLOAD_CONFIG_PATH=./test/_community/config.ts node --no-deprecation ./packages/payload/bin.js generate:types",
|
||||
"lint": "eslint \"packages/**/*.ts\"",
|
||||
"lint-staged": "lint-staged",
|
||||
"prepare": "husky install",
|
||||
@@ -69,10 +72,9 @@
|
||||
"@next/bundle-analyzer": "^14.1.0",
|
||||
"@octokit/core": "^5.1.0",
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@playwright/test": "1.42.1",
|
||||
"@playwright/test": "^1.42.1",
|
||||
"@swc/cli": "^0.1.62",
|
||||
"@swc/jest": "0.2.36",
|
||||
"@swc/register": "0.1.10",
|
||||
"@testing-library/jest-dom": "6.4.2",
|
||||
"@testing-library/react": "14.2.1",
|
||||
"@types/concat-stream": "^2.0.1",
|
||||
@@ -122,6 +124,8 @@
|
||||
"nodemon": "3.0.3",
|
||||
"pino": "8.15.0",
|
||||
"pino-pretty": "10.2.0",
|
||||
"playwright": "^1.42.1",
|
||||
"playwright-core": "^1.42.1",
|
||||
"prettier": "^3.0.3",
|
||||
"prompts": "2.4.2",
|
||||
"qs": "6.11.2",
|
||||
@@ -136,6 +140,7 @@
|
||||
"slash": "3.0.0",
|
||||
"slate": "0.91.4",
|
||||
"tempfile": "^3.0.0",
|
||||
"ts-node": "10.9.1",
|
||||
"tsx": "^4.7.1",
|
||||
"turbo": "^1.12.5",
|
||||
"typescript": "5.2.2",
|
||||
@@ -152,7 +157,8 @@
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx}": [
|
||||
"prettier --write"
|
||||
"prettier --write",
|
||||
"eslint --cache --fix"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -175,5 +181,6 @@
|
||||
"abab": "2",
|
||||
"domexception": "4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"packageManager": "pnpm@8.15.4+sha256.cea6d0bdf2de3a0549582da3983c70c92ffc577ff4410cbf190817ddc35137c2"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"sourceMaps": true,
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"sourceMaps": true,
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"version": "3.0.0-alpha.34",
|
||||
"version": "3.0.0-alpha.45",
|
||||
"description": "The officially supported MongoDB database adapter for Payload - Update 2",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -45,7 +45,7 @@ export const createMigration: CreateMigration = async function createMigration({
|
||||
|
||||
// Check if predefined migration exists
|
||||
if (fs.existsSync(cleanPath)) {
|
||||
const { down, up } = eval(`require(${cleanPath})`)
|
||||
const { down, up } = await eval(`import(${cleanPath})`)
|
||||
migrationFileContent = migrationTemplate(up, down)
|
||||
} else {
|
||||
payload.logger.error({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"sourceMaps": true,
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "3.0.0-alpha.34",
|
||||
"version": "3.0.0-alpha.45",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import type { Payload } from 'payload'
|
||||
import type { Connect } from 'payload/database'
|
||||
|
||||
import { eq, sql } from 'drizzle-orm'
|
||||
import { sql } from 'drizzle-orm'
|
||||
import { drizzle } from 'drizzle-orm/node-postgres'
|
||||
import { numeric, timestamp, varchar } from 'drizzle-orm/pg-core'
|
||||
import { Pool } from 'pg'
|
||||
import prompts from 'prompts'
|
||||
import pg from 'pg'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { pushDevSchema } from './utilities/pushDevSchema.js'
|
||||
|
||||
const connectWithReconnect = async function ({
|
||||
adapter,
|
||||
payload,
|
||||
@@ -61,7 +61,7 @@ export const connect: Connect = async function connect(
|
||||
}
|
||||
|
||||
try {
|
||||
this.pool = new Pool(this.poolOptions)
|
||||
this.pool = new pg.Pool(this.poolOptions)
|
||||
await connectWithReconnect({ adapter: this, payload: this.payload })
|
||||
|
||||
const logger = this.logger || false
|
||||
@@ -93,70 +93,5 @@ export const connect: Connect = async function connect(
|
||||
)
|
||||
return
|
||||
|
||||
const { pushSchema } = await import('drizzle-kit/payload')
|
||||
|
||||
// This will prompt if clarifications are needed for Drizzle to push new schema
|
||||
const { apply, hasDataLoss, statementsToExecute, warnings } = await pushSchema(
|
||||
this.schema,
|
||||
this.drizzle,
|
||||
)
|
||||
|
||||
if (warnings.length) {
|
||||
let message = `Warnings detected during schema push: \n\n${warnings.join('\n')}\n\n`
|
||||
|
||||
if (hasDataLoss) {
|
||||
message += `DATA LOSS WARNING: Possible data loss detected if schema is pushed.\n\n`
|
||||
}
|
||||
|
||||
message += `Accept warnings and push schema to database?`
|
||||
|
||||
const { confirm: acceptWarnings } = await prompts(
|
||||
{
|
||||
name: 'confirm',
|
||||
type: 'confirm',
|
||||
initial: false,
|
||||
message,
|
||||
},
|
||||
{
|
||||
onCancel: () => {
|
||||
process.exit(0)
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// Exit if user does not accept warnings.
|
||||
// Q: Is this the right type of exit for this interaction?
|
||||
if (!acceptWarnings) {
|
||||
process.exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
await apply()
|
||||
|
||||
// Migration table def in order to use query using drizzle
|
||||
const migrationsSchema = this.pgSchema.table('payload_migrations', {
|
||||
name: varchar('name'),
|
||||
batch: numeric('batch'),
|
||||
created_at: timestamp('created_at'),
|
||||
updated_at: timestamp('updated_at'),
|
||||
})
|
||||
|
||||
const devPush = await this.drizzle
|
||||
.select()
|
||||
.from(migrationsSchema)
|
||||
.where(eq(migrationsSchema.batch, '-1'))
|
||||
|
||||
if (!devPush.length) {
|
||||
await this.drizzle.insert(migrationsSchema).values({
|
||||
name: 'dev',
|
||||
batch: '-1',
|
||||
})
|
||||
} else {
|
||||
await this.drizzle
|
||||
.update(migrationsSchema)
|
||||
.set({
|
||||
updated_at: new Date(),
|
||||
})
|
||||
.where(eq(migrationsSchema.batch, '-1'))
|
||||
}
|
||||
await pushDevSchema(this)
|
||||
}
|
||||
|
||||
@@ -3,10 +3,13 @@ import type { DrizzleSnapshotJSON } from 'drizzle-kit/payload'
|
||||
import type { CreateMigration } from 'payload/database'
|
||||
|
||||
import fs from 'fs'
|
||||
import { createRequire } from 'module'
|
||||
import prompts from 'prompts'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
const migrationTemplate = (
|
||||
upSQL?: string,
|
||||
downSQL?: string,
|
||||
@@ -60,7 +63,7 @@ export const createMigration: CreateMigration = async function createMigration(
|
||||
fs.mkdirSync(dir)
|
||||
}
|
||||
|
||||
const { generateDrizzleJson, generateMigration } = await import('drizzle-kit/payload')
|
||||
const { generateDrizzleJson, generateMigration } = require('drizzle-kit/payload')
|
||||
|
||||
const [yyymmdd, hhmmss] = new Date().toISOString().split('T')
|
||||
const formattedDate = yyymmdd.replace(/\D/g, '')
|
||||
|
||||
@@ -2,7 +2,13 @@ import type { Destroy } from 'payload/database'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { pushDevSchema } from './utilities/pushDevSchema.js'
|
||||
|
||||
export const destroy: Destroy = async function destroy(this: PostgresAdapter) {
|
||||
// TODO: this hangs test suite for some reason
|
||||
// await this.pool.end()
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
await pushDevSchema(this)
|
||||
} else {
|
||||
// TODO: this hangs test suite for some reason
|
||||
// await this.pool.end()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { Payload } from 'payload'
|
||||
import type { Migration } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
|
||||
import { createRequire } from 'module'
|
||||
import {
|
||||
commitTransaction,
|
||||
initTransaction,
|
||||
@@ -17,6 +18,8 @@ import { createMigrationTable } from './utilities/createMigrationTable.js'
|
||||
import { migrationTableExists } from './utilities/migrationTableExists.js'
|
||||
import { parseError } from './utilities/parseError.js'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
export async function migrate(this: PostgresAdapter): Promise<void> {
|
||||
const { payload } = this
|
||||
const migrationFiles = await readMigrationFiles({ payload })
|
||||
@@ -82,7 +85,7 @@ export async function migrate(this: PostgresAdapter): Promise<void> {
|
||||
}
|
||||
|
||||
async function runMigrationFile(payload: Payload, migration: Migration, batch: number) {
|
||||
const { generateDrizzleJson } = await import('drizzle-kit/payload')
|
||||
const { generateDrizzleJson } = require('drizzle-kit/payload')
|
||||
|
||||
const start = Date.now()
|
||||
const req = { payload } as PayloadRequest
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { DatabaseError } from 'pg'
|
||||
import pg from 'pg'
|
||||
const { DatabaseError } = pg
|
||||
|
||||
/**
|
||||
* Format error message with hint if available
|
||||
|
||||
83
packages/db-postgres/src/utilities/pushDevSchema.ts
Normal file
83
packages/db-postgres/src/utilities/pushDevSchema.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { numeric, timestamp, varchar } from 'drizzle-orm/pg-core'
|
||||
import { createRequire } from 'module'
|
||||
import prompts from 'prompts'
|
||||
|
||||
import type { PostgresAdapter } from '../types.js'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
/**
|
||||
* Pushes the development schema to the database using Drizzle.
|
||||
*
|
||||
* @param {PostgresAdapter} db - The PostgresAdapter instance connected to the database.
|
||||
* @returns {Promise<void>} - A promise that resolves once the schema push is complete.
|
||||
*/
|
||||
export const pushDevSchema = async (db: PostgresAdapter) => {
|
||||
const { pushSchema } = require('drizzle-kit/payload')
|
||||
|
||||
// This will prompt if clarifications are needed for Drizzle to push new schema
|
||||
const { apply, hasDataLoss, statementsToExecute, warnings } = await pushSchema(
|
||||
db.schema,
|
||||
db.drizzle,
|
||||
)
|
||||
|
||||
if (warnings.length) {
|
||||
let message = `Warnings detected during schema push: \n\n${warnings.join('\n')}\n\n`
|
||||
|
||||
if (hasDataLoss) {
|
||||
message += `DATA LOSS WARNING: Possible data loss detected if schema is pushed.\n\n`
|
||||
}
|
||||
|
||||
message += `Accept warnings and push schema to database?`
|
||||
|
||||
const { confirm: acceptWarnings } = await prompts(
|
||||
{
|
||||
name: 'confirm',
|
||||
type: 'confirm',
|
||||
initial: false,
|
||||
message,
|
||||
},
|
||||
{
|
||||
onCancel: () => {
|
||||
process.exit(0)
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// Exit if user does not accept warnings.
|
||||
// Q: Is this the right type of exit for this interaction?
|
||||
if (!acceptWarnings) {
|
||||
process.exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
await apply()
|
||||
|
||||
// Migration table def in order to use query using drizzle
|
||||
const migrationsSchema = db.pgSchema.table('payload_migrations', {
|
||||
name: varchar('name'),
|
||||
batch: numeric('batch'),
|
||||
created_at: timestamp('created_at'),
|
||||
updated_at: timestamp('updated_at'),
|
||||
})
|
||||
|
||||
const devPush = await db.drizzle
|
||||
.select()
|
||||
.from(migrationsSchema)
|
||||
.where(eq(migrationsSchema.batch, '-1'))
|
||||
|
||||
if (!devPush.length) {
|
||||
await db.drizzle.insert(migrationsSchema).values({
|
||||
name: 'dev',
|
||||
batch: '-1',
|
||||
})
|
||||
} else {
|
||||
await db.drizzle
|
||||
.update(migrationsSchema)
|
||||
.set({
|
||||
updated_at: new Date(),
|
||||
})
|
||||
.where(eq(migrationsSchema.batch, '-1'))
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"sourceMaps": true,
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/graphql",
|
||||
"version": "3.0.0-alpha.34",
|
||||
"version": "3.0.0-alpha.45",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.d.ts",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"sourceMaps": true,
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"sourceMaps": true,
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"sourceMaps": true,
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
},
|
||||
"experimental": {
|
||||
"plugins": [
|
||||
[
|
||||
"swc-plugin-transform-remove-imports",
|
||||
{
|
||||
"test": "\\.(scss|css)$"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/next",
|
||||
"version": "3.0.0-alpha.34",
|
||||
"version": "3.0.0-alpha.45",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.d.ts",
|
||||
"type": "module",
|
||||
@@ -8,9 +8,10 @@
|
||||
"@payloadcms/next": "./dist/bin/index.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm copyfiles && pnpm build:swc && pnpm build:types",
|
||||
"build": "pnpm copyfiles && pnpm build:swc && pnpm build:types && pnpm build:webpack && rm dist/prod/index.js",
|
||||
"build:swc": "swc ./src -d ./dist --config-file .swcrc",
|
||||
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
||||
"build:webpack": "webpack --config webpack.config.js",
|
||||
"clean": "rimraf {dist,*.tsbuildinfo}",
|
||||
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" \"src/app/api/**\" dist/ && pnpm copyfiles:api",
|
||||
"copyfiles:api": "copyfiles -u 1 \"src/app/(payload)/**\" dist/template",
|
||||
@@ -50,7 +51,18 @@
|
||||
"@types/react": "18.2.15",
|
||||
"@types/react-dom": "18.2.7",
|
||||
"@types/ws": "^8.5.10",
|
||||
"payload": "workspace:*"
|
||||
"css-loader": "^6.10.0",
|
||||
"css-minimizer-webpack-plugin": "^6.0.0",
|
||||
"mini-css-extract-plugin": "1.6.2",
|
||||
"payload": "workspace:*",
|
||||
"postcss-loader": "^8.1.1",
|
||||
"postcss-preset-env": "^9.5.0",
|
||||
"sass-loader": "^14.1.1",
|
||||
"swc-loader": "^0.2.6",
|
||||
"swc-plugin-transform-remove-imports": "^1.12.1",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"webpack": "^5.78.0",
|
||||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "6.0.8",
|
||||
@@ -79,6 +91,11 @@
|
||||
"main": "./dist/exports/index.js",
|
||||
"types": "./dist/exports/index.d.ts",
|
||||
"exports": {
|
||||
"./css": {
|
||||
"import": "./dist/prod/styles.css",
|
||||
"require": "./dist/prod/styles.css",
|
||||
"default": "./dist/prod/styles.css"
|
||||
},
|
||||
"./withPayload": {
|
||||
"import": "./dist/withPayload.js",
|
||||
"require": "./dist/withPayload.js",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { SanitizedConfig } from 'payload/types'
|
||||
|
||||
import { auth } from '@payloadcms/next/utilities/auth'
|
||||
import { translations } from '@payloadcms/translations/client'
|
||||
import { RootProvider, buildComponentMap } from '@payloadcms/ui'
|
||||
import '@payloadcms/ui/scss/app.scss'
|
||||
@@ -11,6 +10,7 @@ import React from 'react'
|
||||
import 'react-toastify/dist/ReactToastify.css'
|
||||
|
||||
import { ClearRouteCache } from '../../elements/ClearRouteCache/index.js'
|
||||
import { auth } from '../../utilities/auth.js'
|
||||
import { getPayload } from '../../utilities/getPayload.js'
|
||||
import { getRequestLanguage } from '../../utilities/getRequestLanguage.js'
|
||||
import { DefaultEditView } from '../../views/Edit/Default/index.js'
|
||||
@@ -38,7 +38,7 @@ export const RootLayout = async ({
|
||||
|
||||
const payload = await getPayload({ config: configPromise })
|
||||
|
||||
const { cookies, permissions, user } = await auth({
|
||||
const { cookies, permissions } = await auth({
|
||||
headers,
|
||||
payload,
|
||||
})
|
||||
@@ -58,10 +58,11 @@ export const RootLayout = async ({
|
||||
value: language,
|
||||
}))
|
||||
|
||||
const componentMap = buildComponentMap({
|
||||
const { componentMap, wrappedChildren } = buildComponentMap({
|
||||
DefaultCell,
|
||||
DefaultEditView,
|
||||
DefaultListView,
|
||||
children,
|
||||
config,
|
||||
permissions,
|
||||
})
|
||||
@@ -78,7 +79,7 @@ export const RootLayout = async ({
|
||||
languageOptions={languageOptions}
|
||||
translations={mergedTranslations[lang]}
|
||||
>
|
||||
{children}
|
||||
{wrappedChildren}
|
||||
</RootProvider>
|
||||
<div id="portal" />
|
||||
</body>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@import 'fonts';
|
||||
@import 'styles';
|
||||
@import './fonts.scss';
|
||||
@import './styles.scss';
|
||||
@import './toastify.scss';
|
||||
@import './colors.scss';
|
||||
|
||||
|
||||
@@ -1,34 +1,36 @@
|
||||
import type { Payload, PayloadRequest } from 'payload/types'
|
||||
|
||||
import { getAccessResults, getAuthenticatedUser, parseCookies } from 'payload/auth'
|
||||
import { cache } from 'react'
|
||||
|
||||
export const auth = cache(
|
||||
async ({ headers, payload }: { headers: Request['headers']; payload: Payload }) => {
|
||||
const cookies = parseCookies(headers)
|
||||
type Args = {
|
||||
headers: Request['headers']
|
||||
payload: Payload
|
||||
}
|
||||
|
||||
const user = await getAuthenticatedUser({
|
||||
cookies,
|
||||
export const auth = async ({ headers, payload }: Args) => {
|
||||
const cookies = parseCookies(headers)
|
||||
|
||||
const user = await getAuthenticatedUser({
|
||||
cookies,
|
||||
headers,
|
||||
payload,
|
||||
})
|
||||
|
||||
const permissions = await getAccessResults({
|
||||
req: {
|
||||
context: {},
|
||||
headers,
|
||||
i18n: undefined,
|
||||
payload,
|
||||
})
|
||||
|
||||
const permissions = await getAccessResults({
|
||||
req: {
|
||||
context: {},
|
||||
headers,
|
||||
i18n: undefined,
|
||||
payload,
|
||||
payloadAPI: 'REST',
|
||||
t: undefined,
|
||||
user,
|
||||
} as PayloadRequest,
|
||||
})
|
||||
|
||||
return {
|
||||
cookies,
|
||||
permissions,
|
||||
payloadAPI: 'REST',
|
||||
t: undefined,
|
||||
user,
|
||||
}
|
||||
},
|
||||
)
|
||||
} as PayloadRequest,
|
||||
})
|
||||
|
||||
return {
|
||||
cookies,
|
||||
permissions,
|
||||
user,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export const getDataAndFile: GetDataAndFile = async ({ collection, config, reque
|
||||
let file: CustomPayloadRequest['file'] = undefined
|
||||
|
||||
if (['PATCH', 'POST', 'PUT'].includes(request.method.toUpperCase()) && request.body) {
|
||||
const [contentType] = request.headers.get('Content-Type').split(';')
|
||||
const [contentType] = (request.headers.get('Content-Type') || '').split(';')
|
||||
|
||||
if (contentType === 'application/json') {
|
||||
data = await request.json()
|
||||
|
||||
@@ -8,15 +8,22 @@ let cached = global._payload
|
||||
|
||||
if (!cached) {
|
||||
// eslint-disable-next-line no-multi-assign
|
||||
cached = global._payload = { payload: null, promise: null }
|
||||
cached = global._payload = { payload: null, promise: null, reload: false }
|
||||
}
|
||||
|
||||
export const getPayload = async (options: InitOptions): Promise<Payload> => {
|
||||
if (!options?.config) {
|
||||
throw new Error('Error: the payload config is required for getPayload to work.')
|
||||
}
|
||||
|
||||
if (cached.payload) {
|
||||
const config = await options.config
|
||||
|
||||
if (cached.reload) {
|
||||
cached.reload = false
|
||||
if (cached.reload === true) {
|
||||
let resolve
|
||||
|
||||
cached.reload = new Promise((res) => (resolve = res))
|
||||
|
||||
if (typeof cached.payload.db.destroy === 'function') {
|
||||
await cached.payload.db.destroy()
|
||||
}
|
||||
@@ -33,6 +40,11 @@ export const getPayload = async (options: InitOptions): Promise<Payload> => {
|
||||
|
||||
await cached.payload.db.init()
|
||||
await cached.payload.db.connect({ hotReload: true })
|
||||
resolve()
|
||||
}
|
||||
|
||||
if (cached.reload instanceof Promise) {
|
||||
await cached.reload
|
||||
}
|
||||
|
||||
return cached.payload
|
||||
@@ -47,7 +59,8 @@ export const getPayload = async (options: InitOptions): Promise<Payload> => {
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
try {
|
||||
const ws = new WebSocket('ws://localhost:3000/_next/webpack-hmr')
|
||||
const port = process.env.PORT || '3000'
|
||||
const ws = new WebSocket(`ws://localhost:${port}/_next/webpack-hmr`)
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
if (typeof event.data === 'string') {
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { Data, DocumentPreferences, ServerSideEditViewProps } from 'payload
|
||||
import {
|
||||
DocumentHeader,
|
||||
DocumentInfoProvider,
|
||||
FormQueryParamsProvider,
|
||||
HydrateClientUser,
|
||||
RenderCustomComponent,
|
||||
buildStateFromSchema,
|
||||
@@ -21,6 +22,7 @@ export { generateAccountMetadata } from './meta.js'
|
||||
|
||||
export const Account: React.FC<AdminViewProps> = async ({ initPageResult, searchParams }) => {
|
||||
const {
|
||||
locale,
|
||||
permissions,
|
||||
req: {
|
||||
i18n,
|
||||
@@ -51,6 +53,7 @@ export const Account: React.FC<AdminViewProps> = async ({ initPageResult, search
|
||||
id: user.id,
|
||||
collection: userSlug,
|
||||
depth: 0,
|
||||
overrideAccess: false,
|
||||
user,
|
||||
})
|
||||
} catch (error) {
|
||||
@@ -118,13 +121,22 @@ export const Account: React.FC<AdminViewProps> = async ({ initPageResult, search
|
||||
i18n={i18n}
|
||||
/>
|
||||
<HydrateClientUser permissions={permissions} user={user} />
|
||||
<RenderCustomComponent
|
||||
CustomComponent={
|
||||
typeof CustomAccountComponent === 'function' ? CustomAccountComponent : undefined
|
||||
}
|
||||
DefaultComponent={EditView}
|
||||
componentProps={serverSideProps}
|
||||
/>
|
||||
<FormQueryParamsProvider
|
||||
initialParams={{
|
||||
depth: 0,
|
||||
'fallback-locale': 'null',
|
||||
locale: locale.code,
|
||||
uploadEdits: undefined,
|
||||
}}
|
||||
>
|
||||
<RenderCustomComponent
|
||||
CustomComponent={
|
||||
typeof CustomAccountComponent === 'function' ? CustomAccountComponent : undefined
|
||||
}
|
||||
DefaultComponent={EditView}
|
||||
componentProps={serverSideProps}
|
||||
/>
|
||||
</FormQueryParamsProvider>
|
||||
</DocumentInfoProvider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -106,6 +106,7 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
collection: collectionSlug,
|
||||
depth: 0,
|
||||
locale: locale.code,
|
||||
overrideAccess: false,
|
||||
user,
|
||||
})
|
||||
} catch (error) {} // eslint-disable-line no-empty
|
||||
@@ -148,6 +149,7 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
slug: globalSlug,
|
||||
depth: 0,
|
||||
locale: locale.code,
|
||||
overrideAccess: false,
|
||||
user,
|
||||
})
|
||||
} catch (error) {} // eslint-disable-line no-empty
|
||||
|
||||
@@ -13,7 +13,7 @@ import React, { Fragment, useEffect } from 'react'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
export const EditViewClient: React.FC = () => {
|
||||
const { collectionSlug, getDocPermissions, getVersions, globalSlug, isEditing, setDocumentInfo } =
|
||||
const { collectionSlug, getDocPermissions, getVersions, globalSlug, isEditing, setOnSave } =
|
||||
useDocumentInfo()
|
||||
|
||||
const {
|
||||
@@ -58,10 +58,8 @@ export const EditViewClient: React.FC = () => {
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
setDocumentInfo({
|
||||
onSave,
|
||||
})
|
||||
}, [setDocumentInfo, onSave])
|
||||
setOnSave(() => onSave)
|
||||
}, [setOnSave, onSave])
|
||||
|
||||
// Allow the `DocumentInfoProvider` to hydrate
|
||||
if (!Edit || (!collectionSlug && !globalSlug)) {
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import type { GenerateViewMetadata } from '../Root/index.jsx'
|
||||
|
||||
import { meta } from '../../utilities/meta.js'
|
||||
|
||||
export const generateForgotPasswordMetadata = async ({ config, i18n: { t } }) => {
|
||||
export const generateForgotPasswordMetadata: GenerateViewMetadata = async ({
|
||||
config,
|
||||
i18n: { t },
|
||||
}) => {
|
||||
return meta({
|
||||
config,
|
||||
description: t('authentication:forgotPassword'),
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
import { render } from '@testing-library/react'
|
||||
import React from 'react'
|
||||
|
||||
import type { BlockField, DateField, SelectField } from 'payload/types'
|
||||
|
||||
import { BlocksCell } from './fields/Blocks'
|
||||
import { Checkbox } from './fields/Checkbox'
|
||||
import { DateCell } from './fields/Date'
|
||||
import { Select } from './fields/Select'
|
||||
import { Textarea } from './fields/Textarea'
|
||||
|
||||
jest.mock('../../../../utilities/Config', () => ({
|
||||
useConfig: () => ({ admin: { dateFormat: 'MMMM do yyyy, h:mm a' } }),
|
||||
}))
|
||||
|
||||
jest.mock('../../../../providers/Translation', () => ({
|
||||
useTranslation: () => ({ t: (string) => string }),
|
||||
}))
|
||||
|
||||
describe('Cell Types', () => {
|
||||
describe('Blocks', () => {
|
||||
const field: BlockField = {
|
||||
blocks: [
|
||||
{
|
||||
fields: [],
|
||||
labels: {
|
||||
plural: 'Numbers',
|
||||
singular: 'Number',
|
||||
},
|
||||
slug: 'number',
|
||||
},
|
||||
],
|
||||
label: 'Blocks Content',
|
||||
labels: {
|
||||
plural: 'Blocks Content',
|
||||
singular: 'Block',
|
||||
},
|
||||
name: 'blocks',
|
||||
type: 'blocks',
|
||||
}
|
||||
|
||||
it('renders multiple', () => {
|
||||
const data = [{ blockType: 'number' }, { blockType: 'number' }]
|
||||
const { container } = render(<BlocksCell data={data} field={field} />)
|
||||
const el = container.querySelector('span')
|
||||
expect(el).toHaveTextContent('2 Blocks Content - Number, Number')
|
||||
})
|
||||
|
||||
it('renders zero', () => {
|
||||
const data = []
|
||||
const { container } = render(<BlocksCell data={data} field={field} />)
|
||||
const el = container.querySelector('span')
|
||||
expect(el).toHaveTextContent('0 Blocks Content')
|
||||
})
|
||||
|
||||
it('renders "and X more" if over maximum of 5', () => {
|
||||
const data = [
|
||||
{ blockType: 'number' },
|
||||
{ blockType: 'number' },
|
||||
{ blockType: 'number' },
|
||||
{ blockType: 'number' },
|
||||
{ blockType: 'number' },
|
||||
{ blockType: 'number' },
|
||||
]
|
||||
|
||||
const { container } = render(<BlocksCell data={data} field={field} />)
|
||||
const el = container.querySelector('span')
|
||||
expect(el).toHaveTextContent('fields:itemsAndMore')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Date', () => {
|
||||
const field: DateField = {
|
||||
admin: {
|
||||
date: {
|
||||
pickerAppearance: 'dayOnly',
|
||||
},
|
||||
},
|
||||
name: 'dayOnly',
|
||||
type: 'date',
|
||||
}
|
||||
|
||||
it('renders date', () => {
|
||||
const timeStamp = '2020-10-06T14:07:39.033Z'
|
||||
const { container } = render(<DateCell data={timeStamp} field={field} />)
|
||||
const dateMatch = /October\s6th\s2020,\s\d{1,2}:07\s[A|P]M/ // Had to account for timezones in CI
|
||||
const el = container.querySelector('span')
|
||||
expect(el.textContent).toMatch(dateMatch)
|
||||
})
|
||||
|
||||
it('handles undefined', () => {
|
||||
const timeStamp = undefined
|
||||
const { container } = render(<DateCell data={timeStamp} field={field} />)
|
||||
const el = container.querySelector('span')
|
||||
expect(el.textContent).toBe('')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Checkbox', () => {
|
||||
it('renders true', () => {
|
||||
const { container } = render(<Checkbox data />)
|
||||
const el = container.querySelector('span')
|
||||
expect(el).toHaveTextContent('true')
|
||||
})
|
||||
it('renders false', () => {
|
||||
const { container } = render(<Checkbox data={false} />)
|
||||
const el = container.querySelector('span')
|
||||
expect(el).toHaveTextContent('false')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Textarea', () => {
|
||||
it('renders data', () => {
|
||||
const { container } = render(<Textarea data="data" />)
|
||||
const el = container.querySelector('span')
|
||||
expect(el).toHaveTextContent('data')
|
||||
})
|
||||
it('handle undefined - bug/13', () => {
|
||||
const { container } = render(<Textarea data={undefined} />)
|
||||
const el = container.querySelector('span')
|
||||
expect(el).toHaveTextContent('')
|
||||
})
|
||||
})
|
||||
describe('Select', () => {
|
||||
const fieldWithOptionsObject: SelectField = {
|
||||
name: 'selectObject',
|
||||
options: [
|
||||
{
|
||||
label: 'One',
|
||||
value: 'one',
|
||||
},
|
||||
{
|
||||
label: 'Two',
|
||||
value: 'two',
|
||||
},
|
||||
],
|
||||
type: 'select',
|
||||
}
|
||||
const fieldWithStringsOptions: SelectField = {
|
||||
name: 'selectString',
|
||||
options: ['blue', 'green', 'yellow'],
|
||||
type: 'select',
|
||||
}
|
||||
it('renders options objects', () => {
|
||||
const { container } = render(<Select data="one" field={fieldWithOptionsObject} />)
|
||||
const el = container.querySelector('span')
|
||||
expect(el).toHaveTextContent('One')
|
||||
})
|
||||
it('renders option strings', () => {
|
||||
const { container } = render(<Select data="blue" field={fieldWithStringsOptions} />)
|
||||
const el = container.querySelector('span')
|
||||
expect(el).toHaveTextContent('blue')
|
||||
})
|
||||
|
||||
describe('HasMany', () => {
|
||||
it('renders options objects', () => {
|
||||
const { container } = render(
|
||||
<Select data={['one', 'two']} field={fieldWithOptionsObject} />,
|
||||
)
|
||||
const el = container.querySelector('span')
|
||||
expect(el).toHaveTextContent('One, Two')
|
||||
})
|
||||
it('renders option strings', () => {
|
||||
const { container } = render(
|
||||
<Select data={['blue', 'green']} field={fieldWithStringsOptions} />,
|
||||
)
|
||||
const el = container.querySelector('span')
|
||||
expect(el).toHaveTextContent('blue, green')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -72,6 +72,7 @@ export const ListView: React.FC<AdminViewProps> = async ({ initPageResult, searc
|
||||
collection: collectionSlug,
|
||||
depth: 0,
|
||||
limit,
|
||||
overrideAccess: false,
|
||||
user,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import type { I18n } from '@payloadcms/translations'
|
||||
import type { Metadata } from 'next'
|
||||
import type { SanitizedConfig } from 'payload/types'
|
||||
|
||||
import { meta } from '../../utilities/meta.js'
|
||||
|
||||
export const generateNotFoundMeta = ({ config, i18n }: { config: SanitizedConfig; i18n: I18n }) => {
|
||||
export const generateNotFoundMeta = ({
|
||||
config,
|
||||
i18n,
|
||||
}: {
|
||||
config: SanitizedConfig
|
||||
i18n: I18n
|
||||
}): Promise<Metadata> => {
|
||||
return meta({
|
||||
config,
|
||||
description: i18n.t('general:pageNotFound'),
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { Metadata } from 'next'
|
||||
|
||||
import type { GenerateViewMetadata } from '../Root/index.js'
|
||||
|
||||
import { meta } from '../../utilities/meta.js'
|
||||
@@ -5,7 +7,7 @@ import { meta } from '../../utilities/meta.js'
|
||||
export const generateResetPasswordMetadata: GenerateViewMetadata = async ({
|
||||
config,
|
||||
i18n: { t },
|
||||
}) => {
|
||||
}): Promise<Metadata> => {
|
||||
return meta({
|
||||
config,
|
||||
description: t('authentication:resetPassword'),
|
||||
|
||||
@@ -15,7 +15,7 @@ export const VersionView: React.FC = async (props: ServerSideEditViewProps) => {
|
||||
docID: id,
|
||||
globalConfig,
|
||||
permissions,
|
||||
req: { payload, payload: { config } = {} } = {},
|
||||
req: { payload, payload: { config } = {}, user } = {},
|
||||
} = initPageResult
|
||||
|
||||
// /entityType/:entitySlug/:id/versions/:versionID
|
||||
@@ -43,6 +43,8 @@ export const VersionView: React.FC = async (props: ServerSideEditViewProps) => {
|
||||
collection: slug,
|
||||
depth: 1,
|
||||
locale: '*',
|
||||
overrideAccess: false,
|
||||
user,
|
||||
})
|
||||
|
||||
publishedDoc = await payload.findByID({
|
||||
@@ -51,6 +53,8 @@ export const VersionView: React.FC = async (props: ServerSideEditViewProps) => {
|
||||
depth: 1,
|
||||
draft: false,
|
||||
locale: '*',
|
||||
overrideAccess: false,
|
||||
user,
|
||||
})
|
||||
|
||||
mostRecentDoc = await payload.findByID({
|
||||
@@ -59,6 +63,8 @@ export const VersionView: React.FC = async (props: ServerSideEditViewProps) => {
|
||||
depth: 1,
|
||||
draft: true,
|
||||
locale: '*',
|
||||
overrideAccess: false,
|
||||
user,
|
||||
})
|
||||
} catch (error) {
|
||||
return notFound()
|
||||
@@ -75,6 +81,8 @@ export const VersionView: React.FC = async (props: ServerSideEditViewProps) => {
|
||||
slug,
|
||||
depth: 1,
|
||||
locale: '*',
|
||||
overrideAccess: false,
|
||||
user,
|
||||
})
|
||||
|
||||
publishedDoc = payload.findGlobal({
|
||||
@@ -82,6 +90,8 @@ export const VersionView: React.FC = async (props: ServerSideEditViewProps) => {
|
||||
depth: 1,
|
||||
draft: false,
|
||||
locale: '*',
|
||||
overrideAccess: false,
|
||||
user,
|
||||
})
|
||||
|
||||
mostRecentDoc = payload.findGlobal({
|
||||
@@ -89,6 +99,8 @@ export const VersionView: React.FC = async (props: ServerSideEditViewProps) => {
|
||||
depth: 1,
|
||||
draft: true,
|
||||
locale: '*',
|
||||
overrideAccess: false,
|
||||
user,
|
||||
})
|
||||
} catch (error) {
|
||||
return notFound()
|
||||
|
||||
@@ -43,6 +43,7 @@ export const VersionsView: React.FC<ServerSideEditViewProps> = async (props) =>
|
||||
collection: collectionSlug,
|
||||
depth: 0,
|
||||
limit: limit ? parseInt(limit?.toString(), 10) : undefined,
|
||||
overrideAccess: false,
|
||||
page: page ? parseInt(page.toString(), 10) : undefined,
|
||||
sort: sort as string,
|
||||
user,
|
||||
@@ -62,6 +63,7 @@ export const VersionsView: React.FC<ServerSideEditViewProps> = async (props) =>
|
||||
versionsData = await payload.findGlobalVersions({
|
||||
slug: globalSlug,
|
||||
depth: 0,
|
||||
overrideAccess: false,
|
||||
page: page ? parseInt(page as string, 10) : undefined,
|
||||
sort: sort as string,
|
||||
user,
|
||||
|
||||
@@ -33,8 +33,6 @@ const withPayload = (nextConfig = {}) => {
|
||||
...(incomingWebpackConfig?.externals || []),
|
||||
'drizzle-kit',
|
||||
'drizzle-kit/payload',
|
||||
'pino',
|
||||
'pino-pretty',
|
||||
'sharp',
|
||||
'libsql',
|
||||
],
|
||||
|
||||
107
packages/next/webpack.config.js
Normal file
107
packages/next/webpack.config.js
Normal file
@@ -0,0 +1,107 @@
|
||||
import OptimizeCSSAssetsPlugin from 'css-minimizer-webpack-plugin'
|
||||
import MiniCSSExtractPlugin from 'mini-css-extract-plugin'
|
||||
import path from 'path'
|
||||
import TerserJSPlugin from 'terser-webpack-plugin'
|
||||
import { fileURLToPath } from 'url'
|
||||
import webpack from 'webpack'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
const componentWebpackConfig = {
|
||||
entry: path.resolve(dirname, './src/index.ts'),
|
||||
externals: ['react', 'react-dom', 'payload', 'payload/config', 'react-image-crop'],
|
||||
mode: 'production',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
oneOf: [
|
||||
{
|
||||
// exclude: /node_modules/,
|
||||
test: /\.(t|j)sx?$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'swc-loader',
|
||||
options: {
|
||||
jsc: {
|
||||
experimental: {
|
||||
plugins: [
|
||||
// clear the plugins used in .swcrc
|
||||
],
|
||||
},
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
tsx: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
sideEffects: true,
|
||||
test: /\.(scss|css)$/,
|
||||
use: [
|
||||
MiniCSSExtractPlugin.loader,
|
||||
'css-loader',
|
||||
{
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
postcssOptions: {
|
||||
plugins: ['postcss-preset-env'],
|
||||
},
|
||||
},
|
||||
},
|
||||
'sass-loader',
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'asset/resource',
|
||||
generator: {
|
||||
filename: 'payload/[name][ext]',
|
||||
},
|
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/, /\.svg$/, /\.woff$/, /\.woff2$/],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
optimization: {
|
||||
minimizer: [
|
||||
new TerserJSPlugin({
|
||||
extractComments: false,
|
||||
}),
|
||||
new OptimizeCSSAssetsPlugin({}),
|
||||
],
|
||||
},
|
||||
output: {
|
||||
filename: 'index.js',
|
||||
libraryTarget: 'commonjs2',
|
||||
path: path.resolve(dirname, './dist/prod'),
|
||||
publicPath: '/',
|
||||
},
|
||||
plugins: [
|
||||
new MiniCSSExtractPlugin({
|
||||
filename: 'styles.css',
|
||||
ignoreOrder: true,
|
||||
}),
|
||||
new webpack.optimize.LimitChunkCountPlugin({
|
||||
maxChunks: 1,
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
extensionAlias: {
|
||||
'.js': ['.ts', '.tsx', '.js', '.scss', '.css'],
|
||||
'.mjs': ['.mts', '.mjs'],
|
||||
},
|
||||
extensions: ['.js', '.ts', '.tsx', '.scss', '.css'],
|
||||
modules: [
|
||||
'node_modules',
|
||||
path.resolve(dirname, '../../node_modules'),
|
||||
path.resolve(dirname, './node_modules'),
|
||||
],
|
||||
},
|
||||
stats: 'errors-only',
|
||||
}
|
||||
|
||||
export default componentWebpackConfig
|
||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"sourceMaps": true,
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { getTsconfig } = require('get-tsconfig')
|
||||
const path = require('path')
|
||||
const swcRegister = require('@swc/register')
|
||||
|
||||
const tsConfig = getTsconfig()
|
||||
|
||||
const swcOptions = {
|
||||
ignore: [/.*[\\/]node_modules[\\/].*/],
|
||||
jsc: {
|
||||
baseUrl: path.resolve(),
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
tsx: true,
|
||||
},
|
||||
paths: undefined,
|
||||
},
|
||||
module: {
|
||||
type: 'es6',
|
||||
},
|
||||
sourceMaps: 'inline',
|
||||
}
|
||||
if (tsConfig?.config?.compilerOptions?.paths) {
|
||||
swcOptions.jsc.paths = tsConfig.config.compilerOptions.paths
|
||||
if (tsConfig?.config?.compilerOptions?.baseUrl) {
|
||||
swcOptions.jsc.baseUrl = path.resolve(tsConfig.config.compilerOptions.baseUrl)
|
||||
}
|
||||
}
|
||||
// Allow disabling SWC for debugging
|
||||
if (process.env.DISABLE_SWC !== 'true') {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error - bad @swc/register types
|
||||
swcRegister(swcOptions)
|
||||
}
|
||||
|
||||
const bin = async () => {
|
||||
await import('./dist/bin/index.js')
|
||||
}
|
||||
|
||||
bin()
|
||||
18
packages/payload/bin.js
Executable file
18
packages/payload/bin.js
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { register } from 'node:module'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
|
||||
import { bin } from './dist/bin/index.js'
|
||||
|
||||
// Allow disabling SWC for debugging
|
||||
if (process.env.DISABLE_SWC !== 'true') {
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
const url = pathToFileURL(dirname).toString() + '/'
|
||||
|
||||
register('./dist/bin/register/index.js', url)
|
||||
}
|
||||
|
||||
bin()
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "payload",
|
||||
"version": "3.0.0-alpha.34",
|
||||
"version": "3.0.0-alpha.45",
|
||||
"description": "Node, React and MongoDB Headless CMS and Application Framework",
|
||||
"license": "MIT",
|
||||
"main": "./src/index.js",
|
||||
"types": "./src/index.ts",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"payload": "bin.cjs"
|
||||
"payload": "bin.js"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
@@ -43,11 +43,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/translations": "workspace:*",
|
||||
"@swc-node/core": "^1.13.0",
|
||||
"@swc-node/sourcemap-support": "^0.5.0",
|
||||
"bson-objectid": "2.0.4",
|
||||
"conf": "10.2.0",
|
||||
"console-table-printer": "2.11.2",
|
||||
"dataloader": "2.2.2",
|
||||
"deepmerge": "4.3.1",
|
||||
"dotenv": "8.6.0",
|
||||
"file-type": "16.5.4",
|
||||
"find-up": "4.1.0",
|
||||
"get-tsconfig": "^4.7.2",
|
||||
@@ -62,6 +65,7 @@
|
||||
"nodemailer": "6.9.10",
|
||||
"pino": "8.15.0",
|
||||
"pino-pretty": "10.2.0",
|
||||
"pirates": "^4.0.6",
|
||||
"pluralize": "8.0.0",
|
||||
"sanitize-filename": "1.6.3",
|
||||
"scheduler": "0.23.0",
|
||||
|
||||
@@ -4,7 +4,6 @@ import { compile } from 'json-schema-to-typescript'
|
||||
|
||||
import type { SanitizedConfig } from '../config/types.js'
|
||||
|
||||
import loadConfig from '../config/load.js'
|
||||
import { configToJSONSchema } from '../utilities/configToJSONSchema.js'
|
||||
import Logger from '../utilities/logger.js'
|
||||
|
||||
@@ -36,13 +35,3 @@ export function generateTypes(config: SanitizedConfig): void {
|
||||
logger.info(`Types written to ${outputFile}`)
|
||||
})
|
||||
}
|
||||
|
||||
// when generateTypes.js is launched directly
|
||||
if (module.id === require.main.id) {
|
||||
const loadConfigAndGenerateTypes = async () => {
|
||||
const config = await loadConfig()
|
||||
generateTypes(config)
|
||||
}
|
||||
|
||||
loadConfigAndGenerateTypes()
|
||||
}
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
/* eslint-disable no-console */
|
||||
import minimist from 'minimist'
|
||||
|
||||
import type { BinScript } from '../config/types.js'
|
||||
|
||||
import loadConfig from '../config/load.js'
|
||||
import { findConfig } from '../config/find.js'
|
||||
import { generateTypes } from './generateTypes.js'
|
||||
import { loadEnv } from './loadEnv.js'
|
||||
import { migrate } from './migrate.js'
|
||||
|
||||
loadEnv()
|
||||
export const bin = async () => {
|
||||
loadEnv()
|
||||
const configPath = findConfig()
|
||||
const configPromise = await import(configPath)
|
||||
let config = await configPromise
|
||||
if (config.default) config = await config.default
|
||||
|
||||
const executeBin = async () => {
|
||||
const args = minimist(process.argv.slice(2))
|
||||
const scriptIndex = args._.findIndex((x) => x === 'build')
|
||||
const script = scriptIndex === -1 ? args._[0] : args._[scriptIndex]
|
||||
const config = await loadConfig()
|
||||
const userBinScript = config.bin.find(({ key }) => key === script)
|
||||
const script = (typeof args._[0] === 'string' ? args._[0] : '').toLowerCase()
|
||||
|
||||
const userBinScript = Array.isArray(config.bin)
|
||||
? config.bin.find(({ key }) => key === script)
|
||||
: false
|
||||
|
||||
if (userBinScript) {
|
||||
try {
|
||||
const script: BinScript = require(userBinScript.scriptPath)
|
||||
const script: BinScript = await import(userBinScript.scriptPath)
|
||||
await script(config)
|
||||
} catch (err) {
|
||||
console.log(`Could not find associated bin script for the ${userBinScript.key} command`)
|
||||
@@ -29,19 +35,13 @@ const executeBin = async () => {
|
||||
}
|
||||
|
||||
if (script.startsWith('migrate')) {
|
||||
migrate(args).then(() => process.exit(0))
|
||||
} else {
|
||||
switch (script.toLowerCase()) {
|
||||
case 'generate:types': {
|
||||
generateTypes(config)
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
console.log(`Unknown script "${script}".`)
|
||||
break
|
||||
}
|
||||
return migrate({ config, parsedArgs: args }).then(() => process.exit(0))
|
||||
}
|
||||
}
|
||||
|
||||
executeBin()
|
||||
if (script === 'generate:types') {
|
||||
return generateTypes(config)
|
||||
}
|
||||
|
||||
console.log(`Unknown script: "${script}".`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import type { ParsedArgs } from 'minimist'
|
||||
|
||||
import minimist from 'minimist'
|
||||
import type { SanitizedConfig } from '../exports/types.js'
|
||||
|
||||
import loadConfig from '../config/load.js'
|
||||
import payload from '../index.js'
|
||||
import { prettySyncLoggerDestination } from '../utilities/logger.js'
|
||||
|
||||
@@ -27,7 +26,12 @@ const availableCommands = [
|
||||
|
||||
const availableCommandsMsg = `Available commands: ${availableCommands.join(', ')}`
|
||||
|
||||
export const migrate = async (parsedArgs: ParsedArgs): Promise<void> => {
|
||||
type Args = {
|
||||
config: SanitizedConfig
|
||||
parsedArgs: ParsedArgs
|
||||
}
|
||||
|
||||
export const migrate = async ({ config, parsedArgs }: Args): Promise<void> => {
|
||||
const { _: args, file, forceAcceptWarning, help } = parsedArgs
|
||||
if (help) {
|
||||
// eslint-disable-next-line no-console
|
||||
@@ -39,7 +43,7 @@ export const migrate = async (parsedArgs: ParsedArgs): Promise<void> => {
|
||||
|
||||
// Barebones instance to access database adapter
|
||||
await payload.init({
|
||||
config: loadConfig(),
|
||||
config,
|
||||
disableOnInit: true,
|
||||
...prettySyncLogger,
|
||||
})
|
||||
@@ -98,12 +102,3 @@ export const migrate = async (parsedArgs: ParsedArgs): Promise<void> => {
|
||||
|
||||
payload.logger.info('Done.')
|
||||
}
|
||||
|
||||
// When launched directly call migrate
|
||||
if (module.id === require.main.id) {
|
||||
const args = minimist(process.argv.slice(2))
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
migrate(args).then(() => {
|
||||
process.exit(0)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export const clientFiles = [
|
||||
export const CLIENT_EXTENSIONS = [
|
||||
'.scss',
|
||||
'.css',
|
||||
'.svg',
|
||||
152
packages/payload/src/bin/register/index.ts
Normal file
152
packages/payload/src/bin/register/index.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
/* eslint-disable @typescript-eslint/unbound-method */
|
||||
import { getTsconfig } from 'get-tsconfig'
|
||||
import path from 'path'
|
||||
import ts from 'typescript'
|
||||
import { fileURLToPath, pathToFileURL } from 'url'
|
||||
|
||||
import { CLIENT_EXTENSIONS } from './clientExtensions.js'
|
||||
import { compile } from './register.js'
|
||||
|
||||
interface ResolveContext {
|
||||
conditions: string[]
|
||||
parentURL: string | undefined
|
||||
}
|
||||
interface ResolveResult {
|
||||
format?: string
|
||||
shortCircuit?: boolean
|
||||
url: string
|
||||
}
|
||||
type ResolveArgs = [
|
||||
specifier: string,
|
||||
context?: ResolveContext,
|
||||
nextResolve?: (...args: ResolveArgs) => Promise<ResolveResult>,
|
||||
]
|
||||
type ResolveFn = (...args: Required<ResolveArgs>) => Promise<ResolveResult>
|
||||
|
||||
const locatedConfig = getTsconfig()
|
||||
const tsconfig = locatedConfig.config.compilerOptions as unknown as ts.CompilerOptions
|
||||
|
||||
tsconfig.module = ts.ModuleKind.ESNext
|
||||
tsconfig.moduleResolution = ts.ModuleResolutionKind.NodeNext
|
||||
|
||||
const moduleResolutionCache = ts.createModuleResolutionCache(
|
||||
ts.sys.getCurrentDirectory(),
|
||||
(x) => x,
|
||||
tsconfig,
|
||||
)
|
||||
const host: ts.ModuleResolutionHost = {
|
||||
fileExists: ts.sys.fileExists,
|
||||
readFile: ts.sys.readFile,
|
||||
}
|
||||
const EXTENSIONS: string[] = [ts.Extension.Ts, ts.Extension.Tsx, ts.Extension.Dts, ts.Extension.Mts]
|
||||
|
||||
export const resolve: ResolveFn = async (specifier, context, nextResolve) => {
|
||||
const isTS = EXTENSIONS.some((ext) => specifier.endsWith(ext))
|
||||
const isClient = CLIENT_EXTENSIONS.some((ext) => specifier.endsWith(ext))
|
||||
|
||||
if (isClient) {
|
||||
const nextResult = await nextResolve(specifier, context, nextResolve)
|
||||
const specifierSegments = specifier.split('.')
|
||||
|
||||
return {
|
||||
format: '.' + specifierSegments[specifierSegments.length - 1],
|
||||
shortCircuit: true,
|
||||
url: nextResult.url,
|
||||
}
|
||||
}
|
||||
|
||||
// entrypoint
|
||||
if (!context.parentURL) {
|
||||
return {
|
||||
format: isTS ? 'ts' : undefined,
|
||||
shortCircuit: true,
|
||||
url: specifier,
|
||||
}
|
||||
}
|
||||
|
||||
// import/require from external library
|
||||
if (context.parentURL.includes('/node_modules/') && !isTS) {
|
||||
return nextResolve(specifier)
|
||||
}
|
||||
|
||||
const { resolvedModule } = ts.resolveModuleName(
|
||||
specifier,
|
||||
fileURLToPath(context.parentURL),
|
||||
tsconfig,
|
||||
host,
|
||||
moduleResolutionCache,
|
||||
)
|
||||
|
||||
// import from local project to local project TS file
|
||||
if (
|
||||
resolvedModule &&
|
||||
!resolvedModule.resolvedFileName.includes('/node_modules/') &&
|
||||
EXTENSIONS.includes(resolvedModule.extension)
|
||||
) {
|
||||
return {
|
||||
format: 'ts',
|
||||
shortCircuit: true,
|
||||
url: pathToFileURL(resolvedModule.resolvedFileName).href,
|
||||
}
|
||||
}
|
||||
|
||||
// import from local project to either:
|
||||
// - something TS couldn't resolve
|
||||
// - external library
|
||||
// - local project non-TS file
|
||||
return nextResolve(specifier)
|
||||
}
|
||||
|
||||
interface LoadContext {
|
||||
conditions: string[]
|
||||
format: null | string | undefined
|
||||
}
|
||||
interface LoadResult {
|
||||
format: string
|
||||
shortCircuit?: boolean
|
||||
source: ArrayBuffer | SharedArrayBuffer | Uint8Array | string
|
||||
}
|
||||
type LoadArgs = [
|
||||
url: string,
|
||||
context: LoadContext,
|
||||
nextLoad?: (...args: LoadArgs) => Promise<LoadResult>,
|
||||
]
|
||||
type LoadFn = (...args: Required<LoadArgs>) => Promise<LoadResult>
|
||||
|
||||
const swcOptions = {
|
||||
...tsconfig,
|
||||
baseUrl: path.resolve(''),
|
||||
paths: undefined,
|
||||
}
|
||||
|
||||
if (tsconfig.paths) {
|
||||
swcOptions.paths = tsconfig.paths
|
||||
if (tsconfig.baseUrl) {
|
||||
swcOptions.baseUrl = path.resolve(tsconfig.baseUrl)
|
||||
}
|
||||
}
|
||||
|
||||
export const load: LoadFn = async (url, context, nextLoad) => {
|
||||
if (CLIENT_EXTENSIONS.some((e) => context.format === e)) {
|
||||
const rawSource = '{}'
|
||||
|
||||
return {
|
||||
format: 'json',
|
||||
shortCircuit: true,
|
||||
source: rawSource,
|
||||
}
|
||||
}
|
||||
|
||||
if (context.format === 'ts') {
|
||||
const { source } = await nextLoad(url, context)
|
||||
const code = typeof source === 'string' ? source : Buffer.from(source).toString()
|
||||
const compiled = await compile(code, fileURLToPath(url), swcOptions, true)
|
||||
return {
|
||||
format: 'module',
|
||||
shortCircuit: true,
|
||||
source: compiled,
|
||||
}
|
||||
} else {
|
||||
return nextLoad(url, context)
|
||||
}
|
||||
}
|
||||
108
packages/payload/src/bin/register/read-default-tsconfig.ts
Normal file
108
packages/payload/src/bin/register/read-default-tsconfig.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import type { Options } from '@swc-node/core'
|
||||
|
||||
import { resolve } from 'path'
|
||||
import ts from 'typescript'
|
||||
|
||||
function toTsTarget(target: ts.ScriptTarget): Options['target'] {
|
||||
switch (target) {
|
||||
case ts.ScriptTarget.ES3:
|
||||
return 'es3'
|
||||
case ts.ScriptTarget.ES5:
|
||||
return 'es5'
|
||||
case ts.ScriptTarget.ES2015:
|
||||
return 'es2015'
|
||||
case ts.ScriptTarget.ES2016:
|
||||
return 'es2016'
|
||||
case ts.ScriptTarget.ES2017:
|
||||
return 'es2017'
|
||||
case ts.ScriptTarget.ES2018:
|
||||
return 'es2018'
|
||||
case ts.ScriptTarget.ES2019:
|
||||
return 'es2019'
|
||||
case ts.ScriptTarget.ES2020:
|
||||
return 'es2020'
|
||||
case ts.ScriptTarget.ES2021:
|
||||
return 'es2021'
|
||||
case ts.ScriptTarget.ES2022:
|
||||
case ts.ScriptTarget.ESNext:
|
||||
case ts.ScriptTarget.Latest:
|
||||
return 'es2022'
|
||||
case ts.ScriptTarget.JSON:
|
||||
return 'es5'
|
||||
}
|
||||
}
|
||||
|
||||
function toModule(moduleKind: ts.ModuleKind) {
|
||||
switch (moduleKind) {
|
||||
case ts.ModuleKind.CommonJS:
|
||||
return 'commonjs'
|
||||
case ts.ModuleKind.UMD:
|
||||
return 'umd'
|
||||
case ts.ModuleKind.AMD:
|
||||
return 'amd'
|
||||
case ts.ModuleKind.ES2015:
|
||||
case ts.ModuleKind.ES2020:
|
||||
case ts.ModuleKind.ES2022:
|
||||
case ts.ModuleKind.ESNext:
|
||||
case ts.ModuleKind.Node16:
|
||||
case ts.ModuleKind.NodeNext:
|
||||
case ts.ModuleKind.None:
|
||||
return 'es6'
|
||||
case ts.ModuleKind.System:
|
||||
throw new TypeError('Do not support system kind module')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The default value for useDefineForClassFields depends on the emit target
|
||||
* @see https://www.typescriptlang.org/tsconfig#useDefineForClassFields
|
||||
*/
|
||||
function getUseDefineForClassFields(
|
||||
compilerOptions: ts.CompilerOptions,
|
||||
target: ts.ScriptTarget,
|
||||
): boolean {
|
||||
return compilerOptions.useDefineForClassFields ?? target >= ts.ScriptTarget.ES2022
|
||||
}
|
||||
|
||||
export function tsCompilerOptionsToSwcConfig(
|
||||
options: ts.CompilerOptions,
|
||||
filename: string,
|
||||
): Options {
|
||||
const isJsx = filename.endsWith('.tsx') || filename.endsWith('.jsx') || Boolean(options.jsx)
|
||||
const target = options.target ?? ts.ScriptTarget.ES2018
|
||||
return {
|
||||
baseUrl: options.baseUrl ? resolve(options.baseUrl) : undefined,
|
||||
dynamicImport: true,
|
||||
emitDecoratorMetadata: options.emitDecoratorMetadata ?? false,
|
||||
esModuleInterop: options.esModuleInterop ?? false,
|
||||
experimentalDecorators: options.experimentalDecorators ?? false,
|
||||
externalHelpers: Boolean(options.importHelpers),
|
||||
ignoreDynamic: Boolean(process.env.SWC_NODE_IGNORE_DYNAMIC),
|
||||
jsx: isJsx,
|
||||
keepClassNames: true,
|
||||
module: toModule(options.module ?? ts.ModuleKind.ES2015),
|
||||
paths: Object.fromEntries(
|
||||
Object.entries(options.paths ?? {}).map(([aliasKey, aliasPaths]) => [
|
||||
aliasKey,
|
||||
(aliasPaths ?? []).map((path) => resolve(options.baseUrl ?? './', path)),
|
||||
]),
|
||||
) as Options['paths'],
|
||||
react:
|
||||
options.jsxFactory ?? options.jsxFragmentFactory ?? options.jsx ?? options.jsxImportSource
|
||||
? {
|
||||
importSource: options.jsxImportSource ?? 'react',
|
||||
pragma: options.jsxFactory,
|
||||
pragmaFrag: options.jsxFragmentFactory,
|
||||
runtime: (options.jsx ?? 0) >= ts.JsxEmit.ReactJSX ? 'automatic' : 'classic',
|
||||
useBuiltins: true,
|
||||
}
|
||||
: undefined,
|
||||
sourcemap: options.sourceMap && options.inlineSourceMap ? 'inline' : Boolean(options.sourceMap),
|
||||
swc: {
|
||||
inputSourceMap: options.inlineSourceMap,
|
||||
sourceRoot: options.sourceRoot,
|
||||
},
|
||||
target: toTsTarget(target),
|
||||
useDefineForClassFields: getUseDefineForClassFields(options, target),
|
||||
}
|
||||
}
|
||||
125
packages/payload/src/bin/register/register.ts
Normal file
125
packages/payload/src/bin/register/register.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import type { Options } from '@swc-node/core'
|
||||
|
||||
import { transform, transformSync } from '@swc-node/core'
|
||||
import { SourcemapMap, installSourceMapSupport } from '@swc-node/sourcemap-support'
|
||||
import { getTsconfig } from 'get-tsconfig'
|
||||
import { platform } from 'os'
|
||||
import { resolve } from 'path'
|
||||
import { addHook } from 'pirates'
|
||||
import * as ts from 'typescript'
|
||||
|
||||
import { tsCompilerOptionsToSwcConfig } from './read-default-tsconfig.js'
|
||||
|
||||
const DEFAULT_EXTENSIONS = ['.js', '.jsx', '.es6', '.es', '.mjs', '.ts', '.tsx']
|
||||
const PLATFORM = platform()
|
||||
|
||||
const injectInlineSourceMap = ({
|
||||
code,
|
||||
filename,
|
||||
map,
|
||||
}: {
|
||||
code: string
|
||||
filename: string
|
||||
map: string | undefined
|
||||
}): string => {
|
||||
if (map) {
|
||||
SourcemapMap.set(filename, map)
|
||||
const base64Map = Buffer.from(map, 'utf8').toString('base64')
|
||||
const sourceMapContent = `//# sourceMappingURL=data:application/json;charset=utf-8;base64,${base64Map}`
|
||||
return `${code}\n${sourceMapContent}`
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
export function compile(
|
||||
sourcecode: string,
|
||||
filename: string,
|
||||
options: ts.CompilerOptions & { fallbackToTs?: (filename: string) => boolean },
|
||||
): string
|
||||
|
||||
export function compile(
|
||||
sourcecode: string,
|
||||
filename: string,
|
||||
options: ts.CompilerOptions & { fallbackToTs?: (filename: string) => boolean },
|
||||
async: false,
|
||||
): string
|
||||
|
||||
export function compile(
|
||||
sourcecode: string,
|
||||
filename: string,
|
||||
options: ts.CompilerOptions & { fallbackToTs?: (filename: string) => boolean },
|
||||
async: true,
|
||||
): Promise<string>
|
||||
|
||||
export function compile(
|
||||
sourcecode: string,
|
||||
filename: string,
|
||||
options: ts.CompilerOptions & { fallbackToTs?: (filename: string) => boolean },
|
||||
async: boolean,
|
||||
): Promise<string> | string
|
||||
|
||||
export function compile(
|
||||
sourcecode: string,
|
||||
filename: string,
|
||||
options: ts.CompilerOptions & { fallbackToTs?: (filename: string) => boolean },
|
||||
async = false,
|
||||
) {
|
||||
if (filename.endsWith('.d.ts')) {
|
||||
return ''
|
||||
}
|
||||
if (options.files && (options.files as string[]).length) {
|
||||
if (
|
||||
PLATFORM === 'win32' &&
|
||||
(options.files as string[]).every((file) => filename !== resolve(process.cwd(), file))
|
||||
) {
|
||||
return sourcecode
|
||||
}
|
||||
if (
|
||||
PLATFORM !== 'win32' &&
|
||||
(options.files as string[]).every((file) => !filename.endsWith(file))
|
||||
) {
|
||||
return sourcecode
|
||||
}
|
||||
}
|
||||
if (options && typeof options.fallbackToTs === 'function' && options.fallbackToTs(filename)) {
|
||||
delete options.fallbackToTs
|
||||
const { outputText, sourceMapText } = ts.transpileModule(sourcecode, {
|
||||
compilerOptions: options,
|
||||
fileName: filename,
|
||||
})
|
||||
return injectInlineSourceMap({ code: outputText, filename, map: sourceMapText })
|
||||
}
|
||||
|
||||
let swcRegisterConfig: Options
|
||||
if (process.env.SWCRC) {
|
||||
// when SWCRC environment variable is set to true it will use swcrc file
|
||||
swcRegisterConfig = {
|
||||
swc: {
|
||||
swcrc: true,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
swcRegisterConfig = tsCompilerOptionsToSwcConfig(options, filename)
|
||||
}
|
||||
|
||||
if (async) {
|
||||
return transform(sourcecode, filename, swcRegisterConfig).then(({ code, map }) => {
|
||||
return injectInlineSourceMap({ code, filename, map })
|
||||
})
|
||||
} else {
|
||||
const { code, map } = transformSync(sourcecode, filename, swcRegisterConfig)
|
||||
return injectInlineSourceMap({ code, filename, map })
|
||||
}
|
||||
}
|
||||
|
||||
export function register(options: Partial<ts.CompilerOptions> = {}, hookOpts = {}) {
|
||||
const locatedConfig = getTsconfig()
|
||||
const tsconfig = locatedConfig.config.compilerOptions as unknown as ts.CompilerOptions
|
||||
options = tsconfig
|
||||
// options.module = ts.ModuleKind.CommonJS
|
||||
installSourceMapSupport()
|
||||
return addHook((code, filename) => compile(code, filename, options), {
|
||||
exts: DEFAULT_EXTENSIONS,
|
||||
...hookOpts,
|
||||
})
|
||||
}
|
||||
@@ -94,16 +94,10 @@ const collectionSchema = joi.object().keys({
|
||||
maxLoginAttempts: joi.number(),
|
||||
removeTokenFromResponses: joi.boolean().valid(true),
|
||||
strategies: joi.array().items(
|
||||
joi.alternatives().try(
|
||||
strategyBaseSchema.keys({
|
||||
name: joi.string().required(),
|
||||
strategy: joi.func().maxArity(1).required(),
|
||||
}),
|
||||
strategyBaseSchema.keys({
|
||||
name: joi.string(),
|
||||
strategy: joi.object().required(),
|
||||
}),
|
||||
),
|
||||
joi.object().keys({
|
||||
name: joi.string().required(),
|
||||
authenticate: joi.func().required(),
|
||||
}),
|
||||
),
|
||||
tokenExpiration: joi.number(),
|
||||
useAPIKey: joi.boolean(),
|
||||
|
||||
@@ -1,23 +1,35 @@
|
||||
import type { Field } from '../fields/config/types.js'
|
||||
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from '../exports/types.js'
|
||||
import type { Field, FieldBase, RichTextField } from '../fields/config/types.js'
|
||||
import type { ClientConfig, SanitizedConfig } from './types.js'
|
||||
|
||||
export const sanitizeField = (f) => {
|
||||
export const sanitizeField = (f: Field) => {
|
||||
const field = { ...f }
|
||||
|
||||
if ('access' in field) delete field.access
|
||||
if ('hooks' in field) delete field.hooks
|
||||
if ('validate' in field) delete field.validate
|
||||
if ('defaultValue' in field) delete field.defaultValue
|
||||
if ('label' in field) delete field.label
|
||||
const serverOnlyFieldProperties: Partial<keyof FieldBase | keyof RichTextField>[] = [
|
||||
'hooks',
|
||||
'access',
|
||||
'validate',
|
||||
'defaultValue',
|
||||
'label',
|
||||
// This is a `richText` only property
|
||||
'editor',
|
||||
// `fields`
|
||||
// `blocks`
|
||||
// `tabs`
|
||||
// `admin`
|
||||
// are all handled separately
|
||||
]
|
||||
|
||||
serverOnlyFieldProperties.forEach((key) => {
|
||||
if (key in field) {
|
||||
delete field[key]
|
||||
}
|
||||
})
|
||||
|
||||
if ('fields' in field) {
|
||||
field.fields = sanitizeFields(field.fields)
|
||||
}
|
||||
|
||||
if ('editor' in field) {
|
||||
delete field.editor
|
||||
}
|
||||
|
||||
if ('blocks' in field) {
|
||||
field.blocks = field.blocks.map((block) => {
|
||||
const sanitized = { ...block }
|
||||
@@ -27,23 +39,20 @@ export const sanitizeField = (f) => {
|
||||
}
|
||||
|
||||
if ('tabs' in field) {
|
||||
// @ts-expect-error
|
||||
field.tabs = field.tabs.map((tab) => sanitizeField(tab))
|
||||
}
|
||||
|
||||
if ('admin' in field) {
|
||||
field.admin = { ...field.admin }
|
||||
|
||||
if ('components' in field.admin) {
|
||||
delete field.admin.components
|
||||
}
|
||||
const serverOnlyFieldAdminProperties = ['components', 'condition', 'description']
|
||||
|
||||
if ('condition' in field.admin) {
|
||||
delete field.admin.condition
|
||||
}
|
||||
|
||||
if ('description' in field.admin) {
|
||||
delete field.admin.description
|
||||
}
|
||||
serverOnlyFieldAdminProperties.forEach((key) => {
|
||||
if (key in field.admin) {
|
||||
delete field.admin[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return field
|
||||
@@ -56,31 +65,45 @@ const sanitizeCollections = (
|
||||
const sanitized = { ...collection }
|
||||
sanitized.fields = sanitizeFields(sanitized.fields)
|
||||
|
||||
delete sanitized.hooks
|
||||
delete sanitized.access
|
||||
delete sanitized.endpoints
|
||||
const serverOnlyCollectionProperties: Partial<keyof SanitizedCollectionConfig>[] = [
|
||||
'hooks',
|
||||
'access',
|
||||
'endpoints',
|
||||
// `upload`
|
||||
// `admin`
|
||||
// are all handled separately
|
||||
]
|
||||
|
||||
if ('editor' in sanitized) delete sanitized.editor
|
||||
serverOnlyCollectionProperties.forEach((key) => {
|
||||
if (key in sanitized) {
|
||||
delete sanitized[key]
|
||||
}
|
||||
})
|
||||
|
||||
if ('upload' in sanitized && typeof sanitized.upload === 'object') {
|
||||
sanitized.upload = { ...sanitized.upload }
|
||||
delete sanitized.upload.handlers
|
||||
}
|
||||
|
||||
if ('auth' in sanitized && typeof sanitized.auth === 'object') {
|
||||
sanitized.auth = { ...sanitized.auth }
|
||||
delete sanitized.auth.strategies
|
||||
delete sanitized.auth.forgotPassword
|
||||
delete sanitized.auth.verify
|
||||
}
|
||||
|
||||
if ('admin' in sanitized) {
|
||||
sanitized.admin = { ...sanitized.admin }
|
||||
|
||||
if ('components' in sanitized.admin) {
|
||||
delete sanitized.admin.components
|
||||
}
|
||||
const serverOnlyCollectionAdminProperties: Partial<
|
||||
keyof SanitizedCollectionConfig['admin']
|
||||
>[] = ['components', 'hidden', 'preview']
|
||||
|
||||
if ('hidden' in sanitized.admin) {
|
||||
delete sanitized.admin.hidden
|
||||
}
|
||||
|
||||
if ('preview' in sanitized.admin) {
|
||||
delete sanitized.admin.preview
|
||||
}
|
||||
serverOnlyCollectionAdminProperties.forEach((key) => {
|
||||
if (key in sanitized.admin) {
|
||||
delete sanitized.admin[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return sanitized
|
||||
@@ -90,24 +113,35 @@ const sanitizeGlobals = (globals: SanitizedConfig['globals']): ClientConfig['glo
|
||||
globals.map((global) => {
|
||||
const sanitized = { ...global }
|
||||
sanitized.fields = sanitizeFields(sanitized.fields)
|
||||
delete sanitized.hooks
|
||||
delete sanitized.access
|
||||
delete sanitized.endpoints
|
||||
|
||||
const serverOnlyProperties: Partial<keyof SanitizedGlobalConfig>[] = [
|
||||
'hooks',
|
||||
'access',
|
||||
'endpoints',
|
||||
// `admin`
|
||||
// is handled separately
|
||||
]
|
||||
|
||||
serverOnlyProperties.forEach((key) => {
|
||||
if (key in sanitized) {
|
||||
delete sanitized[key]
|
||||
}
|
||||
})
|
||||
|
||||
if ('admin' in sanitized) {
|
||||
sanitized.admin = { ...sanitized.admin }
|
||||
|
||||
if ('components' in sanitized.admin) {
|
||||
delete sanitized.admin.components
|
||||
}
|
||||
const serverOnlyProperties: Partial<keyof SanitizedGlobalConfig['admin']>[] = [
|
||||
'components',
|
||||
'hidden',
|
||||
'preview',
|
||||
]
|
||||
|
||||
if ('hidden' in sanitized.admin) {
|
||||
delete sanitized.admin.hidden
|
||||
}
|
||||
|
||||
if ('preview' in sanitized.admin) {
|
||||
delete sanitized.admin.preview
|
||||
}
|
||||
serverOnlyProperties.forEach((key) => {
|
||||
if (key in sanitized.admin) {
|
||||
delete sanitized.admin[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return sanitized
|
||||
@@ -121,17 +155,45 @@ export const createClientConfig = async (
|
||||
const config = await configPromise
|
||||
const clientConfig = { ...config }
|
||||
|
||||
delete clientConfig.endpoints
|
||||
delete clientConfig.db
|
||||
delete clientConfig.editor
|
||||
delete clientConfig.plugins
|
||||
delete clientConfig.sharp
|
||||
const serverOnlyConfigProperties: Partial<keyof SanitizedConfig>[] = [
|
||||
'endpoints',
|
||||
'db',
|
||||
'editor',
|
||||
'plugins',
|
||||
'sharp',
|
||||
// `admin`
|
||||
// `onInit`
|
||||
// `localization`
|
||||
// `collections`
|
||||
// `globals`
|
||||
// are all handled separately
|
||||
]
|
||||
|
||||
serverOnlyConfigProperties.forEach((key) => {
|
||||
if (key in clientConfig) {
|
||||
delete clientConfig[key]
|
||||
}
|
||||
})
|
||||
|
||||
if ('localization' in clientConfig && clientConfig.localization) {
|
||||
clientConfig.localization = { ...clientConfig.localization }
|
||||
|
||||
'localization' in clientConfig &&
|
||||
clientConfig.localization &&
|
||||
clientConfig.localization.locales.forEach((locale) => {
|
||||
delete locale.toString
|
||||
})
|
||||
}
|
||||
|
||||
if ('admin' in clientConfig) {
|
||||
clientConfig.admin = { ...clientConfig.admin }
|
||||
|
||||
const serverOnlyAdminProperties: Partial<keyof SanitizedConfig['admin']>[] = ['components']
|
||||
|
||||
serverOnlyAdminProperties.forEach((key) => {
|
||||
if (key in clientConfig.admin) {
|
||||
delete clientConfig.admin[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
clientConfig.onInit = undefined
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ const getTSConfigPaths = (): { outPath: string; srcPath: string } => {
|
||||
* @returns The absolute path to the Payload configuration file.
|
||||
* @throws An error if no configuration file is found.
|
||||
*/
|
||||
const findConfig = (): string => {
|
||||
export const findConfig = (): string => {
|
||||
// If the developer has specified a config path,
|
||||
// format it if relative and use it directly if absolute
|
||||
if (process.env.PAYLOAD_CONFIG_PATH) {
|
||||
@@ -101,5 +101,3 @@ const findConfig = (): string => {
|
||||
'Error: cannot find Payload config. Please create a configuration file located at the root of your current working directory called "payload.config.js" or "payload.config.ts".',
|
||||
)
|
||||
}
|
||||
|
||||
export default findConfig
|
||||
|
||||
@@ -1,26 +1,47 @@
|
||||
/* eslint-disable import/no-dynamic-require */
|
||||
import type pino from 'pino'
|
||||
|
||||
/* eslint-disable global-require */
|
||||
import { createRequire } from 'module'
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import path from 'path'
|
||||
|
||||
import type { SanitizedConfig } from './types.js'
|
||||
|
||||
import { clientFiles } from './clientFiles.js'
|
||||
import findConfig from './find.js'
|
||||
import { CLIENT_EXTENSIONS } from '../bin/register/clientExtensions.js'
|
||||
import Logger from '../utilities/logger.js'
|
||||
import { findConfig } from './find.js'
|
||||
import validate from './validate.js'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
|
||||
const loadConfig = async (logger?: pino.Logger): Promise<SanitizedConfig> => {
|
||||
const localLogger = logger ?? Logger()
|
||||
|
||||
const loadConfig = async (): Promise<SanitizedConfig> => {
|
||||
const configPath = findConfig()
|
||||
|
||||
clientFiles.forEach((ext) => {
|
||||
CLIENT_EXTENSIONS.forEach((ext) => {
|
||||
require.extensions[ext] = () => null
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const configPromise = require(configPath)
|
||||
const configPromise = await import(configPath)
|
||||
|
||||
let config = await configPromise
|
||||
|
||||
if (config.default) config = await config.default
|
||||
if ('default' in config) config = await config.default
|
||||
|
||||
return config
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
config = await validate(config, localLogger)
|
||||
}
|
||||
|
||||
return {
|
||||
...config,
|
||||
paths: {
|
||||
config: configPath,
|
||||
configDir: path.dirname(configPath),
|
||||
rawConfig: configPath,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default loadConfig
|
||||
|
||||
@@ -465,7 +465,7 @@ export type Config = {
|
||||
/**
|
||||
* Wrap the admin dashboard in custom context providers
|
||||
*/
|
||||
providers?: React.ComponentType<{ children: React.ReactNode }>[]
|
||||
providers?: React.ComponentType<{ children?: React.ReactNode }>[]
|
||||
/**
|
||||
* Replace or modify top-level admin routes, or add new ones:
|
||||
* + `Account` - `/admin/account`
|
||||
@@ -688,11 +688,17 @@ export type SanitizedConfig = Omit<
|
||||
}
|
||||
}
|
||||
|
||||
export type ClientConfig = Omit<SanitizedConfig, 'db' | 'endpoints'> & {
|
||||
collections: (Omit<SanitizedCollectionConfig, 'access' | 'endpoints' | 'fields' | 'hooks'> & {
|
||||
export type ClientConfig = Omit<SanitizedConfig, 'admin' | 'db' | 'endpoints'> & {
|
||||
admin: Omit<SanitizedConfig['admin'], 'components'>
|
||||
collections: (Omit<
|
||||
SanitizedCollectionConfig,
|
||||
'access' | 'admin' | 'endpoints' | 'fields' | 'hooks'
|
||||
> & {
|
||||
admin: Omit<SanitizedCollectionConfig['admin'], 'components'>
|
||||
fields: ClientConfigField[]
|
||||
})[]
|
||||
globals: (Omit<SanitizedGlobalConfig, 'access' | 'endpoints' | 'fields' | 'hooks'> & {
|
||||
globals: (Omit<SanitizedGlobalConfig, 'access' | 'admin' | 'endpoints' | 'fields' | 'hooks'> & {
|
||||
admin: Omit<SanitizedGlobalConfig['admin'], 'components'>
|
||||
fields: ClientConfigField[]
|
||||
})[]
|
||||
}
|
||||
|
||||
@@ -36,9 +36,15 @@ export const readMigrationFiles = async ({
|
||||
return Promise.all(
|
||||
files.map(async (filePath) => {
|
||||
// eval used to circumvent errors bundling
|
||||
const migration = eval(`require('${filePath.replaceAll('\\', '/')}')`)
|
||||
migration.name = path.basename(filePath).split('.')?.[0]
|
||||
return migration
|
||||
const migration = await eval(`import('${filePath.replaceAll('\\', '/')}')`)
|
||||
|
||||
const result: Migration = {
|
||||
name: path.basename(filePath).split('.')?.[0],
|
||||
down: migration.down,
|
||||
up: migration.up,
|
||||
}
|
||||
|
||||
return result
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -389,8 +389,8 @@ export type Migration = MigrationData & {
|
||||
}
|
||||
|
||||
export type MigrationData = {
|
||||
batch: number
|
||||
id: string
|
||||
batch?: number
|
||||
id?: string
|
||||
name: string
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { translations } from '@payloadcms/translations/api'
|
||||
import { extractTranslations } from 'payload/utilities'
|
||||
|
||||
import type { Config } from '../../config/types.js'
|
||||
import type { SanitizedGlobalConfig } from './types.js'
|
||||
@@ -10,6 +10,8 @@ import mergeBaseFields from '../../fields/mergeBaseFields.js'
|
||||
import { toWords } from '../../utilities/formatLabels.js'
|
||||
import baseVersionFields from '../../versions/baseFields.js'
|
||||
|
||||
const translations = extractTranslations(['general:createdAt', 'general:updatedAt'])
|
||||
|
||||
const sanitizeGlobals = (config: Config): SanitizedGlobalConfig[] => {
|
||||
const { collections, globals } = config
|
||||
|
||||
|
||||
@@ -273,13 +273,6 @@ export class BasePayload<TGeneratedTypes extends GeneratedTypes> {
|
||||
[slug: string]: any // TODO: Type this
|
||||
} = {}
|
||||
|
||||
delete<T extends keyof TGeneratedTypes['collections']>(
|
||||
options: DeleteOptions<T>,
|
||||
): Promise<BulkOperationResult<T> | TGeneratedTypes['collections'][T]> {
|
||||
const { deleteLocal } = localOperations
|
||||
return deleteLocal<T>(this, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description delete one or more documents
|
||||
* @param options
|
||||
@@ -293,12 +286,22 @@ export class BasePayload<TGeneratedTypes extends GeneratedTypes> {
|
||||
options: DeleteManyOptions<T>,
|
||||
): Promise<BulkOperationResult<T>>
|
||||
|
||||
delete<T extends keyof TGeneratedTypes['collections']>(
|
||||
options: DeleteOptions<T>,
|
||||
): Promise<BulkOperationResult<T> | TGeneratedTypes['collections'][T]> {
|
||||
const { deleteLocal } = localOperations
|
||||
return deleteLocal<T>(this, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Initializes Payload
|
||||
* @param options
|
||||
*/
|
||||
// @ts-expect-error // TODO: TypeScript hallucinating again. fix later
|
||||
async init(options: InitOptions): Promise<Payload> {
|
||||
if (!options?.config) {
|
||||
throw new Error('Error: the payload config is required to initialize payload.')
|
||||
}
|
||||
|
||||
this.logger = Logger('payload', options.loggerOptions, options.loggerDestination)
|
||||
|
||||
this.config = await options.config
|
||||
@@ -421,7 +424,11 @@ if (!cached) {
|
||||
cached = global._payload = { payload: null, promise: null }
|
||||
}
|
||||
|
||||
export const getPayload = async (options?: InitOptions): Promise<BasePayload<GeneratedTypes>> => {
|
||||
export const getPayload = async (options: InitOptions): Promise<BasePayload<GeneratedTypes>> => {
|
||||
if (!options?.config) {
|
||||
throw new Error('Error: the payload config is required for getPayload to work.')
|
||||
}
|
||||
|
||||
if (cached.payload) {
|
||||
return cached.payload
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ async function deleteOperation(args: PreferenceRequest): Promise<Document> {
|
||||
where,
|
||||
})
|
||||
|
||||
// @ts-expect-error // TODO: fix later
|
||||
if (result.docs.length === 1) {
|
||||
return result.docs[0]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export default function isImage(mimeType: string): boolean {
|
||||
return (
|
||||
['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/webp'].indexOf(mimeType) > -1
|
||||
['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/webp', 'image/avif'].indexOf(
|
||||
mimeType,
|
||||
) > -1
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"sourceMaps": true,
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-cloud-storage",
|
||||
"description": "The official cloud storage plugin for Payload CMS",
|
||||
"version": "3.0.0-alpha.34",
|
||||
"version": "3.0.0-alpha.45",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"sourceMaps": true,
|
||||
"exclude": ["/**/*.spec.ts", "/**/mocks"],
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-cloud",
|
||||
"description": "The official Payload Cloud plugin",
|
||||
"version": "3.0.0-alpha.34",
|
||||
"version": "3.0.0-alpha.45",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"sourceMaps": true,
|
||||
"exclude": ["/**/mocks"],
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"test": "echo \"No tests available.\""
|
||||
},
|
||||
"peerDependencies": {
|
||||
"payload": "^0.18.5 || ^1.0.0 || ^2.0.0",
|
||||
"payload": "workspace:*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { PluginConfig } from '../../../types'
|
||||
import type { PluginConfig } from '../../../types.js'
|
||||
|
||||
const createCharge = async (beforeChangeData: any, formConfig: PluginConfig): Promise<any> => {
|
||||
const { data, operation } = beforeChangeData
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { Email, FormattedEmail, PluginConfig } from '../../../types'
|
||||
import type { Email, FormattedEmail, PluginConfig } from '../../../types.js'
|
||||
|
||||
import { serializeLexical } from '../../../utilities/lexical/serializeLexical'
|
||||
import { replaceDoubleCurlys } from '../../../utilities/replaceDoubleCurlys'
|
||||
import { serializeSlate } from '../../../utilities/slate/serializeSlate'
|
||||
import { serializeLexical } from '../../../utilities/lexical/serializeLexical.js'
|
||||
import { replaceDoubleCurlys } from '../../../utilities/replaceDoubleCurlys.js'
|
||||
import { serializeSlate } from '../../../utilities/slate/serializeSlate.js'
|
||||
|
||||
const sendEmail = async (beforeChangeData: any, formConfig: PluginConfig): Promise<any> => {
|
||||
const { data, operation, req } = beforeChangeData
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import type { PluginConfig } from '../../types'
|
||||
import type { PluginConfig } from '../../types.js'
|
||||
|
||||
import createCharge from './hooks/createCharge'
|
||||
import sendEmail from './hooks/sendEmail'
|
||||
import createCharge from './hooks/createCharge.js'
|
||||
import sendEmail from './hooks/sendEmail.js'
|
||||
|
||||
// all settings can be overridden by the config
|
||||
export const generateSubmissionCollection = (formConfig: PluginConfig): CollectionConfig => {
|
||||
@@ -31,7 +31,7 @@ export const generateSubmissionCollection = (formConfig: PluginConfig): Collecti
|
||||
},
|
||||
relationTo: formSlug,
|
||||
required: true,
|
||||
validate: async (value, { payload, req }) => {
|
||||
validate: async (value, { req: { payload }, req }) => {
|
||||
/* Don't run in the client side */
|
||||
if (!payload) return true
|
||||
|
||||
|
||||
@@ -34,5 +34,7 @@ export const DynamicFieldSelector: React.FC<TextField> = (props) => {
|
||||
}
|
||||
}, [fields, getDataByPath])
|
||||
|
||||
// TODO: label from config is Record<string, string> | false | string
|
||||
// but the FormFieldBase type has only label?: string, changing FormFieldBase breaks other ui components
|
||||
return <Select {...props} options={options} />
|
||||
}
|
||||
|
||||
@@ -1,39 +1,41 @@
|
||||
import type { Block, Field } from 'payload/types'
|
||||
|
||||
import type { FieldConfig, PaymentFieldConfig } from '../../types'
|
||||
import type { FieldConfig, PaymentFieldConfig } from '../../types.js'
|
||||
|
||||
import { DynamicFieldSelector } from './DynamicFieldSelector'
|
||||
import { DynamicPriceSelector } from './DynamicPriceSelector'
|
||||
import { DynamicFieldSelector } from './DynamicFieldSelector.js'
|
||||
import { DynamicPriceSelector } from './DynamicPriceSelector.js'
|
||||
|
||||
const name: Field = {
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
label: 'Name (lowercase, no special characters)',
|
||||
required: true,
|
||||
type: 'text',
|
||||
}
|
||||
|
||||
const label: Field = {
|
||||
name: 'label',
|
||||
type: 'text',
|
||||
label: 'Label',
|
||||
localized: true,
|
||||
type: 'text',
|
||||
}
|
||||
|
||||
const required: Field = {
|
||||
name: 'required',
|
||||
label: 'Required',
|
||||
type: 'checkbox',
|
||||
label: 'Required',
|
||||
}
|
||||
|
||||
const width: Field = {
|
||||
name: 'width',
|
||||
label: 'Field Width (percentage)',
|
||||
type: 'number',
|
||||
label: 'Field Width (percentage)',
|
||||
}
|
||||
|
||||
const Select: Block = {
|
||||
slug: 'select',
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -48,9 +50,9 @@ const Select: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...width,
|
||||
@@ -60,42 +62,42 @@ const Select: Block = {
|
||||
},
|
||||
{
|
||||
name: 'defaultValue',
|
||||
type: 'text',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Default Value',
|
||||
localized: true,
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
name: 'options',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'label',
|
||||
type: 'text',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Label',
|
||||
localized: true,
|
||||
required: true,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'value',
|
||||
type: 'text',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Value',
|
||||
required: true,
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
],
|
||||
label: 'Select Attribute Options',
|
||||
@@ -103,7 +105,6 @@ const Select: Block = {
|
||||
plural: 'Options',
|
||||
singular: 'Option',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
required,
|
||||
],
|
||||
@@ -111,12 +112,13 @@ const Select: Block = {
|
||||
plural: 'Select Fields',
|
||||
singular: 'Select',
|
||||
},
|
||||
slug: 'select',
|
||||
}
|
||||
|
||||
const Text: Block = {
|
||||
slug: 'text',
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -131,9 +133,9 @@ const Text: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...width,
|
||||
@@ -143,15 +145,14 @@ const Text: Block = {
|
||||
},
|
||||
{
|
||||
name: 'defaultValue',
|
||||
type: 'text',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Default Value',
|
||||
localized: true,
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
required,
|
||||
],
|
||||
@@ -159,12 +160,13 @@ const Text: Block = {
|
||||
plural: 'Text Fields',
|
||||
singular: 'Text',
|
||||
},
|
||||
slug: 'text',
|
||||
}
|
||||
|
||||
const TextArea: Block = {
|
||||
slug: 'textarea',
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -179,9 +181,9 @@ const TextArea: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...width,
|
||||
@@ -191,15 +193,14 @@ const TextArea: Block = {
|
||||
},
|
||||
{
|
||||
name: 'defaultValue',
|
||||
type: 'text',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Default Value',
|
||||
localized: true,
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
required,
|
||||
],
|
||||
@@ -207,12 +208,13 @@ const TextArea: Block = {
|
||||
plural: 'Text Area Fields',
|
||||
singular: 'Text Area',
|
||||
},
|
||||
slug: 'textarea',
|
||||
}
|
||||
|
||||
const Number: Block = {
|
||||
slug: 'number',
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -227,9 +229,9 @@ const Number: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...width,
|
||||
@@ -239,14 +241,13 @@ const Number: Block = {
|
||||
},
|
||||
{
|
||||
name: 'defaultValue',
|
||||
type: 'number',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Default Value',
|
||||
type: 'number',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
required,
|
||||
],
|
||||
@@ -254,12 +255,13 @@ const Number: Block = {
|
||||
plural: 'Number Fields',
|
||||
singular: 'Number',
|
||||
},
|
||||
slug: 'number',
|
||||
}
|
||||
|
||||
const Email: Block = {
|
||||
slug: 'email',
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -274,7 +276,6 @@ const Email: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
width,
|
||||
required,
|
||||
@@ -283,12 +284,13 @@ const Email: Block = {
|
||||
plural: 'Email Fields',
|
||||
singular: 'Email',
|
||||
},
|
||||
slug: 'email',
|
||||
}
|
||||
|
||||
const State: Block = {
|
||||
slug: 'state',
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -303,7 +305,6 @@ const State: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
width,
|
||||
required,
|
||||
@@ -312,12 +313,13 @@ const State: Block = {
|
||||
plural: 'State Fields',
|
||||
singular: 'State',
|
||||
},
|
||||
slug: 'state',
|
||||
}
|
||||
|
||||
const Country: Block = {
|
||||
slug: 'country',
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -332,7 +334,6 @@ const Country: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
width,
|
||||
required,
|
||||
@@ -341,12 +342,13 @@ const Country: Block = {
|
||||
plural: 'Country Fields',
|
||||
singular: 'Country',
|
||||
},
|
||||
slug: 'country',
|
||||
}
|
||||
|
||||
const Checkbox: Block = {
|
||||
slug: 'checkbox',
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -361,9 +363,9 @@ const Checkbox: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...width,
|
||||
@@ -378,19 +380,17 @@ const Checkbox: Block = {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
name: 'defaultValue',
|
||||
label: 'Default Value',
|
||||
type: 'checkbox',
|
||||
label: 'Default Value',
|
||||
},
|
||||
],
|
||||
labels: {
|
||||
plural: 'Checkbox Fields',
|
||||
singular: 'Checkbox',
|
||||
},
|
||||
slug: 'checkbox',
|
||||
}
|
||||
|
||||
const Payment = (fieldConfig: PaymentFieldConfig): Block => {
|
||||
@@ -398,16 +398,18 @@ const Payment = (fieldConfig: PaymentFieldConfig): Block => {
|
||||
if (fieldConfig?.paymentProcessor) {
|
||||
paymentProcessorField = {
|
||||
name: 'paymentProcessor',
|
||||
type: 'select',
|
||||
label: 'Payment Processor',
|
||||
options: [],
|
||||
type: 'select',
|
||||
...fieldConfig.paymentProcessor,
|
||||
}
|
||||
}
|
||||
|
||||
const fields = {
|
||||
slug: 'payment',
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...name,
|
||||
@@ -422,9 +424,9 @@ const Payment = (fieldConfig: PaymentFieldConfig): Block => {
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
...width,
|
||||
@@ -434,30 +436,31 @@ const Payment = (fieldConfig: PaymentFieldConfig): Block => {
|
||||
},
|
||||
{
|
||||
name: 'basePrice',
|
||||
type: 'number',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Base Price',
|
||||
type: 'number',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
paymentProcessorField,
|
||||
{
|
||||
name: 'priceConditions',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'fieldToUse',
|
||||
type: 'text',
|
||||
admin: {
|
||||
components: {
|
||||
Field: DynamicFieldSelector,
|
||||
},
|
||||
},
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'condition',
|
||||
type: 'select',
|
||||
defaultValue: 'hasValue',
|
||||
label: 'Condition',
|
||||
options: [
|
||||
@@ -474,19 +477,19 @@ const Payment = (fieldConfig: PaymentFieldConfig): Block => {
|
||||
value: 'notEquals',
|
||||
},
|
||||
],
|
||||
type: 'select',
|
||||
},
|
||||
{
|
||||
name: 'valueForCondition',
|
||||
type: 'text',
|
||||
admin: {
|
||||
condition: (_: any, { condition }: any) =>
|
||||
condition === 'equals' || condition === 'notEquals',
|
||||
},
|
||||
label: 'Value',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'operator',
|
||||
type: 'select',
|
||||
defaultValue: 'add',
|
||||
options: [
|
||||
{
|
||||
@@ -506,10 +509,10 @@ const Payment = (fieldConfig: PaymentFieldConfig): Block => {
|
||||
value: 'divide',
|
||||
},
|
||||
],
|
||||
type: 'select',
|
||||
},
|
||||
{
|
||||
name: 'valueType',
|
||||
type: 'radio',
|
||||
admin: {
|
||||
width: '100%',
|
||||
},
|
||||
@@ -525,17 +528,16 @@ const Payment = (fieldConfig: PaymentFieldConfig): Block => {
|
||||
value: 'valueOfField',
|
||||
},
|
||||
],
|
||||
type: 'radio',
|
||||
},
|
||||
{
|
||||
name: 'valueForOperator',
|
||||
type: 'text',
|
||||
admin: {
|
||||
components: {
|
||||
Field: DynamicPriceSelector,
|
||||
},
|
||||
},
|
||||
label: 'Value',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
label: 'Price Conditions',
|
||||
@@ -543,7 +545,6 @@ const Payment = (fieldConfig: PaymentFieldConfig): Block => {
|
||||
plural: 'Price Conditions',
|
||||
singular: 'Price Condition',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
required,
|
||||
].filter(Boolean) as Field[],
|
||||
@@ -551,25 +552,24 @@ const Payment = (fieldConfig: PaymentFieldConfig): Block => {
|
||||
plural: 'Payment Fields',
|
||||
singular: 'Payment',
|
||||
},
|
||||
slug: 'payment',
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
const Message: Block = {
|
||||
slug: 'message',
|
||||
fields: [
|
||||
{
|
||||
name: 'message',
|
||||
localized: true,
|
||||
type: 'richText',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
labels: {
|
||||
plural: 'Message Blocks',
|
||||
singular: 'Message',
|
||||
},
|
||||
slug: 'message',
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
|
||||
@@ -2,14 +2,15 @@ import type { Block, CollectionConfig, Field } from 'payload/types'
|
||||
|
||||
import merge from 'deepmerge'
|
||||
|
||||
import type { FieldConfig, PluginConfig } from '../../types'
|
||||
import type { FieldConfig, PluginConfig } from '../../types.js'
|
||||
|
||||
import { fields } from './fields'
|
||||
import { fields } from './fields.js'
|
||||
|
||||
// all settings can be overridden by the config
|
||||
export const generateFormCollection = (formConfig: PluginConfig): CollectionConfig => {
|
||||
const redirect: Field = {
|
||||
name: 'redirect',
|
||||
type: 'group',
|
||||
admin: {
|
||||
condition: (_, siblingData) => siblingData?.confirmationType === 'redirect',
|
||||
hideGutter: true,
|
||||
@@ -17,17 +18,17 @@ export const generateFormCollection = (formConfig: PluginConfig): CollectionConf
|
||||
fields: [
|
||||
{
|
||||
name: 'url',
|
||||
type: 'text',
|
||||
label: 'URL to redirect to',
|
||||
required: true,
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
type: 'group',
|
||||
}
|
||||
|
||||
if (formConfig.redirectRelationships) {
|
||||
redirect.fields.unshift({
|
||||
name: 'reference',
|
||||
type: 'relationship',
|
||||
admin: {
|
||||
condition: (_, siblingData) => siblingData?.type === 'reference',
|
||||
},
|
||||
@@ -35,11 +36,11 @@ export const generateFormCollection = (formConfig: PluginConfig): CollectionConf
|
||||
maxDepth: 2,
|
||||
relationTo: formConfig.redirectRelationships,
|
||||
required: true,
|
||||
type: 'relationship',
|
||||
})
|
||||
|
||||
redirect.fields.unshift({
|
||||
name: 'type',
|
||||
type: 'radio',
|
||||
admin: {
|
||||
layout: 'horizontal',
|
||||
},
|
||||
@@ -54,7 +55,6 @@ export const generateFormCollection = (formConfig: PluginConfig): CollectionConf
|
||||
value: 'custom',
|
||||
},
|
||||
],
|
||||
type: 'radio',
|
||||
})
|
||||
|
||||
if (redirect.fields[2].type !== 'row') redirect.fields[2].label = 'Custom URL'
|
||||
@@ -66,6 +66,7 @@ export const generateFormCollection = (formConfig: PluginConfig): CollectionConf
|
||||
|
||||
const config: CollectionConfig = {
|
||||
...(formConfig?.formOverrides || {}),
|
||||
slug: formConfig?.formOverrides?.slug || 'forms',
|
||||
access: {
|
||||
read: () => true,
|
||||
...(formConfig?.formOverrides?.access || {}),
|
||||
@@ -78,11 +79,12 @@ export const generateFormCollection = (formConfig: PluginConfig): CollectionConf
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
required: true,
|
||||
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
|
||||
@@ -109,15 +111,15 @@ export const generateFormCollection = (formConfig: PluginConfig): CollectionConf
|
||||
return null
|
||||
})
|
||||
.filter(Boolean) as Block[],
|
||||
type: 'blocks',
|
||||
},
|
||||
{
|
||||
name: 'submitButtonLabel',
|
||||
localized: true,
|
||||
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.',
|
||||
@@ -134,101 +136,99 @@ export const generateFormCollection = (formConfig: PluginConfig): CollectionConf
|
||||
value: 'redirect',
|
||||
},
|
||||
],
|
||||
type: 'radio',
|
||||
},
|
||||
{
|
||||
name: 'confirmationMessage',
|
||||
type: 'richText',
|
||||
admin: {
|
||||
condition: (_, siblingData) => siblingData?.confirmationType === 'message',
|
||||
},
|
||||
localized: true,
|
||||
required: true,
|
||||
type: 'richText',
|
||||
},
|
||||
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',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'cc',
|
||||
type: 'text',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'CC',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'bcc',
|
||||
type: 'text',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
label: 'BCC',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'replyTo',
|
||||
type: 'text',
|
||||
admin: {
|
||||
placeholder: '"Reply To" <reply-to@email.com>',
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Reply To',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'emailFrom',
|
||||
type: 'text',
|
||||
admin: {
|
||||
placeholder: '"Email From" <email-from@email.com>',
|
||||
width: '50%',
|
||||
},
|
||||
label: 'Email From',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
type: 'row',
|
||||
},
|
||||
{
|
||||
name: 'subject',
|
||||
type: 'text',
|
||||
defaultValue: "You've received a new message.",
|
||||
label: 'Subject',
|
||||
localized: true,
|
||||
required: true,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'message',
|
||||
type: 'richText',
|
||||
admin: {
|
||||
description: 'Enter the message that should be sent in this email.',
|
||||
},
|
||||
label: 'Message',
|
||||
localized: true,
|
||||
type: 'richText',
|
||||
},
|
||||
],
|
||||
type: 'array',
|
||||
},
|
||||
...(formConfig?.formOverrides?.fields || []),
|
||||
],
|
||||
slug: formConfig?.formOverrides?.slug || 'forms',
|
||||
}
|
||||
|
||||
return config
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"sourceMaps": true,
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-nested-docs",
|
||||
"version": "1.0.12",
|
||||
"version": "3.0.0-alpha.45",
|
||||
"description": "The official Nested Docs plugin for Payload",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
@@ -22,7 +22,7 @@
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"payload": "^0.18.5 || ^1.0.0 || ^2.0.0"
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { CollectionAfterChangeHook, CollectionConfig, PayloadRequest } from 'payload/types'
|
||||
|
||||
import type { PluginConfig } from '../types'
|
||||
import type { PluginConfig } from '../types.js'
|
||||
|
||||
import populateBreadcrumbs from '../utilities/populateBreadcrumbs'
|
||||
import populateBreadcrumbs from '../utilities/populateBreadcrumbs.js'
|
||||
|
||||
type ResaveArgs = {
|
||||
collection: CollectionConfig
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CollectionAfterChangeHook, CollectionConfig } from 'payload/types'
|
||||
|
||||
import type { PluginConfig } from '../types'
|
||||
import type { PluginConfig } from '../types.js'
|
||||
|
||||
// This hook automatically re-saves a document after it is created
|
||||
// so that we can build its breadcrumbs with the newly created document's ID.
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import type { Plugin } from 'payload/config'
|
||||
import type { SingleRelationshipField } from 'payload/types'
|
||||
|
||||
import type { PluginConfig } from './types'
|
||||
import type { PluginConfig } from './types.js'
|
||||
|
||||
import createBreadcrumbsField from './fields/breadcrumbs'
|
||||
import createParentField from './fields/parent'
|
||||
import parentFilterOptions from './fields/parentFilterOptions'
|
||||
import resaveChildren from './hooks/resaveChildren'
|
||||
import resaveSelfAfterCreate from './hooks/resaveSelfAfterCreate'
|
||||
import populateBreadcrumbs from './utilities/populateBreadcrumbs'
|
||||
import createBreadcrumbsField from './fields/breadcrumbs.js'
|
||||
import createParentField from './fields/parent.js'
|
||||
import parentFilterOptions from './fields/parentFilterOptions.js'
|
||||
import resaveChildren from './hooks/resaveChildren.js'
|
||||
import resaveSelfAfterCreate from './hooks/resaveSelfAfterCreate.js'
|
||||
import populateBreadcrumbs from './utilities/populateBreadcrumbs.js'
|
||||
|
||||
const nestedDocs =
|
||||
(pluginConfig: PluginConfig): Plugin =>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import type { Breadcrumb, PluginConfig } from '../types'
|
||||
import type { Breadcrumb, PluginConfig } from '../types.js'
|
||||
|
||||
const formatBreadcrumb = (
|
||||
pluginConfig: PluginConfig,
|
||||
@@ -24,9 +24,9 @@ const formatBreadcrumb = (
|
||||
}
|
||||
|
||||
return {
|
||||
doc: lastDoc.id as string,
|
||||
label,
|
||||
url,
|
||||
doc: lastDoc.id as string,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import type { PluginConfig } from '../types'
|
||||
import type { PluginConfig } from '../types.js'
|
||||
|
||||
const getParents = async (
|
||||
req: any,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import type { PluginConfig } from '../types'
|
||||
import type { PluginConfig } from '../types.js'
|
||||
|
||||
import formatBreadcrumb from './formatBreadcrumb'
|
||||
import getParents from './getParents'
|
||||
import formatBreadcrumb from './formatBreadcrumb.js'
|
||||
import getParents from './getParents.js'
|
||||
|
||||
const populateBreadcrumbs = async (
|
||||
req: any,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"sourceMaps": true,
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-redirects",
|
||||
"version": "1.0.1",
|
||||
"version": "3.0.0-alpha.45",
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"repository": "git@github.com:payloadcms/plugin-redirects.git",
|
||||
"description": "Redirects plugin for Payload",
|
||||
@@ -27,7 +27,7 @@
|
||||
"author": "dev@payloadcms.com",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"payload": "^0.18.5 || ^1.0.0 || ^2.0.0"
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { Config } from 'payload/config'
|
||||
|
||||
import type { PluginConfig } from './types'
|
||||
import type { PluginConfig } from './types.js'
|
||||
|
||||
import deepMerge from './deepMerge'
|
||||
import deepMerge from './deepMerge.js'
|
||||
|
||||
const redirects =
|
||||
(pluginConfig: PluginConfig) =>
|
||||
@@ -12,6 +12,7 @@ const redirects =
|
||||
...(incomingConfig?.collections || []),
|
||||
deepMerge(
|
||||
{
|
||||
slug: 'redirects',
|
||||
access: {
|
||||
read: (): boolean => true,
|
||||
},
|
||||
@@ -21,16 +22,18 @@ const redirects =
|
||||
fields: [
|
||||
{
|
||||
name: 'from',
|
||||
type: 'text',
|
||||
index: true,
|
||||
label: 'From URL',
|
||||
required: true,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'to',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'type',
|
||||
type: 'radio',
|
||||
admin: {
|
||||
layout: 'horizontal',
|
||||
},
|
||||
@@ -46,33 +49,30 @@ const redirects =
|
||||
value: 'custom',
|
||||
},
|
||||
],
|
||||
type: 'radio',
|
||||
},
|
||||
{
|
||||
name: 'reference',
|
||||
type: 'relationship',
|
||||
admin: {
|
||||
condition: (_, siblingData) => siblingData?.type === 'reference',
|
||||
},
|
||||
label: 'Document to redirect to',
|
||||
relationTo: pluginConfig?.collections || [],
|
||||
required: true,
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
name: 'url',
|
||||
type: 'text',
|
||||
admin: {
|
||||
condition: (_, siblingData) => siblingData?.type === 'custom',
|
||||
},
|
||||
label: 'Custom URL',
|
||||
required: true,
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
label: false,
|
||||
type: 'group',
|
||||
},
|
||||
],
|
||||
slug: 'redirects',
|
||||
},
|
||||
pluginConfig?.overrides || {},
|
||||
),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"sourceMaps": true,
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-search",
|
||||
"version": "1.1.0",
|
||||
"version": "3.0.0-alpha.45",
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"repository": "git@github.com:payloadcms/plugin-search.git",
|
||||
"description": "Search plugin for Payload",
|
||||
@@ -27,12 +27,12 @@
|
||||
"author": "dev@trbl.design",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"payload": "^0.18.5 || ^1.0.0 || ^2.0.0",
|
||||
"payload": "workspace:*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/ui": "workspace:*",
|
||||
"ts-deepmerge": "^2.0.1"
|
||||
"deepmerge": "4.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { DocToSync, SearchConfig, SyncWithSearch } from '../../types'
|
||||
import type { DocToSync, SearchConfig, SyncWithSearch } from '../../types.js'
|
||||
|
||||
const syncWithSearch: SyncWithSearch = async (args) => {
|
||||
const {
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import deepMerge from 'ts-deepmerge'
|
||||
import deepMerge from 'deepmerge'
|
||||
|
||||
import type { SearchConfig } from '../types'
|
||||
import type { SearchConfig } from '../types.js'
|
||||
|
||||
import { LinkToDoc } from './ui'
|
||||
import { LinkToDoc } from './ui/index.js'
|
||||
|
||||
// all settings can be overridden by the config
|
||||
export const generateSearchCollection = (searchConfig: SearchConfig): CollectionConfig =>
|
||||
deepMerge(
|
||||
{
|
||||
slug: 'search',
|
||||
access: {
|
||||
create: (): boolean => false,
|
||||
read: (): boolean => true,
|
||||
@@ -24,20 +25,21 @@ export const generateSearchCollection = (searchConfig: SearchConfig): Collection
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'priority',
|
||||
type: 'number',
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
},
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
name: 'doc',
|
||||
type: 'relationship',
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
readOnly: true,
|
||||
@@ -46,24 +48,22 @@ export const generateSearchCollection = (searchConfig: SearchConfig): Collection
|
||||
maxDepth: 0,
|
||||
relationTo: searchConfig?.collections || [],
|
||||
required: true,
|
||||
type: 'relationship',
|
||||
},
|
||||
{
|
||||
name: 'docUrl',
|
||||
type: 'ui',
|
||||
admin: {
|
||||
components: {
|
||||
Field: LinkToDoc,
|
||||
},
|
||||
position: 'sidebar',
|
||||
},
|
||||
type: 'ui',
|
||||
},
|
||||
],
|
||||
labels: {
|
||||
plural: 'Search Results',
|
||||
singular: 'Search Result',
|
||||
},
|
||||
slug: 'search',
|
||||
},
|
||||
searchConfig?.searchOverrides || {},
|
||||
)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { FormState } from '@payloadcms/ui'
|
||||
import type { UIField } from 'payload/types'
|
||||
import type { FormState, UIField } from 'payload/types'
|
||||
|
||||
import { useConfig, useWatchForm } from '@payloadcms/ui'
|
||||
import React from 'react'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Config } from 'payload/config'
|
||||
|
||||
import type { SearchConfig } from './types'
|
||||
import type { SearchConfig } from './types.js'
|
||||
|
||||
import { generateSearchCollection } from './Search'
|
||||
import deleteFromSearch from './Search/hooks/deleteFromSearch'
|
||||
import syncWithSearch from './Search/hooks/syncWithSearch'
|
||||
import deleteFromSearch from './Search/hooks/deleteFromSearch.js'
|
||||
import syncWithSearch from './Search/hooks/syncWithSearch.js'
|
||||
import { generateSearchCollection } from './Search/index.js'
|
||||
|
||||
const Search =
|
||||
(incomingSearchConfig: SearchConfig) =>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"sourceMaps": true,
|
||||
"exclude": ["/**/*.spec.ts"],
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user