versacompiler 2.0.0 → 2.0.1
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 +345 -69
- package/dist/compiler/compile.js +427 -185
- package/dist/compiler/minify.js +199 -10
- package/dist/compiler/module-resolution-optimizer.js +844 -0
- package/dist/compiler/parser.js +179 -6
- package/dist/compiler/performance-monitor.js +192 -0
- package/dist/compiler/transform-optimizer.js +287 -0
- package/dist/compiler/transforms.js +121 -87
- package/dist/compiler/{typescript.js → typescript-compiler.js} +27 -30
- package/dist/compiler/typescript-error-parser.js +6 -7
- package/dist/compiler/typescript-manager.js +378 -0
- package/dist/compiler/typescript-sync-validator.js +13 -15
- package/dist/compiler/typescript-worker-pool.js +479 -0
- package/dist/compiler/typescript-worker.js +51 -21
- package/dist/compiler/vuejs.js +131 -37
- package/dist/main.js +5 -6
- package/dist/servicios/browserSync.js +313 -21
- package/dist/servicios/file-watcher.js +316 -0
- package/dist/servicios/logger.js +1 -0
- package/dist/servicios/readConfig.js +1 -0
- package/dist/utils/module-resolver.js +8 -19
- package/dist/utils/promptUser.js +1 -1
- package/dist/utils/resolve-bin.js +28 -9
- package/package.json +7 -7
- package/dist/servicios/chokidar.js +0 -178
package/dist/compiler/vuejs.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import path from 'node:path';
|
|
2
3
|
import * as vCompiler from 'vue/compiler-sfc';
|
|
3
4
|
import { logger } from '../servicios/logger.js';
|
|
@@ -10,6 +11,123 @@ async function loadChalk() {
|
|
|
10
11
|
}
|
|
11
12
|
return chalk;
|
|
12
13
|
}
|
|
14
|
+
class VueHMRInjectionCache {
|
|
15
|
+
static instance;
|
|
16
|
+
cache = new Map();
|
|
17
|
+
MAX_CACHE_SIZE = 100;
|
|
18
|
+
CACHE_TTL = 5 * 60 * 1000; // 5 minutos
|
|
19
|
+
static getInstance() {
|
|
20
|
+
if (!VueHMRInjectionCache.instance) {
|
|
21
|
+
VueHMRInjectionCache.instance = new VueHMRInjectionCache();
|
|
22
|
+
}
|
|
23
|
+
return VueHMRInjectionCache.instance;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Genera un hash del contenido original para detectar cambios
|
|
27
|
+
*/
|
|
28
|
+
generateContentHash(data) {
|
|
29
|
+
return createHash('md5').update(data).digest('hex');
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Obtiene código HMR inyectado desde cache o lo genera
|
|
33
|
+
*/
|
|
34
|
+
getOrGenerateHMRInjection(originalData, fileName) {
|
|
35
|
+
const contentHash = this.generateContentHash(originalData);
|
|
36
|
+
const cacheKey = `${fileName}:${contentHash}`;
|
|
37
|
+
// Verificar cache
|
|
38
|
+
const cached = this.cache.get(cacheKey);
|
|
39
|
+
if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {
|
|
40
|
+
return {
|
|
41
|
+
injectedData: cached.injectedCode,
|
|
42
|
+
cached: true,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Generar nueva inyección HMR
|
|
46
|
+
const vueImportPattern = /import\s*\{[^}]*\bref\b[^}]*\}\s*from\s*['"]vue['"]/;
|
|
47
|
+
const hasRefImport = vueImportPattern.test(originalData);
|
|
48
|
+
const varContent = `
|
|
49
|
+
${hasRefImport ? '' : 'import { ref } from "/node_modules/vue/dist/vue.esm-browser.js";'}
|
|
50
|
+
const versaComponentKey = ref(0);
|
|
51
|
+
`;
|
|
52
|
+
let injectedData;
|
|
53
|
+
const ifExistScript = originalData.includes('<script');
|
|
54
|
+
if (!ifExistScript) {
|
|
55
|
+
injectedData =
|
|
56
|
+
`<script setup lang="ts">${varContent}</script>/n` +
|
|
57
|
+
originalData;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
injectedData = originalData.replace(/(<script.*?>)/, `$1${varContent}`);
|
|
61
|
+
}
|
|
62
|
+
// Inyectar :key en el template
|
|
63
|
+
injectedData = injectedData.replace(/(<template[^>]*>[\s\S]*?)(<(\w+)([^>]*?))(\/?>)/, (match, p1, p2, p3, p4, p5) => {
|
|
64
|
+
if (p4.includes(':key=') || p4.includes('key=')) {
|
|
65
|
+
return match;
|
|
66
|
+
}
|
|
67
|
+
const isSelfClosing = p5 === '/>';
|
|
68
|
+
if (isSelfClosing) {
|
|
69
|
+
return `${p1}<${p3}${p4} :key="versaComponentKey" />`;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
return `${p1}<${p3}${p4} :key="versaComponentKey">`;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
// Cachear resultado
|
|
76
|
+
this.cache.set(cacheKey, {
|
|
77
|
+
contentHash,
|
|
78
|
+
injectedCode: injectedData,
|
|
79
|
+
hasRefImport,
|
|
80
|
+
timestamp: Date.now(),
|
|
81
|
+
});
|
|
82
|
+
// Limpiar cache si es necesario
|
|
83
|
+
this.evictIfNeeded();
|
|
84
|
+
return {
|
|
85
|
+
injectedData,
|
|
86
|
+
cached: false,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Limpia entradas de cache cuando se excede el límite
|
|
91
|
+
*/
|
|
92
|
+
evictIfNeeded() {
|
|
93
|
+
if (this.cache.size <= this.MAX_CACHE_SIZE)
|
|
94
|
+
return;
|
|
95
|
+
const entries = Array.from(this.cache.entries());
|
|
96
|
+
entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
|
|
97
|
+
// Eliminar las entradas más antiguas
|
|
98
|
+
const toDelete = entries.slice(0, entries.length - this.MAX_CACHE_SIZE);
|
|
99
|
+
toDelete.forEach(([key]) => this.cache.delete(key));
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Limpia entradas expiradas
|
|
103
|
+
*/
|
|
104
|
+
cleanExpired() {
|
|
105
|
+
const now = Date.now();
|
|
106
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
107
|
+
if (now - entry.timestamp > this.CACHE_TTL) {
|
|
108
|
+
this.cache.delete(key);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Obtiene estadísticas del cache
|
|
114
|
+
*/
|
|
115
|
+
getStats() {
|
|
116
|
+
return {
|
|
117
|
+
size: this.cache.size,
|
|
118
|
+
maxSize: this.MAX_CACHE_SIZE,
|
|
119
|
+
ttl: this.CACHE_TTL,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Limpia todo el cache
|
|
124
|
+
*/
|
|
125
|
+
clear() {
|
|
126
|
+
this.cache.clear();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Instancia global del cache HMR
|
|
130
|
+
const hmrInjectionCache = VueHMRInjectionCache.getInstance();
|
|
13
131
|
const getComponentsVueMap = async (ast) => {
|
|
14
132
|
let components = [];
|
|
15
133
|
const importsStatic = ast?.module?.staticImports;
|
|
@@ -22,12 +140,6 @@ const getComponentsVueMap = async (ast) => {
|
|
|
22
140
|
}
|
|
23
141
|
return components;
|
|
24
142
|
};
|
|
25
|
-
/**
|
|
26
|
-
* Compila un bloque personalizado.
|
|
27
|
-
* @param {Object} block - El bloque personalizado a compilar.
|
|
28
|
-
* @param {string} source - La fuente del bloque.
|
|
29
|
-
*/
|
|
30
|
-
const _compileCustomBlock = async (_block, _source) => { };
|
|
31
143
|
/**
|
|
32
144
|
* Precompila un componente Vue.
|
|
33
145
|
* @param {string} data - El código del componente Vue.
|
|
@@ -45,36 +157,8 @@ export const preCompileVue = async (data, source, isProd = false) => {
|
|
|
45
157
|
};
|
|
46
158
|
}
|
|
47
159
|
if (!isProd) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const hasRefImport = vueImportPattern.test(data);
|
|
51
|
-
// esto es para HMR re re forzado
|
|
52
|
-
const varContent = `
|
|
53
|
-
${hasRefImport ? '' : 'import { ref } from "/node_modules/vue/dist/vue.esm-browser.js";'}
|
|
54
|
-
const versaComponentKey = ref(0);
|
|
55
|
-
`;
|
|
56
|
-
const ifExistScript = data.includes('<script');
|
|
57
|
-
if (!ifExistScript) {
|
|
58
|
-
data =
|
|
59
|
-
`<script setup lang="ts">${varContent}</script>/n` + data;
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
data = data.replace(/(<script.*?>)/, `$1${varContent}`);
|
|
63
|
-
}
|
|
64
|
-
data = data.replace(/(<template[^>]*>[\s\S]*?)(<(\w+)([^>]*?))(\/?>)/, (match, p1, p2, p3, p4, p5) => {
|
|
65
|
-
// Si ya tiene :key, no agregarlo de nuevo
|
|
66
|
-
if (p4.includes(':key=') || p4.includes('key=')) {
|
|
67
|
-
return match;
|
|
68
|
-
}
|
|
69
|
-
// Si es self-closing (termina con '/>'), manejar diferente
|
|
70
|
-
const isSelfClosing = p5 === '/>';
|
|
71
|
-
if (isSelfClosing) {
|
|
72
|
-
return `${p1}<${p3}${p4} :key="versaComponentKey" />`;
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
return `${p1}<${p3}${p4} :key="versaComponentKey">`;
|
|
76
|
-
}
|
|
77
|
-
});
|
|
160
|
+
const { injectedData } = hmrInjectionCache.getOrGenerateHMRInjection(data, fileName);
|
|
161
|
+
data = injectedData;
|
|
78
162
|
}
|
|
79
163
|
const { descriptor, errors } = vCompiler.parse(data, {
|
|
80
164
|
filename: fileName,
|
|
@@ -132,7 +216,7 @@ export const preCompileVue = async (data, source, isProd = false) => {
|
|
|
132
216
|
const ast = await parser(`temp.${scriptLang}`, scriptContent, scriptLang);
|
|
133
217
|
if (ast?.errors.length > 0) {
|
|
134
218
|
throw new Error(`Error al analizar el script del componente Vue ${source}:\n${ast.errors
|
|
135
|
-
.map(e => e.message)
|
|
219
|
+
.map((e) => e.message)
|
|
136
220
|
.join('\n')}`);
|
|
137
221
|
}
|
|
138
222
|
const components = await getComponentsVueMap(ast);
|
|
@@ -293,4 +377,14 @@ export const preCompileVue = async (data, source, isProd = false) => {
|
|
|
293
377
|
};
|
|
294
378
|
}
|
|
295
379
|
};
|
|
380
|
+
// ✨ NUEVA FUNCIÓN: Exportar funcionalidades del cache HMR para uso externo
|
|
381
|
+
export const getVueHMRCacheStats = () => {
|
|
382
|
+
return hmrInjectionCache.getStats();
|
|
383
|
+
};
|
|
384
|
+
export const clearVueHMRCache = () => {
|
|
385
|
+
hmrInjectionCache.clear();
|
|
386
|
+
};
|
|
387
|
+
export const cleanExpiredVueHMRCache = () => {
|
|
388
|
+
hmrInjectionCache.cleanExpired();
|
|
389
|
+
};
|
|
296
390
|
//# sourceMappingURL=vuejs.js.map
|
package/dist/main.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import path from 'node:path'; // Importar el módulo path
|
|
3
|
-
import { env } from 'node:process';
|
|
3
|
+
import process, { env } from 'node:process';
|
|
4
4
|
// Lazy loading optimizations - Only import lightweight modules synchronously
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { logger } from './servicios/logger.js';
|
|
@@ -35,7 +35,7 @@ async function loadBrowserSyncModule() {
|
|
|
35
35
|
return await import('./servicios/browserSync.js');
|
|
36
36
|
}
|
|
37
37
|
async function loadChokidarModule() {
|
|
38
|
-
return await import('./servicios/
|
|
38
|
+
return await import('./servicios/file-watcher.js');
|
|
39
39
|
}
|
|
40
40
|
async function loadConfigModule() {
|
|
41
41
|
return await import('./servicios/readConfig.js');
|
|
@@ -100,7 +100,7 @@ async function main() {
|
|
|
100
100
|
.alias('y', 'yes')
|
|
101
101
|
.option('typeCheck', {
|
|
102
102
|
type: 'boolean',
|
|
103
|
-
description: 'Habilitar/Deshabilitar la verificación de tipos. Por defecto --typeCheck=
|
|
103
|
+
description: 'Habilitar/Deshabilitar la verificación de tipos. Por defecto --typeCheck=false',
|
|
104
104
|
default: false,
|
|
105
105
|
})
|
|
106
106
|
.alias('t', 'typeCheck');
|
|
@@ -199,8 +199,7 @@ async function main() {
|
|
|
199
199
|
}
|
|
200
200
|
if (argv.file) {
|
|
201
201
|
// Compilar archivo individual
|
|
202
|
-
logger.info(chalk.yellow(`📄 Compilando archivo: ${argv.file}`));
|
|
203
|
-
// Verificar si el archivo existe
|
|
202
|
+
logger.info(chalk.yellow(`📄 Compilando archivo: ${argv.file}`)); // Verificar si el archivo existe
|
|
204
203
|
const fs = await import('node:fs/promises');
|
|
205
204
|
const { compileFile } = await loadCompilerModule();
|
|
206
205
|
let absolutePathFile;
|
|
@@ -212,7 +211,7 @@ async function main() {
|
|
|
212
211
|
logger.error(chalk.red(`❌ Error: El archivo '${argv.file}' no existe.`));
|
|
213
212
|
process.exit(1);
|
|
214
213
|
}
|
|
215
|
-
// Compilar el archivo
|
|
214
|
+
// Compilar el archivo (absolutePathFile está garantizado aquí)
|
|
216
215
|
const result = await compileFile(absolutePathFile);
|
|
217
216
|
if (result.success) {
|
|
218
217
|
logger.info(chalk.green(`✅ Archivo compilado exitosamente: ${result.output}`));
|
|
@@ -1,10 +1,276 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import { promises as fs } from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
|
-
import { env } from 'node:process';
|
|
4
|
+
import process, { env } from 'node:process';
|
|
4
5
|
import browserSync from 'browser-sync';
|
|
5
6
|
|
|
6
7
|
import getPort from 'get-port';
|
|
7
8
|
import { logger } from './logger.js';
|
|
9
|
+
class BrowserSyncFileCache {
|
|
10
|
+
static instance;
|
|
11
|
+
cache = new Map();
|
|
12
|
+
MAX_CACHE_SIZE = 200; // Máximo archivos en cache
|
|
13
|
+
MAX_CACHE_MEMORY = 50 * 1024 * 1024; // 50MB límite
|
|
14
|
+
CACHE_TTL = 15 * 60 * 1000; // 15 minutos para archivos estáticos
|
|
15
|
+
currentMemoryUsage = 0;
|
|
16
|
+
// Métricas
|
|
17
|
+
cacheHits = 0;
|
|
18
|
+
cacheMisses = 0;
|
|
19
|
+
totalRequests = 0;
|
|
20
|
+
static getInstance() {
|
|
21
|
+
if (!BrowserSyncFileCache.instance) {
|
|
22
|
+
BrowserSyncFileCache.instance = new BrowserSyncFileCache();
|
|
23
|
+
}
|
|
24
|
+
return BrowserSyncFileCache.instance;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Genera ETag para el archivo basado en contenido y timestamp
|
|
28
|
+
*/
|
|
29
|
+
generateETag(filePath, stats) {
|
|
30
|
+
const hash = createHash('md5')
|
|
31
|
+
.update(`${filePath}:${stats.mtime.getTime()}:${stats.size}`)
|
|
32
|
+
.digest('hex');
|
|
33
|
+
return `"${hash}"`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Determina el Content-Type basado en la extensión del archivo
|
|
37
|
+
*/
|
|
38
|
+
getContentType(filePath) {
|
|
39
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
40
|
+
const mimeTypes = {
|
|
41
|
+
'.js': 'application/javascript',
|
|
42
|
+
'.mjs': 'application/javascript',
|
|
43
|
+
'.ts': 'application/javascript', // Se transpila a JS
|
|
44
|
+
'.css': 'text/css',
|
|
45
|
+
'.html': 'text/html',
|
|
46
|
+
'.json': 'application/json',
|
|
47
|
+
'.png': 'image/png',
|
|
48
|
+
'.jpg': 'image/jpeg',
|
|
49
|
+
'.jpeg': 'image/jpeg',
|
|
50
|
+
'.gif': 'image/gif',
|
|
51
|
+
'.svg': 'image/svg+xml',
|
|
52
|
+
'.ico': 'image/x-icon',
|
|
53
|
+
'.webp': 'image/webp',
|
|
54
|
+
'.avif': 'image/avif',
|
|
55
|
+
'.woff': 'font/woff',
|
|
56
|
+
'.woff2': 'font/woff2',
|
|
57
|
+
'.ttf': 'font/ttf',
|
|
58
|
+
'.eot': 'application/vnd.ms-fontobject',
|
|
59
|
+
'.map': 'application/json',
|
|
60
|
+
'.xml': 'application/xml',
|
|
61
|
+
'.txt': 'text/plain',
|
|
62
|
+
'.pdf': 'application/pdf',
|
|
63
|
+
'.zip': 'application/zip',
|
|
64
|
+
'.mp4': 'video/mp4',
|
|
65
|
+
'.mp3': 'audio/mpeg',
|
|
66
|
+
'.wav': 'audio/wav',
|
|
67
|
+
'.ogg': 'audio/ogg',
|
|
68
|
+
};
|
|
69
|
+
return mimeTypes[ext] || 'application/octet-stream';
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Verifica si el archivo debe ser cacheado
|
|
73
|
+
*/
|
|
74
|
+
shouldCache(filePath) {
|
|
75
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
76
|
+
const cacheableExtensions = [
|
|
77
|
+
'.js',
|
|
78
|
+
'.mjs',
|
|
79
|
+
'.css',
|
|
80
|
+
'.json',
|
|
81
|
+
'.png',
|
|
82
|
+
'.jpg',
|
|
83
|
+
'.jpeg',
|
|
84
|
+
'.gif',
|
|
85
|
+
'.svg',
|
|
86
|
+
'.ico',
|
|
87
|
+
'.webp',
|
|
88
|
+
'.avif',
|
|
89
|
+
'.woff',
|
|
90
|
+
'.woff2',
|
|
91
|
+
'.ttf',
|
|
92
|
+
'.eot',
|
|
93
|
+
'.map',
|
|
94
|
+
];
|
|
95
|
+
return cacheableExtensions.includes(ext);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Obtiene archivo desde cache o lo lee del disco
|
|
99
|
+
*/
|
|
100
|
+
async getOrReadFile(filePath) {
|
|
101
|
+
this.totalRequests++;
|
|
102
|
+
try {
|
|
103
|
+
// Verificar si el archivo existe y obtener stats
|
|
104
|
+
const stats = await fs.stat(filePath);
|
|
105
|
+
const lastModified = stats.mtime.getTime();
|
|
106
|
+
const etag = this.generateETag(filePath, stats);
|
|
107
|
+
// Si no debe ser cacheado, leer directamente
|
|
108
|
+
if (!this.shouldCache(filePath)) {
|
|
109
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
110
|
+
return {
|
|
111
|
+
content,
|
|
112
|
+
contentType: this.getContentType(filePath),
|
|
113
|
+
etag,
|
|
114
|
+
cached: false,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
// Verificar cache
|
|
118
|
+
const cached = this.cache.get(filePath);
|
|
119
|
+
if (cached && cached.lastModified === lastModified) {
|
|
120
|
+
this.cacheHits++;
|
|
121
|
+
return {
|
|
122
|
+
content: cached.content,
|
|
123
|
+
contentType: cached.contentType,
|
|
124
|
+
etag: cached.etag,
|
|
125
|
+
cached: true,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
// Cache miss - leer archivo
|
|
129
|
+
this.cacheMisses++;
|
|
130
|
+
const isBinary = this.isBinaryFile(filePath);
|
|
131
|
+
const content = await fs.readFile(filePath, isBinary ? undefined : 'utf-8');
|
|
132
|
+
const contentType = this.getContentType(filePath);
|
|
133
|
+
// Cachear resultado
|
|
134
|
+
this.addToCache(filePath, {
|
|
135
|
+
content,
|
|
136
|
+
contentType,
|
|
137
|
+
lastModified,
|
|
138
|
+
etag,
|
|
139
|
+
size: stats.size,
|
|
140
|
+
});
|
|
141
|
+
return {
|
|
142
|
+
content,
|
|
143
|
+
contentType,
|
|
144
|
+
etag,
|
|
145
|
+
cached: false,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Determina si un archivo es binario
|
|
154
|
+
*/
|
|
155
|
+
isBinaryFile(filePath) {
|
|
156
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
157
|
+
const binaryExtensions = [
|
|
158
|
+
'.png',
|
|
159
|
+
'.jpg',
|
|
160
|
+
'.jpeg',
|
|
161
|
+
'.gif',
|
|
162
|
+
'.ico',
|
|
163
|
+
'.webp',
|
|
164
|
+
'.avif',
|
|
165
|
+
'.woff',
|
|
166
|
+
'.woff2',
|
|
167
|
+
'.ttf',
|
|
168
|
+
'.eot',
|
|
169
|
+
'.pdf',
|
|
170
|
+
'.zip',
|
|
171
|
+
'.mp4',
|
|
172
|
+
'.mp3',
|
|
173
|
+
'.wav',
|
|
174
|
+
'.ogg',
|
|
175
|
+
];
|
|
176
|
+
return binaryExtensions.includes(ext);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Añade archivo al cache con gestión de memoria
|
|
180
|
+
*/
|
|
181
|
+
addToCache(filePath, fileData) {
|
|
182
|
+
try {
|
|
183
|
+
// Aplicar políticas de eviction si es necesario
|
|
184
|
+
this.evictIfNeeded(fileData.size);
|
|
185
|
+
const cacheEntry = {
|
|
186
|
+
content: fileData.content,
|
|
187
|
+
contentType: fileData.contentType,
|
|
188
|
+
lastModified: fileData.lastModified,
|
|
189
|
+
etag: fileData.etag,
|
|
190
|
+
size: fileData.size,
|
|
191
|
+
};
|
|
192
|
+
this.cache.set(filePath, cacheEntry);
|
|
193
|
+
this.currentMemoryUsage += fileData.size;
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
console.warn('[BrowserSyncFileCache] Error cacheando archivo:', error);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Aplica políticas de eviction LRU si es necesario
|
|
201
|
+
*/
|
|
202
|
+
evictIfNeeded(newFileSize) {
|
|
203
|
+
// Verificar límite de archivos
|
|
204
|
+
while (this.cache.size >= this.MAX_CACHE_SIZE) {
|
|
205
|
+
this.evictOldest();
|
|
206
|
+
}
|
|
207
|
+
// Verificar límite de memoria
|
|
208
|
+
while (this.currentMemoryUsage + newFileSize > this.MAX_CACHE_MEMORY &&
|
|
209
|
+
this.cache.size > 0) {
|
|
210
|
+
this.evictOldest();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Elimina el archivo más antiguo del cache
|
|
215
|
+
*/
|
|
216
|
+
evictOldest() {
|
|
217
|
+
let oldestPath = '';
|
|
218
|
+
let oldestTime = Infinity;
|
|
219
|
+
for (const [filePath, entry] of this.cache) {
|
|
220
|
+
if (entry.lastModified < oldestTime) {
|
|
221
|
+
oldestTime = entry.lastModified;
|
|
222
|
+
oldestPath = filePath;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (oldestPath) {
|
|
226
|
+
const entry = this.cache.get(oldestPath);
|
|
227
|
+
if (entry) {
|
|
228
|
+
this.currentMemoryUsage -= entry.size;
|
|
229
|
+
this.cache.delete(oldestPath);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Invalidar cache para un archivo específico
|
|
235
|
+
*/
|
|
236
|
+
invalidateFile(filePath) {
|
|
237
|
+
const entry = this.cache.get(filePath);
|
|
238
|
+
if (entry) {
|
|
239
|
+
this.currentMemoryUsage -= entry.size;
|
|
240
|
+
this.cache.delete(filePath);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Obtiene estadísticas del cache
|
|
245
|
+
*/
|
|
246
|
+
getStats() {
|
|
247
|
+
const hitRate = this.totalRequests > 0
|
|
248
|
+
? Math.round((this.cacheHits / this.totalRequests) * 100)
|
|
249
|
+
: 0;
|
|
250
|
+
return {
|
|
251
|
+
cacheHits: this.cacheHits,
|
|
252
|
+
cacheMisses: this.cacheMisses,
|
|
253
|
+
hitRate,
|
|
254
|
+
totalRequests: this.totalRequests,
|
|
255
|
+
cacheSize: this.cache.size,
|
|
256
|
+
maxCacheSize: this.MAX_CACHE_SIZE,
|
|
257
|
+
memoryUsage: this.currentMemoryUsage,
|
|
258
|
+
maxMemoryUsage: this.MAX_CACHE_MEMORY,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Limpia todo el cache
|
|
263
|
+
*/
|
|
264
|
+
clear() {
|
|
265
|
+
this.cache.clear();
|
|
266
|
+
this.currentMemoryUsage = 0;
|
|
267
|
+
this.cacheHits = 0;
|
|
268
|
+
this.cacheMisses = 0;
|
|
269
|
+
this.totalRequests = 0;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Instancia global del cache de archivos
|
|
273
|
+
const fileCache = BrowserSyncFileCache.getInstance();
|
|
8
274
|
// Lazy loading para chalk
|
|
9
275
|
const loadChalk = async () => {
|
|
10
276
|
const { default: chalk } = await import('chalk');
|
|
@@ -79,16 +345,21 @@ export async function browserSyncServer() {
|
|
|
79
345
|
res.setHeader('Expires', '0');
|
|
80
346
|
//para redigir a la ubicación correcta
|
|
81
347
|
if (req.url === '/__versa/initHRM.js') {
|
|
82
|
-
//
|
|
348
|
+
// ✨ OPTIMIZADO: Usar cache para archivos HRM
|
|
83
349
|
const vueLoaderPath = path.join(relativeHrmPath, '/initHRM.js');
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
res.
|
|
350
|
+
const cachedFile = await fileCache.getOrReadFile(vueLoaderPath);
|
|
351
|
+
if (cachedFile) {
|
|
352
|
+
res.setHeader('Content-Type', cachedFile.contentType);
|
|
353
|
+
res.setHeader('ETag', cachedFile.etag);
|
|
354
|
+
if (process.env.VERBOSE === 'true' &&
|
|
355
|
+
cachedFile.cached) {
|
|
356
|
+
logger.info(`🚀 File cache hit para ${vueLoaderPath}`);
|
|
357
|
+
}
|
|
358
|
+
res.end(cachedFile.content);
|
|
88
359
|
}
|
|
89
|
-
|
|
360
|
+
else {
|
|
90
361
|
const chalkInstance = await loadChalk();
|
|
91
|
-
logger.error(chalkInstance.red(`🚩 :Error al leer el archivo ${vueLoaderPath}
|
|
362
|
+
logger.error(chalkInstance.red(`🚩 :Error al leer el archivo ${vueLoaderPath}`));
|
|
92
363
|
res.statusCode = 404;
|
|
93
364
|
res.end('// vueLoader.js not found');
|
|
94
365
|
}
|
|
@@ -96,16 +367,21 @@ export async function browserSyncServer() {
|
|
|
96
367
|
}
|
|
97
368
|
// Si la URL comienza con /__versa/hrm/, sirve los archivos de dist/hrm
|
|
98
369
|
if (req.url.startsWith('/__versa/')) {
|
|
99
|
-
//
|
|
370
|
+
// ✨ OPTIMIZADO: Usar cache para archivos Versa
|
|
100
371
|
const filePath = path.join(relativeHrmPath, req.url.replace('/__versa/', ''));
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
res.
|
|
372
|
+
const cachedFile = await fileCache.getOrReadFile(filePath);
|
|
373
|
+
if (cachedFile) {
|
|
374
|
+
res.setHeader('Content-Type', cachedFile.contentType);
|
|
375
|
+
res.setHeader('ETag', cachedFile.etag);
|
|
376
|
+
if (process.env.VERBOSE === 'true' &&
|
|
377
|
+
cachedFile.cached) {
|
|
378
|
+
logger.info(`🚀 File cache hit para ${filePath}`);
|
|
379
|
+
}
|
|
380
|
+
res.end(cachedFile.content);
|
|
105
381
|
}
|
|
106
|
-
|
|
382
|
+
else {
|
|
107
383
|
const chalkInstance = await loadChalk();
|
|
108
|
-
logger.error(chalkInstance.red(`🚩 :Error al leer el archivo ${filePath}
|
|
384
|
+
logger.error(chalkInstance.red(`🚩 :Error al leer el archivo ${filePath}`));
|
|
109
385
|
res.statusCode = 404;
|
|
110
386
|
res.end('// Not found');
|
|
111
387
|
}
|
|
@@ -113,15 +389,21 @@ export async function browserSyncServer() {
|
|
|
113
389
|
}
|
|
114
390
|
// Si la URL comienza con /node_modules/, sirve los archivos de node_modules
|
|
115
391
|
if (req.url.startsWith('/node_modules/')) {
|
|
392
|
+
// ✨ OPTIMIZADO: Usar cache para módulos de node_modules
|
|
116
393
|
const modulePath = path.join(process.cwd(), req.url);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
res.
|
|
394
|
+
const cachedFile = await fileCache.getOrReadFile(modulePath);
|
|
395
|
+
if (cachedFile) {
|
|
396
|
+
res.setHeader('Content-Type', cachedFile.contentType);
|
|
397
|
+
res.setHeader('ETag', cachedFile.etag);
|
|
398
|
+
if (process.env.VERBOSE === 'true' &&
|
|
399
|
+
cachedFile.cached) {
|
|
400
|
+
logger.info(`🚀 Module cache hit para ${modulePath}`);
|
|
401
|
+
}
|
|
402
|
+
res.end(cachedFile.content);
|
|
121
403
|
}
|
|
122
|
-
|
|
404
|
+
else {
|
|
123
405
|
const chalkInstance = await loadChalk();
|
|
124
|
-
logger.error(chalkInstance.red(`🚩 Error al leer el módulo ${modulePath}
|
|
406
|
+
logger.error(chalkInstance.red(`🚩 Error al leer el módulo ${modulePath}`));
|
|
125
407
|
res.statusCode = 404;
|
|
126
408
|
res.end('// Module not found');
|
|
127
409
|
}
|
|
@@ -174,4 +456,14 @@ export async function emitirCambios(bs, action, filePath) {
|
|
|
174
456
|
const nameFile = path.basename(normalizedPath, path.extname(normalizedPath));
|
|
175
457
|
bs.sockets.emit(action, { action, filePath, normalizedPath, nameFile });
|
|
176
458
|
}
|
|
459
|
+
// ✨ NUEVAS FUNCIONES: Exportar funcionalidades del cache de archivos para uso externo
|
|
460
|
+
export const getBrowserSyncCacheStats = () => {
|
|
461
|
+
return fileCache.getStats();
|
|
462
|
+
};
|
|
463
|
+
export const clearBrowserSyncCache = () => {
|
|
464
|
+
fileCache.clear();
|
|
465
|
+
};
|
|
466
|
+
export const invalidateBrowserSyncFile = (filePath) => {
|
|
467
|
+
fileCache.invalidateFile(filePath);
|
|
468
|
+
};
|
|
177
469
|
//# sourceMappingURL=browserSync.js.map
|