Skip to content

Commit cd52e86

Browse files
michaelfaithSukkaW
andauthored
feat: add flat config presets (#122)
Co-authored-by: Sukka <[email protected]>
1 parent f37dc29 commit cd52e86

14 files changed

+253
-23
lines changed

.changeset/great-dodos-dream.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
"eslint-plugin-import-x": minor
3+
---
4+
5+
Add ESLint flat configuration presets. You can access them with:
6+
7+
```ts
8+
import eslintPluginImportX from 'eslint-plugin-import-x';
9+
10+
eslintPluginImportX.flatConfigs.recommended;
11+
eslintPluginImportX.flatConfigs.react;
12+
eslintPluginImportX.flatConfigs.typescript;
13+
eslintPluginImportX.flatConfigs.electron;
14+
```

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"codesandbox:install": "yarn --ignore-engines",
3636
"lint": "run-p lint:*",
3737
"lint:docs": "yarn update:eslint-docs --check",
38-
"lint:es": "ESLINT_USE_FLAT_CONFIG=false eslint . --cache",
38+
"lint:es": "cross-env ESLINT_USE_FLAT_CONFIG=false eslint . --cache",
3939
"lint:tsc": "tsc -p tsconfig.base.json --noEmit",
4040
"prepare": "patch-package",
4141
"release": "changeset publish",

src/config/flat/electron.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { PluginFlatConfig } from '../../types'
2+
3+
/**
4+
* Default settings for Electron applications.
5+
*/
6+
export default {
7+
settings: {
8+
'import-x/core-modules': ['electron'],
9+
},
10+
} satisfies PluginFlatConfig

src/config/flat/errors.ts

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

src/config/flat/react-native.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { PluginFlatBaseConfig } from '../../types'
2+
3+
/**
4+
* adds platform extensions to Node resolver
5+
*/
6+
export default {
7+
settings: {
8+
'import-x/resolver': {
9+
node: {
10+
// Note: will not complain if only _one_ of these files exists.
11+
extensions: ['.js', '.web.js', '.ios.js', '.android.js'],
12+
},
13+
},
14+
},
15+
} satisfies PluginFlatBaseConfig

src/config/flat/react.ts

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

src/config/flat/recommended.ts

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

src/config/flat/stage-0.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { PluginFlatBaseConfig } from '../../types'
2+
3+
/**
4+
* Rules in progress.
5+
*
6+
* Do not expect these to adhere to semver across releases.
7+
*/
8+
export default {
9+
rules: {
10+
'import-x/no-deprecated': 1,
11+
},
12+
} satisfies PluginFlatBaseConfig

src/config/flat/typescript.ts

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import type { PluginFlatBaseConfig } from '../../types'
2+
3+
/**
4+
* This config:
5+
* 1) adds `.jsx`, `.ts`, `.cts`, `.mts`, and `.tsx` as an extension
6+
* 2) enables JSX/TSX parsing
7+
*/
8+
9+
// Omit `.d.ts` because 1) TypeScript compilation already confirms that
10+
// types are resolved, and 2) it would mask an unresolved
11+
// `.ts`/`.tsx`/`.js`/`.jsx` implementation.
12+
const typeScriptExtensions = ['.ts', '.tsx', '.cts', '.mts'] as const
13+
14+
const allExtensions = [
15+
...typeScriptExtensions,
16+
'.js',
17+
'.jsx',
18+
'.cjs',
19+
'.mjs',
20+
] as const
21+
22+
export default {
23+
settings: {
24+
'import-x/extensions': allExtensions,
25+
'import-x/external-module-folders': ['node_modules', 'node_modules/@types'],
26+
'import-x/parsers': {
27+
'@typescript-eslint/parser': [...typeScriptExtensions],
28+
},
29+
'import-x/resolver': {
30+
node: {
31+
extensions: allExtensions,
32+
},
33+
},
34+
},
35+
rules: {
36+
// analysis/correctness
37+
38+
// TypeScript compilation already ensures that named imports exist in the referenced module
39+
'import-x/named': 'off',
40+
},
41+
} satisfies PluginFlatBaseConfig

src/config/flat/warnings.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { PluginFlatBaseConfig } from '../../types'
2+
3+
/**
4+
* more opinionated config.
5+
*/
6+
export default {
7+
rules: {
8+
'import-x/no-named-as-default': 1,
9+
'import-x/no-named-as-default-member': 1,
10+
'import-x/no-duplicates': 1,
11+
},
12+
} satisfies PluginFlatBaseConfig

src/config/typescript.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,22 @@ import type { PluginConfig } from '../types'
99
// Omit `.d.ts` because 1) TypeScript compilation already confirms that
1010
// types are resolved, and 2) it would mask an unresolved
1111
// `.ts`/`.tsx`/`.js`/`.jsx` implementation.
12-
const typeScriptExtensions = ['.ts', '.tsx'] as const
12+
const typeScriptExtensions = ['.ts', '.tsx', '.cts', '.mts'] as const
1313

14-
const allExtensions = [...typeScriptExtensions, '.js', '.jsx'] as const
14+
const allExtensions = [
15+
...typeScriptExtensions,
16+
'.js',
17+
'.jsx',
18+
'.cjs',
19+
'.mjs',
20+
] as const
1521

1622
export = {
1723
settings: {
1824
'import-x/extensions': allExtensions,
1925
'import-x/external-module-folders': ['node_modules', 'node_modules/@types'],
2026
'import-x/parsers': {
21-
'@typescript-eslint/parser': [...typeScriptExtensions, '.cts', '.mts'],
27+
'@typescript-eslint/parser': [...typeScriptExtensions],
2228
},
2329
'import-x/resolver': {
2430
node: {

src/index.ts

+66-18
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
11
import type { TSESLint } from '@typescript-eslint/utils'
22

3-
// rules
3+
import { name, version } from '../package.json'
4+
45
import electron from './config/electron'
56
import errors from './config/errors'
7+
import electronFlat from './config/flat/electron'
8+
import errorsFlat from './config/flat/errors'
9+
import reactFlat from './config/flat/react'
10+
import reactNativeFlat from './config/flat/react-native'
11+
import recommendedFlat from './config/flat/recommended'
12+
import stage0Flat from './config/flat/stage-0'
13+
import typescriptFlat from './config/flat/typescript'
14+
import warningsFlat from './config/flat/warnings'
615
import react from './config/react'
716
import reactNative from './config/react-native'
817
import recommended from './config/recommended'
918
import stage0 from './config/stage-0'
1019
import typescript from './config/typescript'
1120
import warnings from './config/warnings'
21+
22+
// rules
1223
import consistentTypeSpecifierStyle from './rules/consistent-type-specifier-style'
1324
import default_ from './rules/default'
1425
import dynamicImportChunkname from './rules/dynamic-import-chunkname'
@@ -55,23 +66,11 @@ import order from './rules/order'
5566
import preferDefaultExport from './rules/prefer-default-export'
5667
import unambiguous from './rules/unambiguous'
5768
// configs
58-
import type { PluginConfig } from './types'
59-
60-
const configs = {
61-
recommended,
62-
63-
errors,
64-
warnings,
65-
66-
// shhhh... work in progress "secret" rules
67-
'stage-0': stage0,
68-
69-
// useful stuff for folks using various environments
70-
react,
71-
'react-native': reactNative,
72-
electron,
73-
typescript,
74-
} satisfies Record<string, PluginConfig>
69+
import type {
70+
PluginConfig,
71+
PluginFlatBaseConfig,
72+
PluginFlatConfig,
73+
} from './types'
7574

7675
const rules = {
7776
'no-unresolved': noUnresolved,
@@ -129,7 +128,56 @@ const rules = {
129128
'imports-first': importsFirst,
130129
} satisfies Record<string, TSESLint.RuleModule<string, readonly unknown[]>>
131130

131+
const configs = {
132+
recommended,
133+
134+
errors,
135+
warnings,
136+
137+
// shhhh... work in progress "secret" rules
138+
'stage-0': stage0,
139+
140+
// useful stuff for folks using various environments
141+
react,
142+
'react-native': reactNative,
143+
electron,
144+
typescript,
145+
} satisfies Record<string, PluginConfig>
146+
147+
// Base Plugin Object
148+
const plugin = {
149+
meta: { name, version },
150+
rules,
151+
}
152+
153+
// Create flat configs (Only ones that declare plugins and parser options need to be different from the legacy config)
154+
const createFlatConfig = (
155+
baseConfig: PluginFlatBaseConfig,
156+
configName: string,
157+
): PluginFlatConfig => ({
158+
...baseConfig,
159+
name: `import-x/${configName}`,
160+
plugins: { 'import-x': plugin },
161+
})
162+
163+
const flatConfigs = {
164+
recommended: createFlatConfig(recommendedFlat, 'recommended'),
165+
166+
errors: createFlatConfig(errorsFlat, 'errors'),
167+
warnings: createFlatConfig(warningsFlat, 'warnings'),
168+
169+
// shhhh... work in progress "secret" rules
170+
'stage-0': createFlatConfig(stage0Flat, 'stage-0'),
171+
172+
// useful stuff for folks using various environments
173+
react: reactFlat,
174+
'react-native': reactNativeFlat,
175+
electron: electronFlat,
176+
typescript: typescriptFlat,
177+
} satisfies Record<string, PluginFlatConfig>
178+
132179
export = {
133180
configs,
181+
flatConfigs,
134182
rules,
135183
}

src/types.ts

+9
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ export type PluginConfig = {
7070
rules?: Record<`${PluginName}/${string}`, TSESLint.Linter.RuleEntry>
7171
} & TSESLint.Linter.ConfigType
7272

73+
export type PluginFlatBaseConfig = {
74+
settings?: PluginSettings
75+
rules?: Record<`${PluginName}/${string}`, TSESLint.FlatConfig.RuleEntry>
76+
} & TSESLint.FlatConfig.Config
77+
78+
export type PluginFlatConfig = PluginFlatBaseConfig & {
79+
name?: `${PluginName}/${string}`
80+
}
81+
7382
export type RuleContext<
7483
TMessageIds extends string = string,
7584
TOptions extends readonly unknown[] = readonly unknown[],

src/utils/ignore.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ function validExtensions(context: ChildContext | RuleContext) {
2828
export function getFileExtensions(settings: PluginSettings) {
2929
// start with explicit JS-parsed extensions
3030
const exts = new Set<FileExtension>(
31-
settings['import-x/extensions'] || ['.js'],
31+
settings['import-x/extensions'] || ['.js', '.mjs', '.cjs'],
3232
)
3333

3434
// all alternate parser extensions are also valid

0 commit comments

Comments
 (0)