Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

next lint + ESLint in Create Next App #25064

Merged
merged 6 commits into from
Jun 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion docs/api-reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Usage
$ next <command>

Available commands
build, start, export, dev, telemetry
build, start, export, dev, lint, telemetry

Options
--version, -v Version number
Expand Down Expand Up @@ -84,6 +84,16 @@ The application will start at `http://localhost:3000` by default. The default po
npx next start -p 4000
```

## Lint

`next lint` runs ESLint for all files in the `pages` directory and provides a guided setup to install any required dependencies if ESLint is not already configured in your application.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we change from just pages, let's update here too 👍


You can also run ESLint on other directories with the `--dir` flag:

```bash
next lint --dir components
```

## Telemetry

Next.js collects **completely anonymous** telemetry data about general usage.
Expand Down
119 changes: 119 additions & 0 deletions docs/basic-features/eslint.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
---
description: Next.js supports ESLint by default. You can get started with ESLint in Next.js here.
---

# ESLint

Since version **11.0.0**, Next.js provides an integrated [ESLint](https://eslint.org/) experience out of the box. To get started, run `next lint`:

```bash
next lint
```

If you don't already have ESLint configured in your application, you will be guided through the installation of any required packages.

```bash
next lint

# You'll see instructions like these:
#
# Please install eslint and eslint-config-next by running:
#
# yarn add --dev eslint eslint-config-next
#
# ...
```

If no ESLint configuration is present, Next.js will create an `.eslintrc` file in the root of your project and automatically configure it with the base configuration:

```
{
"extends": "next"
}
```

Now you can run `next lint` every time you want to run ESLint to catch errors

> The default base configuration (`"extends": "next"`) can be updated at any time and will only be included if no ESLint configuration is present.

We recommend using an appropriate [integration](https://eslint.org/docs/user-guide/integrations#editors) to view warnings and errors directly in your code editor during development.

## Linting During Builds

Once ESLint has been set up, it will automatically run during every build (`next build`). Errors will fail the build while warnings will not.

If you do not want ESLint to run as a build step, it can be disabled using the `--no-lint` flag:

```bash
next build --no-lint
Copy link
Member

@timneutkens timneutkens Jun 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a next.config.js flag in my opinion, similar to TypeScript

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed 👍

```

This is not recommended unless you have configured ESLint to run in a separate part of your workflow (for example, in CI or a pre-commit hook).

## Linting Custom Directories

By default, Next.js will only run ESLint for all files in the `pages/` directory. However, you can specify other custom directories to run by using the `--dir` flag in `next lint`:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default should definitely be pages, components, and lib as those are the most common setup

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed 👍 and in next.config.js globally.


```bash
next lint --dir components --dir lib
```

## ESLint Plugin

Next.js provides an ESLint plugin, [`eslint-plugin-next`](https://www.npmjs.com/package/@next/eslint-plugin-next), that makes it easier to catch common issues and problems in a Next.js application. The full set of rules can be found in the [package repository](https://github.com/vercel/next.js/tree/master/packages/eslint-plugin-next/lib/rules).
Copy link
Member

@timneutkens timneutkens Jun 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of linking to them we should maintain a list in this document to allow for a description, but for now it's fine to have it linked 👍


## Base Configuration

The Next.js base ESLint configuration is automatically generated when `next lint` is run for the first time:

```
{
"extends": "next"
}
```

This configuration extends recommended rule sets from various Eslint plugins:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ESLint capitalization here is different than the rest of the doc.


- [`eslint-plugin-react`](https://www.npmjs.com/package/eslint-plugin-react)
- [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks)
- [`eslint-plugin-next`](https://www.npmjs.com/package/@next/eslint-plugin-next)

You can see the full details of the shareable configuration in the [`eslint-config-next`](https://www.npmjs.com/package/eslint-config-next) package.

If you would like to modify any rules provided by the supported plugins (`react`, `react-hooks`, `next`), you can directly modify them using the `rules` property:

```
{
"extends": "next",
"rules": {
"react/no-unescaped-entities": "off",
"@next/next/no-page-custom-font": "error",
}
}
```

> **Note**: If you need to also include a separate, custom ESLint configuration, it is highly recommended that `eslint-config-next` is extended last after other configurations. For example:
>
> ```
> {
> "extends": ["eslint:recommended", "next"]
> }
> ```
>
> The `next` configuration already handles setting default values for the `parser`, `plugins` and `settings` properties.
> There is no need to manually re-declare any of these properties unless you need a different configuration for your use case.
> If you include any other shareable configurations, you will need to make sure that these properties are not overwritten or modified.

### Core Web Vitals

A stricter `next/core-web-vitals` entrypoint can also be specified in `.eslintrc`:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"entry point" instead of "entrypoint" IMO.


```
{
"extends": ["next", "next/core-web-vitals"]
}
```

`next/core-web-vitals` updates `eslint-plugin-next` to error on a number of rules that are warnings by default if they affect [Core Web Vitals](https://web.dev/vitals/).

> Both `next` and `next/core-web-vitals` entry points are automatically included for new applications built with [Create Next App](/docs/api-reference/create-next-app.md).
4 changes: 3 additions & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ Open `package.json` and add the following `scripts`:
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
"start": "next start",
"lint": "next lint"
}
```

Expand All @@ -64,6 +65,7 @@ These scripts refer to the different stages of developing an application:
- `dev` - Runs [`next dev`](/docs/api-reference/cli.md#development) which starts Next.js in development mode
- `build` - Runs [`next build`](/docs/api-reference/cli.md#build) which builds the application for production usage
- `start` - Runs [`next start`](/docs/api-reference/cli.md#production) which starts a Next.js production server
- `lint` - Runs [`next lint`](/docs/api-reference/cli.md#lint) which sets up Next.js' built-in ESLint configuration

Next.js is built around the concept of [pages](/docs/basic-features/pages.md). A page is a [React Component](https://reactjs.org/docs/components-and-props.html) exported from a `.js`, `.jsx`, `.ts`, or `.tsx` file in the `pages` directory.

Expand Down
4 changes: 4 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
"title": "Fast Refresh",
"path": "/docs/basic-features/fast-refresh.md"
},
{
"title": "ESLint",
"path": "/docs/basic-features/eslint.md"
},
{
"title": "TypeScript",
"path": "/docs/basic-features/typescript.md"
Expand Down
6 changes: 4 additions & 2 deletions packages/create-next-app/create-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ export async function createApp({
dev: 'next dev',
build: 'next build',
start: 'next start',
lint: 'next lint',
},
}
/**
Expand All @@ -207,7 +208,7 @@ export async function createApp({
/**
* Default devDependencies.
*/
const devDependencies = []
const devDependencies = ['eslint', 'eslint-config-next']
/**
* TypeScript projects will have type definitions and other devDependencies.
*/
Expand Down Expand Up @@ -250,7 +251,8 @@ export async function createApp({
cwd: path.join(__dirname, 'templates', template),
rename: (name) => {
switch (name) {
case 'gitignore': {
case 'gitignore':
case 'eslintrc': {
return '.'.concat(name)
}
// README.md is ignored by webpack-asset-relocator-loader used by ncc:
Expand Down
3 changes: 3 additions & 0 deletions packages/create-next-app/templates/default/eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["next", "next/core-web-vitals"]
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction

export default (req, res) => {
export default function handler(req, res) {
res.status(200).json({ name: 'John Doe' })
}
3 changes: 3 additions & 0 deletions packages/create-next-app/templates/typescript/eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["next", "next/core-web-vitals"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ type Data = {
name: string
}

export default (req: NextApiRequest, res: NextApiResponse<Data>) => {
export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
res.status(200).json({ name: 'John Doe' })
}
8 changes: 8 additions & 0 deletions packages/eslint-config-next/core-web-vitals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
extends: ['.'].map(require.resolve),
rules: {
'@next/next/no-sync-scripts': 2,
'@next/next/no-html-link-for-pages': 2,
'@next/next/no-img-element': 2,
},
}
1 change: 1 addition & 0 deletions packages/eslint-config-next/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module.exports = {
rules: {
'import/no-anonymous-default-export': 'warn',
'react/react-in-jsx-scope': 'off',
'react/prop-types': 'off',
'jsx-a11y/alt-text': [
'warn',
{
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin-next/lib/rules/no-sync-scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module.exports = function (context) {
context.report({
node,
message:
'Synchronous scripts are forbidden. See: https://nextjs.org/docs/messages/no-sync-scripts.',
'External synchronous scripts are forbidden. See: https://nextjs.org/docs/messages/no-sync-scripts.',
})
}
},
Expand Down
1 change: 1 addition & 0 deletions packages/next/bin/next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const commands: { [command: string]: () => Promise<cliCommand> } = {
start: () => import('../cli/next-start').then((i) => i.nextStart),
export: () => import('../cli/next-export').then((i) => i.nextExport),
dev: () => import('../cli/next-dev').then((i) => i.nextDev),
lint: () => import('../cli/next-lint').then((i) => i.nextLint),
telemetry: () => import('../cli/next-telemetry').then((i) => i.nextTelemetry),
}

Expand Down
6 changes: 3 additions & 3 deletions packages/next/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ export default async function build(
dir: string,
conf = null,
reactProductionProfiling = false,
debugOutput = false
debugOutput = false,
runLint = true
): Promise<void> {
const nextBuildSpan = trace('next-build')

Expand Down Expand Up @@ -212,13 +213,12 @@ export default async function build(
typeCheckingSpinner.stopAndPersist()
}

if (config.experimental.eslint) {
if (runLint) {
await nextBuildSpan
.traceChild('verify-and-lint')
.traceAsyncFn(async () => {
await verifyAndLint(
dir,
pagesDir,
config.experimental.cpus,
config.experimental.workerThreads
)
Expand Down
9 changes: 8 additions & 1 deletion packages/next/cli/next-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const nextBuild: cliCommand = (argv) => {
'--help': Boolean,
'--profile': Boolean,
'--debug': Boolean,
'--no-lint': Boolean,
// Aliases
'-h': '--help',
'-d': '--debug',
Expand Down Expand Up @@ -41,13 +42,17 @@ const nextBuild: cliCommand = (argv) => {

Options
--profile Can be used to enable React Production Profiling
--no-lint Disable linting
`,
0
)
}
if (args['--profile']) {
Log.warn('Profiling is enabled. Note: This may affect performance')
}
if (args['--no-lint']) {
Log.warn('Linting is disabled')
}
const dir = resolve(args._[0] || '.')

// Check if the provided directory exists
Expand Down Expand Up @@ -93,7 +98,9 @@ const nextBuild: cliCommand = (argv) => {
}

return preflight()
.then(() => build(dir, null, args['--profile'], args['--debug']))
.then(() =>
build(dir, null, args['--profile'], args['--debug'], !args['--no-lint'])
)
.catch((err) => {
console.error('')
console.error('> Build error occurred')
Expand Down
Loading