chore(templates): clean up templates dependencies (#7139)
- use react 19 types - no need for dotenv - next has their own dotenv file loader - disable deprecation warnings by default (newer node version spam you with it) - disable turbo by default as hmr is broken and we cannot test against it yet - remove ts-node mention in tsconfig as it's not used anymore - remove unused packages - [fix: potential seed issues due to parallel payload operations being on the same transaction](f899f6a408) andb3b565dd75@DanRibbens can you sense-check this? I do remember that anything running in parallel should never be on the same transaction --------- Co-authored-by: Paul Popus <paul@nouance.io>
This commit is contained in:
@@ -22,8 +22,8 @@
|
|||||||
"graphql": "^16.8.1",
|
"graphql": "^16.8.1",
|
||||||
"next": "15.0.0-canary.53",
|
"next": "15.0.0-canary.53",
|
||||||
"payload": "beta",
|
"payload": "beta",
|
||||||
"react": "^19.0.0-rc-6230622a1a-20240610",
|
"react": "19.0.0-rc-6230622a1a-20240610",
|
||||||
"react-dom": "^19.0.0-rc-6230622a1a-20240610",
|
"react-dom": "19.0.0-rc-6230622a1a-20240610",
|
||||||
"sharp": "0.32.6"
|
"sharp": "0.32.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -22,8 +22,8 @@
|
|||||||
"graphql": "^16.8.1",
|
"graphql": "^16.8.1",
|
||||||
"next": "15.0.0-canary.53",
|
"next": "15.0.0-canary.53",
|
||||||
"payload": "beta",
|
"payload": "beta",
|
||||||
"react": "^19.0.0-rc-6230622a1a-20240610",
|
"react": "19.0.0-rc-6230622a1a-20240610",
|
||||||
"react-dom": "^19.0.0-rc-6230622a1a-20240610",
|
"react-dom": "19.0.0-rc-6230622a1a-20240610",
|
||||||
"sharp": "0.32.6"
|
"sharp": "0.32.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
2
templates/website/.gitignore
vendored
2
templates/website/.gitignore
vendored
@@ -7,4 +7,4 @@ node_modules
|
|||||||
.vercel
|
.vercel
|
||||||
|
|
||||||
# Payload default media upload directory
|
# Payload default media upload directory
|
||||||
./public/media
|
public/media/
|
||||||
|
|||||||
@@ -5,24 +5,19 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "next build",
|
"build": "cross-env NODE_OPTIONS=--no-deprecation next build",
|
||||||
"dev": "next dev --turbo",
|
"dev": "cross-env NODE_OPTIONS=--no-deprecation next dev",
|
||||||
"dev:prod": "rm -rf .next && pnpm build && pnpm serve",
|
"dev:prod": "cross-env NODE_OPTIONS=--no-deprecation rm -rf .next && pnpm build && pnpm serve",
|
||||||
"generate:types": "payload generate:types",
|
"generate:types": "cross-env NODE_OPTIONS=--no-deprecation payload generate:types",
|
||||||
"ii": "pnpm --ignore-workspace install",
|
"ii": "cross-env NODE_OPTIONS=--no-deprecation pnpm --ignore-workspace install",
|
||||||
"lint": "next lint",
|
"lint": "cross-env NODE_OPTIONS=--no-deprecation next lint",
|
||||||
"lint:fix": "next lint --fix",
|
"lint:fix": "cross-env NODE_OPTIONS=--no-deprecation next lint --fix",
|
||||||
"payload": "payload",
|
"payload": "cross-env NODE_OPTIONS=--no-deprecation payload",
|
||||||
"reinstall": "rm -rf node_modules && rm pnpm-lock.yaml && pnpm --ignore-workspace install",
|
"reinstall": "cross-env NODE_OPTIONS=--no-deprecation rm -rf node_modules && rm pnpm-lock.yaml && pnpm --ignore-workspace install",
|
||||||
"start": "next start"
|
"start": "cross-env NODE_OPTIONS=--no-deprecation next start"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lexical/list": "0.16.1",
|
|
||||||
"@lexical/react": "0.16.1",
|
|
||||||
"@lexical/rich-text": "0.16.1",
|
|
||||||
"@lexical/utils": "0.16.1",
|
|
||||||
"@payloadcms/db-mongodb": "3.0.0-beta.63",
|
"@payloadcms/db-mongodb": "3.0.0-beta.63",
|
||||||
"@payloadcms/db-postgres": "3.0.0-beta.63",
|
|
||||||
"@payloadcms/live-preview-react": "3.0.0-beta.63",
|
"@payloadcms/live-preview-react": "3.0.0-beta.63",
|
||||||
"@payloadcms/next": "3.0.0-beta.63",
|
"@payloadcms/next": "3.0.0-beta.63",
|
||||||
"@payloadcms/plugin-cloud": "3.0.0-beta.63",
|
"@payloadcms/plugin-cloud": "3.0.0-beta.63",
|
||||||
@@ -39,8 +34,6 @@
|
|||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"dotenv": "^8.2.0",
|
|
||||||
"escape-html": "^1.0.3",
|
|
||||||
"geist": "^1.3.0",
|
"geist": "^1.3.0",
|
||||||
"graphql": "^16.8.1",
|
"graphql": "^16.8.1",
|
||||||
"jsonwebtoken": "9.0.1",
|
"jsonwebtoken": "9.0.1",
|
||||||
@@ -50,11 +43,9 @@
|
|||||||
"payload": "3.0.0-beta.63",
|
"payload": "3.0.0-beta.63",
|
||||||
"payload-admin-bar": "^1.0.6",
|
"payload-admin-bar": "^1.0.6",
|
||||||
"prism-react-renderer": "^2.3.1",
|
"prism-react-renderer": "^2.3.1",
|
||||||
"qs": "6.11.2",
|
"react": "19.0.0-rc-6230622a1a-20240610",
|
||||||
"react": "beta",
|
"react-dom": "19.0.0-rc-6230622a1a-20240610",
|
||||||
"react-dom": "beta",
|
|
||||||
"react-hook-form": "7.45.4",
|
"react-hook-form": "7.45.4",
|
||||||
"react-router-dom": "5.3.4",
|
|
||||||
"sharp": "0.32.6",
|
"sharp": "0.32.6",
|
||||||
"tailwind-merge": "^2.3.0",
|
"tailwind-merge": "^2.3.0",
|
||||||
"tailwindcss-animate": "^1.0.7"
|
"tailwindcss-animate": "^1.0.7"
|
||||||
@@ -65,8 +56,8 @@
|
|||||||
"@tailwindcss/typography": "^0.5.13",
|
"@tailwindcss/typography": "^0.5.13",
|
||||||
"@types/escape-html": "^1.0.2",
|
"@types/escape-html": "^1.0.2",
|
||||||
"@types/node": "18.11.3",
|
"@types/node": "18.11.3",
|
||||||
"@types/qs": "^6.9.8",
|
"@types/react": "npm:types-react@19.0.0-rc.0",
|
||||||
"@types/react": "^18.3.0",
|
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.0",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"copyfiles": "^2.4.1",
|
"copyfiles": "^2.4.1",
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
@@ -79,7 +70,14 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.20.2 || >=20.9.0"
|
"node": "^18.20.2 || >=20.9.0"
|
||||||
},
|
},
|
||||||
|
"pnpm": {
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@types/react": "18.2.74"
|
"@types/react": "npm:types-react@19.0.0-rc.0",
|
||||||
|
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"@types/react": "npm:types-react@19.0.0-rc.0",
|
||||||
|
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1994
templates/website/pnpm-lock.yaml
generated
1994
templates/website/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -19,15 +19,11 @@ export const RelatedPosts: React.FC<RelatedPostsProps> = (props) => {
|
|||||||
<div className={clsx('container', className)}>
|
<div className={clsx('container', className)}>
|
||||||
{introContent && <RichText content={introContent} enableGutter={false} />}
|
{introContent && <RichText content={introContent} enableGutter={false} />}
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 md:gap-8">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 md:gap-8 items-stretch">
|
||||||
{docs?.map((doc, index) => {
|
{docs?.map((doc, index) => {
|
||||||
if (typeof doc === 'string') return null
|
if (typeof doc === 'string') return null
|
||||||
|
|
||||||
return (
|
return <Card key={index} doc={doc} relationTo="posts" showCategories />
|
||||||
<div key={index}>
|
|
||||||
<Card doc={doc} relationTo="posts" showCategories />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -223,8 +223,8 @@ export interface Page {
|
|||||||
)[];
|
)[];
|
||||||
meta?: {
|
meta?: {
|
||||||
title?: string | null;
|
title?: string | null;
|
||||||
description?: string | null;
|
|
||||||
image?: string | Media | null;
|
image?: string | Media | null;
|
||||||
|
description?: string | null;
|
||||||
};
|
};
|
||||||
publishedAt?: string | null;
|
publishedAt?: string | null;
|
||||||
slug?: string | null;
|
slug?: string | null;
|
||||||
@@ -272,7 +272,7 @@ export interface Media {
|
|||||||
*/
|
*/
|
||||||
export interface Category {
|
export interface Category {
|
||||||
id: string;
|
id: string;
|
||||||
title?: string | null;
|
title: string;
|
||||||
parent?: (string | null) | Category;
|
parent?: (string | null) | Category;
|
||||||
breadcrumbs?:
|
breadcrumbs?:
|
||||||
| {
|
| {
|
||||||
@@ -291,6 +291,7 @@ export interface Category {
|
|||||||
*/
|
*/
|
||||||
export interface Post {
|
export interface Post {
|
||||||
id: string;
|
id: string;
|
||||||
|
title: string;
|
||||||
content: {
|
content: {
|
||||||
root: {
|
root: {
|
||||||
type: string;
|
type: string;
|
||||||
@@ -310,10 +311,9 @@ export interface Post {
|
|||||||
categories?: (string | Category)[] | null;
|
categories?: (string | Category)[] | null;
|
||||||
meta?: {
|
meta?: {
|
||||||
title?: string | null;
|
title?: string | null;
|
||||||
description?: string | null;
|
|
||||||
image?: string | Media | null;
|
image?: string | Media | null;
|
||||||
|
description?: string | null;
|
||||||
};
|
};
|
||||||
title: string;
|
|
||||||
publishedAt?: string | null;
|
publishedAt?: string | null;
|
||||||
authors?: (string | User)[] | null;
|
authors?: (string | User)[] | null;
|
||||||
populatedAuthors?:
|
populatedAuthors?:
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import {
|
|||||||
} from '@payloadcms/richtext-lexical'
|
} from '@payloadcms/richtext-lexical'
|
||||||
import sharp from 'sharp' // editor-import
|
import sharp from 'sharp' // editor-import
|
||||||
import { UnderlineFeature } from '@payloadcms/richtext-lexical'
|
import { UnderlineFeature } from '@payloadcms/richtext-lexical'
|
||||||
import dotenv from 'dotenv'
|
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { buildConfig } from 'payload'
|
import { buildConfig } from 'payload'
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath } from 'url'
|
||||||
@@ -32,17 +31,21 @@ import { seed } from './payload/endpoints/seed'
|
|||||||
import { Footer } from './payload/globals/Footer/Footer'
|
import { Footer } from './payload/globals/Footer/Footer'
|
||||||
import { Header } from './payload/globals/Header/Header'
|
import { Header } from './payload/globals/Header/Header'
|
||||||
import { revalidateRedirects } from './payload/hooks/revalidateRedirects'
|
import { revalidateRedirects } from './payload/hooks/revalidateRedirects'
|
||||||
|
import { GenerateTitle, GenerateURL } from '@payloadcms/plugin-seo/types'
|
||||||
|
import { Page, Post } from 'src/payload-types'
|
||||||
|
|
||||||
const filename = fileURLToPath(import.meta.url)
|
const filename = fileURLToPath(import.meta.url)
|
||||||
const dirname = path.dirname(filename)
|
const dirname = path.dirname(filename)
|
||||||
|
|
||||||
const generateTitle = () => {
|
const generateTitle: GenerateTitle<Post | Page> = ({ doc }) => {
|
||||||
return 'My Website'
|
return doc?.title ? `${doc.title} | Payload Website Template` : 'Payload Website Template'
|
||||||
}
|
}
|
||||||
|
|
||||||
dotenv.config({
|
const generateURL: GenerateURL<Post | Page> = ({ doc }) => {
|
||||||
path: path.resolve(dirname, '../../.env'),
|
return doc?.slug
|
||||||
})
|
? `${process.env.NEXT_PUBLIC_SERVER_URL}/${doc.slug}`
|
||||||
|
: process.env.NEXT_PUBLIC_SERVER_URL
|
||||||
|
}
|
||||||
|
|
||||||
export default buildConfig({
|
export default buildConfig({
|
||||||
admin: {
|
admin: {
|
||||||
@@ -133,10 +136,8 @@ export default buildConfig({
|
|||||||
collections: ['categories'],
|
collections: ['categories'],
|
||||||
}),
|
}),
|
||||||
seoPlugin({
|
seoPlugin({
|
||||||
collections: ['pages', 'posts'],
|
|
||||||
generateTitle,
|
generateTitle,
|
||||||
tabbedUI: true,
|
generateURL,
|
||||||
uploadsCollection: 'media',
|
|
||||||
}),
|
}),
|
||||||
formBuilderPlugin({
|
formBuilderPlugin({
|
||||||
fields: {
|
fields: {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ const Categories: CollectionConfig = {
|
|||||||
{
|
{
|
||||||
name: 'title',
|
name: 'title',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export const Media: CollectionConfig = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
upload: {
|
upload: {
|
||||||
staticDir: path.resolve(dirname, '../../public/media'),
|
// Upload to the public/media directory in Next.js making them publicly accessible even outside of Payload
|
||||||
|
staticDir: path.resolve(dirname, '../../../public/media'),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,13 @@ import { populatePublishedAt } from '../../hooks/populatePublishedAt'
|
|||||||
import { generatePreviewPath } from '../../utilities/generatePreviewPath'
|
import { generatePreviewPath } from '../../utilities/generatePreviewPath'
|
||||||
import { revalidatePage } from './hooks/revalidatePage'
|
import { revalidatePage } from './hooks/revalidatePage'
|
||||||
|
|
||||||
|
import {
|
||||||
|
MetaDescriptionField,
|
||||||
|
MetaImageField,
|
||||||
|
MetaTitleField,
|
||||||
|
OverviewField,
|
||||||
|
PreviewField,
|
||||||
|
} from '@payloadcms/plugin-seo/fields'
|
||||||
export const Pages: CollectionConfig = {
|
export const Pages: CollectionConfig = {
|
||||||
slug: 'pages',
|
slug: 'pages',
|
||||||
access: {
|
access: {
|
||||||
@@ -35,19 +42,17 @@ export const Pages: CollectionConfig = {
|
|||||||
generatePreviewPath({ path: `/${typeof doc?.slug === 'string' ? doc.slug : ''}` }),
|
generatePreviewPath({ path: `/${typeof doc?.slug === 'string' ? doc.slug : ''}` }),
|
||||||
useAsTitle: 'title',
|
useAsTitle: 'title',
|
||||||
},
|
},
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
type: 'tabs',
|
|
||||||
tabs: [
|
|
||||||
{
|
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'title',
|
name: 'title',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
hero,
|
{
|
||||||
],
|
type: 'tabs',
|
||||||
|
tabs: [
|
||||||
|
{
|
||||||
|
fields: [hero],
|
||||||
label: 'Hero',
|
label: 'Hero',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -61,6 +66,33 @@ export const Pages: CollectionConfig = {
|
|||||||
],
|
],
|
||||||
label: 'Content',
|
label: 'Content',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'meta',
|
||||||
|
label: 'SEO',
|
||||||
|
fields: [
|
||||||
|
OverviewField({
|
||||||
|
titlePath: 'meta.title',
|
||||||
|
descriptionPath: 'meta.description',
|
||||||
|
imagePath: 'meta.image',
|
||||||
|
}),
|
||||||
|
MetaTitleField({
|
||||||
|
hasGenerateFn: true,
|
||||||
|
}),
|
||||||
|
MetaImageField({
|
||||||
|
relationTo: 'media',
|
||||||
|
}),
|
||||||
|
|
||||||
|
MetaDescriptionField({}),
|
||||||
|
PreviewField({
|
||||||
|
// if the `generateUrl` function is configured
|
||||||
|
hasGenerateFn: true,
|
||||||
|
|
||||||
|
// field paths to match the target field for data
|
||||||
|
titlePath: 'meta.title',
|
||||||
|
descriptionPath: 'meta.description',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -79,7 +111,7 @@ export const Pages: CollectionConfig = {
|
|||||||
versions: {
|
versions: {
|
||||||
drafts: {
|
drafts: {
|
||||||
autosave: {
|
autosave: {
|
||||||
interval: 350, // We set this interval for optimal live preview
|
interval: 100, // We set this interval for optimal live preview
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
maxPerDoc: 50,
|
maxPerDoc: 50,
|
||||||
|
|||||||
@@ -4,18 +4,20 @@ import type { CollectionAfterReadHook } from 'payload'
|
|||||||
// This means that we need to populate the authors manually here to protect user privacy
|
// This means that we need to populate the authors manually here to protect user privacy
|
||||||
// GraphQL will not return mutated user data that differs from the underlying schema
|
// GraphQL will not return mutated user data that differs from the underlying schema
|
||||||
// So we use an alternative `populatedAuthors` field to populate the user data, hidden from the admin UI
|
// So we use an alternative `populatedAuthors` field to populate the user data, hidden from the admin UI
|
||||||
export const populateAuthors: CollectionAfterReadHook = async ({ doc, req: { payload } }) => {
|
export const populateAuthors: CollectionAfterReadHook = async ({ doc, req, req: { payload } }) => {
|
||||||
if (doc?.authors) {
|
if (doc?.authors) {
|
||||||
const authorDocs = await Promise.all(
|
const authorDocs = []
|
||||||
doc.authors.map(
|
|
||||||
async (author) =>
|
for (const author of doc.authors) {
|
||||||
await payload.findByID({
|
const authorDoc = await payload.findByID({
|
||||||
id: typeof author === 'object' ? author?.id : author,
|
id: typeof author === 'object' ? author?.id : author,
|
||||||
collection: 'users',
|
collection: 'users',
|
||||||
depth: 0,
|
depth: 0,
|
||||||
}),
|
req,
|
||||||
),
|
})
|
||||||
)
|
|
||||||
|
authorDocs.push(authorDoc)
|
||||||
|
}
|
||||||
|
|
||||||
doc.populatedAuthors = authorDocs.map((authorDoc) => ({
|
doc.populatedAuthors = authorDocs.map((authorDoc) => ({
|
||||||
id: authorDoc.id,
|
id: authorDoc.id,
|
||||||
|
|||||||
@@ -19,6 +19,14 @@ import { generatePreviewPath } from '../../utilities/generatePreviewPath'
|
|||||||
import { populateAuthors } from './hooks/populateAuthors'
|
import { populateAuthors } from './hooks/populateAuthors'
|
||||||
import { revalidatePost } from './hooks/revalidatePost'
|
import { revalidatePost } from './hooks/revalidatePost'
|
||||||
|
|
||||||
|
import {
|
||||||
|
MetaDescriptionField,
|
||||||
|
MetaImageField,
|
||||||
|
MetaTitleField,
|
||||||
|
OverviewField,
|
||||||
|
PreviewField,
|
||||||
|
} from '@payloadcms/plugin-seo/fields'
|
||||||
|
|
||||||
export const Posts: CollectionConfig = {
|
export const Posts: CollectionConfig = {
|
||||||
slug: 'posts',
|
slug: 'posts',
|
||||||
access: {
|
access: {
|
||||||
@@ -42,6 +50,11 @@ export const Posts: CollectionConfig = {
|
|||||||
useAsTitle: 'title',
|
useAsTitle: 'title',
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
type: 'text',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'tabs',
|
type: 'tabs',
|
||||||
tabs: [
|
tabs: [
|
||||||
@@ -98,15 +111,34 @@ export const Posts: CollectionConfig = {
|
|||||||
],
|
],
|
||||||
label: 'Meta',
|
label: 'Meta',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'meta',
|
||||||
|
label: 'SEO',
|
||||||
|
fields: [
|
||||||
|
OverviewField({
|
||||||
|
titlePath: 'meta.title',
|
||||||
|
descriptionPath: 'meta.description',
|
||||||
|
imagePath: 'meta.image',
|
||||||
|
}),
|
||||||
|
MetaTitleField({
|
||||||
|
hasGenerateFn: true,
|
||||||
|
}),
|
||||||
|
MetaImageField({
|
||||||
|
relationTo: 'media',
|
||||||
|
}),
|
||||||
|
|
||||||
|
MetaDescriptionField({}),
|
||||||
|
PreviewField({
|
||||||
|
// if the `generateUrl` function is configured
|
||||||
|
hasGenerateFn: true,
|
||||||
|
|
||||||
|
// field paths to match the target field for data
|
||||||
|
titlePath: 'meta.title',
|
||||||
|
descriptionPath: 'meta.description',
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
],
|
||||||
name: 'title',
|
|
||||||
type: 'text',
|
|
||||||
admin: {
|
|
||||||
position: 'sidebar',
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'publishedAt',
|
name: 'publishedAt',
|
||||||
@@ -169,7 +201,9 @@ export const Posts: CollectionConfig = {
|
|||||||
},
|
},
|
||||||
versions: {
|
versions: {
|
||||||
drafts: {
|
drafts: {
|
||||||
autosave: true,
|
autosave: {
|
||||||
|
interval: 100, // We set this interval for optimal live preview
|
||||||
|
},
|
||||||
},
|
},
|
||||||
maxPerDoc: 50,
|
maxPerDoc: 50,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,16 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import React, { Fragment, useCallback, useState } from 'react'
|
import React, { Fragment, useCallback, useState } from 'react'
|
||||||
|
import { toast } from '@payloadcms/ui'
|
||||||
|
|
||||||
|
const SuccessMessage: React.FC = () => (
|
||||||
|
<div>
|
||||||
|
Database seeded! You can now{' '}
|
||||||
|
<a target="_blank" href="/">
|
||||||
|
visit your website
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
export const SeedButton: React.FC = () => {
|
export const SeedButton: React.FC = () => {
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
@@ -17,6 +27,7 @@ export const SeedButton: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
await fetch('/api/seed')
|
await fetch('/api/seed')
|
||||||
setSeeded(true)
|
setSeeded(true)
|
||||||
|
toast.success(<SuccessMessage />, { duration: 5000 })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err)
|
setError(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ const BeforeDashboard: React.FC = () => {
|
|||||||
<li>
|
<li>
|
||||||
<SeedButton />
|
<SeedButton />
|
||||||
{' with a few pages, posts, and projects to jump-start your new site, then '}
|
{' with a few pages, posts, and projects to jump-start your new site, then '}
|
||||||
<a href="/">visit your website</a>
|
<a href="/" target="_blank">
|
||||||
|
visit your website
|
||||||
|
</a>
|
||||||
{' to see the results.'}
|
{' to see the results.'}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
@@ -10,9 +10,14 @@ export const seed: PayloadHandler = async (req): Promise<Response> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Create a transaction so that all seeding happens in one transaction
|
||||||
await initTransaction(req)
|
await initTransaction(req)
|
||||||
|
|
||||||
await seedScript({ payload, req })
|
await seedScript({ payload, req })
|
||||||
|
|
||||||
|
// Finalise transactiojn
|
||||||
await commitTransaction(req)
|
await commitTransaction(req)
|
||||||
|
|
||||||
return Response.json({ success: true })
|
return Response.json({ success: true })
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
const message = error instanceof Error ? error.message : 'Unknown error'
|
const message = error instanceof Error ? error.message : 'Unknown error'
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export const contact: Partial<Page> = {
|
|||||||
{
|
{
|
||||||
blockType: 'formBlock',
|
blockType: 'formBlock',
|
||||||
enableIntro: true,
|
enableIntro: true,
|
||||||
|
// @ts-ignore
|
||||||
form: '{{CONTACT_FORM_ID}}',
|
form: '{{CONTACT_FORM_ID}}',
|
||||||
introContent: {
|
introContent: {
|
||||||
root: {
|
root: {
|
||||||
|
|||||||
@@ -80,552 +80,6 @@ export const homeStatic: Page = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
/* layout: [
|
|
||||||
{
|
|
||||||
blockName: 'Content Block',
|
|
||||||
blockType: 'content',
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
richText: {
|
|
||||||
root: {
|
|
||||||
type: 'root',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'heading',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'Core features',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
tag: 'h2',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
size: 'full',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enableLink: false,
|
|
||||||
link: {
|
|
||||||
label: '',
|
|
||||||
reference: null,
|
|
||||||
url: '',
|
|
||||||
},
|
|
||||||
richText: {
|
|
||||||
root: {
|
|
||||||
type: 'root',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'heading',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'Admin Dashboard',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
tag: 'h3',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'paragraph',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: "Manage this site's pages and posts from the ",
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'link',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'admin dashboard',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
fields: {
|
|
||||||
linkType: 'custom',
|
|
||||||
newTab: false,
|
|
||||||
url: '/admin',
|
|
||||||
},
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
version: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: '.',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
textFormat: 0,
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
size: 'oneThird',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enableLink: false,
|
|
||||||
link: {
|
|
||||||
label: '',
|
|
||||||
reference: null,
|
|
||||||
url: '',
|
|
||||||
},
|
|
||||||
richText: {
|
|
||||||
root: {
|
|
||||||
type: 'root',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'heading',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'Preview',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
tag: 'h3',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'paragraph',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'Using versions, drafts, and preview, editors can review and share their changes before publishing them.',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
textFormat: 0,
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
size: 'oneThird',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enableLink: false,
|
|
||||||
link: {
|
|
||||||
label: '',
|
|
||||||
reference: null,
|
|
||||||
url: '',
|
|
||||||
},
|
|
||||||
richText: {
|
|
||||||
root: {
|
|
||||||
type: 'root',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'heading',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'Page Builder',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
tag: 'h3',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'paragraph',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'Custom page builder allows you to create unique page, post, and project layouts for any type of content.',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
textFormat: 0,
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
size: 'oneThird',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enableLink: false,
|
|
||||||
link: {
|
|
||||||
label: '',
|
|
||||||
reference: null,
|
|
||||||
url: '',
|
|
||||||
},
|
|
||||||
richText: {
|
|
||||||
root: {
|
|
||||||
type: 'root',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'heading',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'SEO',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
tag: 'h3',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'paragraph',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'Editors have complete control over SEO data and site content directly from the ',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'link',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'admin dashboard',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
fields: {
|
|
||||||
linkType: 'custom',
|
|
||||||
newTab: false,
|
|
||||||
url: '/admin',
|
|
||||||
},
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
version: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: '.',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
textFormat: 0,
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
size: 'oneThird',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enableLink: false,
|
|
||||||
link: {
|
|
||||||
label: '',
|
|
||||||
reference: null,
|
|
||||||
url: '',
|
|
||||||
},
|
|
||||||
richText: {
|
|
||||||
root: {
|
|
||||||
type: 'root',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'heading',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'Dark Mode',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
tag: 'h3',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'paragraph',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'Users will experience this site in their preferred color scheme and each block can be inverted.',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
textFormat: 0,
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
size: 'oneThird',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
blockName: 'Archive Block',
|
|
||||||
blockType: 'archive',
|
|
||||||
categories: [],
|
|
||||||
introContent: {
|
|
||||||
root: {
|
|
||||||
type: 'root',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'heading',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'Recent posts',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
tag: 'h3',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'paragraph',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'The posts below are displayed in an "Archive" layout building block which is an extremely powerful way to display documents on a page. It can be auto-populated by collection or by category, or posts can be individually selected. Pagination controls will automatically appear if the number of results exceeds the number of items per page.',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
textFormat: 0,
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
populateBy: 'collection',
|
|
||||||
relationTo: 'posts',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
blockName: 'CTA',
|
|
||||||
blockType: 'cta',
|
|
||||||
links: [
|
|
||||||
{
|
|
||||||
link: {
|
|
||||||
type: 'custom',
|
|
||||||
appearance: 'default',
|
|
||||||
label: 'All posts',
|
|
||||||
url: '/posts',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
richText: {
|
|
||||||
root: {
|
|
||||||
type: 'root',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'heading',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'This is a call to action',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
tag: 'h3',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'paragraph',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'This is a custom layout building block ',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'link',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'configured in the admin dashboard',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
fields: {
|
|
||||||
linkType: 'custom',
|
|
||||||
newTab: false,
|
|
||||||
url: '/admin',
|
|
||||||
},
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
version: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: '.',
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
textFormat: 0,
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
direction: 'ltr',
|
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
], */
|
|
||||||
meta: {
|
meta: {
|
||||||
description: 'An open-source website built with Payload and Next.js.',
|
description: 'An open-source website built with Payload and Next.js.',
|
||||||
title: 'Payload Website Template',
|
title: 'Payload Website Template',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Page } from '../../payload-types'
|
import type { RequiredDataFromCollectionSlug } from 'payload'
|
||||||
|
|
||||||
export const home: Partial<Page> = {
|
export const home: RequiredDataFromCollectionSlug<'pages'> = {
|
||||||
slug: 'home',
|
slug: 'home',
|
||||||
_status: 'published',
|
_status: 'published',
|
||||||
hero: {
|
hero: {
|
||||||
@@ -23,6 +23,7 @@ export const home: Partial<Page> = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
// @ts-ignore
|
||||||
media: '{{IMAGE_1}}',
|
media: '{{IMAGE_1}}',
|
||||||
richText: {
|
richText: {
|
||||||
root: {
|
root: {
|
||||||
@@ -501,6 +502,7 @@ export const home: Partial<Page> = {
|
|||||||
{
|
{
|
||||||
blockName: 'Media Block',
|
blockName: 'Media Block',
|
||||||
blockType: 'mediaBlock',
|
blockType: 'mediaBlock',
|
||||||
|
// @ts-ignore
|
||||||
media: '{{IMAGE_2}}',
|
media: '{{IMAGE_2}}',
|
||||||
position: 'default',
|
position: 'default',
|
||||||
},
|
},
|
||||||
@@ -658,6 +660,7 @@ export const home: Partial<Page> = {
|
|||||||
],
|
],
|
||||||
meta: {
|
meta: {
|
||||||
description: 'An open-source website built with Payload and Next.js.',
|
description: 'An open-source website built with Payload and Next.js.',
|
||||||
|
// @ts-ignore
|
||||||
image: '{{IMAGE_1}}',
|
image: '{{IMAGE_1}}',
|
||||||
title: 'Payload Website Template',
|
title: 'Payload Website Template',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Payload, PayloadRequest } from 'payload'
|
import type { CollectionSlug, GlobalSlug, Payload, PayloadRequest } from 'payload'
|
||||||
|
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
@@ -12,12 +12,20 @@ import { image2 } from './image-2'
|
|||||||
import { post1 } from './post-1'
|
import { post1 } from './post-1'
|
||||||
import { post2 } from './post-2'
|
import { post2 } from './post-2'
|
||||||
import { post3 } from './post-3'
|
import { post3 } from './post-3'
|
||||||
|
import { exists } from 'fs-extra'
|
||||||
|
|
||||||
const filename = fileURLToPath(import.meta.url)
|
const filename = fileURLToPath(import.meta.url)
|
||||||
const dirname = path.dirname(filename)
|
const dirname = path.dirname(filename)
|
||||||
|
|
||||||
const collections = ['categories', 'media', 'pages', 'posts', 'forms', 'form-submissions']
|
const collections: CollectionSlug[] = [
|
||||||
const globals = ['header', 'footer']
|
'categories',
|
||||||
|
'media',
|
||||||
|
'pages',
|
||||||
|
'posts',
|
||||||
|
'forms',
|
||||||
|
'form-submissions',
|
||||||
|
]
|
||||||
|
const globals: GlobalSlug[] = ['header', 'footer']
|
||||||
|
|
||||||
// Next.js revalidation errors are normal when seeding the database without a server running
|
// Next.js revalidation errors are normal when seeding the database without a server running
|
||||||
// i.e. running `yarn seed` locally instead of using the admin UI within an active app
|
// i.e. running `yarn seed` locally instead of using the admin UI within an active app
|
||||||
@@ -47,41 +55,50 @@ export const seed = async ({
|
|||||||
payload.logger.info(`— Clearing collections and globals...`)
|
payload.logger.info(`— Clearing collections and globals...`)
|
||||||
|
|
||||||
// clear the database
|
// clear the database
|
||||||
await Promise.all([
|
for (const global of globals) {
|
||||||
...collections.map((collection) =>
|
await payload.updateGlobal({
|
||||||
payload.delete({
|
slug: global,
|
||||||
collection: collection as 'media',
|
data: {
|
||||||
|
navItems: [],
|
||||||
|
},
|
||||||
req,
|
req,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const collection of collections) {
|
||||||
|
console.log('delete', collection)
|
||||||
|
await payload.delete({
|
||||||
|
collection: collection,
|
||||||
|
where: {
|
||||||
|
id: {
|
||||||
|
exists: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
req,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const pages = await payload.delete({
|
||||||
|
collection: 'pages',
|
||||||
where: {},
|
where: {},
|
||||||
}),
|
|
||||||
),
|
|
||||||
...globals.map((global) =>
|
|
||||||
payload.updateGlobal({
|
|
||||||
slug: global as 'header',
|
|
||||||
data: {},
|
|
||||||
req,
|
req,
|
||||||
}),
|
})
|
||||||
),
|
|
||||||
])
|
console.log({ pages })
|
||||||
|
|
||||||
payload.logger.info(`— Seeding demo author and user...`)
|
payload.logger.info(`— Seeding demo author and user...`)
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
['demo-author@payloadcms.com'].map(async (email) => {
|
|
||||||
await payload.delete({
|
await payload.delete({
|
||||||
collection: 'users',
|
collection: 'users',
|
||||||
req,
|
|
||||||
where: {
|
where: {
|
||||||
email: {
|
email: {
|
||||||
equals: email,
|
equals: 'demo-author@payloadcms.com',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
req,
|
||||||
})
|
})
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
const [demoAuthor] = await Promise.all([
|
const demoAuthor = await payload.create({
|
||||||
await payload.create({
|
|
||||||
collection: 'users',
|
collection: 'users',
|
||||||
data: {
|
data: {
|
||||||
name: 'Demo Author',
|
name: 'Demo Author',
|
||||||
@@ -89,91 +106,89 @@ export const seed = async ({
|
|||||||
password: 'password',
|
password: 'password',
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
}),
|
})
|
||||||
])
|
|
||||||
|
|
||||||
let demoAuthorID = demoAuthor.id
|
let demoAuthorID: number | string = demoAuthor.id
|
||||||
|
|
||||||
payload.logger.info(`— Seeding media...`)
|
payload.logger.info(`— Seeding media...`)
|
||||||
|
const image1Doc = await payload.create({
|
||||||
const [image1Doc, image2Doc, image3Doc, imageHomeDoc] = await Promise.all([
|
|
||||||
await payload.create({
|
|
||||||
collection: 'media',
|
collection: 'media',
|
||||||
data: image1,
|
data: image1,
|
||||||
filePath: path.resolve(dirname, 'image-post1.webp'),
|
filePath: path.resolve(dirname, 'image-post1.webp'),
|
||||||
req,
|
req,
|
||||||
}),
|
})
|
||||||
await payload.create({
|
const image2Doc = await payload.create({
|
||||||
collection: 'media',
|
collection: 'media',
|
||||||
data: image2,
|
data: image2,
|
||||||
filePath: path.resolve(dirname, 'image-post2.webp'),
|
filePath: path.resolve(dirname, 'image-post2.webp'),
|
||||||
req,
|
req,
|
||||||
}),
|
})
|
||||||
await payload.create({
|
const image3Doc = await payload.create({
|
||||||
collection: 'media',
|
collection: 'media',
|
||||||
data: image2,
|
data: image2,
|
||||||
filePath: path.resolve(dirname, 'image-post3.webp'),
|
filePath: path.resolve(dirname, 'image-post3.webp'),
|
||||||
req,
|
req,
|
||||||
}),
|
})
|
||||||
await payload.create({
|
const imageHomeDoc = await payload.create({
|
||||||
collection: 'media',
|
collection: 'media',
|
||||||
data: image2,
|
data: image2,
|
||||||
filePath: path.resolve(dirname, 'image-hero1.webp'),
|
filePath: path.resolve(dirname, 'image-hero1.webp'),
|
||||||
req,
|
req,
|
||||||
}),
|
})
|
||||||
])
|
|
||||||
|
|
||||||
payload.logger.info(`— Seeding categories...`)
|
payload.logger.info(`— Seeding categories...`)
|
||||||
|
const technologyCategory = await payload.create({
|
||||||
const [technologyCategory, newsCategory, financeCategory] = await Promise.all([
|
|
||||||
await payload.create({
|
|
||||||
collection: 'categories',
|
collection: 'categories',
|
||||||
data: {
|
data: {
|
||||||
title: 'Technology',
|
title: 'Technology',
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
}),
|
})
|
||||||
await payload.create({
|
|
||||||
|
const newsCategory = await payload.create({
|
||||||
collection: 'categories',
|
collection: 'categories',
|
||||||
data: {
|
data: {
|
||||||
title: 'News',
|
title: 'News',
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
}),
|
})
|
||||||
await payload.create({
|
|
||||||
|
const financeCategory = await payload.create({
|
||||||
collection: 'categories',
|
collection: 'categories',
|
||||||
data: {
|
data: {
|
||||||
title: 'Finance',
|
title: 'Finance',
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
}),
|
})
|
||||||
|
|
||||||
await payload.create({
|
await payload.create({
|
||||||
collection: 'categories',
|
collection: 'categories',
|
||||||
data: {
|
data: {
|
||||||
title: 'Design',
|
title: 'Design',
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
}),
|
})
|
||||||
|
|
||||||
await payload.create({
|
await payload.create({
|
||||||
collection: 'categories',
|
collection: 'categories',
|
||||||
data: {
|
data: {
|
||||||
title: 'Software',
|
title: 'Software',
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
}),
|
})
|
||||||
|
|
||||||
await payload.create({
|
await payload.create({
|
||||||
collection: 'categories',
|
collection: 'categories',
|
||||||
data: {
|
data: {
|
||||||
title: 'Engineering',
|
title: 'Engineering',
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
}),
|
})
|
||||||
])
|
|
||||||
|
|
||||||
let image1ID = image1Doc.id
|
let image1ID: number | string = image1Doc.id
|
||||||
let image2ID = image2Doc.id
|
let image2ID: number | string = image2Doc.id
|
||||||
let image3ID = image3Doc.id
|
let image3ID: number | string = image3Doc.id
|
||||||
let imageHomeID = imageHomeDoc.id
|
let imageHomeID: number | string = imageHomeDoc.id
|
||||||
|
|
||||||
if (payload.db.defaultIDType === 'text') {
|
if (payload.db.defaultIDType === 'text') {
|
||||||
image1ID = `"${image1Doc.id}"`
|
image1ID = `"${image1Doc.id}"`
|
||||||
@@ -191,9 +206,9 @@ export const seed = async ({
|
|||||||
collection: 'posts',
|
collection: 'posts',
|
||||||
data: JSON.parse(
|
data: JSON.parse(
|
||||||
JSON.stringify({ ...post1, categories: [technologyCategory.id] })
|
JSON.stringify({ ...post1, categories: [technologyCategory.id] })
|
||||||
.replace(/"\{\{IMAGE_1\}\}"/g, image1ID)
|
.replace(/"\{\{IMAGE_1\}\}"/g, String(image1ID))
|
||||||
.replace(/"\{\{IMAGE_2\}\}"/g, image2ID)
|
.replace(/"\{\{IMAGE_2\}\}"/g, String(image2ID))
|
||||||
.replace(/"\{\{AUTHOR\}\}"/g, demoAuthorID),
|
.replace(/"\{\{AUTHOR\}\}"/g, String(demoAuthorID)),
|
||||||
),
|
),
|
||||||
req,
|
req,
|
||||||
})
|
})
|
||||||
@@ -202,9 +217,9 @@ export const seed = async ({
|
|||||||
collection: 'posts',
|
collection: 'posts',
|
||||||
data: JSON.parse(
|
data: JSON.parse(
|
||||||
JSON.stringify({ ...post2, categories: [newsCategory.id] })
|
JSON.stringify({ ...post2, categories: [newsCategory.id] })
|
||||||
.replace(/"\{\{IMAGE_1\}\}"/g, image2ID)
|
.replace(/"\{\{IMAGE_1\}\}"/g, String(image2ID))
|
||||||
.replace(/"\{\{IMAGE_2\}\}"/g, image3ID)
|
.replace(/"\{\{IMAGE_2\}\}"/g, String(image3ID))
|
||||||
.replace(/"\{\{AUTHOR\}\}"/g, demoAuthorID),
|
.replace(/"\{\{AUTHOR\}\}"/g, String(demoAuthorID)),
|
||||||
),
|
),
|
||||||
req,
|
req,
|
||||||
})
|
})
|
||||||
@@ -213,16 +228,14 @@ export const seed = async ({
|
|||||||
collection: 'posts',
|
collection: 'posts',
|
||||||
data: JSON.parse(
|
data: JSON.parse(
|
||||||
JSON.stringify({ ...post3, categories: [financeCategory.id] })
|
JSON.stringify({ ...post3, categories: [financeCategory.id] })
|
||||||
.replace(/"\{\{IMAGE_1\}\}"/g, image3ID)
|
.replace(/"\{\{IMAGE_1\}\}"/g, String(image3ID))
|
||||||
.replace(/"\{\{IMAGE_2\}\}"/g, image1ID)
|
.replace(/"\{\{IMAGE_2\}\}"/g, String(image1ID))
|
||||||
.replace(/"\{\{AUTHOR\}\}"/g, demoAuthorID),
|
.replace(/"\{\{AUTHOR\}\}"/g, String(demoAuthorID)),
|
||||||
),
|
),
|
||||||
req,
|
req,
|
||||||
})
|
})
|
||||||
|
|
||||||
// update each post with related posts
|
// update each post with related posts
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
await payload.update({
|
await payload.update({
|
||||||
id: post1Doc.id,
|
id: post1Doc.id,
|
||||||
collection: 'posts',
|
collection: 'posts',
|
||||||
@@ -230,7 +243,7 @@ export const seed = async ({
|
|||||||
relatedPosts: [post2Doc.id, post3Doc.id],
|
relatedPosts: [post2Doc.id, post3Doc.id],
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
}),
|
})
|
||||||
await payload.update({
|
await payload.update({
|
||||||
id: post2Doc.id,
|
id: post2Doc.id,
|
||||||
collection: 'posts',
|
collection: 'posts',
|
||||||
@@ -238,7 +251,7 @@ export const seed = async ({
|
|||||||
relatedPosts: [post1Doc.id, post3Doc.id],
|
relatedPosts: [post1Doc.id, post3Doc.id],
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
}),
|
})
|
||||||
await payload.update({
|
await payload.update({
|
||||||
id: post3Doc.id,
|
id: post3Doc.id,
|
||||||
collection: 'posts',
|
collection: 'posts',
|
||||||
@@ -246,8 +259,7 @@ export const seed = async ({
|
|||||||
relatedPosts: [post1Doc.id, post2Doc.id],
|
relatedPosts: [post1Doc.id, post2Doc.id],
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
}),
|
})
|
||||||
])
|
|
||||||
|
|
||||||
payload.logger.info(`— Seeding home page...`)
|
payload.logger.info(`— Seeding home page...`)
|
||||||
|
|
||||||
@@ -255,8 +267,8 @@ export const seed = async ({
|
|||||||
collection: 'pages',
|
collection: 'pages',
|
||||||
data: JSON.parse(
|
data: JSON.parse(
|
||||||
JSON.stringify(home)
|
JSON.stringify(home)
|
||||||
.replace(/"\{\{IMAGE_1\}\}"/g, imageHomeID)
|
.replace(/"\{\{IMAGE_1\}\}"/g, String(imageHomeID))
|
||||||
.replace(/"\{\{IMAGE_2\}\}"/g, image2ID),
|
.replace(/"\{\{IMAGE_2\}\}"/g, String(image2ID)),
|
||||||
),
|
),
|
||||||
req,
|
req,
|
||||||
})
|
})
|
||||||
@@ -269,7 +281,7 @@ export const seed = async ({
|
|||||||
req,
|
req,
|
||||||
})
|
})
|
||||||
|
|
||||||
let contactFormID = contactForm.id
|
let contactFormID: number | string = contactForm.id
|
||||||
|
|
||||||
if (payload.db.defaultIDType === 'text') {
|
if (payload.db.defaultIDType === 'text') {
|
||||||
contactFormID = `"${contactFormID}"`
|
contactFormID = `"${contactFormID}"`
|
||||||
@@ -280,7 +292,7 @@ export const seed = async ({
|
|||||||
const contactPage = await payload.create({
|
const contactPage = await payload.create({
|
||||||
collection: 'pages',
|
collection: 'pages',
|
||||||
data: JSON.parse(
|
data: JSON.parse(
|
||||||
JSON.stringify(contactPageData).replace(/"\{\{CONTACT_FORM_ID\}\}"/g, contactFormID),
|
JSON.stringify(contactPageData).replace(/"\{\{CONTACT_FORM_ID\}\}"/g, String(contactFormID)),
|
||||||
),
|
),
|
||||||
req,
|
req,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { Post } from '../../payload-types'
|
|||||||
export const post1: Partial<Post> = {
|
export const post1: Partial<Post> = {
|
||||||
slug: 'digital-horizons',
|
slug: 'digital-horizons',
|
||||||
_status: 'published',
|
_status: 'published',
|
||||||
|
// @ts-ignore
|
||||||
authors: ['{{AUTHOR}}'],
|
authors: ['{{AUTHOR}}'],
|
||||||
content: {
|
content: {
|
||||||
root: {
|
root: {
|
||||||
@@ -295,6 +296,7 @@ export const post1: Partial<Post> = {
|
|||||||
meta: {
|
meta: {
|
||||||
description:
|
description:
|
||||||
'Dive into the marvels of modern innovation, where the only constant is change. A journey where pixels and data converge to craft the future.',
|
'Dive into the marvels of modern innovation, where the only constant is change. A journey where pixels and data converge to craft the future.',
|
||||||
|
// @ts-ignore
|
||||||
image: '{{IMAGE_1}}',
|
image: '{{IMAGE_1}}',
|
||||||
title: 'Digital Horizons: A Glimpse into Tomorrow',
|
title: 'Digital Horizons: A Glimpse into Tomorrow',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { Post } from '../../payload-types'
|
|||||||
export const post2: Partial<Post> = {
|
export const post2: Partial<Post> = {
|
||||||
slug: 'global-gaze',
|
slug: 'global-gaze',
|
||||||
_status: 'published',
|
_status: 'published',
|
||||||
|
// @ts-ignore
|
||||||
authors: ['{{AUTHOR}}'],
|
authors: ['{{AUTHOR}}'],
|
||||||
content: {
|
content: {
|
||||||
root: {
|
root: {
|
||||||
@@ -217,6 +218,7 @@ export const post2: Partial<Post> = {
|
|||||||
meta: {
|
meta: {
|
||||||
description:
|
description:
|
||||||
'Explore the untold and overlooked. A magnified view into the corners of the world, where every story deserves its spotlight.',
|
'Explore the untold and overlooked. A magnified view into the corners of the world, where every story deserves its spotlight.',
|
||||||
|
// @ts-ignore
|
||||||
image: '{{IMAGE_1}}',
|
image: '{{IMAGE_1}}',
|
||||||
title: 'Global Gaze: Beyond the Headlines',
|
title: 'Global Gaze: Beyond the Headlines',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { Post } from '../../payload-types'
|
|||||||
export const post3: Partial<Post> = {
|
export const post3: Partial<Post> = {
|
||||||
slug: 'dollar-and-sense-the-financial-forecast',
|
slug: 'dollar-and-sense-the-financial-forecast',
|
||||||
_status: 'published',
|
_status: 'published',
|
||||||
|
// @ts-ignore
|
||||||
authors: ['{{AUTHOR}}'],
|
authors: ['{{AUTHOR}}'],
|
||||||
content: {
|
content: {
|
||||||
root: {
|
root: {
|
||||||
@@ -253,6 +254,7 @@ export const post3: Partial<Post> = {
|
|||||||
},
|
},
|
||||||
meta: {
|
meta: {
|
||||||
description: `Money isn't just currency; it's a language. Dive deep into its nuances, where strategy meets intuition in the vast sea of finance.`,
|
description: `Money isn't just currency; it's a language. Dive deep into its nuances, where strategy meets intuition in the vast sea of finance.`,
|
||||||
|
// @ts-ignore
|
||||||
image: '{{IMAGE_1}}',
|
image: '{{IMAGE_1}}',
|
||||||
title: 'Dollar and Sense: The Financial Forecast',
|
title: 'Dollar and Sense: The Financial Forecast',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,8 +2,12 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"target": "es5",
|
"target": "es6",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"esnext"
|
||||||
|
],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": false,
|
"strict": false,
|
||||||
@@ -22,15 +26,25 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@payload-config": ["./src/payload.config.ts"],
|
"@payload-config": [
|
||||||
"react": ["./node_modules/@types/react"],
|
"./src/payload.config.ts"
|
||||||
"@/*": ["./src/app/*"]
|
],
|
||||||
|
"react": [
|
||||||
|
"./node_modules/@types/react"
|
||||||
|
],
|
||||||
|
"@/*": [
|
||||||
|
"./src/app/*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "redirects.js", "next.config.js"],
|
"include": [
|
||||||
"exclude": ["node_modules"],
|
"**/*.ts",
|
||||||
"ts-node": {
|
"**/*.tsx",
|
||||||
"transpileOnly": true,
|
".next/types/**/*.ts",
|
||||||
"swc": true
|
"redirects.js",
|
||||||
}
|
"next.config.js"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,8 @@
|
|||||||
"graphql": "^16.8.1",
|
"graphql": "^16.8.1",
|
||||||
"next": "15.0.0-canary.53",
|
"next": "15.0.0-canary.53",
|
||||||
"payload": "beta",
|
"payload": "beta",
|
||||||
"react": "^19.0.0-rc-6230622a1a-20240610",
|
"react": "19.0.0-rc-6230622a1a-20240610",
|
||||||
"react-dom": "^19.0.0-rc-6230622a1a-20240610",
|
"react-dom": "19.0.0-rc-6230622a1a-20240610",
|
||||||
"sharp": "0.32.6"
|
"sharp": "0.32.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -23,8 +23,8 @@
|
|||||||
"graphql": "^16.8.1",
|
"graphql": "^16.8.1",
|
||||||
"next": "15.0.0-canary.53",
|
"next": "15.0.0-canary.53",
|
||||||
"payload": "beta",
|
"payload": "beta",
|
||||||
"react": "^19.0.0-rc-6230622a1a-20240610",
|
"react": "19.0.0-rc-6230622a1a-20240610",
|
||||||
"react-dom": "^19.0.0-rc-6230622a1a-20240610"
|
"react-dom": "19.0.0-rc-6230622a1a-20240610"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.14.9",
|
"@types/node": "^20.14.9",
|
||||||
|
|||||||
@@ -23,8 +23,8 @@
|
|||||||
"graphql": "^16.8.1",
|
"graphql": "^16.8.1",
|
||||||
"next": "15.0.0-canary.53",
|
"next": "15.0.0-canary.53",
|
||||||
"payload": "beta",
|
"payload": "beta",
|
||||||
"react": "^19.0.0-rc-6230622a1a-20240610",
|
"react": "19.0.0-rc-6230622a1a-20240610",
|
||||||
"react-dom": "^19.0.0-rc-6230622a1a-20240610"
|
"react-dom": "19.0.0-rc-6230622a1a-20240610"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.14.9",
|
"@types/node": "^20.14.9",
|
||||||
|
|||||||
Reference in New Issue
Block a user