Compare commits

..

1 Commits

Author SHA1 Message Date
179cf83891 Inline repo 2024-04-19 21:42:48 -04:00
117 changed files with 15575 additions and 4761 deletions

62
.eslintrc Normal file
View File

@ -0,0 +1,62 @@
{
"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 }]
}
}

View File

@ -1,195 +0,0 @@
module.exports = {
root: true,
plugins: ['unicorn', 'jsdoc', 'import-x'],
env: { node: true, browser: true, es2023: true },
reportUnusedDisableDirectives: true,
parserOptions: {
project: true,
},
ignorePatterns: [],
globals: {},
extends: [
'eslint:recommended',
'prettier',
'plugin:@typescript-eslint/recommended-type-checked',
'plugin:jsdoc/recommended-typescript',
],
overrides: [
{
files: ['repl.ts', 'scripts/**/*.ts'],
rules: { 'no-console': 'off' },
},
{
files: ['.eslintrc.js', '.eslintrc.cjs', '*.config.js', 'index.js'],
extends: ['plugin:@typescript-eslint/disable-type-checked'],
rules: { 'rules/restrict-template-expressions': 'off' },
},
{
files: ['*.d.ts'],
rules: { '@typescript-eslint/consistent-type-imports': 'off' },
},
],
rules: {
'import-x/order': [
'error',
{
groups: [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index',
'object',
],
'newlines-between': 'always-and-inside-groups',
alphabetize: { order: 'asc', caseInsensitive: true },
},
],
'arrow-body-style': ['error', 'as-needed'],
'class-methods-use-this': 'warn',
'func-style': ['error', 'declaration', { allowArrowFunctions: true }],
'no-async-promise-executor': 'off',
'no-case-declarations': 'off',
'no-console': 'warn',
'no-constant-condition': ['error', { checkLoops: false }],
'no-debugger': 'warn',
'no-duplicate-imports': 'off',
'no-empty': ['error', { allowEmptyCatch: true }],
'no-inner-declarations': 'off',
'no-lonely-if': 'error',
'no-restricted-imports': [
'error',
{
paths: [
{
name: 'crypto',
importNames: ['webcrypto'],
message: 'Use global `crypto` instead',
},
],
},
],
'no-template-curly-in-string': 'error',
'no-var': 'error',
'object-shorthand': ['error', 'always', { ignoreConstructors: true }],
'one-var': ['error', { var: 'never', let: 'never', const: 'never' }],
'prefer-arrow-callback': 'off',
'prefer-const': ['error', { destructuring: 'all' }],
'prefer-destructuring': [
'warn',
{ AssignmentExpression: { array: false, object: false } },
],
'prefer-object-spread': 'error',
'prefer-rest-params': 'warn',
'prefer-spread': 'warn',
'quote-props': ['error', 'as-needed'],
'sort-imports': ['warn', { ignoreDeclarationSort: true }],
'spaced-comment': [
'error',
'always',
{ markers: ['/', '#', '@'], block: { exceptions: ['@'] } },
],
complexity: ['warn', { max: 100 }],
curly: ['error', 'multi-line', 'consistent'],
eqeqeq: ['error', 'smart'],
yoda: ['error', 'never', { exceptRange: true }],
'jsdoc/require-jsdoc': 'off',
'@typescript-eslint/ban-ts-comment': [
'error',
{
'ts-expect-error': 'allow-with-description',
'ts-check': false,
'ts-ignore': 'allow-with-description',
'ts-nocheck': 'allow-with-description',
},
],
'@typescript-eslint/consistent-type-imports': [
'error',
{
disallowTypeAnnotations: false,
fixStyle: 'inline-type-imports',
},
],
'@typescript-eslint/explicit-member-accessibility': [
'warn',
{ accessibility: 'no-public' },
],
'@typescript-eslint/no-empty-interface': ['error', { allowSingleExtends: true }],
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-misused-promises': ['error', { checksVoidReturn: false }],
'@typescript-eslint/no-namespace': 'off',
'@typescript-eslint/no-unnecessary-type-assertion': 'error',
'@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-return': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/restrict-template-expressions': 'off',
'@typescript-eslint/triple-slash-reference': 'off',
'@typescript-eslint/unbound-method': 'off',
'unicorn/better-regex': 'error',
'unicorn/consistent-function-scoping': 'warn',
'unicorn/escape-case': 'error',
'unicorn/no-array-for-each': 'warn',
'unicorn/no-array-method-this-argument': 'error',
'unicorn/no-array-push-push': 'warn',
'unicorn/no-console-spaces': 'warn',
'unicorn/no-for-loop': 'warn',
'unicorn/no-instanceof-array': 'error',
'unicorn/no-lonely-if': 'warn',
'unicorn/no-static-only-class': 'error',
'unicorn/no-typeof-undefined': 'error',
'unicorn/no-useless-fallback-in-spread': 'error',
'unicorn/no-useless-promise-resolve-reject': 'error',
'unicorn/no-useless-spread': 'error',
'unicorn/no-useless-switch-case': 'error',
'unicorn/prefer-array-find': 'error',
'unicorn/prefer-array-flat-map': 'error',
'unicorn/prefer-array-some': 'error',
'unicorn/prefer-at': 'error',
'unicorn/prefer-blob-reading-methods': 'error',
'unicorn/prefer-date-now': 'error',
'unicorn/prefer-default-parameters': 'warn',
'unicorn/prefer-dom-node-dataset': 'error',
'unicorn/prefer-dom-node-remove': 'error',
'unicorn/prefer-export-from': ['error', { ignoreUsedVariables: false }],
'unicorn/prefer-includes': 'error',
'unicorn/prefer-keyboard-event-key': 'warn',
'unicorn/prefer-logical-operator-over-ternary': 'warn',
'unicorn/prefer-math-trunc': 'error',
'unicorn/prefer-modern-math-apis': 'error',
'unicorn/prefer-negative-index': 'error',
'unicorn/prefer-node-protocol': 'error',
'unicorn/prefer-object-from-entries': 'error',
'unicorn/prefer-optional-catch-binding': 'error',
'unicorn/prefer-reflect-apply': 'error',
'unicorn/prefer-regexp-test': 'error',
'unicorn/prefer-set-has': 'warn',
'unicorn/prefer-string-slice': 'error',
'unicorn/prefer-string-starts-ends-with': 'warn',
'unicorn/prefer-string-trim-start-end': 'error',
'unicorn/prefer-ternary': 'warn',
'unicorn/string-content': [
'warn',
{
patterns: {
'->': { suggest: '→', fix: false },
'=>': { suggest: '⇒', fix: false },
'<-': { suggest: '←', fix: false },
'<=': { suggest: '≤', fix: false },
'>=': { suggest: '≥', fix: false },
'!=': { suggest: '≠', fix: false },
'<=>': { suggest: '⇔', fix: false },
'\\.\\.\\.': { suggest: '…', fix: false },
"'s ": { suggest: 's ', fix: false },
},
},
],
'unicorn/template-indent': 'warn',
},
parser: '@typescript-eslint/parser',
};

2
.gitignore vendored
View File

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

View File

@ -4,12 +4,6 @@ Personal ESLint config. Guaranteed to have no useless polyfills.
## 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)
- [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) |
- ⏱️ [a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/pull/891)

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

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

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

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

49
dist/index.d.ts vendored
View File

@ -1,27 +1,9 @@
// Generated by dts-bundle-generator v9.4.0
import { ESLintConfig, KnownExtends, Rules, Settings } from '@aet/eslint-define-config';
import { ESLintUtils } from '@typescript-eslint/utils';
import { Rule } from 'eslint';
import { Merge, SetRequired } from 'type-fest';
import { ESLintConfig, Rules } from 'eslint-define-config';
export type OptionalObjectKey<T> = Exclude<{
[Key in keyof T]: undefined | any[] extends T[Key] ? Key : undefined | Record<any, any> extends T[Key] ? Key : never;
}[keyof T], undefined>;
export type MiddlewareConfig = Merge<SetRequired<ESLintConfig, OptionalObjectKey<ESLintConfig>>, {
extends: KnownExtends[];
}>;
export interface MiddlewareFunctions {
addRules(rules: Partial<RuleOptions>): void;
addSettings(settings: Partial<Settings>): void;
}
export type Middleware = (config: MiddlewareConfig, helpers: MiddlewareFunctions) => void;
export declare const graphql: Middleware;
export declare const jsdoc: Middleware;
export declare const storybook: Middleware;
export declare const react: Middleware;
export declare const reactRefresh: Middleware;
export declare const tailwind: Middleware;
export declare const error = "error";
export declare const warn = "warn";
export declare const off = "off";
@ -32,18 +14,16 @@ export type RuleEntry<Options> = RuleLevel | [
];
export interface LocalRuleOptions {
/** Bans import from the specifier '.' and '..' and replaces it with '.+/index' */
"custom/no-import-dot": RuleEntry<unknown>;
"rules/no-import-dot": RuleEntry<unknown>;
/**
* Enforce template literal expressions to be of `string` type
* @see [restrict-template-expressions](https://typescript-eslint.io/rules/restrict-template-expressions)
*/
"custom/restrict-template-expressions": RuleEntry<{
"rules/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>;
"rules/no-empty-object-literal": RuleEntry<unknown>;
}
export type RuleOptions = Rules & Partial<LocalRuleOptions>;
export interface CustomRule {
@ -61,25 +41,18 @@ export type InputConfig = Omit<ESLintConfig, "rules"> & {
* Rules.
* @see [Rules](https://eslint.org/docs/latest/user-guide/configuring/rules)
*/
rules?: Partial<RuleOptions>;
rules?: RuleOptions;
/**
* Glob pattern to find paths to custom rule files in JavaScript or TypeScript.
* Note this must be a string literal or an array of string literals since
* this is statically analyzed.
*
* Rules are prefixed with `custom/` and the file name is used as the rule name.
*/
customRuleFiles?: string | string[];
/**
* Automatically detect project types, dependencies and deduct the plugins.
* @default true
*/
auto?: boolean;
};
/**
* Returns a ESLint config object.
*
* By default, it includes `["@typescript-eslint", "import-x", "prettier", "unicorn"]` configs.
* By default, it includes `["@typescript-eslint", "import", "prettier"]` configs.
* Additional bundled plugins include:
*
* 1. [`react`](https://github.com/jsx-eslint/eslint-plugin-react#list-of-supported-rules)
@ -88,18 +61,12 @@ export type InputConfig = Omit<ESLintConfig, "rules"> & {
* 2. [`react-refresh`](https://github.com/ArnaudBarre/eslint-plugin-react-refresh)
* 3. [`jsx-a11y`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y#supported-rules)
* 4. [`unicorn`](https://github.com/sindresorhus/eslint-plugin-unicorn#rules)
* 5. [`n`](https://github.com/eslint-community/eslint-plugin-n#-rules) (Node.js specific,
* requires `minimatch`)
* 5. [`n`](https://github.com/eslint-community/eslint-plugin-n#-rules) (Node.js specific)
* 6. [`jsdoc`](https://github.com/gajus/eslint-plugin-jsdoc#rules)
*
* Non bundled:
* 1. [`graphql`](https://the-guild.dev/graphql/eslint/rules)
*
* @param of Configuration options.
* @returns ESLint configuration object.
*/
export declare function extendConfig(of?: InputConfig & {
middlewares?: Middleware[];
}): ESLintConfig;
export declare function extendConfig(of?: InputConfig): ESLintConfig;
export {};

77
dist/package.json vendored
View File

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

View File

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

2
dist/types.d.ts vendored
View File

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

1130
dist/yarn.lock vendored Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

Submodule packages/eslint-plugin-import added at f77ceb679d

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

Submodule packages/eslint-plugin-n updated: 6744257b43...eb11b5b35a

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -1,25 +0,0 @@
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,

5939
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -1,8 +1,8 @@
#!/usr/bin/env node
import * as ts from 'typescript';
import {
type EntryPointConfig,
generateDtsBundle,
type EntryPointConfig,
} from 'dts-bundle-generator/dist/bundle-generator';
export function dts({
@ -17,14 +17,22 @@ export function dts({
const entry: EntryPointConfig = {
filePath: source,
failOnClass: false,
libraries: {
importedLibraries: ['eslint-define-config'],
},
output: {
inlineDeclareExternals: false,
inlineDeclareGlobals: false,
sortNodes: false,
noBanner: false,
respectPreserveConstEnum: false,
exportReferencedTypes: true,
},
};
const generatedDts = generateDtsBundle([entry], {
preferredConfigPath: project,
followSymlinks: true,
followSymlinks: false,
});
ts.sys.writeFile(dist, generatedDts[0]);

View File

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

View File

@ -21,6 +21,8 @@ pull() {
}
pull import-js eslint-import-resolver-typescript
pull import-js eslint-plugin-import
pull jsx-eslint eslint-plugin-jsx-a11y
pull eslint-community eslint-plugin-n
pull jsx-eslint eslint-plugin-react
pull jsx-eslint jsx-ast-utils

View File

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

View File

@ -1,33 +1,33 @@
{
"eslint-plugin-import": {
"hash": "6554bd5c30976290024cecc44ef1e96746cf3cf7",
"date": "2024-05-23T12:47:41-07:00",
"hash": "f77ceb679d59ced5d9a633123385470a9eea10d9",
"date": "2024-04-07T12:55:28+12:00",
"committer": "Jordan Harband",
"subject": "[meta] add `repository.directory` field"
"subject": "[actions] cancel in-progress runs on PR updates"
},
"eslint-import-resolver-typescript": {
"hash": "3dfad602a05b4b3812a4d3fc681051932f86e838",
"date": "2024-08-01T01:15:59+00:00",
"hash": "7a02ac08b5aaac8c217f0e87142f97eafcc38fbc",
"date": "2024-04-01T01:06:20+00:00",
"committer": "GitHub",
"subject": "chore(deps): update dependency node to v18.20.4 (#309)"
"subject": "chore(deps): update dependency npm-run-all2 to ^5.0.2 (#277)"
},
"eslint-plugin-jsx-a11y": {
"hash": "a08fbcc502d6a6fa7d01a48c5f0b895c61e8cdd5",
"date": "2024-08-22T20:21:57+01:00",
"hash": "0d5321a5457c5f0da0ca216053cc5b4f571b53ae",
"date": "2024-01-27T22:18:19-08:00",
"committer": "Jordan Harband",
"subject": "[Fix] `label-has-associated-control`: ignore undetermined label text"
"subject": "[Deps] update `@babel/runtime`, `safe-regex-test`"
},
"eslint-plugin-n": {
"hash": "e5e758ea0cd238220127ae7bcbd967f1d8920f28",
"date": "2024-08-06T04:22:42+12:00",
"hash": "eb11b5b35a6a797dc7fba6df53b1c4dada3a2a55",
"date": "2024-04-17T17:40:32+08:00",
"committer": "GitHub",
"subject": "docs(process-exit-as-throw): update wording (#323)"
"subject": "chore: upgrade globals v15 (#241)"
},
"eslint-plugin-react": {
"hash": "983b88dd3cb5e07919517d3fde4085f60883ded7",
"date": "2024-07-24T15:26:33-07:00",
"hash": "4467db503e38b9356517cf6926d11be544ccf4b1",
"date": "2024-03-16T12:54:58+09:00",
"committer": "Jordan Harband",
"subject": "[Tests] `no-array-index-key`: actually run valid tests"
"subject": "[Fix] `boolean-prop-naming`: avoid a crash with a non-TSTypeReference type"
},
"jsx-ast-utils": {
"hash": "5943318eaf23764eec3ff397ebb969613d728a95",

View File

@ -1,45 +0,0 @@
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;

View File

@ -1,87 +0,0 @@
import * as fs from 'node:fs';
import { resolve } from 'node:path';
import type { Middleware } from './middleware';
import { jsdoc } from './presets/jsdoc';
import { reactQuery, storybook, vitest } from './presets/misc';
import { react, reactRefresh } from './presets/react';
import { tailwind } from './presets/tailwind';
import { testingLibrary } from './presets/testing-library';
const middlewares = {
react,
reactRefresh,
tailwind,
storybook,
reactQuery,
testingLibrary,
jsdoc,
vitest,
};
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 = resolve(rootDir, 'package.json');
const pkgJson = fs.existsSync(pkgJsonPath)
? JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'))
: {};
return new Set(
Object.keys({
...pkgJson.dependencies,
...pkgJson.devDependencies,
...pkgJson.peerDependencies,
}),
);
}
export function* checkEnv(): Generator<Middleware> {
const deps = getProjectDependencies();
for (const { dependency, eslintPlugin, middleware } of envs) {
if (deps.has(dependency) && (!eslintPlugin || deps.has(eslintPlugin))) {
yield middlewares[middleware];
}
}
}

View File

@ -1,26 +1,21 @@
/// <reference path="./modules.d.ts" />
import './redirect';
import type { ESLintConfig, Extends, Plugin, Rules } from '@aet/eslint-define-config';
import type { ESLintUtils } from '@typescript-eslint/utils';
import fs from 'node:fs';
import type { Rule } from 'eslint';
import { uniq } from 'lodash';
import { off } from './constants';
import { checkEnv } from './env';
import type { Middleware, MiddlewareConfig, MiddlewareFunctions } from './middleware';
import { custom } from './presets/custom';
import type { ESLintUtils } from '@typescript-eslint/utils';
import type { ESLintConfig, Rules } from 'eslint-define-config';
import { typescriptRules } from './presets/typescript';
import { unicornRules } from './presets/unicorn';
import { eslintRules } from './presets/eslint';
import { stylistic } from './presets/stylistic';
import { importTypeScript } from './presets/typescript';
import { unicorn } from './presets/unicorn';
import { reactRules } from './presets/react';
import { importRules } from './presets/import';
import { jsDocRules } from './presets/jsdoc';
import { graphqlRules } from './presets/graphql';
import { localRules } from './presets/local';
import { error, warn, off } from './constants';
import { tailwindRules } from './presets/tailwind';
export { graphql } from './presets/graphql';
export { jsdoc } from './presets/jsdoc';
export { storybook } from './presets/misc';
export { react, reactRefresh } from './presets/react';
export { tailwind } from './presets/tailwind';
export { error, warn, off } from './constants';
export { error, warn, off };
declare global {
interface Array<T> {
@ -40,18 +35,22 @@ const ensureArray = <T>(value?: T | T[]): T[] =>
type RuleLevel = 'error' | 'warn' | 'off' | 0 | 1 | 2;
type RuleEntry<Options> = RuleLevel | [RuleLevel, Partial<Options>];
declare module 'eslint-define-config' {
interface NoUnknownPropertyOption {
extends: ('next' | 'emotion')[];
}
}
export interface LocalRuleOptions {
/** Bans import from the specifier '.' and '..' and replaces it with '.+/index' */
'custom/no-import-dot': RuleEntry<unknown>;
'rules/no-import-dot': RuleEntry<unknown>;
/**
* Enforce template literal expressions to be of `string` type
* @see [restrict-template-expressions](https://typescript-eslint.io/rules/restrict-template-expressions)
*/
'custom/restrict-template-expressions': RuleEntry<{ allow: string[] }>;
'rules/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>;
'rules/no-empty-object-literal': RuleEntry<unknown>;
}
export type RuleOptions = Rules & Partial<LocalRuleOptions>;
@ -72,28 +71,20 @@ export type InputConfig = Omit<ESLintConfig, 'rules'> & {
* Rules.
* @see [Rules](https://eslint.org/docs/latest/user-guide/configuring/rules)
*/
rules?: Partial<RuleOptions>;
rules?: RuleOptions;
/**
* Glob pattern to find paths to custom rule files in JavaScript or TypeScript.
* Note this must be a string literal or an array of string literals since
* this is statically analyzed.
*
* Rules are prefixed with `custom/` and the file name is used as the rule name.
*/
customRuleFiles?: string | string[];
/**
* Automatically detect project types, dependencies and deduct the plugins.
* @default true
*/
auto?: boolean;
};
/**
* Returns a ESLint config object.
*
* By default, it includes `["@typescript-eslint", "import-x", "prettier", "unicorn"]` configs.
* By default, it includes `["@typescript-eslint", "import", "prettier"]` configs.
* Additional bundled plugins include:
*
* 1. [`react`](https://github.com/jsx-eslint/eslint-plugin-react#list-of-supported-rules)
@ -102,90 +93,122 @@ export type InputConfig = Omit<ESLintConfig, 'rules'> & {
* 2. [`react-refresh`](https://github.com/ArnaudBarre/eslint-plugin-react-refresh)
* 3. [`jsx-a11y`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y#supported-rules)
* 4. [`unicorn`](https://github.com/sindresorhus/eslint-plugin-unicorn#rules)
* 5. [`n`](https://github.com/eslint-community/eslint-plugin-n#-rules) (Node.js specific,
* requires `minimatch`)
* 5. [`n`](https://github.com/eslint-community/eslint-plugin-n#-rules) (Node.js specific)
* 6. [`jsdoc`](https://github.com/gajus/eslint-plugin-jsdoc#rules)
*
* Non bundled:
* 1. [`graphql`](https://the-guild.dev/graphql/eslint/rules)
*
* @param of Configuration options.
* @returns ESLint configuration object.
*/
export function extendConfig(
of: InputConfig & {
middlewares?: Middleware[];
} = {
middlewares: [],
},
): ESLintConfig {
export function extendConfig(of: InputConfig = {}): ESLintConfig {
const {
auto = true,
plugins: _plugins = [],
settings = {},
plugins = [],
settings,
rules,
extends: _extends,
overrides,
customRuleFiles,
parserOptions,
middlewares: _middlewares = [],
...rest
} = of;
const plugins: Plugin[] = [..._plugins];
const extend: Extends[] = ensureArray(_extends);
const hasReact = plugins.includes('react');
const hasReactRefresh = plugins.includes('react-refresh');
const hasUnicorn = plugins.includes('unicorn');
const hasJsDoc = plugins.includes('jsdoc');
const hasGraphQL = plugins.includes('@graphql-eslint');
const hasNext = ensureArray(_extends).some(name => name.includes(':@next/next'));
const hasTailwind = ensureArray(_extends).some(name =>
name.includes('plugin:tailwindcss/'),
);
if (customRuleFiles != null) {
plugins.push('local');
const ruleDir = false; // ?? findCacheDirectory({ name: '_eslint-rules' });
if (ruleDir) {
fs.rmSync(ruleDir, { recursive: true, force: true });
fs.mkdirSync(ruleDir, { recursive: true });
}
const middlewares: Middleware[] = uniq([
importTypeScript,
unicorn,
custom,
stylistic,
...(auto ? checkEnv() : []),
..._middlewares,
]);
const result: MiddlewareConfig = {
const result: InputConfig = {
root: true,
plugins: unique('custom', plugins),
parser: '@typescript-eslint/parser',
plugins: unique('@typescript-eslint', 'import', 'rules', plugins),
env: { node: true, browser: true, es2023: true },
reportUnusedDisableDirectives: true,
parserOptions: {
project: true,
...parserOptions,
},
ignorePatterns: [],
globals: {},
extends: ['eslint:recommended', 'prettier', ...(extend as string[])],
settings,
extends: unique(
'eslint:recommended',
'prettier',
'plugin:@typescript-eslint/recommended-type-checked',
'plugin:import/errors',
'plugin:import/typescript',
hasReact && [
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
],
hasJsDoc && 'plugin:jsdoc/recommended-typescript',
hasGraphQL && 'plugin:@graphql-eslint/recommended',
_extends,
),
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx', '.mts', '.cts'],
},
'import/resolver': {
typescript: {
alwaysTryTypes: true,
},
},
react: {
version: 'detect',
},
...settings,
},
overrides: [
{ files: ['repl.ts', 'scripts/**/*.ts'], rules: { 'no-console': off } },
{
files: ['.eslintrc.js', '.eslintrc.cjs', '*.config.js', 'index.js'],
extends: ['plugin:@typescript-eslint/disable-type-checked'],
rules: {
'rules/restrict-template-expressions': off,
},
},
{
files: ['*.d.ts'],
rules: {
'@typescript-eslint/consistent-type-imports': off,
},
},
{
files: ['repl.ts', 'scripts/**/*.ts'],
rules: {
'no-console': off,
},
},
...(overrides ?? []),
],
rules: { ...eslintRules },
rules: {
...eslintRules,
...typescriptRules,
...importRules,
...localRules,
...(hasReact && {
...reactRules,
'react/no-unknown-property': [
error,
{ ignore: hasNext ? ['css', 'next'] : ['css'] },
],
}),
...(hasReactRefresh && {
'react-refresh/only-export-components': [warn, { allowConstantExport: true }],
}),
...(hasUnicorn && unicornRules),
...(hasJsDoc && jsDocRules),
...(hasGraphQL && graphqlRules),
...(hasTailwind && tailwindRules),
...rules,
},
...rest,
};
const functions: MiddlewareFunctions = {
addRules(newRules) {
Object.assign(result.rules, newRules);
},
addSettings(newSettings) {
Object.assign(result.settings, newSettings);
},
};
for (const fn of middlewares) {
fn(result, functions);
}
result.plugins = unique(result.plugins);
result.extends = unique(result.extends);
Object.assign(result.rules, rules);
return result;
}

View File

@ -1,17 +0,0 @@
import { installPackage } from '@antfu/install-pkg';
import { uniq } from 'lodash';
import { envs, getProjectDependencies } from './env';
const deps = getProjectDependencies();
const packages = uniq(
envs
.filter(_ => deps.has(_.dependency) && _.eslintPlugin && !deps.has(_.eslintPlugin))
.map(_ => _.eslintPlugin!),
);
console.log('Installing missing ESLint plugins.\n');
void installPackage(packages, {
silent: false,
});

View File

@ -1,16 +1,15 @@
import * as fs from 'node:fs';
import { basename, extname, isAbsolute, resolve } from 'node:path';
import type { ESLint } from 'eslint';
import * as fs from 'node:fs';
import { resolve, basename, extname } from 'node:path';
import { glob } from 'fast-glob';
import { parseModule } from 'esprima';
import query from 'esquery';
import type { Node, Property } from 'estree';
import { glob } from 'fast-glob';
// https://github.com/gulpjs/interpret
const transpilers = [
'esbin/register',
'esbuild-register',
'tsx',
'ts-node/register/transpile-only',
'@swc/register',
'sucrase/register',
@ -21,7 +20,6 @@ const transpilers = [
function tryRequire() {
for (const candidate of transpilers) {
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
require(candidate);
return;
} catch {}
@ -67,11 +65,7 @@ function main() {
if (!customRuleFiles?.length) return;
tryRequire();
for (let file of glob.sync(customRuleFiles)) {
if (!isAbsolute(file)) {
file = resolve(rootDir, file);
}
// eslint-disable-next-line @typescript-eslint/no-require-imports
for (const file of glob.sync(customRuleFiles)) {
const module = unwrapDefault(require(file));
const name = module.name ?? basename(file, extname(file));
plugin.rules![name] = module;

View File

@ -1,31 +0,0 @@
import type { ESLintConfig, KnownExtends, Settings } from '@aet/eslint-define-config';
import type { Merge, SetRequired } from 'type-fest';
import type { RuleOptions } from './index';
type OptionalObjectKey<T> = Exclude<
{
[Key in keyof T]: undefined | any[] extends T[Key]
? Key
: undefined | Record<any, any> extends T[Key]
? Key
: never;
}[keyof T],
undefined
>;
export type MiddlewareConfig = Merge<
SetRequired<ESLintConfig, OptionalObjectKey<ESLintConfig>>,
{ extends: KnownExtends[] }
>;
export interface MiddlewareFunctions {
addRules(rules: Partial<RuleOptions>): void;
addSettings(settings: Partial<Settings>): void;
}
export type Middleware = (config: MiddlewareConfig, helpers: MiddlewareFunctions) => void;
export function defineMiddleware(middleware: Middleware): Middleware {
return middleware;
}

View File

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

View File

@ -1,18 +0,0 @@
import { error } from '../constants';
import type { LocalRuleOptions } from '../index';
import { defineMiddleware } from '../middleware';
const customRules: Partial<LocalRuleOptions> = {
'custom/no-import-dot': error,
'custom/no-useless-import-alias': error,
};
export const custom = defineMiddleware((config, { addRules }) => {
addRules(customRules);
config.overrides.push({
files: ['*.ts', '!*.d.ts'],
rules: {
'custom/restrict-template-expressions': error,
},
});
});

View File

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

View File

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

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

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

View File

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

7
src/presets/local.ts Normal file
View File

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

View File

@ -1,13 +0,0 @@
import { defineMiddleware } from '../middleware';
export const storybook = defineMiddleware(config => {
config.extends.push('plugin:storybook/recommended');
});
export const reactQuery = defineMiddleware(config => {
config.extends.push('plugin:@tanstack/eslint-plugin-query/recommended');
});
export const vitest = defineMiddleware(config => {
config.extends.push('plugin:vitest/recommended');
});

View File

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

View File

@ -1,21 +0,0 @@
import type { StylisticRulesObject } from '@aet/eslint-define-config/src/rules/stylistic';
import { error } from '../constants';
import { defineMiddleware } from '../middleware';
const stylisticRules: Partial<StylisticRulesObject> = {
'@stylistic/spaced-comment': [
error,
'always',
{
markers: ['/', '#', '@'],
// allow /*@__PURE__*/
block: { exceptions: ['@'] },
},
],
};
export const stylistic = defineMiddleware((config, { addRules }) => {
config.plugins.push('@stylistic');
addRules(stylisticRules);
});

View File

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

View File

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

View File

@ -1,26 +1,7 @@
import type { ImportXRulesObject } from '@aet/eslint-define-config/src/rules/import-x';
import type { TypeScriptRulesObject } from '@aet/eslint-define-config/src/rules/typescript-eslint';
import { error, off, warn } from '../constants';
import { defineMiddleware } from '../middleware';
import type { TypeScriptRules } from 'eslint-define-config/rules/typescript-eslint';
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,
};
const typescriptRules: Partial<TypeScriptRulesObject> = {
export const typescriptRules: Partial<TypeScriptRules> = {
'@typescript-eslint/ban-ts-comment': [
error,
{
@ -30,6 +11,7 @@ const typescriptRules: Partial<TypeScriptRulesObject> = {
'ts-nocheck': 'allow-with-description',
},
],
'@typescript-eslint/ban-types': [error, { extendDefaults: true }],
'@typescript-eslint/consistent-type-imports': [
error,
{ disallowTypeAnnotations: false, fixStyle: 'inline-type-imports' },
@ -55,42 +37,3 @@ const typescriptRules: Partial<TypeScriptRulesObject> = {
'@typescript-eslint/triple-slash-reference': off,
'@typescript-eslint/unbound-method': off,
};
export const importTypeScript = defineMiddleware((config, { addRules, addSettings }) => {
config.parser = '@typescript-eslint/parser';
config.plugins.push('@typescript-eslint', 'import-x');
config.extends.push(
'plugin:@typescript-eslint/recommended-type-checked',
'plugin:import-x/errors',
'plugin:import-x/typescript',
);
addSettings({
'import-x/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx', '.mts', '.cts'],
},
'import-x/resolver': {
typescript: true,
},
});
config.overrides.push(
{
files: ['.eslintrc.js', '*.config.js', '*.cjs', '*.mjs'],
extends: ['plugin:@typescript-eslint/disable-type-checked'],
rules: {
'import-x/no-commonjs': off,
'import-x/unambiguous': off,
'@typescript-eslint/no-require-imports': off,
},
},
{
files: ['*.d.ts'],
rules: {
'@typescript-eslint/consistent-type-imports': off,
'import-x/unambiguous': off,
},
},
);
addRules(importRules);
addRules(typescriptRules);
});

View File

@ -1,44 +1,31 @@
/* eslint-disable unicorn/string-content */
import type { UnicornRulesObject } from '@aet/eslint-define-config/src/rules/unicorn';
import { error, off, warn } from '../constants';
import { defineMiddleware } from '../middleware';
import { error, warn } from '../constants';
import { UnicornRules } from 'eslint-define-config/rules/unicorn';
const suggest = (suggest: string) => ({ suggest, fix: false });
// https://github.com/sindresorhus/eslint-plugin-unicorn/tree/28e7498ad06679bb92343db53bb40a7b5ba2990a
const unicornRules: Partial<UnicornRulesObject> = {
export const unicornRules: Partial<UnicornRules> = {
'unicorn/better-regex': error,
'unicorn/consistent-destructuring': warn,
'unicorn/consistent-function-scoping': warn,
'unicorn/escape-case': error,
'unicorn/no-array-for-each': warn,
'unicorn/no-array-method-this-argument': error,
'unicorn/no-array-push-push': warn,
'unicorn/no-await-in-promise-methods': error,
'unicorn/no-console-spaces': warn,
'unicorn/no-for-loop': warn,
'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-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-typeof-undefined': error,
'unicorn/no-unnecessary-await': error,
'unicorn/no-unnecessary-polyfills': error,
'unicorn/no-unreadable-array-destructuring': warn,
// 'unicorn/no-unused-properties': warn,
'unicorn/no-useless-fallback-in-spread': error,
'unicorn/no-useless-promise-resolve-reject': error,
'unicorn/no-useless-spread': error,
'unicorn/no-useless-switch-case': error,
'unicorn/no-useless-undefined': error,
'unicorn/no-zero-fractions': error,
'unicorn/number-literal-case': error,
// https://github.com/prettier/eslint-config-prettier/issues/51
// 'unicorn/number-literal-case': error,
'unicorn/prefer-array-find': error,
'unicorn/prefer-array-flat': error,
'unicorn/prefer-array-flat-map': error,
'unicorn/prefer-array-some': error,
'unicorn/prefer-at': error,
@ -47,32 +34,23 @@ const unicornRules: Partial<UnicornRulesObject> = {
'unicorn/prefer-default-parameters': warn,
'unicorn/prefer-dom-node-dataset': error,
'unicorn/prefer-dom-node-remove': error,
'unicorn/prefer-dom-node-text-content': warn,
'unicorn/prefer-export-from': [error, { ignoreUsedVariables: false }],
'unicorn/prefer-includes': error,
'unicorn/prefer-json-parse-buffer': warn,
'unicorn/prefer-keyboard-event-key': warn,
'unicorn/prefer-logical-operator-over-ternary': warn,
'unicorn/prefer-math-trunc': warn,
'unicorn/prefer-modern-dom-apis': error,
'unicorn/prefer-math-trunc': error,
'unicorn/prefer-modern-math-apis': error,
'unicorn/prefer-negative-index': error,
'unicorn/prefer-node-protocol': error,
'unicorn/prefer-object-from-entries': error,
'unicorn/prefer-optional-catch-binding': error,
'unicorn/prefer-prototype-methods': error,
'unicorn/prefer-reflect-apply': error,
'unicorn/prefer-regexp-test': error,
'unicorn/prefer-set-has': warn,
'unicorn/prefer-set-size': error,
'unicorn/prefer-string-raw': error,
'unicorn/prefer-string-slice': error,
'unicorn/prefer-string-starts-ends-with': warn,
'unicorn/prefer-string-trim-start-end': error,
'unicorn/prefer-switch': warn,
'unicorn/prefer-ternary': warn,
'unicorn/relative-url-style': warn,
'unicorn/require-number-to-fixed-digits-argument': error,
'unicorn/string-content': [
warn,
{
@ -86,26 +64,8 @@ const unicornRules: Partial<UnicornRulesObject> = {
'<=>': suggest('⇔'),
'\\.\\.\\.': suggest('…'),
"'s ": suggest('s '),
"'d ": suggest('d '),
"'t ": suggest('t '),
"l'": suggest('l'),
"d'": suggest('d'),
"qu'": suggest('qu'),
'\\?!': suggest('⁈'),
'!\\?': suggest('⁉'),
},
},
],
'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,
},
});
});

View File

@ -1,14 +1,16 @@
import Module from 'node:module';
import Module from 'module';
const { name } = [require][0]('./package.json');
const _resolveFilename = Module._resolveFilename;
const alias = new Set([
'eslint-import-resolver-typescript',
'eslint-plugin-import',
'eslint-plugin-jsx-a11y',
'eslint-plugin-local',
'eslint-plugin-n',
'eslint-plugin-react-hooks',
'eslint-plugin-custom',
'eslint-plugin-react',
'eslint-plugin-rules',
]);
type CDR<T> = T extends [any, ...infer R] ? R : [];

View File

@ -1,17 +1,15 @@
import type { ESLintUtils } from '@typescript-eslint/utils';
import type { Rule } from 'eslint';
import type { ESLintUtils } from '@typescript-eslint/utils';
import noEmptyObjectLiteral from './no-empty-object-literal';
import noImportDot from './no-import-dot';
import noUselessImportAlias from './no-useless-import-alias';
import noEmptyObjectLiteral from './no-empty-object-literal';
import restrictTemplateExpressions from './restrict-template-expressions';
export const rules: Record<
string,
Rule.RuleModule | ESLintUtils.RuleModule<string, unknown[]>
> = {
'no-empty-object-literal': noEmptyObjectLiteral,
'no-import-dot': noImportDot,
'no-useless-import-alias': noUselessImportAlias,
'no-empty-object-literal': noEmptyObjectLiteral,
'restrict-template-expressions': restrictTemplateExpressions,
};

View File

@ -1,14 +1,14 @@
// https://github.com/typescript-eslint/typescript-eslint/blob/75c128856b1ce05a4fec799bfa6de03b3dab03d0/packages/eslint-plugin/src/rules/restrict-template-expressions.ts
import * as ts from 'typescript';
import { ESLintUtils, type TSESTree, AST_NODE_TYPES } from '@typescript-eslint/utils';
import {
getConstrainedTypeAtLocation,
getTypeName,
isTypeAnyType,
isTypeFlagSet,
isTypeNeverType,
getConstrainedTypeAtLocation,
} from '@typescript-eslint/type-utils';
import { AST_NODE_TYPES, ESLintUtils, 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}`,
@ -29,6 +29,8 @@ export default createRule<Option[], MessageId>({
type: 'problem',
docs: {
description: 'Enforce template literal expressions to be of `string` type',
recommended: 'recommended',
requiresTypeChecking: true,
},
messages: {
invalidType: 'Invalid type "{{type}}" of template literal expression.',
@ -53,9 +55,7 @@ export default createRule<Option[], MessageId>({
defaultOptions: [defaultOption],
create(context, [options]) {
const services = getParserServices(context);
if (!services.program) return {};
const checker = services.program.getTypeChecker();
const checker = services.program!.getTypeChecker();
const allowed = new Set(options.allow);
const { StringLike, NumberLike, BigIntLike, BooleanLike, Null, Undefined } =

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

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

View File

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

View File

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

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

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

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