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/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
- // Função para gerar todas as meta tags
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
- // Description
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
- // Author
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
- // Apple Touch Icon
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
- if (typeof og.image === 'string') {
73
- tags.push(`<meta property="og:image" content="${og.image}">`);
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 meta tags
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
- // Função para criar script ofuscado
123
- function createInitialDataScript(data) {
124
- const obfuscated = obfuscateData(data);
125
- // Usa um atributo data-* ao invés de JSON visível
126
- return `<script id="__vatts_data__" type="text/plain" data-h="${obfuscated}"></script>`;
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 render({ req, route, params, allRoutes }) {
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
- // Pega o layout se existir
134
- const layout = (0, router_1.getLayout)();
135
- let metadata = { title: 'App hweb' };
136
- // Primeiro usa o metadata do layout se existir
137
- if (layout && layout.metadata) {
138
- metadata = { ...metadata, ...layout.metadata };
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
- // Cria script JSON limpo
163
- const initialDataScript = createInitialDataScript(initialData);
164
- // Script de hot reload apenas em desenvolvimento
165
- const hotReloadScript = !isProduction && hotReloadManager
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
- // HTML base sem SSR - apenas o container e scripts para client-side rendering
174
- return `<!DOCTYPE html>
175
- <html lang="${htmlLang}">
176
- <head>
177
- ${metaTags}
178
- <title>${metadata.title || 'Vatts.js'}</title>
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
- try {
198
- // Verifica se existe um manifesto de chunks (gerado pelo ESBuild com splitting)
199
- const manifestPath = path_1.default.join(distDir, 'manifest.json');
200
- if (fs_1.default.existsSync(manifestPath)) {
201
- // Modo chunks - carrega todos os arquivos necessários
202
- const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
203
- const scripts = Object.values(manifest)
204
- .filter((file) => file.endsWith('.js'))
205
- .map((file) => `<script type="module" src="/_vatts/${file}"></script>`)
206
- .join('');
207
- // Se não há arquivos JS no manifesto, build em andamento
208
- if (!scripts) {
209
- return getBuildingHTML();
210
- }
211
- return scripts;
212
- }
213
- else {
214
- // Verifica se existem múltiplos arquivos JS (chunks sem manifesto)
215
- const jsFiles = fs_1.default.readdirSync(distDir)
216
- .filter(file => file.endsWith('.js') && !file.endsWith('.map'))
217
- .sort((a, b) => {
218
- // Ordena para carregar arquivos principais primeiro
219
- if (a.includes('main'))
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
- else {
237
- // Nenhum arquivo JS encontrado - build em andamento ou erro
238
- return getBuildingHTML();
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${require("../package.json").version}
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",
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",