Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit 5972726

Browse files
authored
Add rootDir setting to eslint-plugin-next (vercel#27918)
## Introduction This PR enables setting a `rootDir` for a Next.js project, and follows the same pattern used by [`@typescript-eslint/parser`](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/parser#parseroptionsproject). ## Details Previously, users had to pass paths to the rule itself. ```js module.exports = { rules: { "@next/next/no-html-link-for-pages": [ "error", // This could be a string, or array of strings. "/packages/my-app/pages", ], }, }; ``` With this PR, this has been simplified (the previous implementation still works as expected). ```js module.exports = { settings: { next: { rootDir: "/packages/my-app", }, }, rules: { "@next/next/no-html-link-for-pages": "error", }, }; ``` Further, this rule allows the use of globs, again aligning with `@typescript-eslint/parser`. ```js module.exports = { settings: { next: { // Globs rootDir: "/packages/*", rootDir: "/packages/{app-a,app-b}", // Arrays rootDir: ["/app-a", "/app-b"], // Arrays with globs rootDir: ["/main-app", "/other-apps/*"], }, }; ``` This enables users to either provide per-workspace configuration with overrides, or to use globs for situations like monorepos where the apps share a domain (micro-frontends). This doesn't solve, but improves vercel#26330. ## Feature - [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [x] 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 helpful link attached, see `contributing.md` ## Documentation / Examples - [x] Make sure the linting passes
1 parent 021ddb6 commit 5972726

File tree

11 files changed

+104
-17
lines changed

11 files changed

+104
-17
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# `@next/eslint-plugin-next`
2+
3+
Documentation for `@next/eslint-plugin-next` can be found at:
4+
https://nextjs.org/docs/basic-features/eslint#eslint-plugin
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"compilerOptions": {
3+
"module": "commonjs",
4+
"target": "es2019"
5+
},
6+
"exclude": ["node_modules"]
7+
}

packages/eslint-plugin-next/lib/rules/google-font-display.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const NodeAttributes = require('../utils/nodeAttributes.js')
1+
const NodeAttributes = require('../utils/node-attributes.js')
22

33
module.exports = {
44
meta: {

packages/eslint-plugin-next/lib/rules/google-font-preconnect.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const NodeAttributes = require('../utils/nodeAttributes.js')
1+
const NodeAttributes = require('../utils/node-attributes.js')
22

33
module.exports = {
44
meta: {

packages/eslint-plugin-next/lib/rules/link-passhref.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const NodeAttributes = require('../utils/nodeAttributes.js')
1+
const NodeAttributes = require('../utils/node-attributes.js')
22

33
module.exports = {
44
meta: {

packages/eslint-plugin-next/lib/rules/no-html-link-for-pages.js

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
// @ts-check
12
const path = require('path')
23
const fs = require('fs')
4+
const getRootDir = require('../utils/get-root-dirs')
35
const {
46
getUrlFromPagesDirectories,
57
normalizeURL,
@@ -20,7 +22,7 @@ const fsExistsSyncCache = {}
2022
module.exports = {
2123
meta: {
2224
docs: {
23-
description: 'Prohibit full page refresh for nextjs pages',
25+
description: 'Prohibit full page refresh for Next.js pages',
2426
category: 'HTML',
2527
recommended: true,
2628
},
@@ -43,14 +45,27 @@ module.exports = {
4345
],
4446
},
4547

48+
/**
49+
* Creates an ESLint rule listener.
50+
*
51+
* @param {import('eslint').Rule.RuleContext} context - ESLint rule context
52+
* @returns {import('eslint').Rule.RuleListener} An ESLint rule listener
53+
*/
4654
create: function (context) {
47-
const [customPagesDirectory] = context.options
48-
const pagesDirs = customPagesDirectory
49-
? [customPagesDirectory].flat()
50-
: [
51-
path.join(context.getCwd(), 'pages'),
52-
path.join(context.getCwd(), 'src', 'pages'),
53-
]
55+
/** @type {(string|string[])[]} */
56+
const ruleOptions = context.options
57+
const [customPagesDirectory] = ruleOptions
58+
59+
const rootDirs = getRootDir(context)
60+
61+
const pagesDirs = (customPagesDirectory
62+
? [customPagesDirectory]
63+
: rootDirs.map((dir) => [
64+
path.join(dir, 'pages'),
65+
path.join(dir, 'src', 'pages'),
66+
])
67+
).flat()
68+
5469
const foundPagesDirs = pagesDirs.filter((dir) => {
5570
if (fsExistsSyncCache[dir] === undefined) {
5671
fsExistsSyncCache[dir] = fs.existsSync(dir)

packages/eslint-plugin-next/lib/rules/no-page-custom-font.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const NodeAttributes = require('../utils/nodeAttributes.js')
1+
const NodeAttributes = require('../utils/node-attributes.js')
22

33
module.exports = {
44
meta: {
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// @ts-check
2+
const glob = require('glob')
3+
4+
/**
5+
* Process a Next.js root directory glob.
6+
*
7+
* @param {string} rootDir - A Next.js root directory glob.
8+
* @returns {string[]} - An array of Root directories.
9+
*/
10+
const processRootDir = (rootDir) => {
11+
// Ensures we only match folders.
12+
if (!rootDir.endsWith('/')) rootDir += '/'
13+
return glob.sync(rootDir)
14+
}
15+
16+
/**
17+
* Gets one or more Root
18+
*
19+
* @param {import('eslint').Rule.RuleContext} context - ESLint rule context
20+
* @returns An array of root directories.
21+
*/
22+
const getRootDirs = (context) => {
23+
let rootDirs = [context.getCwd()]
24+
25+
/** @type {{rootDir?:string|string[]}|undefined} */
26+
const nextSettings = context.settings.next || {}
27+
let rootDir = nextSettings.rootDir
28+
29+
if (typeof rootDir === 'string') {
30+
rootDirs = processRootDir(rootDir)
31+
} else if (Array.isArray(rootDir)) {
32+
rootDirs = rootDir
33+
.map((dir) => (typeof dir === 'string' ? processRootDir(dir) : []))
34+
.flat()
35+
}
36+
37+
return rootDirs
38+
}
39+
40+
module.exports = getRootDirs

packages/eslint-plugin-next/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,14 @@
77
"repository": {
88
"url": "vercel/next.js",
99
"directory": "packages/eslint-plugin-next"
10+
},
11+
"files": [
12+
"lib"
13+
],
14+
"dependencies": {
15+
"glob": "7.1.7"
16+
},
17+
"devDependencies": {
18+
"@types/eslint": "7.28.0"
1019
}
1120
}

0 commit comments

Comments
 (0)