holygrail5 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +631 -0
- package/config.json +365 -0
- package/generator.js +58 -0
- package/package.json +48 -0
- package/src/cli-variables.js +147 -0
- package/src/config.js +62 -0
- package/src/dev.js +47 -0
- package/src/guide.js +1798 -0
- package/src/parser.js +624 -0
- package/src/utils.js +74 -0
- package/src/variables-manager.js +248 -0
- package/src/watch.js +88 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
// Gestor de variables CSS
|
|
2
|
+
// Permite detectar variables no usadas y eliminar variables del historial
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
// Carga las variables históricas desde el archivo
|
|
8
|
+
function loadHistoricalVariables(historicalVarsPath = null) {
|
|
9
|
+
const defaultPath = historicalVarsPath || path.join(__dirname, '..', '.data', '.historical-variables.json');
|
|
10
|
+
try {
|
|
11
|
+
if (fs.existsSync(defaultPath)) {
|
|
12
|
+
const content = fs.readFileSync(defaultPath, 'utf8');
|
|
13
|
+
return JSON.parse(content);
|
|
14
|
+
}
|
|
15
|
+
} catch (error) {
|
|
16
|
+
console.error('❌ Error al cargar variables históricas:', error.message);
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
fontFamilyVars: {},
|
|
20
|
+
lineHeightVars: {},
|
|
21
|
+
fontWeightVars: {},
|
|
22
|
+
letterSpacingVars: {},
|
|
23
|
+
textTransformVars: {},
|
|
24
|
+
fontSizeVars: {}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Guarda las variables históricas en el archivo
|
|
29
|
+
function saveHistoricalVariables(variables, historicalVarsPath = null) {
|
|
30
|
+
const defaultPath = historicalVarsPath || path.join(__dirname, '..', '.data', '.historical-variables.json');
|
|
31
|
+
try {
|
|
32
|
+
const dir = path.dirname(defaultPath);
|
|
33
|
+
if (!fs.existsSync(dir)) {
|
|
34
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
fs.writeFileSync(defaultPath, JSON.stringify(variables, null, 2), 'utf8');
|
|
37
|
+
return true;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error('❌ Error al guardar variables históricas:', error.message);
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Extrae todas las variables CSS definidas en :root del CSS generado
|
|
45
|
+
function extractDefinedVariables(cssContent) {
|
|
46
|
+
const variables = new Set();
|
|
47
|
+
// Buscar todas las variables en :root { ... }
|
|
48
|
+
const rootMatch = cssContent.match(/:root\s*\{([^}]+)\}/s);
|
|
49
|
+
if (rootMatch) {
|
|
50
|
+
const rootContent = rootMatch[1];
|
|
51
|
+
// Buscar todas las líneas que contienen --variable-name: value;
|
|
52
|
+
const varRegex = /--[\w-]+/g;
|
|
53
|
+
let match;
|
|
54
|
+
while ((match = varRegex.exec(rootContent)) !== null) {
|
|
55
|
+
variables.add(match[0]);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return variables;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Extrae todas las variables CSS usadas en el CSS generado
|
|
62
|
+
function extractUsedVariables(cssContent) {
|
|
63
|
+
const variables = new Set();
|
|
64
|
+
// Buscar todos los usos de var(--variable-name)
|
|
65
|
+
const varRegex = /var\((--[\w-]+)\)/g;
|
|
66
|
+
let match;
|
|
67
|
+
while ((match = varRegex.exec(cssContent)) !== null) {
|
|
68
|
+
variables.add(match[1]);
|
|
69
|
+
}
|
|
70
|
+
return variables;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Encuentra variables no usadas comparando las definidas con las usadas
|
|
74
|
+
// También incluye variables históricas que no están siendo generadas en el CSS
|
|
75
|
+
function findUnusedVariables(cssContent, historicalVarsPath = null) {
|
|
76
|
+
const defined = extractDefinedVariables(cssContent);
|
|
77
|
+
const used = extractUsedVariables(cssContent);
|
|
78
|
+
const unused = [];
|
|
79
|
+
|
|
80
|
+
// Variables definidas en CSS pero no usadas
|
|
81
|
+
defined.forEach(varName => {
|
|
82
|
+
if (!used.has(varName)) {
|
|
83
|
+
unused.push(varName);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Variables históricas que no están siendo generadas en el CSS
|
|
88
|
+
const historicalVars = loadHistoricalVariables(historicalVarsPath);
|
|
89
|
+
const allHistorical = getAllHistoricalVariables(historicalVars);
|
|
90
|
+
|
|
91
|
+
allHistorical.forEach(varData => {
|
|
92
|
+
const varName = varData.varName;
|
|
93
|
+
// Si la variable histórica no está definida en el CSS y no se usa, es no usada
|
|
94
|
+
if (!defined.has(varName) && !used.has(varName)) {
|
|
95
|
+
// Solo agregar si no está ya en la lista (evitar duplicados)
|
|
96
|
+
if (!unused.includes(varName)) {
|
|
97
|
+
unused.push(varName);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return unused.sort();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Obtiene todas las variables históricas como un array de objetos con información
|
|
106
|
+
function getAllHistoricalVariables(historicalVars) {
|
|
107
|
+
const allVars = [];
|
|
108
|
+
|
|
109
|
+
Object.entries(historicalVars).forEach(([category, vars]) => {
|
|
110
|
+
Object.entries(vars).forEach(([key, varData]) => {
|
|
111
|
+
allVars.push({
|
|
112
|
+
varName: varData.varName,
|
|
113
|
+
value: varData.value,
|
|
114
|
+
category: category.replace('Vars', ''),
|
|
115
|
+
key: key
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return allVars.sort((a, b) => a.varName.localeCompare(b.varName));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Elimina una variable específica del historial
|
|
124
|
+
function removeVariableFromHistory(varName, historicalVarsPath = null) {
|
|
125
|
+
const historicalVars = loadHistoricalVariables(historicalVarsPath);
|
|
126
|
+
let removed = false;
|
|
127
|
+
|
|
128
|
+
// Buscar y eliminar la variable en todas las categorías
|
|
129
|
+
Object.keys(historicalVars).forEach(category => {
|
|
130
|
+
if (historicalVars[category]) {
|
|
131
|
+
Object.keys(historicalVars[category]).forEach(key => {
|
|
132
|
+
if (historicalVars[category][key] && historicalVars[category][key].varName === varName) {
|
|
133
|
+
delete historicalVars[category][key];
|
|
134
|
+
removed = true;
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (removed) {
|
|
141
|
+
saveHistoricalVariables(historicalVars, historicalVarsPath);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return removed;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Elimina múltiples variables del historial
|
|
148
|
+
function removeVariablesFromHistory(varNames, historicalVarsPath = null) {
|
|
149
|
+
const historicalVars = loadHistoricalVariables(historicalVarsPath);
|
|
150
|
+
let removedCount = 0;
|
|
151
|
+
|
|
152
|
+
varNames.forEach(varName => {
|
|
153
|
+
Object.keys(historicalVars).forEach(category => {
|
|
154
|
+
if (historicalVars[category]) {
|
|
155
|
+
Object.keys(historicalVars[category]).forEach(key => {
|
|
156
|
+
if (historicalVars[category][key] && historicalVars[category][key].varName === varName) {
|
|
157
|
+
delete historicalVars[category][key];
|
|
158
|
+
removedCount++;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
if (removedCount > 0) {
|
|
166
|
+
saveHistoricalVariables(historicalVars, historicalVarsPath);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return removedCount;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Función principal para listar variables no usadas
|
|
173
|
+
function listUnusedVariables(cssPath = null, historicalVarsPath = null) {
|
|
174
|
+
const defaultPath = cssPath || path.join(__dirname, '..', 'dist', 'output.css');
|
|
175
|
+
const historicalVarsPathDefault = historicalVarsPath || path.join(__dirname, '..', '.historical-variables.json');
|
|
176
|
+
|
|
177
|
+
if (!fs.existsSync(defaultPath)) {
|
|
178
|
+
console.error(`❌ No se encontró el archivo CSS en: ${defaultPath}`);
|
|
179
|
+
console.log('💡 Ejecuta primero: node generator.js');
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const cssContent = fs.readFileSync(defaultPath, 'utf8');
|
|
184
|
+
const unused = findUnusedVariables(cssContent, historicalVarsPathDefault);
|
|
185
|
+
|
|
186
|
+
return unused;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Función principal para mostrar un reporte de variables
|
|
190
|
+
function showVariablesReport(cssPath = null, historicalVarsPath = null) {
|
|
191
|
+
const cssDefaultPath = cssPath || path.join(__dirname, '..', 'dist', 'output.css');
|
|
192
|
+
const historicalVarsPathDefault = historicalVarsPath || path.join(__dirname, '..', '.historical-variables.json');
|
|
193
|
+
const historicalVars = loadHistoricalVariables(historicalVarsPathDefault);
|
|
194
|
+
|
|
195
|
+
console.log('\n📊 Reporte de Variables CSS\n');
|
|
196
|
+
console.log('═'.repeat(60));
|
|
197
|
+
|
|
198
|
+
// Mostrar variables definidas vs usadas
|
|
199
|
+
if (fs.existsSync(cssDefaultPath)) {
|
|
200
|
+
const cssContent = fs.readFileSync(cssDefaultPath, 'utf8');
|
|
201
|
+
const defined = extractDefinedVariables(cssContent);
|
|
202
|
+
const used = extractUsedVariables(cssContent);
|
|
203
|
+
const unused = findUnusedVariables(cssContent, historicalVarsPathDefault);
|
|
204
|
+
|
|
205
|
+
console.log(`\n📈 Estadísticas:`);
|
|
206
|
+
console.log(` Variables definidas en CSS: ${defined.size}`);
|
|
207
|
+
console.log(` Variables usadas: ${used.size}`);
|
|
208
|
+
console.log(` Variables no usadas: ${unused.length}`);
|
|
209
|
+
|
|
210
|
+
if (unused.length > 0) {
|
|
211
|
+
console.log(`\n⚠️ Variables no usadas (${unused.length}):`);
|
|
212
|
+
unused.forEach((varName, index) => {
|
|
213
|
+
console.log(` ${index + 1}. ${varName}`);
|
|
214
|
+
});
|
|
215
|
+
} else {
|
|
216
|
+
console.log(`\n✅ Todas las variables están en uso`);
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
console.log(`\n⚠️ No se encontró el archivo CSS. Ejecuta primero: node generator.js`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Mostrar variables históricas
|
|
223
|
+
const allHistorical = getAllHistoricalVariables(historicalVars);
|
|
224
|
+
console.log(`\n📚 Variables históricas almacenadas: ${allHistorical.length}`);
|
|
225
|
+
|
|
226
|
+
if (allHistorical.length > 0) {
|
|
227
|
+
console.log(`\n📋 Lista de variables históricas:`);
|
|
228
|
+
allHistorical.forEach((varData, index) => {
|
|
229
|
+
console.log(` ${index + 1}. ${varData.varName} (${varData.category})`);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
console.log('\n' + '═'.repeat(60) + '\n');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
module.exports = {
|
|
237
|
+
loadHistoricalVariables,
|
|
238
|
+
saveHistoricalVariables,
|
|
239
|
+
findUnusedVariables,
|
|
240
|
+
getAllHistoricalVariables,
|
|
241
|
+
removeVariableFromHistory,
|
|
242
|
+
removeVariablesFromHistory,
|
|
243
|
+
listUnusedVariables,
|
|
244
|
+
showVariablesReport,
|
|
245
|
+
extractDefinedVariables,
|
|
246
|
+
extractUsedVariables
|
|
247
|
+
};
|
|
248
|
+
|
package/src/watch.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Modo watch - Detecta cambios en config.json y regenera automáticamente
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { loadConfig } = require('./config');
|
|
6
|
+
const { generateCSS } = require('./parser');
|
|
7
|
+
const { generateHTML } = require('./guide');
|
|
8
|
+
const { writeFile } = require('./utils');
|
|
9
|
+
|
|
10
|
+
// Función para generar CSS y HTML
|
|
11
|
+
function generateFiles(configPath, outputPath, htmlPath) {
|
|
12
|
+
try {
|
|
13
|
+
const configData = loadConfig(configPath);
|
|
14
|
+
|
|
15
|
+
// Generar CSS
|
|
16
|
+
const cssContent = generateCSS(configData);
|
|
17
|
+
writeFile(outputPath, cssContent, 'CSS');
|
|
18
|
+
|
|
19
|
+
// Generar HTML (ajustar ruta del CSS en el HTML si está en carpeta diferente)
|
|
20
|
+
let htmlContent = generateHTML(configData);
|
|
21
|
+
|
|
22
|
+
// Si el HTML y CSS están en carpetas diferentes, ajustar la ruta del CSS
|
|
23
|
+
const outputDir = path.dirname(outputPath);
|
|
24
|
+
const htmlDir = path.dirname(htmlPath);
|
|
25
|
+
|
|
26
|
+
// Si el HTML y CSS están en carpetas diferentes, ajustar la ruta del CSS
|
|
27
|
+
// Si están en la misma carpeta (dist/), usar ruta relativa simple
|
|
28
|
+
if (outputDir !== htmlDir) {
|
|
29
|
+
const relativePath = path.relative(htmlDir, outputDir);
|
|
30
|
+
const cssFileName = path.basename(outputPath);
|
|
31
|
+
const cssRelativePath = path.join(relativePath, cssFileName).replace(/\\/g, '/');
|
|
32
|
+
htmlContent = htmlContent.replace(/href="output\.css[^"]*"/, `href="${cssRelativePath}?v=${Date.now()}"`);
|
|
33
|
+
} else {
|
|
34
|
+
// Si están en la misma carpeta, usar solo el nombre del archivo con timestamp
|
|
35
|
+
htmlContent = htmlContent.replace(/href="output\.css[^"]*"/, `href="output.css?v=${Date.now()}"`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
writeFile(htmlPath, htmlContent, 'HTML');
|
|
39
|
+
|
|
40
|
+
console.log(`\n🎉 Generación completada exitosamente! (${new Date().toLocaleTimeString('es-ES')})\n`);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error('❌ Error:', error.message);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Función principal de watch
|
|
47
|
+
function watch(configPath = path.join(__dirname, '..', 'config.json'), outputPath = path.join(__dirname, '..', 'dist', 'output.css'), htmlPath = path.join(__dirname, '..', 'dist', 'index.html')) {
|
|
48
|
+
console.log('👀 Modo watch activado - Monitoreando cambios en config.json...\n');
|
|
49
|
+
console.log('📝 Presiona Ctrl+C para salir\n');
|
|
50
|
+
console.log('💡 Tip: Abre otro terminal y ejecuta "npm run serve" para levantar el servidor\n');
|
|
51
|
+
|
|
52
|
+
// Generar archivos inicialmente
|
|
53
|
+
generateFiles(configPath, outputPath, htmlPath);
|
|
54
|
+
|
|
55
|
+
// Monitorear cambios en config.json
|
|
56
|
+
let lastModified = fs.statSync(configPath).mtime.getTime();
|
|
57
|
+
|
|
58
|
+
fs.watchFile(configPath, { interval: 500 }, (curr, prev) => {
|
|
59
|
+
const currentModified = curr.mtime.getTime();
|
|
60
|
+
|
|
61
|
+
// Solo regenerar si el archivo realmente cambió
|
|
62
|
+
if (currentModified !== lastModified) {
|
|
63
|
+
lastModified = currentModified;
|
|
64
|
+
console.log('🔄 Detectado cambio en config.json, regenerando...\n');
|
|
65
|
+
generateFiles(configPath, outputPath, htmlPath);
|
|
66
|
+
console.log('✨ Archivos actualizados - Recarga el navegador para ver los cambios\n');
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Manejar cierre del proceso
|
|
71
|
+
process.on('SIGINT', () => {
|
|
72
|
+
console.log('\n\n👋 Modo watch detenido');
|
|
73
|
+
fs.unwatchFile(configPath);
|
|
74
|
+
process.exit(0);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (require.main === module) {
|
|
79
|
+
const args = process.argv.slice(2);
|
|
80
|
+
const configPath = args.find(arg => arg.startsWith('--config='))?.split('=')[1] || path.join(__dirname, '..', 'config.json');
|
|
81
|
+
const outputPath = args.find(arg => arg.startsWith('--output='))?.split('=')[1] || path.join(__dirname, '..', 'dist', 'output.css');
|
|
82
|
+
const htmlPath = args.find(arg => arg.startsWith('--html='))?.split('=')[1] || path.join(__dirname, '..', 'dist', 'index.html');
|
|
83
|
+
|
|
84
|
+
watch(configPath, outputPath, htmlPath);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = { watch, generateFiles };
|
|
88
|
+
|