versacompiler 2.0.1 → 2.0.2
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/dist/compiler/compile.js +344 -7
- package/dist/compiler/module-resolution-optimizer.js +14 -36
- package/dist/compiler/transform-optimizer.js +111 -6
- package/dist/compiler/transforms.js +3 -31
- package/dist/compiler/typescript-worker-pool.js +387 -21
- package/dist/servicios/file-watcher.js +69 -18
- package/dist/utils/excluded-modules.js +36 -0
- package/dist/utils/module-resolver.js +1 -29
- package/package.json +2 -1
package/dist/compiler/compile.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { glob, mkdir, readFile, stat, unlink, writeFile, } from 'node:fs/promises';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
import { cwd, env, stdout } from 'node:process';
|
|
5
|
+
import { argv, cwd, env, stdout } from 'node:process';
|
|
6
6
|
// Lazy loading optimizations - Only import lightweight modules synchronously
|
|
7
7
|
import { logger } from '../servicios/logger.js';
|
|
8
8
|
import { promptUser } from '../utils/promptUser.js';
|
|
@@ -340,11 +340,31 @@ async function loadVue() {
|
|
|
340
340
|
// Almacenamiento global de errores y resultados
|
|
341
341
|
const compilationErrors = [];
|
|
342
342
|
const compilationResults = [];
|
|
343
|
+
// Variables de entorno relevantes para compilación
|
|
344
|
+
const COMPILATION_ENV_VARS = [
|
|
345
|
+
'NODE_ENV',
|
|
346
|
+
'isPROD',
|
|
347
|
+
'TAILWIND',
|
|
348
|
+
'ENABLE_LINTER',
|
|
349
|
+
'VERBOSE',
|
|
350
|
+
'typeCheck',
|
|
351
|
+
'PATH_ALIAS',
|
|
352
|
+
'tailwindcss',
|
|
353
|
+
'linter',
|
|
354
|
+
'tsconfigFile',
|
|
355
|
+
];
|
|
343
356
|
class SmartCompilationCache {
|
|
344
357
|
cache = new Map();
|
|
345
358
|
maxEntries = 500; // Máximo archivos en cache
|
|
346
359
|
maxMemory = 100 * 1024 * 1024; // 100MB límite
|
|
347
360
|
currentMemoryUsage = 0;
|
|
361
|
+
// ✨ ISSUE #3: Sistema de vigilancia de dependencias
|
|
362
|
+
fileWatchers = new Map(); // chokidar watchers
|
|
363
|
+
dependencyGraph = new Map(); // archivo -> dependencias
|
|
364
|
+
reverseDependencyGraph = new Map(); // dependencia -> archivos que la usan
|
|
365
|
+
packageJsonPath = path.join(cwd(), 'package.json');
|
|
366
|
+
nodeModulesPath = path.join(cwd(), 'node_modules');
|
|
367
|
+
isWatchingDependencies = false;
|
|
348
368
|
/**
|
|
349
369
|
* Genera hash SHA-256 del contenido del archivo
|
|
350
370
|
*/ async generateContentHash(filePath) {
|
|
@@ -359,6 +379,130 @@ class SmartCompilationCache {
|
|
|
359
379
|
}
|
|
360
380
|
}
|
|
361
381
|
/**
|
|
382
|
+
* Genera hash de la configuración del compilador
|
|
383
|
+
*/
|
|
384
|
+
generateConfigHash() {
|
|
385
|
+
try {
|
|
386
|
+
// Recopilar configuración relevante de variables de entorno
|
|
387
|
+
const config = {
|
|
388
|
+
isPROD: env.isPROD || 'false',
|
|
389
|
+
TAILWIND: env.TAILWIND || 'false',
|
|
390
|
+
ENABLE_LINTER: env.ENABLE_LINTER || 'false',
|
|
391
|
+
PATH_ALIAS: env.PATH_ALIAS || '{}',
|
|
392
|
+
tailwindcss: env.tailwindcss || 'false',
|
|
393
|
+
linter: env.linter || 'false',
|
|
394
|
+
tsconfigFile: env.tsconfigFile || './tsconfig.json',
|
|
395
|
+
};
|
|
396
|
+
const configStr = JSON.stringify(config, Object.keys(config).sort());
|
|
397
|
+
return createHash('sha256')
|
|
398
|
+
.update(configStr)
|
|
399
|
+
.digest('hex')
|
|
400
|
+
.substring(0, 12);
|
|
401
|
+
}
|
|
402
|
+
catch {
|
|
403
|
+
return 'no-config';
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Genera hash de variables de entorno relevantes
|
|
408
|
+
*/
|
|
409
|
+
generateEnvHash() {
|
|
410
|
+
try {
|
|
411
|
+
const envVars = COMPILATION_ENV_VARS.map(key => `${key}=${env[key] || ''}`).join('|');
|
|
412
|
+
return createHash('sha256')
|
|
413
|
+
.update(envVars)
|
|
414
|
+
.digest('hex')
|
|
415
|
+
.substring(0, 12);
|
|
416
|
+
}
|
|
417
|
+
catch {
|
|
418
|
+
return 'no-env';
|
|
419
|
+
}
|
|
420
|
+
} /**
|
|
421
|
+
* ✨ ISSUE #3: Genera hash avanzado de dependencias del proyecto
|
|
422
|
+
* Incluye vigilancia de package.json, node_modules y versiones instaladas
|
|
423
|
+
*/
|
|
424
|
+
async generateDependencyHash() {
|
|
425
|
+
try {
|
|
426
|
+
const hash = createHash('sha256');
|
|
427
|
+
// 1. Hash del package.json con versiones
|
|
428
|
+
const packagePath = path.join(cwd(), 'package.json');
|
|
429
|
+
const packageContent = await readFile(packagePath, 'utf8');
|
|
430
|
+
const pkg = JSON.parse(packageContent);
|
|
431
|
+
const deps = {
|
|
432
|
+
...pkg.dependencies,
|
|
433
|
+
...pkg.devDependencies,
|
|
434
|
+
};
|
|
435
|
+
const depsStr = JSON.stringify(deps, Object.keys(deps).sort());
|
|
436
|
+
hash.update(`package:${depsStr}`);
|
|
437
|
+
// 2. Hash del package-lock.json si existe (versiones exactas instaladas)
|
|
438
|
+
try {
|
|
439
|
+
const lockPath = path.join(cwd(), 'package-lock.json');
|
|
440
|
+
const lockContent = await readFile(lockPath, 'utf8');
|
|
441
|
+
const lockData = JSON.parse(lockContent);
|
|
442
|
+
// Solo incluir las versiones instaladas, no todo el lockfile
|
|
443
|
+
const installedVersions = {};
|
|
444
|
+
if (lockData.packages) {
|
|
445
|
+
for (const [pkgPath, pkgInfo] of Object.entries(lockData.packages)) {
|
|
446
|
+
if (pkgPath &&
|
|
447
|
+
pkgPath !== '' &&
|
|
448
|
+
typeof pkgInfo === 'object' &&
|
|
449
|
+
pkgInfo !== null) {
|
|
450
|
+
const pkgName = pkgPath.replace('node_modules/', '');
|
|
451
|
+
if (pkgInfo.version) {
|
|
452
|
+
installedVersions[pkgName] = pkgInfo.version;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
hash.update(`lock:${JSON.stringify(installedVersions, Object.keys(installedVersions).sort())}`);
|
|
458
|
+
}
|
|
459
|
+
catch {
|
|
460
|
+
// Ignorar si no existe package-lock.json
|
|
461
|
+
}
|
|
462
|
+
// 3. ✨ NUEVO: Hash de timestamps críticos de node_modules
|
|
463
|
+
try {
|
|
464
|
+
const nodeModulesPath = path.join(cwd(), 'node_modules');
|
|
465
|
+
const nodeModulesStat = await stat(nodeModulesPath);
|
|
466
|
+
hash.update(`nmtime:${nodeModulesStat.mtimeMs}`);
|
|
467
|
+
// Verificar timestamps de dependencias críticas instaladas
|
|
468
|
+
const criticalDeps = Object.keys(deps).slice(0, 10); // Top 10 para performance
|
|
469
|
+
for (const dep of criticalDeps) {
|
|
470
|
+
try {
|
|
471
|
+
const depPath = path.join(nodeModulesPath, dep);
|
|
472
|
+
const depStat = await stat(depPath);
|
|
473
|
+
hash.update(`${dep}:${depStat.mtimeMs}`);
|
|
474
|
+
}
|
|
475
|
+
catch {
|
|
476
|
+
// Dependencia no instalada o error
|
|
477
|
+
hash.update(`${dep}:missing`);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
catch {
|
|
482
|
+
// node_modules no existe
|
|
483
|
+
hash.update('nmtime:none');
|
|
484
|
+
}
|
|
485
|
+
return hash.digest('hex').substring(0, 16);
|
|
486
|
+
}
|
|
487
|
+
catch (error) {
|
|
488
|
+
// Incluir información del error en el hash para debugging
|
|
489
|
+
return createHash('sha256')
|
|
490
|
+
.update(`error:${error instanceof Error ? error.message : 'unknown'}`)
|
|
491
|
+
.digest('hex')
|
|
492
|
+
.substring(0, 16);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Genera clave de cache granular que incluye todos los factores
|
|
497
|
+
*/
|
|
498
|
+
async generateCacheKey(filePath) {
|
|
499
|
+
const contentHash = await this.generateContentHash(filePath);
|
|
500
|
+
const configHash = this.generateConfigHash();
|
|
501
|
+
const envHash = this.generateEnvHash();
|
|
502
|
+
const dependencyHash = await this.generateDependencyHash();
|
|
503
|
+
// Usar | como separador para evitar problemas con rutas de Windows
|
|
504
|
+
return `${filePath}|${contentHash.substring(0, 12)}|${configHash}|${envHash}|${dependencyHash}`;
|
|
505
|
+
} /**
|
|
362
506
|
* Verifica si una entrada de cache es válida
|
|
363
507
|
*/
|
|
364
508
|
async isValid(filePath) {
|
|
@@ -369,8 +513,26 @@ class SmartCompilationCache {
|
|
|
369
513
|
// Verificar si el archivo de salida existe
|
|
370
514
|
await stat(entry.outputPath);
|
|
371
515
|
// Verificar si el contenido ha cambiado
|
|
372
|
-
const
|
|
373
|
-
if (entry.contentHash !==
|
|
516
|
+
const currentContentHash = await this.generateContentHash(filePath);
|
|
517
|
+
if (entry.contentHash !== currentContentHash) {
|
|
518
|
+
this.cache.delete(filePath);
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
// Verificar si la configuración ha cambiado
|
|
522
|
+
const currentConfigHash = this.generateConfigHash();
|
|
523
|
+
if (entry.configHash !== currentConfigHash) {
|
|
524
|
+
this.cache.delete(filePath);
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
// Verificar si las variables de entorno han cambiado
|
|
528
|
+
const currentEnvHash = this.generateEnvHash();
|
|
529
|
+
if (entry.envHash !== currentEnvHash) {
|
|
530
|
+
this.cache.delete(filePath);
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
// Verificar si las dependencias han cambiado
|
|
534
|
+
const currentDependencyHash = await this.generateDependencyHash();
|
|
535
|
+
if (entry.dependencyHash !== currentDependencyHash) {
|
|
374
536
|
this.cache.delete(filePath);
|
|
375
537
|
return false;
|
|
376
538
|
}
|
|
@@ -389,16 +551,21 @@ class SmartCompilationCache {
|
|
|
389
551
|
this.cache.delete(filePath);
|
|
390
552
|
return false;
|
|
391
553
|
}
|
|
392
|
-
}
|
|
393
|
-
/**
|
|
554
|
+
} /**
|
|
394
555
|
* Añade una entrada al cache
|
|
395
556
|
*/
|
|
396
557
|
async set(filePath, outputPath) {
|
|
397
558
|
try {
|
|
398
559
|
const stats = await stat(filePath);
|
|
399
560
|
const contentHash = await this.generateContentHash(filePath);
|
|
561
|
+
const configHash = this.generateConfigHash();
|
|
562
|
+
const envHash = this.generateEnvHash();
|
|
563
|
+
const dependencyHash = await this.generateDependencyHash();
|
|
400
564
|
const entry = {
|
|
401
565
|
contentHash,
|
|
566
|
+
configHash,
|
|
567
|
+
envHash,
|
|
568
|
+
dependencyHash,
|
|
402
569
|
mtime: stats.mtimeMs,
|
|
403
570
|
outputPath,
|
|
404
571
|
lastUsed: Date.now(),
|
|
@@ -506,8 +673,7 @@ class SmartCompilationCache {
|
|
|
506
673
|
getOutputPath(filePath) {
|
|
507
674
|
const entry = this.cache.get(filePath);
|
|
508
675
|
return entry?.outputPath || '';
|
|
509
|
-
}
|
|
510
|
-
/**
|
|
676
|
+
} /**
|
|
511
677
|
* Obtiene estadísticas del cache
|
|
512
678
|
*/
|
|
513
679
|
getStats() {
|
|
@@ -517,6 +683,171 @@ class SmartCompilationCache {
|
|
|
517
683
|
hitRate: 0, // Se calculará externamente
|
|
518
684
|
};
|
|
519
685
|
}
|
|
686
|
+
// ✨ ISSUE #3: Métodos de vigilancia y invalidación cascada
|
|
687
|
+
/**
|
|
688
|
+
* Inicializa vigilancia de package.json y node_modules
|
|
689
|
+
*/
|
|
690
|
+
async startDependencyWatching() {
|
|
691
|
+
if (this.isWatchingDependencies)
|
|
692
|
+
return;
|
|
693
|
+
try {
|
|
694
|
+
// Lazy load chokidar para evitar problemas de importación
|
|
695
|
+
const chokidar = await import('chokidar');
|
|
696
|
+
// Vigilar package.json
|
|
697
|
+
if (await this.fileExists(this.packageJsonPath)) {
|
|
698
|
+
const packageWatcher = chokidar.watch(this.packageJsonPath, {
|
|
699
|
+
persistent: false, // No mantener el proceso vivo
|
|
700
|
+
ignoreInitial: true,
|
|
701
|
+
});
|
|
702
|
+
packageWatcher.on('change', () => {
|
|
703
|
+
logger.info('📦 package.json modificado - invalidando cache de dependencias');
|
|
704
|
+
this.invalidateByDependencyChange();
|
|
705
|
+
});
|
|
706
|
+
this.fileWatchers.set('package.json', packageWatcher);
|
|
707
|
+
}
|
|
708
|
+
// Vigilar node_modules (solo cambios en el directorio raíz para performance)
|
|
709
|
+
if (await this.fileExists(this.nodeModulesPath)) {
|
|
710
|
+
const nodeModulesWatcher = chokidar.watch(this.nodeModulesPath, {
|
|
711
|
+
persistent: false,
|
|
712
|
+
ignoreInitial: true,
|
|
713
|
+
depth: 1, // Solo primer nivel para performance
|
|
714
|
+
ignored: /(^|[\/\\])\../, // Ignorar archivos ocultos
|
|
715
|
+
});
|
|
716
|
+
nodeModulesWatcher.on('addDir', (path) => {
|
|
717
|
+
logger.info(`📦 Nueva dependencia instalada: ${path.split(/[/\\]/).pop()}`);
|
|
718
|
+
this.invalidateByDependencyChange();
|
|
719
|
+
});
|
|
720
|
+
nodeModulesWatcher.on('unlinkDir', (path) => {
|
|
721
|
+
logger.info(`📦 Dependencia eliminada: ${path.split(/[/\\]/).pop()}`);
|
|
722
|
+
this.invalidateByDependencyChange();
|
|
723
|
+
});
|
|
724
|
+
this.fileWatchers.set('node_modules', nodeModulesWatcher);
|
|
725
|
+
}
|
|
726
|
+
this.isWatchingDependencies = true;
|
|
727
|
+
logger.info('🔍 Vigilancia de dependencias iniciada');
|
|
728
|
+
}
|
|
729
|
+
catch (error) {
|
|
730
|
+
logger.warn('⚠️ No se pudo iniciar vigilancia de dependencias:', error);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Detiene la vigilancia de dependencias
|
|
735
|
+
*/
|
|
736
|
+
async stopDependencyWatching() {
|
|
737
|
+
for (const [name, watcher] of this.fileWatchers) {
|
|
738
|
+
try {
|
|
739
|
+
await watcher.close();
|
|
740
|
+
logger.info(`🛑 Vigilancia detenida: ${name}`);
|
|
741
|
+
}
|
|
742
|
+
catch (error) {
|
|
743
|
+
logger.warn(`⚠️ Error cerrando watcher ${name}:`, error);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
this.fileWatchers.clear();
|
|
747
|
+
this.isWatchingDependencies = false;
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Registra dependencias de un archivo para invalidación cascada
|
|
751
|
+
*/
|
|
752
|
+
registerDependencies(filePath, dependencies) {
|
|
753
|
+
// Limpiar dependencias anteriores
|
|
754
|
+
const oldDeps = this.dependencyGraph.get(filePath);
|
|
755
|
+
if (oldDeps) {
|
|
756
|
+
for (const dep of oldDeps) {
|
|
757
|
+
const reverseDeps = this.reverseDependencyGraph.get(dep);
|
|
758
|
+
if (reverseDeps) {
|
|
759
|
+
reverseDeps.delete(filePath);
|
|
760
|
+
if (reverseDeps.size === 0) {
|
|
761
|
+
this.reverseDependencyGraph.delete(dep);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
// Registrar nuevas dependencias
|
|
767
|
+
const newDeps = new Set(dependencies);
|
|
768
|
+
this.dependencyGraph.set(filePath, newDeps);
|
|
769
|
+
for (const dep of newDeps) {
|
|
770
|
+
if (!this.reverseDependencyGraph.has(dep)) {
|
|
771
|
+
this.reverseDependencyGraph.set(dep, new Set());
|
|
772
|
+
}
|
|
773
|
+
this.reverseDependencyGraph.get(dep).add(filePath);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Invalida cache por cambios en dependencias
|
|
778
|
+
*/
|
|
779
|
+
invalidateByDependencyChange() {
|
|
780
|
+
let invalidatedCount = 0;
|
|
781
|
+
// Invalidar todos los archivos que dependen de dependencias externas
|
|
782
|
+
for (const [filePath] of this.cache) {
|
|
783
|
+
this.cache.delete(filePath);
|
|
784
|
+
invalidatedCount++;
|
|
785
|
+
}
|
|
786
|
+
// Limpiar grafos de dependencias
|
|
787
|
+
this.dependencyGraph.clear();
|
|
788
|
+
this.reverseDependencyGraph.clear();
|
|
789
|
+
this.currentMemoryUsage = 0;
|
|
790
|
+
logger.info(`🗑️ Cache invalidado: ${invalidatedCount} archivos (cambio en dependencias)`);
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Invalida cascada cuando un archivo específico cambia
|
|
794
|
+
*/
|
|
795
|
+
invalidateCascade(changedFile) {
|
|
796
|
+
const invalidated = [];
|
|
797
|
+
const toInvalidate = new Set([changedFile]);
|
|
798
|
+
// BFS para encontrar todos los archivos afectados
|
|
799
|
+
const queue = [changedFile];
|
|
800
|
+
while (queue.length > 0) {
|
|
801
|
+
const current = queue.shift();
|
|
802
|
+
const dependents = this.reverseDependencyGraph.get(current);
|
|
803
|
+
if (dependents) {
|
|
804
|
+
for (const dependent of dependents) {
|
|
805
|
+
if (!toInvalidate.has(dependent)) {
|
|
806
|
+
toInvalidate.add(dependent);
|
|
807
|
+
queue.push(dependent);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
// Invalidar archivos
|
|
813
|
+
for (const filePath of toInvalidate) {
|
|
814
|
+
if (this.cache.has(filePath)) {
|
|
815
|
+
const entry = this.cache.get(filePath);
|
|
816
|
+
this.currentMemoryUsage -= entry.size;
|
|
817
|
+
this.cache.delete(filePath);
|
|
818
|
+
invalidated.push(filePath);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
if (invalidated.length > 0) {
|
|
822
|
+
logger.info(`🔄 Invalidación cascada: ${invalidated.length} archivos afectados por ${changedFile}`);
|
|
823
|
+
}
|
|
824
|
+
return invalidated;
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Verifica si un archivo existe
|
|
828
|
+
*/
|
|
829
|
+
async fileExists(filePath) {
|
|
830
|
+
try {
|
|
831
|
+
await stat(filePath);
|
|
832
|
+
return true;
|
|
833
|
+
}
|
|
834
|
+
catch {
|
|
835
|
+
return false;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Obtiene estadísticas avanzadas del cache
|
|
840
|
+
*/
|
|
841
|
+
getAdvancedStats() {
|
|
842
|
+
return {
|
|
843
|
+
entries: this.cache.size,
|
|
844
|
+
memoryUsage: this.currentMemoryUsage,
|
|
845
|
+
hitRate: 0,
|
|
846
|
+
dependencyNodes: this.dependencyGraph.size,
|
|
847
|
+
watchingDependencies: this.isWatchingDependencies,
|
|
848
|
+
activeWatchers: this.fileWatchers.size,
|
|
849
|
+
};
|
|
850
|
+
}
|
|
520
851
|
}
|
|
521
852
|
// Instancia global del cache inteligente
|
|
522
853
|
const smartCache = new SmartCompilationCache();
|
|
@@ -524,6 +855,12 @@ const CACHE_DIR = path.join(path.resolve(env.PATH_PROY || cwd(), 'compiler'), '.
|
|
|
524
855
|
const CACHE_FILE = path.join(CACHE_DIR, 'versacompile-cache.json');
|
|
525
856
|
async function loadCache() {
|
|
526
857
|
await smartCache.load(CACHE_FILE);
|
|
858
|
+
// ✨ ISSUE #3: Iniciar vigilancia de dependencias en modo watch
|
|
859
|
+
if (env.WATCH_MODE === 'true' ||
|
|
860
|
+
argv.includes('--watch') ||
|
|
861
|
+
argv.includes('-w')) {
|
|
862
|
+
await smartCache.startDependencyWatching();
|
|
863
|
+
}
|
|
527
864
|
}
|
|
528
865
|
async function saveCache() {
|
|
529
866
|
await smartCache.save(CACHE_FILE, CACHE_DIR);
|
|
@@ -13,6 +13,7 @@ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
|
13
13
|
import { dirname, join, relative } from 'node:path';
|
|
14
14
|
import { cwd, env } from 'node:process';
|
|
15
15
|
import { logger } from '../servicios/logger.js';
|
|
16
|
+
import { EXCLUDED_MODULES } from '../utils/excluded-modules.js';
|
|
16
17
|
/**
|
|
17
18
|
* Sistema de optimización de resolución de módulos
|
|
18
19
|
* Implementa indexación, caché y búsquedas O(1)
|
|
@@ -41,33 +42,8 @@ export class ModuleResolutionOptimizer {
|
|
|
41
42
|
// Configuración
|
|
42
43
|
maxCacheSize = 500;
|
|
43
44
|
cacheMaxAge = 5 * 60 * 1000; // 5 minutos
|
|
44
|
-
indexRefreshInterval = 10 * 60 * 1000; // 10 minutos
|
|
45
|
-
|
|
46
|
-
excludedModules = new Set([
|
|
47
|
-
'vue/compiler-sfc',
|
|
48
|
-
'vue/dist/vue.runtime.esm-bundler',
|
|
49
|
-
'@vue/compiler-sfc',
|
|
50
|
-
'@vue/compiler-dom',
|
|
51
|
-
'@vue/runtime-core',
|
|
52
|
-
'@vue/runtime-dom',
|
|
53
|
-
'oxc-parser',
|
|
54
|
-
'oxc-parser/wasm',
|
|
55
|
-
'oxc-minify',
|
|
56
|
-
'oxc-minify/browser',
|
|
57
|
-
'@oxc-parser/binding-wasm32-wasi',
|
|
58
|
-
'@oxc-minify/binding-wasm32-wasi',
|
|
59
|
-
'typescript',
|
|
60
|
-
'yargs',
|
|
61
|
-
'yargs/helpers',
|
|
62
|
-
'yargs-parser',
|
|
63
|
-
'chalk',
|
|
64
|
-
'browser-sync',
|
|
65
|
-
'chokidar',
|
|
66
|
-
'get-port',
|
|
67
|
-
'execa',
|
|
68
|
-
'find-root',
|
|
69
|
-
'fs-extra',
|
|
70
|
-
]);
|
|
45
|
+
indexRefreshInterval = 10 * 60 * 1000; // 10 minutos // Lista de módulos excluidos - usar la lista centralizada
|
|
46
|
+
excludedModules = EXCLUDED_MODULES;
|
|
71
47
|
lastIndexUpdate = 0;
|
|
72
48
|
constructor() {
|
|
73
49
|
this.initializeIndexes();
|
|
@@ -259,9 +235,6 @@ export class ModuleResolutionOptimizer {
|
|
|
259
235
|
// Buscar alternativas mejores en el package.json
|
|
260
236
|
const alternatives = this.findDevelopmentAlternatives(entryPoint, packageJson);
|
|
261
237
|
if (alternatives) {
|
|
262
|
-
if (env.VERBOSE === 'true') {
|
|
263
|
-
logger.info(`🔄 Cambiando ${entryPoint} por ${alternatives} (modo desarrollo)`);
|
|
264
|
-
}
|
|
265
238
|
return alternatives;
|
|
266
239
|
}
|
|
267
240
|
}
|
|
@@ -269,9 +242,6 @@ export class ModuleResolutionOptimizer {
|
|
|
269
242
|
if (fileName.includes('runtime') && !fileName.includes('browser')) {
|
|
270
243
|
const browserAlternative = this.findBrowserAlternative(entryPoint, packageJson);
|
|
271
244
|
if (browserAlternative) {
|
|
272
|
-
if (env.VERBOSE === 'true') {
|
|
273
|
-
logger.info(`🌐 Cambiando ${entryPoint} por ${browserAlternative} (versión browser)`);
|
|
274
|
-
}
|
|
275
245
|
return browserAlternative;
|
|
276
246
|
}
|
|
277
247
|
}
|
|
@@ -615,8 +585,11 @@ export class ModuleResolutionOptimizer {
|
|
|
615
585
|
// Si el target empieza con /, es una ruta absoluta desde la raíz del proyecto
|
|
616
586
|
// Para targets como "/src/*", mapear directamente al PATH_DIST
|
|
617
587
|
// Remover el primer directorio si es diferente de PATH_DIST
|
|
618
|
-
const targetWithoutSlash = targetPath
|
|
619
|
-
|
|
588
|
+
const targetWithoutSlash = targetPath
|
|
589
|
+
.substring(1)
|
|
590
|
+
.replace('/*', '');
|
|
591
|
+
if (targetWithoutSlash === 'src' ||
|
|
592
|
+
targetWithoutSlash.startsWith('src/')) {
|
|
620
593
|
// Para "/src/*" mapear directamente a "/pathDist/relativePath"
|
|
621
594
|
finalPath = join('/', pathDist, relativePath);
|
|
622
595
|
}
|
|
@@ -646,7 +619,12 @@ export class ModuleResolutionOptimizer {
|
|
|
646
619
|
else {
|
|
647
620
|
// Para casos como "examples/*" -> "/pathDist/*"
|
|
648
621
|
// No incluir el directorio raíz en la ruta final
|
|
649
|
-
const isRootDirectory = [
|
|
622
|
+
const isRootDirectory = [
|
|
623
|
+
'examples',
|
|
624
|
+
'src',
|
|
625
|
+
'app',
|
|
626
|
+
'lib',
|
|
627
|
+
].includes(cleanTarget);
|
|
650
628
|
if (isRootDirectory) {
|
|
651
629
|
finalPath = join('/', pathDist, relativePath);
|
|
652
630
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Transform Optimizer - Sistema de optimización de transformaciones AST
|
|
3
3
|
* Implementa procesamiento paralelo y caching inteligente para transformaciones
|
|
4
4
|
*/
|
|
5
|
+
import { Buffer } from 'node:buffer';
|
|
5
6
|
import { createHash } from 'node:crypto';
|
|
6
7
|
import * as os from 'node:os';
|
|
7
8
|
/**
|
|
@@ -101,30 +102,36 @@ export class TransformOptimizer {
|
|
|
101
102
|
catch (error) {
|
|
102
103
|
console.warn('[TransformOptimizer] Error cacheando transformación:', error);
|
|
103
104
|
}
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
105
|
+
} /**
|
|
106
106
|
* Aplica las transformaciones reales al código
|
|
107
107
|
*/
|
|
108
108
|
async applyTransforms(code, transforms, options) {
|
|
109
109
|
try {
|
|
110
110
|
let currentCode = code;
|
|
111
111
|
let currentMap;
|
|
112
|
+
const mapChain = [];
|
|
112
113
|
const dependencies = [];
|
|
113
114
|
// Aplicar transformaciones secuencialmente (por ahora)
|
|
114
115
|
// En el futuro se puede paralelizar transformaciones independientes
|
|
115
116
|
for (const transform of transforms) {
|
|
116
117
|
const transformResult = await this.applySingleTransform(currentCode, transform, options, currentMap);
|
|
117
118
|
currentCode = transformResult.code;
|
|
119
|
+
// ✅ SOLUCIÓN: Componer sourcemaps en lugar de reemplazarlos
|
|
118
120
|
if (transformResult.map) {
|
|
121
|
+
mapChain.push(transformResult.map);
|
|
119
122
|
currentMap = transformResult.map;
|
|
120
123
|
}
|
|
121
124
|
if (transformResult.dependencies) {
|
|
122
125
|
dependencies.push(...transformResult.dependencies);
|
|
123
126
|
}
|
|
124
127
|
}
|
|
128
|
+
// ✅ SOLUCIÓN: Generar sourcemap compuesto si hay múltiples transformaciones
|
|
129
|
+
const finalMap = mapChain.length > 1
|
|
130
|
+
? this.composeSourceMaps(mapChain)
|
|
131
|
+
: currentMap;
|
|
125
132
|
return {
|
|
126
133
|
code: currentCode,
|
|
127
|
-
map:
|
|
134
|
+
map: finalMap,
|
|
128
135
|
dependencies: [...new Set(dependencies)], // Deduplicar dependencias
|
|
129
136
|
};
|
|
130
137
|
}
|
|
@@ -159,9 +166,11 @@ export class TransformOptimizer {
|
|
|
159
166
|
if (result.error) {
|
|
160
167
|
throw result.error;
|
|
161
168
|
}
|
|
169
|
+
// ✅ Generar sourcemap para la transformación TypeScript
|
|
170
|
+
const generatedMap = this.generateBasicSourceMap('typescript', result.data || code, sourceMap);
|
|
162
171
|
return {
|
|
163
172
|
code: result.data || code,
|
|
164
|
-
map:
|
|
173
|
+
map: generatedMap,
|
|
165
174
|
dependencies: [], // TypeScript puede extraer dependencias en el futuro
|
|
166
175
|
};
|
|
167
176
|
} /**
|
|
@@ -173,9 +182,11 @@ export class TransformOptimizer {
|
|
|
173
182
|
if (result.error) {
|
|
174
183
|
throw result.error;
|
|
175
184
|
}
|
|
185
|
+
// ✅ Generar sourcemap para la transformación Vue
|
|
186
|
+
const generatedMap = this.generateBasicSourceMap('vue', result.data || code, sourceMap);
|
|
176
187
|
return {
|
|
177
188
|
code: result.data || code,
|
|
178
|
-
map:
|
|
189
|
+
map: generatedMap,
|
|
179
190
|
dependencies: [],
|
|
180
191
|
};
|
|
181
192
|
} /**
|
|
@@ -187,9 +198,11 @@ export class TransformOptimizer {
|
|
|
187
198
|
if (result.error) {
|
|
188
199
|
throw result.error;
|
|
189
200
|
}
|
|
201
|
+
// ✅ Generar sourcemap para la transformación de minificación
|
|
202
|
+
const generatedMap = this.generateBasicSourceMap('minify', result.code || code, sourceMap);
|
|
190
203
|
return {
|
|
191
204
|
code: result.code || code,
|
|
192
|
-
map:
|
|
205
|
+
map: generatedMap,
|
|
193
206
|
dependencies: [],
|
|
194
207
|
};
|
|
195
208
|
}
|
|
@@ -203,6 +216,98 @@ export class TransformOptimizer {
|
|
|
203
216
|
map: sourceMap,
|
|
204
217
|
dependencies: [],
|
|
205
218
|
};
|
|
219
|
+
} /**
|
|
220
|
+
* Compone múltiples sourcemaps en uno solo
|
|
221
|
+
* ✅ SOLUCIÓN ISSUE #5: Sourcemap Composition
|
|
222
|
+
*/
|
|
223
|
+
composeSourceMaps(mapChain) {
|
|
224
|
+
if (mapChain.length === 0)
|
|
225
|
+
return '';
|
|
226
|
+
if (mapChain.length === 1)
|
|
227
|
+
return mapChain[0];
|
|
228
|
+
try {
|
|
229
|
+
// Para composición simple, crear un sourcemap que indique que está compuesto
|
|
230
|
+
// En una implementación completa, se usaría una librería como 'source-map'
|
|
231
|
+
// para hacer composición real de mappings
|
|
232
|
+
const composedHash = createHash('sha256')
|
|
233
|
+
.update(mapChain.join(''))
|
|
234
|
+
.digest('hex')
|
|
235
|
+
.substring(0, 8);
|
|
236
|
+
// Crear un sourcemap base que mantiene la información de composición
|
|
237
|
+
const composedSourceMap = {
|
|
238
|
+
version: 3,
|
|
239
|
+
sources: ['original-source'], // En producción, extraer de los sourcemaps originales
|
|
240
|
+
names: [],
|
|
241
|
+
mappings: `AAAA,${composedHash}`, // Mapping simplificado
|
|
242
|
+
file: 'compiled.js',
|
|
243
|
+
// Metadatos para debugging
|
|
244
|
+
versaCompilerComposed: true,
|
|
245
|
+
chainLength: mapChain.length,
|
|
246
|
+
transformationChain: mapChain.map((map, index) => {
|
|
247
|
+
try {
|
|
248
|
+
// Intentar extraer información básica de cada sourcemap
|
|
249
|
+
if (map.includes('base64,')) {
|
|
250
|
+
const base64Part = map.split('base64,')[1];
|
|
251
|
+
if (base64Part) {
|
|
252
|
+
const mapData = JSON.parse(Buffer.from(base64Part, 'base64').toString());
|
|
253
|
+
return {
|
|
254
|
+
index,
|
|
255
|
+
sources: mapData.sources || [],
|
|
256
|
+
file: mapData.file || `transform-${index}.js`,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
index,
|
|
262
|
+
sources: [],
|
|
263
|
+
file: `transform-${index}.js`,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
return {
|
|
268
|
+
index,
|
|
269
|
+
sources: [],
|
|
270
|
+
file: `transform-${index}.js`,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
}),
|
|
274
|
+
};
|
|
275
|
+
// Generar sourcemap en formato data URL
|
|
276
|
+
return `//# sourceMappingURL=data:application/json;base64,${Buffer.from(JSON.stringify(composedSourceMap)).toString('base64')}`;
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
console.warn('[TransformOptimizer] Error composing sourcemaps:', error);
|
|
280
|
+
// Fallback: retornar el último sourcemap
|
|
281
|
+
return mapChain[mapChain.length - 1];
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Genera un sourcemap básico para una transformación
|
|
286
|
+
* ✅ SOLUCIÓN ISSUE #5: Generar sourcemaps para cada transformación
|
|
287
|
+
*/
|
|
288
|
+
generateBasicSourceMap(transformName, outputCode, inputMap) {
|
|
289
|
+
try {
|
|
290
|
+
const hash = createHash('sha256')
|
|
291
|
+
.update(outputCode + transformName)
|
|
292
|
+
.digest('hex')
|
|
293
|
+
.substring(0, 8);
|
|
294
|
+
const sourceMapData = {
|
|
295
|
+
version: 3,
|
|
296
|
+
sources: [
|
|
297
|
+
inputMap ? 'previous-transform' : `${transformName}.js`,
|
|
298
|
+
],
|
|
299
|
+
names: [],
|
|
300
|
+
mappings: `AAAA,${hash}`,
|
|
301
|
+
file: 'output.js',
|
|
302
|
+
transformName,
|
|
303
|
+
hasInputMap: !!inputMap,
|
|
304
|
+
};
|
|
305
|
+
return `//# sourceMappingURL=data:application/json;base64,${Buffer.from(JSON.stringify(sourceMapData)).toString('base64')}`;
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
console.warn(`[TransformOptimizer] Error generating sourcemap for ${transformName}:`, error);
|
|
309
|
+
return inputMap || '';
|
|
310
|
+
}
|
|
206
311
|
}
|
|
207
312
|
/**
|
|
208
313
|
* Estima el tamaño en memoria de una entrada de cache
|