versacompiler 2.0.0 → 2.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 +345 -69
- package/dist/compiler/compile.js +762 -183
- package/dist/compiler/minify.js +199 -10
- package/dist/compiler/module-resolution-optimizer.js +822 -0
- package/dist/compiler/parser.js +179 -6
- package/dist/compiler/performance-monitor.js +192 -0
- package/dist/compiler/transform-optimizer.js +392 -0
- package/dist/compiler/transforms.js +124 -118
- 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 +845 -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 +367 -0
- package/dist/servicios/logger.js +1 -0
- package/dist/servicios/readConfig.js +1 -0
- package/dist/utils/excluded-modules.js +36 -0
- package/dist/utils/module-resolver.js +9 -48
- package/dist/utils/promptUser.js +1 -1
- package/dist/utils/resolve-bin.js +28 -9
- package/package.json +8 -7
- package/dist/servicios/chokidar.js +0 -178
|
@@ -16,10 +16,10 @@ export class TypeScriptWorkerManager {
|
|
|
16
16
|
pendingTasks = new Map();
|
|
17
17
|
taskCounter = 0;
|
|
18
18
|
workerReady = false;
|
|
19
|
-
initPromise = null;
|
|
20
|
-
// Configuración del worker
|
|
21
|
-
WORKER_TIMEOUT = 30000; // 30 segundos timeout (incrementado)
|
|
19
|
+
initPromise = null; // Configuración del worker private readonly WORKER_TIMEOUT = 45000; // 45 segundos timeout (incrementado para manejar concurrencia)
|
|
22
20
|
MAX_RETRY_ATTEMPTS = 2;
|
|
21
|
+
MAX_CONCURRENT_TASKS = 20; // Aumentar límite de tareas concurrentes
|
|
22
|
+
TASK_TIMEOUT = 15000; // 15 segundos timeout por tarea individual
|
|
23
23
|
// NUEVOS: Gestión de modo y estado para optimización
|
|
24
24
|
currentMode = null;
|
|
25
25
|
constructor() { }
|
|
@@ -50,29 +50,38 @@ export class TypeScriptWorkerManager {
|
|
|
50
50
|
}
|
|
51
51
|
this.initPromise = this._performWorkerInitialization();
|
|
52
52
|
return this.initPromise;
|
|
53
|
-
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
54
55
|
* Realiza la inicialización del worker thread
|
|
55
|
-
*/
|
|
56
|
-
async _performWorkerInitialization() {
|
|
56
|
+
*/ async _performWorkerInitialization() {
|
|
57
57
|
try {
|
|
58
|
-
// Obtener ruta al worker thread (compatible con ES modules y Windows)
|
|
59
|
-
const workerPath = path.join(process.env.PATH_PROY || process.cwd(), 'compiler', 'typescript-worker-thread.cjs');
|
|
60
|
-
|
|
58
|
+
console.log('[WorkerManager] 🚀 Iniciando proceso de inicialización del worker...'); // Obtener ruta al worker thread (compatible con ES modules y Windows)
|
|
59
|
+
const workerPath = path.join(process.env.PATH_PROY || path.join(process.cwd(), 'src'), 'compiler', 'typescript-worker-thread.cjs');
|
|
60
|
+
console.log('[WorkerManager] 📂 Ruta del worker:', workerPath);
|
|
61
|
+
console.log('[WorkerManager] 🌍 PATH_PROY:', process.env.PATH_PROY);
|
|
62
|
+
console.log('[WorkerManager] 📁 CWD:', process.cwd());
|
|
63
|
+
// Verificar que el archivo existe
|
|
64
|
+
const fs = await import('node:fs');
|
|
65
|
+
const exists = fs.existsSync(workerPath);
|
|
66
|
+
console.log('[WorkerManager] 📋 Worker file exists:', exists);
|
|
61
67
|
// Crear el worker thread sin tsx para evitar dependencias externas
|
|
68
|
+
console.log('[WorkerManager] 🔧 Creando Worker...');
|
|
62
69
|
this.worker = new Worker(workerPath, {
|
|
63
70
|
env: {
|
|
64
71
|
...process.env,
|
|
65
72
|
NODE_OPTIONS: '', // Limpiar NODE_OPTIONS para evitar conflictos con tsx
|
|
66
73
|
},
|
|
67
74
|
});
|
|
75
|
+
console.log('[WorkerManager] ✅ Worker creado, configurando listeners...');
|
|
68
76
|
// Configurar listeners del worker
|
|
69
77
|
this.setupWorkerListeners();
|
|
78
|
+
console.log('[WorkerManager] ⏳ Esperando que el worker esté listo...');
|
|
70
79
|
// Esperar a que el worker esté listo
|
|
71
80
|
await this.waitForWorkerReady();
|
|
72
|
-
|
|
81
|
+
console.log('[WorkerManager] ✅ Worker inicializado exitosamente');
|
|
73
82
|
}
|
|
74
83
|
catch (error) {
|
|
75
|
-
console.error('[WorkerManager] Error inicializando worker:', error);
|
|
84
|
+
console.error('[WorkerManager] ❌ Error inicializando worker:', error);
|
|
76
85
|
this.worker = null;
|
|
77
86
|
this.workerReady = false;
|
|
78
87
|
throw error;
|
|
@@ -178,33 +187,55 @@ export class TypeScriptWorkerManager {
|
|
|
178
187
|
*/
|
|
179
188
|
generateTaskId() {
|
|
180
189
|
return `task-${++this.taskCounter}-${Date.now()}`;
|
|
181
|
-
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
182
192
|
* Realiza type checking usando el worker thread (con fallback síncrono)
|
|
183
193
|
* @param fileName - Nombre del archivo TypeScript
|
|
184
194
|
* @param content - Contenido del archivo
|
|
185
195
|
* @param compilerOptions - Opciones del compilador TypeScript
|
|
186
196
|
* @returns Resultado de la validación de tipos
|
|
187
|
-
*/
|
|
188
|
-
|
|
197
|
+
*/ async typeCheck(fileName, content, compilerOptions) {
|
|
198
|
+
// Limitar tareas concurrentes para evitar saturación
|
|
199
|
+
if (this.pendingTasks.size >= this.MAX_CONCURRENT_TASKS) {
|
|
200
|
+
return this.typeCheckWithSyncFallback(fileName, content, compilerOptions);
|
|
201
|
+
}
|
|
189
202
|
// En modo de testing o si hay problemas de inicialización, usar fallback directo
|
|
190
203
|
if (process.env.NODE_ENV === 'test' || !this.worker) {
|
|
204
|
+
if (!this.worker) {
|
|
205
|
+
try {
|
|
206
|
+
await this.initializeWorker();
|
|
207
|
+
if (this.worker && this.workerReady) {
|
|
208
|
+
console.log('[WorkerManager] ✅ Worker inicializado exitosamente, reintentando...');
|
|
209
|
+
return this.typeCheckWithWorker(fileName, content, compilerOptions);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
console.error('[WorkerManager] ❌ Error inicializando worker:', error);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
console.log('[WorkerManager] 🔄 Usando fallback síncrono (test mode o worker no disponible)');
|
|
191
217
|
return this.typeCheckWithSyncFallback(fileName, content, compilerOptions);
|
|
192
218
|
}
|
|
193
219
|
try {
|
|
194
|
-
|
|
220
|
+
console.log('[WorkerManager] 🚀 Intentando usar worker thread...');
|
|
221
|
+
// Intentar usar el worker thread con timeout más realista
|
|
195
222
|
const workerPromise = this.typeCheckWithWorker(fileName, content, compilerOptions);
|
|
196
223
|
const timeoutPromise = new Promise((resolve, reject) => {
|
|
197
224
|
setTimeout(() => {
|
|
225
|
+
console.log('[WorkerManager] ⏰ Worker timeout, usando fallback');
|
|
198
226
|
reject(new Error('Worker timeout - usando fallback'));
|
|
199
|
-
},
|
|
227
|
+
}, this.TASK_TIMEOUT); // Usar timeout por tarea más realista
|
|
200
228
|
});
|
|
201
|
-
|
|
229
|
+
console.log('[WorkerManager] ⏳ Esperando respuesta del worker...');
|
|
230
|
+
const result = await Promise.race([workerPromise, timeoutPromise]);
|
|
231
|
+
console.log('[WorkerManager] ✅ Worker completado exitosamente');
|
|
232
|
+
return result;
|
|
202
233
|
}
|
|
203
234
|
catch (workerError) {
|
|
204
235
|
const errorMessage = workerError instanceof Error
|
|
205
236
|
? workerError.message
|
|
206
237
|
: String(workerError);
|
|
207
|
-
console.warn('[WorkerManager] Error en worker, usando fallback síncrono:', errorMessage);
|
|
238
|
+
console.warn('[WorkerManager] ❌ Error en worker, usando fallback síncrono:', errorMessage);
|
|
208
239
|
// Fallback a validación síncrona
|
|
209
240
|
return this.typeCheckWithSyncFallback(fileName, content, compilerOptions);
|
|
210
241
|
}
|
|
@@ -219,12 +250,11 @@ export class TypeScriptWorkerManager {
|
|
|
219
250
|
throw new Error('Worker no disponible');
|
|
220
251
|
}
|
|
221
252
|
return new Promise((resolve, reject) => {
|
|
222
|
-
const taskId = this.generateTaskId();
|
|
223
|
-
// Configurar timeout para la tarea
|
|
253
|
+
const taskId = this.generateTaskId(); // Configurar timeout para la tarea
|
|
224
254
|
const timeout = setTimeout(() => {
|
|
225
255
|
this.pendingTasks.delete(taskId);
|
|
226
256
|
reject(new Error(`Timeout en type checking para ${fileName}`));
|
|
227
|
-
}, this.
|
|
257
|
+
}, this.TASK_TIMEOUT);
|
|
228
258
|
// Agregar tarea a la lista de pendientes
|
|
229
259
|
this.pendingTasks.set(taskId, {
|
|
230
260
|
resolve,
|
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}`));
|