Compare commits
37 Commits
alpha-post
...
alpha-erro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97dc651e97 | ||
|
|
58aa2e8709 | ||
|
|
ebd521d0b1 | ||
|
|
8409975dd3 | ||
|
|
ab3547b707 | ||
|
|
9f5fcd1746 | ||
|
|
78c0f16871 | ||
|
|
b53573238b | ||
|
|
32938ffd21 | ||
|
|
8e2fa91f49 | ||
|
|
394db778dc | ||
|
|
035b071a38 | ||
|
|
e26e1f434f | ||
|
|
0607162f31 | ||
|
|
6c01f1e300 | ||
|
|
19594c7339 | ||
|
|
fa101ecd02 | ||
|
|
4a819ea16f | ||
|
|
e696579b33 | ||
|
|
edc785e639 | ||
|
|
faee408001 | ||
|
|
01fc1e5914 | ||
|
|
ddeb3bf842 | ||
|
|
f409ae56e3 | ||
|
|
361924a4d8 | ||
|
|
b4e25337dc | ||
|
|
fca6e7bab9 | ||
|
|
7507951900 | ||
|
|
857331cab1 | ||
|
|
afe9ed60f9 | ||
|
|
3840120e19 | ||
|
|
cc611cb553 | ||
|
|
a4f56c886d | ||
|
|
612115a3c9 | ||
|
|
63da5eaffc | ||
|
|
fb4ee0493e | ||
|
|
4d39381235 |
@@ -10,4 +10,3 @@
|
||||
**/temp
|
||||
playwright.config.ts
|
||||
jest.config.js
|
||||
test/live-preview/next-app
|
||||
|
||||
@@ -3,13 +3,6 @@ module.exports = {
|
||||
extends: ['@payloadcms'],
|
||||
ignorePatterns: ['README.md', 'packages/**/*.spec.ts'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['packages/**'],
|
||||
plugins: ['payload'],
|
||||
rules: {
|
||||
'payload/no-jsx-import-statements': 'warn',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['scripts/**'],
|
||||
rules: {
|
||||
|
||||
13
.github/CODEOWNERS
vendored
13
.github/CODEOWNERS
vendored
@@ -1,24 +1,32 @@
|
||||
# Order matters. The last matching pattern takes precedence.
|
||||
|
||||
### Catch-all ###
|
||||
* @denolfe @jmikrut @DanRibbens
|
||||
.* @denolfe @jmikrut @DanRibbens
|
||||
|
||||
### Core ###
|
||||
/packages/payload/ @denolfe @jmikrut @DanRibbens
|
||||
/packages/payload/src/uploads/ @denolfe
|
||||
/packages/payload/src/admin/ @jmikrut @jacobsfletch @JarrodMFlesch
|
||||
|
||||
### Adapters ###
|
||||
/packages/bundler-*/ @denolfe @jmikrut @DanRibbens @JarrodMFlesch
|
||||
/packages/db-*/ @denolfe @jmikrut @DanRibbens
|
||||
/packages/richtext-*/ @denolfe @jmikrut @DanRibbens @AlessioGr
|
||||
|
||||
### Plugins ###
|
||||
/packages/plugin-*/ @denolfe @jmikrut @DanRibbens
|
||||
/packages/plugin-*/ @denolfe @jmikrut @DanRibbens @jacobsfletch @JarrodMFlesch @AlessioGr
|
||||
/packages/plugin-cloud*/ @denolfe
|
||||
/packages/plugin-form-builder/ @jacobsfletch
|
||||
/packages/plugin-live-preview*/ @jacobsfletch
|
||||
/packages/plugin-nested-docs/ @jacobsfletch
|
||||
/packages/plugin-password-protection/ @jmikrut
|
||||
/packages/plugin-redirects/ @jacobsfletch
|
||||
/packages/plugin-search/ @jacobsfletch
|
||||
/packages/plugin-sentry/ @JessChowdhury
|
||||
/packages/plugin-seo/ @jacobsfletch
|
||||
/packages/plugin-stripe/ @jacobsfletch
|
||||
/packages/plugin-zapier/ @JarrodMFlesch
|
||||
|
||||
### Examples ###
|
||||
/examples/ @jacobsfletch
|
||||
@@ -27,7 +35,8 @@
|
||||
/examples/whitelabel/ @JessChowdhury
|
||||
|
||||
### Templates ###
|
||||
/templates/ @jacobsfletch @denolfe
|
||||
/templates/ @jacobsfletch
|
||||
/templates/blank/ @denolfe
|
||||
|
||||
### Misc ###
|
||||
/packages/create-payload-app/ @denolfe
|
||||
|
||||
87
.github/workflows/main.yml
vendored
87
.github/workflows/main.yml
vendored
@@ -2,7 +2,7 @@ name: build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
types: [ opened, reopened, synchronize ]
|
||||
push:
|
||||
branches: ['main', 'alpha']
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 25
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: dorny/paths-filter@v2
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
@@ -46,12 +46,12 @@ jobs:
|
||||
fetch-depth: 25
|
||||
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v3
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
- run: pnpm run build:core
|
||||
|
||||
- name: Cache build
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -90,12 +90,12 @@ jobs:
|
||||
fetch-depth: 25
|
||||
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
@@ -105,7 +105,7 @@ jobs:
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v4
|
||||
- uses: actions/cache@v3
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
@@ -123,12 +123,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
database:
|
||||
- mongodb
|
||||
- postgres
|
||||
- postgres-custom-schema
|
||||
- postgres-uuid
|
||||
- supabase
|
||||
database: [mongoose, postgres, postgres-custom-schema, postgres-uuid, supabase]
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
@@ -140,18 +135,18 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -162,7 +157,7 @@ jobs:
|
||||
- name: Start PostgreSQL
|
||||
uses: CasperWA/postgresql-action@v1.2
|
||||
with:
|
||||
postgresql version: '14' # See https://hub.docker.com/_/postgres for available versions
|
||||
postgresql version: '14' # See https://hub.docker.com/_/postgres for available versions
|
||||
postgresql db: ${{ env.POSTGRES_DB }}
|
||||
postgresql user: ${{ env.POSTGRES_USER }}
|
||||
postgresql password: ${{ env.POSTGRES_PASSWORD }}
|
||||
@@ -202,7 +197,7 @@ jobs:
|
||||
if: matrix.database == 'supabase'
|
||||
|
||||
- name: Integration Tests
|
||||
run: pnpm test:int --testPathIgnorePatterns=test/fields # Ignore fields tests until reworked
|
||||
run: pnpm test:int
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8096
|
||||
PAYLOAD_DATABASE: ${{ matrix.database }}
|
||||
@@ -214,38 +209,22 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# find test -type f -name 'e2e.spec.ts' | sort | xargs dirname | xargs -I {} basename {}
|
||||
suite:
|
||||
- _community
|
||||
- access-control
|
||||
# - admin
|
||||
- auth
|
||||
# - field-error-states
|
||||
# - fields-relationship
|
||||
# - fields
|
||||
- fields/lexical
|
||||
- live-preview
|
||||
# - localization
|
||||
# - plugin-nested-docs
|
||||
# - plugin-seo
|
||||
# - refresh-permissions
|
||||
# - uploads
|
||||
# - versions
|
||||
part: [ 1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8, 8/8 ]
|
||||
|
||||
steps:
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -254,39 +233,38 @@ jobs:
|
||||
run: pnpm exec playwright install
|
||||
|
||||
- name: E2E Tests
|
||||
uses: nick-fields/retry@v3
|
||||
uses: nick-fields/retry@v2
|
||||
with:
|
||||
retry_on: error
|
||||
max_attempts: 2
|
||||
timeout_minutes: 15
|
||||
command: pnpm test:e2e ${{ matrix.suite }}
|
||||
command: pnpm test:e2e --part ${{ matrix.part }} --bail
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: test-results
|
||||
path: test/test-results/
|
||||
path: test-results/
|
||||
retention-days: 1
|
||||
|
||||
tests-type-generation:
|
||||
if: false # This should be replaced with gen on a real Payload project
|
||||
runs-on: ubuntu-latest
|
||||
needs: core-build
|
||||
|
||||
steps:
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -315,18 +293,18 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -336,15 +314,16 @@ jobs:
|
||||
|
||||
- name: Test ${{ matrix.pkg }}
|
||||
run: pnpm --filter ${{ matrix.pkg }} run test
|
||||
if: matrix.pkg != 'create-payload-app' # degit doesn't work within GitHub Actions
|
||||
|
||||
templates:
|
||||
needs: changes
|
||||
if: false # Disable until templates are updated for 3.0
|
||||
if: ${{ needs.changes.outputs.templates == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
template: [blank, website, ecommerce]
|
||||
template: [ blank, website, ecommerce ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -352,7 +331,7 @@ jobs:
|
||||
fetch-depth: 25
|
||||
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
|
||||
16
.release-it.pre.js
Normal file
16
.release-it.pre.js
Normal file
@@ -0,0 +1,16 @@
|
||||
module.exports = {
|
||||
verbose: true,
|
||||
git: {
|
||||
requireCleanWorkingDir: false,
|
||||
commit: false,
|
||||
push: false,
|
||||
tag: false,
|
||||
},
|
||||
npm: {
|
||||
skipChecks: true,
|
||||
tag: 'beta',
|
||||
},
|
||||
hooks: {
|
||||
'before:init': ['pnpm install', 'pnpm clean', 'pnpm build'],
|
||||
},
|
||||
}
|
||||
9
.vscode/launch.json
vendored
9
.vscode/launch.json
vendored
@@ -10,19 +10,12 @@
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js fields",
|
||||
"command": "pnpm run dev _community -- --no-turbo",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Community",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev live-preview -- --no-turbo",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Live Preview",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev plugin-cloud-storage",
|
||||
"cwd": "${workspaceFolder}",
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import PageTemplate from './(pages)/[slug]/page.js'
|
||||
|
||||
export default PageTemplate
|
||||
@@ -167,6 +167,53 @@ those three fields plus the ID field.
|
||||
so your admin queries can remain performant.
|
||||
</Banner>
|
||||
|
||||
### Admin Hooks
|
||||
|
||||
In addition to collection hooks themselves, Payload provides for admin UI-specific hooks that you can leverage.
|
||||
|
||||
**`beforeDuplicate`**
|
||||
|
||||
The `beforeDuplicate` hook is an async function that accepts an object containing the data to duplicate, as well as the
|
||||
locale of the doc to duplicate. Within this hook, you can modify the data to be duplicated, which is useful in cases
|
||||
where you have unique fields that need to be incremented or similar, as well as if you want to automatically modify a
|
||||
document's `title`.
|
||||
|
||||
Example:
|
||||
|
||||
```ts
|
||||
import { BeforeDuplicate, CollectionConfig } from 'payload/types'
|
||||
// Your auto-generated Page type
|
||||
import { Page } from '../payload-types.ts'
|
||||
|
||||
const beforeDuplicate: BeforeDuplicate<Page> = ({ data }) => {
|
||||
return {
|
||||
...data,
|
||||
title: `${data.title} Copy`,
|
||||
uniqueField: data.uniqueField ? `${data.uniqueField}-copy` : '',
|
||||
}
|
||||
}
|
||||
|
||||
export const Page: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
admin: {
|
||||
hooks: {
|
||||
beforeDuplicate,
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'uniqueField',
|
||||
type: 'text',
|
||||
unique: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
You can import collection types as follows:
|
||||
|
||||
@@ -21,7 +21,6 @@ functionalities to be easily reusable across your projects.
|
||||
|
||||
- [beforeValidate](#beforevalidate)
|
||||
- [beforeChange](#beforechange)
|
||||
- beforeDuplicate(#beforeduplicate)
|
||||
- [afterChange](#afterchange)
|
||||
- [afterRead](#afterread)
|
||||
|
||||
@@ -39,7 +38,6 @@ const ExampleField: Field = {
|
||||
hooks: {
|
||||
beforeValidate: [(args) => {...}],
|
||||
beforeChange: [(args) => {...}],
|
||||
beforeDuplicate: [(args) => {...}],
|
||||
afterChange: [(args) => {...}],
|
||||
afterRead: [(args) => {...}],
|
||||
}
|
||||
@@ -219,27 +217,6 @@ Here, the `afterRead` hook for the `dateField` is used to format the date into a
|
||||
using `toLocaleDateString()`. This hook modifies the way the date is presented to the user, making it more
|
||||
user-friendly.
|
||||
|
||||
### beforeDuplicate
|
||||
|
||||
The `beforeDuplicate` field hook is only called when duplicating a document. It may be used when documents having the
|
||||
exact same properties may cause issue. This gives you a way to avoid duplicate names on `unique`, `required` fields or
|
||||
to unset values by returning `null`. This is called immediately after `defaultValue` and before validation occurs.
|
||||
|
||||
```ts
|
||||
import { Field } from 'payload/types'
|
||||
|
||||
const numberField: Field = {
|
||||
name: 'number',
|
||||
type: 'number',
|
||||
hooks: {
|
||||
// increment existing value by 1
|
||||
beforeDuplicate: [({ value }) => {
|
||||
return (value ?? 0) + 1
|
||||
}],
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
Payload exports a type for field hooks which can be accessed and used as follows:
|
||||
|
||||
@@ -32,4 +32,4 @@
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.8.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"strict": false,
|
||||
"esModuleInterop": true,
|
||||
@@ -12,12 +16,22 @@
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"paths": {
|
||||
"payload/generated-types": ["./src/payload-types.ts"],
|
||||
"node_modules/*": ["./node_modules/*"]
|
||||
}
|
||||
"payload/generated-types": [
|
||||
"./src/payload-types.ts"
|
||||
],
|
||||
"node_modules/*": [
|
||||
"./node_modules/*"
|
||||
]
|
||||
},
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist", "build"],
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"build",
|
||||
],
|
||||
"ts-node": {
|
||||
"transpileOnly": true
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"outDir": "./dist",
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
@@ -14,8 +18,10 @@
|
||||
"jsx": "preserve",
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"ts-node": {
|
||||
"transpileOnly": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,4 +26,4 @@
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.8.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"strict": false,
|
||||
"esModuleInterop": true,
|
||||
@@ -10,8 +14,14 @@
|
||||
"rootDir": "./src",
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist", "build"],
|
||||
"include": [
|
||||
"src",
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"build",
|
||||
],
|
||||
"ts-node": {
|
||||
"transpileOnly": true
|
||||
}
|
||||
|
||||
@@ -26,4 +26,4 @@
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.8.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"strict": false,
|
||||
"esModuleInterop": true,
|
||||
@@ -10,8 +14,14 @@
|
||||
"rootDir": "./src",
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist", "build"],
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"build",
|
||||
],
|
||||
"ts-node": {
|
||||
"transpileOnly": true
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/** @type {import('jest').Config} */
|
||||
const customJestConfig = {
|
||||
extensionsToTreatAsEsm: ['.ts', '.tsx'],
|
||||
setupFilesAfterEnv: ['<rootDir>/test/jest.setup.ts'],
|
||||
globalSetup: './test/jest.setup.ts',
|
||||
moduleNameMapper: {
|
||||
'\\.(css|scss)$': '<rootDir>/test/helpers/mocks/emptyModule.js',
|
||||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
|
||||
@@ -18,8 +18,4 @@ const customJestConfig = {
|
||||
verbose: true,
|
||||
}
|
||||
|
||||
if (process.env.CI) {
|
||||
customJestConfig.reporters = [['github-actions', { silent: false }], 'summary']
|
||||
}
|
||||
|
||||
export default customJestConfig
|
||||
|
||||
@@ -19,9 +19,6 @@ export default withBundleAnalyzer(
|
||||
},
|
||||
]
|
||||
},
|
||||
images: {
|
||||
domains: ['localhost'],
|
||||
},
|
||||
webpack: (webpackConfig) => {
|
||||
webpackConfig.resolve.extensionAlias = {
|
||||
'.cjs': ['.cts', '.cjs'],
|
||||
|
||||
35
package.json
35
package.json
@@ -1,11 +1,10 @@
|
||||
{
|
||||
"name": "payload-monorepo",
|
||||
"version": "3.0.0-alpha.49",
|
||||
"version": "3.0.0-alpha.48",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"workspaces:": [
|
||||
"packages/*",
|
||||
"test/*"
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "pnpm run build:core",
|
||||
@@ -35,15 +34,14 @@
|
||||
"build:plugin-stripe": "turbo build --filter plugin-stripe",
|
||||
"build:richtext-lexical": "turbo build --filter richtext-lexical",
|
||||
"build:richtext-slate": "turbo build --filter richtext-slate",
|
||||
"build:tests": "pnpm --filter test run typecheck",
|
||||
"build:translations": "turbo build --filter translations",
|
||||
"build:ui": "turbo build --filter ui",
|
||||
"clean": "turbo clean",
|
||||
"clean:cache": "rimraf node_modules/.cache && rimraf packages/payload/node_modules/.cache && rimraf .next",
|
||||
"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_OPTIONS=--no-deprecation node ./test/dev.js",
|
||||
"devsafe": "rimraf .next && pnpm dev",
|
||||
"dev": "cross-env node --no-deprecation ./test/dev.js",
|
||||
"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",
|
||||
@@ -58,16 +56,15 @@
|
||||
"pretest": "pnpm build",
|
||||
"reinstall": "pnpm clean:all && pnpm install",
|
||||
"script:list-packages": "tsx ./scripts/list-packages.ts",
|
||||
"script:pack": "tsx scripts/pack-all-to-dest.ts",
|
||||
"release:alpha": "tsx ./scripts/release.ts --bump prerelease --tag alpha",
|
||||
"release:beta": "tsx ./scripts/release.ts --bump prerelease --tag beta",
|
||||
"test": "pnpm test:int && pnpm test:components && pnpm test:e2e",
|
||||
"test:components": "cross-env NODE_OPTIONS=--no-deprecation jest --config=jest.components.config.js",
|
||||
"test:e2e": "cross-env NODE_OPTIONS=--no-deprecation NODE_NO_WARNINGS=1 tsx ./test/runE2E.ts",
|
||||
"test:e2e:debug": "cross-env NODE_OPTIONS=--no-deprecation NODE_NO_WARNINGS=1 PWDEBUG=1 DISABLE_LOGGING=true playwright test",
|
||||
"test:e2e:headed": "cross-env NODE_OPTIONS=--no-deprecation NODE_NO_WARNINGS=1 DISABLE_LOGGING=true playwright test --headed",
|
||||
"test:int:postgres": "cross-env NODE_OPTIONS=--no-deprecation NODE_NO_WARNINGS=1 PAYLOAD_DATABASE=postgres DISABLE_LOGGING=true jest --forceExit --detectOpenHandles",
|
||||
"test:int": "cross-env NODE_OPTIONS=--no-deprecation NODE_NO_WARNINGS=1 DISABLE_LOGGING=true jest --forceExit --detectOpenHandles",
|
||||
"test:e2e": "NODE_OPTIONS=--no-deprecation tsx ./test/runE2E.ts",
|
||||
"test:e2e:debug": "cross-env NODE_OPTIONS=--no-deprecation PWDEBUG=1 DISABLE_LOGGING=true playwright test",
|
||||
"test:e2e:headed": "cross-env NODE_OPTIONS=--no-deprecation DISABLE_LOGGING=true playwright test --headed",
|
||||
"test:int:postgres": "cross-env NODE_OPTIONS=--no-deprecation PAYLOAD_DATABASE=postgres DISABLE_LOGGING=true jest --forceExit --detectOpenHandles",
|
||||
"test:int": "cross-env NODE_OPTIONS=--no-deprecation DISABLE_LOGGING=true jest --forceExit --detectOpenHandles",
|
||||
"translateNewKeys": "pnpm --filter payload run translateNewKeys"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -75,7 +72,6 @@
|
||||
"@next/bundle-analyzer": "^14.1.0",
|
||||
"@octokit/core": "^5.1.0",
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@payloadcms/live-preview-react": "workspace:*",
|
||||
"@playwright/test": "^1.42.1",
|
||||
"@swc/cli": "^0.1.62",
|
||||
"@swc/jest": "0.2.36",
|
||||
@@ -89,7 +85,7 @@
|
||||
"@types/fs-extra": "^11.0.2",
|
||||
"@types/jest": "29.5.12",
|
||||
"@types/minimist": "1.2.2",
|
||||
"@types/node": "20.11.28",
|
||||
"@types/node": "20.5.7",
|
||||
"@types/prompts": "^2.4.5",
|
||||
"@types/qs": "6.9.7",
|
||||
"@types/react": "18.2.15",
|
||||
@@ -109,20 +105,17 @@
|
||||
"dotenv": "8.6.0",
|
||||
"drizzle-kit": "0.20.14-1f2c838",
|
||||
"drizzle-orm": "0.29.4",
|
||||
"escape-html": "^1.0.3",
|
||||
"eslint-plugin-payload": "workspace:*",
|
||||
"execa": "5.1.1",
|
||||
"form-data": "3.0.1",
|
||||
"fs-extra": "10.1.0",
|
||||
"get-port": "5.1.1",
|
||||
"get-stream": "6.0.1",
|
||||
"glob": "8.1.0",
|
||||
"globby": "11.1.0",
|
||||
"husky": "^8.0.3",
|
||||
"jest": "29.7.0",
|
||||
"jest-environment-jsdom": "29.7.0",
|
||||
"json5": "^2.2.3",
|
||||
"jwt-decode": "4.0.0",
|
||||
"jwt-decode": "3.1.2",
|
||||
"lexical": "0.13.1",
|
||||
"lint-staged": "^14.0.1",
|
||||
"minimist": "1.2.8",
|
||||
@@ -130,7 +123,6 @@
|
||||
"next": "14.2.0-canary.22",
|
||||
"node-mocks-http": "^1.14.1",
|
||||
"nodemon": "3.0.3",
|
||||
"open": "^10.1.0",
|
||||
"pino": "8.15.0",
|
||||
"pino-pretty": "10.2.0",
|
||||
"playwright": "^1.42.1",
|
||||
@@ -153,8 +145,8 @@
|
||||
"tempy": "^1.0.1",
|
||||
"ts-node": "10.9.1",
|
||||
"tsx": "^4.7.1",
|
||||
"turbo": "^1.13.0",
|
||||
"typescript": "5.4.2",
|
||||
"turbo": "^1.12.5",
|
||||
"typescript": "5.2.2",
|
||||
"uuid": "^9.0.1",
|
||||
"yocto-queue": "^1.0.0"
|
||||
},
|
||||
@@ -167,7 +159,6 @@
|
||||
"pnpm": ">=8"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{md,mdx,yml,json}": "prettier --write",
|
||||
"*.{js,jsx,ts,tsx}": [
|
||||
"prettier --write",
|
||||
"eslint --cache --fix"
|
||||
|
||||
@@ -3,7 +3,7 @@ import baseConfig from '../../jest.config.js'
|
||||
/** @type {import('@jest/types').Config} */
|
||||
const customJestConfig = {
|
||||
...baseConfig,
|
||||
setupFilesAfterEnv: null,
|
||||
globalSetup: null,
|
||||
testMatch: ['**/src/**/?(*.)+(spec|test|it-test).[tj]s?(x)'],
|
||||
testTimeout: 20000,
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
"create-payload-app": "bin/cli.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm copyfiles && pnpm typecheck && pnpm build:swc",
|
||||
"typecheck": "tsc",
|
||||
"build": "pnpm copyfiles && pnpm build:swc",
|
||||
"copyfiles": "copyfiles -u 2 \"../../app/(payload)/**\" \"dist\"",
|
||||
"build:swc": "swc ./src -d ./dist --config-file .swcrc",
|
||||
"clean": "rimraf {dist,*.tsbuildinfo}",
|
||||
@@ -28,7 +27,6 @@
|
||||
"comment-json": "^4.2.3",
|
||||
"degit": "^2.8.4",
|
||||
"detect-package-manager": "^3.0.1",
|
||||
"esprima": "^4.0.1",
|
||||
"execa": "^5.0.0",
|
||||
"figures": "^3.2.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
@@ -41,17 +39,9 @@
|
||||
"devDependencies": {
|
||||
"@types/command-exists": "^1.2.0",
|
||||
"@types/degit": "^2.8.3",
|
||||
"@types/esprima": "^4.0.6",
|
||||
"@types/fs-extra": "^9.0.12",
|
||||
"@types/jest": "^27.0.3",
|
||||
"@types/node": "^16.6.2",
|
||||
"@types/prompts": "^2.4.1"
|
||||
},
|
||||
"exports": {
|
||||
"./commands": {
|
||||
"import": "./src/lib/init-next.ts",
|
||||
"require": "./src/lib/init-next.ts",
|
||||
"types": "./src/lib/init-next.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import fse from 'fs-extra'
|
||||
import globby from 'globby'
|
||||
import path from 'path'
|
||||
|
||||
import type { DbDetails } from '../types.js'
|
||||
|
||||
import { warning } from '../utils/log.js'
|
||||
import { dbReplacements } from './packages.js'
|
||||
import { bundlerPackages, dbPackages, editorPackages } from './packages.js'
|
||||
|
||||
/** Update payload config with necessary imports and adapters */
|
||||
export async function configurePayloadConfig(args: {
|
||||
@@ -15,10 +15,46 @@ export async function configurePayloadConfig(args: {
|
||||
return
|
||||
}
|
||||
|
||||
// Update package.json
|
||||
const packageJsonPath = path.resolve(args.projectDir, 'package.json')
|
||||
try {
|
||||
const payloadConfigPath = (
|
||||
await globby('**/payload.config.ts', { absolute: true, cwd: args.projectDir })
|
||||
)?.[0]
|
||||
const packageObj = await fse.readJson(packageJsonPath)
|
||||
|
||||
packageObj.dependencies['payload'] = '^2.0.0'
|
||||
|
||||
const dbPackage = dbPackages[args.dbDetails.type]
|
||||
const bundlerPackage = bundlerPackages['webpack']
|
||||
const editorPackage = editorPackages['slate']
|
||||
|
||||
// Delete all other db adapters
|
||||
Object.values(dbPackages).forEach((p) => {
|
||||
if (p.packageName !== dbPackage.packageName) {
|
||||
delete packageObj.dependencies[p.packageName]
|
||||
}
|
||||
})
|
||||
|
||||
packageObj.dependencies[dbPackage.packageName] = dbPackage.version
|
||||
packageObj.dependencies[bundlerPackage.packageName] = bundlerPackage.version
|
||||
packageObj.dependencies[editorPackage.packageName] = editorPackage.version
|
||||
|
||||
await fse.writeJson(packageJsonPath, packageObj, { spaces: 2 })
|
||||
} catch (err: unknown) {
|
||||
warning('Unable to update name in package.json')
|
||||
}
|
||||
|
||||
try {
|
||||
const possiblePaths = [
|
||||
path.resolve(args.projectDir, 'src/payload.config.ts'),
|
||||
path.resolve(args.projectDir, 'src/payload/payload.config.ts'),
|
||||
]
|
||||
|
||||
let payloadConfigPath: string | undefined
|
||||
|
||||
possiblePaths.forEach((p) => {
|
||||
if (fse.pathExistsSync(p) && !payloadConfigPath) {
|
||||
payloadConfigPath = p
|
||||
}
|
||||
})
|
||||
|
||||
if (!payloadConfigPath) {
|
||||
warning('Unable to update payload.config.ts with plugins')
|
||||
@@ -28,7 +64,9 @@ export async function configurePayloadConfig(args: {
|
||||
const configContent = fse.readFileSync(payloadConfigPath, 'utf-8')
|
||||
const configLines = configContent.split('\n')
|
||||
|
||||
const dbReplacement = dbReplacements[args.dbDetails.type]
|
||||
const dbReplacement = dbPackages[args.dbDetails.type]
|
||||
const bundlerReplacement = bundlerPackages['webpack']
|
||||
const editorReplacement = editorPackages['slate']
|
||||
|
||||
let dbConfigStartLineIndex: number | undefined
|
||||
let dbConfigEndLineIndex: number | undefined
|
||||
@@ -37,6 +75,21 @@ export async function configurePayloadConfig(args: {
|
||||
if (l.includes('// database-adapter-import')) {
|
||||
configLines[i] = dbReplacement.importReplacement
|
||||
}
|
||||
if (l.includes('// bundler-import')) {
|
||||
configLines[i] = bundlerReplacement.importReplacement
|
||||
}
|
||||
|
||||
if (l.includes('// bundler-config')) {
|
||||
configLines[i] = bundlerReplacement.configReplacement
|
||||
}
|
||||
|
||||
if (l.includes('// editor-import')) {
|
||||
configLines[i] = editorReplacement.importReplacement
|
||||
}
|
||||
|
||||
if (l.includes('// editor-config')) {
|
||||
configLines[i] = editorReplacement.configReplacement
|
||||
}
|
||||
|
||||
if (l.includes('// database-adapter-config-start')) {
|
||||
dbConfigStartLineIndex = i
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import fse from 'fs-extra'
|
||||
import path from 'path'
|
||||
import type { CliArgs, DbType, ProjectTemplate } from '../types.js'
|
||||
import type { BundlerType, CliArgs, DbType, ProjectTemplate } from '../types.js'
|
||||
import { createProject } from './create-project.js'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { dbReplacements } from './packages.js'
|
||||
import { bundlerPackages, dbPackages, editorPackages } from './packages.js'
|
||||
import exp from 'constants'
|
||||
import { getValidTemplates } from './templates.js'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
const projectDir = path.resolve(dirname, './tmp')
|
||||
const projectDir = path.resolve(__dirname, './tmp')
|
||||
describe('createProject', () => {
|
||||
beforeAll(() => {
|
||||
console.log = jest.fn()
|
||||
@@ -31,11 +28,33 @@ describe('createProject', () => {
|
||||
const args = {
|
||||
_: ['project-name'],
|
||||
'--db': 'mongodb',
|
||||
'--local-template': 'blank',
|
||||
'--no-deps': true,
|
||||
} as CliArgs
|
||||
const packageManager = 'yarn'
|
||||
|
||||
it('creates starter project', async () => {
|
||||
const projectName = 'starter-project'
|
||||
const template: ProjectTemplate = {
|
||||
name: 'blank',
|
||||
type: 'starter',
|
||||
url: 'https://github.com/payloadcms/payload/templates/blank',
|
||||
description: 'Blank Template',
|
||||
}
|
||||
await createProject({
|
||||
cliArgs: args,
|
||||
projectName,
|
||||
projectDir,
|
||||
template,
|
||||
packageManager,
|
||||
})
|
||||
|
||||
const packageJsonPath = path.resolve(projectDir, 'package.json')
|
||||
const packageJson = fse.readJsonSync(packageJsonPath)
|
||||
|
||||
// Check package name and description
|
||||
expect(packageJson.name).toEqual(projectName)
|
||||
})
|
||||
|
||||
it('creates plugin template', async () => {
|
||||
const projectName = 'plugin'
|
||||
const template: ProjectTemplate = {
|
||||
@@ -59,34 +78,26 @@ describe('createProject', () => {
|
||||
expect(packageJson.name).toEqual(projectName)
|
||||
})
|
||||
|
||||
describe('creates project from template', () => {
|
||||
describe('db adapters and bundlers', () => {
|
||||
const templates = getValidTemplates()
|
||||
|
||||
it.each([
|
||||
['blank-3.0', 'mongodb'],
|
||||
['blank-3.0', 'postgres'],
|
||||
|
||||
// TODO: Re-enable these once 3.0 is stable and templates updated
|
||||
// ['website', 'mongodb'],
|
||||
// ['website', 'postgres'],
|
||||
// ['ecommerce', 'mongodb'],
|
||||
// ['ecommerce', 'postgres'],
|
||||
])('update config and deps: %s, %s', async (templateName, db) => {
|
||||
['blank', 'mongodb', 'webpack'],
|
||||
['blank', 'postgres', 'webpack'],
|
||||
['website', 'mongodb', 'webpack'],
|
||||
['website', 'postgres', 'webpack'],
|
||||
['ecommerce', 'mongodb', 'webpack'],
|
||||
['ecommerce', 'postgres', 'webpack'],
|
||||
])('update config and deps: %s, %s, %s', async (templateName, db, bundler) => {
|
||||
const projectName = 'starter-project'
|
||||
|
||||
const template = templates.find((t) => t.name === templateName)
|
||||
|
||||
const cliArgs = {
|
||||
...args,
|
||||
'--db': db,
|
||||
'--local-template': templateName,
|
||||
} as CliArgs
|
||||
|
||||
await createProject({
|
||||
cliArgs,
|
||||
cliArgs: args,
|
||||
projectName,
|
||||
projectDir,
|
||||
template: template as ProjectTemplate,
|
||||
template,
|
||||
packageManager,
|
||||
dbDetails: {
|
||||
dbUri: `${db}://localhost:27017/create-project-test`,
|
||||
@@ -94,17 +105,30 @@ describe('createProject', () => {
|
||||
},
|
||||
})
|
||||
|
||||
const dbReplacement = dbReplacements[db as DbType]
|
||||
const dbReplacement = dbPackages[db as DbType]
|
||||
const bundlerReplacement = bundlerPackages[bundler as BundlerType]
|
||||
const editorReplacement = editorPackages['slate']
|
||||
|
||||
const packageJsonPath = path.resolve(projectDir, 'package.json')
|
||||
const packageJson = fse.readJsonSync(packageJsonPath)
|
||||
|
||||
// Check deps
|
||||
expect(packageJson.dependencies['payload']).toEqual('^2.0.0')
|
||||
expect(packageJson.dependencies[dbReplacement.packageName]).toEqual(dbReplacement.version)
|
||||
|
||||
// Should only have one db adapter
|
||||
expect(
|
||||
Object.keys(packageJson.dependencies).filter((n) => n.startsWith('@payloadcms/db-')),
|
||||
).toHaveLength(1)
|
||||
|
||||
let payloadConfigPath = path.resolve(projectDir, 'payload.config.ts')
|
||||
expect(packageJson.dependencies[bundlerReplacement.packageName]).toEqual(
|
||||
bundlerReplacement.version,
|
||||
)
|
||||
expect(packageJson.dependencies[editorReplacement.packageName]).toEqual(
|
||||
editorReplacement.version,
|
||||
)
|
||||
|
||||
let payloadConfigPath = path.resolve(projectDir, 'src/payload.config.ts')
|
||||
|
||||
// Website and ecommerce templates have payload.config.ts in src/payload
|
||||
if (!fse.existsSync(payloadConfigPath)) {
|
||||
@@ -119,6 +143,12 @@ describe('createProject', () => {
|
||||
expect(content).not.toContain('// database-adapter-config-start')
|
||||
expect(content).not.toContain('// database-adapter-config-end')
|
||||
expect(content).toContain(dbReplacement.configReplacement.join('\n'))
|
||||
|
||||
expect(content).not.toContain('// bundler-config-import')
|
||||
expect(content).toContain(bundlerReplacement.importReplacement)
|
||||
|
||||
expect(content).not.toContain('// bundler-config')
|
||||
expect(content).toContain(bundlerReplacement.configReplacement)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,18 +2,14 @@ import chalk from 'chalk'
|
||||
import degit from 'degit'
|
||||
import execa from 'execa'
|
||||
import fse from 'fs-extra'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import ora from 'ora'
|
||||
import path from 'path'
|
||||
|
||||
import type { CliArgs, DbDetails, PackageManager, ProjectTemplate } from '../types.js'
|
||||
|
||||
import { debug, error, success, warning } from '../utils/log.js'
|
||||
import { error, success, warning } from '../utils/log.js'
|
||||
import { configurePayloadConfig } from './configure-payload-config.js'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
async function createOrFindProjectDir(projectDir: string): Promise<void> {
|
||||
const pathExists = await fse.pathExists(projectDir)
|
||||
if (!pathExists) {
|
||||
@@ -44,7 +40,7 @@ async function installDeps(args: {
|
||||
})
|
||||
return true
|
||||
} catch (err: unknown) {
|
||||
error(`Error installing dependencies${err instanceof Error ? `: ${err.message}` : ''}.`)
|
||||
console.log({ err })
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -59,30 +55,12 @@ export async function createProject(args: {
|
||||
}): Promise<void> {
|
||||
const { cliArgs, dbDetails, packageManager, projectDir, projectName, template } = args
|
||||
|
||||
if (cliArgs['--dry-run']) {
|
||||
console.log(`\n Dry run: Creating project in ${chalk.green(projectDir)}\n`)
|
||||
return
|
||||
}
|
||||
|
||||
await createOrFindProjectDir(projectDir)
|
||||
|
||||
console.log(`\n Creating project in ${chalk.green(projectDir)}\n`)
|
||||
console.log(`\n Creating project in ${chalk.green(path.resolve(projectDir))}\n`)
|
||||
|
||||
if (cliArgs['--local-template']) {
|
||||
// Copy template from local path. For development purposes.
|
||||
const localTemplate = path.resolve(
|
||||
dirname,
|
||||
'../../../../templates/',
|
||||
cliArgs['--local-template'],
|
||||
)
|
||||
await fse.copy(localTemplate, projectDir)
|
||||
} else if ('url' in template) {
|
||||
let templateUrl = template.url
|
||||
if (cliArgs['--template-branch']) {
|
||||
templateUrl = `${template.url}#${cliArgs['--template-branch']}`
|
||||
debug(`Using template url: ${templateUrl}`)
|
||||
}
|
||||
const emitter = degit(templateUrl)
|
||||
if ('url' in template) {
|
||||
const emitter = degit(template.url)
|
||||
await emitter.clone(projectDir)
|
||||
}
|
||||
|
||||
@@ -97,19 +75,14 @@ export async function createProject(args: {
|
||||
await fse.remove(lockPath)
|
||||
}
|
||||
|
||||
if (!cliArgs['--no-deps']) {
|
||||
spinner.text = 'Installing dependencies...'
|
||||
const result = await installDeps({ cliArgs, packageManager, projectDir })
|
||||
spinner.stop()
|
||||
spinner.clear()
|
||||
if (result) {
|
||||
success('Dependencies installed')
|
||||
} else {
|
||||
error('Error installing dependencies')
|
||||
}
|
||||
spinner.text = 'Installing dependencies...'
|
||||
const result = await installDeps({ cliArgs, packageManager, projectDir })
|
||||
spinner.stop()
|
||||
spinner.clear()
|
||||
if (result) {
|
||||
success('Dependencies installed')
|
||||
} else {
|
||||
spinner.stop()
|
||||
spinner.clear()
|
||||
error('Error installing dependencies')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { CompilerOptions } from 'typescript'
|
||||
|
||||
import chalk from 'chalk'
|
||||
import { parse, stringify } from 'comment-json'
|
||||
import { detect } from 'detect-package-manager'
|
||||
import execa from 'execa'
|
||||
import fs from 'fs'
|
||||
import fse from 'fs-extra'
|
||||
@@ -16,24 +17,24 @@ const dirname = path.dirname(filename)
|
||||
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
import type { CliArgs, PackageManager } from '../types.js'
|
||||
import type { CliArgs } from '../types.js'
|
||||
|
||||
import { copyRecursiveSync } from '../utils/copy-recursive-sync.js'
|
||||
import { error, info, debug as origDebug, success, warning } from '../utils/log.js'
|
||||
|
||||
type InitNextArgs = Pick<CliArgs, '--debug'> & {
|
||||
packageManager: PackageManager
|
||||
projectDir?: string
|
||||
useDistFiles?: boolean
|
||||
}
|
||||
type InitNextResult = { reason?: string; success: boolean; userAppDir?: string }
|
||||
|
||||
export async function initNext(args: InitNextArgs): Promise<InitNextResult> {
|
||||
const { packageManager, projectDir } = args
|
||||
args.projectDir = args.projectDir || process.cwd()
|
||||
const { projectDir } = args
|
||||
const templateResult = await applyPayloadTemplateFiles(args)
|
||||
if (!templateResult.success) return templateResult
|
||||
|
||||
const { success: installSuccess } = await installDeps(projectDir, packageManager)
|
||||
const { success: installSuccess } = await installDeps(projectDir)
|
||||
if (!installSuccess) {
|
||||
return { ...templateResult, reason: 'Failed to install dependencies', success: false }
|
||||
}
|
||||
@@ -102,7 +103,7 @@ async function applyPayloadTemplateFiles(args: InitNextArgs): Promise<InitNextRe
|
||||
}
|
||||
|
||||
// Next.js configs can be next.config.js, next.config.mjs, etc.
|
||||
const foundConfig = (await globby('next.config.*js', { absolute: true, cwd: projectDir }))?.[0]
|
||||
const foundConfig = (await globby('next.config.*js', { cwd: projectDir }))?.[0]
|
||||
|
||||
if (!foundConfig) {
|
||||
throw new Error(`No next.config.js found at ${projectDir}`)
|
||||
@@ -135,14 +136,11 @@ async function applyPayloadTemplateFiles(args: InitNextArgs): Promise<InitNextRe
|
||||
}
|
||||
|
||||
// src/app or app
|
||||
const userAppDir = (
|
||||
await globby(['**/app'], {
|
||||
absolute: true,
|
||||
cwd: projectDir,
|
||||
onlyDirectories: true,
|
||||
})
|
||||
)?.[0]
|
||||
|
||||
const userAppDirGlob = await globby(['**/app'], {
|
||||
cwd: projectDir,
|
||||
onlyDirectories: true,
|
||||
})
|
||||
const userAppDir = path.resolve(projectDir, userAppDirGlob?.[0])
|
||||
if (!fs.existsSync(userAppDir)) {
|
||||
return { reason: `Could not find user app directory inside ${projectDir}`, success: false }
|
||||
} else {
|
||||
@@ -155,13 +153,18 @@ async function applyPayloadTemplateFiles(args: InitNextArgs): Promise<InitNextRe
|
||||
return { success: true, userAppDir }
|
||||
}
|
||||
|
||||
async function installDeps(projectDir: string, packageManager: PackageManager) {
|
||||
async function installDeps(projectDir: string) {
|
||||
const packageManager = await detect({ cwd: projectDir })
|
||||
if (!packageManager) {
|
||||
throw new Error('Could not detect package manager')
|
||||
}
|
||||
|
||||
info(`Installing dependencies with ${packageManager}`, 1)
|
||||
const packagesToInstall = [
|
||||
'payload',
|
||||
'@payloadcms/db-mongodb',
|
||||
'@payloadcms/next',
|
||||
'@payloadcms/richtext-lexical',
|
||||
'@payloadcms/richtext-slate',
|
||||
].map((pkg) => `${pkg}@alpha`)
|
||||
|
||||
let exitCode = 0
|
||||
@@ -203,7 +206,7 @@ function findOrCreatePayloadConfig(projectDir: string) {
|
||||
const defaultConfig = `import path from "path";
|
||||
|
||||
import { mongooseAdapter } from "@payloadcms/db-mongodb"; // database-adapter-import
|
||||
import { lexicalEditor } from "@payloadcms/richtext-lexical"; // editor-import
|
||||
import { slateEditor } from "@payloadcms/richtext-slate"; // editor-import
|
||||
import { buildConfig } from "payload/config";
|
||||
|
||||
export default buildConfig({
|
||||
|
||||
@@ -1,9 +1,24 @@
|
||||
import type { DbType } from '../types.js'
|
||||
import type { BundlerType, DbType, EditorType } from '../types.js'
|
||||
|
||||
type DbAdapterReplacement = {
|
||||
configReplacement: string[]
|
||||
importReplacement: string
|
||||
packageName: string
|
||||
version: string
|
||||
}
|
||||
|
||||
type BundlerReplacement = {
|
||||
configReplacement: string
|
||||
importReplacement: string
|
||||
packageName: string
|
||||
version: string
|
||||
}
|
||||
|
||||
type EditorReplacement = {
|
||||
configReplacement: string
|
||||
importReplacement: string
|
||||
packageName: string
|
||||
version: string
|
||||
}
|
||||
|
||||
const mongodbReplacement: DbAdapterReplacement = {
|
||||
@@ -11,6 +26,7 @@ const mongodbReplacement: DbAdapterReplacement = {
|
||||
packageName: '@payloadcms/db-mongodb',
|
||||
// Replacement between `// database-adapter-config-start` and `// database-adapter-config-end`
|
||||
configReplacement: [' db: mongooseAdapter({', ' url: process.env.DATABASE_URI,', ' }),'],
|
||||
version: '^1.0.0',
|
||||
}
|
||||
|
||||
const postgresReplacement: DbAdapterReplacement = {
|
||||
@@ -23,9 +39,45 @@ const postgresReplacement: DbAdapterReplacement = {
|
||||
],
|
||||
importReplacement: "import { postgresAdapter } from '@payloadcms/db-postgres'",
|
||||
packageName: '@payloadcms/db-postgres',
|
||||
version: '^0.x', // up to, not including 1.0.0
|
||||
}
|
||||
|
||||
export const dbReplacements: Record<DbType, DbAdapterReplacement> = {
|
||||
export const dbPackages: Record<DbType, DbAdapterReplacement> = {
|
||||
mongodb: mongodbReplacement,
|
||||
postgres: postgresReplacement,
|
||||
}
|
||||
|
||||
const webpackReplacement: BundlerReplacement = {
|
||||
importReplacement: "import { webpackBundler } from '@payloadcms/bundler-webpack'",
|
||||
packageName: '@payloadcms/bundler-webpack',
|
||||
// Replacement of line containing `// bundler-config`
|
||||
configReplacement: ' bundler: webpackBundler(),',
|
||||
version: '^1.0.0',
|
||||
}
|
||||
|
||||
const viteReplacement: BundlerReplacement = {
|
||||
configReplacement: ' bundler: viteBundler(),',
|
||||
importReplacement: "import { viteBundler } from '@payloadcms/bundler-vite'",
|
||||
packageName: '@payloadcms/bundler-vite',
|
||||
version: '^0.x', // up to, not including 1.0.0
|
||||
}
|
||||
|
||||
export const bundlerPackages: Record<BundlerType, BundlerReplacement> = {
|
||||
vite: viteReplacement,
|
||||
webpack: webpackReplacement,
|
||||
}
|
||||
|
||||
export const editorPackages: Record<EditorType, EditorReplacement> = {
|
||||
lexical: {
|
||||
configReplacement: ' editor: lexicalEditor({}),',
|
||||
importReplacement: "import { lexicalEditor } from '@payloadcms/richtext-lexical'",
|
||||
packageName: '@payloadcms/richtext-lexical',
|
||||
version: '^0.x', // up to, not including 1.0.0
|
||||
},
|
||||
slate: {
|
||||
configReplacement: ' editor: slateEditor({}),',
|
||||
importReplacement: "import { slateEditor } from '@payloadcms/richtext-slate'",
|
||||
packageName: '@payloadcms/richtext-slate',
|
||||
version: '^1.0.0',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import prompts from 'prompts'
|
||||
import type { CliArgs } from '../types.js'
|
||||
|
||||
export async function parseProjectName(args: CliArgs): Promise<string> {
|
||||
if (args['--init-next']) return '.'
|
||||
if (args['--name']) return args['--name']
|
||||
if (args._[0]) return args._[0]
|
||||
|
||||
|
||||
@@ -58,36 +58,26 @@ export async function selectDb(args: CliArgs, projectName: string): Promise<DbDe
|
||||
|
||||
const dbChoice = dbChoiceRecord[dbType]
|
||||
|
||||
let dbUri: string | undefined = undefined
|
||||
const initialDbUri = `${dbChoice.dbConnectionPrefix}${
|
||||
projectName === '.' ? `payload-${getRandomDigitSuffix()}` : slugify(projectName)
|
||||
}`
|
||||
|
||||
if (args['--db-accept-recommended']) {
|
||||
dbUri = initialDbUri
|
||||
} else if (args['--db-connection-string']) {
|
||||
dbUri = args['--db-connection-string']
|
||||
} else {
|
||||
const dbUriRes = await prompts(
|
||||
{
|
||||
name: 'value',
|
||||
type: 'text',
|
||||
initial: initialDbUri,
|
||||
message: `Enter ${dbChoice.title.split(' ')[0]} connection string`, // strip beta from title
|
||||
validate: (value: string) => !!value.length,
|
||||
const dbUriRes = await prompts(
|
||||
{
|
||||
name: 'value',
|
||||
type: 'text',
|
||||
initial: `${dbChoice.dbConnectionPrefix}${
|
||||
projectName === '.' ? `payload-${getRandomDigitSuffix()}` : slugify(projectName)
|
||||
}`,
|
||||
message: `Enter ${dbChoice.title.split(' ')[0]} connection string`, // strip beta from title
|
||||
validate: (value: string) => !!value.length,
|
||||
},
|
||||
{
|
||||
onCancel: () => {
|
||||
process.exit(0)
|
||||
},
|
||||
{
|
||||
onCancel: () => {
|
||||
process.exit(0)
|
||||
},
|
||||
},
|
||||
)
|
||||
dbUri = dbUriRes.value
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return {
|
||||
type: dbChoice.value,
|
||||
dbUri,
|
||||
dbUri: dbUriRes.value,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,6 @@ export function validateTemplate(templateName: string): boolean {
|
||||
|
||||
export function getValidTemplates(): ProjectTemplate[] {
|
||||
return [
|
||||
{
|
||||
name: 'blank-3.0',
|
||||
type: 'starter',
|
||||
description: 'Blank 3.0 Template',
|
||||
url: 'https://github.com/payloadcms/payload/templates/blank-3.0',
|
||||
},
|
||||
{
|
||||
name: 'blank',
|
||||
type: 'starter',
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import { parseAndInsertWithPayload, withPayloadImportStatement } from './wrap-next-config.js'
|
||||
|
||||
const defaultNextConfig = `/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {};
|
||||
|
||||
export default nextConfig;
|
||||
`
|
||||
|
||||
const nextConfigWithFunc = `const nextConfig = {
|
||||
// Your Next.js config here
|
||||
}
|
||||
|
||||
export default someFunc(nextConfig)
|
||||
`
|
||||
const nextConfigWithFuncMultiline = `const nextConfig = {
|
||||
// Your Next.js config here
|
||||
}
|
||||
|
||||
export default someFunc(
|
||||
nextConfig
|
||||
)
|
||||
`
|
||||
|
||||
const nextConfigExportNamedDefault = `const nextConfig = {
|
||||
// Your Next.js config here
|
||||
}
|
||||
const wrapped = someFunc(asdf)
|
||||
export { wrapped as default }
|
||||
`
|
||||
|
||||
describe('parseAndInsertWithPayload', () => {
|
||||
it('should parse the default next config', () => {
|
||||
const { modifiedConfigContent } = parseAndInsertWithPayload(defaultNextConfig)
|
||||
expect(modifiedConfigContent).toContain(withPayloadImportStatement)
|
||||
expect(modifiedConfigContent).toContain('withPayload(nextConfig)')
|
||||
})
|
||||
it('should parse the config with a function', () => {
|
||||
const { modifiedConfigContent } = parseAndInsertWithPayload(nextConfigWithFunc)
|
||||
expect(modifiedConfigContent).toContain('withPayload(someFunc(nextConfig))')
|
||||
})
|
||||
|
||||
it('should parse the config with a function on a new line', () => {
|
||||
const { modifiedConfigContent } = parseAndInsertWithPayload(nextConfigWithFuncMultiline)
|
||||
expect(modifiedConfigContent).toContain(withPayloadImportStatement)
|
||||
expect(modifiedConfigContent).toMatch(/withPayload\(someFunc\(\n nextConfig\n\)\)/)
|
||||
})
|
||||
|
||||
// Unsupported: export { wrapped as default }
|
||||
it('should give warning with a named export as default', () => {
|
||||
const { modifiedConfigContent, error } = parseAndInsertWithPayload(nextConfigExportNamedDefault)
|
||||
expect(modifiedConfigContent).toContain(withPayloadImportStatement)
|
||||
expect(error).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -1,118 +0,0 @@
|
||||
import { parseModule } from 'esprima'
|
||||
import fs from 'fs'
|
||||
import globby from 'globby'
|
||||
import path from 'path'
|
||||
|
||||
export const withPayloadImportStatement = `import { withPayload } from '@payloadcms/next'\n`
|
||||
|
||||
export const wrapNextConfig = async (args: { projectDir: string }): Promise<void> => {
|
||||
const foundConfig = (await globby('next.config.*js', { cwd: args.projectDir }))?.[0]
|
||||
|
||||
if (!foundConfig) {
|
||||
throw new Error(`No Next config found at ${args.projectDir}`)
|
||||
}
|
||||
const configPath = path.resolve(args.projectDir, foundConfig)
|
||||
const configContent = fs.readFileSync(configPath, 'utf8')
|
||||
const { error, modifiedConfigContent: newConfig } = parseAndInsertWithPayload(configContent)
|
||||
if (error) {
|
||||
console.warn(error)
|
||||
}
|
||||
fs.writeFileSync(configPath, newConfig)
|
||||
}
|
||||
|
||||
export function parseAndInsertWithPayload(content: string): {
|
||||
error?: string
|
||||
modifiedConfigContent: string
|
||||
} {
|
||||
content = withPayloadImportStatement + content
|
||||
const ast = parseModule(content, { loc: true })
|
||||
const exportDefaultDeclaration = ast.body.find((p) => p.type === 'ExportDefaultDeclaration') as
|
||||
| Directive
|
||||
| undefined
|
||||
|
||||
const exportNamedDeclaration = ast.body.find((p) => p.type === 'ExportNamedDeclaration') as
|
||||
| ExportNamedDeclaration
|
||||
| undefined
|
||||
|
||||
if (!exportDefaultDeclaration && !exportNamedDeclaration) {
|
||||
throw new Error('Could not find ExportDefaultDeclaration in next.config.js')
|
||||
}
|
||||
|
||||
if (exportDefaultDeclaration) {
|
||||
const modifiedConfigContent = insertBeforeAndAfter(
|
||||
content,
|
||||
exportDefaultDeclaration.declaration?.loc,
|
||||
)
|
||||
return { modifiedConfigContent }
|
||||
} else if (exportNamedDeclaration) {
|
||||
const exportSpecifier = exportNamedDeclaration.specifiers.find(
|
||||
(s) =>
|
||||
s.type === 'ExportSpecifier' &&
|
||||
s.exported?.name === 'default' &&
|
||||
s.local?.type === 'Identifier' &&
|
||||
s.local?.name,
|
||||
)
|
||||
|
||||
if (exportSpecifier) {
|
||||
// TODO: Improve with this example and/or link to docs
|
||||
return {
|
||||
error: `Automatic wrapping of named exports as default not supported yet.
|
||||
Please manually wrap your Next config with the withPayload function`,
|
||||
modifiedConfigContent: content,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Error('Could not automatically wrap next.config.js with withPayload')
|
||||
}
|
||||
}
|
||||
|
||||
type Directive = {
|
||||
declaration?: {
|
||||
loc: Loc
|
||||
}
|
||||
}
|
||||
|
||||
type ExportNamedDeclaration = {
|
||||
declaration: null
|
||||
loc: Loc
|
||||
specifiers: {
|
||||
exported: {
|
||||
loc: Loc
|
||||
name: string
|
||||
type: string
|
||||
}
|
||||
loc: Loc
|
||||
local: {
|
||||
loc: Loc
|
||||
name: string
|
||||
type: string
|
||||
}
|
||||
type: string
|
||||
}[]
|
||||
type: string
|
||||
}
|
||||
|
||||
type Loc = {
|
||||
end: { column: number; line: number }
|
||||
start: { column: number; line: number }
|
||||
}
|
||||
|
||||
function insertBeforeAndAfter(content: string, loc: Loc) {
|
||||
const { end, start } = loc
|
||||
const lines = content.split('\n')
|
||||
|
||||
const insert = (line: string, column: number, text: string) => {
|
||||
return line.slice(0, column) + text + line.slice(column)
|
||||
}
|
||||
|
||||
// insert ) after end
|
||||
lines[end.line - 1] = insert(lines[end.line - 1], end.column, ')')
|
||||
// insert withPayload before start
|
||||
if (start.line === end.line) {
|
||||
lines[end.line - 1] = insert(lines[end.line - 1], start.column, 'withPayload(')
|
||||
} else {
|
||||
lines[start.line - 1] = insert(lines[start.line - 1], start.column, 'withPayload(')
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
@@ -1,26 +1,18 @@
|
||||
import chalk from 'chalk'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
|
||||
import type { CliArgs, ProjectTemplate } from '../types.js'
|
||||
import type { ProjectTemplate } from '../types.js'
|
||||
|
||||
import { error, success } from '../utils/log.js'
|
||||
|
||||
/** Parse and swap .env.example values and write .env */
|
||||
export async function writeEnvFile(args: {
|
||||
cliArgs: CliArgs
|
||||
databaseUri: string
|
||||
payloadSecret: string
|
||||
projectDir: string
|
||||
template: ProjectTemplate
|
||||
}): Promise<void> {
|
||||
const { cliArgs, databaseUri, payloadSecret, projectDir, template } = args
|
||||
|
||||
if (cliArgs['--dry-run']) {
|
||||
success(`DRY RUN: .env file created`)
|
||||
return
|
||||
}
|
||||
|
||||
const { databaseUri, payloadSecret, projectDir, template } = args
|
||||
try {
|
||||
if (template.type === 'starter' && fs.existsSync(path.join(projectDir, '.env.example'))) {
|
||||
// Parse .env file into key/value pairs
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/* eslint-disable no-console */
|
||||
import slugify from '@sindresorhus/slugify'
|
||||
import arg from 'arg'
|
||||
import { detect } from 'detect-package-manager'
|
||||
import path from 'path'
|
||||
import commandExists from 'command-exists'
|
||||
|
||||
import type { CliArgs, PackageManager } from './types.js'
|
||||
|
||||
@@ -25,14 +23,10 @@ export class Main {
|
||||
this.args = arg(
|
||||
{
|
||||
'--db': String,
|
||||
'--db-accept-recommended': Boolean,
|
||||
'--db-connection-string': String,
|
||||
'--help': Boolean,
|
||||
'--local-template': String,
|
||||
'--name': String,
|
||||
'--secret': String,
|
||||
'--template': String,
|
||||
'--template-branch': String,
|
||||
|
||||
// Next.js
|
||||
'--init-next': Boolean,
|
||||
@@ -65,25 +59,14 @@ export class Main {
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const projectName = await parseProjectName(this.args)
|
||||
const projectDir = path.resolve(
|
||||
projectName === '.' || this.args['--init-next']
|
||||
? path.basename(process.cwd())
|
||||
: `./${slugify(projectName)}`,
|
||||
)
|
||||
|
||||
console.log(welcomeMessage)
|
||||
const packageManager = await getPackageManager(this.args, projectDir)
|
||||
|
||||
if (this.args['--init-next']) {
|
||||
const result = await initNext({ ...this.args, packageManager })
|
||||
const result = await initNext(this.args)
|
||||
if (!result.success) {
|
||||
error(result.reason || 'Failed to initialize Payload app in Next.js project')
|
||||
} else {
|
||||
success('Payload app successfully initialized in Next.js project')
|
||||
}
|
||||
process.exit(result.success ? 0 : 1)
|
||||
// TODO: This should continue the normal prompt flow
|
||||
}
|
||||
|
||||
const templateArg = this.args['--template']
|
||||
@@ -95,13 +78,18 @@ export class Main {
|
||||
}
|
||||
}
|
||||
|
||||
console.log(welcomeMessage)
|
||||
const projectName = await parseProjectName(this.args)
|
||||
const validTemplates = getValidTemplates()
|
||||
const template = await parseTemplate(this.args, validTemplates)
|
||||
|
||||
switch (template.type) {
|
||||
case 'starter': {
|
||||
const dbDetails = await selectDb(this.args, projectName)
|
||||
const payloadSecret = generateSecret()
|
||||
const projectDir = projectName === '.' ? process.cwd() : `./${slugify(projectName)}`
|
||||
const packageManager = await getPackageManager(this.args)
|
||||
|
||||
if (template.type !== 'plugin') {
|
||||
const dbDetails = await selectDb(this.args, projectName)
|
||||
const payloadSecret = generateSecret()
|
||||
if (!this.args['--dry-run']) {
|
||||
await createProject({
|
||||
cliArgs: this.args,
|
||||
dbDetails,
|
||||
@@ -111,15 +99,14 @@ export class Main {
|
||||
template,
|
||||
})
|
||||
await writeEnvFile({
|
||||
cliArgs: this.args,
|
||||
databaseUri: dbDetails.dbUri,
|
||||
payloadSecret,
|
||||
projectDir,
|
||||
template,
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'plugin': {
|
||||
} else {
|
||||
if (!this.args['--dry-run']) {
|
||||
await createProject({
|
||||
cliArgs: this.args,
|
||||
packageManager,
|
||||
@@ -127,7 +114,6 @@ export class Main {
|
||||
projectName,
|
||||
template,
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +125,7 @@ export class Main {
|
||||
}
|
||||
}
|
||||
|
||||
async function getPackageManager(args: CliArgs, projectDir: string): Promise<PackageManager> {
|
||||
async function getPackageManager(args: CliArgs): Promise<PackageManager> {
|
||||
let packageManager: PackageManager = 'npm'
|
||||
|
||||
if (args['--use-npm']) {
|
||||
@@ -149,8 +135,15 @@ async function getPackageManager(args: CliArgs, projectDir: string): Promise<Pac
|
||||
} else if (args['--use-pnpm']) {
|
||||
packageManager = 'pnpm'
|
||||
} else {
|
||||
const detected = await detect({ cwd: projectDir })
|
||||
packageManager = detected || 'npm'
|
||||
try {
|
||||
if (await commandExists('yarn')) {
|
||||
packageManager = 'yarn'
|
||||
} else if (await commandExists('pnpm')) {
|
||||
packageManager = 'pnpm'
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
packageManager = 'npm'
|
||||
}
|
||||
}
|
||||
return packageManager
|
||||
}
|
||||
|
||||
@@ -3,18 +3,14 @@ import type arg from 'arg'
|
||||
export interface Args extends arg.Spec {
|
||||
'--beta': BooleanConstructor
|
||||
'--db': StringConstructor
|
||||
'--db-accept-recommended': BooleanConstructor
|
||||
'--db-connection-string': StringConstructor
|
||||
'--debug': BooleanConstructor
|
||||
'--dry-run': BooleanConstructor
|
||||
'--help': BooleanConstructor
|
||||
'--init-next': BooleanConstructor
|
||||
'--local-template': StringConstructor
|
||||
'--name': StringConstructor
|
||||
'--no-deps': BooleanConstructor
|
||||
'--secret': StringConstructor
|
||||
'--template': StringConstructor
|
||||
'--template-branch': StringConstructor
|
||||
'--use-npm': BooleanConstructor
|
||||
'--use-pnpm': BooleanConstructor
|
||||
'--use-yarn': BooleanConstructor
|
||||
@@ -54,7 +50,7 @@ interface Template {
|
||||
type: ProjectTemplate['type']
|
||||
}
|
||||
|
||||
export type PackageManager = 'bun' | 'npm' | 'pnpm' | 'yarn'
|
||||
export type PackageManager = 'npm' | 'pnpm' | 'yarn'
|
||||
|
||||
export type DbType = 'mongodb' | 'postgres'
|
||||
|
||||
@@ -63,4 +59,5 @@ export type DbDetails = {
|
||||
type: DbType
|
||||
}
|
||||
|
||||
export type BundlerType = 'vite' | 'webpack'
|
||||
export type EditorType = 'lexical' | 'slate'
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-console */
|
||||
import chalk from 'chalk'
|
||||
import figures from 'figures'
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ import figures from 'figures'
|
||||
import path from 'path'
|
||||
import terminalLink from 'terminal-link'
|
||||
|
||||
import type { ProjectTemplate } from '../types.js'
|
||||
import type { ProjectTemplate } from '../types'
|
||||
|
||||
import { getValidTemplates } from '../lib/templates.js'
|
||||
import { getValidTemplates } from '../lib/templates'
|
||||
|
||||
const header = (message: string): string => `${chalk.yellow(figures.star)} ${chalk.bold(message)}`
|
||||
|
||||
|
||||
@@ -7,6 +7,17 @@
|
||||
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
||||
"rootDir": "./src" /* Specify the root folder within your source files. */
|
||||
},
|
||||
"exclude": ["dist", "build", "tests", "test", "node_modules", ".eslintrc.js"],
|
||||
"include": ["src/**/*.ts", "src/**/*.spec.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.json"]
|
||||
"exclude": [
|
||||
"dist",
|
||||
"build",
|
||||
"tests",
|
||||
"test",
|
||||
"node_modules",
|
||||
".eslintrc.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.spec.tsx"
|
||||
],
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.json"]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"version": "3.0.0-alpha.49",
|
||||
"version": "3.0.0-alpha.48",
|
||||
"description": "The officially supported MongoDB database adapter for Payload - Update 2",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
@@ -11,8 +11,8 @@
|
||||
"name": "Payload",
|
||||
"url": "https://payloadcms.com"
|
||||
},
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/types.ts",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "pnpm build:swc && pnpm build:types",
|
||||
"build:swc": "swc ./src -d ./dist --config-file .swcrc",
|
||||
@@ -41,23 +41,10 @@
|
||||
"peerDependencies": {
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./src/index.ts",
|
||||
"require": "./src/index.ts",
|
||||
"types": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
}
|
||||
}
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -35,12 +35,6 @@ export const connect: Connect = async function connect(
|
||||
try {
|
||||
this.connection = (await mongoose.connect(urlToConnect, connectionOptions)).connection
|
||||
|
||||
// If we are running a replica set with MongoDB Memory Server,
|
||||
// wait until the replica set elects a primary before proceeding
|
||||
if (this.mongoMemoryServer) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000))
|
||||
}
|
||||
|
||||
const client = this.connection.getClient()
|
||||
|
||||
if (!client.options.replicaSet) {
|
||||
|
||||
@@ -26,7 +26,6 @@ export const updateOne: UpdateOne = async function updateOne(
|
||||
})
|
||||
|
||||
let result
|
||||
|
||||
try {
|
||||
result = await Model.findOneAndUpdate(query, data, options)
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "3.0.0-alpha.49",
|
||||
"version": "3.0.0-alpha.48",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
@@ -11,8 +11,8 @@
|
||||
"name": "Payload",
|
||||
"url": "https://payloadcms.com"
|
||||
},
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/types.ts",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "pnpm build:swc && pnpm build:types",
|
||||
"build:swc": "swc ./src -d ./dist --config-file .swcrc",
|
||||
@@ -39,33 +39,10 @@
|
||||
"peerDependencies": {
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./src/index.ts",
|
||||
"require": "./src/index.ts",
|
||||
"types": "./src/index.ts"
|
||||
},
|
||||
"./types": {
|
||||
"import": "./src/types.ts",
|
||||
"require": "./src/types.ts",
|
||||
"types": "./src/types.ts"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"./types": {
|
||||
"import": "./dist/types.js",
|
||||
"require": "./dist/types.js",
|
||||
"types": "./dist/types.d.ts"
|
||||
}
|
||||
}
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,68 +1,47 @@
|
||||
import type { DeleteOne } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
|
||||
import { eq } from 'drizzle-orm'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { buildFindManyArgs } from './find/buildFindManyArgs.js'
|
||||
import buildQuery from './queries/buildQuery.js'
|
||||
import { selectDistinct } from './queries/selectDistinct.js'
|
||||
import { transform } from './transform/read/index.js'
|
||||
|
||||
export const deleteOne: DeleteOne = async function deleteOne(
|
||||
this: PostgresAdapter,
|
||||
{ collection: collectionSlug, req = {} as PayloadRequest, where: whereArg },
|
||||
{ collection, req = {} as PayloadRequest, where: incomingWhere },
|
||||
) {
|
||||
const db = this.sessions[req.transactionID]?.db || this.drizzle
|
||||
const collection = this.payload.collections[collectionSlug].config
|
||||
const tableName = toSnakeCase(collectionSlug)
|
||||
let docToDelete: Record<string, unknown>
|
||||
const collectionConfig = this.payload.collections[collection].config
|
||||
const tableName = toSnakeCase(collection)
|
||||
|
||||
const { joinAliases, joins, selectFields, where } = await buildQuery({
|
||||
const { where } = await buildQuery({
|
||||
adapter: this,
|
||||
fields: collection.fields,
|
||||
locale: req.locale,
|
||||
fields: collectionConfig.fields,
|
||||
tableName,
|
||||
where: whereArg,
|
||||
where: incomingWhere,
|
||||
})
|
||||
|
||||
const selectDistinctResult = await selectDistinct({
|
||||
const findManyArgs = buildFindManyArgs({
|
||||
adapter: this,
|
||||
chainedMethods: [{ args: [1], method: 'limit' }],
|
||||
db,
|
||||
joinAliases,
|
||||
joins,
|
||||
selectFields,
|
||||
depth: 0,
|
||||
fields: collectionConfig.fields,
|
||||
tableName,
|
||||
where,
|
||||
})
|
||||
|
||||
if (selectDistinctResult?.[0]?.id) {
|
||||
docToDelete = await db.query[tableName].findFirst({
|
||||
where: eq(this.tables[tableName].id, selectDistinctResult[0].id),
|
||||
})
|
||||
} else {
|
||||
const findManyArgs = buildFindManyArgs({
|
||||
adapter: this,
|
||||
depth: 0,
|
||||
fields: collection.fields,
|
||||
tableName,
|
||||
})
|
||||
findManyArgs.where = where
|
||||
|
||||
findManyArgs.where = where
|
||||
|
||||
docToDelete = await db.query[tableName].findFirst(findManyArgs)
|
||||
}
|
||||
const docToDelete = await db.query[tableName].findFirst(findManyArgs)
|
||||
|
||||
const result = transform({
|
||||
config: this.payload.config,
|
||||
data: docToDelete,
|
||||
fields: collection.fields,
|
||||
fields: collectionConfig.fields,
|
||||
})
|
||||
|
||||
await db.delete(this.tables[tableName]).where(eq(this.tables[tableName].id, docToDelete.id))
|
||||
await db.delete(this.tables[tableName]).where(where)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import type { QueryPromise } from 'drizzle-orm'
|
||||
|
||||
export type ChainedMethods = {
|
||||
args: unknown[]
|
||||
method: string
|
||||
@@ -10,7 +8,7 @@ export type ChainedMethods = {
|
||||
* @param methods
|
||||
* @param query
|
||||
*/
|
||||
const chainMethods = <T>({ methods, query }): QueryPromise<T> => {
|
||||
const chainMethods = ({ methods, query }): Promise<unknown> => {
|
||||
return methods.reduce((query, { args, method }) => {
|
||||
return query[method](...args)
|
||||
}, query)
|
||||
|
||||
@@ -7,7 +7,6 @@ import type { PostgresAdapter } from '../types.js'
|
||||
import type { ChainedMethods } from './chainMethods.js'
|
||||
|
||||
import buildQuery from '../queries/buildQuery.js'
|
||||
import { selectDistinct } from '../queries/selectDistinct.js'
|
||||
import { transform } from '../transform/read/index.js'
|
||||
import { buildFindManyArgs } from './buildFindManyArgs.js'
|
||||
import { chainMethods } from './chainMethods.js'
|
||||
@@ -40,6 +39,7 @@ export const findMany = async function find({
|
||||
let hasPrevPage: boolean
|
||||
let hasNextPage: boolean
|
||||
let pagingCounter: number
|
||||
let selectDistinctResult
|
||||
|
||||
const { joinAliases, joins, orderBy, selectFields, where } = await buildQuery({
|
||||
adapter,
|
||||
@@ -69,21 +69,36 @@ export const findMany = async function find({
|
||||
tableName,
|
||||
})
|
||||
|
||||
selectDistinctMethods.push({ args: [skip || (page - 1) * limit], method: 'offset' })
|
||||
selectDistinctMethods.push({ args: [limit === 0 ? undefined : limit], method: 'limit' })
|
||||
// only fetch IDs when a sort or where query is used that needs to be done on join tables, otherwise these can be done directly on the table in findMany
|
||||
if (Object.keys(joins).length > 0 || joinAliases.length > 0) {
|
||||
if (where) {
|
||||
selectDistinctMethods.push({ args: [where], method: 'where' })
|
||||
}
|
||||
|
||||
const selectDistinctResult = await selectDistinct({
|
||||
adapter,
|
||||
chainedMethods: selectDistinctMethods,
|
||||
db,
|
||||
joinAliases,
|
||||
joins,
|
||||
selectFields,
|
||||
tableName,
|
||||
where,
|
||||
})
|
||||
joinAliases.forEach(({ condition, table }) => {
|
||||
selectDistinctMethods.push({
|
||||
args: [table, condition],
|
||||
method: 'leftJoin',
|
||||
})
|
||||
})
|
||||
|
||||
Object.entries(joins).forEach(([joinTable, condition]) => {
|
||||
if (joinTable) {
|
||||
selectDistinctMethods.push({
|
||||
args: [adapter.tables[joinTable], condition],
|
||||
method: 'leftJoin',
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
selectDistinctMethods.push({ args: [skip || (page - 1) * limit], method: 'offset' })
|
||||
selectDistinctMethods.push({ args: [limit === 0 ? undefined : limit], method: 'limit' })
|
||||
|
||||
selectDistinctResult = await chainMethods({
|
||||
methods: selectDistinctMethods,
|
||||
query: db.selectDistinct(selectFields).from(table),
|
||||
})
|
||||
|
||||
if (selectDistinctResult) {
|
||||
if (selectDistinctResult.length === 0) {
|
||||
return {
|
||||
docs: [],
|
||||
@@ -97,14 +112,13 @@ export const findMany = async function find({
|
||||
totalDocs: 0,
|
||||
totalPages: 0,
|
||||
}
|
||||
} else {
|
||||
// set the id in an object for sorting later
|
||||
selectDistinctResult.forEach(({ id }, i) => {
|
||||
orderedIDMap[id] = i
|
||||
})
|
||||
orderedIDs = Object.keys(orderedIDMap)
|
||||
findManyArgs.where = inArray(adapter.tables[tableName].id, orderedIDs)
|
||||
}
|
||||
// set the id in an object for sorting later
|
||||
selectDistinctResult.forEach(({ id }, i) => {
|
||||
orderedIDMap[id as number | string] = i
|
||||
})
|
||||
orderedIDs = Object.keys(orderedIDMap)
|
||||
findManyArgs.where = inArray(adapter.tables[tableName].id, orderedIDs)
|
||||
} else {
|
||||
findManyArgs.limit = limitArg === 0 ? undefined : limitArg
|
||||
|
||||
|
||||
@@ -85,10 +85,6 @@ export const sanitizeQueryValue = ({
|
||||
}
|
||||
}
|
||||
|
||||
if ('hasMany' in field && field.hasMany && operator === 'contains') {
|
||||
operator = 'equals'
|
||||
}
|
||||
|
||||
if (operator === 'near' || operator === 'within' || operator === 'intersects') {
|
||||
throw new APIError(
|
||||
`Querying with '${operator}' is not supported with the postgres database adapter.`,
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import type { QueryPromise, SQL } from 'drizzle-orm'
|
||||
|
||||
import type { ChainedMethods } from '../find/chainMethods.js'
|
||||
import type { DrizzleDB, PostgresAdapter } from '../types.js'
|
||||
import type { BuildQueryJoinAliases, BuildQueryJoins } from './buildQuery.js'
|
||||
|
||||
import { chainMethods } from '../find/chainMethods.js'
|
||||
import { type GenericColumn } from '../types.js'
|
||||
|
||||
type Args = {
|
||||
adapter: PostgresAdapter
|
||||
chainedMethods?: ChainedMethods
|
||||
db: DrizzleDB
|
||||
joinAliases: BuildQueryJoinAliases
|
||||
joins: BuildQueryJoins
|
||||
selectFields: Record<string, GenericColumn>
|
||||
tableName: string
|
||||
where: SQL
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects distinct records from a table only if there are joins that need to be used, otherwise return null
|
||||
*/
|
||||
export const selectDistinct = ({
|
||||
adapter,
|
||||
chainedMethods = [],
|
||||
db,
|
||||
joinAliases,
|
||||
joins,
|
||||
selectFields,
|
||||
tableName,
|
||||
where,
|
||||
}: Args): QueryPromise<Record<string, GenericColumn> & { id: number | string }[]> => {
|
||||
if (Object.keys(joins).length > 0 || joinAliases.length > 0) {
|
||||
if (where) {
|
||||
chainedMethods.push({ args: [where], method: 'where' })
|
||||
}
|
||||
|
||||
joinAliases.forEach(({ condition, table }) => {
|
||||
chainedMethods.push({
|
||||
args: [table, condition],
|
||||
method: 'leftJoin',
|
||||
})
|
||||
})
|
||||
|
||||
Object.entries(joins).forEach(([joinTable, condition]) => {
|
||||
if (joinTable) {
|
||||
chainedMethods.push({
|
||||
args: [adapter.tables[joinTable], condition],
|
||||
method: 'leftJoin',
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return chainMethods({
|
||||
methods: chainedMethods,
|
||||
query: db.selectDistinct(selectFields).from(adapter.tables[tableName]),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,11 @@ import type { UpdateOne } from 'payload/database'
|
||||
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { ChainedMethods } from './find/chainMethods.js'
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { chainMethods } from './find/chainMethods.js'
|
||||
import buildQuery from './queries/buildQuery.js'
|
||||
import { selectDistinct } from './queries/selectDistinct.js'
|
||||
import { upsertRow } from './upsertRow/index.js'
|
||||
|
||||
export const updateOne: UpdateOne = async function updateOne(
|
||||
@@ -16,7 +17,6 @@ export const updateOne: UpdateOne = async function updateOne(
|
||||
const collection = this.payload.collections[collectionSlug].config
|
||||
const tableName = toSnakeCase(collectionSlug)
|
||||
const whereToUse = whereArg || { id: { equals: id } }
|
||||
let idToUpdate = id
|
||||
|
||||
const { joinAliases, joins, selectFields, where } = await buildQuery({
|
||||
adapter: this,
|
||||
@@ -26,19 +26,42 @@ export const updateOne: UpdateOne = async function updateOne(
|
||||
where: whereToUse,
|
||||
})
|
||||
|
||||
const selectDistinctResult = await selectDistinct({
|
||||
adapter: this,
|
||||
chainedMethods: [{ args: [1], method: 'limit' }],
|
||||
db,
|
||||
joinAliases,
|
||||
joins,
|
||||
selectFields,
|
||||
tableName,
|
||||
where,
|
||||
})
|
||||
let idToUpdate = id
|
||||
|
||||
if (selectDistinctResult?.[0]?.id) {
|
||||
idToUpdate = selectDistinctResult?.[0]?.id
|
||||
// only fetch IDs when a sort or where query is used that needs to be done on join tables, otherwise these can be done directly on the table in findMany
|
||||
if (Object.keys(joins).length > 0 || joinAliases.length > 0) {
|
||||
const selectDistinctMethods: ChainedMethods = []
|
||||
|
||||
if (where) {
|
||||
selectDistinctMethods.push({ args: [where], method: 'where' })
|
||||
}
|
||||
|
||||
joinAliases.forEach(({ condition, table }) => {
|
||||
selectDistinctMethods.push({
|
||||
args: [table, condition],
|
||||
method: 'leftJoin',
|
||||
})
|
||||
})
|
||||
|
||||
Object.entries(joins).forEach(([joinTable, condition]) => {
|
||||
if (joinTable) {
|
||||
selectDistinctMethods.push({
|
||||
args: [this.tables[joinTable], condition],
|
||||
method: 'leftJoin',
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
selectDistinctMethods.push({ args: [1], method: 'limit' })
|
||||
|
||||
const selectDistinctResult = await chainMethods({
|
||||
methods: selectDistinctMethods,
|
||||
query: db.selectDistinct(selectFields).from(this.tables[tableName]),
|
||||
})
|
||||
|
||||
if (selectDistinctResult?.[0]?.id) {
|
||||
idToUpdate = selectDistinctResult?.[0]?.id
|
||||
}
|
||||
}
|
||||
|
||||
const result = await upsertRow({
|
||||
|
||||
@@ -22,7 +22,6 @@ const baseRules = {
|
||||
},
|
||||
},
|
||||
],
|
||||
'payload/no-jsx-import-statements': 'error',
|
||||
}
|
||||
|
||||
const reactRules = {
|
||||
@@ -113,7 +112,7 @@ module.exports = {
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
plugins: ['@typescript-eslint', 'payload'],
|
||||
plugins: ['@typescript-eslint'],
|
||||
extends: [
|
||||
...baseExtends,
|
||||
'plugin:@typescript-eslint/recommended-type-checked',
|
||||
@@ -127,7 +126,7 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
files: ['**/*.tsx'],
|
||||
plugins: ['@typescript-eslint', 'payload'],
|
||||
plugins: ['@typescript-eslint'],
|
||||
extends: [
|
||||
...baseExtends,
|
||||
'plugin:@typescript-eslint/recommended-type-checked',
|
||||
@@ -145,7 +144,7 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
files: ['**/*.spec.ts'],
|
||||
plugins: ['@typescript-eslint', 'payload'],
|
||||
plugins: ['@typescript-eslint'],
|
||||
extends: [
|
||||
...baseExtends,
|
||||
'plugin:@typescript-eslint/recommended-type-checked',
|
||||
@@ -160,7 +159,6 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
{
|
||||
plugins: ['payload'],
|
||||
files: ['*.config.ts'],
|
||||
rules: {
|
||||
...baseRules,
|
||||
@@ -169,7 +167,6 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
{
|
||||
plugins: ['payload'],
|
||||
files: ['config.ts'],
|
||||
rules: {
|
||||
...baseRules,
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/eslint": "8.56.6",
|
||||
"@typescript-eslint/eslint-plugin": "7.3.1",
|
||||
"@typescript-eslint/parser": "7.3.1",
|
||||
"@types/eslint": "8.56.5",
|
||||
"@typescript-eslint/eslint-plugin": "7.2.0",
|
||||
"@typescript-eslint/parser": "7.2.0",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-plugin-jest": "27.9.0",
|
||||
@@ -23,10 +23,10 @@
|
||||
"eslint-plugin-jsx-a11y": "6.8.0",
|
||||
"eslint-plugin-node": "11.1.0",
|
||||
"eslint-plugin-perfectionist": "2.7.0",
|
||||
"eslint-plugin-react": "7.34.1",
|
||||
"eslint-plugin-playwright": "1.5.2",
|
||||
"eslint-plugin-react": "7.34.0",
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"eslint-plugin-regexp": "2.3.0",
|
||||
"eslint-plugin-payload": "workspace:*"
|
||||
"eslint-plugin-regexp": "2.3.0"
|
||||
},
|
||||
"keywords": []
|
||||
}
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description:
|
||||
'Disallow non-retryable assertions in Playwright E2E tests unless they are wrapped in an expect.poll() or expect().toPass()',
|
||||
category: 'Best Practices',
|
||||
recommended: true,
|
||||
},
|
||||
schema: [],
|
||||
},
|
||||
create: function (context) {
|
||||
const nonRetryableAssertions = [
|
||||
'toBe',
|
||||
'toBeCloseTo',
|
||||
'toBeDefined',
|
||||
'toBeFalsy',
|
||||
'toBeGreaterThan',
|
||||
'toBeGreaterThanOrEqual',
|
||||
'toBeInstanceOf',
|
||||
'toBeLessThan',
|
||||
'toBeLessThanOrEqual',
|
||||
'toBeNaN',
|
||||
'toBeNull',
|
||||
'toBeTruthy',
|
||||
'toBeUndefined',
|
||||
'toContain',
|
||||
'toContainEqual',
|
||||
'toEqual',
|
||||
'toHaveLength',
|
||||
'toHaveProperty',
|
||||
'toMatch',
|
||||
'toMatchObject',
|
||||
'toStrictEqual',
|
||||
'toThrow',
|
||||
'any',
|
||||
'anything',
|
||||
'arrayContaining',
|
||||
'closeTo',
|
||||
'objectContaining',
|
||||
'stringContaining',
|
||||
'stringMatching',
|
||||
]
|
||||
|
||||
function isNonRetryableAssertion(node) {
|
||||
return (
|
||||
node.type === 'MemberExpression' &&
|
||||
node.property.type === 'Identifier' &&
|
||||
nonRetryableAssertions.includes(node.property.name)
|
||||
)
|
||||
}
|
||||
|
||||
function isExpectPollOrToPass(node) {
|
||||
if (
|
||||
node.type === 'MemberExpression' &&
|
||||
(node?.property?.name === 'poll' || node?.property?.name === 'toPass')
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
return (
|
||||
node.type === 'CallExpression' &&
|
||||
node.callee.type === 'MemberExpression' &&
|
||||
((node.callee.object.type === 'CallExpression' &&
|
||||
node.callee.object.callee.type === 'MemberExpression' &&
|
||||
node.callee.object.callee.property.name === 'poll') ||
|
||||
node.callee.property.name === 'toPass')
|
||||
)
|
||||
}
|
||||
|
||||
function hasExpectPollOrToPassInChain(node) {
|
||||
let ancestor = node
|
||||
|
||||
while (ancestor) {
|
||||
if (isExpectPollOrToPass(ancestor)) {
|
||||
return true
|
||||
}
|
||||
ancestor = 'object' in ancestor ? ancestor.object : ancestor.callee
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function hasExpectPollOrToPassInParentChain(node) {
|
||||
let ancestor = node
|
||||
|
||||
while (ancestor) {
|
||||
if (isExpectPollOrToPass(ancestor)) {
|
||||
return true
|
||||
}
|
||||
ancestor = ancestor.parent
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return {
|
||||
CallExpression(node) {
|
||||
// node.callee is MemberExpressiom
|
||||
if (isNonRetryableAssertion(node.callee)) {
|
||||
if (hasExpectPollOrToPassInChain(node.callee)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (hasExpectPollOrToPassInParentChain(node)) {
|
||||
return
|
||||
}
|
||||
|
||||
context.report({
|
||||
node: node.callee.property,
|
||||
message:
|
||||
'Non-retryable, flaky assertion used in Playwright test: "{{ assertion }}". Those need to be wrapped in expect.poll() or expect().toPass().',
|
||||
data: {
|
||||
assertion: node.callee.property.name,
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'Disallow imports from .jsx extensions',
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [],
|
||||
},
|
||||
create: function (context) {
|
||||
return {
|
||||
ImportDeclaration(node) {
|
||||
const importPath = node.source.value
|
||||
|
||||
if (!importPath.endsWith('.jsx')) return
|
||||
|
||||
context.report({
|
||||
node: node.source,
|
||||
message: 'JSX imports are invalid. Use .js instead.',
|
||||
fix: (fixer) => {
|
||||
return fixer.removeRange([node.source.range[1] - 2, node.source.range[1] - 1])
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'Disallow non-retryable assertions in Playwright E2E tests',
|
||||
category: 'Best Practices',
|
||||
recommended: true,
|
||||
},
|
||||
schema: [],
|
||||
},
|
||||
create: function (context) {
|
||||
const nonRetryableAssertions = [
|
||||
'toBe',
|
||||
'toBeCloseTo',
|
||||
'toBeDefined',
|
||||
'toBeFalsy',
|
||||
'toBeGreaterThan',
|
||||
'toBeGreaterThanOrEqual',
|
||||
'toBeInstanceOf',
|
||||
'toBeLessThan',
|
||||
'toBeLessThanOrEqual',
|
||||
'toBeNaN',
|
||||
'toBeNull',
|
||||
'toBeTruthy',
|
||||
'toBeUndefined',
|
||||
'toContain',
|
||||
'toContainEqual',
|
||||
'toEqual',
|
||||
'toHaveLength',
|
||||
'toHaveProperty',
|
||||
'toMatch',
|
||||
'toMatchObject',
|
||||
'toStrictEqual',
|
||||
'toThrow',
|
||||
'any',
|
||||
'anything',
|
||||
'arrayContaining',
|
||||
'closeTo',
|
||||
'objectContaining',
|
||||
'stringContaining',
|
||||
'stringMatching',
|
||||
]
|
||||
|
||||
return {
|
||||
CallExpression(node) {
|
||||
if (
|
||||
node.callee.type === 'MemberExpression' &&
|
||||
//node.callee.object.name === 'expect' &&
|
||||
node.callee.property.type === 'Identifier' &&
|
||||
nonRetryableAssertions.includes(node.callee.property.name)
|
||||
) {
|
||||
context.report({
|
||||
node: node.callee.property,
|
||||
message:
|
||||
'Non-retryable, flaky assertion used in Playwright test: "{{ assertion }}". Those need to be wrapped in expect.poll or expect.toPass.',
|
||||
data: {
|
||||
assertion: node.callee.property.name,
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/** @type {import('eslint').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'Disallow imports from relative monorepo packages/*/src',
|
||||
category: 'Best Practices',
|
||||
recommended: true,
|
||||
},
|
||||
schema: [],
|
||||
},
|
||||
create: function (context) {
|
||||
return {
|
||||
ImportDeclaration(node) {
|
||||
const importPath = node.source.value
|
||||
|
||||
// Match imports starting with any number of "../" followed by "packages/"
|
||||
const regex = /^(\.\.\/)*packages\/[^/]+\/src/
|
||||
|
||||
if (regex.test(importPath)) {
|
||||
context.report({
|
||||
node: node.source,
|
||||
message: 'Import from relative "packages/*/src" is not allowed',
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/** @type {import('eslint').Linter.Config} */
|
||||
module.exports = {
|
||||
rules: {
|
||||
'no-jsx-import-statements': require('./customRules/no-jsx-import-statements'),
|
||||
'no-non-retryable-assertions': require('./customRules/no-non-retryable-assertions'),
|
||||
'no-relative-monorepo-imports': require('./customRules/no-relative-monorepo-imports'),
|
||||
'no-flaky-assertions': require('./customRules/no-flaky-assertions'),
|
||||
'no-wait-function': {
|
||||
create: function (context) {
|
||||
return {
|
||||
CallExpression(node) {
|
||||
// Check if the function being called is named "wait"
|
||||
if (node.callee.name === 'wait') {
|
||||
context.report({
|
||||
node,
|
||||
message:
|
||||
'Usage of "wait" function is discouraged as it\'s flaky. Proper assertions should be used instead.',
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"name": "eslint-plugin-payload",
|
||||
"version": "1.0.0",
|
||||
"description": "Payload plugins for ESLint",
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
"email": "info@payloadcms.com",
|
||||
"name": "Payload",
|
||||
"url": "https://payloadcms.com"
|
||||
},
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/eslint": "8.56.6",
|
||||
"@typescript-eslint/eslint-plugin": "7.3.1",
|
||||
"@typescript-eslint/parser": "7.3.1",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-plugin-jest": "27.9.0",
|
||||
"eslint-plugin-jest-dom": "5.1.0",
|
||||
"eslint-plugin-jsx-a11y": "6.8.0",
|
||||
"eslint-plugin-node": "11.1.0",
|
||||
"eslint-plugin-perfectionist": "2.7.0",
|
||||
"eslint-plugin-react": "7.34.1",
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"eslint-plugin-regexp": "2.3.0"
|
||||
},
|
||||
"keywords": []
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/graphql",
|
||||
"version": "3.0.0-alpha.49",
|
||||
"version": "3.0.0-alpha.48",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.d.ts",
|
||||
"type": "module",
|
||||
@@ -45,7 +45,8 @@
|
||||
"require": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
}
|
||||
}
|
||||
},
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import type { GeneratedTypes } from 'payload'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
import type { Collection } from 'payload/types'
|
||||
|
||||
import { duplicateOperation } from 'payload/operations'
|
||||
import { isolateObjectProperty } from 'payload/utilities'
|
||||
|
||||
import type { Context } from '../types.js'
|
||||
|
||||
export type Resolver<T> = (
|
||||
_: unknown,
|
||||
args: {
|
||||
draft: boolean
|
||||
fallbackLocale?: string
|
||||
id: string
|
||||
locale?: string
|
||||
},
|
||||
context: {
|
||||
req: PayloadRequest
|
||||
},
|
||||
) => Promise<T>
|
||||
|
||||
export default function duplicateResolver<T extends keyof GeneratedTypes['collections']>(
|
||||
collection: Collection,
|
||||
): Resolver<GeneratedTypes['collections'][T]> {
|
||||
return async function resolver(_, args, context: Context) {
|
||||
const { req } = context
|
||||
const locale = req.locale
|
||||
const fallbackLocale = req.fallbackLocale
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
|
||||
const options = {
|
||||
id: args.id,
|
||||
collection,
|
||||
depth: 0,
|
||||
draft: args.draft,
|
||||
req: isolateObjectProperty(req, 'transactionID'),
|
||||
}
|
||||
|
||||
const result = await duplicateOperation(options)
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@ import verifyEmail from '../resolvers/auth/verifyEmail.js'
|
||||
import createResolver from '../resolvers/collections/create.js'
|
||||
import getDeleteResolver from '../resolvers/collections/delete.js'
|
||||
import { docAccessResolver } from '../resolvers/collections/docAccess.js'
|
||||
import duplicateResolver from '../resolvers/collections/duplicate.js'
|
||||
import findResolver from '../resolvers/collections/find.js'
|
||||
import findByIDResolver from '../resolvers/collections/findByID.js'
|
||||
import findVersionByIDResolver from '../resolvers/collections/findVersionByID.js'
|
||||
@@ -238,14 +237,6 @@ function initCollectionsGraphQL({ config, graphqlResult }: InitCollectionsGraphQ
|
||||
resolve: getDeleteResolver(collection),
|
||||
}
|
||||
|
||||
graphqlResult.Mutation.fields[`duplicate${singularName}`] = {
|
||||
type: collection.graphQL.type,
|
||||
args: {
|
||||
id: { type: new GraphQLNonNull(idType) },
|
||||
},
|
||||
resolve: duplicateResolver(collection),
|
||||
}
|
||||
|
||||
if (collectionConfig.versions) {
|
||||
const versionIDType = config.db.defaultIDType === 'text' ? GraphQLString : GraphQLInt
|
||||
const versionCollectionFields: Field[] = [
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./src/index.ts",
|
||||
"default": "./src/index.ts"
|
||||
"default": "./src/index.ts",
|
||||
"types": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./src/index.ts",
|
||||
"default": "./src/index.ts"
|
||||
"default": "./src/index.ts",
|
||||
"types": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
|
||||
8
packages/live-preview/src/handleMessage.d.ts
vendored
8
packages/live-preview/src/handleMessage.d.ts
vendored
@@ -1,8 +0,0 @@
|
||||
export declare const handleMessage: <T>(args: {
|
||||
apiRoute?: string
|
||||
depth?: number
|
||||
event: MessageEvent
|
||||
initialData: T
|
||||
serverURL: string
|
||||
}) => Promise<T>
|
||||
//# sourceMappingURL=handleMessage.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"handleMessage.d.ts","sourceRoot":"","sources":["handleMessage.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,aAAa;eACb,MAAM;YACT,MAAM;WACP,YAAY;;eAER,MAAM;gBAyClB,CAAA"}
|
||||
6
packages/live-preview/src/index.d.ts
vendored
6
packages/live-preview/src/index.d.ts
vendored
@@ -1,6 +0,0 @@
|
||||
export { handleMessage } from './handleMessage.js'
|
||||
export { mergeData } from './mergeData.js'
|
||||
export { ready } from './ready.js'
|
||||
export { subscribe } from './subscribe.js'
|
||||
export { unsubscribe } from './unsubscribe.js'
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA"}
|
||||
26
packages/live-preview/src/mergeData.d.ts
vendored
26
packages/live-preview/src/mergeData.d.ts
vendored
@@ -1,26 +0,0 @@
|
||||
import type { fieldSchemaToJSON } from 'payload/utilities'
|
||||
import type { UpdatedDocument } from './types.js'
|
||||
export declare const mergeData: <T>(args: {
|
||||
apiRoute?: string
|
||||
collectionPopulationRequestHandler?: ({
|
||||
apiPath,
|
||||
endpoint,
|
||||
serverURL,
|
||||
}: {
|
||||
apiPath: string
|
||||
endpoint: string
|
||||
serverURL: string
|
||||
}) => Promise<Response>
|
||||
depth?: number
|
||||
externallyUpdatedRelationship?: UpdatedDocument
|
||||
fieldSchema: ReturnType<typeof fieldSchemaToJSON>
|
||||
incomingData: Partial<T>
|
||||
initialData: T
|
||||
returnNumberOfRequests?: boolean
|
||||
serverURL: string
|
||||
}) => Promise<
|
||||
T & {
|
||||
_numberOfRequests?: number
|
||||
}
|
||||
>
|
||||
//# sourceMappingURL=mergeData.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"mergeData.d.ts","sourceRoot":"","sources":["mergeData.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAE1D,OAAO,KAAK,EAA2B,eAAe,EAAE,MAAM,YAAY,CAAA;AAc1E,eAAO,MAAM,SAAS;eACT,MAAM;;iBAMN,MAAM;kBACL,MAAM;mBACL,MAAM;UACb,QAAQ,QAAQ,CAAC;YACf,MAAM;oCACkB,eAAe;iBAClC,WAAW,wBAAwB,CAAC;;;6BAGxB,OAAO;eACrB,MAAM;;wBAGK,MAAM;EA6D7B,CAAA"}
|
||||
8
packages/live-preview/src/subscribe.d.ts
vendored
8
packages/live-preview/src/subscribe.d.ts
vendored
@@ -1,8 +0,0 @@
|
||||
export declare const subscribe: <T>(args: {
|
||||
apiRoute?: string
|
||||
callback: (data: T) => void
|
||||
depth?: number
|
||||
initialData: T
|
||||
serverURL: string
|
||||
}) => (event: MessageEvent) => void
|
||||
//# sourceMappingURL=subscribe.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"subscribe.d.ts","sourceRoot":"","sources":["subscribe.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,SAAS;eACT,MAAM;2BACM,IAAI;YACnB,MAAM;;eAEH,MAAM;cACN,YAAY,KAAK,IAa7B,CAAA"}
|
||||
10
packages/live-preview/src/traverseFields.d.ts
vendored
10
packages/live-preview/src/traverseFields.d.ts
vendored
@@ -1,10 +0,0 @@
|
||||
import type { fieldSchemaToJSON } from 'payload/utilities'
|
||||
import type { PopulationsByCollection, UpdatedDocument } from './types.js'
|
||||
export declare const traverseFields: <T>(args: {
|
||||
externallyUpdatedRelationship?: UpdatedDocument
|
||||
fieldSchema: ReturnType<typeof fieldSchemaToJSON>
|
||||
incomingData: T
|
||||
populationsByCollection: PopulationsByCollection
|
||||
result: T
|
||||
}) => void
|
||||
//# sourceMappingURL=traverseFields.d.ts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"traverseFields.d.ts","sourceRoot":"","sources":["traverseFields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAE1D,OAAO,KAAK,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAI1E,eAAO,MAAM,cAAc;oCACO,eAAe;iBAClC,WAAW,wBAAwB,CAAC;;6BAExB,uBAAuB;;MAE9C,IA4QH,CAAA"}
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "@payloadcms/next",
|
||||
"version": "3.0.0-alpha.49",
|
||||
"main": "./src/index.js",
|
||||
"types": "./src/index.js",
|
||||
"version": "3.0.0-alpha.48",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.d.ts",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"@payloadcms/next": "./dist/bin/index.js"
|
||||
@@ -13,21 +13,36 @@
|
||||
"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/",
|
||||
"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",
|
||||
"fix": "eslint \"src/**/*.{ts,tsx}\" --fix",
|
||||
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
||||
"prepublishOnly": "pnpm clean && pnpm turbo build"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./src/index.js",
|
||||
"require": "./src/index.js",
|
||||
"types": "./src/index.js"
|
||||
},
|
||||
"./*": {
|
||||
"import": "./src/exports/*.ts",
|
||||
"require": "./src/exports/*.ts",
|
||||
"types": "./src/exports/*.ts"
|
||||
"import": "./dist/exports/*.js",
|
||||
"require": "./dist/exports/*.js",
|
||||
"types": "./dist/exports/*.ts"
|
||||
},
|
||||
"./utilities/*": {
|
||||
"import": "./dist/utilities/*.js",
|
||||
"require": "./dist/utilities/*.js",
|
||||
"types": "./src/utilities/*.ts"
|
||||
},
|
||||
"./layouts/*": {
|
||||
"import": "./dist/layouts/*.js",
|
||||
"require": "./dist/layouts/*.js",
|
||||
"types": "./src/layouts/*.ts"
|
||||
},
|
||||
"./views/*": {
|
||||
"import": "./dist/views/*.js",
|
||||
"require": "./dist/views/*.js",
|
||||
"types": "./src/views/*.ts"
|
||||
},
|
||||
"./routes": {
|
||||
"import": "./dist/routes/index.js",
|
||||
"require": "./dist/routes/index.js"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -73,22 +88,48 @@
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.js",
|
||||
"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",
|
||||
"types": "./dist/withPayload.d.ts"
|
||||
},
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.js"
|
||||
"require": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"./*": {
|
||||
"import": "./dist/exports/*.js",
|
||||
"require": "./dist/exports/*.js",
|
||||
"types": "./dist/exports/*.d.ts"
|
||||
"./utilities/*": {
|
||||
"import": "./dist/utilities/*.js",
|
||||
"require": "./dist/utilities/*.js",
|
||||
"types": "./dist/utilities/*.d.ts"
|
||||
},
|
||||
"./layouts/*": {
|
||||
"import": "./dist/layouts/*.js",
|
||||
"require": "./dist/layouts/*.js",
|
||||
"types": "./dist/layouts/*.d.ts"
|
||||
},
|
||||
"./views/*": {
|
||||
"import": "./dist/views/*.js",
|
||||
"require": "./dist/views/*.js",
|
||||
"types": "./dist/views/*.d.ts"
|
||||
},
|
||||
"./app/*": {
|
||||
"import": "./dist/app/*.js",
|
||||
"require": "./dist/app/*.js",
|
||||
"types": "./dist/app/*.d.ts"
|
||||
},
|
||||
"./routes": {
|
||||
"import": "./dist/routes/index.js",
|
||||
"require": "./dist/routes/index.js",
|
||||
"types": "./dist/routes/index.d.ts"
|
||||
}
|
||||
},
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
|
||||
26
packages/next/src/bin/index.ts
Normal file
26
packages/next/src/bin/index.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import minimist from 'minimist'
|
||||
|
||||
const args = minimist(process.argv.slice(2))
|
||||
const scriptIndex = args._.findIndex((x) => x === 'install')
|
||||
const script = scriptIndex === -1 ? args._[0] : args._[scriptIndex]
|
||||
|
||||
const { debug } = args
|
||||
|
||||
import { install } from './install.js'
|
||||
|
||||
main()
|
||||
|
||||
async function main() {
|
||||
if (debug) console.log({ debug, pwd: process.cwd() })
|
||||
switch (script.toLowerCase()) {
|
||||
case 'install': {
|
||||
if (debug) console.log('Running install')
|
||||
await install({ debug })
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
console.log(`Unknown script "${script}".`)
|
||||
break
|
||||
}
|
||||
}
|
||||
68
packages/next/src/bin/install.ts
Normal file
68
packages/next/src/bin/install.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
export const install = (args?: { debug?: boolean }): Promise<void> => {
|
||||
const debug = args?.debug
|
||||
|
||||
const nextConfigPath = path.resolve(process.cwd(), 'next.config.js')
|
||||
if (!fs.existsSync(nextConfigPath)) {
|
||||
console.log(`No next.config.js found at ${nextConfigPath}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const apiTemplateDir = path.resolve(__dirname, '../..', 'dist', 'template/app/(payload)')
|
||||
const userProjectDir = process.cwd()
|
||||
|
||||
if (!fs.existsSync(apiTemplateDir)) {
|
||||
console.log(`Could not find template source files from ${apiTemplateDir}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (!fs.existsSync(path.resolve(userProjectDir, 'app'))) {
|
||||
console.log(`Could not find user app directory at ${userProjectDir}/app`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const templateFileDest = path.resolve(userProjectDir, 'app/(payload)')
|
||||
|
||||
if (debug) {
|
||||
console.log({
|
||||
cwd: process.cwd(),
|
||||
|
||||
// Paths
|
||||
apiTemplateDir,
|
||||
templateFileDest,
|
||||
userProjectDir,
|
||||
})
|
||||
}
|
||||
|
||||
// Merge api dir into user's app/api, user's files take precedence
|
||||
copyRecursiveSync(apiTemplateDir, templateFileDest, debug)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively copy files from src to dest
|
||||
*/
|
||||
function copyRecursiveSync(src: string, dest: string, debug?: boolean) {
|
||||
const exists = fs.existsSync(src)
|
||||
const stats = exists && fs.statSync(src)
|
||||
const isDirectory = exists && stats.isDirectory()
|
||||
if (isDirectory) {
|
||||
if (debug) console.log(`Dir: ${src}\n--Dest: ${dest}`)
|
||||
fs.mkdirSync(dest, { recursive: true })
|
||||
fs.readdirSync(src).forEach((childItemName) => {
|
||||
copyRecursiveSync(path.join(src, childItemName), path.join(dest, childItemName))
|
||||
})
|
||||
} else {
|
||||
if (debug) console.log(`File: ${src}\n--Dest: ${dest}`)
|
||||
fs.copyFileSync(src, dest)
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
install({ debug: true }).catch((e) => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
'use client'
|
||||
import { Button } from '@payloadcms/ui/elements/Button'
|
||||
import { Modal, useModal } from '@payloadcms/ui/elements/Modal'
|
||||
import { useFormModified } from '@payloadcms/ui/forms/Form'
|
||||
import { useAuth } from '@payloadcms/ui/providers/Auth'
|
||||
import { useTranslation } from '@payloadcms/ui/providers/Translation'
|
||||
import { Modal, useModal } from '@payloadcms/ui'
|
||||
import { Button } from '@payloadcms/ui/elements'
|
||||
import { useFormModified } from '@payloadcms/ui/forms'
|
||||
import { useAuth } from '@payloadcms/ui/providers'
|
||||
import { useTranslation } from '@payloadcms/ui/providers'
|
||||
import React, { useCallback, useEffect } from 'react'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
1
packages/next/src/exports/index.ts
Normal file
1
packages/next/src/exports/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as withPayload } from '../withPayload.js'
|
||||
@@ -1 +0,0 @@
|
||||
export { RootLayout, metadata } from '../layouts/Root/index.js'
|
||||
@@ -1,8 +0,0 @@
|
||||
export { GRAPHQL_PLAYGROUND_GET, GRAPHQL_POST } from '../routes/graphql/index.js'
|
||||
|
||||
export {
|
||||
DELETE as REST_DELETE,
|
||||
GET as REST_GET,
|
||||
PATCH as REST_PATCH,
|
||||
POST as REST_POST,
|
||||
} from '../routes/rest/index.js'
|
||||
@@ -1 +0,0 @@
|
||||
export { getPayloadHMR } from '../utilities/getPayloadHMR.js'
|
||||
@@ -1,3 +0,0 @@
|
||||
export { EditView } from '../views/Edit/index.js'
|
||||
export { NotFoundView } from '../views/NotFound/index.js'
|
||||
export { type GenerateViewMetadata, RootPage, generatePageMetadata } from '../views/Root/index.js'
|
||||
@@ -1 +0,0 @@
|
||||
export { default as withPayload } from './withPayload.js'
|
||||
@@ -1,10 +1,9 @@
|
||||
import type { SanitizedConfig } from 'payload/types'
|
||||
|
||||
import { translations } from '@payloadcms/translations/client'
|
||||
import { RootProvider } from '@payloadcms/ui/providers/Root'
|
||||
import { RootProvider, buildComponentMap } from '@payloadcms/ui'
|
||||
import '@payloadcms/ui/scss/app.scss'
|
||||
import { buildComponentMap } from '@payloadcms/ui/utilities/buildComponentMap'
|
||||
import { headers as getHeaders, cookies as nextCookies } from 'next/headers.js'
|
||||
import { headers as getHeaders } from 'next/headers.js'
|
||||
import { parseCookies } from 'payload/auth'
|
||||
import { createClientConfig } from 'payload/config'
|
||||
import { deepMerge } from 'payload/utilities'
|
||||
@@ -13,6 +12,7 @@ import 'react-toastify/dist/ReactToastify.css'
|
||||
|
||||
import { getRequestLanguage } from '../../utilities/getRequestLanguage.js'
|
||||
import { DefaultEditView } from '../../views/Edit/Default/index.js'
|
||||
import { DefaultCell } from '../../views/List/Default/Cell/index.js'
|
||||
import { DefaultListView } from '../../views/List/Default/index.js'
|
||||
|
||||
export const metadata = {
|
||||
@@ -37,7 +37,6 @@ export const RootLayout = async ({
|
||||
|
||||
const lang =
|
||||
getRequestLanguage({
|
||||
config,
|
||||
cookies,
|
||||
headers,
|
||||
}) ?? clientConfig.i18n.fallbackLanguage
|
||||
@@ -51,17 +50,8 @@ export const RootLayout = async ({
|
||||
value: language,
|
||||
}))
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
async function switchLanguageServerAction(lang: string): Promise<void> {
|
||||
'use server'
|
||||
nextCookies().set({
|
||||
name: `${config.cookiePrefix || 'payload'}-lng'`,
|
||||
path: '/',
|
||||
value: lang,
|
||||
})
|
||||
}
|
||||
|
||||
const { componentMap, wrappedChildren } = buildComponentMap({
|
||||
DefaultCell,
|
||||
DefaultEditView,
|
||||
DefaultListView,
|
||||
children,
|
||||
@@ -77,8 +67,6 @@ export const RootLayout = async ({
|
||||
fallbackLang={clientConfig.i18n.fallbackLanguage}
|
||||
lang={lang}
|
||||
languageOptions={languageOptions}
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
switchLanguageServerAction={switchLanguageServerAction}
|
||||
translations={mergedTranslations[lang]}
|
||||
>
|
||||
{wrappedChildren}
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
import type { Collection, PayloadRequest } from 'payload/types'
|
||||
|
||||
import httpStatus from 'http-status'
|
||||
import { APIError, ValidationError } from 'payload/errors'
|
||||
import { APIError } from 'payload/errors'
|
||||
|
||||
export type ErrorResponse = { data?: any; errors: unknown[]; stack?: string }
|
||||
|
||||
const formatErrors = (incoming: { [key: string]: unknown } | APIError): ErrorResponse => {
|
||||
const formatErrors = (incoming: { [key: string]: unknown } | APIError | Error): ErrorResponse => {
|
||||
if (incoming) {
|
||||
// Cannot use `instanceof` to check error type: https://github.com/microsoft/TypeScript/issues/13965
|
||||
// Instead, get the prototype of the incoming error and check its constructor name
|
||||
const proto = Object.getPrototypeOf(incoming)
|
||||
|
||||
// Payload 'ValidationError' and 'APIError'
|
||||
if (
|
||||
(proto.constructor.name === 'ValidationError' || proto.constructor.name === 'APIError') &&
|
||||
incoming.data
|
||||
) {
|
||||
if (incoming instanceof APIError && incoming.data) {
|
||||
return {
|
||||
errors: [
|
||||
{
|
||||
@@ -27,8 +19,8 @@ const formatErrors = (incoming: { [key: string]: unknown } | APIError): ErrorRes
|
||||
}
|
||||
}
|
||||
|
||||
// Mongoose 'ValidationError': https://mongoosejs.com/docs/api/error.html#Error.ValidationError
|
||||
if (proto.constructor.name === 'ValidationError' && 'errors' in incoming && incoming.errors) {
|
||||
// mongoose
|
||||
if (!(incoming instanceof APIError || incoming instanceof Error) && incoming.errors) {
|
||||
return {
|
||||
errors: Object.keys(incoming.errors).reduce((acc, key) => {
|
||||
acc.push({
|
||||
@@ -66,7 +58,7 @@ const formatErrors = (incoming: { [key: string]: unknown } | APIError): ErrorRes
|
||||
}
|
||||
}
|
||||
|
||||
export const routeError = async ({
|
||||
export const RouteError = async ({
|
||||
collection,
|
||||
err,
|
||||
req,
|
||||
@@ -86,9 +78,7 @@ export const routeError = async ({
|
||||
}
|
||||
|
||||
const { config, logger } = req.payload
|
||||
|
||||
let response = formatErrors(err)
|
||||
|
||||
let status = err.status || httpStatus.INTERNAL_SERVER_ERROR
|
||||
|
||||
logger.error(err.stack)
|
||||
@@ -1,14 +1,9 @@
|
||||
import type { BuildFormStateArgs } from '@payloadcms/ui/forms/buildStateFromSchema'
|
||||
import type { BuildFormStateArgs, FieldSchemaMap } from '@payloadcms/ui'
|
||||
import type { Field, PayloadRequest, SanitizedConfig } from 'payload/types'
|
||||
|
||||
import { buildStateFromSchema } from '@payloadcms/ui/forms/buildStateFromSchema'
|
||||
import { reduceFieldsToValues } from '@payloadcms/ui/utilities/reduceFieldsToValues'
|
||||
import { buildFieldSchemaMap, buildStateFromSchema, reduceFieldsToValues } from '@payloadcms/ui'
|
||||
import httpStatus from 'http-status'
|
||||
|
||||
import type { FieldSchemaMap } from '../../utilities/buildFieldSchemaMap/types.js'
|
||||
|
||||
import { buildFieldSchemaMap } from '../../utilities/buildFieldSchemaMap/index.js'
|
||||
|
||||
let cached = global._payload_fieldSchemaMap
|
||||
|
||||
if (!cached) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import httpStatus from 'http-status'
|
||||
import { createOperation } from 'payload/operations'
|
||||
import { isNumber } from 'payload/utilities'
|
||||
@@ -24,7 +23,7 @@ export const create: CollectionRouteHandler = async ({ collection, req }) => {
|
||||
{
|
||||
doc,
|
||||
message: req.t('general:successfullyCreated', {
|
||||
label: getTranslation(collection.config.labels.singular, req.i18n),
|
||||
label: collection.config.labels.singular,
|
||||
}),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import httpStatus from 'http-status'
|
||||
import { duplicateOperation } from 'payload/operations'
|
||||
import { isNumber } from 'payload/utilities'
|
||||
|
||||
import type { CollectionRouteHandlerWithID } from '../types.js'
|
||||
|
||||
export const duplicate: CollectionRouteHandlerWithID = async ({ id, collection, req }) => {
|
||||
const { searchParams } = req
|
||||
const depth = searchParams.get('depth')
|
||||
// draft defaults to true, unless explicitly set requested as false to prevent the newly duplicated document from being published
|
||||
const draft = searchParams.get('draft') !== 'false'
|
||||
|
||||
const doc = await duplicateOperation({
|
||||
id,
|
||||
collection,
|
||||
depth: isNumber(depth) ? Number(depth) : undefined,
|
||||
draft,
|
||||
req,
|
||||
})
|
||||
|
||||
const message = req.t('general:successfullyDuplicated', {
|
||||
label: getTranslation(collection.config.labels.singular, req.i18n),
|
||||
})
|
||||
|
||||
return Response.json(
|
||||
{
|
||||
doc,
|
||||
message,
|
||||
},
|
||||
{
|
||||
status: httpStatus.OK,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import path from 'path'
|
||||
import { APIError } from 'payload/errors'
|
||||
|
||||
import { streamFile } from '../../../next-stream-file/index.js'
|
||||
import { routeError } from '../routeError.js'
|
||||
import { RouteError } from '../RouteError.js'
|
||||
import { checkFileAccess } from './checkFileAccess.js'
|
||||
|
||||
// /:collectionSlug/file/:filename
|
||||
@@ -64,7 +64,7 @@ export const getFile = async ({ collection, filename, req }: Args): Promise<Resp
|
||||
status: httpStatus.OK,
|
||||
})
|
||||
} catch (error) {
|
||||
return routeError({
|
||||
return RouteError({
|
||||
collection,
|
||||
err: error,
|
||||
req,
|
||||
|
||||
@@ -12,7 +12,7 @@ import type {
|
||||
} from './types.js'
|
||||
|
||||
import { createPayloadRequest } from '../../utilities/createPayloadRequest.js'
|
||||
import { routeError } from './routeError.js'
|
||||
import { RouteError } from './RouteError.js'
|
||||
import { access } from './auth/access.js'
|
||||
import { forgotPassword } from './auth/forgotPassword.js'
|
||||
import { init } from './auth/init.js'
|
||||
@@ -30,7 +30,6 @@ import { create } from './collections/create.js'
|
||||
import { deleteDoc } from './collections/delete.js'
|
||||
import { deleteByID } from './collections/deleteByID.js'
|
||||
import { docAccess } from './collections/docAccess.js'
|
||||
import { duplicate } from './collections/duplicate.js'
|
||||
import { find } from './collections/find.js'
|
||||
import { findByID } from './collections/findByID.js'
|
||||
import { findVersionByID } from './collections/findVersionByID.js'
|
||||
@@ -72,7 +71,6 @@ const endpoints = {
|
||||
'doc-access-by-id': docAccess,
|
||||
'doc-verify-by-id': verifyEmail,
|
||||
'doc-versions-by-id': restoreVersion,
|
||||
duplicate,
|
||||
'first-register': registerFirstUser,
|
||||
'forgot-password': forgotPassword,
|
||||
login,
|
||||
@@ -281,7 +279,7 @@ export const GET =
|
||||
|
||||
return RouteNotFoundResponse(slug)
|
||||
} catch (error) {
|
||||
return routeError({
|
||||
return RouteError({
|
||||
collection,
|
||||
err: error,
|
||||
req,
|
||||
@@ -358,9 +356,6 @@ export const POST =
|
||||
res = await (
|
||||
endpoints.collection.POST[`doc-${slug2}-by-id`] as CollectionRouteHandlerWithID
|
||||
)({ id: slug3, collection, req })
|
||||
} else if (slug3 === 'duplicate') {
|
||||
// /:collection/:id/duplicate
|
||||
res = await endpoints.collection.POST.duplicate({ id: slug2, collection, req })
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -423,7 +418,7 @@ export const POST =
|
||||
|
||||
return RouteNotFoundResponse(slug)
|
||||
} catch (error) {
|
||||
return routeError({
|
||||
return RouteError({
|
||||
collection,
|
||||
err: error,
|
||||
req,
|
||||
@@ -492,7 +487,7 @@ export const DELETE =
|
||||
|
||||
return RouteNotFoundResponse(slug)
|
||||
} catch (error) {
|
||||
return routeError({
|
||||
return RouteError({
|
||||
collection,
|
||||
err: error,
|
||||
req,
|
||||
@@ -561,7 +556,7 @@ export const PATCH =
|
||||
|
||||
return RouteNotFoundResponse(slug)
|
||||
} catch (error) {
|
||||
return routeError({
|
||||
return RouteError({
|
||||
collection,
|
||||
err: error,
|
||||
req,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { translations } from '@payloadcms/translations/api'
|
||||
import { getAuthenticatedUser } from 'payload/auth'
|
||||
import { parseCookies } from 'payload/auth'
|
||||
import { getDataLoader } from 'payload/utilities'
|
||||
import qs from 'qs'
|
||||
import QueryString from 'qs'
|
||||
import { URL } from 'url'
|
||||
|
||||
import { getDataAndFile } from './getDataAndFile.js'
|
||||
@@ -67,7 +67,6 @@ export const createPayloadRequest = async ({
|
||||
}
|
||||
|
||||
const language = getRequestLanguage({
|
||||
config,
|
||||
cookies,
|
||||
headers: request.headers,
|
||||
})
|
||||
@@ -98,10 +97,11 @@ export const createPayloadRequest = async ({
|
||||
port: urlProperties.port,
|
||||
protocol: urlProperties.protocol,
|
||||
query: urlProperties.search
|
||||
? qs.parse(urlProperties.search, {
|
||||
? QueryString.parse(urlProperties.search, {
|
||||
arrayLimit: 1000,
|
||||
depth: 10,
|
||||
ignoreQueryPrefix: true,
|
||||
strictNullHandling: true,
|
||||
})
|
||||
: {},
|
||||
routeParams: params || {},
|
||||
|
||||
@@ -7,16 +7,19 @@ import { cookies, headers } from 'next/headers.js'
|
||||
|
||||
import { getRequestLanguage } from './getRequestLanguage.js'
|
||||
|
||||
export const getNextI18n = ({
|
||||
export const getNextI18n = async ({
|
||||
config,
|
||||
language,
|
||||
}: {
|
||||
config: SanitizedConfig
|
||||
language?: string
|
||||
}): I18n =>
|
||||
initI18n({
|
||||
}): Promise<I18n> => {
|
||||
const i18n = initI18n({
|
||||
config: config.i18n,
|
||||
context: 'client',
|
||||
language: language || getRequestLanguage({ config, cookies: cookies(), headers: headers() }),
|
||||
language: language || getRequestLanguage({ cookies: cookies(), headers: headers() }),
|
||||
translations,
|
||||
})
|
||||
|
||||
return i18n
|
||||
}
|
||||
|
||||
@@ -1,27 +1,24 @@
|
||||
import type { ReadonlyRequestCookies } from 'next/dist/server/web/spec-extension/adapters/request-cookies.js'
|
||||
import type { SanitizedConfig } from 'payload/config'
|
||||
|
||||
import { matchLanguage } from '@payloadcms/translations'
|
||||
|
||||
type GetRequestLanguageArgs = {
|
||||
config: SanitizedConfig
|
||||
cookies: Map<string, string> | ReadonlyRequestCookies
|
||||
defaultLanguage?: string
|
||||
headers: Request['headers']
|
||||
}
|
||||
|
||||
export const getRequestLanguage = ({
|
||||
config,
|
||||
cookies,
|
||||
defaultLanguage = 'en',
|
||||
headers,
|
||||
}: GetRequestLanguageArgs): string => {
|
||||
const acceptLanguage = headers.get('Accept-Language')
|
||||
const cookieLanguage = cookies.get(`${config.cookiePrefix || 'payload'}-lng'`)
|
||||
const cookieLanguage = cookies.get('lng')
|
||||
|
||||
const reqLanguage =
|
||||
(typeof cookieLanguage === 'string' ? cookieLanguage : cookieLanguage?.value) ||
|
||||
acceptLanguage ||
|
||||
(typeof cookieLanguage === 'string' ? cookieLanguage : cookieLanguage?.value) ||
|
||||
defaultLanguage
|
||||
|
||||
return matchLanguage(reqLanguage)
|
||||
|
||||
@@ -8,7 +8,7 @@ import type {
|
||||
|
||||
import { initI18n } from '@payloadcms/translations'
|
||||
import { translations } from '@payloadcms/translations/client'
|
||||
import { findLocaleFromCode } from '@payloadcms/ui/utilities/findLocaleFromCode'
|
||||
import { findLocaleFromCode } from '@payloadcms/ui'
|
||||
import { headers as getHeaders } from 'next/headers.js'
|
||||
import { notFound, redirect } from 'next/navigation.js'
|
||||
import { createLocalReq } from 'payload/utilities'
|
||||
@@ -62,7 +62,7 @@ export const initPage = async ({
|
||||
localization && localization.defaultLocale ? localization.defaultLocale : 'en'
|
||||
const localeCode = localeParam || defaultLocale
|
||||
const locale = localization && findLocaleFromCode(localization, localeCode)
|
||||
const language = getRequestLanguage({ config: payload.config, cookies, headers })
|
||||
const language = getRequestLanguage({ cookies, headers })
|
||||
|
||||
const i18n = initI18n({
|
||||
config: payload.config.i18n,
|
||||
@@ -71,19 +71,13 @@ export const initPage = async ({
|
||||
translations,
|
||||
})
|
||||
|
||||
const queryString = `${qs.stringify(searchParams, { addQueryPrefix: true })}`
|
||||
|
||||
const req = await createLocalReq(
|
||||
{
|
||||
fallbackLocale: null,
|
||||
locale: locale.code,
|
||||
req: {
|
||||
i18n,
|
||||
query: qs.parse(queryString, {
|
||||
depth: 10,
|
||||
ignoreQueryPrefix: true,
|
||||
}),
|
||||
url: `${payload.config.serverURL}${route}${searchParams ? queryString : ''}`,
|
||||
url: `${payload.config.serverURL}${route}${searchParams ? `${qs.stringify(searchParams, { addQueryPrefix: true })}` : ''}`,
|
||||
} as PayloadRequest,
|
||||
user,
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
'use client'
|
||||
import { Chevron } from '@payloadcms/ui/icons/Chevron'
|
||||
import { Chevron } from '@payloadcms/ui'
|
||||
import * as React from 'react'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
'use client'
|
||||
|
||||
import { CopyToClipboard } from '@payloadcms/ui/elements/CopyToClipboard'
|
||||
import { Gutter } from '@payloadcms/ui/elements/Gutter'
|
||||
import { Checkbox } from '@payloadcms/ui/fields/Checkbox'
|
||||
import { NumberField as NumberInput } from '@payloadcms/ui/fields/Number'
|
||||
import { Select } from '@payloadcms/ui/fields/Select'
|
||||
import { Form } from '@payloadcms/ui/forms/Form'
|
||||
import { MinimizeMaximize } from '@payloadcms/ui/icons/MinimizeMaximize'
|
||||
import { SetViewActions } from '@payloadcms/ui/providers/Actions'
|
||||
import { useComponentMap } from '@payloadcms/ui/providers/ComponentMap'
|
||||
import { useConfig } from '@payloadcms/ui/providers/Config'
|
||||
import { useDocumentInfo } from '@payloadcms/ui/providers/DocumentInfo'
|
||||
import { useLocale } from '@payloadcms/ui/providers/Locale'
|
||||
import { useTranslation } from '@payloadcms/ui/providers/Translation'
|
||||
import {
|
||||
Checkbox,
|
||||
CopyToClipboard,
|
||||
Form,
|
||||
Gutter,
|
||||
MinimizeMaximize,
|
||||
Number as NumberInput,
|
||||
Select,
|
||||
SetViewActions,
|
||||
useComponentMap,
|
||||
useConfig,
|
||||
useDocumentInfo,
|
||||
useLocale,
|
||||
useTranslation,
|
||||
} from '@payloadcms/ui'
|
||||
import { useSearchParams } from 'next/navigation.js'
|
||||
import qs from 'qs'
|
||||
import * as React from 'react'
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client'
|
||||
import { ReactSelect } from '@payloadcms/ui/elements/ReactSelect'
|
||||
import { FieldLabel } from '@payloadcms/ui/forms/FieldLabel'
|
||||
import { useTranslation } from '@payloadcms/ui/providers/Translation'
|
||||
import { Label, ReactSelect } from '@payloadcms/ui'
|
||||
import { useTranslation } from '@payloadcms/ui/providers'
|
||||
import React from 'react'
|
||||
|
||||
import { ToggleTheme } from '../ToggleTheme/index.js'
|
||||
@@ -14,18 +13,17 @@ export const Settings: React.FC<{
|
||||
}> = (props) => {
|
||||
const { className } = props
|
||||
|
||||
const { i18n, languageOptions, switchLanguage, t } = useTranslation()
|
||||
const { i18n, languageOptions, t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className={[baseClass, className].filter(Boolean).join(' ')}>
|
||||
<h3>{t('general:payloadSettings')}</h3>
|
||||
<div className={`${baseClass}__language`}>
|
||||
<FieldLabel htmlFor="language-select" label={t('general:language')} />
|
||||
<Label htmlFor="language-select" label={t('general:language')} />
|
||||
<ReactSelect
|
||||
inputId="language-select"
|
||||
onChange={async ({ value }) => {
|
||||
await switchLanguage(value)
|
||||
}}
|
||||
// TODO(i18n): wire up onChange / changeLanguage fn
|
||||
// onChange={({ value }) => i18n.changeLanguage(value)}
|
||||
options={languageOptions}
|
||||
value={languageOptions.find((language) => language.value === i18n.language)}
|
||||
/>
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
'use client'
|
||||
import type { OnChange, Theme } from '@payloadcms/ui'
|
||||
|
||||
import { RadioGroup } from '@payloadcms/ui/fields/RadioGroup'
|
||||
import { useTheme } from '@payloadcms/ui/providers/Theme'
|
||||
import { useTranslation } from '@payloadcms/ui/providers/Translation'
|
||||
import { RadioGroupInput, useTheme, useTranslation } from '@payloadcms/ui'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
export const ToggleTheme: React.FC = () => {
|
||||
const { autoMode, setTheme, theme } = useTheme()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const onChange = useCallback(
|
||||
const onChange = useCallback<OnChange<Theme>>(
|
||||
(newTheme) => {
|
||||
setTheme(newTheme)
|
||||
},
|
||||
@@ -17,7 +16,7 @@ export const ToggleTheme: React.FC = () => {
|
||||
)
|
||||
|
||||
return (
|
||||
<RadioGroup
|
||||
<RadioGroupInput
|
||||
label={t('general:adminTheme')}
|
||||
name="theme"
|
||||
onChange={onChange}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import type { DocumentPreferences, ServerSideEditViewProps, TypeWithID } from 'payload/types'
|
||||
import type { AdminViewProps } from 'payload/types'
|
||||
|
||||
import { DocumentHeader } from '@payloadcms/ui/elements/DocumentHeader'
|
||||
import { HydrateClientUser } from '@payloadcms/ui/elements/HydrateClientUser'
|
||||
import { RenderCustomComponent } from '@payloadcms/ui/elements/RenderCustomComponent'
|
||||
import { buildStateFromSchema } from '@payloadcms/ui/forms/buildStateFromSchema'
|
||||
import { DocumentInfoProvider } from '@payloadcms/ui/providers/DocumentInfo'
|
||||
import { FormQueryParamsProvider } from '@payloadcms/ui/providers/FormQueryParams'
|
||||
import { formatDocTitle } from '@payloadcms/ui/utilities/formatDocTitle'
|
||||
import { formatFields } from '@payloadcms/ui/utilities/formatFields'
|
||||
import {
|
||||
DocumentHeader,
|
||||
DocumentInfoProvider,
|
||||
FormQueryParamsProvider,
|
||||
HydrateClientUser,
|
||||
RenderCustomComponent,
|
||||
buildStateFromSchema,
|
||||
formatDocTitle,
|
||||
formatFields,
|
||||
} from '@payloadcms/ui'
|
||||
import { notFound } from 'next/navigation.js'
|
||||
import React from 'react'
|
||||
|
||||
@@ -17,11 +19,7 @@ import { Settings } from './Settings/index.js'
|
||||
|
||||
export { generateAccountMetadata } from './meta.js'
|
||||
|
||||
export const Account: React.FC<AdminViewProps> = async ({
|
||||
initPageResult,
|
||||
params,
|
||||
searchParams,
|
||||
}) => {
|
||||
export const Account: React.FC<AdminViewProps> = async ({ initPageResult, searchParams }) => {
|
||||
const {
|
||||
locale,
|
||||
permissions,
|
||||
@@ -89,9 +87,8 @@ export const Account: React.FC<AdminViewProps> = async ({
|
||||
req,
|
||||
})
|
||||
|
||||
const viewComponentProps: ServerSideEditViewProps = {
|
||||
const serverSideProps: ServerSideEditViewProps = {
|
||||
initPageResult,
|
||||
params,
|
||||
routeSegments: [],
|
||||
searchParams,
|
||||
}
|
||||
@@ -136,7 +133,7 @@ export const Account: React.FC<AdminViewProps> = async ({
|
||||
typeof CustomAccountComponent === 'function' ? CustomAccountComponent : undefined
|
||||
}
|
||||
DefaultComponent={EditView}
|
||||
componentProps={viewComponentProps}
|
||||
componentProps={serverSideProps}
|
||||
/>
|
||||
</FormQueryParamsProvider>
|
||||
</DocumentInfoProvider>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
'use client'
|
||||
import type { FieldMap } from '@payloadcms/ui/utilities/buildComponentMap'
|
||||
import type { FieldMap } from '@payloadcms/ui'
|
||||
|
||||
import { RenderFields } from '@payloadcms/ui/forms/RenderFields'
|
||||
import { useComponentMap } from '@payloadcms/ui/providers/ComponentMap'
|
||||
import { RenderFields, useComponentMap } from '@payloadcms/ui'
|
||||
import React from 'react'
|
||||
|
||||
export const CreateFirstUserFields: React.FC<{
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import type { Field } from 'payload/types'
|
||||
import type { AdminViewProps } from 'payload/types'
|
||||
|
||||
import { Form } from '@payloadcms/ui/forms/Form'
|
||||
import { FormSubmit } from '@payloadcms/ui/forms/Submit'
|
||||
import { buildStateFromSchema } from '@payloadcms/ui/forms/buildStateFromSchema'
|
||||
import { mapFields } from '@payloadcms/ui/utilities/buildComponentMap'
|
||||
import { Form, FormSubmit, buildStateFromSchema, mapFields } from '@payloadcms/ui'
|
||||
import React from 'react'
|
||||
|
||||
import { CreateFirstUserFields } from './index.client.js'
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
'use client'
|
||||
import type { EntityToGroup, Group } from '@payloadcms/ui/utilities/groupNavItems'
|
||||
import type { EntityToGroup, Group } from '@payloadcms/ui'
|
||||
import type { Permissions } from 'payload/auth'
|
||||
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import { Button } from '@payloadcms/ui/elements/Button'
|
||||
import { Card } from '@payloadcms/ui/elements/Card'
|
||||
import { SetViewActions } from '@payloadcms/ui/providers/Actions'
|
||||
import { useAuth } from '@payloadcms/ui/providers/Auth'
|
||||
import { useConfig } from '@payloadcms/ui/providers/Config'
|
||||
import { useTranslation } from '@payloadcms/ui/providers/Translation'
|
||||
import { EntityType, groupNavItems } from '@payloadcms/ui/utilities/groupNavItems'
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
EntityType,
|
||||
SetViewActions,
|
||||
groupNavItems,
|
||||
useAuth,
|
||||
useConfig,
|
||||
useTranslation,
|
||||
} from '@payloadcms/ui'
|
||||
import React, { Fragment, useEffect, useState } from 'react'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user