All files / src/plugins/code-style eslint.config.ts

100% Statements 3/3
100% Branches 0/0
100% Functions 0/0
100% Lines 3/3

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115                                          1x                                                                                                             1x     1x                                                                      
/**
 * The house style, as an ESLint flat config a unit is linted with as it stands.
 *
 * 2-space indentation, single quotes, semicolons, K&R braces, braces on every control statement,
 * trailing commas on multiline literals, and a blank line around every block and after a run of
 * declarations. The framework's own code in paf/ is linted where it is written,
 * so a unit's copy is ignored here, along with its swap folders and generated or built output.
 *
 * lint.ts hands this to ESLint, which reads every pattern below from the unit's root. A unit that adds
 * rules of its own keeps a config/eslint.config.ts that spreads the default export and adds to it, and
 * the framework's own config builds on the rules and the TypeScript block.
 */
import js from '@eslint/js';
import globals from 'globals';
import stylistic from '@stylistic/eslint-plugin';
import tseslint from 'typescript-eslint';
import { defineConfig, globalIgnores } from 'eslint/config';
import type { Linter } from 'eslint';
import { SKIPPED_FOLDER_GLOBS } from './unit-folders.ts';
 
/** The house formatting and style rules, for any language ESLint reads. */
export const houseStyleRules = {
  '@stylistic/semi': ['error', 'always'],
  '@stylistic/quotes': ['error', 'single', { avoidEscape: true, allowTemplateLiterals: 'always' }],
  '@stylistic/indent': ['error', 2, { SwitchCase: 1 }],
 
  // K&R braces. compact single-line lookup tables are also allowed
  '@stylistic/brace-style': ['error', '1tbs', { allowSingleLine: true }],
 
  '@stylistic/eol-last': ['error', 'always'],
  '@stylistic/no-trailing-spaces': 'error',
 
  // trailing commas on multiline literals
  // never in function parameters or arguments
  '@stylistic/comma-dangle': ['error', {
    arrays: 'always-multiline',
    objects: 'always-multiline',
    imports: 'always-multiline',
    exports: 'always-multiline',
    functions: 'never',
  }],
 
  '@stylistic/object-curly-spacing': ['error', 'always'],
  '@stylistic/keyword-spacing': 'error',
  '@stylistic/space-infix-ops': 'error',
  '@stylistic/comma-spacing': 'error',
  '@stylistic/space-before-blocks': 'error',
  '@stylistic/arrow-parens': ['error', 'always'],
 
  // a blank line either side of every block, and after a run of declarations, so a function reads in
  // steps rather than as one wall
  '@stylistic/padding-line-between-statements': ['error',
    { blankLine: 'always', prev: '*', next: 'block-like' },
    { blankLine: 'always', prev: 'block-like', next: '*' },
    { blankLine: 'always', prev: ['const', 'let'], next: '*' },
    { blankLine: 'any', prev: ['const', 'let'], next: ['const', 'let'] },
  ],
 
  // always require braces around control statements
  'curly': ['error', 'all'],
 
  // allow the == null / != null idiom
  'eqeqeq': ['error', 'always', { null: 'ignore' }],
 
  'no-throw-literal': 'error',
  'no-implicit-coercion': 'error',
  'dot-notation': 'error',
 
  // empty catch blocks are permitted
  'no-empty': ['error', { allowEmptyCatch: true }],
} satisfies Linter.RulesRecord;
 
/**
 * What a unit never lints: the folders that are not its own code, which the format leaves alone too, and
 * generated or built JavaScript.
 */
export const unitIgnores = [...SKIPPED_FOLDER_GLOBS, '**/*.js'];
 
/** Lints every TypeScript source except declaration files, with the house style. */
export const typescript = defineConfig([
  {
    files: ['**/*.ts'],
    ignores: ['**/*.d.ts'],
    // typescript-eslint disables the core rules it replaces
    extends: [js.configs.recommended, tseslint.configs.recommended],
    plugins: {
      '@stylistic': stylistic,
    },
    languageOptions: {
      ecmaVersion: 'latest',
      sourceType: 'module',
      globals: {
        ...globals.node,
        ...globals.browser,
        Pebble: 'readonly',
      },
    },
    rules: {
      ...houseStyleRules,
      // prefer the TypeScript-aware implementations
      'no-shadow': 'off',
      '@typescript-eslint/no-shadow': 'error',
      '@typescript-eslint/no-unused-vars': [
        'error',
        {
          caughtErrors: 'none',
          argsIgnorePattern: '^_',
        },
      ],
    },
  },
]);
 
export default defineConfig([globalIgnores(unitIgnores), ...typescript]);