Add rootDir setting to eslint-plugin-next (#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 https://github.com/vercel/next.js/issues/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
2021-08-11 12:37:55 +02:00
// @ts-check
2020-05-19 10:54:32 +02:00
const path = require ( 'path' )
const fs = require ( 'fs' )
Add rootDir setting to eslint-plugin-next (#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 https://github.com/vercel/next.js/issues/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
2021-08-11 12:37:55 +02:00
const getRootDir = require ( '../utils/get-root-dirs' )
2021-04-30 13:09:07 +02:00
const {
2021-07-20 23:29:54 +02:00
getUrlFromPagesDirectories ,
2021-04-30 13:09:07 +02:00
normalizeURL ,
execOnce ,
} = require ( '../utils/url' )
const pagesDirWarning = execOnce ( ( pagesDirs ) => {
console . warn (
` Pages directory cannot be found at ${ pagesDirs . join ( ' or ' ) } . ` +
` If using a custom path, please configure with the no-html-link-for-pages rule in your eslint config file `
)
} )
2020-05-19 10:54:32 +02:00
2021-07-20 23:29:54 +02:00
// Cache for fs.existsSync lookup.
// Prevent multiple blocking IO requests that have already been calculated.
const fsExistsSyncCache = { }
2020-05-19 10:54:32 +02:00
module . exports = {
meta : {
docs : {
Add rootDir setting to eslint-plugin-next (#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 https://github.com/vercel/next.js/issues/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
2021-08-11 12:37:55 +02:00
description : 'Prohibit full page refresh for Next.js pages' ,
2020-05-19 10:54:32 +02:00
category : 'HTML' ,
recommended : true ,
} ,
fixable : null , // or "code" or "whitespace"
2021-07-20 23:29:54 +02:00
schema : [
{
oneOf : [
{
type : 'string' ,
} ,
{
type : 'array' ,
uniqueItems : true ,
items : {
type : 'string' ,
} ,
} ,
] ,
} ,
] ,
2020-05-19 10:54:32 +02:00
} ,
Add rootDir setting to eslint-plugin-next (#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 https://github.com/vercel/next.js/issues/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
2021-08-11 12:37:55 +02:00
/ * *
* Creates an ESLint rule listener .
*
* @ param { import ( 'eslint' ) . Rule . RuleContext } context - ESLint rule context
* @ returns { import ( 'eslint' ) . Rule . RuleListener } An ESLint rule listener
* /
2020-05-19 10:54:32 +02:00
create : function ( context ) {
Add rootDir setting to eslint-plugin-next (#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 https://github.com/vercel/next.js/issues/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
2021-08-11 12:37:55 +02:00
/** @type {(string|string[])[]} */
const ruleOptions = context . options
const [ customPagesDirectory ] = ruleOptions
const rootDirs = getRootDir ( context )
2021-08-17 09:18:08 +02:00
const pagesDirs = (
customPagesDirectory
? [ customPagesDirectory ]
: rootDirs . map ( ( dir ) => [
path . join ( dir , 'pages' ) ,
path . join ( dir , 'src' , 'pages' ) ,
] )
Add rootDir setting to eslint-plugin-next (#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 https://github.com/vercel/next.js/issues/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
2021-08-11 12:37:55 +02:00
) . flat ( )
2021-07-20 23:29:54 +02:00
const foundPagesDirs = pagesDirs . filter ( ( dir ) => {
if ( fsExistsSyncCache [ dir ] === undefined ) {
fsExistsSyncCache [ dir ] = fs . existsSync ( dir )
}
return fsExistsSyncCache [ dir ]
} )
if ( foundPagesDirs . length === 0 ) {
2021-04-30 13:09:07 +02:00
pagesDirWarning ( pagesDirs )
return { }
2020-05-19 10:54:32 +02:00
}
2021-07-20 23:29:54 +02:00
const urls = getUrlFromPagesDirectories ( '/' , foundPagesDirs )
2020-05-19 10:54:32 +02:00
return {
JSXOpeningElement ( node ) {
if ( node . name . name !== 'a' ) {
return
}
if ( node . attributes . length === 0 ) {
return
}
2020-05-22 01:42:20 +02:00
const href = node . attributes . find (
( attr ) => attr . type === 'JSXAttribute' && attr . name . name === 'href'
)
2020-05-19 10:54:32 +02:00
if ( ! href || href . value . type !== 'Literal' ) {
return
}
const hrefPath = normalizeURL ( href . value . value )
2020-05-22 01:42:20 +02:00
// Outgoing links are ignored
if ( /^(https?:\/\/|\/\/)/ . test ( hrefPath ) ) {
return
}
2020-05-19 10:54:32 +02:00
urls . forEach ( ( url ) => {
if ( url . test ( normalizeURL ( hrefPath ) ) ) {
context . report ( {
node ,
2021-04-30 13:09:07 +02:00
message : ` Do not use the HTML <a> tag to navigate to ${ hrefPath } . Use Link from 'next/link' instead. See: https://nextjs.org/docs/messages/no-html-link-for-pages. ` ,
2020-05-19 10:54:32 +02:00
} )
}
} )
} ,
}
} ,
}