diff --git a/packages/create-payload-app/package.json b/packages/create-payload-app/package.json index e2ff2895d..30c71d3d1 100644 --- a/packages/create-payload-app/package.json +++ b/packages/create-payload-app/package.json @@ -7,7 +7,8 @@ "create-payload-app": "bin/cli.js" }, "scripts": { - "build": "pnpm copyfiles && pnpm build:swc", + "build": "pnpm copyfiles && pnpm typecheck && pnpm build:swc", + "typecheck": "tsc", "copyfiles": "copyfiles -u 2 \"../../app/(payload)/**\" \"dist\"", "build:swc": "swc ./src -d ./dist --config-file .swcrc", "clean": "rimraf {dist,*.tsbuildinfo}", 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 7e3db445c..f24eda14b 100644 --- a/packages/create-payload-app/src/lib/configure-payload-config.ts +++ b/packages/create-payload-app/src/lib/configure-payload-config.ts @@ -1,10 +1,10 @@ import fse from 'fs-extra' -import path from 'path' +import globby from 'globby' import type { DbDetails } from '../types.js' import { warning } from '../utils/log.js' -import { bundlerPackages, dbPackages, editorPackages } from './packages.js' +import { dbReplacements } from './packages.js' /** Update payload config with necessary imports and adapters */ export async function configurePayloadConfig(args: { @@ -15,47 +15,10 @@ export async function configurePayloadConfig(args: { return } - // Update package.json - const packageJsonPath = path.resolve(args.projectDir, 'package.json') try { - const packageObj = await fse.readJson(packageJsonPath) - - packageObj.dependencies['payload'] = '^2.0.0' - - const dbPackage = dbPackages[args.dbDetails.type] - const bundlerPackage = bundlerPackages['webpack'] - const editorPackage = editorPackages['lexical'] - - // Delete all other db adapters - Object.values(dbPackages).forEach((p) => { - if (p.packageName !== dbPackage.packageName) { - delete packageObj.dependencies[p.packageName] - } - }) - - packageObj.dependencies[dbPackage.packageName] = dbPackage.version - packageObj.dependencies[bundlerPackage.packageName] = bundlerPackage.version - packageObj.dependencies[editorPackage.packageName] = editorPackage.version - - await fse.writeJson(packageJsonPath, packageObj, { spaces: 2 }) - } catch (err: unknown) { - warning(`Unable to configure Payload in package.json`) - warning(err instanceof Error ? err.message : '') - } - - try { - const possiblePaths = [ - path.resolve(args.projectDir, 'src/payload.config.ts'), - path.resolve(args.projectDir, 'src/payload/payload.config.ts'), - ] - - let payloadConfigPath: string | undefined - - possiblePaths.forEach((p) => { - if (fse.pathExistsSync(p) && !payloadConfigPath) { - payloadConfigPath = p - } - }) + const payloadConfigPath = ( + await globby('**/payload.config.ts', { absolute: true, cwd: args.projectDir }) + )?.[0] if (!payloadConfigPath) { warning('Unable to update payload.config.ts with plugins') @@ -65,9 +28,7 @@ export async function configurePayloadConfig(args: { const configContent = fse.readFileSync(payloadConfigPath, 'utf-8') const configLines = configContent.split('\n') - const dbReplacement = dbPackages[args.dbDetails.type] - const bundlerReplacement = bundlerPackages['webpack'] - const editorReplacement = editorPackages['slate'] + const dbReplacement = dbReplacements[args.dbDetails.type] let dbConfigStartLineIndex: number | undefined let dbConfigEndLineIndex: number | undefined @@ -76,21 +37,6 @@ export async function configurePayloadConfig(args: { if (l.includes('// database-adapter-import')) { configLines[i] = dbReplacement.importReplacement } - if (l.includes('// bundler-import')) { - configLines[i] = bundlerReplacement.importReplacement - } - - if (l.includes('// bundler-config')) { - configLines[i] = bundlerReplacement.configReplacement - } - - if (l.includes('// editor-import')) { - configLines[i] = editorReplacement.importReplacement - } - - if (l.includes('// editor-config')) { - configLines[i] = editorReplacement.configReplacement - } if (l.includes('// database-adapter-config-start')) { dbConfigStartLineIndex = i diff --git a/packages/create-payload-app/src/lib/create-project.spec.ts b/packages/create-payload-app/src/lib/create-project.spec.ts index 95e6cc0bc..a0ac16be4 100644 --- a/packages/create-payload-app/src/lib/create-project.spec.ts +++ b/packages/create-payload-app/src/lib/create-project.spec.ts @@ -1,8 +1,8 @@ import fse from 'fs-extra' import path from 'path' -import type { BundlerType, CliArgs, DbType, ProjectTemplate } from '../types.js' +import type { CliArgs, DbType, ProjectTemplate } from '../types.js' import { createProject } from './create-project.js' -import { bundlerPackages, dbPackages, editorPackages } from './packages.js' +import { dbReplacements } from './packages.js' import { getValidTemplates } from './templates.js' const projectDir = path.resolve(__dirname, './tmp') @@ -104,29 +104,16 @@ describe('createProject', () => { }, }) - const dbReplacement = dbPackages[db as DbType] - const bundlerReplacement = bundlerPackages[bundler as BundlerType] - const editorReplacement = editorPackages['slate'] + const dbReplacement = dbReplacements[db as DbType] const packageJsonPath = path.resolve(projectDir, 'package.json') const packageJson = fse.readJsonSync(packageJsonPath) - // Check deps - expect(packageJson.dependencies['payload']).toEqual('^2.0.0') - expect(packageJson.dependencies[dbReplacement.packageName]).toEqual(dbReplacement.version) - // Should only have one db adapter expect( Object.keys(packageJson.dependencies).filter((n) => n.startsWith('@payloadcms/db-')), ).toHaveLength(1) - expect(packageJson.dependencies[bundlerReplacement.packageName]).toEqual( - bundlerReplacement.version, - ) - expect(packageJson.dependencies[editorReplacement.packageName]).toEqual( - editorReplacement.version, - ) - let payloadConfigPath = path.resolve(projectDir, 'src/payload.config.ts') // Website and ecommerce templates have payload.config.ts in src/payload @@ -142,12 +129,6 @@ describe('createProject', () => { expect(content).not.toContain('// database-adapter-config-start') expect(content).not.toContain('// database-adapter-config-end') expect(content).toContain(dbReplacement.configReplacement.join('\n')) - - expect(content).not.toContain('// bundler-config-import') - expect(content).toContain(bundlerReplacement.importReplacement) - - expect(content).not.toContain('// bundler-config') - expect(content).toContain(bundlerReplacement.configReplacement) }) }) }) diff --git a/packages/create-payload-app/src/lib/create-project.ts b/packages/create-payload-app/src/lib/create-project.ts index 7e5547f59..e028a3054 100644 --- a/packages/create-payload-app/src/lib/create-project.ts +++ b/packages/create-payload-app/src/lib/create-project.ts @@ -2,14 +2,18 @@ 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 { error, success, warning } from '../utils/log.js' +import { debug, error, success, warning } from '../utils/log.js' import { configurePayloadConfig } from './configure-payload-config.js' +const filename = fileURLToPath(import.meta.url) +const dirname = path.dirname(filename) + async function createOrFindProjectDir(projectDir: string): Promise { const pathExists = await fse.pathExists(projectDir) if (!pathExists) { @@ -40,7 +44,7 @@ async function installDeps(args: { }) return true } catch (err: unknown) { - console.log({ err }) + error(`Error installing dependencies${err instanceof Error ? `: ${err.message}` : ''}.`) return false } } @@ -48,29 +52,34 @@ async function installDeps(args: { export async function createProject(args: { cliArgs: CliArgs dbDetails?: DbDetails - overrides?: { - gitBranch?: string - } packageManager: PackageManager projectDir: string projectName: string template: ProjectTemplate }): Promise { - const { cliArgs, dbDetails, overrides, packageManager, projectDir, projectName, template } = args + const { cliArgs, dbDetails, packageManager, projectDir, projectName, template } = args if (cliArgs['--dry-run']) { - console.log(`\n Dry run: Creating project in ${chalk.green(path.resolve(projectDir))}\n`) + console.log(`\n Dry run: Creating project in ${chalk.green(projectDir)}\n`) return } await createOrFindProjectDir(projectDir) - console.log(`\n Creating project in ${chalk.green(path.resolve(projectDir))}\n`) + console.log(`\n Creating project in ${chalk.green(projectDir)}\n`) - if ('url' in template) { + if (cliArgs['--local-template']) { + // Copy template from local path. For development purposes. + const localTemplate = path.resolve( + dirname, + '../../../../templates/', + cliArgs['--local-template'], + ) + await fse.copy(localTemplate, projectDir) + } else if ('url' in template) { let templateUrl = template.url - if (args.overrides?.gitBranch) { - templateUrl = `${template.url}#${overrides?.gitBranch}` + if (cliArgs['--template-branch']) { + templateUrl = `${template.url}#${cliArgs['--template-branch']}` debug(`Using template url: ${templateUrl}`) } const emitter = degit(templateUrl) @@ -88,14 +97,19 @@ export async function createProject(args: { await fse.remove(lockPath) } - spinner.text = 'Installing dependencies...' - const result = await installDeps({ cliArgs, packageManager, projectDir }) - spinner.stop() - spinner.clear() - if (result) { - success('Dependencies installed') + if (!cliArgs['--no-deps']) { + spinner.text = 'Installing dependencies...' + const result = await installDeps({ cliArgs, packageManager, projectDir }) + spinner.stop() + spinner.clear() + if (result) { + success('Dependencies installed') + } else { + error('Error installing dependencies') + } } else { - error('Error installing dependencies') + spinner.stop() + spinner.clear() } } diff --git a/packages/create-payload-app/src/lib/init-next.ts b/packages/create-payload-app/src/lib/init-next.ts index f68e6c570..71c187c44 100644 --- a/packages/create-payload-app/src/lib/init-next.ts +++ b/packages/create-payload-app/src/lib/init-next.ts @@ -2,7 +2,6 @@ import type { CompilerOptions } from 'typescript' import chalk from 'chalk' import { parse, stringify } from 'comment-json' -import { detect } from 'detect-package-manager' import execa from 'execa' import fs from 'fs' import fse from 'fs-extra' @@ -30,7 +29,6 @@ type InitNextArgs = Pick & { type InitNextResult = { reason?: string; success: boolean; userAppDir?: string } export async function initNext(args: InitNextArgs): Promise { - args.projectDir = args.projectDir || process.cwd() const { packageManager, projectDir } = args const templateResult = await applyPayloadTemplateFiles(args) if (!templateResult.success) return templateResult @@ -104,7 +102,7 @@ async function applyPayloadTemplateFiles(args: InitNextArgs): Promise = { +export const dbReplacements: Record = { mongodb: mongodbReplacement, postgres: postgresReplacement, } - -const webpackReplacement: BundlerReplacement = { - importReplacement: "import { webpackBundler } from '@payloadcms/bundler-webpack'", - packageName: '@payloadcms/bundler-webpack', - // Replacement of line containing `// bundler-config` - configReplacement: ' bundler: webpackBundler(),', - version: '^1.0.0', -} - -const viteReplacement: BundlerReplacement = { - configReplacement: ' bundler: viteBundler(),', - importReplacement: "import { viteBundler } from '@payloadcms/bundler-vite'", - packageName: '@payloadcms/bundler-vite', - version: '^0.x', // up to, not including 1.0.0 -} - -export const bundlerPackages: Record = { - vite: viteReplacement, - webpack: webpackReplacement, -} - -export const editorPackages: Record = { - lexical: { - configReplacement: ' editor: lexicalEditor({}),', - importReplacement: "import { lexicalEditor } from '@payloadcms/richtext-lexical'", - packageName: '@payloadcms/richtext-lexical', - version: '^0.x', // up to, not including 1.0.0 - }, - slate: { - configReplacement: ' editor: slateEditor({}),', - importReplacement: "import { slateEditor } from '@payloadcms/richtext-slate'", - packageName: '@payloadcms/richtext-slate', - version: '^1.0.0', - }, -} diff --git a/packages/create-payload-app/src/lib/select-db.ts b/packages/create-payload-app/src/lib/select-db.ts index 130930bc1..57c92df6f 100644 --- a/packages/create-payload-app/src/lib/select-db.ts +++ b/packages/create-payload-app/src/lib/select-db.ts @@ -58,26 +58,36 @@ export async function selectDb(args: CliArgs, projectName: string): Promise !!value.length, - }, - { - onCancel: () => { - process.exit(0) + let dbUri: string | undefined = undefined + const initialDbUri = `${dbChoice.dbConnectionPrefix}${ + projectName === '.' ? `payload-${getRandomDigitSuffix()}` : slugify(projectName) + }` + + if (args['--db-accept-recommended']) { + dbUri = initialDbUri + } else if (args['--db-connection-string']) { + dbUri = args['--db-connection-string'] + } else { + const dbUriRes = await prompts( + { + name: 'value', + type: 'text', + initial: 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 + } return { type: dbChoice.value, - dbUri: dbUriRes.value, + dbUri, } } diff --git a/packages/create-payload-app/src/lib/wrap-next-config.spec.ts b/packages/create-payload-app/src/lib/wrap-next-config.spec.ts index fcee72ffc..8119bbbc9 100644 --- a/packages/create-payload-app/src/lib/wrap-next-config.spec.ts +++ b/packages/create-payload-app/src/lib/wrap-next-config.spec.ts @@ -1,4 +1,4 @@ -import { parseAndInsertWithPayload, withPayloadImportStatement } from './wrap-next-config' +import { parseAndInsertWithPayload, withPayloadImportStatement } from './wrap-next-config.js' const defaultNextConfig = `/** @type {import('next').NextConfig} */ const nextConfig = {}; diff --git a/packages/create-payload-app/src/lib/wrap-next-config.ts b/packages/create-payload-app/src/lib/wrap-next-config.ts index fd9062781..0082da27d 100644 --- a/packages/create-payload-app/src/lib/wrap-next-config.ts +++ b/packages/create-payload-app/src/lib/wrap-next-config.ts @@ -3,7 +3,7 @@ import fs from 'fs' import globby from 'globby' import path from 'path' -export const withPayloadImportStatement = `import { withPayload } from '@payloadcms/next/withPayload'\n` +export const withPayloadImportStatement = `import { withPayload } from '@payloadcms/next'\n` export const wrapNextConfig = async (args: { projectDir: string }): Promise => { const foundConfig = (await globby('next.config.*js', { cwd: args.projectDir }))?.[0] diff --git a/packages/create-payload-app/src/main.ts b/packages/create-payload-app/src/main.ts index daf770292..6c958fa56 100644 --- a/packages/create-payload-app/src/main.ts +++ b/packages/create-payload-app/src/main.ts @@ -25,7 +25,10 @@ export class Main { this.args = arg( { '--db': String, + '--db-accept-recommended': Boolean, + '--db-connection-string': String, '--help': Boolean, + '--local-template': String, '--name': String, '--secret': String, '--template': String, @@ -63,10 +66,11 @@ export class Main { } const projectName = await parseProjectName(this.args) - const projectDir = + const projectDir = path.resolve( projectName === '.' || this.args['--init-next'] ? path.basename(process.cwd()) - : `./${slugify(projectName)}` + : `./${slugify(projectName)}`, + ) console.log(welcomeMessage) const packageManager = await getPackageManager(this.args, projectDir) @@ -95,7 +99,6 @@ export class Main { const template = await parseTemplate(this.args, validTemplates) switch (template.type) { - case 'local': case 'starter': { const dbDetails = await selectDb(this.args, projectName) const payloadSecret = generateSecret() diff --git a/packages/create-payload-app/src/types.ts b/packages/create-payload-app/src/types.ts index 571942fe4..7d7febde7 100644 --- a/packages/create-payload-app/src/types.ts +++ b/packages/create-payload-app/src/types.ts @@ -3,10 +3,13 @@ import type arg from 'arg' export interface Args extends arg.Spec { '--beta': BooleanConstructor '--db': StringConstructor + '--db-accept-recommended': BooleanConstructor + '--db-connection-string': StringConstructor '--debug': BooleanConstructor '--dry-run': BooleanConstructor '--help': BooleanConstructor '--init-next': BooleanConstructor + '--local-template': StringConstructor '--name': StringConstructor '--no-deps': BooleanConstructor '--secret': StringConstructor @@ -60,5 +63,4 @@ export type DbDetails = { type: DbType } -export type BundlerType = 'vite' | 'webpack' export type EditorType = 'lexical' | 'slate' diff --git a/packages/create-payload-app/tsconfig.json b/packages/create-payload-app/tsconfig.json index 7196960d5..fbecd13e6 100644 --- a/packages/create-payload-app/tsconfig.json +++ b/packages/create-payload-app/tsconfig.json @@ -7,17 +7,6 @@ "outDir": "./dist" /* Specify an output folder for all emitted files. */, "rootDir": "./src" /* Specify the root folder within your source files. */ }, - "exclude": [ - "dist", - "build", - "tests", - "test", - "node_modules", - ".eslintrc.js", - "src/**/*.spec.js", - "src/**/*.spec.jsx", - "src/**/*.spec.ts", - "src/**/*.spec.tsx" - ], + "exclude": ["dist", "build", "tests", "test", "node_modules", ".eslintrc.js"], "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.json"] }