Showing preview only (1,001K chars total). Download the full file or copy to clipboard to get everything.
Repository: schoero/eslint-plugin-readable-tailwind
Branch: main
Commit: cc05c4eeee71
Files: 223
Total size: 937.5 KB
Directory structure:
gitextract_rhr3h2e2/
├── .cspell.json
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ ├── documentation.yml
│ │ ├── feature_request.yml
│ │ └── question.yml
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── .markdownlint.jsonc
├── .vscode/
│ ├── extensions.json
│ ├── launch.json
│ └── settings.json
├── CHANGELOG.md
├── CONTRIBUTING.md
├── FUNDING.yml
├── LICENSE
├── README.md
├── build/
│ ├── index.ts
│ └── utils.ts
├── changelog.config.js
├── docs/
│ ├── api/
│ │ └── defaults.md
│ ├── configuration/
│ │ └── advanced.md
│ ├── parsers/
│ │ ├── angular.md
│ │ ├── astro.md
│ │ ├── css.md
│ │ ├── html.md
│ │ ├── javascript.md
│ │ ├── jsx.md
│ │ ├── svelte.md
│ │ ├── tsx.md
│ │ ├── typescript.md
│ │ └── vue.md
│ ├── rules/
│ │ ├── enforce-canonical-classes.md
│ │ ├── enforce-consistent-class-order.md
│ │ ├── enforce-consistent-important-position.md
│ │ ├── enforce-consistent-line-wrapping.md
│ │ ├── enforce-consistent-variable-syntax.md
│ │ ├── enforce-consistent-variant-order.md
│ │ ├── enforce-logical-properties.md
│ │ ├── enforce-shorthand-classes.md
│ │ ├── no-conflicting-classes.md
│ │ ├── no-deprecated-classes.md
│ │ ├── no-duplicate-classes.md
│ │ ├── no-restricted-classes.md
│ │ ├── no-unknown-classes.md
│ │ └── no-unnecessary-whitespace.md
│ └── settings/
│ └── settings.md
├── eslint.config.ts
├── package.json
├── src/
│ ├── api/
│ │ ├── defaults.ts
│ │ └── types.ts
│ ├── async-utils/
│ │ ├── cache.ts
│ │ ├── escape.ts
│ │ ├── fs.ts
│ │ ├── module.ts
│ │ ├── operations.ts
│ │ ├── order.ts
│ │ ├── path.ts
│ │ ├── platform.ts
│ │ ├── regex.ts
│ │ ├── resolvers.ts
│ │ ├── tsconfig.ts
│ │ └── worker.ts
│ ├── configs/
│ │ ├── config.test.ts
│ │ └── config.ts
│ ├── options/
│ │ ├── callees/
│ │ │ ├── cc.test.ts
│ │ │ ├── cc.ts
│ │ │ ├── clb.test.ts
│ │ │ ├── clb.ts
│ │ │ ├── clsx.test.ts
│ │ │ ├── clsx.ts
│ │ │ ├── cn.test.ts
│ │ │ ├── cn.ts
│ │ │ ├── cnb.test.ts
│ │ │ ├── cnb.ts
│ │ │ ├── ctl.test.ts
│ │ │ ├── ctl.ts
│ │ │ ├── cva.test.ts
│ │ │ ├── cva.ts
│ │ │ ├── cx.test.ts
│ │ │ ├── cx.ts
│ │ │ ├── dcnb.test.ts
│ │ │ ├── dcnb.ts
│ │ │ ├── objstr.test.ts
│ │ │ ├── objstr.ts
│ │ │ ├── tv.test.ts
│ │ │ ├── tv.ts
│ │ │ ├── twJoin.test.ts
│ │ │ ├── twJoin.ts
│ │ │ ├── twMerge.test.ts
│ │ │ └── twMerge.ts
│ │ ├── default-options.test.ts
│ │ ├── default-options.ts
│ │ ├── descriptions.test.ts
│ │ ├── descriptions.ts
│ │ ├── migrate.test.ts
│ │ ├── migrate.ts
│ │ ├── schemas/
│ │ │ ├── attributes.ts
│ │ │ ├── callees.ts
│ │ │ ├── common.ts
│ │ │ ├── matchers.ts
│ │ │ ├── selectors.ts
│ │ │ ├── tags.ts
│ │ │ └── variables.ts
│ │ └── tags/
│ │ ├── twc.test.ts
│ │ ├── twc.ts
│ │ ├── twx.test.ts
│ │ └── twx.ts
│ ├── parsers/
│ │ ├── angular.test.ts
│ │ ├── angular.ts
│ │ ├── css.test.ts
│ │ ├── css.ts
│ │ ├── es.test.ts
│ │ ├── es.ts
│ │ ├── html.test.ts
│ │ ├── html.ts
│ │ ├── jsx.test.ts
│ │ ├── jsx.ts
│ │ ├── svelte.test.ts
│ │ ├── svelte.ts
│ │ ├── vue.test.ts
│ │ └── vue.ts
│ ├── rules/
│ │ ├── enforce-canonical-classes.test.ts
│ │ ├── enforce-canonical-classes.ts
│ │ ├── enforce-consistent-class-order.test.ts
│ │ ├── enforce-consistent-class-order.ts
│ │ ├── enforce-consistent-important-position.test.ts
│ │ ├── enforce-consistent-important-position.ts
│ │ ├── enforce-consistent-line-wrapping.test.ts
│ │ ├── enforce-consistent-line-wrapping.ts
│ │ ├── enforce-consistent-variable-syntax.test.ts
│ │ ├── enforce-consistent-variable-syntax.ts
│ │ ├── enforce-consistent-variant-order.test.ts
│ │ ├── enforce-consistent-variant-order.ts
│ │ ├── enforce-logical-properties.test.ts
│ │ ├── enforce-logical-properties.ts
│ │ ├── enforce-shorthand-classes.test.ts
│ │ ├── enforce-shorthand-classes.ts
│ │ ├── no-conflicting-classes.test.ts
│ │ ├── no-conflicting-classes.ts
│ │ ├── no-deprecated-classes.test.ts
│ │ ├── no-deprecated-classes.ts
│ │ ├── no-duplicate-classes.test.ts
│ │ ├── no-duplicate-classes.ts
│ │ ├── no-restricted-classes.test.ts
│ │ ├── no-restricted-classes.ts
│ │ ├── no-unknown-classes.test.ts
│ │ ├── no-unknown-classes.ts
│ │ ├── no-unnecessary-whitespace.test.ts
│ │ └── no-unnecessary-whitespace.ts
│ ├── tailwindcss/
│ │ ├── canonical-classes.async.v4.ts
│ │ ├── canonical-classes.ts
│ │ ├── class-order.async.v3.ts
│ │ ├── class-order.async.v4.ts
│ │ ├── class-order.ts
│ │ ├── conflicting-classes.async.v4.ts
│ │ ├── conflicting-classes.ts
│ │ ├── context.async.v3.ts
│ │ ├── context.async.v4.ts
│ │ ├── custom-component-classes.async.v3.ts
│ │ ├── custom-component-classes.async.v4.ts
│ │ ├── custom-component-classes.ts
│ │ ├── dissect-classes.async.v3.ts
│ │ ├── dissect-classes.async.v4.ts
│ │ ├── dissect-classes.test.ts
│ │ ├── dissect-classes.ts
│ │ ├── prefix.async.v3.ts
│ │ ├── prefix.async.v4.ts
│ │ ├── prefix.ts
│ │ ├── tailwind.async.worker.v3.ts
│ │ ├── tailwind.async.worker.v4.ts
│ │ ├── unknown-classes.async.v3.ts
│ │ ├── unknown-classes.async.v4.ts
│ │ ├── unknown-classes.ts
│ │ ├── variant-order.async.v3.ts
│ │ ├── variant-order.async.v4.ts
│ │ └── variant-order.ts
│ ├── types/
│ │ ├── ast.ts
│ │ ├── async.ts
│ │ ├── estree.ts
│ │ └── rule.ts
│ └── utils/
│ ├── ast.ts
│ ├── class.ts
│ ├── context.ts
│ ├── lint.ts
│ ├── matchers.test.ts
│ ├── matchers.ts
│ ├── quotes.test.ts
│ ├── quotes.ts
│ ├── rule.ts
│ ├── selectors.ts
│ ├── utils.test.ts
│ ├── utils.ts
│ ├── valibot.ts
│ ├── version.ts
│ └── warn.ts
├── tests/
│ ├── e2e/
│ │ ├── commonjs/
│ │ │ ├── eslint.config.js
│ │ │ ├── package.json
│ │ │ ├── test.html
│ │ │ └── test.test.ts
│ │ ├── eslintrc/
│ │ │ ├── .eslintrc.json
│ │ │ ├── package.json
│ │ │ ├── test.html
│ │ │ └── test.test.ts
│ │ └── esm/
│ │ ├── eslint.config.js
│ │ ├── package.json
│ │ ├── test.html
│ │ └── test.test.ts
│ ├── unit/
│ │ ├── monorepo-cwd-resolution.test.ts
│ │ └── options.test.ts
│ └── utils/
│ ├── context.ts
│ ├── eslint.ts
│ ├── lint.ts
│ ├── prettier.ts
│ ├── setup.ts
│ ├── template.test.ts
│ ├── template.ts
│ ├── tmp.ts
│ ├── values.ts
│ └── version.ts
├── tsconfig.build.json
├── tsconfig.json
└── vite.config.ts
================================================
FILE CONTENTS
================================================
================================================
FILE: .cspell.json
================================================
{
"ignorePaths": [
"node_modules/**",
".vscode/**",
"lib/**"
],
"import": [
"@schoero/configs/cspell"
],
"words": [
"astro",
"Atrule",
"autofix",
"callees",
"classcat",
"classnames",
"clsx",
"cnbuilder",
"csstree",
"daisyui",
"dcnb",
"DCNB",
"Declarators",
"ecma",
"eslintrc",
"espree",
"estree",
"jiti",
"linebreak",
"linebreakstyle",
"longhands",
"memfs",
"objstr",
"OBJSTR",
"oxfmt",
"quasis",
"shadcn",
"synckit",
"Tmpl"
]
}
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug Report
description: Create a report
labels: ["bug"]
body:
- attributes:
description: A clear and concise description of what the bug is.
label: Description
placeholder: Describe the bug here...
id: description
type: textarea
validations:
required: true
- attributes:
description: Which framework/flavor are you using?
label: Flavor
options:
- JSX
- TSX
- Svelte
- Vue
- Astro
- Angular
- HTML
- CSS
- JavaScript
- TypeScript
id: flavor
type: dropdown
validations:
required: true
- attributes:
description: The code that triggers the bug.
label: Code Input
placeholder: |
// Code snippet
render: tsx
id: code-input
type: textarea
validations:
required: true
- attributes:
description: What you expected to happen.
label: Expected Behavior
placeholder: |
// Expected output
render: tsx
id: expected-behavior
type: textarea
validations:
required: true
- attributes:
description: What actually happened.
label: Actual Behavior
placeholder: |
// Actual output
render: tsx
id: actual-behavior
type: textarea
validations:
required: true
- attributes:
description: Link to a reproduction (e.g. StackBlitz, CodeSandbox, GitHub repo).
label: Reproduction URL
placeholder: https://...
id: reproduction-url
type: input
validations:
required: false
- attributes:
description: Please paste the output of `npx eslint ./path/to/file` here.
label: ESLint Log
placeholder: |
// ESLint log output
render: shell
id: eslint-log
type: textarea
validations:
required: true
- attributes:
description: The relevant parts of your ESLint config
label: ESLint Config
render: typescript
id: eslint-config
type: textarea
validations:
required: true
- attributes:
description: Please list the versions of the tools you are using.
label: Versions
value: |
- ESLint:
- Parser:
- Plugin:
- Node:
id: versions
type: textarea
validations:
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
================================================
FILE: .github/ISSUE_TEMPLATE/documentation.yml
================================================
name: Documentation
description: Improvements or additions to documentation
labels: ["documentation"]
body:
- attributes:
description: Describe the documentation issue or improvement.
label: Documentation Issue
id: documentation
type: textarea
validations:
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: Feature Request
description: Suggest an idea for this project
labels: ["feature request"]
body:
- attributes:
description: A clear and concise description of what the feature is.
label: Description
placeholder: Describe the feature here...
id: description
type: textarea
validations:
required: true
================================================
FILE: .github/ISSUE_TEMPLATE/question.yml
================================================
name: Question
description: Ask a question
labels: ["question"]
body:
- attributes:
description: What is your question?
label: Question
id: question
type: textarea
validations:
required: true
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
pull_request:
branches:
- main
push:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
cache: npm
node-version: 24
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint:ci
- name: Typecheck
run: npm run typecheck
- name: Spellcheck
run: npm run spellcheck:ci
test:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
cache: npm
node-version: ${{ matrix.node }}
- name: Install dependencies
run: npm ci
- name: Run build
run: npm run build:ci
- name: Install tailwindcss v3
run: npm run install:v3
- name: Run tests with tailwindcss v3
run: npm run test:v3
- name: Run e2e tests with tailwindcss v3
run: npm run test:e2e
- name: Install tailwindcss v4
run: npm run install:v4
- name: Run tests with tailwindcss v4
run: npm run test:v4
- name: Run e2e tests with tailwindcss v4
run: npm run test:e2e
strategy:
fail-fast: true
matrix:
node:
- 20
- 22
- 24
os:
- ubuntu-latest
- windows-latest
- macos-latest
================================================
FILE: .gitignore
================================================
node_modules
tmp
lib
local
.DS_Store
.env
================================================
FILE: .markdownlint.jsonc
================================================
{
"extends": "@schoero/configs/markdownlint"
}
================================================
FILE: .vscode/extensions.json
================================================
{
"recommendations": [
"dbaeumer.vscode-eslint",
"streetsidesoftware.code-spell-checker",
"davidanson.vscode-markdownlint"
]
}
================================================
FILE: .vscode/launch.json
================================================
{
"configurations": [
{
"args": [
"run",
"${relativeFileDirname}/${fileBasenameNoExtension}"
],
"autoAttachChildProcesses": true,
"console": "integratedTerminal",
"name": "debug current test file",
"program": "${workspaceRoot}/node_modules/vitest/vitest.mjs",
"request": "launch",
"skipFiles": ["<node_internals>/**", "**/node_modules/**"],
"smartStep": true,
"type": "node"
},
{
"args": [
"run",
"${relativeFileDirname}/${fileBasenameNoExtension}"
],
"autoAttachChildProcesses": true,
"console": "integratedTerminal",
"name": "debug current test file with node internals",
"program": "${workspaceRoot}/node_modules/vitest/vitest.mjs",
"request": "launch",
"skipFiles": [],
"smartStep": true,
"type": "node"
}
],
"version": "0.2.0"
}
================================================
FILE: .vscode/settings.json
================================================
{
// ESLint
"[javascript][typescript][json][json5][jsonc][yaml]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"eslint.nodePath": "node_modules/eslint",
"eslint.useFlatConfig": true,
"eslint.validate": ["javascript", "typescript", "json", "jsonc", "json5", "yaml"],
"eslint.rules.customizations": [
{ "rule": "better-tailwindcss/*", "severity": "off" }
],
"eslint.codeActionsOnSave.rules": [
"!better-tailwindcss/*",
"*"
],
// tailwindcss
"tailwindCSS.lint.cssConflict": "ignore",
"tailwindCSS.lint.suggestCanonicalClasses": "ignore",
"editor.formatOnSave": false,
// Prettier
"prettier.enable": false,
// File nesting
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.expand": false,
"explorer.fileNesting.patterns": {
"*.ts": "$(capture).ts,$(capture).test.ts,$(capture).cts,$(capture).mts,$(capture).test.snap,$(capture).test-d.ts,$(capture).v4.ts,$(capture).async.ts,$(capture).v3.ts,$(capture).async.v4.ts,$(capture).async.v3.ts,$(capture).async.worker.ts,$(capture).async.worker.v4.ts,$(capture).async.worker.v3.ts",
"*.v3.ts": "$(capture).v4.ts",
"*.js": "$(capture).test.js,$(capture).cjs,$(capture).mjs,$(capture).d.ts,$(capture).d.ts.map,$(capture).js.map"
},
// ES module import
"typescript.preferences.importModuleSpecifier": "non-relative",
"typescript.preferences.importModuleSpecifierEnding": "js",
"typescript.preferences.useAliasesForRenames": true,
"typescript.preferences.autoImportFileExcludePatterns": [
"@types/node/test.d.ts"
],
// Markdown
"[markdown]": {
"editor.defaultFormatter": "DavidAnson.vscode-markdownlint"
},
// VSCode
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.fixAll.markdownlint": "explicit",
"source.organizeImports": "never"
},
"editor.rulers": [
119
],
"typescript.preferences.autoImportSpecifierExcludeRegexes": ["lib"],
"search.exclude": {
"lib": true
},
"typescript.tsdk": "node_modules/typescript/lib"
}
================================================
FILE: CHANGELOG.md
================================================
# Changelog
## v4.5.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v4.4.1...v4.5.0)
### Features
- Add `ignore` option to `enforce-canonical-classes` ([#371](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/371))
- Add `tabWidth` option to `enforce-consistent-line-wrapping` ([#367](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/367))
### Fixes
- Add missing logical classes ([#368](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/368))
- Warning when tailwind css installation can't be found ([#373](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/373))
- Only sort variants that are safe ([#370](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/370))
## v4.4.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v4.4.0...v4.4.1)
### Fixes
- Remove auto detection of project root to set `cwd` ([#364](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/364))
If you're in a monorepo setup, you may need to [configure the `cwd`](https://github.com/schoero/eslint-plugin-better-tailwindcss?tab=readme-ov-file#monorepo-setup) manually.
## v4.4.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v4.3.2...v4.4.0)
### Features
- Project root based cwd in monorepos ([#345](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/345))
- Target specific arguments of callees ([#347](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/347))
- New Anonymous functions matcher ([#348](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/348))
- Add support for tag paths ([#354](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/354))
- Reintroduce line ending and indentation misconfiguration warnings ([#351](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/351))
- **worker:** Use SYNCKIT_TIMEOUT env var for timeout configuration ([#352](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/352))
- Match default exports ([#346](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/346))
- React twc preset ([#355](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/355))
- Lint Template literal based on prefixed comments ([#356](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/356))
- New rule `enforce-logical-properties` ([#358](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/358))
- New rule `enforce-consistent-variant-order` ([#359](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/359))
### Performance
- Cache regex, early return ([#336](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/336))
### Documentation
- Add example to restrict unnamed groups ([#357](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/357))
### ❤️ Contributors
- Mickaël Depardon ([@squelix](https://github.com/squelix))
- Mike Schutte ([@tmikeschu](https://github.com/tmikeschu))
- Stephen Zhou ([@hyoban](https://github.com/hyoban))
## v4.3.2
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v4.3.1...v4.3.2)
### Fixes
- **no-unnecessary-whitespace:** Preserve whitespaces in concatenated strings ([#339](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/339))
- **enforce-consistent-class-order:** Non localized alphabetical sorting order ([#340](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/340))
### Refactors
- Lint concatenated strings ([#338](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/338))
## v4.3.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v4.3.0...v4.3.1)
### Fixes
- Variable matchers leaking into function expressions ([#333](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/333))
### Documentation
- Add oxlint documentation ([#331](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/331))
## v4.3.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v4.2.0...v4.3.0)
### Features
- Support curried calls ([#325](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/325))
- Support callee paths ([#326](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/326))
### Refactors
- Simplify matcher config ([#324](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/324))
The matcher config has been simplified from a nested tuple structure to a simple array of objects. This makes it easier
to understand while also allowing better flexibility to support the new features. The old structure is still supported
for now, but will be removed in the next major version.
Check the updated [configuration documentation](https://github.com/schoero/eslint-plugin-better-tailwindcss/blob/main/docs/configuration/advanced.md#selectors) for more information.
## v4.2.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v4.1.1...v4.2.0)
### Features
- Add support for ESLint 10 ([#323](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/323))
### Performance
- Use shared worker to handle async calls ([#319](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/319))
### ❤️ Contributors
- Stephen Zhou ([@hyoban](https://github.com/hyoban))
- Bjorn Antonissen ([@Bjornftw](https://github.com/Bjornftw))
## v4.1.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v4.1.0...v4.1.1)
### Fixes
- Filter unrecommended rules ([#317](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/317))
## v4.1.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v4.0.2...v4.1.0)
### Features
- Experimental css linting ([#314](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/314))
- Add solid `classList` matcher ([#315](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/315))
### Fixes
- Type errors ([c3c9c40](https://github.com/schoero/eslint-plugin-better-tailwindcss/commit/c3c9c40))
- Prevent linting when no literals are found ([51333c6](https://github.com/schoero/eslint-plugin-better-tailwindcss/commit/51333c6))
- Add `exactOptionalPropertyTypes` to `tsconfig` ([#311](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/311))
### ❤️ Contributors
- Alexander Kachkaev ([@kachkaev](https://github.com/kachkaev))
## v4.0.2
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v4.0.1...v4.0.2)
### Fixes
- `enforce-canonical-classes`: removal of unrelated classes ([#309](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/309))
- `enforce-consistent-variable-syntax`: Support custom css functions ([#308](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/308))
- Config types ([#310](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/310))
## v4.0.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v4.0.0...v4.0.1)
### Fixes
- Disallow extra properties in rule options (valibot schemas) ([#295](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/295))
- Configuration warnings getting lost ([#297](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/297))
### ❤️ Contributors
- Andrew Kazakov ([@andreww2012](https://github.com/andreww2012))
## v4.0.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.8.0...v4.0.0)
This version includes a major rewrite of the internal architecture, improving performance and maintainability, resolving long-standing issues, and preparing the codebase for the future and for oxlint.
### New Features
- New rule: `enforce-canonical-classes` ([#232](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/232))
- New options for `enforce-consistent-class-order` to sort "component classes" and "unknown classes" ([#263](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/263))
- `detectComponentClasses`: `boolean`
- `componentClassOrder`: `"asc" | "desc" | "preserve"`
- `componentClassPosition`: `"start" | "end"`
- `unknownClassOrder`: `"asc" | "desc" | "preserve"`
- `unknownClassPosition`: `"start" | "end"`
- Added `strictness: "loose"` option to `enforce-consistent-line-wrapping` to improve interoperability with prettier ([#260](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/260))
- Better Performance
- Oxlint support
<br />
### ⚠️ Breaking Changes
First of all, the minimum required Node.js version is has changed to support v23.0.0, v22.12.0, v20.19.0 to support `require(esm)`
- This made it possible to remove the `CommonJS` build ([#264](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/264))
<br />
Some rules have been renamed to better reflect their intentions:
- Renamed rule `no-unregistered-classes` to `no-unknown-classes`
- Renamed rule `sort-classes` to `enforce-consistent-class-order`
- Renamed rule `multiline` to `enforce-consistent-line-wrapping`
The rule recommendations have been updated to enable new rules by default. Check the updated [rule recommendations](https://github.com/schoero/eslint-plugin-better-tailwindcss?tab=readme-ov-file#stylistic-rules) for more information.
<br />
For some rules, the options have been renamed or changed:
- Options for `better-tailwindcss/enforce-consistent-variable-syntax` have been renamed to `shorthand` and `variable`.
- The default for `enforce-consistent-important-position` is now always `recommended`.
- Renamed the `improved` sorting order for `enforce-consistent-class-order` to `strict` ([#245](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/245))
- `improved` is no longer the default option as most people expect the order to match the official order from tailwind.
- the `improved` order got renamed to `strict` to better describe its intentions.
- the logic of the `strict` order has changed:
- Classes that share the same base variants get grouped together.
- Classes with less variants come before classes with more variants.
- Classes with arbitrary variants come last.
- The `enforce-consistent-line-wrapping` rule now groups variants more strictly. Previously it only grouped classes by their first variant. Now all variants are ordered correctly.
<br />
The configs have been renamed and updated to match the recommended shape of ESLint.
- Renamed configs ([#244](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/244))
- The following configs are now exposed:
- `recommended`
- `recommended-warn`
- `recommended-error`
- `stylistic`
- `stylistic-warn`
- `stylistic-error`
- `correctness`
- `correctness-warn`
- `correctness-error`
- `legacy-recommended`
- `legacy-recommended-warn`
- `legacy-recommended-error`
- `legacy-stylistic`
- `legacy-stylistic-warn`
- `legacy-stylistic-error`
- `legacy-correctness`
- `legacy-correctness-warn`
- `legacy-correctness-error`
- Please check the updated [Parser Documentation](https://github.com/schoero/eslint-plugin-better-tailwindcss?tab=readme-ov-file#quick-start) to see the recommended way to set up the plugin with your parser.
<br />
Other changes:
- Function `getDefaultIgnoredUnregisteredClasses()` has been removed.
- Removed rule regex matchers
- Preserve normal quotes whenever possible ([#246](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/246))
Here is the full list of changes in this version:
### Features
- New rule: `enforce-canonical-classes` ([#232](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/232))
- Oxlint support ([#284](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/284)
- Add `strictness: "loose"` option to `enforce-consistent-line-wrapping` ([#260](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/260))
- Add settings option to configure `messageStyle` ([#276](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/276))
- **angular:** Support bound attribute classes ([#277](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/277))
- **svelte:** Support class directive ([#278](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/278))
### Fixes
- Don't match attribute values for bound attribute names ([#291](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/291))
- Correctly override shared settings with rule options ([#289](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/289))
- Invalid variant grouping order ([#282](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/282))
- Ignore variants in custom component classes ([#258](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/258))
- Angular line wrapping ([#259](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/259))
### Refactors
- Deprecate `/api/` path for imports ([#281](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/281))
- Update rule recommendations ([#280](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/280))
### Documentation
- Add `detectComponentClasses` to settings ([388103e](https://github.com/schoero/eslint-plugin-better-tailwindcss/commit/388103e))
- Add attribute matcher example ([#272](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/272))
- Improve configuration guide ([bd873ea](https://github.com/schoero/eslint-plugin-better-tailwindcss/commit/bd873ea))
#### ⚠️ Breaking Changes
- ⚠️ Ignore indexed access keys ([#292](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/292))
- ⚠️ Update rule recommendations ([#280](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/280))
- ⚠️ Remove separate `CommonJS` build ([#264](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/264))
Minimum Node.js version to v23.0.0, v22.12.0, v20.19.0 to support `require(esm)`
- ⚠️ Preserve normal quotes whenever possible ([#246](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/246))
- ⚠️ Renamed the `improved` sorting order to `strict` ([#245](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/245))
- ⚠️ Rename configs ([#244](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/244))
- ⚠️ Renamed rule `no-unregistered-classes` to `no-unknown-classes`
- ⚠️ Renamed rule `sort-classes` to `enforce-consistent-class-order`
- ⚠️ Renamed rule `multiline` to `enforce-consistent-line-wrapping`
- ⚠️ Options for `better-tailwindcss/enforce-consistent-variable-syntax` have been renamed to `shorthand` and `variable`.
- ⚠️ Function `getDefaultIgnoredUnregisteredClasses()` has been removed.
- ⚠️ The default for `enforce-consistent-important-position` is now always `recommended`. If you are on tailwindcss v3 need to manually set it to `legacy` to keep it working for tailwindcss v3.
- ⚠️ Removed rule regex matchers
### ❤️ Contributors
- V-iktor ([@V-iktor](https://github.com/V-iktor))
## v3.8.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.7.11...v3.8.0)
### Features
- **no-unregistered-classes:** Support `@import layer(components)` ([#257](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/257))
### Fixes
- Wrong documentation url ([#255](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/255))
- Ignore variants in custom component classes ([#258](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/258))
- Angular line wrapping ([#259](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/259))
### ❤️ Contributors
- Carlos Marques <karkosyk@gmail.com>
## v3.7.11
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v4.0.0-beta.3...v3.7.11)
### Fixes
- Convert missing flex shrink and grow utilities ([#236](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/236))
- Ignore literals in binary expressions ([#238](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/238))
- Allow interpolations in normal svelte string literals ([#239](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/239))
- Only show config warning when config is set and not found ([#240](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/240))
### ❤️ Contributors
- Akameco ([@akameco](https://github.com/akameco))
## v3.7.10
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.7.9...v3.7.10)
### Fixes
- `enforce-shorthand-classes` to include horizontal and vertical cases for `rounded` classes ([#231](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/231))
### Chore
- Correct recommended rules to match implementation ([#229](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/229))
### ❤️ Contributors
- Andrew Kodkod ([@akodkod](https://github.com/akodkod))
- 2754 ([@2754github](https://github.com/2754github))
## v3.7.9
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.7.8...v3.7.9)
### Fixes
- Don't match index accessed object keys ([#227](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/227))
## v3.7.8
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.7.7...v3.7.8)
### Fixes
- Improved angular support ([#182](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/182))
- Fixes object key detection for intersecting classes
- Adds support for `pathPattern` in angular
## v3.7.7
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.7.6...v3.7.7)
### Fixes
- Compound variants with slots class string not being detected ([#219](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/219))
### ❤️ Contributors
- tim-spitzer-syzygy ([@tim-spitzer-syzygy](https://github.com/tim-spitzer-syzygy))
## v3.7.6
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.7.5...v3.7.6)
### Fixes
- Check for tailwindcss before running rules ([#217](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/217))
- Angular: Prevent crash when objectContent is undefined in createLiteralByLiteralMapKey ([#215](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/215))
### Tests
- Add no-unregistered-classes test for DaisyUI classes ([#186](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/186))
### ❤️ Contributors
- Paul Parker ([@pauldesmondparker](https://github.com/pauldesmondparker))
- Yossi Yedid ([@yossiyedid](https://github.com/yossiyedid))
## v3.7.5
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.7.4...v3.7.5)
### Fixes
- Matching object values with immediate indexed access ([#212](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/212))
## v3.7.4
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.7.3...v3.7.4)
### Fixes
- Error in no-conflicting-classes when used in tailwindcss 3 ([#205](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/205))
- Invalid config warning when config was actually found ([#206](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/206))
- Differentiate shorthands for the same classes with different variants ([#207](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/207))
## v3.7.3
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.7.2...v3.7.3)
### Fixes
- Invalid fix for multiple vars in `enforce-consistent-variable-syntax` ([#200](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/200))
## v3.7.2
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.7.1...v3.7.2)
### Fixes
- Error when no tsconfig is available ([#195](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/195))
### Refactors
- Refine cache ([#196](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/196))
## v3.7.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.7.0...v3.7.1)
### Fixes
- `no-unnecessary-whitespace` false positive on empty string ([#191](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/191))
- Don't convert variable definitions ([#192](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/192))
### Chore
- Update dependencies ([#193](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/193))
## v3.7.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.6.3...v3.7.0)
### Features
- Support tsconfig paths ([#185](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/185))
### Refactors
- Exact unnecessary whitespace fixes ([#184](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/184))
## v3.6.3
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.6.2...v3.6.3)
### Fixes
- Error position ([7b699ee](https://github.com/schoero/eslint-plugin-better-tailwindcss/commit/7b699ee))
### Refactors
- Add missing deprecations ([#181](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/181))
- Variable syntax tailwindcss3 shorthand ([#183](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/183))
## v3.6.2
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.6.1...v3.6.2)
### Fixes
- Fixes crash when importing css files via tsconfig path alias and [`detectComponentClasses`](https://github.com/schoero/eslint-plugin-better-tailwindcss/blob/main/docs/rules/no-unregistered-classes.md#detectcomponentclasses) enabled ([#178](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/178))
- Fixes component classes not getting updated when inside an imported file ([#178](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/178))
- Disallow extra properties in rule options ([#180](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/180))
### ❤️ Contributors
- Andrew Kazakov ([@andreww2012](https://github.com/andreww2012))
## v3.6.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.6.0...v3.6.1)
### Fixes
- Recursively reading imports ([#175](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/175))
## v3.6.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.5.2...v3.6.0)
### Features
- New rule `enforce-consistent-important-position` ([#167](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/167))
- New rule `no-deprecated-classes` ([#169](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/169))
### Fixes
- Support starting important in `enforce-shorthand-classes` ([#164](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/164))
- Error position ([a55a6cc](https://github.com/schoero/eslint-plugin-better-tailwindcss/commit/a55a6cc))
## v3.5.2
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.5.1...v3.5.2)
### Fixes
- Tailwind 3 shorthand classes with important modifier ([#162](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/162))
## v3.5.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.5.0...v3.5.1)
### Fixes
- False reports of shorthand classes ([c5f14ab](https://github.com/schoero/eslint-plugin-better-tailwindcss/commit/c5f14ab))
## v3.5.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.4.4...v3.5.0)
### Features
- New Rule: Enforce shorthand classes ([#153](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/153))
### Fixes
- Bump tailwindcss peer dependency ([#157](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/157))
- Regex deprecation warning ([#161](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/161))
## v3.4.4
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.4.3...v3.4.4)
### Fixes
- Altering variant order in tailwindcss cache ([#151](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/151))
### Documentation
- Add example for arbitrary values ([ef6faa2](https://github.com/schoero/eslint-plugin-better-tailwindcss/commit/ef6faa2))
## v3.4.3
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.4.2...v3.4.3)
### Fixes
- Prevent removal of whitespace between template literals ([#147](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/147))
- Extract class variants via tailwind ([#146](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/146))
## v3.4.2
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.4.1...v3.4.2)
### Fixes
- Template literals resulting in `undefined` path in getESObjectPath causing false positives ([#142](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/142))
### ❤️ Contributors
- Long Zheng ([@longzheng](https://github.com/longzheng))
## v3.4.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.4.0...v3.4.1)
### Fixes
- Detect conflicts with multiple properties ([#137](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/137))
## v3.4.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.3.1...v3.4.0)
### Features
- Add customizable autofix option to `no-restricted-classes` ([#133](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/133))
### Refactors
- Rename rules for better consistency ([#134](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/134))
- better-tailwindcss/multiline -> better-tailwindcss/enforce-consistent-line-wrapping
- better-tailwindcss/sort-classes -> better-tailwindcss/enforce-consistent-class-order
The old names will still work for now, but will be removed in the next major version.
## v3.3.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.3.0...v3.3.1)
### Fixes
- Prevent variable matchers from crossing arrow function boundaries ([#131](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/131))
- Sorting order with unregistered class with variant ([#132](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/132))
## v3.3.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.2.1...v3.3.0)
### Features
- No-restricted-classes rule to support custom error messages ([#129](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/129))
### Fixes
- Node version range ([b50df13](https://github.com/schoero/eslint-plugin-better-tailwindcss/commit/b50df13))
## v3.2.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.2.0...v3.2.1)
### Fixes
- Don't report inside member expressions ([#120](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/120))
## v3.2.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.1.0...v3.2.0)
### Features
- Auto detect custom component layer classes ([#111](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/111))
- Ignore prefix in groups ([#110](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/110))
- Support prefixed groups and tags ([#115](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/115))
### Fixes
- Add additional tailwind variants matchers ([#116](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/116))
## v3.1.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v3.0.0...v3.1.0)
### Features
- Add support for astro syntactic sugar ([#103](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/103))
- New rule `enforce consistent variable syntax` ([#101](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/101))
### Fixes
- Remove `name` property ([#105](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/105))
## v3.0.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v2.1.2...v3.0.0)
This version adds 3 new correctness rules to the plugin. To better reflect the new scope of the plugin it was renamed from `eslint-plugin-readable-tailwind` to `eslint-plugin-better-tailwindcss`. <https://github.com/schoero/eslint-plugin-readable-tailwind/issues/86#issuecomment-2855845766>
The predefined configs also have been renamed to better reflect their scope.
### Features
- [no-unregistered-classes](https://github.com/schoero/eslint-plugin-better-tailwindcss/blob/main/docs/rules/no-unregistered-classes.md): Report classes not registered with tailwindcss. ([#89](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/89))
- [no-conflicting-classes](https://github.com/schoero/eslint-plugin-better-tailwindcss/blob/main/docs/rules/no-conflicting-classes.md): Report classes that produce conflicting styles. ([#90](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/90))
- [no-restricted-classes](https://github.com/schoero/eslint-plugin-better-tailwindcss/blob/main/docs/rules/no-restricted-classes.md): Disallow restricted classes. ([#92](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/92))
#### ⚠️ Breaking changes
- Plugin renamed to `eslint-plugin-better-tailwindcss`
- Deprecate [Regex matchers](https://github.com/schoero/eslint-plugin-readable-tailwind/blob/v2.1.2/docs/concepts/concepts.md#regular-expressions) to simplify the configuration. ([#98](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/98))
[Regex matchers](https://github.com/schoero/eslint-plugin-readable-tailwind/blob/v2.1.2/docs/concepts/concepts.md#regular-expressions) were an early attempt to make the plugin more flexible. However, they were quickly replaced with [Matchers](https://github.com/schoero/eslint-plugin-readable-tailwind/blob/v2.1.2/docs/concepts/concepts.md#matchers) which work on the Abstract Syntax Tree and are far more powerful. Support for [Regex matchers](https://github.com/schoero/eslint-plugin-readable-tailwind/blob/v2.1.2/docs/concepts/concepts.md#regular-expressions) will be removed in the next major version.
- `warning` and `error` configs have been removed. Use `recommended-warn` or `recommended-error` instead. ([#99](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/99))
### Migration
1. Replace `eslint-plugin-readable-tailwind` with `eslint-plugin-better-tailwindcss`:
```sh
npm uninstall eslint-plugin-readable-tailwind
```
```sh
npm i -D eslint-plugin-better-tailwindcss
```
1. Update the imports in your config:
```diff
- import eslintPluginReadableTailwind from "eslint-plugin-readable-tailwind";
+ import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
```
1. Migrate to the new configs
```diff
rules: {
// enable all recommended rules to warn
- ...eslintPluginReadableTailwind.configs.warning.rules,
+ ...eslintPluginBetterTailwindcss.configs["recommended-warn"].rules,
// enable all recommended rules to error
- ...eslintPluginReadableTailwind.configs.error.rules,
+ ...eslintPluginBetterTailwindcss.configs["recommended-error"].rules,
// or configure rules individually
- "readable-tailwind/multiline": ["warn", { printWidth: 100 }]
+ "better-tailwindcss/multiline": ["warn", { printWidth: 100 }]
}
```
## v2.1.2
[compare changes](https://github.com/schoero/eslint-plugin-readable-tailwind/compare/v2.1.1...v2.1.2)
### Fixes
- Multiline quotes ([#96](https://github.com/schoero/eslint-plugin-readable-tailwind/pull/96))
### Refactors
- Report error for each duplicate class instead of the whole class string ([#91](https://github.com/schoero/eslint-plugin-readable-tailwind/pull/91))
## v2.1.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v2.1.0...v2.1.1)
### Fixes
- Unnecessarily escaped quotes in autofixed classes ([#88](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/88))
## v2.1.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v2.0.1...v2.1.0)
### Features
- Experimental angular support. ([#85](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/85))
### Fixes
- Keep carriage return in es literals when used with vue parser ([#84](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/84))
## v2.0.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v2.0.0...v2.0.1)
### Fixes
- Keep original newline characters ([a564783](https://github.com/schoero/eslint-plugin-better-tailwindcss/commit/a564783))
### Refactors
- Display warning if plugin is misconfigured ([7c532cd](https://github.com/schoero/eslint-plugin-better-tailwindcss/commit/7c532cd))
### Documentation
- Update quick start guide ([e570981](https://github.com/schoero/eslint-plugin-better-tailwindcss/commit/e570981))
## v2.0.0
Adds tailwindcss v4 support while keeping support for tailwindcss v3. ([#78](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/78))
This version contains breaking changes. Most notably support for Node.js < 20 had to be dropped. The other breaking changes are mostly just changes of the default config, that may cause linting errors.
### Migration
- If you use tailwindcss v4, you should specify the [`entryPoint`](https://github.com/schoero/eslint-plugin-better-tailwindcss/blob/main/docs/rules/sort-classes.md#entrypoint) of the css based tailwind configuration file for the [sort-classes](https://github.com/schoero/eslint-plugin-better-tailwindcss/blob/main/docs/rules/sort-classes.md) rule or in the [settings](https://github.com/schoero/eslint-plugin-better-tailwindcss/blob/main/docs/settings/settings.md#entrypoint).
- If you have customized the `classAttributes` option for any of the rules or via the [settings](https://github.com/schoero/eslint-plugin-better-tailwindcss/blob/main/docs/settings/settings.md#attributes), rename the option to [`attributes`](https://github.com/schoero/eslint-plugin-better-tailwindcss/blob/main/docs/settings/settings.md#attributes)
- If you have customized `attributes`, `callees`, `variables`, or `tags`, escape any reserved characters for regular expressions in the name as the name is now evaluated as a regular expression.
For example:
```diff
{
variables: [
- "$MyVariable"
+ "\\$MyVariable"
]
}
```
### Changes
- Reload tailwind config automatically if a change is detected.
- Options now correctly override settings ([#66](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/66))
#### ⚠️ Breaking Changes
- ⚠️ Drop support for Node.js < 20 due to incompatibility of worker threads.
- ⚠️ Add support for tailwindcss v4 ([#25](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/25))
- The official class ordering seems to have changed slightly.
- The `improved` sorting order will no longer sort variants alphabetically, instead it just makes sure that identical variants are grouped together.
- ⚠️ Regex names ([#63](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/63))
- ["Names"](https://github.com/schoero/eslint-plugin-better-tailwindcss/blob/main/docs/configuration/advanced.md#name-based-matching) can now be regular expressions. This is a breaking change, if you have names configured that contain reserved characters in regular expressions like `$`.
- ⚠️ Enable `no-duplicate-classes` by default ([#67](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/67))
- ⚠️ Change default `multiline` grouping to `newLine` ([#68](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/68))
- ⚠️ Rename `classAttributes` to `attributes` ([#69](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/69))
## v1.9.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v2.0.0-beta.2...v1.9.1)
### Fixes
- Lint `className` in render functions inside object ([#75](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/75))
## v1.9.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.8.2...v1.9.0)
### Features
- Template literal tags ([#65](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/65))
## v1.8.2
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.8.1...v1.8.2)
### Fixes
- Fixing loop when lines wrap on two lines immediately but was theoretically short enough to not wrap ([#61](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/61))
## v1.8.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.8.0...v1.8.1)
### Refactors
- Improve display of linting errors ([#60](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/60))
## v1.8.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.7.0...v1.8.0)
### Features
- Add support to globally configure shared options across all rules via the settings object ([#56](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/56))
## v1.7.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.6.1...v1.7.0)
### Features
- New option `preferSingleLine` ([#54](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/54))
## v1.6.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.6.0...v1.6.1)
### Fixes
- Group type `never` not working with expressions ([#53](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/53))
## v1.6.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.5.3...v1.6.0)
### Features
- New rule `no-duplicate-classes` ([#49](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/49))
This rule will be enabled by default in v2.0.0. If you want to enable it now, please refer to the [rule documentation](https://github.com/schoero/eslint-plugin-better-tailwindcss/blob/main/docs/rules/no-duplicate-classes.md).
You can suggest additional rules in the [discussions](https://github.com/schoero/eslint-plugin-better-tailwindcss/discussions/categories/new-rules-or-options?discussions_q=category%3A%22New+rules+or+options%22+).
### Refactors
- Revert back to vitest ([38f6eab](https://github.com/schoero/eslint-plugin-better-tailwindcss/commit/38f6eab))
## v1.5.3
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.5.2...v1.5.3)
### Refactors
- Insertion of unnecessary escape characters ([#47](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/47))
## v1.5.2
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.5.1...v1.5.2)
### Fixes
- Remove unnecessary plugin import in shared config ([#44](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/44))
- Support svelte shorthand syntax ([#43](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/43))
## v1.5.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.5.0...v1.5.1)
### Fixes
- Commonjs build ([#39](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/39))
## v1.5.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.4.0...v1.5.0)
### Features
- Vue bound classes ([#31](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/31))
### Fixes
- Change quotes in multiline arrays ([#32](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/32))
- Escape nested quotes ([#33](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/33))
- Allow call expressions as object values ([#34](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/34))
- Attributes are no longer case sensitive ([#35](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/35))
- Warn in html matchers ([#36](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/36))
- Don't treat escape characters as whitespace ([6aa74f8](https://github.com/schoero/eslint-plugin-better-tailwindcss/commit/6aa74f8))
### Refactors
- Simplify build system ([#26](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/26), [#29](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/29))
## v1.4.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.3.2...v1.4.0)
### Features
- Matchers ([#28](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/28))
## v1.3.2
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.3.1...v1.3.2)
### Fixes
- Remove unnecessary newline after single sticky class ([#23](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/23))
- Prevent inserting new line if the first class is already too long ([#24](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/24))
### Tests
- Simplify testing ([#22](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/22))
## v1.3.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.3.0...v1.3.1)
### Fixes
- Accept tabs ([#21](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/21))
## v1.3.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.2.5...v1.3.0)
### Features
- Add eslint 9 support ([#19](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/19))
### Chore
- Update dependencies ([be69b11](https://github.com/schoero/eslint-plugin-better-tailwindcss/commit/be69b11))
## v1.2.5
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.2.4...v1.2.5)
### Performance
- Cache tailwind config and context ([#16](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/16))
### Fixes
- Resolving tailwind config ([#15](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/15))
## v1.2.4
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.2.3...v1.2.4)
### Fixes
- Sticky expressions ([#13](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/13))
## v1.2.3
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.2.2...v1.2.3)
### Fixes
- Remove unnecessary trailing spaces in multiline strings ([#12](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/12))
- False positives when using `crlf` ([#11](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/11))
## v1.2.2
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.2.1...v1.2.2)
### Fixes
- False positives of unnecessary whitespace around template literal elements ([#9](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/9))
## v1.2.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.2.0...v1.2.1)
### Fixes
- Don't wrap empty attributes ([#8](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/8))
## v1.2.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.1.1...v1.2.0)
### Features
- Lint variables ([#7](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/7))
### Fixes
- Apply nested regex only to container groups ([#6](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/6))
## v1.1.1
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.1.0...v1.1.1)
### Fixes
- Invalid collapsing with template literal expressions ([adfafbf](https://github.com/schoero/eslint-plugin-better-tailwindcss/commit/adfafbf))
## v1.1.0
[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v1.0.0...v1.1.0)
### Features
- Collapse unnecessary newlines ([#4](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/4))
- Regex as callees ([#3](https://github.com/schoero/eslint-plugin-better-tailwindcss/pull/3))
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to eslint-plugin-better-tailwindcss
First off, thank you for considering contributing to **eslint-plugin-better-tailwindcss**! Your help is essential to making this project better. This document provides guidelines and instructions for contributing.
<br />
## How Can I Contribute?
### Reporting Bugs
If you find a bug, please create an issue on [GitHub Issues](https://github.com/schoero/eslint-plugin-better-tailwindcss/issues) with:
- **Clear title and description** of the issue
- **Steps to reproduce** the problem
- **Expected behavior** vs **actual behavior**
- **Your environment**: Node.js version, npm version, ESLint version, Tailwind CSS version
- **Code examples** or screenshots if applicable
<br />
### Feature Requests
Feature requests are tracked as GitHub issues. When creating a feature request, include:
- **Clear title and description**
- **Use case and benefits** of the proposed feature
- **Possible implementation** approach (if you have ideas)
- **Examples** of how the feature would be used
<br />
### Pull Requests
Pull requests are appreciated! Here's the process:
#### Prerequisites
- Node.js `^20.11.0` or `>=21.2.0`
- npm `>=8.0.0`
<br />
##### Installation
```bash
git clone https://github.com/schoero/eslint-plugin-better-tailwindcss.git
cd eslint-plugin-better-tailwindcss
npm install
```
<br />
##### Fork the repository and create your branch from `main`
```bash
git checkout -b feat/your-feature-name
```
<br />
##### Set up the development environment
```bash
npm install
```
<br />
##### Make your changes
- Follow the project's code style
- Add tests for new features or bug fixes
- Update documentation if needed
- Keep commits atomic and write clear commit messages
<br />
##### Run the test suite
```bash
npm test
```
If you use vscode, you can open a test file and press <kbd>F5</kbd> to run all tests in the file in debug mode.
The plugin supports both Tailwind CSS v3 and v4. Use the following commands to test against both versions:
```bash
npm run install:v3
npm run test:v3
npm run install:v4
npm run test:v4
```
<br />
##### Fix linting and formatting issues
```bash
npm run lint:fix
```
<br />
##### Check type validity
```bash
npm run typecheck
```
<br />
##### Build the project
```bash
npm run build
```
<br />
##### Commit and push your changes
```bash
git add .
git commit -m "feat: add new feature" # or "fix: fix issue", "docs: update docs", etc.
git push origin feat/your-feature-name
```
Use conventional commit messages:
- `feat:` for new features
- `fix:` for bug fixes
- `docs:` for documentation changes
- `test:` for test changes
- `refactor:` for code refactoring
- `chore:` for maintenance tasks
- `ci:` for CI/CD changes
Example: `feat: add new rule for enforcing class ordering`
<br />
##### Create a pull request on GitHub with
- Clear title and description
- Reference to related issues
- Summary of changes
<br />
### Test Template
The project includes a sophisticated testing abstraction that automatically tests rules across multiple parsers (Angular, Astro, HTML, JSX, Svelte, and Vue). Use the `lint()` helper from `tests/utils/lint.ts`:
```ts
import { describe, it } from "vitest";
import { yourRule } from "better-tailwindcss:rules/your-rule.js";
import { lint } from "better-tailwindcss:tests/utils/lint.js";
describe(yourRule.name, () => {
it("should describe what it does", () => {
lint(yourRule, {
invalid: [
{
angular: '<img class="bad classes" />',
angularOutput: '<img class="fixed classes" />',
html: '<img class="bad classes" />',
htmlOutput: '<img class="fixed classes" />',
jsx: '() => <img class="bad classes" />',
jsxOutput: '() => <img class="fixed classes" />',
svelte: '<img class="bad classes" />',
svelteOutput: '<img class="fixed classes" />',
vue: '<template><img class="bad classes" /></template>',
vueOutput: '<template><img class="fixed classes" /></template>',
errors: 1,
options: [{ /* rule options */ }]
}
],
valid: [
{
angular: '<img class="valid classes" />',
html: '<img class="valid classes" />',
jsx: '() => <img class="valid classes" />',
svelte: '<img class="valid classes" />',
vue: '<template><img class="valid classes" /></template>',
options: [{ /* rule options */ }]
}
]
});
});
});
```
<br />
## Recognition
Contributors will be recognized in:
- Pull request acknowledgments
- CHANGELOG entries
- GitHub contributors list
Thank you for your contributions!
================================================
FILE: FUNDING.yml
================================================
github:
- schoero
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2023 Roger Schönbächler
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<div align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./assets/eslint-plugin-better-tailwindcss-logo-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="./assets/eslint-plugin-better-tailwindcss-logo-light.svg">
<img alt="eslint-plugin-better-tailwindcss logo" src="./assets/eslint-plugin-better-tailwindcss-logo.svg">
</picture>
</div>
<h1 align="center">eslint-plugin-better-tailwindcss</h1>
<br/>
<br/>
<div align="center">
<a alt="GitHub license" href="https://github.com/schoero/eslint-plugin-better-tailwindcss/blob/main/LICENSE"><picture><source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/github/license/schoero/eslint-plugin-better-tailwindcss?style=flat-square&labelColor=32363B&color=ffffff" /><source media="(prefers-color-scheme: light)" srcset="https://img.shields.io/github/license/schoero/eslint-plugin-better-tailwindcss?style=flat-square&labelColor=EBEEF2&color=000000" /><img alt="eslint-plugin-better-tailwindcss logo" src="https://img.shields.io/github/license/schoero/eslint-plugin-better-tailwindcss?style=flat-square&labelColor=EBEEF2&color=000000" /></picture></a>
<a alt="npm version" href="https://www.npmjs.com/package/eslint-plugin-better-tailwindcss?activeTab=versions"><picture><source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/npm/v/eslint-plugin-better-tailwindcss?style=flat-square&labelColor=32363B&color=ffffff" /><source media="(prefers-color-scheme: light)" srcset="https://img.shields.io/npm/v/eslint-plugin-better-tailwindcss?style=flat-square&labelColor=EBEEF2&color=000000" /><img alt="eslint-plugin-better-tailwindcss logo" src="https://img.shields.io/npm/v/eslint-plugin-better-tailwindcss?style=flat-square&labelColor=EBEEF2&color=000000" /></picture></a>
<a alt="GitHub issues" href="https://github.com/schoero/eslint-plugin-better-tailwindcss/issues"><picture><source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/github/issues/schoero/eslint-plugin-better-tailwindcss?style=flat-square&labelColor=32363B&color=ffffff" /><source media="(prefers-color-scheme: light)" srcset="https://img.shields.io/github/issues/schoero/eslint-plugin-better-tailwindcss?style=flat-square&labelColor=EBEEF2&color=000000" /><img alt="eslint-plugin-better-tailwindcss logo" src="https://img.shields.io/github/issues/schoero/eslint-plugin-better-tailwindcss?style=flat-square&labelColor=EBEEF2&color=000000" /></picture></a>
<a alt="npm total downloads" href="https://www.npmjs.com/package/eslint-plugin-better-tailwindcss?activeTab=readme"><picture><source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/npm/dt/eslint-plugin-better-tailwindcss?style=flat-square&labelColor=32363B&color=ffffff" /><source media="(prefers-color-scheme: light)" srcset="https://img.shields.io/npm/dt/eslint-plugin-better-tailwindcss?style=flat-square&labelColor=EBEEF2&color=000000" /><img alt="eslint-plugin-better-tailwindcss logo" src="https://img.shields.io/npm/dt/eslint-plugin-better-tailwindcss?style=flat-square&labelColor=EBEEF2&color=000000" /></picture></a>
<a alt="GitHub repo stars" href="https://github.com/schoero/eslint-plugin-better-tailwindcss/stargazers"><picture><source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/github/stars/schoero/eslint-plugin-better-tailwindcss?style=flat-square&labelColor=32363B&color=ffffff" /><source media="(prefers-color-scheme: light)" srcset="https://img.shields.io/github/stars/schoero/eslint-plugin-better-tailwindcss?style=flat-square&labelColor=EBEEF2&color=000000" /><img alt="eslint-plugin-better-tailwindcss logo" src="https://img.shields.io/github/stars/schoero/eslint-plugin-better-tailwindcss?style=flat-square&labelColor=EBEEF2&color=000000"/></picture></a>
<a alt="GitHub sponsors" href="https://github.com/sponsors/schoero"><picture><source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/github/sponsors/schoero?style=flat-square&labelColor=32363B&color=ffffff" /><source media="(prefers-color-scheme: light)" srcset="hhttps://img.shields.io/github/sponsors/schoero?style=flat-square&labelColor=EBEEF2&color=000000" /><img alt="eslint-plugin-better-tailwindcss logo" src="https://img.shields.io/github/sponsors/schoero?style=flat-square&labelColor=EBEEF2&color=000000" /></picture></a>
</div>
<br/>
<br/>
ESLint/Oxlint plugin with formatting and linting rules to help you write cleaner, more maintainable Tailwind CSS.
The formatting rules focus on improving readability by automatically breaking up long Tailwind class strings into multiple lines and sorting/grouping them in a logical order. The linting rules enforce best practices and catch potential issues, ensuring that you're writing valid Tailwind CSS.
This plugin supports a wide range of projects, including React, Solid.js, Qwik, Svelte, Vue, Astro, Angular, HTML or plain JavaScript or TypeScript.
<br/>
<br/>
<div align="center">
<img alt="eslint-plugin-better-tailwindcss example" width="640px" src="./assets/eslint-plugin-better-tailwindcss-demo.png">
</div>
<br/>
<br/>
<div align="center">
<a href="https://github.com/sponsors/schoero">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./assets/sponsor-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="./assets/sponsor-light.svg">
<img alt="eslint-plugin-better-tailwindcss logo" src="./assets/sponsor-light.svg">
</picture>
</a>
<br/>
<br/>
[Buy me a coffee](https://buymeacoffee.com/schoero) | [GitHub Sponsors](https://github.com/sponsors/schoero)
Help support this project.
If you or your company benefit from this project, please consider becoming a sponsor or making a one-time donation.
Your contribution will help me to maintain and develop the project.
</div>
<br/>
<br/>
## Installation
```sh
npm i -D eslint-plugin-better-tailwindcss
```
<br/>
## Quick start
Depending on the flavor you are using, you need to install and configure the corresponding parser:
- React: [.jsx](docs/parsers/jsx.md) · [.tsx](docs/parsers/tsx.md)
- SolidJS: [.jsx](docs/parsers/jsx.md) · [.tsx](docs/parsers/tsx.md)
- Qwik: [.jsx](docs/parsers/jsx.md) · [.tsx](docs/parsers/tsx.md)
- Svelte: [.svelte](docs/parsers/svelte.md)
- Vue: [.vue](docs/parsers/vue.md)
- Astro: [.astro](docs/parsers/astro.md)
- Angular: [.html, .ts](docs/parsers/angular.md)
- HTML: [.html](docs/parsers/html.md)
- CSS: [.css](docs/parsers/css.md)
- JavaScript: [.js](docs/parsers/javascript.md)
- TypeScript: [.ts](docs/parsers/typescript.md)
<br/>
<br/>
### Rules
The rules are categorized into two types: `stylistic` and `correctness`.
#### Configs
The plugin offers three recommended configurations to help you get started quickly:
- `stylistic`: Enforces stylistic rules for tailwind classes.
- `correctness`: Enforces correctness rules for tailwind classes.
- `recommended`: Enforces both stylistic and correctness rules.
By default:
- `stylistic` rules are reported as warnings
- `correctness` rules are reported as errors
You can change the severity by adding a suffix to the config name:
- Use `-error` to report all rules as errors
- Use `-warn` to report all rules as warnings
For example, `recommended-warn` will report every rule as a warning and `stylistic-error` will report the formatting rules as errors.
If you still use the old .eslintrc configuration format, you can prefix the config names with `legacy-`.
For example, `legacy-recommended` or `legacy-correctness-warn`.
The table below lists all available rules, the Tailwind CSS versions they support, and whether they are enabled by default in each recommended configuration:
<br/>
<br/>
#### Stylistic rules
| Name | Description | `tw3` | `tw4` | `recommended` | autofix |
| :--- | :--- | :---: | :---: | :---: | :---: |
| [enforce-consistent-line-wrapping](docs/rules/enforce-consistent-line-wrapping.md) | Enforce consistent line wrapping for tailwind classes. | ✔ | ✔ | ✔ | ✔ |
| [enforce-consistent-class-order](docs/rules/enforce-consistent-class-order.md) | Enforce a consistent order for tailwind classes. | ✔ | ✔ | ✔ | ✔ |
| [enforce-consistent-variant-order](docs/rules/enforce-consistent-variant-order.md) | Enforce a consistent variant order for tailwind classes. | | ✔ | | ✔ |
| [enforce-consistent-variable-syntax](docs/rules/enforce-consistent-variable-syntax.md) | Enforce consistent variable syntax. | ✔ | ✔ | | ✔ |
| [enforce-consistent-important-position](docs/rules/enforce-consistent-important-position.md) | Enforce consistent position of the important modifier. | ✔ | ✔ | | ✔ |
| [enforce-shorthand-classes](docs/rules/enforce-shorthand-classes.md) | Enforce shorthand class names. | ✔ | ✔ | | ✔ |
| [enforce-logical-properties](docs/rules/enforce-logical-properties.md) | Enforce logical property class names. | ✔ | ✔ | | ✔ |
| [enforce-canonical-classes](docs/rules/enforce-canonical-classes.md) | Enforce canonical class names. | | ✔ | ✔ | ✔ |
| [no-duplicate-classes](docs/rules/no-duplicate-classes.md) | Remove duplicate classes. | ✔ | ✔ | ✔ | ✔ |
| [no-deprecated-classes](docs/rules/no-deprecated-classes.md) | Remove deprecated classes. | | ✔ | ✔ | ✔ |
| [no-unnecessary-whitespace](docs/rules/no-unnecessary-whitespace.md) | Disallow unnecessary whitespace in tailwind classes. | ✔ | ✔ | ✔ | ✔ |
#### Correctness rules
| Name | Description | `tw3` | `tw4` | `recommended` | autofix |
| :--- | :--- | :---: | :---: | :---: | :---: |
| [no-unknown-classes](docs/rules/no-unknown-classes.md) | Report classes not registered with Tailwind CSS. | ✔ | ✔ | ✔ | |
| [no-conflicting-classes](docs/rules/no-conflicting-classes.md) | Report classes that produce conflicting styles. | | ✔ | ✔ | |
| [no-restricted-classes](docs/rules/no-restricted-classes.md) | Disallow restricted classes. | ✔ | ✔ | | ✔ |
<br/>
<br/>
### Utilities
This plugin is pre-configured to lint tailwind classes for the most popular utilities:
- [tailwind merge](https://github.com/dcastil/tailwind-merge): `twMerge` · `twJoin`
- [class variance authority](https://github.com/joe-bell/cva): `cva`
- [tailwind variants](https://github.com/nextui-org/tailwind-variants?tab=readme-ov-file): `tv`
- [shadcn](https://ui.shadcn.com/docs/installation/manual): `cn`
- [classcat](https://github.com/jorgebucaran/classcat): `cc`
- [class list builder](https://github.com/crswll/clb): `clb`
- [clsx](https://github.com/lukeed/clsx): `clsx`
- [cnbuilder](https://github.com/xobotyi/cnbuilder): `cnb`
- [classnames template literals](https://github.com/netlify/classnames-template-literals): `ctl`
- [obj str](https://github.com/lukeed/obj-str): `objstr`
- [react-twc](https://github.com/gregberge/twc): `twc` · `twx`
<br/>
<br/>
### Advanced configuration
If an utility is not supported by default, or you want to customize the configuration, you can define which [attributes](./docs/configuration/advanced.md#attribute), [callees](./docs/configuration/advanced.md#callee), [variables](./docs/configuration/advanced.md#variable), and [tags](./docs/configuration/advanced.md#tag) should get linted.
See the [Advanced configuration guide](./docs/configuration/advanced.md) to learn how to override or extend the default settings.
<br/>
<br/>
### Monorepo setup
In monorepos, linting is often started from the repository root while each package has its own Tailwind setup.
You can configure `settings["better-tailwindcss"].cwd` per file group so the plugin resolves `tailwindcss` and config files from the correct project directory.
```js
// eslint.config.js
export default [
{
files: ["packages/website/**/*.{js,jsx,cjs,mjs,ts,tsx}"],
settings: {
"better-tailwindcss": {
cwd: "./packages/website"
}
}
},
{
files: ["packages/app/**/*.{js,jsx,cjs,mjs,ts,tsx}"],
settings: {
"better-tailwindcss": {
cwd: "./packages/app"
}
}
}
];
```
See [Settings](./docs/settings/settings.md#cwd) for more details.
<br/>
<br/>
### Editor configuration
#### VSCode
##### Auto-fix on save
Most rules are intended to automatically fix the tailwind classes using VSCode extensions.
###### ESLint
For ESLint, you can install the [VSCode ESLint plugin](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) and configure it to automatically fix the classes on save by adding the following options to your `.vscode/settings.json`:
```jsonc
{
// enable VSCode to fix tailwind classes on save
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}
```
###### Oxlint
For Oxlint, you can install the [VSCode Oxc plugin](https://marketplace.visualstudio.com/items?itemName=oxc.oxc-vscode) and configure it to automatically fix the classes on save by adding the following options to your `.vscode/settings.json`:
```jsonc
{
// enable VSCode to fix tailwind classes on save
"editor.codeActionsOnSave": {
"source.fixAll.oxc": "explicit"
}
}
```
<br/>
<br/>
================================================
FILE: build/index.ts
================================================
import { $ } from "better-tailwindcss:build/utils.js";
async function build(){
const outDir = "lib"
console.info("Building...")
await $(`npx tsc --project tsconfig.build.json --outDir ${outDir}`)
await $(`npx tsc-alias --outDir ${outDir}`)
console.info("Build complete")
}
build().catch(console.error);
================================================
FILE: build/utils.ts
================================================
import { exec, type ExecOptions } from 'node:child_process'
export async function $(command: string, options?: ExecOptions): Promise<string | Buffer> {
return new Promise((resolve, reject) => {
exec(command, options, (error, stdout, stderr) => {
if (error) {
reject(error || stderr)
}
resolve(stdout)
})
})
}
================================================
FILE: changelog.config.js
================================================
export { default } from "@schoero/configs/changelogen";
================================================
FILE: docs/api/defaults.md
================================================
# Defaults
The plugin comes with a set of default [selectors](../configuration/advanced.md#selectors). These selectors are used to [determine how the rules should behave](../configuration/advanced.md#advanced-configuration) when checking your code.
In order to extend the default configuration instead of overwriting it, you can import the default options from `eslint-plugin-better-tailwindcss/defaults` and merge them with your own options.
<br/>
<br/>
## Extending the config
```ts
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
import { getDefaultSelectors } from "eslint-plugin-better-tailwindcss/defaults";
import { MatcherType, SelectorKind } from "eslint-plugin-better-tailwindcss/types";
export default [
{
plugins: {
"better-tailwindcss": eslintPluginBetterTailwindcss
},
rules: {
"better-tailwindcss/enforce-consistent-class-order": ["warn", {
selectors: [
...getDefaultSelectors(),
// custom tag
{
kind: SelectorKind.Tag,
match: [
{
type: MatcherType.String
}
],
name: "^myTag$"
},
// custom callee
{
kind: SelectorKind.Callee,
match: [
{
type: MatcherType.String
}
],
name: "^myFunction$"
},
// custom attribute
{
kind: SelectorKind.Attribute,
match: [
{
type: MatcherType.String
}
],
name: "^myAttribute$"
},
// custom variable
{
kind: SelectorKind.Variable,
match: [
{
type: MatcherType.String
}
],
name: "^myVariable$"
}
]
}]
}
}
];
```
================================================
FILE: docs/configuration/advanced.md
================================================
# Advanced Configuration
The [rules](../../README.md#rules) in this plugin lint Tailwind classes inside string literals.
To do that safely, the plugin must know **which strings are expected to contain Tailwind classes**. If it would lint every string literal in your codebase, it would produce many false positives and potentially unsafe fixes.
To configure this, you can provide an array of [selectors](#selectors) that specify where the plugin should look for class strings and how to extract them.
The plugin already ships with defaults that support [most popular tailwind utilities](../../README.md#utilities). You only need advanced configuration when:
- you use custom utilities/APIs not covered by defaults,
- you want to narrow down linting behavior,
- or you want to lint additional locations.
To extend defaults instead of replacing them, import and spread `getDefaultSelectors()` from `eslint-plugin-better-tailwindcss/defaults`.
You can find the default selectors in the [defaults documentation](../api/defaults.md).
<br/>
<br/>
## Selectors
Each selector targets one kind of source location and tells the plugin how to extract class strings from it.
The plugin supports four selector types: `attribute`, `callee`, `variable`, and `tag`.
Every selector can then match different types of string literals based on the provided `match` option.
### Type
<br/>
### `attribute`
- **kind**: `"attribute"`.
- **name**: regular expression for attribute names.
- **match** `optional`: [selector matcher](#selector-matcher-types) list.
When omitted, only direct string literals are collected.
```ts
type AttributeSelector = {
kind: "attribute";
name: string;
match?: SelectorMatcher[];
};
```
<br/>
### `callee`
- **kind**: `"callee"`.
- **name** `optional`: regular expression for callee names.
- **path** `optional`: regular expression for callee member paths like `classes.push`.
When `path` is provided, `name` is not required.
- **targetCall** `optional`: curried call target for example for `fn()("my classes")`.
If a non-negative number is provided, the zero-based call index is used.
Negative numbers count from the end (`-1` is the last call).
When omitted, the first call in a curried chain is used.
- **targetArgument** `optional`: target specific call arguments.
If a non-negative number is provided, the zero-based argument index is used.
Negative numbers count from the end (`-1` is the last argument).
When omitted, all arguments of the selected call are checked.
- **match** `optional`: [selector matcher](#selector-matcher-types) list.
When omitted, only direct string literals are collected.
```ts
type CalleeSelector = {
kind: "callee";
match?: SelectorMatcher[];
name?: string;
path?: string;
targetArgument?: "all" | "first" | "last" | number;
targetCall?: "all" | "first" | "last" | number;
};
```
<br/>
### `variable`
- **kind**: `"variable"`.
- **name**: regular expression for variable names.
Tip: The name `default` targets the `export default ...` declaration.
- **match** `optional`: [selector matcher](#selector-matcher-types) list.
When omitted, only direct string literals are collected.
```ts
type VariableSelector = {
kind: "variable";
name: string;
match?: SelectorMatcher[];
};
```
<br/>
### `tag`
- **kind**: `"tag"`.
- **name**: `optional` regular expression for tagged template names.
- **path** `optional`: regular expression for tagged template member paths like `twc.class`.
When `path` is provided, `name` is not required.
- **match** `optional`: [selector matcher](#selector-matcher-types) list.
When omitted, only direct string literals are collected.
```ts
type TagSelector = {
kind: "tag";
name: string;
match?: SelectorMatcher[];
};
```
<br/>
### How selector matching works
- Names are treated as regular expressions.
- Reserved regex characters must be escaped.
- The regex must match the whole name (not a substring).
```jsonc
{
"selectors": [
{
"kind": "callee",
"path": "^classes\\.push$",
"match": [{ "type": "strings" }]
}
]
}
```
<br/>
<br/>
### Matchers
#### Selector matcher types
##### `strings`
Matches all string literals that are not object keys or object values.
```ts
type SelectorStringMatcher = {
type: "strings";
};
```
```json
{
"selectors": [
{
"kind": "callee",
"name": "^tw$",
"match": [
{ "type": "strings" }
]
}
]
}
```
Matches:
```tsx
tw(
"this will get linted",
{ className: "this will not get linted by this matcher" }
);
```
<br />
##### `objectKeys`
Matches all object keys.
- `path` `optional`: regular expression to narrow matching to specific object key paths
See [Path option details](#path-option-details).
```ts
type SelectorObjectKeyMatcher = {
type: "objectKeys";
path?: string;
};
```
```json
{
"selectors": [
{
"kind": "callee",
"name": "^tw$",
"match": [
{
"type": "objectKeys",
"path": "^compoundVariants\\[\\d+\\]\\.(?:className|class)$"
}
]
}
]
}
```
Matches:
```tsx
tw({
compoundVariants: [
{
className: "<- this key will get linted",
myVariant: "but this key will not get linted"
}
]
});
```
<br />
##### `objectValues`
Matches all object values.
- `path` `optional`: regular expression to narrow matching to specific object value paths
See [Path option details](#path-option-details).
```ts
type SelectorObjectValueMatcher = {
type: "objectValues";
path?: string;
};
```
```json
{
"selectors": [
{
"kind": "callee",
"name": "^tw$",
"match": [
{
"type": "objectValues",
"path": "^compoundVariants\\[\\d+\\]\\.(?:className|class)$"
}
]
}
]
}
```
Matches:
```tsx
tw({
compoundVariants: [
{
className: "this value will get linted",
myVariant: "but this value will not get linted"
}
]
});
```
<br />
##### `anonymousFunctionReturn`
Matches values returned from anonymous functions and applies nested matchers to those return values.
- `match` `required`: nested matcher array
The nested `match` array can include `strings`, `objectKeys`, and `objectValues` matchers.
```ts
type SelectorAnonymousFunctionReturnMatcher = {
match: (SelectorObjectKeyMatcher | SelectorObjectValueMatcher | SelectorStringMatcher)[];
type: "anonymousFunctionReturn";
};
```
```json
{
"selectors": [
{
"kind": "callee",
"name": "^tw$",
"match": [
{
"type": "anonymousFunctionReturn",
"match": [
{ "type": "strings" },
{ "type": "objectKeys" },
{ "type": "objectValues" }
]
}
]
}
]
}
```
Matches:
```tsx
tw(() => "this will get linted with a nested string matcher");
tw(() => ({ className: "<- this key will get linted with a nested objectKeys matcher" }));
tw(() => ({ className: "this will get linted with nested objectValues matcher" }));
```
<br/>
##### Path option details
The `path` option lets you narrow down `objectKeys` and `objectValues` matching to specific object paths.
This is especially useful for libraries like [Class Variance Authority (cva)](https://cva.style/docs/getting-started/installation#intellisense), where class names appear in nested object structures.
`path` is a regex matched against the object path.
For example, the following matcher will only match object values for the `compoundVariants.class` key:
<br/>
```json
{
"selectors": [
{
"kind": "callee",
"name": "^cva$",
"match": [
{
"type": "objectValues",
"path": "^compoundVariants\\[\\d+\\]\\.(?:className|class)$"
}
]
}
]
}
```
```tsx
<img class={
cva("this will not get linted", {
compoundVariants: [
{
class: "but this will get linted",
myVariant: "and this will not get linted"
}
]
})
} />;
```
<br/>
The path reflects how the string is nested in the object:
- Dot notation for plain keys: `root.nested.values`
- Square brackets for arrays: `values[0]`
- Quoted brackets for special characters: `root["some-key"]`
For example, the object path for `value` in the object below is `root["nested-key"].values[0].value`:
```json
{
"root": {
"nested-key": {
"values": [
{
"value": "this will get linted"
}
]
}
}
}
```
<br/>
### Examples
#### Example: lint only the first argument of the last curried call
```jsonc
{
"selectors": [
{
"kind": "callee",
"name": "^tw$",
"targetCall": "last",
"targetArgument": "first"
}
]
}
```
```tsx
tw("keep", "ignore")("this will get linted", "this will not");
```
#### Example: lint `cva` strings + specific nested values
```jsonc
{
"selectors": [
{
"kind": "callee",
"name": "^cva$",
"match": [
{
"type": "strings"
},
{
"type": "objectValues",
"path": "^compoundVariants\\[\\d+\\]\\.(?:className|class)$"
}
]
}
]
}
```
```tsx
<img class={
cva("this will get linted", {
compoundVariants: [
{
class: "and this will get linted",
myVariant: "but this will not get linted"
}
]
})
} />;
```
#### Full example: custom Algolia attribute selector
You can match custom attributes by modifying your `selectors` configuration. Here is an example on how to match the values inside the Algolia `classNames` objects:
```tsx
<SearchBox
classNames={{
form: "relative",
root: "p-3 shadow-sm"
}}
/>;
```
<br/>
```js
// eslint.config.js
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
import { getDefaultSelectors } from "eslint-plugin-better-tailwindcss/defaults";
import { SelectorKind } from "eslint-plugin-better-tailwindcss/types";
import { defineConfig } from "eslint/config";
export default defineConfig({
plugins: {
"better-tailwindcss": eslintPluginBetterTailwindcss
},
settings: {
"better-tailwindcss": {
entryPoint: "app/globals.css",
selectors: [
...getDefaultSelectors(), // preserve default selectors
{
kind: SelectorKind.Attribute,
match: [{ type: "objectValues" }],
name: "^classNames$"
}
]
}
}
});
// ...
```
================================================
FILE: docs/parsers/angular.md
================================================
# Angular
- [ESLint](#eslint)
- [Oxlint](#oxlint)
<br/>
## ESLint
To use ESLint with Angular, install [Angular ESLint](https://github.com/angular-eslint/angular-eslint?tab=readme-ov-file#quick-start) and [TypeScript ESLint](https://typescript-eslint.io/getting-started). You can follow the [flat config](https://github.com/angular-eslint/angular-eslint/blob/main/docs/CONFIGURING_FLAT_CONFIG.md) setup, which includes rules from the Angular ESLint package or you can add the parser directly by following the steps below.
```sh
npm i -D angular-eslint typescript-eslint
```
To lint Tailwind CSS classes in Angular files, ensure that:
- The `angular-eslint` package is installed and configured.
- The `typescript-eslint` package is installed and configured.
- The plugin is added to your configuration.
- The `settings` object contains the correct Tailwind CSS configuration paths.
<br/>
### Flat config
Read more about the [ESLint flat config format](https://eslint.org/docs/latest/use/configure/configuration-files-new)
<br/>
```js
// eslint.config.js
import eslintParserAngular from "angular-eslint";
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
import { defineConfig } from "eslint/config";
import { parser as eslintParserTypeScript } from "typescript-eslint";
export default defineConfig([
{
// enable all recommended rules
extends: [
eslintPluginBetterTailwindcss.configs.recommended
],
// if needed, override rules to configure them individually
// rules: {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { printWidth: 100 }]
// },
settings: {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
entryPoint: "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
tailwindConfig: "tailwind.config.js"
}
}
},
{
files: ["**/*.ts"],
languageOptions: {
parser: eslintParserTypeScript,
parserOptions: {
project: true
}
},
processor: eslintParserAngular.processInlineTemplates
},
{
files: ["**/*.html"],
languageOptions: {
parser: eslintParserAngular.templateParser
}
}
]);
```
<br/>
<details>
<summary><h3>Legacy config</h3></summary>
<br/>
To use ESLint with Angular using the legacy config, install [Angular ESLint](https://github.com/angular-eslint/angular-eslint?tab=readme-ov-file#quick-start) and [@typescript-eslint/parser](https://typescript-eslint.io/getting-started/legacy-eslint-setup). You can follow the [legacy config](https://github.com/angular-eslint/angular-eslint/blob/main/docs/CONFIGURING_ESLINTRC.md) setup, which includes rules from the Angular ESLint package or you can add the parser directly by following the steps below.
```sh
npm i -D angular-eslint @typescript-eslint/parser
```
To lint Tailwind CSS classes in TypeScript files, ensure that:
- The `angular-eslint` package is installed and configured.
- The `@typescript-eslint/parser` is installed and configured.
- The plugin is added to your configuration.
- The `settings` object contains the correct Tailwind CSS configuration paths.
<br/>
```jsonc
// .eslintrc.json
{
// enable all recommended rules
"extends": [
"plugin:better-tailwindcss/legacy-recommended"
],
"settings": {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
"entryPoint": "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
"tailwindConfig": "tailwind.config.js"
}
},
// if needed, override rules to configure them individually
// "rules": {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { "printWidth": 100 }]
// },
"overrides": [
{
"files": ["**/*.ts"],
"parser": "@typescript-eslint/parser",
"extends": [
"plugin:@angular-eslint/template/process-inline-templates"
]
},
{
"files": ["**/*.html"],
"parser": "@angular-eslint/template-parser"
}
]
}
```
</details>
<br/>
## Oxlint
Oxlint currently does **not** support Angular templates and inline template processing.
Framework-specific parsers/processors like Angular are not supported in Oxlint yet, so `eslint-plugin-better-tailwindcss` cannot currently lint Angular templates through Oxlint.
You can continue using ESLint for Angular files until Oxlint adds framework parser support.
================================================
FILE: docs/parsers/astro.md
================================================
# Astro
- [ESLint](#eslint)
- [Oxlint](#oxlint)
<br/>
## ESLint
To use ESLint with Astro files, first install the [astro-eslint-parser](https://github.com/ota-meshi/astro-eslint-parser) and optionally [TypeScript ESLint](https://typescript-eslint.io/getting-started). Then, configure ESLint to use this parser for Astro files.
```sh
npm i -D astro-eslint-parser typescript-eslint
```
To lint Tailwind CSS classes in Astro files, ensure that:
- The `astro-eslint-parser` is installed and configured.
- The `typescript-eslint` package is installed if you want to lint TypeScript within Astro files.
- The plugin is added to your configuration.
- The `settings` object contains the correct Tailwind CSS configuration paths.
<br/>
### Flat config
Read more about the [ESLint flat config format](https://eslint.org/docs/latest/use/configure/configuration-files-new)
<br/>
```js
// eslint.config.js
import eslintParserAstro from "astro-eslint-parser";
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
import { defineConfig } from "eslint/config";
import { parser as eslintParserTypeScript } from "typescript-eslint";
export default defineConfig({
// enable all recommended rules
extends: [
eslintPluginBetterTailwindcss.configs.recommended
],
// if needed, override rules to configure them individually
// rules: {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { printWidth: 100 }]
// },
settings: {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
entryPoint: "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
tailwindConfig: "tailwind.config.js"
}
},
files: ["**/*.astro"],
languageOptions: {
parser: eslintParserAstro,
parserOptions: {
// optionally use TypeScript parser within for Astro files
parser: eslintParserTypeScript
}
}
});
```
<br/>
<details>
<summary><h3>Legacy config</h3></summary>
<br/>
To use ESLint with Astro files using the legacy config, first install the [astro-eslint-parser](https://github.com/ota-meshi/astro-eslint-parser) and optionally [@typescript-eslint/parser](https://typescript-eslint.io/getting-started/legacy-eslint-setup). Then, configure ESLint to use this parser for Astro files.
```sh
npm i -D astro-eslint-parser @typescript-eslint/parser
```
To lint Tailwind CSS classes in TypeScript files, ensure that:
- The `astro-eslint-parser` is installed and configured.
- The `@typescript-eslint/parser` is installed and configured if you want to lint TypeScript within Astro files.
- The plugin is added to your configuration.
- The `settings` object contains the correct Tailwind CSS configuration paths.
<br/>
```jsonc
// .eslintrc.json
{
// enable all recommended rules
"extends": [
"plugin:better-tailwindcss/legacy-recommended"
],
// if needed, override rules to configure them individually
// "rules": {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { "printWidth": 100 }]
// },
"settings": {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
"entryPoint": "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
"tailwindConfig": "tailwind.config.js"
}
},
"parser": "astro-eslint-parser",
"parserOptions": {
// optionally use TypeScript parser within for Astro files
"parser": "@typescript-eslint/parser"
}
}
```
</details>
<br/>
## Oxlint
Oxlint currently does **not** support Astro files (`.astro`).
Framework-specific parsers like Astro are not supported in Oxlint yet, so `eslint-plugin-better-tailwindcss` cannot currently lint Astro templates through Oxlint.
You can continue using ESLint for Astro files until Oxlint adds framework parser support.
================================================
FILE: docs/parsers/css.md
================================================
# CSS
- [ESLint](#eslint)
- [Oxlint](#oxlint)
<br/>
## ESLint
To use ESLint with CSS files containing Tailwind CSS `@apply` directives, first install the [@eslint/css](https://github.com/eslint/css) plugin and the [tailwind-csstree](https://www.npmjs.com/package/tailwind-csstree) custom syntax.
```sh
npm i -D @eslint/css tailwind-csstree
```
To lint Tailwind CSS classes in CSS files, ensure that:
- The `@eslint/css` plugin is installed and configured.
- The `tailwind-csstree` custom syntax is installed and configured.
- The plugin is added to your configuration.
- The `settings` object contains the correct Tailwind CSS configuration paths.
<br/>
### Flat config
Read more about the [ESLint flat config format](https://eslint.org/docs/latest/use/configure/configuration-files-new)
<br/>
```js
// eslint.config.js
import css from "@eslint/css";
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
import { defineConfig } from "eslint/config";
import { tailwind4 } from "tailwind-csstree";
export default defineConfig({
// enable all recommended rules
extends: [
eslintPluginBetterTailwindcss.configs.recommended
],
// if needed, override rules to configure them individually
// rules: {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { printWidth: 100 }]
// },
settings: {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
entryPoint: "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
tailwindConfig: "tailwind.config.js"
}
},
files: ["**/*.css"],
language: "css/css",
languageOptions: {
customSyntax: tailwind4,
tolerant: true
},
plugins: {
css
}
});
```
<br/>
> **Note:** Legacy config is not supported for CSS files as the `@eslint/css` plugin requires the ESLint flat config format.
<br/>
## Oxlint
Oxlint currently does **not** support CSS parser integration for this use case.
Because Oxlint currently only supports JavaScript-like files, `eslint-plugin-better-tailwindcss` cannot currently lint CSS `@apply` directives through Oxlint.
You can continue using ESLint for CSS files until broader parser support is available in Oxlint.
================================================
FILE: docs/parsers/html.md
================================================
# HTML
- [ESLint](#eslint)
- [Oxlint](#oxlint)
<br/>
## ESLint
To use ESLint with HTML files, first install the [@html-eslint/parser](https://github.com/yeonjuan/html-eslint/tree/main/packages/parser).
```sh
npm i -D @html-eslint/parser
```
To lint Tailwind CSS classes in HTML files, ensure that:
- The `@html-eslint/parser` is installed and configured.
- The plugin is added to your configuration.
- The `settings` object contains the correct Tailwind CSS configuration paths.
<br/>
### Flat config
Read more about the [ESLint flat config format](https://eslint.org/docs/latest/use/configure/configuration-files-new)
<br/>
```js
// eslint.config.js
import eslintParserHTML from "@html-eslint/parser";
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
import { defineConfig } from "eslint/config";
export default defineConfig({
// enable all recommended rules
extends: [
eslintPluginBetterTailwindcss.configs.recommended
],
// if needed, override rules to configure them individually
// rules: {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { printWidth: 100 }]
// },
settings: {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
entryPoint: "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
tailwindConfig: "tailwind.config.js"
}
},
files: ["**/*.html"],
languageOptions: {
parser: eslintParserHTML
}
});
```
<br/>
<details>
<summary><h3>Legacy config</h3></summary>
<br/>
```jsonc
// .eslintrc.json
{
// enable all recommended rules
"extends": [
"plugin:better-tailwindcss/legacy-recommended"
],
// if needed, override rules to configure them individually
// "rules": {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { "printWidth": 100 }]
// },
"settings": {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
"entryPoint": "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
"tailwindConfig": "tailwind.config.js"
}
},
"parser": "@html-eslint/parser"
}
```
</details>
<br/>
## Oxlint
Oxlint currently does **not** support HTML parser integration for this use case.
Because Oxlint currently only supports JavaScript-like files, `eslint-plugin-better-tailwindcss` cannot currently lint standalone HTML files through Oxlint.
You can continue using ESLint for HTML files until broader parser support is available in Oxlint.
================================================
FILE: docs/parsers/javascript.md
================================================
# JavaScript
- [ESLint](#eslint)
- [Oxlint](#oxlint)
<br/>
## ESLint
To lint Tailwind CSS classes in JavaScript files, ensure that:
- The plugin is added to your configuration.
- The `settings` object contains the correct Tailwind CSS configuration paths.
<br/>
### Flat config
Read more about the [ESLint flat config format](https://eslint.org/docs/latest/use/configure/configuration-files-new)
<br/>
```js
// eslint.config.js
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
import { defineConfig } from "eslint/config";
export default defineConfig({
// enable all recommended rules
extends: [
eslintPluginBetterTailwindcss.configs.recommended
],
// if needed, override rules to configure them individually
// rules: {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { printWidth: 100 }]
// },
settings: {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
entryPoint: "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
tailwindConfig: "tailwind.config.js"
}
}
});
```
<br/>
<details>
<summary><h3>Legacy config</h3></summary>
<br/>
```jsonc
// .eslintrc.json
{
// enable all recommended rules
"extends": [
"plugin:better-tailwindcss/legacy-recommended"
],
// if needed, override rules to configure them individually
// "rules": {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { "printWidth": 100 }]
// },
"settings": {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
"entryPoint": "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
"tailwindConfig": "tailwind.config.js"
}
}
}
```
</details>
<br/>
## Oxlint
More info about the Oxlint configuration format can be found in the [Oxlint documentation](https://oxc.rs/docs/guide/usage/linter/config.html).
To lint Tailwind CSS classes in JavaScript files, ensure that:
- The plugin is added to the `jsPlugins` array.
- The `settings` object contains the correct Tailwind CSS configuration paths.
- All relevant rules are added to the `rules` object.
<br/>
```js
// oxlint.config.js
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
import { defineConfig } from "oxlint";
export default defineConfig({
overrides: [{
files: ["**/*.{js,cjs,mjs}"],
jsPlugins: [
"eslint-plugin-better-tailwindcss"
],
rules: {
// enable all recommended rules
...eslintPluginBetterTailwindcss.configs.recommended.rules,
// if needed, override rules to configure them individually
"better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { printWidth: 100 }]
}
}],
settings: {
"better-tailwindcss": {
entryPoint: "src/global.css"
}
}
});
```
================================================
FILE: docs/parsers/jsx.md
================================================
# JSX
- [ESLint](#eslint)
- [Oxlint](#oxlint)
<br/>
## ESLint
To lint Tailwind CSS classes in JSX files, ensure that:
- `jsx` parsing is enabled in language options.
- The plugin is added to your configuration.
- The `settings` object contains the correct Tailwind CSS configuration paths.
<br/>
### Flat config
Read more about the [ESLint flat config format](https://eslint.org/docs/latest/use/configure/configuration-files-new)
<br/>
```js
// eslint.config.js
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
import { defineConfig } from "eslint/config";
export default defineConfig({
// enable all recommended rules
extends: [
eslintPluginBetterTailwindcss.configs.recommended
],
// if needed, override rules to configure them individually
// rules: {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { printWidth: 100 }]
// },
settings: {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
entryPoint: "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
tailwindConfig: "tailwind.config.js"
}
},
languageOptions: {
parserOptions: {
ecmaFeatures: {
jsx: true
}
}
}
});
```
<br/>
<details>
<summary><h3>Legacy config</h3></summary>
<br/>
```jsonc
// .eslintrc.json
{
// enable all recommended rules
"extends": [
"plugin:better-tailwindcss/legacy-recommended"
],
// if needed, override rules to configure them individually
// "rules": {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { "printWidth": 100 }]
// },
"settings": {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
"entryPoint": "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
"tailwindConfig": "tailwind.config.js"
}
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": "latest"
}
}
```
</details>
<br/>
## Oxlint
More info about the Oxlint configuration format can be found in the [Oxlint documentation](https://oxc.rs/docs/guide/usage/linter/config.html).
To lint Tailwind CSS classes in JSX files, ensure that:
- The plugin is added to the `jsPlugins` array.
- The `settings` object contains the correct Tailwind CSS configuration paths.
- All relevant rules are added to the `rules` object.
<br/>
```js
// oxlint.config.js
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
import { defineConfig } from "oxlint";
export default defineConfig({
overrides: [{
files: ["**/*.{js,jsx,mjs,cjs}"],
jsPlugins: [
"eslint-plugin-better-tailwindcss"
],
rules: {
// enable all recommended rules
...eslintPluginBetterTailwindcss.configs.recommended.rules,
// if needed, override rules to configure them individually
"better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { printWidth: 100 }]
}
}],
settings: {
"better-tailwindcss": {
entryPoint: "src/global.css"
}
}
});
```
================================================
FILE: docs/parsers/svelte.md
================================================
# Svelte
- [ESLint](#eslint)
- [Oxlint](#oxlint)
<br/>
## ESLint
To use ESLint with Svelte files, first install the [svelte-eslint-parser](https://github.com/sveltejs/svelte-eslint-parser).
```sh
npm i -D svelte-eslint-parser
```
To lint Tailwind CSS classes in Svelte files, ensure that:
- The `svelte-eslint-parser` is installed and configured.
- The plugin is added to your configuration.
- The `settings` object contains the correct Tailwind CSS configuration paths.
<br/>
### Flat config
Read more about the [ESLint flat config format](https://eslint.org/docs/latest/use/configure/configuration-files-new)
<br/>
```js
// eslint.config.js
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
import { defineConfig } from "eslint/config";
import eslintParserSvelte from "svelte-eslint-parser";
export default defineConfig({
// enable all recommended rules
extends: [
eslintPluginBetterTailwindcss.configs.recommended
],
// if needed, override rules to configure them individually
// rules: {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { printWidth: 100 }]
// },
settings: {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
entryPoint: "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
tailwindConfig: "tailwind.config.js"
}
},
files: ["**/*.svelte"],
languageOptions: {
parser: eslintParserSvelte
}
});
```
<br/>
<details>
<summary><h3>Legacy config</h3></summary>
<br/>
```jsonc
// .eslintrc.json
{
// enable all recommended rules
"extends": [
"plugin:better-tailwindcss/legacy-recommended"
],
// if needed, override rules to configure them individually
// "rules": {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { "printWidth": 100 }]
// },
"settings": {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
"entryPoint": "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
"tailwindConfig": "tailwind.config.js"
}
},
"parser": "svelte-eslint-parser"
}
```
</details>
<br/>
## Oxlint
Oxlint currently does **not** support Svelte files (`.svelte`).
Framework-specific parsers like Svelte are not supported in Oxlint yet, so `eslint-plugin-better-tailwindcss` cannot currently lint Svelte templates through Oxlint.
You can continue using ESLint for Svelte files until Oxlint adds framework parser support.
================================================
FILE: docs/parsers/tsx.md
================================================
# TSX
- [ESLint](#eslint)
- [Oxlint](#oxlint)
<br/>
## ESLint
To use ESLint with TSX files, first install the [typescript-eslint](https://typescript-eslint.io/getting-started) package.
```sh
npm i -D typescript-eslint
```
To lint Tailwind CSS classes in TSX files, ensure that:
- The `typescript-eslint` package is installed and configured.
- `jsx` parsing is enabled in language options.
- The plugin is added to your configuration.
- The `settings` object contains the correct Tailwind CSS configuration paths.
<br/>
### Flat config
Read more about the [ESLint flat config format](https://eslint.org/docs/latest/use/configure/configuration-files-new)
<br/>
```js
// eslint.config.js
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
import { defineConfig } from "eslint/config";
import { parser as eslintParserTypeScript } from "typescript-eslint";
export default defineConfig([
{
// enable all recommended rules
extends: [
eslintPluginBetterTailwindcss.configs.recommended
],
// if needed, override rules to configure them individually
// rules: {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { printWidth: 100 }]
// },
settings: {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
entryPoint: "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
tailwindConfig: "tailwind.config.js"
}
}
},
{
files: ["**/*.{ts,tsx,cts,mts}"],
languageOptions: {
parser: eslintParserTypeScript,
parserOptions: {
project: true
}
}
},
{
files: ["**/*.{jsx,tsx}"],
languageOptions: {
parserOptions: {
ecmaFeatures: {
jsx: true
}
}
}
}
]);
```
<br/>
<details>
<summary><h3>Legacy config</h3></summary>
<br/>
To use ESLint with TypeScript files using the legacy config, first install the [@typescript-eslint/parser](https://typescript-eslint.io/getting-started/legacy-eslint-setup).
```sh
npm i -D @typescript-eslint/parser
```
To lint Tailwind CSS classes in TypeScript files, ensure that:
- The `@typescript-eslint/parser` is installed and configured.
- The plugin is added to your configuration.
- The `settings` object contains the correct Tailwind CSS configuration paths.
<br/>
```jsonc
// .eslintrc.json
{
// enable all recommended rules
"extends": [
"plugin:better-tailwindcss/legacy-recommended"
],
// if needed, override rules to configure them individually
// "rules": {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { "printWidth": 100 }]
// },
"settings": {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
"entryPoint": "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
"tailwindConfig": "tailwind.config.js"
}
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": "latest"
}
}
```
</details>
<br/>
## Oxlint
More info about the Oxlint configuration format can be found in the [Oxlint documentation](https://oxc.rs/docs/guide/usage/linter/config.html).
To lint Tailwind CSS classes in TSX files, ensure that:
- The plugin is added to the `jsPlugins` array.
- The `settings` object contains the correct Tailwind CSS configuration paths.
- All relevant rules are added to the `rules` object.
<br/>
```ts
// oxlint.config.ts
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
import { defineConfig } from "oxlint";
export default defineConfig({
overrides: [{
files: ["**/*.{js,cjs,mjs,ts,tsx,cts,mts}"],
jsPlugins: [
"eslint-plugin-better-tailwindcss"
],
rules: {
// enable all recommended rules
...eslintPluginBetterTailwindcss.configs.recommended.rules,
// if needed, override rules to configure them individually
"better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { printWidth: 100 }]
}
}],
settings: {
"better-tailwindcss": {
entryPoint: "src/global.css"
}
}
});
```
================================================
FILE: docs/parsers/typescript.md
================================================
# TypeScript
- [ESLint](#eslint)
- [Oxlint](#oxlint)
<br/>
## ESLint
To use ESLint with TypeScript files, first install the [typescript-eslint](https://typescript-eslint.io/getting-started) package.
```sh
npm i -D typescript-eslint
```
To lint Tailwind CSS classes in TypeScript files, ensure that:
- The `typescript-eslint` package is installed and configured.
- The plugin is added to your configuration.
- The `settings` object contains the correct Tailwind CSS configuration paths.
<br/>
### Flat config
Read more about the [ESLint flat config format](https://eslint.org/docs/latest/use/configure/configuration-files-new)
<br/>
```js
// eslint.config.js
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
import { defineConfig } from "eslint/config";
import { parser as eslintParserTypeScript } from "typescript-eslint";
export default defineConfig({
// enable all recommended rules
extends: [
eslintPluginBetterTailwindcss.configs.recommended
],
// if needed, override rules to configure them individually
// rules: {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { printWidth: 100 }]
// },
settings: {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
entryPoint: "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
tailwindConfig: "tailwind.config.js"
}
},
files: ["**/*.{ts,tsx,cts,mts}"],
languageOptions: {
parser: eslintParserTypeScript,
parserOptions: {
project: true
}
}
});
```
<br/>
<details>
<summary><h3>Legacy config</h3></summary>
<br/>
To use ESLint with TypeScript files using the legacy config, first install the [@typescript-eslint/parser](https://typescript-eslint.io/getting-started/legacy-eslint-setup).
```sh
npm i -D @typescript-eslint/parser
```
To lint Tailwind CSS classes in TypeScript files, ensure that:
- The `@typescript-eslint/parser` is installed and configured.
- The plugin is added to your configuration.
- The `settings` object contains the correct Tailwind CSS configuration paths.
<br/>
```jsonc
// .eslintrc.json
{
// enable all recommended rules
"extends": [
"plugin:better-tailwindcss/legacy-recommended"
],
// if needed, override rules to configure them individually
// "rules": {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { "printWidth": 100 }]
// },
"settings": {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
"entryPoint": "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
"tailwindConfig": "tailwind.config.js"
}
},
"parser": "@typescript-eslint/parser"
}
```
</details>
<br/>
## Oxlint
More info about the Oxlint configuration format can be found in the [Oxlint documentation](https://oxc.rs/docs/guide/usage/linter/config.html).
To lint Tailwind CSS classes in TypeScript files, ensure that:
- The plugin is added to the `jsPlugins` array.
- The `settings` object contains the correct Tailwind CSS configuration paths.
- All relevant rules are added to the `rules` object.
<br/>
```ts
// oxlint.config.ts
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
import { defineConfig } from "oxlint";
export default defineConfig({
overrides: [{
files: ["**/*.{js,cjs,mjs,ts,tsx,cts,mts}"],
jsPlugins: [
"eslint-plugin-better-tailwindcss"
],
rules: {
// enable all recommended rules
...eslintPluginBetterTailwindcss.configs.recommended.rules,
// if needed, override rules to configure them individually
"better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { printWidth: 100 }]
}
}],
settings: {
"better-tailwindcss": {
entryPoint: "src/global.css"
}
}
});
```
================================================
FILE: docs/parsers/vue.md
================================================
# Vue
- [ESLint](#eslint)
- [Oxlint](#oxlint)
<br/>
## ESLint
To use ESLint with Vue files, first install the [vue-eslint-parser](https://github.com/vuejs/vue-eslint-parser).
```sh
npm i -D vue-eslint-parser
```
To lint Tailwind CSS classes in Vue files, ensure that:
- The `vue-eslint-parser` is installed and configured.
- The plugin is added to your configuration.
- The `settings` object contains the correct Tailwind CSS configuration paths.
<br/>
### Flat config
Read more about the [ESLint flat config format](https://eslint.org/docs/latest/use/configure/configuration-files-new)
<br/>
```js
// eslint.config.js
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
import { defineConfig } from "eslint/config";
import eslintParserVue from "vue-eslint-parser";
export default defineConfig({
// enable all recommended rules
extends: [
eslintPluginBetterTailwindcss.configs.recommended
],
// if needed, override rules to configure them individually
// rules: {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { printWidth: 100 }]
// },
settings: {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
entryPoint: "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
tailwindConfig: "tailwind.config.js"
}
},
files: ["**/*.vue"],
languageOptions: {
parser: eslintParserVue
}
});
```
<br/>
<details>
<summary><h3>Legacy config</h3></summary>
<br/>
```jsonc
// .eslintrc.json
{
// enable all recommended rules
"extends": [
"plugin:better-tailwindcss/legacy-recommended"
],
// if needed, override rules to configure them individually
// "rules": {
// "better-tailwindcss/enforce-consistent-line-wrapping": ["warn", { "printWidth": 100 }]
// },
"settings": {
"better-tailwindcss": {
// tailwindcss 4: the path to the entry file of the css based tailwind config (eg: `src/global.css`)
"entryPoint": "src/global.css",
// tailwindcss 3: the path to the tailwind config file (eg: `tailwind.config.js`)
"tailwindConfig": "tailwind.config.js"
}
},
"parser": "vue-eslint-parser"
}
```
</details>
<br/>
## Oxlint
Oxlint currently does **not** support Vue files (`.vue`).
Framework-specific parsers like Vue are not supported in Oxlint yet, so `eslint-plugin-better-tailwindcss` cannot currently lint Vue templates through Oxlint.
You can continue using ESLint for Vue files until Oxlint adds framework parser support.
================================================
FILE: docs/rules/enforce-canonical-classes.md
================================================
# better-tailwindcss/enforce-canonical-classes
Implements the [canonical suggestions](https://github.com/tailwindlabs/tailwindcss/pull/19059) from Tailwind CSS `^4.1.15`.
A canonical class is a simpler representation of a less optimal way of writing the same class. This can be the case when arbitrary values or variants are used while a predefined value exists for example.
> [!NOTE]
>
> - This rule is identical to `suggestCanonicalClasses` from the [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) VSCode extension.
> It is recommended to disable `suggestCanonicalClasses` in your projects `.vscode/settings.json` to avoid confusion:
>
> ```jsonc
> {
> "tailwindCSS.lint.suggestCanonicalClasses": "ignore"
> }
> ```
>
> - The rule covers multiple other rules.
> It is recommended to disable the following rules to avoid duplicate reports:
> - [`better-tailwindcss/enforce-shorthand-classes`](./enforce-shorthand-classes.md)
> - [`better-tailwindcss/enforce-consistent-important-position`](./enforce-consistent-important-position.md)
> - [`better-tailwindcss/enforce-consistent-variable-syntax`](./enforce-consistent-variable-syntax.md)
>
> - The canonical suggestions are based on the internal logic of Tailwind CSS and it is possible that the suggestions can change in future versions of Tailwind CSS.
> - Configurability is also limited to what Tailwind CSS exposes via their API.
> - The rule comes with a [startup cost of around ~1s](https://github.com/tailwindlabs/tailwindcss/pull/19059#:~:text=performance).
<br/>
## Options
<br/>
### `rootFontSize`
The font size of the `<html>` element in pixels. By default, the root font size is `16px` unless it is changed with CSS.
If provided, this will be used to determine if arbitrary values can be replaced with predefined sizing scales. This can also be configured via the [`settings` object](../settings/settings.md).
**Type**: `number | undefined`
**Default**: `undefined`
<br/>
### `collapse`
Whether to collapse multiple utilities into a single utility if possible.
If set to `true`, it is recommended to disable the [`better-tailwindcss/enforce-shorthand-classes`](./enforce-shorthand-classes.md) rule to avoid duplicate reports.
**Type**: `boolean`
**Default**: `true`
<br/>
### `logical`
Whether to convert between logical and physical properties when collapsing utilities.
**Type**: `boolean`
**Default**: `true`
<br/>
### `ignore`
List of List of regex patterns for classes that should not report a canonical suggestion.
This can be useful for cases where a non-canonical class representation is intentional.
**Type**: `string[]`
**Default**: `[]`
<br/>
<details>
<summary>Common options</summary>
<br/>
These options are common to all rules and can also be set globally via the [`settings` object](../settings/settings.md).
<br/>
### `selectors`
Flat list of selectors that determines where Tailwind class strings are linted.
**Type**: Array of [Selectors](../configuration/advanced.md#selectors)
**Default**: See [defaults API](../api/defaults.md)
</details>
<br/>
## Examples
```tsx
// ❌ BAD: using unnecessary arbitrary value
<div class="[display:flex]" />;
```
```tsx
// ✅ GOOD: using canonical class for display flex
<div class="flex" />;
```
```tsx
// ❌ BAD: using unnecessary arbitrary data attribute variant
<div class="data-[is-selected]:opacity-100" />;
```
```tsx
// ✅ GOOD: using canonical data attribute variant
<div class="data-is-selected:opacity-100" />;
```
```tsx
// ❌ BAD: using arbitrary value for spacing
<div class="mt-[16px]" />;
```
```tsx
// ✅ GOOD: using canonical class for spacing (with rootFontSize: 16)
<div class="mt-4" />;
```
```tsx
// ❌ BAD: using multiple utilities for margin
<div class="mt-2 mr-2 mb-2 ml-2" />;
```
```tsx
// ✅ GOOD: using collapsed utility for margin (with collapse: true)
<div class="m-2" />;
```
```tsx
// ❌ BAD: using physical properties for margin
<div class="mr-2 ml-2" />;
```
```tsx
// ✅ GOOD: using logical properties for margin (with logicalToPhysical: true)
<div class="mx-2" />;
```
================================================
FILE: docs/rules/enforce-consistent-class-order.md
================================================
# better-tailwindcss/enforce-consistent-class-order
Enforce the order of tailwind classes. It is possible to sort classes alphabetically or logically.
<br/>
## Options
### `order`
- `asc`: Sort classes alphabetically in ascending order.
- `desc`: Sort classes alphabetically in descending order.
- `official`: Sort classes according to the official sorting order from Tailwind CSS based on semantics.
- `strict`: Same as `official` but sorts variants more strictly:
- Classes that share the same base variants get grouped together.
- Classes with less variants come before classes with more variants.
- Classes with arbitrary variants come last.
**Type**: `"asc" | "desc" | "official" | "strict"`
**Default**: `"official"`
<br/>
### `detectComponentClasses`
Tailwind CSS v4 allows you to define custom [component classes](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes) like `card`, `btn`, `badge` etc.
If you want to create such classes, you can set this option to `true` to allow the rule to detect those classes and not report them as unknown classes. This can also be configured via the [`settings` object](../settings/settings.md).
**Type**: `boolean`
**Default**: `false`
<br/>
### `componentClassOrder`
Defines how component classes should be ordered among themselves.
- `asc`: Sort component classes alphabetically in ascending order.
- `desc`: Sort component classes alphabetically in descending order.
- `preserve`: Keep component classes in their original order.
**Type**: `"asc" | "desc" | "preserve"`
**Default**: `"preserve"`
<br/>
### `componentClassPosition`
Defines where component classes should be placed in relation to the whole string literal.
- `start`: Place component classes at the beginning.
- `end`: Place component classes at the end.
**Type**: `"start" | "end"`
**Default**: `"start"`
<br/>
### `unknownClassOrder`
Defines how unknown classes should be ordered among themselves.
- `asc`: Sort unknown classes alphabetically in ascending order.
- `desc`: Sort unknown classes alphabetically in descending order.
- `preserve`: Keep unknown classes in their original order.
**Type**: `"asc" | "desc" | "preserve"`
**Default**: `"preserve"`
<br/>
### `unknownClassPosition`
Defines where unknown classes should be placed in relation to the whole string literal.
- `start`: Place unknown classes at the beginning.
- `end`: Place unknown classes at the end.
**Type**: `"start" | "end"`
**Default**: `"start"`
<br/>
<details>
<summary>Common options</summary>
<br/>
These options are common to all rules and can also be set globally via the [`settings` object](../settings/settings.md).
<br/>
### `selectors`
Flat list of selectors that determines where Tailwind class strings are linted.
**Type**: Array of [Selectors](../configuration/advanced.md#selectors)
**Default**: See [defaults API](../api/defaults.md)
<br/>
### `entryPoint`
The path to the entry file of the css based tailwind config (eg: `src/global.css`).
If not specified, the plugin will fall back to the default configuration.
**Type**: `string`
**Default**: `undefined`
<br/>
### `tailwindConfig`
The path to the `tailwind.config.js` file. If not specified, the plugin will try to find it automatically or falls back to the default configuration.
This can also be set globally via the [`settings` object](../settings/settings.md#tailwindConfig).
For Tailwind CSS v4 and the css based config, use the [`entryPoint`](#entrypoint) option instead.
**Type**: `string`
**Default**: `undefined`
<br/>
### `tsconfig`
The path to the `tsconfig.json` file. If not specified, the plugin will try to find it automatically.
This can also be set globally via the [`settings` object](../settings/settings.md#tsconfig).
The tsconfig is used to resolve tsconfig [`path`](https://www.typescriptlang.org/tsconfig/#paths) aliases.
**Type**: `string`
**Default**: `undefined`
</details>
<br/>
## Examples
```tsx
// ❌ BAD: all classes are in random order
<div class="underline hover:text-opacity-70 focus:font-bold text-black hover:font-bold focus:text-opacity-70"/>;
```
```tsx
// ✅ GOOD: with option { order: 'asc' }
<div class="focus:font-bold focus:text-opacity-70 hover:font-bold hover:text-opacity-70 text-black underline"/>;
```
```tsx
// ✅ GOOD: with option { order: 'desc' }
<div class="underline text-black hover:text-opacity-70 hover:font-bold focus:text-opacity-70 focus:font-bold"/>;
```
```tsx
// ✅ GOOD: with option { order: 'official' }
<div class="text-black underline hover:font-bold hover:text-opacity-70 focus:font-bold focus:text-opacity-70"/>;
```
```tsx
// ✅ GOOD: with option { componentClassPosition: 'start' }
// 'btn' and 'card' are defined as component classes in the tailwind config
<div class="btn card text-black underline"/>;
```
```tsx
// ✅ GOOD: with option { componentClassPosition: 'end' }
// 'btn' and 'card' are defined as component classes in the tailwind config
<div class="text-black underline btn card"/>;
```
```tsx
// ✅ GOOD: with option { unknownClassPosition: 'start' }
// 'unknown-class' is not defined in the tailwind config
<div class="unknown-class text-black underline"/>;
```
```tsx
// ✅ GOOD: with option { unknownClassPosition: 'end' }
// 'unknown-class' is not defined in the tailwind config
<div class="text-black underline unknown-class"/>;
```
================================================
FILE: docs/rules/enforce-consistent-important-position.md
================================================
# better-tailwindcss/enforce-consistent-important-position
Enforce consistent important position for Tailwind CSS classes. This rule ensures that the important modifier (`!`) is placed consistently either at the beginning (legacy style) or at the end (recommended style) of class names.
Tailwind CSS v4 introduces the "recommended" position as the new standard. This rule helps you migrate to the new syntax or maintain consistency with the legacy format.
<br/>
> [!NOTE]
> This rule might interfere with [`better-tailwindcss/enforce-canonical-classes`](./enforce-canonical-classes.md) if both rules are enabled. It is recommended to use only one of them to avoid conflicting fixes.
<br/>
## Options
### `position`
Controls where the important modifier (`!`) should be placed in class names.
- `legacy`: Places the important modifier at the beginning of the class name.
- `recommended`: Places the important modifier at the end of the class name.
**Type**: `"legacy" | "recommended"`
**Default**: `"recommended"` in Tailwind CSS v4, `"legacy"` in Tailwind CSS v3 and earlier.
<br/>
<details>
<summary>Common options</summary>
<br/>
These options are common to all rules and can also be set globally via the [`settings` object](../settings/settings.md).
<br/>
### `selectors`
Flat list of selectors that determines where Tailwind class strings are linted.
**Type**: Array of [Selectors](../configuration/advanced.md#selectors)
**Default**: See [defaults API](../api/defaults.md)
<br/>
### `entryPoint`
The path to the entry file of the css based tailwind config (eg: `src/global.css`).
If not specified, the plugin will fall back to the default configuration.
**Type**: `string`
**Default**: `undefined`
<br/>
### `tailwindConfig`
The path to the `tailwind.config.js` file. If not specified, the plugin will try to find it automatically or falls back to the default configuration.
This can also be set globally via the [`settings` object](../settings/settings.md#tailwindConfig).
For Tailwind CSS v4 and the css based config, use the [`entryPoint`](#entrypoint) option instead.
**Type**: `string`
**Default**: `undefined`
<br/>
### `tsconfig`
The path to the `tsconfig.json` file. If not specified, the plugin will try to find it automatically.
This can also be set globally via the [`settings` object](../settings/settings.md#tsconfig).
The tsconfig is used to resolve tsconfig [`path`](https://www.typescriptlang.org/tsconfig/#paths) aliases.
**Type**: `string`
**Default**: `undefined`
</details>
<br/>
## Examples
### Recommended Position
```tsx
// ❌ BAD: with option { "position": "recommended" }
<div class="!text-red-500 hover:!bg-blue-500"></div>;
```
```tsx
// ✅ GOOD: with option { "position": "recommended" }
<div class="text-red-500! hover:bg-blue-500!"></div>;
```
### Legacy Position
```tsx
// ❌ BAD: with option { "position": "legacy" }
<div class="text-red-500! hover:bg-blue-500!"></div>;
```
```tsx
// ✅ GOOD: with option { "position": "legacy" }
<div class="!text-red-500 hover:!bg-blue-500"></div>;
```
================================================
FILE: docs/rules/enforce-consistent-line-wrapping.md
================================================
# better-tailwindcss/enforce-consistent-line-wrapping
Enforce tailwind classes to be broken up into multiple lines. It is possible to break at a certain print width or a certain number of classes per line.
<br/>
## Options
### `printWidth`
The maximum line length. Lines are wrapped appropriately to stay within this limit. The value `0` disables line wrapping by `printWidth`.
Tabs count according to [`tabWidth`](#tabwidth) when evaluating this limit.
**Type**: `number`
**Default**: `80`
<br/>
### `classesPerLine`
The maximum amount of classes per line. Lines are wrapped appropriately to stay within this limit . The value `0` disables line wrapping by `classesPerLine`.
**Type**: `number`
**Default**: `0`
<br/>
### `group`
Defines how different groups of classes should be separated. A group is a set of classes that share the same variant.
**Type**: `"emptyLine" | "never" | "newLine"`
**Default**: `"newLine"`
<br/>
### `preferSingleLine`
Prefer a single line for different variants. When set to `true`, the rule will keep all variants on a single line until the line exceeds the `printWidth` or `classesPerLine` limit.
**Type**: `boolean`
**Default**: `false`
<br/>
### `indent`
Determines how the code should be indented. A number defines the amount of space characters, and the string `"tab"` will use a single tab character.
**Type**: `number | "tab"`
**Default**: `2`
<br/>
### `tabWidth`
Determines how many columns a tab character contributes when checking `printWidth`.
This option only affects width calculations and does not change emitted indentation characters.
**Type**: `number`
**Default**: `1`
<br/>
### `lineBreakStyle`
The line break style.
The style `windows` will use `\r\n` as line breaks and `unix` will use `\n`.
**Type**: `"windows" | "unix"`
**Default**: `"unix"`
<br />
### `strictness`
When used in combination with formatters like prettier, biome or oxfmt, the line wrapping might interfere with the line wrapping of those formatters in some [edge cases](https://github.com/schoero/eslint-plugin-better-tailwindcss/issues/243).
If you experience such issues, you can set the `strictness` option to `"loose"` to make the rule less strict about line wrapping.
This will allow the lines to slightly exceed the `printWidth` if the plugin detects that the line wrapping would likely cause conflicts with a formatter.
**Type**: `"strict" | "loose"`
**Default**: `"strict"`
<br/>
<details>
<summary>Common options</summary>
<br/>
These options are common to all rules and can also be set globally via the [`settings` object](../settings/settings.md).
<br/>
### `selectors`
Flat list of selectors that determines where Tailwind class strings are linted.
**Type**: Array of [Selectors](../configuration/advanced.md#selectors)
**Default**: See [defaults API](../api/defaults.md)
<br/>
### `entryPoint`
The path to the entry file of the css based tailwind config (eg: `src/global.css`).
If not specified, the plugin will fall back to the default configuration.
**Type**: `string`
**Default**: `undefined`
<br/>
### `tailwindConfig`
The path to the `tailwind.config.js` file. If not specified, the plugin will try to find it automatically or falls back to the default configuration.
This can also be set globally via the [`settings` object](../settings/settings.md#tailwindConfig).
For Tailwind CSS v4 and the css based config, use the [`entryPoint`](#entrypoint) option instead.
**Type**: `string`
**Default**: `undefined`
<br/>
### `tsconfig`
The path to the `tsconfig.json` file. If not specified, the plugin will try to find it automatically.
This can also be set globally via the [`settings` object](../settings/settings.md#tsconfig).
The tsconfig is used to resolve tsconfig [`path`](https://www.typescriptlang.org/tsconfig/#paths) aliases.
**Type**: `string`
**Default**: `undefined`
</details>
<br/>
## Examples
With the default options, a class name will be broken up into multiple lines and grouped by their variants. Groups are separated by an empty line.
The following examples show how the rule behaves with different options:
```tsx
// ❌ BAD
<div class="text-black underline focus:font-bold focus:text-opacity-70 hover:font-bold hover:text-opacity-70" />;
```
```tsx
// ✅ GOOD: with option { group: 'emptyLine' }
<div class={`
text-black underline
focus:font-bold focus:text-opacity-70
hover:font-bold hover:text-opacity-70
`} />;
```
```tsx
// ✅ GOOD: with option { group: 'newLine' }
<div class={`
text-black underline
focus:font-bold focus:text-opacity-70
hover:font-bold hover:text-opacity-70
`} />;
```
```tsx
// ✅ GOOD: with option { group: 'never', printWidth: 80 }
<div class={`
text-black underline focus:font-bold focus:text-opacity-70 hover:font-bold
hover:text-opacity-70
`} />;
```
```tsx
// ✅ GOOD: with { classesPerLine: 1, group: 'emptyLine' }
<div class={`
text-black
underline
focus:font-bold
focus:text-opacity-70
hover:font-bold
hover:text-opacity-70
`} />;
```
```tsx
// ✅ GOOD: with { group: "newLine", preferSingleLine: true, printWidth: 120 }
<div class="text-black underline focus:font-bold focus:text-opacity-70 hover:font-bold hover:text-opacity-70" />;
```
```tsx
// ✅ GOOD: with { group: "newLine", preferSingleLine: true, printWidth: 80 }
<div class={`
text-black underline
focus:font-bold focus:text-opacity-70
hover:font-bold hover:text-opacity-70
`} />;
```
================================================
FILE: docs/rules/enforce-consistent-variable-syntax.md
================================================
# better-tailwindcss/enforce-consistent-variable-syntax
Enforce consistent css variable syntax in Tailwind CSS class strings.
<br/>
> [!NOTE]
> This rule might interfere with [`better-tailwindcss/enforce-canonical-classes`](./enforce-canonical-classes.md) if both rules are enabled. It is recommended to use only one of them to avoid conflicting fixes.
<br/>
## Options
### `syntax`
The syntax to enforce for css variables in Tailwind CSS class strings.
The `shorthand` syntax uses the `(--variable)` syntax in Tailwind CSS v4 and `[--variable]` syntax in Tailwind CSS v3.
**Type**: `"variable"` | `"shorthand"`
**Default**: `"shorthand"`
<br/>
<details>
<summary>Common options</summary>
<br/>
These options are common to all rules and can also be set globally via the [`settings` object](../settings/settings.md).
<br/>
### `selectors`
Flat list of selectors that determines where Tailwind class strings are linted.
**Type**: Array of [Selectors](../configuration/advanced.md#selectors)
**Default**: See [defaults API](../api/defaults.md)
</details>
<br/>
## Examples
```tsx
// ❌ BAD: Incorrect css variable syntax with option `syntax: "shorthand"`
<div class="bg-[var(--primary)]" />;
```
```tsx
// ✅ GOOD: With option `syntax: "shorthand"` in Tailwind CSS v4
<div class="bg-(--primary)" />;
```
```tsx
// ✅ GOOD: With option `syntax: "shorthand"` in Tailwind CSS v3
<div class="bg-[--primary]" />;
```
```tsx
// ❌ BAD: Incorrect css variable syntax with option `syntax: "variable"` in Tailwind CSS v4
<div class="bg-(--primary)" />;
```
```tsx
// ❌ BAD: Incorrect css variable syntax with option `syntax: "variable"` in Tailwind CSS v3
<div class="bg-[--primary]" />;
```
```tsx
// ✅ GOOD: With option `syntax: "variable"`
<div class="bg-[var(--primary)]" />;
```
================================================
FILE: docs/rules/enforce-consistent-variant-order.md
================================================
# better-tailwindcss/enforce-consistent-variant-order
Enforce Tailwind CSS variant order for class names.
<br/>
## Options
This rule has no custom options.
<br/>
<details>
<summary>Common options</summary>
<br/>
These options are common to all rules and can also be set globally via the [`settings` object](../settings/settings.md).
<br/>
### `selectors`
Flat list of selectors that determines where Tailwind class strings are linted.
**Type**: Array of [Selectors](../configuration/advanced.md#selectors)
**Default**: See [defaults API](../api/defaults.md)
</details>
<br/>
## Examples
```tsx
// ❌ BAD: variants are not in Tailwind's recommended order
<div class="hover:lg:text-red-500" />;
```
```tsx
// ✅ GOOD: variants follow Tailwind's recommended order
<div class="lg:hover:text-red-500" />;
```
<br/>
> [!NOTE]
> This rule only enforces variant order for Tailwind CSS v4 projects.
================================================
FILE: docs/rules/enforce-logical-properties.md
================================================
# better-tailwindcss/enforce-logical-properties
Enforce logical utility class names over physical directions for better RTL and LTR support.
The rule reports physical classes and auto-fixes them to their logical equivalent when a direct Tailwind replacement exists.
## Physical to Logical Mappings
| **Physical** | **Logical** |
| :--- | :--- |
| `pl-*` | `ps-*` |
| `pr-*` | `pe-*` |
| `pt-*` | `pbs-*` |
| `pb-*` | `pbe-*` |
| `ml-*` | `ms-*` |
| `mr-*` | `me-*` |
| `mt-*` | `mbs-*` |
| `mb-*` | `mbe-*` |
| `scroll-pl-*` | `scroll-ps-*` |
| `scroll-pr-*` | `scroll-pe-*` |
| `scroll-pt-*` | `scroll-pbs-*` |
| `scroll-pb-*` | `scroll-pbe-*` |
| `scroll-ml-*` | `scroll-ms-*` |
| `scroll-mr-*` | `scroll-me-*` |
| `scroll-mt-*` | `scroll-mbs-*` |
| `scroll-mb-*` | `scroll-mbe-*` |
| `left-*` | `inset-s-*` |
| `right-*` | `inset-e-*` |
| `top-*` | `inset-bs-*` |
| `bottom-*` | `inset-be-*` |
| `border-l` / `border-l-*` | `border-s` / `border-s-*` |
| `border-r` / `border-r-*` | `border-e` / `border-e-*` |
| `border-t` / `border-t-*` | `border-bs` / `border-bs-*` |
| `border-b` / `border-b-*` | `border-be` / `border-be-*` |
| `rounded-l` / `rounded-l-*` | `rounded-s` / `rounded-s-*` |
| `rounded-r` / `rounded-r-*` | `rounded-e` / `rounded-e-*` |
| `rounded-tl` / `rounded-tl-*` | `rounded-ss` / `rounded-ss-*` |
| `rounded-tr` / `rounded-tr-*` | `rounded-se` / `rounded-se-*` |
| `rounded-br` / `rounded-br-*` | `rounded-ee` / `rounded-ee-*` |
| `rounded-bl` / `rounded-bl-*` | `rounded-es` / `rounded-es-*` |
| `text-left` | `text-start` |
| `text-right` | `text-end` |
| `float-left` | `float-start` |
| `float-right` | `float-end` |
| `clear-left` | `clear-start` |
| `clear-right` | `clear-end` |
| `h-*` | `block-*` |
| `w-*` | `inline-*` |
| `min-h-*` | `min-block-*` |
| `min-w-*` | `min-inline-*` |
| `max-h-*` | `max-block-*` |
| `max-w-*` | `max-inline-*` |
| `size-*` | `block-* inline-*` |
<br/>
## Options
<br/>
<details>
<summary>Common options</summary>
<br/>
These options are common to all rules and can also be set globally via the [settings object](../settings/settings.md).
<br/>
### `selectors`
Flat list of selectors that determines where Tailwind class strings are linted.
**Type**: Array of [Selectors](../configuration/advanced.md#selectors)
**Default**: See [defaults API](../api/defaults.md)
<br/>
### `entryPoint`
The path to the entry file of the css based tailwind config (eg: `src/global.css`).
If not specified, the plugin will fall back to the default configuration.
**Type**: `string`
**Default**: `undefined`
<br/>
### `tailwindConfig`
Tailwind config file path.
**Type**: string
**Default**: Tailwind's default config resolution
<br/>
### `tsconfig`
The path to the `tsconfig.json` file. If not specified, the plugin will try to find it automatically.
This can also be set globally via the [settings object](../settings/settings.md#tsconfig).
The tsconfig is used to resolve tsconfig [path](https://www.typescriptlang.org/tsconfig/#paths) aliases.
**Type**: `string`
**Default**: `undefined`
</details>
<br/>
## Examples
```tsx
// ❌ BAD: physical direction classes
<div class="pl-4 pt-2 mr-2 mt-1 text-left rounded-tr-lg right-0 top-0 border-t" />;
```
```tsx
// ✅ GOOD: logical direction classes
<div class="ps-4 pbs-2 me-2 mbs-1 text-start rounded-se-lg inset-e-0 inset-bs-0 border-bs" />;
```
================================================
FILE: docs/rules/enforce-shorthand-classes.md
================================================
# better-tailwindcss/enforce-shorthand-classes
This rule identifies when multiple longhand Tailwind CSS classes can be replaced with a single shorthand class, improving code readability and reducing bundle size.
<br/>
> [!NOTE]
> This rule might interfere with [`better-tailwindcss/enforce-canonical-classes`](./enforce-canonical-classes.md) if both rules are enabled. It is recommended to use only one of them to avoid conflicting fixes.
<br/>
## Options
<br/>
<details>
<summary>Common options</summary>
<br/>
These options are common to all rules and can also be set globally via the [`settings` object](../settings/settings.md).
<br/>
### `selectors`
Flat list of selectors that determines where Tailwind class strings are linted.
**Type**: Array of [Selectors](../configuration/advanced.md#selectors)
**Default**: See [defaults API](../api/defaults.md)
</details>
<br/>
## Examples
```tsx
// ❌ BAD: using separate padding classes
<div class="pt-4 pr-4 pb-4 pl-4" />;
```
```tsx
// ✅ GOOD: using shorthand padding class
<div class="p-4" />;
```
```tsx
// ❌ BAD: using separate width and height classes
<div class="w-4 h-4" />;
```
```tsx
// ✅ GOOD: using shorthand size class
<div class="size-4" />;
```
================================================
FILE: docs/rules/no-conflicting-classes.md
================================================
# better-tailwindcss/no-conflicting-classes
Disallow conflicting classes in Tailwind CSS class strings. Conflicting classes are classes that apply the same CSS property on the same element. This can cause unexpected behavior as it is not clear which class will take precedence.
<br/>
> [!NOTE]
> This rule is similar to `cssConflict` from the [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) VSCode extension. It is recommended to disable `cssConflict` in your projects `.vscode/settings.json` to avoid confusion:
>
> ```jsonc
> {
> "tailwindCSS.lint.cssConflict": "ignore"
> }
> ```
<br/>
## Options
<br/>
<details>
<summary>Common options</summary>
<br/>
These options are common to all rules and can also be set globally via the [`settings` object](../settings/settings.md).
<br/>
### `selectors`
Flat list of selectors that determines where Tailwind class strings are linted.
**Type**: Array of [Selectors](../configuration/advanced.md#selectors)
**Default**: See [defaults API](../api/defaults.md)
<br/>
### `entryPoint`
The path to the entry file of the css based tailwind config (eg: `src/global.css`).
If not specified, the plugin will fall back to the default configuration.
**Type**: `string`
**Default**: `undefined`
<br/>
### `tailwindConfig`
The path to the `tailwind.config.js` file. If not specified, the plugin will try to find it automatically or falls back to the default configuration.
This can also be set globally via the [`settings` object](../settings/settings.md#tailwindConfig).
For Tailwind CSS v4 and the css based config, use the [`entryPoint`](#entrypoint) option instead.
**Type**: `string`
**Default**: `undefined`
<br/>
### `tsconfig`
The path to the `tsconfig.json` file. If not specified, the plugin will try to find it automatically.
This can also be set globally via the [`settings` object](../settings/settings.md#tsconfig).
The tsconfig is used to resolve tsconfig [`path`](https://www.typescriptlang.org/tsconfig/#paths) aliases.
**Type**: `string`
**Default**: `undefined`
</details>
<br/>
## Examples
```tsx
// ❌ BAD: Conflicting class detected: "flex" and "grid" apply the same css properties: "display"
<div class="flex grid" />;
```
```tsx
// ✅ GOOD: no conflicting classes
<div class="flex w-full" />;
```
================================================
FILE: docs/rules/no-deprecated-classes.md
================================================
# better-tailwindcss/no-deprecated-classes
Disallow the use of [deprecated Tailwind CSS classes](https://tailwindcss.com/docs/upgrade-guide#removed-deprecated-utilities) in Tailwind CSS v4.
The following classes will be reported as deprecated:
## Deprecated Utilities
| **Deprecated** | **Replacement** |
|---------------------------|-----------------------------------------------|
| `bg-opacity-*` | Use opacity modifiers like `bg-black/50` |
| `text-opacity-*` | Use opacity modifiers like `text-black/50` |
| `border-opacity-*` | Use opacity modifiers like `border-black/50` |
| `divide-opacity-*` | Use opacity modifiers like `divide-black/50` |
| `ring-opacity-*` | Use opacity modifiers like `ring-black/50` |
| `placeholder-opacity-*` | Use opacity modifiers like `placeholder-black/50` |
| `flex-shrink` | `shrink` |
| `flex-shrink-*` | `shrink-*` |
| `flex-grow` | `grow` |
| `flex-grow-*` | `grow-*` |
| `overflow-ellipsis` | `text-ellipsis` |
| `decoration-slice` | `box-decoration-slice` |
| `decoration-clone` | `box-decoration-clone` |
## Renamed Utilities (v3 → v4)
| **v3** | **v4** |
|--------------------------|--------------------------|
| `shadow` | `shadow-sm` |
| `drop-shadow` | `drop-shadow-sm` |
| `blur` | `blur-sm` |
| `backdrop-blur` | `backdrop-blur-sm` |
| `rounded` | `rounded-sm` |
<br/>
## Options
<br/>
<details>
<summary>Common options</summary>
<br/>
These options are common to all rules and can also be set globally via the [`settings` object](../settings/settings.md).
<br/>
### `selectors`
Flat list of selectors that determines where Tailwind class strings are linted.
**Type**: Array of [Selectors](../configuration/advanced.md#selectors)
**Default**: See [defaults API](../api/defaults.md)
<br/>
### `entryPoint`
The path to the entry file of the css based tailwind config (eg: `src/global.css`).
If not specified, the plugin will fall back to the default configuration.
**Type**: `string`
**Default**: `undefined`
<br/>
### `tailwindConfig`
Tailwind config file path.
**Type**: string
**Default**: Tailwind's default config resolution
<br/>
### `tsconfig`
The path to the `tsconfig.json` file. If not specified, the plugin will try to find it automatically.
This can also be set globally via the [`settings` object](../settings/settings.md#tsconfig).
The tsconfig is used to resolve tsconfig [`path`](https://www.typescriptlang.org/tsconfig/#paths) aliases.
**Type**: `string`
**Default**: `undefined`
</details>
<br/>
## Examples
```tsx
// ❌ BAD: using deprecated shadow class
<div class="shadow" />;
```
```tsx
// ✅ GOOD: using updated shadow class
<div class="shadow-sm" />;
```
```tsx
// ❌ BAD: using deprecated flex-shrink class
<div class="flex-shrink-1" />;
```
```tsx
// ✅ GOOD: using updated shrink class
<div class="shrink-1" />;
```
================================================
FILE: docs/rules/no-duplicate-classes.md
================================================
# better-tailwindcss/no-duplicate-classes
Disallow duplicate classes in Tailwind CSS class strings.
<br/>
## Options
<br/>
<details>
<summary>Common options</summary>
<br/>
These options are common to all rules and can also be set globally via the [`settings` object](../settings/settings.md).
<br/>
### `selectors`
Flat list of selectors that determines where Tailwind class strings are linted.
**Type**: Array of [Selectors](../configuration/advanced.md#selectors)
**Default**: See [defaults API](../api/defaults.md)
</details>
<br/>
## Examples
```tsx
// ❌ BAD: duplicate classes
<div class="rounded underline rounded" />;
```
```tsx
// ✅ GOOD: no duplicate classes
<div class="rounded underline" />;
```
<br/>
> [!NOTE]
> This rule is smart. It is able to detect duplicates across template literal boundaries.
```tsx
// ❌ BAD: duplicate classes in conditional template literal classes and around template elements
<div class={`
underline italic
${someCondition === true ? "rounded underline font-bold" : "rounded underline font-thin"}
italic
`} />;
```
```tsx
// ✅ GOOD: no duplicate classes
<div class={`
underline italic
${someCondition === true ? "rounded font-bold" : "rounded font-thin"}
`} />;
```
================================================
FILE: docs/rules/no-restricted-classes.md
================================================
# better-tailwindcss/no-restricted-classes
Disallow the usage of certain classes. This can be useful to disallow classes that are not recommended to be used in your project. For example, you can enforce the use of semantic color names or disallow features like arbitrary values (text-[#fff]), child variants (`*:`) or the `!important` modifier (`!`) in your project.
It is also possible to provide a custom error message and a fix for the disallowed class. The fix can be used to automatically replace the disallowed class with a recommended one.
<br/>
## Options
### `restrict`
The classes that should be disallowed. The patterns in this list are treated as regular expressions.
Matched groups of the regular expression can be used in the error message or fix by using the `$1`, `$2`, etc. syntax.
**Type**: `string[] | { pattern: string, message?: string, fix?: string }[]`
**Default**: `[]`
Make sure to match possible variants and modifiers of the class names as well:
```json
{
"restrict": [{
"fix": "$1$2-success$3",
"message": "Restricted class: Use '$1$2-success$3' instead.",
"pattern": "^([a-zA-Z0-9:/_-]*:)?(text|bg)-green-500(\\/[0-9]{1,3})?$"
}]
}
```
<br/>
<details>
<summary>Common options</summary>
<br/>
These options are common to all rules and can also be set globally via the [`settings` object](../settings/settings.md).
<br/>
### `selectors`
Flat list of selectors that determines where Tailwind class strings are linted.
**Type**: Array of [Selectors](../configuration/advanced.md#selectors)
**Default**: See [defaults API](../api/defaults.md)
</details>
<br/>
## Examples
```tsx
// ❌ BAD: disallow the use of the `text-green-500` class with option `{ restrict: [{ pattern: "^(.*)-green-500$", message: "Restricted class: Use '$1-success' instead." }] }`
<div class="rounded text-green-500" />;
// ~~~~~~~~~~~~~~ Restricted class: Use 'text-success' instead.
```
```tsx
// ❌ BAD: disallow the use of the arbitrary values with option `{ restrict: ["\\[([^\\[\\]]*?)\\](?!:)"] }`
<div class="rounded text-[#fff]" />;
// ~~~~~~
```
```tsx
// ❌ BAD: disallow the use of the child variants with option `{ restrict: ["^\\*+:.*"] }`
<div class="rounded *:mx-0" />;
// ~~~~~~
```
```tsx
// ❌ BAD: disallow the use of the important modifier with option `{ restrict: ["^.*!$"] }`
<div class="rounded p-4!" />;
// ~~~~
```
```tsx
// ❌ BAD: disallow the use of unnamed groups with option `{ restrict: ["^group$"] }`
<div class="group" />;
// ~~~~~
```
```tsx
// ❌ BAD: disallow the use of unnamed group variants with option `{ restrict: ["^group-(hover|focus|active|visited|disabled):"] }`
<div class="group-hover:p-4" />;
// ~~~~~~~~~~~
```
================================================
FILE: docs/rules/no-unknown-classes.md
================================================
# better-tailwindcss/no-unknown-classes
Disallow unknown classes in Tailwind CSS class strings. Unknown classes are classes that are not defined in your Tailwind CSS config file and therefore not recognized by Tailwind CSS.
<br/>
## Options
### `ignore`
List of List of regex patterns for classes that should not report an error.
**Type**: `string[]`
**Default**: `[]`
<br/>
### `detectComponentClasses`
Tailwind CSS v4 allows you to define custom [component classes](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes) like `card`, `btn`, `badge` etc.
If you want to create such classes, you can set this option to `true` to allow the rule to detect those classes and not report them as unknown classes. This can also be configured via the [`settings` object](../settings/settings.md).
**Type**: `boolean`
**Default**: `false`
<br/>
<details>
<summary>Common options</summary>
<br/>
These options are common to all rules and can also be set globally via the [`settings` object](../settings/settings.md).
<br/>
### `selectors`
Flat list of selectors that determines where Tailwind class strings are linted.
**Type**: Array of [Selectors](../configuration/advanced.md#selectors)
**Default**: See [defaults API](../api/defaults.md)
<br/>
### `entryPoint`
The path to the entry file of the css based tailwind config (eg: `src/global.css`).
If not specified, the plugin will fall back to the default configuration.
**Type**: `string`
**Default**: `undefined`
<br/>
### `tailwindConfig`
The path to the `tailwind.config.js` file. If not specified, the plugin will try to find it automatically or falls back to the default configuration.
This can also be set globally via the [`settings` object](../settings/settings.md#tailwindConfig).
For Tailwind CSS v4 and the css based config, use the [`entryPoint`](#entrypoint) option instead.
**Type**: `string`
**Default**: `undefined`
<br/>
### `tsconfig`
The path to the `tsconfig.json` file. If not specified, the plugin will try to find it automatically.
This can also be set globally via the [`settings` object](../settings/settings.md#tsconfig).
The tsconfig is used to resolve tsconfig [`path`](https://www.typescriptlang.org/tsconfig/#paths) aliases.
**Type**: `string`
**Default**: `undefined`
</details>
<br/>
## Examples
```tsx
// ❌ BAD: unknown class
<div class="my-class" />;
```
```tsx
// ✅ GOOD: only valid tailwindcss classes
<div class="font-bold hover:underline" />;
```
================================================
FILE: docs/rules/no-unnecessary-whitespace.md
================================================
# better-tailwindcss/no-unnecessary-whitespace
Disallow unnecessary whitespace in between and around tailwind classes.
<br/>
## Options
### `allowMultiline`
Allow multi-line class declarations.
If this option is disabled, template literal strings will be collapsed into a single line string wherever possible. Must be set to `true` when used in combination with [better-tailwindcss/enforce-consistent-line-wrapping](./enforce-consistent-line-wrapping.md).
**Type**: `boolean`
**Default**: `true`
<br/>
<br/>
<details>
<summary>Common options</summary>
<br/>
These options are common to all rules and can also be set globally via the [`settings` object](../settings/settings.md).
<br/>
### `selectors`
Flat list of selectors that determines where Tailwind class strings are linted.
**Type**: Array of [Selectors](../configuration/advanced.md#selectors)
**Default**: See [defaults API](../api/defaults.md)
</details>
<br/>
## Examples
```tsx
// ❌ BAD: random unnecessary whitespace
<div class=" text-black underline hover:text-opacity-70 " />;
```
```tsx
// ✅ GOOD: only necessary whitespace is remaining
<div class="text-black underline hover:text-opacity-70"/>;
```
================================================
FILE: docs/settings/settings.md
================================================
# Settings
## Table of Contents
- [entryPoint](#entrypoint)
- [tailwindConfig](#tailwindconfig)
- [tsconfig](#tsconfig)
- [cwd](#cwd)
- [detectComponentClasses](#detectcomponentclasses)
- [rootFontSize](#rootfontsize)
- [messageStyle](#messagestyle)
- [selectors](#selectors)
<br />
<br />
The settings object can be used to globally configure shared options across all rules. Global options will always be overridden by rule-specific options.
To set the settings object, add a `settings` key to the eslint config.
<br />
<br />
```jsonc
// eslint.config.js
{
// "plugins": {... },
// "rules": { ... },
"settings": {
"better-tailwindcss": {
// ...
}
}
}
```
<br />
<br />
### `entryPoint`
The path to the entry file of the css based tailwind config (eg: `src/global.css`). If not specified, the plugin will fall back to the default configuration. Relative to the [current working directory](#cwd).
The tailwind config is used for various rules.
**Type**: `string`
<br/>
### `tailwindConfig`
The path to the `tailwind.config.js` file. If not specified, the plugin will try to find it automatically or falls back to the default configuration. Relative to the [current working directory](#cwd).
The tailwind config is used for various rules.
For Tailwind CSS v4 and the css based config, use the [`entryPoint`](#entrypoint) option instead.
**Type**: `string`
<br/>
### `tsconfig`
The path to the `tsconfig.json` file. If not specified, the plugin will try to find it automatically.
Relative to the [current working directory](#cwd).
The tsconfig is used to resolve tsconfig [`path`](https://www.typescriptlang.org/tsconfig/#paths) aliases.
**Type**: `string`
**Default**: `undefined`
<br/>
### `cwd`
The working directory used to resolve `tailwindcss` and related config files. This is useful for monorepos where linting runs from the repository root but each project has its own `node_modules` and Tailwind setup.
This path is resolved relative to the current working directory of the ESLint process. If not specified, it falls back to the current working directory of the ESLint process.
**Type**: `string`
**Default**: `undefined`
<br/>
### `detectComponentClasses`
Tailwind CSS v4 allows you to define custom [component classes](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes) like `card`, `btn`, `badge` etc.
If you want to create such classes, you can set this option to `true` to allow the rule to detect those classes and not report them as unknown classes.
**Type**: `boolean`
**Default**: `false`
<br/>
### `rootFontSize`
The font size of the `<html>` element in pixels. By default, the root font size is `16px` unless it is changed with CSS.
If provided, this will be used to determine if arbitrary values can be replaced with predefined sizing scales.
**Type**: `number | undefined`
**Default**: `undefined`
<br/>
### `messageStyle`
Customize how linting messages are displayed.
`"visual"` visualizes whitespaces and line breaks for better readability.
`"compact"` displays visual message on a single line, better suitable for CI environments.
`"raw"` shows only the raw information without whitespace or line break visualization.
**Type**: `"visual" | "compact" | "raw"`
**Default**: `"visual"`, `"compact"` in CI environments
<br/>
### `selectors`
Flat list of selectors that determines where Tailwind class strings are linted.
This controls what gets linted globally: only string literals matched by these selectors are treated as Tailwind class candidates.
**Type**: Array of [Selectors](../configuration/advanced.md#selectors)
**Default**: See [defaults API](../api/defaults.md)
================================================
FILE: eslint.config.ts
================================================
import config from "@schoero/configs/eslint";
import { defineConfig } from "eslint/config";
export default defineConfig([
...config,
{
files: ["**/*.test.{js,jsx,cjs,mjs,ts,tsx}", "**/*.test-d.{ts,tsx}"],
rules: {
"eslint-plugin-stylistic/quotes": ["warn", "double", { allowTemplateLiterals: "always", avoidEscape: true }],
"eslint-plugin-typescript/no-unnecessary-condition": "off",
"eslint-plugin-typescript/no-useless-template-literals": "off",
"eslint-plugin-vitest/expect-expect": "off"
}
},
{
files: ["**/*.test.ts"],
rules: {
"eslint-plugin-perfectionist/sort-objects": [
"warn",
{
customGroups: [
{
elementNamePattern: "^(astro|angular|jsx|svelte|vue|html)(Output)?$",
groupName: "markup",
selector: "property"
}
],
groups: ["markup", { newlinesBetween: "always" }, "unknown"],
ignoreCase: true,
partitionByComment: false,
type: "alphabetical"
}
],
"eslint-plugin-typescript/naming-convention": "off",
"eslint-plugin-typescript/no-floating-promises": "off"
}
},
{
files: ["tests/utils/lint.ts"],
rules: {
"eslint-plugin-typescript/naming-convention": "off"
}
}
]);
================================================
FILE: package.json
================================================
{
"version": "4.5.0",
"type": "module",
"name": "eslint-plugin-better-tailwindcss",
"description": "auto-wraps tailwind classes after a certain print width or class count into multiple lines to improve readability.",
"license": "MIT",
"author": "Roger Schönbächler",
"repository": {
"type": "git",
"url": "git+https://github.com/schoero/eslint-plugin-better-tailwindcss.git"
},
"bugs": {
"url": "https://github.com/schoero/eslint-plugin-better-tailwindcss/issues"
},
"exports": {
".": "./lib/configs/config.js",
"./api/defaults": "./lib/api/defaults.js",
"./api/types": "./lib/api/types.js",
"./defaults": "./lib/api/defaults.js",
"./types": "./lib/api/types.js"
},
"main": "./lib/configs/config.js",
"scripts": {
"build": "vite-node build",
"build:ci": "vite-node build",
"eslint": "eslint .",
"eslint:ci": "npm run eslint -- --max-warnings 0",
"eslint:fix": "npm run eslint -- --fix",
"install:v3": "npm i tailwindcss@^3 --no-save",
"install:v4": "npm i tailwindcss@^4 --no-save",
"lint": "npm run eslint && npm run markdownlint",
"lint:ci": "npm run eslint:ci && npm run markdownlint:ci",
"lint:fix": "npm run eslint:fix && npm run markdownlint:fix",
"markdownlint": "markdownlint-cli2 '**/*.md' '#**/node_modules'",
"markdownlint:ci": "npm run markdownlint",
"markdownlint:fix": "npm run markdownlint -- --fix",
"postrelease:alpha": "eslint --fix package.json && markdownlint-cli2 --fix 'CHANGELOG.md'",
"postrelease:beta": "eslint --fix package.json && markdownlint-cli2 --fix 'CHANGELOG.md'",
"postrelease:latest": "eslint --fix package.json && markdownlint-cli2 --fix 'CHANGELOG.md'",
"prebuild": "npm run typecheck && npm run lint && npm run spellcheck",
"prerelease:alpha": "npm run test -- --run && npm run build",
"prerelease:beta": "npm run test -- --run && npm run build",
"prerelease:latest": "npm run test -- --run && npm run build",
"pretest:v3": "npm run install:v3",
"pretest:v4": "npm run install:v4",
"publish:alpha": "changelogen gh release && changelogen --publish --publishTag alpha",
"publish:beta": "changelogen gh release && changelogen --publish --publishTag beta",
"publish:latest": "changelogen gh release && changelogen --publish",
"release:alpha": "changelogen --bump --output --prerelease alpha",
"release:beta": "changelogen --bump --output --prerelease beta",
"release:latest": "changelogen --bump --output --no-tag",
"spellcheck": "cspell lint",
"spellcheck:ci": "npm run spellcheck -- --no-progress",
"test": "vitest -c ./vite.config.ts --exclude tests/e2e",
"test:all": "npm run test:v3 && npm run test:v4",
"test:e2e": "vitest -c ./vite.config.ts tests/e2e --run",
"test:v3": "npm run test -- --run",
"test:v4": "npm run test -- --run",
"typecheck": "tsc --noEmit"
},
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=23.0.0"
},
"files": [
"lib"
],
"peerDependencies": {
"eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0",
"oxlint": "^1.35.0",
"tailwindcss": "^3.3.0 || ^4.1.17"
},
"peerDependenciesMeta": {
"eslint": {
"optional": true
},
"oxlint": {
"optional": true
}
},
"dependencies": {
"@eslint/css-tree": "^4.0.1",
"@valibot/to-json-schema": "^1.6.0",
"enhanced-resolve": "^5.20.1",
"jiti": "^2.6.1",
"synckit": "^0.11.12",
"tailwind-csstree": "^0.3.0",
"tsconfig-paths-webpack-plugin": "^4.2.0",
"valibot": "^1.3.1"
},
"devDependencies": {
"@angular/compiler": "^21.2.6",
"@angular-eslint/template-parser": "^21.3.1",
"@eslint/css": "^1.1.0",
"@html-eslint/parser": "0.58.1",
"@oxc-node/core": "^0.0.35",
"@schoero/configs": "^1.5.32",
"@types/estree-jsx": "^1.0.5",
"@types/node": "^25.5.0",
"@typescript-eslint/parser": "^8.58.0",
"astro-eslint-parser": "^1.4.0",
"changelogen": "^0.6.2",
"cspell": "^10.0.0",
"daisyui": "^5.5.19",
"es-html-parser": "^0.3.1",
"eslint": "^9.39.4",
"eslint-plugin-better-tailwindcss": "file:./",
"json-schema": "^0.4.0",
"markdownlint": "^0.40.0",
"oxlint": "^1.58.0",
"prettier": "^3.8.1",
"svelte": "^5.55.1",
"svelte-eslint-parser": "^1.6.0",
"tailwindcss": "^4.2.2",
"tsc-alias": "^1.8.16",
"typescript": "^5.9.3",
"vite-node": "^6.0.0",
"vitest": "^4.1.2",
"vue-eslint-parser": "^10.4.0"
},
"keywords": [
"eslint",
"eslint-plugin",
"stylistic",
"formatting",
"tailwind",
"readable",
"readability",
"horizontal",
"scrolling",
"multiline",
"multi",
"newline",
"line",
"break",
"linebreak",
"wrap",
"template",
"literal",
"jsx",
"html",
"astro",
"svelte",
"vue",
"react",
"qwik",
"solid",
"template-literal",
"template-literals",
"tailwindcss",
"tailwind-css",
"tailwind-classes"
],
"volta": {
"node": "25.2.1"
}
}
================================================
FILE: src/api/defaults.ts
================================================
/* eslint-disable eslint-plugin-jsdoc/require-returns */
/* eslint-disable eslint-plugin-jsdoc/require-description */
import { DEFAULT_SELECTORS } from "better-tailwindcss:options/default-options.js";
import { migrateFlatSelectorsToLegacySelectors } from "better-tailwindcss:options/migrate.js";
import { SelectorKind } from "better-tailwindcss:types/rule.js";
import { isSelectorKind } from "better-tailwindcss:utils/selectors.js";
import type { Attributes } from "better-tailwindcss:options/schemas/attributes.js";
import type { Callees } from "better-tailwindcss:options/schemas/callees.js";
import type { Tags } from "better-tailwindcss:options/schemas/tags.js";
import type { Variables } from "better-tailwindcss:options/schemas/variables.js";
/**
* @deprecated Migrate to selectors instead.
*/
export function getDefaultCallees() {
return migrateFlatSelectorsToLegacySelectors(
DEFAULT_SELECTORS.filter(isSelectorKind(SelectorKind.Callee))
).callees ?? [] satisfies Callees;
}
/**
* @deprecated Migrate to selectors instead.
*/
export function getDefaultAttributes() {
return migrateFlatSelectorsToLegacySelectors(
DEFAULT_SELECTORS.filter(isSelectorKind(SelectorKind.Attribute))
).attributes ?? [] satisfies Attributes;
}
/**
* @deprecated Migrate to selectors instead.
*/
export function getDefaultVariables() {
return migrateFlatSelectorsToLegacySelectors(
DEFAULT_SELECTORS.filter(isSelectorKind(SelectorKind.Variable))
).variables ?? [] satisfies Variables;
}
/**
* @deprecated Migrate to selectors instead.
*/
export function getDefaultTags() {
return migrateFlatSelectorsToLegacySelectors(
DEFAULT_SELECTORS.filter(isSelectorKind(SelectorKind.Tag))
).tags ?? [] satisfies Tags;
}
export function getDefaultSelectors() {
return DEFAULT_SELECTORS;
}
================================================
FILE: src/api/types.ts
================================================
/* Targets for arguments and calls */
export type { ArgumentTarget, CallTarget } from "better-tailwindcss:types/rule.js";
/* Legacy Matchers */
export { MatcherType } from "better-tailwindcss:types/rule.js";
export type { Matcher, ObjectKeyMatcher, ObjectValueMatcher, StringMatcher } from "better-tailwindcss:types/rule.js";
/* Selector */
export { SelectorKind } from "better-tailwindcss:types/rule.js";
export type { AttributeSelector, CalleeSelector, Selector, TagSelector, VariableSelector } from "better-tailwindcss:types/rule.js";
export type { SelectorAnonymousFunctionReturnMatcher, SelectorMatcher, SelectorObjectKeyMatcher, SelectorObjectValueMatcher, SelectorStringMatcher } from "better-tailwindcss:types/rule.js";
/* Other types */
export type { Regex, Selectors } from "better-tailwindcss:types/rule.js";
================================================
FILE: src/async-utils/cache.ts
================================================
import { getModifiedDate } from "./fs.js";
interface CacheItem {
date: Date;
value: any;
}
const CACHE = new Map<string, CacheItem>();
export function invalidateByModifiedDate(cache: CacheItem, path: string | undefined): boolean {
if(!path){ return true; }
const modified = getModifiedDate(path);
return modified > cache.date;
}
export function withCache<Result>(key: string, path: string | undefined, callback: () => Result, invalidate?: (cache: CacheItem, path: string | undefined) => boolean): Result;
export function withCache<Result>(key: string, path: string | undefined, callback: () => Promise<Result>, invalidate?: (cache: CacheItem, path: string | undefined) => boolean): Promise<Result>;
export function withCache<Result>(key: string, path: string | undefined, callback: () => Promise<Result> | Result, invalidate: (cache: CacheItem, path: string | undefined) => boolean = invalidateByModifiedDate): Promise<Result> | Result {
const cacheKey = `${key}-${path}`;
const cached = CACHE.get(cacheKey);
if(cached && !invalidate(cached, path)){
return cached.value;
}
const value = callback();
if(value instanceof Promise){
return value.then(resolvedValue => {
CACHE.set(cacheKey, { date: new Date(), value: resolvedValue });
return resolvedValue;
});
} else {
CACHE.set(cacheKey, { date: new Date(), value });
return value;
}
}
export function clearCache() {
CACHE.clear();
}
================================================
FILE: src/async-utils/escape.ts
================================================
import { getCachedRegex } from "./regex.js";
export function escapeForRegex(word: string) {
return word.replace(getCachedRegex(/[$()*+./?[\\\]^{|}-]/g), "\\$&");
}
================================================
FILE: src/async-utils/fs.ts
================================================
import { existsSync, statSync } from "node:fs";
import { basename, dirname, resolve } from "node:path";
export function findPathRecursive(cwd: string, startDirectory: string, entries: string[]): string | undefined {
const resolvedPaths = entries.map(p => resolve(startDirectory, p));
for(let resolvedPath = resolvedPaths.shift(); resolvedPath !== undefined; resolvedPath = resolvedPaths.shift()){
if(existsSync(resolvedPath)){
return resolvedPath;
}
const fileName = basename(resolvedPath);
const directory = dirname(resolvedPath);
const parentDirectory = resolve(directory, "..");
const parentPath = resolve(parentDirectory, fileName);
if(parentDirectory === directory || directory === cwd){
continue;
}
resolvedPaths.push(parentPath);
}
}
export function getModifiedDate(filePath: string): Date {
try {
const stats = statSync(filePath);
return stats.mtime;
} catch {
return new Date();
}
}
================================================
FILE: src/async-utils/module.ts
================================================
export function isCommonJSModule() {
return typeof module !== "undefined" && typeof module.exports !== "undefined";
}
export function isESModule() {
return !isCommonJSModule();
}
================================================
FILE: src/async-utils/operations.ts
================================================
import type { GetCanonicalClasses } from "../tailwindcss/canonical-classes.js";
import type { GetClassOrder } from "../tailwindcss/class-order.js";
import type { GetConflictingClasses } from "../tailwindcss/conflicting-classes.js";
import type { GetCustomComponentClasses } from "../tailwindcss/custom-component-classes.js";
import type { GetDissectedClasses } from "../tailwindcss/dissect-classes.js";
import type { GetPrefix } from "../tailwindcss/prefix.js";
import type { GetUnknownClasses } from "../tailwindcss/unknown-classes.js";
import type { GetVariantOrder } from "../tailwindcss/variant-order.js";
import type { Async } from "../types/async.js";
export interface Operations {
getCanonicalClasses: Async<GetCanonicalClasses>;
getClassOrder: Async<GetClassOrder>;
getConflictingClasses: Async<GetConflictingClasses>;
getCustomComponentClasses: Async<GetCustomComponentClasses>;
getDissectedClasses: Async<GetDissectedClasses>;
getPrefix: Async<GetPrefix>;
getUnknownClasses: Async<GetUnknownClasses>;
getVariantOrder: Async<GetVariantOrder>;
}
// mapped type variant that enables correlated generic dispatch https://github.com/microsoft/TypeScript/issues/30581
export type OperationHandlers = {
[Operation in keyof Operations]: (...args: Parameters<Operations[Operation]>) => ReturnType<Operations[Operation]>;
};
================================================
FILE: src/async-utils/order.ts
================================================
export enum VARIANT_ORDER_FLAGS {
GLOBAL = 1 << 16
}
================================================
FILE: src/async-utils/path.ts
================================================
import { pathToFileURL } from "node:url";
import { isESModule } from "./module.js";
import { isWindows } from "./platform.js";
export function normalize(path: string): string {
return isWindows() && isESModule() ? pathToFileURL(path).toString() : path;
}
================================================
FILE: src/async-utils/platform.ts
================================================
export function isWindows() {
return process.platform === "win32";
}
================================================
FILE: src/async-utils/regex.ts
================================================
const REGEX_CACHE = new Map<string, RegExp>();
const MAX_CACHE_SIZE = 500;
function getRegexCacheKey(pattern: string, flags: string): string {
return `${flags}\u0000${pattern}`;
}
export function getCachedRegex(regex: RegExp): RegExp;
export function getCachedRegex(pattern: string, flags?: string): RegExp;
export function getCachedRegex(patternOrRegex: RegExp | string, flags?: string): RegExp {
const regexFlags = typeof patternOrRegex === "string"
? flags ?? ""
: patternOrRegex.flags;
const regexPattern = typeof patternOrRegex === "string"
? patternOrRegex
: patternOrRegex.source;
const cacheKey = getRegexCacheKey(regexPattern, regexFlags);
let regex = REGEX_CACHE.get(cacheKey);
if(!regex){
if(REGEX_CACHE.size >= MAX_CACHE_SIZE){
const firstKey = REGEX_CACHE.keys().next().value;
if(firstKey !== undefined){
REGEX_CACHE.delete(firstKey);
}
}
regex = typeof patternOrRegex === "string"
? new RegExp(patternOrRegex, flags)
: patternOrRegex;
REGEX_CACHE.set(cacheKey, regex);
}
if(regex.global || regex.sticky){
regex.lastIndex = 0;
}
return regex;
}
================================================
FILE: src/async-utils/resolvers.ts
================================================
import fs from "node:fs";
import enhancedResolve from "enhanced-resolve";
import { TsconfigPathsPlugin } from "tsconfig-paths-webpack-plugin";
import { withCache } from "../async-utils/cache.js";
import type { AsyncContext } from "../utils/context.js";
const fileSystem = new enhancedResolve.CachedInputFileSystem(fs, 30_000);
const getESMResolver = (ctx: AsyncContext | undefined) => withCache("esm-resolver", ctx?.tsconfigPath, () => enhancedResolve.ResolverFactory.createResolver({
conditionNames: ["node", "import"],
extensions: [".mjs", ".js"],
fileSystem,
mainFields: ["module"],
plugins: ctx?.tsconfigPath ? [new TsconfigPathsPlugin({ configFile: ctx.tsconfigPath, mainFields: ["module"] })] : [],
useSyncFileSystemCalls: true
}));
const getCJSResolver = (ctx: AsyncContext | undefined) => withCache("cjs-resolver", ctx?.tsconfigPath, () => enhancedResolve.ResolverFactory.createResolver({
conditionNames: ["node", "require"],
extensions: [".js", ".cjs"],
fileSystem,
mainFields: ["main"],
plugins: ctx?.tsconfigPath ? [new TsconfigPathsPlugin({ configFile: ctx.tsconfigPath, mainFields: ["main"] })] : [],
useSyncFileSystemCalls: true
}));
const getCSSResolver = (ctx: AsyncContext | undefined) => withCache("css-resolver", ctx?.tsconfigPath, () => enhancedResolve.ResolverFactory.createResolver({
conditionNames: ["style"],
extensions: [".css"],
fileSystem,
mainFields: ["style"],
plugins: ctx?.tsconfigPath ? [new TsconfigPathsPlugin({ configFile: ctx.tsconfigPath, mainFields: ["style"] })] : [],
useSyncFileSystemCalls: true
}));
const jsonResolver = enhancedResolve.ResolverFactory.createResolver({
conditionNames: ["json"],
extensions: [".json"],
fileSystem,
useSyncFileSystemCalls: true
});
export function resolveJs(path: string, cwd: string): string;
export function resolveJs(ctx: AsyncContext, path: string, cwd?: string): string;
export function resolveJs(ctxOrPath: AsyncContext | string | undefined, pathOrCwd: string, cwdOrUndefined?: string): string {
const ctx = typeof ctxOrPath === "object" ? ctxOrPath : undefined;
const path = typeof ctxOrPath === "string" ? ctxOrPath : pathOrCwd;
const cwd = (typeof ctxOrPath === "object" ? cwdOrUndefined : pathOrCwd)!;
try {
return getESMResolver(ctx).resolveSync({}, cwd, path) || path;
} catch {
return getCJSResolver(ctx).resolveSync({}, cwd, path) || path;
}
}
export function resolveCss(path: string, cwd: string): string;
export function resolveCss(ctx: AsyncContext, path: string, cwd?: string): string;
export function resolveCss(ctxOrPath: AsyncContext | string | undefined, pathOrCwd: string, cwdOrUndefined?: string): string {
const ctx = typeof ctxOrPath === "object" ? ctxOrPath : undefined;
const path = typeof ctxOrPath === "string" ? ctxOrPath : pathOrCwd;
const cwd = (typeof ctxOrPath === "object" ? cwdOrUndefined : pathOrCwd)!;
try {
return getCSSResolver(ctx).resolveSync({}, cwd, path) || path;
} catch {
return path;
}
}
export function resolveJson(path: string, cwd: string): string | undefined {
try {
return jsonResolver.resolveSync({}, cwd, path) || undefined;
} catch {}
}
================================================
FILE: src/async-utils/tsconfig.ts
================================================
import { resolve } from "node:path";
import { withCache } from "./cache.js";
import { findPathRecursive } from "./fs.js";
import type { Warning } from "../types/async.js";
export interface GetTSConfigRequest {
configPath: string | undefined;
cwd: string;
}
export interface GetTSConfigResponse {
path: string | undefined;
warnings: (Warning | undefined)[];
}
export const getTSConfigPath = ({ configPath, cwd }: GetTSConfigRequest): GetTSConfigResponse => withCache("tsconfig-path", configPath, () => {
const potentialPaths = [
...configPath ? [configPath] : [],
"tsconfig.json",
"jsconfig.json"
];
const foundConfigPath = findPathRecursive(cwd, cwd, potentialPaths);
const warning = getConfigPathWarning(configPath, foundConfigPath);
return {
path: foundConfigPath,
warnings: [warning]
};
});
function getConfigPathWarning(configPath: string | undefined, foundConfigPath: string | undefined): Warning | undefined {
if(!configPath){
return;
}
if(foundConfigPath && resolve(configPath) === resolve(foundConfigPath)){
return;
}
return {
option: "tsconfig",
title: `No tsconfig found at \`${configPath}\``
};
}
================================================
FILE: src/async-utils/worker.ts
================================================
import { env } from "node:process";
import { TsRunner } from "synckit";
import type { SynckitOptions } from "synckit";
const defaultTimeout = 30_000;
export function getWorkerOptions(): SynckitOptions | undefined {
if(env.NODE_ENV === "test"){
return {
timeout: Number(env.SYNCKIT_TIMEOUT) || defaultTimeout,
tsRunner: TsRunner.OXC
};
} else {
return {
timeout: Number(env.SYNCKIT_TIMEOUT) || defaultTimeout
};
}
}
================================================
FILE: src/configs/config.test.ts
================================================
import { describe, expect, it } from "vitest";
import config from "better-tailwindcss:configs/config.js";
describe("configs", () => {
const stylisticRules = Object.entries(config.rules).reduce<string[]>((acc, [name, rule]) => {
if(rule.meta.docs.recommended && rule.meta.type === "layout"){
acc.push(`${config.meta.name}/${name}`);
}
return acc;
}, []);
const correctnessRules = Object.entries(config.rules).reduce<string[]>((acc, [name, rule]) => {
if(rule.meta.docs.recommended && rule.meta.type === "problem"){
acc.push(`${config.meta.name}/${name}`);
}
return acc;
}, []);
describe("stylistic", () => {
it("should only contain recommended stylistic rules", () => {
expect(Object.keys(config.configs.stylistic.rules)).toEqual(stylisticRules);
});
});
describe("correctness", () => {
it("should only contain recommended correctness rules", () => {
expect(Object.keys(config.configs.correctness.rules)).toEqual(correctnessRules);
});
});
describe("recommended", () => {
it("should contain all recommended rules", () => {
expect(Object.keys(config.configs.recommended.rules)).toEqual([
...stylisticRules,
...correctnessRules
]);
});
});
});
================================================
FILE: src/configs/config.ts
================================================
import { enforceCanonicalClasses } from "better-tailwindcss:rules/enforce-canonical-classes.js";
import { enforceConsistentClassOrder } from "better-tailwindcss:rules/enforce-consistent-class-order.js";
import { enforceConsistentImportantPosition } from "better-tailwindcss:rules/enforce-consistent-important-position.js";
import { enforceConsistentLineWrapping } from "better-tailwindcss:rules/enforce-consistent-line-wrapping.js";
import { enforceConsistentVariableSyntax } from "better-tailwindcss:rules/enforce-consistent-variable-syntax.js";
import { enforceConsistentVariantOrder } from "better-tailwindcss:rules/enforce-consistent-variant-order.js";
import { enforceLogicalProperties } from "better-tailwindcss:rules/enforce-logical-properties.js";
import { enforceShorthandClasses } from "better-tailwindcss:rules/enforce-shorthand-classes.js";
import { noConflictingClasses } from "better-tailwindcss:rules/no-conflicting-classes.js";
import { noDeprecatedClasses } from "better-tailwindcss:rules/no-deprecated-classes.js";
import { noDuplicateClasses } from "better-tailwindcss:rules/no-duplicate-classes.js";
import { noRestrictedClasses } from "better-tailwindcss:rules/no-restricted-classes.js";
import { noUnknownClasses } from "better-tailwindcss:rules/no-unknown-classes.js";
import { noUnnecessaryWhitespace } from "better-tailwindcss:rules/no-unnecessary-whitespace.js";
import type { ESLint, Linter } from "eslint";
import type { RuleCategory } from "better-tailwindcss:types/rule.js";
type ConfigName = "recommended" | RuleCategory;
type Severity = "error" | "warn";
type PluginRules = typeof rules[number];
type PluginName = typeof plugin.meta.name;
type RuleObject = {
[Rule in PluginRules as Rule extends any ? Rule["name"] : never]: Rule["rule"]
};
type GetRules<Category extends RuleCategory> = Extract<
PluginRules,
{ category: Category; recommended: true; }
>;
const rules = [
enforceConsistentClassOrder,
enforceConsistentImportantPosition,
enforceConsistentLineWrapping,
enforceConsistentVariantOrder,
enforceConsistentVariableSyntax,
enforceLogicalProperties,
enforceShorthandClasses,
noConflictingClasses,
noDeprecatedClasses,
noDuplicateClasses,
noRestrictedClasses,
noUnnecessaryWhitespace,
noUnknownClasses,
enforceCanonicalClasses
] as const;
const plugin = {
meta: {
name: "better-tailwindcss"
},
rules: rules.reduce(
(acc, { name, rule }) => {
acc[name] = rule;
return acc;
},
{}
) as RuleObject
} as const satisfies ESLint.Plugin;
const getStylisticRules = <SeverityLevel extends Severity = "warn">(severity: SeverityLevel = "warn" as SeverityLevel) => {
return rules.reduce((acc, { category, name, recommended }) => {
if(category !== "stylistic" || !recommended){
return acc;
}
acc[`${plugin.meta.name}/${name}`] = severity;
return acc;
}, {} as Record<`${PluginName}/${GetRules<"stylistic">["name"]}`, SeverityLevel>);
};
const getCorrectnessRules = <SeverityLevel extends Severity = "error">(severity: SeverityLevel = "error" as SeverityLevel) => {
return rules.reduce((acc, { category, name, recommended }) => {
if(category !== "correctness" || !recommended){
return acc;
}
acc[`${plugin.meta.name}/${name}`] = severity;
return acc;
}, {} as Record<`${PluginName}/${GetRules<"correctness">["name"]}`, SeverityLevel>);
};
const getRecommendedRules = <SeverityLevel extends Severity>(severity?: SeverityLevel) => ({
...getStylisticRules(severity),
...getCorrectnessRules(severity)
});
const createLegacyConfig = <Rules extends Linter.RulesRecord>(rules: Rules) => ({
plugins: [plugin.meta.name],
rules
} satisfies Linter.LegacyConfig<Rules>);
const createFlatConfig = <Rules extends Linter.RulesRecord>(rules: Rules) => ({
plugins: {
[plugin.meta.name]: plugin
},
rules
} satisfies Linter.Config<Rules>);
const configEntry = <ConfigName extends string, Config>(key: ConfigName, value: Config) => {
return { [key]: value } as { [Name in ConfigName]: Config };
};
const createConfig = <Name extends ConfigName, RuleName extends string>(name: Name, getRules: <SeverityLevel extends Severity>(severity?: SeverityLevel) => Record<RuleName, SeverityLevel>) => {
return {
...configEntry(`legacy-${name}` as const, createLegacyConfig(getRules())),
...configEntry(
`legacy-${name}-error` as const,
createLegacyConfig(getRules("error"))
),
...configEntry(
`legacy-${name}-warn` as const,
createLegacyConfig(getRules("warn"))
),
...configEntry(name, createFlatConfig(getRules())),
...configEntry(
`${name}-error` as const,
createFlatConfig(getRules("error"))
),
...configEntry(`${name}-warn` as const, createFlatConfig(getRules("warn")))
};
};
const config = {
...plugin,
configs: {
...createConfig("stylistic", getStylisticRules),
...createConfig("correctness", getCorrectnessRules),
...createConfig("recommended", getRecommendedRules)
}
} satisfies ESLint.Plugin;
export default config;
export { config as "module.exports" };
================================================
FILE: src/options/callees/cc.test.ts
================================================
import { describe, it } from "vitest";
import { CC_OBJECT_KEYS, CC_STRINGS } from "better-tailwindcss:options/callees/cc.js";
import { noUnnecessaryWhitespace } from "better-tailwindcss:rules/no-unnecessary-whitespace.js";
import { lint } from "better-tailwindcss:tests/utils/lint.js";
describe("cc", () => {
it("should lint strings and strings in arrays", () => {
const dirty = `cc(" lint ", [" lint ", " lint "])`;
const clean = `cc("lint", ["lint", "lint"])`;
lint(noUnnecessaryWhitespace, {
invalid: [
{
jsx: dirty,
jsxOutput: clean,
svelte: `<script>${dirty}</script>`,
svelteOutput: `<script>${clean}</script>`,
vue: `<script>${dirty}</script>`,
vueOutput: `<script>${clean}</script>`,
errors: 6,
options: [{ selectors: [CC_STRINGS] }]
}
]
});
});
it("should lint object keys", () => {
const dirty = `
cc(" ignore ", {
" lint ": { " lint ": " ignore " },
}
)
`;
const clean = `
cc(" ignore ", {
"lint": { "lint": " ignore " },
}
)
`;
lint(noUnnecessaryWhitespace, {
invalid: [
{
jsx: dirty,
jsxOutput: clean,
svelte: `<script>${dirty}</script>`,
svelteOutput: `<script>${clean}</script>`,
vue: `<script>${dirty}</script>`,
vueOutput: `<script>${clean}</script>`,
errors: 4,
options: [{ selectors: [CC_OBJECT_KEYS] }]
}
]
});
});
});
================================================
FILE: src/options/callees/cc.ts
================================================
import { MatcherType, SelectorKind } from "better-tailwindcss:types/rule.js";
import type { CalleeSelector, Selectors } from "better-tailwindcss:types/rule.js";
export const CC_STRINGS = {
kind: SelectorKind.Callee,
match: [
{
type: MatcherType.String
}
],
name: "^cc$"
} satisfies CalleeSelector;
export const CC_OBJECT_KEYS = {
kind: SelectorKind.Callee,
match: [
{
type: MatcherType.ObjectKey
}
],
name: "^cc$"
} satisfies CalleeSelector;
/** @see https://github.com/jorgebucaran/classcat */
export const CC = [
CC_STRINGS,
CC_OBJECT_KEYS
] satisfies Selectors;
========================================
gitextract_rhr3h2e2/ ├── .cspell.json ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── documentation.yml │ │ ├── feature_request.yml │ │ └── question.yml │ └── workflows/ │ └── ci.yml ├── .gitignore ├── .markdownlint.jsonc ├── .vscode/ │ ├── extensions.json │ ├── launch.json │ └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── FUNDING.yml ├── LICENSE ├── README.md ├── build/ │ ├── index.ts │ └── utils.ts ├── changelog.config.js ├── docs/ │ ├── api/ │ │ └── defaults.md │ ├── configuration/ │ │ └── advanced.md │ ├── parsers/ │ │ ├── angular.md │ │ ├── astro.md │ │ ├── css.md │ │ ├── html.md │ │ ├── javascript.md │ │ ├── jsx.md │ │ ├── svelte.md │ │ ├── tsx.md │ │ ├── typescript.md │ │ └── vue.md │ ├── rules/ │ │ ├── enforce-canonical-classes.md │ │ ├── enforce-consistent-class-order.md │ │ ├── enforce-consistent-important-position.md │ │ ├── enforce-consistent-line-wrapping.md │ │ ├── enforce-consistent-variable-syntax.md │ │ ├── enforce-consistent-variant-order.md │ │ ├── enforce-logical-properties.md │ │ ├── enforce-shorthand-classes.md │ │ ├── no-conflicting-classes.md │ │ ├── no-deprecated-classes.md │ │ ├── no-duplicate-classes.md │ │ ├── no-restricted-classes.md │ │ ├── no-unknown-classes.md │ │ └── no-unnecessary-whitespace.md │ └── settings/ │ └── settings.md ├── eslint.config.ts ├── package.json ├── src/ │ ├── api/ │ │ ├── defaults.ts │ │ └── types.ts │ ├── async-utils/ │ │ ├── cache.ts │ │ ├── escape.ts │ │ ├── fs.ts │ │ ├── module.ts │ │ ├── operations.ts │ │ ├── order.ts │ │ ├── path.ts │ │ ├── platform.ts │ │ ├── regex.ts │ │ ├── resolvers.ts │ │ ├── tsconfig.ts │ │ └── worker.ts │ ├── configs/ │ │ ├── config.test.ts │ │ └── config.ts │ ├── options/ │ │ ├── callees/ │ │ │ ├── cc.test.ts │ │ │ ├── cc.ts │ │ │ ├── clb.test.ts │ │ │ ├── clb.ts │ │ │ ├── clsx.test.ts │ │ │ ├── clsx.ts │ │ │ ├── cn.test.ts │ │ │ ├── cn.ts │ │ │ ├── cnb.test.ts │ │ │ ├── cnb.ts │ │ │ ├── ctl.test.ts │ │ │ ├── ctl.ts │ │ │ ├── cva.test.ts │ │ │ ├── cva.ts │ │ │ ├── cx.test.ts │ │ │ ├── cx.ts │ │ │ ├── dcnb.test.ts │ │ │ ├── dcnb.ts │ │ │ ├── objstr.test.ts │ │ │ ├── objstr.ts │ │ │ ├── tv.test.ts │ │ │ ├── tv.ts │ │ │ ├── twJoin.test.ts │ │ │ ├── twJoin.ts │ │ │ ├── twMerge.test.ts │ │ │ └── twMerge.ts │ │ ├── default-options.test.ts │ │ ├── default-options.ts │ │ ├── descriptions.test.ts │ │ ├── descriptions.ts │ │ ├── migrate.test.ts │ │ ├── migrate.ts │ │ ├── schemas/ │ │ │ ├── attributes.ts │ │ │ ├── callees.ts │ │ │ ├── common.ts │ │ │ ├── matchers.ts │ │ │ ├── selectors.ts │ │ │ ├── tags.ts │ │ │ └── variables.ts │ │ └── tags/ │ │ ├── twc.test.ts │ │ ├── twc.ts │ │ ├── twx.test.ts │ │ └── twx.ts │ ├── parsers/ │ │ ├── angular.test.ts │ │ ├── angular.ts │ │ ├── css.test.ts │ │ ├── css.ts │ │ ├── es.test.ts │ │ ├── es.ts │ │ ├── html.test.ts │ │ ├── html.ts │ │ ├── jsx.test.ts │ │ ├── jsx.ts │ │ ├── svelte.test.ts │ │ ├── svelte.ts │ │ ├── vue.test.ts │ │ └── vue.ts │ ├── rules/ │ │ ├── enforce-canonical-classes.test.ts │ │ ├── enforce-canonical-classes.ts │ │ ├── enforce-consistent-class-order.test.ts │ │ ├── enforce-consistent-class-order.ts │ │ ├── enforce-consistent-important-position.test.ts │ │ ├── enforce-consistent-important-position.ts │ │ ├── enforce-consistent-line-wrapping.test.ts │ │ ├── enforce-consistent-line-wrapping.ts │ │ ├── enforce-consistent-variable-syntax.test.ts │ │ ├── enforce-consistent-variable-syntax.ts │ │ ├── enforce-consistent-variant-order.test.ts │ │ ├── enforce-consistent-variant-order.ts │ │ ├── enforce-logical-properties.test.ts │ │ ├── enforce-logical-properties.ts │ │ ├── enforce-shorthand-classes.test.ts │ │ ├── enforce-shorthand-classes.ts │ │ ├── no-conflicting-classes.test.ts │ │ ├── no-conflicting-classes.ts │ │ ├── no-deprecated-classes.test.ts │ │ ├── no-deprecated-classes.ts │ │ ├── no-duplicate-classes.test.ts │ │ ├── no-duplicate-classes.ts │ │ ├── no-restricted-classes.test.ts │ │ ├── no-restricted-classes.ts │ │ ├── no-unknown-classes.test.ts │ │ ├── no-unknown-classes.ts │ │ ├── no-unnecessary-whitespace.test.ts │ │ └── no-unnecessary-whitespace.ts │ ├── tailwindcss/ │ │ ├── canonical-classes.async.v4.ts │ │ ├── canonical-classes.ts │ │ ├── class-order.async.v3.ts │ │ ├── class-order.async.v4.ts │ │ ├── class-order.ts │ │ ├── conflicting-classes.async.v4.ts │ │ ├── conflicting-classes.ts │ │ ├── context.async.v3.ts │ │ ├── context.async.v4.ts │ │ ├── custom-component-classes.async.v3.ts │ │ ├── custom-component-classes.async.v4.ts │ │ ├── custom-component-classes.ts │ │ ├── dissect-classes.async.v3.ts │ │ ├── dissect-classes.async.v4.ts │ │ ├── dissect-classes.test.ts │ │ ├── dissect-classes.ts │ │ ├── prefix.async.v3.ts │ │ ├── prefix.async.v4.ts │ │ ├── prefix.ts │ │ ├── tailwind.async.worker.v3.ts │ │ ├── tailwind.async.worker.v4.ts │ │ ├── unknown-classes.async.v3.ts │ │ ├── unknown-classes.async.v4.ts │ │ ├── unknown-classes.ts │ │ ├── variant-order.async.v3.ts │ │ ├── variant-order.async.v4.ts │ │ └── variant-order.ts │ ├── types/ │ │ ├── ast.ts │ │ ├── async.ts │ │ ├── estree.ts │ │ └── rule.ts │ └── utils/ │ ├── ast.ts │ ├── class.ts │ ├── context.ts │ ├── lint.ts │ ├── matchers.test.ts │ ├── matchers.ts │ ├── quotes.test.ts │ ├── quotes.ts │ ├── rule.ts │ ├── selectors.ts │ ├── utils.test.ts │ ├── utils.ts │ ├── valibot.ts │ ├── version.ts │ └── warn.ts ├── tests/ │ ├── e2e/ │ │ ├── commonjs/ │ │ │ ├── eslint.config.js │ │ │ ├── package.json │ │ │ ├── test.html │ │ │ └── test.test.ts │ │ ├── eslintrc/ │ │ │ ├── .eslintrc.json │ │ │ ├── package.json │ │ │ ├── test.html │ │ │ └── test.test.ts │ │ └── esm/ │ │ ├── eslint.config.js │ │ ├── package.json │ │ ├── test.html │ │ └── test.test.ts │ ├── unit/ │ │ ├── monorepo-cwd-resolution.test.ts │ │ └── options.test.ts │ └── utils/ │ ├── context.ts │ ├── eslint.ts │ ├── lint.ts │ ├── prettier.ts │ ├── setup.ts │ ├── template.test.ts │ ├── template.ts │ ├── tmp.ts │ ├── values.ts │ └── version.ts ├── tsconfig.build.json ├── tsconfig.json └── vite.config.ts
SYMBOL INDEX (590 symbols across 111 files)
FILE: build/index.ts
function build (line 3) | async function build(){
FILE: build/utils.ts
function $ (line 3) | async function $(command: string, options?: ExecOptions): Promise<string...
FILE: src/api/defaults.ts
function getDefaultCallees (line 16) | function getDefaultCallees() {
function getDefaultAttributes (line 25) | function getDefaultAttributes() {
function getDefaultVariables (line 34) | function getDefaultVariables() {
function getDefaultTags (line 43) | function getDefaultTags() {
function getDefaultSelectors (line 49) | function getDefaultSelectors() {
FILE: src/async-utils/cache.ts
type CacheItem (line 4) | interface CacheItem {
constant CACHE (line 9) | const CACHE = new Map<string, CacheItem>();
function invalidateByModifiedDate (line 11) | function invalidateByModifiedDate(cache: CacheItem, path: string | undef...
function withCache (line 20) | function withCache<Result>(key: string, path: string | undefined, callba...
function clearCache (line 41) | function clearCache() {
FILE: src/async-utils/escape.ts
function escapeForRegex (line 4) | function escapeForRegex(word: string) {
FILE: src/async-utils/fs.ts
function findPathRecursive (line 5) | function findPathRecursive(cwd: string, startDirectory: string, entries:...
function getModifiedDate (line 28) | function getModifiedDate(filePath: string): Date {
FILE: src/async-utils/module.ts
function isCommonJSModule (line 1) | function isCommonJSModule() {
function isESModule (line 5) | function isESModule() {
FILE: src/async-utils/operations.ts
type Operations (line 12) | interface Operations {
type OperationHandlers (line 24) | type OperationHandlers = {
FILE: src/async-utils/order.ts
type VARIANT_ORDER_FLAGS (line 1) | enum VARIANT_ORDER_FLAGS {
FILE: src/async-utils/path.ts
function normalize (line 7) | function normalize(path: string): string {
FILE: src/async-utils/platform.ts
function isWindows (line 1) | function isWindows() {
FILE: src/async-utils/regex.ts
constant REGEX_CACHE (line 1) | const REGEX_CACHE = new Map<string, RegExp>();
constant MAX_CACHE_SIZE (line 2) | const MAX_CACHE_SIZE = 500;
function getRegexCacheKey (line 5) | function getRegexCacheKey(pattern: string, flags: string): string {
function getCachedRegex (line 11) | function getCachedRegex(patternOrRegex: RegExp | string, flags?: string)...
FILE: src/async-utils/resolvers.ts
function resolveJs (line 50) | function resolveJs(ctxOrPath: AsyncContext | string | undefined, pathOrC...
function resolveCss (line 64) | function resolveCss(ctxOrPath: AsyncContext | string | undefined, pathOr...
function resolveJson (line 76) | function resolveJson(path: string, cwd: string): string | undefined {
FILE: src/async-utils/tsconfig.ts
type GetTSConfigRequest (line 9) | interface GetTSConfigRequest {
type GetTSConfigResponse (line 14) | interface GetTSConfigResponse {
function getConfigPathWarning (line 37) | function getConfigPathWarning(configPath: string | undefined, foundConfi...
FILE: src/async-utils/worker.ts
function getWorkerOptions (line 10) | function getWorkerOptions(): SynckitOptions | undefined {
FILE: src/configs/config.ts
type ConfigName (line 21) | type ConfigName = "recommended" | RuleCategory;
type Severity (line 22) | type Severity = "error" | "warn";
type PluginRules (line 24) | type PluginRules = typeof rules[number];
type PluginName (line 26) | type PluginName = typeof plugin.meta.name;
type RuleObject (line 28) | type RuleObject = {
type GetRules (line 32) | type GetRules<Category extends RuleCategory> = Extract<
FILE: src/options/callees/cc.ts
constant CC_STRINGS (line 6) | const CC_STRINGS = {
constant CC_OBJECT_KEYS (line 16) | const CC_OBJECT_KEYS = {
FILE: src/options/callees/clb.ts
constant CLB_BASE_VALUES (line 6) | const CLB_BASE_VALUES = {
constant CLB_VARIANT_VALUES (line 17) | const CLB_VARIANT_VALUES = {
constant CLB_COMPOUND_VARIANTS_CLASSES (line 28) | const CLB_COMPOUND_VARIANTS_CLASSES = {
constant CLB (line 40) | const CLB = [
FILE: src/options/callees/clsx.ts
constant CLSX_STRINGS (line 6) | const CLSX_STRINGS = {
constant CLSX_OBJECT_KEYS (line 16) | const CLSX_OBJECT_KEYS = {
constant CLSX (line 27) | const CLSX = [
FILE: src/options/callees/cn.ts
constant CN_STRINGS (line 6) | const CN_STRINGS = {
constant CN_OBJECT_KEYS (line 16) | const CN_OBJECT_KEYS = {
FILE: src/options/callees/cnb.ts
constant CNB_STRINGS (line 6) | const CNB_STRINGS = {
constant CNB_OBJECT_KEYS (line 16) | const CNB_OBJECT_KEYS = {
constant CNB (line 27) | const CNB = [
FILE: src/options/callees/ctl.ts
constant CTL_STRINGS (line 6) | const CTL_STRINGS = {
constant CTL (line 17) | const CTL = [
FILE: src/options/callees/cva.ts
constant CVA_STRINGS (line 6) | const CVA_STRINGS = {
constant CVA_VARIANT_VALUES (line 16) | const CVA_VARIANT_VALUES = {
constant CVA_COMPOUND_VARIANTS_CLASS (line 27) | const CVA_COMPOUND_VARIANTS_CLASS = {
constant CVA (line 39) | const CVA = [
FILE: src/options/callees/cx.ts
constant CX_STRINGS (line 6) | const CX_STRINGS = {
constant CX_OBJECT_KEYS (line 16) | const CX_OBJECT_KEYS = {
FILE: src/options/callees/dcnb.ts
constant DCNB_STRINGS (line 6) | const DCNB_STRINGS = {
constant DCNB_OBJECT_KEYS (line 16) | const DCNB_OBJECT_KEYS = {
constant DCNB (line 27) | const DCNB = [
FILE: src/options/callees/objstr.ts
constant OBJSTR_STRINGS (line 6) | const OBJSTR_STRINGS = {
constant OBJSTR_OBJECT_KEYS (line 16) | const OBJSTR_OBJECT_KEYS = {
constant OBJSTR (line 27) | const OBJSTR = [
FILE: src/options/callees/tv.ts
constant TV_STRINGS (line 6) | const TV_STRINGS = {
constant TV_VARIANT_VALUES (line 16) | const TV_VARIANT_VALUES = {
constant TV_BASE_VALUES (line 27) | const TV_BASE_VALUES = {
constant TV_SLOTS_VALUES (line 38) | const TV_SLOTS_VALUES = {
constant TV_COMPOUND_VARIANTS_CLASS (line 49) | const TV_COMPOUND_VARIANTS_CLASS = {
constant TV_COMPOUND_SLOTS_CLASS (line 60) | const TV_COMPOUND_SLOTS_CLASS = {
FILE: src/options/callees/twJoin.ts
constant TW_JOIN_STRINGS (line 6) | const TW_JOIN_STRINGS = {
constant TW_JOIN (line 17) | const TW_JOIN = [
FILE: src/options/callees/twMerge.ts
constant TW_MERGE_STRINGS (line 6) | const TW_MERGE_STRINGS = {
constant TW_MERGE (line 17) | const TW_MERGE = [
FILE: src/options/default-options.ts
constant DEFAULT_CALLEE_SELECTORS (line 22) | const DEFAULT_CALLEE_SELECTORS = [
constant DEFAULT_ATTRIBUTE_SELECTORS (line 38) | const DEFAULT_ATTRIBUTE_SELECTORS = [
constant DEFAULT_VARIABLE_NAMES (line 121) | const DEFAULT_VARIABLE_NAMES = [
constant DEFAULT_VARIABLE_SELECTORS (line 145) | const DEFAULT_VARIABLE_SELECTORS = [
constant DEFAULT_TAG_SELECTORS (line 175) | const DEFAULT_TAG_SELECTORS = [
constant DEFAULT_SELECTORS (line 180) | const DEFAULT_SELECTORS = [
FILE: src/options/descriptions.ts
constant COMMON_OPTIONS (line 21) | const COMMON_OPTIONS = strictObject({
type CommonOptions (line 36) | type CommonOptions = InferOutput<typeof COMMON_OPTIONS>;
FILE: src/options/migrate.ts
type LegacySelector (line 10) | type LegacySelector = Attributes[number] | Callees[number] | Tags[number...
type LegacySelectorsByKind (line 12) | type LegacySelectorsByKind = {
function migrateLegacySelectorsToFlatSelectors (line 19) | function migrateLegacySelectorsToFlatSelectors(legacy: LegacySelectorsBy...
function migrateFlatSelectorsToLegacySelectors (line 46) | function migrateFlatSelectorsToLegacySelectors(selectors: Selectors): Le...
function hasLegacySelectorConfig (line 73) | function hasLegacySelectorConfig(options: LegacySelectorsByKind): boolean {
function toSelectorMatcher (line 82) | function toSelectorMatcher(matcher: Matcher): SelectorMatcher {
function toLegacyMatcher (line 97) | function toLegacyMatcher(matcher: SelectorMatcher): Matcher | undefined {
function migrateLegacySelector (line 116) | function migrateLegacySelector(selector: LegacySelector, kind: SelectorK...
function migrateFlatSelector (line 137) | function migrateFlatSelector(selector: Selector): LegacySelector | undef...
FILE: src/options/schemas/attributes.ts
constant ATTRIBUTE_MATCHER_CONFIG (line 21) | const ATTRIBUTE_MATCHER_CONFIG = pipe(
type AttributeMatchers (line 41) | type AttributeMatchers = InferOutput<typeof ATTRIBUTE_MATCHER_CONFIG>;
constant ATTRIBUTE_NAME_CONFIG (line 43) | const ATTRIBUTE_NAME_CONFIG = pipe(
type AttributeName (line 48) | type AttributeName = InferOutput<typeof ATTRIBUTE_NAME_CONFIG>;
constant ATTRIBUTES_SCHEMA (line 50) | const ATTRIBUTES_SCHEMA = pipe(
type Attributes (line 60) | type Attributes = InferOutput<typeof ATTRIBUTES_SCHEMA>;
constant ATTRIBUTES_OPTION_SCHEMA (line 62) | const ATTRIBUTES_OPTION_SCHEMA = strictObject({
type AttributesOptions (line 66) | type AttributesOptions = InferOutput<typeof ATTRIBUTES_OPTION_SCHEMA>;
FILE: src/options/schemas/callees.ts
constant CALLEE_MATCHER_SCHEMA (line 21) | const CALLEE_MATCHER_SCHEMA = pipe(
type CalleeMatchers (line 41) | type CalleeMatchers = InferOutput<typeof CALLEE_MATCHER_SCHEMA>;
constant CALLEE_NAME_SCHEMA (line 43) | const CALLEE_NAME_SCHEMA = pipe(
type CalleeName (line 48) | type CalleeName = InferOutput<typeof CALLEE_NAME_SCHEMA>;
constant CALLEES_SCHEMA (line 50) | const CALLEES_SCHEMA = pipe(
type Callees (line 60) | type Callees = InferOutput<typeof CALLEES_SCHEMA>;
constant CALLEES_OPTION_SCHEMA (line 62) | const CALLEES_OPTION_SCHEMA = strictObject({
type CalleesOptions (line 66) | type CalleesOptions = InferOutput<typeof CALLEES_OPTION_SCHEMA>;
FILE: src/options/schemas/common.ts
constant ENTRYPOINT_OPTION_SCHEMA (line 18) | const ENTRYPOINT_OPTION_SCHEMA = strictObject({
type EntryPointOption (line 28) | type EntryPointOption = InferOutput<typeof ENTRYPOINT_OPTION_SCHEMA>;
constant TAILWIND_OPTION_SCHEMA (line 30) | const TAILWIND_OPTION_SCHEMA = strictObject({
type TailwindConfigOption (line 40) | type TailwindConfigOption = InferOutput<typeof TAILWIND_OPTION_SCHEMA>;
constant TSCONFIG_OPTION_SCHEMA (line 42) | const TSCONFIG_OPTION_SCHEMA = strictObject({
type TSConfigOption (line 52) | type TSConfigOption = InferOutput<typeof TSCONFIG_OPTION_SCHEMA>;
constant MESSAGE_STYLE_OPTION_SCHEMA (line 54) | const MESSAGE_STYLE_OPTION_SCHEMA = strictObject({
type MessageStyleOption (line 70) | type MessageStyleOption = InferOutput<typeof MESSAGE_STYLE_OPTION_SCHEMA>;
constant DETECT_COMPONENT_CLASSES_OPTION_SCHEMA (line 72) | const DETECT_COMPONENT_CLASSES_OPTION_SCHEMA = strictObject({
type DetectComponentClassesOption (line 82) | type DetectComponentClassesOption = InferOutput<typeof DETECT_COMPONENT_...
constant ROOT_FONT_SIZE_OPTION_SCHEMA (line 84) | const ROOT_FONT_SIZE_OPTION_SCHEMA = strictObject({
type RootFontSizeOption (line 94) | type RootFontSizeOption = InferOutput<typeof ROOT_FONT_SIZE_OPTION_SCHEMA>;
constant CWD_OPTION_SCHEMA (line 96) | const CWD_OPTION_SCHEMA = strictObject({
type CwdOption (line 106) | type CwdOption = InferOutput<typeof CWD_OPTION_SCHEMA>;
FILE: src/options/schemas/matchers.ts
constant STRING_MATCHER_SCHEMA (line 6) | const STRING_MATCHER_SCHEMA = strictObject({
constant OBJECT_KEY_MATCHER_SCHEMA (line 13) | const OBJECT_KEY_MATCHER_SCHEMA = strictObject({
constant OBJECT_VALUE_MATCHER_SCHEMA (line 24) | const OBJECT_VALUE_MATCHER_SCHEMA = strictObject({
FILE: src/options/schemas/selectors.ts
constant STRING_SELECTOR_MATCHER_SCHEMA (line 19) | const STRING_SELECTOR_MATCHER_SCHEMA = strictObject({
constant OBJECT_KEY_SELECTOR_MATCHER_SCHEMA (line 26) | const OBJECT_KEY_SELECTOR_MATCHER_SCHEMA = strictObject({
constant OBJECT_VALUE_SELECTOR_MATCHER_SCHEMA (line 37) | const OBJECT_VALUE_SELECTOR_MATCHER_SCHEMA = strictObject({
constant ANONYMOUS_FUNCTION_RETURN_SELECTOR_MATCHER_SCHEMA (line 48) | const ANONYMOUS_FUNCTION_RETURN_SELECTOR_MATCHER_SCHEMA = strictObject({
constant SELECTOR_MATCH_SCHEMA (line 64) | const SELECTOR_MATCH_SCHEMA = pipe(
constant SELECTOR_NAME_SCHEMA (line 78) | const SELECTOR_NAME_SCHEMA = pipe(
constant CALLEE_SELECTOR_PATH_SCHEMA (line 83) | const CALLEE_SELECTOR_PATH_SCHEMA = pipe(
constant TAG_SELECTOR_PATH_SCHEMA (line 88) | const TAG_SELECTOR_PATH_SCHEMA = pipe(
constant CALLEE_SELECTOR_TARGET_VALUE_SCHEMA (line 93) | const CALLEE_SELECTOR_TARGET_VALUE_SCHEMA = optional(
constant CALLEE_SELECTOR_TARGET_ARGUMENT_SCHEMA (line 102) | const CALLEE_SELECTOR_TARGET_ARGUMENT_SCHEMA = pipe(
constant CALLEE_SELECTOR_TARGET_CALL_SCHEMA (line 107) | const CALLEE_SELECTOR_TARGET_CALL_SCHEMA = pipe(
constant CALLEE_SELECTOR_LEGACY_CALL_TARGET_SCHEMA (line 112) | const CALLEE_SELECTOR_LEGACY_CALL_TARGET_SCHEMA = pipe(
constant ATTRIBUTE_SELECTOR_SCHEMA (line 124) | const ATTRIBUTE_SELECTOR_SCHEMA = strictObject({
constant CALLEE_SELECTOR_SCHEMA (line 133) | const CALLEE_SELECTOR_SCHEMA = union([
constant TAG_SELECTOR_SCHEMA (line 160) | const TAG_SELECTOR_SCHEMA = union([
constant VARIABLE_SELECTOR_SCHEMA (line 181) | const VARIABLE_SELECTOR_SCHEMA = strictObject({
constant SELECTOR_SCHEMA (line 190) | const SELECTOR_SCHEMA = union([
type Selector (line 197) | type Selector = InferOutput<typeof SELECTOR_SCHEMA>;
constant SELECTORS_SCHEMA (line 199) | const SELECTORS_SCHEMA = pipe(
type Selectors (line 204) | type Selectors = InferOutput<typeof SELECTORS_SCHEMA>;
constant SELECTORS_OPTION_SCHEMA (line 206) | const SELECTORS_OPTION_SCHEMA = strictObject({
type SelectorsOptions (line 210) | type SelectorsOptions = InferOutput<typeof SELECTORS_OPTION_SCHEMA>;
FILE: src/options/schemas/tags.ts
constant TAG_MATCHER_CONFIG (line 21) | const TAG_MATCHER_CONFIG = pipe(
type TagMatchers (line 41) | type TagMatchers = InferOutput<typeof TAG_MATCHER_CONFIG>;
constant TAG_NAME_CONFIG (line 43) | const TAG_NAME_CONFIG = pipe(
type TagName (line 48) | type TagName = InferOutput<typeof TAG_NAME_CONFIG>;
constant TAGS_SCHEMA (line 50) | const TAGS_SCHEMA = pipe(
type Tags (line 60) | type Tags = InferOutput<typeof TAGS_SCHEMA>;
constant TAGS_OPTIONS_SCHEMA (line 62) | const TAGS_OPTIONS_SCHEMA = strictObject({
type TagsOptions (line 66) | type TagsOptions = InferOutput<typeof TAGS_OPTIONS_SCHEMA>;
FILE: src/options/schemas/variables.ts
constant VARIABLE_MATCHER_CONFIG (line 21) | const VARIABLE_MATCHER_CONFIG = pipe(
type VariableMatchers (line 41) | type VariableMatchers = InferOutput<typeof VARIABLE_MATCHER_CONFIG>;
constant VARIABLE_NAME_CONFIG (line 43) | const VARIABLE_NAME_CONFIG = pipe(
type VariableName (line 48) | type VariableName = InferOutput<typeof VARIABLE_NAME_CONFIG>;
constant VARIABLES_SCHEMA (line 50) | const VARIABLES_SCHEMA = pipe(
type Variables (line 60) | type Variables = InferOutput<typeof VARIABLES_SCHEMA>;
constant VARIABLES_OPTION_SCHEMA (line 62) | const VARIABLES_OPTION_SCHEMA = strictObject({
type VariablesOptions (line 66) | type VariablesOptions = InferOutput<typeof VARIABLES_OPTION_SCHEMA>;
FILE: src/options/tags/twc.ts
constant TWC_TAG (line 6) | const TWC_TAG = {
constant TWC_CALLEE_STRINGS (line 11) | const TWC_CALLEE_STRINGS = {
constant TWC (line 28) | const TWC = [
FILE: src/options/tags/twx.ts
constant TWX_TAG (line 6) | const TWX_TAG = {
constant TWX_CALLEE_STRINGS (line 11) | const TWX_CALLEE_STRINGS = {
constant TWX (line 28) | const TWX = [
FILE: src/parsers/angular.ts
function getAttributesByAngularElement (line 42) | function getAttributesByAngularElement(ctx: Rule.RuleContext, node: Tmpl...
function getLiteralsByAngularAttribute (line 49) | function getLiteralsByAngularAttribute(ctx: Rule.RuleContext, attribute:...
function createLiteralsByAngularAst (line 80) | function createLiteralsByAngularAst(ctx: Rule.RuleContext, ast: AST): Li...
function createLiteralsByAngularConditional (line 116) | function createLiteralsByAngularConditional(ctx: Rule.RuleContext, condi...
function createLiteralsByAngularAttribute (line 125) | function createLiteralsByAngularAttribute(ctx: Rule.RuleContext, attribu...
function getLiteralsByAngularMatchers (line 135) | function getLiteralsByAngularMatchers(ctx: Rule.RuleContext, ast: AST | ...
function getAngularMatcherFunctions (line 144) | function getAngularMatcherFunctions(ctx: Rule.RuleContext, matchers: Sel...
function getAngularObjectPath (line 240) | function getAngularObjectPath(ctx: Rule.RuleContext, ast: AST): string |...
function createLiteralsByAngularBoundAttributeName (line 285) | function createLiteralsByAngularBoundAttributeName(ctx: Rule.RuleContext...
function createLiteralByLiteralMapKey (line 328) | function createLiteralByLiteralMapKey(ctx: Rule.RuleContext, key: Litera...
function createLiteralsByAngularTextAttribute (line 381) | function createLiteralsByAngularTextAttribute(ctx: Rule.RuleContext, att...
function createLiteralByAngularLiteralPrimitive (line 417) | function createLiteralByAngularLiteralPrimitive(ctx: Rule.RuleContext, l...
function createLiteralByAngularTemplateLiteralElement (line 450) | function createLiteralByAngularTemplateLiteralElement(ctx: Rule.RuleCont...
function convertParseSourceSpanToLoc (line 493) | function convertParseSourceSpanToLoc(sourceSpan: ParseSourceSpan): Sourc...
function isInsideInlineTemplate (line 506) | function isInsideInlineTemplate(ctx: Rule.RuleContext) {
function getInlineTemplateComponentIndex (line 510) | function getInlineTemplateComponentIndex(ctx: Rule.RuleContext) {
function getBraces (line 519) | function getBraces(literal: TemplateLiteralElement): BracesMeta {
function getIsInterpolated (line 537) | function getIsInterpolated(literal: TemplateLiteralElement): boolean {
function getAttributeName (line 542) | function getAttributeName(node: TmplAstBoundAttribute | TmplAstTextAttri...
type Parent (line 552) | type Parent = {
function isInsideConditionalExpressionCondition (line 556) | function isInsideConditionalExpressionCondition(ctx: Rule.RuleContext, a...
function isInsideLogicalExpressionLeft (line 567) | function isInsideLogicalExpressionLeft(ctx: Rule.RuleContext, ast: AST):...
function getStringConcatenationMeta (line 578) | function getStringConcatenationMeta(ctx: Rule.RuleContext, ast: AST, isC...
function isInsideObjectValue (line 599) | function isInsideObjectValue(ctx: Rule.RuleContext, ast: AST): boolean {
function isStringLike (line 617) | function isStringLike(ast: AST): ast is LiteralPrimitive | TemplateLiter...
function hasParent (line 621) | function hasParent(ast: AST): ast is AST & Parent {
function findParent (line 633) | function findParent(ctx: Rule.RuleContext, astNode: AST): AST | undefined {
function isBoundAttributeName (line 665) | function isBoundAttributeName(ast: AST | TmplAstBoundAttribute | TmplAst...
function isObjectValue (line 669) | function isObjectValue(ast: AST): ast is LiteralPrimitive {
function isObjectKey (line 673) | function isObjectKey(ast: Record<string, any>): ast is LiteralMapPropert...
function isStringLiteral (line 677) | function isStringLiteral(ast: AST): ast is LiteralPrimitive {
function isAST (line 681) | function isAST(ast: unknown): ast is AST {
function is (line 685) | function is<Type extends AST | TmplAstNode>(ast: AST | TmplAstNode, type...
FILE: src/parsers/css.ts
function getLiteralsByCSSAtRule (line 9) | function getLiteralsByCSSAtRule(ctx: Rule.RuleContext, node: Atrule): Li...
function getLiteralsByAtrule (line 26) | function getLiteralsByAtrule(ctx: Rule.RuleContext, node: Atrule): CSSCl...
function getLoc (line 70) | function getLoc(ctx: Rule.RuleContext, node: Atrule, startOffset: number...
function getRange (line 87) | function getRange(ctx: Rule.RuleContext, node: Atrule, startOffset: numb...
FILE: src/parsers/es.ts
constant ES_CONTAINER_TYPES_TO_REPLACE_QUOTES (line 63) | const ES_CONTAINER_TYPES_TO_REPLACE_QUOTES: string[] = [
constant ES_CONTAINER_TYPES_TO_INSERT_BRACES (line 72) | const ES_CONTAINER_TYPES_TO_INSERT_BRACES: string[] = [
function getLiteralsByESVariableDeclarator (line 76) | function getLiteralsByESVariableDeclarator(ctx: Rule.RuleContext, node: ...
function getLiteralsByESExportDefaultDeclaration (line 102) | function getLiteralsByESExportDefaultDeclaration(ctx: Rule.RuleContext, ...
function getLiteralsByESCallExpression (line 127) | function getLiteralsByESCallExpression(ctx: Rule.RuleContext, node: ESCa...
function getLiteralsByTaggedTemplateExpression (line 173) | function getLiteralsByTaggedTemplateExpression(ctx: Rule.RuleContext, no...
function getLiteralsByESBareTemplateLiteral (line 203) | function getLiteralsByESBareTemplateLiteral(ctx: Rule.RuleContext, node:...
function getLiteralsByESLiteralNode (line 224) | function getLiteralsByESLiteralNode(ctx: Rule.RuleContext, node: ESBaseN...
function getLiteralsByESMatchers (line 245) | function getLiteralsByESMatchers(ctx: Rule.RuleContext, node: ESBaseNode...
function getStringLiteralByESStringLiteral (line 253) | function getStringLiteralByESStringLiteral(ctx: Rule.RuleContext, node: ...
function getLiteralByESTemplateElement (line 290) | function getLiteralByESTemplateElement(ctx: Rule.RuleContext, node: ESTe...
function getMultilineQuotes (line 329) | function getMultilineQuotes(node: ESNode & Rule.NodeParentExtension): Mu...
function getLiteralsByESExpression (line 341) | function getLiteralsByESExpression(ctx: Rule.RuleContext, args: ESExpres...
function getLiteralsByESTemplateLiteral (line 348) | function getLiteralsByESTemplateLiteral(ctx: Rule.RuleContext, node: EST...
function findParentESTemplateLiteralByESTemplateElement (line 357) | function findParentESTemplateLiteralByESTemplateElement(node: WithParent...
function findPriorLiterals (line 363) | function findPriorLiterals(ctx: Rule.RuleContext, node: ESNode) {
function getESObjectPath (line 424) | function getESObjectPath(node: WithParent<ESNode>): string | undefined {
type ESSimpleStringLiteral (line 490) | interface ESSimpleStringLiteral extends Rule.NodeParentExtension, ESSimp...
function isESObjectKey (line 494) | function isESObjectKey(node: ESBaseNode & Rule.NodeParentExtension) {
function isInsideObjectValue (line 502) | function isInsideObjectValue(node: WithParent<ESNode>) {
function findMatchingParentNodes (line 522) | function findMatchingParentNodes<Node>(node: Partial<GenericNodeWithPare...
function isESSimpleStringLiteral (line 532) | function isESSimpleStringLiteral(node: ESBaseNode): node is ESSimpleStri...
function isESStringLike (line 540) | function isESStringLike(node: ESBaseNode): node is ESSimpleStringLiteral...
function isESTemplateLiteral (line 544) | function isESTemplateLiteral(node: ESBaseNode): node is ESTemplateLiteral {
function isESTemplateElement (line 548) | function isESTemplateElement(node: ESBaseNode): node is ESTemplateElement {
function isESNode (line 552) | function isESNode(node: unknown): node is ESNode {
function isESCallExpression (line 560) | function isESCallExpression(node: ESBaseNode): node is ESCallExpression {
function isESArrowFunctionExpression (line 564) | function isESArrowFunctionExpression(node: ESBaseNode): node is ESArrowF...
function isESFunctionExpression (line 568) | function isESFunctionExpression(node: ESBaseNode): node is ESFunctionExp...
function isESFunctionDeclaration (line 572) | function isESFunctionDeclaration(node: ESBaseNode): node is ESFunctionDe...
function isESAnonymousFunction (line 576) | function isESAnonymousFunction(node: ESBaseNode): boolean {
function isESArrowFunctionWithoutBody (line 588) | function isESArrowFunctionWithoutBody(node: ESBaseNode): node is ESArrow...
function isESReturnStatement (line 592) | function isESReturnStatement(node: ESNode): boolean {
function getESMemberExpressionPropertyName (line 596) | function getESMemberExpressionPropertyName(node: ESMemberExpression): st...
function getESCalleeName (line 606) | function getESCalleeName(node: ESBaseNode, type: "name" | "path"): strin...
function getTaggedTemplateName (line 641) | function getTaggedTemplateName(node: ESBaseNode & Partial<Rule.NodeParen...
function isNestedCurriedCall (line 660) | function isNestedCurriedCall(node: ESCallExpression): boolean {
function getCurriedCallChain (line 664) | function getCurriedCallChain(node: ESCallExpression) {
function getTargetCalls (line 676) | function getTargetCalls(callChain: ESCallExpression[], callTarget: CallT...
function getTargetArguments (line 680) | function getTargetArguments(args: (ESExpression | ESSpreadElement)[], ar...
function getTargetItems (line 708) | function getTargetItems<T>(items: T[], target: CallTarget | undefined, d...
function isTaggedTemplateExpression (line 736) | function isTaggedTemplateExpression(node: ESBaseNode): node is ESTaggedT...
function isTaggedTemplateLiteral (line 740) | function isTaggedTemplateLiteral(node: ESBaseNode): node is ESTemplateLi...
function isESVariableDeclarator (line 744) | function isESVariableDeclarator(node: ESBaseNode): node is ESVariableDec...
function isESExportDefaultExpression (line 748) | function isESExportDefaultExpression(node: ESBaseNode): node is ESExpres...
function isESVariableSymbol (line 760) | function isESVariableSymbol(node: ESBaseNode & Partial<Rule.NodeParentEx...
function hasESNodeParentExtension (line 764) | function hasESNodeParentExtension(node: ESBaseNode): node is Rule.Node &...
function getBracesByString (line 768) | function getBracesByString(ctx: Rule.RuleContext, raw: string): BracesMe...
function getIsInterpolated (line 778) | function getIsInterpolated(ctx: Rule.RuleContext, raw: string): boolean {
function getLeadingComment (line 783) | function getLeadingComment(ctx: Rule.RuleContext, node: ESNode): string ...
type ESTokenWithOptionalComment (line 793) | type ESTokenWithOptionalComment = {
function isESTokenComment (line 798) | function isESTokenComment(token: ESTokenWithOptionalComment): token is E...
function getStringConcatenationMeta (line 802) | function getStringConcatenationMeta(node: ESNode, isConcatenatedLeft = f...
function getESMatcherFunctions (line 824) | function getESMatcherFunctions(
FILE: src/parsers/html.ts
function getLiteralsByHTMLAttribute (line 16) | function getLiteralsByHTMLAttribute(ctx: Rule.RuleContext, attribute: At...
function getAttributesByHTMLTag (line 36) | function getAttributesByHTMLTag(ctx: Rule.RuleContext, node: TagNode): A...
function getLiteralsByHTMLAttributeNode (line 40) | function getLiteralsByHTMLAttributeNode(ctx: Rule.RuleContext, attribute...
function getQuotesByHTMLAttribute (line 70) | function getQuotesByHTMLAttribute(ctx: Rule.RuleContext, attribute: Attr...
FILE: src/parsers/jsx.ts
constant JSX_CONTAINER_TYPES_TO_REPLACE_QUOTES (line 23) | const JSX_CONTAINER_TYPES_TO_REPLACE_QUOTES = [
constant JSX_CONTAINER_TYPES_TO_INSERT_BRACES (line 28) | const JSX_CONTAINER_TYPES_TO_INSERT_BRACES = [
function getLiteralsByJSXAttribute (line 33) | function getLiteralsByJSXAttribute(ctx: Rule.RuleContext, attribute: JSX...
function getAttributesByJSXElement (line 62) | function getAttributesByJSXElement(ctx: Rule.RuleContext, node: JSXOpeni...
function getAttributeName (line 71) | function getAttributeName(attribute: JSXAttribute): string | undefined {
function getLiteralsByJSXAttributeValue (line 80) | function getLiteralsByJSXAttributeValue(ctx: Rule.RuleContext, value: JS...
function getStringLiteralByJSXStringLiteral (line 108) | function getStringLiteralByJSXStringLiteral(ctx: Rule.RuleContext, node:...
function getLiteralsByJSXTemplateLiteral (line 122) | function getLiteralsByJSXTemplateLiteral(ctx: Rule.RuleContext, node: ES...
function getMultilineQuotes (line 139) | function getMultilineQuotes(node: ESBaseNode & Rule.NodeParentExtension)...
function isJSXExpressionContainerWithESSimpleStringLiteral (line 151) | function isJSXExpressionContainerWithESSimpleStringLiteral(node: JSXBase...
function isJSXExpressionContainerWithESTemplateLiteral (line 157) | function isJSXExpressionContainerWithESTemplateLiteral(node: JSXBaseNode...
function isJSXAttribute (line 163) | function isJSXAttribute(node: JSXBaseNode): node is JSXAttribute {
FILE: src/parsers/svelte.ts
constant SVELTE_CONTAINER_TYPES_TO_REPLACE_QUOTES (line 46) | const SVELTE_CONTAINER_TYPES_TO_REPLACE_QUOTES = [
constant SVELTE_CONTAINER_TYPES_TO_INSERT_BRACES (line 51) | const SVELTE_CONTAINER_TYPES_TO_INSERT_BRACES: string[] = [
function getAttributesBySvelteTag (line 55) | function getAttributesBySvelteTag(ctx: Rule.RuleContext, node: SvelteSta...
function getDirectivesBySvelteTag (line 64) | function getDirectivesBySvelteTag(ctx: Rule.RuleContext, node: SvelteSta...
function getLiteralsBySvelteAttribute (line 73) | function getLiteralsBySvelteAttribute(ctx: Rule.RuleContext, attribute: ...
function getLiteralsBySvelteDirective (line 104) | function getLiteralsBySvelteDirective(ctx: Rule.RuleContext, directive: ...
function getLiteralsBySvelteMatchers (line 131) | function getLiteralsBySvelteMatchers(ctx: Rule.RuleContext, node: ESBase...
function getLiteralsBySvelteLiteralNode (line 140) | function getLiteralsBySvelteLiteralNode(ctx: Rule.RuleContext, node: ESB...
function getLiteralsBySvelteESLiteralNode (line 170) | function getLiteralsBySvelteESLiteralNode(ctx: Rule.RuleContext, node: E...
function getStringLiteralBySvelteName (line 185) | function getStringLiteralBySvelteName(ctx: Rule.RuleContext, node: Svelt...
function getStringLiteralBySvelteStringLiteral (line 215) | function getStringLiteralBySvelteStringLiteral(ctx: Rule.RuleContext, no...
function getBracesByString (line 245) | function getBracesByString(ctx: Rule.RuleContext, raw: string): BracesMe...
function getIsInterpolated (line 257) | function getIsInterpolated(ctx: Rule.RuleContext, raw: string): boolean {
function getMultilineQuotes (line 262) | function getMultilineQuotes(node: ESBaseNode & Rule.NodeParentExtension ...
function isSvelteAttribute (line 274) | function isSvelteAttribute(node:
function isSvelteDirective (line 286) | function isSvelteDirective(node: ESBaseNode): node is SvelteDirective {
function isSvelteStringLiteral (line 290) | function isSvelteStringLiteral(node: ESBaseNode): node is SvelteLiteral {
function isSvelteName (line 294) | function isSvelteName(node: ESBaseNode): node is SvelteName {
function isSvelteMustacheTag (line 298) | function isSvelteMustacheTag(node: ESBaseNode): node is SvelteMustacheTa...
function getSvelteMatcherFunctions (line 303) | function getSvelteMatcherFunctions(matchers: SelectorMatcher[]): Matcher...
FILE: src/parsers/vue.ts
constant VUE_CONTAINER_TYPES_TO_REPLACE_QUOTES (line 28) | const VUE_CONTAINER_TYPES_TO_REPLACE_QUOTES = [
constant VUE_CONTAINER_TYPES_TO_INSERT_BRACES (line 32) | const VUE_CONTAINER_TYPES_TO_INSERT_BRACES = [
function getAttributesByVueStartTag (line 37) | function getAttributesByVueStartTag(ctx: Rule.RuleContext, node: AST.VSt...
function getLiteralsByVueAttribute (line 41) | function getLiteralsByVueAttribute(ctx: Rule.RuleContext, attribute: AST...
function getLiteralsByVueLiteralNode (line 69) | function getLiteralsByVueLiteralNode(ctx: Rule.RuleContext, node: ESBase...
function getLiteralsByVueMatchers (line 86) | function getLiteralsByVueMatchers(ctx: Rule.RuleContext, node: ESBaseNod...
function getLiteralsByVueESLiteralNode (line 95) | function getLiteralsByVueESLiteralNode(ctx: Rule.RuleContext, node: ESBa...
function getStringLiteralByVueStringLiteral (line 108) | function getStringLiteralByVueStringLiteral(ctx: Rule.RuleContext, node:...
function getMultilineQuotes (line 135) | function getMultilineQuotes(node: ESBaseNode & Rule.NodeParentExtension ...
function getVueBoundName (line 147) | function getVueBoundName(name: string): string {
function getVueAttributeName (line 151) | function getVueAttributeName(attribute: AST.VAttribute | AST.VDirective)...
function isVueAttribute (line 163) | function isVueAttribute(attribute: AST.VAttribute | AST.VDirective): att...
function isVueDirective (line 167) | function isVueDirective(attribute: AST.VAttribute | AST.VDirective): att...
function isVueLiteralNode (line 171) | function isVueLiteralNode(node: ESBaseNode): node is AST.VLiteral {
function getVueMatcherFunctions (line 175) | function getVueMatcherFunctions(matchers: SelectorMatcher[]): MatcherFun...
FILE: src/rules/enforce-canonical-classes.ts
function lintLiterals (line 68) | function lintLiterals(ctx: Context<typeof enforceCanonicalClasses>, lite...
FILE: src/rules/enforce-consistent-class-order.ts
function sortClassNames (line 170) | function sortClassNames(ctx: Context<typeof enforceConsistentClassOrder>...
type VariantMap (line 274) | type VariantMap = {
function getStrictOrder (line 281) | function getStrictOrder(variantMap: VariantMap): string[] {
function getCustomOrder (line 329) | function getCustomOrder(position: "end" | "start", order: "asc" | "desc"...
function compareClasses (line 364) | function compareClasses(classA: string, classB: string): number {
function isArbitrary (line 369) | function isArbitrary(variant?: string): boolean {
FILE: src/rules/enforce-consistent-important-position.ts
method lintLiterals (line 39) | lintLiterals(ctx, literals) {
FILE: src/rules/enforce-consistent-line-wrapping.ts
type Meta (line 24) | interface Meta extends QuoteMeta, BracesMeta, WhitespaceMeta {
function lintLiterals (line 125) | function lintLiterals(ctx: Context<typeof enforceConsistentLineWrapping>...
function getIndentation (line 517) | function getIndentation(ctx: Context<typeof enforceConsistentLineWrappin...
class Lines (line 523) | class Lines {
method constructor (line 530) | constructor(ctx: Context<typeof enforceConsistentLineWrapping>, indent...
method at (line 537) | public at(index: number) {
method line (line 543) | public get line() {
method length (line 547) | public get length() {
method addLine (line 551) | public addLine() {
method toString (line 558) | public toString() {
class Line (line 567) | class Line {
method constructor (line 574) | constructor(ctx: Context<typeof enforceConsistentLineWrapping>, indent...
method indent (line 579) | public indent(start: number = this.indentation) {
method length (line 591) | public get length() {
method classCount (line 610) | public get classCount() {
method printWidth (line 614) | public get printWidth() {
method addMeta (line 618) | public addMeta(meta: Meta) {
method addClass (line 623) | public addClass(className: string) {
method clone (line 628) | public clone() {
method toString (line 635) | public toString(indent: boolean = true) {
method join (line 651) | private join(content: (string | undefined)[], separator: string = " ") {
function groupClasses (line 658) | function groupClasses(classes: string[], dissectedClasses: DissectedClas...
class Groups (line 695) | class Groups {
method constructor (line 700) | constructor() {
method group (line 704) | public get group() {
method at (line 708) | public at(index: number) {
method length (line 712) | public get length() {
method addGroup (line 716) | public addGroup() {
class Group (line 724) | class Group {
method classCount (line 728) | public get classCount() {
method at (line 732) | public at(index: number) {
method addClass (line 736) | public addClass(className: string) {
function getLineBreaks (line 742) | function getLineBreaks(ctx: Context<typeof enforceConsistentLineWrapping...
function isLineBreakStyleLikelyMisconfigured (line 747) | function isLineBreakStyleLikelyMisconfigured(ctx: Context<typeof enforce...
function isIndentationLikelyMisconfigured (line 759) | function isIndentationLikelyMisconfigured(ctx: Context<typeof enforceCon...
FILE: src/rules/enforce-consistent-variable-syntax.ts
function lintLiterals (line 48) | function lintLiterals(ctx: Context<typeof enforceConsistentVariableSynta...
function isBeginningOfArbitraryShorthand (line 174) | function isBeginningOfArbitraryShorthand(base: string): boolean {
function isBeginningOfArbitraryVariable (line 178) | function isBeginningOfArbitraryVariable(base: string): boolean {
function extractBalanced (line 182) | function extractBalanced(className: string, start = "(", end = ")") {
function trimTailwindWhitespace (line 226) | function trimTailwindWhitespace(className: string): string {
FILE: src/rules/enforce-consistent-variant-order.ts
function compareVariantOrder (line 80) | function compareVariantOrder(orderA: number | undefined, orderB: number ...
FILE: src/rules/enforce-logical-properties.ts
function lintLiterals (line 98) | function lintLiterals(ctx: Context<typeof enforceLogicalProperties>, lit...
function getReplacementBases (line 152) | function getReplacementBases(base: string): string[] | undefined {
FILE: src/rules/enforce-shorthand-classes.ts
type Shorthands (line 36) | type Shorthands = [RegExp[], string[]][][];
function lintLiterals (line 126) | function lintLiterals(ctx: Context<typeof enforceShorthandClasses>, lite...
function getShorthands (line 180) | function getShorthands(ctx: Context<typeof enforceShorthandClasses>, dis...
FILE: src/rules/no-conflicting-classes.ts
method initialize (line 26) | initialize(ctx) {
function lintLiterals (line 33) | function lintLiterals(ctx: Context<typeof noConflictingClasses>, literal...
FILE: src/rules/no-deprecated-classes.ts
function lintLiterals (line 74) | function lintLiterals(ctx: Context<typeof noDeprecatedClasses>, literals...
FILE: src/rules/no-duplicate-classes.ts
function lintLiterals (line 25) | function lintLiterals(ctx: Context<typeof noDuplicateClasses>, literals:...
function getClassesFromLiteralNodes (line 52) | function getClassesFromLiteralNodes(literals: Literal[]) {
FILE: src/rules/no-restricted-classes.ts
function lintLiterals (line 62) | function lintLiterals(ctx: Context<typeof noRestrictedClasses>, literals...
FILE: src/rules/no-unknown-classes.ts
function lintLiterals (line 59) | function lintLiterals(ctx: Context<typeof noUnknownClasses>, literals: L...
function getCustomComponentClassRegexes (line 107) | function getCustomComponentClassRegexes(ctx: Context<typeof noUnknownCla...
FILE: src/rules/no-unnecessary-whitespace.ts
function lintLiterals (line 32) | function lintLiterals(ctx: Context<typeof noUnnecessaryWhitespace>, lite...
FILE: src/tailwindcss/canonical-classes.async.v4.ts
function getCanonicalClasses (line 6) | function getCanonicalClasses(tailwindContext: any, classes: string[], op...
FILE: src/tailwindcss/canonical-classes.ts
type CanonicalClasses (line 12) | type CanonicalClasses = {
type CanonicalClassOptions (line 19) | type CanonicalClassOptions = {
type GetCanonicalClasses (line 25) | type GetCanonicalClasses = (ctx: AsyncContext, classes: string[], option...
function createGetCanonicalClasses (line 32) | function createGetCanonicalClasses(ctx: Context): GetCanonicalClasses {
function getWorkerPath (line 42) | function getWorkerPath(ctx: Context) {
FILE: src/tailwindcss/class-order.async.v3.ts
function getClassOrder (line 4) | function getClassOrder(tailwindContext: any, classes: string[]): ClassOr...
FILE: src/tailwindcss/class-order.async.v4.ts
function getClassOrder (line 4) | function getClassOrder(tailwindContext: any, classes: string[]): ClassOr...
FILE: src/tailwindcss/class-order.ts
type ClassOrder (line 12) | type ClassOrder = [className: string, order: bigint | null][];
type GetClassOrder (line 14) | type GetClassOrder = (ctx: AsyncContext, classes: string[]) => {
function createGetClassOrder (line 21) | function createGetClassOrder(ctx: Context): GetClassOrder {
function getWorkerPath (line 31) | function getWorkerPath(ctx: Context) {
FILE: src/tailwindcss/conflicting-classes.async.v4.ts
function getConflictingClasses (line 4) | async function getConflictingClasses(tailwindContext: any, classes: stri...
type StyleRule (line 66) | type StyleRule = {
type AtRule (line 72) | type AtRule = {
type Declaration (line 79) | type Declaration = {
type Comment (line 86) | type Comment = {
type Context (line 91) | type Context = {
type AtRoot (line 97) | type AtRoot = {
type Rule (line 102) | type Rule = AtRule | StyleRule;
type AstNode (line 103) | type AstNode = AtRoot | AtRule | Comment | Context | Declaration | Style...
type Property (line 106) | interface Property {
type RuleContext (line 112) | interface RuleContext {
function getRuleContext (line 116) | function getRuleContext(nodes: AstNode[]): RuleContext {
FILE: src/tailwindcss/conflicting-classes.ts
type ConflictingClasses (line 13) | type ConflictingClasses = {
type GetConflictingClasses (line 23) | type GetConflictingClasses = (ctx: AsyncContext, classes: string[]) => {
function createGetConflictingClasses (line 30) | function createGetConflictingClasses(ctx: Context): GetConflictingClasses {
function getWorkerPath (line 40) | function getWorkerPath(ctx: Context) {
FILE: src/tailwindcss/context.async.v4.ts
function createLoader (line 66) | function createLoader<T>(ctx: AsyncContext, jiti: ReturnType<typeof crea...
function getCurrentFilename (line 103) | function getCurrentFilename() {
FILE: src/tailwindcss/custom-component-classes.async.v3.ts
function getCustomComponentClasses (line 5) | async function getCustomComponentClasses(_ctx: AsyncContext): Promise<Cu...
FILE: src/tailwindcss/custom-component-classes.async.v4.ts
type ImportInfo (line 16) | interface ImportInfo {
type CssFile (line 21) | interface CssFile {
type CssFiles (line 26) | interface CssFiles {
function getCustomComponentClasses (line 32) | async function getCustomComponentClasses(ctx: AsyncContext): Promise<Cus...
function parseCssFilesDeep (line 44) | async function parseCssFilesDeep(ctx: AsyncContext, resolvedPath: string...
function getCustomComponentUtilities (line 109) | function getCustomComponentUtilities(files: CssFiles, filePath: string, ...
FILE: src/tailwindcss/custom-component-classes.ts
type CustomComponentClasses (line 15) | type CustomComponentClasses = string[];
type GetCustomComponentClasses (line 17) | type GetCustomComponentClasses = (ctx: AsyncContext) => {
function createGetCustomComponentClasses (line 24) | function createGetCustomComponentClasses(ctx: Context): GetCustomCompone...
function getWorkerPath (line 34) | function getWorkerPath(ctx: Context) {
FILE: src/tailwindcss/dissect-classes.async.v3.ts
function getDissectedClasses (line 10) | async function getDissectedClasses(ctx: AsyncContext, tailwindContext: a...
FILE: src/tailwindcss/dissect-classes.async.v4.ts
function getDissectedClasses (line 8) | function getDissectedClasses(tailwindContext: any, classes: string[]): D...
FILE: src/tailwindcss/dissect-classes.test.ts
function dissectClass (line 11) | function dissectClass(className: string) {
FILE: src/tailwindcss/dissect-classes.ts
type DissectedClass (line 12) | interface DissectedClass {
type DissectedClasses (line 23) | interface DissectedClasses {
type GetDissectedClasses (line 27) | type GetDissectedClasses = (ctx: AsyncContext, classes: string[]) => {
function createGetDissectedClasses (line 34) | function createGetDissectedClasses(ctx: Context): GetDissectedClasses {
function getWorkerPath (line 44) | function getWorkerPath(ctx: Context) {
FILE: src/tailwindcss/prefix.async.v3.ts
function getPrefix (line 4) | function getPrefix(tailwindContext: any): Prefix {
function getSuffix (line 8) | function getSuffix(tailwindContext: any): string {
FILE: src/tailwindcss/prefix.async.v4.ts
function getPrefix (line 4) | function getPrefix(tailwindContext: any): Prefix {
function getSuffix (line 8) | function getSuffix(tailwindContext: any): Suffix {
FILE: src/tailwindcss/prefix.ts
type Prefix (line 12) | type Prefix = string;
type Suffix (line 13) | type Suffix = string;
type GetPrefix (line 15) | type GetPrefix = (ctx: AsyncContext) => {
function createGetPrefix (line 23) | function createGetPrefix(ctx: Context): GetPrefix {
function getWorkerPath (line 33) | function getWorkerPath(ctx: Context) {
FILE: src/tailwindcss/unknown-classes.async.v3.ts
function getUnknownClasses (line 7) | async function getUnknownClasses(ctx: AsyncContext, tailwindContext: any...
FILE: src/tailwindcss/unknown-classes.async.v4.ts
function getUnknownClasses (line 4) | function getUnknownClasses(tailwindContext: any, classes: string[]): Unk...
FILE: src/tailwindcss/unknown-classes.ts
type UnknownClass (line 12) | type UnknownClass = string;
type GetUnknownClasses (line 14) | type GetUnknownClasses = (ctx: AsyncContext, classes: string[]) => {
function createGetUnknownClasses (line 21) | function createGetUnknownClasses(ctx: Context): GetUnknownClasses {
function getWorkerPath (line 31) | function getWorkerPath(ctx: Context) {
FILE: src/tailwindcss/variant-order.async.v3.ts
function getVariantOrder (line 4) | function getVariantOrder(): VariantOrder {
FILE: src/tailwindcss/variant-order.async.v4.ts
function getVariantOrder (line 6) | function getVariantOrder(tailwindContext: any, classes: string[]): Varia...
function hasGlobalSelector (line 40) | function hasGlobalSelector(variant: any): boolean {
FILE: src/tailwindcss/variant-order.ts
type VariantOrder (line 12) | type VariantOrder = Record<string, number | undefined>;
type GetVariantOrder (line 14) | type GetVariantOrder = (ctx: AsyncContext, classes: string[]) => {
function createGetVariantOrder (line 21) | function createGetVariantOrder(ctx: Context): GetVariantOrder {
function getWorkerPath (line 31) | function getWorkerPath(ctx: Context) {
FILE: src/types/ast.ts
type LiteralValueQuotes (line 1) | type LiteralValueQuotes = "'" | "\"" | "`";
type Range (line 3) | interface Range {
type Loc (line 7) | interface Loc {
type MultilineMeta (line 20) | interface MultilineMeta {
type WhitespaceMeta (line 26) | interface WhitespaceMeta {
type QuoteMeta (line 31) | interface QuoteMeta {
type BracesMeta (line 35) | interface BracesMeta {
type CSSMeta (line 40) | interface CSSMeta {
type Indentation (line 45) | interface Indentation {
type NodeBase (line 49) | interface NodeBase extends Range, Loc {
type LiteralBase (line 54) | interface LiteralBase extends NodeBase, MultilineMeta, QuoteMeta, Braces...
type TemplateLiteral (line 64) | interface TemplateLiteral extends LiteralBase {
type StringLiteral (line 68) | interface StringLiteral extends LiteralBase {
type CSSClassListLiteral (line 72) | interface CSSClassListLiteral extends LiteralBase {
type Literal (line 76) | type Literal = CSSClassListLiteral | StringLiteral | TemplateLiteral;
FILE: src/types/async.ts
type Async (line 1) | type Async<Fn extends (...args: any[]) => any> = (...params: Parameters<...
type Warning (line 3) | interface Warning<Options extends Record<string, any> = Record<string, a...
FILE: src/types/estree.ts
type Nullable (line 4) | type Nullable<Object extends object> = {
type WithParent (line 9) | type WithParent<BaseNode> = BaseNode & Nullable<Partial<Rule.NodeParentE...
FILE: src/types/rule.ts
type MatcherType (line 9) | enum MatcherType {
type SelectorKind (line 20) | enum SelectorKind {
type Regex (line 27) | type Regex = string;
type StringMatcher (line 31) | type StringMatcher = {
type ObjectKeyMatcher (line 35) | type ObjectKeyMatcher = {
type ObjectValueMatcher (line 40) | type ObjectValueMatcher = {
constant MATCHER_RESULT (line 45) | const MATCHER_RESULT = {
type MatcherFunctionResult (line 50) | type MatcherFunctionResult = typeof MATCHER_RESULT[keyof typeof MATCHER_...
type MatcherFunction (line 52) | type MatcherFunction = (node: unknown) => MatcherFunctionResult | Matche...
type MatcherFunctions (line 53) | type MatcherFunctions = MatcherFunction[];
type Matcher (line 54) | type Matcher = ObjectKeyMatcher | ObjectValueMatcher | StringMatcher;
type SelectorStringMatcher (line 58) | type SelectorStringMatcher = {
type SelectorAnonymousFunctionReturnMatcher (line 62) | type SelectorAnonymousFunctionReturnMatcher = {
type SelectorObjectKeyMatcher (line 67) | type SelectorObjectKeyMatcher = {
type SelectorObjectValueMatcher (line 72) | type SelectorObjectValueMatcher = {
type SelectorMatcher (line 77) | type SelectorMatcher =
type BaseSelector (line 83) | type BaseSelector<Kind extends SelectorKind> = {
type Target (line 89) | type Target = "all" | "first" | "last" | number;
type CallTarget (line 90) | type CallTarget = Target;
type ArgumentTarget (line 91) | type ArgumentTarget = Target;
type AttributeSelector (line 93) | type AttributeSelector = BaseSelector<SelectorKind.Attribute>;
type CalleeSelector (line 95) | type CalleeSelector = {
type TagSelector (line 106) | type TagSelector = {
type VariableSelector (line 113) | type VariableSelector = BaseSelector<SelectorKind.Variable>;
type Selector (line 115) | type Selector = AttributeSelector | CalleeSelector | TagSelector | Varia...
type Selectors (line 116) | type Selectors = Selector[];
type SelectorByKind (line 118) | type SelectorByKind<Kind extends SelectorKind> = Extract<Selector, { kin...
type Version (line 120) | type Version = {
type TailwindConfig (line 126) | type TailwindConfig = {
type TSConfig (line 131) | type TSConfig = {
type Schema (line 135) | type Schema = StrictObjectSchema<Record<string, OptionalSchema<BaseSchem...
type JsonSchema (line 136) | type JsonSchema<RawSchema extends Schema> = InferOutput<RawSchema>;
type RuleCategory (line 138) | type RuleCategory = "correctness" | "stylistic";
type CreateRuleOptions (line 140) | interface CreateRuleOptions<
type ESLintRule (line 169) | interface ESLintRule<
type RuleContext (line 187) | interface RuleContext<
type Context (line 232) | type Context<Rule extends ESLintRule = ESLintRule> = RuleContext<Rule["m...
type MessageId (line 234) | type MessageId<Messages extends Record<string, any> | undefined> = Messa...
type Trim (line 238) | type Trim<Content extends string> =
type ExtractVariables (line 243) | type ExtractVariables<Template extends string> =
FILE: src/utils/ast.ts
function getLocByRange (line 5) | function getLocByRange(ctx: Rule.RuleContext, range: [number, number]): ...
FILE: src/utils/class.ts
type ClassParts (line 4) | interface ClassParts {
function buildClass (line 13) | function buildClass(ctx: Context, { base, important, negative, prefix, s...
FILE: src/utils/context.ts
type AsyncContext (line 11) | interface AsyncContext {
function async (line 20) | function async(ctx: Context): AsyncContext {
function getTSConfigPath (line 34) | function getTSConfigPath({ configPath, cwd }: {
function getTailwindConfigPath (line 55) | function getTailwindConfigPath({ configPath, cwd, version }: {
function getEntryPointWarning (line 108) | function getEntryPointWarning(entryPoint: string | undefined, foundEntry...
function getConfigPathWarning (line 120) | function getConfigPathWarning(configPath: string | undefined, foundConfi...
FILE: src/utils/lint.ts
type RemoveNeverProperties (line 8) | type RemoveNeverProperties<ObjectType extends Record<string, any>> = {
function lintClasses (line 12) | function lintClasses<
FILE: src/utils/matchers.ts
function getLiteralNodesByMatchers (line 18) | function getLiteralNodesByMatchers<Node>(ctx: Rule.RuleContext, node: un...
function findMatchingNestedNodes (line 41) | function findMatchingNestedNodes<Node>(node: GenericNodeWithParent, matc...
function matchesPathPattern (line 97) | function matchesPathPattern(path: string, pattern: string): boolean {
function isCalleeName (line 101) | function isCalleeName(callee: Callees[number]): callee is CalleeName {
function isCalleeMatchers (line 105) | function isCalleeMatchers(callee: Callees[number]): callee is CalleeMatc...
function isVariableName (line 109) | function isVariableName(variable: Variables[number]): variable is Variab...
function isVariableMatchers (line 113) | function isVariableMatchers(variable: Variables[number]): variable is Va...
function isTagName (line 117) | function isTagName(tag: Tags[number]): tag is TagName {
function isTagMatchers (line 121) | function isTagMatchers(tag: Tags[number]): tag is TagMatchers {
function isAttributesName (line 125) | function isAttributesName(attributes: Attributes[number]): attributes is...
function isAttributesMatchers (line 129) | function isAttributesMatchers(attributes: Attributes[number]): attribute...
function isInsideConditionalExpressionTest (line 133) | function isInsideConditionalExpressionTest(node: WithParent<ESNode>): bo...
function isInsideDisallowedBinaryExpression (line 139) | function isInsideDisallowedBinaryExpression(node: WithParent<ESNode>): b...
function isInsideLogicalExpressionLeft (line 148) | function isInsideLogicalExpressionLeft(node: WithParent<ESNode>): boolean {
function isInsideMemberExpression (line 154) | function isInsideMemberExpression(node: WithParent<ESNode>): boolean {
function isIndexedAccessLiteral (line 161) | function isIndexedAccessLiteral(node: WithParent<ESNode>): boolean {
FILE: src/utils/quotes.ts
function escapeNestedQuotes (line 4) | function escapeNestedQuotes(content: string, surroundingQuotes: LiteralV...
FILE: src/utils/rule.ts
function createRule (line 69) | function createRule<
function createRuleListener (line 240) | function createRuleListener<Ctx extends Context>(ctx: Rule.RuleContext, ...
FILE: src/utils/selectors.ts
function isSelectorKind (line 4) | function isSelectorKind<Kind extends SelectorKind>(kind: Kind) {
FILE: src/utils/utils.ts
function getWhitespace (line 8) | function getWhitespace(classes: string) {
function getQuotes (line 15) | function getQuotes(raw: string): QuoteMeta {
function getContent (line 25) | function getContent(raw: string, quotes?: QuoteMeta, braces?: BracesMeta) {
function splitClasses (line 32) | function splitClasses(classes: string): string[] {
function deduplicateClasses (line 42) | function deduplicateClasses(classes: string[]): string[] {
function display (line 48) | function display(messageStyle: MessageStyleOption["messageStyle"], class...
function augmentMessageWithWarnings (line 72) | function augmentMessageWithWarnings<Options extends Record<string, any>>...
function escapeMessage (line 90) | function escapeMessage(messageStyle: MessageStyleOption["messageStyle"],...
function splitWhitespaces (line 100) | function splitWhitespaces(classes: string): string[] {
function getIndentation (line 104) | function getIndentation(line: string): number {
function isClassSticky (line 108) | function isClassSticky(literal: Literal, classIndex: number): boolean {
function getExactClassLocation (line 123) | function getExactClassLocation(literal: Literal, startIndex: number, end...
function matchesName (line 147) | function matchesName(pattern: string, name: string | undefined): boolean {
function replacePlaceholders (line 154) | function replacePlaceholders(template: string, match: RegExpMatchArray |...
function addAttribute (line 161) | function addAttribute(name: string | undefined): (literal: Literal, inde...
function deduplicateLiterals (line 172) | function deduplicateLiterals(literal: Literal, index: number, literals: ...
function createObjectPathElement (line 180) | function createObjectPathElement(path?: string): string {
type GenericNodeWithParent (line 188) | interface GenericNodeWithParent {
function isGenericNodeWithParent (line 192) | function isGenericNodeWithParent(node: unknown): node is GenericNodeWith...
FILE: src/utils/valibot.ts
function removeDefaults (line 1) | function removeDefaults(entries: Record<string, any>) {
FILE: src/utils/version.ts
function parseSemanticVersion (line 1) | function parseSemanticVersion(version: string): { major: number; minor: ...
FILE: src/utils/warn.ts
function warnOnce (line 3) | function warnOnce(message: string) {
FILE: tests/utils/context.ts
function createTestContext (line 10) | function createTestContext(): Context {
FILE: tests/utils/eslint.ts
function eslint (line 6) | async function eslint(
FILE: tests/utils/lint.ts
constant TEST_SYNTAXES (line 25) | const TEST_SYNTAXES = {
type Syntaxes (line 54) | type Syntaxes = typeof TEST_SYNTAXES;
constant LINTERS (line 56) | const LINTERS = {
function lint (line 79) | function lint<const Rule extends ESLintRule>(
type GuardedType (line 166) | type GuardedType<Type> = Type extends (value: any) => value is infer Res...
function findNode (line 168) | function findNode<Matcher extends (node: unknown) => node is any>(node: ...
function withParentNodeExtension (line 192) | function withParentNodeExtension(node: ESNode, parent: ESNode = node) {
function getFilesInDirectory (line 209) | function getFilesInDirectory(importURL: string) {
FILE: tests/utils/prettier.ts
function prettier (line 6) | async function prettier(
FILE: tests/utils/setup.ts
function teardown (line 4) | function teardown() {
FILE: tests/utils/template.ts
constant EOL (line 4) | const EOL = "\n";
constant TABS_IN_SPACES (line 5) | const TABS_IN_SPACES = 4;
function createTemplateTag (line 17) | function createTemplateTag(indentation?: number, removeNewLines: boolean...
function findCommonIndentation (line 31) | function findCommonIndentation(content: string, eol: string = EOL) {
function removeCommonIndentation (line 67) | function removeCommonIndentation(content: string, minIndentation: number...
function removeSurroundingNewLines (line 99) | function removeSurroundingNewLines(content: string) {
function assembleTemplateString (line 103) | function assembleTemplateString(templateString: TemplateStringsArray, .....
FILE: tests/utils/tmp.ts
type TestDirectoryFiles (line 10) | type TestDirectoryFiles<Files extends Record<string, string>> = {
class TestDirectory (line 14) | class TestDirectory<Files extends Record<string, string> = Record<string...
method constructor (line 21) | constructor(files?: Files, isolated: boolean = false) {
method files (line 43) | public get files(): TestDirectoryFiles<Files> {
method cleanUp (line 57) | public cleanUp() {
method [Symbol.dispose] (line 62) | [Symbol.dispose]() {
function getProjectRoot (line 68) | function getProjectRoot() {
function getTestDirectoryBasePath (line 72) | function getTestDirectoryBasePath(isolated: boolean = false) {
function cleanUpTestDirectory (line 81) | function cleanUpTestDirectory(path?: string) {
FILE: tests/utils/values.ts
function values (line 1) | function values(message: string, values: Record<string, string>): string {
FILE: tests/utils/version.ts
function getTailwindCSSVersion (line 8) | function getTailwindCSSVersion() {
function getNodeVersion (line 19) | function getNodeVersion() {
Condensed preview — 223 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,023K chars).
[
{
"path": ".cspell.json",
"chars": 573,
"preview": "{\n \"ignorePaths\": [\n \"node_modules/**\",\n \".vscode/**\",\n \"lib/**\"\n ],\n \"import\": [\n \"@schoero/configs/cspe"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.yml",
"chars": 2342,
"preview": "name: Bug Report\ndescription: Create a report\nlabels: [\"bug\"]\nbody:\n - attributes:\n description: A clear and conci"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 29,
"preview": "blank_issues_enabled: false\n\n"
},
{
"path": ".github/ISSUE_TEMPLATE/documentation.yml",
"chars": 304,
"preview": "name: Documentation\ndescription: Improvements or additions to documentation\nlabels: [\"documentation\"]\nbody:\n - attribut"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.yml",
"chars": 343,
"preview": "name: Feature Request\ndescription: Suggest an idea for this project\nlabels: [\"feature request\"]\nbody:\n - attributes:\n "
},
{
"path": ".github/ISSUE_TEMPLATE/question.yml",
"chars": 224,
"preview": "name: Question\ndescription: Ask a question\nlabels: [\"question\"]\nbody:\n - attributes:\n description: What is your qu"
},
{
"path": ".github/workflows/ci.yml",
"chars": 1435,
"preview": "name: CI\n\non:\n pull_request:\n branches:\n - main\n push:\n\njobs:\n lint:\n runs-on: ubuntu-latest\n steps:\n "
},
{
"path": ".gitignore",
"chars": 42,
"preview": "node_modules\ntmp\nlib\nlocal\n.DS_Store\n.env\n"
},
{
"path": ".markdownlint.jsonc",
"chars": 48,
"preview": "{\n \"extends\": \"@schoero/configs/markdownlint\"\n}"
},
{
"path": ".vscode/extensions.json",
"chars": 143,
"preview": "{\n \"recommendations\": [\n \"dbaeumer.vscode-eslint\",\n \"streetsidesoftware.code-spell-checker\",\n \"davidanson.vsco"
},
{
"path": ".vscode/launch.json",
"chars": 908,
"preview": "{\n \"configurations\": [\n {\n \"args\": [\n \"run\",\n \"${relativeFileDirname}/${fileBasenameNoExtension}\""
},
{
"path": ".vscode/settings.json",
"chars": 2048,
"preview": "{\n\n // ESLint\n \"[javascript][typescript][json][json5][jsonc][yaml]\": {\n \"editor.defaultFormatter\": \"dbaeumer.vscode"
},
{
"path": "CHANGELOG.md",
"chars": 44910,
"preview": "# Changelog\n\n## v4.5.0\n\n[compare changes](https://github.com/schoero/eslint-plugin-better-tailwindcss/compare/v4.4.1...v"
},
{
"path": "CONTRIBUTING.md",
"chars": 4782,
"preview": "# Contributing to eslint-plugin-better-tailwindcss\n\nFirst off, thank you for considering contributing to **eslint-plugin"
},
{
"path": "FUNDING.yml",
"chars": 20,
"preview": "github:\n - schoero\n"
},
{
"path": "LICENSE",
"chars": 1075,
"preview": "MIT License\n\nCopyright (c) 2023 Roger Schönbächler\n\nPermission is hereby granted, free of charge, to any person obtainin"
},
{
"path": "README.md",
"chars": 13076,
"preview": "<div align=\"center\">\n <picture>\n <source media=\"(prefers-color-scheme: dark)\" srcset=\"./assets/eslint-plugin-better-"
},
{
"path": "build/index.ts",
"chars": 316,
"preview": "import { $ } from \"better-tailwindcss:build/utils.js\";\n\nasync function build(){\n const outDir = \"lib\"\n\n console.info(\""
},
{
"path": "build/utils.ts",
"chars": 386,
"preview": "import { exec, type ExecOptions } from 'node:child_process'\n\nexport async function $(command: string, options?: ExecOpti"
},
{
"path": "changelog.config.js",
"chars": 56,
"preview": "export { default } from \"@schoero/configs/changelogen\";\n"
},
{
"path": "docs/api/defaults.md",
"chars": 1943,
"preview": "\n# Defaults\n\nThe plugin comes with a set of default [selectors](../configuration/advanced.md#selectors). These selectors"
},
{
"path": "docs/configuration/advanced.md",
"chars": 10508,
"preview": "# Advanced Configuration\n\nThe [rules](../../README.md#rules) in this plugin lint Tailwind classes inside string literals"
},
{
"path": "docs/parsers/angular.md",
"chars": 4707,
"preview": "# Angular\n\n- [ESLint](#eslint)\n- [Oxlint](#oxlint)\n\n<br/>\n\n## ESLint\n\nTo use ESLint with Angular, install [Angular ESLin"
},
{
"path": "docs/parsers/astro.md",
"chars": 4070,
"preview": "# Astro\n\n- [ESLint](#eslint)\n- [Oxlint](#oxlint)\n\n<br/>\n\n## ESLint\n\nTo use ESLint with Astro files, first install the [a"
},
{
"path": "docs/parsers/css.md",
"chars": 2322,
"preview": "# CSS\n\n- [ESLint](#eslint)\n- [Oxlint](#oxlint)\n\n<br/>\n\n## ESLint\n\nTo use ESLint with CSS files containing Tailwind CSS `"
},
{
"path": "docs/parsers/html.md",
"chars": 2746,
"preview": "# HTML\n\n- [ESLint](#eslint)\n- [Oxlint](#oxlint)\n\n<br/>\n\n## ESLint\n\nTo use ESLint with HTML files, first install the [@ht"
},
{
"path": "docs/parsers/javascript.md",
"chars": 3082,
"preview": "# JavaScript\n\n- [ESLint](#eslint)\n- [Oxlint](#oxlint)\n\n<br/>\n\n## ESLint\n\nTo lint Tailwind CSS classes in JavaScript file"
},
{
"path": "docs/parsers/jsx.md",
"chars": 3331,
"preview": "# JSX\n\n- [ESLint](#eslint)\n- [Oxlint](#oxlint)\n\n<br/>\n\n## ESLint\n\nTo lint Tailwind CSS classes in JSX files, ensure that"
},
{
"path": "docs/parsers/svelte.md",
"chars": 2729,
"preview": "# Svelte\n\n- [ESLint](#eslint)\n- [Oxlint](#oxlint)\n\n<br/>\n\n## ESLint\n\nTo use ESLint with Svelte files, first install the "
},
{
"path": "docs/parsers/tsx.md",
"chars": 4437,
"preview": "# TSX\n\n- [ESLint](#eslint)\n- [Oxlint](#oxlint)\n\n<br/>\n\n## ESLint\n\nTo use ESLint with TSX files, first install the [types"
},
{
"path": "docs/parsers/typescript.md",
"chars": 4084,
"preview": "# TypeScript\n\n- [ESLint](#eslint)\n- [Oxlint](#oxlint)\n\n<br/>\n\n## ESLint\n\nTo use ESLint with TypeScript files, first inst"
},
{
"path": "docs/parsers/vue.md",
"chars": 2675,
"preview": "# Vue\n\n- [ESLint](#eslint)\n- [Oxlint](#oxlint)\n\n<br/>\n\n## ESLint\n\nTo use ESLint with Vue files, first install the [vue-e"
},
{
"path": "docs/rules/enforce-canonical-classes.md",
"chars": 4164,
"preview": "# better-tailwindcss/enforce-canonical-classes\n\nImplements the [canonical suggestions](https://github.com/tailwindlabs/t"
},
{
"path": "docs/rules/enforce-consistent-class-order.md",
"chars": 5481,
"preview": "# better-tailwindcss/enforce-consistent-class-order\n\nEnforce the order of tailwind classes. It is possible to sort class"
},
{
"path": "docs/rules/enforce-consistent-important-position.md",
"chars": 3135,
"preview": "# better-tailwindcss/enforce-consistent-important-position\n\nEnforce consistent important position for Tailwind CSS class"
},
{
"path": "docs/rules/enforce-consistent-line-wrapping.md",
"chars": 5584,
"preview": "# better-tailwindcss/enforce-consistent-line-wrapping\n\nEnforce tailwind classes to be broken up into multiple lines. It "
},
{
"path": "docs/rules/enforce-consistent-variable-syntax.md",
"chars": 1818,
"preview": "# better-tailwindcss/enforce-consistent-variable-syntax\n\nEnforce consistent css variable syntax in Tailwind CSS class st"
},
{
"path": "docs/rules/enforce-consistent-variant-order.md",
"chars": 921,
"preview": "# better-tailwindcss/enforce-consistent-variant-order\n\nEnforce Tailwind CSS variant order for class names.\n\n<br/>\n\n## Op"
},
{
"path": "docs/rules/enforce-logical-properties.md",
"chars": 3419,
"preview": "# better-tailwindcss/enforce-logical-properties\n\nEnforce logical utility class names over physical directions for better"
},
{
"path": "docs/rules/enforce-shorthand-classes.md",
"chars": 1242,
"preview": "# better-tailwindcss/enforce-shorthand-classes\n\nThis rule identifies when multiple longhand Tailwind CSS classes can be "
},
{
"path": "docs/rules/no-conflicting-classes.md",
"chars": 2407,
"preview": "# better-tailwindcss/no-conflicting-classes\n\nDisallow conflicting classes in Tailwind CSS class strings. Conflicting cla"
},
{
"path": "docs/rules/no-deprecated-classes.md",
"chars": 3439,
"preview": "# better-tailwindcss/no-deprecated-classes\n\nDisallow the use of [deprecated Tailwind CSS classes](https://tailwindcss.co"
},
{
"path": "docs/rules/no-duplicate-classes.md",
"chars": 1254,
"preview": "# better-tailwindcss/no-duplicate-classes\n\nDisallow duplicate classes in Tailwind CSS class strings.\n\n<br/>\n\n## Options\n"
},
{
"path": "docs/rules/no-restricted-classes.md",
"chars": 2830,
"preview": "# better-tailwindcss/no-restricted-classes\n\nDisallow the usage of certain classes. This can be useful to disallow classe"
},
{
"path": "docs/rules/no-unknown-classes.md",
"chars": 2576,
"preview": "# better-tailwindcss/no-unknown-classes\n\nDisallow unknown classes in Tailwind CSS class strings. Unknown classes are cla"
},
{
"path": "docs/rules/no-unnecessary-whitespace.md",
"chars": 1222,
"preview": "# better-tailwindcss/no-unnecessary-whitespace\n\nDisallow unnecessary whitespace in between and around tailwind classes.\n"
},
{
"path": "docs/settings/settings.md",
"chars": 3781,
"preview": "# Settings\n\n## Table of Contents\n\n- [entryPoint](#entrypoint)\n- [tailwindConfig](#tailwindconfig)\n- [tsconfig](#tsconfig"
},
{
"path": "eslint.config.ts",
"chars": 1330,
"preview": "import config from \"@schoero/configs/eslint\";\nimport { defineConfig } from \"eslint/config\";\n\n\nexport default defineConfi"
},
{
"path": "package.json",
"chars": 5052,
"preview": "{\n \"version\": \"4.5.0\",\n \"type\": \"module\",\n \"name\": \"eslint-plugin-better-tailwindcss\",\n \"description\": \"auto-wraps t"
},
{
"path": "src/api/defaults.ts",
"chars": 1809,
"preview": "/* eslint-disable eslint-plugin-jsdoc/require-returns */\n/* eslint-disable eslint-plugin-jsdoc/require-description */\nim"
},
{
"path": "src/api/types.ts",
"chars": 824,
"preview": "/* Targets for arguments and calls */\nexport type { ArgumentTarget, CallTarget } from \"better-tailwindcss:types/rule.js\""
},
{
"path": "src/async-utils/cache.ts",
"chars": 1452,
"preview": "import { getModifiedDate } from \"./fs.js\";\n\n\ninterface CacheItem {\n date: Date;\n value: any;\n}\n\nconst CACHE = new Map<"
},
{
"path": "src/async-utils/escape.ts",
"chars": 168,
"preview": "import { getCachedRegex } from \"./regex.js\";\n\n\nexport function escapeForRegex(word: string) {\n return word.replace(getC"
},
{
"path": "src/async-utils/fs.ts",
"chars": 973,
"preview": "import { existsSync, statSync } from \"node:fs\";\nimport { basename, dirname, resolve } from \"node:path\";\n\n\nexport functio"
},
{
"path": "src/async-utils/module.ts",
"chars": 184,
"preview": "export function isCommonJSModule() {\n return typeof module !== \"undefined\" && typeof module.exports !== \"undefined\";\n}\n"
},
{
"path": "src/async-utils/operations.ts",
"chars": 1345,
"preview": "import type { GetCanonicalClasses } from \"../tailwindcss/canonical-classes.js\";\nimport type { GetClassOrder } from \"../t"
},
{
"path": "src/async-utils/order.ts",
"chars": 55,
"preview": "export enum VARIANT_ORDER_FLAGS {\n GLOBAL = 1 << 16\n}\n"
},
{
"path": "src/async-utils/path.ts",
"chars": 260,
"preview": "import { pathToFileURL } from \"node:url\";\n\nimport { isESModule } from \"./module.js\";\nimport { isWindows } from \"./platfo"
},
{
"path": "src/async-utils/platform.ts",
"chars": 71,
"preview": "export function isWindows() {\n return process.platform === \"win32\";\n}\n"
},
{
"path": "src/async-utils/regex.ts",
"chars": 1163,
"preview": "const REGEX_CACHE = new Map<string, RegExp>();\nconst MAX_CACHE_SIZE = 500;\n\n\nfunction getRegexCacheKey(pattern: string, "
},
{
"path": "src/async-utils/resolvers.ts",
"chars": 3175,
"preview": "import fs from \"node:fs\";\n\nimport enhancedResolve from \"enhanced-resolve\";\nimport { TsconfigPathsPlugin } from \"tsconfig"
},
{
"path": "src/async-utils/tsconfig.ts",
"chars": 1189,
"preview": "import { resolve } from \"node:path\";\n\nimport { withCache } from \"./cache.js\";\nimport { findPathRecursive } from \"./fs.js"
},
{
"path": "src/async-utils/worker.ts",
"chars": 460,
"preview": "import { env } from \"node:process\";\n\nimport { TsRunner } from \"synckit\";\n\nimport type { SynckitOptions } from \"synckit\";"
},
{
"path": "src/configs/config.test.ts",
"chars": 1268,
"preview": "import { describe, expect, it } from \"vitest\";\n\nimport config from \"better-tailwindcss:configs/config.js\";\n\n\ndescribe(\"c"
},
{
"path": "src/configs/config.ts",
"chars": 5116,
"preview": "import { enforceCanonicalClasses } from \"better-tailwindcss:rules/enforce-canonical-classes.js\";\nimport { enforceConsist"
},
{
"path": "src/options/callees/cc.test.ts",
"chars": 1576,
"preview": "import { describe, it } from \"vitest\";\n\nimport { CC_OBJECT_KEYS, CC_STRINGS } from \"better-tailwindcss:options/callees/c"
},
{
"path": "src/options/callees/cc.ts",
"chars": 618,
"preview": "import { MatcherType, SelectorKind } from \"better-tailwindcss:types/rule.js\";\n\nimport type { CalleeSelector, Selectors }"
},
{
"path": "src/options/callees/clb.test.ts",
"chars": 4233,
"preview": "import { describe, it } from \"vitest\";\n\nimport {\n CLB_BASE_VALUES,\n CLB_COMPOUND_VARIANTS_CLASSES,\n CLB_VARIANT_VALUE"
},
{
"path": "src/options/callees/clb.ts",
"chars": 1000,
"preview": "import { MatcherType, SelectorKind } from \"better-tailwindcss:types/rule.js\";\n\nimport type { CalleeSelector, Selectors }"
},
{
"path": "src/options/callees/clsx.test.ts",
"chars": 1596,
"preview": "import { describe, it } from \"vitest\";\n\nimport { CLSX_OBJECT_KEYS, CLSX_STRINGS } from \"better-tailwindcss:options/calle"
},
{
"path": "src/options/callees/clsx.ts",
"chars": 622,
"preview": "import { MatcherType, SelectorKind } from \"better-tailwindcss:types/rule.js\";\n\nimport type { CalleeSelector, Selectors }"
},
{
"path": "src/options/callees/cn.test.ts",
"chars": 1576,
"preview": "import { describe, it } from \"vitest\";\n\nimport { CN_OBJECT_KEYS, CN_STRINGS } from \"better-tailwindcss:options/callees/c"
},
{
"path": "src/options/callees/cn.ts",
"chars": 624,
"preview": "import { MatcherType, SelectorKind } from \"better-tailwindcss:types/rule.js\";\n\nimport type { CalleeSelector, Selectors }"
},
{
"path": "src/options/callees/cnb.test.ts",
"chars": 1586,
"preview": "import { describe, it } from \"vitest\";\n\nimport { CNB_OBJECT_KEYS, CNB_STRINGS } from \"better-tailwindcss:options/callees"
},
{
"path": "src/options/callees/cnb.ts",
"chars": 621,
"preview": "import { MatcherType, SelectorKind } from \"better-tailwindcss:types/rule.js\";\n\nimport type { CalleeSelector, Selectors }"
},
{
"path": "src/options/callees/ctl.test.ts",
"chars": 823,
"preview": "import { describe, it } from \"vitest\";\n\nimport { CTL_STRINGS } from \"better-tailwindcss:options/callees/ctl.js\";\nimport "
},
{
"path": "src/options/callees/ctl.ts",
"chars": 452,
"preview": "import { MatcherType, SelectorKind } from \"better-tailwindcss:types/rule.js\";\n\nimport type { CalleeSelector, Selectors }"
},
{
"path": "src/options/callees/cva.test.ts",
"chars": 4226,
"preview": "import { describe, it } from \"vitest\";\n\nimport {\n CVA_COMPOUND_VARIANTS_CLASS,\n CVA_STRINGS,\n CVA_VARIANT_VALUES\n} fr"
},
{
"path": "src/options/callees/cva.ts",
"chars": 932,
"preview": "import { MatcherType, SelectorKind } from \"better-tailwindcss:types/rule.js\";\n\nimport type { CalleeSelector, Selectors }"
},
{
"path": "src/options/callees/cx.test.ts",
"chars": 1576,
"preview": "import { describe, it } from \"vitest\";\n\nimport { CX_OBJECT_KEYS, CX_STRINGS } from \"better-tailwindcss:options/callees/c"
},
{
"path": "src/options/callees/cx.ts",
"chars": 617,
"preview": "import { MatcherType, SelectorKind } from \"better-tailwindcss:types/rule.js\";\n\nimport type { CalleeSelector, Selectors }"
},
{
"path": "src/options/callees/dcnb.test.ts",
"chars": 1596,
"preview": "import { describe, it } from \"vitest\";\n\nimport { DCNB_OBJECT_KEYS, DCNB_STRINGS } from \"better-tailwindcss:options/calle"
},
{
"path": "src/options/callees/dcnb.ts",
"chars": 628,
"preview": "import { MatcherType, SelectorKind } from \"better-tailwindcss:types/rule.js\";\n\nimport type { CalleeSelector, Selectors }"
},
{
"path": "src/options/callees/objstr.test.ts",
"chars": 1010,
"preview": "import { describe, it } from \"vitest\";\n\nimport { OBJSTR_OBJECT_KEYS } from \"better-tailwindcss:options/callees/objstr.js"
},
{
"path": "src/options/callees/objstr.ts",
"chars": 621,
"preview": "import { MatcherType, SelectorKind } from \"better-tailwindcss:types/rule.js\";\n\nimport type { CalleeSelector, Selectors }"
},
{
"path": "src/options/callees/tv.test.ts",
"chars": 10980,
"preview": "import { describe, it } from \"vitest\";\n\nimport {\n TV_BASE_VALUES,\n TV_COMPOUND_SLOTS_CLASS,\n TV_COMPOUND_VARIANTS_CLA"
},
{
"path": "src/options/callees/tv.ts",
"chars": 1652,
"preview": "import { MatcherType, SelectorKind } from \"better-tailwindcss:types/rule.js\";\n\nimport type { CalleeSelector, Selectors }"
},
{
"path": "src/options/callees/twJoin.test.ts",
"chars": 905,
"preview": "import { describe, it } from \"vitest\";\n\nimport { TW_JOIN_STRINGS } from \"better-tailwindcss:options/callees/twJoin.js\";\n"
},
{
"path": "src/options/callees/twJoin.ts",
"chars": 453,
"preview": "import { MatcherType, SelectorKind } from \"better-tailwindcss:types/rule.js\";\n\nimport type { CalleeSelector, Selectors }"
},
{
"path": "src/options/callees/twMerge.test.ts",
"chars": 911,
"preview": "import { describe, it } from \"vitest\";\n\nimport { TW_MERGE_STRINGS } from \"better-tailwindcss:options/callees/twMerge.js\""
},
{
"path": "src/options/callees/twMerge.ts",
"chars": 457,
"preview": "import { MatcherType, SelectorKind } from \"better-tailwindcss:types/rule.js\";\n\nimport type { CalleeSelector, Selectors }"
},
{
"path": "src/options/default-options.test.ts",
"chars": 1358,
"preview": "import { describe, expect, it } from \"vitest\";\n\nimport { DEFAULT_CALLEE_SELECTORS, DEFAULT_TAG_SELECTORS } from \"better-"
},
{
"path": "src/options/default-options.ts",
"chars": 3868,
"preview": "import { CC } from \"better-tailwindcss:options/callees/cc.js\";\nimport { CLB } from \"better-tailwindcss:options/callees/c"
},
{
"path": "src/options/descriptions.test.ts",
"chars": 6087,
"preview": "import { toJsonSchema } from \"@valibot/to-json-schema\";\nimport { validate } from \"json-schema\";\nimport { describe, expec"
},
{
"path": "src/options/descriptions.ts",
"chars": 1382,
"preview": "import { strictObject } from \"valibot\";\n\nimport { ATTRIBUTES_OPTION_SCHEMA } from \"better-tailwindcss:options/schemas/at"
},
{
"path": "src/options/migrate.test.ts",
"chars": 2566,
"preview": "import { describe, expect, test } from \"vitest\";\n\nimport {\n hasLegacySelectorConfig,\n migrateFlatSelectorsToLegacySele"
},
{
"path": "src/options/migrate.ts",
"chars": 4702,
"preview": "import { MatcherType, SelectorKind } from \"better-tailwindcss:types/rule.js\";\n\nimport type { Attributes } from \"better-t"
},
{
"path": "src/options/schemas/attributes.ts",
"chars": 1516,
"preview": "import {\n array,\n description,\n optional,\n pipe,\n strictObject,\n string,\n tuple,\n union\n} from \"valibot\";\n\nimpor"
},
{
"path": "src/options/schemas/callees.ts",
"chars": 1453,
"preview": "import {\n array,\n description,\n optional,\n pipe,\n strictObject,\n string,\n tuple,\n union\n} from \"valibot\";\n\nimpor"
},
{
"path": "src/options/schemas/common.ts",
"chars": 2630,
"preview": "import { env } from \"node:process\";\n\nimport {\n boolean,\n description,\n literal,\n number,\n optional,\n pipe,\n stric"
},
{
"path": "src/options/schemas/matchers.ts",
"chars": 1011,
"preview": "import { description, literal, optional, pipe, strictObject, string } from \"valibot\";\n\nimport { MatcherType } from \"bett"
},
{
"path": "src/options/schemas/selectors.ts",
"chars": 5763,
"preview": "import {\n array,\n description,\n literal,\n number,\n optional,\n pipe,\n strictObject,\n string,\n union\n} from \"vali"
},
{
"path": "src/options/schemas/tags.ts",
"chars": 1414,
"preview": "import {\n array,\n description,\n optional,\n pipe,\n strictObject,\n string,\n tuple,\n union\n} from \"valibot\";\n\nimpor"
},
{
"path": "src/options/schemas/variables.ts",
"chars": 1500,
"preview": "import {\n array,\n description,\n optional,\n pipe,\n strictObject,\n string,\n tuple,\n union\n} from \"valibot\";\n\nimpor"
},
{
"path": "src/options/tags/twc.test.ts",
"chars": 4815,
"preview": "import { describe, it } from \"vitest\";\n\nimport { TWC_CALLEE_STRINGS, TWC_TAG } from \"better-tailwindcss:options/tags/twc"
},
{
"path": "src/options/tags/twc.ts",
"chars": 694,
"preview": "import { MatcherType, SelectorKind } from \"better-tailwindcss:types/rule.js\";\n\nimport type { CalleeSelector, Selectors, "
},
{
"path": "src/options/tags/twx.test.ts",
"chars": 3582,
"preview": "import { describe, it } from \"vitest\";\n\nimport { TWX_CALLEE_STRINGS, TWX_TAG } from \"better-tailwindcss:options/tags/twx"
},
{
"path": "src/options/tags/twx.ts",
"chars": 694,
"preview": "import { MatcherType, SelectorKind } from \"better-tailwindcss:types/rule.js\";\n\nimport type { CalleeSelector, Selectors, "
},
{
"path": "src/parsers/angular.test.ts",
"chars": 18646,
"preview": "import { describe, it } from \"vitest\";\n\nimport { enforceConsistentClassOrder } from \"better-tailwindcss:rules/enforce-co"
},
{
"path": "src/parsers/angular.ts",
"chars": 20829,
"preview": "import { MATCHER_RESULT, MatcherType } from \"better-tailwindcss:types/rule.js\";\nimport { getLocByRange } from \"better-ta"
},
{
"path": "src/parsers/css.test.ts",
"chars": 2058,
"preview": "import { describe, it } from \"vitest\";\n\nimport { enforceConsistentLineWrapping } from \"better-tailwindcss:rules/enforce-"
},
{
"path": "src/parsers/css.ts",
"chars": 2475,
"preview": "import { getIndentation, getWhitespace } from \"better-tailwindcss:utils/utils.js\";\n\nimport type { Atrule } from \"@eslint"
},
{
"path": "src/parsers/es.test.ts",
"chars": 29431,
"preview": "import { describe, it } from \"vitest\";\n\nimport { noUnnecessaryWhitespace } from \"better-tailwindcss:rules/no-unnecessary"
},
{
"path": "src/parsers/es.ts",
"chars": 28638,
"preview": "import { MATCHER_RESULT, MatcherType } from \"better-tailwindcss:types/rule.js\";\nimport {\n getLiteralNodesByMatchers,\n "
},
{
"path": "src/parsers/html.test.ts",
"chars": 589,
"preview": "import { describe, it } from \"vitest\";\n\nimport { enforceConsistentClassOrder } from \"better-tailwindcss:rules/enforce-co"
},
{
"path": "src/parsers/html.ts",
"chars": 2240,
"preview": "import {\n addAttribute,\n deduplicateLiterals,\n getContent,\n getIndentation,\n matchesName\n} from \"better-tailwindcss"
},
{
"path": "src/parsers/jsx.test.ts",
"chars": 3658,
"preview": "import { describe, it } from \"vitest\";\n\nimport { enforceConsistentClassOrder } from \"better-tailwindcss:rules/enforce-co"
},
{
"path": "src/parsers/jsx.ts",
"chars": 5001,
"preview": "import {\n ES_CONTAINER_TYPES_TO_INSERT_BRACES,\n ES_CONTAINER_TYPES_TO_REPLACE_QUOTES,\n getLiteralsByESMatchers,\n get"
},
{
"path": "src/parsers/svelte.test.ts",
"chars": 4703,
"preview": "import { describe, it } from \"vitest\";\n\nimport { enforceConsistentClassOrder } from \"better-tailwindcss:rules/enforce-co"
},
{
"path": "src/parsers/svelte.ts",
"chars": 8649,
"preview": "import {\n ES_CONTAINER_TYPES_TO_REPLACE_QUOTES,\n getESMatcherFunctions,\n getLiteralsByESLiteralNode,\n hasESNodeParen"
},
{
"path": "src/parsers/vue.test.ts",
"chars": 6371,
"preview": "import { describe, it } from \"vitest\";\n\nimport { enforceConsistentClassOrder } from \"better-tailwindcss:rules/enforce-co"
},
{
"path": "src/parsers/vue.ts",
"chars": 5281,
"preview": "import {\n ES_CONTAINER_TYPES_TO_INSERT_BRACES,\n ES_CONTAINER_TYPES_TO_REPLACE_QUOTES,\n getESMatcherFunctions,\n getLi"
},
{
"path": "src/rules/enforce-canonical-classes.test.ts",
"chars": 16939,
"preview": "import { describe, it } from \"vitest\";\n\nimport { enforceCanonicalClasses } from \"better-tailwindcss:rules/enforce-canoni"
},
{
"path": "src/rules/enforce-canonical-classes.ts",
"chars": 3682,
"preview": "import {\n array,\n boolean,\n description,\n optional,\n pipe,\n strictObject,\n string\n} from \"valibot\";\n\nimport { cre"
},
{
"path": "src/rules/enforce-consistent-class-order.test.ts",
"chars": 31112,
"preview": "import { describe, it } from \"vitest\";\n\nimport { enforceConsistentClassOrder } from \"better-tailwindcss:rules/enforce-co"
},
{
"path": "src/rules/enforce-consistent-class-order.ts",
"chars": 10891,
"preview": "import { description, literal, optional, pipe, strictObject, union } from \"valibot\";\n\nimport { createGetClassOrder, getC"
},
{
"path": "src/rules/enforce-consistent-important-position.test.ts",
"chars": 18447,
"preview": "import { describe, it } from \"vitest\";\n\nimport { enforceConsistentImportantPosition } from \"better-tailwindcss:rules/enf"
},
{
"path": "src/rules/enforce-consistent-important-position.ts",
"chars": 2924,
"preview": "import { description, literal, optional, pipe, strictObject, union } from \"valibot\";\n\nimport { createGetDissectedClasses"
},
{
"path": "src/rules/enforce-consistent-line-wrapping.test.ts",
"chars": 46466,
"preview": "import eslintParserHTML from \"@html-eslint/parser\";\nimport { ESLint } from \"eslint\";\nimport { describe, expect, it } fro"
},
{
"path": "src/rules/enforce-consistent-line-wrapping.ts",
"chars": 20723,
"preview": "import {\n boolean,\n description,\n literal,\n minValue,\n number,\n optional,\n pipe,\n strictObject,\n union\n} from \""
},
{
"path": "src/rules/enforce-consistent-variable-syntax.test.ts",
"chars": 46556,
"preview": "import { describe, it } from \"vitest\";\n\nimport { enforceConsistentVariableSyntax } from \"better-tailwindcss:rules/enforc"
},
{
"path": "src/rules/enforce-consistent-variable-syntax.ts",
"chars": 6254,
"preview": "import { description, literal, optional, pipe, strictObject, union } from \"valibot\";\n\nimport { createGetDissectedClasses"
},
{
"path": "src/rules/enforce-consistent-variant-order.test.ts",
"chars": 17101,
"preview": "import { describe, it } from \"vitest\";\n\nimport { enforceConsistentVariantOrder } from \"better-tailwindcss:rules/enforce-"
},
{
"path": "src/rules/enforce-consistent-variant-order.ts",
"chars": 2961,
"preview": "import { createGetDissectedClasses, getDissectedClasses } from \"better-tailwindcss:tailwindcss/dissect-classes.js\";\nimpo"
},
{
"path": "src/rules/enforce-logical-properties.test.ts",
"chars": 10701,
"preview": "import { describe, it } from \"vitest\";\n\nimport { enforceLogicalProperties } from \"better-tailwindcss:rules/enforce-logic"
},
{
"path": "src/rules/enforce-logical-properties.ts",
"chars": 5185,
"preview": "import { createGetDissectedClasses, getDissectedClasses } from \"better-tailwindcss:tailwindcss/dissect-classes.js\";\nimpo"
},
{
"path": "src/rules/enforce-shorthand-classes.test.ts",
"chars": 20557,
"preview": "import { describe, it } from \"vitest\";\n\nimport { enforceShorthandClasses } from \"better-tailwindcss:rules/enforce-shorth"
},
{
"path": "src/rules/enforce-shorthand-classes.ts",
"chars": 9537,
"preview": "import { createGetDissectedClasses, getDissectedClasses } from \"better-tailwindcss:tailwindcss/dissect-classes.js\";\nimpo"
},
{
"path": "src/rules/no-conflicting-classes.test.ts",
"chars": 4488,
"preview": "import { describe, it } from \"vitest\";\n\nimport { noConflictingClasses } from \"better-tailwindcss:rules/no-conflicting-cl"
},
{
"path": "src/rules/no-conflicting-classes.ts",
"chars": 2571,
"preview": "import {\n createGetConflictingClasses,\n getConflictingClasses\n} from \"better-tailwindcss:tailwindcss/conflicting-class"
},
{
"path": "src/rules/no-deprecated-classes.test.ts",
"chars": 10174,
"preview": "import { describe, it } from \"vitest\";\n\nimport { noDeprecatedClasses } from \"better-tailwindcss:rules/no-deprecated-clas"
},
{
"path": "src/rules/no-deprecated-classes.ts",
"chars": 4050,
"preview": "import { createGetDissectedClasses, getDissectedClasses } from \"better-tailwindcss:tailwindcss/dissect-classes.js\";\nimpo"
},
{
"path": "src/rules/no-duplicate-classes.test.ts",
"chars": 18510,
"preview": "import { describe, it } from \"vitest\";\n\nimport { noDuplicateClasses } from \"better-tailwindcss:rules/no-duplicate-classe"
},
{
"path": "src/rules/no-duplicate-classes.ts",
"chars": 2037,
"preview": "import { lintClasses } from \"better-tailwindcss:utils/lint.js\";\nimport { createRule } from \"better-tailwindcss:utils/rul"
},
{
"path": "src/rules/no-restricted-classes.test.ts",
"chars": 19432,
"preview": "import { describe, it } from \"vitest\";\n\nimport { noRestrictedClasses } from \"better-tailwindcss:rules/no-restricted-clas"
},
{
"path": "src/rules/no-restricted-classes.ts",
"chars": 2599,
"preview": "import {\n array,\n description,\n optional,\n pipe,\n strictObject,\n string,\n union\n} from \"valibot\";\n\nimport { lintC"
},
{
"path": "src/rules/no-unknown-classes.test.ts",
"chars": 41800,
"preview": "import { describe, it } from \"vitest\";\n\nimport { noUnknownClasses } from \"better-tailwindcss:rules/no-unknown-classes.js"
},
{
"path": "src/rules/no-unknown-classes.ts",
"chars": 3646,
"preview": "import { array, description, optional, pipe, strictObject, string } from \"valibot\";\n\nimport {\n createGetCustomComponent"
},
{
"path": "src/rules/no-unnecessary-whitespace.test.ts",
"chars": 25630,
"preview": "import { describe, it } from \"vitest\";\n\nimport { noUnnecessaryWhitespace } from \"better-tailwindcss:rules/no-unnecessary"
},
{
"path": "src/rules/no-unnecessary-whitespace.ts",
"chars": 5180,
"preview": "import { boolean, description, optional, pipe, strictObject } from \"valibot\";\n\nimport { createRule } from \"better-tailwi"
},
{
"path": "src/tailwindcss/canonical-classes.async.v4.ts",
"chars": 2118,
"preview": "import { getUnknownClasses } from \"./unknown-classes.async.v4.js\";\n\nimport type { CanonicalClasses, CanonicalClassOption"
},
{
"path": "src/tailwindcss/canonical-classes.ts",
"chars": 1442,
"preview": "import { resolve } from \"node:path\";\n\nimport { createSyncFn } from \"synckit\";\n\nimport { getWorkerOptions } from \"better-"
},
{
"path": "src/tailwindcss/class-order.async.v3.ts",
"chars": 190,
"preview": "import type { ClassOrder } from \"./class-order.js\";\n\n\nexport function getClassOrder(tailwindContext: any, classes: strin"
},
{
"path": "src/tailwindcss/class-order.async.v4.ts",
"chars": 190,
"preview": "import type { ClassOrder } from \"./class-order.js\";\n\n\nexport function getClassOrder(tailwindContext: any, classes: strin"
},
{
"path": "src/tailwindcss/class-order.ts",
"chars": 1141,
"preview": "import { resolve } from \"node:path\";\n\nimport { createSyncFn } from \"synckit\";\n\nimport { getWorkerOptions } from \"better-"
},
{
"path": "src/tailwindcss/conflicting-classes.async.v4.ts",
"chars": 3735,
"preview": "import type { ConflictingClasses } from \"./conflicting-classes.js\";\n\n\nexport async function getConflictingClasses(tailwi"
},
{
"path": "src/tailwindcss/conflicting-classes.ts",
"chars": 1377,
"preview": "// runner.js\nimport { resolve } from \"node:path\";\n\nimport { createSyncFn } from \"synckit\";\n\nimport { getWorkerOptions } "
},
{
"path": "src/tailwindcss/context.async.v3.ts",
"chars": 959,
"preview": "import { withCache } from \"../async-utils/cache.js\";\nimport { normalize } from \"../async-utils/path.js\";\n\nimport type { "
},
{
"path": "src/tailwindcss/context.async.v4.ts",
"chars": 3072,
"preview": "import { readFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport { pathToFileURL } from \"node:ur"
},
{
"path": "src/tailwindcss/custom-component-classes.async.v3.ts",
"chars": 254,
"preview": "import type { AsyncContext } from \"../utils/context.js\";\nimport type { CustomComponentClasses } from \"./custom-component"
},
{
"path": "src/tailwindcss/custom-component-classes.async.v4.ts",
"chars": 4092,
"preview": "import { readFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nimport { fork } from \"@eslint/css-tre"
},
{
"path": "src/tailwindcss/custom-component-classes.ts",
"chars": 1226,
"preview": "// runner.js\n\n\nimport { resolve } from \"node:path\";\n\nimport { createSyncFn } from \"synckit\";\n\nimport { getWorkerOptions "
},
{
"path": "src/tailwindcss/dissect-classes.async.v3.ts",
"chars": 1665,
"preview": "import { escapeForRegex } from \"../async-utils/escape.js\";\nimport { normalize } from \"../async-utils/path.js\";\nimport { "
},
{
"path": "src/tailwindcss/dissect-classes.async.v4.ts",
"chars": 1394,
"preview": "import { escapeForRegex } from \"../async-utils/escape.js\";\nimport { getCachedRegex } from \"../async-utils/regex.js\";\nimp"
},
{
"path": "src/tailwindcss/dissect-classes.test.ts",
"chars": 5122,
"preview": "import { afterEach, beforeEach, describe, expect, it } from \"vitest\";\n\nimport { createGetDissectedClasses } from \"better"
},
{
"path": "src/tailwindcss/dissect-classes.ts",
"chars": 1497,
"preview": "import { resolve } from \"node:path\";\n\nimport { createSyncFn } from \"synckit\";\n\nimport { getWorkerOptions } from \"better-"
},
{
"path": "src/tailwindcss/prefix.async.v3.ts",
"chars": 233,
"preview": "import type { Prefix } from \"./prefix.js\";\n\n\nexport function getPrefix(tailwindContext: any): Prefix {\n return tailwind"
},
{
"path": "src/tailwindcss/prefix.async.v4.ts",
"chars": 271,
"preview": "import type { Prefix, Suffix } from \"./prefix.js\";\n\n\nexport function getPrefix(tailwindContext: any): Prefix {\n return "
},
{
"path": "src/tailwindcss/prefix.ts",
"chars": 1064,
"preview": "import { resolve } from \"node:path\";\n\nimport { createSyncFn } from \"synckit\";\n\nimport { getWorkerOptions } from \"better-"
},
{
"path": "src/tailwindcss/tailwind.async.worker.v3.ts",
"chars": 2738,
"preview": "import { runAsWorker } from \"synckit\";\n\nimport { getClassOrder } from \"./class-order.async.v3.js\";\nimport { createTailwi"
},
{
"path": "src/tailwindcss/tailwind.async.worker.v4.ts",
"chars": 2863,
"preview": "import { runAsWorker } from \"synckit\";\n\nimport { getCanonicalClasses } from \"./canonical-classes.async.v4.js\";\nimport { "
},
{
"path": "src/tailwindcss/unknown-classes.async.v3.ts",
"chars": 620,
"preview": "import { normalize } from \"../async-utils/path.js\";\n\nimport type { AsyncContext } from \"../utils/context.js\";\nimport typ"
},
{
"path": "src/tailwindcss/unknown-classes.async.v4.ts",
"chars": 275,
"preview": "import type { UnknownClass } from \"./unknown-classes.js\";\n\n\nexport function getUnknownClasses(tailwindContext: any, clas"
},
{
"path": "src/tailwindcss/unknown-classes.ts",
"chars": 1150,
"preview": "import { resolve } from \"node:path\";\n\nimport { createSyncFn } from \"synckit\";\n\nimport { getWorkerOptions } from \"better-"
},
{
"path": "src/tailwindcss/variant-order.async.v3.ts",
"chars": 123,
"preview": "import type { VariantOrder } from \"./variant-order.js\";\n\n\nexport function getVariantOrder(): VariantOrder {\n return {};"
},
{
"path": "src/tailwindcss/variant-order.async.v4.ts",
"chars": 1570,
"preview": "import { VARIANT_ORDER_FLAGS } from \"../async-utils/order.js\";\n\nimport type { VariantOrder } from \"./variant-order.js\";\n"
},
{
"path": "src/tailwindcss/variant-order.ts",
"chars": 1156,
"preview": "import { resolve } from \"node:path\";\n\nimport { createSyncFn } from \"synckit\";\n\nimport { getWorkerOptions } from \"better-"
},
{
"path": "src/types/ast.ts",
"chars": 1767,
"preview": "export type LiteralValueQuotes = \"'\" | \"\\\"\" | \"`\";\n\nexport interface Range {\n range: [number, number];\n}\n\nexport interf"
},
{
"path": "src/types/async.ts",
"chars": 268,
"preview": "export type Async<Fn extends (...args: any[]) => any> = (...params: Parameters<Fn>) => Promise<ReturnType<Fn>>;\n\nexport "
},
{
"path": "src/types/estree.ts",
"chars": 220,
"preview": "import type { Rule } from \"eslint\";\n\n\ntype Nullable<Object extends object> = {\n [Key in keyof Object]: Object[Key] | nu"
},
{
"path": "src/types/rule.ts",
"chars": 7281,
"preview": "import type { JSRuleDefinition } from \"eslint\";\nimport type { BaseIssue, BaseSchema, Default, InferOutput, OptionalSchem"
},
{
"path": "src/utils/ast.ts",
"chars": 380,
"preview": "import type { Rule } from \"eslint\";\nimport type { SourceLocation } from \"estree\";\n\n\nexport function getLocByRange(ctx: R"
},
{
"path": "src/utils/class.ts",
"chars": 896,
"preview": "import type { Context } from \"better-tailwindcss:types/rule.js\";\n\n\ninterface ClassParts {\n base: string;\n important: ["
},
{
"path": "src/utils/context.ts",
"chars": 3868,
"preview": "import { resolve } from \"node:path\";\n\nimport { withCache } from \"better-tailwindcss:utils/cache.js\";\nimport { findPathRe"
},
{
"path": "src/utils/lint.ts",
"chars": 3044,
"preview": "import { splitClasses, splitWhitespaces } from \"better-tailwindcss:utils/utils.js\";\n\nimport type { Literal } from \"bette"
},
{
"path": "src/utils/matchers.test.ts",
"chars": 19417,
"preview": "import { parse } from \"espree\";\nimport { assert, describe, expect, it } from \"vitest\";\n\nimport {\n getESObjectPath,\n ha"
},
{
"path": "src/utils/matchers.ts",
"chars": 6426,
"preview": "import { hasESNodeParentExtension, isESSimpleStringLiteral } from \"better-tailwindcss:parsers/es.js\";\nimport { MATCHER_R"
},
{
"path": "src/utils/quotes.test.ts",
"chars": 594,
"preview": "import { describe, expect, it } from \"vitest\";\n\nimport { escapeNestedQuotes } from \"better-tailwindcss:utils/quotes.js\";"
},
{
"path": "src/utils/quotes.ts",
"chars": 374,
"preview": "import type { LiteralValueQuotes } from \"better-tailwindcss:types/ast.js\";\n\n\nexport function escapeNestedQuotes(content:"
},
{
"path": "src/utils/rule.ts",
"chars": 14175,
"preview": "import { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\n\nimport { toJsonSchema } from \"@va"
},
{
"path": "src/utils/selectors.ts",
"chars": 263,
"preview": "import type { Selector, SelectorByKind, SelectorKind } from \"better-tailwindcss:types/rule.js\";\n\n\nexport function isSele"
},
{
"path": "src/utils/utils.test.ts",
"chars": 1187,
"preview": "import { describe, expect, it } from \"vitest\";\n\nimport { matchesName } from \"better-tailwindcss:utils/utils.js\";\n\nimport"
},
{
"path": "src/utils/utils.ts",
"chars": 6042,
"preview": "import { getCachedRegex } from \"better-tailwindcss:utils/regex.js\";\n\nimport type { MessageStyleOption } from \"better-tai"
},
{
"path": "src/utils/valibot.ts",
"chars": 296,
"preview": "export function removeDefaults(entries: Record<string, any>) {\n const newEntries = { ...entries };\n for(const key in n"
},
{
"path": "src/utils/version.ts",
"chars": 312,
"preview": "export function parseSemanticVersion(version: string): { major: number; minor: number; patch: number; identifier?: strin"
},
{
"path": "src/utils/warn.ts",
"chars": 204,
"preview": "const warnedMessages = new Set<string>();\n\nexport function warnOnce(message: string) {\n if(!warnedMessages.has(message)"
},
{
"path": "tests/e2e/commonjs/eslint.config.js",
"chars": 305,
"preview": "const eslintParserHTML = require(\"@html-eslint/parser\");\nconst eslintPluginBetterTailwindcss = require(\"eslint-plugin-be"
},
{
"path": "tests/e2e/commonjs/package.json",
"chars": 25,
"preview": "{\n \"type\": \"commonjs\"\n}\n"
},
{
"path": "tests/e2e/commonjs/test.html",
"chars": 113,
"preview": "<!DOCTYPE html>\n <head></head>\n <body>\n <div class=\" hover:font-bold font-bold font-bold\">\n </body>\n</html>"
},
{
"path": "tests/e2e/commonjs/test.test.ts",
"chars": 928,
"preview": "import { loadESLint } from \"eslint\";\nimport { describe, expect, it } from \"vitest\";\n\n\ndescribe(\"e2e/commonjs\", async () "
}
]
// ... and 23 more files (download for full content)
About this extraction
This page contains the full source code of the schoero/eslint-plugin-readable-tailwind GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 223 files (937.5 KB), approximately 246.5k tokens, and a symbol index with 590 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.