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.
@@ -0,0 +1,378 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import * as process from 'node:process';
4
+ import { env } from 'node:process';
5
+ import * as typescript from 'typescript';
6
+ import { createUnifiedErrorMessage, parseTypeScriptErrors, } from './typescript-error-parser.js';
7
+ import { TypeScriptWorkerPool } from './typescript-worker-pool.js';
8
+ /**
9
+ * Cache para la configuración de TypeScript para evitar lecturas repetidas
10
+ */
11
+ let configCache = {};
12
+ /**
13
+ * Carga la configuración de TypeScript desde tsconfig.json
14
+ * @param fileName - Nombre del archivo para buscar el tsconfig.json relativo
15
+ * @returns Opciones del compilador TypeScript
16
+ */
17
+ export const loadTypeScriptConfig = (fileName) => {
18
+ const fileDir = path.dirname(fileName);
19
+ const configPath = typescript.findConfigFile(fileDir, typescript.sys.fileExists, 'tsconfig.json') || 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 } = typescript.readConfigFile(configPath, typescript.sys.readFile);
28
+ if (!configError) {
29
+ const parsedConfig = typescript.parseJsonConfigFileContent(config, typescript.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: typescript.ScriptTarget.ES2020,
63
+ module: typescript.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 = typescript.ScriptTarget.ES2020, module = typescript.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 typescript.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
+ ? typescript.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 typescript.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 typescript.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 typescript.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
+ } // Crear Language Service
234
+ const languageService = typescript.createLanguageService(host);
235
+ // Verificar existencia del archivo
236
+ if (!host.fileExists(actualFileName)) {
237
+ return { diagnostics: [], hasErrors: false };
238
+ }
239
+ // Obtener diagnósticos con manejo de errores optimizado
240
+ const allDiagnostics = [];
241
+ try {
242
+ allDiagnostics.push(...languageService.getSyntacticDiagnostics(actualFileName));
243
+ allDiagnostics.push(...languageService.getSemanticDiagnostics(actualFileName));
244
+ }
245
+ catch {
246
+ // Ignorar errores de diagnósticos
247
+ return { diagnostics: [], hasErrors: false };
248
+ } // Filtrado optimizado de diagnósticos
249
+ const filteredDiagnostics = allDiagnostics.filter(diag => {
250
+ if (diag.category !== typescript.DiagnosticCategory.Error)
251
+ return false;
252
+ const messageText = typescript.flattenDiagnosticMessageText(diag.messageText, '\n');
253
+ // Lista optimizada de patrones a ignorar
254
+ const ignorePatterns = [
255
+ 'Cannot find module',
256
+ 'Could not find source file',
257
+ "Parameter '$props' implicitly has an 'any' type",
258
+ "Parameter '$setup' implicitly has an 'any' type",
259
+ "Parameter '_ctx' implicitly has an 'any' type",
260
+ "Parameter '_cache' implicitly has an 'any' type",
261
+ ];
262
+ return !ignorePatterns.some(pattern => messageText.includes(pattern));
263
+ });
264
+ return {
265
+ diagnostics: filteredDiagnostics,
266
+ hasErrors: filteredDiagnostics.length > 0,
267
+ };
268
+ }
269
+ catch (error) {
270
+ // Error handling simplificado
271
+ return {
272
+ diagnostics: [
273
+ {
274
+ file: undefined,
275
+ start: undefined,
276
+ length: undefined,
277
+ messageText: `Error en validación de tipos: ${error instanceof Error ? error.message : 'Error desconocido'}`,
278
+ category: typescript.DiagnosticCategory.Error,
279
+ code: 0,
280
+ },
281
+ ],
282
+ hasErrors: true,
283
+ };
284
+ }
285
+ };
286
+ /**
287
+ * Valida tipos en archivos Vue antes de la compilación
288
+ * @param vueContent - Contenido del archivo Vue
289
+ * @param fileName - Nombre del archivo Vue
290
+ * @returns Resultado de la validación de tipos
291
+ */
292
+ export const validateVueTypes = (vueContent, fileName) => {
293
+ const compilerOptions = loadTypeScriptConfig(fileName);
294
+ return validateTypesWithLanguageService(fileName, vueContent, compilerOptions);
295
+ };
296
+ /**
297
+ * Precompila el código TypeScript con pipeline optimizado para máxima performance.
298
+ * @param {string} data - El código TypeScript a precompilar.
299
+ * @param {string} fileName - El nombre del archivo que contiene el código typescript.
300
+ * @returns {Promise<CompileResult>} - Un objeto con el código precompilado o un error.
301
+ */
302
+ export const preCompileTS = async (data, fileName) => {
303
+ try {
304
+ // Validación temprana: contenido vacío
305
+ if (!data.trim()) {
306
+ return { error: null, data: data, lang: 'ts' };
307
+ }
308
+ // Cargar configuración de TypeScript desde tsconfig.json
309
+ const compilerOptions = loadTypeScriptConfig(fileName); // PASO 1: Transpilación rápida con detección de errores críticos
310
+ const transpileResult = typescript.transpileModule(data, {
311
+ compilerOptions: {
312
+ ...compilerOptions,
313
+ noLib: true,
314
+ skipLibCheck: true,
315
+ isolatedModules: true,
316
+ },
317
+ fileName,
318
+ reportDiagnostics: true,
319
+ });
320
+ // const transpileResult = traspileTStoJS(
321
+ // fileName,data)
322
+ // Verificar errores críticos de sintaxis
323
+ if (transpileResult.diagnostics?.length) {
324
+ const criticalErrors = transpileResult.diagnostics.filter((diag) => {
325
+ if (diag.category !== typescript.DiagnosticCategory.Error)
326
+ return false;
327
+ const messageText = typescript.flattenDiagnosticMessageText(diag.messageText, '\n');
328
+ // Ignorar errores de módulo no encontrado
329
+ return (!messageText.includes('Cannot find module') &&
330
+ !messageText.includes('Could not find source file') &&
331
+ diag.code !== 2307 &&
332
+ diag.code !== 6059);
333
+ });
334
+ if (criticalErrors.length > 0) {
335
+ const errorMessage = createUnifiedErrorMessage(parseTypeScriptErrors(criticalErrors, fileName, data));
336
+ return {
337
+ error: new Error(errorMessage),
338
+ data: null,
339
+ lang: 'ts',
340
+ };
341
+ }
342
+ } // PASO 2: Type checking opcional (solo si está habilitado)
343
+ if (env.typeCheck === 'true') {
344
+ try {
345
+ const workerPool = TypeScriptWorkerPool.getInstance();
346
+ const serializableOptions = createSerializableCompilerOptions(compilerOptions);
347
+ const typeCheckResult = await workerPool.typeCheck(fileName, data, serializableOptions);
348
+ if (typeCheckResult.hasErrors) {
349
+ const errorMessage = createUnifiedErrorMessage(parseTypeScriptErrors(typeCheckResult.diagnostics, fileName, data));
350
+ return {
351
+ error: new Error(errorMessage),
352
+ data: null,
353
+ lang: 'ts',
354
+ };
355
+ }
356
+ }
357
+ catch (typeCheckError) {
358
+ // Type checking falla, pero continuar con transpilación
359
+ console.warn('[preCompileTS] ❌ Type checking falló, usando transpilación sin verificación de tipos:', typeCheckError);
360
+ }
361
+ }
362
+ // PASO 3: Devolver resultado optimizado
363
+ const output = transpileResult.outputText;
364
+ // Limpiar output vacío
365
+ if (output.trim() === 'export {};') {
366
+ return { error: null, data: '', lang: 'ts' };
367
+ }
368
+ return { error: null, data: output, lang: 'ts' };
369
+ }
370
+ catch (error) {
371
+ return {
372
+ error: error instanceof Error ? error : new Error('Error desconocido'),
373
+ data: null,
374
+ lang: 'ts',
375
+ };
376
+ }
377
+ };
378
+ //# sourceMappingURL=typescript-manager.js.map
@@ -4,7 +4,8 @@
4
4
  */
5
5
  import * as fs from 'node:fs';
6
6
  import * as path from 'node:path';
7
- import * as ts from 'typescript';
7
+ import * as process from 'node:process';
8
+ import * as typescript from 'typescript';
8
9
  /**
9
10
  * Language Service Host para validación de tipos eficiente
10
11
  */
@@ -34,13 +35,12 @@ class TypeScriptLanguageServiceHost {
34
35
  getScriptSnapshot(fileName) {
35
36
  const file = this.files.get(fileName);
36
37
  if (file) {
37
- return ts.ScriptSnapshot.fromString(file.content);
38
- }
39
- // Intentar leer el archivo del sistema de archivos para dependencias
38
+ return typescript.ScriptSnapshot.fromString(file.content);
39
+ } // Intentar leer el archivo del sistema de archivos para dependencias
40
40
  if (fs.existsSync(fileName)) {
41
41
  try {
42
42
  const content = fs.readFileSync(fileName, 'utf-8');
43
- return ts.ScriptSnapshot.fromString(content);
43
+ return typescript.ScriptSnapshot.fromString(content);
44
44
  }
45
45
  catch {
46
46
  return undefined;
@@ -52,7 +52,7 @@ class TypeScriptLanguageServiceHost {
52
52
  return process.cwd();
53
53
  }
54
54
  getDefaultLibFileName(options) {
55
- return ts.getDefaultLibFilePath(options);
55
+ return typescript.getDefaultLibFilePath(options);
56
56
  }
57
57
  fileExists(path) {
58
58
  return this.files.has(path) || fs.existsSync(path);
@@ -73,7 +73,7 @@ class TypeScriptLanguageServiceHost {
73
73
  return undefined;
74
74
  }
75
75
  getNewLine() {
76
- return ts.sys.newLine;
76
+ return typescript.sys.newLine;
77
77
  }
78
78
  }
79
79
  /**
@@ -128,9 +128,8 @@ export const validateTypesWithLanguageService = (fileName, content, compilerOpti
128
128
  }
129
129
  export {}; `;
130
130
  host.addFile(vueTypesPath, vueTypesDeclaration);
131
- }
132
- // Crear Language Service
133
- const languageService = ts.createLanguageService(host);
131
+ } // Crear Language Service
132
+ const languageService = typescript.createLanguageService(host);
134
133
  try {
135
134
  // Verificar que el archivo existe en el host antes de solicitar diagnósticos
136
135
  if (!host.fileExists(actualFileName)) {
@@ -157,12 +156,11 @@ export const validateTypesWithLanguageService = (fileName, content, compilerOpti
157
156
  const allDiagnostics = [
158
157
  ...syntacticDiagnostics,
159
158
  ...semanticDiagnostics,
160
- ];
161
- // Filtrar diagnósticos relevantes
159
+ ]; // Filtrar diagnósticos relevantes
162
160
  const filteredDiagnostics = allDiagnostics.filter((diag) => {
163
- const messageText = ts.flattenDiagnosticMessageText(diag.messageText, '\n');
161
+ const messageText = typescript.flattenDiagnosticMessageText(diag.messageText, '\n');
164
162
  // Solo errores de categoría Error
165
- if (diag.category !== ts.DiagnosticCategory.Error) {
163
+ if (diag.category !== typescript.DiagnosticCategory.Error) {
166
164
  return false;
167
165
  }
168
166
  // Ignorar SOLO errores específicos de infraestructura Vue y rutas de módulos
@@ -208,7 +206,7 @@ export const validateTypesWithLanguageService = (fileName, content, compilerOpti
208
206
  start: undefined,
209
207
  length: undefined,
210
208
  messageText: `Error en validación de tipos: ${error instanceof Error ? error.message : 'Error desconocido'}`,
211
- category: ts.DiagnosticCategory.Error,
209
+ category: typescript.DiagnosticCategory.Error,
212
210
  code: 0,
213
211
  };
214
212
  return {