chore(create-payload-app): configure db in init next flow

This commit is contained in:
Elliot DeNolf
2024-03-29 14:12:30 -04:00
parent 77f401d977
commit 403a86feca
5 changed files with 94 additions and 62 deletions

View File

@@ -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 : ''}`,
)
}
}

View File

@@ -90,7 +90,7 @@ export async function createProject(args: {
const spinner = ora('Checking latest Payload version...').start()
await updatePackageJSON({ projectDir, projectName })
await configurePayloadConfig({ dbDetails, projectDir })
await configurePayloadConfig({ dbDetails, projectDirOrConfigPath: { projectDir } })
// Remove yarn.lock file. This is only desired in Payload Cloud.
const lockPath = path.resolve(projectDir, 'yarn.lock')
@@ -125,6 +125,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 : ''}`)
}
}

View File

@@ -24,16 +24,23 @@ import { moveMessage } from '../utils/messages.js'
import { wrapNextConfig } from './wrap-next-config.js'
type InitNextArgs = Pick<CliArgs, '--debug'> & {
nextConfigPath: string
packageManager: PackageManager
projectDir: string
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> {
const { packageManager, projectDir } = args
// Get app directory
// Get app directory. Could be top-level or src/app
const nextAppDir = (
await globby(['**/app'], {
absolute: true,
@@ -49,27 +56,28 @@ export async function initNext(args: InitNextArgs): Promise<InitNextResult> {
// 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 `(name)`
// Output directions for user to move all files from app to top-level directory named `(app)`
log(moveMessage({ nextAppDir, projectDir }))
return { reason: 'Found existing layout.tsx in app directory', success: false }
}
const templateResult = await installAndConfigurePayload({
const configurationResult = installAndConfigurePayload({
...args,
nextAppDir,
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)
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`
await addPayloadConfigToTsConfig(projectDir)
return templateResult
return configurationResult
}
async function addPayloadConfigToTsConfig(projectDir: string) {
@@ -93,10 +101,8 @@ async function addPayloadConfigToTsConfig(projectDir: string) {
}
}
async function installAndConfigurePayload(
args: InitNextArgs & { nextAppDir: string },
): Promise<InitNextResult> {
const { '--debug': debug, nextAppDir, projectDir, useDistFiles } = args
function installAndConfigurePayload(args: InitNextArgs & { nextAppDir: string }): InitNextResult {
const { '--debug': debug, nextAppDir, nextConfigPath, projectDir, useDistFiles } = args
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 }
}
// 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 =
// dirname.endsWith('dist') || useDistFiles
// ? path.resolve(dirname, '../..', 'dist/template')
// : path.resolve(dirname, '../../../../templates/blank-3.0')
dirname.endsWith('dist') || useDistFiles
? 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 {
@@ -141,13 +127,7 @@ async function installAndConfigurePayload(
success: false,
}
} else {
if (debug) 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('Found template source files')
}
logDebug(`Copying template files from ${templateFilesPath} to ${nextAppDir}`)
@@ -162,7 +142,11 @@ async function installAndConfigurePayload(
wrapNextConfig({ nextConfigPath })
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) {

View File

@@ -6,6 +6,7 @@ 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 +15,13 @@ 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 { debug, error, log, success } from './utils/log.js'
import { helpMessage, successMessage, welcomeMessage } from './utils/messages.js'
import { error, log, success } from './utils/log.js'
import {
helpMessage,
successMessage,
successfulNextInit,
welcomeMessage,
} from './utils/messages.js'
export class Main {
args: CliArgs
@@ -59,7 +65,9 @@ export class Main {
}
async init(): Promise<void> {
const initContext = {
const initContext: {
nextConfigPath: string | undefined
} = {
nextConfigPath: undefined,
}
@@ -71,10 +79,10 @@ export class Main {
log(welcomeMessage)
// Detect if inside Next.js project
const foundConfig = (
const nextConfigPath = (
await globby('next.config.*js', { absolute: true, cwd: process.cwd() })
)?.[0]
initContext.nextConfigPath = foundConfig
initContext.nextConfigPath = nextConfigPath
success('Next.js app detected.')
@@ -85,22 +93,38 @@ export class Main {
}
const projectName = await parseProjectName(this.args)
const projectDir = foundConfig
? path.dirname(foundConfig)
const projectDir = nextConfigPath
? path.dirname(nextConfigPath)
: path.resolve(process.cwd(), slugify(projectName))
const packageManager = await getPackageManager(this.args, projectDir)
debug(`Using package manager: ${packageManager}`)
if (foundConfig) {
const result = await initNext({ ...this.args, packageManager, projectDir })
if (!result.success) {
error(result.reason || 'Failed to initialize Payload app in Next.js project')
if (nextConfigPath) {
const result = await initNext({
...this.args,
nextConfigPath,
packageManager,
projectDir,
})
if (result.success === false) {
error(result.reason)
process.exit(1)
} else {
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
success('Payload project successfully created')
log(successfulNextInit())
return
}
const templateArg = this.args['--template']

View File

@@ -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 {
return `
${header('Next Steps:')}
@@ -77,7 +91,7 @@ export function moveMessage(args: { nextAppDir: string; projectDir: string }): s
${chalk.bold('To continue:')}
- Move all files from ${args.nextAppDir} to a top-level directory named ${chalk.bold(
'(name)',
'(app)',
)} or similar.
Once moved, rerun the create-payload-app command again.