versacompiler 2.4.0 → 2.5.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.
Files changed (33) hide show
  1. package/README.md +722 -722
  2. package/dist/compiler/compile-worker-pool.js +96 -0
  3. package/dist/compiler/compile-worker-thread.cjs +72 -0
  4. package/dist/compiler/compile.js +81 -2
  5. package/dist/compiler/integrity-validator.js +1 -1
  6. package/dist/compiler/module-resolution-optimizer.js +23 -20
  7. package/dist/compiler/performance-monitor.js +61 -61
  8. package/dist/compiler/pipeline/build-pipeline.js +127 -0
  9. package/dist/compiler/pipeline/core-plugins.js +218 -0
  10. package/dist/compiler/pipeline/module-graph.js +63 -0
  11. package/dist/compiler/pipeline/plugin-driver.js +87 -0
  12. package/dist/compiler/pipeline/types.js +2 -0
  13. package/dist/compiler/transforms.js +222 -16
  14. package/dist/compiler/typescript-manager.js +3 -1
  15. package/dist/compiler/typescript-sync-validator.js +33 -31
  16. package/dist/compiler/typescript-worker-pool.js +66 -19
  17. package/dist/compiler/typescript-worker-thread.cjs +482 -469
  18. package/dist/compiler/vuejs.js +32 -32
  19. package/dist/config.js +2 -0
  20. package/dist/hrm/VueHRM.js +359 -359
  21. package/dist/hrm/errorScreen.js +83 -83
  22. package/dist/hrm/getInstanciaVue.js +313 -313
  23. package/dist/hrm/initHRM.js +628 -586
  24. package/dist/main.js +2 -1
  25. package/dist/servicios/browserSync.js +8 -2
  26. package/dist/servicios/file-watcher.js +48 -6
  27. package/dist/servicios/readConfig.js +129 -54
  28. package/dist/servicios/versacompile.config.types.js +2 -0
  29. package/dist/utils/module-resolver.js +74 -40
  30. package/dist/utils/vue-types-setup.js +248 -248
  31. package/dist/wrappers/eslint-node.js +3 -1
  32. package/dist/wrappers/oxlint-node.js +3 -1
  33. package/package.json +73 -54
@@ -1,469 +1,482 @@
1
- /**
2
- * TypeScript Worker Thread - Ejecuta type checking asíncrono
3
- * Este archivo se ejecuta en un worker thread separado para validación de tipos
4
- */
5
-
6
- const fs = require('node:fs');
7
- const path = require('node:path');
8
- const { parentPort } = require('node:worker_threads');
9
-
10
- // Debug: Log de inicio del worker
11
- // console.log('[Worker] TypeScript Worker Thread iniciado');
12
-
13
- let ts;
14
- try {
15
- ts = require('typescript');
16
- // console.log('[Worker] TypeScript cargado exitosamente');
17
- } catch (error) {
18
- console.error('[Worker] Error cargando TypeScript:', error);
19
- process.exit(1);
20
- }
21
-
22
- /**
23
- * Language Service Host para validación de tipos en el worker
24
- */
25
- class WorkerTypeScriptLanguageServiceHost {
26
- constructor(compilerOptions) {
27
- this.files = new Map();
28
- // Crear opciones ultra-limpias para evitar problemas de serialización
29
- this.compilerOptions =
30
- this.createUltraCleanCompilerOptions(compilerOptions);
31
- } /**
32
- * Crea opciones del compilador que respetan la configuración del tsconfig.json
33
- */
34
- createUltraCleanCompilerOptions(options) {
35
- // Usar las opciones del tsconfig.json pasadas desde el hilo principal
36
- const cleanOptions = {
37
- // target: options.target || ts.ScriptTarget.ES2020,
38
- // module: options.module || ts.ModuleKind.ES2020,
39
- // strict: Boolean(options.strict),
40
- // noEmitOnError: Boolean(options.noEmitOnError),
41
- // skipLibCheck: Boolean(options.skipLibCheck !== false), // true por defecto
42
- // skipDefaultLibCheck: Boolean(options.skipDefaultLibCheck !== false), // true por defecto
43
- // allowJs: Boolean(options.allowJs !== false), // true por defecto
44
- // esModuleInterop: Boolean(options.esModuleInterop !== false), // true por defecto
45
- // allowSyntheticDefaultImports: Boolean(
46
- // options.allowSyntheticDefaultImports !== false,
47
- // ), // true por defecto
48
- // declaration: Boolean(options.declaration),
49
- // sourceMap: Boolean(options.sourceMap),
50
- // noImplicitAny: Boolean(options.noImplicitAny),
51
- // noImplicitReturns: Boolean(options.noImplicitReturns),
52
- // noImplicitThis: Boolean(options.noImplicitThis),
53
- // noUnusedLocals: Boolean(options.noUnusedLocals),
54
- // noUnusedParameters: Boolean(options.noUnusedParameters),
55
- // isolatedModules: Boolean(options.isolatedModules !== false), // true por defecto // Usar las librerías especificadas en el tsconfig.json
56
- // lib: Array.isArray(options.lib)
57
- // ? options.lib
58
- // : ['es2020', 'dom', 'dom.iterable'],
59
-
60
- // // Soporte para decorators
61
- // experimentalDecorators: Boolean(
62
- // options.experimentalDecorators !== false,
63
- // ),
64
- // emitDecoratorMetadata: Boolean(
65
- // options.emitDecoratorMetadata !== false,
66
- // ),
67
-
68
- // // Opciones críticas para el worker pero manteniendo compatibilidad
69
- // noLib: false, // Permitir librerías para APIs básicas (DOM, Promise, etc.)
70
- // noResolve: true, // Evitar resolución de módulos compleja pero mantener tipos globales
71
- // suppressOutputPathCheck: true,
72
- // allowNonTsExtensions: true,
73
- ...options,
74
- };
75
-
76
- // console.log(
77
- // '[Worker] Opciones del compilador recibidas:',
78
- // Object.keys(cleanOptions),
79
- // );
80
- return cleanOptions;
81
- }
82
-
83
- addFile(fileName, content) {
84
- const existing = this.files.get(fileName);
85
- this.files.set(fileName, {
86
- version: existing ? existing.version + 1 : 1,
87
- content,
88
- });
89
- }
90
-
91
- getCompilationSettings() {
92
- return this.compilerOptions;
93
- }
94
-
95
- getScriptFileNames() {
96
- return Array.from(this.files.keys());
97
- }
98
-
99
- getScriptVersion(fileName) {
100
- const file = this.files.get(fileName);
101
- return file ? file.version.toString() : '0';
102
- }
103
-
104
- getScriptSnapshot(fileName) {
105
- const file = this.files.get(fileName);
106
- if (file) {
107
- return ts.ScriptSnapshot.fromString(file.content);
108
- }
109
-
110
- // Intentar leer el archivo del sistema de archivos para dependencias
111
- if (fs.existsSync(fileName)) {
112
- try {
113
- const content = fs.readFileSync(fileName, 'utf-8');
114
- return ts.ScriptSnapshot.fromString(content);
115
- } catch {
116
- return undefined;
117
- }
118
- }
119
-
120
- return undefined;
121
- }
122
-
123
- getCurrentDirectory() {
124
- return process.cwd();
125
- }
126
-
127
- getDefaultLibFileName(options) {
128
- return ts.getDefaultLibFilePath(options);
129
- }
130
-
131
- fileExists(filePath) {
132
- return this.files.has(filePath) || fs.existsSync(filePath);
133
- }
134
-
135
- readFile(filePath) {
136
- const file = this.files.get(filePath);
137
- if (file) {
138
- return file.content;
139
- }
140
-
141
- if (fs.existsSync(filePath)) {
142
- try {
143
- return fs.readFileSync(filePath, 'utf-8');
144
- } catch {
145
- return undefined;
146
- }
147
- }
148
-
149
- return undefined;
150
- }
151
-
152
- getNewLine() {
153
- return ts.sys.newLine;
154
- }
155
- }
156
-
157
- /**
158
- * Realiza validación de tipos en el worker thread
159
- */
160
- function validateTypesInWorker(fileName, content, compilerOptions) {
161
- let actualFileName = fileName;
162
- try {
163
- const scriptContent = content;
164
-
165
- // Si el script está vacío o es solo espacios en blanco, no validar
166
- if (!scriptContent.trim()) {
167
- return { diagnostics: [], hasErrors: false };
168
- }
169
-
170
- // Crear Language Service Host
171
- const host = new WorkerTypeScriptLanguageServiceHost(compilerOptions); // Para archivos Vue, crear un archivo virtual .ts
172
- if (fileName.endsWith('.vue')) {
173
- // Crear un nombre de archivo virtual único
174
- const virtualFileName = `${fileName}.ts`;
175
- host.addFile(virtualFileName, scriptContent);
176
- actualFileName = virtualFileName;
177
- // console.log('[Worker] Archivo Vue agregado como:', virtualFileName);
178
- } else {
179
- // Para archivos virtuales, usar el nombre tal como viene
180
- host.addFile(fileName, scriptContent);
181
- actualFileName = fileName;
182
- // console.log('[Worker] Archivo agregado como:', fileName);
183
- }
184
-
185
- // console.log(
186
- // '[Worker] Contenido del archivo:',
187
- // scriptContent.substring(0, 100) + '...',
188
- // );
189
- // console.log('[Worker] Archivos en host:', host.getScriptFileNames());
190
-
191
- // Agregar declaraciones básicas de tipos para Vue si es necesario
192
- if (fileName.endsWith('.vue')) {
193
- // Usar el directorio del archivo actual para las declaraciones
194
- const projectDir = path.dirname(actualFileName);
195
- const vueTypesPath = path.join(projectDir, 'vue-types.d.ts');
196
- const vueTypesDeclaration = `// Declaraciones de tipos Vue para validación
197
- declare global {
198
- function ref<T>(value: T): { value: T };
199
- function reactive<T extends object>(target: T): T;
200
- function computed<T>(getter: () => T): { value: T };
201
- function defineComponent<T>(options: T): T;
202
- function defineProps<T = {}>(): T;
203
- function defineEmits<T = {}>(): T;
204
- function defineExpose<T = {}>(exposed: T): void;
205
- function mergeModels<T>(models: T): T;
206
- function provide<T>(key: string | symbol, value: T): void;
207
- function inject<T>(key: string | symbol, defaultValue?: T): T | undefined;
208
- function useSlots(): { [key: string]: (...args: any[]) => any };
209
- function useAttrs(): { [key: string]: any };
210
- function useModel<T>(modelName: string): { value: T };
211
- function onMounted(fn: () => void): void;
212
- function onUnmounted(fn: () => void): void;
213
- function watch<T>(source: () => T, callback: (newValue: T, oldValue: T) => void): void;
214
- }
215
- export {};`;
216
- host.addFile(vueTypesPath, vueTypesDeclaration);
217
- }
218
-
219
- // Crear Language Service
220
- const languageService = ts.createLanguageService(host);
221
-
222
- try {
223
- // Verificar que el archivo existe en el host antes de solicitar diagnósticos
224
- if (!host.fileExists(actualFileName)) {
225
- return { diagnostics: [], hasErrors: false };
226
- }
227
-
228
-
229
- // Obtener diagnósticos de tipos con manejo de errores
230
- let syntacticDiagnostics = [];
231
- let semanticDiagnostics = [];
232
- try {
233
- syntacticDiagnostics =
234
- languageService.getSyntacticDiagnostics(actualFileName);
235
- // console.log(
236
- // '[Worker] Diagnósticos sintácticos:',
237
- // syntacticDiagnostics.length,
238
- // );
239
- } catch (error) {
240
- console.error(
241
- '[Worker] Error obteniendo diagnósticos sintácticos:',
242
- error.message,
243
- );
244
- }
245
-
246
- try {
247
- semanticDiagnostics =
248
- languageService.getSemanticDiagnostics(actualFileName);
249
- // console.log(
250
- // '[Worker] Diagnósticos semánticos:',
251
- // semanticDiagnostics.length,
252
- // );
253
- } catch (error) {
254
- console.error(
255
- '[Worker] Error obteniendo diagnósticos semánticos:',
256
- error.message,
257
- );
258
- } // Combinar todos los diagnósticos
259
- const allDiagnostics = [
260
- ...syntacticDiagnostics,
261
- ...semanticDiagnostics,
262
- ];
263
-
264
- // console.log(
265
- // '[Worker] Total diagnósticos encontrados:',
266
- // allDiagnostics.length,
267
- // );
268
-
269
- // Log de todos los diagnósticos antes del filtrado
270
- // allDiagnostics.forEach((diag, index) => {
271
- // const messageText = ts.flattenDiagnosticMessageText(
272
- // diag.messageText,
273
- // '\n',
274
- // );
275
- // console.log(
276
- // `[Worker] Diagnóstico ${index + 1}: [${diag.category}] ${messageText}`,
277
- // );
278
- // });
279
-
280
- // Filtrar diagnósticos relevantes
281
- const filteredDiagnostics = allDiagnostics.filter(diag => {
282
- const messageText = ts.flattenDiagnosticMessageText(
283
- diag.messageText,
284
- '\n',
285
- );
286
-
287
- // Solo errores de categoría Error
288
- if (diag.category !== ts.DiagnosticCategory.Error) {
289
- return false;
290
- } // Ignorar SOLO errores específicos de infraestructura Vue y rutas de módulos
291
- return (
292
- !messageText.includes('Cannot find module') &&
293
- !messageText.includes('Could not find source file') &&
294
- !messageText.includes(
295
- "has no exported member 'mergeModels'",
296
- ) &&
297
- !messageText.includes(
298
- "Parameter '$props' implicitly has an 'any' type",
299
- ) &&
300
- !messageText.includes(
301
- "Parameter '$setup' implicitly has an 'any' type",
302
- ) &&
303
- !messageText.includes(
304
- "Parameter '$data' implicitly has an 'any' type",
305
- ) &&
306
- !messageText.includes(
307
- "Parameter '$options' implicitly has an 'any' type",
308
- ) &&
309
- !messageText.includes(
310
- "Parameter '$event' implicitly has an 'any' type",
311
- ) &&
312
- !messageText.includes(
313
- "Parameter '_ctx' implicitly has an 'any' type",
314
- ) &&
315
- !messageText.includes(
316
- "Parameter '_cache' implicitly has an 'any' type",
317
- ) &&
318
- // Ignorar errores específicos de decorators cuando están mal configurados
319
- !messageText.includes(
320
- 'Unable to resolve signature of method decorator when called as an expression',
321
- ) &&
322
- !messageText.includes(
323
- 'The runtime will invoke the decorator with',
324
- ) &&
325
- // NO ignorar errores TS7031 y TS7006 de forma general, solo para parámetros específicos de Vue
326
- // diag.code !== 7031 &&
327
- // diag.code !== 7006 &&
328
- // Ignorar errores TS1241 (decorator signature mismatch) durante desarrollo
329
- diag.code !== 1241 &&
330
- !(
331
- messageText.includes("implicitly has an 'any' type") &&
332
- (messageText.includes('_ctx') ||
333
- messageText.includes('_cache') ||
334
- messageText.includes('$props') ||
335
- messageText.includes('$setup') ||
336
- messageText.includes('__expose') ||
337
- messageText.includes('__emit'))
338
- )
339
- );
340
- });
341
-
342
- return {
343
- diagnostics: filteredDiagnostics,
344
- hasErrors: filteredDiagnostics.length > 0,
345
- };
346
- } catch {
347
- return { diagnostics: [], hasErrors: false };
348
- } finally {
349
- try { languageService.dispose(); } catch { /* ignore dispose errors */ }
350
- }
351
- } catch (error) {
352
- // En caso de error, devolver diagnóstico de error
353
- const errorDiagnostic = {
354
- file: undefined,
355
- start: undefined,
356
- length: undefined,
357
- messageText: `Error en validación de tipos: ${error instanceof Error ? error.message : 'Error desconocido'}`,
358
- category: ts.DiagnosticCategory.Error,
359
- code: 0,
360
- };
361
-
362
- return {
363
- diagnostics: [errorDiagnostic],
364
- hasErrors: true,
365
- };
366
- }
367
- }
368
-
369
- // Escuchar mensajes del proceso principal
370
- if (parentPort) {
371
- parentPort.on('message', message => {
372
- try {
373
- // console.log('[Worker] Mensaje recibido:', {
374
- // id: message.id,
375
- // fileName: message.fileName,
376
- // contentLength: message.content?.length,
377
- // compilerOptions: Object.keys(message.compilerOptions || {}),
378
- // });
379
-
380
- const { id, fileName, content, compilerOptions } = message;
381
-
382
- // Validar que el mensaje tiene la estructura correcta
383
- if (!id || !fileName || typeof content !== 'string') {
384
- parentPort.postMessage({
385
- id: id || 'unknown',
386
- success: false,
387
- error: 'Mensaje del worker inválido',
388
- });
389
- return;
390
- }
391
-
392
- // Realizar validación de tipos
393
- const result = validateTypesInWorker(
394
- fileName,
395
- content,
396
- compilerOptions,
397
- ); // Serializar diagnósticos de forma segura
398
- const serializableDiagnostics = result.diagnostics.map(diag => ({
399
- category: diag.category,
400
- code: diag.code,
401
- messageText:
402
- typeof diag.messageText === 'string'
403
- ? diag.messageText
404
- : ts.flattenDiagnosticMessageText(
405
- diag.messageText,
406
- '\n',
407
- ),
408
- file: diag.file
409
- ? {
410
- fileName: diag.file.fileName,
411
- text: diag.file.text,
412
- }
413
- : undefined,
414
- start: diag.start,
415
- length: diag.length,
416
- }));
417
-
418
- // Enviar respuesta al proceso principal
419
- // console.log('[Worker] Enviando respuesta:', {
420
- // id,
421
- // success: true,
422
- // diagnosticsCount: serializableDiagnostics.length,
423
- // hasErrors: result.hasErrors,
424
- // });
425
-
426
- parentPort.postMessage({
427
- id,
428
- success: true,
429
- diagnostics: serializableDiagnostics,
430
- hasErrors: result.hasErrors,
431
- });
432
- } catch (error) {
433
- // Enviar error al proceso principal
434
- parentPort.postMessage({
435
- id: message?.id || 'unknown',
436
- success: false,
437
- error:
438
- error instanceof Error
439
- ? error.message
440
- : 'Error desconocido en worker',
441
- });
442
- }
443
- });
444
- }
445
-
446
- // Manejar errores no capturados en el worker
447
- process.on('uncaughtException', error => {
448
- console.error('[Worker] Error no capturado en TypeScript worker:', error);
449
- if (parentPort) {
450
- parentPort.postMessage({
451
- id: 'error',
452
- success: false,
453
- error: `Error crítico en worker: ${error.message}`,
454
- });
455
- }
456
- process.exit(1);
457
- });
458
-
459
- // Log de confirmación de que el worker está listo
460
- // console.log('[Worker] TypeScript Worker Thread listo para recibir mensajes');
461
-
462
- // Señal de que el worker está listo
463
- if (parentPort) {
464
- parentPort.postMessage({
465
- id: 'worker-ready',
466
- success: true,
467
- message: 'TypeScript worker iniciado correctamente',
468
- });
469
- }
1
+ /**
2
+ * TypeScript Worker Thread - Ejecuta type checking asíncrono
3
+ * Este archivo se ejecuta en un worker thread separado para validación de tipos
4
+ */
5
+
6
+ const fs = require('node:fs');
7
+ const path = require('node:path');
8
+ const { parentPort } = require('node:worker_threads');
9
+
10
+ // Debug: Log de inicio del worker
11
+ // console.log('[Worker] TypeScript Worker Thread iniciado');
12
+
13
+ let ts;
14
+ try {
15
+ ts = require('typescript');
16
+ // console.log('[Worker] TypeScript cargado exitosamente');
17
+ } catch (error) {
18
+ console.error('[Worker] Error cargando TypeScript:', error);
19
+ process.exit(1);
20
+ }
21
+
22
+ /**
23
+ * Language Service Host para validación de tipos en el worker
24
+ */
25
+ class WorkerTypeScriptLanguageServiceHost {
26
+ constructor(compilerOptions) {
27
+ this.files = new Map();
28
+ // Crear opciones ultra-limpias para evitar problemas de serialización
29
+ this.compilerOptions =
30
+ this.createUltraCleanCompilerOptions(compilerOptions);
31
+ } /**
32
+ * Crea opciones del compilador que respetan la configuración del tsconfig.json
33
+ */
34
+ createUltraCleanCompilerOptions(options) {
35
+ // Usar las opciones del tsconfig.json pasadas desde el hilo principal
36
+ const cleanOptions = {
37
+ // target: options.target || ts.ScriptTarget.ES2020,
38
+ // module: options.module || ts.ModuleKind.ES2020,
39
+ // strict: Boolean(options.strict),
40
+ // noEmitOnError: Boolean(options.noEmitOnError),
41
+ // skipLibCheck: Boolean(options.skipLibCheck !== false), // true por defecto
42
+ // skipDefaultLibCheck: Boolean(options.skipDefaultLibCheck !== false), // true por defecto
43
+ // allowJs: Boolean(options.allowJs !== false), // true por defecto
44
+ // esModuleInterop: Boolean(options.esModuleInterop !== false), // true por defecto
45
+ // allowSyntheticDefaultImports: Boolean(
46
+ // options.allowSyntheticDefaultImports !== false,
47
+ // ), // true por defecto
48
+ // declaration: Boolean(options.declaration),
49
+ // sourceMap: Boolean(options.sourceMap),
50
+ // noImplicitAny: Boolean(options.noImplicitAny),
51
+ // noImplicitReturns: Boolean(options.noImplicitReturns),
52
+ // noImplicitThis: Boolean(options.noImplicitThis),
53
+ // noUnusedLocals: Boolean(options.noUnusedLocals),
54
+ // noUnusedParameters: Boolean(options.noUnusedParameters),
55
+ // isolatedModules: Boolean(options.isolatedModules !== false), // true por defecto // Usar las librerías especificadas en el tsconfig.json
56
+ // lib: Array.isArray(options.lib)
57
+ // ? options.lib
58
+ // : ['es2020', 'dom', 'dom.iterable'],
59
+
60
+ // // Soporte para decorators
61
+ // experimentalDecorators: Boolean(
62
+ // options.experimentalDecorators !== false,
63
+ // ),
64
+ // emitDecoratorMetadata: Boolean(
65
+ // options.emitDecoratorMetadata !== false,
66
+ // ),
67
+
68
+ // // Opciones críticas para el worker pero manteniendo compatibilidad
69
+ // noLib: false, // Permitir librerías para APIs básicas (DOM, Promise, etc.)
70
+ // noResolve: true, // Evitar resolución de módulos compleja pero mantener tipos globales
71
+ // suppressOutputPathCheck: true,
72
+ // allowNonTsExtensions: true,
73
+ ...options,
74
+ };
75
+
76
+ // console.log(
77
+ // '[Worker] Opciones del compilador recibidas:',
78
+ // Object.keys(cleanOptions),
79
+ // );
80
+ return cleanOptions;
81
+ }
82
+
83
+ addFile(fileName, content) {
84
+ const existing = this.files.get(fileName);
85
+ this.files.set(fileName, {
86
+ version: existing ? existing.version + 1 : 1,
87
+ content,
88
+ });
89
+ }
90
+
91
+ getCompilationSettings() {
92
+ return this.compilerOptions;
93
+ }
94
+
95
+ getScriptFileNames() {
96
+ return Array.from(this.files.keys());
97
+ }
98
+
99
+ getScriptVersion(fileName) {
100
+ const file = this.files.get(fileName);
101
+ return file ? file.version.toString() : '0';
102
+ }
103
+
104
+ getScriptSnapshot(fileName) {
105
+ const file = this.files.get(fileName);
106
+ if (file) {
107
+ return ts.ScriptSnapshot.fromString(file.content);
108
+ }
109
+
110
+ // Intentar leer el archivo del sistema de archivos para dependencias
111
+ if (fs.existsSync(fileName)) {
112
+ try {
113
+ const content = fs.readFileSync(fileName, 'utf-8');
114
+ return ts.ScriptSnapshot.fromString(content);
115
+ } catch {
116
+ return undefined;
117
+ }
118
+ }
119
+
120
+ return undefined;
121
+ }
122
+
123
+ getCurrentDirectory() {
124
+ return process.cwd();
125
+ }
126
+
127
+ getDefaultLibFileName(options) {
128
+ return ts.getDefaultLibFilePath(options);
129
+ }
130
+
131
+ fileExists(filePath) {
132
+ return this.files.has(filePath) || fs.existsSync(filePath);
133
+ }
134
+
135
+ readFile(filePath) {
136
+ const file = this.files.get(filePath);
137
+ if (file) {
138
+ return file.content;
139
+ }
140
+
141
+ if (fs.existsSync(filePath)) {
142
+ try {
143
+ return fs.readFileSync(filePath, 'utf-8');
144
+ } catch {
145
+ return undefined;
146
+ }
147
+ }
148
+
149
+ return undefined;
150
+ }
151
+
152
+ getNewLine() {
153
+ return ts.sys.newLine;
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Language Service persistente para reusar entre tareas (evita cold-start de 200-500ms por tarea)
159
+ * Se resetea periódicamente para controlar el uso de memoria
160
+ */
161
+ let _persistentHost = null;
162
+ let _persistentLS = null;
163
+ let _persistentCompilerOptionsStr = null;
164
+ let _tasksSinceReset = 0;
165
+ const MAX_TASKS_BEFORE_LS_RESET = 50; // Resetear cada 50 tareas para controlar memoria
166
+
167
+ function getOrResetLanguageService(compilerOptions) {
168
+ const optionsStr = JSON.stringify(compilerOptions);
169
+ const needsReset =
170
+ !_persistentLS ||
171
+ optionsStr !== _persistentCompilerOptionsStr ||
172
+ _tasksSinceReset >= MAX_TASKS_BEFORE_LS_RESET;
173
+
174
+ if (needsReset) {
175
+ if (_persistentLS) {
176
+ try {
177
+ _persistentLS.dispose();
178
+ } catch {
179
+ /* ignore */
180
+ }
181
+ _persistentLS = null;
182
+ }
183
+ _persistentHost = new WorkerTypeScriptLanguageServiceHost(
184
+ compilerOptions,
185
+ );
186
+ _persistentLS = ts.createLanguageService(_persistentHost);
187
+ _persistentCompilerOptionsStr = optionsStr;
188
+ _tasksSinceReset = 0;
189
+ }
190
+
191
+ _tasksSinceReset++;
192
+ return { host: _persistentHost, ls: _persistentLS };
193
+ }
194
+
195
+ /**
196
+ * Realiza validación de tipos en el worker thread
197
+ */
198
+ function validateTypesInWorker(fileName, content, compilerOptions) {
199
+ let actualFileName = fileName;
200
+ try {
201
+ const scriptContent = content;
202
+
203
+ // Si el script está vacío o es solo espacios en blanco, no validar
204
+ if (!scriptContent.trim()) {
205
+ return { diagnostics: [], hasErrors: false };
206
+ }
207
+
208
+ // Obtener o crear Language Service persistente (rápido después del primer uso)
209
+ const { host, ls: languageService } =
210
+ getOrResetLanguageService(compilerOptions);
211
+
212
+ // Para archivos Vue, crear un archivo virtual .ts
213
+ if (fileName.endsWith('.vue')) {
214
+ const virtualFileName = `${fileName}.ts`;
215
+ host.addFile(virtualFileName, scriptContent);
216
+ actualFileName = virtualFileName;
217
+
218
+ // Agregar declaraciones de tipos Vue si es necesario
219
+ const projectDir = path.dirname(actualFileName);
220
+ const vueTypesPath = path.join(projectDir, 'vue-types.d.ts');
221
+ const vueTypesDeclaration = `// Declaraciones de tipos Vue para validación
222
+ declare global {
223
+ function ref<T>(value: T): { value: T };
224
+ function reactive<T extends object>(target: T): T;
225
+ function computed<T>(getter: () => T): { value: T };
226
+ function defineComponent<T>(options: T): T;
227
+ function defineProps<T = {}>(): T;
228
+ function defineEmits<T = {}>(): T;
229
+ function defineExpose<T = {}>(exposed: T): void;
230
+ function mergeModels<T>(models: T): T;
231
+ function provide<T>(key: string | symbol, value: T): void;
232
+ function inject<T>(key: string | symbol, defaultValue?: T): T | undefined;
233
+ function useSlots(): { [key: string]: (...args: any[]) => any };
234
+ function useAttrs(): { [key: string]: any };
235
+ function useModel<T>(modelName: string): { value: T };
236
+ function onMounted(fn: () => void): void;
237
+ function onUnmounted(fn: () => void): void;
238
+ function watch<T>(source: () => T, callback: (newValue: T, oldValue: T) => void): void;
239
+ }
240
+ export {};`;
241
+ host.addFile(vueTypesPath, vueTypesDeclaration);
242
+
243
+ // Eliminar archivos de tareas anteriores para evitar que el programa crezca.
244
+ // Se hace DESPUÉS de addFile para que la versión se incremente correctamente
245
+ // (evita que el LS use resultados cacheados de versiones anteriores).
246
+ for (const key of host.files.keys()) {
247
+ if (key !== virtualFileName && key !== vueTypesPath) {
248
+ host.files.delete(key);
249
+ }
250
+ }
251
+ } else {
252
+ host.addFile(fileName, scriptContent);
253
+ actualFileName = fileName;
254
+
255
+ // Eliminar archivos de tareas anteriores para evitar que el programa crezca.
256
+ // Se hace DESPUÉS de addFile para que la versión se incremente correctamente.
257
+ for (const key of host.files.keys()) {
258
+ if (key !== fileName) {
259
+ host.files.delete(key);
260
+ }
261
+ }
262
+ }
263
+
264
+ try {
265
+ // Verificar que el archivo existe en el host antes de solicitar diagnósticos
266
+ if (!host.fileExists(actualFileName)) {
267
+ return { diagnostics: [], hasErrors: false };
268
+ }
269
+
270
+ // Obtener diagnósticos de tipos con manejo de errores
271
+ let syntacticDiagnostics = [];
272
+ let semanticDiagnostics = [];
273
+ try {
274
+ syntacticDiagnostics =
275
+ languageService.getSyntacticDiagnostics(actualFileName);
276
+ } catch (error) {
277
+ console.error(
278
+ '[Worker] Error obteniendo diagnósticos sintácticos:',
279
+ error.message,
280
+ );
281
+ }
282
+
283
+ try {
284
+ semanticDiagnostics =
285
+ languageService.getSemanticDiagnostics(actualFileName);
286
+ } catch (error) {
287
+ console.error(
288
+ '[Worker] Error obteniendo diagnósticos semánticos:',
289
+ error.message,
290
+ );
291
+ }
292
+
293
+ const allDiagnostics = [
294
+ ...syntacticDiagnostics,
295
+ ...semanticDiagnostics,
296
+ ];
297
+
298
+ // Filtrar diagnósticos relevantes
299
+ const filteredDiagnostics = allDiagnostics.filter(diag => {
300
+ const messageText = ts.flattenDiagnosticMessageText(
301
+ diag.messageText,
302
+ '\n',
303
+ );
304
+
305
+ // Solo errores de categoría Error
306
+ if (diag.category !== ts.DiagnosticCategory.Error) {
307
+ return false;
308
+ }
309
+
310
+ // Ignorar errores de infraestructura Vue y rutas de módulos
311
+ return (
312
+ !messageText.includes('Cannot find module') &&
313
+ !messageText.includes('Could not find source file') &&
314
+ !messageText.includes(
315
+ "has no exported member 'mergeModels'",
316
+ ) &&
317
+ !messageText.includes(
318
+ "Parameter '$props' implicitly has an 'any' type",
319
+ ) &&
320
+ !messageText.includes(
321
+ "Parameter '$setup' implicitly has an 'any' type",
322
+ ) &&
323
+ !messageText.includes(
324
+ "Parameter '$data' implicitly has an 'any' type",
325
+ ) &&
326
+ !messageText.includes(
327
+ "Parameter '$options' implicitly has an 'any' type",
328
+ ) &&
329
+ !messageText.includes(
330
+ "Parameter '$event' implicitly has an 'any' type",
331
+ ) &&
332
+ !messageText.includes(
333
+ "Parameter '_ctx' implicitly has an 'any' type",
334
+ ) &&
335
+ !messageText.includes(
336
+ "Parameter '_cache' implicitly has an 'any' type",
337
+ ) &&
338
+ !messageText.includes(
339
+ 'Unable to resolve signature of method decorator when called as an expression',
340
+ ) &&
341
+ !messageText.includes(
342
+ 'The runtime will invoke the decorator with',
343
+ ) &&
344
+ diag.code !== 1241 &&
345
+ !(
346
+ messageText.includes("implicitly has an 'any' type") &&
347
+ (messageText.includes('_ctx') ||
348
+ messageText.includes('_cache') ||
349
+ messageText.includes('$props') ||
350
+ messageText.includes('$setup') ||
351
+ messageText.includes('__expose') ||
352
+ messageText.includes('__emit'))
353
+ )
354
+ );
355
+ });
356
+
357
+ return {
358
+ diagnostics: filteredDiagnostics,
359
+ hasErrors: filteredDiagnostics.length > 0,
360
+ };
361
+ } catch {
362
+ return { diagnostics: [], hasErrors: false };
363
+ }
364
+ } catch (error) {
365
+ // En caso de error, devolver diagnóstico de error
366
+ const errorDiagnostic = {
367
+ file: undefined,
368
+ start: undefined,
369
+ length: undefined,
370
+ messageText: `Error en validación de tipos: ${error instanceof Error ? error.message : 'Error desconocido'}`,
371
+ category: ts.DiagnosticCategory.Error,
372
+ code: 0,
373
+ };
374
+
375
+ return {
376
+ diagnostics: [errorDiagnostic],
377
+ hasErrors: true,
378
+ };
379
+ }
380
+ }
381
+
382
+ // Escuchar mensajes del proceso principal
383
+ if (parentPort) {
384
+ parentPort.on('message', message => {
385
+ try {
386
+ // console.log('[Worker] Mensaje recibido:', {
387
+ // id: message.id,
388
+ // fileName: message.fileName,
389
+ // contentLength: message.content?.length,
390
+ // compilerOptions: Object.keys(message.compilerOptions || {}),
391
+ // });
392
+
393
+ const { id, fileName, content, compilerOptions } = message;
394
+
395
+ // Validar que el mensaje tiene la estructura correcta
396
+ if (!id || !fileName || typeof content !== 'string') {
397
+ parentPort.postMessage({
398
+ id: id || 'unknown',
399
+ success: false,
400
+ error: 'Mensaje del worker inválido',
401
+ });
402
+ return;
403
+ }
404
+
405
+ // Realizar validación de tipos
406
+ const result = validateTypesInWorker(
407
+ fileName,
408
+ content,
409
+ compilerOptions,
410
+ ); // Serializar diagnósticos de forma segura
411
+ const serializableDiagnostics = result.diagnostics.map(diag => ({
412
+ category: diag.category,
413
+ code: diag.code,
414
+ messageText:
415
+ typeof diag.messageText === 'string'
416
+ ? diag.messageText
417
+ : ts.flattenDiagnosticMessageText(
418
+ diag.messageText,
419
+ '\n',
420
+ ),
421
+ file: diag.file
422
+ ? {
423
+ fileName: diag.file.fileName,
424
+ text: diag.file.text,
425
+ }
426
+ : undefined,
427
+ start: diag.start,
428
+ length: diag.length,
429
+ }));
430
+
431
+ // Enviar respuesta al proceso principal
432
+ // console.log('[Worker] Enviando respuesta:', {
433
+ // id,
434
+ // success: true,
435
+ // diagnosticsCount: serializableDiagnostics.length,
436
+ // hasErrors: result.hasErrors,
437
+ // });
438
+
439
+ parentPort.postMessage({
440
+ id,
441
+ success: true,
442
+ diagnostics: serializableDiagnostics,
443
+ hasErrors: result.hasErrors,
444
+ });
445
+ } catch (error) {
446
+ // Enviar error al proceso principal
447
+ parentPort.postMessage({
448
+ id: message?.id || 'unknown',
449
+ success: false,
450
+ error:
451
+ error instanceof Error
452
+ ? error.message
453
+ : 'Error desconocido en worker',
454
+ });
455
+ }
456
+ });
457
+ }
458
+
459
+ // Manejar errores no capturados en el worker
460
+ process.on('uncaughtException', error => {
461
+ console.error('[Worker] Error no capturado en TypeScript worker:', error);
462
+ if (parentPort) {
463
+ parentPort.postMessage({
464
+ id: 'error',
465
+ success: false,
466
+ error: `Error crítico en worker: ${error.message}`,
467
+ });
468
+ }
469
+ process.exit(1);
470
+ });
471
+
472
+ // Log de confirmación de que el worker está listo
473
+ // console.log('[Worker] TypeScript Worker Thread listo para recibir mensajes');
474
+
475
+ // Señal de que el worker está listo
476
+ if (parentPort) {
477
+ parentPort.postMessage({
478
+ id: 'worker-ready',
479
+ success: true,
480
+ message: 'TypeScript worker iniciado correctamente',
481
+ });
482
+ }