@csszyx/unplugin 0.13.0 → 0.14.1

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 (36) hide show
  1. package/README.md +8 -10
  2. package/dist/index.cjs +9 -3
  3. package/dist/index.d.cts +2 -2
  4. package/dist/index.d.mts +2 -2
  5. package/dist/index.mjs +3 -3
  6. package/dist/next-config.d.cts +3 -2
  7. package/dist/next-config.d.mts +3 -2
  8. package/dist/next-prebuild.cjs +3 -4
  9. package/dist/next-prebuild.d.cts +1 -2
  10. package/dist/next-prebuild.d.mts +1 -2
  11. package/dist/next-prebuild.mjs +3 -4
  12. package/dist/next-turbo-loader.cjs +5 -7
  13. package/dist/next-turbo-loader.d.cts +1 -2
  14. package/dist/next-turbo-loader.d.mts +1 -2
  15. package/dist/next-turbo-loader.mjs +5 -7
  16. package/dist/shared/{unplugin.CXV7fOa2.d.cts → unplugin.B32Exn-K.d.cts} +2 -2
  17. package/dist/shared/{unplugin.CXV7fOa2.d.mts → unplugin.B32Exn-K.d.mts} +2 -2
  18. package/dist/shared/{unplugin.CUTa6bY9.cjs → unplugin.BJiWWF6w.cjs} +5 -23
  19. package/dist/shared/{unplugin.4du3qMst.cjs → unplugin.BKGCkMgM.cjs} +4 -10
  20. package/dist/shared/{unplugin.5l4RHMQh.mjs → unplugin.BcsvXjIV.mjs} +1 -0
  21. package/dist/shared/{unplugin.DCT7DDG6.d.cts → unplugin.BsjD3mtb.d.cts} +91 -4
  22. package/dist/shared/{unplugin.DCT7DDG6.d.mts → unplugin.BsjD3mtb.d.mts} +91 -4
  23. package/dist/shared/{unplugin.X9a9SL_s.cjs → unplugin.CkNQjA4G.cjs} +1 -0
  24. package/dist/shared/{unplugin.BB0iWSds.mjs → unplugin.DKcwO8O4.mjs} +5 -10
  25. package/dist/shared/{unplugin.CogPHmDs.mjs → unplugin.DjRylBC_.mjs} +6 -24
  26. package/dist/shared/{unplugin.Bf4fNbp7.cjs → unplugin.ZMa9YE1H.cjs} +309 -89
  27. package/dist/shared/{unplugin.ymMe428s.mjs → unplugin.gR_h2DVG.mjs} +303 -89
  28. package/dist/vite.cjs +3 -3
  29. package/dist/vite.d.cts +2 -2
  30. package/dist/vite.d.mts +1 -1
  31. package/dist/vite.mjs +3 -3
  32. package/dist/webpack.cjs +3 -3
  33. package/dist/webpack.d.cts +2 -2
  34. package/dist/webpack.d.mts +1 -1
  35. package/dist/webpack.mjs +3 -3
  36. package/package.json +11 -11
@@ -13,9 +13,9 @@ const svelteAdapter = require('@csszyx/svelte-adapter');
13
13
  const types = require('@csszyx/types');
14
14
  const vueAdapter = require('@csszyx/vue-adapter');
15
15
  const unplugin$1 = require('unplugin');
16
- const transformCache = require('./unplugin.4du3qMst.cjs');
16
+ const transformCache = require('./unplugin.BKGCkMgM.cjs');
17
17
  const cssMangler = require('../css-mangler.cjs');
18
- const themeGroupsFile = require('./unplugin.X9a9SL_s.cjs');
18
+ const themeGroupsFile = require('./unplugin.CkNQjA4G.cjs');
19
19
  const htmlEscape = require('./unplugin.BkRah5Ot.cjs');
20
20
  const node_zlib = require('node:zlib');
21
21
  const postcss = require('postcss');
@@ -1843,11 +1843,13 @@ function escapeForDoubleQuotedString(value) {
1843
1843
  }
1844
1844
 
1845
1845
  function createMangleSizeAccount() {
1846
- return { cssGzBefore: 0, cssGzAfter: 0, channels: /* @__PURE__ */ new Set() };
1846
+ return { cssGzBefore: 0, cssGzAfter: 0, codeGzBefore: 0, codeGzAfter: 0, channels: /* @__PURE__ */ new Set() };
1847
1847
  }
1848
1848
  function resetMangleSizeAccount(account) {
1849
1849
  account.cssGzBefore = 0;
1850
1850
  account.cssGzAfter = 0;
1851
+ account.codeGzBefore = 0;
1852
+ account.codeGzAfter = 0;
1851
1853
  account.channels.clear();
1852
1854
  }
1853
1855
  function gzipBytes(text) {
@@ -1864,30 +1866,53 @@ function recordCssPair(account, before, after) {
1864
1866
  account.cssGzBefore += gzipBytes(before);
1865
1867
  account.cssGzAfter += gzipBytes(after);
1866
1868
  }
1869
+ function recordCodePair(account, before, after) {
1870
+ if (before === after) {
1871
+ const bytes = gzipBytes(before);
1872
+ account.codeGzBefore += bytes;
1873
+ account.codeGzAfter += bytes;
1874
+ return;
1875
+ }
1876
+ account.codeGzBefore += gzipBytes(before);
1877
+ account.codeGzAfter += gzipBytes(after);
1878
+ }
1867
1879
  function computeMangleSizeVerdict(account, mapPayload) {
1868
1880
  const channels = htmlEscape.sortStrings(account.channels);
1869
1881
  const mapCost = gzipBytes(mapPayload) * channels.length;
1870
1882
  const cssSaving = account.cssGzBefore - account.cssGzAfter;
1871
- return { mapCost, cssSaving, net: mapCost - cssSaving, channels };
1883
+ const codeSaving = account.codeGzBefore - account.codeGzAfter;
1884
+ return { mapCost, cssSaving, codeSaving, net: mapCost - cssSaving - codeSaving, channels };
1872
1885
  }
1873
1886
  function mangleSizeMessage(verdict) {
1874
1887
  if (verdict.channels.length === 0) return null;
1875
1888
  if (verdict.net <= 0) return null;
1876
1889
  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)`;
1890
+ const codePart = verdict.codeSaving >= 0 ? `the shortened classes in code save ${verdict.codeSaving} B` : `the shortened classes in code COST ${-verdict.codeSaving} B`;
1877
1891
  const channelPart = verdict.channels.length > 1 ? `${verdict.channels.join(" + ")} (${verdict.channels.length} copies)` : verdict.channels[0];
1878
- 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.)`;
1892
+ 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.`;
1879
1893
  }
1880
1894
 
1881
1895
  function isParserMode(value) {
1882
- return value === "rust" || value === "oxc" || value === "babel";
1896
+ return value === "rust" || value === "wasm";
1883
1897
  }
1884
1898
  function resolveParserMode(input) {
1885
1899
  const { configParser, envParser, defaultParser, isRustAvailable } = input;
1886
1900
  const envValid = isParserMode(envParser);
1887
- const explicit = envValid || configParser != null;
1888
- const parser = envValid ? envParser : configParser ?? defaultParser;
1901
+ const configValid = isParserMode(configParser);
1902
+ const explicit = envValid || configValid;
1903
+ let parser;
1904
+ if (envValid) {
1905
+ parser = envParser;
1906
+ } else if (configValid) {
1907
+ parser = configParser;
1908
+ } else {
1909
+ parser = defaultParser;
1910
+ }
1889
1911
  if (parser === "rust" && !explicit && !isRustAvailable()) {
1890
- return { parser: "oxc", degraded: true, explicit: false };
1912
+ if (input.isWasmAvailable()) {
1913
+ return { parser: "wasm", degraded: true, explicit: false };
1914
+ }
1915
+ return { parser: "rust", degraded: false, explicit: false };
1891
1916
  }
1892
1917
  return { parser, degraded: false, explicit };
1893
1918
  }
@@ -2034,10 +2059,9 @@ let _hasWarnedTsConfig = false;
2034
2059
  let _hasWarnedTransformCacheVersion = false;
2035
2060
  let _hasWarnedNativeFallback = false;
2036
2061
  const _loggedActiveParsers = /* @__PURE__ */ new Set();
2037
- const _babelFallbackFiles = /* @__PURE__ */ new Set();
2038
- const requireFromHere = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/unplugin.Bf4fNbp7.cjs', document.baseURI).href)));
2062
+ const requireFromHere = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/unplugin.ZMa9YE1H.cjs', document.baseURI).href)));
2039
2063
  const PLUGIN_VERSION = findPackageVersionFromFile(
2040
- node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/unplugin.Bf4fNbp7.cjs', document.baseURI).href))),
2064
+ node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/unplugin.ZMa9YE1H.cjs', document.baseURI).href))),
2041
2065
  UNKNOWN_PACKAGE_VERSION
2042
2066
  );
2043
2067
  const COMPILER_VERSION = findPackageVersionFromModule("@csszyx/compiler", UNKNOWN_PACKAGE_VERSION);
@@ -2151,9 +2175,20 @@ function mangleHybridHazardMessage(hazards) {
2151
2175
  }
2152
2176
  return parts.join("");
2153
2177
  }
2178
+ const TAILWIND_IMPORT_SPECIFIER = /@import\s+["']tailwindcss(?:\/[^"']*)?["']/g;
2179
+ const SOURCE_MODIFIER = /\bsource\(/;
2180
+ function tailwindImportScopesContent(css) {
2181
+ for (const match of css.matchAll(TAILWIND_IMPORT_SPECIFIER)) {
2182
+ const modifiersStart = match.index + match[0].length;
2183
+ const terminator = css.indexOf(";", modifiersStart);
2184
+ const modifiers = terminator === -1 ? css.slice(modifiersStart) : css.slice(modifiersStart, terminator);
2185
+ if (SOURCE_MODIFIER.test(modifiers)) return true;
2186
+ }
2187
+ return false;
2188
+ }
2154
2189
  function cssHasContentScope(code) {
2155
2190
  const s = stripCssBlockComments(code);
2156
- return /@import\s+["']tailwindcss(?:\/[^"']*)?["']\s+source\(/.test(s) || /@source\s+not\b/.test(s);
2191
+ return tailwindImportScopesContent(s) || /@source\s+not\b/.test(s);
2157
2192
  }
2158
2193
  function isMonorepoPackage(root) {
2159
2194
  let dir = path__namespace.dirname(path__namespace.resolve(root));
@@ -2179,7 +2214,15 @@ function shouldWarnUnscopedMonorepo(sawTailwindEntry, tailwindEntryScoped, inMon
2179
2214
  return sawTailwindEntry && !tailwindEntryScoped && inMonorepo;
2180
2215
  }
2181
2216
  function unscopedMonorepoMessage() {
2182
- 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 }).';
2217
+ 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 }).';
2218
+ }
2219
+ function isAdvisoryDiagnostic(message) {
2220
+ return !(message.includes("unresolvable sz spread") || message.includes("AST budget exceeded") || compiler.szFallbackConsequenceOf(message) === "missing-css");
2221
+ }
2222
+ function suppressedAdvisoryMessage(count) {
2223
+ if (count <= 0) return null;
2224
+ const held = count === 1 ? "1 advisory sz fallback" : `${count} advisory sz fallbacks`;
2225
+ 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.`;
2183
2226
  }
2184
2227
  function resolveQuietMode(quiet) {
2185
2228
  if (quiet === true || quiet === "all") return "all";
@@ -2315,6 +2358,22 @@ ${szvExportFiles.length} of them may export \`szv\` factories, so they stay out
2315
2358
  ${list}
2316
2359
  Add the package directory to \`compileSources\` (or move the file out of packages/) \u2014 otherwise their classes never reach the safelist.` + registryClause;
2317
2360
  }
2361
+ function lateMangleCensusMessage(lateOwned, lateAuthored) {
2362
+ const sections = [
2363
+ lateOwned.length > 0 ? `csszyx-owned classes: ${lateOwned.join(", ")}` : "",
2364
+ lateAuthored.length > 0 ? `author classes: ${lateAuthored.join(", ")}` : ""
2365
+ ].filter(Boolean);
2366
+ const found = sections.length > 0 ? sections.join("\n") : "the class census changed";
2367
+ return `[csszyx] production.mangle: classes appeared after the mangle map was frozen, so the emitted CSS was hashed against a different map:
2368
+ ${found}
2369
+ 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.`;
2370
+ }
2371
+ function watchModeMangleMessage() {
2372
+ 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.";
2373
+ }
2374
+ function realContentHashDisabledMessage() {
2375
+ 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.";
2376
+ }
2318
2377
  function computeSafelistRelPath(rootDir, safelistFilename, cssId) {
2319
2378
  const safelistPath = transformCache.normalizePathSeparators(path__namespace.join(rootDir, safelistFilename));
2320
2379
  const cssDir = transformCache.normalizePathSeparators(path__namespace.dirname(cssId));
@@ -2390,6 +2449,20 @@ function collectConfiguredGlobalVarCssSources(rootDir, scanCss) {
2390
2449
  }
2391
2450
  });
2392
2451
  }
2452
+ const MANGLEABLE_CSS_ID_RE = /\.(?:css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/;
2453
+ const NON_STYLESHEET_CSS_QUERY_RE = /[?&](?:worker|sharedworker|raw|url)(?:&|=|$)/;
2454
+ const COMMONJS_PROXY_RE = /\?commonjs-proxy/;
2455
+ function isMangleableCssId(id) {
2456
+ return MANGLEABLE_CSS_ID_RE.test(id) && !NON_STYLESHEET_CSS_QUERY_RE.test(id) && !COMMONJS_PROXY_RE.test(id);
2457
+ }
2458
+ function dedupeCssAssetsByName(assets) {
2459
+ const seen = /* @__PURE__ */ new Set();
2460
+ return assets.filter((asset) => {
2461
+ if (seen.has(asset.fileName)) return false;
2462
+ seen.add(asset.fileName);
2463
+ return true;
2464
+ });
2465
+ }
2393
2466
  function collectWebpackGlobalVarCssAssets(assets) {
2394
2467
  return Object.entries(assets).flatMap(([fileName, asset]) => {
2395
2468
  if (!/\.css(?:$|\?)/.test(fileName)) {
@@ -2972,6 +3045,13 @@ function createCsszyxPlugins(options = {}) {
2972
3045
  const szObjectProvidersExamined = /* @__PURE__ */ new Set();
2973
3046
  let specifierAliases = [];
2974
3047
  const mangleReserved = new Set(options.production?.mangleExclude ?? []);
3048
+ let mangleMapFrozen = false;
3049
+ let frozenOwnedClasses = /* @__PURE__ */ new Set();
3050
+ let frozenAuthoredClasses = /* @__PURE__ */ new Set();
3051
+ let cssRewrittenInTransform = false;
3052
+ let projectCssFiles = [];
3053
+ const transformMangledSources = /* @__PURE__ */ new Set();
3054
+ const transformExternalClasses = /* @__PURE__ */ new Set();
2975
3055
  const mangleMapDelivery = options.production?.mangleMapDelivery ?? "both";
2976
3056
  if (!["both", "html", "bundle"].includes(mangleMapDelivery)) {
2977
3057
  throw new Error(
@@ -3016,22 +3096,27 @@ function createCsszyxPlugins(options = {}) {
3016
3096
  configParser: options.build?.parser,
3017
3097
  envParser: process.env.CSSZYX_PARSER,
3018
3098
  defaultParser: types.DEFAULT_BUILD_CONFIG.parser ?? "rust",
3019
- isRustAvailable: compiler.isRustTransformAvailable
3099
+ isRustAvailable: compiler.isRustTransformAvailable,
3100
+ isWasmAvailable: compiler.isWasmTransformAvailable
3020
3101
  });
3021
3102
  function announceActiveParser() {
3022
3103
  if (parserDegraded && !_hasWarnedNativeFallback) {
3023
3104
  _hasWarnedNativeFallback = true;
3024
- console.warn(
3025
- "[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."
3026
- );
3105
+ if (parserMode === "wasm") {
3106
+ console.warn(
3107
+ "[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."
3108
+ );
3109
+ }
3027
3110
  }
3028
3111
  if (!_loggedActiveParsers.has(parserMode)) {
3029
3112
  _loggedActiveParsers.add(parserMode);
3030
3113
  let detail = parserMode;
3031
3114
  if (parserDegraded) {
3032
- detail = "oxc (degraded from default `rust`: no native binary for this platform)";
3115
+ detail = "wasm (degraded from default `rust`: same engine, wasm build)";
3033
3116
  } else if (parserMode === "rust") {
3034
3117
  detail = "rust (native engine)";
3118
+ } else if (parserMode === "wasm") {
3119
+ detail = "wasm (wasm build of the native engine)";
3035
3120
  }
3036
3121
  console.warn(`[csszyx] active parser: ${detail}`);
3037
3122
  }
@@ -3051,6 +3136,7 @@ function createCsszyxPlugins(options = {}) {
3051
3136
  tailwindEntryScoped: false,
3052
3137
  contentScopeWarningEmitted: false,
3053
3138
  spreadWarnings: /* @__PURE__ */ new Set(),
3139
+ suppressedAdvisories: 0,
3054
3140
  skippedSzFiles: /* @__PURE__ */ new Set(),
3055
3141
  skippedSzvExportFiles: /* @__PURE__ */ new Set(),
3056
3142
  skipWarningEmitted: false,
@@ -3112,7 +3198,7 @@ function createCsszyxPlugins(options = {}) {
3112
3198
  const result = validateGlobalVarAliasInputs(
3113
3199
  createGlobalVarAliasValidationOptions({
3114
3200
  rootDir: state.rootDir,
3115
- cssAssets: [...configuredCssAssets, ...cssAssets],
3201
+ cssAssets: dedupeCssAssetsByName([...configuredCssAssets, ...cssAssets]),
3116
3202
  sourceFiles: buildGlobalVarSourceFiles(state),
3117
3203
  tokens: globalVarMangleConfig.tokens,
3118
3204
  autoPrefix: globalVarMangleConfig.autoPrefix,
@@ -3125,6 +3211,25 @@ function createCsszyxPlugins(options = {}) {
3125
3211
  assertGlobalVarPlanMatchesEarlyAliases(result, earlyGlobalVarAliasEntries);
3126
3212
  return result;
3127
3213
  }
3214
+ function revalidateFrozenGlobalVarPlan() {
3215
+ if (!cssRewrittenInTransform) return;
3216
+ validateGlobalVarBundleInputs(collectProjectGlobalVarCssSources());
3217
+ }
3218
+ function collectProjectGlobalVarCssSources() {
3219
+ const sources = [];
3220
+ for (const file of compiler.sortStrings(projectCssFiles)) {
3221
+ try {
3222
+ const snapshot = readStableTextFileSnapshotSync(file);
3223
+ sources.push({
3224
+ fileName: file,
3225
+ source: snapshot.source,
3226
+ mtimeMs: snapshot.mtimeMs
3227
+ });
3228
+ } catch {
3229
+ }
3230
+ }
3231
+ return sources;
3232
+ }
3128
3233
  function trackGlobalVarSourceFile(filename, code) {
3129
3234
  if (!globalVarSourceTrackingEnabled) {
3130
3235
  return;
@@ -3150,6 +3255,7 @@ function createCsszyxPlugins(options = {}) {
3150
3255
  refreshCompileSourceDirs();
3151
3256
  const discovered = themeGroupsFile.discoverProjectTheme(rootDir, [...compileSourceDirs]);
3152
3257
  state.autoThemeCssFiles = discovered.files;
3258
+ projectCssFiles = discovered.scanned;
3153
3259
  const sources = [state.scanCssTheme, discovered.theme].filter(
3154
3260
  (theme) => theme !== null
3155
3261
  );
@@ -3231,43 +3337,16 @@ function createCsszyxPlugins(options = {}) {
3231
3337
  return handoff.inputSha256 === cacheKey.inputSha256 ? handoff.result : null;
3232
3338
  }
3233
3339
  function runConfiguredParser(source, effectiveFilename, compilerOptions) {
3234
- if (parserMode === "babel") {
3235
- return {
3236
- result: compiler.transformSourceCode(source, effectiveFilename, compilerOptions),
3237
- cacheable: true
3238
- };
3239
- }
3240
3340
  if (parserMode === "rust") {
3241
3341
  return {
3242
3342
  result: compiler.transformRust(source, effectiveFilename, compilerOptions),
3243
3343
  cacheable: true
3244
3344
  };
3245
3345
  }
3246
- try {
3247
- return {
3248
- result: compiler.transformOxc(source, effectiveFilename, compilerOptions),
3249
- cacheable: true
3250
- };
3251
- } catch (error) {
3252
- return {
3253
- result: runBabelFallback(source, effectiveFilename, compilerOptions, error),
3254
- cacheable: false
3255
- };
3256
- }
3257
- }
3258
- function runBabelFallback(source, effectiveFilename, compilerOptions, error) {
3259
- const result = compiler.transformSourceCode(source, effectiveFilename, compilerOptions);
3260
- const reason = transformCache.babelFallbackReason(error);
3261
- result.diagnostics.push(
3262
- `[csszyx] oxc parser fell back to Babel for ${effectiveFilename}: ${reason}`
3263
- );
3264
- if (!_babelFallbackFiles.has(effectiveFilename)) {
3265
- _babelFallbackFiles.add(effectiveFilename);
3266
- console.warn(
3267
- `[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.`
3268
- );
3269
- }
3270
- return result;
3346
+ return {
3347
+ result: compiler.transformWasm(source, effectiveFilename, compilerOptions),
3348
+ cacheable: true
3349
+ };
3271
3350
  }
3272
3351
  function createCompilerOptions(astBudget = astBudgetOverride) {
3273
3352
  return {
@@ -3783,16 +3862,39 @@ function createCsszyxPlugins(options = {}) {
3783
3862
  state.authoredClasses.add(className);
3784
3863
  }
3785
3864
  }
3786
- function finalizeMangleMap() {
3865
+ function computeMangleMap() {
3787
3866
  const forbiddenTokens = /* @__PURE__ */ new Set([
3788
3867
  ...mangleReserved,
3789
3868
  ...state.authoredClasses,
3790
3869
  ...state.ownedClasses
3791
3870
  ]);
3792
- state.mangleMap = allocateMangleTokens(
3871
+ return allocateMangleTokens(
3793
3872
  mangleEligibleClasses(state.ownedClasses, state.authoredClasses),
3794
3873
  forbiddenTokens
3795
3874
  );
3875
+ }
3876
+ function freezeMangleMap() {
3877
+ state.mangleMap = computeMangleMap();
3878
+ frozenOwnedClasses = new Set(state.ownedClasses);
3879
+ frozenAuthoredClasses = new Set(state.authoredClasses);
3880
+ mangleMapFrozen = true;
3881
+ }
3882
+ function assertMangleCensusUnchanged() {
3883
+ if (!mangleMapFrozen) return;
3884
+ const current = computeMangleMap();
3885
+ if (JSON.stringify(current) === JSON.stringify(state.mangleMap)) return;
3886
+ const lateOwned = compiler.sortStrings(
3887
+ [...state.ownedClasses].filter((className) => !frozenOwnedClasses.has(className))
3888
+ );
3889
+ const lateAuthored = compiler.sortStrings(
3890
+ [...state.authoredClasses].filter((className) => !frozenAuthoredClasses.has(className))
3891
+ );
3892
+ throw new Error(lateMangleCensusMessage(lateOwned, lateAuthored));
3893
+ }
3894
+ function finalizeMangleMap() {
3895
+ if (!mangleMapFrozen) {
3896
+ state.mangleMap = computeMangleMap();
3897
+ }
3796
3898
  assertVarMangleMapSize(state.varMangleMap, varMangleMapMaxBytes);
3797
3899
  state.checksum = core.compute_mangle_checksum(
3798
3900
  createHydrationMangleMap(state.mangleMap, state.varMangleMap)
@@ -3851,7 +3953,8 @@ function createCsszyxPlugins(options = {}) {
3851
3953
  szPartArgsProvable: true,
3852
3954
  usesColorVar: false,
3853
3955
  usesSpacingVar: false,
3854
- usesUnitVar: false
3956
+ usesUnitVar: false,
3957
+ usesBoolClass: false
3855
3958
  };
3856
3959
  }
3857
3960
  function transformTailwindCssEntry(code, id) {
@@ -3878,13 +3981,13 @@ function createCsszyxPlugins(options = {}) {
3878
3981
  }
3879
3982
  emitMissingCssFallback(quiet, message, id, console.warn);
3880
3983
  }
3881
- if (quiet !== "off" || result.diagnostics.length === 0 || process.env.NODE_ENV === "production") {
3984
+ const advisories = result.diagnostics.filter(isAdvisoryDiagnostic);
3985
+ if (advisories.length === 0) return;
3986
+ if (quiet !== "off" || process.env.NODE_ENV === "production") {
3987
+ state.suppressedAdvisories += advisories.length;
3882
3988
  return;
3883
3989
  }
3884
- for (const message of result.diagnostics) {
3885
- if (message.includes("unresolvable sz spread") || message.includes("AST budget exceeded") || compiler.szFallbackConsequenceOf(message) === "missing-css") {
3886
- continue;
3887
- }
3990
+ for (const message of advisories) {
3888
3991
  warn(`[csszyx] ${id}
3889
3992
  ${message}`);
3890
3993
  }
@@ -3903,6 +4006,7 @@ function createCsszyxPlugins(options = {}) {
3903
4006
  usesColorVar: result.usesColorVar,
3904
4007
  usesSpacingVar: result.usesSpacingVar,
3905
4008
  usesUnitVar: result.usesUnitVar,
4009
+ usesBoolClass: result.usesBoolClass,
3906
4010
  szClasses: result.classes
3907
4011
  };
3908
4012
  }
@@ -4160,6 +4264,8 @@ ${transformedCode}`;
4160
4264
  /** Finalizes the mangle map after all source modules have been processed. */
4161
4265
  buildEnd() {
4162
4266
  finalizeMangleMap();
4267
+ assertMangleCensusUnchanged();
4268
+ revalidateFrozenGlobalVarPlan();
4163
4269
  assertNoRSCGraphViolation(state.rscModules);
4164
4270
  if (!state.tailwindWarningEmitted && shouldWarnMissingTailwindEntry(
4165
4271
  state.ownedClasses.size,
@@ -4284,10 +4390,22 @@ ${transformedCode}`;
4284
4390
  if (config.command === "serve") {
4285
4391
  manglingEnabled = false;
4286
4392
  }
4393
+ if (manglingEnabled && config.build?.watch) {
4394
+ manglingEnabled = false;
4395
+ emitWarning(watchModeMangleMessage());
4396
+ }
4287
4397
  evictTransformCacheOnce();
4288
4398
  prescanAndWriteClasses();
4289
4399
  state.scanCssTheme = runThemeScan(root, options.build?.scanCss) ?? state.scanCssTheme;
4290
4400
  runAutoThemeScan(root);
4401
+ if (config.command === "build") {
4402
+ state.globalVarValidationResult = validateGlobalVarBundleInputs(
4403
+ collectProjectGlobalVarCssSources()
4404
+ );
4405
+ }
4406
+ if (manglingEnabled && config.command === "build") {
4407
+ freezeMangleMap();
4408
+ }
4291
4409
  },
4292
4410
  /**
4293
4411
  * Vite HMR hook: re-runs theme scan when a watched CSS file changes,
@@ -4441,6 +4559,13 @@ ${transformedCode}`;
4441
4559
  }
4442
4560
  return manifest;
4443
4561
  }
4562
+ function rewriteCodeAsset(source, shouldMangle) {
4563
+ if (!shouldMangle) return replacePlaceholders(source);
4564
+ const before = replacePlaceholders(source);
4565
+ const after = replacePlaceholders(mangleCodeClasses(source));
4566
+ recordCodePair(sizeAccount, before, after);
4567
+ return after;
4568
+ }
4444
4569
  function rewriteOutputCss(source, file, shouldMangle, mangledSources, externalClasses) {
4445
4570
  const css = rewriteCssWithValidatedGlobalVarPlan(
4446
4571
  source,
@@ -4469,7 +4594,14 @@ ${transformedCode}`;
4469
4594
  collectMangleHybridHazards(state.mangleMap, mangledSources, externalClasses)
4470
4595
  );
4471
4596
  if (message) console.warn(message);
4597
+ }
4598
+ function reportBuildSummary() {
4472
4599
  reportMangleSize();
4600
+ if (resolveQuietMode(quiet) !== "all") {
4601
+ const message = suppressedAdvisoryMessage(state.suppressedAdvisories);
4602
+ if (message) console.warn(message);
4603
+ }
4604
+ state.suppressedAdvisories = 0;
4473
4605
  }
4474
4606
  function reportMangleSize() {
4475
4607
  const verdict = computeMangleSizeVerdict(
@@ -4489,7 +4621,7 @@ ${transformedCode}`;
4489
4621
  return;
4490
4622
  }
4491
4623
  if (!file.endsWith(".js")) return;
4492
- const rewritten = shouldMangle ? replacePlaceholders(mangleCodeClasses(source)) : replacePlaceholders(source);
4624
+ const rewritten = rewriteCodeAsset(source, shouldMangle);
4493
4625
  if (rewritten !== source) {
4494
4626
  compilation.updateAsset(file, new compiler.webpack.sources.RawSource(rewritten));
4495
4627
  }
@@ -4538,29 +4670,40 @@ ${transformedCode}`;
4538
4670
  }
4539
4671
  }
4540
4672
  reportOutputMangleHazards(shouldMangle, mangledSources, externalClasses);
4673
+ reportBuildSummary();
4541
4674
  }
4542
4675
  function rewriteViteBundleEntry(chunk, file, shouldMangle, mangledSources, externalClasses) {
4543
- if (chunk.type === "asset" && chunk.fileName.endsWith(".css") && chunk.source !== void 0) {
4544
- const originalCss = chunk.source.toString();
4545
- const css = rewriteOutputCss(
4546
- originalCss,
4547
- file,
4548
- shouldMangle,
4549
- mangledSources,
4550
- externalClasses
4551
- );
4552
- if (css !== originalCss) chunk.source = css;
4676
+ if (chunk.type !== "asset" || !chunk.fileName.endsWith(".css") || chunk.source === void 0) {
4553
4677
  return;
4554
4678
  }
4555
- if (chunk.type !== "chunk" || chunk.code === void 0) return;
4556
- const rewritten = shouldMangle ? replacePlaceholders(mangleCodeClasses(chunk.code)) : replacePlaceholders(chunk.code);
4557
- if (rewritten !== chunk.code) chunk.code = rewritten;
4679
+ const originalCss = chunk.source.toString();
4680
+ const css = rewriteOutputCss(
4681
+ originalCss,
4682
+ file,
4683
+ shouldMangle,
4684
+ mangledSources,
4685
+ externalClasses
4686
+ );
4687
+ if (css !== originalCss) chunk.source = css;
4688
+ }
4689
+ function mangleCssModule(code, id) {
4690
+ const shouldMangle = manglingEnabled && mangleMapFrozen && Object.keys(state.mangleMap).length > 0;
4691
+ const css = rewriteOutputCss(
4692
+ code,
4693
+ id,
4694
+ shouldMangle,
4695
+ transformMangledSources,
4696
+ transformExternalClasses
4697
+ );
4698
+ return css === code ? null : { code: css, map: null };
4558
4699
  }
4559
4700
  function processRollupBundle(bundle, emitAsset) {
4560
4701
  finalizeMangleMap();
4561
- state.globalVarValidationResult = validateGlobalVarBundleInputs(
4562
- collectRollupGlobalVarCssAssets(bundle)
4563
- );
4702
+ if (!cssRewrittenInTransform) {
4703
+ state.globalVarValidationResult = validateGlobalVarBundleInputs(
4704
+ collectRollupGlobalVarCssAssets(bundle)
4705
+ );
4706
+ }
4564
4707
  const shouldMangle = manglingEnabled && Object.keys(state.mangleMap).length > 0;
4565
4708
  if (options.build?.emitManifest === true) {
4566
4709
  emitAsset("csszyx-manifest.json", JSON.stringify(createBundleManifest(shouldMangle)));
@@ -4573,16 +4716,18 @@ ${transformedCode}`;
4573
4716
  );
4574
4717
  if (globalVarMap) emitAsset(".csszyx/global-var-map.json", globalVarMap);
4575
4718
  }
4576
- const mangledSources = /* @__PURE__ */ new Set();
4577
- const externalClasses = /* @__PURE__ */ new Set();
4578
- for (const file in bundle) {
4579
- rewriteViteBundleEntry(
4580
- bundle[file],
4581
- file,
4582
- shouldMangle,
4583
- mangledSources,
4584
- externalClasses
4585
- );
4719
+ const mangledSources = new Set(transformMangledSources);
4720
+ const externalClasses = new Set(transformExternalClasses);
4721
+ if (!cssRewrittenInTransform) {
4722
+ for (const file in bundle) {
4723
+ rewriteViteBundleEntry(
4724
+ bundle[file],
4725
+ file,
4726
+ shouldMangle,
4727
+ mangledSources,
4728
+ externalClasses
4729
+ );
4730
+ }
4586
4731
  }
4587
4732
  reportOutputMangleHazards(shouldMangle, mangledSources, externalClasses);
4588
4733
  }
@@ -4596,8 +4741,38 @@ ${transformedCode}`;
4596
4741
  * @param compiler - the Webpack compiler instance
4597
4742
  */
4598
4743
  webpack(compiler) {
4744
+ if (manglingEnabled && compiler.options?.optimization?.realContentHash === false) {
4745
+ emitWarning(realContentHashDisabledMessage());
4746
+ }
4599
4747
  registerWebpackAssetProcessor(compiler, processWebpackAssets);
4600
4748
  },
4749
+ /**
4750
+ * Settle the map once, before the first chunk is rendered.
4751
+ *
4752
+ * `renderChunk` is where the class rewrite has to happen, and Rollup
4753
+ * offers no ordering guarantee among chunks — so the map is completed
4754
+ * here rather than from whichever chunk arrives first.
4755
+ */
4756
+ renderStart() {
4757
+ finalizeMangleMap();
4758
+ },
4759
+ /**
4760
+ * Rewrite a JS chunk while its bytes still decide its filename hash.
4761
+ *
4762
+ * Rollup renders every chunk, hashes the result, substitutes the
4763
+ * filename placeholders and only then calls `generateBundle` — so the
4764
+ * rewrite this used to do there produced a file whose name described
4765
+ * the un-mangled bytes. The map is complete by `buildEnd`, which is
4766
+ * before the render phase, so nothing forces the later hook.
4767
+ *
4768
+ * @param code Rendered chunk source.
4769
+ * @returns Rewritten chunk, or null when nothing changed.
4770
+ */
4771
+ renderChunk(code) {
4772
+ const shouldMangle = manglingEnabled && Object.keys(state.mangleMap).length > 0;
4773
+ const rewritten = rewriteCodeAsset(code, shouldMangle);
4774
+ return rewritten === code ? null : { code: rewritten, map: null };
4775
+ },
4601
4776
  /**
4602
4777
  * Rollup-compatible hook used by both pure Rollup and Vite adapters.
4603
4778
  *
@@ -4610,15 +4785,54 @@ ${transformedCode}`;
4610
4785
  processRollupBundle(bundle, (fileName, source) => {
4611
4786
  this.emitFile({ type: "asset", fileName, source });
4612
4787
  });
4788
+ },
4789
+ /**
4790
+ * Report what mangling cost, after the bundler has printed its assets.
4791
+ *
4792
+ * Vite's reporter prints the asset table from `writeBundle`, which runs
4793
+ * before this hook — so the size verdict lands where a reader is
4794
+ * already looking at sizes, instead of scrolling past mid-build among
4795
+ * the other diagnostics.
4796
+ */
4797
+ closeBundle() {
4798
+ reportBuildSummary();
4799
+ }
4800
+ }));
4801
+ const cssPlugin = unplugin$1.createUnplugin(() => ({
4802
+ name: "csszyx:css-mangle",
4803
+ transformInclude(id) {
4804
+ return cssRewrittenInTransform && isMangleableCssId(id);
4805
+ },
4806
+ transform(code, id) {
4807
+ return mangleCssModule(code, id);
4808
+ },
4809
+ vite: {
4810
+ /**
4811
+ * Claim the CSS rewrite for the transform phase on this lane.
4812
+ *
4813
+ * A dev server never reaches an output hook, and mangling is off
4814
+ * there anyway; claiming it only for `build` keeps `vite serve`
4815
+ * exactly as it was.
4816
+ *
4817
+ * @param config - the resolved Vite configuration object
4818
+ * @param config.command - `'build'` or `'serve'`
4819
+ */
4820
+ configResolved(config) {
4821
+ cssRewrittenInTransform = config.command === "build";
4822
+ }
4613
4823
  }
4614
4824
  }));
4615
- return { prePlugin, postPlugin };
4825
+ return { prePlugin, postPlugin, cssPlugin };
4616
4826
  }
4617
4827
  const defaultInstance = createCsszyxPlugins();
4618
4828
  const unplugin = defaultInstance.prePlugin;
4619
4829
  const vitePlugin = (options = {}) => {
4620
- const { prePlugin, postPlugin } = createCsszyxPlugins(options);
4621
- return [prePlugin.vite(options), postPlugin.vite(options)];
4830
+ const { prePlugin, postPlugin, cssPlugin } = createCsszyxPlugins(options);
4831
+ return [
4832
+ prePlugin.vite(options),
4833
+ cssPlugin.vite(options),
4834
+ postPlugin.vite(options)
4835
+ ];
4622
4836
  };
4623
4837
  const webpackPlugin = (options = {}) => {
4624
4838
  const { prePlugin, postPlugin } = createCsszyxPlugins(options);
@@ -4688,12 +4902,15 @@ exports.findRSCGraphViolation = findRSCGraphViolation;
4688
4902
  exports.hasInjectableTailwindCandidate = hasInjectableTailwindCandidate;
4689
4903
  exports.hasUseClientDirective = hasUseClientDirective;
4690
4904
  exports.hasUseServerDirective = hasUseServerDirective;
4905
+ exports.isAdvisoryDiagnostic = isAdvisoryDiagnostic;
4691
4906
  exports.isCompileSourceOptedIn = isCompileSourceOptedIn;
4692
4907
  exports.isHardIgnoredPath = isHardIgnoredPath;
4908
+ exports.isMangleableCssId = isMangleableCssId;
4693
4909
  exports.isMonorepoPackage = isMonorepoPackage;
4694
4910
  exports.isPackagesSkippedSource = isPackagesSkippedSource;
4695
4911
  exports.isRSCServerModule = isRSCServerModule;
4696
4912
  exports.isTailwindReservedGlobalVar = isTailwindReservedGlobalVar;
4913
+ exports.lateMangleCensusMessage = lateMangleCensusMessage;
4697
4914
  exports.mangleCodeClassesSync = mangleCodeClassesSync;
4698
4915
  exports.mangleEligibleClasses = mangleEligibleClasses;
4699
4916
  exports.mangleHybridHazardMessage = mangleHybridHazardMessage;
@@ -4701,6 +4918,7 @@ exports.missingTailwindEntryMessage = missingTailwindEntryMessage;
4701
4918
  exports.normalizeGlobalVarAliasesForCache = normalizeGlobalVarAliasesForCache;
4702
4919
  exports.planGlobalVarAliases = planGlobalVarAliases;
4703
4920
  exports.readGlobalVarScanCache = readGlobalVarScanCache;
4921
+ exports.realContentHashDisabledMessage = realContentHashDisabledMessage;
4704
4922
  exports.recordGlobalVarSourceFile = recordGlobalVarSourceFile;
4705
4923
  exports.resolveCompileSourceDirs = resolveCompileSourceDirs;
4706
4924
  exports.resolveGlobalVarScanCacheDir = resolveGlobalVarScanCacheDir;
@@ -4715,9 +4933,11 @@ exports.shouldTrackGlobalVarSources = shouldTrackGlobalVarSources;
4715
4933
  exports.shouldWarnMissingTailwindEntry = shouldWarnMissingTailwindEntry;
4716
4934
  exports.shouldWarnUnscopedMonorepo = shouldWarnUnscopedMonorepo;
4717
4935
  exports.skippedSzFilesMessage = skippedSzFilesMessage;
4936
+ exports.suppressedAdvisoryMessage = suppressedAdvisoryMessage;
4718
4937
  exports.unplugin = unplugin;
4719
4938
  exports.unscopedMonorepoMessage = unscopedMonorepoMessage;
4720
4939
  exports.validateGlobalVarAliasInputs = validateGlobalVarAliasInputs;
4721
4940
  exports.vitePlugin = vitePlugin;
4941
+ exports.watchModeMangleMessage = watchModeMangleMessage;
4722
4942
  exports.webpackPlugin = webpackPlugin;
4723
4943
  exports.writeGlobalVarScanCache = writeGlobalVarScanCache;