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"
},
"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}",

View File

@@ -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

View File

@@ -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)
})
})
})

View File

@@ -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<void> {
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<void> {
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()
}
}

View File

@@ -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<CliArgs, '--debug'> & {
type InitNextResult = { reason?: string; success: boolean; userAppDir?: string }
export async function initNext(args: InitNextArgs): Promise<InitNextResult> {
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<InitNextRe
}
// 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) {
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
const userAppDirGlob = await globby(['**/app'], {
cwd: projectDir,
onlyDirectories: true,
})
const userAppDir = path.resolve(projectDir, userAppDirGlob?.[0])
const userAppDir = (
await globby(['**/app'], {
absolute: true,
cwd: projectDir,
onlyDirectories: true,
})
)?.[0]
if (!fs.existsSync(userAppDir)) {
return { reason: `Could not find user app directory inside ${projectDir}`, success: false }
} else {

View File

@@ -1,24 +1,9 @@
import type { BundlerType, DbType, EditorType } from '../types.js'
import type { DbType } from '../types.js'
type DbAdapterReplacement = {
configReplacement: string[]
importReplacement: 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 = {
@@ -26,7 +11,6 @@ const mongodbReplacement: DbAdapterReplacement = {
packageName: '@payloadcms/db-mongodb',
// Replacement between `// database-adapter-config-start` and `// database-adapter-config-end`
configReplacement: [' db: mongooseAdapter({', ' url: process.env.DATABASE_URI,', ' }),'],
version: '^1.0.0',
}
const postgresReplacement: DbAdapterReplacement = {
@@ -39,45 +23,9 @@ const postgresReplacement: DbAdapterReplacement = {
],
importReplacement: "import { postgresAdapter } from '@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,
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,26 +58,36 @@ export async function selectDb(args: CliArgs, projectName: string): Promise<DbDe
const dbChoice = dbChoiceRecord[dbType]
const dbUriRes = await prompts(
{
name: 'value',
type: 'text',
initial: `${dbChoice.dbConnectionPrefix}${
projectName === '.' ? `payload-${getRandomDigitSuffix()}` : slugify(projectName)
}`,
message: `Enter ${dbChoice.title.split(' ')[0]} connection string`, // strip beta from title
validate: (value: string) => !!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,
}
}

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 nextConfig = {};

View File

@@ -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<void> => {
const foundConfig = (await globby('next.config.*js', { cwd: args.projectDir }))?.[0]

View File

@@ -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()

View File

@@ -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'

View File

@@ -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"]
}