From 403a86feca36fe7eab49ff01c1bca9dd7f1eb859 Mon Sep 17 00:00:00 2001 From: Elliot DeNolf Date: Fri, 29 Mar 2024 14:12:30 -0400 Subject: [PATCH] chore(create-payload-app): configure db in init next flow --- .../src/lib/configure-payload-config.ts | 22 +++++-- .../src/lib/create-project.ts | 4 +- .../create-payload-app/src/lib/init-next.ts | 64 +++++++------------ packages/create-payload-app/src/main.ts | 50 +++++++++++---- .../create-payload-app/src/utils/messages.ts | 16 ++++- 5 files changed, 94 insertions(+), 62 deletions(-) diff --git a/packages/create-payload-app/src/lib/configure-payload-config.ts b/packages/create-payload-app/src/lib/configure-payload-config.ts index f24eda14b..91212f0f8 100644 --- a/packages/create-payload-app/src/lib/configure-payload-config.ts +++ b/packages/create-payload-app/src/lib/configure-payload-config.ts @@ -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 { if (!args.dbDetails) { return } try { - const payloadConfigPath = ( - await globby('**/payload.config.ts', { absolute: true, cwd: args.projectDir }) - )?.[0] + 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 : ''}`, + ) } } diff --git a/packages/create-payload-app/src/lib/create-project.ts b/packages/create-payload-app/src/lib/create-project.ts index cf2675c61..5babdc9cf 100644 --- a/packages/create-payload-app/src/lib/create-project.ts +++ b/packages/create-payload-app/src/lib/create-project.ts @@ -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 : ''}`) } } diff --git a/packages/create-payload-app/src/lib/init-next.ts b/packages/create-payload-app/src/lib/init-next.ts index e582d0813..7bf310c01 100644 --- a/packages/create-payload-app/src/lib/init-next.ts +++ b/packages/create-payload-app/src/lib/init-next.ts @@ -24,16 +24,23 @@ import { moveMessage } from '../utils/messages.js' import { wrapNextConfig } from './wrap-next-config.js' type InitNextArgs = Pick & { + 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 { 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 { // 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 { - 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) { diff --git a/packages/create-payload-app/src/main.ts b/packages/create-payload-app/src/main.ts index 6984211fd..e550edaa0 100644 --- a/packages/create-payload-app/src/main.ts +++ b/packages/create-payload-app/src/main.ts @@ -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 { - 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'] diff --git a/packages/create-payload-app/src/utils/messages.ts b/packages/create-payload-app/src/utils/messages.ts index c367dcf0d..e72d307d4 100644 --- a/packages/create-payload-app/src/utils/messages.ts +++ b/packages/create-payload-app/src/utils/messages.ts @@ -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.