tailwint 1.1.9 → 1.1.11
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 +10 -6
- package/dist/edits.d.ts +5 -1
- package/dist/edits.js +2 -2
- package/dist/index.js +2 -2
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
<img src="assets/header.svg" alt="tailwint">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
+
<p align="center">
|
|
6
|
+
<strong>tail</strong>wind + l<strong>int</strong> = <strong>tailwint</strong> ~≈∼〜 a tiny linter for your Tailwind CSS
|
|
7
|
+
</p>
|
|
8
|
+
|
|
5
9
|
<p align="center">
|
|
6
10
|
<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
11
|
<a href="https://github.com/peterwangsc/tailwint/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/tailwint?color=a78bfa" alt="license"></a>
|
|
@@ -10,7 +14,7 @@
|
|
|
10
14
|
|
|
11
15
|
---
|
|
12
16
|
|
|
13
|
-
The same diagnostics VS Code shows — but from the command line. Catches class conflicts, suggests canonical rewrites, and auto-fixes everything.
|
|
17
|
+
The same diagnostics VS Code shows — but from the command line. Catches class conflicts, suggests canonical rewrites, and auto-fixes everything. Powered by the official `@tailwindcss/language-server` — not a custom parser, not a regex hack.
|
|
14
18
|
|
|
15
19
|
**Works with Tailwind CSS v4.**
|
|
16
20
|
|
|
@@ -156,11 +160,11 @@ const exitCode = await run({
|
|
|
156
160
|
|
|
157
161
|
tailwint exits with meaningful codes for CI pipelines:
|
|
158
162
|
|
|
159
|
-
| Exit code | Meaning
|
|
160
|
-
| --------- |
|
|
161
|
-
| `0` | No issues found, or all issues fixed with `--fix`
|
|
162
|
-
| `1` | Issues found
|
|
163
|
-
| `2` | Fatal error (language server not found, crash)
|
|
163
|
+
| Exit code | Meaning |
|
|
164
|
+
| --------- | ---------------------------------------------------------------- |
|
|
165
|
+
| `0` | No issues found, or all issues fixed with `--fix` |
|
|
166
|
+
| `1` | Issues found, or unfixable issues remain after `--fix` |
|
|
167
|
+
| `2` | Fatal error (language server not found, crash) |
|
|
164
168
|
|
|
165
169
|
### GitHub Actions
|
|
166
170
|
|
package/dist/edits.d.ts
CHANGED
|
@@ -15,4 +15,8 @@ export interface TextEdit {
|
|
|
15
15
|
newText: string;
|
|
16
16
|
}
|
|
17
17
|
export declare function applyEdits(content: string, edits: TextEdit[]): string;
|
|
18
|
-
export
|
|
18
|
+
export interface FixResult {
|
|
19
|
+
initial: number;
|
|
20
|
+
remaining: number;
|
|
21
|
+
}
|
|
22
|
+
export declare function fixFile(filePath: string, initialDiags: any[], fileContents: Map<string, string>, version: Map<string, number>, onPass?: (pass: number) => void): Promise<FixResult>;
|
package/dist/edits.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{writeFileSync as v}from"fs";import{send as D,notify as $,fileUri as B,waitForDiagnostic as
|
|
2
|
-
`&&r.push(e+1);function c(e,o){return e>=r.length?t.length:Math.min(r[e]+o,t.length)}const a=n.map(e=>({start:c(e.range.start.line,e.range.start.character),end:c(e.range.end.line,e.range.end.character),newText:e.newText}));a.sort((e,o)=>e.start-o.start);const u=[];let i=0;for(const e of a)e.start>i&&u.push(t.slice(i,e.start)),u.push(e.newText),i=e.end;return i<t.length&&u.push(t.slice(i)),u.join("")}function
|
|
1
|
+
import{writeFileSync as v}from"fs";import{send as D,notify as $,fileUri as B,waitForDiagnostic as F}from"./lsp.js";function k(t,n){if(n.length===0)return t;const r=[0];for(let e=0;e<t.length;e++)t[e]===`
|
|
2
|
+
`&&r.push(e+1);function c(e,o){return e>=r.length?t.length:Math.min(r[e]+o,t.length)}const a=n.map(e=>({start:c(e.range.start.line,e.range.start.character),end:c(e.range.end.line,e.range.end.character),newText:e.newText}));a.sort((e,o)=>e.start-o.start);const u=[];let i=0;for(const e of a)e.start>i&&u.push(t.slice(i,e.start)),u.push(e.newText),i=e.end;return i<t.length&&u.push(t.slice(i)),u.join("")}function C(t){return`${t.start.line}:${t.start.character}-${t.end.line}:${t.end.character}`}function b(t,n){return t.line<n.line||t.line===n.line&&t.character<=n.character}function M(t,n){return b(t.range.start,n.range.start)&&b(n.range.end,t.range.end)}function R(t){const n=[];for(const r of t)t.some(a=>a!==r&&M(a,r))||n.push(r);return n}async function G(t,n,r,c,a){const u=process.env.DEBUG==="1",i=B(t);let e=r.get(t),o=c.get(t);const y=n.length;let l=n;for(let d=0;l.length>0;d++){a?.(d+1),u&&console.error(` pass ${d+1}: ${l.length} remaining`);const E=await Promise.all(l.map(s=>D("textDocument/codeAction",{textDocument:{uri:i},range:s.range,context:{diagnostics:[s],only:["quickfix"]}}).catch(()=>null))),m=new Map;for(let s=0;s<l.length;s++){const f=E[s];if(!f||f.length===0)continue;const h=f[0],x=h.edit?.changes?.[i]||h.edit?.documentChanges?.[0]?.edits||[];if(x.length!==0)for(const p of x){const w=C(p.range);m.set(w,p)}}let g=[...m.values()];if(g.length===0)break;g=R(g);const T=e;if(e=k(e,g),e===T)break;o++,$("textDocument/didChange",{textDocument:{uri:i,version:o},contentChanges:[{text:e}]}),l=(await F(i)).filter(s=>s.severity===1||s.severity===2)}return e!==r.get(t)&&(v(t,e),r.set(t,e),c.set(t,o)),{initial:y,remaining:l.length}}export{k as applyEdits,G as fixFile};
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{resolve as
|
|
2
|
-
${
|
|
1
|
+
import{resolve as ce,relative as C}from"path";import{readFileSync as $e}from"fs";import{glob as ae}from"glob";import{startServer as de,send as fe,notify as q,shutdown as I,fileUri as y,langId as ge,diagnosticsReceived as A,settledProjects as ue,brokenProjects as me,warnings as pe,waitForAllProjects as we,resetState as he}from"./lsp.js";import{fixFile as xe}from"./edits.js";import{prescanCssFiles as be}from"./prescan.js";import{c as e,setTitle as d,windTrail as n,braille as V,windWave as b,dots as j,tick as F,advanceTick as ve,startSpinner as E,progressBar as L,banner as Pe,fileBadge as G,diagLine as ye,rainbowText as Fe,celebrationAnimation as K}from"./ui.js";import{applyEdits as Le}from"./edits.js";const ke=["**/*.{tsx,jsx,html,vue,svelte,astro,mdx,css}"];async function Ve(k={}){he();const S=Date.now(),f=ce(k.cwd||process.cwd()),Y=k.fix??!1,H=k.patterns??ke,U=new Set;for(const t of H){const o=await ae(t,{cwd:f,absolute:!0,nodir:!0,ignore:["**/node_modules/**","**/dist/**","**/build/**","**/out/**","**/coverage/**","**/public/**","**/tmp/**","**/.tmp/**","**/.cache/**","**/vendor/**","**/storybook-static/**","**/.next/**","**/.nuxt/**","**/.output/**","**/.svelte-kit/**","**/.astro/**","**/.vercel/**","**/.expo/**"]});for(const s of o)U.add(s)}const v=[...U];if(await Pe(),v.length===0){const o="the wind blows",s="but there is nothing here",r="~ no files matched ~",$=Math.floor((54-s.length-2)/2),w=54-r.length-8,a=4,u=50-o.length-2,i=$,h=54-$-s.length-2,l=w,x=54-w-r.length-2;return console.error(` ${n(a)} ${e.dim}${o}${e.reset} ${n(u,4)}`),console.error(` ${n(i,2)} ${e.dim}${s}${e.reset} ${n(h,7)}`),console.error(` ${n(l,5)} ${e.dim}${e.italic}${r}${e.reset} ${n(x,9)}`),console.error(""),0}d("tailwint ~ booting...");const J=E(()=>(d(`tailwint ~ booting${".".repeat(Date.now()%4)}`),` ${V()} ${e.dim}booting language server${j()}${e.reset} ${n(24,F)}`));de(f),await fe("initialize",{processId:process.pid,rootUri:y(f),capabilities:{textDocument:{publishDiagnostics:{relatedInformation:!0},codeAction:{codeActionLiteralSupport:{codeActionKind:{valueSet:["quickfix"]}}}},workspace:{workspaceFolders:!0,configuration:!0}},workspaceFolders:[{uri:y(f),name:"workspace"}]}),q("initialized",{}),J(),console.error(` ${e.green}\u2714${e.reset} ${e.dim}language server ready${e.reset} ${n(30)}`);const m=be(v),B=new Map,N=new Map,T=m.unrelatedCssFiles.size;let g=0;for(const t of v){if(m.unrelatedCssFiles.has(t))continue;let o;try{o=$e(t,"utf-8")}catch{continue}B.set(t,o),N.set(t,1),q("textDocument/didOpen",{textDocument:{uri:y(t),languageId:ge(t),version:1,text:o}}),g++}const O=`sent ${g} file${g===1?"":"s"} to lsp`,Q=52-O.length-1;if(console.error(` ${e.green}\u2714${e.reset} ${e.dim}${O}${e.reset} ${n(Q)}`),T>0){const t=`${T} file${T===1?"":"s"} skipped`,o=52-t.length-1;console.error(` ${e.dim}\u2500${e.reset} ${e.dim}${t}${e.reset} ${n(o)}`);for(const s of m.unrelatedCssFiles){const r=C(f,s);console.error(` ${e.dim}${G(r)}${e.reset}`)}}d("tailwint ~ scanning...");const Z=E(()=>{const t=A.size,o=ue+me,s=t>0?"scanning":"initializing";d(`tailwint ~ ${s} ${o}/${m.maxProjects}`);const r=g>0?Math.round(t/g*100):0,P=L(r,18,!0),$=String(g),w=String(t).padStart($.length),a=25+s.length+3+1-2,u=Math.max(0,54-a),i=`${w}/${$}`,h=" ".repeat(Math.max(0,25-i.length-1)),l="files received",x=h.length+i.length+1+l.length+1-2,D=Math.max(0,54-x);return` ${V()} ${P} ${e.dim}${s}${j()}${e.reset} ${n(u,F)}
|
|
2
|
+
${h}${e.bold}${i}${e.reset} ${e.dim}${l}${e.reset} ${n(D,F+3)}`},80);await we(m.predictedRoots,m.maxProjects),Z();for(const t of pe)console.error(` ${e.yellow}\u26A0${e.reset} ${e.dim}${t}${e.reset}`);const z=A.size,R=`${z}/${g} files received`,ee=52-R.length-1;console.error(` ${e.green}\u2714${e.reset} ${e.dim}${R}${e.reset} ${n(ee)}`),console.error("");let p=0;const c=new Map;for(const t of v){const s=(A.get(y(t))||[]).filter(r=>r.severity===1||r.severity===2);s.length>0&&(c.set(t,s),p+=s.length)}const W=[...c.values()].flat().filter(t=>t.code==="cssConflict").length,te=p-W;if(p===0){const t=((Date.now()-S)/1e3).toFixed(1);return d("tailwint \u2714 all clear"),await K(),console.error(` ${e.green}\u2714${e.reset} ${e.bold}${z}${e.reset} files scanned ${e.dim}// ${Fe("all clear")} ${e.dim}${t}s${e.reset}`),console.error(""),await I(),0}console.error(` ${e.bold}${e.white}${z}${e.reset} files scanned ${e.dim}//${e.reset} ${e.orange}${e.bold}${W}${e.reset}${e.orange} conflicts${e.reset} ${e.dim}\u2502${e.reset} ${e.yellow}${e.bold}${te}${e.reset}${e.yellow} canonical${e.reset}`),console.error("");let X=0;for(const[t,o]of c){X>0&&console.log(` ${e.dim}${b()}${e.reset}`),X++;const s=C(f,t);console.log(` ${e.dim}\u250C${e.reset} ${G(s)} ${e.dim}(${o.length})${e.reset}`);for(const r of o)console.log(ye(r));console.log(` ${e.dim}\u2514${n(3)}${e.reset}`),ve()}if(Y){console.error(""),console.error(` ${e.bgCyan}${e.bold} \u2699 FIX ${e.reset} ${e.dim}conflicts first, then canonical${e.reset}`),console.error("");let t=0,o=0,s=0;for(const[$,w]of c){s++;const a=C(f,$);let u=0;const i=a.includes("/")?a.slice(a.lastIndexOf("/")+1):a;d(`tailwint ~ fixing ${i} (${s}/${c.size})`);const h=E(()=>{const M=Math.round((s-1+u/10)/c.size*100),re=L(M,18,!0),_=`pass ${u}`,ie=22+i.length+1+_.length+3+1,le=Math.max(0,56-ie);return` ${V()} ${re} ${e.bold}${e.white}${i}${e.reset} ${e.dim}${_}${j()}${e.reset} ${n(le,F)}`}),l=await xe($,w,B,N,M=>{u=M});h();const x=l.initial-l.remaining;t+=x,o+=l.remaining;const D=Math.round(s/c.size*100),oe=L(D,18),ne=l.remaining>0?` ${e.dim}(${l.remaining} skipped)${e.reset}`:"";console.error(` ${e.green}\u2714${e.reset} ${oe} ${e.bold}${e.white}${i}${e.reset} ${e.green}${x} fixed${e.reset}${ne}`)}console.error("");const r=((Date.now()-S)/1e3).toFixed(1);d(`tailwint \u2714 fixed ${t} issues`),await K();const P=o>0?` ${e.dim}(${o} unfixable)${e.reset}`:"";return console.error(` ${b()} ${e.bgGreen}${e.bold} \u2714 FIXED ${e.reset} ${e.green}${e.bold}${t}${e.reset} of ${e.bold}${p}${e.reset} issues across ${e.bold}${c.size}${e.reset} files ${e.dim}${r}s${e.reset}${P} ${b()}`),console.error(""),await I(),o>0?1:0}const se=((Date.now()-S)/1e3).toFixed(1);return d(`tailwint \u2718 ${p} issues`),console.log(""),console.log(` ${b()} ${e.bgRed}${e.bold} \u2718 FAIL ${e.reset} ${e.red}${e.bold}${p}${e.reset} issues in ${e.bold}${c.size}${e.reset} files ${e.dim}${se}s${e.reset} ${b()}`),console.log(` ${e.dim}run with ${e.white}--fix${e.dim} to auto-fix${e.reset}`),console.log(""),await I(),1}export{Le as applyEdits,Ve as run};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tailwint",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "Tailwind CSS linter for CI —
|
|
3
|
+
"version": "1.1.11",
|
|
4
|
+
"description": "Tiny Tailwind CSS linter and auto-fixer for CI — powered by the official language server, not regex",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Peter Wang",
|
|
7
7
|
"homepage": "https://github.com/peterwangsc/tailwint",
|
|
@@ -13,16 +13,24 @@
|
|
|
13
13
|
"keywords": [
|
|
14
14
|
"tailwindcss",
|
|
15
15
|
"tailwind",
|
|
16
|
+
"tailwind-lint",
|
|
16
17
|
"lint",
|
|
17
18
|
"linter",
|
|
19
|
+
"cli",
|
|
18
20
|
"ci",
|
|
21
|
+
"ci-cd",
|
|
19
22
|
"autofix",
|
|
20
23
|
"language-server",
|
|
21
24
|
"lsp",
|
|
22
25
|
"tailwind-v4",
|
|
23
26
|
"css",
|
|
24
27
|
"diagnostics",
|
|
25
|
-
"code-quality"
|
|
28
|
+
"code-quality",
|
|
29
|
+
"class-conflicts",
|
|
30
|
+
"canonical",
|
|
31
|
+
"github-actions",
|
|
32
|
+
"pre-commit",
|
|
33
|
+
"static-analysis"
|
|
26
34
|
],
|
|
27
35
|
"engines": {
|
|
28
36
|
"node": ">=18"
|