chore(create-payload-app): migrate to esm, adjust init-next tests

This commit is contained in:
Elliot DeNolf
2024-03-14 11:12:01 -04:00
parent e3e0f056a9
commit 53a09f4989
19 changed files with 179 additions and 90 deletions

View File

@@ -9,3 +9,4 @@
**/node_modules **/node_modules
**/temp **/temp
playwright.config.ts playwright.config.ts
jest.config.js

View File

@@ -1,10 +1,4 @@
// const nextJest = require('next/jest.js') /** @type {import('@jest/types').Config} */
// const createJestConfig = nextJest({
// // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
// dir: './',
// })
const customJestConfig = { const customJestConfig = {
extensionsToTreatAsEsm: ['.ts', '.tsx'], extensionsToTreatAsEsm: ['.ts', '.tsx'],
globalSetup: './test/jest.setup.ts', globalSetup: './test/jest.setup.ts',
@@ -23,5 +17,4 @@ const customJestConfig = {
verbose: true, verbose: true,
} }
// module.exports = createJestConfig(customJestConfig)
export default customJestConfig export default customJestConfig

View File

@@ -142,6 +142,7 @@
"slate": "0.91.4", "slate": "0.91.4",
"swc-plugin-transform-remove-imports": "^1.12.1", "swc-plugin-transform-remove-imports": "^1.12.1",
"tempfile": "^3.0.0", "tempfile": "^3.0.0",
"tempy": "^1.0.1",
"ts-node": "10.9.1", "ts-node": "10.9.1",
"tsx": "^4.7.1", "tsx": "^4.7.1",
"turbo": "^1.12.5", "turbo": "^1.12.5",

View File

@@ -1,9 +1,11 @@
module.exports = { import baseConfig from '../../jest.config.js'
testEnvironment: 'node',
/** @type {import('@jest/types').Config} */
const customJestConfig = {
...baseConfig,
globalSetup: null,
testMatch: ['**/src/**/?(*.)+(spec|test|it-test).[tj]s?(x)'], testMatch: ['**/src/**/?(*.)+(spec|test|it-test).[tj]s?(x)'],
testTimeout: 10000, testTimeout: 20000,
transform: {
'^.+\\.(ts|tsx)?$': 'ts-jest',
},
verbose: true,
} }
export default customJestConfig

View File

@@ -2,6 +2,7 @@
"name": "create-payload-app", "name": "create-payload-app",
"version": "1.0.0", "version": "1.0.0",
"license": "MIT", "license": "MIT",
"type": "module",
"bin": { "bin": {
"create-payload-app": "bin/cli.js" "create-payload-app": "bin/cli.js"
}, },
@@ -41,7 +42,6 @@
"@types/fs-extra": "^9.0.12", "@types/fs-extra": "^9.0.12",
"@types/jest": "^27.0.3", "@types/jest": "^27.0.3",
"@types/node": "^16.6.2", "@types/node": "^16.6.2",
"@types/prompts": "^2.4.1", "@types/prompts": "^2.4.1"
"ts-jest": "^29.1.0"
} }
} }

View File

@@ -1,5 +1,5 @@
import { Main } from './main' import { Main } from './main.js'
import { error } from './utils/log' import { error } from './utils/log.js'
async function main(): Promise<void> { async function main(): Promise<void> {
await new Main().init() await new Main().init()

View File

@@ -1,10 +1,10 @@
import fse from 'fs-extra' import fse from 'fs-extra'
import path from 'path' import path from 'path'
import type { DbDetails } from '../types' import type { DbDetails } from '../types.js'
import { warning } from '../utils/log' import { warning } from '../utils/log.js'
import { bundlerPackages, dbPackages, editorPackages } from './packages' import { bundlerPackages, dbPackages, editorPackages } 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: {

View File

@@ -1,10 +1,10 @@
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' import type { BundlerType, CliArgs, DbType, ProjectTemplate } from '../types.js'
import { createProject } from './create-project' import { createProject } from './create-project.js'
import { bundlerPackages, dbPackages, editorPackages } from './packages' import { bundlerPackages, dbPackages, editorPackages } from './packages.js'
import exp from 'constants' import exp from 'constants'
import { getValidTemplates } from './templates' import { getValidTemplates } from './templates.js'
const projectDir = path.resolve(__dirname, './tmp') const projectDir = path.resolve(__dirname, './tmp')
describe('createProject', () => { describe('createProject', () => {

View File

@@ -5,10 +5,10 @@ import fse from 'fs-extra'
import ora from 'ora' import ora from 'ora'
import path from 'path' import path from 'path'
import type { CliArgs, DbDetails, PackageManager, ProjectTemplate } from '../types' import type { CliArgs, DbDetails, PackageManager, ProjectTemplate } from '../types.js'
import { error, success, warning } from '../utils/log' import { error, success, warning } from '../utils/log.js'
import { configurePayloadConfig } from './configure-payload-config' import { configurePayloadConfig } from './configure-payload-config.js'
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)

View File

@@ -1,18 +1,26 @@
import type { CompilerOptions } from 'typescript' import type { CompilerOptions } from 'typescript'
import chalk from 'chalk' import chalk from 'chalk'
import * as CommentJson from 'comment-json' import { parse, stringify } from 'comment-json'
import { detect } from 'detect-package-manager' 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'
import globby from 'globby' import globby from 'globby'
import path from 'path' import path from 'path'
import { promisify } from 'util'
const readFile = promisify(fs.readFile)
const writeFile = promisify(fs.writeFile)
import type { CliArgs } from '../types' const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
import { copyRecursiveSync } from '../utils/copy-recursive-sync' import { fileURLToPath } from 'node:url'
import { error, info, debug as origDebug, success, warning } from '../utils/log'
import type { CliArgs } from '../types.js'
import { copyRecursiveSync } from '../utils/copy-recursive-sync.js'
import { error, info, debug as origDebug, success, warning } from '../utils/log.js'
type InitNextArgs = Pick<CliArgs, '--debug'> & { type InitNextArgs = Pick<CliArgs, '--debug'> & {
projectDir?: string projectDir?: string
@@ -45,13 +53,13 @@ export async function initNext(args: InitNextArgs): Promise<InitNextResult> {
${chalk.bold(`Wrap your existing next.config.js with the withPayload function. Here is an example:`)} ${chalk.bold(`Wrap your existing next.config.js with the withPayload function. Here is an example:`)}
const { withPayload } = require("@payloadcms/next"); import withPayload from '@payloadcms/next/withPayload'
const nextConfig = { const nextConfig = {
// Your Next.js config // Your Next.js config here
}; }
module.exports = withPayload(nextConfig); export default withPayload(nextConfig)
` `
@@ -62,10 +70,10 @@ export async function initNext(args: InitNextArgs): Promise<InitNextResult> {
async function addPayloadConfigToTsConfig(projectDir: string) { async function addPayloadConfigToTsConfig(projectDir: string) {
const tsConfigPath = path.resolve(projectDir, 'tsconfig.json') const tsConfigPath = path.resolve(projectDir, 'tsconfig.json')
const userTsConfigContent = await fse.readFile(tsConfigPath, { const userTsConfigContent = await readFile(tsConfigPath, {
encoding: 'utf8', encoding: 'utf8',
}) })
const userTsConfig = CommentJson.parse(userTsConfigContent) as { const userTsConfig = parse(userTsConfigContent) as {
compilerOptions?: CompilerOptions compilerOptions?: CompilerOptions
} }
if (!userTsConfig.compilerOptions && !('extends' in userTsConfig)) { if (!userTsConfig.compilerOptions && !('extends' in userTsConfig)) {
@@ -77,7 +85,7 @@ async function addPayloadConfigToTsConfig(projectDir: string) {
...(userTsConfig.compilerOptions.paths || {}), ...(userTsConfig.compilerOptions.paths || {}),
'@payload-config': ['./payload.config.ts'], '@payload-config': ['./payload.config.ts'],
} }
await fse.writeFile(tsConfigPath, CommentJson.stringify(userTsConfig, null, 2)) await writeFile(tsConfigPath, stringify(userTsConfig, null, 2), { encoding: 'utf8' })
} }
} }
@@ -96,6 +104,11 @@ 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', { cwd: projectDir }))?.[0]
if (!foundConfig) {
throw new Error(`No next.config.js found at ${projectDir}`)
}
const nextConfigPath = path.resolve(projectDir, foundConfig) const nextConfigPath = path.resolve(projectDir, foundConfig)
if (!fs.existsSync(nextConfigPath)) { if (!fs.existsSync(nextConfigPath)) {
return { return {
@@ -107,9 +120,9 @@ async function applyPayloadTemplateFiles(args: InitNextArgs): Promise<InitNextRe
} }
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, '../../../../app') : path.resolve(dirname, '../../../../app')
if (debug) logDebug(`Using template files from: ${templateFilesPath}`) if (debug) logDebug(`Using template files from: ${templateFilesPath}`)
@@ -152,7 +165,6 @@ async function installDeps(projectDir: string) {
'@payloadcms/db-mongodb', '@payloadcms/db-mongodb',
'@payloadcms/next', '@payloadcms/next',
'@payloadcms/richtext-slate', '@payloadcms/richtext-slate',
'@payloadcms/ui',
].map((pkg) => `${pkg}@alpha`) ].map((pkg) => `${pkg}@alpha`)
let exitCode = 0 let exitCode = 0

View File

@@ -1,4 +1,4 @@
import type { BundlerType, DbType, EditorType } from '../types' import type { BundlerType, DbType, EditorType } from '../types.js'
type DbAdapterReplacement = { type DbAdapterReplacement = {
configReplacement: string[] configReplacement: string[]

View File

@@ -1,6 +1,6 @@
import prompts from 'prompts' import prompts from 'prompts'
import type { CliArgs } from '../types' import type { CliArgs } from '../types.js'
export async function parseProjectName(args: CliArgs): Promise<string> { export async function parseProjectName(args: CliArgs): Promise<string> {
if (args['--name']) return args['--name'] if (args['--name']) return args['--name']
@@ -9,8 +9,8 @@ export async function parseProjectName(args: CliArgs): Promise<string> {
const response = await prompts( const response = await prompts(
{ {
name: 'value', name: 'value',
message: 'Project name?',
type: 'text', type: 'text',
message: 'Project name?',
validate: (value: string) => !!value.length, validate: (value: string) => !!value.length,
}, },
{ {

View File

@@ -1,6 +1,6 @@
import prompts from 'prompts' import prompts from 'prompts'
import type { CliArgs, ProjectTemplate } from '../types' import type { CliArgs, ProjectTemplate } from '../types.js'
export async function parseTemplate( export async function parseTemplate(
args: CliArgs, args: CliArgs,
@@ -16,6 +16,7 @@ export async function parseTemplate(
const response = await prompts( const response = await prompts(
{ {
name: 'value', name: 'value',
type: 'select',
choices: validTemplates.map((p) => { choices: validTemplates.map((p) => {
return { return {
description: p.description, description: p.description,
@@ -24,7 +25,6 @@ export async function parseTemplate(
} }
}), }),
message: 'Choose project template', message: 'Choose project template',
type: 'select',
validate: (value: string) => !!value.length, validate: (value: string) => !!value.length,
}, },
{ {

View File

@@ -1,7 +1,7 @@
import slugify from '@sindresorhus/slugify' import slugify from '@sindresorhus/slugify'
import prompts from 'prompts' import prompts from 'prompts'
import type { CliArgs, DbDetails, DbType } from '../types' import type { CliArgs, DbDetails, DbType } from '../types.js'
type DbChoice = { type DbChoice = {
dbConnectionPrefix: `${string}/` dbConnectionPrefix: `${string}/`
@@ -37,6 +37,7 @@ export async function selectDb(args: CliArgs, projectName: string): Promise<DbDe
const dbTypeRes = await prompts( const dbTypeRes = await prompts(
{ {
name: 'value', name: 'value',
type: 'select',
choices: Object.values(dbChoiceRecord).map((dbChoice) => { choices: Object.values(dbChoiceRecord).map((dbChoice) => {
return { return {
title: dbChoice.title, title: dbChoice.title,
@@ -44,7 +45,6 @@ export async function selectDb(args: CliArgs, projectName: string): Promise<DbDe
} }
}), }),
message: 'Select a database', message: 'Select a database',
type: 'select',
validate: (value: string) => !!value.length, validate: (value: string) => !!value.length,
}, },
{ {
@@ -61,11 +61,11 @@ export async function selectDb(args: CliArgs, projectName: string): Promise<DbDe
const dbUriRes = await prompts( const dbUriRes = await prompts(
{ {
name: 'value', name: 'value',
type: 'text',
initial: `${dbChoice.dbConnectionPrefix}${ initial: `${dbChoice.dbConnectionPrefix}${
projectName === '.' ? `payload-${getRandomDigitSuffix()}` : slugify(projectName) 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
type: 'text',
validate: (value: string) => !!value.length, validate: (value: string) => !!value.length,
}, },
{ {
@@ -76,8 +76,8 @@ export async function selectDb(args: CliArgs, projectName: string): Promise<DbDe
) )
return { return {
dbUri: dbUriRes.value,
type: dbChoice.value, type: dbChoice.value,
dbUri: dbUriRes.value,
} }
} }

View File

@@ -1,6 +1,6 @@
import type { ProjectTemplate } from '../types' import type { ProjectTemplate } from '../types.js'
import { error, info } from '../utils/log' import { error, info } from '../utils/log.js'
export function validateTemplate(templateName: string): boolean { export function validateTemplate(templateName: string): boolean {
const validTemplates = getValidTemplates() const validTemplates = getValidTemplates()
@@ -16,38 +16,38 @@ export function getValidTemplates(): ProjectTemplate[] {
return [ return [
{ {
name: 'blank', name: 'blank',
description: 'Blank Template',
type: 'starter', type: 'starter',
description: 'Blank Template',
url: 'https://github.com/payloadcms/payload/templates/blank', url: 'https://github.com/payloadcms/payload/templates/blank',
}, },
{ {
name: 'website', name: 'website',
description: 'Website Template',
type: 'starter', type: 'starter',
description: 'Website Template',
url: 'https://github.com/payloadcms/payload/templates/website', url: 'https://github.com/payloadcms/payload/templates/website',
}, },
{ {
name: 'ecommerce', name: 'ecommerce',
description: 'E-commerce Template',
type: 'starter', type: 'starter',
description: 'E-commerce Template',
url: 'https://github.com/payloadcms/payload/templates/ecommerce', url: 'https://github.com/payloadcms/payload/templates/ecommerce',
}, },
{ {
name: 'plugin', name: 'plugin',
description: 'Template for creating a Payload plugin',
type: 'plugin', type: 'plugin',
description: 'Template for creating a Payload plugin',
url: 'https://github.com/payloadcms/payload-plugin-template', url: 'https://github.com/payloadcms/payload-plugin-template',
}, },
{ {
name: 'payload-demo', name: 'payload-demo',
description: 'Payload demo site at https://demo.payloadcms.com',
type: 'starter', type: 'starter',
description: 'Payload demo site at https://demo.payloadcms.com',
url: 'https://github.com/payloadcms/public-demo', url: 'https://github.com/payloadcms/public-demo',
}, },
{ {
name: 'payload-website', name: 'payload-website',
description: 'Payload website CMS at https://payloadcms.com',
type: 'starter', type: 'starter',
description: 'Payload website CMS at https://payloadcms.com',
url: 'https://github.com/payloadcms/website-cms', url: 'https://github.com/payloadcms/website-cms',
}, },
] ]

View File

@@ -1,9 +1,9 @@
import fs from 'fs-extra' import fs from 'fs-extra'
import path from 'path' import path from 'path'
import type { ProjectTemplate } from '../types' import type { ProjectTemplate } from '../types.js'
import { error, success } from '../utils/log' import { error, success } from '../utils/log.js'
/** Parse and swap .env.example values and write .env */ /** Parse and swap .env.example values and write .env */
export async function writeEnvFile(args: { export async function writeEnvFile(args: {

View File

@@ -2,18 +2,18 @@ import slugify from '@sindresorhus/slugify'
import arg from 'arg' import arg from 'arg'
import commandExists from 'command-exists' import commandExists from 'command-exists'
import type { CliArgs, PackageManager } from './types' import type { CliArgs, PackageManager } from './types.js'
import { createProject } from './lib/create-project' import { createProject } from './lib/create-project.js'
import { generateSecret } from './lib/generate-secret' import { generateSecret } from './lib/generate-secret.js'
import { initNext } from './lib/init-next' import { initNext } from './lib/init-next.js'
import { parseProjectName } from './lib/parse-project-name' import { parseProjectName } from './lib/parse-project-name.js'
import { parseTemplate } from './lib/parse-template' import { parseTemplate } from './lib/parse-template.js'
import { selectDb } from './lib/select-db' import { selectDb } from './lib/select-db.js'
import { getValidTemplates, validateTemplate } from './lib/templates' import { getValidTemplates, validateTemplate } from './lib/templates.js'
import { writeEnvFile } from './lib/write-env-file' import { writeEnvFile } from './lib/write-env-file.js'
import { error, success } from './utils/log' import { error, success } from './utils/log.js'
import { helpMessage, successMessage, welcomeMessage } from './utils/messages' import { helpMessage, successMessage, welcomeMessage } from './utils/messages.js'
export class Main { export class Main {
args: CliArgs args: CliArgs

77
pnpm-lock.yaml generated
View File

@@ -256,6 +256,9 @@ importers:
tempfile: tempfile:
specifier: ^3.0.0 specifier: ^3.0.0
version: 3.0.0 version: 3.0.0
tempy:
specifier: ^1.0.1
version: 1.0.1
ts-node: ts-node:
specifier: 10.9.1 specifier: 10.9.1
version: 10.9.1(@swc/core@1.4.2)(@types/node@20.5.7)(typescript@5.2.2) version: 10.9.1(@swc/core@1.4.2)(@types/node@20.5.7)(typescript@5.2.2)
@@ -341,9 +344,6 @@ importers:
'@types/prompts': '@types/prompts':
specifier: ^2.4.1 specifier: ^2.4.1
version: 2.4.9 version: 2.4.9
ts-jest:
specifier: ^29.1.0
version: 29.1.2(@babel/core@7.24.0)(esbuild@0.19.12)(jest@29.7.0)(typescript@5.2.2)
packages/db-mongodb: packages/db-mongodb:
dependencies: dependencies:
@@ -6769,6 +6769,14 @@ packages:
- supports-color - supports-color
dev: true dev: true
/aggregate-error@3.1.0:
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
engines: {node: '>=8'}
dependencies:
clean-stack: 2.2.0
indent-string: 4.0.0
dev: true
/ajv-formats@2.1.1(ajv@8.12.0): /ajv-formats@2.1.1(ajv@8.12.0):
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
peerDependencies: peerDependencies:
@@ -6944,7 +6952,6 @@ packages:
/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'}
dev: false
/array.prototype.filter@1.0.3: /array.prototype.filter@1.0.3:
resolution: {integrity: sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==} resolution: {integrity: sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==}
@@ -7594,6 +7601,11 @@ packages:
resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
dev: false dev: false
/clean-stack@2.2.0:
resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
engines: {node: '>=6'}
dev: true
/cli-boxes@3.0.0: /cli-boxes@3.0.0:
resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -8160,6 +8172,11 @@ packages:
resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==}
dev: false dev: false
/crypto-random-string@2.0.0:
resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==}
engines: {node: '>=8'}
dev: true
/crypto-random-string@4.0.0: /crypto-random-string@4.0.0:
resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@@ -8630,6 +8647,20 @@ packages:
hasBin: true hasBin: true
dev: false dev: false
/del@6.1.1:
resolution: {integrity: sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==}
engines: {node: '>=10'}
dependencies:
globby: 11.1.0
graceful-fs: 4.2.11
is-glob: 4.0.3
is-path-cwd: 2.2.0
is-path-inside: 3.0.3
p-map: 4.0.0
rimraf: 3.0.2
slash: 3.0.0
dev: true
/delayed-stream@1.0.0: /delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
@@ -8703,7 +8734,6 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dependencies: dependencies:
path-type: 4.0.0 path-type: 4.0.0
dev: false
/direction@1.0.4: /direction@1.0.4:
resolution: {integrity: sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==} resolution: {integrity: sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==}
@@ -10476,7 +10506,6 @@ packages:
ignore: 5.3.1 ignore: 5.3.1
merge2: 1.4.1 merge2: 1.4.1
slash: 3.0.0 slash: 3.0.0
dev: false
/globby@14.0.1: /globby@14.0.1:
resolution: {integrity: sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==} resolution: {integrity: sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==}
@@ -11196,6 +11225,11 @@ packages:
resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==}
engines: {node: '>=8'} engines: {node: '>=8'}
/is-path-cwd@2.2.0:
resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==}
engines: {node: '>=6'}
dev: true
/is-path-inside@3.0.3: /is-path-inside@3.0.3:
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -13573,6 +13607,13 @@ packages:
p-limit: 4.0.0 p-limit: 4.0.0
dev: true dev: true
/p-map@4.0.0:
resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
engines: {node: '>=10'}
dependencies:
aggregate-error: 3.1.0
dev: true
/p-try@2.2.0: /p-try@2.2.0:
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
@@ -13737,7 +13778,6 @@ packages:
/path-type@4.0.0: /path-type@4.0.0:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'} engines: {node: '>=8'}
dev: false
/path-type@5.0.0: /path-type@5.0.0:
resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==}
@@ -16449,6 +16489,17 @@ packages:
uuid: 3.4.0 uuid: 3.4.0
dev: true dev: true
/tempy@1.0.1:
resolution: {integrity: sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==}
engines: {node: '>=10'}
dependencies:
del: 6.1.1
is-stream: 2.0.1
temp-dir: 2.0.0
type-fest: 0.16.0
unique-string: 2.0.0
dev: true
/terminal-link@2.1.1: /terminal-link@2.1.1:
resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -16890,6 +16941,11 @@ packages:
resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
engines: {node: '>=4'} engines: {node: '>=4'}
/type-fest@0.16.0:
resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==}
engines: {node: '>=10'}
dev: true
/type-fest@0.20.2: /type-fest@0.20.2:
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -17024,6 +17080,13 @@ packages:
engines: {node: '>=18'} engines: {node: '>=18'}
dev: true dev: true
/unique-string@2.0.0:
resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==}
engines: {node: '>=8'}
dependencies:
crypto-random-string: 2.0.0
dev: true
/unique-string@3.0.0: /unique-string@3.0.0:
resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==}
engines: {node: '>=12'} engines: {node: '>=12'}

View File

@@ -1,9 +1,11 @@
import type { CompilerOptions } from 'typescript' import type { CompilerOptions } from 'typescript'
import * as CommentJson from 'comment-json' import * as CommentJson from 'comment-json'
import execa from 'execa'
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 tempy from 'tempy'
import { fileURLToPath } from 'url' import { fileURLToPath } from 'url'
import { promisify } from 'util' import { promisify } from 'util'
@@ -12,12 +14,13 @@ const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename) const dirname = path.dirname(filename)
const readFile = promisify(fs.readFile) const readFile = promisify(fs.readFile)
const writeFile = promisify(fs.writeFile)
const commonNextCreateParams = '--typescript --eslint --no-tailwind --app --import-alias="@/*"'
const nextCreateCommands: Partial<Record<'noSrcDir' | 'srcDir', string>> = { const nextCreateCommands: Partial<Record<'noSrcDir' | 'srcDir', string>> = {
srcDir: noSrcDir: `pnpm create next-app@latest . ${commonNextCreateParams} --no-src-dir`,
'pnpm create next-app@latest . --typescript --eslint --no-tailwind --app --import-alias="@/*" --src-dir', srcDir: `pnpm create next-app@latest . ${commonNextCreateParams} --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', () => {
@@ -34,9 +37,8 @@ describe('create-payload-app', () => {
}) })
describe.each(Object.keys(nextCreateCommands))(`--init-next with %s`, (nextCmdKey) => { describe.each(Object.keys(nextCreateCommands))(`--init-next with %s`, (nextCmdKey) => {
const projectDir = path.resolve(dirname, 'test-app') const projectDir = tempy.directory()
beforeEach(async () => {
beforeEach(() => {
if (fs.existsSync(projectDir)) { if (fs.existsSync(projectDir)) {
fs.rmdirSync(projectDir, { recursive: true }) fs.rmdirSync(projectDir, { recursive: true })
} }
@@ -47,7 +49,20 @@ describe('create-payload-app', () => {
} }
// Create a new Next.js project with default options // Create a new Next.js project with default options
shelljs.exec(nextCreateCommands[nextCmdKey], { cwd: projectDir }) console.log(`Running: ${nextCreateCommands[nextCmdKey]} in ${projectDir}`)
const [cmd, ...args] = nextCreateCommands[nextCmdKey].split(' ')
const { exitCode, stderr } = await execa(cmd, [...args], { cwd: projectDir })
if (exitCode !== 0) {
console.error({ exitCode, stderr })
}
// WARNING: Big WTF here. Replace improper path string inside tsconfig.json.
// For some reason two double quotes are used for the src path when executed in the test environment.
// This is likely ESM-related
const tsConfigPath = path.resolve(projectDir, 'tsconfig.json')
let userTsConfigContent = await readFile(tsConfigPath, { encoding: 'utf8' })
userTsConfigContent = userTsConfigContent.replace('""@/*""', '"@/*"')
await writeFile(tsConfigPath, userTsConfigContent, { encoding: 'utf8' })
}) })
afterEach(() => { afterEach(() => {
@@ -78,9 +93,11 @@ describe('create-payload-app', () => {
const userTsConfig = CommentJson.parse(userTsConfigContent) as { const userTsConfig = CommentJson.parse(userTsConfigContent) as {
compilerOptions?: CompilerOptions compilerOptions?: CompilerOptions
} }
expect(userTsConfig.compilerOptions.paths?.['@payload-config']).toEqual([ expect(userTsConfig.compilerOptions.paths?.['@payload-config']).toStrictEqual([
'./payload.config.ts', './payload.config.ts',
]) ])
// TODO: Start the Next.js app and check if it runs
}) })
}) })
}) })