@csszyx/unplugin 0.10.11 → 0.11.0

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.
@@ -1,3 +1,4 @@
1
+ import { createHash } from 'node:crypto';
1
2
  import * as fs from 'node:fs';
2
3
  import { mkdirSync, writeFileSync } from 'node:fs';
3
4
  import { createRequire } from 'node:module';
@@ -5,7 +6,7 @@ import * as path from 'node:path';
5
6
  import { dirname } from 'node:path';
6
7
  import { performance } from 'node:perf_hooks';
7
8
  import { fileURLToPath } from 'node:url';
8
- import { scanGlobalVarUsages, sortStrings as sortStrings$1, ensureRustTransformAvailable, transformSourceCode, transformRust, transformOxc, transformRustBatch, transform, isRustTransformAvailable } from '@csszyx/compiler';
9
+ import { scanGlobalVarUsages, sortStrings as sortStrings$1, ensureRustTransformAvailable, transformSourceCode, transformRust, transformOxc, transformRustBatch, ASTBudgetExceededError, transform, isRustTransformAvailable } from '@csszyx/compiler';
9
10
  import { encode, compute_mangle_checksum } from '@csszyx/core';
10
11
  import { getNativePackageName } from '@csszyx/core/native';
11
12
  import { preprocess as preprocess$1 } from '@csszyx/svelte-adapter';
@@ -14,8 +15,8 @@ import { preprocess } from '@csszyx/vue-adapter';
14
15
  import { createUnplugin } from 'unplugin';
15
16
  import { mangleCSSSync } from '../css-mangler.mjs';
16
17
  import { s as sortStrings, e as escapeHtmlAttribute } from './unplugin.B1mblcm-.mjs';
17
- import { createHash } from 'node:crypto';
18
- import { r as resolveTransformCacheDir, c as createTransformCacheKey, a as readTransformCache, w as writeTransformCache, e as evictOldTransformCacheEntries, b as evictMemoryCacheToBudget } from './unplugin.CGqFVGlB.mjs';
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
20
  import postcss from 'postcss';
20
21
  import valueParser from 'postcss-value-parser';
21
22
 
@@ -802,19 +803,118 @@ function readRuntimeImportSymbols(source, clause) {
802
803
  }
803
804
  function findLocalImportSources(code) {
804
805
  const out = [];
805
- const staticImportRe = /import\s+(?!type\b)(?:\S(?:.*\S)?\s+from\s+)?['"]([^'"]+)['"]/g;
806
- const exportFromRe = /export\s+(?!type\b)\S(?:.*\S)?\s+from\s+['"]([^'"]+)['"]/g;
807
- const dynamicImportRe = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
808
- for (const re of [staticImportRe, exportFromRe, dynamicImportRe]) {
809
- for (const match of code.matchAll(re)) {
810
- const source = match[1];
811
- if (source.startsWith(".") || source.startsWith("/")) {
812
- out.push(source);
813
- }
806
+ for (const line of code.split("\n")) {
807
+ for (const spec of staticImportSpecifiers(line)) {
808
+ pushIfLocal(out, spec);
814
809
  }
815
810
  }
811
+ for (const line of code.split("\n")) {
812
+ for (const spec of exportFromSpecifiers(line)) {
813
+ pushIfLocal(out, spec);
814
+ }
815
+ }
816
+ for (const match of code.matchAll(/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g)) {
817
+ pushIfLocal(out, match[1]);
818
+ }
816
819
  return out;
817
820
  }
821
+ function pushIfLocal(out, spec) {
822
+ if (spec !== null && (spec.startsWith(".") || spec.startsWith("/"))) {
823
+ out.push(spec);
824
+ }
825
+ }
826
+ function staticImportSpecifiers(line) {
827
+ const specs = [];
828
+ for (const kw of allOccurrences(line, "import")) {
829
+ const afterKw = skipSpaces(line, kw + "import".length);
830
+ if (afterKw === kw + "import".length || startsWithWord(line, afterKw, "type")) {
831
+ continue;
832
+ }
833
+ if (line[afterKw] === '"' || line[afterKw] === "'") {
834
+ const spec2 = readQuoted(line, afterKw);
835
+ if (spec2 !== null) {
836
+ specs.push(spec2);
837
+ }
838
+ continue;
839
+ }
840
+ const spec = specifierAfterFrom(line, afterKw);
841
+ if (spec !== null) {
842
+ specs.push(spec);
843
+ }
844
+ }
845
+ return specs;
846
+ }
847
+ function exportFromSpecifiers(line) {
848
+ const specs = [];
849
+ for (const kw of allOccurrences(line, "export")) {
850
+ const afterKw = skipSpaces(line, kw + "export".length);
851
+ if (afterKw === kw + "export".length || startsWithWord(line, afterKw, "type")) {
852
+ continue;
853
+ }
854
+ const spec = specifierAfterFrom(line, afterKw);
855
+ if (spec !== null) {
856
+ specs.push(spec);
857
+ }
858
+ }
859
+ return specs;
860
+ }
861
+ function specifierAfterFrom(line, clauseStart) {
862
+ const fromAt = findKeywordFrom(line, clauseStart);
863
+ if (fromAt === -1) {
864
+ return null;
865
+ }
866
+ if (skipSpaces(line, clauseStart) >= fromAt) {
867
+ return null;
868
+ }
869
+ const afterFrom = skipSpaces(line, fromAt + "from".length);
870
+ return readQuoted(line, afterFrom);
871
+ }
872
+ function findKeywordFrom(line, at) {
873
+ let i = line.indexOf("from", at);
874
+ while (i !== -1) {
875
+ const before = line[i - 1];
876
+ const after = line[i + "from".length];
877
+ if (before !== void 0 && /\s/.test(before) && after !== void 0 && /\s/.test(after)) {
878
+ return i;
879
+ }
880
+ i = line.indexOf("from", i + 1);
881
+ }
882
+ return -1;
883
+ }
884
+ function readQuoted(line, at) {
885
+ const quote = line[at];
886
+ if (quote !== '"' && quote !== "'") {
887
+ return null;
888
+ }
889
+ const close = line.indexOf(quote, at + 1);
890
+ if (close <= at + 1) {
891
+ return null;
892
+ }
893
+ return line.slice(at + 1, close);
894
+ }
895
+ function allOccurrences(line, word) {
896
+ const positions = [];
897
+ let i = line.indexOf(word);
898
+ while (i !== -1) {
899
+ positions.push(i);
900
+ i = line.indexOf(word, i + 1);
901
+ }
902
+ return positions;
903
+ }
904
+ function startsWithWord(line, at, word) {
905
+ if (!line.startsWith(word, at)) {
906
+ return false;
907
+ }
908
+ const after = line[at + word.length];
909
+ return after === void 0 || !/\w/.test(after);
910
+ }
911
+ function skipSpaces(line, at) {
912
+ let i = at;
913
+ while (i < line.length && /\s/.test(line[i])) {
914
+ i++;
915
+ }
916
+ return i;
917
+ }
818
918
  function normalizeModuleId(id) {
819
919
  const clean = id.split("?")[0] ?? id;
820
920
  const cached = normalizedModuleIdCache.get(clean);
@@ -1113,10 +1213,9 @@ function parseThemeBlocks(cssContent) {
1113
1213
  };
1114
1214
  const stripped = stripLayerWrappers(cssContent);
1115
1215
  const blocks = extractThemeBlocks(stripped);
1116
- const propPattern = /--([a-z][a-z0-9-]*)(?:\s*:[^;]+)?;/g;
1117
1216
  for (const block of blocks) {
1118
- for (const match of block.matchAll(propPattern)) {
1119
- const categorized = categorizeProperty(match[1]);
1217
+ for (const name of scanCustomPropertyNames(block)) {
1218
+ const categorized = categorizeProperty(name);
1120
1219
  if (categorized) {
1121
1220
  result[categorized.category].add(categorized.token);
1122
1221
  }
@@ -1168,6 +1267,48 @@ function mergeThemes(themes) {
1168
1267
  function hasTokens(theme) {
1169
1268
  return Object.values(theme).some((arr) => arr.length > 0);
1170
1269
  }
1270
+ function scanCustomPropertyNames(block) {
1271
+ const names = [];
1272
+ let i = 0;
1273
+ while (i < block.length) {
1274
+ const dashes = block.indexOf("--", i);
1275
+ if (dashes === -1) {
1276
+ break;
1277
+ }
1278
+ let end = dashes + 2;
1279
+ if (end >= block.length || !/[a-z]/.test(block[end])) {
1280
+ i = dashes + 1;
1281
+ continue;
1282
+ }
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
+ }
1303
+ if (matchEnd === -1) {
1304
+ i = dashes + 1;
1305
+ continue;
1306
+ }
1307
+ names.push(name);
1308
+ i = matchEnd;
1309
+ }
1310
+ return names;
1311
+ }
1171
1312
 
1172
1313
  const GLOB_MAGIC_RE = /[*?[\]{}]/;
1173
1314
  const DEFAULT_IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", ".turbo", "dist", "build"]);
@@ -1289,16 +1430,29 @@ function safeJsonForScriptTag(value, prettyPrint = false) {
1289
1430
  const json = prettyPrint ? JSON.stringify(value, null, 2) : JSON.stringify(value);
1290
1431
  return json.replace(/</g, "\\u003C").replace(/>/g, "\\u003E").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
1291
1432
  }
1433
+ function injectHtmlOpeningAttr(html, attr) {
1434
+ const lower = html.toLowerCase();
1435
+ let from = 0;
1436
+ for (; ; ) {
1437
+ const start = lower.indexOf("<html", from);
1438
+ if (start === -1) {
1439
+ return html;
1440
+ }
1441
+ const after = html[start + 5];
1442
+ if (after === void 0 || after === ">" || after === "/" || /\s/.test(after)) {
1443
+ const close = html.indexOf(">", start + 5);
1444
+ if (close === -1) {
1445
+ return html;
1446
+ }
1447
+ const nameEnd = start + 5;
1448
+ return `${html.slice(0, nameEnd)}${attr}${html.slice(nameEnd)}`;
1449
+ }
1450
+ from = start + 5;
1451
+ }
1452
+ }
1292
1453
  function injectChecksum(html, checksum, minify = false) {
1293
1454
  const attrName = minify ? "data-sz-cs" : "data-sz-checksum";
1294
- const htmlTagPattern = /<html([^>]*)>/i;
1295
- const match = html.match(htmlTagPattern);
1296
- if (!match) {
1297
- return html;
1298
- }
1299
- const existingAttrs = match[1];
1300
- const checksumAttr = ` ${attrName}="${checksum}"`;
1301
- return html.replace(htmlTagPattern, `<html${checksumAttr}${existingAttrs}>`);
1455
+ return injectHtmlOpeningAttr(html, ` ${attrName}="${checksum}"`);
1302
1456
  }
1303
1457
  function injectMangleMapScript(html, mangleMap, options = {}) {
1304
1458
  const {
@@ -1327,14 +1481,7 @@ ${debugScript}`;
1327
1481
  function injectMangleMapAttribute(html, mangleMap, minify = false, varMangleMap = {}) {
1328
1482
  const attrName = minify ? "data-sz-m" : "data-sz-map";
1329
1483
  const jsonContent = JSON.stringify(createHydrationMangleMap(mangleMap, varMangleMap));
1330
- const htmlTagPattern = /<html([^>]*)>/i;
1331
- const match = html.match(htmlTagPattern);
1332
- if (!match) {
1333
- return html;
1334
- }
1335
- const existingAttrs = match[1];
1336
- const mapAttr = ` ${attrName}='${jsonContent}'`;
1337
- return html.replace(htmlTagPattern, `<html${mapAttr}${existingAttrs}>`);
1484
+ return injectHtmlOpeningAttr(html, ` ${attrName}='${jsonContent}'`);
1338
1485
  }
1339
1486
  function injectHydrationData(html, mangleMap, checksum, options = {}) {
1340
1487
  const { mode = "script", minify = false } = options;
@@ -1622,11 +1769,26 @@ const MAX_SAFELIST_CLASSES = 1e5;
1622
1769
  const DEFAULT_VAR_MANGLE_MAP_MAX_BYTES = 100 * 1024;
1623
1770
  const GLOBAL_VAR_ALIAS_MAP_OWNER = "\0csszyx:global-var-aliases";
1624
1771
  const DIRECTIVE_PROLOGUE_PREFIX_RE = /^((?:\s|\/\/[^\n]*\n|\/\*(?:[^*]|\*(?!\/))*\*\/)*)(['"]use (?:client|server)['"];?\s*)/;
1625
- const RUNTIME_HELPER_IMPORT_RE = {
1626
- _sz: /(?:import|export)\s+\{[^{}]*\b_sz\b[^{}]*\}\s*from\s*['"]@csszyx\/runtime['"]/,
1627
- _szMerge: /(?:import|export)\s+\{[^{}]*\b_szMerge\b[^{}]*\}\s*from\s*['"]@csszyx\/runtime['"]/,
1628
- __szColorVar: /(?:import|export)\s+\{[^{}]*\b__szColorVar\b[^{}]*\}\s*from\s*['"]@csszyx\/runtime['"]/
1629
- };
1772
+ function findOpeningTag(source, tag) {
1773
+ const lower = source.toLowerCase();
1774
+ const marker = `<${tag}`;
1775
+ let from = 0;
1776
+ for (; ; ) {
1777
+ const start = lower.indexOf(marker, from);
1778
+ if (start === -1) {
1779
+ return null;
1780
+ }
1781
+ const after = source[start + marker.length];
1782
+ if (after === void 0 || after === ">" || after === "/" || /\s/.test(after)) {
1783
+ const close = source.indexOf(">", start + marker.length);
1784
+ if (close !== -1) {
1785
+ return { start, close };
1786
+ }
1787
+ return null;
1788
+ }
1789
+ from = start + marker.length;
1790
+ }
1791
+ }
1630
1792
  let _hasWarnedTsConfig = false;
1631
1793
  let _hasWarnedTransformCacheVersion = false;
1632
1794
  let _hasWarnedNativeFallback = false;
@@ -1898,7 +2060,7 @@ function shouldTrackGlobalVarSources(config) {
1898
2060
  }
1899
2061
  function recordGlobalVarSourceFile(state, filename, code) {
1900
2062
  const normalizedFilename = normalizeSourceFilename(filename);
1901
- if (!/\.[tj]sx?(?:\?.*)?$/.test(normalizedFilename)) {
2063
+ if (!matchesScriptExtension(normalizedFilename, SCRIPT_ID_EXTENSIONS)) {
1902
2064
  return;
1903
2065
  }
1904
2066
  if (code === null) {
@@ -2245,6 +2407,11 @@ function findPackageVersionFromFile(file, fallback) {
2245
2407
  function normalizeSourceFilename(filename) {
2246
2408
  return filename.replace(/\\/g, "/");
2247
2409
  }
2410
+ const SCRIPT_ID_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
2411
+ const SOURCE_MODULE_EXTENSIONS = [...SCRIPT_ID_EXTENSIONS, ".cts", ".mts", ".cjs", ".mjs"];
2412
+ function matchesScriptExtension(id, extensions) {
2413
+ return extensions.some((ext) => id.endsWith(ext) || id.includes(`${ext}?`));
2414
+ }
2248
2415
  function insertRuntimeImport(code, importStmt) {
2249
2416
  const directiveMatch = code.match(DIRECTIVE_PROLOGUE_PREFIX_RE);
2250
2417
  if (!directiveMatch) {
@@ -2453,18 +2620,49 @@ function mangleCodeClassesSync(code, mangleMap) {
2453
2620
  }
2454
2621
  return `${sep}${ws}"${mangled.join(" ")}"`;
2455
2622
  });
2456
- result = result.replace(/\bszs:\s*\{([^{}]*)\}/g, (whole, body) => {
2457
- const mangledBody = body.replace(
2458
- /"((?:[^"\\]|\\.)*)"/g,
2459
- (_m, inner) => `"${mangleClassString(inner)}"`
2460
- ).replace(
2461
- /'((?:[^'\\]|\\.)*)'/g,
2462
- (_m, inner) => `'${mangleClassString(inner)}'`
2463
- );
2623
+ result = result.replace(/\bszsc:\s*\{([^{}]*)\}/g, (whole, body) => {
2624
+ const mangledBody = mangleQuotedStringLiterals(body, mangleClassString);
2464
2625
  return whole.replace(body, mangledBody);
2465
2626
  });
2466
2627
  return result;
2467
2628
  }
2629
+ function mangleQuotedStringLiterals(body, mangle) {
2630
+ let out = "";
2631
+ let i = 0;
2632
+ while (i < body.length) {
2633
+ const quote = body[i];
2634
+ if (quote !== '"' && quote !== "'") {
2635
+ out += quote;
2636
+ i++;
2637
+ continue;
2638
+ }
2639
+ let inner = "";
2640
+ let j = i + 1;
2641
+ let closed = false;
2642
+ while (j < body.length) {
2643
+ const ch = body[j];
2644
+ if (ch === "\\") {
2645
+ inner += ch + (body[j + 1] ?? "");
2646
+ j += 2;
2647
+ continue;
2648
+ }
2649
+ if (ch === quote) {
2650
+ closed = true;
2651
+ break;
2652
+ }
2653
+ inner += ch;
2654
+ j++;
2655
+ }
2656
+ if (!closed) {
2657
+ out += quote;
2658
+ i++;
2659
+ continue;
2660
+ }
2661
+ out += `${quote}${mangle(inner)}${quote}`;
2662
+ i = j + 1;
2663
+ }
2664
+ return out;
2665
+ }
2468
2666
  function assertGlobalVarMangleConfig(options) {
2469
2667
  const config = options.production?.mangleGlobalVars;
2470
2668
  const errors = validateGlobalVarMangleConfig(config);
@@ -2492,6 +2690,7 @@ function createCsszyxPlugins(options = {}) {
2492
2690
  let manglingEnabled = options.production?.mangle !== false;
2493
2691
  const mangleReserved = new Set(options.production?.mangleExclude ?? []);
2494
2692
  const astBudgetOverride = options.build?.astBudgetLimit;
2693
+ const prescanAstBudget = astBudgetOverride ?? 5e5;
2495
2694
  const cacheRequested = (options.build?.cache ?? DEFAULT_BUILD_CONFIG.cache) !== false;
2496
2695
  const cacheVersionsKnown = PLUGIN_VERSION !== UNKNOWN_PACKAGE_VERSION && COMPILER_VERSION !== UNKNOWN_PACKAGE_VERSION;
2497
2696
  const cacheEnabled = cacheRequested && cacheVersionsKnown;
@@ -2539,6 +2738,7 @@ function createCsszyxPlugins(options = {}) {
2539
2738
  let evictedCacheRoot = null;
2540
2739
  const transformMemoryCache = /* @__PURE__ */ new Map();
2541
2740
  let transformMemoryCacheCodeChars = 0;
2741
+ const prescanResultHandoff = /* @__PURE__ */ new Map();
2542
2742
  const state = {
2543
2743
  classes: /* @__PURE__ */ new Set(),
2544
2744
  parsedTheme: null,
@@ -2646,13 +2846,13 @@ function createCsszyxPlugins(options = {}) {
2646
2846
  return isHardIgnoredPath(id, compileSourceDirs);
2647
2847
  }
2648
2848
  function shouldProcessSource(id) {
2649
- return !isHardIgnored(id) && !isUserExcluded(id) && isUserIncluded(id) && (/\.([cm]?[tj]s|[tj]sx)(\?.*)?$/.test(id) || id.endsWith(".vue") || id.endsWith(".svelte"));
2849
+ return !isHardIgnored(id) && !isUserExcluded(id) && isUserIncluded(id) && (matchesScriptExtension(id, SOURCE_MODULE_EXTENSIONS) || id.endsWith(".vue") || id.endsWith(".svelte"));
2650
2850
  }
2651
2851
  function shouldProcessCss(id) {
2652
- return !isHardIgnored(id) && !isUserExcluded(id) && /\.css(\?.*)?$/.test(id);
2852
+ return !isHardIgnored(id) && !isUserExcluded(id) && matchesScriptExtension(id, [".css"]);
2653
2853
  }
2654
- function transformConfiguredSource(source, filename) {
2655
- const compilerOptions = createCompilerOptions();
2854
+ function transformConfiguredSource(source, filename, astBudget) {
2855
+ const compilerOptions = createCompilerOptions(astBudget);
2656
2856
  const effectiveFilename = normalizeSourceFilename(filename);
2657
2857
  const cacheRoot = resolveTransformCacheDir(state.rootDir, options.build?.cacheDir);
2658
2858
  if (cacheEnabled) {
@@ -2679,6 +2879,15 @@ function createCsszyxPlugins(options = {}) {
2679
2879
  rememberTransformCacheEntry(cacheKey.key, cached);
2680
2880
  return cached;
2681
2881
  }
2882
+ if (astBudget === void 0) {
2883
+ const handoff = prescanResultHandoff.get(effectiveFilename);
2884
+ if (handoff) {
2885
+ prescanResultHandoff.delete(effectiveFilename);
2886
+ if (handoff.inputSha256 === cacheKey.inputSha256) {
2887
+ return handoff.result;
2888
+ }
2889
+ }
2890
+ }
2682
2891
  }
2683
2892
  let result;
2684
2893
  if (parserMode === "babel") {
@@ -2709,9 +2918,9 @@ function createCsszyxPlugins(options = {}) {
2709
2918
  }
2710
2919
  return result;
2711
2920
  }
2712
- function createCompilerOptions() {
2921
+ function createCompilerOptions(astBudget = astBudgetOverride) {
2713
2922
  return {
2714
- astBudget: astBudgetOverride,
2923
+ astBudget,
2715
2924
  mangleVars: options.production?.mangleVars === true,
2716
2925
  mangleVarHoistMaxDepth: options.production?.mangleVarHoistMaxDepth,
2717
2926
  globalVarAliases: earlyGlobalVarAliasEntries.length > 0 ? earlyGlobalVarAliasEntries : void 0,
@@ -2726,7 +2935,10 @@ function createCsszyxPlugins(options = {}) {
2726
2935
  nativeIdentity: parserMode === "rust" ? resolveNativeCacheIdentity() : void 0,
2727
2936
  parserMode,
2728
2937
  producer: parserMode,
2729
- astBudget: astBudgetOverride,
2938
+ // The EFFECTIVE budget, not the raw override: prescan-lane results
2939
+ // (larger budget) must not be served to the transform hook, whose
2940
+ // smaller budget could not have produced them (and vice versa).
2941
+ astBudget: compilerOptions.astBudget,
2730
2942
  mangleVars: compilerOptions.mangleVars,
2731
2943
  mangleVarHoistMaxDepth: compilerOptions.mangleVarHoistMaxDepth,
2732
2944
  globalVarAliases: normalizeGlobalVarAliasesForCache(compilerOptions.globalVarAliases),
@@ -2738,7 +2950,7 @@ function createCsszyxPlugins(options = {}) {
2738
2950
  if (parserMode !== "rust" || files.length <= 1) {
2739
2951
  return transformPrescanSourcesIndividually(files);
2740
2952
  }
2741
- const compilerOptions = createCompilerOptions();
2953
+ const compilerOptions = createCompilerOptions(prescanAstBudget);
2742
2954
  const cacheRoot = resolveTransformCacheDir(state.rootDir, options.build?.cacheDir);
2743
2955
  const results = /* @__PURE__ */ new Map();
2744
2956
  const misses = [];
@@ -2808,7 +3020,11 @@ function createCsszyxPlugins(options = {}) {
2808
3020
  try {
2809
3021
  results.set(
2810
3022
  miss.filePath,
2811
- transformConfiguredSource(miss.content, miss.effectiveFilename)
3023
+ transformConfiguredSource(
3024
+ miss.content,
3025
+ miss.effectiveFilename,
3026
+ prescanAstBudget
3027
+ )
2812
3028
  );
2813
3029
  } catch {
2814
3030
  }
@@ -2819,15 +3035,28 @@ function createCsszyxPlugins(options = {}) {
2819
3035
  return result ? { filePath: file.filePath, result } : null;
2820
3036
  }).filter((entry) => entry !== null);
2821
3037
  }
3038
+ function warnPrescanBudgetSkip(filePath) {
3039
+ console.warn(
3040
+ `[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.`
3041
+ );
3042
+ }
2822
3043
  function transformPrescanSourcesIndividually(files) {
2823
3044
  const results = [];
2824
3045
  for (const file of files) {
2825
3046
  try {
2826
3047
  results.push({
2827
3048
  filePath: file.filePath,
2828
- result: transformConfiguredSource(file.content, file.filePath)
3049
+ result: transformConfiguredSource(
3050
+ file.content,
3051
+ file.filePath,
3052
+ prescanAstBudget
3053
+ )
2829
3054
  });
2830
3055
  } catch (err) {
3056
+ if (err instanceof ASTBudgetExceededError) {
3057
+ warnPrescanBudgetSkip(file.filePath);
3058
+ continue;
3059
+ }
2831
3060
  console.warn(
2832
3061
  `[csszyx] prescan skipped ${file.filePath}: transform failed, so none of its classes reached the safelist. ${err instanceof Error ? err.message : String(err)}`
2833
3062
  );
@@ -2955,7 +3184,23 @@ function createCsszyxPlugins(options = {}) {
2955
3184
  }
2956
3185
  scanDir(sourceDir);
2957
3186
  }
3187
+ const prescanContentByPath = new Map(
3188
+ prescanSources.map((file) => [file.filePath, file.content])
3189
+ );
2958
3190
  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
+ }
2959
3204
  if (result.classes.size === 0 && result.rawClassNames.size === 0 && result.diagnostics.some((d) => d.includes("[csszyx] parse error in "))) {
2960
3205
  console.warn(
2961
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).`
@@ -3211,10 +3456,10 @@ function createCsszyxPlugins(options = {}) {
3211
3456
  if (shouldProcessSource(id)) {
3212
3457
  trackGlobalVarSourceFile(id, code);
3213
3458
  }
3214
- if (/\.[tj]sx?(\?.*)?$/.test(id)) {
3459
+ if (matchesScriptExtension(id, SCRIPT_ID_EXTENSIONS)) {
3215
3460
  assertNoRSCBoundaryViolation(code, id);
3216
3461
  }
3217
- if (/\.css(\?.*)?$/.test(id)) {
3462
+ if (matchesScriptExtension(id, [".css"])) {
3218
3463
  state.sawAnyCss = true;
3219
3464
  if (cssImportsTailwind(code)) {
3220
3465
  state.sawTailwindEntry = true;
@@ -3238,6 +3483,8 @@ function createCsszyxPlugins(options = {}) {
3238
3483
  let transformedCode = code;
3239
3484
  let usesRuntime = false;
3240
3485
  let usesMerge = false;
3486
+ let usesSzcn = false;
3487
+ let usesSzPart = false;
3241
3488
  let usesColorVar = false;
3242
3489
  let transformed = false;
3243
3490
  let szClasses;
@@ -3266,6 +3513,8 @@ function createCsszyxPlugins(options = {}) {
3266
3513
  transformedCode = result.code;
3267
3514
  usesRuntime = result.usesRuntime;
3268
3515
  usesMerge = result.usesMerge;
3516
+ usesSzcn = result.usesSzcn;
3517
+ usesSzPart = result.usesSzPart;
3269
3518
  usesColorVar = result.usesColorVar;
3270
3519
  transformed = result.transformed;
3271
3520
  szClasses = result.classes;
@@ -3274,12 +3523,17 @@ function createCsszyxPlugins(options = {}) {
3274
3523
  for (const msg of result.diagnostics) {
3275
3524
  if (msg.includes("unresolvable sz spread")) {
3276
3525
  state.spreadWarnings.add(`${id}
3526
+ ${msg}`);
3527
+ } else if (msg.includes("AST budget exceeded")) {
3528
+ console.warn(`[csszyx] ${id}
3277
3529
  ${msg}`);
3278
3530
  }
3279
3531
  }
3280
3532
  if (!quiet && result.diagnostics.length > 0 && process.env.NODE_ENV !== "production") {
3281
3533
  for (const msg of result.diagnostics) {
3282
- if (msg.includes("unresolvable sz spread")) continue;
3534
+ if (msg.includes("unresolvable sz spread") || msg.includes("AST budget exceeded")) {
3535
+ continue;
3536
+ }
3283
3537
  this.warn(`[csszyx] ${id}
3284
3538
  ${msg}`);
3285
3539
  }
@@ -3294,16 +3548,14 @@ function createCsszyxPlugins(options = {}) {
3294
3548
  }
3295
3549
  if (transformedCode.includes("<html") && /(?:layout|Root|Document|app)\.tsx?$/i.test(id)) {
3296
3550
  const attrName = options.production?.minify ? "data-sz-cs" : "data-sz-checksum";
3297
- transformedCode = transformedCode.replace(
3298
- /<html([^>]*)>/i,
3299
- `<html$1 ${attrName}="${CHECKSUM_PLACEHOLDER}">`
3300
- );
3551
+ const htmlTag = findOpeningTag(transformedCode, "html");
3552
+ if (htmlTag) {
3553
+ transformedCode = `${transformedCode.slice(0, htmlTag.close)} ${attrName}="${CHECKSUM_PLACEHOLDER}"${transformedCode.slice(htmlTag.close)}`;
3554
+ }
3301
3555
  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})}}})()\`}} />`;
3302
- if (transformedCode.includes("<body")) {
3303
- transformedCode = transformedCode.replace(
3304
- /(<body[^>]*>)/i,
3305
- `$1${debugScript}`
3306
- );
3556
+ const bodyTag = findOpeningTag(transformedCode, "body");
3557
+ if (bodyTag) {
3558
+ transformedCode = `${transformedCode.slice(0, bodyTag.close + 1)}${debugScript}${transformedCode.slice(bodyTag.close + 1)}`;
3307
3559
  }
3308
3560
  transformed = true;
3309
3561
  }
@@ -3315,21 +3567,23 @@ function createCsszyxPlugins(options = {}) {
3315
3567
  if (usesMerge) {
3316
3568
  imports.push("_szMerge");
3317
3569
  }
3570
+ if (usesSzcn) {
3571
+ imports.push("_szcn");
3572
+ }
3573
+ if (usesSzPart) {
3574
+ imports.push("_szPart");
3575
+ }
3318
3576
  if (usesColorVar) {
3319
3577
  imports.push("__szColorVar");
3320
3578
  }
3321
3579
  const hasRuntimeImport = imports.length > 0 && transformedCode.includes("@csszyx/runtime");
3322
- const needed = hasRuntimeImport ? imports.filter(
3323
- (name) => !RUNTIME_HELPER_IMPORT_RE[name]?.test(transformedCode)
3324
- ) : imports;
3580
+ const needed = hasRuntimeImport ? imports.filter((name) => !importsRuntimeHelper(transformedCode, name)) : imports;
3325
3581
  if (needed.length > 0) {
3326
- const existingImport = transformedCode.match(
3327
- /^(import\s*\{[^}]*)\}\s*from\s*'@csszyx\/runtime'/m
3328
- );
3582
+ const existingImport = findRuntimeImportClause(transformedCode);
3329
3583
  if (existingImport) {
3330
3584
  transformedCode = transformedCode.replace(
3331
- existingImport[0],
3332
- `${existingImport[1]}, ${needed.join(", ")} } from '@csszyx/runtime'`
3585
+ existingImport.statement,
3586
+ `${existingImport.prefixWithBody}, ${needed.join(", ")} } from '@csszyx/runtime'`
3333
3587
  );
3334
3588
  } else {
3335
3589
  const importStmt = `import { ${needed.join(", ")} } from '@csszyx/runtime';
@@ -3339,12 +3593,12 @@ function createCsszyxPlugins(options = {}) {
3339
3593
  transformed = true;
3340
3594
  }
3341
3595
  }
3342
- if (/\bszcn\s*\(/.test(code) && !transformedCode.includes(THEME_GROUPS_VIRTUAL_ID) && shouldProcessSource(id)) {
3596
+ if ((usesSzcn || /\bszcn\s*\(/.test(code)) && !transformedCode.includes(THEME_GROUPS_VIRTUAL_ID) && shouldProcessSource(id)) {
3343
3597
  transformedCode = `import '${THEME_GROUPS_VIRTUAL_ID}';
3344
3598
  ${transformedCode}`;
3345
3599
  transformed = true;
3346
3600
  }
3347
- if (/\.[tj]sx?(\?.*)?$/.test(id)) {
3601
+ if (matchesScriptExtension(id, SCRIPT_ID_EXTENSIONS)) {
3348
3602
  assertNoRSCBoundaryViolation(transformedCode, id);
3349
3603
  const record = createRSCModuleRecord(transformedCode, id);
3350
3604
  state.rscModules.set(record.id, record);
@@ -3402,6 +3656,7 @@ ${transformedCode}`;
3402
3656
  emitWarning(`[csszyx] ${warning}`);
3403
3657
  }
3404
3658
  state.spreadWarnings.clear();
3659
+ prescanResultHandoff.clear();
3405
3660
  if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
3406
3661
  globalThis.__csszyx_ssr_mangle_map = state.mangleMap;
3407
3662
  }
@@ -3461,6 +3716,7 @@ ${transformedCode}`;
3461
3716
  * @param ctx - HMR context containing the changed file
3462
3717
  */
3463
3718
  handleHotUpdate(ctx) {
3719
+ prescanResultHandoff.clear();
3464
3720
  const scanCss = options.build?.scanCss;
3465
3721
  if (scanCss) {
3466
3722
  const root = ctx.server.config.root || process.cwd();
@@ -3895,4 +4151,4 @@ const esbuildPlugin = (options = {}) => {
3895
4151
  };
3896
4152
  };
3897
4153
 
3898
- export { isRSCServerModule as A, isTailwindReservedGlobalVar as B, mangleCodeClassesSync as C, mangleHybridHazardMessage as D, mergeThemes as E, missingTailwindEntryMessage as F, normalizeGlobalVarAliasesForCache as G, parseThemeBlocks as H, planGlobalVarAliases as I, readGlobalVarScanCache as J, recordGlobalVarSourceFile as K, resolveCompileSourceDirs as L, resolveGlobalVarScanCacheDir as M, resolveNativeCacheIdentity as N, rewriteGlobalVarCssAliases as O, rollupPlugin as P, scanGlobalVarCss as Q, shouldEmitWarning as R, shouldTrackGlobalVarSources as S, shouldWarnMissingTailwindEntry as T, shouldWarnUnscopedMonorepo as U, skippedSzFilesMessage as V, unscopedMonorepoMessage as W, validateGlobalVarAliasInputs as X, vitePlugin as Y, webpackPlugin as Z, writeGlobalVarScanCache as _, appendTailwindSourceDirective as a, assertNoRSCBoundaryViolation as b, assertNoRSCGraphViolation as c, collectMangleHybridHazards as d, computeSafelistRelPath as e, createGlobalVarAliasValidationOptions as f, createGlobalVarMapAssetSource as g, createGlobalVarScanCacheKey as h, createRSCModuleRecord as i, cssHasContentScope as j, cssImportsTailwind as k, deleteRSCModuleRecord as l, esbuildPlugin as m, extractGlobalVarAliasesForManifest as n, fileMayContainSafelistableSz as o, findRSCBoundaryViolation as p, findRSCGraphViolation as q, hasInjectableTailwindCandidate as r, hasTokens as s, hasUseClientDirective as t, unplugin as u, hasUseServerDirective as v, isCompileSourceOptedIn as w, isHardIgnoredPath as x, isMonorepoPackage as y, isPackagesSkippedSource as z };
4154
+ export { webpackPlugin as $, isPackagesSkippedSource as A, isRSCServerModule as B, isTailwindReservedGlobalVar as C, mangleCodeClassesSync as D, mangleHybridHazardMessage as E, mergeThemes as F, missingTailwindEntryMessage as G, normalizeGlobalVarAliasesForCache as H, parseThemeBlocks as I, planGlobalVarAliases as J, readGlobalVarScanCache as K, recordGlobalVarSourceFile as L, resolveCompileSourceDirs as M, resolveGlobalVarScanCacheDir as N, resolveNativeCacheIdentity as O, rewriteGlobalVarCssAliases as P, rollupPlugin as Q, scanCustomPropertyNames as R, scanGlobalVarCss as S, shouldEmitWarning as T, shouldTrackGlobalVarSources as U, shouldWarnMissingTailwindEntry as V, shouldWarnUnscopedMonorepo as W, skippedSzFilesMessage as X, unscopedMonorepoMessage as Y, validateGlobalVarAliasInputs as Z, vitePlugin as _, appendTailwindSourceDirective as a, writeGlobalVarScanCache as a0, assertNoRSCBoundaryViolation as b, assertNoRSCGraphViolation as c, collectMangleHybridHazards as d, computeSafelistRelPath as e, createGlobalVarAliasValidationOptions as f, createGlobalVarMapAssetSource as g, createGlobalVarScanCacheKey as h, createRSCModuleRecord as i, cssHasContentScope as j, cssImportsTailwind as k, deleteRSCModuleRecord as l, esbuildPlugin as m, extractGlobalVarAliasesForManifest as n, fileMayContainSafelistableSz as o, findLocalImportSources as p, findRSCBoundaryViolation as q, findRSCGraphViolation as r, hasInjectableTailwindCandidate as s, hasTokens as t, unplugin as u, hasUseClientDirective as v, hasUseServerDirective as w, isCompileSourceOptedIn as x, isHardIgnoredPath as y, isMonorepoPackage as z };