@nuasite/cms-marker 0.0.64 → 0.0.66
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/dist/types/build-processor.d.ts +2 -1
- package/dist/types/build-processor.d.ts.map +1 -1
- package/dist/types/component-registry.d.ts.map +1 -1
- package/dist/types/config.d.ts +19 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/dev-middleware.d.ts +10 -2
- package/dist/types/dev-middleware.d.ts.map +1 -1
- package/dist/types/error-collector.d.ts +56 -0
- package/dist/types/error-collector.d.ts.map +1 -0
- package/dist/types/html-processor.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/manifest-writer.d.ts +11 -2
- package/dist/types/manifest-writer.d.ts.map +1 -1
- package/dist/types/source-finder.d.ts +18 -3
- package/dist/types/source-finder.d.ts.map +1 -1
- package/dist/types/tailwind-colors.d.ts +6 -30
- package/dist/types/tailwind-colors.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/dist/types/types.d.ts +25 -9
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/vite-plugin.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/build-processor.ts +73 -19
- package/src/component-registry.ts +2 -0
- package/src/config.ts +29 -0
- package/src/dev-middleware.ts +12 -4
- package/src/error-collector.ts +106 -0
- package/src/html-processor.ts +55 -37
- package/src/index.ts +20 -4
- package/src/manifest-writer.ts +38 -7
- package/src/source-finder.ts +1003 -295
- package/src/tailwind-colors.ts +511 -121
- package/src/types.ts +27 -9
- package/src/vite-plugin.ts +4 -12
- package/dist/types/astro-transform.d.ts +0 -21
- package/dist/types/astro-transform.d.ts.map +0 -1
- package/src/astro-transform.ts +0 -205
package/src/tailwind-colors.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import fs from 'node:fs/promises'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
-
import
|
|
3
|
+
import { getProjectRoot } from './config'
|
|
4
|
+
import type { AvailableColors, AvailableTextStyles, ColorClasses, TailwindColor, TextStyleValue } from './types'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Default Tailwind CSS v4 color names.
|
|
7
|
-
* These are available by default in Tailwind v4.
|
|
8
8
|
*/
|
|
9
9
|
export const DEFAULT_TAILWIND_COLORS = [
|
|
10
10
|
'slate',
|
|
@@ -41,50 +41,384 @@ export const STANDARD_SHADES = ['50', '100', '200', '300', '400', '500', '600',
|
|
|
41
41
|
*/
|
|
42
42
|
export const SPECIAL_COLORS = ['transparent', 'current', 'inherit', 'white', 'black'] as const
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Complete Tailwind v4 default color palette with all shade values.
|
|
46
|
+
*/
|
|
47
|
+
const DEFAULT_COLOR_VALUES: Record<string, Record<string, string>> = {
|
|
48
|
+
slate: {
|
|
49
|
+
'50': '#f8fafc',
|
|
50
|
+
'100': '#f1f5f9',
|
|
51
|
+
'200': '#e2e8f0',
|
|
52
|
+
'300': '#cbd5e1',
|
|
53
|
+
'400': '#94a3b8',
|
|
54
|
+
'500': '#64748b',
|
|
55
|
+
'600': '#475569',
|
|
56
|
+
'700': '#334155',
|
|
57
|
+
'800': '#1e293b',
|
|
58
|
+
'900': '#0f172a',
|
|
59
|
+
'950': '#020617',
|
|
60
|
+
},
|
|
61
|
+
gray: {
|
|
62
|
+
'50': '#f9fafb',
|
|
63
|
+
'100': '#f3f4f6',
|
|
64
|
+
'200': '#e5e7eb',
|
|
65
|
+
'300': '#d1d5db',
|
|
66
|
+
'400': '#9ca3af',
|
|
67
|
+
'500': '#6b7280',
|
|
68
|
+
'600': '#4b5563',
|
|
69
|
+
'700': '#374151',
|
|
70
|
+
'800': '#1f2937',
|
|
71
|
+
'900': '#111827',
|
|
72
|
+
'950': '#030712',
|
|
73
|
+
},
|
|
74
|
+
zinc: {
|
|
75
|
+
'50': '#fafafa',
|
|
76
|
+
'100': '#f4f4f5',
|
|
77
|
+
'200': '#e4e4e7',
|
|
78
|
+
'300': '#d4d4d8',
|
|
79
|
+
'400': '#a1a1aa',
|
|
80
|
+
'500': '#71717a',
|
|
81
|
+
'600': '#52525b',
|
|
82
|
+
'700': '#3f3f46',
|
|
83
|
+
'800': '#27272a',
|
|
84
|
+
'900': '#18181b',
|
|
85
|
+
'950': '#09090b',
|
|
86
|
+
},
|
|
87
|
+
neutral: {
|
|
88
|
+
'50': '#fafafa',
|
|
89
|
+
'100': '#f5f5f5',
|
|
90
|
+
'200': '#e5e5e5',
|
|
91
|
+
'300': '#d4d4d4',
|
|
92
|
+
'400': '#a3a3a3',
|
|
93
|
+
'500': '#737373',
|
|
94
|
+
'600': '#525252',
|
|
95
|
+
'700': '#404040',
|
|
96
|
+
'800': '#262626',
|
|
97
|
+
'900': '#171717',
|
|
98
|
+
'950': '#0a0a0a',
|
|
99
|
+
},
|
|
100
|
+
stone: {
|
|
101
|
+
'50': '#fafaf9',
|
|
102
|
+
'100': '#f5f5f4',
|
|
103
|
+
'200': '#e7e5e4',
|
|
104
|
+
'300': '#d6d3d1',
|
|
105
|
+
'400': '#a8a29e',
|
|
106
|
+
'500': '#78716c',
|
|
107
|
+
'600': '#57534e',
|
|
108
|
+
'700': '#44403c',
|
|
109
|
+
'800': '#292524',
|
|
110
|
+
'900': '#1c1917',
|
|
111
|
+
'950': '#0c0a09',
|
|
112
|
+
},
|
|
113
|
+
red: {
|
|
114
|
+
'50': '#fef2f2',
|
|
115
|
+
'100': '#fee2e2',
|
|
116
|
+
'200': '#fecaca',
|
|
117
|
+
'300': '#fca5a5',
|
|
118
|
+
'400': '#f87171',
|
|
119
|
+
'500': '#ef4444',
|
|
120
|
+
'600': '#dc2626',
|
|
121
|
+
'700': '#b91c1c',
|
|
122
|
+
'800': '#991b1b',
|
|
123
|
+
'900': '#7f1d1d',
|
|
124
|
+
'950': '#450a0a',
|
|
125
|
+
},
|
|
126
|
+
orange: {
|
|
127
|
+
'50': '#fff7ed',
|
|
128
|
+
'100': '#ffedd5',
|
|
129
|
+
'200': '#fed7aa',
|
|
130
|
+
'300': '#fdba74',
|
|
131
|
+
'400': '#fb923c',
|
|
132
|
+
'500': '#f97316',
|
|
133
|
+
'600': '#ea580c',
|
|
134
|
+
'700': '#c2410c',
|
|
135
|
+
'800': '#9a3412',
|
|
136
|
+
'900': '#7c2d12',
|
|
137
|
+
'950': '#431407',
|
|
138
|
+
},
|
|
139
|
+
amber: {
|
|
140
|
+
'50': '#fffbeb',
|
|
141
|
+
'100': '#fef3c7',
|
|
142
|
+
'200': '#fde68a',
|
|
143
|
+
'300': '#fcd34d',
|
|
144
|
+
'400': '#fbbf24',
|
|
145
|
+
'500': '#f59e0b',
|
|
146
|
+
'600': '#d97706',
|
|
147
|
+
'700': '#b45309',
|
|
148
|
+
'800': '#92400e',
|
|
149
|
+
'900': '#78350f',
|
|
150
|
+
'950': '#451a03',
|
|
151
|
+
},
|
|
152
|
+
yellow: {
|
|
153
|
+
'50': '#fefce8',
|
|
154
|
+
'100': '#fef9c3',
|
|
155
|
+
'200': '#fef08a',
|
|
156
|
+
'300': '#fde047',
|
|
157
|
+
'400': '#facc15',
|
|
158
|
+
'500': '#eab308',
|
|
159
|
+
'600': '#ca8a04',
|
|
160
|
+
'700': '#a16207',
|
|
161
|
+
'800': '#854d0e',
|
|
162
|
+
'900': '#713f12',
|
|
163
|
+
'950': '#422006',
|
|
164
|
+
},
|
|
165
|
+
lime: {
|
|
166
|
+
'50': '#f7fee7',
|
|
167
|
+
'100': '#ecfccb',
|
|
168
|
+
'200': '#d9f99d',
|
|
169
|
+
'300': '#bef264',
|
|
170
|
+
'400': '#a3e635',
|
|
171
|
+
'500': '#84cc16',
|
|
172
|
+
'600': '#65a30d',
|
|
173
|
+
'700': '#4d7c0f',
|
|
174
|
+
'800': '#3f6212',
|
|
175
|
+
'900': '#365314',
|
|
176
|
+
'950': '#1a2e05',
|
|
177
|
+
},
|
|
178
|
+
green: {
|
|
179
|
+
'50': '#f0fdf4',
|
|
180
|
+
'100': '#dcfce7',
|
|
181
|
+
'200': '#bbf7d0',
|
|
182
|
+
'300': '#86efac',
|
|
183
|
+
'400': '#4ade80',
|
|
184
|
+
'500': '#22c55e',
|
|
185
|
+
'600': '#16a34a',
|
|
186
|
+
'700': '#15803d',
|
|
187
|
+
'800': '#166534',
|
|
188
|
+
'900': '#14532d',
|
|
189
|
+
'950': '#052e16',
|
|
190
|
+
},
|
|
191
|
+
emerald: {
|
|
192
|
+
'50': '#ecfdf5',
|
|
193
|
+
'100': '#d1fae5',
|
|
194
|
+
'200': '#a7f3d0',
|
|
195
|
+
'300': '#6ee7b7',
|
|
196
|
+
'400': '#34d399',
|
|
197
|
+
'500': '#10b981',
|
|
198
|
+
'600': '#059669',
|
|
199
|
+
'700': '#047857',
|
|
200
|
+
'800': '#065f46',
|
|
201
|
+
'900': '#064e3b',
|
|
202
|
+
'950': '#022c22',
|
|
203
|
+
},
|
|
204
|
+
teal: {
|
|
205
|
+
'50': '#f0fdfa',
|
|
206
|
+
'100': '#ccfbf1',
|
|
207
|
+
'200': '#99f6e4',
|
|
208
|
+
'300': '#5eead4',
|
|
209
|
+
'400': '#2dd4bf',
|
|
210
|
+
'500': '#14b8a6',
|
|
211
|
+
'600': '#0d9488',
|
|
212
|
+
'700': '#0f766e',
|
|
213
|
+
'800': '#115e59',
|
|
214
|
+
'900': '#134e4a',
|
|
215
|
+
'950': '#042f2e',
|
|
216
|
+
},
|
|
217
|
+
cyan: {
|
|
218
|
+
'50': '#ecfeff',
|
|
219
|
+
'100': '#cffafe',
|
|
220
|
+
'200': '#a5f3fc',
|
|
221
|
+
'300': '#67e8f9',
|
|
222
|
+
'400': '#22d3ee',
|
|
223
|
+
'500': '#06b6d4',
|
|
224
|
+
'600': '#0891b2',
|
|
225
|
+
'700': '#0e7490',
|
|
226
|
+
'800': '#155e75',
|
|
227
|
+
'900': '#164e63',
|
|
228
|
+
'950': '#083344',
|
|
229
|
+
},
|
|
230
|
+
sky: {
|
|
231
|
+
'50': '#f0f9ff',
|
|
232
|
+
'100': '#e0f2fe',
|
|
233
|
+
'200': '#bae6fd',
|
|
234
|
+
'300': '#7dd3fc',
|
|
235
|
+
'400': '#38bdf8',
|
|
236
|
+
'500': '#0ea5e9',
|
|
237
|
+
'600': '#0284c7',
|
|
238
|
+
'700': '#0369a1',
|
|
239
|
+
'800': '#075985',
|
|
240
|
+
'900': '#0c4a6e',
|
|
241
|
+
'950': '#082f49',
|
|
242
|
+
},
|
|
243
|
+
blue: {
|
|
244
|
+
'50': '#eff6ff',
|
|
245
|
+
'100': '#dbeafe',
|
|
246
|
+
'200': '#bfdbfe',
|
|
247
|
+
'300': '#93c5fd',
|
|
248
|
+
'400': '#60a5fa',
|
|
249
|
+
'500': '#3b82f6',
|
|
250
|
+
'600': '#2563eb',
|
|
251
|
+
'700': '#1d4ed8',
|
|
252
|
+
'800': '#1e40af',
|
|
253
|
+
'900': '#1e3a8a',
|
|
254
|
+
'950': '#172554',
|
|
255
|
+
},
|
|
256
|
+
indigo: {
|
|
257
|
+
'50': '#eef2ff',
|
|
258
|
+
'100': '#e0e7ff',
|
|
259
|
+
'200': '#c7d2fe',
|
|
260
|
+
'300': '#a5b4fc',
|
|
261
|
+
'400': '#818cf8',
|
|
262
|
+
'500': '#6366f1',
|
|
263
|
+
'600': '#4f46e5',
|
|
264
|
+
'700': '#4338ca',
|
|
265
|
+
'800': '#3730a3',
|
|
266
|
+
'900': '#312e81',
|
|
267
|
+
'950': '#1e1b4b',
|
|
268
|
+
},
|
|
269
|
+
violet: {
|
|
270
|
+
'50': '#f5f3ff',
|
|
271
|
+
'100': '#ede9fe',
|
|
272
|
+
'200': '#ddd6fe',
|
|
273
|
+
'300': '#c4b5fd',
|
|
274
|
+
'400': '#a78bfa',
|
|
275
|
+
'500': '#8b5cf6',
|
|
276
|
+
'600': '#7c3aed',
|
|
277
|
+
'700': '#6d28d9',
|
|
278
|
+
'800': '#5b21b6',
|
|
279
|
+
'900': '#4c1d95',
|
|
280
|
+
'950': '#2e1065',
|
|
281
|
+
},
|
|
282
|
+
purple: {
|
|
283
|
+
'50': '#faf5ff',
|
|
284
|
+
'100': '#f3e8ff',
|
|
285
|
+
'200': '#e9d5ff',
|
|
286
|
+
'300': '#d8b4fe',
|
|
287
|
+
'400': '#c084fc',
|
|
288
|
+
'500': '#a855f7',
|
|
289
|
+
'600': '#9333ea',
|
|
290
|
+
'700': '#7e22ce',
|
|
291
|
+
'800': '#6b21a8',
|
|
292
|
+
'900': '#581c87',
|
|
293
|
+
'950': '#3b0764',
|
|
294
|
+
},
|
|
295
|
+
fuchsia: {
|
|
296
|
+
'50': '#fdf4ff',
|
|
297
|
+
'100': '#fae8ff',
|
|
298
|
+
'200': '#f5d0fe',
|
|
299
|
+
'300': '#f0abfc',
|
|
300
|
+
'400': '#e879f9',
|
|
301
|
+
'500': '#d946ef',
|
|
302
|
+
'600': '#c026d3',
|
|
303
|
+
'700': '#a21caf',
|
|
304
|
+
'800': '#86198f',
|
|
305
|
+
'900': '#701a75',
|
|
306
|
+
'950': '#4a044e',
|
|
307
|
+
},
|
|
308
|
+
pink: {
|
|
309
|
+
'50': '#fdf2f8',
|
|
310
|
+
'100': '#fce7f3',
|
|
311
|
+
'200': '#fbcfe8',
|
|
312
|
+
'300': '#f9a8d4',
|
|
313
|
+
'400': '#f472b6',
|
|
314
|
+
'500': '#ec4899',
|
|
315
|
+
'600': '#db2777',
|
|
316
|
+
'700': '#be185d',
|
|
317
|
+
'800': '#9d174d',
|
|
318
|
+
'900': '#831843',
|
|
319
|
+
'950': '#500724',
|
|
320
|
+
},
|
|
321
|
+
rose: {
|
|
322
|
+
'50': '#fff1f2',
|
|
323
|
+
'100': '#ffe4e6',
|
|
324
|
+
'200': '#fecdd3',
|
|
325
|
+
'300': '#fda4af',
|
|
326
|
+
'400': '#fb7185',
|
|
327
|
+
'500': '#f43f5e',
|
|
328
|
+
'600': '#e11d48',
|
|
329
|
+
'700': '#be123c',
|
|
330
|
+
'800': '#9f1239',
|
|
331
|
+
'900': '#881337',
|
|
332
|
+
'950': '#4c0519',
|
|
333
|
+
},
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Special color values.
|
|
338
|
+
*/
|
|
339
|
+
const SPECIAL_COLOR_VALUES: Record<string, string> = {
|
|
340
|
+
transparent: 'transparent',
|
|
341
|
+
current: 'currentColor',
|
|
342
|
+
inherit: 'inherit',
|
|
343
|
+
white: '#ffffff',
|
|
344
|
+
black: '#000000',
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Default Tailwind v4 font weight values.
|
|
349
|
+
*/
|
|
350
|
+
const DEFAULT_FONT_WEIGHTS: TextStyleValue[] = [
|
|
351
|
+
{ class: 'font-thin', label: 'Thin', css: { fontWeight: '100' } },
|
|
352
|
+
{ class: 'font-extralight', label: 'Extra Light', css: { fontWeight: '200' } },
|
|
353
|
+
{ class: 'font-light', label: 'Light', css: { fontWeight: '300' } },
|
|
354
|
+
{ class: 'font-normal', label: 'Normal', css: { fontWeight: '400' } },
|
|
355
|
+
{ class: 'font-medium', label: 'Medium', css: { fontWeight: '500' } },
|
|
356
|
+
{ class: 'font-semibold', label: 'Semibold', css: { fontWeight: '600' } },
|
|
357
|
+
{ class: 'font-bold', label: 'Bold', css: { fontWeight: '700' } },
|
|
358
|
+
{ class: 'font-extrabold', label: 'Extra Bold', css: { fontWeight: '800' } },
|
|
359
|
+
{ class: 'font-black', label: 'Black', css: { fontWeight: '900' } },
|
|
360
|
+
]
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Default Tailwind v4 font size values.
|
|
364
|
+
*/
|
|
365
|
+
const DEFAULT_FONT_SIZES: TextStyleValue[] = [
|
|
366
|
+
{ class: 'text-xs', label: 'XS', css: { fontSize: '0.75rem', lineHeight: '1rem' } },
|
|
367
|
+
{ class: 'text-sm', label: 'SM', css: { fontSize: '0.875rem', lineHeight: '1.25rem' } },
|
|
368
|
+
{ class: 'text-base', label: 'Base', css: { fontSize: '1rem', lineHeight: '1.5rem' } },
|
|
369
|
+
{ class: 'text-lg', label: 'LG', css: { fontSize: '1.125rem', lineHeight: '1.75rem' } },
|
|
370
|
+
{ class: 'text-xl', label: 'XL', css: { fontSize: '1.25rem', lineHeight: '1.75rem' } },
|
|
371
|
+
{ class: 'text-2xl', label: '2XL', css: { fontSize: '1.5rem', lineHeight: '2rem' } },
|
|
372
|
+
{ class: 'text-3xl', label: '3XL', css: { fontSize: '1.875rem', lineHeight: '2.25rem' } },
|
|
373
|
+
{ class: 'text-4xl', label: '4XL', css: { fontSize: '2.25rem', lineHeight: '2.5rem' } },
|
|
374
|
+
{ class: 'text-5xl', label: '5XL', css: { fontSize: '3rem', lineHeight: '1' } },
|
|
375
|
+
{ class: 'text-6xl', label: '6XL', css: { fontSize: '3.75rem', lineHeight: '1' } },
|
|
376
|
+
{ class: 'text-7xl', label: '7XL', css: { fontSize: '4.5rem', lineHeight: '1' } },
|
|
377
|
+
{ class: 'text-8xl', label: '8XL', css: { fontSize: '6rem', lineHeight: '1' } },
|
|
378
|
+
{ class: 'text-9xl', label: '9XL', css: { fontSize: '8rem', lineHeight: '1' } },
|
|
379
|
+
]
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Default text decoration values.
|
|
383
|
+
*/
|
|
384
|
+
const DEFAULT_TEXT_DECORATIONS: TextStyleValue[] = [
|
|
385
|
+
{ class: 'no-underline', label: 'None', css: { textDecoration: 'none' } },
|
|
386
|
+
{ class: 'underline', label: 'Underline', css: { textDecoration: 'underline' } },
|
|
387
|
+
{ class: 'overline', label: 'Overline', css: { textDecoration: 'overline' } },
|
|
388
|
+
{ class: 'line-through', label: 'Strikethrough', css: { textDecoration: 'line-through' } },
|
|
389
|
+
]
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Default font style values.
|
|
393
|
+
*/
|
|
394
|
+
const DEFAULT_FONT_STYLES: TextStyleValue[] = [
|
|
395
|
+
{ class: 'not-italic', label: 'Normal', css: { fontStyle: 'normal' } },
|
|
396
|
+
{ class: 'italic', label: 'Italic', css: { fontStyle: 'italic' } },
|
|
397
|
+
]
|
|
398
|
+
|
|
44
399
|
/**
|
|
45
400
|
* Build a regex pattern for matching color classes.
|
|
46
|
-
* Matches either:
|
|
47
|
-
* - Known default/special colors (e.g., bg-red, text-white)
|
|
48
|
-
* - Any color name followed by a shade number (e.g., bg-primary-500)
|
|
49
401
|
*/
|
|
50
402
|
function buildColorPattern(prefix: string): RegExp {
|
|
51
403
|
const colorNames = [...DEFAULT_TAILWIND_COLORS, ...SPECIAL_COLORS].join('|')
|
|
52
|
-
// Match either: known-color (with optional shade) OR any-name-with-shade (to support custom colors)
|
|
53
404
|
return new RegExp(`^${prefix}-((?:${colorNames})(?:-(\\d+))?|([a-z]+)-(\\d+))$`)
|
|
54
405
|
}
|
|
55
406
|
|
|
56
407
|
/**
|
|
57
408
|
* Regex patterns to match Tailwind color classes.
|
|
58
|
-
* These patterns are specific to color utilities and won't match other utilities
|
|
59
|
-
* like text-lg, text-center, bg-fixed, etc.
|
|
60
409
|
*/
|
|
61
410
|
const COLOR_CLASS_PATTERNS = {
|
|
62
|
-
// Matches: bg-red-500, bg-primary-500, bg-white, bg-transparent
|
|
63
411
|
bg: buildColorPattern('bg'),
|
|
64
|
-
// Matches: text-red-500, text-primary-500, text-white (NOT text-lg, text-center)
|
|
65
412
|
text: buildColorPattern('text'),
|
|
66
|
-
// Matches: border-red-500, border-primary-500
|
|
67
413
|
border: buildColorPattern('border'),
|
|
68
|
-
// Matches: hover:bg-red-500
|
|
69
414
|
hoverBg: buildColorPattern('hover:bg'),
|
|
70
|
-
// Matches: hover:text-red-500
|
|
71
415
|
hoverText: buildColorPattern('hover:text'),
|
|
72
416
|
}
|
|
73
417
|
|
|
74
418
|
/**
|
|
75
|
-
* Parse Tailwind v4 CSS config to extract available colors.
|
|
76
|
-
* Tailwind v4 uses CSS-based configuration with @theme directive.
|
|
77
|
-
*
|
|
78
|
-
* Example CSS:
|
|
79
|
-
* ```css
|
|
80
|
-
* @theme {
|
|
81
|
-
* --color-primary-50: #eff6ff;
|
|
82
|
-
* --color-primary-500: #3b82f6;
|
|
83
|
-
* --color-accent: #f59e0b;
|
|
84
|
-
* }
|
|
85
|
-
* ```
|
|
419
|
+
* Parse Tailwind v4 CSS config to extract available colors with their values.
|
|
86
420
|
*/
|
|
87
|
-
export async function parseTailwindConfig(projectRoot: string =
|
|
421
|
+
export async function parseTailwindConfig(projectRoot: string = getProjectRoot()): Promise<AvailableColors> {
|
|
88
422
|
// Tailwind v4 CSS files to search
|
|
89
423
|
const cssFiles = [
|
|
90
424
|
'src/styles/global.css',
|
|
@@ -112,17 +446,17 @@ export async function parseTailwindConfig(projectRoot: string = process.cwd()):
|
|
|
112
446
|
}
|
|
113
447
|
}
|
|
114
448
|
|
|
115
|
-
// Build default colors
|
|
449
|
+
// Build default colors with values
|
|
116
450
|
const defaultColors: TailwindColor[] = DEFAULT_TAILWIND_COLORS.map(name => ({
|
|
117
451
|
name,
|
|
118
|
-
|
|
452
|
+
values: DEFAULT_COLOR_VALUES[name] || {},
|
|
119
453
|
isCustom: false,
|
|
120
454
|
}))
|
|
121
455
|
|
|
122
|
-
// Add special colors
|
|
456
|
+
// Add special colors
|
|
123
457
|
const specialColors: TailwindColor[] = SPECIAL_COLORS.map(name => ({
|
|
124
458
|
name,
|
|
125
|
-
|
|
459
|
+
values: { '': SPECIAL_COLOR_VALUES[name] || name },
|
|
126
460
|
isCustom: false,
|
|
127
461
|
}))
|
|
128
462
|
|
|
@@ -135,95 +469,192 @@ export async function parseTailwindConfig(projectRoot: string = process.cwd()):
|
|
|
135
469
|
|
|
136
470
|
/**
|
|
137
471
|
* Extract custom colors from Tailwind v4 CSS @theme block.
|
|
138
|
-
*
|
|
139
|
-
* Looks for patterns like:
|
|
140
|
-
* - --color-primary-50: #value;
|
|
141
|
-
* - --color-primary: #value;
|
|
142
|
-
* - --color-accent-500: oklch(...);
|
|
472
|
+
* Extracts both color names and their actual CSS values.
|
|
143
473
|
*/
|
|
144
474
|
function extractColorsFromCss(content: string): TailwindColor[] {
|
|
145
|
-
const colors = new Map<string,
|
|
475
|
+
const colors = new Map<string, Record<string, string>>()
|
|
146
476
|
|
|
147
|
-
// Find @theme blocks
|
|
148
|
-
const themeBlockPattern = /@theme
|
|
477
|
+
// Find @theme blocks (including inline)
|
|
478
|
+
const themeBlockPattern = /@theme(?:\s+inline)?\s*\{([^}]+)\}/gs
|
|
149
479
|
let themeMatch: RegExpExecArray | null
|
|
150
480
|
|
|
151
481
|
while ((themeMatch = themeBlockPattern.exec(content)) !== null) {
|
|
152
482
|
const themeContent = themeMatch[1]
|
|
153
483
|
if (!themeContent) continue
|
|
154
484
|
|
|
155
|
-
// Find all --color-* definitions
|
|
485
|
+
// Find all --color-* definitions with their values
|
|
156
486
|
// Pattern: --color-{name}-{shade}: value; or --color-{name}: value;
|
|
157
|
-
const colorVarPattern = /--color-([a-z]+)(?:-(\d+))
|
|
487
|
+
const colorVarPattern = /--color-([a-z]+)(?:-(\d+))?:\s*([^;]+);/gi
|
|
158
488
|
let colorMatch: RegExpExecArray | null
|
|
159
489
|
|
|
160
490
|
while ((colorMatch = colorVarPattern.exec(themeContent)) !== null) {
|
|
161
491
|
const colorName = colorMatch[1]?.toLowerCase()
|
|
162
|
-
const shade = colorMatch[2]
|
|
492
|
+
const shade = colorMatch[2] || ''
|
|
493
|
+
const value = colorMatch[3]?.trim()
|
|
163
494
|
|
|
164
|
-
if (!colorName) continue
|
|
495
|
+
if (!colorName || !value) continue
|
|
165
496
|
|
|
166
|
-
// Skip if it's a default color
|
|
497
|
+
// Skip if it's a default color (we already have values for those)
|
|
167
498
|
if (DEFAULT_TAILWIND_COLORS.includes(colorName as any)) {
|
|
168
499
|
continue
|
|
169
500
|
}
|
|
170
501
|
|
|
171
502
|
if (!colors.has(colorName)) {
|
|
172
|
-
colors.set(colorName,
|
|
503
|
+
colors.set(colorName, {})
|
|
173
504
|
}
|
|
174
505
|
|
|
175
|
-
|
|
176
|
-
colors.get(colorName)!.add(shade)
|
|
177
|
-
}
|
|
506
|
+
colors.get(colorName)![shade] = value
|
|
178
507
|
}
|
|
179
508
|
}
|
|
180
509
|
|
|
181
|
-
//
|
|
182
|
-
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
510
|
+
// Convert to TailwindColor array
|
|
511
|
+
const result: TailwindColor[] = []
|
|
512
|
+
for (const [name, values] of colors) {
|
|
513
|
+
result.push({
|
|
514
|
+
name,
|
|
515
|
+
values,
|
|
516
|
+
isCustom: true,
|
|
517
|
+
})
|
|
518
|
+
}
|
|
189
519
|
|
|
190
|
-
|
|
191
|
-
|
|
520
|
+
return result
|
|
521
|
+
}
|
|
192
522
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
523
|
+
/**
|
|
524
|
+
* Parse Tailwind v4 CSS config to extract available text styles.
|
|
525
|
+
*/
|
|
526
|
+
export async function parseTextStyles(projectRoot: string = getProjectRoot()): Promise<AvailableTextStyles> {
|
|
527
|
+
// Tailwind v4 CSS files to search
|
|
528
|
+
const cssFiles = [
|
|
529
|
+
'src/styles/global.css',
|
|
530
|
+
'src/styles/tailwind.css',
|
|
531
|
+
'src/styles/app.css',
|
|
532
|
+
'src/app.css',
|
|
533
|
+
'src/global.css',
|
|
534
|
+
'src/index.css',
|
|
535
|
+
'app/globals.css',
|
|
536
|
+
'styles/globals.css',
|
|
537
|
+
]
|
|
196
538
|
|
|
197
|
-
|
|
539
|
+
let customTextStyles: Partial<AvailableTextStyles> = {}
|
|
198
540
|
|
|
199
|
-
|
|
200
|
-
|
|
541
|
+
for (const cssFile of cssFiles) {
|
|
542
|
+
const fullPath = path.join(projectRoot, cssFile)
|
|
543
|
+
try {
|
|
544
|
+
const content = await fs.readFile(fullPath, 'utf-8')
|
|
545
|
+
customTextStyles = extractTextStylesFromCss(content)
|
|
546
|
+
// If we found any custom styles, use this file
|
|
547
|
+
if (Object.values(customTextStyles).some(arr => arr && arr.length > 0)) {
|
|
548
|
+
break
|
|
201
549
|
}
|
|
550
|
+
} catch {
|
|
551
|
+
// File doesn't exist, continue
|
|
552
|
+
}
|
|
553
|
+
}
|
|
202
554
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
555
|
+
// Merge custom styles with defaults (custom overrides default)
|
|
556
|
+
return {
|
|
557
|
+
fontWeight: mergeTextStyles(DEFAULT_FONT_WEIGHTS, customTextStyles.fontWeight),
|
|
558
|
+
fontSize: mergeTextStyles(DEFAULT_FONT_SIZES, customTextStyles.fontSize),
|
|
559
|
+
textDecoration: mergeTextStyles(DEFAULT_TEXT_DECORATIONS, customTextStyles.textDecoration),
|
|
560
|
+
fontStyle: mergeTextStyles(DEFAULT_FONT_STYLES, customTextStyles.fontStyle),
|
|
561
|
+
}
|
|
562
|
+
}
|
|
206
563
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
564
|
+
/**
|
|
565
|
+
* Merge custom text styles with defaults.
|
|
566
|
+
* Custom styles with matching class names override defaults.
|
|
567
|
+
*/
|
|
568
|
+
function mergeTextStyles(defaults: TextStyleValue[], custom?: TextStyleValue[]): TextStyleValue[] {
|
|
569
|
+
if (!custom || custom.length === 0) {
|
|
570
|
+
return defaults
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const customByClass = new Map(custom.map(s => [s.class, s]))
|
|
574
|
+
const result: TextStyleValue[] = []
|
|
575
|
+
|
|
576
|
+
// Update defaults with custom overrides
|
|
577
|
+
for (const def of defaults) {
|
|
578
|
+
const customStyle = customByClass.get(def.class)
|
|
579
|
+
if (customStyle) {
|
|
580
|
+
result.push(customStyle)
|
|
581
|
+
customByClass.delete(def.class)
|
|
582
|
+
} else {
|
|
583
|
+
result.push(def)
|
|
210
584
|
}
|
|
211
585
|
}
|
|
212
586
|
|
|
213
|
-
//
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
const sortedShades = Array.from(shades).sort((a, b) => parseInt(a) - parseInt(b))
|
|
217
|
-
result.push({
|
|
218
|
-
name,
|
|
219
|
-
shades: sortedShades.length > 0 ? sortedShades : [],
|
|
220
|
-
isCustom: true,
|
|
221
|
-
})
|
|
587
|
+
// Add any remaining custom styles that weren't overrides
|
|
588
|
+
for (const style of customByClass.values()) {
|
|
589
|
+
result.push(style)
|
|
222
590
|
}
|
|
223
591
|
|
|
224
592
|
return result
|
|
225
593
|
}
|
|
226
594
|
|
|
595
|
+
/**
|
|
596
|
+
* Extract custom text styles from Tailwind v4 CSS @theme block.
|
|
597
|
+
*/
|
|
598
|
+
function extractTextStylesFromCss(content: string): Partial<AvailableTextStyles> {
|
|
599
|
+
const fontWeights: TextStyleValue[] = []
|
|
600
|
+
const fontSizes: TextStyleValue[] = []
|
|
601
|
+
|
|
602
|
+
// Find @theme blocks (including inline)
|
|
603
|
+
const themeBlockPattern = /@theme(?:\s+inline)?\s*\{([^}]+)\}/gs
|
|
604
|
+
let themeMatch: RegExpExecArray | null
|
|
605
|
+
|
|
606
|
+
while ((themeMatch = themeBlockPattern.exec(content)) !== null) {
|
|
607
|
+
const themeContent = themeMatch[1]
|
|
608
|
+
if (!themeContent) continue
|
|
609
|
+
|
|
610
|
+
// Extract font-weight overrides: --font-weight-{name}: value;
|
|
611
|
+
const fontWeightPattern = /--font-weight-([a-z]+):\s*([^;]+);/gi
|
|
612
|
+
let weightMatch: RegExpExecArray | null
|
|
613
|
+
while ((weightMatch = fontWeightPattern.exec(themeContent)) !== null) {
|
|
614
|
+
const name = weightMatch[1]?.toLowerCase()
|
|
615
|
+
const value = weightMatch[2]?.trim()
|
|
616
|
+
if (!name || !value) continue
|
|
617
|
+
|
|
618
|
+
fontWeights.push({
|
|
619
|
+
class: `font-${name}`,
|
|
620
|
+
label: name.charAt(0).toUpperCase() + name.slice(1),
|
|
621
|
+
css: { fontWeight: value },
|
|
622
|
+
})
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// Extract font-size overrides: --font-size-{name}: value;
|
|
626
|
+
// Also look for corresponding line-height: --line-height-{name}: value;
|
|
627
|
+
const fontSizePattern = /--font-size-([a-z0-9]+):\s*([^;]+);/gi
|
|
628
|
+
let sizeMatch: RegExpExecArray | null
|
|
629
|
+
while ((sizeMatch = fontSizePattern.exec(themeContent)) !== null) {
|
|
630
|
+
const name = sizeMatch[1]?.toLowerCase()
|
|
631
|
+
const value = sizeMatch[2]?.trim()
|
|
632
|
+
if (!name || !value) continue
|
|
633
|
+
|
|
634
|
+
// Try to find matching line-height
|
|
635
|
+
const lineHeightPattern = new RegExp(`--line-height-${name}:\\s*([^;]+);`, 'i')
|
|
636
|
+
const lineHeightMatch = themeContent.match(lineHeightPattern)
|
|
637
|
+
const lineHeight = lineHeightMatch?.[1]?.trim()
|
|
638
|
+
|
|
639
|
+
const css: Record<string, string> = { fontSize: value }
|
|
640
|
+
if (lineHeight) {
|
|
641
|
+
css.lineHeight = lineHeight
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
fontSizes.push({
|
|
645
|
+
class: `text-${name}`,
|
|
646
|
+
label: name.toUpperCase(),
|
|
647
|
+
css,
|
|
648
|
+
})
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
return {
|
|
653
|
+
fontWeight: fontWeights.length > 0 ? fontWeights : undefined,
|
|
654
|
+
fontSize: fontSizes.length > 0 ? fontSizes : undefined,
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
227
658
|
/**
|
|
228
659
|
* Extract color classes from an element's class attribute.
|
|
229
660
|
*/
|
|
@@ -235,14 +666,13 @@ export function extractColorClasses(classAttr: string | null | undefined): Color
|
|
|
235
666
|
const allColorClasses: string[] = []
|
|
236
667
|
|
|
237
668
|
for (const cls of classes) {
|
|
238
|
-
// Check each pattern
|
|
239
669
|
for (const [key, pattern] of Object.entries(COLOR_CLASS_PATTERNS)) {
|
|
240
670
|
const match = cls.match(pattern)
|
|
241
671
|
if (match) {
|
|
242
672
|
allColorClasses.push(cls)
|
|
243
|
-
|
|
244
|
-
if (!(
|
|
245
|
-
|
|
673
|
+
const colorKey = key as keyof Omit<ColorClasses, 'allColorClasses'>
|
|
674
|
+
if (!(colorKey in colorClasses)) {
|
|
675
|
+
colorClasses[colorKey] = cls
|
|
246
676
|
}
|
|
247
677
|
break
|
|
248
678
|
}
|
|
@@ -266,10 +696,6 @@ export function isColorClass(className: string): boolean {
|
|
|
266
696
|
|
|
267
697
|
/**
|
|
268
698
|
* Generate a new class string with a color class replaced.
|
|
269
|
-
* @param currentClasses - Current class attribute value
|
|
270
|
-
* @param oldColorClass - The color class to replace (e.g., 'bg-blue-500')
|
|
271
|
-
* @param newColorClass - The new color class (e.g., 'bg-red-500')
|
|
272
|
-
* @returns New class string with the replacement
|
|
273
699
|
*/
|
|
274
700
|
export function replaceColorClass(
|
|
275
701
|
currentClasses: string,
|
|
@@ -283,8 +709,6 @@ export function replaceColorClass(
|
|
|
283
709
|
|
|
284
710
|
/**
|
|
285
711
|
* Get the color type from a color class.
|
|
286
|
-
* @param colorClass - e.g., 'bg-blue-500', 'text-white', 'hover:bg-red-600'
|
|
287
|
-
* @returns The type: 'bg', 'text', 'border', 'hoverBg', 'hoverText', or undefined
|
|
288
712
|
*/
|
|
289
713
|
export function getColorType(colorClass: string): keyof ColorClasses | undefined {
|
|
290
714
|
for (const [key, pattern] of Object.entries(COLOR_CLASS_PATTERNS)) {
|
|
@@ -297,8 +721,6 @@ export function getColorType(colorClass: string): keyof ColorClasses | undefined
|
|
|
297
721
|
|
|
298
722
|
/**
|
|
299
723
|
* Parse a color class into its components.
|
|
300
|
-
* @param colorClass - e.g., 'bg-blue-500', 'text-white', 'hover:bg-red-600'
|
|
301
|
-
* @returns Object with prefix, colorName, and shade (if any)
|
|
302
724
|
*/
|
|
303
725
|
export function parseColorClass(colorClass: string): {
|
|
304
726
|
prefix: string
|
|
@@ -306,11 +728,9 @@ export function parseColorClass(colorClass: string): {
|
|
|
306
728
|
shade?: string
|
|
307
729
|
isHover: boolean
|
|
308
730
|
} | undefined {
|
|
309
|
-
// Handle hover prefix
|
|
310
731
|
const isHover = colorClass.startsWith('hover:')
|
|
311
732
|
const classWithoutHover = isHover ? colorClass.slice(6) : colorClass
|
|
312
733
|
|
|
313
|
-
// Match prefix-color-shade or prefix-color
|
|
314
734
|
const match = classWithoutHover.match(/^(bg|text|border)-([a-z]+)(?:-(\d+))?$/)
|
|
315
735
|
|
|
316
736
|
if (!match) return undefined
|
|
@@ -336,33 +756,3 @@ export function buildColorClass(
|
|
|
336
756
|
}
|
|
337
757
|
return `${prefix}-${colorName}`
|
|
338
758
|
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Generate a hidden HTML string containing all color classes for Tailwind safelist.
|
|
342
|
-
* This ensures Tailwind includes all color utility classes during build,
|
|
343
|
-
* even if they're only used dynamically via the CMS color editor.
|
|
344
|
-
*
|
|
345
|
-
* @param availableColors - The available colors from Tailwind config
|
|
346
|
-
* @returns HTML string with a hidden div containing all color classes
|
|
347
|
-
*/
|
|
348
|
-
export function generateColorSafelistHtml(availableColors: AvailableColors): string {
|
|
349
|
-
const classes: string[] = []
|
|
350
|
-
|
|
351
|
-
for (const color of availableColors.colors) {
|
|
352
|
-
if (color.shades.length === 0) {
|
|
353
|
-
// Special colors without shades (white, black, transparent, etc.)
|
|
354
|
-
if (color.name !== 'transparent' && color.name !== 'current' && color.name !== 'inherit') {
|
|
355
|
-
classes.push(`bg-${color.name}`)
|
|
356
|
-
classes.push(`text-${color.name}`)
|
|
357
|
-
}
|
|
358
|
-
} else {
|
|
359
|
-
// Colors with shades
|
|
360
|
-
for (const shade of color.shades) {
|
|
361
|
-
classes.push(`bg-${color.name}-${shade}`)
|
|
362
|
-
classes.push(`text-${color.name}-${shade}`)
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
return `<div aria-hidden="true" class="!hidden ${classes.join(' ')}"></div>`
|
|
368
|
-
}
|