tailwint 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -7
- package/bin/tailwint.js +15 -1
- package/dist/index.js +2 -16
- package/package.json +17 -5
package/README.md
CHANGED
|
@@ -1,8 +1,37 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/header.svg" alt="tailwint">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/tailwint"><img src="https://img.shields.io/npm/v/tailwint?color=0ea5e9&label=npm" alt="npm version"></a>
|
|
7
|
+
<a href="https://github.com/peterwangsc/tailwint/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/tailwint?color=a78bfa" alt="license"></a>
|
|
8
|
+
<a href="https://www.npmjs.com/package/tailwint"><img src="https://img.shields.io/npm/dm/tailwint?color=f472b6" alt="downloads"></a>
|
|
9
|
+
</p>
|
|
4
10
|
|
|
5
|
-
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
The same diagnostics VS Code shows — but from the command line. Catches class conflicts, suggests canonical rewrites, and auto-fixes everything. Built on the official `@tailwindcss/language-server`.
|
|
14
|
+
|
|
15
|
+
**Works with Tailwind CSS v4.**
|
|
16
|
+
|
|
17
|
+
## What it catches
|
|
18
|
+
|
|
19
|
+
tailwint detects two categories of issues:
|
|
20
|
+
|
|
21
|
+
**⚡ Conflicts** — classes that apply the same CSS properties, where the last one wins and the rest are dead code:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
⚡ 3:21 conflict 'w-full' applies the same CSS properties as 'w-auto'
|
|
25
|
+
⚡ 3:28 conflict 'w-auto' applies the same CSS properties as 'w-full'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**○ Canonical** — classes that can be written in a shorter or more idiomatic form:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
○ 3:21 canonical The class `flex-shrink-0` can be written as `shrink-0`
|
|
32
|
+
○ 3:35 canonical The class `z-[1]` can be written as `z-1`
|
|
33
|
+
○ 3:41 canonical The class `min-w-[200px]` can be written as `min-w-50`
|
|
34
|
+
```
|
|
6
35
|
|
|
7
36
|
## Install
|
|
8
37
|
|
|
@@ -13,18 +42,82 @@ npm install -D tailwint @tailwindcss/language-server
|
|
|
13
42
|
## Usage
|
|
14
43
|
|
|
15
44
|
```bash
|
|
16
|
-
#
|
|
45
|
+
# Scan default file types (tsx, jsx, html, vue, svelte, css)
|
|
17
46
|
npx tailwint
|
|
18
47
|
|
|
19
|
-
#
|
|
48
|
+
# Scan specific files
|
|
20
49
|
npx tailwint "src/**/*.tsx"
|
|
21
50
|
|
|
22
|
-
# Auto-fix issues
|
|
51
|
+
# Auto-fix all issues
|
|
23
52
|
npx tailwint --fix
|
|
24
53
|
|
|
25
54
|
# Fix specific files
|
|
26
55
|
npx tailwint --fix "app/**/*.tsx"
|
|
56
|
+
|
|
57
|
+
# Verbose LSP logging
|
|
58
|
+
DEBUG=1 npx tailwint
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Example output
|
|
62
|
+
|
|
27
63
|
```
|
|
64
|
+
~≈∼〜~≈∼〜~≈ tailwint ∼〜~≈∼〜~≈∼~
|
|
65
|
+
tailwind css linter // powered by the official lsp
|
|
66
|
+
|
|
67
|
+
✔ language server ready ~≈∼〜~≈∼〜~≈∼〜
|
|
68
|
+
✔ 42 files analyzed ~≈∼〜~≈∼〜~≈∼〜~≈
|
|
69
|
+
|
|
70
|
+
42 files scanned // 8 conflicts │ 12 canonical
|
|
71
|
+
|
|
72
|
+
┌ components/Card.tsx (3)
|
|
73
|
+
⚡ 5:21 conflict 'w-full' applies the same CSS properties as 'w-auto'
|
|
74
|
+
○ 5:35 canonical The class `flex-shrink-0` can be written as `shrink-0`
|
|
75
|
+
○ 5:49 canonical The class `z-[1]` can be written as `z-1`
|
|
76
|
+
└~≈∼
|
|
77
|
+
|
|
78
|
+
≈∼〜~≈ ✘ FAIL 20 issues in 3 files 2.1s ≈∼〜~≈
|
|
79
|
+
run with --fix to auto-fix
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
With `--fix`:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
⚙ FIX conflicts first, then canonical
|
|
86
|
+
|
|
87
|
+
✔ ┃━━━━━━━━━━━━━━━━━━┃ Card.tsx 3 fixed
|
|
88
|
+
✔ ┃━━━━━━━━━━━━━━━━━━┃ Header.tsx 12 fixed
|
|
89
|
+
✔ ┃━━━━━━━━━━━━━━━━━━┃ Sidebar.tsx 5 fixed
|
|
90
|
+
|
|
91
|
+
≈∼〜~≈ ✔ FIXED 20 of 20 issues across 3 files 3.4s ≈∼〜~≈
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Supported file types
|
|
95
|
+
|
|
96
|
+
| Extension | Language ID | Notes |
|
|
97
|
+
|-----------|-----------|-------|
|
|
98
|
+
| `.tsx` | typescriptreact | React / Next.js components |
|
|
99
|
+
| `.jsx` | javascriptreact | React components |
|
|
100
|
+
| `.html` | html | Static HTML files |
|
|
101
|
+
| `.vue` | html | Vue single-file components |
|
|
102
|
+
| `.svelte` | html | Svelte components |
|
|
103
|
+
| `.css` | css | `@apply` directives and Tailwind at-rules |
|
|
104
|
+
|
|
105
|
+
## Tailwind v4 support
|
|
106
|
+
|
|
107
|
+
tailwint fully supports Tailwind CSS v4 features:
|
|
108
|
+
|
|
109
|
+
- **Opacity shorthand** — `bg-red-500/50`, `text-blue-500/75`
|
|
110
|
+
- **`size-*` utility** — `size-10`, `size-full`
|
|
111
|
+
- **Container queries** — `@container`, `@lg:flex`, `@md:grid`
|
|
112
|
+
- **`has-*` / `not-*` variants** — `has-checked:bg-blue-500`, `not-disabled:opacity-100`
|
|
113
|
+
- **`aria-*` variants** — `aria-expanded:bg-blue-500`, `aria-disabled:opacity-50`
|
|
114
|
+
- **`data-*` variants** — `data-[state=open]:bg-blue-500`
|
|
115
|
+
- **`supports-*` variants** — `supports-[display:grid]:grid`
|
|
116
|
+
- **`forced-colors` variant** — `forced-colors:bg-[ButtonFace]`
|
|
117
|
+
- **Logical properties** — `ms-4`, `me-4`, `ps-4`, `pe-4`
|
|
118
|
+
- **Text wrap utilities** — `text-balance`, `text-pretty`, `text-nowrap`
|
|
119
|
+
- **Named groups/peers** — `group/sidebar`, `group-hover/sidebar:bg-blue-500`
|
|
120
|
+
- **CSS-first config** — `@import "tailwindcss"` with `@theme` directive
|
|
28
121
|
|
|
29
122
|
## Programmatic API
|
|
30
123
|
|
|
@@ -38,9 +131,54 @@ const exitCode = await run({
|
|
|
38
131
|
});
|
|
39
132
|
```
|
|
40
133
|
|
|
134
|
+
### Options
|
|
135
|
+
|
|
136
|
+
| Option | Type | Default | Description |
|
|
137
|
+
|--------|------|---------|-------------|
|
|
138
|
+
| `patterns` | `string[]` | `["**/*.{tsx,jsx,html,vue,svelte,css}"]` | Glob patterns for files to scan |
|
|
139
|
+
| `fix` | `boolean` | `false` | Auto-fix issues using LSP code actions |
|
|
140
|
+
| `cwd` | `string` | `process.cwd()` | Working directory for glob resolution and LSP root |
|
|
141
|
+
|
|
142
|
+
### Exports
|
|
143
|
+
|
|
144
|
+
| Export | Description |
|
|
145
|
+
|--------|-------------|
|
|
146
|
+
| `run(options?)` | Run the linter, returns exit code |
|
|
147
|
+
| `applyEdits(content, edits)` | Apply LSP text edits to a string |
|
|
148
|
+
| `TextEdit` | TypeScript type for LSP text edits |
|
|
149
|
+
|
|
150
|
+
## CI integration
|
|
151
|
+
|
|
152
|
+
tailwint exits with meaningful codes for CI pipelines:
|
|
153
|
+
|
|
154
|
+
| Exit code | Meaning |
|
|
155
|
+
|-----------|---------|
|
|
156
|
+
| `0` | No issues found, or all issues fixed with `--fix` |
|
|
157
|
+
| `1` | Issues found (without `--fix`) |
|
|
158
|
+
| `2` | Fatal error (language server not found, crash) |
|
|
159
|
+
|
|
160
|
+
### GitHub Actions
|
|
161
|
+
|
|
162
|
+
```yaml
|
|
163
|
+
- name: Lint Tailwind classes
|
|
164
|
+
run: npx tailwint
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Pre-commit hook
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
npx tailwint --fix && git add -u
|
|
171
|
+
```
|
|
172
|
+
|
|
41
173
|
## How it works
|
|
42
174
|
|
|
43
|
-
|
|
175
|
+
1. **Boot** — spawns `@tailwindcss/language-server` over stdio
|
|
176
|
+
2. **Open** — sends all matched files to the server via `textDocument/didOpen`
|
|
177
|
+
3. **Analyze** — waits for `textDocument/publishDiagnostics` notifications (event-driven, no polling)
|
|
178
|
+
4. **Report** — collects diagnostics, categorizes as conflicts or canonical
|
|
179
|
+
5. **Fix** *(if `--fix`)* — requests `textDocument/codeAction` quickfixes and applies edits in a loop until no diagnostics remain
|
|
180
|
+
|
|
181
|
+
The fix loop is unbounded — it keeps applying edits until the file stabilizes. A single pass may not resolve everything (e.g., fixing a conflict can reveal a canonical issue underneath), so the loop continues as long as edits produce changes.
|
|
44
182
|
|
|
45
183
|
## Requirements
|
|
46
184
|
|
package/bin/tailwint.js
CHANGED
|
@@ -1,2 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "../dist/index.js";
|
|
2
|
+
import { run } from "../dist/index.js";
|
|
3
|
+
import { c, isTTY } from "../dist/ui.js";
|
|
4
|
+
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
const fix = args.includes("--fix");
|
|
7
|
+
const patterns = args.filter((a) => a !== "--fix");
|
|
8
|
+
|
|
9
|
+
run({ fix, patterns: patterns.length > 0 ? patterns : undefined }).then(
|
|
10
|
+
(code) => process.exit(code),
|
|
11
|
+
(err) => {
|
|
12
|
+
console.error(`\n ${c.red}${c.bold}tailwint crashed:${c.reset} ${err}`);
|
|
13
|
+
process.stderr.write(isTTY ? "\x1b[?25h" : "");
|
|
14
|
+
process.exit(2);
|
|
15
|
+
},
|
|
16
|
+
);
|
package/dist/index.js
CHANGED
|
@@ -13,10 +13,10 @@ import { readFileSync } from "fs";
|
|
|
13
13
|
import { glob } from "glob";
|
|
14
14
|
import { startServer, send, notify, shutdown, fileUri, langId, diagnosticsReceived, waitForProjectReady, waitForDiagnosticCount, resetState, } from "./lsp.js";
|
|
15
15
|
import { fixFile } from "./edits.js";
|
|
16
|
-
import { c,
|
|
16
|
+
import { c, setTitle, windTrail, braille, windWave, dots, tick, advanceTick, startSpinner, progressBar, banner, fileBadge, diagLine, rainbowText, celebrationAnimation, } from "./ui.js";
|
|
17
17
|
// Re-export for tests
|
|
18
18
|
export { applyEdits } from "./edits.js";
|
|
19
|
-
const DEFAULT_PATTERNS = ["**/*.{tsx,jsx,html,vue,svelte}"];
|
|
19
|
+
const DEFAULT_PATTERNS = ["**/*.{tsx,jsx,html,vue,svelte,css}"];
|
|
20
20
|
export async function run(options = {}) {
|
|
21
21
|
resetState();
|
|
22
22
|
const t0 = Date.now();
|
|
@@ -194,17 +194,3 @@ export async function run(options = {}) {
|
|
|
194
194
|
await shutdown();
|
|
195
195
|
return 1;
|
|
196
196
|
}
|
|
197
|
-
// CLI entry point — only runs when executed directly, not when imported
|
|
198
|
-
const isCLI = process.argv[1] != null &&
|
|
199
|
-
(import.meta.url === new URL(process.argv[1], "file:").href ||
|
|
200
|
-
process.argv[1].endsWith("/tailwint.js"));
|
|
201
|
-
if (isCLI) {
|
|
202
|
-
const args = process.argv.slice(2);
|
|
203
|
-
const fix = args.includes("--fix");
|
|
204
|
-
const patterns = args.filter((a) => a !== "--fix");
|
|
205
|
-
run({ fix, patterns: patterns.length > 0 ? patterns : undefined }).then((code) => process.exit(code), (err) => {
|
|
206
|
-
console.error(`\n ${c.red}${c.bold}tailwint crashed:${c.reset} ${err}`);
|
|
207
|
-
process.stderr.write(isTTY ? "\x1b[?25h" : "");
|
|
208
|
-
process.exit(2);
|
|
209
|
-
});
|
|
210
|
-
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tailwint",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Tailwind CSS linter for CI — drives the official language server to catch class issues and auto-fix them",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Peter Wang",
|
|
7
|
+
"homepage": "https://github.com/peterwangsc/tailwint",
|
|
8
|
+
"bugs": "https://github.com/peterwangsc/tailwint/issues",
|
|
7
9
|
"repository": {
|
|
8
10
|
"type": "git",
|
|
9
11
|
"url": "git+https://github.com/peterwangsc/tailwint.git"
|
|
@@ -14,25 +16,35 @@
|
|
|
14
16
|
"lint",
|
|
15
17
|
"linter",
|
|
16
18
|
"ci",
|
|
19
|
+
"autofix",
|
|
17
20
|
"language-server",
|
|
18
|
-
"lsp"
|
|
21
|
+
"lsp",
|
|
22
|
+
"tailwind-v4",
|
|
23
|
+
"css",
|
|
24
|
+
"diagnostics",
|
|
25
|
+
"code-quality"
|
|
19
26
|
],
|
|
20
27
|
"type": "module",
|
|
21
28
|
"main": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
22
30
|
"exports": {
|
|
23
|
-
".":
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"default": "./dist/index.js"
|
|
34
|
+
}
|
|
24
35
|
},
|
|
25
36
|
"bin": {
|
|
26
|
-
"tailwint": "
|
|
37
|
+
"tailwint": "bin/tailwint.js"
|
|
27
38
|
},
|
|
28
39
|
"files": [
|
|
29
40
|
"bin",
|
|
30
41
|
"dist"
|
|
31
42
|
],
|
|
32
43
|
"scripts": {
|
|
44
|
+
"clean": "rm -rf dist",
|
|
33
45
|
"build": "tsc",
|
|
34
46
|
"test": "tsx --test src/*.test.ts",
|
|
35
|
-
"prepublishOnly": "npm run build"
|
|
47
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
36
48
|
},
|
|
37
49
|
"dependencies": {
|
|
38
50
|
"glob": "^13.0.6"
|