Skip to content
Documentation GitHub
Build Errors

ESLint v9 Flat Config in Monorepo Packages

Historical Note: This project migrated from ESLint to oxlint. This document is preserved for reference but ESLint is no longer used in this project.

ESLint v9 Flat Config in Monorepo Packages

Problem

After upgrading to ESLint v9 (or when adding a new package to the monorepo), pnpm lint fails silently or with confusing errors. ESLint appears to run but finds nothing to lint, or reports that it cannot find a configuration file.

Symptoms:

  • pnpm lint exits non-zero with no lint output
  • ESLint reports “couldn’t find a configuration file”
  • Using --ext ts,tsx flag causes “Unknown CLI option” error
  • New monorepo package linting silently does nothing

Investigation

Steps Tried

  1. Checked if shared config (@inklings/eslint-config) was installed — it was, as a workspace dependency
  2. Checked if the shared config was in flat format — it was (exports an array)
  3. Realized the desktop app had no eslint.config.js to import the shared config

Root Cause

ESLint v9 is a breaking change that drops support for legacy configuration formats (.eslintrc.*, package.json eslintConfig field). It requires every package to have an eslint.config.js (or .mjs/.cjs) file that exports a flat config array.

Additionally, the --ext CLI flag was removed in v9. File type filtering is now handled within the config itself via the files property on config objects.

In a monorepo, each package that runs ESLint needs its own eslint.config.js that imports the shared config. Without it, ESLint has no configuration and either fails or does nothing.

Solution

1. Create eslint.config.js in each package

apps/desktop/eslint.config.js
import reactConfig from "@inklings/eslint-config/react";
export default [
...reactConfig,
{
ignores: ["dist/**", "src-tauri/**"],
},
];

2. Update lint scripts — remove --ext flag

// Before (ESLint v8)
"lint": "eslint src-react --ext ts,tsx"
// After (ESLint v9)
"lint": "eslint src-react"

Implementation Notes

  • The shared config already defines file type matching via files: ["**/*.{ts,tsx}"] internally
  • The ignores array replaces .eslintignore in flat config
  • Each new monorepo package needs this boilerplate — consider it part of the “new package checklist”

Prevention

Best Practices

  • When adding a new package to the monorepo, always create eslint.config.js that imports the shared config
  • Never use --ext flag with ESLint v9+
  • Test pnpm lint after adding any new package

Warning Signs

  • pnpm lint succeeds suspiciously fast with zero output — likely means ESLint found no config
  • Any lint script containing --ext is using legacy syntax

Checklist for New Packages

  1. Add @inklings/eslint-config as a devDependency
  2. Create eslint.config.js importing the appropriate shared config variant
  3. Add ignores for build output directories
  4. Add "lint": "eslint src" script (adjust path as needed)
  5. Verify: pnpm lint should produce output (even if clean)

References

Was this page helpful?