Español
Monorepos
oxlint-tailwindcss v1 soporta dos patrones para monorepos, ambos totalmente deterministas — sin heurísticas de globs, sin sorpresas del auto-detect. Elige el que se ajuste mejor a cómo tu equipo ya estructura su config.
Patrón A — un solo config raíz con mapping de globs
Un .oxlintrc.json en la raíz. entryPoint es un array de objetos { files, use }, evaluados en orden de declaración; el primer glob que coincide con el archivo lintado gana.
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" }
]
}
}
}Cuándo conviene:
- Todos tus packages comparten las mismas reglas.
- Quieres una sola fuente de verdad de qué se lintea cómo.
- Ya estás usando
overridesde oxlint para personalización por regla en la raíz.
Un entry de fallback "**" al final es recomendado — sin él, cualquier archivo fuera de los globs explícitos va a fallar con MissingEntryPointError.
Patrón B — un .oxlintrc.json por package
Cada package tiene su propio config y su propio CSS entry. oxlint resuelve el config más cercano al archivo lintado, así que los overrides por package "just work".
my-monorepo/
├── .oxlintrc.json # base de reglas (opcional entryPoint para archivos top-level)
├── packages/
│ ├── ui/
│ │ ├── .oxlintrc.json # extends ../../.oxlintrc.json, define entryPoint
│ │ ├── src/styles.css
│ │ └── src/Button.tsx
│ ├── admin/
│ │ ├── .oxlintrc.json
│ │ ├── src/admin.css
│ │ └── src/Page.tsx
│ └── shared-utils/ # sin Tailwind, sin config
│ └── index.tsjsonc
// packages/ui/.oxlintrc.json
{
"extends": ["../../.oxlintrc.json"],
"settings": {
"tailwindcss": { "entryPoint": "./src/styles.css" }
}
}Un entryPoint string relativo se resuelve respecto al directorio del .oxlintrc.json más cercano que lo contiene —es decir, el config que lo declara— y no respecto al working directory actual. Así ./src/styles.css apunta a packages/ui/src/styles.css ya sea que oxlint corra desde el package (cd packages/ui && oxlint, como en la CLI) o desde la raíz del workspace (como hacen las extensiones de editor, p. ej. el plugin de oxlint para VS Code). Si el archivo no se encuentra ahí, la resolución cae de vuelta al CWD. Esto es lo que hace que los configs por package se comporten igual en la terminal y en el editor (issue #39).
Configs explícitos con -c
El anclaje usa la config de oxlint más cercana (.oxlintrc.json, .oxlintrc.jsonc, oxlint.config.ts u oxlint.config.mts) porque ese es el config que oxlint aplica bajo su descubrimiento de configs anidados por defecto. Si en cambio pasas oxlint -c <config> (o deshabilitas los configs anidados), oxlint usa solo ese archivo — y el plugin no puede saber cuál config fue (oxlint le expone la ruta del archivo y el CWD, nunca la ruta del config). El fallback config-más-cercano + CWD igual resuelve bien para el layout habitual, pero si mezclas un config explícito -c con un .oxlintrc.json anidado no relacionado debajo del archivo lintado, prefiere un entryPoint absoluto (o la forma de mapping) para eliminar toda ambigüedad.
Cuándo conviene:
- Los packages divergen mucho en reglas, plugins o globals.
- Distintos equipos son dueños de distintos packages y quieren config autocontenido.
- Tienes packages sin Tailwind que NO deberían ejecutar el plugin.
Opciones y settings distintos por package
Las opciones de las reglas y settings.tailwindcss se resuelven para cada archivo, así que los packages pueden diferir en ambos — dentro de una misma corrida, en la terminal y en el editor. Dónde puedes definirlos:
| Definido en | Opciones de reglas | settings.tailwindcss |
|---|---|---|
Un bloque overrides del config raíz | ✓ | ✗ |
| Un config anidado (Patrón B) | ✓ | ✓ |
El mapping de entryPoint raíz (Patrón A) | — | solo entryPoint |
oxlint rechaza settings dentro de overrides (unknown field `settings` ), así que un package que necesita sus propios attributes, callees, rootFontSize o debug lleva un config anidado. Un config anidado es independiente salvo que liste el raíz en extends — entonces hereda jsPlugins y rules (nunca settings, que cada config pone por su cuenta) y solo agrega lo que cambia, como en el ejemplo del Patrón B. Una config compartida como paquete trae el plugin con oxlint-tailwindcss/config: mira oxlint.config.ts y configs compartidas.
Vite+ y editores
- Vite+ (
vp lint) no descubre los.oxlintrc.jsonanidados (oxlint 1.85+, oxc#26763): solo aplica el config raíz. Usa el mapping deentryPointdel Patrón A para el CSS por package; los demás settings no pueden variar por package ahí. - Editores (la extensión de oxc para VS Code y otros clientes LSP) descubren los configs anidados cuando
oxc.configPathno está definido. Desde oxlint 1.84 un""vacío también cuenta como no definido (oxc#26762); apuntarlo a un archivo desactiva los configs anidados, comooxlint -c.
Lo que NO funciona en v1
entryPoint: ["a.css", "b.css"]— la forma legacystring[]se removió porque su heurística de "entry más cercano por prefix de path" era no-determinista en edge cases. v1 lanzaDeprecatedEntryPointShapeErrore imprime el snippet de migración directo en el diagnóstico.
Archivos que no necesitan el plugin
Si un archivo vive en un package sin Tailwind — por ejemplo, una utilidad pura de TypeScript — las reglas DS-dependientes simplemente no se disparan, porque solo corren cuando los extractors encuentran strings de clases. No hace falta deshabilitar el plugin por archivo.
Verificando qué CSS resolvió por archivo
Activa settings.tailwindcss.debug (o la variable DEBUG=oxlint-tailwindcss) y oxlint va a imprimir una línea por archivo mostrando qué CSS cargó el plugin para ese archivo. Útil cuando un glob coincide con el mapping equivocado.
Motores de Tailwind por package
Para cada entry point CSS resuelto, el plugin carga el motor de Tailwind (@tailwindcss/node) desde el node_modules de ese package, así los packages fijados a versiones distintas de Tailwind se lintean cada uno con el motor con el que compilan. El guard de versión corre por package también — un motor más viejo que v4.1.15, un major futuro, o un drift de major respecto del build de ese package falla fuerte (ver allowUntestedEngine). No tienes que hacer nada para esto; sigue la misma resolución de entry point por archivo de arriba.