@@ -135,7 +135,25 @@ async function lint(
135
135
136
136
const mod = await Promise . resolve ( require ( deps . resolved . get ( 'eslint' ) ! ) )
137
137
138
- const { ESLint } = mod
138
+ const useFlatConfig =
139
+ // If V9 config was found, use flat config, or else use legacy.
140
+ eslintrcFile ?. startsWith ( 'eslint.config.' )
141
+
142
+ let ESLint
143
+ // loadESLint is >= 8.57.0
144
+ // PR https://github.com/eslint/eslint/pull/18098
145
+ // Release https://github.com/eslint/eslint/releases/tag/v8.57.0
146
+ if ( 'loadESLint' in mod ) {
147
+ // By default, configType is `flat`. If `useFlatConfig` is false, the return value is `LegacyESLint`.
148
+ // https://github.com/eslint/eslint/blob/1def4cdfab1f067c5089df8b36242cdf912b0eb6/lib/types/index.d.ts#L1609-L1613
149
+ ESLint = await mod . loadESLint ( {
150
+ useFlatConfig,
151
+ } )
152
+ } else {
153
+ // eslint < 8.57.0, use legacy ESLint
154
+ ESLint = mod . ESLint
155
+ }
156
+
139
157
let eslintVersion = ESLint ?. version ?? mod . CLIEngine ?. version
140
158
141
159
if ( ! eslintVersion || semver . lt ( eslintVersion , '7.0.0' ) ) {
@@ -155,6 +173,23 @@ async function lint(
155
173
...eslintOptions ,
156
174
}
157
175
176
+ if ( semver . gte ( eslintVersion , '9.0.0' ) && useFlatConfig ) {
177
+ for ( const option of [
178
+ 'useEslintrc' ,
179
+ 'extensions' ,
180
+ 'ignorePath' ,
181
+ 'reportUnusedDisableDirectives' ,
182
+ 'resolvePluginsRelativeTo' ,
183
+ 'rulePaths' ,
184
+ 'inlineConfig' ,
185
+ 'maxWarnings' ,
186
+ ] ) {
187
+ if ( option in options ) {
188
+ delete options [ option ]
189
+ }
190
+ }
191
+ }
192
+
158
193
let eslint = new ESLint ( options )
159
194
160
195
let nextEslintPluginIsEnabled = false
@@ -163,10 +198,20 @@ async function lint(
163
198
for ( const configFile of [ eslintrcFile , pkgJsonPath ] ) {
164
199
if ( ! configFile ) continue
165
200
166
- const completeConfig : Config =
201
+ const completeConfig : Config | undefined =
167
202
await eslint . calculateConfigForFile ( configFile )
203
+ if ( ! completeConfig ) continue
204
+
205
+ const plugins = completeConfig . plugins
206
+
207
+ const hasNextPlugin =
208
+ // in ESLint < 9, `plugins` value is string[]
209
+ Array . isArray ( plugins )
210
+ ? plugins . includes ( '@next/next' )
211
+ : // in ESLint >= 9, `plugins` value is Record<string, unknown>
212
+ '@next/next' in plugins
168
213
169
- if ( completeConfig . plugins ?. includes ( '@next/next' ) ) {
214
+ if ( hasNextPlugin ) {
170
215
nextEslintPluginIsEnabled = true
171
216
for ( const [ name , [ severity ] ] of Object . entries ( completeConfig . rules ) ) {
172
217
if ( ! name . startsWith ( '@next/next/' ) ) {
@@ -309,6 +354,17 @@ export async function runLintCheck(
309
354
const eslintrcFile =
310
355
( await findUp (
311
356
[
357
+ // eslint v9
358
+ 'eslint.config.js' ,
359
+ 'eslint.config.mjs' ,
360
+ 'eslint.config.cjs' ,
361
+ // TODO(jiwon): Support when it's stable.
362
+ // TS extensions are experimental and requires to install another package `jiti`.
363
+ // https://eslint.org/docs/latest/use/configure/configuration-files#typescript-configuration-files
364
+ // 'eslint.config.ts',
365
+ // 'eslint.config.mts',
366
+ // 'eslint.config.cts',
367
+ // eslint <= v8
312
368
'.eslintrc.js' ,
313
369
'.eslintrc.cjs' ,
314
370
'.eslintrc.yaml' ,
0 commit comments