@csszyx/unplugin 0.9.9 → 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.Dfao2VkS.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.Dfao2VkS.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 ?? []) {
@@ -2055,6 +2214,45 @@ function mangleCodeClassesSync(code, mangleMap) {
2055
2214
  }
2056
2215
  return changed ? `className:\`${out}\`` : fullMatch;
2057
2216
  });
2217
+ function scanClassExpression(source, from) {
2218
+ let depth = 0;
2219
+ let j = from;
2220
+ while (j < source.length) {
2221
+ const ch = source[j];
2222
+ if (ch === "(" || ch === "[") {
2223
+ depth++;
2224
+ } else if (ch === ")" || ch === "]") {
2225
+ if (depth === 0) {
2226
+ break;
2227
+ }
2228
+ depth--;
2229
+ } else if (depth === 0 && (ch === "," || ch === ";" || ch === "\n" || ch === "}")) {
2230
+ break;
2231
+ }
2232
+ j++;
2233
+ }
2234
+ return j;
2235
+ }
2236
+ function mangleTernaryClassStrings(expr) {
2237
+ const qIdx = expr.indexOf("?");
2238
+ if (qIdx === -1 || !expr.slice(qIdx).includes(":")) {
2239
+ return null;
2240
+ }
2241
+ let changed = false;
2242
+ const mangled = expr.replace(/"([^"]*)"/g, (qm, inner) => {
2243
+ const parts = inner.split(/\s+/).filter(Boolean);
2244
+ if (parts.length === 0) {
2245
+ return qm;
2246
+ }
2247
+ const mangledStr = parts.map((p) => mangleMap[p] || p).join(" ");
2248
+ if (mangledStr !== inner) {
2249
+ changed = true;
2250
+ return `"${mangledStr}"`;
2251
+ }
2252
+ return qm;
2253
+ });
2254
+ return changed ? mangled : null;
2255
+ }
2058
2256
  {
2059
2257
  const marker = "className:";
2060
2258
  let searchFrom = 0;
@@ -2076,47 +2274,39 @@ function mangleCodeClassesSync(code, mangleMap) {
2076
2274
  searchFrom = afterColon;
2077
2275
  continue;
2078
2276
  }
2079
- let depth = 0;
2080
- let j = afterColon;
2081
- while (j < result.length) {
2082
- const ch = result[j];
2083
- if (ch === "(" || ch === "[") {
2084
- depth++;
2085
- } else if (ch === ")" || ch === "]") {
2086
- if (depth === 0) {
2087
- break;
2088
- }
2089
- depth--;
2090
- } else if (depth === 0 && (ch === "," || ch === ";" || ch === "\n" || ch === "}")) {
2091
- break;
2092
- }
2093
- j++;
2094
- }
2277
+ const j = scanClassExpression(result, afterColon);
2095
2278
  const expr = result.slice(afterColon, j);
2096
- const qIdx = expr.indexOf("?");
2097
- if (qIdx === -1 || !expr.slice(qIdx).includes(":")) {
2098
- out += expr;
2099
- searchFrom = j;
2100
- continue;
2101
- }
2102
- let changed = false;
2103
- const mangled = expr.replace(/"([^"]*)"/g, (qm, inner) => {
2104
- const parts = inner.split(/\s+/).filter(Boolean);
2105
- if (parts.length === 0) {
2106
- return qm;
2107
- }
2108
- const mangledStr = parts.map((p) => mangleMap[p] || p).join(" ");
2109
- if (mangledStr !== inner) {
2110
- changed = true;
2111
- return `"${mangledStr}"`;
2112
- }
2113
- return qm;
2114
- });
2115
- out += changed ? mangled : expr;
2279
+ out += mangleTernaryClassStrings(expr) ?? expr;
2116
2280
  searchFrom = j;
2117
2281
  }
2118
2282
  result = out;
2119
2283
  }
2284
+ {
2285
+ const markerRe = /"class(?:Name)?"\s*,\s*/g;
2286
+ let out = "";
2287
+ let copiedTo = 0;
2288
+ let m = markerRe.exec(result);
2289
+ while (m !== null) {
2290
+ const exprStart = m.index + m[0].length;
2291
+ const firstChar = result[exprStart];
2292
+ if (firstChar === '"' || firstChar === "'" || firstChar === "`") {
2293
+ m = markerRe.exec(result);
2294
+ continue;
2295
+ }
2296
+ const j = scanClassExpression(result, exprStart);
2297
+ const expr = result.slice(exprStart, j);
2298
+ const mangled = mangleTernaryClassStrings(expr);
2299
+ if (mangled !== null) {
2300
+ out += result.slice(copiedTo, exprStart) + mangled;
2301
+ copiedTo = j;
2302
+ }
2303
+ markerRe.lastIndex = j;
2304
+ m = markerRe.exec(result);
2305
+ }
2306
+ if (copiedTo > 0) {
2307
+ result = out + result.slice(copiedTo);
2308
+ }
2309
+ }
2120
2310
  result = result.replace(/(?<=(?:[,(]|&&)\s*)"([^"]+)"/g, (match, inner) => {
2121
2311
  const tokens = inner.split(/\s+/).filter(Boolean);
2122
2312
  if (tokens.length === 0) {
@@ -2184,6 +2374,7 @@ function createCsszyxPlugins(options = {}) {
2184
2374
  "[csszyx] Transform cache disabled because package versions could not be resolved."
2185
2375
  );
2186
2376
  }
2377
+ const compilePackages = options.compilePackages ?? [];
2187
2378
  const parserOverride = process.env.CSSZYX_PARSER;
2188
2379
  const defaultParser = types.DEFAULT_BUILD_CONFIG.parser ?? "rust";
2189
2380
  const parserMode = parserOverride === "babel" || parserOverride === "oxc" || parserOverride === "rust" ? parserOverride : options.build?.parser ?? defaultParser;
@@ -2191,6 +2382,15 @@ function createCsszyxPlugins(options = {}) {
2191
2382
  const transformMemoryCache = /* @__PURE__ */ new Map();
2192
2383
  const state = {
2193
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(),
2194
2394
  mangleMap: {},
2195
2395
  varMangleEntriesByFile: /* @__PURE__ */ new Map(),
2196
2396
  varMangleMap: Object.fromEntries(earlyGlobalVarAliasEntries),
@@ -2251,7 +2451,7 @@ function createCsszyxPlugins(options = {}) {
2251
2451
  return result;
2252
2452
  }
2253
2453
  function isHardIgnored(id) {
2254
- return id.includes("node_modules") || id.includes("/packages/") || id.includes(".next") && !id.includes("static");
2454
+ return isHardIgnoredPath(id, compilePackages);
2255
2455
  }
2256
2456
  function shouldProcessSource(id) {
2257
2457
  return !isHardIgnored(id) && !isUserExcluded(id) && isUserIncluded(id) && (/\.[tj]sx?(\?.*)?$/.test(id) || id.endsWith(".vue") || id.endsWith(".svelte"));
@@ -2457,12 +2657,19 @@ function createCsszyxPlugins(options = {}) {
2457
2657
  maxEntries: TRANSFORM_CACHE_MAX_ENTRIES
2458
2658
  });
2459
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
+ }
2460
2667
  function writeSafelistFile(classes) {
2461
2668
  if (classes.size === 0) {
2462
2669
  return;
2463
2670
  }
2464
2671
  const safelistPath = path__namespace.join(state.rootDir, SAFELIST_FILENAME);
2465
- const classList = Array.from(classes).join(" ");
2672
+ const classList = htmlEscape.escapeHtmlAttribute(Array.from(classes).join(" "));
2466
2673
  const content = `<!-- Auto-generated by csszyx \u2014 DO NOT EDIT -->
2467
2674
  <!-- Tailwind CSS scans this file for class name detection -->
2468
2675
  <div class="${classList}"><div class="${classList}">x</div><div class="${classList}">x</div></div>
@@ -2482,6 +2689,20 @@ function createCsszyxPlugins(options = {}) {
2482
2689
  } catch {
2483
2690
  }
2484
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
+ }
2485
2706
  function prescanAndWriteClasses() {
2486
2707
  const prescanStarted = node_perf_hooks.performance.now();
2487
2708
  const discoveredClasses = /* @__PURE__ */ new Set();
@@ -2502,6 +2723,7 @@ function createCsszyxPlugins(options = {}) {
2502
2723
  } else if (SOURCE_EXTENSIONS.has(path__namespace.extname(entry.name))) {
2503
2724
  const filePath = path__namespace.join(dir, entry.name);
2504
2725
  if (!shouldProcessSource(filePath)) {
2726
+ recordPackagesSkipIfSz(filePath);
2505
2727
  continue;
2506
2728
  }
2507
2729
  let content;
@@ -2525,7 +2747,8 @@ function createCsszyxPlugins(options = {}) {
2525
2747
  collectPrescanResult(result, filePath, discoveredClasses, rawDiscoveredClasses);
2526
2748
  }
2527
2749
  for (const cls of discoveredClasses) {
2528
- state.classes.add(cls);
2750
+ addSafelistClass(cls);
2751
+ state.ownedClasses.add(cls);
2529
2752
  }
2530
2753
  const safelistClasses = /* @__PURE__ */ new Set([...discoveredClasses, ...rawDiscoveredClasses]);
2531
2754
  writeSafelistFile(safelistClasses);
@@ -2613,7 +2836,7 @@ function createCsszyxPlugins(options = {}) {
2613
2836
  for (const match of code.matchAll(classPattern)) {
2614
2837
  const classes = match[1].split(/\s+/).filter(Boolean);
2615
2838
  for (const cls of classes) {
2616
- state.classes.add(cls);
2839
+ addSafelistClass(cls);
2617
2840
  }
2618
2841
  }
2619
2842
  }
@@ -2635,13 +2858,13 @@ function createCsszyxPlugins(options = {}) {
2635
2858
  const str = strMatch[1] || strMatch[2];
2636
2859
  const classes = str.split(/\s+/).filter(Boolean);
2637
2860
  for (const cls of classes) {
2638
- state.classes.add(cls);
2861
+ addSafelistClass(cls);
2639
2862
  }
2640
2863
  }
2641
2864
  }
2642
2865
  }
2643
2866
  function finalizeMangleMap() {
2644
- const sortedClasses = Array.from(state.classes);
2867
+ const sortedClasses = Array.from(state.ownedClasses);
2645
2868
  const newMap = {};
2646
2869
  for (let i = 0; i < sortedClasses.length; i++) {
2647
2870
  newMap[sortedClasses[i]] = core.encode(i);
@@ -2688,6 +2911,21 @@ function createCsszyxPlugins(options = {}) {
2688
2911
  }
2689
2912
  return null;
2690
2913
  },
2914
+ /**
2915
+ * Restricts the load hook to csszyx's own virtual modules.
2916
+ *
2917
+ * Without this, unplugin's webpack adapter registers its load
2918
+ * loader with `type: 'javascript/auto'` for every module (its
2919
+ * include defaults to all ids when no loadInclude exists). That
2920
+ * corrupts binary asset modules (images, fonts) in webpack apps —
2921
+ * Next.js builds fail with "not a valid image file" / "Module
2922
+ * parse failed" on assets that build fine without csszyx.
2923
+ * @param id - the module ID webpack is about to load
2924
+ * @returns true only for csszyx virtual modules
2925
+ */
2926
+ loadInclude(id) {
2927
+ return id === RESOLVED_VIRTUAL_MODULE_ID || id === RESOLVED_VIRTUAL_CHECKSUM_ID;
2928
+ },
2691
2929
  /**
2692
2930
  * Loads virtual module content — generates mangle map or checksum module code.
2693
2931
  * @param id - the resolved module ID to load
@@ -2711,7 +2949,7 @@ function createCsszyxPlugins(options = {}) {
2711
2949
  },
2712
2950
  /**
2713
2951
  * Filters files for the pre-transform phase — source files plus CSS files.
2714
- * 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.
2715
2953
  * @param id - the file path to check for inclusion
2716
2954
  * @returns true if the file should be transformed, false otherwise
2717
2955
  */
@@ -2723,7 +2961,7 @@ function createCsszyxPlugins(options = {}) {
2723
2961
  },
2724
2962
  /**
2725
2963
  * Core transform: detects sz prop, compiles to className, injects runtime, collects classes.
2726
- * 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.
2727
2965
  * @param code - the source code to transform
2728
2966
  * @param id - the file path of the module being transformed
2729
2967
  * @returns transformed code with source map, or null if no changes were made
@@ -2739,24 +2977,19 @@ function createCsszyxPlugins(options = {}) {
2739
2977
  assertNoRSCBoundaryViolation(code, id);
2740
2978
  }
2741
2979
  if (/\.css(\?.*)?$/.test(id)) {
2742
- const hasTailwindImport = code.includes('@import "tailwindcss') || code.includes("@import 'tailwindcss");
2743
- if (hasTailwindImport && state.classes.size > 0) {
2744
- const candidates = Array.from(state.classes).filter((c) => c.length >= 2 && /^[a-z]/.test(c)).join(" ");
2745
- if (candidates) {
2746
- const safelistPath = path__namespace.join(state.rootDir, SAFELIST_FILENAME).replace(/\\/g, "/");
2747
- const cssDir = path__namespace.dirname(id).replace(/\\/g, "/");
2748
- let relPath = path__namespace.posix.relative(cssDir, safelistPath);
2749
- if (!relPath.startsWith(".")) {
2750
- relPath = `./${relPath}`;
2751
- }
2752
- const sourceDirective = `@source "${relPath}";
2753
- `;
2754
- const transformed2 = code.replace(
2755
- /(@import\s+["']tailwindcss[^"']*["'];)/,
2756
- `$1
2757
- ${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
2758
2990
  );
2759
- if (transformed2 !== code) {
2991
+ const transformed2 = appendTailwindSourceDirective(code, relPath);
2992
+ if (transformed2 !== null) {
2760
2993
  return { code: transformed2, map: null };
2761
2994
  }
2762
2995
  }
@@ -2799,8 +3032,15 @@ ${sourceDirective}`
2799
3032
  szClasses = result.classes;
2800
3033
  recordFileVarMangleEntries(state, id, cssVariableEntries(result));
2801
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
+ }
2802
3041
  if (result.diagnostics.length > 0 && process.env.NODE_ENV !== "production") {
2803
3042
  for (const msg of result.diagnostics) {
3043
+ if (msg.includes("unresolvable sz spread")) continue;
2804
3044
  this.warn(`[csszyx] ${id}
2805
3045
  ${msg}`);
2806
3046
  }
@@ -2868,7 +3108,8 @@ ${sourceDirective}`
2868
3108
  if (transformed || transformedCode.includes("class=") || transformedCode.includes("className=")) {
2869
3109
  if (szClasses !== void 0) {
2870
3110
  for (const cls of szClasses) {
2871
- state.classes.add(cls);
3111
+ addSafelistClass(cls);
3112
+ state.ownedClasses.add(cls);
2872
3113
  }
2873
3114
  } else {
2874
3115
  extractClasses(transformedCode);
@@ -2881,6 +3122,36 @@ ${sourceDirective}`
2881
3122
  buildEnd() {
2882
3123
  finalizeMangleMap();
2883
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();
2884
3155
  if (manglingEnabled && Object.keys(state.mangleMap).length > 0) {
2885
3156
  globalThis.__csszyx_ssr_mangle_map = state.mangleMap;
2886
3157
  }
@@ -2980,7 +3251,8 @@ ${sourceDirective}`
2980
3251
  const sizeBefore = state.classes.size;
2981
3252
  recordGlobalVarSourceFile(state, ctx.file, fileContent);
2982
3253
  for (const cls of result.classes) {
2983
- state.classes.add(cls);
3254
+ addSafelistClass(cls);
3255
+ state.ownedClasses.add(cls);
2984
3256
  }
2985
3257
  recordFileVarMangleEntries(state, ctx.file, cssVariableEntries(result));
2986
3258
  recordFileCSSVariableMetrics(state, ctx.file, result.code);
@@ -3319,24 +3591,34 @@ const esbuildPlugin = (options = {}) => {
3319
3591
  };
3320
3592
  };
3321
3593
 
3594
+ exports.appendTailwindSourceDirective = appendTailwindSourceDirective;
3322
3595
  exports.assertNoRSCBoundaryViolation = assertNoRSCBoundaryViolation;
3323
3596
  exports.assertNoRSCGraphViolation = assertNoRSCGraphViolation;
3597
+ exports.computeSafelistRelPath = computeSafelistRelPath;
3324
3598
  exports.createGlobalVarAliasValidationOptions = createGlobalVarAliasValidationOptions;
3325
3599
  exports.createGlobalVarMapAssetSource = createGlobalVarMapAssetSource;
3326
3600
  exports.createGlobalVarScanCacheKey = createGlobalVarScanCacheKey;
3327
3601
  exports.createRSCModuleRecord = createRSCModuleRecord;
3602
+ exports.cssHasContentScope = cssHasContentScope;
3603
+ exports.cssImportsTailwind = cssImportsTailwind;
3328
3604
  exports.deleteRSCModuleRecord = deleteRSCModuleRecord;
3329
3605
  exports.esbuildPlugin = esbuildPlugin;
3330
3606
  exports.extractGlobalVarAliasesForManifest = extractGlobalVarAliasesForManifest;
3331
3607
  exports.findRSCBoundaryViolation = findRSCBoundaryViolation;
3332
3608
  exports.findRSCGraphViolation = findRSCGraphViolation;
3609
+ exports.hasInjectableTailwindCandidate = hasInjectableTailwindCandidate;
3333
3610
  exports.hasTokens = hasTokens;
3334
3611
  exports.hasUseClientDirective = hasUseClientDirective;
3335
3612
  exports.hasUseServerDirective = hasUseServerDirective;
3613
+ exports.isCompilePackageOptedIn = isCompilePackageOptedIn;
3614
+ exports.isHardIgnoredPath = isHardIgnoredPath;
3615
+ exports.isMonorepoPackage = isMonorepoPackage;
3616
+ exports.isPackagesSkippedSource = isPackagesSkippedSource;
3336
3617
  exports.isRSCServerModule = isRSCServerModule;
3337
3618
  exports.isTailwindReservedGlobalVar = isTailwindReservedGlobalVar;
3338
3619
  exports.mangleCodeClassesSync = mangleCodeClassesSync;
3339
3620
  exports.mergeThemes = mergeThemes;
3621
+ exports.missingTailwindEntryMessage = missingTailwindEntryMessage;
3340
3622
  exports.normalizeGlobalVarAliasesForCache = normalizeGlobalVarAliasesForCache;
3341
3623
  exports.parseThemeBlocks = parseThemeBlocks;
3342
3624
  exports.planGlobalVarAliases = planGlobalVarAliases;
@@ -3346,7 +3628,11 @@ exports.resolveNativeCacheIdentity = resolveNativeCacheIdentity;
3346
3628
  exports.rewriteGlobalVarCssAliases = rewriteGlobalVarCssAliases;
3347
3629
  exports.rollupPlugin = rollupPlugin;
3348
3630
  exports.scanGlobalVarCss = scanGlobalVarCss;
3631
+ exports.shouldWarnMissingTailwindEntry = shouldWarnMissingTailwindEntry;
3632
+ exports.shouldWarnUnscopedMonorepo = shouldWarnUnscopedMonorepo;
3633
+ exports.skippedSzFilesMessage = skippedSzFilesMessage;
3349
3634
  exports.unplugin = unplugin;
3635
+ exports.unscopedMonorepoMessage = unscopedMonorepoMessage;
3350
3636
  exports.validateGlobalVarAliasInputs = validateGlobalVarAliasInputs;
3351
3637
  exports.vitePlugin = vitePlugin;
3352
3638
  exports.webpackPlugin = webpackPlugin;