Created
May 22, 2026 20:16
-
-
Save slinkardbrandon/50e58e913bf0e8a5d3675cb6d6982081 to your computer and use it in GitHub Desktop.
eslint config suredocs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // @ts-check | |
| import { defineConfig } from "eslint/config"; | |
| import tseslint from "typescript-eslint"; | |
| import react from "eslint-plugin-react"; | |
| import reactHooks from "eslint-plugin-react-hooks"; | |
| import jsxA11y from "eslint-plugin-jsx-a11y"; | |
| import i18next from "eslint-plugin-i18next"; | |
| import security from "eslint-plugin-security"; | |
| import unicorn from "eslint-plugin-unicorn"; | |
| import sonarjs from "eslint-plugin-sonarjs"; | |
| import { suredocsPlugin } from "./packages/config/eslint-rules.js"; | |
| export default defineConfig([ | |
| { | |
| ignores: [ | |
| ".claude/**", | |
| "**/dist/**", | |
| "**/.next/**", | |
| "**/.turbo/**", | |
| "**/dev-dist/**", | |
| "**/.nuxt/**", | |
| "**/.expo/**", | |
| "**/.wrangler/**", | |
| "**/.alchemy/**", | |
| "**/.svelte-kit/**", | |
| "**/.source/**", | |
| "**/convex/_generated/**", | |
| "**/routeTree.gen.ts", | |
| "**/cypress/screenshots/**", | |
| "**/cypress/videos/**", | |
| "**/cypress/downloads/**", | |
| "**/node_modules/**", | |
| "packages/extraction-harness/**", | |
| ], | |
| }, | |
| { | |
| files: ["**/*.{ts,tsx}"], | |
| extends: [...tseslint.configs.strict, sonarjs.configs.recommended], | |
| plugins: { suredocs: suredocsPlugin }, | |
| rules: { | |
| "@typescript-eslint/no-unused-vars": [ | |
| "error", | |
| { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }, | |
| ], | |
| "@typescript-eslint/no-explicit-any": "error", | |
| "@typescript-eslint/no-inferrable-types": "error", | |
| "@typescript-eslint/consistent-type-imports": [ | |
| "error", | |
| { prefer: "type-imports", fixStyle: "inline-type-imports" }, | |
| ], | |
| "@typescript-eslint/no-import-type-side-effects": "error", | |
| "no-console": "error", | |
| "suredocs/no-logger-spread": "error", | |
| "sonarjs/todo-tag": "warn", | |
| "sonarjs/prefer-read-only-props": "off", | |
| "sonarjs/deprecation": "off", | |
| "sonarjs/no-commented-code": "off", | |
| "sonarjs/aws-restricted-ip-admin-access": "off", | |
| "unicorn/string-content": [ | |
| "error", | |
| { | |
| patterns: { | |
| // AI-generated typography artifacts — replace with ASCII equivalents | |
| "—": "--", | |
| "–": "-", | |
| "…": "...", | |
| "\u201C": '"', | |
| "\u201D": '"', | |
| "\u2018": "'", | |
| "\u2019": "'", | |
| // Box-drawing characters used in structural "ASCII art" comments/strings | |
| "─": false, | |
| "│": false, | |
| "┌": false, | |
| "┐": false, | |
| "└": false, | |
| "┘": false, | |
| }, | |
| }, | |
| ], | |
| }, | |
| }, | |
| { | |
| files: [ | |
| "services/*/src/**/*.ts", | |
| "jobs/*/src/**/*.ts", | |
| "packages/api/src/**/*.ts", | |
| "packages/auth/src/**/*.ts", | |
| "packages/db/src/**/*.ts", | |
| "packages/payment-gate/src/**/*.ts", | |
| ], | |
| languageOptions: { | |
| parserOptions: { | |
| projectService: true, | |
| }, | |
| }, | |
| rules: { | |
| "@typescript-eslint/no-floating-promises": "error", | |
| "@typescript-eslint/no-misused-promises": [ | |
| "error", | |
| { checksVoidReturn: { attributes: false } }, | |
| ], | |
| }, | |
| }, | |
| { | |
| files: ["**/*.{ts,tsx}"], | |
| extends: [unicorn.configs.recommended], | |
| rules: { | |
| "unicorn/filename-case": "off", | |
| "unicorn/no-null": "off", | |
| "unicorn/prevent-abbreviations": [ | |
| "error", | |
| { | |
| replacements: { | |
| props: false, | |
| env: false, | |
| db: false, | |
| doc: false, | |
| fn: false, | |
| }, | |
| }, | |
| ], | |
| }, | |
| }, | |
| { | |
| files: ["**/*.{ts,tsx}"], | |
| plugins: { security }, | |
| rules: { | |
| "security/detect-unsafe-regex": "error", | |
| "security/detect-eval-with-expression": "error", | |
| "security/detect-bidi-characters": "error", | |
| }, | |
| }, | |
| { | |
| // The GCP metadata server is an HTTP-only internal service inside Cloud Run. | |
| // Suppressing this rule for the trigger file avoids false positives. | |
| files: ["packages/api/src/trigger.ts"], | |
| rules: { | |
| "sonarjs/no-clear-text-protocols": "off", | |
| }, | |
| }, | |
| { | |
| // Enforce typed i18n error codes — no plain English in TRPCError messages or Zod validators. | |
| // Scoped to API/server code that produces user-visible errors. | |
| files: ["packages/api/src/**/*.ts", "services/server/src/**/*.ts", "jobs/*/src/**/*.ts"], | |
| // Exclude env validation files and test setup — those messages are for developers, not users. | |
| ignores: ["**/env.ts", "**/__tests__/**", "**/*.test.ts", "**/*.test.tsx"], | |
| plugins: { suredocs: suredocsPlugin }, | |
| rules: { | |
| "suredocs/no-trpc-invalid-key": "error", | |
| "suredocs/no-zod-literal-message": "error", | |
| "suredocs/no-raw-error-message": "error", | |
| "suredocs/no-raw-throw-message": "error", | |
| }, | |
| }, | |
| { | |
| files: ["services/server/**/*.ts"], | |
| plugins: { security }, | |
| rules: { | |
| "security/detect-object-injection": "error", | |
| "security/detect-non-literal-regexp": "error", | |
| "security/detect-non-literal-require": "error", | |
| "security/detect-non-literal-fs-filename": "error", | |
| "security/detect-buffer-noassert": "error", | |
| "security/detect-child-process": "error", | |
| "security/detect-new-buffer": "error", | |
| "security/detect-no-csrf-before-method-override": "error", | |
| "security/detect-pseudoRandomBytes": "error", | |
| "security/detect-possible-timing-attacks": "error", | |
| }, | |
| }, | |
| { | |
| files: [ | |
| "apps/**/components/**/*.{ts,tsx}", | |
| "apps/**/pages/**/*.{ts,tsx}", | |
| "packages/**/components/**/*.{ts,tsx}", | |
| "packages/**/templates/**/*.{ts,tsx}", | |
| "services/**/components/**/*.{ts,tsx}", | |
| ], | |
| rules: { | |
| "unicorn/filename-case": ["error", { case: "pascalCase" }], | |
| }, | |
| }, | |
| { | |
| files: [ | |
| "apps/**/lib/**/*.{ts,tsx}", | |
| "apps/**/utils/**/*.{ts,tsx}", | |
| "apps/**/hooks/**/*.{ts,tsx}", | |
| "apps/**/types/**/*.{ts,tsx}", | |
| "packages/**/lib/**/*.{ts,tsx}", | |
| "packages/**/utils/**/*.{ts,tsx}", | |
| "packages/**/hooks/**/*.{ts,tsx}", | |
| "packages/**/types/**/*.{ts,tsx}", | |
| "services/**/lib/**/*.{ts,tsx}", | |
| "services/**/utils/**/*.{ts,tsx}", | |
| "services/**/hooks/**/*.{ts,tsx}", | |
| "services/**/types/**/*.{ts,tsx}", | |
| ], | |
| rules: { | |
| "unicorn/filename-case": ["error", { case: "camelCase" }], | |
| }, | |
| }, | |
| { | |
| // Cypress uses declare namespace Cypress — disable no-namespace there. | |
| // e2e.ts is a required Cypress filename — disable filename rule there too. | |
| // Test passwords and /tmp writes are expected in test/dev infrastructure. | |
| files: ["**/cypress/**/*.ts", "**/*.test.ts", "**/*.test.tsx"], | |
| rules: { | |
| "@typescript-eslint/no-namespace": "off", | |
| "@typescript-eslint/no-non-null-assertion": "off", | |
| "unicorn/prevent-abbreviations": "off", | |
| "unicorn/require-module-specifiers": "off", | |
| "unicorn/no-useless-undefined": ["error", { checkArguments: false }], | |
| "sonarjs/no-hardcoded-passwords": "off", | |
| "sonarjs/publicly-writable-directories": "off", | |
| }, | |
| }, | |
| { | |
| files: ["apps/web/src/**/*.{ts,tsx}"], | |
| rules: { | |
| "no-restricted-imports": [ | |
| "error", | |
| { | |
| patterns: [ | |
| { | |
| group: ["drizzle-orm", "drizzle-orm/*"], | |
| message: "DB libraries cannot be imported in the frontend.", | |
| }, | |
| { | |
| group: ["postgres"], | |
| message: "DB driver cannot be imported in the frontend.", | |
| }, | |
| ], | |
| }, | |
| ], | |
| }, | |
| }, | |
| { | |
| // seed.ts is a dev-only script — console output is intentional. | |
| files: ["**/seed.ts"], | |
| rules: { "no-console": "off" }, | |
| }, | |
| { | |
| files: ["apps/web/**/*.{ts,tsx}"], | |
| extends: [ | |
| react.configs.flat["jsx-runtime"], | |
| reactHooks.configs.flat.recommended, | |
| jsxA11y.flatConfigs.recommended, | |
| ], | |
| plugins: { i18next }, | |
| rules: { | |
| ...i18next.configs["flat/recommended"].rules, | |
| "i18next/no-literal-string": "warn", | |
| "react/button-has-type": "error", | |
| "react/forbid-dom-props": ["warn", { forbid: ["style"] }], | |
| "react-hooks/exhaustive-deps": "warn", | |
| // React Compiler rules (react-hooks v5+). Disabled because they add ~80% | |
| // of frontend lint time with minimal value until we adopt the compiler. | |
| "react-hooks/static-components": "off", | |
| "react-hooks/use-memo": "off", | |
| "react-hooks/purity": "off", | |
| "react-hooks/preserve-manual-memoization": "off", | |
| "react-hooks/component-hook-factories": "off", | |
| "react-hooks/immutability": "off", | |
| "react-hooks/globals": "off", | |
| "react-hooks/refs": "off", | |
| "react-hooks/set-state-in-effect": "off", | |
| "react-hooks/error-boundaries": "off", | |
| "react-hooks/set-state-in-render": "off", | |
| "react-hooks/unsupported-syntax": "off", | |
| "react-hooks/config": "off", | |
| "react-hooks/gating": "off", | |
| "react-hooks/incompatible-library": "off", | |
| "sonarjs/void-use": "off", | |
| }, | |
| }, | |
| { | |
| // CI tooling scripts — relax rules that don't apply to CLI scripts while keeping TS checks. | |
| // Must be last so it overrides the general TypeScript rule blocks above. | |
| files: [".github/scripts/**/*.ts"], | |
| rules: { | |
| "no-console": "off", | |
| "sonarjs/os-command": "off", | |
| "sonarjs/no-os-command-from-path": "off", | |
| "sonarjs/slow-regex": "off", | |
| "sonarjs/no-nested-template-literals": "off", | |
| "unicorn/prevent-abbreviations": "off", | |
| "unicorn/text-encoding-identifier-case": "off", | |
| "unicorn/import-style": "off", | |
| "unicorn/error-message": "off", | |
| }, | |
| }, | |
| ]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment