Files
flyer-crawler.projectium.com/docs/adr/0021-code-formatting-and-linting-unification.md
Torben Sorensen 25d6b76f6d
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Has been cancelled
ADR-026: Client-Side Logging + linting fixes
2026-01-09 17:58:21 -08:00

6.6 KiB

ADR-021: Code Formatting and Linting Unification

Date: 2025-12-12

Status: Accepted

Implemented: 2026-01-09

Context

The project contains both frontend (React) and backend (Node.js) code. While linters may be in use, there isn't a single, enforced standard for code style and quality across the entire repository. This leads to inconsistent code and time wasted in code reviews on stylistic debates.

Decision

We will mandate the use of Prettier for automated code formatting and a unified ESLint configuration for code quality rules across both frontend and backend. This will be enforced automatically using a pre-commit hook managed by Husky and lint-staged.

Consequences

Positive: Improves developer experience and team velocity by automating code consistency. Reduces time spent on stylistic code review comments. Enhances code readability and maintainability.

Negative: Requires an initial setup and configuration of Prettier, ESLint, and Husky. May require a one-time reformatting of the entire codebase.

Implementation Status

What's Implemented

  • Prettier Configuration - .prettierrc with consistent settings
  • Prettier Ignore - .prettierignore to exclude generated files
  • ESLint Configuration - eslint.config.js with TypeScript and React support
  • ESLint + Prettier Integration - eslint-config-prettier to avoid conflicts
  • Husky Pre-commit Hooks - Automatic enforcement on commit
  • lint-staged - Run linters only on staged files for performance

Implementation Details

Prettier Configuration

The project uses a consistent Prettier configuration in .prettierrc:

{
  "semi": true,
  "trailingComma": "all",
  "singleQuote": true,
  "printWidth": 100,
  "tabWidth": 2,
  "useTabs": false,
  "endOfLine": "auto"
}

ESLint Configuration

ESLint is configured with:

  • TypeScript support via typescript-eslint
  • React hooks rules via eslint-plugin-react-hooks
  • React Refresh support for HMR
  • Prettier compatibility via eslint-config-prettier
  • Relaxed rules for test files (see below)
// eslint.config.js (ESLint v9 flat config)
import globals from 'globals';
import tseslint from 'typescript-eslint';
import pluginReact from 'eslint-plugin-react';
import pluginReactHooks from 'eslint-plugin-react-hooks';
import pluginReactRefresh from 'eslint-plugin-react-refresh';
import eslintConfigPrettier from 'eslint-config-prettier';

export default tseslint.config(
  // ... configurations
  eslintConfigPrettier, // Must be last to override formatting rules
);

Relaxed Linting Rules for Test Files

Decision Date: 2026-01-09

Status: Active (revisit when product nears final release)

The following ESLint rules are relaxed for test files (*.test.ts, *.test.tsx, *.spec.ts, *.spec.tsx):

Rule Setting Rationale
@typescript-eslint/no-explicit-any off Mocking complexity often requires any; strict typing in tests adds friction without proportional benefit

Rationale:

  1. Tests are not production code - The primary goal of tests is verifying behavior, not type safety of the test code itself
  2. Mocking complexity - Mocking libraries often require type gymnastics; any simplifies creating partial mocks and test doubles
  3. Testing edge cases - Sometimes tests intentionally pass invalid types to verify error handling
  4. Development velocity - Strict typing in tests slows down test writing without proportional benefit during active development

Future Consideration: This decision should be revisited when the product is nearing its final stages. At that point, stricter linting in tests may be warranted to ensure long-term maintainability.

// eslint.config.js - Test file overrides
{
  files: ['**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts', '**/*.spec.tsx'],
  rules: {
    '@typescript-eslint/no-explicit-any': 'off',
  },
}

Pre-commit Hook

The pre-commit hook runs lint-staged automatically:

# .husky/pre-commit
npx lint-staged

lint-staged Configuration

lint-staged runs appropriate tools based on file type:

{
  "*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
  "*.{json,md,css,html,yml,yaml}": ["prettier --write"]
}

NPM Scripts

Script Description
npm run format Format all files with Prettier
npm run lint Run ESLint on all TypeScript/JavaScript files
npm run validate Run Prettier check + TypeScript check + ESLint

Key Files

File Purpose
.prettierrc Prettier configuration
.prettierignore Files to exclude from formatting
eslint.config.js ESLint flat configuration (v9)
.husky/pre-commit Pre-commit hook script
.lintstagedrc.json lint-staged configuration

Developer Workflow

Automatic Formatting on Commit

When you commit changes:

  1. Husky intercepts the commit
  2. lint-staged identifies staged files
  3. ESLint fixes auto-fixable issues
  4. Prettier formats the code
  5. Changes are automatically staged
  6. Commit proceeds if no errors

Manual Formatting

# Format entire codebase
npm run format

# Check formatting without changes
npx prettier --check .

# Run ESLint
npm run lint

# Run all validation checks
npm run validate

IDE Integration

For the best experience, configure your IDE:

VS Code - Install extensions:

  • Prettier - Code formatter
  • ESLint

Add to .vscode/settings.json:

{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  }
}

Troubleshooting

"eslint --fix failed"

ESLint may fail on unfixable errors. Review the output and manually fix the issues.

"prettier --write failed"

Check for syntax errors in the file that prevent parsing.

Bypassing Hooks (Emergency)

In rare cases, you may need to bypass hooks:

git commit --no-verify -m "emergency fix"

Use sparingly - the CI pipeline will still catch formatting issues.