Compare commits

...

15 Commits

Author SHA1 Message Date
0138cabb27 Update rules 2024-08-25 16:28:34 -04:00
b0cc4a1525 Update rules 2024-08-13 03:37:00 -04:00
191848fdca Update 2024-08-11 17:32:54 -04:00
afb93c4971 Fix local rules 2024-08-08 02:59:28 -04:00
25695599aa Update 2024-08-04 21:59:18 -04:00
f3fbf99c0c Update 2024-08-03 23:20:22 -04:00
92e6e5081b chore 2024-08-03 21:00:40 -04:00
e5546e21ad Update 2024-08-02 23:04:37 -04:00
ba20685f2e Bump 2024-08-02 15:51:10 -04:00
3ac5f91988 chore 2024-07-30 23:00:25 -04:00
2853da4344 Update 2024-07-27 22:45:56 -04:00
1e6d679af3 Bump version 2024-06-27 01:19:33 -04:00
674eaf1811 Remove import 2024-06-16 04:50:37 -04:00
c175f87441 Update 2024-05-04 20:44:31 -04:00
003a43512c New rules 2024-04-19 21:54:02 -04:00
57 changed files with 4627 additions and 5985 deletions

View File

@ -1,62 +0,0 @@
{
"root": true,
"env": {
"node": true,
"browser": true,
"es6": true
},
"extends": ["eslint:recommended", "prettier"],
"parserOptions": {
"sourceType": "module",
"ecmaVersion": "latest"
},
"rules": {
"no-restricted-imports": [
"warn",
{
"paths": [
"array-includes",
"array.prototype.flat",
"array.prototype.flatmap",
"array.prototype.tosorted",
"object.entries",
"object.fromentries",
"object.hasown",
"object.values",
"string.prototype.matchall",
"has"
]
}
],
"arrow-body-style": ["error", "as-needed"],
"class-methods-use-this": [
"warn",
{ "exceptMethods": ["toString", "shouldComponentUpdate"] }
],
"complexity": ["warn", { "max": 100 }],
"curly": ["error", "multi-line", "consistent"],
"eqeqeq": ["error", "smart"],
"no-async-promise-executor": "off",
"no-case-declarations": "off",
"no-constant-condition": ["error", { "checkLoops": false }],
"no-debugger": "off",
"no-empty": ["error", { "allowEmptyCatch": true }],
"no-inner-declarations": "off",
"no-lonely-if": "error",
"no-template-curly-in-string": "error",
"no-var": "error",
"object-shorthand": ["error", "always", { "ignoreConstructors": true }],
"one-var": ["error", { "var": "never", "let": "never" }],
"prefer-const": ["error", { "destructuring": "all" }],
"prefer-destructuring": [
"warn",
{ "AssignmentExpression": { "array": false, "object": false } }
],
"prefer-rest-params": "warn",
"prefer-spread": "warn",
"quote-props": ["error", "as-needed"],
"spaced-comment": ["error", "always", { "markers": ["/"] }],
"sort-imports": ["warn", { "ignoreDeclarationSort": true }],
"yoda": ["error", "never", { "exceptRange": true }]
}
}

195
.eslintrc.js Normal file
View File

@ -0,0 +1,195 @@
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',
};

3
.gitignore vendored
View File

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

View File

@ -4,6 +4,12 @@ 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)
- ⏱️ [a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/pull/891)
- [unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn/pull/1886)
- ❌ [import](https://github.com/un-ts/eslint-plugin-import-x/issues/29)
- ❌ [jsx-a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/issues/978, supports flat config)
| Name | Flat Config | Issue |
| ------- | ----------- | ---------------------------------------------------------------------------------------------------------- |
| react | ✅ | [jsx-eslint/eslint-plugin-react#3429](https://github.com/jsx-eslint/eslint-plugin-react/pull/3429) |
| unicorn | ✅ | [sindresorhus/eslint-plugin-unicorn#1886](https://github.com/sindresorhus/eslint-plugin-unicorn/pull/1886) |

View File

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

View File

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

49
dist/index.d.ts vendored
View File

@ -1,9 +1,27 @@
// 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 { ESLintConfig, Rules } from 'eslint-define-config';
import { Merge, SetRequired } from 'type-fest';
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";
@ -14,16 +32,18 @@ export type RuleEntry<Options> = RuleLevel | [
];
export interface LocalRuleOptions {
/** Bans import from the specifier '.' and '..' and replaces it with '.+/index' */
"rules/no-import-dot": RuleEntry<unknown>;
"custom/no-import-dot": RuleEntry<unknown>;
/**
* Enforce template literal expressions to be of `string` type
* @see [restrict-template-expressions](https://typescript-eslint.io/rules/restrict-template-expressions)
*/
"rules/restrict-template-expressions": RuleEntry<{
"custom/restrict-template-expressions": RuleEntry<{
allow: string[];
}>;
/** Ban assignment of empty object literals `{}` and replace them with `Object.create(null)` */
"rules/no-empty-object-literal": RuleEntry<unknown>;
"custom/no-empty-object-literal": RuleEntry<unknown>;
/** Ban useless import alias */
"custom/no-useless-import-alias": RuleEntry<unknown>;
}
export type RuleOptions = Rules & Partial<LocalRuleOptions>;
export interface CustomRule {
@ -41,18 +61,25 @@ export type InputConfig = Omit<ESLintConfig, "rules"> & {
* Rules.
* @see [Rules](https://eslint.org/docs/latest/user-guide/configuring/rules)
*/
rules?: RuleOptions;
rules?: Partial<RuleOptions>;
/**
* Glob pattern to find paths to custom rule files in JavaScript or TypeScript.
* Note this must be a string literal or an array of string literals since
* this is statically analyzed.
*
* Rules are prefixed with `custom/` and the file name is used as the rule name.
*/
customRuleFiles?: string | string[];
/**
* Automatically detect project types, dependencies and deduct the plugins.
* @default true
*/
auto?: boolean;
};
/**
* Returns a ESLint config object.
*
* By default, it includes `["@typescript-eslint", "import", "prettier"]` configs.
* By default, it includes `["@typescript-eslint", "import-x", "prettier", "unicorn"]` configs.
* Additional bundled plugins include:
*
* 1. [`react`](https://github.com/jsx-eslint/eslint-plugin-react#list-of-supported-rules)
@ -61,12 +88,18 @@ 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)
* 5. [`n`](https://github.com/eslint-community/eslint-plugin-n#-rules) (Node.js specific,
* requires `minimatch`)
* 6. [`jsdoc`](https://github.com/gajus/eslint-plugin-jsdoc#rules)
*
* Non bundled:
* 1. [`graphql`](https://the-guild.dev/graphql/eslint/rules)
*
* @param of Configuration options.
* @returns ESLint configuration object.
*/
export declare function extendConfig(of?: InputConfig): ESLintConfig;
export declare function extendConfig(of?: InputConfig & {
middlewares?: Middleware[];
}): ESLintConfig;
export {};

67
dist/package.json vendored
View File

@ -1,47 +1,64 @@
{
"name": "@aet/eslint-rules",
"version": "0.0.24-beta.1",
"version": "1.0.1-beta.42",
"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": "^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",
"@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",
"aria-query": "^5.3.0",
"axe-core": "^4.9.0",
"axobject-query": "^4.0.0",
"axe-core": "^4.10.0",
"axobject-query": "4.1.0",
"damerau-levenshtein": "1.0.8",
"debug": "^4.3.4",
"debug": "^4.3.6",
"doctrine": "^3.0.0",
"emoji-regex": "^10.3.0",
"enhanced-resolve": "^5.16.0",
"enhanced-resolve": "^5.17.1",
"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": "^7.6.0",
"eslint-plugin-jsdoc": "^48.2.3",
"eslint-plugin-unicorn": "^52.0.0",
"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",
"esprima": "^4.0.1",
"esquery": "^1.5.0",
"esquery": "^1.6.0",
"estraverse": "^5.3.0",
"fast-glob": "^3.3.2",
"get-tsconfig": "^4.7.3",
"ignore": "^5.3.1",
"is-builtin-module": "^3.2.1",
"get-tsconfig": "^4.7.6",
"is-bun-module": "^1.1.0",
"ignore": "^5.3.2",
"is-glob": "^4.0.3",
"language-tags": "^1.0.9",
"lodash": "^4.17.21",
"minimatch": "^9.0.4",
"resolve": "^2.0.0-next.5",
"semver": "^7.6.0",
"tsconfig-paths": "^4.2.0"
"minimatch": "^10.0.1",
"semver": "^7.6.3"
},
"pnpm": {
"overrides": {
"is-core-module": "file:./overrides/is-core-module",
"supports-preserve-symlinks-flag": "file:./overrides/supports-preserve-symlinks-flag"
}
},
"overrides": {
"is-core-module": "file:./overrides/is-core-module",
@ -50,11 +67,5 @@
"resolutions": {
"**/is-core-module": "file:./overrides/is-core-module",
"**/supports-preserve-symlinks-flag": "file:./overrides/supports-preserve-symlinks-flag"
},
"pnpm": {
"overrides": {
"is-core-module": "file:./overrides/is-core-module",
"supports-preserve-symlinks-flag": "file:./overrides/supports-preserve-symlinks-flag"
}
}
}

2
dist/print-config.sh vendored Normal file
View File

@ -0,0 +1,2 @@
#!/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[], ESLintUtils.RuleListener>;
[ruleName: string]: Rule.RuleModule | ESLintUtils.RuleModule<string, unknown[], unknown, ESLintUtils.RuleListener>;
};
export declare function defineRule({ name, create, ...meta }: Rule.RuleMetaData & {
name?: string;

1130
dist/yarn.lock vendored

File diff suppressed because it is too large Load Diff

View File

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

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

Submodule packages/eslint-plugin-import deleted from 6b95a02193

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

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

Submodule packages/eslint-plugin-react deleted from ecadb92609

View File

@ -1,28 +1,10 @@
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
deleted file mode 100644
index a303861..0000000
index 39c6900..6fd8822 100644
--- a/tsconfig.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
+++ b/tsconfig.json
@@ -1,5 +1,4 @@
{
- "extends": "@1stg/tsconfig/node16",
- "compilerOptions": {
- "module": "Node16",
- "outDir": "./lib"
- },
- "include": ["./src", "./shim.d.ts"]
-}
"compilerOptions": {
"module": "Node16",
"outDir": "./lib",

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,16 @@
diff --git a/src/index.js b/src/index.js
index 7b931fe..eaea267 100644
index 2fa185f..29d65d0 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,296 +1,344 @@
@@ -1,48 +1,90 @@
/* eslint-disable global-require */
+// @ts-check
-const flatConfigBase = require('./configs/flat-config-base');
-const legacyConfigBase = require('./configs/legacy-config-base');
-const { name, version } = require('../package.json');
+import flatConfigBase from './configs/flat-config-base';
+import legacyConfigBase from './configs/legacy-config-base';
+import { name, version } from '../package.json';
+
+import accessibleEmoji from './rules/accessible-emoji';
+import altText from './rules/alt-text';
+import anchorAmbiguousText from './rules/anchor-ambiguous-text';
@ -45,8 +51,7 @@ index 7b931fe..eaea267 100644
+import scope from './rules/scope';
+import tabindexNoPositive from './rules/tabindex-no-positive';
-module.exports = {
- rules: {
const allRules = {
- 'accessible-emoji': require('./rules/accessible-emoji'),
- 'alt-text': require('./rules/alt-text'),
- 'anchor-ambiguous-text': require('./rules/anchor-ambiguous-text'),
@ -86,536 +91,65 @@ index 7b931fe..eaea267 100644
- '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,
+ '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,
+ lang,
+ mediaHasCaption,
+ mouseEventsHaveKeyEvents,
+ noAccessKey,
+ noAriaHiddenOnFocusable,
+ noAutofocus,
+ noDistractingElements,
+ '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,
+ noNoninteractiveElementInteractions,
+ 'no-noninteractive-element-interactions': noNoninteractiveElementInteractions,
+ 'no-noninteractive-element-to-interactive-role':
+ noNoninteractiveElementToInteractiveRole,
+ noNoninteractiveTabindex,
+ noOnChange,
+ noRedundantRoles,
+ noStaticElementInteractions,
+ preferTagOverRole,
+ roleHasRequiredAriaProps,
+ roleSupportsAriaProps,
+ '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,
+ scope,
+ 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',
},
},
+ 'tabindex-no-positive': tabindexNoPositive,
};
+
+/** @param {object} obj */
+function kebabCase(obj) {
+ return Object.fromEntries(
+ Object.entries(obj).map(([key, value]) => [
+ key.replace(/([A-Z])/g, '-$1').toLowerCase(),
+ value,
+ ]),
+ );
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 },
});
diff --git a/src/util/mayContainChildComponent.js b/src/util/mayContainChildComponent.js
index 43a03ef..5e1035e 100644
--- a/src/util/mayContainChildComponent.js

View File

@ -27,3 +27,98 @@ 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

@ -1,336 +0,0 @@
diff --git a/.eslintrc b/.eslintrc
deleted file mode 100644
index 4991f200..00000000
--- a/.eslintrc
+++ /dev/null
@@ -1,82 +0,0 @@
-{
- "root": true,
- "extends": ["airbnb-base", "plugin:eslint-plugin/recommended"],
- "plugins": ["eslint-plugin"],
- "env": {
- "es6": true,
- "node": true
- },
- "parserOptions": {
- "ecmaVersion": 6,
- "ecmaFeatures": {
- "jsx": true
- },
- "sourceType": "script",
- },
- "ignorePatterns": [
- "coverage/",
- ".nyc_output/",
- ],
- "rules": {
- "comma-dangle": [2, "always-multiline"],
- "object-shorthand": [2, "always", {
- "ignoreConstructors": false,
- "avoidQuotes": false, // this is the override vs airbnb
- }],
- "max-len": [2, 120, {
- "ignoreStrings": true,
- "ignoreTemplateLiterals": true,
- "ignoreComments": true,
- }],
- "consistent-return": 0,
-
- "prefer-destructuring": [2, { "array": false, "object": false }, { "enforceForRenamedProperties": false }],
- "prefer-object-spread": 0, // until node 8 is required
- "prefer-rest-params": 0, // until node 6 is required
- "prefer-spread": 0, // until node 6 is required
- "function-call-argument-newline": 1, // TODO: enable
- "function-paren-newline": 0,
- "no-plusplus": [2, {"allowForLoopAfterthoughts": true}],
- "no-param-reassign": 1,
- "no-restricted-syntax": [2, {
- "selector": "ObjectPattern",
- "message": "Object destructuring is not compatible with Node v4"
- }],
- "strict": [2, "safe"],
- "valid-jsdoc": [2, {
- "requireReturn": false,
- "requireParamDescription": false,
- "requireReturnDescription": false,
- }],
-
- "eslint-plugin/consistent-output": 0,
- "eslint-plugin/require-meta-docs-description": [2, { "pattern": "^(Enforce|Require|Disallow)" }],
- "eslint-plugin/require-meta-schema": 0,
- "eslint-plugin/require-meta-type": 0
- },
- "overrides": [
- {
- "files": "tests/**",
- "rules": {
- "no-template-curly-in-string": 1,
- },
- },
- {
- "files": "markdown.config.js",
- "rules": {
- "no-console": 0,
- },
- },
- {
- "files": ".github/workflows/*.js",
- "parserOptions": {
- "ecmaVersion": 2019,
- },
- "rules": {
- "camelcase": 0,
- "no-console": 0,
- "no-restricted-syntax": 0,
- },
- },
- ],
-}
diff --git a/index.js b/index.js
index 4140c6c8..03e623af 100644
--- a/index.js
+++ b/index.js
@@ -1,31 +1,25 @@
-'use strict';
-
-const configAll = require('./configs/all');
-const configRecommended = require('./configs/recommended');
-const configRuntime = require('./configs/jsx-runtime');
-
-const allRules = require('./lib/rules');
+import configAll from './configs/all';
+import configRecommended from './configs/recommended';
+import configRuntime from './configs/jsx-runtime';
+import { name } from './package.json';
+export { default as rules } from './lib/rules';
// for legacy config system
-const plugins = [
- 'react',
-];
+const plugins = [name];
+
+export const deprecatedRules = configAll.plugins.react.deprecatedRules;
-module.exports = {
- deprecatedRules: configAll.plugins.react.deprecatedRules,
- rules: allRules,
- configs: {
- recommended: Object.assign({}, configRecommended, {
- parserOptions: configRecommended.languageOptions.parserOptions,
- plugins,
- }),
- all: Object.assign({}, configAll, {
- parserOptions: configAll.languageOptions.parserOptions,
- plugins,
- }),
- 'jsx-runtime': Object.assign({}, configRuntime, {
- parserOptions: configRuntime.languageOptions.parserOptions,
- plugins,
- }),
- },
+export const configs = {
+ recommended: Object.assign({}, configRecommended, {
+ parserOptions: configRecommended.languageOptions.parserOptions,
+ plugins,
+ }),
+ all: Object.assign({}, configAll, {
+ parserOptions: configAll.languageOptions.parserOptions,
+ plugins,
+ }),
+ 'jsx-runtime': Object.assign({}, configRuntime, {
+ parserOptions: configRuntime.languageOptions.parserOptions,
+ plugins,
+ }),
};
diff --git a/lib/rules/button-has-type.js b/lib/rules/button-has-type.js
index 204a33c4..01d992c2 100644
--- a/lib/rules/button-has-type.js
+++ b/lib/rules/button-has-type.js
@@ -5,8 +5,7 @@
'use strict';
-const getProp = require('jsx-ast-utils/getProp');
-const getLiteralPropValue = require('jsx-ast-utils/getLiteralPropValue');
+const { getProp, getLiteralPropValue } = require('jsx-ast-utils');
const docsUrl = require('../util/docsUrl');
const isCreateElement = require('../util/isCreateElement');
const report = require('../util/report');
diff --git a/lib/rules/jsx-fragments.js b/lib/rules/jsx-fragments.js
index 38b4dd8b..d0575572 100644
--- a/lib/rules/jsx-fragments.js
+++ b/lib/rules/jsx-fragments.js
@@ -5,7 +5,7 @@
'use strict';
-const elementType = require('jsx-ast-utils/elementType');
+import { elementType } from 'jsx-ast-utils';
const pragmaUtil = require('../util/pragma');
const variableUtil = require('../util/variable');
const testReactVersion = require('../util/version').testReactVersion;
diff --git a/lib/rules/jsx-key.js b/lib/rules/jsx-key.js
index 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

@ -0,0 +1,25 @@
diff --git a/dist/parseSettings/createParseSettings.js b/dist/parseSettings/createParseSettings.js
index 4c8b40ae895d45bd7dfcf64c8e49e29ce48dd663..0a62880ff50b7341fa909155293cbdb77fa99c97 100644
--- a/dist/parseSettings/createParseSettings.js
+++ b/dist/parseSettings/createParseSettings.js
@@ -1,4 +1,5 @@
"use strict";
+var fs = require("node:fs");
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -89,10 +90,12 @@ function createParseSettings(code, tsestreeOptions = {}) {
tsestreeOptions.extraFileExtensions.every(ext => typeof ext === 'string')
? tsestreeOptions.extraFileExtensions
: [],
- filePath: (0, shared_1.ensureAbsolutePath)(typeof tsestreeOptions.filePath === 'string' &&
+ filePath: fs.realpathSync(
+ (0, shared_1.ensureAbsolutePath)(typeof tsestreeOptions.filePath === 'string' &&
tsestreeOptions.filePath !== '<input>'
? tsestreeOptions.filePath
- : getFileName(tsestreeOptions.jsx), tsconfigRootDir),
+ : getFileName(tsestreeOptions.jsx), tsconfigRootDir)
+ ),
jsDocParsingMode,
jsx: tsestreeOptions.jsx === true,
loc: tsestreeOptions.loc === true,

5631
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,13 +1,17 @@
#!/usr/bin/env tsx
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 '../src/build-local-rules';
import { buildLocalRules } from './build-local-rules';
import { dts } from './dts';
import { babelPlugin } from './modifier';
@ -65,14 +69,15 @@ if (process.env.DEBUG) {
});
}
function bundle(
async function bundle(
entry: string,
outfile = entry
.replace('./packages/', './dist/')
.replace('src/', '')
.replace('.ts', '.js'),
options?: esbuild.BuildOptions & { treeShaking?: boolean },
) {
return esbuild.build({
const output = await esbuild.build({
entryPoints: [entry],
outfile,
bundle: true,
@ -87,7 +92,32 @@ 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() {
@ -124,31 +154,38 @@ function bundleType(source: string, output: string) {
}
async function main() {
console.log('Building local rules...');
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');
console.log('Building packages...');
const unminify = { minify: false };
console.log('Building packages…');
await Promise.all([
bundle('./packages/eslint-plugin-react/index.js'),
bundle('./packages/eslint-plugin-import/src/index.js'),
bundle('./packages/eslint-plugin-jsx-a11y/src/index.js'),
bundle('./packages/eslint-plugin-react-hooks/index.ts'),
bundle('./packages/eslint-plugin-n/lib/index.js', './dist/eslint-plugin-n/index.js'),
bundle('./packages/eslint-import-resolver-typescript/src/index.ts'),
bundle('./src/rules/index.ts', './dist/eslint-plugin-rules/index.js'),
bundle('./src/custom/index.ts', './dist/eslint-plugin-custom/index.js'),
bundle('./src/local/index.ts', './dist/eslint-plugin-local/index.js'),
bundle('./src/index.ts', './dist/index.js'),
bundle('./src/types.ts', './dist/types.js'),
bundle('./src/prettier.ts', './dist/prettier.js'),
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 */',
},
}),
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,14 +1,15 @@
#!/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, overrides } from '../dist/package.json';
import { dependencies, peerDependencies } from '../dist/package.json';
function checkImports() {
const deps = Object.keys({ ...dependencies, ...peerDependencies, ...overrides }).concat(
'eslint',
);
const deps = Object.keys({ ...dependencies, ...peerDependencies }).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 {
generateDtsBundle,
type EntryPointConfig,
generateDtsBundle,
} from 'dts-bundle-generator/dist/bundle-generator';
export function dts({
@ -18,11 +18,6 @@ export function dts({
filePath: source,
failOnClass: false,
output: {
inlineDeclareExternals: false,
inlineDeclareGlobals: false,
sortNodes: false,
noBanner: false,
respectPreserveConstEnum: false,
exportReferencedTypes: true,
},
};

View File

@ -1,25 +1,15 @@
#!/usr/bin/env tsx
import assert from 'node:assert';
import { readFileSync, promises as fs } from 'node:fs';
import { resolve, extname, relative } from 'node:path';
import { isBuiltin } from 'node:module';
import esbuild from 'esbuild';
import { readFileSync } from 'node:fs';
import { resolve, extname } from 'node:path';
import type { Loader, Plugin } from 'esbuild';
import * as babel from '@babel/core';
import { memoize } from 'lodash';
import { gray, green } from 'picocolors';
import type { types as t, types } from '@babel/core';
import { dependencies } from '../dist/package.json';
import { createMacro, type MacroHandler } from 'babel-plugin-macros';
import * as polyfill from '../src/polyfill';
import { buildLocalRules } from '../src/build-local-rules';
import { dts } from './dts';
const polyfills = Object.keys(polyfill);
const ENV = (process.env.NODE_ENV ??= 'production');
const PROD = ENV === 'production';
class HandlerMap {
map = new Map<string, MacroHandler>();

View File

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

View File

@ -1,8 +1,9 @@
import type { Rule } from 'eslint';
import type { ESLintUtils } from '@typescript-eslint/utils';
import type { Rule } from 'eslint';
import noEmptyObjectLiteral from './no-empty-object-literal';
import noImportDot from './no-import-dot';
import noUselessImportAlias from './no-useless-import-alias';
import restrictTemplateExpressions from './restrict-template-expressions';
export const rules: Record<
@ -11,5 +12,6 @@ export const rules: Record<
> = {
'no-empty-object-literal': noEmptyObjectLiteral,
'no-import-dot': noImportDot,
'no-useless-import-alias': noUselessImportAlias,
'restrict-template-expressions': restrictTemplateExpressions,
};

View File

@ -0,0 +1,45 @@
import type { Rule } from 'eslint';
import type { Position } from 'estree';
const rule: Rule.RuleModule = {
meta: {
type: 'problem',
docs: {
description:
"Ban useless import aliasing like `import { abc as abc } from 'module'`",
category: 'Best Practices',
recommended: true,
},
fixable: 'code',
},
create(context) {
return {
ImportDeclaration(node) {
if (node.specifiers.length === 0) return;
for (const specifier of node.specifiers) {
if (specifier.type !== 'ImportSpecifier') continue;
const { imported, local } = specifier;
if (
imported.name === local.name &&
!arePositionsEqual(imported.loc!.start, local.loc!.start)
) {
context.report({
node: specifier,
message: `Useless aliasing of '${imported.name}'?`,
fix(fixer) {
return fixer.removeRange([imported.range![1], local.range![1]]);
},
});
}
}
},
};
},
};
const arePositionsEqual = (a: Position, b: Position) =>
a.line === b.line && a.column === b.column;
export default rule;

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,8 +29,6 @@ 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.',
@ -55,7 +53,9 @@ export default createRule<Option[], MessageId>({
defaultOptions: [defaultOption],
create(context, [options]) {
const services = getParserServices(context);
const checker = services.program!.getTypeChecker();
if (!services.program) return {};
const checker = services.program.getTypeChecker();
const allowed = new Set(options.allow);
const { StringLike, NumberLike, BigIntLike, BooleanLike, Null, Undefined } =

87
src/env.ts Normal file
View File

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

17
src/install.ts Normal file
View File

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

31
src/middleware.ts Normal file
View File

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

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

18
src/presets/custom.ts Normal file
View File

@ -0,0 +1,18 @@
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,9 +1,12 @@
import { error, warn, off } from '../constants';
import { EslintRules } from 'eslint-define-config/src/rules/eslint';
import type { EslintRulesObject } from '@aet/eslint-define-config/src/rules/eslint';
export const eslintRules: Partial<EslintRules> = {
import { error, off, warn } from '../constants';
import restrictedGlobals from './_restrictedGlobals.json';
export const eslintRules: Partial<EslintRulesObject> = {
'arrow-body-style': [error, 'as-needed'],
'class-methods-use-this': off,
'class-methods-use-this': warn,
'func-style': [error, 'declaration', { allowArrowFunctions: true }],
'no-async-promise-executor': off,
'no-case-declarations': off,
@ -14,7 +17,7 @@ export const eslintRules: Partial<EslintRules> = {
'no-empty': [error, { allowEmptyCatch: true }],
'no-inner-declarations': off,
'no-lonely-if': error,
'no-restricted-globals': [error, 'event', 'name', 'length'],
'no-restricted-globals': [error, ...restrictedGlobals],
'no-restricted-imports': [
error,
{
@ -30,7 +33,7 @@ export const eslintRules: Partial<EslintRules> = {
'no-template-curly-in-string': error,
'no-var': error,
'object-shorthand': [error, 'always', { ignoreConstructors: true }],
'one-var': [error, { var: 'never', let: 'never' }],
'one-var': [error, { var: 'never', let: 'never', const: 'never' }],
'prefer-arrow-callback': error,
'prefer-const': [error, { destructuring: 'all' }],
'prefer-destructuring': [
@ -42,7 +45,6 @@ export const eslintRules: Partial<EslintRules> = {
'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,4 +1,12 @@
import { GraphQLRules } from 'eslint-define-config/src/rules/graphql-eslint';
import type { GraphQLRulesObject } from '@aet/eslint-define-config/src/rules/graphql-eslint';
import { defineMiddleware } from '../middleware';
// https://the-guild.dev/graphql/eslint/rules
export const graphqlRules: Partial<GraphQLRules> = {};
const graphqlRules: Partial<GraphQLRulesObject> = {};
export const graphql = defineMiddleware((config, { addRules }) => {
config.plugins.push('@graphql-eslint');
config.extends.push('plugin:@graphql-eslint/recommended');
addRules(graphqlRules);
});

View File

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

View File

@ -1,3 +1,14 @@
import { JSDocRules } from 'eslint-define-config/src/rules/jsdoc';
import type { JSDocRulesObject } from '@aet/eslint-define-config/src/rules/jsdoc';
export const jsDocRules: Partial<JSDocRules> = {};
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);
});

View File

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

13
src/presets/misc.ts Normal file
View File

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

21
src/presets/stylistic.ts Normal file
View File

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

View File

@ -0,0 +1,13 @@
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,7 +1,26 @@
import { error, off, warn } from '../constants';
import type { TypeScriptRules } from 'eslint-define-config/src/rules/typescript-eslint';
import type { ImportXRulesObject } from '@aet/eslint-define-config/src/rules/import-x';
import type { TypeScriptRulesObject } from '@aet/eslint-define-config/src/rules/typescript-eslint';
export const typescriptRules: Partial<TypeScriptRules> = {
import { error, off, warn } from '../constants';
import { defineMiddleware } from '../middleware';
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> = {
'@typescript-eslint/ban-ts-comment': [
error,
{
@ -11,7 +30,6 @@ export const typescriptRules: Partial<TypeScriptRules> = {
'ts-nocheck': 'allow-with-description',
},
],
'@typescript-eslint/ban-types': [error, { extendDefaults: true }],
'@typescript-eslint/consistent-type-imports': [
error,
{ disallowTypeAnnotations: false, fixStyle: 'inline-type-imports' },
@ -37,3 +55,42 @@ export const typescriptRules: Partial<TypeScriptRules> = {
'@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,31 +1,44 @@
import { error, warn } from '../constants';
import { UnicornRules } from 'eslint-define-config/src/rules/unicorn';
/* 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';
const suggest = (suggest: string) => ({ suggest, fix: false });
// https://github.com/sindresorhus/eslint-plugin-unicorn/tree/28e7498ad06679bb92343db53bb40a7b5ba2990a
export const unicornRules: Partial<UnicornRules> = {
const unicornRules: Partial<UnicornRulesObject> = {
'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-unused-properties': warn,
'unicorn/no-unnecessary-await': error,
'unicorn/no-unnecessary-polyfills': error,
'unicorn/no-unreadable-array-destructuring': warn,
'unicorn/no-useless-fallback-in-spread': error,
'unicorn/no-useless-promise-resolve-reject': error,
'unicorn/no-useless-spread': error,
'unicorn/no-useless-switch-case': error,
// https://github.com/prettier/eslint-config-prettier/issues/51
// 'unicorn/number-literal-case': error,
'unicorn/no-useless-undefined': error,
'unicorn/no-zero-fractions': error,
'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,
@ -34,23 +47,32 @@ export const unicornRules: Partial<UnicornRules> = {
'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': error,
'unicorn/prefer-math-trunc': warn,
'unicorn/prefer-modern-dom-apis': 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,
{
@ -64,8 +86,26 @@ export const unicornRules: Partial<UnicornRules> = {
'<=>': 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,16 +1,14 @@
import Module from 'module';
import Module from 'node:module';
const { name } = [require][0]('./package.json');
const _resolveFilename = Module._resolveFilename;
const alias = new Set([
'eslint-import-resolver-typescript',
'eslint-plugin-import',
'eslint-plugin-jsx-a11y',
'eslint-plugin-local',
'eslint-plugin-n',
'eslint-plugin-react-hooks',
'eslint-plugin-react',
'eslint-plugin-rules',
'eslint-plugin-custom',
]);
type CDR<T> = T extends [any, ...infer R] ? R : [];