Skip to content

Commit 806e3c2

Browse files
michaelfaithljharb
authored andcommitted
[New] add support for Flat Config
This change adds support for ESLint's new Flat config system. It maintains backwards compatibility with `eslintrc`-style configs as well. To achieve this, we're now dynamically creating flat configs on a new `flatConfigs` export. Usage ```js import importPlugin from 'eslint-plugin-import'; import js from '@eslint/js'; import tsParser from '@typescript-eslint/parser'; export default [ js.configs.recommended, importPlugin.flatConfigs.recommended, importPlugin.flatConfigs.react, importPlugin.flatConfigs.typescript, { files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'], languageOptions: { parser: tsParser, ecmaVersion: 'latest', sourceType: 'module', }, ignores: ['eslint.config.js'], rules: { 'no-unused-vars': 'off', 'import/no-dynamic-require': 'warn', 'import/no-nodejs-modules': 'warn', }, }, ]; ```
1 parent b340f1f commit 806e3c2

29 files changed

+541
-49
lines changed

.editorconfig

+1
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ insert_final_newline = true
77
indent_style = space
88
indent_size = 2
99
end_of_line = lf
10+
quote_type = single

.eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ tests/files/with-syntax-error
77
tests/files/just-json-files/invalid.json
88
tests/files/typescript-d-ts/
99
resolvers/webpack/test/files
10+
examples
1011
# we want to ignore "tests/files" here, but unfortunately doing so would
1112
# interfere with unit test and fail it for some reason.
1213
# tests/files

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
1010
- [`dynamic-import-chunkname`]: add `allowEmpty` option to allow empty leading comments ([#2942], thanks [@JiangWeixian])
1111
- [`dynamic-import-chunkname`]: Allow empty chunk name when webpackMode: 'eager' is set; add suggestions to remove name in eager mode ([#3004], thanks [@amsardesai])
1212
- [`no-unused-modules`]: Add `ignoreUnusedTypeExports` option ([#3011], thanks [@silverwind])
13+
- add support for Flat Config ([#3018], thanks [@michaelfaith])
1314

1415
### Fixed
1516
- [`no-extraneous-dependencies`]: allow wrong path ([#3012], thanks [@chabb])
@@ -1125,6 +1126,7 @@ for info on changes for earlier releases.
11251126

11261127
[#3036]: https://github.com/import-js/eslint-plugin-import/pull/3036
11271128
[#3033]: https://github.com/import-js/eslint-plugin-import/pull/3033
1129+
[#3018]: https://github.com/import-js/eslint-plugin-import/pull/3018
11281130
[#3012]: https://github.com/import-js/eslint-plugin-import/pull/3012
11291131
[#3011]: https://github.com/import-js/eslint-plugin-import/pull/3011
11301132
[#3004]: https://github.com/import-js/eslint-plugin-import/pull/3004
@@ -1874,6 +1876,7 @@ for info on changes for earlier releases.
18741876
[@meowtec]: https://github.com/meowtec
18751877
[@mgwalker]: https://github.com/mgwalker
18761878
[@mhmadhamster]: https://github.com/MhMadHamster
1879+
[@michaelfaith]: https://github.com/michaelfaith
18771880
[@MikeyBeLike]: https://github.com/MikeyBeLike
18781881
[@minervabot]: https://github.com/minervabot
18791882
[@mpint]: https://github.com/mpint

README.md

+30-1
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ The maintainers of `eslint-plugin-import` and thousands of other packages are wo
106106
npm install eslint-plugin-import --save-dev
107107
```
108108

109+
### Config - Legacy (`.eslintrc`)
110+
109111
All rules are off by default. However, you may configure them manually
110112
in your `.eslintrc.(yml|json|js)`, or extend one of the canned configs:
111113

@@ -123,14 +125,41 @@ plugins:
123125
- import
124126

125127
rules:
126-
import/no-unresolved: [2, {commonjs: true, amd: true}]
128+
import/no-unresolved: [2, { commonjs: true, amd: true }]
127129
import/named: 2
128130
import/namespace: 2
129131
import/default: 2
130132
import/export: 2
131133
# etc...
132134
```
133135

136+
### Config - Flat (`eslint.config.js`)
137+
138+
All rules are off by default. However, you may configure them manually
139+
in your `eslint.config.(js|cjs|mjs)`, or extend one of the canned configs:
140+
141+
```js
142+
import importPlugin from 'eslint-plugin-import';
143+
import js from '@eslint/js';
144+
145+
export default [
146+
js.configs.recommended,
147+
importPlugin.flatConfigs.recommended,
148+
{
149+
files: ['**/*.{js,mjs,cjs}'],
150+
languageOptions: {
151+
ecmaVersion: 'latest',
152+
sourceType: 'module',
153+
},
154+
rules: {
155+
'no-unused-vars': 'off',
156+
'import/no-dynamic-require': 'warn',
157+
'import/no-nodejs-modules': 'warn',
158+
},
159+
},
160+
];
161+
```
162+
134163
## TypeScript
135164

136165
You may use the following snippet or assemble your own config using the granular settings described below it.

config/flat/errors.js

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* unopinionated config. just the things that are necessarily runtime errors
3+
* waiting to happen.
4+
* @type {Object}
5+
*/
6+
module.exports = {
7+
rules: {
8+
'import/no-unresolved': 2,
9+
'import/named': 2,
10+
'import/namespace': 2,
11+
'import/default': 2,
12+
'import/export': 2,
13+
},
14+
};

config/flat/react.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* Adds `.jsx` as an extension, and enables JSX parsing.
3+
*
4+
* Even if _you_ aren't using JSX (or .jsx) directly, if your dependencies
5+
* define jsnext:main and have JSX internally, you may run into problems
6+
* if you don't enable these settings at the top level.
7+
*/
8+
module.exports = {
9+
settings: {
10+
'import/extensions': ['.js', '.jsx', '.mjs', '.cjs'],
11+
},
12+
languageOptions: {
13+
parserOptions: {
14+
ecmaFeatures: {
15+
jsx: true,
16+
},
17+
},
18+
},
19+
};

config/flat/recommended.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* The basics.
3+
* @type {Object}
4+
*/
5+
module.exports = {
6+
rules: {
7+
// analysis/correctness
8+
'import/no-unresolved': 'error',
9+
'import/named': 'error',
10+
'import/namespace': 'error',
11+
'import/default': 'error',
12+
'import/export': 'error',
13+
14+
// red flags (thus, warnings)
15+
'import/no-named-as-default': 'warn',
16+
'import/no-named-as-default-member': 'warn',
17+
'import/no-duplicates': 'warn',
18+
},
19+
20+
// need all these for parsing dependencies (even if _your_ code doesn't need
21+
// all of them)
22+
languageOptions: {
23+
ecmaVersion: 2018,
24+
sourceType: 'module',
25+
},
26+
};

config/flat/warnings.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* more opinionated config.
3+
* @type {Object}
4+
*/
5+
module.exports = {
6+
rules: {
7+
'import/no-named-as-default': 1,
8+
'import/no-named-as-default-member': 1,
9+
'import/no-duplicates': 1,
10+
},
11+
};

config/react.js

-2
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,11 @@
66
* if you don't enable these settings at the top level.
77
*/
88
module.exports = {
9-
109
settings: {
1110
'import/extensions': ['.js', '.jsx'],
1211
},
1312

1413
parserOptions: {
1514
ecmaFeatures: { jsx: true },
1615
},
17-
1816
};

config/typescript.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// `.ts`/`.tsx`/`.js`/`.jsx` implementation.
1010
const typeScriptExtensions = ['.ts', '.cts', '.mts', '.tsx'];
1111

12-
const allExtensions = [...typeScriptExtensions, '.js', '.jsx'];
12+
const allExtensions = [...typeScriptExtensions, '.js', '.jsx', '.mjs', '.cjs'];
1313

1414
module.exports = {
1515
settings: {

examples/flat/eslint.config.mjs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import importPlugin from 'eslint-plugin-import';
2+
import js from '@eslint/js';
3+
import tsParser from '@typescript-eslint/parser';
4+
5+
export default [
6+
js.configs.recommended,
7+
importPlugin.flatConfigs.recommended,
8+
importPlugin.flatConfigs.react,
9+
importPlugin.flatConfigs.typescript,
10+
{
11+
files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
12+
languageOptions: {
13+
parser: tsParser,
14+
ecmaVersion: 'latest',
15+
sourceType: 'module',
16+
},
17+
ignores: ['eslint.config.mjs', '**/exports-unused.ts'],
18+
rules: {
19+
'no-unused-vars': 'off',
20+
'import/no-dynamic-require': 'warn',
21+
'import/no-nodejs-modules': 'warn',
22+
'import/no-unused-modules': ['warn', { unusedExports: true }],
23+
},
24+
},
25+
];

examples/flat/package.json

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "flat",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"scripts": {
6+
"lint": "cross-env ESLINT_USE_FLAT_CONFIG=true eslint src --report-unused-disable-directives"
7+
},
8+
"devDependencies": {
9+
"@eslint/js": "^9.5.0",
10+
"@types/node": "^20.14.5",
11+
"@typescript-eslint/parser": "^7.13.1",
12+
"cross-env": "^7.0.3",
13+
"eslint": "^8.57.0",
14+
"eslint-plugin-import": "file:../..",
15+
"typescript": "^5.4.5"
16+
}
17+
}

examples/flat/src/exports-unused.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export type ScalarType = string | number;
2+
export type ObjType = {
3+
a: ScalarType;
4+
b: ScalarType;
5+
};
6+
7+
export const a = 13;
8+
export const b = 18;
9+
10+
const defaultExport: ObjType = { a, b };
11+
12+
export default defaultExport;

examples/flat/src/exports.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export type ScalarType = string | number;
2+
export type ObjType = {
3+
a: ScalarType;
4+
b: ScalarType;
5+
};
6+
7+
export const a = 13;
8+
export const b = 18;
9+
10+
const defaultExport: ObjType = { a, b };
11+
12+
export default defaultExport;

examples/flat/src/imports.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//import c from './exports';
2+
import { a, b } from './exports';
3+
import type { ScalarType, ObjType } from './exports';
4+
5+
import path from 'path';
6+
import fs from 'node:fs';
7+
import console from 'console';

examples/flat/src/jsx.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const Components = () => {
2+
return <></>;
3+
};

examples/flat/tsconfig.json

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"compilerOptions": {
3+
"jsx": "react-jsx",
4+
"lib": ["ESNext"],
5+
"target": "ESNext",
6+
"module": "ESNext",
7+
"rootDir": "./",
8+
"moduleResolution": "Bundler",
9+
"esModuleInterop": true,
10+
"forceConsistentCasingInFileNames": true,
11+
"strict": true,
12+
"skipLibCheck": true
13+
}
14+
}

examples/legacy/.eslintrc.cjs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
module.exports = {
2+
root: true,
3+
env: { es2022: true },
4+
extends: [
5+
'eslint:recommended',
6+
'plugin:import/recommended',
7+
'plugin:import/react',
8+
'plugin:import/typescript',
9+
],
10+
settings: {},
11+
ignorePatterns: ['.eslintrc.cjs', '**/exports-unused.ts'],
12+
parser: '@typescript-eslint/parser',
13+
parserOptions: {
14+
ecmaVersion: 'latest',
15+
sourceType: 'module',
16+
},
17+
plugins: ['import'],
18+
rules: {
19+
'no-unused-vars': 'off',
20+
'import/no-dynamic-require': 'warn',
21+
'import/no-nodejs-modules': 'warn',
22+
'import/no-unused-modules': ['warn', { unusedExports: true }],
23+
},
24+
};

examples/legacy/package.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "legacy",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"scripts": {
6+
"lint": "cross-env ESLINT_USE_FLAT_CONFIG=false eslint src --ext js,jsx,ts,tsx --report-unused-disable-directives"
7+
},
8+
"devDependencies": {
9+
"@types/node": "^20.14.5",
10+
"@typescript-eslint/parser": "^7.13.1",
11+
"cross-env": "^7.0.3",
12+
"eslint": "^8.57.0",
13+
"eslint-plugin-import": "file:../..",
14+
"typescript": "^5.4.5"
15+
}
16+
}

examples/legacy/src/exports-unused.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export type ScalarType = string | number;
2+
export type ObjType = {
3+
a: ScalarType;
4+
b: ScalarType;
5+
};
6+
7+
export const a = 13;
8+
export const b = 18;
9+
10+
const defaultExport: ObjType = { a, b };
11+
12+
export default defaultExport;

examples/legacy/src/exports.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export type ScalarType = string | number;
2+
export type ObjType = {
3+
a: ScalarType;
4+
b: ScalarType;
5+
};
6+
7+
export const a = 13;
8+
export const b = 18;
9+
10+
const defaultExport: ObjType = { a, b };
11+
12+
export default defaultExport;

examples/legacy/src/imports.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//import c from './exports';
2+
import { a, b } from './exports';
3+
import type { ScalarType, ObjType } from './exports';
4+
5+
import path from 'path';
6+
import fs from 'node:fs';
7+
import console from 'console';

examples/legacy/src/jsx.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const Components = () => {
2+
return <></>;
3+
};

examples/legacy/tsconfig.json

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"compilerOptions": {
3+
"jsx": "react-jsx",
4+
"lib": ["ESNext"],
5+
"target": "ESNext",
6+
"module": "ESNext",
7+
"rootDir": "./",
8+
"moduleResolution": "Bundler",
9+
"esModuleInterop": true,
10+
"forceConsistentCasingInFileNames": true,
11+
"strict": true,
12+
"skipLibCheck": true
13+
}
14+
}

package.json

+3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
"test": "npm run tests-only",
3232
"test-compiled": "npm run prepublish && BABEL_ENV=testCompiled mocha --compilers js:babel-register tests/src",
3333
"test-all": "node --require babel-register ./scripts/testAll",
34+
"test-examples": "npm run build && npm run test-example:legacy && npm run test-example:flat",
35+
"test-example:legacy": "cd examples/legacy && npm install && npm run lint",
36+
"test-example:flat": "cd examples/flat && npm install && npm run lint",
3437
"prepublishOnly": "safe-publish-latest && npm run build",
3538
"prepublish": "not-in-publish || npm run prepublishOnly",
3639
"preupdate:eslint-docs": "npm run build",

0 commit comments

Comments
 (0)