windrunner 1.1.1 → 1.1.2

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.
package/README.md CHANGED
@@ -42,7 +42,7 @@ npm install windrunner
42
42
 
43
43
  ```html
44
44
  <script type="module">
45
- import { windrunner } from "https://cdn.jsdelivr.net/npm/windrunner@1.0.1/dist/index.min.js";
45
+ import { windrunner } from "https://cdn.jsdelivr.net/npm/windrunner@1.1.2/dist/index.min.js";
46
46
  windrunner({ autoStart: true });
47
47
  </script>
48
48
  ```
@@ -279,7 +279,7 @@ windrunner({ autoStart: true, preflight: false });
279
279
  | Arbitrary values | ✓ | ✓ |
280
280
  | Preflight | ✓ | ✓ |
281
281
  | FOUC prevention | ✓ (onReady) | ✗ |
282
- | Plugins | | ✓ |
282
+ | Plugins | | ✓ |
283
283
  | Full utility coverage | ✓ | ✓ |
284
284
 
285
285
  ## 📚 Documentation
package/dist/index.d.ts CHANGED
@@ -35,10 +35,21 @@ export interface WindrunnerOptions {
35
35
  compatGenerateCss?: (options: Record<string, any>) => string;
36
36
  theme?: Record<string, any>;
37
37
  plugins?: Plugin[];
38
+ maxCacheSize?: number;
38
39
  onReady?: () => void;
40
+ onError?: (className: string) => void;
41
+ onCompile?: (className: string, cssRule: string) => void;
39
42
  [key: string]: any;
40
43
  }
41
44
 
45
+ export interface RuntimeStats {
46
+ cacheSize: number;
47
+ insertedRuleCount: number;
48
+ pendingElementCount: number;
49
+ isObserving: boolean;
50
+ isCompatLoaded: boolean;
51
+ }
52
+
42
53
  export interface Runtime {
43
54
  processClassName(className: string): string | undefined;
44
55
  processClassList(classList: string | string[] | ArrayLike<string>): string[];
@@ -48,6 +59,8 @@ export interface Runtime {
48
59
  flush(): void;
49
60
  start(): void;
50
61
  disconnect(): void;
62
+ clearCache(): void;
63
+ getStats(): RuntimeStats;
51
64
  isCompatLoaded(): boolean;
52
65
  getCacheSize(): number;
53
66
  getInsertedRuleCount(): number;
package/dist/index.esm.js CHANGED
@@ -1,59 +1,3 @@
1
- // src/variants.js
2
- var variants = {
3
- accentColor: ["hover", "focus", "not-hover", "not-focus", "not-disabled"],
4
- accessibility: ["hover", "focus"],
5
- aspect: ["hover", "focus"],
6
- backgroundColor: ["hover", "focus", "not-hover", "not-focus", "not-disabled"],
7
- blur: ["hover", "focus"],
8
- borderColor: ["hover", "focus", "not-hover", "not-focus", "not-disabled"],
9
- boxShadow: ["hover", "focus"],
10
- brightness: ["hover", "focus"],
11
- caretColor: ["hover", "focus", "not-hover", "not-focus", "not-disabled"],
12
- contrast: ["hover", "focus"],
13
- dropShadow: ["hover", "focus"],
14
- fill: ["hover", "focus", "not-hover", "not-focus", "not-disabled"],
15
- flexBasis: ["hover", "focus"],
16
- gradientColorStops: ["hover", "focus", "not-hover", "not-focus", "not-disabled"],
17
- grayscale: ["hover", "focus"],
18
- hueRotate: ["hover", "focus"],
19
- insetRing: ["hover", "focus"],
20
- insetShadow: ["hover", "focus"],
21
- invert: ["hover", "focus"],
22
- opacity: ["hover", "focus"],
23
- outlineColor: ["hover", "focus", "not-hover", "not-focus", "not-disabled"],
24
- outlineOffset: ["hover", "focus"],
25
- outlineStyle: ["hover", "focus"],
26
- outlineWidth: ["hover", "focus"],
27
- placeholderColor: ["hover", "focus", "not-hover", "not-focus", "not-disabled"],
28
- ringColor: ["hover", "focus", "not-hover", "not-focus", "not-disabled"],
29
- ringOffsetColor: ["hover", "focus", "not-hover", "not-focus", "not-disabled"],
30
- ringOffsetWidth: ["hover", "focus"],
31
- ringWidth: ["hover", "focus"],
32
- rotate: ["hover", "focus"],
33
- saturate: ["hover", "focus"],
34
- scale: ["hover", "focus"],
35
- sepia: ["hover", "focus"],
36
- skew: ["hover", "focus"],
37
- stroke: ["hover", "focus", "not-hover", "not-focus", "not-disabled"],
38
- strokeWidth: ["hover", "focus"],
39
- textColor: ["hover", "focus", "not-hover", "not-focus", "not-disabled"],
40
- textDecoration: ["focus", "hover"],
41
- textDecorationColor: ["focus", "hover", "not-hover", "not-focus", "not-disabled"],
42
- textDecorationStyle: ["focus", "hover"],
43
- textDecorationThickness: ["focus", "hover"],
44
- textShadowBlur: ["hover", "focus"],
45
- textShadowColor: ["hover", "focus", "not-hover", "not-focus", "not-disabled"],
46
- textShadowOpacity: ["hover", "focus"],
47
- textShadowX: ["hover", "focus"],
48
- textShadowY: ["hover", "focus"],
49
- touchAction: ["hover", "focus"],
50
- mask: ["hover", "focus"],
51
- transform3d: ["hover", "focus"],
52
- translate: ["hover", "focus"],
53
- zIndex: ["hover", "focus"]
54
- };
55
- var variants_default = variants;
56
-
57
1
  // src/theme.js
58
2
  var theme = {
59
3
  accentColor: ({ theme: theme2 }) => ({
@@ -1401,7 +1345,6 @@ var vars_default = vars;
1401
1345
 
1402
1346
  // src/config.js
1403
1347
  var configOptions = {
1404
- variants: variants_default,
1405
1348
  theme: theme_default,
1406
1349
  vars: vars_default
1407
1350
  };
@@ -3308,8 +3251,8 @@ var PluginRegistry = class {
3308
3251
  * Register multiple variants at once
3309
3252
  * @param {Object} variants - Object mapping names to handlers
3310
3253
  */
3311
- addVariants(variants2) {
3312
- Object.entries(variants2).forEach(([name, handler]) => {
3254
+ addVariants(variants) {
3255
+ Object.entries(variants).forEach(([name, handler]) => {
3313
3256
  this.addVariant(name, handler);
3314
3257
  });
3315
3258
  }
@@ -3590,9 +3533,75 @@ function compileBaseToken(baseToken, theme2, pluginRegistry) {
3590
3533
  }
3591
3534
  return checkAllBuilders(baseToken, theme2);
3592
3535
  }
3593
- function applyVariants(selector, variants2, pluginRegistry) {
3536
+ var VARIANT_MAP = /* @__PURE__ */ new Map([
3537
+ // Dark mode
3538
+ ["dark", (s) => `.dark ${s}`],
3539
+ // Pseudo-classes – interactive
3540
+ ["hover", (s) => `${s}:hover`],
3541
+ ["focus", (s) => `${s}:focus`],
3542
+ ["focus-visible", (s) => `${s}:focus-visible`],
3543
+ ["focus-within", (s) => `${s}:focus-within`],
3544
+ ["active", (s) => `${s}:active`],
3545
+ ["visited", (s) => `${s}:visited`],
3546
+ ["disabled", (s) => `${s}:disabled`],
3547
+ ["checked", (s) => `${s}:checked`],
3548
+ ["indeterminate", (s) => `${s}:indeterminate`],
3549
+ ["required", (s) => `${s}:required`],
3550
+ ["valid", (s) => `${s}:valid`],
3551
+ ["invalid", (s) => `${s}:invalid`],
3552
+ ["target", (s) => `${s}:target`],
3553
+ ["enabled", (s) => `${s}:enabled`],
3554
+ ["default", (s) => `${s}:default`],
3555
+ ["optional", (s) => `${s}:optional`],
3556
+ ["user-valid", (s) => `${s}:user-valid`],
3557
+ ["user-invalid", (s) => `${s}:user-invalid`],
3558
+ ["in-range", (s) => `${s}:in-range`],
3559
+ ["out-of-range", (s) => `${s}:out-of-range`],
3560
+ ["placeholder-shown", (s) => `${s}:placeholder-shown`],
3561
+ ["autofill", (s) => `${s}:autofill`],
3562
+ ["details-content", (s) => `${s}:details-content`],
3563
+ ["read-only", (s) => `${s}:read-only`],
3564
+ ["open", (s) => `${s}[open]`],
3565
+ // Pseudo-elements
3566
+ ["placeholder", (s) => `${s}::placeholder`],
3567
+ ["backdrop", (s) => `${s}::backdrop`],
3568
+ ["before", (s) => `${s}::before`],
3569
+ ["after", (s) => `${s}::after`],
3570
+ ["first-letter", (s) => `${s}::first-letter`],
3571
+ ["first-line", (s) => `${s}::first-line`],
3572
+ ["marker", (s) => `${s}::marker`],
3573
+ ["selection", (s) => `${s}::selection`],
3574
+ ["file", (s) => `${s}::file-selector-button`],
3575
+ // Structural pseudo-classes
3576
+ ["first", (s) => `${s}:first-child`],
3577
+ ["last", (s) => `${s}:last-child`],
3578
+ ["odd", (s) => `${s}:nth-child(odd)`],
3579
+ ["even", (s) => `${s}:nth-child(even)`],
3580
+ ["first-of-type", (s) => `${s}:first-of-type`],
3581
+ ["last-of-type", (s) => `${s}:last-of-type`],
3582
+ ["only", (s) => `${s}:only-child`],
3583
+ ["only-of-type", (s) => `${s}:only-of-type`],
3584
+ ["empty", (s) => `${s}:empty`],
3585
+ // Group & peer variants
3586
+ ["group-hover", (s) => `.group:hover ${s}`],
3587
+ ["group-focus", (s) => `.group:focus ${s}`],
3588
+ ["group-active", (s) => `.group:active ${s}`],
3589
+ ["peer-hover", (s) => `.peer:hover ~ ${s}`],
3590
+ ["peer-focus", (s) => `.peer:focus ~ ${s}`],
3591
+ ["peer-checked", (s) => `.peer:checked ~ ${s}`],
3592
+ ["peer-disabled", (s) => `.peer:disabled ~ ${s}`],
3593
+ // Negation variants
3594
+ ["not-hover", (s) => `${s}:not(:hover)`],
3595
+ ["not-focus", (s) => `${s}:not(:focus)`],
3596
+ ["not-disabled", (s) => `${s}:not(:disabled)`],
3597
+ ["not-checked", (s) => `${s}:not(:checked)`],
3598
+ // In-* variants (group-based)
3599
+ ["in-hover", (s) => `.group:hover ${s}`],
3600
+ ["in-focus", (s) => `.group:focus ${s}`]
3601
+ ]);
3602
+ function applyVariants(selector, variants, pluginRegistry) {
3594
3603
  let currentSelector = selector;
3595
- for (const variant of variants2) {
3604
+ for (const variant of variants) {
3596
3605
  if (pluginRegistry) {
3597
3606
  const customHandler = pluginRegistry.matchVariant(variant);
3598
3607
  if (customHandler) {
@@ -3607,180 +3616,11 @@ function applyVariants(selector, variants2, pluginRegistry) {
3607
3616
  }
3608
3617
  }
3609
3618
  }
3610
- switch (variant) {
3611
- case "dark":
3612
- currentSelector = `.dark ${currentSelector}`;
3613
- break;
3614
- case "hover":
3615
- currentSelector = `${currentSelector}:hover`;
3616
- break;
3617
- case "focus":
3618
- currentSelector = `${currentSelector}:focus`;
3619
- break;
3620
- case "focus-visible":
3621
- currentSelector = `${currentSelector}:focus-visible`;
3622
- break;
3623
- case "focus-within":
3624
- currentSelector = `${currentSelector}:focus-within`;
3625
- break;
3626
- case "active":
3627
- currentSelector = `${currentSelector}:active`;
3628
- break;
3629
- case "visited":
3630
- currentSelector = `${currentSelector}:visited`;
3631
- break;
3632
- case "disabled":
3633
- currentSelector = `${currentSelector}:disabled`;
3634
- break;
3635
- case "checked":
3636
- currentSelector = `${currentSelector}:checked`;
3637
- break;
3638
- case "indeterminate":
3639
- currentSelector = `${currentSelector}:indeterminate`;
3640
- break;
3641
- case "required":
3642
- currentSelector = `${currentSelector}:required`;
3643
- break;
3644
- case "valid":
3645
- currentSelector = `${currentSelector}:valid`;
3646
- break;
3647
- case "invalid":
3648
- currentSelector = `${currentSelector}:invalid`;
3649
- break;
3650
- case "target":
3651
- currentSelector = `${currentSelector}:target`;
3652
- break;
3653
- case "enabled":
3654
- currentSelector = `${currentSelector}:enabled`;
3655
- break;
3656
- case "default":
3657
- currentSelector = `${currentSelector}:default`;
3658
- break;
3659
- case "optional":
3660
- currentSelector = `${currentSelector}:optional`;
3661
- break;
3662
- case "user-valid":
3663
- currentSelector = `${currentSelector}:user-valid`;
3664
- break;
3665
- case "user-invalid":
3666
- currentSelector = `${currentSelector}:user-invalid`;
3667
- break;
3668
- case "in-range":
3669
- currentSelector = `${currentSelector}:in-range`;
3670
- break;
3671
- case "out-of-range":
3672
- currentSelector = `${currentSelector}:out-of-range`;
3673
- break;
3674
- case "placeholder-shown":
3675
- currentSelector = `${currentSelector}:placeholder-shown`;
3676
- break;
3677
- case "autofill":
3678
- currentSelector = `${currentSelector}:autofill`;
3679
- break;
3680
- case "details-content":
3681
- currentSelector = `${currentSelector}:details-content`;
3682
- break;
3683
- case "placeholder":
3684
- currentSelector = `${currentSelector}::placeholder`;
3685
- break;
3686
- case "backdrop":
3687
- currentSelector = `${currentSelector}::backdrop`;
3688
- break;
3689
- case "before":
3690
- currentSelector = `${currentSelector}::before`;
3691
- break;
3692
- case "after":
3693
- currentSelector = `${currentSelector}::after`;
3694
- break;
3695
- case "first-letter":
3696
- currentSelector = `${currentSelector}::first-letter`;
3697
- break;
3698
- case "first-line":
3699
- currentSelector = `${currentSelector}::first-line`;
3700
- break;
3701
- case "marker":
3702
- currentSelector = `${currentSelector}::marker`;
3703
- break;
3704
- case "selection":
3705
- currentSelector = `${currentSelector}::selection`;
3706
- break;
3707
- case "file":
3708
- currentSelector = `${currentSelector}::file-selector-button`;
3709
- break;
3710
- case "first":
3711
- currentSelector = `${currentSelector}:first-child`;
3712
- break;
3713
- case "last":
3714
- currentSelector = `${currentSelector}:last-child`;
3715
- break;
3716
- case "odd":
3717
- currentSelector = `${currentSelector}:nth-child(odd)`;
3718
- break;
3719
- case "even":
3720
- currentSelector = `${currentSelector}:nth-child(even)`;
3721
- break;
3722
- case "first-of-type":
3723
- currentSelector = `${currentSelector}:first-of-type`;
3724
- break;
3725
- case "last-of-type":
3726
- currentSelector = `${currentSelector}:last-of-type`;
3727
- break;
3728
- case "only":
3729
- currentSelector = `${currentSelector}:only-child`;
3730
- break;
3731
- case "only-of-type":
3732
- currentSelector = `${currentSelector}:only-of-type`;
3733
- break;
3734
- case "empty":
3735
- currentSelector = `${currentSelector}:empty`;
3736
- break;
3737
- case "read-only":
3738
- currentSelector = `${currentSelector}:read-only`;
3739
- break;
3740
- case "open":
3741
- currentSelector = `${currentSelector}[open]`;
3742
- break;
3743
- case "group-hover":
3744
- currentSelector = `.group:hover ${currentSelector}`;
3745
- break;
3746
- case "group-focus":
3747
- currentSelector = `.group:focus ${currentSelector}`;
3748
- break;
3749
- case "group-active":
3750
- currentSelector = `.group:active ${currentSelector}`;
3751
- break;
3752
- case "peer-hover":
3753
- currentSelector = `.peer:hover ~ ${currentSelector}`;
3754
- break;
3755
- case "peer-focus":
3756
- currentSelector = `.peer:focus ~ ${currentSelector}`;
3757
- break;
3758
- case "peer-checked":
3759
- currentSelector = `.peer:checked ~ ${currentSelector}`;
3760
- break;
3761
- case "peer-disabled":
3762
- currentSelector = `.peer:disabled ~ ${currentSelector}`;
3763
- break;
3764
- case "not-hover":
3765
- currentSelector = `${currentSelector}:not(:hover)`;
3766
- break;
3767
- case "not-focus":
3768
- currentSelector = `${currentSelector}:not(:focus)`;
3769
- break;
3770
- case "not-disabled":
3771
- currentSelector = `${currentSelector}:not(:disabled)`;
3772
- break;
3773
- case "not-checked":
3774
- currentSelector = `${currentSelector}:not(:checked)`;
3775
- break;
3776
- case "in-hover":
3777
- currentSelector = `.group:hover ${currentSelector}`;
3778
- break;
3779
- case "in-focus":
3780
- currentSelector = `.group:focus ${currentSelector}`;
3781
- break;
3782
- default:
3783
- return void 0;
3619
+ const builtinHandler = VARIANT_MAP.get(variant);
3620
+ if (builtinHandler) {
3621
+ currentSelector = builtinHandler(currentSelector);
3622
+ } else {
3623
+ return void 0;
3784
3624
  }
3785
3625
  }
3786
3626
  return currentSelector;
@@ -3796,7 +3636,7 @@ function resolveRuntimeContext(options = {}) {
3796
3636
  addUtility: (pattern, handler) => pluginRegistry.addUtility(pattern, handler),
3797
3637
  addUtilities: (utilities) => pluginRegistry.addUtilities(utilities),
3798
3638
  addVariant: (name, handler) => pluginRegistry.addVariant(name, handler),
3799
- addVariants: (variants2) => pluginRegistry.addVariants(variants2),
3639
+ addVariants: (variants) => pluginRegistry.addVariants(variants),
3800
3640
  theme: (key) => {
3801
3641
  if (!key) return config.theme || {};
3802
3642
  const keys = key.split(".");
@@ -3849,7 +3689,7 @@ function parseClass(className, screens = {}, containers = {}) {
3849
3689
  const parts = splitByVariantDelimiter(normalized);
3850
3690
  if (parts.length === 0) return null;
3851
3691
  const baseToken = parts[parts.length - 1];
3852
- const variants2 = [];
3692
+ const variants = [];
3853
3693
  let breakpoint = null;
3854
3694
  let containerBreakpoint = null;
3855
3695
  let starting = false;
@@ -3870,9 +3710,9 @@ function parseClass(className, screens = {}, containers = {}) {
3870
3710
  breakpoint = part;
3871
3711
  continue;
3872
3712
  }
3873
- variants2.push(part);
3713
+ variants.push(part);
3874
3714
  }
3875
- const result = { original: token, baseToken, variants: variants2, breakpoint, containerBreakpoint, important, starting };
3715
+ const result = { original: token, baseToken, variants, breakpoint, containerBreakpoint, important, starting };
3876
3716
  if (parseCache.size >= PARSE_CACHE_MAX_SIZE) {
3877
3717
  const firstKey = parseCache.keys().next().value;
3878
3718
  parseCache.delete(firstKey);
@@ -3981,6 +3821,9 @@ function createWindrunner(options = {}) {
3981
3821
  const preflight2 = options.preflight !== false;
3982
3822
  const compatMode = options.compatMode || "none";
3983
3823
  const compatStyleId = options.compatStyleId || `${styleId}-full`;
3824
+ const maxCacheSize = options.maxCacheSize || 1e4;
3825
+ const onError = typeof options.onError === "function" ? options.onError : null;
3826
+ const onCompile = typeof options.onCompile === "function" ? options.onCompile : null;
3984
3827
  const tailwindOptions = getBaseTailwindOptions(options);
3985
3828
  const context = resolveRuntimeContext(tailwindOptions);
3986
3829
  const cache = /* @__PURE__ */ new Map();
@@ -4026,6 +3869,10 @@ function createWindrunner(options = {}) {
4026
3869
  const compileWithCache = (className) => {
4027
3870
  if (cache.has(className)) return cache.get(className);
4028
3871
  const cssRule = compileRuntimeClassNameWithContext(className, context);
3872
+ if (cache.size >= maxCacheSize) {
3873
+ const firstKey = cache.keys().next().value;
3874
+ cache.delete(firstKey);
3875
+ }
4029
3876
  cache.set(className, cssRule);
4030
3877
  return cssRule;
4031
3878
  };
@@ -4048,8 +3895,10 @@ function createWindrunner(options = {}) {
4048
3895
  const cssRule = compileWithCache(className);
4049
3896
  if (!cssRule) {
4050
3897
  ensureCompatStyle();
3898
+ if (onError) onError(className);
4051
3899
  } else {
4052
3900
  insertRule(cssRule);
3901
+ if (onCompile) onCompile(className, cssRule);
4053
3902
  }
4054
3903
  return cssRule;
4055
3904
  };
@@ -4152,6 +4001,16 @@ function createWindrunner(options = {}) {
4152
4001
  }
4153
4002
  runStart();
4154
4003
  };
4004
+ const clearCache = () => {
4005
+ cache.clear();
4006
+ };
4007
+ const getStats = () => ({
4008
+ cacheSize: cache.size,
4009
+ insertedRuleCount: insertedRules.size,
4010
+ pendingElementCount: pendingElements.size,
4011
+ isObserving: observer !== null,
4012
+ isCompatLoaded: compatLoaded
4013
+ });
4155
4014
  return {
4156
4015
  processClassName,
4157
4016
  processClassList,
@@ -4161,6 +4020,8 @@ function createWindrunner(options = {}) {
4161
4020
  flush,
4162
4021
  start,
4163
4022
  disconnect,
4023
+ clearCache,
4024
+ getStats,
4164
4025
  isCompatLoaded: () => compatLoaded,
4165
4026
  getCacheSize: () => cache.size,
4166
4027
  getInsertedRuleCount: () => insertedRules.size