// ESLint checks the rules in CODE_CONVENTIONS.md that a linter can; see its // Tooling section for which. Formatting is Prettier's (.prettierrc.json), so // eslint-config-prettier comes last and turns off rules that would clash. import js from '@eslint/js'; import comments from '@eslint-community/eslint-plugin-eslint-comments/configs'; import { defineConfig } from 'eslint/config'; import prettier from 'eslint-config-prettier'; import simpleImportSort from 'eslint-plugin-simple-import-sort'; import solid from 'eslint-plugin-solid/configs/typescript'; import globals from 'globals'; import tseslint from 'typescript-eslint'; export default defineConfig( // src/types/api.ts is generated (npm run generate:api). { ignores: ['dist', '.vite', 'node_modules', 'src/types/api.ts'] }, js.configs.recommended, comments.recommended, tseslint.configs.recommendedTypeChecked, { languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, }, { files: ['src/**/*.{ts,tsx}'], ...solid, languageOptions: { ...solid.languageOptions, globals: globals.browser, }, }, { files: ['src/**/*.{ts,tsx}'], rules: { '@typescript-eslint/naming-convention': [ 'error', { selector: 'default', format: ['camelCase'] }, // Module-level constants may be UPPER_SNAKE_CASE; no variable is // PascalCase. { selector: 'variable', format: ['camelCase', 'UPPER_CASE'] }, // Components are PascalCase functions. { selector: 'function', format: ['camelCase', 'PascalCase'] }, { selector: 'typeLike', format: ['PascalCase'] }, // Backend JSON keys (snake_case) and header names keep their own case. { selector: ['objectLiteralProperty', 'typeProperty'], format: null }, { selector: 'import', format: null }, ], // Types are imported with import type, in their own statement. '@typescript-eslint/consistent-type-imports': [ 'error', { prefer: 'type-imports', fixStyle: 'separate-type-imports' }, ], eqeqeq: 'error', // Exported functions have their parameter and return types written out. '@typescript-eslint/explicit-module-boundary-types': 'error', // Imports from another folder of src use @/, not ../. 'no-restricted-imports': [ 'error', { patterns: [{ group: ['../*'], message: 'Import from another folder with @/.' }] }, ], }, }, { // Solid assigns refs itself (ref={button} compiles to an assignment ESLint // can't see), so a ref variable looks never assigned. files: ['src/**/*.tsx'], rules: { 'no-unassigned-vars': 'off', }, }, { // Imports: packages, then a blank line, then the app's own modules (@/, // then ./), each sorted. plugins: { 'simple-import-sort': simpleImportSort }, rules: { 'simple-import-sort/imports': [ 'error', { groups: [ ['^\\u0000@?\\w', '^@?\\w'], ['^\\u0000', '^@/', '^\\.'], ], }, ], 'simple-import-sort/exports': 'error', }, }, { // Every eslint-disable comment says why, after --. rules: { '@eslint-community/eslint-comments/require-description': 'error', }, }, { // Plain JavaScript (this file) has no types for the type-aware rules. files: ['**/*.js'], extends: [tseslint.configs.disableTypeChecked], }, prettier, );