vatts 1.0.1 → 1.0.2-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/builder.js CHANGED
@@ -303,10 +303,14 @@ const smartAssetPlugin = (isProduction) => {
303
303
  function createRollupConfig(entryPoint, outdir, isProduction) {
304
304
  return {
305
305
  input: entryPoint,
306
+ // Para evitar bare imports no browser (sem import map), em DEV também bundle React/ReactDOM.
307
+ // O HMR evita "Invalid hook call" removendo o script antigo e recarregando main.js.
306
308
  external: nodeBuiltIns,
307
309
  // Otimização: Em prod usa 'recommended' para limpar código morto
308
310
  treeshake: isProduction ? 'recommended' : false,
309
- cache: true,
311
+ // CORREÇÃO CRÍTICA: Desativa cache em desenvolvimento (watch mode)
312
+ // Isso previne que o Rollup emita "sucesso" baseado em um cache obsoleto quando o arquivo ainda tem erro.
313
+ cache: isProduction ? true : false,
310
314
  perf: false,
311
315
  plugins: [
312
316
  // CRÍTICO: 'replace' deve vir PRIMEIRO para injetar NODE_ENV antes que
@@ -417,7 +421,7 @@ async function build(entryPoint, outfile, isProduction = false) {
417
421
  const outputOptions = {
418
422
  file: outfile,
419
423
  format: 'iife',
420
- name: 'HwebApp',
424
+ name: 'Vattsjs',
421
425
  sourcemap: !isProduction,
422
426
  inlineDynamicImports: true
423
427
  };
@@ -434,21 +438,33 @@ async function build(entryPoint, outfile, isProduction = false) {
434
438
  * Helper para lidar com notificações do Watcher
435
439
  */
436
440
  function handleWatcherEvents(watcher, hotReloadManager, resolveFirstBuild) {
437
- let hasError = false;
441
+ // Controla o estado do build por "geração" para evitar END atrasado
442
+ // (ou múltiplos ciclos) emitirem sucesso após um erro.
443
+ let currentBuildId = 0;
444
+ let lastStartedBuildId = 0;
445
+ const erroredBuildIds = new Set();
446
+ // DEBUG: stack trace rate-limited
447
+ let lastTraceAt = 0;
438
448
  watcher.on('event', event => {
439
- if (event.code === 'START')
440
- hasError = false;
449
+ if (event.code === 'START') {
450
+ currentBuildId += 1;
451
+ lastStartedBuildId = currentBuildId;
452
+ }
441
453
  if (event.code === 'ERROR') {
442
- hasError = true;
454
+ // Marca erro para o build atualmente em andamento.
455
+ erroredBuildIds.add(currentBuildId);
443
456
  const errDetails = {
444
457
  message: event.error?.message || 'Unknown build error',
445
458
  name: event.error?.name,
446
459
  stack: event.error?.stack,
447
460
  id: event.error?.id,
448
- loc: event.error?.loc
461
+ loc: event.error?.loc,
462
+ buildId: currentBuildId
449
463
  };
450
- if (hotReloadManager)
464
+ // Notifica erro imediatamente
465
+ if (hotReloadManager) {
451
466
  hotReloadManager.onBuildComplete(false, errDetails);
467
+ }
452
468
  else
453
469
  Console.error("Build Error:", event.error);
454
470
  if (resolveFirstBuild)
@@ -457,8 +473,22 @@ function handleWatcherEvents(watcher, hotReloadManager, resolveFirstBuild) {
457
473
  if (event.code === 'BUNDLE_END')
458
474
  event.result.close();
459
475
  if (event.code === 'END') {
460
- if (!hasError && hotReloadManager)
461
- hotReloadManager.onBuildComplete(true);
476
+ const endBuildId = currentBuildId;
477
+ const hadError = erroredBuildIds.has(endBuildId);
478
+ // Só emite sucesso se:
479
+ // 1) esse END é do build mais recentemente iniciado (evita END atrasado)
480
+ // 2) esse build não teve ERROR
481
+ if (endBuildId === lastStartedBuildId && !hadError) {
482
+ if (hotReloadManager) {
483
+ hotReloadManager.onBuildComplete(true, { buildId: endBuildId });
484
+ }
485
+ }
486
+ // Limpa estados antigos pra não crescer sem limite.
487
+ // (qualquer build mais antigo que o último START não faz mais sentido manter)
488
+ for (const id of erroredBuildIds) {
489
+ if (id < lastStartedBuildId)
490
+ erroredBuildIds.delete(id);
491
+ }
462
492
  if (resolveFirstBuild) {
463
493
  resolveFirstBuild();
464
494
  resolveFirstBuild = null;
@@ -476,10 +506,9 @@ async function watchWithChunks(entryPoint, outdir, hotReloadManager = null) {
476
506
  const inputOptions = createRollupConfig(entryPoint, outdir, false);
477
507
  const outputOptions = {
478
508
  dir: outdir,
479
- format: 'iife',
480
- name: 'HwebApp',
509
+ // Em DEV usamos ESM para suportar externals como react/react-dom sem output.globals
510
+ format: 'es',
481
511
  entryFileNames: 'main.js',
482
- inlineDynamicImports: true,
483
512
  sourcemap: true
484
513
  };
485
514
  const watchOptions = {
@@ -511,9 +540,8 @@ async function watch(entryPoint, outfile, hotReloadManager = null) {
511
540
  const inputOptions = createRollupConfig(entryPoint, outdir, false);
512
541
  const outputOptions = {
513
542
  file: outfile,
514
- format: 'iife',
515
- name: 'HwebApp',
516
- inlineDynamicImports: true,
543
+ // Em DEV usamos ESM para suportar externals como react/react-dom sem output.globals
544
+ format: 'es',
517
545
  sourcemap: true
518
546
  };
519
547
  const watchOptions = {
@@ -15,7 +15,6 @@
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
17
  */
18
- // Sistema de roteamento do lado do cliente para hweb-sdk
19
18
  Object.defineProperty(exports, "__esModule", { value: true });
20
19
  exports.router = void 0;
21
20
  class Router {
@@ -1 +1,6 @@
1
- export {};
1
+ import { Root } from 'react-dom/client';
2
+ declare global {
3
+ interface Window {
4
+ __VATTS_ROOT__?: Root;
5
+ }
6
+ }
@@ -144,44 +144,16 @@ function App({ componentMap, routes, initialComponentPath, initialParams, layout
144
144
  const handleHMRUpdate = async (event) => {
145
145
  const { file, timestamp } = event.detail;
146
146
  const fileName = file ? file.split('/').pop()?.split('\\').pop() : 'unknown';
147
- console.log('🔥 HMR: Hot reloading...', fileName);
147
+ console.log('🔥 HMR: Component Update Triggered', fileName);
148
+ // Neste modelo onde o script main.js é recarregado por completo,
149
+ // a atualização real acontece via reinicialização do initializeClient.
150
+ // Este listener serve mais para feedback visual ou atualizações parciais se implementadas.
148
151
  try {
149
- // Aguarda um pouco para o esbuild terminar de recompilar
150
- await new Promise(resolve => setTimeout(resolve, 300));
151
- // Re-importa o módulo principal com cache busting
152
- const mainScript = document.querySelector('script[src*="main.js"]');
153
- if (mainScript) {
154
- const mainSrc = mainScript.src.split('?')[0];
155
- const cacheBustedSrc = `${mainSrc}?t=${timestamp}`;
156
- // Cria novo script
157
- const newScript = document.createElement('script');
158
- newScript.type = 'module';
159
- newScript.src = cacheBustedSrc;
160
- // Quando o novo script carregar, força re-render
161
- newScript.onload = () => {
162
- console.log('✅ HMR: Modules reloaded');
163
- // Força re-render do componente
164
- setHmrTimestamp(timestamp);
165
- // Marca sucesso
166
- window.__HMR_SUCCESS__ = true;
167
- setTimeout(() => {
168
- window.__HMR_SUCCESS__ = false;
169
- }, 3000);
170
- };
171
- newScript.onerror = () => {
172
- console.error('❌ HMR: Failed to reload modules');
173
- window.__HMR_SUCCESS__ = false;
174
- };
175
- // Remove o script antigo e adiciona o novo
176
- // (não remove para não quebrar o app)
177
- document.head.appendChild(newScript);
178
- }
179
- else {
180
- // Se não encontrou o script, apenas força re-render
181
- console.log('⚡ HMR: Forcing re-render');
182
- setHmrTimestamp(timestamp);
183
- window.__HMR_SUCCESS__ = true;
184
- }
152
+ setHmrTimestamp(timestamp);
153
+ window.__HMR_SUCCESS__ = true;
154
+ setTimeout(() => {
155
+ window.__HMR_SUCCESS__ = false;
156
+ }, 3000);
185
157
  }
186
158
  catch (error) {
187
159
  console.error('❌ HMR Error:', error);
@@ -229,7 +201,7 @@ function App({ componentMap, routes, initialComponentPath, initialParams, layout
229
201
  // para garantir que o ErrorModal apareça em qualquer estado.
230
202
  let resolvedContent;
231
203
  if (!CurrentPageComponent || initialComponentPath === '__404__') {
232
- const NotFoundComponent = window.__HWEB_NOT_FOUND__;
204
+ const NotFoundComponent = window.__VATTS_NOT_FOUND__;
233
205
  if (NotFoundComponent) {
234
206
  const NotFoundContent = (0, jsx_runtime_1.jsx)(NotFoundComponent, {});
235
207
  resolvedContent = layoutComponent
@@ -237,7 +209,7 @@ function App({ componentMap, routes, initialComponentPath, initialParams, layout
237
209
  : NotFoundContent;
238
210
  }
239
211
  else {
240
- const DefaultNotFound = window.__HWEB_DEFAULT_NOT_FOUND__;
212
+ const DefaultNotFound = window.__VATTS_DEFAULT_NOT_FOUND__;
241
213
  const NotFoundContent = (0, jsx_runtime_1.jsx)(DefaultNotFound, {});
242
214
  resolvedContent = layoutComponent
243
215
  ? react_1.default.createElement(layoutComponent, { children: NotFoundContent })
@@ -289,20 +261,38 @@ function initializeClient() {
289
261
  // Cria o mapa de componentes dinamicamente a partir dos módulos carregados
290
262
  const componentMap = {};
291
263
  // Registra todos os componentes que foram importados
292
- if (window.__HWEB_COMPONENTS__) {
293
- Object.assign(componentMap, window.__HWEB_COMPONENTS__);
264
+ if (window.__VATTS_COMPONENTS__) {
265
+ Object.assign(componentMap, window.__VATTS_COMPONENTS__);
294
266
  }
295
267
  else {
296
- console.warn('[Vatts] No components found in window.__HWEB_COMPONENTS__');
268
+ console.warn('[Vatts] No components found in window.__VATTS_COMPONENTS__');
297
269
  }
298
270
  const container = document.getElementById('root');
299
271
  if (!container) {
300
272
  console.error('[Vatts] Container #root not found.');
301
273
  return;
302
274
  }
275
+ // --- CORREÇÃO DO HOT RELOAD ---
276
+ // Verifica se já existe um root montado (acontece durante o reload do main.js)
277
+ if (window.__VATTS_ROOT__) {
278
+ // console.log('[Vatts] HMR: Unmounting previous root...');
279
+ try {
280
+ // Desmonta o app anterior para limpar event listeners e nós do DOM
281
+ // Isso evita o erro "NotFoundError: Failed to execute 'removeChild'"
282
+ window.__VATTS_ROOT__.unmount();
283
+ // Limpa o container explicitamente para garantir que o framer-motion/animações
284
+ // não deixaram lixo para trás que confunda o novo React
285
+ container.innerHTML = '';
286
+ }
287
+ catch (e) {
288
+ console.warn('[Vatts] Warning during unmount:', e);
289
+ }
290
+ }
303
291
  // Usar createRoot para render inicial (CSR)
304
292
  const root = (0, client_1.createRoot)(container);
305
- root.render((0, jsx_runtime_1.jsx)(App, { componentMap: componentMap, routes: initialData.routes, initialComponentPath: initialData.initialComponentPath, initialParams: initialData.initialParams, layoutComponent: window.__HWEB_LAYOUT__ }));
293
+ // Salva a referência globalmente
294
+ window.__VATTS_ROOT__ = root;
295
+ root.render((0, jsx_runtime_1.jsx)(App, { componentMap: componentMap, routes: initialData.routes, initialComponentPath: initialData.initialComponentPath, initialParams: initialData.initialParams, layoutComponent: window.__VATTS_LAYOUT__ }));
306
296
  }
307
297
  catch (error) {
308
298
  console.error('[Watts] Critical Error rendering application:', error);
@@ -326,7 +316,7 @@ if (document.readyState === 'loading') {
326
316
  else {
327
317
  // ESM Hoisting Fix:
328
318
  // Como este arquivo é importado pelo arquivo gerado automaticamente, ele executa
329
- // ANTES do corpo do arquivo gerado (onde window.__HWEB_COMPONENTS__ é definido).
319
+ // ANTES do corpo do arquivo gerado (onde window.__VATTS_COMPONENTS__ é definido).
330
320
  // Usamos setTimeout para garantir que a inicialização ocorra após as atribuições globais.
331
321
  setTimeout(initializeClient, 0);
332
322
  }
package/dist/helpers.js CHANGED
@@ -315,13 +315,13 @@ const parseBody = (req) => {
315
315
  /**
316
316
  * Inicializa servidor nativo do Vatts.js usando HTTP ou HTTPS
317
317
  */
318
- async function initNativeServer(hwebApp, options, port, hostname) {
318
+ async function initNativeServer(vattsApp, options, port, hostname) {
319
319
  const time = Date.now();
320
- await hwebApp.prepare();
320
+ await vattsApp.prepare();
321
321
  const projectDir = options.dir || process.cwd();
322
322
  const phase = options.dev ? 'development' : 'production';
323
323
  const vattsConfig = await loadVattsConfig(projectDir, phase);
324
- const handler = hwebApp.getRequestHandler();
324
+ const handler = vattsApp.getRequestHandler();
325
325
  const msg = console_1.default.dynamicLine(`${console_1.Colors.Bright}Starting Vatts.js on port ${options.port}${console_1.Colors.Reset}`);
326
326
  // --- LÓGICA DO LISTENER (REUTILIZÁVEL) ---
327
327
  // Extraímos a lógica principal para uma variável
@@ -461,7 +461,7 @@ async function initNativeServer(hwebApp, options, port, hostname) {
461
461
  ca: options.ssl.ca ? fs_1.default.readFileSync(options.ssl.ca) : undefined
462
462
  };
463
463
  // 1. Cria o servidor HTTPS principal
464
- server = https_1.default.createServer(sslOptions, requestListener); // (any para contornar HWebIncomingMessage)
464
+ server = https_1.default.createServer(sslOptions, requestListener);
465
465
  // 2. Cria o servidor de REDIRECIONAMENTO (HTTP -> HTTPS)
466
466
  const httpRedirectPort = options.ssl.redirectPort;
467
467
  http_1.default.createServer((req, res) => {
@@ -490,7 +490,7 @@ async function initNativeServer(hwebApp, options, port, hostname) {
490
490
  else {
491
491
  // --- MODO HTTP (Original) ---
492
492
  // Cria o servidor HTTP nativo
493
- server = http_1.default.createServer(requestListener); // (any para contornar HWebIncomingMessage)
493
+ server = http_1.default.createServer(requestListener);
494
494
  }
495
495
  // Configurações de segurança do servidor (usa configuração personalizada)
496
496
  server.setTimeout(vattsConfig.serverTimeout || 35000); // Timeout geral do servidor
@@ -502,26 +502,26 @@ async function initNativeServer(hwebApp, options, port, hostname) {
502
502
  msg.end(`${console_1.Colors.Bright}Ready on port ${console_1.Colors.BgGreen} ${options.port} ${console_1.Colors.Reset}${console_1.Colors.Bright} in ${Date.now() - time}ms${console_1.Colors.Reset}\n`);
503
503
  });
504
504
  // Configura WebSocket para hot reload (Comum a ambos)
505
- hwebApp.setupWebSocket(server);
506
- hwebApp.executeInstrumentation();
505
+ vattsApp.setupWebSocket(server);
506
+ vattsApp.executeInstrumentation();
507
507
  return server;
508
508
  }
509
509
  // --- Função Principal ---
510
510
  function app(options = {}) {
511
511
  const framework = options.framework || 'native';
512
512
  index_1.FrameworkAdapterFactory.setFramework(framework);
513
- // Tipando a app principal do hweb
514
- const hwebApp = (0, index_1.default)(options);
513
+ // Tipando a app principal do vatts
514
+ const vattsApp = (0, index_1.default)(options);
515
515
  return {
516
- ...hwebApp,
516
+ ...vattsApp,
517
517
  /**
518
518
  * Integra com uma aplicação de qualquer framework (Express, Fastify, etc)
519
519
  * O 'serverApp: any' é mantido para flexibilidade, já que pode ser de tipos diferentes.
520
520
  */
521
521
  integrate: async (serverApp) => {
522
- await hwebApp.prepare();
523
- const handler = hwebApp.getRequestHandler();
524
- // O framework é setado nas opções do hweb, que deve
522
+ await vattsApp.prepare();
523
+ const handler = vattsApp.getRequestHandler();
524
+ // O framework é setado nas opções do vatts, que deve
525
525
  // retornar o handler correto em getRequestHandler()
526
526
  // A lógica de integração original parece correta.
527
527
  if (framework === 'express') {
@@ -536,7 +536,7 @@ function app(options = {}) {
536
536
  serverApp.use(express.json());
537
537
  serverApp.use(express.urlencoded({ extended: true }));
538
538
  serverApp.use(handler);
539
- hwebApp.setupWebSocket(serverApp);
539
+ vattsApp.setupWebSocket(serverApp);
540
540
  }
541
541
  else if (framework === 'fastify') {
542
542
  try {
@@ -554,14 +554,14 @@ function app(options = {}) {
554
554
  await serverApp.register(async (fastify) => {
555
555
  fastify.all('*', handler);
556
556
  });
557
- hwebApp.setupWebSocket(serverApp);
557
+ vattsApp.setupWebSocket(serverApp);
558
558
  }
559
559
  else {
560
560
  // Generic integration (assume Express-like)
561
561
  serverApp.use(handler);
562
- hwebApp.setupWebSocket(serverApp);
562
+ vattsApp.setupWebSocket(serverApp);
563
563
  }
564
- hwebApp.executeInstrumentation();
564
+ vattsApp.executeInstrumentation();
565
565
  return serverApp;
566
566
  },
567
567
  /**
@@ -602,7 +602,7 @@ ${console_1.Colors.Bright + console_1.Colors.FgRed} \\/ /~~\\ | | .__/
602
602
  if (framework !== 'native') {
603
603
  console_1.default.warn(`The "${framework}" framework was selected, but the init() method only works with the "native" framework. Starting native server...`);
604
604
  }
605
- return await initNativeServer(hwebApp, options, actualPort, actualHostname);
605
+ return await initNativeServer(vattsApp, options, actualPort, actualHostname);
606
606
  }
607
607
  };
608
608
  }
@@ -11,23 +11,32 @@ export declare class HotReloadManager {
11
11
  private customHotReloadListener;
12
12
  private isBuilding;
13
13
  private buildCompleteResolve;
14
- private lastBuildError;
14
+ private lastFrontendErrorBuildId;
15
+ private get buildState();
16
+ private set buildState(value);
15
17
  constructor(projectDir: string);
16
18
  start(): Promise<void>;
17
19
  handleUpgrade(request: IncomingMessage, socket: any, head: Buffer): void;
18
20
  private setupWebSocketServer;
21
+ private broadcastCurrentState;
19
22
  private cleanupClient;
20
23
  private setupWatchers;
21
24
  private debounce;
25
+ private setBackendError;
22
26
  private handleAnySrcChange;
27
+ private notifyStatusChange;
23
28
  private notifyClients;
24
- private restartServer;
25
29
  getClientScript(): string;
26
30
  private clearBackendCache;
27
31
  onBackendApiChange(callback: () => void): void;
28
32
  onFrontendChange(callback: () => void): void;
29
33
  setHotReloadListener(listener: (file: string) => Promise<void> | void): void;
30
34
  removeHotReloadListener(): void;
35
+ private lastBuildCompleteTraceAt;
36
+ private traceBuildComplete;
31
37
  onBuildComplete(success: boolean, error?: any): void;
32
38
  stop(): void;
39
+ private lastTypecheckAt;
40
+ private lastTypecheckResult;
41
+ private typecheckFrontend;
33
42
  }