Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Add ESLint to Next.js (#22437)
For #22228 This PR: - Adds ESLint to toolchain - Included by default for builds (`next build`) - Can be enabled for development (`next dev`) - Custom formatter built for output - Adds appropriate tests - Adds two documentation pages
- Loading branch information
Showing
with
900 additions
and 25 deletions.
- +2 −0 .eslintignore
- +70 −0 docs/api-reference/next.config.js/eslint-warnings-errors.md
- +50 −0 docs/basic-features/eslint.md
- +8 −0 docs/manifest.json
- +14 −8 packages/eslint-plugin-next/lib/rules/no-html-link-for-pages.js
- +14 −0 packages/eslint-plugin-next/lib/utils/url.js
- +35 −0 packages/next/build/index.ts
- +68 −0 packages/next/lib/eslint/customFormatter.ts
- +141 −0 packages/next/lib/verifyAndLint.ts
- +4 −0 packages/next/next-server/server/config-shared.ts
- +5 −1 packages/next/package.json
- +1 −0 packages/next/server/hot-reloader.ts
- +12 −0 packages/next/server/on-demand-entry-handler.ts
- +3 −0 test-pnp.sh
- +8 −3 test/integration/dist-dir/test/index.test.js
- +16 −0 test/integration/eslint/custom-eslint-config/.eslintrc.json
- +13 −0 test/integration/eslint/custom-eslint-config/pages/index.js
- +14 −0 test/integration/eslint/custom-next-config/.eslintrc.json
- +6 −0 test/integration/eslint/custom-next-config/next.config.js
- +13 −0 test/integration/eslint/custom-next-config/pages/index.js
- +36 −0 test/integration/eslint/pkg-json-eslint-config/package.json
- +13 −0 test/integration/eslint/pkg-json-eslint-config/pages/index.js
- +37 −0 test/integration/eslint/test/custom-eslint-config.test.js
- +55 −0 test/integration/eslint/test/custom-next-config.test.js
- +44 −0 test/integration/eslint/test/pkg-json-eslint-config.test.js
- +1 −1 test/integration/index-index/pages/index/index.js
- +1 −1 test/integration/index-index/pages/index/index/index.js
- +1 −1 test/integration/index-index/pages/index/project/index.js
- +1 −1 test/integration/index-index/pages/index/user.js
- +5 −0 test/integration/production-build-dir/next.config.js
- +2 −1 test/integration/production/pages/error-in-ssr-render.js
- +2 −2 test/integration/tsconfig-verifier/pages/index.tsx
- +3 −0 test/integration/webpack-require-hook/next.config.js
- +202 −6 yarn.lock
| @@ -0,0 +1,70 @@ | ||
| --- | ||
| description: Learn how to opt-in and out of ESLint during development mode and production builds. | ||
| --- | ||
|
|
||
| # ESLint Warnings and Errors | ||
|
|
||
| ## During builds | ||
|
|
||
| Next.js fails your **production build** (`next build`) when ESLint errors are present in your | ||
| project. | ||
|
|
||
| If you'd like Next.js to dangerously produce production code even when your application has errors, | ||
| you can disable ESLint running during the build process. | ||
|
|
||
| > It's recommended to run ESLint as part of the production build process to ensure your application | ||
| > is resilient against runtime issues. | ||
| Open `next.config.js` and disable the `build` option in the `eslint` config: | ||
|
|
||
| ```js | ||
| module.exports = { | ||
| eslint: { | ||
| // !! WARN !! | ||
| // Dangerously allow production builds to successfully complete even if | ||
| // your project has ESLint errors. | ||
| // !! WARN !! | ||
| build: false, | ||
| }, | ||
| } | ||
| ``` | ||
|
|
||
| ## During development | ||
|
|
||
| By default, Next.js does not run ESLint during **development** (`next dev`). | ||
|
|
||
| If you would like Next.js to lint files separately in development mode, you can enable it in your | ||
| configuration. | ||
|
|
||
| > Enabling ESLint during development mode will slow down how fast pages are compiled. Until this is | ||
| > optimized, we recommend that you [integrate ESLint in your code | ||
| > editor](https://eslint.org/docs/user-guide/integrations#editors). | ||
| Open `next.config.js` and enable the `dev` option in the `eslint` config: | ||
|
|
||
| ```js | ||
| module.exports = { | ||
| eslint: { | ||
| // !! WARN !! | ||
| // This can slow down how long pages take to compile during development | ||
| // !! WARN !! | ||
| dev: true, | ||
| }, | ||
| } | ||
| ``` | ||
|
|
||
| ## Related | ||
|
|
||
| <div class="card"> | ||
| <a href="/docs/api-reference/next.config.js/introduction.md"> | ||
| <b>Introduction to next.config.js:</b> | ||
| <small>Learn more about the configuration file used by Next.js.</small> | ||
| </a> | ||
| </div> | ||
|
|
||
| <div class="card"> | ||
| <a href="/docs/basic-features/eslint.md"> | ||
| <b>ESLint:</b> | ||
| <small>Learn more about how to use ESLint in Next.js.</small> | ||
| </a> | ||
| </div> |
| @@ -0,0 +1,50 @@ | ||
| --- | ||
| description: Next.js uses ESLint to find and resolve issues affecting the user or developer | ||
| experience. | ||
| --- | ||
|
|
||
| # ESLint | ||
|
|
||
| Next.js uses [ESLint](https://eslint.org/) to find and resolve issues affecting the user or | ||
| developer experience. A minimal set of Next.js rules are provided by default, but can be extended by | ||
| adding an `.eslintrc` file to your project. | ||
|
|
||
| ## Default configuration | ||
|
|
||
| By default, Next.js provides a set of [recommended ESLint | ||
| rules](https://github.com/vercel/next.js/blob/canary/packages/eslint-plugin-next/lib/index.js#L10-L18) | ||
| that are automatically linted against as part of `next build`. If you would like to control which | ||
| ESLint rules are checked during builds, you will need to add an `.eslintrc` file to the root of your | ||
| project. | ||
|
|
||
| Here's an example of an `.eslintrc.json` file: | ||
|
|
||
| ```json | ||
| { | ||
| "extends": ["plugin:@next/next/recommended"], | ||
| "parser": "@babel/eslint-parser", | ||
| "parserOptions": { | ||
| "requireConfigFile": false, | ||
| "sourceType": "module", | ||
| "babelOptions": { | ||
| "presets": ["next/babel"] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| - Extending the original base of rules (`plugin:@next/next/recommended`) is highly recommended to | ||
| catch and fix significant Next.js issues in your application | ||
| - Including `@babel/eslint-parser` with the `next/babel` preset ensures that all language features | ||
| supported by Next.js will also be supported by ESLint. Although `@babel/eslint-parser` can parse | ||
| TypeScript, consider using | ||
| [`@typescript-eslint/parser`](https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/parser) | ||
| if you have TypeScript enabled in your application to check for type-specific linting rules. | ||
|
|
||
| > If you add an `.eslintrc` file to your application and don't include | ||
| > `plugin:@next/next/recommended`in the config, its rules will not be checked during development or | ||
| > production builds. This is **not recommended**. | ||
| If you want to enable ESLint to run during development, or disable it for production builds; refer | ||
| to the documentation for [ESLint Warnings and | ||
| Errors](/docs/api-reference/next.config.js/eslint-warnings-errors.md). |
| @@ -0,0 +1,68 @@ | ||
| import { ESLint, Linter } from 'eslint' | ||
|
|
||
| import chalk from 'chalk' | ||
| import path from 'path' | ||
|
|
||
| export enum MessageSeverity { | ||
| Warning = 1, | ||
| Error = 2, | ||
| } | ||
|
|
||
| function formatMessage( | ||
| dir: string, | ||
| messages: Linter.LintMessage[], | ||
| filePath: string | ||
| ): string | void { | ||
| let fileName = path.posix.normalize( | ||
| path.relative(dir, filePath).replace(/\\/g, '/') | ||
| ) | ||
|
|
||
| if (!fileName.startsWith('.')) { | ||
| fileName = './' + fileName | ||
| } | ||
|
|
||
| let output = '\n' + chalk.cyan(fileName) | ||
|
|
||
| for (let i = 0; i < messages.length; i++) { | ||
| const { message, severity, line, column, ruleId } = messages[i] | ||
|
|
||
| output = output + '\n' | ||
|
|
||
| if (line && column) { | ||
| output = | ||
| output + | ||
| chalk.yellow(line.toString()) + | ||
| ':' + | ||
| chalk.yellow(column.toString()) + | ||
| ' ' | ||
| } | ||
|
|
||
| if (severity === MessageSeverity.Warning) { | ||
| output += chalk.yellow.bold('Warning') + ': ' | ||
| } else { | ||
| output += chalk.red.bold('Error') + ': ' | ||
| } | ||
|
|
||
| output += message | ||
|
|
||
| if (ruleId) { | ||
| output += ' ' + chalk.gray.bold(ruleId) | ||
| } | ||
| } | ||
|
|
||
| return output | ||
| } | ||
|
|
||
| export function formatResults( | ||
| baseDir: string, | ||
| results: ESLint.LintResult[] | ||
| ): string { | ||
| return ( | ||
| results | ||
| .filter(({ messages }) => messages?.length) | ||
| .map(({ messages, filePath }) => | ||
| formatMessage(baseDir, messages, filePath) | ||
| ) | ||
| .join('\n') + '\n' | ||
| ) | ||
| } |
Oops, something went wrong.