vatts 2.0.2 → 2.1.0-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.
Files changed (40) hide show
  1. package/LICENSE +12 -12
  2. package/README.md +63 -63
  3. package/dist/api/console.d.ts +28 -5
  4. package/dist/api/console.js +305 -137
  5. package/dist/api/native-server.js +25 -4
  6. package/dist/bin/vatts.js +192 -4
  7. package/dist/builder.js +70 -69
  8. package/dist/core-go/core-linux-arm64.node +0 -0
  9. package/dist/core-go/core-linux-x64.node +0 -0
  10. package/dist/core-go/core-win-x64.node +0 -0
  11. package/dist/global/global.d.ts +176 -176
  12. package/dist/helpers.d.ts +2 -2
  13. package/dist/helpers.js +20 -41
  14. package/dist/hotReload.js +205 -205
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.js +104 -19
  17. package/dist/loaders.js +15 -15
  18. package/dist/react/BuildingPage.js +202 -202
  19. package/dist/react/DefaultNotFound.js +16 -16
  20. package/dist/react/DevIndicator.js +101 -101
  21. package/dist/react/entry.client.js +7 -8
  22. package/dist/react/image/Image.js +1 -1
  23. package/dist/react/renderer-react.js +270 -33
  24. package/dist/react/server-error.d.ts +8 -0
  25. package/dist/react/server-error.js +346 -0
  26. package/dist/router.js +1 -65
  27. package/dist/types.d.ts +1 -5
  28. package/dist/utils/utils.d.ts +1 -0
  29. package/dist/utils/utils.js +68 -0
  30. package/dist/vue/App.vue +191 -191
  31. package/dist/vue/BuildingPage.vue +280 -280
  32. package/dist/vue/DefaultNotFound.vue +328 -328
  33. package/dist/vue/DevIndicator.vue +225 -225
  34. package/dist/vue/ErrorModal.vue +316 -316
  35. package/dist/vue/Link.vue +38 -38
  36. package/dist/vue/entry.client.js +7 -7
  37. package/dist/vue/image/Image.vue +106 -106
  38. package/dist/vue/renderer.vue.js +266 -46
  39. package/dist/vue/server-error.vue +351 -0
  40. package/package.json +1 -1
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/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { BackendHandler, BackendRouteConfig, VattsOptions, RequestHandler } from './types';
2
2
  import { VattsRequest, VattsResponse } from './api/http';
3
+ export declare function printTotalStats(outDir: string, https: boolean): Promise<void>;
3
4
  export { VattsRequest, VattsResponse };
4
5
  export type { BackendRouteConfig, BackendHandler };
5
6
  export { ExpressAdapter } from './adapters/express';
@@ -10,7 +11,7 @@ export { app } from './helpers';
10
11
  export type { WebSocketContext, WebSocketHandler } from './types';
11
12
  export type { VattsConfig, VattsConfigFunction } from './types';
12
13
  export default function vatts(options: VattsOptions): {
13
- prepare: () => Promise<void>;
14
+ prepare: (skipBuild?: boolean) => Promise<void>;
14
15
  executeInstrumentation: () => void;
15
16
  getRequestHandler: () => RequestHandler;
16
17
  setupWebSocket: (server: any) => void;
package/dist/index.js CHANGED
@@ -53,9 +53,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
53
53
  };
54
54
  Object.defineProperty(exports, "__esModule", { value: true });
55
55
  exports.app = exports.FrameworkAdapterFactory = exports.FastifyAdapter = exports.ExpressAdapter = exports.VattsResponse = exports.VattsRequest = void 0;
56
+ exports.printTotalStats = printTotalStats;
56
57
  exports.default = vatts;
57
58
  const path_1 = __importDefault(require("path"));
58
59
  const fs_1 = __importDefault(require("fs"));
60
+ const promises_1 = __importDefault(require("fs/promises"));
59
61
  const crypto_1 = __importDefault(require("crypto")); // Adicionado para gerar hash do cache
60
62
  const express_1 = require("./adapters/express");
61
63
  const builder_1 = require("./builder");
@@ -225,6 +227,77 @@ async function handleImageOptimization(req, res, projectDir) {
225
227
  res.status(500).text('Image optimization failed');
226
228
  }
227
229
  }
230
+ const node_zlib_1 = __importDefault(require("node:zlib")); // Certifique-se de ter este import no topo
231
+ async function printTotalStats(outDir, https) {
232
+ let totalOrig = 0;
233
+ let totalGzip = 0;
234
+ let totalBr = 0;
235
+ // REMOVIDO: O loop que lia 'entries' para somar originais foi apagado.
236
+ // Walk recursivo no outDir pra somar .gz e .br e calcular o original reverso
237
+ async function walk(dir) {
238
+ let dirents;
239
+ try {
240
+ dirents = await promises_1.default.readdir(dir, { withFileTypes: true });
241
+ }
242
+ catch {
243
+ return;
244
+ }
245
+ for (const d of dirents) {
246
+ const full = path_1.default.join(dir, d.name);
247
+ if (d.isDirectory()) {
248
+ await walk(full);
249
+ continue;
250
+ }
251
+ if (!d.isFile())
252
+ continue;
253
+ let st;
254
+ try {
255
+ st = await promises_1.default.stat(full);
256
+ }
257
+ catch {
258
+ continue;
259
+ }
260
+ const name = d.name;
261
+ const isGz = name.endsWith(".gz");
262
+ const isBr = name.endsWith(".br");
263
+ if (isGz)
264
+ totalGzip += st.size;
265
+ else if (isBr)
266
+ totalBr += st.size;
267
+ // Lógica nova: Se o arquivo for do tipo que estamos priorizando (https = br, !https = gz),
268
+ // descomprimimos para pegar o tamanho original.
269
+ if ((https && isBr) || (!https && isGz)) {
270
+ try {
271
+ const content = await promises_1.default.readFile(full);
272
+ if (isBr) {
273
+ // Descomprime Brotli síncrono para pegar tamanho
274
+ totalOrig += node_zlib_1.default.brotliDecompressSync(content).length;
275
+ }
276
+ else {
277
+ // Descomprime Gzip síncrono para pegar tamanho
278
+ totalOrig += node_zlib_1.default.gunzipSync(content).length;
279
+ }
280
+ }
281
+ catch (e) {
282
+ // Ignora erro de descompressão se houver
283
+ }
284
+ }
285
+ }
286
+ }
287
+ await walk(outDir);
288
+ // O totalOrig agora é calculado baseado na descompressão, se for 0 não tem o que mostrar
289
+ if (totalOrig <= 0)
290
+ return;
291
+ const finalSize = https ? totalBr : totalGzip;
292
+ if (finalSize <= 0)
293
+ return;
294
+ const diff = totalOrig - finalSize;
295
+ const pct = (diff / totalOrig) * 100;
296
+ const original = ` Original : ${console_1.Colors.Bright}${console_1.Colors.FgGreen}${(totalOrig / 1024).toFixed(2)} KB`;
297
+ const finalStr = ` Final : ${console_1.Colors.Bright}${console_1.Colors.FgGreen}${(finalSize / 1024).toFixed(2)} KB`;
298
+ const saved = ` Saved : ${console_1.Colors.Bright}${console_1.Colors.FgGreen}${(diff / 1024).toFixed(2)} KB ${console_1.Colors.Reset}${console_1.Colors.FgGray}(${pct.toFixed(2)}%)${console_1.Colors.Reset} \n`;
299
+ console_1.default.logCustomLevel("", false, undefined, "", `${console_1.Colors.FgBlue}${console_1.Colors.Bright}Optimization summary:${console_1.Colors.Reset}`, original, finalStr, saved);
300
+ }
228
301
  // Exporta os adapters para uso manual se necessário
229
302
  var express_2 = require("./adapters/express");
230
303
  Object.defineProperty(exports, "ExpressAdapter", { enumerable: true, get: function () { return express_2.ExpressAdapter; } });
@@ -275,7 +348,6 @@ function vatts(options) {
275
348
  notFound: null,
276
349
  framework: framework,
277
350
  projectDir: dir,
278
- pathRouter: helpers_1.config?.pathRouter
279
351
  };
280
352
  const updateBuilderOptions = () => {
281
353
  const newFrontendRoutes = (0, router_1.loadRoutes)(userWebRoutesDir);
@@ -294,7 +366,7 @@ function vatts(options) {
294
366
  vattsBuilderOptions.notFound = newNotFound;
295
367
  };
296
368
  return {
297
- prepare: async () => {
369
+ prepare: async (skipBuild = false) => {
298
370
  const isProduction = !dev;
299
371
  if (!isProduction) {
300
372
  hotReloadManager = new hotReload_1.HotReloadManager(dir);
@@ -329,32 +401,45 @@ function vatts(options) {
329
401
  clearInterval(spinner1);
330
402
  timee.end(`Routes and components loaded in ${Date.now() - now}ms`);
331
403
  if (isProduction) {
332
- const time = console_1.default.dynamicLine(`Starting client build`);
404
+ // Pula o build se skipBuild for true (usado quando já existe build)
405
+ if (!skipBuild) {
406
+ const time = console_1.default.dynamicLine(`Starting client build`);
407
+ const spinnerFrames = ['|', '/', '-', '\\'];
408
+ let frameIndex = 0;
409
+ const spinner = setInterval(() => {
410
+ time.update(` ${console_1.Colors.FgGreen}${spinnerFrames[frameIndex]}${console_1.Colors.Reset} Building...`);
411
+ frameIndex = (frameIndex + 1) % spinnerFrames.length;
412
+ }, 100);
413
+ const now = Date.now();
414
+ // Passa o objeto de opções ao invés do caminho do arquivo
415
+ await (0, builder_1.buildWithChunks)(vattsBuilderOptions, outDir, isProduction);
416
+ const elapsed = Date.now() - now;
417
+ clearInterval(spinner);
418
+ time.update("");
419
+ await printTotalStats(outDir, helpers_1.config?.ssl !== undefined && helpers_1.config.ssl !== null);
420
+ time.end(`Client build completed in ${elapsed}ms`);
421
+ if (hotReloadManager) {
422
+ hotReloadManager.onBuildComplete(true);
423
+ }
424
+ }
425
+ }
426
+ else {
427
+ const time = console_1.default.dynamicLine(` ${console_1.Colors.BgYellow} watcher ${console_1.Colors.Reset} Building initial assets`);
333
428
  const spinnerFrames = ['|', '/', '-', '\\'];
334
429
  let frameIndex = 0;
335
430
  const spinner = setInterval(() => {
336
- time.update(` ${console_1.Colors.FgGreen}${spinnerFrames[frameIndex]}${console_1.Colors.Reset} Building...`);
431
+ time.update(` ${console_1.Colors.FgYellow}${spinnerFrames[frameIndex]}${console_1.Colors.Reset} Building initial assets...`);
337
432
  frameIndex = (frameIndex + 1) % spinnerFrames.length;
338
433
  }, 100);
339
- const now = Date.now();
340
- // Passa o objeto de opções ao invés do caminho do arquivo
341
- await (0, builder_1.buildWithChunks)(vattsBuilderOptions, outDir, isProduction);
342
- const elapsed = Date.now() - now;
343
- clearInterval(spinner);
344
- time.update("");
345
- time.end(`Client build completed in ${elapsed}ms`);
346
- if (hotReloadManager) {
347
- hotReloadManager.onBuildComplete(true);
348
- }
349
- }
350
- else {
351
- const time = console_1.default.dynamicLine(` ${console_1.Colors.BgYellow} watcher ${console_1.Colors.Reset} Starting client watch`);
434
+ const buildStart = Date.now();
352
435
  // @ts-ignore
353
436
  // Passa o objeto de opções ao invés do caminho do arquivo
354
- (0, builder_1.watchWithChunks)(vattsBuilderOptions, outDir, hotReloadManager).catch(err => {
437
+ // Aguarda o primeiro build completar para evitar lentidão na primeira requisição
438
+ await (0, builder_1.watchWithChunks)(vattsBuilderOptions, outDir, hotReloadManager).catch(err => {
355
439
  console_1.default.error(`Error starting watch`, err);
356
440
  });
357
- time.end(`Client Watch started`);
441
+ clearInterval(spinner);
442
+ time.end(`Initial build complete in ${Date.now() - buildStart}ms - watching for changes...`);
358
443
  }
359
444
  },
360
445
  executeInstrumentation: () => {
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, {