diff --git a/.env.example b/.env.example deleted file mode 100644 index 940af56b36..0000000000 --- a/.env.example +++ /dev/null @@ -1,4 +0,0 @@ -DATABASE_URI=mongodb://127.0.0.1/payloadtests -PAYLOAD_SECRET=laijflieawfjlweifjewalifjwe -# PAYLOAD_CONFIG_PATH=MUST BE SET PROGRAMMATICALLY -PAYLOAD_DROP_DATABASE=true diff --git a/next.config.mjs b/next.config.mjs index 57cb05b073..d1af531dde 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -6,6 +6,7 @@ const withBundleAnalyzer = bundleAnalyzer({ enabled: process.env.ANALYZE === 'true', }) +// eslint-disable-next-line no-restricted-exports export default withBundleAnalyzer( withPayload({ reactStrictMode: false, diff --git a/package.json b/package.json index 1e64c5089c..0593c6b1c9 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,7 @@ "lint-staged": "^14.0.1", "minimist": "1.2.8", "mongodb-memory-server": "8.13.0", - "next": "14.1.2", + "next": "14.2.0-canary.21", "node-mocks-http": "^1.14.1", "nodemon": "3.0.3", "pino": "8.15.0", diff --git a/packages/next/src/views/Document/getViewsFromConfig.tsx b/packages/next/src/views/Document/getViewsFromConfig.tsx index 721ba29177..0d3355d5a3 100644 --- a/packages/next/src/views/Document/getViewsFromConfig.tsx +++ b/packages/next/src/views/Document/getViewsFromConfig.tsx @@ -1,20 +1,17 @@ import type { CollectionPermission, GlobalPermission, User } from 'payload/auth' import type { EditViewComponent } from 'payload/config' import type { + AdminViewComponent, SanitizedCollectionConfig, SanitizedConfig, SanitizedGlobalConfig, } from 'payload/types' -import type React from 'react' import { isEntityHidden } from 'payload/utilities' -import type { AdminViewProps } from '../Root/index.js' - import { APIView as DefaultAPIView } from '../API/index.js' import { EditView as DefaultEditView } from '../Edit/index.js' import { LivePreviewView as DefaultLivePreviewView } from '../LivePreview/index.js' -import { NotFoundClient } from '../NotFound/index.client.js' import { Unauthorized } from '../Unauthorized/index.js' import { VersionView as DefaultVersionView } from '../Version/index.js' import { VersionsView as DefaultVersionsView } from '../Versions/index.js' @@ -41,12 +38,12 @@ export const getViewsFromConfig = ({ /** * The error view to display if CustomView or DefaultView do not exist (could be either due to not found, or unauthorized). Can be null */ - ErrorView: React.FC + ErrorView: AdminViewComponent } | null => { // Conditionally import and lazy load the default view let DefaultView: EditViewComponent = null let CustomView: EditViewComponent = null - let ErrorView: React.FC = null + let ErrorView: AdminViewComponent = null const views = (collectionConfig && collectionConfig?.admin?.components?.views) || diff --git a/packages/next/src/views/Document/index.tsx b/packages/next/src/views/Document/index.tsx index 56b6caabaf..f244ae1b27 100644 --- a/packages/next/src/views/Document/index.tsx +++ b/packages/next/src/views/Document/index.tsx @@ -19,6 +19,7 @@ import { formatDocTitle, formatFields, } from '@payloadcms/ui' +import { docAccessOperation } from 'payload/operations' import React from 'react' import type { GenerateEditViewMetadata } from './getMetaBySegment.js' @@ -72,7 +73,14 @@ export const Document: React.FC = async ({ let action: string if (collectionConfig) { - docPermissions = permissions?.collections?.[collectionSlug] + docPermissions = await docAccessOperation({ + id, + collection: { + config: collectionConfig, + }, + req, + }) + fields = collectionConfig.fields action = `${serverURL}${apiRoute}/${collectionSlug}${isEditing ? `/${id}` : ''}` diff --git a/packages/next/webpack.config.js b/packages/next/webpack.config.js index c3670e0166..e52ad57695 100644 --- a/packages/next/webpack.config.js +++ b/packages/next/webpack.config.js @@ -10,7 +10,14 @@ const dirname = path.dirname(filename) const componentWebpackConfig = { entry: path.resolve(dirname, './src/index.ts'), - externals: ['react', 'react-dom', 'payload', 'payload/config', 'react-image-crop'], + externals: [ + 'react', + 'react-dom', + 'payload', + 'payload/config', + 'react-image-crop', + 'payload/operations', + ], mode: 'production', module: { rules: [ diff --git a/packages/payload/src/utilities/telemetry/events/adminInit.ts b/packages/payload/src/utilities/telemetry/events/adminInit.ts index f5e8779f30..cbf6290997 100644 --- a/packages/payload/src/utilities/telemetry/events/adminInit.ts +++ b/packages/payload/src/utilities/telemetry/events/adminInit.ts @@ -1,5 +1,5 @@ -import type { Payload } from '../../../index.js' import type { User } from '../../../auth/types.js' +import type { Payload } from '../../../index.js' import { sendEvent } from '../index.js' import { oneWayHash } from '../oneWayHash.js' @@ -32,8 +32,8 @@ export const adminInit = ({ headers, payload, user }: Args): void => { // eslint-disable-next-line @typescript-eslint/no-floating-promises sendEvent({ event: { - domainID, type: 'admin-init', + domainID, userID, }, payload, diff --git a/packages/ui/webpack.config.js b/packages/ui/webpack.config.js index c3670e0166..e52ad57695 100644 --- a/packages/ui/webpack.config.js +++ b/packages/ui/webpack.config.js @@ -10,7 +10,14 @@ const dirname = path.dirname(filename) const componentWebpackConfig = { entry: path.resolve(dirname, './src/index.ts'), - externals: ['react', 'react-dom', 'payload', 'payload/config', 'react-image-crop'], + externals: [ + 'react', + 'react-dom', + 'payload', + 'payload/config', + 'react-image-crop', + 'payload/operations', + ], mode: 'production', module: { rules: [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 64d3cd7908..792fc31cf3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -191,8 +191,8 @@ importers: specifier: 8.13.0 version: 8.13.0 next: - specifier: 14.1.2 - version: 14.1.2(@babel/core@7.24.0)(react-dom@18.2.0)(react@18.2.0) + specifier: 14.2.0-canary.21 + version: 14.2.0-canary.21(@babel/core@7.24.0)(react-dom@18.2.0)(react@18.2.0) node-mocks-http: specifier: ^1.14.1 version: 1.14.1 @@ -1344,7 +1344,7 @@ importers: version: 2.3.0 next: specifier: 14.2.0-canary.7 - version: 14.2.0-canary.7(@babel/core@7.24.0)(react-dom@18.2.0)(react@18.2.0) + version: 14.2.0-canary.7(@babel/core@7.24.0)(react-dom@18.2.0)(react@18.2.0)(sass@1.71.1) object-to-formdata: specifier: 4.5.1 version: 4.5.1 @@ -4421,8 +4421,8 @@ packages: - utf-8-validate dev: true - /@next/env@14.1.2: - resolution: {integrity: sha512-U0iEG+JF86j6qyu330sfPgsMmDVH8vWVmzZadl+an5EU3o5HqdNytOpM+HsFpl58PmhGBTKx3UmM9c+eoLK0mA==} + /@next/env@14.2.0-canary.21: + resolution: {integrity: sha512-J1iLIGns8PUIwTD9EKm3sMWj3tvut872q/LlG80nUM97Pm3nxXGEtBaF102F1bPtaKnEQXSj5OW2WEGjm7Ol6Q==} dev: true /@next/env@14.2.0-canary.7: @@ -4435,8 +4435,8 @@ packages: glob: 10.3.10 dev: true - /@next/swc-darwin-arm64@14.1.2: - resolution: {integrity: sha512-E4/clgk0ZrYMo9eMRwP/4IO/cvXF1yEYSnGcdGfH+NYTR8bNFy76TSlc1Vb2rK3oaQY4BVHRpx8f/sMN/D5gNw==} + /@next/swc-darwin-arm64@14.2.0-canary.21: + resolution: {integrity: sha512-XU0gVw9TReqFep1C6qPW7+ezcHvyAHd8YfEvGnMo6jzRQgYL3Opf02g48AUV1+mlKN43UiHn9VbFRCwBQqIpig==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -4453,8 +4453,8 @@ packages: dev: false optional: true - /@next/swc-darwin-x64@14.1.2: - resolution: {integrity: sha512-j8mEOI+ZM0tU9B/L/OGa6F7d9FXYMkog5OWWuhTWzz3iZ91UKIGGpD/ojTNKuejainDMgbqOBTNnLg0jZywM/g==} + /@next/swc-darwin-x64@14.2.0-canary.21: + resolution: {integrity: sha512-FXSyOIP+xmlb42rnOUoDO8cktKE+mFSpWGG2nxjZwPlnRFC9kVhw2b1Djt1gXhu524ts/bYnfPWvNbNfuNU8bg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -4471,8 +4471,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-gnu@14.1.2: - resolution: {integrity: sha512-qpRrd5hl6BFTWiFLgHtJmqqQGRMs+ol0MN9pEp0SYoLs3j8OTErPiDMhbKWjMWHGdc2E3kg4RRBV3cSTZiePiQ==} + /@next/swc-linux-arm64-gnu@14.2.0-canary.21: + resolution: {integrity: sha512-fGJxv3g7PlkkAlHP5ahDsQQKGRxggtZfPXuqtPDF3BNPm9Z6e4clyUyeOCD8HUlaB+3yg5mWt9lvR1t82BeqSA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -4489,8 +4489,8 @@ packages: dev: false optional: true - /@next/swc-linux-arm64-musl@14.1.2: - resolution: {integrity: sha512-HAhvVXAv+wnbj0wztT0YnpgJVoHtw1Mv4Y1R/JJcg5yXSU8FsP2uEGUwjQaqPoD76YSZjuKl32YbJlmPgQbLFw==} + /@next/swc-linux-arm64-musl@14.2.0-canary.21: + resolution: {integrity: sha512-5N+wkZi7DTNrnw9dm9CME3DYGvARp8qOvUwUd4Jg4yZmYNUK2GjtKx5oGCzPe82HqXyZPWDeTaCi8rCEDrO45w==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -4507,8 +4507,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-gnu@14.1.2: - resolution: {integrity: sha512-PCWC312woXLWOXiedi1E+fEw6B/ECP1fMiK1nSoGS2E43o56Z8kq4WeJLbJoufFQGVj5ZOKU3jIVyV//3CI4wQ==} + /@next/swc-linux-x64-gnu@14.2.0-canary.21: + resolution: {integrity: sha512-gmE/b9WCV5CTtBYygo5TPnBRyGO7jpF+FTHHiTT4IcQD4KenHS7EuEpRKsh+ILZV8LpCCHSiUXKJLjz78OP5Sw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -4525,8 +4525,8 @@ packages: dev: false optional: true - /@next/swc-linux-x64-musl@14.1.2: - resolution: {integrity: sha512-KQSKzdWPNrYZjeTPCsepEpagOzU8Nf3Zzu53X1cLsSY6QlOIkYcSgEihRjsMKyeQW4aSvc+nN5pIpC2pLWNSMA==} + /@next/swc-linux-x64-musl@14.2.0-canary.21: + resolution: {integrity: sha512-bZtw5eCh00sRyVyKNjofrx6Gyi0xklGUbdvGykQgbh26IiUwNZCXS5wrR5kSF1kMfRZhTdPTIXrEU4aolxdKUw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -4543,8 +4543,8 @@ packages: dev: false optional: true - /@next/swc-win32-arm64-msvc@14.1.2: - resolution: {integrity: sha512-3b0PouKd09Ulm2T1tjaRnwQj9+UwSsMO680d/sD4XAlm29KkNmVLAEIwWTfb3L+E11Qyw+jdcN3HtbDCg5+vYA==} + /@next/swc-win32-arm64-msvc@14.2.0-canary.21: + resolution: {integrity: sha512-bfPh6G3WYsSWeG0IhA7Kh9XWjMxfmvvd9wkK0YHyl7c2iAZsvY6m9kWNlpMUFpXZRNiagDJNc2Zz5a+lpTMBHg==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] @@ -4561,8 +4561,8 @@ packages: dev: false optional: true - /@next/swc-win32-ia32-msvc@14.1.2: - resolution: {integrity: sha512-CC1gaJY4h+wg6d5r2biggGM6nCFXh/6WEim2VOQI0WrA6easCQi2P2hzWyrU6moQ0g1GOiWzesGc6nn0a92Kgg==} + /@next/swc-win32-ia32-msvc@14.2.0-canary.21: + resolution: {integrity: sha512-nApW0yh16ye/ggHsX6TqYN5PWexsjjJm/EiCHU+7gq+TeVFH274a+uQOUDHJ31H3sthaC6bvnLrTkdZt6/12cA==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] @@ -4579,8 +4579,8 @@ packages: dev: false optional: true - /@next/swc-win32-x64-msvc@14.1.2: - resolution: {integrity: sha512-pfASwanOd+yP3D80O63DuQffrBySZPuB7wRN0IGSRq/0rDm9p/MvvnLzzgP2kSiLOUklOrFYVax7P6AEzjGykQ==} + /@next/swc-win32-x64-msvc@14.2.0-canary.21: + resolution: {integrity: sha512-anyhM5mDXz4xMN5qo/naoaGsSPBFiT+3M8jZmY0bPcp251dehNoum6uiGLAShEiF1yFa6m1IzkkOI+3GY5q1DA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -5544,18 +5544,11 @@ packages: /@swc/counter@0.1.3: resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - /@swc/helpers@0.5.2: - resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==} - dependencies: - tslib: 2.6.2 - dev: true - /@swc/helpers@0.5.5: resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} dependencies: '@swc/counter': 0.1.3 tslib: 2.6.2 - dev: false /@swc/jest@0.2.36(@swc/core@1.4.2): resolution: {integrity: sha512-8X80dp81ugxs4a11z1ka43FPhP+/e+mJNXJSxiNYk8gIX/jPBtY4gQTrKu/KIoco8bzKuPI5lUxjfLiGsfvnlw==} @@ -13099,8 +13092,8 @@ packages: /next-tick@1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} - /next@14.1.2(@babel/core@7.24.0)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-p4RfNmopqkzRP1uUyBJnHii+qMg71f2udWhTTZopBB8b3T5QXNzn7yO+LCYHPWZG2kAvEn4l4neyJHqkXvo2wg==} + /next@14.2.0-canary.21(@babel/core@7.24.0)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-8EjhlMmgnXCD+AzgwuomJBREzj5Kj3ZGuDSGgQuVNAoat+OIUDn/0lmX5yEacUJjiDstV4DopvwjTP8Jgm7l6A==} engines: {node: '>=18.17.0'} hasBin: true peerDependencies: @@ -13114,46 +13107,7 @@ packages: sass: optional: true dependencies: - '@next/env': 14.1.2 - '@swc/helpers': 0.5.2 - busboy: 1.6.0 - caniuse-lite: 1.0.30001591 - graceful-fs: 4.2.11 - postcss: 8.4.31 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - styled-jsx: 5.1.1(@babel/core@7.24.0)(react@18.2.0) - optionalDependencies: - '@next/swc-darwin-arm64': 14.1.2 - '@next/swc-darwin-x64': 14.1.2 - '@next/swc-linux-arm64-gnu': 14.1.2 - '@next/swc-linux-arm64-musl': 14.1.2 - '@next/swc-linux-x64-gnu': 14.1.2 - '@next/swc-linux-x64-musl': 14.1.2 - '@next/swc-win32-arm64-msvc': 14.1.2 - '@next/swc-win32-ia32-msvc': 14.1.2 - '@next/swc-win32-x64-msvc': 14.1.2 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros - dev: true - - /next@14.2.0-canary.7(@babel/core@7.24.0)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-X9wrHOzj25uzuECqpaO7SJ34gxZziaJZXdfRQJmEJC5tQ1FmxUm0eZ/SXIw3fPkjX2/04JmavkLJPtD3BA6UyA==} - engines: {node: '>=18.17.0'} - hasBin: true - peerDependencies: - '@opentelemetry/api': ^1.1.0 - react: ^18.2.0 - react-dom: ^18.2.0 - sass: ^1.3.0 - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - sass: - optional: true - dependencies: - '@next/env': 14.2.0-canary.7 + '@next/env': 14.2.0-canary.21 '@swc/helpers': 0.5.5 busboy: 1.6.0 caniuse-lite: 1.0.30001591 @@ -13163,19 +13117,19 @@ packages: react-dom: 18.2.0(react@18.2.0) styled-jsx: 5.1.1(@babel/core@7.24.0)(react@18.2.0) optionalDependencies: - '@next/swc-darwin-arm64': 14.2.0-canary.7 - '@next/swc-darwin-x64': 14.2.0-canary.7 - '@next/swc-linux-arm64-gnu': 14.2.0-canary.7 - '@next/swc-linux-arm64-musl': 14.2.0-canary.7 - '@next/swc-linux-x64-gnu': 14.2.0-canary.7 - '@next/swc-linux-x64-musl': 14.2.0-canary.7 - '@next/swc-win32-arm64-msvc': 14.2.0-canary.7 - '@next/swc-win32-ia32-msvc': 14.2.0-canary.7 - '@next/swc-win32-x64-msvc': 14.2.0-canary.7 + '@next/swc-darwin-arm64': 14.2.0-canary.21 + '@next/swc-darwin-x64': 14.2.0-canary.21 + '@next/swc-linux-arm64-gnu': 14.2.0-canary.21 + '@next/swc-linux-arm64-musl': 14.2.0-canary.21 + '@next/swc-linux-x64-gnu': 14.2.0-canary.21 + '@next/swc-linux-x64-musl': 14.2.0-canary.21 + '@next/swc-win32-arm64-msvc': 14.2.0-canary.21 + '@next/swc-win32-ia32-msvc': 14.2.0-canary.21 + '@next/swc-win32-x64-msvc': 14.2.0-canary.21 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros - dev: false + dev: true /next@14.2.0-canary.7(@babel/core@7.24.0)(react-dom@18.2.0)(react@18.2.0)(sass@1.71.1): resolution: {integrity: sha512-X9wrHOzj25uzuECqpaO7SJ34gxZziaJZXdfRQJmEJC5tQ1FmxUm0eZ/SXIw3fPkjX2/04JmavkLJPtD3BA6UyA==} diff --git a/test/dev.js b/test/dev.js index 9785bb3d7d..2d8d6687b7 100644 --- a/test/dev.js +++ b/test/dev.js @@ -22,6 +22,8 @@ async function main() { process.env.TURBOPACK = '1' } + process.env.PAYLOAD_DROP_DATABASE = 'true' + beforeTest(testSuiteArg) nextDev({ _: [resolve(_dirname, '..')], port: process.env.PORT || 3000 }) diff --git a/test/helpers.ts b/test/helpers.ts index 9a6b58c3fb..8bbceaad39 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -133,6 +133,10 @@ export function initPageConsoleErrorCatch(page: Page) { page.on('console', (msg) => { if ( msg.type() === 'error' && + // Playwright is seemingly loading CJS files from React Select, but Next loads ESM. + // This leads to classnames not matching. Ignore these God-awful errors + // https://github.com/JedWatson/react-select/issues/3590 + !msg.text().includes('did not match. Server:') && !msg.text().includes('the server responded with a status of') && !msg.text().includes('Failed to fetch RSC payload for') ) { diff --git a/test/helpers/configHelpers.ts b/test/helpers/configHelpers.ts index d7cef5dc45..096260c853 100644 --- a/test/helpers/configHelpers.ts +++ b/test/helpers/configHelpers.ts @@ -1,7 +1,7 @@ import { promises as _promises } from 'fs' -import getPort from 'get-port' -import { nextDev } from 'next/dist/cli/next-dev.js' -import path from 'path' +import { createServer } from 'http' +import nextImport from 'next' +import { parse } from 'url' import type { SanitizedConfig } from '../../packages/payload/src/config/types.js' import type { Payload } from '../../packages/payload/src/index.js' @@ -24,24 +24,42 @@ export async function initPayloadE2E({ config, dirname }: Args): Promise const testSuiteName = dirname.split('/').pop() await beforeTest(testSuiteName) - const port = await getPort() - const serverURL = `http://localhost:${port}` process.env.NODE_OPTIONS = '--no-deprecation' process.env.PAYLOAD_DROP_DATABASE = 'true' - process.env.PORT = String(port) // @ts-expect-error process.env.NODE_ENV = 'test' const payload = await getPayloadHMR({ config }) - nextDev({ - _: [path.resolve(dirname, '../../')], - '--port': port, - // Turbo doesn't seem to be reading - // our tsconfig paths, commented out for now - // '--turbo': '1', + // const port = await getPort() + const port = 3000 + process.env.PORT = String(port) + const serverURL = `http://localhost:${port}` + + // @ts-expect-error + const app = nextImport({ dev: true, hostname: 'localhost', port }) + const handle = app.getRequestHandler() + + let resolveServer + + const serverPromise = new Promise((res) => (resolveServer = res)) + + // Need a custom server because calling nextDev straight + // starts up a child process, and payload.onInit() is called twice + // which seeds test data twice + other bad things. + // We initialize Payload above so we can have access to it in the tests + void app.prepare().then(() => { + createServer(async (req, res) => { + const parsedUrl = parse(req.url, true) + await handle(req, res, parsedUrl) + }).listen(port, () => { + resolveServer() + }) }) + await serverPromise + await wait(3000) + return { payload, serverURL } }