Merge branch 'alpha' into temp38
This commit is contained in:
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@@ -226,8 +226,8 @@ jobs:
|
||||
- fields/lexical
|
||||
- live-preview
|
||||
- localization
|
||||
# - plugin-nested-docs
|
||||
# - plugin-seo
|
||||
- plugin-nested-docs
|
||||
- plugin-seo
|
||||
# - refresh-permissions
|
||||
# - uploads
|
||||
- versions
|
||||
|
||||
@@ -1,11 +1,32 @@
|
||||
import baseConfig from '../../jest.config.js'
|
||||
// import baseConfig from '../../jest.config.js'
|
||||
|
||||
/** @type {import('@jest/types').Config} */
|
||||
// /** @type {import('@jest/types').Config} */
|
||||
// const customJestConfig = {
|
||||
// ...baseConfig,
|
||||
// setupFilesAfterEnv: null,
|
||||
// testMatch: ['**/src/**/?(*.)+(spec|test|it-test).[tj]s?(x)'],
|
||||
// testTimeout: 20000,
|
||||
// }
|
||||
|
||||
// export default customJestConfig
|
||||
|
||||
/** @type {import('jest').Config} */
|
||||
const customJestConfig = {
|
||||
...baseConfig,
|
||||
setupFilesAfterEnv: null,
|
||||
testMatch: ['**/src/**/?(*.)+(spec|test|it-test).[tj]s?(x)'],
|
||||
testTimeout: 20000,
|
||||
extensionsToTreatAsEsm: ['.ts', '.tsx'],
|
||||
// setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
|
||||
moduleNameMapper: {
|
||||
'\\.(css|scss)$': '<rootDir>/helpers/mocks/emptyModule.js',
|
||||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
|
||||
'<rootDir>/test/helpers/mocks/fileMock.js',
|
||||
'^(\\.{1,2}/.*)\\.js$': '$1',
|
||||
},
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['<rootDir>/**/*spec.ts'],
|
||||
testTimeout: 90000,
|
||||
transform: {
|
||||
'^.+\\.(t|j)sx?$': ['@swc/jest'],
|
||||
},
|
||||
verbose: true,
|
||||
}
|
||||
|
||||
export default customJestConfig
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
"create-payload-app": "bin/cli.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm copyfiles && pnpm typecheck && pnpm build:swc",
|
||||
"build": "pnpm pack-template-files && pnpm typecheck && pnpm build:swc",
|
||||
"typecheck": "tsc",
|
||||
"copyfiles": "copyfiles -u 2 \"../../app/(payload)/**\" \"dist\"",
|
||||
"pack-template-files": "tsx src/scripts/pack-template-files.ts",
|
||||
"build:swc": "swc ./src -d ./dist --config-file .swcrc",
|
||||
"clean": "rimraf {dist,*.tsbuildinfo}",
|
||||
"test": "jest",
|
||||
@@ -21,6 +21,7 @@
|
||||
"bin"
|
||||
],
|
||||
"dependencies": {
|
||||
"@clack/prompts": "^0.7.0",
|
||||
"@sindresorhus/slugify": "^1.1.0",
|
||||
"arg": "^5.0.0",
|
||||
"chalk": "^4.1.0",
|
||||
@@ -33,9 +34,6 @@
|
||||
"figures": "^3.2.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"globby": "11.1.0",
|
||||
"handlebars": "^4.7.7",
|
||||
"ora": "^5.1.0",
|
||||
"prompts": "^2.4.2",
|
||||
"terminal-link": "^2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -44,8 +42,7 @@
|
||||
"@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"
|
||||
"@types/node": "^16.6.2"
|
||||
},
|
||||
"exports": {
|
||||
"./commands": {
|
||||
|
||||
@@ -9,19 +9,27 @@ import { dbReplacements } from './packages.js'
|
||||
/** Update payload config with necessary imports and adapters */
|
||||
export async function configurePayloadConfig(args: {
|
||||
dbDetails: DbDetails | undefined
|
||||
projectDir: string
|
||||
projectDirOrConfigPath: { payloadConfigPath: string } | { projectDir: string }
|
||||
}): Promise<void> {
|
||||
if (!args.dbDetails) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const payloadConfigPath = (
|
||||
await globby('**/payload.config.ts', { absolute: true, cwd: args.projectDir })
|
||||
let payloadConfigPath: string | undefined
|
||||
if (!('payloadConfigPath' in args.projectDirOrConfigPath)) {
|
||||
payloadConfigPath = (
|
||||
await globby('**/payload.config.ts', {
|
||||
absolute: true,
|
||||
cwd: args.projectDirOrConfigPath.projectDir,
|
||||
})
|
||||
)?.[0]
|
||||
} else {
|
||||
payloadConfigPath = args.projectDirOrConfigPath.payloadConfigPath
|
||||
}
|
||||
|
||||
if (!payloadConfigPath) {
|
||||
warning('Unable to update payload.config.ts with plugins')
|
||||
warning('Unable to update payload.config.ts with plugins. Could not find payload.config.ts.')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -59,6 +67,8 @@ export async function configurePayloadConfig(args: {
|
||||
|
||||
fse.writeFileSync(payloadConfigPath, configLines.join('\n'))
|
||||
} catch (err: unknown) {
|
||||
warning('Unable to update payload.config.ts with plugins')
|
||||
warning(
|
||||
`Unable to update payload.config.ts with plugins: ${err instanceof Error ? err.message : ''}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { createProject } from './create-project.js'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { dbReplacements } from './packages.js'
|
||||
import { getValidTemplates } from './templates.js'
|
||||
import globby from 'globby'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
@@ -104,12 +105,17 @@ describe('createProject', () => {
|
||||
Object.keys(packageJson.dependencies).filter((n) => n.startsWith('@payloadcms/db-')),
|
||||
).toHaveLength(1)
|
||||
|
||||
let payloadConfigPath = path.resolve(projectDir, 'payload.config.ts')
|
||||
const payloadConfigPath = (
|
||||
await globby('**/payload.config.ts', {
|
||||
absolute: true,
|
||||
cwd: projectDir,
|
||||
})
|
||||
)?.[0]
|
||||
|
||||
// Website and ecommerce templates have payload.config.ts in src/payload
|
||||
if (!fse.existsSync(payloadConfigPath)) {
|
||||
payloadConfigPath = path.resolve(projectDir, 'src/payload/payload.config.ts')
|
||||
if (!payloadConfigPath) {
|
||||
throw new Error(`Could not find payload.config.ts inside ${projectDir}`)
|
||||
}
|
||||
|
||||
const content = fse.readFileSync(payloadConfigPath, 'utf-8')
|
||||
|
||||
// Check payload.config.ts
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import * as p from '@clack/prompts'
|
||||
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 { debug, error, warning } from '../utils/log.js'
|
||||
import { configurePayloadConfig } from './configure-payload-config.js'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
@@ -60,14 +60,12 @@ export async function createProject(args: {
|
||||
const { cliArgs, dbDetails, packageManager, projectDir, projectName, template } = args
|
||||
|
||||
if (cliArgs['--dry-run']) {
|
||||
console.log(`\n Dry run: Creating project in ${chalk.green(projectDir)}\n`)
|
||||
debug(`Dry run: Creating project in ${chalk.green(projectDir)}`)
|
||||
return
|
||||
}
|
||||
|
||||
await createOrFindProjectDir(projectDir)
|
||||
|
||||
console.log(`\n Creating project in ${chalk.green(projectDir)}\n`)
|
||||
|
||||
if (cliArgs['--local-template']) {
|
||||
// Copy template from local path. For development purposes.
|
||||
const localTemplate = path.resolve(
|
||||
@@ -86,10 +84,12 @@ export async function createProject(args: {
|
||||
await emitter.clone(projectDir)
|
||||
}
|
||||
|
||||
const spinner = ora('Checking latest Payload version...').start()
|
||||
const spinner = p.spinner()
|
||||
spinner.start('Checking latest Payload version...')
|
||||
|
||||
await updatePackageJSON({ projectDir, projectName })
|
||||
await configurePayloadConfig({ dbDetails, projectDir })
|
||||
spinner.message('Configuring Payload...')
|
||||
await configurePayloadConfig({ dbDetails, projectDirOrConfigPath: { projectDir } })
|
||||
|
||||
// Remove yarn.lock file. This is only desired in Payload Cloud.
|
||||
const lockPath = path.resolve(projectDir, 'yarn.lock')
|
||||
@@ -98,18 +98,16 @@ export async function createProject(args: {
|
||||
}
|
||||
|
||||
if (!cliArgs['--no-deps']) {
|
||||
spinner.text = 'Installing dependencies...'
|
||||
spinner.message('Installing dependencies...')
|
||||
const result = await installDeps({ cliArgs, packageManager, projectDir })
|
||||
spinner.stop()
|
||||
spinner.clear()
|
||||
if (result) {
|
||||
success('Dependencies installed')
|
||||
spinner.stop('Dependencies installed')
|
||||
} else {
|
||||
error('Error installing dependencies')
|
||||
spinner.stop('Error installing dependencies', 1)
|
||||
}
|
||||
} else {
|
||||
spinner.stop()
|
||||
spinner.clear()
|
||||
spinner.stop('Dependency installation skipped')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +122,6 @@ export async function updatePackageJSON(args: {
|
||||
packageObj.name = projectName
|
||||
await fse.writeJson(packageJsonPath, packageObj, { spaces: 2 })
|
||||
} catch (err: unknown) {
|
||||
warning('Unable to update name in package.json')
|
||||
warning(`Unable to update name in package.json. ${err instanceof Error ? err.message : ''}`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { CompilerOptions } from 'typescript'
|
||||
|
||||
import chalk from 'chalk'
|
||||
import * as p from '@clack/prompts'
|
||||
import { parse, stringify } from 'comment-json'
|
||||
import execa from 'execa'
|
||||
import fs from 'fs'
|
||||
import fse from 'fs-extra'
|
||||
import globby from 'globby'
|
||||
import path from 'path'
|
||||
import { promisify } from 'util'
|
||||
|
||||
const readFile = promisify(fs.readFile)
|
||||
const writeFile = promisify(fs.writeFile)
|
||||
|
||||
@@ -16,58 +16,94 @@ const dirname = path.dirname(filename)
|
||||
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
import type { CliArgs, PackageManager } from '../types.js'
|
||||
import type { CliArgs, DbType, PackageManager } from '../types.js'
|
||||
|
||||
import { copyRecursiveSync } from '../utils/copy-recursive-sync.js'
|
||||
import { error, info, debug as origDebug, success, warning } from '../utils/log.js'
|
||||
import { debug as origDebug, warning } from '../utils/log.js'
|
||||
import { moveMessage } from '../utils/messages.js'
|
||||
import { wrapNextConfig } from './wrap-next-config.js'
|
||||
|
||||
type InitNextArgs = Pick<CliArgs, '--debug'> & {
|
||||
dbType: DbType
|
||||
nextConfigPath: string
|
||||
packageManager: PackageManager
|
||||
projectDir?: string
|
||||
projectDir: string
|
||||
useDistFiles?: boolean
|
||||
}
|
||||
type InitNextResult = { reason?: string; success: boolean; userAppDir?: string }
|
||||
|
||||
type InitNextResult =
|
||||
| {
|
||||
isSrcDir: boolean
|
||||
nextAppDir: string
|
||||
payloadConfigPath: string
|
||||
success: true
|
||||
}
|
||||
| { isSrcDir: boolean; nextAppDir?: string; reason: string; success: false }
|
||||
|
||||
export async function initNext(args: InitNextArgs): Promise<InitNextResult> {
|
||||
const { packageManager, projectDir } = args
|
||||
const templateResult = await applyPayloadTemplateFiles(args)
|
||||
if (!templateResult.success) return templateResult
|
||||
const { dbType: dbType, packageManager, projectDir } = args
|
||||
|
||||
const { success: installSuccess } = await installDeps(projectDir, packageManager)
|
||||
if (!installSuccess) {
|
||||
return { ...templateResult, reason: 'Failed to install dependencies', success: false }
|
||||
const isSrcDir = fs.existsSync(path.resolve(projectDir, 'src'))
|
||||
|
||||
// Get app directory. Could be top-level or src/app
|
||||
const nextAppDir = (
|
||||
await globby(['**/app'], {
|
||||
absolute: true,
|
||||
cwd: projectDir,
|
||||
onlyDirectories: true,
|
||||
})
|
||||
)?.[0]
|
||||
|
||||
if (!nextAppDir) {
|
||||
return { isSrcDir, reason: `Could not find app directory in ${projectDir}`, success: false }
|
||||
}
|
||||
|
||||
// Create or find payload.config.ts
|
||||
const createConfigResult = findOrCreatePayloadConfig(projectDir)
|
||||
if (!createConfigResult.success) {
|
||||
return { ...templateResult, ...createConfigResult }
|
||||
// Check for top-level layout.tsx
|
||||
const layoutPath = path.resolve(nextAppDir, 'layout.tsx')
|
||||
if (fs.existsSync(layoutPath)) {
|
||||
// Output directions for user to move all files from app to top-level directory named `(app)`
|
||||
p.log.warn(moveMessage({ nextAppDir, projectDir }))
|
||||
return {
|
||||
isSrcDir,
|
||||
nextAppDir,
|
||||
reason: 'Found existing layout.tsx in app directory',
|
||||
success: false,
|
||||
}
|
||||
}
|
||||
|
||||
const installSpinner = p.spinner()
|
||||
installSpinner.start('Installing Payload and dependencies...')
|
||||
|
||||
const configurationResult = installAndConfigurePayload({
|
||||
...args,
|
||||
isSrcDir,
|
||||
nextAppDir,
|
||||
useDistFiles: true, // Requires running 'pnpm pack-template-files' in cpa
|
||||
})
|
||||
|
||||
if (configurationResult.success === false) {
|
||||
installSpinner.stop(configurationResult.reason, 1)
|
||||
return { ...configurationResult, isSrcDir, success: false }
|
||||
}
|
||||
|
||||
const { success: installSuccess } = await installDeps(projectDir, packageManager, dbType)
|
||||
if (!installSuccess) {
|
||||
installSpinner.stop('Failed to install dependencies', 1)
|
||||
return {
|
||||
...configurationResult,
|
||||
isSrcDir,
|
||||
reason: 'Failed to install dependencies',
|
||||
success: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Add `@payload-config` to tsconfig.json `paths`
|
||||
await addPayloadConfigToTsConfig(projectDir)
|
||||
|
||||
// Output directions for user to update next.config.js
|
||||
const withPayloadMessage = `
|
||||
|
||||
${chalk.bold(`Wrap your existing next.config.js with the withPayload function. Here is an example:`)}
|
||||
|
||||
import withPayload from '@payloadcms/next/withPayload'
|
||||
|
||||
const nextConfig = {
|
||||
// Your Next.js config here
|
||||
}
|
||||
|
||||
export default withPayload(nextConfig)
|
||||
|
||||
`
|
||||
|
||||
console.log(withPayloadMessage)
|
||||
|
||||
return templateResult
|
||||
await addPayloadConfigToTsConfig(projectDir, isSrcDir)
|
||||
installSpinner.stop('Successfully installed Payload and dependencies')
|
||||
return { ...configurationResult, isSrcDir, nextAppDir, success: true }
|
||||
}
|
||||
|
||||
async function addPayloadConfigToTsConfig(projectDir: string) {
|
||||
async function addPayloadConfigToTsConfig(projectDir: string, isSrcDir: boolean) {
|
||||
const tsConfigPath = path.resolve(projectDir, 'tsconfig.json')
|
||||
const userTsConfigContent = await readFile(tsConfigPath, {
|
||||
encoding: 'utf8',
|
||||
@@ -82,48 +118,39 @@ async function addPayloadConfigToTsConfig(projectDir: string) {
|
||||
if (!userTsConfig.compilerOptions.paths?.['@payload-config']) {
|
||||
userTsConfig.compilerOptions.paths = {
|
||||
...(userTsConfig.compilerOptions.paths || {}),
|
||||
'@payload-config': ['./payload.config.ts'],
|
||||
'@payload-config': [`./${isSrcDir ? 'src/' : ''}payload.config.ts`],
|
||||
}
|
||||
await writeFile(tsConfigPath, stringify(userTsConfig, null, 2), { encoding: 'utf8' })
|
||||
}
|
||||
}
|
||||
|
||||
async function applyPayloadTemplateFiles(args: InitNextArgs): Promise<InitNextResult> {
|
||||
const { '--debug': debug, projectDir, useDistFiles } = args
|
||||
|
||||
info('Initializing Payload app in Next.js project', 1)
|
||||
function installAndConfigurePayload(
|
||||
args: InitNextArgs & {
|
||||
isSrcDir: boolean
|
||||
nextAppDir: string
|
||||
},
|
||||
):
|
||||
| { payloadConfigPath: string; success: true }
|
||||
| { payloadConfigPath?: string; reason: string; success: false } {
|
||||
const { '--debug': debug, isSrcDir, nextAppDir, nextConfigPath, projectDir, useDistFiles } = args
|
||||
|
||||
const logDebug = (message: string) => {
|
||||
if (debug) origDebug(message)
|
||||
}
|
||||
|
||||
if (!fs.existsSync(projectDir)) {
|
||||
return { reason: `Could not find specified project directory at ${projectDir}`, success: false }
|
||||
}
|
||||
|
||||
// Next.js configs can be next.config.js, next.config.mjs, etc.
|
||||
const foundConfig = (await globby('next.config.*js', { absolute: true, cwd: projectDir }))?.[0]
|
||||
|
||||
if (!foundConfig) {
|
||||
throw new Error(`No next.config.js found at ${projectDir}`)
|
||||
}
|
||||
|
||||
const nextConfigPath = path.resolve(projectDir, foundConfig)
|
||||
if (!fs.existsSync(nextConfigPath)) {
|
||||
return {
|
||||
reason: `No next.config.js found at ${nextConfigPath}. Ensure you are in a Next.js project directory.`,
|
||||
reason: `Could not find specified project directory at ${projectDir}`,
|
||||
success: false,
|
||||
}
|
||||
} else {
|
||||
if (debug) logDebug(`Found Next config at ${nextConfigPath}`)
|
||||
}
|
||||
|
||||
const templateFilesPath =
|
||||
dirname.endsWith('dist') || useDistFiles
|
||||
? path.resolve(dirname, '../..', 'dist/app')
|
||||
: path.resolve(dirname, '../../../../app')
|
||||
? path.resolve(dirname, '../..', 'dist/template')
|
||||
: path.resolve(dirname, '../../../../templates/blank-3.0')
|
||||
|
||||
if (debug) logDebug(`Using template files from: ${templateFilesPath}`)
|
||||
logDebug(`Using template files from: ${templateFilesPath}`)
|
||||
|
||||
if (!fs.existsSync(templateFilesPath)) {
|
||||
return {
|
||||
@@ -131,38 +158,41 @@ async function applyPayloadTemplateFiles(args: InitNextArgs): Promise<InitNextRe
|
||||
success: false,
|
||||
}
|
||||
} else {
|
||||
if (debug) logDebug('Found template source files')
|
||||
logDebug('Found template source files')
|
||||
}
|
||||
|
||||
// src/app or app
|
||||
const userAppDir = (
|
||||
await globby(['**/app'], {
|
||||
absolute: true,
|
||||
cwd: projectDir,
|
||||
onlyDirectories: true,
|
||||
})
|
||||
)?.[0]
|
||||
logDebug(`Copying template files from ${templateFilesPath} to ${nextAppDir}`)
|
||||
|
||||
if (!fs.existsSync(userAppDir)) {
|
||||
return { reason: `Could not find user app directory inside ${projectDir}`, success: false }
|
||||
} else {
|
||||
logDebug(`Found user app directory: ${userAppDir}`)
|
||||
const templateSrcDir = path.resolve(templateFilesPath, isSrcDir ? '' : 'src')
|
||||
// const templateSrcDir = path.resolve(templateFilesPath)
|
||||
|
||||
logDebug(`templateSrcDir: ${templateSrcDir}`)
|
||||
logDebug(`nextAppDir: ${nextAppDir}`)
|
||||
logDebug(`projectDir: ${projectDir}`)
|
||||
logDebug(`nextConfigPath: ${nextConfigPath}`)
|
||||
|
||||
logDebug(
|
||||
`isSrcDir: ${isSrcDir}. source: ${templateSrcDir}. dest: ${path.dirname(nextConfigPath)}`,
|
||||
)
|
||||
|
||||
// This is a little clunky and needs to account for isSrcDir
|
||||
copyRecursiveSync(templateSrcDir, path.dirname(nextConfigPath), debug)
|
||||
|
||||
// Wrap next.config.js with withPayload
|
||||
wrapNextConfig({ nextConfigPath })
|
||||
|
||||
return {
|
||||
payloadConfigPath: path.resolve(nextAppDir, '../payload.config.ts'),
|
||||
success: true,
|
||||
}
|
||||
|
||||
logDebug(`Copying template files from ${templateFilesPath} to ${userAppDir}`)
|
||||
copyRecursiveSync(templateFilesPath, userAppDir, debug)
|
||||
success('Successfully initialized.')
|
||||
return { success: true, userAppDir }
|
||||
}
|
||||
|
||||
async function installDeps(projectDir: string, packageManager: PackageManager) {
|
||||
info(`Installing dependencies with ${packageManager}`, 1)
|
||||
const packagesToInstall = [
|
||||
'payload',
|
||||
'@payloadcms/db-mongodb',
|
||||
'@payloadcms/next',
|
||||
'@payloadcms/richtext-lexical',
|
||||
].map((pkg) => `${pkg}@alpha`)
|
||||
async function installDeps(projectDir: string, packageManager: PackageManager, dbType: DbType) {
|
||||
const packagesToInstall = ['payload', '@payloadcms/next', '@payloadcms/richtext-lexical'].map(
|
||||
(pkg) => `${pkg}@alpha`,
|
||||
)
|
||||
|
||||
packagesToInstall.push(`@payloadcms/db-${dbType}@alpha`)
|
||||
|
||||
let exitCode = 0
|
||||
switch (packageManager) {
|
||||
@@ -186,43 +216,5 @@ async function installDeps(projectDir: string, packageManager: PackageManager) {
|
||||
}
|
||||
}
|
||||
|
||||
if (exitCode !== 0) {
|
||||
error(`Failed to install dependencies with ${packageManager}`)
|
||||
} else {
|
||||
success(`Successfully installed dependencies`)
|
||||
}
|
||||
return { success: exitCode === 0 }
|
||||
}
|
||||
function findOrCreatePayloadConfig(projectDir: string) {
|
||||
const configPath = path.resolve(projectDir, 'payload.config.ts')
|
||||
if (fs.existsSync(configPath)) {
|
||||
return { message: 'Found existing payload.config.ts', success: true }
|
||||
} else {
|
||||
// Create default config
|
||||
// TODO: Pull this from templates
|
||||
const defaultConfig = `import path from "path";
|
||||
|
||||
import { mongooseAdapter } from "@payloadcms/db-mongodb"; // database-adapter-import
|
||||
import { lexicalEditor } from "@payloadcms/richtext-lexical"; // editor-import
|
||||
import { buildConfig } from "payload/config";
|
||||
|
||||
export default buildConfig({
|
||||
editor: slateEditor({}), // editor-config
|
||||
collections: [],
|
||||
secret: "asdfasdf",
|
||||
typescript: {
|
||||
outputFile: path.resolve(__dirname, "payload-types.ts"),
|
||||
},
|
||||
graphQL: {
|
||||
schemaOutputFile: path.resolve(__dirname, "generated-schema.graphql"),
|
||||
},
|
||||
db: mongooseAdapter({
|
||||
url: "mongodb://localhost:27017/next-payload-3",
|
||||
}),
|
||||
});
|
||||
`
|
||||
|
||||
fse.writeFileSync(configPath, defaultConfig)
|
||||
return { message: 'Created default payload.config.ts', success: true }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
import prompts from 'prompts'
|
||||
import * as p from '@clack/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]
|
||||
|
||||
const response = await prompts(
|
||||
{
|
||||
name: 'value',
|
||||
type: 'text',
|
||||
const projectName = await p.text({
|
||||
message: 'Project name?',
|
||||
validate: (value: string) => !!value.length,
|
||||
validate: (value) => {
|
||||
if (!value) return 'Please enter a project name.'
|
||||
},
|
||||
{
|
||||
onCancel: () => {
|
||||
})
|
||||
if (p.isCancel(projectName)) {
|
||||
process.exit(0)
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
return response.value
|
||||
}
|
||||
return projectName
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import prompts from 'prompts'
|
||||
import * as p from '@clack/prompts'
|
||||
|
||||
import type { CliArgs, ProjectTemplate } from '../types.js'
|
||||
|
||||
export async function parseTemplate(
|
||||
args: CliArgs,
|
||||
validTemplates: ProjectTemplate[],
|
||||
): Promise<ProjectTemplate> {
|
||||
): Promise<ProjectTemplate | undefined> {
|
||||
if (args['--template']) {
|
||||
const templateName = args['--template']
|
||||
const template = validTemplates.find((t) => t.name === templateName)
|
||||
@@ -13,29 +13,20 @@ export async function parseTemplate(
|
||||
return template
|
||||
}
|
||||
|
||||
const response = await prompts(
|
||||
{
|
||||
name: 'value',
|
||||
type: 'select',
|
||||
choices: validTemplates.map((p) => {
|
||||
const response = await p.select<{ label: string; value: string }[], string>({
|
||||
message: 'Choose project template',
|
||||
options: validTemplates.map((p) => {
|
||||
return {
|
||||
description: p.description,
|
||||
title: p.name,
|
||||
label: p.name,
|
||||
value: p.name,
|
||||
}
|
||||
}),
|
||||
message: 'Choose project template',
|
||||
validate: (value: string) => !!value.length,
|
||||
},
|
||||
{
|
||||
onCancel: () => {
|
||||
})
|
||||
if (p.isCancel(response)) {
|
||||
process.exit(0)
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const template = validTemplates.find((t) => t.name === response.value)
|
||||
if (!template) throw new Error('Template is undefined')
|
||||
const template = validTemplates.find((t) => t.name === response)
|
||||
|
||||
return template
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as p from '@clack/prompts'
|
||||
import slugify from '@sindresorhus/slugify'
|
||||
import prompts from 'prompts'
|
||||
|
||||
import type { CliArgs, DbDetails, DbType } from '../types.js'
|
||||
|
||||
@@ -23,7 +23,7 @@ const dbChoiceRecord: Record<DbType, DbChoice> = {
|
||||
}
|
||||
|
||||
export async function selectDb(args: CliArgs, projectName: string): Promise<DbDetails> {
|
||||
let dbType: DbType | undefined = undefined
|
||||
let dbType: DbType | symbol | undefined = undefined
|
||||
if (args['--db']) {
|
||||
if (!Object.values(dbChoiceRecord).some((dbChoice) => dbChoice.value === args['--db'])) {
|
||||
throw new Error(
|
||||
@@ -34,31 +34,20 @@ export async function selectDb(args: CliArgs, projectName: string): Promise<DbDe
|
||||
}
|
||||
dbType = args['--db'] as DbType
|
||||
} else {
|
||||
const dbTypeRes = await prompts(
|
||||
{
|
||||
name: 'value',
|
||||
type: 'select',
|
||||
choices: Object.values(dbChoiceRecord).map((dbChoice) => {
|
||||
return {
|
||||
title: dbChoice.title,
|
||||
value: dbChoice.value,
|
||||
}
|
||||
}),
|
||||
message: 'Select a database',
|
||||
validate: (value: string) => !!value.length,
|
||||
},
|
||||
{
|
||||
onCancel: () => {
|
||||
process.exit(0)
|
||||
},
|
||||
},
|
||||
)
|
||||
dbType = dbTypeRes.value
|
||||
dbType = await p.select<{ label: string; value: DbType }[], DbType>({
|
||||
initialValue: 'mongodb',
|
||||
message: `Select a database`,
|
||||
options: [
|
||||
{ label: 'MongoDB', value: 'mongodb' },
|
||||
{ label: 'Postgres', value: 'postgres' },
|
||||
],
|
||||
})
|
||||
if (p.isCancel(dbType)) process.exit(0)
|
||||
}
|
||||
|
||||
const dbChoice = dbChoiceRecord[dbType]
|
||||
|
||||
let dbUri: string | undefined = undefined
|
||||
let dbUri: string | symbol | undefined = undefined
|
||||
const initialDbUri = `${dbChoice.dbConnectionPrefix}${
|
||||
projectName === '.' ? `payload-${getRandomDigitSuffix()}` : slugify(projectName)
|
||||
}`
|
||||
@@ -68,21 +57,11 @@ export async function selectDb(args: CliArgs, projectName: string): Promise<DbDe
|
||||
} else if (args['--db-connection-string']) {
|
||||
dbUri = args['--db-connection-string']
|
||||
} else {
|
||||
const dbUriRes = await prompts(
|
||||
{
|
||||
name: 'value',
|
||||
type: 'text',
|
||||
initial: initialDbUri,
|
||||
dbUri = await p.text({
|
||||
initialValue: initialDbUri,
|
||||
message: `Enter ${dbChoice.title.split(' ')[0]} connection string`, // strip beta from title
|
||||
validate: (value: string) => !!value.length,
|
||||
},
|
||||
{
|
||||
onCancel: () => {
|
||||
process.exit(0)
|
||||
},
|
||||
},
|
||||
)
|
||||
dbUri = dbUriRes.value
|
||||
})
|
||||
if (p.isCancel(dbUri)) process.exit(0)
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { parseAndInsertWithPayload, withPayloadImportStatement } from './wrap-next-config.js'
|
||||
import { parseAndModifyConfigContent, withPayloadImportStatement } from './wrap-next-config.js'
|
||||
import * as p from '@clack/prompts'
|
||||
|
||||
const defaultNextConfig = `/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {};
|
||||
@@ -30,25 +31,31 @@ export { wrapped as default }
|
||||
|
||||
describe('parseAndInsertWithPayload', () => {
|
||||
it('should parse the default next config', () => {
|
||||
const { modifiedConfigContent } = parseAndInsertWithPayload(defaultNextConfig)
|
||||
const { modifiedConfigContent } = parseAndModifyConfigContent(defaultNextConfig)
|
||||
expect(modifiedConfigContent).toContain(withPayloadImportStatement)
|
||||
expect(modifiedConfigContent).toContain('withPayload(nextConfig)')
|
||||
})
|
||||
it('should parse the config with a function', () => {
|
||||
const { modifiedConfigContent } = parseAndInsertWithPayload(nextConfigWithFunc)
|
||||
const { modifiedConfigContent } = parseAndModifyConfigContent(nextConfigWithFunc)
|
||||
expect(modifiedConfigContent).toContain('withPayload(someFunc(nextConfig))')
|
||||
})
|
||||
|
||||
it('should parse the config with a function on a new line', () => {
|
||||
const { modifiedConfigContent } = parseAndInsertWithPayload(nextConfigWithFuncMultiline)
|
||||
const { modifiedConfigContent } = parseAndModifyConfigContent(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)
|
||||
const warnLogSpy = jest.spyOn(p.log, 'warn').mockImplementation(() => {})
|
||||
|
||||
const { modifiedConfigContent, success } = parseAndModifyConfigContent(
|
||||
nextConfigExportNamedDefault,
|
||||
)
|
||||
expect(modifiedConfigContent).toContain(withPayloadImportStatement)
|
||||
expect(error).toBeTruthy()
|
||||
expect(success).toBe(false)
|
||||
|
||||
expect(warnLogSpy).toHaveBeenCalledWith(expect.stringContaining('Could not automatically wrap'))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
import chalk from 'chalk'
|
||||
import { parseModule } from 'esprima'
|
||||
import fs from 'fs'
|
||||
import globby from 'globby'
|
||||
import path from 'path'
|
||||
|
||||
import { warning } from '../utils/log.js'
|
||||
import { log } from '../utils/log.js'
|
||||
|
||||
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]
|
||||
export const wrapNextConfig = (args: { nextConfigPath: string }) => {
|
||||
const { nextConfigPath } = args
|
||||
const configContent = fs.readFileSync(nextConfigPath, 'utf8')
|
||||
const { modifiedConfigContent: newConfig, success } = parseAndModifyConfigContent(configContent)
|
||||
|
||||
if (!foundConfig) {
|
||||
throw new Error(`No Next config found at ${args.projectDir}`)
|
||||
if (!success) {
|
||||
return
|
||||
}
|
||||
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)
|
||||
|
||||
fs.writeFileSync(nextConfigPath, newConfig)
|
||||
}
|
||||
|
||||
export function parseAndInsertWithPayload(content: string): {
|
||||
error?: string
|
||||
/**
|
||||
* Parses config content with AST and wraps it with withPayload function
|
||||
*/
|
||||
export function parseAndModifyConfigContent(content: string): {
|
||||
modifiedConfigContent: string
|
||||
success: boolean
|
||||
} {
|
||||
content = withPayloadImportStatement + content
|
||||
const ast = parseModule(content, { loc: true })
|
||||
@@ -43,7 +45,7 @@ export function parseAndInsertWithPayload(content: string): {
|
||||
content,
|
||||
exportDefaultDeclaration.declaration?.loc,
|
||||
)
|
||||
return { modifiedConfigContent }
|
||||
return { modifiedConfigContent, success: true }
|
||||
} else if (exportNamedDeclaration) {
|
||||
const exportSpecifier = exportNamedDeclaration.specifiers.find(
|
||||
(s) =>
|
||||
@@ -54,16 +56,42 @@ export function parseAndInsertWithPayload(content: string): {
|
||||
)
|
||||
|
||||
if (exportSpecifier) {
|
||||
// TODO: Improve with this example and/or link to docs
|
||||
warning('Could not automatically wrap next.config.js with withPayload.')
|
||||
warning('Automatic wrapping of named exports as default not supported yet.')
|
||||
|
||||
warnUserWrapNotSuccessful()
|
||||
return {
|
||||
error: `Automatic wrapping of named exports as default not supported yet.
|
||||
Please manually wrap your Next config with the withPayload function`,
|
||||
modifiedConfigContent: content,
|
||||
success: false,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Error('Could not automatically wrap next.config.js with withPayload')
|
||||
warning('Could not automatically wrap next.config.js with withPayload.')
|
||||
warnUserWrapNotSuccessful()
|
||||
return {
|
||||
modifiedConfigContent: content,
|
||||
success: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function warnUserWrapNotSuccessful() {
|
||||
// Output directions for user to update next.config.js
|
||||
const withPayloadMessage = `
|
||||
|
||||
${chalk.bold(`Please manually wrap your existing next.config.js with the withPayload function. Here is an example:`)}
|
||||
|
||||
import withPayload from '@payloadcms/next/withPayload'
|
||||
|
||||
const nextConfig = {
|
||||
// Your Next.js config here
|
||||
}
|
||||
|
||||
export default withPayload(nextConfig)
|
||||
|
||||
`
|
||||
|
||||
log(withPayloadMessage)
|
||||
}
|
||||
|
||||
type Directive = {
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import chalk from 'chalk'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
|
||||
import type { CliArgs, ProjectTemplate } from '../types.js'
|
||||
|
||||
import { error, success } from '../utils/log.js'
|
||||
import { debug, error } from '../utils/log.js'
|
||||
|
||||
/** Parse and swap .env.example values and write .env */
|
||||
export async function writeEnvFile(args: {
|
||||
@@ -17,7 +16,7 @@ export async function writeEnvFile(args: {
|
||||
const { cliArgs, databaseUri, payloadSecret, projectDir, template } = args
|
||||
|
||||
if (cliArgs['--dry-run']) {
|
||||
success(`DRY RUN: .env file created`)
|
||||
debug(`DRY RUN: .env file created`)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -51,8 +50,6 @@ export async function writeEnvFile(args: {
|
||||
const content = `MONGODB_URI=${databaseUri}\nPAYLOAD_SECRET=${payloadSecret}`
|
||||
await fs.outputFile(`${projectDir}/.env`, content)
|
||||
}
|
||||
|
||||
success('.env file created')
|
||||
} catch (err: unknown) {
|
||||
error('Unable to write .env file')
|
||||
if (err instanceof Error) {
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
/* eslint-disable no-console */
|
||||
import * as p from '@clack/prompts'
|
||||
import slugify from '@sindresorhus/slugify'
|
||||
import arg from 'arg'
|
||||
import chalk from 'chalk'
|
||||
import { detect } from 'detect-package-manager'
|
||||
import globby from 'globby'
|
||||
import path from 'path'
|
||||
|
||||
import type { CliArgs, PackageManager } from './types.js'
|
||||
|
||||
import { configurePayloadConfig } from './lib/configure-payload-config.js'
|
||||
import { createProject } from './lib/create-project.js'
|
||||
import { generateSecret } from './lib/generate-secret.js'
|
||||
import { initNext } from './lib/init-next.js'
|
||||
@@ -14,8 +17,8 @@ import { parseTemplate } from './lib/parse-template.js'
|
||||
import { selectDb } from './lib/select-db.js'
|
||||
import { getValidTemplates, validateTemplate } from './lib/templates.js'
|
||||
import { writeEnvFile } from './lib/write-env-file.js'
|
||||
import { error, success } from './utils/log.js'
|
||||
import { helpMessage, successMessage, welcomeMessage } from './utils/messages.js'
|
||||
import { error, info } from './utils/log.js'
|
||||
import { feedbackOutro, helpMessage, successMessage, successfulNextInit } from './utils/messages.js'
|
||||
|
||||
export class Main {
|
||||
args: CliArgs
|
||||
@@ -35,7 +38,7 @@ export class Main {
|
||||
'--template-branch': String,
|
||||
|
||||
// Next.js
|
||||
'--init-next': Boolean,
|
||||
'--init-next': Boolean, // TODO: Is this needed if we detect if inside Next.js project?
|
||||
|
||||
// Package manager
|
||||
'--no-deps': Boolean,
|
||||
@@ -59,44 +62,92 @@ export class Main {
|
||||
}
|
||||
|
||||
async init(): Promise<void> {
|
||||
const initContext: {
|
||||
nextConfigPath: string | undefined
|
||||
} = {
|
||||
nextConfigPath: undefined,
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.args['--help']) {
|
||||
console.log(helpMessage())
|
||||
helpMessage()
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const projectName = await parseProjectName(this.args)
|
||||
const projectDir = path.resolve(
|
||||
projectName === '.' || this.args['--init-next']
|
||||
? path.basename(process.cwd())
|
||||
: `./${slugify(projectName)}`,
|
||||
)
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('\n')
|
||||
p.intro(chalk.bgCyan(chalk.black(' create-payload-app ')))
|
||||
p.log.message("Welcome to Payload. Let's create a project!")
|
||||
// Detect if inside Next.js projeckpt
|
||||
const nextConfigPath = (
|
||||
await globby('next.config.*js', { absolute: true, cwd: process.cwd() })
|
||||
)?.[0]
|
||||
initContext.nextConfigPath = nextConfigPath
|
||||
|
||||
if (initContext.nextConfigPath) {
|
||||
this.args['--name'] = slugify(path.basename(path.dirname(initContext.nextConfigPath)))
|
||||
}
|
||||
|
||||
const projectName = await parseProjectName(this.args)
|
||||
const projectDir = nextConfigPath
|
||||
? path.dirname(nextConfigPath)
|
||||
: path.resolve(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 })
|
||||
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')
|
||||
if (nextConfigPath) {
|
||||
// p.note('Detected existing Next.js project.')
|
||||
p.log.step(chalk.bold('Detected existing Next.js project.'))
|
||||
const proceed = await p.confirm({
|
||||
initialValue: true,
|
||||
message: 'Install Payload in this project?',
|
||||
})
|
||||
if (p.isCancel(proceed) || !proceed) {
|
||||
process.exit(0)
|
||||
}
|
||||
process.exit(result.success ? 0 : 1)
|
||||
// TODO: This should continue the normal prompt flow
|
||||
|
||||
const dbDetails = await selectDb(this.args, projectName)
|
||||
|
||||
const result = await initNext({
|
||||
...this.args,
|
||||
dbType: dbDetails.type,
|
||||
nextConfigPath,
|
||||
packageManager,
|
||||
projectDir,
|
||||
})
|
||||
|
||||
if (result.success === false) {
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
await configurePayloadConfig({
|
||||
dbDetails,
|
||||
projectDirOrConfigPath: {
|
||||
payloadConfigPath: result.payloadConfigPath,
|
||||
},
|
||||
})
|
||||
|
||||
info('Payload project successfully created!')
|
||||
p.note(successfulNextInit(), chalk.bgGreen(chalk.black(' Documentation ')))
|
||||
p.outro(feedbackOutro())
|
||||
return
|
||||
}
|
||||
|
||||
const templateArg = this.args['--template']
|
||||
if (templateArg) {
|
||||
const valid = validateTemplate(templateArg)
|
||||
if (!valid) {
|
||||
console.log(helpMessage())
|
||||
helpMessage()
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
const validTemplates = getValidTemplates()
|
||||
const template = await parseTemplate(this.args, validTemplates)
|
||||
if (!template) {
|
||||
p.log.error('Invalid template given')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
switch (template.type) {
|
||||
case 'starter': {
|
||||
@@ -131,10 +182,11 @@ export class Main {
|
||||
}
|
||||
}
|
||||
|
||||
success('Payload project successfully created')
|
||||
console.log(successMessage(projectDir, packageManager))
|
||||
} catch (error: unknown) {
|
||||
console.log(error)
|
||||
info('Payload project successfully created!')
|
||||
p.note(successMessage(projectDir, packageManager), chalk.bgGreen(chalk.black(' Next Steps ')))
|
||||
p.outro(feedbackOutro())
|
||||
} catch (err: unknown) {
|
||||
error(err instanceof Error ? err.message : 'An error occurred')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import fs from 'fs'
|
||||
import fsp from 'fs/promises'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import path from 'path'
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
main()
|
||||
|
||||
/**
|
||||
* Copy the necessary template files from `templates/blank-3.0` to `dist/template`
|
||||
*
|
||||
* Eventually, this should be replaced with using tar.x to stream from the git repo
|
||||
*/
|
||||
|
||||
async function main() {
|
||||
const root = path.resolve(dirname, '../../../../')
|
||||
const outputPath = path.resolve(dirname, '../../dist/template')
|
||||
const sourceTemplatePath = path.resolve(root, 'templates/blank-3.0')
|
||||
|
||||
if (!fs.existsSync(sourceTemplatePath)) {
|
||||
throw new Error(`Source path does not exist: ${sourceTemplatePath}`)
|
||||
}
|
||||
|
||||
if (!fs.existsSync(outputPath)) {
|
||||
fs.mkdirSync(outputPath, { recursive: true })
|
||||
}
|
||||
|
||||
// Copy the src directory from `templates/blank-3.0` to `dist`
|
||||
const srcPath = path.resolve(sourceTemplatePath, 'src')
|
||||
const distSrcPath = path.resolve(outputPath, 'src')
|
||||
// Copy entire file structure from src to dist
|
||||
await fsp.cp(srcPath, distSrcPath, { recursive: true })
|
||||
}
|
||||
@@ -1,27 +1,23 @@
|
||||
/* eslint-disable no-console */
|
||||
import * as p from '@clack/prompts'
|
||||
import chalk from 'chalk'
|
||||
import figures from 'figures'
|
||||
|
||||
export const success = (message: string): void => {
|
||||
console.log(`${chalk.green(figures.tick)} ${chalk.bold(message)}`)
|
||||
}
|
||||
|
||||
export const warning = (message: string): void => {
|
||||
console.log(chalk.yellow('? ') + chalk.bold(message))
|
||||
p.log.warn(chalk.yellow('? ') + chalk.bold(message))
|
||||
}
|
||||
|
||||
export const info = (message: string, paddingTop?: number): void => {
|
||||
console.log(
|
||||
`${'\n'.repeat(paddingTop || 0)}${chalk.green(figures.pointerSmall)} ${chalk.bold(message)}`,
|
||||
)
|
||||
export const info = (message: string): void => {
|
||||
p.log.step(chalk.bold(message))
|
||||
}
|
||||
|
||||
export const error = (message: string): void => {
|
||||
console.log(`${chalk.red(figures.cross)} ${chalk.bold(message)}`)
|
||||
p.log.error(chalk.bold(message))
|
||||
}
|
||||
|
||||
export const debug = (message: string): void => {
|
||||
console.log(
|
||||
`${chalk.gray(figures.pointerSmall)} ${chalk.bgGray('[DEBUG]')} ${chalk.gray(message)}`,
|
||||
)
|
||||
p.log.step(`${chalk.bgGray('[DEBUG]')} ${chalk.gray(message)}`)
|
||||
}
|
||||
|
||||
export const log = (message: string): void => {
|
||||
p.log.message(message)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
/* eslint-disable no-console */
|
||||
import chalk from 'chalk'
|
||||
import figures from 'figures'
|
||||
import path from 'path'
|
||||
import terminalLink from 'terminal-link'
|
||||
|
||||
import type { ProjectTemplate } from '../types.js'
|
||||
import type { PackageManager } from '../types.js'
|
||||
|
||||
import { getValidTemplates } from '../lib/templates.js'
|
||||
|
||||
const header = (message: string): string => `${chalk.yellow(figures.star)} ${chalk.bold(message)}`
|
||||
const header = (message: string): string => chalk.bold(message)
|
||||
|
||||
export const welcomeMessage = chalk`
|
||||
{green Welcome to Payload. Let's create a project! }
|
||||
@@ -15,14 +16,14 @@ export const welcomeMessage = chalk`
|
||||
|
||||
const spacer = ' '.repeat(8)
|
||||
|
||||
export function helpMessage(): string {
|
||||
export function helpMessage(): void {
|
||||
const validTemplates = getValidTemplates()
|
||||
return chalk`
|
||||
console.log(chalk`
|
||||
{bold USAGE}
|
||||
|
||||
{dim $} {bold npx create-payload-app}
|
||||
{dim $} {bold npx create-payload-app} my-project
|
||||
{dim $} {bold npx create-payload-app} -n my-project -t blog
|
||||
{dim $} {bold npx create-payload-app} -n my-project -t template-name
|
||||
|
||||
{bold OPTIONS}
|
||||
|
||||
@@ -36,7 +37,7 @@ export function helpMessage(): string {
|
||||
--use-pnpm Use pnpm to install dependencies
|
||||
--no-deps Do not install any dependencies
|
||||
-h Show help
|
||||
`
|
||||
`)
|
||||
}
|
||||
|
||||
function formatTemplates(templates: ProjectTemplate[]) {
|
||||
@@ -45,19 +46,20 @@ function formatTemplates(templates: ProjectTemplate[]) {
|
||||
.join(`\n${spacer}`)}`
|
||||
}
|
||||
|
||||
export function successMessage(projectDir: string, packageManager: string): string {
|
||||
export function successMessage(projectDir: string, packageManager: PackageManager): string {
|
||||
const relativePath = path.relative(process.cwd(), projectDir)
|
||||
return `
|
||||
${header('Launch Application:')}
|
||||
${header('Launch Application:')}
|
||||
|
||||
- cd ${projectDir}
|
||||
- cd ./${relativePath}
|
||||
- ${
|
||||
packageManager === 'yarn' ? 'yarn' : 'npm run'
|
||||
packageManager === 'npm' ? 'npm run' : packageManager
|
||||
} dev or follow directions in ${createTerminalLink(
|
||||
'README.md',
|
||||
`file://${path.resolve(projectDir, 'README.md')}`,
|
||||
)}
|
||||
|
||||
${header('Documentation:')}
|
||||
${header('Documentation:')}
|
||||
|
||||
- ${createTerminalLink(
|
||||
'Getting Started',
|
||||
@@ -68,6 +70,34 @@ export function successMessage(projectDir: string, packageManager: string): stri
|
||||
`
|
||||
}
|
||||
|
||||
export function successfulNextInit(): string {
|
||||
return `- ${createTerminalLink(
|
||||
'Getting Started',
|
||||
'https://payloadcms.com/docs/getting-started/what-is-payload',
|
||||
)}
|
||||
- ${createTerminalLink('Configuration', 'https://payloadcms.com/docs/configuration/overview')}
|
||||
`
|
||||
}
|
||||
|
||||
export function moveMessage(args: { nextAppDir: string; projectDir: string }): string {
|
||||
const relativePath = path.relative(process.cwd(), args.nextAppDir)
|
||||
return `
|
||||
${header('Next Steps:')}
|
||||
|
||||
Payload does not support a top-level layout.tsx file in your Next.js app directory.
|
||||
|
||||
${chalk.bold('To continue:')}
|
||||
|
||||
Move all files from ./${relativePath} to a named directory such as ${chalk.bold('(app)')}
|
||||
|
||||
Once moved, rerun the create-payload-app command again.
|
||||
`
|
||||
}
|
||||
|
||||
export function feedbackOutro(): string {
|
||||
return `${chalk.bgCyan(chalk.black(' Have feedback? '))} Visit ${createTerminalLink('GitHub', 'https://github.com/payloadcms/payload')}`
|
||||
}
|
||||
|
||||
// Create terminalLink with fallback for unsupported terminals
|
||||
function createTerminalLink(text: string, url: string) {
|
||||
return terminalLink(text, url, {
|
||||
|
||||
@@ -132,23 +132,37 @@ const seo =
|
||||
(collection.auth ||
|
||||
!(typeof collection.auth === 'object' && collection.auth.disableLocalStrategy)) &&
|
||||
collection.fields?.find((field) => 'name' in field && field.name === 'email')
|
||||
const hasOnlyEmailField = collection.fields?.length === 1 && emailField
|
||||
|
||||
const seoTabs: TabsField[] = [
|
||||
const seoTabs: TabsField[] = hasOnlyEmailField
|
||||
? [
|
||||
{
|
||||
type: 'tabs',
|
||||
tabs: [
|
||||
{
|
||||
fields: seoFields,
|
||||
label: 'SEO',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
type: 'tabs',
|
||||
tabs: [
|
||||
// append a new tab onto the end of the tabs array, if there is one at the first index
|
||||
// if needed, create a new `Content` tab in the first index for this collection's base fields
|
||||
...(collection?.fields?.[0]?.type === 'tabs' && collection?.fields?.[0]?.tabs
|
||||
...(collection?.fields?.[0]?.type === 'tabs' &&
|
||||
collection?.fields?.[0]?.tabs
|
||||
? collection.fields[0].tabs
|
||||
: [
|
||||
{
|
||||
fields: [
|
||||
...((emailField
|
||||
...(emailField
|
||||
? collection.fields.filter(
|
||||
(field) => 'name' in field && field.name !== 'email',
|
||||
)
|
||||
: collection.fields) || []),
|
||||
: collection.fields),
|
||||
],
|
||||
label: collection?.labels?.singular || 'Content',
|
||||
},
|
||||
|
||||
@@ -1,7 +1,127 @@
|
||||
import en from './en.json'
|
||||
import es from './es.json'
|
||||
import fa from './fa.json'
|
||||
import fr from './fr.json'
|
||||
import pl from './pl.json'
|
||||
|
||||
export const translations = { en, es, fa, fr, pl }
|
||||
export const translations = {
|
||||
en: {
|
||||
$schema: './translation-schema.json',
|
||||
'plugin-seo': {
|
||||
almostThere: 'Almost there',
|
||||
autoGenerate: 'Auto-generate',
|
||||
bestPractices: 'best practices',
|
||||
characterCount: '{{current}}/{{minLength}}-{{maxLength}} chars, ',
|
||||
charactersLeftOver: '{{characters}} left over',
|
||||
charactersToGo: '{{characters}} to go',
|
||||
charactersTooMany: '{{characters}} too many',
|
||||
checksPassing: '{{current}}/{{max}} checks are passing',
|
||||
good: 'Good',
|
||||
imageAutoGenerationTip: 'Auto-generation will retrieve the selected hero image.',
|
||||
lengthTipDescription:
|
||||
'This should be between {{minLength}} and {{maxLength}} characters. For help in writing quality meta descriptions, see ',
|
||||
lengthTipTitle:
|
||||
'This should be between {{minLength}} and {{maxLength}} characters. For help in writing quality meta titles, see ',
|
||||
noImage: 'No image',
|
||||
preview: 'Preview',
|
||||
previewDescription: 'Exact result listings may vary based on content and search relevancy.',
|
||||
tooLong: 'Too long',
|
||||
tooShort: 'Too short',
|
||||
},
|
||||
},
|
||||
es: {
|
||||
$schema: './translation-schema.json',
|
||||
'plugin-seo': {
|
||||
almostThere: 'Ya casi está',
|
||||
autoGenerate: 'Autogénerar',
|
||||
bestPractices: 'mejores prácticas',
|
||||
characterCount: '{{current}}/{{minLength}}-{{maxLength}} letras, ',
|
||||
charactersLeftOver: '{{characters}} letras sobrantes',
|
||||
charactersToGo: '{{characters}} letras sobrantes',
|
||||
charactersTooMany: '{{characters}} letras demasiados',
|
||||
checksPassing: '{{current}}/{{max}} las comprobaciones están pasando',
|
||||
good: 'Bien',
|
||||
imageAutoGenerationTip: 'La autogeneración recuperará la imagen de héroe seleccionada.',
|
||||
lengthTipDescription:
|
||||
'Esto debe estar entre {{minLength}} y {{maxLength}} caracteres. Para obtener ayuda sobre cómo escribir meta descripciones de calidad, consulte ',
|
||||
lengthTipTitle:
|
||||
'Debe tener entre {{minLength}} y {{maxLength}} caracteres. Para obtener ayuda sobre cómo escribir metatítulos de calidad, consulte ',
|
||||
noImage: 'Sin imagen',
|
||||
preview: 'Vista previa',
|
||||
previewDescription:
|
||||
'Las listas de resultados pueden variar segun la relevancia de buesqueda y el contenido.',
|
||||
tooLong: 'Demasiado largo',
|
||||
tooShort: 'Demasiado corto',
|
||||
},
|
||||
},
|
||||
fa: {
|
||||
$schema: './translation-schema.json',
|
||||
'plugin-seo': {
|
||||
almostThere: 'چیزیی باقی نمونده',
|
||||
autoGenerate: 'تولید خودکار',
|
||||
bestPractices: 'آموزش بیشتر',
|
||||
characterCount: '{{current}}/{{minLength}}-{{maxLength}} کلمه، ',
|
||||
charactersLeftOver: '{{characters}} باقی مانده',
|
||||
charactersToGo: '{{characters}} باقی مانده',
|
||||
charactersTooMany: '{{characters}} بیش از حد',
|
||||
checksPassing: '{{current}}/{{max}} بررسیها با موفقیت انجام شده است',
|
||||
good: 'خوب',
|
||||
imageAutoGenerationTip:
|
||||
'این قابلیت، تصویر فعلی بارگذاری شده در مجموعه محتوای شما را بازیابی میکند',
|
||||
lengthTipDescription:
|
||||
'این باید بین {{minLength}} و {{maxLength}} کلمه باشد. برای کمک در نوشتن توضیحات متا با کیفیت، مراجعه کنید به ',
|
||||
lengthTipTitle:
|
||||
'این باید بین {{minLength}} و {{maxLength}} کلمه باشد. برای کمک در نوشتن عناوین متا با کیفیت، مراجعه کنید به ',
|
||||
noImage: 'بدون تصویر',
|
||||
preview: 'پیشنمایش',
|
||||
previewDescription:
|
||||
'فهرست نتایج ممکن است بر اساس محتوا و متناسب با کلمه کلیدی جستجو شده باشند',
|
||||
tooLong: 'خیلی طولانی',
|
||||
tooShort: 'خیلی کوتاه',
|
||||
},
|
||||
},
|
||||
fr: {
|
||||
$schema: './translation-schema.json',
|
||||
'plugin-seo': {
|
||||
almostThere: 'On y est presque',
|
||||
autoGenerate: 'Auto-générer',
|
||||
bestPractices: 'bonnes pratiques',
|
||||
characterCount: '{{current}}/{{minLength}}-{{maxLength}} caractères, ',
|
||||
charactersLeftOver: '{{characters}} restants',
|
||||
charactersToGo: '{{characters}} à ajouter',
|
||||
charactersTooMany: '{{characters}} en trop',
|
||||
checksPassing: '{{current}}/{{max}} vérifications réussies',
|
||||
good: 'Bien',
|
||||
imageAutoGenerationTip: "L'auto-génération récupérera l'image principale sélectionnée.",
|
||||
lengthTipDescription:
|
||||
"Ceci devrait contenir entre {{minLength}} et {{maxLength}} caractères. Pour obtenir de l'aide pour rédiger des descriptions meta de qualité, consultez les ",
|
||||
lengthTipTitle:
|
||||
"Ceci devrait contenir entre {{minLength}} et {{maxLength}} caractères. Pour obtenir de l'aide pour rédiger des titres meta de qualité, consultez les ",
|
||||
noImage: "Pas d'image",
|
||||
preview: 'Aperçu',
|
||||
previewDescription:
|
||||
'Les résultats exacts peuvent varier en fonction du contenu et de la pertinence de la recherche.',
|
||||
tooLong: 'Trop long',
|
||||
tooShort: 'Trop court',
|
||||
},
|
||||
},
|
||||
pl: {
|
||||
$schema: './translation-schema.json',
|
||||
'plugin-seo': {
|
||||
almostThere: 'Prawie gotowe',
|
||||
autoGenerate: 'Wygeneruj automatycznie',
|
||||
bestPractices: 'najlepsze praktyki',
|
||||
characterCount: '{{current}}/{{minLength}}-{{maxLength}} znaków, ',
|
||||
charactersLeftOver: 'zostało {{characters}} znaków',
|
||||
charactersToGo: 'pozostało {{characters}} znaków',
|
||||
charactersTooMany: '{{characters}} znaków za dużo',
|
||||
checksPassing: '{{current}}/{{max}} testów zakończonych pomyślnie',
|
||||
good: 'Dobrze',
|
||||
imageAutoGenerationTip: 'Automatyczne generowanie pobierze wybrany główny obraz.',
|
||||
lengthTipDescription:
|
||||
'Długość powinna wynosić od {{minLength}} do {{maxLength}} znaków. Po porady dotyczące pisania wysokiej jakości meta opisów zobacz ',
|
||||
lengthTipTitle:
|
||||
'Długość powinna wynosić od {{minLength}} do {{maxLength}} znaków. Po porady dotyczące pisania wysokiej jakości meta tytułów zobacz ',
|
||||
noImage: 'Brak obrazu',
|
||||
preview: 'Podgląd',
|
||||
previewDescription:
|
||||
'Dokładne wyniki listowania mogą się różnić w zależności od treści i zgodności z kryteriami wyszukiwania.',
|
||||
tooLong: 'Zbyt długie',
|
||||
tooShort: 'Zbyt krótkie',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
46
pnpm-lock.yaml
generated
46
pnpm-lock.yaml
generated
@@ -295,6 +295,9 @@ importers:
|
||||
|
||||
packages/create-payload-app:
|
||||
dependencies:
|
||||
'@clack/prompts':
|
||||
specifier: ^0.7.0
|
||||
version: 0.7.0
|
||||
'@sindresorhus/slugify':
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.2
|
||||
@@ -331,15 +334,6 @@ importers:
|
||||
globby:
|
||||
specifier: 11.1.0
|
||||
version: 11.1.0
|
||||
handlebars:
|
||||
specifier: ^4.7.7
|
||||
version: 4.7.8
|
||||
ora:
|
||||
specifier: ^5.1.0
|
||||
version: 5.4.1
|
||||
prompts:
|
||||
specifier: ^2.4.2
|
||||
version: 2.4.2
|
||||
terminal-link:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
@@ -362,9 +356,6 @@ importers:
|
||||
'@types/node':
|
||||
specifier: ^16.6.2
|
||||
version: 16.18.85
|
||||
'@types/prompts':
|
||||
specifier: ^2.4.1
|
||||
version: 2.4.9
|
||||
|
||||
packages/db-mongodb:
|
||||
dependencies:
|
||||
@@ -2745,6 +2736,23 @@ packages:
|
||||
/@bcoe/v8-coverage@0.2.3:
|
||||
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
|
||||
|
||||
/@clack/core@0.3.4:
|
||||
resolution: {integrity: sha512-H4hxZDXgHtWTwV3RAVenqcC4VbJZNegbBjlPvzOzCouXtS2y3sDvlO3IsbrPNWuLWPPlYVYPghQdSF64683Ldw==}
|
||||
dependencies:
|
||||
picocolors: 1.0.0
|
||||
sisteransi: 1.0.5
|
||||
dev: false
|
||||
|
||||
/@clack/prompts@0.7.0:
|
||||
resolution: {integrity: sha512-0MhX9/B4iL6Re04jPrttDm+BsP8y6mS7byuv0BvXgdXhbV5PdlsHt55dvNsuBCPZ7xq1oTAOOuotR9NFbQyMSA==}
|
||||
dependencies:
|
||||
'@clack/core': 0.3.4
|
||||
picocolors: 1.0.0
|
||||
sisteransi: 1.0.5
|
||||
dev: false
|
||||
bundledDependencies:
|
||||
- is-unicode-supported
|
||||
|
||||
/@cspotcode/source-map-support@0.8.1:
|
||||
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -7451,6 +7459,7 @@ packages:
|
||||
buffer: 5.7.1
|
||||
inherits: 2.0.4
|
||||
readable-stream: 3.6.2
|
||||
dev: true
|
||||
|
||||
/body-parser@1.20.1:
|
||||
resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==}
|
||||
@@ -7790,6 +7799,7 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
restore-cursor: 3.1.0
|
||||
dev: true
|
||||
|
||||
/cli-cursor@4.0.0:
|
||||
resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==}
|
||||
@@ -7801,6 +7811,7 @@ packages:
|
||||
/cli-spinners@2.9.2:
|
||||
resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
|
||||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/cli-truncate@3.1.0:
|
||||
resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==}
|
||||
@@ -7852,6 +7863,7 @@ packages:
|
||||
/clone@1.0.4:
|
||||
resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
|
||||
engines: {node: '>=0.8'}
|
||||
dev: true
|
||||
|
||||
/clsx@1.2.1:
|
||||
resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
|
||||
@@ -8738,6 +8750,7 @@ packages:
|
||||
resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
|
||||
dependencies:
|
||||
clone: 1.0.4
|
||||
dev: true
|
||||
|
||||
/defer-to-connect@2.0.1:
|
||||
resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
|
||||
@@ -10725,6 +10738,7 @@ packages:
|
||||
wordwrap: 1.0.0
|
||||
optionalDependencies:
|
||||
uglify-js: 3.17.4
|
||||
dev: true
|
||||
|
||||
/hanji@0.0.5:
|
||||
resolution: {integrity: sha512-Abxw1Lq+TnYiL4BueXqMau222fPSPMFtya8HdpWsz/xVAhifXou71mPh/kY2+08RgFcVccjG3uZHs6K5HAe3zw==}
|
||||
@@ -11234,6 +11248,7 @@ packages:
|
||||
/is-interactive@1.0.0:
|
||||
resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/is-interactive@2.0.0:
|
||||
resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==}
|
||||
@@ -11366,6 +11381,7 @@ packages:
|
||||
/is-unicode-supported@0.1.0:
|
||||
resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/is-unicode-supported@1.3.0:
|
||||
resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==}
|
||||
@@ -12595,6 +12611,7 @@ packages:
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
is-unicode-supported: 0.1.0
|
||||
dev: true
|
||||
|
||||
/log-symbols@6.0.0:
|
||||
resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==}
|
||||
@@ -13060,6 +13077,7 @@ packages:
|
||||
|
||||
/neo-async@2.6.2:
|
||||
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
|
||||
dev: true
|
||||
|
||||
/netmask@2.0.2:
|
||||
resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==}
|
||||
@@ -13489,6 +13507,7 @@ packages:
|
||||
log-symbols: 4.1.0
|
||||
strip-ansi: 6.0.1
|
||||
wcwidth: 1.0.1
|
||||
dev: true
|
||||
|
||||
/ora@8.0.1:
|
||||
resolution: {integrity: sha512-ANIvzobt1rls2BDny5fWZ3ZVKyD6nscLvfFRpQgfWsythlcsVUC9kL0zq6j2Z5z9wwp1kd7wpsD/T9qNPVLCaQ==}
|
||||
@@ -15472,6 +15491,7 @@ packages:
|
||||
dependencies:
|
||||
onetime: 5.1.2
|
||||
signal-exit: 3.0.7
|
||||
dev: true
|
||||
|
||||
/restore-cursor@4.0.0:
|
||||
resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==}
|
||||
@@ -17014,6 +17034,7 @@ packages:
|
||||
engines: {node: '>=0.8.0'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/unbox-primitive@1.0.2:
|
||||
@@ -17242,6 +17263,7 @@ packages:
|
||||
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
|
||||
dependencies:
|
||||
defaults: 1.0.4
|
||||
dev: true
|
||||
|
||||
/web-streams-polyfill@3.3.3:
|
||||
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { mongooseAdapter } from '@payloadcms/db-mongodb' // database-adapter-import
|
||||
import { payloadCloud } from '@payloadcms/plugin-cloud'
|
||||
// import { payloadCloud } from '@payloadcms/plugin-cloud'
|
||||
import { lexicalEditor } from '@payloadcms/richtext-lexical' // editor-import
|
||||
import path from 'path'
|
||||
import { buildConfig } from 'payload/config'
|
||||
import sharp from 'sharp'
|
||||
// import sharp from 'sharp'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import { Users } from './src/collections/Users'
|
||||
import { Users } from './collections/Users'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
@@ -17,7 +17,7 @@ export default buildConfig({
|
||||
},
|
||||
collections: [Users],
|
||||
editor: lexicalEditor({}),
|
||||
plugins: [payloadCloud()],
|
||||
// plugins: [payloadCloud()], // TODO: Re-enable when cloud supports 3.0
|
||||
secret: process.env.PAYLOAD_SECRET || '',
|
||||
typescript: {
|
||||
outputFile: path.resolve(dirname, 'payload-types.ts'),
|
||||
@@ -33,5 +33,6 @@ export default buildConfig({
|
||||
|
||||
// This is temporary - we may make an adapter pattern
|
||||
// for this before reaching 3.0 stable
|
||||
sharp,
|
||||
|
||||
// sharp,
|
||||
})
|
||||
@@ -20,7 +20,7 @@
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"@payload-config": ["./payload.config.ts"]
|
||||
"@payload-config": ["./src/payload.config.ts"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
|
||||
@@ -7,12 +7,8 @@ import fs from 'fs'
|
||||
import path from 'path'
|
||||
import shelljs from 'shelljs'
|
||||
import tempy from 'tempy'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { promisify } from 'util'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
const readFile = promisify(fs.readFile)
|
||||
const writeFile = promisify(fs.writeFile)
|
||||
|
||||
@@ -29,13 +25,6 @@ describe('create-payload-app', () => {
|
||||
shelljs.exec('pnpm build:create-payload-app')
|
||||
})
|
||||
|
||||
describe('Next.js app template files', () => {
|
||||
it('should exist in dist', () => {
|
||||
const distPath = path.resolve(dirname, '../../packages/create-payload-app/dist/app/(payload)')
|
||||
expect(fs.existsSync(distPath)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe.each(Object.keys(nextCreateCommands))(`--init-next with %s`, (nextCmdKey) => {
|
||||
const projectDir = tempy.directory()
|
||||
beforeEach(async () => {
|
||||
@@ -74,19 +63,53 @@ describe('create-payload-app', () => {
|
||||
it('should install payload app in Next.js project', async () => {
|
||||
expect(fs.existsSync(projectDir)).toBe(true)
|
||||
|
||||
const result = await initNext({
|
||||
const firstResult = await initNext({
|
||||
'--debug': true,
|
||||
projectDir,
|
||||
nextConfigPath: path.resolve(projectDir, 'next.config.mjs'),
|
||||
dbType: 'mongodb',
|
||||
useDistFiles: true, // create-payload-app/dist/app/(payload)
|
||||
packageManager: 'pnpm',
|
||||
})
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
// Will fail because we detect top-level layout.tsx file
|
||||
expect(firstResult.success).toEqual(false)
|
||||
|
||||
const payloadFilesPath = path.resolve(result.userAppDir, '(payload)')
|
||||
// Move all files from app to top-level directory named `(app)`
|
||||
if (firstResult.success === false && 'nextAppDir' in firstResult) {
|
||||
fs.mkdirSync(path.resolve(firstResult.nextAppDir, '(app)'))
|
||||
fs.readdirSync(path.resolve(firstResult.nextAppDir)).forEach((file) => {
|
||||
if (file === '(app)') return
|
||||
fs.renameSync(
|
||||
path.resolve(firstResult.nextAppDir, file),
|
||||
path.resolve(firstResult.nextAppDir, '(app)', file),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// Rerun after moving files
|
||||
const result = await initNext({
|
||||
'--debug': true,
|
||||
projectDir,
|
||||
nextConfigPath: path.resolve(projectDir, 'next.config.mjs'),
|
||||
dbType: 'mongodb',
|
||||
useDistFiles: true, // create-payload-app/dist/app/(payload)
|
||||
packageManager: 'pnpm',
|
||||
})
|
||||
|
||||
expect(result.success).toEqual(true)
|
||||
expect(result.nextAppDir).toEqual(
|
||||
path.resolve(projectDir, result.isSrcDir ? 'src/app' : 'app'),
|
||||
)
|
||||
|
||||
const payloadFilesPath = path.resolve(result.nextAppDir, '(payload)')
|
||||
// shelljs.exec(`tree ${projectDir}`)
|
||||
expect(fs.existsSync(payloadFilesPath)).toBe(true)
|
||||
|
||||
const payloadConfig = path.resolve(projectDir, 'payload.config.ts')
|
||||
const payloadConfig = path.resolve(
|
||||
projectDir,
|
||||
result.isSrcDir ? 'src/payload.config.ts' : 'payload.config.ts',
|
||||
)
|
||||
expect(fs.existsSync(payloadConfig)).toBe(true)
|
||||
|
||||
const tsConfigPath = path.resolve(projectDir, 'tsconfig.json')
|
||||
@@ -95,7 +118,7 @@ describe('create-payload-app', () => {
|
||||
compilerOptions?: CompilerOptions
|
||||
}
|
||||
expect(userTsConfig.compilerOptions.paths?.['@payload-config']).toStrictEqual([
|
||||
'./payload.config.ts',
|
||||
`./${result.isSrcDir ? 'src/' : ''}payload.config.ts`,
|
||||
])
|
||||
|
||||
// TODO: Start the Next.js app and check if it runs
|
||||
|
||||
@@ -149,7 +149,7 @@ describe('Live Preview', () => {
|
||||
|
||||
test('global - can edit fields', async () => {
|
||||
await goToGlobalPreview(page, 'header')
|
||||
const field = page.locator('input#field-navItems__0__link__newTab')
|
||||
const field = page.locator('input#field-navItems__0__link____newTab')
|
||||
await expect(field).toBeVisible()
|
||||
await expect(field).toBeEnabled()
|
||||
await field.check()
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { Page } from '@playwright/test'
|
||||
|
||||
import { expect, test } from '@playwright/test'
|
||||
import path from 'path'
|
||||
import payload from 'payload'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import type { Page as PayloadPage } from './payload-types.js'
|
||||
@@ -11,6 +10,7 @@ import { initPageConsoleErrorCatch } from '../helpers.js'
|
||||
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
|
||||
import { initPayloadE2E } from '../helpers/initPayloadE2E.js'
|
||||
import config from './config.js'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
@@ -22,22 +22,32 @@ let parentId: string
|
||||
let draftChildId: string
|
||||
let childId: string
|
||||
|
||||
async function createPage(data: Partial<PayloadPage>): Promise<PayloadPage> {
|
||||
return payload.create({
|
||||
collection: 'pages',
|
||||
data,
|
||||
}) as unknown as Promise<PayloadPage>
|
||||
}
|
||||
|
||||
describe('Nested Docs Plugin', () => {
|
||||
beforeAll(async ({ browser }) => {
|
||||
const { serverURL } = await initPayloadE2E({ config, dirname })
|
||||
const { serverURL, payload } = await initPayloadE2E({ config, dirname })
|
||||
url = new AdminUrlUtil(serverURL, 'pages')
|
||||
|
||||
const context = await browser.newContext()
|
||||
page = await context.newPage()
|
||||
|
||||
initPageConsoleErrorCatch(page)
|
||||
|
||||
async function createPage({
|
||||
slug,
|
||||
title = 'Title page',
|
||||
parent,
|
||||
_status = 'published',
|
||||
}: Partial<PayloadPage>): Promise<PayloadPage> {
|
||||
return payload.create({
|
||||
collection: 'pages',
|
||||
data: {
|
||||
title,
|
||||
slug,
|
||||
_status,
|
||||
parent,
|
||||
},
|
||||
}) as unknown as Promise<PayloadPage>
|
||||
}
|
||||
|
||||
const parentPage = await createPage({ slug: 'parent-slug' })
|
||||
parentId = parentPage.id
|
||||
|
||||
@@ -70,41 +80,58 @@ describe('Nested Docs Plugin', () => {
|
||||
let slug = page.locator(slugClass).nth(0)
|
||||
await expect(slug).toHaveValue('child-slug')
|
||||
|
||||
const parentSlugInChildClass = '#field-breadcrumbs__0__url'
|
||||
// TODO: remove when error states are fixed
|
||||
const apiTabButton = page.locator('text=API')
|
||||
await apiTabButton.click()
|
||||
const breadcrumbs = page.locator('text=/parent-slug').first()
|
||||
await expect(breadcrumbs).toBeVisible()
|
||||
|
||||
const parentSlugInChild = page.locator(parentSlugInChildClass).nth(0)
|
||||
await expect(parentSlugInChild).toHaveValue('/parent-slug')
|
||||
// TODO: add back once error states are fixed
|
||||
// const parentSlugInChildClass = '#field-breadcrumbs__0__url'
|
||||
// const parentSlugInChild = page.locator(parentSlugInChildClass).nth(0)
|
||||
// await expect(parentSlugInChild).toHaveValue('/parent-slug')
|
||||
|
||||
await page.goto(url.edit(parentId))
|
||||
|
||||
slug = page.locator(slugClass).nth(0)
|
||||
await slug.fill('updated-parent-slug')
|
||||
await expect(slug).toHaveValue('updated-parent-slug')
|
||||
await page.locator(publishButtonClass).nth(0).click()
|
||||
|
||||
await page.waitForTimeout(1500)
|
||||
|
||||
await page.goto(url.edit(childId))
|
||||
await expect(parentSlugInChild).toHaveValue('/updated-parent-slug')
|
||||
|
||||
// TODO: remove when error states are fixed
|
||||
await apiTabButton.click()
|
||||
const updatedBreadcrumbs = page.locator('text=/updated-parent-slug').first()
|
||||
await expect(updatedBreadcrumbs).toBeVisible()
|
||||
|
||||
// TODO: add back once error states are fixed
|
||||
// await expect(parentSlugInChild).toHaveValue('/updated-parent-slug')
|
||||
})
|
||||
|
||||
test('Draft parent slug does not update child', async () => {
|
||||
await page.goto(url.edit(draftChildId))
|
||||
|
||||
const parentSlugInChildClass = '#field-breadcrumbs__0__url'
|
||||
// TODO: remove when error states are fixed
|
||||
const apiTabButton = page.locator('text=API')
|
||||
await apiTabButton.click()
|
||||
const breadcrumbs = page.locator('text=/parent-slug-draft').first()
|
||||
await expect(breadcrumbs).toBeVisible()
|
||||
|
||||
const parentSlugInChild = page.locator(parentSlugInChildClass).nth(0)
|
||||
await expect(parentSlugInChild).toHaveValue('/parent-slug-draft')
|
||||
// TODO: add back once error states are fixed
|
||||
// const parentSlugInChildClass = '#field-breadcrumbs__0__url'
|
||||
// const parentSlugInChild = page.locator(parentSlugInChildClass).nth(0)
|
||||
// await expect(parentSlugInChild).toHaveValue('/parent-slug-draft')
|
||||
|
||||
await page.goto(url.edit(parentId))
|
||||
|
||||
await page.locator(slugClass).nth(0).fill('parent-updated-draft')
|
||||
await page.locator(draftButtonClass).nth(0).click()
|
||||
|
||||
await page.waitForTimeout(1500)
|
||||
|
||||
await page.goto(url.edit(draftChildId))
|
||||
await expect(parentSlugInChild).toHaveValue('/parent-slug-draft')
|
||||
|
||||
await apiTabButton.click()
|
||||
const updatedBreadcrumbs = page.locator('text=/parent-slug-draft').first()
|
||||
await expect(updatedBreadcrumbs).toBeVisible()
|
||||
|
||||
// TODO: add back when error states are fixed
|
||||
// await expect(parentSlugInChild).toHaveValue('/parent-slug-draft')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
9
test/plugin-nested-docs/payload-types.ts
Normal file
9
test/plugin-nested-docs/payload-types.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export interface Page {
|
||||
id: string
|
||||
parent?: string
|
||||
slug: string
|
||||
_status?: 'draft' | 'published'
|
||||
title?: string
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
@@ -37,11 +37,10 @@ export default buildConfigWithDefaults({
|
||||
plugins: [
|
||||
seo({
|
||||
collections: ['users'],
|
||||
fields: [],
|
||||
tabbedUI: true,
|
||||
}),
|
||||
seo({
|
||||
collections: ['pages', 'posts'],
|
||||
collections: ['pages'],
|
||||
fieldOverrides: {
|
||||
title: {
|
||||
required: true,
|
||||
@@ -58,7 +57,6 @@ export default buildConfigWithDefaults({
|
||||
generateTitle: (data: any) => `Website.com — ${data?.doc?.title?.value}`,
|
||||
generateURL: ({ doc, locale }: any) =>
|
||||
`https://yoursite.com/${locale ? locale + '/' : ''}${doc?.slug?.value || ''}`,
|
||||
globals: ['settings'],
|
||||
tabbedUI: true,
|
||||
uploadsCollection: 'media',
|
||||
}),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { Page } from '@playwright/test'
|
||||
import type { Payload } from 'payload/types'
|
||||
|
||||
import { expect, test } from '@playwright/test'
|
||||
import path from 'path'
|
||||
@@ -11,20 +10,21 @@ import type { Page as PayloadPage } from './payload-types.js'
|
||||
import { initPageConsoleErrorCatch } from '../helpers.js'
|
||||
import { AdminUrlUtil } from '../helpers/adminUrlUtil.js'
|
||||
import { initPayloadE2E } from '../helpers/initPayloadE2E.js'
|
||||
import config from '../uploads/config.js'
|
||||
import config from './config.js'
|
||||
import { mediaSlug } from './shared.js'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
const { beforeAll, describe } = test
|
||||
|
||||
let url: AdminUrlUtil
|
||||
let page: Page
|
||||
let id: string
|
||||
let payload: Payload
|
||||
|
||||
describe('SEO Plugin', () => {
|
||||
beforeAll(async ({ browser }) => {
|
||||
const { serverURL } = await initPayloadE2E({ config, dirname })
|
||||
const { serverURL, payload } = await initPayloadE2E({ config, dirname })
|
||||
url = new AdminUrlUtil(serverURL, 'pages')
|
||||
|
||||
const context = await browser.newContext()
|
||||
@@ -68,15 +68,17 @@ describe('SEO Plugin', () => {
|
||||
test('Should auto-generate meta title when button is clicked in tabs', async () => {
|
||||
const contentTabsClass = '.tabs-field__tabs .tabs-field__tab-button'
|
||||
const autoGenerateButtonClass = '.group-field__wrap .render-fields div:nth-of-type(1) button'
|
||||
const metaTitleClass = '#field-title'
|
||||
const metaTitleClass = '#field-meta__title'
|
||||
|
||||
const secondTab = page.locator(contentTabsClass).nth(1)
|
||||
await secondTab.click()
|
||||
|
||||
const metaTitle = page.locator(metaTitleClass).nth(0)
|
||||
const metaTitle = page.locator(metaTitleClass)
|
||||
|
||||
await expect(metaTitle).toHaveValue('This is a test meta title')
|
||||
|
||||
const autoGenButton = page.locator(autoGenerateButtonClass).nth(0)
|
||||
await expect(autoGenButton).toContainText('Auto-generate')
|
||||
await autoGenButton.click()
|
||||
|
||||
await expect(metaTitle).toHaveValue('Website.com — Test Page')
|
||||
@@ -108,17 +110,17 @@ describe('SEO Plugin', () => {
|
||||
await page.goto(url.edit(id))
|
||||
const contentTabsClass = '.tabs-field__tabs .tabs-field__tab-button'
|
||||
const autoGenerateButtonClass = '.group-field__wrap .render-fields div:nth-of-type(1) button'
|
||||
const metaDescriptionClass = '#field-description'
|
||||
const metaDescriptionClass = '#field-meta__description'
|
||||
const previewClass =
|
||||
'#field-meta > div > div.render-fields.render-fields--margins-small > div:nth-child(6) > div:nth-child(3)'
|
||||
'#field-meta > div > div.render-fields.render-fields--margins-small > div:nth-child(6)'
|
||||
|
||||
const secondTab = page.locator(contentTabsClass).nth(1)
|
||||
await secondTab.click()
|
||||
|
||||
const metaDescription = page.locator(metaDescriptionClass).nth(0)
|
||||
const metaDescription = page.locator(metaDescriptionClass)
|
||||
await metaDescription.fill('My new amazing SEO description')
|
||||
|
||||
const preview = page.locator(previewClass).nth(0)
|
||||
const preview = page.locator(previewClass)
|
||||
await expect(preview).toContainText('https://yoursite.com/en/')
|
||||
await expect(preview).toContainText('This is a test meta title')
|
||||
await expect(preview).toContainText('My new amazing SEO description')
|
||||
@@ -147,6 +149,7 @@ describe('SEO Plugin', () => {
|
||||
// Change language to Spanish
|
||||
await languageField.click()
|
||||
await options.locator('text=Español').click()
|
||||
await expect(languageField).toContainText('Español')
|
||||
|
||||
// Navigate back to the page
|
||||
await page.goto(url.edit(id))
|
||||
|
||||
@@ -11,11 +11,7 @@
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"jsx": "preserve",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"noEmit": true,
|
||||
"outDir": "./dist",
|
||||
"resolveJsonModule": true,
|
||||
@@ -23,11 +19,7 @@
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": false,
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"@types/jest"
|
||||
],
|
||||
"types": ["jest", "node", "@types/jest"],
|
||||
"incremental": true,
|
||||
"isolatedModules": true,
|
||||
"plugins": [
|
||||
@@ -36,65 +28,26 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@payload-config": [
|
||||
"./test/_community/config.ts"
|
||||
],
|
||||
"@payloadcms/live-preview": [
|
||||
"./packages/live-preview/src"
|
||||
],
|
||||
"@payloadcms/live-preview-react": [
|
||||
"./packages/live-preview-react/src/index.ts"
|
||||
],
|
||||
"@payloadcms/ui/assets": [
|
||||
"./packages/ui/src/assets/index.ts"
|
||||
],
|
||||
"@payloadcms/ui/elements/*": [
|
||||
"./packages/ui/src/elements/*/index.tsx"
|
||||
],
|
||||
"@payloadcms/ui/fields/*": [
|
||||
"./packages/ui/src/fields/*/index.tsx"
|
||||
],
|
||||
"@payloadcms/ui/forms/*": [
|
||||
"./packages/ui/src/forms/*/index.tsx"
|
||||
],
|
||||
"@payloadcms/ui/graphics/*": [
|
||||
"./packages/ui/src/graphics/*/index.tsx"
|
||||
],
|
||||
"@payloadcms/ui/hooks/*": [
|
||||
"./packages/ui/src/hooks/*.ts"
|
||||
],
|
||||
"@payloadcms/ui/icons/*": [
|
||||
"./packages/ui/src/icons/*/index.tsx"
|
||||
],
|
||||
"@payloadcms/ui/providers/*": [
|
||||
"./packages/ui/src/providers/*/index.tsx"
|
||||
],
|
||||
"@payloadcms/ui/templates/*": [
|
||||
"./packages/ui/src/templates/*/index.tsx"
|
||||
],
|
||||
"@payloadcms/ui/utilities/*": [
|
||||
"./packages/ui/src/utilities/*.ts"
|
||||
],
|
||||
"@payloadcms/ui/scss": [
|
||||
"./packages/ui/src/scss.scss"
|
||||
],
|
||||
"@payloadcms/ui/scss/app.scss": [
|
||||
"./packages/ui/src/scss/app.scss"
|
||||
],
|
||||
"@payloadcms/next/*": [
|
||||
"./packages/next/src/*"
|
||||
],
|
||||
"@payloadcms/next": [
|
||||
"./packages/next/src/exports/*"
|
||||
]
|
||||
"@payload-config": ["./test/_community/config.ts"],
|
||||
"@payloadcms/live-preview": ["./packages/live-preview/src"],
|
||||
"@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"],
|
||||
"@payloadcms/ui/assets": ["./packages/ui/src/assets/index.ts"],
|
||||
"@payloadcms/ui/elements/*": ["./packages/ui/src/elements/*/index.tsx"],
|
||||
"@payloadcms/ui/fields/*": ["./packages/ui/src/fields/*/index.tsx"],
|
||||
"@payloadcms/ui/forms/*": ["./packages/ui/src/forms/*/index.tsx"],
|
||||
"@payloadcms/ui/graphics/*": ["./packages/ui/src/graphics/*/index.tsx"],
|
||||
"@payloadcms/ui/hooks/*": ["./packages/ui/src/hooks/*.ts"],
|
||||
"@payloadcms/ui/icons/*": ["./packages/ui/src/icons/*/index.tsx"],
|
||||
"@payloadcms/ui/providers/*": ["./packages/ui/src/providers/*/index.tsx"],
|
||||
"@payloadcms/ui/templates/*": ["./packages/ui/src/templates/*/index.tsx"],
|
||||
"@payloadcms/ui/utilities/*": ["./packages/ui/src/utilities/*.ts"],
|
||||
"@payloadcms/ui/scss": ["./packages/ui/src/scss.scss"],
|
||||
"@payloadcms/ui/scss/app.scss": ["./packages/ui/src/scss/app.scss"],
|
||||
"@payloadcms/next/*": ["./packages/next/src/*"],
|
||||
"@payloadcms/next": ["./packages/next/src/exports/*"]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
"build",
|
||||
"temp",
|
||||
"node_modules"
|
||||
],
|
||||
"exclude": ["dist", "build", "temp", "node_modules"],
|
||||
"composite": true,
|
||||
"references": [
|
||||
{
|
||||
@@ -155,9 +108,5 @@
|
||||
"path": "./packages/ui"
|
||||
}
|
||||
],
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
".next/types/**/*.ts",
|
||||
"scripts/**/*.ts"
|
||||
]
|
||||
"include": ["next-env.d.ts", ".next/types/**/*.ts", "scripts/**/*.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user