@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,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const node_crypto = require('node:crypto');
3
4
  const fs = require('node:fs');
4
5
  const node_module = require('node:module');
5
6
  const path = require('node:path');
@@ -14,8 +15,8 @@ const vueAdapter = require('@csszyx/vue-adapter');
14
15
  const unplugin$1 = require('unplugin');
15
16
  const cssMangler = require('../css-mangler.cjs');
16
17
  const htmlEscape = require('./unplugin.BCwRIUs_.cjs');
17
- const node_crypto = require('node:crypto');
18
- const transformCache = require('./unplugin.D32Rb29j.cjs');
18
+ const runtimeImportScan = require('./unplugin.Rov-j_Wm.cjs');
19
+ const transformCache = require('./unplugin.D0N9bprz.cjs');
19
20
  const postcss = require('postcss');
20
21
  const valueParser = require('postcss-value-parser');
21
22
 
@@ -822,19 +823,118 @@ function readRuntimeImportSymbols(source, clause) {
822
823
  }
823
824
  function findLocalImportSources(code) {
824
825
  const out = [];
825
- const staticImportRe = /import\s+(?!type\b)(?:\S(?:.*\S)?\s+from\s+)?['"]([^'"]+)['"]/g;
826
- const exportFromRe = /export\s+(?!type\b)\S(?:.*\S)?\s+from\s+['"]([^'"]+)['"]/g;
827
- const dynamicImportRe = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
828
- for (const re of [staticImportRe, exportFromRe, dynamicImportRe]) {
829
- for (const match of code.matchAll(re)) {
830
- const source = match[1];
831
- if (source.startsWith(".") || source.startsWith("/")) {
832
- out.push(source);
833
- }
826
+ for (const line of code.split("\n")) {
827
+ for (const spec of staticImportSpecifiers(line)) {
828
+ pushIfLocal(out, spec);
834
829
  }
835
830
  }
831
+ for (const line of code.split("\n")) {
832
+ for (const spec of exportFromSpecifiers(line)) {
833
+ pushIfLocal(out, spec);
834
+ }
835
+ }
836
+ for (const match of code.matchAll(/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g)) {
837
+ pushIfLocal(out, match[1]);
838
+ }
836
839
  return out;
837
840
  }
841
+ function pushIfLocal(out, spec) {
842
+ if (spec !== null && (spec.startsWith(".") || spec.startsWith("/"))) {
843
+ out.push(spec);
844
+ }
845
+ }
846
+ function staticImportSpecifiers(line) {
847
+ const specs = [];
848
+ for (const kw of allOccurrences(line, "import")) {
849
+ const afterKw = skipSpaces(line, kw + "import".length);
850
+ if (afterKw === kw + "import".length || startsWithWord(line, afterKw, "type")) {
851
+ continue;
852
+ }
853
+ if (line[afterKw] === '"' || line[afterKw] === "'") {
854
+ const spec2 = readQuoted(line, afterKw);
855
+ if (spec2 !== null) {
856
+ specs.push(spec2);
857
+ }
858
+ continue;
859
+ }
860
+ const spec = specifierAfterFrom(line, afterKw);
861
+ if (spec !== null) {
862
+ specs.push(spec);
863
+ }
864
+ }
865
+ return specs;
866
+ }
867
+ function exportFromSpecifiers(line) {
868
+ const specs = [];
869
+ for (const kw of allOccurrences(line, "export")) {
870
+ const afterKw = skipSpaces(line, kw + "export".length);
871
+ if (afterKw === kw + "export".length || startsWithWord(line, afterKw, "type")) {
872
+ continue;
873
+ }
874
+ const spec = specifierAfterFrom(line, afterKw);
875
+ if (spec !== null) {
876
+ specs.push(spec);
877
+ }
878
+ }
879
+ return specs;
880
+ }
881
+ function specifierAfterFrom(line, clauseStart) {
882
+ const fromAt = findKeywordFrom(line, clauseStart);
883
+ if (fromAt === -1) {
884
+ return null;
885
+ }
886
+ if (skipSpaces(line, clauseStart) >= fromAt) {
887
+ return null;
888
+ }
889
+ const afterFrom = skipSpaces(line, fromAt + "from".length);
890
+ return readQuoted(line, afterFrom);
891
+ }
892
+ function findKeywordFrom(line, at) {
893
+ let i = line.indexOf("from", at);
894
+ while (i !== -1) {
895
+ const before = line[i - 1];
896
+ const after = line[i + "from".length];
897
+ if (before !== void 0 && /\s/.test(before) && after !== void 0 && /\s/.test(after)) {
898
+ return i;
899
+ }
900
+ i = line.indexOf("from", i + 1);
901
+ }
902
+ return -1;
903
+ }
904
+ function readQuoted(line, at) {
905
+ const quote = line[at];
906
+ if (quote !== '"' && quote !== "'") {
907
+ return null;
908
+ }
909
+ const close = line.indexOf(quote, at + 1);
910
+ if (close <= at + 1) {
911
+ return null;
912
+ }
913
+ return line.slice(at + 1, close);
914
+ }
915
+ function allOccurrences(line, word) {
916
+ const positions = [];
917
+ let i = line.indexOf(word);
918
+ while (i !== -1) {
919
+ positions.push(i);
920
+ i = line.indexOf(word, i + 1);
921
+ }
922
+ return positions;
923
+ }
924
+ function startsWithWord(line, at, word) {
925
+ if (!line.startsWith(word, at)) {
926
+ return false;
927
+ }
928
+ const after = line[at + word.length];
929
+ return after === void 0 || !/\w/.test(after);
930
+ }
931
+ function skipSpaces(line, at) {
932
+ let i = at;
933
+ while (i < line.length && /\s/.test(line[i])) {
934
+ i++;
935
+ }
936
+ return i;
937
+ }
838
938
  function normalizeModuleId(id) {
839
939
  const clean = id.split("?")[0] ?? id;
840
940
  const cached = normalizedModuleIdCache.get(clean);
@@ -1133,10 +1233,9 @@ function parseThemeBlocks(cssContent) {
1133
1233
  };
1134
1234
  const stripped = stripLayerWrappers(cssContent);
1135
1235
  const blocks = extractThemeBlocks(stripped);
1136
- const propPattern = /--([a-z][a-z0-9-]*)(?:\s*:[^;]+)?;/g;
1137
1236
  for (const block of blocks) {
1138
- for (const match of block.matchAll(propPattern)) {
1139
- const categorized = categorizeProperty(match[1]);
1237
+ for (const name of scanCustomPropertyNames(block)) {
1238
+ const categorized = categorizeProperty(name);
1140
1239
  if (categorized) {
1141
1240
  result[categorized.category].add(categorized.token);
1142
1241
  }
@@ -1188,6 +1287,48 @@ function mergeThemes(themes) {
1188
1287
  function hasTokens(theme) {
1189
1288
  return Object.values(theme).some((arr) => arr.length > 0);
1190
1289
  }
1290
+ function scanCustomPropertyNames(block) {
1291
+ const names = [];
1292
+ let i = 0;
1293
+ while (i < block.length) {
1294
+ const dashes = block.indexOf("--", i);
1295
+ if (dashes === -1) {
1296
+ break;
1297
+ }
1298
+ let end = dashes + 2;
1299
+ if (end >= block.length || !/[a-z]/.test(block[end])) {
1300
+ i = dashes + 1;
1301
+ continue;
1302
+ }
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
+ }
1323
+ if (matchEnd === -1) {
1324
+ i = dashes + 1;
1325
+ continue;
1326
+ }
1327
+ names.push(name);
1328
+ i = matchEnd;
1329
+ }
1330
+ return names;
1331
+ }
1191
1332
 
1192
1333
  const GLOB_MAGIC_RE = /[*?[\]{}]/;
1193
1334
  const DEFAULT_IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", ".turbo", "dist", "build"]);
@@ -1309,16 +1450,29 @@ function safeJsonForScriptTag(value, prettyPrint = false) {
1309
1450
  const json = prettyPrint ? JSON.stringify(value, null, 2) : JSON.stringify(value);
1310
1451
  return json.replace(/</g, "\\u003C").replace(/>/g, "\\u003E").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
1311
1452
  }
1453
+ function injectHtmlOpeningAttr(html, attr) {
1454
+ const lower = html.toLowerCase();
1455
+ let from = 0;
1456
+ for (; ; ) {
1457
+ const start = lower.indexOf("<html", from);
1458
+ if (start === -1) {
1459
+ return html;
1460
+ }
1461
+ const after = html[start + 5];
1462
+ if (after === void 0 || after === ">" || after === "/" || /\s/.test(after)) {
1463
+ const close = html.indexOf(">", start + 5);
1464
+ if (close === -1) {
1465
+ return html;
1466
+ }
1467
+ const nameEnd = start + 5;
1468
+ return `${html.slice(0, nameEnd)}${attr}${html.slice(nameEnd)}`;
1469
+ }
1470
+ from = start + 5;
1471
+ }
1472
+ }
1312
1473
  function injectChecksum(html, checksum, minify = false) {
1313
1474
  const attrName = minify ? "data-sz-cs" : "data-sz-checksum";
1314
- const htmlTagPattern = /<html([^>]*)>/i;
1315
- const match = html.match(htmlTagPattern);
1316
- if (!match) {
1317
- return html;
1318
- }
1319
- const existingAttrs = match[1];
1320
- const checksumAttr = ` ${attrName}="${checksum}"`;
1321
- return html.replace(htmlTagPattern, `<html${checksumAttr}${existingAttrs}>`);
1475
+ return injectHtmlOpeningAttr(html, ` ${attrName}="${checksum}"`);
1322
1476
  }
1323
1477
  function injectMangleMapScript(html, mangleMap, options = {}) {
1324
1478
  const {
@@ -1347,14 +1501,7 @@ ${debugScript}`;
1347
1501
  function injectMangleMapAttribute(html, mangleMap, minify = false, varMangleMap = {}) {
1348
1502
  const attrName = minify ? "data-sz-m" : "data-sz-map";
1349
1503
  const jsonContent = JSON.stringify(createHydrationMangleMap(mangleMap, varMangleMap));
1350
- const htmlTagPattern = /<html([^>]*)>/i;
1351
- const match = html.match(htmlTagPattern);
1352
- if (!match) {
1353
- return html;
1354
- }
1355
- const existingAttrs = match[1];
1356
- const mapAttr = ` ${attrName}='${jsonContent}'`;
1357
- return html.replace(htmlTagPattern, `<html${mapAttr}${existingAttrs}>`);
1504
+ return injectHtmlOpeningAttr(html, ` ${attrName}='${jsonContent}'`);
1358
1505
  }
1359
1506
  function injectHydrationData(html, mangleMap, checksum, options = {}) {
1360
1507
  const { mode = "script", minify = false } = options;
@@ -1642,19 +1789,34 @@ const MAX_SAFELIST_CLASSES = 1e5;
1642
1789
  const DEFAULT_VAR_MANGLE_MAP_MAX_BYTES = 100 * 1024;
1643
1790
  const GLOBAL_VAR_ALIAS_MAP_OWNER = "\0csszyx:global-var-aliases";
1644
1791
  const DIRECTIVE_PROLOGUE_PREFIX_RE = /^((?:\s|\/\/[^\n]*\n|\/\*(?:[^*]|\*(?!\/))*\*\/)*)(['"]use (?:client|server)['"];?\s*)/;
1645
- const RUNTIME_HELPER_IMPORT_RE = {
1646
- _sz: /(?:import|export)\s+\{[^{}]*\b_sz\b[^{}]*\}\s*from\s*['"]@csszyx\/runtime['"]/,
1647
- _szMerge: /(?:import|export)\s+\{[^{}]*\b_szMerge\b[^{}]*\}\s*from\s*['"]@csszyx\/runtime['"]/,
1648
- __szColorVar: /(?:import|export)\s+\{[^{}]*\b__szColorVar\b[^{}]*\}\s*from\s*['"]@csszyx\/runtime['"]/
1649
- };
1792
+ function findOpeningTag(source, tag) {
1793
+ const lower = source.toLowerCase();
1794
+ const marker = `<${tag}`;
1795
+ let from = 0;
1796
+ for (; ; ) {
1797
+ const start = lower.indexOf(marker, from);
1798
+ if (start === -1) {
1799
+ return null;
1800
+ }
1801
+ const after = source[start + marker.length];
1802
+ if (after === void 0 || after === ">" || after === "/" || /\s/.test(after)) {
1803
+ const close = source.indexOf(">", start + marker.length);
1804
+ if (close !== -1) {
1805
+ return { start, close };
1806
+ }
1807
+ return null;
1808
+ }
1809
+ from = start + marker.length;
1810
+ }
1811
+ }
1650
1812
  let _hasWarnedTsConfig = false;
1651
1813
  let _hasWarnedTransformCacheVersion = false;
1652
1814
  let _hasWarnedNativeFallback = false;
1653
1815
  const _loggedActiveParsers = /* @__PURE__ */ new Set();
1654
1816
  const _babelFallbackFiles = /* @__PURE__ */ new Set();
1655
- 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.q0k37crS.cjs', document.baseURI).href)));
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)));
1656
1818
  const PLUGIN_VERSION = findPackageVersionFromFile(
1657
- node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/unplugin.q0k37crS.cjs', document.baseURI).href))),
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))),
1658
1820
  UNKNOWN_PACKAGE_VERSION
1659
1821
  );
1660
1822
  const COMPILER_VERSION = findPackageVersionFromModule("@csszyx/compiler", UNKNOWN_PACKAGE_VERSION);
@@ -1918,7 +2080,7 @@ function shouldTrackGlobalVarSources(config) {
1918
2080
  }
1919
2081
  function recordGlobalVarSourceFile(state, filename, code) {
1920
2082
  const normalizedFilename = normalizeSourceFilename(filename);
1921
- if (!/\.[tj]sx?(?:\?.*)?$/.test(normalizedFilename)) {
2083
+ if (!matchesScriptExtension(normalizedFilename, SCRIPT_ID_EXTENSIONS)) {
1922
2084
  return;
1923
2085
  }
1924
2086
  if (code === null) {
@@ -2265,6 +2427,11 @@ function findPackageVersionFromFile(file, fallback) {
2265
2427
  function normalizeSourceFilename(filename) {
2266
2428
  return filename.replace(/\\/g, "/");
2267
2429
  }
2430
+ const SCRIPT_ID_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
2431
+ const SOURCE_MODULE_EXTENSIONS = [...SCRIPT_ID_EXTENSIONS, ".cts", ".mts", ".cjs", ".mjs"];
2432
+ function matchesScriptExtension(id, extensions) {
2433
+ return extensions.some((ext) => id.endsWith(ext) || id.includes(`${ext}?`));
2434
+ }
2268
2435
  function insertRuntimeImport(code, importStmt) {
2269
2436
  const directiveMatch = code.match(DIRECTIVE_PROLOGUE_PREFIX_RE);
2270
2437
  if (!directiveMatch) {
@@ -2473,18 +2640,49 @@ function mangleCodeClassesSync(code, mangleMap) {
2473
2640
  }
2474
2641
  return `${sep}${ws}"${mangled.join(" ")}"`;
2475
2642
  });
2476
- result = result.replace(/\bszs:\s*\{([^{}]*)\}/g, (whole, body) => {
2477
- const mangledBody = body.replace(
2478
- /"((?:[^"\\]|\\.)*)"/g,
2479
- (_m, inner) => `"${mangleClassString(inner)}"`
2480
- ).replace(
2481
- /'((?:[^'\\]|\\.)*)'/g,
2482
- (_m, inner) => `'${mangleClassString(inner)}'`
2483
- );
2643
+ result = result.replace(/\bszsc:\s*\{([^{}]*)\}/g, (whole, body) => {
2644
+ const mangledBody = mangleQuotedStringLiterals(body, mangleClassString);
2484
2645
  return whole.replace(body, mangledBody);
2485
2646
  });
2486
2647
  return result;
2487
2648
  }
2649
+ function mangleQuotedStringLiterals(body, mangle) {
2650
+ let out = "";
2651
+ let i = 0;
2652
+ while (i < body.length) {
2653
+ const quote = body[i];
2654
+ if (quote !== '"' && quote !== "'") {
2655
+ out += quote;
2656
+ i++;
2657
+ continue;
2658
+ }
2659
+ let inner = "";
2660
+ let j = i + 1;
2661
+ let closed = false;
2662
+ while (j < body.length) {
2663
+ const ch = body[j];
2664
+ if (ch === "\\") {
2665
+ inner += ch + (body[j + 1] ?? "");
2666
+ j += 2;
2667
+ continue;
2668
+ }
2669
+ if (ch === quote) {
2670
+ closed = true;
2671
+ break;
2672
+ }
2673
+ inner += ch;
2674
+ j++;
2675
+ }
2676
+ if (!closed) {
2677
+ out += quote;
2678
+ i++;
2679
+ continue;
2680
+ }
2681
+ out += `${quote}${mangle(inner)}${quote}`;
2682
+ i = j + 1;
2683
+ }
2684
+ return out;
2685
+ }
2488
2686
  function assertGlobalVarMangleConfig(options) {
2489
2687
  const config = options.production?.mangleGlobalVars;
2490
2688
  const errors = types.validateGlobalVarMangleConfig(config);
@@ -2512,6 +2710,7 @@ function createCsszyxPlugins(options = {}) {
2512
2710
  let manglingEnabled = options.production?.mangle !== false;
2513
2711
  const mangleReserved = new Set(options.production?.mangleExclude ?? []);
2514
2712
  const astBudgetOverride = options.build?.astBudgetLimit;
2713
+ const prescanAstBudget = astBudgetOverride ?? 5e5;
2515
2714
  const cacheRequested = (options.build?.cache ?? types.DEFAULT_BUILD_CONFIG.cache) !== false;
2516
2715
  const cacheVersionsKnown = PLUGIN_VERSION !== UNKNOWN_PACKAGE_VERSION && COMPILER_VERSION !== UNKNOWN_PACKAGE_VERSION;
2517
2716
  const cacheEnabled = cacheRequested && cacheVersionsKnown;
@@ -2559,6 +2758,7 @@ function createCsszyxPlugins(options = {}) {
2559
2758
  let evictedCacheRoot = null;
2560
2759
  const transformMemoryCache = /* @__PURE__ */ new Map();
2561
2760
  let transformMemoryCacheCodeChars = 0;
2761
+ const prescanResultHandoff = /* @__PURE__ */ new Map();
2562
2762
  const state = {
2563
2763
  classes: /* @__PURE__ */ new Set(),
2564
2764
  parsedTheme: null,
@@ -2666,13 +2866,13 @@ function createCsszyxPlugins(options = {}) {
2666
2866
  return isHardIgnoredPath(id, compileSourceDirs);
2667
2867
  }
2668
2868
  function shouldProcessSource(id) {
2669
- return !isHardIgnored(id) && !isUserExcluded(id) && isUserIncluded(id) && (/\.([cm]?[tj]s|[tj]sx)(\?.*)?$/.test(id) || id.endsWith(".vue") || id.endsWith(".svelte"));
2869
+ return !isHardIgnored(id) && !isUserExcluded(id) && isUserIncluded(id) && (matchesScriptExtension(id, SOURCE_MODULE_EXTENSIONS) || id.endsWith(".vue") || id.endsWith(".svelte"));
2670
2870
  }
2671
2871
  function shouldProcessCss(id) {
2672
- return !isHardIgnored(id) && !isUserExcluded(id) && /\.css(\?.*)?$/.test(id);
2872
+ return !isHardIgnored(id) && !isUserExcluded(id) && matchesScriptExtension(id, [".css"]);
2673
2873
  }
2674
- function transformConfiguredSource(source, filename) {
2675
- const compilerOptions = createCompilerOptions();
2874
+ function transformConfiguredSource(source, filename, astBudget) {
2875
+ const compilerOptions = createCompilerOptions(astBudget);
2676
2876
  const effectiveFilename = normalizeSourceFilename(filename);
2677
2877
  const cacheRoot = transformCache.resolveTransformCacheDir(state.rootDir, options.build?.cacheDir);
2678
2878
  if (cacheEnabled) {
@@ -2699,6 +2899,15 @@ function createCsszyxPlugins(options = {}) {
2699
2899
  rememberTransformCacheEntry(cacheKey.key, cached);
2700
2900
  return cached;
2701
2901
  }
2902
+ if (astBudget === void 0) {
2903
+ const handoff = prescanResultHandoff.get(effectiveFilename);
2904
+ if (handoff) {
2905
+ prescanResultHandoff.delete(effectiveFilename);
2906
+ if (handoff.inputSha256 === cacheKey.inputSha256) {
2907
+ return handoff.result;
2908
+ }
2909
+ }
2910
+ }
2702
2911
  }
2703
2912
  let result;
2704
2913
  if (parserMode === "babel") {
@@ -2729,9 +2938,9 @@ function createCsszyxPlugins(options = {}) {
2729
2938
  }
2730
2939
  return result;
2731
2940
  }
2732
- function createCompilerOptions() {
2941
+ function createCompilerOptions(astBudget = astBudgetOverride) {
2733
2942
  return {
2734
- astBudget: astBudgetOverride,
2943
+ astBudget,
2735
2944
  mangleVars: options.production?.mangleVars === true,
2736
2945
  mangleVarHoistMaxDepth: options.production?.mangleVarHoistMaxDepth,
2737
2946
  globalVarAliases: earlyGlobalVarAliasEntries.length > 0 ? earlyGlobalVarAliasEntries : void 0,
@@ -2746,7 +2955,10 @@ function createCsszyxPlugins(options = {}) {
2746
2955
  nativeIdentity: parserMode === "rust" ? resolveNativeCacheIdentity() : void 0,
2747
2956
  parserMode,
2748
2957
  producer: parserMode,
2749
- astBudget: astBudgetOverride,
2958
+ // The EFFECTIVE budget, not the raw override: prescan-lane results
2959
+ // (larger budget) must not be served to the transform hook, whose
2960
+ // smaller budget could not have produced them (and vice versa).
2961
+ astBudget: compilerOptions.astBudget,
2750
2962
  mangleVars: compilerOptions.mangleVars,
2751
2963
  mangleVarHoistMaxDepth: compilerOptions.mangleVarHoistMaxDepth,
2752
2964
  globalVarAliases: normalizeGlobalVarAliasesForCache(compilerOptions.globalVarAliases),
@@ -2758,7 +2970,7 @@ function createCsszyxPlugins(options = {}) {
2758
2970
  if (parserMode !== "rust" || files.length <= 1) {
2759
2971
  return transformPrescanSourcesIndividually(files);
2760
2972
  }
2761
- const compilerOptions = createCompilerOptions();
2973
+ const compilerOptions = createCompilerOptions(prescanAstBudget);
2762
2974
  const cacheRoot = transformCache.resolveTransformCacheDir(state.rootDir, options.build?.cacheDir);
2763
2975
  const results = /* @__PURE__ */ new Map();
2764
2976
  const misses = [];
@@ -2828,7 +3040,11 @@ function createCsszyxPlugins(options = {}) {
2828
3040
  try {
2829
3041
  results.set(
2830
3042
  miss.filePath,
2831
- transformConfiguredSource(miss.content, miss.effectiveFilename)
3043
+ transformConfiguredSource(
3044
+ miss.content,
3045
+ miss.effectiveFilename,
3046
+ prescanAstBudget
3047
+ )
2832
3048
  );
2833
3049
  } catch {
2834
3050
  }
@@ -2839,15 +3055,28 @@ function createCsszyxPlugins(options = {}) {
2839
3055
  return result ? { filePath: file.filePath, result } : null;
2840
3056
  }).filter((entry) => entry !== null);
2841
3057
  }
3058
+ function warnPrescanBudgetSkip(filePath) {
3059
+ console.warn(
3060
+ `[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.`
3061
+ );
3062
+ }
2842
3063
  function transformPrescanSourcesIndividually(files) {
2843
3064
  const results = [];
2844
3065
  for (const file of files) {
2845
3066
  try {
2846
3067
  results.push({
2847
3068
  filePath: file.filePath,
2848
- result: transformConfiguredSource(file.content, file.filePath)
3069
+ result: transformConfiguredSource(
3070
+ file.content,
3071
+ file.filePath,
3072
+ prescanAstBudget
3073
+ )
2849
3074
  });
2850
3075
  } catch (err) {
3076
+ if (err instanceof compiler.ASTBudgetExceededError) {
3077
+ warnPrescanBudgetSkip(file.filePath);
3078
+ continue;
3079
+ }
2851
3080
  console.warn(
2852
3081
  `[csszyx] prescan skipped ${file.filePath}: transform failed, so none of its classes reached the safelist. ${err instanceof Error ? err.message : String(err)}`
2853
3082
  );
@@ -2975,7 +3204,23 @@ function createCsszyxPlugins(options = {}) {
2975
3204
  }
2976
3205
  scanDir(sourceDir);
2977
3206
  }
3207
+ const prescanContentByPath = new Map(
3208
+ prescanSources.map((file) => [file.filePath, file.content])
3209
+ );
2978
3210
  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
+ }
2979
3224
  if (result.classes.size === 0 && result.rawClassNames.size === 0 && result.diagnostics.some((d) => d.includes("[csszyx] parse error in "))) {
2980
3225
  console.warn(
2981
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).`
@@ -3231,10 +3476,10 @@ function createCsszyxPlugins(options = {}) {
3231
3476
  if (shouldProcessSource(id)) {
3232
3477
  trackGlobalVarSourceFile(id, code);
3233
3478
  }
3234
- if (/\.[tj]sx?(\?.*)?$/.test(id)) {
3479
+ if (matchesScriptExtension(id, SCRIPT_ID_EXTENSIONS)) {
3235
3480
  assertNoRSCBoundaryViolation(code, id);
3236
3481
  }
3237
- if (/\.css(\?.*)?$/.test(id)) {
3482
+ if (matchesScriptExtension(id, [".css"])) {
3238
3483
  state.sawAnyCss = true;
3239
3484
  if (cssImportsTailwind(code)) {
3240
3485
  state.sawTailwindEntry = true;
@@ -3258,6 +3503,8 @@ function createCsszyxPlugins(options = {}) {
3258
3503
  let transformedCode = code;
3259
3504
  let usesRuntime = false;
3260
3505
  let usesMerge = false;
3506
+ let usesSzcn = false;
3507
+ let usesSzPart = false;
3261
3508
  let usesColorVar = false;
3262
3509
  let transformed = false;
3263
3510
  let szClasses;
@@ -3286,6 +3533,8 @@ function createCsszyxPlugins(options = {}) {
3286
3533
  transformedCode = result.code;
3287
3534
  usesRuntime = result.usesRuntime;
3288
3535
  usesMerge = result.usesMerge;
3536
+ usesSzcn = result.usesSzcn;
3537
+ usesSzPart = result.usesSzPart;
3289
3538
  usesColorVar = result.usesColorVar;
3290
3539
  transformed = result.transformed;
3291
3540
  szClasses = result.classes;
@@ -3294,12 +3543,17 @@ function createCsszyxPlugins(options = {}) {
3294
3543
  for (const msg of result.diagnostics) {
3295
3544
  if (msg.includes("unresolvable sz spread")) {
3296
3545
  state.spreadWarnings.add(`${id}
3546
+ ${msg}`);
3547
+ } else if (msg.includes("AST budget exceeded")) {
3548
+ console.warn(`[csszyx] ${id}
3297
3549
  ${msg}`);
3298
3550
  }
3299
3551
  }
3300
3552
  if (!quiet && result.diagnostics.length > 0 && process.env.NODE_ENV !== "production") {
3301
3553
  for (const msg of result.diagnostics) {
3302
- if (msg.includes("unresolvable sz spread")) continue;
3554
+ if (msg.includes("unresolvable sz spread") || msg.includes("AST budget exceeded")) {
3555
+ continue;
3556
+ }
3303
3557
  this.warn(`[csszyx] ${id}
3304
3558
  ${msg}`);
3305
3559
  }
@@ -3314,16 +3568,14 @@ function createCsszyxPlugins(options = {}) {
3314
3568
  }
3315
3569
  if (transformedCode.includes("<html") && /(?:layout|Root|Document|app)\.tsx?$/i.test(id)) {
3316
3570
  const attrName = options.production?.minify ? "data-sz-cs" : "data-sz-checksum";
3317
- transformedCode = transformedCode.replace(
3318
- /<html([^>]*)>/i,
3319
- `<html$1 ${attrName}="${CHECKSUM_PLACEHOLDER}">`
3320
- );
3571
+ const htmlTag = findOpeningTag(transformedCode, "html");
3572
+ if (htmlTag) {
3573
+ transformedCode = `${transformedCode.slice(0, htmlTag.close)} ${attrName}="${CHECKSUM_PLACEHOLDER}"${transformedCode.slice(htmlTag.close)}`;
3574
+ }
3321
3575
  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})}}})()\`}} />`;
3322
- if (transformedCode.includes("<body")) {
3323
- transformedCode = transformedCode.replace(
3324
- /(<body[^>]*>)/i,
3325
- `$1${debugScript}`
3326
- );
3576
+ const bodyTag = findOpeningTag(transformedCode, "body");
3577
+ if (bodyTag) {
3578
+ transformedCode = `${transformedCode.slice(0, bodyTag.close + 1)}${debugScript}${transformedCode.slice(bodyTag.close + 1)}`;
3327
3579
  }
3328
3580
  transformed = true;
3329
3581
  }
@@ -3335,21 +3587,23 @@ function createCsszyxPlugins(options = {}) {
3335
3587
  if (usesMerge) {
3336
3588
  imports.push("_szMerge");
3337
3589
  }
3590
+ if (usesSzcn) {
3591
+ imports.push("_szcn");
3592
+ }
3593
+ if (usesSzPart) {
3594
+ imports.push("_szPart");
3595
+ }
3338
3596
  if (usesColorVar) {
3339
3597
  imports.push("__szColorVar");
3340
3598
  }
3341
3599
  const hasRuntimeImport = imports.length > 0 && transformedCode.includes("@csszyx/runtime");
3342
- const needed = hasRuntimeImport ? imports.filter(
3343
- (name) => !RUNTIME_HELPER_IMPORT_RE[name]?.test(transformedCode)
3344
- ) : imports;
3600
+ const needed = hasRuntimeImport ? imports.filter((name) => !runtimeImportScan.importsRuntimeHelper(transformedCode, name)) : imports;
3345
3601
  if (needed.length > 0) {
3346
- const existingImport = transformedCode.match(
3347
- /^(import\s*\{[^}]*)\}\s*from\s*'@csszyx\/runtime'/m
3348
- );
3602
+ const existingImport = runtimeImportScan.findRuntimeImportClause(transformedCode);
3349
3603
  if (existingImport) {
3350
3604
  transformedCode = transformedCode.replace(
3351
- existingImport[0],
3352
- `${existingImport[1]}, ${needed.join(", ")} } from '@csszyx/runtime'`
3605
+ existingImport.statement,
3606
+ `${existingImport.prefixWithBody}, ${needed.join(", ")} } from '@csszyx/runtime'`
3353
3607
  );
3354
3608
  } else {
3355
3609
  const importStmt = `import { ${needed.join(", ")} } from '@csszyx/runtime';
@@ -3359,12 +3613,12 @@ function createCsszyxPlugins(options = {}) {
3359
3613
  transformed = true;
3360
3614
  }
3361
3615
  }
3362
- if (/\bszcn\s*\(/.test(code) && !transformedCode.includes(THEME_GROUPS_VIRTUAL_ID) && shouldProcessSource(id)) {
3616
+ if ((usesSzcn || /\bszcn\s*\(/.test(code)) && !transformedCode.includes(THEME_GROUPS_VIRTUAL_ID) && shouldProcessSource(id)) {
3363
3617
  transformedCode = `import '${THEME_GROUPS_VIRTUAL_ID}';
3364
3618
  ${transformedCode}`;
3365
3619
  transformed = true;
3366
3620
  }
3367
- if (/\.[tj]sx?(\?.*)?$/.test(id)) {
3621
+ if (matchesScriptExtension(id, SCRIPT_ID_EXTENSIONS)) {
3368
3622
  assertNoRSCBoundaryViolation(transformedCode, id);
3369
3623
  const record = createRSCModuleRecord(transformedCode, id);
3370
3624
  state.rscModules.set(record.id, record);
@@ -3422,6 +3676,7 @@ ${transformedCode}`;
3422
3676
  emitWarning(`[csszyx] ${warning}`);
3423
3677
  }
3424
3678
  state.spreadWarnings.clear();
3679
+ prescanResultHandoff.clear();
3425
3680
  if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
3426
3681
  globalThis.__csszyx_ssr_mangle_map = state.mangleMap;
3427
3682
  }
@@ -3481,6 +3736,7 @@ ${transformedCode}`;
3481
3736
  * @param ctx - HMR context containing the changed file
3482
3737
  */
3483
3738
  handleHotUpdate(ctx) {
3739
+ prescanResultHandoff.clear();
3484
3740
  const scanCss = options.build?.scanCss;
3485
3741
  if (scanCss) {
3486
3742
  const root = ctx.server.config.root || process.cwd();
@@ -3930,6 +4186,7 @@ exports.deleteRSCModuleRecord = deleteRSCModuleRecord;
3930
4186
  exports.esbuildPlugin = esbuildPlugin;
3931
4187
  exports.extractGlobalVarAliasesForManifest = extractGlobalVarAliasesForManifest;
3932
4188
  exports.fileMayContainSafelistableSz = fileMayContainSafelistableSz;
4189
+ exports.findLocalImportSources = findLocalImportSources;
3933
4190
  exports.findRSCBoundaryViolation = findRSCBoundaryViolation;
3934
4191
  exports.findRSCGraphViolation = findRSCGraphViolation;
3935
4192
  exports.hasInjectableTailwindCandidate = hasInjectableTailwindCandidate;
@@ -3956,6 +4213,7 @@ exports.resolveGlobalVarScanCacheDir = resolveGlobalVarScanCacheDir;
3956
4213
  exports.resolveNativeCacheIdentity = resolveNativeCacheIdentity;
3957
4214
  exports.rewriteGlobalVarCssAliases = rewriteGlobalVarCssAliases;
3958
4215
  exports.rollupPlugin = rollupPlugin;
4216
+ exports.scanCustomPropertyNames = scanCustomPropertyNames;
3959
4217
  exports.scanGlobalVarCss = scanGlobalVarCss;
3960
4218
  exports.shouldEmitWarning = shouldEmitWarning;
3961
4219
  exports.shouldTrackGlobalVarSources = shouldTrackGlobalVarSources;