versacompiler 1.0.4 → 2.0.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 +357 -145
- package/dist/compiler/compile.js +1120 -0
- package/dist/compiler/error-reporter.js +467 -0
- package/dist/compiler/linter.js +72 -0
- package/dist/{services → compiler}/minify.js +40 -31
- package/dist/compiler/parser.js +30 -0
- package/dist/compiler/tailwindcss.js +39 -0
- package/dist/compiler/transformTStoJS.js +16 -0
- package/dist/compiler/transforms.js +544 -0
- package/dist/compiler/typescript-error-parser.js +282 -0
- package/dist/compiler/typescript-sync-validator.js +230 -0
- package/dist/compiler/typescript-worker-thread.cjs +457 -0
- package/dist/compiler/typescript-worker.js +309 -0
- package/dist/compiler/typescript.js +382 -0
- package/dist/compiler/vuejs.js +296 -0
- package/dist/hrm/VueHRM.js +353 -0
- package/dist/hrm/errorScreen.js +23 -1
- package/dist/hrm/getInstanciaVue.js +313 -0
- package/dist/hrm/initHRM.js +140 -0
- package/dist/main.js +287 -0
- package/dist/servicios/browserSync.js +177 -0
- package/dist/servicios/chokidar.js +178 -0
- package/dist/servicios/logger.js +33 -0
- package/dist/servicios/readConfig.js +429 -0
- package/dist/utils/module-resolver.js +506 -0
- package/dist/utils/promptUser.js +48 -0
- package/dist/utils/resolve-bin.js +29 -0
- package/dist/utils/utils.js +21 -48
- package/dist/wrappers/eslint-node.js +145 -0
- package/dist/wrappers/oxlint-node.js +120 -0
- package/dist/wrappers/tailwind-node.js +92 -0
- package/package.json +62 -15
- package/dist/hrm/devMode.js +0 -249
- package/dist/hrm/instanciaVue.js +0 -35
- package/dist/hrm/setupHMR.js +0 -57
- package/dist/index.js +0 -873
- package/dist/services/acorn.js +0 -29
- package/dist/services/linter.js +0 -55
- package/dist/services/typescript.js +0 -89
- package/dist/services/vueLoader.js +0 -324
- package/dist/services/vuejs.js +0 -259
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript Worker Manager - Gestiona workers dedicados para type checking asíncrono
|
|
3
|
+
* Implementa el patrón Singleton para reutilizar workers entre compilaciones
|
|
4
|
+
*/
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import * as process from 'node:process';
|
|
7
|
+
import { Worker } from 'node:worker_threads';
|
|
8
|
+
import { validateTypesWithLanguageService } from './typescript-sync-validator.js';
|
|
9
|
+
/**
|
|
10
|
+
* Gestiona workers dedicados para type checking asíncrono de TypeScript
|
|
11
|
+
* Implementa patrón Singleton para eficiencia y reutilización de recursos
|
|
12
|
+
*/
|
|
13
|
+
export class TypeScriptWorkerManager {
|
|
14
|
+
static instance;
|
|
15
|
+
worker = null;
|
|
16
|
+
pendingTasks = new Map();
|
|
17
|
+
taskCounter = 0;
|
|
18
|
+
workerReady = false;
|
|
19
|
+
initPromise = null;
|
|
20
|
+
// Configuración del worker
|
|
21
|
+
WORKER_TIMEOUT = 30000; // 30 segundos timeout (incrementado)
|
|
22
|
+
MAX_RETRY_ATTEMPTS = 2;
|
|
23
|
+
// NUEVOS: Gestión de modo y estado para optimización
|
|
24
|
+
currentMode = null;
|
|
25
|
+
constructor() { }
|
|
26
|
+
/**
|
|
27
|
+
* NUEVO: Configura el modo de operación del worker
|
|
28
|
+
*/
|
|
29
|
+
setMode(mode) {
|
|
30
|
+
this.currentMode = mode;
|
|
31
|
+
// En modo watch, el worker se mantiene activo más tiempo
|
|
32
|
+
// En otros modos, se puede optimizar la gestión de recursos
|
|
33
|
+
console.log(`[WorkerManager] Modo establecido: ${mode}`);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Obtiene la instancia singleton del Worker Manager
|
|
37
|
+
*/
|
|
38
|
+
static getInstance() {
|
|
39
|
+
if (!TypeScriptWorkerManager.instance) {
|
|
40
|
+
TypeScriptWorkerManager.instance = new TypeScriptWorkerManager();
|
|
41
|
+
}
|
|
42
|
+
return TypeScriptWorkerManager.instance;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Inicializa el worker thread de TypeScript
|
|
46
|
+
*/
|
|
47
|
+
async initializeWorker() {
|
|
48
|
+
if (this.initPromise) {
|
|
49
|
+
return this.initPromise;
|
|
50
|
+
}
|
|
51
|
+
this.initPromise = this._performWorkerInitialization();
|
|
52
|
+
return this.initPromise;
|
|
53
|
+
} /**
|
|
54
|
+
* Realiza la inicialización del worker thread
|
|
55
|
+
*/
|
|
56
|
+
async _performWorkerInitialization() {
|
|
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
|
+
// console.log('[WorkerManager] Inicializando worker en:', workerPath);
|
|
61
|
+
// Crear el worker thread sin tsx para evitar dependencias externas
|
|
62
|
+
this.worker = new Worker(workerPath, {
|
|
63
|
+
env: {
|
|
64
|
+
...process.env,
|
|
65
|
+
NODE_OPTIONS: '', // Limpiar NODE_OPTIONS para evitar conflictos con tsx
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
// Configurar listeners del worker
|
|
69
|
+
this.setupWorkerListeners();
|
|
70
|
+
// Esperar a que el worker esté listo
|
|
71
|
+
await this.waitForWorkerReady();
|
|
72
|
+
// console.log('[WorkerManager] Worker inicializado exitosamente');
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error('[WorkerManager] Error inicializando worker:', error);
|
|
76
|
+
this.worker = null;
|
|
77
|
+
this.workerReady = false;
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Configura los listeners del worker thread
|
|
83
|
+
*/
|
|
84
|
+
setupWorkerListeners() {
|
|
85
|
+
if (!this.worker)
|
|
86
|
+
return;
|
|
87
|
+
this.worker.on('message', (response) => {
|
|
88
|
+
try {
|
|
89
|
+
// console.log('[WorkerManager] Mensaje recibido del worker:', {
|
|
90
|
+
// id: response.id,
|
|
91
|
+
// success: response.success,
|
|
92
|
+
// hasErrors: response.hasErrors,
|
|
93
|
+
// diagnosticsCount: response.diagnostics?.length,
|
|
94
|
+
// });
|
|
95
|
+
// Manejar mensaje de worker ready
|
|
96
|
+
if (response.id === 'worker-ready') {
|
|
97
|
+
this.workerReady = true;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// Buscar la tarea pendiente correspondiente
|
|
101
|
+
const pendingTask = this.pendingTasks.get(response.id);
|
|
102
|
+
if (!pendingTask) {
|
|
103
|
+
console.warn('[WorkerManager] Respuesta para tarea desconocida:', response.id);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
// Limpiar timeout y eliminar tarea pendiente
|
|
107
|
+
clearTimeout(pendingTask.timeout);
|
|
108
|
+
this.pendingTasks.delete(response.id);
|
|
109
|
+
// Procesar respuesta
|
|
110
|
+
if (response.success &&
|
|
111
|
+
response.diagnostics !== undefined &&
|
|
112
|
+
response.hasErrors !== undefined) {
|
|
113
|
+
pendingTask.resolve({
|
|
114
|
+
diagnostics: response.diagnostics,
|
|
115
|
+
hasErrors: response.hasErrors,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
const errorMessage = response.error || 'Error desconocido del worker';
|
|
120
|
+
pendingTask.reject(new Error(errorMessage));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
console.error('[WorkerManager] Error procesando respuesta del worker:', error);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
this.worker.on('error', error => {
|
|
128
|
+
console.error('[WorkerManager] Error en worker thread:', error);
|
|
129
|
+
this.handleWorkerError(error);
|
|
130
|
+
});
|
|
131
|
+
this.worker.on('exit', code => {
|
|
132
|
+
console.warn('[WorkerManager] Worker thread cerrado con código:', code);
|
|
133
|
+
this.workerReady = false;
|
|
134
|
+
// Rechazar todas las tareas pendientes
|
|
135
|
+
this.pendingTasks.forEach(task => {
|
|
136
|
+
clearTimeout(task.timeout);
|
|
137
|
+
task.reject(new Error(`Worker cerrado inesperadamente con código ${code}`));
|
|
138
|
+
});
|
|
139
|
+
this.pendingTasks.clear();
|
|
140
|
+
});
|
|
141
|
+
} /**
|
|
142
|
+
* Espera a que el worker esté listo para recibir tareas
|
|
143
|
+
*/
|
|
144
|
+
async waitForWorkerReady() {
|
|
145
|
+
return new Promise((resolve, reject) => {
|
|
146
|
+
// Reducir timeout para inicialización a 5 segundos
|
|
147
|
+
const timeout = setTimeout(() => {
|
|
148
|
+
reject(new Error('Timeout esperando a que el worker esté listo'));
|
|
149
|
+
}, 5000);
|
|
150
|
+
const checkReady = () => {
|
|
151
|
+
if (this.workerReady) {
|
|
152
|
+
clearTimeout(timeout);
|
|
153
|
+
resolve();
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
setTimeout(checkReady, 50); // Verificar cada 50ms (más frecuente)
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
checkReady();
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Maneja errores del worker thread
|
|
164
|
+
*/
|
|
165
|
+
handleWorkerError(error) {
|
|
166
|
+
console.error('[WorkerManager] Manejando error del worker:', error);
|
|
167
|
+
// Rechazar todas las tareas pendientes
|
|
168
|
+
this.pendingTasks.forEach(task => {
|
|
169
|
+
clearTimeout(task.timeout);
|
|
170
|
+
task.reject(new Error(`Error en worker: ${error.message}`));
|
|
171
|
+
});
|
|
172
|
+
this.pendingTasks.clear();
|
|
173
|
+
// Marcar worker como no disponible
|
|
174
|
+
this.workerReady = false;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Genera un ID único para cada tarea
|
|
178
|
+
*/
|
|
179
|
+
generateTaskId() {
|
|
180
|
+
return `task-${++this.taskCounter}-${Date.now()}`;
|
|
181
|
+
} /**
|
|
182
|
+
* Realiza type checking usando el worker thread (con fallback síncrono)
|
|
183
|
+
* @param fileName - Nombre del archivo TypeScript
|
|
184
|
+
* @param content - Contenido del archivo
|
|
185
|
+
* @param compilerOptions - Opciones del compilador TypeScript
|
|
186
|
+
* @returns Resultado de la validación de tipos
|
|
187
|
+
*/
|
|
188
|
+
async typeCheck(fileName, content, compilerOptions) {
|
|
189
|
+
// En modo de testing o si hay problemas de inicialización, usar fallback directo
|
|
190
|
+
if (process.env.NODE_ENV === 'test' || !this.worker) {
|
|
191
|
+
return this.typeCheckWithSyncFallback(fileName, content, compilerOptions);
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
// Intentar usar el worker thread con timeout más corto
|
|
195
|
+
const workerPromise = this.typeCheckWithWorker(fileName, content, compilerOptions);
|
|
196
|
+
const timeoutPromise = new Promise((resolve, reject) => {
|
|
197
|
+
setTimeout(() => {
|
|
198
|
+
reject(new Error('Worker timeout - usando fallback'));
|
|
199
|
+
}, 5000); // 5 segundos max para worker
|
|
200
|
+
});
|
|
201
|
+
return await Promise.race([workerPromise, timeoutPromise]);
|
|
202
|
+
}
|
|
203
|
+
catch (workerError) {
|
|
204
|
+
const errorMessage = workerError instanceof Error
|
|
205
|
+
? workerError.message
|
|
206
|
+
: String(workerError);
|
|
207
|
+
console.warn('[WorkerManager] Error en worker, usando fallback síncrono:', errorMessage);
|
|
208
|
+
// Fallback a validación síncrona
|
|
209
|
+
return this.typeCheckWithSyncFallback(fileName, content, compilerOptions);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Realiza type checking usando el worker thread
|
|
214
|
+
*/
|
|
215
|
+
async typeCheckWithWorker(fileName, content, compilerOptions) {
|
|
216
|
+
// Asegurar que el worker esté inicializado
|
|
217
|
+
await this.initializeWorker();
|
|
218
|
+
if (!this.worker || !this.workerReady) {
|
|
219
|
+
throw new Error('Worker no disponible');
|
|
220
|
+
}
|
|
221
|
+
return new Promise((resolve, reject) => {
|
|
222
|
+
const taskId = this.generateTaskId();
|
|
223
|
+
// Configurar timeout para la tarea
|
|
224
|
+
const timeout = setTimeout(() => {
|
|
225
|
+
this.pendingTasks.delete(taskId);
|
|
226
|
+
reject(new Error(`Timeout en type checking para ${fileName}`));
|
|
227
|
+
}, this.WORKER_TIMEOUT);
|
|
228
|
+
// Agregar tarea a la lista de pendientes
|
|
229
|
+
this.pendingTasks.set(taskId, {
|
|
230
|
+
resolve,
|
|
231
|
+
reject,
|
|
232
|
+
timeout,
|
|
233
|
+
});
|
|
234
|
+
try {
|
|
235
|
+
// Crear mensaje para el worker
|
|
236
|
+
const message = {
|
|
237
|
+
id: taskId,
|
|
238
|
+
fileName,
|
|
239
|
+
content,
|
|
240
|
+
compilerOptions,
|
|
241
|
+
};
|
|
242
|
+
// console.log('[WorkerManager] Enviando tarea al worker:', {
|
|
243
|
+
// id: taskId,
|
|
244
|
+
// fileName,
|
|
245
|
+
// contentLength: content.length,
|
|
246
|
+
// compilerOptionsKeys: Object.keys(compilerOptions),
|
|
247
|
+
// });
|
|
248
|
+
// Enviar mensaje al worker
|
|
249
|
+
this.worker.postMessage(message);
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
// Limpiar en caso de error
|
|
253
|
+
clearTimeout(timeout);
|
|
254
|
+
this.pendingTasks.delete(taskId);
|
|
255
|
+
reject(error);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Fallback síncrono para type checking cuando el worker no está disponible
|
|
261
|
+
*/
|
|
262
|
+
typeCheckWithSyncFallback(fileName, content, compilerOptions) {
|
|
263
|
+
// console.log(
|
|
264
|
+
// '[WorkerManager] Ejecutando type checking síncrono como fallback',
|
|
265
|
+
// );
|
|
266
|
+
try {
|
|
267
|
+
return validateTypesWithLanguageService(fileName, content, compilerOptions);
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
console.error('[WorkerManager] Error en fallback síncrono:', error);
|
|
271
|
+
// Devolver resultado vacío en caso de error total
|
|
272
|
+
return {
|
|
273
|
+
diagnostics: [],
|
|
274
|
+
hasErrors: false,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Cierra el worker thread y limpia recursos
|
|
280
|
+
*/
|
|
281
|
+
async terminate() {
|
|
282
|
+
if (this.worker) {
|
|
283
|
+
// console.log('[WorkerManager] Cerrando worker thread...');
|
|
284
|
+
// Rechazar todas las tareas pendientes
|
|
285
|
+
this.pendingTasks.forEach(task => {
|
|
286
|
+
clearTimeout(task.timeout);
|
|
287
|
+
task.reject(new Error('Worker manager cerrado'));
|
|
288
|
+
});
|
|
289
|
+
this.pendingTasks.clear();
|
|
290
|
+
// Cerrar worker
|
|
291
|
+
await this.worker.terminate();
|
|
292
|
+
this.worker = null;
|
|
293
|
+
this.workerReady = false;
|
|
294
|
+
this.initPromise = null;
|
|
295
|
+
// console.log('[WorkerManager] Worker cerrado exitosamente');
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Obtiene estadísticas del worker manager
|
|
300
|
+
*/
|
|
301
|
+
getStats() {
|
|
302
|
+
return {
|
|
303
|
+
workerReady: this.workerReady,
|
|
304
|
+
pendingTasks: this.pendingTasks.size,
|
|
305
|
+
taskCounter: this.taskCounter,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=typescript-worker.js.map
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { env } from 'node:process';
|
|
4
|
+
import * as ts from 'typescript';
|
|
5
|
+
import { createUnifiedErrorMessage, parseTypeScriptErrors, } from './typescript-error-parser.js';
|
|
6
|
+
import { TypeScriptWorkerManager } from './typescript-worker.js';
|
|
7
|
+
/**
|
|
8
|
+
* Cache para la configuración de TypeScript para evitar lecturas repetidas
|
|
9
|
+
*/
|
|
10
|
+
let configCache = {};
|
|
11
|
+
/**
|
|
12
|
+
* Carga la configuración de TypeScript desde tsconfig.json
|
|
13
|
+
* @param fileName - Nombre del archivo para buscar el tsconfig.json relativo
|
|
14
|
+
* @returns Opciones del compilador TypeScript
|
|
15
|
+
*/
|
|
16
|
+
export const loadTypeScriptConfig = (fileName) => {
|
|
17
|
+
const fileDir = path.dirname(fileName);
|
|
18
|
+
const configPath = ts.findConfigFile(fileDir, ts.sys.fileExists, 'tsconfig.json') ||
|
|
19
|
+
path.resolve(process.cwd(), 'tsconfig.json');
|
|
20
|
+
// Usar cache si el path no ha cambiado
|
|
21
|
+
if (configCache.path === configPath && configCache.options) {
|
|
22
|
+
return configCache.options;
|
|
23
|
+
}
|
|
24
|
+
let compilerOptions;
|
|
25
|
+
if (configPath && fs.existsSync(configPath)) {
|
|
26
|
+
try {
|
|
27
|
+
const { config, error: configError } = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
28
|
+
if (!configError) {
|
|
29
|
+
const parsedConfig = ts.parseJsonConfigFileContent(config, ts.sys, path.dirname(configPath));
|
|
30
|
+
compilerOptions = {
|
|
31
|
+
...parsedConfig.options,
|
|
32
|
+
// Asegurar opciones básicas necesarias
|
|
33
|
+
allowJs: parsedConfig.options.allowJs !== false,
|
|
34
|
+
esModuleInterop: parsedConfig.options.esModuleInterop !== false,
|
|
35
|
+
allowSyntheticDefaultImports: parsedConfig.options.allowSyntheticDefaultImports !==
|
|
36
|
+
false,
|
|
37
|
+
skipLibCheck: parsedConfig.options.skipLibCheck !== false,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
throw new Error(`Error al leer tsconfig.json: ${configError.messageText}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.warn(`[loadTypeScriptConfig] Error cargando ${configPath}:`, error);
|
|
46
|
+
// Fallback a opciones por defecto
|
|
47
|
+
compilerOptions = getDefaultCompilerOptions();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
// Opciones por defecto si no se encuentra tsconfig.json
|
|
52
|
+
compilerOptions = getDefaultCompilerOptions();
|
|
53
|
+
}
|
|
54
|
+
// Guardar en cache
|
|
55
|
+
configCache = { path: configPath, options: compilerOptions };
|
|
56
|
+
return compilerOptions;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Obtiene las opciones por defecto del compilador TypeScript
|
|
60
|
+
*/
|
|
61
|
+
const getDefaultCompilerOptions = () => ({
|
|
62
|
+
target: ts.ScriptTarget.ES2020,
|
|
63
|
+
module: ts.ModuleKind.ES2020,
|
|
64
|
+
lib: ['es2020', 'dom', 'dom.iterable'],
|
|
65
|
+
strict: false,
|
|
66
|
+
skipLibCheck: true,
|
|
67
|
+
allowJs: true,
|
|
68
|
+
esModuleInterop: true,
|
|
69
|
+
allowSyntheticDefaultImports: true,
|
|
70
|
+
isolatedModules: true,
|
|
71
|
+
});
|
|
72
|
+
/**
|
|
73
|
+
* Crea una versión optimizada y serializable de las opciones del compilador TypeScript.
|
|
74
|
+
* @param options - Opciones originales del compilador
|
|
75
|
+
* @returns Opciones serializables seguras para workers
|
|
76
|
+
*/
|
|
77
|
+
const createSerializableCompilerOptions = (options) => {
|
|
78
|
+
// Usar las opciones del tsconfig.json pero con optimizaciones para el worker
|
|
79
|
+
const { target = ts.ScriptTarget.ES2020, module = ts.ModuleKind.ES2020, lib = ['es2020', 'dom', 'dom.iterable'], allowJs = true, jsx, strict = false, skipLibCheck = true, esModuleInterop = true, allowSyntheticDefaultImports = true, isolatedModules = true, } = options;
|
|
80
|
+
return {
|
|
81
|
+
target,
|
|
82
|
+
module,
|
|
83
|
+
lib: Array.isArray(lib) ? lib : ['es2020', 'dom', 'dom.iterable'],
|
|
84
|
+
allowJs,
|
|
85
|
+
jsx,
|
|
86
|
+
strict,
|
|
87
|
+
skipLibCheck,
|
|
88
|
+
skipDefaultLibCheck: true,
|
|
89
|
+
esModuleInterop,
|
|
90
|
+
allowSyntheticDefaultImports,
|
|
91
|
+
isolatedModules,
|
|
92
|
+
noEmitOnError: false,
|
|
93
|
+
declaration: false,
|
|
94
|
+
sourceMap: false,
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Crea un Language Service Host optimizado para validación de tipos eficiente.
|
|
99
|
+
*/
|
|
100
|
+
class TypeScriptLanguageServiceHost {
|
|
101
|
+
files = new Map();
|
|
102
|
+
compilerOptions;
|
|
103
|
+
fileSystemCache = new Map();
|
|
104
|
+
constructor(compilerOptions) {
|
|
105
|
+
this.compilerOptions = compilerOptions;
|
|
106
|
+
}
|
|
107
|
+
addFile(fileName, content) {
|
|
108
|
+
const existing = this.files.get(fileName);
|
|
109
|
+
this.files.set(fileName, {
|
|
110
|
+
version: existing ? existing.version + 1 : 1,
|
|
111
|
+
content,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
getCompilationSettings() {
|
|
115
|
+
return this.compilerOptions;
|
|
116
|
+
}
|
|
117
|
+
getScriptFileNames() {
|
|
118
|
+
return Array.from(this.files.keys());
|
|
119
|
+
}
|
|
120
|
+
getScriptVersion(fileName) {
|
|
121
|
+
const file = this.files.get(fileName);
|
|
122
|
+
return file ? file.version.toString() : '0';
|
|
123
|
+
}
|
|
124
|
+
getScriptSnapshot(fileName) {
|
|
125
|
+
const file = this.files.get(fileName);
|
|
126
|
+
if (file) {
|
|
127
|
+
return ts.ScriptSnapshot.fromString(file.content);
|
|
128
|
+
}
|
|
129
|
+
// Cache de sistema de archivos para evitar lecturas repetidas
|
|
130
|
+
if (this.fileSystemCache.has(fileName)) {
|
|
131
|
+
const cachedContent = this.fileSystemCache.get(fileName);
|
|
132
|
+
return cachedContent
|
|
133
|
+
? ts.ScriptSnapshot.fromString(cachedContent)
|
|
134
|
+
: undefined;
|
|
135
|
+
}
|
|
136
|
+
// Intentar leer el archivo del sistema de archivos solo si es necesario
|
|
137
|
+
try {
|
|
138
|
+
if (fs.existsSync(fileName)) {
|
|
139
|
+
const content = fs.readFileSync(fileName, 'utf-8');
|
|
140
|
+
this.fileSystemCache.set(fileName, content);
|
|
141
|
+
return ts.ScriptSnapshot.fromString(content);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// Error al leer archivo
|
|
146
|
+
}
|
|
147
|
+
this.fileSystemCache.set(fileName, undefined);
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
getCurrentDirectory() {
|
|
151
|
+
return process.cwd();
|
|
152
|
+
}
|
|
153
|
+
getDefaultLibFileName(options) {
|
|
154
|
+
return ts.getDefaultLibFilePath(options);
|
|
155
|
+
}
|
|
156
|
+
fileExists(path) {
|
|
157
|
+
if (this.files.has(path))
|
|
158
|
+
return true;
|
|
159
|
+
if (this.fileSystemCache.has(path)) {
|
|
160
|
+
return this.fileSystemCache.get(path) !== undefined;
|
|
161
|
+
}
|
|
162
|
+
const exists = fs.existsSync(path);
|
|
163
|
+
if (!exists)
|
|
164
|
+
this.fileSystemCache.set(path, undefined);
|
|
165
|
+
return exists;
|
|
166
|
+
}
|
|
167
|
+
readFile(path) {
|
|
168
|
+
const file = this.files.get(path);
|
|
169
|
+
if (file)
|
|
170
|
+
return file.content;
|
|
171
|
+
if (this.fileSystemCache.has(path)) {
|
|
172
|
+
return this.fileSystemCache.get(path);
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
if (fs.existsSync(path)) {
|
|
176
|
+
const content = fs.readFileSync(path, 'utf-8');
|
|
177
|
+
this.fileSystemCache.set(path, content);
|
|
178
|
+
return content;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// Error al leer archivo
|
|
183
|
+
}
|
|
184
|
+
this.fileSystemCache.set(path, undefined);
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
getNewLine() {
|
|
188
|
+
return ts.sys.newLine;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Realiza validación de tipos optimizada usando TypeScript Language Service.
|
|
193
|
+
* @param fileName - Nombre del archivo
|
|
194
|
+
* @param content - Contenido del archivo
|
|
195
|
+
* @param compilerOptions - Opciones del compilador
|
|
196
|
+
* @returns Resultado de la validación de tipos
|
|
197
|
+
*/
|
|
198
|
+
const validateTypesWithLanguageService = (fileName, content, compilerOptions) => {
|
|
199
|
+
try {
|
|
200
|
+
// Validación temprana: contenido vacío
|
|
201
|
+
if (!content.trim()) {
|
|
202
|
+
return { diagnostics: [], hasErrors: false };
|
|
203
|
+
}
|
|
204
|
+
// Crear Language Service Host optimizado
|
|
205
|
+
const host = new TypeScriptLanguageServiceHost(compilerOptions);
|
|
206
|
+
// Determinar nombre de archivo efectivo
|
|
207
|
+
let actualFileName = path.isAbsolute(fileName)
|
|
208
|
+
? fileName
|
|
209
|
+
: path.resolve(fileName);
|
|
210
|
+
// Para archivos Vue, crear archivo virtual
|
|
211
|
+
if (fileName.endsWith('.vue')) {
|
|
212
|
+
actualFileName = actualFileName.replace('.vue', '.vue.ts');
|
|
213
|
+
host.addFile(actualFileName, content);
|
|
214
|
+
// Añadir declaraciones Vue básicas solo si es necesario
|
|
215
|
+
const vueTypesPath = path.join(path.dirname(actualFileName), 'vue-types.d.ts');
|
|
216
|
+
const vueTypesDeclaration = `
|
|
217
|
+
declare global {
|
|
218
|
+
function ref<T>(value: T): { value: T };
|
|
219
|
+
function reactive<T extends object>(target: T): T;
|
|
220
|
+
function computed<T>(getter: () => T): { value: T };
|
|
221
|
+
function defineComponent<T>(options: T): T;
|
|
222
|
+
function defineProps<T = {}>(): T;
|
|
223
|
+
function defineEmits<T = {}>(): T;
|
|
224
|
+
function onMounted(fn: () => void): void;
|
|
225
|
+
function onUnmounted(fn: () => void): void;
|
|
226
|
+
function watch<T>(source: () => T, callback: (newValue: T, oldValue: T) => void): void;
|
|
227
|
+
}
|
|
228
|
+
export {};`;
|
|
229
|
+
host.addFile(vueTypesPath, vueTypesDeclaration);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
host.addFile(actualFileName, content);
|
|
233
|
+
}
|
|
234
|
+
// Crear Language Service
|
|
235
|
+
const languageService = ts.createLanguageService(host);
|
|
236
|
+
// Verificar existencia del archivo
|
|
237
|
+
if (!host.fileExists(actualFileName)) {
|
|
238
|
+
return { diagnostics: [], hasErrors: false };
|
|
239
|
+
}
|
|
240
|
+
// Obtener diagnósticos con manejo de errores optimizado
|
|
241
|
+
const allDiagnostics = [];
|
|
242
|
+
try {
|
|
243
|
+
allDiagnostics.push(...languageService.getSyntacticDiagnostics(actualFileName));
|
|
244
|
+
allDiagnostics.push(...languageService.getSemanticDiagnostics(actualFileName));
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
// Ignorar errores de diagnósticos
|
|
248
|
+
return { diagnostics: [], hasErrors: false };
|
|
249
|
+
}
|
|
250
|
+
// Filtrado optimizado de diagnósticos
|
|
251
|
+
const filteredDiagnostics = allDiagnostics.filter(diag => {
|
|
252
|
+
if (diag.category !== ts.DiagnosticCategory.Error)
|
|
253
|
+
return false;
|
|
254
|
+
const messageText = ts.flattenDiagnosticMessageText(diag.messageText, '\n');
|
|
255
|
+
// Lista optimizada de patrones a ignorar
|
|
256
|
+
const ignorePatterns = [
|
|
257
|
+
'Cannot find module',
|
|
258
|
+
'Could not find source file',
|
|
259
|
+
"Parameter '$props' implicitly has an 'any' type",
|
|
260
|
+
"Parameter '$setup' implicitly has an 'any' type",
|
|
261
|
+
"Parameter '_ctx' implicitly has an 'any' type",
|
|
262
|
+
"Parameter '_cache' implicitly has an 'any' type",
|
|
263
|
+
];
|
|
264
|
+
return !ignorePatterns.some(pattern => messageText.includes(pattern));
|
|
265
|
+
});
|
|
266
|
+
return {
|
|
267
|
+
diagnostics: filteredDiagnostics,
|
|
268
|
+
hasErrors: filteredDiagnostics.length > 0,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
// Error handling simplificado
|
|
273
|
+
return {
|
|
274
|
+
diagnostics: [
|
|
275
|
+
{
|
|
276
|
+
file: undefined,
|
|
277
|
+
start: undefined,
|
|
278
|
+
length: undefined,
|
|
279
|
+
messageText: `Error en validación de tipos: ${error instanceof Error ? error.message : 'Error desconocido'}`,
|
|
280
|
+
category: ts.DiagnosticCategory.Error,
|
|
281
|
+
code: 0,
|
|
282
|
+
},
|
|
283
|
+
],
|
|
284
|
+
hasErrors: true,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
289
|
+
* Valida tipos en archivos Vue antes de la compilación
|
|
290
|
+
* @param vueContent - Contenido del archivo Vue
|
|
291
|
+
* @param fileName - Nombre del archivo Vue
|
|
292
|
+
* @returns Resultado de la validación de tipos
|
|
293
|
+
*/
|
|
294
|
+
export const validateVueTypes = (vueContent, fileName) => {
|
|
295
|
+
const compilerOptions = loadTypeScriptConfig(fileName);
|
|
296
|
+
return validateTypesWithLanguageService(fileName, vueContent, compilerOptions);
|
|
297
|
+
};
|
|
298
|
+
/**
|
|
299
|
+
* Precompila el código TypeScript con pipeline optimizado para máxima performance.
|
|
300
|
+
* @param {string} data - El código TypeScript a precompilar.
|
|
301
|
+
* @param {string} fileName - El nombre del archivo que contiene el código TypeScript.
|
|
302
|
+
* @returns {Promise<CompileResult>} - Un objeto con el código precompilado o un error.
|
|
303
|
+
*/
|
|
304
|
+
export const preCompileTS = async (data, fileName) => {
|
|
305
|
+
try {
|
|
306
|
+
// Validación temprana: contenido vacío
|
|
307
|
+
if (!data.trim()) {
|
|
308
|
+
return { error: null, data: data, lang: 'ts' };
|
|
309
|
+
}
|
|
310
|
+
// Cargar configuración de TypeScript desde tsconfig.json
|
|
311
|
+
const compilerOptions = loadTypeScriptConfig(fileName);
|
|
312
|
+
// PASO 1: Transpilación rápida con detección de errores críticos
|
|
313
|
+
const transpileResult = ts.transpileModule(data, {
|
|
314
|
+
compilerOptions: {
|
|
315
|
+
...compilerOptions,
|
|
316
|
+
noLib: true,
|
|
317
|
+
skipLibCheck: true,
|
|
318
|
+
isolatedModules: true,
|
|
319
|
+
},
|
|
320
|
+
fileName,
|
|
321
|
+
reportDiagnostics: true,
|
|
322
|
+
});
|
|
323
|
+
// const transpileResult = traspileTStoJS(
|
|
324
|
+
// fileName,data)
|
|
325
|
+
// Verificar errores críticos de sintaxis
|
|
326
|
+
if (transpileResult.diagnostics?.length) {
|
|
327
|
+
const criticalErrors = transpileResult.diagnostics.filter(diag => {
|
|
328
|
+
if (diag.category !== ts.DiagnosticCategory.Error)
|
|
329
|
+
return false;
|
|
330
|
+
const messageText = ts.flattenDiagnosticMessageText(diag.messageText, '\n');
|
|
331
|
+
// Ignorar errores de módulo no encontrado
|
|
332
|
+
return (!messageText.includes('Cannot find module') &&
|
|
333
|
+
!messageText.includes('Could not find source file') &&
|
|
334
|
+
diag.code !== 2307 &&
|
|
335
|
+
diag.code !== 6059);
|
|
336
|
+
});
|
|
337
|
+
if (criticalErrors.length > 0) {
|
|
338
|
+
const errorMessage = createUnifiedErrorMessage(parseTypeScriptErrors(criticalErrors, fileName, data));
|
|
339
|
+
return {
|
|
340
|
+
error: new Error(errorMessage),
|
|
341
|
+
data: null,
|
|
342
|
+
lang: 'ts',
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// PASO 2: Type checking opcional (solo si está habilitado)
|
|
347
|
+
if (env.typeCheck === 'true') {
|
|
348
|
+
try {
|
|
349
|
+
const workerManager = TypeScriptWorkerManager.getInstance();
|
|
350
|
+
const serializableOptions = createSerializableCompilerOptions(compilerOptions);
|
|
351
|
+
const typeCheckResult = await workerManager.typeCheck(fileName, data, serializableOptions);
|
|
352
|
+
if (typeCheckResult.hasErrors) {
|
|
353
|
+
const errorMessage = createUnifiedErrorMessage(parseTypeScriptErrors(typeCheckResult.diagnostics, fileName, data));
|
|
354
|
+
return {
|
|
355
|
+
error: new Error(errorMessage),
|
|
356
|
+
data: null,
|
|
357
|
+
lang: 'ts',
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
catch (typeCheckError) {
|
|
362
|
+
// Type checking falla, pero continuar con transpilación
|
|
363
|
+
console.warn('[preCompileTS] Type checking failed:', typeCheckError);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
// PASO 3: Devolver resultado optimizado
|
|
367
|
+
const output = transpileResult.outputText;
|
|
368
|
+
// Limpiar output vacío
|
|
369
|
+
if (output.trim() === 'export {};') {
|
|
370
|
+
return { error: null, data: '', lang: 'ts' };
|
|
371
|
+
}
|
|
372
|
+
return { error: null, data: output, lang: 'ts' };
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
return {
|
|
376
|
+
error: error instanceof Error ? error : new Error('Error desconocido'),
|
|
377
|
+
data: null,
|
|
378
|
+
lang: 'ts',
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
//# sourceMappingURL=typescript.js.map
|