@csszyx/unplugin 0.9.10 → 0.10.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.
@@ -13,6 +13,7 @@ const types = require('@csszyx/types');
13
13
  const vueAdapter = require('@csszyx/vue-adapter');
14
14
  const unplugin$1 = require('unplugin');
15
15
  const cssMangler = require('../css-mangler.cjs');
16
+ const htmlEscape = require('./unplugin.DoBHTRra.cjs');
16
17
  const node_crypto = require('node:crypto');
17
18
  const transformCache = require('./unplugin.pmTknYLy.cjs');
18
19
  const postcss = require('postcss');
@@ -474,15 +475,7 @@ const CLIENT_RUNTIME_MODULES = /* @__PURE__ */ new Set(["csszyx/browser"]);
474
475
  const CLIENT_RUNTIME_MODULE_ROOTS = ["@csszyx/dynamic", "csszyx/dynamic"];
475
476
  const normalizedModuleIdCache = /* @__PURE__ */ new Map();
476
477
  const resolvedLocalModuleCache = /* @__PURE__ */ new Map();
477
- const FORBIDDEN_SYMBOLS = /* @__PURE__ */ new Set([
478
- "_sz",
479
- "_sz2",
480
- "_sz3",
481
- "_szIf",
482
- "_szMerge",
483
- "_szSwitch",
484
- "__csszyx_runtime__"
485
- ]);
478
+ const FORBIDDEN_SYMBOLS = /* @__PURE__ */ new Set(["_sz", "_sz2", "_sz3", "_szMerge", "__csszyx_runtime__"]);
486
479
  function hasUseServerDirective(code) {
487
480
  for (const statement of readDirectivePrologue(code)) {
488
481
  if (SERVER_DIRECTIVE_RE.test(statement)) {
@@ -861,7 +854,10 @@ function resolveLocalModule(importer, source) {
861
854
  const cacheKey = `${importer}\0${source}`;
862
855
  const cached = resolvedLocalModuleCache.get(cacheKey);
863
856
  if (cached) {
864
- return cached;
857
+ if (fs__namespace.existsSync(cached)) {
858
+ return cached;
859
+ }
860
+ resolvedLocalModuleCache.delete(cacheKey);
865
861
  }
866
862
  const base = source.startsWith("/") ? source : path__namespace.resolve(path__namespace.dirname(importer), source);
867
863
  const candidates = [
@@ -1026,7 +1022,14 @@ function stripCommentsForImportScan(code) {
1026
1022
  return out;
1027
1023
  }
1028
1024
 
1029
- const EMPTY_THEME = { colors: [], spacings: [], fonts: [], radii: [], shadows: [] };
1025
+ const EMPTY_THEME = {
1026
+ colors: [],
1027
+ spacings: [],
1028
+ fonts: [],
1029
+ radii: [],
1030
+ shadows: [],
1031
+ breakpoints: []
1032
+ };
1030
1033
  function stripLayerWrappers(css) {
1031
1034
  let result = "";
1032
1035
  let i = 0;
@@ -1094,12 +1097,15 @@ function categorizeProperty(prop) {
1094
1097
  ["spacing-", "spacings"],
1095
1098
  ["font-", "fonts"],
1096
1099
  ["radius-", "radii"],
1097
- ["shadow-", "shadows"]
1100
+ ["shadow-", "shadows"],
1101
+ ["breakpoint-", "breakpoints"]
1098
1102
  ];
1099
1103
  for (const [prefix, category] of categoryMap) {
1100
1104
  if (prop.startsWith(prefix)) {
1101
1105
  let token = prop.slice(prefix.length);
1102
- token = token.replace(/-\d+$/, "");
1106
+ if (category !== "breakpoints") {
1107
+ token = token.replace(/-\d+$/, "");
1108
+ }
1103
1109
  if (token) {
1104
1110
  return { category, token };
1105
1111
  }
@@ -1113,7 +1119,8 @@ function parseThemeBlocks(cssContent) {
1113
1119
  spacings: /* @__PURE__ */ new Set(),
1114
1120
  fonts: /* @__PURE__ */ new Set(),
1115
1121
  radii: /* @__PURE__ */ new Set(),
1116
- shadows: /* @__PURE__ */ new Set()
1122
+ shadows: /* @__PURE__ */ new Set(),
1123
+ breakpoints: /* @__PURE__ */ new Set()
1117
1124
  };
1118
1125
  const stripped = stripLayerWrappers(cssContent);
1119
1126
  const blocks = extractThemeBlocks(stripped);
@@ -1131,7 +1138,8 @@ function parseThemeBlocks(cssContent) {
1131
1138
  spacings: [...result.spacings].sort(),
1132
1139
  fonts: [...result.fonts].sort(),
1133
1140
  radii: [...result.radii].sort(),
1134
- shadows: [...result.shadows].sort()
1141
+ shadows: [...result.shadows].sort(),
1142
+ breakpoints: [...result.breakpoints].sort()
1135
1143
  };
1136
1144
  }
1137
1145
  function mergeThemes(themes) {
@@ -1143,7 +1151,8 @@ function mergeThemes(themes) {
1143
1151
  spacings: /* @__PURE__ */ new Set(),
1144
1152
  fonts: /* @__PURE__ */ new Set(),
1145
1153
  radii: /* @__PURE__ */ new Set(),
1146
- shadows: /* @__PURE__ */ new Set()
1154
+ shadows: /* @__PURE__ */ new Set(),
1155
+ breakpoints: /* @__PURE__ */ new Set()
1147
1156
  };
1148
1157
  for (const theme of themes) {
1149
1158
  for (const cat of Object.keys(merged)) {
@@ -1157,7 +1166,8 @@ function mergeThemes(themes) {
1157
1166
  spacings: [...merged.spacings].sort(),
1158
1167
  fonts: [...merged.fonts].sort(),
1159
1168
  radii: [...merged.radii].sort(),
1160
- shadows: [...merged.shadows].sort()
1169
+ shadows: [...merged.shadows].sort(),
1170
+ breakpoints: [...merged.breakpoints].sort()
1161
1171
  };
1162
1172
  }
1163
1173
  function hasTokens(theme) {
@@ -1445,11 +1455,15 @@ function isSameFileVersion(before, after) {
1445
1455
  return before.dev === after.dev && before.ino === after.ino && before.size === after.size && before.mtimeNs === after.mtimeNs && before.ctimeNs === after.ctimeNs;
1446
1456
  }
1447
1457
 
1458
+ function escapeTsString(value) {
1459
+ return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\r/g, "\\r").replace(/\n/g, "\\n");
1460
+ }
1448
1461
  function generateThemeDts(opts) {
1449
1462
  const { theme, sourceFiles } = opts;
1450
1463
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1451
- const sources = sourceFiles.join(", ");
1452
- const toUnion = (tokens) => tokens.map((t) => `'${t}'`).join(" | ");
1464
+ const sources = sourceFiles.join(", ").replace(/[\r\n]/g, " ");
1465
+ const toUnion = (tokens) => tokens.map((t) => `'${escapeTsString(t)}'`).join(" | ");
1466
+ const quoteKey = (key) => /^[a-z_$][\w$]*$/i.test(key) ? key : `'${escapeTsString(key)}'`;
1453
1467
  const entries = [];
1454
1468
  if (theme.colors.length > 0) {
1455
1469
  entries.push(` colors: ${toUnion(theme.colors)};`);
@@ -1466,19 +1480,36 @@ function generateThemeDts(opts) {
1466
1480
  if (theme.shadows.length > 0) {
1467
1481
  entries.push(` shadows: ${toUnion(theme.shadows)};`);
1468
1482
  }
1469
- return [
1470
- "// Auto-generated by csszyx theme-scanner \u2014 DO NOT EDIT",
1471
- `// Source: ${sources}`,
1472
- `// Updated: ${timestamp}`,
1473
- "",
1474
- "declare module '@csszyx/compiler' {",
1483
+ const moduleBody = [
1475
1484
  " /**",
1476
1485
  " * Custom design tokens extracted from @theme blocks.",
1477
1486
  " * These tokens are surfaced in sz prop IntelliSense.",
1478
1487
  " */",
1479
1488
  " interface CustomTheme {",
1480
1489
  ...entries,
1481
- " }",
1490
+ " }"
1491
+ ];
1492
+ if (theme.breakpoints.length > 0) {
1493
+ const variantEntries = theme.breakpoints.map(
1494
+ (bp) => ` ${quoteKey(bp)}?: SzPropsBase;`
1495
+ );
1496
+ moduleBody.push(
1497
+ " /**",
1498
+ " * Custom responsive breakpoints from @theme (--breakpoint-*),",
1499
+ " * surfaced as typed sz variant keys.",
1500
+ " */",
1501
+ " interface VariantModifiers {",
1502
+ ...variantEntries,
1503
+ " }"
1504
+ );
1505
+ }
1506
+ return [
1507
+ "// Auto-generated by csszyx theme-scanner \u2014 DO NOT EDIT",
1508
+ `// Source: ${sources}`,
1509
+ `// Updated: ${timestamp}`,
1510
+ "",
1511
+ "declare module '@csszyx/compiler' {",
1512
+ ...moduleBody,
1482
1513
  "}",
1483
1514
  "",
1484
1515
  "export {};",
@@ -1552,6 +1583,7 @@ const UNKNOWN_PACKAGE_VERSION = "0.0.0";
1552
1583
  const TRANSFORM_CACHE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
1553
1584
  const TRANSFORM_CACHE_MAX_ENTRIES = 1e4;
1554
1585
  const TRANSFORM_MEMORY_CACHE_MAX_ENTRIES = 1e3;
1586
+ const MAX_SAFELIST_CLASSES = 1e5;
1555
1587
  const DEFAULT_VAR_MANGLE_MAP_MAX_BYTES = 100 * 1024;
1556
1588
  const GLOBAL_VAR_ALIAS_MAP_OWNER = "\0csszyx:global-var-aliases";
1557
1589
  const DIRECTIVE_PROLOGUE_PREFIX_RE = /^((?:\s|\/\/[^\n]*\n|\/\*(?:[^*]|\*(?!\/))*\*\/)*)(['"]use (?:client|server)['"];?\s*)/;
@@ -1562,9 +1594,9 @@ const RUNTIME_HELPER_IMPORT_RE = {
1562
1594
  };
1563
1595
  let _hasWarnedTsConfig = false;
1564
1596
  let _hasWarnedTransformCacheVersion = false;
1565
- 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.BT-U5kd1.cjs', document.baseURI).href)));
1597
+ 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.Dk6ddrZX.cjs', document.baseURI).href)));
1566
1598
  const PLUGIN_VERSION = findPackageVersionFromFile(
1567
- node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/unplugin.BT-U5kd1.cjs', document.baseURI).href))),
1599
+ node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/unplugin.Dk6ddrZX.cjs', document.baseURI).href))),
1568
1600
  UNKNOWN_PACKAGE_VERSION
1569
1601
  );
1570
1602
  const COMPILER_VERSION = findPackageVersionFromModule("@csszyx/compiler", UNKNOWN_PACKAGE_VERSION);
@@ -1592,6 +1624,133 @@ function resolveNativeCacheIdentity() {
1592
1624
  }
1593
1625
  const BENCH_TRACE_ENABLED = process.env.CSSZYX_BENCH_TRACE === "1";
1594
1626
  const BENCH_TRACE_FILE = process.env.CSSZYX_BENCH_TRACE_FILE;
1627
+ function appendTailwindSourceDirective(code, relPath) {
1628
+ const directive = `@source "${relPath}";`;
1629
+ if (code.includes(directive)) {
1630
+ return null;
1631
+ }
1632
+ const separator = code.length === 0 || code.endsWith("\n") ? "" : "\n";
1633
+ return `${code}${separator}${directive}
1634
+ `;
1635
+ }
1636
+ function stripCssBlockComments(code) {
1637
+ const SLASH = 47;
1638
+ const STAR = 42;
1639
+ let out = "";
1640
+ let last = 0;
1641
+ let i = 0;
1642
+ const n = code.length;
1643
+ while (i < n) {
1644
+ if (code.charCodeAt(i) === SLASH && code.charCodeAt(i + 1) === STAR) {
1645
+ out += code.slice(last, i);
1646
+ i += 2;
1647
+ while (i < n && !(code.charCodeAt(i) === STAR && code.charCodeAt(i + 1) === SLASH)) {
1648
+ i++;
1649
+ }
1650
+ i += 2;
1651
+ last = i;
1652
+ } else {
1653
+ i++;
1654
+ }
1655
+ }
1656
+ return out + code.slice(last);
1657
+ }
1658
+ function cssImportsTailwind(code) {
1659
+ const withoutBlockComments = stripCssBlockComments(code);
1660
+ return /@import\s+["']tailwindcss(?:\/[^"']*)?["']/.test(withoutBlockComments);
1661
+ }
1662
+ function hasInjectableTailwindCandidate(classes) {
1663
+ for (const c of classes) {
1664
+ if (c.length >= 2 && /^[a-z]/.test(c)) {
1665
+ return true;
1666
+ }
1667
+ }
1668
+ return false;
1669
+ }
1670
+ function shouldWarnMissingTailwindEntry(ownedClassCount, sawTailwindEntry) {
1671
+ return ownedClassCount > 0 && !sawTailwindEntry;
1672
+ }
1673
+ function missingTailwindEntryMessage(ownedClassCount) {
1674
+ return `[csszyx] generated ${ownedClassCount} sz class(es) but found no CSS entry importing "tailwindcss" \u2014 those classes will produce no CSS. Import "tailwindcss" in a CSS file (csszyx auto-injects @source for the generated classes) so Tailwind emits their styles.`;
1675
+ }
1676
+ function cssHasContentScope(code) {
1677
+ const s = stripCssBlockComments(code);
1678
+ return /@import\s+["']tailwindcss(?:\/[^"']*)?["']\s+source\(/.test(s) || /@source\s+not\b/.test(s);
1679
+ }
1680
+ function isMonorepoPackage(root) {
1681
+ let dir = path__namespace.dirname(path__namespace.resolve(root));
1682
+ const { root: fsRoot } = path__namespace.parse(dir);
1683
+ while (dir !== fsRoot) {
1684
+ if (fs__namespace.existsSync(path__namespace.join(dir, "pnpm-workspace.yaml")) || fs__namespace.existsSync(path__namespace.join(dir, "nx.json")) || fs__namespace.existsSync(path__namespace.join(dir, "lerna.json"))) {
1685
+ return true;
1686
+ }
1687
+ const pkgPath = path__namespace.join(dir, "package.json");
1688
+ if (fs__namespace.existsSync(pkgPath)) {
1689
+ try {
1690
+ if ("workspaces" in JSON.parse(fs__namespace.readFileSync(pkgPath, "utf8"))) {
1691
+ return true;
1692
+ }
1693
+ } catch {
1694
+ }
1695
+ }
1696
+ dir = path__namespace.dirname(dir);
1697
+ }
1698
+ return false;
1699
+ }
1700
+ function shouldWarnUnscopedMonorepo(sawTailwindEntry, tailwindEntryScoped, inMonorepo) {
1701
+ return sawTailwindEntry && !tailwindEntryScoped && inMonorepo;
1702
+ }
1703
+ function unscopedMonorepoMessage() {
1704
+ return '[csszyx] Tailwind content detection is UNSCOPED in a monorepo. Tailwind v4 climbs to the workspace root and scans sibling packages + docs (.md/.mdx/.txt are not ignored), which can generate phantom or broken url() classes and fail the build. Scope it in your Tailwind CSS entry:\n @import "tailwindcss" source(none);\n @source "."; /* this package, relative to the CSS file */\ncsszyx auto-injects @source for its generated classes, so only your own templates need listing. Guide: https://csszyx.com/docs/monorepo-content-scope/\nSilence (if a broad scan is intentional): csszyx({ contentScopeCheck: false }).';
1705
+ }
1706
+ function isCompilePackageOptedIn(id, compilePackages) {
1707
+ const path2 = id.replace(/\\/g, "/");
1708
+ if (path2.includes("node_modules")) {
1709
+ return false;
1710
+ }
1711
+ return compilePackages.some((name) => path2.includes(`/packages/${name}/`));
1712
+ }
1713
+ function isHardIgnoredPath(id, compilePackages = []) {
1714
+ const path2 = id.replace(/\\/g, "/");
1715
+ if (path2.includes("node_modules")) {
1716
+ return true;
1717
+ }
1718
+ if (path2.includes(".next") && !path2.includes("static")) {
1719
+ return true;
1720
+ }
1721
+ if (path2.includes("/packages/")) {
1722
+ return !isCompilePackageOptedIn(path2, compilePackages);
1723
+ }
1724
+ return false;
1725
+ }
1726
+ function isPackagesSkippedSource(id, compilePackages = []) {
1727
+ const path2 = id.replace(/\\/g, "/");
1728
+ if (path2.includes("node_modules")) {
1729
+ return false;
1730
+ }
1731
+ if (path2.includes(".next") && !path2.includes("static")) {
1732
+ return false;
1733
+ }
1734
+ if (!path2.includes("/packages/")) {
1735
+ return false;
1736
+ }
1737
+ return !isCompilePackageOptedIn(path2, compilePackages);
1738
+ }
1739
+ function skippedSzFilesMessage(files) {
1740
+ const list = files.map((file) => ` - ${file}`).join("\n");
1741
+ return `[csszyx] ${files.length} file(s) under packages/ contain \`sz\` but were skipped by ignore rules:
1742
+ ${list}
1743
+ Add the package to \`compilePackages\` (or move the file out of packages/) \u2014 otherwise their \`sz\` produces no CSS.`;
1744
+ }
1745
+ function computeSafelistRelPath(rootDir, safelistFilename, cssId) {
1746
+ const safelistPath = path__namespace.join(rootDir, safelistFilename).replace(/\\/g, "/");
1747
+ const cssDir = path__namespace.dirname(cssId).replace(/\\/g, "/");
1748
+ let relPath = path__namespace.posix.relative(cssDir, safelistPath);
1749
+ if (!relPath.startsWith(".")) {
1750
+ relPath = `./${relPath}`;
1751
+ }
1752
+ return relPath;
1753
+ }
1595
1754
  function cssVariableEntries(result) {
1596
1755
  const entries = [];
1597
1756
  for (const [original, value] of result.cssVariableMap ?? []) {
@@ -2215,6 +2374,7 @@ function createCsszyxPlugins(options = {}) {
2215
2374
  "[csszyx] Transform cache disabled because package versions could not be resolved."
2216
2375
  );
2217
2376
  }
2377
+ const compilePackages = options.compilePackages ?? [];
2218
2378
  const parserOverride = process.env.CSSZYX_PARSER;
2219
2379
  const defaultParser = types.DEFAULT_BUILD_CONFIG.parser ?? "rust";
2220
2380
  const parserMode = parserOverride === "babel" || parserOverride === "oxc" || parserOverride === "rust" ? parserOverride : options.build?.parser ?? defaultParser;
@@ -2222,6 +2382,15 @@ function createCsszyxPlugins(options = {}) {
2222
2382
  const transformMemoryCache = /* @__PURE__ */ new Map();
2223
2383
  const state = {
2224
2384
  classes: /* @__PURE__ */ new Set(),
2385
+ sawTailwindEntry: false,
2386
+ tailwindWarningEmitted: false,
2387
+ tailwindEntryScoped: false,
2388
+ contentScopeWarningEmitted: false,
2389
+ spreadWarnings: /* @__PURE__ */ new Set(),
2390
+ skippedSzFiles: /* @__PURE__ */ new Set(),
2391
+ skipWarningEmitted: false,
2392
+ classesCapped: false,
2393
+ ownedClasses: /* @__PURE__ */ new Set(),
2225
2394
  mangleMap: {},
2226
2395
  varMangleEntriesByFile: /* @__PURE__ */ new Map(),
2227
2396
  varMangleMap: Object.fromEntries(earlyGlobalVarAliasEntries),
@@ -2282,7 +2451,7 @@ function createCsszyxPlugins(options = {}) {
2282
2451
  return result;
2283
2452
  }
2284
2453
  function isHardIgnored(id) {
2285
- return id.includes("node_modules") || id.includes("/packages/") || id.includes(".next") && !id.includes("static");
2454
+ return isHardIgnoredPath(id, compilePackages);
2286
2455
  }
2287
2456
  function shouldProcessSource(id) {
2288
2457
  return !isHardIgnored(id) && !isUserExcluded(id) && isUserIncluded(id) && (/\.[tj]sx?(\?.*)?$/.test(id) || id.endsWith(".vue") || id.endsWith(".svelte"));
@@ -2488,12 +2657,19 @@ function createCsszyxPlugins(options = {}) {
2488
2657
  maxEntries: TRANSFORM_CACHE_MAX_ENTRIES
2489
2658
  });
2490
2659
  }
2660
+ function addSafelistClass(cls) {
2661
+ if (state.classes.size >= MAX_SAFELIST_CLASSES) {
2662
+ state.classesCapped = true;
2663
+ return;
2664
+ }
2665
+ state.classes.add(cls);
2666
+ }
2491
2667
  function writeSafelistFile(classes) {
2492
2668
  if (classes.size === 0) {
2493
2669
  return;
2494
2670
  }
2495
2671
  const safelistPath = path__namespace.join(state.rootDir, SAFELIST_FILENAME);
2496
- const classList = Array.from(classes).join(" ");
2672
+ const classList = htmlEscape.escapeHtmlAttribute(Array.from(classes).join(" "));
2497
2673
  const content = `<!-- Auto-generated by csszyx \u2014 DO NOT EDIT -->
2498
2674
  <!-- Tailwind CSS scans this file for class name detection -->
2499
2675
  <div class="${classList}"><div class="${classList}">x</div><div class="${classList}">x</div></div>
@@ -2513,6 +2689,20 @@ function createCsszyxPlugins(options = {}) {
2513
2689
  } catch {
2514
2690
  }
2515
2691
  }
2692
+ function recordPackagesSkipIfSz(filePath) {
2693
+ if (!isPackagesSkippedSource(filePath, compilePackages)) {
2694
+ return;
2695
+ }
2696
+ let content;
2697
+ try {
2698
+ content = fs__namespace.readFileSync(filePath, "utf-8");
2699
+ } catch {
2700
+ return;
2701
+ }
2702
+ if (content.includes("sz=") || content.includes("sz:")) {
2703
+ state.skippedSzFiles.add(filePath);
2704
+ }
2705
+ }
2516
2706
  function prescanAndWriteClasses() {
2517
2707
  const prescanStarted = node_perf_hooks.performance.now();
2518
2708
  const discoveredClasses = /* @__PURE__ */ new Set();
@@ -2533,6 +2723,7 @@ function createCsszyxPlugins(options = {}) {
2533
2723
  } else if (SOURCE_EXTENSIONS.has(path__namespace.extname(entry.name))) {
2534
2724
  const filePath = path__namespace.join(dir, entry.name);
2535
2725
  if (!shouldProcessSource(filePath)) {
2726
+ recordPackagesSkipIfSz(filePath);
2536
2727
  continue;
2537
2728
  }
2538
2729
  let content;
@@ -2556,7 +2747,8 @@ function createCsszyxPlugins(options = {}) {
2556
2747
  collectPrescanResult(result, filePath, discoveredClasses, rawDiscoveredClasses);
2557
2748
  }
2558
2749
  for (const cls of discoveredClasses) {
2559
- state.classes.add(cls);
2750
+ addSafelistClass(cls);
2751
+ state.ownedClasses.add(cls);
2560
2752
  }
2561
2753
  const safelistClasses = /* @__PURE__ */ new Set([...discoveredClasses, ...rawDiscoveredClasses]);
2562
2754
  writeSafelistFile(safelistClasses);
@@ -2644,7 +2836,7 @@ function createCsszyxPlugins(options = {}) {
2644
2836
  for (const match of code.matchAll(classPattern)) {
2645
2837
  const classes = match[1].split(/\s+/).filter(Boolean);
2646
2838
  for (const cls of classes) {
2647
- state.classes.add(cls);
2839
+ addSafelistClass(cls);
2648
2840
  }
2649
2841
  }
2650
2842
  }
@@ -2666,13 +2858,13 @@ function createCsszyxPlugins(options = {}) {
2666
2858
  const str = strMatch[1] || strMatch[2];
2667
2859
  const classes = str.split(/\s+/).filter(Boolean);
2668
2860
  for (const cls of classes) {
2669
- state.classes.add(cls);
2861
+ addSafelistClass(cls);
2670
2862
  }
2671
2863
  }
2672
2864
  }
2673
2865
  }
2674
2866
  function finalizeMangleMap() {
2675
- const sortedClasses = Array.from(state.classes);
2867
+ const sortedClasses = Array.from(state.ownedClasses);
2676
2868
  const newMap = {};
2677
2869
  for (let i = 0; i < sortedClasses.length; i++) {
2678
2870
  newMap[sortedClasses[i]] = core.encode(i);
@@ -2757,7 +2949,7 @@ function createCsszyxPlugins(options = {}) {
2757
2949
  },
2758
2950
  /**
2759
2951
  * Filters files for the pre-transform phase — source files plus CSS files.
2760
- * CSS files need special handling to inject @source inline() for Tailwind class discovery.
2952
+ * CSS files need special handling to append an @source directive for Tailwind class discovery.
2761
2953
  * @param id - the file path to check for inclusion
2762
2954
  * @returns true if the file should be transformed, false otherwise
2763
2955
  */
@@ -2769,7 +2961,7 @@ function createCsszyxPlugins(options = {}) {
2769
2961
  },
2770
2962
  /**
2771
2963
  * Core transform: detects sz prop, compiles to className, injects runtime, collects classes.
2772
- * For CSS files: injects @source inline() so Tailwind generates CSS for sz-derived classes.
2964
+ * For CSS files: appends an @source directive so Tailwind generates CSS for sz-derived classes.
2773
2965
  * @param code - the source code to transform
2774
2966
  * @param id - the file path of the module being transformed
2775
2967
  * @returns transformed code with source map, or null if no changes were made
@@ -2785,24 +2977,19 @@ function createCsszyxPlugins(options = {}) {
2785
2977
  assertNoRSCBoundaryViolation(code, id);
2786
2978
  }
2787
2979
  if (/\.css(\?.*)?$/.test(id)) {
2788
- const hasTailwindImport = code.includes('@import "tailwindcss') || code.includes("@import 'tailwindcss");
2789
- if (hasTailwindImport && state.classes.size > 0) {
2790
- const candidates = Array.from(state.classes).filter((c) => c.length >= 2 && /^[a-z]/.test(c)).join(" ");
2791
- if (candidates) {
2792
- const safelistPath = path__namespace.join(state.rootDir, SAFELIST_FILENAME).replace(/\\/g, "/");
2793
- const cssDir = path__namespace.dirname(id).replace(/\\/g, "/");
2794
- let relPath = path__namespace.posix.relative(cssDir, safelistPath);
2795
- if (!relPath.startsWith(".")) {
2796
- relPath = `./${relPath}`;
2797
- }
2798
- const sourceDirective = `@source "${relPath}";
2799
- `;
2800
- const transformed2 = code.replace(
2801
- /(@import\s+["']tailwindcss[^"']*["'];)/,
2802
- `$1
2803
- ${sourceDirective}`
2980
+ if (cssImportsTailwind(code)) {
2981
+ state.sawTailwindEntry = true;
2982
+ if (cssHasContentScope(code)) {
2983
+ state.tailwindEntryScoped = true;
2984
+ }
2985
+ if (hasInjectableTailwindCandidate(state.classes)) {
2986
+ const relPath = computeSafelistRelPath(
2987
+ state.rootDir,
2988
+ SAFELIST_FILENAME,
2989
+ id
2804
2990
  );
2805
- if (transformed2 !== code) {
2991
+ const transformed2 = appendTailwindSourceDirective(code, relPath);
2992
+ if (transformed2 !== null) {
2806
2993
  return { code: transformed2, map: null };
2807
2994
  }
2808
2995
  }
@@ -2845,8 +3032,15 @@ ${sourceDirective}`
2845
3032
  szClasses = result.classes;
2846
3033
  recordFileVarMangleEntries(state, id, cssVariableEntries(result));
2847
3034
  recordFileCSSVariableMetrics(state, id, result.code);
3035
+ for (const msg of result.diagnostics) {
3036
+ if (msg.includes("unresolvable sz spread")) {
3037
+ state.spreadWarnings.add(`${id}
3038
+ ${msg}`);
3039
+ }
3040
+ }
2848
3041
  if (result.diagnostics.length > 0 && process.env.NODE_ENV !== "production") {
2849
3042
  for (const msg of result.diagnostics) {
3043
+ if (msg.includes("unresolvable sz spread")) continue;
2850
3044
  this.warn(`[csszyx] ${id}
2851
3045
  ${msg}`);
2852
3046
  }
@@ -2914,7 +3108,8 @@ ${sourceDirective}`
2914
3108
  if (transformed || transformedCode.includes("class=") || transformedCode.includes("className=")) {
2915
3109
  if (szClasses !== void 0) {
2916
3110
  for (const cls of szClasses) {
2917
- state.classes.add(cls);
3111
+ addSafelistClass(cls);
3112
+ state.ownedClasses.add(cls);
2918
3113
  }
2919
3114
  } else {
2920
3115
  extractClasses(transformedCode);
@@ -2927,6 +3122,36 @@ ${sourceDirective}`
2927
3122
  buildEnd() {
2928
3123
  finalizeMangleMap();
2929
3124
  assertNoRSCGraphViolation(state.rscModules);
3125
+ if (!state.tailwindWarningEmitted && shouldWarnMissingTailwindEntry(state.ownedClasses.size, state.sawTailwindEntry)) {
3126
+ state.tailwindWarningEmitted = true;
3127
+ console.warn(missingTailwindEntryMessage(state.ownedClasses.size));
3128
+ }
3129
+ if (!state.contentScopeWarningEmitted && options.contentScopeCheck !== false && state.sawTailwindEntry && !state.tailwindEntryScoped) {
3130
+ if (state.inMonorepo === void 0) {
3131
+ state.inMonorepo = isMonorepoPackage(state.rootDir);
3132
+ }
3133
+ if (shouldWarnUnscopedMonorepo(
3134
+ state.sawTailwindEntry,
3135
+ state.tailwindEntryScoped,
3136
+ state.inMonorepo
3137
+ )) {
3138
+ state.contentScopeWarningEmitted = true;
3139
+ console.warn(unscopedMonorepoMessage());
3140
+ }
3141
+ }
3142
+ if (!state.skipWarningEmitted && state.skippedSzFiles.size > 0) {
3143
+ state.skipWarningEmitted = true;
3144
+ console.warn(skippedSzFilesMessage([...state.skippedSzFiles].sort()));
3145
+ }
3146
+ if (state.classesCapped) {
3147
+ console.warn(
3148
+ `[csszyx] safelist exceeded ${MAX_SAFELIST_CLASSES} classes; additional classes were dropped. This usually means an unbounded set of arbitrary values reached an sz prop.`
3149
+ );
3150
+ }
3151
+ for (const warning of state.spreadWarnings) {
3152
+ console.warn(`[csszyx] ${warning}`);
3153
+ }
3154
+ state.spreadWarnings.clear();
2930
3155
  if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
2931
3156
  globalThis.__csszyx_ssr_mangle_map = state.mangleMap;
2932
3157
  }
@@ -3026,7 +3251,8 @@ ${sourceDirective}`
3026
3251
  const sizeBefore = state.classes.size;
3027
3252
  recordGlobalVarSourceFile(state, ctx.file, fileContent);
3028
3253
  for (const cls of result.classes) {
3029
- state.classes.add(cls);
3254
+ addSafelistClass(cls);
3255
+ state.ownedClasses.add(cls);
3030
3256
  }
3031
3257
  recordFileVarMangleEntries(state, ctx.file, cssVariableEntries(result));
3032
3258
  recordFileCSSVariableMetrics(state, ctx.file, result.code);
@@ -3365,24 +3591,34 @@ const esbuildPlugin = (options = {}) => {
3365
3591
  };
3366
3592
  };
3367
3593
 
3594
+ exports.appendTailwindSourceDirective = appendTailwindSourceDirective;
3368
3595
  exports.assertNoRSCBoundaryViolation = assertNoRSCBoundaryViolation;
3369
3596
  exports.assertNoRSCGraphViolation = assertNoRSCGraphViolation;
3597
+ exports.computeSafelistRelPath = computeSafelistRelPath;
3370
3598
  exports.createGlobalVarAliasValidationOptions = createGlobalVarAliasValidationOptions;
3371
3599
  exports.createGlobalVarMapAssetSource = createGlobalVarMapAssetSource;
3372
3600
  exports.createGlobalVarScanCacheKey = createGlobalVarScanCacheKey;
3373
3601
  exports.createRSCModuleRecord = createRSCModuleRecord;
3602
+ exports.cssHasContentScope = cssHasContentScope;
3603
+ exports.cssImportsTailwind = cssImportsTailwind;
3374
3604
  exports.deleteRSCModuleRecord = deleteRSCModuleRecord;
3375
3605
  exports.esbuildPlugin = esbuildPlugin;
3376
3606
  exports.extractGlobalVarAliasesForManifest = extractGlobalVarAliasesForManifest;
3377
3607
  exports.findRSCBoundaryViolation = findRSCBoundaryViolation;
3378
3608
  exports.findRSCGraphViolation = findRSCGraphViolation;
3609
+ exports.hasInjectableTailwindCandidate = hasInjectableTailwindCandidate;
3379
3610
  exports.hasTokens = hasTokens;
3380
3611
  exports.hasUseClientDirective = hasUseClientDirective;
3381
3612
  exports.hasUseServerDirective = hasUseServerDirective;
3613
+ exports.isCompilePackageOptedIn = isCompilePackageOptedIn;
3614
+ exports.isHardIgnoredPath = isHardIgnoredPath;
3615
+ exports.isMonorepoPackage = isMonorepoPackage;
3616
+ exports.isPackagesSkippedSource = isPackagesSkippedSource;
3382
3617
  exports.isRSCServerModule = isRSCServerModule;
3383
3618
  exports.isTailwindReservedGlobalVar = isTailwindReservedGlobalVar;
3384
3619
  exports.mangleCodeClassesSync = mangleCodeClassesSync;
3385
3620
  exports.mergeThemes = mergeThemes;
3621
+ exports.missingTailwindEntryMessage = missingTailwindEntryMessage;
3386
3622
  exports.normalizeGlobalVarAliasesForCache = normalizeGlobalVarAliasesForCache;
3387
3623
  exports.parseThemeBlocks = parseThemeBlocks;
3388
3624
  exports.planGlobalVarAliases = planGlobalVarAliases;
@@ -3392,7 +3628,11 @@ exports.resolveNativeCacheIdentity = resolveNativeCacheIdentity;
3392
3628
  exports.rewriteGlobalVarCssAliases = rewriteGlobalVarCssAliases;
3393
3629
  exports.rollupPlugin = rollupPlugin;
3394
3630
  exports.scanGlobalVarCss = scanGlobalVarCss;
3631
+ exports.shouldWarnMissingTailwindEntry = shouldWarnMissingTailwindEntry;
3632
+ exports.shouldWarnUnscopedMonorepo = shouldWarnUnscopedMonorepo;
3633
+ exports.skippedSzFilesMessage = skippedSzFilesMessage;
3395
3634
  exports.unplugin = unplugin;
3635
+ exports.unscopedMonorepoMessage = unscopedMonorepoMessage;
3396
3636
  exports.validateGlobalVarAliasInputs = validateGlobalVarAliasInputs;
3397
3637
  exports.vitePlugin = vitePlugin;
3398
3638
  exports.webpackPlugin = webpackPlugin;
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ function escapeHtmlAttribute(value) {
4
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5
+ }
6
+
7
+ exports.escapeHtmlAttribute = escapeHtmlAttribute;
package/dist/vite.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- const unplugin = require('./shared/unplugin.BT-U5kd1.cjs');
5
+ const unplugin = require('./shared/unplugin.Dk6ddrZX.cjs');
6
6
  require('node:fs');
7
7
  require('node:module');
8
8
  require('node:path');
@@ -18,6 +18,7 @@ require('unplugin');
18
18
  require('./css-mangler.cjs');
19
19
  require('postcss');
20
20
  require('postcss-selector-parser');
21
+ require('./shared/unplugin.DoBHTRra.cjs');
21
22
  require('node:crypto');
22
23
  require('./shared/unplugin.pmTknYLy.cjs');
23
24
  require('postcss-value-parser');
package/dist/vite.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { w as vitePlugin } from './shared/unplugin.CMNZChGZ.cjs';
1
+ import { M as vitePlugin } from './shared/unplugin.BqLCtc84.cjs';
2
2
  import '@csszyx/compiler';
3
3
  import '@csszyx/types';
4
4
  import 'esbuild';
package/dist/vite.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { w as vitePlugin } from './shared/unplugin.CMNZChGZ.mjs';
1
+ import { M as vitePlugin } from './shared/unplugin.BqLCtc84.mjs';
2
2
  import '@csszyx/compiler';
3
3
  import '@csszyx/types';
4
4
  import 'esbuild';
package/dist/vite.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { D as vitePlugin } from './shared/unplugin.jEasXLFM.mjs';
1
+ import { R as vitePlugin } from './shared/unplugin.CLXpKBe4.mjs';
2
2
  import 'node:fs';
3
3
  import 'node:module';
4
4
  import 'node:path';
@@ -14,6 +14,7 @@ import 'unplugin';
14
14
  import './css-mangler.mjs';
15
15
  import 'postcss';
16
16
  import 'postcss-selector-parser';
17
+ import './shared/unplugin.3UumZ5gn.mjs';
17
18
  import 'node:crypto';
18
19
  import './shared/unplugin.BpWUtI9U.mjs';
19
20
  import 'postcss-value-parser';