Skip to content

Commit

Permalink
feat!: expose used plugins and automatic inference used plugins
Browse files Browse the repository at this point in the history
this allows users to easier customize based on our presets

BREAKING CHANGE: renamed `utils.config()` to `utils.defineConfig()`
  • Loading branch information
frantic1048 committed Feb 5, 2025
1 parent 20df425 commit 42d16db
Show file tree
Hide file tree
Showing 13 changed files with 145 additions and 64 deletions.
24 changes: 21 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ In your `eslint.config.mjs`([or equivalent](https://eslint.org/docs/latest/use/c
```js
import eslintConfigRightcapital from '@rightcapital/eslint-config';

const { config } = eslintConfigRightcapital.utils;
const { defineConfig } = eslintConfigRightcapital.utils;

export default config(
export default defineConfig(
...eslintConfigRightcapital.configs.recommended,

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

**`utils`**

- `config`: reexported util from `typescript-eslint` for easier compositing ESLint config. (docs: https://typescript-eslint.io/packages/typescript-eslint#config)
- `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`).

```js
const { defineConfig } = eslintConfigRightcapital.utils;

export default defineConfig({
plugins: {
/**
* You can omit this since it's already known to `@rightcapital/eslint-config`.
* And `defineConfig` will automatically infer the plugin from `@rightcapital/eslint-config`.
*/
// unicorn: eslintPluginUnicorn,
},
rules: {
'unicorn/no-hex-escape': 'error',
},
});
```

- `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).

---
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"comment": "feat!: expose used plugins and automatic inference used plugins",
"type": "major",
"packageName": "@rightcapital/eslint-config",
"email": "[email protected]",
"dependentChangeType": "patch"
}
4 changes: 2 additions & 2 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import eslintConfigRightcapital from '@rightcapital/eslint-config';
import eslintPluginEslintPlugin from 'eslint-plugin-eslint-plugin';

const { config } = eslintConfigRightcapital.utils;
const { defineConfig } = eslintConfigRightcapital.utils;

export default config(
export default defineConfig(
{
/**
* NOTE:
Expand Down
9 changes: 3 additions & 6 deletions packages/eslint-config/src/config/base/best-practices.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import type { TSESLint } from '@typescript-eslint/utils';
import eslintPluginLodash from 'eslint-plugin-lodash';
import eslintPluginUnicorn from 'eslint-plugin-unicorn';

import { pickPlugins } from '../../utils.js';

// extracted from [email protected]
// https://github.com/airbnb/javascript/blob/eslint-config-airbnb-base-v15.0.0/packages/eslint-config-airbnb-base/rules/best-practices.js
const config: TSESLint.FlatConfig.ConfigArray = [
{
plugins: {
lodash: eslintPluginLodash,
unicorn: eslintPluginUnicorn,
},
plugins: pickPlugins(['lodash', 'unicorn']),
rules: {
// enforces return statements in callbacks of array's methods
// https://eslint.org/docs/rules/array-callback-return
Expand Down
8 changes: 2 additions & 6 deletions packages/eslint-config/src/config/base/imports.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import type { TSESLint } from '@typescript-eslint/utils';
import eslintPluginSimpleImportSort from 'eslint-plugin-simple-import-sort';

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

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

rules: {
// Static analysis:
Expand Down
8 changes: 4 additions & 4 deletions packages/eslint-config/src/config/base/style.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import eslintPluginStylistic from '@stylistic/eslint-plugin';
import type { TSESLint } from '@typescript-eslint/utils';

import { pickPlugins } from '../../utils.js';

// extracted from [email protected]
// https://github.com/airbnb/javascript/blob/eslint-config-airbnb-base-v15.0.0/packages/eslint-config-airbnb-base/rules/style.js
const config: TSESLint.FlatConfig.ConfigArray = [
{
plugins: {
'@stylistic': eslintPluginStylistic as TSESLint.FlatConfig.Plugin,
},
plugins: pickPlugins(['@stylistic']),

rules: {
// require camel case names
camelcase: ['error', { properties: 'never', ignoreDestructuring: false }],
Expand Down
7 changes: 3 additions & 4 deletions packages/eslint-config/src/config/mixin/node.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { TSESLint } from '@typescript-eslint/utils';
import n from 'eslint-plugin-n';
import globals from 'globals';

import { pickPlugins } from '../../utils.js';
/**
* Common rules for JavaScript files.
*/
Expand All @@ -12,9 +13,7 @@ const config: TSESLint.FlatConfig.ConfigArray = [
},
},

plugins: {
n,
},
plugins: pickPlugins(['n']),

rules: {
// require all requires be top-level
Expand Down
21 changes: 10 additions & 11 deletions packages/eslint-config/src/config/mixin/react.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import eslintPluginReact from '@eslint-react/eslint-plugin';
import eslintPluginRightcapital from '@rightcapital/eslint-plugin';
import eslintPluginStylistic from '@stylistic/eslint-plugin';
import type { TSESLint } from '@typescript-eslint/utils';
import eslintPluginA11y from 'eslint-plugin-jsx-a11y';
import globals from 'globals';

import eslintPluginReactHooks from '../../plugins/eslint-plugin-react-hooks.js';
import { pickPlugins } from '../../utils.js';
/**
* Common rules for React, working with TypeScript.
*/
Expand All @@ -17,13 +14,15 @@ const config: TSESLint.FlatConfig.ConfigArray = [
...globals.browser,
},
},
plugins: {
...eslintPluginReact.configs.all.plugins,
'@rightcapital': eslintPluginRightcapital,
'@stylistic': eslintPluginStylistic as TSESLint.FlatConfig.Plugin,
'react-hooks': eslintPluginReactHooks,
'jsx-a11y': eslintPluginA11y,
},
plugins: pickPlugins([
...(Object.keys(
eslintPluginReact.configs.all.plugins,
) as (keyof typeof eslintPluginReact.configs.all.plugins)[]),
'@rightcapital',
'@stylistic',
'react-hooks',
'jsx-a11y',
]),
rules: {
// naming convention
'@eslint-react/naming-convention/component-name': ['error', 'PascalCase'],
Expand Down
9 changes: 2 additions & 7 deletions packages/eslint-config/src/config/typescript.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import eslintPluginRightcapital from '@rightcapital/eslint-plugin';
import type { TSESLint } from '@typescript-eslint/utils';
import * as typescriptEslint from 'typescript-eslint';

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

/**
Expand All @@ -12,11 +11,7 @@ const config: TSESLint.FlatConfig.ConfigArray = [
...baseConfig,
...typescriptEslint.configs.recommendedTypeChecked,
{
plugins: {
'@typescript-eslint': typescriptEslint.plugin,
'@rightcapital': eslintPluginRightcapital,
'import-x': eslintPluginImportX,
},
plugins: pickPlugins(['@typescript-eslint', '@rightcapital', 'import-x']),
languageOptions: {
parser: typescriptEslint.parser,
parserOptions: {
Expand Down
21 changes: 2 additions & 19 deletions packages/eslint-config/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,27 @@
import type { TSESLint } from '@typescript-eslint/utils';
import globals from 'globals';
import { config } from 'typescript-eslint';

import jsConfig from './config/javascript.js';
import linterConfig from './config/linter.js';
import nodeConfig from './config/mixin/node.js';
import reactConfig from './config/mixin/react.js';
import scriptConfig from './config/mixin/script.js';
import tsConfig from './config/typescript.js';
import utils from './utils.js';

const recommendedConfig = config(
const recommendedConfig = utils.defineConfig(
...linterConfig,

{
files: ['**/*.{js,cjs,mjs,jsx}'],
extends: [...jsConfig],
},

{
files: ['**/*.{ts,cts,mts,tsx}'],
extends: [...tsConfig],
},

{
files: ['**/*.tsx'],
extends: [...reactConfig],
},

{
// test files
files: [
Expand All @@ -46,7 +41,6 @@ const recommendedConfig = config(
],
},
},

{
// scripts
files: [
Expand All @@ -60,7 +54,6 @@ const recommendedConfig = config(
],
extends: [...scriptConfig],
},

{
files: ['*.{js,cjs,mjs,ts,cts,mts}'], // files in the root directory, typically work in node environment
extends: [...nodeConfig],
Expand All @@ -76,15 +69,5 @@ const configs = {
script: scriptConfig,
} as const satisfies Record<string, TSESLint.FlatConfig.ConfigArray>;

const utils = {
/**
* Utility function for composing configs from `typescript-eslint`.
*
* @see https://typescript-eslint.io/packages/typescript-eslint#config
*/
config,
globals,
} as const;

export { configs, utils };
export default { configs, utils };
30 changes: 30 additions & 0 deletions packages/eslint-config/src/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import eslintPluginReact from '@eslint-react/eslint-plugin';
import eslintPluginRightcapital from '@rightcapital/eslint-plugin';
import eslintPluginStylistic from '@stylistic/eslint-plugin';
import type { TSESLint } from '@typescript-eslint/utils';
import eslintPluginA11y from 'eslint-plugin-jsx-a11y';
import eslintPluginLodash from 'eslint-plugin-lodash';
import n from 'eslint-plugin-n';
import eslintPluginSimpleImportSort from 'eslint-plugin-simple-import-sort';
import eslintPluginUnicorn from 'eslint-plugin-unicorn';
import * as typescriptEslint from 'typescript-eslint';

import eslintPluginImportX from './eslint-plugin-import-x.js';
import eslintPluginReactHooks from './eslint-plugin-react-hooks.js';

/**
* All plugins used in `@rightcapital/eslint-config`.
*/
export const plugins = {
'@typescript-eslint': typescriptEslint.plugin,
'@rightcapital': eslintPluginRightcapital,
'import-x': eslintPluginImportX,
'simple-import-sort': eslintPluginSimpleImportSort,
n,
...eslintPluginReact.configs.all.plugins,
'@stylistic': eslintPluginStylistic as TSESLint.FlatConfig.Plugin,
'react-hooks': eslintPluginReactHooks,
'jsx-a11y': eslintPluginA11y,
lodash: eslintPluginLodash,
unicorn: eslintPluginUnicorn,
};
57 changes: 57 additions & 0 deletions packages/eslint-config/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import globals from 'globals';
import tseslint from 'typescript-eslint';

import { plugins } from './plugins/index.js';

/**
* Generate a plugins object from a list of ESLint plugin names
* (Only plugins that are known to `@rightcapital/eslint-config`).
*
* @see {@link plugins} for the list of plugins.
*/
export function pickPlugins(pluginNames?: Array<keyof typeof plugins>) {
if (!pluginNames) {
return plugins;
}

return Object.fromEntries(
pluginNames.map((pluginName) => [pluginName, plugins[pluginName]]),
);
}

const defineConfig: typeof tseslint.config = (...configs) =>
tseslint.config(...configs).map((config) => {
const knownPluginNames = Object.keys(plugins).filter((pluginName) =>
Object.keys(config.rules ?? {}).some((rule) =>
rule.startsWith(`${pluginName}/`),
),
) as Array<keyof typeof plugins>;
const resolvedPlugins = {
...pickPlugins(knownPluginNames),
...config.plugins,
};
return {
...(Object.keys(resolvedPlugins).length > 0
? { plugins: resolvedPlugins }
: null),
...config,
};
});

const utils = {
/**
* Utility function for easily composing configs.
*
* This is a wrapper around `typescript-eslint`'s `config` function.
*
* With automatic plugin inference(if the plugin is known to `@rightcapital/eslint-config`).
*
* @see https://typescript-eslint.io/packages/typescript-eslint#config
*/
defineConfig,
globals,
plugins,
pickPlugins,
} as const;

export default utils;
4 changes: 2 additions & 2 deletions specs/eslint-configs/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import eslintConfigRightcapital from '@rightcapital/eslint-config';

const { config } = eslintConfigRightcapital.utils;
const { defineConfig } = eslintConfigRightcapital.utils;

export default config(
export default defineConfig(
{
files: ['src/javascript.js'],
extends: [...eslintConfigRightcapital.configs.js],
Expand Down

0 comments on commit 42d16db

Please sign in to comment.