hightjs 0.1.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/.idea/HightJS.iml +9 -0
- package/.idea/copilot.data.migration.agent.xml +6 -0
- package/.idea/copilot.data.migration.ask.xml +6 -0
- package/.idea/copilot.data.migration.ask2agent.xml +6 -0
- package/.idea/copilot.data.migration.edit.xml +6 -0
- package/.idea/inspectionProfiles/Project_Default.xml +13 -0
- package/.idea/libraries/test_package.xml +9 -0
- package/.idea/libraries/ts_commonjs_default_export.xml +9 -0
- package/.idea/misc.xml +7 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/LICENSE +13 -0
- package/README.md +508 -0
- package/dist/adapters/express.d.ts +7 -0
- package/dist/adapters/express.js +63 -0
- package/dist/adapters/factory.d.ts +23 -0
- package/dist/adapters/factory.js +122 -0
- package/dist/adapters/fastify.d.ts +25 -0
- package/dist/adapters/fastify.js +61 -0
- package/dist/adapters/native.d.ts +8 -0
- package/dist/adapters/native.js +203 -0
- package/dist/adapters/starters/express.d.ts +0 -0
- package/dist/adapters/starters/express.js +1 -0
- package/dist/adapters/starters/factory.d.ts +0 -0
- package/dist/adapters/starters/factory.js +1 -0
- package/dist/adapters/starters/fastify.d.ts +0 -0
- package/dist/adapters/starters/fastify.js +1 -0
- package/dist/adapters/starters/index.d.ts +0 -0
- package/dist/adapters/starters/index.js +1 -0
- package/dist/adapters/starters/native.d.ts +0 -0
- package/dist/adapters/starters/native.js +1 -0
- package/dist/api/console.d.ts +92 -0
- package/dist/api/console.js +276 -0
- package/dist/api/http.d.ts +180 -0
- package/dist/api/http.js +467 -0
- package/dist/auth/client.d.ts +14 -0
- package/dist/auth/client.js +68 -0
- package/dist/auth/components.d.ts +29 -0
- package/dist/auth/components.js +84 -0
- package/dist/auth/core.d.ts +38 -0
- package/dist/auth/core.js +124 -0
- package/dist/auth/index.d.ts +7 -0
- package/dist/auth/index.js +27 -0
- package/dist/auth/jwt.d.ts +41 -0
- package/dist/auth/jwt.js +169 -0
- package/dist/auth/providers.d.ts +5 -0
- package/dist/auth/providers.js +14 -0
- package/dist/auth/react/index.d.ts +6 -0
- package/dist/auth/react/index.js +32 -0
- package/dist/auth/react.d.ts +22 -0
- package/dist/auth/react.js +175 -0
- package/dist/auth/routes.d.ts +16 -0
- package/dist/auth/routes.js +104 -0
- package/dist/auth/types.d.ts +62 -0
- package/dist/auth/types.js +2 -0
- package/dist/bin/hightjs.d.ts +2 -0
- package/dist/bin/hightjs.js +35 -0
- package/dist/builder.d.ts +32 -0
- package/dist/builder.js +341 -0
- package/dist/client/DefaultNotFound.d.ts +1 -0
- package/dist/client/DefaultNotFound.js +53 -0
- package/dist/client/ErrorBoundary.d.ts +16 -0
- package/dist/client/ErrorBoundary.js +181 -0
- package/dist/client/clientRouter.d.ts +58 -0
- package/dist/client/clientRouter.js +116 -0
- package/dist/client/entry.client.d.ts +1 -0
- package/dist/client/entry.client.js +271 -0
- package/dist/client/routerContext.d.ts +26 -0
- package/dist/client/routerContext.js +62 -0
- package/dist/client.d.ts +3 -0
- package/dist/client.js +8 -0
- package/dist/components/Link.d.ts +7 -0
- package/dist/components/Link.js +13 -0
- package/dist/eslint/index.d.ts +32 -0
- package/dist/eslint/index.js +15 -0
- package/dist/eslint/use-client-rule.d.ts +19 -0
- package/dist/eslint/use-client-rule.js +99 -0
- package/dist/eslintSetup.d.ts +0 -0
- package/dist/eslintSetup.js +1 -0
- package/dist/example/src/web/routes/index.d.ts +3 -0
- package/dist/example/src/web/routes/index.js +15 -0
- package/dist/helpers.d.ts +18 -0
- package/dist/helpers.js +318 -0
- package/dist/hotReload.d.ts +23 -0
- package/dist/hotReload.js +292 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +480 -0
- package/dist/renderer.d.ts +14 -0
- package/dist/renderer.js +106 -0
- package/dist/router.d.ts +78 -0
- package/dist/router.js +359 -0
- package/dist/types/framework.d.ts +37 -0
- package/dist/types/framework.js +2 -0
- package/dist/types.d.ts +43 -0
- package/dist/types.js +2 -0
- package/dist/typescript/use-client-plugin.d.ts +5 -0
- package/dist/typescript/use-client-plugin.js +113 -0
- package/dist/validation.d.ts +0 -0
- package/dist/validation.js +1 -0
- package/package.json +72 -0
- package/src/adapters/express.ts +70 -0
- package/src/adapters/factory.ts +96 -0
- package/src/adapters/fastify.ts +88 -0
- package/src/adapters/native.ts +223 -0
- package/src/api/console.ts +285 -0
- package/src/api/http.ts +515 -0
- package/src/auth/client.ts +74 -0
- package/src/auth/components.tsx +109 -0
- package/src/auth/core.ts +143 -0
- package/src/auth/index.ts +9 -0
- package/src/auth/jwt.ts +194 -0
- package/src/auth/providers.ts +13 -0
- package/src/auth/react/index.ts +9 -0
- package/src/auth/react.tsx +209 -0
- package/src/auth/routes.ts +133 -0
- package/src/auth/types.ts +73 -0
- package/src/bin/hightjs.js +40 -0
- package/src/builder.js +362 -0
- package/src/client/DefaultNotFound.tsx +68 -0
- package/src/client/clientRouter.ts +137 -0
- package/src/client/entry.client.tsx +302 -0
- package/src/client.ts +8 -0
- package/src/components/Link.tsx +22 -0
- package/src/helpers.ts +316 -0
- package/src/hotReload.ts +289 -0
- package/src/index.ts +514 -0
- package/src/renderer.tsx +122 -0
- package/src/router.ts +400 -0
- package/src/types/framework.ts +42 -0
- package/src/types.ts +54 -0
- package/tsconfig.json +17 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.app = exports.FrameworkAdapterFactory = exports.FastifyAdapter = exports.ExpressAdapter = exports.HightJSResponse = exports.HightJSRequest = void 0;
|
|
40
|
+
exports.default = hweb;
|
|
41
|
+
const path_1 = __importDefault(require("path"));
|
|
42
|
+
const fs_1 = __importDefault(require("fs"));
|
|
43
|
+
const express_1 = require("./adapters/express");
|
|
44
|
+
const builder_1 = require("./builder");
|
|
45
|
+
const router_1 = require("./router");
|
|
46
|
+
const renderer_1 = require("./renderer");
|
|
47
|
+
const http_1 = require("./api/http");
|
|
48
|
+
Object.defineProperty(exports, "HightJSRequest", { enumerable: true, get: function () { return http_1.HightJSRequest; } });
|
|
49
|
+
Object.defineProperty(exports, "HightJSResponse", { enumerable: true, get: function () { return http_1.HightJSResponse; } });
|
|
50
|
+
const hotReload_1 = require("./hotReload");
|
|
51
|
+
const factory_1 = require("./adapters/factory");
|
|
52
|
+
const console_1 = __importStar(require("./api/console"));
|
|
53
|
+
// Exporta os adapters para uso manual se necessário
|
|
54
|
+
var express_2 = require("./adapters/express");
|
|
55
|
+
Object.defineProperty(exports, "ExpressAdapter", { enumerable: true, get: function () { return express_2.ExpressAdapter; } });
|
|
56
|
+
var fastify_1 = require("./adapters/fastify");
|
|
57
|
+
Object.defineProperty(exports, "FastifyAdapter", { enumerable: true, get: function () { return fastify_1.FastifyAdapter; } });
|
|
58
|
+
var factory_2 = require("./adapters/factory");
|
|
59
|
+
Object.defineProperty(exports, "FrameworkAdapterFactory", { enumerable: true, get: function () { return factory_2.FrameworkAdapterFactory; } });
|
|
60
|
+
// Exporta os helpers para facilitar integração
|
|
61
|
+
var helpers_1 = require("./helpers");
|
|
62
|
+
Object.defineProperty(exports, "app", { enumerable: true, get: function () { return helpers_1.app; } });
|
|
63
|
+
// Exporta o sistema de autenticação
|
|
64
|
+
// Função para verificar se o projeto é grande o suficiente para se beneficiar de chunks
|
|
65
|
+
function isLargeProject(projectDir) {
|
|
66
|
+
try {
|
|
67
|
+
const srcDir = path_1.default.join(projectDir, 'src');
|
|
68
|
+
if (!fs_1.default.existsSync(srcDir))
|
|
69
|
+
return false;
|
|
70
|
+
let totalFiles = 0;
|
|
71
|
+
let totalSize = 0;
|
|
72
|
+
function scanDirectory(dir) {
|
|
73
|
+
const items = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
74
|
+
for (const item of items) {
|
|
75
|
+
const fullPath = path_1.default.join(dir, item.name);
|
|
76
|
+
if (item.isDirectory() && item.name !== 'node_modules' && item.name !== '.git') {
|
|
77
|
+
scanDirectory(fullPath);
|
|
78
|
+
}
|
|
79
|
+
else if (item.isFile() && /\.(tsx?|jsx?|css|scss|less)$/i.test(item.name)) {
|
|
80
|
+
totalFiles++;
|
|
81
|
+
totalSize += fs_1.default.statSync(fullPath).size;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
scanDirectory(srcDir);
|
|
86
|
+
// Considera projeto grande se:
|
|
87
|
+
// - Mais de 20 arquivos de frontend/style
|
|
88
|
+
// - Ou tamanho total > 500KB
|
|
89
|
+
return totalFiles > 20 || totalSize > 500 * 1024;
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
// Em caso de erro, assume que não é um projeto grande
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Função para gerar o arquivo de entrada para o esbuild
|
|
97
|
+
function createEntryFile(projectDir, routes) {
|
|
98
|
+
const tempDir = path_1.default.join(projectDir, '.hweb');
|
|
99
|
+
fs_1.default.mkdirSync(tempDir, { recursive: true });
|
|
100
|
+
const entryFilePath = path_1.default.join(tempDir, 'entry.client.js');
|
|
101
|
+
// Verifica se há layout
|
|
102
|
+
const layout = (0, router_1.getLayout)();
|
|
103
|
+
// Verifica se há notFound personalizado
|
|
104
|
+
const notFound = (0, router_1.getNotFound)();
|
|
105
|
+
// Gera imports dinâmicos para cada componente
|
|
106
|
+
const imports = routes
|
|
107
|
+
.map((route, index) => {
|
|
108
|
+
const relativePath = path_1.default.relative(tempDir, route.componentPath).replace(/\\/g, '/');
|
|
109
|
+
return `import route${index} from '${relativePath}';`;
|
|
110
|
+
})
|
|
111
|
+
.join('\n');
|
|
112
|
+
// Import do layout se existir
|
|
113
|
+
const layoutImport = layout
|
|
114
|
+
? `import LayoutComponent from '${path_1.default.relative(tempDir, layout.componentPath).replace(/\\/g, '/')}';`
|
|
115
|
+
: '';
|
|
116
|
+
// Import do notFound se existir
|
|
117
|
+
const notFoundImport = notFound
|
|
118
|
+
? `import NotFoundComponent from '${path_1.default.relative(tempDir, notFound.componentPath).replace(/\\/g, '/')}';`
|
|
119
|
+
: '';
|
|
120
|
+
// Registra os componentes no window para o cliente acessar
|
|
121
|
+
const componentRegistration = routes
|
|
122
|
+
.map((route, index) => ` '${route.componentPath}': route${index}.component || route${index}.default?.component,`)
|
|
123
|
+
.join('\n');
|
|
124
|
+
// Registra o layout se existir
|
|
125
|
+
const layoutRegistration = layout
|
|
126
|
+
? `window.__HWEB_LAYOUT__ = LayoutComponent.default || LayoutComponent;`
|
|
127
|
+
: `window.__HWEB_LAYOUT__ = null;`;
|
|
128
|
+
// Registra o notFound se existir
|
|
129
|
+
const notFoundRegistration = notFound
|
|
130
|
+
? `window.__HWEB_NOT_FOUND__ = NotFoundComponent.default || NotFoundComponent;`
|
|
131
|
+
: `window.__HWEB_NOT_FOUND__ = null;`;
|
|
132
|
+
// Caminho correto para o entry.client.tsx
|
|
133
|
+
const sdkDir = path_1.default.dirname(__dirname); // Vai para a pasta pai de src (onde está o hweb-sdk)
|
|
134
|
+
const entryClientPath = path_1.default.join(sdkDir, 'src', 'client', 'entry.client.tsx');
|
|
135
|
+
const relativeEntryPath = path_1.default.relative(tempDir, entryClientPath).replace(/\\/g, '/');
|
|
136
|
+
// Import do DefaultNotFound do SDK
|
|
137
|
+
const defaultNotFoundPath = path_1.default.join(sdkDir, 'src', 'client', 'DefaultNotFound.tsx');
|
|
138
|
+
const relativeDefaultNotFoundPath = path_1.default.relative(tempDir, defaultNotFoundPath).replace(/\\/g, '/');
|
|
139
|
+
const entryContent = `// Arquivo gerado automaticamente pelo hweb
|
|
140
|
+
${imports}
|
|
141
|
+
${layoutImport}
|
|
142
|
+
${notFoundImport}
|
|
143
|
+
import DefaultNotFound from '${relativeDefaultNotFoundPath}';
|
|
144
|
+
|
|
145
|
+
// Registra os componentes para o cliente
|
|
146
|
+
window.__HWEB_COMPONENTS__ = {
|
|
147
|
+
${componentRegistration}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Registra o layout se existir
|
|
151
|
+
${layoutRegistration}
|
|
152
|
+
|
|
153
|
+
// Registra o notFound se existir
|
|
154
|
+
${notFoundRegistration}
|
|
155
|
+
|
|
156
|
+
// Registra o DefaultNotFound do hweb
|
|
157
|
+
window.__HWEB_DEFAULT_NOT_FOUND__ = DefaultNotFound;
|
|
158
|
+
|
|
159
|
+
// Importa e executa o entry.client.tsx
|
|
160
|
+
import '${relativeEntryPath}';
|
|
161
|
+
`;
|
|
162
|
+
fs_1.default.writeFileSync(entryFilePath, entryContent);
|
|
163
|
+
return entryFilePath;
|
|
164
|
+
}
|
|
165
|
+
function hweb(options) {
|
|
166
|
+
const { dev = true, dir = process.cwd(), port = 3000 } = options;
|
|
167
|
+
const userWebDir = path_1.default.join(dir, 'src', 'web');
|
|
168
|
+
const userWebRoutesDir = path_1.default.join(userWebDir, 'routes');
|
|
169
|
+
const userBackendRoutesDir = path_1.default.join(userWebDir, 'backend', 'routes');
|
|
170
|
+
/**
|
|
171
|
+
* Executa middlewares sequencialmente e depois o handler final
|
|
172
|
+
* @param middlewares Array de middlewares para executar
|
|
173
|
+
* @param finalHandler Handler final da rota
|
|
174
|
+
* @param request Requisição do HightJS
|
|
175
|
+
* @param params Parâmetros da rota
|
|
176
|
+
* @returns Resposta do middleware ou handler final
|
|
177
|
+
*/
|
|
178
|
+
async function executeMiddlewareChain(middlewares, finalHandler, request, params) {
|
|
179
|
+
if (!middlewares || middlewares.length === 0) {
|
|
180
|
+
// Não há middlewares, executa diretamente o handler final
|
|
181
|
+
return await finalHandler(request, params);
|
|
182
|
+
}
|
|
183
|
+
let currentIndex = 0;
|
|
184
|
+
// Função next que será chamada pelos middlewares
|
|
185
|
+
const next = async () => {
|
|
186
|
+
if (currentIndex < middlewares.length) {
|
|
187
|
+
// Ainda há middlewares para executar
|
|
188
|
+
const currentMiddleware = middlewares[currentIndex];
|
|
189
|
+
currentIndex++;
|
|
190
|
+
return await currentMiddleware(request, params, next);
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
// Todos os middlewares foram executados, chama o handler final
|
|
194
|
+
return await finalHandler(request, params);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
// Inicia a cadeia de execução
|
|
198
|
+
return await next();
|
|
199
|
+
}
|
|
200
|
+
let frontendRoutes = [];
|
|
201
|
+
let hotReloadManager = null;
|
|
202
|
+
let entryPoint;
|
|
203
|
+
let outfile;
|
|
204
|
+
// Função para regenerar o entry file
|
|
205
|
+
const regenerateEntryFile = () => {
|
|
206
|
+
// Recarrega todas as rotas e componentes
|
|
207
|
+
frontendRoutes = (0, router_1.loadRoutes)(userWebRoutesDir);
|
|
208
|
+
(0, router_1.loadLayout)(userWebDir);
|
|
209
|
+
(0, router_1.loadNotFound)(userWebDir);
|
|
210
|
+
// Regenera o entry file
|
|
211
|
+
entryPoint = createEntryFile(dir, frontendRoutes);
|
|
212
|
+
};
|
|
213
|
+
const app = {
|
|
214
|
+
prepare: async () => {
|
|
215
|
+
const isProduction = !dev;
|
|
216
|
+
if (!isProduction) {
|
|
217
|
+
// Inicia hot reload apenas em desenvolvimento (sem logs)
|
|
218
|
+
hotReloadManager = new hotReload_1.HotReloadManager(dir);
|
|
219
|
+
await hotReloadManager.start();
|
|
220
|
+
// Adiciona callback para recarregar rotas de backend quando mudarem
|
|
221
|
+
hotReloadManager.onBackendApiChange(() => {
|
|
222
|
+
(0, router_1.loadBackendRoutes)(userBackendRoutesDir);
|
|
223
|
+
});
|
|
224
|
+
// Adiciona callback para regenerar entry file quando frontend mudar
|
|
225
|
+
hotReloadManager.onFrontendChange(() => {
|
|
226
|
+
regenerateEntryFile();
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
// ORDEM IMPORTANTE: Carrega TUDO antes de criar o arquivo de entrada
|
|
230
|
+
frontendRoutes = (0, router_1.loadRoutes)(userWebRoutesDir);
|
|
231
|
+
(0, router_1.loadBackendRoutes)(userBackendRoutesDir);
|
|
232
|
+
// Carrega layout.tsx ANTES de criar o entry file
|
|
233
|
+
const layout = (0, router_1.loadLayout)(userWebDir);
|
|
234
|
+
const notFound = (0, router_1.loadNotFound)(userWebDir);
|
|
235
|
+
const outDir = path_1.default.join(dir, 'hweb-dist');
|
|
236
|
+
fs_1.default.mkdirSync(outDir, { recursive: true });
|
|
237
|
+
entryPoint = createEntryFile(dir, frontendRoutes);
|
|
238
|
+
// Usa chunks quando há muitas rotas ou o projeto é grande
|
|
239
|
+
const shouldUseChunks = frontendRoutes.length > 5 || isLargeProject(dir);
|
|
240
|
+
if (shouldUseChunks) {
|
|
241
|
+
const outDir = path_1.default.join(dir, 'hweb-dist');
|
|
242
|
+
if (isProduction) {
|
|
243
|
+
await (0, builder_1.buildWithChunks)(entryPoint, outDir, isProduction);
|
|
244
|
+
console_1.default.info(`✅ Build com chunks finalizado! ${frontendRoutes.length} rotas processadas.`);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
(0, builder_1.watchWithChunks)(entryPoint, outDir, hotReloadManager).catch(err => {
|
|
248
|
+
console_1.default.error(`Erro ao iniciar o watch com chunks`, err);
|
|
249
|
+
});
|
|
250
|
+
console_1.default.info(`🚀 Modo watch com chunks ativo para ${frontendRoutes.length} rotas.`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
outfile = path_1.default.join(dir, 'hweb-dist', 'main.js');
|
|
255
|
+
if (isProduction) {
|
|
256
|
+
await (0, builder_1.build)(entryPoint, outfile, isProduction);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
(0, builder_1.watch)(entryPoint, outfile, hotReloadManager).catch(err => {
|
|
260
|
+
console_1.default.error(`Erro ao iniciar o watch`, err);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
executeInstrumentation: () => {
|
|
266
|
+
// verificar se dir/src/instrumentation.(tsx/jsx/js/ts) existe com regex
|
|
267
|
+
const instrumentationFile = fs_1.default.readdirSync(path_1.default.join(dir, 'src')).find(file => /^hightweb\.(tsx|jsx|js|ts)$/.test(file));
|
|
268
|
+
if (instrumentationFile) {
|
|
269
|
+
const instrumentationPath = path_1.default.join(dir, 'src', instrumentationFile);
|
|
270
|
+
// dar require, e executar a função principal do arquivo
|
|
271
|
+
const instrumentation = require(instrumentationPath);
|
|
272
|
+
if (typeof instrumentation === 'function') {
|
|
273
|
+
instrumentation();
|
|
274
|
+
}
|
|
275
|
+
else if (typeof instrumentation.default === 'function') {
|
|
276
|
+
instrumentation.default();
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
console_1.default.warn(`O arquivo de instrumentação ${instrumentationFile} não exporta uma função padrão.`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
getRequestHandler: () => {
|
|
284
|
+
return async (req, res) => {
|
|
285
|
+
// Detecta automaticamente o framework e cria o adapter apropriado
|
|
286
|
+
const adapter = factory_1.FrameworkAdapterFactory.detectFramework(req, res);
|
|
287
|
+
const genericReq = adapter.parseRequest(req);
|
|
288
|
+
const genericRes = adapter.createResponse(res);
|
|
289
|
+
// Adiciona informações do hweb na requisição genérica
|
|
290
|
+
genericReq.hwebDev = dev;
|
|
291
|
+
genericReq.hotReloadManager = hotReloadManager;
|
|
292
|
+
const { pathname } = new URL(genericReq.url, `http://${genericReq.headers.host || 'localhost'}`);
|
|
293
|
+
const method = genericReq.method.toUpperCase();
|
|
294
|
+
// 1. Verifica se é WebSocket upgrade para hot reload
|
|
295
|
+
if (pathname === '/hweb-hotreload/' && genericReq.headers.upgrade === 'websocket' && hotReloadManager) {
|
|
296
|
+
// Framework vai chamar o evento 'upgrade' do servidor HTTP
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
// 2. Primeiro verifica se é um arquivo estático da pasta public
|
|
300
|
+
if (pathname !== '/' && !pathname.startsWith('/api/') && !pathname.startsWith('/hweb-')) {
|
|
301
|
+
const publicDir = path_1.default.join(dir, 'public');
|
|
302
|
+
const filePath = path_1.default.join(publicDir, pathname);
|
|
303
|
+
if (fs_1.default.existsSync(filePath) && fs_1.default.statSync(filePath).isFile()) {
|
|
304
|
+
const ext = path_1.default.extname(filePath).toLowerCase();
|
|
305
|
+
const contentTypes = {
|
|
306
|
+
'.html': 'text/html',
|
|
307
|
+
'.css': 'text/css',
|
|
308
|
+
'.js': 'application/javascript',
|
|
309
|
+
'.json': 'application/json',
|
|
310
|
+
'.png': 'image/png',
|
|
311
|
+
'.jpg': 'image/jpeg',
|
|
312
|
+
'.jpeg': 'image/jpeg',
|
|
313
|
+
'.gif': 'image/gif',
|
|
314
|
+
'.svg': 'image/svg+xml',
|
|
315
|
+
'.ico': 'image/x-icon',
|
|
316
|
+
'.webp': 'image/webp',
|
|
317
|
+
'.mp4': 'video/mp4',
|
|
318
|
+
'.webm': 'video/webm',
|
|
319
|
+
'.mp3': 'audio/mpeg',
|
|
320
|
+
'.wav': 'audio/wav',
|
|
321
|
+
'.pdf': 'application/pdf',
|
|
322
|
+
'.txt': 'text/plain',
|
|
323
|
+
'.xml': 'application/xml',
|
|
324
|
+
'.zip': 'application/zip'
|
|
325
|
+
};
|
|
326
|
+
genericRes.header('Content-Type', contentTypes[ext] || 'application/octet-stream');
|
|
327
|
+
// Para arquivos estáticos, usamos o método nativo do framework
|
|
328
|
+
if (adapter.type === 'express') {
|
|
329
|
+
res.sendFile(filePath);
|
|
330
|
+
}
|
|
331
|
+
else if (adapter.type === 'fastify') {
|
|
332
|
+
const fileContent = fs_1.default.readFileSync(filePath);
|
|
333
|
+
genericRes.send(fileContent);
|
|
334
|
+
}
|
|
335
|
+
else if (adapter.type === 'native') {
|
|
336
|
+
const fileContent = fs_1.default.readFileSync(filePath);
|
|
337
|
+
genericRes.send(fileContent);
|
|
338
|
+
}
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// 3. Verifica se é um arquivo estático do hweb-dist
|
|
343
|
+
if (pathname.startsWith('/hweb-dist/')) {
|
|
344
|
+
const staticPath = path_1.default.join(dir, 'hweb-dist');
|
|
345
|
+
const filePath = path_1.default.join(staticPath, pathname.replace('/hweb-dist/', ''));
|
|
346
|
+
if (fs_1.default.existsSync(filePath)) {
|
|
347
|
+
const ext = path_1.default.extname(filePath).toLowerCase();
|
|
348
|
+
const contentTypes = {
|
|
349
|
+
'.js': 'application/javascript',
|
|
350
|
+
'.css': 'text/css',
|
|
351
|
+
'.map': 'application/json'
|
|
352
|
+
};
|
|
353
|
+
genericRes.header('Content-Type', contentTypes[ext] || 'text/plain');
|
|
354
|
+
// Para arquivos estáticos, usamos o método nativo do framework
|
|
355
|
+
if (adapter.type === 'express') {
|
|
356
|
+
res.sendFile(filePath);
|
|
357
|
+
}
|
|
358
|
+
else if (adapter.type === 'fastify') {
|
|
359
|
+
const fileContent = fs_1.default.readFileSync(filePath);
|
|
360
|
+
genericRes.send(fileContent);
|
|
361
|
+
}
|
|
362
|
+
else if (adapter.type === 'native') {
|
|
363
|
+
const fileContent = fs_1.default.readFileSync(filePath);
|
|
364
|
+
genericRes.send(fileContent);
|
|
365
|
+
}
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
// 4. REMOVIDO: Verificação de arquivos React UMD - não precisamos mais
|
|
370
|
+
// O React agora será bundlado diretamente no main.js
|
|
371
|
+
// 5. Verifica se é uma rota de API (backend)
|
|
372
|
+
const backendMatch = (0, router_1.findMatchingBackendRoute)(pathname, method);
|
|
373
|
+
if (backendMatch) {
|
|
374
|
+
try {
|
|
375
|
+
const handler = backendMatch.route[method];
|
|
376
|
+
if (handler) {
|
|
377
|
+
const hwebReq = new http_1.HightJSRequest(genericReq);
|
|
378
|
+
// Executa middlewares e depois o handler final
|
|
379
|
+
const hwebRes = await executeMiddlewareChain(backendMatch.route.middleware, handler, hwebReq, backendMatch.params);
|
|
380
|
+
// Aplica a resposta usando o adapter correto
|
|
381
|
+
hwebRes._applyTo(genericRes);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
console_1.default.error(`Erro na rota de API ${pathname}:`, error);
|
|
387
|
+
genericRes.status(500).text('Erro interno do servidor na API');
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
// 6. Por último, tenta renderizar uma página (frontend) ou 404
|
|
392
|
+
const pageMatch = (0, router_1.findMatchingRoute)(pathname);
|
|
393
|
+
if (!pageMatch) {
|
|
394
|
+
// Em vez de enviar texto simples, renderiza a página 404 React
|
|
395
|
+
try {
|
|
396
|
+
// Cria uma "rota falsa" para a página 404
|
|
397
|
+
const notFoundRoute = {
|
|
398
|
+
pattern: '/__404__',
|
|
399
|
+
component: () => null, // Componente vazio, será tratado no cliente
|
|
400
|
+
componentPath: '__404__'
|
|
401
|
+
};
|
|
402
|
+
const html = await (0, renderer_1.render)({
|
|
403
|
+
req: genericReq,
|
|
404
|
+
route: notFoundRoute,
|
|
405
|
+
params: {},
|
|
406
|
+
allRoutes: frontendRoutes
|
|
407
|
+
});
|
|
408
|
+
genericRes.status(404).header('Content-Type', 'text/html').send(html);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
console_1.default.error(`Erro ao renderizar página 404:`, error);
|
|
413
|
+
genericRes.status(404).text('Página não encontrada');
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
try {
|
|
418
|
+
const html = await (0, renderer_1.render)({
|
|
419
|
+
req: genericReq,
|
|
420
|
+
route: pageMatch.route,
|
|
421
|
+
params: pageMatch.params,
|
|
422
|
+
allRoutes: frontendRoutes
|
|
423
|
+
});
|
|
424
|
+
genericRes.status(200).header('Content-Type', 'text/html').send(html);
|
|
425
|
+
}
|
|
426
|
+
catch (error) {
|
|
427
|
+
console_1.default.error(`Erro ao renderizar a página ${pathname}:`, error);
|
|
428
|
+
genericRes.status(500).text('Erro interno do servidor');
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
},
|
|
432
|
+
// Método para configurar WebSocket upgrade nos servidores Express e Fastify
|
|
433
|
+
setupWebSocket: (server) => {
|
|
434
|
+
if (hotReloadManager) {
|
|
435
|
+
// Detecta se é um servidor Express ou Fastify
|
|
436
|
+
const isExpressServer = factory_1.FrameworkAdapterFactory.getCurrentAdapter() instanceof express_1.ExpressAdapter;
|
|
437
|
+
if (isExpressServer) {
|
|
438
|
+
server.on('upgrade', (request, socket, head) => {
|
|
439
|
+
const { pathname } = new URL(request.url, `http://${request.headers.host}`);
|
|
440
|
+
if (pathname === '/hweb-hotreload/') {
|
|
441
|
+
hotReloadManager.handleUpgrade(request, socket, head);
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
socket.destroy();
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
// Fastify usa um approach diferente para WebSockets
|
|
450
|
+
const actualServer = server.server || server;
|
|
451
|
+
actualServer.on('upgrade', (request, socket, head) => {
|
|
452
|
+
const { pathname } = new URL(request.url, `http://${request.headers.host}`);
|
|
453
|
+
if (pathname === '/hweb-hotreload/') {
|
|
454
|
+
hotReloadManager.handleUpgrade(request, socket, head);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
socket.destroy();
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
build: async () => {
|
|
464
|
+
const msg = console_1.default.dynamicLine(` ${console_1.Colors.FgYellow}● ${console_1.Colors.Reset}Iniciando build do cliente para produção`);
|
|
465
|
+
const outDir = path_1.default.join(dir, 'hweb-dist');
|
|
466
|
+
fs_1.default.mkdirSync(outDir, { recursive: true });
|
|
467
|
+
const routes = (0, router_1.loadRoutes)(userWebRoutesDir);
|
|
468
|
+
const entryPoint = createEntryFile(dir, routes);
|
|
469
|
+
const outfile = path_1.default.join(outDir, 'main.js');
|
|
470
|
+
await (0, builder_1.build)(entryPoint, outfile, true); // Força produção no build manual
|
|
471
|
+
msg.end(` ${console_1.Colors.FgGreen}● ${console_1.Colors.Reset}Build do cliente concluído: ${outfile}`);
|
|
472
|
+
},
|
|
473
|
+
stop: () => {
|
|
474
|
+
if (hotReloadManager) {
|
|
475
|
+
hotReloadManager.stop();
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
return app;
|
|
480
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { RouteConfig } from './types';
|
|
2
|
+
import type { GenericRequest } from './types/framework';
|
|
3
|
+
interface RenderOptions {
|
|
4
|
+
req: GenericRequest;
|
|
5
|
+
route: RouteConfig & {
|
|
6
|
+
componentPath: string;
|
|
7
|
+
};
|
|
8
|
+
params: Record<string, string>;
|
|
9
|
+
allRoutes: (RouteConfig & {
|
|
10
|
+
componentPath: string;
|
|
11
|
+
})[];
|
|
12
|
+
}
|
|
13
|
+
export declare function render({ req, route, params, allRoutes }: RenderOptions): Promise<string>;
|
|
14
|
+
export {};
|
package/dist/renderer.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.render = render;
|
|
7
|
+
const router_1 = require("./router");
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
// Funções para codificar/decodificar dados (disfarça o JSON no HTML)
|
|
11
|
+
function encodeInitialData(data) {
|
|
12
|
+
// Converte para JSON, depois para base64, e adiciona um prefixo fake
|
|
13
|
+
const jsonStr = JSON.stringify(data);
|
|
14
|
+
const base64 = Buffer.from(jsonStr).toString('base64');
|
|
15
|
+
return `hweb_${base64}_config`;
|
|
16
|
+
}
|
|
17
|
+
function createDecodeScript() {
|
|
18
|
+
return `
|
|
19
|
+
const process = { env: { NODE_ENV: '${process.env.NODE_ENV || 'development'}' } };
|
|
20
|
+
window.__HWEB_DECODE__ = function(encoded) { const base64 = encoded.replace('hweb_', '').replace('_config', ''); const jsonStr = atob(base64); return JSON.parse(jsonStr); };
|
|
21
|
+
`;
|
|
22
|
+
}
|
|
23
|
+
async function render({ req, route, params, allRoutes }) {
|
|
24
|
+
const { generateMetadata } = route;
|
|
25
|
+
// Pega a opção dev e hot reload manager do req
|
|
26
|
+
const isProduction = !req.hwebDev;
|
|
27
|
+
const hotReloadManager = req.hotReloadManager;
|
|
28
|
+
// Pega o layout se existir
|
|
29
|
+
const layout = (0, router_1.getLayout)();
|
|
30
|
+
let metadata = { title: 'App hweb' };
|
|
31
|
+
// Primeiro usa o metadata do layout se existir
|
|
32
|
+
if (layout && layout.metadata) {
|
|
33
|
+
metadata = { ...metadata, ...layout.metadata };
|
|
34
|
+
}
|
|
35
|
+
// Depois sobrescreve com metadata específico da rota se existir
|
|
36
|
+
if (generateMetadata) {
|
|
37
|
+
const routeMetadata = await Promise.resolve(generateMetadata(params, req));
|
|
38
|
+
metadata = { ...metadata, ...routeMetadata };
|
|
39
|
+
}
|
|
40
|
+
// Prepara os dados para injetar na janela do navegador
|
|
41
|
+
const initialData = {
|
|
42
|
+
routes: allRoutes.map(r => ({ pattern: r.pattern, componentPath: r.componentPath })),
|
|
43
|
+
initialComponentPath: route.componentPath,
|
|
44
|
+
initialParams: params,
|
|
45
|
+
};
|
|
46
|
+
// Codifica os dados para disfarçar
|
|
47
|
+
const encodedData = encodeInitialData(initialData);
|
|
48
|
+
// Script de hot reload apenas em desenvolvimento
|
|
49
|
+
const hotReloadScript = !isProduction && hotReloadManager
|
|
50
|
+
? hotReloadManager.getClientScript()
|
|
51
|
+
: '';
|
|
52
|
+
const favicon = metadata.favicon ? `<link rel="icon" href="${metadata.favicon}">` : '';
|
|
53
|
+
// Determina quais arquivos JavaScript carregar
|
|
54
|
+
const jsFiles = getJavaScriptFiles(req);
|
|
55
|
+
// HTML base sem SSR - apenas o container e scripts para client-side rendering
|
|
56
|
+
return `<!DOCTYPE html><html lang="pt-BR"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>${metadata.title || 'App hweb'}</title>${metadata.description ? `<meta name="description" content="${metadata.description}">` : ''}${favicon}</head><body><div id="root"></div><script>${createDecodeScript()}window.__HWEB_INITIAL_DATA__ = window.__HWEB_DECODE__('${encodedData}');</script>${jsFiles}${hotReloadScript}</body></html>`;
|
|
57
|
+
}
|
|
58
|
+
// Função para determinar quais arquivos JavaScript carregar
|
|
59
|
+
function getJavaScriptFiles(req) {
|
|
60
|
+
const projectDir = process.cwd();
|
|
61
|
+
const distDir = path_1.default.join(projectDir, 'hweb-dist');
|
|
62
|
+
try {
|
|
63
|
+
// Verifica se existe um manifesto de chunks (gerado pelo ESBuild com splitting)
|
|
64
|
+
const manifestPath = path_1.default.join(distDir, 'manifest.json');
|
|
65
|
+
if (fs_1.default.existsSync(manifestPath)) {
|
|
66
|
+
// Modo chunks - carrega todos os arquivos necessários
|
|
67
|
+
const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
|
|
68
|
+
const scripts = Object.values(manifest)
|
|
69
|
+
.filter((file) => file.endsWith('.js'))
|
|
70
|
+
.map((file) => `<script src="/hweb-dist/${file}"></script>`)
|
|
71
|
+
.join('');
|
|
72
|
+
return scripts;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// Verifica se existem múltiplos arquivos JS (chunks sem manifesto)
|
|
76
|
+
const jsFiles = fs_1.default.readdirSync(distDir)
|
|
77
|
+
.filter(file => file.endsWith('.js') && !file.endsWith('.map'))
|
|
78
|
+
.sort((a, b) => {
|
|
79
|
+
// Ordena para carregar arquivos principais primeiro
|
|
80
|
+
if (a.includes('main'))
|
|
81
|
+
return -1;
|
|
82
|
+
if (b.includes('main'))
|
|
83
|
+
return 1;
|
|
84
|
+
if (a.includes('vendor') || a.includes('react'))
|
|
85
|
+
return -1;
|
|
86
|
+
if (b.includes('vendor') || b.includes('react'))
|
|
87
|
+
return 1;
|
|
88
|
+
return a.localeCompare(b);
|
|
89
|
+
});
|
|
90
|
+
if (jsFiles.length > 1) {
|
|
91
|
+
// Modo chunks sem manifesto
|
|
92
|
+
return jsFiles
|
|
93
|
+
.map(file => `<script src="/hweb-dist/${file}"></script>`)
|
|
94
|
+
.join('');
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// Modo tradicional - único arquivo
|
|
98
|
+
return '<script src="/hweb-dist/main.js"></script>';
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
// Fallback para o modo tradicional
|
|
104
|
+
return '<script src="/hweb-dist/main.js"></script>';
|
|
105
|
+
}
|
|
106
|
+
}
|