@kbach/react 0.6.4 → 0.6.5

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
@@ -88,6 +88,8 @@ import './kbach.css';
88
88
 
89
89
  The plugin scans your source files and writes generated CSS between the `/* kbach:start */`/`/* kbach:end */` markers. Importing `kbach.css` automatically disables runtime injection — all styles come from the file instead. On HMR, only the changed file is rescanned; unchanged tokens reuse cached CSS.
90
90
 
91
+ While it's scanning, the plugin also indexes every `.css`/`.scss`/`.sass`/`.less` file under the same directories and warns in the terminal (not the browser console) for any class that's neither a real Kbach utility nor defined anywhere in those stylesheets — likely a typo. A class you've defined yourself elsewhere (CSS Modules, styled-components, a third-party component's class) is recognized as soon as something in the project literally has a `.that-class-name` rule, so it won't get flagged just because Kbach itself doesn't know it.
92
+
91
93
  ## Next.js
92
94
 
93
95
  Step 1 (tsconfig `jsxImportSource`) and step 2 (`ThemeProvider`) above apply as-is — Next.js's SWC compiler reads `jsxImportSource` from `tsconfig.json` the same way Vite does, and `@kbach/react` shipping its own `"use client"` directive means `ThemeProvider` works directly in a Server Component tree (e.g. the App Router root `layout.tsx`) with no wrapper of your own needed.
@@ -298,6 +300,17 @@ Negated: `not-hover:`, `not-focus:`, `not-pressed:`, `not-active:`, `not-disable
298
300
  <p className="peer-focus:text-blue-6" />
299
301
  ```
300
302
 
303
+ Nested groups need names, or the inner element reacts to whichever `.group` is nearest — not necessarily the one you meant:
304
+
305
+ ```jsx
306
+ <div className="group/card">
307
+ <div className="group/icon">
308
+ <span className="group-hover/icon:opacity-100" /> {/* only the inner group */}
309
+ </div>
310
+ <span className="group-hover/card:underline" /> {/* only the outer group */}
311
+ </div>
312
+ ```
313
+
301
314
  ### Responsive
302
315
 
303
316
  | Modifier | Min-width |
@@ -650,11 +650,30 @@ function clearPluginModifiers() {
650
650
  for (const k of Object.keys(_pluginModifiers)) delete _pluginModifiers[k];
651
651
  _invalidate();
652
652
  }
653
+ var NAMED_GROUP_PEER_RE = /^(group|peer)-(hover|focus)\/(.+)$/;
654
+ var _namedModifierCache = /* @__PURE__ */ new Map();
655
+ function getNamedGroupPeerModifier(name) {
656
+ const cached = _namedModifierCache.get(name);
657
+ if (cached) return cached;
658
+ const m = NAMED_GROUP_PEER_RE.exec(name);
659
+ if (!m) return void 0;
660
+ const [, kind, trigger, groupName] = m;
661
+ const escapedName = escapeCSSSelector(groupName);
662
+ const pseudo = trigger === "hover" ? ":hover" : ":focus";
663
+ const combinator = kind === "group" ? " " : " ~ ";
664
+ const def = {
665
+ ancestorSelector: `.${kind}\\/${escapedName}${pseudo}${combinator}`,
666
+ jsBehavior: "css-only",
667
+ forcesImportant: true
668
+ };
669
+ _namedModifierCache.set(name, def);
670
+ return def;
671
+ }
653
672
  function getModifier(name) {
654
- return BUILTIN_MODIFIERS[name] ?? _pluginModifiers[name];
673
+ return BUILTIN_MODIFIERS[name] ?? _pluginModifiers[name] ?? getNamedGroupPeerModifier(name);
655
674
  }
656
675
  function isKnownModifier(name) {
657
- return name in BUILTIN_MODIFIERS || name in _pluginModifiers;
676
+ return name in BUILTIN_MODIFIERS || name in _pluginModifiers || NAMED_GROUP_PEER_RE.test(name);
658
677
  }
659
678
  function _allEntries() {
660
679
  return [...Object.entries(BUILTIN_MODIFIERS), ...Object.entries(_pluginModifiers)];
@@ -2331,10 +2350,12 @@ for (const [utility, prop] of Object.entries(PADDING_MARGIN_PROPS)) {
2331
2350
  for (const [utility, [widthProp, colorProp]] of Object.entries(BORDER_SIDE_PROPS)) {
2332
2351
  RESOLVERS[utility] = makeBorderSideResolver(widthProp, colorProp);
2333
2352
  }
2353
+ var NAMED_GROUP_PEER_MARKER_RE = /^(group|peer)\/.+$/;
2334
2354
  function resolveUtility(parsed, theme) {
2335
2355
  if (!parsed.value) {
2336
2356
  if (parsed.utility in PLUGIN_STANDALONE) return PLUGIN_STANDALONE[parsed.utility] ?? null;
2337
2357
  if (parsed.utility in getStandalone()) return getStandalone()[parsed.utility] ?? null;
2358
+ if (NAMED_GROUP_PEER_MARKER_RE.test(parsed.utility)) return getEffectiveIsWeb() ? {} : null;
2338
2359
  }
2339
2360
  const resolver = PLUGIN_RESOLVERS[parsed.utility] ?? RESOLVERS[parsed.utility];
2340
2361
  if (resolver) return resolver(parsed, theme);
@@ -2352,7 +2373,7 @@ function getPluginStandaloneMap() {
2352
2373
  return PLUGIN_STANDALONE;
2353
2374
  }
2354
2375
  function isKnownUtility(utility) {
2355
- return utility in getStandalone() || utility in RESOLVERS || utility in PLUGIN_STANDALONE || utility in PLUGIN_RESOLVERS;
2376
+ return utility in getStandalone() || utility in RESOLVERS || utility in PLUGIN_STANDALONE || utility in PLUGIN_RESOLVERS || NAMED_GROUP_PEER_MARKER_RE.test(utility);
2356
2377
  }
2357
2378
  var _sortedPrefixes = null;
2358
2379
  var _standaloneNames = null;
@@ -3631,9 +3652,19 @@ var InteractiveWrapper = forwardRef(
3631
3652
  style: finalStyle,
3632
3653
  ...!isNative && className ? { className } : {},
3633
3654
  // On web, onPointerDown/Up drive the wrapper's own pressed state (covers mouse + touch),
3634
- // but onPressIn/onPressOut are still forwarded unchanged — react-native-web components
3635
- // (and any component that accepts both prop names) depend on receiving them directly.
3636
- ...!isNative ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave, onPointerCancel: handlePointerCancel, onPressIn, onPressOut } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
3655
+ // and onPressIn/onPressOut are ALSO forwarded — but only when Component is an actual
3656
+ // component (react-native-web components accept both prop names), never when it's a
3657
+ // literal HTML tag string ('div', 'a', …). React DOM warns "Unknown event handler
3658
+ // property" for onPressIn/onPressOut on a host element even when the value is
3659
+ // undefined — the check is on the prop KEY being present at all, not its value — so
3660
+ // the key must be omitted entirely for string components, not just set to undefined.
3661
+ ...!isNative ? {
3662
+ onPointerDown: handlePointerDown,
3663
+ onPointerUp: handlePointerUp,
3664
+ onPointerLeave: handlePointerLeave,
3665
+ onPointerCancel: handlePointerCancel,
3666
+ ...typeof Component !== "string" ? { onPressIn, onPressOut } : {}
3667
+ } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
3637
3668
  onMouseEnter: handleMouseEnter,
3638
3669
  onMouseLeave: handleMouseLeave,
3639
3670
  onFocus: handleFocus,
@@ -23,7 +23,7 @@ import {
23
23
  transformToWebProps,
24
24
  useConditionalWidth,
25
25
  useGlobalDarkMode
26
- } from "./chunk-SVPKCLOI.mjs";
26
+ } from "./chunk-BAUO36PM.mjs";
27
27
 
28
28
  // src/jsx-runtime.tsx
29
29
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
package/dist/index.js CHANGED
@@ -775,11 +775,30 @@ function clearPluginModifiers() {
775
775
  for (const k of Object.keys(_pluginModifiers)) delete _pluginModifiers[k];
776
776
  _invalidate();
777
777
  }
778
+ var NAMED_GROUP_PEER_RE = /^(group|peer)-(hover|focus)\/(.+)$/;
779
+ var _namedModifierCache = /* @__PURE__ */ new Map();
780
+ function getNamedGroupPeerModifier(name) {
781
+ const cached = _namedModifierCache.get(name);
782
+ if (cached) return cached;
783
+ const m = NAMED_GROUP_PEER_RE.exec(name);
784
+ if (!m) return void 0;
785
+ const [, kind, trigger, groupName] = m;
786
+ const escapedName = escapeCSSSelector(groupName);
787
+ const pseudo = trigger === "hover" ? ":hover" : ":focus";
788
+ const combinator = kind === "group" ? " " : " ~ ";
789
+ const def = {
790
+ ancestorSelector: `.${kind}\\/${escapedName}${pseudo}${combinator}`,
791
+ jsBehavior: "css-only",
792
+ forcesImportant: true
793
+ };
794
+ _namedModifierCache.set(name, def);
795
+ return def;
796
+ }
778
797
  function getModifier(name) {
779
- return BUILTIN_MODIFIERS[name] ?? _pluginModifiers[name];
798
+ return BUILTIN_MODIFIERS[name] ?? _pluginModifiers[name] ?? getNamedGroupPeerModifier(name);
780
799
  }
781
800
  function isKnownModifier(name) {
782
- return name in BUILTIN_MODIFIERS || name in _pluginModifiers;
801
+ return name in BUILTIN_MODIFIERS || name in _pluginModifiers || NAMED_GROUP_PEER_RE.test(name);
783
802
  }
784
803
  function _allEntries() {
785
804
  return [...Object.entries(BUILTIN_MODIFIERS), ...Object.entries(_pluginModifiers)];
@@ -2450,10 +2469,12 @@ for (const [utility, prop] of Object.entries(PADDING_MARGIN_PROPS)) {
2450
2469
  for (const [utility, [widthProp, colorProp]] of Object.entries(BORDER_SIDE_PROPS)) {
2451
2470
  RESOLVERS[utility] = makeBorderSideResolver(widthProp, colorProp);
2452
2471
  }
2472
+ var NAMED_GROUP_PEER_MARKER_RE = /^(group|peer)\/.+$/;
2453
2473
  function resolveUtility(parsed, theme) {
2454
2474
  if (!parsed.value) {
2455
2475
  if (parsed.utility in PLUGIN_STANDALONE) return PLUGIN_STANDALONE[parsed.utility] ?? null;
2456
2476
  if (parsed.utility in getStandalone()) return getStandalone()[parsed.utility] ?? null;
2477
+ if (NAMED_GROUP_PEER_MARKER_RE.test(parsed.utility)) return getEffectiveIsWeb() ? {} : null;
2457
2478
  }
2458
2479
  const resolver = PLUGIN_RESOLVERS[parsed.utility] ?? RESOLVERS[parsed.utility];
2459
2480
  if (resolver) return resolver(parsed, theme);
@@ -2471,7 +2492,7 @@ function getPluginStandaloneMap() {
2471
2492
  return PLUGIN_STANDALONE;
2472
2493
  }
2473
2494
  function isKnownUtility(utility) {
2474
- return utility in getStandalone() || utility in RESOLVERS || utility in PLUGIN_STANDALONE || utility in PLUGIN_RESOLVERS;
2495
+ return utility in getStandalone() || utility in RESOLVERS || utility in PLUGIN_STANDALONE || utility in PLUGIN_RESOLVERS || NAMED_GROUP_PEER_MARKER_RE.test(utility);
2475
2496
  }
2476
2497
  var _sortedPrefixes = null;
2477
2498
  var _standaloneNames = null;
@@ -3917,8 +3938,25 @@ function styled(Component, baseClasses = "") {
3917
3938
  // Only attach state-tracking handlers when interactive modifiers are present.
3918
3939
  // Always forward user-provided handlers to avoid silently swallowing them.
3919
3940
  // onPressIn/onPressOut are forwarded on web too (not just native) since
3920
- // react-native-web components rely on receiving them directly.
3921
- ...isWeb ? hasInteractive ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave, onPointerCancel: handlePointerCancel, onPressIn, onPressOut } : { onPointerDown, onPointerUp, onPointerLeave, onPointerCancel, onPressIn, onPressOut } : hasInteractive ? { onPressIn: handlePressIn, onPressOut: handlePressOut } : { onPressIn, onPressOut },
3941
+ // react-native-web components rely on receiving them directly — but only
3942
+ // when effectiveComponent is an actual component, never a literal HTML tag
3943
+ // string ('div', 'a', …). React DOM warns "Unknown event handler property"
3944
+ // for onPressIn/onPressOut on a host element even when the value is
3945
+ // undefined — the check is on the prop KEY being present at all, not its
3946
+ // value — so the key must be omitted entirely for string components.
3947
+ ...isWeb ? hasInteractive ? {
3948
+ onPointerDown: handlePointerDown,
3949
+ onPointerUp: handlePointerUp,
3950
+ onPointerLeave: handlePointerLeave,
3951
+ onPointerCancel: handlePointerCancel,
3952
+ ...typeof effectiveComponent !== "string" ? { onPressIn, onPressOut } : {}
3953
+ } : {
3954
+ onPointerDown,
3955
+ onPointerUp,
3956
+ onPointerLeave,
3957
+ onPointerCancel,
3958
+ ...typeof effectiveComponent !== "string" ? { onPressIn, onPressOut } : {}
3959
+ } : hasInteractive ? { onPressIn: handlePressIn, onPressOut: handlePressOut } : { onPressIn, onPressOut },
3922
3960
  onMouseEnter: hasInteractive ? handleMouseEnter : onMouseEnter,
3923
3961
  onMouseLeave: hasInteractive ? handleMouseLeave : onMouseLeave,
3924
3962
  onFocus: hasInteractive ? handleFocus : onFocus,
@@ -4039,9 +4077,19 @@ var InteractiveWrapper = (0, import_react8.forwardRef)(
4039
4077
  style: finalStyle,
4040
4078
  ...!isNative && className ? { className } : {},
4041
4079
  // On web, onPointerDown/Up drive the wrapper's own pressed state (covers mouse + touch),
4042
- // but onPressIn/onPressOut are still forwarded unchanged — react-native-web components
4043
- // (and any component that accepts both prop names) depend on receiving them directly.
4044
- ...!isNative ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave, onPointerCancel: handlePointerCancel, onPressIn, onPressOut } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
4080
+ // and onPressIn/onPressOut are ALSO forwarded — but only when Component is an actual
4081
+ // component (react-native-web components accept both prop names), never when it's a
4082
+ // literal HTML tag string ('div', 'a', …). React DOM warns "Unknown event handler
4083
+ // property" for onPressIn/onPressOut on a host element even when the value is
4084
+ // undefined — the check is on the prop KEY being present at all, not its value — so
4085
+ // the key must be omitted entirely for string components, not just set to undefined.
4086
+ ...!isNative ? {
4087
+ onPointerDown: handlePointerDown,
4088
+ onPointerUp: handlePointerUp,
4089
+ onPointerLeave: handlePointerLeave,
4090
+ onPointerCancel: handlePointerCancel,
4091
+ ...typeof Component !== "string" ? { onPressIn, onPressOut } : {}
4092
+ } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
4045
4093
  onMouseEnter: handleMouseEnter,
4046
4094
  onMouseLeave: handleMouseLeave,
4047
4095
  onFocus: handleFocus,
package/dist/index.mjs CHANGED
@@ -36,7 +36,7 @@ import {
36
36
  useGlobalDarkMode,
37
37
  useGlobalWidth,
38
38
  useSyncExternalStore
39
- } from "./chunk-SVPKCLOI.mjs";
39
+ } from "./chunk-BAUO36PM.mjs";
40
40
 
41
41
  // src/context.tsx
42
42
  import { createContext, useContext } from "react";
@@ -330,8 +330,25 @@ function styled(Component, baseClasses = "") {
330
330
  // Only attach state-tracking handlers when interactive modifiers are present.
331
331
  // Always forward user-provided handlers to avoid silently swallowing them.
332
332
  // onPressIn/onPressOut are forwarded on web too (not just native) since
333
- // react-native-web components rely on receiving them directly.
334
- ...isWeb ? hasInteractive ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave, onPointerCancel: handlePointerCancel, onPressIn, onPressOut } : { onPointerDown, onPointerUp, onPointerLeave, onPointerCancel, onPressIn, onPressOut } : hasInteractive ? { onPressIn: handlePressIn, onPressOut: handlePressOut } : { onPressIn, onPressOut },
333
+ // react-native-web components rely on receiving them directly — but only
334
+ // when effectiveComponent is an actual component, never a literal HTML tag
335
+ // string ('div', 'a', …). React DOM warns "Unknown event handler property"
336
+ // for onPressIn/onPressOut on a host element even when the value is
337
+ // undefined — the check is on the prop KEY being present at all, not its
338
+ // value — so the key must be omitted entirely for string components.
339
+ ...isWeb ? hasInteractive ? {
340
+ onPointerDown: handlePointerDown,
341
+ onPointerUp: handlePointerUp,
342
+ onPointerLeave: handlePointerLeave,
343
+ onPointerCancel: handlePointerCancel,
344
+ ...typeof effectiveComponent !== "string" ? { onPressIn, onPressOut } : {}
345
+ } : {
346
+ onPointerDown,
347
+ onPointerUp,
348
+ onPointerLeave,
349
+ onPointerCancel,
350
+ ...typeof effectiveComponent !== "string" ? { onPressIn, onPressOut } : {}
351
+ } : hasInteractive ? { onPressIn: handlePressIn, onPressOut: handlePressOut } : { onPressIn, onPressOut },
335
352
  onMouseEnter: hasInteractive ? handleMouseEnter : onMouseEnter,
336
353
  onMouseLeave: hasInteractive ? handleMouseLeave : onMouseLeave,
337
354
  onFocus: hasInteractive ? handleFocus : onFocus,
@@ -728,11 +728,30 @@ function clearPluginModifiers() {
728
728
  for (const k of Object.keys(_pluginModifiers)) delete _pluginModifiers[k];
729
729
  _invalidate();
730
730
  }
731
+ var NAMED_GROUP_PEER_RE = /^(group|peer)-(hover|focus)\/(.+)$/;
732
+ var _namedModifierCache = /* @__PURE__ */ new Map();
733
+ function getNamedGroupPeerModifier(name) {
734
+ const cached = _namedModifierCache.get(name);
735
+ if (cached) return cached;
736
+ const m = NAMED_GROUP_PEER_RE.exec(name);
737
+ if (!m) return void 0;
738
+ const [, kind, trigger, groupName] = m;
739
+ const escapedName = escapeCSSSelector(groupName);
740
+ const pseudo = trigger === "hover" ? ":hover" : ":focus";
741
+ const combinator = kind === "group" ? " " : " ~ ";
742
+ const def = {
743
+ ancestorSelector: `.${kind}\\/${escapedName}${pseudo}${combinator}`,
744
+ jsBehavior: "css-only",
745
+ forcesImportant: true
746
+ };
747
+ _namedModifierCache.set(name, def);
748
+ return def;
749
+ }
731
750
  function getModifier(name) {
732
- return BUILTIN_MODIFIERS[name] ?? _pluginModifiers[name];
751
+ return BUILTIN_MODIFIERS[name] ?? _pluginModifiers[name] ?? getNamedGroupPeerModifier(name);
733
752
  }
734
753
  function isKnownModifier(name) {
735
- return name in BUILTIN_MODIFIERS || name in _pluginModifiers;
754
+ return name in BUILTIN_MODIFIERS || name in _pluginModifiers || NAMED_GROUP_PEER_RE.test(name);
736
755
  }
737
756
  function _allEntries() {
738
757
  return [...Object.entries(BUILTIN_MODIFIERS), ...Object.entries(_pluginModifiers)];
@@ -2409,10 +2428,12 @@ for (const [utility, prop] of Object.entries(PADDING_MARGIN_PROPS)) {
2409
2428
  for (const [utility, [widthProp, colorProp]] of Object.entries(BORDER_SIDE_PROPS)) {
2410
2429
  RESOLVERS[utility] = makeBorderSideResolver(widthProp, colorProp);
2411
2430
  }
2431
+ var NAMED_GROUP_PEER_MARKER_RE = /^(group|peer)\/.+$/;
2412
2432
  function resolveUtility(parsed, theme) {
2413
2433
  if (!parsed.value) {
2414
2434
  if (parsed.utility in PLUGIN_STANDALONE) return PLUGIN_STANDALONE[parsed.utility] ?? null;
2415
2435
  if (parsed.utility in getStandalone()) return getStandalone()[parsed.utility] ?? null;
2436
+ if (NAMED_GROUP_PEER_MARKER_RE.test(parsed.utility)) return getEffectiveIsWeb() ? {} : null;
2416
2437
  }
2417
2438
  const resolver = PLUGIN_RESOLVERS[parsed.utility] ?? RESOLVERS[parsed.utility];
2418
2439
  if (resolver) return resolver(parsed, theme);
@@ -2430,7 +2451,7 @@ function getPluginStandaloneMap() {
2430
2451
  return PLUGIN_STANDALONE;
2431
2452
  }
2432
2453
  function isKnownUtility(utility) {
2433
- return utility in getStandalone() || utility in RESOLVERS || utility in PLUGIN_STANDALONE || utility in PLUGIN_RESOLVERS;
2454
+ return utility in getStandalone() || utility in RESOLVERS || utility in PLUGIN_STANDALONE || utility in PLUGIN_RESOLVERS || NAMED_GROUP_PEER_MARKER_RE.test(utility);
2434
2455
  }
2435
2456
  var _sortedPrefixes = null;
2436
2457
  var _standaloneNames = null;
@@ -3328,9 +3349,19 @@ var InteractiveWrapper = (0, import_react2.forwardRef)(
3328
3349
  style: finalStyle,
3329
3350
  ...!isNative && className ? { className } : {},
3330
3351
  // On web, onPointerDown/Up drive the wrapper's own pressed state (covers mouse + touch),
3331
- // but onPressIn/onPressOut are still forwarded unchanged — react-native-web components
3332
- // (and any component that accepts both prop names) depend on receiving them directly.
3333
- ...!isNative ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave, onPointerCancel: handlePointerCancel, onPressIn, onPressOut } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
3352
+ // and onPressIn/onPressOut are ALSO forwarded — but only when Component is an actual
3353
+ // component (react-native-web components accept both prop names), never when it's a
3354
+ // literal HTML tag string ('div', 'a', …). React DOM warns "Unknown event handler
3355
+ // property" for onPressIn/onPressOut on a host element even when the value is
3356
+ // undefined — the check is on the prop KEY being present at all, not its value — so
3357
+ // the key must be omitted entirely for string components, not just set to undefined.
3358
+ ...!isNative ? {
3359
+ onPointerDown: handlePointerDown,
3360
+ onPointerUp: handlePointerUp,
3361
+ onPointerLeave: handlePointerLeave,
3362
+ onPointerCancel: handlePointerCancel,
3363
+ ...typeof Component !== "string" ? { onPressIn, onPressOut } : {}
3364
+ } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
3334
3365
  onMouseEnter: handleMouseEnter,
3335
3366
  onMouseLeave: handleMouseLeave,
3336
3367
  onFocus: handleFocus,
@@ -3,8 +3,8 @@ import {
3
3
  Fragment,
4
4
  jsx,
5
5
  jsxs
6
- } from "./chunk-EU7RK3K4.mjs";
7
- import "./chunk-SVPKCLOI.mjs";
6
+ } from "./chunk-FXDK5Z6V.mjs";
7
+ import "./chunk-BAUO36PM.mjs";
8
8
 
9
9
  // src/jsx-dev-runtime.tsx
10
10
  function jsxDEV(type, props, key, isStaticChildren, source, self) {
@@ -729,11 +729,30 @@ function clearPluginModifiers() {
729
729
  for (const k of Object.keys(_pluginModifiers)) delete _pluginModifiers[k];
730
730
  _invalidate();
731
731
  }
732
+ var NAMED_GROUP_PEER_RE = /^(group|peer)-(hover|focus)\/(.+)$/;
733
+ var _namedModifierCache = /* @__PURE__ */ new Map();
734
+ function getNamedGroupPeerModifier(name) {
735
+ const cached = _namedModifierCache.get(name);
736
+ if (cached) return cached;
737
+ const m = NAMED_GROUP_PEER_RE.exec(name);
738
+ if (!m) return void 0;
739
+ const [, kind, trigger, groupName] = m;
740
+ const escapedName = escapeCSSSelector(groupName);
741
+ const pseudo = trigger === "hover" ? ":hover" : ":focus";
742
+ const combinator = kind === "group" ? " " : " ~ ";
743
+ const def = {
744
+ ancestorSelector: `.${kind}\\/${escapedName}${pseudo}${combinator}`,
745
+ jsBehavior: "css-only",
746
+ forcesImportant: true
747
+ };
748
+ _namedModifierCache.set(name, def);
749
+ return def;
750
+ }
732
751
  function getModifier(name) {
733
- return BUILTIN_MODIFIERS[name] ?? _pluginModifiers[name];
752
+ return BUILTIN_MODIFIERS[name] ?? _pluginModifiers[name] ?? getNamedGroupPeerModifier(name);
734
753
  }
735
754
  function isKnownModifier(name) {
736
- return name in BUILTIN_MODIFIERS || name in _pluginModifiers;
755
+ return name in BUILTIN_MODIFIERS || name in _pluginModifiers || NAMED_GROUP_PEER_RE.test(name);
737
756
  }
738
757
  function _allEntries() {
739
758
  return [...Object.entries(BUILTIN_MODIFIERS), ...Object.entries(_pluginModifiers)];
@@ -2410,10 +2429,12 @@ for (const [utility, prop] of Object.entries(PADDING_MARGIN_PROPS)) {
2410
2429
  for (const [utility, [widthProp, colorProp]] of Object.entries(BORDER_SIDE_PROPS)) {
2411
2430
  RESOLVERS[utility] = makeBorderSideResolver(widthProp, colorProp);
2412
2431
  }
2432
+ var NAMED_GROUP_PEER_MARKER_RE = /^(group|peer)\/.+$/;
2413
2433
  function resolveUtility(parsed, theme) {
2414
2434
  if (!parsed.value) {
2415
2435
  if (parsed.utility in PLUGIN_STANDALONE) return PLUGIN_STANDALONE[parsed.utility] ?? null;
2416
2436
  if (parsed.utility in getStandalone()) return getStandalone()[parsed.utility] ?? null;
2437
+ if (NAMED_GROUP_PEER_MARKER_RE.test(parsed.utility)) return getEffectiveIsWeb() ? {} : null;
2417
2438
  }
2418
2439
  const resolver = PLUGIN_RESOLVERS[parsed.utility] ?? RESOLVERS[parsed.utility];
2419
2440
  if (resolver) return resolver(parsed, theme);
@@ -2431,7 +2452,7 @@ function getPluginStandaloneMap() {
2431
2452
  return PLUGIN_STANDALONE;
2432
2453
  }
2433
2454
  function isKnownUtility(utility) {
2434
- return utility in getStandalone() || utility in RESOLVERS || utility in PLUGIN_STANDALONE || utility in PLUGIN_RESOLVERS;
2455
+ return utility in getStandalone() || utility in RESOLVERS || utility in PLUGIN_STANDALONE || utility in PLUGIN_RESOLVERS || NAMED_GROUP_PEER_MARKER_RE.test(utility);
2435
2456
  }
2436
2457
  var _sortedPrefixes = null;
2437
2458
  var _standaloneNames = null;
@@ -3329,9 +3350,19 @@ var InteractiveWrapper = (0, import_react2.forwardRef)(
3329
3350
  style: finalStyle,
3330
3351
  ...!isNative && className ? { className } : {},
3331
3352
  // On web, onPointerDown/Up drive the wrapper's own pressed state (covers mouse + touch),
3332
- // but onPressIn/onPressOut are still forwarded unchanged — react-native-web components
3333
- // (and any component that accepts both prop names) depend on receiving them directly.
3334
- ...!isNative ? { onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerLeave: handlePointerLeave, onPointerCancel: handlePointerCancel, onPressIn, onPressOut } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
3353
+ // and onPressIn/onPressOut are ALSO forwarded — but only when Component is an actual
3354
+ // component (react-native-web components accept both prop names), never when it's a
3355
+ // literal HTML tag string ('div', 'a', …). React DOM warns "Unknown event handler
3356
+ // property" for onPressIn/onPressOut on a host element even when the value is
3357
+ // undefined — the check is on the prop KEY being present at all, not its value — so
3358
+ // the key must be omitted entirely for string components, not just set to undefined.
3359
+ ...!isNative ? {
3360
+ onPointerDown: handlePointerDown,
3361
+ onPointerUp: handlePointerUp,
3362
+ onPointerLeave: handlePointerLeave,
3363
+ onPointerCancel: handlePointerCancel,
3364
+ ...typeof Component !== "string" ? { onPressIn, onPressOut } : {}
3365
+ } : { onPressIn: handlePressIn, onPressOut: handlePressOut },
3335
3366
  onMouseEnter: handleMouseEnter,
3336
3367
  onMouseLeave: handleMouseLeave,
3337
3368
  onFocus: handleFocus,
@@ -4,10 +4,10 @@ import {
4
4
  _invalidateDefaultFontCache,
5
5
  jsx,
6
6
  jsxs
7
- } from "./chunk-EU7RK3K4.mjs";
7
+ } from "./chunk-FXDK5Z6V.mjs";
8
8
  import {
9
9
  registerWebElement
10
- } from "./chunk-SVPKCLOI.mjs";
10
+ } from "./chunk-BAUO36PM.mjs";
11
11
  export {
12
12
  Fragment,
13
13
  _invalidateDefaultFontCache,
@@ -2269,10 +2269,12 @@ for (const [utility, prop] of Object.entries(PADDING_MARGIN_PROPS)) {
2269
2269
  for (const [utility, [widthProp, colorProp]] of Object.entries(BORDER_SIDE_PROPS)) {
2270
2270
  RESOLVERS[utility] = makeBorderSideResolver(widthProp, colorProp);
2271
2271
  }
2272
+ var NAMED_GROUP_PEER_MARKER_RE = /^(group|peer)\/.+$/;
2272
2273
  function resolveUtility(parsed, theme) {
2273
2274
  if (!parsed.value) {
2274
2275
  if (parsed.utility in PLUGIN_STANDALONE) return PLUGIN_STANDALONE[parsed.utility] ?? null;
2275
2276
  if (parsed.utility in getStandalone()) return getStandalone()[parsed.utility] ?? null;
2277
+ if (NAMED_GROUP_PEER_MARKER_RE.test(parsed.utility)) return getEffectiveIsWeb() ? {} : null;
2276
2278
  }
2277
2279
  const resolver = PLUGIN_RESOLVERS[parsed.utility] ?? RESOLVERS[parsed.utility];
2278
2280
  if (resolver) return resolver(parsed, theme);
@@ -2390,11 +2392,30 @@ function clearPluginModifiers() {
2390
2392
  for (const k of Object.keys(_pluginModifiers)) delete _pluginModifiers[k];
2391
2393
  _invalidate();
2392
2394
  }
2395
+ var NAMED_GROUP_PEER_RE = /^(group|peer)-(hover|focus)\/(.+)$/;
2396
+ var _namedModifierCache = /* @__PURE__ */ new Map();
2397
+ function getNamedGroupPeerModifier(name) {
2398
+ const cached = _namedModifierCache.get(name);
2399
+ if (cached) return cached;
2400
+ const m = NAMED_GROUP_PEER_RE.exec(name);
2401
+ if (!m) return void 0;
2402
+ const [, kind, trigger, groupName] = m;
2403
+ const escapedName = escapeCSSSelector(groupName);
2404
+ const pseudo = trigger === "hover" ? ":hover" : ":focus";
2405
+ const combinator = kind === "group" ? " " : " ~ ";
2406
+ const def = {
2407
+ ancestorSelector: `.${kind}\\/${escapedName}${pseudo}${combinator}`,
2408
+ jsBehavior: "css-only",
2409
+ forcesImportant: true
2410
+ };
2411
+ _namedModifierCache.set(name, def);
2412
+ return def;
2413
+ }
2393
2414
  function getModifier(name) {
2394
- return BUILTIN_MODIFIERS[name] ?? _pluginModifiers[name];
2415
+ return BUILTIN_MODIFIERS[name] ?? _pluginModifiers[name] ?? getNamedGroupPeerModifier(name);
2395
2416
  }
2396
2417
  function isKnownModifier(name) {
2397
- return name in BUILTIN_MODIFIERS || name in _pluginModifiers;
2418
+ return name in BUILTIN_MODIFIERS || name in _pluginModifiers || NAMED_GROUP_PEER_RE.test(name);
2398
2419
  }
2399
2420
 
2400
2421
  // src/core/parser.ts
@@ -3203,6 +3224,38 @@ function scanDir(dir, onFile) {
3203
3224
  }
3204
3225
  }
3205
3226
  }
3227
+ var CSS_CLASS_SELECTOR_RE = /\.(-?[a-zA-Z_][a-zA-Z0-9_-]*)/g;
3228
+ var CSS_FILE_RE = /\.(css|scss|sass|less)$/;
3229
+ function scanCssFileInto(filePath, into) {
3230
+ try {
3231
+ const text = (0, import_fs.readFileSync)(filePath, "utf-8");
3232
+ let m;
3233
+ CSS_CLASS_SELECTOR_RE.lastIndex = 0;
3234
+ while ((m = CSS_CLASS_SELECTOR_RE.exec(text)) !== null) into.add(m[1]);
3235
+ } catch {
3236
+ }
3237
+ }
3238
+ function scanCssSelectorsInto(dir, into) {
3239
+ let entries;
3240
+ try {
3241
+ entries = (0, import_fs.readdirSync)(dir);
3242
+ } catch {
3243
+ return;
3244
+ }
3245
+ for (const entry of entries) {
3246
+ if (entry.startsWith(".") || entry === "node_modules") continue;
3247
+ const full = (0, import_path.join)(dir, entry);
3248
+ try {
3249
+ const st = (0, import_fs.statSync)(full);
3250
+ if (st.isDirectory()) scanCssSelectorsInto(full, into);
3251
+ else if (CSS_FILE_RE.test(entry)) scanCssFileInto(full, into);
3252
+ } catch {
3253
+ }
3254
+ }
3255
+ }
3256
+ function stripModifierPrefix(token) {
3257
+ return token.replace(/^(?:[a-zA-Z0-9_/-]+:)+/, "");
3258
+ }
3206
3259
  var DEFAULT_SCAN_DIRS = ["src", "app", "pages", "components", "views", "layouts"];
3207
3260
  function kbach(userConfigOrOptions) {
3208
3261
  const isOptions = userConfigOrOptions != null && ("framework" in userConfigOrOptions || "include" in userConfigOrOptions);
@@ -3217,6 +3270,19 @@ function kbach(userConfigOrOptions) {
3217
3270
  let mainCSSFile = null;
3218
3271
  const fileTokens = /* @__PURE__ */ new Map();
3219
3272
  const tokenCSS = /* @__PURE__ */ new Map();
3273
+ const projectCssClasses = /* @__PURE__ */ new Set();
3274
+ const warnedTokens = /* @__PURE__ */ new Set();
3275
+ function checkUnknownToken(tok) {
3276
+ if (process2.env.NODE_ENV === "production" || warnedTokens.has(tok) || tok.startsWith("__")) return;
3277
+ const parsed = parseClass(tok);
3278
+ if (parsed && resolveUtility(parsed, cfg.theme) !== null) return;
3279
+ const base = stripModifierPrefix(tok);
3280
+ if (!base || projectCssClasses.has(base)) return;
3281
+ warnedTokens.add(tok);
3282
+ console.warn(
3283
+ `[kbach] "${tok}" isn't a Kbach utility and no matching CSS rule was found anywhere in the project (checked .css/.scss/.sass/.less files) \u2014 possible typo? If this is intentional (a third-party class, one defined via CSS-in-JS, etc.), this warning is safe to ignore.`
3284
+ );
3285
+ }
3220
3286
  function processFile(filePath, code) {
3221
3287
  const key = normPath(filePath);
3222
3288
  const tokens = /* @__PURE__ */ new Set();
@@ -3225,9 +3291,13 @@ function kbach(userConfigOrOptions) {
3225
3291
  if (!tokenCSS.has(tok)) {
3226
3292
  tokenCSS.set(tok, generateClassCSS(tok, cfg.theme, cfg.darkMode, screens));
3227
3293
  }
3294
+ checkUnknownToken(tok);
3228
3295
  }
3229
3296
  fileTokens.set(key, tokens);
3230
3297
  }
3298
+ function scanProjectCssSelectors() {
3299
+ for (const dir of includeDirs) scanCssSelectorsInto((0, import_path.join)(root, dir), projectCssClasses);
3300
+ }
3231
3301
  function buildTokenCSSView() {
3232
3302
  const active = /* @__PURE__ */ new Set();
3233
3303
  for (const tokens of fileTokens.values()) for (const t of tokens) active.add(t);
@@ -3272,11 +3342,17 @@ ${code}`,
3272
3342
  },
3273
3343
  buildStart() {
3274
3344
  mainCSSFile = findKbachCSS(root);
3345
+ scanProjectCssSelectors();
3275
3346
  initialScan();
3276
3347
  if (mainCSSFile) writeKbachToFile(mainCSSFile, generateCSS());
3277
3348
  },
3278
3349
  handleHotUpdate({ file, server }) {
3279
- if (file.includes("node_modules") || !/\.(tsx?|jsx?)$/.test(file)) return;
3350
+ if (file.includes("node_modules")) return;
3351
+ if (CSS_FILE_RE.test(file)) {
3352
+ scanCssFileInto(file, projectCssClasses);
3353
+ return;
3354
+ }
3355
+ if (!/\.(tsx?|jsx?)$/.test(file)) return;
3280
3356
  try {
3281
3357
  processFile(file, (0, import_fs.readFileSync)(file, "utf-8"));
3282
3358
  } catch {
@@ -2235,10 +2235,12 @@ for (const [utility, prop] of Object.entries(PADDING_MARGIN_PROPS)) {
2235
2235
  for (const [utility, [widthProp, colorProp]] of Object.entries(BORDER_SIDE_PROPS)) {
2236
2236
  RESOLVERS[utility] = makeBorderSideResolver(widthProp, colorProp);
2237
2237
  }
2238
+ var NAMED_GROUP_PEER_MARKER_RE = /^(group|peer)\/.+$/;
2238
2239
  function resolveUtility(parsed, theme) {
2239
2240
  if (!parsed.value) {
2240
2241
  if (parsed.utility in PLUGIN_STANDALONE) return PLUGIN_STANDALONE[parsed.utility] ?? null;
2241
2242
  if (parsed.utility in getStandalone()) return getStandalone()[parsed.utility] ?? null;
2243
+ if (NAMED_GROUP_PEER_MARKER_RE.test(parsed.utility)) return getEffectiveIsWeb() ? {} : null;
2242
2244
  }
2243
2245
  const resolver = PLUGIN_RESOLVERS[parsed.utility] ?? RESOLVERS[parsed.utility];
2244
2246
  if (resolver) return resolver(parsed, theme);
@@ -2356,11 +2358,30 @@ function clearPluginModifiers() {
2356
2358
  for (const k of Object.keys(_pluginModifiers)) delete _pluginModifiers[k];
2357
2359
  _invalidate();
2358
2360
  }
2361
+ var NAMED_GROUP_PEER_RE = /^(group|peer)-(hover|focus)\/(.+)$/;
2362
+ var _namedModifierCache = /* @__PURE__ */ new Map();
2363
+ function getNamedGroupPeerModifier(name) {
2364
+ const cached = _namedModifierCache.get(name);
2365
+ if (cached) return cached;
2366
+ const m = NAMED_GROUP_PEER_RE.exec(name);
2367
+ if (!m) return void 0;
2368
+ const [, kind, trigger, groupName] = m;
2369
+ const escapedName = escapeCSSSelector(groupName);
2370
+ const pseudo = trigger === "hover" ? ":hover" : ":focus";
2371
+ const combinator = kind === "group" ? " " : " ~ ";
2372
+ const def = {
2373
+ ancestorSelector: `.${kind}\\/${escapedName}${pseudo}${combinator}`,
2374
+ jsBehavior: "css-only",
2375
+ forcesImportant: true
2376
+ };
2377
+ _namedModifierCache.set(name, def);
2378
+ return def;
2379
+ }
2359
2380
  function getModifier(name) {
2360
- return BUILTIN_MODIFIERS[name] ?? _pluginModifiers[name];
2381
+ return BUILTIN_MODIFIERS[name] ?? _pluginModifiers[name] ?? getNamedGroupPeerModifier(name);
2361
2382
  }
2362
2383
  function isKnownModifier(name) {
2363
- return name in BUILTIN_MODIFIERS || name in _pluginModifiers;
2384
+ return name in BUILTIN_MODIFIERS || name in _pluginModifiers || NAMED_GROUP_PEER_RE.test(name);
2364
2385
  }
2365
2386
 
2366
2387
  // src/core/parser.ts
@@ -3169,6 +3190,38 @@ function scanDir(dir, onFile) {
3169
3190
  }
3170
3191
  }
3171
3192
  }
3193
+ var CSS_CLASS_SELECTOR_RE = /\.(-?[a-zA-Z_][a-zA-Z0-9_-]*)/g;
3194
+ var CSS_FILE_RE = /\.(css|scss|sass|less)$/;
3195
+ function scanCssFileInto(filePath, into) {
3196
+ try {
3197
+ const text = readFileSync(filePath, "utf-8");
3198
+ let m;
3199
+ CSS_CLASS_SELECTOR_RE.lastIndex = 0;
3200
+ while ((m = CSS_CLASS_SELECTOR_RE.exec(text)) !== null) into.add(m[1]);
3201
+ } catch {
3202
+ }
3203
+ }
3204
+ function scanCssSelectorsInto(dir, into) {
3205
+ let entries;
3206
+ try {
3207
+ entries = readdirSync(dir);
3208
+ } catch {
3209
+ return;
3210
+ }
3211
+ for (const entry of entries) {
3212
+ if (entry.startsWith(".") || entry === "node_modules") continue;
3213
+ const full = join(dir, entry);
3214
+ try {
3215
+ const st = statSync(full);
3216
+ if (st.isDirectory()) scanCssSelectorsInto(full, into);
3217
+ else if (CSS_FILE_RE.test(entry)) scanCssFileInto(full, into);
3218
+ } catch {
3219
+ }
3220
+ }
3221
+ }
3222
+ function stripModifierPrefix(token) {
3223
+ return token.replace(/^(?:[a-zA-Z0-9_/-]+:)+/, "");
3224
+ }
3172
3225
  var DEFAULT_SCAN_DIRS = ["src", "app", "pages", "components", "views", "layouts"];
3173
3226
  function kbach(userConfigOrOptions) {
3174
3227
  const isOptions = userConfigOrOptions != null && ("framework" in userConfigOrOptions || "include" in userConfigOrOptions);
@@ -3183,6 +3236,19 @@ function kbach(userConfigOrOptions) {
3183
3236
  let mainCSSFile = null;
3184
3237
  const fileTokens = /* @__PURE__ */ new Map();
3185
3238
  const tokenCSS = /* @__PURE__ */ new Map();
3239
+ const projectCssClasses = /* @__PURE__ */ new Set();
3240
+ const warnedTokens = /* @__PURE__ */ new Set();
3241
+ function checkUnknownToken(tok) {
3242
+ if (process2.env.NODE_ENV === "production" || warnedTokens.has(tok) || tok.startsWith("__")) return;
3243
+ const parsed = parseClass(tok);
3244
+ if (parsed && resolveUtility(parsed, cfg.theme) !== null) return;
3245
+ const base = stripModifierPrefix(tok);
3246
+ if (!base || projectCssClasses.has(base)) return;
3247
+ warnedTokens.add(tok);
3248
+ console.warn(
3249
+ `[kbach] "${tok}" isn't a Kbach utility and no matching CSS rule was found anywhere in the project (checked .css/.scss/.sass/.less files) \u2014 possible typo? If this is intentional (a third-party class, one defined via CSS-in-JS, etc.), this warning is safe to ignore.`
3250
+ );
3251
+ }
3186
3252
  function processFile(filePath, code) {
3187
3253
  const key = normPath(filePath);
3188
3254
  const tokens = /* @__PURE__ */ new Set();
@@ -3191,9 +3257,13 @@ function kbach(userConfigOrOptions) {
3191
3257
  if (!tokenCSS.has(tok)) {
3192
3258
  tokenCSS.set(tok, generateClassCSS(tok, cfg.theme, cfg.darkMode, screens));
3193
3259
  }
3260
+ checkUnknownToken(tok);
3194
3261
  }
3195
3262
  fileTokens.set(key, tokens);
3196
3263
  }
3264
+ function scanProjectCssSelectors() {
3265
+ for (const dir of includeDirs) scanCssSelectorsInto(join(root, dir), projectCssClasses);
3266
+ }
3197
3267
  function buildTokenCSSView() {
3198
3268
  const active = /* @__PURE__ */ new Set();
3199
3269
  for (const tokens of fileTokens.values()) for (const t of tokens) active.add(t);
@@ -3238,11 +3308,17 @@ ${code}`,
3238
3308
  },
3239
3309
  buildStart() {
3240
3310
  mainCSSFile = findKbachCSS(root);
3311
+ scanProjectCssSelectors();
3241
3312
  initialScan();
3242
3313
  if (mainCSSFile) writeKbachToFile(mainCSSFile, generateCSS());
3243
3314
  },
3244
3315
  handleHotUpdate({ file, server }) {
3245
- if (file.includes("node_modules") || !/\.(tsx?|jsx?)$/.test(file)) return;
3316
+ if (file.includes("node_modules")) return;
3317
+ if (CSS_FILE_RE.test(file)) {
3318
+ scanCssFileInto(file, projectCssClasses);
3319
+ return;
3320
+ }
3321
+ if (!/\.(tsx?|jsx?)$/.test(file)) return;
3246
3322
  try {
3247
3323
  processFile(file, readFileSync(file, "utf-8"));
3248
3324
  } catch {
package/kbach-react.md CHANGED
@@ -67,6 +67,7 @@ The `kbach()` Vite plugin scans your source files and writes generated CSS betwe
67
67
  - Runs on `buildStart` (initial load) and on every HMR file change
68
68
  - Stateless: rescans all files fresh every time — removed classes are immediately evicted
69
69
  - Output is grouped by category with CSS custom properties for theme colors
70
+ - Also indexes every `.css`/`.scss`/`.sass`/`.less` file the same `include` dirs cover, and warns (`console.warn`, dev-server terminal, not the browser) for any class that's neither a real Kbach utility NOR defined anywhere in those stylesheets — a likely typo. Classes intentionally handled elsewhere (CSS Modules, styled-components, a third-party component's own class) are recognized once anything in the project literally defines `.that-class-name` and stay silent.
70
71
 
71
72
  ```ts
72
73
  // vite.config.ts
@@ -253,6 +254,18 @@ Mark a parent with `group`, then use `group-hover:` etc. on children.
253
254
  | `peer-hover:` | Previous sibling `.peer` is hovered |
254
255
  | `peer-focus:` | Previous sibling `.peer` is focused |
255
256
 
257
+ **Named groups/peers** — nested groups need names to avoid an inner element
258
+ reacting to the wrong (nearest) ancestor: `group/{name}` + `group-hover/{name}:`,
259
+ same for `peer/{name}` + `peer-hover/{name}:`/`peer-focus/{name}:`.
260
+ ```jsx
261
+ <div className="group/card">
262
+ <div className="group/icon">
263
+ <span className="group-hover/icon:opacity-100" />
264
+ </div>
265
+ <span className="group-hover/card:underline" />
266
+ </div>
267
+ ```
268
+
256
269
  ### Pseudo-element modifiers
257
270
  ```jsx
258
271
  <div className="before:content-['*'] before:text-red-6 relative" />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kbach/react",
3
- "version": "0.6.4",
3
+ "version": "0.6.5",
4
4
  "description": "React / React Native components and hooks for the Kbach framework",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./dist/index.js",