Compare commits
35 Commits
d5f4de03d6
...
flat
Author | SHA1 | Date | |
---|---|---|---|
eb366f3b2e | |||
00d0dfa107 | |||
0138cabb27 | |||
b0cc4a1525 | |||
191848fdca | |||
afb93c4971 | |||
25695599aa | |||
f3fbf99c0c | |||
92e6e5081b | |||
e5546e21ad | |||
ba20685f2e | |||
3ac5f91988 | |||
2853da4344 | |||
1e6d679af3 | |||
674eaf1811 | |||
c175f87441 | |||
003a43512c | |||
fb50ede688 | |||
51455e3c21 | |||
1d955d951d | |||
8ba9b0725a | |||
4d2762de39 | |||
19b07691fc | |||
4c67de9f72 | |||
9ea078f414 | |||
ba2671d760 | |||
d8f224c5cf | |||
690b46fd2b | |||
4563047e01 | |||
3ec7c342ba | |||
02fe9f4799 | |||
4d94af3f70 | |||
a022050ab4 | |||
60b1b8fde3 | |||
ee3e7e4203 |
51
.eslint.ts
Normal file
51
.eslint.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import type { FlatESLintConfig } from '@aet/eslint-define-config';
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import * as tsParser from '@typescript-eslint/parser';
|
||||||
|
import importPlugin from 'eslint-plugin-import-x';
|
||||||
|
import unicorn from 'eslint-plugin-unicorn';
|
||||||
|
import tsEslint from 'typescript-eslint';
|
||||||
|
|
||||||
|
import { importRules } from './src/presets/typescript';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
js.configs.recommended, //
|
||||||
|
...tsEslint.configs.recommendedTypeChecked,
|
||||||
|
unicorn.configs['flat/recommended'],
|
||||||
|
importPlugin.flatConfigs.recommended,
|
||||||
|
importPlugin.flatConfigs.react,
|
||||||
|
importPlugin.flatConfigs.typescript,
|
||||||
|
{
|
||||||
|
files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
parser: tsParser,
|
||||||
|
projectService: true,
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
// https://github.com/unjs/jiti/issues/167 import.meta.dirname
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
sourceType: 'module',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
'import-x/parsers': {
|
||||||
|
'@typescript-eslint/parser': ['.ts', '.tsx', '.mts', '.cts'],
|
||||||
|
},
|
||||||
|
'import-x/resolver': {
|
||||||
|
typescript: true,
|
||||||
|
node: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ignores: ['eslint.config.cjs'],
|
||||||
|
rules: {
|
||||||
|
...importRules,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
'unicorn/prevent-abbreviations': 'off',
|
||||||
|
'unicorn/import-style': 'off',
|
||||||
|
'unicorn/switch-case-braces': ['error', 'avoid'],
|
||||||
|
'unicorn/no-null': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as FlatESLintConfig[];
|
62
.eslintrc
62
.eslintrc
@ -1,62 +0,0 @@
|
|||||||
{
|
|
||||||
"root": true,
|
|
||||||
"env": {
|
|
||||||
"node": true,
|
|
||||||
"browser": true,
|
|
||||||
"es6": true
|
|
||||||
},
|
|
||||||
"extends": ["eslint:recommended", "prettier"],
|
|
||||||
"parserOptions": {
|
|
||||||
"sourceType": "module",
|
|
||||||
"ecmaVersion": "latest"
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"no-restricted-imports": [
|
|
||||||
"warn",
|
|
||||||
{
|
|
||||||
"paths": [
|
|
||||||
"array-includes",
|
|
||||||
"array.prototype.flat",
|
|
||||||
"array.prototype.flatmap",
|
|
||||||
"array.prototype.tosorted",
|
|
||||||
"object.entries",
|
|
||||||
"object.fromentries",
|
|
||||||
"object.hasown",
|
|
||||||
"object.values",
|
|
||||||
"string.prototype.matchall",
|
|
||||||
"has"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"arrow-body-style": ["error", "as-needed"],
|
|
||||||
"class-methods-use-this": [
|
|
||||||
"warn",
|
|
||||||
{ "exceptMethods": ["toString", "shouldComponentUpdate"] }
|
|
||||||
],
|
|
||||||
"complexity": ["warn", { "max": 100 }],
|
|
||||||
"curly": ["error", "multi-line", "consistent"],
|
|
||||||
"eqeqeq": ["error", "smart"],
|
|
||||||
"no-async-promise-executor": "off",
|
|
||||||
"no-case-declarations": "off",
|
|
||||||
"no-constant-condition": ["error", { "checkLoops": false }],
|
|
||||||
"no-debugger": "off",
|
|
||||||
"no-empty": ["error", { "allowEmptyCatch": true }],
|
|
||||||
"no-inner-declarations": "off",
|
|
||||||
"no-lonely-if": "error",
|
|
||||||
"no-template-curly-in-string": "error",
|
|
||||||
"no-var": "error",
|
|
||||||
"object-shorthand": ["error", "always", { "ignoreConstructors": true }],
|
|
||||||
"one-var": ["error", { "var": "never", "let": "never" }],
|
|
||||||
"prefer-const": ["error", { "destructuring": "all" }],
|
|
||||||
"prefer-destructuring": [
|
|
||||||
"warn",
|
|
||||||
{ "AssignmentExpression": { "array": false, "object": false } }
|
|
||||||
],
|
|
||||||
"prefer-rest-params": "warn",
|
|
||||||
"prefer-spread": "warn",
|
|
||||||
"quote-props": ["error", "as-needed"],
|
|
||||||
"spaced-comment": ["error", "always", { "markers": ["/"] }],
|
|
||||||
"sort-imports": ["warn", { "ignoreDeclarationSort": true }],
|
|
||||||
"yoda": ["error", "never", { "exceptRange": true }]
|
|
||||||
}
|
|
||||||
}
|
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,6 +1,11 @@
|
|||||||
|
drafts
|
||||||
!/packages/eslint-plugin-react-hooks
|
!/packages/eslint-plugin-react-hooks
|
||||||
|
/packages/eslint-define-config
|
||||||
/react
|
/react
|
||||||
|
/test
|
||||||
|
src/types/rules
|
||||||
|
|
||||||
|
dist2
|
||||||
dist/**/*.js
|
dist/**/*.js
|
||||||
dist/**/*.js.map
|
dist/**/*.js.map
|
||||||
|
|
||||||
|
3
.npmrc
3
.npmrc
@ -1,2 +1,3 @@
|
|||||||
registry http://raspberrypi.local:4873
|
registry http://raspberrypi.local:4873
|
||||||
always-auth=true
|
always-auth=true
|
||||||
|
ignore-scripts=true
|
||||||
|
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"eslint.runtime": "node",
|
"eslint.runtime": "node",
|
||||||
|
"eslint.useFlatConfig": true,
|
||||||
"search.exclude": {
|
"search.exclude": {
|
||||||
"**/node_modules": true,
|
"**/node_modules": true,
|
||||||
"**/bower_components": true,
|
"**/bower_components": true,
|
||||||
|
12
README.md
12
README.md
@ -1,7 +1,15 @@
|
|||||||
# eslint-rules
|
# eslint-rules
|
||||||
|
|
||||||
|
Personal ESLint config. Guaranteed to have no useless polyfills.
|
||||||
|
|
||||||
## flat config support
|
## flat config support
|
||||||
|
|
||||||
- ❌ [import](https://github.com/import-js/eslint-plugin-import/issues/2556)
|
|
||||||
- ✅ [react](https://github.com/jsx-eslint/eslint-plugin-react/pull/3429)
|
- ✅ [react](https://github.com/jsx-eslint/eslint-plugin-react/pull/3429)
|
||||||
- 🇵🇷 [a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/pull/891)
|
- ✅ [unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn/pull/1886)
|
||||||
|
- ❌ [import](https://github.com/un-ts/eslint-plugin-import-x/issues/29)
|
||||||
|
- ❌ [jsx-a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/issues/978, supports flat config)
|
||||||
|
|
||||||
|
| Name | Flat Config | Issue |
|
||||||
|
| ------- | ----------- | ---------------------------------------------------------------------------------------------------------- |
|
||||||
|
| react | ✅ | [jsx-eslint/eslint-plugin-react#3429](https://github.com/jsx-eslint/eslint-plugin-react/pull/3429) |
|
||||||
|
| unicorn | ✅ | [sindresorhus/eslint-plugin-unicorn#1886](https://github.com/sindresorhus/eslint-plugin-unicorn/pull/1886) |
|
||||||
|
6
build.sh
6
build.sh
@ -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
|
|
||||||
./esbuild.ts
|
|
||||||
sed -i '' '/import.*redirect.*;/d' "dist/index.d.ts"
|
|
||||||
|
|
1
dist/.npmrc
vendored
Normal file
1
dist/.npmrc
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
shamefully-hoist=true
|
44
dist/config/index.d.ts
vendored
Normal file
44
dist/config/index.d.ts
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import type { FlatESLintConfig } from '@aet/eslint-define-config';
|
||||||
|
import type { Linter } from 'eslint';
|
||||||
|
|
||||||
|
type MiddlewareResult = Linter.Config | Linter.Config[];
|
||||||
|
|
||||||
|
export type Middleware =
|
||||||
|
| (() => Promise<MiddlewareResult>)
|
||||||
|
| (() => Promise<{ default: MiddlewareResult }>);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a ESLint config object.
|
||||||
|
*
|
||||||
|
* By default, it includes `["@typescript-eslint", "import-x", "prettier", "unicorn"]` configs.
|
||||||
|
* Additional bundled plugins include:
|
||||||
|
*
|
||||||
|
* 1. [`react`](https://github.com/jsx-eslint/eslint-plugin-react#list-of-supported-rules)
|
||||||
|
* (automatically enables
|
||||||
|
* [`react-hooks`](https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks))
|
||||||
|
* 2. [`react-refresh`](https://github.com/ArnaudBarre/eslint-plugin-react-refresh)
|
||||||
|
* 3. [`jsx-a11y`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y#supported-rules)
|
||||||
|
* 4. [`unicorn`](https://github.com/sindresorhus/eslint-plugin-unicorn#rules)
|
||||||
|
* 5. [`n`](https://github.com/eslint-community/eslint-plugin-n#-rules) (Node.js specific,
|
||||||
|
* requires `minimatch`)
|
||||||
|
* 6. [`jsdoc`](https://github.com/gajus/eslint-plugin-jsdoc#rules)
|
||||||
|
*
|
||||||
|
* Non bundled:
|
||||||
|
* 1. [`graphql`](https://the-guild.dev/graphql/eslint/rules)
|
||||||
|
*
|
||||||
|
* @param of Configuration options.
|
||||||
|
* @returns ESLint configuration object.
|
||||||
|
*/
|
||||||
|
export function extendConfig({
|
||||||
|
auto,
|
||||||
|
middlewares: addMiddlewares,
|
||||||
|
configs,
|
||||||
|
}: {
|
||||||
|
auto?: boolean;
|
||||||
|
middlewares?: Middleware[];
|
||||||
|
configs: FlatESLintConfig[];
|
||||||
|
}): Promise<FlatESLintConfig[]>;
|
||||||
|
|
||||||
|
export const error = 'error';
|
||||||
|
export const warn = 'warn';
|
||||||
|
export const off = 'off';
|
14
dist/eslint-plugin-import/index.d.ts
vendored
14
dist/eslint-plugin-import/index.d.ts
vendored
@ -1,14 +0,0 @@
|
|||||||
import type { Linter } from 'eslint';
|
|
||||||
|
|
||||||
export const rules: Readonly<Linter.RulesRecord>;
|
|
||||||
|
|
||||||
export const configs: {
|
|
||||||
recommended: Linter.BaseConfig;
|
|
||||||
errors: Linter.BaseConfig;
|
|
||||||
warnings: Linter.BaseConfig;
|
|
||||||
'stage-0': Linter.BaseConfig;
|
|
||||||
react: Linter.BaseConfig;
|
|
||||||
'react-native': Linter.BaseConfig;
|
|
||||||
electron: Linter.BaseConfig;
|
|
||||||
typescript: Linter.BaseConfig;
|
|
||||||
};
|
|
8
dist/eslint-plugin-jsx-a11y/index.d.ts
vendored
8
dist/eslint-plugin-jsx-a11y/index.d.ts
vendored
@ -1,8 +0,0 @@
|
|||||||
import type { Linter } from 'eslint';
|
|
||||||
|
|
||||||
export const rules: Readonly<Linter.RulesRecord>;
|
|
||||||
|
|
||||||
export const configs: {
|
|
||||||
recommended: Linter.BaseConfig;
|
|
||||||
strict: Linter.BaseConfig;
|
|
||||||
};
|
|
12
dist/eslint-plugin-react-hooks/index.d.ts
vendored
12
dist/eslint-plugin-react-hooks/index.d.ts
vendored
@ -1,12 +0,0 @@
|
|||||||
import type { Linter, Rule } from 'eslint';
|
|
||||||
|
|
||||||
export const __EXPERIMENTAL__: false;
|
|
||||||
|
|
||||||
export const configs: {
|
|
||||||
recommended: Linter.BaseConfig;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const rules: {
|
|
||||||
'rules-of-hooks': Rule.RuleModule;
|
|
||||||
'exhaustive-deps': Rule.RuleModule;
|
|
||||||
};
|
|
9
dist/eslint-plugin-react/index.d.ts
vendored
9
dist/eslint-plugin-react/index.d.ts
vendored
@ -1,9 +0,0 @@
|
|||||||
import type { Linter } from 'eslint';
|
|
||||||
|
|
||||||
export const deprecatedRules: Readonly<Linter.RulesRecord>;
|
|
||||||
|
|
||||||
export const configs: {
|
|
||||||
recommended: Linter.BaseConfig;
|
|
||||||
all: Linter.BaseConfig;
|
|
||||||
'jsx-runtime': Linter.BaseConfig;
|
|
||||||
};
|
|
10
dist/index.d.ts
vendored
10
dist/index.d.ts
vendored
@ -1,10 +0,0 @@
|
|||||||
// Generated by dts-bundle-generator v8.1.2
|
|
||||||
|
|
||||||
import { ESLintConfig } from 'eslint-define-config';
|
|
||||||
|
|
||||||
export declare const error = "error";
|
|
||||||
export declare const warn = "warn";
|
|
||||||
export declare const off = "off";
|
|
||||||
export declare function extendConfig({ plugins, settings, rules, extends: _extends, overrides, ...rest }?: ESLintConfig): ESLintConfig;
|
|
||||||
|
|
||||||
export {};
|
|
4
dist/overrides/is-core-module/package.json
vendored
Normal file
4
dist/overrides/is-core-module/package.json
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "is-core-module",
|
||||||
|
"version": "2.13.1"
|
||||||
|
}
|
4
dist/overrides/supports-preserve-symlinks-flag/package.json
vendored
Normal file
4
dist/overrides/supports-preserve-symlinks-flag/package.json
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "supports-preserve-symlinks-flag",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
84
dist/package.json
vendored
84
dist/package.json
vendored
@ -1,39 +1,75 @@
|
|||||||
{
|
{
|
||||||
"name": "@aet/eslint-rules",
|
"name": "@aet/eslint-rules",
|
||||||
"version": "0.0.2",
|
"version": "2.0.1-beta.8",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
|
"type": "module",
|
||||||
|
"bin": {
|
||||||
|
"eslint-install": "install.js",
|
||||||
|
"eslint-print": "print-config.sh"
|
||||||
|
},
|
||||||
|
"main": "./config/index.js",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"eslint": "^8.53.0",
|
"eslint": "^9.12.0",
|
||||||
"typescript": "^5.2.2"
|
"typescript": "^5.4.4"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@tanstack/eslint-plugin-query": "^5.52.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/eslint": "^8.44.7",
|
"@antfu/install-pkg": "^0.4.1",
|
||||||
"@typescript-eslint/eslint-plugin": "6.10.0",
|
"@nolyfill/is-core-module": "^1.0.39",
|
||||||
"@typescript-eslint/parser": "6.10.0",
|
"@aet/eslint-define-config": "^0.1.0-beta.28",
|
||||||
"aria-query": "^5.3.0",
|
"@eslint/js": "^9.12.0",
|
||||||
"axe-core": "4.8.2",
|
"@eslint-community/eslint-utils": "^4.4.0",
|
||||||
"axobject-query": "^4.0.0",
|
"@types/eslint": "^9.6.1",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.10.0",
|
||||||
|
"@typescript-eslint/parser": "^8.10.0",
|
||||||
|
"@eslint-react/eslint-plugin": "1.15.0",
|
||||||
|
"@stylistic/eslint-plugin": "^2.9.0",
|
||||||
|
"@typescript-eslint/type-utils": "^8.10.0",
|
||||||
|
"@typescript-eslint/utils": "^8.10.0",
|
||||||
|
"aria-query": "^5.3.2",
|
||||||
|
"axe-core": "^4.10.1",
|
||||||
|
"axobject-query": "4.1.0",
|
||||||
"damerau-levenshtein": "1.0.8",
|
"damerau-levenshtein": "1.0.8",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.7",
|
||||||
"doctrine": "^3.0.0",
|
"doctrine": "^3.0.0",
|
||||||
"emoji-regex": "^10.3.0",
|
"emoji-regex": "^10.4.0",
|
||||||
"enhanced-resolve": "^5.15.0",
|
"enhanced-resolve": "^5.17.1",
|
||||||
"eslint-config-prettier": "9.0.0",
|
"typescript-eslint": "^8.10.0",
|
||||||
"eslint-define-config": "^1.24.1",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-import-resolver-node": "^0.3.9",
|
"eslint-import-resolver-node": "^0.3.9",
|
||||||
"eslint-module-utils": "^2.8.0",
|
"eslint-import-resolver-typescript": "^3.6.3",
|
||||||
"eslint-plugin-unicorn": "^49.0.0",
|
"eslint-module-utils": "^2.12.0",
|
||||||
|
"eslint-plugin-es-x": "^8.0.0",
|
||||||
|
"eslint-plugin-import-x": "^4.3.1",
|
||||||
|
"eslint-plugin-unicorn": "^56.0.0",
|
||||||
|
"esprima": "^4.0.1",
|
||||||
|
"esquery": "^1.6.0",
|
||||||
"estraverse": "^5.3.0",
|
"estraverse": "^5.3.0",
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
"get-tsconfig": "^4.7.2",
|
"get-tsconfig": "^4.8.1",
|
||||||
"ignore": "^5.2.4",
|
"globals": "^15.11.0",
|
||||||
"is-core-module": "^2.13.1",
|
"ignore": "^6.0.2",
|
||||||
|
"is-bun-module": "^1.2.1",
|
||||||
"is-glob": "^4.0.3",
|
"is-glob": "^4.0.3",
|
||||||
"language-tags": "^1.0.9",
|
"language-tags": "^1.0.9",
|
||||||
"lodash": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"minimatch": "^9.0.3",
|
"minimatch": "^10.0.1",
|
||||||
"resolve": "^2.0.0-next.5",
|
"semver": "^7.6.3"
|
||||||
"semver": "^7.5.4",
|
},
|
||||||
"tsconfig-paths": "^4.2.0"
|
"pnpm": {
|
||||||
|
"overrides": {
|
||||||
|
"is-core-module": "file:./overrides/is-core-module",
|
||||||
|
"supports-preserve-symlinks-flag": "file:./overrides/supports-preserve-symlinks-flag"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"is-core-module": "file:./overrides/is-core-module",
|
||||||
|
"supports-preserve-symlinks-flag": "file:./overrides/supports-preserve-symlinks-flag"
|
||||||
|
},
|
||||||
|
"resolutions": {
|
||||||
|
"**/is-core-module": "file:./overrides/is-core-module",
|
||||||
|
"**/supports-preserve-symlinks-flag": "file:./overrides/supports-preserve-symlinks-flag"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2267
dist/pnpm-lock.yaml
generated
vendored
Normal file
2267
dist/pnpm-lock.yaml
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
13
dist/prettier.d.ts
vendored
Normal file
13
dist/prettier.d.ts
vendored
Normal 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 {};
|
2
dist/print-config.sh
vendored
Executable file
2
dist/print-config.sh
vendored
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
node -e "import('./eslint.config.mjs').then(config => console.dir(config, { depth: null }))"
|
16
dist/types.d.ts
vendored
Normal file
16
dist/types.d.ts
vendored
Normal 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[], unknown, ESLintUtils.RuleListener>;
|
||||||
|
};
|
||||||
|
export declare function defineRule({ name, create, ...meta }: Rule.RuleMetaData & {
|
||||||
|
name?: string;
|
||||||
|
create: (context: Rule.RuleContext) => Rule.RuleListener;
|
||||||
|
}): Rule.RuleModule;
|
||||||
|
|
||||||
|
export {};
|
3
eslint.config.cjs
Normal file
3
eslint.config.cjs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
require('@swc-node/register');
|
||||||
|
module.exports = require('./.eslint.ts').default;
|
108
package.json
108
package.json
@ -1,38 +1,74 @@
|
|||||||
{
|
{
|
||||||
"name": "@aet/eslint-configs",
|
"name": "@aet/eslint-configs",
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "./build.sh",
|
"build": "./scripts/build.ts",
|
||||||
"check-import": "./src/check-imports.ts"
|
"check-import": "./scripts/check-imports.ts",
|
||||||
|
"define": "/usr/local/bin/codium ./packages/eslint-define-config",
|
||||||
|
"do": "yarn build; (cd dist && ver bump && npm publish && ver unpub)"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.23.2",
|
"@aet/eslint-define-config": "^0.1.0-beta.29",
|
||||||
"@babel/plugin-transform-flow-strip-types": "^7.22.5",
|
"@antfu/install-pkg": "^0.4.1",
|
||||||
"@babel/preset-env": "^7.23.2",
|
"@babel/core": "^7.25.8",
|
||||||
"@types/babel-plugin-macros": "^3.1.2",
|
"@babel/plugin-transform-flow-strip-types": "^7.25.7",
|
||||||
"@types/babel__core": "^7.20.3",
|
"@babel/preset-env": "^7.25.8",
|
||||||
"@types/eslint": "^8.44.6",
|
"@eslint-react/eslint-plugin": "^1.15.0",
|
||||||
"@types/estree": "^1.0.4",
|
"@eslint/js": "^9.13.0",
|
||||||
"@types/estree-jsx": "^1.0.2",
|
"@graphql-eslint/eslint-plugin": "^3.20.1",
|
||||||
"@types/lodash": "^4.14.200",
|
"@stylistic/eslint-plugin": "^2.9.0",
|
||||||
"@types/node": "^20.8.10",
|
"@swc-node/register": "^1.10.9",
|
||||||
"@typescript-eslint/types": "^6.9.1",
|
"@tanstack/eslint-plugin-query": "^5.59.7",
|
||||||
|
"@types/babel-plugin-macros": "^3.1.3",
|
||||||
|
"@types/babel__core": "^7.20.5",
|
||||||
|
"@types/eslint": "^9.6.1",
|
||||||
|
"@types/eslint-plugin-tailwindcss": "^3.17.0",
|
||||||
|
"@types/eslint__js": "^8.42.3",
|
||||||
|
"@types/esprima": "^4.0.6",
|
||||||
|
"@types/esquery": "^1.5.4",
|
||||||
|
"@types/estree": "^1.0.6",
|
||||||
|
"@types/estree-jsx": "^1.0.5",
|
||||||
|
"@types/lodash-es": "^4.17.12",
|
||||||
|
"@types/node": "^22.7.7",
|
||||||
|
"@types/react-refresh": "^0.14.6",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^8.10.0",
|
||||||
|
"@typescript-eslint/parser": "^8.10.0",
|
||||||
|
"@typescript-eslint/type-utils": "^8.10.0",
|
||||||
|
"@typescript-eslint/types": "^8.10.0",
|
||||||
|
"@typescript-eslint/typescript-estree": "^8.10.0",
|
||||||
|
"@typescript-eslint/utils": "^8.10.0",
|
||||||
"babel-plugin-macros": "^3.1.0",
|
"babel-plugin-macros": "^3.1.0",
|
||||||
"dts-bundle-generator": "^8.1.2",
|
"dts-bundle-generator": "9.5.1",
|
||||||
"esbin": "0.0.3",
|
"esbuild": "0.24.0",
|
||||||
"esbuild": "0.19.5",
|
|
||||||
"esbuild-plugin-alias": "^0.2.1",
|
"esbuild-plugin-alias": "^0.2.1",
|
||||||
"eslint": "8.52.0",
|
"eslint": "9.13.0",
|
||||||
"eslint-config-prettier": "9.0.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-define-config": "^1.24.1",
|
"eslint-import-resolver-typescript": "^3.6.3",
|
||||||
"fast-glob": "^3.3.1",
|
"eslint-plugin-import-x": "^4.3.1",
|
||||||
"json-schema-to-ts": "^2.9.2",
|
"eslint-plugin-jsdoc": "^50.4.3",
|
||||||
"lodash": "^4.17.21",
|
"eslint-plugin-react-refresh": "^0.4.12",
|
||||||
"minimatch": "^9.0.3",
|
"eslint-plugin-storybook": "canary",
|
||||||
"picocolors": "^1.0.0",
|
"eslint-plugin-testing-library": "^6.4.0",
|
||||||
"prettier": "^3.0.3",
|
"eslint-plugin-unicorn": "^56.0.0",
|
||||||
|
"eslint-plugin-vitest": "^0.5.4",
|
||||||
|
"esprima": "^4.0.1",
|
||||||
|
"esquery": "^1.6.0",
|
||||||
|
"fast-glob": "^3.3.2",
|
||||||
|
"find-cache-dir": "^5.0.0",
|
||||||
|
"globals": "^15.11.0",
|
||||||
|
"graphql": "^16.9.0",
|
||||||
|
"json-schema-to-ts": "^3.1.1",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"nolyfill": "^1.0.41",
|
||||||
|
"patch-package": "^8.0.0",
|
||||||
|
"picocolors": "^1.1.1",
|
||||||
|
"prettier": "^3.3.3",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"typescript": "5.2.2"
|
"terser": "^5.36.0",
|
||||||
|
"type-fest": "^4.26.1",
|
||||||
|
"typescript": "^5.6.3",
|
||||||
|
"typescript-eslint": "^8.10.0"
|
||||||
},
|
},
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"arrowParens": "avoid",
|
"arrowParens": "avoid",
|
||||||
@ -41,5 +77,25 @@
|
|||||||
"semi": true,
|
"semi": true,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "all"
|
"trailingComma": "all"
|
||||||
|
},
|
||||||
|
"pnpm": {
|
||||||
|
"overrides": {
|
||||||
|
"@typescript-eslint/utils": "8.0.0",
|
||||||
|
"function-bind": "npm:@nolyfill/function-bind@^1",
|
||||||
|
"has-proto": "npm:@nolyfill/has-proto@^1",
|
||||||
|
"has-symbols": "npm:@nolyfill/has-symbols@^1",
|
||||||
|
"hasown": "npm:@nolyfill/hasown@^1",
|
||||||
|
"isarray": "npm:@nolyfill/isarray@^1",
|
||||||
|
"jsonify": "npm:@nolyfill/jsonify@^1",
|
||||||
|
"object-keys": "npm:@nolyfill/object-keys@^1",
|
||||||
|
"set-function-length": "npm:@nolyfill/set-function-length@^1",
|
||||||
|
"@babel/types": "7.25.2",
|
||||||
|
"is-core-module": "npm:@nolyfill/is-core-module@^1",
|
||||||
|
"json-stable-stringify": "npm:@nolyfill/json-stable-stringify@^1"
|
||||||
|
},
|
||||||
|
"patchedDependencies": {
|
||||||
|
"@typescript-eslint/typescript-estree@8.0.0": "patches/@typescript-eslint__typescript-estree@8.0.0.patch",
|
||||||
|
"dts-bundle-generator": "patches/dts-bundle-generator.patch"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Submodule packages/eslint-import-resolver-typescript updated: 7b6bfc3947...3dfad602a0
Submodule packages/eslint-plugin-import deleted from 6b95a02193
Submodule packages/eslint-plugin-jsx-a11y updated: fffb05b38c...cca288b73a
Submodule packages/eslint-plugin-n updated: 47cd9a6a0e...6744257b43
Submodule packages/eslint-plugin-react deleted from ecadb92609
@ -22,8 +22,11 @@ import type {
|
|||||||
ChainExpression,
|
ChainExpression,
|
||||||
Pattern,
|
Pattern,
|
||||||
OptionalMemberExpression,
|
OptionalMemberExpression,
|
||||||
|
ArrayExpression,
|
||||||
|
VariableDeclaration,
|
||||||
} from 'estree';
|
} from 'estree';
|
||||||
import type { FromSchema } from 'json-schema-to-ts';
|
import type { FromSchema } from 'json-schema-to-ts';
|
||||||
|
|
||||||
import { __EXPERIMENTAL__ } from './index';
|
import { __EXPERIMENTAL__ } from './index';
|
||||||
|
|
||||||
const schema = {
|
const schema = {
|
||||||
@ -81,7 +84,23 @@ const rule: Rule.RuleModule = {
|
|||||||
context.report(problem);
|
context.report(problem);
|
||||||
}
|
}
|
||||||
|
|
||||||
const scopeManager = context.sourceCode.scopeManager;
|
/**
|
||||||
|
* SourceCode#getText that also works down to ESLint 3.0.0
|
||||||
|
*/
|
||||||
|
const getSource =
|
||||||
|
typeof context.getSource === 'function'
|
||||||
|
? (node: Node) => context.getSource(node)
|
||||||
|
: (node: Node) => context.sourceCode.getText(node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SourceCode#getScope that also works down to ESLint 3.0.0
|
||||||
|
*/
|
||||||
|
const getScope =
|
||||||
|
typeof context.getScope === 'function'
|
||||||
|
? () => context.getScope()
|
||||||
|
: (node: Node) => context.sourceCode.getScope(node);
|
||||||
|
|
||||||
|
const scopeManager = context.getSourceCode().scopeManager;
|
||||||
|
|
||||||
// Should be shared between visitors.
|
// Should be shared between visitors.
|
||||||
const setStateCallSites = new WeakMap<Expression, Pattern>();
|
const setStateCallSites = new WeakMap<Expression, Pattern>();
|
||||||
@ -128,7 +147,7 @@ const rule: Rule.RuleModule = {
|
|||||||
' }\n' +
|
' }\n' +
|
||||||
' fetchData();\n' +
|
' fetchData();\n' +
|
||||||
`}, [someId]); // Or [] if effect doesn't need props or state\n\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',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,6 +192,8 @@ const rule: Rule.RuleModule = {
|
|||||||
// ^^^ true for this reference
|
// ^^^ true for this reference
|
||||||
// const [state, dispatch] = useReducer() / React.useReducer()
|
// const [state, dispatch] = useReducer() / React.useReducer()
|
||||||
// ^^^ true for this reference
|
// ^^^ true for this reference
|
||||||
|
// const [state, dispatch] = useActionState() / React.useActionState()
|
||||||
|
// ^^^ true for this reference
|
||||||
// const ref = useRef()
|
// const ref = useRef()
|
||||||
// ^^^ true for this reference
|
// ^^^ true for this reference
|
||||||
// const onStuff = useEffectEvent(() => {})
|
// const onStuff = useEffectEvent(() => {})
|
||||||
@ -187,31 +208,32 @@ const rule: Rule.RuleModule = {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Look for `let stuff = ...`
|
// Look for `let stuff = ...`
|
||||||
if (def.node.type !== 'VariableDeclarator') {
|
const node = def.node as Node;
|
||||||
|
if (node.type !== 'VariableDeclarator') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let init = (def.node as VariableDeclarator).init;
|
let init = node.init;
|
||||||
if (init == null) {
|
if (init == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
while (init.type === 'TSAsExpression') {
|
while (init.type === 'TSAsExpression' || init.type === 'AsExpression') {
|
||||||
init = init.expression;
|
init = init.expression;
|
||||||
}
|
}
|
||||||
// Detect primitive constants
|
// Detect primitive constants
|
||||||
// const foo = 42
|
// const foo = 42
|
||||||
let declaration = def.node.parent;
|
let declaration = node.parent;
|
||||||
if (declaration == null) {
|
if (declaration == null) {
|
||||||
// This might happen if variable is declared after the callback.
|
// This might happen if variable is declared after the callback.
|
||||||
// In that case ESLint won't set up .parent refs.
|
// In that case ESLint won't set up .parent refs.
|
||||||
// So we'll set them up manually.
|
// So we'll set them up manually.
|
||||||
fastFindReferenceWithParent(componentScope.block, def.node.id);
|
fastFindReferenceWithParent(componentScope.block, node.id);
|
||||||
declaration = def.node.parent;
|
declaration = node.parent;
|
||||||
if (declaration == null) {
|
if (declaration == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
declaration.kind === 'const' &&
|
(declaration as VariableDeclaration).kind === 'const' &&
|
||||||
init.type === 'Literal' &&
|
init.type === 'Literal' &&
|
||||||
(typeof init.value === 'string' ||
|
(typeof init.value === 'string' ||
|
||||||
typeof init.value === 'number' ||
|
typeof init.value === 'number' ||
|
||||||
@ -252,7 +274,11 @@ const rule: Rule.RuleModule = {
|
|||||||
}
|
}
|
||||||
// useEffectEvent() return value is always unstable.
|
// useEffectEvent() return value is always unstable.
|
||||||
return true;
|
return true;
|
||||||
} else if (name === 'useState' || name === 'useReducer') {
|
} else if (
|
||||||
|
name === 'useState' ||
|
||||||
|
name === 'useReducer' ||
|
||||||
|
name === 'useActionState'
|
||||||
|
) {
|
||||||
// Only consider second value in initializing tuple stable.
|
// Only consider second value in initializing tuple stable.
|
||||||
if (
|
if (
|
||||||
id.type === 'ArrayPattern' &&
|
id.type === 'ArrayPattern' &&
|
||||||
@ -264,14 +290,14 @@ const rule: Rule.RuleModule = {
|
|||||||
if (name === 'useState') {
|
if (name === 'useState') {
|
||||||
const references = resolved.references;
|
const references = resolved.references;
|
||||||
let writeCount = 0;
|
let writeCount = 0;
|
||||||
for (let i = 0; i < references.length; i++) {
|
for (const reference of references) {
|
||||||
if (references[i].isWrite()) {
|
if (reference.isWrite()) {
|
||||||
writeCount++;
|
writeCount++;
|
||||||
}
|
}
|
||||||
if (writeCount > 1) {
|
if (writeCount > 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
setStateCallSites.set(references[i].identifier, id.elements[0]!);
|
setStateCallSites.set(reference.identifier, id.elements[0]!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Setter is stable.
|
// Setter is stable.
|
||||||
@ -279,27 +305,25 @@ const rule: Rule.RuleModule = {
|
|||||||
} else if (id.elements[0] === resolved.identifiers[0]) {
|
} else if (id.elements[0] === resolved.identifiers[0]) {
|
||||||
if (name === 'useState') {
|
if (name === 'useState') {
|
||||||
const references = resolved.references;
|
const references = resolved.references;
|
||||||
for (let i = 0; i < references.length; i++) {
|
for (const reference of references) {
|
||||||
stateVariables.add(references[i].identifier);
|
stateVariables.add(reference.identifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// State variable itself is dynamic.
|
// State variable itself is dynamic.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (name === 'useTransition') {
|
} else if (
|
||||||
// Only consider second value in initializing tuple stable.
|
// Only consider second value in initializing tuple stable.
|
||||||
if (
|
name === 'useTransition' &&
|
||||||
id.type === 'ArrayPattern' &&
|
id.type === 'ArrayPattern' &&
|
||||||
id.elements.length === 2 &&
|
id.elements.length === 2 &&
|
||||||
Array.isArray(resolved.identifiers)
|
Array.isArray(resolved.identifiers) &&
|
||||||
) {
|
// Is second tuple value the same reference we're checking?
|
||||||
// Is second tuple value the same reference we're checking?
|
id.elements[1] === resolved.identifiers[0]
|
||||||
if (id.elements[1] === resolved.identifiers[0]) {
|
) {
|
||||||
// Setter is stable.
|
// Setter is stable.
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// By default assume it's dynamic.
|
// By default assume it's dynamic.
|
||||||
return false;
|
return false;
|
||||||
@ -319,7 +343,7 @@ const rule: Rule.RuleModule = {
|
|||||||
}
|
}
|
||||||
// Search the direct component subscopes for
|
// Search the direct component subscopes for
|
||||||
// top-level function definitions matching this reference.
|
// top-level function definitions matching this reference.
|
||||||
const fnNode = def.node;
|
const fnNode = def.node as Node;
|
||||||
const childScopes = componentScope.childScopes;
|
const childScopes = componentScope.childScopes;
|
||||||
let fnScope = null;
|
let fnScope = null;
|
||||||
let i;
|
let i;
|
||||||
@ -424,9 +448,9 @@ const rule: Rule.RuleModule = {
|
|||||||
dependencyNode.type === 'Identifier' &&
|
dependencyNode.type === 'Identifier' &&
|
||||||
(dependencyNode.parent!.type === 'MemberExpression' ||
|
(dependencyNode.parent!.type === 'MemberExpression' ||
|
||||||
dependencyNode.parent!.type === 'OptionalMemberExpression') &&
|
dependencyNode.parent!.type === 'OptionalMemberExpression') &&
|
||||||
!dependencyNode.parent!.computed &&
|
!dependencyNode.parent.computed &&
|
||||||
dependencyNode.parent!.property.type === 'Identifier' &&
|
dependencyNode.parent.property.type === 'Identifier' &&
|
||||||
dependencyNode.parent!.property.name === 'current' &&
|
dependencyNode.parent.property.name === 'current' &&
|
||||||
// ...in a cleanup function or below...
|
// ...in a cleanup function or below...
|
||||||
isInsideEffectCleanup(reference)
|
isInsideEffectCleanup(reference)
|
||||||
) {
|
) {
|
||||||
@ -479,12 +503,11 @@ const rule: Rule.RuleModule = {
|
|||||||
|
|
||||||
// Warn about accessing .current in cleanup effects.
|
// Warn about accessing .current in cleanup effects.
|
||||||
currentRefsInEffectCleanup.forEach(({ reference, dependencyNode }, dependency) => {
|
currentRefsInEffectCleanup.forEach(({ reference, dependencyNode }, dependency) => {
|
||||||
const references: Scope.Reference[] = reference.resolved!.references;
|
const references: Scope.Reference[] = reference.resolved.references;
|
||||||
// Is React managing this ref or us?
|
// Is React managing this ref or us?
|
||||||
// Let's see if we can find a .current assignment.
|
// Let's see if we can find a .current assignment.
|
||||||
let foundCurrentAssignment = false;
|
let foundCurrentAssignment = false;
|
||||||
for (let i = 0; i < references.length; i++) {
|
for (const { identifier } of references) {
|
||||||
const { identifier } = references[i];
|
|
||||||
const { parent } = identifier;
|
const { parent } = identifier;
|
||||||
if (
|
if (
|
||||||
parent != null &&
|
parent != null &&
|
||||||
@ -496,7 +519,7 @@ const rule: Rule.RuleModule = {
|
|||||||
parent.property.name === 'current' &&
|
parent.property.name === 'current' &&
|
||||||
// ref.current = <something>
|
// ref.current = <something>
|
||||||
parent.parent!.type === 'AssignmentExpression' &&
|
parent.parent!.type === 'AssignmentExpression' &&
|
||||||
parent.parent!.left === parent
|
parent.parent.left === parent
|
||||||
) {
|
) {
|
||||||
foundCurrentAssignment = true;
|
foundCurrentAssignment = true;
|
||||||
break;
|
break;
|
||||||
@ -529,11 +552,11 @@ const rule: Rule.RuleModule = {
|
|||||||
node: writeExpr,
|
node: writeExpr,
|
||||||
message:
|
message:
|
||||||
`Assignments to the '${key}' variable from inside React Hook ` +
|
`Assignments to the '${key}' variable from inside React Hook ` +
|
||||||
`${context.getSource(reactiveHook)} will be lost after each ` +
|
`${getSource(reactiveHook)} will be lost after each ` +
|
||||||
`render. To preserve the value over time, store it in a useRef ` +
|
`render. To preserve the value over time, store it in a useRef ` +
|
||||||
`Hook and keep the mutable value in the '.current' property. ` +
|
`Hook and keep the mutable value in the '.current' property. ` +
|
||||||
`Otherwise, you can move this variable directly inside ` +
|
`Otherwise, you can move this variable directly inside ` +
|
||||||
`${context.getSource(reactiveHook)}.`,
|
`${getSource(reactiveHook)}.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -543,11 +566,11 @@ const rule: Rule.RuleModule = {
|
|||||||
if (isStable) {
|
if (isStable) {
|
||||||
stableDependencies.add(key);
|
stableDependencies.add(key);
|
||||||
}
|
}
|
||||||
references.forEach(reference => {
|
for (const reference of references) {
|
||||||
if (reference.writeExpr) {
|
if (reference.writeExpr) {
|
||||||
reportStaleAssignment(reference.writeExpr, key);
|
reportStaleAssignment(reference.writeExpr, key);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (staleAssignments.size > 0) {
|
if (staleAssignments.size > 0) {
|
||||||
@ -563,15 +586,15 @@ const rule: Rule.RuleModule = {
|
|||||||
if (setStateInsideEffectWithoutDeps) {
|
if (setStateInsideEffectWithoutDeps) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
references.forEach(reference => {
|
for (const reference of references) {
|
||||||
if (setStateInsideEffectWithoutDeps) {
|
if (setStateInsideEffectWithoutDeps) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = reference.identifier;
|
const id = reference.identifier;
|
||||||
const isSetState: boolean = setStateCallSites.has(id);
|
const isSetState: boolean = setStateCallSites.has(id);
|
||||||
if (!isSetState) {
|
if (!isSetState) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let fnScope: Scope.Scope = reference.from;
|
let fnScope: Scope.Scope = reference.from;
|
||||||
@ -583,9 +606,8 @@ const rule: Rule.RuleModule = {
|
|||||||
// TODO: we could potentially ignore early returns.
|
// TODO: we could potentially ignore early returns.
|
||||||
setStateInsideEffectWithoutDeps = key;
|
setStateInsideEffectWithoutDeps = key;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (setStateInsideEffectWithoutDeps) {
|
if (setStateInsideEffectWithoutDeps) {
|
||||||
const { suggestedDependencies } = collectRecommendations({
|
const { suggestedDependencies } = collectRecommendations({
|
||||||
dependencies,
|
dependencies,
|
||||||
@ -620,49 +642,56 @@ const rule: Rule.RuleModule = {
|
|||||||
|
|
||||||
const declaredDependencies: DeclaredDependency[] = [];
|
const declaredDependencies: DeclaredDependency[] = [];
|
||||||
const externalDependencies = new Set<string>();
|
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
|
// If the declared dependencies are not an array expression then we
|
||||||
// can't verify that the user provided the correct dependencies. Tell
|
// can't verify that the user provided the correct dependencies. Tell
|
||||||
// the user this in an error.
|
// the user this in an error.
|
||||||
reportProblem({
|
reportProblem({
|
||||||
node: declaredDependenciesNode,
|
node: declaredDependenciesNode,
|
||||||
message:
|
message:
|
||||||
`React Hook ${context.getSource(reactiveHook)} was passed a ` +
|
`React Hook ${getSource(reactiveHook)} was passed a ` +
|
||||||
'dependency list that is not an array literal. This means we ' +
|
'dependency list that is not an array literal. This means we ' +
|
||||||
"can't statically verify whether you've passed the correct " +
|
"can't statically verify whether you've passed the correct " +
|
||||||
'dependencies.',
|
'dependencies.',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
declaredDependenciesNode.elements.forEach(declaredDependencyNode => {
|
const arrayExpression = isTSAsArrayExpression
|
||||||
|
? declaredDependenciesNode.expression
|
||||||
|
: declaredDependenciesNode;
|
||||||
|
|
||||||
|
for (const declaredDependencyNode of (arrayExpression as ArrayExpression)
|
||||||
|
.elements) {
|
||||||
// Skip elided elements.
|
// Skip elided elements.
|
||||||
if (declaredDependencyNode === null) {
|
if (declaredDependencyNode === null) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
// If we see a spread element then add a special warning.
|
// If we see a spread element then add a special warning.
|
||||||
if (declaredDependencyNode.type === 'SpreadElement') {
|
if (declaredDependencyNode.type === 'SpreadElement') {
|
||||||
reportProblem({
|
reportProblem({
|
||||||
node: declaredDependencyNode,
|
node: declaredDependencyNode,
|
||||||
message:
|
message:
|
||||||
`React Hook ${context.getSource(reactiveHook)} has a spread ` +
|
`React Hook ${getSource(reactiveHook)} has a spread ` +
|
||||||
"element in its dependency array. This means we can't " +
|
"element in its dependency array. This means we can't " +
|
||||||
"statically verify whether you've passed the " +
|
"statically verify whether you've passed the " +
|
||||||
'correct dependencies.',
|
'correct dependencies.',
|
||||||
});
|
});
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
if (useEffectEventVariables.has(declaredDependencyNode)) {
|
if (useEffectEventVariables.has(declaredDependencyNode)) {
|
||||||
reportProblem({
|
reportProblem({
|
||||||
node: declaredDependencyNode,
|
node: declaredDependencyNode,
|
||||||
message:
|
message:
|
||||||
'Functions returned from `useEffectEvent` must not be included in the dependency array. ' +
|
'Functions returned from `useEffectEvent` must not be included in the dependency array. ' +
|
||||||
`Remove \`${context.getSource(declaredDependencyNode)}\` from the list.`,
|
`Remove \`${getSource(declaredDependencyNode)}\` from the list.`,
|
||||||
suggest: [
|
suggest: [
|
||||||
{
|
{
|
||||||
desc: `Remove the dependency \`${context.getSource(
|
desc: `Remove the dependency \`${getSource(declaredDependencyNode)}\``,
|
||||||
declaredDependencyNode,
|
|
||||||
)}\``,
|
|
||||||
fix(fixer) {
|
fix(fixer) {
|
||||||
return fixer.removeRange(declaredDependencyNode.range!);
|
return fixer.removeRange(declaredDependencyNode.range);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -696,13 +725,13 @@ const rule: Rule.RuleModule = {
|
|||||||
reportProblem({
|
reportProblem({
|
||||||
node: declaredDependencyNode,
|
node: declaredDependencyNode,
|
||||||
message:
|
message:
|
||||||
`React Hook ${context.getSource(reactiveHook)} has a ` +
|
`React Hook ${getSource(reactiveHook)} has a ` +
|
||||||
`complex expression in the dependency array. ` +
|
`complex expression in the dependency array. ` +
|
||||||
'Extract it to a separate variable so it can be statically checked.',
|
'Extract it to a separate variable so it can be statically checked.',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@ -731,7 +760,7 @@ const rule: Rule.RuleModule = {
|
|||||||
if (!isDeclaredInComponent) {
|
if (!isDeclaredInComponent) {
|
||||||
externalDependencies.add(declaredDependency);
|
externalDependencies.add(declaredDependency);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -782,9 +811,7 @@ const rule: Rule.RuleModule = {
|
|||||||
|
|
||||||
const message =
|
const message =
|
||||||
`The '${construction.name.name}' ${depType} ${causation} the dependencies of ` +
|
`The '${construction.name.name}' ${depType} ${causation} the dependencies of ` +
|
||||||
`${reactiveHookName} Hook (at line ${
|
`${reactiveHookName} Hook (at line ${declaredDependenciesNode.loc!.start.line}) ` +
|
||||||
declaredDependenciesNode.loc!.start.line
|
|
||||||
}) ` +
|
|
||||||
`change on every render. ${advice}`;
|
`change on every render. ${advice}`;
|
||||||
|
|
||||||
let suggest: Rule.SuggestionReportDescriptor[] | undefined;
|
let suggest: Rule.SuggestionReportDescriptor[] | undefined;
|
||||||
@ -838,7 +865,7 @@ const rule: Rule.RuleModule = {
|
|||||||
// in some extra deduplication. We can't do this
|
// in some extra deduplication. We can't do this
|
||||||
// for effects though because those have legit
|
// for effects though because those have legit
|
||||||
// use cases for over-specifying deps.
|
// use cases for over-specifying deps.
|
||||||
if (!isEffect && missingDependencies.size) {
|
if (!isEffect && missingDependencies.size > 0) {
|
||||||
suggestedDeps = collectRecommendations({
|
suggestedDeps = collectRecommendations({
|
||||||
dependencies,
|
dependencies,
|
||||||
declaredDependencies: [], // Pretend we don't know
|
declaredDependencies: [], // Pretend we don't know
|
||||||
@ -854,7 +881,7 @@ const rule: Rule.RuleModule = {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const declaredDepKeys = declaredDependencies.map(dep => dep.key);
|
const declaredDepKeys = declaredDependencies.map(dep => dep.key);
|
||||||
const sortedDeclaredDepKeys = declaredDepKeys.slice().sort();
|
const sortedDeclaredDepKeys = [...declaredDepKeys].sort();
|
||||||
return declaredDepKeys.join(',') === sortedDeclaredDepKeys.join(',');
|
return declaredDepKeys.join(',') === sortedDeclaredDepKeys.join(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -895,11 +922,7 @@ const rule: Rule.RuleModule = {
|
|||||||
' ' +
|
' ' +
|
||||||
(deps.size > 1 ? 'dependencies' : 'dependency') +
|
(deps.size > 1 ? 'dependencies' : 'dependency') +
|
||||||
': ' +
|
': ' +
|
||||||
joinEnglish(
|
joinEnglish([...deps].sort().map(name => "'" + formatDependency(name) + "'")) +
|
||||||
Array.from(deps)
|
|
||||||
.sort()
|
|
||||||
.map(name => "'" + formatDependency(name) + "'"),
|
|
||||||
) +
|
|
||||||
`. Either ${fixVerb} ${
|
`. Either ${fixVerb} ${
|
||||||
deps.size > 1 ? 'them' : 'it'
|
deps.size > 1 ? 'them' : 'it'
|
||||||
} or remove the dependency array.`
|
} or remove the dependency array.`
|
||||||
@ -909,20 +932,20 @@ const rule: Rule.RuleModule = {
|
|||||||
let extraWarning = '';
|
let extraWarning = '';
|
||||||
if (unnecessaryDependencies.size > 0) {
|
if (unnecessaryDependencies.size > 0) {
|
||||||
let badRef: string | null = null;
|
let badRef: string | null = null;
|
||||||
Array.from(unnecessaryDependencies.keys()).forEach(key => {
|
for (const key of unnecessaryDependencies.keys()) {
|
||||||
if (badRef !== null) {
|
if (badRef !== null) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
if (key.endsWith('.current')) {
|
if (key.endsWith('.current')) {
|
||||||
badRef = key;
|
badRef = key;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
if (badRef !== null) {
|
if (badRef !== null) {
|
||||||
extraWarning =
|
extraWarning =
|
||||||
` Mutable values like '${badRef}' aren't valid dependencies ` +
|
` Mutable values like '${badRef}' aren't valid dependencies ` +
|
||||||
"because mutating them doesn't re-render the component.";
|
"because mutating them doesn't re-render the component.";
|
||||||
} else if (externalDependencies.size > 0) {
|
} else if (externalDependencies.size > 0) {
|
||||||
const dep = Array.from(externalDependencies)[0];
|
const dep = [...externalDependencies][0];
|
||||||
// Don't show this warning for things that likely just got moved *inside* the callback
|
// Don't show this warning for things that likely just got moved *inside* the callback
|
||||||
// because in that case they're clearly not referring to globals.
|
// because in that case they're clearly not referring to globals.
|
||||||
if (!scope.set.has(dep)) {
|
if (!scope.set.has(dep)) {
|
||||||
@ -971,11 +994,11 @@ const rule: Rule.RuleModule = {
|
|||||||
` However, 'props' will change when *any* prop changes, so the ` +
|
` However, 'props' will change when *any* prop changes, so the ` +
|
||||||
`preferred fix is to destructure the 'props' object outside of ` +
|
`preferred fix is to destructure the 'props' object outside of ` +
|
||||||
`the ${reactiveHookName} call and refer to those specific props ` +
|
`the ${reactiveHookName} call and refer to those specific props ` +
|
||||||
`inside ${context.getSource(reactiveHook)}.`;
|
`inside ${getSource(reactiveHook)}.`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!extraWarning && missingDependencies.size) {
|
if (!extraWarning && missingDependencies.size > 0) {
|
||||||
// See if the user is trying to avoid specifying a callable prop.
|
// See if the user is trying to avoid specifying a callable prop.
|
||||||
// This usually means they're unaware of useCallback.
|
// This usually means they're unaware of useCallback.
|
||||||
let missingCallbackDep: string | null = null;
|
let missingCallbackDep: string | null = null;
|
||||||
@ -1041,7 +1064,7 @@ const rule: Rule.RuleModule = {
|
|||||||
let id: Identifier;
|
let id: Identifier;
|
||||||
let maybeCall: Node | null;
|
let maybeCall: Node | null;
|
||||||
for (let i = 0; i < references.length; i++) {
|
for (let i = 0; i < references.length; i++) {
|
||||||
id = references[i].identifier as Identifier;
|
id = references[i].identifier;
|
||||||
maybeCall = id.parent!;
|
maybeCall = id.parent!;
|
||||||
// Try to see if we have setState(someExpr(missingDep)).
|
// Try to see if we have setState(someExpr(missingDep)).
|
||||||
while (maybeCall != null && maybeCall !== componentScope.block) {
|
while (maybeCall != null && maybeCall !== componentScope.block) {
|
||||||
@ -1125,7 +1148,7 @@ const rule: Rule.RuleModule = {
|
|||||||
reportProblem({
|
reportProblem({
|
||||||
node: declaredDependenciesNode,
|
node: declaredDependenciesNode,
|
||||||
message:
|
message:
|
||||||
`React Hook ${context.getSource(reactiveHook)} has ` +
|
`React Hook ${getSource(reactiveHook)} has ` +
|
||||||
// To avoid a long message, show the next actionable item.
|
// To avoid a long message, show the next actionable item.
|
||||||
(getWarningMessage(missingDependencies, 'a', 'missing', 'include') ||
|
(getWarningMessage(missingDependencies, 'a', 'missing', 'include') ||
|
||||||
getWarningMessage(unnecessaryDependencies, 'an', 'unnecessary', 'exclude') ||
|
getWarningMessage(unnecessaryDependencies, 'an', 'unnecessary', 'exclude') ||
|
||||||
@ -1158,7 +1181,11 @@ const rule: Rule.RuleModule = {
|
|||||||
const reactiveHook = node.callee as Identifier | MemberExpression;
|
const reactiveHook = node.callee as Identifier | MemberExpression;
|
||||||
const reactiveHookName = (getNodeWithoutReactNamespace(reactiveHook) as Identifier)
|
const reactiveHookName = (getNodeWithoutReactNamespace(reactiveHook) as Identifier)
|
||||||
.name;
|
.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);
|
const isEffect = /Effect($|[^a-z])/g.test(reactiveHookName);
|
||||||
|
|
||||||
// Check whether a callback is supplied. If there is no callback supplied
|
// Check whether a callback is supplied. If there is no callback supplied
|
||||||
@ -1203,7 +1230,16 @@ const rule: Rule.RuleModule = {
|
|||||||
isEffect,
|
isEffect,
|
||||||
);
|
);
|
||||||
return; // Handled
|
return; // Handled
|
||||||
case 'Identifier':
|
case 'TSAsExpression':
|
||||||
|
visitFunctionWithDependencies(
|
||||||
|
callback.expression,
|
||||||
|
declaredDependenciesNode,
|
||||||
|
reactiveHook,
|
||||||
|
reactiveHookName,
|
||||||
|
isEffect,
|
||||||
|
);
|
||||||
|
return; // Handled
|
||||||
|
case 'Identifier': {
|
||||||
if (!declaredDependenciesNode) {
|
if (!declaredDependenciesNode) {
|
||||||
// No deps, no problems.
|
// No deps, no problems.
|
||||||
return; // Handled
|
return; // Handled
|
||||||
@ -1221,7 +1257,7 @@ const rule: Rule.RuleModule = {
|
|||||||
return; // Handled
|
return; // Handled
|
||||||
}
|
}
|
||||||
// We'll do our best effort to find it, complain otherwise.
|
// We'll do our best effort to find it, complain otherwise.
|
||||||
const variable = context.getScope().set.get(callback.name);
|
const variable = getScope(callback).set.get(callback.name);
|
||||||
if (variable == null || variable.defs == null) {
|
if (variable == null || variable.defs == null) {
|
||||||
// If it's not in scope, we don't care.
|
// If it's not in scope, we don't care.
|
||||||
return; // Handled
|
return; // Handled
|
||||||
@ -1271,6 +1307,7 @@ const rule: Rule.RuleModule = {
|
|||||||
break; // Unhandled
|
break; // Unhandled
|
||||||
}
|
}
|
||||||
break; // Unhandled
|
break; // Unhandled
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
// useEffect(generateEffectBody(), []);
|
// useEffect(generateEffectBody(), []);
|
||||||
reportProblem({
|
reportProblem({
|
||||||
@ -1358,33 +1395,33 @@ function collectRecommendations({
|
|||||||
|
|
||||||
function createDepTree(): DepTree {
|
function createDepTree(): DepTree {
|
||||||
return {
|
return {
|
||||||
isUsed: false,
|
isUsed: false, // True if used in code
|
||||||
isSatisfiedRecursively: false,
|
isSatisfiedRecursively: false, // True if specified in deps
|
||||||
isSubtreeUsed: false,
|
isSubtreeUsed: false, // True if something deeper is used by code
|
||||||
children: new Map<string, never>(),
|
children: new Map(), // Nodes for properties
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark all required nodes first.
|
// Mark all required nodes first.
|
||||||
// Imagine exclamation marks next to each used deep property.
|
// Imagine exclamation marks next to each used deep property.
|
||||||
dependencies.forEach((_, key) => {
|
for (const key of dependencies.keys()) {
|
||||||
const node = getOrCreateNodeByPath(depTree, key);
|
const node = getOrCreateNodeByPath(depTree, key);
|
||||||
node.isUsed = true;
|
node.isUsed = true;
|
||||||
markAllParentsByPath(depTree, key, parent => {
|
markAllParentsByPath(depTree, key, parent => {
|
||||||
parent.isSubtreeUsed = true;
|
parent.isSubtreeUsed = true;
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
// Mark all satisfied nodes.
|
// Mark all satisfied nodes.
|
||||||
// Imagine checkmarks next to each declared dependency.
|
// Imagine checkmarks next to each declared dependency.
|
||||||
declaredDependencies.forEach(({ key }) => {
|
for (const { key } of declaredDependencies) {
|
||||||
const node = getOrCreateNodeByPath(depTree, key);
|
const node = getOrCreateNodeByPath(depTree, key);
|
||||||
node.isSatisfiedRecursively = true;
|
node.isSatisfiedRecursively = true;
|
||||||
});
|
}
|
||||||
stableDependencies.forEach(key => {
|
for (const key of stableDependencies) {
|
||||||
const node = getOrCreateNodeByPath(depTree, key);
|
const node = getOrCreateNodeByPath(depTree, key);
|
||||||
node.isSatisfiedRecursively = true;
|
node.isSatisfiedRecursively = true;
|
||||||
});
|
}
|
||||||
|
|
||||||
// Tree manipulation helpers.
|
// Tree manipulation helpers.
|
||||||
function getOrCreateNodeByPath(rootNode: DepTree, path: string): DepTree {
|
function getOrCreateNodeByPath(rootNode: DepTree, path: string): DepTree {
|
||||||
@ -1460,15 +1497,15 @@ function collectRecommendations({
|
|||||||
const suggestedDependencies: string[] = [];
|
const suggestedDependencies: string[] = [];
|
||||||
const unnecessaryDependencies = new Set<string>();
|
const unnecessaryDependencies = new Set<string>();
|
||||||
const duplicateDependencies = new Set<string>();
|
const duplicateDependencies = new Set<string>();
|
||||||
declaredDependencies.forEach(({ key }) => {
|
for (const { key } of declaredDependencies) {
|
||||||
// Does this declared dep satisfy a real need?
|
// Does this declared dep satisfy a real need?
|
||||||
if (satisfyingDependencies.has(key)) {
|
if (satisfyingDependencies.has(key)) {
|
||||||
if (!suggestedDependencies.includes(key)) {
|
if (suggestedDependencies.includes(key)) {
|
||||||
// Good one.
|
|
||||||
suggestedDependencies.push(key);
|
|
||||||
} else {
|
|
||||||
// Duplicate.
|
// Duplicate.
|
||||||
duplicateDependencies.add(key);
|
duplicateDependencies.add(key);
|
||||||
|
} else {
|
||||||
|
// Good one.
|
||||||
|
suggestedDependencies.push(key);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isEffect && !key.endsWith('.current') && !externalDependencies.has(key)) {
|
if (isEffect && !key.endsWith('.current') && !externalDependencies.has(key)) {
|
||||||
@ -1476,7 +1513,7 @@ function collectRecommendations({
|
|||||||
// Such as resetting scroll when ID changes.
|
// Such as resetting scroll when ID changes.
|
||||||
// Consider them legit.
|
// Consider them legit.
|
||||||
// The exception is ref.current which is always wrong.
|
// The exception is ref.current which is always wrong.
|
||||||
if (suggestedDependencies.indexOf(key) === -1) {
|
if (!suggestedDependencies.includes(key)) {
|
||||||
suggestedDependencies.push(key);
|
suggestedDependencies.push(key);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1484,12 +1521,12 @@ function collectRecommendations({
|
|||||||
unnecessaryDependencies.add(key);
|
unnecessaryDependencies.add(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
// Then add the missing ones at the end.
|
// Then add the missing ones at the end.
|
||||||
missingDependencies.forEach(key => {
|
for (const key of missingDependencies) {
|
||||||
suggestedDependencies.push(key);
|
suggestedDependencies.push(key);
|
||||||
});
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
suggestedDependencies,
|
suggestedDependencies,
|
||||||
@ -1545,7 +1582,7 @@ function getConstructionExpressionType(node: Node) {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
case 'TypeCastExpression':
|
case 'TypeCastExpression':
|
||||||
return getConstructionExpressionType(node.expression);
|
case 'AsExpression':
|
||||||
case 'TSAsExpression':
|
case 'TSAsExpression':
|
||||||
return getConstructionExpressionType(node.expression);
|
return getConstructionExpressionType(node.expression);
|
||||||
}
|
}
|
||||||
@ -1623,12 +1660,13 @@ function scanForConstructions({
|
|||||||
while (currentScope !== scope && currentScope != null) {
|
while (currentScope !== scope && currentScope != null) {
|
||||||
currentScope = currentScope.upper!;
|
currentScope = currentScope.upper!;
|
||||||
}
|
}
|
||||||
if (currentScope !== scope) {
|
if (
|
||||||
|
currentScope !== scope &&
|
||||||
// This reference is outside the Hook callback.
|
// This reference is outside the Hook callback.
|
||||||
// It can only be legit if it's the deps array.
|
// It can only be legit if it's the deps array.
|
||||||
if (!isAncestorNodeOf(declaredDependenciesNode, reference.identifier)) {
|
!isAncestorNodeOf(declaredDependenciesNode, reference.identifier)
|
||||||
return true;
|
) {
|
||||||
}
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -1653,7 +1691,6 @@ function getDependency(node: Node): Node {
|
|||||||
if (
|
if (
|
||||||
(parent.type === 'MemberExpression' || parent.type === 'OptionalMemberExpression') &&
|
(parent.type === 'MemberExpression' || parent.type === 'OptionalMemberExpression') &&
|
||||||
parent.object === node &&
|
parent.object === node &&
|
||||||
parent.property.type === 'Identifier' &&
|
|
||||||
parent.property.name !== 'current' &&
|
parent.property.name !== 'current' &&
|
||||||
!parent.computed &&
|
!parent.computed &&
|
||||||
!(
|
!(
|
||||||
@ -1796,7 +1833,7 @@ function getReactiveHookCallbackIndex(
|
|||||||
try {
|
try {
|
||||||
name = analyzePropertyChain(node, null);
|
name = analyzePropertyChain(node, null);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (/Unsupported node type/.test(error.message)) {
|
if (/Unsupported node type/.test((error as Error).message)) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
throw error;
|
throw error;
|
||||||
@ -1842,12 +1879,12 @@ function fastFindReferenceWithParent(start: Node, target: Node): Node | null {
|
|||||||
value.parent = item;
|
value.parent = item;
|
||||||
queue.push(value);
|
queue.push(value);
|
||||||
} else if (Array.isArray(value)) {
|
} else if (Array.isArray(value)) {
|
||||||
value.forEach(val => {
|
for (const val of value) {
|
||||||
if (isNodeLike(val)) {
|
if (isNodeLike(val)) {
|
||||||
val.parent = item;
|
val.parent = item;
|
||||||
queue.push(val);
|
queue.push(val);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1870,7 +1907,7 @@ function joinEnglish(arr: string[]): string {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNodeLike(val: any): boolean {
|
function isNodeLike(val: unknown): val is Node {
|
||||||
return (
|
return (
|
||||||
typeof val === 'object' &&
|
typeof val === 'object' &&
|
||||||
val !== null &&
|
val !== null &&
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* global BigInt */
|
/* global BigInt */
|
||||||
/* eslint-disable no-for-of-loops/no-for-of-loops */
|
|
||||||
import type { Rule, Scope } from 'eslint';
|
import type { Rule, Scope } from 'eslint';
|
||||||
import type {
|
import type {
|
||||||
CallExpression,
|
CallExpression,
|
||||||
@ -16,6 +15,7 @@ import type {
|
|||||||
Identifier,
|
Identifier,
|
||||||
BaseFunction,
|
BaseFunction,
|
||||||
} from 'estree';
|
} from 'estree';
|
||||||
|
|
||||||
import { __EXPERIMENTAL__ } from './index';
|
import { __EXPERIMENTAL__ } from './index';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,10 +24,7 @@ import { __EXPERIMENTAL__ } from './index';
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function isHookName(s: string) {
|
function isHookName(s: string) {
|
||||||
if (__EXPERIMENTAL__) {
|
return s === 'use' || /^use[\dA-Z]/.test(s);
|
||||||
return s === 'use' || /^use[A-Z0-9]/.test(s);
|
|
||||||
}
|
|
||||||
return /^use[A-Z0-9]/.test(s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -94,10 +91,8 @@ function isMemoCallback(node: Rule.Node) {
|
|||||||
function isInsideComponentOrHook(node: Rule.Node) {
|
function isInsideComponentOrHook(node: Rule.Node) {
|
||||||
while (node) {
|
while (node) {
|
||||||
const functionName = getFunctionName(node);
|
const functionName = getFunctionName(node);
|
||||||
if (functionName) {
|
if (functionName && (isComponentName(functionName) || isHook(functionName))) {
|
||||||
if (isComponentName(functionName) || isHook(functionName)) {
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (isForwardRefCallback(node) || isMemoCallback(node)) {
|
if (isForwardRefCallback(node) || isMemoCallback(node)) {
|
||||||
return true;
|
return true;
|
||||||
@ -115,10 +110,7 @@ function isUseEffectEventIdentifier(node: Node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isUseIdentifier(node: Node) {
|
function isUseIdentifier(node: Node) {
|
||||||
if (__EXPERIMENTAL__) {
|
return isReactFunction(node as Expression, 'use');
|
||||||
return node.type === 'Identifier' && node.name === 'use';
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const rule: Rule.RuleModule = {
|
const rule: Rule.RuleModule = {
|
||||||
@ -161,6 +153,22 @@ const rule: Rule.RuleModule = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SourceCode#getText that also works down to ESLint 3.0.0
|
||||||
|
*/
|
||||||
|
const getSource =
|
||||||
|
typeof context.getSource === 'function'
|
||||||
|
? (node: Node) => context.getSource(node)
|
||||||
|
: (node: Node) => context.sourceCode.getText(node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SourceCode#getScope that also works down to ESLint 3.0.0
|
||||||
|
*/
|
||||||
|
const getScope =
|
||||||
|
typeof context.getScope === 'function'
|
||||||
|
? () => context.getScope()
|
||||||
|
: (node: Node) => context.sourceCode.getScope(node);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// Maintain code segment path stack as we traverse.
|
// Maintain code segment path stack as we traverse.
|
||||||
onCodePathSegmentStart: segment => codePathSegmentStack.push(segment),
|
onCodePathSegmentStart: segment => codePathSegmentStack.push(segment),
|
||||||
@ -479,7 +487,7 @@ const rule: Rule.RuleModule = {
|
|||||||
context.report({
|
context.report({
|
||||||
node: hook,
|
node: hook,
|
||||||
message:
|
message:
|
||||||
`React Hook "${context.getSource(hook)}" may be executed ` +
|
`React Hook "${getSource(hook)}" may be executed ` +
|
||||||
'more than once. Possibly because it is called in a loop. ' +
|
'more than once. Possibly because it is called in a loop. ' +
|
||||||
'React Hooks must be called in the exact same order in ' +
|
'React Hooks must be called in the exact same order in ' +
|
||||||
'every component render.',
|
'every component render.',
|
||||||
@ -498,7 +506,7 @@ const rule: Rule.RuleModule = {
|
|||||||
context.report({
|
context.report({
|
||||||
node: hook,
|
node: hook,
|
||||||
message:
|
message:
|
||||||
`React Hook "${context.getSource(hook)}" cannot be ` +
|
`React Hook "${getSource(hook)}" cannot be ` +
|
||||||
'called in an async function.',
|
'called in an async function.',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -513,7 +521,7 @@ const rule: Rule.RuleModule = {
|
|||||||
!isUseIdentifier(hook) // `use(...)` can be called conditionally.
|
!isUseIdentifier(hook) // `use(...)` can be called conditionally.
|
||||||
) {
|
) {
|
||||||
const message =
|
const message =
|
||||||
`React Hook "${context.getSource(hook)}" is called ` +
|
`React Hook "${getSource(hook)}" is called ` +
|
||||||
'conditionally. React Hooks must be called in the exact ' +
|
'conditionally. React Hooks must be called in the exact ' +
|
||||||
'same order in every component render.' +
|
'same order in every component render.' +
|
||||||
(possiblyHasEarlyReturn
|
(possiblyHasEarlyReturn
|
||||||
@ -530,15 +538,15 @@ const rule: Rule.RuleModule = {
|
|||||||
) {
|
) {
|
||||||
// Custom message for hooks inside a class
|
// Custom message for hooks inside a class
|
||||||
const message =
|
const message =
|
||||||
`React Hook "${context.getSource(hook)}" cannot be called ` +
|
`React Hook "${getSource(hook)}" cannot be called ` +
|
||||||
'in a class component. React Hooks must be called in a ' +
|
'in a class component. React Hooks must be called in a ' +
|
||||||
'React function component or a custom React Hook function.';
|
'React function component or a custom React Hook function.';
|
||||||
context.report({ node: hook, message });
|
context.report({ node: hook, message });
|
||||||
} else if (codePathFunctionName) {
|
} else if (codePathFunctionName) {
|
||||||
// Custom message if we found an invalid function name.
|
// Custom message if we found an invalid function name.
|
||||||
const message =
|
const message =
|
||||||
`React Hook "${context.getSource(hook)}" is called in ` +
|
`React Hook "${getSource(hook)}" is called in ` +
|
||||||
`function "${context.getSource(codePathFunctionName)}" ` +
|
`function "${getSource(codePathFunctionName)}" ` +
|
||||||
'that is neither a React function component nor a custom ' +
|
'that is neither a React function component nor a custom ' +
|
||||||
'React Hook function.' +
|
'React Hook function.' +
|
||||||
' React component names must start with an uppercase letter.' +
|
' React component names must start with an uppercase letter.' +
|
||||||
@ -547,7 +555,7 @@ const rule: Rule.RuleModule = {
|
|||||||
} else if (codePathNode.type === 'Program') {
|
} else if (codePathNode.type === 'Program') {
|
||||||
// These are dangerous if you have inline requires enabled.
|
// These are dangerous if you have inline requires enabled.
|
||||||
const message =
|
const message =
|
||||||
`React Hook "${context.getSource(hook)}" cannot be called ` +
|
`React Hook "${getSource(hook)}" cannot be called ` +
|
||||||
'at the top level. React Hooks must be called in a ' +
|
'at the top level. React Hooks must be called in a ' +
|
||||||
'React function component or a custom React Hook function.';
|
'React function component or a custom React Hook function.';
|
||||||
context.report({ node: hook, message });
|
context.report({ node: hook, message });
|
||||||
@ -560,7 +568,7 @@ const rule: Rule.RuleModule = {
|
|||||||
// `use(...)` can be called in callbacks.
|
// `use(...)` can be called in callbacks.
|
||||||
if (isSomewhereInsideComponentOrHook && !isUseIdentifier(hook)) {
|
if (isSomewhereInsideComponentOrHook && !isUseIdentifier(hook)) {
|
||||||
const message =
|
const message =
|
||||||
`React Hook "${context.getSource(hook)}" cannot be called ` +
|
`React Hook "${getSource(hook)}" cannot be called ` +
|
||||||
'inside a callback. React Hooks must be called in a ' +
|
'inside a callback. React Hooks must be called in a ' +
|
||||||
'React function component or a custom React Hook function.';
|
'React function component or a custom React Hook function.';
|
||||||
context.report({ node: hook, message });
|
context.report({ node: hook, message });
|
||||||
@ -612,7 +620,7 @@ const rule: Rule.RuleModule = {
|
|||||||
context.report({
|
context.report({
|
||||||
node,
|
node,
|
||||||
message:
|
message:
|
||||||
`\`${context.getSource(
|
`\`${getSource(
|
||||||
node,
|
node,
|
||||||
)}\` is a function created with React Hook "useEffectEvent", and can only be called from ` +
|
)}\` is a function created with React Hook "useEffectEvent", and can only be called from ` +
|
||||||
'the same component. They cannot be assigned to variables or passed down.',
|
'the same component. They cannot be assigned to variables or passed down.',
|
||||||
@ -629,14 +637,14 @@ const rule: Rule.RuleModule = {
|
|||||||
FunctionDeclaration(node) {
|
FunctionDeclaration(node) {
|
||||||
// function MyComponent() { const onClick = useEffectEvent(...) }
|
// function MyComponent() { const onClick = useEffectEvent(...) }
|
||||||
if (isInsideComponentOrHook(node)) {
|
if (isInsideComponentOrHook(node)) {
|
||||||
recordAllUseEffectEventFunctions(context.getScope());
|
recordAllUseEffectEventFunctions(getScope(node));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
ArrowFunctionExpression(node) {
|
ArrowFunctionExpression(node) {
|
||||||
// const MyComponent = () => { const onClick = useEffectEvent(...) }
|
// const MyComponent = () => { const onClick = useEffectEvent(...) }
|
||||||
if (isInsideComponentOrHook(node)) {
|
if (isInsideComponentOrHook(node)) {
|
||||||
recordAllUseEffectEventFunctions(context.getScope());
|
recordAllUseEffectEventFunctions(getScope(node));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -4,23 +4,30 @@
|
|||||||
* This source code is licensed under the MIT license found in the
|
* This source code is licensed under the MIT license found in the
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
import type { Linter } from 'eslint';
|
|
||||||
import RulesOfHooks from './RulesOfHooks';
|
import { Linter } from 'eslint';
|
||||||
|
|
||||||
import ExhaustiveDeps from './ExhaustiveDeps';
|
import ExhaustiveDeps from './ExhaustiveDeps';
|
||||||
|
import { name, version } from './package.json';
|
||||||
|
import RulesOfHooks from './RulesOfHooks';
|
||||||
|
|
||||||
export const __EXPERIMENTAL__ = false;
|
export const __EXPERIMENTAL__ = false;
|
||||||
|
|
||||||
export const configs = {
|
export const flatConfigs = {
|
||||||
recommended: {
|
recommended: {
|
||||||
plugins: ['react-hooks'],
|
name: 'react-hooks/recommended',
|
||||||
|
plugins: {
|
||||||
|
'react-hooks': {
|
||||||
|
meta: { name, version },
|
||||||
|
rules: {
|
||||||
|
'rules-of-hooks': RulesOfHooks,
|
||||||
|
'exhaustive-deps': ExhaustiveDeps,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
rules: {
|
rules: {
|
||||||
'react-hooks/rules-of-hooks': 'error',
|
'react-hooks/rules-of-hooks': 'error',
|
||||||
'react-hooks/exhaustive-deps': 'warn',
|
'react-hooks/exhaustive-deps': 'warn',
|
||||||
},
|
},
|
||||||
} as Linter.BaseConfig,
|
} satisfies Linter.Config,
|
||||||
};
|
|
||||||
|
|
||||||
export const rules = {
|
|
||||||
'rules-of-hooks': RulesOfHooks,
|
|
||||||
'exhaustive-deps': ExhaustiveDeps,
|
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
{
|
{
|
||||||
|
"name": "eslint-plugin-react-hooks",
|
||||||
|
"version": "4.2.0",
|
||||||
"upstream": {
|
"upstream": {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
|
"comment": "https://github.com/facebook/react/pull/30774",
|
||||||
"sources": {
|
"sources": {
|
||||||
"main": {
|
"main": {
|
||||||
"repository": "git@github.com:facebook/react.git",
|
"repository": "git@github.com:facebook/react.git",
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
diff --git a/package.json b/package.json
|
|
||||||
index 510ac18..569cdc0 100644
|
|
||||||
--- a/package.json
|
|
||||||
+++ b/package.json
|
|
||||||
@@ -62,8 +62,7 @@
|
|
||||||
"typecov": "type-coverage"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
- "eslint": "*",
|
|
||||||
- "eslint-plugin-import": "*"
|
|
||||||
+ "eslint": "*"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"debug": "^4.3.4",
|
|
||||||
diff --git a/tsconfig.json b/tsconfig.json
|
|
||||||
deleted file mode 100644
|
|
||||||
index 81e4c05..0000000
|
|
||||||
--- a/tsconfig.json
|
|
||||||
+++ /dev/null
|
|
||||||
@@ -1,7 +0,0 @@
|
|
||||||
-{
|
|
||||||
- "extends": "@1stg/tsconfig/node16",
|
|
||||||
- "compilerOptions": {
|
|
||||||
- "outDir": "./lib"
|
|
||||||
- },
|
|
||||||
- "include": ["./src", "./shim.d.ts"]
|
|
||||||
-}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,16 @@
|
|||||||
diff --git a/src/index.js b/src/index.js
|
diff --git a/src/index.js b/src/index.js
|
||||||
index 7b931fe..eaea267 100644
|
index 2fa185f..3cf8018 100644
|
||||||
--- a/src/index.js
|
--- a/src/index.js
|
||||||
+++ b/src/index.js
|
+++ b/src/index.js
|
||||||
@@ -1,296 +1,344 @@
|
@@ -1,48 +1,90 @@
|
||||||
/* eslint-disable global-require */
|
/* eslint-disable global-require */
|
||||||
+// @ts-check
|
-const flatConfigBase = require('./configs/flat-config-base');
|
||||||
|
-const legacyConfigBase = require('./configs/legacy-config-base');
|
||||||
|
-const { name, version } = require('../package.json');
|
||||||
|
+import flatConfigBase from './configs/flat-config-base';
|
||||||
|
+import legacyConfigBase from './configs/legacy-config-base';
|
||||||
|
+import { name, version } from '../package.json';
|
||||||
|
+
|
||||||
+import accessibleEmoji from './rules/accessible-emoji';
|
+import accessibleEmoji from './rules/accessible-emoji';
|
||||||
+import altText from './rules/alt-text';
|
+import altText from './rules/alt-text';
|
||||||
+import anchorAmbiguousText from './rules/anchor-ambiguous-text';
|
+import anchorAmbiguousText from './rules/anchor-ambiguous-text';
|
||||||
@ -45,577 +51,160 @@ index 7b931fe..eaea267 100644
|
|||||||
+import scope from './rules/scope';
|
+import scope from './rules/scope';
|
||||||
+import tabindexNoPositive from './rules/tabindex-no-positive';
|
+import tabindexNoPositive from './rules/tabindex-no-positive';
|
||||||
|
|
||||||
-module.exports = {
|
const allRules = {
|
||||||
- rules: {
|
- 'accessible-emoji': require('./rules/accessible-emoji'),
|
||||||
- 'accessible-emoji': require('./rules/accessible-emoji'),
|
- 'alt-text': require('./rules/alt-text'),
|
||||||
- 'alt-text': require('./rules/alt-text'),
|
- 'anchor-ambiguous-text': require('./rules/anchor-ambiguous-text'),
|
||||||
- 'anchor-ambiguous-text': require('./rules/anchor-ambiguous-text'),
|
- 'anchor-has-content': require('./rules/anchor-has-content'),
|
||||||
- 'anchor-has-content': require('./rules/anchor-has-content'),
|
- 'anchor-is-valid': require('./rules/anchor-is-valid'),
|
||||||
- 'anchor-is-valid': require('./rules/anchor-is-valid'),
|
- 'aria-activedescendant-has-tabindex': require('./rules/aria-activedescendant-has-tabindex'),
|
||||||
- 'aria-activedescendant-has-tabindex': require('./rules/aria-activedescendant-has-tabindex'),
|
- 'aria-props': require('./rules/aria-props'),
|
||||||
- 'aria-props': require('./rules/aria-props'),
|
- 'aria-proptypes': require('./rules/aria-proptypes'),
|
||||||
- 'aria-proptypes': require('./rules/aria-proptypes'),
|
- 'aria-role': require('./rules/aria-role'),
|
||||||
- 'aria-role': require('./rules/aria-role'),
|
- 'aria-unsupported-elements': require('./rules/aria-unsupported-elements'),
|
||||||
- 'aria-unsupported-elements': require('./rules/aria-unsupported-elements'),
|
- 'autocomplete-valid': require('./rules/autocomplete-valid'),
|
||||||
- 'autocomplete-valid': require('./rules/autocomplete-valid'),
|
- 'click-events-have-key-events': require('./rules/click-events-have-key-events'),
|
||||||
- 'click-events-have-key-events': require('./rules/click-events-have-key-events'),
|
- 'control-has-associated-label': require('./rules/control-has-associated-label'),
|
||||||
- 'control-has-associated-label': require('./rules/control-has-associated-label'),
|
- 'heading-has-content': require('./rules/heading-has-content'),
|
||||||
- 'heading-has-content': require('./rules/heading-has-content'),
|
- 'html-has-lang': require('./rules/html-has-lang'),
|
||||||
- 'html-has-lang': require('./rules/html-has-lang'),
|
- 'iframe-has-title': require('./rules/iframe-has-title'),
|
||||||
- 'iframe-has-title': require('./rules/iframe-has-title'),
|
- 'img-redundant-alt': require('./rules/img-redundant-alt'),
|
||||||
- 'img-redundant-alt': require('./rules/img-redundant-alt'),
|
- 'interactive-supports-focus': require('./rules/interactive-supports-focus'),
|
||||||
- 'interactive-supports-focus': require('./rules/interactive-supports-focus'),
|
- 'label-has-associated-control': require('./rules/label-has-associated-control'),
|
||||||
- 'label-has-associated-control': require('./rules/label-has-associated-control'),
|
- 'label-has-for': require('./rules/label-has-for'),
|
||||||
- 'label-has-for': require('./rules/label-has-for'),
|
- lang: require('./rules/lang'),
|
||||||
- lang: require('./rules/lang'),
|
- 'media-has-caption': require('./rules/media-has-caption'),
|
||||||
- 'media-has-caption': require('./rules/media-has-caption'),
|
- 'mouse-events-have-key-events': require('./rules/mouse-events-have-key-events'),
|
||||||
- 'mouse-events-have-key-events': require('./rules/mouse-events-have-key-events'),
|
- 'no-access-key': require('./rules/no-access-key'),
|
||||||
- 'no-access-key': require('./rules/no-access-key'),
|
- 'no-aria-hidden-on-focusable': require('./rules/no-aria-hidden-on-focusable'),
|
||||||
- 'no-aria-hidden-on-focusable': require('./rules/no-aria-hidden-on-focusable'),
|
- 'no-autofocus': require('./rules/no-autofocus'),
|
||||||
- 'no-autofocus': require('./rules/no-autofocus'),
|
- 'no-distracting-elements': require('./rules/no-distracting-elements'),
|
||||||
- 'no-distracting-elements': require('./rules/no-distracting-elements'),
|
- 'no-interactive-element-to-noninteractive-role': require('./rules/no-interactive-element-to-noninteractive-role'),
|
||||||
- 'no-interactive-element-to-noninteractive-role': require('./rules/no-interactive-element-to-noninteractive-role'),
|
- 'no-noninteractive-element-interactions': require('./rules/no-noninteractive-element-interactions'),
|
||||||
- 'no-noninteractive-element-interactions': require('./rules/no-noninteractive-element-interactions'),
|
- 'no-noninteractive-element-to-interactive-role': require('./rules/no-noninteractive-element-to-interactive-role'),
|
||||||
- 'no-noninteractive-element-to-interactive-role': require('./rules/no-noninteractive-element-to-interactive-role'),
|
- 'no-noninteractive-tabindex': require('./rules/no-noninteractive-tabindex'),
|
||||||
- 'no-noninteractive-tabindex': require('./rules/no-noninteractive-tabindex'),
|
- 'no-onchange': require('./rules/no-onchange'),
|
||||||
- 'no-onchange': require('./rules/no-onchange'),
|
- 'no-redundant-roles': require('./rules/no-redundant-roles'),
|
||||||
- 'no-redundant-roles': require('./rules/no-redundant-roles'),
|
- 'no-static-element-interactions': require('./rules/no-static-element-interactions'),
|
||||||
- 'no-static-element-interactions': require('./rules/no-static-element-interactions'),
|
- 'prefer-tag-over-role': require('./rules/prefer-tag-over-role'),
|
||||||
- 'prefer-tag-over-role': require('./rules/prefer-tag-over-role'),
|
- 'role-has-required-aria-props': require('./rules/role-has-required-aria-props'),
|
||||||
- 'role-has-required-aria-props': require('./rules/role-has-required-aria-props'),
|
- 'role-supports-aria-props': require('./rules/role-supports-aria-props'),
|
||||||
- 'role-supports-aria-props': require('./rules/role-supports-aria-props'),
|
- scope: require('./rules/scope'),
|
||||||
- scope: require('./rules/scope'),
|
- 'tabindex-no-positive': require('./rules/tabindex-no-positive'),
|
||||||
- 'tabindex-no-positive': require('./rules/tabindex-no-positive'),
|
+ 'accessible-emoji': accessibleEmoji,
|
||||||
- },
|
+ 'alt-text': altText,
|
||||||
- configs: {
|
+ 'anchor-ambiguous-text': anchorAmbiguousText,
|
||||||
- recommended: {
|
+ 'anchor-has-content': anchorHasContent,
|
||||||
- plugins: [
|
+ 'anchor-is-valid': anchorIsValid,
|
||||||
- 'jsx-a11y',
|
+ 'aria-activedescendant-has-tabindex': ariaActivedescendantHasTabindex,
|
||||||
+export const rules = kebabCase({
|
+ 'aria-props': ariaProps,
|
||||||
+ accessibleEmoji,
|
+ 'aria-proptypes': ariaProptypes,
|
||||||
+ altText,
|
+ 'aria-role': ariaRole,
|
||||||
+ anchorAmbiguousText,
|
+ 'aria-unsupported-elements': ariaUnsupportedElements,
|
||||||
+ anchorHasContent,
|
+ 'autocomplete-valid': autocompleteValid,
|
||||||
+ anchorIsValid,
|
+ 'click-events-have-key-events': clickEventsHaveKeyEvents,
|
||||||
+ ariaActivedescendantHasTabindex,
|
+ 'control-has-associated-label': controlHasAssociatedLabel,
|
||||||
+ ariaProps,
|
+ 'heading-has-content': headingHasContent,
|
||||||
+ ariaProptypes,
|
+ 'html-has-lang': htmlHasLang,
|
||||||
+ ariaRole,
|
+ 'iframe-has-title': iframeHasTitle,
|
||||||
+ ariaUnsupportedElements,
|
+ 'img-redundant-alt': imgRedundantAlt,
|
||||||
+ autocompleteValid,
|
+ 'interactive-supports-focus': interactiveSupportsFocus,
|
||||||
+ clickEventsHaveKeyEvents,
|
+ 'label-has-associated-control': labelHasAssociatedControl,
|
||||||
+ controlHasAssociatedLabel,
|
+ 'label-has-for': labelHasFor,
|
||||||
+ headingHasContent,
|
|
||||||
+ htmlHasLang,
|
|
||||||
+ iframeHasTitle,
|
|
||||||
+ imgRedundantAlt,
|
|
||||||
+ interactiveSupportsFocus,
|
|
||||||
+ labelHasAssociatedControl,
|
|
||||||
+ labelHasFor,
|
|
||||||
+ lang,
|
+ lang,
|
||||||
+ mediaHasCaption,
|
+ 'media-has-caption': mediaHasCaption,
|
||||||
+ mouseEventsHaveKeyEvents,
|
+ 'mouse-events-have-key-events': mouseEventsHaveKeyEvents,
|
||||||
+ noAccessKey,
|
+ 'no-access-key': noAccessKey,
|
||||||
+ noAriaHiddenOnFocusable,
|
+ 'no-aria-hidden-on-focusable': noAriaHiddenOnFocusable,
|
||||||
+ noAutofocus,
|
+ 'no-autofocus': noAutofocus,
|
||||||
+ noDistractingElements,
|
+ 'no-distracting-elements': noDistractingElements,
|
||||||
+ noInteractiveElementToNoninteractiveRole,
|
+ 'no-interactive-element-to-noninteractive-role':
|
||||||
+ noNoninteractiveElementInteractions,
|
+ noInteractiveElementToNoninteractiveRole,
|
||||||
+ noNoninteractiveElementToInteractiveRole,
|
+ 'no-noninteractive-element-interactions': noNoninteractiveElementInteractions,
|
||||||
+ noNoninteractiveTabindex,
|
+ 'no-noninteractive-element-to-interactive-role':
|
||||||
+ noOnChange,
|
+ noNoninteractiveElementToInteractiveRole,
|
||||||
+ noRedundantRoles,
|
+ 'no-noninteractive-tabindex': noNoninteractiveTabindex,
|
||||||
+ noStaticElementInteractions,
|
+ 'no-onchange': noOnChange,
|
||||||
+ preferTagOverRole,
|
+ 'no-redundant-roles': noRedundantRoles,
|
||||||
+ roleHasRequiredAriaProps,
|
+ 'no-static-element-interactions': noStaticElementInteractions,
|
||||||
+ roleSupportsAriaProps,
|
+ 'prefer-tag-over-role': preferTagOverRole,
|
||||||
|
+ 'role-has-required-aria-props': roleHasRequiredAriaProps,
|
||||||
|
+ 'role-supports-aria-props': roleSupportsAriaProps,
|
||||||
+ scope,
|
+ scope,
|
||||||
+ tabindexNoPositive,
|
+ 'tabindex-no-positive': tabindexNoPositive,
|
||||||
+});
|
|
||||||
+export const configs = {
|
|
||||||
+ recommended: {
|
|
||||||
+ plugins: [
|
|
||||||
+ 'jsx-a11y',
|
|
||||||
+ ],
|
|
||||||
+ parserOptions: {
|
|
||||||
+ ecmaFeatures: {
|
|
||||||
+ jsx: true,
|
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
+ rules: {
|
|
||||||
+ 'jsx-a11y/alt-text': 'error',
|
|
||||||
+ 'jsx-a11y/anchor-ambiguous-text': 'off', // TODO: error
|
|
||||||
+ 'jsx-a11y/anchor-has-content': 'error',
|
|
||||||
+ 'jsx-a11y/anchor-is-valid': 'error',
|
|
||||||
+ 'jsx-a11y/aria-activedescendant-has-tabindex': 'error',
|
|
||||||
+ 'jsx-a11y/aria-props': 'error',
|
|
||||||
+ 'jsx-a11y/aria-proptypes': 'error',
|
|
||||||
+ 'jsx-a11y/aria-role': 'error',
|
|
||||||
+ 'jsx-a11y/aria-unsupported-elements': 'error',
|
|
||||||
+ 'jsx-a11y/autocomplete-valid': 'error',
|
|
||||||
+ 'jsx-a11y/click-events-have-key-events': 'error',
|
|
||||||
+ 'jsx-a11y/control-has-associated-label': ['off', {
|
|
||||||
+ ignoreElements: [
|
|
||||||
+ 'audio',
|
|
||||||
+ 'canvas',
|
|
||||||
+ 'embed',
|
|
||||||
+ 'input',
|
|
||||||
+ 'textarea',
|
|
||||||
+ 'tr',
|
|
||||||
+ 'video',
|
|
||||||
+ ],
|
|
||||||
+ ignoreRoles: [
|
|
||||||
+ 'grid',
|
|
||||||
+ 'listbox',
|
|
||||||
+ 'menu',
|
|
||||||
+ 'menubar',
|
|
||||||
+ 'radiogroup',
|
|
||||||
+ 'row',
|
|
||||||
+ 'tablist',
|
|
||||||
+ 'toolbar',
|
|
||||||
+ 'tree',
|
|
||||||
+ 'treegrid',
|
|
||||||
+ ],
|
|
||||||
+ includeRoles: [
|
|
||||||
+ 'alert',
|
|
||||||
+ 'dialog',
|
|
||||||
+ ],
|
|
||||||
+ }],
|
|
||||||
+ 'jsx-a11y/heading-has-content': 'error',
|
|
||||||
+ 'jsx-a11y/html-has-lang': 'error',
|
|
||||||
+ 'jsx-a11y/iframe-has-title': 'error',
|
|
||||||
+ 'jsx-a11y/img-redundant-alt': 'error',
|
|
||||||
+ 'jsx-a11y/interactive-supports-focus': [
|
|
||||||
+ 'error',
|
|
||||||
+ {
|
|
||||||
+ tabbable: [
|
|
||||||
+ 'button',
|
|
||||||
+ 'checkbox',
|
|
||||||
+ 'link',
|
|
||||||
+ 'searchbox',
|
|
||||||
+ 'spinbutton',
|
|
||||||
+ 'switch',
|
|
||||||
+ 'textbox',
|
|
||||||
+ ],
|
|
||||||
+ },
|
|
||||||
],
|
|
||||||
- parserOptions: {
|
|
||||||
- ecmaFeatures: {
|
|
||||||
- jsx: true,
|
|
||||||
+ 'jsx-a11y/label-has-associated-control': 'error',
|
|
||||||
+ 'jsx-a11y/label-has-for': 'off',
|
|
||||||
+ 'jsx-a11y/media-has-caption': 'error',
|
|
||||||
+ 'jsx-a11y/mouse-events-have-key-events': 'error',
|
|
||||||
+ 'jsx-a11y/no-access-key': 'error',
|
|
||||||
+ 'jsx-a11y/no-autofocus': 'error',
|
|
||||||
+ 'jsx-a11y/no-distracting-elements': 'error',
|
|
||||||
+ 'jsx-a11y/no-interactive-element-to-noninteractive-role': [
|
|
||||||
+ 'error',
|
|
||||||
+ {
|
|
||||||
+ tr: ['none', 'presentation'],
|
|
||||||
+ canvas: ['img'],
|
|
||||||
},
|
|
||||||
- },
|
|
||||||
- rules: {
|
|
||||||
- 'jsx-a11y/alt-text': 'error',
|
|
||||||
- 'jsx-a11y/anchor-ambiguous-text': 'off', // TODO: error
|
|
||||||
- 'jsx-a11y/anchor-has-content': 'error',
|
|
||||||
- 'jsx-a11y/anchor-is-valid': 'error',
|
|
||||||
- 'jsx-a11y/aria-activedescendant-has-tabindex': 'error',
|
|
||||||
- 'jsx-a11y/aria-props': 'error',
|
|
||||||
- 'jsx-a11y/aria-proptypes': 'error',
|
|
||||||
- 'jsx-a11y/aria-role': 'error',
|
|
||||||
- 'jsx-a11y/aria-unsupported-elements': 'error',
|
|
||||||
- 'jsx-a11y/autocomplete-valid': 'error',
|
|
||||||
- 'jsx-a11y/click-events-have-key-events': 'error',
|
|
||||||
- 'jsx-a11y/control-has-associated-label': ['off', {
|
|
||||||
- ignoreElements: [
|
|
||||||
- 'audio',
|
|
||||||
- 'canvas',
|
|
||||||
- 'embed',
|
|
||||||
- 'input',
|
|
||||||
- 'textarea',
|
|
||||||
- 'tr',
|
|
||||||
- 'video',
|
|
||||||
+ ],
|
|
||||||
+ 'jsx-a11y/no-noninteractive-element-interactions': [
|
|
||||||
+ 'error',
|
|
||||||
+ {
|
|
||||||
+ handlers: [
|
|
||||||
+ 'onClick',
|
|
||||||
+ 'onError',
|
|
||||||
+ 'onLoad',
|
|
||||||
+ 'onMouseDown',
|
|
||||||
+ 'onMouseUp',
|
|
||||||
+ 'onKeyPress',
|
|
||||||
+ 'onKeyDown',
|
|
||||||
+ 'onKeyUp',
|
|
||||||
],
|
|
||||||
- ignoreRoles: [
|
|
||||||
- 'grid',
|
|
||||||
+ alert: ['onKeyUp', 'onKeyDown', 'onKeyPress'],
|
|
||||||
+ body: ['onError', 'onLoad'],
|
|
||||||
+ dialog: ['onKeyUp', 'onKeyDown', 'onKeyPress'],
|
|
||||||
+ iframe: ['onError', 'onLoad'],
|
|
||||||
+ img: ['onError', 'onLoad'],
|
|
||||||
+ },
|
|
||||||
+ ],
|
|
||||||
+ 'jsx-a11y/no-noninteractive-element-to-interactive-role': [
|
|
||||||
+ 'error',
|
|
||||||
+ {
|
|
||||||
+ ul: [
|
|
||||||
'listbox',
|
|
||||||
'menu',
|
|
||||||
'menubar',
|
|
||||||
'radiogroup',
|
|
||||||
- 'row',
|
|
||||||
'tablist',
|
|
||||||
- 'toolbar',
|
|
||||||
'tree',
|
|
||||||
'treegrid',
|
|
||||||
],
|
|
||||||
- includeRoles: [
|
|
||||||
- 'alert',
|
|
||||||
- 'dialog',
|
|
||||||
- ],
|
|
||||||
- }],
|
|
||||||
- 'jsx-a11y/heading-has-content': 'error',
|
|
||||||
- 'jsx-a11y/html-has-lang': 'error',
|
|
||||||
- 'jsx-a11y/iframe-has-title': 'error',
|
|
||||||
- 'jsx-a11y/img-redundant-alt': 'error',
|
|
||||||
- 'jsx-a11y/interactive-supports-focus': [
|
|
||||||
- 'error',
|
|
||||||
- {
|
|
||||||
- tabbable: [
|
|
||||||
- 'button',
|
|
||||||
- 'checkbox',
|
|
||||||
- 'link',
|
|
||||||
- 'searchbox',
|
|
||||||
- 'spinbutton',
|
|
||||||
- 'switch',
|
|
||||||
- 'textbox',
|
|
||||||
- ],
|
|
||||||
- },
|
|
||||||
- ],
|
|
||||||
- 'jsx-a11y/label-has-associated-control': 'error',
|
|
||||||
- 'jsx-a11y/label-has-for': 'off',
|
|
||||||
- 'jsx-a11y/media-has-caption': 'error',
|
|
||||||
- 'jsx-a11y/mouse-events-have-key-events': 'error',
|
|
||||||
- 'jsx-a11y/no-access-key': 'error',
|
|
||||||
- 'jsx-a11y/no-autofocus': 'error',
|
|
||||||
- 'jsx-a11y/no-distracting-elements': 'error',
|
|
||||||
- 'jsx-a11y/no-interactive-element-to-noninteractive-role': [
|
|
||||||
- 'error',
|
|
||||||
- {
|
|
||||||
- tr: ['none', 'presentation'],
|
|
||||||
- canvas: ['img'],
|
|
||||||
- },
|
|
||||||
- ],
|
|
||||||
- 'jsx-a11y/no-noninteractive-element-interactions': [
|
|
||||||
- 'error',
|
|
||||||
- {
|
|
||||||
- handlers: [
|
|
||||||
- 'onClick',
|
|
||||||
- 'onError',
|
|
||||||
- 'onLoad',
|
|
||||||
- 'onMouseDown',
|
|
||||||
- 'onMouseUp',
|
|
||||||
- 'onKeyPress',
|
|
||||||
- 'onKeyDown',
|
|
||||||
- 'onKeyUp',
|
|
||||||
- ],
|
|
||||||
- alert: ['onKeyUp', 'onKeyDown', 'onKeyPress'],
|
|
||||||
- body: ['onError', 'onLoad'],
|
|
||||||
- dialog: ['onKeyUp', 'onKeyDown', 'onKeyPress'],
|
|
||||||
- iframe: ['onError', 'onLoad'],
|
|
||||||
- img: ['onError', 'onLoad'],
|
|
||||||
- },
|
|
||||||
- ],
|
|
||||||
- 'jsx-a11y/no-noninteractive-element-to-interactive-role': [
|
|
||||||
- 'error',
|
|
||||||
- {
|
|
||||||
- ul: [
|
|
||||||
- 'listbox',
|
|
||||||
- 'menu',
|
|
||||||
- 'menubar',
|
|
||||||
- 'radiogroup',
|
|
||||||
- 'tablist',
|
|
||||||
- 'tree',
|
|
||||||
- 'treegrid',
|
|
||||||
- ],
|
|
||||||
- ol: [
|
|
||||||
- 'listbox',
|
|
||||||
- 'menu',
|
|
||||||
- 'menubar',
|
|
||||||
- 'radiogroup',
|
|
||||||
- 'tablist',
|
|
||||||
- 'tree',
|
|
||||||
- 'treegrid',
|
|
||||||
- ],
|
|
||||||
- li: ['menuitem', 'option', 'row', 'tab', 'treeitem'],
|
|
||||||
- table: ['grid'],
|
|
||||||
- td: ['gridcell'],
|
|
||||||
- fieldset: ['radiogroup', 'presentation'],
|
|
||||||
- },
|
|
||||||
- ],
|
|
||||||
- 'jsx-a11y/no-noninteractive-tabindex': [
|
|
||||||
- 'error',
|
|
||||||
- {
|
|
||||||
- tags: [],
|
|
||||||
- roles: ['tabpanel'],
|
|
||||||
- allowExpressionValues: true,
|
|
||||||
- },
|
|
||||||
- ],
|
|
||||||
- 'jsx-a11y/no-redundant-roles': 'error',
|
|
||||||
- 'jsx-a11y/no-static-element-interactions': [
|
|
||||||
- 'error',
|
|
||||||
- {
|
|
||||||
- allowExpressionValues: true,
|
|
||||||
- handlers: [
|
|
||||||
- 'onClick',
|
|
||||||
- 'onMouseDown',
|
|
||||||
- 'onMouseUp',
|
|
||||||
- 'onKeyPress',
|
|
||||||
- 'onKeyDown',
|
|
||||||
- 'onKeyUp',
|
|
||||||
- ],
|
|
||||||
- },
|
|
||||||
- ],
|
|
||||||
- 'jsx-a11y/role-has-required-aria-props': 'error',
|
|
||||||
- 'jsx-a11y/role-supports-aria-props': 'error',
|
|
||||||
- 'jsx-a11y/scope': 'error',
|
|
||||||
- 'jsx-a11y/tabindex-no-positive': 'error',
|
|
||||||
- },
|
|
||||||
- },
|
|
||||||
- strict: {
|
|
||||||
- plugins: [
|
|
||||||
- 'jsx-a11y',
|
|
||||||
- ],
|
|
||||||
- parserOptions: {
|
|
||||||
- ecmaFeatures: {
|
|
||||||
- jsx: true,
|
|
||||||
- },
|
|
||||||
- },
|
|
||||||
- rules: {
|
|
||||||
- 'jsx-a11y/alt-text': 'error',
|
|
||||||
- 'jsx-a11y/anchor-has-content': 'error',
|
|
||||||
- 'jsx-a11y/anchor-is-valid': 'error',
|
|
||||||
- 'jsx-a11y/aria-activedescendant-has-tabindex': 'error',
|
|
||||||
- 'jsx-a11y/aria-props': 'error',
|
|
||||||
- 'jsx-a11y/aria-proptypes': 'error',
|
|
||||||
- 'jsx-a11y/aria-role': 'error',
|
|
||||||
- 'jsx-a11y/aria-unsupported-elements': 'error',
|
|
||||||
- 'jsx-a11y/autocomplete-valid': 'error',
|
|
||||||
- 'jsx-a11y/click-events-have-key-events': 'error',
|
|
||||||
- 'jsx-a11y/control-has-associated-label': ['off', {
|
|
||||||
- ignoreElements: [
|
|
||||||
- 'audio',
|
|
||||||
- 'canvas',
|
|
||||||
- 'embed',
|
|
||||||
- 'input',
|
|
||||||
- 'textarea',
|
|
||||||
- 'tr',
|
|
||||||
- 'video',
|
|
||||||
- ],
|
|
||||||
- ignoreRoles: [
|
|
||||||
- 'grid',
|
|
||||||
+ ol: [
|
|
||||||
'listbox',
|
|
||||||
'menu',
|
|
||||||
'menubar',
|
|
||||||
'radiogroup',
|
|
||||||
- 'row',
|
|
||||||
'tablist',
|
|
||||||
- 'toolbar',
|
|
||||||
'tree',
|
|
||||||
'treegrid',
|
|
||||||
],
|
|
||||||
- includeRoles: [
|
|
||||||
- 'alert',
|
|
||||||
- 'dialog',
|
|
||||||
+ li: ['menuitem', 'option', 'row', 'tab', 'treeitem'],
|
|
||||||
+ table: ['grid'],
|
|
||||||
+ td: ['gridcell'],
|
|
||||||
+ fieldset: ['radiogroup', 'presentation'],
|
|
||||||
+ },
|
|
||||||
+ ],
|
|
||||||
+ 'jsx-a11y/no-noninteractive-tabindex': [
|
|
||||||
+ 'error',
|
|
||||||
+ {
|
|
||||||
+ tags: [],
|
|
||||||
+ roles: ['tabpanel'],
|
|
||||||
+ allowExpressionValues: true,
|
|
||||||
+ },
|
|
||||||
+ ],
|
|
||||||
+ 'jsx-a11y/no-redundant-roles': 'error',
|
|
||||||
+ 'jsx-a11y/no-static-element-interactions': [
|
|
||||||
+ 'error',
|
|
||||||
+ {
|
|
||||||
+ allowExpressionValues: true,
|
|
||||||
+ handlers: [
|
|
||||||
+ 'onClick',
|
|
||||||
+ 'onMouseDown',
|
|
||||||
+ 'onMouseUp',
|
|
||||||
+ 'onKeyPress',
|
|
||||||
+ 'onKeyDown',
|
|
||||||
+ 'onKeyUp',
|
|
||||||
],
|
|
||||||
- }],
|
|
||||||
- 'jsx-a11y/heading-has-content': 'error',
|
|
||||||
- 'jsx-a11y/html-has-lang': 'error',
|
|
||||||
- 'jsx-a11y/iframe-has-title': 'error',
|
|
||||||
- 'jsx-a11y/img-redundant-alt': 'error',
|
|
||||||
- 'jsx-a11y/interactive-supports-focus': [
|
|
||||||
- 'error',
|
|
||||||
- {
|
|
||||||
- tabbable: [
|
|
||||||
- 'button',
|
|
||||||
- 'checkbox',
|
|
||||||
- 'link',
|
|
||||||
- 'progressbar',
|
|
||||||
- 'searchbox',
|
|
||||||
- 'slider',
|
|
||||||
- 'spinbutton',
|
|
||||||
- 'switch',
|
|
||||||
- 'textbox',
|
|
||||||
- ],
|
|
||||||
- },
|
|
||||||
+ },
|
|
||||||
+ ],
|
|
||||||
+ 'jsx-a11y/role-has-required-aria-props': 'error',
|
|
||||||
+ 'jsx-a11y/role-supports-aria-props': 'error',
|
|
||||||
+ 'jsx-a11y/scope': 'error',
|
|
||||||
+ 'jsx-a11y/tabindex-no-positive': 'error',
|
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
+ strict: {
|
|
||||||
+ plugins: [
|
|
||||||
+ 'jsx-a11y',
|
|
||||||
+ ],
|
|
||||||
+ parserOptions: {
|
|
||||||
+ ecmaFeatures: {
|
|
||||||
+ jsx: true,
|
|
||||||
+ },
|
|
||||||
+ },
|
|
||||||
+ rules: {
|
|
||||||
+ 'jsx-a11y/alt-text': 'error',
|
|
||||||
+ 'jsx-a11y/anchor-has-content': 'error',
|
|
||||||
+ 'jsx-a11y/anchor-is-valid': 'error',
|
|
||||||
+ 'jsx-a11y/aria-activedescendant-has-tabindex': 'error',
|
|
||||||
+ 'jsx-a11y/aria-props': 'error',
|
|
||||||
+ 'jsx-a11y/aria-proptypes': 'error',
|
|
||||||
+ 'jsx-a11y/aria-role': 'error',
|
|
||||||
+ 'jsx-a11y/aria-unsupported-elements': 'error',
|
|
||||||
+ 'jsx-a11y/autocomplete-valid': 'error',
|
|
||||||
+ 'jsx-a11y/click-events-have-key-events': 'error',
|
|
||||||
+ 'jsx-a11y/control-has-associated-label': ['off', {
|
|
||||||
+ ignoreElements: [
|
|
||||||
+ 'audio',
|
|
||||||
+ 'canvas',
|
|
||||||
+ 'embed',
|
|
||||||
+ 'input',
|
|
||||||
+ 'textarea',
|
|
||||||
+ 'tr',
|
|
||||||
+ 'video',
|
|
||||||
],
|
|
||||||
- 'jsx-a11y/label-has-for': 'off',
|
|
||||||
- 'jsx-a11y/label-has-associated-control': 'error',
|
|
||||||
- 'jsx-a11y/media-has-caption': 'error',
|
|
||||||
- 'jsx-a11y/mouse-events-have-key-events': 'error',
|
|
||||||
- 'jsx-a11y/no-access-key': 'error',
|
|
||||||
- 'jsx-a11y/no-autofocus': 'error',
|
|
||||||
- 'jsx-a11y/no-distracting-elements': 'error',
|
|
||||||
- 'jsx-a11y/no-interactive-element-to-noninteractive-role': 'error',
|
|
||||||
- 'jsx-a11y/no-noninteractive-element-interactions': [
|
|
||||||
- 'error',
|
|
||||||
- {
|
|
||||||
- body: ['onError', 'onLoad'],
|
|
||||||
- iframe: ['onError', 'onLoad'],
|
|
||||||
- img: ['onError', 'onLoad'],
|
|
||||||
- },
|
|
||||||
+ ignoreRoles: [
|
|
||||||
+ 'grid',
|
|
||||||
+ 'listbox',
|
|
||||||
+ 'menu',
|
|
||||||
+ 'menubar',
|
|
||||||
+ 'radiogroup',
|
|
||||||
+ 'row',
|
|
||||||
+ 'tablist',
|
|
||||||
+ 'toolbar',
|
|
||||||
+ 'tree',
|
|
||||||
+ 'treegrid',
|
|
||||||
],
|
|
||||||
- 'jsx-a11y/no-noninteractive-element-to-interactive-role': 'error',
|
|
||||||
- 'jsx-a11y/no-noninteractive-tabindex': 'error',
|
|
||||||
- 'jsx-a11y/no-redundant-roles': 'error',
|
|
||||||
- 'jsx-a11y/no-static-element-interactions': 'error',
|
|
||||||
- 'jsx-a11y/role-has-required-aria-props': 'error',
|
|
||||||
- 'jsx-a11y/role-supports-aria-props': 'error',
|
|
||||||
- 'jsx-a11y/scope': 'error',
|
|
||||||
- 'jsx-a11y/tabindex-no-positive': 'error',
|
|
||||||
- },
|
|
||||||
+ includeRoles: [
|
|
||||||
+ 'alert',
|
|
||||||
+ 'dialog',
|
|
||||||
+ ],
|
|
||||||
+ }],
|
|
||||||
+ 'jsx-a11y/heading-has-content': 'error',
|
|
||||||
+ 'jsx-a11y/html-has-lang': 'error',
|
|
||||||
+ 'jsx-a11y/iframe-has-title': 'error',
|
|
||||||
+ 'jsx-a11y/img-redundant-alt': 'error',
|
|
||||||
+ 'jsx-a11y/interactive-supports-focus': [
|
|
||||||
+ 'error',
|
|
||||||
+ {
|
|
||||||
+ tabbable: [
|
|
||||||
+ 'button',
|
|
||||||
+ 'checkbox',
|
|
||||||
+ 'link',
|
|
||||||
+ 'progressbar',
|
|
||||||
+ 'searchbox',
|
|
||||||
+ 'slider',
|
|
||||||
+ 'spinbutton',
|
|
||||||
+ 'switch',
|
|
||||||
+ 'textbox',
|
|
||||||
+ ],
|
|
||||||
+ },
|
|
||||||
+ ],
|
|
||||||
+ 'jsx-a11y/label-has-for': 'off',
|
|
||||||
+ 'jsx-a11y/label-has-associated-control': 'error',
|
|
||||||
+ 'jsx-a11y/media-has-caption': 'error',
|
|
||||||
+ 'jsx-a11y/mouse-events-have-key-events': 'error',
|
|
||||||
+ 'jsx-a11y/no-access-key': 'error',
|
|
||||||
+ 'jsx-a11y/no-autofocus': 'error',
|
|
||||||
+ 'jsx-a11y/no-distracting-elements': 'error',
|
|
||||||
+ 'jsx-a11y/no-interactive-element-to-noninteractive-role': 'error',
|
|
||||||
+ 'jsx-a11y/no-noninteractive-element-interactions': [
|
|
||||||
+ 'error',
|
|
||||||
+ {
|
|
||||||
+ body: ['onError', 'onLoad'],
|
|
||||||
+ iframe: ['onError', 'onLoad'],
|
|
||||||
+ img: ['onError', 'onLoad'],
|
|
||||||
+ },
|
|
||||||
+ ],
|
|
||||||
+ 'jsx-a11y/no-noninteractive-element-to-interactive-role': 'error',
|
|
||||||
+ 'jsx-a11y/no-noninteractive-tabindex': 'error',
|
|
||||||
+ 'jsx-a11y/no-redundant-roles': 'error',
|
|
||||||
+ 'jsx-a11y/no-static-element-interactions': 'error',
|
|
||||||
+ 'jsx-a11y/role-has-required-aria-props': 'error',
|
|
||||||
+ 'jsx-a11y/role-supports-aria-props': 'error',
|
|
||||||
+ 'jsx-a11y/scope': 'error',
|
|
||||||
+ 'jsx-a11y/tabindex-no-positive': 'error',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
+
|
|
||||||
+/** @param {object} obj */
|
const recommendedRules = {
|
||||||
+function kebabCase(obj) {
|
@@ -294,15 +336,15 @@ const jsxA11y = {
|
||||||
+ return Object.fromEntries(
|
* Given a ruleset and optionally a flat config name, generate a config.
|
||||||
+ Object.entries(obj).map(([key, value]) => [
|
* @param {object} rules - ruleset for this config
|
||||||
+ key.replace(/([A-Z])/g, '-$1').toLowerCase(),
|
* @param {string} flatConfigName - name for the config if flat
|
||||||
+ value,
|
- * @returns Config for this set of rules.
|
||||||
+ ]),
|
+ * @returns {import('eslint').Linter.Config} Config for this set of rules.
|
||||||
+ );
|
*/
|
||||||
+}
|
const createConfig = (rules, flatConfigName) => ({
|
||||||
|
...(flatConfigName
|
||||||
|
? {
|
||||||
|
- ...flatConfigBase,
|
||||||
|
- name: `jsx-a11y/${flatConfigName}`,
|
||||||
|
- plugins: { 'jsx-a11y': jsxA11y },
|
||||||
|
- }
|
||||||
|
+ ...flatConfigBase,
|
||||||
|
+ name: `jsx-a11y/${flatConfigName}`,
|
||||||
|
+ plugins: { 'jsx-a11y': jsxA11y },
|
||||||
|
+ }
|
||||||
|
: { ...legacyConfigBase, plugins: ['jsx-a11y'] }),
|
||||||
|
rules: { ...rules },
|
||||||
|
});
|
||||||
|
@@ -317,4 +359,4 @@ const flatConfigs = {
|
||||||
|
strict: createConfig(strictRules, 'strict'),
|
||||||
|
};
|
||||||
|
|
||||||
|
-module.exports = { ...jsxA11y, configs, flatConfigs };
|
||||||
|
+export default { ...jsxA11y, configs, flatConfigs };
|
||||||
|
diff --git a/src/rules/autocomplete-valid.js b/src/rules/autocomplete-valid.js
|
||||||
|
index df7b6b8..c4d0da1 100644
|
||||||
|
--- a/src/rules/autocomplete-valid.js
|
||||||
|
+++ b/src/rules/autocomplete-valid.js
|
||||||
|
@@ -6,7 +6,7 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Rule Definition
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
-import { runVirtualRule } from 'axe-core';
|
||||||
|
+import axe from 'axe-core';
|
||||||
|
import { getLiteralPropValue, getProp } from 'jsx-ast-utils';
|
||||||
|
import { generateObjSchema, arraySchema } from '../util/schemas';
|
||||||
|
import getElementType from '../util/getElementType';
|
||||||
|
@@ -24,23 +24,25 @@ export default {
|
||||||
|
schema: [schema],
|
||||||
|
},
|
||||||
|
|
||||||
|
- create: (context) => {
|
||||||
|
+ create: context => {
|
||||||
|
const elementType = getElementType(context);
|
||||||
|
return {
|
||||||
|
- JSXOpeningElement: (node) => {
|
||||||
|
+ JSXOpeningElement: node => {
|
||||||
|
const options = context.options[0] || {};
|
||||||
|
const { inputComponents = [] } = options;
|
||||||
|
const inputTypes = ['input'].concat(inputComponents);
|
||||||
|
|
||||||
|
const elType = elementType(node);
|
||||||
|
- const autocomplete = getLiteralPropValue(getProp(node.attributes, 'autocomplete'));
|
||||||
|
+ const autocomplete = getLiteralPropValue(
|
||||||
|
+ getProp(node.attributes, 'autocomplete'),
|
||||||
|
+ );
|
||||||
|
|
||||||
|
if (typeof autocomplete !== 'string' || !inputTypes.includes(elType)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = getLiteralPropValue(getProp(node.attributes, 'type'));
|
||||||
|
- const { violations } = runVirtualRule('autocomplete-valid', {
|
||||||
|
+ const { violations } = axe.runVirtualRule('autocomplete-valid', {
|
||||||
|
nodeName: 'input',
|
||||||
|
attributes: {
|
||||||
|
autocomplete,
|
||||||
diff --git a/src/util/mayContainChildComponent.js b/src/util/mayContainChildComponent.js
|
diff --git a/src/util/mayContainChildComponent.js b/src/util/mayContainChildComponent.js
|
||||||
index 43a03ef..5e1035e 100644
|
index 43a03ef..5e1035e 100644
|
||||||
--- a/src/util/mayContainChildComponent.js
|
--- a/src/util/mayContainChildComponent.js
|
||||||
@ -629,3 +218,16 @@ index 43a03ef..5e1035e 100644
|
|||||||
|
|
||||||
export default function mayContainChildComponent(
|
export default function mayContainChildComponent(
|
||||||
root: Node,
|
root: Node,
|
||||||
|
diff --git a/src/util/mayHaveAccessibleLabel.js b/src/util/mayHaveAccessibleLabel.js
|
||||||
|
index 186ef5e..3dd7d4d 100644
|
||||||
|
--- a/src/util/mayHaveAccessibleLabel.js
|
||||||
|
+++ b/src/util/mayHaveAccessibleLabel.js
|
||||||
|
@@ -11,7 +11,7 @@
|
||||||
|
import includes from 'array-includes';
|
||||||
|
import { getPropValue, propName, elementType as rawElementType } from 'jsx-ast-utils';
|
||||||
|
import type { JSXOpeningElement, Node } from 'ast-types-flow';
|
||||||
|
-import minimatch from 'minimatch';
|
||||||
|
+import { minimatch } from 'minimatch';
|
||||||
|
|
||||||
|
function tryTrim(value: any) {
|
||||||
|
return typeof value === 'string' ? value.trim() : value;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
diff --git a/lib/index.js b/lib/index.js
|
diff --git a/lib/index.js b/lib/index.js
|
||||||
index def1bbf..6fdff14 100644
|
index 49fd4c7..a0fdd81 100644
|
||||||
--- a/lib/index.js
|
--- a/lib/index.js
|
||||||
+++ b/lib/index.js
|
+++ b/lib/index.js
|
||||||
@@ -1,9 +1,9 @@
|
@@ -1,9 +1,9 @@
|
||||||
@ -14,19 +14,19 @@ index def1bbf..6fdff14 100644
|
|||||||
+import cjsConfig from "./configs/recommended-script"
|
+import cjsConfig from "./configs/recommended-script"
|
||||||
+import recommendedConfig from "./configs/recommended"
|
+import recommendedConfig from "./configs/recommended"
|
||||||
|
|
||||||
const rules = {
|
/**
|
||||||
"callback-return": require("./rules/callback-return"),
|
* @typedef {{
|
||||||
@@ -51,8 +51,8 @@ const rules = {
|
@@ -20,8 +20,8 @@ const recommendedConfig = require("./configs/recommended")
|
||||||
|
/** @type {import('eslint').ESLint.Plugin & { configs: Configs }} */
|
||||||
const mod = {
|
const plugin = {
|
||||||
meta: {
|
meta: {
|
||||||
- name: pkg.name,
|
- name: pkg.name,
|
||||||
- version: pkg.version,
|
- version: pkg.version,
|
||||||
+ name,
|
+ name,
|
||||||
+ version,
|
+ version,
|
||||||
},
|
},
|
||||||
rules,
|
rules: /** @type {Record<string, import('eslint').Rule.RuleModule>} */ ({
|
||||||
}
|
"callback-return": require("./rules/callback-return"),
|
||||||
diff --git a/tests/fixtures/no-extraneous/dependencies/node_modules/@bbb/aaa.js b/tests/fixtures/no-extraneous/dependencies/node_modules/@bbb/aaa.js
|
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
|
deleted file mode 100644
|
||||||
index e69de29..0000000
|
index e69de29..0000000
|
||||||
|
@ -1,369 +0,0 @@
|
|||||||
diff --git a/.eslintrc b/.eslintrc
|
|
||||||
deleted file mode 100644
|
|
||||||
index 4991f200..00000000
|
|
||||||
--- a/.eslintrc
|
|
||||||
+++ /dev/null
|
|
||||||
@@ -1,82 +0,0 @@
|
|
||||||
-{
|
|
||||||
- "root": true,
|
|
||||||
- "extends": ["airbnb-base", "plugin:eslint-plugin/recommended"],
|
|
||||||
- "plugins": ["eslint-plugin"],
|
|
||||||
- "env": {
|
|
||||||
- "es6": true,
|
|
||||||
- "node": true
|
|
||||||
- },
|
|
||||||
- "parserOptions": {
|
|
||||||
- "ecmaVersion": 6,
|
|
||||||
- "ecmaFeatures": {
|
|
||||||
- "jsx": true
|
|
||||||
- },
|
|
||||||
- "sourceType": "script",
|
|
||||||
- },
|
|
||||||
- "ignorePatterns": [
|
|
||||||
- "coverage/",
|
|
||||||
- ".nyc_output/",
|
|
||||||
- ],
|
|
||||||
- "rules": {
|
|
||||||
- "comma-dangle": [2, "always-multiline"],
|
|
||||||
- "object-shorthand": [2, "always", {
|
|
||||||
- "ignoreConstructors": false,
|
|
||||||
- "avoidQuotes": false, // this is the override vs airbnb
|
|
||||||
- }],
|
|
||||||
- "max-len": [2, 120, {
|
|
||||||
- "ignoreStrings": true,
|
|
||||||
- "ignoreTemplateLiterals": true,
|
|
||||||
- "ignoreComments": true,
|
|
||||||
- }],
|
|
||||||
- "consistent-return": 0,
|
|
||||||
-
|
|
||||||
- "prefer-destructuring": [2, { "array": false, "object": false }, { "enforceForRenamedProperties": false }],
|
|
||||||
- "prefer-object-spread": 0, // until node 8 is required
|
|
||||||
- "prefer-rest-params": 0, // until node 6 is required
|
|
||||||
- "prefer-spread": 0, // until node 6 is required
|
|
||||||
- "function-call-argument-newline": 1, // TODO: enable
|
|
||||||
- "function-paren-newline": 0,
|
|
||||||
- "no-plusplus": [2, {"allowForLoopAfterthoughts": true}],
|
|
||||||
- "no-param-reassign": 1,
|
|
||||||
- "no-restricted-syntax": [2, {
|
|
||||||
- "selector": "ObjectPattern",
|
|
||||||
- "message": "Object destructuring is not compatible with Node v4"
|
|
||||||
- }],
|
|
||||||
- "strict": [2, "safe"],
|
|
||||||
- "valid-jsdoc": [2, {
|
|
||||||
- "requireReturn": false,
|
|
||||||
- "requireParamDescription": false,
|
|
||||||
- "requireReturnDescription": false,
|
|
||||||
- }],
|
|
||||||
-
|
|
||||||
- "eslint-plugin/consistent-output": 0,
|
|
||||||
- "eslint-plugin/require-meta-docs-description": [2, { "pattern": "^(Enforce|Require|Disallow)" }],
|
|
||||||
- "eslint-plugin/require-meta-schema": 0,
|
|
||||||
- "eslint-plugin/require-meta-type": 0
|
|
||||||
- },
|
|
||||||
- "overrides": [
|
|
||||||
- {
|
|
||||||
- "files": "tests/**",
|
|
||||||
- "rules": {
|
|
||||||
- "no-template-curly-in-string": 1,
|
|
||||||
- },
|
|
||||||
- },
|
|
||||||
- {
|
|
||||||
- "files": "markdown.config.js",
|
|
||||||
- "rules": {
|
|
||||||
- "no-console": 0,
|
|
||||||
- },
|
|
||||||
- },
|
|
||||||
- {
|
|
||||||
- "files": ".github/workflows/*.js",
|
|
||||||
- "parserOptions": {
|
|
||||||
- "ecmaVersion": 2019,
|
|
||||||
- },
|
|
||||||
- "rules": {
|
|
||||||
- "camelcase": 0,
|
|
||||||
- "no-console": 0,
|
|
||||||
- "no-restricted-syntax": 0,
|
|
||||||
- },
|
|
||||||
- },
|
|
||||||
- ],
|
|
||||||
-}
|
|
||||||
diff --git a/index.js b/index.js
|
|
||||||
index 4140c6c8..03e623af 100644
|
|
||||||
--- a/index.js
|
|
||||||
+++ b/index.js
|
|
||||||
@@ -1,31 +1,25 @@
|
|
||||||
-'use strict';
|
|
||||||
-
|
|
||||||
-const configAll = require('./configs/all');
|
|
||||||
-const configRecommended = require('./configs/recommended');
|
|
||||||
-const configRuntime = require('./configs/jsx-runtime');
|
|
||||||
-
|
|
||||||
-const allRules = require('./lib/rules');
|
|
||||||
+import configAll from './configs/all';
|
|
||||||
+import configRecommended from './configs/recommended';
|
|
||||||
+import configRuntime from './configs/jsx-runtime';
|
|
||||||
+import { name } from './package.json';
|
|
||||||
+export { default as rules } from './lib/rules';
|
|
||||||
|
|
||||||
// for legacy config system
|
|
||||||
-const plugins = [
|
|
||||||
- 'react',
|
|
||||||
-];
|
|
||||||
+const plugins = [name];
|
|
||||||
+
|
|
||||||
+export const deprecatedRules = configAll.plugins.react.deprecatedRules;
|
|
||||||
|
|
||||||
-module.exports = {
|
|
||||||
- deprecatedRules: configAll.plugins.react.deprecatedRules,
|
|
||||||
- rules: allRules,
|
|
||||||
- configs: {
|
|
||||||
- recommended: Object.assign({}, configRecommended, {
|
|
||||||
- parserOptions: configRecommended.languageOptions.parserOptions,
|
|
||||||
- plugins,
|
|
||||||
- }),
|
|
||||||
- all: Object.assign({}, configAll, {
|
|
||||||
- parserOptions: configAll.languageOptions.parserOptions,
|
|
||||||
- plugins,
|
|
||||||
- }),
|
|
||||||
- 'jsx-runtime': Object.assign({}, configRuntime, {
|
|
||||||
- parserOptions: configRuntime.languageOptions.parserOptions,
|
|
||||||
- plugins,
|
|
||||||
- }),
|
|
||||||
- },
|
|
||||||
+export const configs = {
|
|
||||||
+ recommended: Object.assign({}, configRecommended, {
|
|
||||||
+ parserOptions: configRecommended.languageOptions.parserOptions,
|
|
||||||
+ plugins,
|
|
||||||
+ }),
|
|
||||||
+ all: Object.assign({}, configAll, {
|
|
||||||
+ parserOptions: configAll.languageOptions.parserOptions,
|
|
||||||
+ plugins,
|
|
||||||
+ }),
|
|
||||||
+ 'jsx-runtime': Object.assign({}, configRuntime, {
|
|
||||||
+ parserOptions: configRuntime.languageOptions.parserOptions,
|
|
||||||
+ plugins,
|
|
||||||
+ }),
|
|
||||||
};
|
|
||||||
diff --git a/lib/rules/button-has-type.js b/lib/rules/button-has-type.js
|
|
||||||
index 204a33c4..01d992c2 100644
|
|
||||||
--- a/lib/rules/button-has-type.js
|
|
||||||
+++ b/lib/rules/button-has-type.js
|
|
||||||
@@ -5,8 +5,7 @@
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
-const getProp = require('jsx-ast-utils/getProp');
|
|
||||||
-const getLiteralPropValue = require('jsx-ast-utils/getLiteralPropValue');
|
|
||||||
+const { getProp, getLiteralPropValue } = require('jsx-ast-utils');
|
|
||||||
const docsUrl = require('../util/docsUrl');
|
|
||||||
const isCreateElement = require('../util/isCreateElement');
|
|
||||||
const report = require('../util/report');
|
|
||||||
diff --git a/lib/rules/jsx-fragments.js b/lib/rules/jsx-fragments.js
|
|
||||||
index 38b4dd8b..d0575572 100644
|
|
||||||
--- a/lib/rules/jsx-fragments.js
|
|
||||||
+++ b/lib/rules/jsx-fragments.js
|
|
||||||
@@ -5,7 +5,7 @@
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
-const elementType = require('jsx-ast-utils/elementType');
|
|
||||||
+import { elementType } from 'jsx-ast-utils';
|
|
||||||
const pragmaUtil = require('../util/pragma');
|
|
||||||
const variableUtil = require('../util/variable');
|
|
||||||
const testReactVersion = require('../util/version').testReactVersion;
|
|
||||||
diff --git a/lib/rules/jsx-key.js b/lib/rules/jsx-key.js
|
|
||||||
index c13a00cf..fb457e63 100644
|
|
||||||
--- a/lib/rules/jsx-key.js
|
|
||||||
+++ b/lib/rules/jsx-key.js
|
|
||||||
@@ -5,8 +5,7 @@
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
-const hasProp = require('jsx-ast-utils/hasProp');
|
|
||||||
-const propName = require('jsx-ast-utils/propName');
|
|
||||||
+import { hasProp, propName } from 'jsx-ast-utils';
|
|
||||||
const values = require('object.values');
|
|
||||||
const docsUrl = require('../util/docsUrl');
|
|
||||||
const pragmaUtil = require('../util/pragma');
|
|
||||||
diff --git a/lib/rules/jsx-no-bind.js b/lib/rules/jsx-no-bind.js
|
|
||||||
index 0a4c38e6..75a57bd9 100644
|
|
||||||
--- a/lib/rules/jsx-no-bind.js
|
|
||||||
+++ b/lib/rules/jsx-no-bind.js
|
|
||||||
@@ -7,7 +7,7 @@
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
-const propName = require('jsx-ast-utils/propName');
|
|
||||||
+import { propName } from 'jsx-ast-utils';
|
|
||||||
const docsUrl = require('../util/docsUrl');
|
|
||||||
const jsxUtil = require('../util/jsx');
|
|
||||||
const report = require('../util/report');
|
|
||||||
diff --git a/lib/rules/jsx-pascal-case.js b/lib/rules/jsx-pascal-case.js
|
|
||||||
index a1bb4811..db051356 100644
|
|
||||||
--- a/lib/rules/jsx-pascal-case.js
|
|
||||||
+++ b/lib/rules/jsx-pascal-case.js
|
|
||||||
@@ -5,7 +5,7 @@
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
-const elementType = require('jsx-ast-utils/elementType');
|
|
||||||
+import { elementType } from 'jsx-ast-utils';
|
|
||||||
const minimatch = require('minimatch');
|
|
||||||
const docsUrl = require('../util/docsUrl');
|
|
||||||
const jsxUtil = require('../util/jsx');
|
|
||||||
diff --git a/lib/rules/jsx-sort-props.js b/lib/rules/jsx-sort-props.js
|
|
||||||
index 6d19f201..4b1849cc 100644
|
|
||||||
--- a/lib/rules/jsx-sort-props.js
|
|
||||||
+++ b/lib/rules/jsx-sort-props.js
|
|
||||||
@@ -5,7 +5,7 @@
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
-const propName = require('jsx-ast-utils/propName');
|
|
||||||
+import { propName } from 'jsx-ast-utils';
|
|
||||||
const includes = require('array-includes');
|
|
||||||
const toSorted = require('array.prototype.tosorted');
|
|
||||||
|
|
||||||
diff --git a/lib/rules/no-namespace.js b/lib/rules/no-namespace.js
|
|
||||||
index 64bbc8d5..b5e9c803 100644
|
|
||||||
--- a/lib/rules/no-namespace.js
|
|
||||||
+++ b/lib/rules/no-namespace.js
|
|
||||||
@@ -5,7 +5,7 @@
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
-const elementType = require('jsx-ast-utils/elementType');
|
|
||||||
+import { elementType } from 'jsx-ast-utils';
|
|
||||||
const docsUrl = require('../util/docsUrl');
|
|
||||||
const isCreateElement = require('../util/isCreateElement');
|
|
||||||
const report = require('../util/report');
|
|
||||||
diff --git a/lib/rules/no-unknown-property.js b/lib/rules/no-unknown-property.js
|
|
||||||
index fc573366..74bc36f6 100644
|
|
||||||
--- a/lib/rules/no-unknown-property.js
|
|
||||||
+++ b/lib/rules/no-unknown-property.js
|
|
||||||
@@ -516,6 +516,13 @@ module.exports = {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
+ extends: {
|
|
||||||
+ type: 'array',
|
|
||||||
+ items: {
|
|
||||||
+ type: 'string',
|
|
||||||
+ enum: ['next', 'emotion'],
|
|
||||||
+ },
|
|
||||||
+ }
|
|
||||||
},
|
|
||||||
additionalProperties: false,
|
|
||||||
}],
|
|
||||||
@@ -523,14 +530,18 @@ module.exports = {
|
|
||||||
|
|
||||||
create(context) {
|
|
||||||
function getIgnoreConfig() {
|
|
||||||
- return (context.options[0] && context.options[0].ignore) || DEFAULTS.ignore;
|
|
||||||
+ return context.options[0]?.ignore || DEFAULTS.ignore;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ const extensions = /** @type {string[]} */ (context.options[0]?.extends || []);
|
|
||||||
+ const hasEmotion = extensions.includes('emotion');
|
|
||||||
+ const hasNext = extensions.includes('next');
|
|
||||||
+
|
|
||||||
return {
|
|
||||||
JSXAttribute(node) {
|
|
||||||
const ignoreNames = getIgnoreConfig();
|
|
||||||
const actualName = context.getSourceCode().getText(node.name);
|
|
||||||
- if (ignoreNames.indexOf(actualName) >= 0) {
|
|
||||||
+ if (ignoreNames.includes(actualName)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const name = normalizeAttributeCase(actualName);
|
|
||||||
@@ -546,6 +557,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
|
|
||||||
+++ b/lib/util/annotations.js
|
|
||||||
@@ -27,6 +27,6 @@ function isAnnotatedFunctionPropsDeclaration(node, context) {
|
|
||||||
return (isAnnotated && (isDestructuredProps || isProps));
|
|
||||||
}
|
|
||||||
|
|
||||||
-module.exports = {
|
|
||||||
+export {
|
|
||||||
isAnnotatedFunctionPropsDeclaration,
|
|
||||||
};
|
|
||||||
diff --git a/lib/util/ast.js b/lib/util/ast.js
|
|
||||||
index fd6019a3..3cbc293e 100644
|
|
||||||
--- a/lib/util/ast.js
|
|
||||||
+++ b/lib/util/ast.js
|
|
||||||
@@ -4,7 +4,7 @@
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
-const estraverse = require('estraverse');
|
|
||||||
+import estraverse from 'estraverse';
|
|
||||||
// const pragmaUtil = require('./pragma');
|
|
||||||
|
|
||||||
/**
|
|
||||||
@@ -428,7 +428,7 @@ function isTSTypeParameterInstantiation(node) {
|
|
||||||
return nodeType === 'TSTypeParameterInstantiation';
|
|
||||||
}
|
|
||||||
|
|
||||||
-module.exports = {
|
|
||||||
+export {
|
|
||||||
traverse,
|
|
||||||
findReturnStatement,
|
|
||||||
getFirstNodeInLine,
|
|
||||||
diff --git a/lib/util/jsx.js b/lib/util/jsx.js
|
|
||||||
index 55073bfe..efc07af1 100644
|
|
||||||
--- a/lib/util/jsx.js
|
|
||||||
+++ b/lib/util/jsx.js
|
|
||||||
@@ -4,7 +4,7 @@
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
-const elementType = require('jsx-ast-utils/elementType');
|
|
||||||
+import { elementType } from 'jsx-ast-utils';
|
|
||||||
|
|
||||||
const astUtil = require('./ast');
|
|
||||||
const isCreateElement = require('./isCreateElement');
|
|
||||||
diff --git a/tsconfig.json b/tsconfig.json
|
|
||||||
deleted file mode 100644
|
|
||||||
index 39187b7f..00000000
|
|
||||||
--- a/tsconfig.json
|
|
||||||
+++ /dev/null
|
|
||||||
@@ -1,23 +0,0 @@
|
|
||||||
-{
|
|
||||||
- "compilerOptions": {
|
|
||||||
- "target": "ES2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
|
||||||
- "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
|
||||||
- // "lib": ["es2015"], /* Specify library files to be included in the compilation. */
|
|
||||||
- "allowJs": true, /* Allow javascript files to be compiled. */
|
|
||||||
- "checkJs": true, /* Report errors in .js files. */
|
|
||||||
- "noEmit": true, /* Do not emit outputs. */
|
|
||||||
- "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
|
||||||
-
|
|
||||||
- /* Strict Type-Checking Options */
|
|
||||||
- // "strict": true, /* Enable all strict type-checking options. */
|
|
||||||
- "noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */
|
|
||||||
- // "strictNullChecks": true, /* Enable strict null checks. */
|
|
||||||
- // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
|
||||||
- "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
|
||||||
- "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
|
||||||
- "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
|
||||||
- "alwaysStrict": false, /* Parse in strict mode and emit "use strict" for each source file. */
|
|
||||||
- "resolveJsonModule": true
|
|
||||||
- },
|
|
||||||
- "include": ["lib"],
|
|
||||||
-}
|
|
10
patches/@typescript-eslint+utils+6.13.1.patch
Normal file
10
patches/@typescript-eslint+utils+6.13.1.patch
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
diff --git a/node_modules/@typescript-eslint/utils/dist/ts-eslint/Scope.d.ts b/node_modules/@typescript-eslint/utils/dist/ts-eslint/Scope.d.ts
|
||||||
|
index 4e57af9..29b7cf7 100644
|
||||||
|
--- a/node_modules/@typescript-eslint/utils/dist/ts-eslint/Scope.d.ts
|
||||||
|
+++ b/node_modules/@typescript-eslint/utils/dist/ts-eslint/Scope.d.ts
|
||||||
|
@@ -1,4 +1,4 @@
|
||||||
|
-import * as scopeManager from '@typescript-eslint/scope-manager';
|
||||||
|
+import * as scopeManager from '@typescript-eslint/scope-manager/dist';
|
||||||
|
declare namespace Scope {
|
||||||
|
type ScopeManager = scopeManager.ScopeManager;
|
||||||
|
type Reference = scopeManager.Reference;
|
25
patches/@typescript-eslint__typescript-estree@8.0.0.patch
Normal file
25
patches/@typescript-eslint__typescript-estree@8.0.0.patch
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
diff --git a/dist/parseSettings/createParseSettings.js b/dist/parseSettings/createParseSettings.js
|
||||||
|
index 4c8b40ae895d45bd7dfcf64c8e49e29ce48dd663..0a62880ff50b7341fa909155293cbdb77fa99c97 100644
|
||||||
|
--- a/dist/parseSettings/createParseSettings.js
|
||||||
|
+++ b/dist/parseSettings/createParseSettings.js
|
||||||
|
@@ -1,4 +1,5 @@
|
||||||
|
"use strict";
|
||||||
|
+var fs = require("node:fs");
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||||
|
@@ -89,10 +90,12 @@ function createParseSettings(code, tsestreeOptions = {}) {
|
||||||
|
tsestreeOptions.extraFileExtensions.every(ext => typeof ext === 'string')
|
||||||
|
? tsestreeOptions.extraFileExtensions
|
||||||
|
: [],
|
||||||
|
- filePath: (0, shared_1.ensureAbsolutePath)(typeof tsestreeOptions.filePath === 'string' &&
|
||||||
|
+ filePath: fs.realpathSync(
|
||||||
|
+ (0, shared_1.ensureAbsolutePath)(typeof tsestreeOptions.filePath === 'string' &&
|
||||||
|
tsestreeOptions.filePath !== '<input>'
|
||||||
|
? tsestreeOptions.filePath
|
||||||
|
- : getFileName(tsestreeOptions.jsx), tsconfigRootDir),
|
||||||
|
+ : getFileName(tsestreeOptions.jsx), tsconfigRootDir)
|
||||||
|
+ ),
|
||||||
|
jsDocParsingMode,
|
||||||
|
jsx: tsestreeOptions.jsx === true,
|
||||||
|
loc: tsestreeOptions.loc === true,
|
13
patches/@typescript-eslint__utils@8.2.0.patch
Normal file
13
patches/@typescript-eslint__utils@8.2.0.patch
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
diff --git a/dist/eslint-utils/getParserServices.js b/dist/eslint-utils/getParserServices.js
|
||||||
|
index 3b3020f601ba9cc92fdaf643ee3a8bdc44d1291a..730fccd5838b388b496a8861705e0d9883fc2fcb 100644
|
||||||
|
--- a/dist/eslint-utils/getParserServices.js
|
||||||
|
+++ b/dist/eslint-utils/getParserServices.js
|
||||||
|
@@ -24,7 +24,7 @@ function getParserServices(context, allowWithoutFullTypeInformation = false) {
|
||||||
|
// this forces the user to supply parserOptions.project
|
||||||
|
if (context.sourceCode.parserServices.program == null &&
|
||||||
|
!allowWithoutFullTypeInformation) {
|
||||||
|
- throwError(parser);
|
||||||
|
+ // throwError(parser);
|
||||||
|
}
|
||||||
|
return context.sourceCode.parserServices;
|
||||||
|
}
|
11
patches/dts-bundle-generator.patch
Normal file
11
patches/dts-bundle-generator.patch
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
diff --git a/dist/helpers/check-diagnostics-errors.js b/dist/helpers/check-diagnostics-errors.js
|
||||||
|
index 3ff0a59509fe381189764a253e6b668241e3b921..9b1eadf36278cea8dadc6cb5cfed4c4a89e91609 100644
|
||||||
|
--- a/dist/helpers/check-diagnostics-errors.js
|
||||||
|
+++ b/dist/helpers/check-diagnostics-errors.js
|
||||||
|
@@ -20,6 +20,5 @@ function checkDiagnosticsErrors(diagnostics, failMessage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(0, logger_1.errorLog)(ts.formatDiagnostics(diagnostics, formatDiagnosticsHost).trim());
|
||||||
|
- throw new Error(failMessage);
|
||||||
|
}
|
||||||
|
exports.checkDiagnosticsErrors = checkDiagnosticsErrors;
|
3
playground/.eslintrc.js
Normal file
3
playground/.eslintrc.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
const { extendConfig } = require('@aet/eslint-rules');
|
||||||
|
|
||||||
|
module.exports = extendConfig({});
|
17
playground/package.json
Normal file
17
playground/package.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "playground",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@aet/eslint-rules": "link:/Users/aet/Documents/Git/eslint/dist",
|
||||||
|
"eslint": "^8.54.0",
|
||||||
|
"typescript": "^5.3.2"
|
||||||
|
}
|
||||||
|
}
|
672
playground/pnpm-lock.yaml
generated
Normal file
672
playground/pnpm-lock.yaml
generated
Normal file
@ -0,0 +1,672 @@
|
|||||||
|
lockfileVersion: '6.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
'@aet/eslint-rules':
|
||||||
|
specifier: link:/Users/aet/Documents/Git/eslint/dist
|
||||||
|
version: link:../dist
|
||||||
|
eslint:
|
||||||
|
specifier: 8.54.0
|
||||||
|
version: 8.54.0
|
||||||
|
typescript:
|
||||||
|
specifier: ^5.3.2
|
||||||
|
version: 5.3.2
|
||||||
|
|
||||||
|
packages:
|
||||||
|
|
||||||
|
/@aashutoshrathi/word-wrap@1.2.6:
|
||||||
|
resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@eslint-community/eslint-utils@4.4.0(eslint@8.54.0):
|
||||||
|
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
|
||||||
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
|
||||||
|
dependencies:
|
||||||
|
eslint: 8.54.0
|
||||||
|
eslint-visitor-keys: 3.4.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@eslint-community/regexpp@4.10.0:
|
||||||
|
resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==}
|
||||||
|
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@eslint/eslintrc@2.1.3:
|
||||||
|
resolution: {integrity: sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==}
|
||||||
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
|
dependencies:
|
||||||
|
ajv: 6.12.6
|
||||||
|
debug: 4.3.4
|
||||||
|
espree: 9.6.1
|
||||||
|
globals: 13.23.0
|
||||||
|
ignore: 5.3.0
|
||||||
|
import-fresh: 3.3.0
|
||||||
|
js-yaml: 4.1.0
|
||||||
|
minimatch: 3.1.2
|
||||||
|
strip-json-comments: 3.1.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@eslint/js@8.54.0:
|
||||||
|
resolution: {integrity: sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==}
|
||||||
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@humanwhocodes/config-array@0.11.13:
|
||||||
|
resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==}
|
||||||
|
engines: {node: '>=10.10.0'}
|
||||||
|
dependencies:
|
||||||
|
'@humanwhocodes/object-schema': 2.0.1
|
||||||
|
debug: 4.3.4
|
||||||
|
minimatch: 3.1.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@humanwhocodes/module-importer@1.0.1:
|
||||||
|
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
|
||||||
|
engines: {node: '>=12.22'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@humanwhocodes/object-schema@2.0.1:
|
||||||
|
resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@nodelib/fs.scandir@2.1.5:
|
||||||
|
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
dependencies:
|
||||||
|
'@nodelib/fs.stat': 2.0.5
|
||||||
|
run-parallel: 1.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@nodelib/fs.stat@2.0.5:
|
||||||
|
resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@nodelib/fs.walk@1.2.8:
|
||||||
|
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
dependencies:
|
||||||
|
'@nodelib/fs.scandir': 2.1.5
|
||||||
|
fastq: 1.15.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@ungap/structured-clone@1.2.0:
|
||||||
|
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/acorn-jsx@5.3.2(acorn@8.11.2):
|
||||||
|
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
|
||||||
|
peerDependencies:
|
||||||
|
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
|
||||||
|
dependencies:
|
||||||
|
acorn: 8.11.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/acorn@8.11.2:
|
||||||
|
resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==}
|
||||||
|
engines: {node: '>=0.4.0'}
|
||||||
|
hasBin: true
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/ajv@6.12.6:
|
||||||
|
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
||||||
|
dependencies:
|
||||||
|
fast-deep-equal: 3.1.3
|
||||||
|
fast-json-stable-stringify: 2.1.0
|
||||||
|
json-schema-traverse: 0.4.1
|
||||||
|
uri-js: 4.4.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/ansi-regex@5.0.1:
|
||||||
|
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/ansi-styles@4.3.0:
|
||||||
|
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
color-convert: 2.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/argparse@2.0.1:
|
||||||
|
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/balanced-match@1.0.2:
|
||||||
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/brace-expansion@1.1.11:
|
||||||
|
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
||||||
|
dependencies:
|
||||||
|
balanced-match: 1.0.2
|
||||||
|
concat-map: 0.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/callsites@3.1.0:
|
||||||
|
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/chalk@4.1.2:
|
||||||
|
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
ansi-styles: 4.3.0
|
||||||
|
supports-color: 7.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/color-convert@2.0.1:
|
||||||
|
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||||
|
engines: {node: '>=7.0.0'}
|
||||||
|
dependencies:
|
||||||
|
color-name: 1.1.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/color-name@1.1.4:
|
||||||
|
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/concat-map@0.0.1:
|
||||||
|
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/cross-spawn@7.0.3:
|
||||||
|
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
dependencies:
|
||||||
|
path-key: 3.1.1
|
||||||
|
shebang-command: 2.0.0
|
||||||
|
which: 2.0.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/debug@4.3.4:
|
||||||
|
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||||
|
engines: {node: '>=6.0'}
|
||||||
|
peerDependencies:
|
||||||
|
supports-color: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
supports-color:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
ms: 2.1.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/deep-is@0.1.4:
|
||||||
|
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/doctrine@3.0.0:
|
||||||
|
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
dependencies:
|
||||||
|
esutils: 2.0.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/escape-string-regexp@4.0.0:
|
||||||
|
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/eslint-scope@7.2.2:
|
||||||
|
resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
|
||||||
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
|
dependencies:
|
||||||
|
esrecurse: 4.3.0
|
||||||
|
estraverse: 5.3.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/eslint-visitor-keys@3.4.3:
|
||||||
|
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
|
||||||
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/eslint@8.54.0:
|
||||||
|
resolution: {integrity: sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==}
|
||||||
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
'@eslint-community/eslint-utils': 4.4.0(eslint@8.54.0)
|
||||||
|
'@eslint-community/regexpp': 4.10.0
|
||||||
|
'@eslint/eslintrc': 2.1.3
|
||||||
|
'@eslint/js': 8.54.0
|
||||||
|
'@humanwhocodes/config-array': 0.11.13
|
||||||
|
'@humanwhocodes/module-importer': 1.0.1
|
||||||
|
'@nodelib/fs.walk': 1.2.8
|
||||||
|
'@ungap/structured-clone': 1.2.0
|
||||||
|
ajv: 6.12.6
|
||||||
|
chalk: 4.1.2
|
||||||
|
cross-spawn: 7.0.3
|
||||||
|
debug: 4.3.4
|
||||||
|
doctrine: 3.0.0
|
||||||
|
escape-string-regexp: 4.0.0
|
||||||
|
eslint-scope: 7.2.2
|
||||||
|
eslint-visitor-keys: 3.4.3
|
||||||
|
espree: 9.6.1
|
||||||
|
esquery: 1.5.0
|
||||||
|
esutils: 2.0.3
|
||||||
|
fast-deep-equal: 3.1.3
|
||||||
|
file-entry-cache: 6.0.1
|
||||||
|
find-up: 5.0.0
|
||||||
|
glob-parent: 6.0.2
|
||||||
|
globals: 13.23.0
|
||||||
|
graphemer: 1.4.0
|
||||||
|
ignore: 5.3.0
|
||||||
|
imurmurhash: 0.1.4
|
||||||
|
is-glob: 4.0.3
|
||||||
|
is-path-inside: 3.0.3
|
||||||
|
js-yaml: 4.1.0
|
||||||
|
json-stable-stringify-without-jsonify: 1.0.1
|
||||||
|
levn: 0.4.1
|
||||||
|
lodash.merge: 4.6.2
|
||||||
|
minimatch: 3.1.2
|
||||||
|
natural-compare: 1.4.0
|
||||||
|
optionator: 0.9.3
|
||||||
|
strip-ansi: 6.0.1
|
||||||
|
text-table: 0.2.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/espree@9.6.1:
|
||||||
|
resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
|
||||||
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
|
dependencies:
|
||||||
|
acorn: 8.11.2
|
||||||
|
acorn-jsx: 5.3.2(acorn@8.11.2)
|
||||||
|
eslint-visitor-keys: 3.4.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/esquery@1.5.0:
|
||||||
|
resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
|
||||||
|
engines: {node: '>=0.10'}
|
||||||
|
dependencies:
|
||||||
|
estraverse: 5.3.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/esrecurse@4.3.0:
|
||||||
|
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
|
||||||
|
engines: {node: '>=4.0'}
|
||||||
|
dependencies:
|
||||||
|
estraverse: 5.3.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/estraverse@5.3.0:
|
||||||
|
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
|
||||||
|
engines: {node: '>=4.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/esutils@2.0.3:
|
||||||
|
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/fast-deep-equal@3.1.3:
|
||||||
|
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/fast-json-stable-stringify@2.1.0:
|
||||||
|
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/fast-levenshtein@2.0.6:
|
||||||
|
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/fastq@1.15.0:
|
||||||
|
resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
|
||||||
|
dependencies:
|
||||||
|
reusify: 1.0.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/file-entry-cache@6.0.1:
|
||||||
|
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
||||||
|
engines: {node: ^10.12.0 || >=12.0.0}
|
||||||
|
dependencies:
|
||||||
|
flat-cache: 3.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/find-up@5.0.0:
|
||||||
|
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
locate-path: 6.0.0
|
||||||
|
path-exists: 4.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/flat-cache@3.2.0:
|
||||||
|
resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
|
||||||
|
engines: {node: ^10.12.0 || >=12.0.0}
|
||||||
|
dependencies:
|
||||||
|
flatted: 3.2.9
|
||||||
|
keyv: 4.5.4
|
||||||
|
rimraf: 3.0.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/flatted@3.2.9:
|
||||||
|
resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/fs.realpath@1.0.0:
|
||||||
|
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/glob-parent@6.0.2:
|
||||||
|
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
|
||||||
|
engines: {node: '>=10.13.0'}
|
||||||
|
dependencies:
|
||||||
|
is-glob: 4.0.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/glob@7.2.3:
|
||||||
|
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
||||||
|
dependencies:
|
||||||
|
fs.realpath: 1.0.0
|
||||||
|
inflight: 1.0.6
|
||||||
|
inherits: 2.0.4
|
||||||
|
minimatch: 3.1.2
|
||||||
|
once: 1.4.0
|
||||||
|
path-is-absolute: 1.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/globals@13.23.0:
|
||||||
|
resolution: {integrity: sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
type-fest: 0.20.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/graphemer@1.4.0:
|
||||||
|
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/has-flag@4.0.0:
|
||||||
|
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/ignore@5.3.0:
|
||||||
|
resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==}
|
||||||
|
engines: {node: '>= 4'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/import-fresh@3.3.0:
|
||||||
|
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
dependencies:
|
||||||
|
parent-module: 1.0.1
|
||||||
|
resolve-from: 4.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/imurmurhash@0.1.4:
|
||||||
|
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
|
||||||
|
engines: {node: '>=0.8.19'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/inflight@1.0.6:
|
||||||
|
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
|
||||||
|
dependencies:
|
||||||
|
once: 1.4.0
|
||||||
|
wrappy: 1.0.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/inherits@2.0.4:
|
||||||
|
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/is-extglob@2.1.1:
|
||||||
|
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/is-glob@4.0.3:
|
||||||
|
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dependencies:
|
||||||
|
is-extglob: 2.1.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/is-path-inside@3.0.3:
|
||||||
|
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/isexe@2.0.0:
|
||||||
|
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/js-yaml@4.1.0:
|
||||||
|
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
argparse: 2.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/json-buffer@3.0.1:
|
||||||
|
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/json-schema-traverse@0.4.1:
|
||||||
|
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/json-stable-stringify-without-jsonify@1.0.1:
|
||||||
|
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/keyv@4.5.4:
|
||||||
|
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||||
|
dependencies:
|
||||||
|
json-buffer: 3.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/levn@0.4.1:
|
||||||
|
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
|
||||||
|
engines: {node: '>= 0.8.0'}
|
||||||
|
dependencies:
|
||||||
|
prelude-ls: 1.2.1
|
||||||
|
type-check: 0.4.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/locate-path@6.0.0:
|
||||||
|
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
p-locate: 5.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/lodash.merge@4.6.2:
|
||||||
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/minimatch@3.1.2:
|
||||||
|
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||||
|
dependencies:
|
||||||
|
brace-expansion: 1.1.11
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/ms@2.1.2:
|
||||||
|
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/natural-compare@1.4.0:
|
||||||
|
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/once@1.4.0:
|
||||||
|
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||||
|
dependencies:
|
||||||
|
wrappy: 1.0.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/optionator@0.9.3:
|
||||||
|
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
|
||||||
|
engines: {node: '>= 0.8.0'}
|
||||||
|
dependencies:
|
||||||
|
'@aashutoshrathi/word-wrap': 1.2.6
|
||||||
|
deep-is: 0.1.4
|
||||||
|
fast-levenshtein: 2.0.6
|
||||||
|
levn: 0.4.1
|
||||||
|
prelude-ls: 1.2.1
|
||||||
|
type-check: 0.4.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/p-limit@3.1.0:
|
||||||
|
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
yocto-queue: 0.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/p-locate@5.0.0:
|
||||||
|
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
p-limit: 3.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/parent-module@1.0.1:
|
||||||
|
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
dependencies:
|
||||||
|
callsites: 3.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/path-exists@4.0.0:
|
||||||
|
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/path-is-absolute@1.0.1:
|
||||||
|
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/path-key@3.1.1:
|
||||||
|
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/prelude-ls@1.2.1:
|
||||||
|
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
|
||||||
|
engines: {node: '>= 0.8.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/punycode@2.3.1:
|
||||||
|
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/queue-microtask@1.2.3:
|
||||||
|
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/resolve-from@4.0.0:
|
||||||
|
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/reusify@1.0.4:
|
||||||
|
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
|
||||||
|
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/rimraf@3.0.2:
|
||||||
|
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
glob: 7.2.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/run-parallel@1.2.0:
|
||||||
|
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||||
|
dependencies:
|
||||||
|
queue-microtask: 1.2.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/shebang-command@2.0.0:
|
||||||
|
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
shebang-regex: 3.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/shebang-regex@3.0.0:
|
||||||
|
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/strip-ansi@6.0.1:
|
||||||
|
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
ansi-regex: 5.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/strip-json-comments@3.1.1:
|
||||||
|
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/supports-color@7.2.0:
|
||||||
|
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
has-flag: 4.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/text-table@0.2.0:
|
||||||
|
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/type-check@0.4.0:
|
||||||
|
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
||||||
|
engines: {node: '>= 0.8.0'}
|
||||||
|
dependencies:
|
||||||
|
prelude-ls: 1.2.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/type-fest@0.20.2:
|
||||||
|
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/typescript@5.3.2:
|
||||||
|
resolution: {integrity: sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==}
|
||||||
|
engines: {node: '>=14.17'}
|
||||||
|
hasBin: true
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/uri-js@4.4.1:
|
||||||
|
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||||
|
dependencies:
|
||||||
|
punycode: 2.3.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/which@2.0.2:
|
||||||
|
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
isexe: 2.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/wrappy@1.0.2:
|
||||||
|
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/yocto-queue@0.1.0:
|
||||||
|
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dev: false
|
7347
pnpm-lock.yaml
generated
7347
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
196
scripts/build.ts
Executable file
196
scripts/build.ts
Executable file
@ -0,0 +1,196 @@
|
|||||||
|
#!/usr/bin/env tsx
|
||||||
|
import { promises as fs } from 'node:fs';
|
||||||
|
import { isBuiltin } from 'node:module';
|
||||||
|
import { relative, resolve } from 'node:path';
|
||||||
|
|
||||||
|
import esbuild from 'esbuild';
|
||||||
|
import type { Plugin } from 'esbuild';
|
||||||
|
import { memoize } from 'lodash-es';
|
||||||
|
import c from 'picocolors';
|
||||||
|
import { minify_sync } from 'terser';
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
const { gray, green } = c;
|
||||||
|
|
||||||
|
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 bundle(
|
||||||
|
entry: string,
|
||||||
|
outfile: string,
|
||||||
|
options?: esbuild.BuildOptions & { treeShaking?: boolean },
|
||||||
|
) {
|
||||||
|
const output = await esbuild.build({
|
||||||
|
entryPoints: [entry],
|
||||||
|
outfile,
|
||||||
|
bundle: true,
|
||||||
|
minify: PROD,
|
||||||
|
platform: 'node',
|
||||||
|
packages: 'external',
|
||||||
|
sourcemap: 'linked',
|
||||||
|
plugins,
|
||||||
|
define: {},
|
||||||
|
alias: {},
|
||||||
|
external: ['find-cache-dir'],
|
||||||
|
format: 'esm',
|
||||||
|
banner: {
|
||||||
|
js: '/* eslint-disable */',
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (options?.treeShaking) {
|
||||||
|
const [text, setText] = await useText(outfile);
|
||||||
|
const minified = minify_sync(text, {
|
||||||
|
module: true,
|
||||||
|
compress: {
|
||||||
|
conditionals: true,
|
||||||
|
dead_code: true,
|
||||||
|
defaults: false,
|
||||||
|
evaluate: true,
|
||||||
|
passes: 3,
|
||||||
|
pure_new: true,
|
||||||
|
side_effects: true,
|
||||||
|
unused: true,
|
||||||
|
},
|
||||||
|
mangle: false,
|
||||||
|
format: {
|
||||||
|
comments: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await setText(minified.code!);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, 'utf8');
|
||||||
|
const setState = (text: string) => fs.writeFile(path, text);
|
||||||
|
return [state, setState] as const;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bundleType(source: string, output: string) {
|
||||||
|
try {
|
||||||
|
return dts({
|
||||||
|
source,
|
||||||
|
dist: output,
|
||||||
|
project: './tsconfig.build.json',
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log('Building type definitions…');
|
||||||
|
try {
|
||||||
|
await fs.rm('dist/config', { recursive: true });
|
||||||
|
} catch {
|
||||||
|
// noop
|
||||||
|
}
|
||||||
|
|
||||||
|
bundleType('./src/prettier.ts', './dist/prettier.d.ts');
|
||||||
|
bundleType('./src/types.ts', './dist/types.d.ts');
|
||||||
|
|
||||||
|
const unminify = { minify: false };
|
||||||
|
|
||||||
|
console.log('Building packages…');
|
||||||
|
await Promise.all([
|
||||||
|
bundle('./src/index.ts', undefined!, {
|
||||||
|
format: 'esm',
|
||||||
|
splitting: true,
|
||||||
|
outdir: './dist/config',
|
||||||
|
...unminify,
|
||||||
|
}),
|
||||||
|
bundle('./src/types.ts', './dist/types.js', unminify),
|
||||||
|
bundle('./src/prettier.ts', './dist/prettier.js', unminify),
|
||||||
|
bundle('./src/install.ts', './dist/install.js', {
|
||||||
|
treeShaking: true,
|
||||||
|
minify: false,
|
||||||
|
banner: {
|
||||||
|
js: '#!/usr/bin/env node\n/* eslint-disable */',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
editPackageJson(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// bundleType('./src/index.ts', './dist/config/index.d.ts');
|
||||||
|
await fs.copyFile('./src/config.d.ts', './dist/config/index.d.ts');
|
||||||
|
}
|
||||||
|
|
||||||
|
void main();
|
69
scripts/check-imports.ts
Executable file
69
scripts/check-imports.ts
Executable file
@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env bun
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import { builtinModules } from 'node:module';
|
||||||
|
|
||||||
|
import glob from 'fast-glob';
|
||||||
|
import { uniq } from 'lodash-es';
|
||||||
|
|
||||||
|
import { dependencies, peerDependencies } from '../dist/package.json';
|
||||||
|
|
||||||
|
function checkImports() {
|
||||||
|
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', '!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(),
|
||||||
|
});
|
31
scripts/dts.ts
Normal file
31
scripts/dts.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
import {
|
||||||
|
type EntryPointConfig,
|
||||||
|
generateDtsBundle,
|
||||||
|
} from 'dts-bundle-generator/dist/bundle-generator';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
|
export function dts({
|
||||||
|
source,
|
||||||
|
dist,
|
||||||
|
project,
|
||||||
|
}: {
|
||||||
|
source: string;
|
||||||
|
dist: string;
|
||||||
|
project: string;
|
||||||
|
}): void {
|
||||||
|
const entry: EntryPointConfig = {
|
||||||
|
filePath: source,
|
||||||
|
failOnClass: false,
|
||||||
|
output: {
|
||||||
|
exportReferencedTypes: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const generatedDts = generateDtsBundle([entry], {
|
||||||
|
preferredConfigPath: project,
|
||||||
|
followSymlinks: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
ts.sys.writeFile(dist, generatedDts[0]);
|
||||||
|
}
|
145
esbuild.ts → scripts/modifier.ts
Executable file → Normal file
145
esbuild.ts → scripts/modifier.ts
Executable file → Normal file
@ -1,29 +1,24 @@
|
|||||||
#!/usr/bin/env tsx
|
#!/usr/bin/env tsx
|
||||||
import assert from 'node:assert';
|
import assert from 'node:assert';
|
||||||
import { readFileSync } from 'node:fs';
|
import { readFileSync } from 'node:fs';
|
||||||
import { resolve, extname, relative } from 'node:path';
|
import { extname, resolve } from 'node:path';
|
||||||
import { isBuiltin } from 'node:module';
|
|
||||||
import esbuild from 'esbuild';
|
|
||||||
import type { Loader, Plugin } from 'esbuild';
|
|
||||||
import * as babel from '@babel/core';
|
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 type { types as t, types } from '@babel/core';
|
||||||
import { dependencies } from './dist/package.json';
|
import babelMacros, { type MacroHandler } from 'babel-plugin-macros';
|
||||||
import { createMacro, type MacroHandler } from 'babel-plugin-macros';
|
import type { Loader, Plugin } from 'esbuild';
|
||||||
import * as polyfill from './src/polyfill';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import * as polyfill from '../src/polyfill';
|
||||||
|
|
||||||
const polyfills = Object.keys(polyfill);
|
const polyfills = Object.keys(polyfill);
|
||||||
|
|
||||||
const ENV = process.env.NODE_ENV || 'development';
|
|
||||||
const PROD = ENV === 'production';
|
|
||||||
|
|
||||||
class HandlerMap {
|
class HandlerMap {
|
||||||
map = new Map<string, MacroHandler>();
|
map = new Map<string, MacroHandler>();
|
||||||
|
|
||||||
set(names: string | string[], handler: MacroHandler) {
|
set(names: string | string[], handler: MacroHandler) {
|
||||||
names = Array.isArray(names) ? names : [names];
|
names = Array.isArray(names) ? names : [names];
|
||||||
const macro = createMacro(handler);
|
const macro = babelMacros.createMacro(handler);
|
||||||
for (const name of names) {
|
for (const name of names) {
|
||||||
this.map.set(name, macro);
|
this.map.set(name, macro);
|
||||||
}
|
}
|
||||||
@ -31,7 +26,7 @@ class HandlerMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get keys() {
|
get keys() {
|
||||||
return Array.from(this.map.keys());
|
return [...this.map.keys()];
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvePath = (module: string) => module;
|
resolvePath = (module: string) => module;
|
||||||
@ -84,22 +79,34 @@ const map = new HandlerMap()
|
|||||||
'array.prototype.tosorted',
|
'array.prototype.tosorted',
|
||||||
proto(t => t.identifier('toSorted')),
|
proto(t => t.identifier('toSorted')),
|
||||||
)
|
)
|
||||||
|
.set(
|
||||||
|
'array.prototype.toreversed',
|
||||||
|
proto(t => t.identifier('toReversed')),
|
||||||
|
)
|
||||||
|
.set(
|
||||||
|
'array.prototype.findlast',
|
||||||
|
proto(t => t.identifier('findLast')),
|
||||||
|
)
|
||||||
.set(
|
.set(
|
||||||
'string.prototype.matchall',
|
'string.prototype.matchall',
|
||||||
proto(t => t.identifier('matchAll')),
|
proto(t => t.identifier('matchAll')),
|
||||||
)
|
)
|
||||||
|
.set(
|
||||||
|
'string.prototype.includes',
|
||||||
|
proto(t => t.identifier('includes')),
|
||||||
|
)
|
||||||
.set(
|
.set(
|
||||||
'object.groupby',
|
'object.groupby',
|
||||||
replace(t =>
|
replace(t =>
|
||||||
t.memberExpression(
|
t.memberExpression(
|
||||||
t.callExpression(t.identifier('require'), [t.stringLiteral('lodash')]),
|
t.callExpression(t.identifier('require'), [t.stringLiteral('lodash-es')]),
|
||||||
t.identifier('groupBy'),
|
t.identifier('groupBy'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// es-iterator-helpers/Iterator.prototype.*
|
// es-iterator-helpers/Iterator.prototype.*
|
||||||
const polyfillPath = resolve(__dirname, './src/polyfill.ts');
|
const polyfillPath = resolve(import.meta.dirname, '../src/polyfill.ts');
|
||||||
const requirePolyfill = (t: typeof types, name: string) =>
|
const requirePolyfill = (t: typeof types, name: string) =>
|
||||||
t.memberExpression(
|
t.memberExpression(
|
||||||
t.callExpression(t.identifier('require'), [t.stringLiteral(polyfillPath)]),
|
t.callExpression(t.identifier('require'), [t.stringLiteral(polyfillPath)]),
|
||||||
@ -116,17 +123,22 @@ for (const name of polyfills) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
map.set(
|
||||||
|
'safe-regex-test',
|
||||||
|
replace(t => requirePolyfill(t, 'safeRegexTest')),
|
||||||
|
);
|
||||||
|
|
||||||
function replace(getReplacement: (types: typeof t) => t.Expression): MacroHandler {
|
function replace(getReplacement: (types: typeof t) => t.Expression): MacroHandler {
|
||||||
return ({ references, babel: { types: t } }) => {
|
return ({ references, babel: { types: t } }) => {
|
||||||
references.default.forEach(referencePath => {
|
for (const referencePath of references.default) {
|
||||||
referencePath.replaceWith(getReplacement(t));
|
referencePath.replaceWith(getReplacement(t));
|
||||||
});
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function proto(getProperty: (types: typeof t) => t.Expression): MacroHandler {
|
function proto(getProperty: (types: typeof t) => t.Expression): MacroHandler {
|
||||||
return ({ references, babel: { types: t } }) => {
|
return ({ references, babel: { types: t } }) => {
|
||||||
references.default.forEach(referencePath => {
|
for (const referencePath of references.default) {
|
||||||
const { parent, parentPath } = referencePath;
|
const { parent, parentPath } = referencePath;
|
||||||
assert(t.isCallExpression(parent));
|
assert(t.isCallExpression(parent));
|
||||||
const [callee, ...rest] = parent.arguments;
|
const [callee, ...rest] = parent.arguments;
|
||||||
@ -136,7 +148,7 @@ function proto(getProperty: (types: typeof t) => t.Expression): MacroHandler {
|
|||||||
rest,
|
rest,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,21 +163,14 @@ export const babelPlugin: Plugin = {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let source = readFileSync(path, 'utf-8')
|
const source = readFileSync(path, 'utf8')
|
||||||
.replaceAll("require('object.hasown/polyfill')()", 'Object.hasOwn')
|
.replaceAll("require('object.hasown/polyfill')()", 'Object.hasOwn')
|
||||||
.replaceAll("require('object.fromentries/polyfill')()", 'Object.fromEntries')
|
.replaceAll("require('object.fromentries/polyfill')()", 'Object.fromEntries')
|
||||||
.replaceAll(
|
.replaceAll(
|
||||||
"Object.keys(require('prop-types'))",
|
"Object.keys(require('prop-types'))",
|
||||||
JSON.stringify(Object.keys(require('prop-types'))),
|
JSON.stringify(Object.keys(PropTypes)),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
|
||||||
path.includes('packages/eslint-plugin-import/src/rules/') ||
|
|
||||||
path.includes('packages/eslint-plugin-import/config/')
|
|
||||||
) {
|
|
||||||
source = source.replace('\nmodule.exports = {', '\nexport default {');
|
|
||||||
}
|
|
||||||
|
|
||||||
const isFlow = source.includes('@flow');
|
const isFlow = source.includes('@flow');
|
||||||
const loader = extname(path).slice(1) as Loader;
|
const loader = extname(path).slice(1) as Loader;
|
||||||
|
|
||||||
@ -193,85 +198,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: {},
|
|
||||||
banner: {
|
|
||||||
js: '/* eslint-disable */',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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');
|
|
||||||
main('./packages/eslint-import-resolver-typescript/src/index.ts');
|
|
||||||
main('./src/rules/index.ts', './dist/rules/index.js');
|
|
||||||
main('./src/index.ts', './dist/index.js');
|
|
@ -1,19 +1,26 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
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() {
|
pull() {
|
||||||
echo "🚛 Pulling $2"
|
echo "🚛 Pulling $2"
|
||||||
if [ ! -d "packages/$2" ]; then
|
if [ ! -d "packages/$2" ]; then
|
||||||
echo "📦 Repository not found, cloning..."
|
echo "📦 Repository not found, cloning..."
|
||||||
git clone "https://github.com/$1/$2.git" "packages/$2"
|
git clone "https://github.com/$1/$2.git" "packages/$2"
|
||||||
fi
|
fi
|
||||||
(cd "packages/$2" && git config pull.rebase true && git config rebase.autoStash true && git pull)
|
(cd "packages/$2" && git-pull "$2")
|
||||||
echo
|
echo
|
||||||
}
|
}
|
||||||
|
|
||||||
pull import-js eslint-import-resolver-typescript
|
pull import-js eslint-import-resolver-typescript
|
||||||
pull import-js eslint-plugin-import
|
|
||||||
pull jsx-eslint eslint-plugin-jsx-a11y
|
pull jsx-eslint eslint-plugin-jsx-a11y
|
||||||
pull eslint-community eslint-plugin-n
|
pull eslint-community eslint-plugin-n
|
||||||
pull jsx-eslint eslint-plugin-react
|
|
||||||
pull jsx-eslint jsx-ast-utils
|
pull jsx-eslint jsx-ast-utils
|
@ -3,9 +3,6 @@ sync() (
|
|||||||
cd "packages/$1" && git diff HEAD > "../../patch/$1.patch"
|
cd "packages/$1" && git diff HEAD > "../../patch/$1.patch"
|
||||||
)
|
)
|
||||||
|
|
||||||
sync eslint-import-resolver-typescript
|
|
||||||
sync eslint-plugin-import
|
|
||||||
sync eslint-plugin-jsx-a11y
|
sync eslint-plugin-jsx-a11y
|
||||||
sync eslint-plugin-n
|
sync eslint-plugin-n
|
||||||
sync eslint-plugin-react
|
|
||||||
sync jsx-ast-utils
|
sync jsx-ast-utils
|
@ -1,20 +0,0 @@
|
|||||||
#!/usr/bin/env tsx
|
|
||||||
import { readdirSync, writeFileSync } from 'node:fs';
|
|
||||||
import { camelCase } from 'lodash';
|
|
||||||
|
|
||||||
const files = readdirSync('./src/rules')
|
|
||||||
.filter(file => file.endsWith('.ts'))
|
|
||||||
.filter(file => file !== 'index.ts')
|
|
||||||
.map(file => file.slice(0, -3));
|
|
||||||
|
|
||||||
const entryFile = `
|
|
||||||
import type { Rule } from 'eslint';
|
|
||||||
|
|
||||||
${files.map(file => `import ${camelCase(file)} from "./${file}"`).join(';\n')}
|
|
||||||
|
|
||||||
export const rules: Record<string, Rule.RuleModule> = {
|
|
||||||
${files.map(file => `"${file}": ${camelCase(file)}`).join(',\n ')}
|
|
||||||
};
|
|
||||||
`.trim();
|
|
||||||
|
|
||||||
writeFileSync('./src/rules/index.ts', entryFile);
|
|
@ -1,34 +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}`]));
|
|
||||||
|
|
||||||
const files = Object.fromEntries(
|
|
||||||
glob
|
|
||||||
.sync('dist/**/*.js')
|
|
||||||
.map(path => [
|
|
||||||
path,
|
|
||||||
uniq(
|
|
||||||
Array.from(fs.readFileSync(path, 'utf8').matchAll(/require\(["']([^"']+)["']\)/g))
|
|
||||||
.map(m => m[1])
|
|
||||||
.filter(
|
|
||||||
module =>
|
|
||||||
!(
|
|
||||||
builtIn.has(module) ||
|
|
||||||
deps.includes(module) ||
|
|
||||||
deps.some(dep => module.startsWith(`${dep}/`)) ||
|
|
||||||
module.startsWith('eslint/') ||
|
|
||||||
module.startsWith('typescript/')
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
])
|
|
||||||
.filter(([, modules]) => modules.length > 0),
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(files);
|
|
38
src/commits.json
Normal file
38
src/commits.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"eslint-plugin-import": {
|
||||||
|
"hash": "6554bd5c30976290024cecc44ef1e96746cf3cf7",
|
||||||
|
"date": "2024-05-23T12:47:41-07:00",
|
||||||
|
"committer": "Jordan Harband",
|
||||||
|
"subject": "[meta] add `repository.directory` field"
|
||||||
|
},
|
||||||
|
"eslint-import-resolver-typescript": {
|
||||||
|
"hash": "5ee5879b4428f42edbc262d66e192c76202b7f47",
|
||||||
|
"date": "2024-10-01T03:12:28+00:00",
|
||||||
|
"committer": "GitHub",
|
||||||
|
"subject": "fix(deps): update dependency debug to ^4.3.7 (#316)"
|
||||||
|
},
|
||||||
|
"eslint-plugin-jsx-a11y": {
|
||||||
|
"hash": "4925ba8d0bf80a4b1d8e8645d310590bf1b40b64",
|
||||||
|
"date": "2024-09-20T14:09:27-07:00",
|
||||||
|
"committer": "Jordan Harband",
|
||||||
|
"subject": "[Fix] handle interactive/noninteractive changes from aria-query"
|
||||||
|
},
|
||||||
|
"eslint-plugin-n": {
|
||||||
|
"hash": "23d0e846e9dbfb68ccf7f410a83457514d432263",
|
||||||
|
"date": "2024-10-09T13:49:20+02:00",
|
||||||
|
"committer": "GitHub",
|
||||||
|
"subject": "chore(master): release 17.11.1 (#352)"
|
||||||
|
},
|
||||||
|
"eslint-plugin-react": {
|
||||||
|
"hash": "983b88dd3cb5e07919517d3fde4085f60883ded7",
|
||||||
|
"date": "2024-07-24T15:26:33-07:00",
|
||||||
|
"committer": "Jordan Harband",
|
||||||
|
"subject": "[Tests] `no-array-index-key`: actually run valid tests"
|
||||||
|
},
|
||||||
|
"jsx-ast-utils": {
|
||||||
|
"hash": "5943318eaf23764eec3ff397ebb969613d728a95",
|
||||||
|
"date": "2023-07-28T18:34:04-07:00",
|
||||||
|
"committer": "Jordan Harband",
|
||||||
|
"subject": "v3.3.5"
|
||||||
|
}
|
||||||
|
}
|
44
src/config.d.ts
vendored
Normal file
44
src/config.d.ts
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import type { FlatESLintConfig } from '@aet/eslint-define-config';
|
||||||
|
import type { Linter } from 'eslint';
|
||||||
|
|
||||||
|
type MiddlewareResult = Linter.Config | Linter.Config[];
|
||||||
|
|
||||||
|
export type Middleware =
|
||||||
|
| (() => Promise<MiddlewareResult>)
|
||||||
|
| (() => Promise<{ default: MiddlewareResult }>);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a ESLint config object.
|
||||||
|
*
|
||||||
|
* By default, it includes `["@typescript-eslint", "import-x", "prettier", "unicorn"]` configs.
|
||||||
|
* Additional bundled plugins include:
|
||||||
|
*
|
||||||
|
* 1. [`react`](https://github.com/jsx-eslint/eslint-plugin-react#list-of-supported-rules)
|
||||||
|
* (automatically enables
|
||||||
|
* [`react-hooks`](https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks))
|
||||||
|
* 2. [`react-refresh`](https://github.com/ArnaudBarre/eslint-plugin-react-refresh)
|
||||||
|
* 3. [`jsx-a11y`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y#supported-rules)
|
||||||
|
* 4. [`unicorn`](https://github.com/sindresorhus/eslint-plugin-unicorn#rules)
|
||||||
|
* 5. [`n`](https://github.com/eslint-community/eslint-plugin-n#-rules) (Node.js specific,
|
||||||
|
* requires `minimatch`)
|
||||||
|
* 6. [`jsdoc`](https://github.com/gajus/eslint-plugin-jsdoc#rules)
|
||||||
|
*
|
||||||
|
* Non bundled:
|
||||||
|
* 1. [`graphql`](https://the-guild.dev/graphql/eslint/rules)
|
||||||
|
*
|
||||||
|
* @param of Configuration options.
|
||||||
|
* @returns ESLint configuration object.
|
||||||
|
*/
|
||||||
|
export function extendConfig({
|
||||||
|
auto,
|
||||||
|
middlewares: addMiddlewares,
|
||||||
|
configs,
|
||||||
|
}: {
|
||||||
|
auto?: boolean;
|
||||||
|
middlewares?: Middleware[];
|
||||||
|
configs: FlatESLintConfig[];
|
||||||
|
}): Promise<FlatESLintConfig[]>;
|
||||||
|
|
||||||
|
export const error = 'error';
|
||||||
|
export const warn = 'warn';
|
||||||
|
export const off = 'off';
|
40
src/custom/index.ts
Normal file
40
src/custom/index.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import type { ESLint } from 'eslint';
|
||||||
|
|
||||||
|
import noEmptyObjectLiteral from './no-empty-object-literal';
|
||||||
|
import noImportDot from './no-import-dot';
|
||||||
|
import noUselessImportAlias from './no-useless-import-alias';
|
||||||
|
import restrictTemplateExpressions from './restrict-template-expressions';
|
||||||
|
|
||||||
|
type RuleLevel = 'error' | 'warn' | 'off' | 0 | 1 | 2;
|
||||||
|
type RuleEntry<Options> = RuleLevel | [RuleLevel, Partial<Options>];
|
||||||
|
|
||||||
|
export interface LocalRuleOptions {
|
||||||
|
/** Bans import from the specifier '.' and '..' and replaces it with '.+/index' */
|
||||||
|
'custom/no-import-dot': RuleEntry<unknown>;
|
||||||
|
/**
|
||||||
|
* Enforce template literal expressions to be of `string` type
|
||||||
|
* @see [restrict-template-expressions](https://typescript-eslint.io/rules/restrict-template-expressions)
|
||||||
|
*/
|
||||||
|
'typed-custom/restrict-template-expressions': RuleEntry<{ allow: string[] }>;
|
||||||
|
/** Ban assignment of empty object literals `{}` and replace them with `Object.create(null)` */
|
||||||
|
'custom/no-empty-object-literal': RuleEntry<unknown>;
|
||||||
|
/** Ban useless import alias */
|
||||||
|
'custom/no-useless-import-alias': RuleEntry<unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const plugin: ESLint.Plugin = {
|
||||||
|
name: 'custom',
|
||||||
|
rules: {
|
||||||
|
'no-empty-object-literal': noEmptyObjectLiteral,
|
||||||
|
'no-import-dot': noImportDot,
|
||||||
|
'no-useless-import-alias': noUselessImportAlias,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const typedPlugin: ESLint.Plugin = {
|
||||||
|
name: 'typed-custom',
|
||||||
|
rules: {
|
||||||
|
// @ts-expect-error type mismatch
|
||||||
|
'restrict-template-expressions': restrictTemplateExpressions,
|
||||||
|
},
|
||||||
|
};
|
29
src/custom/no-empty-object-literal.ts
Normal file
29
src/custom/no-empty-object-literal.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { defineRule } from '../types';
|
||||||
|
|
||||||
|
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 (
|
||||||
|
node.operator === '=' &&
|
||||||
|
node.right.type === 'ObjectExpression' &&
|
||||||
|
node.right.properties.length === 0
|
||||||
|
) {
|
||||||
|
context.report({
|
||||||
|
node,
|
||||||
|
message:
|
||||||
|
'Assigning empty object literals are not allowed, use `Object.create(null)` instead.',
|
||||||
|
fix: fixer => fixer.replaceText(node.right, 'Object.create(null)'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
@ -1,19 +1,19 @@
|
|||||||
import type { Rule } from "eslint";
|
import type { Rule } from 'eslint';
|
||||||
|
|
||||||
const rule: Rule.RuleModule = {
|
const rule: Rule.RuleModule = {
|
||||||
meta: {
|
meta: {
|
||||||
type: "problem",
|
type: 'problem',
|
||||||
docs: {
|
docs: {
|
||||||
description:
|
description:
|
||||||
"Bans import from the specifier '.' and '..' and replaces it with '.+/index'",
|
"Bans import from the specifier '.' and '..' and replaces it with '.+/index'",
|
||||||
category: "Best Practices",
|
category: 'Best Practices',
|
||||||
recommended: true,
|
recommended: true,
|
||||||
},
|
},
|
||||||
fixable: "code",
|
fixable: 'code',
|
||||||
},
|
},
|
||||||
create: context => ({
|
create: context => ({
|
||||||
ImportDeclaration(node) {
|
ImportDeclaration(node) {
|
||||||
if (node.source.value === ".") {
|
if (node.source.value === '.') {
|
||||||
context.report({
|
context.report({
|
||||||
node: node.source,
|
node: node.source,
|
||||||
message:
|
message:
|
||||||
@ -22,7 +22,7 @@ const rule: Rule.RuleModule = {
|
|||||||
return fixer.replaceText(node.source, '"./index"');
|
return fixer.replaceText(node.source, '"./index"');
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else if (node.source.value === "..") {
|
} else if (node.source.value === '..') {
|
||||||
context.report({
|
context.report({
|
||||||
node: node.source,
|
node: node.source,
|
||||||
message:
|
message:
|
45
src/custom/no-useless-import-alias.ts
Normal file
45
src/custom/no-useless-import-alias.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import type { Rule } from 'eslint';
|
||||||
|
import type { Position } from 'estree';
|
||||||
|
|
||||||
|
const rule: Rule.RuleModule = {
|
||||||
|
meta: {
|
||||||
|
type: 'problem',
|
||||||
|
docs: {
|
||||||
|
description:
|
||||||
|
"Ban useless import aliasing like `import { abc as abc } from 'module'`",
|
||||||
|
category: 'Best Practices',
|
||||||
|
recommended: true,
|
||||||
|
},
|
||||||
|
fixable: 'code',
|
||||||
|
},
|
||||||
|
create(context) {
|
||||||
|
return {
|
||||||
|
ImportDeclaration(node) {
|
||||||
|
if (node.specifiers.length === 0) return;
|
||||||
|
|
||||||
|
for (const specifier of node.specifiers) {
|
||||||
|
if (specifier.type !== 'ImportSpecifier') continue;
|
||||||
|
|
||||||
|
const { imported, local } = specifier;
|
||||||
|
if (
|
||||||
|
imported.name === local.name &&
|
||||||
|
!arePositionsEqual(imported.loc!.start, local.loc!.start)
|
||||||
|
) {
|
||||||
|
context.report({
|
||||||
|
node: specifier,
|
||||||
|
message: `Useless aliasing of '${imported.name}'?`,
|
||||||
|
fix(fixer) {
|
||||||
|
return fixer.removeRange([imported.range![1], local.range![1]]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const arePositionsEqual = (a: Position, b: Position) =>
|
||||||
|
a.line === b.line && a.column === b.column;
|
||||||
|
|
||||||
|
export default rule;
|
133
src/custom/restrict-template-expressions.ts
Normal file
133
src/custom/restrict-template-expressions.ts
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
// https://github.com/typescript-eslint/typescript-eslint/blob/75c128856b1ce05a4fec799bfa6de03b3dab03d0/packages/eslint-plugin/src/rules/restrict-template-expressions.ts
|
||||||
|
import {
|
||||||
|
getConstrainedTypeAtLocation,
|
||||||
|
getTypeName,
|
||||||
|
isTypeAnyType,
|
||||||
|
isTypeFlagSet,
|
||||||
|
isTypeNeverType,
|
||||||
|
} from '@typescript-eslint/type-utils';
|
||||||
|
import {
|
||||||
|
AST_NODE_TYPES,
|
||||||
|
ESLintUtils,
|
||||||
|
ParserServicesWithTypeInformation,
|
||||||
|
type TSESTree,
|
||||||
|
} from '@typescript-eslint/utils';
|
||||||
|
import { getParserServices } from '@typescript-eslint/utils/eslint-utils';
|
||||||
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
|
const createRule = ESLintUtils.RuleCreator(
|
||||||
|
name => `https://typescript-eslint.io/rules/${name}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
interface Option {
|
||||||
|
allow: string[];
|
||||||
|
}
|
||||||
|
const defaultOption: Option = {
|
||||||
|
allow: ['any', 'boolean', 'null', 'undefined', 'number', 'RegExp', 'URLSearchParams'],
|
||||||
|
};
|
||||||
|
|
||||||
|
type MessageId = 'invalidType';
|
||||||
|
|
||||||
|
export default createRule<Option[], MessageId>({
|
||||||
|
name: 'restrict-template-expressions',
|
||||||
|
meta: {
|
||||||
|
type: 'problem',
|
||||||
|
docs: {
|
||||||
|
description: 'Enforce template literal expressions to be of `string` type',
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
invalidType: 'Invalid type "{{type}}" of template literal expression.',
|
||||||
|
},
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
type: 'object',
|
||||||
|
additionalProperties: false,
|
||||||
|
properties: {
|
||||||
|
allow: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
description: 'Allow specific types',
|
||||||
|
uniqueItems: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
defaultOptions: [defaultOption],
|
||||||
|
create(context, [options]) {
|
||||||
|
let services: ParserServicesWithTypeInformation | undefined;
|
||||||
|
try {
|
||||||
|
services = getParserServices(context);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!services?.program) return {};
|
||||||
|
|
||||||
|
const checker = services.program.getTypeChecker();
|
||||||
|
const allowed = new Set(options.allow);
|
||||||
|
|
||||||
|
const { StringLike, NumberLike, BigIntLike, BooleanLike, Null, Undefined } =
|
||||||
|
ts.TypeFlags;
|
||||||
|
|
||||||
|
function isUnderlyingTypePrimitive(type: ts.Type): boolean {
|
||||||
|
return (
|
||||||
|
isTypeFlagSet(type, StringLike) ||
|
||||||
|
(allowed.has('number') && isTypeFlagSet(type, NumberLike | BigIntLike)) ||
|
||||||
|
(allowed.has('boolean') && isTypeFlagSet(type, BooleanLike)) ||
|
||||||
|
(allowed.has('any') && isTypeAnyType(type)) ||
|
||||||
|
allowed.has(getTypeName(checker, type)) ||
|
||||||
|
(allowed.has('null') && isTypeFlagSet(type, Null)) ||
|
||||||
|
(allowed.has('undefined') && isTypeFlagSet(type, Undefined)) ||
|
||||||
|
(allowed.has('never') && isTypeNeverType(type))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
TemplateLiteral(node: TSESTree.TemplateLiteral): void {
|
||||||
|
// don't check tagged template literals
|
||||||
|
if (node.parent.type === AST_NODE_TYPES.TaggedTemplateExpression) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const expression of node.expressions) {
|
||||||
|
const expressionType = getConstrainedTypeAtLocation(services, expression);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!isInnerUnionOrIntersectionConformingTo(
|
||||||
|
expressionType,
|
||||||
|
isUnderlyingTypePrimitive,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
context.report({
|
||||||
|
node: expression,
|
||||||
|
messageId: 'invalidType',
|
||||||
|
data: { type: checker.typeToString(expressionType) },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function isInnerUnionOrIntersectionConformingTo(
|
||||||
|
type: ts.Type,
|
||||||
|
predicate: (underlyingType: ts.Type) => boolean,
|
||||||
|
): boolean {
|
||||||
|
return rec(type);
|
||||||
|
|
||||||
|
function rec(innerType: ts.Type): boolean {
|
||||||
|
if (innerType.isUnion()) {
|
||||||
|
return innerType.types.every(rec);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (innerType.isIntersection()) {
|
||||||
|
return innerType.types.some(rec);
|
||||||
|
}
|
||||||
|
|
||||||
|
return predicate(innerType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
94
src/environment.ts
Normal file
94
src/environment.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
import type { Middleware } from './middleware';
|
||||||
|
import { reactQuery, storybook, vitest } from './presets/misc';
|
||||||
|
import { react, reactRefresh } from './presets/react';
|
||||||
|
|
||||||
|
const jsdoc = () => import('./presets/jsdoc');
|
||||||
|
const tailwind = () => import('./presets/tailwind');
|
||||||
|
const testingLibrary = () => import('./presets/testing-library');
|
||||||
|
|
||||||
|
const middlewares = {
|
||||||
|
react,
|
||||||
|
reactRefresh,
|
||||||
|
tailwind,
|
||||||
|
storybook,
|
||||||
|
reactQuery,
|
||||||
|
testingLibrary,
|
||||||
|
jsdoc,
|
||||||
|
vitest,
|
||||||
|
} satisfies {
|
||||||
|
[key: string]: Middleware;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const envs: {
|
||||||
|
dependency: string;
|
||||||
|
eslintPlugin?: string;
|
||||||
|
middleware: keyof typeof middlewares;
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
dependency: 'react',
|
||||||
|
middleware: 'react',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dependency: '@vitejs/plugin-react',
|
||||||
|
middleware: 'reactRefresh',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dependency: 'tailwindcss',
|
||||||
|
eslintPlugin: 'eslint-plugin-tailwindcss',
|
||||||
|
middleware: 'tailwind',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dependency: 'storybook',
|
||||||
|
eslintPlugin: 'eslint-plugin-storybook',
|
||||||
|
middleware: 'storybook',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dependency: '@tanstack/react-query',
|
||||||
|
eslintPlugin: '@tanstack/eslint-plugin-query',
|
||||||
|
middleware: 'reactQuery',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dependency: '@testing-library/react',
|
||||||
|
eslintPlugin: 'eslint-plugin-testing-library',
|
||||||
|
middleware: 'testingLibrary',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dependency: 'vitest',
|
||||||
|
eslintPlugin: 'eslint-plugin-vitest',
|
||||||
|
middleware: 'vitest',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function getProjectDependencies() {
|
||||||
|
const rootDir = process.cwd();
|
||||||
|
|
||||||
|
const pkgJsonPath = path.resolve(rootDir, 'package.json');
|
||||||
|
const pkgJson = fs.existsSync(pkgJsonPath)
|
||||||
|
? (JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')) as {
|
||||||
|
dependencies?: Record<string, string>;
|
||||||
|
devDependencies?: Record<string, string>;
|
||||||
|
peerDependencies?: Record<string, string>;
|
||||||
|
})
|
||||||
|
: {};
|
||||||
|
|
||||||
|
return new Set(
|
||||||
|
Object.keys({
|
||||||
|
...pkgJson.dependencies,
|
||||||
|
...pkgJson.devDependencies,
|
||||||
|
...pkgJson.peerDependencies,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* checkEnv(): Generator<Middleware> {
|
||||||
|
const deps = getProjectDependencies();
|
||||||
|
|
||||||
|
for (const { dependency, eslintPlugin, middleware } of envs) {
|
||||||
|
if (deps.has(dependency) && (!eslintPlugin || deps.has(eslintPlugin))) {
|
||||||
|
yield middlewares[middleware];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
202
src/index.ts
202
src/index.ts
@ -1,110 +1,110 @@
|
|||||||
import './redirect';
|
import type { FlatESLintConfig } from '@aet/eslint-define-config';
|
||||||
import type { ESLintConfig } from 'eslint-define-config';
|
import * as tsParser from '@typescript-eslint/parser';
|
||||||
import { typescriptRules } from './presets/typescript';
|
import importPlugin from 'eslint-plugin-import-x';
|
||||||
import { unicornRules } from './presets/unicorn';
|
import { uniq } from 'lodash-es';
|
||||||
|
import tseslint from 'typescript-eslint';
|
||||||
|
|
||||||
|
import { off } from './constants';
|
||||||
|
import { checkEnv } from './environment';
|
||||||
|
import { Middleware } from './middleware';
|
||||||
import { eslintRules } from './presets/eslint';
|
import { eslintRules } from './presets/eslint';
|
||||||
import { reactRules } from './presets/react';
|
import stylistic from './presets/stylistic';
|
||||||
import { error, warn, off } from './constants';
|
import { importRules, typescriptRules } from './presets/typescript';
|
||||||
// @ts-expect-error
|
import unicorn from './presets/unicorn';
|
||||||
const { name } = (0, require)('./package.json');
|
|
||||||
|
|
||||||
export { error, warn, off };
|
export { error, warn, off } from './constants';
|
||||||
|
|
||||||
const unique = <T>(arr: T[]): T[] => [...new Set(arr)];
|
export async function extendConfig({
|
||||||
const ensureArray = <T>(value?: T | T[]): T[] =>
|
auto = true,
|
||||||
value == null ? [] : Array.isArray(value) ? value : [value];
|
middlewares: addMiddlewares = [],
|
||||||
|
configs = [],
|
||||||
|
}: {
|
||||||
|
auto?: boolean;
|
||||||
|
middlewares?: Middleware[];
|
||||||
|
configs: FlatESLintConfig[];
|
||||||
|
}): Promise<FlatESLintConfig[]> {
|
||||||
|
const middlewares: Middleware[] = uniq([
|
||||||
|
() => import('./presets/custom'),
|
||||||
|
...(auto ? checkEnv() : []),
|
||||||
|
...addMiddlewares,
|
||||||
|
]);
|
||||||
|
|
||||||
declare module 'eslint-define-config/src/rules/react/no-unknown-property.d.ts' {
|
const result: FlatESLintConfig[] = [
|
||||||
export interface NoUnknownPropertyOption {
|
{
|
||||||
extends: ('next' | 'emotion')[];
|
name: 'eslint-rules/eslint',
|
||||||
|
rules: eslintRules,
|
||||||
|
},
|
||||||
|
...tseslint.configs.recommendedTypeChecked,
|
||||||
|
importPlugin.flatConfigs.recommended,
|
||||||
|
importPlugin.flatConfigs.react,
|
||||||
|
importPlugin.flatConfigs.typescript,
|
||||||
|
...unicorn,
|
||||||
|
stylistic,
|
||||||
|
{
|
||||||
|
name: 'eslint-rules: TypeScript and import-x',
|
||||||
|
files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
parser: tsParser,
|
||||||
|
projectService: true,
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
tsconfigRootDir: import.meta.dirname,
|
||||||
|
sourceType: 'module',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
'import-x/parsers': {
|
||||||
|
'@typescript-eslint/parser': ['.ts', '.tsx', '.mts', '.cts'],
|
||||||
|
},
|
||||||
|
'import-x/resolver': {
|
||||||
|
typescript: true,
|
||||||
|
node: true,
|
||||||
|
},
|
||||||
|
'import-x/core-modules': ['node:sqlite'],
|
||||||
|
},
|
||||||
|
ignores: ['eslint.config.cjs'],
|
||||||
|
rules: {
|
||||||
|
...importRules,
|
||||||
|
...typescriptRules,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'eslint-rules: Disable type checking',
|
||||||
|
files: ['*.js', '*.mjs', '*.cjs', '*.jsx'],
|
||||||
|
...tseslint.configs.disableTypeChecked,
|
||||||
|
rules: {
|
||||||
|
'import-x/no-commonjs': off,
|
||||||
|
'import-x/unambiguous': off,
|
||||||
|
'@typescript-eslint/no-require-imports': off,
|
||||||
|
'typed-custom/restrict-template-expressions': off,
|
||||||
|
...tseslint.configs.disableTypeChecked.rules,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'eslint-rules: .d.ts files',
|
||||||
|
files: ['*.d.ts'],
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/consistent-type-imports': off,
|
||||||
|
'import-x/unambiguous': off,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as FlatESLintConfig[];
|
||||||
|
|
||||||
|
for (const middleware of middlewares) {
|
||||||
|
let fn = await middleware();
|
||||||
|
if ('default' in fn) {
|
||||||
|
fn = fn.default;
|
||||||
|
}
|
||||||
|
if (Array.isArray(fn)) {
|
||||||
|
result.push(...(fn as FlatESLintConfig[]).flat(Infinity));
|
||||||
|
} else {
|
||||||
|
result.push(fn as unknown as FlatESLintConfig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export function extendConfig({
|
if (configs) {
|
||||||
plugins,
|
result.push(...configs);
|
||||||
settings,
|
}
|
||||||
rules,
|
|
||||||
extends: _extends,
|
|
||||||
overrides,
|
|
||||||
...rest
|
|
||||||
}: ESLintConfig = {}): ESLintConfig {
|
|
||||||
const hasReact = plugins?.includes('react');
|
|
||||||
const hasReactRefresh = plugins?.includes('react-refresh');
|
|
||||||
const hasUnicorn = plugins?.includes('unicorn');
|
|
||||||
const hasNext = ensureArray(_extends).some(name => name.includes(':@next/next'));
|
|
||||||
|
|
||||||
const result: ESLintConfig = {
|
|
||||||
root: true,
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
plugins: unique(['@typescript-eslint', 'import', ...(plugins ?? [])]),
|
|
||||||
env: { node: true, browser: true, es2023: true },
|
|
||||||
reportUnusedDisableDirectives: true,
|
|
||||||
parserOptions: {
|
|
||||||
project: true,
|
|
||||||
},
|
|
||||||
extends: unique([
|
|
||||||
'eslint:recommended',
|
|
||||||
'prettier',
|
|
||||||
'plugin:@typescript-eslint/recommended-type-checked',
|
|
||||||
'plugin:import/errors',
|
|
||||||
'plugin:import/typescript',
|
|
||||||
...(hasReact
|
|
||||||
? [
|
|
||||||
'plugin:react/recommended',
|
|
||||||
'plugin:react-hooks/recommended',
|
|
||||||
'plugin:jsx-a11y/recommended',
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
...(_extends ?? []),
|
|
||||||
]),
|
|
||||||
settings: {
|
|
||||||
'import/parsers': {
|
|
||||||
'@typescript-eslint/parser': ['.ts', '.tsx', '.mts', '.cts'],
|
|
||||||
},
|
|
||||||
'import/resolver': {
|
|
||||||
typescript: {
|
|
||||||
alwaysTryTypes: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
react: {
|
|
||||||
version: 'detect',
|
|
||||||
},
|
|
||||||
...settings,
|
|
||||||
},
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: ['.eslintrc.js', '*.config.js', 'index.js', 'babel.config.js'],
|
|
||||||
extends: ['plugin:@typescript-eslint/disable-type-checked'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['*.d.ts'],
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/consistent-type-imports': off,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
...(overrides ?? []),
|
|
||||||
],
|
|
||||||
rules: {
|
|
||||||
...eslintRules,
|
|
||||||
...typescriptRules,
|
|
||||||
'import/export': off,
|
|
||||||
'import/no-duplicates': error,
|
|
||||||
'import/order': [error, { groups: ['builtin', 'external'] }],
|
|
||||||
...(hasReact && {
|
|
||||||
...reactRules,
|
|
||||||
'react/no-unknown-property': [
|
|
||||||
error,
|
|
||||||
{ ignore: hasNext ? ['emotion', 'next'] : ['emotion'] },
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
...(hasReactRefresh && {
|
|
||||||
'react-refresh/only-export-components': [warn, { allowConstantExport: true }],
|
|
||||||
}),
|
|
||||||
...(hasUnicorn && unicornRules),
|
|
||||||
...rules,
|
|
||||||
},
|
|
||||||
...rest,
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
17
src/install.ts
Normal file
17
src/install.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { installPackage } from '@antfu/install-pkg';
|
||||||
|
import { uniq } from 'lodash-es';
|
||||||
|
|
||||||
|
import { envs, getProjectDependencies } from './environment';
|
||||||
|
|
||||||
|
const deps = getProjectDependencies();
|
||||||
|
const packages = uniq(
|
||||||
|
envs
|
||||||
|
.filter(_ => deps.has(_.dependency) && _.eslintPlugin && !deps.has(_.eslintPlugin))
|
||||||
|
.map(_ => _.eslintPlugin!),
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('Installing missing ESLint plugins.\n');
|
||||||
|
|
||||||
|
void installPackage(packages, {
|
||||||
|
silent: false,
|
||||||
|
});
|
@ -1,37 +0,0 @@
|
|||||||
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 {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/gulpjs/interpret
|
|
||||||
tryRequire([
|
|
||||||
'esbin/register',
|
|
||||||
'esbuild-register',
|
|
||||||
'ts-node/register/transpile-only',
|
|
||||||
'@swc/register',
|
|
||||||
'@babel/register',
|
|
||||||
'coffeescript/register',
|
|
||||||
]);
|
|
||||||
|
|
||||||
const folders = resolve(process.cwd(), 'eslint-local-rules');
|
|
||||||
const files = fs.readdirSync(folders);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
export = plugin;
|
|
10
src/middleware.ts
Normal file
10
src/middleware.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import type { Linter } from 'eslint';
|
||||||
|
|
||||||
|
type MiddlewareResult = Linter.Config | Linter.Config[];
|
||||||
|
|
||||||
|
export type Middleware =
|
||||||
|
| (() => Promise<MiddlewareResult>)
|
||||||
|
| (() => Promise<{ default: MiddlewareResult }>);
|
||||||
|
|
||||||
|
// eslint-disable-next-line unicorn/prevent-abbreviations
|
||||||
|
export const def = <T>(module: { default: T }): T => module.default;
|
30
src/modules.d.ts
vendored
Normal file
30
src/modules.d.ts
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// eslint-disable-next-line import-x/unambiguous
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'eslint-plugin-storybook' {
|
||||||
|
import type { Linter } from 'eslint';
|
||||||
|
|
||||||
|
export const configs: {
|
||||||
|
/** @deprecated */
|
||||||
|
csf: Linter.Config;
|
||||||
|
/** @deprecated */
|
||||||
|
recommended: Linter.Config;
|
||||||
|
'flat/csf': Linter.Config;
|
||||||
|
'flat/recommended': Linter.Config;
|
||||||
|
'flat/csf-strict': Linter.Config;
|
||||||
|
'flat/addon-interactions': Linter.Config;
|
||||||
|
};
|
||||||
|
}
|
@ -57,3 +57,8 @@ export function* map<T, U>(
|
|||||||
yield callback(value);
|
yield callback(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// safe-regex-test/index.js
|
||||||
|
export function safeRegexTest(regex: RegExp) {
|
||||||
|
return (text: string) => regex.test(text);
|
||||||
|
}
|
||||||
|
164
src/presets/_restrictedGlobals.json
Normal file
164
src/presets/_restrictedGlobals.json
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
[
|
||||||
|
"addEventListener",
|
||||||
|
"blur",
|
||||||
|
"caches",
|
||||||
|
"captureEvents",
|
||||||
|
"clientInformation",
|
||||||
|
"close",
|
||||||
|
"closed",
|
||||||
|
"crossOriginIsolated",
|
||||||
|
"devicePixelRatio",
|
||||||
|
"dispatchEvent",
|
||||||
|
"event",
|
||||||
|
"external",
|
||||||
|
"focus",
|
||||||
|
"innerHeight",
|
||||||
|
"innerWidth",
|
||||||
|
"length",
|
||||||
|
"locationbar",
|
||||||
|
"menubar",
|
||||||
|
"name",
|
||||||
|
"onabort",
|
||||||
|
"onafterprint",
|
||||||
|
"onanimationcancel",
|
||||||
|
"onanimationend",
|
||||||
|
"onanimationiteration",
|
||||||
|
"onanimationstart",
|
||||||
|
"onauxclick",
|
||||||
|
"onbeforeinput",
|
||||||
|
"onbeforeprint",
|
||||||
|
"onbeforetoggle",
|
||||||
|
"onbeforeunload",
|
||||||
|
"onblur",
|
||||||
|
"oncancel",
|
||||||
|
"oncanplay",
|
||||||
|
"oncanplaythrough",
|
||||||
|
"onchange",
|
||||||
|
"onclick",
|
||||||
|
"onclose",
|
||||||
|
"oncontextmenu",
|
||||||
|
"oncopy",
|
||||||
|
"oncuechange",
|
||||||
|
"oncut",
|
||||||
|
"ondblclick",
|
||||||
|
"ondevicemotion",
|
||||||
|
"ondeviceorientation",
|
||||||
|
"ondeviceorientationabsolute",
|
||||||
|
"ondrag",
|
||||||
|
"ondragend",
|
||||||
|
"ondragenter",
|
||||||
|
"ondragleave",
|
||||||
|
"ondragover",
|
||||||
|
"ondragstart",
|
||||||
|
"ondrop",
|
||||||
|
"ondurationchange",
|
||||||
|
"onemptied",
|
||||||
|
"onended",
|
||||||
|
"onerror",
|
||||||
|
"onfocus",
|
||||||
|
"onformdata",
|
||||||
|
"ongamepadconnected",
|
||||||
|
"ongamepaddisconnected",
|
||||||
|
"ongotpointercapture",
|
||||||
|
"onhashchange",
|
||||||
|
"oninput",
|
||||||
|
"oninvalid",
|
||||||
|
"onkeydown",
|
||||||
|
"onkeypress",
|
||||||
|
"onkeyup",
|
||||||
|
"onlanguagechange",
|
||||||
|
"onload",
|
||||||
|
"onloadeddata",
|
||||||
|
"onloadedmetadata",
|
||||||
|
"onloadstart",
|
||||||
|
"onlostpointercapture",
|
||||||
|
"onmessage",
|
||||||
|
"onmessageerror",
|
||||||
|
"onmousedown",
|
||||||
|
"onmouseenter",
|
||||||
|
"onmouseleave",
|
||||||
|
"onmousemove",
|
||||||
|
"onmouseout",
|
||||||
|
"onmouseover",
|
||||||
|
"onmouseup",
|
||||||
|
"onoffline",
|
||||||
|
"ononline",
|
||||||
|
"onorientationchange",
|
||||||
|
"onpagehide",
|
||||||
|
"onpageshow",
|
||||||
|
"onpaste",
|
||||||
|
"onpause",
|
||||||
|
"onplay",
|
||||||
|
"onplaying",
|
||||||
|
"onpointercancel",
|
||||||
|
"onpointerdown",
|
||||||
|
"onpointerenter",
|
||||||
|
"onpointerleave",
|
||||||
|
"onpointermove",
|
||||||
|
"onpointerout",
|
||||||
|
"onpointerover",
|
||||||
|
"onpointerup",
|
||||||
|
"onpopstate",
|
||||||
|
"onprogress",
|
||||||
|
"onratechange",
|
||||||
|
"onrejectionhandled",
|
||||||
|
"onreset",
|
||||||
|
"onresize",
|
||||||
|
"onscroll",
|
||||||
|
"onscrollend",
|
||||||
|
"onsecuritypolicyviolation",
|
||||||
|
"onseeked",
|
||||||
|
"onseeking",
|
||||||
|
"onselect",
|
||||||
|
"onselectionchange",
|
||||||
|
"onselectstart",
|
||||||
|
"onslotchange",
|
||||||
|
"onstalled",
|
||||||
|
"onstorage",
|
||||||
|
"onsubmit",
|
||||||
|
"onsuspend",
|
||||||
|
"ontimeupdate",
|
||||||
|
"ontoggle",
|
||||||
|
"ontouchcancel",
|
||||||
|
"ontouchend",
|
||||||
|
"ontouchmove",
|
||||||
|
"ontouchstart",
|
||||||
|
"ontransitioncancel",
|
||||||
|
"ontransitionend",
|
||||||
|
"ontransitionrun",
|
||||||
|
"ontransitionstart",
|
||||||
|
"onunhandledrejection",
|
||||||
|
"onunload",
|
||||||
|
"onvolumechange",
|
||||||
|
"onwaiting",
|
||||||
|
"onwebkitanimationend",
|
||||||
|
"onwebkitanimationiteration",
|
||||||
|
"onwebkitanimationstart",
|
||||||
|
"onwebkittransitionend",
|
||||||
|
"onwheel",
|
||||||
|
"orientation",
|
||||||
|
"origin",
|
||||||
|
"outerHeight",
|
||||||
|
"outerWidth",
|
||||||
|
"pageXOffset",
|
||||||
|
"pageYOffset",
|
||||||
|
"personalbar",
|
||||||
|
"releaseEvents",
|
||||||
|
"removeEventListener",
|
||||||
|
"reportError",
|
||||||
|
"screenLeft",
|
||||||
|
"screenTop",
|
||||||
|
"screenX",
|
||||||
|
"screenY",
|
||||||
|
"scroll",
|
||||||
|
"scrollbars",
|
||||||
|
"scrollBy",
|
||||||
|
"scrollTo",
|
||||||
|
"scrollX",
|
||||||
|
"scrollY",
|
||||||
|
"status",
|
||||||
|
"statusbar",
|
||||||
|
"stop",
|
||||||
|
"toolbar",
|
||||||
|
"top"
|
||||||
|
]
|
23
src/presets/custom.ts
Normal file
23
src/presets/custom.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { error } from '../constants';
|
||||||
|
import { plugin, typedPlugin, LocalRuleOptions } from '../custom/index';
|
||||||
|
import { defineConfig } from '../types';
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
{
|
||||||
|
name: 'eslint-rules/custom',
|
||||||
|
plugins: { custom: plugin },
|
||||||
|
rules: {
|
||||||
|
'custom/no-import-dot': error,
|
||||||
|
'custom/no-useless-import-alias': error,
|
||||||
|
} satisfies Partial<LocalRuleOptions>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'eslint-rules/typed-custom',
|
||||||
|
plugins: { 'typed-custom': typedPlugin },
|
||||||
|
files: ['*.ts'],
|
||||||
|
ignores: ['*.d.ts'],
|
||||||
|
rules: {
|
||||||
|
'typed-custom/restrict-template-expressions': error,
|
||||||
|
} satisfies Partial<LocalRuleOptions>,
|
||||||
|
},
|
||||||
|
]);
|
@ -1,9 +1,13 @@
|
|||||||
import { error, warn, off } from '../constants';
|
import type { EslintRulesObject } from '@aet/eslint-define-config/src/rules/eslint';
|
||||||
import { EslintRules } from 'eslint-define-config/src/rules/eslint';
|
|
||||||
|
|
||||||
export const eslintRules: Partial<EslintRules> = {
|
import { error, off, warn } from '../constants';
|
||||||
|
|
||||||
|
import restrictedGlobals from './_restrictedGlobals.json';
|
||||||
|
|
||||||
|
export const eslintRules: Partial<EslintRulesObject> = {
|
||||||
'arrow-body-style': [error, 'as-needed'],
|
'arrow-body-style': [error, 'as-needed'],
|
||||||
'class-methods-use-this': off,
|
'class-methods-use-this': warn,
|
||||||
|
'func-style': [error, 'declaration', { allowArrowFunctions: true }],
|
||||||
'no-async-promise-executor': off,
|
'no-async-promise-executor': off,
|
||||||
'no-case-declarations': off,
|
'no-case-declarations': off,
|
||||||
'no-console': warn,
|
'no-console': warn,
|
||||||
@ -13,7 +17,7 @@ export const eslintRules: Partial<EslintRules> = {
|
|||||||
'no-empty': [error, { allowEmptyCatch: true }],
|
'no-empty': [error, { allowEmptyCatch: true }],
|
||||||
'no-inner-declarations': off,
|
'no-inner-declarations': off,
|
||||||
'no-lonely-if': error,
|
'no-lonely-if': error,
|
||||||
'no-restricted-globals': [error, 'event', 'name', 'length'],
|
'no-restricted-globals': [error, ...restrictedGlobals],
|
||||||
'no-restricted-imports': [
|
'no-restricted-imports': [
|
||||||
error,
|
error,
|
||||||
{
|
{
|
||||||
@ -29,7 +33,7 @@ export const eslintRules: Partial<EslintRules> = {
|
|||||||
'no-template-curly-in-string': error,
|
'no-template-curly-in-string': error,
|
||||||
'no-var': error,
|
'no-var': error,
|
||||||
'object-shorthand': [error, 'always', { ignoreConstructors: true }],
|
'object-shorthand': [error, 'always', { ignoreConstructors: true }],
|
||||||
'one-var': [error, { var: 'never', let: 'never' }],
|
'one-var': [error, { var: 'never', let: 'never', const: 'never' }],
|
||||||
'prefer-arrow-callback': error,
|
'prefer-arrow-callback': error,
|
||||||
'prefer-const': [error, { destructuring: 'all' }],
|
'prefer-const': [error, { destructuring: 'all' }],
|
||||||
'prefer-destructuring': [
|
'prefer-destructuring': [
|
||||||
@ -41,7 +45,6 @@ export const eslintRules: Partial<EslintRules> = {
|
|||||||
'prefer-spread': warn,
|
'prefer-spread': warn,
|
||||||
'quote-props': [error, 'as-needed'],
|
'quote-props': [error, 'as-needed'],
|
||||||
'sort-imports': [warn, { ignoreDeclarationSort: true }],
|
'sort-imports': [warn, { ignoreDeclarationSort: true }],
|
||||||
'spaced-comment': [error, 'always', { markers: ['/', '#', '@'] }],
|
|
||||||
complexity: [warn, { max: 100 }],
|
complexity: [warn, { max: 100 }],
|
||||||
curly: [error, 'multi-line', 'consistent'],
|
curly: [error, 'multi-line', 'consistent'],
|
||||||
eqeqeq: [error, 'smart'],
|
eqeqeq: [error, 'smart'],
|
||||||
|
13
src/presets/graphql.ts
Normal file
13
src/presets/graphql.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Not usable. https://github.com/dimaMachina/graphql-eslint/issues/2178
|
||||||
|
import type { GraphQLRulesObject } from '@aet/eslint-define-config/src/rules/graphql-eslint';
|
||||||
|
import * as graphql from '@graphql-eslint/eslint-plugin';
|
||||||
|
|
||||||
|
import { defineConfig } from '../types';
|
||||||
|
|
||||||
|
// https://the-guild.dev/graphql/eslint/rules
|
||||||
|
const graphqlRules: Partial<GraphQLRulesObject> = {};
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
processor: graphql.processors.graphql,
|
||||||
|
rules: graphqlRules,
|
||||||
|
});
|
14
src/presets/jsdoc.ts
Normal file
14
src/presets/jsdoc.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import type { JSDocRulesObject } from '@aet/eslint-define-config/src/rules/jsdoc';
|
||||||
|
import module from 'eslint-plugin-jsdoc';
|
||||||
|
|
||||||
|
import { off } from '../constants';
|
||||||
|
import { defineConfig } from '../types';
|
||||||
|
|
||||||
|
const jsdocRules: Partial<JSDocRulesObject> = {
|
||||||
|
'jsdoc/require-jsdoc': off,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
module.configs['flat/recommended-typescript'],
|
||||||
|
{ name: 'eslint-rules/jsdoc', rules: jsdocRules },
|
||||||
|
]);
|
17
src/presets/misc.ts
Normal file
17
src/presets/misc.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { def } from '../middleware';
|
||||||
|
import { defineConfig } from '../types';
|
||||||
|
|
||||||
|
export async function storybook() {
|
||||||
|
const { configs } = def(await import('eslint-plugin-storybook'));
|
||||||
|
return defineConfig([configs['flat/recommended']]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function reactQuery() {
|
||||||
|
const { configs } = def(await import('@tanstack/eslint-plugin-query'));
|
||||||
|
return defineConfig(configs['flat/recommended']);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function vitest() {
|
||||||
|
const { configs } = def(await import('eslint-plugin-vitest'));
|
||||||
|
return defineConfig([configs.recommended]);
|
||||||
|
}
|
@ -1,9 +1,45 @@
|
|||||||
import { error, off } from '../constants';
|
import type { ReactRulesObject } from '@aet/eslint-define-config/src/rules/react';
|
||||||
import { ReactRules } from 'eslint-define-config/src/rules/react';
|
import type { ReactRefreshRulesObject } from '@aet/eslint-define-config/src/rules/react-refresh';
|
||||||
|
import type { Linter, ESLint } from 'eslint';
|
||||||
|
|
||||||
export const reactRules: Partial<ReactRules> = {
|
import { error, off, warn } from '../constants';
|
||||||
'react/display-name': off,
|
import { def } from '../middleware';
|
||||||
'react/no-children-prop': error,
|
import { defineConfig } from '../types';
|
||||||
'react/prop-types': off,
|
|
||||||
'react/react-in-jsx-scope': off,
|
const reactRules: Partial<ReactRulesObject> = {
|
||||||
|
'@eslint-react/no-missing-component-display-name': off,
|
||||||
|
'@eslint-react/no-children-prop': error,
|
||||||
|
'@eslint-react/no-leaked-conditional-rendering': error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export async function react() {
|
||||||
|
const reactPlugin = def(await import('@eslint-react/eslint-plugin'));
|
||||||
|
const a11y = def(await import('../../packages/eslint-plugin-jsx-a11y/src/index'));
|
||||||
|
const hooks = await import('../../packages/eslint-plugin-react-hooks');
|
||||||
|
|
||||||
|
return defineConfig([
|
||||||
|
reactPlugin.configs['recommended-type-checked'] as unknown as Linter.Config,
|
||||||
|
hooks.flatConfigs.recommended,
|
||||||
|
a11y.flatConfigs.recommended,
|
||||||
|
{
|
||||||
|
name: 'eslint-rules/react',
|
||||||
|
files: ['*.tsx'],
|
||||||
|
rules: reactRules,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const refreshRules: Partial<ReactRefreshRulesObject> = {
|
||||||
|
'react-refresh/only-export-components': [warn, { allowConstantExport: true }],
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function reactRefresh() {
|
||||||
|
const refreshPlugin = def(await import('eslint-plugin-react-refresh'));
|
||||||
|
return defineConfig({
|
||||||
|
name: 'eslint-rules/react-refresh',
|
||||||
|
plugins: {
|
||||||
|
'react-refresh': refreshPlugin as unknown as ESLint.Plugin,
|
||||||
|
},
|
||||||
|
rules: refreshRules,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
26
src/presets/stylistic.ts
Normal file
26
src/presets/stylistic.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import type { StylisticRulesObject } from '@aet/eslint-define-config/src/rules/stylistic';
|
||||||
|
import stylistic from '@stylistic/eslint-plugin';
|
||||||
|
|
||||||
|
import { error } from '../constants';
|
||||||
|
import { defineConfig } from '../types';
|
||||||
|
|
||||||
|
const stylisticRules: Partial<StylisticRulesObject> = {
|
||||||
|
'stylistic/spaced-comment': [
|
||||||
|
error,
|
||||||
|
'always',
|
||||||
|
// allow /*@__PURE__*/
|
||||||
|
{ markers: ['/', '#', '@'], block: { exceptions: ['@'] } },
|
||||||
|
],
|
||||||
|
'stylistic/jsx-sort-props': [
|
||||||
|
error,
|
||||||
|
{ callbacksLast: true, shorthandFirst: true, multiline: 'last' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
name: 'eslint-rules/stylistic',
|
||||||
|
plugins: {
|
||||||
|
stylistic,
|
||||||
|
},
|
||||||
|
rules: stylisticRules,
|
||||||
|
});
|
17
src/presets/tailwind.ts
Normal file
17
src/presets/tailwind.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import type { TailwindRulesObject } from '@aet/eslint-define-config/src/rules/tailwind';
|
||||||
|
import tailwind from 'eslint-plugin-tailwindcss';
|
||||||
|
|
||||||
|
import { off } from '../constants';
|
||||||
|
import { defineConfig } from '../types';
|
||||||
|
|
||||||
|
const tailwindRules: Partial<TailwindRulesObject> = {
|
||||||
|
'tailwindcss/no-custom-classname': off,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
...tailwind.configs['flat/recommended'],
|
||||||
|
{
|
||||||
|
name: 'eslint-rules/tailwind',
|
||||||
|
rules: tailwindRules,
|
||||||
|
},
|
||||||
|
]);
|
16
src/presets/testing-library.ts
Normal file
16
src/presets/testing-library.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import type { TestingLibraryRulesObject } from '@aet/eslint-define-config/src/rules/testing-library';
|
||||||
|
import testingLibrary from 'eslint-plugin-testing-library';
|
||||||
|
|
||||||
|
import { defineConfig } from '../types';
|
||||||
|
|
||||||
|
const testingLibraryRules: Partial<TestingLibraryRulesObject> = {};
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
name: 'eslint-rules/testing-library',
|
||||||
|
files: ['**/*.(spec|test).{ts,tsx}'],
|
||||||
|
...testingLibrary.configs['flat/react'],
|
||||||
|
rules: {
|
||||||
|
...testingLibrary.configs['flat/react'].rules,
|
||||||
|
...testingLibraryRules,
|
||||||
|
},
|
||||||
|
});
|
@ -1,7 +1,25 @@
|
|||||||
import { error, off } from '../constants';
|
import type { ImportXRulesObject } from '@aet/eslint-define-config/src/rules/import-x';
|
||||||
import type { TypeScriptRules } from 'eslint-define-config/src/rules/typescript-eslint';
|
import type { TypeScriptRulesObject } from '@aet/eslint-define-config/src/rules/typescript-eslint';
|
||||||
|
|
||||||
export const typescriptRules: Partial<TypeScriptRules> = {
|
import { error, off, warn } from '../constants';
|
||||||
|
|
||||||
|
export const importRules: Partial<ImportXRulesObject> = {
|
||||||
|
'import-x/first': error,
|
||||||
|
'import-x/no-absolute-path': error,
|
||||||
|
'import-x/no-duplicates': warn,
|
||||||
|
'import-x/no-useless-path-segments': error,
|
||||||
|
'import-x/order': [
|
||||||
|
warn,
|
||||||
|
{
|
||||||
|
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object'],
|
||||||
|
'newlines-between': 'always',
|
||||||
|
alphabetize: { order: 'asc', caseInsensitive: true },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'import-x/unambiguous': error,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const typescriptRules: Partial<TypeScriptRulesObject> = {
|
||||||
'@typescript-eslint/ban-ts-comment': [
|
'@typescript-eslint/ban-ts-comment': [
|
||||||
error,
|
error,
|
||||||
{
|
{
|
||||||
@ -11,14 +29,20 @@ export const typescriptRules: Partial<TypeScriptRules> = {
|
|||||||
'ts-nocheck': 'allow-with-description',
|
'ts-nocheck': 'allow-with-description',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'@typescript-eslint/ban-types': [error, { extendDefaults: true }],
|
|
||||||
'@typescript-eslint/consistent-type-imports': [
|
'@typescript-eslint/consistent-type-imports': [
|
||||||
error,
|
error,
|
||||||
{ disallowTypeAnnotations: false, fixStyle: 'inline-type-imports' },
|
{ disallowTypeAnnotations: false, fixStyle: 'inline-type-imports' },
|
||||||
],
|
],
|
||||||
|
'@typescript-eslint/explicit-member-accessibility': [
|
||||||
|
warn,
|
||||||
|
{ accessibility: 'no-public' },
|
||||||
|
],
|
||||||
|
'@typescript-eslint/no-empty-object-type': [
|
||||||
|
error,
|
||||||
|
{ allowInterfaces: 'with-single-extends' },
|
||||||
|
],
|
||||||
'@typescript-eslint/no-empty-interface': [error, { allowSingleExtends: true }],
|
'@typescript-eslint/no-empty-interface': [error, { allowSingleExtends: true }],
|
||||||
'@typescript-eslint/no-explicit-any': off,
|
'@typescript-eslint/no-explicit-any': off,
|
||||||
'@typescript-eslint/no-extraneous-class': error,
|
|
||||||
'@typescript-eslint/no-misused-promises': [error, { checksVoidReturn: false }],
|
'@typescript-eslint/no-misused-promises': [error, { checksVoidReturn: false }],
|
||||||
'@typescript-eslint/no-namespace': off,
|
'@typescript-eslint/no-namespace': off,
|
||||||
'@typescript-eslint/no-unnecessary-type-assertion': error,
|
'@typescript-eslint/no-unnecessary-type-assertion': error,
|
||||||
@ -27,12 +51,10 @@ export const typescriptRules: Partial<TypeScriptRules> = {
|
|||||||
'@typescript-eslint/no-unsafe-call': off,
|
'@typescript-eslint/no-unsafe-call': off,
|
||||||
'@typescript-eslint/no-unsafe-member-access': off,
|
'@typescript-eslint/no-unsafe-member-access': off,
|
||||||
'@typescript-eslint/no-unsafe-return': off,
|
'@typescript-eslint/no-unsafe-return': off,
|
||||||
'@typescript-eslint/no-unused-vars': [
|
'@typescript-eslint/no-unused-vars': off,
|
||||||
error,
|
|
||||||
{ ignoreRestSiblings: true, varsIgnorePattern: '^_' },
|
|
||||||
],
|
|
||||||
'@typescript-eslint/no-use-before-define': off,
|
'@typescript-eslint/no-use-before-define': off,
|
||||||
'@typescript-eslint/no-var-requires': off,
|
'@typescript-eslint/no-var-requires': off,
|
||||||
|
'@typescript-eslint/restrict-template-expressions': off,
|
||||||
'@typescript-eslint/triple-slash-reference': off,
|
'@typescript-eslint/triple-slash-reference': off,
|
||||||
'@typescript-eslint/unbound-method': off,
|
'@typescript-eslint/unbound-method': off,
|
||||||
};
|
};
|
||||||
|
@ -1,31 +1,45 @@
|
|||||||
import { error, warn } from '../constants';
|
import type { UnicornRulesObject } from '@aet/eslint-define-config/src/rules/unicorn';
|
||||||
import { UnicornRules } from 'eslint-define-config/src/rules/unicorn';
|
import unicorn from 'eslint-plugin-unicorn';
|
||||||
|
import globals from 'globals';
|
||||||
|
|
||||||
|
import { error, off, warn } from '../constants';
|
||||||
|
import { defineConfig } from '../types';
|
||||||
|
|
||||||
const suggest = (suggest: string) => ({ suggest, fix: false });
|
const suggest = (suggest: string) => ({ suggest, fix: false });
|
||||||
|
|
||||||
// https://github.com/sindresorhus/eslint-plugin-unicorn/tree/28e7498ad06679bb92343db53bb40a7b5ba2990a
|
// https://github.com/sindresorhus/eslint-plugin-unicorn/tree/28e7498ad06679bb92343db53bb40a7b5ba2990a
|
||||||
export const unicornRules: Partial<UnicornRules> = {
|
const unicornRules: Partial<UnicornRulesObject> = {
|
||||||
'unicorn/better-regex': error,
|
'unicorn/better-regex': error,
|
||||||
|
'unicorn/consistent-destructuring': warn,
|
||||||
'unicorn/consistent-function-scoping': warn,
|
'unicorn/consistent-function-scoping': warn,
|
||||||
'unicorn/escape-case': error,
|
'unicorn/escape-case': error,
|
||||||
'unicorn/no-array-for-each': warn,
|
'unicorn/no-array-for-each': warn,
|
||||||
'unicorn/no-array-method-this-argument': error,
|
'unicorn/no-array-method-this-argument': error,
|
||||||
'unicorn/no-array-push-push': warn,
|
'unicorn/no-array-push-push': warn,
|
||||||
|
'unicorn/no-await-in-promise-methods': error,
|
||||||
'unicorn/no-console-spaces': warn,
|
'unicorn/no-console-spaces': warn,
|
||||||
'unicorn/no-for-loop': warn,
|
'unicorn/no-for-loop': warn,
|
||||||
'unicorn/no-instanceof-array': error,
|
'unicorn/no-instanceof-array': error,
|
||||||
|
'unicorn/no-invalid-fetch-options': error,
|
||||||
|
'unicorn/no-invalid-remove-event-listener': error,
|
||||||
'unicorn/no-lonely-if': warn,
|
'unicorn/no-lonely-if': warn,
|
||||||
|
'unicorn/no-negation-in-equality-check': error,
|
||||||
|
'unicorn/no-new-buffer': error,
|
||||||
|
'unicorn/no-single-promise-in-promise-methods': error,
|
||||||
'unicorn/no-static-only-class': error,
|
'unicorn/no-static-only-class': error,
|
||||||
'unicorn/no-typeof-undefined': error,
|
'unicorn/no-typeof-undefined': error,
|
||||||
// 'unicorn/no-unused-properties': warn,
|
'unicorn/no-unnecessary-await': error,
|
||||||
|
'unicorn/no-unnecessary-polyfills': error,
|
||||||
|
'unicorn/no-unreadable-array-destructuring': warn,
|
||||||
'unicorn/no-useless-fallback-in-spread': error,
|
'unicorn/no-useless-fallback-in-spread': error,
|
||||||
'unicorn/no-useless-promise-resolve-reject': error,
|
'unicorn/no-useless-promise-resolve-reject': error,
|
||||||
'unicorn/no-useless-spread': error,
|
'unicorn/no-useless-spread': error,
|
||||||
'unicorn/no-useless-switch-case': error,
|
'unicorn/no-useless-switch-case': error,
|
||||||
|
'unicorn/no-useless-undefined': error,
|
||||||
// https://github.com/prettier/eslint-config-prettier/issues/51
|
'unicorn/no-zero-fractions': error,
|
||||||
// 'unicorn/number-literal-case': error,
|
'unicorn/number-literal-case': error,
|
||||||
'unicorn/prefer-array-find': error,
|
'unicorn/prefer-array-find': error,
|
||||||
|
'unicorn/prefer-array-flat': error,
|
||||||
'unicorn/prefer-array-flat-map': error,
|
'unicorn/prefer-array-flat-map': error,
|
||||||
'unicorn/prefer-array-some': error,
|
'unicorn/prefer-array-some': error,
|
||||||
'unicorn/prefer-at': error,
|
'unicorn/prefer-at': error,
|
||||||
@ -34,23 +48,32 @@ export const unicornRules: Partial<UnicornRules> = {
|
|||||||
'unicorn/prefer-default-parameters': warn,
|
'unicorn/prefer-default-parameters': warn,
|
||||||
'unicorn/prefer-dom-node-dataset': error,
|
'unicorn/prefer-dom-node-dataset': error,
|
||||||
'unicorn/prefer-dom-node-remove': error,
|
'unicorn/prefer-dom-node-remove': error,
|
||||||
|
'unicorn/prefer-dom-node-text-content': warn,
|
||||||
'unicorn/prefer-export-from': [error, { ignoreUsedVariables: false }],
|
'unicorn/prefer-export-from': [error, { ignoreUsedVariables: false }],
|
||||||
'unicorn/prefer-includes': error,
|
'unicorn/prefer-includes': error,
|
||||||
|
'unicorn/prefer-json-parse-buffer': warn,
|
||||||
'unicorn/prefer-keyboard-event-key': warn,
|
'unicorn/prefer-keyboard-event-key': warn,
|
||||||
'unicorn/prefer-logical-operator-over-ternary': warn,
|
'unicorn/prefer-logical-operator-over-ternary': warn,
|
||||||
'unicorn/prefer-math-trunc': error,
|
'unicorn/prefer-math-trunc': warn,
|
||||||
|
'unicorn/prefer-modern-dom-apis': error,
|
||||||
'unicorn/prefer-modern-math-apis': error,
|
'unicorn/prefer-modern-math-apis': error,
|
||||||
'unicorn/prefer-negative-index': error,
|
'unicorn/prefer-negative-index': error,
|
||||||
'unicorn/prefer-node-protocol': error,
|
'unicorn/prefer-node-protocol': error,
|
||||||
'unicorn/prefer-object-from-entries': error,
|
'unicorn/prefer-object-from-entries': error,
|
||||||
'unicorn/prefer-optional-catch-binding': error,
|
'unicorn/prefer-optional-catch-binding': error,
|
||||||
|
'unicorn/prefer-prototype-methods': error,
|
||||||
'unicorn/prefer-reflect-apply': error,
|
'unicorn/prefer-reflect-apply': error,
|
||||||
'unicorn/prefer-regexp-test': error,
|
'unicorn/prefer-regexp-test': error,
|
||||||
'unicorn/prefer-set-has': warn,
|
'unicorn/prefer-set-has': warn,
|
||||||
|
'unicorn/prefer-set-size': error,
|
||||||
|
'unicorn/prefer-string-raw': error,
|
||||||
'unicorn/prefer-string-slice': error,
|
'unicorn/prefer-string-slice': error,
|
||||||
'unicorn/prefer-string-starts-ends-with': warn,
|
'unicorn/prefer-string-starts-ends-with': warn,
|
||||||
'unicorn/prefer-string-trim-start-end': error,
|
'unicorn/prefer-string-trim-start-end': error,
|
||||||
|
'unicorn/prefer-switch': warn,
|
||||||
'unicorn/prefer-ternary': warn,
|
'unicorn/prefer-ternary': warn,
|
||||||
|
'unicorn/relative-url-style': warn,
|
||||||
|
'unicorn/require-number-to-fixed-digits-argument': error,
|
||||||
'unicorn/string-content': [
|
'unicorn/string-content': [
|
||||||
warn,
|
warn,
|
||||||
{
|
{
|
||||||
@ -64,8 +87,46 @@ export const unicornRules: Partial<UnicornRules> = {
|
|||||||
'<=>': suggest('⇔'),
|
'<=>': suggest('⇔'),
|
||||||
'\\.\\.\\.': suggest('…'),
|
'\\.\\.\\.': suggest('…'),
|
||||||
"'s ": suggest('’s '),
|
"'s ": suggest('’s '),
|
||||||
|
"'d ": suggest('’d '),
|
||||||
|
"'t ": suggest('’t '),
|
||||||
|
"l'": suggest('l’'),
|
||||||
|
"d'": suggest('d’'),
|
||||||
|
"qu'": suggest('qu’'),
|
||||||
|
'\\?!': suggest('⁈'),
|
||||||
|
'!\\?': suggest('⁉'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'unicorn/template-indent': warn,
|
'unicorn/template-indent': warn,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// export const unicorn = defineMiddleware((config, { addRules }) => {
|
||||||
|
// config.plugins.push('unicorn');
|
||||||
|
// addRules(unicornRules);
|
||||||
|
// config.overrides.push({
|
||||||
|
// files: ['*.test.ts', '*.test.tsx'],
|
||||||
|
// rules: {
|
||||||
|
// 'unicorn/no-useless-undefined': off,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
{
|
||||||
|
name: 'eslint-rules/unicorn',
|
||||||
|
languageOptions: {
|
||||||
|
globals: globals.builtin,
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
unicorn,
|
||||||
|
},
|
||||||
|
rules: unicornRules,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'eslint-rules/unicorn/tests',
|
||||||
|
files: ['*.test.ts', '*.test.tsx'],
|
||||||
|
rules: {
|
||||||
|
'unicorn/no-useless-undefined': off,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
37
src/prettier.ts
Normal file
37
src/prettier.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,22 +0,0 @@
|
|||||||
import Module from 'module';
|
|
||||||
// @ts-expect-error
|
|
||||||
const { name } = (0, require)('./package.json');
|
|
||||||
|
|
||||||
const _resolveFilename = (Module as any)._resolveFilename;
|
|
||||||
const alias = new Set([
|
|
||||||
'eslint-import-resolver-typescript',
|
|
||||||
'eslint-plugin-import',
|
|
||||||
'eslint-plugin-jsx-a11y',
|
|
||||||
'eslint-plugin-local',
|
|
||||||
'eslint-plugin-n',
|
|
||||||
'eslint-plugin-react-hooks',
|
|
||||||
'eslint-plugin-react',
|
|
||||||
'eslint-plugin-rules',
|
|
||||||
]);
|
|
||||||
|
|
||||||
(Module as any)._resolveFilename = function (module: string, ...args: any[]) {
|
|
||||||
if (alias.has(module)) {
|
|
||||||
module = `${name}/${module}`;
|
|
||||||
}
|
|
||||||
return _resolveFilename(module, ...args);
|
|
||||||
};
|
|
@ -1,7 +0,0 @@
|
|||||||
import type { Rule } from 'eslint';
|
|
||||||
|
|
||||||
import noImportDot from "./no-import-dot"
|
|
||||||
|
|
||||||
export const rules: Record<string, Rule.RuleModule> = {
|
|
||||||
"no-import-dot": noImportDot
|
|
||||||
};
|
|
37
src/types.ts
Normal file
37
src/types.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import type { ESLintUtils } from '@typescript-eslint/utils';
|
||||||
|
import type { Rule, Linter } from 'eslint';
|
||||||
|
|
||||||
|
export function defineRules(rules: {
|
||||||
|
[ruleName: string]: Rule.RuleModule | ESLintUtils.RuleModule<string, unknown[]>;
|
||||||
|
}) {
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function defineConfig(config: Linter.Config): Linter.Config;
|
||||||
|
export function defineConfig(config: Linter.Config[]): Linter.Config[];
|
||||||
|
|
||||||
|
export function defineConfig(config: Linter.Config | Linter.Config[]) {
|
||||||
|
if (!config || (Array.isArray(config) && config.some(c => !c))) {
|
||||||
|
console.trace();
|
||||||
|
throw new Error('Config cannot be empty');
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
18
src/types/eslint-plugin-react-refresh.d.ts
vendored
Normal file
18
src/types/eslint-plugin-react-refresh.d.ts
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// eslint-disable-next-line import-x/unambiguous
|
||||||
|
declare module 'eslint-plugin-react-refresh' {
|
||||||
|
import type { TSESLint } from '@typescript-eslint/utils';
|
||||||
|
|
||||||
|
export const rules: {
|
||||||
|
'only-export-components': TSESLint.RuleModule<
|
||||||
|
'exportAll' | 'namedExport' | 'anonymousExport' | 'noExport' | 'localComponents',
|
||||||
|
| []
|
||||||
|
| [
|
||||||
|
{
|
||||||
|
allowConstantExport?: boolean;
|
||||||
|
checkJS?: boolean;
|
||||||
|
allowExportNames?: string[];
|
||||||
|
},
|
||||||
|
]
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
}
|
55
src/types/eslint-plugin-testing-library.d.ts
vendored
Normal file
55
src/types/eslint-plugin-testing-library.d.ts
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// eslint-disable-next-line import-x/unambiguous
|
||||||
|
declare module 'eslint-plugin-testing-library' {
|
||||||
|
import type { Rule, Linter } from 'eslint';
|
||||||
|
|
||||||
|
// 6.3.0
|
||||||
|
const plugin: {
|
||||||
|
meta: {
|
||||||
|
name: 'eslint-plugin-testing-library';
|
||||||
|
version: '6.3.0';
|
||||||
|
};
|
||||||
|
configs: {
|
||||||
|
dom: Linter.BaseConfig;
|
||||||
|
angular: Linter.BaseConfig;
|
||||||
|
react: Linter.BaseConfig;
|
||||||
|
vue: Linter.BaseConfig;
|
||||||
|
marko: Linter.BaseConfig;
|
||||||
|
'flat/dom': Linter.Config;
|
||||||
|
'flat/angular': Linter.Config;
|
||||||
|
'flat/react': Linter.Config;
|
||||||
|
'flat/vue': Linter.Config;
|
||||||
|
'flat/marko': Linter.Config;
|
||||||
|
};
|
||||||
|
rules: {
|
||||||
|
'await-async-events': Rule.RuleModule;
|
||||||
|
'await-async-queries': Rule.RuleModule;
|
||||||
|
'await-async-utils': Rule.RuleModule;
|
||||||
|
'consistent-data-testid': Rule.RuleModule;
|
||||||
|
'no-await-sync-events': Rule.RuleModule;
|
||||||
|
'no-await-sync-queries': Rule.RuleModule;
|
||||||
|
'no-container': Rule.RuleModule;
|
||||||
|
'no-debugging-utils': Rule.RuleModule;
|
||||||
|
'no-dom-import': Rule.RuleModule;
|
||||||
|
'no-global-regexp-flag-in-query': Rule.RuleModule;
|
||||||
|
'no-manual-cleanup': Rule.RuleModule;
|
||||||
|
'no-node-access': Rule.RuleModule;
|
||||||
|
'no-promise-in-fire-event': Rule.RuleModule;
|
||||||
|
'no-render-in-lifecycle': Rule.RuleModule;
|
||||||
|
'no-unnecessary-act': Rule.RuleModule;
|
||||||
|
'no-wait-for-multiple-assertions': Rule.RuleModule;
|
||||||
|
'no-wait-for-side-effects': Rule.RuleModule;
|
||||||
|
'no-wait-for-snapshot': Rule.RuleModule;
|
||||||
|
'prefer-explicit-assert': Rule.RuleModule;
|
||||||
|
'prefer-find-by': Rule.RuleModule;
|
||||||
|
'prefer-implicit-assert': Rule.RuleModule;
|
||||||
|
'prefer-presence-queries': Rule.RuleModule;
|
||||||
|
'prefer-query-by-disappearance': Rule.RuleModule;
|
||||||
|
'prefer-query-matchers': Rule.RuleModule;
|
||||||
|
'prefer-screen-queries': Rule.RuleModule;
|
||||||
|
'prefer-user-event': Rule.RuleModule;
|
||||||
|
'render-result-naming-convention': Rule.RuleModule;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export = plugin;
|
||||||
|
}
|
@ -6,8 +6,8 @@
|
|||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"module": "commonjs",
|
"module": "ESNext",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "Bundler",
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"noImplicitOverride": true,
|
"noImplicitOverride": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
@ -17,11 +17,5 @@
|
|||||||
"stripInternal": true,
|
"stripInternal": true,
|
||||||
"target": "esnext",
|
"target": "esnext",
|
||||||
"useUnknownInCatchVariables": false
|
"useUnknownInCatchVariables": false
|
||||||
},
|
|
||||||
"ts-node": {
|
|
||||||
"transpileOnly": true,
|
|
||||||
"compilerOptions": {
|
|
||||||
"module": "commonjs"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user