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 +43 -15
- package/dist/client/entry.client.d.ts +6 -1
- package/dist/client/entry.client.js +27 -37
- package/dist/hotReload.d.ts +11 -2
- package/dist/hotReload.js +369 -293
- package/dist/index.js +12 -3
- package/dist/router.d.ts +5 -56
- package/dist/router.js +287 -473
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
449
|
+
if (event.code === 'START') {
|
|
450
|
+
currentBuildId += 1;
|
|
451
|
+
lastStartedBuildId = currentBuildId;
|
|
452
|
+
}
|
|
441
453
|
if (event.code === 'ERROR') {
|
|
442
|
-
|
|
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
|
-
|
|
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
|
-
|
|
461
|
-
|
|
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
|
-
|
|
480
|
-
|
|
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
|
-
|
|
515
|
-
|
|
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 = {
|
|
@@ -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:
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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) {
|
package/dist/hotReload.d.ts
CHANGED
|
@@ -11,23 +11,32 @@ export declare class HotReloadManager {
|
|
|
11
11
|
private customHotReloadListener;
|
|
12
12
|
private isBuilding;
|
|
13
13
|
private buildCompleteResolve;
|
|
14
|
-
private
|
|
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
|
}
|