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