Skip to content

Commit 2cedd40

Browse files
RoomWithOutRoofRoomWithOutRoof
authored andcommitted
fix(eslint-plugin-next): respect pageExtensions in no-html-link-for-pages rule
The no-html-link-for-pages rule was using hardcoded regex patterns /(\.(j|t)sx?)$/ to detect page files, ignoring the pageExtensions configured in next.config.js. This change: - Reads pageExtensions from context.settings.next.pageExtensions - Passes pageExtensions to getUrlFromPagesDirectories and getUrlFromAppDirectory - Updates parseUrlForPages and parseUrlForAppDir to build dynamic regex patterns from the configured extensions - Defaults to ['jsx', 'js', 'ts', 'tsx'] when no pageExtensions is set Fixes #53473
1 parent e68639f commit 2cedd40

2 files changed

Lines changed: 50 additions & 23 deletions

File tree

packages/eslint-plugin-next/src/rules/no-html-link-for-pages.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,18 @@ export default defineRule({
107107
return {}
108108
}
109109

110-
const pageUrls = cachedGetUrlFromPagesDirectories('/', foundPagesDirs)
111-
const appDirUrls = cachedGetUrlFromAppDirectory('/', foundAppDirs)
110+
// Get pageExtensions from settings, default to ['jsx', 'js', 'ts', 'tsx']
111+
const nextSettings: { pageExtensions?: string[] } =
112+
context.settings.next || {}
113+
const pageExtensions = nextSettings.pageExtensions || [
114+
'jsx',
115+
'js',
116+
'ts',
117+
'tsx',
118+
]
119+
120+
const pageUrls = cachedGetUrlFromPagesDirectories('/', foundPagesDirs, pageExtensions)
121+
const appDirUrls = cachedGetUrlFromAppDirectory('/', foundAppDirs, pageExtensions)
112122
const allUrlRegex = [...pageUrls, ...appDirUrls]
113123

114124
return {

packages/eslint-plugin-next/src/utils/url.ts

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,31 @@ const fsReadDirSyncCache = {}
88
/**
99
* Recursively parse directory for page URLs.
1010
*/
11-
function parseUrlForPages(urlprefix: string, directory: string) {
11+
function parseUrlForPages(
12+
urlprefix: string,
13+
directory: string,
14+
pageExtensions: string[]
15+
) {
1216
fsReadDirSyncCache[directory] ??= fs.readdirSync(directory, {
1317
withFileTypes: true,
1418
})
1519
const res = []
20+
// Build a regex from the configured pageExtensions
21+
const extPattern = new RegExp(`(\\.(?:${pageExtensions.join('|')}))$`)
22+
const indexPattern = new RegExp(`^index(\\.(?:${pageExtensions.join('|')}))$`)
23+
1624
fsReadDirSyncCache[directory].forEach((dirent) => {
17-
// TODO: this should account for all page extensions
18-
// not just js(x) and ts(x)
19-
if (/(\.(j|t)sx?)$/.test(dirent.name)) {
20-
if (/^index(\.(j|t)sx?)$/.test(dirent.name)) {
25+
if (extPattern.test(dirent.name)) {
26+
if (indexPattern.test(dirent.name)) {
2127
res.push(
22-
`${urlprefix}${dirent.name.replace(/^index(\.(j|t)sx?)$/, '')}`
28+
`${urlprefix}${dirent.name.replace(indexPattern, '')}`
2329
)
2430
}
25-
res.push(`${urlprefix}${dirent.name.replace(/(\.(j|t)sx?)$/, '')}`)
31+
res.push(`${urlprefix}${dirent.name.replace(extPattern, '')}`)
2632
} else {
2733
const dirPath = path.join(directory, dirent.name)
2834
if (dirent.isDirectory() && !dirent.isSymbolicLink()) {
29-
res.push(...parseUrlForPages(urlprefix + dirent.name + '/', dirPath))
35+
res.push(...parseUrlForPages(urlprefix + dirent.name + '/', dirPath, pageExtensions))
3036
}
3137
}
3238
})
@@ -36,24 +42,31 @@ function parseUrlForPages(urlprefix: string, directory: string) {
3642
/**
3743
* Recursively parse app directory for URLs.
3844
*/
39-
function parseUrlForAppDir(urlprefix: string, directory: string) {
45+
function parseUrlForAppDir(
46+
urlprefix: string,
47+
directory: string,
48+
pageExtensions: string[]
49+
) {
4050
fsReadDirSyncCache[directory] ??= fs.readdirSync(directory, {
4151
withFileTypes: true,
4252
})
4353
const res = []
54+
// Build a regex from the configured pageExtensions
55+
const extPattern = new RegExp(`(\\.(?:${pageExtensions.join('|')}))$`)
56+
const pagePattern = new RegExp(`^page(\\.(?:${pageExtensions.join('|')}))$`)
57+
const layoutPattern = new RegExp(`^layout(\\.(?:${pageExtensions.join('|')}))$`)
58+
4459
fsReadDirSyncCache[directory].forEach((dirent) => {
45-
// TODO: this should account for all page extensions
46-
// not just js(x) and ts(x)
47-
if (/(\.(j|t)sx?)$/.test(dirent.name)) {
48-
if (/^page(\.(j|t)sx?)$/.test(dirent.name)) {
49-
res.push(`${urlprefix}${dirent.name.replace(/^page(\.(j|t)sx?)$/, '')}`)
50-
} else if (!/^layout(\.(j|t)sx?)$/.test(dirent.name)) {
51-
res.push(`${urlprefix}${dirent.name.replace(/(\.(j|t)sx?)$/, '')}`)
60+
if (extPattern.test(dirent.name)) {
61+
if (pagePattern.test(dirent.name)) {
62+
res.push(`${urlprefix}${dirent.name.replace(pagePattern, '')}`)
63+
} else if (!layoutPattern.test(dirent.name)) {
64+
res.push(`${urlprefix}${dirent.name.replace(extPattern, '')}`)
5265
}
5366
} else {
5467
const dirPath = path.join(directory, dirent.name)
5568
if (dirent.isDirectory(dirPath) && !dirent.isSymbolicLink()) {
56-
res.push(...parseUrlForPages(urlprefix + dirent.name + '/', dirPath))
69+
res.push(...parseUrlForAppDir(urlprefix + dirent.name + '/', dirPath, pageExtensions))
5770
}
5871
}
5972
})
@@ -136,13 +149,16 @@ export function normalizeAppPath(route: string) {
136149
*/
137150
export function getUrlFromPagesDirectories(
138151
urlPrefix: string,
139-
directories: string[]
152+
directories: string[],
153+
pageExtensions: string[] = ['jsx', 'js', 'ts', 'tsx']
140154
) {
141155
return Array.from(
142156
// De-duplicate similar pages across multiple directories.
143157
new Set(
144158
directories
145-
.flatMap((directory) => parseUrlForPages(urlPrefix, directory))
159+
.flatMap((directory) =>
160+
parseUrlForPages(urlPrefix, directory, pageExtensions)
161+
)
146162
.map(
147163
// Since the URLs are normalized we add `^` and `$` to the RegExp to make sure they match exactly.
148164
(url) => `^${normalizeURL(url)}$`
@@ -156,13 +172,14 @@ export function getUrlFromPagesDirectories(
156172

157173
export function getUrlFromAppDirectory(
158174
urlPrefix: string,
159-
directories: string[]
175+
directories: string[],
176+
pageExtensions: string[] = ['jsx', 'js', 'ts', 'tsx']
160177
) {
161178
return Array.from(
162179
// De-duplicate similar pages across multiple directories.
163180
new Set(
164181
directories
165-
.map((directory) => parseUrlForAppDir(urlPrefix, directory))
182+
.map((directory) => parseUrlForAppDir(urlPrefix, directory, pageExtensions))
166183
.flat()
167184
.map(
168185
// Since the URLs are normalized we add `^` and `$` to the RegExp to make sure they match exactly.

0 commit comments

Comments
 (0)