@csszyx/unplugin 0.7.0 → 0.8.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,19 +1,24 @@
1
- import {
2
- mangleCSSSync
3
- } from "./chunk-4M7CPGP7.js";
1
+ import * as fs from 'node:fs';
2
+ import { mkdirSync, writeFileSync } from 'node:fs';
3
+ import * as path from 'node:path';
4
+ import { dirname } from 'node:path';
5
+ import { transformSourceCode, transformOxc, transform } from '@csszyx/compiler';
6
+ import { encode, compute_mangle_checksum } from '@csszyx/core';
7
+ import { preprocess as preprocess$1 } from '@csszyx/svelte-adapter';
8
+ import { preprocess } from '@csszyx/vue-adapter';
9
+ import { createUnplugin } from 'unplugin';
10
+ import { mangleCSSSync } from '../css-mangler.mjs';
11
+ import { createHash } from 'node:crypto';
4
12
 
5
- // src/rsc-boundary.ts
6
- import * as fs from "fs";
7
- import * as path from "path";
8
- var SERVER_DIRECTIVE_RE = /^['"]use server['"];?$/;
9
- var CLIENT_DIRECTIVE_RE = /^['"]use client['"];?$/;
10
- var RUNTIME_MODULES = /* @__PURE__ */ new Set([
13
+ const SERVER_DIRECTIVE_RE = /^['"]use server['"];?$/;
14
+ const CLIENT_DIRECTIVE_RE = /^['"]use client['"];?$/;
15
+ const RUNTIME_MODULES = /* @__PURE__ */ new Set([
11
16
  "@csszyx/runtime",
12
17
  "@csszyx/runtime/lite",
13
18
  "csszyx",
14
19
  "csszyx/lite"
15
20
  ]);
16
- var FORBIDDEN_SYMBOLS = /* @__PURE__ */ new Set([
21
+ const FORBIDDEN_SYMBOLS = /* @__PURE__ */ new Set([
17
22
  "_sz",
18
23
  "_sz2",
19
24
  "_sz3",
@@ -77,7 +82,9 @@ function createRSCModuleRecord(code, id) {
77
82
  isServer: isRSCServerModule(code, normalized),
78
83
  isClient: hasUseClientDirective(code),
79
84
  imports: findLocalImportSources(code).map((source) => resolveLocalModule(normalized, source)).filter((resolved) => resolved !== null),
80
- runtimeImports: findRuntimeImports(code).filter((imported) => imported.symbols.some((symbol) => FORBIDDEN_SYMBOLS.has(symbol)))
85
+ runtimeImports: findRuntimeImports(code).filter(
86
+ (imported) => imported.symbols.some((symbol) => FORBIDDEN_SYMBOLS.has(symbol))
87
+ )
81
88
  };
82
89
  }
83
90
  function findRSCGraphViolation(records) {
@@ -105,7 +112,9 @@ function assertNoRSCGraphViolation(records) {
105
112
  }
106
113
  function isNextAppRouterEntry(id) {
107
114
  const clean = id.split("?")[0]?.replace(/\\/g, "/") ?? id;
108
- return /(^|\/)app\/.*\/?(?:page|layout|template|loading|error|not-found|global-error|default|route)\.[cm]?[tj]sx?$/.test(clean);
115
+ return /(^|\/)app\/.*\/?(?:page|layout|template|loading|error|not-found|global-error|default|route)\.[cm]?[tj]sx?$/.test(
116
+ clean
117
+ );
109
118
  }
110
119
  function assertNoRSCBoundaryViolation(code, id) {
111
120
  const violation = findRSCBoundaryViolation(code, id);
@@ -209,8 +218,7 @@ function findRuntimeImports(code) {
209
218
  const staticImportRe = /import\s+(?!type\b)([\s\S]*?)\s+from\s+['"]([^'"]+)['"]/g;
210
219
  const sideEffectImportRe = /import\s+['"]([^'"]+)['"]/g;
211
220
  const dynamicImportRe = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
212
- let match;
213
- while ((match = staticImportRe.exec(code)) !== null) {
221
+ for (const match of code.matchAll(staticImportRe)) {
214
222
  const clause = match[1];
215
223
  const source = match[2];
216
224
  if (!RUNTIME_MODULES.has(source)) {
@@ -218,13 +226,13 @@ function findRuntimeImports(code) {
218
226
  }
219
227
  imports.push({ source, symbols: readImportedSymbols(clause) });
220
228
  }
221
- while ((match = sideEffectImportRe.exec(code)) !== null) {
229
+ for (const match of code.matchAll(sideEffectImportRe)) {
222
230
  const source = match[1];
223
231
  if (RUNTIME_MODULES.has(source)) {
224
232
  imports.push({ source, symbols: [] });
225
233
  }
226
234
  }
227
- while ((match = dynamicImportRe.exec(code)) !== null) {
235
+ for (const match of code.matchAll(dynamicImportRe)) {
228
236
  const source = match[1];
229
237
  if (RUNTIME_MODULES.has(source)) {
230
238
  imports.push({ source, symbols: Array.from(FORBIDDEN_SYMBOLS) });
@@ -237,9 +245,8 @@ function findLocalImportSources(code) {
237
245
  const staticImportRe = /import\s+(?!type\b)(?:[\s\S]*?\s+from\s+)?['"]([^'"]+)['"]/g;
238
246
  const exportFromRe = /export\s+(?!type\b)(?:[\s\S]*?)\s+from\s+['"]([^'"]+)['"]/g;
239
247
  const dynamicImportRe = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
240
- let match;
241
248
  for (const re of [staticImportRe, exportFromRe, dynamicImportRe]) {
242
- while ((match = re.exec(code)) !== null) {
249
+ for (const match of code.matchAll(re)) {
243
250
  const source = match[1];
244
251
  if (source.startsWith(".") || source.startsWith("/")) {
245
252
  out.push(source);
@@ -299,8 +306,7 @@ function readImportedSymbols(clause) {
299
306
  return symbols;
300
307
  }
301
308
 
302
- // src/theme-scanner.ts
303
- var EMPTY_THEME = { colors: [], spacings: [], fonts: [], radii: [], shadows: [] };
309
+ const EMPTY_THEME = { colors: [], spacings: [], fonts: [], radii: [], shadows: [] };
304
310
  function stripLayerWrappers(css) {
305
311
  let result = "";
306
312
  let i = 0;
@@ -342,8 +348,7 @@ function stripLayerWrappers(css) {
342
348
  function extractThemeBlocks(css) {
343
349
  const blocks = [];
344
350
  const themeStart = /@theme\s+(?:inline\s+)?\{|@theme\{/g;
345
- let match;
346
- while ((match = themeStart.exec(css)) !== null) {
351
+ for (const match of css.matchAll(themeStart)) {
347
352
  const openPos = css.indexOf("{", match.index);
348
353
  let depth = 0;
349
354
  let j = openPos;
@@ -394,14 +399,12 @@ function parseThemeBlocks(cssContent) {
394
399
  const blocks = extractThemeBlocks(stripped);
395
400
  const propPattern = /--([a-z][a-z0-9-]*)(?:\s*:[^;]+)?;/g;
396
401
  for (const block of blocks) {
397
- let match;
398
- while ((match = propPattern.exec(block)) !== null) {
402
+ for (const match of block.matchAll(propPattern)) {
399
403
  const categorized = categorizeProperty(match[1]);
400
404
  if (categorized) {
401
405
  result[categorized.category].add(categorized.token);
402
406
  }
403
407
  }
404
- propPattern.lastIndex = 0;
405
408
  }
406
409
  return {
407
410
  colors: [...result.colors].sort(),
@@ -441,17 +444,122 @@ function hasTokens(theme) {
441
444
  return Object.values(theme).some((arr) => arr.length > 0);
442
445
  }
443
446
 
444
- // src/unplugin.ts
445
- import * as fs2 from "fs";
446
- import * as path2 from "path";
447
- import { transform, transformSourceCode } from "@csszyx/compiler";
448
- import { compute_mangle_checksum, encode } from "@csszyx/core";
449
- import { preprocess as sveltePreprocess } from "@csszyx/svelte-adapter";
450
- import { preprocess as vuePreprocess } from "@csszyx/vue-adapter";
451
- import { createUnplugin } from "unplugin";
447
+ const GLOB_MAGIC_RE = /[*?[\]{}]/;
448
+ const DEFAULT_IGNORED_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", ".turbo", "dist", "build"]);
449
+ function normalizeFileId(id) {
450
+ return id.split(/[?#]/, 1)[0].replace(/\\/g, "/");
451
+ }
452
+ function normalizeRoot(rootDir) {
453
+ return path.resolve(rootDir).replace(/\\/g, "/");
454
+ }
455
+ function hasGlobMagic(pattern) {
456
+ return GLOB_MAGIC_RE.test(pattern);
457
+ }
458
+ function escapeRegExp(ch) {
459
+ return /[|\\{}()[\]^$+?.]/.test(ch) ? `\\${ch}` : ch;
460
+ }
461
+ function globToRegExp(pattern) {
462
+ const normalized = pattern.replace(/\\/g, "/");
463
+ let out = "^";
464
+ for (let i = 0; i < normalized.length; i++) {
465
+ const ch = normalized[i];
466
+ if (ch === "*") {
467
+ if (normalized[i + 1] === "*") {
468
+ out += ".*";
469
+ i++;
470
+ } else {
471
+ out += "[^/]*";
472
+ }
473
+ } else if (ch === "?") {
474
+ out += "[^/]";
475
+ } else {
476
+ out += escapeRegExp(ch);
477
+ }
478
+ }
479
+ out += "$";
480
+ return new RegExp(out);
481
+ }
482
+ function relativeToRoot(file, rootDir) {
483
+ const root = normalizeRoot(rootDir);
484
+ const normalizedFile = normalizeFileId(file);
485
+ return path.posix.relative(root, normalizedFile).replace(/\\/g, "/");
486
+ }
487
+ function matchesPattern(id, pattern, rootDir) {
488
+ const file = normalizeFileId(id);
489
+ const relative = relativeToRoot(file, rootDir);
490
+ if (pattern instanceof RegExp) {
491
+ pattern.lastIndex = 0;
492
+ return pattern.test(file) || pattern.test(relative);
493
+ }
494
+ const normalizedPattern = pattern.replace(/\\/g, "/");
495
+ if (hasGlobMagic(normalizedPattern)) {
496
+ const re = globToRegExp(normalizedPattern);
497
+ return re.test(file) || re.test(relative);
498
+ }
499
+ const absolutePattern = path.isAbsolute(normalizedPattern) ? normalizeFileId(normalizedPattern) : normalizeFileId(path.join(rootDir, normalizedPattern));
500
+ return file === absolutePattern || relative === normalizedPattern;
501
+ }
502
+ function matchesAnyPattern(id, patterns, rootDir) {
503
+ if (!patterns) {
504
+ return false;
505
+ }
506
+ const list = Array.isArray(patterns) ? patterns : [patterns];
507
+ return list.some((pattern) => matchesPattern(id, pattern, rootDir));
508
+ }
509
+ function expandFilePatterns(rootDir, patterns) {
510
+ const list = Array.isArray(patterns) ? patterns : [patterns];
511
+ const files = /* @__PURE__ */ new Set();
512
+ const walk = (dir) => {
513
+ let entries;
514
+ try {
515
+ entries = fs.readdirSync(dir, { withFileTypes: true });
516
+ } catch {
517
+ return;
518
+ }
519
+ for (const entry of entries) {
520
+ const full = path.join(dir, entry.name);
521
+ if (entry.isDirectory()) {
522
+ if (!DEFAULT_IGNORED_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
523
+ walk(full);
524
+ }
525
+ } else {
526
+ files.add(path.resolve(full));
527
+ }
528
+ }
529
+ };
530
+ let needsWalk = false;
531
+ for (const pattern of list) {
532
+ const resolved = path.isAbsolute(pattern) ? pattern : path.join(rootDir, pattern);
533
+ if (hasGlobMagic(pattern)) {
534
+ needsWalk = true;
535
+ continue;
536
+ }
537
+ if (fs.existsSync(resolved)) {
538
+ files.add(path.resolve(resolved));
539
+ }
540
+ }
541
+ if (needsWalk) {
542
+ walk(rootDir);
543
+ for (const file of Array.from(files)) {
544
+ if (!list.some(
545
+ (pattern) => hasGlobMagic(pattern) && matchesPattern(file, pattern, rootDir)
546
+ )) {
547
+ const isLiteralMatch = list.some((pattern) => {
548
+ if (hasGlobMagic(pattern)) {
549
+ return false;
550
+ }
551
+ const resolved = path.isAbsolute(pattern) ? pattern : path.join(rootDir, pattern);
552
+ return normalizeFileId(path.resolve(resolved)) === normalizeFileId(file);
553
+ });
554
+ if (!isLiteralMatch) {
555
+ files.delete(file);
556
+ }
557
+ }
558
+ }
559
+ }
560
+ return Array.from(files).sort();
561
+ }
452
562
 
453
- // src/html-transformer.ts
454
- import { createHash } from "crypto";
455
563
  function injectChecksum(html, checksum, minify = false) {
456
564
  const attrName = minify ? "data-sz-cs" : "data-sz-checksum";
457
565
  const htmlTagPattern = /<html([^>]*)>/i;
@@ -461,16 +569,13 @@ function injectChecksum(html, checksum, minify = false) {
461
569
  }
462
570
  const existingAttrs = match[1];
463
571
  const checksumAttr = ` ${attrName}="${checksum}"`;
464
- return html.replace(
465
- htmlTagPattern,
466
- `<html${checksumAttr}${existingAttrs}>`
467
- );
572
+ return html.replace(htmlTagPattern, `<html${checksumAttr}${existingAttrs}>`);
468
573
  }
469
574
  function injectMangleMapScript(html, mangleMap, options = {}) {
470
575
  const { prettyPrint = false } = options;
471
576
  const jsonContent = prettyPrint ? JSON.stringify(mangleMap, null, 2) : JSON.stringify(mangleMap);
472
- const scriptTag = `<script id="__CSSZYX_MANGLE_MAP__" type="application/json">${jsonContent}</script>`;
473
- const debugScript = `<script>(function(){var m=${jsonContent};var r={};for(var k in m)r[m[k]]=k;var cs=document.documentElement.getAttribute("data-sz-checksum")||"";window.__csszyx={mangleMap:m,checksum:cs,decode:function(c){return r[c]},encode:function(c){return m[c]},decodeAll:function(el){return(el.className||"").split(" ").map(function(c){return r[c]||c})}}})()</script>`;
577
+ const scriptTag = `<script id="__CSSZYX_MANGLE_MAP__" type="application/json">${jsonContent}<\/script>`;
578
+ const debugScript = `<script>(function(){var m=${jsonContent};var r={};for(var k in m)r[m[k]]=k;var cs=document.documentElement.getAttribute("data-sz-checksum")||"";window.__csszyx={mangleMap:m,checksum:cs,decode:function(c){return r[c]},encode:function(c){return m[c]},decodeAll:function(el){return(el.className||"").split(" ").map(function(c){return r[c]||c})}}})()<\/script>`;
474
579
  const combined = `${scriptTag}
475
580
  ${debugScript}`;
476
581
  if (html.includes("</head>")) {
@@ -541,7 +646,7 @@ function injectRecoveryManifest(html, manifest) {
541
646
  return html;
542
647
  }
543
648
  const json = JSON.stringify(manifest);
544
- const scriptTag = `<script id="__SZ_RECOVERY_MANIFEST__" type="application/json">${json}</script>`;
649
+ const scriptTag = `<script id="__SZ_RECOVERY_MANIFEST__" type="application/json">${json}<\/script>`;
545
650
  if (html.includes("</head>")) {
546
651
  return html.replace("</head>", `${scriptTag}
547
652
  </head>`);
@@ -552,9 +657,6 @@ function injectRecoveryManifest(html, manifest) {
552
657
  return html + scriptTag;
553
658
  }
554
659
 
555
- // src/theme-type-writer.ts
556
- import { mkdirSync, writeFileSync } from "fs";
557
- import { dirname as dirname2 } from "path";
558
660
  function generateThemeDts(opts) {
559
661
  const { theme, sourceFiles } = opts;
560
662
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
@@ -597,15 +699,14 @@ function generateThemeDts(opts) {
597
699
  }
598
700
  function writeThemeDts(opts) {
599
701
  const content = generateThemeDts(opts);
600
- mkdirSync(dirname2(opts.outputPath), { recursive: true });
702
+ mkdirSync(dirname(opts.outputPath), { recursive: true });
601
703
  writeFileSync(opts.outputPath, content, "utf-8");
602
704
  }
603
705
 
604
- // src/virtual-modules.ts
605
- var VIRTUAL_MODULE_ID = "virtual:csszyx/mangle-map";
606
- var RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
607
- var VIRTUAL_CHECKSUM_ID = "virtual:csszyx/checksum";
608
- var RESOLVED_VIRTUAL_CHECKSUM_ID = "\0" + VIRTUAL_CHECKSUM_ID;
706
+ const VIRTUAL_MODULE_ID = "virtual:csszyx/mangle-map";
707
+ const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
708
+ const VIRTUAL_CHECKSUM_ID = "virtual:csszyx/checksum";
709
+ const RESOLVED_VIRTUAL_CHECKSUM_ID = `\0${VIRTUAL_CHECKSUM_ID}`;
609
710
  function createMangleMapModule(mangleMap, checksum) {
610
711
  return `/**
611
712
  * Auto-generated mangle map for csszyx.
@@ -650,52 +751,46 @@ function resolveVirtualModule(id) {
650
751
  return void 0;
651
752
  }
652
753
 
653
- // src/unplugin.ts
654
- var CHECKSUM_PLACEHOLDER = "___CSSZYX_CHECKSUM___";
655
- var MANGLE_MAP_PLACEHOLDER = "___CSSZYX_MANGLE_MAP___";
656
- var _hasWarnedTsConfig = false;
754
+ const CHECKSUM_PLACEHOLDER = "___CSSZYX_CHECKSUM___";
755
+ const MANGLE_MAP_PLACEHOLDER = "___CSSZYX_MANGLE_MAP___";
756
+ let _hasWarnedTsConfig = false;
657
757
  function runThemeScan(rootDir, scanCss) {
658
758
  if (!scanCss) {
659
759
  return;
660
760
  }
661
- const patterns = Array.isArray(scanCss) ? scanCss : [scanCss];
662
- const sourceFiles = [];
663
- for (const pattern of patterns) {
664
- const resolved = path2.isAbsolute(pattern) ? pattern : path2.join(rootDir, pattern);
665
- if (fs2.existsSync(resolved)) {
666
- sourceFiles.push(resolved);
667
- }
668
- }
761
+ const sourceFiles = expandFilePatterns(rootDir, scanCss).filter((file) => file.endsWith(".css"));
669
762
  if (sourceFiles.length === 0) {
670
763
  return;
671
764
  }
672
765
  const themes = sourceFiles.map((f) => {
673
766
  try {
674
- return parseThemeBlocks(fs2.readFileSync(f, "utf-8"));
767
+ return parseThemeBlocks(fs.readFileSync(f, "utf-8"));
675
768
  } catch {
676
769
  return null;
677
770
  }
678
771
  }).filter((t) => t !== null);
679
772
  const merged = mergeThemes(themes);
680
- const outputPath = path2.join(rootDir, ".csszyx", "theme.d.ts");
773
+ const outputPath = path.join(rootDir, ".csszyx", "theme.d.ts");
681
774
  writeThemeDts({ outputPath, theme: merged, sourceFiles });
682
775
  if (!_hasWarnedTsConfig) {
683
776
  _hasWarnedTsConfig = true;
684
777
  try {
685
778
  const checkFile = (cfgPath) => {
686
- if (fs2.existsSync(cfgPath)) {
687
- const content = fs2.readFileSync(cfgPath, "utf-8");
779
+ if (fs.existsSync(cfgPath)) {
780
+ const content = fs.readFileSync(cfgPath, "utf-8");
688
781
  if (!content.includes(".csszyx")) {
689
- console.warn(`
782
+ console.warn(
783
+ `
690
784
  \x1B[33m\u26A0\uFE0F CSSzyx: Theme Auto-Scan enabled, but TypeScript isn't configured. Run "npx @csszyx/cli init" to fix.\x1B[0m
691
- `);
785
+ `
786
+ );
692
787
  }
693
788
  return true;
694
789
  }
695
790
  return false;
696
791
  };
697
- if (!checkFile(path2.join(rootDir, "tsconfig.json"))) {
698
- checkFile(path2.join(rootDir, "tsconfig.app.json"));
792
+ if (!checkFile(path.join(rootDir, "tsconfig.json"))) {
793
+ checkFile(path.join(rootDir, "tsconfig.app.json"));
699
794
  }
700
795
  } catch {
701
796
  }
@@ -780,12 +875,12 @@ function mangleCodeClassesSync(code, mangleMap) {
780
875
  return qm;
781
876
  }
782
877
  changed = true;
783
- return '"' + m + '"';
878
+ return `"${m}"`;
784
879
  });
785
- out += "${" + mangledInner + "}";
880
+ out += `\${${mangledInner}}`;
786
881
  i = j;
787
882
  }
788
- return changed ? "className:`" + out + "`" : fullMatch;
883
+ return changed ? `className:\`${out}\`` : fullMatch;
789
884
  });
790
885
  {
791
886
  const marker = "className:";
@@ -840,7 +935,7 @@ function mangleCodeClassesSync(code, mangleMap) {
840
935
  const mangledStr = parts.map((p) => mangleMap[p] || p).join(" ");
841
936
  if (mangledStr !== inner) {
842
937
  changed = true;
843
- return '"' + mangledStr + '"';
938
+ return `"${mangledStr}"`;
844
939
  }
845
940
  return qm;
846
941
  });
@@ -869,13 +964,15 @@ function mangleCodeClassesSync(code, mangleMap) {
869
964
  if (!changed) {
870
965
  return match;
871
966
  }
872
- return '"' + mangled.join(" ") + '"';
967
+ return `"${mangled.join(" ")}"`;
873
968
  });
874
969
  return result;
875
970
  }
876
971
  function createCsszyxPlugins(options = {}) {
877
972
  const manglingEnabled = options.production?.mangle !== false;
878
973
  const astBudgetOverride = options.build?.astBudgetLimit;
974
+ const parserOverride = process.env.CSSZYX_PARSER;
975
+ const parserMode = parserOverride === "babel" || parserOverride === "oxc" ? parserOverride : options.build?.parser ?? "oxc";
879
976
  const state = {
880
977
  classes: /* @__PURE__ */ new Set(),
881
978
  mangleMap: {},
@@ -888,20 +985,51 @@ function createCsszyxPlugins(options = {}) {
888
985
  const SAFELIST_FILENAME = "csszyx-classes.html";
889
986
  const SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".tsx", ".jsx", ".ts", ".js"]);
890
987
  const IGNORE_DIRS = /* @__PURE__ */ new Set(["node_modules", ".next", ".git", "dist", "build", ".turbo"]);
988
+ function isUserExcluded(id) {
989
+ return matchesAnyPattern(id, options.exclude, state.rootDir);
990
+ }
991
+ function isUserIncluded(id) {
992
+ return !options.include || matchesAnyPattern(id, options.include, state.rootDir);
993
+ }
994
+ function isHardIgnored(id) {
995
+ return id.includes("node_modules") || id.includes("/packages/") || id.includes(".next") && !id.includes("static");
996
+ }
997
+ function shouldProcessSource(id) {
998
+ return !isHardIgnored(id) && !isUserExcluded(id) && isUserIncluded(id) && (/\.[tj]sx?(\?.*)?$/.test(id) || id.endsWith(".vue") || id.endsWith(".svelte"));
999
+ }
1000
+ function shouldProcessCss(id) {
1001
+ return !isHardIgnored(id) && !isUserExcluded(id) && /\.css(\?.*)?$/.test(id);
1002
+ }
1003
+ function transformConfiguredSource(source, filename) {
1004
+ const compilerOptions = { astBudget: astBudgetOverride };
1005
+ if (parserMode !== "oxc") {
1006
+ return transformSourceCode(source, filename, compilerOptions);
1007
+ }
1008
+ try {
1009
+ return transformOxc(source, filename, compilerOptions);
1010
+ } catch (err) {
1011
+ const result = transformSourceCode(source, filename, compilerOptions);
1012
+ const reason = err instanceof Error ? err.message : String(err);
1013
+ result.diagnostics.push(
1014
+ `[csszyx] oxc parser fell back to Babel for ${filename}: ${reason}`
1015
+ );
1016
+ return result;
1017
+ }
1018
+ }
891
1019
  function writeSafelistFile(classes) {
892
1020
  if (classes.size === 0) {
893
1021
  return;
894
1022
  }
895
- const safelistPath = path2.join(state.rootDir, SAFELIST_FILENAME);
1023
+ const safelistPath = path.join(state.rootDir, SAFELIST_FILENAME);
896
1024
  const classList = Array.from(classes).join(" ");
897
1025
  const content = `<!-- Auto-generated by csszyx \u2014 DO NOT EDIT -->
898
1026
  <!-- Tailwind CSS scans this file for class name detection -->
899
1027
  <div class="${classList}"><div class="${classList}">x</div><div class="${classList}">x</div></div>
900
1028
  `;
901
1029
  try {
902
- const existing = fs2.existsSync(safelistPath) ? fs2.readFileSync(safelistPath, "utf-8") : "";
1030
+ const existing = fs.existsSync(safelistPath) ? fs.readFileSync(safelistPath, "utf-8") : "";
903
1031
  if (existing !== content) {
904
- fs2.writeFileSync(safelistPath, content);
1032
+ fs.writeFileSync(safelistPath, content);
905
1033
  }
906
1034
  } catch {
907
1035
  }
@@ -912,23 +1040,26 @@ function createCsszyxPlugins(options = {}) {
912
1040
  function scanDir(dir) {
913
1041
  let entries;
914
1042
  try {
915
- entries = fs2.readdirSync(dir, { withFileTypes: true });
1043
+ entries = fs.readdirSync(dir, { withFileTypes: true });
916
1044
  } catch {
917
1045
  return;
918
1046
  }
919
1047
  for (const entry of entries) {
920
1048
  if (entry.isDirectory()) {
921
1049
  if (!IGNORE_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
922
- scanDir(path2.join(dir, entry.name));
1050
+ scanDir(path.join(dir, entry.name));
1051
+ }
1052
+ } else if (SOURCE_EXTENSIONS.has(path.extname(entry.name))) {
1053
+ const filePath = path.join(dir, entry.name);
1054
+ if (!shouldProcessSource(filePath)) {
1055
+ continue;
923
1056
  }
924
- } else if (SOURCE_EXTENSIONS.has(path2.extname(entry.name))) {
925
- const filePath = path2.join(dir, entry.name);
926
1057
  try {
927
- const content = fs2.readFileSync(filePath, "utf-8");
1058
+ const content = fs.readFileSync(filePath, "utf-8");
928
1059
  if (!content.includes("sz=") && !content.includes("sz:")) {
929
1060
  continue;
930
1061
  }
931
- const result = transformSourceCode(content, filePath, { astBudget: astBudgetOverride });
1062
+ const result = transformConfiguredSource(content, filePath);
932
1063
  if (!result.transformed) {
933
1064
  continue;
934
1065
  }
@@ -943,10 +1074,9 @@ function createCsszyxPlugins(options = {}) {
943
1074
  }
944
1075
  if (result.usesRuntime) {
945
1076
  const szCallRe = /_sz\(\s*\{/g;
946
- let szMatch;
947
- while ((szMatch = szCallRe.exec(result.code)) !== null) {
1077
+ for (const szMatch of result.code.matchAll(szCallRe)) {
948
1078
  let depth = 1;
949
- let idx = szMatch.index + szMatch[0].length;
1079
+ let idx = (szMatch.index ?? 0) + szMatch[0].length;
950
1080
  while (idx < result.code.length && depth > 0) {
951
1081
  if (result.code[idx] === "{") {
952
1082
  depth++;
@@ -955,10 +1085,12 @@ function createCsszyxPlugins(options = {}) {
955
1085
  }
956
1086
  idx++;
957
1087
  }
958
- const objStr = result.code.slice(szMatch.index + szMatch[0].length, idx - 1);
1088
+ const objStr = result.code.slice(
1089
+ (szMatch.index ?? 0) + szMatch[0].length,
1090
+ idx - 1
1091
+ );
959
1092
  const strKv = /(\w+)\s*:\s*(?:"([^"]*)"|'([^']*)')/g;
960
- let kv;
961
- while ((kv = strKv.exec(objStr)) !== null) {
1093
+ for (const kv of objStr.matchAll(strKv)) {
962
1094
  try {
963
1095
  const val = kv[2] ?? kv[3];
964
1096
  const r = transform({ [kv[1]]: val });
@@ -969,7 +1101,7 @@ function createCsszyxPlugins(options = {}) {
969
1101
  }
970
1102
  }
971
1103
  const numKv = /(\w+)\s*:\s*(-?\d+(?:\.\d+)?)\s*(?=[,}\n])/g;
972
- while ((kv = numKv.exec(objStr)) !== null) {
1104
+ for (const kv of objStr.matchAll(numKv)) {
973
1105
  try {
974
1106
  const r = transform({ [kv[1]]: parseFloat(kv[2]) });
975
1107
  for (const c of r.className.split(/\s+/).filter(Boolean)) {
@@ -979,7 +1111,7 @@ function createCsszyxPlugins(options = {}) {
979
1111
  }
980
1112
  }
981
1113
  const boolKv = /(\w+)\s*:\s*(true|false)\s*(?=[,}\n])/g;
982
- while ((kv = boolKv.exec(objStr)) !== null) {
1114
+ for (const kv of objStr.matchAll(boolKv)) {
983
1115
  try {
984
1116
  const r = transform({ [kv[1]]: kv[2] === "true" });
985
1117
  for (const c of r.className.split(/\s+/).filter(Boolean)) {
@@ -1005,9 +1137,8 @@ function createCsszyxPlugins(options = {}) {
1005
1137
  function extractClasses(code) {
1006
1138
  const dqPattern = /(?:class(?:Name)?|sz)[:=]\s*"([^"]*)"/g;
1007
1139
  const sqPattern = /(?:class(?:Name)?|sz)[:=]\s*'([^']*)'/g;
1008
- let match;
1009
1140
  for (const classPattern of [dqPattern, sqPattern]) {
1010
- while ((match = classPattern.exec(code)) !== null) {
1141
+ for (const match of code.matchAll(classPattern)) {
1011
1142
  const classes = match[1].split(/\s+/).filter(Boolean);
1012
1143
  for (const cls of classes) {
1013
1144
  state.classes.add(cls);
@@ -1015,9 +1146,9 @@ function createCsszyxPlugins(options = {}) {
1015
1146
  }
1016
1147
  }
1017
1148
  const exprStart = /className=\{/g;
1018
- while ((match = exprStart.exec(code)) !== null) {
1149
+ for (const match of code.matchAll(exprStart)) {
1019
1150
  let depth = 1;
1020
- let i = match.index + match[0].length;
1151
+ let i = (match.index ?? 0) + match[0].length;
1021
1152
  while (i < code.length && depth > 0) {
1022
1153
  if (code[i] === "{") {
1023
1154
  depth++;
@@ -1026,10 +1157,9 @@ function createCsszyxPlugins(options = {}) {
1026
1157
  }
1027
1158
  i++;
1028
1159
  }
1029
- const expr = code.slice(match.index + match[0].length, i - 1);
1160
+ const expr = code.slice((match.index ?? 0) + match[0].length, i - 1);
1030
1161
  const strPattern = /"([^"]+)"|'([^']+)'/g;
1031
- let strMatch;
1032
- while ((strMatch = strPattern.exec(expr)) !== null) {
1162
+ for (const strMatch of expr.matchAll(strPattern)) {
1033
1163
  const str = strMatch[1] || strMatch[2];
1034
1164
  const classes = str.split(/\s+/).filter(Boolean);
1035
1165
  for (const cls of classes) {
@@ -1063,325 +1193,327 @@ function createCsszyxPlugins(options = {}) {
1063
1193
  }
1064
1194
  return result;
1065
1195
  }
1066
- const prePlugin = createUnplugin((_pluginOptions) => ({
1067
- name: "csszyx:pre",
1068
- enforce: "pre",
1069
- /**
1070
- * Resolves virtual module IDs for csszyx mangle-map and checksum modules.
1071
- * @param id - the module ID to resolve
1072
- * @returns resolved ID if virtual, null otherwise
1073
- */
1074
- resolveId(id) {
1075
- if (isVirtualModule(id)) {
1076
- return resolveVirtualModule(id);
1077
- }
1078
- return null;
1079
- },
1080
- /**
1081
- * Loads virtual module content — generates mangle map or checksum module code.
1082
- * @param id - the resolved module ID to load
1083
- * @returns generated module source if virtual, null otherwise
1084
- */
1085
- load(id) {
1086
- if (id === RESOLVED_VIRTUAL_MODULE_ID) {
1087
- finalizeMangleMap();
1088
- return createMangleMapModule(state.mangleMap, state.checksum);
1089
- }
1090
- if (id === RESOLVED_VIRTUAL_CHECKSUM_ID) {
1091
- finalizeMangleMap();
1092
- return createChecksumModule(state.checksum);
1093
- }
1094
- return null;
1095
- },
1096
- /**
1097
- * Filters files for the pre-transform phase — source files plus CSS files.
1098
- * CSS files need special handling to inject @source inline() for Tailwind class discovery.
1099
- * @param id - the file path to check for inclusion
1100
- * @returns true if the file should be transformed, false otherwise
1101
- */
1102
- transformInclude(id) {
1103
- if (id.includes("node_modules") || id.includes("/packages/") || id.includes(".next") && !id.includes("static")) {
1104
- return false;
1105
- }
1106
- if (/\.css(\?.*)?$/.test(id)) {
1107
- return true;
1108
- }
1109
- return /\.[tj]sx?$/.test(id) || id.endsWith(".vue") || id.endsWith(".svelte");
1110
- },
1111
- /**
1112
- * Core transform: detects sz prop, compiles to className, injects runtime, collects classes.
1113
- * For CSS files: injects @source inline() so Tailwind generates CSS for sz-derived classes.
1114
- * @param code - the source code to transform
1115
- * @param id - the file path of the module being transformed
1116
- * @returns transformed code with source map, or null if no changes were made
1117
- */
1118
- transform(code, id) {
1119
- if (/\.[tj]sx?(\?.*)?$/.test(id)) {
1120
- assertNoRSCBoundaryViolation(code, id);
1121
- }
1122
- if (/\.css(\?.*)?$/.test(id)) {
1123
- const hasTailwindImport = code.includes('@import "tailwindcss') || code.includes("@import 'tailwindcss");
1124
- if (hasTailwindImport && state.classes.size > 0) {
1125
- const candidates = Array.from(state.classes).filter((c) => c.length >= 2 && /^[a-z]/.test(c)).join(" ");
1126
- if (candidates) {
1127
- const safelistPath = path2.join(state.rootDir, SAFELIST_FILENAME).replace(/\\/g, "/");
1128
- const cssDir = path2.dirname(id).replace(/\\/g, "/");
1129
- let relPath = path2.posix.relative(cssDir, safelistPath);
1130
- if (!relPath.startsWith(".")) {
1131
- relPath = "./" + relPath;
1132
- }
1133
- const sourceDirective = `@source "${relPath}";
1196
+ const prePlugin = createUnplugin(
1197
+ (_pluginOptions) => ({
1198
+ name: "csszyx:pre",
1199
+ enforce: "pre",
1200
+ /**
1201
+ * Resolves virtual module IDs for csszyx mangle-map and checksum modules.
1202
+ * @param id - the module ID to resolve
1203
+ * @returns resolved ID if virtual, null otherwise
1204
+ */
1205
+ resolveId(id) {
1206
+ if (isVirtualModule(id)) {
1207
+ return resolveVirtualModule(id);
1208
+ }
1209
+ return null;
1210
+ },
1211
+ /**
1212
+ * Loads virtual module content generates mangle map or checksum module code.
1213
+ * @param id - the resolved module ID to load
1214
+ * @returns generated module source if virtual, null otherwise
1215
+ */
1216
+ load(id) {
1217
+ if (id === RESOLVED_VIRTUAL_MODULE_ID) {
1218
+ finalizeMangleMap();
1219
+ return createMangleMapModule(state.mangleMap, state.checksum);
1220
+ }
1221
+ if (id === RESOLVED_VIRTUAL_CHECKSUM_ID) {
1222
+ finalizeMangleMap();
1223
+ return createChecksumModule(state.checksum);
1224
+ }
1225
+ return null;
1226
+ },
1227
+ /**
1228
+ * Filters files for the pre-transform phase source files plus CSS files.
1229
+ * CSS files need special handling to inject @source inline() for Tailwind class discovery.
1230
+ * @param id - the file path to check for inclusion
1231
+ * @returns true if the file should be transformed, false otherwise
1232
+ */
1233
+ transformInclude(id) {
1234
+ if (shouldProcessCss(id)) {
1235
+ return true;
1236
+ }
1237
+ return shouldProcessSource(id);
1238
+ },
1239
+ /**
1240
+ * Core transform: detects sz prop, compiles to className, injects runtime, collects classes.
1241
+ * For CSS files: injects @source inline() so Tailwind generates CSS for sz-derived classes.
1242
+ * @param code - the source code to transform
1243
+ * @param id - the file path of the module being transformed
1244
+ * @returns transformed code with source map, or null if no changes were made
1245
+ */
1246
+ transform(code, id) {
1247
+ if (!shouldProcessCss(id) && !shouldProcessSource(id)) {
1248
+ return null;
1249
+ }
1250
+ if (/\.[tj]sx?(\?.*)?$/.test(id)) {
1251
+ assertNoRSCBoundaryViolation(code, id);
1252
+ }
1253
+ if (/\.css(\?.*)?$/.test(id)) {
1254
+ const hasTailwindImport = code.includes('@import "tailwindcss') || code.includes("@import 'tailwindcss");
1255
+ if (hasTailwindImport && state.classes.size > 0) {
1256
+ const candidates = Array.from(state.classes).filter((c) => c.length >= 2 && /^[a-z]/.test(c)).join(" ");
1257
+ if (candidates) {
1258
+ const safelistPath = path.join(state.rootDir, SAFELIST_FILENAME).replace(/\\/g, "/");
1259
+ const cssDir = path.dirname(id).replace(/\\/g, "/");
1260
+ let relPath = path.posix.relative(cssDir, safelistPath);
1261
+ if (!relPath.startsWith(".")) {
1262
+ relPath = `./${relPath}`;
1263
+ }
1264
+ const sourceDirective = `@source "${relPath}";
1134
1265
  `;
1135
- const transformed2 = code.replace(
1136
- /(@import\s+["']tailwindcss[^"']*["'];)/,
1137
- `$1
1266
+ const transformed2 = code.replace(
1267
+ /(@import\s+["']tailwindcss[^"']*["'];)/,
1268
+ `$1
1138
1269
  ${sourceDirective}`
1139
- );
1140
- if (transformed2 !== code) {
1141
- return { code: transformed2, map: null };
1270
+ );
1271
+ if (transformed2 !== code) {
1272
+ return { code: transformed2, map: null };
1273
+ }
1142
1274
  }
1143
1275
  }
1276
+ return null;
1144
1277
  }
1145
- return null;
1146
- }
1147
- let transformedCode = code;
1148
- let usesRuntime = false;
1149
- let usesMerge = false;
1150
- let usesColorVar = false;
1151
- let transformed = false;
1152
- let szClasses;
1153
- const hasSzProp = code.includes("sz=") || /\bsz\s*:\s*["'{]/.test(code) || code.includes('sz: "');
1154
- if (hasSzProp) {
1155
- if (id.endsWith(".vue")) {
1156
- const result = vuePreprocess(code, options);
1157
- if (result.transformed) {
1158
- transformedCode = result.code;
1159
- transformed = true;
1160
- }
1161
- } else if (id.endsWith(".svelte")) {
1162
- const result = sveltePreprocess(code, options);
1163
- if (result) {
1278
+ let transformedCode = code;
1279
+ let usesRuntime = false;
1280
+ let usesMerge = false;
1281
+ let usesColorVar = false;
1282
+ let transformed = false;
1283
+ let szClasses;
1284
+ const hasSzProp = code.includes("sz=") || /\bsz\s*:\s*["'{]/.test(code) || code.includes('sz: "');
1285
+ if (hasSzProp) {
1286
+ if (id.endsWith(".vue")) {
1287
+ const result = preprocess(code, options);
1288
+ if (result.transformed) {
1289
+ transformedCode = result.code;
1290
+ transformed = true;
1291
+ }
1292
+ } else if (id.endsWith(".svelte")) {
1293
+ const result = preprocess$1(code, options);
1294
+ if (result) {
1295
+ transformedCode = result.code;
1296
+ transformed = true;
1297
+ }
1298
+ } else {
1299
+ const result = transformConfiguredSource(code, id);
1164
1300
  transformedCode = result.code;
1165
- transformed = true;
1166
- }
1167
- } else {
1168
- const result = transformSourceCode(code, id, { astBudget: astBudgetOverride });
1169
- transformedCode = result.code;
1170
- usesRuntime = result.usesRuntime;
1171
- usesMerge = result.usesMerge;
1172
- usesColorVar = result.usesColorVar;
1173
- transformed = result.transformed;
1174
- szClasses = result.classes;
1175
- if (result.diagnostics.length > 0 && process.env.NODE_ENV !== "production") {
1176
- for (const msg of result.diagnostics) {
1177
- this.warn(`[csszyx] ${id}
1301
+ usesRuntime = result.usesRuntime;
1302
+ usesMerge = result.usesMerge;
1303
+ usesColorVar = result.usesColorVar;
1304
+ transformed = result.transformed;
1305
+ szClasses = result.classes;
1306
+ if (result.diagnostics.length > 0 && process.env.NODE_ENV !== "production") {
1307
+ for (const msg of result.diagnostics) {
1308
+ this.warn(`[csszyx] ${id}
1178
1309
  ${msg}`);
1310
+ }
1311
+ }
1312
+ for (const [token, data] of result.recoveryTokens) {
1313
+ state.recoveryTokens.set(token, data);
1179
1314
  }
1180
- }
1181
- for (const [token, data] of result.recoveryTokens) {
1182
- state.recoveryTokens.set(token, data);
1183
1315
  }
1184
1316
  }
1185
- }
1186
- if (transformedCode.includes("<html") && /layout|Root|Document|app\\.tsx?$/i.test(id)) {
1187
- const attrName = options.production?.minify ? "data-sz-cs" : "data-sz-checksum";
1188
- transformedCode = transformedCode.replace(/<html([^>]*)>/i, `<html$1 ${attrName}="${CHECKSUM_PLACEHOLDER}">`);
1189
- const debugScript = `<script dangerouslySetInnerHTML={{__html: \`(function(){var m=${MANGLE_MAP_PLACEHOLDER};var r={};for(var k in m)r[m[k]]=k;window.__csszyx={mangleMap:m,checksum:"${CHECKSUM_PLACEHOLDER}",decode:function(c){return r[c]},encode:function(c){return m[c]},decodeAll:function(el){return(el.className||"").split(" ").map(function(c){return r[c]||c})}}})()\`}} />`;
1190
- if (transformedCode.includes("<body")) {
1317
+ if (transformedCode.includes("<html") && /layout|Root|Document|app\\.tsx?$/i.test(id)) {
1318
+ const attrName = options.production?.minify ? "data-sz-cs" : "data-sz-checksum";
1191
1319
  transformedCode = transformedCode.replace(
1192
- /(<body[^>]*>)/i,
1193
- `$1${debugScript}`
1320
+ /<html([^>]*)>/i,
1321
+ `<html$1 ${attrName}="${CHECKSUM_PLACEHOLDER}">`
1194
1322
  );
1195
- }
1196
- transformed = true;
1197
- }
1198
- {
1199
- const imports = [];
1200
- if (usesRuntime) {
1201
- imports.push("_sz");
1202
- }
1203
- if (usesMerge) {
1204
- imports.push("_szMerge");
1205
- }
1206
- if (usesColorVar) {
1207
- imports.push("__szColorVar");
1208
- }
1209
- const needed = imports.filter(
1210
- (name) => !new RegExp(`\\{[^}]*\\b${name}\\b[^}]*\\}\\s*from\\s*['"]@csszyx/runtime['"]`).test(transformedCode)
1211
- );
1212
- if (needed.length > 0) {
1213
- const existingImport = transformedCode.match(/^(import\s*\{[^}]*)\}\s*from\s*'@csszyx\/runtime'/m);
1214
- if (existingImport) {
1323
+ const debugScript = `<script dangerouslySetInnerHTML={{__html: \`(function(){var m=${MANGLE_MAP_PLACEHOLDER};var r={};for(var k in m)r[m[k]]=k;window.__csszyx={mangleMap:m,checksum:"${CHECKSUM_PLACEHOLDER}",decode:function(c){return r[c]},encode:function(c){return m[c]},decodeAll:function(el){return(el.className||"").split(" ").map(function(c){return r[c]||c})}}})()\`}} />`;
1324
+ if (transformedCode.includes("<body")) {
1215
1325
  transformedCode = transformedCode.replace(
1216
- existingImport[0],
1217
- `${existingImport[1]}, ${needed.join(", ")} } from '@csszyx/runtime'`
1326
+ /(<body[^>]*>)/i,
1327
+ `$1${debugScript}`
1218
1328
  );
1219
- } else {
1220
- const importStmt = `import { ${needed.join(", ")} } from '@csszyx/runtime';
1221
- `;
1222
- const directiveMatch = transformedCode.match(/^['"]use (client|server)['"];?\s*/);
1223
- if (directiveMatch) {
1224
- const directive = directiveMatch[0];
1225
- transformedCode = transformedCode.replace(directive, `${directive}${importStmt}`);
1226
- } else {
1227
- transformedCode = `${importStmt}${transformedCode}`;
1228
- }
1229
1329
  }
1230
1330
  transformed = true;
1231
1331
  }
1232
- }
1233
- if (/\.[tj]sx?(\?.*)?$/.test(id)) {
1234
- assertNoRSCBoundaryViolation(transformedCode, id);
1235
- const record = createRSCModuleRecord(transformedCode, id);
1236
- state.rscModules.set(record.id, record);
1237
- }
1238
- if (transformed || transformedCode.includes("class=") || transformedCode.includes("className=")) {
1239
- if (szClasses !== void 0) {
1240
- for (const cls of szClasses) {
1241
- state.classes.add(cls);
1332
+ {
1333
+ const imports = [];
1334
+ if (usesRuntime) {
1335
+ imports.push("_sz");
1336
+ }
1337
+ if (usesMerge) {
1338
+ imports.push("_szMerge");
1339
+ }
1340
+ if (usesColorVar) {
1341
+ imports.push("__szColorVar");
1342
+ }
1343
+ const needed = imports.filter(
1344
+ (name) => !new RegExp(
1345
+ `\\{[^}]*\\b${name}\\b[^}]*\\}\\s*from\\s*['"]@csszyx/runtime['"]`
1346
+ ).test(transformedCode)
1347
+ );
1348
+ if (needed.length > 0) {
1349
+ const existingImport = transformedCode.match(
1350
+ /^(import\s*\{[^}]*)\}\s*from\s*'@csszyx\/runtime'/m
1351
+ );
1352
+ if (existingImport) {
1353
+ transformedCode = transformedCode.replace(
1354
+ existingImport[0],
1355
+ `${existingImport[1]}, ${needed.join(", ")} } from '@csszyx/runtime'`
1356
+ );
1357
+ } else {
1358
+ const importStmt = `import { ${needed.join(", ")} } from '@csszyx/runtime';
1359
+ `;
1360
+ const directiveMatch = transformedCode.match(
1361
+ /^['"]use (client|server)['"];?\s*/
1362
+ );
1363
+ if (directiveMatch) {
1364
+ const directive = directiveMatch[0];
1365
+ transformedCode = transformedCode.replace(
1366
+ directive,
1367
+ `${directive}${importStmt}`
1368
+ );
1369
+ } else {
1370
+ transformedCode = `${importStmt}${transformedCode}`;
1371
+ }
1372
+ }
1373
+ transformed = true;
1242
1374
  }
1243
- } else {
1244
- extractClasses(transformedCode);
1245
1375
  }
1246
- return { code: transformedCode, map: null };
1247
- }
1248
- return null;
1249
- },
1250
- /** Finalizes the mangle map after all source modules have been processed. */
1251
- buildEnd() {
1252
- finalizeMangleMap();
1253
- assertNoRSCGraphViolation(state.rscModules);
1254
- if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
1255
- globalThis.__csszyx_ssr_mangle_map = state.mangleMap;
1256
- }
1257
- },
1258
- /**
1259
- * Webpack hook: pre-scans source files before compilation for Tailwind class discovery.
1260
- * @param compiler - the Webpack compiler instance
1261
- */
1262
- webpack(compiler) {
1263
- compiler.hooks.beforeCompile.tap("csszyx:prescan", () => {
1264
- const root = compiler.context || process.cwd();
1265
- state.rootDir = root;
1266
- if (state.classes.size === 0) {
1267
- prescanAndWriteClasses();
1376
+ if (/\.[tj]sx?(\?.*)?$/.test(id)) {
1377
+ assertNoRSCBoundaryViolation(transformedCode, id);
1378
+ const record = createRSCModuleRecord(transformedCode, id);
1379
+ state.rscModules.set(record.id, record);
1268
1380
  }
1269
- runThemeScan(root, options.build?.scanCss);
1270
- });
1271
- if (options.build?.scanCss) {
1272
- const patterns = Array.isArray(options.build.scanCss) ? options.build.scanCss : [options.build.scanCss];
1273
- compiler.hooks.thisCompilation.tap("csszyx:theme-deps", (compilation) => {
1274
- const root = compiler.context || process.cwd();
1275
- for (const pattern of patterns) {
1276
- const resolved = path2.isAbsolute(pattern) ? pattern : path2.join(root, pattern);
1277
- if (fs2.existsSync(resolved)) {
1278
- compilation.fileDependencies.add(resolved);
1381
+ if (transformed || transformedCode.includes("class=") || transformedCode.includes("className=")) {
1382
+ if (szClasses !== void 0) {
1383
+ for (const cls of szClasses) {
1384
+ state.classes.add(cls);
1279
1385
  }
1386
+ } else {
1387
+ extractClasses(transformedCode);
1280
1388
  }
1281
- });
1282
- }
1283
- },
1284
- vite: {
1285
- /**
1286
- * Vite hook: pre-scans source files when config is resolved.
1287
- * Also runs theme scan to generate .csszyx/theme.d.ts if scanCss is configured.
1288
- * @param config - the resolved Vite configuration object
1289
- */
1290
- configResolved(config) {
1291
- const root = config.root || process.cwd();
1292
- state.rootDir = root;
1293
- prescanAndWriteClasses();
1294
- runThemeScan(root, options.build?.scanCss);
1389
+ return { code: transformedCode, map: null };
1390
+ }
1391
+ return null;
1392
+ },
1393
+ /** Finalizes the mangle map after all source modules have been processed. */
1394
+ buildEnd() {
1395
+ finalizeMangleMap();
1396
+ assertNoRSCGraphViolation(state.rscModules);
1397
+ if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
1398
+ globalThis.__csszyx_ssr_mangle_map = state.mangleMap;
1399
+ }
1295
1400
  },
1296
1401
  /**
1297
- * Vite HMR hook: re-runs theme scan when a watched CSS file changes,
1298
- * and incrementally updates csszyx-classes.html when a source file gains new sz classes.
1299
- * @param ctx - HMR context containing the changed file
1402
+ * Webpack hook: pre-scans source files before compilation for Tailwind class discovery.
1403
+ * @param compiler - the Webpack compiler instance
1300
1404
  */
1301
- handleHotUpdate(ctx) {
1302
- const scanCss = options.build?.scanCss;
1303
- if (scanCss) {
1304
- const patterns = Array.isArray(scanCss) ? scanCss : [scanCss];
1305
- const root = ctx.server.config.root || process.cwd();
1306
- const isWatched = patterns.some((p) => {
1307
- const resolved = path2.isAbsolute(p) ? p : path2.join(root, p);
1308
- return ctx.file === resolved;
1309
- });
1310
- if (isWatched) {
1311
- runThemeScan(root, scanCss);
1405
+ webpack(compiler) {
1406
+ compiler.hooks.beforeCompile.tap("csszyx:prescan", () => {
1407
+ const root = compiler.context || process.cwd();
1408
+ state.rootDir = root;
1409
+ if (state.classes.size === 0) {
1410
+ prescanAndWriteClasses();
1312
1411
  }
1313
- }
1314
- if (!SOURCE_EXTENSIONS.has(path2.extname(ctx.file))) {
1315
- return;
1316
- }
1317
- if (ctx.file.includes("node_modules")) {
1318
- return;
1319
- }
1320
- let fileContent, result;
1321
- try {
1322
- fileContent = fs2.readFileSync(ctx.file, "utf-8");
1323
- } catch {
1324
- return;
1325
- }
1326
- if (!fileContent.includes("sz=") && !/\bsz\s*:\s*["'{]/.test(fileContent)) {
1327
- return;
1328
- }
1329
- try {
1330
- result = transformSourceCode(fileContent, ctx.file, { astBudget: astBudgetOverride });
1331
- } catch {
1332
- return;
1333
- }
1334
- if (!result.transformed) {
1335
- return;
1336
- }
1337
- const sizeBefore = state.classes.size;
1338
- for (const cls of result.classes) {
1339
- state.classes.add(cls);
1340
- }
1341
- for (const [token, data] of result.recoveryTokens) {
1342
- state.recoveryTokens.set(token, data);
1343
- }
1344
- if (state.classes.size > sizeBefore) {
1345
- writeSafelistFile(state.classes);
1346
- const safelistPath = path2.join(state.rootDir, SAFELIST_FILENAME);
1347
- ctx.server.watcher.emit("change", safelistPath);
1412
+ runThemeScan(root, options.build?.scanCss);
1413
+ });
1414
+ if (options.build?.scanCss) {
1415
+ compiler.hooks.thisCompilation.tap("csszyx:theme-deps", (compilation) => {
1416
+ const root = compiler.context || process.cwd();
1417
+ for (const file of expandFilePatterns(root, options.build?.scanCss ?? [])) {
1418
+ compilation.fileDependencies.add(file);
1419
+ }
1420
+ });
1348
1421
  }
1349
1422
  },
1350
- transformIndexHtml: {
1351
- order: "pre",
1423
+ vite: {
1352
1424
  /**
1353
- * Injects hydration data (mangle map + checksum) into the HTML document.
1354
- * Also mangles class attributes in SSR-rendered HTML so they match mangled CSS selectors.
1355
- * @param html - the raw HTML string to transform
1356
- * @returns transformed HTML with injected hydration data
1425
+ * Vite hook: pre-scans source files when config is resolved.
1426
+ * Also runs theme scan to generate .csszyx/theme.d.ts if scanCss is configured.
1427
+ * @param config - the resolved Vite configuration object
1357
1428
  */
1358
- handler(html) {
1359
- finalizeMangleMap();
1360
- let result = transformIndexHtml(html, state.mangleMap, state.checksum, {
1361
- mode: options.production?.injectChecksum === false ? "script" : "script",
1362
- minify: process.env.NODE_ENV === "production"
1363
- });
1364
- if (state.recoveryTokens.size > 0) {
1365
- const isProduction = process.env.NODE_ENV === "production";
1366
- const { manifest, strippedDevOnlyPaths } = buildRecoveryManifest(
1367
- state.recoveryTokens,
1368
- {
1369
- production: isProduction,
1370
- mangleChecksum: state.checksum
1371
- }
1372
- );
1373
- if (strippedDevOnlyPaths.length > 0) {
1374
- console.warn(
1375
- `[csszyx] Stripped ${strippedDevOnlyPaths.length} szRecover="dev-only" token(s) from the production manifest. Recovery for these elements is disabled in production by design. Sites: ${strippedDevOnlyPaths.join(", ")}`
1429
+ configResolved(config) {
1430
+ const root = config.root || process.cwd();
1431
+ state.rootDir = root;
1432
+ prescanAndWriteClasses();
1433
+ runThemeScan(root, options.build?.scanCss);
1434
+ },
1435
+ /**
1436
+ * Vite HMR hook: re-runs theme scan when a watched CSS file changes,
1437
+ * and incrementally updates csszyx-classes.html when a source file gains new sz classes.
1438
+ * @param ctx - HMR context containing the changed file
1439
+ */
1440
+ handleHotUpdate(ctx) {
1441
+ const scanCss = options.build?.scanCss;
1442
+ if (scanCss) {
1443
+ const root = ctx.server.config.root || process.cwd();
1444
+ if (matchesAnyPattern(ctx.file, scanCss, root)) {
1445
+ runThemeScan(root, scanCss);
1446
+ }
1447
+ }
1448
+ if (!shouldProcessSource(ctx.file)) {
1449
+ return;
1450
+ }
1451
+ let fileContent, result;
1452
+ try {
1453
+ fileContent = fs.readFileSync(ctx.file, "utf-8");
1454
+ } catch {
1455
+ return;
1456
+ }
1457
+ if (!fileContent.includes("sz=") && !/\bsz\s*:\s*["'{]/.test(fileContent)) {
1458
+ return;
1459
+ }
1460
+ try {
1461
+ result = transformConfiguredSource(fileContent, ctx.file);
1462
+ } catch {
1463
+ return;
1464
+ }
1465
+ if (!result.transformed) {
1466
+ return;
1467
+ }
1468
+ const sizeBefore = state.classes.size;
1469
+ for (const cls of result.classes) {
1470
+ state.classes.add(cls);
1471
+ }
1472
+ for (const [token, data] of result.recoveryTokens) {
1473
+ state.recoveryTokens.set(token, data);
1474
+ }
1475
+ if (state.classes.size > sizeBefore) {
1476
+ writeSafelistFile(state.classes);
1477
+ const safelistPath = path.join(state.rootDir, SAFELIST_FILENAME);
1478
+ ctx.server.watcher.emit("change", safelistPath);
1479
+ }
1480
+ },
1481
+ transformIndexHtml: {
1482
+ order: "pre",
1483
+ /**
1484
+ * Injects hydration data (mangle map + checksum) into the HTML document.
1485
+ * Also mangles class attributes in SSR-rendered HTML so they match mangled CSS selectors.
1486
+ * @param html - the raw HTML string to transform
1487
+ * @returns transformed HTML with injected hydration data
1488
+ */
1489
+ handler(html) {
1490
+ finalizeMangleMap();
1491
+ let result = transformIndexHtml(html, state.mangleMap, state.checksum, {
1492
+ mode: options.production?.injectChecksum === false ? "script" : "script",
1493
+ minify: process.env.NODE_ENV === "production"
1494
+ });
1495
+ if (state.recoveryTokens.size > 0) {
1496
+ const isProduction = process.env.NODE_ENV === "production";
1497
+ const { manifest, strippedDevOnlyPaths } = buildRecoveryManifest(
1498
+ state.recoveryTokens,
1499
+ {
1500
+ production: isProduction,
1501
+ mangleChecksum: state.checksum
1502
+ }
1376
1503
  );
1504
+ if (strippedDevOnlyPaths.length > 0) {
1505
+ console.warn(
1506
+ `[csszyx] Stripped ${strippedDevOnlyPaths.length} szRecover="dev-only" token(s) from the production manifest. Recovery for these elements is disabled in production by design. Sites: ${strippedDevOnlyPaths.join(", ")}`
1507
+ );
1508
+ }
1509
+ result = injectRecoveryManifest(result, manifest);
1377
1510
  }
1378
- result = injectRecoveryManifest(result, manifest);
1511
+ return result;
1379
1512
  }
1380
- return result;
1381
1513
  }
1382
1514
  }
1383
- }
1384
- }));
1515
+ })
1516
+ );
1385
1517
  const postPlugin = createUnplugin(() => ({
1386
1518
  name: "csszyx:post",
1387
1519
  enforce: "post",
@@ -1393,8 +1525,7 @@ ${sourceDirective}`
1393
1525
  */
1394
1526
  webpack(compiler) {
1395
1527
  compiler.hooks.compilation.tap("csszyx:post", (compilation) => {
1396
- const stage = compiler.webpack?.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE || // eslint-disable-next-line @typescript-eslint/no-explicit-any
1397
- compilation.constructor.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE;
1528
+ const stage = compiler.webpack?.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE || compilation.constructor.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE;
1398
1529
  compilation.hooks.processAssets.tap(
1399
1530
  {
1400
1531
  name: "csszyx:post",
@@ -1434,19 +1565,24 @@ ${sourceDirective}`
1434
1565
  continue;
1435
1566
  }
1436
1567
  } catch (e) {
1437
- if (e && typeof e === "object" && "name" in e && e.name === "CssSyntaxError") {
1438
- } else {
1568
+ if (e && typeof e === "object" && "name" in e && e.name === "CssSyntaxError") ; else {
1439
1569
  throw e;
1440
1570
  }
1441
1571
  }
1442
1572
  } else if (file.endsWith(".html")) {
1443
- const mangledHtml = source.replace(/\bclass="([^"]*)"/g, (_m, cls) => {
1444
- const out = cls.split(/\s+/).filter(Boolean).map((c) => state.mangleMap[c] || c).join(" ");
1445
- return out !== cls ? `class="${out}"` : _m;
1446
- }).replace(/\bclass='([^']*)'/g, (_m, cls) => {
1447
- const out = cls.split(/\s+/).filter(Boolean).map((c) => state.mangleMap[c] || c).join(" ");
1448
- return out !== cls ? `class='${out}'` : _m;
1449
- });
1573
+ const mangledHtml = source.replace(
1574
+ /\bclass="([^"]*)"/g,
1575
+ (_m, cls) => {
1576
+ const out = cls.split(/\s+/).filter(Boolean).map((c) => state.mangleMap[c] || c).join(" ");
1577
+ return out !== cls ? `class="${out}"` : _m;
1578
+ }
1579
+ ).replace(
1580
+ /\bclass='([^']*)'/g,
1581
+ (_m, cls) => {
1582
+ const out = cls.split(/\s+/).filter(Boolean).map((c) => state.mangleMap[c] || c).join(" ");
1583
+ return out !== cls ? `class='${out}'` : _m;
1584
+ }
1585
+ );
1450
1586
  if (mangledHtml !== source) {
1451
1587
  compilation.updateAsset(
1452
1588
  file,
@@ -1515,8 +1651,7 @@ ${sourceDirective}`
1515
1651
  chunk.source = result.css;
1516
1652
  }
1517
1653
  } catch (e) {
1518
- if (e && typeof e === "object" && "name" in e && e.name === "CssSyntaxError") {
1519
- } else {
1654
+ if (e && typeof e === "object" && "name" in e && e.name === "CssSyntaxError") ; else {
1520
1655
  throw e;
1521
1656
  }
1522
1657
  }
@@ -1542,13 +1677,13 @@ ${sourceDirective}`
1542
1677
  }));
1543
1678
  return { prePlugin, postPlugin };
1544
1679
  }
1545
- var defaultInstance = createCsszyxPlugins();
1546
- var unplugin = defaultInstance.prePlugin;
1547
- var vitePlugin = (options = {}) => {
1680
+ const defaultInstance = createCsszyxPlugins();
1681
+ const unplugin = defaultInstance.prePlugin;
1682
+ const vitePlugin = (options = {}) => {
1548
1683
  const { prePlugin, postPlugin } = createCsszyxPlugins(options);
1549
1684
  return [prePlugin.vite(options), postPlugin.vite(options)];
1550
1685
  };
1551
- var webpackPlugin = (options = {}) => {
1686
+ const webpackPlugin = (options = {}) => {
1552
1687
  const { prePlugin, postPlugin } = createCsszyxPlugins(options);
1553
1688
  return {
1554
1689
  /**
@@ -1561,11 +1696,14 @@ var webpackPlugin = (options = {}) => {
1561
1696
  }
1562
1697
  };
1563
1698
  };
1564
- var rollupPlugin = (options = {}) => {
1699
+ const rollupPlugin = (options = {}) => {
1565
1700
  const { prePlugin, postPlugin } = createCsszyxPlugins(options);
1566
- return [prePlugin.rollup(options), postPlugin.rollup(options)];
1701
+ return [
1702
+ prePlugin.rollup(options),
1703
+ postPlugin.rollup(options)
1704
+ ];
1567
1705
  };
1568
- var esbuildPlugin = (options = {}) => {
1706
+ const esbuildPlugin = (options = {}) => {
1569
1707
  const { prePlugin, postPlugin } = createCsszyxPlugins(options);
1570
1708
  return {
1571
1709
  name: "csszyx",
@@ -1581,22 +1719,4 @@ var esbuildPlugin = (options = {}) => {
1581
1719
  };
1582
1720
  };
1583
1721
 
1584
- export {
1585
- hasUseServerDirective,
1586
- hasUseClientDirective,
1587
- isRSCServerModule,
1588
- findRSCBoundaryViolation,
1589
- createRSCModuleRecord,
1590
- findRSCGraphViolation,
1591
- assertNoRSCGraphViolation,
1592
- assertNoRSCBoundaryViolation,
1593
- parseThemeBlocks,
1594
- mergeThemes,
1595
- hasTokens,
1596
- mangleCodeClassesSync,
1597
- unplugin,
1598
- vitePlugin,
1599
- webpackPlugin,
1600
- rollupPlugin,
1601
- esbuildPlugin
1602
- };
1722
+ export { assertNoRSCBoundaryViolation as a, assertNoRSCGraphViolation as b, createRSCModuleRecord as c, findRSCGraphViolation as d, esbuildPlugin as e, findRSCBoundaryViolation as f, hasUseClientDirective as g, hasTokens as h, hasUseServerDirective as i, isRSCServerModule as j, mergeThemes as k, mangleCodeClassesSync as m, parseThemeBlocks as p, rollupPlugin as r, unplugin as u, vitePlugin as v, webpackPlugin as w };