Upgrade to ESLint 9
This commit is contained in:
@ -6,22 +6,22 @@
|
||||
"subject": "[meta] add `repository.directory` field"
|
||||
},
|
||||
"eslint-import-resolver-typescript": {
|
||||
"hash": "3dfad602a05b4b3812a4d3fc681051932f86e838",
|
||||
"date": "2024-08-01T01:15:59+00:00",
|
||||
"hash": "5ee5879b4428f42edbc262d66e192c76202b7f47",
|
||||
"date": "2024-10-01T03:12:28+00:00",
|
||||
"committer": "GitHub",
|
||||
"subject": "chore(deps): update dependency node to v18.20.4 (#309)"
|
||||
"subject": "fix(deps): update dependency debug to ^4.3.7 (#316)"
|
||||
},
|
||||
"eslint-plugin-jsx-a11y": {
|
||||
"hash": "a08fbcc502d6a6fa7d01a48c5f0b895c61e8cdd5",
|
||||
"date": "2024-08-22T20:21:57+01:00",
|
||||
"hash": "4925ba8d0bf80a4b1d8e8645d310590bf1b40b64",
|
||||
"date": "2024-09-20T14:09:27-07:00",
|
||||
"committer": "Jordan Harband",
|
||||
"subject": "[Fix] `label-has-associated-control`: ignore undetermined label text"
|
||||
"subject": "[Fix] handle interactive/noninteractive changes from aria-query"
|
||||
},
|
||||
"eslint-plugin-n": {
|
||||
"hash": "e5e758ea0cd238220127ae7bcbd967f1d8920f28",
|
||||
"date": "2024-08-06T04:22:42+12:00",
|
||||
"hash": "23d0e846e9dbfb68ccf7f410a83457514d432263",
|
||||
"date": "2024-10-09T13:49:20+02:00",
|
||||
"committer": "GitHub",
|
||||
"subject": "docs(process-exit-as-throw): update wording (#323)"
|
||||
"subject": "chore(master): release 17.11.1 (#352)"
|
||||
},
|
||||
"eslint-plugin-react": {
|
||||
"hash": "983b88dd3cb5e07919517d3fde4085f60883ded7",
|
||||
|
44
src/config.d.ts
vendored
Normal file
44
src/config.d.ts
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
import type { FlatESLintConfig } from '@aet/eslint-define-config';
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
type MiddlewareResult = Linter.Config | Linter.Config[];
|
||||
|
||||
export type Middleware =
|
||||
| (() => Promise<MiddlewareResult>)
|
||||
| (() => Promise<{ default: MiddlewareResult }>);
|
||||
|
||||
/**
|
||||
* Returns a ESLint config object.
|
||||
*
|
||||
* By default, it includes `["@typescript-eslint", "import-x", "prettier", "unicorn"]` configs.
|
||||
* Additional bundled plugins include:
|
||||
*
|
||||
* 1. [`react`](https://github.com/jsx-eslint/eslint-plugin-react#list-of-supported-rules)
|
||||
* (automatically enables
|
||||
* [`react-hooks`](https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks))
|
||||
* 2. [`react-refresh`](https://github.com/ArnaudBarre/eslint-plugin-react-refresh)
|
||||
* 3. [`jsx-a11y`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y#supported-rules)
|
||||
* 4. [`unicorn`](https://github.com/sindresorhus/eslint-plugin-unicorn#rules)
|
||||
* 5. [`n`](https://github.com/eslint-community/eslint-plugin-n#-rules) (Node.js specific,
|
||||
* requires `minimatch`)
|
||||
* 6. [`jsdoc`](https://github.com/gajus/eslint-plugin-jsdoc#rules)
|
||||
*
|
||||
* Non bundled:
|
||||
* 1. [`graphql`](https://the-guild.dev/graphql/eslint/rules)
|
||||
*
|
||||
* @param of Configuration options.
|
||||
* @returns ESLint configuration object.
|
||||
*/
|
||||
export function extendConfig({
|
||||
auto,
|
||||
middlewares: addMiddlewares,
|
||||
configs,
|
||||
}: {
|
||||
auto?: boolean;
|
||||
middlewares?: Middleware[];
|
||||
configs: FlatESLintConfig[];
|
||||
}): Promise<FlatESLintConfig[]>;
|
||||
|
||||
export const error = 'error';
|
||||
export const warn = 'warn';
|
||||
export const off = 'off';
|
@ -1,17 +1,40 @@
|
||||
import type { ESLintUtils } from '@typescript-eslint/utils';
|
||||
import type { Rule } from 'eslint';
|
||||
import type { ESLint } from 'eslint';
|
||||
|
||||
import noEmptyObjectLiteral from './no-empty-object-literal';
|
||||
import noImportDot from './no-import-dot';
|
||||
import noUselessImportAlias from './no-useless-import-alias';
|
||||
import restrictTemplateExpressions from './restrict-template-expressions';
|
||||
|
||||
export const rules: Record<
|
||||
string,
|
||||
Rule.RuleModule | ESLintUtils.RuleModule<string, unknown[]>
|
||||
> = {
|
||||
'no-empty-object-literal': noEmptyObjectLiteral,
|
||||
'no-import-dot': noImportDot,
|
||||
'no-useless-import-alias': noUselessImportAlias,
|
||||
'restrict-template-expressions': restrictTemplateExpressions,
|
||||
type RuleLevel = 'error' | 'warn' | 'off' | 0 | 1 | 2;
|
||||
type RuleEntry<Options> = RuleLevel | [RuleLevel, Partial<Options>];
|
||||
|
||||
export interface LocalRuleOptions {
|
||||
/** Bans import from the specifier '.' and '..' and replaces it with '.+/index' */
|
||||
'custom/no-import-dot': RuleEntry<unknown>;
|
||||
/**
|
||||
* Enforce template literal expressions to be of `string` type
|
||||
* @see [restrict-template-expressions](https://typescript-eslint.io/rules/restrict-template-expressions)
|
||||
*/
|
||||
'typed-custom/restrict-template-expressions': RuleEntry<{ allow: string[] }>;
|
||||
/** Ban assignment of empty object literals `{}` and replace them with `Object.create(null)` */
|
||||
'custom/no-empty-object-literal': RuleEntry<unknown>;
|
||||
/** Ban useless import alias */
|
||||
'custom/no-useless-import-alias': RuleEntry<unknown>;
|
||||
}
|
||||
|
||||
export const plugin: ESLint.Plugin = {
|
||||
name: 'custom',
|
||||
rules: {
|
||||
'no-empty-object-literal': noEmptyObjectLiteral,
|
||||
'no-import-dot': noImportDot,
|
||||
'no-useless-import-alias': noUselessImportAlias,
|
||||
},
|
||||
};
|
||||
|
||||
export const typedPlugin: ESLint.Plugin = {
|
||||
name: 'typed-custom',
|
||||
rules: {
|
||||
// @ts-expect-error type mismatch
|
||||
'restrict-template-expressions': restrictTemplateExpressions,
|
||||
},
|
||||
};
|
||||
|
@ -6,7 +6,12 @@ import {
|
||||
isTypeFlagSet,
|
||||
isTypeNeverType,
|
||||
} from '@typescript-eslint/type-utils';
|
||||
import { AST_NODE_TYPES, ESLintUtils, type TSESTree } from '@typescript-eslint/utils';
|
||||
import {
|
||||
AST_NODE_TYPES,
|
||||
ESLintUtils,
|
||||
ParserServicesWithTypeInformation,
|
||||
type TSESTree,
|
||||
} from '@typescript-eslint/utils';
|
||||
import { getParserServices } from '@typescript-eslint/utils/eslint-utils';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
@ -52,8 +57,14 @@ export default createRule<Option[], MessageId>({
|
||||
},
|
||||
defaultOptions: [defaultOption],
|
||||
create(context, [options]) {
|
||||
const services = getParserServices(context);
|
||||
if (!services.program) return {};
|
||||
let services: ParserServicesWithTypeInformation | undefined;
|
||||
try {
|
||||
services = getParserServices(context);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
if (!services?.program) return {};
|
||||
|
||||
const checker = services.program.getTypeChecker();
|
||||
const allowed = new Set(options.allow);
|
||||
|
@ -1,12 +1,13 @@
|
||||
import * as fs from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
import type { Middleware } from './middleware';
|
||||
import { jsdoc } from './presets/jsdoc';
|
||||
import { reactQuery, storybook, vitest } from './presets/misc';
|
||||
import { react, reactRefresh } from './presets/react';
|
||||
import { tailwind } from './presets/tailwind';
|
||||
import { testingLibrary } from './presets/testing-library';
|
||||
|
||||
const jsdoc = () => import('./presets/jsdoc');
|
||||
const tailwind = () => import('./presets/tailwind');
|
||||
const testingLibrary = () => import('./presets/testing-library');
|
||||
|
||||
const middlewares = {
|
||||
react,
|
||||
@ -17,6 +18,8 @@ const middlewares = {
|
||||
testingLibrary,
|
||||
jsdoc,
|
||||
vitest,
|
||||
} satisfies {
|
||||
[key: string]: Middleware;
|
||||
};
|
||||
|
||||
export const envs: {
|
||||
@ -62,9 +65,13 @@ export const envs: {
|
||||
export function getProjectDependencies() {
|
||||
const rootDir = process.cwd();
|
||||
|
||||
const pkgJsonPath = resolve(rootDir, 'package.json');
|
||||
const pkgJsonPath = path.resolve(rootDir, 'package.json');
|
||||
const pkgJson = fs.existsSync(pkgJsonPath)
|
||||
? JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'))
|
||||
? (JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')) as {
|
||||
dependencies?: Record<string, string>;
|
||||
devDependencies?: Record<string, string>;
|
||||
peerDependencies?: Record<string, string>;
|
||||
})
|
||||
: {};
|
||||
|
||||
return new Set(
|
259
src/index.ts
259
src/index.ts
@ -1,191 +1,104 @@
|
||||
/// <reference path="./modules.d.ts" />
|
||||
import './redirect';
|
||||
import type { ESLintConfig, Extends, Plugin, Rules } from '@aet/eslint-define-config';
|
||||
import type { ESLintUtils } from '@typescript-eslint/utils';
|
||||
import type { Rule } from 'eslint';
|
||||
import { uniq } from 'lodash';
|
||||
import type { FlatESLintConfig } from '@aet/eslint-define-config';
|
||||
import * as tsParser from '@typescript-eslint/parser';
|
||||
import importPlugin from 'eslint-plugin-import-x';
|
||||
import { uniq } from 'lodash-es';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
import { off } from './constants';
|
||||
import { checkEnv } from './env';
|
||||
import type { Middleware, MiddlewareConfig, MiddlewareFunctions } from './middleware';
|
||||
import { custom } from './presets/custom';
|
||||
import { checkEnv } from './environment';
|
||||
import { Middleware } from './middleware';
|
||||
import { eslintRules } from './presets/eslint';
|
||||
import { stylistic } from './presets/stylistic';
|
||||
import { importTypeScript } from './presets/typescript';
|
||||
import { unicorn } from './presets/unicorn';
|
||||
|
||||
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';
|
||||
import stylistic from './presets/stylistic';
|
||||
import { importRules, typescriptRules } from './presets/typescript';
|
||||
import unicorn from './presets/unicorn';
|
||||
|
||||
export { error, warn, off } from './constants';
|
||||
|
||||
declare global {
|
||||
interface Array<T> {
|
||||
filter(
|
||||
predicate: BooleanConstructor,
|
||||
): Exclude<T, null | undefined | false | '' | 0>[];
|
||||
}
|
||||
}
|
||||
|
||||
const unique = (...arr: (false | undefined | string | string[])[]): string[] => [
|
||||
...new Set(arr.flat(1).filter(Boolean)),
|
||||
];
|
||||
|
||||
const ensureArray = <T>(value?: T | T[]): T[] =>
|
||||
value == null ? [] : Array.isArray(value) ? value : [value];
|
||||
|
||||
type RuleLevel = 'error' | 'warn' | 'off' | 0 | 1 | 2;
|
||||
type RuleEntry<Options> = RuleLevel | [RuleLevel, Partial<Options>];
|
||||
|
||||
export interface LocalRuleOptions {
|
||||
/** Bans import from the specifier '.' and '..' and replaces it with '.+/index' */
|
||||
'custom/no-import-dot': RuleEntry<unknown>;
|
||||
/**
|
||||
* Enforce template literal expressions to be of `string` type
|
||||
* @see [restrict-template-expressions](https://typescript-eslint.io/rules/restrict-template-expressions)
|
||||
*/
|
||||
'custom/restrict-template-expressions': RuleEntry<{ allow: string[] }>;
|
||||
/** Ban assignment of empty object literals `{}` and replace them with `Object.create(null)` */
|
||||
'custom/no-empty-object-literal': RuleEntry<unknown>;
|
||||
/** Ban useless import alias */
|
||||
'custom/no-useless-import-alias': RuleEntry<unknown>;
|
||||
}
|
||||
|
||||
export type RuleOptions = Rules & Partial<LocalRuleOptions>;
|
||||
|
||||
export interface CustomRule {
|
||||
rule: () => Promise<{
|
||||
default: Rule.RuleModule | ESLintUtils.RuleModule<string, unknown[]>;
|
||||
}>;
|
||||
options?: RuleLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* ESLint Configuration.
|
||||
* @see [ESLint Configuration](https://eslint.org/docs/latest/user-guide/configuring/)
|
||||
*/
|
||||
export type InputConfig = Omit<ESLintConfig, 'rules'> & {
|
||||
/**
|
||||
* Rules.
|
||||
* @see [Rules](https://eslint.org/docs/latest/user-guide/configuring/rules)
|
||||
*/
|
||||
rules?: Partial<RuleOptions>;
|
||||
|
||||
/**
|
||||
* Glob pattern to find paths to custom rule files in JavaScript or TypeScript.
|
||||
* Note this must be a string literal or an array of string literals since
|
||||
* this is statically analyzed.
|
||||
*
|
||||
* Rules are prefixed with `custom/` and the file name is used as the rule name.
|
||||
*/
|
||||
customRuleFiles?: string | string[];
|
||||
|
||||
/**
|
||||
* Automatically detect project types, dependencies and deduct the plugins.
|
||||
* @default true
|
||||
*/
|
||||
export async function extendConfig({
|
||||
auto = true,
|
||||
middlewares: addMiddlewares = [],
|
||||
configs = [],
|
||||
}: {
|
||||
auto?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a ESLint config object.
|
||||
*
|
||||
* By default, it includes `["@typescript-eslint", "import-x", "prettier", "unicorn"]` configs.
|
||||
* Additional bundled plugins include:
|
||||
*
|
||||
* 1. [`react`](https://github.com/jsx-eslint/eslint-plugin-react#list-of-supported-rules)
|
||||
* (automatically enables
|
||||
* [`react-hooks`](https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks))
|
||||
* 2. [`react-refresh`](https://github.com/ArnaudBarre/eslint-plugin-react-refresh)
|
||||
* 3. [`jsx-a11y`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y#supported-rules)
|
||||
* 4. [`unicorn`](https://github.com/sindresorhus/eslint-plugin-unicorn#rules)
|
||||
* 5. [`n`](https://github.com/eslint-community/eslint-plugin-n#-rules) (Node.js specific,
|
||||
* requires `minimatch`)
|
||||
* 6. [`jsdoc`](https://github.com/gajus/eslint-plugin-jsdoc#rules)
|
||||
*
|
||||
* Non bundled:
|
||||
* 1. [`graphql`](https://the-guild.dev/graphql/eslint/rules)
|
||||
*
|
||||
* @param of Configuration options.
|
||||
* @returns ESLint configuration object.
|
||||
*/
|
||||
export function extendConfig(
|
||||
of: InputConfig & {
|
||||
middlewares?: Middleware[];
|
||||
} = {
|
||||
middlewares: [],
|
||||
},
|
||||
): ESLintConfig {
|
||||
const {
|
||||
auto = true,
|
||||
plugins: _plugins = [],
|
||||
settings = {},
|
||||
rules,
|
||||
extends: _extends,
|
||||
overrides,
|
||||
customRuleFiles,
|
||||
parserOptions,
|
||||
middlewares: _middlewares = [],
|
||||
...rest
|
||||
} = of;
|
||||
|
||||
const plugins: Plugin[] = [..._plugins];
|
||||
const extend: Extends[] = ensureArray(_extends);
|
||||
|
||||
if (customRuleFiles != null) {
|
||||
plugins.push('local');
|
||||
}
|
||||
|
||||
middlewares?: Middleware[];
|
||||
configs: FlatESLintConfig[];
|
||||
}): Promise<FlatESLintConfig[]> {
|
||||
const middlewares: Middleware[] = uniq([
|
||||
importTypeScript,
|
||||
unicorn,
|
||||
custom,
|
||||
stylistic,
|
||||
() => import('./presets/custom'),
|
||||
...(auto ? checkEnv() : []),
|
||||
..._middlewares,
|
||||
...addMiddlewares,
|
||||
]);
|
||||
|
||||
const result: MiddlewareConfig = {
|
||||
root: true,
|
||||
plugins: unique('custom', plugins),
|
||||
env: { node: true, browser: true, es2023: true },
|
||||
reportUnusedDisableDirectives: true,
|
||||
parserOptions: {
|
||||
project: true,
|
||||
...parserOptions,
|
||||
const result: FlatESLintConfig[] = [
|
||||
{ rules: eslintRules }, //
|
||||
...tseslint.configs.recommendedTypeChecked,
|
||||
importPlugin.flatConfigs.recommended,
|
||||
importPlugin.flatConfigs.react,
|
||||
importPlugin.flatConfigs.typescript,
|
||||
...unicorn,
|
||||
stylistic,
|
||||
{
|
||||
files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
parser: tsParser,
|
||||
projectService: true,
|
||||
ecmaVersion: 'latest',
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
'import-x/parsers': {
|
||||
'@typescript-eslint/parser': ['.ts', '.tsx', '.mts', '.cts'],
|
||||
},
|
||||
'import-x/resolver': {
|
||||
typescript: true,
|
||||
node: true,
|
||||
},
|
||||
'import-x/core-modules': ['node:sqlite'],
|
||||
},
|
||||
ignores: ['eslint.config.cjs'],
|
||||
rules: {
|
||||
...importRules,
|
||||
...typescriptRules,
|
||||
},
|
||||
},
|
||||
ignorePatterns: [],
|
||||
globals: {},
|
||||
extends: ['eslint:recommended', 'prettier', ...(extend as string[])],
|
||||
settings,
|
||||
overrides: [
|
||||
{ files: ['repl.ts', 'scripts/**/*.ts'], rules: { 'no-console': off } },
|
||||
...(overrides ?? []),
|
||||
],
|
||||
rules: { ...eslintRules },
|
||||
...rest,
|
||||
};
|
||||
{
|
||||
files: ['*.js', '*.mjs', '*.cjs', '*.jsx'],
|
||||
...tseslint.configs.disableTypeChecked,
|
||||
rules: {
|
||||
'import-x/no-commonjs': off,
|
||||
'import-x/unambiguous': off,
|
||||
'@typescript-eslint/no-require-imports': off,
|
||||
'typed-custom/restrict-template-expressions': off,
|
||||
...tseslint.configs.disableTypeChecked.rules,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.d.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/consistent-type-imports': off,
|
||||
'import-x/unambiguous': off,
|
||||
},
|
||||
},
|
||||
] as FlatESLintConfig[];
|
||||
|
||||
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);
|
||||
for (const middleware of middlewares) {
|
||||
let fn = await middleware();
|
||||
if ('default' in fn) {
|
||||
fn = fn.default;
|
||||
}
|
||||
if (Array.isArray(fn)) {
|
||||
result.push(...(fn as FlatESLintConfig[]).flat(Infinity));
|
||||
} else {
|
||||
result.push(fn as unknown as FlatESLintConfig);
|
||||
}
|
||||
}
|
||||
|
||||
result.plugins = unique(result.plugins);
|
||||
result.extends = unique(result.extends);
|
||||
|
||||
Object.assign(result.rules, rules);
|
||||
if (configs) {
|
||||
result.push(...configs);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { installPackage } from '@antfu/install-pkg';
|
||||
import { uniq } from 'lodash';
|
||||
import { uniq } from 'lodash-es';
|
||||
|
||||
import { envs, getProjectDependencies } from './env';
|
||||
import { envs, getProjectDependencies } from './environment';
|
||||
|
||||
const deps = getProjectDependencies();
|
||||
const packages = uniq(
|
||||
|
@ -1,83 +0,0 @@
|
||||
import * as fs from 'node:fs';
|
||||
import { basename, extname, isAbsolute, resolve } from 'node:path';
|
||||
|
||||
import type { ESLint } from 'eslint';
|
||||
import { parseModule } from 'esprima';
|
||||
import query from 'esquery';
|
||||
import type { Node, Property } from 'estree';
|
||||
import { glob } from 'fast-glob';
|
||||
|
||||
// https://github.com/gulpjs/interpret
|
||||
const transpilers = [
|
||||
'esbuild-register',
|
||||
'tsx',
|
||||
'ts-node/register/transpile-only',
|
||||
'@swc/register',
|
||||
'sucrase/register',
|
||||
'@babel/register',
|
||||
'coffeescript/register',
|
||||
];
|
||||
|
||||
function tryRequire() {
|
||||
for (const candidate of transpilers) {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
require(candidate);
|
||||
return;
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
const unwrapDefault = <T = any>(module: any): T => module.default ?? module;
|
||||
|
||||
const plugin: ESLint.Plugin = {
|
||||
rules: {},
|
||||
};
|
||||
|
||||
function hydrateESTreeNode(n: Node): any {
|
||||
switch (n.type) {
|
||||
case 'Literal':
|
||||
return n.value;
|
||||
case 'ArrayExpression':
|
||||
return n.elements.filter(Boolean).map(hydrateESTreeNode);
|
||||
default:
|
||||
throw new Error(`Unsupported node type: ${n.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
function parseConfigFile(js: string) {
|
||||
const [node] = query(
|
||||
parseModule(js),
|
||||
'CallExpression[callee.name="extendConfig"] > ObjectExpression > Property[key.name="customRuleFiles"]',
|
||||
);
|
||||
return hydrateESTreeNode((node as Property).value);
|
||||
}
|
||||
|
||||
function main() {
|
||||
const rootDir = process.cwd();
|
||||
|
||||
const eslintConfigFile = ['.eslintrc.js', '.eslintrc.cjs']
|
||||
.map(file => resolve(rootDir, file))
|
||||
.find(file => fs.existsSync(file));
|
||||
|
||||
if (!eslintConfigFile) return;
|
||||
|
||||
const eslintConfig = fs.readFileSync(eslintConfigFile, 'utf8');
|
||||
const customRuleFiles = parseConfigFile(eslintConfig);
|
||||
if (!customRuleFiles?.length) return;
|
||||
|
||||
tryRequire();
|
||||
for (let file of glob.sync(customRuleFiles)) {
|
||||
if (!isAbsolute(file)) {
|
||||
file = resolve(rootDir, file);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const module = unwrapDefault(require(file));
|
||||
const name = module.name ?? basename(file, extname(file));
|
||||
plugin.rules![name] = module;
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
export = plugin;
|
@ -1,31 +1,10 @@
|
||||
import type { ESLintConfig, KnownExtends, Settings } from '@aet/eslint-define-config';
|
||||
import type { Merge, SetRequired } from 'type-fest';
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
import type { RuleOptions } from './index';
|
||||
type MiddlewareResult = Linter.Config | Linter.Config[];
|
||||
|
||||
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 Middleware =
|
||||
| (() => Promise<MiddlewareResult>)
|
||||
| (() => Promise<{ default: MiddlewareResult }>);
|
||||
|
||||
export type MiddlewareConfig = Merge<
|
||||
SetRequired<ESLintConfig, OptionalObjectKey<ESLintConfig>>,
|
||||
{ extends: KnownExtends[] }
|
||||
>;
|
||||
|
||||
export interface MiddlewareFunctions {
|
||||
addRules(rules: Partial<RuleOptions>): void;
|
||||
addSettings(settings: Partial<Settings>): void;
|
||||
}
|
||||
|
||||
export type Middleware = (config: MiddlewareConfig, helpers: MiddlewareFunctions) => void;
|
||||
|
||||
export function defineMiddleware(middleware: Middleware): Middleware {
|
||||
return middleware;
|
||||
}
|
||||
// eslint-disable-next-line unicorn/prevent-abbreviations
|
||||
export const def = <T>(module: { default: T }): T => module.default;
|
||||
|
32
src/modules.d.ts
vendored
32
src/modules.d.ts
vendored
@ -1,25 +1,4 @@
|
||||
declare module '@typescript-eslint/utils' {
|
||||
export * from '@typescript-eslint/utils/dist/index';
|
||||
}
|
||||
declare module '@typescript-eslint/typescript-estree' {
|
||||
export * from '@typescript-eslint/typescript-estree/dist/index';
|
||||
}
|
||||
declare module '@typescript-eslint/type-utils' {
|
||||
export * from '@typescript-eslint/type-utils/dist/index';
|
||||
}
|
||||
declare module '@typescript-eslint/utils/eslint-utils' {
|
||||
export * from '@typescript-eslint/utils/dist/eslint-utils';
|
||||
}
|
||||
declare module '@typescript-eslint/utils/json-schema' {
|
||||
export * from '@typescript-eslint/utils/dist/json-schema';
|
||||
}
|
||||
declare module '@typescript-eslint/scope-manager' {
|
||||
export * from '@typescript-eslint/scope-manager/dist/index';
|
||||
}
|
||||
declare module '@typescript-eslint/types' {
|
||||
export * from '@typescript-eslint/types/dist/index';
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import-x/unambiguous
|
||||
declare module 'module' {
|
||||
export function _resolveFilename(
|
||||
request: string,
|
||||
@ -34,3 +13,12 @@ declare module 'module' {
|
||||
options?: Record<PropertyKey, unknown>,
|
||||
): string;
|
||||
}
|
||||
|
||||
declare module 'eslint-plugin-storybook' {
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
export const configs: {
|
||||
csf: Linter.Config;
|
||||
recommended: Linter.Config;
|
||||
};
|
||||
}
|
||||
|
@ -1,18 +1,20 @@
|
||||
import { error } from '../constants';
|
||||
import type { LocalRuleOptions } from '../index';
|
||||
import { defineMiddleware } from '../middleware';
|
||||
import { plugin, typedPlugin, LocalRuleOptions } from '../custom/index';
|
||||
import { defineConfig } from '../types';
|
||||
|
||||
const customRules: Partial<LocalRuleOptions> = {
|
||||
'custom/no-import-dot': error,
|
||||
'custom/no-useless-import-alias': error,
|
||||
};
|
||||
|
||||
export const custom = defineMiddleware((config, { addRules }) => {
|
||||
addRules(customRules);
|
||||
config.overrides.push({
|
||||
export default defineConfig([
|
||||
{
|
||||
plugins: { custom: plugin },
|
||||
rules: {
|
||||
'custom/no-import-dot': error,
|
||||
'custom/no-useless-import-alias': error,
|
||||
} satisfies Partial<LocalRuleOptions>,
|
||||
},
|
||||
{
|
||||
plugins: { 'typed-custom': typedPlugin },
|
||||
files: ['*.ts', '!*.d.ts'],
|
||||
rules: {
|
||||
'custom/restrict-template-expressions': error,
|
||||
},
|
||||
});
|
||||
});
|
||||
'typed-custom/restrict-template-expressions': error,
|
||||
} satisfies Partial<LocalRuleOptions>,
|
||||
},
|
||||
]);
|
||||
|
@ -1,12 +1,13 @@
|
||||
// Not usable. https://github.com/dimaMachina/graphql-eslint/issues/2178
|
||||
import type { GraphQLRulesObject } from '@aet/eslint-define-config/src/rules/graphql-eslint';
|
||||
import * as graphql from '@graphql-eslint/eslint-plugin';
|
||||
|
||||
import { defineMiddleware } from '../middleware';
|
||||
import { defineConfig } from '../types';
|
||||
|
||||
// https://the-guild.dev/graphql/eslint/rules
|
||||
const graphqlRules: Partial<GraphQLRulesObject> = {};
|
||||
|
||||
export const graphql = defineMiddleware((config, { addRules }) => {
|
||||
config.plugins.push('@graphql-eslint');
|
||||
config.extends.push('plugin:@graphql-eslint/recommended');
|
||||
addRules(graphqlRules);
|
||||
export default defineConfig({
|
||||
processor: graphql.processors.graphql,
|
||||
rules: graphqlRules,
|
||||
});
|
||||
|
@ -1,14 +1,14 @@
|
||||
import type { JSDocRulesObject } from '@aet/eslint-define-config/src/rules/jsdoc';
|
||||
import module from 'eslint-plugin-jsdoc';
|
||||
|
||||
import { off } from '../constants';
|
||||
import { defineMiddleware } from '../middleware';
|
||||
import { defineConfig } from '../types';
|
||||
|
||||
const jsdocRules: Partial<JSDocRulesObject> = {
|
||||
'jsdoc/require-jsdoc': off,
|
||||
};
|
||||
|
||||
export const jsdoc = defineMiddleware((config, { addRules }) => {
|
||||
config.plugins.push('jsdoc');
|
||||
config.extends.push('plugin:jsdoc/recommended-typescript');
|
||||
addRules(jsdocRules);
|
||||
});
|
||||
export default defineConfig([
|
||||
module.configs['flat/recommended-typescript'],
|
||||
{ rules: jsdocRules },
|
||||
]);
|
||||
|
@ -1,13 +1,17 @@
|
||||
import { defineMiddleware } from '../middleware';
|
||||
import { def } from '../middleware';
|
||||
import { defineConfig } from '../types';
|
||||
|
||||
export const storybook = defineMiddleware(config => {
|
||||
config.extends.push('plugin:storybook/recommended');
|
||||
});
|
||||
export async function storybook() {
|
||||
const { configs } = await import('eslint-plugin-storybook');
|
||||
return defineConfig([configs.recommended]);
|
||||
}
|
||||
|
||||
export const reactQuery = defineMiddleware(config => {
|
||||
config.extends.push('plugin:@tanstack/eslint-plugin-query/recommended');
|
||||
});
|
||||
export async function reactQuery() {
|
||||
const { configs } = def(await import('@tanstack/eslint-plugin-query'));
|
||||
return defineConfig(configs['flat/recommended']);
|
||||
}
|
||||
|
||||
export const vitest = defineMiddleware(config => {
|
||||
config.extends.push('plugin:vitest/recommended');
|
||||
});
|
||||
export async function vitest() {
|
||||
const { configs } = def(await import('eslint-plugin-vitest'));
|
||||
return defineConfig([configs.recommended]);
|
||||
}
|
||||
|
@ -2,35 +2,42 @@ import type { ReactRulesObject } from '@aet/eslint-define-config/src/rules/react
|
||||
import type { ReactRefreshRulesObject } from '@aet/eslint-define-config/src/rules/react-refresh';
|
||||
|
||||
import { error, off, warn } from '../constants';
|
||||
import { defineMiddleware } from '../middleware';
|
||||
import { def } from '../middleware';
|
||||
import { defineConfig } from '../types';
|
||||
|
||||
const reactRules: Partial<ReactRulesObject> = {
|
||||
'@eslint-react/no-missing-component-display-name': off,
|
||||
'@eslint-react/no-children-prop': error,
|
||||
'@eslint-react/no-leaked-conditional-rendering': error,
|
||||
};
|
||||
|
||||
export const react = defineMiddleware((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,
|
||||
export async function react() {
|
||||
const reactPlugin = def(await import('@eslint-react/eslint-plugin'));
|
||||
const a11y = def(await import('../../packages/eslint-plugin-jsx-a11y/src/index'));
|
||||
const hooks = await import('../../packages/eslint-plugin-react-hooks');
|
||||
|
||||
return defineConfig([
|
||||
reactPlugin.configs['recommended-type-checked'] as any,
|
||||
hooks.flatConfigs.recommended,
|
||||
a11y.flatConfigs.recommended,
|
||||
{
|
||||
files: ['*.tsx'],
|
||||
rules: reactRules,
|
||||
},
|
||||
});
|
||||
addRules(reactRules);
|
||||
});
|
||||
]);
|
||||
}
|
||||
|
||||
const refreshRules: Partial<ReactRefreshRulesObject> = {
|
||||
'react-refresh/only-export-components': [warn, { allowConstantExport: true }],
|
||||
};
|
||||
|
||||
export const reactRefresh = defineMiddleware((config, { addRules }) => {
|
||||
config.plugins.push('react-refresh');
|
||||
addRules(refreshRules);
|
||||
});
|
||||
export async function reactRefresh() {
|
||||
const refreshPlugin = def(await import('eslint-plugin-react-refresh'));
|
||||
return defineConfig({
|
||||
plugins: {
|
||||
// @ts-expect-error no types
|
||||
'react-refresh': refreshPlugin,
|
||||
},
|
||||
rules: refreshRules,
|
||||
});
|
||||
}
|
||||
|
@ -1,21 +1,25 @@
|
||||
import type { StylisticRulesObject } from '@aet/eslint-define-config/src/rules/stylistic';
|
||||
import stylistic from '@stylistic/eslint-plugin';
|
||||
|
||||
import { error } from '../constants';
|
||||
import { defineMiddleware } from '../middleware';
|
||||
import { defineConfig } from '../types';
|
||||
|
||||
const stylisticRules: Partial<StylisticRulesObject> = {
|
||||
'@stylistic/spaced-comment': [
|
||||
'stylistic/spaced-comment': [
|
||||
error,
|
||||
'always',
|
||||
{
|
||||
markers: ['/', '#', '@'],
|
||||
// allow /*@__PURE__*/
|
||||
block: { exceptions: ['@'] },
|
||||
},
|
||||
// allow /*@__PURE__*/
|
||||
{ markers: ['/', '#', '@'], block: { exceptions: ['@'] } },
|
||||
],
|
||||
'stylistic/jsx-sort-props': [
|
||||
error,
|
||||
{ callbacksLast: true, shorthandFirst: true, multiline: 'last' },
|
||||
],
|
||||
};
|
||||
|
||||
export const stylistic = defineMiddleware((config, { addRules }) => {
|
||||
config.plugins.push('@stylistic');
|
||||
addRules(stylisticRules);
|
||||
export default defineConfig({
|
||||
plugins: {
|
||||
stylistic,
|
||||
},
|
||||
rules: stylisticRules,
|
||||
});
|
||||
|
@ -1,13 +1,14 @@
|
||||
import type { TailwindRulesObject } from '@aet/eslint-define-config/src/rules/tailwind';
|
||||
import tailwind from 'eslint-plugin-tailwindcss';
|
||||
|
||||
import { off } from '../constants';
|
||||
import { defineMiddleware } from '../middleware';
|
||||
import { defineConfig } from '../types';
|
||||
|
||||
const tailwindRules: Partial<TailwindRulesObject> = {
|
||||
'tailwindcss/no-custom-classname': off,
|
||||
} as const;
|
||||
|
||||
export const tailwind = defineMiddleware((config, { addRules }) => {
|
||||
config.extends.push('plugin:tailwindcss/recommended');
|
||||
addRules(tailwindRules);
|
||||
});
|
||||
export default defineConfig([
|
||||
...tailwind.configs['flat/recommended'],
|
||||
{ rules: tailwindRules },
|
||||
]);
|
||||
|
@ -1,13 +1,15 @@
|
||||
import type { TestingLibraryRulesObject } from '@aet/eslint-define-config/src/rules/testing-library';
|
||||
import testingLibrary from 'eslint-plugin-testing-library';
|
||||
|
||||
import { defineMiddleware } from '../middleware';
|
||||
import { defineConfig } from '../types';
|
||||
|
||||
const testingLibraryRules: Partial<TestingLibraryRulesObject> = {};
|
||||
|
||||
export const testingLibrary = defineMiddleware((config, { addRules }) => {
|
||||
config.overrides.push({
|
||||
files: ['**/*.(spec|test).{ts,tsx}'],
|
||||
plugins: ['plugin:testing-library/react'],
|
||||
});
|
||||
addRules(testingLibraryRules);
|
||||
export default defineConfig({
|
||||
files: ['**/*.(spec|test).{ts,tsx}'],
|
||||
...testingLibrary.configs['flat/react'],
|
||||
rules: {
|
||||
...testingLibrary.configs['flat/react'].rules,
|
||||
...testingLibraryRules,
|
||||
},
|
||||
});
|
||||
|
@ -2,9 +2,8 @@ import type { ImportXRulesObject } from '@aet/eslint-define-config/src/rules/imp
|
||||
import type { TypeScriptRulesObject } from '@aet/eslint-define-config/src/rules/typescript-eslint';
|
||||
|
||||
import { error, off, warn } from '../constants';
|
||||
import { defineMiddleware } from '../middleware';
|
||||
|
||||
const importRules: Partial<ImportXRulesObject> = {
|
||||
export const importRules: Partial<ImportXRulesObject> = {
|
||||
'import-x/first': error,
|
||||
'import-x/no-absolute-path': error,
|
||||
'import-x/no-duplicates': warn,
|
||||
@ -20,7 +19,7 @@ const importRules: Partial<ImportXRulesObject> = {
|
||||
'import-x/unambiguous': error,
|
||||
};
|
||||
|
||||
const typescriptRules: Partial<TypeScriptRulesObject> = {
|
||||
export const typescriptRules: Partial<TypeScriptRulesObject> = {
|
||||
'@typescript-eslint/ban-ts-comment': [
|
||||
error,
|
||||
{
|
||||
@ -38,6 +37,10 @@ const typescriptRules: Partial<TypeScriptRulesObject> = {
|
||||
warn,
|
||||
{ accessibility: 'no-public' },
|
||||
],
|
||||
'@typescript-eslint/no-empty-object-type': [
|
||||
error,
|
||||
{ allowInterfaces: 'with-single-extends' },
|
||||
],
|
||||
'@typescript-eslint/no-empty-interface': [error, { allowSingleExtends: true }],
|
||||
'@typescript-eslint/no-explicit-any': off,
|
||||
'@typescript-eslint/no-misused-promises': [error, { checksVoidReturn: false }],
|
||||
@ -55,42 +58,3 @@ const typescriptRules: Partial<TypeScriptRulesObject> = {
|
||||
'@typescript-eslint/triple-slash-reference': off,
|
||||
'@typescript-eslint/unbound-method': off,
|
||||
};
|
||||
|
||||
export const importTypeScript = defineMiddleware((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', '*.config.js', '*.cjs', '*.mjs'],
|
||||
extends: ['plugin:@typescript-eslint/disable-type-checked'],
|
||||
rules: {
|
||||
'import-x/no-commonjs': off,
|
||||
'import-x/unambiguous': off,
|
||||
'@typescript-eslint/no-require-imports': off,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.d.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/consistent-type-imports': off,
|
||||
'import-x/unambiguous': off,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
addRules(importRules);
|
||||
addRules(typescriptRules);
|
||||
});
|
||||
|
@ -1,8 +1,9 @@
|
||||
/* eslint-disable unicorn/string-content */
|
||||
import type { UnicornRulesObject } from '@aet/eslint-define-config/src/rules/unicorn';
|
||||
import unicorn from 'eslint-plugin-unicorn';
|
||||
import globals from 'globals';
|
||||
|
||||
import { error, off, warn } from '../constants';
|
||||
import { defineMiddleware } from '../middleware';
|
||||
import { defineConfig } from '../types';
|
||||
|
||||
const suggest = (suggest: string) => ({ suggest, fix: false });
|
||||
|
||||
@ -99,13 +100,31 @@ const unicornRules: Partial<UnicornRulesObject> = {
|
||||
'unicorn/template-indent': warn,
|
||||
};
|
||||
|
||||
export const unicorn = defineMiddleware((config, { addRules }) => {
|
||||
config.plugins.push('unicorn');
|
||||
addRules(unicornRules);
|
||||
config.overrides.push({
|
||||
// export const unicorn = defineMiddleware((config, { addRules }) => {
|
||||
// config.plugins.push('unicorn');
|
||||
// addRules(unicornRules);
|
||||
// config.overrides.push({
|
||||
// files: ['*.test.ts', '*.test.tsx'],
|
||||
// rules: {
|
||||
// 'unicorn/no-useless-undefined': off,
|
||||
// },
|
||||
// });
|
||||
// });
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
languageOptions: {
|
||||
globals: globals.builtin,
|
||||
},
|
||||
plugins: {
|
||||
unicorn,
|
||||
},
|
||||
rules: unicornRules,
|
||||
},
|
||||
{
|
||||
files: ['*.test.ts', '*.test.tsx'],
|
||||
rules: {
|
||||
'unicorn/no-useless-undefined': off,
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
]);
|
||||
|
@ -1,24 +0,0 @@
|
||||
import Module from 'node:module';
|
||||
const { name } = [require][0]('./package.json');
|
||||
|
||||
const _resolveFilename = Module._resolveFilename;
|
||||
const alias = new Set([
|
||||
'eslint-import-resolver-typescript',
|
||||
'eslint-plugin-jsx-a11y',
|
||||
'eslint-plugin-local',
|
||||
'eslint-plugin-n',
|
||||
'eslint-plugin-react-hooks',
|
||||
'eslint-plugin-custom',
|
||||
]);
|
||||
|
||||
type CDR<T> = T extends [any, ...infer R] ? R : [];
|
||||
|
||||
Module._resolveFilename = function (
|
||||
request: string,
|
||||
...args: CDR<Parameters<typeof _resolveFilename>>
|
||||
) {
|
||||
if (alias.has(request)) {
|
||||
request = `${name}/${request}`;
|
||||
}
|
||||
return _resolveFilename(request, ...args);
|
||||
};
|
@ -1,5 +1,5 @@
|
||||
import type { Rule } from 'eslint';
|
||||
import type { ESLintUtils } from '@typescript-eslint/utils';
|
||||
import type { Rule, Linter } from 'eslint';
|
||||
|
||||
export function defineRules(rules: {
|
||||
[ruleName: string]: Rule.RuleModule | ESLintUtils.RuleModule<string, unknown[]>;
|
||||
@ -7,6 +7,13 @@ export function defineRules(rules: {
|
||||
return rules;
|
||||
}
|
||||
|
||||
export function defineConfig(config: Linter.Config): Linter.Config;
|
||||
export function defineConfig(config: Linter.Config[]): Linter.Config[];
|
||||
|
||||
export function defineConfig(config: Linter.Config | Linter.Config[]) {
|
||||
return config;
|
||||
}
|
||||
|
||||
export function defineRule({
|
||||
name,
|
||||
create,
|
||||
|
18
src/types/eslint-plugin-react-refresh.d.ts
vendored
Normal file
18
src/types/eslint-plugin-react-refresh.d.ts
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
// eslint-disable-next-line import-x/unambiguous
|
||||
declare module 'eslint-plugin-react-refresh' {
|
||||
import type { TSESLint } from '@typescript-eslint/utils';
|
||||
|
||||
export const rules: {
|
||||
'only-export-components': TSESLint.RuleModule<
|
||||
'exportAll' | 'namedExport' | 'anonymousExport' | 'noExport' | 'localComponents',
|
||||
| []
|
||||
| [
|
||||
{
|
||||
allowConstantExport?: boolean;
|
||||
checkJS?: boolean;
|
||||
allowExportNames?: string[];
|
||||
},
|
||||
]
|
||||
>;
|
||||
};
|
||||
}
|
55
src/types/eslint-plugin-testing-library.d.ts
vendored
Normal file
55
src/types/eslint-plugin-testing-library.d.ts
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
// eslint-disable-next-line import-x/unambiguous
|
||||
declare module 'eslint-plugin-testing-library' {
|
||||
import type { Rule, Linter } from 'eslint';
|
||||
|
||||
// 6.3.0
|
||||
const plugin: {
|
||||
meta: {
|
||||
name: 'eslint-plugin-testing-library';
|
||||
version: '6.3.0';
|
||||
};
|
||||
configs: {
|
||||
dom: Linter.BaseConfig;
|
||||
angular: Linter.BaseConfig;
|
||||
react: Linter.BaseConfig;
|
||||
vue: Linter.BaseConfig;
|
||||
marko: Linter.BaseConfig;
|
||||
'flat/dom': Linter.Config;
|
||||
'flat/angular': Linter.Config;
|
||||
'flat/react': Linter.Config;
|
||||
'flat/vue': Linter.Config;
|
||||
'flat/marko': Linter.Config;
|
||||
};
|
||||
rules: {
|
||||
'await-async-events': Rule.RuleModule;
|
||||
'await-async-queries': Rule.RuleModule;
|
||||
'await-async-utils': Rule.RuleModule;
|
||||
'consistent-data-testid': Rule.RuleModule;
|
||||
'no-await-sync-events': Rule.RuleModule;
|
||||
'no-await-sync-queries': Rule.RuleModule;
|
||||
'no-container': Rule.RuleModule;
|
||||
'no-debugging-utils': Rule.RuleModule;
|
||||
'no-dom-import': Rule.RuleModule;
|
||||
'no-global-regexp-flag-in-query': Rule.RuleModule;
|
||||
'no-manual-cleanup': Rule.RuleModule;
|
||||
'no-node-access': Rule.RuleModule;
|
||||
'no-promise-in-fire-event': Rule.RuleModule;
|
||||
'no-render-in-lifecycle': Rule.RuleModule;
|
||||
'no-unnecessary-act': Rule.RuleModule;
|
||||
'no-wait-for-multiple-assertions': Rule.RuleModule;
|
||||
'no-wait-for-side-effects': Rule.RuleModule;
|
||||
'no-wait-for-snapshot': Rule.RuleModule;
|
||||
'prefer-explicit-assert': Rule.RuleModule;
|
||||
'prefer-find-by': Rule.RuleModule;
|
||||
'prefer-implicit-assert': Rule.RuleModule;
|
||||
'prefer-presence-queries': Rule.RuleModule;
|
||||
'prefer-query-by-disappearance': Rule.RuleModule;
|
||||
'prefer-query-matchers': Rule.RuleModule;
|
||||
'prefer-screen-queries': Rule.RuleModule;
|
||||
'prefer-user-event': Rule.RuleModule;
|
||||
'render-result-naming-convention': Rule.RuleModule;
|
||||
};
|
||||
};
|
||||
|
||||
export = plugin;
|
||||
}
|
Reference in New Issue
Block a user