harb/landing/eslint.config.js
openhands 0d761744df fix: Add architectural lint rules with agent-friendly error messages (#232)
- landing/eslint.config.js: ban imports from web-app paths (rule 1),
  direct RPC clients from viem/@wagmi/vue (rule 2), and axios (rule 4)
- web-app/eslint.config.js: ban string interpolation inside GraphQL
  query/mutation property values (rule 3); fixes 4 pre-existing violations
  in usePositionDashboard, usePositions, useSnatchNotifications,
  useWalletDashboard by migrating to variables: {} pattern
- services/ponder/eslint.config.js: ban findMany() calls that lack a
  limit parameter to prevent unbounded indexed-data growth (rule 5)

All error messages follow the [what is wrong][rule][how to fix][where to
read more] template so agents and humans fix on the first try.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 20:44:17 +00:00

106 lines
3.9 KiB
JavaScript

import pluginVue from 'eslint-plugin-vue';
import vueTsEslintConfig from '@vue/eslint-config-typescript';
import eslintConfigPrettier from 'eslint-config-prettier';
export default [
{
name: 'app/files-to-lint',
files: ['**/*.{ts,mts,tsx,vue}'],
},
{
name: 'app/files-to-ignore',
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**', '**/.vite/**', '**/.ponder/**', '**/node_modules/**'],
},
...pluginVue.configs['flat/essential'],
...vueTsEslintConfig(),
{
name: 'arch/landing-import-restrictions',
rules: {
'no-restricted-imports': [
'error',
{
patterns: [
{
group: ['**/web-app/**', '@harb/web-app/*'],
message:
'Landing component imports from web-app. Landing must not depend on web-app. Move shared code to kraiken-lib or a shared/ package. See docs/ARCHITECTURE.md.',
},
],
paths: [
{
name: 'viem',
importNames: ['createPublicClient', 'publicActions'],
message:
'Landing component imports a direct RPC client from viem. Landing must not make direct RPC calls from the browser — this will kill any node under load. Use Ponder GraphQL at /api/graphql instead. See docs/UX-DECISIONS.md.',
},
{
name: '@wagmi/vue',
importNames: ['usePublicClient'],
message:
'Landing component imports usePublicClient from @wagmi/vue. Landing must not make direct RPC calls from the browser — this will kill any node under load. Use Ponder GraphQL at /api/graphql instead. See docs/UX-DECISIONS.md.',
},
{
name: 'axios',
message:
'Landing component imports axios. Landing must not use axios — it increases bundle size for no benefit. Use native fetch() with the /api/graphql proxy instead.',
},
],
},
],
},
},
{
name: 'arch/graphql-no-interpolation',
rules: {
'no-restricted-syntax': [
'error',
{
selector:
"Property[key.name='query'] > TemplateLiteral[expressions.length>0], Property[key.name='mutation'] > TemplateLiteral[expressions.length>0]",
message:
'String interpolation used in a GraphQL query or mutation string. GraphQL queries must not use string interpolation — it bypasses type-checking and is unsafe. Use the `variables` parameter in the fetch body instead. Example: `variables: { holder: address }`. See PR #191 for the pattern.',
},
],
},
},
{
name: 'app/custom-rules',
rules: {
// TypeScript rules
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
'@typescript-eslint/no-explicit-any': 'error',
// Vue rules
'vue/multi-word-component-names': 'error',
'vue/no-unused-vars': 'error',
'vue/component-name-in-template-casing': ['error', 'PascalCase'],
'vue/require-default-prop': 'error',
'vue/require-prop-types': 'error',
'vue/html-indent': ['error', 2],
'vue/max-attributes-per-line': 'off', // Prettier handles this
'vue/singleline-html-element-content-newline': 'off', // Prettier handles this
// General rules
'no-console': 'error',
'max-len': ['error', { code: 140, ignoreUrls: true, ignoreStrings: true, ignoreTemplateLiterals: true }],
indent: ['error', 2, { SwitchCase: 1 }],
// Disable complexity rules
complexity: 'off',
'max-depth': 'off',
'max-nested-callbacks': 'off',
'max-lines': 'off',
'max-lines-per-function': 'off',
'max-params': 'off',
'max-statements': 'off',
},
},
eslintConfigPrettier,
];