## Description
- Adds a new "join" field type to Payload and is supported by all database adapters
- The UI uses a table view for the new field
- `db-mongodb` changes relationships to be stored as ObjectIDs instead of strings (for now querying works using both types internally to the DB so no data migration should be necessary unless you're querying directly, see breaking changes for details
- Adds a reusable traverseFields utility to Payload to make it easier to work with nested fields, used internally and for plugin maintainers
```ts
export const Categories: CollectionConfig = {
slug: 'categories',
fields: [
{
name: 'relatedPosts',
type: 'join',
collection: 'posts',
on: 'category',
}
]
}
```
BREAKING CHANGES:
All mongodb relationship and upload values will be stored as MongoDB ObjectIDs instead of strings going forward. If you have existing data and you are querying data directly, outside of Payload's APIs, you get different results. For example, a `contains` query will no longer works given a partial ID of a relationship since the ObjectID requires the whole identifier to work.
---------
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
Co-authored-by: James <james@trbl.design>
92 lines
4.0 KiB
TypeScript
92 lines
4.0 KiB
TypeScript
import fs from 'fs'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
const filename = fileURLToPath(import.meta.url)
|
|
const dirname = path.dirname(filename)
|
|
|
|
export const tgzToPkgNameMap = {
|
|
payload: 'payload-*',
|
|
'@payloadcms/db-mongodb': 'payloadcms-db-mongodb-*',
|
|
'@payloadcms/db-postgres': 'payloadcms-db-postgres-*',
|
|
'@payloadcms/db-vercel-postgres': 'payloadcms-db-vercel-postgres-*',
|
|
'@payloadcms/db-sqlite': 'payloadcms-db-sqlite-*',
|
|
'@payloadcms/drizzle': 'payloadcms-drizzle-*',
|
|
'@payloadcms/email-nodemailer': 'payloadcms-email-nodemailer-*',
|
|
'@payloadcms/email-resend': 'payloadcms-email-resend-*',
|
|
'@payloadcms/eslint-config': 'payloadcms-eslint-config-*',
|
|
'@payloadcms/eslint-plugin': 'payloadcms-eslint-plugin-*',
|
|
'@payloadcms/graphql': 'payloadcms-graphql-*',
|
|
'@payloadcms/live-preview': 'payloadcms-live-preview-*',
|
|
'@payloadcms/live-preview-react': 'payloadcms-live-preview-react-*',
|
|
'@payloadcms/next': 'payloadcms-next-*',
|
|
'@payloadcms/plugin-cloud': 'payloadcms-plugin-cloud-*',
|
|
'@payloadcms/plugin-cloud-storage': 'payloadcms-plugin-cloud-storage-*',
|
|
'@payloadcms/plugin-form-builder': 'payloadcms-plugin-form-builder-*',
|
|
'@payloadcms/plugin-nested-docs': 'payloadcms-plugin-nested-docs-*',
|
|
'@payloadcms/plugin-redirects': 'payloadcms-plugin-redirects-*',
|
|
'@payloadcms/plugin-search': 'payloadcms-plugin-search-*',
|
|
'@payloadcms/plugin-sentry': 'payloadcms-plugin-sentry-*',
|
|
'@payloadcms/plugin-seo': 'payloadcms-plugin-seo-*',
|
|
'@payloadcms/plugin-stripe': 'payloadcms-plugin-stripe-*',
|
|
'@payloadcms/richtext-lexical': 'payloadcms-richtext-lexical-*',
|
|
'@payloadcms/richtext-slate': 'payloadcms-richtext-slate-*',
|
|
'@payloadcms/storage-azure': 'payloadcms-storage-azure-*',
|
|
'@payloadcms/storage-gcs': 'payloadcms-storage-gcs-*',
|
|
'@payloadcms/storage-s3': 'payloadcms-storage-s3-*',
|
|
'@payloadcms/storage-uploadthing': 'payloadcms-storage-uploadthing-*',
|
|
'@payloadcms/storage-vercel-blob': 'payloadcms-storage-vercel-blob-*',
|
|
'@payloadcms/translations': 'payloadcms-translations-*',
|
|
'@payloadcms/ui': 'payloadcms-ui-*',
|
|
'create-payload-app': 'create-payload-app-*',
|
|
}
|
|
|
|
function findActualTgzName(pattern: string) {
|
|
const packedDir = path.resolve(dirname, 'packed')
|
|
const files = fs.readdirSync(packedDir)
|
|
const matchingFile = files.find((file) => file.startsWith(pattern.replace('*', '')))
|
|
return matchingFile ? `file:packed/${matchingFile}` : null
|
|
}
|
|
|
|
/**
|
|
* This does the following:
|
|
* - installs all packages from test/packed to test/package.json
|
|
*/
|
|
export function setupProd() {
|
|
const packageJsonString = fs.readFileSync(path.resolve(dirname, 'package.json'), 'utf8')
|
|
const packageJson = JSON.parse(packageJsonString)
|
|
|
|
const allDependencies = {}
|
|
// Go through all the dependencies and devDependencies, replace the normal package entry with the tgz entry
|
|
for (const key of ['dependencies', 'devDependencies']) {
|
|
const dependencies = packageJson[key]
|
|
if (dependencies) {
|
|
for (const [packageName, _packageVersion] of Object.entries(dependencies)) {
|
|
if (tgzToPkgNameMap[packageName]) {
|
|
const actualTgzPath = findActualTgzName(tgzToPkgNameMap[packageName])
|
|
if (actualTgzPath) {
|
|
dependencies[packageName] = actualTgzPath
|
|
allDependencies[packageName] = actualTgzPath
|
|
} else {
|
|
console.warn(`Warning: No matching tgz found for ${packageName}`)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// now add them all to overrides and pnpm.overrides as well
|
|
packageJson.pnpm = packageJson.pnpm || {}
|
|
packageJson.pnpm.overrides = packageJson.pnpm.overrides || {}
|
|
packageJson.overrides = packageJson.overrides || {}
|
|
for (const [packageName, packageVersion] of Object.entries(allDependencies)) {
|
|
packageJson.pnpm.overrides[packageName] = packageVersion
|
|
packageJson.overrides[packageName] = packageVersion
|
|
}
|
|
|
|
// write it out
|
|
fs.writeFileSync(path.resolve(dirname, 'package.json'), JSON.stringify(packageJson, null, 2))
|
|
}
|
|
|
|
setupProd()
|