Compare commits

...

11 Commits

Author SHA1 Message Date
179cf83891 Inline repo 2024-04-19 21:42:48 -04:00
fb50ede688 Update 2024-04-17 00:43:45 -04:00
51455e3c21 Update 2024-04-16 22:29:31 -04:00
1d955d951d Bump ver 2024-04-06 19:15:05 -04:00
8ba9b0725a Update 2024-04-06 19:05:14 -04:00
4d2762de39 Update 2024-03-30 02:06:38 -04:00
19b07691fc bump 2024-03-14 22:31:04 -04:00
4c67de9f72 Update 2024-03-04 03:37:23 -05:00
9ea078f414 Bump 2024-02-26 03:29:42 -05:00
ba2671d760 New patch 2024-02-03 20:22:42 -05:00
d8f224c5cf Update and refactor 2024-01-13 18:20:36 -05:00
108 changed files with 12320 additions and 1367 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
!/packages/eslint-plugin-react-hooks
/react
src/types/rules
dist/**/*.js
dist/**/*.js.map

View File

@ -1,5 +1,7 @@
# eslint-rules
Personal ESLint config. Guaranteed to have no useless polyfills.
## flat config support
- ❌ [import](https://github.com/import-js/eslint-plugin-import/issues/2556)

View File

@ -1,6 +0,0 @@
#!/bin/bash
./src/build-local-rules.ts
npx dts-bundle-generator "./src/index.ts" -o "./dist/index.d.ts" --project "./tsconfig.build.json" --no-check
# printf "\nimport './types.d';" >> "./dist/index.d.ts"
NODE_ENV=production ./esbuild.ts
sed -i '' '/import.*redirect.*;/d' "dist/index.d.ts"

1
dist/.npmrc vendored Normal file
View File

@ -0,0 +1 @@
shamefully-hoist=true

46
dist/index.d.ts vendored
View File

@ -1,4 +1,4 @@
// Generated by dts-bundle-generator v9.2.1
// Generated by dts-bundle-generator v9.4.0
import { ESLintUtils } from '@typescript-eslint/utils';
import { Rule } from 'eslint';
@ -26,32 +26,28 @@ export interface LocalRuleOptions {
"rules/no-empty-object-literal": RuleEntry<unknown>;
}
export type RuleOptions = Rules & Partial<LocalRuleOptions>;
export interface CustomRule {
rule: () => Promise<{
default: Rule.RuleModule | ESLintUtils.RuleModule<string, unknown[]>;
}>;
options?: RuleLevel;
}
/**
* ESLint Configuration.
* @see [ESLint Configuration](https://eslint.org/docs/latest/user-guide/configuring/)
*/
export type Config = Omit<ESLintConfig, "rules"> & {
export type InputConfig = Omit<ESLintConfig, "rules"> & {
/**
* Rules.
* @see [Rules](https://eslint.org/docs/latest/user-guide/configuring/rules)
*/
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.
*/
customRules?: {
rule: () => Promise<{
default: Rule.RuleModule | ESLintUtils.RuleModule<string, unknown[]>;
}>;
options?: RuleLevel;
}[];
};
export declare function defineCustomRule<Options extends readonly unknown[]>(rule: () => Promise<{
default: Rule.RuleModule | ESLintUtils.RuleModule<string, Options>;
}>, options?: Options): {
rule: () => Promise<{
default: Rule.RuleModule | ESLintUtils.RuleModule<string, Options>;
}>;
options: Options | undefined;
customRuleFiles?: string | string[];
};
/**
* Returns a ESLint config object.
@ -59,16 +55,18 @@ export declare function defineCustomRule<Options extends readonly unknown[]>(rul
* 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)
* 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)
* 6. [jsdoc](https://github.com/gajus/eslint-plugin-jsdoc#rules)
* 1. [`react`](https://github.com/jsx-eslint/eslint-plugin-react#list-of-supported-rules)
* (automatically enables
* [`react-hooks`](https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks))
* 2. [`react-refresh`](https://github.com/ArnaudBarre/eslint-plugin-react-refresh)
* 3. [`jsx-a11y`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y#supported-rules)
* 4. [`unicorn`](https://github.com/sindresorhus/eslint-plugin-unicorn#rules)
* 5. [`n`](https://github.com/eslint-community/eslint-plugin-n#-rules) (Node.js specific)
* 6. [`jsdoc`](https://github.com/gajus/eslint-plugin-jsdoc#rules)
*
* Non bundled:
* 1. [graphql](https://the-guild.dev/graphql/eslint/rules)
* 1. [`graphql`](https://the-guild.dev/graphql/eslint/rules)
*/
export declare function extendConfig({ plugins, settings, rules, extends: _extends, overrides, customRules, ...rest }?: Config): ESLintConfig;
export declare function extendConfig(of?: InputConfig): ESLintConfig;
export {};

View File

@ -0,0 +1,4 @@
{
"name": "is-core-module",
"version": "2.13.1"
}

View File

@ -0,0 +1,4 @@
{
"name": "supports-preserve-symlinks-flag",
"version": "1.0.0"
}

55
dist/package.json vendored
View File

@ -1,45 +1,60 @@
{
"name": "@aet/eslint-rules",
"version": "0.0.11",
"version": "0.0.24-beta.1",
"license": "UNLICENSED",
"peerDependencies": {
"eslint": "^8.53.0",
"typescript": "^5.2.2"
"eslint": "^8.57.0",
"typescript": "^5.4.4"
},
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/eslint": "^8.56.0",
"@typescript-eslint/eslint-plugin": "^6.16.0",
"@typescript-eslint/parser": "^6.16.0",
"@typescript-eslint/type-utils": "^6.16.0",
"@typescript-eslint/utils": "^6.16.0",
"@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.8.3",
"axe-core": "^4.9.0",
"axobject-query": "^4.0.0",
"damerau-levenshtein": "1.0.8",
"debug": "^4.3.4",
"doctrine": "^3.0.0",
"emoji-regex": "^10.3.0",
"enhanced-resolve": "^5.15.0",
"eslint-config-prettier": "9.1.0",
"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.0",
"eslint-plugin-es-x": "7.5.0",
"eslint-plugin-jsdoc": "^46.10.1",
"eslint-plugin-unicorn": "^50.0.1",
"eslint-module-utils": "^2.8.1",
"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.5.0",
"estraverse": "^5.3.0",
"fast-glob": "^3.3.2",
"get-tsconfig": "^4.7.2",
"ignore": "^5.3.0",
"get-tsconfig": "^4.7.3",
"ignore": "^5.3.1",
"is-builtin-module": "^3.2.1",
"is-core-module": "^2.13.1",
"is-glob": "^4.0.3",
"language-tags": "^1.0.9",
"lodash": "^4.17.21",
"minimatch": "^9.0.3",
"minimatch": "^9.0.4",
"resolve": "^2.0.0-next.5",
"semver": "^7.5.4",
"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": {
"supports-preserve-symlinks-flag": "file:./overrides/supports-preserve-symlinks-flag",
"is-core-module": "file:./overrides/is-core-module"
}
}
}

13
dist/prettier.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
// Generated by dts-bundle-generator v9.4.0
import { Config } from 'prettier';
declare function defineConfig({ tailwind, ...config }: Partial<Config> & {
tailwind?: boolean;
}): Config;
export {
defineConfig as default,
};
export {};

16
dist/types.d.ts vendored Normal file
View File

@ -0,0 +1,16 @@
// Generated by dts-bundle-generator v9.4.0
import { ESLintUtils } from '@typescript-eslint/utils';
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[], ESLintUtils.RuleListener>;
};
export declare function defineRule({ name, create, ...meta }: Rule.RuleMetaData & {
name?: string;
create: (context: Rule.RuleContext) => Rule.RuleListener;
}): Rule.RuleModule;
export {};

1130
dist/yarn.lock vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,44 +1,49 @@
{
"name": "@aet/eslint-configs",
"scripts": {
"build": "./build.sh",
"check-import": "./src/check-imports.ts"
"build": "./scripts/build-all.ts",
"build-types": "cd ./packages/eslint-define-config && ./scripts/index.ts",
"check-import": "./scripts/check-imports.ts"
},
"private": true,
"devDependencies": {
"@babel/core": "^7.23.7",
"@babel/plugin-transform-flow-strip-types": "^7.23.3",
"@babel/preset-env": "^7.23.7",
"@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": "^8.56.0",
"@types/eslint": "^8.56.10",
"@types/esprima": "^4.0.6",
"@types/esquery": "^1.5.3",
"@types/estree": "^1.0.5",
"@types/estree-jsx": "^1.0.3",
"@types/lodash": "^4.14.202",
"@types/node": "^20.10.6",
"@typescript-eslint/eslint-plugin": "6.16.0",
"@typescript-eslint/type-utils": "^6.16.0",
"@typescript-eslint/types": "^6.16.0",
"@typescript-eslint/typescript-estree": "^6.16.0",
"@typescript-eslint/utils": "^6.16.0",
"@types/estree-jsx": "^1.0.5",
"@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.2.1",
"dts-bundle-generator": "^9.5.0",
"esbin": "0.0.4",
"esbuild": "0.19.11",
"esbuild": "0.20.2",
"esbuild-plugin-alias": "^0.2.1",
"eslint": "8.56.0",
"eslint": "8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-define-config": "^1.24.1",
"eslint-define-config": "file:./src/types",
"esprima": "^4.0.1",
"esquery": "^1.5.0",
"fast-glob": "^3.3.2",
"find-cache-dir": "^5.0.0",
"json-schema-to-ts": "^3.0.0",
"json-schema-to-ts": "^3.0.1",
"lodash": "^4.17.21",
"minimatch": "^9.0.3",
"minimatch": "^9.0.4",
"patch-package": "^8.0.0",
"picocolors": "^1.0.0",
"prettier": "^3.1.1",
"prettier": "^3.2.5",
"prop-types": "^15.8.1",
"typescript": "^5.3.3"
"typescript": "^5.4.5"
},
"prettier": {
"arrowParens": "avoid",
@ -47,5 +52,18 @@
"semi": true,
"singleQuote": true,
"trailingComma": "all"
},
"pnpm": {
"overrides": {
"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: 7b6bfc3947...7a02ac08b5

Submodule packages/eslint-plugin-import updated: 6b95a02193...f77ceb679d

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

Submodule packages/eslint-plugin-n updated: 47cd9a6a0e...eb11b5b35a

Submodule packages/eslint-plugin-react updated: ecadb92609...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,5 +1,5 @@
diff --git a/package.json b/package.json
index 79b79cb..bd8c856 100644
index 98370b5..da6cd9b 100644
--- a/package.json
+++ b/package.json
@@ -62,8 +62,7 @@

View File

@ -26,10 +26,10 @@ index 883c03b7..0111d616 100644
}
diff --git a/.eslintrc b/.eslintrc
deleted file mode 100644
index 3c9c658f..00000000
index 80e1014c..00000000
--- a/.eslintrc
+++ /dev/null
@@ -1,248 +0,0 @@
@@ -1,267 +0,0 @@
-{
- "root": true,
- "plugins": [
@ -128,6 +128,7 @@ index 3c9c658f..00000000
- "no-multiple-empty-lines": [2, { "max": 1, "maxEOF": 1, "maxBOF": 0 }],
- "no-return-assign": [2, "always"],
- "no-trailing-spaces": 2,
- "no-use-before-define": [2, { "functions": true, "classes": true, "variables": true }],
- "no-var": 2,
- "object-curly-spacing": [2, "always"],
- "object-shorthand": ["error", "always", {
@ -241,10 +242,10 @@ index 3c9c658f..00000000
- "exports": "always-multiline",
- "functions": "never"
- }],
- "prefer-destructuring": "warn",
- "prefer-destructuring": "off",
- "prefer-object-spread": "off",
- "prefer-rest-params": "off",
- "prefer-spread": "warn",
- "prefer-spread": "off",
- "prefer-template": "off",
- }
- },
@ -259,6 +260,24 @@ index 3c9c658f..00000000
- },
- {
- "files": [
- "utils/**", // TODO
- ],
- "rules": {
- "no-use-before-define": "off",
- },
- },
- {
- "files": [
- "resolvers/webpack/index.js",
- "resolvers/webpack/test/example.js",
- "utils/parse.js",
- ],
- "rules": {
- "no-console": "off",
- },
- },
- {
- "files": [
- "resolvers/*/test/**/*",
- ],
- "env": {

View File

@ -1,5 +1,5 @@
diff --git a/lib/index.js b/lib/index.js
index def1bbf..6fdff14 100644
index 49fd4c7..a0fdd81 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -1,9 +1,9 @@
@ -14,111 +14,16 @@ index def1bbf..6fdff14 100644
+import cjsConfig from "./configs/recommended-script"
+import recommendedConfig from "./configs/recommended"
const rules = {
"callback-return": require("./rules/callback-return"),
@@ -51,8 +51,8 @@ const rules = {
const mod = {
/**
* @typedef {{
@@ -20,8 +20,8 @@ const recommendedConfig = require("./configs/recommended")
/** @type {import('eslint').ESLint.Plugin & { configs: Configs }} */
const plugin = {
meta: {
- name: pkg.name,
- version: pkg.version,
+ name,
+ version,
},
rules,
}
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
rules: /** @type {Record<string, import('eslint').Rule.RuleModule>} */ ({
"callback-return": require("./rules/callback-return"),

View File

@ -171,7 +171,7 @@ index 38b4dd8b..d0575572 100644
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 c13a00cf..fb457e63 100644
index 7ea874d0..48df0dba 100644
--- a/lib/rules/jsx-key.js
+++ b/lib/rules/jsx-key.js
@@ -5,8 +5,7 @@
@ -198,7 +198,7 @@ index 17e56e2e..cb6dec1a 100644
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 a1bb4811..db051356 100644
index efeef403..33df4653 100644
--- a/lib/rules/jsx-pascal-case.js
+++ b/lib/rules/jsx-pascal-case.js
@@ -5,7 +5,7 @@
@ -211,7 +211,7 @@ index a1bb4811..db051356 100644
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 6d19f201..4b1849cc 100644
index 3ca1724e..faf58f91 100644
--- a/lib/rules/jsx-sort-props.js
+++ b/lib/rules/jsx-sort-props.js
@@ -5,7 +5,7 @@
@ -224,7 +224,7 @@ index 6d19f201..4b1849cc 100644
const toSorted = require('array.prototype.tosorted');
diff --git a/lib/rules/no-namespace.js b/lib/rules/no-namespace.js
index 64bbc8d5..b5e9c803 100644
index d7559f5e..fbfad23a 100644
--- a/lib/rules/no-namespace.js
+++ b/lib/rules/no-namespace.js
@@ -5,7 +5,7 @@
@ -237,10 +237,10 @@ index 64bbc8d5..b5e9c803 100644
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 2e77f289..cff14d45 100644
index 9491f9c6..44396948 100644
--- a/lib/rules/no-unknown-property.js
+++ b/lib/rules/no-unknown-property.js
@@ -541,7 +541,7 @@ module.exports = {
@@ -543,7 +543,7 @@ module.exports = {
create(context) {
function getIgnoreConfig() {
@ -249,7 +249,7 @@ index 2e77f289..cff14d45 100644
}
function getRequireDataLowercase() {
@@ -554,7 +554,7 @@ module.exports = {
@@ -556,7 +556,7 @@ module.exports = {
JSXAttribute(node) {
const ignoreNames = getIgnoreConfig();
const actualName = context.getSourceCode().getText(node.name);
@ -258,22 +258,6 @@ index 2e77f289..cff14d45 100644
return;
}
const name = normalizeAttributeCase(actualName);
@@ -582,6 +582,15 @@ module.exports = {
const tagName = getTagName(node);
+ if (
+ (actualName === 'css' && hasEmotion) ||
+ (tagName === 'style' &&
+ (actualName === 'global' || actualName === 'jsx') &&
+ hasNext)
+ ) {
+ return;
+ }
+
if (tagName === 'fbt' || tagName === 'fbs') { return; } // fbt/fbs nodes are bonkers, let's not go there
if (!isValidHTMLTagInJSX(node)) { return; }
diff --git a/lib/util/annotations.js b/lib/util/annotations.js
index 60aaef8c..ad8dc0bf 100644
--- a/lib/util/annotations.js

1822
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

153
scripts/build-all.ts Executable file
View File

@ -0,0 +1,153 @@
#!/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';
import esbuild from 'esbuild';
import type { Plugin } from 'esbuild';
import { memoize } from 'lodash';
import { gray, green } from 'picocolors';
import { dependencies } from '../dist/package.json';
import { dts } from './dts';
import { babelPlugin } from './modifier';
const ENV = (process.env.NODE_ENV ??= 'production');
const PROD = ENV === 'production';
declare global {
interface Array<T> {
filter(
predicate: BooleanConstructor,
): Exclude<T, null | undefined | false | '' | 0>[];
}
}
const log = memoize(console.log);
const plugins: Plugin[] = [
babelPlugin,
{
name: 'alias',
setup(build) {
build.onResolve({ filter: /^jsx-ast-utils$/ }, () => ({
path: resolve('./packages/jsx-ast-utils/src/index.js'),
}));
build.onResolve({ filter: /^jsx-ast-utils\/.+$/ }, ({ path }) => ({
path:
resolve('./packages/jsx-ast-utils/', path.slice('jsx-ast-utils/'.length)) +
'.js',
}));
},
},
];
if (process.env.DEBUG) {
plugins.push({
name: 'deps-check',
setup(build) {
const declared = new Set(Object.keys(dependencies));
build.onResolve({ filter: /^.*$/ }, ({ path, importer }) => {
if (
!path.startsWith('./') &&
!path.startsWith('../') &&
!isBuiltin(path) &&
path !== 'eslint' &&
!path.startsWith('eslint/') &&
!path.startsWith('eslint-module-utils/') &&
!declared.has(path)
) {
log(green(path), gray('from'), './' + relative(process.cwd(), importer));
}
return null;
});
},
});
}
function bundle(
entry: string,
outfile = entry
.replace('./packages/', './dist/')
.replace('src/', '')
.replace('.ts', '.js'),
) {
return esbuild.build({
entryPoints: [entry],
outfile,
bundle: true,
minify: PROD,
platform: 'node',
packages: 'external',
sourcemap: 'linked',
plugins,
define: {},
alias: {},
external: ['find-cache-dir'],
banner: {
js: '/* eslint-disable */',
},
});
}
async function editPackageJson() {
const [state, setState] = await useText('./dist/package.json');
const distPackageJson = JSON.parse(state);
const overrideList = await fs.readdir('dist/overrides');
const npmOverrides = Object.fromEntries(
overrideList.map(name => [name, `file:./overrides/${name}`]),
);
Object.assign(distPackageJson, {
overrides: npmOverrides,
resolutions: Object.fromEntries(
overrideList.map(name => [`**/${name}`, `file:./overrides/${name}`]),
),
pnpm: { overrides: npmOverrides },
});
await setState(JSON.stringify(distPackageJson, null, 2) + '\n');
}
async function useText(path: string) {
const state = await fs.readFile(path, 'utf-8');
const setState = (text: string) => fs.writeFile(path, text);
return [state, setState] as const;
}
function bundleType(source: string, output: string) {
return dts({
source,
dist: output,
project: './tsconfig.build.json',
});
}
async function main() {
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');
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'),
bundle('./src/types.ts', './dist/types.js'),
bundle('./src/prettier.ts', './dist/prettier.js'),
editPackageJson(),
]);
console.log('Removing redirect...');
const [distIndex, setDistIndex] = await useText('./dist/index.js');
await setDistIndex(distIndex.replace(/import.*redirect.*;/g, ''));
}
void main();

View File

@ -1,8 +1,7 @@
#!/usr/bin/env tsx
import { readdirSync, writeFileSync } from 'node:fs';
import { promises as fs } from 'node:fs';
import { camelCase } from 'lodash';
const files = readdirSync('./src/rules')
const files = (await fs.readdir('./src/rules'))
.filter(file => file.endsWith('.ts'))
.filter(file => file !== 'index.ts')
.map(file => file.slice(0, -3));
@ -21,4 +20,5 @@ export const rules: Record<
};
`.trim();
writeFileSync('./src/rules/index.ts', entryFile + '\n');
console.log('Building local rules...');
await fs.writeFile('./src/rules/index.ts', entryFile + '\n');

69
scripts/check-imports.ts Executable file
View File

@ -0,0 +1,69 @@
#!/usr/bin/env bun
import glob from 'fast-glob';
import fs from 'fs';
import { builtinModules } from 'module';
import { uniq } from 'lodash';
import { dependencies, peerDependencies, overrides } from '../dist/package.json';
function checkImports() {
const deps = Object.keys({ ...dependencies, ...peerDependencies, ...overrides }).concat(
'eslint',
);
const builtIn = new Set(builtinModules.flatMap(module => [module, `node:${module}`]));
function findRequires(text: string) {
const list = Array.from(text.matchAll(/require\(["']([^"']+)["']\)/g))
.map(m => m[1])
.filter(
module =>
!(
builtIn.has(module) ||
module.startsWith('eslint/') ||
module.startsWith('typescript/')
),
);
return uniq(list);
}
const moduleMap = glob
.sync(['dist/**/*.js', '!dist/node_modules/**'])
.map(path => ({ key: path, value: findRequires(fs.readFileSync(path, 'utf8')) }));
const files = Object.fromEntries(
moduleMap
.map(({ key, value }) => ({
key,
value: value.filter(
module =>
!(deps.includes(module) || deps.some(dep => module.startsWith(`${dep}/`))),
),
}))
.filter(({ value }) => value.length > 0)
.map(({ key, value }) => [key, value]),
);
const uselessDeps = Object.keys(dependencies).filter(
dep => !moduleMap.some(({ value }) => value.includes(dep)),
);
return {
missingImports: files,
unusedDependencies: uselessDeps,
};
}
function checkDeps() {
const pkgJson = glob
.sync(['dist/node_modules/@*/*/package.json', 'dist/node_modules/*/package.json'])
.sort()
.map(path => fs.readFileSync(path, 'utf8'))
.map(content => JSON.parse(content))
.filter(({ author }) => JSON.stringify(author ?? 'null').includes('ljharb'))
.map(({ name }) => name);
return { suspiciousPackages: pkgJson };
}
console.log({
...checkImports(),
...checkDeps(),
});

39
scripts/dts.ts Normal file
View File

@ -0,0 +1,39 @@
#!/usr/bin/env node
import * as ts from 'typescript';
import {
generateDtsBundle,
type EntryPointConfig,
} from 'dts-bundle-generator/dist/bundle-generator';
export function dts({
source,
dist,
project,
}: {
source: string;
dist: string;
project: string;
}): void {
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: false,
});
ts.sys.writeFile(dist, generatedDts[0]);
}

118
esbuild.ts → scripts/modifier.ts Executable file → Normal file
View File

@ -1,22 +1,16 @@
#!/usr/bin/env tsx
import assert from 'node:assert';
import { readFileSync } from 'node:fs';
import { resolve, extname, relative } from 'node:path';
import { isBuiltin } from 'node:module';
import esbuild from 'esbuild';
import { resolve, extname } from 'node:path';
import type { Loader, Plugin } from 'esbuild';
import * as babel from '@babel/core';
import { memoize } from 'lodash';
import { gray, green } from 'picocolors';
import type { types as t, types } from '@babel/core';
import { dependencies } from './dist/package.json';
import { createMacro, type MacroHandler } from 'babel-plugin-macros';
import * as polyfill from './src/polyfill';
import * as polyfill from '../src/polyfill';
const polyfills = Object.keys(polyfill);
const ENV = process.env.NODE_ENV || 'development';
const PROD = ENV === 'production';
const ENV = (process.env.NODE_ENV ??= 'production');
class HandlerMap {
map = new Map<string, MacroHandler>();
@ -96,6 +90,10 @@ const map = new HandlerMap()
'string.prototype.matchall',
proto(t => t.identifier('matchAll')),
)
.set(
'string.prototype.includes',
proto(t => t.identifier('includes')),
)
.set(
'object.groupby',
replace(t =>
@ -107,7 +105,7 @@ const map = new HandlerMap()
);
// es-iterator-helpers/Iterator.prototype.*
const polyfillPath = resolve(__dirname, './src/polyfill.ts');
const polyfillPath = resolve(__dirname, '../src/polyfill.ts');
const requirePolyfill = (t: typeof types, name: string) =>
t.memberExpression(
t.callExpression(t.identifier('require'), [t.stringLiteral(polyfillPath)]),
@ -124,6 +122,11 @@ for (const name of polyfills) {
);
}
map.set(
'safe-regex-test',
replace(t => requirePolyfill(t, 'safeRegexTest')),
);
function replace(getReplacement: (types: typeof t) => t.Expression): MacroHandler {
return ({ references, babel: { types: t } }) => {
references.default.forEach(referencePath => {
@ -201,98 +204,3 @@ export const babelPlugin: Plugin = {
});
},
};
declare global {
interface Array<T> {
filter(
predicate: BooleanConstructor,
): Exclude<T, null | undefined | false | '' | 0>[];
}
}
const log = memoize(console.log);
const plugins: Plugin[] = [
babelPlugin,
{
name: 'alias',
setup(build) {
build.onResolve({ filter: /^jsx-ast-utils$/ }, () => ({
path: resolve('./packages/jsx-ast-utils/src/index.js'),
}));
build.onResolve({ filter: /^jsx-ast-utils\/.+$/ }, ({ path }) => ({
path:
resolve('./packages/jsx-ast-utils/', path.slice('jsx-ast-utils/'.length)) +
'.js',
}));
},
},
];
if (process.env.DEBUG) {
plugins.push({
name: 'deps-check',
setup(build) {
const declared = new Set(Object.keys(dependencies));
build.onResolve({ filter: /^.*$/ }, ({ path, importer }) => {
if (
!path.startsWith('./') &&
!path.startsWith('../') &&
!isBuiltin(path) &&
path !== 'eslint' &&
!path.startsWith('eslint/') &&
!path.startsWith('eslint-module-utils/') &&
!declared.has(path)
) {
log(green(path), gray('from'), './' + relative(process.cwd(), importer));
}
return null;
});
},
});
}
async function main(
entry: string,
outfile = entry
.replace('./packages/', './dist/')
.replace('src/', '')
.replace('.ts', '.js'),
) {
await esbuild.build({
entryPoints: [entry],
outfile,
bundle: true,
minify: PROD,
platform: 'node',
packages: 'external',
sourcemap: 'linked',
plugins,
define: {},
alias: {
// esm modules
},
external: ['find-cache-dir'],
banner: {
js: '/* eslint-disable */',
},
});
// https://github.com/eslint-types/define-config-plugin-types/issues/32
// const distPackageJson = JSON.parse(await fs.readFile('./dist/package.json', 'utf-8'));
// Object.assign(distPackageJson.dependencies, pkg.dependencies);
// await fs.writeFile(
// './dist/package.json',
// JSON.stringify(distPackageJson, null, 2) + '\n',
// );
}
main('./packages/eslint-plugin-react/index.js');
main('./packages/eslint-plugin-import/src/index.js');
main('./packages/eslint-plugin-jsx-a11y/src/index.js');
main('./packages/eslint-plugin-react-hooks/index.ts');
main('./packages/eslint-plugin-n/lib/index.js', './dist/eslint-plugin-n/index.js');
main('./packages/eslint-import-resolver-typescript/src/index.ts');
main('./src/rules/index.ts', './dist/eslint-plugin-rules/index.js');
main('./src/local/index.ts', './dist/eslint-plugin-local/index.js');
main('./src/index.ts', './dist/index.js');

View File

@ -1,13 +1,22 @@
#!/bin/bash
set -e
git-pull() {
name=$1
git config pull.rebase true
git config rebase.autoStash true
git pull --quiet
ref=$(git log -1 --pretty='{"hash":"%H","date":"%aI","committer":"%cn","subject":"%s"}')
yq -iP ".$name=$ref" ../../src/commits.json -o json
}
pull() {
echo "🚛 Pulling $2"
if [ ! -d "packages/$2" ]; then
echo "📦 Repository not found, cloning..."
git clone "https://github.com/$1/$2.git" "packages/$2"
fi
(cd "packages/$2" && git config pull.rebase true && git config rebase.autoStash true && git pull)
(cd "packages/$2" && git-pull "$2")
echo
}

View File

@ -1,46 +0,0 @@
#!/usr/bin/env bun
import glob from 'fast-glob';
import fs from 'fs';
import { builtinModules } from 'module';
import { uniq } from 'lodash';
import { dependencies, peerDependencies } from '../dist/package.json';
const deps = Object.keys({ ...dependencies, ...peerDependencies }).concat('eslint');
const builtIn = new Set(builtinModules.flatMap(module => [module, `node:${module}`]));
function findRequires(text: string) {
const list = Array.from(text.matchAll(/require\(["']([^"']+)["']\)/g))
.map(m => m[1])
.filter(
module =>
!(
builtIn.has(module) ||
module.startsWith('eslint/') ||
module.startsWith('typescript/')
),
);
return uniq(list);
}
const moduleMap = glob
.sync('dist/**/*.js')
.map(path => ({ key: path, value: findRequires(fs.readFileSync(path, 'utf8')) }));
const files = Object.fromEntries(
moduleMap
.map(({ key, value }) => ({
key,
value: value.filter(
module =>
!(deps.includes(module) || deps.some(dep => module.startsWith(`${dep}/`))),
),
}))
.filter(({ value }) => value.length > 0)
.map(({ key, value }) => [key, value]),
);
const uselessDeps = Object.keys(dependencies).filter(
dep => !moduleMap.some(({ value }) => value.includes(dep)),
);
console.log('Missing imports:', files);
console.log('Unused dependencies:', uselessDeps);

38
src/commits.json Normal file
View File

@ -0,0 +1,38 @@
{
"eslint-plugin-import": {
"hash": "f77ceb679d59ced5d9a633123385470a9eea10d9",
"date": "2024-04-07T12:55:28+12:00",
"committer": "Jordan Harband",
"subject": "[actions] cancel in-progress runs on PR updates"
},
"eslint-import-resolver-typescript": {
"hash": "7a02ac08b5aaac8c217f0e87142f97eafcc38fbc",
"date": "2024-04-01T01:06:20+00:00",
"committer": "GitHub",
"subject": "chore(deps): update dependency npm-run-all2 to ^5.0.2 (#277)"
},
"eslint-plugin-jsx-a11y": {
"hash": "0d5321a5457c5f0da0ca216053cc5b4f571b53ae",
"date": "2024-01-27T22:18:19-08:00",
"committer": "Jordan Harband",
"subject": "[Deps] update `@babel/runtime`, `safe-regex-test`"
},
"eslint-plugin-n": {
"hash": "eb11b5b35a6a797dc7fba6df53b1c4dada3a2a55",
"date": "2024-04-17T17:40:32+08:00",
"committer": "GitHub",
"subject": "chore: upgrade globals v15 (#241)"
},
"eslint-plugin-react": {
"hash": "4467db503e38b9356517cf6926d11be544ccf4b1",
"date": "2024-03-16T12:54:58+09:00",
"committer": "Jordan Harband",
"subject": "[Fix] `boolean-prop-naming`: avoid a crash with a non-TSTypeReference type"
},
"jsx-ast-utils": {
"hash": "5943318eaf23764eec3ff397ebb969613d728a95",
"date": "2023-07-28T18:34:04-07:00",
"committer": "Jordan Harband",
"subject": "v3.3.5"
}
}

View File

@ -1,10 +1,9 @@
/// <reference path="./types.d.ts" />
/// <reference path="./modules.d.ts" />
import './redirect';
import fs from 'node:fs';
import type { Rule } from 'eslint';
import type { ESLintUtils } from '@typescript-eslint/utils';
import type { ESLintConfig, Rules } from 'eslint-define-config';
// import findCacheDirectory from 'find-cache-dir';
import { typescriptRules } from './presets/typescript';
import { unicornRules } from './presets/unicorn';
import { eslintRules } from './presets/eslint';
@ -14,6 +13,7 @@ import { jsDocRules } from './presets/jsdoc';
import { graphqlRules } from './presets/graphql';
import { localRules } from './presets/local';
import { error, warn, off } from './constants';
import { tailwindRules } from './presets/tailwind';
export { error, warn, off };
@ -35,8 +35,8 @@ 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/src/rules/react/no-unknown-property.d.ts' {
export interface NoUnknownPropertyOption {
declare module 'eslint-define-config' {
interface NoUnknownPropertyOption {
extends: ('next' | 'emotion')[];
}
}
@ -55,11 +55,18 @@ export interface LocalRuleOptions {
export type RuleOptions = Rules & Partial<LocalRuleOptions>;
export interface CustomRule {
rule: () => Promise<{
default: Rule.RuleModule | ESLintUtils.RuleModule<string, unknown[]>;
}>;
options?: RuleLevel;
}
/**
* ESLint Configuration.
* @see [ESLint Configuration](https://eslint.org/docs/latest/user-guide/configuring/)
*/
type Config = Omit<ESLintConfig, 'rules'> & {
export type InputConfig = Omit<ESLintConfig, 'rules'> & {
/**
* Rules.
* @see [Rules](https://eslint.org/docs/latest/user-guide/configuring/rules)
@ -67,55 +74,51 @@ type Config = Omit<ESLintConfig, 'rules'> & {
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.
*/
customRules?: {
rule: () => Promise<{
default: Rule.RuleModule | ESLintUtils.RuleModule<string, unknown[]>;
}>;
options?: RuleLevel;
}[];
customRuleFiles?: string | string[];
};
export function defineCustomRule<Options extends readonly unknown[]>(
rule: () => Promise<{
default: Rule.RuleModule | ESLintUtils.RuleModule<string, Options>;
}>,
options?: Options,
) {
return { rule, options };
}
/**
* Returns a ESLint config object.
*
* 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)
* 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)
* 6. [jsdoc](https://github.com/gajus/eslint-plugin-jsdoc#rules)
* 1. [`react`](https://github.com/jsx-eslint/eslint-plugin-react#list-of-supported-rules)
* (automatically enables
* [`react-hooks`](https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks))
* 2. [`react-refresh`](https://github.com/ArnaudBarre/eslint-plugin-react-refresh)
* 3. [`jsx-a11y`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y#supported-rules)
* 4. [`unicorn`](https://github.com/sindresorhus/eslint-plugin-unicorn#rules)
* 5. [`n`](https://github.com/eslint-community/eslint-plugin-n#-rules) (Node.js specific)
* 6. [`jsdoc`](https://github.com/gajus/eslint-plugin-jsdoc#rules)
*
* Non bundled:
* 1. [graphql](https://the-guild.dev/graphql/eslint/rules)
* 1. [`graphql`](https://the-guild.dev/graphql/eslint/rules)
*/
export function extendConfig({
plugins,
settings,
rules,
extends: _extends,
overrides,
customRules,
...rest
}: Config = {}): ESLintConfig {
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');
export function extendConfig(of: InputConfig = {}): ESLintConfig {
const {
plugins = [],
settings,
rules,
extends: _extends,
overrides,
customRuleFiles,
...rest
} = of;
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 ruleDir = false; // ?? findCacheDirectory({ name: '_eslint-rules' });
if (ruleDir) {
@ -123,7 +126,7 @@ export function extendConfig({
fs.mkdirSync(ruleDir, { recursive: true });
}
const result: Config = {
const result: InputConfig = {
root: true,
parser: '@typescript-eslint/parser',
plugins: unique('@typescript-eslint', 'import', 'rules', plugins),
@ -163,13 +166,7 @@ export function extendConfig({
},
overrides: [
{
files: [
'.eslintrc.js',
'*.config.js',
'index.js',
'babel.config.js',
'next.config.js',
],
files: ['.eslintrc.js', '.eslintrc.cjs', '*.config.js', 'index.js'],
extends: ['plugin:@typescript-eslint/disable-type-checked'],
rules: {
'rules/restrict-template-expressions': off,
@ -181,6 +178,12 @@ export function extendConfig({
'@typescript-eslint/consistent-type-imports': off,
},
},
{
files: ['repl.ts', 'scripts/**/*.ts'],
rules: {
'no-console': off,
},
},
...(overrides ?? []),
],
rules: {
@ -192,7 +195,7 @@ export function extendConfig({
...reactRules,
'react/no-unknown-property': [
error,
{ ignore: hasNext ? ['emotion', 'next'] : ['emotion'] },
{ ignore: hasNext ? ['css', 'next'] : ['css'] },
],
}),
...(hasReactRefresh && {
@ -201,6 +204,7 @@ export function extendConfig({
...(hasUnicorn && unicornRules),
...(hasJsDoc && jsDocRules),
...(hasGraphQL && graphqlRules),
...(hasTailwind && tailwindRules),
...rules,
},
...rest,

View File

@ -1,18 +1,13 @@
import type { ESLint } from 'eslint';
import * as fs from 'node:fs';
import { resolve, basename, extname } from 'node:path';
function tryRequire(candidates: string[]) {
for (const candidate of candidates) {
try {
require(candidate);
return;
} catch {}
}
}
import { glob } from 'fast-glob';
import { parseModule } from 'esprima';
import query from 'esquery';
import type { Node, Property } from 'estree';
// https://github.com/gulpjs/interpret
tryRequire([
const transpilers = [
'esbin/register',
'esbuild-register',
'ts-node/register/transpile-only',
@ -20,19 +15,63 @@ tryRequire([
'sucrase/register',
'@babel/register',
'coffeescript/register',
]);
];
const folders = resolve(process.cwd(), 'eslint-local-rules');
const files = fs.readdirSync(folders);
function tryRequire() {
for (const candidate of transpilers) {
try {
require(candidate);
return;
} catch {}
}
}
const unwrapDefault = <T = any>(module: any): T => module.default ?? module;
const plugin: ESLint.Plugin = {
rules: {},
};
for (const file of files) {
const name = basename(file, extname(file));
const module = require(resolve(folders, file));
plugin.rules![name] = module.default ?? module;
function hydrateESTreeNode(n: Node): any {
switch (n.type) {
case 'Literal':
return n.value;
case 'ArrayExpression':
return n.elements.filter(Boolean).map(hydrateESTreeNode);
default:
throw new Error(`Unsupported node type: ${n.type}`);
}
}
function parseConfigFile(js: string) {
const [node] = query(
parseModule(js),
'CallExpression[callee.name="extendConfig"] > ObjectExpression > Property[key.name="customRuleFiles"]',
);
return hydrateESTreeNode((node as Property).value);
}
function main() {
const rootDir = process.cwd();
const eslintConfigFile = ['.eslintrc.js', '.eslintrc.cjs']
.map(file => resolve(rootDir, file))
.find(file => fs.existsSync(file));
if (!eslintConfigFile) return;
const eslintConfig = fs.readFileSync(eslintConfigFile, 'utf8');
const customRuleFiles = parseConfigFile(eslintConfig);
if (!customRuleFiles?.length) return;
tryRequire();
for (const file of glob.sync(customRuleFiles)) {
const module = unwrapDefault(require(file));
const name = module.name ?? basename(file, extname(file));
plugin.rules![name] = module;
}
}
main();
export = plugin;

View File

@ -19,3 +19,18 @@ declare module '@typescript-eslint/scope-manager' {
declare module '@typescript-eslint/types' {
export * from '@typescript-eslint/types/dist/index';
}
declare module 'module' {
export function _resolveFilename(
request: string,
parent: {
/**
* Can be null if the parent id is 'internal/preload' (e.g. via --require)
* which doesn't have a file path.
*/
filename: string | null;
},
isMain: boolean,
options?: Record<PropertyKey, unknown>,
): string;
}

View File

@ -57,3 +57,8 @@ export function* map<T, U>(
yield callback(value);
}
}
// safe-regex-test/index.js
export function safeRegexTest(regex: RegExp) {
return (text: string) => regex.test(text);
}

View File

@ -1,9 +1,10 @@
import { error, warn, off } from '../constants';
import { EslintRules } from 'eslint-define-config/src/rules/eslint';
import { ESLintRules } from 'eslint-define-config/rules/eslint';
export const eslintRules: Partial<EslintRules> = {
export const eslintRules: Partial<ESLintRules> = {
'arrow-body-style': [error, 'as-needed'],
'class-methods-use-this': off,
'func-style': [error, 'declaration', { allowArrowFunctions: true }],
'no-async-promise-executor': off,
'no-case-declarations': off,
'no-console': warn,

View File

@ -1,4 +1,4 @@
import { GraphQLRules } from 'eslint-define-config/src/rules/graphql-eslint';
import { GraphQLRules } from 'eslint-define-config/rules/graphql-eslint';
// https://the-guild.dev/graphql/eslint/rules
export const graphqlRules: Partial<GraphQLRules> = {};

View File

@ -1,5 +1,5 @@
import { error, off } from '../constants';
import { ImportRules } from 'eslint-define-config/src/rules/import';
import { ImportRules } from 'eslint-define-config/rules/import';
export const importRules: Partial<ImportRules> = {
'import/export': off,

View File

@ -1,3 +1,3 @@
import { JSDocRules } from 'eslint-define-config/src/rules/jsdoc';
import { JSDocRules } from 'eslint-define-config/rules/jsdoc';
export const jsDocRules: Partial<JSDocRules> = {};

View File

@ -1,5 +1,5 @@
import { error, off } from '../constants';
import { ReactRules } from 'eslint-define-config/src/rules/react';
import { ReactRules } from 'eslint-define-config/rules/react';
export const reactRules: Partial<ReactRules> = {
'react/display-name': off,

5
src/presets/tailwind.ts Normal file
View File

@ -0,0 +1,5 @@
import { off } from '../constants';
export const tailwindRules = {
'tailwindcss/no-custom-classname': off,
} as const;

View File

@ -1,5 +1,5 @@
import { error, off, warn } from '../constants';
import type { TypeScriptRules } from 'eslint-define-config/src/rules/typescript-eslint';
import type { TypeScriptRules } from 'eslint-define-config/rules/typescript-eslint';
export const typescriptRules: Partial<TypeScriptRules> = {
'@typescript-eslint/ban-ts-comment': [

View File

@ -1,5 +1,5 @@
import { error, warn } from '../constants';
import { UnicornRules } from 'eslint-define-config/src/rules/unicorn';
import { UnicornRules } from 'eslint-define-config/rules/unicorn';
const suggest = (suggest: string) => ({ suggest, fix: false });

37
src/prettier.ts Normal file
View File

@ -0,0 +1,37 @@
import type { Config } from 'prettier';
const prettier: Config = {
arrowParens: 'avoid',
tabWidth: 2,
printWidth: 90,
semi: true,
singleQuote: true,
trailingComma: 'all',
plugins: [],
};
export default function defineConfig({
tailwind,
...config
}: Partial<Config> & {
tailwind?: boolean;
}) {
const result: Config = {
...prettier,
...config,
};
if (tailwind) {
ensureHas(result.plugins!, 'prettier-plugin-tailwindcss');
result.tailwindAttributes ??= ['css'];
result.tailwindFunctions ??= ['tw'];
}
return result;
}
function ensureHas<T>(list: T[], item: T) {
if (!list.includes(item)) {
list.push(item);
}
}

View File

@ -1,7 +1,7 @@
import Module from 'module';
const { name } = [require][0]('./package.json');
const _resolveFilename = (Module as any)._resolveFilename;
const _resolveFilename = Module._resolveFilename;
const alias = new Set([
'eslint-import-resolver-typescript',
'eslint-plugin-import',
@ -13,9 +13,14 @@ const alias = new Set([
'eslint-plugin-rules',
]);
(Module as any)._resolveFilename = function (module: string, ...args: any[]) {
if (alias.has(module)) {
module = `${name}/${module}`;
type CDR<T> = T extends [any, ...infer R] ? R : [];
Module._resolveFilename = function (
request: string,
...args: CDR<Parameters<typeof _resolveFilename>>
) {
if (alias.has(request)) {
request = `${name}/${request}`;
}
return _resolveFilename(module, ...args);
return _resolveFilename(request, ...args);
};

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,
};

View File

@ -1,16 +1,15 @@
import type { Rule } from 'eslint';
import { defineRule } from '../types';
const rule: Rule.RuleModule = {
meta: {
type: 'problem',
docs: {
description:
'Ban assignment of empty object literals `{}` and replace them with `Object.create(null)`',
category: 'Best Practices',
recommended: true,
},
fixable: 'code',
export default defineRule({
type: 'problem',
docs: {
description:
'Ban assignment of empty object literals `{}` and replace them with `Object.create(null)`',
category: 'Best Practices',
recommended: true,
},
fixable: 'code',
create: context => ({
AssignmentExpression(node) {
if (
@ -27,6 +26,4 @@ const rule: Rule.RuleModule = {
}
},
}),
};
export default rule;
});

26
src/types.ts Normal file
View File

@ -0,0 +1,26 @@
import type { Rule } from 'eslint';
import type { ESLintUtils } from '@typescript-eslint/utils';
export function defineRules(rules: {
[ruleName: string]: Rule.RuleModule | ESLintUtils.RuleModule<string, unknown[]>;
}) {
return rules;
}
export function defineRule({
name,
create,
...meta
}: Rule.RuleMetaData & {
name?: string;
create: (context: Rule.RuleContext) => Rule.RuleListener;
}): Rule.RuleModule {
const module: Rule.RuleModule = {
meta,
create,
};
if (name != null) {
Object.defineProperty(module, 'name', { value: name });
}
return module;
}

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