teqo/eslint.config.mjs
Francisco Ignácio Fontoura Solla c706c62098 Pass 2 W2: components/campaign em subpastas por dominio
106 arquivos flat -> 15 dominios (actionPlan, advisor, auth, dashboard, demand, invite, leadership, map, municipality, organization, stateDeputy, supporter, votePledge, shared, shell) via git mv; imports absolutos reescritos em 98 arquivos; excecao de lint do campaign-logo e paths dos specs de contrato (campaignInviteUi, campaignComponents) atualizados. Sem mudanca de conteudo nos componentes.
2026-07-25 14:52:17 -03:00

192 lines
5.5 KiB
JavaScript

import { FlatCompat } from '@eslint/eslintrc'
import prettier from 'eslint-config-prettier/flat'
import checkFile from 'eslint-plugin-check-file'
import { globalIgnores } from 'eslint/config'
import { dirname } from 'path'
import { fileURLToPath } from 'url'
import { localRules } from './eslint-local-rules.mjs'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
// eslint-config-next ships legacy (eslintrc) configs, so wrap them with
// FlatCompat to use them from this flat config with ESLint 9.
const compat = new FlatCompat({ baseDirectory: __dirname })
const frameworkComponentFiles = [
'src/app/**/{page,layout,loading,error,not-found,template,default,global-error}.tsx',
]
// These predate the naming convention. Keep them linted by Next's rules while
// requiring new component files to use PascalCase.
const legacyComponentFilenameIgnores = [
'src/components/campaign/shell/campaign-logo.tsx',
'src/components/socialIcons.tsx',
'src/components/socialLinks.tsx',
'src/components/ui/breadcrumb.tsx',
'src/components/ui/button.tsx',
'src/components/ui/card.tsx',
'src/components/ui/combobox.tsx',
'src/components/ui/dialog.tsx',
'src/components/ui/field.tsx',
'src/components/ui/input-group.tsx',
'src/components/ui/input.tsx',
'src/components/ui/label.tsx',
'src/components/ui/native-select.tsx',
'src/components/ui/select.tsx',
'src/components/ui/separator.tsx',
'src/components/ui/skeleton.tsx',
'src/components/ui/textarea.tsx',
'src/components/ui/tooltip.tsx',
]
const legacyFrameworkExportIgnores = [
'src/app/(payload)/admin/\\[\\[...segments\\]\\]/not-found.tsx',
'src/app/(payload)/admin/\\[\\[...segments\\]\\]/page.tsx',
'src/app/(payload)/layout.tsx',
]
const legacyComponentSyntaxIgnores = [
'src/components/StateSelect.tsx',
'src/components/ThemeProvider.tsx',
]
const legacyCamelCaseFilenameIgnores = [
'src/lib/schemas/campaign-login.ts',
'src/lib/schemas/petition-form.ts',
'src/lib/schemas/whatsapp-form.ts',
'tests/int/petition-page-layout.int.spec.ts',
]
const eslintConfig = [
...compat.extends('next/core-web-vitals', 'next/typescript'),
{
files: ['src/**/*.tsx'],
ignores: [
...frameworkComponentFiles,
...legacyComponentFilenameIgnores,
...legacyFrameworkExportIgnores,
...legacyComponentSyntaxIgnores,
],
plugins: {
'check-file': checkFile,
local: localRules,
},
rules: {
'check-file/filename-naming-convention': [
'error',
{
'**/*.tsx': 'PASCAL_CASE',
},
],
'local/component-arrow-conventions': 'error',
'local/no-component-default-export': 'error',
'react/function-component-definition': [
'error',
{
namedComponents: 'arrow-function',
unnamedComponents: 'arrow-function',
},
],
},
},
{
files: frameworkComponentFiles,
ignores: legacyFrameworkExportIgnores,
plugins: {
'check-file': checkFile,
local: localRules,
},
rules: {
'check-file/filename-naming-convention': [
'error',
{
'**/*.tsx': '@(page|layout|loading|error|not-found|template|default|global-error)',
},
],
'local/framework-default-export': 'error',
'react/function-component-definition': 'off',
},
},
{
files: [
'src/components/**/*.{js,ts}',
'src/lib/**/*.{js,ts}',
'src/utilities/**/*.{js,ts}',
'tests/**/*.{js,jsx,ts,tsx}',
],
ignores: legacyCamelCaseFilenameIgnores,
plugins: {
'check-file': checkFile,
},
rules: {
'check-file/filename-naming-convention': [
'error',
{
'**/*.{js,jsx,ts,tsx}': 'CAMEL_CASE',
},
{
ignoreMiddleExtensions: true,
},
],
},
},
{
// Catch Next.js invalid-use-server-value before `next build` (Vercel).
files: ['src/**/*.{ts,tsx}'],
plugins: {
local: localRules,
},
rules: {
'local/use-server-async-exports': 'error',
},
},
{
// Underscore-prefixed bindings are deliberate placeholders (unused action
// state, ignored tuple slots); everything else unused is dead code.
files: ['**/*.{js,mjs,jsx,ts,tsx}'],
rules: {
'@typescript-eslint/no-unused-vars': [
'warn',
{
args: 'all',
argsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
// `as never` silences every type check on the expression (worse than
// `any`). Fix the types instead; a justified eslint-disable comment is
// the only sanctioned escape hatch.
'no-restricted-syntax': [
'error',
{
selector: 'TSAsExpression > TSNeverKeyword',
message:
'Do not cast with `as never` — it disables type checking entirely. Fix the types or use a narrowly-typed helper.',
},
],
},
},
{
// Payload generates migration signatures with unused destructured args.
files: ['src/migrations/**/*.ts'],
rules: {
'@typescript-eslint/no-unused-vars': ['warn', { args: 'none' }],
},
},
prettier,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
'.next/**',
'out/**',
'build/**',
'next-env.d.ts',
// Playwright artifacts (generated on e2e failures/reports):
'playwright-report/**',
'test-results/**',
]),
]
export default eslintConfig