Compare commits

..

1 Commits

Author SHA1 Message Date
179cf83891 Inline repo 2024-04-19 21:42:48 -04:00
108 changed files with 15483 additions and 3975 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 }]
}
}

2
.gitignore vendored
View File

@ -1,6 +1,4 @@
drafts
!/packages/eslint-plugin-react-hooks
/packages/eslint-define-config
/react
src/types/rules

View File

@ -4,8 +4,6 @@ Personal ESLint config. Guaranteed to have no useless polyfills.
## flat config support
- ⏱️ [a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/issues/978)
- [import](https://github.com/import-js/eslint-plugin-import/issues/2556)
- ✅ [react](https://github.com/jsx-eslint/eslint-plugin-react/pull/3429)
- [unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn/pull/1886)
- ❌ [import](https://github.com/un-ts/eslint-plugin-import-x/issues/29)
- ❌ [jsx-a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/issues/978, supports flat config)
- ⏱️ [a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/pull/891)

14
dist/eslint-plugin-import/index.d.ts vendored Normal file
View File

@ -0,0 +1,14 @@
import type { Linter } from 'eslint';
export const rules: Readonly<Linter.RulesRecord>;
export const configs: {
recommended: Linter.BaseConfig;
errors: Linter.BaseConfig;
warnings: Linter.BaseConfig;
'stage-0': Linter.BaseConfig;
react: Linter.BaseConfig;
'react-native': Linter.BaseConfig;
electron: Linter.BaseConfig;
typescript: Linter.BaseConfig;
};

9
dist/eslint-plugin-react/index.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
import type { Linter } from 'eslint';
export const deprecatedRules: Readonly<Linter.RulesRecord>;
export const configs: {
recommended: Linter.BaseConfig;
all: Linter.BaseConfig;
'jsx-runtime': Linter.BaseConfig;
};

35
dist/index.d.ts vendored
View File

@ -1,19 +1,12 @@
// Generated by dts-bundle-generator v9.4.0
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';
import { ESLintConfig, Rules } from 'eslint-define-config';
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,
@ -39,7 +32,6 @@ 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/)
@ -49,32 +41,18 @@ export type InputConfig = Omit<ESLintConfig, "rules"> & {
* Rules.
* @see [Rules](https://eslint.org/docs/latest/user-guide/configuring/rules)
*/
rules?: Partial<RuleOptions>;
rules?: 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.
*/
customRuleFiles?: string | string[];
/**
* Automatically detect project types, dependencies and deduct the plugins.
*/
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.
*
* By default, it includes `["@typescript-eslint", "import-x", "prettier", "unicorn"]` configs.
* By default, it includes `["@typescript-eslint", "import", "prettier"]` configs.
* Additional bundled plugins include:
*
* 1. [`react`](https://github.com/jsx-eslint/eslint-plugin-react#list-of-supported-rules)
@ -83,15 +61,12 @@ export interface MiddlewareFunctions {
* 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`)
* 5. [`n`](https://github.com/eslint-community/eslint-plugin-n#-rules) (Node.js specific)
* 6. [`jsdoc`](https://github.com/gajus/eslint-plugin-jsdoc#rules)
*
* Non bundled:
* 1. [`graphql`](https://the-guild.dev/graphql/eslint/rules)
*/
export declare function extendConfig(of?: InputConfig & {
middlewares: Middleware[];
}): ESLintConfig;
export declare function extendConfig(of?: InputConfig): ESLintConfig;
export {};

69
dist/package.json vendored
View File

@ -1,67 +1,60 @@
{
"name": "@aet/eslint-rules",
"version": "1.0.1-beta.15",
"version": "0.0.24-beta.1",
"license": "UNLICENSED",
"bin": {
"eslint-install": "install.js",
"eslint-print": "print-config.sh"
},
"peerDependencies": {
"eslint": "^8.57.0",
"typescript": "^5.4.4"
},
"dependencies": {
"@nolyfill/is-core-module": "^1.0.39",
"@aet/eslint-define-config": "^0.1.0-beta.16",
"@eslint-community/eslint-utils": "^4.4.0",
"@tanstack/eslint-plugin-query": "^5.51.15",
"@types/eslint": "^9.6.0",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"@typescript-eslint/type-utils": "^8.0.0",
"@typescript-eslint/utils": "^8.0.0",
"@eslint-react/eslint-plugin": "1.7.1",
"@types/eslint": "^8.56.9",
"@typescript-eslint/eslint-plugin": "^7.7.0",
"@typescript-eslint/parser": "^7.7.0",
"@typescript-eslint/type-utils": "^7.7.0",
"@typescript-eslint/utils": "^7.7.0",
"aria-query": "^5.3.0",
"axe-core": "^4.10.0",
"axobject-query": "4.1.0",
"axe-core": "^4.9.0",
"axobject-query": "^4.0.0",
"damerau-levenshtein": "1.0.8",
"debug": "^4.3.6",
"debug": "^4.3.4",
"doctrine": "^3.0.0",
"emoji-regex": "^10.3.0",
"enhanced-resolve": "^5.17.1",
"enhanced-resolve": "^5.16.0",
"eslint-config-prettier": "^9.1.0",
"eslint-define-config": "^1.24.1",
"eslint-import-resolver-node": "^0.3.9",
"eslint-module-utils": "^2.8.1",
"eslint-plugin-es-x": "^8.0.0",
"eslint-plugin-import-x": "^3.1.0",
"eslint-plugin-jsdoc": "^48.11.0",
"eslint-plugin-react-refresh": "^0.4.9",
"eslint-plugin-unicorn": "^55.0.0",
"eslint-plugin-es-x": "^7.6.0",
"eslint-plugin-jsdoc": "^48.2.3",
"eslint-plugin-unicorn": "^52.0.0",
"esprima": "^4.0.1",
"esquery": "^1.6.0",
"esquery": "^1.5.0",
"estraverse": "^5.3.0",
"fast-glob": "^3.3.2",
"get-tsconfig": "^4.7.6",
"is-bun-module": "^1.0.2",
"get-tsconfig": "^4.7.3",
"ignore": "^5.3.1",
"is-builtin-module": "^3.2.1",
"is-glob": "^4.0.3",
"language-tags": "^1.0.9",
"lodash": "^4.17.21",
"minimatch": "^10.0.1",
"semver": "^7.6.3"
"minimatch": "^9.0.4",
"resolve": "^2.0.0-next.5",
"semver": "^7.6.0",
"tsconfig-paths": "^4.2.0"
},
"overrides": {
"supports-preserve-symlinks-flag": "file:./overrides/supports-preserve-symlinks-flag",
"is-core-module": "file:./overrides/is-core-module"
},
"resolutions": {
"**/supports-preserve-symlinks-flag": "file:./overrides/supports-preserve-symlinks-flag",
"**/is-core-module": "file:./overrides/is-core-module"
},
"pnpm": {
"overrides": {
"is-core-module": "file:./overrides/is-core-module",
"supports-preserve-symlinks-flag": "file:./overrides/supports-preserve-symlinks-flag"
"supports-preserve-symlinks-flag": "file:./overrides/supports-preserve-symlinks-flag",
"is-core-module": "file:./overrides/is-core-module"
}
},
"overrides": {
"is-core-module": "file:./overrides/is-core-module",
"supports-preserve-symlinks-flag": "file:./overrides/supports-preserve-symlinks-flag"
},
"resolutions": {
"**/is-core-module": "file:./overrides/is-core-module",
"**/supports-preserve-symlinks-flag": "file:./overrides/supports-preserve-symlinks-flag"
}
}

View File

@ -1,2 +0,0 @@
#!/bin/bash
node -e "console.dir(require('./.eslintrc.js'), { depth: null })"

2
dist/types.d.ts vendored
View File

@ -6,7 +6,7 @@ import { Rule } from 'eslint';
export declare function defineRules(rules: {
[ruleName: string]: Rule.RuleModule | ESLintUtils.RuleModule<string, unknown[]>;
}): {
[ruleName: string]: Rule.RuleModule | ESLintUtils.RuleModule<string, unknown[], unknown, ESLintUtils.RuleListener>;
[ruleName: string]: Rule.RuleModule | ESLintUtils.RuleModule<string, unknown[], ESLintUtils.RuleListener>;
};
export declare function defineRule({ name, create, ...meta }: Rule.RuleMetaData & {
name?: string;

1130
dist/yarn.lock vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,53 +1,49 @@
{
"name": "@aet/eslint-configs",
"scripts": {
"build": "./scripts/build.ts",
"check-import": "./scripts/check-imports.ts",
"define": "/usr/local/bin/codium ./packages/eslint-define-config",
"do": "yarn build; (cd dist && ver bump && npm publish)"
"build": "./scripts/build-all.ts",
"build-types": "cd ./packages/eslint-define-config && ./scripts/index.ts",
"check-import": "./scripts/check-imports.ts"
},
"private": true,
"devDependencies": {
"@aet/eslint-define-config": "^0.1.0-beta.16",
"@antfu/install-pkg": "^0.3.3",
"@babel/core": "^7.25.2",
"@babel/plugin-transform-flow-strip-types": "^7.25.2",
"@babel/preset-env": "^7.25.3",
"@babel/core": "^7.24.4",
"@babel/plugin-transform-flow-strip-types": "^7.24.1",
"@babel/preset-env": "^7.24.4",
"@types/babel-plugin-macros": "^3.1.3",
"@types/babel__core": "^7.20.5",
"@types/eslint": "^9.6.0",
"@types/eslint": "^8.56.10",
"@types/esprima": "^4.0.6",
"@types/esquery": "^1.5.4",
"@types/esquery": "^1.5.3",
"@types/estree": "^1.0.5",
"@types/estree-jsx": "^1.0.5",
"@types/lodash": "^4.17.7",
"@types/node": "^22.1.0",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/type-utils": "^8.0.0",
"@typescript-eslint/types": "^8.0.0",
"@typescript-eslint/typescript-estree": "^8.0.0",
"@typescript-eslint/utils": "^8.0.0",
"@types/lodash": "^4.17.0",
"@types/node": "^20.12.7",
"@typescript-eslint/eslint-plugin": "7.7.0",
"@typescript-eslint/type-utils": "^7.7.0",
"@typescript-eslint/types": "^7.7.0",
"@typescript-eslint/typescript-estree": "^7.7.0",
"@typescript-eslint/utils": "^7.7.0",
"babel-plugin-macros": "^3.1.0",
"dts-bundle-generator": "9.4.0",
"esbuild": "0.23.0",
"dts-bundle-generator": "^9.5.0",
"esbin": "0.0.4",
"esbuild": "0.20.2",
"esbuild-plugin-alias": "^0.2.1",
"eslint": "8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import-x": "^3.1.0",
"eslint-define-config": "file:./src/types",
"esprima": "^4.0.1",
"esquery": "^1.6.0",
"esquery": "^1.5.0",
"fast-glob": "^3.3.2",
"find-cache-dir": "^5.0.0",
"json-schema-to-ts": "^3.1.0",
"json-schema-to-ts": "^3.0.1",
"lodash": "^4.17.21",
"nolyfill": "^1.0.39",
"minimatch": "^9.0.4",
"patch-package": "^8.0.0",
"picocolors": "^1.0.1",
"prettier": "^3.3.3",
"picocolors": "^1.0.0",
"prettier": "^3.2.5",
"prop-types": "^15.8.1",
"terser": "^5.31.3",
"type-fest": "^4.23.0",
"typescript": "^5.5.4"
"typescript": "^5.4.5"
},
"prettier": {
"arrowParens": "avoid",
@ -59,17 +55,15 @@
},
"pnpm": {
"overrides": {
"function-bind": "npm:@nolyfill/function-bind@^1",
"has-proto": "npm:@nolyfill/has-proto@^1",
"has-symbols": "npm:@nolyfill/has-symbols@^1",
"hasown": "npm:@nolyfill/hasown@^1",
"isarray": "npm:@nolyfill/isarray@^1",
"jsonify": "npm:@nolyfill/jsonify@^1",
"object-keys": "npm:@nolyfill/object-keys@^1",
"set-function-length": "npm:@nolyfill/set-function-length@^1",
"@babel/types": "7.25.2",
"is-core-module": "npm:@nolyfill/is-core-module@^1",
"json-stable-stringify": "npm:@nolyfill/json-stable-stringify@^1"
"function-bind": "npm:@nolyfill/function-bind@latest",
"has-proto": "npm:@nolyfill/has-proto@latest",
"has-symbols": "npm:@nolyfill/has-symbols@latest",
"hasown": "npm:@nolyfill/hasown@latest",
"isarray": "npm:@nolyfill/isarray@latest",
"jsonify": "npm:@nolyfill/jsonify@latest",
"object-keys": "npm:@nolyfill/object-keys@latest",
"set-function-length": "npm:@nolyfill/set-function-length@latest",
"@babel/types": "7.24.0"
}
}
}

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021-2023 Christopher Quadflieg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,144 @@
<p>
<a href="https://www.npmjs.com/package/eslint-define-config" target="_blank">
<img alt="NPM package" src="https://img.shields.io/npm/v/eslint-define-config.svg">
</a>
<a href="https://www.npmjs.com/package/eslint-define-config" target="_blank">
<img alt="Downloads" src="https://img.shields.io/npm/dt/eslint-define-config.svg">
</a>
<a href="https://github.com/eslint-types/eslint-define-config/actions/workflows/ci.yml">
<img alt="Build Status" src="https://github.com/eslint-types/eslint-define-config/actions/workflows/ci.yml/badge.svg?branch=main">
</a>
<a href="https://github.com/eslint-types/eslint-define-config/blob/main/LICENSE">
<img alt="License: MIT" src="https://img.shields.io/github/license/eslint-types/eslint-define-config.svg">
</a>
<a href="https://prettier.io" target="_blank">
<img alt="Code Style: Prettier" src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg">
</a>
<a href="https://www.paypal.com/donate?hosted_button_id=L7GY729FBKTZY" target="_blank">
<img alt="Donate: PayPal" src="https://img.shields.io/badge/Donate-PayPal-blue.svg">
</a>
</p>
# eslint-define-config
Provide a `defineConfig` function for `.eslintrc.js`, and a `defineFlatConfig` function for `eslint.config.js` files.
> This project is written by a human and only partially automatically generated!
> Some rules are even enhanced by hand!
> Unfortunately, this has the disadvantage that not everything is immediately defined. For example, if a rule is not defined, it falls back to a basic definition.
> However, the advantage is that you get documentation for pretty much everything in the code and usually get a direct link to the respective plugin or eslint rule. The types are also strictly typed.
>
> So if you are missing something like a rule or a plugin that should also be supported or a rule definition is e.g. out of date, feel free to open an issue or PR for it.
# Installation
```bash
# add eslint and eslint-define-config to projects dev dependencies
npm add --save-dev eslint eslint-define-config
# or
yarn add --dev eslint eslint-define-config
# or
pnpm add --save-dev eslint eslint-define-config
```
# Usage
`.eslintrc.js`
```ts
// @ts-check
// To activate auto-suggestions for Rules of specific plugins, you need to add a `/// <reference types="eslint-plugin-PLUGIN_NAME/define-config-support" />` comment.
// ⚠️ This feature is very new and requires the support of the respective plugin owners.
/// <reference types="@typescript-eslint/eslint-plugin/define-config-support" />
const { defineConfig } = require('eslint-define-config');
module.exports = defineConfig({
root: true,
rules: {
// rules...
},
});
```
## Flat Config
`eslint.config.js`
```ts
// @ts-check
const { defineFlatConfig } = require('eslint-define-config');
module.exports = defineFlatConfig([
'eslint:recommended',
{
plugins: {
// plugins...
},
rules: {
// rules...
},
},
]);
```
# Why?
Improve your eslint configuration experience with:
- auto-suggestions
- type checking (Use `// @ts-check` at the first line in your `.eslintrc.js` or `eslint.config.js`)
- documentation
- deprecation warnings
<img src="https://user-images.githubusercontent.com/7195563/112484789-8a416480-8d7a-11eb-9337-d8b5bc16de17.png" alt="Image" width="600px"/>
## Video
_Click on the thumbnail to play the video_
<a href="https://user-images.githubusercontent.com/7195563/112726158-4a19e780-8f1c-11eb-8cc6-4ea6c100137f.mp4" target="_blank">
<img src="https://user-images.githubusercontent.com/7195563/112726343-30c56b00-8f1d-11eb-9b92-260c530caf1b.png" alt="Video" width="600px"/>
</a>
## Want to support your own plugin?
:warning: **This feature is very new and requires the support of the respective plugin owners**
Add a `declare module` to your plugin package like this:
```ts
declare module 'eslint-define-config' {
export interface CustomRuleOptions {
/**
* Require consistently using either `T[]` or `Array<T>` for arrays.
*
* @see [array-type](https://typescript-eslint.io/rules/array-type)
*/
'@typescript-eslint/array-type': [
{
default?: 'array' | 'generic' | 'array-simple';
readonly?: 'array' | 'generic' | 'array-simple';
},
];
// ... more Rules
}
}
```
There are other interfaces that can be extended.
- `CustomExtends`
- `CustomParserOptions`
- `CustomParsers`
- `CustomPlugins`
- `CustomSettings`
# Credits
- [Proposal Idea](https://github.com/eslint/eslint/issues/14249)
- [Vite](https://github.com/vitejs/vite) and [Evan You](https://github.com/yyx990803) for the idea
- [@antfu](https://github.com/antfu) and his [tweet](https://twitter.com/antfu7/status/1365907188338753536)

View File

@ -0,0 +1,120 @@
{
"name": "eslint-define-config",
"version": "1.24.1",
"description": "Provide a defineConfig function for .eslintrc.js files",
"scripts": {
"clean": "rimraf coverage .eslintcache dist pnpm-lock.yaml node_modules",
"format": "prettier --cache --write .",
"lint:run": "eslint --cache --cache-strategy content --report-unused-disable-directives .",
"lint": "run-s build lint:run",
"typecheck": "vitest typecheck",
"ts-check": "tsc",
"test": "vitest",
"coverage": "vitest run --coverage",
"generate:rules": "tsx ./scripts/generate-rule-files/cli.ts",
"prepublishOnly": "pnpm run clean && pnpm install && pnpm run build",
"preflight": "pnpm install && run-s format lint ts-check test typecheck"
},
"type": "module",
"main": "dist/index.cjs",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs",
"default": "./dist/index.js"
}
},
"keywords": [
"config",
"configuration",
"define-config",
"eslint-config",
"eslint",
"eslintconfig",
"typed",
"typescript"
],
"author": {
"name": "Christopher Quadflieg",
"email": "chrissi92@hotmail.de",
"url": "https://github.com/Shinigami92"
},
"repository": {
"type": "git",
"url": "https://github.com/eslint-types/eslint-define-config.git"
},
"funding": [
{
"type": "github",
"url": "https://github.com/Shinigami92"
},
{
"type": "paypal",
"url": "https://www.paypal.com/donate/?hosted_button_id=L7GY729FBKTZY"
}
],
"bugs": "https://github.com/eslint-types/eslint-define-config/issues",
"license": "MIT",
"files": [
"dist",
"src",
"tsconfig.json"
],
"devDependencies": {
"@graphql-eslint/eslint-plugin": "~3.20.1",
"@intlify/eslint-plugin-vue-i18n": "~2.0.0",
"@poppinss/cliui": "~6.4.1",
"@types/dedent": "^0.7.2",
"@types/eslint": "~8.44.3",
"@types/json-schema": "~7.0.13",
"@types/node": "~20.8.3",
"@typescript-eslint/eslint-plugin": "~6.7.4",
"@typescript-eslint/parser": "~6.7.4",
"@vitest/coverage-v8": "~0.34.6",
"change-case": "~5.4.4",
"dedent": "^1.5.3",
"eslint-config-prettier": "~9.0.0",
"eslint-gitignore": "~0.1.0",
"eslint-plugin-deprecation": "~2.0.0",
"eslint-plugin-eslint-comments": "~3.2.0",
"eslint-plugin-import": "~2.28.1",
"eslint-plugin-inclusive-language": "~2.2.1",
"eslint-plugin-jsdoc": "~46.8.2",
"eslint-plugin-jsonc": "~2.9.0",
"eslint-plugin-jsx-a11y": "~6.7.1",
"eslint-plugin-mdx": "~3.1.5",
"eslint-plugin-n": "~16.1.0",
"eslint-plugin-node": "~11.1.0",
"eslint-plugin-prettier": "~5.0.0",
"eslint-plugin-promise": "~6.1.1",
"eslint-plugin-react": "~7.33.2",
"eslint-plugin-react-hooks": "~4.6.0",
"eslint-plugin-sonarjs": "~0.21.0",
"eslint-plugin-spellcheck": "~0.0.20",
"eslint-plugin-testing-library": "~6.0.2",
"eslint-plugin-unicorn": "~48.0.1",
"eslint-plugin-vitest": "~0.3.2",
"eslint-plugin-vue": "~9.17.0",
"eslint-plugin-vue-pug": "~0.6.0",
"eslint-plugin-yml": "~1.10.0",
"expect-type": "~0.17.3",
"graphql": "~16.8.1",
"json-schema": "~0.4.0",
"json-schema-to-ts": "~2.9.2",
"json-schema-to-typescript": "~13.1.1",
"npm-run-all": "~4.1.5",
"prettier-plugin-organize-imports": "^3.2.4",
"rimraf": "~5.0.5",
"tsup": "~7.2.0",
"vue-eslint-parser": "~9.3.2"
},
"packageManager": "pnpm@8.8.0",
"engines": {
"node": ">=18.0.0",
"npm": ">=9.0.0",
"pnpm": ">=8.6.0"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
diff --git b/lib/rules/eslint/no-constructor-return.d.ts a/lib/rules/eslint/no-constructor-return.d.ts
index fedeca4..3e1fd03 100644
--- b/lib/rules/eslint/no-constructor-return.d.ts
+++ a/lib/rules/eslint/no-constructor-return.d.ts
@@ -1,16 +1,9 @@
import type { RuleConfig } from '../rule-config';
-/**
- * Option.
- */
-export interface NoConstructorReturnOption {
- [k: string]: any;
-}
-
/**
* Options.
*/
-export type NoConstructorReturnOptions = NoConstructorReturnOption;
+export type NoConstructorReturnOptions = [];
/**
* Disallow returning value from constructor.

View File

@ -0,0 +1,14 @@
diff --git b/lib/rules/graphql-eslint/naming-convention.d.ts a/lib/rules/graphql-eslint/naming-convention.d.ts
index 60b228b..5e01373 100644
--- b/lib/rules/graphql-eslint/naming-convention.d.ts
+++ a/lib/rules/graphql-eslint/naming-convention.d.ts
@@ -78,8 +78,7 @@ export type NamingConventionOption =
VariableDefinition?: AsString | AsObject;
allowLeadingUnderscore?: boolean;
allowTrailingUnderscore?: boolean;
- /**
- */
+ } & {
[k: string]: AsString | AsObject;
},
];

View File

@ -0,0 +1,13 @@
diff --git b/lib/rules/node/file-extension-in-import.d.ts a/lib/rules/node/file-extension-in-import.d.ts
index 652b18d..7261252 100644
--- b/lib/rules/node/file-extension-in-import.d.ts
+++ a/lib/rules/node/file-extension-in-import.d.ts
@@ -5,7 +5,7 @@ import type { RuleConfig } from '../rule-config';
*/
export interface FileExtensionInImportConfig {
tryExtensions?: string[];
- [k: string]: 'always' | 'never';
+ [ext: `.${string}`]: 'always' | 'never';
}
/**

View File

@ -0,0 +1,23 @@
diff --git a/lib/rules/react/jsx-no-constructed-context-values.d.ts b/lib/rules/react/jsx-no-constructed-context-values.d.ts
index 410f060..e356693 100644
--- a/lib/rules/react/jsx-no-constructed-context-values.d.ts
+++ b/lib/rules/react/jsx-no-constructed-context-values.d.ts
@@ -1,17 +1,9 @@
import type { RuleConfig } from '../rule-config';
-/**
- * Option.
- */
-export interface JsxNoConstructedContextValuesOption {
- [k: string]: any;
-}
-
/**
* Options.
*/
-export type JsxNoConstructedContextValuesOptions =
- JsxNoConstructedContextValuesOption;
+export type JsxNoConstructedContextValuesOptions = [];
/**
* Disallows JSX context provider values from taking values that will cause needless rerenders.

View File

@ -0,0 +1,13 @@
diff --git a/lib/rules/react/jsx-props-no-spreading.d.ts b/lib/rules/react/jsx-props-no-spreading.d.ts
index c1e0069..ba1e1bc 100644
--- a/lib/rules/react/jsx-props-no-spreading.d.ts
+++ b/lib/rules/react/jsx-props-no-spreading.d.ts
@@ -8,8 +8,6 @@ export type JsxPropsNoSpreadingOption = {
custom?: 'enforce' | 'ignore';
exceptions?: string[];
[k: string]: any;
-} & {
- [k: string]: any;
};
/**

View File

@ -0,0 +1,21 @@
diff --git a/lib/rules/vitest/valid-title.d.ts b/lib/rules/vitest/valid-title.d.ts
index 160be76..834ac9b 100644
--- a/lib/rules/vitest/valid-title.d.ts
+++ b/lib/rules/vitest/valid-title.d.ts
@@ -7,15 +7,7 @@ export interface ValidTitleOption {
ignoreTypeOfDescribeName?: boolean;
allowArguments?: boolean;
disallowedWords?: string[];
- /**
- */
- [k: string]:
- | string
- | [string]
- | [string, string]
- | {
- [k: string]: string | [string] | [string, string];
- };
+ [k: string]: any;
}
/**

View File

@ -0,0 +1,178 @@
#!/usr/bin/env bun
import { Logger, colors as cliColors } from '@poppinss/cliui';
import { pascalCase } from 'change-case';
import type { Rule } from 'eslint';
import { existsSync, promises as fs } from 'node:fs';
import { join, resolve } from 'node:path';
import { format } from 'prettier';
import { buildJSDoc, prettierConfig, type Plugin } from './utils';
import { PLUGIN_REGISTRY, loadPlugin } from './plugins-map';
import { RuleFile } from './rule-file';
interface FailedRule {
ruleName: string;
err: unknown;
}
const logger = new Logger();
const colors = cliColors.ansi();
const getRuleName = (name: string) =>
name === 'ESLint' ? 'ESLintRule' : `${pascalCase(name)}Rule`;
const index: [string, string][] = [];
/**
* Generate the `index.d.ts` file for the plugin's rules that will re-export all rules.
*/
async function generateRuleIndexFile(
pluginDirectory: string,
{ rules, name, id }: Plugin,
failedRules: FailedRule[],
): Promise<void> {
const generatedRules = Object.keys(rules!).filter(
ruleName => !failedRules.some(failedRule => failedRule.ruleName === ruleName),
);
/**
* Build all the import statements for the rules.
*/
const rulesImports = generatedRules
.map(name => `import type { ${getRuleName(name)} } from './${name}';`)
.join('\n');
/**
* Build the exported type that is an intersection of all the rules.
*/
const rulesFinalIntersection = generatedRules
.map(name => `${getRuleName(name)}`)
.sort()
.join(' & ');
const pluginRulesType = `
${buildJSDoc([`All ${name} rules.`])}
export type ${name}Rules = ${rulesFinalIntersection};
`;
/**
* Write the final `index.d.ts` file.
*/
const fileContent = `
${rulesImports}
${pluginRulesType}
`;
const indexPath = join(pluginDirectory, 'index.d.ts');
await fs.writeFile(indexPath, await format(fileContent, prettierConfig));
index.push([`${name}Rules`, `./${id}/index`]);
}
/**
* Print a report after having generated rules files for a plugin.
*/
function printGenerationReport(
rules: Array<[string, Rule.RuleModule]>,
failedRules: FailedRule[],
): void {
const msg: string = ` ✅ Generated ${rules.length - failedRules.length} rules`;
logger.logUpdate(colors.green(msg));
logger.logUpdatePersist();
if (failedRules.length) {
logger.log(colors.red(` ❌ Failed ${failedRules.length} rules`));
failedRules.forEach(({ ruleName, err }) => {
logger.log(colors.red(` - ${ruleName}: ${String(err)}`));
});
}
logger.log('');
}
/**
* Generate a `.d.ts` file for each rule in the given plugin.
*/
async function generateRulesFiles(
plugin: Plugin,
pluginDirectory: string,
): Promise<{ failedRules: FailedRule[] }> {
const failedRules: FailedRule[] = [];
const pluginRules = plugin.rules;
if (!pluginRules) {
throw new Error(
`Plugin ${plugin.name} doesn't have any rules. Did you forget to load them?`,
);
}
const rules = Object.entries(pluginRules);
for (const [ruleName, rule] of rules) {
logger.logUpdate(colors.yellow(` Generating > ${ruleName}`));
try {
await RuleFile(plugin, pluginDirectory, ruleName, rule);
} catch (err) {
failedRules.push({ ruleName, err });
}
}
printGenerationReport(rules, failedRules);
return { failedRules };
}
const rulesDirectory = resolve(__dirname, '../../../src/types/rules');
/**
* If it doesn't exist, create the directory that will contain the plugin's rule files.
*/
async function createPluginDirectory(pluginName: string): Promise<string> {
const pluginDirectory = join(rulesDirectory, pluginName);
if (existsSync(pluginDirectory)) {
await fs.rm(pluginDirectory, { recursive: true, force: true });
}
await fs.mkdir(pluginDirectory, { mode: 0o755, recursive: true });
return pluginDirectory;
}
export interface RunOptions {
plugins?: string[];
targetDirectory?: string;
}
for (const plugin of PLUGIN_REGISTRY) {
logger.info(`Generating ${plugin.name} rules.`);
logger.logUpdate(colors.yellow(` Loading plugin > ${plugin.name}`));
const loadedPlugin = await loadPlugin(plugin);
const pluginDir = await createPluginDirectory(plugin.id);
const { failedRules } = await generateRulesFiles(loadedPlugin, pluginDir);
await generateRuleIndexFile(pluginDir, loadedPlugin, failedRules);
}
await fs.writeFile(
resolve(rulesDirectory, 'index.d.ts'),
await format(
[
'import type { RuleConfig } from "./rule-config";',
...index.map(([name, path]) => `import { ${name} } from ${JSON.stringify(path)};`),
'',
'/**',
' * Rules.',
' *',
' * @see [Rules](https://eslint.org/docs/user-guide/configuring/rules)',
' */',
'',
'export type Rules = Partial<',
...index.map(([name]) => ` ${name} &`),
' Record<string, RuleConfig>',
'>;',
].join('\n'),
prettierConfig,
),
);

View File

@ -0,0 +1,58 @@
import type { JSONSchema4 } from 'json-schema';
import { compile } from 'json-schema-to-typescript';
/**
* Remove unnecessary comments that are generated by `json-schema-to-typescript`.
*/
function cleanJsDoc(content: string): string {
const patterns = [
/\* This interface was referenced by .+ JSON-Schema definition/,
/\* via the `.+` "/,
];
return content
.split('\n')
.filter(line => !patterns.some(ignoredLine => ignoredLine.test(line)))
.join('\n');
}
/**
* Replace some types that are generated by `json-schema-to-typescript`.
*/
export function patchTypes(content: string): string {
const replacements: [RegExp, string][] = [
[
/\(string & \{\s*\[k: string\]: any\s*\} & \{\s*\[k: string\]: any\s*\}\)\[\]/,
'string[]',
],
];
for (const [pattern, replacement] of replacements) {
content = content.replace(pattern, replacement);
}
return content;
}
/**
* Generate a type from the given JSON schema.
*/
export async function generateTypeFromSchema(
schema: JSONSchema4,
typeName: string,
): Promise<string> {
schema = JSON.parse(
JSON.stringify(schema).replace(/#\/items\/0\/\$defs\//g, '#/$defs/'),
);
const result = await compile(schema, typeName, {
format: false,
bannerComment: '',
style: {
singleQuote: true,
trailingComma: 'all',
},
unknownAny: false,
});
return patchTypes(cleanJsDoc(result));
}

View File

@ -0,0 +1,145 @@
import type { Plugin, PluginRules } from './utils';
/**
* Map of plugins for which the script will generate rule files.
*/
export const PLUGIN_REGISTRY: Plugin[] = [
{
id: 'deprecation',
name: 'Deprecation',
module: () => import('eslint-plugin-deprecation'),
},
{
id: 'eslint',
name: 'ESLint',
module: () => import('eslint'),
},
{
id: 'typescript-eslint',
name: 'TypeScript',
prefix: '@typescript-eslint',
module: () => import('@typescript-eslint/eslint-plugin'),
},
{
id: 'import',
name: 'Import',
module: () => import('eslint-plugin-import'),
},
{
id: 'eslint-comments',
name: 'EslintComments',
module: () => import('eslint-plugin-eslint-comments'),
},
{
id: 'graphql-eslint',
name: 'GraphQL',
prefix: '@graphql-eslint',
module: () => import('@graphql-eslint/eslint-plugin'),
},
{
id: 'jsdoc',
name: 'JSDoc',
prefix: 'jsdoc',
module: () => import('eslint-plugin-jsdoc'),
},
{
id: 'jsonc',
name: 'Jsonc',
module: () => import('eslint-plugin-jsonc'),
},
{
id: 'jsx-a11y',
name: 'JsxA11y',
module: () => import('eslint-plugin-jsx-a11y'),
},
{
id: 'mdx',
name: 'Mdx',
module: () => import('eslint-plugin-mdx'),
},
{
id: 'n',
name: 'N',
module: () => import('eslint-plugin-n'),
},
{
id: 'node',
name: 'Node',
module: () => import('eslint-plugin-node'),
},
{
id: 'promise',
name: 'Promise',
module: () => import('eslint-plugin-promise'),
},
{
id: 'react',
name: 'React',
module: () => import('eslint-plugin-react'),
},
{
id: 'react-hooks',
name: 'ReactHooks',
module: () => import('eslint-plugin-react-hooks'),
},
{
id: 'sonarjs',
name: 'SonarJS',
prefix: 'sonarjs',
module: () => import('eslint-plugin-sonarjs'),
},
{
id: 'spellcheck',
name: 'Spellcheck',
module: () => import('eslint-plugin-spellcheck'),
},
{
id: 'testing-library',
name: 'TestingLibrary',
module: () => import('eslint-plugin-testing-library'),
},
{
id: 'unicorn',
name: 'Unicorn',
module: () => import('eslint-plugin-unicorn'),
},
{
id: 'vitest',
name: 'Vitest',
module: () => import('eslint-plugin-vitest'),
},
{
id: 'vue',
name: 'Vue',
module: () => import('eslint-plugin-vue'),
},
{
id: 'vue-i18n',
name: 'VueI18n',
prefix: '@intlify/vue-i18n',
module: () => import('@intlify/eslint-plugin-vue-i18n'),
},
{
id: 'vue-pug',
name: 'VuePug',
module: () => import('eslint-plugin-vue-pug'),
},
{
id: 'yml',
name: 'Yml',
module: () => import('eslint-plugin-yml'),
},
] as const;
export async function loadPlugin(plugin: Plugin): Promise<Plugin> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const mod: any = await plugin.module();
const rules: PluginRules =
plugin.name === 'ESLint'
? Object.fromEntries(
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
new mod.Linter().getRules().entries(),
)
: mod.rules ?? mod.default.rules;
return { ...plugin, rules };
}

View File

@ -0,0 +1,168 @@
import { Logger, colors as cliColors } from '@poppinss/cliui';
import { pascalCase, kebabCase } from 'change-case';
import type { Rule } from 'eslint';
import type { JSONSchema4 } from 'json-schema';
import { execSync } from 'node:child_process';
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { format } from 'prettier';
import { generateTypeFromSchema } from './json-schema-to-ts';
import { buildJSDoc, prettierConfig, type Plugin } from './utils';
const logger = new Logger();
const colors = cliColors.ansi();
/**
* Build the rule description to append to the JSDoc.
*/
function buildDescription(description = ''): string {
description = description.charAt(0).toUpperCase() + description.slice(1);
if (description.length > 0 && !description.endsWith('.')) {
description += '.';
}
return description.replace('*/', '');
}
export async function RuleFile(
plugin: Plugin,
pluginDirectory: string,
ruleName: string,
rule: Rule.RuleModule,
) {
let content = '';
const rulePath = resolve(pluginDirectory, `${ruleName}.d.ts`);
const ruleNamePascalCase = pascalCase(ruleName);
const schema: JSONSchema4 | JSONSchema4[] | undefined = rule.meta?.schema;
const isSchemaArray = Array.isArray(schema);
const mainSchema = isSchemaArray ? schema[0] : schema;
const sideSchema = isSchemaArray && schema.length > 1 ? schema[1] : undefined;
const thirdSchema = isSchemaArray && schema.length > 2 ? schema[2] : undefined;
/**
* Generate a JSDoc with the rule description and `@see` url.
*/
function generateTypeJsDoc(): string {
const { meta } = rule;
const seeDocLink: string = meta?.docs?.url
? `@see [${ruleName}](${meta.docs.url})`
: '';
return buildJSDoc([
buildDescription(rule.meta?.docs?.description),
' ',
rule.meta?.deprecated ? '@deprecated' : '',
rule.meta?.deprecated ? ' ' : '',
seeDocLink,
]);
}
/**
* Generate a type from a JSON schema and append it to the file content.
*/
async function appendJsonSchemaType(
schema: JSONSchema4,
comment: string,
): Promise<void> {
const type = await generateTypeFromSchema(schema, ruleNamePascalCase + comment);
const jsdoc = buildJSDoc([`${comment}.`]);
content += `\n${jsdoc}`;
content += `\n${type}\n`;
}
/**
* Scoped rule name ESLint config uses.
*/
function prefixedRuleName(): string {
const { prefix, name } = plugin;
const rulePrefix = (prefix ?? kebabCase(name)) + '/';
return name === 'ESLint' ? ruleName : `${rulePrefix}${ruleName}`;
}
/**
* Append the `import type { RuleConfig } from '../rule-config'` at the top of the file.
*/
const nestedDepth = ruleName.split('/').length;
content += `import type { RuleConfig } from '${'../'.repeat(nestedDepth)}rule-config'\n\n`;
/**
* Generate and append types for the rule schemas.
*/
if (thirdSchema) {
await appendJsonSchemaType(thirdSchema, 'Setting');
}
if (sideSchema) {
await appendJsonSchemaType(sideSchema, 'Config');
}
if (mainSchema) {
await appendJsonSchemaType(mainSchema, 'Option');
}
if (mainSchema) {
/**
* Append the rule type options to the file content.
*/
let type: string = '';
if (!isSchemaArray) {
type = `${ruleNamePascalCase}Option`;
} else if (thirdSchema) {
type = `[${ruleNamePascalCase}Option?, ${ruleNamePascalCase}Config?, ${ruleNamePascalCase}Setting?]`;
} else if (sideSchema) {
type = `[${ruleNamePascalCase}Option?, ${ruleNamePascalCase}Config?]`;
} else if (mainSchema) {
type = `[${ruleNamePascalCase}Option?]`;
}
content += buildJSDoc(['Options.']) + '\n';
content += `export type ${ruleNamePascalCase}Options = ${type}\n\n`;
}
/**
* Append the rule config type to the file content.
*/
content += generateTypeJsDoc() + '\n';
content += `export type ${ruleNamePascalCase}RuleConfig = RuleConfig<${mainSchema ? `${ruleNamePascalCase}Options` : '[]'}>;\n\n`;
/**
* Append the final rule interface to the file content.
*/
content += generateTypeJsDoc() + '\n';
content += `export interface ${ruleNamePascalCase}Rule {`;
content += `${generateTypeJsDoc()}\n`;
content += `'${prefixedRuleName()}': ${ruleNamePascalCase}RuleConfig;`;
content += '}';
content = await format(content, prettierConfig);
/**
* Create the directory of the rule file if it doesn't exist.
*/
const subPath = dirname(rulePath);
if (!existsSync(subPath)) {
mkdirSync(subPath, { recursive: true });
}
writeFileSync(rulePath, content);
/**
* Apply a patch to the generated content if a diff file exists for it.
*
* Must be called after `generate()`.
*/
const pathParts = rulePath.split('/');
const diffFile = resolve(
__dirname,
'./diffs/rules',
pathParts.at(-2) ?? '',
`${pathParts.at(-1) ?? ''}.diff`,
);
if (existsSync(diffFile)) {
logger.logUpdate(colors.yellow(` 🧹 Adjusting ${prefixedRuleName()}`));
logger.logUpdatePersist();
execSync(`git apply ${diffFile}`);
}
}

View File

@ -0,0 +1,24 @@
import type { Rule } from 'eslint';
import type { Config } from 'prettier';
export function buildJSDoc(content: string[]) {
return ['/**', ...content.filter(Boolean).map(line => ` * ${line}`), ' */'].join('\n');
}
export const prettierConfig: Config = {
plugins: ['prettier-plugin-organize-imports'],
parser: 'typescript',
singleQuote: true,
trailingComma: 'all',
};
export type MaybeArray<T> = T | T[];
export type PluginRules = Record<string, Rule.RuleModule>;
export interface Plugin {
id: string;
name: string;
prefix?: string;
module: () => Promise<any>;
rules?: PluginRules;
}

Submodule packages/eslint-import-resolver-typescript updated: c9b5626ee6...7a02ac08b5

Submodule packages/eslint-plugin-import added at f77ceb679d

Submodule packages/eslint-plugin-jsx-a11y updated: cca288b73a...0d5321a545

Submodule packages/eslint-plugin-n updated: 5aad5f1c41...eb11b5b35a

Submodule packages/eslint-plugin-react added at 4467db503e

View File

@ -8,20 +8,22 @@
/* eslint-disable no-for-of-loops/no-for-of-loops */
import type { Rule, Scope } from 'eslint';
import type {
FunctionDeclaration,
CallExpression,
Expression,
Super,
Node,
ArrayExpression,
ArrowFunctionExpression,
FunctionExpression,
SpreadElement,
Identifier,
VariableDeclarator,
MemberExpression,
CallExpression,
ChainExpression,
Pattern,
Expression,
FunctionDeclaration,
FunctionExpression,
Identifier,
MemberExpression,
Node,
OptionalMemberExpression,
Pattern,
SpreadElement,
Super,
TSAsExpression,
VariableDeclarator,
} from 'estree';
import type { FromSchema } from 'json-schema-to-ts';
import { __EXPERIMENTAL__ } from './index';
@ -109,7 +111,7 @@ const rule: Rule.RuleModule = {
*/
function visitFunctionWithDependencies(
node: ArrowFunctionExpression | FunctionExpression | FunctionDeclaration,
declaredDependenciesNode: SpreadElement | Expression,
declaredDependenciesNode: SpreadElement | Expression | undefined,
reactiveHook: Super | Expression,
reactiveHookName: string,
isEffect: boolean,
@ -128,7 +130,7 @@ const rule: Rule.RuleModule = {
' }\n' +
' fetchData();\n' +
`}, [someId]); // Or [] if effect doesn't need props or state\n\n` +
'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching',
'Learn more about data fetching with Hooks: https://react.dev/link/hooks-data-fetching',
});
}
@ -194,7 +196,7 @@ const rule: Rule.RuleModule = {
if (init == null) {
return false;
}
while (init.type === 'TSAsExpression') {
while (init.type === 'TSAsExpression' || init.type === 'AsExpression') {
init = init.expression;
}
// Detect primitive constants
@ -620,7 +622,11 @@ const rule: Rule.RuleModule = {
const declaredDependencies: DeclaredDependency[] = [];
const externalDependencies = new Set<string>();
if (declaredDependenciesNode.type !== 'ArrayExpression') {
const isArrayExpression = declaredDependenciesNode.type === 'ArrayExpression';
const isTSAsArrayExpression =
declaredDependenciesNode.type === 'TSAsExpression' &&
declaredDependenciesNode.expression.type === 'ArrayExpression';
if (!isArrayExpression && !isTSAsArrayExpression) {
// If the declared dependencies are not an array expression then we
// can't verify that the user provided the correct dependencies. Tell
// the user this in an error.
@ -633,7 +639,10 @@ const rule: Rule.RuleModule = {
'dependencies.',
});
} else {
declaredDependenciesNode.elements.forEach(declaredDependencyNode => {
const arrayExpression = isTSAsArrayExpression
? ((declaredDependenciesNode as TSAsExpression).expression as ArrayExpression)
: (declaredDependenciesNode as ArrayExpression);
arrayExpression.elements.forEach(declaredDependencyNode => {
// Skip elided elements.
if (declaredDependencyNode === null) {
return;
@ -1158,7 +1167,11 @@ const rule: Rule.RuleModule = {
const reactiveHook = node.callee as Identifier | MemberExpression;
const reactiveHookName = (getNodeWithoutReactNamespace(reactiveHook) as Identifier)
.name;
const declaredDependenciesNode = node.arguments[callbackIndex + 1];
const maybeNode = node.arguments[callbackIndex + 1];
const declaredDependenciesNode =
maybeNode && !(maybeNode.type === 'Identifier' && maybeNode.name === 'undefined')
? maybeNode
: undefined;
const isEffect = /Effect($|[^a-z])/g.test(reactiveHookName);
// Check whether a callback is supplied. If there is no callback supplied
@ -1203,6 +1216,15 @@ const rule: Rule.RuleModule = {
isEffect,
);
return; // Handled
case 'TSAsExpression':
visitFunctionWithDependencies(
callback.expression,
declaredDependenciesNode,
reactiveHook,
reactiveHookName,
isEffect,
);
return; // Handled
case 'Identifier':
if (!declaredDependenciesNode) {
// No deps, no problems.
@ -1545,7 +1567,7 @@ function getConstructionExpressionType(node: Node) {
}
return null;
case 'TypeCastExpression':
return getConstructionExpressionType(node.expression);
case 'AsExpression':
case 'TSAsExpression':
return getConstructionExpressionType(node.expression);
}
@ -1749,7 +1771,7 @@ function analyzePropertyChain(
}
}
function getNodeWithoutReactNamespace(node: Identifier | MemberExpression) {
function getNodeWithoutReactNamespace(node: Expression | Super) {
if (
node.type === 'MemberExpression' &&
node.object.type === 'Identifier' &&

View File

@ -24,10 +24,7 @@ import { __EXPERIMENTAL__ } from './index';
*/
function isHookName(s: string) {
if (__EXPERIMENTAL__) {
return s === 'use' || /^use[A-Z0-9]/.test(s);
}
return /^use[A-Z0-9]/.test(s);
return s === 'use' || /^use[A-Z0-9]/.test(s);
}
/**
@ -59,7 +56,7 @@ function isComponentName(node: Node) {
return node.type === 'Identifier' && /^[A-Z]/.test(node.name);
}
function isReactFunction(node: Expression | Super, functionName: string) {
function isReactFunction(node: Node, functionName: string) {
return (
(node as Identifier).name === functionName ||
(node.type === 'MemberExpression' &&
@ -115,10 +112,7 @@ function isUseEffectEventIdentifier(node: Node) {
}
function isUseIdentifier(node: Node) {
if (__EXPERIMENTAL__) {
return node.type === 'Identifier' && node.name === 'use';
}
return false;
return isReactFunction(node, 'use');
}
const rule: Rule.RuleModule = {

View File

@ -3,8 +3,8 @@
"version": 1,
"sources": {
"main": {
"repository": "git@github.com:facebook/react.git",
"commit": "899cb95f52cc83ab5ca1eb1e268c909d3f0961e7",
"repository": "https://github.com/facebook/react",
"commit": "0e0b69321a6fcfe8a3eaae3b1016beb110437b38",
"branch": "main"
}
}

View File

@ -1,10 +1,28 @@
diff --git a/package.json b/package.json
index 98370b5..da6cd9b 100644
--- a/package.json
+++ b/package.json
@@ -62,8 +62,7 @@
"typecov": "type-coverage"
},
"peerDependencies": {
- "eslint": "*",
- "eslint-plugin-import": "*"
+ "eslint": "*"
},
"dependencies": {
"debug": "^4.3.4",
diff --git a/tsconfig.json b/tsconfig.json
index 39c6900..6fd8822 100644
deleted file mode 100644
index a303861..0000000
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,5 +1,4 @@
{
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "extends": "@1stg/tsconfig/node16",
"compilerOptions": {
"module": "Node16",
"outDir": "./lib",
- "compilerOptions": {
- "module": "Node16",
- "outDir": "./lib"
- },
- "include": ["./src", "./shim.d.ts"]
-}

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,10 @@
diff --git a/src/index.js b/src/index.js
index 2fa185f..29d65d0 100644
index 7b931fe..eaea267 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,48 +1,90 @@
@@ -1,296 +1,344 @@
/* eslint-disable global-require */
-const flatConfigBase = require('./configs/flat-config-base');
-const legacyConfigBase = require('./configs/legacy-config-base');
-const { name, version } = require('../package.json');
+import flatConfigBase from './configs/flat-config-base';
+import legacyConfigBase from './configs/legacy-config-base';
+import { name, version } from '../package.json';
+
+// @ts-check
+import accessibleEmoji from './rules/accessible-emoji';
+import altText from './rules/alt-text';
+import anchorAmbiguousText from './rules/anchor-ambiguous-text';
@ -51,105 +45,577 @@ index 2fa185f..29d65d0 100644
+import scope from './rules/scope';
+import tabindexNoPositive from './rules/tabindex-no-positive';
const allRules = {
- 'accessible-emoji': require('./rules/accessible-emoji'),
- 'alt-text': require('./rules/alt-text'),
- 'anchor-ambiguous-text': require('./rules/anchor-ambiguous-text'),
- 'anchor-has-content': require('./rules/anchor-has-content'),
- 'anchor-is-valid': require('./rules/anchor-is-valid'),
- 'aria-activedescendant-has-tabindex': require('./rules/aria-activedescendant-has-tabindex'),
- 'aria-props': require('./rules/aria-props'),
- 'aria-proptypes': require('./rules/aria-proptypes'),
- 'aria-role': require('./rules/aria-role'),
- 'aria-unsupported-elements': require('./rules/aria-unsupported-elements'),
- 'autocomplete-valid': require('./rules/autocomplete-valid'),
- 'click-events-have-key-events': require('./rules/click-events-have-key-events'),
- 'control-has-associated-label': require('./rules/control-has-associated-label'),
- 'heading-has-content': require('./rules/heading-has-content'),
- 'html-has-lang': require('./rules/html-has-lang'),
- 'iframe-has-title': require('./rules/iframe-has-title'),
- 'img-redundant-alt': require('./rules/img-redundant-alt'),
- 'interactive-supports-focus': require('./rules/interactive-supports-focus'),
- 'label-has-associated-control': require('./rules/label-has-associated-control'),
- 'label-has-for': require('./rules/label-has-for'),
- lang: require('./rules/lang'),
- 'media-has-caption': require('./rules/media-has-caption'),
- 'mouse-events-have-key-events': require('./rules/mouse-events-have-key-events'),
- 'no-access-key': require('./rules/no-access-key'),
- 'no-aria-hidden-on-focusable': require('./rules/no-aria-hidden-on-focusable'),
- 'no-autofocus': require('./rules/no-autofocus'),
- 'no-distracting-elements': require('./rules/no-distracting-elements'),
- 'no-interactive-element-to-noninteractive-role': require('./rules/no-interactive-element-to-noninteractive-role'),
- 'no-noninteractive-element-interactions': require('./rules/no-noninteractive-element-interactions'),
- 'no-noninteractive-element-to-interactive-role': require('./rules/no-noninteractive-element-to-interactive-role'),
- 'no-noninteractive-tabindex': require('./rules/no-noninteractive-tabindex'),
- 'no-onchange': require('./rules/no-onchange'),
- 'no-redundant-roles': require('./rules/no-redundant-roles'),
- 'no-static-element-interactions': require('./rules/no-static-element-interactions'),
- 'prefer-tag-over-role': require('./rules/prefer-tag-over-role'),
- 'role-has-required-aria-props': require('./rules/role-has-required-aria-props'),
- 'role-supports-aria-props': require('./rules/role-supports-aria-props'),
- scope: require('./rules/scope'),
- 'tabindex-no-positive': require('./rules/tabindex-no-positive'),
+ 'accessible-emoji': accessibleEmoji,
+ 'alt-text': altText,
+ 'anchor-ambiguous-text': anchorAmbiguousText,
+ 'anchor-has-content': anchorHasContent,
+ 'anchor-is-valid': anchorIsValid,
+ 'aria-activedescendant-has-tabindex': ariaActivedescendantHasTabindex,
+ 'aria-props': ariaProps,
+ 'aria-proptypes': ariaProptypes,
+ 'aria-role': ariaRole,
+ 'aria-unsupported-elements': ariaUnsupportedElements,
+ 'autocomplete-valid': autocompleteValid,
+ 'click-events-have-key-events': clickEventsHaveKeyEvents,
+ 'control-has-associated-label': controlHasAssociatedLabel,
+ 'heading-has-content': headingHasContent,
+ 'html-has-lang': htmlHasLang,
+ 'iframe-has-title': iframeHasTitle,
+ 'img-redundant-alt': imgRedundantAlt,
+ 'interactive-supports-focus': interactiveSupportsFocus,
+ 'label-has-associated-control': labelHasAssociatedControl,
+ 'label-has-for': labelHasFor,
-module.exports = {
- rules: {
- 'accessible-emoji': require('./rules/accessible-emoji'),
- 'alt-text': require('./rules/alt-text'),
- 'anchor-ambiguous-text': require('./rules/anchor-ambiguous-text'),
- 'anchor-has-content': require('./rules/anchor-has-content'),
- 'anchor-is-valid': require('./rules/anchor-is-valid'),
- 'aria-activedescendant-has-tabindex': require('./rules/aria-activedescendant-has-tabindex'),
- 'aria-props': require('./rules/aria-props'),
- 'aria-proptypes': require('./rules/aria-proptypes'),
- 'aria-role': require('./rules/aria-role'),
- 'aria-unsupported-elements': require('./rules/aria-unsupported-elements'),
- 'autocomplete-valid': require('./rules/autocomplete-valid'),
- 'click-events-have-key-events': require('./rules/click-events-have-key-events'),
- 'control-has-associated-label': require('./rules/control-has-associated-label'),
- 'heading-has-content': require('./rules/heading-has-content'),
- 'html-has-lang': require('./rules/html-has-lang'),
- 'iframe-has-title': require('./rules/iframe-has-title'),
- 'img-redundant-alt': require('./rules/img-redundant-alt'),
- 'interactive-supports-focus': require('./rules/interactive-supports-focus'),
- 'label-has-associated-control': require('./rules/label-has-associated-control'),
- 'label-has-for': require('./rules/label-has-for'),
- lang: require('./rules/lang'),
- 'media-has-caption': require('./rules/media-has-caption'),
- 'mouse-events-have-key-events': require('./rules/mouse-events-have-key-events'),
- 'no-access-key': require('./rules/no-access-key'),
- 'no-aria-hidden-on-focusable': require('./rules/no-aria-hidden-on-focusable'),
- 'no-autofocus': require('./rules/no-autofocus'),
- 'no-distracting-elements': require('./rules/no-distracting-elements'),
- 'no-interactive-element-to-noninteractive-role': require('./rules/no-interactive-element-to-noninteractive-role'),
- 'no-noninteractive-element-interactions': require('./rules/no-noninteractive-element-interactions'),
- 'no-noninteractive-element-to-interactive-role': require('./rules/no-noninteractive-element-to-interactive-role'),
- 'no-noninteractive-tabindex': require('./rules/no-noninteractive-tabindex'),
- 'no-onchange': require('./rules/no-onchange'),
- 'no-redundant-roles': require('./rules/no-redundant-roles'),
- 'no-static-element-interactions': require('./rules/no-static-element-interactions'),
- 'prefer-tag-over-role': require('./rules/prefer-tag-over-role'),
- 'role-has-required-aria-props': require('./rules/role-has-required-aria-props'),
- 'role-supports-aria-props': require('./rules/role-supports-aria-props'),
- scope: require('./rules/scope'),
- 'tabindex-no-positive': require('./rules/tabindex-no-positive'),
- },
- configs: {
- recommended: {
- plugins: [
- 'jsx-a11y',
+export const rules = kebabCase({
+ accessibleEmoji,
+ altText,
+ anchorAmbiguousText,
+ anchorHasContent,
+ anchorIsValid,
+ ariaActivedescendantHasTabindex,
+ ariaProps,
+ ariaProptypes,
+ ariaRole,
+ ariaUnsupportedElements,
+ autocompleteValid,
+ clickEventsHaveKeyEvents,
+ controlHasAssociatedLabel,
+ headingHasContent,
+ htmlHasLang,
+ iframeHasTitle,
+ imgRedundantAlt,
+ interactiveSupportsFocus,
+ labelHasAssociatedControl,
+ labelHasFor,
+ lang,
+ 'media-has-caption': mediaHasCaption,
+ 'mouse-events-have-key-events': mouseEventsHaveKeyEvents,
+ 'no-access-key': noAccessKey,
+ 'no-aria-hidden-on-focusable': noAriaHiddenOnFocusable,
+ 'no-autofocus': noAutofocus,
+ 'no-distracting-elements': noDistractingElements,
+ 'no-interactive-element-to-noninteractive-role':
+ noInteractiveElementToNoninteractiveRole,
+ 'no-noninteractive-element-interactions': noNoninteractiveElementInteractions,
+ 'no-noninteractive-element-to-interactive-role':
+ noNoninteractiveElementToInteractiveRole,
+ 'no-noninteractive-tabindex': noNoninteractiveTabindex,
+ 'no-onchange': noOnChange,
+ 'no-redundant-roles': noRedundantRoles,
+ 'no-static-element-interactions': noStaticElementInteractions,
+ 'prefer-tag-over-role': preferTagOverRole,
+ 'role-has-required-aria-props': roleHasRequiredAriaProps,
+ 'role-supports-aria-props': roleSupportsAriaProps,
+ mediaHasCaption,
+ mouseEventsHaveKeyEvents,
+ noAccessKey,
+ noAriaHiddenOnFocusable,
+ noAutofocus,
+ noDistractingElements,
+ noInteractiveElementToNoninteractiveRole,
+ noNoninteractiveElementInteractions,
+ noNoninteractiveElementToInteractiveRole,
+ noNoninteractiveTabindex,
+ noOnChange,
+ noRedundantRoles,
+ noStaticElementInteractions,
+ preferTagOverRole,
+ roleHasRequiredAriaProps,
+ roleSupportsAriaProps,
+ scope,
+ 'tabindex-no-positive': tabindexNoPositive,
+ tabindexNoPositive,
+});
+export const configs = {
+ recommended: {
+ plugins: [
+ 'jsx-a11y',
+ ],
+ parserOptions: {
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+ rules: {
+ 'jsx-a11y/alt-text': 'error',
+ 'jsx-a11y/anchor-ambiguous-text': 'off', // TODO: error
+ 'jsx-a11y/anchor-has-content': 'error',
+ 'jsx-a11y/anchor-is-valid': 'error',
+ 'jsx-a11y/aria-activedescendant-has-tabindex': 'error',
+ 'jsx-a11y/aria-props': 'error',
+ 'jsx-a11y/aria-proptypes': 'error',
+ 'jsx-a11y/aria-role': 'error',
+ 'jsx-a11y/aria-unsupported-elements': 'error',
+ 'jsx-a11y/autocomplete-valid': 'error',
+ 'jsx-a11y/click-events-have-key-events': 'error',
+ 'jsx-a11y/control-has-associated-label': ['off', {
+ ignoreElements: [
+ 'audio',
+ 'canvas',
+ 'embed',
+ 'input',
+ 'textarea',
+ 'tr',
+ 'video',
+ ],
+ ignoreRoles: [
+ 'grid',
+ 'listbox',
+ 'menu',
+ 'menubar',
+ 'radiogroup',
+ 'row',
+ 'tablist',
+ 'toolbar',
+ 'tree',
+ 'treegrid',
+ ],
+ includeRoles: [
+ 'alert',
+ 'dialog',
+ ],
+ }],
+ 'jsx-a11y/heading-has-content': 'error',
+ 'jsx-a11y/html-has-lang': 'error',
+ 'jsx-a11y/iframe-has-title': 'error',
+ 'jsx-a11y/img-redundant-alt': 'error',
+ 'jsx-a11y/interactive-supports-focus': [
+ 'error',
+ {
+ tabbable: [
+ 'button',
+ 'checkbox',
+ 'link',
+ 'searchbox',
+ 'spinbutton',
+ 'switch',
+ 'textbox',
+ ],
+ },
],
- parserOptions: {
- ecmaFeatures: {
- jsx: true,
+ 'jsx-a11y/label-has-associated-control': 'error',
+ 'jsx-a11y/label-has-for': 'off',
+ 'jsx-a11y/media-has-caption': 'error',
+ 'jsx-a11y/mouse-events-have-key-events': 'error',
+ 'jsx-a11y/no-access-key': 'error',
+ 'jsx-a11y/no-autofocus': 'error',
+ 'jsx-a11y/no-distracting-elements': 'error',
+ 'jsx-a11y/no-interactive-element-to-noninteractive-role': [
+ 'error',
+ {
+ tr: ['none', 'presentation'],
+ canvas: ['img'],
},
- },
- rules: {
- 'jsx-a11y/alt-text': 'error',
- 'jsx-a11y/anchor-ambiguous-text': 'off', // TODO: error
- 'jsx-a11y/anchor-has-content': 'error',
- 'jsx-a11y/anchor-is-valid': 'error',
- 'jsx-a11y/aria-activedescendant-has-tabindex': 'error',
- 'jsx-a11y/aria-props': 'error',
- 'jsx-a11y/aria-proptypes': 'error',
- 'jsx-a11y/aria-role': 'error',
- 'jsx-a11y/aria-unsupported-elements': 'error',
- 'jsx-a11y/autocomplete-valid': 'error',
- 'jsx-a11y/click-events-have-key-events': 'error',
- 'jsx-a11y/control-has-associated-label': ['off', {
- ignoreElements: [
- 'audio',
- 'canvas',
- 'embed',
- 'input',
- 'textarea',
- 'tr',
- 'video',
+ ],
+ 'jsx-a11y/no-noninteractive-element-interactions': [
+ 'error',
+ {
+ handlers: [
+ 'onClick',
+ 'onError',
+ 'onLoad',
+ 'onMouseDown',
+ 'onMouseUp',
+ 'onKeyPress',
+ 'onKeyDown',
+ 'onKeyUp',
],
- ignoreRoles: [
- 'grid',
+ alert: ['onKeyUp', 'onKeyDown', 'onKeyPress'],
+ body: ['onError', 'onLoad'],
+ dialog: ['onKeyUp', 'onKeyDown', 'onKeyPress'],
+ iframe: ['onError', 'onLoad'],
+ img: ['onError', 'onLoad'],
+ },
+ ],
+ 'jsx-a11y/no-noninteractive-element-to-interactive-role': [
+ 'error',
+ {
+ ul: [
'listbox',
'menu',
'menubar',
'radiogroup',
- 'row',
'tablist',
- 'toolbar',
'tree',
'treegrid',
],
- includeRoles: [
- 'alert',
- 'dialog',
- ],
- }],
- 'jsx-a11y/heading-has-content': 'error',
- 'jsx-a11y/html-has-lang': 'error',
- 'jsx-a11y/iframe-has-title': 'error',
- 'jsx-a11y/img-redundant-alt': 'error',
- 'jsx-a11y/interactive-supports-focus': [
- 'error',
- {
- tabbable: [
- 'button',
- 'checkbox',
- 'link',
- 'searchbox',
- 'spinbutton',
- 'switch',
- 'textbox',
- ],
- },
- ],
- 'jsx-a11y/label-has-associated-control': 'error',
- 'jsx-a11y/label-has-for': 'off',
- 'jsx-a11y/media-has-caption': 'error',
- 'jsx-a11y/mouse-events-have-key-events': 'error',
- 'jsx-a11y/no-access-key': 'error',
- 'jsx-a11y/no-autofocus': 'error',
- 'jsx-a11y/no-distracting-elements': 'error',
- 'jsx-a11y/no-interactive-element-to-noninteractive-role': [
- 'error',
- {
- tr: ['none', 'presentation'],
- canvas: ['img'],
- },
- ],
- 'jsx-a11y/no-noninteractive-element-interactions': [
- 'error',
- {
- handlers: [
- 'onClick',
- 'onError',
- 'onLoad',
- 'onMouseDown',
- 'onMouseUp',
- 'onKeyPress',
- 'onKeyDown',
- 'onKeyUp',
- ],
- alert: ['onKeyUp', 'onKeyDown', 'onKeyPress'],
- body: ['onError', 'onLoad'],
- dialog: ['onKeyUp', 'onKeyDown', 'onKeyPress'],
- iframe: ['onError', 'onLoad'],
- img: ['onError', 'onLoad'],
- },
- ],
- 'jsx-a11y/no-noninteractive-element-to-interactive-role': [
- 'error',
- {
- ul: [
- 'listbox',
- 'menu',
- 'menubar',
- 'radiogroup',
- 'tablist',
- 'tree',
- 'treegrid',
- ],
- ol: [
- 'listbox',
- 'menu',
- 'menubar',
- 'radiogroup',
- 'tablist',
- 'tree',
- 'treegrid',
- ],
- li: ['menuitem', 'option', 'row', 'tab', 'treeitem'],
- table: ['grid'],
- td: ['gridcell'],
- fieldset: ['radiogroup', 'presentation'],
- },
- ],
- 'jsx-a11y/no-noninteractive-tabindex': [
- 'error',
- {
- tags: [],
- roles: ['tabpanel'],
- allowExpressionValues: true,
- },
- ],
- 'jsx-a11y/no-redundant-roles': 'error',
- 'jsx-a11y/no-static-element-interactions': [
- 'error',
- {
- allowExpressionValues: true,
- handlers: [
- 'onClick',
- 'onMouseDown',
- 'onMouseUp',
- 'onKeyPress',
- 'onKeyDown',
- 'onKeyUp',
- ],
- },
- ],
- 'jsx-a11y/role-has-required-aria-props': 'error',
- 'jsx-a11y/role-supports-aria-props': 'error',
- 'jsx-a11y/scope': 'error',
- 'jsx-a11y/tabindex-no-positive': 'error',
- },
- },
- strict: {
- plugins: [
- 'jsx-a11y',
- ],
- parserOptions: {
- ecmaFeatures: {
- jsx: true,
- },
- },
- rules: {
- 'jsx-a11y/alt-text': 'error',
- 'jsx-a11y/anchor-has-content': 'error',
- 'jsx-a11y/anchor-is-valid': 'error',
- 'jsx-a11y/aria-activedescendant-has-tabindex': 'error',
- 'jsx-a11y/aria-props': 'error',
- 'jsx-a11y/aria-proptypes': 'error',
- 'jsx-a11y/aria-role': 'error',
- 'jsx-a11y/aria-unsupported-elements': 'error',
- 'jsx-a11y/autocomplete-valid': 'error',
- 'jsx-a11y/click-events-have-key-events': 'error',
- 'jsx-a11y/control-has-associated-label': ['off', {
- ignoreElements: [
- 'audio',
- 'canvas',
- 'embed',
- 'input',
- 'textarea',
- 'tr',
- 'video',
- ],
- ignoreRoles: [
- 'grid',
+ ol: [
'listbox',
'menu',
'menubar',
'radiogroup',
- 'row',
'tablist',
- 'toolbar',
'tree',
'treegrid',
],
- includeRoles: [
- 'alert',
- 'dialog',
+ li: ['menuitem', 'option', 'row', 'tab', 'treeitem'],
+ table: ['grid'],
+ td: ['gridcell'],
+ fieldset: ['radiogroup', 'presentation'],
+ },
+ ],
+ 'jsx-a11y/no-noninteractive-tabindex': [
+ 'error',
+ {
+ tags: [],
+ roles: ['tabpanel'],
+ allowExpressionValues: true,
+ },
+ ],
+ 'jsx-a11y/no-redundant-roles': 'error',
+ 'jsx-a11y/no-static-element-interactions': [
+ 'error',
+ {
+ allowExpressionValues: true,
+ handlers: [
+ 'onClick',
+ 'onMouseDown',
+ 'onMouseUp',
+ 'onKeyPress',
+ 'onKeyDown',
+ 'onKeyUp',
],
- }],
- 'jsx-a11y/heading-has-content': 'error',
- 'jsx-a11y/html-has-lang': 'error',
- 'jsx-a11y/iframe-has-title': 'error',
- 'jsx-a11y/img-redundant-alt': 'error',
- 'jsx-a11y/interactive-supports-focus': [
- 'error',
- {
- tabbable: [
- 'button',
- 'checkbox',
- 'link',
- 'progressbar',
- 'searchbox',
- 'slider',
- 'spinbutton',
- 'switch',
- 'textbox',
- ],
- },
+ },
+ ],
+ 'jsx-a11y/role-has-required-aria-props': 'error',
+ 'jsx-a11y/role-supports-aria-props': 'error',
+ 'jsx-a11y/scope': 'error',
+ 'jsx-a11y/tabindex-no-positive': 'error',
+ },
+ },
+ strict: {
+ plugins: [
+ 'jsx-a11y',
+ ],
+ parserOptions: {
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+ rules: {
+ 'jsx-a11y/alt-text': 'error',
+ 'jsx-a11y/anchor-has-content': 'error',
+ 'jsx-a11y/anchor-is-valid': 'error',
+ 'jsx-a11y/aria-activedescendant-has-tabindex': 'error',
+ 'jsx-a11y/aria-props': 'error',
+ 'jsx-a11y/aria-proptypes': 'error',
+ 'jsx-a11y/aria-role': 'error',
+ 'jsx-a11y/aria-unsupported-elements': 'error',
+ 'jsx-a11y/autocomplete-valid': 'error',
+ 'jsx-a11y/click-events-have-key-events': 'error',
+ 'jsx-a11y/control-has-associated-label': ['off', {
+ ignoreElements: [
+ 'audio',
+ 'canvas',
+ 'embed',
+ 'input',
+ 'textarea',
+ 'tr',
+ 'video',
],
- 'jsx-a11y/label-has-for': 'off',
- 'jsx-a11y/label-has-associated-control': 'error',
- 'jsx-a11y/media-has-caption': 'error',
- 'jsx-a11y/mouse-events-have-key-events': 'error',
- 'jsx-a11y/no-access-key': 'error',
- 'jsx-a11y/no-autofocus': 'error',
- 'jsx-a11y/no-distracting-elements': 'error',
- 'jsx-a11y/no-interactive-element-to-noninteractive-role': 'error',
- 'jsx-a11y/no-noninteractive-element-interactions': [
- 'error',
- {
- body: ['onError', 'onLoad'],
- iframe: ['onError', 'onLoad'],
- img: ['onError', 'onLoad'],
- },
+ ignoreRoles: [
+ 'grid',
+ 'listbox',
+ 'menu',
+ 'menubar',
+ 'radiogroup',
+ 'row',
+ 'tablist',
+ 'toolbar',
+ 'tree',
+ 'treegrid',
],
- 'jsx-a11y/no-noninteractive-element-to-interactive-role': 'error',
- 'jsx-a11y/no-noninteractive-tabindex': 'error',
- 'jsx-a11y/no-redundant-roles': 'error',
- 'jsx-a11y/no-static-element-interactions': 'error',
- 'jsx-a11y/role-has-required-aria-props': 'error',
- 'jsx-a11y/role-supports-aria-props': 'error',
- 'jsx-a11y/scope': 'error',
- 'jsx-a11y/tabindex-no-positive': 'error',
- },
+ includeRoles: [
+ 'alert',
+ 'dialog',
+ ],
+ }],
+ 'jsx-a11y/heading-has-content': 'error',
+ 'jsx-a11y/html-has-lang': 'error',
+ 'jsx-a11y/iframe-has-title': 'error',
+ 'jsx-a11y/img-redundant-alt': 'error',
+ 'jsx-a11y/interactive-supports-focus': [
+ 'error',
+ {
+ tabbable: [
+ 'button',
+ 'checkbox',
+ 'link',
+ 'progressbar',
+ 'searchbox',
+ 'slider',
+ 'spinbutton',
+ 'switch',
+ 'textbox',
+ ],
+ },
+ ],
+ 'jsx-a11y/label-has-for': 'off',
+ 'jsx-a11y/label-has-associated-control': 'error',
+ 'jsx-a11y/media-has-caption': 'error',
+ 'jsx-a11y/mouse-events-have-key-events': 'error',
+ 'jsx-a11y/no-access-key': 'error',
+ 'jsx-a11y/no-autofocus': 'error',
+ 'jsx-a11y/no-distracting-elements': 'error',
+ 'jsx-a11y/no-interactive-element-to-noninteractive-role': 'error',
+ 'jsx-a11y/no-noninteractive-element-interactions': [
+ 'error',
+ {
+ body: ['onError', 'onLoad'],
+ iframe: ['onError', 'onLoad'],
+ img: ['onError', 'onLoad'],
+ },
+ ],
+ 'jsx-a11y/no-noninteractive-element-to-interactive-role': 'error',
+ 'jsx-a11y/no-noninteractive-tabindex': 'error',
+ 'jsx-a11y/no-redundant-roles': 'error',
+ 'jsx-a11y/no-static-element-interactions': 'error',
+ 'jsx-a11y/role-has-required-aria-props': 'error',
+ 'jsx-a11y/role-supports-aria-props': 'error',
+ 'jsx-a11y/scope': 'error',
+ 'jsx-a11y/tabindex-no-positive': 'error',
},
},
};
const recommendedRules = {
@@ -299,10 +341,10 @@ const jsxA11y = {
const createConfig = (rules, flatConfigName) => ({
...(flatConfigName
? {
- ...flatConfigBase,
- name: `jsx-a11y/${flatConfigName}`,
- plugins: { 'jsx-a11y': jsxA11y },
- }
+ ...flatConfigBase,
+ name: `jsx-a11y/${flatConfigName}`,
+ plugins: { 'jsx-a11y': jsxA11y },
+ }
: { ...legacyConfigBase, plugins: ['jsx-a11y'] }),
rules: { ...rules },
});
+
+/** @param {object} obj */
+function kebabCase(obj) {
+ return Object.fromEntries(
+ Object.entries(obj).map(([key, value]) => [
+ key.replace(/([A-Z])/g, '-$1').toLowerCase(),
+ value,
+ ]),
+ );
+}
diff --git a/src/util/mayContainChildComponent.js b/src/util/mayContainChildComponent.js
index 43a03ef..5e1035e 100644
--- a/src/util/mayContainChildComponent.js

View File

@ -27,98 +27,3 @@ index 49fd4c7..a0fdd81 100644
},
rules: /** @type {Record<string, import('eslint').Rule.RuleModule>} */ ({
"callback-return": require("./rules/callback-return"),
diff --git a/tests/fixtures/no-extraneous/dependencies/node_modules/@bbb/aaa.js b/tests/fixtures/no-extraneous/dependencies/node_modules/@bbb/aaa.js
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures/no-extraneous/dependencies/node_modules/aaa.js b/tests/fixtures/no-extraneous/dependencies/node_modules/aaa.js
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures/no-extraneous/dependencies/node_modules/bbb/index.js b/tests/fixtures/no-extraneous/dependencies/node_modules/bbb/index.js
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures/no-extraneous/dependencies/node_modules/bbb/package.json b/tests/fixtures/no-extraneous/dependencies/node_modules/bbb/package.json
deleted file mode 100644
index b7d25e2..0000000
--- a/tests/fixtures/no-extraneous/dependencies/node_modules/bbb/package.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "bbb",
- "main": "index.js"
-}
\ No newline at end of file
diff --git a/tests/fixtures/no-extraneous/devDependencies/node_modules/@bbb/aaa.js b/tests/fixtures/no-extraneous/devDependencies/node_modules/@bbb/aaa.js
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures/no-extraneous/devDependencies/node_modules/aaa.js b/tests/fixtures/no-extraneous/devDependencies/node_modules/aaa.js
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures/no-extraneous/devDependencies/node_modules/bbb/index.js b/tests/fixtures/no-extraneous/devDependencies/node_modules/bbb/index.js
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures/no-extraneous/devDependencies/node_modules/bbb/package.json b/tests/fixtures/no-extraneous/devDependencies/node_modules/bbb/package.json
deleted file mode 100644
index b7d25e2..0000000
--- a/tests/fixtures/no-extraneous/devDependencies/node_modules/bbb/package.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "bbb",
- "main": "index.js"
-}
\ No newline at end of file
diff --git a/tests/fixtures/no-extraneous/noDependencies/node_modules/@bbb/aaa.js b/tests/fixtures/no-extraneous/noDependencies/node_modules/@bbb/aaa.js
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures/no-extraneous/noDependencies/node_modules/aaa.js b/tests/fixtures/no-extraneous/noDependencies/node_modules/aaa.js
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures/no-extraneous/noDependencies/node_modules/bbb.js b/tests/fixtures/no-extraneous/noDependencies/node_modules/bbb.js
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures/no-extraneous/optionalDependencies/node_modules/@bbb/aaa.js b/tests/fixtures/no-extraneous/optionalDependencies/node_modules/@bbb/aaa.js
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures/no-extraneous/optionalDependencies/node_modules/aaa.js b/tests/fixtures/no-extraneous/optionalDependencies/node_modules/aaa.js
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures/no-extraneous/optionalDependencies/node_modules/bbb/index.js b/tests/fixtures/no-extraneous/optionalDependencies/node_modules/bbb/index.js
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures/no-extraneous/optionalDependencies/node_modules/bbb/package.json b/tests/fixtures/no-extraneous/optionalDependencies/node_modules/bbb/package.json
deleted file mode 100644
index b7d25e2..0000000
--- a/tests/fixtures/no-extraneous/optionalDependencies/node_modules/bbb/package.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "bbb",
- "main": "index.js"
-}
\ No newline at end of file
diff --git a/tests/fixtures/no-extraneous/peerDependencies/node_modules/@bbb/aaa.js b/tests/fixtures/no-extraneous/peerDependencies/node_modules/@bbb/aaa.js
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures/no-extraneous/peerDependencies/node_modules/aaa.js b/tests/fixtures/no-extraneous/peerDependencies/node_modules/aaa.js
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures/no-extraneous/peerDependencies/node_modules/bbb/index.js b/tests/fixtures/no-extraneous/peerDependencies/node_modules/bbb/index.js
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures/no-extraneous/peerDependencies/node_modules/bbb/package.json b/tests/fixtures/no-extraneous/peerDependencies/node_modules/bbb/package.json
deleted file mode 100644
index b7d25e2..0000000
--- a/tests/fixtures/no-extraneous/peerDependencies/node_modules/bbb/package.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "bbb",
- "main": "index.js"
-}
\ No newline at end of file
diff --git a/tests/fixtures/no-hide-core-modules/indirect-thirdparty/node_modules/util/index.js b/tests/fixtures/no-hide-core-modules/indirect-thirdparty/node_modules/util/index.js
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/fixtures/no-hide-core-modules/thirdparty/node_modules/util/index.js b/tests/fixtures/no-hide-core-modules/thirdparty/node_modules/util/index.js
deleted file mode 100644
index e69de29..0000000

View File

@ -0,0 +1,336 @@
diff --git a/.eslintrc b/.eslintrc
deleted file mode 100644
index 4991f200..00000000
--- a/.eslintrc
+++ /dev/null
@@ -1,82 +0,0 @@
-{
- "root": true,
- "extends": ["airbnb-base", "plugin:eslint-plugin/recommended"],
- "plugins": ["eslint-plugin"],
- "env": {
- "es6": true,
- "node": true
- },
- "parserOptions": {
- "ecmaVersion": 6,
- "ecmaFeatures": {
- "jsx": true
- },
- "sourceType": "script",
- },
- "ignorePatterns": [
- "coverage/",
- ".nyc_output/",
- ],
- "rules": {
- "comma-dangle": [2, "always-multiline"],
- "object-shorthand": [2, "always", {
- "ignoreConstructors": false,
- "avoidQuotes": false, // this is the override vs airbnb
- }],
- "max-len": [2, 120, {
- "ignoreStrings": true,
- "ignoreTemplateLiterals": true,
- "ignoreComments": true,
- }],
- "consistent-return": 0,
-
- "prefer-destructuring": [2, { "array": false, "object": false }, { "enforceForRenamedProperties": false }],
- "prefer-object-spread": 0, // until node 8 is required
- "prefer-rest-params": 0, // until node 6 is required
- "prefer-spread": 0, // until node 6 is required
- "function-call-argument-newline": 1, // TODO: enable
- "function-paren-newline": 0,
- "no-plusplus": [2, {"allowForLoopAfterthoughts": true}],
- "no-param-reassign": 1,
- "no-restricted-syntax": [2, {
- "selector": "ObjectPattern",
- "message": "Object destructuring is not compatible with Node v4"
- }],
- "strict": [2, "safe"],
- "valid-jsdoc": [2, {
- "requireReturn": false,
- "requireParamDescription": false,
- "requireReturnDescription": false,
- }],
-
- "eslint-plugin/consistent-output": 0,
- "eslint-plugin/require-meta-docs-description": [2, { "pattern": "^(Enforce|Require|Disallow)" }],
- "eslint-plugin/require-meta-schema": 0,
- "eslint-plugin/require-meta-type": 0
- },
- "overrides": [
- {
- "files": "tests/**",
- "rules": {
- "no-template-curly-in-string": 1,
- },
- },
- {
- "files": "markdown.config.js",
- "rules": {
- "no-console": 0,
- },
- },
- {
- "files": ".github/workflows/*.js",
- "parserOptions": {
- "ecmaVersion": 2019,
- },
- "rules": {
- "camelcase": 0,
- "no-console": 0,
- "no-restricted-syntax": 0,
- },
- },
- ],
-}
diff --git a/index.js b/index.js
index 4140c6c8..03e623af 100644
--- a/index.js
+++ b/index.js
@@ -1,31 +1,25 @@
-'use strict';
-
-const configAll = require('./configs/all');
-const configRecommended = require('./configs/recommended');
-const configRuntime = require('./configs/jsx-runtime');
-
-const allRules = require('./lib/rules');
+import configAll from './configs/all';
+import configRecommended from './configs/recommended';
+import configRuntime from './configs/jsx-runtime';
+import { name } from './package.json';
+export { default as rules } from './lib/rules';
// for legacy config system
-const plugins = [
- 'react',
-];
+const plugins = [name];
+
+export const deprecatedRules = configAll.plugins.react.deprecatedRules;
-module.exports = {
- deprecatedRules: configAll.plugins.react.deprecatedRules,
- rules: allRules,
- configs: {
- recommended: Object.assign({}, configRecommended, {
- parserOptions: configRecommended.languageOptions.parserOptions,
- plugins,
- }),
- all: Object.assign({}, configAll, {
- parserOptions: configAll.languageOptions.parserOptions,
- plugins,
- }),
- 'jsx-runtime': Object.assign({}, configRuntime, {
- parserOptions: configRuntime.languageOptions.parserOptions,
- plugins,
- }),
- },
+export const configs = {
+ recommended: Object.assign({}, configRecommended, {
+ parserOptions: configRecommended.languageOptions.parserOptions,
+ plugins,
+ }),
+ all: Object.assign({}, configAll, {
+ parserOptions: configAll.languageOptions.parserOptions,
+ plugins,
+ }),
+ 'jsx-runtime': Object.assign({}, configRuntime, {
+ parserOptions: configRuntime.languageOptions.parserOptions,
+ plugins,
+ }),
};
diff --git a/lib/rules/button-has-type.js b/lib/rules/button-has-type.js
index 204a33c4..01d992c2 100644
--- a/lib/rules/button-has-type.js
+++ b/lib/rules/button-has-type.js
@@ -5,8 +5,7 @@
'use strict';
-const getProp = require('jsx-ast-utils/getProp');
-const getLiteralPropValue = require('jsx-ast-utils/getLiteralPropValue');
+const { getProp, getLiteralPropValue } = require('jsx-ast-utils');
const docsUrl = require('../util/docsUrl');
const isCreateElement = require('../util/isCreateElement');
const report = require('../util/report');
diff --git a/lib/rules/jsx-fragments.js b/lib/rules/jsx-fragments.js
index 38b4dd8b..d0575572 100644
--- a/lib/rules/jsx-fragments.js
+++ b/lib/rules/jsx-fragments.js
@@ -5,7 +5,7 @@
'use strict';
-const elementType = require('jsx-ast-utils/elementType');
+import { elementType } from 'jsx-ast-utils';
const pragmaUtil = require('../util/pragma');
const variableUtil = require('../util/variable');
const testReactVersion = require('../util/version').testReactVersion;
diff --git a/lib/rules/jsx-key.js b/lib/rules/jsx-key.js
index 7ea874d0..48df0dba 100644
--- a/lib/rules/jsx-key.js
+++ b/lib/rules/jsx-key.js
@@ -5,8 +5,7 @@
'use strict';
-const hasProp = require('jsx-ast-utils/hasProp');
-const propName = require('jsx-ast-utils/propName');
+import { hasProp, propName } from 'jsx-ast-utils';
const values = require('object.values');
const docsUrl = require('../util/docsUrl');
const pragmaUtil = require('../util/pragma');
diff --git a/lib/rules/jsx-no-bind.js b/lib/rules/jsx-no-bind.js
index 17e56e2e..cb6dec1a 100644
--- a/lib/rules/jsx-no-bind.js
+++ b/lib/rules/jsx-no-bind.js
@@ -7,7 +7,7 @@
'use strict';
-const propName = require('jsx-ast-utils/propName');
+import { propName } from 'jsx-ast-utils';
const docsUrl = require('../util/docsUrl');
const jsxUtil = require('../util/jsx');
const report = require('../util/report');
diff --git a/lib/rules/jsx-pascal-case.js b/lib/rules/jsx-pascal-case.js
index efeef403..33df4653 100644
--- a/lib/rules/jsx-pascal-case.js
+++ b/lib/rules/jsx-pascal-case.js
@@ -5,7 +5,7 @@
'use strict';
-const elementType = require('jsx-ast-utils/elementType');
+import { elementType } from 'jsx-ast-utils';
const minimatch = require('minimatch');
const docsUrl = require('../util/docsUrl');
const jsxUtil = require('../util/jsx');
diff --git a/lib/rules/jsx-sort-props.js b/lib/rules/jsx-sort-props.js
index 3ca1724e..faf58f91 100644
--- a/lib/rules/jsx-sort-props.js
+++ b/lib/rules/jsx-sort-props.js
@@ -5,7 +5,7 @@
'use strict';
-const propName = require('jsx-ast-utils/propName');
+import { propName } from 'jsx-ast-utils';
const includes = require('array-includes');
const toSorted = require('array.prototype.tosorted');
diff --git a/lib/rules/no-namespace.js b/lib/rules/no-namespace.js
index d7559f5e..fbfad23a 100644
--- a/lib/rules/no-namespace.js
+++ b/lib/rules/no-namespace.js
@@ -5,7 +5,7 @@
'use strict';
-const elementType = require('jsx-ast-utils/elementType');
+import { elementType } from 'jsx-ast-utils';
const docsUrl = require('../util/docsUrl');
const isCreateElement = require('../util/isCreateElement');
const report = require('../util/report');
diff --git a/lib/rules/no-unknown-property.js b/lib/rules/no-unknown-property.js
index 9491f9c6..44396948 100644
--- a/lib/rules/no-unknown-property.js
+++ b/lib/rules/no-unknown-property.js
@@ -543,7 +543,7 @@ module.exports = {
create(context) {
function getIgnoreConfig() {
- return (context.options[0] && context.options[0].ignore) || DEFAULTS.ignore;
+ return context.options[0]?.ignore || DEFAULTS.ignore;
}
function getRequireDataLowercase() {
@@ -556,7 +556,7 @@ module.exports = {
JSXAttribute(node) {
const ignoreNames = getIgnoreConfig();
const actualName = context.getSourceCode().getText(node.name);
- if (ignoreNames.indexOf(actualName) >= 0) {
+ if (ignoreNames.includes(actualName)) {
return;
}
const name = normalizeAttributeCase(actualName);
diff --git a/lib/util/annotations.js b/lib/util/annotations.js
index 60aaef8c..ad8dc0bf 100644
--- a/lib/util/annotations.js
+++ b/lib/util/annotations.js
@@ -27,6 +27,6 @@ function isAnnotatedFunctionPropsDeclaration(node, context) {
return (isAnnotated && (isDestructuredProps || isProps));
}
-module.exports = {
+export {
isAnnotatedFunctionPropsDeclaration,
};
diff --git a/lib/util/ast.js b/lib/util/ast.js
index fd6019a3..3cbc293e 100644
--- a/lib/util/ast.js
+++ b/lib/util/ast.js
@@ -4,7 +4,7 @@
'use strict';
-const estraverse = require('estraverse');
+import estraverse from 'estraverse';
// const pragmaUtil = require('./pragma');
/**
@@ -428,7 +428,7 @@ function isTSTypeParameterInstantiation(node) {
return nodeType === 'TSTypeParameterInstantiation';
}
-module.exports = {
+export {
traverse,
findReturnStatement,
getFirstNodeInLine,
diff --git a/lib/util/jsx.js b/lib/util/jsx.js
index 55073bfe..efc07af1 100644
--- a/lib/util/jsx.js
+++ b/lib/util/jsx.js
@@ -4,7 +4,7 @@
'use strict';
-const elementType = require('jsx-ast-utils/elementType');
+import { elementType } from 'jsx-ast-utils';
const astUtil = require('./ast');
const isCreateElement = require('./isCreateElement');
diff --git a/tsconfig.json b/tsconfig.json
deleted file mode 100644
index 39187b7f..00000000
--- a/tsconfig.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "compilerOptions": {
- "target": "ES2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
- "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
- // "lib": ["es2015"], /* Specify library files to be included in the compilation. */
- "allowJs": true, /* Allow javascript files to be compiled. */
- "checkJs": true, /* Report errors in .js files. */
- "noEmit": true, /* Do not emit outputs. */
- "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
-
- /* Strict Type-Checking Options */
- // "strict": true, /* Enable all strict type-checking options. */
- "noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */
- // "strictNullChecks": true, /* Enable strict null checks. */
- // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
- "strictFunctionTypes": true, /* Enable strict checking of function types. */
- "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
- "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
- "alwaysStrict": false, /* Parse in strict mode and emit "use strict" for each source file. */
- "resolveJsonModule": true
- },
- "include": ["lib"],
-}

5604
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,5 @@
#!/usr/bin/env tsx
#!/usr/bin/env bun
import './build-local-rules';
import { promises as fs } from 'node:fs';
import { resolve, relative } from 'node:path';
import { isBuiltin } from 'node:module';
@ -6,9 +7,7 @@ import esbuild from 'esbuild';
import type { Plugin } from 'esbuild';
import { memoize } from 'lodash';
import { gray, green } from 'picocolors';
import { minify_sync } from 'terser';
import { dependencies } from '../dist/package.json';
import { buildLocalRules } from './build-local-rules';
import { dts } from './dts';
import { babelPlugin } from './modifier';
@ -66,15 +65,14 @@ if (process.env.DEBUG) {
});
}
async function bundle(
function bundle(
entry: string,
outfile = entry
.replace('./packages/', './dist/')
.replace('src/', '')
.replace('.ts', '.js'),
options?: esbuild.BuildOptions & { treeShaking?: boolean },
) {
const output = await esbuild.build({
return esbuild.build({
entryPoints: [entry],
outfile,
bundle: true,
@ -89,32 +87,7 @@ async function bundle(
banner: {
js: '/* eslint-disable */',
},
...options,
});
if (options?.treeShaking) {
const [text, setText] = await useText(outfile);
const minified = minify_sync(text, {
module: true,
compress: {
conditionals: true,
dead_code: true,
defaults: false,
evaluate: true,
passes: 3,
pure_new: true,
side_effects: true,
unused: true,
},
mangle: false,
format: {
comments: true,
},
});
setText(minified.code!);
}
return output;
}
async function editPackageJson() {
@ -151,29 +124,24 @@ function bundleType(source: string, output: string) {
}
async function main() {
console.log('Building local rules...');
await buildLocalRules();
console.log('Building type definitions...');
bundleType('./src/index.ts', './dist/index.d.ts');
bundleType('./src/prettier.ts', './dist/prettier.d.ts');
bundleType('./src/types.ts', './dist/types.d.ts');
const unminify = { minify: false };
const treeShake = { treeShaking: true, minify: false };
console.log('Building packages...');
await Promise.all([
bundle('./packages/eslint-plugin-react/index.js'),
bundle('./packages/eslint-plugin-import/src/index.js'),
bundle('./packages/eslint-plugin-jsx-a11y/src/index.js'),
bundle('./packages/eslint-plugin-react-hooks/index.ts'),
bundle('./packages/eslint-plugin-n/lib/index.js', './dist/eslint-plugin-n/index.js'),
bundle('./packages/eslint-import-resolver-typescript/src/index.ts'),
bundle('./src/rules/index.ts', './dist/eslint-plugin-rules/index.js'),
bundle('./src/local/index.ts', './dist/eslint-plugin-local/index.js'),
bundle('./src/index.ts', './dist/index.js', unminify),
bundle('./src/types.ts', './dist/types.js', unminify),
bundle('./src/prettier.ts', './dist/prettier.js', unminify),
bundle('./src/install.ts', './dist/install.js', treeShake),
bundle('./src/index.ts', './dist/index.js'),
bundle('./src/types.ts', './dist/types.js'),
bundle('./src/prettier.ts', './dist/prettier.js'),
editPackageJson(),
]);

View File

@ -1,14 +1,12 @@
#!/usr/bin/env tsx
import { promises as fs } from 'node:fs';
import { camelCase } from 'lodash';
export async function buildLocalRules() {
const files = (await fs.readdir('./src/rules'))
.filter(file => file.endsWith('.ts'))
.filter(file => file !== 'index.ts')
.map(file => file.slice(0, -3));
const files = (await fs.readdir('./src/rules'))
.filter(file => file.endsWith('.ts'))
.filter(file => file !== 'index.ts')
.map(file => file.slice(0, -3));
const entryFile = /* js */ `
const entryFile = /* js */ `
import type { Rule } from 'eslint';
import type { ESLintUtils } from '@typescript-eslint/utils';
@ -22,9 +20,5 @@ export const rules: Record<
};
`.trim();
await fs.writeFile('./src/rules/index.ts', entryFile + '\n');
}
if (require.main === module) {
buildLocalRules();
}
console.log('Building local rules...');
await fs.writeFile('./src/rules/index.ts', entryFile + '\n');

View File

@ -3,10 +3,12 @@ import glob from 'fast-glob';
import fs from 'fs';
import { builtinModules } from 'module';
import { uniq } from 'lodash';
import { dependencies, peerDependencies } from '../dist/package.json';
import { dependencies, peerDependencies, overrides } from '../dist/package.json';
function checkImports() {
const deps = Object.keys({ ...dependencies, ...peerDependencies }).concat('eslint');
const deps = Object.keys({ ...dependencies, ...peerDependencies, ...overrides }).concat(
'eslint',
);
const builtIn = new Set(builtinModules.flatMap(module => [module, `node:${module}`]));
function findRequires(text: string) {

View File

@ -17,14 +17,22 @@ export function dts({
const entry: EntryPointConfig = {
filePath: source,
failOnClass: false,
libraries: {
importedLibraries: ['eslint-define-config'],
},
output: {
inlineDeclareExternals: false,
inlineDeclareGlobals: false,
sortNodes: false,
noBanner: false,
respectPreserveConstEnum: false,
exportReferencedTypes: true,
},
};
const generatedDts = generateDtsBundle([entry], {
preferredConfigPath: project,
followSymlinks: true,
followSymlinks: false,
});
ts.sys.writeFile(dist, generatedDts[0]);

View File

@ -10,6 +10,8 @@ import * as polyfill from '../src/polyfill';
const polyfills = Object.keys(polyfill);
const ENV = (process.env.NODE_ENV ??= 'production');
class HandlerMap {
map = new Map<string, MacroHandler>();

View File

@ -21,6 +21,8 @@ pull() {
}
pull import-js eslint-import-resolver-typescript
pull import-js eslint-plugin-import
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

@ -4,6 +4,8 @@ sync() (
)
sync eslint-import-resolver-typescript
sync eslint-plugin-import
sync eslint-plugin-jsx-a11y
sync eslint-plugin-n
sync eslint-plugin-react
sync jsx-ast-utils

View File

@ -1,33 +1,33 @@
{
"eslint-plugin-import": {
"hash": "6554bd5c30976290024cecc44ef1e96746cf3cf7",
"date": "2024-05-23T12:47:41-07:00",
"hash": "f77ceb679d59ced5d9a633123385470a9eea10d9",
"date": "2024-04-07T12:55:28+12:00",
"committer": "Jordan Harband",
"subject": "[meta] add `repository.directory` field"
"subject": "[actions] cancel in-progress runs on PR updates"
},
"eslint-import-resolver-typescript": {
"hash": "3dfad602a05b4b3812a4d3fc681051932f86e838",
"date": "2024-08-01T01:15:59+00:00",
"hash": "7a02ac08b5aaac8c217f0e87142f97eafcc38fbc",
"date": "2024-04-01T01:06:20+00:00",
"committer": "GitHub",
"subject": "chore(deps): update dependency node to v18.20.4 (#309)"
"subject": "chore(deps): update dependency npm-run-all2 to ^5.0.2 (#277)"
},
"eslint-plugin-jsx-a11y": {
"hash": "cca288b73a39fa0932a57c02a7a88de68fc971fc",
"date": "2024-07-22T02:39:43+01:00",
"hash": "0d5321a5457c5f0da0ca216053cc5b4f571b53ae",
"date": "2024-01-27T22:18:19-08:00",
"committer": "Jordan Harband",
"subject": "[readme] fix typo in shareable config section in readme"
"subject": "[Deps] update `@babel/runtime`, `safe-regex-test`"
},
"eslint-plugin-n": {
"hash": "6744257b43560181412a76eadeb7de564b886ad4",
"date": "2024-07-26T11:46:54+01:00",
"hash": "eb11b5b35a6a797dc7fba6df53b1c4dada3a2a55",
"date": "2024-04-17T17:40:32+08:00",
"committer": "GitHub",
"subject": "chore(master): release 17.10.1 (#319)"
"subject": "chore: upgrade globals v15 (#241)"
},
"eslint-plugin-react": {
"hash": "983b88dd3cb5e07919517d3fde4085f60883ded7",
"date": "2024-07-24T15:26:33-07:00",
"hash": "4467db503e38b9356517cf6926d11be544ccf4b1",
"date": "2024-03-16T12:54:58+09:00",
"committer": "Jordan Harband",
"subject": "[Tests] `no-array-index-key`: actually run valid tests"
"subject": "[Fix] `boolean-prop-naming`: avoid a crash with a non-TSTypeReference type"
},
"jsx-ast-utils": {
"hash": "5943318eaf23764eec3ff397ebb969613d728a95",

View File

@ -1,79 +0,0 @@
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';
import { testingLibrary } from './presets/testing-library';
const middlewares = {
react,
reactRefresh,
tailwind,
storybook,
reactQuery,
testingLibrary,
};
export const envs: {
dependency: string;
eslintPlugin?: string;
middleware: keyof typeof middlewares;
}[] = [
{
dependency: 'react',
middleware: 'react',
},
{
dependency: '@vitejs/plugin-react',
eslintPlugin: 'eslint-plugin-react-refresh',
middleware: 'reactRefresh',
},
{
dependency: 'tailwindcss',
eslintPlugin: 'eslint-plugin-tailwindcss',
middleware: 'tailwind',
},
{
dependency: 'storybook',
eslintPlugin: 'eslint-plugin-storybook',
middleware: 'storybook',
},
{
dependency: '@tanstack/react-query',
eslintPlugin: '@tanstack/eslint-plugin-query',
middleware: 'reactQuery',
},
{
dependency: '@testing-library/react',
eslintPlugin: 'eslint-plugin-testing-library',
middleware: 'testingLibrary',
},
];
export function getProjectDependencies() {
const rootDir = process.cwd();
const pkgJsonPath = resolve(rootDir, 'package.json');
const pkgJson = fs.existsSync(pkgJsonPath)
? JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'))
: {};
return new Set(
Object.keys({
...pkgJson.dependencies,
...pkgJson.devDependencies,
...pkgJson.peerDependencies,
}),
);
}
export function* checkEnv(): Generator<Middleware> {
const deps = getProjectDependencies();
for (const { dependency, eslintPlugin, middleware } of envs) {
if (deps.has(dependency) && (!eslintPlugin || deps.has(eslintPlugin))) {
yield middlewares[middleware];
}
}
}

View File

@ -1,30 +1,19 @@
/// <reference path="./modules.d.ts" />
import './redirect';
import { uniq } from 'lodash';
import type { Merge, SetRequired } from 'type-fest';
import fs from 'node:fs';
import type { Rule } from 'eslint';
import type { ESLintUtils } from '@typescript-eslint/utils';
import type {
ESLintConfig,
Extends,
KnownExtends,
Plugin,
Rules,
Settings,
} from '@aet/eslint-define-config';
import { importTypeScript } from './presets/typescript';
import { unicorn } from './presets/unicorn';
import type { ESLintConfig, Rules } from 'eslint-define-config';
import { typescriptRules } from './presets/typescript';
import { unicornRules } from './presets/unicorn';
import { eslintRules } from './presets/eslint';
import { local } from './presets/local';
import { reactRules } from './presets/react';
import { importRules } from './presets/import';
import { jsDocRules } from './presets/jsdoc';
import { graphqlRules } from './presets/graphql';
import { localRules } from './presets/local';
import { error, warn, off } from './constants';
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';
import { tailwindRules } from './presets/tailwind';
export { error, warn, off };
@ -46,6 +35,12 @@ const ensureArray = <T>(value?: T | T[]): T[] =>
type RuleLevel = 'error' | 'warn' | 'off' | 0 | 1 | 2;
type RuleEntry<Options> = RuleLevel | [RuleLevel, Partial<Options>];
declare module 'eslint-define-config' {
interface NoUnknownPropertyOption {
extends: ('next' | 'emotion')[];
}
}
export interface LocalRuleOptions {
/** Bans import from the specifier '.' and '..' and replaces it with '.+/index' */
'rules/no-import-dot': RuleEntry<unknown>;
@ -67,8 +62,6 @@ 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/)
@ -78,7 +71,7 @@ export type InputConfig = Omit<ESLintConfig, 'rules'> & {
* Rules.
* @see [Rules](https://eslint.org/docs/latest/user-guide/configuring/rules)
*/
rules?: Partial<RuleOptions>;
rules?: RuleOptions;
/**
* Glob pattern to find paths to custom rule files in JavaScript or TypeScript.
@ -86,38 +79,12 @@ export type InputConfig = Omit<ESLintConfig, 'rules'> & {
* this is statically analyzed.
*/
customRuleFiles?: string | string[];
/**
* Automatically detect project types, dependencies and deduct the plugins.
*/
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.
*
* By default, it includes `["@typescript-eslint", "import-x", "prettier", "unicorn"]` configs.
* By default, it includes `["@typescript-eslint", "import", "prettier"]` configs.
* Additional bundled plugins include:
*
* 1. [`react`](https://github.com/jsx-eslint/eslint-plugin-react#list-of-supported-rules)
@ -126,82 +93,122 @@ interface MiddlewareFunctions {
* 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`)
* 5. [`n`](https://github.com/eslint-community/eslint-plugin-n#-rules) (Node.js specific)
* 6. [`jsdoc`](https://github.com/gajus/eslint-plugin-jsdoc#rules)
*
* Non bundled:
* 1. [`graphql`](https://the-guild.dev/graphql/eslint/rules)
*/
export function extendConfig(
of: InputConfig & {
middlewares?: Middleware[];
} = {
middlewares: [],
},
): ESLintConfig {
export function extendConfig(of: InputConfig = {}): ESLintConfig {
const {
auto = true,
plugins: _plugins = [],
settings = {},
plugins = [],
settings,
rules,
extends: _extends,
overrides,
customRuleFiles,
parserOptions,
middlewares: _middlewares = [],
// @ts-expect-error
localRules: _,
...rest
} = of;
let plugins: Plugin[] = [..._plugins];
let extend: Extends[] = ensureArray(_extends);
const hasReact = plugins.includes('react');
const hasReactRefresh = plugins.includes('react-refresh');
const hasUnicorn = plugins.includes('unicorn');
const hasJsDoc = plugins.includes('jsdoc');
const hasGraphQL = plugins.includes('@graphql-eslint');
const hasNext = ensureArray(_extends).some(name => name.includes(':@next/next'));
const hasTailwind = ensureArray(_extends).some(name =>
name.includes('plugin:tailwindcss/'),
);
const middlewares: Middleware[] = uniq([
importTypeScript,
unicorn,
local,
...(auto ? checkEnv() : []),
..._middlewares,
]);
const ruleDir = false; // ?? findCacheDirectory({ name: '_eslint-rules' });
if (ruleDir) {
fs.rmSync(ruleDir, { recursive: true, force: true });
fs.mkdirSync(ruleDir, { recursive: true });
}
const result: MiddlewareConfig = {
const result: InputConfig = {
root: true,
plugins: unique('rules', plugins),
parser: '@typescript-eslint/parser',
plugins: unique('@typescript-eslint', 'import', 'rules', plugins),
env: { node: true, browser: true, es2023: true },
reportUnusedDisableDirectives: true,
parserOptions: {
project: true,
...parserOptions,
},
ignorePatterns: [],
globals: {},
extends: ['eslint:recommended', 'prettier', ...(extend as string[])],
settings,
extends: unique(
'eslint:recommended',
'prettier',
'plugin:@typescript-eslint/recommended-type-checked',
'plugin:import/errors',
'plugin:import/typescript',
hasReact && [
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
],
hasJsDoc && 'plugin:jsdoc/recommended-typescript',
hasGraphQL && 'plugin:@graphql-eslint/recommended',
_extends,
),
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx', '.mts', '.cts'],
},
'import/resolver': {
typescript: {
alwaysTryTypes: true,
},
},
react: {
version: 'detect',
},
...settings,
},
overrides: [
{ files: ['repl.ts', 'scripts/**/*.ts'], rules: { 'no-console': off } },
{
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,
},
},
...(overrides ?? []),
],
rules: { ...eslintRules, ...rules },
rules: {
...eslintRules,
...typescriptRules,
...importRules,
...localRules,
...(hasReact && {
...reactRules,
'react/no-unknown-property': [
error,
{ ignore: hasNext ? ['css', 'next'] : ['css'] },
],
}),
...(hasReactRefresh && {
'react-refresh/only-export-components': [warn, { allowConstantExport: true }],
}),
...(hasUnicorn && unicornRules),
...(hasJsDoc && jsDocRules),
...(hasGraphQL && graphqlRules),
...(hasTailwind && tailwindRules),
...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,16 +0,0 @@
import { installPackage } from '@antfu/install-pkg';
import { uniq } from 'lodash';
import { getProjectDependencies, envs } from './env';
const deps = getProjectDependencies();
const packages = uniq(
envs
.filter(_ => deps.has(_.dependency) && _.eslintPlugin && !deps.has(_.eslintPlugin))
.map(_ => _.eslintPlugin!),
);
console.log('Installing missing ESLint plugins.\n');
void installPackage(packages, {
silent: false,
});

View File

@ -8,6 +8,7 @@ import type { Node, Property } from 'estree';
// https://github.com/gulpjs/interpret
const transpilers = [
'esbin/register',
'esbuild-register',
'ts-node/register/transpile-only',
'@swc/register',

View File

@ -1,164 +0,0 @@
[
"addEventListener",
"blur",
"caches",
"captureEvents",
"clientInformation",
"close",
"closed",
"crossOriginIsolated",
"devicePixelRatio",
"dispatchEvent",
"event",
"external",
"focus",
"innerHeight",
"innerWidth",
"length",
"locationbar",
"menubar",
"name",
"onabort",
"onafterprint",
"onanimationcancel",
"onanimationend",
"onanimationiteration",
"onanimationstart",
"onauxclick",
"onbeforeinput",
"onbeforeprint",
"onbeforetoggle",
"onbeforeunload",
"onblur",
"oncancel",
"oncanplay",
"oncanplaythrough",
"onchange",
"onclick",
"onclose",
"oncontextmenu",
"oncopy",
"oncuechange",
"oncut",
"ondblclick",
"ondevicemotion",
"ondeviceorientation",
"ondeviceorientationabsolute",
"ondrag",
"ondragend",
"ondragenter",
"ondragleave",
"ondragover",
"ondragstart",
"ondrop",
"ondurationchange",
"onemptied",
"onended",
"onerror",
"onfocus",
"onformdata",
"ongamepadconnected",
"ongamepaddisconnected",
"ongotpointercapture",
"onhashchange",
"oninput",
"oninvalid",
"onkeydown",
"onkeypress",
"onkeyup",
"onlanguagechange",
"onload",
"onloadeddata",
"onloadedmetadata",
"onloadstart",
"onlostpointercapture",
"onmessage",
"onmessageerror",
"onmousedown",
"onmouseenter",
"onmouseleave",
"onmousemove",
"onmouseout",
"onmouseover",
"onmouseup",
"onoffline",
"ononline",
"onorientationchange",
"onpagehide",
"onpageshow",
"onpaste",
"onpause",
"onplay",
"onplaying",
"onpointercancel",
"onpointerdown",
"onpointerenter",
"onpointerleave",
"onpointermove",
"onpointerout",
"onpointerover",
"onpointerup",
"onpopstate",
"onprogress",
"onratechange",
"onrejectionhandled",
"onreset",
"onresize",
"onscroll",
"onscrollend",
"onsecuritypolicyviolation",
"onseeked",
"onseeking",
"onselect",
"onselectionchange",
"onselectstart",
"onslotchange",
"onstalled",
"onstorage",
"onsubmit",
"onsuspend",
"ontimeupdate",
"ontoggle",
"ontouchcancel",
"ontouchend",
"ontouchmove",
"ontouchstart",
"ontransitioncancel",
"ontransitionend",
"ontransitionrun",
"ontransitionstart",
"onunhandledrejection",
"onunload",
"onvolumechange",
"onwaiting",
"onwebkitanimationend",
"onwebkitanimationiteration",
"onwebkitanimationstart",
"onwebkittransitionend",
"onwheel",
"orientation",
"origin",
"outerHeight",
"outerWidth",
"pageXOffset",
"pageYOffset",
"personalbar",
"releaseEvents",
"removeEventListener",
"reportError",
"screenLeft",
"screenTop",
"screenX",
"screenY",
"scroll",
"scrollbars",
"scrollBy",
"scrollTo",
"scrollX",
"scrollY",
"status",
"statusbar",
"stop",
"toolbar",
"top"
]

View File

@ -1,10 +1,9 @@
import { error, warn, off } from '../constants';
import { EslintRulesObject } from '@aet/eslint-define-config/src/rules/eslint';
import restrictedGlobals from './_restrictedGlobals.json';
import { ESLintRules } from 'eslint-define-config/rules/eslint';
export const eslintRules: Partial<EslintRulesObject> = {
export const eslintRules: Partial<ESLintRules> = {
'arrow-body-style': [error, 'as-needed'],
'class-methods-use-this': warn,
'class-methods-use-this': off,
'func-style': [error, 'declaration', { allowArrowFunctions: true }],
'no-async-promise-executor': off,
'no-case-declarations': off,
@ -15,7 +14,7 @@ export const eslintRules: Partial<EslintRulesObject> = {
'no-empty': [error, { allowEmptyCatch: true }],
'no-inner-declarations': off,
'no-lonely-if': error,
'no-restricted-globals': [error, ...restrictedGlobals],
'no-restricted-globals': [error, 'event', 'name', 'length'],
'no-restricted-imports': [
error,
{
@ -31,7 +30,7 @@ export const eslintRules: Partial<EslintRulesObject> = {
'no-template-curly-in-string': error,
'no-var': error,
'object-shorthand': [error, 'always', { ignoreConstructors: true }],
'one-var': [error, { var: 'never', let: 'never', const: 'never' }],
'one-var': [error, { var: 'never', let: 'never' }],
'prefer-arrow-callback': error,
'prefer-const': [error, { destructuring: 'all' }],
'prefer-destructuring': [
@ -43,15 +42,7 @@ export const eslintRules: Partial<EslintRulesObject> = {
'prefer-spread': warn,
'quote-props': [error, 'as-needed'],
'sort-imports': [warn, { ignoreDeclarationSort: true }],
'spaced-comment': [
error,
'always',
{
markers: ['/', '#', '@'],
// allow /*@__PURE__*/
block: { exceptions: ['@'] },
},
],
'spaced-comment': [error, 'always', { markers: ['/', '#', '@'] }],
complexity: [warn, { max: 100 }],
curly: [error, 'multi-line', 'consistent'],
eqeqeq: [error, 'smart'],

View File

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

8
src/presets/import.ts Normal file
View File

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

View File

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

View File

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

View File

@ -1,9 +0,0 @@
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,35 +1,9 @@
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';
import { error, off } from '../constants';
import { ReactRules } from 'eslint-define-config/rules/react';
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);
export const reactRules: Partial<ReactRules> = {
'react/display-name': off,
'react/no-children-prop': error,
'react/prop-types': off,
'react/react-in-jsx-scope': off,
};

View File

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

View File

@ -1,12 +0,0 @@
import type { Middleware } from '../index';
import { TestingLibraryRulesObject } from '@aet/eslint-define-config/src/rules/testing-library';
const testingLibraryRules: Partial<TestingLibraryRulesObject> = {};
export const testingLibrary: Middleware = (config, { addRules }) => {
config.overrides.push({
files: ['**/*.(spec|test).{ts,tsx}'],
plugins: ['plugin:testing-library/react'],
});
addRules(testingLibraryRules);
};

View File

@ -1,15 +1,7 @@
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';
import type { TypeScriptRules } from 'eslint-define-config/rules/typescript-eslint';
const importRules: Partial<ImportXRulesObject> = {
'import-x/export': off,
'import-x/no-duplicates': error,
'import-x/order': [error, { groups: ['builtin', 'external'] }],
};
const typescriptRules: Partial<TypeScriptRulesObject> = {
export const typescriptRules: Partial<TypeScriptRules> = {
'@typescript-eslint/ban-ts-comment': [
error,
{
@ -19,6 +11,7 @@ const typescriptRules: Partial<TypeScriptRulesObject> = {
'ts-nocheck': 'allow-with-description',
},
],
'@typescript-eslint/ban-types': [error, { extendDefaults: true }],
'@typescript-eslint/consistent-type-imports': [
error,
{ disallowTypeAnnotations: false, fixStyle: 'inline-type-imports' },
@ -44,39 +37,3 @@ 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,11 +1,10 @@
import type { Middleware } from '../index';
import { error, warn } from '../constants';
import { UnicornRulesObject } from '@aet/eslint-define-config/src/rules/unicorn';
import { UnicornRules } from 'eslint-define-config/rules/unicorn';
const suggest = (suggest: string) => ({ suggest, fix: false });
// https://github.com/sindresorhus/eslint-plugin-unicorn/tree/28e7498ad06679bb92343db53bb40a7b5ba2990a
const unicornRules: Partial<UnicornRulesObject> = {
export const unicornRules: Partial<UnicornRules> = {
'unicorn/better-regex': error,
'unicorn/consistent-function-scoping': warn,
'unicorn/escape-case': error,
@ -70,8 +69,3 @@ const unicornRules: Partial<UnicornRulesObject> = {
],
'unicorn/template-indent': warn,
};
export const unicorn: Middleware = (config, { addRules }) => {
config.plugins.push('unicorn');
addRules(unicornRules);
};

View File

@ -4,10 +4,12 @@ const { name } = [require][0]('./package.json');
const _resolveFilename = Module._resolveFilename;
const alias = new Set([
'eslint-import-resolver-typescript',
'eslint-plugin-import',
'eslint-plugin-jsx-a11y',
'eslint-plugin-local',
'eslint-plugin-n',
'eslint-plugin-react-hooks',
'eslint-plugin-react',
'eslint-plugin-rules',
]);

View File

@ -1,15 +1,15 @@
import type { Rule } from 'eslint';
import type { ESLintUtils } from '@typescript-eslint/utils';
import noEmptyObjectLiteral from './no-empty-object-literal';
import noImportDot from './no-import-dot';
import noEmptyObjectLiteral from './no-empty-object-literal';
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-empty-object-literal': noEmptyObjectLiteral,
'restrict-template-expressions': restrictTemplateExpressions,
};

171
src/types/config/env.d.ts vendored Normal file
View File

@ -0,0 +1,171 @@
/**
* An environment provides predefined global variables.
*
* @see [Environments](https://eslint.org/docs/user-guide/configuring/language-options#specifying-environments)
*/
export interface Environments extends Partial<Record<string, boolean>> {
/**
* Browser global variables.
*/
browser?: boolean;
/**
* Node.js global variables and Node.js scoping.
*/
node?: boolean;
/**
* CommonJS global variables and CommonJS scoping (use this for browser-only code that uses Browserify/WebPack).
*/
commonjs?: boolean;
/**
* Globals common to both Node.js and Browser.
*/
'shared-node-browser'?: boolean;
/**
* Enable all ECMAScript 6 features except for modules (this automatically sets the `ecmaVersion` parser option to 6).
*/
es6?: boolean;
/**
* Adds all ECMAScript 2016 globals and automatically sets the `ecmaVersion` parser option to 7.
*/
es2016?: boolean;
/**
* Adds all ECMAScript 2017 globals and automatically sets the `ecmaVersion` parser option to 8.
*/
es2017?: boolean;
/**
* Adds all ECMAScript 2018 globals and automatically sets the `ecmaVersion` parser option to 9.
*/
es2018?: boolean;
/**
* Adds all ECMAScript 2019 globals and automatically sets the `ecmaVersion` parser option to 10.
*/
es2019?: boolean;
/**
* Adds all ECMAScript 2020 globals and automatically sets the `ecmaVersion` parser option to 11.
*/
es2020?: boolean;
/**
* Adds all ECMAScript 2021 globals and automatically sets the `ecmaVersion` parser option to 12.
*/
es2021?: boolean;
/**
* Adds all ECMAScript 2022 globals and automatically sets the `ecmaVersion` parser option to 13.
*/
es2022?: boolean;
/**
* Adds all ECMAScript 2023 globals and automatically sets the `ecmaVersion` parser option to 14.
*/
es2023?: boolean;
/**
* Web workers global variables.
*/
worker?: boolean;
/**
* Defines `require()` and `define()` as global variables as per the amd spec.
*/
amd?: boolean;
/**
* Adds all of the Mocha testing global variables.
*/
mocha?: boolean;
/**
* Adds all of the Jasmine testing global variables for version 1.3 and 2.0.
*/
jasmine?: boolean;
/**
* Jest global variables.
*/
jest?: boolean;
/**
* PhantomJS global variables.
*/
phantomjs?: boolean;
/**
* Protractor global variables.
*/
protractor?: boolean;
/**
* QUnit global variables.
*/
qunit?: boolean;
/**
* jQuery global variables.
*/
jquery?: boolean;
/**
* Prototype.js global variables.
*/
prototypejs?: boolean;
/**
* ShellJS global variables.
*/
shelljs?: boolean;
/**
* Meteor global variables.
*/
meteor?: boolean;
/**
* MongoDB global variables.
*/
mongo?: boolean;
/**
* AppleScript global variables.
*/
applescript?: boolean;
/**
* Java 8 Nashorn global variables.
*/
nashorn?: boolean;
/**
* Service Worker global variables.
*/
serviceworker?: boolean;
/**
* Atom test helper globals.
*/
atomtest?: boolean;
/**
* Ember test helper globals.
*/
embertest?: boolean;
/**
* WebExtensions globals.
*/
webextensions?: boolean;
/**
* GreaseMonkey globals.
*/
greasemonkey?: boolean;
}

View File

@ -0,0 +1,6 @@
/**
* Eslint EslintComments extends.
*
* @see [Eslint EslintComments extends](https://mysticatea.github.io/eslint-plugin-eslint-comments/#%F0%9F%93%96-usage)
*/
export type EslintCommentsExtends = 'plugin:eslint-comments/recommended';

View File

@ -0,0 +1,11 @@
/**
* Eslint GraphQL extends.
*
* @see [Eslint GraphQL extends](https://the-guild.dev/graphql/eslint/docs/configs)
*/
export type GraphqlExtends =
| 'plugin:@graphql-eslint/operations-all'
| 'plugin:@graphql-eslint/operations-recommended'
| 'plugin:@graphql-eslint/relay'
| 'plugin:@graphql-eslint/schema-all'
| 'plugin:@graphql-eslint/schema-recommended';

View File

@ -0,0 +1,9 @@
/**
* Eslint import extends.
*
* @see [Eslint import extends](https://github.com/benmosher/eslint-plugin-import#installation)
*/
export type ImportExtends =
| 'plugin:import/errors'
| 'plugin:import/warnings'
| 'plugin:import/typescript';

View File

@ -0,0 +1,6 @@
/**
* Eslint JSDoc extends.
*
* @see [Eslint JSDoc extends](https://github.com/gajus/eslint-plugin-jsdoc#configuration)
*/
export type JsdocExtends = 'plugin:jsdoc/recommended';

View File

@ -0,0 +1,13 @@
/**
* Eslint Jsonc extends.
*
* @see [Eslint Jsonc extends](https://github.com/ota-meshi/eslint-plugin-jsonc#configuration)
*/
export type JsoncExtends =
| 'plugin:jsdoc/base'
| 'plugin:jsdoc/recommended'
| 'plugin:jsonc/recommended-with-json'
| 'plugin:jsonc/recommended-with-jsonc'
| 'plugin:jsonc/recommended-with-json5'
| 'plugin:jsonc/prettier'
| 'plugin:jsonc/all';

View File

@ -0,0 +1,8 @@
/**
* Eslint JSX A11y extends.
*
* @see [Eslint JSX A11y extends](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y)
*/
export type JsxA11yExtends =
| 'plugin:jsx-a11y/strict'
| 'plugin:jsx-a11y/recommended';

View File

@ -0,0 +1,10 @@
/**
* Eslint MDX extends.
*
* @see [Eslint MDX extends](https://github.com/mdx-js/eslint-mdx/tree/master/packages/eslint-plugin-mdx)
*/
export type MdxExtends =
| 'plugin:mdx/base'
| 'plugin:mdx/code-blocks'
| 'plugin:mdx/overrides'
| 'plugin:mdx/recommended';

View File

@ -0,0 +1,9 @@
/**
* Eslint N (Node) extends.
*
* @see [Eslint N extends](https://github.com/eslint-community/eslint-plugin-n#-configs)
*/
export type NExtends =
| 'plugin:n/recommended'
| 'plugin:n/recommended-module'
| 'plugin:n/recommended-script';

View File

@ -0,0 +1,9 @@
/**
* Eslint Node extends.
*
* @see [Eslint Node extends](https://github.com/mysticatea/eslint-plugin-node#-configs)
*/
export type NodeExtends =
| 'plugin:node/recommended'
| 'plugin:node/recommended-module'
| 'plugin:node/recommended-script';

View File

@ -0,0 +1,6 @@
/**
* Eslint Prettier extends.
*
* @see [Eslint Prettier extends](https://github.com/prettier/eslint-plugin-prettier#recommended-configuration)
*/
export type PrettierExtends = 'plugin:prettier/recommended' | 'prettier';

View File

@ -0,0 +1,6 @@
/**
* Eslint promise extends.
*
* @see [Eslint promise extends](https://github.com/eslint-community/eslint-plugin-promise#usage)
*/
export type PromiseExtends = 'plugin:promise/recommended';

View File

@ -0,0 +1,6 @@
/**
* Eslint ReactHooks extends.
*
* @see [Eslint ReactHooks extends](https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks)
*/
export type ReactHooksExtends = 'plugin:react-hooks/recommended';

View File

@ -0,0 +1,9 @@
/**
* Eslint React extends.
*
* @see [Eslint React extends](https://github.com/jsx-eslint/eslint-plugin-react)
*/
export type ReactExtends =
| 'plugin:react/all'
| 'plugin:react/jsx-runtime'
| 'plugin:react/recommended';

View File

@ -0,0 +1,6 @@
/**
* Eslint Sonarjs extends.
*
* @see [Eslint Sonarjs extends](https://github.com/SonarSource/eslint-plugin-sonarjs#available-configurations)
*/
export type SonarjsExtends = 'plugin:sonarjs/recommended';

View File

@ -0,0 +1,11 @@
/**
* Eslint TestingLibrary extends.
*
* @see [Eslint TestingLibrary extends](https://github.com/testing-library/eslint-plugin-testing-library)
*/
export type TestingLibraryExtends =
| 'plugin:testing-library/angular'
| 'plugin:testing-library/dom'
| 'plugin:testing-library/marko'
| 'plugin:testing-library/react'
| 'plugin:testing-library/vue';

View File

@ -0,0 +1,8 @@
/**
* Eslint Unicorn extends.
*
* @see [Eslint Unicorn extends](https://github.com/sindresorhus/eslint-plugin-unicorn)
*/
export type UnicornExtends =
| 'plugin:unicorn/recommended'
| 'plugin:unicorn/all';

View File

@ -0,0 +1,6 @@
/**
* Eslint Vitest extends.
*
* @see [Eslint Vitest extends](https://eslint.vuejs.org/user-guide/#usage)
*/
export type VitestExtends = 'plugin:vitest/all' | 'plugin:vitest/recommended';

View File

@ -0,0 +1,13 @@
/**
* Eslint Vue Pug extends.
*
* @see [Eslint Vue Pug extends](https://github.com/rashfael/eslint-plugin-vue-pug#usage)
*/
export type VuePugExtends =
| 'plugin:vue-pug/base'
| 'plugin:vue-pug/vue3-essential'
| 'plugin:vue-pug/vue3-strongly-recommended'
| 'plugin:vue-pug/vue3-recommended'
| 'plugin:vue-pug/essential'
| 'plugin:vue-pug/strongly-recommended'
| 'plugin:vue-pug/recommended';

View File

@ -0,0 +1,13 @@
/**
* Eslint Vue extends.
*
* @see [Eslint Vue extends](https://eslint.vuejs.org/user-guide/#usage)
*/
export type VueExtends =
| 'plugin:vue/base'
| 'plugin:vue/vue3-essential'
| 'plugin:vue/vue3-strongly-recommended'
| 'plugin:vue/vue3-recommended'
| 'plugin:vue/essential'
| 'plugin:vue/strongly-recommended'
| 'plugin:vue/recommended';

4
src/types/config/extends/eslint.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
/**
* Eslint extends.
*/
export type EslintExtends = 'eslint:recommended' | 'eslint:all';

82
src/types/config/extends/index.d.ts vendored Normal file
View File

@ -0,0 +1,82 @@
import type { LiteralUnion } from '../../utility-types';
import type { EslintExtends } from './eslint';
import type { EslintCommentsExtends } from './eslint-plugin-eslint-comment';
import type { GraphqlExtends } from './eslint-plugin-graphql';
import type { ImportExtends } from './eslint-plugin-import';
import type { JsdocExtends } from './eslint-plugin-jsdoc';
import type { JsoncExtends } from './eslint-plugin-jsonc';
import type { MdxExtends } from './eslint-plugin-mdx';
import type { NExtends } from './eslint-plugin-n';
import type { NodeExtends } from './eslint-plugin-node';
import type { PrettierExtends } from './eslint-plugin-prettier';
import type { PromiseExtends } from './eslint-plugin-promise';
import type { ReactExtends } from './eslint-plugin-react';
import type { ReactHooksExtends } from './eslint-plugin-react-hooks';
import type { SonarjsExtends } from './eslint-plugin-sonarjs';
import type { TestingLibraryExtends } from './eslint-plugin-testing-library';
import type { UnicornExtends } from './eslint-plugin-unicorn';
import type { VitestExtends } from './eslint-plugin-vitest';
import type { VueExtends } from './eslint-plugin-vue';
import type { VuePugExtends } from './eslint-plugin-vue-pug';
import type { IntlifyVueI18nExtends } from './intlify-vue-i18n';
import type { TypescriptEslintExtends } from './typescript-eslint';
/**
* This is a special exported interface for other packages to declare
* additional extensions that should bail out for eslint extensions. For example
* `'@typescript-eslint/eslint-plugin'` can declare it like so in its `d.ts`:
*
* ```ts
* declare module 'eslint-define-config' {
* export interface CustomExtends {
* 'plugin:@typescript-eslint/all': void;
* 'plugin:@typescript-eslint/base': void;
* 'plugin:@typescript-eslint/disable-type-checked': void;
* 'plugin:@typescript-eslint/eslint-recommended': void;
* 'plugin:@typescript-eslint/recommended-type-checked': void;
* 'plugin:@typescript-eslint/recommended': void;
* 'plugin:@typescript-eslint/strict-type-checked': void;
* 'plugin:@typescript-eslint/strict': void;
* 'plugin:@typescript-eslint/stylistic-type-checked': void;
* 'plugin:@typescript-eslint/stylistic': void;
* }
* }
* ```
*/
export interface CustomExtends {}
/**
* All known extends.
*/
export type KnownExtends = LiteralUnion<
| EslintCommentsExtends
| EslintExtends
| GraphqlExtends
| ImportExtends
| IntlifyVueI18nExtends
| JsdocExtends
| JsoncExtends
| MdxExtends
| NExtends
| NodeExtends
| PrettierExtends
| PromiseExtends
| ReactExtends
| ReactHooksExtends
| SonarjsExtends
| TestingLibraryExtends
| TypescriptEslintExtends
| UnicornExtends
| VitestExtends
| VueExtends
| VuePugExtends
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
| keyof CustomExtends
>;
/**
* Extending Configuration Files.
*
* @see [Extends](https://eslint.org/docs/user-guide/configuring/configuration-files#extending-configuration-files)
*/
export type Extends = KnownExtends | KnownExtends[];

View File

@ -0,0 +1,6 @@
/**
* Eslint Intlify VueI18n extends.
*
* @see [Eslint Intlify VueI18n extends](https://eslint-plugin-vue-i18n.intlify.dev/started.html)
*/
export type IntlifyVueI18nExtends = 'plugin:@intlify/vue-i18n/recommended';

View File

@ -0,0 +1,17 @@
/**
* Typescript eslint extends.
*
* @see [Typescript eslint extends](https://typescript-eslint.io/linting/configs#recommended-configurations)
*/
export type TypescriptEslintExtends =
| 'plugin:@typescript-eslint/all'
| 'plugin:@typescript-eslint/base'
| 'plugin:@typescript-eslint/disable-type-checked'
| 'plugin:@typescript-eslint/eslint-recommended'
| 'plugin:@typescript-eslint/recommended-requiring-type-checking' // this requiring-type-checking is deprecated and only for @typescript-eslint/eslint-plugin@v5
| 'plugin:@typescript-eslint/recommended-type-checked'
| 'plugin:@typescript-eslint/recommended'
| 'plugin:@typescript-eslint/strict-type-checked'
| 'plugin:@typescript-eslint/strict'
| 'plugin:@typescript-eslint/stylistic-type-checked'
| 'plugin:@typescript-eslint/stylistic';

121
src/types/config/index.d.ts vendored Normal file
View File

@ -0,0 +1,121 @@
import type { Parser, ParserOptions } from '../parser-options';
import type { Rules } from '../rules';
import type { Environments } from './env';
import type { Extends } from './extends';
import type { Overrides } from './overrides';
import type { Plugin } from './plugin';
import type { Settings } from './settings';
/**
* ESLint Configuration.
*
* @see [ESLint Configuration](https://eslint.org/docs/latest/user-guide/configuring/)
*/
export interface ESLintConfig {
/**
* @see [Using Configuration Files](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#using-configuration-files)
*/
root?: boolean;
/**
* Tell ESLint to ignore specific files and directories.
*
* @see [Ignore Patterns](https://eslint.org/docs/latest/user-guide/configuring/ignoring-code)
*/
ignorePatterns?: string[];
/**
* An environment provides predefined global variables.
*
* @see [Environments](https://eslint.org/docs/latest/user-guide/configuring/language-options#specifying-environments)
*/
env?: Environments;
/**
* Extending Configuration Files.
*
* @see [Extends](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#extending-configuration-files)
*/
extends?: Extends;
/**
* Specifying Globals.
*
* @see [Globals](https://eslint.org/docs/latest/user-guide/configuring/language-options#specifying-globals)
*/
globals?: Record<
string,
'readonly' | 'writable' | false | 'readable' | true | 'writeable' | 'off'
>;
/**
* Parser.
*
* @see [Working with Custom Parsers](https://eslint.org/docs/latest/developer-guide/working-with-custom-parsers)
* @see [Specifying Parser](https://eslint.org/docs/latest/user-guide/configuring/plugins#configure-a-parser)
*/
parser?: Parser;
/**
* Parser Options.
*
* @see [Working with Custom Parsers](https://eslint.org/docs/latest/developer-guide/working-with-custom-parsers)
* @see [Specifying Parser Options](https://eslint.org/docs/latest/user-guide/configuring/language-options#specifying-parser-options)
*/
parserOptions?: ParserOptions;
/**
* Which third-party plugins define additional rules, environments, configs, etc. for ESLint to use.
*
* @see [Configuring Plugins](https://eslint.org/docs/latest/user-guide/configuring/plugins#configure-plugins)
*/
plugins?: Plugin[];
/**
* Specifying Processor.
*
* @see [processor](https://eslint.org/docs/latest/user-guide/configuring/plugins#specify-a-processor)
*/
processor?: string;
/**
* Rules.
*
* @see [Rules](https://eslint.org/docs/latest/user-guide/configuring/rules)
*/
rules?: Partial<Rules>;
/**
* Overrides.
*
* @see [How do overrides work](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#how-do-overrides-work)
*/
overrides?: Overrides;
/**
* Settings.
*
* @see [Settings](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#adding-shared-settings)
*/
settings?: Settings;
/**
* Disabling Inline Comments.
*
* @see [Disabling Inline Comments](https://eslint.org/docs/latest/user-guide/configuring/rules#disabling-inline-comments)
*/
noInlineConfig?: boolean;
/**
* Report unused `ESLint-disable` comments.
*
* @see [Report unused `ESLint-disable` comments](https://eslint.org/docs/latest/user-guide/configuring/rules#report-unused-eslint-disable-comments)
*/
reportUnusedDisableDirectives?: boolean;
}
export * from './env';
export * from './extends';
export * from './overrides';
export * from './plugin';
export * from './settings';

96
src/types/config/overrides.d.ts vendored Normal file
View File

@ -0,0 +1,96 @@
import type { Parser, ParserOptions } from '../parser-options';
import type { Rules } from '../rules';
import type { Environments } from './env';
import type { Extends } from './extends';
import type { Plugin } from './plugin';
import type { Settings } from './settings';
/**
* OverrideConfigData.
*/
export interface Override {
/**
* The glob patterns for target files.
*/
// https://github.com/eslint/eslint/blob/762a8727fb3b5619cff900826053b643ca5f1162/lib/shared/types.js#L61
files: string[] | string;
/**
* The glob patterns for excluded files.
*/
// https://github.com/eslint/eslint/blob/762a8727fb3b5619cff900826053b643ca5f1162/lib/shared/types.js#L59
excludedFiles?: string | string[];
/**
* An environment provides predefined global variables.
*
* @see [Environments](https://eslint.org/docs/user-guide/configuring/language-options#specifying-environments)
*/
env?: Environments;
/**
* Extending Configuration Files.
*
* @see [Extends](https://eslint.org/docs/user-guide/configuring/configuration-files#extending-configuration-files)
*/
extends?: Extends;
/**
* Specifying Globals.
*
* @see [Globals](https://eslint.org/docs/latest/user-guide/configuring/language-options#specifying-globals)
*/
globals?: Record<
string,
'readonly' | 'writable' | false | 'readable' | true | 'writeable' | 'off'
>;
/**
* Parser.
*
* @see [Working with Custom Parsers](https://eslint.org/docs/developer-guide/working-with-custom-parsers)
* @see [Specifying Parser](https://eslint.org/docs/user-guide/configuring/plugins#specifying-parser)
*/
parser?: Parser;
/**
* Parser Options.
*
* @see [Working with Custom Parsers](https://eslint.org/docs/developer-guide/working-with-custom-parsers)
* @see [Specifying Parser Options](https://eslint.org/docs/user-guide/configuring/language-options#specifying-parser-options)
*/
parserOptions?: ParserOptions;
/**
* Which third-party plugins define additional rules, environments, configs, etc. for ESLint to use.
*
* @see [Configuring Plugins](https://eslint.org/docs/user-guide/configuring/plugins#configuring-plugins)
*/
plugins?: Plugin[];
/**
* Specifying Processor.
*
* @see [processor](https://eslint.org/docs/user-guide/configuring/plugins#specifying-processor)
*/
processor?: string;
/**
* Rules.
*
* @see [Rules](https://eslint.org/docs/user-guide/configuring/rules)
*/
rules?: Partial<Rules>;
/**
* Settings.
*
* @see [Settings](https://eslint.org/docs/user-guide/configuring/configuration-files#adding-shared-settings)
*/
settings?: Settings;
}
/**
* Overrides.
*/
export type Overrides = Override[];

40
src/types/config/plugin.d.ts vendored Normal file
View File

@ -0,0 +1,40 @@
import type { LiteralUnion } from '../utility-types';
/**
* This is a special exported interface for other packages to declare
* additional plugins that should bail out for eslint plugins. For example
* `'@typescript-eslint/eslint-plugin'` can declare it like so in its `d.ts`:
*
* ```ts
* declare module 'eslint-define-config' {
* export interface CustomPlugins {
* '@typescript-eslint': void;
* }
* }
* ```
*/
export interface CustomPlugins {}
/** Plugin. */
export type Plugin = LiteralUnion<
| '@graphql-eslint'
| '@typescript-eslint'
| 'deprecation'
| 'import'
| 'inclusive-language'
| 'jsdoc'
| 'jsx-a11y'
| 'mdx'
| 'prettier'
| 'promise'
| 'react-hooks'
| 'react'
| 'sonarjs'
| 'spellcheck'
| 'testing-library'
| 'unicorn'
| 'vitest'
| 'vue'
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
| keyof CustomPlugins
>;

66
src/types/config/settings/import.d.ts vendored Normal file
View File

@ -0,0 +1,66 @@
import type { Parser } from '../../parser-options';
/**
* Import settings.
*/
export interface ImportSettings {
/**
* A list of file extensions that will be parsed as modules and inspected for `export`s.
*
* @see [import/extensions](https://github.com/benmosher/eslint-plugin-import/blob/master/README.md#importextensions)
*/
'import/extensions'?: string[];
/**
* If you require more granular extension definitions.
*
* @see [import/resolver](https://github.com/benmosher/eslint-plugin-import/blob/master/README.md#importresolver)
*/
'import/resolver'?: string | Record<string, unknown>;
/**
* A list of regex strings that, if matched by a path, will not report the matching module if no `exports` are found.
*
* @see [import/ignore](https://github.com/benmosher/eslint-plugin-import/blob/master/README.md#importignore)
*/
'import/ignore'?: string[];
/**
* An array of additional modules to consider as "core" modules--modules that should be considered resolved but have no path on the filesystem.
*
* @see [import/core-modules](https://github.com/benmosher/eslint-plugin-import/blob/master/README.md#importcore-modules)
*/
'import/core-modules'?: string[];
/**
* An array of folders. Resolved modules only from those folders will be considered as "external".
*
* @default ["node_modules"]
*
* @see [import/external-module-folders](https://github.com/benmosher/eslint-plugin-import/blob/master/README.md#importexternal-module-folders)
*/
'import/external-module-folders'?: string[];
/**
* A map from parsers to file extension arrays.
*
* @see [import/parsers](https://github.com/benmosher/eslint-plugin-import/blob/master/README.md#importparsers)
*/
'import/parsers'?: Partial<Record<Parser, string[]>>;
/**
* Settings for cache behavior.
*
* @see [import/cache](https://github.com/benmosher/eslint-plugin-import/blob/master/README.md#importcache)
*/
'import/cache'?: { lifetime?: number } & Record<string, unknown>;
/**
* A regex for packages should be treated as internal.
*
* Useful when you are utilizing a monorepo setup or developing a set of packages that depend on each other.
*
* @see [import/internal-regex](https://github.com/benmosher/eslint-plugin-import/blob/master/README.md#importinternal-regex)
*/
'import/internal-regex'?: string;
}

36
src/types/config/settings/index.d.ts vendored Normal file
View File

@ -0,0 +1,36 @@
import type { ImportSettings } from './import';
import type { JSDocSettings } from './jsdoc';
import type { JsxA11ySettings } from './jsx-a11y';
import type { MdxSettings } from './mdx';
import type { NodeSettings } from './node';
import type { ReactSettings } from './react';
/**
* This is a special exported interface for other packages to declare
* additional settings that should bail out for eslint settings. For example
* `'eslint-plugin-jsx-a11y'` can declare it like so in its `d.ts`:
*
* ```ts
* declare module 'eslint-define-config' {
* export interface CustomSettings {
* 'jsx-a11y': {
* components?: Record<string, string>;
* };
* }
* }
* ```
*/
export interface CustomSettings {}
/**
* Settings.
*/
export interface Settings
extends ImportSettings,
JSDocSettings,
JsxA11ySettings,
MdxSettings,
NodeSettings,
ReactSettings,
Partial<CustomSettings>,
Partial<Record<string, unknown>> {}

59
src/types/config/settings/jsdoc.d.ts vendored Normal file
View File

@ -0,0 +1,59 @@
/**
* JSDoc settings.
*/
export interface JSDocSetting extends Partial<Record<string, unknown>> {
/**
* Disables all rules for the comment block on which a `@private` tag (or `@access private`) occurs.
*
* Defaults to `false`.
*
* Note: This has no effect with the rule `check-access` (whose purpose is to check access modifiers) or `empty-tags` (which checks `@private` itself).
*
* @see [mode](https://github.com/gajus/eslint-plugin-jsdoc#allow-tags-private-or-internal-to-disable-rules-for-that-comment-block)
*/
ignorePrivate?: boolean;
/**
* Disables all rules for the comment block on which a `@internal` tag occurs.
*
* Defaults to `false`.
*
* Note: This has no effect with the rule `empty-tags` (which checks `@internal` itself).
*
* @see [mode](https://github.com/gajus/eslint-plugin-jsdoc#allow-tags-private-or-internal-to-disable-rules-for-that-comment-block)
*/
ignoreInternal?: boolean;
/**
* Set to `typescript`, `closure`, or `jsdoc` (the default unless the `@typescript-eslint` parser is in use in which case `typescript` will be the default).
*
* @see [mode](https://github.com/gajus/eslint-plugin-jsdoc#mode)
*/
mode?: 'typescript' | 'closure' | 'jsdoc';
/**
* Configure a preferred alias name for a JSDoc tag.
*
* @see [Alias Preference](https://github.com/gajus/eslint-plugin-jsdoc#alias-preference)
*/
tagNamePreference?: Record<
string,
string | { message: string; replacement?: string } | false
>;
overrideReplacesDocs?: boolean;
augmentsExtendsReplacesDocs?: boolean;
implementsReplacesDocs?: boolean;
preferredTypes?: unknown;
}
/**
* JSDoc settings.
*/
export interface JSDocSettings {
/**
* JSDoc settings.
*
* @see [JSDoc settings](https://github.com/gajus/eslint-plugin-jsdoc#eslint-plugin-jsdoc-settings)
*/
jsdoc?: JSDocSetting;
}

10
src/types/config/settings/jsx-a11y.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
/**
* JSX A11y settings.
*
* @see [JSX A11y settings](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y)
*/
export interface JsxA11ySettings extends Partial<Record<string, unknown>> {
'jsx-a11y'?: {
components?: Record<string, string>;
};
}

6
src/types/config/settings/mdx.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
import type { ESLintMdxSettings } from 'eslint-plugin-mdx';
/**
* MDX settings.
*/
export type MdxSettings = ESLintMdxSettings;

18
src/types/config/settings/node.d.ts vendored Normal file
View File

@ -0,0 +1,18 @@
/**
* Node settings.
*/
export interface NodeSetting extends Partial<Record<string, unknown>> {
allowModules?: string[];
resolvePaths?: string[];
tryExtensions?: string[];
}
/**
* Node settings.
*/
export interface NodeSettings {
/**
* Node settings.
*/
node?: NodeSetting;
}

103
src/types/config/settings/react.d.ts vendored Normal file
View File

@ -0,0 +1,103 @@
import type { LiteralUnion } from '../../utility-types';
/**
* React settings.
*
* @see [React settings](https://github.com/jsx-eslint/eslint-plugin-react)
*/
export interface ReactSettings extends Partial<Record<string, unknown>> {
react?: {
/**
* Regex for Component Factory to use.
*
* @default 'createReactClass'
*/
createClass?: LiteralUnion<'createReactClass'>;
/**
* Pragma to use.
*
* @default 'React'
*/
pragma?: LiteralUnion<'React'>;
/**
* Fragment to use (may be a property of <pragma>).
*
* @default 'Fragment'
*/
fragment?: LiteralUnion<'Fragment'>;
/**
* React version. "detect" automatically picks the version you have installed.
*
* You can also use `16.0`, `16.3`, etc, if you want to override the detected value.
*
* It will default to "latest" and warn if missing, and to "detect" in the future.
*
* @default 'latest'
*/
version?: LiteralUnion<'latest' | 'detect'>;
/**
* Flow version.
*/
flowVersion?: string;
};
/**
* The names of any function used to wrap propTypes, e.g. `forbidExtraProps`.
*
* If this isn't set, any propTypes wrapped in a function will be skipped.
*/
propWrapperFunctions?: Array<
| string
| {
property: string;
object?: string;
/**
* For rules that check exact prop wrappers.
*/
exact?: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[k: string]: any;
}
>;
/**
* The name of any function used to wrap components, e.g. Mobx `observer` function.
*
* If this isn't set, components wrapped by these functions will be skipped.
*/
componentWrapperFunctions?: Array<
| string
| {
property: string;
object?: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[k: string]: any;
}
>;
/**
* Components used as alternatives to <form> for forms, eg. <Form endpoint={ url } />.
*/
formComponents?: Array<
| string
| {
name: string;
formAttribute: string;
}
>;
/**
* Components used as alternatives to <a> for linking, eg. <Link to={ url } />.
*/
linkComponents?: Array<
| string
| {
name: string;
linkAttribute: string;
}
>;
}

Some files were not shown because too many files have changed in this diff Show More