vatts 1.0.5-alpha.3 → 1.1.0-alpha.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/dist/client/entry.client.js +1 -0
- package/dist/helpers.d.ts +2 -1
- package/dist/helpers.js +3 -0
- package/dist/index.js +15 -6
- package/dist/router.d.ts +3 -0
- package/dist/router.js +97 -7
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
|
@@ -292,6 +292,7 @@ function initializeClient() {
|
|
|
292
292
|
const root = (0, client_1.createRoot)(container);
|
|
293
293
|
// Salva a referência globalmente
|
|
294
294
|
window.__VATTS_ROOT__ = root;
|
|
295
|
+
console.log(componentMap, initialData);
|
|
295
296
|
root.render((0, jsx_runtime_1.jsx)(App, { componentMap: componentMap, routes: initialData.routes, initialComponentPath: initialData.initialComponentPath, initialParams: initialData.initialParams, layoutComponent: window.__VATTS_LAYOUT__ }));
|
|
296
297
|
}
|
|
297
298
|
catch (error) {
|
package/dist/helpers.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import http, { Server } from 'http';
|
|
3
|
-
import type { VattsOptions } from './types';
|
|
3
|
+
import type { VattsOptions, VattsConfig } from './types';
|
|
4
4
|
import https from 'https';
|
|
5
|
+
export declare let config: VattsConfig | undefined;
|
|
5
6
|
export declare function app(options?: VattsOptions): {
|
|
6
7
|
/**
|
|
7
8
|
* Integra com uma aplicação de qualquer framework (Express, Fastify, etc)
|
package/dist/helpers.js
CHANGED
|
@@ -53,6 +53,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
53
53
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
54
54
|
};
|
|
55
55
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
|
+
exports.config = void 0;
|
|
56
57
|
exports.app = app;
|
|
57
58
|
// Imports Nativos do Node.js (movidos para o topo)
|
|
58
59
|
const http_1 = __importDefault(require("http"));
|
|
@@ -133,6 +134,7 @@ async function loadVattsConfig(projectDir, phase) {
|
|
|
133
134
|
maxUrlLength: 2048,
|
|
134
135
|
accessLogging: true,
|
|
135
136
|
envFiles: [],
|
|
137
|
+
pathRouter: false
|
|
136
138
|
};
|
|
137
139
|
try {
|
|
138
140
|
// Tenta primeiro .ts, depois .js
|
|
@@ -321,6 +323,7 @@ async function initNativeServer(vattsApp, options, port, hostname) {
|
|
|
321
323
|
const projectDir = options.dir || process.cwd();
|
|
322
324
|
const phase = options.dev ? 'development' : 'production';
|
|
323
325
|
const vattsConfig = await loadVattsConfig(projectDir, phase);
|
|
326
|
+
exports.config = vattsConfig;
|
|
324
327
|
// Passa envFiles da config para as opções do vatts
|
|
325
328
|
options.envFiles = vattsConfig.envFiles;
|
|
326
329
|
await vattsApp.prepare();
|
package/dist/index.js
CHANGED
|
@@ -70,6 +70,7 @@ const env_1 = require("./env/env");
|
|
|
70
70
|
// RPC
|
|
71
71
|
const server_1 = require("./rpc/server");
|
|
72
72
|
const types_1 = require("./rpc/types");
|
|
73
|
+
const helpers_1 = require("./helpers");
|
|
73
74
|
// Helpers de segurança para servir arquivos estáticos sem path traversal
|
|
74
75
|
function isSuspiciousPathname(p) {
|
|
75
76
|
try {
|
|
@@ -97,8 +98,8 @@ Object.defineProperty(exports, "FastifyAdapter", { enumerable: true, get: functi
|
|
|
97
98
|
var factory_2 = require("./adapters/factory");
|
|
98
99
|
Object.defineProperty(exports, "FrameworkAdapterFactory", { enumerable: true, get: function () { return factory_2.FrameworkAdapterFactory; } });
|
|
99
100
|
// Exporta os helpers para facilitar integração
|
|
100
|
-
var
|
|
101
|
-
Object.defineProperty(exports, "app", { enumerable: true, get: function () { return
|
|
101
|
+
var helpers_2 = require("./helpers");
|
|
102
|
+
Object.defineProperty(exports, "app", { enumerable: true, get: function () { return helpers_2.app; } });
|
|
102
103
|
// Função para verificar se o projeto é grande o suficiente para se beneficiar de chunks
|
|
103
104
|
function isLargeProject(projectDir) {
|
|
104
105
|
try {
|
|
@@ -147,9 +148,17 @@ function createEntryFile(projectDir, routes) {
|
|
|
147
148
|
const notFoundImport = notFound
|
|
148
149
|
? `import NotFoundComponent from '${path_1.default.relative(tempDir, notFound.componentPath).replace(/\\/g, '/')}';`
|
|
149
150
|
: '';
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
let componentRegistration;
|
|
152
|
+
if (helpers_1.config?.pathRouter === true) {
|
|
153
|
+
componentRegistration = routes
|
|
154
|
+
.map((route, index) => ` '${route.componentPath}': route${index} || route${index}.default,`)
|
|
155
|
+
.join('\n');
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
componentRegistration = routes
|
|
159
|
+
.map((route, index) => ` '${route.componentPath}': route${index}.component || route${index}.default?.component,`)
|
|
160
|
+
.join('\n');
|
|
161
|
+
}
|
|
153
162
|
const layoutRegistration = layout
|
|
154
163
|
? `window.__VATTS_LAYOUT__ = LayoutComponent.default || LayoutComponent;`
|
|
155
164
|
: `window.__VATTS_LAYOUT__ = null;`;
|
|
@@ -294,7 +303,7 @@ function vatts(options) {
|
|
|
294
303
|
}
|
|
295
304
|
},
|
|
296
305
|
executeInstrumentation: () => {
|
|
297
|
-
const instrumentationFile = fs_1.default.readdirSync(path_1.default.join(dir, 'src')).find(file => /^vattsweb\.(
|
|
306
|
+
const instrumentationFile = fs_1.default.readdirSync(path_1.default.join(dir, 'src')).find(file => /^vattsweb\.(js|ts)$/.test(file));
|
|
298
307
|
if (instrumentationFile) {
|
|
299
308
|
const instrumentationPath = path_1.default.join(dir, 'src', instrumentationFile);
|
|
300
309
|
const instrumentation = require(instrumentationPath);
|
package/dist/router.d.ts
CHANGED
|
@@ -9,6 +9,9 @@ export declare function getLayout(): {
|
|
|
9
9
|
componentPath: string;
|
|
10
10
|
metadata?: any;
|
|
11
11
|
} | null;
|
|
12
|
+
export declare function loadPathRoutes(routesDir: string): (RouteConfig & {
|
|
13
|
+
componentPath: string;
|
|
14
|
+
})[];
|
|
12
15
|
export declare function loadRoutes(routesDir: string): (RouteConfig & {
|
|
13
16
|
componentPath: string;
|
|
14
17
|
})[];
|
package/dist/router.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.clearAllRouteCache = clearAllRouteCache;
|
|
|
7
7
|
exports.clearFileCache = clearFileCache;
|
|
8
8
|
exports.loadLayout = loadLayout;
|
|
9
9
|
exports.getLayout = getLayout;
|
|
10
|
+
exports.loadPathRoutes = loadPathRoutes;
|
|
10
11
|
exports.loadRoutes = loadRoutes;
|
|
11
12
|
exports.findMatchingRoute = findMatchingRoute;
|
|
12
13
|
exports.loadBackendRoutes = loadBackendRoutes;
|
|
@@ -39,6 +40,7 @@ const url_1 = require("url");
|
|
|
39
40
|
const console_1 = __importDefault(require("./api/console"));
|
|
40
41
|
const factory_1 = require("./adapters/factory");
|
|
41
42
|
const http_1 = require("./api/http");
|
|
43
|
+
const helpers_1 = require("./helpers");
|
|
42
44
|
// --- Estado Global ---
|
|
43
45
|
let allRoutes = [];
|
|
44
46
|
let allBackendRoutes = [];
|
|
@@ -125,7 +127,7 @@ function requireWithoutStyles(modulePath) {
|
|
|
125
127
|
}
|
|
126
128
|
// --- Carregamento de Layout (Otimizado - Sem I/O de Disco) ---
|
|
127
129
|
function loadLayout(webDir) {
|
|
128
|
-
const extensions = ['layout.tsx', 'layout.
|
|
130
|
+
const extensions = ['layout.tsx', 'layout.jsx'];
|
|
129
131
|
let layoutFile = null;
|
|
130
132
|
for (const ext of extensions) {
|
|
131
133
|
const fullPath = path_1.default.join(webDir, ext);
|
|
@@ -162,9 +164,98 @@ function loadLayout(webDir) {
|
|
|
162
164
|
}
|
|
163
165
|
function getLayout() { return layoutComponent; }
|
|
164
166
|
// --- Carregamento de Rotas Frontend ---
|
|
167
|
+
// Helper para converter o caminho do arquivo no padrão de URL (Next.js style)
|
|
168
|
+
function convertPathToRoutePattern(absolutePath, routesDir) {
|
|
169
|
+
// 1. Pega o caminho relativo e normaliza as barras
|
|
170
|
+
let relPath = path_1.default.relative(routesDir, absolutePath).replace(/\\/g, '/');
|
|
171
|
+
// 2. Remove o nome do arquivo (page.tsx, page.ts, page.jsx ou page.js) do final
|
|
172
|
+
relPath = relPath.replace(/\/?page\.(?:ts|js)x?$/, '');
|
|
173
|
+
// 3. Remove os "Route Groups" do Next.js, ex: (auth)/login vira /login
|
|
174
|
+
relPath = relPath.replace(/\/\([^)]+\)/g, '').replace(/^\([^)]+\)\/?/, '');
|
|
175
|
+
// 4. Se a string ficou vazia, é a rota raiz
|
|
176
|
+
if (!relPath)
|
|
177
|
+
return '/';
|
|
178
|
+
// 5. Garante que comece com "/"
|
|
179
|
+
return '/' + relPath;
|
|
180
|
+
}
|
|
181
|
+
function loadPathRoutes(routesDir) {
|
|
182
|
+
if (!fs_1.default.existsSync(routesDir)) {
|
|
183
|
+
allRoutes = [];
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
const loaded = [];
|
|
187
|
+
const cwdPath = process.cwd();
|
|
188
|
+
const scanAndLoad = (dir) => {
|
|
189
|
+
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
190
|
+
for (const entry of entries) {
|
|
191
|
+
const name = entry.name;
|
|
192
|
+
// Ignora arquivos/pastas ocultas, de sistema ou de componentes (ex: _components)
|
|
193
|
+
if (name.startsWith('.') || name.startsWith('_'))
|
|
194
|
+
continue;
|
|
195
|
+
const fullPath = path_1.default.join(dir, name);
|
|
196
|
+
if (entry.isDirectory()) {
|
|
197
|
+
if (name === 'backend' || name === 'api')
|
|
198
|
+
continue;
|
|
199
|
+
scanAndLoad(fullPath);
|
|
200
|
+
}
|
|
201
|
+
else if (entry.isFile() && (name === 'page.tsx' || name === 'page.ts' || name === 'page.jsx' || name === 'page.js')) {
|
|
202
|
+
// SÓ carrega se for um arquivo "page"
|
|
203
|
+
try {
|
|
204
|
+
const absolutePath = path_1.default.resolve(fullPath);
|
|
205
|
+
// OTIMIZAÇÃO: Limpa cache apenas se já existia
|
|
206
|
+
if (loadedFiles.has(absolutePath)) {
|
|
207
|
+
safeClearCache(absolutePath);
|
|
208
|
+
}
|
|
209
|
+
// Importa o módulo ignorando estilos
|
|
210
|
+
const routeModule = requireWithoutStyles(absolutePath);
|
|
211
|
+
// O "default" agora é o Componente React em si
|
|
212
|
+
const PageComponent = routeModule.default;
|
|
213
|
+
if (PageComponent) {
|
|
214
|
+
const componentPath = path_1.default.relative(cwdPath, fullPath).replace(/\\/g, '/');
|
|
215
|
+
// Gera o pattern baseado na pasta (ex: /blog/[id])
|
|
216
|
+
const pattern = convertPathToRoutePattern(absolutePath, routesDir);
|
|
217
|
+
// Monta o config dinamicamente
|
|
218
|
+
const generatedConfig = {
|
|
219
|
+
pattern,
|
|
220
|
+
component: PageComponent,
|
|
221
|
+
generateMetadata: routeModule.generateMetadata || (() => ({})),
|
|
222
|
+
};
|
|
223
|
+
// OTIMIZAÇÃO: Pré-compila a regex
|
|
224
|
+
const regex = compileRoutePatternWithGroups(pattern);
|
|
225
|
+
loaded.push({
|
|
226
|
+
config: generatedConfig,
|
|
227
|
+
componentPath,
|
|
228
|
+
regex,
|
|
229
|
+
paramNames: [] // Seus named groups cuidam disso
|
|
230
|
+
});
|
|
231
|
+
loadedFiles.add(absolutePath);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
console_1.default.error(`Error loading page ${fullPath}:`, error);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
scanAndLoad(routesDir);
|
|
241
|
+
// Ordena as rotas para que rotas estáticas tenham prioridade sobre rotas dinâmicas [id]
|
|
242
|
+
loaded.sort((a, b) => {
|
|
243
|
+
const aDynamic = a.config.pattern.includes('[');
|
|
244
|
+
const bDynamic = b.config.pattern.includes('[');
|
|
245
|
+
if (aDynamic && !bDynamic)
|
|
246
|
+
return 1;
|
|
247
|
+
if (!aDynamic && bDynamic)
|
|
248
|
+
return -1;
|
|
249
|
+
return b.config.pattern.length - a.config.pattern.length; // Mais específicas primeiro
|
|
250
|
+
});
|
|
251
|
+
allRoutes = loaded;
|
|
252
|
+
return allRoutes.map(r => ({ ...r.config, componentPath: r.componentPath }));
|
|
253
|
+
}
|
|
165
254
|
function loadRoutes(routesDir) {
|
|
255
|
+
if (helpers_1.config?.pathRouter == true) {
|
|
256
|
+
return loadPathRoutes(path_1.default.join(routesDir, "../"));
|
|
257
|
+
}
|
|
166
258
|
if (!fs_1.default.existsSync(routesDir)) {
|
|
167
|
-
console_1.default.warn(`Frontend routes directory not found at ${routesDir}.`);
|
|
168
259
|
allRoutes = [];
|
|
169
260
|
return [];
|
|
170
261
|
}
|
|
@@ -184,7 +275,7 @@ function loadRoutes(routesDir) {
|
|
|
184
275
|
continue;
|
|
185
276
|
scanAndLoad(fullPath);
|
|
186
277
|
}
|
|
187
|
-
else if (entry.isFile() && (name.endsWith('.tsx') || name.endsWith('.ts'))) {
|
|
278
|
+
else if (entry.isFile() && (name.endsWith('.tsx') || name.endsWith('.ts') || name.endsWith(".jsx") || name.endsWith(".js"))) {
|
|
188
279
|
try {
|
|
189
280
|
const absolutePath = path_1.default.resolve(fullPath);
|
|
190
281
|
// OTIMIZAÇÃO CRÍTICA: Só limpa cache se o arquivo já foi carregado antes.
|
|
@@ -239,7 +330,7 @@ const middlewareCache = new Map();
|
|
|
239
330
|
function getMiddlewaresForDir(dir) {
|
|
240
331
|
if (middlewareCache.has(dir))
|
|
241
332
|
return middlewareCache.get(dir);
|
|
242
|
-
const files = ['middleware.ts', 'middleware.
|
|
333
|
+
const files = ['middleware.ts', 'middleware.js'];
|
|
243
334
|
let middlewares = [];
|
|
244
335
|
for (const file of files) {
|
|
245
336
|
const fullPath = path_1.default.join(dir, file);
|
|
@@ -293,7 +384,7 @@ function loadBackendRoutes(backendRoutesDir) {
|
|
|
293
384
|
if (entry.isDirectory()) {
|
|
294
385
|
scanAndLoadAPI(fullPath);
|
|
295
386
|
}
|
|
296
|
-
else if (entry.isFile() && (name.endsWith('.ts') || name.endsWith(
|
|
387
|
+
else if (entry.isFile() && (name.endsWith('.ts') || name.endsWith(".js"))) {
|
|
297
388
|
if (name.startsWith('middleware'))
|
|
298
389
|
continue;
|
|
299
390
|
try {
|
|
@@ -334,7 +425,6 @@ function loadBackendRoutes(backendRoutesDir) {
|
|
|
334
425
|
function findMatchingBackendRoute(pathname, method) {
|
|
335
426
|
const methodUpper = method.toUpperCase();
|
|
336
427
|
for (const route of allBackendRoutes) {
|
|
337
|
-
// Verifica método antes de rodar regex (otimização barata)
|
|
338
428
|
// @ts-ignore
|
|
339
429
|
if (!route.config[methodUpper])
|
|
340
430
|
continue;
|
|
@@ -350,7 +440,7 @@ function findMatchingBackendRoute(pathname, method) {
|
|
|
350
440
|
}
|
|
351
441
|
// --- 404 Not Found ---
|
|
352
442
|
function loadNotFound(webDir) {
|
|
353
|
-
const files = ['notFound.tsx', 'notFound.
|
|
443
|
+
const files = ['notFound.tsx', 'notFound.jsx'];
|
|
354
444
|
for (const file of files) {
|
|
355
445
|
const fullPath = path_1.default.join(webDir, file);
|
|
356
446
|
if (fs_1.default.existsSync(fullPath)) {
|
package/dist/types.d.ts
CHANGED
|
@@ -33,6 +33,11 @@ export interface VattsOptions {
|
|
|
33
33
|
* Essas configurações podem ser definidas no arquivo vatts.config.js
|
|
34
34
|
*/
|
|
35
35
|
export interface VattsConfig {
|
|
36
|
+
/**
|
|
37
|
+
* Prefere utilizar rotas por path, sem precisar registrar?
|
|
38
|
+
* Padrão: false
|
|
39
|
+
*/
|
|
40
|
+
pathRouter?: boolean;
|
|
36
41
|
/**
|
|
37
42
|
* Limita o número máximo de headers HTTP permitidos por requisição.
|
|
38
43
|
* Padrão: 100
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vatts",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0-alpha.1",
|
|
4
4
|
"description": "Vatts.js is a high-level framework for building web applications with ease and speed. It provides a robust set of tools and features to streamline development and enhance productivity.",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"author": "itsmuzin",
|