# Monorepos

`oxlint-tailwindcss` v1 supports two patterns for monorepos, both fully deterministic — no glob
heuristics, no auto-detect surprises. Pick whichever maps better to how your team already structures
its config.

## Pattern A — single root config with a glob mapping

One `.oxlintrc.json` at the root. `entryPoint` is an array of `{ files, use }` objects, evaluated in
declaration order; the first glob matching the linted file wins.

```jsonc
// /my-monorepo/.oxlintrc.json
{
  "$schema": "./node_modules/oxlint/configuration_schema.json",
  "jsPlugins": ["oxlint-tailwindcss"],
  "rules": {
    "tailwindcss/no-unknown-classes": "error"
  },
  "settings": {
    "tailwindcss": {
      "entryPoint": [
        { "files": "packages/ui/**",     "use": "packages/ui/src/styles.css" },
        { "files": "packages/admin/**",  "use": "packages/admin/src/admin.css" },
        { "files": "packages/web/**",    "use": "packages/web/src/app.css" },
        { "files": "**",                  "use": "src/global.css" }
      ]
    }
  }
}
```

When to use this:

- All your packages share the same rule set.
- You want one source of truth for what lints how.
- You're already using oxlint's `overrides` for rule-by-rule customization at the root.

A `"**"` fallback entry at the end is recommended — without it, any file outside the explicit globs
will fail with `MissingEntryPointError`.

## Pattern B — one `.oxlintrc.json` per package

Each package owns its own config and CSS entry. oxlint resolves the closest config to the file being
linted, so per-package overrides just work.

```
my-monorepo/
├── .oxlintrc.json                       # rules base (optional entryPoint for top-level files)
├── packages/
│   ├── ui/
│   │   ├── .oxlintrc.json               # extends ../../.oxlintrc.json, sets entryPoint
│   │   ├── src/styles.css
│   │   └── src/Button.tsx
│   ├── admin/
│   │   ├── .oxlintrc.json
│   │   ├── src/admin.css
│   │   └── src/Page.tsx
│   └── shared-utils/                    # no Tailwind, no config needed
│       └── index.ts
```

```jsonc
// packages/ui/.oxlintrc.json
{
  "extends": ["../../.oxlintrc.json"],
  "settings": {
    "tailwindcss": { "entryPoint": "./src/styles.css" }
  }
}
```

A **relative** string `entryPoint` is resolved against the directory of the nearest enclosing
`.oxlintrc.json` — i.e. the config that declares it — not the current working directory. So
`./src/styles.css` points at `packages/ui/src/styles.css` whether oxlint runs from the package
(`cd packages/ui && oxlint`, as on the CLI) or from the workspace root (as editor extensions like
the VS Code oxlint plugin do). If the file isn't found there, resolution falls back to the CWD. This
is what makes per-package configs behave the same in the terminal and the editor (issue #39).

::: tip Explicit `-c` configs

Anchoring uses the nearest oxlint config (`.oxlintrc.json`, `.oxlintrc.jsonc`, `oxlint.config.ts` or
`oxlint.config.mts`) because that's the config oxlint applies under its default nested-config
discovery. If you instead pass `oxlint -c <config>` (or disable nested configs), oxlint uses only
that one file — and the plugin can't see which config that was (oxlint exposes the file path and CWD
to plugins, never the config path). The nearest-config + CWD fallback still resolves correctly for
the usual layout, but if you mix an explicit `-c` config with an unrelated nested `.oxlintrc.json`
below the linted file, prefer an **absolute** `entryPoint` (or the mapping shape) to remove all
ambiguity.

:::

When to use this:

- Packages diverge significantly in rules, plugins, or globals.
- Different teams own different packages and want self-contained config.
- You have packages without Tailwind that should not run the plugin at all.

## Different options and settings per package

Rule options and `settings.tailwindcss` are resolved for each file, so packages can differ in both —
within one run, in the terminal and in the editor. Where you can set them:

| Set in                                    | Rule options | `settings.tailwindcss` |
| ----------------------------------------- | ------------ | ---------------------- |
| An `overrides` block of the root config   | ✓            | ✗                      |
| A nested config (Pattern B)               | ✓            | ✓                      |
| The root `entryPoint` mapping (Pattern A) | —            | `entryPoint` only      |

oxlint rejects `settings` inside `overrides` (``unknown field `settings` ``), so a package that
needs its own `attributes`, `callees`, `rootFontSize` or `debug` gets a nested config. A nested
config stands alone unless it lists the root in `extends` — then it inherits `jsPlugins` and `rules`
(never `settings`, which every config sets for itself) and only adds what differs, as in the Pattern
B example. A config shared as a package brings the plugin through `oxlint-tailwindcss/config`
instead: see
[`oxlint.config.ts` and shared configs](/setup#with-oxlint-config-ts-or-in-a-shared-config).

::: warning Vite+ and editors

- **Vite+** (`vp lint`) doesn't discover nested `.oxlintrc.json` files (oxlint 1.85+,
  [oxc#26763](https://github.com/oxc-project/oxc/pull/26763)): only the root config applies. Use
  Pattern A's `entryPoint` mapping for per-package CSS; other settings can't vary by package there.
- **Editors** (the oxc VS Code extension and other LSP clients) discover nested configs when
  `oxc.configPath` is unset. Since oxlint 1.84 an empty `""` counts as unset too
  ([oxc#26762](https://github.com/oxc-project/oxc/pull/26762)); pointing it at one file disables
  nested configs, like `oxlint -c`.

:::

## What does **not** work in v1

- `entryPoint: ["a.css", "b.css"]` — the legacy `string[]` shape was removed because its "closest
  entry by path prefix" heuristic was non-deterministic in edge cases. v1 throws
  `DeprecatedEntryPointShapeError` and prints the migration snippet directly in the diagnostic.

## Files that should not need the plugin

If a file lives in a package without any Tailwind — e.g. a pure TypeScript utility — the plugin's
DS-dependent rules will simply not fire on it, because they only run when the extractors find class
strings. There is no need to disable the plugin per-file.

## Verifying which CSS resolved per file

Set `settings.tailwindcss.debug` (or the `DEBUG=oxlint-tailwindcss` env var) and oxlint will print
one line per file showing which CSS the plugin loaded for it. Useful when a glob is matching the
wrong mapping.

## Per-package Tailwind engines

For each resolved CSS entry point the plugin loads the Tailwind engine (`@tailwindcss/node`) from
that package's own `node_modules`, so packages pinned to different Tailwind versions are each linted
with the engine they actually build with. The version guard runs per package too — an engine older
than v4.1.15, a future major, or a major-version drift from that package's build fails loud (see
[`allowUntestedEngine`](/settings#allowuntestedengine)). You don't need to do anything for this; it
follows the same per-file entry-point resolution above.
