vatts 2.0.2 → 2.0.3-canary.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/LICENSE +12 -12
- package/README.md +63 -63
- package/dist/api/native-server.js +25 -4
- package/dist/builder.js +39 -39
- package/dist/core-go/core-linux-arm64.node +0 -0
- package/dist/core-go/core-linux-x64.node +0 -0
- package/dist/core-go/core-win-x64.node +0 -0
- package/dist/global/global.d.ts +176 -176
- package/dist/helpers.js +17 -7
- package/dist/hotReload.js +205 -205
- package/dist/loaders.js +15 -15
- package/dist/react/BuildingPage.js +201 -201
- package/dist/react/DefaultNotFound.js +15 -15
- package/dist/react/DevIndicator.js +101 -101
- package/dist/react/entry.client.js +7 -7
- package/dist/react/renderer-react.js +172 -33
- package/dist/react/server-error.d.ts +8 -0
- package/dist/react/server-error.js +64 -0
- package/dist/vue/App.vue +191 -191
- package/dist/vue/BuildingPage.vue +280 -280
- package/dist/vue/DefaultNotFound.vue +328 -328
- package/dist/vue/DevIndicator.vue +225 -225
- package/dist/vue/ErrorModal.vue +316 -316
- package/dist/vue/Link.vue +38 -38
- package/dist/vue/entry.client.js +7 -7
- package/dist/vue/image/Image.vue +106 -106
- package/dist/vue/renderer.vue.js +190 -46
- package/dist/vue/server-error.vue +119 -0
- package/package.json +1 -1
|
@@ -301,13 +301,13 @@ function initializeClient() {
|
|
|
301
301
|
console.error('[Watts] Critical Error rendering application:', error);
|
|
302
302
|
// Exibe erro na tela caso algo crítico falhe
|
|
303
303
|
if (typeof document !== 'undefined') {
|
|
304
|
-
document.body.innerHTML = `
|
|
305
|
-
<div style="font-family: monospace; padding: 20px; color: #ff4444; background: #1a1a1a; min-height: 100vh;">
|
|
306
|
-
<h1>Vatts Client Error</h1>
|
|
307
|
-
<p>A critical error occurred while initializing the application.</p>
|
|
308
|
-
<pre style="background: #000; padding: 15px; border-radius: 5px; overflow: auto;">${error?.message || error}</pre>
|
|
309
|
-
<pre style="color: #666; font-size: 12px; margin-top: 10px;">${error?.stack || ''}</pre>
|
|
310
|
-
</div>
|
|
304
|
+
document.body.innerHTML = `
|
|
305
|
+
<div style="font-family: monospace; padding: 20px; color: #ff4444; background: #1a1a1a; min-height: 100vh;">
|
|
306
|
+
<h1>Vatts Client Error</h1>
|
|
307
|
+
<p>A critical error occurred while initializing the application.</p>
|
|
308
|
+
<pre style="background: #000; padding: 15px; border-radius: 5px; overflow: auto;">${error?.message || error}</pre>
|
|
309
|
+
<pre style="color: #666; font-size: 12px; margin-top: 10px;">${error?.stack || ''}</pre>
|
|
310
|
+
</div>
|
|
311
311
|
`;
|
|
312
312
|
}
|
|
313
313
|
}
|
|
@@ -26,6 +26,94 @@ const router_1 = require("../router");
|
|
|
26
26
|
const fs_1 = __importDefault(require("fs"));
|
|
27
27
|
const path_1 = __importDefault(require("path"));
|
|
28
28
|
const BuildingPage_1 = __importDefault(require("../react/BuildingPage"));
|
|
29
|
+
const server_error_1 = __importDefault(require("../react/server-error"));
|
|
30
|
+
function stripScriptTags(html) {
|
|
31
|
+
if (!html)
|
|
32
|
+
return '';
|
|
33
|
+
return html.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, '');
|
|
34
|
+
}
|
|
35
|
+
function getRequestUrl(req) {
|
|
36
|
+
return req?.originalUrl || req?.url;
|
|
37
|
+
}
|
|
38
|
+
function toError(err) {
|
|
39
|
+
if (err instanceof Error)
|
|
40
|
+
return err;
|
|
41
|
+
if (typeof err === 'string')
|
|
42
|
+
return new Error(err);
|
|
43
|
+
try {
|
|
44
|
+
return new Error(JSON.stringify(err));
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return new Error(String(err));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function buildShellHtml(options) {
|
|
51
|
+
const { lang, title, metaTagsHtml, stylesHtml, hotReloadScript, obfuscatedData, scriptsHtml } = options;
|
|
52
|
+
return `<!DOCTYPE html>
|
|
53
|
+
<html lang="${lang}">
|
|
54
|
+
<head>
|
|
55
|
+
<meta charset="utf-8" />
|
|
56
|
+
<title>${title}</title>
|
|
57
|
+
${metaTagsHtml || ''}
|
|
58
|
+
${stylesHtml || ''}
|
|
59
|
+
</head>
|
|
60
|
+
<body>
|
|
61
|
+
<script id="__vatts_data__" type="text/plain" data-h="${obfuscatedData}"></script>
|
|
62
|
+
<div id="root"></div>
|
|
63
|
+
${scriptsHtml || ''}
|
|
64
|
+
${hotReloadScript ? `<div style="display:none">${hotReloadScript}</div>` : ''}
|
|
65
|
+
</body>
|
|
66
|
+
</html>`;
|
|
67
|
+
}
|
|
68
|
+
async function sendReactSsrFallback(options) {
|
|
69
|
+
const { req, res, isProduction, error, assets, lang, title, metaTagsHtml, stylesHtml, hotReloadScript, obfuscatedData, } = options;
|
|
70
|
+
const scriptsHtml = assets.scripts
|
|
71
|
+
.map((src) => `<script type="module" src="${src}"></script>`)
|
|
72
|
+
.join('\n');
|
|
73
|
+
// No DEV a gente remove scripts do head pra garantir que só aparece a página de erro.
|
|
74
|
+
const safeMetaTagsHtmlForDev = stripScriptTags(metaTagsHtml);
|
|
75
|
+
if (res.headersSent) {
|
|
76
|
+
try {
|
|
77
|
+
res.end();
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// ignore
|
|
81
|
+
}
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
85
|
+
res.statusCode = isProduction ? 200 : 500;
|
|
86
|
+
if (isProduction) {
|
|
87
|
+
res.end(buildShellHtml({
|
|
88
|
+
lang,
|
|
89
|
+
title,
|
|
90
|
+
metaTagsHtml,
|
|
91
|
+
stylesHtml,
|
|
92
|
+
hotReloadScript: '',
|
|
93
|
+
obfuscatedData,
|
|
94
|
+
scriptsHtml,
|
|
95
|
+
}));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const err = toError(error);
|
|
99
|
+
return new Promise((resolve) => {
|
|
100
|
+
const { pipe } = (0, server_1.renderToPipeableStream)(react_1.default.createElement(ServerRoot, { lang: lang, title: title, metaTagsHtml: safeMetaTagsHtmlForDev, stylesHtml: stylesHtml, initialDataScript: `/* Data Injection */`, hotReloadScript: '', dataScript: react_1.default.createElement("script", { id: "__vatts_data__", type: "text/plain", "data-h": obfuscatedData }) },
|
|
101
|
+
react_1.default.createElement(server_error_1.default, { error: err, requestUrl: getRequestUrl(req), hint: "O SSR falhou ao renderizar essa rota. Veja o erro abaixo." })), {
|
|
102
|
+
onAllReady() {
|
|
103
|
+
pipe(res);
|
|
104
|
+
resolve();
|
|
105
|
+
},
|
|
106
|
+
onError() {
|
|
107
|
+
// ignore (já estamos numa tela de erro)
|
|
108
|
+
},
|
|
109
|
+
onShellError() {
|
|
110
|
+
// fallback extremo
|
|
111
|
+
res.end('<h1>SSR Error</h1>');
|
|
112
|
+
resolve();
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
29
117
|
// --- Helpers de Servidor ---
|
|
30
118
|
// Função auxiliar para importar módulos ignorando CSS (mesma lógica do router.ts)
|
|
31
119
|
function requireWithoutStyles(modulePath) {
|
|
@@ -222,12 +310,12 @@ function getBuildAssets(req) {
|
|
|
222
310
|
}
|
|
223
311
|
function ServerRoot({ lang, title, metaTagsHtml, stylesHtml, initialDataScript, hotReloadScript, dataScript, children }) {
|
|
224
312
|
// Concatena tudo que vai no head em uma única string
|
|
225
|
-
const headContent = `
|
|
226
|
-
<meta charset="utf-8" />
|
|
227
|
-
<title>${title}</title>
|
|
228
|
-
${initialDataScript ? `<script>${initialDataScript}</script>` : ''}
|
|
229
|
-
${metaTagsHtml || ''}
|
|
230
|
-
${stylesHtml || ''}
|
|
313
|
+
const headContent = `
|
|
314
|
+
<meta charset="utf-8" />
|
|
315
|
+
<title>${title}</title>
|
|
316
|
+
${initialDataScript ? `<script>${initialDataScript}</script>` : ''}
|
|
317
|
+
${metaTagsHtml || ''}
|
|
318
|
+
${stylesHtml || ''}
|
|
231
319
|
`;
|
|
232
320
|
return (react_1.default.createElement("html", { lang: lang },
|
|
233
321
|
react_1.default.createElement("head", { dangerouslySetInnerHTML: { __html: headContent } }),
|
|
@@ -241,22 +329,23 @@ async function render({ req, res, route, params, allRoutes }) {
|
|
|
241
329
|
const isProduction = !req.hwebDev;
|
|
242
330
|
const hotReloadManager = req.hotReloadManager;
|
|
243
331
|
// SILENCIAR CONSOLE: Inicia o silêncio para evitar logs de renderização
|
|
332
|
+
let assets = null;
|
|
333
|
+
let metadata = { title: 'Vatts App' };
|
|
334
|
+
let layoutInfo = null;
|
|
244
335
|
try {
|
|
245
336
|
// 1. Verificar Build - Se não tiver scripts, retorna tela de Loading
|
|
246
|
-
|
|
337
|
+
assets = getBuildAssets(req);
|
|
247
338
|
if (!assets || assets.scripts.length === 0) {
|
|
248
|
-
// Se falhar o build, restauramos o console para o erro aparecer se necessário
|
|
249
|
-
// Usando stream para a tela de loading também, agora via React Component
|
|
250
339
|
const { pipe } = (0, server_1.renderToPipeableStream)(react_1.default.createElement(BuildingPage_1.default, null), {
|
|
251
340
|
onShellReady() {
|
|
252
|
-
res.setHeader('Content-Type', 'text/html');
|
|
341
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
253
342
|
pipe(res);
|
|
254
|
-
}
|
|
343
|
+
},
|
|
255
344
|
});
|
|
256
345
|
return;
|
|
257
346
|
}
|
|
258
347
|
// 2. Preparar Layout
|
|
259
|
-
|
|
348
|
+
layoutInfo = (0, router_1.getLayout)();
|
|
260
349
|
let LayoutComponent = null;
|
|
261
350
|
if (layoutInfo) {
|
|
262
351
|
try {
|
|
@@ -269,7 +358,6 @@ async function render({ req, res, route, params, allRoutes }) {
|
|
|
269
358
|
}
|
|
270
359
|
}
|
|
271
360
|
// 3. Preparar Metadata
|
|
272
|
-
let metadata = { title: 'Vatts App' };
|
|
273
361
|
if (layoutInfo && layoutInfo.metadata) {
|
|
274
362
|
metadata = { ...metadata, ...layoutInfo.metadata };
|
|
275
363
|
}
|
|
@@ -311,37 +399,88 @@ async function render({ req, res, route, params, allRoutes }) {
|
|
|
311
399
|
if (LayoutComponent) {
|
|
312
400
|
AppTree = react_1.default.createElement(LayoutComponent, null, AppTree);
|
|
313
401
|
}
|
|
314
|
-
// 6. Streaming
|
|
315
|
-
return new Promise((resolve
|
|
402
|
+
// 6. Streaming (segura até onAllReady para conseguir fallback sem HTML parcial)
|
|
403
|
+
return new Promise((resolve) => {
|
|
316
404
|
let didError = false;
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
initialDataScript: `/* Data Injection */`, hotReloadScript: hotReloadScript, dataScript: react_1.default.createElement("script", { id: "__vatts_data__", type: "text/plain", "data-h": obfuscatedData }) }, AppTree), {
|
|
320
|
-
// Usar bootstrapModules para scripts tipo módulo (ESM)
|
|
405
|
+
let firstError = null;
|
|
406
|
+
const stream = (0, server_1.renderToPipeableStream)(react_1.default.createElement(ServerRoot, { lang: htmlLang, title: metadata.title || 'Vatts.js', metaTagsHtml: metaTagsHtml, stylesHtml: stylesHtml, initialDataScript: `/* Data Injection */`, hotReloadScript: hotReloadScript, dataScript: react_1.default.createElement("script", { id: "__vatts_data__", type: "text/plain", "data-h": obfuscatedData }) }, AppTree), {
|
|
321
407
|
bootstrapModules: assets.scripts,
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
408
|
+
onAllReady() {
|
|
409
|
+
if (didError) {
|
|
410
|
+
stream.abort();
|
|
411
|
+
sendReactSsrFallback({
|
|
412
|
+
req,
|
|
413
|
+
res,
|
|
414
|
+
isProduction,
|
|
415
|
+
error: firstError || new Error('SSR error'),
|
|
416
|
+
assets: assets,
|
|
417
|
+
lang: htmlLang,
|
|
418
|
+
title: metadata.title || 'Vatts.js',
|
|
419
|
+
metaTagsHtml,
|
|
420
|
+
stylesHtml,
|
|
421
|
+
hotReloadScript,
|
|
422
|
+
obfuscatedData,
|
|
423
|
+
}).then(resolve);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
427
|
+
stream.pipe(res);
|
|
325
428
|
resolve();
|
|
326
429
|
},
|
|
327
430
|
onShellError(error) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
res.setHeader('Content-Type', 'text/html');
|
|
331
|
-
res.end('<h1>Internal Server Error</h1>');
|
|
332
|
-
resolve();
|
|
431
|
+
firstError ||= error;
|
|
432
|
+
didError = true;
|
|
333
433
|
},
|
|
334
434
|
onError(error) {
|
|
435
|
+
firstError ||= error;
|
|
335
436
|
didError = true;
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
437
|
+
if (!isProduction) {
|
|
438
|
+
console.error('Streaming Error:', error);
|
|
439
|
+
}
|
|
440
|
+
},
|
|
340
441
|
});
|
|
341
442
|
});
|
|
342
443
|
}
|
|
343
444
|
catch (err) {
|
|
344
|
-
|
|
345
|
-
|
|
445
|
+
if (!assets) {
|
|
446
|
+
// sem assets não dá pra montar shell completo; evita crash
|
|
447
|
+
if (!res.headersSent) {
|
|
448
|
+
res.statusCode = 500;
|
|
449
|
+
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
450
|
+
res.end(isProduction ? '' : '<h1>SSR Error</h1>');
|
|
451
|
+
}
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
await sendReactSsrFallback({
|
|
455
|
+
req,
|
|
456
|
+
res,
|
|
457
|
+
isProduction,
|
|
458
|
+
error: err,
|
|
459
|
+
assets,
|
|
460
|
+
lang: metadata?.language || 'pt-BR',
|
|
461
|
+
title: metadata.title || 'Vatts.js',
|
|
462
|
+
metaTagsHtml: (() => {
|
|
463
|
+
try {
|
|
464
|
+
return generateMetaTags(metadata);
|
|
465
|
+
}
|
|
466
|
+
catch {
|
|
467
|
+
return '';
|
|
468
|
+
}
|
|
469
|
+
})(),
|
|
470
|
+
stylesHtml: assets.styles.map((styleUrl) => `<link rel="stylesheet" href="${styleUrl}">`).join('\n'),
|
|
471
|
+
hotReloadScript: !isProduction && hotReloadManager ? hotReloadManager.getClientScript() : '',
|
|
472
|
+
obfuscatedData: (() => {
|
|
473
|
+
try {
|
|
474
|
+
return obfuscateData({
|
|
475
|
+
routes: [],
|
|
476
|
+
initialComponentPath: route.componentPath,
|
|
477
|
+
initialParams: params,
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
catch {
|
|
481
|
+
return '';
|
|
482
|
+
}
|
|
483
|
+
})(),
|
|
484
|
+
});
|
|
346
485
|
}
|
|
347
486
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
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.default = ServerError;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
function formatUnknownError(error) {
|
|
9
|
+
if (!error)
|
|
10
|
+
return { message: 'Erro desconhecido no SSR.' };
|
|
11
|
+
if (error instanceof Error) {
|
|
12
|
+
return { message: error.message || String(error), stack: error.stack };
|
|
13
|
+
}
|
|
14
|
+
if (typeof error === 'string') {
|
|
15
|
+
return { message: error };
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
return { message: JSON.stringify(error, null, 2) };
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return { message: String(error) };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function ServerError({ title = 'Erro no Server-Side Renderer', error, hint, requestUrl, }) {
|
|
25
|
+
const { message, stack } = formatUnknownError(error);
|
|
26
|
+
return (react_1.default.createElement("div", { style: {
|
|
27
|
+
fontFamily: 'ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, Apple Color Emoji, Segoe UI Emoji',
|
|
28
|
+
padding: 24,
|
|
29
|
+
color: '#0f172a',
|
|
30
|
+
background: '#ffffff',
|
|
31
|
+
} },
|
|
32
|
+
react_1.default.createElement("div", { style: { maxWidth: 980, margin: '0 auto' } },
|
|
33
|
+
react_1.default.createElement("h1", { style: { fontSize: 20, margin: 0 } }, title),
|
|
34
|
+
requestUrl ? (react_1.default.createElement("p", { style: { marginTop: 8, marginBottom: 0, color: '#334155' } },
|
|
35
|
+
"URL: ",
|
|
36
|
+
react_1.default.createElement("code", { style: { fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace' } }, requestUrl))) : null,
|
|
37
|
+
hint ? react_1.default.createElement("p", { style: { marginTop: 8, color: '#334155' } }, hint) : null,
|
|
38
|
+
react_1.default.createElement("div", { style: {
|
|
39
|
+
marginTop: 16,
|
|
40
|
+
padding: 16,
|
|
41
|
+
border: '1px solid #e2e8f0',
|
|
42
|
+
borderRadius: 10,
|
|
43
|
+
background: '#f8fafc',
|
|
44
|
+
} },
|
|
45
|
+
react_1.default.createElement("div", { style: { fontSize: 12, color: '#64748b', marginBottom: 8 } }, "Mensagem"),
|
|
46
|
+
react_1.default.createElement("pre", { style: {
|
|
47
|
+
whiteSpace: 'pre-wrap',
|
|
48
|
+
overflowWrap: 'anywhere',
|
|
49
|
+
margin: 0,
|
|
50
|
+
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
|
|
51
|
+
fontSize: 13,
|
|
52
|
+
} }, message),
|
|
53
|
+
stack ? (react_1.default.createElement(react_1.default.Fragment, null,
|
|
54
|
+
react_1.default.createElement("div", { style: { fontSize: 12, color: '#64748b', marginTop: 16, marginBottom: 8 } }, "Stack"),
|
|
55
|
+
react_1.default.createElement("pre", { style: {
|
|
56
|
+
whiteSpace: 'pre-wrap',
|
|
57
|
+
overflowWrap: 'anywhere',
|
|
58
|
+
margin: 0,
|
|
59
|
+
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
|
|
60
|
+
fontSize: 12,
|
|
61
|
+
color: '#334155',
|
|
62
|
+
} }, stack))) : null),
|
|
63
|
+
react_1.default.createElement("p", { style: { marginTop: 16, color: '#475569', fontSize: 13 } }, "Dica: em desenvolvimento isso aparece para debugar. Em produ\u00E7\u00E3o o SSR falha de forma silenciosa e o cliente assume a renderiza\u00E7\u00E3o."))));
|
|
64
|
+
}
|