@hiscovega/grisso 1.0.5 → 1.0.7

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/lib/build.d.ts CHANGED
@@ -5,6 +5,8 @@ export interface BuildOptions {
5
5
  content?: string[];
6
6
  /** Minificar el CSS de salida (default: true) */
7
7
  minify?: boolean;
8
+ /** Patrones de clases protegidas del tree-shaking (se mergean con config.safelist) */
9
+ safelist?: (string | RegExp)[];
8
10
  }
9
11
  /**
10
12
  * API principal de Grisso — genera, purga y optimiza CSS
package/lib/build.js CHANGED
@@ -1,6 +1,13 @@
1
1
  import { generateCSS } from "./index.js";
2
2
  import { optimizeCSS } from "./optimize.js";
3
3
  import { purgeCSS } from "./purge.js";
4
+ import { resolveConfig } from "./resolve-config.js";
5
+ /** Convierte strings a RegExp, deja RegExp intactos */
6
+ function toRegExpArray(list) {
7
+ if (!list)
8
+ return [];
9
+ return list.map((item) => (item instanceof RegExp ? item : new RegExp(item)));
10
+ }
4
11
  /**
5
12
  * API principal de Grisso — genera, purga y optimiza CSS
6
13
  * sin depender de PostCSS.
@@ -16,12 +23,18 @@ import { purgeCSS } from "./purge.js";
16
23
  * });
17
24
  */
18
25
  export async function buildCSS(options = {}) {
19
- const { config, content, minify = true } = options;
26
+ const { config, content, minify = true, safelist: optsSafelist } = options;
20
27
  // 1. Generar CSS raw desde los generators
21
28
  let css = await generateCSS(config);
22
29
  // 2. Tree-shaking si hay rutas de contenido
23
30
  if (content && content.length > 0) {
24
- css = await purgeCSS(css, { content });
31
+ // Resolver config para extraer safelist configurada
32
+ const resolved = await resolveConfig(config);
33
+ const mergedSafelist = [
34
+ ...toRegExpArray(resolved.safelist),
35
+ ...toRegExpArray(optsSafelist),
36
+ ];
37
+ css = await purgeCSS(css, { content, safelist: mergedSafelist });
25
38
  }
26
39
  // 3. Optimizar (nesting, autoprefixer, minificación)
27
40
  css = await optimizeCSS(css, { minify });
package/lib/cli.js CHANGED
@@ -19,6 +19,7 @@ Uso:
19
19
 
20
20
  Comandos:
21
21
  build Genera CSS de utilidades
22
+ tokens Genera scaffold de tokens (custom properties)
22
23
 
23
24
  Opciones globales:
24
25
  --help, -h Muestra esta ayuda
@@ -34,6 +35,7 @@ Uso:
34
35
  Opciones:
35
36
  --config <ruta> Ruta a grisso.config.mjs
36
37
  --content <glob> Globs para tree-shaking (repetible)
38
+ --safelist <regex> Patrones de clases a preservar (repetible)
37
39
  --output <ruta> Archivo de salida (sin --output → stdout)
38
40
  --no-minify Deshabilitar minificación
39
41
  --help, -h Muestra esta ayuda
@@ -42,6 +44,7 @@ Ejemplos:
42
44
  grisso build # CSS completo a stdout
43
45
  grisso build --output dist/grisso.css # Escribir a archivo
44
46
  grisso build --content "src/**/*.tsx" --output out.css # Tree-shaking
47
+ grisso build --safelist "^p-" --safelist "^m-" # Proteger clases
45
48
  grisso build --no-minify # Sin minificar`);
46
49
  }
47
50
  /** Comando build: genera CSS */
@@ -51,6 +54,7 @@ async function runBuild(argv) {
51
54
  options: {
52
55
  config: { type: "string" },
53
56
  content: { type: "string", multiple: true },
57
+ safelist: { type: "string", multiple: true },
54
58
  output: { type: "string" },
55
59
  minify: { type: "boolean", default: true },
56
60
  help: { type: "boolean", short: "h", default: false },
@@ -65,6 +69,7 @@ async function runBuild(argv) {
65
69
  // Import dinámico para que --help sea instantáneo
66
70
  const { buildCSS } = await import("./build.js");
67
71
  const content = values.content && values.content.length > 0 ? values.content : undefined;
72
+ const safelist = values.safelist && values.safelist.length > 0 ? values.safelist : undefined;
68
73
  if (values.output) {
69
74
  const label = content
70
75
  ? "Generando CSS con tree-shaking..."
@@ -75,6 +80,7 @@ async function runBuild(argv) {
75
80
  config: values.config,
76
81
  content,
77
82
  minify: values.minify,
83
+ safelist,
78
84
  });
79
85
  if (values.output) {
80
86
  const outputPath = path.resolve(values.output);
@@ -90,6 +96,63 @@ async function runBuild(argv) {
90
96
  process.stdout.write(css);
91
97
  }
92
98
  }
99
+ /** Texto de ayuda del comando tokens */
100
+ function printTokensHelp() {
101
+ console.log(`grisso tokens — Genera scaffold de tokens (custom properties)
102
+
103
+ Uso:
104
+ grisso tokens [opciones]
105
+
106
+ Opciones:
107
+ --config <ruta> Ruta a grisso.config.mjs
108
+ --format <fmt> Formato de salida: css (default) o json
109
+ --output <ruta> Archivo de salida (sin --output → stdout)
110
+ --help, -h Muestra esta ayuda
111
+
112
+ Ejemplos:
113
+ grisso tokens # Scaffold CSS a stdout
114
+ grisso tokens --output tokens.css # Escribir a archivo
115
+ grisso tokens --config grisso.config.mjs # Usa config custom
116
+ grisso tokens --format json # Config como JSON`);
117
+ }
118
+ /** Comando tokens: genera scaffold de tokens */
119
+ async function runTokens(argv) {
120
+ const { values } = parseArgs({
121
+ args: argv,
122
+ options: {
123
+ config: { type: "string" },
124
+ format: { type: "string" },
125
+ output: { type: "string" },
126
+ help: { type: "boolean", short: "h", default: false },
127
+ },
128
+ strict: true,
129
+ });
130
+ if (values.help) {
131
+ printTokensHelp();
132
+ return;
133
+ }
134
+ const format = values.format;
135
+ if (format && format !== "css" && format !== "json") {
136
+ console.error(`Formato no soportado: ${format} (usa "css" o "json")`);
137
+ process.exit(1);
138
+ }
139
+ // Import dinámico para que --help sea instantáneo
140
+ const { extractTokens } = await import("./tokens.js");
141
+ const output = await extractTokens({ config: values.config, format });
142
+ if (values.output) {
143
+ const outputPath = path.resolve(values.output);
144
+ const dir = path.dirname(outputPath);
145
+ if (!fs.existsSync(dir)) {
146
+ fs.mkdirSync(dir, { recursive: true });
147
+ }
148
+ fs.writeFileSync(outputPath, output);
149
+ const kb = (Buffer.byteLength(output, "utf8") / 1024).toFixed(1);
150
+ console.error(`Generado ${path.relative(process.cwd(), outputPath)} (${kb} KB)`);
151
+ }
152
+ else {
153
+ process.stdout.write(output);
154
+ }
155
+ }
93
156
  /** Punto de entrada */
94
157
  async function main() {
95
158
  const { values, positionals } = parseArgs({
@@ -116,9 +179,12 @@ async function main() {
116
179
  }
117
180
  switch (command) {
118
181
  case "build":
119
- // Pasar solo los args después de "build"
182
+ // Pasar solo los args después del comando
120
183
  await runBuild(process.argv.slice(3));
121
184
  break;
185
+ case "tokens":
186
+ await runTokens(process.argv.slice(3));
187
+ break;
122
188
  default:
123
189
  console.error(`Comando desconocido: ${command}`);
124
190
  printHelp();
package/lib/defaults.js CHANGED
@@ -4,11 +4,19 @@
4
4
  */
5
5
  const defaults = {
6
6
  columns: 12,
7
+ safelist: [],
7
8
  breakpoints: {
8
9
  tablet: "(min-width: 700px)",
9
10
  desktop: "(min-width: 1024px)",
10
11
  ultrawide: "(min-width: 1680px)",
11
12
  },
13
+ states: {
14
+ hover: ":hover",
15
+ focus: ":focus",
16
+ "focus-visible": ":focus-visible",
17
+ active: ":active",
18
+ disabled: ":disabled",
19
+ },
12
20
  spacing: {
13
21
  auto: "auto",
14
22
  zero: "0",
@@ -1,16 +1,16 @@
1
- import type { Breakpoints, Declarations, TokenMap } from "./types.js";
1
+ import type { Breakpoints, Declarations, States, TokenMap } from "./types.js";
2
2
  /**
3
- * Genera una clase simple con variantes responsive.
3
+ * Genera una clase simple con variantes responsive y de estado.
4
4
  * Equivalente al mixin grisso_simple_class.
5
5
  */
6
- export declare function simpleClass(className: string, property: string, value: string, breakpoints: Breakpoints): string;
6
+ export declare function simpleClass(className: string, property: string, value: string, breakpoints: Breakpoints, states?: States): string;
7
7
  /**
8
- * Genera clases basadas en un mapa de tokens con variantes responsive.
8
+ * Genera clases basadas en un mapa de tokens con variantes responsive y de estado.
9
9
  * Equivalente al mixin grisso_complex_class.
10
10
  */
11
- export declare function complexClass(prefix: string, properties: string | string[], tokens: TokenMap, breakpoints: Breakpoints): string;
11
+ export declare function complexClass(prefix: string, properties: string | string[], tokens: TokenMap, breakpoints: Breakpoints, states?: States): string;
12
12
  /**
13
- * Genera una clase con declaraciones custom y variantes responsive.
13
+ * Genera una clase con declaraciones custom y variantes responsive y de estado.
14
14
  * Para casos especiales: divide >*+*, truncate, smoothing, outline-none, etc.
15
15
  */
16
- export declare function customClass(className: string, declarations: Declarations, breakpoints: Breakpoints, selectorSuffix?: string): string;
16
+ export declare function customClass(className: string, declarations: Declarations, breakpoints: Breakpoints, selectorSuffix?: string, states?: States): string;
package/lib/generators.js CHANGED
@@ -7,22 +7,34 @@ function escapeCSS(name) {
7
7
  return name.replace(/\//g, "\\/");
8
8
  }
9
9
  /**
10
- * Genera una clase simple con variantes responsive.
10
+ * Genera una clase simple con variantes responsive y de estado.
11
11
  * Equivalente al mixin grisso_simple_class.
12
12
  */
13
- export function simpleClass(className, property, value, breakpoints) {
13
+ export function simpleClass(className, property, value, breakpoints, states) {
14
14
  const escaped = escapeCSS(className);
15
15
  let css = `.${escaped} { ${property}: ${value}; }\n`;
16
+ // Variantes de estado
17
+ if (states) {
18
+ for (const [state, pseudo] of Object.entries(states)) {
19
+ css += `.${state}-${escaped}${pseudo} { ${property}: ${value}; }\n`;
20
+ }
21
+ }
16
22
  for (const [bp, mq] of Object.entries(breakpoints)) {
17
23
  css += `@media ${mq} { .${bp}-${escaped} { ${property}: ${value}; } }\n`;
24
+ // Variantes responsive + estado
25
+ if (states) {
26
+ for (const [state, pseudo] of Object.entries(states)) {
27
+ css += `@media ${mq} { .${bp}-${state}-${escaped}${pseudo} { ${property}: ${value}; } }\n`;
28
+ }
29
+ }
18
30
  }
19
31
  return css;
20
32
  }
21
33
  /**
22
- * Genera clases basadas en un mapa de tokens con variantes responsive.
34
+ * Genera clases basadas en un mapa de tokens con variantes responsive y de estado.
23
35
  * Equivalente al mixin grisso_complex_class.
24
36
  */
25
- export function complexClass(prefix, properties, tokens, breakpoints) {
37
+ export function complexClass(prefix, properties, tokens, breakpoints, states) {
26
38
  const props = Array.isArray(properties) ? properties : [properties];
27
39
  let css = "";
28
40
  for (const [name, value] of Object.entries(tokens)) {
@@ -30,27 +42,55 @@ export function complexClass(prefix, properties, tokens, breakpoints) {
30
42
  for (const prop of props) {
31
43
  css += `.${escaped} { ${prop}: ${value}; }\n`;
32
44
  }
45
+ // Variantes de estado
46
+ if (states) {
47
+ for (const [state, pseudo] of Object.entries(states)) {
48
+ for (const prop of props) {
49
+ css += `.${state}-${escaped}${pseudo} { ${prop}: ${value}; }\n`;
50
+ }
51
+ }
52
+ }
33
53
  for (const [bp, mq] of Object.entries(breakpoints)) {
34
54
  for (const prop of props) {
35
55
  css += `@media ${mq} { .${bp}-${escaped} { ${prop}: ${value}; } }\n`;
36
56
  }
57
+ // Variantes responsive + estado
58
+ if (states) {
59
+ for (const [state, pseudo] of Object.entries(states)) {
60
+ for (const prop of props) {
61
+ css += `@media ${mq} { .${bp}-${state}-${escaped}${pseudo} { ${prop}: ${value}; } }\n`;
62
+ }
63
+ }
64
+ }
37
65
  }
38
66
  }
39
67
  return css;
40
68
  }
41
69
  /**
42
- * Genera una clase con declaraciones custom y variantes responsive.
70
+ * Genera una clase con declaraciones custom y variantes responsive y de estado.
43
71
  * Para casos especiales: divide >*+*, truncate, smoothing, outline-none, etc.
44
72
  */
45
- export function customClass(className, declarations, breakpoints, selectorSuffix) {
73
+ export function customClass(className, declarations, breakpoints, selectorSuffix, states) {
46
74
  const escaped = escapeCSS(className);
47
75
  const suffix = selectorSuffix || "";
48
76
  const decls = Object.entries(declarations)
49
77
  .map(([p, v]) => `${p}: ${v}`)
50
78
  .join("; ");
51
79
  let css = `.${escaped}${suffix} { ${decls}; }\n`;
80
+ // Variantes de estado: pseudo va antes del selectorSuffix
81
+ if (states) {
82
+ for (const [state, pseudo] of Object.entries(states)) {
83
+ css += `.${state}-${escaped}${pseudo}${suffix} { ${decls}; }\n`;
84
+ }
85
+ }
52
86
  for (const [bp, mq] of Object.entries(breakpoints)) {
53
87
  css += `@media ${mq} { .${bp}-${escaped}${suffix} { ${decls}; } }\n`;
88
+ // Variantes responsive + estado
89
+ if (states) {
90
+ for (const [state, pseudo] of Object.entries(states)) {
91
+ css += `@media ${mq} { .${bp}-${state}-${escaped}${pseudo}${suffix} { ${decls}; } }\n`;
92
+ }
93
+ }
54
94
  }
55
95
  return css;
56
96
  }
@@ -1,16 +1,16 @@
1
1
  import { complexClass } from "../generators.js";
2
2
  export default function backgrounds(config) {
3
- const { breakpoints, backgroundColors } = config;
3
+ const { breakpoints, states, backgroundColors } = config;
4
4
  let css = "";
5
5
  // background-color
6
- css += complexClass("bg-", "background-color", backgroundColors, breakpoints);
6
+ css += complexClass("bg-", "background-color", backgroundColors, breakpoints, states);
7
7
  // background-attachment
8
8
  const bgAttachment = {
9
9
  fixed: "fixed",
10
10
  local: "local",
11
11
  scroll: "scroll",
12
12
  };
13
- css += complexClass("bg-", "background-attachment", bgAttachment, breakpoints);
13
+ css += complexClass("bg-", "background-attachment", bgAttachment, breakpoints, states);
14
14
  // background-clip
15
15
  const bgClip = {
16
16
  border: "border-box",
@@ -18,14 +18,14 @@ export default function backgrounds(config) {
18
18
  content: "content-box",
19
19
  text: "text",
20
20
  };
21
- css += complexClass("bg-clip-", "background-clip", bgClip, breakpoints);
21
+ css += complexClass("bg-clip-", "background-clip", bgClip, breakpoints, states);
22
22
  // background-origin
23
23
  const bgOrigin = {
24
24
  border: "border-box",
25
25
  padding: "padding-box",
26
26
  content: "content-box",
27
27
  };
28
- css += complexClass("bg-origin-", "background-origin", bgOrigin, breakpoints);
28
+ css += complexClass("bg-origin-", "background-origin", bgOrigin, breakpoints, states);
29
29
  // background-position
30
30
  const bgPosition = {
31
31
  bottom: "bottom",
@@ -38,7 +38,7 @@ export default function backgrounds(config) {
38
38
  "right-top": "right top",
39
39
  top: "top",
40
40
  };
41
- css += complexClass("bg-", "background-position", bgPosition, breakpoints);
41
+ css += complexClass("bg-", "background-position", bgPosition, breakpoints, states);
42
42
  // background-repeat
43
43
  const bgRepeat = {
44
44
  repeat: "repeat",
@@ -48,7 +48,7 @@ export default function backgrounds(config) {
48
48
  "repeat-round": "round",
49
49
  "repeat-space": "space",
50
50
  };
51
- css += complexClass("bg-", "background-repeat", bgRepeat, breakpoints);
51
+ css += complexClass("bg-", "background-repeat", bgRepeat, breakpoints, states);
52
52
  // background-size
53
53
  const bgSize = {
54
54
  auto: "auto",
@@ -59,6 +59,6 @@ export default function backgrounds(config) {
59
59
  revert: "revert",
60
60
  unset: "unset",
61
61
  };
62
- css += complexClass("bg-", "background-size", bgSize, breakpoints);
62
+ css += complexClass("bg-", "background-size", bgSize, breakpoints, states);
63
63
  return css;
64
64
  }
@@ -1,6 +1,6 @@
1
1
  import { complexClass, customClass } from "../generators.js";
2
2
  export default function borders(config) {
3
- const { breakpoints, borderWidth, borderColors } = config;
3
+ const { breakpoints, states, borderWidth, borderColors } = config;
4
4
  let css = "";
5
5
  // Variables locales (equivale a borders/vars.scss)
6
6
  const extendedColors = {
@@ -18,35 +18,35 @@ export default function borders(config) {
18
18
  none: "none",
19
19
  };
20
20
  // border-width
21
- css += complexClass("border-", "border-width", borderWidth, breakpoints);
21
+ css += complexClass("border-", "border-width", borderWidth, breakpoints, states);
22
22
  // border-{side}-width
23
- css += complexClass("border-t-", "border-top-width", borderWidth, breakpoints);
24
- css += complexClass("border-r-", "border-right-width", borderWidth, breakpoints);
25
- css += complexClass("border-b-", "border-bottom-width", borderWidth, breakpoints);
26
- css += complexClass("border-l-", "border-left-width", borderWidth, breakpoints);
23
+ css += complexClass("border-t-", "border-top-width", borderWidth, breakpoints, states);
24
+ css += complexClass("border-r-", "border-right-width", borderWidth, breakpoints, states);
25
+ css += complexClass("border-b-", "border-bottom-width", borderWidth, breakpoints, states);
26
+ css += complexClass("border-l-", "border-left-width", borderWidth, breakpoints, states);
27
27
  // border-color
28
- css += complexClass("border-", "border-color", extendedColors, breakpoints);
28
+ css += complexClass("border-", "border-color", extendedColors, breakpoints, states);
29
29
  // border-style
30
- css += complexClass("border-", "border-style", borderStyle, breakpoints);
30
+ css += complexClass("border-", "border-style", borderStyle, breakpoints, states);
31
31
  // divide-color
32
32
  for (const [key, value] of Object.entries(extendedColors)) {
33
- css += customClass(`divide-${key}`, { "border-color": value }, breakpoints, " > * + *");
33
+ css += customClass(`divide-${key}`, { "border-color": value }, breakpoints, " > * + *", states);
34
34
  }
35
35
  // divide-width
36
36
  for (const [key, value] of Object.entries(borderWidth)) {
37
- css += customClass(`divide-x-${key}`, { "border-right-width": value, "border-left-width": value }, breakpoints, " > * + *");
38
- css += customClass(`divide-y-${key}`, { "border-top-width": value, "border-bottom-width": value }, breakpoints, " > * + *");
37
+ css += customClass(`divide-x-${key}`, { "border-right-width": value, "border-left-width": value }, breakpoints, " > * + *", states);
38
+ css += customClass(`divide-y-${key}`, { "border-top-width": value, "border-bottom-width": value }, breakpoints, " > * + *", states);
39
39
  }
40
- css += customClass("divide-x", { "border-right-width": "0", "border-left-width": "1px" }, breakpoints, " > * + *");
41
- css += customClass("divide-y", { "border-top-width": "1px", "border-bottom-width": "0" }, breakpoints, " > * + *");
40
+ css += customClass("divide-x", { "border-right-width": "0", "border-left-width": "1px" }, breakpoints, " > * + *", states);
41
+ css += customClass("divide-y", { "border-top-width": "1px", "border-bottom-width": "0" }, breakpoints, " > * + *", states);
42
42
  // divide-style
43
43
  for (const [key, value] of Object.entries(borderStyle)) {
44
- css += customClass(`divide-${key}`, { "border-style": value }, breakpoints, " > * + *");
44
+ css += customClass(`divide-${key}`, { "border-style": value }, breakpoints, " > * + *", states);
45
45
  }
46
46
  // outline-color
47
- css += complexClass("outline-", "outline-color", extendedColors, breakpoints);
47
+ css += complexClass("outline-", "outline-color", extendedColors, breakpoints, states);
48
48
  // outline-width
49
- css += complexClass("outline-", "outline-width", borderWidth, breakpoints);
49
+ css += complexClass("outline-", "outline-width", borderWidth, breakpoints, states);
50
50
  // outline-style
51
51
  const outlineStyle = {
52
52
  outline: "solid",
@@ -54,9 +54,9 @@ export default function borders(config) {
54
54
  "outline-dotted": "dotted",
55
55
  "outline-double": "double",
56
56
  };
57
- css += complexClass("", "outline-style", outlineStyle, breakpoints);
58
- css += customClass("outline-none", { outline: "2px solid transparent", "outline-offset": "2px" }, breakpoints);
57
+ css += complexClass("", "outline-style", outlineStyle, breakpoints, states);
58
+ css += customClass("outline-none", { outline: "2px solid transparent", "outline-offset": "2px" }, breakpoints, undefined, states);
59
59
  // outline-offset
60
- css += complexClass("outline-offset-", "outline-offset", borderWidth, breakpoints);
60
+ css += complexClass("outline-offset-", "outline-offset", borderWidth, breakpoints, states);
61
61
  return css;
62
62
  }
@@ -1,17 +1,17 @@
1
1
  import { complexClass } from "../generators.js";
2
2
  export default function effects(config) {
3
- const { breakpoints, opacity, shadows, overlayColors } = config;
3
+ const { breakpoints, states, opacity, shadows, overlayColors } = config;
4
4
  let css = "";
5
5
  // opacity
6
- css += complexClass("opacity-", "opacity", opacity, breakpoints);
6
+ css += complexClass("opacity-", "opacity", opacity, breakpoints, states);
7
7
  // box-shadow
8
8
  const extendedShadows = {
9
9
  ...shadows,
10
10
  inner: "inset 0 2px 4px 0 rgb(0 0 0 / 0.05)",
11
11
  none: "0 0 #0000",
12
12
  };
13
- css += complexClass("shadow-", "box-shadow", extendedShadows, breakpoints);
13
+ css += complexClass("shadow-", "box-shadow", extendedShadows, breakpoints, states);
14
14
  // overlay (background-color)
15
- css += complexClass("overlay-", "background-color", overlayColors, breakpoints);
15
+ css += complexClass("overlay-", "background-color", overlayColors, breakpoints, states);
16
16
  return css;
17
17
  }