import { fixupPluginRules, includeIgnoreFile } from '@eslint/compat' import checkFilePlugin from 'eslint-plugin-check-file' import importPlugin from 'eslint-plugin-import' import perfectionist from 'eslint-plugin-perfectionist' import globals from 'globals' import path from 'node:path' import { fileURLToPath } from 'node:url' import tsEslint, { parser } from 'typescript-eslint' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const gitIgnorePath = path.join(__dirname, '.gitignore') const autofix = process.env.CI ? 'error' : 'warn' /** @type {import('@typescript-eslint/utils/ts-eslint').FlatConfig.Config[]} */ export default [ // Global ignore { ignores: ['**/*.json'], name: 'claudesidian/global-ignore', }, // Git ignore { ...includeIgnoreFile(gitIgnorePath), name: 'claudesidian/gitignore', }, // Default config { files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.mjs'], languageOptions: { parser, parserOptions: { warnOnUnsupportedTypeScriptVersion: false, }, sourceType: 'module', }, linterOptions: { reportUnusedDisableDirectives: 'error', reportUnusedInlineConfigs: 'error', }, name: 'claudesidian/default', plugins: { '@typescript-eslint': tsEslint.plugin, 'check-file': checkFilePlugin, 'import': fixupPluginRules(importPlugin), perfectionist, }, rules: { '@typescript-eslint/array-type': [autofix], '@typescript-eslint/ban-ts-comment': [ 'error', { minimumDescriptionLength: 10 }, ], '@typescript-eslint/ban-tslint-comment': [autofix], '@typescript-eslint/consistent-generic-constructors': [autofix], '@typescript-eslint/consistent-indexed-object-style': [autofix], '@typescript-eslint/consistent-type-assertions': [autofix], '@typescript-eslint/consistent-type-definitions': [autofix], '@typescript-eslint/default-param-last': ['error'], '@typescript-eslint/method-signature-style': [autofix, 'property'], '@typescript-eslint/no-array-constructor': [autofix], '@typescript-eslint/no-empty-function': ['error'], '@typescript-eslint/no-empty-object-type': [ 'error', { allowInterfaces: 'with-single-extends' }, ], '@typescript-eslint/no-explicit-any': ['error'], '@typescript-eslint/no-extra-non-null-assertion': [autofix], '@typescript-eslint/no-import-type-side-effects': [autofix], '@typescript-eslint/no-inferrable-types': [autofix], '@typescript-eslint/no-loop-func': ['error'], '@typescript-eslint/no-misused-new': ['error'], '@typescript-eslint/no-namespace': ['error'], '@typescript-eslint/no-non-null-asserted-optional-chain': ['error'], '@typescript-eslint/no-require-imports': ['error'], '@typescript-eslint/no-this-alias': ['error'], '@typescript-eslint/no-unnecessary-type-constraint': ['error'], '@typescript-eslint/no-unsafe-declaration-merging': ['error'], '@typescript-eslint/no-unsafe-function-type': ['error'], '@typescript-eslint/no-unused-expressions': ['error'], '@typescript-eslint/no-unused-vars': [ 'error', { args: 'all', argsIgnorePattern: '^_', caughtErrors: 'all', caughtErrorsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_', ignoreRestSiblings: false, varsIgnorePattern: '^_', }, ], '@typescript-eslint/no-useless-constructor': ['error'], '@typescript-eslint/no-useless-empty-export': [autofix], '@typescript-eslint/no-wrapper-object-types': [autofix], '@typescript-eslint/prefer-as-const': [autofix], '@typescript-eslint/prefer-for-of': ['error'], '@typescript-eslint/prefer-function-type': [autofix], '@typescript-eslint/prefer-literal-enum-member': ['error'], '@typescript-eslint/prefer-namespace-keyword': [autofix], '@typescript-eslint/triple-slash-reference': ['error'], 'check-file/filename-naming-convention': [ 'error', { '**/*': 'KEBAB_CASE' }, { ignoreMiddleExtensions: true }, ], 'check-file/folder-naming-convention': [ 'error', { '**/*': 'NEXT_JS_APP_ROUTER_CASE' }, { ignoreMiddleExtensions: true }, ], 'check-file/no-index': ['error', { ignoreMiddleExtensions: true }], 'constructor-super': ['error'], 'curly': [autofix, 'multi-line', 'consistent'], 'eqeqeq': [autofix, 'always', { null: 'ignore' }], 'for-direction': ['error'], 'getter-return': ['error'], 'import/enforce-node-protocol-usage': [autofix, 'always'], 'import/first': [autofix], 'import/newline-after-import': [autofix], 'import/no-default-export': ['error'], 'import/no-duplicates': [autofix], 'import/no-mutable-exports': ['error'], 'logical-assignment-operators': [autofix], '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-div-regex': [autofix], 'no-dupe-args': ['error'], 'no-dupe-else-if': ['error'], 'no-dupe-keys': ['error'], 'no-duplicate-case': ['error'], 'no-else-return': [autofix], 'no-empty': ['error'], 'no-empty-character-class': ['error'], 'no-empty-pattern': ['error'], 'no-empty-static-block': ['error'], 'no-ex-assign': ['error'], 'no-extra-bind': [autofix], 'no-extra-boolean-cast': [autofix], 'no-extra-label': [autofix], 'no-fallthrough': ['error', { commentPattern: 'no-fallthrough-ignore' }], 'no-func-assign': ['error'], 'no-global-assign': ['error'], 'no-implicit-coercion': [autofix, { allow: ['!!'] }], 'no-import-assign': ['error'], 'no-inner-declarations': ['error'], 'no-invalid-regexp': ['error'], 'no-irregular-whitespace': ['error'], 'no-lonely-if': [autofix], '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-regex-spaces': [autofix], 'no-self-assign': ['error'], 'no-setter-return': ['error'], 'no-shadow-restricted-names': ['error'], 'no-sparse-arrays': ['error'], 'no-template-curly-in-string': ['error'], 'no-this-before-super': ['error'], 'no-unassigned-vars': ['error'], 'no-undef': ['error'], 'no-undef-init': [autofix], 'no-unexpected-multiline': ['error'], 'no-unneeded-ternary': [autofix], 'no-unreachable': ['error'], 'no-unsafe-finally': ['error'], 'no-unsafe-negation': ['error'], 'no-unsafe-optional-chaining': ['error'], 'no-unused-labels': [autofix], 'no-unused-private-class-members': ['error'], 'no-useless-assignment': ['error'], 'no-useless-backreference': ['error'], 'no-useless-catch': ['error'], 'no-useless-computed-key': [autofix], 'no-useless-escape': ['error'], 'no-useless-rename': [autofix], 'no-useless-return': [autofix], 'no-var': [autofix], 'no-with': ['error'], 'object-shorthand': [autofix], 'one-var': [autofix, 'never'], 'operator-assignment': [autofix], 'perfectionist/sort-array-includes': [autofix], 'perfectionist/sort-classes': [autofix], 'perfectionist/sort-decorators': [autofix], 'perfectionist/sort-enums': [autofix], 'perfectionist/sort-exports': [autofix], 'perfectionist/sort-heritage-clauses': [autofix], 'perfectionist/sort-imports': [autofix], 'perfectionist/sort-interfaces': [autofix], 'perfectionist/sort-intersection-types': [autofix], 'perfectionist/sort-jsx-props': [autofix], 'perfectionist/sort-maps': [autofix], 'perfectionist/sort-modules': [autofix], 'perfectionist/sort-named-exports': [autofix], 'perfectionist/sort-named-imports': [autofix], 'perfectionist/sort-object-types': [autofix], 'perfectionist/sort-objects': [autofix], 'perfectionist/sort-sets': [autofix], 'perfectionist/sort-switch-case': [autofix], 'perfectionist/sort-union-types': [autofix], 'prefer-const': [autofix], 'prefer-exponentiation-operator': [autofix], 'prefer-numeric-literals': [autofix], 'prefer-object-has-own': [autofix], 'prefer-object-spread': [autofix], 'prefer-template': [autofix], 'radix': ['error'], 'require-yield': ['error'], 'unicode-bom': [autofix], 'use-isnan': ['error'], 'valid-typeof': ['error'], 'yoda': [autofix], }, settings: { perfectionist: { partitionByComment: true, type: 'natural', }, }, }, // Files that require default exports { files: [ '**/*config.*', '**/global-error.tsx', '**/error.tsx', '**/layout.tsx', '**/loading.tsx', '**/middleware.ts', '**/page.tsx', '**/not-found.tsx', ], name: 'claudesidian/disable-default-export', rules: { 'import/no-default-export': 'off', }, }, // Typed rules { files: ['**/*.ts', '**/*.tsx'], languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: __dirname, }, }, name: 'claudesidian/typed', plugins: { tsEslint: tsEslint.plugin }, rules: { '@typescript-eslint/await-thenable': ['error'], '@typescript-eslint/consistent-type-exports': [autofix], '@typescript-eslint/consistent-type-imports': [ autofix, { fixStyle: 'inline-type-imports' }, ], '@typescript-eslint/dot-notation': [autofix], '@typescript-eslint/explicit-member-accessibility': [ autofix, { overrides: { constructors: 'no-public' } }, ], '@typescript-eslint/no-array-delete': ['error'], '@typescript-eslint/no-base-to-string': ['error'], '@typescript-eslint/no-confusing-void-expression': [autofix], '@typescript-eslint/no-deprecated': ['error'], '@typescript-eslint/no-duplicate-enum-values': ['error'], '@typescript-eslint/no-duplicate-type-constituents': [autofix], '@typescript-eslint/no-dynamic-delete': [autofix], '@typescript-eslint/no-extraneous-class': ['error'], '@typescript-eslint/no-floating-promises': ['error'], '@typescript-eslint/no-for-in-array': ['error'], '@typescript-eslint/no-implied-eval': ['error'], '@typescript-eslint/no-invalid-void-type': ['error'], '@typescript-eslint/no-meaningless-void-operator': [autofix], '@typescript-eslint/no-misused-promises': [ 'error', { checksVoidReturn: false }, ], '@typescript-eslint/no-misused-spread': ['error'], '@typescript-eslint/no-mixed-enums': ['error'], '@typescript-eslint/no-non-null-asserted-nullish-coalescing': ['error'], '@typescript-eslint/no-non-null-assertion': ['error'], '@typescript-eslint/no-redundant-type-constituents': ['error'], '@typescript-eslint/no-unnecessary-boolean-literal-compare': [autofix], '@typescript-eslint/no-unnecessary-condition': ['error'], '@typescript-eslint/no-unnecessary-qualifier': [autofix], '@typescript-eslint/no-unnecessary-template-expression': [autofix], '@typescript-eslint/no-unnecessary-type-arguments': [autofix], '@typescript-eslint/no-unnecessary-type-assertion': [autofix], '@typescript-eslint/no-unnecessary-type-conversion': ['error'], '@typescript-eslint/no-unnecessary-type-parameters': ['error'], '@typescript-eslint/no-unsafe-argument': ['error'], '@typescript-eslint/no-unsafe-assignment': ['error'], '@typescript-eslint/no-unsafe-call': ['error'], '@typescript-eslint/no-unsafe-enum-comparison': ['error'], '@typescript-eslint/no-unsafe-member-access': ['error'], '@typescript-eslint/no-unsafe-return': ['error'], '@typescript-eslint/no-unsafe-type-assertion': ['error'], '@typescript-eslint/no-unsafe-unary-minus': ['error'], '@typescript-eslint/non-nullable-type-assertion-style': [autofix], '@typescript-eslint/only-throw-error': ['error'], '@typescript-eslint/prefer-destructuring': [autofix, { array: false }], '@typescript-eslint/prefer-find': ['error'], '@typescript-eslint/prefer-includes': [autofix], '@typescript-eslint/prefer-nullish-coalescing': [ 'error', { ignorePrimitives: { string: true } }, ], '@typescript-eslint/prefer-optional-chain': [autofix], '@typescript-eslint/prefer-promise-reject-errors': ['error'], '@typescript-eslint/prefer-readonly': [autofix], '@typescript-eslint/prefer-reduce-type-parameter': [autofix], '@typescript-eslint/prefer-regexp-exec': [autofix], '@typescript-eslint/prefer-return-this-type': [autofix], '@typescript-eslint/prefer-string-starts-ends-with': [autofix], '@typescript-eslint/promise-function-async': [autofix], '@typescript-eslint/related-getter-setter-pairs': ['error'], '@typescript-eslint/require-array-sort-compare': ['error'], '@typescript-eslint/require-await': ['error'], '@typescript-eslint/restrict-plus-operands': ['error'], '@typescript-eslint/restrict-template-expressions': ['error'], '@typescript-eslint/return-await': [autofix], '@typescript-eslint/switch-exhaustiveness-check': ['error'], '@typescript-eslint/unbound-method': ['error'], '@typescript-eslint/unified-signatures': ['error'], '@typescript-eslint/use-unknown-in-catch-callback-variable': ['error'], }, }, // Node environment { files: ['**/*.ts', '**/*.js', '**/*.mjs'], languageOptions: { globals: globals.nodeBuiltin }, name: 'claudesidian/node', }, // App-specific rules ]