Skip to content

Commit c021c2f

Browse files
JoshuaKGoldbergeps1lon
authored andcommitted
feat: enable @typescript-eslint/recommended in create-next-app --typescript (#52845)
Co-authored-by: eps1lon <[email protected]>
1 parent 442807b commit c021c2f

File tree

16 files changed

+296
-55
lines changed

16 files changed

+296
-55
lines changed

docs/02-app/01-building-your-application/07-configuring/02-eslint.mdx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,19 @@ The `next/core-web-vitals` rule set is enabled when `next lint` is run for the f
202202

203203
> The `next/core-web-vitals` entry point is automatically included for new applications built with [Create Next App](/docs/app/api-reference/create-next-app).
204204
205+
### TypeScript
206+
207+
In addition to the Next.js ESLint rules, `create-next-app --typescript` will also add TypeScript-specific lint rules with `next/typescript` to your config:
208+
209+
```json filename=".eslintrc.json"
210+
{
211+
"extends": ["next/core-web-vitals", "next/typescript"]
212+
}
213+
```
214+
215+
Those rules are based on [`plugin:@typescript-eslint/recommended`](https://typescript-eslint.io/linting/configs#recommended).
216+
See [typescript-eslint > Configs](https://typescript-eslint.io/linting/configs) for more details.
217+
205218
## Usage With Other Tools
206219

207220
### Prettier

examples/with-temporal/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
"@types/node-fetch": "^3.0.3",
2525
"@types/react": "^17.0.2",
2626
"@types/react-dom": "^17.0.1",
27-
"@typescript-eslint/eslint-plugin": "^5.3.0",
28-
"@typescript-eslint/parser": "^5.3.0",
27+
"@typescript-eslint/eslint-plugin": "^6.1.0",
28+
"@typescript-eslint/parser": "^6.1.0",
2929
"cross-env": "^7.0.3",
3030
"nodemon": "^2.0.12",
3131
"ts-node": "^10.2.1",
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"extends": "next/core-web-vitals"
2+
"extends": ["next/core-web-vitals", "next/typescript"]
33
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"extends": "next/core-web-vitals"
2+
"extends": ["next/core-web-vitals", "next/typescript"]
33
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"extends": "next/core-web-vitals"
2+
"extends": ["next/core-web-vitals", "next/typescript"]
33
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"extends": "next/core-web-vitals"
2+
"extends": ["next/core-web-vitals", "next/typescript"]
33
}

packages/eslint-config-next/index.js

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,13 @@ sortedPaths.push(...keptPaths)
3030

3131
const hookPropertyMap = new Map(
3232
[
33-
['eslint-plugin-import', 'eslint-plugin-import'],
34-
['eslint-plugin-react', 'eslint-plugin-react'],
35-
['eslint-plugin-jsx-a11y', 'eslint-plugin-jsx-a11y'],
36-
].map(([request, replacement]) => [
33+
'@typescript-eslint/eslint-plugin',
34+
'eslint-plugin-import',
35+
'eslint-plugin-react',
36+
'eslint-plugin-jsx-a11y',
37+
].map((request) => [
3738
request,
38-
require.resolve(replacement, { paths: sortedPaths }),
39+
require.resolve(request, { paths: sortedPaths }),
3940
])
4041
)
4142

@@ -96,10 +97,6 @@ module.exports = {
9697
parser: '@typescript-eslint/parser',
9798
parserOptions: {
9899
sourceType: 'module',
99-
ecmaFeatures: {
100-
jsx: true,
101-
},
102-
warnOnUnsupportedTypeScriptVersion: true,
103100
},
104101
},
105102
],

packages/eslint-config-next/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"dependencies": {
1313
"@next/eslint-plugin-next": "14.2.7",
1414
"@rushstack/eslint-patch": "^1.3.3",
15+
"@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0",
1516
"@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0",
1617
"eslint-import-resolver-node": "^0.3.6",
1718
"eslint-import-resolver-typescript": "^3.5.2",
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
extends: ['plugin:@typescript-eslint/recommended'],
3+
}

packages/next/src/lib/constants.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -78,26 +78,6 @@ export const SSG_FALLBACK_EXPORT_ERROR = `Pages with \`fallback\` enabled in \`g
7878

7979
export const ESLINT_DEFAULT_DIRS = ['app', 'pages', 'components', 'lib', 'src']
8080

81-
export const ESLINT_PROMPT_VALUES = [
82-
{
83-
title: 'Strict',
84-
recommended: true,
85-
config: {
86-
extends: 'next/core-web-vitals',
87-
},
88-
},
89-
{
90-
title: 'Base',
91-
config: {
92-
extends: 'next',
93-
},
94-
},
95-
{
96-
title: 'Cancel',
97-
config: null,
98-
},
99-
]
100-
10181
export const SERVER_RUNTIME: Record<string, ServerRuntime> = {
10282
edge: 'edge',
10383
experimentalEdge: 'experimental-edge',
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import findUp from 'next/dist/compiled/find-up'
2+
3+
export const getESLintStrictValue = async (cwd: string) => {
4+
const tsConfigLocation = await findUp('tsconfig.json', { cwd })
5+
const hasTSConfig = tsConfigLocation !== undefined
6+
7+
return {
8+
title: 'Strict',
9+
recommended: true,
10+
config: {
11+
extends: hasTSConfig
12+
? ['next/core-web-vitals', 'next/typescript']
13+
: 'next/core-web-vitals',
14+
},
15+
}
16+
}
17+
18+
export const getESLintPromptValues = async (cwd: string) => {
19+
return [
20+
await getESLintStrictValue(cwd),
21+
{
22+
title: 'Base',
23+
config: {
24+
extends: 'next',
25+
},
26+
},
27+
{
28+
title: 'Cancel',
29+
config: null,
30+
},
31+
]
32+
}

packages/next/src/lib/eslint/runLintCheck.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { writeDefaultConfig } from './writeDefaultConfig'
1212
import { hasEslintConfiguration } from './hasEslintConfiguration'
1313
import { writeOutputFile } from './writeOutputFile'
1414

15-
import { ESLINT_PROMPT_VALUES } from '../constants'
1615
import { findPagesDir } from '../find-pages-dir'
1716
import { installDependencies } from '../install-dependencies'
1817
import { hasNecessaryDependencies } from '../has-necessary-dependencies'
@@ -21,6 +20,10 @@ import * as Log from '../../build/output/log'
2120
import type { EventLintCheckCompleted } from '../../telemetry/events/build'
2221
import isError, { getProperError } from '../is-error'
2322
import { getPkgManager } from '../helpers/get-pkg-manager'
23+
import {
24+
getESLintStrictValue,
25+
getESLintPromptValues,
26+
} from './getESLintPromptValues'
2427

2528
type Config = {
2629
plugins: string[]
@@ -44,7 +47,7 @@ const requiredPackages = [
4447
},
4548
]
4649

47-
async function cliPrompt(): Promise<{ config?: any }> {
50+
async function cliPrompt(cwd: string): Promise<{ config?: any }> {
4851
console.log(
4952
bold(
5053
`${cyan(
@@ -58,7 +61,7 @@ async function cliPrompt(): Promise<{ config?: any }> {
5861
await Promise.resolve(require('next/dist/compiled/cli-select'))
5962
).default
6063
const { value } = await cliSelect({
61-
values: ESLINT_PROMPT_VALUES,
64+
values: await getESLintPromptValues(cwd),
6265
valueRenderer: (
6366
{
6467
title,
@@ -355,10 +358,8 @@ export async function runLintCheck(
355358
} else {
356359
// Ask user what config they would like to start with for first time "next lint" setup
357360
const { config: selectedConfig } = strict
358-
? ESLINT_PROMPT_VALUES.find(
359-
(opt: { title: string }) => opt.title === 'Strict'
360-
)!
361-
: await cliPrompt()
361+
? await getESLintStrictValue(baseDir)
362+
: await cliPrompt(baseDir)
362363

363364
if (selectedConfig == null) {
364365
// Show a warning if no option is selected in prompt

pnpm-lock.yaml

Lines changed: 90 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/integration/eslint/test/next-lint.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,9 @@ describe('Next Lint', () => {
120120
'We created the .eslintrc.json file for you and included your selected configuration'
121121
)
122122
expect(eslintrcJson).toMatchObject({ extends: 'next/core-web-vitals' })
123+
})
123124

125+
test('creates .eslintrc.json file with a default app router configuration', async () => {
124126
// App Router
125127
const { stdout: appStdout, eslintrcJson: appEslintrcJson } =
126128
await nextLintTemp(null, true)

0 commit comments

Comments
 (0)