Skip to main content

Concern

Linting and static code quality (style, bugs, best practices, import order).

Technology

ESLint, eslint-plugin-import, eslint-plugin-unused-imports

Documentation

Integration

Runs in backend, frontend, and shared. Often paired with formatting (Prettier); use eslint-config-prettier so ESLint does not enforce style rules Prettier handles. Use eslint-plugin-import for import ordering (import/order) and eslint-plugin-unused-imports for removing unused imports (no-unused-imports); both are auto-fixable with eslint --fix. Can be run via ci (e.g. lint job).

Implementation

Linting is set up similarly to formatting: a root lint script runs lint in each workspace package; every package that contains source has a lint script and an ESLint config (flat config: eslint.config.mjs or eslint.config.js).

  • Root: From the repo root, pnpm lint runs pnpm -r run lint so every workspace package lints its own sources. Use pnpm lint (each package’s lint script includes --fix) to auto-fix (e.g. import order, unused imports removal, other fixable rules). The root does not install ESLint; each package declares its own ESLint and plugin dependencies.
  • Backend: ESLint flat config in backend/eslint.config.mjs. Includes eslint-plugin-import (import/order) and eslint-plugin-unused-imports (no-unused-imports, no-unused-vars with argsIgnorePattern). Uses typescript-eslint, eslint-plugin-prettier, and eslint-config-prettier. Lint script: eslint "{src,apps,libs,test}/**/*.ts" --fix (or equivalent).
  • Desktop (frontends/desktop): ESLint flat config in frontends/desktop/eslint.config.js. Includes eslint-plugin-import (import/order) and eslint-plugin-unused-imports. Uses @eslint/js, typescript-eslint, react-hooks, react-refresh; optionally eslint-config-prettier if Prettier is integrated. Lint script: eslint . --fix (or equivalent).
  • Shared: ESLint flat config in shared/eslint.config.mjs (or .js). Minimal config: recommended + typescript-eslint + eslint-plugin-import (import/order) + eslint-plugin-unused-imports. Lint script: eslint "src/**/*.ts" --fix.
  • Frontends/shared: ESLint flat config in frontends/shared/eslint.config.mjs. Same idea as shared; include import/order and unused-imports. Lint script: eslint "src/**/*.{ts,tsx}" --fix.

Import order: Use import/order with groups ["builtin", "external", "internal", "parent", "sibling", "index"], alphabetize for consistent sorting within groups, and newlines-between so groups are separated by a newline. Internal group is for workspace aliases (e.g. shared/*, frontends-shared/*). This keeps the generated boilerplate and project code consistent.

Cursor agent hook: The boilerplate can run lint with fix after format in the afterFileEdit hook (e.g. .cursor/hooks/format.sh runs pnpm format then pnpm lint -- --fix) so agent edits are formatted and lint-fixed automatically.