Skip to content

feat/no-unregistered-classes #89

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 26 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
989e8db
feat: add new rule `no-unregistered-classes`
schoero Apr 24, 2025
a16f0a8
docs: add new rule
schoero Apr 24, 2025
5165128
test: add more tests to implement
schoero Apr 24, 2025
0bf8af9
docs: update rule documentation
schoero Apr 24, 2025
3c9a25c
test: add config tests
schoero Apr 24, 2025
068f8c8
style: eslint fixes
schoero Apr 24, 2025
e63bd5f
chore: ignore eslint violations
schoero Apr 24, 2025
c7a5cfe
fix: no unregistered classes in v3
schoero Apr 29, 2025
edddc6c
fix: remove type
schoero Apr 29, 2025
3b0a4a4
docs: fix spacing
schoero Apr 29, 2025
f1bcb29
fix: change import
schoero Apr 29, 2025
6cf5a4c
chore: remove unused type
schoero Apr 29, 2025
64ba8f6
docs: mention regular expressions
schoero Apr 29, 2025
6b5db3c
refactor: change option name to `ignore`
schoero Apr 29, 2025
30a5cb6
test: add pending tests
schoero Apr 29, 2025
ac95a11
refactor: cache whole config
schoero Apr 29, 2025
7d004b5
test: allow tests without autofixes
schoero Apr 29, 2025
872bea3
test: fix tests without autofixes
schoero Apr 29, 2025
2a69ae9
chore: beta release
schoero Apr 29, 2025
62d416e
fix: don't enable new rule in predefined configs
schoero Apr 29, 2025
dccd9d1
chore: remove commented out code
schoero Apr 29, 2025
90065ff
Merge branch 'main' into feat/no-unregistered-classes
schoero Apr 29, 2025
595a5cc
refactor: add default ignore list
schoero May 6, 2025
4dab530
fix: highlight correct index the string is included multiple times
schoero May 6, 2025
8d2ad53
test: does no longer fail
schoero May 6, 2025
19fc709
fix: location for first class of string
schoero May 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@
<br/>
<br/>

ESLint plugin to automatically break up long tailwind class strings into multiple lines based on a specified print width or class count. This improves readability and eliminates horizontal scrolling.
In addition it sorts the classes logically, removes unnecessary whitespaces and duplicate classes and groups the classes by their variants. It works in React, Solid.js, Qwik, Svelte, Vue, Angular, HTML, JavaScript and TypeScript projects.
ESLint plugin with a strong focus on improving readability of lengthy tailwindcss class strings. The core feature is to automatically break up long tailwind class strings into multiple lines based on a specified print width or class count. This makes your code cleaner and easier to read while eliminating the need for horizontal scrolling.
Beyond formatting, it also sorts classes in a logical order, removes duplicates and unnecessary whitespace, and groups classes by their variants. It works in React, Solid.js, Qwik, Svelte, Vue, Angular, HTML, JavaScript and TypeScript projects.

<br/>
<br/>
Expand Down Expand Up @@ -161,6 +161,7 @@ The following table shows the available rules and if they are enabled by default
| [no-unnecessary-whitespace](docs/rules/no-unnecessary-whitespace.md) | Disallow unnecessary whitespace in tailwind classes. | ✔ | ✔ | ✔ |
| [sort-classes](docs/rules/sort-classes.md) | Enforce a consistent order for tailwind classes. | ✔ | ✔ | ✔ |
| [no-duplicate-classes](docs/rules/no-duplicate-classes.md) | Remove duplicate classes. | ✔ | ✔ | ✔ |
| [no-unregistered-classes](docs/rules/no-unregistered-classes.md) | Report classes not registered with tailwindcss. | | | |

<br/>
<br/>
Expand Down Expand Up @@ -195,7 +196,7 @@ Read the [API documentation](./docs/api/defaults.md) to learn how to override or

##### Auto-fix on save

All rules are intended to automatically fix the tailwind classes. If you have installed the [VSCode ESLint plugin](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint), you can configure it to automatically fix the classes on save by adding the following options to your `.vscode/settings.json`:
Most rules are intended to automatically fix the tailwind classes. If you have installed the [VSCode ESLint plugin](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint), you can configure it to automatically fix the classes on save by adding the following options to your `.vscode/settings.json`:

```jsonc
{
Expand Down
70 changes: 70 additions & 0 deletions docs/rules/no-unregistered-classes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# readable-tailwind/no-unregistered-classes

Disallow unregistered classes in tailwindcss class strings. Unregistered classes are classes that are not defined in your tailwind config file and therefore not recognized by tailwindcss.

<br/>

## Options

### `ignore`

List of classes that should not report an error. The entries in this list are treated as regular expressions.

The rule works, by checking the output that a given class will produce. By default, the utilities `group` and `peer` are ignored, because they don't produce any css output.

If you want to customize the ignore list, it is recommended to add the default options to the ignore override. You can use the function `getDefaultIgnoredUnregisteredClasses()` exported from `/api/defaults` to get the original ignore list.

**Type**: `string[]`
**Default**: `["^group(?:\\/(\\S*))?$", "^peer(?:\\/(\\S*))?$"]`

<br/>

### `attributes`

The name of the attribute that contains the tailwind classes. This can also be set globally via the [`settings` object](../settings/settings.md#attributes).

**Type**: Array of [Name](../concepts/concepts.md#name), [Regex](../concepts/concepts.md#regular-expressions) or [Matchers](../concepts/concepts.md#matchers)
**Default**: [Name](../concepts/concepts.md#name) for `"class"` and [strings Matcher](../concepts/concepts.md#types-of-matchers) for `"class", "className"`

<br/>

### `callees`

List of function names which arguments should also get linted. This can also be set globally via the [`settings` object](../settings/settings.md#callees).

**Type**: Array of [Name](../concepts/concepts.md#name), [Regex](../concepts/concepts.md#regular-expressions) or [Matchers](../concepts/concepts.md#matchers)
**Default**: [Matchers](../concepts/concepts.md#types-of-matchers) for `"cc", "clb", "clsx", "cn", "cnb", "ctl", "cva", "cx", "dcnb", "objstr", "tv", "twJoin", "twMerge"`

<br/>

### `variables`

List of variable names whose initializer should also get linted. This can also be set globally via the [`settings` object](../settings/settings.md#variables).

**Type**: Array of [Name](../concepts/concepts.md#name), [Regex](../concepts/concepts.md#regular-expressions) or [Matchers](../concepts/concepts.md#matchers)
**Default**: [strings Matcher](../concepts/concepts.md#types-of-matchers) for `"className", "classNames", "classes", "style", "styles"`

<br/>

### `tags`

List of template literal tag names whose content should get linted. This can also be set globally via the [`settings` object](../settings/settings.md#tags).

**Type**: Array of [Name](../concepts/concepts.md#name), [Regex](../concepts/concepts.md#regular-expressions) or [Matchers](../concepts/concepts.md#matchers)
**Default**: None

Note: When using the `tags` option, it is recommended to use the [strings Matcher](../concepts/concepts.md#types-of-matchers) for your tag names. This will ensure that nested expressions get linted correctly.

<br/>

## Examples

```tsx
// ❌ BAD: unregistered class
<div class="my-class" />;
```

```tsx
// ✅ GOOD: only valid tailwindcss classes
<div class="font-bold hover:underline" />;
```
26 changes: 24 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "2.1.1",
"version": "2.2.0-no-unregistered-classes.0",
"type": "module",
"name": "eslint-plugin-readable-tailwind",
"description": "auto-wraps tailwind classes after a certain print width or class count into multiple lines to improve readability.",
Expand Down
5 changes: 5 additions & 0 deletions src/api/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
DEFAULT_TAG_NAMES,
DEFAULT_VARIABLE_NAMES
} from "readable-tailwind:options:default-options.js";
import { DEFAULT_IGNORED_UNREGISTERED_CLASSES } from "readable-tailwind:rules:tailwind-no-unregistered-classes.js";


export function getDefaultCallees() {
Expand All @@ -21,3 +22,7 @@ export function getDefaultVariables() {
export function getDefaultTags() {
return DEFAULT_TAG_NAMES;
}

export function getDefaultIgnoredUnregisteredClasses() {
return DEFAULT_IGNORED_UNREGISTERED_CLASSES;
}
2 changes: 2 additions & 0 deletions src/configs/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { tailwindMultiline } from "readable-tailwind:rules:tailwind-multiline.js";
import { tailwindNoDuplicateClasses } from "readable-tailwind:rules:tailwind-no-duplicate-classes.js";
import { tailwindNoUnnecessaryWhitespace } from "readable-tailwind:rules:tailwind-no-unnecessary-whitespace.js";
import { tailwindNoUnregisteredClasses } from "readable-tailwind:rules:tailwind-no-unregistered-classes.js";
import { tailwindSortClasses } from "readable-tailwind:rules:tailwind-sort-classes.js";

import type { ESLint } from "eslint";
Expand Down Expand Up @@ -31,6 +32,7 @@ export const config = {
[tailwindMultiline.name]: tailwindMultiline.rule,
[tailwindNoDuplicateClasses.name]: tailwindNoDuplicateClasses.rule,
[tailwindNoUnnecessaryWhitespace.name]: tailwindNoUnnecessaryWhitespace.rule,
[tailwindNoUnregisteredClasses.name]: tailwindNoUnregisteredClasses.rule,
[tailwindSortClasses.name]: tailwindSortClasses.rule
}
} satisfies ESLint.Plugin;
14 changes: 14 additions & 0 deletions src/options/descriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,3 +272,17 @@ export const TAG_SCHEMA = {
type: "array"
}
} satisfies Rule.RuleMetaData["schema"];

export const ENTRYPOINT_SCHEMA = {
entryPoint: {
description: "The path to the css entry point of the project. If not specified, the plugin will fall back to the default tailwind classes.",
type: "string"
}
};

export const TAILWIND_CONFIG_SCHEMA = {
tailwindConfig: {
description: "The path to the tailwind config file. If not specified, the plugin will try to find it automatically or falls back to the default configuration.",
type: "string"
}
};
Loading