agent-ecosystem/eslint.fast.config.js

256 lines
7 KiB
JavaScript

import { defineConfig, globalIgnores } from 'eslint/config';
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import boundaries from 'eslint-plugin-boundaries';
import reactPlugin from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import jsxA11y from 'eslint-plugin-jsx-a11y';
import simpleImportSort from 'eslint-plugin-simple-import-sort';
import importPlugin from 'eslint-plugin-import';
import security from 'eslint-plugin-security';
import sonarjs from 'eslint-plugin-sonarjs';
import tailwindcss from 'eslint-plugin-tailwindcss';
import globals from 'globals';
export default defineConfig([
{
name: 'fast-linter-options',
linterOptions: {
reportUnusedDisableDirectives: 'off',
},
},
globalIgnores([
'dist/**',
'dist-electron/**',
'build/**',
'node_modules/**',
'out/**',
'landing/.nuxt/**',
]),
js.configs.recommended,
...tseslint.configs.recommended,
{
name: 'fast-known-plugin-namespaces',
plugins: {
boundaries,
import: importPlugin,
security,
sonarjs,
tailwindcss,
},
rules: {
'@typescript-eslint/no-require-imports': 'warn',
'no-control-regex': 'warn',
'no-unsafe-finally': 'warn',
'no-useless-escape': 'warn',
},
},
{
name: 'fast-typescript',
files: ['**/*.{ts,tsx}'],
languageOptions: {
parser: tseslint.parser,
parserOptions: {
projectService: false,
},
},
rules: {
'no-undef': 'off',
'prefer-const': 'error',
'no-var': 'error',
eqeqeq: ['error', 'always', { null: 'ignore' }],
'@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
},
},
{
name: 'fast-imports',
files: ['src/**/*.{js,jsx,ts,tsx}', 'test/**/*.{ts,tsx}', 'packages/agent-graph/src/**/*.{ts,tsx}'],
plugins: {
'simple-import-sort': simpleImportSort,
},
rules: {
'simple-import-sort/imports': [
'error',
{
groups: [
['^\\u0000'],
['^node:'],
['^react', '^react-dom'],
['^@?\\w'],
['^@/'],
['^\\.\\.(?!/?$)', '^\\.\\./?$'],
['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'],
['^.+\\u0000$'],
],
},
],
'simple-import-sort/exports': 'error',
},
},
{
name: 'fast-node-globals',
files: ['src/main/**/*.ts', 'src/preload/**/*.ts', 'scripts/**/*.{js,mjs,ts}', 'test/**/*.ts'],
languageOptions: {
globals: {
...globals.node,
},
},
},
{
name: 'fast-browser-globals',
files: ['src/renderer/**/*.{ts,tsx}', 'src/features/**/renderer/**/*.{ts,tsx}'],
languageOptions: {
globals: {
...globals.browser,
},
},
},
{
name: 'fast-react',
files: ['src/renderer/**/*.{tsx,ts}', 'src/features/**/renderer/**/*.{tsx,ts}'],
plugins: {
react: reactPlugin,
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
'jsx-a11y': jsxA11y,
},
settings: {
react: {
version: 'detect',
},
},
rules: {
...reactPlugin.configs.recommended.rules,
...reactPlugin.configs['jsx-runtime'].rules,
...reactHooks.configs.recommended.rules,
...jsxA11y.configs.recommended.rules,
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
'react/prop-types': 'off',
'react-hooks/exhaustive-deps': 'warn',
'react-hooks/rules-of-hooks': 'warn',
'react-hooks/globals': 'off',
'react-hooks/purity': 'off',
'react-hooks/refs': 'off',
'react-hooks/set-state-in-effect': 'off',
'react-hooks/preserve-manual-memoization': 'off',
'react-hooks/immutability': 'off',
'jsx-a11y/click-events-have-key-events': 'warn',
'jsx-a11y/no-static-element-interactions': 'warn',
'jsx-a11y/label-has-associated-control': 'warn',
'jsx-a11y/no-noninteractive-tabindex': 'warn',
'jsx-a11y/no-autofocus': 'off',
'react/function-component-definition': [
'warn',
{
namedComponents: 'arrow-function',
unnamedComponents: 'arrow-function',
},
],
'react/jsx-key': [
'error',
{
checkFragmentShorthand: true,
checkKeyMustBeforeSpread: true,
},
],
'react/self-closing-comp': ['error', { component: true, html: true }],
},
},
{
name: 'fast-feature-entrypoints',
files: [
'src/main/**/*.{ts,tsx}',
'src/preload/**/*.{ts,tsx}',
'src/renderer/**/*.{ts,tsx}',
'src/shared/**/*.{ts,tsx}',
],
rules: {
'no-restricted-imports': [
'error',
{
patterns: [
{
group: [
'@features/*/contracts/*',
'@features/*/core/**',
'@features/*/main/*',
'@features/*/preload/*',
'@features/*/renderer/*',
],
message: 'Import feature public entrypoints only.',
},
],
},
],
},
},
{
name: 'fast-feature-core-guards',
files: ['src/features/*/core/{domain,application}/**/*.ts'],
rules: {
'no-restricted-imports': [
'error',
{
paths: [
{ name: 'electron', message: 'Feature core must stay Electron-free.' },
{ name: 'fastify', message: 'Feature core must stay transport-free.' },
{ name: 'child_process', message: 'Feature core must not spawn processes directly.' },
{
name: 'node:child_process',
message: 'Feature core must not spawn processes directly.',
},
],
patterns: [
{
group: ['@main/*', '@preload/*', '@renderer/*'],
message: 'Feature core must stay process-agnostic.',
},
{
group: ['@features/*/main/**', '@features/*/preload/**', '@features/*/renderer/**'],
message: 'Feature core must not import runtime or transport layers.',
},
],
},
],
},
},
{
name: 'fast-feature-renderer-ui-guards',
files: ['src/features/*/renderer/ui/**/*.{ts,tsx}'],
rules: {
'no-restricted-imports': [
'error',
{
paths: [
{ name: '@renderer/api', message: 'renderer/ui must stay presentational.' },
{ name: '@renderer/store', message: 'renderer/ui must stay store-free.' },
{ name: 'electron', message: 'renderer/ui must stay Electron-free.' },
],
patterns: [
{ group: ['@main/*'], message: 'renderer/ui must not import main modules.' },
{ group: ['@renderer/store/*'], message: 'renderer/ui must stay store-free.' },
],
},
],
},
},
]);