@csszyx/unplugin 0.11.7 → 0.11.8

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 (41) hide show
  1. package/README.md +6 -1
  2. package/dist/css-mangler.cjs +9 -6
  3. package/dist/css-mangler.mjs +9 -6
  4. package/dist/index.cjs +5 -4
  5. package/dist/index.d.cts +2 -2
  6. package/dist/index.d.mts +2 -2
  7. package/dist/index.mjs +4 -4
  8. package/dist/next-config.cjs +1 -1
  9. package/dist/next-config.mjs +1 -1
  10. package/dist/next-prebuild.cjs +5 -5
  11. package/dist/next-prebuild.mjs +5 -5
  12. package/dist/next-turbo-loader.cjs +7 -12
  13. package/dist/next-turbo-loader.mjs +7 -12
  14. package/dist/next-watcher.cjs +2 -2
  15. package/dist/next-watcher.mjs +2 -2
  16. package/dist/shared/{unplugin.D6hes1O8.cjs → unplugin.C9dMG7ye.cjs} +5 -3
  17. package/dist/shared/{unplugin.PhOFTQpB.d.cts → unplugin.CNwV3RrQ.d.cts} +18 -5
  18. package/dist/shared/{unplugin.PhOFTQpB.d.mts → unplugin.CNwV3RrQ.d.mts} +18 -5
  19. package/dist/shared/unplugin.CcA7jP1r.cjs +48 -0
  20. package/dist/shared/{unplugin.CwcOd1q3.mjs → unplugin.D-K57LNR.mjs} +6 -1
  21. package/dist/shared/{unplugin.D8vSG36M.cjs → unplugin.DVCQC5wq.cjs} +6 -0
  22. package/dist/shared/{unplugin.CZKUWN1J.cjs → unplugin.DdtOZANG.cjs} +51 -50
  23. package/dist/shared/{unplugin.Dqt1aLCX.mjs → unplugin.Dg1tGPSd.mjs} +5 -3
  24. package/dist/shared/{unplugin.Rov-j_Wm.cjs → unplugin.DmuOKVS4.cjs} +21 -0
  25. package/dist/shared/{unplugin.ByAzFdqO.cjs → unplugin.DtPQz8Kg.cjs} +1072 -754
  26. package/dist/shared/{unplugin.CtHwCYxE.mjs → unplugin.DtYxS8M8.mjs} +1071 -754
  27. package/dist/shared/{unplugin.VSslRpfN.mjs → unplugin.Rl8sIkHi.mjs} +51 -50
  28. package/dist/shared/unplugin.TtmP77BS.mjs +40 -0
  29. package/dist/shared/unplugin.nnsa7OVW.mjs +49 -0
  30. package/dist/vite.cjs +4 -4
  31. package/dist/vite.d.cts +2 -25
  32. package/dist/vite.d.mts +1 -25
  33. package/dist/vite.mjs +4 -8
  34. package/dist/webpack.cjs +4 -4
  35. package/dist/webpack.d.cts +2 -5
  36. package/dist/webpack.d.mts +1 -5
  37. package/dist/webpack.mjs +4 -8
  38. package/package.json +7 -7
  39. package/dist/shared/unplugin.B1mblcm-.mjs +0 -9
  40. package/dist/shared/unplugin.B3RHYokB.mjs +0 -29
  41. package/dist/shared/unplugin.BCwRIUs_.cjs +0 -12
@@ -14,9 +14,9 @@ const types = require('@csszyx/types');
14
14
  const vueAdapter = require('@csszyx/vue-adapter');
15
15
  const unplugin$1 = require('unplugin');
16
16
  const cssMangler = require('../css-mangler.cjs');
17
- const htmlEscape = require('./unplugin.BCwRIUs_.cjs');
18
- const runtimeImportScan = require('./unplugin.Rov-j_Wm.cjs');
19
- const transformCache = require('./unplugin.D8vSG36M.cjs');
17
+ const runtimeImportScan = require('./unplugin.DmuOKVS4.cjs');
18
+ const transformCache = require('./unplugin.DVCQC5wq.cjs');
19
+ const htmlEscape = require('./unplugin.CcA7jP1r.cjs');
20
20
  const postcss = require('postcss');
21
21
  const valueParser = require('postcss-value-parser');
22
22
 
@@ -116,7 +116,8 @@ function buildScopeId(node) {
116
116
  }
117
117
  current = current.parent;
118
118
  }
119
- return parts.reverse().join(" > ") || DEFAULT_SCOPE_ID;
119
+ parts.reverse();
120
+ return parts.join(" > ") || DEFAULT_SCOPE_ID;
120
121
  }
121
122
 
122
123
  const VAR_REFERENCE_RE = /var\(\s*(--[\w-]+)/g;
@@ -389,7 +390,7 @@ function rewriteGlobalVarValue(value, aliases) {
389
390
  return;
390
391
  }
391
392
  const firstArgument = node.nodes.find((child) => child.type !== "space");
392
- if (!firstArgument || firstArgument.type !== "word") {
393
+ if (firstArgument?.type !== "word") {
393
394
  return;
394
395
  }
395
396
  const alias = aliases.get(firstArgument.value);
@@ -539,8 +540,8 @@ function createRSCModuleRecord(code, id) {
539
540
  }
540
541
  function deleteRSCModuleRecord(records, id) {
541
542
  const normalized = normalizeModuleId(id);
542
- const clean = id.split("?")[0]?.replace(/\\/g, "/") ?? id;
543
- const resolved = path__namespace.resolve(clean).replace(/\\/g, "/");
543
+ const clean = transformCache.normalizePathSeparators(id.split("?")[0] ?? id);
544
+ const resolved = transformCache.normalizePathSeparators(path__namespace.resolve(clean));
544
545
  pruneRSCModulePathCaches(/* @__PURE__ */ new Set([normalized, resolved, clean]));
545
546
  let deleted = records.delete(normalized);
546
547
  if (resolved !== normalized) {
@@ -586,7 +587,7 @@ const APP_ROUTER_ENTRIES = /* @__PURE__ */ new Set([
586
587
  "route"
587
588
  ]);
588
589
  function isNextAppRouterEntry(id) {
589
- const clean = id.split("?")[0]?.replace(/\\/g, "/") ?? id;
590
+ const clean = transformCache.normalizePathSeparators(id.split("?")[0] ?? id);
590
591
  if (!clean.includes("/app/") && !clean.startsWith("app/")) return false;
591
592
  const basename = clean.split("/").pop() ?? "";
592
593
  const dotIdx = basename.indexOf(".");
@@ -658,7 +659,7 @@ function findDirectiveStatementEnd(code, start) {
658
659
  }
659
660
  function readDirectivePrologue(code) {
660
661
  const out = [];
661
- let i = code.charCodeAt(0) === 65279 ? 1 : 0;
662
+ let i = readCodePoint(code, 0) === 65279 ? 1 : 0;
662
663
  while (i < code.length) {
663
664
  i = skipWhitespaceAndComments(code, i);
664
665
  const quote = code[i];
@@ -725,55 +726,50 @@ function findStaticImports(code) {
725
726
  let cursor = 0;
726
727
  while (cursor < code.length) {
727
728
  const importStart = code.indexOf("import", cursor);
728
- if (importStart === -1) {
729
- break;
730
- }
731
- cursor = importStart + 6;
732
- if (importStart > 0 && isIdentifierPart(code.charCodeAt(importStart - 1)) || isIdentifierPart(code.charCodeAt(cursor))) {
733
- continue;
734
- }
735
- let position = skipAsciiWhitespace(code, cursor);
736
- const opener = code.charAt(position);
737
- if (opener === "(") {
738
- continue;
739
- }
740
- if (opener === '"' || opener === "'") {
741
- const literal2 = readQuotedString(code, position);
742
- if (literal2) {
743
- imports.push({ clause: null, source: literal2.value });
744
- cursor = literal2.end;
745
- }
746
- continue;
747
- }
748
- const clauseStart = position;
749
- let fromStart = -1;
750
- while (position < code.length) {
751
- if (code.startsWith("from", position) && (position === clauseStart || !isIdentifierPart(code.charCodeAt(position - 1))) && !isIdentifierPart(code.charCodeAt(position + 4))) {
752
- fromStart = position;
753
- break;
754
- }
755
- if (code.charAt(position) === ";") {
756
- break;
757
- }
758
- position += 1;
759
- }
760
- if (fromStart === -1) {
761
- continue;
762
- }
763
- const clause = code.slice(clauseStart, fromStart).trim();
764
- if (splitAsciiWhitespace(clause)[0] === "type") {
765
- cursor = fromStart + 4;
766
- continue;
767
- }
768
- position = skipAsciiWhitespace(code, fromStart + 4);
769
- const literal = readQuotedString(code, position);
770
- if (literal) {
771
- imports.push({ clause, source: literal.value });
772
- cursor = literal.end;
773
- }
729
+ if (importStart === -1) break;
730
+ const result = scanStaticImport(code, importStart);
731
+ cursor = result.nextCursor;
732
+ if (result.value) imports.push(result.value);
774
733
  }
775
734
  return imports;
776
735
  }
736
+ function scanStaticImport(code, importStart) {
737
+ const afterKeyword = importStart + 6;
738
+ if (importStart > 0 && isIdentifierPart(readCodePoint(code, importStart - 1)) || isIdentifierPart(readCodePoint(code, afterKeyword))) {
739
+ return { nextCursor: afterKeyword };
740
+ }
741
+ const clauseStart = skipAsciiWhitespace(code, afterKeyword);
742
+ const opener = code.charAt(clauseStart);
743
+ if (opener === "(") return { nextCursor: afterKeyword };
744
+ if (opener === '"' || opener === "'") {
745
+ return scanSideEffectImport(code, clauseStart, afterKeyword);
746
+ }
747
+ return scanImportClause(code, clauseStart, afterKeyword);
748
+ }
749
+ function scanSideEffectImport(code, literalStart, fallbackCursor) {
750
+ const literal = readQuotedString(code, literalStart);
751
+ return literal ? { nextCursor: literal.end, value: { clause: null, source: literal.value } } : { nextCursor: fallbackCursor };
752
+ }
753
+ function scanImportClause(code, clauseStart, fallbackCursor) {
754
+ const fromStart = findImportFromKeyword(code, clauseStart);
755
+ if (fromStart === -1) return { nextCursor: fallbackCursor };
756
+ const clause = code.slice(clauseStart, fromStart).trim();
757
+ if (splitAsciiWhitespace(clause)[0] === "type") {
758
+ return { nextCursor: fromStart + 4 };
759
+ }
760
+ const literalStart = skipAsciiWhitespace(code, fromStart + 4);
761
+ const literal = readQuotedString(code, literalStart);
762
+ return literal ? { nextCursor: literal.end, value: { clause, source: literal.value } } : { nextCursor: fallbackCursor };
763
+ }
764
+ function findImportFromKeyword(code, clauseStart) {
765
+ for (let position = clauseStart; position < code.length; position += 1) {
766
+ if (code.charAt(position) === ";") return -1;
767
+ if (code.startsWith("from", position) && (position === clauseStart || !isIdentifierPart(readCodePoint(code, position - 1))) && !isIdentifierPart(readCodePoint(code, position + 4))) {
768
+ return position;
769
+ }
770
+ }
771
+ return -1;
772
+ }
777
773
  function readQuotedString(code, start) {
778
774
  const quote = code.charAt(start);
779
775
  if (quote !== '"' && quote !== "'") {
@@ -801,7 +797,7 @@ function readQuotedString(code, start) {
801
797
  function skipAsciiWhitespace(code, start) {
802
798
  let index = start;
803
799
  while (index < code.length) {
804
- const charCode = code.charCodeAt(index);
800
+ const charCode = readCodePoint(code, index);
805
801
  if (charCode !== 9 && charCode !== 10 && charCode !== 13 && charCode !== 32) {
806
802
  break;
807
803
  }
@@ -946,9 +942,9 @@ function normalizeModuleId(id) {
946
942
  }
947
943
  let normalized;
948
944
  try {
949
- normalized = fs__namespace.realpathSync.native(clean).replace(/\\/g, "/");
945
+ normalized = transformCache.normalizePathSeparators(fs__namespace.realpathSync.native(clean));
950
946
  } catch {
951
- normalized = path__namespace.resolve(clean).replace(/\\/g, "/");
947
+ normalized = transformCache.normalizePathSeparators(path__namespace.resolve(clean));
952
948
  }
953
949
  normalizedModuleIdCache.set(clean, normalized);
954
950
  return normalized;
@@ -994,7 +990,7 @@ function resolveLocalModule(importer, source) {
994
990
  }
995
991
  function pruneRSCModulePathCaches(moduleIds) {
996
992
  for (const [key, value] of normalizedModuleIdCache) {
997
- const normalizedKey = key.replace(/\\/g, "/");
993
+ const normalizedKey = transformCache.normalizePathSeparators(key);
998
994
  if (moduleIds.has(value) || moduleIds.has(normalizedKey)) {
999
995
  normalizedModuleIdCache.delete(key);
1000
996
  }
@@ -1038,11 +1034,11 @@ function readImportedSymbols(clause) {
1038
1034
  return symbols;
1039
1035
  }
1040
1036
  function isIdentifier(value) {
1041
- if (value.length === 0 || !isIdentifierStart(value.charCodeAt(0))) {
1037
+ if (value.length === 0 || !isIdentifierStart(readCodePoint(value, 0))) {
1042
1038
  return false;
1043
1039
  }
1044
1040
  for (let index = 1; index < value.length; index += 1) {
1045
- const code = value.charCodeAt(index);
1041
+ const code = readCodePoint(value, index);
1046
1042
  if (!isIdentifierStart(code) && (code < 48 || code > 57)) {
1047
1043
  return false;
1048
1044
  }
@@ -1052,11 +1048,14 @@ function isIdentifier(value) {
1052
1048
  function isIdentifierStart(code) {
1053
1049
  return code === 36 || code === 95 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
1054
1050
  }
1051
+ function readCodePoint(value, index) {
1052
+ return value.codePointAt(index) ?? -1;
1053
+ }
1055
1054
  function splitAsciiWhitespace(value) {
1056
1055
  const parts = [];
1057
1056
  let start = -1;
1058
1057
  for (let index = 0; index <= value.length; index += 1) {
1059
- const code = index < value.length ? value.charCodeAt(index) : 32;
1058
+ const code = index < value.length ? readCodePoint(value, index) : 32;
1060
1059
  const whitespace = code === 9 || code === 10 || code === 13 || code === 32;
1061
1060
  if (whitespace) {
1062
1061
  if (start !== -1) {
@@ -1079,13 +1078,7 @@ function stripCommentsForImportScan(code) {
1079
1078
  const next = code[i + 1];
1080
1079
  if (quote) {
1081
1080
  out += ch;
1082
- if (escaped) {
1083
- escaped = false;
1084
- } else if (ch === "\\") {
1085
- escaped = true;
1086
- } else if (ch === quote) {
1087
- quote = null;
1088
- }
1081
+ ({ quote, escaped } = advanceQuotedImportScan(ch, quote, escaped));
1089
1082
  i++;
1090
1083
  continue;
1091
1084
  }
@@ -1096,28 +1089,15 @@ function stripCommentsForImportScan(code) {
1096
1089
  continue;
1097
1090
  }
1098
1091
  if (ch === "/" && next === "/") {
1099
- out += " ";
1100
- i += 2;
1101
- while (i < code.length && code[i] !== "\n") {
1102
- out += " ";
1103
- i++;
1104
- }
1092
+ const masked = maskLineComment(code, i);
1093
+ out += masked.value;
1094
+ i = masked.end;
1105
1095
  continue;
1106
1096
  }
1107
1097
  if (ch === "/" && next === "*") {
1108
- out += " ";
1109
- i += 2;
1110
- while (i < code.length) {
1111
- const blockCh = code[i];
1112
- const blockNext = code[i + 1];
1113
- if (blockCh === "*" && blockNext === "/") {
1114
- out += " ";
1115
- i += 2;
1116
- break;
1117
- }
1118
- out += blockCh === "\n" ? "\n" : " ";
1119
- i++;
1120
- }
1098
+ const masked = maskBlockComment(code, i);
1099
+ out += masked.value;
1100
+ i = masked.end;
1121
1101
  continue;
1122
1102
  }
1123
1103
  out += ch;
@@ -1125,6 +1105,31 @@ function stripCommentsForImportScan(code) {
1125
1105
  }
1126
1106
  return out;
1127
1107
  }
1108
+ function advanceQuotedImportScan(character, quote, escaped) {
1109
+ if (escaped) return { quote, escaped: false };
1110
+ if (character === "\\") return { quote, escaped: true };
1111
+ return { quote: character === quote ? null : quote, escaped: false };
1112
+ }
1113
+ function maskLineComment(code, start) {
1114
+ const newline = code.indexOf("\n", start + 2);
1115
+ const end = newline === -1 ? code.length : newline;
1116
+ return { end, value: " ".repeat(end - start) };
1117
+ }
1118
+ function maskBlockComment(code, start) {
1119
+ let end = start + 2;
1120
+ while (end < code.length) {
1121
+ if (code.charAt(end) === "*" && code.charAt(end + 1) === "/") {
1122
+ end += 2;
1123
+ break;
1124
+ }
1125
+ end += 1;
1126
+ }
1127
+ let value = "";
1128
+ for (let index = start; index < end; index += 1) {
1129
+ value += code.charAt(index) === "\n" ? "\n" : " ";
1130
+ }
1131
+ return { end, value };
1132
+ }
1128
1133
 
1129
1134
  const EMPTY_THEME = {
1130
1135
  colors: [],
@@ -1328,13 +1333,376 @@ function scanCustomPropertyNames(block) {
1328
1333
  return names;
1329
1334
  }
1330
1335
 
1336
+ const REGEX_PREFIXES = new Set("([{:,;=!?&|+-*%^~<>/");
1337
+ const REGEX_PREFIX_KEYWORDS = /* @__PURE__ */ new Set([
1338
+ "await",
1339
+ "case",
1340
+ "delete",
1341
+ "do",
1342
+ "else",
1343
+ "in",
1344
+ "instanceof",
1345
+ "of",
1346
+ "return",
1347
+ "throw",
1348
+ "typeof",
1349
+ "void",
1350
+ "yield"
1351
+ ]);
1352
+ const SIMPLE_ESCAPES = {
1353
+ b: "\b",
1354
+ f: "\f",
1355
+ n: "\n",
1356
+ r: "\r",
1357
+ t: " ",
1358
+ v: "\v",
1359
+ "0": "\0"
1360
+ };
1361
+ function isFixedHex(value, width) {
1362
+ if (value.length !== width) return false;
1363
+ for (const character of value) {
1364
+ if (!"0123456789abcdefABCDEF".includes(character)) return false;
1365
+ }
1366
+ return true;
1367
+ }
1368
+ function decodeEscape(value, cursor) {
1369
+ const escaped = value[cursor];
1370
+ if (SIMPLE_ESCAPES[escaped] !== void 0) {
1371
+ return { value: SIMPLE_ESCAPES[escaped], cursor };
1372
+ }
1373
+ if (escaped === "\r" && value[cursor + 1] === "\n") {
1374
+ return { value: "", cursor: cursor + 1 };
1375
+ }
1376
+ if (escaped === "u" && value[cursor + 1] === "{") {
1377
+ const close = value.indexOf("}", cursor + 2);
1378
+ const hex2 = close === -1 ? "" : value.slice(cursor + 2, close);
1379
+ const codePoint = hex2 && /^[\dA-F]+$/i.test(hex2) ? Number.parseInt(hex2, 16) : -1;
1380
+ if (codePoint >= 0 && codePoint <= 1114111) {
1381
+ return { value: String.fromCodePoint(codePoint), cursor: close };
1382
+ }
1383
+ }
1384
+ let width = 0;
1385
+ if (escaped === "x") width = 2;
1386
+ else if (escaped === "u") width = 4;
1387
+ const hex = value.slice(cursor + 1, cursor + width + 1);
1388
+ if (width > 0 && isFixedHex(hex, width)) {
1389
+ return {
1390
+ value: String.fromCodePoint(Number.parseInt(hex, 16)),
1391
+ cursor: cursor + width
1392
+ };
1393
+ }
1394
+ const decoded = escaped === "\n" || escaped === "\r" ? "" : escaped;
1395
+ return { value: decoded, cursor };
1396
+ }
1397
+ function decodeJavaScriptString(value) {
1398
+ let output = "";
1399
+ let cursor = 0;
1400
+ while (cursor < value.length) {
1401
+ if (value[cursor] !== "\\" || cursor + 1 >= value.length) {
1402
+ output += value[cursor++];
1403
+ continue;
1404
+ }
1405
+ const decoded = decodeEscape(value, cursor + 1);
1406
+ output += decoded.value;
1407
+ cursor = decoded.cursor + 1;
1408
+ }
1409
+ return output;
1410
+ }
1411
+ function decodeAttributeWhitespace(value) {
1412
+ return value.replace(
1413
+ /&#(?:(\d+)|x([\da-f]+));|&(?:Tab|NewLine);/gi,
1414
+ (entity, decimal, hexadecimal) => {
1415
+ if (!decimal && !hexadecimal) return " ";
1416
+ const codePoint = Number.parseInt(decimal ?? hexadecimal ?? "", decimal ? 10 : 16);
1417
+ return [9, 10, 12, 13, 32].includes(codePoint) ? " " : entity;
1418
+ }
1419
+ );
1420
+ }
1421
+ function collectClassTokens(target, value) {
1422
+ for (const className of value.split(/\s+/)) {
1423
+ if (className) target.add(className);
1424
+ }
1425
+ }
1426
+ function quotedEnd(source, start, quote) {
1427
+ let cursor = start + 1;
1428
+ while (cursor < source.length) {
1429
+ if (source[cursor] === "\\") cursor += 2;
1430
+ else if (source[cursor++] === quote) break;
1431
+ }
1432
+ return cursor;
1433
+ }
1434
+ function commentEnd(source, start) {
1435
+ if (source[start + 1] === "/") {
1436
+ const end2 = source.indexOf("\n", start + 2);
1437
+ return end2 === -1 ? source.length : end2 + 1;
1438
+ }
1439
+ const end = source.indexOf("*/", start + 2);
1440
+ return end === -1 ? source.length : end + 2;
1441
+ }
1442
+ function isRegexStart(source, start) {
1443
+ let cursor = start - 1;
1444
+ while (cursor >= 0 && /\s/.test(source[cursor])) cursor--;
1445
+ if (source[cursor] === "<" && /[A-Z]/i.test(source[start + 1] ?? "")) return false;
1446
+ if (cursor < 0 || REGEX_PREFIXES.has(source[cursor])) return true;
1447
+ if (!/[\w$]/.test(source[cursor])) return false;
1448
+ const end = cursor + 1;
1449
+ while (cursor >= 0 && /[\w$]/.test(source[cursor])) cursor--;
1450
+ return REGEX_PREFIX_KEYWORDS.has(source.slice(cursor + 1, end));
1451
+ }
1452
+ function regexEnd(source, start) {
1453
+ let cursor = start + 1;
1454
+ let inCharacterClass = false;
1455
+ while (cursor < source.length) {
1456
+ const character = source[cursor];
1457
+ if (character === "\\") cursor += 2;
1458
+ else if (character === "[") {
1459
+ inCharacterClass = true;
1460
+ cursor++;
1461
+ } else if (character === "]") {
1462
+ inCharacterClass = false;
1463
+ cursor++;
1464
+ } else if (character === "/" && !inCharacterClass) {
1465
+ cursor++;
1466
+ while (/[A-Z]/i.test(source[cursor] ?? "")) cursor++;
1467
+ break;
1468
+ } else cursor++;
1469
+ }
1470
+ return cursor;
1471
+ }
1472
+ function templateEnd(source, start) {
1473
+ let cursor = start + 1;
1474
+ while (cursor < source.length) {
1475
+ if (source[cursor] === "\\") cursor += 2;
1476
+ else if (source[cursor] === "`") return cursor + 1;
1477
+ else if (source[cursor] === "$" && source[cursor + 1] === "{") {
1478
+ cursor = findBalancedCodeEnd(source, cursor + 2);
1479
+ } else cursor++;
1480
+ }
1481
+ return source.length;
1482
+ }
1483
+ function lexicalRegionEnd(source, start) {
1484
+ const character = source[start];
1485
+ if (character === '"' || character === "'") return quotedEnd(source, start, character);
1486
+ if (character === "`") return templateEnd(source, start);
1487
+ if (character === "/" && (source[start + 1] === "/" || source[start + 1] === "*")) {
1488
+ return commentEnd(source, start);
1489
+ }
1490
+ if (character === "/" && isRegexStart(source, start)) return regexEnd(source, start);
1491
+ return start;
1492
+ }
1493
+ function findBalancedCodeEnd(source, bodyStart) {
1494
+ let depth = 1;
1495
+ let cursor = bodyStart;
1496
+ while (cursor < source.length) {
1497
+ const regionEnd = lexicalRegionEnd(source, cursor);
1498
+ if (regionEnd !== cursor) {
1499
+ cursor = regionEnd;
1500
+ continue;
1501
+ }
1502
+ if (source[cursor] === "{") depth++;
1503
+ else if (source[cursor] === "}" && --depth === 0) return cursor;
1504
+ cursor++;
1505
+ }
1506
+ return source.length;
1507
+ }
1508
+ function collectTemplateTokens(target, source, start) {
1509
+ let cursor = start + 1;
1510
+ let quasiStart = cursor;
1511
+ while (cursor < source.length) {
1512
+ if (source[cursor] === "\\") {
1513
+ cursor += 2;
1514
+ continue;
1515
+ }
1516
+ if (source[cursor] === "`") {
1517
+ collectClassTokens(target, decodeJavaScriptString(source.slice(quasiStart, cursor)));
1518
+ return cursor + 1;
1519
+ }
1520
+ if (source[cursor] === "$" && source[cursor + 1] === "{") {
1521
+ collectClassTokens(target, decodeJavaScriptString(source.slice(quasiStart, cursor)));
1522
+ const expressionEnd = findBalancedCodeEnd(source, cursor + 2);
1523
+ collectExpressionTokens(target, source.slice(cursor + 2, expressionEnd));
1524
+ cursor = expressionEnd + 1;
1525
+ quasiStart = cursor;
1526
+ continue;
1527
+ }
1528
+ cursor++;
1529
+ }
1530
+ collectClassTokens(target, decodeJavaScriptString(source.slice(quasiStart)));
1531
+ return source.length;
1532
+ }
1533
+ function collectExpressionTokens(target, expression) {
1534
+ let cursor = 0;
1535
+ let pendingLiteral = "";
1536
+ let previousLiteralEnd = -1;
1537
+ const flushPending = () => {
1538
+ if (pendingLiteral) collectClassTokens(target, pendingLiteral);
1539
+ pendingLiteral = "";
1540
+ };
1541
+ while (cursor < expression.length) {
1542
+ const character = expression[cursor];
1543
+ if (character === "/" && (expression[cursor + 1] === "/" || expression[cursor + 1] === "*")) {
1544
+ cursor = commentEnd(expression, cursor);
1545
+ continue;
1546
+ }
1547
+ if (character === "/" && isRegexStart(expression, cursor)) {
1548
+ cursor = regexEnd(expression, cursor);
1549
+ continue;
1550
+ }
1551
+ if (character === "`") {
1552
+ flushPending();
1553
+ cursor = collectTemplateTokens(target, expression, cursor);
1554
+ previousLiteralEnd = -1;
1555
+ continue;
1556
+ }
1557
+ if (character !== '"' && character !== "'") {
1558
+ cursor++;
1559
+ continue;
1560
+ }
1561
+ const end = quotedEnd(expression, cursor, character);
1562
+ const decoded = decodeJavaScriptString(expression.slice(cursor + 1, end - 1));
1563
+ const joinsPrevious = previousLiteralEnd >= 0 && expression.slice(previousLiteralEnd, cursor).trim() === "+";
1564
+ if (!joinsPrevious) flushPending();
1565
+ pendingLiteral += decoded;
1566
+ previousLiteralEnd = end;
1567
+ cursor = end;
1568
+ }
1569
+ flushPending();
1570
+ }
1571
+ function triviaEnd(source, start) {
1572
+ let cursor = start;
1573
+ while (cursor < source.length) {
1574
+ if (/\s/.test(source[cursor])) cursor++;
1575
+ else if (source[cursor] === "/" && (source[cursor + 1] === "/" || source[cursor + 1] === "*")) {
1576
+ cursor = commentEnd(source, cursor);
1577
+ } else break;
1578
+ }
1579
+ return cursor;
1580
+ }
1581
+ function propertyExpressionEnd(source, start) {
1582
+ let cursor = start;
1583
+ let parenDepth = 0;
1584
+ let bracketDepth = 0;
1585
+ let braceDepth = 0;
1586
+ while (cursor < source.length) {
1587
+ const regionEnd = lexicalRegionEnd(source, cursor);
1588
+ if (regionEnd !== cursor) {
1589
+ cursor = regionEnd;
1590
+ continue;
1591
+ }
1592
+ const character = source[cursor];
1593
+ if (character === "(") parenDepth++;
1594
+ else if (character === ")") parenDepth--;
1595
+ else if (character === "[") bracketDepth++;
1596
+ else if (character === "]") bracketDepth--;
1597
+ else if (character === "{") braceDepth++;
1598
+ else if (character === "}" && braceDepth > 0) braceDepth--;
1599
+ else if ((character === "," || character === ";" || character === "}") && parenDepth === 0 && bracketDepth === 0 && braceDepth === 0)
1600
+ return cursor;
1601
+ cursor++;
1602
+ }
1603
+ return source.length;
1604
+ }
1605
+ function isClassSink(source, start, end) {
1606
+ const name = source.slice(start, end);
1607
+ if (name !== "class" && name !== "className") return false;
1608
+ let previous = start - 1;
1609
+ while (previous >= 0 && /\s/.test(source[previous])) previous--;
1610
+ return source[previous] !== "." && source[previous] !== "-";
1611
+ }
1612
+ function collectClassDirective(target, source, sinkStart, valueStart) {
1613
+ if (!/[\w$-]/.test(source[valueStart] ?? "")) return null;
1614
+ let directiveEnd = valueStart;
1615
+ while (/[\w$-]/.test(source[directiveEnd] ?? "")) directiveEnd++;
1616
+ const equals = triviaEnd(source, directiveEnd);
1617
+ if (source[equals] !== "=") {
1618
+ const insideMarkupTag = source.lastIndexOf("<", sinkStart) > source.lastIndexOf(">", sinkStart);
1619
+ const next = source[equals] ?? "";
1620
+ const continuesMarkupTag = next === "/" || next === ">" || next === "{" || /[A-Z_:]/i.test(next);
1621
+ if (!insideMarkupTag || !continuesMarkupTag) return null;
1622
+ collectClassTokens(target, source.slice(valueStart, directiveEnd));
1623
+ return directiveEnd;
1624
+ }
1625
+ const directive = source.slice(valueStart, directiveEnd);
1626
+ const directiveValueStart = triviaEnd(source, equals + 1);
1627
+ const directiveQuote = source[directiveValueStart];
1628
+ if (directive !== "list") collectClassTokens(target, directive);
1629
+ if (directiveQuote === "{") {
1630
+ const end = findBalancedCodeEnd(source, directiveValueStart + 1);
1631
+ if (directive === "list") {
1632
+ collectExpressionTokens(target, source.slice(directiveValueStart + 1, end));
1633
+ }
1634
+ return end + 1;
1635
+ }
1636
+ if (directive === "list" && (directiveQuote === '"' || directiveQuote === "'")) {
1637
+ const end = quotedEnd(source, directiveValueStart, directiveQuote);
1638
+ collectExpressionTokens(target, source.slice(directiveValueStart, end));
1639
+ return end;
1640
+ }
1641
+ return directiveValueStart;
1642
+ }
1643
+ function collectClassSink(target, source, start, nameEnd) {
1644
+ const operatorStart = triviaEnd(source, nameEnd);
1645
+ const operator = source[operatorStart];
1646
+ if (operator !== "=" && operator !== ":") return nameEnd;
1647
+ const valueStart = triviaEnd(source, operatorStart + 1);
1648
+ const quote = source[valueStart];
1649
+ const vueBinding = operator === "=" && source.slice(Math.max(0, start - 1), start) === ":";
1650
+ if (operator === ":" && valueStart === operatorStart + 1) {
1651
+ const directiveEnd = collectClassDirective(target, source, start, valueStart);
1652
+ if (directiveEnd !== null) return directiveEnd;
1653
+ }
1654
+ if (quote === '"' || quote === "'") {
1655
+ const end = quotedEnd(source, valueStart, quote);
1656
+ const value = source.slice(valueStart + 1, end - 1);
1657
+ if (vueBinding) collectExpressionTokens(target, decodeAttributeWhitespace(value));
1658
+ else collectClassTokens(target, decodeAttributeWhitespace(value));
1659
+ return end;
1660
+ }
1661
+ if (operator === "=" && quote === "{") {
1662
+ const end = findBalancedCodeEnd(source, valueStart + 1);
1663
+ collectExpressionTokens(target, source.slice(valueStart + 1, end));
1664
+ return end + 1;
1665
+ }
1666
+ if (operator === ":") {
1667
+ const end = propertyExpressionEnd(source, valueStart);
1668
+ collectExpressionTokens(target, source.slice(valueStart, end));
1669
+ return end;
1670
+ }
1671
+ return valueStart;
1672
+ }
1673
+ function collectAuthoredClassNames(source) {
1674
+ const classes = /* @__PURE__ */ new Set();
1675
+ let cursor = 0;
1676
+ while (cursor < source.length) {
1677
+ if (source[cursor] === '"' || source[cursor] === "'") {
1678
+ const keyEnd = quotedEnd(source, cursor, source[cursor]);
1679
+ const key = decodeJavaScriptString(source.slice(cursor + 1, keyEnd - 1));
1680
+ cursor = key === "class" || key === "className" ? collectClassSink(classes, source, cursor, keyEnd) : keyEnd;
1681
+ continue;
1682
+ }
1683
+ const regionEnd = lexicalRegionEnd(source, cursor);
1684
+ if (regionEnd !== cursor) {
1685
+ cursor = regionEnd;
1686
+ continue;
1687
+ }
1688
+ if (!/[A-Z_$]/i.test(source[cursor])) {
1689
+ cursor++;
1690
+ continue;
1691
+ }
1692
+ let nameEnd = cursor + 1;
1693
+ while (/[\w$]/.test(source[nameEnd] ?? "")) nameEnd++;
1694
+ cursor = isClassSink(source, cursor, nameEnd) ? collectClassSink(classes, source, cursor, nameEnd) : nameEnd;
1695
+ }
1696
+ return classes;
1697
+ }
1698
+
1331
1699
  const GLOB_MAGIC_RE = /[*?[\]{}]/;
1332
1700
  const DEFAULT_IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", ".turbo", "dist", "build"]);
1333
1701
  function normalizeFileId(id) {
1334
- return id.split(/[?#]/, 1)[0].replace(/\\/g, "/");
1702
+ return transformCache.normalizePathSeparators(id.split(/[?#]/, 1)[0]);
1335
1703
  }
1336
1704
  function normalizeRoot(rootDir) {
1337
- return path__namespace.resolve(rootDir).replace(/\\/g, "/");
1705
+ return transformCache.normalizePathSeparators(path__namespace.resolve(rootDir));
1338
1706
  }
1339
1707
  function hasGlobMagic(pattern) {
1340
1708
  return GLOB_MAGIC_RE.test(pattern);
@@ -1343,7 +1711,7 @@ function escapeRegExp(ch) {
1343
1711
  return /[|\\{}()[\]^$+?.]/.test(ch) ? `\\${ch}` : ch;
1344
1712
  }
1345
1713
  function globToRegExp(pattern) {
1346
- const normalized = pattern.replace(/\\/g, "/");
1714
+ const normalized = transformCache.normalizePathSeparators(pattern);
1347
1715
  let out = "^";
1348
1716
  for (let i = 0; i < normalized.length; i++) {
1349
1717
  const ch = normalized[i];
@@ -1366,7 +1734,7 @@ function globToRegExp(pattern) {
1366
1734
  function relativeToRoot(file, rootDir) {
1367
1735
  const root = normalizeRoot(rootDir);
1368
1736
  const normalizedFile = normalizeFileId(file);
1369
- return path__namespace.posix.relative(root, normalizedFile).replace(/\\/g, "/");
1737
+ return transformCache.normalizePathSeparators(path__namespace.posix.relative(root, normalizedFile));
1370
1738
  }
1371
1739
  function matchesPattern(id, pattern, rootDir) {
1372
1740
  const file = normalizeFileId(id);
@@ -1375,7 +1743,7 @@ function matchesPattern(id, pattern, rootDir) {
1375
1743
  pattern.lastIndex = 0;
1376
1744
  return pattern.test(file) || pattern.test(relative);
1377
1745
  }
1378
- const normalizedPattern = pattern.replace(/\\/g, "/");
1746
+ const normalizedPattern = transformCache.normalizePathSeparators(pattern);
1379
1747
  if (hasGlobMagic(normalizedPattern)) {
1380
1748
  const re = globToRegExp(normalizedPattern);
1381
1749
  return re.test(file) || re.test(relative);
@@ -1442,9 +1810,15 @@ function expandFilePatterns(rootDir, patterns) {
1442
1810
  return htmlEscape.sortStrings(files);
1443
1811
  }
1444
1812
 
1813
+ const LINE_SEPARATOR$1 = String.fromCodePoint(8232);
1814
+ const PARAGRAPH_SEPARATOR$1 = String.fromCodePoint(8233);
1445
1815
  function safeJsonForScriptTag(value, prettyPrint = false) {
1446
1816
  const json = prettyPrint ? JSON.stringify(value, null, 2) : JSON.stringify(value);
1447
- return json.replace(/</g, "\\u003C").replace(/>/g, "\\u003E").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
1817
+ let escaped = htmlEscape.replaceEveryLiteral(json, "<", htmlEscape.unicodeEscape("003C"));
1818
+ escaped = htmlEscape.replaceEveryLiteral(escaped, ">", htmlEscape.unicodeEscape("003E"));
1819
+ escaped = htmlEscape.replaceEveryLiteral(escaped, "&", htmlEscape.unicodeEscape("0026"));
1820
+ escaped = htmlEscape.replaceEveryLiteral(escaped, LINE_SEPARATOR$1, htmlEscape.unicodeEscape("2028"));
1821
+ return htmlEscape.replaceEveryLiteral(escaped, PARAGRAPH_SEPARATOR$1, htmlEscape.unicodeEscape("2029"));
1448
1822
  }
1449
1823
  function injectHtmlOpeningAttr(html, attr) {
1450
1824
  const lower = html.toLowerCase();
@@ -1576,11 +1950,17 @@ function injectRecoveryManifest(html, manifest) {
1576
1950
  return html + scriptTag;
1577
1951
  }
1578
1952
 
1953
+ const LINE_SEPARATOR = String.fromCodePoint(8232);
1954
+ const PARAGRAPH_SEPARATOR = String.fromCodePoint(8233);
1579
1955
  function escapeJsonForInlineScript(json) {
1580
- return json.replace(/`/g, "\\u0060").replace(/\$/g, "\\u0024").replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
1956
+ let escaped = htmlEscape.replaceEveryLiteral(json, "`", htmlEscape.unicodeEscape("0060"));
1957
+ escaped = htmlEscape.replaceEveryLiteral(escaped, "$", htmlEscape.unicodeEscape("0024"));
1958
+ escaped = htmlEscape.replaceEveryLiteral(escaped, "<", htmlEscape.unicodeEscape("003c"));
1959
+ escaped = htmlEscape.replaceEveryLiteral(escaped, LINE_SEPARATOR, htmlEscape.unicodeEscape("2028"));
1960
+ return htmlEscape.replaceEveryLiteral(escaped, PARAGRAPH_SEPARATOR, htmlEscape.unicodeEscape("2029"));
1581
1961
  }
1582
1962
  function escapeForDoubleQuotedString(value) {
1583
- return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
1963
+ return htmlEscape.escapeDoubleQuotedString(value);
1584
1964
  }
1585
1965
 
1586
1966
  function isParserMode(value) {
@@ -1627,15 +2007,13 @@ function isSameFileVersion(before, after) {
1627
2007
  return before.dev === after.dev && before.ino === after.ino && before.size === after.size && before.mtimeNs === after.mtimeNs && before.ctimeNs === after.ctimeNs;
1628
2008
  }
1629
2009
 
1630
- function escapeTsString(value) {
1631
- return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\r/g, "\\r").replace(/\n/g, "\\n");
1632
- }
1633
2010
  function generateThemeDts(opts) {
1634
2011
  const { theme, sourceFiles } = opts;
1635
2012
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1636
- const sources = sourceFiles.join(", ").replace(/[\r\n]/g, " ");
1637
- const toUnion = (tokens) => tokens.map((t) => `'${escapeTsString(t)}'`).join(" | ");
1638
- const quoteKey = (key) => /^[a-z_$][\w$]*$/i.test(key) ? key : `'${escapeTsString(key)}'`;
2013
+ const withoutCarriageReturns = htmlEscape.replaceEveryLiteral(sourceFiles.join(", "), "\r", " ");
2014
+ const sources = htmlEscape.replaceEveryLiteral(withoutCarriageReturns, "\n", " ");
2015
+ const toUnion = (tokens) => tokens.map((t) => `'${htmlEscape.escapeSingleQuotedString(t)}'`).join(" | ");
2016
+ const quoteKey = (key) => /^[a-z_$][\w$]*$/i.test(key) ? key : `'${htmlEscape.escapeSingleQuotedString(key)}'`;
1639
2017
  const entries = [];
1640
2018
  if (theme.colors.length > 0) {
1641
2019
  entries.push(` colors: ${toUnion(theme.colors)};`);
@@ -1773,6 +2151,15 @@ function createThemeGroupsModule(tokens) {
1773
2151
  ].join("\n");
1774
2152
  }
1775
2153
 
2154
+ function registerWebpackAssetProcessor(compiler, processAssets) {
2155
+ compiler.hooks.compilation.tap("csszyx:post", (compilation) => {
2156
+ const stage = compiler.webpack?.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE || compilation.constructor.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE;
2157
+ compilation.hooks.processAssets.tap(
2158
+ { name: "csszyx:post", stage: stage || 400 },
2159
+ (assets) => processAssets(assets, compilation, compiler)
2160
+ );
2161
+ });
2162
+ }
1776
2163
  const CHECKSUM_PLACEHOLDER = "___CSSZYX_CHECKSUM___";
1777
2164
  const MANGLE_MAP_PLACEHOLDER = "___CSSZYX_MANGLE_MAP___";
1778
2165
  const VAR_MANGLE_MAP_PLACEHOLDER = "___CSSZYX_VAR_MANGLE_MAP___";
@@ -1784,7 +2171,6 @@ const TRANSFORM_MEMORY_CACHE_MAX_CODE_CHARS = 32e6;
1784
2171
  const MAX_SAFELIST_CLASSES = 1e5;
1785
2172
  const DEFAULT_VAR_MANGLE_MAP_MAX_BYTES = 100 * 1024;
1786
2173
  const GLOBAL_VAR_ALIAS_MAP_OWNER = "\0csszyx:global-var-aliases";
1787
- const DIRECTIVE_PROLOGUE_PREFIX_RE = /^((?:\s|\/\/[^\n]*\n|\/\*(?:[^*]|\*(?!\/))*\*\/)*)(['"]use (?:client|server)['"];?\s*)/;
1788
2174
  function findOpeningTag(source, tag) {
1789
2175
  const lower = source.toLowerCase();
1790
2176
  const marker = `<${tag}`;
@@ -1810,9 +2196,9 @@ let _hasWarnedTransformCacheVersion = false;
1810
2196
  let _hasWarnedNativeFallback = false;
1811
2197
  const _loggedActiveParsers = /* @__PURE__ */ new Set();
1812
2198
  const _babelFallbackFiles = /* @__PURE__ */ new Set();
1813
- 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.ByAzFdqO.cjs', document.baseURI).href)));
2199
+ 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.DtPQz8Kg.cjs', document.baseURI).href)));
1814
2200
  const PLUGIN_VERSION = findPackageVersionFromFile(
1815
- node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/unplugin.ByAzFdqO.cjs', document.baseURI).href))),
2201
+ node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/unplugin.DtPQz8Kg.cjs', document.baseURI).href))),
1816
2202
  UNKNOWN_PACKAGE_VERSION
1817
2203
  );
1818
2204
  const COMPILER_VERSION = findPackageVersionFromModule("@csszyx/compiler", UNKNOWN_PACKAGE_VERSION);
@@ -1857,10 +2243,10 @@ function stripCssBlockComments(code) {
1857
2243
  let i = 0;
1858
2244
  const n = code.length;
1859
2245
  while (i < n) {
1860
- if (code.charCodeAt(i) === SLASH && code.charCodeAt(i + 1) === STAR) {
2246
+ if (code.codePointAt(i) === SLASH && code.codePointAt(i + 1) === STAR) {
1861
2247
  out += code.slice(last, i);
1862
2248
  i += 2;
1863
- while (i < n && !(code.charCodeAt(i) === STAR && code.charCodeAt(i + 1) === SLASH)) {
2249
+ while (i < n && !(code.codePointAt(i) === STAR && code.codePointAt(i + 1) === SLASH)) {
1864
2250
  i++;
1865
2251
  }
1866
2252
  i += 2;
@@ -1966,9 +2352,17 @@ function shouldEmitWarning(quiet, devOnly, isProduction) {
1966
2352
  return true;
1967
2353
  }
1968
2354
  function normalizeForMatch(p) {
1969
- const n = p.replace(/\\/g, "/");
2355
+ const n = transformCache.normalizePathSeparators(p);
1970
2356
  return n.length > 1 && n.endsWith("/") ? n.slice(0, -1) : n;
1971
2357
  }
2358
+ function findJsxExpressionEnd(code, bodyStart) {
2359
+ return findBalancedCodeEnd(code, bodyStart);
2360
+ }
2361
+ function warnPrescanBudgetSkip(filePath) {
2362
+ console.warn(
2363
+ `[csszyx] prescan skipped ${filePath}: the file exceeds the AST node budget, so NONE of its classes reached the safelist and their CSS will not be generated. Raise \`build.astBudgetLimit\` in the csszyx plugin options, or split the file.`
2364
+ );
2365
+ }
1972
2366
  function resolveCompileSourceDirs(root, sources, realpathDir = (p) => {
1973
2367
  try {
1974
2368
  const real = fs__namespace.realpathSync(p);
@@ -2021,6 +2415,9 @@ function fileMayContainSafelistableSz(content) {
2021
2415
  // engine-parity harness caught its classes missing on all engines.
2022
2416
  content.includes("dynamic(");
2023
2417
  }
2418
+ function mangleEligibleClasses(ownedClasses, authoredClasses) {
2419
+ return compiler.sortStrings([...ownedClasses].filter((className) => !authoredClasses.has(className)));
2420
+ }
2024
2421
  function isPackagesSkippedSource(id, sourceDirs = []) {
2025
2422
  const p = normalizeForMatch(id);
2026
2423
  if (p.includes("node_modules")) {
@@ -2041,8 +2438,8 @@ ${list}
2041
2438
  Add the package directory to \`compileSources\` (or move the file out of packages/) \u2014 otherwise their \`sz\` produces no CSS.`;
2042
2439
  }
2043
2440
  function computeSafelistRelPath(rootDir, safelistFilename, cssId) {
2044
- const safelistPath = path__namespace.join(rootDir, safelistFilename).replace(/\\/g, "/");
2045
- const cssDir = path__namespace.dirname(cssId).replace(/\\/g, "/");
2441
+ const safelistPath = transformCache.normalizePathSeparators(path__namespace.join(rootDir, safelistFilename));
2442
+ const cssDir = transformCache.normalizePathSeparators(path__namespace.dirname(cssId));
2046
2443
  let relPath = path__namespace.posix.relative(cssDir, safelistPath);
2047
2444
  if (!relPath.startsWith(".")) {
2048
2445
  relPath = `./${relPath}`;
@@ -2216,7 +2613,10 @@ function normalizeGlobalVarAliasesForCache(aliases) {
2216
2613
  if (!aliases) {
2217
2614
  return [];
2218
2615
  }
2219
- const entries = aliases instanceof Map ? aliases.entries() : Array.isArray(aliases) ? aliases : Object.entries(aliases);
2616
+ let entries;
2617
+ if (aliases instanceof Map) entries = aliases.entries();
2618
+ else if (Array.isArray(aliases)) entries = aliases;
2619
+ else entries = Object.entries(aliases);
2220
2620
  const normalized = /* @__PURE__ */ new Map();
2221
2621
  for (const [original, alias] of entries) {
2222
2622
  if (original.startsWith("--") && alias.startsWith("--")) {
@@ -2421,7 +2821,7 @@ function findPackageVersionFromFile(file, fallback) {
2421
2821
  }
2422
2822
  }
2423
2823
  function normalizeSourceFilename(filename) {
2424
- return filename.replace(/\\/g, "/");
2824
+ return transformCache.normalizePathSeparators(filename);
2425
2825
  }
2426
2826
  const SCRIPT_ID_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
2427
2827
  const SOURCE_MODULE_EXTENSIONS = [...SCRIPT_ID_EXTENSIONS, ".cts", ".mts", ".cjs", ".mjs"];
@@ -2429,11 +2829,26 @@ function matchesScriptExtension(id, extensions) {
2429
2829
  return extensions.some((ext) => id.endsWith(ext) || id.includes(`${ext}?`));
2430
2830
  }
2431
2831
  function insertRuntimeImport(code, importStmt) {
2432
- const directiveMatch = code.match(DIRECTIVE_PROLOGUE_PREFIX_RE);
2433
- if (!directiveMatch) {
2434
- return `${importStmt}${code}`;
2832
+ return runtimeImportScan.insertAfterUseDirective(code, importStmt);
2833
+ }
2834
+ function scanClassExpression(source, from) {
2835
+ let depth = 0;
2836
+ let index = from;
2837
+ while (index < source.length) {
2838
+ const char = source[index];
2839
+ if (char === "(" || char === "[") {
2840
+ depth++;
2841
+ } else if (char === ")" || char === "]") {
2842
+ if (depth === 0) {
2843
+ break;
2844
+ }
2845
+ depth--;
2846
+ } else if (depth === 0 && (char === "," || char === ";" || char === "\n" || char === "}")) {
2847
+ break;
2848
+ }
2849
+ index++;
2435
2850
  }
2436
- return code.replace(directiveMatch[0], `${directiveMatch[1]}${directiveMatch[2]}${importStmt}`);
2851
+ return index;
2437
2852
  }
2438
2853
  function mangleCodeClassesSync(code, mangleMap) {
2439
2854
  function mangleClassString(classString) {
@@ -2441,6 +2856,58 @@ function mangleCodeClassesSync(code, mangleMap) {
2441
2856
  return mangleMap[cls.replace(/\\(.)/g, "$1")] || cls;
2442
2857
  }).join(" ");
2443
2858
  }
2859
+ function mangleClassTemplate(fullMatch, templateContent) {
2860
+ let changed = false;
2861
+ let output = "";
2862
+ let cursor = 0;
2863
+ while (cursor < templateContent.length) {
2864
+ const interpolationStart = templateContent.indexOf("${", cursor);
2865
+ const quasiEnd = interpolationStart === -1 ? templateContent.length : interpolationStart;
2866
+ const quasi = mangleTemplateQuasi(templateContent.slice(cursor, quasiEnd));
2867
+ output += quasi.value;
2868
+ changed ||= quasi.changed;
2869
+ if (interpolationStart === -1) break;
2870
+ const interpolationEnd = findTemplateInterpolationEnd(
2871
+ templateContent,
2872
+ interpolationStart + 2
2873
+ );
2874
+ const inner = templateContent.slice(interpolationStart + 2, interpolationEnd - 1);
2875
+ const interpolation = mangleTemplateInterpolation(inner);
2876
+ output += `\${${interpolation.value}}`;
2877
+ changed ||= interpolation.changed;
2878
+ cursor = interpolationEnd;
2879
+ }
2880
+ return changed ? `className:\`${output}\`` : fullMatch;
2881
+ }
2882
+ function mangleTemplateQuasi(quasi) {
2883
+ const trimmed = quasi.trim();
2884
+ if (!trimmed) return { value: quasi, changed: false };
2885
+ const mangled = mangleClassString(trimmed);
2886
+ return {
2887
+ value: mangled === trimmed ? quasi : quasi.replace(trimmed, mangled),
2888
+ changed: mangled !== trimmed
2889
+ };
2890
+ }
2891
+ function findTemplateInterpolationEnd(templateContent, bodyStart) {
2892
+ let depth = 0;
2893
+ for (let cursor = bodyStart; cursor < templateContent.length; cursor++) {
2894
+ if (templateContent[cursor] === "{") depth++;
2895
+ else if (templateContent[cursor] === "}" && depth-- === 0) return cursor + 1;
2896
+ }
2897
+ return templateContent.length;
2898
+ }
2899
+ function mangleTemplateInterpolation(inner) {
2900
+ let changed = false;
2901
+ const value = inner.replace(/"([^"]*)"/g, (quoted, classString) => {
2902
+ const parts = classString.split(/\s+/).filter(Boolean);
2903
+ if (parts.length === 0) return quoted;
2904
+ const mangled = parts.map((className) => mangleMap[className] || className).join(" ");
2905
+ if (mangled === classString) return quoted;
2906
+ changed = true;
2907
+ return `"${mangled}"`;
2908
+ });
2909
+ return { value, changed };
2910
+ }
2444
2911
  let result = code.replace(/(?:class(?:Name)?|sz)[:=]\s*"((?:[^"\\]|\\.)*)"/g, (match, classes) => {
2445
2912
  const mangled = mangleClassString(classes);
2446
2913
  if (mangled === classes) {
@@ -2454,92 +2921,7 @@ function mangleCodeClassesSync(code, mangleMap) {
2454
2921
  }
2455
2922
  return match.replace(classes, mangled);
2456
2923
  });
2457
- result = result.replace(/className:\s*`([^`]+)`/g, (fullMatch, tplContent) => {
2458
- let changed = false;
2459
- let out = "";
2460
- let i = 0;
2461
- while (i < tplContent.length) {
2462
- const interStart = tplContent.indexOf("${", i);
2463
- if (interStart === -1) {
2464
- const quasi2 = tplContent.slice(i);
2465
- const trimmed2 = quasi2.trim();
2466
- if (trimmed2) {
2467
- const m = mangleClassString(trimmed2);
2468
- if (m !== trimmed2) {
2469
- changed = true;
2470
- out += quasi2.replace(trimmed2, m);
2471
- } else {
2472
- out += quasi2;
2473
- }
2474
- } else {
2475
- out += quasi2;
2476
- }
2477
- break;
2478
- }
2479
- const quasi = tplContent.slice(i, interStart);
2480
- const trimmed = quasi.trim();
2481
- if (trimmed) {
2482
- const m = mangleClassString(trimmed);
2483
- if (m !== trimmed) {
2484
- changed = true;
2485
- out += quasi.replace(trimmed, m);
2486
- } else {
2487
- out += quasi;
2488
- }
2489
- } else {
2490
- out += quasi;
2491
- }
2492
- let j = interStart + 2;
2493
- let depth = 0;
2494
- while (j < tplContent.length) {
2495
- if (tplContent[j] === "{") {
2496
- depth++;
2497
- } else if (tplContent[j] === "}") {
2498
- if (depth === 0) {
2499
- j++;
2500
- break;
2501
- }
2502
- depth--;
2503
- }
2504
- j++;
2505
- }
2506
- const interInner = tplContent.slice(interStart + 2, j - 1);
2507
- const mangledInner = interInner.replace(/"([^"]*)"/g, (qm, inner) => {
2508
- const parts = inner.split(/\s+/).filter(Boolean);
2509
- if (parts.length === 0) {
2510
- return qm;
2511
- }
2512
- const m = parts.map((p) => mangleMap[p] || p).join(" ");
2513
- if (m === inner) {
2514
- return qm;
2515
- }
2516
- changed = true;
2517
- return `"${m}"`;
2518
- });
2519
- out += `\${${mangledInner}}`;
2520
- i = j;
2521
- }
2522
- return changed ? `className:\`${out}\`` : fullMatch;
2523
- });
2524
- function scanClassExpression(source, from) {
2525
- let depth = 0;
2526
- let j = from;
2527
- while (j < source.length) {
2528
- const ch = source[j];
2529
- if (ch === "(" || ch === "[") {
2530
- depth++;
2531
- } else if (ch === ")" || ch === "]") {
2532
- if (depth === 0) {
2533
- break;
2534
- }
2535
- depth--;
2536
- } else if (depth === 0 && (ch === "," || ch === ";" || ch === "\n" || ch === "}")) {
2537
- break;
2538
- }
2539
- j++;
2540
- }
2541
- return j;
2542
- }
2924
+ result = result.replace(/className:\s*`([^`]+)`/g, mangleClassTemplate);
2543
2925
  function mangleTernaryClassStrings(expr) {
2544
2926
  const qIdx = expr.indexOf("?");
2545
2927
  if (qIdx === -1 || !expr.slice(qIdx).includes(":")) {
@@ -2747,7 +3129,12 @@ function createCsszyxPlugins(options = {}) {
2747
3129
  }
2748
3130
  if (!_loggedActiveParsers.has(parserMode)) {
2749
3131
  _loggedActiveParsers.add(parserMode);
2750
- const detail = parserDegraded ? "oxc (degraded from default `rust`: no native binary for this platform)" : parserMode === "rust" ? "rust (native engine)" : parserMode;
3132
+ let detail = parserMode;
3133
+ if (parserDegraded) {
3134
+ detail = "oxc (degraded from default `rust`: no native binary for this platform)";
3135
+ } else if (parserMode === "rust") {
3136
+ detail = "rust (native engine)";
3137
+ }
2751
3138
  console.warn(`[csszyx] active parser: ${detail}`);
2752
3139
  }
2753
3140
  }
@@ -2768,6 +3155,7 @@ function createCsszyxPlugins(options = {}) {
2768
3155
  skipWarningEmitted: false,
2769
3156
  classesCapped: false,
2770
3157
  ownedClasses: /* @__PURE__ */ new Set(),
3158
+ authoredClasses: /* @__PURE__ */ new Set(),
2771
3159
  mangleMap: {},
2772
3160
  varMangleEntriesByFile: /* @__PURE__ */ new Map(),
2773
3161
  varMangleMap: Object.fromEntries(earlyGlobalVarAliasEntries),
@@ -2884,53 +3272,76 @@ function createCsszyxPlugins(options = {}) {
2884
3272
  }
2885
3273
  const cacheKey = cacheEnabled ? transformCache.createTransformCacheKey(cacheInput) : null;
2886
3274
  if (cacheEnabled && cacheKey) {
2887
- const memoryCached = transformMemoryCache.get(cacheKey.key);
2888
- if (memoryCached) {
2889
- transformMemoryCache.delete(cacheKey.key);
2890
- transformMemoryCache.set(cacheKey.key, memoryCached);
2891
- return memoryCached;
2892
- }
2893
- const cached = transformCache.readTransformCache(cacheRoot, cacheInput, cacheKey);
2894
- if (cached) {
2895
- rememberTransformCacheEntry(cacheKey.key, cached);
2896
- return cached;
2897
- }
2898
- if (astBudget === void 0) {
2899
- const handoff = prescanResultHandoff.get(effectiveFilename);
2900
- if (handoff) {
2901
- prescanResultHandoff.delete(effectiveFilename);
2902
- if (handoff.inputSha256 === cacheKey.inputSha256) {
2903
- return handoff.result;
2904
- }
2905
- }
2906
- }
2907
- }
2908
- let result;
3275
+ const cached = findConfiguredTransformCacheEntry(
3276
+ cacheRoot,
3277
+ cacheInput,
3278
+ cacheKey,
3279
+ effectiveFilename,
3280
+ astBudget
3281
+ );
3282
+ if (cached) return cached;
3283
+ }
3284
+ const execution = runConfiguredParser(source, effectiveFilename, compilerOptions);
3285
+ if (cacheEnabled && cacheKey && execution.cacheable) {
3286
+ transformCache.writeTransformCache(cacheRoot, cacheInput, execution.result, cacheKey);
3287
+ rememberTransformCacheEntry(cacheKey.key, execution.result);
3288
+ }
3289
+ return execution.result;
3290
+ }
3291
+ function findConfiguredTransformCacheEntry(cacheRoot, cacheInput, cacheKey, effectiveFilename, astBudget) {
3292
+ const memoryCached = transformMemoryCache.get(cacheKey.key);
3293
+ if (memoryCached) {
3294
+ transformMemoryCache.delete(cacheKey.key);
3295
+ transformMemoryCache.set(cacheKey.key, memoryCached);
3296
+ return memoryCached;
3297
+ }
3298
+ const diskCached = transformCache.readTransformCache(cacheRoot, cacheInput, cacheKey);
3299
+ if (diskCached) {
3300
+ rememberTransformCacheEntry(cacheKey.key, diskCached);
3301
+ return diskCached;
3302
+ }
3303
+ if (astBudget !== void 0) return null;
3304
+ const handoff = prescanResultHandoff.get(effectiveFilename);
3305
+ if (!handoff) return null;
3306
+ prescanResultHandoff.delete(effectiveFilename);
3307
+ return handoff.inputSha256 === cacheKey.inputSha256 ? handoff.result : null;
3308
+ }
3309
+ function runConfiguredParser(source, effectiveFilename, compilerOptions) {
2909
3310
  if (parserMode === "babel") {
2910
- result = compiler.transformSourceCode(source, effectiveFilename, compilerOptions);
2911
- } else if (parserMode === "rust") {
2912
- result = compiler.transformRust(source, effectiveFilename, compilerOptions);
2913
- } else {
2914
- try {
2915
- result = compiler.transformOxc(source, effectiveFilename, compilerOptions);
2916
- } catch (err) {
2917
- result = compiler.transformSourceCode(source, effectiveFilename, compilerOptions);
2918
- const reason = err instanceof Error ? err.message : String(err);
2919
- result.diagnostics.push(
2920
- `[csszyx] oxc parser fell back to Babel for ${effectiveFilename}: ${reason}`
2921
- );
2922
- if (!_babelFallbackFiles.has(effectiveFilename)) {
2923
- _babelFallbackFiles.add(effectiveFilename);
2924
- console.warn(
2925
- `[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.`
2926
- );
2927
- }
2928
- return result;
2929
- }
3311
+ return {
3312
+ result: compiler.transformSourceCode(source, effectiveFilename, compilerOptions),
3313
+ cacheable: true
3314
+ };
2930
3315
  }
2931
- if (cacheEnabled && cacheKey) {
2932
- transformCache.writeTransformCache(cacheRoot, cacheInput, result, cacheKey);
2933
- rememberTransformCacheEntry(cacheKey.key, result);
3316
+ if (parserMode === "rust") {
3317
+ return {
3318
+ result: compiler.transformRust(source, effectiveFilename, compilerOptions),
3319
+ cacheable: true
3320
+ };
3321
+ }
3322
+ try {
3323
+ return {
3324
+ result: compiler.transformOxc(source, effectiveFilename, compilerOptions),
3325
+ cacheable: true
3326
+ };
3327
+ } catch (error) {
3328
+ return {
3329
+ result: runBabelFallback(source, effectiveFilename, compilerOptions, error),
3330
+ cacheable: false
3331
+ };
3332
+ }
3333
+ }
3334
+ function runBabelFallback(source, effectiveFilename, compilerOptions, error) {
3335
+ const result = compiler.transformSourceCode(source, effectiveFilename, compilerOptions);
3336
+ const reason = error instanceof Error ? error.message : String(error);
3337
+ result.diagnostics.push(
3338
+ `[csszyx] oxc parser fell back to Babel for ${effectiveFilename}: ${reason}`
3339
+ );
3340
+ if (!_babelFallbackFiles.has(effectiveFilename)) {
3341
+ _babelFallbackFiles.add(effectiveFilename);
3342
+ console.warn(
3343
+ `[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.`
3344
+ );
2934
3345
  }
2935
3346
  return result;
2936
3347
  }
@@ -2969,11 +3380,19 @@ function createCsszyxPlugins(options = {}) {
2969
3380
  const compilerOptions = createCompilerOptions(prescanAstBudget);
2970
3381
  const cacheRoot = transformCache.resolveTransformCacheDir(state.rootDir, options.build?.cacheDir);
2971
3382
  const results = /* @__PURE__ */ new Map();
2972
- const misses = [];
2973
- if (cacheEnabled) {
2974
- evictTransformCacheOnce();
2975
- }
3383
+ if (cacheEnabled) evictTransformCacheOnce();
2976
3384
  compiler.ensureRustTransformAvailable();
3385
+ const misses = collectRustPrescanMisses(files, compilerOptions, cacheRoot, results);
3386
+ if (misses.length === 0) return orderPrescanResults(files, results);
3387
+ try {
3388
+ runRustPrescanBatch(misses, compilerOptions, cacheRoot, results);
3389
+ } catch {
3390
+ runRustPrescanFallback(misses, results);
3391
+ }
3392
+ return orderPrescanResults(files, results);
3393
+ }
3394
+ function collectRustPrescanMisses(files, compilerOptions, cacheRoot, results) {
3395
+ const misses = [];
2977
3396
  for (const file of files) {
2978
3397
  const effectiveFilename = normalizeSourceFilename(file.filePath);
2979
3398
  const cacheInput = createConfiguredTransformCacheInput(
@@ -2982,79 +3401,54 @@ function createCsszyxPlugins(options = {}) {
2982
3401
  compilerOptions
2983
3402
  );
2984
3403
  const cacheKey = cacheEnabled ? transformCache.createTransformCacheKey(cacheInput) : null;
2985
- if (cacheEnabled && cacheKey) {
2986
- const memoryCached = transformMemoryCache.get(cacheKey.key);
2987
- if (memoryCached) {
2988
- transformMemoryCache.delete(cacheKey.key);
2989
- transformMemoryCache.set(cacheKey.key, memoryCached);
2990
- results.set(file.filePath, memoryCached);
2991
- continue;
2992
- }
2993
- const cached = transformCache.readTransformCache(cacheRoot, cacheInput, cacheKey);
2994
- if (cached) {
2995
- rememberTransformCacheEntry(cacheKey.key, cached);
2996
- results.set(file.filePath, cached);
2997
- continue;
2998
- }
2999
- }
3000
- misses.push({
3001
- filePath: file.filePath,
3002
- effectiveFilename,
3003
- content: file.content,
3404
+ const cached = cacheKey ? findConfiguredTransformCacheEntry(
3405
+ cacheRoot,
3004
3406
  cacheInput,
3005
- cacheKey
3006
- });
3407
+ cacheKey,
3408
+ effectiveFilename,
3409
+ prescanAstBudget
3410
+ ) : null;
3411
+ if (cached) results.set(file.filePath, cached);
3412
+ else misses.push({ ...file, effectiveFilename, cacheInput, cacheKey });
3007
3413
  }
3008
- if (misses.length === 0) {
3009
- return files.map((file) => {
3010
- const result = results.get(file.filePath);
3011
- return result ? { filePath: file.filePath, result } : null;
3012
- }).filter((entry) => entry !== null);
3414
+ return misses;
3415
+ }
3416
+ function runRustPrescanBatch(misses, compilerOptions, cacheRoot, results) {
3417
+ const batchResults = compiler.transformRustBatch(
3418
+ misses.map((file) => ({ filename: file.effectiveFilename, source: file.content })),
3419
+ compilerOptions
3420
+ );
3421
+ for (let index = 0; index < misses.length; index++) {
3422
+ const miss = misses[index];
3423
+ const result = batchResults[index];
3424
+ if (!miss || !result) continue;
3425
+ cacheRustPrescanResult(miss, result, cacheRoot);
3426
+ results.set(miss.filePath, result);
3013
3427
  }
3014
- try {
3015
- const batchResults = compiler.transformRustBatch(
3016
- misses.map((file) => ({
3017
- filename: file.effectiveFilename,
3018
- source: file.content
3019
- })),
3020
- compilerOptions
3021
- );
3022
- for (let index = 0; index < misses.length; index++) {
3023
- const miss = misses[index];
3024
- const result = batchResults[index];
3025
- if (!miss || !result) {
3026
- continue;
3027
- }
3028
- if (cacheEnabled && miss.cacheKey) {
3029
- transformCache.writeTransformCache(cacheRoot, miss.cacheInput, result, miss.cacheKey);
3030
- rememberTransformCacheEntry(miss.cacheKey.key, result);
3031
- }
3428
+ }
3429
+ function cacheRustPrescanResult(miss, result, cacheRoot) {
3430
+ if (!cacheEnabled || !miss.cacheKey) return;
3431
+ transformCache.writeTransformCache(cacheRoot, miss.cacheInput, result, miss.cacheKey);
3432
+ rememberTransformCacheEntry(miss.cacheKey.key, result);
3433
+ }
3434
+ function runRustPrescanFallback(misses, results) {
3435
+ for (const miss of misses) {
3436
+ try {
3437
+ const result = transformConfiguredSource(
3438
+ miss.content,
3439
+ miss.effectiveFilename,
3440
+ prescanAstBudget
3441
+ );
3032
3442
  results.set(miss.filePath, result);
3033
- }
3034
- } catch {
3035
- for (const miss of misses) {
3036
- try {
3037
- results.set(
3038
- miss.filePath,
3039
- transformConfiguredSource(
3040
- miss.content,
3041
- miss.effectiveFilename,
3042
- prescanAstBudget
3043
- )
3044
- );
3045
- } catch {
3046
- }
3443
+ } catch {
3047
3444
  }
3048
3445
  }
3049
- return files.map((file) => {
3050
- const result = results.get(file.filePath);
3051
- return result ? { filePath: file.filePath, result } : null;
3052
- }).filter((entry) => entry !== null);
3053
3446
  }
3054
- function warnPrescanBudgetSkip(filePath) {
3055
- console.warn(
3056
- `[csszyx] prescan skipped ${filePath}: the file exceeds the AST node budget, so NONE of its classes reached the safelist and their CSS will not be generated. Raise \`build.astBudgetLimit\` in the csszyx plugin options, or split the file.`
3057
- );
3447
+ function orderPrescanResults(files, results) {
3448
+ return files.flatMap((file) => {
3449
+ const result = results.get(file.filePath);
3450
+ return result ? [{ filePath: file.filePath, result }] : [];
3451
+ });
3058
3452
  }
3059
3453
  function transformPrescanSourcesIndividually(files) {
3060
3454
  const results = [];
@@ -3121,11 +3515,12 @@ function createCsszyxPlugins(options = {}) {
3121
3515
  return;
3122
3516
  }
3123
3517
  const safelistPath = path__namespace.join(state.rootDir, SAFELIST_FILENAME);
3124
- const classList = htmlEscape.escapeHtmlAttribute(Array.from(classes).join(" "));
3518
+ const classNames = Array.from(classes);
3519
+ const classList = htmlEscape.escapeHtmlAttribute(classNames.join(" "));
3125
3520
  const content = `<!-- Auto-generated by csszyx \u2014 DO NOT EDIT -->
3126
3521
  <!-- Tailwind CSS scans this file for class name detection -->
3127
3522
  <div class="${classList}"><div class="${classList}">x</div><div class="${classList}">x</div></div>
3128
- `;
3523
+ ` + htmlEscape.renderTailwindScannerCandidates(classNames);
3129
3524
  try {
3130
3525
  let existing = "";
3131
3526
  try {
@@ -3200,6 +3595,7 @@ function createCsszyxPlugins(options = {}) {
3200
3595
  } catch {
3201
3596
  return;
3202
3597
  }
3598
+ recordAuthoredClasses(content);
3203
3599
  if (fileMayContainSafelistableSz(content)) {
3204
3600
  prescanSources.push({ filePath, content });
3205
3601
  }
@@ -3302,7 +3698,7 @@ function createCsszyxPlugins(options = {}) {
3302
3698
  for (const kv of objStr.matchAll(numKv)) {
3303
3699
  try {
3304
3700
  collectTransformClasses(
3305
- compiler.transform({ [kv[1]]: parseFloat(kv[2]) }),
3701
+ compiler.transform({ [kv[1]]: Number.parseFloat(kv[2]) }),
3306
3702
  discoveredClasses
3307
3703
  );
3308
3704
  } catch {
@@ -3342,19 +3738,6 @@ function createCsszyxPlugins(options = {}) {
3342
3738
  }
3343
3739
  }
3344
3740
  }
3345
- function findJsxExpressionEnd(code, bodyStart) {
3346
- let depth = 1;
3347
- let index = bodyStart;
3348
- while (index < code.length && depth > 0) {
3349
- if (code[index] === "{") {
3350
- depth++;
3351
- } else if (code[index] === "}") {
3352
- depth--;
3353
- }
3354
- index++;
3355
- }
3356
- return index - 1;
3357
- }
3358
3741
  function collectExpressionClasses(code) {
3359
3742
  for (const match of code.matchAll(/className=\{/g)) {
3360
3743
  const bodyStart = (match.index ?? 0) + match[0].length;
@@ -3368,17 +3751,22 @@ function createCsszyxPlugins(options = {}) {
3368
3751
  collectQuotedAttributeClasses(code);
3369
3752
  collectExpressionClasses(code);
3370
3753
  }
3754
+ function recordAuthoredClasses(code) {
3755
+ for (const className of collectAuthoredClassNames(code)) {
3756
+ addSafelistClass(className);
3757
+ state.authoredClasses.add(className);
3758
+ }
3759
+ }
3371
3760
  function finalizeMangleMap() {
3372
- const sortedClasses = Array.from(state.ownedClasses);
3373
3761
  const newMap = {};
3374
3762
  let tokenIndex = 0;
3375
- for (let i = 0; i < sortedClasses.length; i++) {
3763
+ for (const className of mangleEligibleClasses(state.ownedClasses, state.authoredClasses)) {
3376
3764
  let token = core.encode(tokenIndex);
3377
- while (mangleReserved.has(token)) {
3765
+ while (mangleReserved.has(token) || state.authoredClasses.has(token)) {
3378
3766
  tokenIndex++;
3379
3767
  token = core.encode(tokenIndex);
3380
3768
  }
3381
- newMap[sortedClasses[i]] = token;
3769
+ newMap[className] = token;
3382
3770
  tokenIndex++;
3383
3771
  }
3384
3772
  state.mangleMap = newMap;
@@ -3426,6 +3814,147 @@ function createCsszyxPlugins(options = {}) {
3426
3814
  }
3427
3815
  return result;
3428
3816
  }
3817
+ function unchangedPreTransform(code) {
3818
+ return {
3819
+ code,
3820
+ transformed: false,
3821
+ usesRuntime: false,
3822
+ usesMerge: false,
3823
+ usesSzcn: false,
3824
+ usesSzPart: false,
3825
+ usesColorVar: false,
3826
+ usesSpacingVar: false,
3827
+ usesUnitVar: false
3828
+ };
3829
+ }
3830
+ function transformTailwindCssEntry(code, id) {
3831
+ state.sawAnyCss = true;
3832
+ if (!cssImportsTailwind(code)) return null;
3833
+ state.sawTailwindEntry = true;
3834
+ if (cssHasContentScope(code)) state.tailwindEntryScoped = true;
3835
+ if (!hasInjectableTailwindCandidate(state.classes)) return null;
3836
+ const relPath = computeSafelistRelPath(state.rootDir, SAFELIST_FILENAME, id);
3837
+ const transformed = appendTailwindSourceDirective(code, relPath);
3838
+ return transformed === null ? null : { code: transformed, map: null };
3839
+ }
3840
+ function reportTransformDiagnostics(result, id, warn) {
3841
+ for (const message of result.diagnostics) {
3842
+ if (message.includes("unresolvable sz spread")) {
3843
+ state.spreadWarnings.add(`${id}
3844
+ ${message}`);
3845
+ } else if (message.includes("AST budget exceeded")) {
3846
+ console.warn(`[csszyx] ${id}
3847
+ ${message}`);
3848
+ }
3849
+ }
3850
+ if (quiet || result.diagnostics.length === 0 || process.env.NODE_ENV === "production") {
3851
+ return;
3852
+ }
3853
+ for (const message of result.diagnostics) {
3854
+ if (message.includes("unresolvable sz spread") || message.includes("AST budget exceeded")) {
3855
+ continue;
3856
+ }
3857
+ warn(`[csszyx] ${id}
3858
+ ${message}`);
3859
+ }
3860
+ }
3861
+ function compilerPreTransformOutput(result) {
3862
+ return {
3863
+ code: result.code,
3864
+ transformed: result.transformed,
3865
+ usesRuntime: result.usesRuntime,
3866
+ usesMerge: result.usesMerge,
3867
+ usesSzcn: result.usesSzcn,
3868
+ usesSzPart: result.usesSzPart,
3869
+ usesColorVar: result.usesColorVar,
3870
+ usesSpacingVar: result.usesSpacingVar,
3871
+ usesUnitVar: result.usesUnitVar,
3872
+ szClasses: result.classes
3873
+ };
3874
+ }
3875
+ function transformSzSource(code, id, warn) {
3876
+ if (id.endsWith(".vue")) {
3877
+ const result2 = vueAdapter.preprocess(code, options);
3878
+ return { ...unchangedPreTransform(result2.code), transformed: result2.transformed };
3879
+ }
3880
+ if (id.endsWith(".svelte")) {
3881
+ const result2 = svelteAdapter.preprocess(code, options);
3882
+ return result2 ? { ...unchangedPreTransform(result2.code), transformed: true } : unchangedPreTransform(code);
3883
+ }
3884
+ const transformStarted = node_perf_hooks.performance.now();
3885
+ const result = transformConfiguredSource(code, id);
3886
+ traceBenchTiming("transform-hook", id, node_perf_hooks.performance.now() - transformStarted);
3887
+ recordFileVarMangleEntries(state, id, cssVariableEntries(result));
3888
+ recordFileCSSVariableMetrics(state, id, result.code);
3889
+ reportTransformDiagnostics(result, id, warn);
3890
+ for (const [token, data] of result.recoveryTokens) state.recoveryTokens.set(token, data);
3891
+ return compilerPreTransformOutput(result);
3892
+ }
3893
+ function injectLayoutHydration(code, id) {
3894
+ if (!code.includes("<html") || !/(?:layout|Root|Document|app)\.tsx?$/i.test(id)) {
3895
+ return null;
3896
+ }
3897
+ let transformedCode = code;
3898
+ const attrName = options.production?.minify ? "data-sz-cs" : "data-sz-checksum";
3899
+ const htmlTag = findOpeningTag(transformedCode, "html");
3900
+ if (htmlTag) {
3901
+ transformedCode = `${transformedCode.slice(0, htmlTag.close)} ${attrName}="${CHECKSUM_PLACEHOLDER}"${transformedCode.slice(htmlTag.close)}`;
3902
+ }
3903
+ const debugScript = `<script dangerouslySetInnerHTML={{__html: \`(function(){var m=${MANGLE_MAP_PLACEHOLDER};var vm=${VAR_MANGLE_MAP_PLACEHOLDER};var gp=decodeURIComponent(${escapeJsonForInlineScript(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})}}})()\`}} />`;
3904
+ const bodyTag = findOpeningTag(transformedCode, "body");
3905
+ if (bodyTag) {
3906
+ transformedCode = `${transformedCode.slice(0, bodyTag.close + 1)}${debugScript}${transformedCode.slice(bodyTag.close + 1)}`;
3907
+ }
3908
+ return transformedCode;
3909
+ }
3910
+ function requiredRuntimeHelpers(output) {
3911
+ const helpers = [];
3912
+ if (output.usesRuntime) helpers.push("_sz");
3913
+ if (output.usesMerge) helpers.push("_szMerge");
3914
+ if (output.usesSzcn) helpers.push("_szcn");
3915
+ if (output.usesSzPart) helpers.push("_szPart");
3916
+ if (output.usesColorVar) helpers.push("__szColorVar");
3917
+ if (output.usesSpacingVar) helpers.push("__szSpacingVar");
3918
+ if (output.usesUnitVar) helpers.push("__szUnitVar");
3919
+ return helpers;
3920
+ }
3921
+ function injectRuntimeHelpers(code, output) {
3922
+ const imports = requiredRuntimeHelpers(output);
3923
+ const hasRuntimeImport = imports.length > 0 && code.includes("@csszyx/runtime");
3924
+ const needed = hasRuntimeImport ? imports.filter((name) => !runtimeImportScan.importsRuntimeHelper(code, name)) : imports;
3925
+ if (needed.length === 0) return null;
3926
+ const existingImport = runtimeImportScan.findRuntimeImportClause(code);
3927
+ if (existingImport) {
3928
+ return code.replace(
3929
+ existingImport.statement,
3930
+ `${existingImport.prefixWithBody}, ${needed.join(", ")} } from '@csszyx/runtime'`
3931
+ );
3932
+ }
3933
+ const importStatement = `import { ${needed.join(", ")} } from '@csszyx/runtime';
3934
+ `;
3935
+ return insertRuntimeImport(code, importStatement);
3936
+ }
3937
+ function injectThemeGroups(code, transformedCode, id, usesSzcn) {
3938
+ if (!usesSzcn && !/\bszcn\s*\(/.test(code) || transformedCode.includes(THEME_GROUPS_VIRTUAL_ID) || !shouldProcessSource(id)) {
3939
+ return null;
3940
+ }
3941
+ return `import '${THEME_GROUPS_VIRTUAL_ID}';
3942
+ ${transformedCode}`;
3943
+ }
3944
+ function collectPreTransformClasses(output) {
3945
+ if (!output.transformed && !output.code.includes("class=") && !output.code.includes("className=")) {
3946
+ return null;
3947
+ }
3948
+ if (output.szClasses) {
3949
+ for (const className of output.szClasses) {
3950
+ addSafelistClass(className);
3951
+ state.ownedClasses.add(className);
3952
+ }
3953
+ } else {
3954
+ extractClasses(output.code);
3955
+ }
3956
+ return { code: output.code, map: null };
3957
+ }
3429
3958
  const prePlugin = unplugin$1.createUnplugin(
3430
3959
  (_pluginOptions) => ({
3431
3960
  name: "csszyx:pre",
@@ -3512,176 +4041,41 @@ function createCsszyxPlugins(options = {}) {
3512
4041
  }
3513
4042
  if (shouldProcessSource(id)) {
3514
4043
  trackGlobalVarSourceFile(id, code);
4044
+ recordAuthoredClasses(code);
3515
4045
  }
3516
4046
  if (matchesScriptExtension(id, SCRIPT_ID_EXTENSIONS)) {
3517
4047
  assertNoRSCBoundaryViolation(code, id);
3518
4048
  }
3519
4049
  if (matchesScriptExtension(id, [".css"])) {
3520
- state.sawAnyCss = true;
3521
- if (cssImportsTailwind(code)) {
3522
- state.sawTailwindEntry = true;
3523
- if (cssHasContentScope(code)) {
3524
- state.tailwindEntryScoped = true;
3525
- }
3526
- if (hasInjectableTailwindCandidate(state.classes)) {
3527
- const relPath = computeSafelistRelPath(
3528
- state.rootDir,
3529
- SAFELIST_FILENAME,
3530
- id
3531
- );
3532
- const transformed2 = appendTailwindSourceDirective(code, relPath);
3533
- if (transformed2 !== null) {
3534
- return { code: transformed2, map: null };
3535
- }
3536
- }
3537
- }
3538
- return null;
4050
+ return transformTailwindCssEntry(code, id);
3539
4051
  }
3540
- let transformedCode = code;
3541
- let usesRuntime = false;
3542
- let usesMerge = false;
3543
- let usesSzcn = false;
3544
- let usesSzPart = false;
3545
- let usesColorVar = false;
3546
- let usesSpacingVar = false;
3547
- let usesUnitVar = false;
3548
- let transformed = false;
3549
- let szClasses;
3550
4052
  const hasSzProp = code.includes("sz=") || code.includes("szs=") || /\bsz\s*:\s*["'{]/.test(code) || code.includes('sz: "');
3551
- if (hasSzProp) {
3552
- if (id.endsWith(".vue")) {
3553
- const result = vueAdapter.preprocess(code, options);
3554
- if (result.transformed) {
3555
- transformedCode = result.code;
3556
- transformed = true;
3557
- }
3558
- } else if (id.endsWith(".svelte")) {
3559
- const result = svelteAdapter.preprocess(code, options);
3560
- if (result) {
3561
- transformedCode = result.code;
3562
- transformed = true;
3563
- }
3564
- } else {
3565
- const transformStarted = node_perf_hooks.performance.now();
3566
- const result = transformConfiguredSource(code, id);
3567
- traceBenchTiming(
3568
- "transform-hook",
3569
- id,
3570
- node_perf_hooks.performance.now() - transformStarted
3571
- );
3572
- transformedCode = result.code;
3573
- usesRuntime = result.usesRuntime;
3574
- usesMerge = result.usesMerge;
3575
- usesSzcn = result.usesSzcn;
3576
- usesSzPart = result.usesSzPart;
3577
- usesColorVar = result.usesColorVar;
3578
- usesSpacingVar = result.usesSpacingVar;
3579
- usesUnitVar = result.usesUnitVar;
3580
- transformed = result.transformed;
3581
- szClasses = result.classes;
3582
- recordFileVarMangleEntries(state, id, cssVariableEntries(result));
3583
- recordFileCSSVariableMetrics(state, id, result.code);
3584
- for (const msg of result.diagnostics) {
3585
- if (msg.includes("unresolvable sz spread")) {
3586
- state.spreadWarnings.add(`${id}
3587
- ${msg}`);
3588
- } else if (msg.includes("AST budget exceeded")) {
3589
- console.warn(`[csszyx] ${id}
3590
- ${msg}`);
3591
- }
3592
- }
3593
- if (!quiet && result.diagnostics.length > 0 && process.env.NODE_ENV !== "production") {
3594
- for (const msg of result.diagnostics) {
3595
- if (msg.includes("unresolvable sz spread") || msg.includes("AST budget exceeded")) {
3596
- continue;
3597
- }
3598
- this.warn(`[csszyx] ${id}
3599
- ${msg}`);
3600
- }
3601
- }
3602
- for (const [token, data] of result.recoveryTokens) {
3603
- state.recoveryTokens.set(token, data);
3604
- }
3605
- }
3606
- } else if (shouldProcessSource(id)) {
4053
+ const output = hasSzProp ? transformSzSource(code, id, (message) => this.warn(message)) : unchangedPreTransform(code);
4054
+ if (!hasSzProp && shouldProcessSource(id)) {
3607
4055
  recordFileVarMangleEntries(state, id, []);
3608
4056
  recordFileCSSVariableMetrics(state, id, null);
3609
4057
  }
3610
- if (transformedCode.includes("<html") && /(?:layout|Root|Document|app)\.tsx?$/i.test(id)) {
3611
- const attrName = options.production?.minify ? "data-sz-cs" : "data-sz-checksum";
3612
- const htmlTag = findOpeningTag(transformedCode, "html");
3613
- if (htmlTag) {
3614
- transformedCode = `${transformedCode.slice(0, htmlTag.close)} ${attrName}="${CHECKSUM_PLACEHOLDER}"${transformedCode.slice(htmlTag.close)}`;
3615
- }
3616
- const debugScript = `<script dangerouslySetInnerHTML={{__html: \`(function(){var m=${MANGLE_MAP_PLACEHOLDER};var vm=${VAR_MANGLE_MAP_PLACEHOLDER};var gp=decodeURIComponent(${escapeJsonForInlineScript(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})}}})()\`}} />`;
3617
- const bodyTag = findOpeningTag(transformedCode, "body");
3618
- if (bodyTag) {
3619
- transformedCode = `${transformedCode.slice(0, bodyTag.close + 1)}${debugScript}${transformedCode.slice(bodyTag.close + 1)}`;
3620
- }
3621
- transformed = true;
4058
+ const layoutCode = injectLayoutHydration(output.code, id);
4059
+ if (layoutCode !== null) {
4060
+ output.code = layoutCode;
4061
+ output.transformed = true;
3622
4062
  }
3623
- {
3624
- const imports = [];
3625
- if (usesRuntime) {
3626
- imports.push("_sz");
3627
- }
3628
- if (usesMerge) {
3629
- imports.push("_szMerge");
3630
- }
3631
- if (usesSzcn) {
3632
- imports.push("_szcn");
3633
- }
3634
- if (usesSzPart) {
3635
- imports.push("_szPart");
3636
- }
3637
- if (usesColorVar) {
3638
- imports.push("__szColorVar");
3639
- }
3640
- if (usesSpacingVar) {
3641
- imports.push("__szSpacingVar");
3642
- }
3643
- if (usesUnitVar) {
3644
- imports.push("__szUnitVar");
3645
- }
3646
- const hasRuntimeImport = imports.length > 0 && transformedCode.includes("@csszyx/runtime");
3647
- const needed = hasRuntimeImport ? imports.filter((name) => !runtimeImportScan.importsRuntimeHelper(transformedCode, name)) : imports;
3648
- if (needed.length > 0) {
3649
- const existingImport = runtimeImportScan.findRuntimeImportClause(transformedCode);
3650
- if (existingImport) {
3651
- transformedCode = transformedCode.replace(
3652
- existingImport.statement,
3653
- `${existingImport.prefixWithBody}, ${needed.join(", ")} } from '@csszyx/runtime'`
3654
- );
3655
- } else {
3656
- const importStmt = `import { ${needed.join(", ")} } from '@csszyx/runtime';
3657
- `;
3658
- transformedCode = insertRuntimeImport(transformedCode, importStmt);
3659
- }
3660
- transformed = true;
3661
- }
4063
+ const runtimeCode = injectRuntimeHelpers(output.code, output);
4064
+ if (runtimeCode !== null) {
4065
+ output.code = runtimeCode;
4066
+ output.transformed = true;
3662
4067
  }
3663
- if ((usesSzcn || /\bszcn\s*\(/.test(code)) && !transformedCode.includes(THEME_GROUPS_VIRTUAL_ID) && shouldProcessSource(id)) {
3664
- transformedCode = `import '${THEME_GROUPS_VIRTUAL_ID}';
3665
- ${transformedCode}`;
3666
- transformed = true;
4068
+ const themedCode = injectThemeGroups(code, output.code, id, output.usesSzcn);
4069
+ if (themedCode !== null) {
4070
+ output.code = themedCode;
4071
+ output.transformed = true;
3667
4072
  }
3668
4073
  if (matchesScriptExtension(id, SCRIPT_ID_EXTENSIONS)) {
3669
- assertNoRSCBoundaryViolation(transformedCode, id);
3670
- const record = createRSCModuleRecord(transformedCode, id);
4074
+ assertNoRSCBoundaryViolation(output.code, id);
4075
+ const record = createRSCModuleRecord(output.code, id);
3671
4076
  state.rscModules.set(record.id, record);
3672
4077
  }
3673
- if (transformed || transformedCode.includes("class=") || transformedCode.includes("className=")) {
3674
- if (szClasses !== void 0) {
3675
- for (const cls of szClasses) {
3676
- addSafelistClass(cls);
3677
- state.ownedClasses.add(cls);
3678
- }
3679
- } else {
3680
- extractClasses(transformedCode);
3681
- }
3682
- return { code: transformedCode, map: null };
3683
- }
3684
- return null;
4078
+ return collectPreTransformClasses(output);
3685
4079
  },
3686
4080
  /** Finalizes the mangle map after all source modules have been processed. */
3687
4081
  buildEnd() {
@@ -3890,6 +4284,166 @@ ${transformedCode}`;
3890
4284
  }
3891
4285
  })
3892
4286
  );
4287
+ function createBundleManifest(includeMangleMap) {
4288
+ const manifest = {
4289
+ version: "0.4.0",
4290
+ buildId: state.checksum,
4291
+ classes: Object.keys(state.mangleMap)
4292
+ };
4293
+ if (includeMangleMap && Object.keys(state.mangleMap).length > 0) {
4294
+ manifest.mangleMap = state.mangleMap;
4295
+ }
4296
+ if (Object.keys(state.varMangleMap).length > 0) {
4297
+ manifest.varMangleMap = state.varMangleMap;
4298
+ }
4299
+ const globalVarAliases = extractGlobalVarAliasesForManifest(
4300
+ state.varMangleMap,
4301
+ globalVarAliasPrefix,
4302
+ state.globalVarValidationResult
4303
+ );
4304
+ if (Object.keys(globalVarAliases).length > 0) {
4305
+ manifest.globalVarAliases = globalVarAliases;
4306
+ }
4307
+ if (hasCSSVariableMetrics(state.cssVarMetrics)) {
4308
+ manifest.cssVarMetrics = state.cssVarMetrics;
4309
+ }
4310
+ return manifest;
4311
+ }
4312
+ function isCssSyntaxError(error) {
4313
+ return !!error && typeof error === "object" && "name" in error && error.name === "CssSyntaxError";
4314
+ }
4315
+ function rewriteOutputCss(source, file, shouldMangle, mangledSources, externalClasses) {
4316
+ let css = rewriteCssWithValidatedGlobalVarPlan(
4317
+ source,
4318
+ file,
4319
+ state.globalVarValidationResult
4320
+ );
4321
+ if (!shouldMangle) return css;
4322
+ try {
4323
+ const result = cssMangler.mangleCSSSync(css, state.mangleMap, {
4324
+ debug: options.development?.debug,
4325
+ from: file
4326
+ });
4327
+ for (const className of result.mangledClasses) mangledSources.add(className);
4328
+ for (const className of result.unmangledClasses) externalClasses.add(className);
4329
+ if (result.transformedCount > 0) css = result.css;
4330
+ return css;
4331
+ } catch (error) {
4332
+ if (isCssSyntaxError(error)) return css;
4333
+ throw error;
4334
+ }
4335
+ }
4336
+ function reportOutputMangleHazards(shouldMangle, mangledSources, externalClasses) {
4337
+ if (!shouldMangle) return;
4338
+ const message = mangleHybridHazardMessage(
4339
+ collectMangleHybridHazards(state.mangleMap, mangledSources, externalClasses)
4340
+ );
4341
+ if (message) console.warn(message);
4342
+ }
4343
+ function rewriteWebpackCodeAsset(file, source, shouldMangle, compilation, compiler) {
4344
+ if (shouldMangle && file.endsWith(".html")) {
4345
+ const mangledHtml = mangleHtmlClasses(source);
4346
+ if (mangledHtml !== source) {
4347
+ compilation.updateAsset(file, new compiler.webpack.sources.RawSource(mangledHtml));
4348
+ }
4349
+ return;
4350
+ }
4351
+ if (!file.endsWith(".js")) return;
4352
+ const rewritten = shouldMangle ? replacePlaceholders(mangleCodeClasses(source)) : replacePlaceholders(source);
4353
+ if (rewritten !== source) {
4354
+ compilation.updateAsset(file, new compiler.webpack.sources.RawSource(rewritten));
4355
+ }
4356
+ }
4357
+ function processWebpackAssets(assets, compilation, compiler) {
4358
+ finalizeMangleMap();
4359
+ state.globalVarValidationResult = validateGlobalVarBundleInputs(
4360
+ collectWebpackGlobalVarCssAssets(assets)
4361
+ );
4362
+ const shouldMangle = manglingEnabled && compiler.options.mode !== "development" && Object.keys(state.mangleMap).length > 0;
4363
+ const manifest = createBundleManifest(shouldMangle);
4364
+ compilation.emitAsset(
4365
+ "csszyx-manifest.json",
4366
+ new compiler.webpack.sources.RawSource(JSON.stringify(manifest))
4367
+ );
4368
+ if (shouldEmitGlobalVarMapAsset(globalVarMangleConfig)) {
4369
+ const globalVarMap = createGlobalVarMapAssetSource(
4370
+ state.varMangleMap,
4371
+ globalVarAliasPrefix,
4372
+ state.globalVarValidationResult
4373
+ );
4374
+ if (globalVarMap) {
4375
+ compilation.emitAsset(
4376
+ ".csszyx/global-var-map.json",
4377
+ new compiler.webpack.sources.RawSource(globalVarMap)
4378
+ );
4379
+ }
4380
+ }
4381
+ const mangledSources = /* @__PURE__ */ new Set();
4382
+ const externalClasses = /* @__PURE__ */ new Set();
4383
+ for (const file in assets) {
4384
+ const source = assets[file].source().toString();
4385
+ if (file.endsWith(".css")) {
4386
+ const css = rewriteOutputCss(
4387
+ source,
4388
+ file,
4389
+ shouldMangle,
4390
+ mangledSources,
4391
+ externalClasses
4392
+ );
4393
+ if (css !== source) {
4394
+ compilation.updateAsset(file, new compiler.webpack.sources.RawSource(css));
4395
+ }
4396
+ } else {
4397
+ rewriteWebpackCodeAsset(file, source, shouldMangle, compilation, compiler);
4398
+ }
4399
+ }
4400
+ reportOutputMangleHazards(shouldMangle, mangledSources, externalClasses);
4401
+ }
4402
+ function rewriteViteBundleEntry(chunk, file, shouldMangle, mangledSources, externalClasses) {
4403
+ if (chunk.type === "asset" && chunk.fileName.endsWith(".css") && chunk.source !== void 0) {
4404
+ const originalCss = chunk.source.toString();
4405
+ const css = rewriteOutputCss(
4406
+ originalCss,
4407
+ file,
4408
+ shouldMangle,
4409
+ mangledSources,
4410
+ externalClasses
4411
+ );
4412
+ if (css !== originalCss) chunk.source = css;
4413
+ return;
4414
+ }
4415
+ if (chunk.type !== "chunk" || chunk.code === void 0) return;
4416
+ const rewritten = shouldMangle ? replacePlaceholders(mangleCodeClasses(chunk.code)) : replacePlaceholders(chunk.code);
4417
+ if (rewritten !== chunk.code) chunk.code = rewritten;
4418
+ }
4419
+ function processRollupBundle(bundle, emitAsset) {
4420
+ finalizeMangleMap();
4421
+ state.globalVarValidationResult = validateGlobalVarBundleInputs(
4422
+ collectRollupGlobalVarCssAssets(bundle)
4423
+ );
4424
+ const shouldMangle = manglingEnabled && Object.keys(state.mangleMap).length > 0;
4425
+ emitAsset("csszyx-manifest.json", JSON.stringify(createBundleManifest(shouldMangle)));
4426
+ if (shouldEmitGlobalVarMapAsset(globalVarMangleConfig)) {
4427
+ const globalVarMap = createGlobalVarMapAssetSource(
4428
+ state.varMangleMap,
4429
+ globalVarAliasPrefix,
4430
+ state.globalVarValidationResult
4431
+ );
4432
+ if (globalVarMap) emitAsset(".csszyx/global-var-map.json", globalVarMap);
4433
+ }
4434
+ const mangledSources = /* @__PURE__ */ new Set();
4435
+ const externalClasses = /* @__PURE__ */ new Set();
4436
+ for (const file in bundle) {
4437
+ rewriteViteBundleEntry(
4438
+ bundle[file],
4439
+ file,
4440
+ shouldMangle,
4441
+ mangledSources,
4442
+ externalClasses
4443
+ );
4444
+ }
4445
+ reportOutputMangleHazards(shouldMangle, mangledSources, externalClasses);
4446
+ }
3893
4447
  const postPlugin = unplugin$1.createUnplugin(() => ({
3894
4448
  name: "csszyx:post",
3895
4449
  enforce: "post",
@@ -3900,266 +4454,20 @@ ${transformedCode}`;
3900
4454
  * @param compiler - the Webpack compiler instance
3901
4455
  */
3902
4456
  webpack(compiler) {
3903
- compiler.hooks.compilation.tap("csszyx:post", (compilation) => {
3904
- const stage = compiler.webpack?.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE || compilation.constructor.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE;
3905
- compilation.hooks.processAssets.tap(
3906
- {
3907
- name: "csszyx:post",
3908
- stage: stage || 400
3909
- // Fallback integer
3910
- },
3911
- (assets) => {
3912
- finalizeMangleMap();
3913
- state.globalVarValidationResult = validateGlobalVarBundleInputs(
3914
- collectWebpackGlobalVarCssAssets(assets)
3915
- );
3916
- const isWebpackDevMode = compiler.options.mode === "development";
3917
- const manifestData = {
3918
- version: "0.4.0",
3919
- buildId: state.checksum,
3920
- classes: Object.keys(state.mangleMap)
3921
- };
3922
- if (manglingEnabled && !isWebpackDevMode && Object.keys(state.mangleMap).length > 0) {
3923
- manifestData.mangleMap = state.mangleMap;
3924
- }
3925
- if (Object.keys(state.varMangleMap).length > 0) {
3926
- manifestData.varMangleMap = state.varMangleMap;
3927
- }
3928
- const globalVarAliases = extractGlobalVarAliasesForManifest(
3929
- state.varMangleMap,
3930
- globalVarAliasPrefix,
3931
- state.globalVarValidationResult
3932
- );
3933
- if (Object.keys(globalVarAliases).length > 0) {
3934
- manifestData.globalVarAliases = globalVarAliases;
3935
- }
3936
- if (hasCSSVariableMetrics(state.cssVarMetrics)) {
3937
- manifestData.cssVarMetrics = state.cssVarMetrics;
3938
- }
3939
- compilation.emitAsset(
3940
- "csszyx-manifest.json",
3941
- new compiler.webpack.sources.RawSource(JSON.stringify(manifestData))
3942
- );
3943
- if (shouldEmitGlobalVarMapAsset(globalVarMangleConfig)) {
3944
- const globalVarMapAsset = createGlobalVarMapAssetSource(
3945
- state.varMangleMap,
3946
- globalVarAliasPrefix,
3947
- state.globalVarValidationResult
3948
- );
3949
- if (globalVarMapAsset) {
3950
- compilation.emitAsset(
3951
- ".csszyx/global-var-map.json",
3952
- new compiler.webpack.sources.RawSource(globalVarMapAsset)
3953
- );
3954
- }
3955
- }
3956
- const mangledSources = /* @__PURE__ */ new Set();
3957
- const externalClasses = /* @__PURE__ */ new Set();
3958
- for (const file in assets) {
3959
- const asset = assets[file];
3960
- const source = asset.source().toString();
3961
- if (file.endsWith(".css")) {
3962
- let css = rewriteCssWithValidatedGlobalVarPlan(
3963
- source,
3964
- file,
3965
- state.globalVarValidationResult
3966
- );
3967
- if (manglingEnabled && !isWebpackDevMode && Object.keys(state.mangleMap).length > 0) {
3968
- try {
3969
- const result = cssMangler.mangleCSSSync(css, state.mangleMap, {
3970
- debug: options.development?.debug,
3971
- from: file
3972
- });
3973
- for (const c of result.mangledClasses) {
3974
- mangledSources.add(c);
3975
- }
3976
- for (const c of result.unmangledClasses) {
3977
- externalClasses.add(c);
3978
- }
3979
- if (result.transformedCount > 0) {
3980
- css = result.css;
3981
- }
3982
- } catch (e) {
3983
- if (e && typeof e === "object" && "name" in e && e.name === "CssSyntaxError") ; else {
3984
- throw e;
3985
- }
3986
- }
3987
- }
3988
- if (css !== source) {
3989
- compilation.updateAsset(
3990
- file,
3991
- new compiler.webpack.sources.RawSource(css)
3992
- );
3993
- }
3994
- continue;
3995
- }
3996
- if (manglingEnabled && !isWebpackDevMode && Object.keys(state.mangleMap).length > 0) {
3997
- if (file.endsWith(".html")) {
3998
- const mangledHtml = mangleHtmlClasses(source);
3999
- if (mangledHtml !== source) {
4000
- compilation.updateAsset(
4001
- file,
4002
- new compiler.webpack.sources.RawSource(mangledHtml)
4003
- );
4004
- continue;
4005
- }
4006
- } else if (file.endsWith(".js")) {
4007
- let mangled = mangleCodeClasses(source);
4008
- mangled = replacePlaceholders(mangled);
4009
- if (mangled !== source) {
4010
- compilation.updateAsset(
4011
- file,
4012
- new compiler.webpack.sources.RawSource(mangled)
4013
- );
4014
- continue;
4015
- }
4016
- }
4017
- }
4018
- if (file.endsWith(".js") && (source.includes(CHECKSUM_PLACEHOLDER) || source.includes(MANGLE_MAP_PLACEHOLDER))) {
4019
- const replaced = replacePlaceholders(source);
4020
- if (replaced !== source) {
4021
- compilation.updateAsset(
4022
- file,
4023
- new compiler.webpack.sources.RawSource(replaced)
4024
- );
4025
- }
4026
- }
4027
- }
4028
- if (manglingEnabled && !isWebpackDevMode && Object.keys(state.mangleMap).length > 0) {
4029
- const hazardMessage = mangleHybridHazardMessage(
4030
- collectMangleHybridHazards(
4031
- state.mangleMap,
4032
- mangledSources,
4033
- externalClasses
4034
- )
4035
- );
4036
- if (hazardMessage) {
4037
- console.warn(hazardMessage);
4038
- }
4039
- }
4040
- }
4041
- );
4042
- });
4457
+ registerWebpackAssetProcessor(compiler, processWebpackAssets);
4043
4458
  },
4044
- vite: {
4045
- /**
4046
- * Vite hook: mangles CSS selectors and JS class strings in the final bundle.
4047
- * @param _options - the output options (unused)
4048
- * @param bundle - the output bundle containing chunks and assets to process
4049
- */
4050
- generateBundle(_options, bundle) {
4051
- finalizeMangleMap();
4052
- state.globalVarValidationResult = validateGlobalVarBundleInputs(
4053
- collectRollupGlobalVarCssAssets(bundle)
4054
- );
4055
- const manifestData = {
4056
- version: "0.4.0",
4057
- buildId: state.checksum,
4058
- classes: Object.keys(state.mangleMap)
4059
- };
4060
- if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
4061
- manifestData.mangleMap = state.mangleMap;
4062
- }
4063
- if (Object.keys(state.varMangleMap).length > 0) {
4064
- manifestData.varMangleMap = state.varMangleMap;
4065
- }
4066
- const globalVarAliases = extractGlobalVarAliasesForManifest(
4067
- state.varMangleMap,
4068
- globalVarAliasPrefix,
4069
- state.globalVarValidationResult
4070
- );
4071
- if (Object.keys(globalVarAliases).length > 0) {
4072
- manifestData.globalVarAliases = globalVarAliases;
4073
- }
4074
- if (hasCSSVariableMetrics(state.cssVarMetrics)) {
4075
- manifestData.cssVarMetrics = state.cssVarMetrics;
4076
- }
4077
- this.emitFile({
4078
- type: "asset",
4079
- fileName: "csszyx-manifest.json",
4080
- source: JSON.stringify(manifestData)
4081
- });
4082
- if (shouldEmitGlobalVarMapAsset(globalVarMangleConfig)) {
4083
- const globalVarMapAsset = createGlobalVarMapAssetSource(
4084
- state.varMangleMap,
4085
- globalVarAliasPrefix,
4086
- state.globalVarValidationResult
4087
- );
4088
- if (globalVarMapAsset) {
4089
- this.emitFile({
4090
- type: "asset",
4091
- fileName: ".csszyx/global-var-map.json",
4092
- source: globalVarMapAsset
4093
- });
4094
- }
4095
- }
4096
- const mangledSources = /* @__PURE__ */ new Set();
4097
- const externalClasses = /* @__PURE__ */ new Set();
4098
- for (const file in bundle) {
4099
- const chunk = bundle[file];
4100
- if (chunk.type === "asset" && chunk.fileName.endsWith(".css")) {
4101
- const originalCss = chunk.source.toString();
4102
- let css = rewriteCssWithValidatedGlobalVarPlan(
4103
- originalCss,
4104
- file,
4105
- state.globalVarValidationResult
4106
- );
4107
- if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
4108
- try {
4109
- const result = cssMangler.mangleCSSSync(css, state.mangleMap, {
4110
- debug: options.development?.debug,
4111
- from: file
4112
- });
4113
- for (const c of result.mangledClasses) {
4114
- mangledSources.add(c);
4115
- }
4116
- for (const c of result.unmangledClasses) {
4117
- externalClasses.add(c);
4118
- }
4119
- if (result.transformedCount > 0) {
4120
- css = result.css;
4121
- }
4122
- } catch (e) {
4123
- if (e && typeof e === "object" && "name" in e && e.name === "CssSyntaxError") ; else {
4124
- throw e;
4125
- }
4126
- }
4127
- }
4128
- if (css !== originalCss) {
4129
- chunk.source = css;
4130
- }
4131
- continue;
4132
- }
4133
- if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
4134
- if (chunk.type === "chunk") {
4135
- let mangledCode = mangleCodeClasses(chunk.code);
4136
- mangledCode = replacePlaceholders(mangledCode);
4137
- if (mangledCode !== chunk.code) {
4138
- chunk.code = mangledCode;
4139
- }
4140
- continue;
4141
- }
4142
- }
4143
- if (chunk.type === "chunk" && (chunk.code.includes(CHECKSUM_PLACEHOLDER) || chunk.code.includes(MANGLE_MAP_PLACEHOLDER))) {
4144
- const replaced = replacePlaceholders(chunk.code);
4145
- if (replaced !== chunk.code) {
4146
- chunk.code = replaced;
4147
- }
4148
- }
4149
- }
4150
- if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
4151
- const hazardMessage = mangleHybridHazardMessage(
4152
- collectMangleHybridHazards(
4153
- state.mangleMap,
4154
- mangledSources,
4155
- externalClasses
4156
- )
4157
- );
4158
- if (hazardMessage) {
4159
- console.warn(hazardMessage);
4160
- }
4161
- }
4162
- }
4459
+ /**
4460
+ * Rollup-compatible hook used by both pure Rollup and Vite adapters.
4461
+ *
4462
+ * @param this Rollup-compatible output hook context.
4463
+ * @param this.emitFile Output asset emitter.
4464
+ * @param _options Output options (unused).
4465
+ * @param bundle Complete Rollup output bundle.
4466
+ */
4467
+ generateBundle(_options, bundle) {
4468
+ processRollupBundle(bundle, (fileName, source) => {
4469
+ this.emitFile({ type: "asset", fileName, source });
4470
+ });
4163
4471
  }
4164
4472
  }));
4165
4473
  return { prePlugin, postPlugin };
@@ -4191,7 +4499,16 @@ const rollupPlugin = (options = {}) => {
4191
4499
  ];
4192
4500
  };
4193
4501
  const esbuildPlugin = (options = {}) => {
4194
- const { prePlugin, postPlugin } = createCsszyxPlugins(options);
4502
+ if (options.production?.mangle === true) {
4503
+ console.warn(
4504
+ "[csszyx] production.mangle is not supported by the esbuild adapter; class mangling is disabled so emitted JS and CSS keep matching names."
4505
+ );
4506
+ }
4507
+ const safeOptions = {
4508
+ ...options,
4509
+ production: { ...options.production, mangle: false }
4510
+ };
4511
+ const { prePlugin, postPlugin } = createCsszyxPlugins(safeOptions);
4195
4512
  return {
4196
4513
  name: "csszyx",
4197
4514
  /**
@@ -4200,8 +4517,8 @@ const esbuildPlugin = (options = {}) => {
4200
4517
  */
4201
4518
  setup(build) {
4202
4519
  const b = build;
4203
- prePlugin.esbuild(options).setup(b);
4204
- postPlugin.esbuild(options).setup(b);
4520
+ prePlugin.esbuild(safeOptions).setup(b);
4521
+ postPlugin.esbuild(safeOptions).setup(b);
4205
4522
  }
4206
4523
  };
4207
4524
  };
@@ -4235,6 +4552,7 @@ exports.isPackagesSkippedSource = isPackagesSkippedSource;
4235
4552
  exports.isRSCServerModule = isRSCServerModule;
4236
4553
  exports.isTailwindReservedGlobalVar = isTailwindReservedGlobalVar;
4237
4554
  exports.mangleCodeClassesSync = mangleCodeClassesSync;
4555
+ exports.mangleEligibleClasses = mangleEligibleClasses;
4238
4556
  exports.mangleHybridHazardMessage = mangleHybridHazardMessage;
4239
4557
  exports.mergeThemes = mergeThemes;
4240
4558
  exports.missingTailwindEntryMessage = missingTailwindEntryMessage;