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 +44 -16
- package/dist/client/clientRouter.js +0 -1
- package/dist/client/entry.client.d.ts +6 -1
- package/dist/client/entry.client.js +34 -44
- package/dist/helpers.js +18 -18
- package/dist/hotReload.d.ts +11 -2
- package/dist/hotReload.js +369 -293
- package/dist/index.d.ts +1 -1
- package/dist/index.js +22 -13
- package/dist/renderer.js +1 -1
- package/dist/router.d.ts +5 -56
- package/dist/router.js +287 -473
- package/dist/types.d.ts +2 -4
- 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
|
|
@@ -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: '
|
|
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
|
-
|
|
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 = {
|
|
@@ -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 {
|
|
@@ -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);
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
293
|
-
Object.assign(componentMap, window.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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(
|
|
318
|
+
async function initNativeServer(vattsApp, options, port, hostname) {
|
|
319
319
|
const time = Date.now();
|
|
320
|
-
await
|
|
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 =
|
|
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);
|
|
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);
|
|
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
|
-
|
|
506
|
-
|
|
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
|
|
514
|
-
const
|
|
513
|
+
// Tipando a app principal do vatts
|
|
514
|
+
const vattsApp = (0, index_1.default)(options);
|
|
515
515
|
return {
|
|
516
|
-
...
|
|
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
|
|
523
|
-
const handler =
|
|
524
|
-
// O framework é setado nas opções do
|
|
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
|
-
|
|
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
|
-
|
|
557
|
+
vattsApp.setupWebSocket(serverApp);
|
|
558
558
|
}
|
|
559
559
|
else {
|
|
560
560
|
// Generic integration (assume Express-like)
|
|
561
561
|
serverApp.use(handler);
|
|
562
|
-
|
|
562
|
+
vattsApp.setupWebSocket(serverApp);
|
|
563
563
|
}
|
|
564
|
-
|
|
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(
|
|
605
|
+
return await initNativeServer(vattsApp, options, actualPort, actualHostname);
|
|
606
606
|
}
|
|
607
607
|
};
|
|
608
608
|
}
|
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
|
}
|