Monorepos
Import Integrity supports three setup patterns for monorepos, each with different tradeoffs around performance, configuration overhead, and which rules are available.
Option 1: separate configs per package
Use this when each package is linted independently.
Each package has its own ESLint or Oxlint config, with its own packageRootDir and the standard recommended config. The monorepo task runner (Nx, Turborepo, etc.) handles invoking the linter across packages.
In this setup, each config sees only its own package. This means cross-package rules like no-unused-package-exports can't be used with this setup because they require a config that sees the whole monorepo. If you need those rules, see Option 2 or 3.
Setup
Create an ESLint config file in each package root directory and set settings['import-integrity'].packageRootDir to the package root directory.
Example structure:
repo
└── packages
├── web
│ ├── eslint.config.mjs
│ └── src
└── shared
├── eslint.config.mjs
├── import-integrity.config.jsonc
└── srcIn each package's ESLint config:
import { defineConfig } from 'eslint/config';
import importIntegrityPlugin from 'import-integrity-lint';
export default defineConfig([
{
settings: {
'import-integrity': {
packageRootDir: import.meta.dirname,
},
},
},
importIntegrityPlugin.configs.recommended,
]);Option 2: one root config with monorepoRootDir
Use this when you want a single config covering the whole monorepo, or when nested configs cause issues with other tools.
A single root-level config covers every package. Workspace packages are discovered from your monorepo configuration under monorepoRootDir. Individual packages can override their settings with an import-integrity.config.json file at the package root.
WARNING
This option has performance implications for larger codebases, since we need to run a single lint job for the entire monorepo. This means we can't parallelize across packages or skip linting packages with no changes. See Option 3 for a more performant alternative.
Setup
- Set
settings['import-integrity'].monorepoRootDirin your root config and add themonorepoRecommendedconfig. - Optionally add an
import-integrity.config.json(or.jsonc) file to any workspace package that needs non-default package-scoped options.
Example structure:
repo
├── eslint.config.mjs
└── packages
├── web
│ └── src
└── shared
├── import-integrity.config.jsonc
└── srcRoot ESLint config:
import { defineConfig } from 'eslint/config';
import importIntegrityPlugin from 'import-integrity-lint';
export default defineConfig([
{
settings: {
'import-integrity': {
monorepoRootDir: import.meta.dirname,
},
},
},
importIntegrityPlugin.configs.monorepoRecommended,
]);shared package import-integrity config:
{
"testFilePatterns": ["__custom_test__"]
}Note: config files outside the declared workspace globs are ignored.
Option 3: root + separate configs (recommended)
Recommended for most monorepos. Combines a minimal root config for repo-wide rules with per-package configs for performance.
This setup uses a minimal root config to enable repo-wide rules (like no-unused-package-exports) alongside per-package configs that handle everything else. Both run, orchestrated by your monorepo task runner.
Why this is faster
ESLint is single-threaded by default, so a single root config that lints the whole monorepo runs serially. Oxlint itself is multi-threaded, but Oxlint's JS plugins (including Import Integrity) are not, so we still run into the same performance limitation.
Per-package configs can be parallelized and cached by task runners like Nx and Turborepo, which makes a noticeable difference for editor responsiveness and AI agents using LSP integrations.
Setup
- In your root config, set
settings['import-integrity'].monorepoRootDirand add themonorepoRecommendedconfig. - In each package's config, set
settings['import-integrity'].packageRootDirand add the regularrecommendedconfig. - If a package needs non-default options (alias, testFilePatterns, etc.), add an
import-integrity.config.jsonfile at the package root. Don't put these settings in the package's ESLint config.
Example structure:
repo
├── eslint.config.mjs
└── packages
├── web
│ ├── eslint.config.mjs
│ └── src
└── shared
├── eslint.config.mjs
├── import-integrity.config.jsonc
└── srcRoot config:
import { defineConfig } from 'eslint/config';
import importIntegrityPlugin from 'import-integrity-lint';
export default defineConfig([
{
settings: {
'import-integrity': {
monorepoRootDir: import.meta.dirname,
},
},
},
importIntegrityPlugin.configs.monorepoRecommended,
]);shared and web package configs:
import { defineConfig } from 'eslint/config';
import importIntegrityPlugin from 'import-integrity-lint';
export default defineConfig([
{
settings: {
'import-integrity': {
packageRootDir: import.meta.dirname,
},
},
},
importIntegrityPlugin.configs.recommended,
]);For a complete working example of this approach, see Aquarium Control.
Oxlint nested-config caveat: Oxlint has a known issue with nested configs in LSP contexts (editors, AI agents). If you hit this, use Option 2 until this issue is resolved. ESLint isn't affected.