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