import nextPlugin from "@next/eslint-plugin-next"; import prettierConfig from "eslint-config-prettier/flat"; import { createTypeScriptImportResolver } from "eslint-import-resolver-typescript"; import importX, { createNodeResolver } from "eslint-plugin-import-x"; import jsxA11y from "eslint-plugin-jsx-a11y"; import reactPlugin from "eslint-plugin-react"; import reactHooksPlugin from "eslint-plugin-react-hooks"; import security from "eslint-plugin-security"; import globals from "globals"; import tseslint from "typescript-eslint"; export default tseslint.config( { ignores: [ ".now/**", "**/*.css", ".changeset/**", "dist/**", "esm/**", "public/**", "tests/**", "scripts/**", "*.config.js", "*.config.mjs", "auth.config.ts", "eslint.config.ts", "knip.config.ts", "playwright.config.ts", "vitest.config.ts", ".DS_Store", "node_modules/**", "coverage/**", ".next/**", "build/**", "next-env.d.ts", ], }, importX.flatConfigs.recommended, importX.flatConfigs.typescript, { files: ["**/*.{ts,tsx,js,jsx}"], linterOptions: { reportUnusedDisableDirectives: "error", }, plugins: { "@typescript-eslint": tseslint.plugin, "@next/next": nextPlugin, react: reactPlugin, "react-hooks": reactHooksPlugin, "jsx-a11y": jsxA11y, security, }, languageOptions: { parser: tseslint.parser, parserOptions: { projectService: { allowDefaultProject: [ // Duplicate of events-timeline.test.ts in the same folder; // TypeScript only picks the .ts sibling, so this .tsx file is // outside the project graph. Tracked for follow-up cleanup. "components/shared/events-timeline/events-timeline.test.tsx", ], }, tsconfigRootDir: import.meta.dirname, ecmaVersion: "latest", sourceType: "module", ecmaFeatures: { jsx: true, }, }, globals: { ...globals.browser, ...globals.node, ...globals.es2021, React: "readonly", }, }, settings: { react: { version: "detect", }, "import-x/resolver-next": [ createTypeScriptImportResolver({ alwaysTryTypes: true, project: "./tsconfig.json", }), createNodeResolver(), ], }, rules: { "no-console": ["error", { allow: ["error"] }], eqeqeq: "error", quotes: ["error", "double", { avoidEscape: true }], "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-unused-vars": [ "error", { enableAutofixRemoval: { imports: true }, argsIgnorePattern: "^_", varsIgnorePattern: "^_", caughtErrorsIgnorePattern: "^_", }, ], "security/detect-object-injection": "off", "eol-last": ["error", "always"], "import-x/order": [ "error", { groups: [ "builtin", "external", "internal", "parent", "sibling", "index", ], "newlines-between": "always", alphabetize: { order: "asc", caseInsensitive: true }, }, ], // Pre-existing duplicate exports and re-export shape mismatches are // tracked separately; the migration keeps behavior parity with the // legacy config until the rule is enforced in the canonical Base layer. "import-x/export": "off", "jsx-a11y/anchor-is-valid": [ "error", { components: ["Link"], specialLink: ["hrefLeft", "hrefRight"], aspects: ["invalidHref", "preferButton"], }, ], "jsx-a11y/alt-text": "error", "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn", "@next/next/no-html-link-for-pages": "error", "@next/next/no-img-element": "warn", "@next/next/no-sync-scripts": "error", }, }, prettierConfig, );