Skip to content

Commit

Permalink
refactor(eslint): Manual rule setup with rule documentation (#108)
Browse files Browse the repository at this point in the history
* Add setup section

* More rule refactoring

* Start documenting rules

* Modularise

* Remove js from extraFileExtensions

* More documenting

* Add more typescript rules/comments

* Comment rule sources
  • Loading branch information
lachlancollins authored Jun 27, 2024
1 parent 18b4f28 commit 920bffb
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 93 deletions.
36 changes: 36 additions & 0 deletions src/eslint/import.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// https://github.com/un-ts/eslint-plugin-import-x

/** @type {import('eslint').Linter.FlatConfig} */
export const importRules = {
name: 'tanstack/import',
rules: {
/** Stylistic preference */
'import/newline-after-import': 'error',
/** No require() or module.exports */
'import/no-commonjs': 'error',
/** No import loops */
'import/no-cycle': 'error',
/** Reports if a resolved path is imported more than once */
'import/no-duplicates': 'error',
/** Default export name cannot match named export */
'import/no-named-as-default': 'error',
/** Don't access named imports from default export */
'import/no-named-as-default-member': 'error',
/** Stylistic preference */
'import/order': [
'error',
{
groups: [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index',
'object',
'type',
],
},
],
},
}
111 changes: 19 additions & 92 deletions src/eslint/index.js
Original file line number Diff line number Diff line change
@@ -1,109 +1,36 @@
import tseslint from 'typescript-eslint'
import pluginImport from 'eslint-plugin-import-x'
import globals from 'globals'
// @ts-expect-error
import eslint from '@eslint/js'
// @ts-expect-error
import configPrettier from 'eslint-config-prettier'
import { javascriptRules } from './javascript.js'
import { importRules } from './import.js'
import { typescriptRules } from './typescript.js'

/** @type {import('eslint').Linter.FlatConfig[]} */
export const rootConfig = [
{
name: 'eslint/rules',
rules: {
...eslint.configs.recommended.rules,
'no-async-promise-executor': 'off',
'no-empty': 'off',
'no-redeclare': 'off',
'no-shadow': 'error',
'no-undef': 'off',
'sort-imports': ['error', { ignoreDeclarationSort: true }],
},
},
{
name: 'prettier/rules',
...configPrettier,
},
...tseslint.configs.recommended,
...tseslint.configs.stylistic,
{
name: 'import/rules',
plugins: {
import: pluginImport,
},
rules: {
'import/newline-after-import': 'error',
'import/no-cycle': 'error',
'import/order': [
'error',
{
groups: [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index',
'object',
'type',
],
},
],
},
},
{
name: 'tanstack/custom',
name: 'tanstack/setup',
languageOptions: {
globals: {
...globals.browser,
},
ecmaVersion: 2020,
sourceType: 'module',
ecmaVersion: 2020,
// @ts-expect-error
parser: tseslint.parser,
parserOptions: {
extraFileExtensions: ['.js', '.svelte', '.vue'],
project: true,
extraFileExtensions: ['.svelte', '.vue'],
parser: tseslint.parser,
},
globals: {
...globals.browser,
},
},
plugins: {
'@typescript-eslint': tseslint.plugin,
},
rules: {
'@typescript-eslint/array-type': [
'error',
{ default: 'generic', readonly: 'generic' },
],
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/consistent-type-definitions': 'off',
'@typescript-eslint/consistent-type-imports': [
'error',
{ prefer: 'type-imports' },
],
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/method-signature-style': ['error', 'property'],
'@typescript-eslint/naming-convention': [
'error',
{
selector: 'typeParameter',
format: ['PascalCase'],
leadingUnderscore: 'forbid',
trailingUnderscore: 'forbid',
custom: {
regex: '^(T|T[A-Z][A-Za-z]+)$',
match: true,
},
},
],
'@typescript-eslint/no-empty-interface': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-unnecessary-condition': 'error',
'@typescript-eslint/no-unnecessary-type-assertion': 'error',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-inferrable-types': [
'error',
{ ignoreParameters: true },
],
// @ts-expect-error
import: pluginImport,
// @ts-expect-error
ts: tseslint.plugin,
},
},
javascriptRules,
importRules,
typescriptRules,
]
77 changes: 77 additions & 0 deletions src/eslint/javascript.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// https://eslint.org/docs/latest/rules/

/** @type {import('eslint').Linter.FlatConfig} */
export const javascriptRules = {
name: 'tanstack/javascript',
rules: {
/** Constructors of derived classes must call super() */
'constructor-super': 'error',
/** TODO */
'for-direction': 'error',
'getter-return': 'error',
'no-async-promise-executor': 'error',
'no-case-declarations': 'error',
'no-class-assign': 'error',
'no-compare-neg-zero': 'error',
'no-cond-assign': 'error',
'no-const-assign': 'error',
'no-constant-binary-expression': 'error',
'no-constant-condition': 'error',
'no-control-regex': 'error',
'no-debugger': 'error',
'no-delete-var': 'error',
'no-dupe-args': 'error',
'no-dupe-class-members': 'error',
'no-dupe-else-if': 'error',
'no-dupe-keys': 'error',
'no-duplicate-case': 'error',
'no-empty': 'error',
'no-empty-character-class': 'error',
'no-empty-pattern': 'error',
'no-empty-static-block': 'error',
'no-ex-assign': 'error',
'no-extra-boolean-cast': 'error',
'no-fallthrough': 'error',
'no-func-assign': 'error',
'no-global-assign': 'error',
'no-import-assign': 'error',
'no-invalid-regexp': 'error',
'no-irregular-whitespace': 'error',
'no-loss-of-precision': 'error',
'no-misleading-character-class': 'error',
'no-new-native-nonconstructor': 'error',
'no-nonoctal-decimal-escape': 'error',
'no-obj-calls': 'error',
'no-octal': 'error',
'no-prototype-builtins': 'error',
'no-redeclare': 'error',
'no-regex-spaces': 'error',
'no-self-assign': 'error',
'no-setter-return': 'error',
'no-shadow': 'error',
'no-shadow-restricted-names': 'error',
'no-sparse-arrays': 'error',
'no-this-before-super': 'error',
'no-unreachable': 'error',
'no-unsafe-finally': 'error',
'no-unsafe-negation': 'error',
'no-unsafe-optional-chaining': 'error',
'no-unused-labels': 'error',
'no-unused-private-class-members': 'error',
'no-unused-vars': 'error',
'no-useless-backreference': 'error',
'no-useless-catch': 'error',
'no-useless-escape': 'error',
/** Prefer let and const */
'no-var': 'error',
'no-with': 'error',
/** Prefer const if never re-assigned */
'prefer-const': 'error',
'require-yield': 'error',
/** Stylistic consistency */
'sort-imports': ['error', { ignoreDeclarationSort: true }],
'use-isnan': 'error',
/** Enforce comparing typeof against valid strings */
'valid-typeof': 'error',
},
}
58 changes: 58 additions & 0 deletions src/eslint/typescript.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// https://typescript-eslint.io/rules/

/** @type {import('eslint').Linter.FlatConfig} */
export const typescriptRules = {
name: 'tanstack/typescript',
rules: {
/** Prefer Array<T> format */
'ts/array-type': ['error', { default: 'generic', readonly: 'generic' }],
/** Prevent @ts-ignore, allow @ts-expect-error */
'ts/ban-ts-comment': ['error', { 'ts-expect-error': false }],
/** Bans specific built-in types and can suggest alternatives */
'ts/ban-types': 'error',
/** Enforce import type { T } */
'ts/consistent-type-imports': ['error', { prefer: 'type-imports' }],
/** Shorthand method style is less strict */
'ts/method-signature-style': ['error', 'property'],
/** Enforces generic type convention */
'ts/naming-convention': [
'error',
{
selector: 'typeParameter',
format: ['PascalCase'],
leadingUnderscore: 'forbid',
trailingUnderscore: 'forbid',
custom: {
regex: '^(T|T[A-Z][A-Za-z]+)$',
match: true,
},
},
],
/** Duplicate values can lead to bugs that are hard to track down */
'ts/no-duplicate-enum-values': 'error',
/** Using the operator any more than once does nothing */
'ts/no-extra-non-null-assertion': 'error',
/** There are several potential bugs with this compared to other loops */
'ts/no-for-in-array': 'error',
/** Enforce valid definition of new and constructor */
'ts/no-misused-new': 'error',
/** Disallow TypeScript namespaces */
'ts/no-namespace': 'error',
/** Disallow non-null assertions after an optional chain expression */
'ts/no-non-null-asserted-optional-chain': 'error',
/** Detects conditionals which will always evaluate truthy or falsy */
'ts/no-unnecessary-condition': 'error',
/** Checks if the the explicit type is identical to the inferred type */
'ts/no-unnecessary-type-assertion': 'error',
/** Don't over-define types for simple things like strings */
'ts/no-inferrable-types': ['error', { ignoreParameters: true }],
/** Enforce the use of as const over literal type */
'ts/prefer-as-const': 'error',
/** From recommended preset */
'ts/prefer-for-of': 'error',
/** Disallow async functions which have no await expression */
'ts/require-await': 'error',
/** Prefer of ES6-style import declarations */
'ts/triple-slash-reference': 'error',
},
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"include": [
"bin",
"src",
".eslintrc.cjs",
"eslint.config.js",
"prettier.config.js",
"tanstack.config.js"
]
Expand Down

0 comments on commit 920bffb

Please sign in to comment.