@yahoo/uds 3.113.0 → 3.114.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/dist/automated-config/dist/mapTextVariantFixtureToValue.cjs +12 -1
  2. package/dist/automated-config/dist/mapTextVariantFixtureToValue.js +12 -1
  3. package/dist/automated-config/dist/properties.cjs +1 -1
  4. package/dist/automated-config/dist/properties.js +1 -1
  5. package/dist/automated-config/dist/utils/getConfigVariantProperties.d.cts +2 -2
  6. package/dist/automated-config/dist/utils/getConfigVariantProperties.d.ts +2 -2
  7. package/dist/cli/commands/sync.cjs +1 -3
  8. package/dist/cli/commands/sync.d.cts +1 -1
  9. package/dist/cli/commands/sync.d.ts +1 -1
  10. package/dist/cli/commands/sync.js +1 -3
  11. package/dist/cli/commands/version.cjs +0 -2
  12. package/dist/cli/commands/version.d.cts +1 -1
  13. package/dist/cli/commands/version.d.ts +1 -1
  14. package/dist/cli/commands/version.js +0 -2
  15. package/dist/cli/dist/cli.cjs +1 -1
  16. package/dist/cli/dist/cli.js +1 -1
  17. package/dist/cli/dist/commands/editor-rules.cjs +2 -2
  18. package/dist/cli/dist/commands/editor-rules.js +2 -2
  19. package/dist/cli/dist/lib/logger.cjs +66 -0
  20. package/dist/cli/dist/lib/logger.js +66 -0
  21. package/dist/cli/dist/utils/rules/config.cjs +1 -1
  22. package/dist/cli/dist/utils/rules/config.js +1 -1
  23. package/dist/cli/runner.cjs +11 -2
  24. package/dist/cli/runner.js +11 -2
  25. package/dist/components/client/Menu/Menu.ItemCheckbox.d.cts +1 -1
  26. package/dist/components/client/Menu/Menu.ItemCheckbox.d.ts +1 -1
  27. package/dist/index.cjs +2 -0
  28. package/dist/index.d.cts +3 -1
  29. package/dist/index.d.ts +3 -1
  30. package/dist/index.js +2 -1
  31. package/dist/styles/styler.d.cts +41 -41
  32. package/dist/styles/styler.d.ts +41 -41
  33. package/dist/styles/variants.cjs +278 -278
  34. package/dist/styles/variants.js +278 -278
  35. package/dist/tailwind/dist/commands/css.cjs +79 -0
  36. package/dist/tailwind/dist/commands/css.d.ts +3 -0
  37. package/dist/tailwind/dist/commands/css.helpers.cjs +32 -0
  38. package/dist/tailwind/dist/commands/css.helpers.js +28 -0
  39. package/dist/tailwind/dist/commands/css.js +79 -0
  40. package/dist/tailwind/dist/commands/generateComponentData.cjs +33 -31
  41. package/dist/tailwind/dist/commands/generateComponentData.d.ts +1 -1
  42. package/dist/tailwind/dist/commands/generateComponentData.js +33 -31
  43. package/dist/tailwind/dist/commands/generatePurgeCSSData.d.ts +1 -1
  44. package/dist/tailwind/dist/commands/purge.cjs +3 -4
  45. package/dist/tailwind/dist/commands/purge.d.ts +1 -1
  46. package/dist/tailwind/dist/commands/purge.js +3 -4
  47. package/dist/tailwind/dist/css/generate.cjs +120 -0
  48. package/dist/tailwind/dist/css/generate.d.cts +30 -0
  49. package/dist/tailwind/dist/css/generate.d.ts +31 -0
  50. package/dist/tailwind/dist/css/generate.helpers.cjs +112 -0
  51. package/dist/tailwind/dist/css/generate.helpers.js +100 -0
  52. package/dist/tailwind/dist/css/generate.js +115 -0
  53. package/dist/tailwind/dist/css/postcss.cjs +35 -0
  54. package/dist/tailwind/dist/css/postcss.helpers.cjs +27 -0
  55. package/dist/tailwind/dist/css/postcss.helpers.js +26 -0
  56. package/dist/tailwind/dist/css/postcss.js +35 -0
  57. package/dist/tailwind/dist/css/runner.cjs +278 -0
  58. package/dist/tailwind/dist/css/runner.helpers.cjs +26 -0
  59. package/dist/tailwind/dist/css/runner.helpers.js +23 -0
  60. package/dist/tailwind/dist/css/runner.js +275 -0
  61. package/dist/tailwind/dist/css/theme.cjs +12 -0
  62. package/dist/tailwind/dist/css/theme.d.cts +66 -0
  63. package/dist/tailwind/dist/css/theme.d.ts +66 -0
  64. package/dist/tailwind/dist/css/theme.js +11 -0
  65. package/dist/tailwind/dist/css/utils.cjs +234 -0
  66. package/dist/tailwind/dist/css/utils.js +223 -0
  67. package/dist/tailwind/dist/index.d.cts +1 -0
  68. package/dist/tailwind/dist/index.d.ts +5 -3
  69. package/dist/tailwind/dist/purger/legacy/purgeCSS.cjs +4 -3
  70. package/dist/tailwind/dist/purger/legacy/purgeCSS.js +4 -3
  71. package/dist/tailwind/dist/purger/optimized/ast/expressions.cjs +122 -125
  72. package/dist/tailwind/dist/purger/optimized/ast/expressions.js +122 -125
  73. package/dist/tailwind/dist/purger/optimized/ast/jsx.cjs +1 -8
  74. package/dist/tailwind/dist/purger/optimized/ast/jsx.js +1 -8
  75. package/dist/tailwind/dist/purger/optimized/purge.cjs +11 -10
  76. package/dist/tailwind/dist/purger/optimized/purge.js +10 -9
  77. package/dist/tailwind/dist/purger/optimized/purgeFromCode.cjs +232 -127
  78. package/dist/tailwind/dist/purger/optimized/purgeFromCode.js +232 -127
  79. package/dist/tailwind/dist/purger/optimized/utils/componentAnalyzer.cjs +330 -262
  80. package/dist/tailwind/dist/purger/optimized/utils/componentAnalyzer.js +329 -262
  81. package/dist/tailwind/dist/purger/optimized/utils/files.cjs +4 -3
  82. package/dist/tailwind/dist/purger/optimized/utils/files.js +4 -3
  83. package/dist/tailwind/dist/purger/optimized/utils/safelist.cjs +13 -21
  84. package/dist/tailwind/dist/purger/optimized/utils/safelist.js +13 -21
  85. package/dist/tailwind/dist/tailwind/plugins/typography.cjs +41 -13
  86. package/dist/tailwind/dist/tailwind/plugins/typography.js +41 -13
  87. package/dist/tailwind/dist/tailwind/utils/composeTailwindPlugins.cjs +4 -2
  88. package/dist/tailwind/dist/tailwind/utils/composeTailwindPlugins.d.cts +10 -1
  89. package/dist/tailwind/dist/tailwind/utils/composeTailwindPlugins.d.ts +10 -1
  90. package/dist/tailwind/dist/tailwind/utils/composeTailwindPlugins.js +4 -2
  91. package/dist/tailwind/dist/tailwind/utils/getFontStyles.d.cts +1 -1
  92. package/dist/tailwind/dist/tailwind/utils/getFontStyles.d.ts +1 -1
  93. package/dist/tailwind/dist/utils/optimizeCSS.cjs +405 -0
  94. package/dist/tailwind/dist/utils/optimizeCSS.js +403 -0
  95. package/dist/tailwind/dist/utils/postcssPreserveVars.cjs +67 -0
  96. package/dist/tailwind/dist/utils/postcssPreserveVars.js +65 -0
  97. package/dist/tailwind/dist/utils/tsMorph.cjs +1 -1
  98. package/dist/uds/generated/componentData.cjs +943 -928
  99. package/dist/uds/generated/componentData.js +943 -928
  100. package/dist/uds/package.cjs +10 -4
  101. package/dist/uds/package.js +10 -4
  102. package/generated/componentData.json +2397 -0
  103. package/generated/tailwindPurge.ts +4560 -0
  104. package/package.json +7 -4
@@ -0,0 +1,405 @@
1
+ /*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
2
+ const require_runtime = require('../../../_virtual/_rolldown/runtime.cjs');
3
+ let node_zlib = require("node:zlib");
4
+ let postcss = require("postcss");
5
+ postcss = require_runtime.__toESM(postcss);
6
+
7
+ //#region ../tailwind/dist/utils/optimizeCSS.js
8
+ /*! © 2026 Yahoo, Inc. UDS Tailwind and Purger v0.0.0-development */
9
+ /**
10
+ * Check if a selector is a "meaningful" selector (class, ID, attribute, or pseudo-class)
11
+ * as opposed to just element/type selectors (like `code, kbd, pre` in preflight).
12
+ * Meaningful selectors indicate the font is actually used by components, not just base reset.
13
+ */
14
+ function isMeaningfulSelector(selector) {
15
+ const parts = selector.split(",").map((s) => s.trim());
16
+ for (const part of parts) if (part.includes(".") || part.includes("#") || part.includes("[") || part.includes(":not(") || part.includes(":where(") || part.includes(":is(") || part.includes(":has(")) return true;
17
+ return false;
18
+ }
19
+ /**
20
+ * PostCSS plugin to remove @font-face declarations for font families that are not used
21
+ * anywhere in the CSS (excluding preflight/reset styles that only use element selectors).
22
+ */
23
+ function removeUnusedFontFaces() {
24
+ return {
25
+ postcssPlugin: "remove-unused-font-faces",
26
+ Once(root) {
27
+ const fontVarUsage = /* @__PURE__ */ new Map();
28
+ const usedFontFamilies = /* @__PURE__ */ new Set();
29
+ root.walkDecls("font-family", (decl) => {
30
+ if (decl.parent?.type === "atrule" && decl.parent.name === "font-face") return;
31
+ const meaningful = isMeaningfulSelector(decl.parent?.selector || "");
32
+ const value = decl.value;
33
+ const varMatches = value.matchAll(/var\(--uds-font-([^),]+)\)/g);
34
+ for (const match of varMatches) {
35
+ const varName = match[1];
36
+ const existing = fontVarUsage.get(varName) || {
37
+ used: false,
38
+ meaningful: false
39
+ };
40
+ fontVarUsage.set(varName, {
41
+ used: true,
42
+ meaningful: existing.meaningful || meaningful
43
+ });
44
+ }
45
+ if (meaningful) value.split(",").map((font) => font.trim()).map((font) => {
46
+ if (font.startsWith("\"") && font.endsWith("\"") || font.startsWith("'") && font.endsWith("'")) return font.slice(1, -1);
47
+ if (font.startsWith("var(") || [
48
+ "serif",
49
+ "sans-serif",
50
+ "monospace",
51
+ "cursive",
52
+ "fantasy",
53
+ "system-ui",
54
+ "ui-monospace",
55
+ "ui-serif",
56
+ "ui-sans-serif",
57
+ "ui-rounded"
58
+ ].includes(font.toLowerCase())) return null;
59
+ return font;
60
+ }).filter(Boolean).forEach((name) => usedFontFamilies.add(name));
61
+ });
62
+ const fontVarDefinitions = /* @__PURE__ */ new Map();
63
+ root.walkDecls((decl) => {
64
+ if (decl.prop.startsWith("--uds-font-") && !decl.prop.includes("size") && !decl.prop.includes("weight") && !decl.prop.includes("slant") && !decl.prop.includes("width")) {
65
+ const varName = decl.prop.replace("--uds-font-", "");
66
+ fontVarDefinitions.set(varName, decl.value);
67
+ }
68
+ });
69
+ const resolveToFontNames = (value, visited = /* @__PURE__ */ new Set()) => {
70
+ const fontNames = [];
71
+ const varMatch = value.match(/var\(--uds-font-([^),]+)\)/);
72
+ if (varMatch) {
73
+ const nestedVarName = varMatch[1];
74
+ if (!visited.has(nestedVarName)) {
75
+ visited.add(nestedVarName);
76
+ const nestedValue = fontVarDefinitions.get(nestedVarName);
77
+ if (nestedValue) {
78
+ const nestedUsage = fontVarUsage.get(nestedVarName);
79
+ if (nestedUsage) {
80
+ if (fontVarUsage.get(value.replace(/var\(--uds-font-([^),]+)\).*/, "$1"))?.meaningful) nestedUsage.meaningful = true;
81
+ }
82
+ fontNames.push(...resolveToFontNames(nestedValue, visited));
83
+ }
84
+ }
85
+ }
86
+ value.split(",").map((font) => font.trim()).forEach((font) => {
87
+ if (font.startsWith("var(")) return;
88
+ if (font.startsWith("\"") && font.endsWith("\"") || font.startsWith("'") && font.endsWith("'")) font = font.slice(1, -1);
89
+ if ([
90
+ "serif",
91
+ "sans-serif",
92
+ "monospace",
93
+ "cursive",
94
+ "fantasy",
95
+ "system-ui",
96
+ "ui-monospace",
97
+ "ui-serif",
98
+ "ui-sans-serif",
99
+ "ui-rounded"
100
+ ].includes(font.toLowerCase())) return;
101
+ if (font) fontNames.push(font);
102
+ });
103
+ return fontNames;
104
+ };
105
+ for (const [varName, value] of fontVarDefinitions) {
106
+ const usage = fontVarUsage.get(varName);
107
+ if (usage?.used && usage?.meaningful) resolveToFontNames(value).forEach((name) => usedFontFamilies.add(name));
108
+ }
109
+ const keptFonts = /* @__PURE__ */ new Set();
110
+ root.walkAtRules("font-face", (atRule) => {
111
+ let fontFamily = null;
112
+ atRule.walkDecls("font-family", (decl) => {
113
+ fontFamily = decl.value.replace(/["']/g, "").trim();
114
+ });
115
+ if (fontFamily && !usedFontFamilies.has(fontFamily)) atRule.remove();
116
+ else if (fontFamily) keptFonts.add(fontFamily);
117
+ });
118
+ root.walkDecls((decl) => {
119
+ if (decl.prop.startsWith("--uds-font-") && !decl.prop.includes("size") && !decl.prop.includes("weight") && !decl.prop.includes("slant") && !decl.prop.includes("width")) {
120
+ const fonts = decl.value.split(",").map((f) => f.trim());
121
+ const filteredFonts = fonts.filter((font) => {
122
+ if (font.startsWith("var(")) return true;
123
+ let fontName = font;
124
+ if (font.startsWith("\"") && font.endsWith("\"") || font.startsWith("'") && font.endsWith("'")) fontName = font.slice(1, -1);
125
+ if ([
126
+ "serif",
127
+ "sans-serif",
128
+ "monospace",
129
+ "cursive",
130
+ "fantasy",
131
+ "system-ui",
132
+ "ui-monospace",
133
+ "ui-serif",
134
+ "ui-sans-serif",
135
+ "ui-rounded"
136
+ ].includes(fontName.toLowerCase())) return true;
137
+ return keptFonts.has(fontName);
138
+ });
139
+ if (filteredFonts.length < fonts.length && filteredFonts.length > 0) decl.value = filteredFonts.join(", ");
140
+ }
141
+ });
142
+ }
143
+ };
144
+ }
145
+ removeUnusedFontFaces.postcss = true;
146
+ /**
147
+ * PostCSS plugin to remove redundant .uds-color-mode-light rules.
148
+ * Light mode is the default (set on :root), so the explicit class is unnecessary.
149
+ */
150
+ function removeRedundantLightModePlugin() {
151
+ return {
152
+ postcssPlugin: "remove-redundant-light-mode",
153
+ Once(root) {
154
+ root.walkRules((rule) => {
155
+ const selectors = rule.selector.split(",").map((s) => s.trim());
156
+ const filteredSelectors = selectors.filter((s) => {
157
+ const normalized = s.replace(/\s+/g, " ").trim();
158
+ return normalized !== ".uds-color-mode-light" && normalized !== ":root .uds-color-mode-light" && normalized !== ":where(.uds-color-mode-light)" && normalized !== ":is(.uds-color-mode-light)";
159
+ });
160
+ if (filteredSelectors.length === 0) rule.remove();
161
+ else if (filteredSelectors.length < selectors.length) rule.selector = filteredSelectors.join(", ");
162
+ });
163
+ }
164
+ };
165
+ }
166
+ removeRedundantLightModePlugin.postcss = true;
167
+ /**
168
+ * PostCSS plugin to remove empty CSS rules (rules with no declarations).
169
+ * Also aggregates duplicate selectors.
170
+ */
171
+ function removeEmptyRulesPlugin() {
172
+ return {
173
+ postcssPlugin: "remove-empty-rules",
174
+ Once(root) {
175
+ root.walkRules((rule) => {
176
+ if (!rule.nodes?.some((node) => node.type === "decl" || node.type === "rule" && node.nodes?.length > 0)) rule.remove();
177
+ });
178
+ root.walkAtRules((atRule) => {
179
+ if (atRule.name !== "font-face" && atRule.name !== "keyframes") {
180
+ if (!atRule.nodes?.some((node) => {
181
+ if (node.type === "rule") return node.nodes?.some((n) => n.type === "decl");
182
+ if (node.type === "decl") return true;
183
+ return false;
184
+ }) && atRule.nodes?.length === 0) atRule.remove();
185
+ }
186
+ });
187
+ }
188
+ };
189
+ }
190
+ removeEmptyRulesPlugin.postcss = true;
191
+ /**
192
+ * PostCSS plugin to aggregate rules with identical selectors.
193
+ */
194
+ function aggregateDuplicateSelectorsPlugin() {
195
+ return {
196
+ postcssPlugin: "aggregate-duplicate-selectors",
197
+ Once(root) {
198
+ const selectorMap = /* @__PURE__ */ new Map();
199
+ const rulesToProcess = [];
200
+ root.walkRules((rule) => {
201
+ if (rule.parent?.type === "root" || rule.parent?.type === "atrule") rulesToProcess.push(rule);
202
+ });
203
+ for (const rule of rulesToProcess) {
204
+ const selector = rule.selector;
205
+ const parent = rule.parent;
206
+ const key = `${parent?.type === "atrule" ? `@${parent.name}:${parent.params}` : "root"}|${selector}`;
207
+ if (selectorMap.has(key)) {
208
+ const existingRule = selectorMap.get(key);
209
+ rule.walkDecls((decl) => {
210
+ const existingDecl = existingRule.nodes?.find((node) => node.type === "decl" && node.prop === decl.prop);
211
+ if (existingDecl) existingDecl.value = decl.value;
212
+ else existingRule.append(decl.clone());
213
+ });
214
+ rule.remove();
215
+ } else selectorMap.set(key, rule);
216
+ }
217
+ }
218
+ };
219
+ }
220
+ aggregateDuplicateSelectorsPlugin.postcss = true;
221
+ /**
222
+ * Validates CSS syntax and returns any errors found.
223
+ */
224
+ function validateCSS(css) {
225
+ const errors = [];
226
+ try {
227
+ const result = postcss.default.parse(css);
228
+ result.walkRules((rule) => {
229
+ if (!rule.selector.trim()) errors.push(`Empty selector found at line ${rule.source?.start?.line}`);
230
+ try {
231
+ if (rule.selector.includes("{{") || rule.selector.includes("}}")) errors.push(`Malformed selector "${rule.selector}" at line ${rule.source?.start?.line}`);
232
+ } catch {
233
+ errors.push(`Invalid selector "${rule.selector}" at line ${rule.source?.start?.line}`);
234
+ }
235
+ });
236
+ result.walkDecls((decl) => {
237
+ if (!decl.value.trim()) errors.push(`Empty value for property "${decl.prop}" at line ${decl.source?.start?.line}`);
238
+ });
239
+ return {
240
+ valid: errors.length === 0,
241
+ errors
242
+ };
243
+ } catch (error) {
244
+ if (error instanceof Error) errors.push(`CSS Parse Error: ${error.message}`);
245
+ else errors.push("Unknown CSS parse error");
246
+ return {
247
+ valid: false,
248
+ errors
249
+ };
250
+ }
251
+ }
252
+ /**
253
+ * Create a fingerprint for a @font-face rule
254
+ */
255
+ function getFontFaceFingerprint(atRule) {
256
+ let fontFamily = "";
257
+ let src = "";
258
+ let fontWeight = "normal";
259
+ let fontStyle = "normal";
260
+ atRule.walkDecls((decl) => {
261
+ if (decl.prop === "font-family") fontFamily = decl.value.replace(/["']/g, "").trim();
262
+ else if (decl.prop === "src") src = decl.value;
263
+ else if (decl.prop === "font-weight") fontWeight = decl.value;
264
+ else if (decl.prop === "font-style") fontStyle = decl.value;
265
+ });
266
+ return JSON.stringify({
267
+ fontFamily,
268
+ src,
269
+ fontWeight,
270
+ fontStyle
271
+ });
272
+ }
273
+ /**
274
+ * Create a fingerprint for a @keyframes rule
275
+ */
276
+ function getKeyframesFingerprint(atRule) {
277
+ const name = atRule.params;
278
+ const content = atRule.toString().replace(/\s+/g, " ").trim();
279
+ return JSON.stringify({
280
+ name,
281
+ content
282
+ });
283
+ }
284
+ /**
285
+ * Normalize a selector by removing scope prefix and whitespace
286
+ */
287
+ function normalizeSelector(selector, scopeClass) {
288
+ let normalized = selector.trim();
289
+ if (scopeClass) {
290
+ const escapedScope = scopeClass.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
291
+ normalized = normalized.replace(new RegExp(`${escapedScope}\\s+`, "g"), "").replace(new RegExp(`${escapedScope}\\.`, "g"), ".").replace(new RegExp(`${escapedScope}`, "g"), "");
292
+ }
293
+ return normalized.replace(/\s+/g, " ").trim();
294
+ }
295
+ /**
296
+ * Create a fingerprint for a CSS rule (selector + declarations)
297
+ */
298
+ function getRuleFingerprint(rule, scopeClass) {
299
+ const selector = normalizeSelector(rule.selector, scopeClass);
300
+ const declarations = [];
301
+ rule.walkDecls((decl) => {
302
+ declarations.push(`${decl.prop}:${decl.value}`);
303
+ });
304
+ declarations.sort();
305
+ return JSON.stringify({
306
+ selector,
307
+ declarations
308
+ });
309
+ }
310
+ /**
311
+ * Extract reference data from CSS for deduplication
312
+ */
313
+ function extractCSSReferenceData(css, scopeClass) {
314
+ const data = {
315
+ fontFaces: /* @__PURE__ */ new Set(),
316
+ keyframes: /* @__PURE__ */ new Set(),
317
+ rules: /* @__PURE__ */ new Set()
318
+ };
319
+ try {
320
+ const root = postcss.default.parse(css);
321
+ root.walkAtRules((atRule) => {
322
+ if (atRule.name === "font-face") data.fontFaces.add(getFontFaceFingerprint(atRule));
323
+ else if (atRule.name === "keyframes" || atRule.name === "-webkit-keyframes") data.keyframes.add(getKeyframesFingerprint(atRule));
324
+ });
325
+ root.walkRules((rule) => {
326
+ if (rule.parent?.type === "atrule") {
327
+ const parentName = rule.parent.name;
328
+ if (parentName === "font-face" || parentName === "keyframes" || parentName === "-webkit-keyframes") return;
329
+ }
330
+ data.rules.add(getRuleFingerprint(rule, scopeClass));
331
+ });
332
+ } catch {}
333
+ return data;
334
+ }
335
+ /**
336
+ * PostCSS plugin to remove CSS that exists in reference CSS
337
+ * Handles @font-face, @keyframes, and regular rule deduplication
338
+ */
339
+ function removeDuplicateCss(referenceData, scopeClass) {
340
+ return {
341
+ postcssPlugin: "remove-duplicate-css",
342
+ Once(root) {
343
+ const toRemove = [];
344
+ root.walkAtRules((atRule) => {
345
+ if (atRule.name === "font-face") {
346
+ const fingerprint = getFontFaceFingerprint(atRule);
347
+ if (referenceData.fontFaces.has(fingerprint)) toRemove.push(atRule);
348
+ } else if (atRule.name === "keyframes" || atRule.name === "-webkit-keyframes") {
349
+ const fingerprint = getKeyframesFingerprint(atRule);
350
+ if (referenceData.keyframes.has(fingerprint)) toRemove.push(atRule);
351
+ }
352
+ });
353
+ root.walkRules((rule) => {
354
+ if (rule.parent?.type === "atrule") {
355
+ const parentName = rule.parent.name;
356
+ if (parentName === "font-face" || parentName === "keyframes" || parentName === "-webkit-keyframes") return;
357
+ }
358
+ const fingerprint = getRuleFingerprint(rule, scopeClass);
359
+ if (referenceData.rules.has(fingerprint)) toRemove.push(rule);
360
+ });
361
+ for (const node of toRemove) node.remove();
362
+ }
363
+ };
364
+ }
365
+ /**
366
+ * Optimizes CSS by:
367
+ * 1. Removing unused @font-face declarations
368
+ * 2. Removing empty rules
369
+ * 3. Aggregating duplicate selectors
370
+ *
371
+ * Returns the optimized CSS and validation results.
372
+ */
373
+ async function optimizeCSS(css, options) {
374
+ const originalSize = Buffer.byteLength(css, "utf8");
375
+ const originalSizeGzip = (0, node_zlib.gzipSync)(css).length;
376
+ const fontFacesBefore = (css.match(/@font-face/g) || []).length;
377
+ const rulesBefore = (css.match(/\{[^}]*\}/g) || []).length;
378
+ const plugins = [];
379
+ if (options?.removeUnusedFonts !== false) plugins.push(removeUnusedFontFaces());
380
+ plugins.push(removeRedundantLightModePlugin());
381
+ if (options?.referenceCss) {
382
+ const referenceData = extractCSSReferenceData(options.referenceCss);
383
+ if (referenceData.fontFaces.size > 0 || referenceData.keyframes.size > 0 || referenceData.rules.size > 0) plugins.push(removeDuplicateCss(referenceData, options.scopeClass));
384
+ }
385
+ if (options?.removeEmptyRules !== false) plugins.push(removeEmptyRulesPlugin());
386
+ if (options?.aggregateDuplicateSelectors !== false) plugins.push(aggregateDuplicateSelectorsPlugin());
387
+ const optimizedCSS = (plugins.length > 0 ? await (0, postcss.default)(plugins).process(css, { from: void 0 }) : { css }).css;
388
+ const optimizedSize = Buffer.byteLength(optimizedCSS, "utf8");
389
+ const fontFacesAfter = (optimizedCSS.match(/@font-face/g) || []).length;
390
+ const rulesAfter = (optimizedCSS.match(/\{[^}]*\}/g) || []).length;
391
+ return {
392
+ css: optimizedCSS,
393
+ validation: validateCSS(optimizedCSS),
394
+ stats: {
395
+ originalSize,
396
+ originalSizeGzip,
397
+ optimizedSize,
398
+ fontFacesRemoved: fontFacesBefore - fontFacesAfter,
399
+ emptyRulesRemoved: Math.max(0, rulesBefore - rulesAfter)
400
+ }
401
+ };
402
+ }
403
+
404
+ //#endregion
405
+ exports.optimizeCSS = optimizeCSS;