chore(create-payload-app): configure db in init next flow
This commit is contained in:
@@ -9,19 +9,27 @@ import { dbReplacements } from './packages.js'
|
|||||||
/** Update payload config with necessary imports and adapters */
|
/** Update payload config with necessary imports and adapters */
|
||||||
export async function configurePayloadConfig(args: {
|
export async function configurePayloadConfig(args: {
|
||||||
dbDetails: DbDetails | undefined
|
dbDetails: DbDetails | undefined
|
||||||
projectDir: string
|
projectDirOrConfigPath: { payloadConfigPath: string } | { projectDir: string }
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
if (!args.dbDetails) {
|
if (!args.dbDetails) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payloadConfigPath = (
|
let payloadConfigPath: string | undefined
|
||||||
await globby('**/payload.config.ts', { absolute: true, cwd: args.projectDir })
|
if (!('payloadConfigPath' in args.projectDirOrConfigPath)) {
|
||||||
)?.[0]
|
payloadConfigPath = (
|
||||||
|
await globby('**/payload.config.ts', {
|
||||||
|
absolute: true,
|
||||||
|
cwd: args.projectDirOrConfigPath.projectDir,
|
||||||
|
})
|
||||||
|
)?.[0]
|
||||||
|
} else {
|
||||||
|
payloadConfigPath = args.projectDirOrConfigPath.payloadConfigPath
|
||||||
|
}
|
||||||
|
|
||||||
if (!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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +67,8 @@ export async function configurePayloadConfig(args: {
|
|||||||
|
|
||||||
fse.writeFileSync(payloadConfigPath, configLines.join('\n'))
|
fse.writeFileSync(payloadConfigPath, configLines.join('\n'))
|
||||||
} catch (err: unknown) {
|
} 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 : ''}`,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export async function createProject(args: {
|
|||||||
const spinner = ora('Checking latest Payload version...').start()
|
const spinner = ora('Checking latest Payload version...').start()
|
||||||
|
|
||||||
await updatePackageJSON({ projectDir, projectName })
|
await updatePackageJSON({ projectDir, projectName })
|
||||||
await configurePayloadConfig({ dbDetails, projectDir })
|
await configurePayloadConfig({ dbDetails, projectDirOrConfigPath: { projectDir } })
|
||||||
|
|
||||||
// Remove yarn.lock file. This is only desired in Payload Cloud.
|
// Remove yarn.lock file. This is only desired in Payload Cloud.
|
||||||
const lockPath = path.resolve(projectDir, 'yarn.lock')
|
const lockPath = path.resolve(projectDir, 'yarn.lock')
|
||||||
@@ -125,6 +125,6 @@ export async function updatePackageJSON(args: {
|
|||||||
packageObj.name = projectName
|
packageObj.name = projectName
|
||||||
await fse.writeJson(packageJsonPath, packageObj, { spaces: 2 })
|
await fse.writeJson(packageJsonPath, packageObj, { spaces: 2 })
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
warning('Unable to update name in package.json')
|
warning(`Unable to update name in package.json. ${err instanceof Error ? err.message : ''}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,16 +24,23 @@ import { moveMessage } from '../utils/messages.js'
|
|||||||
import { wrapNextConfig } from './wrap-next-config.js'
|
import { wrapNextConfig } from './wrap-next-config.js'
|
||||||
|
|
||||||
type InitNextArgs = Pick<CliArgs, '--debug'> & {
|
type InitNextArgs = Pick<CliArgs, '--debug'> & {
|
||||||
|
nextConfigPath: string
|
||||||
packageManager: PackageManager
|
packageManager: PackageManager
|
||||||
projectDir: string
|
projectDir: string
|
||||||
useDistFiles?: boolean
|
useDistFiles?: boolean
|
||||||
}
|
}
|
||||||
type InitNextResult = { nextAppDir?: string; reason?: string; success: boolean }
|
type InitNextResult =
|
||||||
|
| {
|
||||||
|
nextAppDir: string
|
||||||
|
payloadConfigPath: string
|
||||||
|
success: true
|
||||||
|
}
|
||||||
|
| { reason: string; success: false }
|
||||||
|
|
||||||
export async function initNext(args: InitNextArgs): Promise<InitNextResult> {
|
export async function initNext(args: InitNextArgs): Promise<InitNextResult> {
|
||||||
const { packageManager, projectDir } = args
|
const { packageManager, projectDir } = args
|
||||||
|
|
||||||
// Get app directory
|
// Get app directory. Could be top-level or src/app
|
||||||
const nextAppDir = (
|
const nextAppDir = (
|
||||||
await globby(['**/app'], {
|
await globby(['**/app'], {
|
||||||
absolute: true,
|
absolute: true,
|
||||||
@@ -49,27 +56,28 @@ export async function initNext(args: InitNextArgs): Promise<InitNextResult> {
|
|||||||
// Check for top-level layout.tsx
|
// Check for top-level layout.tsx
|
||||||
const layoutPath = path.resolve(nextAppDir, 'layout.tsx')
|
const layoutPath = path.resolve(nextAppDir, 'layout.tsx')
|
||||||
if (fs.existsSync(layoutPath)) {
|
if (fs.existsSync(layoutPath)) {
|
||||||
// Output directions for user to move all files from app to top-level directory named `(name)`
|
// Output directions for user to move all files from app to top-level directory named `(app)`
|
||||||
log(moveMessage({ nextAppDir, projectDir }))
|
log(moveMessage({ nextAppDir, projectDir }))
|
||||||
return { reason: 'Found existing layout.tsx in app directory', success: false }
|
return { reason: 'Found existing layout.tsx in app directory', success: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
const templateResult = await installAndConfigurePayload({
|
const configurationResult = installAndConfigurePayload({
|
||||||
...args,
|
...args,
|
||||||
nextAppDir,
|
nextAppDir,
|
||||||
useDistFiles: true, // Requires running 'pnpm pack-template-files' in cpa
|
useDistFiles: true, // Requires running 'pnpm pack-template-files' in cpa
|
||||||
})
|
})
|
||||||
if (!templateResult.success) return templateResult
|
|
||||||
|
if (!configurationResult.success) return configurationResult
|
||||||
|
|
||||||
const { success: installSuccess } = await installDeps(projectDir, packageManager)
|
const { success: installSuccess } = await installDeps(projectDir, packageManager)
|
||||||
if (!installSuccess) {
|
if (!installSuccess) {
|
||||||
return { ...templateResult, reason: 'Failed to install dependencies', success: false }
|
return { ...configurationResult, reason: 'Failed to install dependencies', success: false }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add `@payload-config` to tsconfig.json `paths`
|
// Add `@payload-config` to tsconfig.json `paths`
|
||||||
await addPayloadConfigToTsConfig(projectDir)
|
await addPayloadConfigToTsConfig(projectDir)
|
||||||
|
|
||||||
return templateResult
|
return configurationResult
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addPayloadConfigToTsConfig(projectDir: string) {
|
async function addPayloadConfigToTsConfig(projectDir: string) {
|
||||||
@@ -93,10 +101,8 @@ async function addPayloadConfigToTsConfig(projectDir: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function installAndConfigurePayload(
|
function installAndConfigurePayload(args: InitNextArgs & { nextAppDir: string }): InitNextResult {
|
||||||
args: InitNextArgs & { nextAppDir: string },
|
const { '--debug': debug, nextAppDir, nextConfigPath, projectDir, useDistFiles } = args
|
||||||
): Promise<InitNextResult> {
|
|
||||||
const { '--debug': debug, nextAppDir, projectDir, useDistFiles } = args
|
|
||||||
|
|
||||||
info('Initializing Payload app in Next.js project', 1)
|
info('Initializing Payload app in Next.js project', 1)
|
||||||
|
|
||||||
@@ -108,32 +114,12 @@ async function installAndConfigurePayload(
|
|||||||
return { reason: `Could not find specified project directory at ${projectDir}`, success: false }
|
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.`,
|
|
||||||
success: false,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (debug) logDebug(`Found Next config at ${nextConfigPath}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const templateFilesPath =
|
const templateFilesPath =
|
||||||
// dirname.endsWith('dist') || useDistFiles
|
|
||||||
// ? path.resolve(dirname, '../..', 'dist/template')
|
|
||||||
// : path.resolve(dirname, '../../../../templates/blank-3.0')
|
|
||||||
dirname.endsWith('dist') || useDistFiles
|
dirname.endsWith('dist') || useDistFiles
|
||||||
? path.resolve(dirname, '../..', 'dist/template')
|
? path.resolve(dirname, '../..', 'dist/template')
|
||||||
: path.resolve(dirname, '../../../../templates/blank-3.0')
|
: 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)) {
|
if (!fs.existsSync(templateFilesPath)) {
|
||||||
return {
|
return {
|
||||||
@@ -141,13 +127,7 @@ async function installAndConfigurePayload(
|
|||||||
success: false,
|
success: false,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (debug) logDebug('Found template source files')
|
logDebug('Found template source files')
|
||||||
}
|
|
||||||
|
|
||||||
if (!fs.existsSync(nextAppDir)) {
|
|
||||||
return { reason: `Could not find user app directory inside ${projectDir}`, success: false }
|
|
||||||
} else {
|
|
||||||
logDebug(`Found user app directory: ${nextAppDir}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logDebug(`Copying template files from ${templateFilesPath} to ${nextAppDir}`)
|
logDebug(`Copying template files from ${templateFilesPath} to ${nextAppDir}`)
|
||||||
@@ -162,7 +142,11 @@ async function installAndConfigurePayload(
|
|||||||
wrapNextConfig({ nextConfigPath })
|
wrapNextConfig({ nextConfigPath })
|
||||||
|
|
||||||
success('Successfully initialized.')
|
success('Successfully initialized.')
|
||||||
return { nextAppDir, success: true }
|
return {
|
||||||
|
nextAppDir,
|
||||||
|
payloadConfigPath: path.resolve(nextAppDir, '../payload.config.ts'),
|
||||||
|
success: true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function installDeps(projectDir: string, packageManager: PackageManager) {
|
async function installDeps(projectDir: string, packageManager: PackageManager) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import path from 'path'
|
|||||||
|
|
||||||
import type { CliArgs, PackageManager } from './types.js'
|
import type { CliArgs, PackageManager } from './types.js'
|
||||||
|
|
||||||
|
import { configurePayloadConfig } from './lib/configure-payload-config.js'
|
||||||
import { createProject } from './lib/create-project.js'
|
import { createProject } from './lib/create-project.js'
|
||||||
import { generateSecret } from './lib/generate-secret.js'
|
import { generateSecret } from './lib/generate-secret.js'
|
||||||
import { initNext } from './lib/init-next.js'
|
import { initNext } from './lib/init-next.js'
|
||||||
@@ -14,8 +15,13 @@ import { parseTemplate } from './lib/parse-template.js'
|
|||||||
import { selectDb } from './lib/select-db.js'
|
import { selectDb } from './lib/select-db.js'
|
||||||
import { getValidTemplates, validateTemplate } from './lib/templates.js'
|
import { getValidTemplates, validateTemplate } from './lib/templates.js'
|
||||||
import { writeEnvFile } from './lib/write-env-file.js'
|
import { writeEnvFile } from './lib/write-env-file.js'
|
||||||
import { debug, error, log, success } from './utils/log.js'
|
import { error, log, success } from './utils/log.js'
|
||||||
import { helpMessage, successMessage, welcomeMessage } from './utils/messages.js'
|
import {
|
||||||
|
helpMessage,
|
||||||
|
successMessage,
|
||||||
|
successfulNextInit,
|
||||||
|
welcomeMessage,
|
||||||
|
} from './utils/messages.js'
|
||||||
|
|
||||||
export class Main {
|
export class Main {
|
||||||
args: CliArgs
|
args: CliArgs
|
||||||
@@ -59,7 +65,9 @@ export class Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async init(): Promise<void> {
|
async init(): Promise<void> {
|
||||||
const initContext = {
|
const initContext: {
|
||||||
|
nextConfigPath: string | undefined
|
||||||
|
} = {
|
||||||
nextConfigPath: undefined,
|
nextConfigPath: undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,10 +79,10 @@ export class Main {
|
|||||||
log(welcomeMessage)
|
log(welcomeMessage)
|
||||||
|
|
||||||
// Detect if inside Next.js project
|
// Detect if inside Next.js project
|
||||||
const foundConfig = (
|
const nextConfigPath = (
|
||||||
await globby('next.config.*js', { absolute: true, cwd: process.cwd() })
|
await globby('next.config.*js', { absolute: true, cwd: process.cwd() })
|
||||||
)?.[0]
|
)?.[0]
|
||||||
initContext.nextConfigPath = foundConfig
|
initContext.nextConfigPath = nextConfigPath
|
||||||
|
|
||||||
success('Next.js app detected.')
|
success('Next.js app detected.')
|
||||||
|
|
||||||
@@ -85,22 +93,38 @@ export class Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const projectName = await parseProjectName(this.args)
|
const projectName = await parseProjectName(this.args)
|
||||||
const projectDir = foundConfig
|
const projectDir = nextConfigPath
|
||||||
? path.dirname(foundConfig)
|
? path.dirname(nextConfigPath)
|
||||||
: path.resolve(process.cwd(), slugify(projectName))
|
: path.resolve(process.cwd(), slugify(projectName))
|
||||||
|
|
||||||
const packageManager = await getPackageManager(this.args, projectDir)
|
const packageManager = await getPackageManager(this.args, projectDir)
|
||||||
debug(`Using package manager: ${packageManager}`)
|
|
||||||
|
|
||||||
if (foundConfig) {
|
if (nextConfigPath) {
|
||||||
const result = await initNext({ ...this.args, packageManager, projectDir })
|
const result = await initNext({
|
||||||
if (!result.success) {
|
...this.args,
|
||||||
error(result.reason || 'Failed to initialize Payload app in Next.js project')
|
nextConfigPath,
|
||||||
|
packageManager,
|
||||||
|
projectDir,
|
||||||
|
})
|
||||||
|
if (result.success === false) {
|
||||||
|
error(result.reason)
|
||||||
|
process.exit(1)
|
||||||
} else {
|
} else {
|
||||||
success('Payload app successfully initialized in Next.js project')
|
success('Payload app successfully initialized in Next.js project')
|
||||||
}
|
}
|
||||||
process.exit(result.success ? 0 : 1)
|
|
||||||
|
const dbDetails = await selectDb(this.args, projectName)
|
||||||
|
await configurePayloadConfig({
|
||||||
|
dbDetails,
|
||||||
|
projectDirOrConfigPath: {
|
||||||
|
payloadConfigPath: result.payloadConfigPath,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
// TODO: This should continue the normal prompt flow
|
// TODO: This should continue the normal prompt flow
|
||||||
|
success('Payload project successfully created')
|
||||||
|
log(successfulNextInit())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const templateArg = this.args['--template']
|
const templateArg = this.args['--template']
|
||||||
|
|||||||
@@ -68,6 +68,20 @@ export function successMessage(projectDir: string, packageManager: string): stri
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function successfulNextInit(): string {
|
||||||
|
return `
|
||||||
|
${header('Successful Payload Installation!')}
|
||||||
|
|
||||||
|
${header('Documentation:')}
|
||||||
|
|
||||||
|
- ${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 {
|
export function moveMessage(args: { nextAppDir: string; projectDir: string }): string {
|
||||||
return `
|
return `
|
||||||
${header('Next Steps:')}
|
${header('Next Steps:')}
|
||||||
@@ -77,7 +91,7 @@ export function moveMessage(args: { nextAppDir: string; projectDir: string }): s
|
|||||||
${chalk.bold('To continue:')}
|
${chalk.bold('To continue:')}
|
||||||
|
|
||||||
- Move all files from ${args.nextAppDir} to a top-level directory named ${chalk.bold(
|
- Move all files from ${args.nextAppDir} to a top-level directory named ${chalk.bold(
|
||||||
'(name)',
|
'(app)',
|
||||||
)} or similar.
|
)} or similar.
|
||||||
|
|
||||||
Once moved, rerun the create-payload-app command again.
|
Once moved, rerun the create-payload-app command again.
|
||||||
|
|||||||
Reference in New Issue
Block a user