clases 1.1.8 → 1.1.11

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
@@ -4,19 +4,19 @@ A high-performance, recursive, and type-safe utility for managing CSS classes. D
4
4
 
5
5
  ## ✨ Features
6
6
 
7
- - 🔄 **Deep Recursion:** Nest variants like `md: { hover: '...' }` to stack prefixes automatically.
8
- - 🛡️ **Hard Typing:** Full IntelliSense autocomplete for all Tailwind variants and custom plugins.
9
- - 🔌 **Stackable Plugins:** Merge multiple design systems or custom configurations into a single `cl` function.
10
- - 🗜️ **Zero Overhead:** Built on top of `tailwind-merge` and `clsx` for optimal performance and conflict resolution.
11
- - 📦 **Monorepo Ready:** Lightweight, tree-shakable packages.
7
+ - 🔄 **Deep Recursion:** Nest variants like `md: { hover: '...' }` to stack prefixes automatically.
8
+ - 🛡️ **Hard Typing:** Full IntelliSense autocomplete for all Tailwind variants and custom plugins.
9
+ - 🔌 **Stackable Plugins:** Merge multiple design systems or custom configurations into a single `cl` function.
10
+ - 🗜️ **Zero Overhead:** Built on top of `tailwind-merge` and `clsx` for optimal performance and conflict resolution.
11
+ - 📦 **Monorepo Ready:** Lightweight, tree-shakable packages.
12
12
 
13
13
  ---
14
14
 
15
15
  ## 📦 Packages
16
16
 
17
- | Package | Description |
18
- | :--- | :--- |
19
- | **clases** | The recursive engine. Use this to build custom variants or plugins. |
17
+ | Package | Description |
18
+ | :------------------ | :------------------------------------------------------------------ |
19
+ | **clases** | The recursive engine. Use this to build custom variants or plugins. |
20
20
  | **clases-tailwind** | Pre-configured with all Tailwind CSS variants and type definitions. |
21
21
 
22
22
  ---
@@ -35,13 +35,13 @@ pnpm add clases-tailwind clases
35
35
  import { cl } from 'clases-tailwind';
36
36
 
37
37
  const className = cl({
38
- base: 'p-4 text-sm transition-all',
39
- hover: 'bg-blue-500 text-white',
40
- md: 'text-lg p-8',
41
- dark: {
42
- base: 'bg-gray-900',
43
- hover: 'bg-gray-800'
44
- }
38
+ base: 'p-4 text-sm transition-all',
39
+ hover: 'bg-blue-500 text-white',
40
+ md: 'text-lg p-8',
41
+ dark: {
42
+ base: 'bg-gray-900',
43
+ hover: 'bg-gray-800'
44
+ }
45
45
  });
46
46
  ```
47
47
 
@@ -50,144 +50,86 @@ const className = cl({
50
50
  ## 💡 Advanced Use Cases
51
51
 
52
52
  ### 🔄 Recursive Stacking (The "Secret Sauce")
53
+
53
54
  Stop repeating prefixes. Nesting objects automatically stacks variants in the correct order.
54
55
 
55
56
  ```typescript
56
57
  cl({
57
- md: {
58
- hover: {
59
- base: 'scale-105',
60
- after: 'content-["*"]'
58
+ md: {
59
+ hover: {
60
+ base: 'scale-105',
61
+ after: 'content-["*"]'
62
+ }
61
63
  }
62
- }
63
64
  });
64
65
  // Result: "md:hover:scale-105 md:hover:after:content-['*']"
65
66
  ```
66
- ---
67
-
68
- ## 🌈 Beautiful Syntax & Variants
69
-
70
- The most powerful feature of this utility is **Transparent Logical Nesting**. It allows you to organize your design system using nested objects that represent your business logic (variants, states, or themes) without polluting the final CSS output.
71
-
72
- #### How it Works
73
-
74
- The engine distinguishes between **Registered Prefixes** (modifiers like `md`, `hover`, or `ui`) and **Logical Keys** (your own organizational names like `variants`, `primary`, or `[state]`):
75
-
76
- * **Registered Keys**: Concatenate to form the final CSS prefix.
77
- * **Unregistered Keys**: Act as transparent wrappers. They are ignored in the final string but pass the current prefix down to their children.
78
-
79
- #### Component Variants Example
80
-
81
- This structure allows you to colocate base styles, responsive modifiers, and interaction states within a single logical branch:
82
-
83
- ```typescript
84
- const variant = 'primary';
85
- const theme = 'dark';
86
-
87
- const className = cl({
88
- md: {
89
- // 'variants' is NOT in the registry, so it is transparent
90
- variants: {
91
- // We select the active branch using standard JS
92
- [variant]: {
93
- base: 'rounded-lg px-4 py-2 transition',
94
- // 'dark' is a registered prefix, so it will be mapped
95
- dark: 'border-white text-white',
96
- hover: 'opacity-80'
97
- },
98
- secondary: 'bg-gray-200 text-black'
99
- }[variant]
100
- }
101
- });
102
-
103
- /**
104
- * Output (for variant 'primary'):
105
- * "md:rounded-lg md:px-4 md:py-2 md:transition md:dark:border-white md:dark:text-white md:hover:opacity-80"
106
- */
107
- ```
108
67
 
109
- #### Why this is superior:
110
-
111
- 1. **Clean DOM**: You won't see "ghost" prefixes like `variants:primary:bg-blue-500` in your HTML.
112
- 2. **Zero Boilerplate**: You don't have to repeat `md:dark:...` for every single class; the engine handles the chain automatically.
113
- 3. **Type-Safe Organization**: Use your own naming conventions to group styles while keeping the output perfectly compatible with Tailwind CSS.
114
-
115
- #### Best Practice: Selection Logic
116
-
117
- To keep the output optimized and prevent class collisions, handle the selection at the logical level so the engine only processes the "winning" branch:
118
-
119
- ```typescript
120
- cl({
121
- ui: {
122
- [status]: {
123
- success: 'text-green-600',
124
- error: 'text-red-600',
125
- pending: 'text-yellow-600'
126
- }[status]
127
- }
128
- });
129
- ```
130
68
  ---
131
69
 
132
70
  ### 🛠️ Custom Plugin Management
71
+
133
72
  You can stack the Tailwind plugin with your own semantic aliases or project-specific configs.
134
73
 
135
74
  ```typescript
136
75
  import { createCl } from 'clases';
137
76
  import { tailwind } from 'clases-tailwind';
138
77
 
139
- const cl = createCl(
140
- tailwind,
141
- {
78
+ const cl = createCl(tailwind, {
142
79
  hocus: 'hover:focus',
143
- brand: 'text-indigo-600 dark:text-indigo-400'
144
- }
145
- );
80
+ brand: 'text-indigo-600 dark:text-indigo-400'
81
+ });
146
82
 
147
- cl({
148
- hocus: 'outline-none ring-2',
149
- brand: 'font-bold'
83
+ cl({
84
+ hocus: 'outline-none ring-2',
85
+ brand: 'font-bold'
150
86
  });
151
87
  ```
152
88
 
153
89
  ### 📂 Clean Multi-line Layouts
90
+
154
91
  Use backticks and commas to organize large chunks of layout logic without losing readability.
155
92
 
156
93
  ```typescript
157
94
  cl({
158
- base: `
95
+ base: `
159
96
  grid grid-cols-1,
160
97
  gap-4 items-center,
161
98
  w-full max-w-7xl mx-auto
162
99
  `,
163
- lg: 'grid-cols-3 gap-8'
100
+ lg: 'grid-cols-3 gap-8'
164
101
  });
165
102
  ```
103
+
166
104
  ---
167
105
 
168
106
  ## ⌨️ Why Objects?
169
107
 
170
- | Feature | Standard Tailwind Strings | Class Utilities Objects |
171
- | :--- | :--- | :--- |
172
- | **Readability** | ❌ Hard to scan long lines | ✅ Grouped by variant |
173
- | **Maintenance** | ❌ Easy to forget prefixes | ✅ Automatic stacking |
174
- | **Logic** | ❌ Messy ternary operators | ✅ Native JS object logic |
175
- | **Types** | ❌ String-based (no safety) | ✅ Full Autocomplete |
108
+ | Feature | Standard Tailwind Strings | Class Utilities Objects |
109
+ | :-------------- | :-------------------------- | :------------------------ |
110
+ | **Readability** | ❌ Hard to scan long lines | ✅ Grouped by variant |
111
+ | **Maintenance** | ❌ Easy to forget prefixes | ✅ Automatic stacking |
112
+ | **Logic** | ❌ Messy ternary operators | ✅ Native JS object logic |
113
+ | **Types** | ❌ String-based (no safety) | ✅ Full Autocomplete |
176
114
 
177
115
  ---
178
116
 
179
117
  ## 🛠️ API Reference
180
118
 
181
119
  ### `cl(...inputs)`
120
+
182
121
  The main utility function. Accepts strings, arrays, objects, or nested structures.
183
122
 
184
123
  ### `createCl(...plugins)`
124
+
185
125
  Factory function to create a customized `cl` instance. Merges all provided objects into a single type-safe registry.
186
126
 
187
127
  ### `tailwind`
128
+
188
129
  The raw plugin data containing all Tailwind CSS variants.
189
130
 
190
131
  ---
191
132
 
192
133
  ## 📄 License
193
- MIT © Mauricio Frías
134
+
135
+ MIT © Mauricio Frías
package/dist/index.cjs CHANGED
@@ -10,34 +10,30 @@ var clsx__default = /*#__PURE__*/_interopDefault(clsx);
10
10
  // src/index.ts
11
11
  function createCl(...plugins) {
12
12
  const registry = Object.assign({}, ...plugins);
13
- const getPrefix = (key) => {
14
- return key.split(":").map((part) => registry[part]).filter(Boolean).join(":");
15
- };
16
- const process = (accumulatedKey, value) => {
17
- if (!value) return "";
18
- if (Array.isArray(value)) {
19
- return value.map((v) => process(accumulatedKey, v)).filter(Boolean).join(" ");
13
+ const process = (accumulatedPrefix, input) => {
14
+ if (!input) return "";
15
+ if (typeof input === "string") {
16
+ const resolved = accumulatedPrefix.split(":").map((part) => part === "base" ? null : registry[part] || null).filter(Boolean).join(":");
17
+ return input.split(/[,\s\n]+/).filter(Boolean).map((cls) => resolved ? `${resolved}:${cls}` : cls).join(" ");
20
18
  }
21
- if (typeof value === "object") {
22
- return Object.entries(value).map(([k, v]) => {
23
- if (!v) return "";
24
- if (registry[k] !== void 0) {
25
- const nextKey = accumulatedKey ? `${accumulatedKey}:${k}` : k;
26
- return process(nextKey, v);
27
- }
28
- return applyPrefix(accumulatedKey, k);
29
- }).join(" ");
19
+ if (Array.isArray(input)) {
20
+ return input.map((i) => process(accumulatedPrefix, i)).filter(Boolean).join(" ");
30
21
  }
31
- if (typeof value === "string") {
32
- return applyPrefix(accumulatedKey, value);
22
+ if (typeof input === "object") {
23
+ return Object.entries(input).map(([key, value]) => {
24
+ if (!value) return "";
25
+ const isBase = key === "base";
26
+ const isPrefix = registry[key] !== void 0;
27
+ if (isBase || isPrefix) {
28
+ const nextPrefix = accumulatedPrefix ? `${accumulatedPrefix}:${key}` : key;
29
+ return process(nextPrefix, value);
30
+ } else {
31
+ return process(accumulatedPrefix, key);
32
+ }
33
+ }).filter(Boolean).join(" ");
33
34
  }
34
35
  return "";
35
36
  };
36
- const applyPrefix = (key, classString) => {
37
- const resolved = getPrefix(key);
38
- if (!resolved) return classString;
39
- return classString.split(/[,\s\n]+/).filter(Boolean).map((cls) => `${resolved}:${cls}`).join(" ");
40
- };
41
37
  return (...inputs) => {
42
38
  const processed = inputs.map((input) => process("", input));
43
39
  return tailwindMerge.twMerge(clsx__default.default(processed));
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["twMerge","clsx"],"mappings":";;;;;;;;;;AAGO,SAAS,YAAuD,OAAA,EAAmB;AACtF,EAAA,MAAM,WAAmC,MAAA,CAAO,MAAA,CAAO,EAAC,EAAG,GAAG,OAAO,CAAA;AAGrE,EAAA,MAAM,SAAA,GAAY,CAAC,GAAA,KAAgB;AAC/B,IAAA,OAAO,GAAA,CACF,KAAA,CAAM,GAAG,CAAA,CACT,IAAI,CAAC,IAAA,KAAS,QAAA,CAAS,IAAI,CAAC,CAAA,CAC5B,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAAA,EACjB,CAAA;AAEA,EAAA,MAAM,OAAA,GAAU,CAAC,cAAA,EAAwB,KAAA,KAAuB;AAC5D,IAAA,IAAI,CAAC,OAAO,OAAO,EAAA;AAGnB,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACtB,MAAA,OAAO,KAAA,CACF,GAAA,CAAI,CAAC,CAAA,KAAM,OAAA,CAAQ,cAAA,EAAgB,CAAC,CAAC,CAAA,CACrC,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAAA,IACjB;AAGA,IAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC3B,MAAA,OAAO,MAAA,CAAO,QAAQ,KAAK,CAAA,CACtB,IAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,KAAM;AACb,QAAA,IAAI,CAAC,GAAG,OAAO,EAAA;AAGf,QAAA,IAAI,QAAA,CAAS,CAAC,CAAA,KAAM,MAAA,EAAW;AAC3B,UAAA,MAAM,UAAU,cAAA,GAAiB,CAAA,EAAG,cAAc,CAAA,CAAA,EAAI,CAAC,CAAA,CAAA,GAAK,CAAA;AAC5D,UAAA,OAAO,OAAA,CAAQ,SAAS,CAAC,CAAA;AAAA,QAC7B;AAIA,QAAA,OAAO,WAAA,CAAY,gBAAgB,CAAC,CAAA;AAAA,MACxC,CAAC,CAAA,CACA,IAAA,CAAK,GAAG,CAAA;AAAA,IACjB;AAGA,IAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC3B,MAAA,OAAO,WAAA,CAAY,gBAAgB,KAAK,CAAA;AAAA,IAC5C;AAEA,IAAA,OAAO,EAAA;AAAA,EACX,CAAA;AAGA,EAAA,MAAM,WAAA,GAAc,CAAC,GAAA,EAAa,WAAA,KAAgC;AAC9D,IAAA,MAAM,QAAA,GAAW,UAAU,GAAG,CAAA;AAC9B,IAAA,IAAI,CAAC,UAAU,OAAO,WAAA;AAEtB,IAAA,OAAO,YACF,KAAA,CAAM,UAAU,CAAA,CAChB,MAAA,CAAO,OAAO,CAAA,CACd,GAAA,CAAI,CAAC,GAAA,KAAQ,GAAG,QAAQ,CAAA,CAAA,EAAI,GAAG,CAAA,CAAE,CAAA,CACjC,KAAK,GAAG,CAAA;AAAA,EACjB,CAAA;AAEA,EAAA,OAAO,IAAI,MAAA,KAAyB;AAChC,IAAA,MAAM,SAAA,GAAY,OAAO,GAAA,CAAI,CAAC,UAAU,OAAA,CAAQ,EAAA,EAAI,KAAK,CAAC,CAAA;AAC1D,IAAA,OAAOA,qBAAA,CAAQC,qBAAA,CAAK,SAAS,CAAC,CAAA;AAAA,EAClC,CAAA;AACJ","file":"index.cjs","sourcesContent":["import { twMerge } from 'tailwind-merge';\r\nimport clsx, { type ClassValue } from 'clsx';\r\n\r\nexport function createCl<TPlugins extends Record<string, string>[]>(...plugins: TPlugins) {\r\n const registry: Record<string, string> = Object.assign({}, ...plugins);\r\n\r\n // Función auxiliar para obtener el prefijo real de Tailwind\r\n const getPrefix = (key: string) => {\r\n return key\r\n .split(':')\r\n .map((part) => registry[part])\r\n .filter(Boolean)\r\n .join(':');\r\n };\r\n\r\n const process = (accumulatedKey: string, value: any): string => {\r\n if (!value) return '';\r\n\r\n // 1. Arrays: Multilínea\r\n if (Array.isArray(value)) {\r\n return value\r\n .map((v) => process(accumulatedKey, v))\r\n .filter(Boolean)\r\n .join(' ');\r\n }\r\n\r\n // 2. Objetos\r\n if (typeof value === 'object') {\r\n return Object.entries(value)\r\n .map(([k, v]) => {\r\n if (!v) return '';\r\n\r\n // Si la llave es un prefijo (md, hover), profundizamos\r\n if (registry[k] !== undefined) {\r\n const nextKey = accumulatedKey ? `${accumulatedKey}:${k}` : k;\r\n return process(nextKey, v);\r\n }\r\n\r\n // Si NO es prefijo, tratamos la llave 'k' como la clase final\r\n // Pero le aplicamos el prefijo acumulado hasta ahora\r\n return applyPrefix(accumulatedKey, k);\r\n })\r\n .join(' ');\r\n }\r\n\r\n // 3. Strings directos: Aplicamos el prefijo acumulado a cada palabra\r\n if (typeof value === 'string') {\r\n return applyPrefix(accumulatedKey, value);\r\n }\r\n\r\n return '';\r\n };\r\n\r\n // Función para aplicar el prefijo resuelto a un string de clases\r\n const applyPrefix = (key: string, classString: string): string => {\r\n const resolved = getPrefix(key);\r\n if (!resolved) return classString;\r\n\r\n return classString\r\n .split(/[,\\s\\n]+/)\r\n .filter(Boolean)\r\n .map((cls) => `${resolved}:${cls}`)\r\n .join(' ');\r\n };\r\n\r\n return (...inputs: ClassValue[]) => {\r\n const processed = inputs.map((input) => process('', input));\r\n return twMerge(clsx(processed));\r\n };\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["twMerge","clsx"],"mappings":";;;;;;;;;;AAUO,SAAS,YAAuD,OAAA,EAAmB;AACtF,EAAA,MAAM,WAAmC,MAAA,CAAO,MAAA,CAAO,EAAC,EAAG,GAAG,OAAO,CAAA;AAQrE,EAAA,MAAM,OAAA,GAAU,CAAC,iBAAA,EAA2B,KAAA,KAAuB;AAC/D,IAAA,IAAI,CAAC,OAAO,OAAO,EAAA;AAGnB,IAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC3B,MAAA,MAAM,QAAA,GAAW,kBACZ,KAAA,CAAM,GAAG,EACT,GAAA,CAAI,CAAC,SAAU,IAAA,KAAS,MAAA,GAAS,OAAO,QAAA,CAAS,IAAI,KAAK,IAAK,CAAA,CAC/D,OAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAEb,MAAA,OAAO,MACF,KAAA,CAAM,UAAU,EAChB,MAAA,CAAO,OAAO,EACd,GAAA,CAAI,CAAC,QAAS,QAAA,GAAW,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,GAAG,KAAK,GAAI,CAAA,CACpD,KAAK,GAAG,CAAA;AAAA,IACjB;AAGA,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACtB,MAAA,OAAO,KAAA,CACF,GAAA,CAAI,CAAC,CAAA,KAAM,OAAA,CAAQ,iBAAA,EAAmB,CAAC,CAAC,CAAA,CACxC,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAAA,IACjB;AAGA,IAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC3B,MAAA,OAAO,MAAA,CAAO,QAAQ,KAAK,CAAA,CACtB,IAAI,CAAC,CAAC,GAAA,EAAK,KAAK,CAAA,KAAM;AACnB,QAAA,IAAI,CAAC,OAAO,OAAO,EAAA;AAEnB,QAAA,MAAM,SAAS,GAAA,KAAQ,MAAA;AACvB,QAAA,MAAM,QAAA,GAAW,QAAA,CAAS,GAAG,CAAA,KAAM,MAAA;AAEnC,QAAA,IAAI,UAAU,QAAA,EAAU;AAEpB,UAAA,MAAM,aAAa,iBAAA,GAAoB,CAAA,EAAG,iBAAiB,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA,GAAK,GAAA;AACvE,UAAA,OAAO,OAAA,CAAQ,YAAY,KAAK,CAAA;AAAA,QACpC,CAAA,MAAO;AAEH,UAAA,OAAO,OAAA,CAAQ,mBAAmB,GAAG,CAAA;AAAA,QACzC;AAAA,MACJ,CAAC,CAAA,CACA,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAAA,IACjB;AAEA,IAAA,OAAO,EAAA;AAAA,EACX,CAAA;AAUA,EAAA,OAAO,IAAI,MAAA,KAAyB;AAChC,IAAA,MAAM,SAAA,GAAY,OAAO,GAAA,CAAI,CAAC,UAAU,OAAA,CAAQ,EAAA,EAAI,KAAK,CAAC,CAAA;AAC1D,IAAA,OAAOA,qBAAA,CAAQC,qBAAA,CAAK,SAAS,CAAC,CAAA;AAAA,EAClC,CAAA;AACJ","file":"index.cjs","sourcesContent":["import { twMerge } from 'tailwind-merge';\r\nimport clsx, { type ClassValue } from 'clsx';\r\n\r\n/**\r\n * Creates a customized Tailwind class engine instance with prefix registry support.\r\n * * @param plugins - Objects mapping custom aliases (e.g., 'ui') to real Tailwind prefixes (e.g., 'prefix').\r\n * @returns A recursive 'cl' function that processes strings, arrays, and objects.\r\n * * @example\r\n * const tw = createCl({ md: 'md', ui: 'prefix' });\r\n */\r\nexport function createCl<TPlugins extends Record<string, string>[]>(...plugins: TPlugins) {\r\n const registry: Record<string, string> = Object.assign({}, ...plugins);\r\n\r\n /**\r\n * Recursively processes input values to apply prefixes and logic.\r\n * * @param accumulatedPrefix - The prefix path built during recursion (e.g., 'md:hover').\r\n * @param input - The value to process (string, array, or object).\r\n * @returns A string of prefixed and filtered Tailwind classes.\r\n */\r\n const process = (accumulatedPrefix: string, input: any): string => {\r\n if (!input) return '';\r\n\r\n // 1. Strings: Resolve real Tailwind prefixes and apply them\r\n if (typeof input === 'string') {\r\n const resolved = accumulatedPrefix\r\n .split(':')\r\n .map((part) => (part === 'base' ? null : registry[part] || null))\r\n .filter(Boolean)\r\n .join(':');\r\n\r\n return input\r\n .split(/[,\\s\\n]+/) // Split by commas, spaces, or newlines\r\n .filter(Boolean)\r\n .map((cls) => (resolved ? `${resolved}:${cls}` : cls))\r\n .join(' ');\r\n }\r\n\r\n // 2. Arrays: Multi-line support and recursive processing\r\n if (Array.isArray(input)) {\r\n return input\r\n .map((i) => process(accumulatedPrefix, i))\r\n .filter(Boolean)\r\n .join(' ');\r\n }\r\n\r\n // 3. Objects: Prefix navigation and Conditional Logic (clsx-style)\r\n if (typeof input === 'object') {\r\n return Object.entries(input)\r\n .map(([key, value]) => {\r\n if (!value) return '';\r\n\r\n const isBase = key === 'base';\r\n const isPrefix = registry[key] !== undefined;\r\n\r\n if (isBase || isPrefix) {\r\n // It's an organization node or a prefix: accumulate and dive deeper\r\n const nextPrefix = accumulatedPrefix ? `${accumulatedPrefix}:${key}` : key;\r\n return process(nextPrefix, value);\r\n } else {\r\n // Standard logic { 'class-name': boolean }: treat the key as the class content\r\n return process(accumulatedPrefix, key);\r\n }\r\n })\r\n .filter(Boolean)\r\n .join(' ');\r\n }\r\n\r\n return '';\r\n };\r\n\r\n /**\r\n * Main utility for generating Tailwind classes.\r\n * Supports strings, nested objects with prefixes, and arrays.\r\n * * @param inputs - A list of arguments following the clsx pattern.\r\n * @returns A processed, deduplicated string of classes via twMerge.\r\n * * @example\r\n * tw('btn-base', { md: 'p-4', hover: { 'opacity-50': isDim } });\r\n */\r\n return (...inputs: ClassValue[]) => {\r\n const processed = inputs.map((input) => process('', input));\r\n return twMerge(clsx(processed));\r\n };\r\n}\r\n"]}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,12 @@
1
1
  import { ClassValue } from 'clsx';
2
2
 
3
+ /**
4
+ * Creates a customized Tailwind class engine instance with prefix registry support.
5
+ * * @param plugins - Objects mapping custom aliases (e.g., 'ui') to real Tailwind prefixes (e.g., 'prefix').
6
+ * @returns A recursive 'cl' function that processes strings, arrays, and objects.
7
+ * * @example
8
+ * const tw = createCl({ md: 'md', ui: 'prefix' });
9
+ */
3
10
  declare function createCl<TPlugins extends Record<string, string>[]>(...plugins: TPlugins): (...inputs: ClassValue[]) => string;
4
11
 
5
12
  export { createCl };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,12 @@
1
1
  import { ClassValue } from 'clsx';
2
2
 
3
+ /**
4
+ * Creates a customized Tailwind class engine instance with prefix registry support.
5
+ * * @param plugins - Objects mapping custom aliases (e.g., 'ui') to real Tailwind prefixes (e.g., 'prefix').
6
+ * @returns A recursive 'cl' function that processes strings, arrays, and objects.
7
+ * * @example
8
+ * const tw = createCl({ md: 'md', ui: 'prefix' });
9
+ */
3
10
  declare function createCl<TPlugins extends Record<string, string>[]>(...plugins: TPlugins): (...inputs: ClassValue[]) => string;
4
11
 
5
12
  export { createCl };
package/dist/index.js CHANGED
@@ -4,34 +4,30 @@ import clsx from 'clsx';
4
4
  // src/index.ts
5
5
  function createCl(...plugins) {
6
6
  const registry = Object.assign({}, ...plugins);
7
- const getPrefix = (key) => {
8
- return key.split(":").map((part) => registry[part]).filter(Boolean).join(":");
9
- };
10
- const process = (accumulatedKey, value) => {
11
- if (!value) return "";
12
- if (Array.isArray(value)) {
13
- return value.map((v) => process(accumulatedKey, v)).filter(Boolean).join(" ");
7
+ const process = (accumulatedPrefix, input) => {
8
+ if (!input) return "";
9
+ if (typeof input === "string") {
10
+ const resolved = accumulatedPrefix.split(":").map((part) => part === "base" ? null : registry[part] || null).filter(Boolean).join(":");
11
+ return input.split(/[,\s\n]+/).filter(Boolean).map((cls) => resolved ? `${resolved}:${cls}` : cls).join(" ");
14
12
  }
15
- if (typeof value === "object") {
16
- return Object.entries(value).map(([k, v]) => {
17
- if (!v) return "";
18
- if (registry[k] !== void 0) {
19
- const nextKey = accumulatedKey ? `${accumulatedKey}:${k}` : k;
20
- return process(nextKey, v);
21
- }
22
- return applyPrefix(accumulatedKey, k);
23
- }).join(" ");
13
+ if (Array.isArray(input)) {
14
+ return input.map((i) => process(accumulatedPrefix, i)).filter(Boolean).join(" ");
24
15
  }
25
- if (typeof value === "string") {
26
- return applyPrefix(accumulatedKey, value);
16
+ if (typeof input === "object") {
17
+ return Object.entries(input).map(([key, value]) => {
18
+ if (!value) return "";
19
+ const isBase = key === "base";
20
+ const isPrefix = registry[key] !== void 0;
21
+ if (isBase || isPrefix) {
22
+ const nextPrefix = accumulatedPrefix ? `${accumulatedPrefix}:${key}` : key;
23
+ return process(nextPrefix, value);
24
+ } else {
25
+ return process(accumulatedPrefix, key);
26
+ }
27
+ }).filter(Boolean).join(" ");
27
28
  }
28
29
  return "";
29
30
  };
30
- const applyPrefix = (key, classString) => {
31
- const resolved = getPrefix(key);
32
- if (!resolved) return classString;
33
- return classString.split(/[,\s\n]+/).filter(Boolean).map((cls) => `${resolved}:${cls}`).join(" ");
34
- };
35
31
  return (...inputs) => {
36
32
  const processed = inputs.map((input) => process("", input));
37
33
  return twMerge(clsx(processed));
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;AAGO,SAAS,YAAuD,OAAA,EAAmB;AACtF,EAAA,MAAM,WAAmC,MAAA,CAAO,MAAA,CAAO,EAAC,EAAG,GAAG,OAAO,CAAA;AAGrE,EAAA,MAAM,SAAA,GAAY,CAAC,GAAA,KAAgB;AAC/B,IAAA,OAAO,GAAA,CACF,KAAA,CAAM,GAAG,CAAA,CACT,IAAI,CAAC,IAAA,KAAS,QAAA,CAAS,IAAI,CAAC,CAAA,CAC5B,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAAA,EACjB,CAAA;AAEA,EAAA,MAAM,OAAA,GAAU,CAAC,cAAA,EAAwB,KAAA,KAAuB;AAC5D,IAAA,IAAI,CAAC,OAAO,OAAO,EAAA;AAGnB,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACtB,MAAA,OAAO,KAAA,CACF,GAAA,CAAI,CAAC,CAAA,KAAM,OAAA,CAAQ,cAAA,EAAgB,CAAC,CAAC,CAAA,CACrC,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAAA,IACjB;AAGA,IAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC3B,MAAA,OAAO,MAAA,CAAO,QAAQ,KAAK,CAAA,CACtB,IAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,KAAM;AACb,QAAA,IAAI,CAAC,GAAG,OAAO,EAAA;AAGf,QAAA,IAAI,QAAA,CAAS,CAAC,CAAA,KAAM,MAAA,EAAW;AAC3B,UAAA,MAAM,UAAU,cAAA,GAAiB,CAAA,EAAG,cAAc,CAAA,CAAA,EAAI,CAAC,CAAA,CAAA,GAAK,CAAA;AAC5D,UAAA,OAAO,OAAA,CAAQ,SAAS,CAAC,CAAA;AAAA,QAC7B;AAIA,QAAA,OAAO,WAAA,CAAY,gBAAgB,CAAC,CAAA;AAAA,MACxC,CAAC,CAAA,CACA,IAAA,CAAK,GAAG,CAAA;AAAA,IACjB;AAGA,IAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC3B,MAAA,OAAO,WAAA,CAAY,gBAAgB,KAAK,CAAA;AAAA,IAC5C;AAEA,IAAA,OAAO,EAAA;AAAA,EACX,CAAA;AAGA,EAAA,MAAM,WAAA,GAAc,CAAC,GAAA,EAAa,WAAA,KAAgC;AAC9D,IAAA,MAAM,QAAA,GAAW,UAAU,GAAG,CAAA;AAC9B,IAAA,IAAI,CAAC,UAAU,OAAO,WAAA;AAEtB,IAAA,OAAO,YACF,KAAA,CAAM,UAAU,CAAA,CAChB,MAAA,CAAO,OAAO,CAAA,CACd,GAAA,CAAI,CAAC,GAAA,KAAQ,GAAG,QAAQ,CAAA,CAAA,EAAI,GAAG,CAAA,CAAE,CAAA,CACjC,KAAK,GAAG,CAAA;AAAA,EACjB,CAAA;AAEA,EAAA,OAAO,IAAI,MAAA,KAAyB;AAChC,IAAA,MAAM,SAAA,GAAY,OAAO,GAAA,CAAI,CAAC,UAAU,OAAA,CAAQ,EAAA,EAAI,KAAK,CAAC,CAAA;AAC1D,IAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,SAAS,CAAC,CAAA;AAAA,EAClC,CAAA;AACJ","file":"index.js","sourcesContent":["import { twMerge } from 'tailwind-merge';\r\nimport clsx, { type ClassValue } from 'clsx';\r\n\r\nexport function createCl<TPlugins extends Record<string, string>[]>(...plugins: TPlugins) {\r\n const registry: Record<string, string> = Object.assign({}, ...plugins);\r\n\r\n // Función auxiliar para obtener el prefijo real de Tailwind\r\n const getPrefix = (key: string) => {\r\n return key\r\n .split(':')\r\n .map((part) => registry[part])\r\n .filter(Boolean)\r\n .join(':');\r\n };\r\n\r\n const process = (accumulatedKey: string, value: any): string => {\r\n if (!value) return '';\r\n\r\n // 1. Arrays: Multilínea\r\n if (Array.isArray(value)) {\r\n return value\r\n .map((v) => process(accumulatedKey, v))\r\n .filter(Boolean)\r\n .join(' ');\r\n }\r\n\r\n // 2. Objetos\r\n if (typeof value === 'object') {\r\n return Object.entries(value)\r\n .map(([k, v]) => {\r\n if (!v) return '';\r\n\r\n // Si la llave es un prefijo (md, hover), profundizamos\r\n if (registry[k] !== undefined) {\r\n const nextKey = accumulatedKey ? `${accumulatedKey}:${k}` : k;\r\n return process(nextKey, v);\r\n }\r\n\r\n // Si NO es prefijo, tratamos la llave 'k' como la clase final\r\n // Pero le aplicamos el prefijo acumulado hasta ahora\r\n return applyPrefix(accumulatedKey, k);\r\n })\r\n .join(' ');\r\n }\r\n\r\n // 3. Strings directos: Aplicamos el prefijo acumulado a cada palabra\r\n if (typeof value === 'string') {\r\n return applyPrefix(accumulatedKey, value);\r\n }\r\n\r\n return '';\r\n };\r\n\r\n // Función para aplicar el prefijo resuelto a un string de clases\r\n const applyPrefix = (key: string, classString: string): string => {\r\n const resolved = getPrefix(key);\r\n if (!resolved) return classString;\r\n\r\n return classString\r\n .split(/[,\\s\\n]+/)\r\n .filter(Boolean)\r\n .map((cls) => `${resolved}:${cls}`)\r\n .join(' ');\r\n };\r\n\r\n return (...inputs: ClassValue[]) => {\r\n const processed = inputs.map((input) => process('', input));\r\n return twMerge(clsx(processed));\r\n };\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;AAUO,SAAS,YAAuD,OAAA,EAAmB;AACtF,EAAA,MAAM,WAAmC,MAAA,CAAO,MAAA,CAAO,EAAC,EAAG,GAAG,OAAO,CAAA;AAQrE,EAAA,MAAM,OAAA,GAAU,CAAC,iBAAA,EAA2B,KAAA,KAAuB;AAC/D,IAAA,IAAI,CAAC,OAAO,OAAO,EAAA;AAGnB,IAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC3B,MAAA,MAAM,QAAA,GAAW,kBACZ,KAAA,CAAM,GAAG,EACT,GAAA,CAAI,CAAC,SAAU,IAAA,KAAS,MAAA,GAAS,OAAO,QAAA,CAAS,IAAI,KAAK,IAAK,CAAA,CAC/D,OAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAEb,MAAA,OAAO,MACF,KAAA,CAAM,UAAU,EAChB,MAAA,CAAO,OAAO,EACd,GAAA,CAAI,CAAC,QAAS,QAAA,GAAW,CAAA,EAAG,QAAQ,CAAA,CAAA,EAAI,GAAG,KAAK,GAAI,CAAA,CACpD,KAAK,GAAG,CAAA;AAAA,IACjB;AAGA,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACtB,MAAA,OAAO,KAAA,CACF,GAAA,CAAI,CAAC,CAAA,KAAM,OAAA,CAAQ,iBAAA,EAAmB,CAAC,CAAC,CAAA,CACxC,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAAA,IACjB;AAGA,IAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC3B,MAAA,OAAO,MAAA,CAAO,QAAQ,KAAK,CAAA,CACtB,IAAI,CAAC,CAAC,GAAA,EAAK,KAAK,CAAA,KAAM;AACnB,QAAA,IAAI,CAAC,OAAO,OAAO,EAAA;AAEnB,QAAA,MAAM,SAAS,GAAA,KAAQ,MAAA;AACvB,QAAA,MAAM,QAAA,GAAW,QAAA,CAAS,GAAG,CAAA,KAAM,MAAA;AAEnC,QAAA,IAAI,UAAU,QAAA,EAAU;AAEpB,UAAA,MAAM,aAAa,iBAAA,GAAoB,CAAA,EAAG,iBAAiB,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA,GAAK,GAAA;AACvE,UAAA,OAAO,OAAA,CAAQ,YAAY,KAAK,CAAA;AAAA,QACpC,CAAA,MAAO;AAEH,UAAA,OAAO,OAAA,CAAQ,mBAAmB,GAAG,CAAA;AAAA,QACzC;AAAA,MACJ,CAAC,CAAA,CACA,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAAA,IACjB;AAEA,IAAA,OAAO,EAAA;AAAA,EACX,CAAA;AAUA,EAAA,OAAO,IAAI,MAAA,KAAyB;AAChC,IAAA,MAAM,SAAA,GAAY,OAAO,GAAA,CAAI,CAAC,UAAU,OAAA,CAAQ,EAAA,EAAI,KAAK,CAAC,CAAA;AAC1D,IAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,SAAS,CAAC,CAAA;AAAA,EAClC,CAAA;AACJ","file":"index.js","sourcesContent":["import { twMerge } from 'tailwind-merge';\r\nimport clsx, { type ClassValue } from 'clsx';\r\n\r\n/**\r\n * Creates a customized Tailwind class engine instance with prefix registry support.\r\n * * @param plugins - Objects mapping custom aliases (e.g., 'ui') to real Tailwind prefixes (e.g., 'prefix').\r\n * @returns A recursive 'cl' function that processes strings, arrays, and objects.\r\n * * @example\r\n * const tw = createCl({ md: 'md', ui: 'prefix' });\r\n */\r\nexport function createCl<TPlugins extends Record<string, string>[]>(...plugins: TPlugins) {\r\n const registry: Record<string, string> = Object.assign({}, ...plugins);\r\n\r\n /**\r\n * Recursively processes input values to apply prefixes and logic.\r\n * * @param accumulatedPrefix - The prefix path built during recursion (e.g., 'md:hover').\r\n * @param input - The value to process (string, array, or object).\r\n * @returns A string of prefixed and filtered Tailwind classes.\r\n */\r\n const process = (accumulatedPrefix: string, input: any): string => {\r\n if (!input) return '';\r\n\r\n // 1. Strings: Resolve real Tailwind prefixes and apply them\r\n if (typeof input === 'string') {\r\n const resolved = accumulatedPrefix\r\n .split(':')\r\n .map((part) => (part === 'base' ? null : registry[part] || null))\r\n .filter(Boolean)\r\n .join(':');\r\n\r\n return input\r\n .split(/[,\\s\\n]+/) // Split by commas, spaces, or newlines\r\n .filter(Boolean)\r\n .map((cls) => (resolved ? `${resolved}:${cls}` : cls))\r\n .join(' ');\r\n }\r\n\r\n // 2. Arrays: Multi-line support and recursive processing\r\n if (Array.isArray(input)) {\r\n return input\r\n .map((i) => process(accumulatedPrefix, i))\r\n .filter(Boolean)\r\n .join(' ');\r\n }\r\n\r\n // 3. Objects: Prefix navigation and Conditional Logic (clsx-style)\r\n if (typeof input === 'object') {\r\n return Object.entries(input)\r\n .map(([key, value]) => {\r\n if (!value) return '';\r\n\r\n const isBase = key === 'base';\r\n const isPrefix = registry[key] !== undefined;\r\n\r\n if (isBase || isPrefix) {\r\n // It's an organization node or a prefix: accumulate and dive deeper\r\n const nextPrefix = accumulatedPrefix ? `${accumulatedPrefix}:${key}` : key;\r\n return process(nextPrefix, value);\r\n } else {\r\n // Standard logic { 'class-name': boolean }: treat the key as the class content\r\n return process(accumulatedPrefix, key);\r\n }\r\n })\r\n .filter(Boolean)\r\n .join(' ');\r\n }\r\n\r\n return '';\r\n };\r\n\r\n /**\r\n * Main utility for generating Tailwind classes.\r\n * Supports strings, nested objects with prefixes, and arrays.\r\n * * @param inputs - A list of arguments following the clsx pattern.\r\n * @returns A processed, deduplicated string of classes via twMerge.\r\n * * @example\r\n * tw('btn-base', { md: 'p-4', hover: { 'opacity-50': isDim } });\r\n */\r\n return (...inputs: ClassValue[]) => {\r\n const processed = inputs.map((input) => process('', input));\r\n return twMerge(clsx(processed));\r\n };\r\n}\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clases",
3
- "version": "1.1.8",
3
+ "version": "1.1.11",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
package/src/index.ts CHANGED
@@ -1,68 +1,81 @@
1
1
  import { twMerge } from 'tailwind-merge';
2
2
  import clsx, { type ClassValue } from 'clsx';
3
3
 
4
+ /**
5
+ * Creates a customized Tailwind class engine instance with prefix registry support.
6
+ * * @param plugins - Objects mapping custom aliases (e.g., 'ui') to real Tailwind prefixes (e.g., 'prefix').
7
+ * @returns A recursive 'cl' function that processes strings, arrays, and objects.
8
+ * * @example
9
+ * const tw = createCl({ md: 'md', ui: 'prefix' });
10
+ */
4
11
  export function createCl<TPlugins extends Record<string, string>[]>(...plugins: TPlugins) {
5
12
  const registry: Record<string, string> = Object.assign({}, ...plugins);
6
13
 
7
- // Función auxiliar para obtener el prefijo real de Tailwind
8
- const getPrefix = (key: string) => {
9
- return key
10
- .split(':')
11
- .map((part) => registry[part])
12
- .filter(Boolean)
13
- .join(':');
14
- };
14
+ /**
15
+ * Recursively processes input values to apply prefixes and logic.
16
+ * * @param accumulatedPrefix - The prefix path built during recursion (e.g., 'md:hover').
17
+ * @param input - The value to process (string, array, or object).
18
+ * @returns A string of prefixed and filtered Tailwind classes.
19
+ */
20
+ const process = (accumulatedPrefix: string, input: any): string => {
21
+ if (!input) return '';
15
22
 
16
- const process = (accumulatedKey: string, value: any): string => {
17
- if (!value) return '';
23
+ // 1. Strings: Resolve real Tailwind prefixes and apply them
24
+ if (typeof input === 'string') {
25
+ const resolved = accumulatedPrefix
26
+ .split(':')
27
+ .map((part) => (part === 'base' ? null : registry[part] || null))
28
+ .filter(Boolean)
29
+ .join(':');
18
30
 
19
- // 1. Arrays: Multilínea
20
- if (Array.isArray(value)) {
21
- return value
22
- .map((v) => process(accumulatedKey, v))
31
+ return input
32
+ .split(/[,\s\n]+/) // Split by commas, spaces, or newlines
23
33
  .filter(Boolean)
34
+ .map((cls) => (resolved ? `${resolved}:${cls}` : cls))
24
35
  .join(' ');
25
36
  }
26
37
 
27
- // 2. Objetos
28
- if (typeof value === 'object') {
29
- return Object.entries(value)
30
- .map(([k, v]) => {
31
- if (!v) return '';
38
+ // 2. Arrays: Multi-line support and recursive processing
39
+ if (Array.isArray(input)) {
40
+ return input
41
+ .map((i) => process(accumulatedPrefix, i))
42
+ .filter(Boolean)
43
+ .join(' ');
44
+ }
32
45
 
33
- // Si la llave es un prefijo (md, hover), profundizamos
34
- if (registry[k] !== undefined) {
35
- const nextKey = accumulatedKey ? `${accumulatedKey}:${k}` : k;
36
- return process(nextKey, v);
37
- }
46
+ // 3. Objects: Prefix navigation and Conditional Logic (clsx-style)
47
+ if (typeof input === 'object') {
48
+ return Object.entries(input)
49
+ .map(([key, value]) => {
50
+ if (!value) return '';
51
+
52
+ const isBase = key === 'base';
53
+ const isPrefix = registry[key] !== undefined;
38
54
 
39
- // Si NO es prefijo, tratamos la llave 'k' como la clase final
40
- // Pero le aplicamos el prefijo acumulado hasta ahora
41
- return applyPrefix(accumulatedKey, k);
55
+ if (isBase || isPrefix) {
56
+ // It's an organization node or a prefix: accumulate and dive deeper
57
+ const nextPrefix = accumulatedPrefix ? `${accumulatedPrefix}:${key}` : key;
58
+ return process(nextPrefix, value);
59
+ } else {
60
+ // Standard logic { 'class-name': boolean }: treat the key as the class content
61
+ return process(accumulatedPrefix, key);
62
+ }
42
63
  })
64
+ .filter(Boolean)
43
65
  .join(' ');
44
66
  }
45
67
 
46
- // 3. Strings directos: Aplicamos el prefijo acumulado a cada palabra
47
- if (typeof value === 'string') {
48
- return applyPrefix(accumulatedKey, value);
49
- }
50
-
51
68
  return '';
52
69
  };
53
70
 
54
- // Función para aplicar el prefijo resuelto a un string de clases
55
- const applyPrefix = (key: string, classString: string): string => {
56
- const resolved = getPrefix(key);
57
- if (!resolved) return classString;
58
-
59
- return classString
60
- .split(/[,\s\n]+/)
61
- .filter(Boolean)
62
- .map((cls) => `${resolved}:${cls}`)
63
- .join(' ');
64
- };
65
-
71
+ /**
72
+ * Main utility for generating Tailwind classes.
73
+ * Supports strings, nested objects with prefixes, and arrays.
74
+ * * @param inputs - A list of arguments following the clsx pattern.
75
+ * @returns A processed, deduplicated string of classes via twMerge.
76
+ * * @example
77
+ * tw('btn-base', { md: 'p-4', hover: { 'opacity-50': isDim } });
78
+ */
66
79
  return (...inputs: ClassValue[]) => {
67
80
  const processed = inputs.map((input) => process('', input));
68
81
  return twMerge(clsx(processed));
@@ -70,4 +70,29 @@ describe('cl - Prefix Engine (Scope: Prefixes + clsx + twMerge)', () => {
70
70
  });
71
71
  expect(result).toBe('base-btn bg-blue-500 md:px-4 md:text-white');
72
72
  });
73
+
74
+ it('Nivel 8: Lógica Condicional Anidada dentro de Prefijos', () => {
75
+ const isActive = true;
76
+ const isError = false;
77
+ // Forzamos el tipo para que TS permita comparar con 'light' en el test
78
+ const theme = 'dark' as 'light' | 'dark';
79
+
80
+ const result = cl({
81
+ md: {
82
+ // Objeto de lógica pura dentro de un prefijo
83
+ 'bg-green-500': isActive,
84
+ 'bg-red-500': isError,
85
+ // Combinación con otro prefijo anidado
86
+ dark: {
87
+ 'text-white': theme === 'dark',
88
+ 'text-black': theme === 'light' // Ahora TS ya no se queja
89
+ }
90
+ }
91
+ });
92
+
93
+ expect(result).toContain('md:bg-green-500');
94
+ expect(result).toContain('md:dark:text-white');
95
+ expect(result).not.toContain('bg-red-500');
96
+ expect(result).not.toContain('text-black');
97
+ });
73
98
  });