UNPKG

eslint-config-sensible-prettier-typescript

Version:

Sensible default eslint rules and prettier rules for Typescript projects

369 lines 12.7 kB
"use strict"; import js from '@eslint/js'; import typescript from '@typescript-eslint/eslint-plugin'; import typescriptParser from '@typescript-eslint/parser'; import prettierConfig from 'eslint-config-prettier'; import importPlugin from 'eslint-plugin-import-x'; import jsdoc from 'eslint-plugin-jsdoc'; import perfectionistPlugin from 'eslint-plugin-perfectionist'; import unicornPlugin from 'eslint-plugin-unicorn'; import { defineConfig } from 'eslint/config'; import globals from 'globals'; import merge from 'lodash.merge'; function commonPlugins() { return { 'import-x': importPlugin, unicorn: unicornPlugin, perfectionist: perfectionistPlugin, }; } function commonImportResolvers() { return { typescript: { alwaysTryTypes: true, }, node: { extensions: ['.js', '.mjs', '.cjs', '.jsx', '.ts', '.mts', '.cts', '.tsx'], }, }; } function commonRules() { return { 'import-x/no-unresolved': 'error', 'perfectionist/sort-classes': 'off', 'perfectionist/sort-objects': 'off', 'perfectionist/sort-union-types': 'off', 'perfectionist/sort-array-includes': 'off', 'perfectionist/sort-interfaces': 'off', 'perfectionist/sort-enums': 'off', 'perfectionist/sort-object-types': 'off', 'perfectionist/sort-modules': 'off', 'perfectionist/sort-switch-case': 'off', 'unicorn/prefer-module': 'warn', 'unicorn/prevent-abbreviations': 'off', 'unicorn/no-array-reduce': 'off', 'unicorn/no-array-for-each': 'off', 'unicorn/no-array-for-loop': 'off', 'unicorn/no-static-only-class': 'off', 'unicorn/no-unreadable-array-destructuring': 'off', 'unicorn/empty-brace-spaces': 'off', 'unicorn/numeric-separators-style': 'off', 'unicorn/no-null': 'off', 'unicorn/no-object-as-default-parameter': 'off', 'unicorn/no-nested-ternary': 'off', // conflicts with prettier 'no-unused-vars': [ 'warn', { varsIgnorePattern: '^_', argsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_', }, ], 'no-unused-expressions': 'warn', 'no-await-in-loop': 'off', 'no-useless-constructor': 'warn', 'no-multi-spaces': 'off', 'no-eq-null': 'off', 'no-constant-condition': [ 'warn', { checkLoops: true, }, ], 'jsdoc/require-jsdoc': [ 'warn', { publicOnly: true, }, ], 'jsdoc/require-param': 'warn', 'jsdoc/require-returns': 'warn', 'jsdoc/tag-lines': 'off', 'capitalized-comments': 'off', 'comma-dangle': ['error', 'always-multiline'], 'default-case': 'off', curly: 'off', quotes: ['error', 'single', { avoidEscape: true }], eqeqeq: ['error', 'smart'], 'lines-between-class-members': [ 'error', 'always', { exceptAfterSingleLine: true, }, ], 'object-curly-spacing': ['error', 'always'], semi: ['warn', 'always'], 'max-params': ['warn', 7], indent: [ 'warn', 2, { MemberExpression: 'off', SwitchCase: 1, offsetTernaryExpressions: true, }, ], 'max-depth': ['warn', 7], 'padding-line-between-statements': 'off', }; } function commonJsRules() { return { // Import rules ...importPlugin.configs.recommended.rules, // Unicorn rules ...unicornPlugin.configs.recommended.rules, // Perfectionist rules ...perfectionistPlugin.configs['recommended-natural'].rules, // Custom rule overrides ...commonRules(), }; } /** * Builds a TypeScript-specific ESLint configuration object. * * This function creates a comprehensive TypeScript configuration that includes: * - TypeScript parser and plugin * - TypeScript-specific rules and recommendations * - Import/export rules with TypeScript support * - Code quality rules (unicorn, perfectionist, jsdoc) * - Custom rule overrides for TypeScript projects * * @param {import('eslint').Linter.Config['files']} [files] - File patterns to apply this configuration to * @param {import('eslint').Linter.Config['languageOptions']} [languageOptions] - Additional language options to merge * @returns {import('eslint').Linter.Config} TypeScript ESLint configuration object */ export function buildTsConfig(files, languageOptions) { return { files, languageOptions: merge({ parser: typescriptParser, parserOptions: { ecmaVersion: 'latest', sourceType: 'module', allowDefaultProject: true, }, globals: languageOptions?.globals ?? globals.node, // Don't merge globals, use the ones provided by the user }, languageOptions), plugins: { '@typescript-eslint': typescript, ...commonPlugins(), }, rules: { // TypeScript specific rules ...typescript.configs.recommended.rules, ...typescript.configs['eslint-recommended'].rules, // Import rules ...importPlugin.configs.recommended.rules, ...importPlugin.configs.typescript.rules, // Unicorn rules ...unicornPlugin.configs.recommended.rules, // Perfectionist rules ...perfectionistPlugin.configs['recommended-natural'].rules, ...jsdoc.configs['flat/recommended-typescript'].rules, ...commonRules(), 'no-redeclare': 'off', // It is safe to disable this rule when using TypeScript because TypeScript's compiler enforces this check. 'no-unused-vars': 'off', // false alarms in TypeScript 'no-undef': 'off', // false alarms in TypeScript // Custom rule overrides '@typescript-eslint/no-unused-vars': [ 'warn', { varsIgnorePattern: '^_', argsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_', }, ], '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-var-requires': 'off', }, settings: { 'import-x/parsers': { '@typescript-eslint/parser': ['.ts', '.tsx', '.mts', '.cts'], }, 'import-x/resolver': commonImportResolvers(), }, }; } /** * Builds a JavaScript-specific ESLint configuration object. * * This function creates a JavaScript configuration that includes: * - Import/export rules * - Code quality rules (unicorn, perfectionist) * - Common JavaScript linting rules * - Custom rule overrides * * @param {import('eslint').Linter.Config['files']} [files] - File patterns to apply this configuration to * @param {import('eslint').Linter.Config['languageOptions']} [languageOptions] - Additional language options to merge * @returns {import('eslint').Linter.Config} JavaScript ESLint configuration object */ export function buildJsConfig(files, languageOptions) { return { files, languageOptions: merge({ parserOptions: { ecmaVersion: 'latest', sourceType: 'module', }, globals: languageOptions?.globals ?? globals.node, // Don't merge globals, use the ones provided by the user }, languageOptions), plugins: { ...commonPlugins(), }, rules: { ...commonJsRules(), }, settings: { 'import-x/resolver': commonImportResolvers(), }, }; } /** * Builds a complete ESLint 9 flat configuration array. * * This function returns a comprehensive ESLint configuration that includes: * - Global ignore patterns * - Base JavaScript recommended rules * - Prettier integration * - JSDoc rules * - TypeScript configurations for various file types * - JavaScript configurations for various file types * - Browser and Node.js globals where appropriate * * @param {{ defaultSourceType?: string }} [options] - Optional configuration options. * - defaultSourceType: Default source type for TypeScript and JavaScript files that don't have source type indicated in the file name extension. * * @returns {import('eslint').Linter.Config[]} Complete ESLint flat configuration array * * @example * ```javascript * // Use the complete configuration * const config = buildFullConfig(); * * // Extend with custom rules * module.exports = [ * ...buildFullConfig(), * { rules: { 'no-console': 'warn' } } * ]; * ``` */ export function buildESLintConfig(options) { const { defaultSourceType } = options ?? {}; return defineConfig([ { ignores: ['dist', 'node_modules', '.vscode'], }, // Base JavaScript recommended rules js.configs.recommended, // Prettier config (must be last to override formatting rules) prettierConfig, // JSDoc config jsdoc.configs['flat/recommended'], // TypeScript files configurations buildTsConfig(['**/*.ts'], { parserOptions: { sourceType: defaultSourceType, }, }), buildTsConfig(['**/*.tsx'], { parserOptions: { sourceType: defaultSourceType, }, globals: { ...globals.node, ...globals.browser, }, }), buildTsConfig(['**/*.mts'], { parserOptions: { sourceType: 'module', }, }), buildTsConfig(['**/*.cts'], { parserOptions: { sourceType: 'commonjs', }, }), // JavaScript files configurations buildJsConfig(['**/*.js'], { parserOptions: { sourceType: defaultSourceType, }, }), buildJsConfig(['**/*.mjs'], { parserOptions: { sourceType: 'module', }, }), buildJsConfig(['**/*.cjs'], { parserOptions: { sourceType: 'commonjs', }, }), buildJsConfig(['**/*.jsx'], { parserOptions: { sourceType: defaultSourceType, }, globals: { ...globals.node, ...globals.browser, }, }), ]); } /** * Customizes an existing ESLint configuration array by applying modifications to matching configurations. * * This utility function allows you to modify specific configuration objects within an array * based on a selector function. Useful for applying project-specific customizations. * * @param {import('eslint').Linter.Config[]} configArray - Array of ESLint configurations to modify * @param {function(import('eslint').Linter.Config): boolean} selector - Function to select which configs to modify * @param {function(import('eslint').Linter.Config): void} modifier - Function to apply modifications to selected configs * * @example * ```javascript * const { buildESLintConfig, customiseESLintConfig } = require('eslint-config-sensible-prettier-typescript'); * const { defineConfig } = require('eslint/config'); * const globals = require('globals'); * * // Modify all TypeScript configurations for applying desired globals * const config = buildESLintConfig({ defaultSourceType: 'module' }); * customiseESLintConfig( * config, * (cfg) => [cfg.files].flat().some((f) => typeof f === 'string' && f.endsWith('*.ts')), * (cfg) => { * cfg.languageOptions.globals = { * ...globals.node, * ...globals.browser, * ...globals.jquery, * }; * }, * ); * * // More overriding * module.exports = defineConfig([ * { * ignores: ['dist', 'coverage', 'report', 'node_modules'], * }, * ...config, * { * rules: { * 'unicorn/filename-case': 'off', * }, * }, * ]); * ``` */ export function customiseESLintConfig(configArray, selector, modifier) { for (const config of configArray) { if (selector(config)) { modifier(config); } } } //# sourceMappingURL=config.mjs.map