@csszyx/unplugin 0.11.5 → 0.11.7

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.
@@ -16,7 +16,7 @@ const unplugin$1 = require('unplugin');
16
16
  const cssMangler = require('../css-mangler.cjs');
17
17
  const htmlEscape = require('./unplugin.BCwRIUs_.cjs');
18
18
  const runtimeImportScan = require('./unplugin.Rov-j_Wm.cjs');
19
- const transformCache = require('./unplugin.D0N9bprz.cjs');
19
+ const transformCache = require('./unplugin.D8vSG36M.cjs');
20
20
  const postcss = require('postcss');
21
21
  const valueParser = require('postcss-value-parser');
22
22
 
@@ -635,6 +635,27 @@ function walkRSCGraph(current, records, chain, seen) {
635
635
  }
636
636
  return null;
637
637
  }
638
+ function findDirectiveQuoteEnd(code, start, quote) {
639
+ let escaped = false;
640
+ for (let index = start + 1; index < code.length; index++) {
641
+ const char = code[index];
642
+ if (escaped) {
643
+ escaped = false;
644
+ } else if (char === "\\") {
645
+ escaped = true;
646
+ } else if (char === quote) {
647
+ return index;
648
+ }
649
+ }
650
+ return -1;
651
+ }
652
+ function findDirectiveStatementEnd(code, start) {
653
+ let end = start;
654
+ while (end < code.length && /[ \t\r\n]/.test(code[end])) {
655
+ end++;
656
+ }
657
+ return code[end] === ";" ? end + 1 : end;
658
+ }
638
659
  function readDirectivePrologue(code) {
639
660
  const out = [];
640
661
  let i = code.charCodeAt(0) === 65279 ? 1 : 0;
@@ -644,29 +665,11 @@ function readDirectivePrologue(code) {
644
665
  if (quote !== '"' && quote !== "'") {
645
666
  break;
646
667
  }
647
- let j = i + 1;
648
- let escaped = false;
649
- while (j < code.length) {
650
- const ch = code[j];
651
- if (escaped) {
652
- escaped = false;
653
- } else if (ch === "\\") {
654
- escaped = true;
655
- } else if (ch === quote) {
656
- break;
657
- }
658
- j++;
659
- }
660
- if (j >= code.length) {
668
+ const quoteEnd = findDirectiveQuoteEnd(code, i, quote);
669
+ if (quoteEnd === -1) {
661
670
  break;
662
671
  }
663
- let end = j + 1;
664
- while (end < code.length && /[ \t\r\n]/.test(code[end])) {
665
- end++;
666
- }
667
- if (code[end] === ";") {
668
- end++;
669
- }
672
+ const end = findDirectiveStatementEnd(code, quoteEnd + 1);
670
673
  out.push(code.slice(i, end).trim());
671
674
  i = end;
672
675
  }
@@ -1002,33 +1005,34 @@ function pruneRSCModulePathCaches(moduleIds) {
1002
1005
  }
1003
1006
  }
1004
1007
  }
1005
- function readImportedSymbols(clause) {
1006
- const symbols = [];
1008
+ function readNamedImportedSymbols(clause) {
1007
1009
  const openBrace = clause.indexOf("{");
1008
1010
  const closeBrace = openBrace === -1 ? -1 : clause.indexOf("}", openBrace);
1009
- if (openBrace !== -1 && closeBrace !== -1) {
1010
- const namedPart = clause.slice(openBrace + 1, closeBrace);
1011
- for (const part of namedPart.split(",")) {
1012
- const trimmed = part.trim();
1013
- if (!trimmed || trimmed.startsWith("type ")) {
1014
- continue;
1015
- }
1016
- const sourceName = trimmed.replace(/^type[ \t]+/, "").split(/(?<![ \t])[ \t]+as[ \t]+/)[0]?.trim();
1017
- if (sourceName) {
1018
- symbols.push(sourceName);
1019
- }
1020
- }
1021
- }
1022
- const namespaceParts = splitAsciiWhitespace(clause);
1023
- if (namespaceParts.length >= 3 && namespaceParts[0] === "*" && namespaceParts[1] === "as" && isIdentifier(namespaceParts[2] ?? "")) {
1024
- symbols.push(...FORBIDDEN_SYMBOLS);
1011
+ if (openBrace === -1 || closeBrace === -1) {
1012
+ return [];
1025
1013
  }
1014
+ return clause.slice(openBrace + 1, closeBrace).split(",").map((part) => part.trim()).filter((part) => part !== "" && !part.startsWith("type ")).map(
1015
+ (part) => part.replace(/^type[ \t]+/, "").split(/(?<![ \t])[ \t]+as[ \t]+/)[0]?.trim()
1016
+ ).filter((symbol) => Boolean(symbol));
1017
+ }
1018
+ function readForbiddenDefaultSymbol(clause) {
1026
1019
  const braceStart = clause.indexOf("{");
1027
1020
  const braceEnd = clause.indexOf("}", braceStart);
1028
1021
  const stripped = braceStart !== -1 && braceEnd !== -1 ? clause.slice(0, braceStart) + clause.slice(braceEnd + 1) : clause;
1029
- const defaultCandidate = stripped.trimStart().split(",", 1)[0]?.trim() ?? "";
1030
- const defaultSymbol = isIdentifier(defaultCandidate) ? defaultCandidate : void 0;
1031
- if (defaultSymbol && FORBIDDEN_SYMBOLS.has(defaultSymbol)) {
1022
+ const candidate = stripped.trimStart().split(",", 1)[0]?.trim() ?? "";
1023
+ return isIdentifier(candidate) && FORBIDDEN_SYMBOLS.has(candidate) ? candidate : null;
1024
+ }
1025
+ function hasNamespaceImport(clause) {
1026
+ const parts = splitAsciiWhitespace(clause);
1027
+ return parts.length >= 3 && parts[0] === "*" && parts[1] === "as" && isIdentifier(parts[2] ?? "");
1028
+ }
1029
+ function readImportedSymbols(clause) {
1030
+ const symbols = readNamedImportedSymbols(clause);
1031
+ if (hasNamespaceImport(clause)) {
1032
+ symbols.push(...FORBIDDEN_SYMBOLS);
1033
+ }
1034
+ const defaultSymbol = readForbiddenDefaultSymbol(clause);
1035
+ if (defaultSymbol) {
1032
1036
  symbols.push(defaultSymbol);
1033
1037
  }
1034
1038
  return symbols;
@@ -1132,6 +1136,20 @@ const EMPTY_THEME = {
1132
1136
  shadows: [],
1133
1137
  breakpoints: []
1134
1138
  };
1139
+ function findMatchingBrace(source, openBrace) {
1140
+ let depth = 0;
1141
+ for (let index = openBrace; index < source.length; index++) {
1142
+ if (source[index] === "{") {
1143
+ depth++;
1144
+ } else if (source[index] === "}") {
1145
+ depth--;
1146
+ if (depth === 0) {
1147
+ return index;
1148
+ }
1149
+ }
1150
+ }
1151
+ return -1;
1152
+ }
1135
1153
  function stripLayerWrappers(css) {
1136
1154
  let result = "";
1137
1155
  let i = 0;
@@ -1147,26 +1165,13 @@ function stripLayerWrappers(css) {
1147
1165
  result += css.slice(layerIdx);
1148
1166
  break;
1149
1167
  }
1150
- let depth = 0;
1151
- let j = openBrace;
1152
- while (j < css.length) {
1153
- if (css[j] === "{") {
1154
- depth++;
1155
- }
1156
- if (css[j] === "}") {
1157
- depth--;
1158
- if (depth === 0) {
1159
- result += css.slice(openBrace + 1, j);
1160
- i = j + 1;
1161
- break;
1162
- }
1163
- }
1164
- j++;
1165
- }
1166
- if (depth !== 0) {
1168
+ const closeBrace = findMatchingBrace(css, openBrace);
1169
+ if (closeBrace === -1) {
1167
1170
  result += css.slice(openBrace);
1168
1171
  break;
1169
1172
  }
1173
+ result += css.slice(openBrace + 1, closeBrace);
1174
+ i = closeBrace + 1;
1170
1175
  }
1171
1176
  return result;
1172
1177
  }
@@ -1175,20 +1180,9 @@ function extractThemeBlocks(css) {
1175
1180
  const themeStart = /@theme\s+(?:inline\s+)?\{|@theme\{/g;
1176
1181
  for (const match of css.matchAll(themeStart)) {
1177
1182
  const openPos = css.indexOf("{", match.index);
1178
- let depth = 0;
1179
- let j = openPos;
1180
- while (j < css.length) {
1181
- if (css[j] === "{") {
1182
- depth++;
1183
- }
1184
- if (css[j] === "}") {
1185
- depth--;
1186
- if (depth === 0) {
1187
- blocks.push(css.slice(openPos + 1, j));
1188
- break;
1189
- }
1190
- }
1191
- j++;
1183
+ const closePos = findMatchingBrace(css, openPos);
1184
+ if (closePos !== -1) {
1185
+ blocks.push(css.slice(openPos + 1, closePos));
1192
1186
  }
1193
1187
  }
1194
1188
  return blocks;
@@ -1287,6 +1281,29 @@ function mergeThemes(themes) {
1287
1281
  function hasTokens(theme) {
1288
1282
  return Object.values(theme).some((arr) => arr.length > 0);
1289
1283
  }
1284
+ function readCustomPropertyName(block, dashes) {
1285
+ let end = dashes + 2;
1286
+ if (end >= block.length || !/[a-z]/.test(block[end])) {
1287
+ return null;
1288
+ }
1289
+ end++;
1290
+ while (end < block.length && /[a-z0-9-]/.test(block[end])) {
1291
+ end++;
1292
+ }
1293
+ return { name: block.slice(dashes + 2, end), end };
1294
+ }
1295
+ function findCustomPropertyDeclarationEnd(block, nameEnd) {
1296
+ let cursor = nameEnd;
1297
+ while (cursor < block.length && /\s/.test(block[cursor])) {
1298
+ cursor++;
1299
+ }
1300
+ if (block[cursor] === ":") {
1301
+ const valueStart = cursor + 1;
1302
+ const semicolon = block.indexOf(";", valueStart);
1303
+ return semicolon > valueStart ? semicolon + 1 : -1;
1304
+ }
1305
+ return block[nameEnd] === ";" ? nameEnd + 1 : -1;
1306
+ }
1290
1307
  function scanCustomPropertyNames(block) {
1291
1308
  const names = [];
1292
1309
  let i = 0;
@@ -1295,36 +1312,17 @@ function scanCustomPropertyNames(block) {
1295
1312
  if (dashes === -1) {
1296
1313
  break;
1297
1314
  }
1298
- let end = dashes + 2;
1299
- if (end >= block.length || !/[a-z]/.test(block[end])) {
1315
+ const property = readCustomPropertyName(block, dashes);
1316
+ if (!property) {
1300
1317
  i = dashes + 1;
1301
1318
  continue;
1302
1319
  }
1303
- end++;
1304
- while (end < block.length && /[a-z0-9-]/.test(block[end])) {
1305
- end++;
1306
- }
1307
- const name = block.slice(dashes + 2, end);
1308
- let matchEnd = -1;
1309
- let cursor = end;
1310
- while (cursor < block.length && /\s/.test(block[cursor])) {
1311
- cursor++;
1312
- }
1313
- if (block[cursor] === ":") {
1314
- const valueStart = cursor + 1;
1315
- const semi = block.indexOf(";", valueStart);
1316
- if (semi > valueStart) {
1317
- matchEnd = semi + 1;
1318
- }
1319
- }
1320
- if (matchEnd === -1 && block[end] === ";") {
1321
- matchEnd = end + 1;
1322
- }
1320
+ const matchEnd = findCustomPropertyDeclarationEnd(block, property.end);
1323
1321
  if (matchEnd === -1) {
1324
1322
  i = dashes + 1;
1325
1323
  continue;
1326
1324
  }
1327
- names.push(name);
1325
+ names.push(property.name);
1328
1326
  i = matchEnd;
1329
1327
  }
1330
1328
  return names;
@@ -1392,27 +1390,36 @@ function matchesAnyPattern(id, patterns, rootDir) {
1392
1390
  const list = Array.isArray(patterns) ? patterns : [patterns];
1393
1391
  return list.some((pattern) => matchesPattern(id, pattern, rootDir));
1394
1392
  }
1393
+ function collectDirectoryFiles(dir, files) {
1394
+ let entries;
1395
+ try {
1396
+ entries = fs__namespace.readdirSync(dir, { withFileTypes: true });
1397
+ } catch {
1398
+ return;
1399
+ }
1400
+ for (const entry of entries) {
1401
+ const full = path__namespace.join(dir, entry.name);
1402
+ if (entry.isDirectory()) {
1403
+ if (!DEFAULT_IGNORED_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
1404
+ collectDirectoryFiles(full, files);
1405
+ }
1406
+ } else {
1407
+ files.add(path__namespace.resolve(full));
1408
+ }
1409
+ }
1410
+ }
1411
+ function isExpandedPatternMatch(file, patterns, rootDir) {
1412
+ return patterns.some((pattern) => {
1413
+ if (hasGlobMagic(pattern)) {
1414
+ return matchesPattern(file, pattern, rootDir);
1415
+ }
1416
+ const resolved = path__namespace.isAbsolute(pattern) ? pattern : path__namespace.join(rootDir, pattern);
1417
+ return normalizeFileId(path__namespace.resolve(resolved)) === normalizeFileId(file);
1418
+ });
1419
+ }
1395
1420
  function expandFilePatterns(rootDir, patterns) {
1396
1421
  const list = Array.isArray(patterns) ? patterns : [patterns];
1397
1422
  const files = /* @__PURE__ */ new Set();
1398
- const walk = (dir) => {
1399
- let entries;
1400
- try {
1401
- entries = fs__namespace.readdirSync(dir, { withFileTypes: true });
1402
- } catch {
1403
- return;
1404
- }
1405
- for (const entry of entries) {
1406
- const full = path__namespace.join(dir, entry.name);
1407
- if (entry.isDirectory()) {
1408
- if (!DEFAULT_IGNORED_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
1409
- walk(full);
1410
- }
1411
- } else {
1412
- files.add(path__namespace.resolve(full));
1413
- }
1414
- }
1415
- };
1416
1423
  let needsWalk = false;
1417
1424
  for (const pattern of list) {
1418
1425
  const resolved = path__namespace.isAbsolute(pattern) ? pattern : path__namespace.join(rootDir, pattern);
@@ -1425,21 +1432,10 @@ function expandFilePatterns(rootDir, patterns) {
1425
1432
  }
1426
1433
  }
1427
1434
  if (needsWalk) {
1428
- walk(rootDir);
1435
+ collectDirectoryFiles(rootDir, files);
1429
1436
  for (const file of Array.from(files)) {
1430
- if (!list.some(
1431
- (pattern) => hasGlobMagic(pattern) && matchesPattern(file, pattern, rootDir)
1432
- )) {
1433
- const isLiteralMatch = list.some((pattern) => {
1434
- if (hasGlobMagic(pattern)) {
1435
- return false;
1436
- }
1437
- const resolved = path__namespace.isAbsolute(pattern) ? pattern : path__namespace.join(rootDir, pattern);
1438
- return normalizeFileId(path__namespace.resolve(resolved)) === normalizeFileId(file);
1439
- });
1440
- if (!isLiteralMatch) {
1441
- files.delete(file);
1442
- }
1437
+ if (!isExpandedPatternMatch(file, list, rootDir)) {
1438
+ files.delete(file);
1443
1439
  }
1444
1440
  }
1445
1441
  }
@@ -1814,9 +1810,9 @@ let _hasWarnedTransformCacheVersion = false;
1814
1810
  let _hasWarnedNativeFallback = false;
1815
1811
  const _loggedActiveParsers = /* @__PURE__ */ new Set();
1816
1812
  const _babelFallbackFiles = /* @__PURE__ */ new Set();
1817
- 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.CP2cz9bA.cjs', document.baseURI).href)));
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)));
1818
1814
  const PLUGIN_VERSION = findPackageVersionFromFile(
1819
- node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/unplugin.CP2cz9bA.cjs', document.baseURI).href))),
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))),
1820
1816
  UNKNOWN_PACKAGE_VERSION
1821
1817
  );
1822
1818
  const COMPILER_VERSION = findPackageVersionFromModule("@csszyx/compiler", UNKNOWN_PACKAGE_VERSION);
@@ -3159,12 +3155,55 @@ function createCsszyxPlugins(options = {}) {
3159
3155
  state.skippedSzFiles.add(filePath);
3160
3156
  }
3161
3157
  }
3158
+ function processPrescanTransform(filePath, content, result, discoveredClasses, rawDiscoveredClasses) {
3159
+ const budgetExceeded = result.diagnostics.some(
3160
+ (diagnostic) => diagnostic.includes("AST budget exceeded")
3161
+ );
3162
+ if (cacheEnabled && !budgetExceeded && content !== void 0) {
3163
+ prescanResultHandoff.set(normalizeSourceFilename(filePath), {
3164
+ inputSha256: node_crypto.createHash("sha256").update(content).digest("hex"),
3165
+ result
3166
+ });
3167
+ }
3168
+ if (budgetExceeded) {
3169
+ warnPrescanBudgetSkip(filePath);
3170
+ return;
3171
+ }
3172
+ const parseFailed = result.diagnostics.some(
3173
+ (diagnostic) => diagnostic.includes("[csszyx] parse error in ")
3174
+ );
3175
+ if (result.classes.size === 0 && result.rawClassNames.size === 0 && parseFailed) {
3176
+ console.warn(
3177
+ `[csszyx] prescan skipped ${filePath}: the file failed to parse, so none of its classes reached the safelist. Fix the syntax error (or check the file extension matches its contents).`
3178
+ );
3179
+ return;
3180
+ }
3181
+ if (!result.transformed && result.classes.size === 0) {
3182
+ return;
3183
+ }
3184
+ collectPrescanResult(result, filePath, discoveredClasses, rawDiscoveredClasses);
3185
+ }
3162
3186
  function prescanAndWriteClasses() {
3163
3187
  refreshCompileSourceDirs();
3164
3188
  const prescanStarted = node_perf_hooks.performance.now();
3165
3189
  const discoveredClasses = /* @__PURE__ */ new Set();
3166
3190
  const rawDiscoveredClasses = /* @__PURE__ */ new Set();
3167
3191
  const prescanSources = [];
3192
+ function collectPrescanSource(filePath) {
3193
+ if (!shouldProcessSource(filePath)) {
3194
+ recordPackagesSkipIfSz(filePath);
3195
+ return;
3196
+ }
3197
+ let content;
3198
+ try {
3199
+ content = fs__namespace.readFileSync(filePath, "utf-8");
3200
+ } catch {
3201
+ return;
3202
+ }
3203
+ if (fileMayContainSafelistableSz(content)) {
3204
+ prescanSources.push({ filePath, content });
3205
+ }
3206
+ }
3168
3207
  function scanDir(dir) {
3169
3208
  let entries;
3170
3209
  try {
@@ -3177,22 +3216,10 @@ function createCsszyxPlugins(options = {}) {
3177
3216
  if (!IGNORE_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
3178
3217
  scanDir(path__namespace.join(dir, entry.name));
3179
3218
  }
3180
- } else if (SOURCE_EXTENSIONS.has(path__namespace.extname(entry.name))) {
3181
- const filePath = path__namespace.join(dir, entry.name);
3182
- if (!shouldProcessSource(filePath)) {
3183
- recordPackagesSkipIfSz(filePath);
3184
- continue;
3185
- }
3186
- let content;
3187
- try {
3188
- content = fs__namespace.readFileSync(filePath, "utf-8");
3189
- } catch {
3190
- continue;
3191
- }
3192
- if (!fileMayContainSafelistableSz(content)) {
3193
- continue;
3194
- }
3195
- prescanSources.push({ filePath, content });
3219
+ continue;
3220
+ }
3221
+ if (SOURCE_EXTENSIONS.has(path__namespace.extname(entry.name))) {
3222
+ collectPrescanSource(path__namespace.join(dir, entry.name));
3196
3223
  }
3197
3224
  }
3198
3225
  }
@@ -3208,29 +3235,13 @@ function createCsszyxPlugins(options = {}) {
3208
3235
  prescanSources.map((file) => [file.filePath, file.content])
3209
3236
  );
3210
3237
  for (const { filePath, result } of transformPrescanSources(prescanSources)) {
3211
- if (cacheEnabled && !result.diagnostics.some((d) => d.includes("AST budget exceeded"))) {
3212
- const content = prescanContentByPath.get(filePath);
3213
- if (content !== void 0) {
3214
- prescanResultHandoff.set(normalizeSourceFilename(filePath), {
3215
- inputSha256: node_crypto.createHash("sha256").update(content).digest("hex"),
3216
- result
3217
- });
3218
- }
3219
- }
3220
- if (result.diagnostics.some((d) => d.includes("AST budget exceeded"))) {
3221
- warnPrescanBudgetSkip(filePath);
3222
- continue;
3223
- }
3224
- if (result.classes.size === 0 && result.rawClassNames.size === 0 && result.diagnostics.some((d) => d.includes("[csszyx] parse error in "))) {
3225
- console.warn(
3226
- `[csszyx] prescan skipped ${filePath}: the file failed to parse, so none of its classes reached the safelist. Fix the syntax error (or check the file extension matches its contents).`
3227
- );
3228
- continue;
3229
- }
3230
- if (!result.transformed && result.classes.size === 0) {
3231
- continue;
3232
- }
3233
- collectPrescanResult(result, filePath, discoveredClasses, rawDiscoveredClasses);
3238
+ processPrescanTransform(
3239
+ filePath,
3240
+ prescanContentByPath.get(filePath),
3241
+ result,
3242
+ discoveredClasses,
3243
+ rawDiscoveredClasses
3244
+ );
3234
3245
  }
3235
3246
  for (const cls of discoveredClasses) {
3236
3247
  addSafelistClass(cls);
@@ -3315,40 +3326,48 @@ function createCsszyxPlugins(options = {}) {
3315
3326
  discoveredClasses.add(cls);
3316
3327
  }
3317
3328
  }
3318
- function extractClasses(code) {
3319
- const dqPattern = /(?:class(?:Name)?|sz)[:=]\s*"([^"]*)"/g;
3320
- const sqPattern = /(?:class(?:Name)?|sz)[:=]\s*'([^']*)'/g;
3321
- for (const classPattern of [dqPattern, sqPattern]) {
3322
- for (const match of code.matchAll(classPattern)) {
3323
- const classes = match[1].split(/\s+/).filter(Boolean);
3324
- for (const cls of classes) {
3325
- addSafelistClass(cls);
3326
- }
3329
+ function addSafelistClasses(value) {
3330
+ for (const className of value.split(/\s+/).filter(Boolean)) {
3331
+ addSafelistClass(className);
3332
+ }
3333
+ }
3334
+ function collectQuotedAttributeClasses(code) {
3335
+ const patterns = [
3336
+ /(?:class(?:Name)?|sz)[:=]\s*"([^"]*)"/g,
3337
+ /(?:class(?:Name)?|sz)[:=]\s*'([^']*)'/g
3338
+ ];
3339
+ for (const pattern of patterns) {
3340
+ for (const match of code.matchAll(pattern)) {
3341
+ addSafelistClasses(match[1] ?? "");
3327
3342
  }
3328
3343
  }
3329
- const exprStart = /className=\{/g;
3330
- for (const match of code.matchAll(exprStart)) {
3331
- let depth = 1;
3332
- let i = (match.index ?? 0) + match[0].length;
3333
- while (i < code.length && depth > 0) {
3334
- if (code[i] === "{") {
3335
- depth++;
3336
- } else if (code[i] === "}") {
3337
- depth--;
3338
- }
3339
- i++;
3344
+ }
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--;
3340
3353
  }
3341
- const expr = code.slice((match.index ?? 0) + match[0].length, i - 1);
3342
- const strPattern = /"([^"]+)"|'([^']+)'/g;
3343
- for (const strMatch of expr.matchAll(strPattern)) {
3344
- const str = strMatch[1] || strMatch[2];
3345
- const classes = str.split(/\s+/).filter(Boolean);
3346
- for (const cls of classes) {
3347
- addSafelistClass(cls);
3348
- }
3354
+ index++;
3355
+ }
3356
+ return index - 1;
3357
+ }
3358
+ function collectExpressionClasses(code) {
3359
+ for (const match of code.matchAll(/className=\{/g)) {
3360
+ const bodyStart = (match.index ?? 0) + match[0].length;
3361
+ const expression = code.slice(bodyStart, findJsxExpressionEnd(code, bodyStart));
3362
+ for (const stringMatch of expression.matchAll(/"([^"]+)"|'([^']+)'/g)) {
3363
+ addSafelistClasses(stringMatch[1] ?? stringMatch[2] ?? "");
3349
3364
  }
3350
3365
  }
3351
3366
  }
3367
+ function extractClasses(code) {
3368
+ collectQuotedAttributeClasses(code);
3369
+ collectExpressionClasses(code);
3370
+ }
3352
3371
  function finalizeMangleMap() {
3353
3372
  const sortedClasses = Array.from(state.ownedClasses);
3354
3373
  const newMap = {};
@@ -3372,6 +3391,24 @@ function createCsszyxPlugins(options = {}) {
3372
3391
  function mangleCodeClasses(code) {
3373
3392
  return mangleCodeClassesSync(code, state.mangleMap);
3374
3393
  }
3394
+ function mangleHtmlClassList(classes) {
3395
+ const output = [];
3396
+ for (const className of classes.split(/\s+/)) {
3397
+ if (className) output.push(state.mangleMap[className] || className);
3398
+ }
3399
+ return output.join(" ");
3400
+ }
3401
+ function replaceDoubleQuotedHtmlClass(match, classes) {
3402
+ const output = mangleHtmlClassList(classes);
3403
+ return output !== classes ? `class="${output}"` : match;
3404
+ }
3405
+ function replaceSingleQuotedHtmlClass(match, classes) {
3406
+ const output = mangleHtmlClassList(classes);
3407
+ return output !== classes ? `class='${output}'` : match;
3408
+ }
3409
+ function mangleHtmlClasses(source) {
3410
+ return source.replace(/\bclass="([^"]*)"/g, replaceDoubleQuotedHtmlClass).replace(/\bclass='([^']*)'/g, replaceSingleQuotedHtmlClass);
3411
+ }
3375
3412
  function replacePlaceholders(code) {
3376
3413
  let result = code;
3377
3414
  if (result.includes(CHECKSUM_PLACEHOLDER)) {
@@ -3506,6 +3543,8 @@ function createCsszyxPlugins(options = {}) {
3506
3543
  let usesSzcn = false;
3507
3544
  let usesSzPart = false;
3508
3545
  let usesColorVar = false;
3546
+ let usesSpacingVar = false;
3547
+ let usesUnitVar = false;
3509
3548
  let transformed = false;
3510
3549
  let szClasses;
3511
3550
  const hasSzProp = code.includes("sz=") || code.includes("szs=") || /\bsz\s*:\s*["'{]/.test(code) || code.includes('sz: "');
@@ -3536,6 +3575,8 @@ function createCsszyxPlugins(options = {}) {
3536
3575
  usesSzcn = result.usesSzcn;
3537
3576
  usesSzPart = result.usesSzPart;
3538
3577
  usesColorVar = result.usesColorVar;
3578
+ usesSpacingVar = result.usesSpacingVar;
3579
+ usesUnitVar = result.usesUnitVar;
3539
3580
  transformed = result.transformed;
3540
3581
  szClasses = result.classes;
3541
3582
  recordFileVarMangleEntries(state, id, cssVariableEntries(result));
@@ -3596,6 +3637,12 @@ function createCsszyxPlugins(options = {}) {
3596
3637
  if (usesColorVar) {
3597
3638
  imports.push("__szColorVar");
3598
3639
  }
3640
+ if (usesSpacingVar) {
3641
+ imports.push("__szSpacingVar");
3642
+ }
3643
+ if (usesUnitVar) {
3644
+ imports.push("__szUnitVar");
3645
+ }
3599
3646
  const hasRuntimeImport = imports.length > 0 && transformedCode.includes("@csszyx/runtime");
3600
3647
  const needed = hasRuntimeImport ? imports.filter((name) => !runtimeImportScan.importsRuntimeHelper(transformedCode, name)) : imports;
3601
3648
  if (needed.length > 0) {
@@ -3948,19 +3995,7 @@ ${transformedCode}`;
3948
3995
  }
3949
3996
  if (manglingEnabled && !isWebpackDevMode && Object.keys(state.mangleMap).length > 0) {
3950
3997
  if (file.endsWith(".html")) {
3951
- const mangledHtml = source.replace(
3952
- /\bclass="([^"]*)"/g,
3953
- (_m, cls) => {
3954
- const out = cls.split(/\s+/).filter(Boolean).map((c) => state.mangleMap[c] || c).join(" ");
3955
- return out !== cls ? `class="${out}"` : _m;
3956
- }
3957
- ).replace(
3958
- /\bclass='([^']*)'/g,
3959
- (_m, cls) => {
3960
- const out = cls.split(/\s+/).filter(Boolean).map((c) => state.mangleMap[c] || c).join(" ");
3961
- return out !== cls ? `class='${out}'` : _m;
3962
- }
3963
- );
3998
+ const mangledHtml = mangleHtmlClasses(source);
3964
3999
  if (mangledHtml !== source) {
3965
4000
  compilation.updateAsset(
3966
4001
  file,
@@ -2,7 +2,7 @@
2
2
 
3
3
  const fs = require('node:fs');
4
4
  const compiler = require('@csszyx/compiler');
5
- const transformCache = require('./unplugin.D0N9bprz.cjs');
5
+ const transformCache = require('./unplugin.D8vSG36M.cjs');
6
6
  const node_crypto = require('node:crypto');
7
7
  const htmlEscape = require('./unplugin.BCwRIUs_.cjs');
8
8