chore(create-payload-app): init-next now create payload config and modifies tsconfig.json (#5242)

This commit is contained in:
Elliot DeNolf
2024-03-04 16:55:43 -05:00
committed by GitHub
parent 56ecd2ac14
commit 40a0a0083f
6 changed files with 285 additions and 54 deletions

View File

@@ -89,6 +89,7 @@
"@types/testing-library__jest-dom": "5.14.8", "@types/testing-library__jest-dom": "5.14.8",
"add-stream": "^1.0.0", "add-stream": "^1.0.0",
"chalk": "^4.1.2", "chalk": "^4.1.2",
"comment-json": "^4.2.3",
"concat-stream": "^2.0.0", "concat-stream": "^2.0.0",
"conventional-changelog": "^5.1.0", "conventional-changelog": "^5.1.0",
"conventional-changelog-conventionalcommits": "^7.0.2", "conventional-changelog-conventionalcommits": "^7.0.2",

View File

@@ -7,7 +7,7 @@
}, },
"scripts": { "scripts": {
"build": "pnpm copyfiles && pnpm build:swc", "build": "pnpm copyfiles && pnpm build:swc",
"copyfiles": "copyfiles -u 4 \"../next/src/app/(payload)/**\" \"dist/app\"", "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}",
"test": "jest", "test": "jest",
@@ -23,7 +23,9 @@
"arg": "^5.0.0", "arg": "^5.0.0",
"chalk": "^4.1.0", "chalk": "^4.1.0",
"command-exists": "^1.2.9", "command-exists": "^1.2.9",
"comment-json": "^4.2.3",
"degit": "^2.8.4", "degit": "^2.8.4",
"detect-package-manager": "^3.0.1",
"execa": "^5.0.0", "execa": "^5.0.0",
"figures": "^3.2.0", "figures": "^3.2.0",
"fs-extra": "^9.0.1", "fs-extra": "^9.0.1",

View File

@@ -1,16 +1,88 @@
import type { CompilerOptions } from 'typescript'
import chalk from 'chalk'
import * as CommentJson from 'comment-json'
import { detect } from 'detect-package-manager'
import execa from 'execa'
import fs from 'fs' import fs from 'fs'
import fse from 'fs-extra'
import globby from 'globby' import globby from 'globby'
import path from 'path' import path from 'path'
import type { CliArgs } from '../types' import type { CliArgs } from '../types'
import { copyRecursiveSync } from '../utils/copy-recursive-sync' import { copyRecursiveSync } from '../utils/copy-recursive-sync'
import { error, info, debug as origDebug, success } from '../utils/log' import { error, info, debug as origDebug, success, warning } from '../utils/log'
export async function initNext( type InitNextArgs = Pick<CliArgs, '--debug'> & {
args: Pick<CliArgs, '--debug'> & { nextDir?: string; useDistFiles?: boolean }, projectDir?: string
): Promise<{ success: boolean }> { useDistFiles?: boolean
const { '--debug': debug, nextDir, useDistFiles } = args }
type InitNextResult = { reason?: string; success: boolean; userAppDir?: string }
export async function initNext(args: InitNextArgs): Promise<InitNextResult> {
args.projectDir = args.projectDir || process.cwd()
const { projectDir } = args
const templateResult = await applyPayloadTemplateFiles(args)
if (!templateResult.success) return templateResult
const { success: installSuccess } = await installDeps(projectDir)
if (!installSuccess) {
return { ...templateResult, reason: 'Failed to install dependencies', success: false }
}
// Create or find payload.config.ts
const createConfigResult = findOrCreatePayloadConfig(projectDir)
if (!createConfigResult.success) {
return { ...templateResult, ...createConfigResult }
}
// Add `@payload-config` to tsconfig.json `paths`
await addPayloadConfigToTsConfig(projectDir)
// Output directions for user to update next.config.js
const withPayloadMessage = `
${chalk.bold(`Wrap your existing next.config.js with the withPayload function. Here is an example:`)}
const { withPayload } = require("@payloadcms/next");
const nextConfig = {
// Your Next.js config
};
module.exports = withPayload(nextConfig);
`
console.log(withPayloadMessage)
return templateResult
}
async function addPayloadConfigToTsConfig(projectDir: string) {
const tsConfigPath = path.resolve(projectDir, 'tsconfig.json')
const userTsConfigContent = await fse.readFile(tsConfigPath, {
encoding: 'utf8',
})
const userTsConfig = CommentJson.parse(userTsConfigContent) as {
compilerOptions?: CompilerOptions
}
if (!userTsConfig.compilerOptions && !('extends' in userTsConfig)) {
userTsConfig.compilerOptions = {}
}
if (!userTsConfig.compilerOptions.paths?.['@payload-config']) {
userTsConfig.compilerOptions.paths = {
...(userTsConfig.compilerOptions.paths || {}),
'@payload-config': ['./payload.config.ts'],
}
await fse.writeFile(tsConfigPath, CommentJson.stringify(userTsConfig, null, 2))
}
}
async function applyPayloadTemplateFiles(args: InitNextArgs): Promise<InitNextResult> {
const { '--debug': debug, projectDir, useDistFiles } = args
info('Initializing Payload app in Next.js project', 1) info('Initializing Payload app in Next.js project', 1)
@@ -18,24 +90,18 @@ export async function initNext(
if (debug) origDebug(message) if (debug) origDebug(message)
} }
let projectDir = process.cwd()
if (nextDir) {
projectDir = path.resolve(projectDir, nextDir)
if (debug) logDebug(`Overriding project directory to ${projectDir}`)
}
if (!fs.existsSync(projectDir)) { if (!fs.existsSync(projectDir)) {
error(`Could not find specified project directory at ${projectDir}`) return { reason: `Could not find specified project directory at ${projectDir}`, success: false }
return { success: false }
} }
// 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', { cwd: projectDir }))?.[0]
const nextConfigPath = path.resolve(projectDir, foundConfig) const nextConfigPath = path.resolve(projectDir, foundConfig)
if (!fs.existsSync(nextConfigPath)) { if (!fs.existsSync(nextConfigPath)) {
error( return {
`No next.config.js found at ${nextConfigPath}. Ensure you are in a Next.js project directory.`, reason: `No next.config.js found at ${nextConfigPath}. Ensure you are in a Next.js project directory.`,
) success: false,
return { success: false } }
} else { } else {
if (debug) logDebug(`Found Next config at ${nextConfigPath}`) if (debug) logDebug(`Found Next config at ${nextConfigPath}`)
} }
@@ -43,21 +109,27 @@ export async function initNext(
const templateFilesPath = const templateFilesPath =
__dirname.endsWith('dist') || useDistFiles __dirname.endsWith('dist') || useDistFiles
? path.resolve(__dirname, '../..', 'dist/app') ? path.resolve(__dirname, '../..', 'dist/app')
: path.resolve(__dirname, '../../../next/src/app') : path.resolve(__dirname, '../../../../app')
if (debug) logDebug(`Using template files from: ${templateFilesPath}`) if (debug) logDebug(`Using template files from: ${templateFilesPath}`)
if (!fs.existsSync(templateFilesPath)) { if (!fs.existsSync(templateFilesPath)) {
error(`Could not find template source files from ${templateFilesPath}`) return {
return { success: false } reason: `Could not find template source files from ${templateFilesPath}`,
success: false,
}
} else { } else {
if (debug) logDebug('Found template source files') if (debug) logDebug('Found template source files')
} }
const userAppDir = path.resolve(projectDir, 'src/app') // src/app or app
const userAppDirGlob = await globby(['**/app'], {
cwd: projectDir,
onlyDirectories: true,
})
const userAppDir = path.resolve(projectDir, userAppDirGlob?.[0])
if (!fs.existsSync(userAppDir)) { if (!fs.existsSync(userAppDir)) {
error(`Could not find user app directory at ${userAppDir}`) return { reason: `Could not find user app directory inside ${projectDir}`, success: false }
return { success: false }
} else { } else {
logDebug(`Found user app directory: ${userAppDir}`) logDebug(`Found user app directory: ${userAppDir}`)
} }
@@ -65,5 +137,83 @@ export async function initNext(
logDebug(`Copying template files from ${templateFilesPath} to ${userAppDir}`) logDebug(`Copying template files from ${templateFilesPath} to ${userAppDir}`)
copyRecursiveSync(templateFilesPath, userAppDir, debug) copyRecursiveSync(templateFilesPath, userAppDir, debug)
success('Successfully initialized.') success('Successfully initialized.')
return { success: true } return { success: true, userAppDir }
}
async function installDeps(projectDir: string) {
const packageManager = await detect({ cwd: projectDir })
if (!packageManager) {
throw new Error('Could not detect package manager')
}
info(`Installing dependencies with ${packageManager}`, 1)
const packagesToInstall = [
'payload',
'@payloadcms/db-mongodb',
'@payloadcms/next',
'@payloadcms/richtext-slate',
'@payloadcms/ui',
].map((pkg) => `${pkg}@alpha`)
let exitCode = 0
switch (packageManager) {
case 'npm': {
;({ exitCode } = await execa('npm', ['install', '--save', ...packagesToInstall], {
cwd: projectDir,
}))
break
}
case 'yarn':
case 'pnpm': {
;({ exitCode } = await execa(packageManager, ['add', ...packagesToInstall], {
cwd: projectDir,
}))
break
}
case 'bun': {
warning('Bun support is untested.')
;({ exitCode } = await execa('bun', ['add', ...packagesToInstall], { cwd: projectDir }))
break
}
}
if (exitCode !== 0) {
error(`Failed to install dependencies with ${packageManager}`)
} else {
success(`Successfully installed dependencies`)
}
return { success: exitCode === 0 }
}
function findOrCreatePayloadConfig(projectDir: string) {
const configPath = path.resolve(projectDir, 'payload.config.ts')
if (fs.existsSync(configPath)) {
return { message: 'Found existing payload.config.ts', success: true }
} else {
// Create default config
// TODO: Pull this from templates
const defaultConfig = `import path from "path";
import { mongooseAdapter } from "@payloadcms/db-mongodb"; // database-adapter-import
import { slateEditor } from "@payloadcms/richtext-slate"; // editor-import
import { buildConfig } from "payload/config";
export default buildConfig({
editor: slateEditor({}), // editor-config
collections: [],
secret: "asdfasdf",
typescript: {
outputFile: path.resolve(__dirname, "payload-types.ts"),
},
graphQL: {
schemaOutputFile: path.resolve(__dirname, "generated-schema.graphql"),
},
db: mongooseAdapter({
url: "mongodb://localhost:27017/next-payload-3",
}),
});
`
fse.writeFileSync(configPath, defaultConfig)
return { message: 'Created default payload.config.ts', success: true }
}
} }

View File

@@ -12,7 +12,7 @@ import { parseTemplate } from './lib/parse-template'
import { selectDb } from './lib/select-db' import { selectDb } from './lib/select-db'
import { getValidTemplates, validateTemplate } from './lib/templates' import { getValidTemplates, validateTemplate } from './lib/templates'
import { writeEnvFile } from './lib/write-env-file' import { writeEnvFile } from './lib/write-env-file'
import { success } from './utils/log' import { error, success } from './utils/log'
import { helpMessage, successMessage, welcomeMessage } from './utils/messages' import { helpMessage, successMessage, welcomeMessage } from './utils/messages'
export class Main { export class Main {
@@ -61,6 +61,11 @@ export class Main {
if (this.args['--init-next']) { if (this.args['--init-next']) {
const result = await initNext(this.args) const result = await initNext(this.args)
if (!result.success) {
error(result.reason || 'Failed to initialize Payload app in Next.js project')
} else {
success('Payload app successfully initialized in Next.js project')
}
process.exit(result.success ? 0 : 1) process.exit(result.success ? 0 : 1)
} }

56
pnpm-lock.yaml generated
View File

@@ -110,6 +110,9 @@ importers:
chalk: chalk:
specifier: ^4.1.2 specifier: ^4.1.2
version: 4.1.2 version: 4.1.2
comment-json:
specifier: ^4.2.3
version: 4.2.3
concat-stream: concat-stream:
specifier: ^2.0.0 specifier: ^2.0.0
version: 2.0.0 version: 2.0.0
@@ -278,9 +281,15 @@ importers:
command-exists: command-exists:
specifier: ^1.2.9 specifier: ^1.2.9
version: 1.2.9 version: 1.2.9
comment-json:
specifier: ^4.2.3
version: 4.2.3
degit: degit:
specifier: ^2.8.4 specifier: ^2.8.4
version: 2.8.4 version: 2.8.4
detect-package-manager:
specifier: ^3.0.1
version: 3.0.1
execa: execa:
specifier: ^5.0.0 specifier: ^5.0.0
version: 5.1.1 version: 5.1.1
@@ -4305,7 +4314,7 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies: dependencies:
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 20.6.2 '@types/node': 16.18.85
chalk: 4.1.2 chalk: 4.1.2
jest-message-util: 29.7.0 jest-message-util: 29.7.0
jest-util: 29.7.0 jest-util: 29.7.0
@@ -4366,7 +4375,7 @@ packages:
dependencies: dependencies:
'@jest/fake-timers': 29.7.0 '@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 20.6.2 '@types/node': 16.18.85
jest-mock: 29.7.0 jest-mock: 29.7.0
/@jest/expect-utils@29.7.0: /@jest/expect-utils@29.7.0:
@@ -4390,7 +4399,7 @@ packages:
dependencies: dependencies:
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@sinonjs/fake-timers': 10.3.0 '@sinonjs/fake-timers': 10.3.0
'@types/node': 20.6.2 '@types/node': 16.18.85
jest-message-util: 29.7.0 jest-message-util: 29.7.0
jest-mock: 29.7.0 jest-mock: 29.7.0
jest-util: 29.7.0 jest-util: 29.7.0
@@ -4421,7 +4430,7 @@ packages:
'@jest/transform': 29.7.0 '@jest/transform': 29.7.0
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@jridgewell/trace-mapping': 0.3.23 '@jridgewell/trace-mapping': 0.3.23
'@types/node': 20.6.2 '@types/node': 16.18.85
chalk: 4.1.2 chalk: 4.1.2
collect-v8-coverage: 1.0.2 collect-v8-coverage: 1.0.2
exit: 0.1.2 exit: 0.1.2
@@ -7491,6 +7500,9 @@ packages:
is-string: 1.0.7 is-string: 1.0.7
dev: false dev: false
/array-timsort@1.0.3:
resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==}
/array-union@2.1.0: /array-union@2.1.0:
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -8290,6 +8302,16 @@ packages:
engines: {node: ^12.20.0 || >=14} engines: {node: ^12.20.0 || >=14}
dev: false dev: false
/comment-json@4.2.3:
resolution: {integrity: sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==}
engines: {node: '>= 6'}
dependencies:
array-timsort: 1.0.3
core-util-is: 1.0.3
esprima: 4.0.1
has-own-prop: 2.0.0
repeat-string: 1.6.1
/comment-parser@1.4.1: /comment-parser@1.4.1:
resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
@@ -8559,7 +8581,6 @@ packages:
/core-util-is@1.0.3: /core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
dev: true
/cosmiconfig@7.1.0: /cosmiconfig@7.1.0:
resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==}
@@ -9177,6 +9198,13 @@ packages:
resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
engines: {node: '>=8'} engines: {node: '>=8'}
/detect-package-manager@3.0.1:
resolution: {integrity: sha512-qoHDH6+lMcpJPAScE7+5CYj91W0mxZNXTwZPrCqi1KMk+x+AoQScQ2V1QyqTln1rHU5Haq5fikvOGHv+leKD8A==}
engines: {node: '>=12'}
dependencies:
execa: 5.1.1
dev: false
/diff-sequences@27.5.1: /diff-sequences@27.5.1:
resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
@@ -11166,6 +11194,10 @@ packages:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
/has-own-prop@2.0.0:
resolution: {integrity: sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==}
engines: {node: '>=8'}
/has-property-descriptors@1.0.2: /has-property-descriptors@1.0.2:
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
dependencies: dependencies:
@@ -12313,7 +12345,7 @@ packages:
dependencies: dependencies:
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/graceful-fs': 4.1.9 '@types/graceful-fs': 4.1.9
'@types/node': 20.6.2 '@types/node': 16.18.85
anymatch: 3.1.3 anymatch: 3.1.3
fb-watchman: 2.0.2 fb-watchman: 2.0.2
graceful-fs: 4.2.11 graceful-fs: 4.2.11
@@ -12370,7 +12402,7 @@ packages:
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
dependencies: dependencies:
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 20.6.2 '@types/node': 16.18.85
jest-util: 29.7.0 jest-util: 29.7.0
/jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): /jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
@@ -12420,7 +12452,7 @@ packages:
'@jest/test-result': 29.7.0 '@jest/test-result': 29.7.0
'@jest/transform': 29.7.0 '@jest/transform': 29.7.0
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 20.6.2 '@types/node': 16.18.85
chalk: 4.1.2 chalk: 4.1.2
emittery: 0.13.1 emittery: 0.13.1
graceful-fs: 4.2.11 graceful-fs: 4.2.11
@@ -12450,7 +12482,7 @@ packages:
'@jest/test-result': 29.7.0 '@jest/test-result': 29.7.0
'@jest/transform': 29.7.0 '@jest/transform': 29.7.0
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 20.6.2 '@types/node': 16.18.85
chalk: 4.1.2 chalk: 4.1.2
cjs-module-lexer: 1.2.3 cjs-module-lexer: 1.2.3
collect-v8-coverage: 1.0.2 collect-v8-coverage: 1.0.2
@@ -12523,7 +12555,7 @@ packages:
dependencies: dependencies:
'@jest/test-result': 29.7.0 '@jest/test-result': 29.7.0
'@jest/types': 29.6.3 '@jest/types': 29.6.3
'@types/node': 20.6.2 '@types/node': 16.18.85
ansi-escapes: 4.3.2 ansi-escapes: 4.3.2
chalk: 4.1.2 chalk: 4.1.2
emittery: 0.13.1 emittery: 0.13.1
@@ -15847,6 +15879,10 @@ packages:
- typescript - typescript
dev: true dev: true
/repeat-string@1.6.1:
resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==}
engines: {node: '>=0.10'}
/require-directory@2.1.1: /require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}

View File

@@ -1,48 +1,85 @@
import type { CompilerOptions } from 'typescript'
import * as CommentJson from 'comment-json'
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
import shelljs from 'shelljs' import shelljs from 'shelljs'
import { promisify } from 'util'
import { initNext } from '../../packages/create-payload-app/src/lib/init-next' import { initNext } from '../../packages/create-payload-app/src/lib/init-next'
const readFile = promisify(fs.readFile)
const nextCreateCommands: Partial<Record<'noSrcDir' | 'srcDir', string>> = {
srcDir:
'pnpm create next-app@latest . --typescript --eslint --no-tailwind --app --import-alias="@/*" --src-dir',
noSrcDir:
'pnpm create next-app@latest . --typescript --eslint --no-tailwind --app --import-alias="@/*" --no-src-dir',
}
describe('create-payload-app', () => { describe('create-payload-app', () => {
describe('--init-next', () => { beforeAll(() => {
const nextDir = path.resolve(__dirname, 'test-app') // Runs copyfiles copy app/(payload) -> dist/app/(payload)
shelljs.exec('pnpm build:create-payload-app')
})
beforeAll(() => { describe('Next.js app template files', () => {
if (fs.existsSync(nextDir)) { it('should exist in dist', () => {
fs.rmdirSync(nextDir, { recursive: true }) const distPath = path.resolve(
__dirname,
'../../packages/create-payload-app/dist/app/(payload)',
)
expect(fs.existsSync(distPath)).toBe(true)
})
})
describe.each(Object.keys(nextCreateCommands))(`--init-next with %s`, (nextCmdKey) => {
const projectDir = path.resolve(__dirname, 'test-app')
beforeEach(() => {
if (fs.existsSync(projectDir)) {
fs.rmdirSync(projectDir, { recursive: true })
} }
// Create dir for Next.js project // Create dir for Next.js project
if (!fs.existsSync(nextDir)) { if (!fs.existsSync(projectDir)) {
fs.mkdirSync(nextDir) fs.mkdirSync(projectDir)
} }
// Create a new Next.js project with default options // Create a new Next.js project with default options
shelljs.exec( shelljs.exec(nextCreateCommands[nextCmdKey], { cwd: projectDir })
'pnpm create next-app@latest . --typescript --eslint --no-tailwind --app --import-alias="@/*" --src-dir',
{ cwd: nextDir },
)
}) })
afterAll(() => { afterEach(() => {
if (fs.existsSync(nextDir)) { if (fs.existsSync(projectDir)) {
fs.rmdirSync(nextDir, { recursive: true }) fs.rmdirSync(projectDir, { recursive: true })
} }
}) })
it('should install payload app in Next.js project', async () => { it('should install payload app in Next.js project', async () => {
expect(fs.existsSync(nextDir)).toBe(true) expect(fs.existsSync(projectDir)).toBe(true)
const result = await initNext({ const result = await initNext({
'--debug': true, '--debug': true,
nextDir, projectDir,
useDistFiles: true, // create-payload-app must be built useDistFiles: true, // create-payload-app/dist/app/(payload)
}) })
expect(result.success).toBe(true) expect(result.success).toBe(true)
const payloadFilesPath = path.resolve(nextDir, 'src/app/(payload)')
const payloadFilesPath = path.resolve(result.userAppDir, '(payload)')
expect(fs.existsSync(payloadFilesPath)).toBe(true) expect(fs.existsSync(payloadFilesPath)).toBe(true)
const payloadConfig = path.resolve(projectDir, 'payload.config.ts')
expect(fs.existsSync(payloadConfig)).toBe(true)
const tsConfigPath = path.resolve(projectDir, 'tsconfig.json')
const userTsConfigContent = await readFile(tsConfigPath, { encoding: 'utf8' })
const userTsConfig = CommentJson.parse(userTsConfigContent) as {
compilerOptions?: CompilerOptions
}
expect(userTsConfig.compilerOptions.paths?.['@payload-config']).toEqual([
'./payload.config.ts',
])
}) })
}) })
}) })