122 lines
4 KiB
JavaScript
122 lines
4 KiB
JavaScript
import { dirname, resolve } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import pluginVue from 'eslint-plugin-vue';
|
|
import vueTsEslintConfig from '@vue/eslint-config-typescript';
|
|
import tsParser from '@typescript-eslint/parser';
|
|
import stylistic from '@stylistic/eslint-plugin';
|
|
import prettier from 'eslint-config-prettier';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
|
|
export default [
|
|
{
|
|
name: 'app/files-to-lint',
|
|
files: ['**/*.{ts,mts,tsx,vue}'],
|
|
languageOptions: {
|
|
parser: tsParser,
|
|
parserOptions: {
|
|
projectService: {
|
|
defaultProject: resolve(__dirname, 'tsconfig.app.json'),
|
|
},
|
|
tsconfigRootDir: __dirname,
|
|
sourceType: 'module',
|
|
ecmaVersion: 'latest',
|
|
allowDefaultProject: true,
|
|
},
|
|
},
|
|
plugins: {
|
|
'@stylistic': stylistic,
|
|
},
|
|
},
|
|
|
|
{
|
|
name: 'app/files-to-ignore',
|
|
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**', '**/node_modules/**', '**/.ponder/**', '**/__tests__/**'],
|
|
},
|
|
|
|
...pluginVue.configs['flat/essential'],
|
|
...vueTsEslintConfig(),
|
|
|
|
{
|
|
name: 'app/custom-rules',
|
|
rules: {
|
|
// TypeScript rules
|
|
'@typescript-eslint/no-explicit-any': 'error',
|
|
'@typescript-eslint/no-unused-vars': [
|
|
'error',
|
|
{
|
|
argsIgnorePattern: '^_',
|
|
varsIgnorePattern: '^_',
|
|
caughtErrorsIgnorePattern: '^_',
|
|
},
|
|
],
|
|
|
|
// General code quality
|
|
'no-console': 'error',
|
|
'@stylistic/indent': ['error', 2, { SwitchCase: 1 }],
|
|
'max-len': ['error', { code: 140 }],
|
|
|
|
// Vue specific rules
|
|
'vue/multi-word-component-names': 'error',
|
|
'vue/component-name-in-template-casing': ['error', 'PascalCase'],
|
|
'vue/require-prop-types': 'error',
|
|
'vue/require-default-prop': 'error',
|
|
'vue/html-indent': ['error', 2],
|
|
'vue/no-unused-vars': 'error',
|
|
|
|
// Disable rules that conflict with Prettier
|
|
'vue/max-attributes-per-line': 'off',
|
|
'vue/singleline-html-element-content-newline': 'off',
|
|
'vue/html-self-closing': 'off',
|
|
|
|
// Naming conventions
|
|
camelcase: ['error', { properties: 'never', ignoreDestructuring: true, allow: ['^UNSAFE_'] }],
|
|
|
|
// Complexity rules (disabled as per requirements)
|
|
complexity: 'off',
|
|
'max-depth': 'off',
|
|
'max-lines': 'off',
|
|
'max-params': 'off',
|
|
},
|
|
},
|
|
|
|
{
|
|
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.',
|
|
},
|
|
{
|
|
selector: "CallExpression[callee.property.name='waitForTimeout']",
|
|
message:
|
|
'[BANNED] waitForTimeout is a fixed delay. → Subscribe to events instead (eth_newFilter for on-chain, waitForSelector/waitForURL for DOM). → Polling with timeout is acceptable only if no event source exists. → See AGENTS.md #Engineering Principles.',
|
|
},
|
|
{
|
|
selector:
|
|
"NewExpression[callee.name='Promise'] > ArrowFunctionExpression CallExpression[callee.name='setTimeout']",
|
|
message:
|
|
'[BANNED] Promise+setTimeout sleep pattern. → Use event subscription or polling with timeout instead. → See AGENTS.md #Engineering Principles.',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
|
|
{
|
|
name: 'app/tests-override',
|
|
files: ['src/**/__tests__/**/*.ts', 'src/**/__tests__/**/*.tsx'],
|
|
languageOptions: {
|
|
parserOptions: {
|
|
projectService: false,
|
|
project: undefined,
|
|
},
|
|
},
|
|
},
|
|
|
|
prettier,
|
|
];
|