versacompiler 2.4.1 → 2.6.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 (52) hide show
  1. package/README.md +722 -722
  2. package/dist/compiler/compile-worker-pool.js +108 -0
  3. package/dist/compiler/compile-worker-thread.cjs +72 -0
  4. package/dist/compiler/compile.js +177 -18
  5. package/dist/compiler/error-reporter.js +12 -0
  6. package/dist/compiler/integrity-validator.js +13 -1
  7. package/dist/compiler/linter.js +12 -0
  8. package/dist/compiler/minify.js +12 -0
  9. package/dist/compiler/minifyTemplate.js +12 -0
  10. package/dist/compiler/module-resolution-optimizer.js +35 -20
  11. package/dist/compiler/parser.js +12 -0
  12. package/dist/compiler/performance-monitor.js +73 -61
  13. package/dist/compiler/pipeline/build-pipeline.js +139 -0
  14. package/dist/compiler/pipeline/core-plugins.js +230 -0
  15. package/dist/compiler/pipeline/module-graph.js +75 -0
  16. package/dist/compiler/pipeline/plugin-driver.js +99 -0
  17. package/dist/compiler/pipeline/types.js +14 -0
  18. package/dist/compiler/tailwindcss.js +12 -0
  19. package/dist/compiler/transform-optimizer.js +12 -0
  20. package/dist/compiler/transformTStoJS.js +38 -5
  21. package/dist/compiler/transforms.js +234 -16
  22. package/dist/compiler/typescript-compiler.js +12 -0
  23. package/dist/compiler/typescript-error-parser.js +12 -0
  24. package/dist/compiler/typescript-manager.js +15 -1
  25. package/dist/compiler/typescript-sync-validator.js +45 -31
  26. package/dist/compiler/typescript-worker-pool.js +12 -0
  27. package/dist/compiler/typescript-worker-thread.cjs +482 -475
  28. package/dist/compiler/typescript-worker.js +12 -0
  29. package/dist/compiler/vuejs.js +73 -47
  30. package/dist/config.js +14 -0
  31. package/dist/hrm/VueHRM.js +484 -359
  32. package/dist/hrm/errorScreen.js +95 -83
  33. package/dist/hrm/getInstanciaVue.js +325 -313
  34. package/dist/hrm/initHRM.js +736 -586
  35. package/dist/hrm/versaHMR.js +317 -0
  36. package/dist/main.js +23 -3
  37. package/dist/servicios/browserSync.js +127 -6
  38. package/dist/servicios/file-watcher.js +139 -8
  39. package/dist/servicios/logger.js +12 -0
  40. package/dist/servicios/readConfig.js +141 -54
  41. package/dist/servicios/versacompile.config.types.js +14 -0
  42. package/dist/utils/excluded-modules.js +12 -0
  43. package/dist/utils/module-resolver.js +86 -40
  44. package/dist/utils/promptUser.js +12 -0
  45. package/dist/utils/proxyValidator.js +12 -0
  46. package/dist/utils/resolve-bin.js +12 -0
  47. package/dist/utils/utils.js +12 -0
  48. package/dist/utils/vue-types-setup.js +260 -248
  49. package/dist/wrappers/eslint-node.js +15 -1
  50. package/dist/wrappers/oxlint-node.js +15 -1
  51. package/dist/wrappers/tailwind-node.js +12 -0
  52. package/package.json +74 -54
@@ -1,12 +1,24 @@
1
- import { readdir, rm, stat, unlink } from 'node:fs/promises';
1
+ /* VersaCompiler HMR shim [dev] */
2
+ if (typeof window !== 'undefined' && window.__versaHMR) {
3
+ (() => {
4
+ const _id = new URL(import.meta.url).pathname;
5
+ import.meta.hot = {
6
+ accept(cb) { window.__versaHMR.accept(_id, typeof cb === 'function' ? cb : () => {}); },
7
+ invalidate() { window.__versaHMR._invalidate?.(_id); },
8
+ dispose(cb) { window.__versaHMR._onDispose?.(_id, cb); },
9
+ get data() { return window.__versaHMR._getHotData?.(_id) ?? {}; },
10
+ };
11
+ })();
12
+ }
13
+ import { readdir, readFile, rm, stat, unlink } from 'node:fs/promises';
2
14
  import * as path from 'node:path';
3
15
  import * as process from 'node:process';
4
16
  const { env } = process;
5
17
  import * as chokidar from 'chokidar';
6
18
  import { minimatch } from 'minimatch';
7
- import { clearCompilationState, getOutputPath, initCompile, normalizeRuta } from '../compiler/compile.js';
19
+ import { clearCompilationState, getOutputPath, getPipelineModuleGraph, initCompile, normalizeRuta, runPipelineHotUpdate, } from '../compiler/compile.js';
8
20
  import { promptUser } from '../utils/promptUser.js';
9
- import { emitirCambios } from './browserSync.js';
21
+ import { emitirCambios, registerHMRUpdate } from './browserSync.js';
10
22
  import { logger } from './logger.js';
11
23
  // Lazy loading para chalk
12
24
  let chalk;
@@ -16,6 +28,49 @@ async function loadChalk() {
16
28
  }
17
29
  return chalk;
18
30
  }
31
+ /**
32
+ * Analiza el contenido de un módulo JS compilado para determinar si es seguro
33
+ * hacer HMR sin full-reload. Un módulo es "simple" si:
34
+ * - Solo exporta funciones, constantes o valores (no clases con estado ni patrones singleton)
35
+ * - No usa new/class en el scope raíz (como estado global mutable)
36
+ *
37
+ * @param outputPath - Ruta del archivo compilado a analizar
38
+ * @returns 'propagate' si es seguro sin full-reload, 'full-reload' si no se puede determinar
39
+ */
40
+ async function analyzeCompiledModuleStrategy(outputPath) {
41
+ try {
42
+ const content = await readFile(outputPath, 'utf8');
43
+ // Si el módulo fue compilado por VersaCompiler en modo dev, el shim HMR
44
+ // está inyectado y gestiona la estrategia de reemplazo vía versaHMR.
45
+ // No analizar el shim como side-effect — siempre es propagate.
46
+ if (content.startsWith('/* VersaCompiler HMR shim [dev] */')) {
47
+ return 'propagate';
48
+ }
49
+ // Si el módulo declara que acepta HMR, propagate es seguro
50
+ if (/import\.meta\.hot\.accept/.test(content)) {
51
+ return 'propagate';
52
+ }
53
+ // Heurística: módulos con solo exports de funciones/constantes son seguros
54
+ const hasExports = /export\s+(const|let|function|async function|class\b)/.test(content) || /export\s+default/.test(content);
55
+ if (!hasExports) {
56
+ return 'full-reload';
57
+ }
58
+ // Señales de estado global mutable o efectos secundarios al import:
59
+ // new ClassName() en scope raíz, módulos con side-effects de init
60
+ const hasRootLevelSideEffects =
61
+ // new en scope raíz (fuera de funciones/clases) — detectar heurísticamente
62
+ /^\s*(?:const|let|var)\s+\w+\s*=\s*new\s+\w+/m.test(content) ||
63
+ // Llamadas a funciones en scope raíz que sugieren init
64
+ /^\s*(?:init|setup|bootstrap|start|connect|register)\s*\(/m.test(content);
65
+ if (hasRootLevelSideEffects) {
66
+ return 'full-reload';
67
+ }
68
+ return 'propagate';
69
+ }
70
+ catch {
71
+ return 'full-reload';
72
+ }
73
+ }
19
74
  class WatchDebouncer {
20
75
  pendingChanges = new Map();
21
76
  debounceTimer = null;
@@ -147,13 +202,44 @@ class WatchDebouncer {
147
202
  */
148
203
  async processCompilableFiles(compilableFiles) {
149
204
  const chalkInstance = await loadChalk();
205
+ const graph = getPipelineModuleGraph();
150
206
  // Procesar en batches para evitar sobrecarga
151
207
  for (let i = 0; i < compilableFiles.length; i += this.BATCH_SIZE) {
152
208
  const batch = compilableFiles.slice(i, i + this.BATCH_SIZE);
153
209
  if (batch.length > 1) {
154
210
  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
211
  }
156
- const promises = batch.map(change => this.compileFile(change));
212
+ const expandedChanges = new Map();
213
+ for (const change of batch) {
214
+ expandedChanges.set(change.filePath, change);
215
+ if (graph) {
216
+ const invalidated = graph.invalidate(change.filePath);
217
+ for (const invalidatedPath of invalidated) {
218
+ if (expandedChanges.has(invalidatedPath))
219
+ continue;
220
+ // Usar la acción correcta según la extensión del archivo invalidado
221
+ // en lugar de forzar 'reloadFull' para todos los importers
222
+ const invalidatedExt = path
223
+ .extname(invalidatedPath)
224
+ .replace('.', '');
225
+ const invalidatedAction = {
226
+ vue: 'HRMVue',
227
+ ts: 'HRMHelper',
228
+ js: 'HRMHelper',
229
+ mjs: 'HRMHelper',
230
+ cjs: 'HRMHelper',
231
+ }[invalidatedExt] ?? 'reloadFull';
232
+ expandedChanges.set(invalidatedPath, {
233
+ filePath: invalidatedPath,
234
+ action: 'change',
235
+ timestamp: Date.now(),
236
+ extensionAction: invalidatedAction,
237
+ isAdditionalFile: false,
238
+ });
239
+ }
240
+ }
241
+ }
242
+ const promises = Array.from(expandedChanges.values()).map(change => this.compileFile(change));
157
243
  await Promise.allSettled(promises);
158
244
  }
159
245
  if (compilableFiles.length > 1) {
@@ -177,12 +263,59 @@ class WatchDebouncer {
177
263
  */
178
264
  async compileFile(change) {
179
265
  try {
266
+ let pluginHmrReload = 'none';
267
+ if (change.action !== 'unlink') {
268
+ const hotUpdate = await runPipelineHotUpdate(change.filePath, change.action);
269
+ pluginHmrReload = hotUpdate.reload || 'none';
270
+ }
180
271
  const result = await initCompile(change.filePath, true, 'watch');
181
272
  if (result.success) {
273
+ // Registrar el output para que el middleware HMR reescriba imports dependientes
274
+ if (result.output) {
275
+ registerHMRUpdate(result.output);
276
+ }
182
277
  let accion = result.action || change.extensionAction;
183
278
  accion =
184
279
  accion === 'extension' ? change.extensionAction : accion;
185
- emitirCambios(this.browserSyncInstance, accion || 'reloadFull', result.output);
280
+ let payload = {};
281
+ if (accion === 'HRMHelper') {
282
+ if (pluginHmrReload === 'full') {
283
+ accion = 'reloadFull';
284
+ }
285
+ else {
286
+ const graph = getPipelineModuleGraph();
287
+ const node = graph?.getNode(change.filePath);
288
+ const importers = node
289
+ ? Array.from(node.importers)
290
+ : [];
291
+ // Determinar estrategia base:
292
+ // Si el plugin dice 'module' → propagate garantizado
293
+ // Si hay importers conocidos → propagate (los consumers actualizarán sus refs)
294
+ // Si no hay importers → analizar el módulo compilado para detectar si es "simple"
295
+ let strategy;
296
+ if (pluginHmrReload === 'module') {
297
+ strategy = 'propagate';
298
+ }
299
+ else if (importers.length > 0) {
300
+ strategy = 'propagate';
301
+ }
302
+ else {
303
+ // Sin importers conocidos: analizar el output compilado
304
+ strategy = await analyzeCompiledModuleStrategy(result.output);
305
+ }
306
+ // moduleId: path del output relativo al proyecto para que el cliente
307
+ // pueda hacer lookup en VersaModuleRegistry
308
+ const moduleId = result.output.startsWith('/')
309
+ ? result.output
310
+ : `/${result.output}`;
311
+ payload = {
312
+ moduleId,
313
+ importers,
314
+ strategy,
315
+ };
316
+ }
317
+ }
318
+ emitirCambios(this.browserSyncInstance, accion || 'reloadFull', result.output, payload);
186
319
  }
187
320
  }
188
321
  catch (error) {
@@ -355,9 +488,7 @@ export async function initChokidar(bs) {
355
488
  // ✨ OPTIMIZACIÓN: Pre-cargar módulos críticos al iniciar el watcher
356
489
  watcher.on('ready', async () => {
357
490
  const chalkInstance = await loadChalk();
358
- logger.info(chalkInstance.green(`👀 : Listo para observar \n${fileWatch
359
- .map((item) => `${item}`)
360
- .join('\n')}\n`));
491
+ logger.info(chalkInstance.green(`👀 : Listo para observar \n${fileWatch.map((item) => `${item}`).join('\n')}\n`));
361
492
  // Pre-cargar módulos críticos para primera compilación más rápida
362
493
  setImmediate(async () => {
363
494
  try {
@@ -1,3 +1,15 @@
1
+ /* VersaCompiler HMR shim [dev] */
2
+ if (typeof window !== 'undefined' && window.__versaHMR) {
3
+ (() => {
4
+ const _id = new URL(import.meta.url).pathname;
5
+ import.meta.hot = {
6
+ accept(cb) { window.__versaHMR.accept(_id, typeof cb === 'function' ? cb : () => {}); },
7
+ invalidate() { window.__versaHMR._invalidate?.(_id); },
8
+ dispose(cb) { window.__versaHMR._onDispose?.(_id, cb); },
9
+ get data() { return window.__versaHMR._getHotData?.(_id) ?? {}; },
10
+ };
11
+ })();
12
+ }
1
13
  import * as process from 'node:process';
2
14
  // Función para obtener ProgressManager (lazy import para evitar dependencias circulares)
3
15
  let getProgressManager = null;
@@ -1,3 +1,15 @@
1
+ /* VersaCompiler HMR shim [dev] */
2
+ if (typeof window !== 'undefined' && window.__versaHMR) {
3
+ (() => {
4
+ const _id = new URL(import.meta.url).pathname;
5
+ import.meta.hot = {
6
+ accept(cb) { window.__versaHMR.accept(_id, typeof cb === 'function' ? cb : () => {}); },
7
+ invalidate() { window.__versaHMR._invalidate?.(_id); },
8
+ dispose(cb) { window.__versaHMR._onDispose?.(_id, cb); },
9
+ get data() { return window.__versaHMR._getHotData?.(_id) ?? {}; },
10
+ };
11
+ })();
12
+ }
1
13
  import { normalize, relative, resolve } from 'node:path';
2
14
  import * as process from 'node:process';
3
15
  import { env } from 'node:process';
@@ -25,7 +37,7 @@ export function validatePath(pathStr) {
25
37
  return false;
26
38
  }
27
39
  // Rechazar rutas absolutas de Windows (válido en cualquier plataforma)
28
- if (/^[A-Za-z]:[\\\/]/.test(pathStr)) {
40
+ if (/^[A-Za-z]:[/\\]/.test(pathStr)) {
29
41
  logger.error(`Ruta absoluta de Windows no permitida: ${pathStr}`);
30
42
  return false;
31
43
  }
@@ -238,6 +250,80 @@ function safeJsonStringify(obj, fallback = 'false') {
238
250
  return fallback;
239
251
  }
240
252
  }
253
+ function normalizeAliasKey(key) {
254
+ if (key.includes('*'))
255
+ return key;
256
+ if (key.endsWith('/'))
257
+ return `${key}*`;
258
+ return `${key}/*`;
259
+ }
260
+ function normalizeAliasValue(value) {
261
+ if (value.includes('*'))
262
+ return value;
263
+ if (value.endsWith('/'))
264
+ return `${value}*`;
265
+ return `${value}/*`;
266
+ }
267
+ function normalizeAlias(alias) {
268
+ if (!alias)
269
+ return {};
270
+ const result = {};
271
+ if (Array.isArray(alias)) {
272
+ for (const entry of alias) {
273
+ if (!entry || typeof entry.find !== 'string')
274
+ continue;
275
+ if (typeof entry.replacement !== 'string')
276
+ continue;
277
+ const key = normalizeAliasKey(entry.find);
278
+ const value = normalizeAliasValue(entry.replacement);
279
+ result[key] = [value];
280
+ }
281
+ return result;
282
+ }
283
+ for (const [key, value] of Object.entries(alias)) {
284
+ const normalizedKey = normalizeAliasKey(key);
285
+ if (Array.isArray(value)) {
286
+ result[normalizedKey] = value
287
+ .filter(v => typeof v === 'string')
288
+ .map(v => normalizeAliasValue(v));
289
+ }
290
+ else if (typeof value === 'string') {
291
+ result[normalizedKey] = [normalizeAliasValue(value)];
292
+ }
293
+ }
294
+ return result;
295
+ }
296
+ function normalizeConfig(input) {
297
+ if (input?.compilerOptions) {
298
+ return input;
299
+ }
300
+ const viteConfig = input;
301
+ const aliases = normalizeAlias(viteConfig?.resolve?.alias);
302
+ return {
303
+ tsconfig: viteConfig?.tsconfig,
304
+ compilerOptions: {
305
+ sourceRoot: viteConfig?.root || './src',
306
+ outDir: viteConfig?.build?.outDir || './dist',
307
+ pathsAlias: aliases,
308
+ },
309
+ plugins: viteConfig?.plugins,
310
+ proxyConfig: {
311
+ proxyUrl: viteConfig?.server?.proxyUrl || '',
312
+ assetsOmit: viteConfig?.server?.assetsOmit ?? false,
313
+ },
314
+ aditionalWatch: viteConfig?.watch?.additional ||
315
+ viteConfig?.server?.watch?.additional ||
316
+ [],
317
+ tailwindConfig: viteConfig?.tailwindConfig,
318
+ linter: viteConfig?.linter,
319
+ bundlers: viteConfig?.build?.bundlers,
320
+ typeCheckOptions: viteConfig?.typeCheckOptions,
321
+ };
322
+ }
323
+ let loadedConfig = null;
324
+ export function getLoadedConfig() {
325
+ return loadedConfig;
326
+ }
241
327
  /**
242
328
  * Wrapper para el import dinámico que permite mejor testing
243
329
  */
@@ -272,7 +358,8 @@ export async function readConfig() {
272
358
  if (!data) {
273
359
  throw new Error('No se pudo leer el archivo de configuración.');
274
360
  }
275
- const tsConfig = data.default || data;
361
+ const rawConfig = data.default || data;
362
+ const tsConfig = normalizeConfig(rawConfig);
276
363
  // Validar tamaño de configuración
277
364
  if (!validateConfigSize(tsConfig)) {
278
365
  throw new Error('Configuración demasiado grande o contiene referencias circulares.');
@@ -323,6 +410,7 @@ export async function readConfig() {
323
410
  if (!tsConfig.compilerOptions.sourceRoot) {
324
411
  env.tsConfig = safeJsonStringify(tsConfig, '{}');
325
412
  }
413
+ loadedConfig = tsConfig;
326
414
  logger.info('✅ Configuration loaded and validated successfully');
327
415
  return true;
328
416
  }
@@ -347,58 +435,57 @@ export async function initConfig() {
347
435
  logger.warn(`🚩 El archivo de configuración '${env.PATH_CONFIG_FILE}' ya existe.`);
348
436
  return true;
349
437
  }
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
- };
438
+ const configContent = `import { defineConfig } from 'versacompiler/config';
439
+
440
+ export default defineConfig({
441
+ root: './src',
442
+ build: {
443
+ outDir: './dist',
444
+ bundlers: [
445
+ {
446
+ name: 'appLoader',
447
+ fileInput: './public/module/appLoader.js',
448
+ fileOutput: './public/module/appLoader.prod.js',
449
+ },
450
+ ],
451
+ },
452
+ resolve: {
453
+ alias: {
454
+ '/dist/public': 'src',
455
+ '/dist/public': 'public',
456
+ },
457
+ },
458
+ server: {
459
+ proxyUrl: '',
460
+ assetsOmit: true,
461
+ watch: {
462
+ additional: ['./app/templates/**/*.twig', './app/templates/**/*.html'],
463
+ },
464
+ },
465
+ tsconfig: './tsconfig.json',
466
+ tailwindConfig: {
467
+ bin: './node_modules/.bin/tailwindcss',
468
+ input: './src/css/input.css',
469
+ output: './public/css/output.css',
470
+ },
471
+ linter: [
472
+ {
473
+ name: 'eslint',
474
+ bin: './node_modules/.bin/eslint',
475
+ configFile: './.eslintrc.json',
476
+ fix: false,
477
+ paths: ['src/'],
478
+ },
479
+ {
480
+ name: 'oxlint',
481
+ bin: './node_modules/.bin/oxlint',
482
+ configFile: './.oxlintrc.json',
483
+ fix: false,
484
+ paths: ['src/'],
485
+ },
486
+ ],
487
+ plugins: [],
488
+ });
402
489
  `;
403
490
  fs.writeFileSync(configPath, configContent, 'utf8');
404
491
  logger.info(`🚩 Archivo de configuración '${env.PATH_CONFIG_FILE}' creado correctamente.`);
@@ -0,0 +1,14 @@
1
+ /* VersaCompiler HMR shim [dev] */
2
+ if (typeof window !== 'undefined' && window.__versaHMR) {
3
+ (() => {
4
+ const _id = new URL(import.meta.url).pathname;
5
+ import.meta.hot = {
6
+ accept(cb) { window.__versaHMR.accept(_id, typeof cb === 'function' ? cb : () => {}); },
7
+ invalidate() { window.__versaHMR._invalidate?.(_id); },
8
+ dispose(cb) { window.__versaHMR._onDispose?.(_id, cb); },
9
+ get data() { return window.__versaHMR._getHotData?.(_id) ?? {}; },
10
+ };
11
+ })();
12
+ }
13
+ export const defineConfig = (config) => config;
14
+ //# sourceMappingURL=versacompile.config.types.js.map
@@ -1,3 +1,15 @@
1
+ /* VersaCompiler HMR shim [dev] */
2
+ if (typeof window !== 'undefined' && window.__versaHMR) {
3
+ (() => {
4
+ const _id = new URL(import.meta.url).pathname;
5
+ import.meta.hot = {
6
+ accept(cb) { window.__versaHMR.accept(_id, typeof cb === 'function' ? cb : () => {}); },
7
+ invalidate() { window.__versaHMR._invalidate?.(_id); },
8
+ dispose(cb) { window.__versaHMR._onDispose?.(_id, cb); },
9
+ get data() { return window.__versaHMR._getHotData?.(_id) ?? {}; },
10
+ };
11
+ })();
12
+ }
1
13
  /**
2
14
  * Lista centralizada de módulos excluidos de la resolución automática
3
15
  * Estos módulos se mantienen con su importación original sin transformar
@@ -1,9 +1,21 @@
1
- // Opción con librería '/node_modules/resolve/index.js' (npm install resolve)
1
+ /* VersaCompiler HMR shim [dev] */
2
+ if (typeof window !== 'undefined' && window.__versaHMR) {
3
+ (() => {
4
+ const _id = new URL(import.meta.url).pathname;
5
+ import.meta.hot = {
6
+ accept(cb) { window.__versaHMR.accept(_id, typeof cb === 'function' ? cb : () => {}); },
7
+ invalidate() { window.__versaHMR._invalidate?.(_id); },
8
+ dispose(cb) { window.__versaHMR._onDispose?.(_id, cb); },
9
+ get data() { return window.__versaHMR._getHotData?.(_id) ?? {}; },
10
+ };
11
+ })();
12
+ }
13
+ // Opción con librería 'resolve' (npm install resolve)
2
14
  import fs, { readFileSync } from 'node:fs';
3
15
  import { dirname, join, relative } from 'node:path';
4
16
  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';
17
+ // import pkg from 'enhanced-resolve';
18
+ // import resolve from 'resolve';
7
19
  import { logger } from '../servicios/logger.js';
8
20
  import { EXCLUDED_MODULES } from './excluded-modules.js';
9
21
  class PackageJsonCache {
@@ -72,7 +84,7 @@ const packageJsonCache = PackageJsonCache.getInstance();
72
84
  // return null;
73
85
  // }
74
86
  // }
75
- // Opción con '/node_modules/enhanced-resolve/lib/index.js' (webpack's resolver)
87
+ // Opción con 'enhanced-resolve' (webpack's resolver)
76
88
  // npm install enhanced-resolve
77
89
  // const { ResolverFactory } = pkg;
78
90
  // const resolver = ResolverFactory.createResolver({
@@ -350,26 +362,7 @@ function simpleESMResolver(moduleName) {
350
362
  entryPoint = dotExport;
351
363
  }
352
364
  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
- }
365
+ entryPoint = resolveExportValue(dotExport);
373
366
  }
374
367
  }
375
368
  }
@@ -489,6 +482,64 @@ function getNodeModulesRelativePath(fullPath, _fromFile) {
489
482
  }
490
483
  return rel;
491
484
  }
485
+ /**
486
+ * Parses a module specifier into packageName and subPath.
487
+ * Handles scoped packages correctly.
488
+ * Examples:
489
+ * '@vueuse/core' → { packageName: '@vueuse/core', subPath: '' }
490
+ * '@vueuse/core/sub' → { packageName: '@vueuse/core', subPath: 'sub' }
491
+ * 'vue/dist/x' → { packageName: 'vue', subPath: 'dist/x' }
492
+ * 'vue' → { packageName: 'vue', subPath: '' }
493
+ */
494
+ export function parseModuleSpecifier(moduleName) {
495
+ if (moduleName.startsWith('@')) {
496
+ const parts = moduleName.split('/');
497
+ if (parts.length < 2 || !parts[1]) {
498
+ return { packageName: moduleName, subPath: '' };
499
+ }
500
+ const packageName = `${parts[0]}/${parts[1]}`;
501
+ const subPath = parts.slice(2).join('/');
502
+ return { packageName, subPath };
503
+ }
504
+ else {
505
+ const slashIdx = moduleName.indexOf('/');
506
+ if (slashIdx === -1) {
507
+ return { packageName: moduleName, subPath: '' };
508
+ }
509
+ return {
510
+ packageName: moduleName.slice(0, slashIdx),
511
+ subPath: moduleName.slice(slashIdx + 1),
512
+ };
513
+ }
514
+ }
515
+ /**
516
+ * Recursively resolves a conditional export value.
517
+ * Priority: import > browser > module > default
518
+ * Handles nested objects up to a depth limit.
519
+ */
520
+ export function resolveExportValue(exportVal, depth = 0) {
521
+ if (depth > 5)
522
+ return null;
523
+ if (typeof exportVal === 'string')
524
+ return exportVal;
525
+ if (typeof exportVal === 'object' && exportVal !== null) {
526
+ const obj = exportVal;
527
+ for (const key of [
528
+ 'import',
529
+ 'browser',
530
+ 'module',
531
+ 'default',
532
+ 'require',
533
+ ]) {
534
+ if (obj[key] !== undefined) {
535
+ const resolved = resolveExportValue(obj[key], depth + 1);
536
+ if (resolved)
537
+ return resolved;
538
+ }
539
+ }
540
+ }
541
+ return null;
542
+ }
492
543
  export function getModulePath(moduleName, fromFile) {
493
544
  // Verificar si el módulo está en la lista de excluidos
494
545
  if (EXCLUDED_MODULES.has(moduleName)) {
@@ -501,14 +552,15 @@ export function getModuleSubPath(moduleName, fromFile) {
501
552
  // Verificar si el módulo está en la lista de excluidos
502
553
  if (EXCLUDED_MODULES.has(moduleName)) {
503
554
  return null; // Retornar null para mantener la importación original
504
- } // Si contiene '/', es un subpath
555
+ }
556
+ // Usar parseModuleSpecifier para manejar correctamente paquetes con scope
557
+ const { packageName, subPath } = parseModuleSpecifier(moduleName);
558
+ // Si no hay subPath, delegar al resolver normal
559
+ if (!subPath) {
560
+ return getModulePath(moduleName, fromFile);
561
+ }
562
+ // Es un subpath: resolver con el packageName correcto
505
563
  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
564
  try {
513
565
  const nodeModulesPath = join(cwd(), 'node_modules', packageName);
514
566
  const packagePath = join(nodeModulesPath, 'package.json');
@@ -525,15 +577,9 @@ export function getModuleSubPath(moduleName, fromFile) {
525
577
  const exportKey = `./${subPath}`;
526
578
  const exportPath = packageJson.exports[exportKey];
527
579
  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
- }
580
+ const resolved = resolveExportValue(exportPath);
581
+ if (resolved) {
582
+ return getNodeModulesRelativePath(join(moduleDir, resolved), fromFile);
537
583
  }
538
584
  }
539
585
  }
@@ -1,3 +1,15 @@
1
+ /* VersaCompiler HMR shim [dev] */
2
+ if (typeof window !== 'undefined' && window.__versaHMR) {
3
+ (() => {
4
+ const _id = new URL(import.meta.url).pathname;
5
+ import.meta.hot = {
6
+ accept(cb) { window.__versaHMR.accept(_id, typeof cb === 'function' ? cb : () => {}); },
7
+ invalidate() { window.__versaHMR._invalidate?.(_id); },
8
+ dispose(cb) { window.__versaHMR._onDispose?.(_id, cb); },
9
+ get data() { return window.__versaHMR._getHotData?.(_id) ?? {}; },
10
+ };
11
+ })();
12
+ }
1
13
  import process, { stdin as input, stdout as output } from 'node:process';
2
14
  import * as readline from 'node:readline/promises';
3
15
  import { logger } from '../servicios/logger.js';