Skip to content

Commit 30efd99

Browse files
authored
Merge pull request #230 from RightCapitalHQ/feature/expose-plugins-in-presets-for-easier-customizing
feat!: expose used plugins and automatic inference used plugins
2 parents 7a8b97e + f50dad5 commit 30efd99

14 files changed

+162
-67
lines changed

Diff for: README.md

+21-3
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ In your `eslint.config.mjs`([or equivalent](https://eslint.org/docs/latest/use/c
4040
```js
4141
import eslintConfigRightcapital from '@rightcapital/eslint-config';
4242

43-
const { config } = eslintConfigRightcapital.utils;
43+
const { defineConfig } = eslintConfigRightcapital.utils;
4444

45-
export default config(
45+
export default defineConfig(
4646
...eslintConfigRightcapital.configs.recommended,
4747

4848
// add more configs for specific files or packages if needed
@@ -73,7 +73,25 @@ export default config(
7373

7474
**`utils`**
7575

76-
- `config`: reexported util from `typescript-eslint` for easier compositing ESLint config. (docs: https://typescript-eslint.io/packages/typescript-eslint#config)
76+
- `defineConfig`: reexported util from `typescript-eslint` for easier compositing ESLint config. (docs: https://typescript-eslint.io/packages/typescript-eslint#config), with automatic plugin inference (when the plugin is known to `@rightcapital/eslint-config`).
77+
78+
```js
79+
const { defineConfig } = eslintConfigRightcapital.utils;
80+
81+
export default defineConfig({
82+
plugins: {
83+
/**
84+
* You can omit this since it's already known to `@rightcapital/eslint-config`.
85+
* And `defineConfig` will automatically infer the plugin from `@rightcapital/eslint-config`.
86+
*/
87+
// unicorn: eslintPluginUnicorn,
88+
},
89+
rules: {
90+
'unicorn/no-hex-escape': 'error',
91+
},
92+
});
93+
```
94+
7795
- `globals`: reexported util from [globals](https://github.com/sindresorhus/globals), useful for configuring [`languageOptions.globals`](https://eslint.org/docs/latest/use/configure/language-options#specifying-globals).
7896

7997
---
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"comment": "feat!: expose used plugins and automatic inference used plugins",
3+
"type": "major",
4+
"packageName": "@rightcapital/eslint-config",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

Diff for: eslint.config.mjs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import eslintConfigRightcapital from '@rightcapital/eslint-config';
22
import eslintPluginEslintPlugin from 'eslint-plugin-eslint-plugin';
33

4-
const { config } = eslintConfigRightcapital.utils;
4+
const { defineConfig } = eslintConfigRightcapital.utils;
55

6-
export default config(
6+
export default defineConfig(
77
{
88
/**
99
* NOTE:

Diff for: packages/eslint-config/src/config/base/best-practices.ts

+3-6
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import type { TSESLint } from '@typescript-eslint/utils';
2-
import eslintPluginLodash from 'eslint-plugin-lodash';
3-
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
2+
3+
import { pickPlugins } from '../../utils.js';
44

55
// extracted from [email protected]
66
// https://github.com/airbnb/javascript/blob/eslint-config-airbnb-base-v15.0.0/packages/eslint-config-airbnb-base/rules/best-practices.js
77
const config: TSESLint.FlatConfig.ConfigArray = [
88
{
9-
plugins: {
10-
lodash: eslintPluginLodash,
11-
unicorn: eslintPluginUnicorn,
12-
},
9+
plugins: pickPlugins(['lodash', 'unicorn']),
1310
rules: {
1411
// enforces return statements in callbacks of array's methods
1512
// https://eslint.org/docs/rules/array-callback-return

Diff for: packages/eslint-config/src/config/base/imports.ts

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
import type { TSESLint } from '@typescript-eslint/utils';
2-
import eslintPluginSimpleImportSort from 'eslint-plugin-simple-import-sort';
32

4-
import eslintPluginImportX from '../../plugins/eslint-plugin-import-x.js';
3+
import { pickPlugins } from '../../utils.js';
54

65
// extracted from [email protected]
76
// https://github.com/airbnb/javascript/blob/eslint-config-airbnb-base-v15.0.0/packages/eslint-config-airbnb-base/rules/imports.js
87
const config: TSESLint.FlatConfig.ConfigArray = [
98
{
10-
plugins: {
11-
'import-x': eslintPluginImportX,
12-
'simple-import-sort': eslintPluginSimpleImportSort,
13-
},
9+
plugins: pickPlugins(['import-x', 'simple-import-sort']),
1410

1511
rules: {
1612
// Static analysis:

Diff for: packages/eslint-config/src/config/base/style.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import eslintPluginStylistic from '@stylistic/eslint-plugin';
21
import type { TSESLint } from '@typescript-eslint/utils';
32

3+
import { pickPlugins } from '../../utils.js';
4+
45
// extracted from [email protected]
56
// https://github.com/airbnb/javascript/blob/eslint-config-airbnb-base-v15.0.0/packages/eslint-config-airbnb-base/rules/style.js
67
const config: TSESLint.FlatConfig.ConfigArray = [
78
{
8-
plugins: {
9-
'@stylistic': eslintPluginStylistic as TSESLint.FlatConfig.Plugin,
10-
},
9+
plugins: pickPlugins(['@stylistic']),
10+
1111
rules: {
1212
// require camel case names
1313
camelcase: ['error', { properties: 'never', ignoreDestructuring: false }],

Diff for: packages/eslint-config/src/config/mixin/node.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { TSESLint } from '@typescript-eslint/utils';
2-
import n from 'eslint-plugin-n';
32
import globals from 'globals';
3+
4+
import { pickPlugins } from '../../utils.js';
45
/**
56
* Common rules for JavaScript files.
67
*/
@@ -12,9 +13,7 @@ const config: TSESLint.FlatConfig.ConfigArray = [
1213
},
1314
},
1415

15-
plugins: {
16-
n,
17-
},
16+
plugins: pickPlugins(['n']),
1817

1918
rules: {
2019
// require all requires be top-level

Diff for: packages/eslint-config/src/config/mixin/react.ts

+17-12
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
import eslintPluginReact from '@eslint-react/eslint-plugin';
2-
import eslintPluginRightcapital from '@rightcapital/eslint-plugin';
3-
import eslintPluginStylistic from '@stylistic/eslint-plugin';
42
import type { TSESLint } from '@typescript-eslint/utils';
5-
import eslintPluginA11y from 'eslint-plugin-jsx-a11y';
63
import globals from 'globals';
74

8-
import eslintPluginReactHooks from '../../plugins/eslint-plugin-react-hooks.js';
5+
import { pickPlugins } from '../../utils.js';
96
/**
107
* Common rules for React, working with TypeScript.
118
*/
@@ -17,13 +14,15 @@ const config: TSESLint.FlatConfig.ConfigArray = [
1714
...globals.browser,
1815
},
1916
},
20-
plugins: {
21-
...eslintPluginReact.configs.all.plugins,
22-
'@rightcapital': eslintPluginRightcapital,
23-
'@stylistic': eslintPluginStylistic as TSESLint.FlatConfig.Plugin,
24-
'react-hooks': eslintPluginReactHooks,
25-
'jsx-a11y': eslintPluginA11y,
26-
},
17+
plugins: pickPlugins([
18+
...(Object.keys(
19+
eslintPluginReact.configs.all.plugins,
20+
) as (keyof typeof eslintPluginReact.configs.all.plugins)[]),
21+
'@rightcapital',
22+
'@stylistic',
23+
'react-hooks',
24+
'jsx-a11y',
25+
]),
2726
rules: {
2827
// naming convention
2928
'@eslint-react/naming-convention/component-name': ['error', 'PascalCase'],
@@ -61,6 +60,9 @@ const config: TSESLint.FlatConfig.ConfigArray = [
6160
// MEMO: There are too many false positives with this rule.
6261
'@eslint-react/hooks-extra/no-direct-set-state-in-use-effect': 'off',
6362

63+
// MEMO: Too opinionated thus we disable it
64+
'@eslint-react/hooks-extra/no-useless-custom-hooks': 'off',
65+
6466
// A11y
6567
// Enforce that all elements that require alternative text have meaningful information
6668
// https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/alt-text.md
@@ -109,12 +111,15 @@ const config: TSESLint.FlatConfig.ConfigArray = [
109111
controlComponents: [],
110112
ignoreElements: [
111113
'audio',
114+
'video',
112115
'canvas',
113116
'embed',
114117
'input',
115118
'textarea',
116119
'tr',
117-
'video',
120+
// https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/issues/959
121+
'th',
122+
'td',
118123
],
119124
ignoreRoles: [
120125
'grid',

Diff for: packages/eslint-config/src/config/typescript.ts

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import eslintPluginRightcapital from '@rightcapital/eslint-plugin';
21
import type { TSESLint } from '@typescript-eslint/utils';
32
import * as typescriptEslint from 'typescript-eslint';
43

5-
import eslintPluginImportX from '../plugins/eslint-plugin-import-x.js';
4+
import { pickPlugins } from '../utils.js';
65
import baseConfig from './base/index.js';
76

87
/**
@@ -12,11 +11,7 @@ const config: TSESLint.FlatConfig.ConfigArray = [
1211
...baseConfig,
1312
...typescriptEslint.configs.recommendedTypeChecked,
1413
{
15-
plugins: {
16-
'@typescript-eslint': typescriptEslint.plugin,
17-
'@rightcapital': eslintPluginRightcapital,
18-
'import-x': eslintPluginImportX,
19-
},
14+
plugins: pickPlugins(['@typescript-eslint', '@rightcapital', 'import-x']),
2015
languageOptions: {
2116
parser: typescriptEslint.parser,
2217
parserOptions: {

Diff for: packages/eslint-config/src/index.ts

+2-19
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,27 @@
11
import type { TSESLint } from '@typescript-eslint/utils';
2-
import globals from 'globals';
3-
import { config } from 'typescript-eslint';
42

53
import jsConfig from './config/javascript.js';
64
import linterConfig from './config/linter.js';
75
import nodeConfig from './config/mixin/node.js';
86
import reactConfig from './config/mixin/react.js';
97
import scriptConfig from './config/mixin/script.js';
108
import tsConfig from './config/typescript.js';
9+
import utils from './utils.js';
1110

12-
const recommendedConfig = config(
11+
const recommendedConfig = utils.defineConfig(
1312
...linterConfig,
14-
1513
{
1614
files: ['**/*.{js,cjs,mjs,jsx}'],
1715
extends: [...jsConfig],
1816
},
19-
2017
{
2118
files: ['**/*.{ts,cts,mts,tsx}'],
2219
extends: [...tsConfig],
2320
},
24-
2521
{
2622
files: ['**/*.tsx'],
2723
extends: [...reactConfig],
2824
},
29-
3025
{
3126
// test files
3227
files: [
@@ -46,7 +41,6 @@ const recommendedConfig = config(
4641
],
4742
},
4843
},
49-
5044
{
5145
// scripts
5246
files: [
@@ -60,7 +54,6 @@ const recommendedConfig = config(
6054
],
6155
extends: [...scriptConfig],
6256
},
63-
6457
{
6558
files: ['*.{js,cjs,mjs,ts,cts,mts}'], // files in the root directory, typically work in node environment
6659
extends: [...nodeConfig],
@@ -76,15 +69,5 @@ const configs = {
7669
script: scriptConfig,
7770
} as const satisfies Record<string, TSESLint.FlatConfig.ConfigArray>;
7871

79-
const utils = {
80-
/**
81-
* Utility function for composing configs from `typescript-eslint`.
82-
*
83-
* @see https://typescript-eslint.io/packages/typescript-eslint#config
84-
*/
85-
config,
86-
globals,
87-
} as const;
88-
8972
export { configs, utils };
9073
export default { configs, utils };

Diff for: packages/eslint-config/src/plugins/index.ts

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import eslintPluginReact from '@eslint-react/eslint-plugin';
2+
import eslintPluginRightcapital from '@rightcapital/eslint-plugin';
3+
import eslintPluginStylistic from '@stylistic/eslint-plugin';
4+
import type { TSESLint } from '@typescript-eslint/utils';
5+
import eslintPluginA11y from 'eslint-plugin-jsx-a11y';
6+
import eslintPluginLodash from 'eslint-plugin-lodash';
7+
import n from 'eslint-plugin-n';
8+
import eslintPluginSimpleImportSort from 'eslint-plugin-simple-import-sort';
9+
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
10+
import * as typescriptEslint from 'typescript-eslint';
11+
12+
import eslintPluginImportX from './eslint-plugin-import-x.js';
13+
import eslintPluginReactHooks from './eslint-plugin-react-hooks.js';
14+
15+
const definePlugins = <TPluginName extends string>(
16+
plugins: Record<TPluginName, Pick<TSESLint.FlatConfig.Plugin, 'rules'>>,
17+
): Record<TPluginName, TSESLint.FlatConfig.Plugin> => {
18+
return plugins;
19+
};
20+
21+
/**
22+
* All plugins used in `@rightcapital/eslint-config`.
23+
*/
24+
export const plugins = definePlugins({
25+
'@typescript-eslint': typescriptEslint.plugin,
26+
'@rightcapital': eslintPluginRightcapital,
27+
'import-x': eslintPluginImportX,
28+
'simple-import-sort': eslintPluginSimpleImportSort,
29+
n,
30+
...eslintPluginReact.configs.all.plugins,
31+
'@stylistic': eslintPluginStylistic as TSESLint.FlatConfig.Plugin,
32+
'react-hooks': eslintPluginReactHooks,
33+
'jsx-a11y': eslintPluginA11y,
34+
lodash: eslintPluginLodash,
35+
unicorn: eslintPluginUnicorn,
36+
});

Diff for: packages/eslint-config/src/utils.ts

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import globals from 'globals';
2+
import tseslint from 'typescript-eslint';
3+
4+
import { plugins } from './plugins/index.js';
5+
6+
/**
7+
* Generate a plugins object from a list of ESLint plugin names
8+
* (Only plugins that are known to `@rightcapital/eslint-config`).
9+
*
10+
* @see {@link plugins} for the list of plugins.
11+
*/
12+
export function pickPlugins(pluginNames?: Array<keyof typeof plugins>) {
13+
if (!pluginNames) {
14+
return plugins;
15+
}
16+
17+
return Object.fromEntries(
18+
pluginNames.map((pluginName) => [pluginName, plugins[pluginName]]),
19+
);
20+
}
21+
22+
const defineConfig: typeof tseslint.config = (...configs) =>
23+
tseslint.config(...configs).map((config) => {
24+
const knownPluginNames = Object.keys(plugins).filter((pluginName) =>
25+
Object.keys(config.rules ?? {}).some((rule) =>
26+
rule.startsWith(`${pluginName}/`),
27+
),
28+
) as Array<keyof typeof plugins>;
29+
const resolvedPlugins = {
30+
...pickPlugins(knownPluginNames),
31+
...config.plugins,
32+
};
33+
return {
34+
...(Object.keys(resolvedPlugins).length > 0
35+
? { plugins: resolvedPlugins }
36+
: null),
37+
...config,
38+
};
39+
});
40+
41+
const utils = {
42+
/**
43+
* Utility function for easily composing configs.
44+
*
45+
* This is a wrapper around `typescript-eslint`'s `config` function.
46+
*
47+
* With automatic plugin inference(if the plugin is known to `@rightcapital/eslint-config`).
48+
*
49+
* @see https://typescript-eslint.io/packages/typescript-eslint#config
50+
*/
51+
defineConfig,
52+
globals,
53+
plugins,
54+
pickPlugins,
55+
} as const;
56+
57+
export default utils;

0 commit comments

Comments
 (0)