chore(create-payload-app): remove unneeded 2.0 func, add cli overrides

This commit is contained in:
Elliot DeNolf
2024-03-26 14:22:56 -04:00
parent 05bb73bb7c
commit f35b8b05e8
12 changed files with 92 additions and 197 deletions

View File

@@ -7,7 +7,8 @@
"create-payload-app": "bin/cli.js" "create-payload-app": "bin/cli.js"
}, },
"scripts": { "scripts": {
"build": "pnpm copyfiles && pnpm build:swc", "build": "pnpm copyfiles && pnpm typecheck && pnpm build:swc",
"typecheck": "tsc",
"copyfiles": "copyfiles -u 2 \"../../app/(payload)/**\" \"dist\"", "copyfiles": "copyfiles -u 2 \"../../app/(payload)/**\" \"dist\"",
"build:swc": "swc ./src -d ./dist --config-file .swcrc", "build:swc": "swc ./src -d ./dist --config-file .swcrc",
"clean": "rimraf {dist,*.tsbuildinfo}", "clean": "rimraf {dist,*.tsbuildinfo}",

View File

@@ -1,10 +1,10 @@
import fse from 'fs-extra' import fse from 'fs-extra'
import path from 'path' import globby from 'globby'
import type { DbDetails } from '../types.js' import type { DbDetails } from '../types.js'
import { warning } from '../utils/log.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 */ /** Update payload config with necessary imports and adapters */
export async function configurePayloadConfig(args: { export async function configurePayloadConfig(args: {
@@ -15,47 +15,10 @@ export async function configurePayloadConfig(args: {
return return
} }
// Update package.json
const packageJsonPath = path.resolve(args.projectDir, 'package.json')
try { try {
const packageObj = await fse.readJson(packageJsonPath) const payloadConfigPath = (
await globby('**/payload.config.ts', { absolute: true, cwd: args.projectDir })
packageObj.dependencies['payload'] = '^2.0.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
}
})
if (!payloadConfigPath) { if (!payloadConfigPath) {
warning('Unable to update payload.config.ts with plugins') 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 configContent = fse.readFileSync(payloadConfigPath, 'utf-8')
const configLines = configContent.split('\n') const configLines = configContent.split('\n')
const dbReplacement = dbPackages[args.dbDetails.type] const dbReplacement = dbReplacements[args.dbDetails.type]
const bundlerReplacement = bundlerPackages['webpack']
const editorReplacement = editorPackages['slate']
let dbConfigStartLineIndex: number | undefined let dbConfigStartLineIndex: number | undefined
let dbConfigEndLineIndex: number | undefined let dbConfigEndLineIndex: number | undefined
@@ -76,21 +37,6 @@ export async function configurePayloadConfig(args: {
if (l.includes('// database-adapter-import')) { if (l.includes('// database-adapter-import')) {
configLines[i] = dbReplacement.importReplacement 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')) { if (l.includes('// database-adapter-config-start')) {
dbConfigStartLineIndex = i dbConfigStartLineIndex = i

View File

@@ -1,8 +1,8 @@
import fse from 'fs-extra' import fse from 'fs-extra'
import path from 'path' 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 { createProject } from './create-project.js'
import { bundlerPackages, dbPackages, editorPackages } from './packages.js' import { dbReplacements } from './packages.js'
import { getValidTemplates } from './templates.js' import { getValidTemplates } from './templates.js'
const projectDir = path.resolve(__dirname, './tmp') const projectDir = path.resolve(__dirname, './tmp')
@@ -104,29 +104,16 @@ describe('createProject', () => {
}, },
}) })
const dbReplacement = dbPackages[db as DbType] const dbReplacement = dbReplacements[db as DbType]
const bundlerReplacement = bundlerPackages[bundler as BundlerType]
const editorReplacement = editorPackages['slate']
const packageJsonPath = path.resolve(projectDir, 'package.json') const packageJsonPath = path.resolve(projectDir, 'package.json')
const packageJson = fse.readJsonSync(packageJsonPath) 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 // Should only have one db adapter
expect( expect(
Object.keys(packageJson.dependencies).filter((n) => n.startsWith('@payloadcms/db-')), Object.keys(packageJson.dependencies).filter((n) => n.startsWith('@payloadcms/db-')),
).toHaveLength(1) ).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') let payloadConfigPath = path.resolve(projectDir, 'src/payload.config.ts')
// Website and ecommerce templates have payload.config.ts in src/payload // 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-start')
expect(content).not.toContain('// database-adapter-config-end') expect(content).not.toContain('// database-adapter-config-end')
expect(content).toContain(dbReplacement.configReplacement.join('\n')) 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)
}) })
}) })
}) })

View File

@@ -2,14 +2,18 @@ import chalk from 'chalk'
import degit from 'degit' import degit from 'degit'
import execa from 'execa' import execa from 'execa'
import fse from 'fs-extra' import fse from 'fs-extra'
import { fileURLToPath } from 'node:url'
import ora from 'ora' import ora from 'ora'
import path from 'path' import path from 'path'
import type { CliArgs, DbDetails, PackageManager, ProjectTemplate } from '../types.js' 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' import { configurePayloadConfig } from './configure-payload-config.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
async function createOrFindProjectDir(projectDir: string): Promise<void> { async function createOrFindProjectDir(projectDir: string): Promise<void> {
const pathExists = await fse.pathExists(projectDir) const pathExists = await fse.pathExists(projectDir)
if (!pathExists) { if (!pathExists) {
@@ -40,7 +44,7 @@ async function installDeps(args: {
}) })
return true return true
} catch (err: unknown) { } catch (err: unknown) {
console.log({ err }) error(`Error installing dependencies${err instanceof Error ? `: ${err.message}` : ''}.`)
return false return false
} }
} }
@@ -48,29 +52,34 @@ async function installDeps(args: {
export async function createProject(args: { export async function createProject(args: {
cliArgs: CliArgs cliArgs: CliArgs
dbDetails?: DbDetails dbDetails?: DbDetails
overrides?: {
gitBranch?: string
}
packageManager: PackageManager packageManager: PackageManager
projectDir: string projectDir: string
projectName: string projectName: string
template: ProjectTemplate template: ProjectTemplate
}): Promise<void> { }): Promise<void> {
const { cliArgs, dbDetails, overrides, packageManager, projectDir, projectName, template } = args const { cliArgs, dbDetails, packageManager, projectDir, projectName, template } = args
if (cliArgs['--dry-run']) { 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 return
} }
await createOrFindProjectDir(projectDir) 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 let templateUrl = template.url
if (args.overrides?.gitBranch) { if (cliArgs['--template-branch']) {
templateUrl = `${template.url}#${overrides?.gitBranch}` templateUrl = `${template.url}#${cliArgs['--template-branch']}`
debug(`Using template url: ${templateUrl}`) debug(`Using template url: ${templateUrl}`)
} }
const emitter = degit(templateUrl) const emitter = degit(templateUrl)
@@ -88,6 +97,7 @@ export async function createProject(args: {
await fse.remove(lockPath) await fse.remove(lockPath)
} }
if (!cliArgs['--no-deps']) {
spinner.text = 'Installing dependencies...' spinner.text = 'Installing dependencies...'
const result = await installDeps({ cliArgs, packageManager, projectDir }) const result = await installDeps({ cliArgs, packageManager, projectDir })
spinner.stop() spinner.stop()
@@ -97,6 +107,10 @@ export async function createProject(args: {
} else { } else {
error('Error installing dependencies') error('Error installing dependencies')
} }
} else {
spinner.stop()
spinner.clear()
}
} }
export async function updatePackageJSON(args: { export async function updatePackageJSON(args: {

View File

@@ -2,7 +2,6 @@ import type { CompilerOptions } from 'typescript'
import chalk from 'chalk' import chalk from 'chalk'
import { parse, stringify } from 'comment-json' import { parse, stringify } from 'comment-json'
import { detect } from 'detect-package-manager'
import execa from 'execa' import execa from 'execa'
import fs from 'fs' import fs from 'fs'
import fse from 'fs-extra' import fse from 'fs-extra'
@@ -30,7 +29,6 @@ type InitNextArgs = Pick<CliArgs, '--debug'> & {
type InitNextResult = { reason?: string; success: boolean; userAppDir?: string } type InitNextResult = { reason?: string; success: boolean; userAppDir?: string }
export async function initNext(args: InitNextArgs): Promise<InitNextResult> { export async function initNext(args: InitNextArgs): Promise<InitNextResult> {
args.projectDir = args.projectDir || process.cwd()
const { packageManager, projectDir } = args const { packageManager, projectDir } = args
const templateResult = await applyPayloadTemplateFiles(args) const templateResult = await applyPayloadTemplateFiles(args)
if (!templateResult.success) return templateResult if (!templateResult.success) return templateResult
@@ -104,7 +102,7 @@ async function applyPayloadTemplateFiles(args: InitNextArgs): Promise<InitNextRe
} }
// Next.js configs can be next.config.js, next.config.mjs, etc. // Next.js configs can be next.config.js, next.config.mjs, etc.
const foundConfig = (await globby('next.config.*js', { cwd: projectDir }))?.[0] const foundConfig = (await globby('next.config.*js', { absolute: true, cwd: projectDir }))?.[0]
if (!foundConfig) { if (!foundConfig) {
throw new Error(`No next.config.js found at ${projectDir}`) throw new Error(`No next.config.js found at ${projectDir}`)
@@ -137,11 +135,14 @@ async function applyPayloadTemplateFiles(args: InitNextArgs): Promise<InitNextRe
} }
// src/app or app // src/app or app
const userAppDirGlob = await globby(['**/app'], { const userAppDir = (
await globby(['**/app'], {
absolute: true,
cwd: projectDir, cwd: projectDir,
onlyDirectories: true, onlyDirectories: true,
}) })
const userAppDir = path.resolve(projectDir, userAppDirGlob?.[0]) )?.[0]
if (!fs.existsSync(userAppDir)) { if (!fs.existsSync(userAppDir)) {
return { reason: `Could not find user app directory inside ${projectDir}`, success: false } return { reason: `Could not find user app directory inside ${projectDir}`, success: false }
} else { } else {

View File

@@ -1,24 +1,9 @@
import type { BundlerType, DbType, EditorType } from '../types.js' import type { DbType } from '../types.js'
type DbAdapterReplacement = { type DbAdapterReplacement = {
configReplacement: string[] configReplacement: string[]
importReplacement: string importReplacement: string
packageName: string packageName: string
version: string
}
type BundlerReplacement = {
configReplacement: string
importReplacement: string
packageName: string
version: string
}
type EditorReplacement = {
configReplacement: string
importReplacement: string
packageName: string
version: string
} }
const mongodbReplacement: DbAdapterReplacement = { const mongodbReplacement: DbAdapterReplacement = {
@@ -26,7 +11,6 @@ const mongodbReplacement: DbAdapterReplacement = {
packageName: '@payloadcms/db-mongodb', packageName: '@payloadcms/db-mongodb',
// Replacement between `// database-adapter-config-start` and `// database-adapter-config-end` // Replacement between `// database-adapter-config-start` and `// database-adapter-config-end`
configReplacement: [' db: mongooseAdapter({', ' url: process.env.DATABASE_URI,', ' }),'], configReplacement: [' db: mongooseAdapter({', ' url: process.env.DATABASE_URI,', ' }),'],
version: '^1.0.0',
} }
const postgresReplacement: DbAdapterReplacement = { const postgresReplacement: DbAdapterReplacement = {
@@ -39,45 +23,9 @@ const postgresReplacement: DbAdapterReplacement = {
], ],
importReplacement: "import { postgresAdapter } from '@payloadcms/db-postgres'", importReplacement: "import { postgresAdapter } from '@payloadcms/db-postgres'",
packageName: '@payloadcms/db-postgres', packageName: '@payloadcms/db-postgres',
version: '^0.x', // up to, not including 1.0.0
} }
export const dbPackages: Record<DbType, DbAdapterReplacement> = { export const dbReplacements: Record<DbType, DbAdapterReplacement> = {
mongodb: mongodbReplacement, mongodb: mongodbReplacement,
postgres: postgresReplacement, 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<BundlerType, BundlerReplacement> = {
vite: viteReplacement,
webpack: webpackReplacement,
}
export const editorPackages: Record<EditorType, EditorReplacement> = {
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',
},
}

View File

@@ -58,13 +58,21 @@ export async function selectDb(args: CliArgs, projectName: string): Promise<DbDe
const dbChoice = dbChoiceRecord[dbType] const dbChoice = dbChoiceRecord[dbType]
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( const dbUriRes = await prompts(
{ {
name: 'value', name: 'value',
type: 'text', type: 'text',
initial: `${dbChoice.dbConnectionPrefix}${ initial: initialDbUri,
projectName === '.' ? `payload-${getRandomDigitSuffix()}` : slugify(projectName)
}`,
message: `Enter ${dbChoice.title.split(' ')[0]} connection string`, // strip beta from title message: `Enter ${dbChoice.title.split(' ')[0]} connection string`, // strip beta from title
validate: (value: string) => !!value.length, validate: (value: string) => !!value.length,
}, },
@@ -74,10 +82,12 @@ export async function selectDb(args: CliArgs, projectName: string): Promise<DbDe
}, },
}, },
) )
dbUri = dbUriRes.value
}
return { return {
type: dbChoice.value, type: dbChoice.value,
dbUri: dbUriRes.value, dbUri,
} }
} }

View File

@@ -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 defaultNextConfig = `/** @type {import('next').NextConfig} */
const nextConfig = {}; const nextConfig = {};

View File

@@ -3,7 +3,7 @@ import fs from 'fs'
import globby from 'globby' import globby from 'globby'
import path from 'path' 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<void> => { export const wrapNextConfig = async (args: { projectDir: string }): Promise<void> => {
const foundConfig = (await globby('next.config.*js', { cwd: args.projectDir }))?.[0] const foundConfig = (await globby('next.config.*js', { cwd: args.projectDir }))?.[0]

View File

@@ -25,7 +25,10 @@ export class Main {
this.args = arg( this.args = arg(
{ {
'--db': String, '--db': String,
'--db-accept-recommended': Boolean,
'--db-connection-string': String,
'--help': Boolean, '--help': Boolean,
'--local-template': String,
'--name': String, '--name': String,
'--secret': String, '--secret': String,
'--template': String, '--template': String,
@@ -63,10 +66,11 @@ export class Main {
} }
const projectName = await parseProjectName(this.args) const projectName = await parseProjectName(this.args)
const projectDir = const projectDir = path.resolve(
projectName === '.' || this.args['--init-next'] projectName === '.' || this.args['--init-next']
? path.basename(process.cwd()) ? path.basename(process.cwd())
: `./${slugify(projectName)}` : `./${slugify(projectName)}`,
)
console.log(welcomeMessage) console.log(welcomeMessage)
const packageManager = await getPackageManager(this.args, projectDir) const packageManager = await getPackageManager(this.args, projectDir)
@@ -95,7 +99,6 @@ export class Main {
const template = await parseTemplate(this.args, validTemplates) const template = await parseTemplate(this.args, validTemplates)
switch (template.type) { switch (template.type) {
case 'local':
case 'starter': { case 'starter': {
const dbDetails = await selectDb(this.args, projectName) const dbDetails = await selectDb(this.args, projectName)
const payloadSecret = generateSecret() const payloadSecret = generateSecret()

View File

@@ -3,10 +3,13 @@ import type arg from 'arg'
export interface Args extends arg.Spec { export interface Args extends arg.Spec {
'--beta': BooleanConstructor '--beta': BooleanConstructor
'--db': StringConstructor '--db': StringConstructor
'--db-accept-recommended': BooleanConstructor
'--db-connection-string': StringConstructor
'--debug': BooleanConstructor '--debug': BooleanConstructor
'--dry-run': BooleanConstructor '--dry-run': BooleanConstructor
'--help': BooleanConstructor '--help': BooleanConstructor
'--init-next': BooleanConstructor '--init-next': BooleanConstructor
'--local-template': StringConstructor
'--name': StringConstructor '--name': StringConstructor
'--no-deps': BooleanConstructor '--no-deps': BooleanConstructor
'--secret': StringConstructor '--secret': StringConstructor
@@ -60,5 +63,4 @@ export type DbDetails = {
type: DbType type: DbType
} }
export type BundlerType = 'vite' | 'webpack'
export type EditorType = 'lexical' | 'slate' export type EditorType = 'lexical' | 'slate'

View File

@@ -7,17 +7,6 @@
"outDir": "./dist" /* Specify an output folder for all emitted files. */, "outDir": "./dist" /* Specify an output folder for all emitted files. */,
"rootDir": "./src" /* Specify the root folder within your source files. */ "rootDir": "./src" /* Specify the root folder within your source files. */
}, },
"exclude": [ "exclude": ["dist", "build", "tests", "test", "node_modules", ".eslintrc.js"],
"dist",
"build",
"tests",
"test",
"node_modules",
".eslintrc.js",
"src/**/*.spec.js",
"src/**/*.spec.jsx",
"src/**/*.spec.ts",
"src/**/*.spec.tsx"
],
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.json"] "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.json"]
} }