code-auditor-mcp 3.4.0 → 3.4.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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +196 -0
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
- package/dist/analyzers/orm/drizzleAdapter.d.ts +15 -0
- package/dist/analyzers/orm/drizzleAdapter.d.ts.map +1 -1
- package/dist/analyzers/orm/drizzleAdapter.js +35 -2
- package/dist/analyzers/orm/drizzleAdapter.js.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +27 -8
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +12 -0
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +18 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +9 -0
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +39 -0
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +17 -0
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js +98 -33
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +39 -15
- package/dist/auditRunner.js.map +1 -1
- package/dist/churn/churnExtractor.js +4 -1
- package/dist/churn/churnExtractor.js.map +1 -1
- package/dist/cli.js +5 -2
- package/dist/cli.js.map +1 -1
- package/dist/codeIndexDB.d.ts +1 -0
- package/dist/codeIndexDB.d.ts.map +1 -1
- package/dist/codeIndexDB.js +24 -6
- package/dist/codeIndexDB.js.map +1 -1
- package/dist/componentScanner.d.ts.map +1 -1
- package/dist/componentScanner.js +4 -3
- package/dist/componentScanner.js.map +1 -1
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +9 -6
- package/dist/config/configLoader.js.map +1 -1
- package/dist/config/defaults.d.ts +1 -0
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +9 -2
- package/dist/config/defaults.js.map +1 -1
- package/dist/conventions/conventionMiner.d.ts.map +1 -1
- package/dist/conventions/conventionMiner.js +144 -47
- package/dist/conventions/conventionMiner.js.map +1 -1
- package/dist/hotspots/hotspotScorer.js +1 -1
- package/dist/languages/UniversalAnalyzer.d.ts.map +1 -1
- package/dist/languages/UniversalAnalyzer.js +2 -0
- package/dist/languages/UniversalAnalyzer.js.map +1 -1
- package/dist/languages/adapterBridge.d.ts +7 -0
- package/dist/languages/adapterBridge.d.ts.map +1 -1
- package/dist/languages/adapterBridge.js +9 -2
- package/dist/languages/adapterBridge.js.map +1 -1
- package/dist/languages/tree-sitter/converter.d.ts +9 -1
- package/dist/languages/tree-sitter/converter.d.ts.map +1 -1
- package/dist/languages/tree-sitter/converter.js +13 -6
- package/dist/languages/tree-sitter/converter.js.map +1 -1
- package/dist/scripts/runBench.d.ts.map +1 -1
- package/dist/scripts/runBench.js +16 -4
- package/dist/scripts/runBench.js.map +1 -1
- package/dist/styles/styleIndexer.js +9 -3
- package/dist/styles/styleIndexer.js.map +1 -1
- package/dist/styles/tailwindUtilityExpander.d.ts +116 -0
- package/dist/styles/tailwindUtilityExpander.d.ts.map +1 -0
- package/dist/styles/tailwindUtilityExpander.js +880 -0
- package/dist/styles/tailwindUtilityExpander.js.map +1 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/astParser.d.ts.map +1 -1
- package/dist/utils/astParser.js +3 -2
- package/dist/utils/astParser.js.map +1 -1
- package/dist/utils/dependencyExtractor.d.ts.map +1 -1
- package/dist/utils/dependencyExtractor.js +5 -3
- package/dist/utils/dependencyExtractor.js.map +1 -1
- package/dist/utils/fileDiscovery.d.ts +1 -0
- package/dist/utils/fileDiscovery.d.ts.map +1 -1
- package/dist/utils/fileDiscovery.js +8 -2
- package/dist/utils/fileDiscovery.js.map +1 -1
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/scripts/hook-audit.sh +2 -2
|
@@ -0,0 +1,880 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tailwind Utility Class Expander — Spec 22 R1
|
|
3
|
+
*
|
|
4
|
+
* Generates the full set of valid Tailwind utility classes from default theme
|
|
5
|
+
* scales plus arbitrary-value grammar, variant prefix stripping, and project
|
|
6
|
+
* config resolution. Replaces the hand-curated ~30-class Set previously in
|
|
7
|
+
* UniversalStylesAnalyzer.detectUndefinedClasses.
|
|
8
|
+
*
|
|
9
|
+
* Fail-open rule (per Spec 22 R1.3): if project Tailwind config resolution
|
|
10
|
+
* fails, the undefined-class detector disables for that project with one
|
|
11
|
+
* visible warning naming the failure. A claim that a class "does not exist"
|
|
12
|
+
* may not ship on a known-incomplete dictionary.
|
|
13
|
+
*/
|
|
14
|
+
import { loadTailwindConfig } from './tailwindConfigLoader.js';
|
|
15
|
+
// ─── Variant Prefixes ──────────────────────────────────────────────────────
|
|
16
|
+
/**
|
|
17
|
+
* Tailwind variant prefixes that modify utilities.
|
|
18
|
+
* When stripped, the underlying class should match a known utility or
|
|
19
|
+
* arbitrary-value pattern.
|
|
20
|
+
*/
|
|
21
|
+
const VARIANT_PREFIXES = [
|
|
22
|
+
// Responsive
|
|
23
|
+
'sm', 'md', 'lg', 'xl', '2xl',
|
|
24
|
+
// State
|
|
25
|
+
'hover', 'focus', 'active', 'disabled', 'visited',
|
|
26
|
+
'focus-visible', 'focus-within', 'focus-visible',
|
|
27
|
+
// Form
|
|
28
|
+
'checked', 'indeterminate', 'required', 'valid', 'invalid',
|
|
29
|
+
'in-range', 'out-of-range', 'placeholder-shown', 'autofill', 'read-only',
|
|
30
|
+
// Structure
|
|
31
|
+
'first', 'last', 'only', 'odd', 'even', 'first-of-type', 'last-of-type', 'only-of-type',
|
|
32
|
+
'empty',
|
|
33
|
+
// Mode
|
|
34
|
+
'dark', 'light',
|
|
35
|
+
// Group
|
|
36
|
+
'group-hover', 'group-focus', 'group-focus-visible', 'group-focus-within',
|
|
37
|
+
'group-active', 'group-disabled', 'group-visited', 'group-checked',
|
|
38
|
+
// Peer
|
|
39
|
+
'peer-hover', 'peer-focus', 'peer-focus-visible', 'peer-active',
|
|
40
|
+
'peer-disabled', 'peer-checked', 'peer-invalid', 'peer-required',
|
|
41
|
+
// Motion
|
|
42
|
+
'motion-safe', 'motion-reduce',
|
|
43
|
+
// Other
|
|
44
|
+
'before', 'after', 'first-letter', 'first-line',
|
|
45
|
+
'marker', 'selection', 'target', 'file', 'placeholder',
|
|
46
|
+
'rtl', 'ltr',
|
|
47
|
+
'open', 'closed',
|
|
48
|
+
'portrait', 'landscape',
|
|
49
|
+
'print',
|
|
50
|
+
// Aria
|
|
51
|
+
'aria-checked', 'aria-disabled',
|
|
52
|
+
'has-[:checked]', 'has-[:disabled]',
|
|
53
|
+
// Redundant but safe: already covered by individual state modifiers
|
|
54
|
+
];
|
|
55
|
+
// ─── Utility Prefixes (for arbitrary-value grammar) ─────────────────────────
|
|
56
|
+
/**
|
|
57
|
+
* Known Tailwind utility prefixes that can accept arbitrary values via
|
|
58
|
+
* the `prefix-[...]` syntax. Covers spacing, color, sizing, and other
|
|
59
|
+
* commonly-arbitrary-value'd families.
|
|
60
|
+
*/
|
|
61
|
+
const ARBITRARY_VALUE_PREFIXES = new Set([
|
|
62
|
+
// Spacing-based
|
|
63
|
+
'p', 'px', 'py', 'pt', 'pr', 'pb', 'pl',
|
|
64
|
+
'm', 'mx', 'my', 'mt', 'mr', 'mb', 'ml',
|
|
65
|
+
'gap', 'gap-x', 'gap-y',
|
|
66
|
+
'w', 'min-w', 'max-w', 'h', 'min-h', 'max-h',
|
|
67
|
+
'top', 'right', 'bottom', 'left', 'inset',
|
|
68
|
+
'inset-x', 'inset-y',
|
|
69
|
+
'translate-x', 'translate-y', 'translate',
|
|
70
|
+
'skew-x', 'skew-y', 'skew',
|
|
71
|
+
'rotate',
|
|
72
|
+
'scale', 'scale-x', 'scale-y',
|
|
73
|
+
'space-x', 'space-y',
|
|
74
|
+
'leading', 'tracking', 'indent',
|
|
75
|
+
'scroll-m', 'scroll-mx', 'scroll-my', 'scroll-mt', 'scroll-mr', 'scroll-mb', 'scroll-ml',
|
|
76
|
+
'scroll-p', 'scroll-px', 'scroll-py', 'scroll-pt', 'scroll-pr', 'scroll-pb', 'scroll-pl',
|
|
77
|
+
// Color-based
|
|
78
|
+
'bg', 'text', 'border', 'ring', 'ring-offset', 'shadow',
|
|
79
|
+
'fill', 'stroke', 'outline', 'accent', 'caret',
|
|
80
|
+
'placeholder', 'divide',
|
|
81
|
+
'from', 'via', 'to', // gradient stops
|
|
82
|
+
'decoration',
|
|
83
|
+
// Sizing
|
|
84
|
+
'basis', 'grow', 'shrink',
|
|
85
|
+
'order',
|
|
86
|
+
'z', // z-index
|
|
87
|
+
// Border
|
|
88
|
+
'rounded', 'rounded-t', 'rounded-r', 'rounded-b', 'rounded-l',
|
|
89
|
+
'rounded-tl', 'rounded-tr', 'rounded-br', 'rounded-bl',
|
|
90
|
+
'rounded-s', 'rounded-e', 'rounded-ss', 'rounded-se', 'rounded-es', 'rounded-ee',
|
|
91
|
+
'border', 'border-t', 'border-r', 'border-b', 'border-l',
|
|
92
|
+
'border-x', 'border-y',
|
|
93
|
+
'outline',
|
|
94
|
+
'outline-offset',
|
|
95
|
+
// Text
|
|
96
|
+
'text', 'text-wrap',
|
|
97
|
+
'underline-offset',
|
|
98
|
+
// Opacity
|
|
99
|
+
'opacity',
|
|
100
|
+
'bg-opacity', 'text-opacity', 'border-opacity', 'ring-opacity',
|
|
101
|
+
'divide-opacity', 'placeholder-opacity',
|
|
102
|
+
// Shadow
|
|
103
|
+
'shadow',
|
|
104
|
+
// Duration / timing
|
|
105
|
+
'duration', 'delay', 'easing',
|
|
106
|
+
// Animation
|
|
107
|
+
'animate',
|
|
108
|
+
// Grid
|
|
109
|
+
'grid-cols', 'grid-rows', 'auto-cols', 'auto-rows',
|
|
110
|
+
'col', 'col-start', 'col-end', 'col-span',
|
|
111
|
+
'row', 'row-start', 'row-end', 'row-span',
|
|
112
|
+
// Columns
|
|
113
|
+
'columns',
|
|
114
|
+
// Filter
|
|
115
|
+
'blur', 'brightness', 'contrast', 'grayscale', 'hue-rotate',
|
|
116
|
+
'invert', 'saturate', 'sepia', 'drop-shadow',
|
|
117
|
+
'backdrop-blur', 'backdrop-brightness', 'backdrop-contrast',
|
|
118
|
+
'backdrop-grayscale', 'backdrop-hue-rotate', 'backdrop-invert',
|
|
119
|
+
'backdrop-opacity', 'backdrop-saturate', 'backdrop-sepia',
|
|
120
|
+
// Aspect ratio
|
|
121
|
+
'aspect',
|
|
122
|
+
// Break
|
|
123
|
+
'break',
|
|
124
|
+
// Container queries
|
|
125
|
+
'@',
|
|
126
|
+
]);
|
|
127
|
+
/**
|
|
128
|
+
* Prefixes that allow a negative sign to be prepended to the utility.
|
|
129
|
+
* e.g., -mt-1 (negative margin-top), -top-2 (negative position).
|
|
130
|
+
*/
|
|
131
|
+
const NEGATIVE_ALLOWED_PREFIXES = new Set([
|
|
132
|
+
// Margin — negative margins pull elements closer / overlap
|
|
133
|
+
'm', 'mx', 'my', 'mt', 'mr', 'mb', 'ml',
|
|
134
|
+
// Position — negative offsets move element beyond container edge
|
|
135
|
+
'top', 'right', 'bottom', 'left',
|
|
136
|
+
// Inset
|
|
137
|
+
'inset', 'inset-x', 'inset-y',
|
|
138
|
+
// Z-index
|
|
139
|
+
'z',
|
|
140
|
+
// Transform
|
|
141
|
+
'translate-x', 'translate-y',
|
|
142
|
+
'rotate',
|
|
143
|
+
'scale', 'scale-x', 'scale-y',
|
|
144
|
+
'skew-x', 'skew-y',
|
|
145
|
+
// Indent (negative text indent)
|
|
146
|
+
'indent',
|
|
147
|
+
]);
|
|
148
|
+
// ─── Static Utility Classes ─────────────────────────────────────────────────
|
|
149
|
+
/**
|
|
150
|
+
* Standard Tailwind utility classes that don't depend on a theme scale.
|
|
151
|
+
* These are the fixed-name classes: display, flex, grid, alignment, etc.
|
|
152
|
+
*/
|
|
153
|
+
function getStaticUtilityClasses() {
|
|
154
|
+
return [
|
|
155
|
+
// Display
|
|
156
|
+
'block', 'inline-block', 'inline', 'flex', 'inline-flex', 'grid',
|
|
157
|
+
'inline-grid', 'hidden', 'flow-root', 'contents', 'table', 'table-row',
|
|
158
|
+
'table-cell', 'table-caption', 'table-column', 'table-column-group',
|
|
159
|
+
'table-footer-group', 'table-header-group', 'table-row-group', 'list-item',
|
|
160
|
+
// Position
|
|
161
|
+
'static', 'fixed', 'absolute', 'relative', 'sticky',
|
|
162
|
+
// Flex direction
|
|
163
|
+
'flex-row', 'flex-row-reverse', 'flex-col', 'flex-col-reverse',
|
|
164
|
+
'flex-wrap', 'flex-nowrap', 'flex-wrap-reverse',
|
|
165
|
+
'flex-1', 'flex-auto', 'flex-initial', 'flex-none',
|
|
166
|
+
'grow', 'grow-0', 'shrink', 'shrink-0',
|
|
167
|
+
'flex-grow', 'flex-grow-0', 'flex-shrink', 'flex-shrink-0',
|
|
168
|
+
'group',
|
|
169
|
+
// Grid
|
|
170
|
+
'grid-cols-1', 'grid-cols-2', 'grid-cols-3', 'grid-cols-4',
|
|
171
|
+
'grid-cols-5', 'grid-cols-6', 'grid-cols-7', 'grid-cols-8',
|
|
172
|
+
'grid-cols-9', 'grid-cols-10', 'grid-cols-11', 'grid-cols-12',
|
|
173
|
+
'grid-cols-none', 'grid-cols-subgrid',
|
|
174
|
+
'grid-rows-1', 'grid-rows-2', 'grid-rows-3', 'grid-rows-4',
|
|
175
|
+
'grid-rows-5', 'grid-rows-6', 'grid-rows-none', 'grid-rows-subgrid',
|
|
176
|
+
'grid-flow-row', 'grid-flow-col', 'grid-flow-dense', 'grid-flow-row-dense', 'grid-flow-col-dense',
|
|
177
|
+
'auto-cols-auto', 'auto-cols-min', 'auto-cols-max', 'auto-cols-fr',
|
|
178
|
+
'auto-rows-auto', 'auto-rows-min', 'auto-rows-max', 'auto-rows-fr',
|
|
179
|
+
// Alignment
|
|
180
|
+
'items-start', 'items-end', 'items-center', 'items-baseline', 'items-stretch',
|
|
181
|
+
'justify-start', 'justify-end', 'justify-center', 'justify-between',
|
|
182
|
+
'justify-around', 'justify-evenly', 'justify-stretch', 'justify-normal',
|
|
183
|
+
'justify-items-start', 'justify-items-end', 'justify-items-center', 'justify-items-stretch',
|
|
184
|
+
'content-center', 'content-start', 'content-end', 'content-between',
|
|
185
|
+
'content-around', 'content-evenly', 'content-baseline', 'content-normal', 'content-stretch',
|
|
186
|
+
'place-content-center', 'place-content-start', 'place-content-end',
|
|
187
|
+
'place-content-between', 'place-content-around', 'place-content-evenly',
|
|
188
|
+
'place-items-center', 'place-items-start', 'place-items-end', 'place-items-stretch',
|
|
189
|
+
'self-auto', 'self-start', 'self-end', 'self-center', 'self-stretch', 'self-baseline',
|
|
190
|
+
'place-self-auto', 'place-self-start', 'place-self-end', 'place-self-center', 'place-self-stretch',
|
|
191
|
+
// Text alignment
|
|
192
|
+
'text-left', 'text-center', 'text-right', 'text-justify', 'text-start', 'text-end',
|
|
193
|
+
// Text decoration
|
|
194
|
+
'underline', 'line-through', 'no-underline', 'overline',
|
|
195
|
+
'decoration-solid', 'decoration-double', 'decoration-dotted', 'decoration-dashed', 'decoration-wavy',
|
|
196
|
+
// Text transform
|
|
197
|
+
'uppercase', 'lowercase', 'capitalize', 'normal-case',
|
|
198
|
+
// Text overflow
|
|
199
|
+
'truncate', 'text-ellipsis', 'text-clip',
|
|
200
|
+
// Whitespace
|
|
201
|
+
'whitespace-normal', 'whitespace-nowrap', 'whitespace-pre', 'whitespace-pre-line', 'whitespace-pre-wrap',
|
|
202
|
+
'whitespace-break-spaces',
|
|
203
|
+
// Word break
|
|
204
|
+
'break-normal', 'break-words', 'break-all', 'break-keep', 'hyphens-none', 'hyphens-manual', 'hyphens-auto',
|
|
205
|
+
// Font weight
|
|
206
|
+
'font-thin', 'font-extralight', 'font-light', 'font-normal', 'font-medium',
|
|
207
|
+
'font-semibold', 'font-bold', 'font-extrabold', 'font-black',
|
|
208
|
+
// Font style
|
|
209
|
+
'italic', 'not-italic',
|
|
210
|
+
// Font variant
|
|
211
|
+
'ordinal', 'slashed-zero', 'lining-nums', 'oldstyle-nums', 'proportional-nums',
|
|
212
|
+
'tabular-nums', 'diagonal-fractions', 'stacked-fractions',
|
|
213
|
+
// Width
|
|
214
|
+
'w-auto', 'w-full', 'w-screen', 'w-svw', 'w-dvw', 'w-lvw', 'w-min', 'w-max', 'w-fit',
|
|
215
|
+
'w-0', 'w-px', 'w-0.5', 'w-1', 'w-2', 'w-3', 'w-4', 'w-5', 'w-6', 'w-7', 'w-8',
|
|
216
|
+
'w-9', 'w-10', 'w-11', 'w-12', 'w-14', 'w-16', 'w-20', 'w-24', 'w-28', 'w-32',
|
|
217
|
+
'w-36', 'w-40', 'w-44', 'w-48', 'w-52', 'w-56', 'w-60', 'w-64', 'w-72', 'w-80', 'w-96',
|
|
218
|
+
'w-1/2', 'w-1/3', 'w-2/3', 'w-1/4', 'w-2/4', 'w-3/4', 'w-1/5', 'w-2/5', 'w-3/5', 'w-4/5',
|
|
219
|
+
'w-1/6', 'w-2/6', 'w-3/6', 'w-4/6', 'w-5/6',
|
|
220
|
+
// Height
|
|
221
|
+
'h-auto', 'h-full', 'h-screen', 'h-svh', 'h-dvh', 'h-lvh', 'h-min', 'h-max', 'h-fit',
|
|
222
|
+
'h-0', 'h-px', 'h-0.5', 'h-1', 'h-2', 'h-3', 'h-4', 'h-5', 'h-6', 'h-7', 'h-8',
|
|
223
|
+
'h-9', 'h-10', 'h-11', 'h-12', 'h-14', 'h-16', 'h-20', 'h-24', 'h-28', 'h-32',
|
|
224
|
+
'h-36', 'h-40', 'h-44', 'h-48', 'h-52', 'h-56', 'h-60', 'h-64', 'h-72', 'h-80', 'h-96',
|
|
225
|
+
'h-1/2', 'h-1/3', 'h-2/3', 'h-1/4', 'h-2/4', 'h-3/4', 'h-1/5', 'h-2/5', 'h-3/5', 'h-4/5',
|
|
226
|
+
'h-1/6', 'h-2/6', 'h-3/6', 'h-4/6', 'h-5/6',
|
|
227
|
+
// Min/max sizing
|
|
228
|
+
'min-w-0', 'min-w-full', 'min-w-min', 'min-w-max', 'min-w-fit',
|
|
229
|
+
'max-w-0', 'max-w-none', 'max-w-xs', 'max-w-sm', 'max-w-md', 'max-w-lg', 'max-w-xl',
|
|
230
|
+
'max-w-2xl', 'max-w-3xl', 'max-w-4xl', 'max-w-5xl', 'max-w-6xl', 'max-w-7xl',
|
|
231
|
+
'max-w-full', 'max-w-min', 'max-w-max', 'max-w-fit', 'max-w-prose',
|
|
232
|
+
'max-w-screen-sm', 'max-w-screen-md', 'max-w-screen-lg', 'max-w-screen-xl', 'max-w-screen-2xl',
|
|
233
|
+
'min-h-0', 'min-h-full', 'min-h-screen', 'min-h-min', 'min-h-max', 'min-h-fit',
|
|
234
|
+
'max-h-0', 'max-h-full', 'max-h-screen', 'max-h-min', 'max-h-max', 'max-h-fit',
|
|
235
|
+
// Size (both w and h)
|
|
236
|
+
'size-auto', 'size-full', 'size-min', 'size-max', 'size-fit',
|
|
237
|
+
// Overflow
|
|
238
|
+
'overflow-auto', 'overflow-hidden', 'overflow-visible', 'overflow-scroll',
|
|
239
|
+
'overflow-x-auto', 'overflow-x-hidden', 'overflow-x-visible', 'overflow-x-scroll',
|
|
240
|
+
'overflow-y-auto', 'overflow-y-hidden', 'overflow-y-visible', 'overflow-y-scroll',
|
|
241
|
+
// Cursor
|
|
242
|
+
'cursor-auto', 'cursor-default', 'cursor-pointer', 'cursor-wait', 'cursor-text',
|
|
243
|
+
'cursor-move', 'cursor-help', 'cursor-not-allowed', 'cursor-none',
|
|
244
|
+
'cursor-context-menu', 'cursor-progress', 'cursor-cell', 'cursor-crosshair',
|
|
245
|
+
'cursor-vertical-text', 'cursor-alias', 'cursor-copy', 'cursor-grab', 'cursor-grabbing',
|
|
246
|
+
'cursor-no-drop', 'cursor-zoom-in', 'cursor-zoom-out',
|
|
247
|
+
// Visibility
|
|
248
|
+
'visible', 'invisible', 'collapse',
|
|
249
|
+
// Border
|
|
250
|
+
'border', 'border-0', 'border-2', 'border-4', 'border-8',
|
|
251
|
+
'border-t', 'border-r', 'border-b', 'border-l',
|
|
252
|
+
'border-t-0', 'border-r-0', 'border-b-0', 'border-l-0',
|
|
253
|
+
'border-x', 'border-y',
|
|
254
|
+
'border-solid', 'border-dashed', 'border-dotted', 'border-double', 'border-hidden', 'border-none',
|
|
255
|
+
// Shadow
|
|
256
|
+
'shadow', 'shadow-sm', 'shadow-md', 'shadow-lg', 'shadow-xl', 'shadow-2xl',
|
|
257
|
+
'shadow-inner', 'shadow-none',
|
|
258
|
+
// Ring
|
|
259
|
+
'ring', 'ring-0', 'ring-1', 'ring-2', 'ring-4', 'ring-8', 'ring-inset',
|
|
260
|
+
'ring-offset-0', 'ring-offset-1', 'ring-offset-2', 'ring-offset-4', 'ring-offset-8',
|
|
261
|
+
// Opacity
|
|
262
|
+
'opacity-0', 'opacity-5', 'opacity-10', 'opacity-20', 'opacity-25', 'opacity-30',
|
|
263
|
+
'opacity-40', 'opacity-50', 'opacity-60', 'opacity-70', 'opacity-75', 'opacity-80',
|
|
264
|
+
'opacity-90', 'opacity-95', 'opacity-100',
|
|
265
|
+
// Z-index
|
|
266
|
+
'z-0', 'z-10', 'z-20', 'z-30', 'z-40', 'z-50', 'z-auto',
|
|
267
|
+
// Object fit
|
|
268
|
+
'object-contain', 'object-cover', 'object-fill', 'object-none', 'object-scale-down',
|
|
269
|
+
// Object position
|
|
270
|
+
'object-bottom', 'object-center', 'object-left', 'object-left-bottom',
|
|
271
|
+
'object-left-top', 'object-right', 'object-right-bottom', 'object-right-top', 'object-top',
|
|
272
|
+
// Transition
|
|
273
|
+
'transition-none', 'transition-all', 'transition', 'transition-colors',
|
|
274
|
+
'transition-opacity', 'transition-shadow', 'transition-transform',
|
|
275
|
+
// Duration
|
|
276
|
+
'duration-0', 'duration-75', 'duration-100', 'duration-150', 'duration-200',
|
|
277
|
+
'duration-300', 'duration-500', 'duration-700', 'duration-1000',
|
|
278
|
+
// Delay
|
|
279
|
+
'delay-0', 'delay-75', 'delay-100', 'delay-150', 'delay-200', 'delay-300',
|
|
280
|
+
'delay-500', 'delay-700', 'delay-1000',
|
|
281
|
+
// Timing
|
|
282
|
+
'ease-linear', 'ease-in', 'ease-out', 'ease-in-out',
|
|
283
|
+
// Transform
|
|
284
|
+
'transform', 'transform-gpu', 'transform-none',
|
|
285
|
+
'scale-0', 'scale-50', 'scale-75', 'scale-90', 'scale-95', 'scale-100',
|
|
286
|
+
'scale-105', 'scale-110', 'scale-125', 'scale-150',
|
|
287
|
+
'scale-x-0', 'scale-x-50', 'scale-x-75', 'scale-x-90', 'scale-x-95', 'scale-x-100',
|
|
288
|
+
'scale-x-105', 'scale-x-110', 'scale-x-125', 'scale-x-150',
|
|
289
|
+
'scale-y-0', 'scale-y-50', 'scale-y-75', 'scale-y-90', 'scale-y-95', 'scale-y-100',
|
|
290
|
+
'scale-y-105', 'scale-y-110', 'scale-y-125', 'scale-y-150',
|
|
291
|
+
'rotate-0', 'rotate-1', 'rotate-2', 'rotate-3', 'rotate-6', 'rotate-12',
|
|
292
|
+
'rotate-45', 'rotate-90', 'rotate-180',
|
|
293
|
+
'origin-center', 'origin-top', 'origin-top-right', 'origin-right',
|
|
294
|
+
'origin-bottom-right', 'origin-bottom', 'origin-bottom-left', 'origin-left', 'origin-top-left',
|
|
295
|
+
// Box sizing
|
|
296
|
+
'box-border', 'box-content',
|
|
297
|
+
// Container
|
|
298
|
+
'container',
|
|
299
|
+
// Pointer events
|
|
300
|
+
'pointer-events-none', 'pointer-events-auto',
|
|
301
|
+
// Resize
|
|
302
|
+
'resize-none', 'resize', 'resize-y', 'resize-x',
|
|
303
|
+
// User select
|
|
304
|
+
'select-none', 'select-text', 'select-all', 'select-auto',
|
|
305
|
+
// Screen readers
|
|
306
|
+
'sr-only', 'not-sr-only',
|
|
307
|
+
// Background
|
|
308
|
+
'bg-auto', 'bg-cover', 'bg-contain',
|
|
309
|
+
'bg-bottom', 'bg-center', 'bg-left', 'bg-left-bottom', 'bg-left-top',
|
|
310
|
+
'bg-right', 'bg-right-bottom', 'bg-right-top', 'bg-top',
|
|
311
|
+
'bg-fixed', 'bg-local', 'bg-scroll',
|
|
312
|
+
'bg-no-repeat', 'bg-repeat', 'bg-repeat-x', 'bg-repeat-y', 'bg-repeat-round', 'bg-repeat-space',
|
|
313
|
+
'bg-none', 'bg-origin-border', 'bg-origin-padding', 'bg-origin-content',
|
|
314
|
+
'bg-clip-border', 'bg-clip-padding', 'bg-clip-content', 'bg-clip-text',
|
|
315
|
+
// Gradient
|
|
316
|
+
'bg-gradient-to-t', 'bg-gradient-to-tr', 'bg-gradient-to-r', 'bg-gradient-to-br',
|
|
317
|
+
'bg-gradient-to-b', 'bg-gradient-to-bl', 'bg-gradient-to-l', 'bg-gradient-to-tl',
|
|
318
|
+
'bg-none',
|
|
319
|
+
// List style
|
|
320
|
+
'list-none', 'list-disc', 'list-decimal', 'list-image-none',
|
|
321
|
+
'list-inside', 'list-outside',
|
|
322
|
+
// Table
|
|
323
|
+
'table-auto', 'table-fixed',
|
|
324
|
+
'border-collapse', 'border-separate',
|
|
325
|
+
'border-spacing-0', 'border-spacing-1', 'border-spacing-2',
|
|
326
|
+
'border-spacing-x-0', 'border-spacing-y-0',
|
|
327
|
+
'table-layout-auto', 'table-layout-fixed',
|
|
328
|
+
'caption-top', 'caption-bottom',
|
|
329
|
+
// Vertical align
|
|
330
|
+
'align-baseline', 'align-top', 'align-middle', 'align-bottom',
|
|
331
|
+
'align-text-top', 'align-text-bottom', 'align-sub', 'align-super',
|
|
332
|
+
// Aspect ratio
|
|
333
|
+
'aspect-auto', 'aspect-square', 'aspect-video',
|
|
334
|
+
// Columns
|
|
335
|
+
'columns-1', 'columns-2', 'columns-3', 'columns-4', 'columns-5', 'columns-6',
|
|
336
|
+
'columns-7', 'columns-8', 'columns-9', 'columns-10', 'columns-11', 'columns-12',
|
|
337
|
+
'columns-auto', 'columns-3xs', 'columns-2xs', 'columns-xs', 'columns-sm',
|
|
338
|
+
'columns-md', 'columns-lg', 'columns-xl', 'columns-2xl', 'columns-3xl',
|
|
339
|
+
'columns-4xl', 'columns-5xl', 'columns-6xl', 'columns-7xl',
|
|
340
|
+
'break-before-auto', 'break-before-avoid', 'break-before-all', 'break-before-avoid-page', 'break-before-page',
|
|
341
|
+
'break-before-left', 'break-before-right', 'break-before-column',
|
|
342
|
+
'break-after-auto', 'break-after-avoid', 'break-after-all', 'break-after-avoid-page', 'break-after-page',
|
|
343
|
+
'break-after-left', 'break-after-right', 'break-after-column',
|
|
344
|
+
'break-inside-auto', 'break-inside-avoid', 'break-inside-avoid-page', 'break-inside-avoid-column',
|
|
345
|
+
// Float
|
|
346
|
+
'float-right', 'float-left', 'float-none', 'float-start', 'float-end',
|
|
347
|
+
'clear-left', 'clear-right', 'clear-both', 'clear-none', 'clear-start', 'clear-end',
|
|
348
|
+
// Box decoration
|
|
349
|
+
'decoration-slice', 'decoration-clone',
|
|
350
|
+
'box-decoration-slice', 'box-decoration-clone',
|
|
351
|
+
// Isolation
|
|
352
|
+
'isolate', 'isolation-auto',
|
|
353
|
+
// Overscroll
|
|
354
|
+
'overscroll-auto', 'overscroll-contain', 'overscroll-none',
|
|
355
|
+
'overscroll-x-auto', 'overscroll-x-contain', 'overscroll-x-none',
|
|
356
|
+
'overscroll-y-auto', 'overscroll-y-contain', 'overscroll-y-none',
|
|
357
|
+
'overscroll-behavior-auto', 'overscroll-behavior-contain', 'overscroll-behavior-none',
|
|
358
|
+
// Scroll snap
|
|
359
|
+
'snap-none', 'snap-x', 'snap-y', 'snap-both', 'snap-mandatory', 'snap-proximity',
|
|
360
|
+
'snap-start', 'snap-end', 'snap-center', 'snap-align-none', 'snap-normal', 'snap-always',
|
|
361
|
+
// Scroll behavior
|
|
362
|
+
'scroll-auto', 'scroll-smooth',
|
|
363
|
+
// Appearance
|
|
364
|
+
'appearance-none', 'appearance-auto',
|
|
365
|
+
// Touch
|
|
366
|
+
'touch-auto', 'touch-none', 'touch-pan-x', 'touch-pan-left', 'touch-pan-right',
|
|
367
|
+
'touch-pan-y', 'touch-pan-up', 'touch-pan-down', 'touch-pinch-zoom',
|
|
368
|
+
'touch-manipulation',
|
|
369
|
+
// Will change
|
|
370
|
+
'will-change-auto', 'will-change-scroll', 'will-change-contents', 'will-change-transform',
|
|
371
|
+
// Content
|
|
372
|
+
'content-none',
|
|
373
|
+
// Blend mode
|
|
374
|
+
'mix-blend-normal', 'mix-blend-multiply', 'mix-blend-screen', 'mix-blend-overlay',
|
|
375
|
+
'mix-blend-darken', 'mix-blend-lighten', 'mix-blend-color-dodge', 'mix-blend-color-burn',
|
|
376
|
+
'mix-blend-hard-light', 'mix-blend-soft-light', 'mix-blend-difference',
|
|
377
|
+
'mix-blend-exclusion', 'mix-blend-hue', 'mix-blend-saturation', 'mix-blend-color', 'mix-blend-luminosity',
|
|
378
|
+
'mix-blend-plus-darker', 'mix-blend-plus-lighter',
|
|
379
|
+
'bg-blend-normal', 'bg-blend-multiply', 'bg-blend-screen', 'bg-blend-overlay',
|
|
380
|
+
'bg-blend-darken', 'bg-blend-lighten', 'bg-blend-color-dodge', 'bg-blend-color-burn',
|
|
381
|
+
// Gap
|
|
382
|
+
'gap-0', 'gap-px', 'gap-0.5', 'gap-1', 'gap-2', 'gap-3', 'gap-4', 'gap-5',
|
|
383
|
+
'gap-6', 'gap-7', 'gap-8', 'gap-9', 'gap-10', 'gap-11', 'gap-12',
|
|
384
|
+
'gap-14', 'gap-16', 'gap-20', 'gap-24', 'gap-28', 'gap-32', 'gap-36', 'gap-40',
|
|
385
|
+
'gap-44', 'gap-48', 'gap-52', 'gap-56', 'gap-60', 'gap-64', 'gap-72', 'gap-80', 'gap-96',
|
|
386
|
+
'gap-x-0', 'gap-y-0',
|
|
387
|
+
// Space
|
|
388
|
+
'space-x-0', 'space-y-0',
|
|
389
|
+
// Divide
|
|
390
|
+
'divide-x', 'divide-y',
|
|
391
|
+
'divide-x-0', 'divide-y-0',
|
|
392
|
+
'divide-solid', 'divide-dashed', 'divide-dotted', 'divide-double', 'divide-none',
|
|
393
|
+
'divide-x-reverse', 'divide-y-reverse',
|
|
394
|
+
// Ring
|
|
395
|
+
'ring-inset',
|
|
396
|
+
// Line clamp
|
|
397
|
+
'line-clamp-1', 'line-clamp-2', 'line-clamp-3', 'line-clamp-4', 'line-clamp-5', 'line-clamp-6', 'line-clamp-none',
|
|
398
|
+
// Animation
|
|
399
|
+
'animate-none', 'animate-spin', 'animate-ping', 'animate-pulse', 'animate-bounce',
|
|
400
|
+
// Font family
|
|
401
|
+
'font-sans', 'font-serif', 'font-mono',
|
|
402
|
+
// Letter spacing (tracking)
|
|
403
|
+
'tracking-tighter', 'tracking-tight', 'tracking-normal',
|
|
404
|
+
'tracking-wide', 'tracking-wider', 'tracking-widest',
|
|
405
|
+
// Backdrop filters
|
|
406
|
+
'backdrop-blur', 'backdrop-blur-sm', 'backdrop-blur-md', 'backdrop-blur-lg',
|
|
407
|
+
'backdrop-blur-xl', 'backdrop-blur-2xl', 'backdrop-blur-3xl', 'backdrop-blur-none',
|
|
408
|
+
'backdrop-opacity-0', 'backdrop-opacity-50', 'backdrop-opacity-100',
|
|
409
|
+
];
|
|
410
|
+
}
|
|
411
|
+
// ─── Generated Utility Classes (from theme scales) ──────────────────────────
|
|
412
|
+
/**
|
|
413
|
+
* Generate all valid Tailwind utility classes by combining known prefixes with
|
|
414
|
+
* default theme scales + common string values for non-scale prefixes.
|
|
415
|
+
*/
|
|
416
|
+
function generateScaleBasedClasses() {
|
|
417
|
+
const classes = [];
|
|
418
|
+
// Color prefixes — generate bg-{color}, text-{color}, etc.
|
|
419
|
+
const colorPrefixes = [
|
|
420
|
+
'bg', 'text', 'border', 'ring', 'shadow', 'fill', 'stroke',
|
|
421
|
+
'accent', 'caret', 'outline', 'placeholder', 'divide', 'from', 'via', 'to',
|
|
422
|
+
'decoration',
|
|
423
|
+
];
|
|
424
|
+
// Color names from the default Tailwind palette
|
|
425
|
+
const colorNames = [
|
|
426
|
+
'inherit', 'current', 'transparent',
|
|
427
|
+
'white', 'black',
|
|
428
|
+
// Slate
|
|
429
|
+
'slate-50', 'slate-100', 'slate-200', 'slate-300', 'slate-400', 'slate-500',
|
|
430
|
+
'slate-600', 'slate-700', 'slate-800', 'slate-900', 'slate-950',
|
|
431
|
+
// Gray
|
|
432
|
+
'gray-50', 'gray-100', 'gray-200', 'gray-300', 'gray-400', 'gray-500',
|
|
433
|
+
'gray-600', 'gray-700', 'gray-800', 'gray-900', 'gray-950',
|
|
434
|
+
// Zinc
|
|
435
|
+
'zinc-50', 'zinc-100', 'zinc-200', 'zinc-300', 'zinc-400', 'zinc-500',
|
|
436
|
+
'zinc-600', 'zinc-700', 'zinc-800', 'zinc-900', 'zinc-950',
|
|
437
|
+
// Neutral
|
|
438
|
+
'neutral-50', 'neutral-100', 'neutral-200', 'neutral-300', 'neutral-400', 'neutral-500',
|
|
439
|
+
'neutral-600', 'neutral-700', 'neutral-800', 'neutral-900', 'neutral-950',
|
|
440
|
+
// Stone
|
|
441
|
+
'stone-50', 'stone-100', 'stone-200', 'stone-300', 'stone-400', 'stone-500',
|
|
442
|
+
'stone-600', 'stone-700', 'stone-800', 'stone-900', 'stone-950',
|
|
443
|
+
// Red
|
|
444
|
+
'red-50', 'red-100', 'red-200', 'red-300', 'red-400', 'red-500',
|
|
445
|
+
'red-600', 'red-700', 'red-800', 'red-900', 'red-950',
|
|
446
|
+
// Orange
|
|
447
|
+
'orange-50', 'orange-100', 'orange-200', 'orange-300', 'orange-400', 'orange-500',
|
|
448
|
+
'orange-600', 'orange-700', 'orange-800', 'orange-900', 'orange-950',
|
|
449
|
+
// Amber
|
|
450
|
+
'amber-50', 'amber-100', 'amber-200', 'amber-300', 'amber-400', 'amber-500',
|
|
451
|
+
'amber-600', 'amber-700', 'amber-800', 'amber-900', 'amber-950',
|
|
452
|
+
// Yellow
|
|
453
|
+
'yellow-50', 'yellow-100', 'yellow-200', 'yellow-300', 'yellow-400', 'yellow-500',
|
|
454
|
+
'yellow-600', 'yellow-700', 'yellow-800', 'yellow-900', 'yellow-950',
|
|
455
|
+
// Lime
|
|
456
|
+
'lime-50', 'lime-100', 'lime-200', 'lime-300', 'lime-400', 'lime-500',
|
|
457
|
+
'lime-600', 'lime-700', 'lime-800', 'lime-900', 'lime-950',
|
|
458
|
+
// Green
|
|
459
|
+
'green-50', 'green-100', 'green-200', 'green-300', 'green-400', 'green-500',
|
|
460
|
+
'green-600', 'green-700', 'green-800', 'green-900', 'green-950',
|
|
461
|
+
// Emerald
|
|
462
|
+
'emerald-50', 'emerald-100', 'emerald-200', 'emerald-300', 'emerald-400', 'emerald-500',
|
|
463
|
+
'emerald-600', 'emerald-700', 'emerald-800', 'emerald-900', 'emerald-950',
|
|
464
|
+
// Teal
|
|
465
|
+
'teal-50', 'teal-100', 'teal-200', 'teal-300', 'teal-400', 'teal-500',
|
|
466
|
+
'teal-600', 'teal-700', 'teal-800', 'teal-900', 'teal-950',
|
|
467
|
+
// Cyan
|
|
468
|
+
'cyan-50', 'cyan-100', 'cyan-200', 'cyan-300', 'cyan-400', 'cyan-500',
|
|
469
|
+
'cyan-600', 'cyan-700', 'cyan-800', 'cyan-900', 'cyan-950',
|
|
470
|
+
// Sky
|
|
471
|
+
'sky-50', 'sky-100', 'sky-200', 'sky-300', 'sky-400', 'sky-500',
|
|
472
|
+
'sky-600', 'sky-700', 'sky-800', 'sky-900', 'sky-950',
|
|
473
|
+
// Blue
|
|
474
|
+
'blue-50', 'blue-100', 'blue-200', 'blue-300', 'blue-400', 'blue-500',
|
|
475
|
+
'blue-600', 'blue-700', 'blue-800', 'blue-900', 'blue-950',
|
|
476
|
+
// Indigo
|
|
477
|
+
'indigo-50', 'indigo-100', 'indigo-200', 'indigo-300', 'indigo-400', 'indigo-500',
|
|
478
|
+
'indigo-600', 'indigo-700', 'indigo-800', 'indigo-900', 'indigo-950',
|
|
479
|
+
// Violet
|
|
480
|
+
'violet-50', 'violet-100', 'violet-200', 'violet-300', 'violet-400', 'violet-500',
|
|
481
|
+
'violet-600', 'violet-700', 'violet-800', 'violet-900', 'violet-950',
|
|
482
|
+
// Purple
|
|
483
|
+
'purple-50', 'purple-100', 'purple-200', 'purple-300', 'purple-400', 'purple-500',
|
|
484
|
+
'purple-600', 'purple-700', 'purple-800', 'purple-900', 'purple-950',
|
|
485
|
+
// Fuchsia
|
|
486
|
+
'fuchsia-50', 'fuchsia-100', 'fuchsia-200', 'fuchsia-300', 'fuchsia-400', 'fuchsia-500',
|
|
487
|
+
'fuchsia-600', 'fuchsia-700', 'fuchsia-800', 'fuchsia-900', 'fuchsia-950',
|
|
488
|
+
// Pink
|
|
489
|
+
'pink-50', 'pink-100', 'pink-200', 'pink-300', 'pink-400', 'pink-500',
|
|
490
|
+
'pink-600', 'pink-700', 'pink-800', 'pink-900', 'pink-950',
|
|
491
|
+
// Rose
|
|
492
|
+
'rose-50', 'rose-100', 'rose-200', 'rose-300', 'rose-400', 'rose-500',
|
|
493
|
+
'rose-600', 'rose-700', 'rose-800', 'rose-900', 'rose-950',
|
|
494
|
+
];
|
|
495
|
+
// Special opacity variants for color prefixes that support it
|
|
496
|
+
const dividerColors = new Set(['divide', 'border', 'ring', 'placeholder', 'text']);
|
|
497
|
+
const opacityNumbers = ['0', '5', '10', '20', '25', '30', '40', '50', '60', '70', '75', '80', '90', '95', '100'];
|
|
498
|
+
for (const prefix of colorPrefixes) {
|
|
499
|
+
for (const colorName of colorNames) {
|
|
500
|
+
classes.push(`${prefix}-${colorName}`);
|
|
501
|
+
// Opacity modifiers for supported prefixes
|
|
502
|
+
if (dividerColors.has(prefix)) {
|
|
503
|
+
for (const opacity of opacityNumbers) {
|
|
504
|
+
classes.push(`${prefix}-${colorName}/${opacity}`);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
// Font size prefixes
|
|
510
|
+
const fontSizePrefixes = ['text'];
|
|
511
|
+
const fontSizeNames = [
|
|
512
|
+
'xs', 'sm', 'base', 'lg', 'xl', '2xl', '3xl', '4xl', '5xl', '6xl', '7xl', '8xl', '9xl',
|
|
513
|
+
];
|
|
514
|
+
for (const prefix of fontSizePrefixes) {
|
|
515
|
+
for (const sizeName of fontSizeNames) {
|
|
516
|
+
classes.push(`${prefix}-${sizeName}`);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
// Border radius prefixes
|
|
520
|
+
const radiusPrefixes = [
|
|
521
|
+
'rounded', 'rounded-t', 'rounded-r', 'rounded-b', 'rounded-l',
|
|
522
|
+
'rounded-tl', 'rounded-tr', 'rounded-br', 'rounded-bl',
|
|
523
|
+
'rounded-s', 'rounded-e', 'rounded-ss', 'rounded-se', 'rounded-es', 'rounded-ee',
|
|
524
|
+
];
|
|
525
|
+
const radiusNames = ['none', 'sm', '', 'md', 'lg', 'xl', '2xl', '3xl', 'full'];
|
|
526
|
+
for (const prefix of radiusPrefixes) {
|
|
527
|
+
for (const radiusName of radiusNames) {
|
|
528
|
+
if (radiusName === '') {
|
|
529
|
+
classes.push(prefix); // "rounded" alone
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
classes.push(`${prefix}-${radiusName}`);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
// ── Spacing-scale utilities ─────────────────────────────────────────
|
|
537
|
+
// Padding, margin, position, inset, gap-x, gap-y, space-x, space-y,
|
|
538
|
+
// size, leading, indent, scroll-margin/padding — all accept the default
|
|
539
|
+
// spacing scale as values.
|
|
540
|
+
const spacingScaleValues = [
|
|
541
|
+
'0', 'px', '0.5', '1', '1.5', '2', '2.5', '3', '3.5', '4', '5', '6', '7',
|
|
542
|
+
'8', '9', '10', '11', '12', '14', '16', '20', '24', '28', '32', '36', '40',
|
|
543
|
+
'44', '48', '52', '56', '60', '64', '72', '80', '96',
|
|
544
|
+
];
|
|
545
|
+
// Spacing-based prefixes — utilities that accept spacing scale values
|
|
546
|
+
const spacingPrefixes = [
|
|
547
|
+
// Padding
|
|
548
|
+
'p', 'px', 'py', 'pt', 'pr', 'pb', 'pl',
|
|
549
|
+
// Margin
|
|
550
|
+
'm', 'mx', 'my', 'mt', 'mr', 'mb', 'ml',
|
|
551
|
+
// Position
|
|
552
|
+
'top', 'right', 'bottom', 'left',
|
|
553
|
+
// Inset
|
|
554
|
+
'inset', 'inset-x', 'inset-y',
|
|
555
|
+
// Gap (gap-{x,y} — full scale; gap base already in static)
|
|
556
|
+
'gap-x', 'gap-y',
|
|
557
|
+
// Space (space-{x,y} — full scale)
|
|
558
|
+
'space-x', 'space-y',
|
|
559
|
+
// Size (both width and height)
|
|
560
|
+
'size',
|
|
561
|
+
// Scroll margin / padding
|
|
562
|
+
'scroll-m', 'scroll-mx', 'scroll-my', 'scroll-mt', 'scroll-mr', 'scroll-mb', 'scroll-ml',
|
|
563
|
+
'scroll-p', 'scroll-px', 'scroll-py', 'scroll-pt', 'scroll-pr', 'scroll-pb', 'scroll-pl',
|
|
564
|
+
];
|
|
565
|
+
for (const prefix of spacingPrefixes) {
|
|
566
|
+
for (const value of spacingScaleValues) {
|
|
567
|
+
classes.push(`${prefix}-${value}`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
// Special non-scale values for margin: auto
|
|
571
|
+
for (const prefix of ['m', 'mx', 'my', 'mt', 'mr', 'mb', 'ml']) {
|
|
572
|
+
classes.push(`${prefix}-auto`);
|
|
573
|
+
}
|
|
574
|
+
// Special non-scale values for position: auto, fractions, full
|
|
575
|
+
for (const prefix of ['top', 'right', 'bottom', 'left']) {
|
|
576
|
+
classes.push(`${prefix}-auto`);
|
|
577
|
+
classes.push(`${prefix}-full`);
|
|
578
|
+
for (const frac of ['1/2', '1/3', '2/3', '1/4', '3/4']) {
|
|
579
|
+
classes.push(`${prefix}-${frac}`);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
// Special non-scale values for inset: auto, full, fractions
|
|
583
|
+
for (const prefix of ['inset', 'inset-x', 'inset-y']) {
|
|
584
|
+
classes.push(`${prefix}-auto`);
|
|
585
|
+
classes.push(`${prefix}-full`);
|
|
586
|
+
for (const frac of ['1/2', '1/3', '2/3', '1/4', '3/4']) {
|
|
587
|
+
classes.push(`${prefix}-${frac}`);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
// Leading (line-height) — spacing values + text keywords
|
|
591
|
+
for (const value of spacingScaleValues) {
|
|
592
|
+
classes.push(`leading-${value}`);
|
|
593
|
+
}
|
|
594
|
+
for (const kw of ['none', 'tight', 'snug', 'normal', 'relaxed', 'loose']) {
|
|
595
|
+
classes.push(`leading-${kw}`);
|
|
596
|
+
}
|
|
597
|
+
// Indent — spacing scale values
|
|
598
|
+
for (const value of spacingScaleValues) {
|
|
599
|
+
classes.push(`indent-${value}`);
|
|
600
|
+
}
|
|
601
|
+
// Border-width directional — only major widths for border-{side}-{width}
|
|
602
|
+
for (const side of ['t', 'r', 'b', 'l', 'x', 'y']) {
|
|
603
|
+
for (const w of ['0', '2', '4', '8']) {
|
|
604
|
+
classes.push(`border-${side}-${w}`);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
// Opacity modifiers for bg/text/shadow/ring (non-divider color prefixes)
|
|
608
|
+
// — divider colors (border/ring/placeholder/divide/text) already generated
|
|
609
|
+
const nonDividerOpacityPrefixes = ['bg', 'shadow'];
|
|
610
|
+
const opacityValues = ['0', '5', '10', '20', '25', '30', '40', '50', '60', '70', '75', '80', '90', '95', '100'];
|
|
611
|
+
for (const prefix of nonDividerOpacityPrefixes) {
|
|
612
|
+
for (const op of opacityValues) {
|
|
613
|
+
classes.push(`${prefix}-opacity-${op}`);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
// Translate — fractions and full
|
|
617
|
+
for (const prefix of ['translate-x', 'translate-y']) {
|
|
618
|
+
for (const frac of ['1/2', '1/3', '2/3', '1/4', '3/4', 'full']) {
|
|
619
|
+
classes.push(`${prefix}-${frac}`);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
// Grid column spans — col-span-{1..12}, col-start-{1..13}, col-end-{1..13}
|
|
623
|
+
for (let i = 1; i <= 12; i++)
|
|
624
|
+
classes.push(`col-span-${i}`);
|
|
625
|
+
for (let i = 1; i <= 13; i++) {
|
|
626
|
+
classes.push(`col-start-${i}`);
|
|
627
|
+
classes.push(`col-end-${i}`);
|
|
628
|
+
}
|
|
629
|
+
// Grid row spans — row-span-{1..6}, row-start-{1..7}, row-end-{1..7}
|
|
630
|
+
for (let i = 1; i <= 6; i++)
|
|
631
|
+
classes.push(`row-span-${i}`);
|
|
632
|
+
for (let i = 1; i <= 7; i++) {
|
|
633
|
+
classes.push(`row-start-${i}`);
|
|
634
|
+
classes.push(`row-end-${i}`);
|
|
635
|
+
}
|
|
636
|
+
// Outline utilities
|
|
637
|
+
classes.push('outline-none', 'outline', 'outline-dashed', 'outline-dotted', 'outline-double');
|
|
638
|
+
return classes;
|
|
639
|
+
}
|
|
640
|
+
// ─── Main Expander API ──────────────────────────────────────────────────────
|
|
641
|
+
/** Cached bundled utility classes. */
|
|
642
|
+
let _bundledClasses = null;
|
|
643
|
+
/**
|
|
644
|
+
* Get the full set of bundled Tailwind utility classes.
|
|
645
|
+
* Generated from the default theme scales plus static utilities.
|
|
646
|
+
* Cached after first call — callers get the same Set instance.
|
|
647
|
+
*/
|
|
648
|
+
export function getBundledUtilityClasses() {
|
|
649
|
+
if (_bundledClasses)
|
|
650
|
+
return _bundledClasses;
|
|
651
|
+
_bundledClasses = new Set();
|
|
652
|
+
// Static utilities
|
|
653
|
+
for (const cls of getStaticUtilityClasses()) {
|
|
654
|
+
_bundledClasses.add(cls);
|
|
655
|
+
}
|
|
656
|
+
// Scale-generated utilities
|
|
657
|
+
for (const cls of generateScaleBasedClasses()) {
|
|
658
|
+
_bundledClasses.add(cls);
|
|
659
|
+
}
|
|
660
|
+
return _bundledClasses;
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Tailwind utility class expander.
|
|
664
|
+
*
|
|
665
|
+
* Validates a class name against the full expansion path:
|
|
666
|
+
* 1. Bundled default theme dictionary
|
|
667
|
+
* 2. Project config custom classes (if resolved)
|
|
668
|
+
* 3. Arbitrary-value grammar (`prefix-[...]`)
|
|
669
|
+
* 4. Variant prefix stripping + retry against all of the above
|
|
670
|
+
*
|
|
671
|
+
* Fail-open: if project config resolution is requested but fails, the
|
|
672
|
+
* expander enters fail-open mode where project-config tier is silently
|
|
673
|
+
* empty. The CALLER is responsible for emitting the visible warning and
|
|
674
|
+
* potentially disabling the undefined-class detector.
|
|
675
|
+
*/
|
|
676
|
+
export class TailwindUtilityExpander {
|
|
677
|
+
bundled = null;
|
|
678
|
+
projectClasses = null;
|
|
679
|
+
_configFailed = false;
|
|
680
|
+
_configFailureReason = null;
|
|
681
|
+
/**
|
|
682
|
+
* Initialize the expander with optional project config.
|
|
683
|
+
* Call once at the start of a project audit.
|
|
684
|
+
*/
|
|
685
|
+
init(config = {}) {
|
|
686
|
+
this.bundled = getBundledUtilityClasses();
|
|
687
|
+
if (config.customClasses) {
|
|
688
|
+
this.projectClasses = new Set(config.customClasses);
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
if (config.useProjectConfig && config.projectRoot) {
|
|
692
|
+
try {
|
|
693
|
+
const result = loadTailwindConfig(config.projectRoot);
|
|
694
|
+
if (result && result.tokens) {
|
|
695
|
+
this.projectClasses = new Set();
|
|
696
|
+
// Generate utility classes from project-specific theme tokens
|
|
697
|
+
const colorPrefixes = ['bg', 'text', 'border', 'ring', 'shadow', 'fill', 'stroke', 'accent', 'caret'];
|
|
698
|
+
for (const colorName of Object.keys(result.tokens.colors)) {
|
|
699
|
+
for (const prefix of colorPrefixes) {
|
|
700
|
+
this.projectClasses.add(`${prefix}-${colorName}`);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
for (const spaceName of Object.keys(result.tokens.spacing)) {
|
|
704
|
+
for (const prefix of [
|
|
705
|
+
'p', 'px', 'py', 'pt', 'pr', 'pb', 'pl',
|
|
706
|
+
'm', 'mx', 'my', 'mt', 'mr', 'mb', 'ml',
|
|
707
|
+
'w', 'h', 'min-w', 'min-h', 'max-w', 'max-h',
|
|
708
|
+
'gap', 'gap-x', 'gap-y', 'space-x', 'space-y',
|
|
709
|
+
'top', 'right', 'bottom', 'left', 'inset',
|
|
710
|
+
'inset-x', 'inset-y',
|
|
711
|
+
'leading', 'indent',
|
|
712
|
+
]) {
|
|
713
|
+
this.projectClasses.add(`${prefix}-${spaceName}`);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
for (const sizeName of Object.keys(result.tokens.fontSize)) {
|
|
717
|
+
this.projectClasses.add(`text-${sizeName}`);
|
|
718
|
+
}
|
|
719
|
+
for (const radiusName of Object.keys(result.tokens.borderRadius)) {
|
|
720
|
+
this.projectClasses.add(`rounded-${radiusName}`);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
catch (err) {
|
|
725
|
+
this._configFailed = true;
|
|
726
|
+
this._configFailureReason = err instanceof Error ? err.message : String(err);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
/** Did the project config resolution fail? */
|
|
731
|
+
get configFailed() {
|
|
732
|
+
return this._configFailed;
|
|
733
|
+
}
|
|
734
|
+
/** Why did project config resolution fail? */
|
|
735
|
+
get configFailureReason() {
|
|
736
|
+
return this._configFailureReason;
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Resolve a single class name against the expansion path.
|
|
740
|
+
* Returns the highest-priority matching tier, or 'none'.
|
|
741
|
+
*/
|
|
742
|
+
resolve(className) {
|
|
743
|
+
const bundled = this.bundled ?? getBundledUtilityClasses();
|
|
744
|
+
// 1. Check raw class against bundled dictionary
|
|
745
|
+
if (bundled.has(className)) {
|
|
746
|
+
return { valid: true, tier: 'bundled' };
|
|
747
|
+
}
|
|
748
|
+
// 2. Check raw class against project config
|
|
749
|
+
if (this.projectClasses?.has(className)) {
|
|
750
|
+
return { valid: true, tier: 'project-config' };
|
|
751
|
+
}
|
|
752
|
+
// 3. Check opacity-modifier syntax: {color-utility}/{opacity}
|
|
753
|
+
if (className.includes('/')) {
|
|
754
|
+
const result = this.checkOpacityModifier(className);
|
|
755
|
+
if (result)
|
|
756
|
+
return result;
|
|
757
|
+
}
|
|
758
|
+
// 4. Check arbitrary-value grammar: prefix-[...]
|
|
759
|
+
if (this.matchesArbitraryValue(className)) {
|
|
760
|
+
return { valid: true, tier: 'arbitrary-value' };
|
|
761
|
+
}
|
|
762
|
+
// 5. Check negative-value utility: -{prefix}-{value}
|
|
763
|
+
const negResult = this.checkNegativeUtility(className);
|
|
764
|
+
if (negResult)
|
|
765
|
+
return negResult;
|
|
766
|
+
// 6. Strip variant prefix and retry
|
|
767
|
+
const stripped = this.stripVariantPrefix(className);
|
|
768
|
+
if (stripped !== className) {
|
|
769
|
+
return this.resolve(stripped); // Recurse once with the stripped class
|
|
770
|
+
}
|
|
771
|
+
return { valid: false, tier: 'none' };
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Check whether a class matches the arbitrary-value grammar:
|
|
775
|
+
* `{known-prefix}-[...]`
|
|
776
|
+
*/
|
|
777
|
+
matchesArbitraryValue(className) {
|
|
778
|
+
const bracketIdx = className.indexOf('[');
|
|
779
|
+
if (bracketIdx <= 0)
|
|
780
|
+
return false;
|
|
781
|
+
// Extract the prefix before the [
|
|
782
|
+
const prefix = className.substring(0, bracketIdx);
|
|
783
|
+
// Handle prefix-[value] where prefix ends with '-'
|
|
784
|
+
if (prefix.endsWith('-')) {
|
|
785
|
+
const normalizedPrefix = prefix.slice(0, -1);
|
|
786
|
+
return ARBITRARY_VALUE_PREFIXES.has(normalizedPrefix);
|
|
787
|
+
}
|
|
788
|
+
return false;
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Check the opacity-modifier syntax: `{utility}/{opacity}`.
|
|
792
|
+
* e.g., `bg-red-500/50` → `bg-red-500` must be a valid utility AND
|
|
793
|
+
* `50` must be a valid opacity value.
|
|
794
|
+
*
|
|
795
|
+
* We split on the LAST `/` to avoid confusing fractional widths
|
|
796
|
+
* (e.g. `w-1/2`) — those are in the dictionary directly and won't
|
|
797
|
+
* reach this check.
|
|
798
|
+
*/
|
|
799
|
+
checkOpacityModifier(className) {
|
|
800
|
+
const lastSlash = className.lastIndexOf('/');
|
|
801
|
+
if (lastSlash <= 0)
|
|
802
|
+
return null;
|
|
803
|
+
const prefix = className.substring(0, lastSlash);
|
|
804
|
+
const suffix = className.substring(lastSlash + 1);
|
|
805
|
+
// Suffix must be a numeric opacity value (0-100)
|
|
806
|
+
if (!/^\d{1,3}$/.test(suffix))
|
|
807
|
+
return null;
|
|
808
|
+
const opacity = parseInt(suffix, 10);
|
|
809
|
+
if (opacity < 0 || opacity > 100)
|
|
810
|
+
return null;
|
|
811
|
+
// Prefix must be a known utility
|
|
812
|
+
const bundled = this.bundled ?? getBundledUtilityClasses();
|
|
813
|
+
if (bundled.has(prefix) || this.projectClasses?.has(prefix)) {
|
|
814
|
+
return { valid: true, tier: 'project-config' };
|
|
815
|
+
}
|
|
816
|
+
return null;
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Check a negative utility: `-{prefix}-{value}`.
|
|
820
|
+
* e.g., `-mt-1`, `-top-2`, `-translate-x-1/2`, `-z-10`.
|
|
821
|
+
*
|
|
822
|
+
* Strips the leading `-` and checks whether the remainder is a valid
|
|
823
|
+
* utility whose prefix is in the negative-allowed set.
|
|
824
|
+
*/
|
|
825
|
+
checkNegativeUtility(className) {
|
|
826
|
+
if (!className.startsWith('-'))
|
|
827
|
+
return null;
|
|
828
|
+
const positive = className.slice(1); // strip leading '-'
|
|
829
|
+
// Determine the prefix of the positive utility
|
|
830
|
+
// e.g., "top-2" → prefix="top", "translate-x-1/2" → prefix="translate-x"
|
|
831
|
+
const lastDash = positive.lastIndexOf('-');
|
|
832
|
+
const prefix = lastDash > 0 ? positive.substring(0, lastDash) : positive;
|
|
833
|
+
if (!NEGATIVE_ALLOWED_PREFIXES.has(prefix))
|
|
834
|
+
return null;
|
|
835
|
+
// Recurse: is the positive utility itself valid?
|
|
836
|
+
const result = this.resolve(positive);
|
|
837
|
+
if (result.valid) {
|
|
838
|
+
return { valid: true, tier: result.tier };
|
|
839
|
+
}
|
|
840
|
+
return null;
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Strip a Tailwind variant prefix from a class name.
|
|
844
|
+
* E.g., "hover:bg-blue-500" → "bg-blue-500"
|
|
845
|
+
* Returns the original if no variant prefix is matched.
|
|
846
|
+
*/
|
|
847
|
+
stripVariantPrefix(className) {
|
|
848
|
+
for (const prefix of VARIANT_PREFIXES) {
|
|
849
|
+
if (className.startsWith(prefix + ':')) {
|
|
850
|
+
return className.slice(prefix.length + 1);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
return className;
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Check if a class has a variant prefix at all.
|
|
857
|
+
*/
|
|
858
|
+
hasVariantPrefix(className) {
|
|
859
|
+
return className.includes(':') && this.stripVariantPrefix(className) !== className;
|
|
860
|
+
}
|
|
861
|
+
/** Reset state (useful for testing). */
|
|
862
|
+
reset() {
|
|
863
|
+
this.bundled = null;
|
|
864
|
+
this.projectClasses = null;
|
|
865
|
+
this._configFailed = false;
|
|
866
|
+
this._configFailureReason = null;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
/** Singleton instance for the analyzer layer. */
|
|
870
|
+
let _instance = null;
|
|
871
|
+
export function getTailwindExpander() {
|
|
872
|
+
if (!_instance) {
|
|
873
|
+
_instance = new TailwindUtilityExpander();
|
|
874
|
+
}
|
|
875
|
+
return _instance;
|
|
876
|
+
}
|
|
877
|
+
export function resetTailwindExpander() {
|
|
878
|
+
_instance = null;
|
|
879
|
+
}
|
|
880
|
+
//# sourceMappingURL=tailwindUtilityExpander.js.map
|