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.
- package/README.md +106 -17
- package/dist/compiler/compile.js +21 -2
- package/dist/compiler/integrity-validator.js +527 -0
- package/dist/compiler/minify.js +19 -1
- package/dist/compiler/minifyTemplate.js +16 -0
- package/dist/compiler/transforms.js +34 -3
- package/dist/compiler/typescript-error-parser.js +113 -26
- package/dist/compiler/typescript-manager.js +10 -4
- package/dist/compiler/vuejs.js +16 -1
- package/dist/main.js +14 -2
- package/dist/servicios/readConfig.js +6 -3
- package/dist/utils/module-resolver.js +164 -154
- package/package.json +5 -4
|
@@ -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
|
-
//
|
|
9
|
-
const
|
|
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.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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 =
|
|
29
|
-
|
|
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
|
-
//
|
|
33
|
-
|
|
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
|
|
38
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
|
85
|
-
location = `
|
|
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
|
|
90
|
-
location = `
|
|
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
|
-
|
|
178
|
+
// Obtener código fuente apropiado
|
|
179
|
+
const text = scriptInfo?.originalData || sourceCode || sourceFile.text;
|
|
97
180
|
const lines = text.split('\n');
|
|
98
|
-
|
|
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,
|
|
102
|
-
const endLine = Math.min(lines.length - 1,
|
|
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 ===
|
|
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
|
|
286
|
+
error,
|
|
281
287
|
data: null,
|
|
282
288
|
lang: 'ts',
|
|
283
289
|
};
|
package/dist/compiler/vuejs.js
CHANGED
|
@@ -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
|
-
|
|
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';
|
|
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 ||
|
|
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 &&
|
|
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 &&
|
|
139
|
+
if (config.compilerOptions.outDir &&
|
|
140
|
+
!validatePath(config.compilerOptions.outDir)) {
|
|
138
141
|
return false;
|
|
139
142
|
}
|
|
140
143
|
// Validar linter si existe
|