versacompiler 2.0.8 → 2.2.0
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 +1 -1
- package/dist/compiler/compile.js +2520 -26
- package/dist/compiler/error-reporter.js +467 -38
- package/dist/compiler/linter.js +72 -1
- package/dist/compiler/minify.js +272 -1
- package/dist/compiler/minifyTemplate.js +230 -0
- package/dist/compiler/module-resolution-optimizer.js +844 -1
- package/dist/compiler/parser.js +336 -1
- package/dist/compiler/performance-monitor.js +204 -56
- package/dist/compiler/tailwindcss.js +39 -1
- package/dist/compiler/transform-optimizer.js +392 -1
- package/dist/compiler/transformTStoJS.js +16 -1
- package/dist/compiler/transforms.js +554 -1
- package/dist/compiler/typescript-compiler.js +172 -2
- package/dist/compiler/typescript-error-parser.js +281 -10
- package/dist/compiler/typescript-manager.js +304 -2
- package/dist/compiler/typescript-sync-validator.js +295 -31
- package/dist/compiler/typescript-worker-pool.js +936 -1
- package/dist/compiler/typescript-worker-thread.cjs +466 -41
- package/dist/compiler/typescript-worker.js +339 -1
- package/dist/compiler/vuejs.js +396 -37
- package/dist/hrm/VueHRM.js +359 -1
- package/dist/hrm/errorScreen.js +83 -1
- package/dist/hrm/getInstanciaVue.js +313 -1
- package/dist/hrm/initHRM.js +586 -1
- package/dist/main.js +353 -7
- package/dist/servicios/browserSync.js +587 -5
- package/dist/servicios/file-watcher.js +425 -4
- package/dist/servicios/logger.js +63 -3
- package/dist/servicios/readConfig.js +399 -105
- package/dist/utils/excluded-modules.js +37 -1
- package/dist/utils/module-resolver.js +466 -1
- package/dist/utils/promptUser.js +48 -2
- package/dist/utils/proxyValidator.js +68 -1
- package/dist/utils/resolve-bin.js +58 -1
- package/dist/utils/utils.js +21 -1
- package/dist/utils/vue-types-setup.js +435 -241
- package/dist/wrappers/eslint-node.js +147 -1
- package/dist/wrappers/oxlint-node.js +122 -1
- package/dist/wrappers/tailwind-node.js +94 -1
- package/package.json +39 -42
|
@@ -1,7 +1,589 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { promises as fs } from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import * as process from 'node:process';
|
|
5
|
+
const { env } = process;
|
|
6
|
+
import * as browserSync from 'browser-sync';
|
|
7
|
+
|
|
8
|
+
import getPort from 'get-port';
|
|
9
|
+
import { promptUser } from '../utils/promptUser.js';
|
|
10
|
+
import { getProxyInfo, validateProxyAvailability, } from '../utils/proxyValidator.js';
|
|
11
|
+
import { logger } from './logger.js';
|
|
12
|
+
class BrowserSyncFileCache {
|
|
13
|
+
static instance;
|
|
14
|
+
cache = new Map();
|
|
15
|
+
MAX_CACHE_SIZE = 200; // Máximo archivos en cache
|
|
16
|
+
MAX_CACHE_MEMORY = 50 * 1024 * 1024; // 50MB límite
|
|
17
|
+
CACHE_TTL = 15 * 60 * 1000; // 15 minutos para archivos estáticos
|
|
18
|
+
currentMemoryUsage = 0;
|
|
19
|
+
// ✨ FIX #8: Intervalo de limpieza automática
|
|
20
|
+
cleanupInterval = null;
|
|
21
|
+
// Métricas
|
|
22
|
+
cacheHits = 0;
|
|
23
|
+
cacheMisses = 0;
|
|
24
|
+
totalRequests = 0;
|
|
25
|
+
constructor() {
|
|
26
|
+
// ✨ FIX #8: Iniciar limpieza automática cada 5 minutos
|
|
27
|
+
this.startAutoCleanup();
|
|
28
|
+
}
|
|
29
|
+
static getInstance() {
|
|
30
|
+
if (!BrowserSyncFileCache.instance) {
|
|
31
|
+
BrowserSyncFileCache.instance = new BrowserSyncFileCache();
|
|
32
|
+
}
|
|
33
|
+
return BrowserSyncFileCache.instance;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* ✨ FIX #8: Inicia la limpieza automática de entradas expiradas
|
|
37
|
+
*/
|
|
38
|
+
startAutoCleanup() {
|
|
39
|
+
this.cleanupInterval = setInterval(() => {
|
|
40
|
+
this.cleanupExpiredEntries();
|
|
41
|
+
}, 5 * 60 * 1000); // Cada 5 minutos
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* ✨ FIX #8: Limpia entradas de cache expiradas
|
|
45
|
+
*/
|
|
46
|
+
cleanupExpiredEntries() {
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
let cleaned = 0;
|
|
49
|
+
for (const [filePath, entry] of this.cache.entries()) {
|
|
50
|
+
if (now - entry.lastModified > this.CACHE_TTL) {
|
|
51
|
+
this.currentMemoryUsage -= entry.size;
|
|
52
|
+
this.cache.delete(filePath);
|
|
53
|
+
cleaned++;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (cleaned > 0) {
|
|
57
|
+
console.log(`[BrowserSync] Limpiadas ${cleaned} entradas expiradas del cache`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Genera ETag para el archivo basado en contenido y timestamp
|
|
62
|
+
*/
|
|
63
|
+
generateETag(filePath, stats) {
|
|
64
|
+
const hash = createHash('md5')
|
|
65
|
+
.update(`${filePath}:${stats.mtime.getTime()}:${stats.size}`)
|
|
66
|
+
.digest('hex');
|
|
67
|
+
return `"${hash}"`;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Determina el Content-Type basado en la extensión del archivo
|
|
71
|
+
*/
|
|
72
|
+
getContentType(filePath) {
|
|
73
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
74
|
+
const mimeTypes = {
|
|
75
|
+
'.js': 'application/javascript',
|
|
76
|
+
'.mjs': 'application/javascript',
|
|
77
|
+
'.ts': 'application/javascript', // Se transpila a JS
|
|
78
|
+
'.css': 'text/css',
|
|
79
|
+
'.html': 'text/html',
|
|
80
|
+
'.json': 'application/json',
|
|
81
|
+
'.png': 'image/png',
|
|
82
|
+
'.jpg': 'image/jpeg',
|
|
83
|
+
'.jpeg': 'image/jpeg',
|
|
84
|
+
'.gif': 'image/gif',
|
|
85
|
+
'.svg': 'image/svg+xml',
|
|
86
|
+
'.ico': 'image/x-icon',
|
|
87
|
+
'.webp': 'image/webp',
|
|
88
|
+
'.avif': 'image/avif',
|
|
89
|
+
'.woff': 'font/woff',
|
|
90
|
+
'.woff2': 'font/woff2',
|
|
91
|
+
'.ttf': 'font/ttf',
|
|
92
|
+
'.eot': 'application/vnd.ms-fontobject',
|
|
93
|
+
'.map': 'application/json',
|
|
94
|
+
'.xml': 'application/xml',
|
|
95
|
+
'.txt': 'text/plain',
|
|
96
|
+
'.pdf': 'application/pdf',
|
|
97
|
+
'.zip': 'application/zip',
|
|
98
|
+
'.mp4': 'video/mp4',
|
|
99
|
+
'.mp3': 'audio/mpeg',
|
|
100
|
+
'.wav': 'audio/wav',
|
|
101
|
+
'.ogg': 'audio/ogg',
|
|
102
|
+
};
|
|
103
|
+
return mimeTypes[ext] || 'application/octet-stream';
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Verifica si el archivo debe ser cacheado
|
|
107
|
+
*/
|
|
108
|
+
shouldCache(filePath) {
|
|
109
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
110
|
+
const cacheableExtensions = [
|
|
111
|
+
'.js',
|
|
112
|
+
'.mjs',
|
|
113
|
+
'.css',
|
|
114
|
+
'.json',
|
|
115
|
+
'.png',
|
|
116
|
+
'.jpg',
|
|
117
|
+
'.jpeg',
|
|
118
|
+
'.gif',
|
|
119
|
+
'.svg',
|
|
120
|
+
'.ico',
|
|
121
|
+
'.webp',
|
|
122
|
+
'.avif',
|
|
123
|
+
'.woff',
|
|
124
|
+
'.woff2',
|
|
125
|
+
'.ttf',
|
|
126
|
+
'.eot',
|
|
127
|
+
'.map',
|
|
128
|
+
];
|
|
129
|
+
return cacheableExtensions.includes(ext);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Obtiene archivo desde cache o lo lee del disco
|
|
133
|
+
*/
|
|
134
|
+
async getOrReadFile(filePath) {
|
|
135
|
+
this.totalRequests++;
|
|
136
|
+
try {
|
|
137
|
+
// Verificar si el archivo existe y obtener stats
|
|
138
|
+
const stats = await fs.stat(filePath);
|
|
139
|
+
const lastModified = stats.mtime.getTime();
|
|
140
|
+
const etag = this.generateETag(filePath, stats);
|
|
141
|
+
// Si no debe ser cacheado, leer directamente
|
|
142
|
+
if (!this.shouldCache(filePath)) {
|
|
143
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
144
|
+
return {
|
|
145
|
+
content,
|
|
146
|
+
contentType: this.getContentType(filePath),
|
|
147
|
+
etag,
|
|
148
|
+
cached: false,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
// Verificar cache
|
|
152
|
+
const cached = this.cache.get(filePath);
|
|
153
|
+
if (cached && cached.lastModified === lastModified) {
|
|
154
|
+
this.cacheHits++;
|
|
155
|
+
return {
|
|
156
|
+
content: cached.content,
|
|
157
|
+
contentType: cached.contentType,
|
|
158
|
+
etag: cached.etag,
|
|
159
|
+
cached: true,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
// Cache miss - leer archivo
|
|
163
|
+
this.cacheMisses++;
|
|
164
|
+
const isBinary = this.isBinaryFile(filePath);
|
|
165
|
+
const content = await fs.readFile(filePath, isBinary ? undefined : 'utf-8');
|
|
166
|
+
const contentType = this.getContentType(filePath);
|
|
167
|
+
// Cachear resultado
|
|
168
|
+
this.addToCache(filePath, {
|
|
169
|
+
content,
|
|
170
|
+
contentType,
|
|
171
|
+
lastModified,
|
|
172
|
+
etag,
|
|
173
|
+
size: stats.size,
|
|
174
|
+
});
|
|
175
|
+
return {
|
|
176
|
+
content,
|
|
177
|
+
contentType,
|
|
178
|
+
etag,
|
|
179
|
+
cached: false,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Determina si un archivo es binario
|
|
188
|
+
*/
|
|
189
|
+
isBinaryFile(filePath) {
|
|
190
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
191
|
+
const binaryExtensions = [
|
|
192
|
+
'.png',
|
|
193
|
+
'.jpg',
|
|
194
|
+
'.jpeg',
|
|
195
|
+
'.gif',
|
|
196
|
+
'.ico',
|
|
197
|
+
'.webp',
|
|
198
|
+
'.avif',
|
|
199
|
+
'.woff',
|
|
200
|
+
'.woff2',
|
|
201
|
+
'.ttf',
|
|
202
|
+
'.eot',
|
|
203
|
+
'.pdf',
|
|
204
|
+
'.zip',
|
|
205
|
+
'.mp4',
|
|
206
|
+
'.mp3',
|
|
207
|
+
'.wav',
|
|
208
|
+
'.ogg',
|
|
209
|
+
];
|
|
210
|
+
return binaryExtensions.includes(ext);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Añade archivo al cache con gestión de memoria
|
|
214
|
+
*/
|
|
215
|
+
addToCache(filePath, fileData) {
|
|
216
|
+
try {
|
|
217
|
+
// Aplicar políticas de eviction si es necesario
|
|
218
|
+
this.evictIfNeeded(fileData.size);
|
|
219
|
+
const cacheEntry = {
|
|
220
|
+
content: fileData.content,
|
|
221
|
+
contentType: fileData.contentType,
|
|
222
|
+
lastModified: fileData.lastModified,
|
|
223
|
+
etag: fileData.etag,
|
|
224
|
+
size: fileData.size,
|
|
225
|
+
};
|
|
226
|
+
this.cache.set(filePath, cacheEntry);
|
|
227
|
+
this.currentMemoryUsage += fileData.size;
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
console.warn('[BrowserSyncFileCache] Error cacheando archivo:', error);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Aplica políticas de eviction LRU si es necesario
|
|
235
|
+
*/
|
|
236
|
+
evictIfNeeded(newFileSize) {
|
|
237
|
+
// Verificar límite de archivos
|
|
238
|
+
while (this.cache.size >= this.MAX_CACHE_SIZE) {
|
|
239
|
+
this.evictOldest();
|
|
240
|
+
}
|
|
241
|
+
// Verificar límite de memoria
|
|
242
|
+
while (this.currentMemoryUsage + newFileSize > this.MAX_CACHE_MEMORY &&
|
|
243
|
+
this.cache.size > 0) {
|
|
244
|
+
this.evictOldest();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Elimina el archivo más antiguo del cache
|
|
249
|
+
*/
|
|
250
|
+
evictOldest() {
|
|
251
|
+
let oldestPath = '';
|
|
252
|
+
let oldestTime = Infinity;
|
|
253
|
+
for (const [filePath, entry] of this.cache) {
|
|
254
|
+
if (entry.lastModified < oldestTime) {
|
|
255
|
+
oldestTime = entry.lastModified;
|
|
256
|
+
oldestPath = filePath;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (oldestPath) {
|
|
260
|
+
const entry = this.cache.get(oldestPath);
|
|
261
|
+
if (entry) {
|
|
262
|
+
this.currentMemoryUsage -= entry.size;
|
|
263
|
+
this.cache.delete(oldestPath);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Invalidar cache para un archivo específico
|
|
269
|
+
*/
|
|
270
|
+
invalidateFile(filePath) {
|
|
271
|
+
const entry = this.cache.get(filePath);
|
|
272
|
+
if (entry) {
|
|
273
|
+
this.currentMemoryUsage -= entry.size;
|
|
274
|
+
this.cache.delete(filePath);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Obtiene estadísticas del cache
|
|
279
|
+
*/
|
|
280
|
+
getStats() {
|
|
281
|
+
const hitRate = this.totalRequests > 0
|
|
282
|
+
? Math.round((this.cacheHits / this.totalRequests) * 100)
|
|
283
|
+
: 0;
|
|
284
|
+
return {
|
|
285
|
+
cacheHits: this.cacheHits,
|
|
286
|
+
cacheMisses: this.cacheMisses,
|
|
287
|
+
hitRate,
|
|
288
|
+
totalRequests: this.totalRequests,
|
|
289
|
+
cacheSize: this.cache.size,
|
|
290
|
+
maxCacheSize: this.MAX_CACHE_SIZE,
|
|
291
|
+
memoryUsage: this.currentMemoryUsage,
|
|
292
|
+
maxMemoryUsage: this.MAX_CACHE_MEMORY,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Limpia todo el cache
|
|
297
|
+
*/
|
|
298
|
+
clear() {
|
|
299
|
+
this.cache.clear();
|
|
300
|
+
this.currentMemoryUsage = 0;
|
|
301
|
+
this.cacheHits = 0;
|
|
302
|
+
this.cacheMisses = 0;
|
|
303
|
+
this.totalRequests = 0;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* ✨ FIX #8: Destruye la instancia y limpia recursos
|
|
307
|
+
*/
|
|
308
|
+
terminate() {
|
|
309
|
+
if (this.cleanupInterval) {
|
|
310
|
+
clearInterval(this.cleanupInterval);
|
|
311
|
+
this.cleanupInterval = null;
|
|
312
|
+
}
|
|
313
|
+
this.clear();
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
// Instancia global del cache de archivos
|
|
317
|
+
const fileCache = BrowserSyncFileCache.getInstance();
|
|
318
|
+
// Lazy loading para chalk - pre-cargado para mejor rendimiento
|
|
319
|
+
let chalk;
|
|
320
|
+
let chalkPromise = null;
|
|
321
|
+
async function loadChalk() {
|
|
322
|
+
if (!chalk) {
|
|
323
|
+
if (!chalkPromise) {
|
|
324
|
+
chalkPromise = import('chalk').then(m => {
|
|
325
|
+
chalk = m.default;
|
|
326
|
+
return chalk;
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
await chalkPromise;
|
|
330
|
+
}
|
|
331
|
+
return chalk;
|
|
332
|
+
}
|
|
333
|
+
// Pre-cargar chalk al cargar el módulo para hot reload más rápido
|
|
334
|
+
loadChalk().catch(() => { });
|
|
335
|
+
export async function browserSyncServer() {
|
|
336
|
+
try {
|
|
337
|
+
let bs = null;
|
|
338
|
+
const AssetsOmit = env.AssetsOmit === 'true';
|
|
339
|
+
let proxy = {
|
|
340
|
+
server: './',
|
|
341
|
+
};
|
|
342
|
+
// ✨ VALIDACIÓN DE PROXY: Verificar disponibilidad antes de inicializar BrowserSync
|
|
343
|
+
if (env.proxyUrl) {
|
|
344
|
+
logger.info(`🔍 Validando disponibilidad del servidor proxy: ${env.proxyUrl}`);
|
|
345
|
+
const isProxyAvailable = await validateProxyAvailability(env.proxyUrl, 5000);
|
|
346
|
+
if (!isProxyAvailable) {
|
|
347
|
+
const proxyInfo = getProxyInfo(env.proxyUrl);
|
|
348
|
+
logger.warn(`⚠️ El servidor proxy no está disponible:`);
|
|
349
|
+
logger.warn(` Host: ${proxyInfo.host}`);
|
|
350
|
+
logger.warn(` Puerto: ${proxyInfo.port}`);
|
|
351
|
+
logger.warn(` Protocolo: ${proxyInfo.protocol}`);
|
|
352
|
+
const response = await promptUser('\n¿Desea continuar de todos modos? El modo proxy podría no funcionar correctamente. (s/n): ', 30000);
|
|
353
|
+
if (response.toLowerCase().trim() !== 's' &&
|
|
354
|
+
response.toLowerCase().trim() !== 'si') {
|
|
355
|
+
logger.info('🛑 Operación cancelada por el usuario.');
|
|
356
|
+
process.exit(0);
|
|
357
|
+
}
|
|
358
|
+
logger.warn('⚠️ Continuando con el servidor proxy no disponible...');
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
logger.info('✅ Servidor proxy disponible');
|
|
362
|
+
}
|
|
363
|
+
proxy = {
|
|
364
|
+
proxy: env.proxyUrl,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
const hrmDir = path.join(env.PATH_PROY || process.cwd(), 'hrm');
|
|
368
|
+
const projectRoot = process.cwd();
|
|
369
|
+
const relativeHrmPath = path.relative(projectRoot, hrmDir);
|
|
370
|
+
bs = browserSync.create();
|
|
371
|
+
const port = await getPort({ port: 3000 });
|
|
372
|
+
const uiPort = await getPort({ port: 4000 });
|
|
373
|
+
bs.init({
|
|
374
|
+
...proxy,
|
|
375
|
+
files: [`${env.PATH_DIST}/**/*.css`], // Observa cambios en archivos CSS
|
|
376
|
+
injectChanges: true, // Inyecta CSS sin recargar la página
|
|
377
|
+
open: false, // No abre automáticamente el navegador
|
|
378
|
+
port, // Puerto aleatorio para BrowserSync
|
|
379
|
+
ui: {
|
|
380
|
+
port: uiPort, // Puerto aleatorio para la interfaz de usuario
|
|
381
|
+
},
|
|
382
|
+
socket: {
|
|
383
|
+
path: '/browser-sync/socket.io', // Ruta correcta para socket.io
|
|
384
|
+
},
|
|
385
|
+
snippetOptions: {
|
|
386
|
+
rule: {
|
|
387
|
+
match: /<\/body>/i,
|
|
388
|
+
fn: (snippet, match) => {
|
|
389
|
+
return `
|
|
390
|
+
${snippet}${match}
|
|
4
391
|
<script
|
|
5
392
|
type="module"
|
|
6
|
-
src="/__versa/initHRM.js"
|
|
7
|
-
|
|
393
|
+
src="/__versa/initHRM.js"></script>
|
|
394
|
+
`;
|
|
395
|
+
},
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
logLevel: 'info',
|
|
399
|
+
logPrefix: 'BS',
|
|
400
|
+
logConnections: true,
|
|
401
|
+
logFileChanges: true,
|
|
402
|
+
watchEvents: ['change', 'add', 'unlink', 'addDir', 'unlinkDir'],
|
|
403
|
+
reloadDelay: 500,
|
|
404
|
+
reloadDebounce: 500,
|
|
405
|
+
reloadOnRestart: true,
|
|
406
|
+
notify: true,
|
|
407
|
+
watchOptions: {
|
|
408
|
+
ignoreInitial: true,
|
|
409
|
+
ignored: ['node_modules', '.git'],
|
|
410
|
+
},
|
|
411
|
+
middleware: [
|
|
412
|
+
async function (req, res, next) {
|
|
413
|
+
//para evitar el error de CORS
|
|
414
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
415
|
+
res.setHeader('Access-Control-Allow-Methods', '*');
|
|
416
|
+
res.setHeader('Access-Control-Allow-Headers', '*');
|
|
417
|
+
res.setHeader('Access-Control-Allow-Credentials', 'true');
|
|
418
|
+
res.setHeader('Access-Control-Max-Age', '3600');
|
|
419
|
+
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
|
420
|
+
res.setHeader('Pragma', 'no-cache');
|
|
421
|
+
res.setHeader('Expires', '0');
|
|
422
|
+
//para redigir a la ubicación correcta
|
|
423
|
+
if (req.url === '/__versa/initHRM.js') {
|
|
424
|
+
// ✨ OPTIMIZADO: Usar cache para archivos HRM
|
|
425
|
+
const vueLoaderPath = path.join(relativeHrmPath, '/initHRM.js');
|
|
426
|
+
const cachedFile = await fileCache.getOrReadFile(vueLoaderPath);
|
|
427
|
+
if (cachedFile) {
|
|
428
|
+
res.setHeader('Content-Type', cachedFile.contentType);
|
|
429
|
+
res.setHeader('ETag', cachedFile.etag);
|
|
430
|
+
// if (
|
|
431
|
+
// process.env.VERBOSE === 'true' &&
|
|
432
|
+
// cachedFile.cached
|
|
433
|
+
// ) {
|
|
434
|
+
// logger.info(
|
|
435
|
+
// `🚀 File cache hit para ${vueLoaderPath}`,
|
|
436
|
+
// );
|
|
437
|
+
// }
|
|
438
|
+
res.end(cachedFile.content);
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
const chalkInstance = await loadChalk();
|
|
442
|
+
logger.error(chalkInstance.red(`🚩 :Error al leer el archivo ${vueLoaderPath}`));
|
|
443
|
+
res.statusCode = 404;
|
|
444
|
+
res.end('// vueLoader.js not found');
|
|
445
|
+
}
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
// Si la URL comienza con /__versa/hrm/, sirve los archivos de dist/hrm
|
|
449
|
+
if (req.url.startsWith('/__versa/')) {
|
|
450
|
+
// ✨ OPTIMIZADO: Usar cache para archivos Versa
|
|
451
|
+
const filePath = path.join(relativeHrmPath, req.url.replace('/__versa/', ''));
|
|
452
|
+
const cachedFile = await fileCache.getOrReadFile(filePath);
|
|
453
|
+
if (cachedFile) {
|
|
454
|
+
res.setHeader('Content-Type', cachedFile.contentType);
|
|
455
|
+
res.setHeader('ETag', cachedFile.etag);
|
|
456
|
+
// if (
|
|
457
|
+
// process.env.VERBOSE === 'true' &&
|
|
458
|
+
// cachedFile.cached
|
|
459
|
+
// ) {
|
|
460
|
+
// logger.info(
|
|
461
|
+
// `🚀 File cache hit para ${filePath}`,
|
|
462
|
+
// );
|
|
463
|
+
// }
|
|
464
|
+
res.end(cachedFile.content);
|
|
465
|
+
}
|
|
466
|
+
else {
|
|
467
|
+
const chalkInstance = await loadChalk();
|
|
468
|
+
logger.error(chalkInstance.red(`🚩 :Error al leer el archivo ${filePath}`));
|
|
469
|
+
res.statusCode = 404;
|
|
470
|
+
res.end('// Not found');
|
|
471
|
+
}
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
// Si la URL comienza con /node_modules/, sirve los archivos de node_modules
|
|
475
|
+
if (req.url.startsWith('/node_modules/')) {
|
|
476
|
+
// ✨ OPTIMIZADO: Usar cache para módulos de node_modules
|
|
477
|
+
const modulePath = path.join(process.cwd(), req.url);
|
|
478
|
+
const cachedFile = await fileCache.getOrReadFile(modulePath);
|
|
479
|
+
if (cachedFile) {
|
|
480
|
+
res.setHeader('Content-Type', cachedFile.contentType);
|
|
481
|
+
res.setHeader('ETag', cachedFile.etag);
|
|
482
|
+
// if (
|
|
483
|
+
// process.env.VERBOSE === 'true' &&
|
|
484
|
+
// cachedFile.cached
|
|
485
|
+
// ) {
|
|
486
|
+
// logger.info(
|
|
487
|
+
// `🚀 Module cache hit para ${modulePath}`,
|
|
488
|
+
// );
|
|
489
|
+
// }
|
|
490
|
+
res.end(cachedFile.content);
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
const chalkInstance = await loadChalk();
|
|
494
|
+
logger.error(chalkInstance.red(`🚩 Error al leer el módulo ${modulePath}`));
|
|
495
|
+
res.statusCode = 404;
|
|
496
|
+
res.end('// Module not found');
|
|
497
|
+
}
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
// detectar si es un archivo estático, puede que contenga un . y alguna extensión o dashUsers.js?v=1746559083866
|
|
501
|
+
const isAssets = req.url.match(/\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot|map|webp|avif|json|html|xml|txt|pdf|zip|mp4|mp3|wav|ogg)(\?.*)?$/i);
|
|
502
|
+
if (req.method === 'GET') {
|
|
503
|
+
const chalkInstance = await loadChalk();
|
|
504
|
+
// omitir archivos estáticos sólo si AssetsOmit es true
|
|
505
|
+
if (isAssets && !AssetsOmit) {
|
|
506
|
+
logger.info(chalkInstance.white(`GET: ${req.url}`));
|
|
507
|
+
}
|
|
508
|
+
else if (!isAssets) {
|
|
509
|
+
logger.info(chalkInstance.cyan(`GET: ${req.url}`));
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
else if (req.method === 'POST') {
|
|
513
|
+
const chalkInstance = await loadChalk();
|
|
514
|
+
logger.info(chalkInstance.blue(`POST: ${req.url}`));
|
|
515
|
+
}
|
|
516
|
+
else if (req.method === 'PUT') {
|
|
517
|
+
const chalkInstance = await loadChalk();
|
|
518
|
+
logger.info(chalkInstance.yellow(`PUT: ${req.url}`));
|
|
519
|
+
}
|
|
520
|
+
else if (req.method === 'DELETE') {
|
|
521
|
+
const chalkInstance = await loadChalk();
|
|
522
|
+
logger.info(chalkInstance.red(`DELETE: ${req.url}`));
|
|
523
|
+
}
|
|
524
|
+
else {
|
|
525
|
+
const chalkInstance = await loadChalk();
|
|
526
|
+
logger.info(chalkInstance.gray(`${req.method}: ${req.url}`));
|
|
527
|
+
}
|
|
528
|
+
// Aquí podrías, por ejemplo, escribir estos logs en un archivo o base de datos
|
|
529
|
+
next();
|
|
530
|
+
},
|
|
531
|
+
],
|
|
532
|
+
});
|
|
533
|
+
// 🔍 Listener para errores del cliente
|
|
534
|
+
bs.sockets.on('connection', (socket) => {
|
|
535
|
+
socket.on('client:error', async (errorData) => {
|
|
536
|
+
const chalkInstance = await loadChalk();
|
|
537
|
+
logger.error(chalkInstance.red('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
538
|
+
logger.error(chalkInstance.red.bold('🔥 ERROR DEL CLIENTE (NAVEGADOR)'));
|
|
539
|
+
logger.error(chalkInstance.red('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
|
|
540
|
+
logger.error(chalkInstance.yellow(`📍 Tipo: ${errorData.type}`));
|
|
541
|
+
logger.error(chalkInstance.yellow(`⏰ Timestamp: ${errorData.timestamp}`));
|
|
542
|
+
logger.error(chalkInstance.yellow(`🌐 URL: ${errorData.url}`));
|
|
543
|
+
logger.error(chalkInstance.yellow(`🖥️ User Agent: ${errorData.userAgent}`));
|
|
544
|
+
if (errorData.context &&
|
|
545
|
+
Object.keys(errorData.context).length > 0) {
|
|
546
|
+
logger.error(chalkInstance.cyan('\n📋 Contexto:'));
|
|
547
|
+
logger.error(chalkInstance.cyan(JSON.stringify(errorData.context, null, 2)));
|
|
548
|
+
}
|
|
549
|
+
if (errorData.error) {
|
|
550
|
+
logger.error(chalkInstance.red('\n💥 Error:'));
|
|
551
|
+
logger.error(chalkInstance.red(` Nombre: ${errorData.error.name}`));
|
|
552
|
+
logger.error(chalkInstance.red(` Mensaje: ${errorData.error.message}`));
|
|
553
|
+
if (errorData.error.stack) {
|
|
554
|
+
logger.error(chalkInstance.gray('\n📚 Stack Trace:'));
|
|
555
|
+
logger.error(chalkInstance.gray(errorData.error.stack));
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
logger.error(chalkInstance.red('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
|
|
559
|
+
});
|
|
560
|
+
});
|
|
561
|
+
return bs;
|
|
562
|
+
}
|
|
563
|
+
catch (error) {
|
|
564
|
+
logger.error(`🚩 :Error al iniciar BrowserSync: ${error instanceof Error ? error.message : String(error)}`);
|
|
565
|
+
process.exit(1);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
export async function emitirCambios(bs, action, filePath) {
|
|
569
|
+
// ✨ OPTIMIZACIÓN: Emitir PRIMERO (crítico), logging DESPUÉS (no crítico)
|
|
570
|
+
const normalizedPath = path.normalize(filePath).replace(/\\/g, '/');
|
|
571
|
+
const nameFile = path.basename(normalizedPath, path.extname(normalizedPath));
|
|
572
|
+
bs.sockets.emit(action, { action, filePath, normalizedPath, nameFile });
|
|
573
|
+
// Logging asíncrono para no bloquear la emisión
|
|
574
|
+
setImmediate(async () => {
|
|
575
|
+
const chalkInstance = await loadChalk();
|
|
576
|
+
logger.info(chalkInstance.green(`[HMR] Emitiendo cambios: ${action} ${filePath}\n`));
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
// ✨ NUEVAS FUNCIONES: Exportar funcionalidades del cache de archivos para uso externo
|
|
580
|
+
export const getBrowserSyncCacheStats = () => {
|
|
581
|
+
return fileCache.getStats();
|
|
582
|
+
};
|
|
583
|
+
export const clearBrowserSyncCache = () => {
|
|
584
|
+
fileCache.clear();
|
|
585
|
+
};
|
|
586
|
+
export const invalidateBrowserSyncFile = (filePath) => {
|
|
587
|
+
fileCache.invalidateFile(filePath);
|
|
588
|
+
};
|
|
589
|
+
//# sourceMappingURL=browserSync.js.map
|