@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 @@ import { createUnplugin } from 'unplugin';
16
16
  import { mangleCSSSync } from '../css-mangler.mjs';
17
17
  import { s as sortStrings, e as escapeHtmlAttribute } from './unplugin.B1mblcm-.mjs';
18
18
  import { i as importsRuntimeHelper, f as findRuntimeImportClause } from './unplugin.B3RHYokB.mjs';
19
- import { r as resolveTransformCacheDir, c as createTransformCacheKey, a as readTransformCache, w as writeTransformCache, e as evictOldTransformCacheEntries, b as evictMemoryCacheToBudget } from './unplugin.ByzV6iZE.mjs';
19
+ import { r as resolveTransformCacheDir, c as createTransformCacheKey, a as readTransformCache, w as writeTransformCache, e as evictOldTransformCacheEntries, b as evictMemoryCacheToBudget } from './unplugin.CwcOd1q3.mjs';
20
20
  import postcss from 'postcss';
21
21
  import valueParser from 'postcss-value-parser';
22
22
 
@@ -615,6 +615,27 @@ function walkRSCGraph(current, records, chain, seen) {
615
615
  }
616
616
  return null;
617
617
  }
618
+ function findDirectiveQuoteEnd(code, start, quote) {
619
+ let escaped = false;
620
+ for (let index = start + 1; index < code.length; index++) {
621
+ const char = code[index];
622
+ if (escaped) {
623
+ escaped = false;
624
+ } else if (char === "\\") {
625
+ escaped = true;
626
+ } else if (char === quote) {
627
+ return index;
628
+ }
629
+ }
630
+ return -1;
631
+ }
632
+ function findDirectiveStatementEnd(code, start) {
633
+ let end = start;
634
+ while (end < code.length && /[ \t\r\n]/.test(code[end])) {
635
+ end++;
636
+ }
637
+ return code[end] === ";" ? end + 1 : end;
638
+ }
618
639
  function readDirectivePrologue(code) {
619
640
  const out = [];
620
641
  let i = code.charCodeAt(0) === 65279 ? 1 : 0;
@@ -624,29 +645,11 @@ function readDirectivePrologue(code) {
624
645
  if (quote !== '"' && quote !== "'") {
625
646
  break;
626
647
  }
627
- let j = i + 1;
628
- let escaped = false;
629
- while (j < code.length) {
630
- const ch = code[j];
631
- if (escaped) {
632
- escaped = false;
633
- } else if (ch === "\\") {
634
- escaped = true;
635
- } else if (ch === quote) {
636
- break;
637
- }
638
- j++;
639
- }
640
- if (j >= code.length) {
648
+ const quoteEnd = findDirectiveQuoteEnd(code, i, quote);
649
+ if (quoteEnd === -1) {
641
650
  break;
642
651
  }
643
- let end = j + 1;
644
- while (end < code.length && /[ \t\r\n]/.test(code[end])) {
645
- end++;
646
- }
647
- if (code[end] === ";") {
648
- end++;
649
- }
652
+ const end = findDirectiveStatementEnd(code, quoteEnd + 1);
650
653
  out.push(code.slice(i, end).trim());
651
654
  i = end;
652
655
  }
@@ -982,33 +985,34 @@ function pruneRSCModulePathCaches(moduleIds) {
982
985
  }
983
986
  }
984
987
  }
985
- function readImportedSymbols(clause) {
986
- const symbols = [];
988
+ function readNamedImportedSymbols(clause) {
987
989
  const openBrace = clause.indexOf("{");
988
990
  const closeBrace = openBrace === -1 ? -1 : clause.indexOf("}", openBrace);
989
- if (openBrace !== -1 && closeBrace !== -1) {
990
- const namedPart = clause.slice(openBrace + 1, closeBrace);
991
- for (const part of namedPart.split(",")) {
992
- const trimmed = part.trim();
993
- if (!trimmed || trimmed.startsWith("type ")) {
994
- continue;
995
- }
996
- const sourceName = trimmed.replace(/^type[ \t]+/, "").split(/(?<![ \t])[ \t]+as[ \t]+/)[0]?.trim();
997
- if (sourceName) {
998
- symbols.push(sourceName);
999
- }
1000
- }
1001
- }
1002
- const namespaceParts = splitAsciiWhitespace(clause);
1003
- if (namespaceParts.length >= 3 && namespaceParts[0] === "*" && namespaceParts[1] === "as" && isIdentifier(namespaceParts[2] ?? "")) {
1004
- symbols.push(...FORBIDDEN_SYMBOLS);
991
+ if (openBrace === -1 || closeBrace === -1) {
992
+ return [];
1005
993
  }
994
+ return clause.slice(openBrace + 1, closeBrace).split(",").map((part) => part.trim()).filter((part) => part !== "" && !part.startsWith("type ")).map(
995
+ (part) => part.replace(/^type[ \t]+/, "").split(/(?<![ \t])[ \t]+as[ \t]+/)[0]?.trim()
996
+ ).filter((symbol) => Boolean(symbol));
997
+ }
998
+ function readForbiddenDefaultSymbol(clause) {
1006
999
  const braceStart = clause.indexOf("{");
1007
1000
  const braceEnd = clause.indexOf("}", braceStart);
1008
1001
  const stripped = braceStart !== -1 && braceEnd !== -1 ? clause.slice(0, braceStart) + clause.slice(braceEnd + 1) : clause;
1009
- const defaultCandidate = stripped.trimStart().split(",", 1)[0]?.trim() ?? "";
1010
- const defaultSymbol = isIdentifier(defaultCandidate) ? defaultCandidate : void 0;
1011
- if (defaultSymbol && FORBIDDEN_SYMBOLS.has(defaultSymbol)) {
1002
+ const candidate = stripped.trimStart().split(",", 1)[0]?.trim() ?? "";
1003
+ return isIdentifier(candidate) && FORBIDDEN_SYMBOLS.has(candidate) ? candidate : null;
1004
+ }
1005
+ function hasNamespaceImport(clause) {
1006
+ const parts = splitAsciiWhitespace(clause);
1007
+ return parts.length >= 3 && parts[0] === "*" && parts[1] === "as" && isIdentifier(parts[2] ?? "");
1008
+ }
1009
+ function readImportedSymbols(clause) {
1010
+ const symbols = readNamedImportedSymbols(clause);
1011
+ if (hasNamespaceImport(clause)) {
1012
+ symbols.push(...FORBIDDEN_SYMBOLS);
1013
+ }
1014
+ const defaultSymbol = readForbiddenDefaultSymbol(clause);
1015
+ if (defaultSymbol) {
1012
1016
  symbols.push(defaultSymbol);
1013
1017
  }
1014
1018
  return symbols;
@@ -1112,6 +1116,20 @@ const EMPTY_THEME = {
1112
1116
  shadows: [],
1113
1117
  breakpoints: []
1114
1118
  };
1119
+ function findMatchingBrace(source, openBrace) {
1120
+ let depth = 0;
1121
+ for (let index = openBrace; index < source.length; index++) {
1122
+ if (source[index] === "{") {
1123
+ depth++;
1124
+ } else if (source[index] === "}") {
1125
+ depth--;
1126
+ if (depth === 0) {
1127
+ return index;
1128
+ }
1129
+ }
1130
+ }
1131
+ return -1;
1132
+ }
1115
1133
  function stripLayerWrappers(css) {
1116
1134
  let result = "";
1117
1135
  let i = 0;
@@ -1127,26 +1145,13 @@ function stripLayerWrappers(css) {
1127
1145
  result += css.slice(layerIdx);
1128
1146
  break;
1129
1147
  }
1130
- let depth = 0;
1131
- let j = openBrace;
1132
- while (j < css.length) {
1133
- if (css[j] === "{") {
1134
- depth++;
1135
- }
1136
- if (css[j] === "}") {
1137
- depth--;
1138
- if (depth === 0) {
1139
- result += css.slice(openBrace + 1, j);
1140
- i = j + 1;
1141
- break;
1142
- }
1143
- }
1144
- j++;
1145
- }
1146
- if (depth !== 0) {
1148
+ const closeBrace = findMatchingBrace(css, openBrace);
1149
+ if (closeBrace === -1) {
1147
1150
  result += css.slice(openBrace);
1148
1151
  break;
1149
1152
  }
1153
+ result += css.slice(openBrace + 1, closeBrace);
1154
+ i = closeBrace + 1;
1150
1155
  }
1151
1156
  return result;
1152
1157
  }
@@ -1155,20 +1160,9 @@ function extractThemeBlocks(css) {
1155
1160
  const themeStart = /@theme\s+(?:inline\s+)?\{|@theme\{/g;
1156
1161
  for (const match of css.matchAll(themeStart)) {
1157
1162
  const openPos = css.indexOf("{", match.index);
1158
- let depth = 0;
1159
- let j = openPos;
1160
- while (j < css.length) {
1161
- if (css[j] === "{") {
1162
- depth++;
1163
- }
1164
- if (css[j] === "}") {
1165
- depth--;
1166
- if (depth === 0) {
1167
- blocks.push(css.slice(openPos + 1, j));
1168
- break;
1169
- }
1170
- }
1171
- j++;
1163
+ const closePos = findMatchingBrace(css, openPos);
1164
+ if (closePos !== -1) {
1165
+ blocks.push(css.slice(openPos + 1, closePos));
1172
1166
  }
1173
1167
  }
1174
1168
  return blocks;
@@ -1267,6 +1261,29 @@ function mergeThemes(themes) {
1267
1261
  function hasTokens(theme) {
1268
1262
  return Object.values(theme).some((arr) => arr.length > 0);
1269
1263
  }
1264
+ function readCustomPropertyName(block, dashes) {
1265
+ let end = dashes + 2;
1266
+ if (end >= block.length || !/[a-z]/.test(block[end])) {
1267
+ return null;
1268
+ }
1269
+ end++;
1270
+ while (end < block.length && /[a-z0-9-]/.test(block[end])) {
1271
+ end++;
1272
+ }
1273
+ return { name: block.slice(dashes + 2, end), end };
1274
+ }
1275
+ function findCustomPropertyDeclarationEnd(block, nameEnd) {
1276
+ let cursor = nameEnd;
1277
+ while (cursor < block.length && /\s/.test(block[cursor])) {
1278
+ cursor++;
1279
+ }
1280
+ if (block[cursor] === ":") {
1281
+ const valueStart = cursor + 1;
1282
+ const semicolon = block.indexOf(";", valueStart);
1283
+ return semicolon > valueStart ? semicolon + 1 : -1;
1284
+ }
1285
+ return block[nameEnd] === ";" ? nameEnd + 1 : -1;
1286
+ }
1270
1287
  function scanCustomPropertyNames(block) {
1271
1288
  const names = [];
1272
1289
  let i = 0;
@@ -1275,36 +1292,17 @@ function scanCustomPropertyNames(block) {
1275
1292
  if (dashes === -1) {
1276
1293
  break;
1277
1294
  }
1278
- let end = dashes + 2;
1279
- if (end >= block.length || !/[a-z]/.test(block[end])) {
1295
+ const property = readCustomPropertyName(block, dashes);
1296
+ if (!property) {
1280
1297
  i = dashes + 1;
1281
1298
  continue;
1282
1299
  }
1283
- end++;
1284
- while (end < block.length && /[a-z0-9-]/.test(block[end])) {
1285
- end++;
1286
- }
1287
- const name = block.slice(dashes + 2, end);
1288
- let matchEnd = -1;
1289
- let cursor = end;
1290
- while (cursor < block.length && /\s/.test(block[cursor])) {
1291
- cursor++;
1292
- }
1293
- if (block[cursor] === ":") {
1294
- const valueStart = cursor + 1;
1295
- const semi = block.indexOf(";", valueStart);
1296
- if (semi > valueStart) {
1297
- matchEnd = semi + 1;
1298
- }
1299
- }
1300
- if (matchEnd === -1 && block[end] === ";") {
1301
- matchEnd = end + 1;
1302
- }
1300
+ const matchEnd = findCustomPropertyDeclarationEnd(block, property.end);
1303
1301
  if (matchEnd === -1) {
1304
1302
  i = dashes + 1;
1305
1303
  continue;
1306
1304
  }
1307
- names.push(name);
1305
+ names.push(property.name);
1308
1306
  i = matchEnd;
1309
1307
  }
1310
1308
  return names;
@@ -1372,27 +1370,36 @@ function matchesAnyPattern(id, patterns, rootDir) {
1372
1370
  const list = Array.isArray(patterns) ? patterns : [patterns];
1373
1371
  return list.some((pattern) => matchesPattern(id, pattern, rootDir));
1374
1372
  }
1373
+ function collectDirectoryFiles(dir, files) {
1374
+ let entries;
1375
+ try {
1376
+ entries = fs.readdirSync(dir, { withFileTypes: true });
1377
+ } catch {
1378
+ return;
1379
+ }
1380
+ for (const entry of entries) {
1381
+ const full = path.join(dir, entry.name);
1382
+ if (entry.isDirectory()) {
1383
+ if (!DEFAULT_IGNORED_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
1384
+ collectDirectoryFiles(full, files);
1385
+ }
1386
+ } else {
1387
+ files.add(path.resolve(full));
1388
+ }
1389
+ }
1390
+ }
1391
+ function isExpandedPatternMatch(file, patterns, rootDir) {
1392
+ return patterns.some((pattern) => {
1393
+ if (hasGlobMagic(pattern)) {
1394
+ return matchesPattern(file, pattern, rootDir);
1395
+ }
1396
+ const resolved = path.isAbsolute(pattern) ? pattern : path.join(rootDir, pattern);
1397
+ return normalizeFileId(path.resolve(resolved)) === normalizeFileId(file);
1398
+ });
1399
+ }
1375
1400
  function expandFilePatterns(rootDir, patterns) {
1376
1401
  const list = Array.isArray(patterns) ? patterns : [patterns];
1377
1402
  const files = /* @__PURE__ */ new Set();
1378
- const walk = (dir) => {
1379
- let entries;
1380
- try {
1381
- entries = fs.readdirSync(dir, { withFileTypes: true });
1382
- } catch {
1383
- return;
1384
- }
1385
- for (const entry of entries) {
1386
- const full = path.join(dir, entry.name);
1387
- if (entry.isDirectory()) {
1388
- if (!DEFAULT_IGNORED_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
1389
- walk(full);
1390
- }
1391
- } else {
1392
- files.add(path.resolve(full));
1393
- }
1394
- }
1395
- };
1396
1403
  let needsWalk = false;
1397
1404
  for (const pattern of list) {
1398
1405
  const resolved = path.isAbsolute(pattern) ? pattern : path.join(rootDir, pattern);
@@ -1405,21 +1412,10 @@ function expandFilePatterns(rootDir, patterns) {
1405
1412
  }
1406
1413
  }
1407
1414
  if (needsWalk) {
1408
- walk(rootDir);
1415
+ collectDirectoryFiles(rootDir, files);
1409
1416
  for (const file of Array.from(files)) {
1410
- if (!list.some(
1411
- (pattern) => hasGlobMagic(pattern) && matchesPattern(file, pattern, rootDir)
1412
- )) {
1413
- const isLiteralMatch = list.some((pattern) => {
1414
- if (hasGlobMagic(pattern)) {
1415
- return false;
1416
- }
1417
- const resolved = path.isAbsolute(pattern) ? pattern : path.join(rootDir, pattern);
1418
- return normalizeFileId(path.resolve(resolved)) === normalizeFileId(file);
1419
- });
1420
- if (!isLiteralMatch) {
1421
- files.delete(file);
1422
- }
1417
+ if (!isExpandedPatternMatch(file, list, rootDir)) {
1418
+ files.delete(file);
1423
1419
  }
1424
1420
  }
1425
1421
  }
@@ -3139,12 +3135,55 @@ function createCsszyxPlugins(options = {}) {
3139
3135
  state.skippedSzFiles.add(filePath);
3140
3136
  }
3141
3137
  }
3138
+ function processPrescanTransform(filePath, content, result, discoveredClasses, rawDiscoveredClasses) {
3139
+ const budgetExceeded = result.diagnostics.some(
3140
+ (diagnostic) => diagnostic.includes("AST budget exceeded")
3141
+ );
3142
+ if (cacheEnabled && !budgetExceeded && content !== void 0) {
3143
+ prescanResultHandoff.set(normalizeSourceFilename(filePath), {
3144
+ inputSha256: createHash("sha256").update(content).digest("hex"),
3145
+ result
3146
+ });
3147
+ }
3148
+ if (budgetExceeded) {
3149
+ warnPrescanBudgetSkip(filePath);
3150
+ return;
3151
+ }
3152
+ const parseFailed = result.diagnostics.some(
3153
+ (diagnostic) => diagnostic.includes("[csszyx] parse error in ")
3154
+ );
3155
+ if (result.classes.size === 0 && result.rawClassNames.size === 0 && parseFailed) {
3156
+ console.warn(
3157
+ `[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).`
3158
+ );
3159
+ return;
3160
+ }
3161
+ if (!result.transformed && result.classes.size === 0) {
3162
+ return;
3163
+ }
3164
+ collectPrescanResult(result, filePath, discoveredClasses, rawDiscoveredClasses);
3165
+ }
3142
3166
  function prescanAndWriteClasses() {
3143
3167
  refreshCompileSourceDirs();
3144
3168
  const prescanStarted = performance.now();
3145
3169
  const discoveredClasses = /* @__PURE__ */ new Set();
3146
3170
  const rawDiscoveredClasses = /* @__PURE__ */ new Set();
3147
3171
  const prescanSources = [];
3172
+ function collectPrescanSource(filePath) {
3173
+ if (!shouldProcessSource(filePath)) {
3174
+ recordPackagesSkipIfSz(filePath);
3175
+ return;
3176
+ }
3177
+ let content;
3178
+ try {
3179
+ content = fs.readFileSync(filePath, "utf-8");
3180
+ } catch {
3181
+ return;
3182
+ }
3183
+ if (fileMayContainSafelistableSz(content)) {
3184
+ prescanSources.push({ filePath, content });
3185
+ }
3186
+ }
3148
3187
  function scanDir(dir) {
3149
3188
  let entries;
3150
3189
  try {
@@ -3157,22 +3196,10 @@ function createCsszyxPlugins(options = {}) {
3157
3196
  if (!IGNORE_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
3158
3197
  scanDir(path.join(dir, entry.name));
3159
3198
  }
3160
- } else if (SOURCE_EXTENSIONS.has(path.extname(entry.name))) {
3161
- const filePath = path.join(dir, entry.name);
3162
- if (!shouldProcessSource(filePath)) {
3163
- recordPackagesSkipIfSz(filePath);
3164
- continue;
3165
- }
3166
- let content;
3167
- try {
3168
- content = fs.readFileSync(filePath, "utf-8");
3169
- } catch {
3170
- continue;
3171
- }
3172
- if (!fileMayContainSafelistableSz(content)) {
3173
- continue;
3174
- }
3175
- prescanSources.push({ filePath, content });
3199
+ continue;
3200
+ }
3201
+ if (SOURCE_EXTENSIONS.has(path.extname(entry.name))) {
3202
+ collectPrescanSource(path.join(dir, entry.name));
3176
3203
  }
3177
3204
  }
3178
3205
  }
@@ -3188,29 +3215,13 @@ function createCsszyxPlugins(options = {}) {
3188
3215
  prescanSources.map((file) => [file.filePath, file.content])
3189
3216
  );
3190
3217
  for (const { filePath, result } of transformPrescanSources(prescanSources)) {
3191
- if (cacheEnabled && !result.diagnostics.some((d) => d.includes("AST budget exceeded"))) {
3192
- const content = prescanContentByPath.get(filePath);
3193
- if (content !== void 0) {
3194
- prescanResultHandoff.set(normalizeSourceFilename(filePath), {
3195
- inputSha256: createHash("sha256").update(content).digest("hex"),
3196
- result
3197
- });
3198
- }
3199
- }
3200
- if (result.diagnostics.some((d) => d.includes("AST budget exceeded"))) {
3201
- warnPrescanBudgetSkip(filePath);
3202
- continue;
3203
- }
3204
- if (result.classes.size === 0 && result.rawClassNames.size === 0 && result.diagnostics.some((d) => d.includes("[csszyx] parse error in "))) {
3205
- console.warn(
3206
- `[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).`
3207
- );
3208
- continue;
3209
- }
3210
- if (!result.transformed && result.classes.size === 0) {
3211
- continue;
3212
- }
3213
- collectPrescanResult(result, filePath, discoveredClasses, rawDiscoveredClasses);
3218
+ processPrescanTransform(
3219
+ filePath,
3220
+ prescanContentByPath.get(filePath),
3221
+ result,
3222
+ discoveredClasses,
3223
+ rawDiscoveredClasses
3224
+ );
3214
3225
  }
3215
3226
  for (const cls of discoveredClasses) {
3216
3227
  addSafelistClass(cls);
@@ -3295,40 +3306,48 @@ function createCsszyxPlugins(options = {}) {
3295
3306
  discoveredClasses.add(cls);
3296
3307
  }
3297
3308
  }
3298
- function extractClasses(code) {
3299
- const dqPattern = /(?:class(?:Name)?|sz)[:=]\s*"([^"]*)"/g;
3300
- const sqPattern = /(?:class(?:Name)?|sz)[:=]\s*'([^']*)'/g;
3301
- for (const classPattern of [dqPattern, sqPattern]) {
3302
- for (const match of code.matchAll(classPattern)) {
3303
- const classes = match[1].split(/\s+/).filter(Boolean);
3304
- for (const cls of classes) {
3305
- addSafelistClass(cls);
3306
- }
3309
+ function addSafelistClasses(value) {
3310
+ for (const className of value.split(/\s+/).filter(Boolean)) {
3311
+ addSafelistClass(className);
3312
+ }
3313
+ }
3314
+ function collectQuotedAttributeClasses(code) {
3315
+ const patterns = [
3316
+ /(?:class(?:Name)?|sz)[:=]\s*"([^"]*)"/g,
3317
+ /(?:class(?:Name)?|sz)[:=]\s*'([^']*)'/g
3318
+ ];
3319
+ for (const pattern of patterns) {
3320
+ for (const match of code.matchAll(pattern)) {
3321
+ addSafelistClasses(match[1] ?? "");
3307
3322
  }
3308
3323
  }
3309
- const exprStart = /className=\{/g;
3310
- for (const match of code.matchAll(exprStart)) {
3311
- let depth = 1;
3312
- let i = (match.index ?? 0) + match[0].length;
3313
- while (i < code.length && depth > 0) {
3314
- if (code[i] === "{") {
3315
- depth++;
3316
- } else if (code[i] === "}") {
3317
- depth--;
3318
- }
3319
- i++;
3324
+ }
3325
+ function findJsxExpressionEnd(code, bodyStart) {
3326
+ let depth = 1;
3327
+ let index = bodyStart;
3328
+ while (index < code.length && depth > 0) {
3329
+ if (code[index] === "{") {
3330
+ depth++;
3331
+ } else if (code[index] === "}") {
3332
+ depth--;
3320
3333
  }
3321
- const expr = code.slice((match.index ?? 0) + match[0].length, i - 1);
3322
- const strPattern = /"([^"]+)"|'([^']+)'/g;
3323
- for (const strMatch of expr.matchAll(strPattern)) {
3324
- const str = strMatch[1] || strMatch[2];
3325
- const classes = str.split(/\s+/).filter(Boolean);
3326
- for (const cls of classes) {
3327
- addSafelistClass(cls);
3328
- }
3334
+ index++;
3335
+ }
3336
+ return index - 1;
3337
+ }
3338
+ function collectExpressionClasses(code) {
3339
+ for (const match of code.matchAll(/className=\{/g)) {
3340
+ const bodyStart = (match.index ?? 0) + match[0].length;
3341
+ const expression = code.slice(bodyStart, findJsxExpressionEnd(code, bodyStart));
3342
+ for (const stringMatch of expression.matchAll(/"([^"]+)"|'([^']+)'/g)) {
3343
+ addSafelistClasses(stringMatch[1] ?? stringMatch[2] ?? "");
3329
3344
  }
3330
3345
  }
3331
3346
  }
3347
+ function extractClasses(code) {
3348
+ collectQuotedAttributeClasses(code);
3349
+ collectExpressionClasses(code);
3350
+ }
3332
3351
  function finalizeMangleMap() {
3333
3352
  const sortedClasses = Array.from(state.ownedClasses);
3334
3353
  const newMap = {};
@@ -3352,6 +3371,24 @@ function createCsszyxPlugins(options = {}) {
3352
3371
  function mangleCodeClasses(code) {
3353
3372
  return mangleCodeClassesSync(code, state.mangleMap);
3354
3373
  }
3374
+ function mangleHtmlClassList(classes) {
3375
+ const output = [];
3376
+ for (const className of classes.split(/\s+/)) {
3377
+ if (className) output.push(state.mangleMap[className] || className);
3378
+ }
3379
+ return output.join(" ");
3380
+ }
3381
+ function replaceDoubleQuotedHtmlClass(match, classes) {
3382
+ const output = mangleHtmlClassList(classes);
3383
+ return output !== classes ? `class="${output}"` : match;
3384
+ }
3385
+ function replaceSingleQuotedHtmlClass(match, classes) {
3386
+ const output = mangleHtmlClassList(classes);
3387
+ return output !== classes ? `class='${output}'` : match;
3388
+ }
3389
+ function mangleHtmlClasses(source) {
3390
+ return source.replace(/\bclass="([^"]*)"/g, replaceDoubleQuotedHtmlClass).replace(/\bclass='([^']*)'/g, replaceSingleQuotedHtmlClass);
3391
+ }
3355
3392
  function replacePlaceholders(code) {
3356
3393
  let result = code;
3357
3394
  if (result.includes(CHECKSUM_PLACEHOLDER)) {
@@ -3486,6 +3523,8 @@ function createCsszyxPlugins(options = {}) {
3486
3523
  let usesSzcn = false;
3487
3524
  let usesSzPart = false;
3488
3525
  let usesColorVar = false;
3526
+ let usesSpacingVar = false;
3527
+ let usesUnitVar = false;
3489
3528
  let transformed = false;
3490
3529
  let szClasses;
3491
3530
  const hasSzProp = code.includes("sz=") || code.includes("szs=") || /\bsz\s*:\s*["'{]/.test(code) || code.includes('sz: "');
@@ -3516,6 +3555,8 @@ function createCsszyxPlugins(options = {}) {
3516
3555
  usesSzcn = result.usesSzcn;
3517
3556
  usesSzPart = result.usesSzPart;
3518
3557
  usesColorVar = result.usesColorVar;
3558
+ usesSpacingVar = result.usesSpacingVar;
3559
+ usesUnitVar = result.usesUnitVar;
3519
3560
  transformed = result.transformed;
3520
3561
  szClasses = result.classes;
3521
3562
  recordFileVarMangleEntries(state, id, cssVariableEntries(result));
@@ -3576,6 +3617,12 @@ function createCsszyxPlugins(options = {}) {
3576
3617
  if (usesColorVar) {
3577
3618
  imports.push("__szColorVar");
3578
3619
  }
3620
+ if (usesSpacingVar) {
3621
+ imports.push("__szSpacingVar");
3622
+ }
3623
+ if (usesUnitVar) {
3624
+ imports.push("__szUnitVar");
3625
+ }
3579
3626
  const hasRuntimeImport = imports.length > 0 && transformedCode.includes("@csszyx/runtime");
3580
3627
  const needed = hasRuntimeImport ? imports.filter((name) => !importsRuntimeHelper(transformedCode, name)) : imports;
3581
3628
  if (needed.length > 0) {
@@ -3928,19 +3975,7 @@ ${transformedCode}`;
3928
3975
  }
3929
3976
  if (manglingEnabled && !isWebpackDevMode && Object.keys(state.mangleMap).length > 0) {
3930
3977
  if (file.endsWith(".html")) {
3931
- const mangledHtml = source.replace(
3932
- /\bclass="([^"]*)"/g,
3933
- (_m, cls) => {
3934
- const out = cls.split(/\s+/).filter(Boolean).map((c) => state.mangleMap[c] || c).join(" ");
3935
- return out !== cls ? `class="${out}"` : _m;
3936
- }
3937
- ).replace(
3938
- /\bclass='([^']*)'/g,
3939
- (_m, cls) => {
3940
- const out = cls.split(/\s+/).filter(Boolean).map((c) => state.mangleMap[c] || c).join(" ");
3941
- return out !== cls ? `class='${out}'` : _m;
3942
- }
3943
- );
3978
+ const mangledHtml = mangleHtmlClasses(source);
3944
3979
  if (mangledHtml !== source) {
3945
3980
  compilation.updateAsset(
3946
3981
  file,
@@ -1,8 +1,8 @@
1
- import { createHash } from 'node:crypto';
1
+ import { createHash, randomUUID } from 'node:crypto';
2
2
  import * as fs from 'node:fs';
3
3
  import * as path from 'node:path';
4
4
 
5
- const CACHE_SCHEMA_VERSION = 9;
5
+ const CACHE_SCHEMA_VERSION = 10;
6
6
  function resolveTransformCacheDir(rootDir, cacheDir) {
7
7
  return path.resolve(rootDir, cacheDir ?? ".csszyx/cache", "transform");
8
8
  }
@@ -48,10 +48,7 @@ function writeTransformCache(cacheRoot, input, result, precomputedKey) {
48
48
  const globalVarAliases = normalizeGlobalVarAliasEntries(input.globalVarAliases);
49
49
  const file = cacheEntryPath(cacheRoot, key);
50
50
  const dir = path.dirname(file);
51
- const tmp = path.join(
52
- dir,
53
- `.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}.json`
54
- );
51
+ const tmp = path.join(dir, `.tmp-${process.pid}-${Date.now()}-${randomUUID()}.json`);
55
52
  const entry = {
56
53
  version: CACHE_SCHEMA_VERSION,
57
54
  pluginVersion: input.pluginVersion,
@@ -120,6 +117,8 @@ function serializeResult(result) {
120
117
  usesSzcn: result.usesSzcn,
121
118
  usesSzPart: result.usesSzPart,
122
119
  usesColorVar: result.usesColorVar,
120
+ usesSpacingVar: result.usesSpacingVar,
121
+ usesUnitVar: result.usesUnitVar,
123
122
  classes: [...result.classes],
124
123
  rawClassNames: [...result.rawClassNames],
125
124
  diagnostics: [...result.diagnostics],
@@ -136,6 +135,8 @@ function deserializeResult(result) {
136
135
  usesSzcn: result.usesSzcn,
137
136
  usesSzPart: result.usesSzPart,
138
137
  usesColorVar: result.usesColorVar,
138
+ usesSpacingVar: result.usesSpacingVar,
139
+ usesUnitVar: result.usesUnitVar,
139
140
  classes: new Set(result.classes),
140
141
  rawClassNames: new Set(result.rawClassNames),
141
142
  diagnostics: [...result.diagnostics],
@@ -152,7 +152,7 @@ function atomicWriteFileSync(file, content, options = {}) {
152
152
  const dir = path__namespace.dirname(file);
153
153
  const tmp = path__namespace.join(
154
154
  dir,
155
- `.tmp-${path__namespace.basename(file)}-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`
155
+ `.tmp-${path__namespace.basename(file)}-${process.pid}-${Date.now()}-${node_crypto.randomUUID()}`
156
156
  );
157
157
  fs__namespace.mkdirSync(dir, { recursive: true });
158
158
  fs__namespace.writeFileSync(tmp, content, "utf8");
@@ -262,7 +262,7 @@ function createLockMetadata(options) {
262
262
  return {
263
263
  version: 1,
264
264
  pid,
265
- token: options.token ?? node_crypto.createHash("sha256").update(`${pid}\0${now}\0${Math.random().toString(36)}`).digest("hex"),
265
+ token: options.token ?? node_crypto.randomUUID(),
266
266
  hostname: node_os.hostname(),
267
267
  root: path__namespace.resolve(options.root ?? process.cwd()),
268
268
  mode: options.mode ?? "development",