tailwind-to-style 4.0.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * tailwind-to-style v4.0.2
2
+ * tailwind-to-style v5.0.0
3
3
  * Zero-build runtime Tailwind CSS engine
4
4
  *
5
5
  * @author Bigetion
@@ -2151,6 +2151,182 @@ function getActiveCollector() {
2151
2151
  return _activeCollector;
2152
2152
  }
2153
2153
 
2154
+ /**
2155
+ * Shared Constants
2156
+ *
2157
+ * All constant values used across the library.
2158
+ * Extracted from monolithic index.js for better maintainability.
2159
+ *
2160
+ * @module shared/constants
2161
+ */
2162
+
2163
+ // ============================================================================
2164
+ // Environment Detection
2165
+ // ============================================================================
2166
+
2167
+ /**
2168
+ * True when running in a browser environment (window & document available)
2169
+ */
2170
+ const IS_BROWSER$1 = typeof window !== "undefined" && typeof document !== "undefined";
2171
+
2172
+ /**
2173
+ * Maximum number of entries in Set-based caches
2174
+ */
2175
+ const MAX_SET_SIZE$1 = 10000;
2176
+
2177
+ // ============================================================================
2178
+ // Built-in Animation Keyframes
2179
+ // ============================================================================
2180
+
2181
+ /**
2182
+ * Mapping of animation names to their keyframe definitions
2183
+ */
2184
+ const BUILTIN_KEYFRAMES = {
2185
+ spin: {
2186
+ "0%": {
2187
+ transform: "rotate(0deg)"
2188
+ },
2189
+ "100%": {
2190
+ transform: "rotate(360deg)"
2191
+ }
2192
+ },
2193
+ ping: {
2194
+ "75%, 100%": {
2195
+ transform: "scale(2)",
2196
+ opacity: "0"
2197
+ }
2198
+ },
2199
+ pulse: {
2200
+ "50%": {
2201
+ opacity: ".5"
2202
+ }
2203
+ },
2204
+ bounce: {
2205
+ "0%, 100%": {
2206
+ transform: "translateY(-25%)",
2207
+ animationTimingFunction: "cubic-bezier(0.8,0,1,1)"
2208
+ },
2209
+ "50%": {
2210
+ transform: "none",
2211
+ animationTimingFunction: "cubic-bezier(0,0,0.2,1)"
2212
+ }
2213
+ },
2214
+ fadeIn: {
2215
+ "0%": {
2216
+ opacity: "0"
2217
+ },
2218
+ "50%": {
2219
+ opacity: "1"
2220
+ },
2221
+ "100%": {
2222
+ opacity: "0"
2223
+ }
2224
+ },
2225
+ slideUp: {
2226
+ "0%": {
2227
+ transform: "translateY(20px)",
2228
+ opacity: "0"
2229
+ },
2230
+ "50%": {
2231
+ transform: "translateY(0)",
2232
+ opacity: "1"
2233
+ },
2234
+ "100%": {
2235
+ transform: "translateY(-20px)",
2236
+ opacity: "0"
2237
+ }
2238
+ }
2239
+ };
2240
+
2241
+ /**
2242
+ * Opacity modifiers
2243
+ */
2244
+ const OPACITY_MODIFIER_REGEX = /\/(\d+)$/;
2245
+
2246
+ /**
2247
+ * Opacity property patterns for substitution
2248
+ */
2249
+ const OPACITY_PROP_REGEXES = {
2250
+ "--text-opacity": /--text-opacity\s*:\s*[\d.]+/gi,
2251
+ "--bg-opacity": /--bg-opacity\s*:\s*[\d.]+/gi,
2252
+ "--border-opacity": /--border-opacity\s*:\s*[\d.]+/gi,
2253
+ "--ring-opacity": /--ring-opacity\s*:\s*[\d.]+/gi,
2254
+ "--placeholder-opacity": /--placeholder-opacity\s*:\s*[\d.]+/gi,
2255
+ "--divide-opacity": /--divide-opacity\s*:\s*[\d.]+/gi
2256
+ };
2257
+
2258
+ /**
2259
+ * Bracket encoding/decoding
2260
+ */
2261
+ const BRACKET_CONTENT_REGEX = /\[([^\]]+)\]/g;
2262
+ const OPENING_PAREN_REGEX = /\(/g;
2263
+ const CLOSING_PAREN_REGEX = /\)/g;
2264
+ const ENCODED_PAREN_OPEN_REGEX = /__P__/g;
2265
+ const ENCODED_PAREN_CLOSE_REGEX = /__C__/g;
2266
+
2267
+ /**
2268
+ * Animation detection
2269
+ */
2270
+ const ANIMATION_NAME_REGEX = /animation(?:-name)?:\s*([a-zA-Z0-9-]+)/gi;
2271
+
2272
+ /**
2273
+ * Color property regex patterns (pre-compiled for each color property)
2274
+ * Used in processOpacityModifier for 50-100x performance improvement
2275
+ */
2276
+ const COLOR_PROPERTIES$1 = ["color", "background-color", "border-color", "border-top-color", "border-right-color", "border-bottom-color", "border-left-color"];
2277
+
2278
+ /**
2279
+ * CSS property name conversion
2280
+ */
2281
+ const UPPERCASE_LETTER_REGEX = /([A-Z])/g;
2282
+
2283
+ /**
2284
+ * Hash Utilities
2285
+ *
2286
+ * Fast hashing functions for string and object caching.
2287
+ * Extracted from monolithic index.js for reusability.
2288
+ *
2289
+ * @module shared/hash
2290
+ */
2291
+
2292
+ /**
2293
+ * Fast string hash using FNV-1a algorithm.
2294
+ * Produces 32-bit hash values for cache keys.
2295
+ *
2296
+ * @param {string} str - String to hash
2297
+ * @returns {number} 32-bit hash
2298
+ *
2299
+ * @example
2300
+ * hashString('bg-blue-500') // → 2845719273
2301
+ * hashString('hover:bg-red-600') // → 3927461034
2302
+ */
2303
+ function hashString$2(str) {
2304
+ let hash = 2166136261; // FNV offset basis
2305
+ for (let i = 0; i < str.length; i++) {
2306
+ hash ^= str.charCodeAt(i);
2307
+ // FNV prime: 16777619
2308
+ hash = hash * 16777619 >>> 0; // >>> 0 keeps it a 32-bit unsigned integer
2309
+ }
2310
+ return hash;
2311
+ }
2312
+
2313
+ /**
2314
+ * Generate a short hash for CSS class names.
2315
+ * Uses base36 encoding for compact, URL-safe identifiers.
2316
+ *
2317
+ * @param {string} str - Input string
2318
+ * @param {number} length - Desired hash length (default: 8)
2319
+ * @returns {string} Short hash string
2320
+ *
2321
+ * @example
2322
+ * getCssHash('bg-blue-500 p-4 rounded') // → 'x7k2m9n1'
2323
+ */
2324
+ function getCssHash(str) {
2325
+ let length = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 8;
2326
+ const hash = hashString$2(str);
2327
+ return hash.toString(36).substring(0, length);
2328
+ }
2329
+
2154
2330
  /**
2155
2331
  * Proper LRU (Least Recently Used) Cache implementation
2156
2332
  * Efficiently manages memory by removing least recently used items
@@ -2177,49 +2353,496 @@ class LRUCache {
2177
2353
  return value;
2178
2354
  }
2179
2355
 
2180
- /**
2181
- * Set value in cache
2182
- * Removes least recently used item if cache is full
2183
- */
2184
- set(key, value) {
2185
- // If key exists, delete it first to update position
2186
- if (this.cache.has(key)) {
2187
- this.cache.delete(key);
2188
- } else if (this.cache.size >= this.maxSize) {
2189
- // Remove least recently used (first item)
2190
- const firstKey = this.cache.keys().next().value;
2191
- this.cache.delete(firstKey);
2356
+ /**
2357
+ * Set value in cache
2358
+ * Removes least recently used item if cache is full
2359
+ */
2360
+ set(key, value) {
2361
+ // If key exists, delete it first to update position
2362
+ if (this.cache.has(key)) {
2363
+ this.cache.delete(key);
2364
+ } else if (this.cache.size >= this.maxSize) {
2365
+ // Remove least recently used (first item)
2366
+ const firstKey = this.cache.keys().next().value;
2367
+ this.cache.delete(firstKey);
2368
+ }
2369
+ this.cache.set(key, value);
2370
+ }
2371
+
2372
+ /**
2373
+ * Check if key exists in cache
2374
+ */
2375
+ has(key) {
2376
+ return this.cache.has(key);
2377
+ }
2378
+
2379
+ /**
2380
+ * Clear all cache entries
2381
+ */
2382
+ clear() {
2383
+ this.cache.clear();
2384
+ }
2385
+
2386
+ /**
2387
+ * Get current cache size
2388
+ */
2389
+ get size() {
2390
+ return this.cache.size;
2391
+ }
2392
+
2393
+ /**
2394
+ * Delete specific key
2395
+ */
2396
+ delete(key) {
2397
+ return this.cache.delete(key);
2398
+ }
2399
+ }
2400
+
2401
+ /**
2402
+ * Cache Management
2403
+ *
2404
+ * Global caches and cache utilities for performance optimization.
2405
+ * Uses LRU (Least Recently Used) strategy to prevent memory leaks.
2406
+ *
2407
+ * @module shared/cache
2408
+ */
2409
+
2410
+
2411
+ /**
2412
+ * Cache for bracket value encoding
2413
+ * @type {LRUCache}
2414
+ */
2415
+ const encodeBracketCache = new LRUCache(1000);
2416
+
2417
+ /**
2418
+ * Cache for bracket value decoding
2419
+ * @type {LRUCache}
2420
+ */
2421
+ const decodeBracketCache = new LRUCache(1000);
2422
+
2423
+ /**
2424
+ * Set of injected CSS hashes (prevents duplicate injection)
2425
+ * @type {Set<string>}
2426
+ */
2427
+ const injectedCssHashSet = new Set();
2428
+
2429
+ /**
2430
+ * Registry of sourceKey → cssBlock for smart slot-based replacement
2431
+ * Prevents stale CSS chunks from accumulating across HMR cycles
2432
+ * @type {Map<string, string>}
2433
+ */
2434
+ const cssBlockRegistry = new Map();
2435
+
2436
+ /**
2437
+ * Evict old entries from a Set when size exceeds maxSize.
2438
+ *
2439
+ * @deprecated Use LRUCache instead - kept for backward compatibility
2440
+ * @param {Set|LRUCache} set - Set to evict from
2441
+ * @param {number} maxSize - Maximum size
2442
+ */
2443
+ function evictSet(set) {
2444
+ let maxSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : MAX_SET_SIZE$1;
2445
+ if (set instanceof LRUCache) return; // LRUCache handles its own eviction
2446
+ if (set.size <= maxSize) return;
2447
+ const excess = set.size - maxSize;
2448
+ const iter = set.values();
2449
+ for (let i = 0; i < excess; i++) {
2450
+ set.delete(iter.next().value);
2451
+ }
2452
+ }
2453
+
2454
+ /**
2455
+ * Generate minified CSS keyframes for animations.
2456
+ * Converts camelCase property names to kebab-case.
2457
+ *
2458
+ * @param {string[]} animationNames - Array of animation names to generate
2459
+ * @returns {string} Minified CSS @keyframes rules
2460
+ *
2461
+ * @example
2462
+ * generateMinifiedKeyframes(['spin', 'pulse'])
2463
+ * // → '@keyframes spin{0%{transform:rotate(0deg);}100%{transform:rotate(360deg);}}
2464
+ * // @keyframes pulse{50%{opacity:.5;}}'
2465
+ *
2466
+ * @example
2467
+ * generateMinifiedKeyframes(['fadeIn'])
2468
+ * // → '@keyframes fadeIn{0%{opacity:0;}50%{opacity:1;}100%{opacity:0;}}'
2469
+ */
2470
+ function generateMinifiedKeyframes(animationNames) {
2471
+ let css = "";
2472
+ for (const name of animationNames) {
2473
+ const keyframe = BUILTIN_KEYFRAMES[name];
2474
+ if (!keyframe) continue;
2475
+ css += "@keyframes ".concat(name, "{");
2476
+ for (const _ref of Object.entries(keyframe)) {
2477
+ var _ref2 = _slicedToArray(_ref, 2);
2478
+ const percentage = _ref2[0];
2479
+ const styles = _ref2[1];
2480
+ css += "".concat(percentage, "{");
2481
+ for (const _ref3 of Object.entries(styles)) {
2482
+ var _ref4 = _slicedToArray(_ref3, 2);
2483
+ const prop = _ref4[0];
2484
+ const value = _ref4[1];
2485
+ const cssProp = prop.replace(UPPERCASE_LETTER_REGEX, "-$1").toLowerCase();
2486
+ css += "".concat(cssProp, ":").concat(value, ";");
2487
+ }
2488
+ css += "}";
2489
+ }
2490
+ css += "}";
2491
+ }
2492
+ return css;
2493
+ }
2494
+
2495
+ /**
2496
+ * Opacity Modifier Processing
2497
+ *
2498
+ * Handles Tailwind opacity modifiers (e.g., bg-blue-500/50).
2499
+ * Converts color values to rgba/hsla with specified opacity.
2500
+ *
2501
+ * @module generator/opacity
2502
+ */
2503
+
2504
+
2505
+ // Pre-compile regex patterns for each color property
2506
+ const COLOR_REGEX_PATTERNS$1 = new Map();
2507
+ for (const prop of COLOR_PROPERTIES$1) {
2508
+ const escapedProp = prop.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2509
+ COLOR_REGEX_PATTERNS$1.set(prop, {
2510
+ rgb: new RegExp("(".concat(escapedProp, "\\s*:\\s*)rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)"), "gi"),
2511
+ rgba: new RegExp("(".concat(escapedProp, "\\s*:\\s*)rgba\\((\\d+),\\s*(\\d+),\\s*(\\d+),\\s*[\\d.]+\\)"), "gi"),
2512
+ hsl: new RegExp("(".concat(escapedProp, "\\s*:\\s*)hsl\\((\\d+),\\s*([\\d.]+%),\\s*([\\d.]+%)\\)"), "gi"),
2513
+ hsla: new RegExp("(".concat(escapedProp, "\\s*:\\s*)hsla\\((\\d+),\\s*([\\d.]+%),\\s*([\\d.]+%),\\s*[\\d.]+\\)"), "gi"),
2514
+ hex: new RegExp("(".concat(escapedProp, "\\s*:\\s*)(#[0-9a-fA-F]{3,6})"), "gi")
2515
+ });
2516
+ }
2517
+
2518
+ /**
2519
+ * Process opacity modifiers in Tailwind classes.
2520
+ *
2521
+ * Handles:
2522
+ * - Tailwind CSS custom properties (--text-opacity, --bg-opacity, etc.)
2523
+ * - Direct color values (rgb, rgba, hsl, hsla, hex)
2524
+ * - Converts colors to rgba/hsla with specified opacity
2525
+ *
2526
+ * @param {string} className - Tailwind class name (e.g., 'bg-blue-500/50')
2527
+ * @param {string} cssDeclaration - CSS declaration to modify
2528
+ * @returns {string} Modified CSS declaration with opacity applied
2529
+ *
2530
+ * @example
2531
+ * processOpacityModifier('bg-blue-500/50', 'background-color: rgb(59, 130, 246);')
2532
+ * // → 'background-color: rgba(59, 130, 246, 0.5);'
2533
+ *
2534
+ * @example
2535
+ * processOpacityModifier('text-red-600/75', 'color: #dc2626;')
2536
+ * // → 'color: rgba(220, 38, 38, 0.75);'
2537
+ *
2538
+ * @example
2539
+ * processOpacityModifier('bg-blue-500', 'background-color: rgb(59, 130, 246);')
2540
+ * // → 'background-color: rgb(59, 130, 246);' (no change - no opacity modifier)
2541
+ */
2542
+ function processOpacityModifier(className, cssDeclaration) {
2543
+ const opacityMatch = OPACITY_MODIFIER_REGEX.exec(className);
2544
+ if (!opacityMatch) return cssDeclaration;
2545
+ const opacityValue = parseInt(opacityMatch[1], 10);
2546
+ if (opacityValue < 0 || opacityValue > 100) return cssDeclaration;
2547
+ const alphaValue = (opacityValue / 100).toString();
2548
+
2549
+ // Handle Tailwind's CSS custom property pattern
2550
+ let modifiedDeclaration = cssDeclaration;
2551
+
2552
+ // Replace opacity custom properties using pre-compiled regexes
2553
+ for (const prop in OPACITY_PROP_REGEXES) {
2554
+ const regex = OPACITY_PROP_REGEXES[prop];
2555
+ regex.lastIndex = 0; // Reset global regex
2556
+ modifiedDeclaration = modifiedDeclaration.replace(regex, "".concat(prop, ": ").concat(alphaValue));
2557
+ }
2558
+
2559
+ // Also handle direct color values using pre-compiled regex patterns
2560
+ for (const prop of COLOR_PROPERTIES$1) {
2561
+ const patterns = COLOR_REGEX_PATTERNS$1.get(prop);
2562
+ if (!patterns) continue;
2563
+
2564
+ // Reset all regex lastIndex for reuse
2565
+ patterns.rgb.lastIndex = 0;
2566
+ patterns.rgba.lastIndex = 0;
2567
+ patterns.hsl.lastIndex = 0;
2568
+ patterns.hsla.lastIndex = 0;
2569
+ patterns.hex.lastIndex = 0;
2570
+
2571
+ // Convert rgb to rgba with opacity
2572
+ modifiedDeclaration = modifiedDeclaration.replace(patterns.rgb, "$1rgba($2, $3, $4, ".concat(alphaValue, ")"));
2573
+
2574
+ // Update existing rgba opacity
2575
+ modifiedDeclaration = modifiedDeclaration.replace(patterns.rgba, "$1rgba($2, $3, $4, ".concat(alphaValue, ")"));
2576
+
2577
+ // Convert hsl to hsla with opacity
2578
+ modifiedDeclaration = modifiedDeclaration.replace(patterns.hsl, "$1hsla($2, $3, $4, ".concat(alphaValue, ")"));
2579
+
2580
+ // Update existing hsla opacity
2581
+ modifiedDeclaration = modifiedDeclaration.replace(patterns.hsla, "$1hsla($2, $3, $4, ".concat(alphaValue, ")"));
2582
+
2583
+ // Handle hex colors - convert to rgba
2584
+ modifiedDeclaration = modifiedDeclaration.replace(patterns.hex, (match, propPart, hexColor) => {
2585
+ // Convert hex to rgba
2586
+ const hex = hexColor.replace("#", "");
2587
+ let r, g, b;
2588
+ if (hex.length === 3) {
2589
+ r = parseInt(hex[0] + hex[0], 16);
2590
+ g = parseInt(hex[1] + hex[1], 16);
2591
+ b = parseInt(hex[2] + hex[2], 16);
2592
+ } else {
2593
+ r = parseInt(hex.substring(0, 2), 16);
2594
+ g = parseInt(hex.substring(2, 4), 16);
2595
+ b = parseInt(hex.substring(4, 6), 16);
2596
+ }
2597
+ return "".concat(propPart, "rgba(").concat(r, ", ").concat(g, ", ").concat(b, ", ").concat(alphaValue, ")");
2598
+ });
2599
+ }
2600
+ return modifiedDeclaration;
2601
+ }
2602
+
2603
+ /**
2604
+ * Performance Monitoring Utilities
2605
+ *
2606
+ * Tracks and logs performance metrics for optimization analysis.
2607
+ *
2608
+ * @module utils/performanceMonitor
2609
+ */
2610
+
2611
+
2612
+ /**
2613
+ * Performance monitoring system
2614
+ *
2615
+ * Provides start/end timing and automatic slow operation logging.
2616
+ */
2617
+ const performanceMonitor$1 = {
2618
+ enabled: typeof performance !== "undefined",
2619
+ /**
2620
+ * Start performance measurement
2621
+ *
2622
+ * @param {string} label - Label for the measurement
2623
+ * @returns {Object|null} Marker object with label and start time
2624
+ */
2625
+ start(label) {
2626
+ if (!this.enabled) return null;
2627
+ return {
2628
+ label,
2629
+ startTime: performance.now()
2630
+ };
2631
+ },
2632
+ /**
2633
+ * End performance measurement and log if slow
2634
+ *
2635
+ * @param {Object} marker - Marker from start()
2636
+ */
2637
+ end(marker) {
2638
+ if (!this.enabled || !marker) return;
2639
+ const duration = performance.now() - marker.startTime;
2640
+ if (duration > 5) {
2641
+ // Only log if > 5ms
2642
+ logger.warn("Slow ".concat(marker.label, ": ").concat(duration.toFixed(2), "ms"));
2643
+ }
2644
+ },
2645
+ /**
2646
+ * Measure function execution time
2647
+ *
2648
+ * @param {Function} fn - Function to measure
2649
+ * @param {string} label - Label for measurement
2650
+ * @returns {*} Function result
2651
+ */
2652
+ measure(fn, label) {
2653
+ const marker = this.start(label);
2654
+ try {
2655
+ const result = fn();
2656
+ this.end(marker);
2657
+ return result;
2658
+ } catch (error) {
2659
+ this.end(marker);
2660
+ throw error;
2661
+ }
2662
+ }
2663
+ };
2664
+
2665
+ /**
2666
+ * DOM CSS Injector
2667
+ *
2668
+ * Handles automatic CSS injection into the DOM.
2669
+ * Supports SSR mode, HMR-friendly block registry, and hash-based deduplication.
2670
+ *
2671
+ * @module injector/dom
2672
+ */
2673
+
2674
+
2675
+ /**
2676
+ * Rebuild the style tag from the CSS block registry.
2677
+ *
2678
+ * Used for slot-based updates where each twsx() call owns its own CSS block.
2679
+ * Rebuilds the entire style tag to avoid stale CSS from previous HMR cycles.
2680
+ *
2681
+ * @private
2682
+ */
2683
+ function rebuildStyleTag$1() {
2684
+ let styleTag = document.getElementById("twsx-auto-style");
2685
+ if (!styleTag) {
2686
+ styleTag = document.createElement("style");
2687
+ styleTag.id = "twsx-auto-style";
2688
+ styleTag.setAttribute("data-twsx", "");
2689
+ document.head.appendChild(styleTag);
2690
+ }
2691
+ styleTag.textContent = [...cssBlockRegistry.values()].join("\n");
2692
+ }
2693
+
2694
+ /**
2695
+ * Automatically inject CSS into the DOM or collect for SSR.
2696
+ *
2697
+ * Supports three modes:
2698
+ * 1. SSR mode: Delegates to utils/ssr.js collector
2699
+ * 2. Slot-based mode (with sourceKey): HMR-friendly block registry
2700
+ * 3. Hash-based mode (no sourceKey): Legacy deduplication
2701
+ *
2702
+ * @param {string} cssString - CSS to inject
2703
+ * @param {string|null} sourceKey - Optional unique key for slot-based updates
2704
+ *
2705
+ * @example
2706
+ * // Slot-based (HMR-friendly)
2707
+ * autoInjectCss('.btn { color: blue; }', 'twsx-call-123');
2708
+ *
2709
+ * @example
2710
+ * // Hash-based (legacy)
2711
+ * autoInjectCss('.btn { color: blue; }');
2712
+ *
2713
+ * @example
2714
+ * // SSR mode (delegates to ssr.js)
2715
+ * startSSR();
2716
+ * autoInjectCss('.btn { color: blue; }');
2717
+ * const css = stopSSR();
2718
+ */
2719
+ function autoInjectCss(cssString) {
2720
+ let sourceKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
2721
+ const marker = performanceMonitor$1.start("css:inject");
2722
+ try {
2723
+ // SSR mode: collect CSS strings instead of DOM injection. Delegates to
2724
+ // the shared collector in ./utils/ssr.js so both the legacy
2725
+ // startSSR()/stopSSR() pair and the modern createSSRCollector() actually
2726
+ // receive the CSS generated by tw()/twsx().
2727
+ if (isSSRCollecting()) {
2728
+ collectSSRCSS(cssString);
2729
+ performanceMonitor$1.end(marker);
2730
+ return;
2731
+ }
2732
+ if (IS_BROWSER$1) {
2733
+ if (sourceKey) {
2734
+ // Slot-based update: each unique twsx(obj) call owns its own CSS block.
2735
+ // When styles change (new content, same logical call site via cacheKey)
2736
+ // the old slot is replaced and the style tag is fully rebuilt, so no
2737
+ // stale rules from previous HMR cycles can pile up.
2738
+ const existing = cssBlockRegistry.get(sourceKey);
2739
+ if (existing === cssString) {
2740
+ // Identical content – nothing to do.
2741
+ performanceMonitor$1.end(marker);
2742
+ return;
2743
+ }
2744
+ cssBlockRegistry.set(sourceKey, cssString);
2745
+ rebuildStyleTag$1();
2746
+ if (cssBlockRegistry.size % 10 === 0) {
2747
+ logger.debug("CSS registry stats: ".concat(cssBlockRegistry.size, " blocks registered"));
2748
+ }
2749
+ performanceMonitor$1.end(marker);
2750
+ return;
2751
+ }
2752
+
2753
+ // Fallback path (e.g. direct autoInjectCss calls without a sourceKey):
2754
+ // keep the original hash-based dedup + append behaviour.
2755
+ const cssHash = getCssHash(cssString);
2756
+ if (injectedCssHashSet.has(cssHash)) {
2757
+ performanceMonitor$1.end(marker);
2758
+ return;
2759
+ }
2760
+ injectedCssHashSet.add(cssHash);
2761
+ evictSet(injectedCssHashSet);
2762
+ let styleTag = document.getElementById("twsx-auto-style");
2763
+ if (!styleTag) {
2764
+ styleTag = document.createElement("style");
2765
+ styleTag.id = "twsx-auto-style";
2766
+ styleTag.setAttribute("data-twsx", "");
2767
+ document.head.appendChild(styleTag);
2768
+ }
2769
+
2770
+ // Append CSS to style tag using textContent for reliability
2771
+ // Note: insertRule + textContent mixing destroys CSSOM rules,
2772
+ // so we use textContent exclusively for consistent behavior
2773
+ // with @keyframes, @media, and other nested CSS blocks.
2774
+ styleTag.textContent += "\n".concat(cssString);
2775
+
2776
+ // Log injection stats periodically
2777
+ if (injectedCssHashSet.size % 10 === 0) {
2778
+ logger.debug("CSS injection stats: ".concat(injectedCssHashSet.size, " unique stylesheets injected"));
2779
+ }
2192
2780
  }
2193
- this.cache.set(key, value);
2781
+ performanceMonitor$1.end(marker);
2782
+ } catch (error) {
2783
+ performanceMonitor$1.end(marker);
2784
+ logger.error("Error injecting CSS:", error);
2194
2785
  }
2786
+ }
2195
2787
 
2196
- /**
2197
- * Check if key exists in cache
2198
- */
2199
- has(key) {
2200
- return this.cache.has(key);
2201
- }
2788
+ /**
2789
+ * Bracket Value Encoding/Decoding
2790
+ *
2791
+ * Handles encoding and decoding of bracket values in Tailwind classes.
2792
+ * Example: bg-[rgb(255,0,0)] → bg-[rgb__P__255%2C0%2C0__C__]
2793
+ *
2794
+ * @module parser/bracket
2795
+ */
2202
2796
 
2203
- /**
2204
- * Clear all cache entries
2205
- */
2206
- clear() {
2207
- this.cache.clear();
2208
- }
2209
2797
 
2210
- /**
2211
- * Get current cache size
2212
- */
2213
- get size() {
2214
- return this.cache.size;
2215
- }
2798
+ /**
2799
+ * Encode bracket values to prevent parsing issues.
2800
+ * Encodes parentheses within brackets to special markers.
2801
+ *
2802
+ * @param {string} input - Class string with brackets
2803
+ * @returns {string} Encoded string
2804
+ *
2805
+ * @example
2806
+ * encodeBracketValues('bg-[rgb(255,0,0)]')
2807
+ * // → 'bg-[rgb__P__255%2C0%2C0__C__]'
2808
+ *
2809
+ * @example
2810
+ * encodeBracketValues('w-[calc(100%-20px)]')
2811
+ * // → 'w-[calc__P__100%25-20px__C__]'
2812
+ */
2813
+ function encodeBracketValues(input) {
2814
+ if (!input) return input;
2815
+ if (encodeBracketCache.has(input)) return encodeBracketCache.get(input);
2816
+ BRACKET_CONTENT_REGEX.lastIndex = 0; // Reset global regex
2817
+ const result = input.replace(BRACKET_CONTENT_REGEX, (_, content) => {
2818
+ const encoded = encodeURIComponent(content).replace(OPENING_PAREN_REGEX, "__P__").replace(CLOSING_PAREN_REGEX, "__C__");
2819
+ return "[".concat(encoded, "]");
2820
+ });
2821
+ encodeBracketCache.set(input, result);
2822
+ return result;
2823
+ }
2216
2824
 
2217
- /**
2218
- * Delete specific key
2219
- */
2220
- delete(key) {
2221
- return this.cache.delete(key);
2222
- }
2825
+ /**
2826
+ * Decode previously encoded bracket values.
2827
+ * Reverses the encoding done by encodeBracketValues().
2828
+ *
2829
+ * @param {string} input - Encoded class string
2830
+ * @returns {string} Decoded string
2831
+ *
2832
+ * @example
2833
+ * decodeBracketValues('bg-[rgb__P__255%2C0%2C0__C__]')
2834
+ * // → 'bg-[rgb(255,0,0)]'
2835
+ *
2836
+ * @example
2837
+ * decodeBracketValues('w-[calc__P__100%25-20px__C__]')
2838
+ * // → 'w-[calc(100%-20px)]'
2839
+ */
2840
+ function decodeBracketValues(input) {
2841
+ if (!input) return input;
2842
+ if (decodeBracketCache.has(input)) return decodeBracketCache.get(input);
2843
+ const result = decodeURIComponent(input).replace(ENCODED_PAREN_OPEN_REGEX, "(").replace(ENCODED_PAREN_CLOSE_REGEX, ")");
2844
+ decodeBracketCache.set(input, result);
2845
+ return result;
2223
2846
  }
2224
2847
 
2225
2848
  function generator$2S() {
@@ -5958,220 +6581,94 @@ const patterns = _objectSpread2(_objectSpread2({}, transition), fontFamily);
5958
6581
  * // Arrays (nested)
5959
6582
  * cx(['p-4', 'bg-white'], isActive && ['ring-2', 'ring-blue-500'])
5960
6583
  * // → 'p-4 bg-white ring-2 ring-blue-500'
5961
- *
5962
- * @example
5963
- * // Mixed
5964
- * cx(
5965
- * 'base-class',
5966
- * condition && 'conditional-class',
5967
- * { 'object-class': true, 'ignored-class': false },
5968
- * ['array-class-1', 'array-class-2']
5969
- * )
5970
- */
5971
- function cx() {
5972
- const classes = [];
5973
- for (let i = 0; i < arguments.length; i++) {
5974
- const arg = i < 0 || arguments.length <= i ? undefined : arguments[i];
5975
-
5976
- // Skip falsy values
5977
- if (!arg) continue;
5978
- const type = typeof arg;
5979
- if (type === 'string') {
5980
- classes.push(arg);
5981
- } else if (Array.isArray(arg)) {
5982
- // Recursively process arrays
5983
- const inner = cx(...arg);
5984
- if (inner) classes.push(inner);
5985
- } else if (type === 'object') {
5986
- // Object: keys are class names, values are conditions
5987
- const keys = Object.keys(arg);
5988
- for (let j = 0; j < keys.length; j++) {
5989
- if (arg[keys[j]]) {
5990
- classes.push(keys[j]);
5991
- }
5992
- }
5993
- }
5994
- }
5995
- return classes.join(' ');
5996
- }
5997
-
5998
- /**
5999
- * Create a cx function bound with base classes.
6000
- * Useful for component-level class composition.
6001
- *
6002
- * @param {...(string|Object|Array)} baseArgs - Base class arguments always included
6003
- * @returns {Function} A cx function pre-filled with base classes
6004
- *
6005
- * @example
6006
- * const btnClasses = cx.with('px-4 py-2 rounded font-medium transition-colors')
6007
- *
6008
- * btnClasses('bg-blue-500 text-white')
6009
- * // → 'px-4 py-2 rounded font-medium transition-colors bg-blue-500 text-white'
6010
- *
6011
- * btnClasses({ 'opacity-50 cursor-not-allowed': isDisabled })
6012
- * // → 'px-4 py-2 rounded font-medium transition-colors opacity-50 cursor-not-allowed'
6013
- */
6014
- cx.with = function () {
6015
- for (var _len = arguments.length, baseArgs = new Array(_len), _key = 0; _key < _len; _key++) {
6016
- baseArgs[_key] = arguments[_key];
6017
- }
6018
- return function () {
6019
- for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
6020
- args[_key2] = arguments[_key2];
6021
- }
6022
- return cx(...baseArgs, ...args);
6023
- };
6024
- };
6025
-
6026
- // ============================================================================
6027
- // SSR (Server-Side Rendering) Support
6028
- // Detect environment once at module load for zero-cost runtime checks
6029
- // ============================================================================
6030
- const IS_BROWSER$1 = typeof window !== "undefined" && typeof document !== "undefined";
6031
- const MAX_CACHE_SIZE$1 = 5000;
6032
- const MAX_SET_SIZE = 10000;
6033
-
6034
- // Global registry to track injected keyframes (prevents duplication) - now with LRU
6035
- const _injectedKeyframes = new LRUCache(MAX_SET_SIZE);
6036
-
6037
- // Global LRU cache instances (true LRU, not FIFO)
6038
- const _twsxInputCache = new LRUCache(MAX_CACHE_SIZE$1);
6039
-
6040
- // WeakMap for object identity-based caching (fast lookup for repeated objects)
6041
- const _objectIdentityCache = new WeakMap();
6042
-
6043
- /** @deprecated Use LRUCache instead - kept for backward compatibility */
6044
- function evictSet(set) {
6045
- let maxSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : MAX_SET_SIZE;
6046
- if (set instanceof LRUCache) return; // LRUCache handles its own eviction
6047
- if (set.size <= maxSize) return;
6048
- const excess = set.size - maxSize;
6049
- const iter = set.values();
6050
- for (let i = 0; i < excess; i++) {
6051
- set.delete(iter.next().value);
6052
- }
6053
- }
6054
-
6055
- // Mapping of animation names to their keyframe definitions
6056
- const BUILTIN_KEYFRAMES = {
6057
- spin: {
6058
- "0%": {
6059
- transform: "rotate(0deg)"
6060
- },
6061
- "100%": {
6062
- transform: "rotate(360deg)"
6063
- }
6064
- },
6065
- ping: {
6066
- "75%, 100%": {
6067
- transform: "scale(2)",
6068
- opacity: "0"
6069
- }
6070
- },
6071
- pulse: {
6072
- "50%": {
6073
- opacity: ".5"
6074
- }
6075
- },
6076
- bounce: {
6077
- "0%, 100%": {
6078
- transform: "translateY(-25%)",
6079
- animationTimingFunction: "cubic-bezier(0.8,0,1,1)"
6080
- },
6081
- "50%": {
6082
- transform: "none",
6083
- animationTimingFunction: "cubic-bezier(0,0,0.2,1)"
6084
- }
6085
- },
6086
- fadeIn: {
6087
- "0%": {
6088
- opacity: "0"
6089
- },
6090
- "50%": {
6091
- opacity: "1"
6092
- },
6093
- "100%": {
6094
- opacity: "0"
6095
- }
6096
- },
6097
- slideUp: {
6098
- "0%": {
6099
- transform: "translateY(20px)",
6100
- opacity: "0"
6101
- },
6102
- "50%": {
6103
- transform: "translateY(0)",
6104
- opacity: "1"
6105
- },
6106
- "100%": {
6107
- transform: "translateY(-20px)",
6108
- opacity: "0"
6109
- }
6110
- }
6111
- };
6112
-
6113
- // Generate minified keyframes CSS
6114
- function generateMinifiedKeyframes(animationNames) {
6115
- let css = "";
6116
- for (const name of animationNames) {
6117
- const keyframe = BUILTIN_KEYFRAMES[name];
6118
- if (!keyframe) continue;
6119
- css += "@keyframes ".concat(name, "{");
6120
- for (const _ref of Object.entries(keyframe)) {
6121
- var _ref2 = _slicedToArray(_ref, 2);
6122
- const percentage = _ref2[0];
6123
- const styles = _ref2[1];
6124
- css += "".concat(percentage, "{");
6125
- for (const _ref3 of Object.entries(styles)) {
6126
- var _ref4 = _slicedToArray(_ref3, 2);
6127
- const prop = _ref4[0];
6128
- const value = _ref4[1];
6129
- const cssProp = prop.replace(UPPERCASE_LETTER_REGEX, "-$1").toLowerCase();
6130
- css += "".concat(cssProp, ":").concat(value, ";");
6584
+ *
6585
+ * @example
6586
+ * // Mixed
6587
+ * cx(
6588
+ * 'base-class',
6589
+ * condition && 'conditional-class',
6590
+ * { 'object-class': true, 'ignored-class': false },
6591
+ * ['array-class-1', 'array-class-2']
6592
+ * )
6593
+ */
6594
+ function cx() {
6595
+ const classes = [];
6596
+ for (let i = 0; i < arguments.length; i++) {
6597
+ const arg = i < 0 || arguments.length <= i ? undefined : arguments[i];
6598
+
6599
+ // Skip falsy values
6600
+ if (!arg) continue;
6601
+ const type = typeof arg;
6602
+ if (type === 'string') {
6603
+ classes.push(arg);
6604
+ } else if (Array.isArray(arg)) {
6605
+ // Recursively process arrays
6606
+ const inner = cx(...arg);
6607
+ if (inner) classes.push(inner);
6608
+ } else if (type === 'object') {
6609
+ // Object: keys are class names, values are conditions
6610
+ const keys = Object.keys(arg);
6611
+ for (let j = 0; j < keys.length; j++) {
6612
+ if (arg[keys[j]]) {
6613
+ classes.push(keys[j]);
6614
+ }
6131
6615
  }
6132
- css += "}";
6133
6616
  }
6134
- css += "}";
6135
6617
  }
6136
- return css;
6618
+ return classes.join(' ');
6137
6619
  }
6138
6620
 
6139
- // Opacity modifiers
6140
- const OPACITY_MODIFIER_REGEX = /\/(\d+)$/;
6141
- const OPACITY_PROP_REGEXES = {
6142
- "--text-opacity": /--text-opacity\s*:\s*[\d.]+/gi,
6143
- "--bg-opacity": /--bg-opacity\s*:\s*[\d.]+/gi,
6144
- "--border-opacity": /--border-opacity\s*:\s*[\d.]+/gi,
6145
- "--ring-opacity": /--ring-opacity\s*:\s*[\d.]+/gi,
6146
- "--divide-opacity": /--divide-opacity\s*:\s*[\d.]+/gi,
6147
- "--placeholder-opacity": /--placeholder-opacity\s*:\s*[\d.]+/gi,
6148
- "--text-decoration-opacity": /--text-decoration-opacity\s*:\s*[\d.]+/gi,
6149
- "--outline-opacity": /--outline-opacity\s*:\s*[\d.]+/gi,
6150
- "--accent-opacity": /--accent-opacity\s*:\s*[\d.]+/gi,
6151
- "--caret-opacity": /--caret-opacity\s*:\s*[\d.]+/gi
6621
+ /**
6622
+ * Create a cx function bound with base classes.
6623
+ * Useful for component-level class composition.
6624
+ *
6625
+ * @param {...(string|Object|Array)} baseArgs - Base class arguments always included
6626
+ * @returns {Function} A cx function pre-filled with base classes
6627
+ *
6628
+ * @example
6629
+ * const btnClasses = cx.with('px-4 py-2 rounded font-medium transition-colors')
6630
+ *
6631
+ * btnClasses('bg-blue-500 text-white')
6632
+ * // → 'px-4 py-2 rounded font-medium transition-colors bg-blue-500 text-white'
6633
+ *
6634
+ * btnClasses({ 'opacity-50 cursor-not-allowed': isDisabled })
6635
+ * // → 'px-4 py-2 rounded font-medium transition-colors opacity-50 cursor-not-allowed'
6636
+ */
6637
+ cx.with = function () {
6638
+ for (var _len = arguments.length, baseArgs = new Array(_len), _key = 0; _key < _len; _key++) {
6639
+ baseArgs[_key] = arguments[_key];
6640
+ }
6641
+ return function () {
6642
+ for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
6643
+ args[_key2] = arguments[_key2];
6644
+ }
6645
+ return cx(...baseArgs, ...args);
6646
+ };
6152
6647
  };
6153
6648
 
6649
+ const MAX_CACHE_SIZE$1 = 5000;
6650
+ const MAX_SET_SIZE = 10000;
6651
+
6652
+ // Global registry to track injected keyframes (prevents duplication) - now with LRU
6653
+ const _injectedKeyframes = new LRUCache(MAX_SET_SIZE);
6654
+
6655
+ // Global LRU cache instances (true LRU, not FIFO)
6656
+ const _twsxInputCache = new LRUCache(MAX_CACHE_SIZE$1);
6657
+
6658
+ // WeakMap for object identity-based caching (fast lookup for repeated objects)
6659
+ const _objectIdentityCache = new WeakMap();
6660
+
6154
6661
  // CSS parsing
6155
6662
  const CSS_CLASS_REGEX = /([a-zA-Z0-9\-_\\/.]+)\s*{\s*([^}]+)\s*}/g;
6156
6663
  const DOUBLE_BACKSLASH_REGEX = /\\\\/g;
6157
6664
  const LEADING_UNDERSCORE_REGEX = /^_/;
6158
6665
  const MULTIPLE_SPACES_REGEX = /\s+/g;
6159
6666
 
6160
- // Bracket encoding/decoding
6161
- const BRACKET_CONTENT_REGEX = /\[([^\]]+)\]/g;
6162
- const OPENING_PAREN_REGEX = /\(/g;
6163
- const CLOSING_PAREN_REGEX = /\)/g;
6164
- const ENCODED_PAREN_OPEN_REGEX = /__P__/g;
6165
- const ENCODED_PAREN_CLOSE_REGEX = /__C__/g;
6166
-
6167
6667
  // Variant expansion
6168
6668
  const DIRECTIVE_GROUP_REGEX = /(\w+)\(([^()]+)\)/g;
6169
6669
  const VARIANT_GROUP_REGEX = /(\w+):\(([^()]+(?:\((?:[^()]+)\))?[^()]*)\)/g;
6170
6670
  const WHITESPACE_SPLIT_REGEX = /\s+/;
6171
6671
  const VARIANT_COLON_SPLIT_REGEX = /:/;
6172
-
6173
- // Animation detection
6174
- const ANIMATION_NAME_REGEX = /animation(?:-name)?:\s*([a-zA-Z0-9-]+)/gi;
6175
6672
  const CUSTOM_VALUE_FULL_REGEX = /^(.+?)\[(.+)\]$/;
6176
6673
 
6177
6674
  // Selector variants
@@ -6196,9 +6693,6 @@ for (const prop of COLOR_PROPERTIES) {
6196
6693
  });
6197
6694
  }
6198
6695
 
6199
- // CSS property name conversion
6200
- const UPPERCASE_LETTER_REGEX = /([A-Z])/g;
6201
-
6202
6696
  // Escape characters
6203
6697
  const ESCAPE_SLASH_REGEX = /\//g;
6204
6698
  const ESCAPE_DOT_REGEX = /\./g;
@@ -6402,6 +6896,14 @@ function parseCustomClassWithPatterns(className) {
6402
6896
  return null;
6403
6897
  }
6404
6898
 
6899
+ /**
6900
+ * Resolve all CSS custom properties (var) in a CSS string and output only clear CSS (no custom property)
6901
+ * Optimized with for loops and indexOf for 2-3x better performance
6902
+ * @param {string} cssString
6903
+ * @returns {string} e.g. 'color: rgba(255,255,255,1); background: #fff;'
6904
+ */
6905
+ // NOTE: resolveCssToClearCss now imported from ./generator/css-string.js
6906
+
6405
6907
  // Cache for getConfigOptions - use LRU cache
6406
6908
  const configOptionsCache = new LRUCache(500);
6407
6909
  function generateTailwindCssString() {
@@ -6491,27 +6993,9 @@ const selectorVariants = {
6491
6993
  number: arg => "> :nth-child(".concat(arg, ")")
6492
6994
  };
6493
6995
 
6494
- // Optimize encoding/decoding bracket values with memoization
6495
- const encodeBracketCache = new LRUCache(1000);
6496
- function encodeBracketValues(input) {
6497
- if (!input) return input;
6498
- if (encodeBracketCache.has(input)) return encodeBracketCache.get(input);
6499
- BRACKET_CONTENT_REGEX.lastIndex = 0; // Reset global regex
6500
- const result = input.replace(BRACKET_CONTENT_REGEX, (_, content) => {
6501
- const encoded = encodeURIComponent(content).replace(OPENING_PAREN_REGEX, "__P__").replace(CLOSING_PAREN_REGEX, "__C__");
6502
- return "[".concat(encoded, "]");
6503
- });
6504
- encodeBracketCache.set(input, result);
6505
- return result;
6506
- }
6507
- const decodeBracketCache = new LRUCache(1000);
6508
- function decodeBracketValues(input) {
6509
- if (!input) return input;
6510
- if (decodeBracketCache.has(input)) return decodeBracketCache.get(input);
6511
- const result = decodeURIComponent(input).replace(ENCODED_PAREN_OPEN_REGEX, "(").replace(ENCODED_PAREN_CLOSE_REGEX, ")");
6512
- decodeBracketCache.set(input, result);
6513
- return result;
6514
- }
6996
+ // NOTE: encodeBracketValues and decodeBracketValues now imported from ./parser/bracket.js
6997
+ // NOTE: encodeBracketCache and decodeBracketCache are in ./parser/bracket.js
6998
+
6515
6999
  function replaceSelector(selector) {
6516
7000
  SELECTOR_VARIANT_REGEX.lastIndex = 0; // Reset global regex
6517
7001
  return selector.replace(SELECTOR_VARIANT_REGEX, (_, raw) => {
@@ -6549,73 +7033,6 @@ function resolveVariants(selector, variants) {
6549
7033
  };
6550
7034
  }
6551
7035
 
6552
- /**
6553
- * Process opacity modifier from class name (e.g., text-red-500/50 -> 50% opacity)
6554
- * @param {string} className - Class name with potential opacity modifier
6555
- * @param {string} cssDeclaration - CSS declaration to modify
6556
- * @returns {string} Modified CSS declaration with opacity applied
6557
- */
6558
- function processOpacityModifier(className, cssDeclaration) {
6559
- const opacityMatch = OPACITY_MODIFIER_REGEX.exec(className);
6560
- if (!opacityMatch) return cssDeclaration;
6561
- const opacityValue = parseInt(opacityMatch[1], 10);
6562
- if (opacityValue < 0 || opacityValue > 100) return cssDeclaration;
6563
- const alphaValue = (opacityValue / 100).toString();
6564
-
6565
- // Handle Tailwind's CSS custom property pattern
6566
- let modifiedDeclaration = cssDeclaration;
6567
-
6568
- // Replace opacity custom properties using pre-compiled regexes
6569
- for (const prop in OPACITY_PROP_REGEXES) {
6570
- const regex = OPACITY_PROP_REGEXES[prop];
6571
- regex.lastIndex = 0; // Reset global regex
6572
- modifiedDeclaration = modifiedDeclaration.replace(regex, "".concat(prop, ": ").concat(alphaValue));
6573
- }
6574
-
6575
- // Also handle direct color values using pre-compiled regex patterns
6576
- for (const prop of COLOR_PROPERTIES) {
6577
- const patterns = COLOR_REGEX_PATTERNS.get(prop);
6578
- if (!patterns) continue;
6579
-
6580
- // Reset all regex lastIndex for reuse
6581
- patterns.rgb.lastIndex = 0;
6582
- patterns.rgba.lastIndex = 0;
6583
- patterns.hsl.lastIndex = 0;
6584
- patterns.hsla.lastIndex = 0;
6585
- patterns.hex.lastIndex = 0;
6586
-
6587
- // Convert rgb to rgba with opacity
6588
- modifiedDeclaration = modifiedDeclaration.replace(patterns.rgb, "$1rgba($2, $3, $4, ".concat(alphaValue, ")"));
6589
-
6590
- // Update existing rgba opacity
6591
- modifiedDeclaration = modifiedDeclaration.replace(patterns.rgba, "$1rgba($2, $3, $4, ".concat(alphaValue, ")"));
6592
-
6593
- // Convert hsl to hsla with opacity
6594
- modifiedDeclaration = modifiedDeclaration.replace(patterns.hsl, "$1hsla($2, $3, $4, ".concat(alphaValue, ")"));
6595
-
6596
- // Update existing hsla opacity
6597
- modifiedDeclaration = modifiedDeclaration.replace(patterns.hsla, "$1hsla($2, $3, $4, ".concat(alphaValue, ")"));
6598
-
6599
- // Handle hex colors - convert to rgba
6600
- modifiedDeclaration = modifiedDeclaration.replace(patterns.hex, (match, propPart, hexColor) => {
6601
- // Convert hex to rgba
6602
- const hex = hexColor.replace("#", "");
6603
- let r, g, b;
6604
- if (hex.length === 3) {
6605
- r = parseInt(hex[0] + hex[0], 16);
6606
- g = parseInt(hex[1] + hex[1], 16);
6607
- b = parseInt(hex[2] + hex[2], 16);
6608
- } else {
6609
- r = parseInt(hex.substring(0, 2), 16);
6610
- g = parseInt(hex.substring(2, 4), 16);
6611
- b = parseInt(hex.substring(4, 6), 16);
6612
- }
6613
- return "".concat(propPart, "rgba(").concat(r, ", ").concat(g, ", ").concat(b, ", ").concat(alphaValue, ")");
6614
- });
6615
- }
6616
- return modifiedDeclaration;
6617
- }
6618
-
6619
7036
  // Performance monitoring utilities
6620
7037
  const performanceMonitor = {
6621
7038
  enabled: typeof performance !== "undefined",
@@ -6692,9 +7109,11 @@ function expandGroupedClass(input) {
6692
7109
  } while (result !== prev);
6693
7110
  return result;
6694
7111
  }
6695
- // CSS Processing utilities
7112
+ // CSS Processing utilities
6696
7113
  const parseSelectorCache = new LRUCache(500);
6697
- function parseSelector(selector) {
7114
+
7115
+ // Parse selector with @css property support (different from parser/selector.js)
7116
+ function parseSelectorWithCssProperty(selector) {
6698
7117
  if (parseSelectorCache.has(selector)) {
6699
7118
  return parseSelectorCache.get(selector);
6700
7119
  }
@@ -6719,10 +7138,10 @@ function parseSelector(selector) {
6719
7138
  }
6720
7139
  function processClass(cls, selector, styles) {
6721
7140
  if (cls.trim() === "") return;
6722
- const _ref5 = cls.includes(":") ? [cls.split(":").slice(0, -1), cls.split(":").slice(-1)[0]] : [[], cls],
6723
- _ref6 = _slicedToArray(_ref5, 2),
6724
- rawVariants = _ref6[0],
6725
- className = _ref6[1];
7141
+ const _ref = cls.includes(":") ? [cls.split(":").slice(0, -1), cls.split(":").slice(-1)[0]] : [[], cls],
7142
+ _ref2 = _slicedToArray(_ref, 2),
7143
+ rawVariants = _ref2[0],
7144
+ className = _ref2[1];
6726
7145
  let isImportant = false;
6727
7146
  let pureClassName = className;
6728
7147
  if (className.startsWith("!")) {
@@ -6800,10 +7219,10 @@ function processNestedSelectors(nested, selector, styles, walk) {
6800
7219
  const nestedVal = nested[nestedSel];
6801
7220
  if (nestedSel === "@css" && typeof nestedVal === "object") {
6802
7221
  // For @css directive, use raw CSS values without any processing
6803
- const cssDeclarations = Object.entries(nestedVal).map(_ref7 => {
6804
- let _ref8 = _slicedToArray(_ref7, 2),
6805
- key = _ref8[0],
6806
- value = _ref8[1];
7222
+ const cssDeclarations = Object.entries(nestedVal).map(_ref3 => {
7223
+ let _ref4 = _slicedToArray(_ref3, 2),
7224
+ key = _ref4[0],
7225
+ value = _ref4[1];
6807
7226
  // Convert camelCase to kebab-case (e.g., borderTopColor -> border-top-color)
6808
7227
  const cssKey = key.replace(/([A-Z])/g, "-$1").toLowerCase();
6809
7228
  // Ensure CSS values are properly formatted and not processed through Tailwind conversion
@@ -6826,9 +7245,9 @@ function walkStyleTree(selector, val, styles, walk) {
6826
7245
  logger.warn("Invalid selector in walk function:", selector);
6827
7246
  return;
6828
7247
  }
6829
- const _parseSelector = parseSelector(selector),
6830
- baseSelector = _parseSelector.baseSelector,
6831
- cssProperty = _parseSelector.cssProperty;
7248
+ const _parseSelectorWithCss = parseSelectorWithCssProperty(selector),
7249
+ baseSelector = _parseSelectorWithCss.baseSelector,
7250
+ cssProperty = _parseSelectorWithCss.cssProperty;
6832
7251
  if (cssProperty && typeof val === "object" && Array.isArray(val) && val.length > 0) {
6833
7252
  const cssValue = val[0];
6834
7253
  if (typeof cssValue === "string") {
@@ -6867,9 +7286,9 @@ function walkStyleTree(selector, val, styles, walk) {
6867
7286
  }
6868
7287
  walk(selector, [expandGroupedClass(val)]);
6869
7288
  } else if (typeof val === "object" && val !== null) {
6870
- const _parseSelector2 = parseSelector(selector),
6871
- baseSelector = _parseSelector2.baseSelector,
6872
- cssProperty = _parseSelector2.cssProperty;
7289
+ const _parseSelectorWithCss2 = parseSelectorWithCssProperty(selector),
7290
+ baseSelector = _parseSelectorWithCss2.baseSelector,
7291
+ cssProperty = _parseSelectorWithCss2.cssProperty;
6873
7292
  if (cssProperty) {
6874
7293
  const cssValue = Object.values(val).join(" ");
6875
7294
  styles[baseSelector] = styles[baseSelector] || "";
@@ -6880,10 +7299,10 @@ function walkStyleTree(selector, val, styles, walk) {
6880
7299
  // Check if this is a @css object within the current object
6881
7300
  if (val["@css"] && typeof val["@css"] === "object") {
6882
7301
  // Handle object with @css directive - process the @css part specially
6883
- const cssDeclarations = Object.entries(val["@css"]).map(_ref9 => {
6884
- let _ref0 = _slicedToArray(_ref9, 2),
6885
- key = _ref0[0],
6886
- value = _ref0[1];
7302
+ const cssDeclarations = Object.entries(val["@css"]).map(_ref5 => {
7303
+ let _ref6 = _slicedToArray(_ref5, 2),
7304
+ key = _ref6[0],
7305
+ value = _ref6[1];
6887
7306
  // Convert camelCase to kebab-case (e.g., borderTopColor -> border-top-color)
6888
7307
  const cssKey = key.replace(/([A-Z])/g, "-$1").toLowerCase();
6889
7308
  // Keep CSS values intact without any processing
@@ -7029,9 +7448,9 @@ function generateCssString(styles) {
7029
7448
  let cssString = "";
7030
7449
 
7031
7450
  // Add base styles
7032
- for (const _ref1 of baseStyles) {
7033
- const sel = _ref1.sel;
7034
- const css = _ref1.css;
7451
+ for (const _ref7 of baseStyles) {
7452
+ const sel = _ref7.sel;
7453
+ const css = _ref7.css;
7035
7454
  cssString += "".concat(sel, "{").concat(css.trim().replace(/\n/g, ""), "}");
7036
7455
  }
7037
7456
 
@@ -7041,9 +7460,9 @@ function generateCssString(styles) {
7041
7460
  return match ? parseInt(match[1], 10) : 99999;
7042
7461
  }
7043
7462
  mediaStyles.sort((a, b) => mediaPriority(a.sel) - mediaPriority(b.sel));
7044
- for (const _ref10 of mediaStyles) {
7045
- const sel = _ref10.sel;
7046
- const content = _ref10.content;
7463
+ for (const _ref8 of mediaStyles) {
7464
+ const sel = _ref8.sel;
7465
+ const content = _ref8.content;
7047
7466
  cssString += "".concat(sel, "{");
7048
7467
  for (const subSel in content) {
7049
7468
  cssString += "".concat(subSel, "{").concat(content[subSel].trim().replace(/\n/g, ""), "}");
@@ -7240,14 +7659,14 @@ function twsxNoCache(obj) {
7240
7659
  })
7241
7660
  });
7242
7661
  const configOptions = getConfigOptions(mergedOptions, Object.keys(plugins));
7243
- const _ref11 = configOptions.theme || {},
7244
- _ref11$keyframes = _ref11.keyframes,
7245
- keyframes = _ref11$keyframes === void 0 ? {} : _ref11$keyframes;
7662
+ const _ref9 = configOptions.theme || {},
7663
+ _ref9$keyframes = _ref9.keyframes,
7664
+ keyframes = _ref9$keyframes === void 0 ? {} : _ref9$keyframes;
7246
7665
  let customKeyframesCSS = "";
7247
- for (const _ref12 of Object.entries(keyframes)) {
7248
- var _ref13 = _slicedToArray(_ref12, 2);
7249
- const name = _ref13[0];
7250
- const keyframe = _ref13[1];
7666
+ for (const _ref0 of Object.entries(keyframes)) {
7667
+ var _ref1 = _slicedToArray(_ref0, 2);
7668
+ const name = _ref1[0];
7669
+ const keyframe = _ref1[1];
7251
7670
  // Skip if already in built-in keyframes or already injected
7252
7671
  if (BUILTIN_KEYFRAMES[name] || _injectedKeyframes.has(name)) continue;
7253
7672
 
@@ -7258,15 +7677,15 @@ function twsxNoCache(obj) {
7258
7677
 
7259
7678
  // Generate minified custom keyframe
7260
7679
  customKeyframesCSS += "@keyframes ".concat(name, "{");
7261
- for (const _ref14 of Object.entries(keyframe)) {
7262
- var _ref15 = _slicedToArray(_ref14, 2);
7263
- const percentage = _ref15[0];
7264
- const styles = _ref15[1];
7680
+ for (const _ref10 of Object.entries(keyframe)) {
7681
+ var _ref11 = _slicedToArray(_ref10, 2);
7682
+ const percentage = _ref11[0];
7683
+ const styles = _ref11[1];
7265
7684
  customKeyframesCSS += "".concat(percentage, "{");
7266
- for (const _ref16 of Object.entries(styles)) {
7267
- var _ref17 = _slicedToArray(_ref16, 2);
7268
- const prop = _ref17[0];
7269
- const value = _ref17[1];
7685
+ for (const _ref12 of Object.entries(styles)) {
7686
+ var _ref13 = _slicedToArray(_ref12, 2);
7687
+ const prop = _ref13[0];
7688
+ const value = _ref13[1];
7270
7689
  const cssProp = prop.replace(UPPERCASE_LETTER_REGEX, "-$1").toLowerCase();
7271
7690
  customKeyframesCSS += "".concat(cssProp, ":").concat(value, ";");
7272
7691
  }
@@ -7422,109 +7841,14 @@ function twsx(obj) {
7422
7841
  return result;
7423
7842
  }
7424
7843
 
7425
- // Simple hashCode function for CSS deduplication
7426
- function getCssHash(str) {
7427
- let hash = 0,
7428
- i,
7429
- chr;
7430
- if (str.length === 0) return hash;
7431
- for (i = 0; i < str.length; i++) {
7432
- chr = str.charCodeAt(i);
7433
- hash = (hash << 5) - hash + chr;
7434
- hash |= 0; // Convert to 32bit integer
7435
- }
7436
- return hash;
7437
- }
7438
-
7439
- // Enhanced auto-inject CSS with performance monitoring & SSR support
7440
- const injectedCssHashSet = new Set();
7441
-
7442
- // Registry of sourceKey → cssBlock for smart slot-based replacement.
7443
- // Prevents stale CSS chunks from accumulating across HMR cycles.
7444
- const _cssBlockRegistry = new Map();
7445
-
7446
- /**
7447
- * Rebuild the single twsx style tag from the full CSS block registry.
7448
- * Called whenever a block is added or updated.
7449
- */
7450
- function rebuildStyleTag$1() {
7451
- let styleTag = document.getElementById("twsx-auto-style");
7452
- if (!styleTag) {
7453
- styleTag = document.createElement("style");
7454
- styleTag.id = "twsx-auto-style";
7455
- styleTag.setAttribute("data-twsx", "");
7456
- document.head.appendChild(styleTag);
7457
- }
7458
- styleTag.textContent = [..._cssBlockRegistry.values()].join("\n");
7459
- }
7460
- function autoInjectCss(cssString) {
7461
- let sourceKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
7462
- const marker = performanceMonitor.start("css:inject");
7463
- try {
7464
- // SSR mode: collect CSS strings instead of DOM injection. Delegates to
7465
- // the shared collector in ./utils/ssr.js so both the legacy
7466
- // startSSR()/stopSSR() pair and the modern createSSRCollector() actually
7467
- // receive the CSS generated by tw()/twsx().
7468
- if (isSSRCollecting()) {
7469
- collectSSRCSS(cssString);
7470
- performanceMonitor.end(marker);
7471
- return;
7472
- }
7473
- if (IS_BROWSER$1) {
7474
- if (sourceKey) {
7475
- // Slot-based update: each unique twsx(obj) call owns its own CSS block.
7476
- // When styles change (new content, same logical call site via cacheKey)
7477
- // the old slot is replaced and the style tag is fully rebuilt, so no
7478
- // stale rules from previous HMR cycles can pile up.
7479
- const existing = _cssBlockRegistry.get(sourceKey);
7480
- if (existing === cssString) {
7481
- // Identical content – nothing to do.
7482
- performanceMonitor.end(marker);
7483
- return;
7484
- }
7485
- _cssBlockRegistry.set(sourceKey, cssString);
7486
- rebuildStyleTag$1();
7487
- if (_cssBlockRegistry.size % 10 === 0) {
7488
- logger.debug("CSS registry stats: ".concat(_cssBlockRegistry.size, " blocks registered"));
7489
- }
7490
- performanceMonitor.end(marker);
7491
- return;
7492
- }
7493
-
7494
- // Fallback path (e.g. direct autoInjectCss calls without a sourceKey):
7495
- // keep the original hash-based dedup + append behaviour.
7496
- const cssHash = getCssHash(cssString);
7497
- if (injectedCssHashSet.has(cssHash)) {
7498
- performanceMonitor.end(marker);
7499
- return;
7500
- }
7501
- injectedCssHashSet.add(cssHash);
7502
- evictSet(injectedCssHashSet);
7503
- let styleTag = document.getElementById("twsx-auto-style");
7504
- if (!styleTag) {
7505
- styleTag = document.createElement("style");
7506
- styleTag.id = "twsx-auto-style";
7507
- styleTag.setAttribute("data-twsx", "");
7508
- document.head.appendChild(styleTag);
7509
- }
7510
-
7511
- // Append CSS to style tag using textContent for reliability
7512
- // Note: insertRule + textContent mixing destroys CSSOM rules,
7513
- // so we use textContent exclusively for consistent behavior
7514
- // with @keyframes, @media, and other nested CSS blocks.
7515
- styleTag.textContent += "\n".concat(cssString);
7516
-
7517
- // Log injection stats periodically
7518
- if (injectedCssHashSet.size % 10 === 0) {
7519
- logger.debug("CSS injection stats: ".concat(injectedCssHashSet.size, " unique stylesheets injected"));
7520
- }
7521
- }
7522
- performanceMonitor.end(marker);
7523
- } catch (error) {
7524
- performanceMonitor.end(marker);
7525
- logger.error("Error injecting CSS:", error);
7526
- }
7527
- }
7844
+ // ============================================================================
7845
+ // Main API Exports
7846
+ // ============================================================================
7847
+ // twsxClassName - Unified CSS-in-JS API with variants and slots
7848
+ // tw - Atomic CSS class generator with pseudo-class support
7849
+ // REMOVED: Re-export removed to break circular dependency
7850
+ // Import directly from './className/index.js' if needed
7851
+ // export { twsxClassName, tw } from "./className/index.js";
7528
7852
 
7529
7853
  const _excluded = ["name", "prefix", "hash", "hashLength", "inject", "_"],
7530
7854
  _excluded2 = ["class", "className"],