tailwind-canonical 0.3.0 → 0.4.1

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.
Files changed (102) hide show
  1. package/README.md +85 -9
  2. package/dist/cli/cli.d.ts +9 -1
  3. package/dist/cli/cli.d.ts.map +1 -1
  4. package/dist/cli/cli.js +409 -78
  5. package/dist/cli/cli.js.map +1 -1
  6. package/dist/core/analyzer.d.ts +2 -1
  7. package/dist/core/analyzer.d.ts.map +1 -1
  8. package/dist/core/analyzer.js +5 -15
  9. package/dist/core/analyzer.js.map +1 -1
  10. package/dist/core/class-strings.d.ts +5 -1
  11. package/dist/core/class-strings.d.ts.map +1 -1
  12. package/dist/core/class-strings.js +74 -55
  13. package/dist/core/class-strings.js.map +1 -1
  14. package/dist/core/config.d.ts +3 -2
  15. package/dist/core/config.d.ts.map +1 -1
  16. package/dist/core/config.js +91 -33
  17. package/dist/core/config.js.map +1 -1
  18. package/dist/core/consistency.d.ts +18 -1
  19. package/dist/core/consistency.d.ts.map +1 -1
  20. package/dist/core/consistency.js +76 -110
  21. package/dist/core/consistency.js.map +1 -1
  22. package/dist/core/deduplicator.d.ts +5 -1
  23. package/dist/core/deduplicator.d.ts.map +1 -1
  24. package/dist/core/deduplicator.js +22 -17
  25. package/dist/core/deduplicator.js.map +1 -1
  26. package/dist/core/fixer.d.ts +5 -1
  27. package/dist/core/fixer.d.ts.map +1 -1
  28. package/dist/core/fixer.js +6 -10
  29. package/dist/core/fixer.js.map +1 -1
  30. package/dist/core/lexicon.d.ts +16 -0
  31. package/dist/core/lexicon.d.ts.map +1 -0
  32. package/dist/core/lexicon.js +139 -0
  33. package/dist/core/lexicon.js.map +1 -0
  34. package/dist/core/merger.d.ts +5 -1
  35. package/dist/core/merger.d.ts.map +1 -1
  36. package/dist/core/merger.js +3 -8
  37. package/dist/core/merger.js.map +1 -1
  38. package/dist/core/rules.d.ts +17 -0
  39. package/dist/core/rules.d.ts.map +1 -1
  40. package/dist/core/rules.js +18 -9
  41. package/dist/core/rules.js.map +1 -1
  42. package/dist/core/scanner.d.ts +5 -2
  43. package/dist/core/scanner.d.ts.map +1 -1
  44. package/dist/core/scanner.js +12 -80
  45. package/dist/core/scanner.js.map +1 -1
  46. package/dist/core/sorter.d.ts +5 -1
  47. package/dist/core/sorter.d.ts.map +1 -1
  48. package/dist/core/sorter.js +9 -10
  49. package/dist/core/sorter.js.map +1 -1
  50. package/dist/core/suppressions.d.ts +4 -0
  51. package/dist/core/suppressions.d.ts.map +1 -1
  52. package/dist/core/suppressions.js +10 -4
  53. package/dist/core/suppressions.js.map +1 -1
  54. package/dist/core/typos.d.ts +14 -0
  55. package/dist/core/typos.d.ts.map +1 -0
  56. package/dist/core/typos.js +71 -0
  57. package/dist/core/typos.js.map +1 -0
  58. package/dist/eslint/plugin.d.ts +3 -95
  59. package/dist/eslint/plugin.d.ts.map +1 -1
  60. package/dist/eslint/plugin.js +68 -31
  61. package/dist/eslint/plugin.js.map +1 -1
  62. package/dist/index.d.ts +5 -5
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +11 -4
  65. package/dist/index.js.map +1 -1
  66. package/dist/io/analyzer.d.ts +4 -0
  67. package/dist/io/analyzer.d.ts.map +1 -0
  68. package/dist/io/analyzer.js +7 -0
  69. package/dist/io/analyzer.js.map +1 -0
  70. package/dist/io/config.d.ts +3 -0
  71. package/dist/io/config.d.ts.map +1 -0
  72. package/dist/io/config.js +13 -0
  73. package/dist/io/config.js.map +1 -0
  74. package/dist/io/consistency.d.ts +4 -0
  75. package/dist/io/consistency.d.ts.map +1 -0
  76. package/dist/io/consistency.js +21 -0
  77. package/dist/io/consistency.js.map +1 -0
  78. package/dist/io/deduplicator.d.ts +3 -0
  79. package/dist/io/deduplicator.d.ts.map +1 -0
  80. package/dist/io/deduplicator.js +10 -0
  81. package/dist/io/deduplicator.js.map +1 -0
  82. package/dist/io/fixer.d.ts +3 -0
  83. package/dist/io/fixer.d.ts.map +1 -0
  84. package/dist/io/fixer.js +10 -0
  85. package/dist/io/fixer.js.map +1 -0
  86. package/dist/io/merger.d.ts +3 -0
  87. package/dist/io/merger.d.ts.map +1 -0
  88. package/dist/io/merger.js +17 -0
  89. package/dist/io/merger.js.map +1 -0
  90. package/dist/io/scanner.d.ts +4 -0
  91. package/dist/io/scanner.d.ts.map +1 -0
  92. package/dist/io/scanner.js +84 -0
  93. package/dist/io/scanner.js.map +1 -0
  94. package/dist/io/sorter.d.ts +4 -0
  95. package/dist/io/sorter.d.ts.map +1 -0
  96. package/dist/io/sorter.js +10 -0
  97. package/dist/io/sorter.js.map +1 -0
  98. package/dist/io/typos.d.ts +4 -0
  99. package/dist/io/typos.d.ts.map +1 -0
  100. package/dist/io/typos.js +7 -0
  101. package/dist/io/typos.js.map +1 -0
  102. package/package.json +22 -13
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Lint and auto-fix Tailwind CSS classes: arbitrary values → canonical, deduplication, shorthand collapsing, and class sorting.
4
4
 
5
+ > Compatible with Tailwind CSS v3.3+ and v4 — zero `tailwindcss` dependency (conventions are built in). Custom theme tokens are supported via config.
6
+
5
7
  ## Install
6
8
 
7
9
  ```bash
@@ -89,6 +91,18 @@ npx tailwind-canonical --fix --reporter json ./src
89
91
  npx tailwind-canonical --reporter sarif ./src > results.sarif
90
92
  ```
91
93
 
94
+ ## Typo detection (`--typos`)
95
+
96
+ Flags likely misspelled Tailwind color names (Levenshtein distance 1, low false-positive bias). Read-only.
97
+
98
+ ```bash
99
+ npx tailwind-canonical --typos ./src
100
+ # src/Card.tsx:3:17 text-gry-500 → text-gray-500 [typo]
101
+ # src/Nav.tsx:8:12 bg-slte-100 → bg-slate-100 [typo]
102
+ ```
103
+
104
+ Only color-property classes (`text-`, `bg-`, `border-`, …) are checked. Valid colors, non-color utilities (`text-center`), custom colors (`bg-brand-500`), and arbitrary values are never flagged. Pairs with `--reporter json`; exits `1` on findings.
105
+
92
106
  ## Suppression comments
93
107
 
94
108
  Skip specific lines or blocks from check and all transforms (`--fix`, `--dedup`, `--merge`, `--sort`):
@@ -111,9 +125,19 @@ Detects semantic inconsistencies visible only at project scale — it never modi
111
125
 
112
126
  ```bash
113
127
  npx tailwind-canonical --analyze ./src
114
- # Warning: 3 red color variants used for text: text-red-500 (4), text-rose-500 (1), text-red-600 (2)
115
- # Warning: px inconsistency: px-4 (8 files) vs px-3 (2 files)
116
- # Warning: z inconsistency: z-[100] (3 files) vs z-[200] (1 file) vs z-[50] (2 files)
128
+ # tailwind-canonical analyze
129
+ # Files analyzed: 42
130
+ # Issue groups: 3 (1 color, 2 scales, 0 patterns)
131
+ #
132
+ # Color variants
133
+ # - text/red: text-red-500 x4, text-rose-500 x1
134
+ #
135
+ # Scale inconsistency groups
136
+ # - px: 2 values, 10 uses, 10 files
137
+ # Top: px-4 (8 uses, 8 files), px-3 (2 uses, 2 files)
138
+ #
139
+ # Rare scale values
140
+ # - px-11: 3 uses in 1 file (608 px uses total); e.g. src/Dialog.tsx
117
141
  ```
118
142
 
119
143
  Three detectors:
@@ -122,6 +146,7 @@ Three detectors:
122
146
  |---|---|
123
147
  | Color variants | Multiple shades/colors of the same hue family used for one property (`text`, `bg`, `border`, …) |
124
148
  | Scale inconsistency | Competing values for the same spacing / `gap` / `z` property across files |
149
+ | Rare scale values | Low-frequency scale values inside otherwise common properties (`ml-13`, `gap-24`, `px-11`, …) |
125
150
  | Repeated patterns | Identical class combinations recurring across 3+ files |
126
151
 
127
152
  Pairs with `--reporter json` for machine-readable output. Exits `1` when any inconsistency is found.
@@ -146,6 +171,10 @@ npx tailwind-canonical --analyze --reporter json ./src
146
171
  { "value": "3", "count": 2, "files": ["src/IconButton.tsx"] }
147
172
  ] }
148
173
  ],
174
+ "rareScaleValues": [
175
+ { "property": "px", "value": "11", "className": "px-11",
176
+ "count": 3, "files": ["src/Dialog.tsx"], "propertyCount": 608 }
177
+ ],
149
178
  "combinations": []
150
179
  }
151
180
  ```
@@ -168,8 +197,8 @@ Variants (`hover:`, `sm:`, `focus:`) are sorted after base classes, with respons
168
197
 
169
198
  Override the category order via config. Categories you omit (and unknown classes) sort to the end, preserving their original relative order. Variants are always sorted after base classes regardless of order.
170
199
 
171
- ```js
172
- // tailwind-canonical.config.js
200
+ ```ts
201
+ // tailwind-canonical.config.ts
173
202
  export default {
174
203
  sortOrder: [
175
204
  'display',
@@ -231,15 +260,18 @@ left-2 right-2 → inset-x-2
231
260
  | `h-[64px]` | `h-16` | Spacing scale ÷4 |
232
261
  | `w-[50%]` | `w-1/2` | Percentage fractions |
233
262
  | `opacity-[0.5]` | `opacity-50` | Opacity scale |
234
- | `text-[11px]` | `text-2xs` | Custom token |
263
+ | `text-[11px]` | `text-2xs` | Built-in extra token |
264
+ | `text-[8px]`–`text-[10px]` | `text-3xs` | Built-in extra token |
265
+
266
+ `text-2xs` (11px) and `text-3xs` (8–10px) are part of the built-in text size map — they convert with no config. Override or extend the map via `customTextTokens`.
235
267
 
236
268
  Non-divisible values (`h-[22px]`, `px-[7px]`) are left untouched.
237
269
 
238
270
  ## Config
239
271
 
240
- Create `tailwind-canonical.config.js` at the root:
272
+ Create `tailwind-canonical.config.ts` at the root:
241
273
 
242
- ```js
274
+ ```ts
243
275
  export default {
244
276
  customTextTokens: {
245
277
  10: '3xs',
@@ -252,6 +284,16 @@ export default {
252
284
  attributeNames: ['className', 'class', ':class', 'tw'],
253
285
  // Support utility function wrappers
254
286
  functionNames: ['cn', 'clsx', 'cva'],
287
+ // Tune --analyze reporting
288
+ analyze: {
289
+ maxScaleGroups: 8,
290
+ maxScaleValues: 5,
291
+ maxRareValues: 12,
292
+ maxPatterns: 10,
293
+ minRareScalePropertyOccurrences: 10,
294
+ rareScaleMaxFiles: 2,
295
+ rareScaleMaxCount: 3,
296
+ },
255
297
  // Never suggest replacements for classes matching these patterns
256
298
  ignorePatterns: [/^font-/, /-\[var\(/],
257
299
  }
@@ -263,6 +305,20 @@ export default {
263
305
 
264
306
  `functionNames` enables scanning inside utility function calls like `cn(...)` and `clsx(...)`.
265
307
 
308
+ `analyze` controls project-scale consistency reporting. `maxScaleGroups`,
309
+ `maxScaleValues`, `maxRareValues`, and `maxPatterns` keep terminal output
310
+ compact. `minRareScalePropertyOccurrences`, `rareScaleMaxFiles`, and
311
+ `rareScaleMaxCount` control which low-frequency values are surfaced as rare
312
+ scale values. Defaults are conservative: only properties with 10+ total
313
+ occurrences are considered, and a value is rare when it appears in at most 2
314
+ files and at most 3 times.
315
+
316
+ The older top-level rare-value keys are still accepted for compatibility, but
317
+ new configs should prefer `analyze`.
318
+
319
+ `tailwind-canonical.config.js` is still loaded as a fallback when no TypeScript
320
+ config exists.
321
+
266
322
  ## ESLint plugin
267
323
 
268
324
  ```js
@@ -281,6 +337,26 @@ export default [
281
337
  ]
282
338
  ```
283
339
 
340
+ ### Rule options
341
+
342
+ `no-arbitrary-canonical` accepts the full tailwind-canonical `Config` object as
343
+ its rule options, so the same shared config can be reused for both the CLI and
344
+ ESLint without schema errors:
345
+
346
+ ```js
347
+ 'tailwind-canonical/no-arbitrary-canonical': [
348
+ 'warn',
349
+ { customTextTokens: { 11: '2xs' }, ignorePatterns: [/^font-/] },
350
+ ],
351
+ ```
352
+
353
+ The rule **honors**: `customTextTokens`, `customSpacingTokens`, `ignorePatterns`
354
+ (all applied via `suggestCanonical`).
355
+
356
+ It **accepts but ignores**: `functionNames`, `attributeNames`, `sortOrder`.
357
+ These are CLI-only options, allowed in the schema purely so a single shared
358
+ config object can be passed without errors.
359
+
284
360
  ## Pre-commit hook (Husky / Lefthook)
285
361
 
286
362
  ```yaml
@@ -309,7 +385,7 @@ npx tailwind-canonical --fix --dedup --sort ./src ./app
309
385
  pnpm knip
310
386
  ```
311
387
 
312
- Detects unused exports, files, and dependencies. Config in `knip.json`.
388
+ Detects unused exports, files, and dependencies. Config in `knip.json`. Enforced in CI and in the `pre-push` git hook.
313
389
 
314
390
  ## Programmatic API
315
391
 
package/dist/cli/cli.d.ts CHANGED
@@ -3,7 +3,6 @@ export type Sink = {
3
3
  error: (s: string) => void;
4
4
  write: (s: string) => void;
5
5
  };
6
- export declare const defaultSink: Sink;
7
6
  export type RunResult = {
8
7
  exitCode: number;
9
8
  watching?: boolean;
@@ -16,10 +15,19 @@ type Flags = {
16
15
  sort: boolean;
17
16
  watch: boolean;
18
17
  analyze: boolean;
18
+ typos: boolean;
19
19
  reporter: Reporter;
20
20
  targets: string[];
21
+ error?: string;
21
22
  };
22
23
  export declare function parseArgs(argv: string[]): Flags;
24
+ /**
25
+ * Modes are mutually exclusive with a fixed precedence: analyze > typos >
26
+ * transform/check. `--watch` is only honored in transform/check mode. This
27
+ * computes warnings for every flag that the active mode will ignore. Pure and
28
+ * order-stable so it can be unit-tested via the injectable sink.
29
+ */
30
+ export declare function flagWarnings(flags: Flags): string[];
23
31
  export declare function run(argv: string[], cwd: string, sink?: Sink): Promise<RunResult>;
24
32
  export {};
25
33
  //# sourceMappingURL=cli.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli/cli.ts"],"names":[],"mappings":"AAmBA,MAAM,MAAM,IAAI,GAAG;IACjB,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACzB,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,IAIzB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEjE,KAAK,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE1C,KAAK,KAAK,GAAG;IACX,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAuBF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,CA2B/C;AAmJD,wBAAsB,GAAG,CACvB,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,IAAkB,GACvB,OAAO,CAAC,SAAS,CAAC,CAgMpB"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli/cli.ts"],"names":[],"mappings":"AAwBA,MAAM,MAAM,IAAI,GAAG;IACjB,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACzB,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3B,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5B,CAAC;AAQF,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEjE,KAAK,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE1C,KAAK,KAAK,GAAG;IACX,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA2IF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,KAAK,CAqD/C;AASD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,CAwBnD;AAgbD,wBAAsB,GAAG,CACvB,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,IAAkB,GACvB,OAAO,CAAC,SAAS,CAAC,CAqLpB"}