22b16e1e37
## Bug - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Errors have a helpful link attached, see `contributing.md` Fixes: https://github.com/vercel/next.js/issues/43019#issuecomment-1319340037
72 lines
2.4 KiB
TypeScript
72 lines
2.4 KiB
TypeScript
import { fileExists } from '../../lib/file-exists'
|
|
import { getPagePaths } from '../../shared/lib/page-path/get-page-paths'
|
|
import { nonNullable } from '../../lib/non-nullable'
|
|
import { join, sep, normalize } from 'path'
|
|
import { promises } from 'fs'
|
|
import { warn } from '../../build/output/log'
|
|
import chalk from '../../lib/chalk'
|
|
|
|
async function isTrueCasePagePath(pagePath: string, pagesDir: string) {
|
|
const pageSegments = normalize(pagePath).split(sep).filter(Boolean)
|
|
const segmentExistsPromises = pageSegments.map(async (segment, i) => {
|
|
const segmentParentDir = join(pagesDir, ...pageSegments.slice(0, i))
|
|
const parentDirEntries = await promises.readdir(segmentParentDir)
|
|
return parentDirEntries.includes(segment)
|
|
})
|
|
|
|
return (await Promise.all(segmentExistsPromises)).every(Boolean)
|
|
}
|
|
|
|
/**
|
|
* Finds a page file with the given parameters. If the page is duplicated with
|
|
* multiple extensions it will throw, otherwise it will return the *relative*
|
|
* path to the page file or null if it is not found.
|
|
*
|
|
* @param pagesDir Absolute path to the pages folder with trailing `/pages`.
|
|
* @param normalizedPagePath The page normalized (it will be denormalized).
|
|
* @param pageExtensions Array of page extensions.
|
|
*/
|
|
export async function findPageFile(
|
|
pagesDir: string,
|
|
normalizedPagePath: string,
|
|
pageExtensions: string[],
|
|
isAppDir: boolean
|
|
): Promise<string | null> {
|
|
const pagePaths = getPagePaths(normalizedPagePath, pageExtensions, isAppDir)
|
|
const [existingPath, ...others] = (
|
|
await Promise.all(
|
|
pagePaths.map(async (path) => {
|
|
const filePath = join(pagesDir, path)
|
|
return (await fileExists(filePath)) ? path : null
|
|
})
|
|
)
|
|
).filter(nonNullable)
|
|
|
|
if (!existingPath) {
|
|
return null
|
|
}
|
|
|
|
if (!(await isTrueCasePagePath(existingPath, pagesDir))) {
|
|
return null
|
|
}
|
|
|
|
if (others.length > 0) {
|
|
warn(
|
|
`Duplicate page detected. ${chalk.cyan(
|
|
join('pages', existingPath)
|
|
)} and ${chalk.cyan(
|
|
join('pages', others[0])
|
|
)} both resolve to ${chalk.cyan(normalizedPagePath)}.`
|
|
)
|
|
}
|
|
|
|
return existingPath
|
|
}
|
|
|
|
// Determine if the file is leaf node page file under layouts,
|
|
// The filename should start with 'page' and end with one of the allowed extensions
|
|
export function isLayoutsLeafPage(filePath: string, pageExtensions: string[]) {
|
|
return new RegExp(
|
|
`(^page|[\\\\/]page)\\.(?:${pageExtensions.join('|')})$`
|
|
).test(filePath)
|
|
}
|