versacompiler 2.4.0 → 2.5.0

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.
Files changed (33) hide show
  1. package/README.md +722 -722
  2. package/dist/compiler/compile-worker-pool.js +96 -0
  3. package/dist/compiler/compile-worker-thread.cjs +72 -0
  4. package/dist/compiler/compile.js +81 -2
  5. package/dist/compiler/integrity-validator.js +1 -1
  6. package/dist/compiler/module-resolution-optimizer.js +23 -20
  7. package/dist/compiler/performance-monitor.js +61 -61
  8. package/dist/compiler/pipeline/build-pipeline.js +127 -0
  9. package/dist/compiler/pipeline/core-plugins.js +218 -0
  10. package/dist/compiler/pipeline/module-graph.js +63 -0
  11. package/dist/compiler/pipeline/plugin-driver.js +87 -0
  12. package/dist/compiler/pipeline/types.js +2 -0
  13. package/dist/compiler/transforms.js +222 -16
  14. package/dist/compiler/typescript-manager.js +3 -1
  15. package/dist/compiler/typescript-sync-validator.js +33 -31
  16. package/dist/compiler/typescript-worker-pool.js +66 -19
  17. package/dist/compiler/typescript-worker-thread.cjs +482 -469
  18. package/dist/compiler/vuejs.js +32 -32
  19. package/dist/config.js +2 -0
  20. package/dist/hrm/VueHRM.js +359 -359
  21. package/dist/hrm/errorScreen.js +83 -83
  22. package/dist/hrm/getInstanciaVue.js +313 -313
  23. package/dist/hrm/initHRM.js +628 -586
  24. package/dist/main.js +2 -1
  25. package/dist/servicios/browserSync.js +8 -2
  26. package/dist/servicios/file-watcher.js +48 -6
  27. package/dist/servicios/readConfig.js +129 -54
  28. package/dist/servicios/versacompile.config.types.js +2 -0
  29. package/dist/utils/module-resolver.js +74 -40
  30. package/dist/utils/vue-types-setup.js +248 -248
  31. package/dist/wrappers/eslint-node.js +3 -1
  32. package/dist/wrappers/oxlint-node.js +3 -1
  33. package/package.json +73 -54
package/dist/main.js CHANGED
@@ -66,7 +66,8 @@ async function main() {
66
66
  const chalkInstance = await loadChalk();
67
67
  let yargInstance = yargsInstance(hideBinFn(globalProcess.argv))
68
68
  .scriptName('versa')
69
- .usage(chalkInstance.blue('VersaCompiler') + ' - Compilador de archivos Vue/TS/JS')
69
+ .usage(chalkInstance.blue('VersaCompiler') +
70
+ ' - Compilador de archivos Vue/TS/JS')
70
71
  .option('init', {
71
72
  type: 'boolean',
72
73
  description: 'Inicializar la configuración',
@@ -565,11 +565,17 @@ export async function browserSyncServer() {
565
565
  process.exit(1);
566
566
  }
567
567
  }
568
- export async function emitirCambios(bs, action, filePath) {
568
+ export async function emitirCambios(bs, action, filePath, payload = {}) {
569
569
  // ✨ OPTIMIZACIÓN: Emitir PRIMERO (crítico), logging DESPUÉS (no crítico)
570
570
  const normalizedPath = path.normalize(filePath).replace(/\\/g, '/');
571
571
  const nameFile = path.basename(normalizedPath, path.extname(normalizedPath));
572
- bs.sockets.emit(action, { action, filePath, normalizedPath, nameFile });
572
+ bs.sockets.emit(action, {
573
+ action,
574
+ filePath,
575
+ normalizedPath,
576
+ nameFile,
577
+ ...payload,
578
+ });
573
579
  // Logging asíncrono para no bloquear la emisión
574
580
  setImmediate(async () => {
575
581
  const chalkInstance = await loadChalk();
@@ -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 { clearCompilationState, getOutputPath, initCompile, normalizeRuta } from '../compiler/compile.js';
7
+ import { clearCompilationState, getOutputPath, getPipelineModuleGraph, initCompile, normalizeRuta, runPipelineHotUpdate, } 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';
@@ -147,13 +147,32 @@ class WatchDebouncer {
147
147
  */
148
148
  async processCompilableFiles(compilableFiles) {
149
149
  const chalkInstance = await loadChalk();
150
+ const graph = getPipelineModuleGraph();
150
151
  // Procesar en batches para evitar sobrecarga
151
152
  for (let i = 0; i < compilableFiles.length; i += this.BATCH_SIZE) {
152
153
  const batch = compilableFiles.slice(i, i + this.BATCH_SIZE);
153
154
  if (batch.length > 1) {
154
155
  logger.info(chalkInstance.cyan(`📦 Procesando batch de ${batch.length} archivos compilables (${i + 1}-${Math.min(i + this.BATCH_SIZE, compilableFiles.length)} de ${compilableFiles.length})`));
155
156
  }
156
- const promises = batch.map(change => this.compileFile(change));
157
+ const expandedChanges = new Map();
158
+ for (const change of batch) {
159
+ expandedChanges.set(change.filePath, change);
160
+ if (graph) {
161
+ const invalidated = graph.invalidate(change.filePath);
162
+ for (const invalidatedPath of invalidated) {
163
+ if (expandedChanges.has(invalidatedPath))
164
+ continue;
165
+ expandedChanges.set(invalidatedPath, {
166
+ filePath: invalidatedPath,
167
+ action: 'change',
168
+ timestamp: Date.now(),
169
+ extensionAction: 'reloadFull',
170
+ isAdditionalFile: false,
171
+ });
172
+ }
173
+ }
174
+ }
175
+ const promises = Array.from(expandedChanges.values()).map(change => this.compileFile(change));
157
176
  await Promise.allSettled(promises);
158
177
  }
159
178
  if (compilableFiles.length > 1) {
@@ -177,12 +196,37 @@ class WatchDebouncer {
177
196
  */
178
197
  async compileFile(change) {
179
198
  try {
199
+ let pluginHmrReload = 'none';
200
+ if (change.action !== 'unlink') {
201
+ const hotUpdate = await runPipelineHotUpdate(change.filePath, change.action);
202
+ pluginHmrReload = hotUpdate.reload || 'none';
203
+ }
180
204
  const result = await initCompile(change.filePath, true, 'watch');
181
205
  if (result.success) {
182
206
  let accion = result.action || change.extensionAction;
183
207
  accion =
184
208
  accion === 'extension' ? change.extensionAction : accion;
185
- emitirCambios(this.browserSyncInstance, accion || 'reloadFull', result.output);
209
+ let payload = {};
210
+ if (accion === 'HRMHelper') {
211
+ if (pluginHmrReload === 'full') {
212
+ accion = 'reloadFull';
213
+ }
214
+ const graph = getPipelineModuleGraph();
215
+ const node = graph?.getNode(change.filePath);
216
+ const importers = node ? Array.from(node.importers) : [];
217
+ const strategy = importers.length > 0 ? 'propagate' : 'full-reload';
218
+ if (pluginHmrReload === 'module') {
219
+ payload.strategy = 'propagate';
220
+ }
221
+ else {
222
+ payload.strategy = strategy;
223
+ }
224
+ payload = {
225
+ importers,
226
+ ...payload,
227
+ };
228
+ }
229
+ emitirCambios(this.browserSyncInstance, accion || 'reloadFull', result.output, payload);
186
230
  }
187
231
  }
188
232
  catch (error) {
@@ -355,9 +399,7 @@ export async function initChokidar(bs) {
355
399
  // ✨ OPTIMIZACIÓN: Pre-cargar módulos críticos al iniciar el watcher
356
400
  watcher.on('ready', async () => {
357
401
  const chalkInstance = await loadChalk();
358
- logger.info(chalkInstance.green(`👀 : Listo para observar \n${fileWatch
359
- .map((item) => `${item}`)
360
- .join('\n')}\n`));
402
+ logger.info(chalkInstance.green(`👀 : Listo para observar \n${fileWatch.map((item) => `${item}`).join('\n')}\n`));
361
403
  // Pre-cargar módulos críticos para primera compilación más rápida
362
404
  setImmediate(async () => {
363
405
  try {
@@ -25,7 +25,7 @@ export function validatePath(pathStr) {
25
25
  return false;
26
26
  }
27
27
  // Rechazar rutas absolutas de Windows (válido en cualquier plataforma)
28
- if (/^[A-Za-z]:[\\\/]/.test(pathStr)) {
28
+ if (/^[A-Za-z]:[/\\]/.test(pathStr)) {
29
29
  logger.error(`Ruta absoluta de Windows no permitida: ${pathStr}`);
30
30
  return false;
31
31
  }
@@ -238,6 +238,80 @@ function safeJsonStringify(obj, fallback = 'false') {
238
238
  return fallback;
239
239
  }
240
240
  }
241
+ function normalizeAliasKey(key) {
242
+ if (key.includes('*'))
243
+ return key;
244
+ if (key.endsWith('/'))
245
+ return `${key}*`;
246
+ return `${key}/*`;
247
+ }
248
+ function normalizeAliasValue(value) {
249
+ if (value.includes('*'))
250
+ return value;
251
+ if (value.endsWith('/'))
252
+ return `${value}*`;
253
+ return `${value}/*`;
254
+ }
255
+ function normalizeAlias(alias) {
256
+ if (!alias)
257
+ return {};
258
+ const result = {};
259
+ if (Array.isArray(alias)) {
260
+ for (const entry of alias) {
261
+ if (!entry || typeof entry.find !== 'string')
262
+ continue;
263
+ if (typeof entry.replacement !== 'string')
264
+ continue;
265
+ const key = normalizeAliasKey(entry.find);
266
+ const value = normalizeAliasValue(entry.replacement);
267
+ result[key] = [value];
268
+ }
269
+ return result;
270
+ }
271
+ for (const [key, value] of Object.entries(alias)) {
272
+ const normalizedKey = normalizeAliasKey(key);
273
+ if (Array.isArray(value)) {
274
+ result[normalizedKey] = value
275
+ .filter(v => typeof v === 'string')
276
+ .map(v => normalizeAliasValue(v));
277
+ }
278
+ else if (typeof value === 'string') {
279
+ result[normalizedKey] = [normalizeAliasValue(value)];
280
+ }
281
+ }
282
+ return result;
283
+ }
284
+ function normalizeConfig(input) {
285
+ if (input?.compilerOptions) {
286
+ return input;
287
+ }
288
+ const viteConfig = input;
289
+ const aliases = normalizeAlias(viteConfig?.resolve?.alias);
290
+ return {
291
+ tsconfig: viteConfig?.tsconfig,
292
+ compilerOptions: {
293
+ sourceRoot: viteConfig?.root || './src',
294
+ outDir: viteConfig?.build?.outDir || './dist',
295
+ pathsAlias: aliases,
296
+ },
297
+ plugins: viteConfig?.plugins,
298
+ proxyConfig: {
299
+ proxyUrl: viteConfig?.server?.proxyUrl || '',
300
+ assetsOmit: viteConfig?.server?.assetsOmit ?? false,
301
+ },
302
+ aditionalWatch: viteConfig?.watch?.additional ||
303
+ viteConfig?.server?.watch?.additional ||
304
+ [],
305
+ tailwindConfig: viteConfig?.tailwindConfig,
306
+ linter: viteConfig?.linter,
307
+ bundlers: viteConfig?.build?.bundlers,
308
+ typeCheckOptions: viteConfig?.typeCheckOptions,
309
+ };
310
+ }
311
+ let loadedConfig = null;
312
+ export function getLoadedConfig() {
313
+ return loadedConfig;
314
+ }
241
315
  /**
242
316
  * Wrapper para el import dinámico que permite mejor testing
243
317
  */
@@ -272,7 +346,8 @@ export async function readConfig() {
272
346
  if (!data) {
273
347
  throw new Error('No se pudo leer el archivo de configuración.');
274
348
  }
275
- const tsConfig = data.default || data;
349
+ const rawConfig = data.default || data;
350
+ const tsConfig = normalizeConfig(rawConfig);
276
351
  // Validar tamaño de configuración
277
352
  if (!validateConfigSize(tsConfig)) {
278
353
  throw new Error('Configuración demasiado grande o contiene referencias circulares.');
@@ -323,6 +398,7 @@ export async function readConfig() {
323
398
  if (!tsConfig.compilerOptions.sourceRoot) {
324
399
  env.tsConfig = safeJsonStringify(tsConfig, '{}');
325
400
  }
401
+ loadedConfig = tsConfig;
326
402
  logger.info('✅ Configuration loaded and validated successfully');
327
403
  return true;
328
404
  }
@@ -347,58 +423,57 @@ export async function initConfig() {
347
423
  logger.warn(`🚩 El archivo de configuración '${env.PATH_CONFIG_FILE}' ya existe.`);
348
424
  return true;
349
425
  }
350
- const configContent = `// Archivo de configuración de VersaCompiler
351
- export default {
352
- tsconfig: './tsconfig.json',
353
- compilerOptions: {
354
- sourceRoot: './src',
355
- outDir: './dist',
356
- pathsAlias: {
357
- '/dist/examples/*': ['src/*'],
358
- '/dist/public/*': ['public/*'],
359
- },
360
- },
361
- proxyConfig: {
362
- proxyUrl: '',
363
- assetsOmit: true,
364
- },
365
- aditionalWatch: ['./app/templates/**/*.twig', './app/templates/**/*.html'],
366
- // puede dejar en false o no agregarlo si no quiere que se ejecute el compilador de tailwind
367
- tailwindConfig: {
368
- bin: './node_modules/.bin/tailwindcss',
369
- input: './src/css/input.css',
370
- output: './public/css/output.css',
371
- },
372
- linter: [
373
- {
374
- name: 'eslint',
375
- bin: './node_modules/.bin/eslint',
376
- configFile: './.eslintrc.json',
377
- fix: false,
378
- paths: ['src/']
379
- },
380
- {
381
- name: 'oxlint',
382
- bin: './node_modules/.bin/oxlint',
383
- configFile: './.oxlintrc.json',
384
- fix: false,
385
- paths: ['src/']
386
- },
387
- ],
388
- // Configuración de bundlers
389
- bundlers: [
390
- {
391
- name: 'appLoader',
392
- fileInput: './public/module/appLoader.js',
393
- fileOutput: './public/module/appLoader.prod.js',
394
- },
395
- {
396
- name: 'mainApp',
397
- fileInput: './src/main.ts',
398
- fileOutput: './dist/main.bundle.js',
399
- }
400
- ],
401
- };
426
+ const configContent = `import { defineConfig } from 'versacompiler/config';
427
+
428
+ export default defineConfig({
429
+ root: './src',
430
+ build: {
431
+ outDir: './dist',
432
+ bundlers: [
433
+ {
434
+ name: 'appLoader',
435
+ fileInput: './public/module/appLoader.js',
436
+ fileOutput: './public/module/appLoader.prod.js',
437
+ },
438
+ ],
439
+ },
440
+ resolve: {
441
+ alias: {
442
+ '/dist': 'src',
443
+ '/dist/public': 'public',
444
+ },
445
+ },
446
+ server: {
447
+ proxyUrl: '',
448
+ assetsOmit: true,
449
+ watch: {
450
+ additional: ['./app/templates/**/*.twig', './app/templates/**/*.html'],
451
+ },
452
+ },
453
+ tsconfig: './tsconfig.json',
454
+ tailwindConfig: {
455
+ bin: './node_modules/.bin/tailwindcss',
456
+ input: './src/css/input.css',
457
+ output: './public/css/output.css',
458
+ },
459
+ linter: [
460
+ {
461
+ name: 'eslint',
462
+ bin: './node_modules/.bin/eslint',
463
+ configFile: './.eslintrc.json',
464
+ fix: false,
465
+ paths: ['src/'],
466
+ },
467
+ {
468
+ name: 'oxlint',
469
+ bin: './node_modules/.bin/oxlint',
470
+ configFile: './.oxlintrc.json',
471
+ fix: false,
472
+ paths: ['src/'],
473
+ },
474
+ ],
475
+ plugins: [],
476
+ });
402
477
  `;
403
478
  fs.writeFileSync(configPath, configContent, 'utf8');
404
479
  logger.info(`🚩 Archivo de configuración '${env.PATH_CONFIG_FILE}' creado correctamente.`);
@@ -0,0 +1,2 @@
1
+ export const defineConfig = (config) => config;
2
+ //# sourceMappingURL=versacompile.config.types.js.map
@@ -1,9 +1,9 @@
1
- // Opción con librería '/node_modules/resolve/index.js' (npm install resolve)
1
+ // Opción con librería 'resolve' (npm install resolve)
2
2
  import fs, { readFileSync } from 'node:fs';
3
3
  import { dirname, join, relative } from 'node:path';
4
4
  import { cwd, env } from 'node:process';
5
- // import pkg from '/node_modules/enhanced-resolve/lib/index.js';
6
- // import resolve from '/node_modules/resolve/index.js';
5
+ // import pkg from 'enhanced-resolve';
6
+ // import resolve from 'resolve';
7
7
  import { logger } from '../servicios/logger.js';
8
8
  import { EXCLUDED_MODULES } from './excluded-modules.js';
9
9
  class PackageJsonCache {
@@ -72,7 +72,7 @@ const packageJsonCache = PackageJsonCache.getInstance();
72
72
  // return null;
73
73
  // }
74
74
  // }
75
- // Opción con '/node_modules/enhanced-resolve/lib/index.js' (webpack's resolver)
75
+ // Opción con 'enhanced-resolve' (webpack's resolver)
76
76
  // npm install enhanced-resolve
77
77
  // const { ResolverFactory } = pkg;
78
78
  // const resolver = ResolverFactory.createResolver({
@@ -350,26 +350,7 @@ function simpleESMResolver(moduleName) {
350
350
  entryPoint = dotExport;
351
351
  }
352
352
  else if (typeof dotExport === 'object') {
353
- // Priorizar import > browser > default para compatibilidad ESM
354
- // Buscar específicamente patrones esm-browser primero
355
- const exportKeys = Object.keys(dotExport);
356
- const esmBrowserKey = exportKeys.find(key => key.includes('browser') &&
357
- (key.includes('esm') || key.includes('module')));
358
- if (esmBrowserKey &&
359
- typeof dotExport[esmBrowserKey] === 'string') {
360
- entryPoint = dotExport[esmBrowserKey];
361
- }
362
- else {
363
- // Priorizar import > browser > default
364
- entryPoint =
365
- (typeof dotExport.import === 'string'
366
- ? dotExport.import
367
- : null) ||
368
- dotExport.browser ||
369
- (typeof dotExport.default === 'string'
370
- ? dotExport.default
371
- : null);
372
- }
353
+ entryPoint = resolveExportValue(dotExport);
373
354
  }
374
355
  }
375
356
  }
@@ -489,6 +470,64 @@ function getNodeModulesRelativePath(fullPath, _fromFile) {
489
470
  }
490
471
  return rel;
491
472
  }
473
+ /**
474
+ * Parses a module specifier into packageName and subPath.
475
+ * Handles scoped packages correctly.
476
+ * Examples:
477
+ * '@vueuse/core' → { packageName: '@vueuse/core', subPath: '' }
478
+ * '@vueuse/core/sub' → { packageName: '@vueuse/core', subPath: 'sub' }
479
+ * 'vue/dist/x' → { packageName: 'vue', subPath: 'dist/x' }
480
+ * 'vue' → { packageName: 'vue', subPath: '' }
481
+ */
482
+ export function parseModuleSpecifier(moduleName) {
483
+ if (moduleName.startsWith('@')) {
484
+ const parts = moduleName.split('/');
485
+ if (parts.length < 2 || !parts[1]) {
486
+ return { packageName: moduleName, subPath: '' };
487
+ }
488
+ const packageName = `${parts[0]}/${parts[1]}`;
489
+ const subPath = parts.slice(2).join('/');
490
+ return { packageName, subPath };
491
+ }
492
+ else {
493
+ const slashIdx = moduleName.indexOf('/');
494
+ if (slashIdx === -1) {
495
+ return { packageName: moduleName, subPath: '' };
496
+ }
497
+ return {
498
+ packageName: moduleName.slice(0, slashIdx),
499
+ subPath: moduleName.slice(slashIdx + 1),
500
+ };
501
+ }
502
+ }
503
+ /**
504
+ * Recursively resolves a conditional export value.
505
+ * Priority: import > browser > module > default
506
+ * Handles nested objects up to a depth limit.
507
+ */
508
+ export function resolveExportValue(exportVal, depth = 0) {
509
+ if (depth > 5)
510
+ return null;
511
+ if (typeof exportVal === 'string')
512
+ return exportVal;
513
+ if (typeof exportVal === 'object' && exportVal !== null) {
514
+ const obj = exportVal;
515
+ for (const key of [
516
+ 'import',
517
+ 'browser',
518
+ 'module',
519
+ 'default',
520
+ 'require',
521
+ ]) {
522
+ if (obj[key] !== undefined) {
523
+ const resolved = resolveExportValue(obj[key], depth + 1);
524
+ if (resolved)
525
+ return resolved;
526
+ }
527
+ }
528
+ }
529
+ return null;
530
+ }
492
531
  export function getModulePath(moduleName, fromFile) {
493
532
  // Verificar si el módulo está en la lista de excluidos
494
533
  if (EXCLUDED_MODULES.has(moduleName)) {
@@ -501,14 +540,15 @@ export function getModuleSubPath(moduleName, fromFile) {
501
540
  // Verificar si el módulo está en la lista de excluidos
502
541
  if (EXCLUDED_MODULES.has(moduleName)) {
503
542
  return null; // Retornar null para mantener la importación original
504
- } // Si contiene '/', es un subpath
543
+ }
544
+ // Usar parseModuleSpecifier para manejar correctamente paquetes con scope
545
+ const { packageName, subPath } = parseModuleSpecifier(moduleName);
546
+ // Si no hay subPath, delegar al resolver normal
547
+ if (!subPath) {
548
+ return getModulePath(moduleName, fromFile);
549
+ }
550
+ // Es un subpath: resolver con el packageName correcto
505
551
  if (moduleName.includes('/')) {
506
- const [packageName, ...subPathParts] = moduleName.split('/');
507
- const subPath = subPathParts.join('/');
508
- // Verificar que packageName no esté vacío
509
- if (!packageName) {
510
- return null;
511
- }
512
552
  try {
513
553
  const nodeModulesPath = join(cwd(), 'node_modules', packageName);
514
554
  const packagePath = join(nodeModulesPath, 'package.json');
@@ -525,15 +565,9 @@ export function getModuleSubPath(moduleName, fromFile) {
525
565
  const exportKey = `./${subPath}`;
526
566
  const exportPath = packageJson.exports[exportKey];
527
567
  if (exportPath) {
528
- if (typeof exportPath === 'string') {
529
- return getNodeModulesRelativePath(join(moduleDir, exportPath), fromFile);
530
- }
531
- else if (typeof exportPath === 'object') {
532
- // Priorizar import > default para ESM
533
- const importPath = exportPath.import || exportPath.default;
534
- if (typeof importPath === 'string') {
535
- return getNodeModulesRelativePath(join(moduleDir, importPath), fromFile);
536
- }
568
+ const resolved = resolveExportValue(exportPath);
569
+ if (resolved) {
570
+ return getNodeModulesRelativePath(join(moduleDir, resolved), fromFile);
537
571
  }
538
572
  }
539
573
  }