versacompiler 2.3.3 → 2.3.5

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.
@@ -1,12 +1,18 @@
1
+ import { readFileSync } from 'node:fs';
1
2
  import * as typescript from 'typescript';
2
3
  /**
3
4
  * Parsea errores de TypeScript y los convierte a un formato limpio
4
5
  * que incluye solo: archivo, mensaje, severidad y ubicación como ayuda
6
+ * 🚀 OPTIMIZADO: Sin split preventivo, sin enhance - solo lo esencial para máxima velocidad
7
+ * @param scriptInfo - Información de extracción de script para archivos Vue (opcional)
5
8
  */
6
- export function parseTypeScriptErrors(diagnostics, fileName, sourceCode) {
9
+ export function parseTypeScriptErrors(diagnostics, fileName, sourceCode, scriptInfo) {
7
10
  return diagnostics.map(diagnostic => {
8
- // Usar el mejorador de errores para obtener mensaje detallado
9
- const enhancedMessage = enhanceErrorMessage(diagnostic, fileName, sourceCode); // Determinar la severidad
11
+ // Extraer mensaje básico sin enhance (más rápido)
12
+ const message = typeof diagnostic.messageText === 'string'
13
+ ? diagnostic.messageText
14
+ : typescript.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
15
+ const cleanedMessage = cleanErrorMessage(message); // Determinar la severidad
10
16
  let severity;
11
17
  switch (diagnostic.category) {
12
18
  case typescript.DiagnosticCategory.Error:
@@ -20,32 +26,102 @@ export function parseTypeScriptErrors(diagnostics, fileName, sourceCode) {
20
26
  break;
21
27
  } // Construir información de ubicación limpia
22
28
  let help = `Código TS${diagnostic.code}`;
23
- if (diagnostic.file && diagnostic.start !== undefined) {
24
- const sourceFile = diagnostic.file;
25
- // Verificar que el método getLineAndCharacterOfPosition existe (para compatibilidad con mocks)
26
- if (typeof sourceFile.getLineAndCharacterOfPosition === 'function') {
29
+ if (diagnostic.start !== undefined) {
30
+ // Intentar usar el sourceFile si está disponible
31
+ if (diagnostic.file &&
32
+ typeof diagnostic.file.getLineAndCharacterOfPosition ===
33
+ 'function') {
27
34
  try {
28
- const lineAndChar = sourceFile.getLineAndCharacterOfPosition(diagnostic.start);
29
- help += ` | Línea ${lineAndChar.line + 1}, Columna ${lineAndChar.character + 1}`;
35
+ const lineAndChar = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
36
+ // Ajustar línea si es un archivo Vue con script extraído
37
+ const adjustedLine = scriptInfo
38
+ ? lineAndChar.line + scriptInfo.startLine
39
+ : lineAndChar.line + 1;
40
+ help += ` | Línea ${adjustedLine}, Columna ${lineAndChar.character + 1}`;
30
41
  }
31
42
  catch {
32
- // Si falla, solo mostrar la posición de carácter
33
- help += ` | Posición ${diagnostic.start}`;
43
+ // Fallback: calcular posición leyendo el archivo
44
+ try {
45
+ const content = readFileSync(fileName, 'utf-8');
46
+ const lines = content.split('\n');
47
+ const { line, column } = getLineAndColumnFromOffset(lines, diagnostic.start);
48
+ help += ` | Línea ${line}, Columna ${column}`;
49
+ }
50
+ catch {
51
+ // Último fallback: solo mostrar posición
52
+ help += ` | Posición ${diagnostic.start}`;
53
+ }
34
54
  }
35
55
  }
36
56
  else {
37
- // Fallback para cuando no está disponible el método (como en tests)
38
- help += ` | Posición ${diagnostic.start}`;
57
+ // Fallback: calcular posición y mostrar snippet de código
58
+ try {
59
+ // Para archivos Vue con sourceCode, extraer el snippet del código compilado
60
+ if (scriptInfo && sourceCode) {
61
+ const compiledLines = sourceCode.split('\n');
62
+ const { line: compiledLine, column } = getLineAndColumnFromOffset(compiledLines, diagnostic.start);
63
+ // Extraer la línea donde está el error
64
+ const errorLine = compiledLines[compiledLine - 1]?.trim() || '';
65
+ // Truncar si es muy largo, centrando en el punto del error
66
+ const maxSnippetLength = 80;
67
+ let snippet = errorLine;
68
+ if (snippet.length > maxSnippetLength) {
69
+ // Tomar alrededor del punto del error
70
+ const start = Math.max(0, column - 40);
71
+ const end = Math.min(snippet.length, column + 40);
72
+ snippet =
73
+ (start > 0 ? '...' : '') +
74
+ snippet.substring(start, end) +
75
+ (end < snippet.length ? '...' : '');
76
+ }
77
+ // Para archivos Vue, mostrar snippet para buscar
78
+ help += ` | Buscar en archivo: "${snippet}"`;
79
+ }
80
+ else {
81
+ // No es Vue o no tenemos sourceCode, usar el archivo directamente
82
+ const content = readFileSync(fileName, 'utf-8');
83
+ const lines = content.split('\n');
84
+ const { line, column } = getLineAndColumnFromOffset(lines, diagnostic.start);
85
+ help += ` | Línea ${line}, Columna ${column}`;
86
+ }
87
+ }
88
+ catch {
89
+ // Último fallback: solo mostrar posición
90
+ help += ` | Posición ${diagnostic.start}`;
91
+ }
39
92
  }
40
93
  }
41
94
  return {
42
95
  file: fileName,
43
- message: enhancedMessage,
96
+ message: cleanedMessage,
44
97
  severity,
45
98
  help,
46
99
  };
47
100
  });
48
101
  }
102
+ /**
103
+ * Calcula línea y columna desde un offset de caracteres
104
+ * @param lines - Array de líneas ya procesadas (para evitar múltiples splits)
105
+ * @param offset - Posición del carácter
106
+ */
107
+ function getLineAndColumnFromOffset(lines, offset) {
108
+ let currentOffset = 0;
109
+ let line = 1;
110
+ let column = 1;
111
+ for (let i = 0; i < lines.length; i++) {
112
+ const currentLine = lines[i];
113
+ if (currentLine === undefined)
114
+ continue;
115
+ const lineLength = currentLine.length + 1; // +1 para el \n
116
+ if (currentOffset + lineLength > offset) {
117
+ line = i + 1;
118
+ column = offset - currentOffset + 1;
119
+ break;
120
+ }
121
+ currentOffset += lineLength;
122
+ }
123
+ return { line, column };
124
+ }
49
125
  /**
50
126
  * Limpia el mensaje de error eliminando información redundante
51
127
  */
@@ -61,8 +137,11 @@ function cleanErrorMessage(message) {
61
137
  }
62
138
  /**
63
139
  * Mejora significativamente el mensaje de error TypeScript con contexto visual
140
+ * ⚠️ DEPRECATED: Ya no se usa en el flujo normal para evitar overhead de performance
141
+ * Se mantiene para compatibilidad futura o modo verbose avanzado
142
+ * @param scriptInfo - Información de extracción de script para archivos Vue (opcional)
64
143
  */
65
- function enhanceErrorMessage(diagnostic, fileName, sourceCode) {
144
+ function enhanceErrorMessage(diagnostic, fileName, sourceCode, scriptInfo) {
66
145
  // Extraer el mensaje del error
67
146
  const message = typeof diagnostic.messageText === 'string'
68
147
  ? diagnostic.messageText
@@ -76,35 +155,43 @@ function enhanceErrorMessage(diagnostic, fileName, sourceCode) {
76
155
  if (typeof sourceFile.getLineAndCharacterOfPosition === 'function') {
77
156
  try {
78
157
  const lineAndChar = sourceFile.getLineAndCharacterOfPosition(diagnostic.start);
79
- const line = lineAndChar.line + 1;
158
+ // Ajustar línea si es un archivo Vue con script extraído
159
+ const line = scriptInfo
160
+ ? lineAndChar.line + scriptInfo.startLine
161
+ : lineAndChar.line + 1;
80
162
  const column = lineAndChar.character + 1;
81
163
  location = `Línea ${line}, Columna ${column} | Código TS${diagnostic.code}`;
82
164
  }
83
165
  catch {
84
- // Si falla, solo mostrar la posición de carácter
85
- location = `Posición ${diagnostic.start} | Código TS${diagnostic.code}`;
166
+ // Si falla, solo mostrar el código de error
167
+ location = `Código TS${diagnostic.code}`;
86
168
  }
87
169
  }
88
170
  else {
89
- // Fallback para cuando no está disponible el método (como en tests)
90
- location = `Posición ${diagnostic.start} | Código TS${diagnostic.code}`;
171
+ // Fallback: solo mostrar el código de error
172
+ location = `Código TS${diagnostic.code}`;
91
173
  } // Agregar contexto del código si está disponible
92
- if ((sourceCode || sourceFile.text) &&
174
+ if ((sourceCode || scriptInfo?.originalData || sourceFile.text) &&
93
175
  typeof sourceFile.getLineAndCharacterOfPosition === 'function') {
94
176
  try {
95
177
  const lineAndChar = sourceFile.getLineAndCharacterOfPosition(diagnostic.start);
96
- const text = sourceCode || sourceFile.text;
178
+ // Obtener código fuente apropiado
179
+ const text = scriptInfo?.originalData || sourceCode || sourceFile.text;
97
180
  const lines = text.split('\n');
98
- const errorLine = lines[lineAndChar.line];
181
+ // Calcular la línea real en el archivo original
182
+ const actualLineIndex = scriptInfo
183
+ ? lineAndChar.line + scriptInfo.startLine - 1
184
+ : lineAndChar.line;
185
+ const errorLine = lines[actualLineIndex];
99
186
  if (errorLine) {
100
187
  // Mostrar hasta 2 líneas antes y después para contexto
101
- const startLine = Math.max(0, lineAndChar.line - 2);
102
- const endLine = Math.min(lines.length - 1, lineAndChar.line + 2);
188
+ const startLine = Math.max(0, actualLineIndex - 2);
189
+ const endLine = Math.min(lines.length - 1, actualLineIndex + 2);
103
190
  codeContext = '\n\n📝 Contexto del código:\n';
104
191
  for (let i = startLine; i <= endLine; i++) {
105
192
  const currentLine = i + 1;
106
193
  const lineContent = lines[i] || '';
107
- const isErrorLine = i === lineAndChar.line;
194
+ const isErrorLine = i === actualLineIndex;
108
195
  if (isErrorLine) {
109
196
  codeContext += ` ${currentLine.toString().padStart(3, ' ')} ❌ ${lineContent}\n`;
110
197
  // Agregar flecha apuntando al error
@@ -213,9 +213,10 @@ const cleanupUnnecessaryExports = (compiledOutput, originalSource) => {
213
213
  * Precompila el código TypeScript con pipeline optimizado para máxima performance.
214
214
  * @param {string} data - El código TypeScript a precompilar.
215
215
  * @param {string} fileName - El nombre del archivo que contiene el código typescript.
216
+ * @param {ScriptExtractionInfo} scriptInfo - Información de extracción de script para archivos Vue (opcional).
216
217
  * @returns {Promise<CompileResult>} - Un objeto con el código precompilado o un error.
217
218
  */
218
- export const preCompileTS = async (data, fileName) => {
219
+ export const preCompileTS = async (data, fileName, scriptInfo) => {
219
220
  try {
220
221
  // Validación temprana: contenido vacío
221
222
  if (!data.trim()) {
@@ -261,7 +262,7 @@ export const preCompileTS = async (data, fileName) => {
261
262
  diag.code !== 6059);
262
263
  });
263
264
  if (criticalErrors.length > 0) {
264
- const errorMessage = createUnifiedErrorMessage(parseTypeScriptErrors(criticalErrors, fileName, data));
265
+ const errorMessage = createUnifiedErrorMessage(parseTypeScriptErrors(criticalErrors, fileName, data, scriptInfo));
265
266
  return {
266
267
  error: new Error(errorMessage),
267
268
  data: null,
@@ -275,9 +276,14 @@ export const preCompileTS = async (data, fileName) => {
275
276
  const serializableOptions = createSerializableCompilerOptions(compilerOptions);
276
277
  const typeCheckResult = await workerPool.typeCheck(fileName, data, serializableOptions);
277
278
  if (typeCheckResult.hasErrors) {
278
- const errorMessage = createUnifiedErrorMessage(parseTypeScriptErrors(typeCheckResult.diagnostics, fileName, data));
279
+ const errorMessage = createUnifiedErrorMessage(parseTypeScriptErrors(typeCheckResult.diagnostics, fileName, data, scriptInfo));
280
+ const error = new Error(errorMessage);
281
+ // Marcar como error de tipo (no error del compilador)
282
+ error.isTypeError = true;
283
+ // Limpiar stack trace para no confundir con errores del compilador
284
+ error.stack = undefined;
279
285
  return {
280
- error: new Error(errorMessage),
286
+ error,
281
287
  data: null,
282
288
  lang: 'ts',
283
289
  };
@@ -158,8 +158,11 @@ export const preCompileVue = async (data, source, isProd = false) => {
158
158
  error: null,
159
159
  data: 'export default {};',
160
160
  lang: 'js',
161
+ scriptInfo: undefined,
161
162
  };
162
163
  }
164
+ // Guardar el código original antes de inyectar HMR
165
+ const originalData = data;
163
166
  if (!isProd) {
164
167
  const { injectedData } = hmrInjectionCache.getOrGenerateHMRInjection(data, fileName);
165
168
  data = injectedData;
@@ -370,11 +373,22 @@ export const preCompileVue = async (data, source, isProd = false) => {
370
373
 
371
374
  export default ${componentName}; `;
372
375
  output = `${output}\n${finishComponent}`;
373
- return {
376
+ // 🚀 OPTIMIZACIÓN CRÍTICA: Evitar crear scriptInfo si no hay script
377
+ const result = {
374
378
  lang: finalCompiledScript.lang,
375
379
  error: null,
376
380
  data: output,
377
381
  };
382
+ // Solo agregar scriptInfo cuando realmente hay script (evita overhead)
383
+ if (descriptor.script || descriptor.scriptSetup) {
384
+ result.scriptInfo = {
385
+ startLine: (descriptor.script || descriptor.scriptSetup).loc?.start
386
+ .line || 1,
387
+ content: (descriptor.script || descriptor.scriptSetup).content,
388
+ originalData: originalData, // String directa, no closure
389
+ };
390
+ }
391
+ return result;
378
392
  }
379
393
  catch (error) {
380
394
  logger.error('Vue compilation error:', error);
@@ -382,6 +396,7 @@ export const preCompileVue = async (data, source, isProd = false) => {
382
396
  lang: null,
383
397
  error: error instanceof Error ? error : new Error(String(error)),
384
398
  data: null,
399
+ scriptInfo: undefined,
385
400
  };
386
401
  }
387
402
  };
package/dist/main.js CHANGED
@@ -121,7 +121,13 @@ async function main() {
121
121
  description: 'Habilitar/Deshabilitar la verificación de tipos. Por defecto --typeCheck=false',
122
122
  default: false,
123
123
  })
124
- .alias('t', 'typeCheck');
124
+ .alias('t', 'typeCheck')
125
+ .option('checkIntegrity', {
126
+ type: 'boolean',
127
+ description: 'Validar la integridad del código compilado (para builds de deploy). Por defecto --checkIntegrity=false',
128
+ default: false,
129
+ })
130
+ .alias('ci', 'checkIntegrity');
125
131
  // Definir la opción tailwind dinámicamente
126
132
  // Asumiendo que env.TAILWIND es una cadena que podría ser 'true', 'false', o undefined
127
133
  if (env.tailwindcss !== 'false') {
@@ -177,7 +183,8 @@ async function main() {
177
183
  env.TAILWIND =
178
184
  argv.tailwind === undefined ? 'true' : String(argv.tailwind);
179
185
  env.ENABLE_LINTER = String(argv.linter);
180
- env.VERBOSE = argv.verbose ? 'true' : 'false'; // 🎯 Configuración moderna y organizada
186
+ env.VERBOSE = argv.verbose ? 'true' : 'false';
187
+ env.CHECK_INTEGRITY = argv.checkIntegrity ? 'true' : 'false'; // 🎯 Configuración moderna y organizada
181
188
  logger.info(chalk.bold.blue('⚙️ Configuración'));
182
189
  logger.info(chalk.gray(' ┌─ Modo de ejecución'));
183
190
  const modes = [
@@ -199,6 +206,11 @@ async function main() {
199
206
  icon: '🔍',
200
207
  },
201
208
  { label: 'Verificar tipos', value: argv.typeCheck, icon: '📘' },
209
+ {
210
+ label: 'Validar integridad',
211
+ value: argv.checkIntegrity,
212
+ icon: '🛡️',
213
+ },
202
214
  { label: 'Detallado', value: env.VERBOSE === 'true', icon: '📝' },
203
215
  ];
204
216
  modes.forEach(mode => {
@@ -108,7 +108,8 @@ export function validateConfigStructure(config) {
108
108
  logger.error('compilerOptions es requerido y debe ser un objeto');
109
109
  return false;
110
110
  }
111
- if (!config.compilerOptions.pathsAlias || typeof config.compilerOptions.pathsAlias !== 'object') {
111
+ if (!config.compilerOptions.pathsAlias ||
112
+ typeof config.compilerOptions.pathsAlias !== 'object') {
112
113
  logger.error('pathsAlias es requerido y debe ser un objeto');
113
114
  return false;
114
115
  }
@@ -130,11 +131,13 @@ export function validateConfigStructure(config) {
130
131
  }
131
132
  }
132
133
  // Validar sourceRoot si existe
133
- if (config.compilerOptions.sourceRoot && !validatePath(config.compilerOptions.sourceRoot)) {
134
+ if (config.compilerOptions.sourceRoot &&
135
+ !validatePath(config.compilerOptions.sourceRoot)) {
134
136
  return false;
135
137
  }
136
138
  // Validar outDir si existe
137
- if (config.compilerOptions.outDir && !validatePath(config.compilerOptions.outDir)) {
139
+ if (config.compilerOptions.outDir &&
140
+ !validatePath(config.compilerOptions.outDir)) {
138
141
  return false;
139
142
  }
140
143
  // Validar linter si existe