clases 1.1.13 → 1.1.14
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/dist/index.cjs +11 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +11 -25
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
- package/src/index.ts +54 -31
- package/src/scripts/init.js +19 -13
- package/src/test/cn.test.ts +76 -64
package/dist/index.cjs
CHANGED
|
@@ -8,48 +8,34 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
8
8
|
var clsx__default = /*#__PURE__*/_interopDefault(clsx);
|
|
9
9
|
|
|
10
10
|
// src/index.ts
|
|
11
|
-
function createCl(
|
|
11
|
+
function createCl(plugins, options = {}) {
|
|
12
12
|
const registry = Object.assign({}, ...plugins);
|
|
13
|
+
const { baseKey = "base", conditionKey = "if", prefix: globalPrefix = "" } = options;
|
|
13
14
|
const process = (accumulatedPath, input) => {
|
|
14
|
-
|
|
15
|
-
if (!input) {
|
|
16
|
-
console.log(`[SKIP] Input is falsy`);
|
|
17
|
-
return "";
|
|
18
|
-
}
|
|
15
|
+
if (!input) return "";
|
|
19
16
|
if (typeof input === "string") {
|
|
20
17
|
const resolvedPrefix = accumulatedPath.split(":").map((part) => {
|
|
21
|
-
if (part ===
|
|
18
|
+
if (part === baseKey || part === conditionKey) return null;
|
|
22
19
|
return registry[part] || part;
|
|
23
20
|
}).filter(Boolean).join(":");
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
return input.split(/[,\s\n]+/).filter(Boolean).map((cls) => {
|
|
22
|
+
const finalClass = globalPrefix ? `${globalPrefix}${cls}` : cls;
|
|
23
|
+
return resolvedPrefix ? `${resolvedPrefix}:${finalClass}` : finalClass;
|
|
24
|
+
}).join(" ");
|
|
27
25
|
}
|
|
28
26
|
if (Array.isArray(input)) {
|
|
29
|
-
console.log(`[ARRAY] Processing ${input.length} elements...`);
|
|
30
27
|
return input.map((i) => process(accumulatedPath, i)).filter(Boolean).join(" ");
|
|
31
28
|
}
|
|
32
29
|
if (typeof input === "object") {
|
|
33
|
-
console.log(`[OBJECT] Keys:`, Object.keys(input));
|
|
34
30
|
return Object.entries(input).map(([key, value]) => {
|
|
35
|
-
|
|
31
|
+
if (!value) return "";
|
|
32
|
+
const isTransparent = key === baseKey || key === conditionKey;
|
|
36
33
|
const isRegistered = registry[key] !== void 0;
|
|
37
|
-
console.log(
|
|
38
|
-
` -> Key: "${key}" | Value: ${value} | isPrefix: ${isRegistered} | isTransparent: ${isTransparent}`
|
|
39
|
-
);
|
|
40
|
-
if (!value) {
|
|
41
|
-
console.log(` [SKIP KEY] "${key}" because value is falsy`);
|
|
42
|
-
return "";
|
|
43
|
-
}
|
|
44
34
|
if (isTransparent || isRegistered) {
|
|
45
35
|
const newPath = accumulatedPath ? `${accumulatedPath}:${key}` : key;
|
|
46
|
-
console.log(` [DIVE] Moving to path: "${newPath}"`);
|
|
47
36
|
return process(newPath, value);
|
|
48
37
|
} else {
|
|
49
|
-
|
|
50
|
-
` [CLASS] Treating key "${key}" as class under path "${accumulatedPath}"`
|
|
51
|
-
);
|
|
52
|
-
return process(accumulatedPath, key);
|
|
38
|
+
return value ? process(accumulatedPath, key) : "";
|
|
53
39
|
}
|
|
54
40
|
}).filter(Boolean).join(" ");
|
|
55
41
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["twMerge","clsx"],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["twMerge","clsx"],"mappings":";;;;;;;;;;AAUO,SAAS,QAAA,CAId,OAAA,EAAmB,OAAA,GAA2B,EAAC,EAAG;AAEhD,EAAA,MAAM,WAAmC,MAAA,CAAO,MAAA,CAAO,EAAC,EAAG,GAAG,OAAO,CAAA;AAGrE,EAAA,MAAM,EAAE,UAAU,MAAA,EAAa,YAAA,GAAe,MAAW,MAAA,EAAQ,YAAA,GAAe,IAAG,GAAI,OAAA;AAoBvF,EAAA,MAAM,OAAA,GAAU,CAAC,eAAA,EAAyB,KAAA,KAAuB;AAC7D,IAAA,IAAI,CAAC,OAAO,OAAO,EAAA;AAGnB,IAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC3B,MAAA,MAAM,iBAAiB,eAAA,CAClB,KAAA,CAAM,GAAG,CAAA,CACT,GAAA,CAAI,CAAC,IAAA,KAAS;AAEX,QAAA,IAAI,IAAA,KAAS,OAAA,IAAW,IAAA,KAAS,YAAA,EAAc,OAAO,IAAA;AAEtD,QAAA,OAAO,QAAA,CAAS,IAAI,CAAA,IAAK,IAAA;AAAA,MAC7B,CAAC,CAAA,CACA,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAEb,MAAA,OAAO,KAAA,CACF,MAAM,UAAU,CAAA,CAChB,OAAO,OAAO,CAAA,CACd,GAAA,CAAI,CAAC,GAAA,KAAQ;AAEV,QAAA,MAAM,aAAa,YAAA,GAAe,CAAA,EAAG,YAAY,CAAA,EAAG,GAAG,CAAA,CAAA,GAAK,GAAA;AAE5D,QAAA,OAAO,cAAA,GAAiB,CAAA,EAAG,cAAc,CAAA,CAAA,EAAI,UAAU,CAAA,CAAA,GAAK,UAAA;AAAA,MAChE,CAAC,CAAA,CACA,IAAA,CAAK,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,eAAA,EAAiB,CAAC,CAAC,CAAA,CACtC,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,aAAA,GAAgB,GAAA,KAAQ,OAAA,IAAW,GAAA,KAAQ,YAAA;AACjD,QAAA,MAAM,YAAA,GAAe,QAAA,CAAS,GAAG,CAAA,KAAM,MAAA;AAEvC,QAAA,IAAI,iBAAiB,YAAA,EAAc;AAE/B,UAAA,MAAM,UAAU,eAAA,GAAkB,CAAA,EAAG,eAAe,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA,GAAK,GAAA;AAChE,UAAA,OAAO,OAAA,CAAQ,SAAS,KAAK,CAAA;AAAA,QACjC,CAAA,MAAO;AAGH,UAAA,OAAO,KAAA,GAAQ,OAAA,CAAQ,eAAA,EAAiB,GAAG,CAAA,GAAI,EAAA;AAAA,QACnD;AAAA,MACJ,CAAC,CAAA,CACA,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAAA,IACjB;AAEA,IAAA,OAAO,EAAA;AAAA,EACX,CAAA;AAEA,EAAA,OAAO,IAAI,MAAA,KAAyB;AAChC,IAAA,MAAM,SAAA,GAAY,OAAO,GAAA,CAAI,CAAC,UAAU,OAAA,CAAQ,EAAA,EAAI,KAAK,CAAC,CAAA;AAE1D,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// Tipos para las opciones de configuración\r\nexport interface ClOptions<B extends string, C extends string> {\r\n baseKey?: B;\r\n conditionKey?: C;\r\n prefix?: string;\r\n}\r\n\r\nexport function createCl<\r\n TPlugins extends Record<string, string>[],\r\n B extends string = 'base',\r\n C extends string = '?'\r\n>(plugins: TPlugins, options: ClOptions<B, C> = {}) {\r\n // Unificamos los plugins en un solo registro\r\n const registry: Record<string, string> = Object.assign({}, ...plugins);\r\n\r\n // Extraemos opciones con valores por defecto\r\n const { baseKey = 'base' as B, conditionKey = 'if' as C, prefix: globalPrefix = '' } = options;\r\n\r\n /**\r\n * Aplica el prefijo global de configuración (ej: 'tw-') a una clase\r\n */\r\n const applyGlobalPrefix = (cls: string) => {\r\n if (!globalPrefix) return cls;\r\n // Evitamos prefijar si la clase ya tiene el prefijo o es vacía\r\n return cls\r\n .split(':')\r\n .map((part) => {\r\n // Solo prefijamos la clase final, no los modificadores (hover, md, etc)\r\n // pero como tailwind-merge y clsx se encargan de la limpieza,\r\n // aplicamos una lógica simple:\r\n return part;\r\n })\r\n .join(':');\r\n // Nota: El prefijo global de Tailwind suele aplicarse a la clase base: 'tw-bg-red'\r\n };\r\n\r\n const process = (accumulatedPath: string, input: any): string => {\r\n if (!input) return '';\r\n\r\n // 1. Caso String: Aquí es donde se resuelve el path acumulado\r\n if (typeof input === 'string') {\r\n const resolvedPrefix = accumulatedPath\r\n .split(':')\r\n .map((part) => {\r\n // Si la parte del path es una de las llaves especiales, no genera prefijo CSS\r\n if (part === baseKey || part === conditionKey) return null;\r\n // Retornamos el valor del registro (ej: 'hover') o la parte tal cual\r\n return registry[part] || part;\r\n })\r\n .filter(Boolean)\r\n .join(':');\r\n\r\n return input\r\n .split(/[,\\s\\n]+/)\r\n .filter(Boolean)\r\n .map((cls) => {\r\n // Aplicamos el prefijo global a la clase\r\n const finalClass = globalPrefix ? `${globalPrefix}${cls}` : cls;\r\n // Aplicamos el prefijo acumulado (hover, md, etc)\r\n return resolvedPrefix ? `${resolvedPrefix}:${finalClass}` : finalClass;\r\n })\r\n .join(' ');\r\n }\r\n\r\n // 2. Caso Array\r\n if (Array.isArray(input)) {\r\n return input\r\n .map((i) => process(accumulatedPath, i))\r\n .filter(Boolean)\r\n .join(' ');\r\n }\r\n\r\n // 3. Caso Objeto\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 isTransparent = key === baseKey || key === conditionKey;\r\n const isRegistered = registry[key] !== undefined;\r\n\r\n if (isTransparent || isRegistered) {\r\n // Si es una llave especial o registrada, profundizamos en el path\r\n const newPath = accumulatedPath ? `${accumulatedPath}:${key}` : key;\r\n return process(newPath, value);\r\n } else {\r\n // Si la llave no está registrada, se trata como una clase bajo el path actual\r\n // (Aquí value actúa como condición booleana para la llave)\r\n return value ? process(accumulatedPath, 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 return (...inputs: ClassValue[]) => {\r\n const processed = inputs.map((input) => process('', input));\r\n // twMerge se encarga de que 'bg-red-500 bg-blue-500' resulte en 'bg-blue-500'\r\n return twMerge(clsx(processed));\r\n };\r\n}\r\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { ClassValue } from 'clsx';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
interface ClOptions<B extends string, C extends string> {
|
|
4
|
+
baseKey?: B;
|
|
5
|
+
conditionKey?: C;
|
|
6
|
+
prefix?: string;
|
|
7
|
+
}
|
|
8
|
+
declare function createCl<TPlugins extends Record<string, string>[], B extends string = 'base', C extends string = '?'>(plugins: TPlugins, options?: ClOptions<B, C>): (...inputs: ClassValue[]) => string;
|
|
4
9
|
|
|
5
|
-
export { createCl };
|
|
10
|
+
export { type ClOptions, createCl };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { ClassValue } from 'clsx';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
interface ClOptions<B extends string, C extends string> {
|
|
4
|
+
baseKey?: B;
|
|
5
|
+
conditionKey?: C;
|
|
6
|
+
prefix?: string;
|
|
7
|
+
}
|
|
8
|
+
declare function createCl<TPlugins extends Record<string, string>[], B extends string = 'base', C extends string = '?'>(plugins: TPlugins, options?: ClOptions<B, C>): (...inputs: ClassValue[]) => string;
|
|
4
9
|
|
|
5
|
-
export { createCl };
|
|
10
|
+
export { type ClOptions, createCl };
|
package/dist/index.js
CHANGED
|
@@ -2,48 +2,34 @@ import { twMerge } from 'tailwind-merge';
|
|
|
2
2
|
import clsx from 'clsx';
|
|
3
3
|
|
|
4
4
|
// src/index.ts
|
|
5
|
-
function createCl(
|
|
5
|
+
function createCl(plugins, options = {}) {
|
|
6
6
|
const registry = Object.assign({}, ...plugins);
|
|
7
|
+
const { baseKey = "base", conditionKey = "if", prefix: globalPrefix = "" } = options;
|
|
7
8
|
const process = (accumulatedPath, input) => {
|
|
8
|
-
|
|
9
|
-
if (!input) {
|
|
10
|
-
console.log(`[SKIP] Input is falsy`);
|
|
11
|
-
return "";
|
|
12
|
-
}
|
|
9
|
+
if (!input) return "";
|
|
13
10
|
if (typeof input === "string") {
|
|
14
11
|
const resolvedPrefix = accumulatedPath.split(":").map((part) => {
|
|
15
|
-
if (part ===
|
|
12
|
+
if (part === baseKey || part === conditionKey) return null;
|
|
16
13
|
return registry[part] || part;
|
|
17
14
|
}).filter(Boolean).join(":");
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
return input.split(/[,\s\n]+/).filter(Boolean).map((cls) => {
|
|
16
|
+
const finalClass = globalPrefix ? `${globalPrefix}${cls}` : cls;
|
|
17
|
+
return resolvedPrefix ? `${resolvedPrefix}:${finalClass}` : finalClass;
|
|
18
|
+
}).join(" ");
|
|
21
19
|
}
|
|
22
20
|
if (Array.isArray(input)) {
|
|
23
|
-
console.log(`[ARRAY] Processing ${input.length} elements...`);
|
|
24
21
|
return input.map((i) => process(accumulatedPath, i)).filter(Boolean).join(" ");
|
|
25
22
|
}
|
|
26
23
|
if (typeof input === "object") {
|
|
27
|
-
console.log(`[OBJECT] Keys:`, Object.keys(input));
|
|
28
24
|
return Object.entries(input).map(([key, value]) => {
|
|
29
|
-
|
|
25
|
+
if (!value) return "";
|
|
26
|
+
const isTransparent = key === baseKey || key === conditionKey;
|
|
30
27
|
const isRegistered = registry[key] !== void 0;
|
|
31
|
-
console.log(
|
|
32
|
-
` -> Key: "${key}" | Value: ${value} | isPrefix: ${isRegistered} | isTransparent: ${isTransparent}`
|
|
33
|
-
);
|
|
34
|
-
if (!value) {
|
|
35
|
-
console.log(` [SKIP KEY] "${key}" because value is falsy`);
|
|
36
|
-
return "";
|
|
37
|
-
}
|
|
38
28
|
if (isTransparent || isRegistered) {
|
|
39
29
|
const newPath = accumulatedPath ? `${accumulatedPath}:${key}` : key;
|
|
40
|
-
console.log(` [DIVE] Moving to path: "${newPath}"`);
|
|
41
30
|
return process(newPath, value);
|
|
42
31
|
} else {
|
|
43
|
-
|
|
44
|
-
` [CLASS] Treating key "${key}" as class under path "${accumulatedPath}"`
|
|
45
|
-
);
|
|
46
|
-
return process(accumulatedPath, key);
|
|
32
|
+
return value ? process(accumulatedPath, key) : "";
|
|
47
33
|
}
|
|
48
34
|
}).filter(Boolean).join(" ");
|
|
49
35
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;AAUO,SAAS,QAAA,CAId,OAAA,EAAmB,OAAA,GAA2B,EAAC,EAAG;AAEhD,EAAA,MAAM,WAAmC,MAAA,CAAO,MAAA,CAAO,EAAC,EAAG,GAAG,OAAO,CAAA;AAGrE,EAAA,MAAM,EAAE,UAAU,MAAA,EAAa,YAAA,GAAe,MAAW,MAAA,EAAQ,YAAA,GAAe,IAAG,GAAI,OAAA;AAoBvF,EAAA,MAAM,OAAA,GAAU,CAAC,eAAA,EAAyB,KAAA,KAAuB;AAC7D,IAAA,IAAI,CAAC,OAAO,OAAO,EAAA;AAGnB,IAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC3B,MAAA,MAAM,iBAAiB,eAAA,CAClB,KAAA,CAAM,GAAG,CAAA,CACT,GAAA,CAAI,CAAC,IAAA,KAAS;AAEX,QAAA,IAAI,IAAA,KAAS,OAAA,IAAW,IAAA,KAAS,YAAA,EAAc,OAAO,IAAA;AAEtD,QAAA,OAAO,QAAA,CAAS,IAAI,CAAA,IAAK,IAAA;AAAA,MAC7B,CAAC,CAAA,CACA,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAEb,MAAA,OAAO,KAAA,CACF,MAAM,UAAU,CAAA,CAChB,OAAO,OAAO,CAAA,CACd,GAAA,CAAI,CAAC,GAAA,KAAQ;AAEV,QAAA,MAAM,aAAa,YAAA,GAAe,CAAA,EAAG,YAAY,CAAA,EAAG,GAAG,CAAA,CAAA,GAAK,GAAA;AAE5D,QAAA,OAAO,cAAA,GAAiB,CAAA,EAAG,cAAc,CAAA,CAAA,EAAI,UAAU,CAAA,CAAA,GAAK,UAAA;AAAA,MAChE,CAAC,CAAA,CACA,IAAA,CAAK,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,eAAA,EAAiB,CAAC,CAAC,CAAA,CACtC,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,aAAA,GAAgB,GAAA,KAAQ,OAAA,IAAW,GAAA,KAAQ,YAAA;AACjD,QAAA,MAAM,YAAA,GAAe,QAAA,CAAS,GAAG,CAAA,KAAM,MAAA;AAEvC,QAAA,IAAI,iBAAiB,YAAA,EAAc;AAE/B,UAAA,MAAM,UAAU,eAAA,GAAkB,CAAA,EAAG,eAAe,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA,GAAK,GAAA;AAChE,UAAA,OAAO,OAAA,CAAQ,SAAS,KAAK,CAAA;AAAA,QACjC,CAAA,MAAO;AAGH,UAAA,OAAO,KAAA,GAAQ,OAAA,CAAQ,eAAA,EAAiB,GAAG,CAAA,GAAI,EAAA;AAAA,QACnD;AAAA,MACJ,CAAC,CAAA,CACA,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAAA,IACjB;AAEA,IAAA,OAAO,EAAA;AAAA,EACX,CAAA;AAEA,EAAA,OAAO,IAAI,MAAA,KAAyB;AAChC,IAAA,MAAM,SAAA,GAAY,OAAO,GAAA,CAAI,CAAC,UAAU,OAAA,CAAQ,EAAA,EAAI,KAAK,CAAC,CAAA;AAE1D,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// Tipos para las opciones de configuración\r\nexport interface ClOptions<B extends string, C extends string> {\r\n baseKey?: B;\r\n conditionKey?: C;\r\n prefix?: string;\r\n}\r\n\r\nexport function createCl<\r\n TPlugins extends Record<string, string>[],\r\n B extends string = 'base',\r\n C extends string = '?'\r\n>(plugins: TPlugins, options: ClOptions<B, C> = {}) {\r\n // Unificamos los plugins en un solo registro\r\n const registry: Record<string, string> = Object.assign({}, ...plugins);\r\n\r\n // Extraemos opciones con valores por defecto\r\n const { baseKey = 'base' as B, conditionKey = 'if' as C, prefix: globalPrefix = '' } = options;\r\n\r\n /**\r\n * Aplica el prefijo global de configuración (ej: 'tw-') a una clase\r\n */\r\n const applyGlobalPrefix = (cls: string) => {\r\n if (!globalPrefix) return cls;\r\n // Evitamos prefijar si la clase ya tiene el prefijo o es vacía\r\n return cls\r\n .split(':')\r\n .map((part) => {\r\n // Solo prefijamos la clase final, no los modificadores (hover, md, etc)\r\n // pero como tailwind-merge y clsx se encargan de la limpieza,\r\n // aplicamos una lógica simple:\r\n return part;\r\n })\r\n .join(':');\r\n // Nota: El prefijo global de Tailwind suele aplicarse a la clase base: 'tw-bg-red'\r\n };\r\n\r\n const process = (accumulatedPath: string, input: any): string => {\r\n if (!input) return '';\r\n\r\n // 1. Caso String: Aquí es donde se resuelve el path acumulado\r\n if (typeof input === 'string') {\r\n const resolvedPrefix = accumulatedPath\r\n .split(':')\r\n .map((part) => {\r\n // Si la parte del path es una de las llaves especiales, no genera prefijo CSS\r\n if (part === baseKey || part === conditionKey) return null;\r\n // Retornamos el valor del registro (ej: 'hover') o la parte tal cual\r\n return registry[part] || part;\r\n })\r\n .filter(Boolean)\r\n .join(':');\r\n\r\n return input\r\n .split(/[,\\s\\n]+/)\r\n .filter(Boolean)\r\n .map((cls) => {\r\n // Aplicamos el prefijo global a la clase\r\n const finalClass = globalPrefix ? `${globalPrefix}${cls}` : cls;\r\n // Aplicamos el prefijo acumulado (hover, md, etc)\r\n return resolvedPrefix ? `${resolvedPrefix}:${finalClass}` : finalClass;\r\n })\r\n .join(' ');\r\n }\r\n\r\n // 2. Caso Array\r\n if (Array.isArray(input)) {\r\n return input\r\n .map((i) => process(accumulatedPath, i))\r\n .filter(Boolean)\r\n .join(' ');\r\n }\r\n\r\n // 3. Caso Objeto\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 isTransparent = key === baseKey || key === conditionKey;\r\n const isRegistered = registry[key] !== undefined;\r\n\r\n if (isTransparent || isRegistered) {\r\n // Si es una llave especial o registrada, profundizamos en el path\r\n const newPath = accumulatedPath ? `${accumulatedPath}:${key}` : key;\r\n return process(newPath, value);\r\n } else {\r\n // Si la llave no está registrada, se trata como una clase bajo el path actual\r\n // (Aquí value actúa como condición booleana para la llave)\r\n return value ? process(accumulatedPath, 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 return (...inputs: ClassValue[]) => {\r\n const processed = inputs.map((input) => process('', input));\r\n // twMerge se encarga de que 'bg-red-500 bg-blue-500' resulte en 'bg-blue-500'\r\n return twMerge(clsx(processed));\r\n };\r\n}\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clases",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"clases-init": "./scripts/init.js"
|
|
9
|
+
},
|
|
7
10
|
"exports": {
|
|
8
11
|
".": {
|
|
9
12
|
"types": "./dist/index.d.ts",
|
package/src/index.ts
CHANGED
|
@@ -1,41 +1,72 @@
|
|
|
1
1
|
import { twMerge } from 'tailwind-merge';
|
|
2
2
|
import clsx, { type ClassValue } from 'clsx';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// Tipos para las opciones de configuración
|
|
5
|
+
export interface ClOptions<B extends string, C extends string> {
|
|
6
|
+
baseKey?: B;
|
|
7
|
+
conditionKey?: C;
|
|
8
|
+
prefix?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createCl<
|
|
12
|
+
TPlugins extends Record<string, string>[],
|
|
13
|
+
B extends string = 'base',
|
|
14
|
+
C extends string = '?'
|
|
15
|
+
>(plugins: TPlugins, options: ClOptions<B, C> = {}) {
|
|
16
|
+
// Unificamos los plugins en un solo registro
|
|
5
17
|
const registry: Record<string, string> = Object.assign({}, ...plugins);
|
|
6
18
|
|
|
7
|
-
|
|
8
|
-
|
|
19
|
+
// Extraemos opciones con valores por defecto
|
|
20
|
+
const { baseKey = 'base' as B, conditionKey = 'if' as C, prefix: globalPrefix = '' } = options;
|
|
9
21
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Aplica el prefijo global de configuración (ej: 'tw-') a una clase
|
|
24
|
+
*/
|
|
25
|
+
const applyGlobalPrefix = (cls: string) => {
|
|
26
|
+
if (!globalPrefix) return cls;
|
|
27
|
+
// Evitamos prefijar si la clase ya tiene el prefijo o es vacía
|
|
28
|
+
return cls
|
|
29
|
+
.split(':')
|
|
30
|
+
.map((part) => {
|
|
31
|
+
// Solo prefijamos la clase final, no los modificadores (hover, md, etc)
|
|
32
|
+
// pero como tailwind-merge y clsx se encargan de la limpieza,
|
|
33
|
+
// aplicamos una lógica simple:
|
|
34
|
+
return part;
|
|
35
|
+
})
|
|
36
|
+
.join(':');
|
|
37
|
+
// Nota: El prefijo global de Tailwind suele aplicarse a la clase base: 'tw-bg-red'
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const process = (accumulatedPath: string, input: any): string => {
|
|
41
|
+
if (!input) return '';
|
|
14
42
|
|
|
15
|
-
// 1. Caso String
|
|
43
|
+
// 1. Caso String: Aquí es donde se resuelve el path acumulado
|
|
16
44
|
if (typeof input === 'string') {
|
|
17
45
|
const resolvedPrefix = accumulatedPath
|
|
18
46
|
.split(':')
|
|
19
47
|
.map((part) => {
|
|
20
|
-
|
|
48
|
+
// Si la parte del path es una de las llaves especiales, no genera prefijo CSS
|
|
49
|
+
if (part === baseKey || part === conditionKey) return null;
|
|
50
|
+
// Retornamos el valor del registro (ej: 'hover') o la parte tal cual
|
|
21
51
|
return registry[part] || part;
|
|
22
52
|
})
|
|
23
53
|
.filter(Boolean)
|
|
24
54
|
.join(':');
|
|
25
55
|
|
|
26
|
-
|
|
56
|
+
return input
|
|
27
57
|
.split(/[,\s\n]+/)
|
|
28
58
|
.filter(Boolean)
|
|
29
|
-
.map((cls) =>
|
|
59
|
+
.map((cls) => {
|
|
60
|
+
// Aplicamos el prefijo global a la clase
|
|
61
|
+
const finalClass = globalPrefix ? `${globalPrefix}${cls}` : cls;
|
|
62
|
+
// Aplicamos el prefijo acumulado (hover, md, etc)
|
|
63
|
+
return resolvedPrefix ? `${resolvedPrefix}:${finalClass}` : finalClass;
|
|
64
|
+
})
|
|
30
65
|
.join(' ');
|
|
31
|
-
|
|
32
|
-
console.log(`[STRING] Resolved: "${resolvedPrefix}" | Out: "${result}"`);
|
|
33
|
-
return result;
|
|
34
66
|
}
|
|
35
67
|
|
|
36
68
|
// 2. Caso Array
|
|
37
69
|
if (Array.isArray(input)) {
|
|
38
|
-
console.log(`[ARRAY] Processing ${input.length} elements...`);
|
|
39
70
|
return input
|
|
40
71
|
.map((i) => process(accumulatedPath, i))
|
|
41
72
|
.filter(Boolean)
|
|
@@ -44,30 +75,21 @@ export function createCl<TPlugins extends Record<string, string>[]>(...plugins:
|
|
|
44
75
|
|
|
45
76
|
// 3. Caso Objeto
|
|
46
77
|
if (typeof input === 'object') {
|
|
47
|
-
console.log(`[OBJECT] Keys:`, Object.keys(input));
|
|
48
78
|
return Object.entries(input)
|
|
49
79
|
.map(([key, value]) => {
|
|
50
|
-
|
|
51
|
-
const isRegistered = registry[key] !== undefined;
|
|
80
|
+
if (!value) return '';
|
|
52
81
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
if (!value) {
|
|
58
|
-
console.log(` [SKIP KEY] "${key}" because value is falsy`);
|
|
59
|
-
return '';
|
|
60
|
-
}
|
|
82
|
+
const isTransparent = key === baseKey || key === conditionKey;
|
|
83
|
+
const isRegistered = registry[key] !== undefined;
|
|
61
84
|
|
|
62
85
|
if (isTransparent || isRegistered) {
|
|
86
|
+
// Si es una llave especial o registrada, profundizamos en el path
|
|
63
87
|
const newPath = accumulatedPath ? `${accumulatedPath}:${key}` : key;
|
|
64
|
-
console.log(` [DIVE] Moving to path: "${newPath}"`);
|
|
65
88
|
return process(newPath, value);
|
|
66
89
|
} else {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
);
|
|
70
|
-
return process(accumulatedPath, key);
|
|
90
|
+
// Si la llave no está registrada, se trata como una clase bajo el path actual
|
|
91
|
+
// (Aquí value actúa como condición booleana para la llave)
|
|
92
|
+
return value ? process(accumulatedPath, key) : '';
|
|
71
93
|
}
|
|
72
94
|
})
|
|
73
95
|
.filter(Boolean)
|
|
@@ -79,6 +101,7 @@ export function createCl<TPlugins extends Record<string, string>[]>(...plugins:
|
|
|
79
101
|
|
|
80
102
|
return (...inputs: ClassValue[]) => {
|
|
81
103
|
const processed = inputs.map((input) => process('', input));
|
|
104
|
+
// twMerge se encarga de que 'bg-red-500 bg-blue-500' resulte en 'bg-blue-500'
|
|
82
105
|
return twMerge(clsx(processed));
|
|
83
106
|
};
|
|
84
107
|
}
|
package/src/scripts/init.js
CHANGED
|
@@ -1,28 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
const fs = require('fs');
|
|
2
3
|
const path = require('path');
|
|
3
4
|
|
|
4
|
-
// Intentamos
|
|
5
|
-
const projectRoot = process.env.INIT_CWD ||
|
|
6
|
-
// Buscamos si existe la carpeta 'src' para ponerlo ahí (mejor para Tailwind/Vite)
|
|
5
|
+
// Intentamos INIT_CWD (npm/pnpm), si no, el directorio actual
|
|
6
|
+
const projectRoot = process.env.INIT_CWD || process.cwd();
|
|
7
7
|
const hasSrc = fs.existsSync(path.join(projectRoot, 'src'));
|
|
8
8
|
const targetDir = hasSrc ? path.join(projectRoot, 'src') : projectRoot;
|
|
9
9
|
const configPath = path.join(targetDir, 'clases.config.ts');
|
|
10
10
|
|
|
11
11
|
const template = `import { createCl } from 'clases';
|
|
12
12
|
|
|
13
|
-
// Instancia base agnóstica
|
|
14
13
|
export const cl = createCl({});
|
|
15
14
|
`;
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
function init() {
|
|
17
|
+
if (fs.existsSync(configPath)) {
|
|
18
|
+
// Si el usuario lo corre manualmente con npx, avisamos.
|
|
19
|
+
// Si es postinstall, mejor callar.
|
|
20
|
+
if (!process.env.INIT_CWD) {
|
|
21
|
+
console.log('⚠️ El archivo clases.config.ts ya existe.');
|
|
22
|
+
}
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
20
27
|
fs.writeFileSync(configPath, template, 'utf8');
|
|
21
|
-
console.log(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
);
|
|
28
|
+
console.log('\x1b[32m%s\x1b[0m', '✅ clases.config.ts generado con éxito.');
|
|
29
|
+
} catch (e) {
|
|
30
|
+
console.error('❌ Error al crear la configuración:', e.message);
|
|
25
31
|
}
|
|
26
|
-
} catch (err) {
|
|
27
|
-
// Fallo silencioso para no romper la instalación si hay problemas de permisos
|
|
28
32
|
}
|
|
33
|
+
|
|
34
|
+
init();
|
package/src/test/cn.test.ts
CHANGED
|
@@ -1,98 +1,110 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { createCl } from '../index';
|
|
2
|
+
import { createCl } from '../index'; // Ajusta la ruta a tu archivo
|
|
3
3
|
|
|
4
|
-
describe('
|
|
5
|
-
//
|
|
6
|
-
const
|
|
4
|
+
describe('createCl - Motor de Clases', () => {
|
|
5
|
+
// Configuración de prueba simulando Tailwind
|
|
6
|
+
const tailwindRegistry = {
|
|
7
|
+
hover: 'hover',
|
|
8
|
+
md: 'md',
|
|
9
|
+
dark: 'dark'
|
|
10
|
+
};
|
|
7
11
|
|
|
8
|
-
it('
|
|
12
|
+
it('debe manejar clases base simples', () => {
|
|
13
|
+
const cl = createCl([tailwindRegistry]);
|
|
14
|
+
const result = cl({ base: 'flex items-center' });
|
|
15
|
+
expect(result).toBe('flex items-center');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('debe aplicar prefijos del registro (Anidamiento)', () => {
|
|
19
|
+
const cl = createCl([tailwindRegistry]);
|
|
9
20
|
const result = cl({
|
|
10
|
-
|
|
11
|
-
|
|
21
|
+
base: 'block',
|
|
22
|
+
hover: 'bg-red-500',
|
|
23
|
+
md: {
|
|
24
|
+
base: 'p-4',
|
|
25
|
+
hover: 'bg-blue-500'
|
|
26
|
+
}
|
|
12
27
|
});
|
|
13
|
-
expect(result).
|
|
28
|
+
expect(result).toContain('block');
|
|
29
|
+
expect(result).toContain('hover:bg-red-500');
|
|
30
|
+
expect(result).toContain('md:p-4');
|
|
31
|
+
expect(result).toContain('md:hover:bg-blue-500');
|
|
14
32
|
});
|
|
15
33
|
|
|
16
|
-
it('
|
|
34
|
+
it('debe procesar condicionales con la llave "?"', () => {
|
|
35
|
+
const cl = createCl([tailwindRegistry]);
|
|
36
|
+
const isError = true;
|
|
37
|
+
const isLarge = false;
|
|
38
|
+
|
|
17
39
|
const result = cl({
|
|
18
|
-
|
|
40
|
+
base: 'text-sm',
|
|
41
|
+
'?': {
|
|
42
|
+
'text-red-500': isError,
|
|
43
|
+
'p-8': isLarge
|
|
44
|
+
}
|
|
19
45
|
});
|
|
20
|
-
|
|
21
|
-
expect(result).toBe('md:p-4 md:flex md:items-center md:text-white');
|
|
46
|
+
expect(result).toBe('text-sm text-red-500');
|
|
22
47
|
});
|
|
23
48
|
|
|
24
|
-
it('
|
|
25
|
-
const
|
|
26
|
-
|
|
49
|
+
it('debe permitir sobreescribir baseKey y conditionKey', () => {
|
|
50
|
+
const cl = createCl([tailwindRegistry], {
|
|
51
|
+
baseKey: 'root',
|
|
52
|
+
conditionKey: 'when'
|
|
53
|
+
});
|
|
27
54
|
|
|
28
55
|
const result = cl({
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
'opacity-
|
|
32
|
-
// Mezcla con prefijos
|
|
33
|
-
hover: 'bg-blue-500'
|
|
56
|
+
root: 'container',
|
|
57
|
+
when: { hidden: true },
|
|
58
|
+
hover: { root: 'opacity-50' }
|
|
34
59
|
});
|
|
35
60
|
|
|
36
|
-
expect(result).
|
|
37
|
-
expect(result).toContain('hover:bg-blue-500');
|
|
38
|
-
expect(result).not.toContain('opacity-0');
|
|
61
|
+
expect(result).toBe('container hidden hover:opacity-50');
|
|
39
62
|
});
|
|
40
63
|
|
|
41
|
-
it('
|
|
64
|
+
it('debe aplicar un prefijo global a todas las clases', () => {
|
|
65
|
+
const cl = createCl([tailwindRegistry], {
|
|
66
|
+
prefix: 'tw-'
|
|
67
|
+
});
|
|
68
|
+
|
|
42
69
|
const result = cl({
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
dark: 'bg-black text-white'
|
|
46
|
-
}
|
|
47
|
-
}
|
|
70
|
+
base: 'flex',
|
|
71
|
+
hover: 'bg-black'
|
|
48
72
|
});
|
|
49
|
-
expect(result).toBe('md:hover:dark:bg-black md:hover:dark:text-white');
|
|
50
|
-
});
|
|
51
73
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const result = cl('p-2', { md: 'p-4' }, { md: 'p-8' });
|
|
55
|
-
// md:p-8 debe ganar a md:p-4, y p-2 se mantiene por ser base
|
|
56
|
-
expect(result).toBe('p-2 md:p-8');
|
|
74
|
+
// Resultado esperado: "tw-flex hover:tw-bg-black"
|
|
75
|
+
expect(result).toBe('tw-flex hover:tw-bg-black');
|
|
57
76
|
});
|
|
58
77
|
|
|
59
|
-
it('
|
|
78
|
+
it('debe resolver clases tratadas como llaves de objeto (shorthand)', () => {
|
|
79
|
+
const cl = createCl([tailwindRegistry]);
|
|
80
|
+
const isVisible = true;
|
|
81
|
+
|
|
60
82
|
const result = cl({
|
|
61
|
-
|
|
83
|
+
'bg-white': true,
|
|
84
|
+
'text-black': isVisible,
|
|
85
|
+
hidden: false
|
|
62
86
|
});
|
|
63
|
-
|
|
87
|
+
|
|
88
|
+
expect(result).toBe('bg-white text-black');
|
|
64
89
|
});
|
|
65
90
|
|
|
66
|
-
it('
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
expect(result).toBe('base-btn bg-blue-500 md:px-4 md:text-white');
|
|
91
|
+
it('debe integrar tailwind-merge correctamente', () => {
|
|
92
|
+
const cl = createCl([tailwindRegistry]);
|
|
93
|
+
// twMerge debería eliminar la clase redundante 'p-2'
|
|
94
|
+
const result = cl('p-2', 'p-4');
|
|
95
|
+
expect(result).toBe('p-4');
|
|
72
96
|
});
|
|
73
97
|
|
|
74
|
-
it('
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
const theme = 'dark' as 'light' | 'dark';
|
|
98
|
+
it('debe manejar múltiples plugins/diccionarios', () => {
|
|
99
|
+
const pluginA = { ui: 'ui' };
|
|
100
|
+
const pluginB = { theme: 'theme' };
|
|
101
|
+
const cl = createCl([pluginA, pluginB]);
|
|
79
102
|
|
|
80
103
|
const result = cl({
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
}
|
|
104
|
+
ui: 'button',
|
|
105
|
+
theme: 'dark'
|
|
91
106
|
});
|
|
92
107
|
|
|
93
|
-
expect(result).
|
|
94
|
-
expect(result).toContain('md:dark:text-white');
|
|
95
|
-
expect(result).not.toContain('bg-red-500');
|
|
96
|
-
expect(result).not.toContain('text-black');
|
|
108
|
+
expect(result).toBe('ui:button theme:dark');
|
|
97
109
|
});
|
|
98
110
|
});
|