@csszyx/unplugin 0.9.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/index.cjs +19 -2
  2. package/dist/index.d.cts +91 -5
  3. package/dist/index.d.mts +90 -3
  4. package/dist/index.mjs +4 -2
  5. package/dist/next-prebuild.cjs +148 -0
  6. package/dist/next-prebuild.d.cts +66 -0
  7. package/dist/next-prebuild.d.mts +66 -0
  8. package/dist/next-prebuild.mjs +131 -0
  9. package/dist/next-turbo-loader.cjs +210 -0
  10. package/dist/next-turbo-loader.d.cts +68 -0
  11. package/dist/next-turbo-loader.d.mts +66 -0
  12. package/dist/next-turbo-loader.mjs +190 -0
  13. package/dist/next-watcher.cjs +237 -0
  14. package/dist/next-watcher.d.cts +106 -0
  15. package/dist/next-watcher.d.mts +106 -0
  16. package/dist/next-watcher.mjs +219 -0
  17. package/dist/shared/unplugin.8er8o6rn.mjs +276 -0
  18. package/dist/shared/unplugin.B_U4rZvG.cjs +281 -0
  19. package/dist/shared/{unplugin.BEOG6ePC.mjs → unplugin.BbtspS8t.mjs} +1436 -324
  20. package/dist/shared/unplugin.BceVw1eW.mjs +184 -0
  21. package/dist/shared/unplugin.BtQzlC2C.mjs +567 -0
  22. package/dist/shared/{unplugin.CL0F6RZa.cjs → unplugin.CFp386gH.cjs} +1456 -327
  23. package/dist/shared/unplugin.CPEWNSA0.d.cts +77 -0
  24. package/dist/shared/unplugin.CPEWNSA0.d.mts +77 -0
  25. package/dist/shared/unplugin.CScQRdTp.d.cts +15 -0
  26. package/dist/shared/unplugin.CScQRdTp.d.mts +15 -0
  27. package/dist/shared/unplugin.CdZxp0x-.d.mts +16 -0
  28. package/dist/shared/unplugin.DLrBgECN.d.cts +282 -0
  29. package/dist/shared/unplugin.DLrBgECN.d.mts +282 -0
  30. package/dist/shared/unplugin.DUxdYaSG.cjs +205 -0
  31. package/dist/shared/unplugin.s62yJbu1.cjs +591 -0
  32. package/dist/shared/unplugin.xeED_qwh.d.cts +16 -0
  33. package/dist/vite.cjs +3 -1
  34. package/dist/vite.d.cts +2 -1
  35. package/dist/vite.d.mts +2 -1
  36. package/dist/vite.mjs +3 -1
  37. package/dist/webpack.cjs +3 -1
  38. package/dist/webpack.d.cts +2 -1
  39. package/dist/webpack.d.mts +2 -1
  40. package/dist/webpack.mjs +3 -1
  41. package/package.json +41 -8
  42. package/dist/shared/unplugin.DUbr5w-N.d.cts +0 -49
  43. package/dist/shared/unplugin.DUbr5w-N.d.mts +0 -49
@@ -5,14 +5,441 @@ import * as path from 'node:path';
5
5
  import { dirname } from 'node:path';
6
6
  import { performance } from 'node:perf_hooks';
7
7
  import { fileURLToPath } from 'node:url';
8
- import { ensureRustTransformAvailable, transformSourceCode, transformRust, transformOxc, transform } from '@csszyx/compiler';
8
+ import { scanGlobalVarUsages, ensureRustTransformAvailable, transformSourceCode, transformRust, transformOxc, transformRustBatch, transform } from '@csszyx/compiler';
9
9
  import { encode, compute_mangle_checksum } from '@csszyx/core';
10
10
  import { preprocess as preprocess$1 } from '@csszyx/svelte-adapter';
11
- import { DEFAULT_BUILD_CONFIG } from '@csszyx/types';
11
+ import { isTailwindReservedCustomProperty, CSSZYX_GLOBAL_ALIAS_PREFIX, isCsszyxGlobalAliasCustomProperty, DEFAULT_BUILD_CONFIG, validateGlobalVarMangleConfig } from '@csszyx/types';
12
12
  import { preprocess } from '@csszyx/vue-adapter';
13
13
  import { createUnplugin } from 'unplugin';
14
14
  import { mangleCSSSync } from '../css-mangler.mjs';
15
15
  import { createHash } from 'node:crypto';
16
+ import { r as resolveTransformCacheDir, c as createTransformCacheKey, a as readTransformCache, w as writeTransformCache, e as evictOldTransformCacheEntries } from './unplugin.BceVw1eW.mjs';
17
+ import postcss from 'postcss';
18
+ import valueParser from 'postcss-value-parser';
19
+
20
+ function resolveGlobalVarScanCacheDir(cacheDir) {
21
+ return path.join(cacheDir, "global-vars");
22
+ }
23
+ function createGlobalVarScanCacheKey(input) {
24
+ const hash = createHash("sha256");
25
+ hash.update(input.filePath);
26
+ hash.update("\0");
27
+ hash.update(String(input.mtimeMs));
28
+ hash.update("\0");
29
+ hash.update(createHash("sha256").update(input.css).digest("hex"));
30
+ return hash.digest("hex");
31
+ }
32
+ function readGlobalVarScanCache(cacheDir, key) {
33
+ try {
34
+ const raw = fs.readFileSync(globalVarScanCacheFile(cacheDir, key), "utf8");
35
+ const entry = JSON.parse(raw);
36
+ if (entry.key !== key || !entry.result) {
37
+ return null;
38
+ }
39
+ return entry.result;
40
+ } catch {
41
+ return null;
42
+ }
43
+ }
44
+ function writeGlobalVarScanCache(cacheDir, key, result) {
45
+ try {
46
+ fs.mkdirSync(cacheDir, { recursive: true });
47
+ fs.writeFileSync(
48
+ globalVarScanCacheFile(cacheDir, key),
49
+ JSON.stringify({ key, result }),
50
+ "utf8"
51
+ );
52
+ } catch {
53
+ }
54
+ }
55
+ function globalVarScanCacheFile(cacheDir, key) {
56
+ return path.join(cacheDir, `${key}.json`);
57
+ }
58
+
59
+ const DEFAULT_SCOPE_ID = "<root>";
60
+ function isInsideThemeAtRule(node) {
61
+ let current = node.parent;
62
+ while (current && current.type !== "root") {
63
+ if (current.type === "atrule" && current.name === "theme") {
64
+ return true;
65
+ }
66
+ current = current.parent;
67
+ }
68
+ return false;
69
+ }
70
+ function hasSiblingDeclaration(decl, prop) {
71
+ const parent = decl.parent;
72
+ if (!parent || !("nodes" in parent)) {
73
+ return false;
74
+ }
75
+ return parent.nodes.some((node) => node.type === "decl" && node.prop === prop);
76
+ }
77
+ function nodeLocation(node, filePath) {
78
+ return {
79
+ filePath,
80
+ line: node.source?.start?.line ?? 1,
81
+ column: node.source?.start?.column ?? 1
82
+ };
83
+ }
84
+ function buildScopeId(node) {
85
+ const parts = [];
86
+ let current = node.parent;
87
+ while (current && current.type !== "root") {
88
+ if (current.type === "rule") {
89
+ parts.push(`rule:${current.selector}`);
90
+ } else if (current.type === "atrule") {
91
+ const atRule = current;
92
+ parts.push(`@${atRule.name} ${atRule.params}`.trim());
93
+ }
94
+ current = current.parent;
95
+ }
96
+ return parts.reverse().join(" > ") || DEFAULT_SCOPE_ID;
97
+ }
98
+
99
+ const VAR_REFERENCE_RE = /var\(\s*(--[\w-]+)/g;
100
+ function scanGlobalVarCss(css, options = {}) {
101
+ const filePath = options.filePath ?? "<inline>";
102
+ const root = postcss.parse(css, { from: filePath });
103
+ const registered = collectRegisteredProperties(root);
104
+ const definitions = [];
105
+ const references = [];
106
+ root.walkDecls((decl) => {
107
+ const scopeId = buildScopeId(decl);
108
+ const tailwindOwned = isInsideThemeAtRule(decl);
109
+ if (decl.prop.startsWith("--")) {
110
+ definitions.push({
111
+ name: decl.prop,
112
+ scopeId,
113
+ tailwindOwned,
114
+ registered: registered.has(decl.prop),
115
+ ...nodeLocation(decl, filePath)
116
+ });
117
+ }
118
+ for (const name of extractVarReferences(decl.value)) {
119
+ references.push({
120
+ name,
121
+ scopeId,
122
+ owner: decl.prop,
123
+ tailwindOwned,
124
+ ...nodeLocation(decl, filePath)
125
+ });
126
+ }
127
+ });
128
+ root.walkAtRules((atRule) => {
129
+ if (atRule.name === "property") {
130
+ return;
131
+ }
132
+ for (const name of extractVarReferences(atRule.params)) {
133
+ references.push({
134
+ name,
135
+ scopeId: buildScopeId(atRule),
136
+ owner: `@${atRule.name}`,
137
+ tailwindOwned: isInsideThemeAtRule(atRule),
138
+ ...nodeLocation(atRule, filePath)
139
+ });
140
+ }
141
+ });
142
+ return {
143
+ filePath,
144
+ definitions,
145
+ references,
146
+ registered: [...registered].sort(),
147
+ thirdParty: isThirdPartyCssPath(filePath)
148
+ };
149
+ }
150
+ function collectRegisteredProperties(root) {
151
+ const registered = /* @__PURE__ */ new Set();
152
+ root.walkAtRules("property", (atRule) => {
153
+ const name = atRule.params.trim();
154
+ if (name.startsWith("--")) {
155
+ registered.add(name);
156
+ }
157
+ });
158
+ return registered;
159
+ }
160
+ function extractVarReferences(value) {
161
+ const references = /* @__PURE__ */ new Set();
162
+ for (const match of value.matchAll(VAR_REFERENCE_RE)) {
163
+ references.add(match[1]);
164
+ }
165
+ return [...references].sort();
166
+ }
167
+ function isThirdPartyCssPath(filePath) {
168
+ return filePath.split(/[\\/]/).includes("node_modules");
169
+ }
170
+
171
+ function planGlobalVarAliases(input) {
172
+ const definitions = flattenDefinitions(input.scans);
173
+ const candidates = collectCandidates(input, definitions);
174
+ const aliasPrefix = input.aliasPrefix ?? CSSZYX_GLOBAL_ALIAS_PREFIX;
175
+ const diagnostics = validateCandidates(
176
+ candidates,
177
+ definitions,
178
+ input.reserved ?? [],
179
+ aliasPrefix
180
+ );
181
+ if (diagnostics.length > 0) {
182
+ return { entries: [], aliases: /* @__PURE__ */ new Map(), diagnostics };
183
+ }
184
+ const definitionNames = new Set(definitions.keys());
185
+ const entries = [];
186
+ for (const [index, original] of candidates.entries()) {
187
+ const alias = `${aliasPrefix}${encode(index)}`;
188
+ if (definitionNames.has(alias)) {
189
+ return {
190
+ entries: [],
191
+ aliases: /* @__PURE__ */ new Map(),
192
+ diagnostics: [
193
+ {
194
+ code: "alias-collision",
195
+ severity: "error",
196
+ name: alias,
197
+ message: `Generated alias ${alias} collides with an existing CSS custom property.`,
198
+ location: definitions.get(alias)?.[0]
199
+ }
200
+ ]
201
+ };
202
+ }
203
+ entries.push({
204
+ original,
205
+ alias,
206
+ scopes: [
207
+ ...new Set((definitions.get(original) ?? []).map((definition) => definition.scopeId))
208
+ ].sort()
209
+ });
210
+ }
211
+ return {
212
+ entries,
213
+ aliases: new Map(entries.map((entry) => [entry.original, entry.alias])),
214
+ diagnostics: []
215
+ };
216
+ }
217
+ function isTailwindReservedGlobalVar(name) {
218
+ return isTailwindReservedCustomProperty(name);
219
+ }
220
+ function flattenDefinitions(scans) {
221
+ const definitions = /* @__PURE__ */ new Map();
222
+ for (const scan of scans) {
223
+ for (const definition of scan.definitions) {
224
+ const existing = definitions.get(definition.name) ?? [];
225
+ existing.push(definition);
226
+ definitions.set(definition.name, existing);
227
+ }
228
+ }
229
+ return definitions;
230
+ }
231
+ function collectCandidates(input, definitions) {
232
+ const candidates = new Set(input.tokens ?? []);
233
+ const autoPrefix = input.autoPrefix ?? "";
234
+ if (autoPrefix !== "") {
235
+ for (const name of definitions.keys()) {
236
+ if (name.startsWith(autoPrefix)) {
237
+ candidates.add(name);
238
+ }
239
+ }
240
+ }
241
+ return [...candidates].sort();
242
+ }
243
+ function validateCandidates(candidates, definitions, reserved, aliasPrefix) {
244
+ const diagnostics = [];
245
+ for (const name of candidates) {
246
+ const tokenDefinitions = definitions.get(name) ?? [];
247
+ if (tokenDefinitions.length === 0) {
248
+ diagnostics.push({
249
+ code: "missing-definition",
250
+ severity: "error",
251
+ name,
252
+ message: `Global variable token ${name} is not defined in scanned CSS.`
253
+ });
254
+ continue;
255
+ }
256
+ if (isTailwindReservedGlobalVar(name) || matchesUserReserved(name, reserved)) {
257
+ diagnostics.push({
258
+ code: "tailwind-reserved",
259
+ severity: "error",
260
+ name,
261
+ message: `Global variable token ${name} is reserved and cannot be aliased.`,
262
+ location: tokenDefinitions[0]
263
+ });
264
+ }
265
+ if (isCsszyxGlobalAliasCustomProperty(name, aliasPrefix)) {
266
+ diagnostics.push({
267
+ code: "tailwind-reserved",
268
+ severity: "error",
269
+ name,
270
+ message: `Global variable token ${name} uses csszyx reserved namespace ${aliasPrefix}* and cannot be aliased.`,
271
+ location: tokenDefinitions[0]
272
+ });
273
+ }
274
+ const tailwindDefinition = tokenDefinitions.find((definition) => definition.tailwindOwned);
275
+ if (tailwindDefinition) {
276
+ diagnostics.push({
277
+ code: "tailwind-owned",
278
+ severity: "error",
279
+ name,
280
+ message: `Global variable token ${name} is declared inside @theme and cannot be aliased.`,
281
+ location: tailwindDefinition
282
+ });
283
+ }
284
+ const registeredDefinition = tokenDefinitions.find((definition) => definition.registered);
285
+ if (registeredDefinition) {
286
+ diagnostics.push({
287
+ code: "registered-property",
288
+ severity: "error",
289
+ name,
290
+ message: `Registered custom property ${name} is not aliasable in Phase H v1.`,
291
+ location: registeredDefinition
292
+ });
293
+ }
294
+ }
295
+ return diagnostics;
296
+ }
297
+ function matchesUserReserved(name, reserved) {
298
+ return reserved.some((pattern) => {
299
+ if (pattern.endsWith("*")) {
300
+ return name.startsWith(pattern.slice(0, -1));
301
+ }
302
+ return name === pattern;
303
+ });
304
+ }
305
+
306
+ function rewriteGlobalVarCssAliases(options) {
307
+ if (options.plan.diagnostics.length > 0 || options.plan.entries.length === 0) {
308
+ return {
309
+ css: options.css,
310
+ aliasDeclarations: 0,
311
+ rewrittenReferences: 0,
312
+ diagnostics: options.plan.diagnostics
313
+ };
314
+ }
315
+ const root = postcss.parse(options.css, { from: options.filePath });
316
+ const aliasNames = new Set(options.plan.entries.map((entry) => entry.alias));
317
+ const referenceAliases = new Map(
318
+ options.plan.entries.filter((entry) => canRewriteGlobalVarReferences(entry)).map((entry) => [entry.original, entry.alias])
319
+ );
320
+ let aliasDeclarations = 0;
321
+ let rewrittenReferences = 0;
322
+ root.walkDecls((decl) => {
323
+ if (isInsideThemeAtRule(decl)) {
324
+ return;
325
+ }
326
+ const alias = options.plan.aliases.get(decl.prop);
327
+ if (alias && !hasSiblingDeclaration(decl, alias)) {
328
+ decl.cloneAfter({ prop: alias, value: `var(${decl.prop})` });
329
+ aliasDeclarations++;
330
+ }
331
+ if (options.plan.aliases.has(decl.prop) || aliasNames.has(decl.prop)) {
332
+ return;
333
+ }
334
+ const rewrite = rewriteGlobalVarValue(decl.value, referenceAliases);
335
+ if (rewrite.count > 0) {
336
+ decl.value = rewrite.value;
337
+ rewrittenReferences += rewrite.count;
338
+ }
339
+ });
340
+ return {
341
+ css: root.toString(),
342
+ aliasDeclarations,
343
+ rewrittenReferences,
344
+ diagnostics: []
345
+ };
346
+ }
347
+ function canRewriteGlobalVarReferences(entry) {
348
+ return entry.scopes.some(isInheritedGlobalAliasScope);
349
+ }
350
+ function isInheritedGlobalAliasScope(scope) {
351
+ const leaf = scope.split(" > ").at(-1);
352
+ if (!leaf?.startsWith("rule:")) {
353
+ return false;
354
+ }
355
+ const selector = leaf.slice("rule:".length);
356
+ return selector.split(",").some((part) => {
357
+ const normalized = part.trim();
358
+ return normalized === ":root" || normalized === "html" || normalized === "body";
359
+ });
360
+ }
361
+ function rewriteGlobalVarValue(value, aliases) {
362
+ const parsed = valueParser(value);
363
+ let count = 0;
364
+ parsed.walk((node) => {
365
+ if (node.type !== "function" || node.value.toLowerCase() !== "var") {
366
+ return;
367
+ }
368
+ const firstArgument = node.nodes.find((child) => child.type !== "space");
369
+ if (!firstArgument || firstArgument.type !== "word") {
370
+ return;
371
+ }
372
+ const alias = aliases.get(firstArgument.value);
373
+ if (!alias) {
374
+ return;
375
+ }
376
+ firstArgument.value = alias;
377
+ count++;
378
+ });
379
+ return { value: count > 0 ? valueParser.stringify(parsed.nodes) : value, count };
380
+ }
381
+
382
+ function validateGlobalVarAliasInputs(options) {
383
+ const scans = options.cssFiles.map(
384
+ (file) => scanCssSourceWithOptionalCache(file, options.cacheDir)
385
+ );
386
+ const plan = planGlobalVarAliases({
387
+ scans,
388
+ tokens: options.tokens,
389
+ autoPrefix: options.autoPrefix,
390
+ aliasPrefix: options.aliasPrefix,
391
+ reserved: options.reserved
392
+ });
393
+ if (plan.diagnostics.length > 0 || plan.entries.length === 0) {
394
+ return { scans, plan, usageDiagnostics: [] };
395
+ }
396
+ const candidateTokens = plan.entries.map((entry) => entry.original);
397
+ const usageDiagnostics = (options.sourceFiles ?? []).flatMap(
398
+ (file) => scanGlobalVarUsages(file.code, file.filePath, { tokens: candidateTokens })
399
+ );
400
+ return { scans, plan, usageDiagnostics };
401
+ }
402
+ function createGlobalVarAliasValidationOptions(input) {
403
+ return {
404
+ cssFiles: input.cssAssets.filter((asset) => /\.css(?:$|\?)/.test(asset.fileName)).map((asset) => ({
405
+ filePath: normalizeBuildAssetPath(input.rootDir, asset.fileName),
406
+ css: cssAssetSourceToString(asset.source),
407
+ mtimeMs: asset.mtimeMs
408
+ })),
409
+ sourceFiles: input.sourceFiles ?? [],
410
+ tokens: input.tokens,
411
+ autoPrefix: input.autoPrefix,
412
+ aliasPrefix: input.aliasPrefix,
413
+ reserved: input.reserved,
414
+ cacheDir: input.cacheDir
415
+ };
416
+ }
417
+ function scanCssSourceWithOptionalCache(file, cacheDir) {
418
+ if (cacheDir === void 0 || file.mtimeMs === void 0) {
419
+ return scanGlobalVarCss(file.css, { filePath: file.filePath });
420
+ }
421
+ const key = createGlobalVarScanCacheKey({
422
+ filePath: file.filePath,
423
+ css: file.css,
424
+ mtimeMs: file.mtimeMs
425
+ });
426
+ const cached = readGlobalVarScanCache(cacheDir, key);
427
+ if (cached) {
428
+ return cached;
429
+ }
430
+ const result = scanGlobalVarCss(file.css, { filePath: file.filePath });
431
+ writeGlobalVarScanCache(cacheDir, key, result);
432
+ return result;
433
+ }
434
+ function normalizeBuildAssetPath(rootDir, fileName) {
435
+ return (path.isAbsolute(fileName) ? fileName : path.join(rootDir, fileName)).replace(
436
+ /\\/g,
437
+ "/"
438
+ );
439
+ }
440
+ function cssAssetSourceToString(source) {
441
+ return typeof source === "string" ? source : Buffer.from(source).toString("utf8");
442
+ }
16
443
 
17
444
  const SERVER_DIRECTIVE_RE = /^['"]use server['"];?$/;
18
445
  const CLIENT_DIRECTIVE_RE = /^['"]use client['"];?$/;
@@ -253,24 +680,18 @@ function skipWhitespaceAndComments(code, start) {
253
680
  function findRuntimeImports(code) {
254
681
  const imports = [];
255
682
  const scanCode = stripCommentsForImportScan(code);
256
- const staticImportRe = /import\s+(?!type\b)(\S(?:.*\S)?)\s+from\s+['"]([^'"]+)['"]/g;
257
- const sideEffectImportRe = /import\s+['"]([^'"]+)['"]/g;
258
683
  const dynamicImportRe = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
259
- for (const match of scanCode.matchAll(staticImportRe)) {
260
- const clause = match[1];
261
- const source = match[2];
684
+ for (const { clause, source } of findStaticImports(scanCode)) {
262
685
  if (!isRuntimeImportSource(source)) {
263
686
  continue;
264
687
  }
265
- imports.push({ source, symbols: readRuntimeImportSymbols(source, clause) });
266
- }
267
- for (const match of scanCode.matchAll(sideEffectImportRe)) {
268
- const source = match[1];
269
- if (isRuntimeImportSource(source)) {
688
+ if (clause === null) {
270
689
  imports.push({
271
690
  source,
272
691
  symbols: isWholeRuntimeModuleForbidden(source) ? Array.from(FORBIDDEN_SYMBOLS) : []
273
692
  });
693
+ } else {
694
+ imports.push({ source, symbols: readRuntimeImportSymbols(source, clause) });
274
695
  }
275
696
  }
276
697
  for (const match of scanCode.matchAll(dynamicImportRe)) {
@@ -281,6 +702,98 @@ function findRuntimeImports(code) {
281
702
  }
282
703
  return imports;
283
704
  }
705
+ function findStaticImports(code) {
706
+ const imports = [];
707
+ let cursor = 0;
708
+ while (cursor < code.length) {
709
+ const importStart = code.indexOf("import", cursor);
710
+ if (importStart === -1) {
711
+ break;
712
+ }
713
+ cursor = importStart + 6;
714
+ if (importStart > 0 && isIdentifierPart(code.charCodeAt(importStart - 1)) || isIdentifierPart(code.charCodeAt(cursor))) {
715
+ continue;
716
+ }
717
+ let position = skipAsciiWhitespace(code, cursor);
718
+ const opener = code.charAt(position);
719
+ if (opener === "(") {
720
+ continue;
721
+ }
722
+ if (opener === '"' || opener === "'") {
723
+ const literal2 = readQuotedString(code, position);
724
+ if (literal2) {
725
+ imports.push({ clause: null, source: literal2.value });
726
+ cursor = literal2.end;
727
+ }
728
+ continue;
729
+ }
730
+ const clauseStart = position;
731
+ let fromStart = -1;
732
+ while (position < code.length) {
733
+ if (code.startsWith("from", position) && (position === clauseStart || !isIdentifierPart(code.charCodeAt(position - 1))) && !isIdentifierPart(code.charCodeAt(position + 4))) {
734
+ fromStart = position;
735
+ break;
736
+ }
737
+ if (code.charAt(position) === ";") {
738
+ break;
739
+ }
740
+ position += 1;
741
+ }
742
+ if (fromStart === -1) {
743
+ continue;
744
+ }
745
+ const clause = code.slice(clauseStart, fromStart).trim();
746
+ if (splitAsciiWhitespace(clause)[0] === "type") {
747
+ cursor = fromStart + 4;
748
+ continue;
749
+ }
750
+ position = skipAsciiWhitespace(code, fromStart + 4);
751
+ const literal = readQuotedString(code, position);
752
+ if (literal) {
753
+ imports.push({ clause, source: literal.value });
754
+ cursor = literal.end;
755
+ }
756
+ }
757
+ return imports;
758
+ }
759
+ function readQuotedString(code, start) {
760
+ const quote = code.charAt(start);
761
+ if (quote !== '"' && quote !== "'") {
762
+ return null;
763
+ }
764
+ let value = "";
765
+ for (let index = start + 1; index < code.length; index += 1) {
766
+ const char = code.charAt(index);
767
+ if (char === "\\") {
768
+ if (index + 1 >= code.length) {
769
+ return null;
770
+ }
771
+ value += code.charAt(index + 1);
772
+ index += 1;
773
+ } else if (char === quote) {
774
+ return { end: index + 1, value };
775
+ } else if (char === "\n" || char === "\r") {
776
+ return null;
777
+ } else {
778
+ value += char;
779
+ }
780
+ }
781
+ return null;
782
+ }
783
+ function skipAsciiWhitespace(code, start) {
784
+ let index = start;
785
+ while (index < code.length) {
786
+ const charCode = code.charCodeAt(index);
787
+ if (charCode !== 9 && charCode !== 10 && charCode !== 13 && charCode !== 32) {
788
+ break;
789
+ }
790
+ index += 1;
791
+ }
792
+ return index;
793
+ }
794
+ function isIdentifierPart(code) {
795
+ return isIdentifierStart(code) || code >= 48 && code <= 57;
796
+ }
284
797
  function isRuntimeImportSource(source) {
285
798
  return RUNTIME_HELPER_MODULES.has(source) || source.startsWith("@csszyx/runtime/") || CLIENT_RUNTIME_MODULES.has(source) || CLIENT_RUNTIME_MODULE_ROOTS.some((root) => source === root || source.startsWith(`${root}/`));
286
799
  }
@@ -344,10 +857,17 @@ function resolveLocalModule(importer, source) {
344
857
  path.join(base, "index.js")
345
858
  ];
346
859
  for (const candidate of candidates) {
347
- if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
860
+ try {
861
+ if (!fs.statSync(candidate).isFile()) {
862
+ continue;
863
+ }
348
864
  const resolved = normalizeModuleId(candidate);
349
865
  resolvedLocalModuleCache.set(cacheKey, resolved);
350
866
  return resolved;
867
+ } catch (error) {
868
+ if (error.code !== "ENOENT") {
869
+ throw error;
870
+ }
351
871
  }
352
872
  }
353
873
  return null;
@@ -367,32 +887,67 @@ function pruneRSCModulePathCaches(moduleIds) {
367
887
  }
368
888
  function readImportedSymbols(clause) {
369
889
  const symbols = [];
370
- const named = clause.match(/\{([\s\S]*?)\}/);
371
- if (named) {
372
- for (const part of named[1].split(",")) {
890
+ const openBrace = clause.indexOf("{");
891
+ const closeBrace = openBrace === -1 ? -1 : clause.indexOf("}", openBrace);
892
+ if (openBrace !== -1 && closeBrace !== -1) {
893
+ const namedPart = clause.slice(openBrace + 1, closeBrace);
894
+ for (const part of namedPart.split(",")) {
373
895
  const trimmed = part.trim();
374
896
  if (!trimmed || trimmed.startsWith("type ")) {
375
897
  continue;
376
898
  }
377
- const sourceName = trimmed.replace(/^type\s+/, "").split(/\s+as\s+/)[0]?.trim();
899
+ const sourceName = trimmed.replace(/^type[ \t]+/, "").split(/[ \t]+as[ \t]+/)[0]?.trim();
378
900
  if (sourceName) {
379
901
  symbols.push(sourceName);
380
902
  }
381
903
  }
382
904
  }
383
- if (/\*\s+as\s+\w+/.test(clause)) {
905
+ const namespaceParts = splitAsciiWhitespace(clause);
906
+ if (namespaceParts.length >= 3 && namespaceParts[0] === "*" && namespaceParts[1] === "as" && isIdentifier(namespaceParts[2] ?? "")) {
384
907
  symbols.push(...FORBIDDEN_SYMBOLS);
385
908
  }
386
909
  const braceStart = clause.indexOf("{");
387
910
  const braceEnd = clause.indexOf("}", braceStart);
388
911
  const stripped = braceStart !== -1 && braceEnd !== -1 ? clause.slice(0, braceStart) + clause.slice(braceEnd + 1) : clause;
389
- const defaultImport = stripped.match(/^\s*([A-Z_$][\w$]*)\s*(?:,|$)/i);
390
- const defaultSymbol = defaultImport?.[1];
912
+ const defaultCandidate = stripped.trimStart().split(",", 1)[0]?.trim() ?? "";
913
+ const defaultSymbol = isIdentifier(defaultCandidate) ? defaultCandidate : void 0;
391
914
  if (defaultSymbol && FORBIDDEN_SYMBOLS.has(defaultSymbol)) {
392
915
  symbols.push(defaultSymbol);
393
916
  }
394
917
  return symbols;
395
918
  }
919
+ function isIdentifier(value) {
920
+ if (value.length === 0 || !isIdentifierStart(value.charCodeAt(0))) {
921
+ return false;
922
+ }
923
+ for (let index = 1; index < value.length; index += 1) {
924
+ const code = value.charCodeAt(index);
925
+ if (!isIdentifierStart(code) && (code < 48 || code > 57)) {
926
+ return false;
927
+ }
928
+ }
929
+ return true;
930
+ }
931
+ function isIdentifierStart(code) {
932
+ return code === 36 || code === 95 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
933
+ }
934
+ function splitAsciiWhitespace(value) {
935
+ const parts = [];
936
+ let start = -1;
937
+ for (let index = 0; index <= value.length; index += 1) {
938
+ const code = index < value.length ? value.charCodeAt(index) : 32;
939
+ const whitespace = code === 9 || code === 10 || code === 13 || code === 32;
940
+ if (whitespace) {
941
+ if (start !== -1) {
942
+ parts.push(value.slice(start, index));
943
+ start = -1;
944
+ }
945
+ } else if (start === -1) {
946
+ start = index;
947
+ }
948
+ }
949
+ return parts;
950
+ }
396
951
  function stripCommentsForImportScan(code) {
397
952
  let out = "";
398
953
  let i = 0;
@@ -704,6 +1259,10 @@ function expandFilePatterns(rootDir, patterns) {
704
1259
  return Array.from(files).sort();
705
1260
  }
706
1261
 
1262
+ function safeJsonForScriptTag(value, prettyPrint = false) {
1263
+ const json = prettyPrint ? JSON.stringify(value, null, 2) : JSON.stringify(value);
1264
+ return json.replace(/</g, "\\u003C").replace(/>/g, "\\u003E").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
1265
+ }
707
1266
  function injectChecksum(html, checksum, minify = false) {
708
1267
  const attrName = minify ? "data-sz-cs" : "data-sz-checksum";
709
1268
  const htmlTagPattern = /<html([^>]*)>/i;
@@ -716,10 +1275,18 @@ function injectChecksum(html, checksum, minify = false) {
716
1275
  return html.replace(htmlTagPattern, `<html${checksumAttr}${existingAttrs}>`);
717
1276
  }
718
1277
  function injectMangleMapScript(html, mangleMap, options = {}) {
719
- const { prettyPrint = false } = options;
720
- const jsonContent = prettyPrint ? JSON.stringify(mangleMap, null, 2) : JSON.stringify(mangleMap);
1278
+ const {
1279
+ prettyPrint = false,
1280
+ varMangleMap = {},
1281
+ globalVarAliasPrefix = CSSZYX_GLOBAL_ALIAS_PREFIX
1282
+ } = options;
1283
+ const checksumMap = createHydrationMangleMap(mangleMap, varMangleMap);
1284
+ const jsonContent = safeJsonForScriptTag(checksumMap, prettyPrint);
1285
+ const classMapContent = safeJsonForScriptTag(mangleMap);
1286
+ const varMapContent = safeJsonForScriptTag(varMangleMap);
721
1287
  const scriptTag = `<script id="__CSSZYX_MANGLE_MAP__" type="application/json">${jsonContent}<\/script>`;
722
- const debugScript = `<script>(function(){var m=${jsonContent};var r={};for(var k in m)r[m[k]]=k;var cs=document.documentElement.getAttribute("data-sz-checksum")||"";window.__csszyx={mangleMap:m,checksum:cs,decode:function(c){return r[c]},encode:function(c){return m[c]},decodeAll:function(el){return(el.className||"").split(" ").map(function(c){return r[c]||c})}}})()<\/script>`;
1288
+ const prefixContent = safeJsonForScriptTag(globalVarAliasPrefix);
1289
+ const debugScript = `<script>(function(){var m=${classMapContent};var vm=${varMapContent};var gp=${prefixContent};var r={};var vr={};for(var k in m)r[m[k]]=k;for(var vk in vm){var vv=vm[vk];var vs=Array.isArray(vv)?vv:[vv];for(var vi=0;vi<vs.length;vi++)(vr[vs[vi]]||(vr[vs[vi]]=[])).push(vk)}var cs=document.documentElement.getAttribute("data-sz-checksum")||"";window.__csszyx={mangleMap:m,varMangleMap:vm,checksum:cs,decode:function(c){return r[c]},encode:function(c){return m[c]},decodeVar:function(v){return vr[v]||[]},encodeVar:function(v){return vm[v]},decodeGlobalVar:function(v){var a=vr[v]||[];return v.indexOf(gp)===0?a[0]:void 0},decodeAll:function(el){return(el.className||"").split(" ").map(function(c){return r[c]||c})}}})()<\/script>`;
723
1290
  const combined = `${scriptTag}
724
1291
  ${debugScript}`;
725
1292
  if (html.includes("</head>")) {
@@ -731,9 +1298,9 @@ ${debugScript}`;
731
1298
  }
732
1299
  return html + combined;
733
1300
  }
734
- function injectMangleMapAttribute(html, mangleMap, minify = false) {
1301
+ function injectMangleMapAttribute(html, mangleMap, minify = false, varMangleMap = {}) {
735
1302
  const attrName = minify ? "data-sz-m" : "data-sz-map";
736
- const jsonContent = JSON.stringify(mangleMap);
1303
+ const jsonContent = JSON.stringify(createHydrationMangleMap(mangleMap, varMangleMap));
737
1304
  const htmlTagPattern = /<html([^>]*)>/i;
738
1305
  const match = html.match(htmlTagPattern);
739
1306
  if (!match) {
@@ -748,11 +1315,11 @@ function injectHydrationData(html, mangleMap, checksum, options = {}) {
748
1315
  let result = html;
749
1316
  result = injectChecksum(result, checksum, minify);
750
1317
  if (mode === "inline") {
751
- result = injectMangleMapAttribute(result, mangleMap, minify);
1318
+ result = injectMangleMapAttribute(result, mangleMap, minify, options.varMangleMap);
752
1319
  } else if (mode === "script") {
753
1320
  result = injectMangleMapScript(result, mangleMap, options);
754
1321
  } else if (mode === "both") {
755
- result = injectMangleMapAttribute(result, mangleMap, minify);
1322
+ result = injectMangleMapAttribute(result, mangleMap, minify, options.varMangleMap);
756
1323
  result = injectMangleMapScript(result, mangleMap, options);
757
1324
  }
758
1325
  return result;
@@ -760,6 +1327,25 @@ function injectHydrationData(html, mangleMap, checksum, options = {}) {
760
1327
  function transformIndexHtml(html, mangleMap, checksum, options = {}) {
761
1328
  return injectHydrationData(html, mangleMap, checksum, options);
762
1329
  }
1330
+ function createHydrationMangleMap(classMap, varMap = {}) {
1331
+ if (Object.keys(varMap).length === 0) {
1332
+ return classMap;
1333
+ }
1334
+ const payload = {};
1335
+ for (const [key, value] of Object.entries(classMap)) {
1336
+ payload[`class:${key}`] = value;
1337
+ }
1338
+ for (const [key, value] of Object.entries(varMap)) {
1339
+ if (Array.isArray(value)) {
1340
+ for (const mangled of value) {
1341
+ payload[`var:${key}:${mangled}`] = mangled;
1342
+ }
1343
+ } else {
1344
+ payload[`var:${key}`] = value;
1345
+ }
1346
+ }
1347
+ return payload;
1348
+ }
763
1349
  function buildRecoveryManifest(tokens, options) {
764
1350
  const stripped = options.production === true;
765
1351
  const strippedDevOnlyPaths = [];
@@ -801,6 +1387,36 @@ function injectRecoveryManifest(html, manifest) {
801
1387
  return html + scriptTag;
802
1388
  }
803
1389
 
1390
+ const DEFAULT_MAX_ATTEMPTS = 3;
1391
+ const NODE_FS = {
1392
+ openSync: fs.openSync,
1393
+ fstatSync: (fd, options) => fs.fstatSync(fd, options),
1394
+ readFileSync: (fd, encoding) => fs.readFileSync(fd, encoding),
1395
+ closeSync: fs.closeSync
1396
+ };
1397
+ function readStableTextFileSnapshotSync(file, maxAttempts = DEFAULT_MAX_ATTEMPTS, fsApi = NODE_FS) {
1398
+ if (!Number.isInteger(maxAttempts) || maxAttempts < 1) {
1399
+ throw new RangeError("maxAttempts must be a positive integer.");
1400
+ }
1401
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
1402
+ const fd = fsApi.openSync(file, "r");
1403
+ try {
1404
+ const before = fsApi.fstatSync(fd, { bigint: true });
1405
+ const source = fsApi.readFileSync(fd, "utf8");
1406
+ const after = fsApi.fstatSync(fd, { bigint: true });
1407
+ if (isSameFileVersion(before, after)) {
1408
+ return { source, mtimeMs: Number(after.mtimeMs) };
1409
+ }
1410
+ } finally {
1411
+ fsApi.closeSync(fd);
1412
+ }
1413
+ }
1414
+ throw new Error(`CSS source changed while being read: ${file}`);
1415
+ }
1416
+ function isSameFileVersion(before, after) {
1417
+ return before.dev === after.dev && before.ino === after.ino && before.size === after.size && before.mtimeNs === after.mtimeNs && before.ctimeNs === after.ctimeNs;
1418
+ }
1419
+
804
1420
  function generateThemeDts(opts) {
805
1421
  const { theme, sourceFiles } = opts;
806
1422
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
@@ -847,219 +1463,368 @@ function writeThemeDts(opts) {
847
1463
  writeFileSync(opts.outputPath, content, "utf-8");
848
1464
  }
849
1465
 
850
- const CACHE_SCHEMA_VERSION = 2;
851
- function resolveTransformCacheDir(rootDir, cacheDir) {
852
- return path.resolve(rootDir, cacheDir ?? ".csszyx/cache", "transform");
853
- }
854
- function createTransformCacheKey(input) {
855
- const inputSha256 = createHash("sha256").update(input.source).digest("hex");
856
- const keyMaterial = [
857
- `schema=${CACHE_SCHEMA_VERSION}`,
858
- `plugin=${input.pluginVersion}`,
859
- `compiler=${input.compilerVersion}`,
860
- `parser=${input.parserMode}`,
861
- `producer=${input.producer}`,
862
- `astBudget=${input.astBudget ?? "default"}`,
863
- `filename=${input.filename}`,
864
- `source=${inputSha256}`
865
- ].join("\n");
866
- return {
867
- key: createHash("sha256").update(keyMaterial).digest("hex").slice(0, 16),
868
- inputSha256
869
- };
1466
+ const VIRTUAL_MODULE_ID = "virtual:csszyx/mangle-map";
1467
+ const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
1468
+ const VIRTUAL_CHECKSUM_ID = "virtual:csszyx/checksum";
1469
+ const RESOLVED_VIRTUAL_CHECKSUM_ID = `\0${VIRTUAL_CHECKSUM_ID}`;
1470
+ function createMangleMapModule(mangleMap, checksum, varMangleMap = {}, cssVarMetrics = null) {
1471
+ return `/**
1472
+ * Auto-generated mangle map for csszyx.
1473
+ * This module is generated at build time and contains the mapping
1474
+ * from original class names and CSS variable names to mangled names.
1475
+ *
1476
+ * @generated
1477
+ */
1478
+
1479
+ export const mangleMap = ${JSON.stringify(mangleMap, null, 2)};
1480
+
1481
+ export const varMangleMap = ${JSON.stringify(varMangleMap, null, 2)};
1482
+
1483
+ export const cssVarMetrics = ${JSON.stringify(cssVarMetrics, null, 2)};
1484
+
1485
+ export const checksum = ${JSON.stringify(checksum)};
1486
+
1487
+ export default {
1488
+ mangleMap,
1489
+ varMangleMap,
1490
+ cssVarMetrics,
1491
+ checksum,
1492
+ };
1493
+ `;
870
1494
  }
871
- function readTransformCache(cacheRoot, input, precomputedKey) {
872
- const { key, inputSha256 } = precomputedKey ?? createTransformCacheKey(input);
873
- const file = cacheEntryPath(cacheRoot, key);
874
- let entry;
875
- try {
876
- entry = JSON.parse(fs.readFileSync(file, "utf8"));
877
- } catch {
878
- return null;
1495
+ function createChecksumModule(checksum) {
1496
+ return `/**
1497
+ * Auto-generated checksum for csszyx mangle map.
1498
+ *
1499
+ * @generated
1500
+ */
1501
+
1502
+ export const checksum = ${JSON.stringify(checksum)};
1503
+
1504
+ export default checksum;
1505
+ `;
1506
+ }
1507
+ function isVirtualModule(id) {
1508
+ return id === VIRTUAL_MODULE_ID || id === VIRTUAL_CHECKSUM_ID;
1509
+ }
1510
+ function resolveVirtualModule(id) {
1511
+ if (id === VIRTUAL_MODULE_ID) {
1512
+ return RESOLVED_VIRTUAL_MODULE_ID;
879
1513
  }
880
- if (entry.version !== CACHE_SCHEMA_VERSION || entry.pluginVersion !== input.pluginVersion || entry.compilerVersion !== input.compilerVersion || entry.parserMode !== input.parserMode || entry.producer !== input.producer || entry.astBudget !== (input.astBudget ?? null) || entry.filename !== input.filename || entry.inputSha256 !== inputSha256) {
881
- return null;
1514
+ if (id === VIRTUAL_CHECKSUM_ID) {
1515
+ return RESOLVED_VIRTUAL_CHECKSUM_ID;
882
1516
  }
883
- return deserializeResult(entry.result);
1517
+ return void 0;
884
1518
  }
885
- function writeTransformCache(cacheRoot, input, result, precomputedKey) {
886
- const { key, inputSha256 } = precomputedKey ?? createTransformCacheKey(input);
887
- const file = cacheEntryPath(cacheRoot, key);
888
- const dir = path.dirname(file);
889
- const tmp = path.join(
890
- dir,
891
- `.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`
892
- );
893
- const entry = {
894
- version: CACHE_SCHEMA_VERSION,
895
- pluginVersion: input.pluginVersion,
896
- compilerVersion: input.compilerVersion,
897
- parserMode: input.parserMode,
898
- producer: input.producer,
899
- astBudget: input.astBudget ?? null,
900
- filename: input.filename,
901
- inputSha256,
902
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
903
- result: serializeResult(result)
904
- };
905
- try {
906
- fs.mkdirSync(dir, { recursive: true });
907
- fs.writeFileSync(tmp, JSON.stringify(entry), "utf8");
908
- fs.renameSync(tmp, file);
909
- } catch {
910
- try {
911
- fs.rmSync(tmp, { force: true });
912
- } catch {
1519
+
1520
+ const CHECKSUM_PLACEHOLDER = "___CSSZYX_CHECKSUM___";
1521
+ const MANGLE_MAP_PLACEHOLDER = "___CSSZYX_MANGLE_MAP___";
1522
+ const VAR_MANGLE_MAP_PLACEHOLDER = "___CSSZYX_VAR_MANGLE_MAP___";
1523
+ const UNKNOWN_PACKAGE_VERSION = "0.0.0";
1524
+ const TRANSFORM_CACHE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
1525
+ const TRANSFORM_CACHE_MAX_ENTRIES = 1e4;
1526
+ const TRANSFORM_MEMORY_CACHE_MAX_ENTRIES = 1e3;
1527
+ const DEFAULT_VAR_MANGLE_MAP_MAX_BYTES = 100 * 1024;
1528
+ const GLOBAL_VAR_ALIAS_MAP_OWNER = "\0csszyx:global-var-aliases";
1529
+ const DIRECTIVE_PROLOGUE_PREFIX_RE = /^((?:\s|\/\/[^\n]*\n|\/\*(?:[^*]|\*(?!\/))*\*\/)*)(['"]use (?:client|server)['"];?\s*)/;
1530
+ const RUNTIME_HELPER_IMPORT_RE = {
1531
+ _sz: /\{[^}]*\b_sz\b[^}]*\}\s*from\s*['"]@csszyx\/runtime['"]/,
1532
+ _szMerge: /\{[^}]*\b_szMerge\b[^}]*\}\s*from\s*['"]@csszyx\/runtime['"]/,
1533
+ __szColorVar: /\{[^}]*\b__szColorVar\b[^}]*\}\s*from\s*['"]@csszyx\/runtime['"]/
1534
+ };
1535
+ let _hasWarnedTsConfig = false;
1536
+ let _hasWarnedTransformCacheVersion = false;
1537
+ const requireFromHere = createRequire(import.meta.url);
1538
+ const PLUGIN_VERSION = findPackageVersionFromFile(
1539
+ fileURLToPath(import.meta.url),
1540
+ UNKNOWN_PACKAGE_VERSION
1541
+ );
1542
+ const COMPILER_VERSION = findPackageVersionFromModule("@csszyx/compiler", UNKNOWN_PACKAGE_VERSION);
1543
+ const BENCH_TRACE_ENABLED = process.env.CSSZYX_BENCH_TRACE === "1";
1544
+ const BENCH_TRACE_FILE = process.env.CSSZYX_BENCH_TRACE_FILE;
1545
+ function cssVariableEntries(result) {
1546
+ const entries = [];
1547
+ for (const [original, value] of result.cssVariableMap ?? []) {
1548
+ if (Array.isArray(value)) {
1549
+ for (const mangled of value) {
1550
+ entries.push([original, mangled]);
1551
+ }
1552
+ } else {
1553
+ entries.push([original, value]);
913
1554
  }
914
1555
  }
1556
+ return entries;
1557
+ }
1558
+ function recordFileVarMangleEntries(state, filename, entries) {
1559
+ const normalizedFilename = normalizeSourceFilename(filename);
1560
+ if (entries.length === 0) {
1561
+ state.varMangleEntriesByFile.delete(normalizedFilename);
1562
+ } else {
1563
+ state.varMangleEntriesByFile.set(normalizedFilename, entries);
1564
+ }
1565
+ state.varMangleMap = buildVarMangleMap(state.varMangleEntriesByFile);
915
1566
  }
916
- function evictOldTransformCacheEntries(cacheRoot, options) {
917
- let deleted = 0;
918
- const now = options.now ?? Date.now();
919
- const survivors = [];
920
- for (const file of listJsonFiles(cacheRoot)) {
1567
+ function recordGlobalVarSourceFile(state, filename, code) {
1568
+ const normalizedFilename = normalizeSourceFilename(filename);
1569
+ if (!/\.[tj]sx?(?:\?.*)?$/.test(normalizedFilename)) {
1570
+ return;
1571
+ }
1572
+ if (code === null) {
1573
+ state.globalVarSourceFilesByFile.delete(normalizedFilename);
1574
+ } else {
1575
+ state.globalVarSourceFilesByFile.set(normalizedFilename, code);
1576
+ }
1577
+ }
1578
+ function buildGlobalVarSourceFiles(state) {
1579
+ return [...state.globalVarSourceFilesByFile.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([filePath, code]) => ({ filePath, code }));
1580
+ }
1581
+ function collectRollupGlobalVarCssAssets(bundle) {
1582
+ return Object.values(bundle).filter(
1583
+ (chunk) => chunk.type === "asset" && typeof chunk.fileName === "string" && /\.css(?:$|\?)/.test(chunk.fileName) && (typeof chunk.source === "string" || chunk.source instanceof Uint8Array)
1584
+ ).sort((left, right) => left.fileName.localeCompare(right.fileName)).map((asset) => ({
1585
+ fileName: asset.fileName,
1586
+ source: asset.source
1587
+ }));
1588
+ }
1589
+ function collectConfiguredGlobalVarCssSources(rootDir, scanCss) {
1590
+ if (!scanCss) {
1591
+ return [];
1592
+ }
1593
+ return expandFilePatterns(rootDir, scanCss).filter((file) => file.endsWith(".css")).sort((left, right) => left.localeCompare(right)).flatMap((file) => {
921
1594
  try {
922
- const entry = JSON.parse(fs.readFileSync(file, "utf8"));
923
- const timestamp = typeof entry.timestamp === "string" ? Date.parse(entry.timestamp) : 0;
924
- if (!Number.isFinite(timestamp) || now - timestamp > options.maxAgeMs) {
925
- fs.rmSync(file, { force: true });
926
- deleted++;
927
- } else {
928
- survivors.push({ file, timestamp });
929
- }
1595
+ const snapshot = readStableTextFileSnapshotSync(file);
1596
+ return [
1597
+ {
1598
+ fileName: file,
1599
+ source: snapshot.source,
1600
+ mtimeMs: snapshot.mtimeMs
1601
+ }
1602
+ ];
930
1603
  } catch {
931
- fs.rmSync(file, { force: true });
932
- deleted++;
1604
+ return [];
1605
+ }
1606
+ });
1607
+ }
1608
+ function collectWebpackGlobalVarCssAssets(assets) {
1609
+ return Object.entries(assets).flatMap(([fileName, asset]) => {
1610
+ if (!/\.css(?:$|\?)/.test(fileName)) {
1611
+ return [];
1612
+ }
1613
+ const source = asset.source();
1614
+ if (typeof source !== "string" && !(source instanceof Uint8Array)) {
1615
+ return [];
1616
+ }
1617
+ return [{ fileName, source }];
1618
+ }).sort((left, right) => left.fileName.localeCompare(right.fileName)).map(({ fileName, source }) => ({ fileName, source }));
1619
+ }
1620
+ function assertNoGlobalVarAliasValidationErrors(result) {
1621
+ const messages = [
1622
+ ...result.plan.diagnostics.map((diagnostic) => {
1623
+ const location = diagnostic.location ? ` (${diagnostic.location.filePath}:${diagnostic.location.line}:${diagnostic.location.column})` : "";
1624
+ return `[${diagnostic.code}] ${diagnostic.name}${location}: ${diagnostic.message}`;
1625
+ }),
1626
+ ...result.usageDiagnostics.map((diagnostic) => {
1627
+ const location = diagnostic.location;
1628
+ return `[${diagnostic.kind}] ${diagnostic.name} (${location.filePath}:${location.line}:${location.column}): ${diagnostic.message}`;
1629
+ })
1630
+ ];
1631
+ if (messages.length > 0) {
1632
+ throw new Error(
1633
+ `[csszyx] production.mangleGlobalVars validation failed:
1634
+ ${messages.join("\n")}`
1635
+ );
1636
+ }
1637
+ }
1638
+ function rewriteCssWithValidatedGlobalVarPlan(css, filePath, result) {
1639
+ if (result === null || result.plan.entries.length === 0) {
1640
+ return css;
1641
+ }
1642
+ const rewrite = rewriteGlobalVarCssAliases({
1643
+ css,
1644
+ plan: result.plan,
1645
+ filePath
1646
+ });
1647
+ assertNoGlobalVarAliasValidationErrors({
1648
+ scans: result.scans,
1649
+ plan: {
1650
+ ...result.plan,
1651
+ diagnostics: rewrite.diagnostics
1652
+ },
1653
+ usageDiagnostics: []
1654
+ });
1655
+ return rewrite.css;
1656
+ }
1657
+ function assertGlobalVarPlanMatchesEarlyAliases(result, expectedEntries) {
1658
+ const actualEntries = [...result.plan.aliases.entries()].sort(
1659
+ ([left], [right]) => left.localeCompare(right)
1660
+ );
1661
+ const expected = expectedEntries.map(([original, alias]) => [original, alias]).sort(([left], [right]) => left.localeCompare(right));
1662
+ const expectedJson = JSON.stringify(expected);
1663
+ const actualJson = JSON.stringify(actualEntries);
1664
+ if (expectedJson !== actualJson) {
1665
+ throw new Error(
1666
+ `[csszyx] production.mangleGlobalVars validation failed:
1667
+ CSS alias plan ${actualJson} does not match source-transform alias table ${expectedJson}.`
1668
+ );
1669
+ }
1670
+ }
1671
+ function buildVarMangleMap(entriesByFile) {
1672
+ const next = {};
1673
+ const files = [...entriesByFile.keys()].sort();
1674
+ for (const file of files) {
1675
+ for (const [original, mangled] of entriesByFile.get(file) ?? []) {
1676
+ addVarMangleMapping(next, original, mangled);
933
1677
  }
934
1678
  }
935
- const overflow = survivors.length - options.maxEntries ;
936
- if (overflow > 0) {
937
- survivors.sort((a, b) => a.timestamp - b.timestamp);
938
- for (const survivor of survivors.slice(0, overflow)) {
939
- fs.rmSync(survivor.file, { force: true });
940
- deleted++;
1679
+ return next;
1680
+ }
1681
+ function extractGlobalVarAliasesForManifest(varMangleMap, aliasPrefix = CSSZYX_GLOBAL_ALIAS_PREFIX, validationResult = null) {
1682
+ const aliases = {};
1683
+ for (const [original, value] of Object.entries(varMangleMap).sort(
1684
+ ([left], [right]) => left.localeCompare(right)
1685
+ )) {
1686
+ const values = Array.isArray(value) ? value : [value];
1687
+ const alias = values.find((candidate) => candidate.startsWith(aliasPrefix));
1688
+ if (alias) {
1689
+ aliases[original] = alias;
941
1690
  }
942
1691
  }
943
- return deleted;
1692
+ for (const entry of validationResult?.plan.entries ?? []) {
1693
+ if (entry.alias.startsWith(aliasPrefix)) {
1694
+ aliases[entry.original] = entry.alias;
1695
+ }
1696
+ }
1697
+ return Object.fromEntries(
1698
+ Object.entries(aliases).sort(([left], [right]) => left.localeCompare(right))
1699
+ );
944
1700
  }
945
- function cacheEntryPath(cacheRoot, key) {
946
- return path.join(cacheRoot, key.slice(0, 2), `${key.slice(2)}.json`);
1701
+ function createGlobalVarMapAssetSource(varMangleMap, aliasPrefix = CSSZYX_GLOBAL_ALIAS_PREFIX, validationResult = null) {
1702
+ const aliases = extractGlobalVarAliasesForManifest(varMangleMap, aliasPrefix, validationResult);
1703
+ return Object.keys(aliases).length > 0 ? JSON.stringify(aliases) : null;
947
1704
  }
948
- function serializeResult(result) {
949
- return {
950
- code: result.code,
951
- transformed: result.transformed,
952
- usesRuntime: result.usesRuntime,
953
- usesMerge: result.usesMerge,
954
- usesColorVar: result.usesColorVar,
955
- classes: [...result.classes],
956
- rawClassNames: [...result.rawClassNames],
957
- diagnostics: [...result.diagnostics],
958
- recoveryTokens: [...result.recoveryTokens]
959
- };
1705
+ function normalizeGlobalVarAliasesForCache(aliases) {
1706
+ if (!aliases) {
1707
+ return [];
1708
+ }
1709
+ const entries = aliases instanceof Map ? aliases.entries() : Array.isArray(aliases) ? aliases : Object.entries(aliases);
1710
+ const normalized = /* @__PURE__ */ new Map();
1711
+ for (const [original, alias] of entries) {
1712
+ if (original.startsWith("--") && alias.startsWith("--")) {
1713
+ normalized.set(original, alias);
1714
+ }
1715
+ }
1716
+ return [...normalized].sort(([left], [right]) => left.localeCompare(right));
1717
+ }
1718
+ function createEarlyGlobalVarAliasEntries(config, aliasPrefix) {
1719
+ if (config?.enabled !== true || !config.tokens || config.tokens.length === 0) {
1720
+ return [];
1721
+ }
1722
+ const tokens = [...new Set(config.tokens)].sort();
1723
+ return tokens.map((original, index) => [original, `${aliasPrefix}${encode(index)}`]);
1724
+ }
1725
+ function shouldEmitGlobalVarMapAsset(config) {
1726
+ return config?.emitMap !== false;
1727
+ }
1728
+ function assertVarMangleMapSize(varMangleMap, maxBytes) {
1729
+ const size = Buffer.byteLength(JSON.stringify(varMangleMap), "utf8");
1730
+ if (size <= maxBytes) {
1731
+ return;
1732
+ }
1733
+ throw new Error(
1734
+ `[csszyx] CSS variable mangle map is ${size} bytes, which exceeds the ${maxBytes} byte safety cap. Reduce production.mangleVars usage, split the bundle, or raise CSSZYX_VAR_MANGLE_MAP_MAX_BYTES if this payload size is intentional.`
1735
+ );
1736
+ }
1737
+ function resolveVarMangleMapMaxBytes() {
1738
+ const raw = process.env.CSSZYX_VAR_MANGLE_MAP_MAX_BYTES;
1739
+ if (!raw) {
1740
+ return DEFAULT_VAR_MANGLE_MAP_MAX_BYTES;
1741
+ }
1742
+ const value = Number.parseInt(raw, 10);
1743
+ return Number.isFinite(value) && value > 0 ? value : DEFAULT_VAR_MANGLE_MAP_MAX_BYTES;
960
1744
  }
961
- function deserializeResult(result) {
1745
+ function addVarMangleMapping(map, original, mangled) {
1746
+ const existing = map[original];
1747
+ if (!existing) {
1748
+ map[original] = mangled;
1749
+ return;
1750
+ }
1751
+ const values = Array.isArray(existing) ? existing : [existing];
1752
+ if (!values.includes(mangled)) {
1753
+ map[original] = [...values, mangled];
1754
+ }
1755
+ }
1756
+ function emptyCSSVariableMetrics() {
962
1757
  return {
963
- code: result.code,
964
- transformed: result.transformed,
965
- usesRuntime: result.usesRuntime,
966
- usesMerge: result.usesMerge,
967
- usesColorVar: result.usesColorVar,
968
- classes: new Set(result.classes),
969
- rawClassNames: new Set(result.rawClassNames),
970
- diagnostics: [...result.diagnostics],
971
- recoveryTokens: new Map(result.recoveryTokens)
1758
+ componentClassUses: 0,
1759
+ componentStyleDeclarations: 0,
1760
+ estimatedHoistedDeclarationsSaved: 0,
1761
+ scopedClassUses: 0,
1762
+ scopedStyleDeclarations: 0
972
1763
  };
973
1764
  }
974
- function listJsonFiles(dir) {
975
- let entries;
976
- try {
977
- entries = fs.readdirSync(dir, { withFileTypes: true });
978
- } catch {
979
- return [];
1765
+ function recordFileCSSVariableMetrics(state, filename, code) {
1766
+ const normalizedFilename = normalizeSourceFilename(filename);
1767
+ if (!code) {
1768
+ state.cssVarMetricsByFile.delete(normalizedFilename);
1769
+ } else {
1770
+ const metrics = collectCSSVariableMetrics(code);
1771
+ if (hasCSSVariableMetrics(metrics)) {
1772
+ state.cssVarMetricsByFile.set(normalizedFilename, metrics);
1773
+ } else {
1774
+ state.cssVarMetricsByFile.delete(normalizedFilename);
1775
+ }
1776
+ }
1777
+ state.cssVarMetrics = buildCSSVariableMetrics(state.cssVarMetricsByFile);
1778
+ }
1779
+ function collectCSSVariableMetrics(code) {
1780
+ const componentUses = /* @__PURE__ */ new Map();
1781
+ const componentDeclarations = /* @__PURE__ */ new Map();
1782
+ const metrics = emptyCSSVariableMetrics();
1783
+ for (const match of code.matchAll(/\(--([cs][A-Za-z0-9]+)\)/g)) {
1784
+ const name = `--${match[1]}`;
1785
+ if (name.startsWith("--c")) {
1786
+ metrics.componentClassUses++;
1787
+ incrementCount(componentUses, name);
1788
+ } else {
1789
+ metrics.scopedClassUses++;
1790
+ }
980
1791
  }
981
- const files = [];
982
- for (const entry of entries) {
983
- const fullPath = path.join(dir, entry.name);
984
- if (entry.isDirectory()) {
985
- files.push(...listJsonFiles(fullPath));
986
- } else if (entry.isFile() && entry.name.endsWith(".json")) {
987
- files.push(fullPath);
1792
+ for (const match of code.matchAll(/["'](--([cs][A-Za-z0-9]+))["']\s*:/g)) {
1793
+ const name = match[1];
1794
+ if (name.startsWith("--c")) {
1795
+ metrics.componentStyleDeclarations++;
1796
+ incrementCount(componentDeclarations, name);
1797
+ } else {
1798
+ metrics.scopedStyleDeclarations++;
988
1799
  }
989
1800
  }
990
- return files;
991
- }
992
-
993
- const VIRTUAL_MODULE_ID = "virtual:csszyx/mangle-map";
994
- const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
995
- const VIRTUAL_CHECKSUM_ID = "virtual:csszyx/checksum";
996
- const RESOLVED_VIRTUAL_CHECKSUM_ID = `\0${VIRTUAL_CHECKSUM_ID}`;
997
- function createMangleMapModule(mangleMap, checksum) {
998
- return `/**
999
- * Auto-generated mangle map for csszyx.
1000
- * This module is generated at build time and contains the mapping
1001
- * from original class names to mangled class names.
1002
- *
1003
- * @generated
1004
- */
1005
-
1006
- export const mangleMap = ${JSON.stringify(mangleMap, null, 2)};
1007
-
1008
- export const checksum = ${JSON.stringify(checksum)};
1009
-
1010
- export default {
1011
- mangleMap,
1012
- checksum,
1013
- };
1014
- `;
1801
+ for (const [name, uses] of componentUses) {
1802
+ const declarations = componentDeclarations.get(name) ?? 0;
1803
+ metrics.estimatedHoistedDeclarationsSaved += Math.max(0, uses - declarations);
1804
+ }
1805
+ return metrics;
1015
1806
  }
1016
- function createChecksumModule(checksum) {
1017
- return `/**
1018
- * Auto-generated checksum for csszyx mangle map.
1019
- *
1020
- * @generated
1021
- */
1022
-
1023
- export const checksum = ${JSON.stringify(checksum)};
1024
-
1025
- export default checksum;
1026
- `;
1807
+ function buildCSSVariableMetrics(metricsByFile) {
1808
+ const total = emptyCSSVariableMetrics();
1809
+ for (const file of [...metricsByFile.keys()].sort()) {
1810
+ const metrics = metricsByFile.get(file);
1811
+ if (!metrics) {
1812
+ continue;
1813
+ }
1814
+ total.componentClassUses += metrics.componentClassUses;
1815
+ total.componentStyleDeclarations += metrics.componentStyleDeclarations;
1816
+ total.estimatedHoistedDeclarationsSaved += metrics.estimatedHoistedDeclarationsSaved;
1817
+ total.scopedClassUses += metrics.scopedClassUses;
1818
+ total.scopedStyleDeclarations += metrics.scopedStyleDeclarations;
1819
+ }
1820
+ return total;
1027
1821
  }
1028
- function isVirtualModule(id) {
1029
- return id === VIRTUAL_MODULE_ID || id === VIRTUAL_CHECKSUM_ID;
1822
+ function hasCSSVariableMetrics(metrics) {
1823
+ return Object.values(metrics).some((value) => value > 0);
1030
1824
  }
1031
- function resolveVirtualModule(id) {
1032
- if (id === VIRTUAL_MODULE_ID) {
1033
- return RESOLVED_VIRTUAL_MODULE_ID;
1034
- }
1035
- if (id === VIRTUAL_CHECKSUM_ID) {
1036
- return RESOLVED_VIRTUAL_CHECKSUM_ID;
1037
- }
1038
- return void 0;
1825
+ function incrementCount(map, key) {
1826
+ map.set(key, (map.get(key) ?? 0) + 1);
1039
1827
  }
1040
-
1041
- const CHECKSUM_PLACEHOLDER = "___CSSZYX_CHECKSUM___";
1042
- const MANGLE_MAP_PLACEHOLDER = "___CSSZYX_MANGLE_MAP___";
1043
- const UNKNOWN_PACKAGE_VERSION = "0.0.0";
1044
- const TRANSFORM_CACHE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
1045
- const TRANSFORM_CACHE_MAX_ENTRIES = 1e4;
1046
- const TRANSFORM_MEMORY_CACHE_MAX_ENTRIES = 1e3;
1047
- const DIRECTIVE_PROLOGUE_PREFIX_RE = /^((?:\s|\/\/[^\n]*\n|\/\*(?:[^*]|\*(?!\/))*\*\/)*)(['"]use (?:client|server)['"];?\s*)/;
1048
- const RUNTIME_HELPER_IMPORT_RE = {
1049
- _sz: /\{[^}]*\b_sz\b[^}]*\}\s*from\s*['"]@csszyx\/runtime['"]/,
1050
- _szMerge: /\{[^}]*\b_szMerge\b[^}]*\}\s*from\s*['"]@csszyx\/runtime['"]/,
1051
- __szColorVar: /\{[^}]*\b__szColorVar\b[^}]*\}\s*from\s*['"]@csszyx\/runtime['"]/
1052
- };
1053
- let _hasWarnedTsConfig = false;
1054
- let _hasWarnedTransformCacheVersion = false;
1055
- const requireFromHere = createRequire(import.meta.url);
1056
- const PLUGIN_VERSION = findPackageVersionFromFile(
1057
- fileURLToPath(import.meta.url),
1058
- UNKNOWN_PACKAGE_VERSION
1059
- );
1060
- const COMPILER_VERSION = findPackageVersionFromModule("@csszyx/compiler", UNKNOWN_PACKAGE_VERSION);
1061
- const BENCH_TRACE_ENABLED = process.env.CSSZYX_BENCH_TRACE === "1";
1062
- const BENCH_TRACE_FILE = process.env.CSSZYX_BENCH_TRACE_FILE;
1063
1828
  function traceBenchTiming(label, filename, elapsedMs) {
1064
1829
  if (!BENCH_TRACE_ENABLED) {
1065
1830
  return;
@@ -1081,7 +1846,7 @@ function runThemeScan(rootDir, scanCss) {
1081
1846
  }
1082
1847
  const themes = sourceFiles.map((f) => {
1083
1848
  try {
1084
- return parseThemeBlocks(fs.readFileSync(f, "utf-8"));
1849
+ return parseThemeBlocks(readStableTextFileSnapshotSync(f).source);
1085
1850
  } catch {
1086
1851
  return null;
1087
1852
  }
@@ -1093,18 +1858,23 @@ function runThemeScan(rootDir, scanCss) {
1093
1858
  _hasWarnedTsConfig = true;
1094
1859
  try {
1095
1860
  const checkFile = (cfgPath) => {
1096
- if (fs.existsSync(cfgPath)) {
1097
- const content = fs.readFileSync(cfgPath, "utf-8");
1098
- if (!content.includes(".csszyx")) {
1099
- console.warn(
1100
- `
1861
+ let content;
1862
+ try {
1863
+ content = fs.readFileSync(cfgPath, "utf-8");
1864
+ } catch (err) {
1865
+ if (err?.code === "ENOENT") {
1866
+ return false;
1867
+ }
1868
+ throw err;
1869
+ }
1870
+ if (!content.includes(".csszyx")) {
1871
+ console.warn(
1872
+ `
1101
1873
  \x1B[33m\u26A0\uFE0F CSSzyx: Theme Auto-Scan enabled, but TypeScript isn't configured. Run "npx @csszyx/cli init" to fix.\x1B[0m
1102
1874
  `
1103
- );
1104
- }
1105
- return true;
1875
+ );
1106
1876
  }
1107
- return false;
1877
+ return true;
1108
1878
  };
1109
1879
  if (!checkFile(path.join(rootDir, "tsconfig.json"))) {
1110
1880
  checkFile(path.join(rootDir, "tsconfig.app.json"));
@@ -1321,12 +2091,43 @@ function mangleCodeClassesSync(code, mangleMap) {
1321
2091
  });
1322
2092
  return result;
1323
2093
  }
2094
+ function assertGlobalVarMangleConfig(options) {
2095
+ const config = options.production?.mangleGlobalVars;
2096
+ const errors = validateGlobalVarMangleConfig(config);
2097
+ if (errors.length > 0) {
2098
+ throw new Error(
2099
+ `[csszyx] Invalid production.mangleGlobalVars config:
2100
+ ${errors.join("\n")}`
2101
+ );
2102
+ }
2103
+ if (config?.enabled === true) {
2104
+ if (!config.tokens || config.tokens.length === 0) {
2105
+ throw new Error(
2106
+ "[csszyx] production.mangleGlobalVars.enabled requires explicit tokens in Phase H v1."
2107
+ );
2108
+ }
2109
+ if (config.autoPrefix !== void 0 && config.autoPrefix !== "") {
2110
+ throw new Error(
2111
+ "[csszyx] production.mangleGlobalVars.autoPrefix requires CSS pre-scan support and is not enabled in Phase H v1."
2112
+ );
2113
+ }
2114
+ }
2115
+ }
1324
2116
  function createCsszyxPlugins(options = {}) {
2117
+ assertGlobalVarMangleConfig(options);
1325
2118
  const manglingEnabled = options.production?.mangle !== false;
1326
2119
  const astBudgetOverride = options.build?.astBudgetLimit;
1327
2120
  const cacheRequested = (options.build?.cache ?? DEFAULT_BUILD_CONFIG.cache) !== false;
1328
2121
  const cacheVersionsKnown = PLUGIN_VERSION !== UNKNOWN_PACKAGE_VERSION && COMPILER_VERSION !== UNKNOWN_PACKAGE_VERSION;
1329
2122
  const cacheEnabled = cacheRequested && cacheVersionsKnown;
2123
+ const varMangleMapMaxBytes = resolveVarMangleMapMaxBytes();
2124
+ const globalVarMangleConfig = options.production?.mangleGlobalVars;
2125
+ const globalVarAliasPrefix = globalVarMangleConfig?.aliasPrefix ?? CSSZYX_GLOBAL_ALIAS_PREFIX;
2126
+ const encodedGlobalVarAliasPrefix = encodeURIComponent(globalVarAliasPrefix);
2127
+ const earlyGlobalVarAliasEntries = createEarlyGlobalVarAliasEntries(
2128
+ globalVarMangleConfig,
2129
+ globalVarAliasPrefix
2130
+ );
1330
2131
  if (cacheRequested && !cacheVersionsKnown && !_hasWarnedTransformCacheVersion) {
1331
2132
  _hasWarnedTransformCacheVersion = true;
1332
2133
  console.warn(
@@ -1341,12 +2142,21 @@ function createCsszyxPlugins(options = {}) {
1341
2142
  const state = {
1342
2143
  classes: /* @__PURE__ */ new Set(),
1343
2144
  mangleMap: {},
2145
+ varMangleEntriesByFile: /* @__PURE__ */ new Map(),
2146
+ varMangleMap: Object.fromEntries(earlyGlobalVarAliasEntries),
2147
+ cssVarMetricsByFile: /* @__PURE__ */ new Map(),
2148
+ cssVarMetrics: emptyCSSVariableMetrics(),
1344
2149
  checksum: "",
1345
2150
  finalized: false,
1346
2151
  rootDir: process.cwd(),
1347
2152
  recoveryTokens: /* @__PURE__ */ new Map(),
1348
- rscModules: /* @__PURE__ */ new Map()
2153
+ rscModules: /* @__PURE__ */ new Map(),
2154
+ globalVarSourceFilesByFile: /* @__PURE__ */ new Map(),
2155
+ globalVarValidationResult: null
1349
2156
  };
2157
+ if (earlyGlobalVarAliasEntries.length > 0) {
2158
+ state.varMangleEntriesByFile.set(GLOBAL_VAR_ALIAS_MAP_OWNER, earlyGlobalVarAliasEntries);
2159
+ }
1350
2160
  const SAFELIST_FILENAME = "csszyx-classes.html";
1351
2161
  const SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".tsx", ".jsx", ".ts", ".js"]);
1352
2162
  const IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".next", ".git", "dist", "build", ".turbo"]);
@@ -1356,6 +2166,40 @@ function createCsszyxPlugins(options = {}) {
1356
2166
  function isUserIncluded(id) {
1357
2167
  return !options.include || matchesAnyPattern(id, options.include, state.rootDir);
1358
2168
  }
2169
+ function resolveGlobalVarValidationCacheDir() {
2170
+ if (!cacheEnabled) {
2171
+ return void 0;
2172
+ }
2173
+ const cacheRoot = path.resolve(
2174
+ state.rootDir,
2175
+ options.build?.cacheDir ?? DEFAULT_BUILD_CONFIG.cacheDir ?? ".csszyx/cache"
2176
+ );
2177
+ return resolveGlobalVarScanCacheDir(cacheRoot);
2178
+ }
2179
+ function validateGlobalVarBundleInputs(cssAssets) {
2180
+ if (globalVarMangleConfig?.enabled !== true) {
2181
+ return null;
2182
+ }
2183
+ const configuredCssAssets = collectConfiguredGlobalVarCssSources(
2184
+ state.rootDir,
2185
+ options.build?.scanCss
2186
+ );
2187
+ const result = validateGlobalVarAliasInputs(
2188
+ createGlobalVarAliasValidationOptions({
2189
+ rootDir: state.rootDir,
2190
+ cssAssets: [...configuredCssAssets, ...cssAssets],
2191
+ sourceFiles: buildGlobalVarSourceFiles(state),
2192
+ tokens: globalVarMangleConfig.tokens,
2193
+ autoPrefix: globalVarMangleConfig.autoPrefix,
2194
+ aliasPrefix: globalVarAliasPrefix,
2195
+ reserved: globalVarMangleConfig.reserved,
2196
+ cacheDir: resolveGlobalVarValidationCacheDir()
2197
+ })
2198
+ );
2199
+ assertNoGlobalVarAliasValidationErrors(result);
2200
+ assertGlobalVarPlanMatchesEarlyAliases(result, earlyGlobalVarAliasEntries);
2201
+ return result;
2202
+ }
1359
2203
  function isHardIgnored(id) {
1360
2204
  return id.includes("node_modules") || id.includes("/packages/") || id.includes(".next") && !id.includes("static");
1361
2205
  }
@@ -1366,21 +2210,17 @@ function createCsszyxPlugins(options = {}) {
1366
2210
  return !isHardIgnored(id) && !isUserExcluded(id) && /\.css(\?.*)?$/.test(id);
1367
2211
  }
1368
2212
  function transformConfiguredSource(source, filename) {
1369
- const compilerOptions = { astBudget: astBudgetOverride };
2213
+ const compilerOptions = createCompilerOptions();
1370
2214
  const effectiveFilename = normalizeSourceFilename(filename);
1371
2215
  const cacheRoot = resolveTransformCacheDir(state.rootDir, options.build?.cacheDir);
1372
2216
  if (cacheEnabled) {
1373
2217
  evictTransformCacheOnce();
1374
2218
  }
1375
- const cacheInput = {
1376
- pluginVersion: PLUGIN_VERSION,
1377
- compilerVersion: COMPILER_VERSION,
1378
- parserMode,
1379
- producer: parserMode,
1380
- astBudget: astBudgetOverride,
1381
- filename: effectiveFilename,
1382
- source
1383
- };
2219
+ const cacheInput = createConfiguredTransformCacheInput(
2220
+ source,
2221
+ effectiveFilename,
2222
+ compilerOptions
2223
+ );
1384
2224
  if (parserMode === "rust") {
1385
2225
  ensureRustTransformAvailable();
1386
2226
  }
@@ -1421,6 +2261,126 @@ function createCsszyxPlugins(options = {}) {
1421
2261
  }
1422
2262
  return result;
1423
2263
  }
2264
+ function createCompilerOptions() {
2265
+ return {
2266
+ astBudget: astBudgetOverride,
2267
+ mangleVars: options.production?.mangleVars === true,
2268
+ mangleVarHoistMaxDepth: options.production?.mangleVarHoistMaxDepth,
2269
+ globalVarAliases: earlyGlobalVarAliasEntries.length > 0 ? earlyGlobalVarAliasEntries : void 0
2270
+ };
2271
+ }
2272
+ function createConfiguredTransformCacheInput(source, effectiveFilename, compilerOptions) {
2273
+ return {
2274
+ pluginVersion: PLUGIN_VERSION,
2275
+ compilerVersion: COMPILER_VERSION,
2276
+ parserMode,
2277
+ producer: parserMode,
2278
+ astBudget: astBudgetOverride,
2279
+ mangleVars: compilerOptions.mangleVars,
2280
+ mangleVarHoistMaxDepth: compilerOptions.mangleVarHoistMaxDepth,
2281
+ globalVarAliases: normalizeGlobalVarAliasesForCache(compilerOptions.globalVarAliases),
2282
+ filename: effectiveFilename,
2283
+ source
2284
+ };
2285
+ }
2286
+ function transformPrescanSources(files) {
2287
+ if (parserMode !== "rust" || files.length <= 1) {
2288
+ return transformPrescanSourcesIndividually(files);
2289
+ }
2290
+ const compilerOptions = createCompilerOptions();
2291
+ const cacheRoot = resolveTransformCacheDir(state.rootDir, options.build?.cacheDir);
2292
+ const results = /* @__PURE__ */ new Map();
2293
+ const misses = [];
2294
+ if (cacheEnabled) {
2295
+ evictTransformCacheOnce();
2296
+ }
2297
+ ensureRustTransformAvailable();
2298
+ for (const file of files) {
2299
+ const effectiveFilename = normalizeSourceFilename(file.filePath);
2300
+ const cacheInput = createConfiguredTransformCacheInput(
2301
+ file.content,
2302
+ effectiveFilename,
2303
+ compilerOptions
2304
+ );
2305
+ const cacheKey = cacheEnabled ? createTransformCacheKey(cacheInput) : null;
2306
+ if (cacheEnabled && cacheKey) {
2307
+ const memoryCached = transformMemoryCache.get(cacheKey.key);
2308
+ if (memoryCached) {
2309
+ transformMemoryCache.delete(cacheKey.key);
2310
+ transformMemoryCache.set(cacheKey.key, memoryCached);
2311
+ results.set(file.filePath, memoryCached);
2312
+ continue;
2313
+ }
2314
+ const cached = readTransformCache(cacheRoot, cacheInput, cacheKey);
2315
+ if (cached) {
2316
+ rememberTransformCacheEntry(cacheKey.key, cached);
2317
+ results.set(file.filePath, cached);
2318
+ continue;
2319
+ }
2320
+ }
2321
+ misses.push({
2322
+ filePath: file.filePath,
2323
+ effectiveFilename,
2324
+ content: file.content,
2325
+ cacheInput,
2326
+ cacheKey
2327
+ });
2328
+ }
2329
+ if (misses.length === 0) {
2330
+ return files.map((file) => {
2331
+ const result = results.get(file.filePath);
2332
+ return result ? { filePath: file.filePath, result } : null;
2333
+ }).filter((entry) => entry !== null);
2334
+ }
2335
+ try {
2336
+ const batchResults = transformRustBatch(
2337
+ misses.map((file) => ({
2338
+ filename: file.effectiveFilename,
2339
+ source: file.content
2340
+ })),
2341
+ compilerOptions
2342
+ );
2343
+ for (let index = 0; index < misses.length; index++) {
2344
+ const miss = misses[index];
2345
+ const result = batchResults[index];
2346
+ if (!miss || !result) {
2347
+ continue;
2348
+ }
2349
+ if (cacheEnabled && miss.cacheKey) {
2350
+ writeTransformCache(cacheRoot, miss.cacheInput, result, miss.cacheKey);
2351
+ rememberTransformCacheEntry(miss.cacheKey.key, result);
2352
+ }
2353
+ results.set(miss.filePath, result);
2354
+ }
2355
+ } catch {
2356
+ for (const miss of misses) {
2357
+ try {
2358
+ results.set(
2359
+ miss.filePath,
2360
+ transformConfiguredSource(miss.content, miss.effectiveFilename)
2361
+ );
2362
+ } catch {
2363
+ }
2364
+ }
2365
+ }
2366
+ return files.map((file) => {
2367
+ const result = results.get(file.filePath);
2368
+ return result ? { filePath: file.filePath, result } : null;
2369
+ }).filter((entry) => entry !== null);
2370
+ }
2371
+ function transformPrescanSourcesIndividually(files) {
2372
+ const results = [];
2373
+ for (const file of files) {
2374
+ try {
2375
+ results.push({
2376
+ filePath: file.filePath,
2377
+ result: transformConfiguredSource(file.content, file.filePath)
2378
+ });
2379
+ } catch {
2380
+ }
2381
+ }
2382
+ return results;
2383
+ }
1424
2384
  function rememberTransformCacheEntry(key, result) {
1425
2385
  transformMemoryCache.delete(key);
1426
2386
  transformMemoryCache.set(key, result);
@@ -1457,7 +2417,14 @@ function createCsszyxPlugins(options = {}) {
1457
2417
  <div class="${classList}"><div class="${classList}">x</div><div class="${classList}">x</div></div>
1458
2418
  `;
1459
2419
  try {
1460
- const existing = fs.existsSync(safelistPath) ? fs.readFileSync(safelistPath, "utf-8") : "";
2420
+ let existing = "";
2421
+ try {
2422
+ existing = fs.readFileSync(safelistPath, "utf-8");
2423
+ } catch (err) {
2424
+ if (err?.code !== "ENOENT") {
2425
+ throw err;
2426
+ }
2427
+ }
1461
2428
  if (existing !== content) {
1462
2429
  fs.writeFileSync(safelistPath, content);
1463
2430
  }
@@ -1465,8 +2432,10 @@ function createCsszyxPlugins(options = {}) {
1465
2432
  }
1466
2433
  }
1467
2434
  function prescanAndWriteClasses() {
2435
+ const prescanStarted = performance.now();
1468
2436
  const discoveredClasses = /* @__PURE__ */ new Set();
1469
2437
  const rawDiscoveredClasses = /* @__PURE__ */ new Set();
2438
+ const prescanSources = [];
1470
2439
  function scanDir(dir) {
1471
2440
  let entries;
1472
2441
  try {
@@ -1484,85 +2453,107 @@ function createCsszyxPlugins(options = {}) {
1484
2453
  if (!shouldProcessSource(filePath)) {
1485
2454
  continue;
1486
2455
  }
2456
+ let content;
1487
2457
  try {
1488
- const content = fs.readFileSync(filePath, "utf-8");
1489
- if (!content.includes("sz=") && !content.includes("sz:")) {
1490
- continue;
1491
- }
1492
- const result = transformConfiguredSource(content, filePath);
1493
- if (!result.transformed) {
1494
- continue;
1495
- }
1496
- for (const cls of result.classes) {
1497
- discoveredClasses.add(cls);
1498
- }
1499
- for (const cls of result.rawClassNames) {
1500
- rawDiscoveredClasses.add(cls);
1501
- }
1502
- for (const [token, data] of result.recoveryTokens) {
1503
- state.recoveryTokens.set(token, data);
1504
- }
1505
- if (result.usesRuntime) {
1506
- const szCallRe = /_sz\(\s*\{/g;
1507
- for (const szMatch of result.code.matchAll(szCallRe)) {
1508
- let depth = 1;
1509
- let idx = (szMatch.index ?? 0) + szMatch[0].length;
1510
- while (idx < result.code.length && depth > 0) {
1511
- if (result.code[idx] === "{") {
1512
- depth++;
1513
- } else if (result.code[idx] === "}") {
1514
- depth--;
1515
- }
1516
- idx++;
1517
- }
1518
- const objStr = result.code.slice(
1519
- (szMatch.index ?? 0) + szMatch[0].length,
1520
- idx - 1
1521
- );
1522
- const strKv = /(\w+)\s*:\s*(?:"([^"]*)"|'([^']*)')/g;
1523
- for (const kv of objStr.matchAll(strKv)) {
1524
- try {
1525
- const val = kv[2] ?? kv[3];
1526
- const r = transform({ [kv[1]]: val });
1527
- for (const c of r.className.split(/\s+/).filter(Boolean)) {
1528
- discoveredClasses.add(c);
1529
- }
1530
- } catch {
1531
- }
1532
- }
1533
- const numKv = /(\w+)\s*:\s*(-?\d+(?:\.\d+)?)\s*(?=[,}\n])/g;
1534
- for (const kv of objStr.matchAll(numKv)) {
1535
- try {
1536
- const r = transform({ [kv[1]]: parseFloat(kv[2]) });
1537
- for (const c of r.className.split(/\s+/).filter(Boolean)) {
1538
- discoveredClasses.add(c);
1539
- }
1540
- } catch {
1541
- }
1542
- }
1543
- const boolKv = /(\w+)\s*:\s*(true|false)\s*(?=[,}\n])/g;
1544
- for (const kv of objStr.matchAll(boolKv)) {
1545
- try {
1546
- const r = transform({ [kv[1]]: kv[2] === "true" });
1547
- for (const c of r.className.split(/\s+/).filter(Boolean)) {
1548
- discoveredClasses.add(c);
1549
- }
1550
- } catch {
1551
- }
1552
- }
1553
- }
1554
- }
2458
+ content = fs.readFileSync(filePath, "utf-8");
1555
2459
  } catch {
2460
+ continue;
2461
+ }
2462
+ if (!content.includes("sz=") && !content.includes("sz:")) {
2463
+ continue;
1556
2464
  }
2465
+ prescanSources.push({ filePath, content });
1557
2466
  }
1558
2467
  }
1559
2468
  }
1560
2469
  scanDir(state.rootDir);
2470
+ for (const { filePath, result } of transformPrescanSources(prescanSources)) {
2471
+ if (!result.transformed) {
2472
+ continue;
2473
+ }
2474
+ collectPrescanResult(result, filePath, discoveredClasses, rawDiscoveredClasses);
2475
+ }
1561
2476
  for (const cls of discoveredClasses) {
1562
2477
  state.classes.add(cls);
1563
2478
  }
1564
2479
  const safelistClasses = /* @__PURE__ */ new Set([...discoveredClasses, ...rawDiscoveredClasses]);
1565
2480
  writeSafelistFile(safelistClasses);
2481
+ traceBenchTiming("prescan", state.rootDir, performance.now() - prescanStarted);
2482
+ }
2483
+ function collectPrescanResult(result, filePath, discoveredClasses, rawDiscoveredClasses) {
2484
+ for (const cls of result.classes) {
2485
+ discoveredClasses.add(cls);
2486
+ }
2487
+ for (const cls of result.rawClassNames) {
2488
+ rawDiscoveredClasses.add(cls);
2489
+ }
2490
+ for (const [token, data] of result.recoveryTokens) {
2491
+ state.recoveryTokens.set(token, data);
2492
+ }
2493
+ recordFileVarMangleEntries(state, filePath, cssVariableEntries(result));
2494
+ recordFileCSSVariableMetrics(state, filePath, result.code);
2495
+ collectRuntimeStaticClasses(result, discoveredClasses);
2496
+ }
2497
+ function collectRuntimeStaticClasses(result, discoveredClasses) {
2498
+ if (!result.usesRuntime) {
2499
+ return;
2500
+ }
2501
+ const szCallRe = /_sz\(\s*\{/g;
2502
+ for (const szMatch of result.code.matchAll(szCallRe)) {
2503
+ let depth = 1;
2504
+ let idx = (szMatch.index ?? 0) + szMatch[0].length;
2505
+ while (idx < result.code.length && depth > 0) {
2506
+ if (result.code[idx] === "{") {
2507
+ depth++;
2508
+ } else if (result.code[idx] === "}") {
2509
+ depth--;
2510
+ }
2511
+ idx++;
2512
+ }
2513
+ const objStr = result.code.slice((szMatch.index ?? 0) + szMatch[0].length, idx - 1);
2514
+ collectRuntimeStringClasses(objStr, discoveredClasses);
2515
+ collectRuntimeNumberClasses(objStr, discoveredClasses);
2516
+ collectRuntimeBooleanClasses(objStr, discoveredClasses);
2517
+ }
2518
+ }
2519
+ function collectRuntimeStringClasses(objStr, discoveredClasses) {
2520
+ const strKv = /(\w+)\s*:\s*(?:"([^"]*)"|'([^']*)')/g;
2521
+ for (const kv of objStr.matchAll(strKv)) {
2522
+ try {
2523
+ const val = kv[2] ?? kv[3];
2524
+ collectTransformClasses(transform({ [kv[1]]: val }), discoveredClasses);
2525
+ } catch {
2526
+ }
2527
+ }
2528
+ }
2529
+ function collectRuntimeNumberClasses(objStr, discoveredClasses) {
2530
+ const numKv = /(\w+)\s*:\s*(-?\d+(?:\.\d+)?)\s*(?=[,}\n])/g;
2531
+ for (const kv of objStr.matchAll(numKv)) {
2532
+ try {
2533
+ collectTransformClasses(
2534
+ transform({ [kv[1]]: parseFloat(kv[2]) }),
2535
+ discoveredClasses
2536
+ );
2537
+ } catch {
2538
+ }
2539
+ }
2540
+ }
2541
+ function collectRuntimeBooleanClasses(objStr, discoveredClasses) {
2542
+ const boolKv = /(\w+)\s*:\s*(true|false)\s*(?=[,}\n])/g;
2543
+ for (const kv of objStr.matchAll(boolKv)) {
2544
+ try {
2545
+ collectTransformClasses(
2546
+ transform({ [kv[1]]: kv[2] === "true" }),
2547
+ discoveredClasses
2548
+ );
2549
+ } catch {
2550
+ }
2551
+ }
2552
+ }
2553
+ function collectTransformClasses(result, discoveredClasses) {
2554
+ for (const cls of result.className.split(/\s+/).filter(Boolean)) {
2555
+ discoveredClasses.add(cls);
2556
+ }
1566
2557
  }
1567
2558
  function extractClasses(code) {
1568
2559
  const dqPattern = /(?:class(?:Name)?|sz)[:=]\s*"([^"]*)"/g;
@@ -1605,7 +2596,10 @@ function createCsszyxPlugins(options = {}) {
1605
2596
  newMap[sortedClasses[i]] = encode(i);
1606
2597
  }
1607
2598
  state.mangleMap = newMap;
1608
- state.checksum = compute_mangle_checksum(state.mangleMap);
2599
+ assertVarMangleMapSize(state.varMangleMap, varMangleMapMaxBytes);
2600
+ state.checksum = compute_mangle_checksum(
2601
+ createHydrationMangleMap(state.mangleMap, state.varMangleMap)
2602
+ );
1609
2603
  state.finalized = true;
1610
2604
  }
1611
2605
  function mangleCodeClasses(code) {
@@ -1621,6 +2615,11 @@ function createCsszyxPlugins(options = {}) {
1621
2615
  const escapedMap = result.includes("eval(") ? jsonMap.replace(/"/g, '\\"') : jsonMap;
1622
2616
  result = result.split(MANGLE_MAP_PLACEHOLDER).join(escapedMap);
1623
2617
  }
2618
+ if (result.includes(VAR_MANGLE_MAP_PLACEHOLDER)) {
2619
+ const jsonMap = JSON.stringify(state.varMangleMap);
2620
+ const escapedMap = result.includes("eval(") ? jsonMap.replace(/"/g, '\\"') : jsonMap;
2621
+ result = result.split(VAR_MANGLE_MAP_PLACEHOLDER).join(escapedMap);
2622
+ }
1624
2623
  return result;
1625
2624
  }
1626
2625
  const prePlugin = createUnplugin(
@@ -1646,7 +2645,12 @@ function createCsszyxPlugins(options = {}) {
1646
2645
  load(id) {
1647
2646
  if (id === RESOLVED_VIRTUAL_MODULE_ID) {
1648
2647
  finalizeMangleMap();
1649
- return createMangleMapModule(state.mangleMap, state.checksum);
2648
+ return createMangleMapModule(
2649
+ state.mangleMap,
2650
+ state.checksum,
2651
+ state.varMangleMap,
2652
+ state.cssVarMetrics
2653
+ );
1650
2654
  }
1651
2655
  if (id === RESOLVED_VIRTUAL_CHECKSUM_ID) {
1652
2656
  finalizeMangleMap();
@@ -1677,6 +2681,9 @@ function createCsszyxPlugins(options = {}) {
1677
2681
  if (!shouldProcessCss(id) && !shouldProcessSource(id)) {
1678
2682
  return null;
1679
2683
  }
2684
+ if (shouldProcessSource(id)) {
2685
+ recordGlobalVarSourceFile(state, id, code);
2686
+ }
1680
2687
  if (/\.[tj]sx?(\?.*)?$/.test(id)) {
1681
2688
  assertNoRSCBoundaryViolation(code, id);
1682
2689
  }
@@ -1739,6 +2746,8 @@ ${sourceDirective}`
1739
2746
  usesColorVar = result.usesColorVar;
1740
2747
  transformed = result.transformed;
1741
2748
  szClasses = result.classes;
2749
+ recordFileVarMangleEntries(state, id, cssVariableEntries(result));
2750
+ recordFileCSSVariableMetrics(state, id, result.code);
1742
2751
  if (result.diagnostics.length > 0 && process.env.NODE_ENV !== "production") {
1743
2752
  for (const msg of result.diagnostics) {
1744
2753
  this.warn(`[csszyx] ${id}
@@ -1749,14 +2758,17 @@ ${sourceDirective}`
1749
2758
  state.recoveryTokens.set(token, data);
1750
2759
  }
1751
2760
  }
2761
+ } else if (shouldProcessSource(id)) {
2762
+ recordFileVarMangleEntries(state, id, []);
2763
+ recordFileCSSVariableMetrics(state, id, null);
1752
2764
  }
1753
- if (transformedCode.includes("<html") && /layout|Root|Document|app\\.tsx?$/i.test(id)) {
2765
+ if (transformedCode.includes("<html") && /(?:layout|Root|Document|app)\.tsx?$/i.test(id)) {
1754
2766
  const attrName = options.production?.minify ? "data-sz-cs" : "data-sz-checksum";
1755
2767
  transformedCode = transformedCode.replace(
1756
2768
  /<html([^>]*)>/i,
1757
2769
  `<html$1 ${attrName}="${CHECKSUM_PLACEHOLDER}">`
1758
2770
  );
1759
- const debugScript = `<script dangerouslySetInnerHTML={{__html: \`(function(){var m=${MANGLE_MAP_PLACEHOLDER};var r={};for(var k in m)r[m[k]]=k;window.__csszyx={mangleMap:m,checksum:"${CHECKSUM_PLACEHOLDER}",decode:function(c){return r[c]},encode:function(c){return m[c]},decodeAll:function(el){return(el.className||"").split(" ").map(function(c){return r[c]||c})}}})()\`}} />`;
2771
+ const debugScript = `<script dangerouslySetInnerHTML={{__html: \`(function(){var m=${MANGLE_MAP_PLACEHOLDER};var vm=${VAR_MANGLE_MAP_PLACEHOLDER};var gp=decodeURIComponent(${JSON.stringify(encodedGlobalVarAliasPrefix)});var r={};var vr={};for(var k in m)r[m[k]]=k;for(var vk in vm){var vv=vm[vk];var vs=Array.isArray(vv)?vv:[vv];for(var vi=0;vi<vs.length;vi++)(vr[vs[vi]]||(vr[vs[vi]]=[])).push(vk)}window.__csszyx={mangleMap:m,varMangleMap:vm,checksum:"${CHECKSUM_PLACEHOLDER}",decode:function(c){return r[c]},encode:function(c){return m[c]},decodeVar:function(v){return vr[v]||[]},encodeVar:function(v){return vm[v]},decodeGlobalVar:function(v){var a=vr[v]||[];return v.indexOf(gp)===0?a[0]:void 0},decodeAll:function(el){return(el.className||"").split(" ").map(function(c){return r[c]||c})}}})()\`}} />`;
1760
2772
  if (transformedCode.includes("<body")) {
1761
2773
  transformedCode = transformedCode.replace(
1762
2774
  /(<body[^>]*>)/i,
@@ -1825,6 +2837,9 @@ ${sourceDirective}`
1825
2837
  watchChange(id, change) {
1826
2838
  if (change.event === "delete") {
1827
2839
  deleteRSCModuleRecord(state.rscModules, id);
2840
+ recordGlobalVarSourceFile(state, id, null);
2841
+ recordFileVarMangleEntries(state, id, []);
2842
+ recordFileCSSVariableMetrics(state, id, null);
1828
2843
  }
1829
2844
  },
1830
2845
  /**
@@ -1886,6 +2901,9 @@ ${sourceDirective}`
1886
2901
  return;
1887
2902
  }
1888
2903
  if (!fileContent.includes("sz=") && !/\bsz\s*:\s*["'{]/.test(fileContent)) {
2904
+ recordGlobalVarSourceFile(state, ctx.file, fileContent);
2905
+ recordFileVarMangleEntries(state, ctx.file, []);
2906
+ recordFileCSSVariableMetrics(state, ctx.file, null);
1889
2907
  return;
1890
2908
  }
1891
2909
  try {
@@ -1897,15 +2915,24 @@ ${sourceDirective}`
1897
2915
  performance.now() - hmrTransformStarted
1898
2916
  );
1899
2917
  } catch {
2918
+ recordGlobalVarSourceFile(state, ctx.file, fileContent);
2919
+ recordFileVarMangleEntries(state, ctx.file, []);
2920
+ recordFileCSSVariableMetrics(state, ctx.file, null);
1900
2921
  return;
1901
2922
  }
1902
2923
  if (!result.transformed) {
2924
+ recordGlobalVarSourceFile(state, ctx.file, fileContent);
2925
+ recordFileVarMangleEntries(state, ctx.file, []);
2926
+ recordFileCSSVariableMetrics(state, ctx.file, null);
1903
2927
  return;
1904
2928
  }
1905
2929
  const sizeBefore = state.classes.size;
2930
+ recordGlobalVarSourceFile(state, ctx.file, fileContent);
1906
2931
  for (const cls of result.classes) {
1907
2932
  state.classes.add(cls);
1908
2933
  }
2934
+ recordFileVarMangleEntries(state, ctx.file, cssVariableEntries(result));
2935
+ recordFileCSSVariableMetrics(state, ctx.file, result.code);
1909
2936
  for (const [token, data] of result.recoveryTokens) {
1910
2937
  state.recoveryTokens.set(token, data);
1911
2938
  }
@@ -1927,7 +2954,9 @@ ${sourceDirective}`
1927
2954
  finalizeMangleMap();
1928
2955
  let result = transformIndexHtml(html, state.mangleMap, state.checksum, {
1929
2956
  mode: options.production?.injectChecksum === false ? "script" : "script",
1930
- minify: process.env.NODE_ENV === "production"
2957
+ minify: process.env.NODE_ENV === "production",
2958
+ varMangleMap: state.varMangleMap,
2959
+ globalVarAliasPrefix
1931
2960
  });
1932
2961
  if (state.recoveryTokens.size > 0) {
1933
2962
  const isProduction = process.env.NODE_ENV === "production";
@@ -1971,6 +3000,9 @@ ${sourceDirective}`
1971
3000
  },
1972
3001
  (assets) => {
1973
3002
  finalizeMangleMap();
3003
+ state.globalVarValidationResult = validateGlobalVarBundleInputs(
3004
+ collectWebpackGlobalVarCssAssets(assets)
3005
+ );
1974
3006
  const isWebpackDevMode = compiler.options.mode === "development";
1975
3007
  const manifestData = {
1976
3008
  version: "0.4.0",
@@ -1980,33 +3012,71 @@ ${sourceDirective}`
1980
3012
  if (manglingEnabled && !isWebpackDevMode && Object.keys(state.mangleMap).length > 0) {
1981
3013
  manifestData.mangleMap = state.mangleMap;
1982
3014
  }
3015
+ if (Object.keys(state.varMangleMap).length > 0) {
3016
+ manifestData.varMangleMap = state.varMangleMap;
3017
+ }
3018
+ const globalVarAliases = extractGlobalVarAliasesForManifest(
3019
+ state.varMangleMap,
3020
+ globalVarAliasPrefix,
3021
+ state.globalVarValidationResult
3022
+ );
3023
+ if (Object.keys(globalVarAliases).length > 0) {
3024
+ manifestData.globalVarAliases = globalVarAliases;
3025
+ }
3026
+ if (hasCSSVariableMetrics(state.cssVarMetrics)) {
3027
+ manifestData.cssVarMetrics = state.cssVarMetrics;
3028
+ }
1983
3029
  compilation.emitAsset(
1984
3030
  "csszyx-manifest.json",
1985
3031
  new compiler.webpack.sources.RawSource(JSON.stringify(manifestData))
1986
3032
  );
3033
+ if (shouldEmitGlobalVarMapAsset(globalVarMangleConfig)) {
3034
+ const globalVarMapAsset = createGlobalVarMapAssetSource(
3035
+ state.varMangleMap,
3036
+ globalVarAliasPrefix,
3037
+ state.globalVarValidationResult
3038
+ );
3039
+ if (globalVarMapAsset) {
3040
+ compilation.emitAsset(
3041
+ ".csszyx/global-var-map.json",
3042
+ new compiler.webpack.sources.RawSource(globalVarMapAsset)
3043
+ );
3044
+ }
3045
+ }
1987
3046
  for (const file in assets) {
1988
3047
  const asset = assets[file];
1989
3048
  const source = asset.source().toString();
1990
- if (manglingEnabled && !isWebpackDevMode && Object.keys(state.mangleMap).length > 0) {
1991
- if (file.endsWith(".css")) {
3049
+ if (file.endsWith(".css")) {
3050
+ let css = rewriteCssWithValidatedGlobalVarPlan(
3051
+ source,
3052
+ file,
3053
+ state.globalVarValidationResult
3054
+ );
3055
+ if (manglingEnabled && !isWebpackDevMode && Object.keys(state.mangleMap).length > 0) {
1992
3056
  try {
1993
- const result = mangleCSSSync(source, state.mangleMap, {
3057
+ const result = mangleCSSSync(css, state.mangleMap, {
1994
3058
  debug: options.development?.debug,
1995
3059
  from: file
1996
3060
  });
1997
3061
  if (result.transformedCount > 0) {
1998
- compilation.updateAsset(
1999
- file,
2000
- new compiler.webpack.sources.RawSource(result.css)
2001
- );
2002
- continue;
3062
+ css = result.css;
2003
3063
  }
2004
3064
  } catch (e) {
2005
3065
  if (e && typeof e === "object" && "name" in e && e.name === "CssSyntaxError") ; else {
2006
3066
  throw e;
2007
3067
  }
2008
3068
  }
2009
- } else if (file.endsWith(".html")) {
3069
+ }
3070
+ if (css !== source) {
3071
+ compilation.updateAsset(
3072
+ file,
3073
+ new compiler.webpack.sources.RawSource(css)
3074
+ );
3075
+ }
3076
+ continue;
3077
+ }
3078
+ if (manglingEnabled && !isWebpackDevMode && Object.keys(state.mangleMap).length > 0) {
3079
+ if (file.endsWith(".html")) {
2010
3080
  const mangledHtml = source.replace(
2011
3081
  /\bclass="([^"]*)"/g,
2012
3082
  (_m, cls) => {
@@ -2061,6 +3131,9 @@ ${sourceDirective}`
2061
3131
  */
2062
3132
  generateBundle(_options, bundle) {
2063
3133
  finalizeMangleMap();
3134
+ state.globalVarValidationResult = validateGlobalVarBundleInputs(
3135
+ collectRollupGlobalVarCssAssets(bundle)
3136
+ );
2064
3137
  const manifestData = {
2065
3138
  version: "0.4.0",
2066
3139
  buildId: state.checksum,
@@ -2069,31 +3142,70 @@ ${sourceDirective}`
2069
3142
  if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
2070
3143
  manifestData.mangleMap = state.mangleMap;
2071
3144
  }
3145
+ if (Object.keys(state.varMangleMap).length > 0) {
3146
+ manifestData.varMangleMap = state.varMangleMap;
3147
+ }
3148
+ const globalVarAliases = extractGlobalVarAliasesForManifest(
3149
+ state.varMangleMap,
3150
+ globalVarAliasPrefix,
3151
+ state.globalVarValidationResult
3152
+ );
3153
+ if (Object.keys(globalVarAliases).length > 0) {
3154
+ manifestData.globalVarAliases = globalVarAliases;
3155
+ }
3156
+ if (hasCSSVariableMetrics(state.cssVarMetrics)) {
3157
+ manifestData.cssVarMetrics = state.cssVarMetrics;
3158
+ }
2072
3159
  this.emitFile({
2073
3160
  type: "asset",
2074
3161
  fileName: "csszyx-manifest.json",
2075
3162
  source: JSON.stringify(manifestData)
2076
3163
  });
3164
+ if (shouldEmitGlobalVarMapAsset(globalVarMangleConfig)) {
3165
+ const globalVarMapAsset = createGlobalVarMapAssetSource(
3166
+ state.varMangleMap,
3167
+ globalVarAliasPrefix,
3168
+ state.globalVarValidationResult
3169
+ );
3170
+ if (globalVarMapAsset) {
3171
+ this.emitFile({
3172
+ type: "asset",
3173
+ fileName: ".csszyx/global-var-map.json",
3174
+ source: globalVarMapAsset
3175
+ });
3176
+ }
3177
+ }
2077
3178
  for (const file in bundle) {
2078
3179
  const chunk = bundle[file];
2079
- if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
2080
- if (chunk.type === "asset" && chunk.fileName.endsWith(".css")) {
2081
- const css = chunk.source.toString();
3180
+ if (chunk.type === "asset" && chunk.fileName.endsWith(".css")) {
3181
+ const originalCss = chunk.source.toString();
3182
+ let css = rewriteCssWithValidatedGlobalVarPlan(
3183
+ originalCss,
3184
+ file,
3185
+ state.globalVarValidationResult
3186
+ );
3187
+ if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
2082
3188
  try {
2083
3189
  const result = mangleCSSSync(css, state.mangleMap, {
2084
3190
  debug: options.development?.debug,
2085
3191
  from: file
2086
3192
  });
2087
3193
  if (result.transformedCount > 0) {
2088
- chunk.source = result.css;
3194
+ css = result.css;
2089
3195
  }
2090
3196
  } catch (e) {
2091
3197
  if (e && typeof e === "object" && "name" in e && e.name === "CssSyntaxError") ; else {
2092
3198
  throw e;
2093
3199
  }
2094
3200
  }
2095
- continue;
2096
- } else if (chunk.type === "chunk") {
3201
+ }
3202
+ if (css !== originalCss) {
3203
+ chunk.source = css;
3204
+ }
3205
+ continue;
3206
+ }
3207
+ if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
3208
+ if (chunk.type === "chunk") {
2097
3209
  let mangledCode = mangleCodeClasses(chunk.code);
2098
3210
  mangledCode = replacePlaceholders(mangledCode);
2099
3211
  if (mangledCode !== chunk.code) {
@@ -2156,4 +3268,4 @@ const esbuildPlugin = (options = {}) => {
2156
3268
  };
2157
3269
  };
2158
3270
 
2159
- export { assertNoRSCBoundaryViolation as a, assertNoRSCGraphViolation as b, createRSCModuleRecord as c, deleteRSCModuleRecord as d, esbuildPlugin as e, findRSCBoundaryViolation as f, findRSCGraphViolation as g, hasTokens as h, hasUseClientDirective as i, hasUseServerDirective as j, isRSCServerModule as k, mergeThemes as l, mangleCodeClassesSync as m, parseThemeBlocks as p, rollupPlugin as r, unplugin as u, vitePlugin as v, webpackPlugin as w };
3271
+ export { scanGlobalVarCss as A, validateGlobalVarAliasInputs as B, vitePlugin as C, webpackPlugin as D, writeGlobalVarScanCache as E, assertNoRSCBoundaryViolation as a, assertNoRSCGraphViolation as b, createGlobalVarAliasValidationOptions as c, createGlobalVarMapAssetSource as d, createGlobalVarScanCacheKey as e, createRSCModuleRecord as f, deleteRSCModuleRecord as g, esbuildPlugin as h, extractGlobalVarAliasesForManifest as i, findRSCBoundaryViolation as j, findRSCGraphViolation as k, hasTokens as l, hasUseClientDirective as m, hasUseServerDirective as n, isRSCServerModule as o, isTailwindReservedGlobalVar as p, mangleCodeClassesSync as q, mergeThemes as r, normalizeGlobalVarAliasesForCache as s, parseThemeBlocks as t, unplugin as u, planGlobalVarAliases as v, readGlobalVarScanCache as w, resolveGlobalVarScanCacheDir as x, rewriteGlobalVarCssAliases as y, rollupPlugin as z };