Skip to content

Commit 6f5cf5d

Browse files
authored
feat(cpa): warn on unsupported Next.js version (#7434)
Improves messaging if running an unsupported version of Next.js. Closes #7430
1 parent aaf3a39 commit 6f5cf5d

File tree

3 files changed

+66
-3
lines changed

3 files changed

+66
-3
lines changed

packages/create-payload-app/src/lib/init-next.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,25 +240,63 @@ async function installDeps(projectDir: string, packageManager: PackageManager, d
240240
export async function getNextAppDetails(projectDir: string): Promise<NextAppDetails> {
241241
const isSrcDir = fs.existsSync(path.resolve(projectDir, 'src'))
242242

243+
// Match next.config.js, next.config.ts, next.config.mjs, next.config.cjs
243244
const nextConfigPath: string | undefined = (
244-
await globby('next.config.*(t|j)s', { absolute: true, cwd: projectDir })
245+
await globby('next.config.(\\w)?(t|j)s', { absolute: true, cwd: projectDir })
245246
)?.[0]
246247

247248
if (!nextConfigPath || nextConfigPath.length === 0) {
248249
return {
249250
hasTopLevelLayout: false,
250251
isSrcDir,
252+
isSupportedNextVersion: false,
251253
nextConfigPath: undefined,
254+
nextVersion: null,
252255
}
253256
}
254257

255258
const packageObj = await fse.readJson(path.resolve(projectDir, 'package.json'))
259+
// Check if Next.js version is new enough
260+
let nextVersion = null
261+
if (packageObj.dependencies?.next) {
262+
nextVersion = packageObj.dependencies.next
263+
// Match versions using regex matching groups
264+
const versionMatch = /(?<major>\d+)/.exec(nextVersion)
265+
if (!versionMatch) {
266+
p.log.warn(`Could not determine Next.js version from ${nextVersion}`)
267+
return {
268+
hasTopLevelLayout: false,
269+
isSrcDir,
270+
isSupportedNextVersion: false,
271+
nextConfigPath,
272+
nextVersion,
273+
}
274+
}
275+
276+
const { major } = versionMatch.groups as { major: string }
277+
const majorVersion = parseInt(major)
278+
if (majorVersion < 15) {
279+
return {
280+
hasTopLevelLayout: false,
281+
isSrcDir,
282+
isSupportedNextVersion: false,
283+
nextConfigPath,
284+
nextVersion,
285+
}
286+
}
287+
}
288+
289+
const isSupportedNextVersion = true
290+
291+
// Check if Payload already installed
256292
if (packageObj.dependencies?.payload) {
257293
return {
258294
hasTopLevelLayout: false,
259295
isPayloadInstalled: true,
260296
isSrcDir,
297+
isSupportedNextVersion,
261298
nextConfigPath,
299+
nextVersion,
262300
}
263301
}
264302

@@ -281,7 +319,15 @@ export async function getNextAppDetails(projectDir: string): Promise<NextAppDeta
281319
? fs.existsSync(path.resolve(nextAppDir, 'layout.tsx'))
282320
: false
283321

284-
return { hasTopLevelLayout, isSrcDir, nextAppDir, nextConfigPath, nextConfigType: configType }
322+
return {
323+
hasTopLevelLayout,
324+
isSrcDir,
325+
isSupportedNextVersion,
326+
nextAppDir,
327+
nextConfigPath,
328+
nextConfigType: configType,
329+
nextVersion,
330+
}
285331
}
286332

287333
function getProjectType(args: {

packages/create-payload-app/src/main.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,22 @@ export class Main {
8585

8686
// Detect if inside Next.js project
8787
const nextAppDetails = await getNextAppDetails(process.cwd())
88-
const { hasTopLevelLayout, isPayloadInstalled, nextAppDir, nextConfigPath } = nextAppDetails
88+
const {
89+
hasTopLevelLayout,
90+
isPayloadInstalled,
91+
isSupportedNextVersion,
92+
nextAppDir,
93+
nextConfigPath,
94+
nextVersion,
95+
} = nextAppDetails
96+
97+
if (nextConfigPath && !isSupportedNextVersion) {
98+
p.log.warn(
99+
`Next.js v${nextVersion} is unsupported. Next.js >= 15 is required to use Payload.`,
100+
)
101+
p.outro(feedbackOutro())
102+
process.exit(0)
103+
}
89104

90105
// Upgrade Payload in existing project
91106
if (isPayloadInstalled && nextConfigPath) {

packages/create-payload-app/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,11 @@ export type NextAppDetails = {
7070
hasTopLevelLayout: boolean
7171
isPayloadInstalled?: boolean
7272
isSrcDir: boolean
73+
isSupportedNextVersion: boolean
7374
nextAppDir?: string
7475
nextConfigPath?: string
7576
nextConfigType?: NextConfigType
77+
nextVersion: null | string
7678
}
7779

7880
export type NextConfigType = 'cjs' | 'esm' | 'ts'

0 commit comments

Comments
 (0)