vatts 1.0.2-alpha.3 → 1.0.2-alpha.5
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/bin/vatts.js +91 -100
- package/dist/builder.js +2 -1
- package/dist/client/clientRouter.d.ts +11 -54
- package/dist/client/clientRouter.js +39 -94
- package/dist/client/rpc.d.ts +1 -1
- package/dist/client/rpc.js +19 -2
- package/dist/index.js +41 -85
- package/dist/renderer.d.ts +3 -1
- package/dist/renderer.js +156 -138
- package/package.json +1 -1
package/dist/renderer.js
CHANGED
|
@@ -3,56 +3,70 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.renderAsStream = renderAsStream;
|
|
6
7
|
exports.render = render;
|
|
8
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
9
|
+
const server_1 = require("react-dom/server");
|
|
7
10
|
const router_1 = require("./router");
|
|
8
11
|
const fs_1 = __importDefault(require("fs"));
|
|
9
12
|
const path_1 = __importDefault(require("path"));
|
|
10
|
-
//
|
|
13
|
+
// --- Helpers de Servidor ---
|
|
14
|
+
// Função auxiliar para importar módulos ignorando CSS (mesma lógica do router.ts)
|
|
15
|
+
function requireWithoutStyles(modulePath) {
|
|
16
|
+
const extensions = ['.css', '.scss', '.sass', '.less', '.png', '.jpg', '.jpeg', '.gif', '.svg'];
|
|
17
|
+
const originalHandlers = {};
|
|
18
|
+
extensions.forEach(ext => {
|
|
19
|
+
originalHandlers[ext] = require.extensions[ext];
|
|
20
|
+
require.extensions[ext] = (m, filename) => {
|
|
21
|
+
m.exports = {};
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
try {
|
|
25
|
+
const resolved = require.resolve(modulePath);
|
|
26
|
+
if (require.cache[resolved])
|
|
27
|
+
delete require.cache[resolved];
|
|
28
|
+
return require(modulePath);
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
return require(modulePath);
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
extensions.forEach(ext => {
|
|
35
|
+
if (originalHandlers[ext]) {
|
|
36
|
+
require.extensions[ext] = originalHandlers[ext];
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
delete require.extensions[ext];
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// --- Funções de Metadata e Scripts ---
|
|
11
45
|
function generateMetaTags(metadata) {
|
|
12
46
|
const tags = [];
|
|
13
|
-
// Charset
|
|
14
47
|
tags.push(`<meta charset="${metadata.charset || 'UTF-8'}">`);
|
|
15
|
-
// Viewport
|
|
16
48
|
tags.push(`<meta name="viewport" content="${metadata.viewport || 'width=device-width, initial-scale=1.0'}">`);
|
|
17
|
-
|
|
18
|
-
if (metadata.description) {
|
|
49
|
+
if (metadata.description)
|
|
19
50
|
tags.push(`<meta name="description" content="${metadata.description}">`);
|
|
20
|
-
}
|
|
21
|
-
// Keywords
|
|
22
51
|
if (metadata.keywords) {
|
|
23
|
-
const keywordsStr = Array.isArray(metadata.keywords)
|
|
24
|
-
? metadata.keywords.join(', ')
|
|
25
|
-
: metadata.keywords;
|
|
52
|
+
const keywordsStr = Array.isArray(metadata.keywords) ? metadata.keywords.join(', ') : metadata.keywords;
|
|
26
53
|
tags.push(`<meta name="keywords" content="${keywordsStr}">`);
|
|
27
54
|
}
|
|
28
|
-
|
|
29
|
-
if (metadata.author) {
|
|
55
|
+
if (metadata.author)
|
|
30
56
|
tags.push(`<meta name="author" content="${metadata.author}">`);
|
|
31
|
-
|
|
32
|
-
// Theme color
|
|
33
|
-
if (metadata.themeColor) {
|
|
57
|
+
if (metadata.themeColor)
|
|
34
58
|
tags.push(`<meta name="theme-color" content="${metadata.themeColor}">`);
|
|
35
|
-
|
|
36
|
-
// Robots
|
|
37
|
-
if (metadata.robots) {
|
|
59
|
+
if (metadata.robots)
|
|
38
60
|
tags.push(`<meta name="robots" content="${metadata.robots}">`);
|
|
39
|
-
|
|
40
|
-
// Canonical
|
|
41
|
-
if (metadata.canonical) {
|
|
61
|
+
if (metadata.canonical)
|
|
42
62
|
tags.push(`<link rel="canonical" href="${metadata.canonical}">`);
|
|
43
|
-
|
|
44
|
-
// Favicon
|
|
45
|
-
if (metadata.favicon) {
|
|
63
|
+
if (metadata.favicon)
|
|
46
64
|
tags.push(`<link rel="icon" href="${metadata.favicon}">`);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (metadata.appleTouchIcon) {
|
|
65
|
+
// Apple & Manifest
|
|
66
|
+
if (metadata.appleTouchIcon)
|
|
50
67
|
tags.push(`<link rel="apple-touch-icon" href="${metadata.appleTouchIcon}">`);
|
|
51
|
-
|
|
52
|
-
// Manifest
|
|
53
|
-
if (metadata.manifest) {
|
|
68
|
+
if (metadata.manifest)
|
|
54
69
|
tags.push(`<link rel="manifest" href="${metadata.manifest}">`);
|
|
55
|
-
}
|
|
56
70
|
// Open Graph
|
|
57
71
|
if (metadata.openGraph) {
|
|
58
72
|
const og = metadata.openGraph;
|
|
@@ -69,11 +83,9 @@ function generateMetaTags(metadata) {
|
|
|
69
83
|
if (og.locale)
|
|
70
84
|
tags.push(`<meta property="og:locale" content="${og.locale}">`);
|
|
71
85
|
if (og.image) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
else {
|
|
76
|
-
tags.push(`<meta property="og:image" content="${og.image.url}">`);
|
|
86
|
+
const imgUrl = typeof og.image === 'string' ? og.image : og.image.url;
|
|
87
|
+
tags.push(`<meta property="og:image" content="${imgUrl}">`);
|
|
88
|
+
if (typeof og.image !== 'string') {
|
|
77
89
|
if (og.image.width)
|
|
78
90
|
tags.push(`<meta property="og:image:width" content="${og.image.width}">`);
|
|
79
91
|
if (og.image.height)
|
|
@@ -101,7 +113,7 @@ function generateMetaTags(metadata) {
|
|
|
101
113
|
if (tw.imageAlt)
|
|
102
114
|
tags.push(`<meta name="twitter:image:alt" content="${tw.imageAlt}">`);
|
|
103
115
|
}
|
|
104
|
-
// Custom
|
|
116
|
+
// Custom Meta Tags
|
|
105
117
|
if (metadata.other) {
|
|
106
118
|
for (const [key, value] of Object.entries(metadata.other)) {
|
|
107
119
|
tags.push(`<meta name="${key}" content="${value}">`);
|
|
@@ -109,39 +121,83 @@ function generateMetaTags(metadata) {
|
|
|
109
121
|
}
|
|
110
122
|
return tags.join('\n');
|
|
111
123
|
}
|
|
112
|
-
// Função para ofuscar dados (não é criptografia, apenas ofuscação)
|
|
113
124
|
function obfuscateData(data) {
|
|
114
|
-
// 1. Serializa para JSON minificado
|
|
115
125
|
const jsonStr = JSON.stringify(data);
|
|
116
|
-
// 2. Converte para base64
|
|
117
126
|
const base64 = Buffer.from(jsonStr).toString('base64');
|
|
118
|
-
// 3. Adiciona um hash fake no início para parecer um token
|
|
119
127
|
const hash = Buffer.from(Date.now().toString()).toString('base64').substring(0, 8);
|
|
120
128
|
return `${hash}.${base64}`;
|
|
121
129
|
}
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
130
|
+
// Retorna as URLs dos scripts para o bootstrapModules do React
|
|
131
|
+
// Retorna null se não encontrar scripts (indica que o build ainda não terminou)
|
|
132
|
+
function getJavaScriptUrls(req) {
|
|
133
|
+
const projectDir = process.cwd();
|
|
134
|
+
const distDir = path_1.default.join(projectDir, '.vatts');
|
|
135
|
+
if (!fs_1.default.existsSync(distDir))
|
|
136
|
+
return null;
|
|
137
|
+
try {
|
|
138
|
+
const manifestPath = path_1.default.join(distDir, 'manifest.json');
|
|
139
|
+
if (fs_1.default.existsSync(manifestPath)) {
|
|
140
|
+
const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
|
|
141
|
+
const files = Object.values(manifest)
|
|
142
|
+
.filter((file) => file.endsWith('.js'))
|
|
143
|
+
.map((file) => `/_vatts/${file}`);
|
|
144
|
+
return files.length > 0 ? files : null;
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
const jsFiles = fs_1.default.readdirSync(distDir)
|
|
148
|
+
.filter(file => file.endsWith('.js') && !file.endsWith('.map'))
|
|
149
|
+
.sort((a, b) => {
|
|
150
|
+
if (a.includes('main'))
|
|
151
|
+
return -1;
|
|
152
|
+
if (b.includes('main'))
|
|
153
|
+
return 1;
|
|
154
|
+
return a.localeCompare(b);
|
|
155
|
+
});
|
|
156
|
+
return jsFiles.length > 0 ? jsFiles.map(file => `/_vatts/${file}`) : null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function ServerRoot({ lang, title, metaTagsHtml, initialDataScript, hotReloadScript, dataScript, children }) {
|
|
164
|
+
return ((0, jsx_runtime_1.jsxs)("html", { lang: lang, children: [(0, jsx_runtime_1.jsxs)("head", { children: [(0, jsx_runtime_1.jsx)("title", { children: title }), (0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: { __html: initialDataScript } }), (0, jsx_runtime_1.jsx)("span", { dangerouslySetInnerHTML: { __html: metaTagsHtml } })] }), (0, jsx_runtime_1.jsxs)("body", { children: [dataScript, (0, jsx_runtime_1.jsx)("div", { id: "root", children: children }), hotReloadScript && (0, jsx_runtime_1.jsx)("span", { dangerouslySetInnerHTML: { __html: hotReloadScript } })] })] }));
|
|
127
165
|
}
|
|
128
|
-
async function
|
|
166
|
+
async function renderAsStream({ req, res, route, params, allRoutes }) {
|
|
129
167
|
const { generateMetadata } = route;
|
|
130
|
-
// Pega a opção dev e hot reload manager do req
|
|
131
168
|
const isProduction = !req.hwebDev;
|
|
132
169
|
const hotReloadManager = req.hotReloadManager;
|
|
133
|
-
//
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
170
|
+
// 1. Verificar Build - Se não tiver scripts, retorna tela de Loading
|
|
171
|
+
const bootstrapScripts = getJavaScriptUrls(req);
|
|
172
|
+
if (!bootstrapScripts) {
|
|
173
|
+
res.setHeader('Content-Type', 'text/html');
|
|
174
|
+
// Usamos .end() diretamente para garantir que a resposta seja enviada sem stream
|
|
175
|
+
res.end(getBuildingHTML());
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
// 2. Preparar Layout
|
|
179
|
+
const layoutInfo = (0, router_1.getLayout)();
|
|
180
|
+
let LayoutComponent = null;
|
|
181
|
+
if (layoutInfo) {
|
|
182
|
+
try {
|
|
183
|
+
// Recarrega o componente de layout para ter acesso à função (o router só guarda metadata)
|
|
184
|
+
const layoutModule = requireWithoutStyles(path_1.default.resolve(process.cwd(), layoutInfo.componentPath));
|
|
185
|
+
LayoutComponent = layoutModule.default;
|
|
186
|
+
}
|
|
187
|
+
catch (e) {
|
|
188
|
+
console.error("Error loading layout component for SSR:", e);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// 3. Preparar Metadata
|
|
192
|
+
let metadata = { title: 'Vatts App' };
|
|
193
|
+
if (layoutInfo && layoutInfo.metadata) {
|
|
194
|
+
metadata = { ...metadata, ...layoutInfo.metadata };
|
|
139
195
|
}
|
|
140
|
-
// Depois sobrescreve com metadata específico da rota se existir
|
|
141
196
|
if (generateMetadata) {
|
|
142
197
|
const routeMetadata = await Promise.resolve(generateMetadata(params, req));
|
|
143
198
|
metadata = { ...metadata, ...routeMetadata };
|
|
144
199
|
}
|
|
200
|
+
// 4. Preparar Dados Iniciais
|
|
145
201
|
const results = await Promise.all(allRoutes.map(async (r) => {
|
|
146
202
|
let routeMeta = {};
|
|
147
203
|
if (r.generateMetadata) {
|
|
@@ -153,99 +209,61 @@ async function render({ req, route, params, allRoutes }) {
|
|
|
153
209
|
metadata: routeMeta,
|
|
154
210
|
};
|
|
155
211
|
}));
|
|
156
|
-
// Prepara os dados para injetar na janela do navegador
|
|
157
212
|
const initialData = {
|
|
158
213
|
routes: results,
|
|
159
214
|
initialComponentPath: route.componentPath,
|
|
160
215
|
initialParams: params,
|
|
161
216
|
};
|
|
162
|
-
//
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
const
|
|
166
|
-
? hotReloadManager.getClientScript()
|
|
167
|
-
: '';
|
|
168
|
-
// Gera todas as meta tags
|
|
169
|
-
const metaTags = generateMetaTags(metadata);
|
|
170
|
-
// Determina quais arquivos JavaScript carregar
|
|
171
|
-
const jsFiles = getJavaScriptFiles(req);
|
|
217
|
+
// Scripts
|
|
218
|
+
const obfuscatedData = obfuscateData(initialData);
|
|
219
|
+
const hotReloadScript = !isProduction && hotReloadManager ? hotReloadManager.getClientScript() : '';
|
|
220
|
+
const metaTagsHtml = generateMetaTags(metadata);
|
|
172
221
|
const htmlLang = metadata.language || 'pt-BR';
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
</head>
|
|
180
|
-
<body>
|
|
181
|
-
<div id="root"></div>
|
|
182
|
-
${initialDataScript}
|
|
183
|
-
${jsFiles}
|
|
184
|
-
${hotReloadScript}
|
|
185
|
-
</body>
|
|
186
|
-
</html>`;
|
|
187
|
-
}
|
|
188
|
-
// Função para determinar quais arquivos JavaScript carregar
|
|
189
|
-
function getJavaScriptFiles(req) {
|
|
190
|
-
const projectDir = process.cwd();
|
|
191
|
-
const distDir = path_1.default.join(projectDir, '.vatts');
|
|
192
|
-
// Verifica se o diretório de build existe
|
|
193
|
-
if (!fs_1.default.existsSync(distDir)) {
|
|
194
|
-
// Diretório não existe - build ainda não foi executado
|
|
195
|
-
return getBuildingHTML();
|
|
222
|
+
// 5. Componente da Página Atual
|
|
223
|
+
const PageComponent = route.component;
|
|
224
|
+
// Monta a árvore da aplicação
|
|
225
|
+
let AppTree = (0, jsx_runtime_1.jsx)(PageComponent, { params: params });
|
|
226
|
+
if (LayoutComponent) {
|
|
227
|
+
AppTree = (0, jsx_runtime_1.jsx)(LayoutComponent, { children: AppTree });
|
|
196
228
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
return -1;
|
|
221
|
-
if (b.includes('main'))
|
|
222
|
-
return 1;
|
|
223
|
-
if (a.includes('vendor') || a.includes('react'))
|
|
224
|
-
return -1;
|
|
225
|
-
if (b.includes('vendor') || b.includes('react'))
|
|
226
|
-
return 1;
|
|
227
|
-
return a.localeCompare(b);
|
|
228
|
-
});
|
|
229
|
-
// @ts-ignore
|
|
230
|
-
if (jsFiles.length >= 1) {
|
|
231
|
-
// Modo chunks sem manifesto
|
|
232
|
-
return jsFiles
|
|
233
|
-
.map(file => `<script type="module" src="/_vatts/${file}"></script>`)
|
|
234
|
-
.join('');
|
|
229
|
+
// 6. Streaming
|
|
230
|
+
return new Promise((resolve, reject) => {
|
|
231
|
+
let didError = false;
|
|
232
|
+
const { pipe } = (0, server_1.renderToPipeableStream)((0, jsx_runtime_1.jsx)(ServerRoot, { lang: htmlLang, title: metadata.title || 'Vatts.js', metaTagsHtml: metaTagsHtml,
|
|
233
|
+
// Recriando o script de dados exatamente como o client espera
|
|
234
|
+
initialDataScript: `/* Data Injection */`, hotReloadScript: hotReloadScript, dataScript: (0, jsx_runtime_1.jsx)("script", { id: "__vatts_data__", type: "text/plain", "data-h": obfuscatedData }), children: AppTree }), {
|
|
235
|
+
// Usar bootstrapModules para scripts tipo módulo (ESM)
|
|
236
|
+
bootstrapModules: bootstrapScripts,
|
|
237
|
+
onShellReady() {
|
|
238
|
+
res.setHeader('Content-Type', 'text/html');
|
|
239
|
+
pipe(res);
|
|
240
|
+
resolve();
|
|
241
|
+
},
|
|
242
|
+
onShellError(error) {
|
|
243
|
+
console.error('Streaming Shell Error:', error);
|
|
244
|
+
res.statusCode = 500;
|
|
245
|
+
res.setHeader('Content-Type', 'text/html');
|
|
246
|
+
res.end('<h1>Internal Server Error</h1>');
|
|
247
|
+
resolve();
|
|
248
|
+
},
|
|
249
|
+
onError(error) {
|
|
250
|
+
didError = true;
|
|
251
|
+
console.error('Streaming Error:', error);
|
|
235
252
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
catch (error) {
|
|
243
|
-
// Erro ao ler diretório - build em andamento ou erro
|
|
244
|
-
return getBuildingHTML();
|
|
245
|
-
}
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
// Mantemos a função antiga para compatibilidade
|
|
257
|
+
async function render(options) {
|
|
258
|
+
return "";
|
|
246
259
|
}
|
|
247
260
|
// Função para retornar HTML de "Build em andamento" com auto-refresh
|
|
248
261
|
function getBuildingHTML() {
|
|
262
|
+
let version = "1.0.0";
|
|
263
|
+
try {
|
|
264
|
+
version = require("../package.json").version;
|
|
265
|
+
}
|
|
266
|
+
catch (e) { }
|
|
249
267
|
return `
|
|
250
268
|
<!DOCTYPE html>
|
|
251
269
|
<html lang="en">
|
|
@@ -489,7 +507,7 @@ function getBuildingHTML() {
|
|
|
489
507
|
<span>Building...</span>
|
|
490
508
|
<div class="status-active">
|
|
491
509
|
<div class="dot"></div>
|
|
492
|
-
v${
|
|
510
|
+
v${version}
|
|
493
511
|
</div>
|
|
494
512
|
</div>
|
|
495
513
|
</div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vatts",
|
|
3
|
-
"version": "1.0.2-alpha.
|
|
3
|
+
"version": "1.0.2-alpha.5",
|
|
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",
|