Skip to content

Instantly share code, notes, and snippets.

@slinkardbrandon
Created May 22, 2026 20:16
Show Gist options
  • Select an option

  • Save slinkardbrandon/50e58e913bf0e8a5d3675cb6d6982081 to your computer and use it in GitHub Desktop.

Select an option

Save slinkardbrandon/50e58e913bf0e8a5d3675cb6d6982081 to your computer and use it in GitHub Desktop.
eslint config suredocs
// @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