Upgrade to ESLint 9
This commit is contained in:
259
src/index.ts
259
src/index.ts
@ -1,191 +1,104 @@
|
||||
/// <reference path="./modules.d.ts" />
|
||||
import './redirect';
|
||||
import type { ESLintConfig, Extends, Plugin, Rules } from '@aet/eslint-define-config';
|
||||
import type { ESLintUtils } from '@typescript-eslint/utils';
|
||||
import type { Rule } from 'eslint';
|
||||
import { uniq } from 'lodash';
|
||||
import type { FlatESLintConfig } from '@aet/eslint-define-config';
|
||||
import * as tsParser from '@typescript-eslint/parser';
|
||||
import importPlugin from 'eslint-plugin-import-x';
|
||||
import { uniq } from 'lodash-es';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
import { off } from './constants';
|
||||
import { checkEnv } from './env';
|
||||
import type { Middleware, MiddlewareConfig, MiddlewareFunctions } from './middleware';
|
||||
import { custom } from './presets/custom';
|
||||
import { checkEnv } from './environment';
|
||||
import { Middleware } from './middleware';
|
||||
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';
|
||||
import stylistic from './presets/stylistic';
|
||||
import { importRules, typescriptRules } from './presets/typescript';
|
||||
import unicorn from './presets/unicorn';
|
||||
|
||||
export { error, warn, off } from './constants';
|
||||
|
||||
declare global {
|
||||
interface Array<T> {
|
||||
filter(
|
||||
predicate: BooleanConstructor,
|
||||
): Exclude<T, null | undefined | false | '' | 0>[];
|
||||
}
|
||||
}
|
||||
|
||||
const unique = (...arr: (false | undefined | string | string[])[]): string[] => [
|
||||
...new Set(arr.flat(1).filter(Boolean)),
|
||||
];
|
||||
|
||||
const ensureArray = <T>(value?: T | T[]): T[] =>
|
||||
value == null ? [] : Array.isArray(value) ? value : [value];
|
||||
|
||||
type RuleLevel = 'error' | 'warn' | 'off' | 0 | 1 | 2;
|
||||
type RuleEntry<Options> = RuleLevel | [RuleLevel, Partial<Options>];
|
||||
|
||||
export interface LocalRuleOptions {
|
||||
/** Bans import from the specifier '.' and '..' and replaces it with '.+/index' */
|
||||
'custom/no-import-dot': RuleEntry<unknown>;
|
||||
/**
|
||||
* Enforce template literal expressions to be of `string` type
|
||||
* @see [restrict-template-expressions](https://typescript-eslint.io/rules/restrict-template-expressions)
|
||||
*/
|
||||
'custom/restrict-template-expressions': RuleEntry<{ allow: string[] }>;
|
||||
/** Ban assignment of empty object literals `{}` and replace them with `Object.create(null)` */
|
||||
'custom/no-empty-object-literal': RuleEntry<unknown>;
|
||||
/** Ban useless import alias */
|
||||
'custom/no-useless-import-alias': RuleEntry<unknown>;
|
||||
}
|
||||
|
||||
export type RuleOptions = Rules & Partial<LocalRuleOptions>;
|
||||
|
||||
export interface CustomRule {
|
||||
rule: () => Promise<{
|
||||
default: Rule.RuleModule | ESLintUtils.RuleModule<string, unknown[]>;
|
||||
}>;
|
||||
options?: RuleLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* ESLint Configuration.
|
||||
* @see [ESLint Configuration](https://eslint.org/docs/latest/user-guide/configuring/)
|
||||
*/
|
||||
export type InputConfig = Omit<ESLintConfig, 'rules'> & {
|
||||
/**
|
||||
* Rules.
|
||||
* @see [Rules](https://eslint.org/docs/latest/user-guide/configuring/rules)
|
||||
*/
|
||||
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
|
||||
*/
|
||||
export async function extendConfig({
|
||||
auto = true,
|
||||
middlewares: addMiddlewares = [],
|
||||
configs = [],
|
||||
}: {
|
||||
auto?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a ESLint config object.
|
||||
*
|
||||
* By default, it includes `["@typescript-eslint", "import-x", "prettier", "unicorn"]` configs.
|
||||
* Additional bundled plugins include:
|
||||
*
|
||||
* 1. [`react`](https://github.com/jsx-eslint/eslint-plugin-react#list-of-supported-rules)
|
||||
* (automatically enables
|
||||
* [`react-hooks`](https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks))
|
||||
* 2. [`react-refresh`](https://github.com/ArnaudBarre/eslint-plugin-react-refresh)
|
||||
* 3. [`jsx-a11y`](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y#supported-rules)
|
||||
* 4. [`unicorn`](https://github.com/sindresorhus/eslint-plugin-unicorn#rules)
|
||||
* 5. [`n`](https://github.com/eslint-community/eslint-plugin-n#-rules) (Node.js specific,
|
||||
* requires `minimatch`)
|
||||
* 6. [`jsdoc`](https://github.com/gajus/eslint-plugin-jsdoc#rules)
|
||||
*
|
||||
* Non bundled:
|
||||
* 1. [`graphql`](https://the-guild.dev/graphql/eslint/rules)
|
||||
*
|
||||
* @param of Configuration options.
|
||||
* @returns ESLint configuration object.
|
||||
*/
|
||||
export function extendConfig(
|
||||
of: InputConfig & {
|
||||
middlewares?: Middleware[];
|
||||
} = {
|
||||
middlewares: [],
|
||||
},
|
||||
): ESLintConfig {
|
||||
const {
|
||||
auto = true,
|
||||
plugins: _plugins = [],
|
||||
settings = {},
|
||||
rules,
|
||||
extends: _extends,
|
||||
overrides,
|
||||
customRuleFiles,
|
||||
parserOptions,
|
||||
middlewares: _middlewares = [],
|
||||
...rest
|
||||
} = of;
|
||||
|
||||
const plugins: Plugin[] = [..._plugins];
|
||||
const extend: Extends[] = ensureArray(_extends);
|
||||
|
||||
if (customRuleFiles != null) {
|
||||
plugins.push('local');
|
||||
}
|
||||
|
||||
middlewares?: Middleware[];
|
||||
configs: FlatESLintConfig[];
|
||||
}): Promise<FlatESLintConfig[]> {
|
||||
const middlewares: Middleware[] = uniq([
|
||||
importTypeScript,
|
||||
unicorn,
|
||||
custom,
|
||||
stylistic,
|
||||
() => import('./presets/custom'),
|
||||
...(auto ? checkEnv() : []),
|
||||
..._middlewares,
|
||||
...addMiddlewares,
|
||||
]);
|
||||
|
||||
const result: MiddlewareConfig = {
|
||||
root: true,
|
||||
plugins: unique('custom', plugins),
|
||||
env: { node: true, browser: true, es2023: true },
|
||||
reportUnusedDisableDirectives: true,
|
||||
parserOptions: {
|
||||
project: true,
|
||||
...parserOptions,
|
||||
const result: FlatESLintConfig[] = [
|
||||
{ rules: eslintRules }, //
|
||||
...tseslint.configs.recommendedTypeChecked,
|
||||
importPlugin.flatConfigs.recommended,
|
||||
importPlugin.flatConfigs.react,
|
||||
importPlugin.flatConfigs.typescript,
|
||||
...unicorn,
|
||||
stylistic,
|
||||
{
|
||||
files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
parser: tsParser,
|
||||
projectService: true,
|
||||
ecmaVersion: 'latest',
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
'import-x/parsers': {
|
||||
'@typescript-eslint/parser': ['.ts', '.tsx', '.mts', '.cts'],
|
||||
},
|
||||
'import-x/resolver': {
|
||||
typescript: true,
|
||||
node: true,
|
||||
},
|
||||
'import-x/core-modules': ['node:sqlite'],
|
||||
},
|
||||
ignores: ['eslint.config.cjs'],
|
||||
rules: {
|
||||
...importRules,
|
||||
...typescriptRules,
|
||||
},
|
||||
},
|
||||
ignorePatterns: [],
|
||||
globals: {},
|
||||
extends: ['eslint:recommended', 'prettier', ...(extend as string[])],
|
||||
settings,
|
||||
overrides: [
|
||||
{ files: ['repl.ts', 'scripts/**/*.ts'], rules: { 'no-console': off } },
|
||||
...(overrides ?? []),
|
||||
],
|
||||
rules: { ...eslintRules },
|
||||
...rest,
|
||||
};
|
||||
{
|
||||
files: ['*.js', '*.mjs', '*.cjs', '*.jsx'],
|
||||
...tseslint.configs.disableTypeChecked,
|
||||
rules: {
|
||||
'import-x/no-commonjs': off,
|
||||
'import-x/unambiguous': off,
|
||||
'@typescript-eslint/no-require-imports': off,
|
||||
'typed-custom/restrict-template-expressions': off,
|
||||
...tseslint.configs.disableTypeChecked.rules,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.d.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/consistent-type-imports': off,
|
||||
'import-x/unambiguous': off,
|
||||
},
|
||||
},
|
||||
] as FlatESLintConfig[];
|
||||
|
||||
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);
|
||||
for (const middleware of middlewares) {
|
||||
let fn = await middleware();
|
||||
if ('default' in fn) {
|
||||
fn = fn.default;
|
||||
}
|
||||
if (Array.isArray(fn)) {
|
||||
result.push(...(fn as FlatESLintConfig[]).flat(Infinity));
|
||||
} else {
|
||||
result.push(fn as unknown as FlatESLintConfig);
|
||||
}
|
||||
}
|
||||
|
||||
result.plugins = unique(result.plugins);
|
||||
result.extends = unique(result.extends);
|
||||
|
||||
Object.assign(result.rules, rules);
|
||||
if (configs) {
|
||||
result.push(...configs);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
Reference in New Issue
Block a user