import js from '@eslint/js'; import tseslint from 'typescript-eslint'; import sonarjs from 'eslint-plugin-sonarjs'; import globals from 'globals'; // Named `.mjs` because this package is CommonJS — `eslint.config.js` would be // parsed as CJS and the imports above would fail. // // Policy: every preset is downgraded to advisory, and the rules that actually // fail the build are listed once at the bottom. That way the CI gate is // readable in one place rather than inferred from four presets' defaults. // The reasoning behind the split, and the measurements it rests on, are in // docs/superpowers/specs/2026-08-19-eslint-design.md. /** * Rewrites a preset's enabled rules to `warn`, preserving each rule's options. * Rules the preset explicitly turned off stay off — a preset that disables a * rule means it, and flipping those to `warn` turns the whole of SonarJS's * opt-in catalogue (file headers, naming conventions) into daily noise. */ const advisory = (config) => ({ ...config, rules: Object.fromEntries( Object.entries(config.rules ?? {}).map(([rule, level]) => { const severity = Array.isArray(level) ? level[0] : level; if (severity === 'off' || severity === 0) return [rule, level]; return [rule, Array.isArray(level) ? ['warn', ...level.slice(1)] : 'warn']; }) ), }); export default tseslint.config( { ignores: ['dist/**', 'coverage/**', 'eslint.config.mjs'] }, ...[js.configs.recommended, ...tseslint.configs.recommended, sonarjs.configs.recommended].map( advisory ), { // `tests/` is deliberately out of scope for now: tsconfig.json only includes // `src`, so type-aware linting has no program for the test files, and // widening it is a separate change with its own violation count. files: ['src/**/*.ts'], languageOptions: { globals: globals.node, parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, rules: { // The two rules this repo has actually been bitten by. #59 is the whole // argument: an async handler whose rejection nothing forwards produces no // response at all, and the request hangs rather than failing visibly. '@typescript-eslint/no-floating-promises': 'error', '@typescript-eslint/no-misused-promises': [ 'error', { checksVoidReturn: { attributes: false } }, ], }, } );