Skip to content

Commit e74de1a

Browse files
timneutkensijjk
andauthored
Clarify app and pages file conflicting files (#42415)
Improvement to how the `app` and `pages` files conflict is shown. Especially the last log line `"pages/" - "app/"` made it seem like you should remove the `pages` folder altogether. This was a bug in how the `''` case was displayed. After having a look at this I went further and added exactly which file caused the conflict given that `app` allows you to create `app/(home)/page.js` and such it saves some digging for what the actual conflicting file is. Similarly in `pages` both `pages/dashboard/index.js` and `pages/dashboard.js` are possible. Before: ``` error - Conflicting app and page files were found, please remove the conflicting files to continue: error - "pages/another" - "app/another" error - "pages/hello" - "app/hello" error - "pages/" - "app/" ``` After: ``` error - Conflicting app and page files were found, please remove the conflicting files to continue: error - "pages/another.js" - "app/another/page.js" error - "pages/index.js" - "app/page.js" error - "pages/hello.js" - "app/hello/page.js" ``` ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm build && pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md) Co-authored-by: JJ Kasper <[email protected]>
1 parent 21b6654 commit e74de1a

File tree

5 files changed

+70
-38
lines changed

5 files changed

+70
-38
lines changed

Diff for: packages/next/build/index.ts

+32-26
Original file line numberDiff line numberDiff line change
@@ -576,36 +576,42 @@ export default async function build(
576576
})
577577
)
578578

579-
const pageKeys = {
580-
pages: Object.keys(mappedPages),
581-
app: mappedAppPages
582-
? Object.keys(mappedAppPages).map(
583-
(key) => normalizeAppPath(key) || '/'
584-
)
585-
: undefined,
586-
}
587-
588-
if (pageKeys.app) {
589-
const conflictingAppPagePaths = []
590-
591-
for (const appPath of pageKeys.app) {
592-
if (pageKeys.pages.includes(appPath)) {
593-
conflictingAppPagePaths.push(appPath)
579+
const pagesPageKeys = Object.keys(mappedPages)
580+
581+
const conflictingAppPagePaths: [pagePath: string, appPath: string][] = []
582+
const appPageKeys: string[] = []
583+
if (mappedAppPages) {
584+
for (const appKey in mappedAppPages) {
585+
const normalizedAppPageKey = normalizeAppPath(appKey) || '/'
586+
const pagePath = mappedPages[normalizedAppPageKey]
587+
if (pagePath) {
588+
const appPath = mappedAppPages[appKey]
589+
conflictingAppPagePaths.push([
590+
pagePath.replace(/^private-next-pages/, 'pages'),
591+
appPath.replace(/^private-next-app-dir/, 'app'),
592+
])
594593
}
594+
595+
appPageKeys.push(normalizedAppPageKey)
595596
}
596-
const numConflicting = conflictingAppPagePaths.length
597+
}
597598

598-
if (numConflicting > 0) {
599-
Log.error(
600-
`Conflicting app and page file${
601-
numConflicting === 1 ? ' was' : 's were'
602-
} found, please remove the conflicting files to continue:`
603-
)
604-
for (const p of conflictingAppPagePaths) {
605-
Log.error(` "pages${p}" - "app${p}"`)
606-
}
607-
process.exit(1)
599+
const pageKeys = {
600+
pages: pagesPageKeys,
601+
app: appPageKeys.length > 0 ? appPageKeys : undefined,
602+
}
603+
604+
const numConflictingAppPaths = conflictingAppPagePaths.length
605+
if (mappedAppPages && numConflictingAppPaths > 0) {
606+
Log.error(
607+
`Conflicting app and page file${
608+
numConflictingAppPaths === 1 ? ' was' : 's were'
609+
} found, please remove the conflicting files to continue:`
610+
)
611+
for (const [pagePath, appPath] of conflictingAppPagePaths) {
612+
Log.error(` "${pagePath}" - "${appPath}"`)
608613
}
614+
process.exit(1)
609615
}
610616

611617
const conflictingPublicFiles: string[] = []

Diff for: packages/next/server/dev/next-dev-server.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,9 @@ export default class DevServer extends Server {
323323
const appPaths: Record<string, string[]> = {}
324324
const edgeRoutesSet = new Set<string>()
325325
const pageNameSet = new Set<string>()
326-
const conflictingAppPagePaths: string[] = []
326+
const conflictingAppPagePaths = new Set<string>()
327+
const appPageFilePaths = new Map<string, string>()
328+
const pagesPageFilePaths = new Map<string, string>()
327329

328330
let envChange = false
329331
let tsconfigChange = false
@@ -422,8 +424,13 @@ export default class DevServer extends Server {
422424
pageName = pageName.replace(/\/index$/, '') || '/'
423425
}
424426

425-
if (pageNameSet.has(pageName)) {
426-
conflictingAppPagePaths.push(pageName)
427+
;(isAppPath ? appPageFilePaths : pagesPageFilePaths).set(
428+
pageName,
429+
fileName
430+
)
431+
432+
if (this.appDir && pageNameSet.has(pageName)) {
433+
conflictingAppPagePaths.add(pageName)
427434
} else {
428435
pageNameSet.add(pageName)
429436
}
@@ -451,17 +458,18 @@ export default class DevServer extends Server {
451458
})
452459
}
453460

454-
const numConflicting = conflictingAppPagePaths.length
461+
const numConflicting = conflictingAppPagePaths.size
455462
if (numConflicting > 0) {
456463
Log.error(
457464
`Conflicting app and page file${
458465
numConflicting === 1 ? ' was' : 's were'
459466
} found, please remove the conflicting files to continue:`
460467
)
461468
for (const p of conflictingAppPagePaths) {
462-
Log.error(` "pages${p}" - "app${p}"`)
469+
const appPath = relative(this.dir, appPageFilePaths.get(p)!)
470+
const pagesPath = relative(this.dir, pagesPageFilePaths.get(p)!)
471+
Log.error(` "${pagesPath}" - "${appPath}"`)
463472
}
464-
//process.exit(1)
465473
}
466474

467475
if (!this.usingTypeScript && enabledTypeScript) {
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page(props) {
2+
return <p>index page</p>
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return <h1>Hello World!</h1>
3+
}

Diff for: test/integration/conflicting-app-page-error/test/index.test.js

+18-6
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,26 @@ function runTests({ dev }) {
2121
it('should print error for conflicting app/page', async () => {
2222
expect(output).toMatch(/Conflicting app and page files were found/)
2323

24-
for (const conflict of ['/hello', '/another']) {
25-
expect(output).toContain(conflict)
24+
for (const [pagePath, appPath] of [
25+
['pages/index.js', 'app/page.js'],
26+
['pages/hello.js', 'app/hello/page.js'],
27+
['pages/another.js', 'app/another/page.js'],
28+
]) {
29+
expect(output).toContain(`"${pagePath}" - "${appPath}"`)
2630
}
27-
expect(output).not.toContain('/index')
31+
expect(output).not.toContain('/non-conflict-pages')
2832
expect(output).not.toContain('/non-conflict')
2933
})
3034

3135
if (dev) {
36+
it('should show error overlay for /', async () => {
37+
const browser = await webdriver(appPort, '/')
38+
expect(await hasRedbox(browser, true)).toBe(true)
39+
expect(await getRedboxHeader(browser)).toContain(
40+
'Conflicting app and page file found: "app/page.js" and "pages/index.js". Please remove one to continue.'
41+
)
42+
})
43+
3244
it('should show error overlay for /hello', async () => {
3345
const browser = await webdriver(appPort, '/hello')
3446
expect(await hasRedbox(browser, true)).toBe(true)
@@ -45,11 +57,11 @@ function runTests({ dev }) {
4557
)
4658
})
4759

48-
it('should not show error overlay for /', async () => {
49-
const browser = await webdriver(appPort, '/')
60+
it('should not show error overlay for /non-conflict-pages', async () => {
61+
const browser = await webdriver(appPort, '/non-conflict-pages')
5062
expect(await hasRedbox(browser, false)).toBe(false)
5163
expect(await getRedboxHeader(browser)).toBe(null)
52-
expect(await browser.elementByCss('p').text()).toBe('index page')
64+
expect(await browser.elementByCss('h1').text()).toBe('Hello World!')
5365
})
5466

5567
it('should not show error overlay for /non-conflict', async () => {

0 commit comments

Comments
 (0)