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/src/router.ts
ADDED
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { RouteConfig, BackendRouteConfig, HightMiddleware } from './types';
|
|
4
|
+
import Console from "./api/console"
|
|
5
|
+
|
|
6
|
+
// --- Roteamento do Frontend ---
|
|
7
|
+
|
|
8
|
+
// Guarda todas as rotas de PÁGINA (React) encontradas
|
|
9
|
+
// A rota agora também armazena o caminho do arquivo para ser usado como um ID único no cliente
|
|
10
|
+
let allRoutes: (RouteConfig & { componentPath: string })[] = [];
|
|
11
|
+
|
|
12
|
+
// Guarda o layout se existir
|
|
13
|
+
let layoutComponent: { componentPath: string; metadata?: any } | null = null;
|
|
14
|
+
|
|
15
|
+
// Guarda o componente 404 personalizado se existir
|
|
16
|
+
let notFoundComponent: { componentPath: string } | null = null;
|
|
17
|
+
|
|
18
|
+
// Cache de arquivos carregados para limpeza
|
|
19
|
+
let loadedRouteFiles: Set<string> = new Set();
|
|
20
|
+
let loadedLayoutFiles: Set<string> = new Set();
|
|
21
|
+
let loadedNotFoundFiles: Set<string> = new Set();
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Limpa o cache do require para um arquivo específico
|
|
25
|
+
* @param filePath Caminho do arquivo para limpar
|
|
26
|
+
*/
|
|
27
|
+
function clearRequireCache(filePath: string) {
|
|
28
|
+
try {
|
|
29
|
+
const resolvedPath = require.resolve(filePath);
|
|
30
|
+
delete require.cache[resolvedPath];
|
|
31
|
+
|
|
32
|
+
// Também limpa arquivos temporários relacionados
|
|
33
|
+
const tempFile = filePath.replace(/\.(tsx|ts)$/, '.temp.$1');
|
|
34
|
+
try {
|
|
35
|
+
const tempResolvedPath = require.resolve(tempFile);
|
|
36
|
+
delete require.cache[tempResolvedPath];
|
|
37
|
+
} catch {
|
|
38
|
+
// Arquivo temporário pode não existir
|
|
39
|
+
}
|
|
40
|
+
} catch (error) {
|
|
41
|
+
// Arquivo pode não estar no cache
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Limpa todo o cache de rotas carregadas
|
|
47
|
+
*/
|
|
48
|
+
export function clearAllRouteCache() {
|
|
49
|
+
// Limpa cache das rotas
|
|
50
|
+
loadedRouteFiles.forEach(filePath => {
|
|
51
|
+
clearRequireCache(filePath);
|
|
52
|
+
});
|
|
53
|
+
loadedRouteFiles.clear();
|
|
54
|
+
|
|
55
|
+
// Limpa cache do layout
|
|
56
|
+
loadedLayoutFiles.forEach(filePath => {
|
|
57
|
+
clearRequireCache(filePath);
|
|
58
|
+
});
|
|
59
|
+
loadedLayoutFiles.clear();
|
|
60
|
+
|
|
61
|
+
// Limpa cache do notFound
|
|
62
|
+
loadedNotFoundFiles.forEach(filePath => {
|
|
63
|
+
clearRequireCache(filePath);
|
|
64
|
+
});
|
|
65
|
+
loadedNotFoundFiles.clear();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Limpa o cache de um arquivo específico e recarrega as rotas se necessário
|
|
70
|
+
* @param changedFilePath Caminho do arquivo que foi alterado
|
|
71
|
+
*/
|
|
72
|
+
export function clearFileCache(changedFilePath: string) {
|
|
73
|
+
const absolutePath = path.isAbsolute(changedFilePath) ? changedFilePath : path.resolve(changedFilePath);
|
|
74
|
+
|
|
75
|
+
// Limpa o cache do arquivo específico
|
|
76
|
+
clearRequireCache(absolutePath);
|
|
77
|
+
|
|
78
|
+
// Remove das listas de arquivos carregados
|
|
79
|
+
loadedRouteFiles.delete(absolutePath);
|
|
80
|
+
loadedLayoutFiles.delete(absolutePath);
|
|
81
|
+
loadedNotFoundFiles.delete(absolutePath);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Carrega o layout.tsx se existir no diretório web
|
|
86
|
+
* @param webDir O diretório web onde procurar o layout
|
|
87
|
+
* @returns O layout carregado ou null se não existir
|
|
88
|
+
*/
|
|
89
|
+
export function loadLayout(webDir: string): { componentPath: string; metadata?: any } | null {
|
|
90
|
+
const layoutPath = path.join(webDir, 'layout.tsx');
|
|
91
|
+
const layoutPathJs = path.join(webDir, 'layout.ts');
|
|
92
|
+
|
|
93
|
+
const layoutFile = fs.existsSync(layoutPath) ? layoutPath :
|
|
94
|
+
fs.existsSync(layoutPathJs) ? layoutPathJs : null;
|
|
95
|
+
|
|
96
|
+
if (layoutFile) {
|
|
97
|
+
const componentPath = path.relative(process.cwd(), layoutFile).replace(/\\/g, '/');
|
|
98
|
+
const absolutePath = path.resolve(layoutFile);
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
// Limpa o cache antes de recarregar
|
|
102
|
+
clearRequireCache(absolutePath);
|
|
103
|
+
|
|
104
|
+
// HACK: Cria uma versão temporária do layout SEM imports de CSS para carregar no servidor
|
|
105
|
+
const layoutContent = fs.readFileSync(layoutFile, 'utf8');
|
|
106
|
+
const tempContent = layoutContent
|
|
107
|
+
.replace(/import\s+['"][^'"]*\.css['"];?/g, '// CSS import removido para servidor')
|
|
108
|
+
.replace(/import\s+['"][^'"]*\.scss['"];?/g, '// SCSS import removido para servidor')
|
|
109
|
+
.replace(/import\s+['"][^'"]*\.sass['"];?/g, '// SASS import removido para servidor');
|
|
110
|
+
|
|
111
|
+
const tempFile = layoutFile.replace(/\.(tsx|ts)$/, '.temp.$1');
|
|
112
|
+
fs.writeFileSync(tempFile, tempContent);
|
|
113
|
+
|
|
114
|
+
// Carrega o arquivo temporário sem CSS
|
|
115
|
+
delete require.cache[require.resolve(tempFile)];
|
|
116
|
+
const layoutModule = require(tempFile);
|
|
117
|
+
|
|
118
|
+
// Remove o arquivo temporário
|
|
119
|
+
fs.unlinkSync(tempFile);
|
|
120
|
+
|
|
121
|
+
const metadata = layoutModule.metadata || null;
|
|
122
|
+
|
|
123
|
+
// Registra o arquivo como carregado
|
|
124
|
+
loadedLayoutFiles.add(absolutePath);
|
|
125
|
+
|
|
126
|
+
layoutComponent = { componentPath, metadata };
|
|
127
|
+
return layoutComponent;
|
|
128
|
+
} catch (error) {
|
|
129
|
+
Console.error(`Erro ao carregar layout ${layoutFile}:`, error);
|
|
130
|
+
layoutComponent = { componentPath };
|
|
131
|
+
return layoutComponent;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
layoutComponent = null;
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Retorna o layout atual se carregado
|
|
141
|
+
*/
|
|
142
|
+
export function getLayout(): { componentPath: string; metadata?: any } | null {
|
|
143
|
+
return layoutComponent;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Carrega dinamicamente todas as rotas de frontend do diretório do usuário.
|
|
148
|
+
* @param routesDir O diretório onde as rotas de página estão localizadas.
|
|
149
|
+
* @returns A lista de rotas de página que foram carregadas.
|
|
150
|
+
*/
|
|
151
|
+
export function loadRoutes(routesDir: string): (RouteConfig & { componentPath: string })[] {
|
|
152
|
+
if (!fs.existsSync(routesDir)) {
|
|
153
|
+
Console.warn(`Diretório de rotas de frontend não encontrado em ${routesDir}. Nenhuma página será carregada.`);
|
|
154
|
+
allRoutes = [];
|
|
155
|
+
return allRoutes;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const files = fs.readdirSync(routesDir, { recursive: true, encoding: 'utf-8' });
|
|
159
|
+
// Corrigindo o filtro para excluir corretamente o diretório backend
|
|
160
|
+
const routeFiles = files.filter(file => {
|
|
161
|
+
const isTypeScriptFile = file.endsWith('.ts') || file.endsWith('.tsx');
|
|
162
|
+
const isNotBackend = !file.includes('backend' + path.sep) && !file.includes('backend/');
|
|
163
|
+
return isTypeScriptFile && isNotBackend;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const loaded: (RouteConfig & { componentPath: string })[] = [];
|
|
167
|
+
for (const file of routeFiles) {
|
|
168
|
+
const filePath = path.join(routesDir, file);
|
|
169
|
+
const absolutePath = path.resolve(filePath);
|
|
170
|
+
// Usamos um caminho relativo ao CWD como um ID estável para o componente.
|
|
171
|
+
const componentPath = path.relative(process.cwd(), filePath).replace(/\\/g, '/');
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
// Limpa o cache antes de recarregar para pegar alterações nos metadados
|
|
175
|
+
clearRequireCache(absolutePath);
|
|
176
|
+
|
|
177
|
+
const routeModule = require(filePath);
|
|
178
|
+
if (routeModule.default && routeModule.default.pattern && routeModule.default.component) {
|
|
179
|
+
loaded.push({ ...routeModule.default, componentPath });
|
|
180
|
+
|
|
181
|
+
// Registra o arquivo como carregado
|
|
182
|
+
loadedRouteFiles.add(absolutePath);
|
|
183
|
+
}
|
|
184
|
+
} catch (error) {
|
|
185
|
+
Console.error(`Erro ao carregar a rota de página ${filePath}:`, error);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
allRoutes = loaded;
|
|
189
|
+
return allRoutes;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Encontra a rota de página correspondente para uma URL.
|
|
194
|
+
* @param pathname O caminho da URL (ex: "/users/123").
|
|
195
|
+
* @returns Um objeto com a rota e os parâmetros, ou null se não encontrar.
|
|
196
|
+
*/
|
|
197
|
+
export function findMatchingRoute(pathname: string) {
|
|
198
|
+
for (const route of allRoutes) {
|
|
199
|
+
if (!route.pattern) continue;
|
|
200
|
+
|
|
201
|
+
// Converte o padrão da rota (ex: /users/[id]) em uma RegExp
|
|
202
|
+
const regexPattern = route.pattern.replace(/\[(\w+)\]/g, '(?<$1>[^/]+)');
|
|
203
|
+
const regex = new RegExp(`^${regexPattern}/?$`);
|
|
204
|
+
const match = pathname.match(regex);
|
|
205
|
+
|
|
206
|
+
if (match) {
|
|
207
|
+
return {
|
|
208
|
+
route,
|
|
209
|
+
params: match.groups || {}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// --- Roteamento do Backend ---
|
|
217
|
+
|
|
218
|
+
// Guarda todas as rotas de API encontradas
|
|
219
|
+
let allBackendRoutes: BackendRouteConfig[] = [];
|
|
220
|
+
|
|
221
|
+
// Cache de middlewares carregados por diretório
|
|
222
|
+
let loadedMiddlewares: Map<string, HightMiddleware[]> = new Map();
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Carrega middlewares de um diretório específico
|
|
226
|
+
* @param dir O diretório onde procurar por middleware.ts
|
|
227
|
+
* @returns Array de middlewares encontrados
|
|
228
|
+
*/
|
|
229
|
+
function loadMiddlewareFromDirectory(dir: string): HightMiddleware[] {
|
|
230
|
+
const middlewares: HightMiddleware[] = [];
|
|
231
|
+
|
|
232
|
+
// Procura por middleware.ts ou middleware.tsx
|
|
233
|
+
const middlewarePath = path.join(dir, 'middleware.ts');
|
|
234
|
+
const middlewarePathTsx = path.join(dir, 'middleware.tsx');
|
|
235
|
+
|
|
236
|
+
const middlewareFile = fs.existsSync(middlewarePath) ? middlewarePath :
|
|
237
|
+
fs.existsSync(middlewarePathTsx) ? middlewarePathTsx : null;
|
|
238
|
+
|
|
239
|
+
if (middlewareFile) {
|
|
240
|
+
try {
|
|
241
|
+
const absolutePath = path.resolve(middlewareFile);
|
|
242
|
+
clearRequireCache(absolutePath);
|
|
243
|
+
|
|
244
|
+
const middlewareModule = require(middlewareFile);
|
|
245
|
+
|
|
246
|
+
// Suporte para export default (função única) ou export { middleware1, middleware2 }
|
|
247
|
+
if (typeof middlewareModule.default === 'function') {
|
|
248
|
+
middlewares.push(middlewareModule.default);
|
|
249
|
+
} else if (middlewareModule.default && Array.isArray(middlewareModule.default)) {
|
|
250
|
+
middlewares.push(...middlewareModule.default);
|
|
251
|
+
} else {
|
|
252
|
+
// Procura por exports nomeados que sejam funções
|
|
253
|
+
Object.keys(middlewareModule).forEach(key => {
|
|
254
|
+
if (key !== 'default' && typeof middlewareModule[key] === 'function') {
|
|
255
|
+
middlewares.push(middlewareModule[key]);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
} catch (error) {
|
|
261
|
+
Console.error(`Erro ao carregar middleware ${middlewareFile}:`, error);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return middlewares;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Coleta middlewares do diretório específico da rota (não herda dos pais)
|
|
270
|
+
* @param routeFilePath Caminho completo do arquivo de rota
|
|
271
|
+
* @param backendRoutesDir Diretório raiz das rotas de backend
|
|
272
|
+
* @returns Array com middlewares apenas do diretório da rota
|
|
273
|
+
*/
|
|
274
|
+
function collectMiddlewaresForRoute(routeFilePath: string, backendRoutesDir: string): HightMiddleware[] {
|
|
275
|
+
const relativePath = path.relative(backendRoutesDir, routeFilePath);
|
|
276
|
+
const routeDir = path.dirname(path.join(backendRoutesDir, relativePath));
|
|
277
|
+
|
|
278
|
+
// Carrega middlewares APENAS do diretório específico da rota (não herda dos pais)
|
|
279
|
+
if (!loadedMiddlewares.has(routeDir)) {
|
|
280
|
+
const middlewares = loadMiddlewareFromDirectory(routeDir);
|
|
281
|
+
loadedMiddlewares.set(routeDir, middlewares);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return loadedMiddlewares.get(routeDir) || [];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Carrega dinamicamente todas as rotas de API do diretório de backend.
|
|
289
|
+
* @param backendRoutesDir O diretório onde as rotas de API estão localizadas.
|
|
290
|
+
*/
|
|
291
|
+
export function loadBackendRoutes(backendRoutesDir: string) {
|
|
292
|
+
if (!fs.existsSync(backendRoutesDir)) {
|
|
293
|
+
// É opcional ter uma API, então não mostramos um aviso se a pasta não existir.
|
|
294
|
+
allBackendRoutes = [];
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Limpa cache de middlewares para recarregar
|
|
299
|
+
loadedMiddlewares.clear();
|
|
300
|
+
|
|
301
|
+
const files = fs.readdirSync(backendRoutesDir, { recursive: true, encoding: 'utf-8' });
|
|
302
|
+
const routeFiles = files.filter(file => {
|
|
303
|
+
const isTypeScript = file.endsWith('.ts') || file.endsWith('.tsx');
|
|
304
|
+
const isNotMiddleware = !path.basename(file).startsWith('middleware');
|
|
305
|
+
return isTypeScript && isNotMiddleware;
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const loaded: BackendRouteConfig[] = [];
|
|
309
|
+
for (const file of routeFiles) {
|
|
310
|
+
const filePath = path.join(backendRoutesDir, file);
|
|
311
|
+
try {
|
|
312
|
+
const routeModule = require(filePath);
|
|
313
|
+
if (routeModule.default && routeModule.default.pattern) {
|
|
314
|
+
const routeConfig = { ...routeModule.default };
|
|
315
|
+
|
|
316
|
+
// Se a rota NÃO tem a propriedade middleware definida, carrega os da pasta
|
|
317
|
+
if (!routeConfig.hasOwnProperty('middleware')) {
|
|
318
|
+
const folderMiddlewares = collectMiddlewaresForRoute(filePath, backendRoutesDir);
|
|
319
|
+
if (folderMiddlewares.length > 0) {
|
|
320
|
+
routeConfig.middleware = folderMiddlewares;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// Se tem middleware definido (mesmo que seja []), usa só esses (não adiciona os da pasta)
|
|
324
|
+
|
|
325
|
+
loaded.push(routeConfig);
|
|
326
|
+
}
|
|
327
|
+
} catch (error) {
|
|
328
|
+
Console.error(`Erro ao carregar a rota de API ${filePath}:`, error);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
allBackendRoutes = loaded;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Encontra a rota de API correspondente para uma URL e método HTTP.
|
|
336
|
+
* @param pathname O caminho da URL (ex: "/api/users/123").
|
|
337
|
+
* @param method O método HTTP da requisição (GET, POST, etc.).
|
|
338
|
+
* @returns Um objeto com a rota e os parâmetros, ou null se não encontrar.
|
|
339
|
+
*/
|
|
340
|
+
export function findMatchingBackendRoute(pathname: string, method: string) {
|
|
341
|
+
for (const route of allBackendRoutes) {
|
|
342
|
+
// Verifica se a rota tem um handler para o método HTTP atual
|
|
343
|
+
if (!route.pattern || !route[method.toUpperCase() as keyof BackendRouteConfig]) continue;
|
|
344
|
+
|
|
345
|
+
const regexPattern = route.pattern.replace(/\[(\w+)\]/g, '(?<$1>[^/]+)');
|
|
346
|
+
const regex = new RegExp(`^${regexPattern}/?$`);
|
|
347
|
+
const match = pathname.match(regex);
|
|
348
|
+
|
|
349
|
+
if (match) {
|
|
350
|
+
return {
|
|
351
|
+
route,
|
|
352
|
+
params: match.groups || {}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Carrega o notFound.tsx se existir no diretório web
|
|
361
|
+
* @param webDir O diretório web onde procurar o notFound
|
|
362
|
+
* @returns O notFound carregado ou null se não existir
|
|
363
|
+
*/
|
|
364
|
+
export function loadNotFound(webDir: string): { componentPath: string } | null {
|
|
365
|
+
const notFoundPath = path.join(webDir, 'notFound.tsx');
|
|
366
|
+
const notFoundPathJs = path.join(webDir, 'notFound.ts');
|
|
367
|
+
|
|
368
|
+
const notFoundFile = fs.existsSync(notFoundPath) ? notFoundPath :
|
|
369
|
+
fs.existsSync(notFoundPathJs) ? notFoundPathJs : null;
|
|
370
|
+
|
|
371
|
+
if (notFoundFile) {
|
|
372
|
+
const componentPath = path.relative(process.cwd(), notFoundFile).replace(/\\/g, '/');
|
|
373
|
+
const absolutePath = path.resolve(notFoundFile);
|
|
374
|
+
|
|
375
|
+
try {
|
|
376
|
+
// Limpa o cache antes de recarregar
|
|
377
|
+
clearRequireCache(absolutePath);
|
|
378
|
+
|
|
379
|
+
// Registra o arquivo como carregado
|
|
380
|
+
loadedNotFoundFiles.add(absolutePath);
|
|
381
|
+
|
|
382
|
+
notFoundComponent = { componentPath };
|
|
383
|
+
return notFoundComponent;
|
|
384
|
+
} catch (error) {
|
|
385
|
+
Console.error(`Erro ao carregar notFound ${notFoundFile}:`, error);
|
|
386
|
+
notFoundComponent = { componentPath };
|
|
387
|
+
return notFoundComponent;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
notFoundComponent = null;
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Retorna o componente 404 atual se carregado
|
|
397
|
+
*/
|
|
398
|
+
export function getNotFound(): { componentPath: string } | null {
|
|
399
|
+
return notFoundComponent;
|
|
400
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Tipos genéricos para abstrair diferentes frameworks web
|
|
2
|
+
export interface GenericRequest {
|
|
3
|
+
method: string;
|
|
4
|
+
url: string;
|
|
5
|
+
headers: Record<string, string | string[]>;
|
|
6
|
+
body?: any;
|
|
7
|
+
query?: Record<string, any>;
|
|
8
|
+
params?: Record<string, string>;
|
|
9
|
+
cookies?: Record<string, string>;
|
|
10
|
+
raw?: any; // Requisição original do framework
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface GenericResponse {
|
|
14
|
+
status(code: number): GenericResponse;
|
|
15
|
+
header(name: string, value: string): GenericResponse;
|
|
16
|
+
cookie(name: string, value: string, options?: CookieOptions): GenericResponse;
|
|
17
|
+
clearCookie(name: string, options?: CookieOptions): GenericResponse;
|
|
18
|
+
json(data: any): void;
|
|
19
|
+
text(data: string): void;
|
|
20
|
+
send(data: any): void;
|
|
21
|
+
redirect(url: string): void;
|
|
22
|
+
raw?: any; // Resposta original do framework
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface CookieOptions {
|
|
26
|
+
domain?: string;
|
|
27
|
+
expires?: Date;
|
|
28
|
+
httpOnly?: boolean;
|
|
29
|
+
maxAge?: number;
|
|
30
|
+
path?: string;
|
|
31
|
+
secure?: boolean;
|
|
32
|
+
signed?: boolean;
|
|
33
|
+
sameSite?: boolean | 'lax' | 'strict' | 'none';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type FrameworkType = 'express' | 'fastify' | 'native';
|
|
37
|
+
|
|
38
|
+
export interface FrameworkAdapter {
|
|
39
|
+
type: FrameworkType;
|
|
40
|
+
parseRequest(req: any): GenericRequest;
|
|
41
|
+
createResponse(res: any): GenericResponse;
|
|
42
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ComponentType } from 'react';
|
|
2
|
+
import type { GenericRequest } from './types/framework';
|
|
3
|
+
import {HightJSRequest, HightJSResponse} from "./api/http";
|
|
4
|
+
|
|
5
|
+
// --- Tipos do Frontend (sem alteração) ---
|
|
6
|
+
export interface HightJSOptions {
|
|
7
|
+
dev?: boolean;
|
|
8
|
+
hostname?: string;
|
|
9
|
+
port?: number;
|
|
10
|
+
dir?: string;
|
|
11
|
+
framework?: 'express' | 'fastify' | 'native';
|
|
12
|
+
}
|
|
13
|
+
export interface Metadata {
|
|
14
|
+
title?: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
favicon?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface RouteConfig {
|
|
19
|
+
pattern: string;
|
|
20
|
+
component: ComponentType<any>;
|
|
21
|
+
generateMetadata?: (params: any, req: GenericRequest) => Promise<Metadata> | Metadata;
|
|
22
|
+
}
|
|
23
|
+
export type RequestHandler = (req: any, res: any) => Promise<void>;
|
|
24
|
+
|
|
25
|
+
// --- NOVO: Tipos do Backend ---
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Define o formato de uma função que manipula uma rota da API.
|
|
29
|
+
*/
|
|
30
|
+
export type BackendHandler = (
|
|
31
|
+
request: HightJSRequest, // HWebRequest será importado onde necessário
|
|
32
|
+
params: { [key: string]: string }
|
|
33
|
+
) => Promise<HightJSResponse> | HightJSResponse; // HWebResponse será importado onde necessário
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
export type HightMiddleware = (
|
|
38
|
+
request: HightJSRequest, // HWebRequest será importado onde necessário
|
|
39
|
+
params: { [key: string]: string },
|
|
40
|
+
next: () => Promise<HightJSResponse>
|
|
41
|
+
) => Promise<HightJSResponse> | HightJSResponse; // HWebResponse será importado onde necessário
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Define a estrutura de cada rota da API, com suporte para métodos HTTP.
|
|
46
|
+
*/
|
|
47
|
+
export interface BackendRouteConfig {
|
|
48
|
+
pattern: string;
|
|
49
|
+
GET?: BackendHandler;
|
|
50
|
+
POST?: BackendHandler;
|
|
51
|
+
PUT?: BackendHandler;
|
|
52
|
+
DELETE?: BackendHandler;
|
|
53
|
+
middleware?: HightMiddleware[]; // Permite adicionar middlewares específicos à rota
|
|
54
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "CommonJS",
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"jsx": "react-jsx",
|
|
12
|
+
"moduleResolution": "node",
|
|
13
|
+
"allowJs": true,
|
|
14
|
+
},
|
|
15
|
+
"include": ["src"],
|
|
16
|
+
"exclude": ["node_modules"]
|
|
17
|
+
}
|