hightjs 0.3.2 → 0.3.4
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/adapters/factory.js +8 -8
- package/dist/adapters/native.js +3 -3
- package/dist/auth/client.js +5 -5
- package/dist/auth/components.js +2 -2
- package/dist/auth/core.js +2 -2
- package/dist/auth/react.js +4 -4
- package/dist/auth/routes.js +1 -1
- package/dist/bin/hightjs.js +29 -328
- package/dist/builder.js +47 -21
- package/dist/client/DefaultNotFound.js +1 -1
- package/dist/client/entry.client.js +3 -3
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.js +90 -28
- package/dist/hotReload.d.ts +3 -1
- package/dist/hotReload.js +43 -51
- package/dist/index.d.ts +1 -1
- package/dist/index.js +16 -30
- package/dist/router.js +133 -62
- package/dist/types.d.ts +42 -0
- package/docs/config.md +201 -0
- package/example/hightjs.config.ts +81 -0
- package/example/package-lock.json +633 -3054
- package/example/package.json +1 -1
- package/package.json +1 -1
- package/src/adapters/factory.ts +8 -8
- package/src/adapters/native.ts +3 -3
- package/src/auth/client.ts +5 -5
- package/src/auth/components.tsx +2 -2
- package/src/auth/core.ts +2 -2
- package/src/auth/react.tsx +4 -4
- package/src/auth/routes.ts +1 -1
- package/src/bin/hightjs.js +30 -391
- package/src/builder.js +47 -22
- package/src/client/DefaultNotFound.tsx +1 -1
- package/src/client/entry.client.tsx +3 -3
- package/src/helpers.ts +105 -29
- package/src/hotReload.ts +45 -55
- package/src/index.ts +20 -33
- package/src/router.ts +140 -63
- package/src/types.ts +52 -0
- package/example/.hweb/entry.client.js +0 -24
- package/example/hweb-dist/main-5KKAYNUU.js +0 -1137
|
@@ -284,7 +284,7 @@ function initializeClient() {
|
|
|
284
284
|
const initialData = (window as any).__HWEB_INITIAL_DATA__;
|
|
285
285
|
|
|
286
286
|
if (!initialData) {
|
|
287
|
-
console.error('[hweb]
|
|
287
|
+
console.error('[hweb] Initial data not found on page.');
|
|
288
288
|
return;
|
|
289
289
|
}
|
|
290
290
|
|
|
@@ -298,7 +298,7 @@ function initializeClient() {
|
|
|
298
298
|
|
|
299
299
|
const container = document.getElementById('root');
|
|
300
300
|
if (!container) {
|
|
301
|
-
console.error('[hweb] Container #root
|
|
301
|
+
console.error('[hweb] Container #root not found.');
|
|
302
302
|
return;
|
|
303
303
|
}
|
|
304
304
|
|
|
@@ -316,7 +316,7 @@ function initializeClient() {
|
|
|
316
316
|
/>
|
|
317
317
|
);
|
|
318
318
|
} catch (error) {
|
|
319
|
-
console.error('[hweb]
|
|
319
|
+
console.error('[hweb] Error rendering application:', error);
|
|
320
320
|
}
|
|
321
321
|
}
|
|
322
322
|
|
package/src/helpers.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
1
3
|
/*
|
|
2
4
|
* This file is part of the HightJS Project.
|
|
3
5
|
* Copyright (c) 2025 itsmuzin
|
|
@@ -19,9 +21,10 @@
|
|
|
19
21
|
import http, {IncomingMessage, Server, ServerResponse} from 'http';
|
|
20
22
|
import os from 'os';
|
|
21
23
|
import {URLSearchParams} from 'url'; // API moderna, substitui 'querystring'
|
|
24
|
+
import path from 'path';
|
|
22
25
|
// Helpers para integração com diferentes frameworks
|
|
23
26
|
import hweb, {FrameworkAdapterFactory} from './index'; // Importando o tipo
|
|
24
|
-
import type {HightJSOptions} from './types';
|
|
27
|
+
import type {HightJSOptions, HightConfig, HightConfigFunction} from './types';
|
|
25
28
|
import Console, {Colors} from "./api/console";
|
|
26
29
|
import https, { Server as HttpsServer } from 'https'; // <-- ADICIONAR
|
|
27
30
|
import fs from 'fs'; // <-- ADICIONAR
|
|
@@ -66,7 +69,7 @@ function getLocalExternalIp(): string {
|
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
const sendBox = (options: HightJSOptions) => {
|
|
69
|
-
const isDev = options.dev ? "
|
|
72
|
+
const isDev = options.dev ? "Running in development mode" : null;
|
|
70
73
|
|
|
71
74
|
// 1. Verifica se o SSL está ativado (baseado na mesma lógica do initNativeServer)
|
|
72
75
|
const isSSL = options.ssl && options.ssl.key && options.ssl.cert;
|
|
@@ -75,12 +78,12 @@ const sendBox = (options: HightJSOptions) => {
|
|
|
75
78
|
|
|
76
79
|
// 2. Monta as mensagens com o protocolo correto
|
|
77
80
|
const messages = [
|
|
78
|
-
` ${Colors.FgGray}┃${Colors.Reset} Local:
|
|
81
|
+
` ${Colors.FgGray}┃${Colors.Reset} Local: ${Colors.FgGreen}${protocol}://localhost:${options.port}${Colors.Reset}`,
|
|
79
82
|
];
|
|
80
83
|
|
|
81
84
|
// Só adiciona a rede se o IP local for encontrado
|
|
82
85
|
if (localIp) {
|
|
83
|
-
messages.push(` ${Colors.FgGray}┃${Colors.Reset}
|
|
86
|
+
messages.push(` ${Colors.FgGray}┃${Colors.Reset} Network: ${Colors.FgGreen}${protocol}://${localIp}:${options.port}${Colors.Reset}`);
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
if (isDev) {
|
|
@@ -89,10 +92,78 @@ const sendBox = (options: HightJSOptions) => {
|
|
|
89
92
|
|
|
90
93
|
// Adiciona aviso de redirecionamento se estiver em modo SSL
|
|
91
94
|
if (isSSL && options.ssl?.redirectPort) {
|
|
92
|
-
messages.push(` ${Colors.FgGray}┃${Colors.Reset} HTTP (
|
|
95
|
+
messages.push(` ${Colors.FgGray}┃${Colors.Reset} HTTP (port ${options.ssl?.redirectPort}) is redirecting to HTTPS.`);
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
Console.box(messages.join("\n"), { title: "
|
|
98
|
+
Console.box(messages.join("\n"), { title: "Access on:" });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Carrega o arquivo de configuração hightjs.config.ts ou hightjs.config.js do projeto
|
|
103
|
+
* @param projectDir Diretório raiz do projeto
|
|
104
|
+
* @param phase Fase de execução ('development' ou 'production')
|
|
105
|
+
* @returns Configuração mesclada com os valores padrão
|
|
106
|
+
*/
|
|
107
|
+
async function loadHightConfig(projectDir: string, phase: string): Promise<HightConfig> {
|
|
108
|
+
const defaultConfig: HightConfig = {
|
|
109
|
+
maxHeadersCount: 100,
|
|
110
|
+
headersTimeout: 60000,
|
|
111
|
+
requestTimeout: 30000,
|
|
112
|
+
serverTimeout: 35000,
|
|
113
|
+
individualRequestTimeout: 30000,
|
|
114
|
+
maxUrlLength: 2048,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
// Tenta primeiro .ts, depois .js
|
|
119
|
+
const possiblePaths = [
|
|
120
|
+
path.join(projectDir, 'hightjs.config.ts'),
|
|
121
|
+
path.join(projectDir, 'hightjs.config.js'),
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
let configPath: string | null = null;
|
|
125
|
+
for (const p of possiblePaths) {
|
|
126
|
+
if (fs.existsSync(p)) {
|
|
127
|
+
configPath = p;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!configPath) {
|
|
133
|
+
return defaultConfig;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Remove do cache para permitir hot reload da configuração em dev
|
|
137
|
+
delete require.cache[require.resolve(configPath)];
|
|
138
|
+
|
|
139
|
+
const configModule = require(configPath);
|
|
140
|
+
const configExport = configModule.default || configModule;
|
|
141
|
+
|
|
142
|
+
let userConfig: HightConfig;
|
|
143
|
+
|
|
144
|
+
if (typeof configExport === 'function') {
|
|
145
|
+
// Suporta tanto função síncrona quanto assíncrona
|
|
146
|
+
userConfig = await Promise.resolve(
|
|
147
|
+
(configExport as HightConfigFunction)(phase, { defaultConfig })
|
|
148
|
+
);
|
|
149
|
+
} else {
|
|
150
|
+
userConfig = configExport;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Mescla a configuração do usuário com a padrão
|
|
154
|
+
const mergedConfig = { ...defaultConfig, ...userConfig };
|
|
155
|
+
|
|
156
|
+
const configFileName = path.basename(configPath);
|
|
157
|
+
Console.info(`${Colors.FgCyan}[Config]${Colors.Reset} Loaded ${configFileName}`);
|
|
158
|
+
|
|
159
|
+
return mergedConfig;
|
|
160
|
+
} catch (error) {
|
|
161
|
+
if (error instanceof Error) {
|
|
162
|
+
Console.warn(`${Colors.FgYellow}[Config]${Colors.Reset} Error loading hightjs.config: ${error.message}`);
|
|
163
|
+
Console.warn(`${Colors.FgYellow}[Config]${Colors.Reset} Using default configuration`);
|
|
164
|
+
}
|
|
165
|
+
return defaultConfig;
|
|
166
|
+
}
|
|
96
167
|
}
|
|
97
168
|
|
|
98
169
|
/**
|
|
@@ -181,8 +252,14 @@ async function initNativeServer(hwebApp: HWebApp, options: HightJSOptions, port:
|
|
|
181
252
|
const time = Date.now();
|
|
182
253
|
|
|
183
254
|
await hwebApp.prepare();
|
|
255
|
+
|
|
256
|
+
// Carrega a configuração do arquivo hightjs.config.js
|
|
257
|
+
const projectDir = options.dir || process.cwd();
|
|
258
|
+
const phase = options.dev ? 'development' : 'production';
|
|
259
|
+
const hightConfig = await loadHightConfig(projectDir, phase);
|
|
260
|
+
|
|
184
261
|
const handler = hwebApp.getRequestHandler();
|
|
185
|
-
const msg = Console.dynamicLine(` ${Colors.BgYellow} ready ${Colors.Reset} ${Colors.Bright}
|
|
262
|
+
const msg = Console.dynamicLine(` ${Colors.BgYellow} ready ${Colors.Reset} ${Colors.Bright}Starting HightJS on port ${options.port}${Colors.Reset}`);
|
|
186
263
|
|
|
187
264
|
// --- LÓGICA DO LISTENER (REUTILIZÁVEL) ---
|
|
188
265
|
// Extraímos a lógica principal para uma variável
|
|
@@ -200,16 +277,17 @@ async function initNativeServer(hwebApp: HWebApp, options: HightJSOptions, port:
|
|
|
200
277
|
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
|
201
278
|
}
|
|
202
279
|
|
|
203
|
-
// Timeout por requisição
|
|
204
|
-
req.setTimeout(30000, () => {
|
|
280
|
+
// Timeout por requisição (usa configuração personalizada)
|
|
281
|
+
req.setTimeout(hightConfig.individualRequestTimeout || 30000, () => {
|
|
205
282
|
res.statusCode = 408; // Request Timeout
|
|
206
283
|
res.end('Request timeout');
|
|
207
284
|
});
|
|
208
285
|
|
|
209
286
|
try {
|
|
210
|
-
// Validação básica de URL
|
|
287
|
+
// Validação básica de URL (usa configuração personalizada)
|
|
211
288
|
const url = req.url || '/';
|
|
212
|
-
|
|
289
|
+
const maxUrlLength = hightConfig.maxUrlLength || 2048;
|
|
290
|
+
if (url.length > maxUrlLength) {
|
|
213
291
|
res.statusCode = 414; // URI Too Long
|
|
214
292
|
res.end('URL too long');
|
|
215
293
|
return;
|
|
@@ -227,9 +305,9 @@ async function initNativeServer(hwebApp: HWebApp, options: HightJSOptions, port:
|
|
|
227
305
|
} catch (error) {
|
|
228
306
|
// Log do erro no servidor
|
|
229
307
|
if (error instanceof Error) {
|
|
230
|
-
Console.error(`
|
|
308
|
+
Console.error(`Native server error: ${error.message}`);
|
|
231
309
|
} else {
|
|
232
|
-
Console.error('
|
|
310
|
+
Console.error('Unknown native server error:', error);
|
|
233
311
|
}
|
|
234
312
|
|
|
235
313
|
// Tratamento de erro (idêntico ao seu original)
|
|
@@ -297,15 +375,15 @@ async function initNativeServer(hwebApp: HWebApp, options: HightJSOptions, port:
|
|
|
297
375
|
server = http.createServer(requestListener as any); // (any para contornar HWebIncomingMessage)
|
|
298
376
|
}
|
|
299
377
|
|
|
300
|
-
// Configurações de segurança do servidor (
|
|
301
|
-
server.setTimeout(35000); // Timeout geral do servidor
|
|
302
|
-
server.maxHeadersCount = 100; // Limita número de headers
|
|
303
|
-
server.headersTimeout = 60000; // Timeout para headers
|
|
304
|
-
server.requestTimeout = 30000; // Timeout para requisições
|
|
378
|
+
// Configurações de segurança do servidor (usa configuração personalizada)
|
|
379
|
+
server.setTimeout(hightConfig.serverTimeout || 35000); // Timeout geral do servidor
|
|
380
|
+
server.maxHeadersCount = hightConfig.maxHeadersCount || 100; // Limita número de headers
|
|
381
|
+
server.headersTimeout = hightConfig.headersTimeout || 60000; // Timeout para headers
|
|
382
|
+
server.requestTimeout = hightConfig.requestTimeout || 30000; // Timeout para requisições
|
|
305
383
|
|
|
306
384
|
server.listen(port, hostname, () => {
|
|
307
385
|
sendBox({ ...options, port });
|
|
308
|
-
msg.end(` ${Colors.BgGreen} ready ${Colors.Reset} ${Colors.Bright}
|
|
386
|
+
msg.end(` ${Colors.BgGreen} ready ${Colors.Reset} ${Colors.Bright}Ready on port ${Colors.BgGreen} ${options.port} ${Colors.Reset}${Colors.Bright} in ${Date.now() - time}ms${Colors.Reset}\n`);
|
|
309
387
|
});
|
|
310
388
|
|
|
311
389
|
// Configura WebSocket para hot reload (Comum a ambos)
|
|
@@ -344,7 +422,7 @@ export function app(options: HightJSOptions = {}) {
|
|
|
344
422
|
const cookieParser = require('cookie-parser');
|
|
345
423
|
serverApp.use(cookieParser());
|
|
346
424
|
} catch (e) {
|
|
347
|
-
Console.error("
|
|
425
|
+
Console.error("Could not find cookie-parser");
|
|
348
426
|
}
|
|
349
427
|
serverApp.use(express.json());
|
|
350
428
|
serverApp.use(express.urlencoded({ extended: true }));
|
|
@@ -355,12 +433,12 @@ export function app(options: HightJSOptions = {}) {
|
|
|
355
433
|
try {
|
|
356
434
|
await serverApp.register(require('@fastify/cookie'));
|
|
357
435
|
} catch (e) {
|
|
358
|
-
Console.error("
|
|
436
|
+
Console.error("Could not find @fastify/cookie");
|
|
359
437
|
}
|
|
360
438
|
try {
|
|
361
439
|
await serverApp.register(require('@fastify/formbody'));
|
|
362
440
|
} catch (e) {
|
|
363
|
-
Console.error("
|
|
441
|
+
Console.error("Could not find @fastify/formbody");
|
|
364
442
|
}
|
|
365
443
|
await serverApp.register(async (fastify: any) => {
|
|
366
444
|
fastify.all('*', handler);
|
|
@@ -390,7 +468,7 @@ export function app(options: HightJSOptions = {}) {
|
|
|
390
468
|
const data = await response.json();
|
|
391
469
|
return data.version;
|
|
392
470
|
} catch (error) {
|
|
393
|
-
Console.error('
|
|
471
|
+
Console.error('Could not check for the latest HightJS version:', error);
|
|
394
472
|
return currentVersion; // Retorna a versão atual em caso de erro
|
|
395
473
|
}
|
|
396
474
|
}
|
|
@@ -398,23 +476,21 @@ export function app(options: HightJSOptions = {}) {
|
|
|
398
476
|
const isUpToDate = latestVersion === currentVersion;
|
|
399
477
|
let message;
|
|
400
478
|
if (!isUpToDate) {
|
|
401
|
-
message = `${Colors.FgGreen}
|
|
479
|
+
message = `${Colors.FgGreen} A new version is available (v${latestVersion})${Colors.FgMagenta}`
|
|
402
480
|
} else {
|
|
403
|
-
message = `${Colors.FgGreen}
|
|
481
|
+
message = `${Colors.FgGreen} You are on the latest version${Colors.FgMagenta}`
|
|
404
482
|
}
|
|
405
483
|
console.log(`${Colors.FgMagenta}
|
|
406
484
|
__ ___ ${Colors.FgGreen} __ ${Colors.FgMagenta}
|
|
407
485
|
|__| | / _\` |__| | ${Colors.FgGreen} | /__\` ${Colors.FgMagenta} ${Colors.FgMagenta}HightJS ${Colors.FgGray}(v${require('../package.json').version}) - itsmuzin${Colors.FgMagenta}
|
|
408
|
-
| | | \\__> | | | ${Colors.FgGreen}\\__/ .__/ ${message}
|
|
409
|
-
|
|
410
|
-
|
|
486
|
+
| | | \\__> | | | ${Colors.FgGreen}\\__/ .__/ ${message}
|
|
411
487
|
${Colors.Reset}`);
|
|
412
488
|
|
|
413
489
|
const actualPort = options.port || 3000;
|
|
414
490
|
const actualHostname = options.hostname || "0.0.0.0";
|
|
415
491
|
|
|
416
492
|
if (framework !== 'native') {
|
|
417
|
-
Console.warn(`
|
|
493
|
+
Console.warn(`The "${framework}" framework was selected, but the init() method only works with the "native" framework. Starting native server...`);
|
|
418
494
|
}
|
|
419
495
|
|
|
420
496
|
|
package/src/hotReload.ts
CHANGED
|
@@ -39,6 +39,8 @@ export class HotReloadManager {
|
|
|
39
39
|
private isShuttingDown: boolean = false;
|
|
40
40
|
private debounceTimers: Map<string, NodeJS.Timeout> = new Map();
|
|
41
41
|
private customHotReloadListener: ((file: string) => Promise<void> | void) | null = null;
|
|
42
|
+
private isBuilding: boolean = false;
|
|
43
|
+
private buildCompleteResolve: (() => void) | null = null;
|
|
42
44
|
|
|
43
45
|
constructor(projectDir: string) {
|
|
44
46
|
this.projectDir = projectDir;
|
|
@@ -200,33 +202,48 @@ export class HotReloadManager {
|
|
|
200
202
|
clearFileCache(filePath);
|
|
201
203
|
this.clearBackendCache(filePath);
|
|
202
204
|
|
|
203
|
-
//
|
|
204
|
-
const ext = path.extname(filePath);
|
|
205
|
-
if ([".ts", ".tsx", ".js", ".jsx"].includes(ext)) {
|
|
206
|
-
const result = await this.checkFrontendBuild(filePath);
|
|
207
|
-
if (result.error) {
|
|
208
|
-
this.notifyClients('src-error', { file: filePath, error: result.error });
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Se for arquivo de frontend, notifica o cliente para recarregar a página
|
|
205
|
+
// Se for arquivo de frontend, aguarda o build terminar antes de recarregar
|
|
214
206
|
if (isFrontendFile) {
|
|
215
|
-
Console.logWithout(Levels.INFO, Colors.BgRed,`📄
|
|
216
|
-
|
|
217
|
-
|
|
207
|
+
Console.logWithout(Levels.INFO, Colors.BgRed,`📄 Waiting for frontend build...`);
|
|
208
|
+
|
|
209
|
+
// Marca que estamos esperando um build
|
|
210
|
+
this.isBuilding = true;
|
|
211
|
+
|
|
212
|
+
// Cria uma promise que será resolvida quando o build terminar
|
|
213
|
+
const buildPromise = new Promise<void>((resolve) => {
|
|
214
|
+
this.buildCompleteResolve = resolve;
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Aguarda o build terminar (com timeout de 30 segundos)
|
|
218
|
+
const timeoutPromise = new Promise<void>((_, reject) => {
|
|
219
|
+
setTimeout(() => reject(new Error('Build timeout')), 30000);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
await Promise.race([buildPromise, timeoutPromise]);
|
|
224
|
+
Console.logWithout(Levels.INFO, Colors.BgRed,`✅ Build complete, reloading frontend...`);
|
|
225
|
+
this.frontendChangeCallback?.();
|
|
226
|
+
this.notifyClients('frontend-reload', { file: filePath, event: 'change' });
|
|
227
|
+
} catch (error) {
|
|
228
|
+
Console.logWithout(Levels.ERROR, Colors.BgRed,`⚠️ Timeout in build, reloading anyway...`);
|
|
229
|
+
this.frontendChangeCallback?.();
|
|
230
|
+
this.notifyClients('frontend-reload', { file: filePath, event: 'change' });
|
|
231
|
+
} finally {
|
|
232
|
+
this.isBuilding = false;
|
|
233
|
+
this.buildCompleteResolve = null;
|
|
234
|
+
}
|
|
218
235
|
}
|
|
219
236
|
|
|
220
237
|
// Se for arquivo de backend, recarrega o módulo e notifica
|
|
221
238
|
if (isBackendFile) {
|
|
222
|
-
Console.logWithout(Levels.INFO, Colors.BgRed,`⚙️
|
|
239
|
+
Console.logWithout(Levels.INFO, Colors.BgRed,`⚙️ Reloading backend...`);
|
|
223
240
|
this.backendApiChangeCallback?.();
|
|
224
241
|
this.notifyClients('backend-api-reload', { file: filePath, event: 'change' });
|
|
225
242
|
}
|
|
226
243
|
|
|
227
244
|
// Fallback: se não for nem frontend nem backend detectado, recarrega tudo
|
|
228
245
|
if (!isFrontendFile && !isBackendFile) {
|
|
229
|
-
Console.logWithout(Levels.INFO, Colors.BgRed,`🔄
|
|
246
|
+
Console.logWithout(Levels.INFO, Colors.BgRed,`🔄 Reloading application...`);
|
|
230
247
|
this.frontendChangeCallback?.();
|
|
231
248
|
this.backendApiChangeCallback?.();
|
|
232
249
|
this.notifyClients('src-reload', { file: filePath, event: 'change' });
|
|
@@ -238,7 +255,7 @@ export class HotReloadManager {
|
|
|
238
255
|
await this.customHotReloadListener(filePath);
|
|
239
256
|
} catch (error) {
|
|
240
257
|
// @ts-ignore
|
|
241
|
-
Console.logWithout(Levels.ERROR, `
|
|
258
|
+
Console.logWithout(Levels.ERROR, `Error in custom listener: ${error.message}`);
|
|
242
259
|
}
|
|
243
260
|
}
|
|
244
261
|
}
|
|
@@ -256,7 +273,7 @@ export class HotReloadManager {
|
|
|
256
273
|
try {
|
|
257
274
|
ws.send(message);
|
|
258
275
|
} catch (error) {
|
|
259
|
-
Console.logWithout(Levels.ERROR, Colors.BgRed, `
|
|
276
|
+
Console.logWithout(Levels.ERROR, Colors.BgRed, `Error sending WebSocket message: ${error}`);
|
|
260
277
|
deadClients.push(ws);
|
|
261
278
|
}
|
|
262
279
|
} else {
|
|
@@ -325,7 +342,7 @@ export class HotReloadManager {
|
|
|
325
342
|
ws = new WebSocket('ws://localhost:3000/hweb-hotreload/');
|
|
326
343
|
|
|
327
344
|
ws.onopen = function() {
|
|
328
|
-
console.log('🔌 Hot-reload
|
|
345
|
+
console.log('🔌 Hot-reload connected');
|
|
329
346
|
isConnected = true;
|
|
330
347
|
reconnectAttempts = 0;
|
|
331
348
|
reconnectInterval = 1000;
|
|
@@ -345,13 +362,13 @@ export class HotReloadManager {
|
|
|
345
362
|
window.location.reload();
|
|
346
363
|
break;
|
|
347
364
|
case 'server-restart':
|
|
348
|
-
console.log('🔄
|
|
365
|
+
console.log('🔄 Server restarting...');
|
|
349
366
|
break;
|
|
350
367
|
case 'server-ready':
|
|
351
368
|
setTimeout(() => window.location.reload(), 500);
|
|
352
369
|
break;
|
|
353
370
|
case 'frontend-error':
|
|
354
|
-
console.error('❌
|
|
371
|
+
console.error('❌ Frontend error:', message.data);
|
|
355
372
|
break;
|
|
356
373
|
}
|
|
357
374
|
} catch (e) {
|
|
@@ -376,7 +393,7 @@ export class HotReloadManager {
|
|
|
376
393
|
};
|
|
377
394
|
|
|
378
395
|
} catch (error) {
|
|
379
|
-
console.error('
|
|
396
|
+
console.error('Error creating WebSocket:', error);
|
|
380
397
|
scheduleReconnect();
|
|
381
398
|
}
|
|
382
399
|
}
|
|
@@ -446,45 +463,18 @@ export class HotReloadManager {
|
|
|
446
463
|
|
|
447
464
|
setHotReloadListener(listener: (file: string) => Promise<void> | void) {
|
|
448
465
|
this.customHotReloadListener = listener;
|
|
449
|
-
Console.info('🔌 Hot reload listener
|
|
466
|
+
Console.info('🔌 Hot reload custom listener registered');
|
|
450
467
|
}
|
|
451
468
|
|
|
452
469
|
removeHotReloadListener() {
|
|
453
470
|
this.customHotReloadListener = null;
|
|
454
471
|
}
|
|
455
472
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
return new Promise<{ error?: string }>((resolve) => {
|
|
462
|
-
const proc = spawn(process.execPath, [tsNodePath, '--transpile-only', filePath], {
|
|
463
|
-
cwd: this.projectDir,
|
|
464
|
-
env: process.env,
|
|
465
|
-
timeout: 10000 // Timeout de 10 segundos
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
let errorMsg = '';
|
|
469
|
-
|
|
470
|
-
proc.stderr.on('data', (data: Buffer) => {
|
|
471
|
-
errorMsg += data.toString();
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
proc.on('close', (code: number) => {
|
|
475
|
-
if (code !== 0 && errorMsg) {
|
|
476
|
-
resolve({ error: errorMsg });
|
|
477
|
-
} else {
|
|
478
|
-
resolve({});
|
|
479
|
-
}
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
proc.on('error', (error: Error) => {
|
|
483
|
-
resolve({ error: error.message });
|
|
484
|
-
});
|
|
485
|
-
});
|
|
486
|
-
} catch (error) {
|
|
487
|
-
return { error: `Erro ao verificar build: ${error}` };
|
|
473
|
+
onBuildComplete(success: boolean) {
|
|
474
|
+
if (this.buildCompleteResolve) {
|
|
475
|
+
this.buildCompleteResolve();
|
|
476
|
+
this.buildCompleteResolve = null;
|
|
488
477
|
}
|
|
478
|
+
this.isBuilding = false;
|
|
489
479
|
}
|
|
490
480
|
}
|
package/src/index.ts
CHANGED
|
@@ -61,6 +61,9 @@ export { app } from './helpers';
|
|
|
61
61
|
// Exporta o sistema de WebSocket
|
|
62
62
|
export type { WebSocketContext, WebSocketHandler } from './types';
|
|
63
63
|
|
|
64
|
+
// Exporta os tipos de configuração
|
|
65
|
+
export type { HightConfig, HightConfigFunction } from './types';
|
|
66
|
+
|
|
64
67
|
// Função para verificar se o projeto é grande o suficiente para se beneficiar de chunks
|
|
65
68
|
function isLargeProject(projectDir: string): boolean {
|
|
66
69
|
try {
|
|
@@ -254,7 +257,7 @@ export default function hweb(options: HightJSOptions) {
|
|
|
254
257
|
|
|
255
258
|
// Adiciona callback para recarregar TUDO quando qualquer arquivo mudar
|
|
256
259
|
hotReloadManager.onBackendApiChange(() => {
|
|
257
|
-
|
|
260
|
+
|
|
258
261
|
|
|
259
262
|
loadBackendRoutes(userBackendRoutesDir);
|
|
260
263
|
processWebSocketRoutes(); // Processa rotas WS após recarregar backend
|
|
@@ -262,18 +265,16 @@ export default function hweb(options: HightJSOptions) {
|
|
|
262
265
|
|
|
263
266
|
// Adiciona callback para regenerar entry file quando frontend mudar
|
|
264
267
|
hotReloadManager.onFrontendChange(() => {
|
|
265
|
-
Console.info('🔄 Regenerando frontend...');
|
|
266
268
|
regenerateEntryFile();
|
|
267
269
|
});
|
|
268
270
|
}
|
|
269
271
|
const now = Date.now();
|
|
270
|
-
const timee = Console.dynamicLine(` ${Colors.BgYellow} router ${Colors.Reset}
|
|
272
|
+
const timee = Console.dynamicLine(` ${Colors.BgYellow} router ${Colors.Reset} Loading routes and components`);
|
|
271
273
|
const spinnerFrames1 = ['|', '/', '-', '\\'];
|
|
272
274
|
let frameIndex1 = 0;
|
|
273
275
|
|
|
274
276
|
const spinner1 = setInterval(() => {
|
|
275
|
-
|
|
276
|
-
timee.update(` ${Colors.FgYellow}${spinnerFrames1[frameIndex1]}${Colors.Reset} Carregando rotas e componentes...`);
|
|
277
|
+
timee.update(` ${Colors.FgYellow}${spinnerFrames1[frameIndex1]}${Colors.Reset} Loading routes and components...`);
|
|
277
278
|
frameIndex1 = (frameIndex1 + 1) % spinnerFrames1.length;
|
|
278
279
|
}, 100); // muda a cada 100ms
|
|
279
280
|
// ORDEM IMPORTANTE: Carrega TUDO antes de criar o arquivo de entrada
|
|
@@ -293,18 +294,18 @@ export default function hweb(options: HightJSOptions) {
|
|
|
293
294
|
|
|
294
295
|
entryPoint = createEntryFile(dir, frontendRoutes);
|
|
295
296
|
clearInterval(spinner1)
|
|
296
|
-
timee.end(` ${Colors.BgGreen} router ${Colors.Reset}
|
|
297
|
+
timee.end(` ${Colors.BgGreen} router ${Colors.Reset} Routes and components loaded in ${Date.now() - now}ms`);
|
|
297
298
|
|
|
298
299
|
|
|
299
300
|
if (isProduction) {
|
|
300
|
-
const time = Console.dynamicLine(` ${Colors.BgYellow} build ${Colors.Reset}
|
|
301
|
+
const time = Console.dynamicLine(` ${Colors.BgYellow} build ${Colors.Reset} Starting client build`);
|
|
301
302
|
|
|
302
303
|
// Spinner
|
|
303
304
|
const spinnerFrames = ['|', '/', '-', '\\'];
|
|
304
305
|
let frameIndex = 0;
|
|
305
306
|
|
|
306
307
|
const spinner = setInterval(() => {
|
|
307
|
-
time.update(` ${Colors.FgYellow}${spinnerFrames[frameIndex]}${Colors.Reset}
|
|
308
|
+
time.update(` ${Colors.FgYellow}${spinnerFrames[frameIndex]}${Colors.Reset} Building...`);
|
|
308
309
|
frameIndex = (frameIndex + 1) % spinnerFrames.length;
|
|
309
310
|
}, 100); // muda a cada 100ms
|
|
310
311
|
|
|
@@ -314,14 +315,14 @@ export default function hweb(options: HightJSOptions) {
|
|
|
314
315
|
|
|
315
316
|
clearInterval(spinner); // para o spinner
|
|
316
317
|
time.update(""); // limpa a linha
|
|
317
|
-
time.end(` ${Colors.BgGreen} build ${Colors.Reset}
|
|
318
|
+
time.end(` ${Colors.BgGreen} build ${Colors.Reset} Client build completed in ${elapsed}ms`);
|
|
318
319
|
|
|
319
320
|
} else {
|
|
320
|
-
const time = Console.dynamicLine(` ${Colors.BgYellow} watcher ${Colors.Reset}
|
|
321
|
+
const time = Console.dynamicLine(` ${Colors.BgYellow} watcher ${Colors.Reset} Starting client watch`);
|
|
321
322
|
watchWithChunks(entryPoint, outDir, hotReloadManager!).catch(err => {
|
|
322
|
-
Console.error(`
|
|
323
|
+
Console.error(`Error starting watch`, err);
|
|
323
324
|
});
|
|
324
|
-
time.end(` ${Colors.BgGreen} watcher ${Colors.Reset} Watch
|
|
325
|
+
time.end(` ${Colors.BgGreen} watcher ${Colors.Reset} Client Watch started`);
|
|
325
326
|
}
|
|
326
327
|
|
|
327
328
|
},
|
|
@@ -339,7 +340,6 @@ export default function hweb(options: HightJSOptions) {
|
|
|
339
340
|
if (instrumentation.hotReloadListener && typeof instrumentation.hotReloadListener === 'function') {
|
|
340
341
|
if (hotReloadManager) {
|
|
341
342
|
hotReloadManager.setHotReloadListener(instrumentation.hotReloadListener);
|
|
342
|
-
Console.info('✅ Hot reload listener registrado');
|
|
343
343
|
}
|
|
344
344
|
}
|
|
345
345
|
|
|
@@ -348,7 +348,7 @@ export default function hweb(options: HightJSOptions) {
|
|
|
348
348
|
} else if (typeof instrumentation.default === 'function') {
|
|
349
349
|
instrumentation.default();
|
|
350
350
|
} else {
|
|
351
|
-
Console.warn(`
|
|
351
|
+
Console.warn(`The instrumentation file ${instrumentationFile} does not export a default function.`);
|
|
352
352
|
}
|
|
353
353
|
}
|
|
354
354
|
},
|
|
@@ -470,8 +470,8 @@ export default function hweb(options: HightJSOptions) {
|
|
|
470
470
|
return;
|
|
471
471
|
}
|
|
472
472
|
} catch (error) {
|
|
473
|
-
Console.error(`
|
|
474
|
-
genericRes.status(500).text('
|
|
473
|
+
Console.error(`API route error ${pathname}:`, error);
|
|
474
|
+
genericRes.status(500).text('Internal server error in API');
|
|
475
475
|
return;
|
|
476
476
|
}
|
|
477
477
|
}
|
|
@@ -498,8 +498,8 @@ export default function hweb(options: HightJSOptions) {
|
|
|
498
498
|
genericRes.status(404).header('Content-Type', 'text/html').send(html);
|
|
499
499
|
return;
|
|
500
500
|
} catch (error) {
|
|
501
|
-
Console.error(`
|
|
502
|
-
genericRes.status(404).text('
|
|
501
|
+
Console.error(`Error rendering page 404:`, error);
|
|
502
|
+
genericRes.status(404).text('Page not found');
|
|
503
503
|
return;
|
|
504
504
|
}
|
|
505
505
|
}
|
|
@@ -513,8 +513,8 @@ export default function hweb(options: HightJSOptions) {
|
|
|
513
513
|
});
|
|
514
514
|
genericRes.status(200).header('Content-Type', 'text/html').send(html);
|
|
515
515
|
} catch (error) {
|
|
516
|
-
Console.error(`
|
|
517
|
-
genericRes.status(500).text('
|
|
516
|
+
Console.error(`Error rendering page ${pathname}:`, error);
|
|
517
|
+
genericRes.status(500).text('Internal server error');
|
|
518
518
|
}
|
|
519
519
|
};
|
|
520
520
|
},
|
|
@@ -529,19 +529,6 @@ export default function hweb(options: HightJSOptions) {
|
|
|
529
529
|
setupWebSocketUpgrade(actualServer, hotReloadManager);
|
|
530
530
|
},
|
|
531
531
|
|
|
532
|
-
build: async () => {
|
|
533
|
-
const msg = Console.dynamicLine(` ${Colors.FgYellow}● ${Colors.Reset}Iniciando build do cliente para produção`);
|
|
534
|
-
const outDir = path.join(dir, 'hweb-dist');
|
|
535
|
-
fs.mkdirSync(outDir, {recursive: true});
|
|
536
|
-
|
|
537
|
-
const routes = loadRoutes(userWebRoutesDir);
|
|
538
|
-
const entryPoint = createEntryFile(dir, routes);
|
|
539
|
-
const outfile = path.join(outDir, 'main.js');
|
|
540
|
-
|
|
541
|
-
await build(entryPoint, outfile, true); // Força produção no build manual
|
|
542
|
-
|
|
543
|
-
msg.end(` ${Colors.FgGreen}● ${Colors.Reset}Build do cliente concluído: ${outfile}`);
|
|
544
|
-
},
|
|
545
532
|
|
|
546
533
|
stop: () => {
|
|
547
534
|
if (hotReloadManager) {
|