This commit is contained in:
Alex 2024-07-30 23:00:25 -04:00
parent 2853da4344
commit 3ac5f91988
18 changed files with 281 additions and 171 deletions

View File

@ -3,12 +3,12 @@
"env": {
"node": true,
"browser": true,
"es6": true
"es6": true,
},
"extends": ["eslint:recommended", "prettier"],
"parserOptions": {
"sourceType": "module",
"ecmaVersion": "latest"
"ecmaVersion": "latest",
},
"rules": {
"no-restricted-imports": [
@ -24,14 +24,14 @@
"object.hasown",
"object.values",
"string.prototype.matchall",
"has"
]
}
"has",
],
},
],
"arrow-body-style": ["error", "as-needed"],
"class-methods-use-this": [
"warn",
{ "exceptMethods": ["toString", "shouldComponentUpdate"] }
{ "exceptMethods": ["toString", "shouldComponentUpdate"] },
],
"complexity": ["warn", { "max": 100 }],
"curly": ["error", "multi-line", "consistent"],
@ -50,13 +50,13 @@
"prefer-const": ["error", { "destructuring": "all" }],
"prefer-destructuring": [
"warn",
{ "AssignmentExpression": { "array": false, "object": false } }
{ "AssignmentExpression": { "array": false, "object": false } },
],
"prefer-rest-params": "warn",
"prefer-spread": "warn",
"quote-props": ["error", "as-needed"],
"spaced-comment": ["error", "always", { "markers": ["/"] }],
"sort-imports": ["warn", { "ignoreDeclarationSort": true }],
"yoda": ["error", "never", { "exceptRange": true }]
}
"yoda": ["error", "never", { "exceptRange": true }],
},
}

24
dist/index.d.ts vendored
View File

@ -1,12 +1,19 @@
// Generated by dts-bundle-generator v9.4.0
import { ESLintConfig, Rules } from '@aet/eslint-define-config';
import { ESLintConfig, KnownExtends, Rules, Settings } from '@aet/eslint-define-config';
import { ESLintUtils } from '@typescript-eslint/utils';
import { Rule } from 'eslint';
import { Merge, SetRequired } from 'type-fest';
export declare const error = "error";
export declare const warn = "warn";
export declare const off = "off";
export declare const graphql: Middleware;
export declare const jsdoc: Middleware;
export declare const storybook: Middleware;
export declare const react: Middleware;
export declare const reactRefresh: Middleware;
export declare const tailwind: Middleware;
export type RuleLevel = "error" | "warn" | "off" | 0 | 1 | 2;
export type RuleEntry<Options> = RuleLevel | [
RuleLevel,
@ -32,6 +39,7 @@ export interface CustomRule {
}>;
options?: RuleLevel;
}
export type Middleware = (config: MiddlewareConfig, helpers: MiddlewareFunctions) => void;
/**
* ESLint Configuration.
* @see [ESLint Configuration](https://eslint.org/docs/latest/user-guide/configuring/)
@ -53,6 +61,16 @@ export type InputConfig = Omit<ESLintConfig, "rules"> & {
*/
auto?: boolean;
};
export type OptionalObjectKey<T> = Exclude<{
[Key in keyof T]: undefined | any[] extends T[Key] ? Key : undefined | Record<any, any> extends T[Key] ? Key : never;
}[keyof T], undefined>;
export type MiddlewareConfig = Merge<SetRequired<ESLintConfig, OptionalObjectKey<ESLintConfig>>, {
extends: KnownExtends[];
}>;
export interface MiddlewareFunctions {
addRules(rules: Partial<RuleOptions>): void;
addSettings(settings: Partial<Settings>): void;
}
/**
* Returns a ESLint config object.
*
@ -72,6 +90,8 @@ export type InputConfig = Omit<ESLintConfig, "rules"> & {
* Non bundled:
* 1. [`graphql`](https://the-guild.dev/graphql/eslint/rules)
*/
export declare function extendConfig(of?: InputConfig): ESLintConfig;
export declare function extendConfig(of?: InputConfig & {
middlewares: Middleware[];
}): ESLintConfig;
export {};

6
dist/package.json vendored
View File

@ -1,6 +1,6 @@
{
"name": "@aet/eslint-rules",
"version": "1.0.1-beta.10",
"version": "1.0.1-beta.11",
"license": "UNLICENSED",
"peerDependencies": {
"eslint": "^8.57.0",
@ -8,7 +8,7 @@
},
"dependencies": {
"@nolyfill/is-core-module": "^1.0.39",
"@aet/eslint-define-config": "0.1.0-beta.10",
"@aet/eslint-define-config": "^0.1.0-beta.15",
"@eslint-community/eslint-utils": "^4.4.0",
"@tanstack/eslint-plugin-query": "^5.51.15",
"@types/eslint": "^8.56.11",
@ -60,4 +60,4 @@
"supports-preserve-symlinks-flag": "file:./overrides/supports-preserve-symlinks-flag"
}
}
}
}

View File

@ -7,7 +7,7 @@
},
"private": true,
"devDependencies": {
"@aet/eslint-define-config": "^0.1.0-beta.10",
"@aet/eslint-define-config": "^0.1.0-beta.15",
"@babel/core": "^7.24.9",
"@babel/plugin-transform-flow-strip-types": "^7.24.7",
"@babel/preset-env": "^7.25.0",
@ -43,6 +43,7 @@
"picocolors": "^1.0.1",
"prettier": "^3.3.3",
"prop-types": "^15.8.1",
"type-fest": "^4.23.0",
"typescript": "^5.5.4"
},
"prettier": {

19
pnpm-lock.yaml generated
View File

@ -22,8 +22,8 @@ importers:
.:
devDependencies:
'@aet/eslint-define-config':
specifier: ^0.1.0-beta.10
version: 0.1.0-beta.10
specifier: ^0.1.0-beta.15
version: 0.1.0-beta.15
'@babel/core':
specifier: ^7.24.9
version: 7.24.9
@ -129,6 +129,9 @@ importers:
prop-types:
specifier: ^15.8.1
version: 15.8.1
type-fest:
specifier: ^4.23.0
version: 4.23.0
typescript:
specifier: ^5.5.4
version: 5.5.4
@ -139,8 +142,8 @@ packages:
resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
engines: {node: '>=0.10.0'}
'@aet/eslint-define-config@0.1.0-beta.10':
resolution: {integrity: sha512-YTm+DdRvCPMMN+R3PZxJsBLYwUscxIpBhlpo2hSS/KEXtOiNUTGPIdzUERFkdNA1PayYfCDwh4dquKX3IZ0uwQ==}
'@aet/eslint-define-config@0.1.0-beta.15':
resolution: {integrity: sha512-WQuQyKU4V4RSZi0ShPpBE0LB8+evUjLr34T9XLWDTFJBrVkD6tJXdj8eQlzEWkp8ZxQrPGbYfkegwGHQ1NdLXg==}
engines: {node: '>=18.0.0', npm: '>=9.0.0'}
'@ampproject/remapping@2.2.1':
@ -1935,6 +1938,10 @@ packages:
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
engines: {node: '>=10'}
type-fest@4.23.0:
resolution: {integrity: sha512-ZiBujro2ohr5+Z/hZWHESLz3g08BBdrdLMieYFULJO+tWc437sn8kQsWLJoZErY8alNhxre9K4p3GURAG11n+w==}
engines: {node: '>=16'}
typescript@5.5.4:
resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==}
engines: {node: '>=14.17'}
@ -2022,7 +2029,7 @@ snapshots:
'@aashutoshrathi/word-wrap@1.2.6': {}
'@aet/eslint-define-config@0.1.0-beta.10': {}
'@aet/eslint-define-config@0.1.0-beta.15': {}
'@ampproject/remapping@2.2.1':
dependencies:
@ -4018,6 +4025,8 @@ snapshots:
type-fest@0.20.2: {}
type-fest@4.23.0: {}
typescript@5.5.4: {}
undici-types@5.26.5: {}

View File

@ -23,5 +23,4 @@ pull() {
pull import-js eslint-import-resolver-typescript
pull jsx-eslint eslint-plugin-jsx-a11y
pull eslint-community eslint-plugin-n
pull jsx-eslint eslint-plugin-react
pull jsx-eslint jsx-ast-utils

View File

@ -18,10 +18,10 @@
"subject": "[readme] fix typo in shareable config section in readme"
},
"eslint-plugin-n": {
"hash": "5aad5f1c419b3143ffb9356bd299fc50dc576ee5",
"date": "2024-07-26T10:04:35+08:00",
"hash": "6744257b43560181412a76eadeb7de564b886ad4",
"date": "2024-07-26T11:46:54+01:00",
"committer": "GitHub",
"subject": "chore(master): release 17.10.0 (#305)"
"subject": "chore(master): release 17.10.1 (#319)"
},
"eslint-plugin-react": {
"hash": "983b88dd3cb5e07919517d3fde4085f60883ded7",

View File

@ -1,46 +1,39 @@
import type { Extends, Plugin } from '@aet/eslint-define-config';
import * as fs from 'node:fs';
import { resolve } from 'node:path';
import { Middleware, storybook } from './index';
import { react, reactRefresh } from './presets/react';
import { tailwind } from './presets/tailwind';
import { reactQuery } from './presets/misc';
export function checkEnv() {
export function* checkEnv(): Generator<Middleware> {
const rootDir = process.cwd();
const plugins: Plugin[] = [];
const extend: Extends[] = [];
const pkgJsonPath = resolve(rootDir, 'package.json');
const pkgJson = fs.existsSync(pkgJsonPath)
? JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'))
: {};
const deps = Object.keys({
...pkgJson.dependencies,
...pkgJson.devDependencies,
...pkgJson.peerDependencies,
});
const deps = new Set(
Object.keys({
...pkgJson.dependencies,
...pkgJson.devDependencies,
...pkgJson.peerDependencies,
}),
);
const hasReact = deps.includes('react');
if (hasReact) {
plugins.push('react-hooks');
if (deps.has('react')) {
yield react;
}
if (deps.includes('@vitejs/plugin-react')) {
plugins.push('react-refresh');
if (deps.has('@vitejs/plugin-react') && deps.has('eslint-plugin-react-refresh')) {
yield reactRefresh;
}
if (deps.includes('tailwindcss') && deps.includes('eslint-plugin-tailwindcss')) {
plugins.push('tailwindcss');
if (deps.has('tailwindcss') && deps.has('eslint-plugin-tailwindcss')) {
yield tailwind;
}
if (deps.includes('storybook') && deps.includes('eslint-plugin-storybook')) {
extend.push('plugin:storybook/recommended');
if (deps.has('storybook') && deps.has('eslint-plugin-storybook')) {
yield storybook;
}
if (deps.includes('@tanstack/react-query')) {
extend.push('plugin:@tanstack/eslint-plugin-query/recommended');
if (deps.has('@tanstack/react-query') && deps.has('@tanstack/eslint-plugin-query')) {
yield reactQuery;
}
return {
react: hasReact,
plugins,
extends: extend,
};
}

View File

@ -1,21 +1,31 @@
/// <reference path="./modules.d.ts" />
import './redirect';
import { uniq } from 'lodash';
import type { Merge, SetRequired } from 'type-fest';
import type { Rule } from 'eslint';
import type { ESLintUtils } from '@typescript-eslint/utils';
import type { ESLintConfig, Extends, Plugin, Rules } from '@aet/eslint-define-config';
// import findCacheDirectory from 'find-cache-dir';
import { typescriptRules } from './presets/typescript';
import { unicornRules } from './presets/unicorn';
import type {
ESLintConfig,
Extends,
KnownExtends,
Plugin,
Rules,
Settings,
} from '@aet/eslint-define-config';
import { importTypeScript } from './presets/typescript';
import { unicorn } from './presets/unicorn';
import { eslintRules } from './presets/eslint';
import { reactRules } from './presets/react';
import { importRules } from './presets/import-x';
import { jsDocRules } from './presets/jsdoc';
import { graphqlRules } from './presets/graphql';
import { localRules } from './presets/local';
import { local } from './presets/local';
import { error, warn, off } from './constants';
import { tailwindRules } from './presets/tailwind';
import { checkEnv } from './env';
export { graphql } from './presets/graphql';
export { jsdoc } from './presets/jsdoc';
export { storybook } from './presets/misc';
export { react, reactRefresh } from './presets/react';
export { tailwind } from './presets/tailwind';
export { error, warn, off };
declare global {
@ -57,6 +67,8 @@ export interface CustomRule {
options?: RuleLevel;
}
export type Middleware = (config: MiddlewareConfig, helpers: MiddlewareFunctions) => void;
/**
* ESLint Configuration.
* @see [ESLint Configuration](https://eslint.org/docs/latest/user-guide/configuring/)
@ -81,6 +93,27 @@ export type InputConfig = Omit<ESLintConfig, 'rules'> & {
auto?: boolean;
};
type OptionalObjectKey<T> = Exclude<
{
[Key in keyof T]: undefined | any[] extends T[Key]
? Key
: undefined | Record<any, any> extends T[Key]
? Key
: never;
}[keyof T],
undefined
>;
type MiddlewareConfig = Merge<
SetRequired<ESLintConfig, OptionalObjectKey<ESLintConfig>>,
{ extends: KnownExtends[] }
>;
interface MiddlewareFunctions {
addRules(rules: Partial<RuleOptions>): void;
addSettings(settings: Partial<Settings>): void;
}
/**
* Returns a ESLint config object.
*
@ -100,124 +133,75 @@ export type InputConfig = Omit<ESLintConfig, 'rules'> & {
* Non bundled:
* 1. [`graphql`](https://the-guild.dev/graphql/eslint/rules)
*/
export function extendConfig(of: InputConfig = {}): ESLintConfig {
export function extendConfig(
of: InputConfig & {
middlewares: Middleware[];
} = {
middlewares: [],
},
): ESLintConfig {
const {
auto,
auto = true,
plugins: _plugins = [],
settings,
settings = {},
rules,
extends: _extends,
overrides,
customRuleFiles,
parserOptions,
middlewares: _middlewares = [],
// @ts-expect-error
localRules: _,
...rest
} = of;
let hasReact = false;
let plugins: Plugin[] = _plugins;
let plugins: Plugin[] = [..._plugins];
let extend: Extends[] = ensureArray(_extends);
if (auto) {
const env = checkEnv();
hasReact = env.react;
plugins = [..._plugins, ...env.plugins];
extend = [...extend, ...env.extends];
}
const middlewares: Middleware[] = uniq([
importTypeScript,
unicorn,
local,
...(auto ? checkEnv() : []),
..._middlewares,
]);
const hasJsDoc = plugins.includes('jsdoc');
const hasGraphQL = plugins.includes('@graphql-eslint');
const hasTailwind = extend.some(name => name.includes('plugin:tailwindcss/'));
// const ruleDir = false; // ?? findCacheDirectory({ name: '_eslint-rules' });
// if (ruleDir) {
// fs.rmSync(ruleDir, { recursive: true, force: true });
// fs.mkdirSync(ruleDir, { recursive: true });
// }
const result: InputConfig = {
const result: MiddlewareConfig = {
root: true,
parser: '@typescript-eslint/parser',
plugins: unique('@typescript-eslint', 'import-x', 'rules', 'unicorn', plugins),
plugins: unique('rules', plugins),
env: { node: true, browser: true, es2023: true },
reportUnusedDisableDirectives: true,
parserOptions: {
project: true,
...parserOptions,
},
extends: unique(
'eslint:recommended',
'prettier',
'plugin:@typescript-eslint/recommended-type-checked',
'plugin:import-x/errors',
'plugin:import-x/typescript',
hasReact && [
'plugin:@eslint-react/recommended-legacy',
'plugin:@eslint-react/dom-legacy',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
],
hasJsDoc && 'plugin:jsdoc/recommended-typescript',
hasGraphQL && 'plugin:@graphql-eslint/recommended',
extend as string[],
),
settings: {
'import-x/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx', '.mts', '.cts'],
},
'import-x/resolver': {
typescript: {
alwaysTryTypes: true,
},
},
...settings,
},
ignorePatterns: [],
globals: {},
extends: ['eslint:recommended', 'prettier', ...(extend as string[])],
settings,
overrides: [
{
files: ['.eslintrc.js', '.eslintrc.cjs', '*.config.js', 'index.js'],
extends: ['plugin:@typescript-eslint/disable-type-checked'],
rules: {
'rules/restrict-template-expressions': off,
},
},
{
files: ['*.d.ts'],
rules: {
'@typescript-eslint/consistent-type-imports': off,
},
},
{
files: ['repl.ts', 'scripts/**/*.ts'],
rules: {
'no-console': off,
},
},
{
files: ['*.tsx'],
rules: {
'@eslint-react/no-leaked-conditional-rendering': error,
},
},
{ files: ['repl.ts', 'scripts/**/*.ts'], rules: { 'no-console': off } },
...(overrides ?? []),
],
rules: {
...eslintRules,
...typescriptRules,
...importRules,
...localRules,
...(hasReact && reactRules),
...(plugins.includes('react-refresh') && {
'react-refresh/only-export-components': [warn, { allowConstantExport: true }],
}),
...unicornRules,
...(hasJsDoc && jsDocRules),
...(hasGraphQL && graphqlRules),
...(hasTailwind && tailwindRules),
...rules,
},
rules: { ...eslintRules, ...rules },
...rest,
};
const functions: MiddlewareFunctions = {
addRules(newRules) {
Object.assign(result.rules, newRules);
},
addSettings(newSettings) {
Object.assign(result.settings, newSettings);
},
};
for (const fn of middlewares) {
fn(result, functions);
}
result.plugins = unique(result.plugins);
result.extends = unique(result.extends);
return result;
}

View File

@ -1,4 +1,11 @@
import { GraphQLRulesObject } from '@aet/eslint-define-config/src/rules/graphql-eslint';
import type { Middleware } from '../index';
// https://the-guild.dev/graphql/eslint/rules
export const graphqlRules: Partial<GraphQLRulesObject> = {};
const graphqlRules: Partial<GraphQLRulesObject> = {};
export const graphql: Middleware = (config, { addRules }) => {
config.plugins.push('@graphql-eslint');
config.extends.push('plugin:@graphql-eslint/recommended');
addRules(graphqlRules);
};

View File

@ -1,8 +0,0 @@
import { error, off } from '../constants';
import { ImportXRulesObject } from '@aet/eslint-define-config/src/rules/import-x';
export const importRules: Partial<ImportXRulesObject> = {
'import-x/export': off,
'import-x/no-duplicates': error,
'import-x/order': [error, { groups: ['builtin', 'external'] }],
};

View File

@ -1,3 +1,10 @@
import { JSDocRulesObject } from '@aet/eslint-define-config/src/rules/jsdoc';
import type { Middleware } from '../index';
export const jsDocRules: Partial<JSDocRulesObject> = {};
const jsdocRules: Partial<JSDocRulesObject> = {};
export const jsdoc: Middleware = (config, { addRules }) => {
config.plugins.push('jsdoc');
config.extends.push('plugin:jsdoc/recommended-typescript');
addRules(jsdocRules);
};

View File

@ -1,7 +1,11 @@
import type { LocalRuleOptions } from '..';
import type { LocalRuleOptions, Middleware } from '../index';
import { error } from '../constants';
export const localRules: Partial<LocalRuleOptions> = {
const localRules: Partial<LocalRuleOptions> = {
'rules/no-import-dot': error,
'rules/restrict-template-expressions': error,
};
export const local: Middleware = (_, { addRules }) => {
addRules(localRules);
};

9
src/presets/misc.ts Normal file
View File

@ -0,0 +1,9 @@
import type { Middleware } from '../index';
export const storybook: Middleware = config => {
config.extends.push('plugin:storybook/recommended');
};
export const reactQuery: Middleware = config => {
config.extends.push('plugin:@tanstack/eslint-plugin-query/recommended');
};

View File

@ -1,7 +1,35 @@
import { error, off } from '../constants';
import type { Middleware } from '../index';
import { error, off, warn } from '../constants';
import { ReactRulesObject } from '@aet/eslint-define-config/src/rules/react';
import { ReactRefreshRulesObject } from '@aet/eslint-define-config/src/rules/react-refresh';
export const reactRules: Partial<ReactRulesObject> = {
const reactRules: Partial<ReactRulesObject> = {
'@eslint-react/no-missing-component-display-name': off,
'@eslint-react/no-children-prop': error,
};
export const react: Middleware = (config, { addRules }) => {
config.plugins.push('@eslint-react/eslint-plugin', 'react-hooks');
config.extends.push(
'plugin:@eslint-react/recommended-legacy',
'plugin:@eslint-react/dom-legacy',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
);
config.overrides.push({
files: ['*.tsx'],
rules: {
'@eslint-react/no-leaked-conditional-rendering': error,
},
});
addRules(reactRules);
};
const refreshRules: Partial<ReactRefreshRulesObject> = {
'react-refresh/only-export-components': [warn, { allowConstantExport: true }],
};
export const reactRefresh: Middleware = (config, { addRules }) => {
config.plugins.push('react-refresh');
addRules(refreshRules);
};

View File

@ -1,5 +1,12 @@
import type { Middleware } from '../index';
import { off } from '../constants';
import type { TailwindRulesObject } from '@aet/eslint-define-config/src/rules/tailwind';
export const tailwindRules = {
const tailwindRules: Partial<TailwindRulesObject> = {
'tailwindcss/no-custom-classname': off,
} as const;
export const tailwind: Middleware = (config, { addRules }) => {
config.extends.push('plugin:tailwindcss/recommended');
addRules(tailwindRules);
};

View File

@ -1,7 +1,15 @@
import { error, off, warn } from '../constants';
import type { TypeScriptRulesObject } from '@aet/eslint-define-config/src/rules/typescript-eslint';
import type { ImportXRulesObject } from '@aet/eslint-define-config/src/rules/import-x';
import type { Middleware } from '../index';
export const typescriptRules: Partial<TypeScriptRulesObject> = {
const importRules: Partial<ImportXRulesObject> = {
'import-x/export': off,
'import-x/no-duplicates': error,
'import-x/order': [error, { groups: ['builtin', 'external'] }],
};
const typescriptRules: Partial<TypeScriptRulesObject> = {
'@typescript-eslint/ban-ts-comment': [
error,
{
@ -37,3 +45,39 @@ export const typescriptRules: Partial<TypeScriptRulesObject> = {
'@typescript-eslint/triple-slash-reference': off,
'@typescript-eslint/unbound-method': off,
};
export const importTypeScript: Middleware = (config, { addRules, addSettings }) => {
config.parser = '@typescript-eslint/parser';
config.plugins.push('@typescript-eslint', 'import-x');
config.extends.push(
'plugin:@typescript-eslint/recommended-type-checked',
'plugin:import-x/errors',
'plugin:import-x/typescript',
);
addSettings({
'import-x/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx', '.mts', '.cts'],
},
'import-x/resolver': {
typescript: true,
},
});
config.overrides.push(
{
files: ['.eslintrc.js', '.eslintrc.cjs', '*.config.js', 'index.js'],
extends: ['plugin:@typescript-eslint/disable-type-checked'],
rules: {
'rules/restrict-template-expressions': off,
},
},
{
files: ['*.d.ts'],
rules: {
'@typescript-eslint/consistent-type-imports': off,
},
},
);
addRules(importRules);
addRules(typescriptRules);
};

View File

@ -1,10 +1,11 @@
import type { Middleware } from '../index';
import { error, warn } from '../constants';
import { UnicornRulesObject } from '@aet/eslint-define-config/src/rules/unicorn';
const suggest = (suggest: string) => ({ suggest, fix: false });
// https://github.com/sindresorhus/eslint-plugin-unicorn/tree/28e7498ad06679bb92343db53bb40a7b5ba2990a
export const unicornRules: Partial<UnicornRulesObject> = {
const unicornRules: Partial<UnicornRulesObject> = {
'unicorn/better-regex': error,
'unicorn/consistent-function-scoping': warn,
'unicorn/escape-case': error,
@ -69,3 +70,8 @@ export const unicornRules: Partial<UnicornRulesObject> = {
],
'unicorn/template-indent': warn,
};
export const unicorn: Middleware = (config, { addRules }) => {
config.plugins.push('unicorn');
addRules(unicornRules);
};