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