@csszyx/unplugin 0.12.0 → 0.14.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.
Files changed (46) hide show
  1. package/README.md +8 -10
  2. package/dist/index.cjs +14 -7
  3. package/dist/index.d.cts +4 -4
  4. package/dist/index.d.mts +4 -4
  5. package/dist/index.mjs +3 -3
  6. package/dist/next-config.cjs +3 -0
  7. package/dist/next-config.d.cts +13 -2
  8. package/dist/next-config.d.mts +13 -2
  9. package/dist/next-config.mjs +3 -0
  10. package/dist/next-prebuild.cjs +13 -7
  11. package/dist/next-prebuild.d.cts +8 -2
  12. package/dist/next-prebuild.d.mts +8 -2
  13. package/dist/next-prebuild.mjs +13 -7
  14. package/dist/next-turbo-loader.cjs +31 -15
  15. package/dist/next-turbo-loader.d.cts +11 -2
  16. package/dist/next-turbo-loader.d.mts +11 -2
  17. package/dist/next-turbo-loader.mjs +30 -14
  18. package/dist/next-watcher.cjs +1 -1
  19. package/dist/next-watcher.mjs +1 -1
  20. package/dist/shared/{unplugin.CXV7fOa2.d.cts → unplugin.B32Exn-K.d.cts} +2 -2
  21. package/dist/shared/{unplugin.CXV7fOa2.d.mts → unplugin.B32Exn-K.d.mts} +2 -2
  22. package/dist/shared/{unplugin.Bb5TeU9B.cjs → unplugin.BJiWWF6w.cjs} +93 -23
  23. package/dist/shared/unplugin.BKGCkMgM.cjs +523 -0
  24. package/dist/shared/{unplugin.DMcbmP01.mjs → unplugin.BU0O4IkX.mjs} +4 -1
  25. package/dist/shared/unplugin.BcsvXjIV.mjs +579 -0
  26. package/dist/shared/{unplugin.CtnKJhAi.d.cts → unplugin.BsjD3mtb.d.cts} +165 -33
  27. package/dist/shared/{unplugin.CtnKJhAi.d.mts → unplugin.BsjD3mtb.d.mts} +165 -33
  28. package/dist/shared/{unplugin.DblMogcN.cjs → unplugin.Cji6O5jv.cjs} +4 -1
  29. package/dist/shared/unplugin.CkNQjA4G.cjs +613 -0
  30. package/dist/shared/unplugin.DKcwO8O4.mjs +490 -0
  31. package/dist/shared/{unplugin.DXgxFHzO.mjs → unplugin.DjRylBC_.mjs} +77 -25
  32. package/dist/shared/{unplugin.BK3XVHe8.cjs → unplugin.ZMa9YE1H.cjs} +621 -615
  33. package/dist/shared/{unplugin.B9vpjOhD.mjs → unplugin.gR_h2DVG.mjs} +585 -582
  34. package/dist/vite.cjs +3 -3
  35. package/dist/vite.d.cts +2 -2
  36. package/dist/vite.d.mts +1 -1
  37. package/dist/vite.mjs +3 -3
  38. package/dist/webpack.cjs +3 -3
  39. package/dist/webpack.d.cts +2 -2
  40. package/dist/webpack.d.mts +1 -1
  41. package/dist/webpack.mjs +3 -3
  42. package/package.json +9 -9
  43. package/dist/shared/unplugin.C2lHQFii.cjs +0 -114
  44. package/dist/shared/unplugin.CBMJufQ8.mjs +0 -108
  45. package/dist/shared/unplugin.CDqY7kmk.mjs +0 -224
  46. package/dist/shared/unplugin.DbZ7tCfN.cjs +0 -248
@@ -6,16 +6,16 @@ import * as path from 'node:path';
6
6
  import { dirname } from 'node:path';
7
7
  import { performance } from 'node:perf_hooks';
8
8
  import { fileURLToPath } from 'node:url';
9
- import { scanGlobalVarUsages, extractSzvRegistryEntries, sortStrings as sortStrings$1, szFallbackConsequenceOf, ensureRustTransformAvailable, transformSourceCode, transformRust, transformOxc, ASTBudgetExceededError, transformRustBatch, transform, isRustTransformAvailable } from '@csszyx/compiler';
9
+ import { scanGlobalVarUsages, sortStrings as sortStrings$1, szFallbackConsequenceOf, isWasmTransformAvailable, isRustTransformAvailable, ensureRustTransformAvailable, transformRust, transformWasm, ASTBudgetExceededError, transformRustBatch, transform } from '@csszyx/compiler';
10
10
  import { encode, compute_mangle_checksum } from '@csszyx/core';
11
11
  import { getNativePackageName } from '@csszyx/core/native';
12
12
  import { preprocess as preprocess$1 } from '@csszyx/svelte-adapter';
13
- import { isTailwindReservedCustomProperty, CSSZYX_GLOBAL_ALIAS_PREFIX, isCsszyxGlobalAliasCustomProperty, DEFAULT_BUILD_CONFIG, validateGlobalVarMangleConfig } from '@csszyx/types';
13
+ import { isTailwindReservedCustomProperty, CSSZYX_GLOBAL_ALIAS_PREFIX, isCsszyxGlobalAliasCustomProperty, DEFAULT_IMPORTED_STATIC_SZ, DEFAULT_BUILD_CONFIG, validateGlobalVarMangleConfig } from '@csszyx/types';
14
14
  import { preprocess } from '@csszyx/vue-adapter';
15
15
  import { createUnplugin } from 'unplugin';
16
- import { n as normalizePathSeparators, r as resolveTransformCacheDir, c as createTransformCacheKey, w as writeTransformCache, e as evictOldTransformCacheEntries, a as readTransformCache, d as evictMemoryCacheToBudget, b as babelFallbackReason } from './unplugin.CDqY7kmk.mjs';
16
+ import { n as normalizePathSeparators, c as collectSpecifierAliases, g as recordSzvRegistryFile, h as recordSzObjectRegistryFile, j as resolveCrossModuleStaticsFor, r as resolveTransformCacheDir, e as createTransformCacheKey, w as writeTransformCache, k as evictOldTransformCacheEntries, m as mayExportSzvFactories, i as importedSpecifiersIn, s as specifierBases, a as resolveProviderPathWith, f as readTransformCache, l as evictMemoryCacheToBudget, o as resolveProviderPath, d as isReadableProviderFile } from './unplugin.DKcwO8O4.mjs';
17
17
  import { mangleCSSSync } from '../css-mangler.mjs';
18
- import { a as importsRuntimeHelper, f as findRuntimeImportClause, r as runtimeHelperGroupsFromUsage, b as insertAfterUseDirective } from './unplugin.CBMJufQ8.mjs';
18
+ import { c as createThemeGroupsModule, e as ensureThemeGroupsFile, R as RESOLVED_VIRTUAL_MODULE_ID, b as createMangleMapModule, d as RESOLVED_VIRTUAL_CHECKSUM_ID, f as createChecksumModule, g as RESOLVED_MANGLE_RUNTIME_VIRTUAL_ID, j as createMangleRuntimeModule, k as RESOLVED_THEME_GROUPS_VIRTUAL_ID, l as isVirtualModule, r as resolveVirtualModule, p as parseThemeBlocks, m as mergeThemes, n as discoverProjectTheme, C as CHECKSUM_PLACEHOLDER, M as MANGLE_MAP_PLACEHOLDER, V as VAR_MANGLE_MAP_PLACEHOLDER, o as importsRuntimeHelper, q as findRuntimeImportClause, T as THEME_GROUPS_VIRTUAL_ID, u as THEME_GROUPS_FILE_MARKER, t as themeGroupsSpecifier, v as MANGLE_RUNTIME_VIRTUAL_ID, w as runtimeHelperGroupsFromUsage, a as insertAfterUseDirective } from './unplugin.BcsvXjIV.mjs';
19
19
  import { s as sortStrings, a as replaceEveryLiteral, u as unicodeEscape, b as escapeDoubleQuotedString, c as escapeSingleQuotedString, e as escapeHtmlAttribute, r as renderTailwindScannerCandidates } from './unplugin.B3XoSRB8.mjs';
20
20
  import { gzipSync } from 'node:zlib';
21
21
  import postcss from 'postcss';
@@ -292,7 +292,7 @@ function validateCandidates(candidates, definitions, reserved, aliasPrefix) {
292
292
  code: "registered-property",
293
293
  severity: "error",
294
294
  name,
295
- message: `Registered custom property ${name} is not aliasable in Phase H v1.`,
295
+ message: `Registered custom property ${name} cannot be aliased: @property gives it a type and inheritance behaviour that an alias would not carry.`,
296
296
  location: registeredDefinition
297
297
  });
298
298
  }
@@ -1112,237 +1112,6 @@ function maskBlockComment(code, start) {
1112
1112
  return { end, value };
1113
1113
  }
1114
1114
 
1115
- const EMPTY_THEME = {
1116
- colors: [],
1117
- spacings: [],
1118
- fonts: [],
1119
- textSizes: [],
1120
- fontWeights: [],
1121
- radii: [],
1122
- shadows: [],
1123
- breakpoints: []
1124
- };
1125
- function findMatchingBrace(source, openBrace) {
1126
- let depth = 0;
1127
- for (let index = openBrace; index < source.length; index++) {
1128
- if (source[index] === "{") {
1129
- depth++;
1130
- } else if (source[index] === "}") {
1131
- depth--;
1132
- if (depth === 0) {
1133
- return index;
1134
- }
1135
- }
1136
- }
1137
- return -1;
1138
- }
1139
- function stripLayerWrappers(css) {
1140
- let result = "";
1141
- let i = 0;
1142
- while (i < css.length) {
1143
- const layerIdx = css.indexOf("@layer", i);
1144
- if (layerIdx === -1) {
1145
- result += css.slice(i);
1146
- break;
1147
- }
1148
- result += css.slice(i, layerIdx);
1149
- const openBrace = css.indexOf("{", layerIdx);
1150
- if (openBrace === -1) {
1151
- result += css.slice(layerIdx);
1152
- break;
1153
- }
1154
- const closeBrace = findMatchingBrace(css, openBrace);
1155
- if (closeBrace === -1) {
1156
- result += css.slice(openBrace);
1157
- break;
1158
- }
1159
- result += css.slice(openBrace + 1, closeBrace);
1160
- i = closeBrace + 1;
1161
- }
1162
- return result;
1163
- }
1164
- function themePreludeStart(css, at) {
1165
- const cursor = at + "@theme".length;
1166
- const next = css[cursor];
1167
- const validBoundary = next === "{" || next === " " || next === " " || next === "\n" || next === "\r";
1168
- return validBoundary ? cursor : null;
1169
- }
1170
- function readThemeBlock(css, start) {
1171
- for (let cursor = start; cursor < css.length; cursor++) {
1172
- const character = css[cursor];
1173
- if (character === "{") {
1174
- const close = findMatchingBrace(css, cursor);
1175
- return {
1176
- body: close === -1 ? null : css.slice(cursor + 1, close),
1177
- end: close === -1 ? cursor : close
1178
- };
1179
- }
1180
- if (character === ";" || character === "}" || character === "@") {
1181
- return { body: null, end: cursor };
1182
- }
1183
- }
1184
- return { body: null, end: css.length };
1185
- }
1186
- function extractThemeBlocks(css) {
1187
- const blocks = [];
1188
- let searchFrom = 0;
1189
- for (; ; ) {
1190
- const at = css.indexOf("@theme", searchFrom);
1191
- if (at === -1) {
1192
- break;
1193
- }
1194
- const preludeStart = themePreludeStart(css, at);
1195
- if (preludeStart === null) {
1196
- searchFrom = at + "@theme".length;
1197
- continue;
1198
- }
1199
- const match = readThemeBlock(css, preludeStart);
1200
- if (match.body !== null) blocks.push(match.body);
1201
- searchFrom = Math.max(match.end, at + "@theme".length);
1202
- }
1203
- return blocks;
1204
- }
1205
- function categorizeProperty(prop) {
1206
- const categoryMap = [
1207
- ["color-", "colors"],
1208
- ["spacing-", "spacings"],
1209
- // `font-weight-` MUST precede `font-`: startsWith would otherwise route
1210
- // `font-weight-chunky` into font FAMILIES as token "weight-chunky".
1211
- ["font-weight-", "fontWeights"],
1212
- ["font-", "fonts"],
1213
- // `--text-*` defines font-size utilities (text-huge) in Tailwind v4.
1214
- ["text-", "textSizes"],
1215
- ["radius-", "radii"],
1216
- ["shadow-", "shadows"],
1217
- ["breakpoint-", "breakpoints"]
1218
- ];
1219
- for (const [prefix, category] of categoryMap) {
1220
- if (prop.startsWith(prefix)) {
1221
- let token = prop.slice(prefix.length);
1222
- if (category !== "breakpoints") {
1223
- token = token.replace(/-\d+$/, "");
1224
- }
1225
- if (token) {
1226
- return { category, token };
1227
- }
1228
- }
1229
- }
1230
- return null;
1231
- }
1232
- function parseThemeBlocks(cssContent) {
1233
- const result = {
1234
- colors: /* @__PURE__ */ new Set(),
1235
- spacings: /* @__PURE__ */ new Set(),
1236
- fonts: /* @__PURE__ */ new Set(),
1237
- textSizes: /* @__PURE__ */ new Set(),
1238
- fontWeights: /* @__PURE__ */ new Set(),
1239
- radii: /* @__PURE__ */ new Set(),
1240
- shadows: /* @__PURE__ */ new Set(),
1241
- breakpoints: /* @__PURE__ */ new Set()
1242
- };
1243
- const stripped = stripLayerWrappers(cssContent);
1244
- const blocks = extractThemeBlocks(stripped);
1245
- for (const block of blocks) {
1246
- for (const name of scanCustomPropertyNames(block)) {
1247
- const categorized = categorizeProperty(name);
1248
- if (categorized) {
1249
- result[categorized.category].add(categorized.token);
1250
- }
1251
- }
1252
- }
1253
- return {
1254
- colors: sortStrings(result.colors),
1255
- spacings: sortStrings(result.spacings),
1256
- fonts: sortStrings(result.fonts),
1257
- textSizes: sortStrings(result.textSizes),
1258
- fontWeights: sortStrings(result.fontWeights),
1259
- radii: sortStrings(result.radii),
1260
- shadows: sortStrings(result.shadows),
1261
- breakpoints: sortStrings(result.breakpoints)
1262
- };
1263
- }
1264
- function mergeThemes(themes) {
1265
- if (themes.length === 0) {
1266
- return { ...EMPTY_THEME };
1267
- }
1268
- const merged = {
1269
- colors: /* @__PURE__ */ new Set(),
1270
- spacings: /* @__PURE__ */ new Set(),
1271
- fonts: /* @__PURE__ */ new Set(),
1272
- textSizes: /* @__PURE__ */ new Set(),
1273
- fontWeights: /* @__PURE__ */ new Set(),
1274
- radii: /* @__PURE__ */ new Set(),
1275
- shadows: /* @__PURE__ */ new Set(),
1276
- breakpoints: /* @__PURE__ */ new Set()
1277
- };
1278
- for (const theme of themes) {
1279
- for (const cat of Object.keys(merged)) {
1280
- for (const token of theme[cat]) {
1281
- merged[cat].add(token);
1282
- }
1283
- }
1284
- }
1285
- return {
1286
- colors: sortStrings(merged.colors),
1287
- spacings: sortStrings(merged.spacings),
1288
- fonts: sortStrings(merged.fonts),
1289
- textSizes: sortStrings(merged.textSizes),
1290
- fontWeights: sortStrings(merged.fontWeights),
1291
- radii: sortStrings(merged.radii),
1292
- shadows: sortStrings(merged.shadows),
1293
- breakpoints: sortStrings(merged.breakpoints)
1294
- };
1295
- }
1296
- function hasTokens(theme) {
1297
- return Object.values(theme).some((arr) => arr.length > 0);
1298
- }
1299
- function readCustomPropertyName(block, dashes) {
1300
- let end = dashes + 2;
1301
- if (end >= block.length || !/[a-z]/.test(block[end])) {
1302
- return null;
1303
- }
1304
- end++;
1305
- while (end < block.length && /[a-z0-9-]/.test(block[end])) {
1306
- end++;
1307
- }
1308
- return { name: block.slice(dashes + 2, end), end };
1309
- }
1310
- function findCustomPropertyDeclarationEnd(block, nameEnd) {
1311
- let cursor = nameEnd;
1312
- while (cursor < block.length && /\s/.test(block[cursor])) {
1313
- cursor++;
1314
- }
1315
- if (block[cursor] === ":") {
1316
- const valueStart = cursor + 1;
1317
- const semicolon = block.indexOf(";", valueStart);
1318
- return semicolon > valueStart ? semicolon + 1 : -1;
1319
- }
1320
- return block[nameEnd] === ";" ? nameEnd + 1 : -1;
1321
- }
1322
- function scanCustomPropertyNames(block) {
1323
- const names = [];
1324
- let i = 0;
1325
- while (i < block.length) {
1326
- const dashes = block.indexOf("--", i);
1327
- if (dashes === -1) {
1328
- break;
1329
- }
1330
- const property = readCustomPropertyName(block, dashes);
1331
- if (!property) {
1332
- i = dashes + 1;
1333
- continue;
1334
- }
1335
- const matchEnd = findCustomPropertyDeclarationEnd(block, property.end);
1336
- if (matchEnd === -1) {
1337
- i = dashes + 1;
1338
- continue;
1339
- }
1340
- names.push(property.name);
1341
- i = matchEnd;
1342
- }
1343
- return names;
1344
- }
1345
-
1346
1115
  const REGEX_PREFIXES = new Set("([{:,;=!?&|+-*%^~<>/");
1347
1116
  const REGEX_PREFIX_KEYWORDS = /* @__PURE__ */ new Set([
1348
1117
  "await",
@@ -1712,6 +1481,78 @@ function collectAuthoredClassNames(source) {
1712
1481
  return classes;
1713
1482
  }
1714
1483
 
1484
+ const KNOWN_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
1485
+ "include",
1486
+ "exclude",
1487
+ "compileSources",
1488
+ "contentScopeCheck",
1489
+ "quiet",
1490
+ "mode",
1491
+ "development",
1492
+ "production",
1493
+ "build",
1494
+ "hydration"
1495
+ ]);
1496
+ const RENAMED_KEYS = {
1497
+ // Replaced by the path-based form: package NAMES could not express a
1498
+ // sibling directory outside the project root, and resolving them meant
1499
+ // guessing at workspace layout.
1500
+ compilePackages: "compileSources"
1501
+ };
1502
+ function nearestKnownConfigKey(key) {
1503
+ const lower = key.toLowerCase();
1504
+ let best = null;
1505
+ let bestDistance = Number.POSITIVE_INFINITY;
1506
+ for (const candidate of KNOWN_TOP_LEVEL_KEYS) {
1507
+ const distance = editDistance(lower, candidate.toLowerCase());
1508
+ if (distance < bestDistance) {
1509
+ bestDistance = distance;
1510
+ best = candidate;
1511
+ }
1512
+ }
1513
+ return best !== null && bestDistance <= Math.max(1, Math.floor(key.length / 4)) ? best : null;
1514
+ }
1515
+ function editDistance(a, b) {
1516
+ let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
1517
+ for (let i = 1; i <= a.length; i += 1) {
1518
+ const current = [i];
1519
+ for (let j = 1; j <= b.length; j += 1) {
1520
+ const substitution = previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1);
1521
+ current.push(Math.min(substitution, previous[j] + 1, current[j - 1] + 1));
1522
+ }
1523
+ previous = current;
1524
+ }
1525
+ return previous[b.length];
1526
+ }
1527
+ function findUnknownConfigKeys(options) {
1528
+ if (options === null || typeof options !== "object" || Array.isArray(options)) {
1529
+ return [];
1530
+ }
1531
+ const unknown = [];
1532
+ for (const key of Object.keys(options)) {
1533
+ if (KNOWN_TOP_LEVEL_KEYS.has(key)) continue;
1534
+ const renamedTo = RENAMED_KEYS[key];
1535
+ if (renamedTo !== void 0) {
1536
+ unknown.push({ key, renamedTo });
1537
+ continue;
1538
+ }
1539
+ const suggestion = nearestKnownConfigKey(key);
1540
+ unknown.push(suggestion === null ? { key } : { key, suggestion });
1541
+ }
1542
+ return unknown;
1543
+ }
1544
+ function unknownConfigKeysMessage(unknown) {
1545
+ const lines = unknown.map((entry) => {
1546
+ if (entry.renamedTo !== void 0) {
1547
+ return ` - \`${entry.key}\` was replaced by \`${entry.renamedTo}\``;
1548
+ }
1549
+ return entry.suggestion === void 0 ? ` - \`${entry.key}\`` : ` - \`${entry.key}\` \u2014 did you mean \`${entry.suggestion}\`?`;
1550
+ });
1551
+ return `[csszyx] ${unknown.length} plugin option(s) are not recognized and have no effect:
1552
+ ${lines.join("\n")}
1553
+ An unread option is silent, so whatever you configured it for is not happening.`;
1554
+ }
1555
+
1715
1556
  const GLOB_MAGIC_RE = /[*?[\]{}]/;
1716
1557
  const DEFAULT_IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", ".turbo", "dist", "build"]);
1717
1558
  function normalizeFileId(id) {
@@ -1982,11 +1823,13 @@ function escapeForDoubleQuotedString(value) {
1982
1823
  }
1983
1824
 
1984
1825
  function createMangleSizeAccount() {
1985
- return { cssGzBefore: 0, cssGzAfter: 0, channels: /* @__PURE__ */ new Set() };
1826
+ return { cssGzBefore: 0, cssGzAfter: 0, codeGzBefore: 0, codeGzAfter: 0, channels: /* @__PURE__ */ new Set() };
1986
1827
  }
1987
1828
  function resetMangleSizeAccount(account) {
1988
1829
  account.cssGzBefore = 0;
1989
1830
  account.cssGzAfter = 0;
1831
+ account.codeGzBefore = 0;
1832
+ account.codeGzAfter = 0;
1990
1833
  account.channels.clear();
1991
1834
  }
1992
1835
  function gzipBytes(text) {
@@ -2003,30 +1846,53 @@ function recordCssPair(account, before, after) {
2003
1846
  account.cssGzBefore += gzipBytes(before);
2004
1847
  account.cssGzAfter += gzipBytes(after);
2005
1848
  }
1849
+ function recordCodePair(account, before, after) {
1850
+ if (before === after) {
1851
+ const bytes = gzipBytes(before);
1852
+ account.codeGzBefore += bytes;
1853
+ account.codeGzAfter += bytes;
1854
+ return;
1855
+ }
1856
+ account.codeGzBefore += gzipBytes(before);
1857
+ account.codeGzAfter += gzipBytes(after);
1858
+ }
2006
1859
  function computeMangleSizeVerdict(account, mapPayload) {
2007
1860
  const channels = sortStrings(account.channels);
2008
1861
  const mapCost = gzipBytes(mapPayload) * channels.length;
2009
1862
  const cssSaving = account.cssGzBefore - account.cssGzAfter;
2010
- return { mapCost, cssSaving, net: mapCost - cssSaving, channels };
1863
+ const codeSaving = account.codeGzBefore - account.codeGzAfter;
1864
+ return { mapCost, cssSaving, codeSaving, net: mapCost - cssSaving - codeSaving, channels };
2011
1865
  }
2012
1866
  function mangleSizeMessage(verdict) {
2013
1867
  if (verdict.channels.length === 0) return null;
2014
1868
  if (verdict.net <= 0) return null;
2015
1869
  const cssPart = verdict.cssSaving >= 0 ? `the mangled CSS saves ${verdict.cssSaving} B` : `the mangled CSS COSTS ${-verdict.cssSaving} B (short tokens compress worse than the names they replaced)`;
1870
+ const codePart = verdict.codeSaving >= 0 ? `the shortened classes in code save ${verdict.codeSaving} B` : `the shortened classes in code COST ${-verdict.codeSaving} B`;
2016
1871
  const channelPart = verdict.channels.length > 1 ? `${verdict.channels.join(" + ")} (${verdict.channels.length} copies)` : verdict.channels[0];
2017
- return `[csszyx] production.mangle is making this build BIGGER: +${verdict.net} B gzipped. The runtime mangle map costs ${verdict.mapCost} B via ${channelPart}, while ${cssPart}. Mangling is a name-obfuscation feature; over a compressed response it does not reduce payload, because utility class names compress far better than the map they need. If you enabled it for size, set \`production.mangle: false\`. If you enabled it to hide class names, this is the expected price and you can ignore this. Narrowing \`production.mangleMapDelivery\` removes a map copy when only one channel is needed. (Measured on CSS and the map; class shortening inside JS chunks is not counted, so the real net is slightly better than this figure.)`;
1872
+ return `[csszyx] production.mangle is making this build BIGGER: +${verdict.net} B gzipped. The runtime mangle map costs ${verdict.mapCost} B via ${channelPart}, while ${cssPart} and ${codePart}. Mangling is a name-obfuscation feature; over a compressed response it does not reduce payload, because utility class names compress far better than the map they need. If you enabled it for size, set \`production.mangle: false\`. If you enabled it to hide class names, this is the expected price and you can ignore this. Narrowing \`production.mangleMapDelivery\` removes a map copy when only one channel is needed.`;
2018
1873
  }
2019
1874
 
2020
1875
  function isParserMode(value) {
2021
- return value === "rust" || value === "oxc" || value === "babel";
1876
+ return value === "rust" || value === "wasm";
2022
1877
  }
2023
1878
  function resolveParserMode(input) {
2024
1879
  const { configParser, envParser, defaultParser, isRustAvailable } = input;
2025
1880
  const envValid = isParserMode(envParser);
2026
- const explicit = envValid || configParser != null;
2027
- const parser = envValid ? envParser : configParser ?? defaultParser;
1881
+ const configValid = isParserMode(configParser);
1882
+ const explicit = envValid || configValid;
1883
+ let parser;
1884
+ if (envValid) {
1885
+ parser = envParser;
1886
+ } else if (configValid) {
1887
+ parser = configParser;
1888
+ } else {
1889
+ parser = defaultParser;
1890
+ }
2028
1891
  if (parser === "rust" && !explicit && !isRustAvailable()) {
2029
- return { parser: "oxc", degraded: true, explicit: false };
1892
+ if (input.isWasmAvailable()) {
1893
+ return { parser: "wasm", degraded: true, explicit: false };
1894
+ }
1895
+ return { parser: "rust", degraded: false, explicit: false };
2030
1896
  }
2031
1897
  return { parser, degraded: false, explicit };
2032
1898
  }
@@ -2061,41 +1927,6 @@ function isSameFileVersion(before, after) {
2061
1927
  return before.dev === after.dev && before.ino === after.ino && before.size === after.size && before.mtimeNs === after.mtimeNs && before.ctimeNs === after.ctimeNs;
2062
1928
  }
2063
1929
 
2064
- function recordSzvRegistryFile(registry, filePath, content) {
2065
- const key = normalizePathSeparators(filePath);
2066
- const qualifies = content.includes("szv(") && content.includes("export");
2067
- const entries = qualifies ? extractSzvRegistryEntries(content, filePath) : [];
2068
- if (entries.length === 0) {
2069
- registry.delete(key);
2070
- return;
2071
- }
2072
- const byName = {};
2073
- for (const entry of entries) {
2074
- byName[entry.exportName] = entry.config;
2075
- }
2076
- registry.set(key, byName);
2077
- }
2078
- const SPECIFIER_PROBES = ["", ".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx"];
2079
- function resolveCrossModuleStaticsFor(registry, filename, source) {
2080
- if (registry.size === 0 || !source.includes("from")) return void 0;
2081
- const directory = path.dirname(filename);
2082
- let resolved;
2083
- for (const match of source.matchAll(/from\s*['"](\.[^'"]*)['"]/g)) {
2084
- const specifier = match[1];
2085
- if (resolved?.[specifier] !== void 0) continue;
2086
- const base = normalizePathSeparators(path.resolve(directory, specifier));
2087
- for (const probe of SPECIFIER_PROBES) {
2088
- const entries = registry.get(`${base}${probe}`);
2089
- if (entries !== void 0) {
2090
- resolved ??= {};
2091
- resolved[specifier] = entries;
2092
- break;
2093
- }
2094
- }
2095
- }
2096
- return resolved;
2097
- }
2098
-
2099
1930
  function generateThemeDts(opts) {
2100
1931
  const { theme, sourceFiles } = opts;
2101
1932
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
@@ -2167,127 +1998,6 @@ function writeThemeDts(opts) {
2167
1998
  writeFileSync(opts.outputPath, content, "utf-8");
2168
1999
  }
2169
2000
 
2170
- const VIRTUAL_MODULE_ID = "virtual:csszyx/mangle-map";
2171
- const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
2172
- const THEME_GROUPS_VIRTUAL_ID = "virtual:csszyx/theme-groups";
2173
- const RESOLVED_THEME_GROUPS_VIRTUAL_ID = `\0${THEME_GROUPS_VIRTUAL_ID}`;
2174
- const VIRTUAL_CHECKSUM_ID = "virtual:csszyx/checksum";
2175
- const RESOLVED_VIRTUAL_CHECKSUM_ID = `\0${VIRTUAL_CHECKSUM_ID}`;
2176
- function createMangleMapModule(mangleMap, checksum, varMangleMap = {}, cssVarMetrics = null) {
2177
- return `/**
2178
- * Auto-generated mangle map for csszyx.
2179
- * This module is generated at build time and contains the mapping
2180
- * from original class names and CSS variable names to mangled names.
2181
- *
2182
- * @generated
2183
- */
2184
-
2185
- export const mangleMap = ${JSON.stringify(mangleMap, null, 2)};
2186
-
2187
- export const varMangleMap = ${JSON.stringify(varMangleMap, null, 2)};
2188
-
2189
- export const cssVarMetrics = ${JSON.stringify(cssVarMetrics, null, 2)};
2190
-
2191
- export const checksum = ${JSON.stringify(checksum)};
2192
-
2193
- export default {
2194
- mangleMap,
2195
- varMangleMap,
2196
- cssVarMetrics,
2197
- checksum,
2198
- };
2199
- `;
2200
- }
2201
- function createChecksumModule(checksum) {
2202
- return `/**
2203
- * Auto-generated checksum for csszyx mangle map.
2204
- *
2205
- * @generated
2206
- */
2207
-
2208
- export const checksum = ${JSON.stringify(checksum)};
2209
-
2210
- export default checksum;
2211
- `;
2212
- }
2213
- function isVirtualModule(id) {
2214
- return id === VIRTUAL_MODULE_ID || id === VIRTUAL_CHECKSUM_ID || id === THEME_GROUPS_VIRTUAL_ID || id === MANGLE_RUNTIME_VIRTUAL_ID;
2215
- }
2216
- function resolveVirtualModule(id) {
2217
- if (id === VIRTUAL_MODULE_ID) {
2218
- return RESOLVED_VIRTUAL_MODULE_ID;
2219
- }
2220
- if (id === VIRTUAL_CHECKSUM_ID) {
2221
- return RESOLVED_VIRTUAL_CHECKSUM_ID;
2222
- }
2223
- if (id === THEME_GROUPS_VIRTUAL_ID) {
2224
- return RESOLVED_THEME_GROUPS_VIRTUAL_ID;
2225
- }
2226
- if (id === MANGLE_RUNTIME_VIRTUAL_ID) {
2227
- return RESOLVED_MANGLE_RUNTIME_VIRTUAL_ID;
2228
- }
2229
- return void 0;
2230
- }
2231
- const MANGLE_RUNTIME_VIRTUAL_ID = "virtual:csszyx/mangle-runtime";
2232
- const RESOLVED_MANGLE_RUNTIME_VIRTUAL_ID = `\0${MANGLE_RUNTIME_VIRTUAL_ID}`;
2233
- const MANGLE_MAP_PLACEHOLDER = "___CSSZYX_MANGLE_MAP___";
2234
- const VAR_MANGLE_MAP_PLACEHOLDER = "___CSSZYX_VAR_MANGLE_MAP___";
2235
- const CHECKSUM_PLACEHOLDER = "___CSSZYX_CHECKSUM___";
2236
- function createMangleRuntimeModule(globalVarAliasPrefix) {
2237
- return `/**
2238
- * Auto-generated by csszyx: installs the runtime mangle map from the bundle.
2239
- *
2240
- * @generated
2241
- */
2242
-
2243
- const m = ${MANGLE_MAP_PLACEHOLDER};
2244
- const vm = ${VAR_MANGLE_MAP_PLACEHOLDER};
2245
- const gp = ${JSON.stringify(globalVarAliasPrefix)};
2246
- const checksum = "${CHECKSUM_PLACEHOLDER}";
2247
-
2248
- if (typeof window !== 'undefined' && !window.__csszyx) {
2249
- const r = {};
2250
- const vr = {};
2251
- for (const k in m) r[m[k]] = k;
2252
- for (const vk in vm) {
2253
- const vv = vm[vk];
2254
- const vs = Array.isArray(vv) ? vv : [vv];
2255
- for (const v of vs) (vr[v] || (vr[v] = [])).push(vk);
2256
- }
2257
- window.__csszyx = {
2258
- mangleMap: m,
2259
- varMangleMap: vm,
2260
- checksum,
2261
- decode: (c) => r[c],
2262
- encode: (c) => m[c],
2263
- decodeVar: (v) => vr[v] || [],
2264
- encodeVar: (v) => vm[v],
2265
- decodeGlobalVar: (v) => {
2266
- const a = vr[v] || [];
2267
- return v.indexOf(gp) === 0 ? a[0] : undefined;
2268
- },
2269
- decodeAll: (el) => (el.className || '').split(' ').map((c) => r[c] || c),
2270
- };
2271
- }
2272
-
2273
- export {};
2274
- `;
2275
- }
2276
- function createThemeGroupsModule(tokens) {
2277
- const payload = JSON.stringify({
2278
- colors: tokens.colors,
2279
- textSizes: tokens.textSizes,
2280
- fontFamilies: tokens.fontFamilies,
2281
- fontWeights: tokens.fontWeights
2282
- });
2283
- return [
2284
- "// Auto-generated by csszyx from the @theme blocks in scanned CSS.",
2285
- "import { registerSzcnGroups } from '@csszyx/runtime';",
2286
- `registerSzcnGroups(${payload});`,
2287
- "export {};"
2288
- ].join("\n");
2289
- }
2290
-
2291
2001
  function registerWebpackAssetProcessor(compiler, processAssets) {
2292
2002
  compiler.hooks.compilation.tap("csszyx:post", (compilation) => {
2293
2003
  const stage = compiler.webpack?.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE || compilation.constructor.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE;
@@ -2329,7 +2039,6 @@ let _hasWarnedTsConfig = false;
2329
2039
  let _hasWarnedTransformCacheVersion = false;
2330
2040
  let _hasWarnedNativeFallback = false;
2331
2041
  const _loggedActiveParsers = /* @__PURE__ */ new Set();
2332
- const _babelFallbackFiles = /* @__PURE__ */ new Set();
2333
2042
  const requireFromHere = createRequire(import.meta.url);
2334
2043
  const PLUGIN_VERSION = findPackageVersionFromFile(
2335
2044
  fileURLToPath(import.meta.url),
@@ -2446,9 +2155,20 @@ function mangleHybridHazardMessage(hazards) {
2446
2155
  }
2447
2156
  return parts.join("");
2448
2157
  }
2158
+ const TAILWIND_IMPORT_SPECIFIER = /@import\s+["']tailwindcss(?:\/[^"']*)?["']/g;
2159
+ const SOURCE_MODIFIER = /\bsource\(/;
2160
+ function tailwindImportScopesContent(css) {
2161
+ for (const match of css.matchAll(TAILWIND_IMPORT_SPECIFIER)) {
2162
+ const modifiersStart = match.index + match[0].length;
2163
+ const terminator = css.indexOf(";", modifiersStart);
2164
+ const modifiers = terminator === -1 ? css.slice(modifiersStart) : css.slice(modifiersStart, terminator);
2165
+ if (SOURCE_MODIFIER.test(modifiers)) return true;
2166
+ }
2167
+ return false;
2168
+ }
2449
2169
  function cssHasContentScope(code) {
2450
2170
  const s = stripCssBlockComments(code);
2451
- return /@import\s+["']tailwindcss(?:\/[^"']*)?["']\s+source\(/.test(s) || /@source\s+not\b/.test(s);
2171
+ return tailwindImportScopesContent(s) || /@source\s+not\b/.test(s);
2452
2172
  }
2453
2173
  function isMonorepoPackage(root) {
2454
2174
  let dir = path.dirname(path.resolve(root));
@@ -2474,19 +2194,33 @@ function shouldWarnUnscopedMonorepo(sawTailwindEntry, tailwindEntryScoped, inMon
2474
2194
  return sawTailwindEntry && !tailwindEntryScoped && inMonorepo;
2475
2195
  }
2476
2196
  function unscopedMonorepoMessage() {
2477
- return '[csszyx] Tailwind content detection is UNSCOPED in a monorepo. Tailwind v4 climbs to the workspace root and scans sibling packages + docs (.md/.mdx/.txt are not ignored), which can generate phantom or broken url() classes and fail the build. Scope it in your Tailwind CSS entry:\n @import "tailwindcss" source(none);\n @source "."; /* this package, relative to the CSS file */\ncsszyx auto-injects @source for its generated classes, so only your own templates need listing. Guide: https://csszyx.com/docs/monorepo-content-scope/\nSilence (if a broad scan is intentional): csszyx({ contentScopeCheck: false }).';
2197
+ return '[csszyx] Tailwind content detection is UNSCOPED in a monorepo. Tailwind v4 scans every file under its detection base \u2014 .md/.mdx/.txt included \u2014 so a doc or fixture holding class-shaped strings becomes CSS, which can generate phantom or broken url() classes and fail the build. That base is as wide as the build makes it: a Vite build roots it at the Vite root, other setups at the workspace root. Scope it in your Tailwind CSS entry:\n @import "tailwindcss" source(none);\n @source ".";\nThat @source is your package, relative to the CSS file; csszyx auto-injects one for the classes it generates, so only your own templates need listing. Guide: https://csszyx.com/docs/monorepo-content-scope/\nSilence (if a broad scan is intentional): csszyx({ contentScopeCheck: false }).';
2198
+ }
2199
+ function isAdvisoryDiagnostic(message) {
2200
+ return !(message.includes("unresolvable sz spread") || message.includes("AST budget exceeded") || szFallbackConsequenceOf(message) === "missing-css");
2201
+ }
2202
+ function suppressedAdvisoryMessage(count) {
2203
+ if (count <= 0) return null;
2204
+ const held = count === 1 ? "1 advisory sz fallback" : `${count} advisory sz fallbacks`;
2205
+ return `[csszyx] ${held} not listed above. At an sz prop a fallback is advisory \u2014 the runtime path works and the classes are collected \u2014 so a production build keeps the list short. A development build prints each one with its file and position.`;
2206
+ }
2207
+ function resolveQuietMode(quiet) {
2208
+ if (quiet === true || quiet === "all") return "all";
2209
+ if (quiet === "nudges") return "nudges";
2210
+ return "off";
2478
2211
  }
2479
2212
  function shouldEmitWarning(quiet, devOnly, isProduction) {
2480
- if (quiet) {
2213
+ const mode = resolveQuietMode(quiet);
2214
+ if (mode === "all") {
2481
2215
  return false;
2482
2216
  }
2483
- if (devOnly && isProduction) {
2217
+ if (devOnly && (isProduction || mode === "nudges")) {
2484
2218
  return false;
2485
2219
  }
2486
2220
  return true;
2487
2221
  }
2488
2222
  function shouldEmitMissingCssFallback(quiet, message) {
2489
- return !quiet && szFallbackConsequenceOf(message) === "missing-css";
2223
+ return resolveQuietMode(quiet) !== "all" && szFallbackConsequenceOf(message) === "missing-css";
2490
2224
  }
2491
2225
  function emitMissingCssFallback(quiet, message, id, emit) {
2492
2226
  if (shouldEmitMissingCssFallback(quiet, message)) emit(`[csszyx] ${id}
@@ -2596,11 +2330,29 @@ function isPackagesSkippedSource(id, sourceDirs = []) {
2596
2330
  }
2597
2331
  return !isCompileSourceOptedIn(p, sourceDirs);
2598
2332
  }
2599
- function skippedSzFilesMessage(files) {
2333
+ function skippedSzFilesMessage(files, szvExportFiles = []) {
2600
2334
  const list = files.map((file) => ` - ${file}`).join("\n");
2601
- return `[csszyx] ${files.length} file(s) under packages/ contain \`sz\` but were skipped by ignore rules:
2335
+ const registryClause = szvExportFiles.length === 0 ? "" : `
2336
+ ${szvExportFiles.length} of them may export \`szv\` factories, so they stay out of the cross-module registry and every importer falls back to the runtime path.`;
2337
+ return `[csszyx] ${files.length} file(s) under packages/ use csszyx but were skipped by ignore rules:
2602
2338
  ${list}
2603
- Add the package directory to \`compileSources\` (or move the file out of packages/) \u2014 otherwise their \`sz\` produces no CSS.`;
2339
+ Add the package directory to \`compileSources\` (or move the file out of packages/) \u2014 otherwise their classes never reach the safelist.` + registryClause;
2340
+ }
2341
+ function lateMangleCensusMessage(lateOwned, lateAuthored) {
2342
+ const sections = [
2343
+ lateOwned.length > 0 ? `csszyx-owned classes: ${lateOwned.join(", ")}` : "",
2344
+ lateAuthored.length > 0 ? `author classes: ${lateAuthored.join(", ")}` : ""
2345
+ ].filter(Boolean);
2346
+ const found = sections.length > 0 ? sections.join("\n") : "the class census changed";
2347
+ return `[csszyx] production.mangle: classes appeared after the mangle map was frozen, so the emitted CSS was hashed against a different map:
2348
+ ${found}
2349
+ The prescan walks the project root and every \`compileSources\` directory. Move the file that owns these classes under one of them, or turn off \`production.mangle\` for this build.`;
2350
+ }
2351
+ function watchModeMangleMessage() {
2352
+ return "[csszyx] production.mangle is disabled for this watch build. Mangling needs the whole-project prescan that only runs once per process, and a watch rebuild cannot repeat it \u2014 the map would go stale against the files you edit. Run a one-shot build to get mangled output.";
2353
+ }
2354
+ function realContentHashDisabledMessage() {
2355
+ return "[csszyx] optimization.realContentHash is off while production.mangle is on. csszyx mangles assets after webpack computes their content hashes, and realContentHash is what recomputes those hashes from the final bytes. Without it, two builds that differ only by mangling emit the same filenames with different contents and caches keep serving the old file.";
2604
2356
  }
2605
2357
  function computeSafelistRelPath(rootDir, safelistFilename, cssId) {
2606
2358
  const safelistPath = normalizePathSeparators(path.join(rootDir, safelistFilename));
@@ -2677,6 +2429,20 @@ function collectConfiguredGlobalVarCssSources(rootDir, scanCss) {
2677
2429
  }
2678
2430
  });
2679
2431
  }
2432
+ const MANGLEABLE_CSS_ID_RE = /\.(?:css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/;
2433
+ const NON_STYLESHEET_CSS_QUERY_RE = /[?&](?:worker|sharedworker|raw|url)(?:&|=|$)/;
2434
+ const COMMONJS_PROXY_RE = /\?commonjs-proxy/;
2435
+ function isMangleableCssId(id) {
2436
+ return MANGLEABLE_CSS_ID_RE.test(id) && !NON_STYLESHEET_CSS_QUERY_RE.test(id) && !COMMONJS_PROXY_RE.test(id);
2437
+ }
2438
+ function dedupeCssAssetsByName(assets) {
2439
+ const seen = /* @__PURE__ */ new Set();
2440
+ return assets.filter((asset) => {
2441
+ if (seen.has(asset.fileName)) return false;
2442
+ seen.add(asset.fileName);
2443
+ return true;
2444
+ });
2445
+ }
2680
2446
  function collectWebpackGlobalVarCssAssets(assets) {
2681
2447
  return Object.entries(assets).flatMap(([fileName, asset]) => {
2682
2448
  if (!/\.css(?:$|\?)/.test(fileName)) {
@@ -3241,12 +3007,12 @@ ${errors.join("\n")}`
3241
3007
  if (config?.enabled === true) {
3242
3008
  if (!config.tokens || config.tokens.length === 0) {
3243
3009
  throw new Error(
3244
- "[csszyx] production.mangleGlobalVars.enabled requires explicit tokens in Phase H v1."
3010
+ "[csszyx] production.mangleGlobalVars.enabled requires explicit tokens. Aliasing a property csszyx was not told about would rename references it cannot see."
3245
3011
  );
3246
3012
  }
3247
3013
  if (config.autoPrefix !== void 0 && config.autoPrefix !== "") {
3248
3014
  throw new Error(
3249
- "[csszyx] production.mangleGlobalVars.autoPrefix requires CSS pre-scan support and is not enabled in Phase H v1."
3015
+ "[csszyx] production.mangleGlobalVars.autoPrefix is not available: choosing tokens by prefix needs a CSS pre-scan that does not exist yet. List the tokens instead."
3250
3016
  );
3251
3017
  }
3252
3018
  }
@@ -3254,9 +3020,18 @@ ${errors.join("\n")}`
3254
3020
  function createCsszyxPlugins(options = {}) {
3255
3021
  assertGlobalVarMangleConfig(options);
3256
3022
  let manglingEnabled = options.production?.mangle === true;
3257
- let crossModuleRegistryEnabled = process.env.NODE_ENV !== "development";
3023
+ const importedStaticSzEnabled = options.build?.importedStaticSz ?? DEFAULT_IMPORTED_STATIC_SZ;
3258
3024
  const szvCrossModuleRegistry = /* @__PURE__ */ new Map();
3025
+ const szObjectProvidersExamined = /* @__PURE__ */ new Set();
3026
+ let specifierAliases = [];
3259
3027
  const mangleReserved = new Set(options.production?.mangleExclude ?? []);
3028
+ let mangleMapFrozen = false;
3029
+ let frozenOwnedClasses = /* @__PURE__ */ new Set();
3030
+ let frozenAuthoredClasses = /* @__PURE__ */ new Set();
3031
+ let cssRewrittenInTransform = false;
3032
+ let projectCssFiles = [];
3033
+ const transformMangledSources = /* @__PURE__ */ new Set();
3034
+ const transformExternalClasses = /* @__PURE__ */ new Set();
3260
3035
  const mangleMapDelivery = options.production?.mangleMapDelivery ?? "both";
3261
3036
  if (!["both", "html", "bundle"].includes(mangleMapDelivery)) {
3262
3037
  throw new Error(
@@ -3287,7 +3062,11 @@ function createCsszyxPlugins(options = {}) {
3287
3062
  );
3288
3063
  }
3289
3064
  const compileSources = options.compileSources ?? [];
3290
- const quiet = options.quiet === true;
3065
+ const quiet = resolveQuietMode(options.quiet);
3066
+ const unknownConfigKeys = findUnknownConfigKeys(options);
3067
+ if (unknownConfigKeys.length > 0) {
3068
+ emitWarning(unknownConfigKeysMessage(unknownConfigKeys));
3069
+ }
3291
3070
  function emitWarning(message, opts = {}) {
3292
3071
  if (shouldEmitWarning(quiet, opts.devOnly ?? false, process.env.NODE_ENV === "production")) {
3293
3072
  console.warn(message);
@@ -3297,22 +3076,27 @@ function createCsszyxPlugins(options = {}) {
3297
3076
  configParser: options.build?.parser,
3298
3077
  envParser: process.env.CSSZYX_PARSER,
3299
3078
  defaultParser: DEFAULT_BUILD_CONFIG.parser ?? "rust",
3300
- isRustAvailable: isRustTransformAvailable
3079
+ isRustAvailable: isRustTransformAvailable,
3080
+ isWasmAvailable: isWasmTransformAvailable
3301
3081
  });
3302
3082
  function announceActiveParser() {
3303
3083
  if (parserDegraded && !_hasWarnedNativeFallback) {
3304
3084
  _hasWarnedNativeFallback = true;
3305
- console.warn(
3306
- "[csszyx] No prebuilt native binary (@csszyx/core-*) is available for this platform, so the default `rust` parser fell back to `oxc`. Output classes are identical (parity-tested); only parse speed differs. To use the native engine, install the matching @csszyx/core-<platform> package (or do not omit optional dependencies). Set `build.parser` explicitly to silence this."
3307
- );
3085
+ if (parserMode === "wasm") {
3086
+ console.warn(
3087
+ "[csszyx] No prebuilt native binary (@csszyx/core-*) is available for this platform, so the default `rust` parser fell back to its wasm build. Same engine, same output; only parse speed differs. To use the native engine, install the matching @csszyx/core-<platform> package (or do not omit optional dependencies). Set `build.parser` explicitly to silence this."
3088
+ );
3089
+ }
3308
3090
  }
3309
3091
  if (!_loggedActiveParsers.has(parserMode)) {
3310
3092
  _loggedActiveParsers.add(parserMode);
3311
3093
  let detail = parserMode;
3312
3094
  if (parserDegraded) {
3313
- detail = "oxc (degraded from default `rust`: no native binary for this platform)";
3095
+ detail = "wasm (degraded from default `rust`: same engine, wasm build)";
3314
3096
  } else if (parserMode === "rust") {
3315
3097
  detail = "rust (native engine)";
3098
+ } else if (parserMode === "wasm") {
3099
+ detail = "wasm (wasm build of the native engine)";
3316
3100
  }
3317
3101
  console.warn(`[csszyx] active parser: ${detail}`);
3318
3102
  }
@@ -3324,6 +3108,7 @@ function createCsszyxPlugins(options = {}) {
3324
3108
  const state = {
3325
3109
  classes: /* @__PURE__ */ new Set(),
3326
3110
  parsedTheme: null,
3111
+ scanCssTheme: null,
3327
3112
  autoThemeCssFiles: [],
3328
3113
  sawTailwindEntry: false,
3329
3114
  sawAnyCss: false,
@@ -3331,7 +3116,9 @@ function createCsszyxPlugins(options = {}) {
3331
3116
  tailwindEntryScoped: false,
3332
3117
  contentScopeWarningEmitted: false,
3333
3118
  spreadWarnings: /* @__PURE__ */ new Set(),
3119
+ suppressedAdvisories: 0,
3334
3120
  skippedSzFiles: /* @__PURE__ */ new Set(),
3121
+ skippedSzvExportFiles: /* @__PURE__ */ new Set(),
3335
3122
  skipWarningEmitted: false,
3336
3123
  classesCapped: false,
3337
3124
  ownedClasses: /* @__PURE__ */ new Set(),
@@ -3391,7 +3178,7 @@ function createCsszyxPlugins(options = {}) {
3391
3178
  const result = validateGlobalVarAliasInputs(
3392
3179
  createGlobalVarAliasValidationOptions({
3393
3180
  rootDir: state.rootDir,
3394
- cssAssets: [...configuredCssAssets, ...cssAssets],
3181
+ cssAssets: dedupeCssAssetsByName([...configuredCssAssets, ...cssAssets]),
3395
3182
  sourceFiles: buildGlobalVarSourceFiles(state),
3396
3183
  tokens: globalVarMangleConfig.tokens,
3397
3184
  autoPrefix: globalVarMangleConfig.autoPrefix,
@@ -3404,6 +3191,25 @@ function createCsszyxPlugins(options = {}) {
3404
3191
  assertGlobalVarPlanMatchesEarlyAliases(result, earlyGlobalVarAliasEntries);
3405
3192
  return result;
3406
3193
  }
3194
+ function revalidateFrozenGlobalVarPlan() {
3195
+ if (!cssRewrittenInTransform) return;
3196
+ validateGlobalVarBundleInputs(collectProjectGlobalVarCssSources());
3197
+ }
3198
+ function collectProjectGlobalVarCssSources() {
3199
+ const sources = [];
3200
+ for (const file of sortStrings$1(projectCssFiles)) {
3201
+ try {
3202
+ const snapshot = readStableTextFileSnapshotSync(file);
3203
+ sources.push({
3204
+ fileName: file,
3205
+ source: snapshot.source,
3206
+ mtimeMs: snapshot.mtimeMs
3207
+ });
3208
+ } catch {
3209
+ }
3210
+ }
3211
+ return sources;
3212
+ }
3407
3213
  function trackGlobalVarSourceFile(filename, code) {
3408
3214
  if (!globalVarSourceTrackingEnabled) {
3409
3215
  return;
@@ -3426,56 +3232,15 @@ function createCsszyxPlugins(options = {}) {
3426
3232
  }
3427
3233
  }
3428
3234
  function runAutoThemeScan(rootDir) {
3429
- if (options.build?.scanCss) {
3430
- return;
3431
- }
3432
3235
  refreshCompileSourceDirs();
3433
- const cssFiles = [];
3434
- const walk = (dir) => {
3435
- let entries;
3436
- try {
3437
- entries = fs.readdirSync(dir, { withFileTypes: true });
3438
- } catch {
3439
- return;
3440
- }
3441
- for (const entry of entries) {
3442
- if (entry.isDirectory()) {
3443
- if (!IGNORE_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
3444
- walk(path.join(dir, entry.name));
3445
- }
3446
- continue;
3447
- }
3448
- if (entry.name.endsWith(".css")) {
3449
- cssFiles.push(path.join(dir, entry.name));
3450
- }
3451
- }
3452
- };
3453
- walk(rootDir);
3454
- const normRoot = normalizeForMatch(rootDir);
3455
- for (const sourceDir of compileSourceDirs) {
3456
- if (sourceDir === normRoot || sourceDir.startsWith(`${normRoot}/`)) {
3457
- continue;
3458
- }
3459
- walk(sourceDir);
3460
- }
3461
- const themes = [];
3462
- const themeFiles = [];
3463
- for (const file of cssFiles) {
3464
- let content;
3465
- try {
3466
- content = fs.readFileSync(file, "utf-8");
3467
- } catch {
3468
- continue;
3469
- }
3470
- if (!content.includes("@theme")) {
3471
- continue;
3472
- }
3473
- themes.push(parseThemeBlocks(content));
3474
- themeFiles.push(file);
3475
- }
3476
- state.autoThemeCssFiles = themeFiles;
3477
- if (themes.length > 0) {
3478
- state.parsedTheme = mergeThemes(themes);
3236
+ const discovered = discoverProjectTheme(rootDir, [...compileSourceDirs]);
3237
+ state.autoThemeCssFiles = discovered.files;
3238
+ projectCssFiles = discovered.scanned;
3239
+ const sources = [state.scanCssTheme, discovered.theme].filter(
3240
+ (theme) => theme !== null
3241
+ );
3242
+ if (sources.length > 0) {
3243
+ state.parsedTheme = mergeThemes(sources);
3479
3244
  }
3480
3245
  }
3481
3246
  function isHardIgnored(id) {
@@ -3494,10 +3259,14 @@ function createCsszyxPlugins(options = {}) {
3494
3259
  const crossModuleStatics = resolveCrossModuleStaticsFor(
3495
3260
  szvCrossModuleRegistry,
3496
3261
  filename,
3497
- source
3262
+ source,
3263
+ specifierAliases
3498
3264
  );
3499
- if (crossModuleStatics !== void 0) {
3500
- compilerOptions.crossModuleStatics = crossModuleStatics;
3265
+ if (crossModuleStatics.szvConfigs !== void 0) {
3266
+ compilerOptions.crossModuleStatics = crossModuleStatics.szvConfigs;
3267
+ }
3268
+ if (crossModuleStatics.szObjects !== void 0) {
3269
+ compilerOptions.crossModuleSzObjects = crossModuleStatics.szObjects;
3501
3270
  }
3502
3271
  const cacheRoot = resolveTransformCacheDir(state.rootDir, options.build?.cacheDir);
3503
3272
  if (cacheEnabled) {
@@ -3548,43 +3317,16 @@ function createCsszyxPlugins(options = {}) {
3548
3317
  return handoff.inputSha256 === cacheKey.inputSha256 ? handoff.result : null;
3549
3318
  }
3550
3319
  function runConfiguredParser(source, effectiveFilename, compilerOptions) {
3551
- if (parserMode === "babel") {
3552
- return {
3553
- result: transformSourceCode(source, effectiveFilename, compilerOptions),
3554
- cacheable: true
3555
- };
3556
- }
3557
3320
  if (parserMode === "rust") {
3558
3321
  return {
3559
3322
  result: transformRust(source, effectiveFilename, compilerOptions),
3560
3323
  cacheable: true
3561
3324
  };
3562
3325
  }
3563
- try {
3564
- return {
3565
- result: transformOxc(source, effectiveFilename, compilerOptions),
3566
- cacheable: true
3567
- };
3568
- } catch (error) {
3569
- return {
3570
- result: runBabelFallback(source, effectiveFilename, compilerOptions, error),
3571
- cacheable: false
3572
- };
3573
- }
3574
- }
3575
- function runBabelFallback(source, effectiveFilename, compilerOptions, error) {
3576
- const result = transformSourceCode(source, effectiveFilename, compilerOptions);
3577
- const reason = babelFallbackReason(error);
3578
- result.diagnostics.push(
3579
- `[csszyx] oxc parser fell back to Babel for ${effectiveFilename}: ${reason}`
3580
- );
3581
- if (!_babelFallbackFiles.has(effectiveFilename)) {
3582
- _babelFallbackFiles.add(effectiveFilename);
3583
- console.warn(
3584
- `[csszyx] oxc parser fell back to Babel for ${effectiveFilename}: ${reason} (${_babelFallbackFiles.size} file(s) so far). Output is still correct; this usually means the file uses a syntax the oxc lane does not yet handle.`
3585
- );
3586
- }
3587
- return result;
3326
+ return {
3327
+ result: transformWasm(source, effectiveFilename, compilerOptions),
3328
+ cacheable: true
3329
+ };
3588
3330
  }
3589
3331
  function createCompilerOptions(astBudget = astBudgetOverride) {
3590
3332
  return {
@@ -3614,6 +3356,7 @@ function createCsszyxPlugins(options = {}) {
3614
3356
  // module A's config change must miss B's cached transform, or the
3615
3357
  // cache serves a stale table.
3616
3358
  crossModuleStatics: compilerOptions.crossModuleStatics === void 0 ? void 0 : JSON.stringify(compilerOptions.crossModuleStatics),
3359
+ crossModuleSzObjects: compilerOptions.crossModuleSzObjects === void 0 ? void 0 : JSON.stringify(compilerOptions.crossModuleSzObjects),
3617
3360
  filename: effectiveFilename,
3618
3361
  source
3619
3362
  };
@@ -3622,19 +3365,35 @@ function createCsszyxPlugins(options = {}) {
3622
3365
  if (parserMode !== "rust" || files.length <= 1) {
3623
3366
  return transformPrescanSourcesIndividually(files);
3624
3367
  }
3368
+ const perFile = [];
3369
+ const batchable = [];
3370
+ for (const file of files) {
3371
+ (hasCrossModuleStatics(file) ? perFile : batchable).push(file);
3372
+ }
3373
+ const individual = transformPrescanSourcesIndividually(perFile);
3625
3374
  const compilerOptions = createCompilerOptions(prescanAstBudget);
3626
3375
  const cacheRoot = resolveTransformCacheDir(state.rootDir, options.build?.cacheDir);
3627
3376
  const results = /* @__PURE__ */ new Map();
3628
3377
  if (cacheEnabled) evictTransformCacheOnce();
3629
3378
  ensureRustTransformAvailable();
3630
- const misses = collectRustPrescanMisses(files, compilerOptions, cacheRoot, results);
3631
- if (misses.length === 0) return orderPrescanResults(files, results);
3632
- try {
3633
- runRustPrescanBatch(misses, compilerOptions, cacheRoot, results);
3634
- } catch {
3635
- runRustPrescanFallback(misses, results);
3379
+ const misses = collectRustPrescanMisses(batchable, compilerOptions, cacheRoot, results);
3380
+ if (misses.length > 0) {
3381
+ try {
3382
+ runRustPrescanBatch(misses, compilerOptions, cacheRoot, results);
3383
+ } catch {
3384
+ runRustPrescanFallback(misses, results);
3385
+ }
3636
3386
  }
3637
- return orderPrescanResults(files, results);
3387
+ return [...individual, ...orderPrescanResults(batchable, results)];
3388
+ }
3389
+ function hasCrossModuleStatics(file) {
3390
+ const resolved = resolveCrossModuleStaticsFor(
3391
+ szvCrossModuleRegistry,
3392
+ file.filePath,
3393
+ file.content,
3394
+ specifierAliases
3395
+ );
3396
+ return resolved.szvConfigs !== void 0 || resolved.szObjects !== void 0;
3638
3397
  }
3639
3398
  function collectRustPrescanMisses(files, compilerOptions, cacheRoot, results) {
3640
3399
  const misses = [];
@@ -3782,7 +3541,7 @@ function createCsszyxPlugins(options = {}) {
3782
3541
  }
3783
3542
  }
3784
3543
  function recordPackagesSkipIfSz(filePath) {
3785
- if (!isPackagesSkippedSource(filePath, compileSourceDirs)) {
3544
+ if (state.skippedSzFiles.has(filePath) || !isPackagesSkippedSource(filePath, compileSourceDirs)) {
3786
3545
  return;
3787
3546
  }
3788
3547
  let content;
@@ -3791,8 +3550,12 @@ function createCsszyxPlugins(options = {}) {
3791
3550
  } catch {
3792
3551
  return;
3793
3552
  }
3794
- if (content.includes("sz=") || content.includes("szs=") || content.includes("sz:")) {
3795
- state.skippedSzFiles.add(filePath);
3553
+ if (!fileMayContainSafelistableSz(content)) {
3554
+ return;
3555
+ }
3556
+ state.skippedSzFiles.add(filePath);
3557
+ if (mayExportSzvFactories(content)) {
3558
+ state.skippedSzvExportFiles.add(filePath);
3796
3559
  }
3797
3560
  }
3798
3561
  function processPrescanTransform(filePath, content, result, discoveredClasses, rawDiscoveredClasses) {
@@ -3823,13 +3586,73 @@ function createCsszyxPlugins(options = {}) {
3823
3586
  }
3824
3587
  collectPrescanResult(result, filePath, discoveredClasses, rawDiscoveredClasses);
3825
3588
  }
3589
+ function refreshSzvRegistryEntry(filePath, content) {
3590
+ if (!shouldProcessSource(filePath)) return;
3591
+ if (content === null) {
3592
+ szvCrossModuleRegistry.delete(normalizePathSeparators(filePath));
3593
+ return;
3594
+ }
3595
+ recordSzvRegistryFile(szvCrossModuleRegistry, filePath, content);
3596
+ if (importedStaticSzEnabled) {
3597
+ recordSzObjectRegistryFile(szvCrossModuleRegistry, filePath, content);
3598
+ szObjectProvidersExamined.add(normalizePathSeparators(filePath));
3599
+ recordProvidersDemandedBy(filePath, content);
3600
+ }
3601
+ }
3602
+ function recordProvidersDemandedBy(filePath, content) {
3603
+ if (!fileMayContainSafelistableSz(content)) return;
3604
+ const directory = path.dirname(filePath);
3605
+ for (const specifier of importedSpecifiersIn(content)) {
3606
+ for (const base of specifierBases(specifier, directory, specifierAliases)) {
3607
+ const providerPath = resolveProviderPathWith(base, isReadableProviderFile);
3608
+ if (providerPath === void 0) continue;
3609
+ const key = normalizePathSeparators(providerPath);
3610
+ if (!szObjectProvidersExamined.has(key)) {
3611
+ szObjectProvidersExamined.add(key);
3612
+ readAndRecordSzObjectProvider(providerPath);
3613
+ }
3614
+ break;
3615
+ }
3616
+ }
3617
+ }
3618
+ function readAndRecordSzObjectProvider(providerPath) {
3619
+ let content;
3620
+ try {
3621
+ content = fs.readFileSync(providerPath, "utf-8");
3622
+ } catch {
3623
+ return;
3624
+ }
3625
+ recordSzObjectRegistryFile(szvCrossModuleRegistry, providerPath, content);
3626
+ }
3627
+ function recordDemandedSzObjectProviders(seenSourcePaths, demand) {
3628
+ for (const base of demand) {
3629
+ const providerPath = resolveProviderPath(seenSourcePaths, base);
3630
+ if (providerPath === void 0) continue;
3631
+ szObjectProvidersExamined.add(normalizePathSeparators(providerPath));
3632
+ readAndRecordSzObjectProvider(providerPath);
3633
+ }
3634
+ }
3635
+ function refreshSzvRegistryEntryFromDisk(filePath) {
3636
+ if (!shouldProcessSource(filePath)) return;
3637
+ let content;
3638
+ try {
3639
+ content = fs.readFileSync(filePath, "utf-8");
3640
+ } catch {
3641
+ refreshSzvRegistryEntry(filePath, null);
3642
+ return;
3643
+ }
3644
+ refreshSzvRegistryEntry(filePath, content);
3645
+ }
3826
3646
  function prescanAndWriteClasses() {
3827
3647
  refreshCompileSourceDirs();
3828
3648
  szvCrossModuleRegistry.clear();
3649
+ szObjectProvidersExamined.clear();
3829
3650
  const prescanStarted = performance.now();
3830
3651
  const discoveredClasses = /* @__PURE__ */ new Set();
3831
3652
  const rawDiscoveredClasses = /* @__PURE__ */ new Set();
3832
3653
  const prescanSources = [];
3654
+ const seenSourcePaths = /* @__PURE__ */ new Set();
3655
+ const szObjectDemand = /* @__PURE__ */ new Set();
3833
3656
  function collectPrescanSource(filePath) {
3834
3657
  if (!shouldProcessSource(filePath)) {
3835
3658
  recordPackagesSkipIfSz(filePath);
@@ -3843,12 +3666,22 @@ function createCsszyxPlugins(options = {}) {
3843
3666
  }
3844
3667
  recordAuthoredClasses(content);
3845
3668
  recordSzvRegistryEntries(filePath, content);
3669
+ seenSourcePaths.add(normalizePathSeparators(filePath));
3846
3670
  if (fileMayContainSafelistableSz(content)) {
3847
3671
  prescanSources.push({ filePath, content });
3672
+ recordSzObjectDemand(filePath, content);
3673
+ }
3674
+ }
3675
+ function recordSzObjectDemand(filePath, content) {
3676
+ if (!importedStaticSzEnabled) return;
3677
+ const directory = path.dirname(filePath);
3678
+ for (const specifier of importedSpecifiersIn(content)) {
3679
+ for (const base of specifierBases(specifier, directory, specifierAliases)) {
3680
+ szObjectDemand.add(base);
3681
+ }
3848
3682
  }
3849
3683
  }
3850
3684
  function recordSzvRegistryEntries(filePath, content) {
3851
- if (!crossModuleRegistryEnabled) return;
3852
3685
  recordSzvRegistryFile(szvCrossModuleRegistry, filePath, content);
3853
3686
  }
3854
3687
  function scanDir(dir) {
@@ -3878,6 +3711,7 @@ function createCsszyxPlugins(options = {}) {
3878
3711
  }
3879
3712
  scanDir(sourceDir);
3880
3713
  }
3714
+ recordDemandedSzObjectProviders(seenSourcePaths, szObjectDemand);
3881
3715
  const prescanContentByPath = new Map(
3882
3716
  prescanSources.map((file) => [file.filePath, file.content])
3883
3717
  );
@@ -4008,16 +3842,39 @@ function createCsszyxPlugins(options = {}) {
4008
3842
  state.authoredClasses.add(className);
4009
3843
  }
4010
3844
  }
4011
- function finalizeMangleMap() {
3845
+ function computeMangleMap() {
4012
3846
  const forbiddenTokens = /* @__PURE__ */ new Set([
4013
3847
  ...mangleReserved,
4014
3848
  ...state.authoredClasses,
4015
3849
  ...state.ownedClasses
4016
3850
  ]);
4017
- state.mangleMap = allocateMangleTokens(
3851
+ return allocateMangleTokens(
4018
3852
  mangleEligibleClasses(state.ownedClasses, state.authoredClasses),
4019
3853
  forbiddenTokens
4020
3854
  );
3855
+ }
3856
+ function freezeMangleMap() {
3857
+ state.mangleMap = computeMangleMap();
3858
+ frozenOwnedClasses = new Set(state.ownedClasses);
3859
+ frozenAuthoredClasses = new Set(state.authoredClasses);
3860
+ mangleMapFrozen = true;
3861
+ }
3862
+ function assertMangleCensusUnchanged() {
3863
+ if (!mangleMapFrozen) return;
3864
+ const current = computeMangleMap();
3865
+ if (JSON.stringify(current) === JSON.stringify(state.mangleMap)) return;
3866
+ const lateOwned = sortStrings$1(
3867
+ [...state.ownedClasses].filter((className) => !frozenOwnedClasses.has(className))
3868
+ );
3869
+ const lateAuthored = sortStrings$1(
3870
+ [...state.authoredClasses].filter((className) => !frozenAuthoredClasses.has(className))
3871
+ );
3872
+ throw new Error(lateMangleCensusMessage(lateOwned, lateAuthored));
3873
+ }
3874
+ function finalizeMangleMap() {
3875
+ if (!mangleMapFrozen) {
3876
+ state.mangleMap = computeMangleMap();
3877
+ }
4021
3878
  assertVarMangleMapSize(state.varMangleMap, varMangleMapMaxBytes);
4022
3879
  state.checksum = compute_mangle_checksum(
4023
3880
  createHydrationMangleMap(state.mangleMap, state.varMangleMap)
@@ -4076,7 +3933,8 @@ function createCsszyxPlugins(options = {}) {
4076
3933
  szPartArgsProvable: true,
4077
3934
  usesColorVar: false,
4078
3935
  usesSpacingVar: false,
4079
- usesUnitVar: false
3936
+ usesUnitVar: false,
3937
+ usesBoolClass: false
4080
3938
  };
4081
3939
  }
4082
3940
  function transformTailwindCssEntry(code, id) {
@@ -4103,13 +3961,13 @@ function createCsszyxPlugins(options = {}) {
4103
3961
  }
4104
3962
  emitMissingCssFallback(quiet, message, id, console.warn);
4105
3963
  }
4106
- if (quiet || result.diagnostics.length === 0 || process.env.NODE_ENV === "production") {
3964
+ const advisories = result.diagnostics.filter(isAdvisoryDiagnostic);
3965
+ if (advisories.length === 0) return;
3966
+ if (quiet !== "off" || process.env.NODE_ENV === "production") {
3967
+ state.suppressedAdvisories += advisories.length;
4107
3968
  return;
4108
3969
  }
4109
- for (const message of result.diagnostics) {
4110
- if (message.includes("unresolvable sz spread") || message.includes("AST budget exceeded") || szFallbackConsequenceOf(message) === "missing-css") {
4111
- continue;
4112
- }
3970
+ for (const message of advisories) {
4113
3971
  warn(`[csszyx] ${id}
4114
3972
  ${message}`);
4115
3973
  }
@@ -4128,6 +3986,7 @@ function createCsszyxPlugins(options = {}) {
4128
3986
  usesColorVar: result.usesColorVar,
4129
3987
  usesSpacingVar: result.usesSpacingVar,
4130
3988
  usesUnitVar: result.usesUnitVar,
3989
+ usesBoolClass: result.usesBoolClass,
4131
3990
  szClasses: result.classes
4132
3991
  };
4133
3992
  }
@@ -4196,10 +4055,29 @@ function createCsszyxPlugins(options = {}) {
4196
4055
  `;
4197
4056
  return insertRuntimeImport(current, importStatement);
4198
4057
  }
4058
+ function themeGroupTokens() {
4059
+ const theme = state.parsedTheme;
4060
+ return {
4061
+ colors: theme?.colors ?? [],
4062
+ textSizes: theme?.textSizes ?? [],
4063
+ fontFamilies: theme?.fonts ?? [],
4064
+ fontWeights: theme?.fontWeights ?? []
4065
+ };
4066
+ }
4199
4067
  function injectThemeGroups(code, transformedCode, id, usesSzcn) {
4200
- if (!usesSzcn && !/\bszcn\s*\(/.test(code) || transformedCode.includes(THEME_GROUPS_VIRTUAL_ID) || !shouldProcessSource(id)) {
4068
+ if (!usesSzcn && !/\bszcn\s*\(/.test(code) || transformedCode.includes(THEME_GROUPS_VIRTUAL_ID) || transformedCode.includes(THEME_GROUPS_FILE_MARKER) || !shouldProcessSource(id)) {
4201
4069
  return null;
4202
4070
  }
4071
+ if (activeFramework === "webpack") {
4072
+ const groups = ensureThemeGroupsFile(
4073
+ state.rootDir,
4074
+ path.join(state.rootDir, ".csszyx")
4075
+ );
4076
+ if (groups.file === null) return null;
4077
+ const [from] = id.split("?");
4078
+ return `import '${themeGroupsSpecifier(from, groups.file)}';
4079
+ ${transformedCode}`;
4080
+ }
4203
4081
  return `import '${THEME_GROUPS_VIRTUAL_ID}';
4204
4082
  ${transformedCode}`;
4205
4083
  }
@@ -4283,13 +4161,7 @@ ${transformedCode}`;
4283
4161
  return createMangleRuntimeModule(globalVarAliasPrefix);
4284
4162
  }
4285
4163
  if (id === RESOLVED_THEME_GROUPS_VIRTUAL_ID) {
4286
- const theme = state.parsedTheme;
4287
- return createThemeGroupsModule({
4288
- colors: theme?.colors ?? [],
4289
- textSizes: theme?.textSizes ?? [],
4290
- fontFamilies: theme?.fonts ?? [],
4291
- fontWeights: theme?.fontWeights ?? []
4292
- });
4164
+ return createThemeGroupsModule(themeGroupTokens());
4293
4165
  }
4294
4166
  return null;
4295
4167
  },
@@ -4303,7 +4175,11 @@ ${transformedCode}`;
4303
4175
  if (shouldProcessCss(id)) {
4304
4176
  return true;
4305
4177
  }
4306
- return shouldProcessSource(id);
4178
+ if (shouldProcessSource(id)) {
4179
+ return true;
4180
+ }
4181
+ recordPackagesSkipIfSz(id);
4182
+ return false;
4307
4183
  },
4308
4184
  /**
4309
4185
  * Core transform: detects sz prop, compiles to className, injects runtime, collects classes.
@@ -4368,6 +4244,8 @@ ${transformedCode}`;
4368
4244
  /** Finalizes the mangle map after all source modules have been processed. */
4369
4245
  buildEnd() {
4370
4246
  finalizeMangleMap();
4247
+ assertMangleCensusUnchanged();
4248
+ revalidateFrozenGlobalVarPlan();
4371
4249
  assertNoRSCGraphViolation(state.rscModules);
4372
4250
  if (!state.tailwindWarningEmitted && shouldWarnMissingTailwindEntry(
4373
4251
  state.ownedClasses.size,
@@ -4390,9 +4268,13 @@ ${transformedCode}`;
4390
4268
  }
4391
4269
  if (!state.skipWarningEmitted && state.skippedSzFiles.size > 0) {
4392
4270
  state.skipWarningEmitted = true;
4393
- emitWarning(skippedSzFilesMessage(sortStrings$1(state.skippedSzFiles)), {
4394
- devOnly: true
4395
- });
4271
+ emitWarning(
4272
+ skippedSzFilesMessage(
4273
+ sortStrings$1(state.skippedSzFiles),
4274
+ sortStrings$1(state.skippedSzvExportFiles)
4275
+ ),
4276
+ { devOnly: state.skippedSzvExportFiles.size === 0 }
4277
+ );
4396
4278
  }
4397
4279
  if (state.classesCapped) {
4398
4280
  emitWarning(
@@ -4414,7 +4296,10 @@ ${transformedCode}`;
4414
4296
  recordGlobalVarSourceFile(state, id, null);
4415
4297
  recordFileVarMangleEntries(state, id, []);
4416
4298
  recordFileCSSVariableMetrics(state, id, null);
4299
+ refreshSzvRegistryEntry(id, null);
4300
+ return;
4417
4301
  }
4302
+ refreshSzvRegistryEntryFromDisk(id);
4418
4303
  },
4419
4304
  /**
4420
4305
  * Webpack hook: pre-scans source files before compilation for Tailwind class discovery.
@@ -4422,12 +4307,13 @@ ${transformedCode}`;
4422
4307
  */
4423
4308
  webpack(compiler) {
4424
4309
  activeFramework = "webpack";
4310
+ compiler.hooks?.beforeCompile?.tap?.("csszyx:theme-groups", () => {
4311
+ const root = compiler.context || process.cwd();
4312
+ ensureThemeGroupsFile(root, path.join(root, ".csszyx"));
4313
+ });
4425
4314
  if (compiler.options?.mode === "development") {
4426
4315
  manglingEnabled = false;
4427
4316
  }
4428
- if (compiler.watchMode === true || compiler.options?.watch === true) {
4429
- crossModuleRegistryEnabled = false;
4430
- }
4431
4317
  if (manglingEnabled && mangleMapDelivery !== "both") {
4432
4318
  console.warn(
4433
4319
  `[csszyx] production.mangleMapDelivery: '${mangleMapDelivery}' has no effect on the webpack lane \u2014 map delivery only narrows on vite/rollup builds.`
@@ -4437,11 +4323,21 @@ ${transformedCode}`;
4437
4323
  announceActiveParser();
4438
4324
  const root = compiler.context || process.cwd();
4439
4325
  state.rootDir = root;
4326
+ specifierAliases = collectSpecifierAliases(
4327
+ root,
4328
+ compiler.options?.resolve?.alias
4329
+ );
4440
4330
  evictTransformCacheOnce();
4441
4331
  if (state.classes.size === 0) {
4442
4332
  prescanAndWriteClasses();
4443
4333
  }
4444
- state.parsedTheme = runThemeScan(root, options.build?.scanCss) ?? state.parsedTheme;
4334
+ for (const changed of compiler.modifiedFiles ?? []) {
4335
+ refreshSzvRegistryEntryFromDisk(changed);
4336
+ }
4337
+ for (const removed of compiler.removedFiles ?? []) {
4338
+ refreshSzvRegistryEntry(removed, null);
4339
+ }
4340
+ state.scanCssTheme = runThemeScan(root, options.build?.scanCss) ?? state.scanCssTheme;
4445
4341
  runAutoThemeScan(root);
4446
4342
  });
4447
4343
  if (options.build?.scanCss) {
@@ -4457,10 +4353,6 @@ ${transformedCode}`;
4457
4353
  /** Records the rollup lane for the mangle-runtime gate. */
4458
4354
  buildStart() {
4459
4355
  activeFramework = "rollup";
4460
- const meta = this.meta;
4461
- if (meta?.watchMode) {
4462
- crossModuleRegistryEnabled = false;
4463
- }
4464
4356
  }
4465
4357
  },
4466
4358
  vite: {
@@ -4474,17 +4366,26 @@ ${transformedCode}`;
4474
4366
  announceActiveParser();
4475
4367
  const root = config.root || process.cwd();
4476
4368
  state.rootDir = root;
4369
+ specifierAliases = collectSpecifierAliases(root, config.resolve?.alias);
4477
4370
  if (config.command === "serve") {
4478
4371
  manglingEnabled = false;
4479
- crossModuleRegistryEnabled = false;
4480
4372
  }
4481
- if (config.build?.watch) {
4482
- crossModuleRegistryEnabled = false;
4373
+ if (manglingEnabled && config.build?.watch) {
4374
+ manglingEnabled = false;
4375
+ emitWarning(watchModeMangleMessage());
4483
4376
  }
4484
4377
  evictTransformCacheOnce();
4485
4378
  prescanAndWriteClasses();
4486
- state.parsedTheme = runThemeScan(root, options.build?.scanCss) ?? state.parsedTheme;
4379
+ state.scanCssTheme = runThemeScan(root, options.build?.scanCss) ?? state.scanCssTheme;
4487
4380
  runAutoThemeScan(root);
4381
+ if (config.command === "build") {
4382
+ state.globalVarValidationResult = validateGlobalVarBundleInputs(
4383
+ collectProjectGlobalVarCssSources()
4384
+ );
4385
+ }
4386
+ if (manglingEnabled && config.command === "build") {
4387
+ freezeMangleMap();
4388
+ }
4488
4389
  },
4489
4390
  /**
4490
4391
  * Vite HMR hook: re-runs theme scan when a watched CSS file changes,
@@ -4502,15 +4403,17 @@ ${transformedCode}`;
4502
4403
  ctx.server.moduleGraph.invalidateModule(themeGroupsModule);
4503
4404
  }
4504
4405
  };
4505
- if (scanCss) {
4406
+ if (ctx.file.endsWith(".css")) {
4506
4407
  const root = ctx.server.config.root || process.cwd();
4507
- if (matchesAnyPattern(ctx.file, scanCss, root)) {
4508
- state.parsedTheme = runThemeScan(root, scanCss) ?? state.parsedTheme;
4509
- reloadThemeGroupsModule();
4408
+ const before = createThemeGroupsModule(themeGroupTokens());
4409
+ if (scanCss && matchesAnyPattern(ctx.file, scanCss, root)) {
4410
+ state.scanCssTheme = runThemeScan(root, scanCss) ?? state.scanCssTheme;
4510
4411
  }
4511
- } else if (ctx.file.endsWith(".css")) {
4512
- runAutoThemeScan(ctx.server.config.root || process.cwd());
4412
+ runAutoThemeScan(root);
4513
4413
  reloadThemeGroupsModule();
4414
+ if (createThemeGroupsModule(themeGroupTokens()) !== before) {
4415
+ ctx.server.ws.send({ type: "full-reload" });
4416
+ }
4514
4417
  }
4515
4418
  if (!shouldProcessSource(ctx.file)) {
4516
4419
  return;
@@ -4519,8 +4422,10 @@ ${transformedCode}`;
4519
4422
  try {
4520
4423
  fileContent = fs.readFileSync(ctx.file, "utf-8");
4521
4424
  } catch {
4425
+ refreshSzvRegistryEntry(ctx.file, null);
4522
4426
  return;
4523
4427
  }
4428
+ refreshSzvRegistryEntry(ctx.file, fileContent);
4524
4429
  if (!fileContent.includes("sz=") && !fileContent.includes("szs=") && !/\bsz\s*:\s*["'{]/.test(fileContent)) {
4525
4430
  trackGlobalVarSourceFile(ctx.file, fileContent);
4526
4431
  recordFileVarMangleEntries(state, ctx.file, []);
@@ -4634,6 +4539,13 @@ ${transformedCode}`;
4634
4539
  }
4635
4540
  return manifest;
4636
4541
  }
4542
+ function rewriteCodeAsset(source, shouldMangle) {
4543
+ if (!shouldMangle) return replacePlaceholders(source);
4544
+ const before = replacePlaceholders(source);
4545
+ const after = replacePlaceholders(mangleCodeClasses(source));
4546
+ recordCodePair(sizeAccount, before, after);
4547
+ return after;
4548
+ }
4637
4549
  function rewriteOutputCss(source, file, shouldMangle, mangledSources, externalClasses) {
4638
4550
  const css = rewriteCssWithValidatedGlobalVarPlan(
4639
4551
  source,
@@ -4662,7 +4574,14 @@ ${transformedCode}`;
4662
4574
  collectMangleHybridHazards(state.mangleMap, mangledSources, externalClasses)
4663
4575
  );
4664
4576
  if (message) console.warn(message);
4577
+ }
4578
+ function reportBuildSummary() {
4665
4579
  reportMangleSize();
4580
+ if (resolveQuietMode(quiet) !== "all") {
4581
+ const message = suppressedAdvisoryMessage(state.suppressedAdvisories);
4582
+ if (message) console.warn(message);
4583
+ }
4584
+ state.suppressedAdvisories = 0;
4666
4585
  }
4667
4586
  function reportMangleSize() {
4668
4587
  const verdict = computeMangleSizeVerdict(
@@ -4682,7 +4601,7 @@ ${transformedCode}`;
4682
4601
  return;
4683
4602
  }
4684
4603
  if (!file.endsWith(".js")) return;
4685
- const rewritten = shouldMangle ? replacePlaceholders(mangleCodeClasses(source)) : replacePlaceholders(source);
4604
+ const rewritten = rewriteCodeAsset(source, shouldMangle);
4686
4605
  if (rewritten !== source) {
4687
4606
  compilation.updateAsset(file, new compiler.webpack.sources.RawSource(rewritten));
4688
4607
  }
@@ -4731,31 +4650,44 @@ ${transformedCode}`;
4731
4650
  }
4732
4651
  }
4733
4652
  reportOutputMangleHazards(shouldMangle, mangledSources, externalClasses);
4653
+ reportBuildSummary();
4734
4654
  }
4735
4655
  function rewriteViteBundleEntry(chunk, file, shouldMangle, mangledSources, externalClasses) {
4736
- if (chunk.type === "asset" && chunk.fileName.endsWith(".css") && chunk.source !== void 0) {
4737
- const originalCss = chunk.source.toString();
4738
- const css = rewriteOutputCss(
4739
- originalCss,
4740
- file,
4741
- shouldMangle,
4742
- mangledSources,
4743
- externalClasses
4744
- );
4745
- if (css !== originalCss) chunk.source = css;
4656
+ if (chunk.type !== "asset" || !chunk.fileName.endsWith(".css") || chunk.source === void 0) {
4746
4657
  return;
4747
4658
  }
4748
- if (chunk.type !== "chunk" || chunk.code === void 0) return;
4749
- const rewritten = shouldMangle ? replacePlaceholders(mangleCodeClasses(chunk.code)) : replacePlaceholders(chunk.code);
4750
- if (rewritten !== chunk.code) chunk.code = rewritten;
4659
+ const originalCss = chunk.source.toString();
4660
+ const css = rewriteOutputCss(
4661
+ originalCss,
4662
+ file,
4663
+ shouldMangle,
4664
+ mangledSources,
4665
+ externalClasses
4666
+ );
4667
+ if (css !== originalCss) chunk.source = css;
4668
+ }
4669
+ function mangleCssModule(code, id) {
4670
+ const shouldMangle = manglingEnabled && mangleMapFrozen && Object.keys(state.mangleMap).length > 0;
4671
+ const css = rewriteOutputCss(
4672
+ code,
4673
+ id,
4674
+ shouldMangle,
4675
+ transformMangledSources,
4676
+ transformExternalClasses
4677
+ );
4678
+ return css === code ? null : { code: css, map: null };
4751
4679
  }
4752
4680
  function processRollupBundle(bundle, emitAsset) {
4753
4681
  finalizeMangleMap();
4754
- state.globalVarValidationResult = validateGlobalVarBundleInputs(
4755
- collectRollupGlobalVarCssAssets(bundle)
4756
- );
4682
+ if (!cssRewrittenInTransform) {
4683
+ state.globalVarValidationResult = validateGlobalVarBundleInputs(
4684
+ collectRollupGlobalVarCssAssets(bundle)
4685
+ );
4686
+ }
4757
4687
  const shouldMangle = manglingEnabled && Object.keys(state.mangleMap).length > 0;
4758
- emitAsset("csszyx-manifest.json", JSON.stringify(createBundleManifest(shouldMangle)));
4688
+ if (options.build?.emitManifest === true) {
4689
+ emitAsset("csszyx-manifest.json", JSON.stringify(createBundleManifest(shouldMangle)));
4690
+ }
4759
4691
  if (shouldEmitGlobalVarMapAsset(globalVarMangleConfig)) {
4760
4692
  const globalVarMap = createGlobalVarMapAssetSource(
4761
4693
  state.varMangleMap,
@@ -4764,16 +4696,18 @@ ${transformedCode}`;
4764
4696
  );
4765
4697
  if (globalVarMap) emitAsset(".csszyx/global-var-map.json", globalVarMap);
4766
4698
  }
4767
- const mangledSources = /* @__PURE__ */ new Set();
4768
- const externalClasses = /* @__PURE__ */ new Set();
4769
- for (const file in bundle) {
4770
- rewriteViteBundleEntry(
4771
- bundle[file],
4772
- file,
4773
- shouldMangle,
4774
- mangledSources,
4775
- externalClasses
4776
- );
4699
+ const mangledSources = new Set(transformMangledSources);
4700
+ const externalClasses = new Set(transformExternalClasses);
4701
+ if (!cssRewrittenInTransform) {
4702
+ for (const file in bundle) {
4703
+ rewriteViteBundleEntry(
4704
+ bundle[file],
4705
+ file,
4706
+ shouldMangle,
4707
+ mangledSources,
4708
+ externalClasses
4709
+ );
4710
+ }
4777
4711
  }
4778
4712
  reportOutputMangleHazards(shouldMangle, mangledSources, externalClasses);
4779
4713
  }
@@ -4787,8 +4721,38 @@ ${transformedCode}`;
4787
4721
  * @param compiler - the Webpack compiler instance
4788
4722
  */
4789
4723
  webpack(compiler) {
4724
+ if (manglingEnabled && compiler.options?.optimization?.realContentHash === false) {
4725
+ emitWarning(realContentHashDisabledMessage());
4726
+ }
4790
4727
  registerWebpackAssetProcessor(compiler, processWebpackAssets);
4791
4728
  },
4729
+ /**
4730
+ * Settle the map once, before the first chunk is rendered.
4731
+ *
4732
+ * `renderChunk` is where the class rewrite has to happen, and Rollup
4733
+ * offers no ordering guarantee among chunks — so the map is completed
4734
+ * here rather than from whichever chunk arrives first.
4735
+ */
4736
+ renderStart() {
4737
+ finalizeMangleMap();
4738
+ },
4739
+ /**
4740
+ * Rewrite a JS chunk while its bytes still decide its filename hash.
4741
+ *
4742
+ * Rollup renders every chunk, hashes the result, substitutes the
4743
+ * filename placeholders and only then calls `generateBundle` — so the
4744
+ * rewrite this used to do there produced a file whose name described
4745
+ * the un-mangled bytes. The map is complete by `buildEnd`, which is
4746
+ * before the render phase, so nothing forces the later hook.
4747
+ *
4748
+ * @param code Rendered chunk source.
4749
+ * @returns Rewritten chunk, or null when nothing changed.
4750
+ */
4751
+ renderChunk(code) {
4752
+ const shouldMangle = manglingEnabled && Object.keys(state.mangleMap).length > 0;
4753
+ const rewritten = rewriteCodeAsset(code, shouldMangle);
4754
+ return rewritten === code ? null : { code: rewritten, map: null };
4755
+ },
4792
4756
  /**
4793
4757
  * Rollup-compatible hook used by both pure Rollup and Vite adapters.
4794
4758
  *
@@ -4801,15 +4765,54 @@ ${transformedCode}`;
4801
4765
  processRollupBundle(bundle, (fileName, source) => {
4802
4766
  this.emitFile({ type: "asset", fileName, source });
4803
4767
  });
4768
+ },
4769
+ /**
4770
+ * Report what mangling cost, after the bundler has printed its assets.
4771
+ *
4772
+ * Vite's reporter prints the asset table from `writeBundle`, which runs
4773
+ * before this hook — so the size verdict lands where a reader is
4774
+ * already looking at sizes, instead of scrolling past mid-build among
4775
+ * the other diagnostics.
4776
+ */
4777
+ closeBundle() {
4778
+ reportBuildSummary();
4804
4779
  }
4805
4780
  }));
4806
- return { prePlugin, postPlugin };
4781
+ const cssPlugin = createUnplugin(() => ({
4782
+ name: "csszyx:css-mangle",
4783
+ transformInclude(id) {
4784
+ return cssRewrittenInTransform && isMangleableCssId(id);
4785
+ },
4786
+ transform(code, id) {
4787
+ return mangleCssModule(code, id);
4788
+ },
4789
+ vite: {
4790
+ /**
4791
+ * Claim the CSS rewrite for the transform phase on this lane.
4792
+ *
4793
+ * A dev server never reaches an output hook, and mangling is off
4794
+ * there anyway; claiming it only for `build` keeps `vite serve`
4795
+ * exactly as it was.
4796
+ *
4797
+ * @param config - the resolved Vite configuration object
4798
+ * @param config.command - `'build'` or `'serve'`
4799
+ */
4800
+ configResolved(config) {
4801
+ cssRewrittenInTransform = config.command === "build";
4802
+ }
4803
+ }
4804
+ }));
4805
+ return { prePlugin, postPlugin, cssPlugin };
4807
4806
  }
4808
4807
  const defaultInstance = createCsszyxPlugins();
4809
4808
  const unplugin = defaultInstance.prePlugin;
4810
4809
  const vitePlugin = (options = {}) => {
4811
- const { prePlugin, postPlugin } = createCsszyxPlugins(options);
4812
- return [prePlugin.vite(options), postPlugin.vite(options)];
4810
+ const { prePlugin, postPlugin, cssPlugin } = createCsszyxPlugins(options);
4811
+ return [
4812
+ prePlugin.vite(options),
4813
+ cssPlugin.vite(options),
4814
+ postPlugin.vite(options)
4815
+ ];
4813
4816
  };
4814
4817
  const webpackPlugin = (options = {}) => {
4815
4818
  const { prePlugin, postPlugin } = createCsszyxPlugins(options);
@@ -4856,4 +4859,4 @@ const esbuildPlugin = (options = {}) => {
4856
4859
  };
4857
4860
  };
4858
4861
 
4859
- export { skippedSzFilesMessage as $, isHardIgnoredPath as A, isMonorepoPackage as B, isPackagesSkippedSource as C, isRSCServerModule as D, isTailwindReservedGlobalVar as E, mangleCodeClassesSync as F, mangleEligibleClasses as G, mangleHybridHazardMessage as H, mergeThemes as I, missingTailwindEntryMessage as J, normalizeGlobalVarAliasesForCache as K, parseThemeBlocks as L, planGlobalVarAliases as M, readGlobalVarScanCache as N, recordGlobalVarSourceFile as O, resolveCompileSourceDirs as P, resolveGlobalVarScanCacheDir as Q, resolveNativeCacheIdentity as R, rewriteGlobalVarCssAliases as S, rollupPlugin as T, scanCustomPropertyNames as U, scanGlobalVarCss as V, shouldEmitMissingCssFallback as W, shouldEmitWarning as X, shouldTrackGlobalVarSources as Y, shouldWarnMissingTailwindEntry as Z, shouldWarnUnscopedMonorepo as _, allocateMangleTokens as a, unscopedMonorepoMessage as a0, validateGlobalVarAliasInputs as a1, vitePlugin as a2, webpackPlugin as a3, writeGlobalVarScanCache as a4, appendTailwindSourceDirective as b, assertNoRSCBoundaryViolation as c, assertNoRSCGraphViolation as d, collectMangleHybridHazards as e, computeSafelistRelPath as f, createGlobalVarAliasValidationOptions as g, createGlobalVarMapAssetSource as h, createGlobalVarScanCacheKey as i, createRSCModuleRecord as j, cssHasContentScope as k, cssImportsTailwind as l, deleteRSCModuleRecord as m, emitMissingCssFallback as n, esbuildPlugin as o, extractGlobalVarAliasesForManifest as p, fileMayContainSafelistableSz as q, findLocalImportSources as r, findRSCBoundaryViolation as s, findRSCGraphViolation as t, unplugin as u, hasInjectableTailwindCandidate as v, hasTokens as w, hasUseClientDirective as x, hasUseServerDirective as y, isCompileSourceOptedIn as z };
4862
+ export { shouldWarnUnscopedMonorepo as $, isHardIgnoredPath as A, isMangleableCssId as B, isMonorepoPackage as C, isPackagesSkippedSource as D, isRSCServerModule as E, isTailwindReservedGlobalVar as F, lateMangleCensusMessage as G, mangleCodeClassesSync as H, mangleEligibleClasses as I, mangleHybridHazardMessage as J, missingTailwindEntryMessage as K, normalizeGlobalVarAliasesForCache as L, planGlobalVarAliases as M, readGlobalVarScanCache as N, realContentHashDisabledMessage as O, recordGlobalVarSourceFile as P, resolveCompileSourceDirs as Q, resolveGlobalVarScanCacheDir as R, resolveNativeCacheIdentity as S, resolveQuietMode as T, rewriteGlobalVarCssAliases as U, rollupPlugin as V, scanGlobalVarCss as W, shouldEmitMissingCssFallback as X, shouldEmitWarning as Y, shouldTrackGlobalVarSources as Z, shouldWarnMissingTailwindEntry as _, allocateMangleTokens as a, skippedSzFilesMessage as a0, suppressedAdvisoryMessage as a1, unscopedMonorepoMessage as a2, validateGlobalVarAliasInputs as a3, vitePlugin as a4, watchModeMangleMessage as a5, webpackPlugin as a6, writeGlobalVarScanCache as a7, appendTailwindSourceDirective as b, assertNoRSCBoundaryViolation as c, assertNoRSCGraphViolation as d, collectMangleHybridHazards as e, computeSafelistRelPath as f, createGlobalVarAliasValidationOptions as g, createGlobalVarMapAssetSource as h, createGlobalVarScanCacheKey as i, createRSCModuleRecord as j, cssHasContentScope as k, cssImportsTailwind as l, deleteRSCModuleRecord as m, emitMissingCssFallback as n, esbuildPlugin as o, extractGlobalVarAliasesForManifest as p, fileMayContainSafelistableSz as q, findLocalImportSources as r, findRSCBoundaryViolation as s, findRSCGraphViolation as t, unplugin as u, hasInjectableTailwindCandidate as v, hasUseClientDirective as w, hasUseServerDirective as x, isAdvisoryDiagnostic as y, isCompileSourceOptedIn as z };