vatts 2.0.2 → 2.0.3-canary.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.
package/dist/hotReload.js CHANGED
@@ -412,211 +412,211 @@ class HotReloadManager {
412
412
  // ... (rest of methods like getClientScript, clearBackendCache keep unchanged or minimally adjusted)
413
413
  // Script do cliente otimizado com reconnection backoff
414
414
  getClientScript() {
415
- return `
416
- <script>
417
- (function() {
418
- if (typeof window !== 'undefined') {
419
- let ws;
420
- let reconnectAttempts = 0;
421
- let maxReconnectInterval = 30000;
422
- let reconnectInterval = 1000;
423
- let reconnectTimer;
424
- let isConnected = false;
425
- let lastBuildError = null;
426
-
427
- function emitBuildError(error) {
428
- try {
429
- lastBuildError = error;
430
- // Não persistimos erro no client. Ele sempre vem do servidor via WebSocket.
431
- window.dispatchEvent(new CustomEvent('vatts:build-error', { detail: error }));
432
- } catch {}
433
- }
434
-
435
- function clearBuildError() {
436
- try {
437
- lastBuildError = null;
438
- window.dispatchEvent(new CustomEvent('vatts:build-ok', { detail: { ts: Date.now() } }));
439
- } catch {}
440
- }
441
-
442
- // Não faz bootstrap de erro salvo (sem sessionStorage/localStorage).
443
-
444
- function notifyHotReloadState(state, payload) {
445
- try {
446
- window.__VATTS_HOT_RELOAD__ = { state: state, payload: payload || null, ts: Date.now() };
447
- const ev = new CustomEvent('vatts:hotreload', { detail: window.__VATTS_HOT_RELOAD__ });
448
- window.dispatchEvent(ev);
449
- } catch {}
450
- }
451
-
452
- function requestBuildStatus(reason) {
453
- try {
454
- if (!ws || ws.readyState !== WebSocket.OPEN) return;
455
- ws.send(JSON.stringify({ type: 'status-request', reason: reason || 'unknown', ts: Date.now() }));
456
- } catch {}
457
- }
458
-
459
- async function waitForMainToBeUpdated(timeoutMs, pollMs) {
460
- try {
461
- const script = document.querySelector('script[src*="main.js"]');
462
- if (!script || !script.src) return true;
463
- const mainUrl = script.src.split('?')[0];
464
- const start = Date.now();
465
- let baseline = '';
466
- try {
467
- const r0 = await fetch(mainUrl + '?t=' + Date.now(), { cache: 'no-store' });
468
- baseline = await r0.text();
469
- } catch { return true; }
470
-
471
- while (Date.now() - start < timeoutMs) {
472
- await new Promise(r => setTimeout(r, pollMs));
473
- try {
474
- const r = await fetch(mainUrl + '?t=' + Date.now(), { cache: 'no-store' });
475
- const txt = await r.text();
476
- if (txt && txt !== baseline) return true;
477
- } catch {}
478
- }
479
- return false;
480
- } catch { return true; }
481
- }
482
-
483
- function connect() {
484
- const url = window.location;
485
- const protocol = url.protocol === "https:" ? "wss:" : "ws:";
486
- const wsUrl = protocol + '//' + url.host + '/hweb-hotreload/';
487
- if (ws && (ws.readyState === WebSocket.CONNECTING || ws.readyState === WebSocket.OPEN)) return;
488
-
489
- try {
490
- ws = new WebSocket(wsUrl);
491
- ws.onopen = function() {
492
- console.log('\u001b[32m[vatts]\u001b[0m Hot-reload connected');
493
- isConnected = true;
494
- reconnectAttempts = 0;
495
- clearTimeout(reconnectTimer);
496
- // Ao conectar, sempre pede o status atual ao servidor.
497
- requestBuildStatus('ws-open');
498
- };
499
-
500
- ws.onmessage = function(event) {
501
- try {
502
- const message = JSON.parse(event.data);
503
- switch(message.type) {
504
- case 'frontend-reload':
505
- handleFrontendReload(message.data);
506
- break;
507
- case 'backend-api-reload':
508
- console.log('[vatts] Backend changed, reloading page...');
509
- window.location.reload();
510
- break;
511
- case 'server-restart':
512
- console.log('[vatts] Server restarting...');
513
- break;
514
- case 'server-ready':
515
- setTimeout(() => window.location.reload(), 500);
516
- break;
517
- case 'build-error':
518
- console.log('Erro detectado')
519
- notifyHotReloadState('build-error', message.data);
520
- emitBuildError(message.data);
521
- break;
522
- case 'build-complete':
523
- notifyHotReloadState('idle', { build: 'ok' });
524
- clearBuildError();
525
- console.log('[vatts] Build complete');
526
- break;
527
- }
528
- } catch (e) {
529
- console.error('[vatts] Error processing msg:', e);
530
- }
531
- };
532
-
533
- async function handleFrontendReload(data) {
534
- notifyHotReloadState('reloading', { type: 'frontend', data: data || null });
535
- if (!data || !data.file) {
536
- window.dispatchEvent(new CustomEvent('hmr:component-update', { detail: { file: null, timestamp: Date.now() } }));
537
- return;
538
- }
539
- const file = (data.file || '').toLowerCase();
540
- console.log('[vatts] Frontend changed:', data.file);
541
- const needsFullReload = file.includes('layout.tsx') || file.includes('not-found.tsx') || file.endsWith('.css');
542
- if (needsFullReload) {
543
- notifyHotReloadState('full-reload', { reason: 'layout/css', file: data.file });
544
- window.location.reload();
545
- return;
546
- }
547
-
548
- // Recarrega main.js (o bundle do app) com cache-busting.
549
- // Em DEV, React/ReactDOM agora são externos, então isso não duplica hooks.
550
- await waitForMainToBeUpdated(5000, 120);
551
-
552
- const script = document.querySelector('script[src*="main.js"]');
553
- if (script) {
554
- const mainUrl = (script.getAttribute('src') || script.src || '');
555
- const base = String(mainUrl).split('?')[0];
556
-
557
- // Remove scripts antigos de main.js para evitar múltiplas execuções do bundle
558
- try {
559
- document.querySelectorAll('script[src*="main.js"]').forEach(s => {
560
- if (s && s.parentNode) s.parentNode.removeChild(s);
561
- });
562
- } catch {}
563
-
564
- const newScript = document.createElement('script');
565
- newScript.type = 'module';
566
- newScript.src = base + '?t=' + Date.now();
567
-
568
- newScript.onload = () => {
569
- // depois de carregar o novo bundle, pede render/update
570
- const ts = Date.now();
571
- window.dispatchEvent(new CustomEvent('hmr:component-update', { detail: { file: data.file, timestamp: ts } }));
572
- setTimeout(() => requestBuildStatus('after-frontend-reload'), 50);
573
- };
574
-
575
- newScript.onerror = () => {
576
- window.location.reload();
577
- };
578
-
579
- document.head.appendChild(newScript);
580
- } else {
581
- // fallback
582
- const ts = Date.now();
583
- window.dispatchEvent(new CustomEvent('hmr:component-update', { detail: { file: data.file, timestamp: ts } }));
584
- setTimeout(() => requestBuildStatus('after-frontend-reload'), 50);
585
- }
586
-
587
- setTimeout(() => {
588
- if (window.__HMR_SUCCESS__) {
589
- notifyHotReloadState('idle', { success: true, file: data.file });
590
- requestBuildStatus('after-hmr-success');
591
- return;
592
- }
593
-
594
- window.location.reload();
595
- }, 1600);
596
- }
597
-
598
- ws.onclose = function(event) {
599
- isConnected = false;
600
- if (event.code === 1000) return;
601
- scheduleReconnect();
602
- };
603
- ws.onerror = function() {};
604
- } catch (error) { scheduleReconnect(); }
605
- }
606
-
607
- function scheduleReconnect() {
608
- if (reconnectTimer) clearTimeout(reconnectTimer);
609
- reconnectAttempts++;
610
- const baseInterval = Math.min(reconnectInterval * Math.pow(1.5, reconnectAttempts - 1), maxReconnectInterval);
611
- reconnectTimer = setTimeout(() => { if (!isConnected) connect(); }, baseInterval + Math.random() * 1000);
612
- }
613
-
614
- window.addEventListener('beforeunload', function() { if (ws && ws.readyState === WebSocket.OPEN) ws.close(1000, 'Page unloading'); });
615
- document.addEventListener('visibilitychange', function() { if (!document.hidden && !isConnected) { reconnectAttempts = 0; connect(); } });
616
- connect();
617
- }
618
- })();
619
- </script>
415
+ return `
416
+ <script>
417
+ (function() {
418
+ if (typeof window !== 'undefined') {
419
+ let ws;
420
+ let reconnectAttempts = 0;
421
+ let maxReconnectInterval = 30000;
422
+ let reconnectInterval = 1000;
423
+ let reconnectTimer;
424
+ let isConnected = false;
425
+ let lastBuildError = null;
426
+
427
+ function emitBuildError(error) {
428
+ try {
429
+ lastBuildError = error;
430
+ // Não persistimos erro no client. Ele sempre vem do servidor via WebSocket.
431
+ window.dispatchEvent(new CustomEvent('vatts:build-error', { detail: error }));
432
+ } catch {}
433
+ }
434
+
435
+ function clearBuildError() {
436
+ try {
437
+ lastBuildError = null;
438
+ window.dispatchEvent(new CustomEvent('vatts:build-ok', { detail: { ts: Date.now() } }));
439
+ } catch {}
440
+ }
441
+
442
+ // Não faz bootstrap de erro salvo (sem sessionStorage/localStorage).
443
+
444
+ function notifyHotReloadState(state, payload) {
445
+ try {
446
+ window.__VATTS_HOT_RELOAD__ = { state: state, payload: payload || null, ts: Date.now() };
447
+ const ev = new CustomEvent('vatts:hotreload', { detail: window.__VATTS_HOT_RELOAD__ });
448
+ window.dispatchEvent(ev);
449
+ } catch {}
450
+ }
451
+
452
+ function requestBuildStatus(reason) {
453
+ try {
454
+ if (!ws || ws.readyState !== WebSocket.OPEN) return;
455
+ ws.send(JSON.stringify({ type: 'status-request', reason: reason || 'unknown', ts: Date.now() }));
456
+ } catch {}
457
+ }
458
+
459
+ async function waitForMainToBeUpdated(timeoutMs, pollMs) {
460
+ try {
461
+ const script = document.querySelector('script[src*="main.js"]');
462
+ if (!script || !script.src) return true;
463
+ const mainUrl = script.src.split('?')[0];
464
+ const start = Date.now();
465
+ let baseline = '';
466
+ try {
467
+ const r0 = await fetch(mainUrl + '?t=' + Date.now(), { cache: 'no-store' });
468
+ baseline = await r0.text();
469
+ } catch { return true; }
470
+
471
+ while (Date.now() - start < timeoutMs) {
472
+ await new Promise(r => setTimeout(r, pollMs));
473
+ try {
474
+ const r = await fetch(mainUrl + '?t=' + Date.now(), { cache: 'no-store' });
475
+ const txt = await r.text();
476
+ if (txt && txt !== baseline) return true;
477
+ } catch {}
478
+ }
479
+ return false;
480
+ } catch { return true; }
481
+ }
482
+
483
+ function connect() {
484
+ const url = window.location;
485
+ const protocol = url.protocol === "https:" ? "wss:" : "ws:";
486
+ const wsUrl = protocol + '//' + url.host + '/hweb-hotreload/';
487
+ if (ws && (ws.readyState === WebSocket.CONNECTING || ws.readyState === WebSocket.OPEN)) return;
488
+
489
+ try {
490
+ ws = new WebSocket(wsUrl);
491
+ ws.onopen = function() {
492
+ console.log('\u001b[32m[vatts]\u001b[0m Hot-reload connected');
493
+ isConnected = true;
494
+ reconnectAttempts = 0;
495
+ clearTimeout(reconnectTimer);
496
+ // Ao conectar, sempre pede o status atual ao servidor.
497
+ requestBuildStatus('ws-open');
498
+ };
499
+
500
+ ws.onmessage = function(event) {
501
+ try {
502
+ const message = JSON.parse(event.data);
503
+ switch(message.type) {
504
+ case 'frontend-reload':
505
+ handleFrontendReload(message.data);
506
+ break;
507
+ case 'backend-api-reload':
508
+ console.log('[vatts] Backend changed, reloading page...');
509
+ window.location.reload();
510
+ break;
511
+ case 'server-restart':
512
+ console.log('[vatts] Server restarting...');
513
+ break;
514
+ case 'server-ready':
515
+ setTimeout(() => window.location.reload(), 500);
516
+ break;
517
+ case 'build-error':
518
+ console.log('Erro detectado')
519
+ notifyHotReloadState('build-error', message.data);
520
+ emitBuildError(message.data);
521
+ break;
522
+ case 'build-complete':
523
+ notifyHotReloadState('idle', { build: 'ok' });
524
+ clearBuildError();
525
+ console.log('[vatts] Build complete');
526
+ break;
527
+ }
528
+ } catch (e) {
529
+ console.error('[vatts] Error processing msg:', e);
530
+ }
531
+ };
532
+
533
+ async function handleFrontendReload(data) {
534
+ notifyHotReloadState('reloading', { type: 'frontend', data: data || null });
535
+ if (!data || !data.file) {
536
+ window.dispatchEvent(new CustomEvent('hmr:component-update', { detail: { file: null, timestamp: Date.now() } }));
537
+ return;
538
+ }
539
+ const file = (data.file || '').toLowerCase();
540
+ console.log('[vatts] Frontend changed:', data.file);
541
+ const needsFullReload = file.includes('layout.tsx') || file.includes('not-found.tsx') || file.endsWith('.css');
542
+ if (needsFullReload) {
543
+ notifyHotReloadState('full-reload', { reason: 'layout/css', file: data.file });
544
+ window.location.reload();
545
+ return;
546
+ }
547
+
548
+ // Recarrega main.js (o bundle do app) com cache-busting.
549
+ // Em DEV, React/ReactDOM agora são externos, então isso não duplica hooks.
550
+ await waitForMainToBeUpdated(5000, 120);
551
+
552
+ const script = document.querySelector('script[src*="main.js"]');
553
+ if (script) {
554
+ const mainUrl = (script.getAttribute('src') || script.src || '');
555
+ const base = String(mainUrl).split('?')[0];
556
+
557
+ // Remove scripts antigos de main.js para evitar múltiplas execuções do bundle
558
+ try {
559
+ document.querySelectorAll('script[src*="main.js"]').forEach(s => {
560
+ if (s && s.parentNode) s.parentNode.removeChild(s);
561
+ });
562
+ } catch {}
563
+
564
+ const newScript = document.createElement('script');
565
+ newScript.type = 'module';
566
+ newScript.src = base + '?t=' + Date.now();
567
+
568
+ newScript.onload = () => {
569
+ // depois de carregar o novo bundle, pede render/update
570
+ const ts = Date.now();
571
+ window.dispatchEvent(new CustomEvent('hmr:component-update', { detail: { file: data.file, timestamp: ts } }));
572
+ setTimeout(() => requestBuildStatus('after-frontend-reload'), 50);
573
+ };
574
+
575
+ newScript.onerror = () => {
576
+ window.location.reload();
577
+ };
578
+
579
+ document.head.appendChild(newScript);
580
+ } else {
581
+ // fallback
582
+ const ts = Date.now();
583
+ window.dispatchEvent(new CustomEvent('hmr:component-update', { detail: { file: data.file, timestamp: ts } }));
584
+ setTimeout(() => requestBuildStatus('after-frontend-reload'), 50);
585
+ }
586
+
587
+ setTimeout(() => {
588
+ if (window.__HMR_SUCCESS__) {
589
+ notifyHotReloadState('idle', { success: true, file: data.file });
590
+ requestBuildStatus('after-hmr-success');
591
+ return;
592
+ }
593
+
594
+ window.location.reload();
595
+ }, 1600);
596
+ }
597
+
598
+ ws.onclose = function(event) {
599
+ isConnected = false;
600
+ if (event.code === 1000) return;
601
+ scheduleReconnect();
602
+ };
603
+ ws.onerror = function() {};
604
+ } catch (error) { scheduleReconnect(); }
605
+ }
606
+
607
+ function scheduleReconnect() {
608
+ if (reconnectTimer) clearTimeout(reconnectTimer);
609
+ reconnectAttempts++;
610
+ const baseInterval = Math.min(reconnectInterval * Math.pow(1.5, reconnectAttempts - 1), maxReconnectInterval);
611
+ reconnectTimer = setTimeout(() => { if (!isConnected) connect(); }, baseInterval + Math.random() * 1000);
612
+ }
613
+
614
+ window.addEventListener('beforeunload', function() { if (ws && ws.readyState === WebSocket.OPEN) ws.close(1000, 'Page unloading'); });
615
+ document.addEventListener('visibilitychange', function() { if (!document.hidden && !isConnected) { reconnectAttempts = 0; connect(); } });
616
+ connect();
617
+ }
618
+ })();
619
+ </script>
620
620
  `;
621
621
  }
622
622
  // Mantendo métodos de cache iguais
package/dist/loaders.js CHANGED
@@ -164,21 +164,21 @@ function registerLoaders(options = {}) {
164
164
  }
165
165
  }
166
166
  // 4. Montagem do Código Final (ESM Virtual)
167
- finalEsm = `
168
- ${scriptContent}
169
- ${templateContent}
170
-
171
- // Anexa a função de renderização SSR ao componente principal
172
- if (typeof _sfc_main !== 'undefined') {
173
- if (typeof ssrRender !== 'undefined') {
174
- _sfc_main.ssrRender = ssrRender;
175
- }
176
- if (typeof render !== 'undefined') {
177
- _sfc_main.render = render;
178
- }
179
- }
180
-
181
- export default _sfc_main;
167
+ finalEsm = `
168
+ ${scriptContent}
169
+ ${templateContent}
170
+
171
+ // Anexa a função de renderização SSR ao componente principal
172
+ if (typeof _sfc_main !== 'undefined') {
173
+ if (typeof ssrRender !== 'undefined') {
174
+ _sfc_main.ssrRender = ssrRender;
175
+ }
176
+ if (typeof render !== 'undefined') {
177
+ _sfc_main.render = render;
178
+ }
179
+ }
180
+
181
+ export default _sfc_main;
182
182
  `;
183
183
  // 5. Transformação final para CommonJS (Node.js) via Esbuild
184
184
  const result = esbuild.transformSync(finalEsm, {