versacompiler 2.3.5 → 2.4.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.
@@ -128,19 +128,19 @@ class WorkerTypeScriptLanguageServiceHost {
128
128
  return ts.getDefaultLibFilePath(options);
129
129
  }
130
130
 
131
- fileExists(path) {
132
- return this.files.has(path) || fs.existsSync(path);
131
+ fileExists(filePath) {
132
+ return this.files.has(filePath) || fs.existsSync(filePath);
133
133
  }
134
134
 
135
- readFile(path) {
136
- const file = this.files.get(path);
135
+ readFile(filePath) {
136
+ const file = this.files.get(filePath);
137
137
  if (file) {
138
138
  return file.content;
139
139
  }
140
140
 
141
- if (fs.existsSync(path)) {
141
+ if (fs.existsSync(filePath)) {
142
142
  try {
143
- return fs.readFileSync(path, 'utf-8');
143
+ return fs.readFileSync(filePath, 'utf-8');
144
144
  } catch {
145
145
  return undefined;
146
146
  }
@@ -154,6 +154,38 @@ class WorkerTypeScriptLanguageServiceHost {
154
154
  }
155
155
  }
156
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 { _persistentLS.dispose(); } catch { /* ignore */ }
177
+ _persistentLS = null;
178
+ }
179
+ _persistentHost = new WorkerTypeScriptLanguageServiceHost(compilerOptions);
180
+ _persistentLS = ts.createLanguageService(_persistentHost);
181
+ _persistentCompilerOptionsStr = optionsStr;
182
+ _tasksSinceReset = 0;
183
+ }
184
+
185
+ _tasksSinceReset++;
186
+ return { host: _persistentHost, ls: _persistentLS };
187
+ }
188
+
157
189
  /**
158
190
  * Realiza validación de tipos en el worker thread
159
191
  */
@@ -167,30 +199,16 @@ function validateTypesInWorker(fileName, content, compilerOptions) {
167
199
  return { diagnostics: [], hasErrors: false };
168
200
  }
169
201
 
170
- // Crear Language Service Host
171
- const host = new WorkerTypeScriptLanguageServiceHost(compilerOptions); // Para archivos Vue, crear un archivo virtual .ts
202
+ // Obtener o crear Language Service persistente (rápido después del primer uso)
203
+ const { host, ls: languageService } = getOrResetLanguageService(compilerOptions);
204
+
205
+ // Para archivos Vue, crear un archivo virtual .ts
172
206
  if (fileName.endsWith('.vue')) {
173
- // Crear un nombre de archivo virtual único
174
207
  const virtualFileName = `${fileName}.ts`;
175
208
  host.addFile(virtualFileName, scriptContent);
176
209
  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
210
 
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
211
+ // Agregar declaraciones de tipos Vue si es necesario
194
212
  const projectDir = path.dirname(actualFileName);
195
213
  const vueTypesPath = path.join(projectDir, 'vue-types.d.ts');
196
214
  const vueTypesDeclaration = `// Declaraciones de tipos Vue para validación
@@ -214,10 +232,27 @@ function validateTypesInWorker(fileName, content, compilerOptions) {
214
232
  }
215
233
  export {};`;
216
234
  host.addFile(vueTypesPath, vueTypesDeclaration);
217
- }
218
235
 
219
- // Crear Language Service
220
- const languageService = ts.createLanguageService(host);
236
+ // Eliminar archivos de tareas anteriores para evitar que el programa crezca.
237
+ // Se hace DESPUÉS de addFile para que la versión se incremente correctamente
238
+ // (evita que el LS use resultados cacheados de versiones anteriores).
239
+ for (const key of host.files.keys()) {
240
+ if (key !== virtualFileName && key !== vueTypesPath) {
241
+ host.files.delete(key);
242
+ }
243
+ }
244
+ } else {
245
+ host.addFile(fileName, scriptContent);
246
+ actualFileName = fileName;
247
+
248
+ // Eliminar archivos de tareas anteriores para evitar que el programa crezca.
249
+ // Se hace DESPUÉS de addFile para que la versión se incremente correctamente.
250
+ for (const key of host.files.keys()) {
251
+ if (key !== fileName) {
252
+ host.files.delete(key);
253
+ }
254
+ }
255
+ }
221
256
 
222
257
  try {
223
258
  // Verificar que el archivo existe en el host antes de solicitar diagnósticos
@@ -231,10 +266,6 @@ function validateTypesInWorker(fileName, content, compilerOptions) {
231
266
  try {
232
267
  syntacticDiagnostics =
233
268
  languageService.getSyntacticDiagnostics(actualFileName);
234
- // console.log(
235
- // '[Worker] Diagnósticos sintácticos:',
236
- // syntacticDiagnostics.length,
237
- // );
238
269
  } catch (error) {
239
270
  console.error(
240
271
  '[Worker] Error obteniendo diagnósticos sintácticos:',
@@ -245,37 +276,18 @@ function validateTypesInWorker(fileName, content, compilerOptions) {
245
276
  try {
246
277
  semanticDiagnostics =
247
278
  languageService.getSemanticDiagnostics(actualFileName);
248
- // console.log(
249
- // '[Worker] Diagnósticos semánticos:',
250
- // semanticDiagnostics.length,
251
- // );
252
279
  } catch (error) {
253
280
  console.error(
254
281
  '[Worker] Error obteniendo diagnósticos semánticos:',
255
282
  error.message,
256
283
  );
257
- } // Combinar todos los diagnósticos
284
+ }
285
+
258
286
  const allDiagnostics = [
259
287
  ...syntacticDiagnostics,
260
288
  ...semanticDiagnostics,
261
289
  ];
262
290
 
263
- // console.log(
264
- // '[Worker] Total diagnósticos encontrados:',
265
- // allDiagnostics.length,
266
- // );
267
-
268
- // Log de todos los diagnósticos antes del filtrado
269
- // allDiagnostics.forEach((diag, index) => {
270
- // const messageText = ts.flattenDiagnosticMessageText(
271
- // diag.messageText,
272
- // '\n',
273
- // );
274
- // console.log(
275
- // `[Worker] Diagnóstico ${index + 1}: [${diag.category}] ${messageText}`,
276
- // );
277
- // });
278
-
279
291
  // Filtrar diagnósticos relevantes
280
292
  const filteredDiagnostics = allDiagnostics.filter(diag => {
281
293
  const messageText = ts.flattenDiagnosticMessageText(
@@ -286,7 +298,9 @@ function validateTypesInWorker(fileName, content, compilerOptions) {
286
298
  // Solo errores de categoría Error
287
299
  if (diag.category !== ts.DiagnosticCategory.Error) {
288
300
  return false;
289
- } // Ignorar SOLO errores específicos de infraestructura Vue y rutas de módulos
301
+ }
302
+
303
+ // Ignorar errores de infraestructura Vue y rutas de módulos
290
304
  return (
291
305
  !messageText.includes('Cannot find module') &&
292
306
  !messageText.includes('Could not find source file') &&
@@ -314,17 +328,12 @@ function validateTypesInWorker(fileName, content, compilerOptions) {
314
328
  !messageText.includes(
315
329
  "Parameter '_cache' implicitly has an 'any' type",
316
330
  ) &&
317
- // Ignorar errores específicos de decorators cuando están mal configurados
318
331
  !messageText.includes(
319
332
  'Unable to resolve signature of method decorator when called as an expression',
320
333
  ) &&
321
334
  !messageText.includes(
322
335
  'The runtime will invoke the decorator with',
323
336
  ) &&
324
- // NO ignorar errores TS7031 y TS7006 de forma general, solo para parámetros específicos de Vue
325
- // diag.code !== 7031 &&
326
- // diag.code !== 7006 &&
327
- // Ignorar errores TS1241 (decorator signature mismatch) durante desarrollo
328
337
  diag.code !== 1241 &&
329
338
  !(
330
339
  messageText.includes("implicitly has an 'any' type") &&
@@ -91,16 +91,22 @@ class VueHMRInjectionCache {
91
91
  };
92
92
  }
93
93
  /**
94
- * Limpia entradas de cache cuando se excede el límite
94
+ * Limpia entradas de cache cuando se excede el límite (O(n) en lugar de O(n log n))
95
95
  */
96
96
  evictIfNeeded() {
97
97
  if (this.cache.size <= this.MAX_CACHE_SIZE)
98
98
  return;
99
- const entries = Array.from(this.cache.entries());
100
- entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
101
- // Eliminar las entradas más antiguas
102
- const toDelete = entries.slice(0, entries.length - this.MAX_CACHE_SIZE);
103
- toDelete.forEach(([key]) => this.cache.delete(key));
99
+ // Eliminar la entrada más antigua con un solo barrido O(n)
100
+ let oldestKey = '';
101
+ let oldestTime = Infinity;
102
+ for (const [key, entry] of this.cache) {
103
+ if (entry.timestamp < oldestTime) {
104
+ oldestTime = entry.timestamp;
105
+ oldestKey = key;
106
+ }
107
+ }
108
+ if (oldestKey)
109
+ this.cache.delete(oldestKey);
104
110
  }
105
111
  /**
106
112
  * Limpia entradas expiradas
@@ -213,7 +213,7 @@ export async function handleLibraryHotReload(
213
213
  ) {
214
214
  try {
215
215
  newLibraryVersion.clearCache();
216
- } catch (_e) {
216
+ } catch {
217
217
  // Ignorar errores de clearCache, no es crítico
218
218
  }
219
219
  }
@@ -238,7 +238,7 @@ export async function handleLibraryHotReload(
238
238
  ) {
239
239
  window.performance.clearResourceTimings();
240
240
  }
241
- } catch (_e) {
241
+ } catch {
242
242
  // Ignorar errores de limpieza de cache
243
243
  }
244
244
 
package/dist/main.js CHANGED
@@ -63,10 +63,10 @@ function stopCompile() {
63
63
  async function main() {
64
64
  // Load yargs dynamically
65
65
  const { yargs: yargsInstance, hideBin: hideBinFn } = await loadYargs();
66
- const chalk = await loadChalk();
66
+ const chalkInstance = await loadChalk();
67
67
  let yargInstance = yargsInstance(hideBinFn(globalProcess.argv))
68
68
  .scriptName('versa')
69
- .usage(chalk.blue('VersaCompiler') + ' - Compilador de archivos Vue/TS/JS')
69
+ .usage(chalkInstance.blue('VersaCompiler') + ' - Compilador de archivos Vue/TS/JS')
70
70
  .option('init', {
71
71
  type: 'boolean',
72
72
  description: 'Inicializar la configuración',
@@ -147,8 +147,8 @@ async function main() {
147
147
  const argv = (await yargInstance
148
148
  .help()
149
149
  .alias('h', 'help')
150
- .command('* [files...]', 'Compilar archivos específicos', (yargs) => {
151
- return yargs.positional('files', {
150
+ .command('* [files...]', 'Compilar archivos específicos', (yargsCmd) => {
151
+ return yargsCmd.positional('files', {
152
152
  describe: 'Archivos para compilar',
153
153
  type: 'string',
154
154
  array: true,
@@ -4,7 +4,7 @@ import * as process from 'node:process';
4
4
  const { env } = process;
5
5
  import * as chokidar from 'chokidar';
6
6
  import { minimatch } from 'minimatch';
7
- import { getOutputPath, initCompile, normalizeRuta } from '../compiler/compile.js';
7
+ import { clearCompilationState, getOutputPath, initCompile, normalizeRuta } from '../compiler/compile.js';
8
8
  import { promptUser } from '../utils/promptUser.js';
9
9
  import { emitirCambios } from './browserSync.js';
10
10
  import { logger } from './logger.js';
@@ -76,6 +76,8 @@ class WatchDebouncer {
76
76
  if (this.isProcessing || this.pendingChanges.size === 0) {
77
77
  return;
78
78
  }
79
+ // Limpiar errores del ciclo anterior para evitar memory leak en watch mode
80
+ clearCompilationState();
79
81
  this.isProcessing = true;
80
82
  const changes = Array.from(this.pendingChanges.values());
81
83
  this.pendingChanges.clear();
@@ -24,6 +24,11 @@ export function validatePath(pathStr) {
24
24
  logger.warn(`Ruta demasiado larga: ${pathStr.length} caracteres`);
25
25
  return false;
26
26
  }
27
+ // Rechazar rutas absolutas de Windows (válido en cualquier plataforma)
28
+ if (/^[A-Za-z]:[\\\/]/.test(pathStr)) {
29
+ logger.error(`Ruta absoluta de Windows no permitida: ${pathStr}`);
30
+ return false;
31
+ }
27
32
  // Normalizar la ruta para detectar path traversal
28
33
  const normalizedPath = normalize(pathStr);
29
34
  const resolvedPath = resolve(process.cwd(), normalizedPath);
@@ -243,7 +248,7 @@ export async function dynamicImport(url) {
243
248
  * Timeout wrapper para promises
244
249
  */
245
250
  export function withTimeout(promise, timeoutMs, errorMessage) {
246
- const timeoutPromise = new Promise((resolve, reject) => setTimeout(() => reject(new Error(errorMessage)), timeoutMs));
251
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error(errorMessage)), timeoutMs));
247
252
  return Promise.race([promise, timeoutPromise]);
248
253
  }
249
254
  /**
@@ -309,6 +314,11 @@ export async function readConfig() {
309
314
  env.PATH_DIST = outDir;
310
315
  env.aditionalWatch = safeJsonStringify(tsConfig?.aditionalWatch, '[]');
311
316
  env.bundlers = safeJsonStringify(tsConfig?.bundlers, 'false');
317
+ // Configurar número máximo de workers para type checking
318
+ if (tsConfig?.typeCheckOptions?.maxWorkers) {
319
+ const maxWorkers = Math.max(1, Math.min(8, Number(tsConfig.typeCheckOptions.maxWorkers)));
320
+ env.TS_MAX_WORKERS = String(maxWorkers);
321
+ }
312
322
  // Configuración adicional para compatibilidad
313
323
  if (!tsConfig.compilerOptions.sourceRoot) {
314
324
  env.tsConfig = safeJsonStringify(tsConfig, '{}');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "versacompiler",
3
- "version": "2.3.5",
3
+ "version": "2.4.1",
4
4
  "description": "Una herramienta para compilar y minificar archivos .vue, .js y .ts para proyectos de Vue 3 con soporte para TypeScript.",
5
5
  "main": "dist/main.js",
6
6
  "bin": {
@@ -21,7 +21,7 @@
21
21
  "build": "tsx src/main.ts --all -t --cc --co -y --verbose",
22
22
  "compileDev": "tsx src/main.ts --all --ci --cc -y -t --linter --verbose",
23
23
  "vlint": "tsx src/main.ts --all --cc --co -y --linter",
24
- "vtlint": "tsx src/main.ts --all --cc --co -y -t",
24
+ "vtlint": "tsx src/main.ts --all --cc --co -y -t --verbose",
25
25
  "test": "vitest run",
26
26
  "test:watch": "vitest",
27
27
  "test:ui": "vitest --ui",
@@ -49,63 +49,63 @@
49
49
  },
50
50
  "homepage": "https://github.com/kriollo/versaCompiler#readme",
51
51
  "dependencies": {
52
- "@vue/compiler-dom": "^3.5.27",
53
- "@vue/reactivity": "^3.5.27",
54
- "@vue/runtime-core": "^3.5.27",
55
- "@vue/runtime-dom": "^3.5.27",
52
+ "@vue/compiler-dom": "^3.5.29",
53
+ "@vue/reactivity": "^3.5.29",
54
+ "@vue/runtime-core": "^3.5.29",
55
+ "@vue/runtime-dom": "^3.5.29",
56
56
  "browser-sync": "^3.0.4",
57
57
  "chalk": "5.6.2",
58
58
  "chokidar": "^5.0.0",
59
- "enhanced-resolve": "^5.19.0",
59
+ "enhanced-resolve": "^5.20.0",
60
60
  "execa": "^9.6.1",
61
61
  "find-root": "^1.1.0",
62
- "fs-extra": "^11.3.3",
62
+ "fs-extra": "^11.3.4",
63
63
  "get-port": "^7.1.0",
64
64
  "minify-html-literals": "^1.3.5",
65
- "minimatch": "^10.1.2",
66
- "oxc-minify": "^0.112.0",
67
- "oxc-parser": "^0.112.0",
68
- "oxc-transform": "^0.112.0",
65
+ "minimatch": "^10.2.4",
66
+ "oxc-minify": "^0.116.0",
67
+ "oxc-parser": "^0.116.0",
68
+ "oxc-transform": "^0.116.0",
69
69
  "resolve": "^1.22.11",
70
70
  "tsx": "^4.21.0",
71
71
  "typescript": "^5.9.3",
72
- "vue": "3.5.27",
72
+ "vue": "3.5.29",
73
73
  "yargs": "^18.0.0"
74
74
  },
75
75
  "devDependencies": {
76
- "@eslint/eslintrc": "^3.3.3",
77
- "@tailwindcss/cli": "^4.1.18",
76
+ "@eslint/eslintrc": "^3.3.5",
77
+ "@tailwindcss/cli": "^4.2.1",
78
78
  "@types/browser-sync": "^2.29.1",
79
79
  "@types/find-root": "^1.1.4",
80
80
  "@types/fs-extra": "^11.0.4",
81
81
  "@types/jest": "^30.0.0",
82
82
  "@types/mocha": "^10.0.10",
83
- "@types/node": "^25.2.0",
83
+ "@types/node": "^25.3.5",
84
84
  "@types/resolve": "^1.20.6",
85
85
  "@types/yargs": "^17.0.35",
86
- "@typescript-eslint/eslint-plugin": "^8.54.0",
87
- "@typescript-eslint/parser": "^8.54.0",
86
+ "@typescript-eslint/eslint-plugin": "^8.56.1",
87
+ "@typescript-eslint/parser": "^8.56.1",
88
88
  "@vitest/coverage-v8": "^4.0.18",
89
89
  "@vitest/ui": "^4.0.18",
90
- "@vue/eslint-config-typescript": "^14.6.0",
90
+ "@vue/eslint-config-typescript": "^14.7.0",
91
91
  "@vue/test-utils": "^2.4.6",
92
92
  "code-tag": "^1.2.0",
93
- "eslint": "^9.39.2",
93
+ "eslint": "^10.0.3",
94
94
  "eslint-import-resolver-typescript": "^4.4.4",
95
- "eslint-plugin-import": "^2.32.0",
96
- "eslint-plugin-oxlint": "^1.43.0",
95
+ "eslint-plugin-import-x": "^4.16.1",
96
+ "eslint-plugin-oxlint": "^1.51.0",
97
97
  "eslint-plugin-promise": "^7.2.1",
98
- "eslint-plugin-unicorn": "^62.0.0",
99
- "eslint-plugin-vue": "^10.7.0",
100
- "oxlint": "^1.43.0",
98
+ "eslint-plugin-unicorn": "^63.0.0",
99
+ "eslint-plugin-vue": "^10.8.0",
100
+ "oxlint": "^1.51.0",
101
101
  "pinia": "^3.0.4",
102
102
  "prettier": "3.8.1",
103
- "rimraf": "^6.1.2",
104
- "sweetalert2": "^11.26.18",
105
- "tailwindcss": "^4.1.18",
103
+ "rimraf": "^6.1.3",
104
+ "sweetalert2": "^11.26.22",
105
+ "tailwindcss": "^4.2.1",
106
106
  "vitest": "^4.0.18",
107
- "vue-eslint-parser": "^10.2.0",
108
- "vue-router": "^5.0.2"
107
+ "vue-eslint-parser": "^10.4.0",
108
+ "vue-router": "^5.0.3"
109
109
  },
110
110
  "packageManager": "pnpm@10.15.0+sha512.486ebc259d3e999a4e8691ce03b5cac4a71cbeca39372a9b762cb500cfdf0873e2cb16abe3d951b1ee2cf012503f027b98b6584e4df22524e0c7450d9ec7aa7b"
111
111
  }