diff --git a/.eslintrc b/.eslintrc index 12fe1ff..1e867ea 100644 --- a/.eslintrc +++ b/.eslintrc @@ -3,12 +3,12 @@ "env": { "node": true, "browser": true, - "es6": true + "es6": true, }, "extends": ["eslint:recommended", "prettier"], "parserOptions": { "sourceType": "module", - "ecmaVersion": "latest" + "ecmaVersion": "latest", }, "rules": { "no-restricted-imports": [ @@ -24,14 +24,14 @@ "object.hasown", "object.values", "string.prototype.matchall", - "has" - ] - } + "has", + ], + }, ], "arrow-body-style": ["error", "as-needed"], "class-methods-use-this": [ "warn", - { "exceptMethods": ["toString", "shouldComponentUpdate"] } + { "exceptMethods": ["toString", "shouldComponentUpdate"] }, ], "complexity": ["warn", { "max": 100 }], "curly": ["error", "multi-line", "consistent"], @@ -50,13 +50,13 @@ "prefer-const": ["error", { "destructuring": "all" }], "prefer-destructuring": [ "warn", - { "AssignmentExpression": { "array": false, "object": false } } + { "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 }] - } + "yoda": ["error", "never", { "exceptRange": true }], + }, } diff --git a/dist/index.d.ts b/dist/index.d.ts index 271a439..ac271d2 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -1,12 +1,19 @@ // Generated by dts-bundle-generator v9.4.0 -import { ESLintConfig, Rules } from '@aet/eslint-define-config'; +import { ESLintConfig, KnownExtends, Rules, Settings } from '@aet/eslint-define-config'; import { ESLintUtils } from '@typescript-eslint/utils'; import { Rule } from 'eslint'; +import { Merge, SetRequired } from 'type-fest'; export declare const error = "error"; export declare const warn = "warn"; export declare const off = "off"; +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 type RuleLevel = "error" | "warn" | "off" | 0 | 1 | 2; export type RuleEntry = RuleLevel | [ RuleLevel, @@ -32,6 +39,7 @@ export interface CustomRule { }>; options?: RuleLevel; } +export type Middleware = (config: MiddlewareConfig, helpers: MiddlewareFunctions) => void; /** * ESLint Configuration. * @see [ESLint Configuration](https://eslint.org/docs/latest/user-guide/configuring/) @@ -53,6 +61,16 @@ export type InputConfig = Omit & { */ auto?: boolean; }; +export type OptionalObjectKey = Exclude<{ + [Key in keyof T]: undefined | any[] extends T[Key] ? Key : undefined | Record extends T[Key] ? Key : never; +}[keyof T], undefined>; +export type MiddlewareConfig = Merge>, { + extends: KnownExtends[]; +}>; +export interface MiddlewareFunctions { + addRules(rules: Partial): void; + addSettings(settings: Partial): void; +} /** * Returns a ESLint config object. * @@ -72,6 +90,8 @@ export type InputConfig = Omit & { * Non bundled: * 1. [`graphql`](https://the-guild.dev/graphql/eslint/rules) */ -export declare function extendConfig(of?: InputConfig): ESLintConfig; +export declare function extendConfig(of?: InputConfig & { + middlewares: Middleware[]; +}): ESLintConfig; export {}; diff --git a/dist/package.json b/dist/package.json index e249a6c..dcef484 100644 --- a/dist/package.json +++ b/dist/package.json @@ -1,6 +1,6 @@ { "name": "@aet/eslint-rules", - "version": "1.0.1-beta.10", + "version": "1.0.1-beta.11", "license": "UNLICENSED", "peerDependencies": { "eslint": "^8.57.0", @@ -8,7 +8,7 @@ }, "dependencies": { "@nolyfill/is-core-module": "^1.0.39", - "@aet/eslint-define-config": "0.1.0-beta.10", + "@aet/eslint-define-config": "^0.1.0-beta.15", "@eslint-community/eslint-utils": "^4.4.0", "@tanstack/eslint-plugin-query": "^5.51.15", "@types/eslint": "^8.56.11", @@ -60,4 +60,4 @@ "supports-preserve-symlinks-flag": "file:./overrides/supports-preserve-symlinks-flag" } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index 0c004ed..8f1ba2a 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ }, "private": true, "devDependencies": { - "@aet/eslint-define-config": "^0.1.0-beta.10", + "@aet/eslint-define-config": "^0.1.0-beta.15", "@babel/core": "^7.24.9", "@babel/plugin-transform-flow-strip-types": "^7.24.7", "@babel/preset-env": "^7.25.0", @@ -43,6 +43,7 @@ "picocolors": "^1.0.1", "prettier": "^3.3.3", "prop-types": "^15.8.1", + "type-fest": "^4.23.0", "typescript": "^5.5.4" }, "prettier": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 990f1a2..004d4ef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,8 +22,8 @@ importers: .: devDependencies: '@aet/eslint-define-config': - specifier: ^0.1.0-beta.10 - version: 0.1.0-beta.10 + specifier: ^0.1.0-beta.15 + version: 0.1.0-beta.15 '@babel/core': specifier: ^7.24.9 version: 7.24.9 @@ -129,6 +129,9 @@ importers: prop-types: specifier: ^15.8.1 version: 15.8.1 + type-fest: + specifier: ^4.23.0 + version: 4.23.0 typescript: specifier: ^5.5.4 version: 5.5.4 @@ -139,8 +142,8 @@ packages: resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} engines: {node: '>=0.10.0'} - '@aet/eslint-define-config@0.1.0-beta.10': - resolution: {integrity: sha512-YTm+DdRvCPMMN+R3PZxJsBLYwUscxIpBhlpo2hSS/KEXtOiNUTGPIdzUERFkdNA1PayYfCDwh4dquKX3IZ0uwQ==} + '@aet/eslint-define-config@0.1.0-beta.15': + resolution: {integrity: sha512-WQuQyKU4V4RSZi0ShPpBE0LB8+evUjLr34T9XLWDTFJBrVkD6tJXdj8eQlzEWkp8ZxQrPGbYfkegwGHQ1NdLXg==} engines: {node: '>=18.0.0', npm: '>=9.0.0'} '@ampproject/remapping@2.2.1': @@ -1935,6 +1938,10 @@ packages: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} + type-fest@4.23.0: + resolution: {integrity: sha512-ZiBujro2ohr5+Z/hZWHESLz3g08BBdrdLMieYFULJO+tWc437sn8kQsWLJoZErY8alNhxre9K4p3GURAG11n+w==} + engines: {node: '>=16'} + typescript@5.5.4: resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} engines: {node: '>=14.17'} @@ -2022,7 +2029,7 @@ snapshots: '@aashutoshrathi/word-wrap@1.2.6': {} - '@aet/eslint-define-config@0.1.0-beta.10': {} + '@aet/eslint-define-config@0.1.0-beta.15': {} '@ampproject/remapping@2.2.1': dependencies: @@ -4018,6 +4025,8 @@ snapshots: type-fest@0.20.2: {} + type-fest@4.23.0: {} + typescript@5.5.4: {} undici-types@5.26.5: {} diff --git a/scripts/pull.sh b/scripts/pull.sh index 2d8ec2c..521ae79 100755 --- a/scripts/pull.sh +++ b/scripts/pull.sh @@ -23,5 +23,4 @@ pull() { pull import-js eslint-import-resolver-typescript 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 diff --git a/src/commits.json b/src/commits.json index 980026b..a0aba95 100644 --- a/src/commits.json +++ b/src/commits.json @@ -18,10 +18,10 @@ "subject": "[readme] fix typo in shareable config section in readme" }, "eslint-plugin-n": { - "hash": "5aad5f1c419b3143ffb9356bd299fc50dc576ee5", - "date": "2024-07-26T10:04:35+08:00", + "hash": "6744257b43560181412a76eadeb7de564b886ad4", + "date": "2024-07-26T11:46:54+01:00", "committer": "GitHub", - "subject": "chore(master): release 17.10.0 (#305)" + "subject": "chore(master): release 17.10.1 (#319)" }, "eslint-plugin-react": { "hash": "983b88dd3cb5e07919517d3fde4085f60883ded7", diff --git a/src/env.ts b/src/env.ts index ee97cdf..dd1547f 100644 --- a/src/env.ts +++ b/src/env.ts @@ -1,46 +1,39 @@ -import type { Extends, Plugin } from '@aet/eslint-define-config'; import * as fs from 'node:fs'; import { resolve } from 'node:path'; +import { Middleware, storybook } from './index'; +import { react, reactRefresh } from './presets/react'; +import { tailwind } from './presets/tailwind'; +import { reactQuery } from './presets/misc'; -export function checkEnv() { +export function* checkEnv(): Generator { const rootDir = process.cwd(); - const plugins: Plugin[] = []; - const extend: Extends[] = []; const pkgJsonPath = resolve(rootDir, 'package.json'); const pkgJson = fs.existsSync(pkgJsonPath) ? JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) : {}; - const deps = Object.keys({ - ...pkgJson.dependencies, - ...pkgJson.devDependencies, - ...pkgJson.peerDependencies, - }); + const deps = new Set( + Object.keys({ + ...pkgJson.dependencies, + ...pkgJson.devDependencies, + ...pkgJson.peerDependencies, + }), + ); - const hasReact = deps.includes('react'); - if (hasReact) { - plugins.push('react-hooks'); + if (deps.has('react')) { + yield react; } - if (deps.includes('@vitejs/plugin-react')) { - plugins.push('react-refresh'); + if (deps.has('@vitejs/plugin-react') && deps.has('eslint-plugin-react-refresh')) { + yield reactRefresh; } - - if (deps.includes('tailwindcss') && deps.includes('eslint-plugin-tailwindcss')) { - plugins.push('tailwindcss'); + if (deps.has('tailwindcss') && deps.has('eslint-plugin-tailwindcss')) { + yield tailwind; } - - if (deps.includes('storybook') && deps.includes('eslint-plugin-storybook')) { - extend.push('plugin:storybook/recommended'); + if (deps.has('storybook') && deps.has('eslint-plugin-storybook')) { + yield storybook; } - - if (deps.includes('@tanstack/react-query')) { - extend.push('plugin:@tanstack/eslint-plugin-query/recommended'); + if (deps.has('@tanstack/react-query') && deps.has('@tanstack/eslint-plugin-query')) { + yield reactQuery; } - - return { - react: hasReact, - plugins, - extends: extend, - }; } diff --git a/src/index.ts b/src/index.ts index cde9085..e1786a9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,21 +1,31 @@ /// import './redirect'; +import { uniq } from 'lodash'; +import type { Merge, SetRequired } from 'type-fest'; import type { Rule } from 'eslint'; import type { ESLintUtils } from '@typescript-eslint/utils'; -import type { ESLintConfig, Extends, Plugin, Rules } from '@aet/eslint-define-config'; -// import findCacheDirectory from 'find-cache-dir'; -import { typescriptRules } from './presets/typescript'; -import { unicornRules } from './presets/unicorn'; +import type { + ESLintConfig, + Extends, + KnownExtends, + Plugin, + Rules, + Settings, +} from '@aet/eslint-define-config'; + +import { importTypeScript } from './presets/typescript'; +import { unicorn } from './presets/unicorn'; import { eslintRules } from './presets/eslint'; -import { reactRules } from './presets/react'; -import { importRules } from './presets/import-x'; -import { jsDocRules } from './presets/jsdoc'; -import { graphqlRules } from './presets/graphql'; -import { localRules } from './presets/local'; +import { local } from './presets/local'; import { error, warn, off } from './constants'; -import { tailwindRules } from './presets/tailwind'; import { checkEnv } from './env'; +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 }; declare global { @@ -57,6 +67,8 @@ export interface CustomRule { options?: RuleLevel; } +export type Middleware = (config: MiddlewareConfig, helpers: MiddlewareFunctions) => void; + /** * ESLint Configuration. * @see [ESLint Configuration](https://eslint.org/docs/latest/user-guide/configuring/) @@ -81,6 +93,27 @@ export type InputConfig = Omit & { auto?: boolean; }; +type OptionalObjectKey = Exclude< + { + [Key in keyof T]: undefined | any[] extends T[Key] + ? Key + : undefined | Record extends T[Key] + ? Key + : never; + }[keyof T], + undefined +>; + +type MiddlewareConfig = Merge< + SetRequired>, + { extends: KnownExtends[] } +>; + +interface MiddlewareFunctions { + addRules(rules: Partial): void; + addSettings(settings: Partial): void; +} + /** * Returns a ESLint config object. * @@ -100,124 +133,75 @@ export type InputConfig = Omit & { * Non bundled: * 1. [`graphql`](https://the-guild.dev/graphql/eslint/rules) */ -export function extendConfig(of: InputConfig = {}): ESLintConfig { +export function extendConfig( + of: InputConfig & { + middlewares: Middleware[]; + } = { + middlewares: [], + }, +): ESLintConfig { const { - auto, + auto = true, plugins: _plugins = [], - settings, + settings = {}, rules, extends: _extends, overrides, customRuleFiles, parserOptions, + middlewares: _middlewares = [], // @ts-expect-error localRules: _, ...rest } = of; - let hasReact = false; - let plugins: Plugin[] = _plugins; + let plugins: Plugin[] = [..._plugins]; let extend: Extends[] = ensureArray(_extends); - if (auto) { - const env = checkEnv(); - hasReact = env.react; - plugins = [..._plugins, ...env.plugins]; - extend = [...extend, ...env.extends]; - } + const middlewares: Middleware[] = uniq([ + importTypeScript, + unicorn, + local, + ...(auto ? checkEnv() : []), + ..._middlewares, + ]); - const hasJsDoc = plugins.includes('jsdoc'); - const hasGraphQL = plugins.includes('@graphql-eslint'); - const hasTailwind = extend.some(name => name.includes('plugin:tailwindcss/')); - - // const ruleDir = false; // ?? findCacheDirectory({ name: '_eslint-rules' }); - // if (ruleDir) { - // fs.rmSync(ruleDir, { recursive: true, force: true }); - // fs.mkdirSync(ruleDir, { recursive: true }); - // } - - const result: InputConfig = { + const result: MiddlewareConfig = { root: true, - parser: '@typescript-eslint/parser', - plugins: unique('@typescript-eslint', 'import-x', 'rules', 'unicorn', plugins), + plugins: unique('rules', 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-x/errors', - 'plugin:import-x/typescript', - hasReact && [ - 'plugin:@eslint-react/recommended-legacy', - 'plugin:@eslint-react/dom-legacy', - 'plugin:react-hooks/recommended', - 'plugin:jsx-a11y/recommended', - ], - hasJsDoc && 'plugin:jsdoc/recommended-typescript', - hasGraphQL && 'plugin:@graphql-eslint/recommended', - extend as string[], - ), - settings: { - 'import-x/parsers': { - '@typescript-eslint/parser': ['.ts', '.tsx', '.mts', '.cts'], - }, - 'import-x/resolver': { - typescript: { - alwaysTryTypes: true, - }, - }, - ...settings, - }, + ignorePatterns: [], + globals: {}, + extends: ['eslint:recommended', 'prettier', ...(extend as string[])], + settings, overrides: [ - { - files: ['.eslintrc.js', '.eslintrc.cjs', '*.config.js', 'index.js'], - extends: ['plugin:@typescript-eslint/disable-type-checked'], - rules: { - 'rules/restrict-template-expressions': off, - }, - }, - { - files: ['*.d.ts'], - rules: { - '@typescript-eslint/consistent-type-imports': off, - }, - }, - { - files: ['repl.ts', 'scripts/**/*.ts'], - rules: { - 'no-console': off, - }, - }, - { - files: ['*.tsx'], - rules: { - '@eslint-react/no-leaked-conditional-rendering': error, - }, - }, + { files: ['repl.ts', 'scripts/**/*.ts'], rules: { 'no-console': off } }, ...(overrides ?? []), ], - rules: { - ...eslintRules, - ...typescriptRules, - ...importRules, - ...localRules, - ...(hasReact && reactRules), - ...(plugins.includes('react-refresh') && { - 'react-refresh/only-export-components': [warn, { allowConstantExport: true }], - }), - ...unicornRules, - ...(hasJsDoc && jsDocRules), - ...(hasGraphQL && graphqlRules), - ...(hasTailwind && tailwindRules), - ...rules, - }, + rules: { ...eslintRules, ...rules }, ...rest, }; + const functions: MiddlewareFunctions = { + addRules(newRules) { + Object.assign(result.rules, newRules); + }, + addSettings(newSettings) { + Object.assign(result.settings, newSettings); + }, + }; + + for (const fn of middlewares) { + fn(result, functions); + } + + result.plugins = unique(result.plugins); + result.extends = unique(result.extends); + return result; } diff --git a/src/presets/graphql.ts b/src/presets/graphql.ts index 9cd3ea6..6b499f2 100644 --- a/src/presets/graphql.ts +++ b/src/presets/graphql.ts @@ -1,4 +1,11 @@ import { GraphQLRulesObject } from '@aet/eslint-define-config/src/rules/graphql-eslint'; +import type { Middleware } from '../index'; // https://the-guild.dev/graphql/eslint/rules -export const graphqlRules: Partial = {}; +const graphqlRules: Partial = {}; + +export const graphql: Middleware = (config, { addRules }) => { + config.plugins.push('@graphql-eslint'); + config.extends.push('plugin:@graphql-eslint/recommended'); + addRules(graphqlRules); +}; diff --git a/src/presets/import-x.ts b/src/presets/import-x.ts deleted file mode 100644 index 7959d5f..0000000 --- a/src/presets/import-x.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { error, off } from '../constants'; -import { ImportXRulesObject } from '@aet/eslint-define-config/src/rules/import-x'; - -export const importRules: Partial = { - 'import-x/export': off, - 'import-x/no-duplicates': error, - 'import-x/order': [error, { groups: ['builtin', 'external'] }], -}; diff --git a/src/presets/jsdoc.ts b/src/presets/jsdoc.ts index b9dcfdd..95a0549 100644 --- a/src/presets/jsdoc.ts +++ b/src/presets/jsdoc.ts @@ -1,3 +1,10 @@ import { JSDocRulesObject } from '@aet/eslint-define-config/src/rules/jsdoc'; +import type { Middleware } from '../index'; -export const jsDocRules: Partial = {}; +const jsdocRules: Partial = {}; + +export const jsdoc: Middleware = (config, { addRules }) => { + config.plugins.push('jsdoc'); + config.extends.push('plugin:jsdoc/recommended-typescript'); + addRules(jsdocRules); +}; diff --git a/src/presets/local.ts b/src/presets/local.ts index 93a72b0..a27ec36 100644 --- a/src/presets/local.ts +++ b/src/presets/local.ts @@ -1,7 +1,11 @@ -import type { LocalRuleOptions } from '..'; +import type { LocalRuleOptions, Middleware } from '../index'; import { error } from '../constants'; -export const localRules: Partial = { +const localRules: Partial = { 'rules/no-import-dot': error, 'rules/restrict-template-expressions': error, }; + +export const local: Middleware = (_, { addRules }) => { + addRules(localRules); +}; diff --git a/src/presets/misc.ts b/src/presets/misc.ts new file mode 100644 index 0000000..dc659a3 --- /dev/null +++ b/src/presets/misc.ts @@ -0,0 +1,9 @@ +import type { Middleware } from '../index'; + +export const storybook: Middleware = config => { + config.extends.push('plugin:storybook/recommended'); +}; + +export const reactQuery: Middleware = config => { + config.extends.push('plugin:@tanstack/eslint-plugin-query/recommended'); +}; diff --git a/src/presets/react.ts b/src/presets/react.ts index 1e2bd48..5e55c8b 100644 --- a/src/presets/react.ts +++ b/src/presets/react.ts @@ -1,7 +1,35 @@ -import { error, off } from '../constants'; +import type { Middleware } from '../index'; +import { error, off, warn } from '../constants'; import { ReactRulesObject } from '@aet/eslint-define-config/src/rules/react'; +import { ReactRefreshRulesObject } from '@aet/eslint-define-config/src/rules/react-refresh'; -export const reactRules: Partial = { +const reactRules: Partial = { '@eslint-react/no-missing-component-display-name': off, '@eslint-react/no-children-prop': error, }; + +export const react: Middleware = (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 = { + 'react-refresh/only-export-components': [warn, { allowConstantExport: true }], +}; + +export const reactRefresh: Middleware = (config, { addRules }) => { + config.plugins.push('react-refresh'); + addRules(refreshRules); +}; diff --git a/src/presets/tailwind.ts b/src/presets/tailwind.ts index 9a5c385..08ceb42 100644 --- a/src/presets/tailwind.ts +++ b/src/presets/tailwind.ts @@ -1,5 +1,12 @@ +import type { Middleware } from '../index'; import { off } from '../constants'; +import type { TailwindRulesObject } from '@aet/eslint-define-config/src/rules/tailwind'; -export const tailwindRules = { +const tailwindRules: Partial = { 'tailwindcss/no-custom-classname': off, } as const; + +export const tailwind: Middleware = (config, { addRules }) => { + config.extends.push('plugin:tailwindcss/recommended'); + addRules(tailwindRules); +}; diff --git a/src/presets/typescript.ts b/src/presets/typescript.ts index b0513e5..ce81a05 100644 --- a/src/presets/typescript.ts +++ b/src/presets/typescript.ts @@ -1,7 +1,15 @@ import { error, off, warn } from '../constants'; import type { TypeScriptRulesObject } from '@aet/eslint-define-config/src/rules/typescript-eslint'; +import type { ImportXRulesObject } from '@aet/eslint-define-config/src/rules/import-x'; +import type { Middleware } from '../index'; -export const typescriptRules: Partial = { +const importRules: Partial = { + 'import-x/export': off, + 'import-x/no-duplicates': error, + 'import-x/order': [error, { groups: ['builtin', 'external'] }], +}; + +const typescriptRules: Partial = { '@typescript-eslint/ban-ts-comment': [ error, { @@ -37,3 +45,39 @@ export const typescriptRules: Partial = { '@typescript-eslint/triple-slash-reference': off, '@typescript-eslint/unbound-method': off, }; + +export const importTypeScript: Middleware = (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', '.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, + }, + }, + ); + + addRules(importRules); + addRules(typescriptRules); +}; diff --git a/src/presets/unicorn.ts b/src/presets/unicorn.ts index 6e6babf..3e23e13 100644 --- a/src/presets/unicorn.ts +++ b/src/presets/unicorn.ts @@ -1,10 +1,11 @@ +import type { Middleware } from '../index'; import { error, warn } from '../constants'; import { UnicornRulesObject } from '@aet/eslint-define-config/src/rules/unicorn'; const suggest = (suggest: string) => ({ suggest, fix: false }); // https://github.com/sindresorhus/eslint-plugin-unicorn/tree/28e7498ad06679bb92343db53bb40a7b5ba2990a -export const unicornRules: Partial = { +const unicornRules: Partial = { 'unicorn/better-regex': error, 'unicorn/consistent-function-scoping': warn, 'unicorn/escape-case': error, @@ -69,3 +70,8 @@ export const unicornRules: Partial = { ], 'unicorn/template-indent': warn, }; + +export const unicorn: Middleware = (config, { addRules }) => { + config.plugins.push('unicorn'); + addRules(unicornRules); +};