tunewind 0.0.0-stage → 0.1.0
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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +112 -2
- package/dist/index.cjs +1638 -0
- package/dist/index.d.cts +123 -0
- package/dist/index.d.mts +123 -0
- package/dist/index.mjs +1614 -0
- package/package.json +84 -3
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1614 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
import { createJiti } from "jiti";
|
|
7
|
+
import { parse } from "postcss";
|
|
8
|
+
// src/const/diagnostics.ts
|
|
9
|
+
/** diagnostic levels of the dropped nodes */
|
|
10
|
+
const DIAGNOSTIC_LEVELS = [
|
|
11
|
+
"off",
|
|
12
|
+
"warn",
|
|
13
|
+
"error"
|
|
14
|
+
];
|
|
15
|
+
// src/const/strict.ts
|
|
16
|
+
/** strict mode names */
|
|
17
|
+
const STRICT_MODES = ["bypass"];
|
|
18
|
+
// src/const/tailwind.ts
|
|
19
|
+
/** color family names of the tailwind default palette */
|
|
20
|
+
const COLOR_FAMILIES = [
|
|
21
|
+
"amber",
|
|
22
|
+
"blue",
|
|
23
|
+
"cyan",
|
|
24
|
+
"emerald",
|
|
25
|
+
"fuchsia",
|
|
26
|
+
"gray",
|
|
27
|
+
"green",
|
|
28
|
+
"indigo",
|
|
29
|
+
"lime",
|
|
30
|
+
"mauve",
|
|
31
|
+
"mist",
|
|
32
|
+
"neutral",
|
|
33
|
+
"olive",
|
|
34
|
+
"orange",
|
|
35
|
+
"pink",
|
|
36
|
+
"purple",
|
|
37
|
+
"red",
|
|
38
|
+
"rose",
|
|
39
|
+
"sky",
|
|
40
|
+
"slate",
|
|
41
|
+
"stone",
|
|
42
|
+
"taupe",
|
|
43
|
+
"teal",
|
|
44
|
+
"violet",
|
|
45
|
+
"yellow",
|
|
46
|
+
"zinc"
|
|
47
|
+
];
|
|
48
|
+
/** shade numbers of the tailwind default palette */
|
|
49
|
+
const COLOR_SHADES = [
|
|
50
|
+
50,
|
|
51
|
+
100,
|
|
52
|
+
200,
|
|
53
|
+
300,
|
|
54
|
+
400,
|
|
55
|
+
500,
|
|
56
|
+
600,
|
|
57
|
+
700,
|
|
58
|
+
800,
|
|
59
|
+
900,
|
|
60
|
+
950
|
|
61
|
+
];
|
|
62
|
+
/** flat colors */
|
|
63
|
+
const FLAT_COLORS = {
|
|
64
|
+
white: "#fff",
|
|
65
|
+
black: "#000"
|
|
66
|
+
};
|
|
67
|
+
/** built-in breakpoint widths */
|
|
68
|
+
const BREAKPOINTS = {
|
|
69
|
+
"sm": "640px",
|
|
70
|
+
"md": "768px",
|
|
71
|
+
"lg": "1024px",
|
|
72
|
+
"xl": "1280px",
|
|
73
|
+
"2xl": "1536px"
|
|
74
|
+
};
|
|
75
|
+
/** tailwind default palette */
|
|
76
|
+
const PALETTE = {
|
|
77
|
+
amber: {
|
|
78
|
+
50: "oklch(98.7% 0.022 95.277)",
|
|
79
|
+
100: "oklch(96.2% 0.059 95.617)",
|
|
80
|
+
200: "oklch(92.4% 0.12 95.746)",
|
|
81
|
+
300: "oklch(87.9% 0.169 91.605)",
|
|
82
|
+
400: "oklch(82.8% 0.189 84.429)",
|
|
83
|
+
500: "oklch(76.9% 0.188 70.08)",
|
|
84
|
+
600: "oklch(66.6% 0.179 58.318)",
|
|
85
|
+
700: "oklch(55.5% 0.163 48.998)",
|
|
86
|
+
800: "oklch(47.3% 0.137 46.201)",
|
|
87
|
+
900: "oklch(41.4% 0.112 45.904)",
|
|
88
|
+
950: "oklch(27.9% 0.077 45.635)"
|
|
89
|
+
},
|
|
90
|
+
blue: {
|
|
91
|
+
50: "oklch(97% 0.014 254.604)",
|
|
92
|
+
100: "oklch(93.2% 0.032 255.585)",
|
|
93
|
+
200: "oklch(88.2% 0.059 254.128)",
|
|
94
|
+
300: "oklch(80.9% 0.105 251.813)",
|
|
95
|
+
400: "oklch(70.7% 0.165 254.624)",
|
|
96
|
+
500: "oklch(62.3% 0.214 259.815)",
|
|
97
|
+
600: "oklch(54.6% 0.245 262.881)",
|
|
98
|
+
700: "oklch(48.8% 0.243 264.376)",
|
|
99
|
+
800: "oklch(42.4% 0.199 265.638)",
|
|
100
|
+
900: "oklch(37.9% 0.146 265.522)",
|
|
101
|
+
950: "oklch(28.2% 0.091 267.935)"
|
|
102
|
+
},
|
|
103
|
+
cyan: {
|
|
104
|
+
50: "oklch(98.4% 0.019 200.873)",
|
|
105
|
+
100: "oklch(95.6% 0.045 203.388)",
|
|
106
|
+
200: "oklch(91.7% 0.08 205.041)",
|
|
107
|
+
300: "oklch(86.5% 0.127 207.078)",
|
|
108
|
+
400: "oklch(78.9% 0.154 211.53)",
|
|
109
|
+
500: "oklch(71.5% 0.143 215.221)",
|
|
110
|
+
600: "oklch(60.9% 0.126 221.723)",
|
|
111
|
+
700: "oklch(52% 0.105 223.128)",
|
|
112
|
+
800: "oklch(45% 0.085 224.283)",
|
|
113
|
+
900: "oklch(39.8% 0.07 227.392)",
|
|
114
|
+
950: "oklch(30.2% 0.056 229.695)"
|
|
115
|
+
},
|
|
116
|
+
emerald: {
|
|
117
|
+
50: "oklch(97.9% 0.021 166.113)",
|
|
118
|
+
100: "oklch(95% 0.052 163.051)",
|
|
119
|
+
200: "oklch(90.5% 0.093 164.15)",
|
|
120
|
+
300: "oklch(84.5% 0.143 164.978)",
|
|
121
|
+
400: "oklch(76.5% 0.177 163.223)",
|
|
122
|
+
500: "oklch(69.6% 0.17 162.48)",
|
|
123
|
+
600: "oklch(59.6% 0.145 163.225)",
|
|
124
|
+
700: "oklch(50.8% 0.118 165.612)",
|
|
125
|
+
800: "oklch(43.2% 0.095 166.913)",
|
|
126
|
+
900: "oklch(37.8% 0.077 168.94)",
|
|
127
|
+
950: "oklch(26.2% 0.051 172.552)"
|
|
128
|
+
},
|
|
129
|
+
fuchsia: {
|
|
130
|
+
50: "oklch(97.7% 0.017 320.058)",
|
|
131
|
+
100: "oklch(95.2% 0.037 318.852)",
|
|
132
|
+
200: "oklch(90.3% 0.076 319.62)",
|
|
133
|
+
300: "oklch(83.3% 0.145 321.434)",
|
|
134
|
+
400: "oklch(74% 0.238 322.16)",
|
|
135
|
+
500: "oklch(66.7% 0.295 322.15)",
|
|
136
|
+
600: "oklch(59.1% 0.293 322.896)",
|
|
137
|
+
700: "oklch(51.8% 0.253 323.949)",
|
|
138
|
+
800: "oklch(45.2% 0.211 324.591)",
|
|
139
|
+
900: "oklch(40.1% 0.17 325.612)",
|
|
140
|
+
950: "oklch(29.3% 0.136 325.661)"
|
|
141
|
+
},
|
|
142
|
+
gray: {
|
|
143
|
+
50: "oklch(98.5% 0.002 247.839)",
|
|
144
|
+
100: "oklch(96.7% 0.003 264.542)",
|
|
145
|
+
200: "oklch(92.8% 0.006 264.531)",
|
|
146
|
+
300: "oklch(87.2% 0.01 258.338)",
|
|
147
|
+
400: "oklch(70.7% 0.022 261.325)",
|
|
148
|
+
500: "oklch(55.1% 0.027 264.364)",
|
|
149
|
+
600: "oklch(44.6% 0.03 256.802)",
|
|
150
|
+
700: "oklch(37.3% 0.034 259.733)",
|
|
151
|
+
800: "oklch(27.8% 0.033 256.848)",
|
|
152
|
+
900: "oklch(21% 0.034 264.665)",
|
|
153
|
+
950: "oklch(13% 0.028 261.692)"
|
|
154
|
+
},
|
|
155
|
+
green: {
|
|
156
|
+
50: "oklch(98.2% 0.018 155.826)",
|
|
157
|
+
100: "oklch(96.2% 0.044 156.743)",
|
|
158
|
+
200: "oklch(92.5% 0.084 155.995)",
|
|
159
|
+
300: "oklch(87.1% 0.15 154.449)",
|
|
160
|
+
400: "oklch(79.2% 0.209 151.711)",
|
|
161
|
+
500: "oklch(72.3% 0.219 149.579)",
|
|
162
|
+
600: "oklch(62.7% 0.194 149.214)",
|
|
163
|
+
700: "oklch(52.7% 0.154 150.069)",
|
|
164
|
+
800: "oklch(44.8% 0.119 151.328)",
|
|
165
|
+
900: "oklch(39.3% 0.095 152.535)",
|
|
166
|
+
950: "oklch(26.6% 0.065 152.934)"
|
|
167
|
+
},
|
|
168
|
+
indigo: {
|
|
169
|
+
50: "oklch(96.2% 0.018 272.314)",
|
|
170
|
+
100: "oklch(93% 0.034 272.788)",
|
|
171
|
+
200: "oklch(87% 0.065 274.039)",
|
|
172
|
+
300: "oklch(78.5% 0.115 274.713)",
|
|
173
|
+
400: "oklch(67.3% 0.182 276.935)",
|
|
174
|
+
500: "oklch(58.5% 0.233 277.117)",
|
|
175
|
+
600: "oklch(51.1% 0.262 276.966)",
|
|
176
|
+
700: "oklch(45.7% 0.24 277.023)",
|
|
177
|
+
800: "oklch(39.8% 0.195 277.366)",
|
|
178
|
+
900: "oklch(35.9% 0.144 278.697)",
|
|
179
|
+
950: "oklch(25.7% 0.09 281.288)"
|
|
180
|
+
},
|
|
181
|
+
lime: {
|
|
182
|
+
50: "oklch(98.6% 0.031 120.757)",
|
|
183
|
+
100: "oklch(96.7% 0.067 122.328)",
|
|
184
|
+
200: "oklch(93.8% 0.127 124.321)",
|
|
185
|
+
300: "oklch(89.7% 0.196 126.665)",
|
|
186
|
+
400: "oklch(84.1% 0.238 128.85)",
|
|
187
|
+
500: "oklch(76.8% 0.233 130.85)",
|
|
188
|
+
600: "oklch(64.8% 0.2 131.684)",
|
|
189
|
+
700: "oklch(53.2% 0.157 131.589)",
|
|
190
|
+
800: "oklch(45.3% 0.124 130.933)",
|
|
191
|
+
900: "oklch(40.5% 0.101 131.063)",
|
|
192
|
+
950: "oklch(27.4% 0.072 132.109)"
|
|
193
|
+
},
|
|
194
|
+
mauve: {
|
|
195
|
+
50: "oklch(98.5% 0 none)",
|
|
196
|
+
100: "oklch(96% 0.003 325.6)",
|
|
197
|
+
200: "oklch(92.2% 0.005 325.62)",
|
|
198
|
+
300: "oklch(86.5% 0.012 325.68)",
|
|
199
|
+
400: "oklch(71.1% 0.019 323.02)",
|
|
200
|
+
500: "oklch(54.2% 0.034 322.5)",
|
|
201
|
+
600: "oklch(43.5% 0.029 321.78)",
|
|
202
|
+
700: "oklch(36.4% 0.029 323.89)",
|
|
203
|
+
800: "oklch(26.3% 0.024 320.12)",
|
|
204
|
+
900: "oklch(21.2% 0.019 322.12)",
|
|
205
|
+
950: "oklch(14.5% 0.008 326)"
|
|
206
|
+
},
|
|
207
|
+
mist: {
|
|
208
|
+
50: "oklch(98.7% 0.002 197.1)",
|
|
209
|
+
100: "oklch(96.3% 0.002 197.1)",
|
|
210
|
+
200: "oklch(92.5% 0.005 214.3)",
|
|
211
|
+
300: "oklch(87.2% 0.007 219.6)",
|
|
212
|
+
400: "oklch(72.3% 0.014 214.4)",
|
|
213
|
+
500: "oklch(56% 0.021 213.5)",
|
|
214
|
+
600: "oklch(45% 0.017 213.2)",
|
|
215
|
+
700: "oklch(37.8% 0.015 216)",
|
|
216
|
+
800: "oklch(27.5% 0.011 216.9)",
|
|
217
|
+
900: "oklch(21.8% 0.008 223.9)",
|
|
218
|
+
950: "oklch(14.8% 0.004 228.8)"
|
|
219
|
+
},
|
|
220
|
+
neutral: {
|
|
221
|
+
50: "oklch(98.5% 0 none)",
|
|
222
|
+
100: "oklch(97% 0 none)",
|
|
223
|
+
200: "oklch(92.2% 0 none)",
|
|
224
|
+
300: "oklch(87% 0 none)",
|
|
225
|
+
400: "oklch(70.8% 0 none)",
|
|
226
|
+
500: "oklch(55.6% 0 none)",
|
|
227
|
+
600: "oklch(43.9% 0 none)",
|
|
228
|
+
700: "oklch(37.1% 0 none)",
|
|
229
|
+
800: "oklch(26.9% 0 none)",
|
|
230
|
+
900: "oklch(20.5% 0 none)",
|
|
231
|
+
950: "oklch(14.5% 0 none)"
|
|
232
|
+
},
|
|
233
|
+
olive: {
|
|
234
|
+
50: "oklch(98.8% 0.003 106.5)",
|
|
235
|
+
100: "oklch(96.6% 0.005 106.5)",
|
|
236
|
+
200: "oklch(93% 0.007 106.5)",
|
|
237
|
+
300: "oklch(88% 0.011 106.6)",
|
|
238
|
+
400: "oklch(73.7% 0.021 106.9)",
|
|
239
|
+
500: "oklch(58% 0.031 107.3)",
|
|
240
|
+
600: "oklch(46.6% 0.025 107.3)",
|
|
241
|
+
700: "oklch(39.4% 0.023 107.4)",
|
|
242
|
+
800: "oklch(28.6% 0.016 107.4)",
|
|
243
|
+
900: "oklch(22.8% 0.013 107.4)",
|
|
244
|
+
950: "oklch(15.3% 0.006 107.1)"
|
|
245
|
+
},
|
|
246
|
+
orange: {
|
|
247
|
+
50: "oklch(98% 0.016 73.684)",
|
|
248
|
+
100: "oklch(95.4% 0.038 75.164)",
|
|
249
|
+
200: "oklch(90.1% 0.076 70.697)",
|
|
250
|
+
300: "oklch(83.7% 0.128 66.29)",
|
|
251
|
+
400: "oklch(75% 0.183 55.934)",
|
|
252
|
+
500: "oklch(70.5% 0.213 47.604)",
|
|
253
|
+
600: "oklch(64.6% 0.222 41.116)",
|
|
254
|
+
700: "oklch(55.3% 0.195 38.402)",
|
|
255
|
+
800: "oklch(47% 0.157 37.304)",
|
|
256
|
+
900: "oklch(40.8% 0.123 38.172)",
|
|
257
|
+
950: "oklch(26.6% 0.079 36.259)"
|
|
258
|
+
},
|
|
259
|
+
pink: {
|
|
260
|
+
50: "oklch(97.1% 0.014 343.198)",
|
|
261
|
+
100: "oklch(94.8% 0.028 342.258)",
|
|
262
|
+
200: "oklch(89.9% 0.061 343.231)",
|
|
263
|
+
300: "oklch(82.3% 0.12 346.018)",
|
|
264
|
+
400: "oklch(71.8% 0.202 349.761)",
|
|
265
|
+
500: "oklch(65.6% 0.241 354.308)",
|
|
266
|
+
600: "oklch(59.2% 0.249 0.584)",
|
|
267
|
+
700: "oklch(52.5% 0.223 3.958)",
|
|
268
|
+
800: "oklch(45.9% 0.187 3.815)",
|
|
269
|
+
900: "oklch(40.8% 0.153 2.432)",
|
|
270
|
+
950: "oklch(28.4% 0.109 3.907)"
|
|
271
|
+
},
|
|
272
|
+
purple: {
|
|
273
|
+
50: "oklch(97.7% 0.014 308.299)",
|
|
274
|
+
100: "oklch(94.6% 0.033 307.174)",
|
|
275
|
+
200: "oklch(90.2% 0.063 306.703)",
|
|
276
|
+
300: "oklch(82.7% 0.119 306.383)",
|
|
277
|
+
400: "oklch(71.4% 0.203 305.504)",
|
|
278
|
+
500: "oklch(62.7% 0.265 303.9)",
|
|
279
|
+
600: "oklch(55.8% 0.288 302.321)",
|
|
280
|
+
700: "oklch(49.6% 0.265 301.924)",
|
|
281
|
+
800: "oklch(43.8% 0.218 303.724)",
|
|
282
|
+
900: "oklch(38.1% 0.176 304.987)",
|
|
283
|
+
950: "oklch(29.1% 0.149 302.717)"
|
|
284
|
+
},
|
|
285
|
+
red: {
|
|
286
|
+
50: "oklch(97.1% 0.013 17.38)",
|
|
287
|
+
100: "oklch(93.6% 0.032 17.717)",
|
|
288
|
+
200: "oklch(88.5% 0.062 18.334)",
|
|
289
|
+
300: "oklch(80.8% 0.114 19.571)",
|
|
290
|
+
400: "oklch(70.4% 0.191 22.216)",
|
|
291
|
+
500: "oklch(63.7% 0.237 25.331)",
|
|
292
|
+
600: "oklch(57.7% 0.245 27.325)",
|
|
293
|
+
700: "oklch(50.5% 0.213 27.518)",
|
|
294
|
+
800: "oklch(44.4% 0.177 26.899)",
|
|
295
|
+
900: "oklch(39.6% 0.141 25.723)",
|
|
296
|
+
950: "oklch(25.8% 0.092 26.042)"
|
|
297
|
+
},
|
|
298
|
+
rose: {
|
|
299
|
+
50: "oklch(96.9% 0.015 12.422)",
|
|
300
|
+
100: "oklch(94.1% 0.03 12.58)",
|
|
301
|
+
200: "oklch(89.2% 0.058 10.001)",
|
|
302
|
+
300: "oklch(81% 0.117 11.638)",
|
|
303
|
+
400: "oklch(71.2% 0.194 13.428)",
|
|
304
|
+
500: "oklch(64.5% 0.246 16.439)",
|
|
305
|
+
600: "oklch(58.6% 0.253 17.585)",
|
|
306
|
+
700: "oklch(51.4% 0.222 16.935)",
|
|
307
|
+
800: "oklch(45.5% 0.188 13.697)",
|
|
308
|
+
900: "oklch(41% 0.159 10.272)",
|
|
309
|
+
950: "oklch(27.1% 0.105 12.094)"
|
|
310
|
+
},
|
|
311
|
+
sky: {
|
|
312
|
+
50: "oklch(97.7% 0.013 236.62)",
|
|
313
|
+
100: "oklch(95.1% 0.026 236.824)",
|
|
314
|
+
200: "oklch(90.1% 0.058 230.902)",
|
|
315
|
+
300: "oklch(82.8% 0.111 230.318)",
|
|
316
|
+
400: "oklch(74.6% 0.16 232.661)",
|
|
317
|
+
500: "oklch(68.5% 0.169 237.323)",
|
|
318
|
+
600: "oklch(58.8% 0.158 241.966)",
|
|
319
|
+
700: "oklch(50% 0.134 242.749)",
|
|
320
|
+
800: "oklch(44.3% 0.11 240.79)",
|
|
321
|
+
900: "oklch(39.1% 0.09 240.876)",
|
|
322
|
+
950: "oklch(29.3% 0.066 243.157)"
|
|
323
|
+
},
|
|
324
|
+
slate: {
|
|
325
|
+
50: "oklch(98.4% 0.003 247.858)",
|
|
326
|
+
100: "oklch(96.8% 0.007 247.896)",
|
|
327
|
+
200: "oklch(92.9% 0.013 255.508)",
|
|
328
|
+
300: "oklch(86.9% 0.022 252.894)",
|
|
329
|
+
400: "oklch(70.4% 0.04 256.788)",
|
|
330
|
+
500: "oklch(55.4% 0.046 257.417)",
|
|
331
|
+
600: "oklch(44.6% 0.043 257.281)",
|
|
332
|
+
700: "oklch(37.2% 0.044 257.287)",
|
|
333
|
+
800: "oklch(27.9% 0.041 260.031)",
|
|
334
|
+
900: "oklch(20.8% 0.042 265.755)",
|
|
335
|
+
950: "oklch(12.9% 0.042 264.695)"
|
|
336
|
+
},
|
|
337
|
+
stone: {
|
|
338
|
+
50: "oklch(98.5% 0.001 106.423)",
|
|
339
|
+
100: "oklch(97% 0.001 106.424)",
|
|
340
|
+
200: "oklch(92.3% 0.003 48.717)",
|
|
341
|
+
300: "oklch(86.9% 0.005 56.366)",
|
|
342
|
+
400: "oklch(70.9% 0.01 56.259)",
|
|
343
|
+
500: "oklch(55.3% 0.013 58.071)",
|
|
344
|
+
600: "oklch(44.4% 0.011 73.639)",
|
|
345
|
+
700: "oklch(37.4% 0.01 67.558)",
|
|
346
|
+
800: "oklch(26.8% 0.007 34.298)",
|
|
347
|
+
900: "oklch(21.6% 0.006 56.043)",
|
|
348
|
+
950: "oklch(14.7% 0.004 49.25)"
|
|
349
|
+
},
|
|
350
|
+
taupe: {
|
|
351
|
+
50: "oklch(98.6% 0.002 67.8)",
|
|
352
|
+
100: "oklch(96% 0.002 17.2)",
|
|
353
|
+
200: "oklch(92.2% 0.005 34.3)",
|
|
354
|
+
300: "oklch(86.8% 0.007 39.5)",
|
|
355
|
+
400: "oklch(71.4% 0.014 41.2)",
|
|
356
|
+
500: "oklch(54.7% 0.021 43.1)",
|
|
357
|
+
600: "oklch(43.8% 0.017 39.3)",
|
|
358
|
+
700: "oklch(36.7% 0.016 35.7)",
|
|
359
|
+
800: "oklch(26.8% 0.011 36.5)",
|
|
360
|
+
900: "oklch(21.4% 0.009 43.1)",
|
|
361
|
+
950: "oklch(14.7% 0.004 49.3)"
|
|
362
|
+
},
|
|
363
|
+
teal: {
|
|
364
|
+
50: "oklch(98.4% 0.014 180.72)",
|
|
365
|
+
100: "oklch(95.3% 0.051 180.801)",
|
|
366
|
+
200: "oklch(91% 0.096 180.426)",
|
|
367
|
+
300: "oklch(85.5% 0.138 181.071)",
|
|
368
|
+
400: "oklch(77.7% 0.152 181.912)",
|
|
369
|
+
500: "oklch(70.4% 0.14 182.503)",
|
|
370
|
+
600: "oklch(60% 0.118 184.704)",
|
|
371
|
+
700: "oklch(51.1% 0.096 186.391)",
|
|
372
|
+
800: "oklch(43.7% 0.078 188.216)",
|
|
373
|
+
900: "oklch(38.6% 0.063 188.416)",
|
|
374
|
+
950: "oklch(27.7% 0.046 192.524)"
|
|
375
|
+
},
|
|
376
|
+
violet: {
|
|
377
|
+
50: "oklch(96.9% 0.016 293.756)",
|
|
378
|
+
100: "oklch(94.3% 0.029 294.588)",
|
|
379
|
+
200: "oklch(89.4% 0.057 293.283)",
|
|
380
|
+
300: "oklch(81.1% 0.111 293.571)",
|
|
381
|
+
400: "oklch(70.2% 0.183 293.541)",
|
|
382
|
+
500: "oklch(60.6% 0.25 292.717)",
|
|
383
|
+
600: "oklch(54.1% 0.281 293.009)",
|
|
384
|
+
700: "oklch(49.1% 0.27 292.581)",
|
|
385
|
+
800: "oklch(43.2% 0.232 292.759)",
|
|
386
|
+
900: "oklch(38% 0.189 293.745)",
|
|
387
|
+
950: "oklch(28.3% 0.141 291.089)"
|
|
388
|
+
},
|
|
389
|
+
yellow: {
|
|
390
|
+
50: "oklch(98.7% 0.026 102.212)",
|
|
391
|
+
100: "oklch(97.3% 0.071 103.193)",
|
|
392
|
+
200: "oklch(94.5% 0.129 101.54)",
|
|
393
|
+
300: "oklch(90.5% 0.182 98.111)",
|
|
394
|
+
400: "oklch(85.2% 0.199 91.936)",
|
|
395
|
+
500: "oklch(79.5% 0.184 86.047)",
|
|
396
|
+
600: "oklch(68.1% 0.162 75.834)",
|
|
397
|
+
700: "oklch(55.4% 0.135 66.442)",
|
|
398
|
+
800: "oklch(47.6% 0.114 61.907)",
|
|
399
|
+
900: "oklch(42.1% 0.095 57.708)",
|
|
400
|
+
950: "oklch(28.6% 0.066 53.813)"
|
|
401
|
+
},
|
|
402
|
+
zinc: {
|
|
403
|
+
50: "oklch(98.5% 0 none)",
|
|
404
|
+
100: "oklch(96.7% 0.001 286.375)",
|
|
405
|
+
200: "oklch(92% 0.004 286.32)",
|
|
406
|
+
300: "oklch(87.1% 0.006 286.286)",
|
|
407
|
+
400: "oklch(70.5% 0.015 286.067)",
|
|
408
|
+
500: "oklch(55.2% 0.016 285.938)",
|
|
409
|
+
600: "oklch(44.2% 0.017 285.786)",
|
|
410
|
+
700: "oklch(37% 0.013 285.805)",
|
|
411
|
+
800: "oklch(27.4% 0.006 286.033)",
|
|
412
|
+
900: "oklch(21% 0.006 285.885)",
|
|
413
|
+
950: "oklch(14.1% 0.005 285.823)"
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
// src/text/index.ts
|
|
417
|
+
const text = {
|
|
418
|
+
config: {
|
|
419
|
+
keyFormat: "key must match [A-Za-z0-9_]+",
|
|
420
|
+
colorScaleEmpty: "color scale must define at least one shade",
|
|
421
|
+
defaultUnitRequired: "units must define a default unit",
|
|
422
|
+
fileNotFound: (cwd, files) => `tune config not found in ${cwd}, expected one of ${files.join(", ")}`,
|
|
423
|
+
defaultExportRequired: (file) => `${file} must export default config`,
|
|
424
|
+
invalid: (file, detail) => `invalid tune config in ${file}\n${detail}`
|
|
425
|
+
},
|
|
426
|
+
postprocess: {
|
|
427
|
+
walkIncomplete: (visited, total) => `walk visited ${visited} of ${total} nodes`,
|
|
428
|
+
/** reason phrases given by the checks */
|
|
429
|
+
reason: {
|
|
430
|
+
arbitrary: "arbitrary value is not allowed",
|
|
431
|
+
family: "raw color family is not allowed",
|
|
432
|
+
opacity: "opacity outside the theme keys",
|
|
433
|
+
size: "size outside the breakpoint unit range"
|
|
434
|
+
},
|
|
435
|
+
/** one dropped node */
|
|
436
|
+
dropped: (reason, target, owner) => owner ? `dropped ${target} in ${owner} (${reason})` : `dropped ${target} (${reason})`,
|
|
437
|
+
/** prefix of one warning line */
|
|
438
|
+
warn: (line) => `tunewind: ${line}`,
|
|
439
|
+
/** header of an error listing several lines */
|
|
440
|
+
summary: (count) => `tunewind: ${count} violations`,
|
|
441
|
+
/** tail of a list cut by the limit */
|
|
442
|
+
more: (count) => `... and ${count} more`
|
|
443
|
+
},
|
|
444
|
+
preprocess: {
|
|
445
|
+
breakpointDuplicated: (first, second) => `breakpoints "${first}" and "${second}" have the same width`,
|
|
446
|
+
breakpointPrefixDuplicated: (first, second) => `breakpoints "${first}" and "${second}" share the same prefix`,
|
|
447
|
+
breakpointWidthMissing: (breakpoint) => `breakpoint "${breakpoint}" has no width and no built-in width`,
|
|
448
|
+
maxMissing: (unit) => `size unit "${unit}" has no max`,
|
|
449
|
+
rangeReversed: (unit) => `size unit "${unit}" has larger min than max`,
|
|
450
|
+
shadeMissing: (family, shade) => `color family "${family}" has no shade ${shade}`,
|
|
451
|
+
themeDuplicated: (theme) => `theme "${theme}" is defined more than once`,
|
|
452
|
+
unknownUnit: (breakpoint, unit) => `breakpoint "${breakpoint}" overrides unknown unit "${unit}"`
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
// src/config/schema.ts
|
|
456
|
+
/** css length with an explicit unit, such as `4px` or `1rem` */
|
|
457
|
+
const cssLength = z.string().regex(/^(?:\d+(?:\.\d+)?|\.\d+)(?:px|rem)$/);
|
|
458
|
+
/** any css color value */
|
|
459
|
+
const colorValue = z.string().min(1);
|
|
460
|
+
/** config key, limited to word characters */
|
|
461
|
+
const keySchema = z.string().regex(/^\w+$/, { message: text.config.keyFormat });
|
|
462
|
+
/** built-in color family name */
|
|
463
|
+
const colorFamilySchema = z.enum(COLOR_FAMILIES);
|
|
464
|
+
/** built-in shade number */
|
|
465
|
+
const colorShadeSchema = z.literal(COLOR_SHADES);
|
|
466
|
+
/** color family scale, shade number to color value */
|
|
467
|
+
const colorScaleSchema = z.record(z.string().regex(/^\d+$/), colorValue).refine((scale) => Object.keys(scale).length > 0, { message: text.config.colorScaleEmpty });
|
|
468
|
+
/** palette entry, an alias to a built-in family or an explicit scale */
|
|
469
|
+
const paletteEntrySchema = z.union([colorFamilySchema, colorScaleSchema]);
|
|
470
|
+
/** color families, alias name to family or scale */
|
|
471
|
+
const paletteSchema = z.record(keySchema, paletteEntrySchema);
|
|
472
|
+
/** shade mapping, semantic key to shade number */
|
|
473
|
+
const shadeSchema = z.record(keySchema, colorShadeSchema);
|
|
474
|
+
/** opacity mapping, semantic key to percentage */
|
|
475
|
+
const opacitySchema = z.record(keySchema, z.number().min(0).max(100));
|
|
476
|
+
/** color scheme, overriding the global layers */
|
|
477
|
+
const schemeSchema = z.strictObject({
|
|
478
|
+
/** variant prefix, such as `dark` for `dark:bg-primary` */
|
|
479
|
+
prefix: z.string().min(1).optional(),
|
|
480
|
+
/** scheme palette */
|
|
481
|
+
palette: paletteSchema.optional(),
|
|
482
|
+
/** scheme shade mapping */
|
|
483
|
+
shade: shadeSchema.optional(),
|
|
484
|
+
/** scheme opacity mapping */
|
|
485
|
+
opacity: opacitySchema.optional()
|
|
486
|
+
});
|
|
487
|
+
/** color theme, global layers plus named schemes */
|
|
488
|
+
const themeSchema = z.strictObject({
|
|
489
|
+
/** global palette */
|
|
490
|
+
palette: paletteSchema.optional(),
|
|
491
|
+
/** global shade mapping */
|
|
492
|
+
shade: shadeSchema.optional(),
|
|
493
|
+
/** global opacity mapping */
|
|
494
|
+
opacity: opacitySchema.optional(),
|
|
495
|
+
/** named schemes, key to scheme */
|
|
496
|
+
schemes: z.record(keySchema, schemeSchema).optional()
|
|
497
|
+
});
|
|
498
|
+
/** size unit, a plain length or a constrained length */
|
|
499
|
+
const unitSchema = z.union([cssLength, z.strictObject({
|
|
500
|
+
/** unit length */
|
|
501
|
+
value: cssLength,
|
|
502
|
+
/** smallest usable number */
|
|
503
|
+
min: z.number().min(0).optional(),
|
|
504
|
+
/** allowed size number precision */
|
|
505
|
+
step: z.number().positive().optional(),
|
|
506
|
+
/** allowed size number upper bound */
|
|
507
|
+
max: z.number().positive().optional()
|
|
508
|
+
})]);
|
|
509
|
+
/** breakpoint, a plain width or a width with unit overrides */
|
|
510
|
+
const breakpointSchema = z.union([cssLength, z.strictObject({
|
|
511
|
+
/** variant name, the key when omitted */
|
|
512
|
+
prefix: z.string().min(1).optional(),
|
|
513
|
+
/** breakpoint width, the built-in width when omitted */
|
|
514
|
+
value: cssLength.optional(),
|
|
515
|
+
/** units redefined in this breakpoint */
|
|
516
|
+
units: z.record(keySchema, unitSchema).optional()
|
|
517
|
+
})]);
|
|
518
|
+
/** size system, units and breakpoints */
|
|
519
|
+
const sizeSchema = z.strictObject({
|
|
520
|
+
/** default smallest usable number for all units */
|
|
521
|
+
min: z.number().min(0).optional(),
|
|
522
|
+
/** default size number precision for all units */
|
|
523
|
+
step: z.number().positive().optional(),
|
|
524
|
+
/** default size number upper bound for all units */
|
|
525
|
+
max: z.number().positive().optional(),
|
|
526
|
+
/** units, key to unit, must define a `default` unit */
|
|
527
|
+
units: z.record(keySchema, unitSchema).refine((units) => "default" in units, { message: text.config.defaultUnitRequired }),
|
|
528
|
+
/** breakpoints, key to breakpoint */
|
|
529
|
+
breakpoints: z.record(keySchema, breakpointSchema).optional()
|
|
530
|
+
});
|
|
531
|
+
/** strict flags, every key allows the listed escape */
|
|
532
|
+
const strictFlagsSchema = z.strictObject({
|
|
533
|
+
/** allow arbitrary values such as `w-[123px]` */
|
|
534
|
+
arbitrary: z.boolean().optional(),
|
|
535
|
+
/** allow raw color family names such as `text-sky-600` */
|
|
536
|
+
family: z.boolean().optional(),
|
|
537
|
+
/** allow the raw prefix to bypass the postprocess stage */
|
|
538
|
+
bypass: z.boolean().optional()
|
|
539
|
+
});
|
|
540
|
+
/** strict mode, a switch, a mode name, or explicit flags */
|
|
541
|
+
const strictSchema = z.union([
|
|
542
|
+
z.boolean(),
|
|
543
|
+
z.enum(STRICT_MODES),
|
|
544
|
+
strictFlagsSchema
|
|
545
|
+
]);
|
|
546
|
+
/** diagnostic level, silent when omitted */
|
|
547
|
+
const diagnosticsSchema = z.union([z.enum(DIAGNOSTIC_LEVELS), z.strictObject({
|
|
548
|
+
/** diagnostic level */
|
|
549
|
+
mode: z.enum(DIAGNOSTIC_LEVELS),
|
|
550
|
+
/** most lines reported, defaults to the built-in limit */
|
|
551
|
+
limit: z.number().int().positive().optional()
|
|
552
|
+
})]);
|
|
553
|
+
/** tunewind config */
|
|
554
|
+
const configSchema = z.strictObject({
|
|
555
|
+
/** color theme */
|
|
556
|
+
theme: themeSchema.optional(),
|
|
557
|
+
/** size system */
|
|
558
|
+
size: sizeSchema.optional(),
|
|
559
|
+
/** strict mode */
|
|
560
|
+
strict: strictSchema.optional(),
|
|
561
|
+
/** diagnostic level of the dropped nodes */
|
|
562
|
+
diagnostics: diagnosticsSchema.optional()
|
|
563
|
+
});
|
|
564
|
+
// src/config/define.ts
|
|
565
|
+
function defineConfig(input) {
|
|
566
|
+
return configSchema.parse(input);
|
|
567
|
+
}
|
|
568
|
+
// src/const/config.ts
|
|
569
|
+
/** config file names, resolved in order */
|
|
570
|
+
const CONFIG_FILES = [
|
|
571
|
+
"tune.config.ts",
|
|
572
|
+
"tune.config.mts",
|
|
573
|
+
"tune.config.js",
|
|
574
|
+
"tune.config.mjs"
|
|
575
|
+
];
|
|
576
|
+
// src/plugins/output.ts
|
|
577
|
+
/** one expected failure of the build */
|
|
578
|
+
var TuneError = class extends Error {
|
|
579
|
+
constructor(message) {
|
|
580
|
+
super(message);
|
|
581
|
+
this.name = "TuneError";
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
/** read the message of an unknown thrown value */
|
|
585
|
+
function messageOf(error) {
|
|
586
|
+
return error instanceof Error ? error.message : String(error);
|
|
587
|
+
}
|
|
588
|
+
/** sink of the build in progress */
|
|
589
|
+
let sink;
|
|
590
|
+
const output = {
|
|
591
|
+
/** attach the sink of the build in progress */
|
|
592
|
+
bind: (next) => {
|
|
593
|
+
sink = next;
|
|
594
|
+
},
|
|
595
|
+
/** stop the build */
|
|
596
|
+
error: (message) => {
|
|
597
|
+
throw new TuneError(message);
|
|
598
|
+
},
|
|
599
|
+
/** report a non-fatal line, a missing sink escalates to an error */
|
|
600
|
+
warn: (line) => {
|
|
601
|
+
if (sink === void 0) output.error(line);
|
|
602
|
+
sink.warn(line);
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
// src/config/loader.ts
|
|
606
|
+
/** extensions tried when resolving an imported config file */
|
|
607
|
+
const IMPORT_EXTENSIONS = [
|
|
608
|
+
"",
|
|
609
|
+
".ts",
|
|
610
|
+
".mts",
|
|
611
|
+
".cts",
|
|
612
|
+
".js",
|
|
613
|
+
".mjs",
|
|
614
|
+
".cjs"
|
|
615
|
+
];
|
|
616
|
+
/** import forms a config may use for a local file */
|
|
617
|
+
const IMPORT_PATTERN = /(?:from|import|require)[\s(]*["']([^"']+)["']/g;
|
|
618
|
+
/** shared loader, evaluating the config source on every call */
|
|
619
|
+
const jiti = createJiti(import.meta.url, {
|
|
620
|
+
fsCache: false,
|
|
621
|
+
moduleCache: false
|
|
622
|
+
});
|
|
623
|
+
function findConfigFile(cwd = process.cwd()) {
|
|
624
|
+
for (const name of CONFIG_FILES) {
|
|
625
|
+
const file = resolve(cwd, name);
|
|
626
|
+
if (existsSync(file)) return file;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
/** resolve one local import of a config file */
|
|
630
|
+
function resolveImport(from, specifier) {
|
|
631
|
+
const base = resolve(dirname(from), specifier);
|
|
632
|
+
for (const extension of IMPORT_EXTENSIONS) {
|
|
633
|
+
const file = `${base}${extension}`;
|
|
634
|
+
if (existsSync(file)) return file;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
/** collect the local files a config imports, depth first */
|
|
638
|
+
function collectDependencies(file, found) {
|
|
639
|
+
if (found.has(file)) return;
|
|
640
|
+
found.add(file);
|
|
641
|
+
let source;
|
|
642
|
+
try {
|
|
643
|
+
source = readFileSync(file, "utf8");
|
|
644
|
+
} catch {
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
for (const match of source.matchAll(IMPORT_PATTERN)) {
|
|
648
|
+
const specifier = match[1];
|
|
649
|
+
if (!specifier.startsWith(".")) continue;
|
|
650
|
+
const target = resolveImport(file, specifier);
|
|
651
|
+
if (target !== void 0) collectDependencies(target, found);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
/** find the config file and every local file it imports */
|
|
655
|
+
function findConfigDependencies(file) {
|
|
656
|
+
const found = /* @__PURE__ */ new Set();
|
|
657
|
+
collectDependencies(file, found);
|
|
658
|
+
return Array.from(found);
|
|
659
|
+
}
|
|
660
|
+
async function loadConfig(cwd = process.cwd()) {
|
|
661
|
+
const file = findConfigFile(cwd);
|
|
662
|
+
if (!file) output.error(text.config.fileNotFound(cwd, CONFIG_FILES));
|
|
663
|
+
const source = await readFile(file, "utf8");
|
|
664
|
+
const mod = jiti.evalModule(source, {
|
|
665
|
+
filename: file,
|
|
666
|
+
id: file
|
|
667
|
+
});
|
|
668
|
+
const input = mod.default ?? mod.config;
|
|
669
|
+
if (input === void 0) output.error(text.config.defaultExportRequired(file));
|
|
670
|
+
try {
|
|
671
|
+
return {
|
|
672
|
+
config: configSchema.parse(input),
|
|
673
|
+
dependencies: findConfigDependencies(file)
|
|
674
|
+
};
|
|
675
|
+
} catch (error) {
|
|
676
|
+
if (error instanceof z.ZodError) output.error(text.config.invalid(file, z.prettifyError(error)));
|
|
677
|
+
throw error;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
// src/const/postcss.ts
|
|
681
|
+
/** postcss plugin name of the preprocess plugin */
|
|
682
|
+
const PREPROCESS_PLUGIN_NAME = "tunewind-preprocess";
|
|
683
|
+
/** postcss plugin name of the postprocess plugin */
|
|
684
|
+
const POSTPROCESS_PLUGIN_NAME = "tunewind-postprocess";
|
|
685
|
+
/** at-rule replaced by the generated css */
|
|
686
|
+
const AT_RULE_NAME = "tunewind";
|
|
687
|
+
/** node types of the postcss ast */
|
|
688
|
+
const NODE_TYPES = {
|
|
689
|
+
root: "root",
|
|
690
|
+
rule: "rule",
|
|
691
|
+
atRule: "atrule",
|
|
692
|
+
declaration: "decl",
|
|
693
|
+
comment: "comment"
|
|
694
|
+
};
|
|
695
|
+
/** at-rules removed when they lose every child */
|
|
696
|
+
const EMPTY_AT_RULES = [
|
|
697
|
+
"media",
|
|
698
|
+
"supports",
|
|
699
|
+
"container",
|
|
700
|
+
"layer"
|
|
701
|
+
];
|
|
702
|
+
/** at-rules whose content never takes part in the checks */
|
|
703
|
+
const BLOCKED_AT_RULES = [
|
|
704
|
+
"keyframes",
|
|
705
|
+
"font-face",
|
|
706
|
+
"page",
|
|
707
|
+
"property"
|
|
708
|
+
];
|
|
709
|
+
// src/postprocess/plugins/utils.ts
|
|
710
|
+
/** whether the node sits in the given cascade layer */
|
|
711
|
+
function hasLayer(atRules, name) {
|
|
712
|
+
return atRules.some((rule) => rule.name === "layer" && rule.params.split(",").some((part) => part.trim() === name));
|
|
713
|
+
}
|
|
714
|
+
// src/postprocess/plugins/arbitrary.ts
|
|
715
|
+
/** drop every selector holding an arbitrary value */
|
|
716
|
+
function createArbitraryPlugin() {
|
|
717
|
+
return { rule: (context) => {
|
|
718
|
+
if (!hasLayer(context.atRules, "utilities")) return;
|
|
719
|
+
if (context.bypassed()) return;
|
|
720
|
+
const hit = context.selectors().filter((item) => item.raw.includes("\\["));
|
|
721
|
+
if (hit.length > 0) context.dropSelectors(hit, text.postprocess.reason.arbitrary);
|
|
722
|
+
} };
|
|
723
|
+
}
|
|
724
|
+
// src/postprocess/plugins/family.ts
|
|
725
|
+
/** collect the color variables that only exist under their raw family name */
|
|
726
|
+
function collectRawVars(theme) {
|
|
727
|
+
const aliases = new Set(Object.keys(theme.palette ?? {}));
|
|
728
|
+
const keys = new Set(Object.keys(theme.shade ?? {}));
|
|
729
|
+
for (const scheme of Object.values(theme.schemes ?? {})) {
|
|
730
|
+
for (const name of Object.keys(scheme.palette ?? {})) aliases.add(name);
|
|
731
|
+
for (const key of Object.keys(scheme.shade ?? {})) keys.add(key);
|
|
732
|
+
}
|
|
733
|
+
const vars = /* @__PURE__ */ new Set();
|
|
734
|
+
for (const family of COLOR_FAMILIES) {
|
|
735
|
+
if (aliases.has(family)) continue;
|
|
736
|
+
for (const key of keys) vars.add(key === "default" ? `--color-${family}` : `--color-${family}-${key}`);
|
|
737
|
+
}
|
|
738
|
+
return vars;
|
|
739
|
+
}
|
|
740
|
+
/** drop every declaration referencing a raw color family name */
|
|
741
|
+
function createFamilyPlugin(theme) {
|
|
742
|
+
const vars = collectRawVars(theme);
|
|
743
|
+
return { declaration: (context) => {
|
|
744
|
+
if (!hasLayer(context.atRules, "utilities")) return;
|
|
745
|
+
if (context.bypassed()) return;
|
|
746
|
+
if (context.variables().some((name) => vars.has(name))) context.drop(text.postprocess.reason.family);
|
|
747
|
+
} };
|
|
748
|
+
}
|
|
749
|
+
// src/postprocess/plugins/opacity.ts
|
|
750
|
+
/** bodies of every call of a function, in order of appearance */
|
|
751
|
+
function callBodies(value, name) {
|
|
752
|
+
const bodies = [];
|
|
753
|
+
const head = `${name}(`;
|
|
754
|
+
let from = 0;
|
|
755
|
+
while (true) {
|
|
756
|
+
const start = value.indexOf(head, from);
|
|
757
|
+
if (start < 0) break;
|
|
758
|
+
let depth = 1;
|
|
759
|
+
let end = start + head.length;
|
|
760
|
+
for (; end < value.length; end += 1) {
|
|
761
|
+
const char = value[end];
|
|
762
|
+
if (char === "(") depth += 1;
|
|
763
|
+
else if (char === ")") {
|
|
764
|
+
depth -= 1;
|
|
765
|
+
if (depth === 0) break;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
bodies.push(value.slice(start + head.length, end));
|
|
769
|
+
from = end + 1;
|
|
770
|
+
}
|
|
771
|
+
return bodies;
|
|
772
|
+
}
|
|
773
|
+
/** split a call body by the commas outside brackets */
|
|
774
|
+
function splitArgs(body) {
|
|
775
|
+
const args = [];
|
|
776
|
+
let depth = 0;
|
|
777
|
+
let start = 0;
|
|
778
|
+
for (let index = 0; index < body.length; index += 1) {
|
|
779
|
+
const char = body[index];
|
|
780
|
+
if (char === "(" || char === "[") depth += 1;
|
|
781
|
+
else if (char === ")" || char === "]") depth -= 1;
|
|
782
|
+
else if (char === "," && depth === 0) {
|
|
783
|
+
args.push(body.slice(start, index).trim());
|
|
784
|
+
start = index + 1;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
args.push(body.slice(start).trim());
|
|
788
|
+
return args;
|
|
789
|
+
}
|
|
790
|
+
/** trailing token of a value holding several, absent when it holds one */
|
|
791
|
+
function trailingToken(value) {
|
|
792
|
+
const text = value.trim();
|
|
793
|
+
let depth = 0;
|
|
794
|
+
for (let index = text.length - 1; index >= 0; index -= 1) {
|
|
795
|
+
const char = text[index];
|
|
796
|
+
if (char === ")") depth += 1;
|
|
797
|
+
else if (char === "(") depth -= 1;
|
|
798
|
+
else if (/\s/.test(char) && depth === 0) return text.slice(index + 1);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
/** whether a slot is a literal percentage */
|
|
802
|
+
function isLiteral(slot) {
|
|
803
|
+
return /^[\d.]+%$/.test(slot.trim());
|
|
804
|
+
}
|
|
805
|
+
/** whether any opacity slot of a value is a literal percentage */
|
|
806
|
+
function hasLiteralSlot(value) {
|
|
807
|
+
for (const body of callBodies(value, "color-mix")) {
|
|
808
|
+
const args = splitArgs(body);
|
|
809
|
+
if (args.length !== 3 || args[2] !== "transparent") continue;
|
|
810
|
+
const amount = trailingToken(args[1]);
|
|
811
|
+
if (amount !== void 0 && isLiteral(amount)) return true;
|
|
812
|
+
}
|
|
813
|
+
for (const body of callBodies(value, "opacity")) if (isLiteral(body)) return true;
|
|
814
|
+
return false;
|
|
815
|
+
}
|
|
816
|
+
/** whether a declaration applies an opacity outside the allowed keys */
|
|
817
|
+
function hasLiteralOpacity(declaration) {
|
|
818
|
+
if (declaration.prop === "opacity") return isLiteral(declaration.value);
|
|
819
|
+
return hasLiteralSlot(declaration.value);
|
|
820
|
+
}
|
|
821
|
+
/** drop every declaration whose opacity does not come from a theme key */
|
|
822
|
+
function createOpacityPlugin() {
|
|
823
|
+
return { declaration: (context) => {
|
|
824
|
+
if (!hasLayer(context.atRules, "utilities")) return;
|
|
825
|
+
if (hasLiteralOpacity(context.declaration)) context.drop(text.postprocess.reason.opacity);
|
|
826
|
+
} };
|
|
827
|
+
}
|
|
828
|
+
// src/postprocess/scan/utils.ts
|
|
829
|
+
/** fallback of an invalid code point */
|
|
830
|
+
const REPLACEMENT = "�";
|
|
831
|
+
/** turn a code point into a character, invalid ones become the replacement */
|
|
832
|
+
function codePoint(code) {
|
|
833
|
+
if (code <= 0 || code > 1114111) return REPLACEMENT;
|
|
834
|
+
return String.fromCodePoint(code);
|
|
835
|
+
}
|
|
836
|
+
/** collect the matches of a value, once each */
|
|
837
|
+
function collect(value, pattern) {
|
|
838
|
+
const found = /* @__PURE__ */ new Set();
|
|
839
|
+
for (const match of value.matchAll(pattern)) found.add(match[1]);
|
|
840
|
+
return [...found];
|
|
841
|
+
}
|
|
842
|
+
/** unescape the css escapes of a selector */
|
|
843
|
+
function unescape(text) {
|
|
844
|
+
let result = "";
|
|
845
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
846
|
+
const char = text[index];
|
|
847
|
+
if (char !== "\\") {
|
|
848
|
+
result += char;
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
const hex = /^[\da-f]{1,6}/i.exec(text.slice(index + 1));
|
|
852
|
+
if (hex) {
|
|
853
|
+
result += codePoint(Number.parseInt(hex[0], 16));
|
|
854
|
+
index += hex[0].length;
|
|
855
|
+
if (text[index + 1] === " ") index += 1;
|
|
856
|
+
continue;
|
|
857
|
+
}
|
|
858
|
+
if (index + 1 < text.length) {
|
|
859
|
+
result += text[index + 1];
|
|
860
|
+
index += 1;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return result;
|
|
864
|
+
}
|
|
865
|
+
/** split by a separator outside brackets, skipping escaped characters */
|
|
866
|
+
function splitTopLevel(text, separator) {
|
|
867
|
+
const parts = [];
|
|
868
|
+
let depth = 0;
|
|
869
|
+
let start = 0;
|
|
870
|
+
let escaped = false;
|
|
871
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
872
|
+
const char = text[index];
|
|
873
|
+
if (escaped) {
|
|
874
|
+
escaped = false;
|
|
875
|
+
continue;
|
|
876
|
+
}
|
|
877
|
+
if (char === "\\") {
|
|
878
|
+
escaped = true;
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
if (char === "(" || char === "[") {
|
|
882
|
+
depth += 1;
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
if (char === ")" || char === "]") {
|
|
886
|
+
depth = Math.max(0, depth - 1);
|
|
887
|
+
continue;
|
|
888
|
+
}
|
|
889
|
+
if (char === separator && depth === 0) {
|
|
890
|
+
parts.push(text.slice(start, index));
|
|
891
|
+
start = index + 1;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
parts.push(text.slice(start));
|
|
895
|
+
return parts;
|
|
896
|
+
}
|
|
897
|
+
/** build one selector item from a part of a selector list */
|
|
898
|
+
function toSelectorItem(part) {
|
|
899
|
+
const raw = part.trim();
|
|
900
|
+
let text = unescape(raw);
|
|
901
|
+
if (text.startsWith(".")) text = text.slice(1);
|
|
902
|
+
return {
|
|
903
|
+
raw,
|
|
904
|
+
text,
|
|
905
|
+
segments: splitTopLevel(text, ":")
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
/** collect the `var()` references of a value, escapes kept as written */
|
|
909
|
+
function variablesOf(value) {
|
|
910
|
+
return collect(value, /var\(\s*(--(?:[\w-]|\\[^\n])+)/g);
|
|
911
|
+
}
|
|
912
|
+
// src/postprocess/plugins/size.ts
|
|
913
|
+
/** prefix of the spacing keys */
|
|
914
|
+
const SPACING_PREFIX = "--spacing-";
|
|
915
|
+
/** min widths of the media at-rules, cached by node */
|
|
916
|
+
const WIDTHS = /* @__PURE__ */ new WeakMap();
|
|
917
|
+
/** convert a css length into pixels */
|
|
918
|
+
function toPixels$1(length) {
|
|
919
|
+
return length.endsWith("rem") ? Number.parseFloat(length) * 16 : Number.parseFloat(length);
|
|
920
|
+
}
|
|
921
|
+
/** read the range of a unit, patched over the previous range */
|
|
922
|
+
function rangeOf(unit, size, previous) {
|
|
923
|
+
const options = typeof unit === "string" ? void 0 : unit;
|
|
924
|
+
const max = options?.max ?? previous?.max ?? size.max;
|
|
925
|
+
const min = options?.min ?? previous?.min ?? size.min ?? 0;
|
|
926
|
+
if (max === void 0 || min > max) return void 0;
|
|
927
|
+
return {
|
|
928
|
+
min,
|
|
929
|
+
max,
|
|
930
|
+
step: options?.step ?? previous?.step ?? size.step ?? 1
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
/** patch the ranges of a scope, field by field */
|
|
934
|
+
function patchRanges(scope, patch, size) {
|
|
935
|
+
const next = new Map(scope);
|
|
936
|
+
for (const [name, unit] of Object.entries(patch)) {
|
|
937
|
+
const range = rangeOf(unit, size, scope.get(name));
|
|
938
|
+
if (range !== void 0) next.set(name, range);
|
|
939
|
+
}
|
|
940
|
+
return next;
|
|
941
|
+
}
|
|
942
|
+
/** read the breakpoints ordered by width */
|
|
943
|
+
function parseBreakpoints$1(size) {
|
|
944
|
+
const parsed = [];
|
|
945
|
+
for (const [name, breakpoint] of Object.entries(size.breakpoints ?? {})) {
|
|
946
|
+
const options = typeof breakpoint === "string" ? void 0 : breakpoint;
|
|
947
|
+
const width = options?.value ?? BREAKPOINTS[name];
|
|
948
|
+
if (width === void 0) continue;
|
|
949
|
+
parsed.push({
|
|
950
|
+
width,
|
|
951
|
+
units: options?.units ?? {}
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
return parsed.sort((a, b) => toPixels$1(a.width) - toPixels$1(b.width));
|
|
955
|
+
}
|
|
956
|
+
/** build the width to unit ranges table of the configured breakpoints */
|
|
957
|
+
function buildScopes(size) {
|
|
958
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
959
|
+
let current = patchRanges(/* @__PURE__ */ new Map(), size.units, size);
|
|
960
|
+
for (const breakpoint of parseBreakpoints$1(size)) {
|
|
961
|
+
current = patchRanges(current, breakpoint.units, size);
|
|
962
|
+
scopes.set(toPixels$1(breakpoint.width), current);
|
|
963
|
+
}
|
|
964
|
+
return scopes;
|
|
965
|
+
}
|
|
966
|
+
/** min width in pixels of a media query, absent when it has none */
|
|
967
|
+
function minWidthOf(query) {
|
|
968
|
+
const body = query.replace(/[()\s]/g, "");
|
|
969
|
+
if (body.startsWith("min-width:")) return toPixels$1(body.slice(10));
|
|
970
|
+
if (body.startsWith("width>=")) return toPixels$1(body.slice(7));
|
|
971
|
+
}
|
|
972
|
+
/** min width in pixels of a media at-rule, cached by node */
|
|
973
|
+
function widthOf(atRule) {
|
|
974
|
+
const cached = WIDTHS.get(atRule);
|
|
975
|
+
if (cached !== void 0) return cached ?? void 0;
|
|
976
|
+
const width = minWidthOf(atRule.params);
|
|
977
|
+
WIDTHS.set(atRule, width ?? null);
|
|
978
|
+
return width;
|
|
979
|
+
}
|
|
980
|
+
/** ranges of the widest matching breakpoint among the enclosing at-rules */
|
|
981
|
+
function scopeOf(atRules, scopes) {
|
|
982
|
+
let found;
|
|
983
|
+
let widest = -1;
|
|
984
|
+
for (const atRule of atRules) {
|
|
985
|
+
if (atRule.name !== "media") continue;
|
|
986
|
+
const width = widthOf(atRule);
|
|
987
|
+
if (width === void 0 || width <= widest) continue;
|
|
988
|
+
const scope = scopes.get(width);
|
|
989
|
+
if (scope === void 0) continue;
|
|
990
|
+
widest = width;
|
|
991
|
+
found = scope;
|
|
992
|
+
}
|
|
993
|
+
return found;
|
|
994
|
+
}
|
|
995
|
+
/** whether a value stays inside the range of a unit */
|
|
996
|
+
function inRange(value, range) {
|
|
997
|
+
if (value === 0) return true;
|
|
998
|
+
const steps = value / range.step;
|
|
999
|
+
const index = Math.round(steps);
|
|
1000
|
+
if (Math.abs(steps - index) > 1e-6) return false;
|
|
1001
|
+
return index >= Math.ceil(range.min / range.step) && index <= Math.floor(range.max / range.step);
|
|
1002
|
+
}
|
|
1003
|
+
/** read the number and the range of a spacing reference */
|
|
1004
|
+
function spacingOf(name, scope) {
|
|
1005
|
+
const plain = unescape(name);
|
|
1006
|
+
if (!plain.startsWith(SPACING_PREFIX)) return void 0;
|
|
1007
|
+
const body = plain.slice(10);
|
|
1008
|
+
let matched;
|
|
1009
|
+
for (const [unit, range] of scope) {
|
|
1010
|
+
const suffix = unit === "default" ? "" : unit;
|
|
1011
|
+
if (!body.endsWith(suffix)) continue;
|
|
1012
|
+
if (matched !== void 0 && matched.suffix.length >= suffix.length) continue;
|
|
1013
|
+
matched = {
|
|
1014
|
+
suffix,
|
|
1015
|
+
range
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
if (matched === void 0) return void 0;
|
|
1019
|
+
const digits = body.slice(0, body.length - matched.suffix.length);
|
|
1020
|
+
if (digits === "") return void 0;
|
|
1021
|
+
const value = Number(digits);
|
|
1022
|
+
if (!Number.isFinite(value)) return void 0;
|
|
1023
|
+
return {
|
|
1024
|
+
value,
|
|
1025
|
+
range: matched.range
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
/** whether a variable reference breaks the unit range of the scope */
|
|
1029
|
+
function isOutside(name, scope) {
|
|
1030
|
+
const spacing = spacingOf(name, scope);
|
|
1031
|
+
if (spacing === void 0) return false;
|
|
1032
|
+
return !inRange(spacing.value, spacing.range);
|
|
1033
|
+
}
|
|
1034
|
+
/** drop every declaration whose size breaks the range of its breakpoint */
|
|
1035
|
+
function createSizePlugin(size) {
|
|
1036
|
+
const scopes = buildScopes(size);
|
|
1037
|
+
return { declaration: (context) => {
|
|
1038
|
+
if (!hasLayer(context.atRules, "utilities")) return;
|
|
1039
|
+
const scope = scopeOf(context.atRules, scopes);
|
|
1040
|
+
if (scope === void 0) return;
|
|
1041
|
+
if (context.variables().some((name) => isOutside(name, scope))) context.drop(text.postprocess.reason.size);
|
|
1042
|
+
} };
|
|
1043
|
+
}
|
|
1044
|
+
// src/postprocess/scan/context.ts
|
|
1045
|
+
/** selector lists already split, keyed by rule */
|
|
1046
|
+
const SELECTORS = /* @__PURE__ */ new WeakMap();
|
|
1047
|
+
/** split the selector list of a rule, reusing the split of the same rule */
|
|
1048
|
+
function selectorsOf(rule) {
|
|
1049
|
+
const cached = SELECTORS.get(rule);
|
|
1050
|
+
if (cached) return cached;
|
|
1051
|
+
const items = splitTopLevel(rule.selector, ",").map(toSelectorItem);
|
|
1052
|
+
SELECTORS.set(rule, items);
|
|
1053
|
+
return items;
|
|
1054
|
+
}
|
|
1055
|
+
/** whether the bypass marker of a rule exempts its nodes */
|
|
1056
|
+
function isBypassed(rule, enabled) {
|
|
1057
|
+
if (!enabled) return false;
|
|
1058
|
+
return selectorsOf(rule).some((item) => item.segments.includes("raw"));
|
|
1059
|
+
}
|
|
1060
|
+
/** build the context of a scanned rule */
|
|
1061
|
+
function createRuleContext(rule, atRules, recorder, bypass) {
|
|
1062
|
+
return {
|
|
1063
|
+
rule,
|
|
1064
|
+
atRules,
|
|
1065
|
+
selectors: () => selectorsOf(rule),
|
|
1066
|
+
bypassed: () => isBypassed(rule, bypass),
|
|
1067
|
+
drop: (reason) => {
|
|
1068
|
+
recorder.rules.add(rule);
|
|
1069
|
+
recorder.reports.push(text.postprocess.dropped(reason, rule.selector));
|
|
1070
|
+
},
|
|
1071
|
+
dropSelectors: (items, reason) => {
|
|
1072
|
+
const dropped = recorder.selectors.get(rule) ?? /* @__PURE__ */ new Set();
|
|
1073
|
+
for (const item of items) {
|
|
1074
|
+
dropped.add(item);
|
|
1075
|
+
recorder.reports.push(text.postprocess.dropped(reason, item.raw));
|
|
1076
|
+
}
|
|
1077
|
+
recorder.selectors.set(rule, dropped);
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
/** build the context of a scanned declaration */
|
|
1082
|
+
function createDeclarationContext(declaration, rule, atRules, recorder, bypass) {
|
|
1083
|
+
return {
|
|
1084
|
+
declaration,
|
|
1085
|
+
rule,
|
|
1086
|
+
atRules,
|
|
1087
|
+
variables: () => variablesOf(declaration.value),
|
|
1088
|
+
bypassed: () => isBypassed(rule, bypass),
|
|
1089
|
+
drop: (reason) => {
|
|
1090
|
+
recorder.declarations.add(declaration);
|
|
1091
|
+
recorder.reports.push(text.postprocess.dropped(reason, `${declaration.prop}: ${declaration.value}`, rule.selector));
|
|
1092
|
+
}
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
// src/postprocess/scan/commit.ts
|
|
1096
|
+
/** whether a container kind is removable once emptied */
|
|
1097
|
+
function cleanable(node) {
|
|
1098
|
+
if (node.type === NODE_TYPES.rule) return true;
|
|
1099
|
+
return EMPTY_AT_RULES.includes(node.name);
|
|
1100
|
+
}
|
|
1101
|
+
/** whether a container belongs to a removable kind */
|
|
1102
|
+
function prunable(node) {
|
|
1103
|
+
return node.type === NODE_TYPES.rule || node.type === NODE_TYPES.atRule;
|
|
1104
|
+
}
|
|
1105
|
+
/** remove the containers emptied by the last removal, climbing up */
|
|
1106
|
+
function clean(start) {
|
|
1107
|
+
let current = start;
|
|
1108
|
+
while (current.parent && prunable(current) && cleanable(current) && current.nodes?.length === 0) {
|
|
1109
|
+
const parent = current.parent;
|
|
1110
|
+
current.remove();
|
|
1111
|
+
current = parent;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
/** remove one node, then clean up what it leaves empty */
|
|
1115
|
+
function remove(node) {
|
|
1116
|
+
const parent = node.parent;
|
|
1117
|
+
node.remove();
|
|
1118
|
+
if (parent) clean(parent);
|
|
1119
|
+
}
|
|
1120
|
+
/** apply the recorded deletions, cleaning the nodes they empty */
|
|
1121
|
+
function commit(recorder) {
|
|
1122
|
+
for (const [rule, dropped] of recorder.selectors) {
|
|
1123
|
+
if (!rule.parent) continue;
|
|
1124
|
+
const kept = selectorsOf(rule).filter((item) => !dropped.has(item));
|
|
1125
|
+
if (kept.length === 0) {
|
|
1126
|
+
remove(rule);
|
|
1127
|
+
continue;
|
|
1128
|
+
}
|
|
1129
|
+
rule.selector = kept.map((item) => item.raw).join(", ");
|
|
1130
|
+
}
|
|
1131
|
+
for (const rule of recorder.rules) if (rule.parent) remove(rule);
|
|
1132
|
+
for (const declaration of recorder.declarations) if (declaration.parent) remove(declaration);
|
|
1133
|
+
}
|
|
1134
|
+
// src/postprocess/scan/walk.ts
|
|
1135
|
+
/** extend the at-rule stack with one block */
|
|
1136
|
+
function enterAtRule(atRule, atRules) {
|
|
1137
|
+
return atRules.concat(atRule);
|
|
1138
|
+
}
|
|
1139
|
+
/** dispatch the plugins over the subtree of a container, returning the node count */
|
|
1140
|
+
function visit(container, scope, plugins, recorder, bypass) {
|
|
1141
|
+
const active = scope.blocked ? [] : plugins;
|
|
1142
|
+
let count = 0;
|
|
1143
|
+
for (const child of container.nodes ?? []) {
|
|
1144
|
+
count += 1;
|
|
1145
|
+
if (child.type === NODE_TYPES.rule) {
|
|
1146
|
+
const context = createRuleContext(child, scope.atRules, recorder, bypass);
|
|
1147
|
+
for (const plugin of active) plugin.rule?.(context);
|
|
1148
|
+
count += visit(child, {
|
|
1149
|
+
atRules: scope.atRules,
|
|
1150
|
+
owner: child,
|
|
1151
|
+
blocked: scope.blocked
|
|
1152
|
+
}, plugins, recorder, bypass);
|
|
1153
|
+
} else if (child.type === NODE_TYPES.atRule) {
|
|
1154
|
+
if (child.nodes) count += visit(child, {
|
|
1155
|
+
atRules: enterAtRule(child, scope.atRules),
|
|
1156
|
+
owner: scope.owner,
|
|
1157
|
+
blocked: scope.blocked || BLOCKED_AT_RULES.includes(child.name)
|
|
1158
|
+
}, plugins, recorder, bypass);
|
|
1159
|
+
} else if (child.type === NODE_TYPES.declaration) {
|
|
1160
|
+
const owner = scope.owner;
|
|
1161
|
+
if (owner !== void 0 && !recorder.rules.has(owner)) {
|
|
1162
|
+
const context = createDeclarationContext(child, owner, scope.atRules, recorder, bypass);
|
|
1163
|
+
for (const plugin of active) plugin.declaration?.(context);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
return count;
|
|
1168
|
+
}
|
|
1169
|
+
/** walk the tree once, dispatching the plugins in order, returning the visited node count */
|
|
1170
|
+
function walk(root, plugins, recorder, options) {
|
|
1171
|
+
return visit(root, {
|
|
1172
|
+
atRules: [],
|
|
1173
|
+
blocked: false
|
|
1174
|
+
}, plugins, recorder, options.bypass ?? false);
|
|
1175
|
+
}
|
|
1176
|
+
// src/postprocess/scan/index.ts
|
|
1177
|
+
/** check that every node of the tree was visited */
|
|
1178
|
+
function verify(root, visited) {
|
|
1179
|
+
let total = 0;
|
|
1180
|
+
root.walk(() => {
|
|
1181
|
+
total += 1;
|
|
1182
|
+
});
|
|
1183
|
+
if (total !== visited) throw new Error(text.postprocess.walkIncomplete(visited, total));
|
|
1184
|
+
}
|
|
1185
|
+
/** scan the built css once with the given plugins, returning the diagnostic lines */
|
|
1186
|
+
function scan(root, plugins, options = {}) {
|
|
1187
|
+
const recorder = {
|
|
1188
|
+
rules: /* @__PURE__ */ new Set(),
|
|
1189
|
+
selectors: /* @__PURE__ */ new Map(),
|
|
1190
|
+
declarations: /* @__PURE__ */ new Set(),
|
|
1191
|
+
reports: []
|
|
1192
|
+
};
|
|
1193
|
+
const visited = walk(root, plugins, recorder, options);
|
|
1194
|
+
if (options.verify) verify(root, visited);
|
|
1195
|
+
commit(recorder);
|
|
1196
|
+
return recorder.reports;
|
|
1197
|
+
}
|
|
1198
|
+
// src/postprocess/index.ts
|
|
1199
|
+
/** normalize the strict forms into explicit allowances */
|
|
1200
|
+
function normalizeStrict(strict) {
|
|
1201
|
+
if (strict === void 0 || strict === false) return void 0;
|
|
1202
|
+
if (typeof strict === "object") return {
|
|
1203
|
+
arbitrary: strict.arbitrary === true,
|
|
1204
|
+
family: strict.family === true,
|
|
1205
|
+
bypass: strict.bypass === true
|
|
1206
|
+
};
|
|
1207
|
+
return {
|
|
1208
|
+
arbitrary: false,
|
|
1209
|
+
family: false,
|
|
1210
|
+
bypass: strict === "bypass"
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
/** normalize the diagnostics forms into an explicit level and limit */
|
|
1214
|
+
function normalizeDiagnostics(diagnostics) {
|
|
1215
|
+
if (diagnostics === void 0) return {
|
|
1216
|
+
level: "off",
|
|
1217
|
+
limit: 100
|
|
1218
|
+
};
|
|
1219
|
+
if (typeof diagnostics === "string") return {
|
|
1220
|
+
level: diagnostics,
|
|
1221
|
+
limit: 100
|
|
1222
|
+
};
|
|
1223
|
+
return {
|
|
1224
|
+
level: diagnostics.mode,
|
|
1225
|
+
limit: diagnostics.limit ?? 100
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
/** build the enabled checks for a config */
|
|
1229
|
+
function buildPlugins(config, policy) {
|
|
1230
|
+
const plugins = [];
|
|
1231
|
+
if (policy !== void 0 && !policy.arbitrary) plugins.push(createArbitraryPlugin());
|
|
1232
|
+
if (policy !== void 0 && !policy.family) plugins.push(createFamilyPlugin(config.theme ?? {}));
|
|
1233
|
+
plugins.push(createOpacityPlugin());
|
|
1234
|
+
if (config.size !== void 0) plugins.push(createSizePlugin(config.size));
|
|
1235
|
+
return plugins;
|
|
1236
|
+
}
|
|
1237
|
+
/** build the message listing the dropped nodes */
|
|
1238
|
+
function listing(lines, total) {
|
|
1239
|
+
const body = lines.map((line) => ` ${line}`).join("\n");
|
|
1240
|
+
const tail = total > lines.length ? `\n ${text.postprocess.more(total - lines.length)}` : "";
|
|
1241
|
+
return `${text.postprocess.summary(total)}\n${body}${tail}`;
|
|
1242
|
+
}
|
|
1243
|
+
/** report the dropped nodes according to the diagnostic settings */
|
|
1244
|
+
function report(lines, policy) {
|
|
1245
|
+
if (policy.level === "off" || lines.length === 0) return;
|
|
1246
|
+
const unique = Array.from(new Set(lines));
|
|
1247
|
+
const shown = unique.slice(0, policy.limit);
|
|
1248
|
+
if (policy.level === "warn") {
|
|
1249
|
+
for (const line of shown) output.warn(text.postprocess.warn(line));
|
|
1250
|
+
if (unique.length > shown.length) output.warn(text.postprocess.warn(text.postprocess.more(unique.length - shown.length)));
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
output.error(listing(shown, unique.length));
|
|
1254
|
+
}
|
|
1255
|
+
/** entry of the postprocess stage */
|
|
1256
|
+
function main$1(root, config) {
|
|
1257
|
+
const policy = normalizeStrict(config.strict);
|
|
1258
|
+
report(scan(root, buildPlugins(config, policy), { bypass: policy?.bypass ?? false }), normalizeDiagnostics(config.diagnostics));
|
|
1259
|
+
}
|
|
1260
|
+
// src/plugins/postprocess.ts
|
|
1261
|
+
const postprocess = Object.assign((options = {}) => ({
|
|
1262
|
+
postcssPlugin: POSTPROCESS_PLUGIN_NAME,
|
|
1263
|
+
async OnceExit(root, { result }) {
|
|
1264
|
+
output.bind({ warn: (line) => result.warn(line, { plugin: POSTPROCESS_PLUGIN_NAME }) });
|
|
1265
|
+
try {
|
|
1266
|
+
const { config, dependencies } = await loadConfig(options.cwd);
|
|
1267
|
+
for (const file of dependencies) result.messages.push({
|
|
1268
|
+
type: "dependency",
|
|
1269
|
+
plugin: POSTPROCESS_PLUGIN_NAME,
|
|
1270
|
+
file
|
|
1271
|
+
});
|
|
1272
|
+
main$1(root, config);
|
|
1273
|
+
} catch (error) {
|
|
1274
|
+
if (error instanceof TuneError) throw error;
|
|
1275
|
+
output.error(messageOf(error));
|
|
1276
|
+
} finally {
|
|
1277
|
+
output.bind(void 0);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
}), { postcss: true });
|
|
1281
|
+
// src/preprocess/raw.ts
|
|
1282
|
+
/** register the variant exempting a class from the checks */
|
|
1283
|
+
function buildRaw() {
|
|
1284
|
+
return `@custom-variant raw (&);`;
|
|
1285
|
+
}
|
|
1286
|
+
// src/preprocess/size/normalize.ts
|
|
1287
|
+
/** read a unit definition as options, a plain length becomes `{ value }` */
|
|
1288
|
+
function toOptions(unit) {
|
|
1289
|
+
return typeof unit === "string" ? { value: unit } : unit;
|
|
1290
|
+
}
|
|
1291
|
+
/** merge a unit patch into the units, field by field */
|
|
1292
|
+
function mergeUnits(units, patch) {
|
|
1293
|
+
const merged = { ...units };
|
|
1294
|
+
for (const [name, next] of Object.entries(patch)) merged[name] = {
|
|
1295
|
+
...toOptions(units[name] ?? next),
|
|
1296
|
+
...toOptions(next)
|
|
1297
|
+
};
|
|
1298
|
+
return merged;
|
|
1299
|
+
}
|
|
1300
|
+
/** convert a css length into pixels, used to order breakpoints */
|
|
1301
|
+
function toPixels(length) {
|
|
1302
|
+
return length.endsWith("rem") ? Number.parseFloat(length) * 16 : Number.parseFloat(length);
|
|
1303
|
+
}
|
|
1304
|
+
/** parse the breakpoints and order them by width */
|
|
1305
|
+
function parseBreakpoints(breakpoints) {
|
|
1306
|
+
const ordered = Object.entries(breakpoints).map(([name, breakpoint]) => {
|
|
1307
|
+
const options = typeof breakpoint === "string" ? void 0 : breakpoint;
|
|
1308
|
+
const builtin = BREAKPOINTS[name];
|
|
1309
|
+
const width = options?.value ?? builtin;
|
|
1310
|
+
if (width === void 0) output.error(text.preprocess.breakpointWidthMissing(name));
|
|
1311
|
+
return {
|
|
1312
|
+
name,
|
|
1313
|
+
prefix: options?.prefix ?? name,
|
|
1314
|
+
width,
|
|
1315
|
+
units: options?.units ?? {}
|
|
1316
|
+
};
|
|
1317
|
+
}).sort((a, b) => toPixels(a.width) - toPixels(b.width));
|
|
1318
|
+
const identifiers = /* @__PURE__ */ new Map();
|
|
1319
|
+
for (const [index, current] of ordered.entries()) {
|
|
1320
|
+
const previous = ordered[index - 1];
|
|
1321
|
+
if (previous !== void 0 && toPixels(previous.width) === toPixels(current.width)) output.error(text.preprocess.breakpointDuplicated(previous.name, current.name));
|
|
1322
|
+
const first = identifiers.get(current.prefix);
|
|
1323
|
+
if (first !== void 0) output.error(text.preprocess.breakpointPrefixDuplicated(first, current.name));
|
|
1324
|
+
identifiers.set(current.prefix, current.name);
|
|
1325
|
+
}
|
|
1326
|
+
return ordered;
|
|
1327
|
+
}
|
|
1328
|
+
/** resolve one scope of units into size units */
|
|
1329
|
+
function resolveUnits(units, min, max, step) {
|
|
1330
|
+
return Object.entries(units).map(([name, unit]) => {
|
|
1331
|
+
const options = toOptions(unit);
|
|
1332
|
+
const bound = options.max ?? max;
|
|
1333
|
+
if (bound === void 0) output.error(text.preprocess.maxMissing(name));
|
|
1334
|
+
const lower = options.min ?? min ?? 0;
|
|
1335
|
+
if (lower > bound) output.error(text.preprocess.rangeReversed(name));
|
|
1336
|
+
return {
|
|
1337
|
+
name,
|
|
1338
|
+
value: options.value,
|
|
1339
|
+
min: lower,
|
|
1340
|
+
max: bound,
|
|
1341
|
+
step: options.step ?? step ?? 1
|
|
1342
|
+
};
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
// src/preprocess/size/serialize.ts
|
|
1346
|
+
function unitName(unit) {
|
|
1347
|
+
return unit.name === "default" ? "--unit" : `--unit-${unit.name}`;
|
|
1348
|
+
}
|
|
1349
|
+
/** build the unit variables of one scope */
|
|
1350
|
+
function buildUnits(units) {
|
|
1351
|
+
return units.map((unit) => ` ${unitName(unit)}: ${unit.value};`);
|
|
1352
|
+
}
|
|
1353
|
+
/** build the spacing keys of one scope */
|
|
1354
|
+
function buildKeys(units) {
|
|
1355
|
+
const content = [];
|
|
1356
|
+
for (const unit of units) {
|
|
1357
|
+
const suffix = unit.name === "default" ? "" : unit.name;
|
|
1358
|
+
const precision = `${unit.step}`.split(".")[1]?.length ?? 0;
|
|
1359
|
+
const numbers = /* @__PURE__ */ new Set([0]);
|
|
1360
|
+
const start = Math.ceil(unit.min / unit.step);
|
|
1361
|
+
const end = Math.floor(unit.max / unit.step);
|
|
1362
|
+
for (let index = start; index <= end; index++) numbers.add(Number((index * unit.step).toFixed(precision)));
|
|
1363
|
+
for (const number of [...numbers].sort((a, b) => a - b)) content.push(` --spacing-${number}${suffix}: calc(${number} * var(${unitName(unit)}));`);
|
|
1364
|
+
}
|
|
1365
|
+
return content;
|
|
1366
|
+
}
|
|
1367
|
+
/** build the width variables of the configured breakpoints */
|
|
1368
|
+
function buildBreakpoints(breakpoints) {
|
|
1369
|
+
return breakpoints.map((breakpoint) => ` --breakpoint-${breakpoint.prefix}: ${breakpoint.width};`);
|
|
1370
|
+
}
|
|
1371
|
+
/** keep the units whose length differs from the previous table */
|
|
1372
|
+
function diffUnits(previous, next) {
|
|
1373
|
+
return next.filter((unit) => previous.find((item) => item.name === unit.name)?.value !== unit.value);
|
|
1374
|
+
}
|
|
1375
|
+
/** wrap the content into the theme block that resets the tailwind spacing */
|
|
1376
|
+
function wrapTheme$1(content) {
|
|
1377
|
+
return [
|
|
1378
|
+
"@theme {",
|
|
1379
|
+
" --spacing: initial;",
|
|
1380
|
+
...content,
|
|
1381
|
+
"}"
|
|
1382
|
+
];
|
|
1383
|
+
}
|
|
1384
|
+
/** wrap the content into the media query of a breakpoint */
|
|
1385
|
+
function wrapBreakpoint(content, width) {
|
|
1386
|
+
return [
|
|
1387
|
+
`@media (min-width: ${width}) {`,
|
|
1388
|
+
" :root {",
|
|
1389
|
+
...content.map((line) => ` ${line}`),
|
|
1390
|
+
" }",
|
|
1391
|
+
"}"
|
|
1392
|
+
];
|
|
1393
|
+
}
|
|
1394
|
+
// src/preprocess/size/index.ts
|
|
1395
|
+
/** build the css of the size system and its breakpoints */
|
|
1396
|
+
function buildSize(config) {
|
|
1397
|
+
const size = config.size;
|
|
1398
|
+
if (size === void 0) return "";
|
|
1399
|
+
const defaults = [
|
|
1400
|
+
size.min,
|
|
1401
|
+
size.max,
|
|
1402
|
+
size.step
|
|
1403
|
+
];
|
|
1404
|
+
const base = resolveUnits(size.units, ...defaults);
|
|
1405
|
+
const breakpoints = parseBreakpoints(size.breakpoints ?? {});
|
|
1406
|
+
const keys = new Set(buildKeys(base));
|
|
1407
|
+
const blocks = [];
|
|
1408
|
+
let units = size.units;
|
|
1409
|
+
let previous = base;
|
|
1410
|
+
for (const breakpoint of breakpoints) {
|
|
1411
|
+
for (const name of Object.keys(breakpoint.units)) if (!(name in size.units)) output.error(text.preprocess.unknownUnit(breakpoint.name, name));
|
|
1412
|
+
units = mergeUnits(units, breakpoint.units);
|
|
1413
|
+
const scoped = resolveUnits(units, ...defaults);
|
|
1414
|
+
for (const key of buildKeys(scoped)) keys.add(key);
|
|
1415
|
+
const changed = diffUnits(previous, scoped);
|
|
1416
|
+
if (changed.length > 0) blocks.push(wrapBreakpoint(buildUnits(changed), breakpoint.width));
|
|
1417
|
+
previous = scoped;
|
|
1418
|
+
}
|
|
1419
|
+
return [...wrapTheme$1([
|
|
1420
|
+
...buildBreakpoints(breakpoints),
|
|
1421
|
+
...buildUnits(base),
|
|
1422
|
+
...keys
|
|
1423
|
+
]), ...blocks.flat()].join("\n");
|
|
1424
|
+
}
|
|
1425
|
+
// src/preprocess/theme/normalize.ts
|
|
1426
|
+
/** fold a theme config into a group, filling the absent dimensions */
|
|
1427
|
+
function groupOf(theme) {
|
|
1428
|
+
return {
|
|
1429
|
+
palette: theme.palette ?? {},
|
|
1430
|
+
shade: theme.shade ?? {},
|
|
1431
|
+
opacity: theme.opacity ?? {}
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
/** merge a scheme group over the default group, name by name */
|
|
1435
|
+
function mergeGroup(base, scheme) {
|
|
1436
|
+
return {
|
|
1437
|
+
palette: {
|
|
1438
|
+
...base.palette,
|
|
1439
|
+
...scheme.palette
|
|
1440
|
+
},
|
|
1441
|
+
shade: {
|
|
1442
|
+
...base.shade,
|
|
1443
|
+
...scheme.shade
|
|
1444
|
+
},
|
|
1445
|
+
opacity: {
|
|
1446
|
+
...base.opacity,
|
|
1447
|
+
...scheme.opacity
|
|
1448
|
+
}
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
/** build the palette of every usable color name */
|
|
1452
|
+
function buildPalette(palette) {
|
|
1453
|
+
const themePalette = /* @__PURE__ */ new Map();
|
|
1454
|
+
for (const family of COLOR_FAMILIES) {
|
|
1455
|
+
const scale = /* @__PURE__ */ new Map();
|
|
1456
|
+
for (const shade of COLOR_SHADES) scale.set(shade, PALETTE[family][shade]);
|
|
1457
|
+
themePalette.set(family, scale);
|
|
1458
|
+
}
|
|
1459
|
+
for (const [name, entry] of Object.entries(palette)) {
|
|
1460
|
+
if (typeof entry === "string") continue;
|
|
1461
|
+
const scale = themePalette.get(name) ?? /* @__PURE__ */ new Map();
|
|
1462
|
+
for (const [shade, value] of Object.entries(entry)) scale.set(Number(shade), value);
|
|
1463
|
+
themePalette.set(name, scale);
|
|
1464
|
+
}
|
|
1465
|
+
for (const [name, entry] of Object.entries(palette)) {
|
|
1466
|
+
if (typeof entry !== "string") continue;
|
|
1467
|
+
const scale = themePalette.get(entry) ?? /* @__PURE__ */ new Map();
|
|
1468
|
+
themePalette.set(name, new Map(scale));
|
|
1469
|
+
}
|
|
1470
|
+
return themePalette;
|
|
1471
|
+
}
|
|
1472
|
+
/** build the color table of every usable color name by the shade mapping */
|
|
1473
|
+
function buildTable(themePalette, shade) {
|
|
1474
|
+
const table = /* @__PURE__ */ new Map();
|
|
1475
|
+
for (const [name, scale] of themePalette) {
|
|
1476
|
+
const values = /* @__PURE__ */ new Map();
|
|
1477
|
+
for (const [key, colorShade] of Object.entries(shade)) {
|
|
1478
|
+
const value = scale.get(colorShade);
|
|
1479
|
+
if (value === void 0) output.error(text.preprocess.shadeMissing(name, colorShade));
|
|
1480
|
+
values.set(key, value);
|
|
1481
|
+
}
|
|
1482
|
+
table.set(name, values);
|
|
1483
|
+
}
|
|
1484
|
+
return table;
|
|
1485
|
+
}
|
|
1486
|
+
/** resolve palette and shade mapping into the color table of every usable name */
|
|
1487
|
+
function normalize(palette, shade) {
|
|
1488
|
+
return buildTable(buildPalette(palette), shade);
|
|
1489
|
+
}
|
|
1490
|
+
// src/preprocess/theme/serialize.ts
|
|
1491
|
+
/** build one color declaration, omitting the suffix of the default shade */
|
|
1492
|
+
function buildColor(name, value, key) {
|
|
1493
|
+
return ` --color-${name}${key === void 0 || key === "default" ? "" : `-${key}`}: ${value};`;
|
|
1494
|
+
}
|
|
1495
|
+
/** build the content of a theme block from the opacity mapping */
|
|
1496
|
+
function buildOpacity(opacity) {
|
|
1497
|
+
return Object.entries(opacity).map(([key, value]) => ` --opacity-${key}: ${value}%;`);
|
|
1498
|
+
}
|
|
1499
|
+
/** build the content of a theme block from the color table */
|
|
1500
|
+
function buildContent(table) {
|
|
1501
|
+
const content = [];
|
|
1502
|
+
for (const [name, values] of table) for (const [key, value] of values) content.push(buildColor(name, value, key));
|
|
1503
|
+
return content;
|
|
1504
|
+
}
|
|
1505
|
+
/** keep the opacity of a scheme that differ from the default theme */
|
|
1506
|
+
function diffOpacity(base, next) {
|
|
1507
|
+
const opacity = {};
|
|
1508
|
+
for (const [key, value] of Object.entries(next)) if (base[key] !== value) opacity[key] = value;
|
|
1509
|
+
return opacity;
|
|
1510
|
+
}
|
|
1511
|
+
/** keep the colors of a scheme that differ from the default theme */
|
|
1512
|
+
function diffTable(base, next) {
|
|
1513
|
+
const table = /* @__PURE__ */ new Map();
|
|
1514
|
+
for (const [name, values] of next) {
|
|
1515
|
+
const changed = /* @__PURE__ */ new Map();
|
|
1516
|
+
for (const [key, value] of values) if (base.get(name)?.get(key) !== value) changed.set(key, value);
|
|
1517
|
+
if (changed.size > 0) table.set(name, changed);
|
|
1518
|
+
}
|
|
1519
|
+
return table;
|
|
1520
|
+
}
|
|
1521
|
+
/** wrap theme content into a block, resetting the namespace for the default theme */
|
|
1522
|
+
function wrapTheme(content, space, theme) {
|
|
1523
|
+
if (theme === void 0) {
|
|
1524
|
+
const constants = space === "color" ? Object.entries(FLAT_COLORS).map(([name, value]) => buildColor(name, value)) : [];
|
|
1525
|
+
return [
|
|
1526
|
+
"@theme {",
|
|
1527
|
+
` --${space}-*: initial;`,
|
|
1528
|
+
...constants,
|
|
1529
|
+
...content,
|
|
1530
|
+
"}"
|
|
1531
|
+
];
|
|
1532
|
+
}
|
|
1533
|
+
return [
|
|
1534
|
+
`[data-theme="${theme}"] {`,
|
|
1535
|
+
...content,
|
|
1536
|
+
"}"
|
|
1537
|
+
];
|
|
1538
|
+
}
|
|
1539
|
+
/** register the variant that scopes utilities to a theme */
|
|
1540
|
+
function customVariant(theme) {
|
|
1541
|
+
return `@custom-variant ${theme} (&:where([data-theme="${theme}"] *));`;
|
|
1542
|
+
}
|
|
1543
|
+
// src/preprocess/theme/index.ts
|
|
1544
|
+
/** build the css of the default theme and all schemes */
|
|
1545
|
+
function buildTheme(config) {
|
|
1546
|
+
const theme = config.theme ?? {};
|
|
1547
|
+
const base = groupOf(theme);
|
|
1548
|
+
const baseTable = normalize(base.palette, base.shade);
|
|
1549
|
+
const variants = [];
|
|
1550
|
+
const blocks = [wrapTheme(buildContent(baseTable), "color")];
|
|
1551
|
+
if (Object.keys(base.opacity).length > 0) blocks.push(wrapTheme(buildOpacity(base.opacity), "opacity"));
|
|
1552
|
+
const identifiers = /* @__PURE__ */ new Set();
|
|
1553
|
+
for (const [key, scheme] of Object.entries(theme.schemes ?? {})) {
|
|
1554
|
+
const identifier = scheme.prefix ?? key;
|
|
1555
|
+
if (identifiers.has(identifier)) output.error(text.preprocess.themeDuplicated(identifier));
|
|
1556
|
+
identifiers.add(identifier);
|
|
1557
|
+
variants.push(customVariant(identifier));
|
|
1558
|
+
const merged = mergeGroup(base, groupOf(scheme));
|
|
1559
|
+
const table = diffTable(baseTable, normalize(merged.palette, merged.shade));
|
|
1560
|
+
const opacity = diffOpacity(base.opacity, merged.opacity);
|
|
1561
|
+
if (table.size > 0) blocks.push(wrapTheme(buildContent(table), "color", identifier));
|
|
1562
|
+
if (Object.keys(opacity).length > 0) blocks.push(wrapTheme(buildOpacity(opacity), "opacity", identifier));
|
|
1563
|
+
}
|
|
1564
|
+
return [...variants, ...blocks.flat()].join("\n");
|
|
1565
|
+
}
|
|
1566
|
+
// src/preprocess/index.ts
|
|
1567
|
+
function main(config) {
|
|
1568
|
+
return [
|
|
1569
|
+
buildRaw(),
|
|
1570
|
+
buildTheme(config),
|
|
1571
|
+
buildSize(config)
|
|
1572
|
+
].filter(Boolean).join("\n");
|
|
1573
|
+
}
|
|
1574
|
+
// src/plugins/preprocess.ts
|
|
1575
|
+
function findAtRules(root) {
|
|
1576
|
+
const atRules = [];
|
|
1577
|
+
root.walkAtRules(AT_RULE_NAME, (atRule) => {
|
|
1578
|
+
atRules.push(atRule);
|
|
1579
|
+
});
|
|
1580
|
+
return atRules;
|
|
1581
|
+
}
|
|
1582
|
+
// src/plugins/index.ts
|
|
1583
|
+
const tunewind = {
|
|
1584
|
+
preprocess: Object.assign((options = {}) => ({
|
|
1585
|
+
postcssPlugin: PREPROCESS_PLUGIN_NAME,
|
|
1586
|
+
async Once(root, { result }) {
|
|
1587
|
+
output.bind({ warn: (line) => result.warn(line, { plugin: PREPROCESS_PLUGIN_NAME }) });
|
|
1588
|
+
try {
|
|
1589
|
+
const atRules = findAtRules(root);
|
|
1590
|
+
if (atRules.length === 0) return;
|
|
1591
|
+
const { config, dependencies } = await loadConfig(options.cwd);
|
|
1592
|
+
for (const file of dependencies) result.messages.push({
|
|
1593
|
+
type: "dependency",
|
|
1594
|
+
plugin: PREPROCESS_PLUGIN_NAME,
|
|
1595
|
+
file
|
|
1596
|
+
});
|
|
1597
|
+
const css = main(config);
|
|
1598
|
+
if (css.length === 0) {
|
|
1599
|
+
atRules.forEach((atRule) => atRule.remove());
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1602
|
+
const nodes = parse(css).nodes;
|
|
1603
|
+
atRules.forEach((atRule) => atRule.replaceWith(...nodes.map((node) => node.clone())));
|
|
1604
|
+
} catch (error) {
|
|
1605
|
+
if (error instanceof TuneError) throw error;
|
|
1606
|
+
output.error(messageOf(error));
|
|
1607
|
+
} finally {
|
|
1608
|
+
output.bind(void 0);
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
}), { postcss: true }),
|
|
1612
|
+
postprocess
|
|
1613
|
+
};
|
|
1614
|
+
export { defineConfig, tunewind };
|