eslint.config.mjs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import stylistic from '@stylistic/eslint-plugin'
  2. import tseslint from 'typescript-eslint'
  3. /**
  4. * ESLint flat config. Two layers:
  5. *
  6. * 1. typescript-eslint strict-type-checked — correctness rules that need the
  7. * type checker. The headline rules for this codebase: no-floating-promises
  8. * and no-misused-promises (an un-awaited promise in the agent loop is our
  9. * primary bug class), switch-exhaustiveness-check (we switch over
  10. * merge-extensible unions everywhere).
  11. * 2. @stylistic — formatting (2-space, no semicolons, single quotes, trailing
  12. * commas), so style is enforced rather than drifting between agents.
  13. *
  14. * vendor/ is linted lightly (style only stays OFF — vendored code keeps
  15. * upstream style; only a few safety rules apply there) and examples/tests are
  16. * linted with relaxed unsafe-* rules where mocks intentionally bend types.
  17. */
  18. export default tseslint.config(
  19. {
  20. ignores: [
  21. '**/lib/**',
  22. '**/node_modules/**',
  23. 'vendor/**', // vendored source keeps upstream style and idioms
  24. '**/*.js',
  25. '**/*.mjs',
  26. ],
  27. },
  28. // --- our packages: full strictness -------------------------------------
  29. {
  30. files: ['packages/*/src/**/*.ts', 'examples/**/*.ts', 'scripts/**/*.ts'],
  31. extends: [
  32. ...tseslint.configs.strictTypeChecked,
  33. ],
  34. languageOptions: {
  35. parserOptions: {
  36. project: ['./tsconfig.typecheck.json'],
  37. tsconfigRootDir: import.meta.dirname,
  38. },
  39. },
  40. rules: {
  41. // The bug class this repo cares most about: lost promises in the loop.
  42. '@typescript-eslint/no-floating-promises': 'error',
  43. '@typescript-eslint/no-misused-promises': 'error',
  44. '@typescript-eslint/require-await': 'error',
  45. '@typescript-eslint/switch-exhaustiveness-check': ['error', {
  46. considerDefaultExhaustiveForUnions: true,
  47. }],
  48. '@typescript-eslint/no-unnecessary-condition': ['error', {
  49. allowConstantLoopConditions: true,
  50. }],
  51. // `any` requires a justification comment — enforced as: no bare casts.
  52. '@typescript-eslint/no-explicit-any': 'error',
  53. // Style points where the codebase intentionally diverges from preset:
  54. '@typescript-eslint/no-namespace': 'off', // Cordis Config-namespace idiom
  55. '@typescript-eslint/no-empty-object-type': 'off', // merge-extensible maps
  56. '@typescript-eslint/no-invalid-void-type': 'off', // event signatures
  57. '@typescript-eslint/restrict-template-expressions': ['error', {
  58. allowNumber: true,
  59. allowBoolean: true,
  60. }],
  61. // `void foo()` in arrow listeners is our idiom for intentional fire-and-forget
  62. 'no-void': 'off',
  63. '@typescript-eslint/no-unused-vars': ['error', {
  64. argsIgnorePattern: '^_',
  65. varsIgnorePattern: '^_',
  66. caughtErrorsIgnorePattern: '^_',
  67. }],
  68. },
  69. },
  70. // --- examples: demo code conforms to async interfaces without awaiting ---
  71. {
  72. files: ['examples/**/*.ts'],
  73. rules: {
  74. '@typescript-eslint/require-await': 'off',
  75. },
  76. },
  77. // --- tests: same rules, minus the friction that fights test ergonomics --
  78. {
  79. files: ['packages/*/tests/**/*.ts'],
  80. extends: [
  81. ...tseslint.configs.strictTypeChecked,
  82. ],
  83. languageOptions: {
  84. parserOptions: {
  85. project: ['./tsconfig.typecheck.json'],
  86. tsconfigRootDir: import.meta.dirname,
  87. },
  88. },
  89. rules: {
  90. '@typescript-eslint/no-floating-promises': 'error',
  91. '@typescript-eslint/no-misused-promises': 'error',
  92. '@typescript-eslint/no-explicit-any': 'error',
  93. '@typescript-eslint/no-non-null-assertion': 'off', // assertions follow expect()s
  94. '@typescript-eslint/no-unnecessary-condition': 'off',
  95. '@typescript-eslint/require-await': 'off', // mock execute() signatures
  96. '@typescript-eslint/no-empty-function': 'off', // stub agents
  97. '@typescript-eslint/only-throw-error': 'off', // testing non-Error throws
  98. '@typescript-eslint/no-namespace': 'off',
  99. '@typescript-eslint/no-empty-object-type': 'off',
  100. '@typescript-eslint/restrict-template-expressions': 'off',
  101. '@typescript-eslint/no-unused-vars': ['error', {
  102. argsIgnorePattern: '^_',
  103. varsIgnorePattern: '^_',
  104. caughtErrorsIgnorePattern: '^_',
  105. }],
  106. },
  107. },
  108. // --- formatting (everything we own) -------------------------------------
  109. {
  110. files: ['packages/**/*.ts', 'examples/**/*.ts', 'scripts/**/*.ts', 'eslint.config.mjs'],
  111. plugins: { '@stylistic': stylistic },
  112. rules: {
  113. '@stylistic/indent': ['error', 2],
  114. '@stylistic/semi': ['error', 'never'],
  115. '@stylistic/quotes': ['error', 'single', { avoidEscape: true }],
  116. '@stylistic/comma-dangle': ['error', 'always-multiline'],
  117. '@stylistic/eol-last': ['error', 'always'],
  118. '@stylistic/no-trailing-spaces': 'error',
  119. '@stylistic/object-curly-spacing': ['error', 'always'],
  120. '@stylistic/arrow-parens': ['error', 'as-needed', { requireForBlockBody: true }],
  121. '@stylistic/member-delimiter-style': ['error', {
  122. multiline: { delimiter: 'none' },
  123. singleline: { delimiter: 'semi', requireLast: false },
  124. }],
  125. '@stylistic/max-len': ['error', { code: 140, ignoreUrls: true, ignoreStrings: true, ignoreTemplateLiterals: true }],
  126. },
  127. },
  128. )