vatts 1.0.1 → 1.0.2-alpha.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/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
@@ -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 = {
@@ -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);
@@ -300,8 +272,26 @@ function initializeClient() {
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);
293
+ // Salva a referência globalmente
294
+ window.__VATTS_ROOT__ = root;
305
295
  root.render((0, jsx_runtime_1.jsx)(App, { componentMap: componentMap, routes: initialData.routes, initialComponentPath: initialData.initialComponentPath, initialParams: initialData.initialParams, layoutComponent: window.__HWEB_LAYOUT__ }));
306
296
  }
307
297
  catch (error) {
@@ -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
  }