vatts 1.3.1-test.3 → 1.4.1-test.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/api/http3.d.ts +41 -0
- package/dist/api/http3.js +110 -0
- package/dist/api/optimizer.d.ts +35 -0
- package/dist/api/optimizer.js +104 -0
- package/dist/bin/vatts.js +1 -0
- package/dist/builder.js +67 -31
- 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/helpers.d.ts +1 -2
- package/dist/helpers.js +53 -55
- package/dist/index.js +22 -35
- package/dist/react/renderer-react.js +6 -6
- package/dist/types.d.ts +1 -0
- package/dist/vue/renderer.vue.js +5 -5
- package/package.json +3 -2
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface para as opções do servidor Proxy HTTP/3.
|
|
3
|
+
*/
|
|
4
|
+
export interface ProxyOptions {
|
|
5
|
+
/** Porta HTTP para redirecionamento (ex: ":80") */
|
|
6
|
+
httpPort: string;
|
|
7
|
+
/** Porta HTTPS/HTTP3 (ex: ":443") */
|
|
8
|
+
httpsPort: string;
|
|
9
|
+
/** URL do servidor backend (ex: "http://localhost:3000") */
|
|
10
|
+
backendUrl: string;
|
|
11
|
+
/** Caminho para o arquivo de certificado SSL (.pem) */
|
|
12
|
+
certPath: string;
|
|
13
|
+
/** Caminho para o arquivo de chave privada SSL (.pem) */
|
|
14
|
+
keyPath: string;
|
|
15
|
+
/** (Opcional) Sobrescreve o caminho da biblioteca manualmente */
|
|
16
|
+
customLibPath?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare class NativeProxy {
|
|
19
|
+
private static instance;
|
|
20
|
+
/**
|
|
21
|
+
* Detecta a plataforma e arquitetura para montar o nome do arquivo.
|
|
22
|
+
* Padrão esperado: vatts-proxy-{os}-{arch}.node
|
|
23
|
+
* Ex: vatts-proxy-win-x64.node
|
|
24
|
+
*/
|
|
25
|
+
static getLibPath(): string;
|
|
26
|
+
/**
|
|
27
|
+
* Carrega a biblioteca nativa usando Koffi.
|
|
28
|
+
*/
|
|
29
|
+
private static loadLibrary;
|
|
30
|
+
/**
|
|
31
|
+
* Inicia o servidor Proxy.
|
|
32
|
+
* Nota: A execução no Go é assíncrona (non-blocking), então esta função retorna
|
|
33
|
+
* imediatamente após iniciar as threads do servidor, a menos que haja um erro inicial.
|
|
34
|
+
*/
|
|
35
|
+
static start(options: ProxyOptions): void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* API Pública
|
|
39
|
+
*/
|
|
40
|
+
export declare const startProxy: (options: ProxyOptions) => void;
|
|
41
|
+
export default startProxy;
|
|
@@ -0,0 +1,110 @@
|
|
|
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.startProxy = exports.NativeProxy = void 0;
|
|
7
|
+
const koffi_1 = __importDefault(require("koffi"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const os_1 = __importDefault(require("os"));
|
|
11
|
+
class NativeProxy {
|
|
12
|
+
static instance = null;
|
|
13
|
+
/**
|
|
14
|
+
* Detecta a plataforma e arquitetura para montar o nome do arquivo.
|
|
15
|
+
* Padrão esperado: vatts-proxy-{os}-{arch}.node
|
|
16
|
+
* Ex: vatts-proxy-win-x64.node
|
|
17
|
+
*/
|
|
18
|
+
static getLibPath() {
|
|
19
|
+
const platform = os_1.default.platform();
|
|
20
|
+
const arch = os_1.default.arch();
|
|
21
|
+
let osName = '';
|
|
22
|
+
let ext = '.node'; // Mantendo sua convenção de extensão
|
|
23
|
+
// Mapeamento de SO
|
|
24
|
+
switch (platform) {
|
|
25
|
+
case 'win32':
|
|
26
|
+
osName = 'win';
|
|
27
|
+
break;
|
|
28
|
+
case 'linux':
|
|
29
|
+
osName = 'linux';
|
|
30
|
+
break;
|
|
31
|
+
case 'darwin':
|
|
32
|
+
osName = 'darwin';
|
|
33
|
+
break;
|
|
34
|
+
default:
|
|
35
|
+
throw new Error(`Sistema operacional não suportado: ${platform}`);
|
|
36
|
+
}
|
|
37
|
+
// Mapeamento de Arquitetura
|
|
38
|
+
let archName = '';
|
|
39
|
+
switch (arch) {
|
|
40
|
+
case 'x64':
|
|
41
|
+
archName = 'x64';
|
|
42
|
+
break;
|
|
43
|
+
case 'arm64':
|
|
44
|
+
archName = 'arm64';
|
|
45
|
+
break;
|
|
46
|
+
default:
|
|
47
|
+
throw new Error(`Arquitetura não suportada: ${arch}`);
|
|
48
|
+
}
|
|
49
|
+
const filename = `core-${osName}-${archName}${ext}`;
|
|
50
|
+
// Ajuste o caminho relativo conforme a estrutura do seu projeto
|
|
51
|
+
// Assumindo que estará na pasta '../proxy' relativo a este arquivo
|
|
52
|
+
return path_1.default.resolve(__dirname, '..', 'core-go', filename);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Carrega a biblioteca nativa usando Koffi.
|
|
56
|
+
*/
|
|
57
|
+
static loadLibrary(customPath) {
|
|
58
|
+
if (this.instance)
|
|
59
|
+
return this.instance;
|
|
60
|
+
const libPath = customPath || this.getLibPath();
|
|
61
|
+
if (!fs_1.default.existsSync(libPath)) {
|
|
62
|
+
throw new Error(`Biblioteca nativa não encontrada: ${libPath}.\n`);
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const lib = koffi_1.default.load(libPath);
|
|
66
|
+
// Mapeia a função Go: StartServer
|
|
67
|
+
// Argumentos: 5 strings
|
|
68
|
+
// Retorno: string (erro) ou null (sucesso)
|
|
69
|
+
this.instance = lib.func('StartServer', 'str', ['str', 'str', 'str', 'str', 'str']);
|
|
70
|
+
return this.instance;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
throw new Error(`Falha ao carregar a biblioteca nativa de proxy em ${libPath}: ${error}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Inicia o servidor Proxy.
|
|
78
|
+
* Nota: A execução no Go é assíncrona (non-blocking), então esta função retorna
|
|
79
|
+
* imediatamente após iniciar as threads do servidor, a menos que haja um erro inicial.
|
|
80
|
+
*/
|
|
81
|
+
static start(options) {
|
|
82
|
+
const { httpPort, httpsPort, backendUrl, certPath, keyPath, customLibPath } = options;
|
|
83
|
+
const startServer = this.loadLibrary(customLibPath);
|
|
84
|
+
// Resolve caminhos absolutos para evitar erros no Go
|
|
85
|
+
const absCert = path_1.default.resolve(certPath);
|
|
86
|
+
const absKey = path_1.default.resolve(keyPath);
|
|
87
|
+
// Validação básica de existência dos arquivos de certificado antes de chamar o Go
|
|
88
|
+
if (!fs_1.default.existsSync(absCert)) {
|
|
89
|
+
throw new Error(`Arquivo de certificado não encontrado: ${absCert}`);
|
|
90
|
+
}
|
|
91
|
+
if (!fs_1.default.existsSync(absKey)) {
|
|
92
|
+
throw new Error(`Arquivo de chave não encontrado: ${absKey}`);
|
|
93
|
+
}
|
|
94
|
+
// Chama a função nativa
|
|
95
|
+
const errorMsg = startServer(httpPort, httpsPort, backendUrl, absCert, absKey);
|
|
96
|
+
if (errorMsg) {
|
|
97
|
+
throw new Error(`[Vatts Proxy Error] ${errorMsg}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.NativeProxy = NativeProxy;
|
|
102
|
+
/**
|
|
103
|
+
* API Pública
|
|
104
|
+
*/
|
|
105
|
+
const startProxy = (options) => {
|
|
106
|
+
return NativeProxy.start(options);
|
|
107
|
+
};
|
|
108
|
+
exports.startProxy = startProxy;
|
|
109
|
+
// Export padrão
|
|
110
|
+
exports.default = exports.startProxy;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface para as opções do otimizador.
|
|
3
|
+
*/
|
|
4
|
+
export interface OptimizerOptions {
|
|
5
|
+
/** Diretório onde estão os arquivos a serem otimizados (ex: .vatts/exported) */
|
|
6
|
+
targetDir: string;
|
|
7
|
+
/** (Opcional) Diretório de saída. Padrão: 'optimized' dentro do target */
|
|
8
|
+
outputDir?: string;
|
|
9
|
+
/** (Opcional) Lista de arquivos ou pastas a serem ignorados */
|
|
10
|
+
ignoredPatterns?: string[];
|
|
11
|
+
/** (Opcional) Sobrescreve o caminho da biblioteca manualmemte */
|
|
12
|
+
customLibPath?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class NativeOptimizer {
|
|
15
|
+
private static instance;
|
|
16
|
+
/**
|
|
17
|
+
* Detecta a plataforma e arquitetura para montar o nome do arquivo.
|
|
18
|
+
* Padrão esperado: optimizer-{os}-{arch}.{ext}
|
|
19
|
+
* Ex: optimizer-win-64.dll, optimizer-linux-arm64.so
|
|
20
|
+
*/
|
|
21
|
+
static getLibPath(): string;
|
|
22
|
+
/**
|
|
23
|
+
* Carrega a biblioteca nativa usando Koffi.
|
|
24
|
+
*/
|
|
25
|
+
private static loadLibrary;
|
|
26
|
+
/**
|
|
27
|
+
* Executa a otimização.
|
|
28
|
+
*/
|
|
29
|
+
static run(options: OptimizerOptions): void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* API Pública
|
|
33
|
+
*/
|
|
34
|
+
export declare const runOptimizer: (options: OptimizerOptions) => void;
|
|
35
|
+
export default runOptimizer;
|
|
@@ -0,0 +1,104 @@
|
|
|
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.runOptimizer = exports.NativeOptimizer = void 0;
|
|
7
|
+
const koffi_1 = __importDefault(require("koffi"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const os_1 = __importDefault(require("os"));
|
|
11
|
+
class NativeOptimizer {
|
|
12
|
+
static instance = null;
|
|
13
|
+
/**
|
|
14
|
+
* Detecta a plataforma e arquitetura para montar o nome do arquivo.
|
|
15
|
+
* Padrão esperado: optimizer-{os}-{arch}.{ext}
|
|
16
|
+
* Ex: optimizer-win-64.dll, optimizer-linux-arm64.so
|
|
17
|
+
*/
|
|
18
|
+
static getLibPath() {
|
|
19
|
+
const platform = os_1.default.platform(); // 'win32', 'linux', 'darwin'
|
|
20
|
+
const arch = os_1.default.arch(); // 'x64', 'arm64'
|
|
21
|
+
let osName = '';
|
|
22
|
+
let ext = '.node';
|
|
23
|
+
// Mapeamento de SO
|
|
24
|
+
switch (platform) {
|
|
25
|
+
case 'win32':
|
|
26
|
+
osName = 'win';
|
|
27
|
+
break;
|
|
28
|
+
case 'linux':
|
|
29
|
+
osName = 'linux';
|
|
30
|
+
break;
|
|
31
|
+
case 'darwin':
|
|
32
|
+
osName = 'darwin'; // ou 'darwin', ajuste conforme seu build
|
|
33
|
+
break;
|
|
34
|
+
default:
|
|
35
|
+
throw new Error(`Sistema operacional não suportado: ${platform}`);
|
|
36
|
+
}
|
|
37
|
+
// Mapeamento de Arquitetura (user pediu '64' para x64)
|
|
38
|
+
let archName = '';
|
|
39
|
+
switch (arch) {
|
|
40
|
+
case 'x64':
|
|
41
|
+
archName = 'x64';
|
|
42
|
+
break;
|
|
43
|
+
case 'arm64':
|
|
44
|
+
archName = 'arm64';
|
|
45
|
+
break;
|
|
46
|
+
default:
|
|
47
|
+
throw new Error(`Arquitetura não suportada: ${arch}`);
|
|
48
|
+
}
|
|
49
|
+
const filename = `core-${osName}-${archName}${ext}`;
|
|
50
|
+
// Caminho relativo: sobe um nível (fora do dist/src) e entra em builds
|
|
51
|
+
// Ajuste o '..' conforme a estrutura final da sua pasta de compilação
|
|
52
|
+
return path_1.default.resolve(__dirname, '..', 'core-go', filename);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Carrega a biblioteca nativa usando Koffi.
|
|
56
|
+
*/
|
|
57
|
+
static loadLibrary(customPath) {
|
|
58
|
+
if (this.instance)
|
|
59
|
+
return this.instance;
|
|
60
|
+
const libPath = customPath || this.getLibPath();
|
|
61
|
+
if (!fs_1.default.existsSync(libPath)) {
|
|
62
|
+
throw new Error(`Biblioteca nativa não encontrada: ${libPath}.\n`);
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const lib = koffi_1.default.load(libPath);
|
|
66
|
+
// Mapeia a função Go: func Optimize(...) *C.char
|
|
67
|
+
// Em Koffi, 'str' como retorno significa char* (string C)
|
|
68
|
+
// Se o Go retornar nil, o Koffi converte para null no JS
|
|
69
|
+
this.instance = lib.func('Optimize', 'str', ['str', 'str', 'str']);
|
|
70
|
+
return this.instance;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
throw new Error(`Falha ao carregar a biblioteca nativa em ${libPath}: ${error}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Executa a otimização.
|
|
78
|
+
*/
|
|
79
|
+
static run(options) {
|
|
80
|
+
const { targetDir, outputDir = '', ignoredPatterns = [], customLibPath } = options;
|
|
81
|
+
const optimize = this.loadLibrary(customLibPath);
|
|
82
|
+
const absTarget = path_1.default.resolve(targetDir);
|
|
83
|
+
const absOutput = outputDir ? path_1.default.resolve(outputDir) : '';
|
|
84
|
+
const ignoredStr = ignoredPatterns.join(',');
|
|
85
|
+
// Validação básica
|
|
86
|
+
if (!fs_1.default.existsSync(absTarget)) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const errorMsg = optimize(absTarget, absOutput, ignoredStr);
|
|
90
|
+
if (errorMsg) {
|
|
91
|
+
throw new Error(errorMsg);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.NativeOptimizer = NativeOptimizer;
|
|
96
|
+
/**
|
|
97
|
+
* API Pública
|
|
98
|
+
*/
|
|
99
|
+
const runOptimizer = (options) => {
|
|
100
|
+
return NativeOptimizer.run(options);
|
|
101
|
+
};
|
|
102
|
+
exports.runOptimizer = runOptimizer;
|
|
103
|
+
// Export padrão também para facilitar imports
|
|
104
|
+
exports.default = exports.runOptimizer;
|
package/dist/bin/vatts.js
CHANGED
|
@@ -94,6 +94,7 @@ program
|
|
|
94
94
|
.option('--no-html', 'Do not generate index.html (assets only)')
|
|
95
95
|
.option('--output-h-data <file>', 'Write the data-h value into this file')
|
|
96
96
|
.action(async (options) => {
|
|
97
|
+
process.env.NODE_ENV = 'production';
|
|
97
98
|
const projectDir = process.cwd();
|
|
98
99
|
const outputInput = (typeof options.output === 'string' && options.output.trim()) || 'exported';
|
|
99
100
|
const exportDirResolved = path.resolve(projectDir, outputInput);
|
package/dist/builder.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
3
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
4
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
5
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
return path;
|
|
9
|
+
};
|
|
2
10
|
/*
|
|
3
11
|
* This file is part of the Vatts.js Project.
|
|
4
12
|
* Copyright (c) 2026 mfraz
|
|
@@ -15,19 +23,12 @@
|
|
|
15
23
|
* See the License for the specific language governing permissions and
|
|
16
24
|
* limitations under the License.
|
|
17
25
|
*/
|
|
18
|
-
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
19
|
-
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
20
|
-
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
21
|
-
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
return path;
|
|
25
|
-
};
|
|
26
26
|
const { rollup, watch: rollupWatch } = require('rollup');
|
|
27
27
|
const path = require('path');
|
|
28
28
|
const Console = require("./api/console").default;
|
|
29
29
|
const fs = require('fs');
|
|
30
|
-
const
|
|
30
|
+
const crypto = require('crypto');
|
|
31
|
+
const { readdir, stat, rm, rename } = require("node:fs/promises");
|
|
31
32
|
const { loadTsConfigPaths, resolveTsConfigAlias } = require('./tsconfigPaths');
|
|
32
33
|
// --- Optimization Plugins ---
|
|
33
34
|
let terser, replace;
|
|
@@ -137,12 +138,9 @@ const customPostCssPlugin = (isProduction, isWatch = false) => {
|
|
|
137
138
|
};
|
|
138
139
|
return {
|
|
139
140
|
name: 'custom-postcss-plugin',
|
|
140
|
-
// O load hook anterior estava causando conflitos com o watch interno do Rollup.
|
|
141
|
-
// Removemos ele e usamos addWatchFile no transform.
|
|
142
141
|
async transform(code, id) {
|
|
143
142
|
if (!id.endsWith('.css'))
|
|
144
143
|
return null;
|
|
145
|
-
// Garante que o Rollup vigie este arquivo explicitamente
|
|
146
144
|
if (isWatch) {
|
|
147
145
|
this.addWatchFile(id);
|
|
148
146
|
}
|
|
@@ -157,13 +155,13 @@ const customPostCssPlugin = (isProduction, isWatch = false) => {
|
|
|
157
155
|
Console.warn(`PostCSS process error:`, e.message);
|
|
158
156
|
}
|
|
159
157
|
}
|
|
160
|
-
|
|
158
|
+
// --- FIX: Evitar colisão de nomes usando Hash do ID ---
|
|
159
|
+
const hash = crypto.createHash('md5').update(id).digest('hex').slice(0, 8);
|
|
160
|
+
const baseName = path.basename(id).split('?')[0];
|
|
161
|
+
const uniqueName = `${hash}-${baseName}`;
|
|
161
162
|
// Sanitiza o nome para usar como ID no DOM
|
|
162
|
-
const safeId =
|
|
163
|
-
const referenceId = this.emitFile({ type: 'asset', name:
|
|
164
|
-
// Lógica melhorada para injetar o CSS:
|
|
165
|
-
// 1. Usa um ID único para evitar tags <link> duplicadas.
|
|
166
|
-
// 2. Adiciona um timestamp (?t=...) no href se estiver em dev para quebrar o cache do navegador.
|
|
163
|
+
const safeId = uniqueName.replace(/[^a-zA-Z0-9-_]/g, '_');
|
|
164
|
+
const referenceId = this.emitFile({ type: 'asset', name: uniqueName, source: processedCss });
|
|
167
165
|
return {
|
|
168
166
|
code: `
|
|
169
167
|
const cssUrl = String(import.meta.ROLLUP_FILE_URL_${referenceId});
|
|
@@ -178,7 +176,6 @@ const customPostCssPlugin = (isProduction, isWatch = false) => {
|
|
|
178
176
|
document.head.appendChild(link);
|
|
179
177
|
}
|
|
180
178
|
|
|
181
|
-
// Em dev, força o reload do CSS adicionando timestamp
|
|
182
179
|
const timestamp = ${isWatch ? 'Date.now()' : 'null'};
|
|
183
180
|
link.href = timestamp ? (cssUrl + '?t=' + timestamp) : cssUrl;
|
|
184
181
|
}
|
|
@@ -218,6 +215,10 @@ const smartAssetPlugin = (isProduction) => {
|
|
|
218
215
|
}
|
|
219
216
|
let buffer = await fs.promises.readFile(cleanId);
|
|
220
217
|
const size = buffer.length;
|
|
218
|
+
// --- FIX: Evitar colisão de nomes em assets estáticos ---
|
|
219
|
+
const hash = crypto.createHash('md5').update(cleanId).digest('hex').slice(0, 8);
|
|
220
|
+
const baseName = path.basename(cleanId);
|
|
221
|
+
const uniqueName = `${hash}-${baseName}`;
|
|
221
222
|
if (type === 'svg') {
|
|
222
223
|
if (size < INLINE_LIMIT) {
|
|
223
224
|
const content = buffer.toString('utf8');
|
|
@@ -228,7 +229,7 @@ const smartAssetPlugin = (isProduction) => {
|
|
|
228
229
|
`;
|
|
229
230
|
}
|
|
230
231
|
else {
|
|
231
|
-
const referenceId = this.emitFile({ type: 'asset', name:
|
|
232
|
+
const referenceId = this.emitFile({ type: 'asset', name: uniqueName, source: buffer });
|
|
232
233
|
const content = buffer.toString('utf8');
|
|
233
234
|
return `
|
|
234
235
|
export default String(import.meta.ROLLUP_FILE_URL_${referenceId});
|
|
@@ -241,7 +242,7 @@ const smartAssetPlugin = (isProduction) => {
|
|
|
241
242
|
return `export default "data:${type};base64,${base64}";`;
|
|
242
243
|
}
|
|
243
244
|
else {
|
|
244
|
-
const referenceId = this.emitFile({ type: 'asset', name:
|
|
245
|
+
const referenceId = this.emitFile({ type: 'asset', name: uniqueName, source: buffer });
|
|
245
246
|
return `export default String(import.meta.ROLLUP_FILE_URL_${referenceId});`;
|
|
246
247
|
}
|
|
247
248
|
}
|
|
@@ -379,12 +380,10 @@ async function buildWithChunks(entryPoint, outdir, isProduction = false) {
|
|
|
379
380
|
manualChunks(id) {
|
|
380
381
|
if (id.includes('node_modules')) {
|
|
381
382
|
const normalizedId = id.replace(/\\/g, '/');
|
|
382
|
-
// --- VUE SPLITTING AVANÇADO ---
|
|
383
383
|
if (/\/node_modules\/vue-router\//.test(normalizedId))
|
|
384
384
|
return 'vendor-vue-router';
|
|
385
385
|
if (/\/node_modules\/(pinia|vuex)\//.test(normalizedId))
|
|
386
386
|
return 'vendor-vue-store';
|
|
387
|
-
// Separa os modulos internos do Vue para evitar um chunk gigante
|
|
388
387
|
if (/\/node_modules\/(vue|@vue)\//.test(normalizedId)) {
|
|
389
388
|
if (normalizedId.includes('/runtime-core'))
|
|
390
389
|
return 'vendor-vue-runtime-core';
|
|
@@ -398,8 +397,6 @@ async function buildWithChunks(entryPoint, outdir, isProduction = false) {
|
|
|
398
397
|
return 'vendor-vue-compiler';
|
|
399
398
|
return 'vendor-vue-core';
|
|
400
399
|
}
|
|
401
|
-
// --- REACT SPLITTING AVANÇADO ---
|
|
402
|
-
// Separa DOM de Core e Scheduler
|
|
403
400
|
if (/\/node_modules\/react-dom\//.test(normalizedId))
|
|
404
401
|
return 'vendor-react-dom';
|
|
405
402
|
if (/\/node_modules\/scheduler\//.test(normalizedId))
|
|
@@ -408,7 +405,6 @@ async function buildWithChunks(entryPoint, outdir, isProduction = false) {
|
|
|
408
405
|
return 'vendor-react-router';
|
|
409
406
|
if (/\/node_modules\/react\//.test(normalizedId))
|
|
410
407
|
return 'vendor-react-core';
|
|
411
|
-
// --- UI LIBS (Granular) ---
|
|
412
408
|
if (id.includes('framer-motion'))
|
|
413
409
|
return 'vendor-framer';
|
|
414
410
|
if (id.includes('@radix-ui'))
|
|
@@ -417,15 +413,12 @@ async function buildWithChunks(entryPoint, outdir, isProduction = false) {
|
|
|
417
413
|
return 'vendor-headless';
|
|
418
414
|
if (id.includes('@heroicons'))
|
|
419
415
|
return 'vendor-icons';
|
|
420
|
-
// --- UTILS ---
|
|
421
416
|
if (id.includes('lodash'))
|
|
422
417
|
return 'vendor-lodash';
|
|
423
418
|
if (id.includes('date-fns') || id.includes('moment'))
|
|
424
419
|
return 'vendor-date';
|
|
425
420
|
if (id.includes('axios'))
|
|
426
421
|
return 'vendor-axios';
|
|
427
|
-
// Resto cai em vendor-libs genérico para não criar 1 arquivo por pacote,
|
|
428
|
-
// mas já tiramos o peso pesado acima.
|
|
429
422
|
return 'vendor-libs';
|
|
430
423
|
}
|
|
431
424
|
}
|
|
@@ -433,6 +426,51 @@ async function buildWithChunks(entryPoint, outdir, isProduction = false) {
|
|
|
433
426
|
const bundle = await rollup(inputOptions);
|
|
434
427
|
await bundle.write(outputOptions);
|
|
435
428
|
await bundle.close();
|
|
429
|
+
// --- Native Optimization Integration ---
|
|
430
|
+
if (isProduction) {
|
|
431
|
+
try {
|
|
432
|
+
// Dynamically require to avoid issues if file doesn't exist in dev/non-opt environments
|
|
433
|
+
// Assuming ./api/optimizer matches the transpiled location of src/optimizer.ts
|
|
434
|
+
const { runOptimizer } = require('./api/optimizer');
|
|
435
|
+
const optimizedDir = path.join(outdir, 'optimized');
|
|
436
|
+
// 1. Run Optimizer
|
|
437
|
+
runOptimizer({
|
|
438
|
+
targetDir: outdir,
|
|
439
|
+
outputDir: optimizedDir,
|
|
440
|
+
// Ignore already optimized assets or node_modules if present
|
|
441
|
+
ignoredPatterns: ['assets']
|
|
442
|
+
});
|
|
443
|
+
// 2. Cleanup: Remove original main*.js and chunks folder from root
|
|
444
|
+
const rootFiles = await readdir(outdir);
|
|
445
|
+
for (const file of rootFiles) {
|
|
446
|
+
// Match main.js, main.hash.js, etc and the chunks directory
|
|
447
|
+
if ((file.startsWith('main') && file.endsWith('.js')) || file === 'chunks') {
|
|
448
|
+
await rm(path.join(outdir, file), { recursive: true, force: true });
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
// 3. Move optimized files back to root
|
|
452
|
+
if (fs.existsSync(optimizedDir)) {
|
|
453
|
+
const optFiles = await readdir(optimizedDir);
|
|
454
|
+
for (const file of optFiles) {
|
|
455
|
+
const srcPath = path.join(optimizedDir, file);
|
|
456
|
+
const destPath = path.join(outdir, file);
|
|
457
|
+
// Force remove destination if it exists (e.g. if assets folder exists and we are moving optimized/assets)
|
|
458
|
+
// This prevents EEXIST errors on rename
|
|
459
|
+
await rm(destPath, { recursive: true, force: true });
|
|
460
|
+
await rename(srcPath, destPath);
|
|
461
|
+
}
|
|
462
|
+
// Remove the now empty optimized directory
|
|
463
|
+
await rm(optimizedDir, { recursive: true, force: true });
|
|
464
|
+
}
|
|
465
|
+
Console.log("✅ Build successfully optimized with native optimizer.");
|
|
466
|
+
}
|
|
467
|
+
catch (err) {
|
|
468
|
+
Console.error('Native optimization failed:', err);
|
|
469
|
+
// Not exiting process here to allow build to "succeed" even if optimization fails,
|
|
470
|
+
// though files might be in a weird state.
|
|
471
|
+
// If critical, uncomment: process.exit(1);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
436
474
|
}
|
|
437
475
|
catch (error) {
|
|
438
476
|
Console.error('An error occurred while building with chunks:', error);
|
|
@@ -541,7 +579,6 @@ async function watchWithChunks(entryPoint, outdir, hotReloadManager = null) {
|
|
|
541
579
|
dir: outdir,
|
|
542
580
|
format: 'es',
|
|
543
581
|
entryFileNames: 'main.js',
|
|
544
|
-
// CHANGE: Remove hash in watch mode to prevent file accumulation in assets folder
|
|
545
582
|
assetFileNames: 'assets/[name][extname]',
|
|
546
583
|
sourcemap: true,
|
|
547
584
|
intro: processPolyfill
|
|
@@ -573,7 +610,6 @@ async function watch(entryPoint, outfile, hotReloadManager = null) {
|
|
|
573
610
|
const outputOptions = {
|
|
574
611
|
file: outfile,
|
|
575
612
|
format: 'es',
|
|
576
|
-
// CHANGE: Remove hash in watch mode to prevent file accumulation
|
|
577
613
|
assetFileNames: 'assets/[name][extname]',
|
|
578
614
|
sourcemap: true,
|
|
579
615
|
intro: processPolyfill
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/helpers.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import http, { Server } from 'http';
|
|
3
3
|
import type { VattsOptions, VattsConfig } from './types';
|
|
4
|
-
import http2 from 'http2';
|
|
5
4
|
/**
|
|
6
5
|
* Carrega o arquivo de configuração vatts.config.ts ou vatts.config.js do projeto
|
|
7
6
|
* @param projectDir Diretório raiz do projeto
|
|
@@ -20,7 +19,7 @@ export declare function app(options?: VattsOptions): {
|
|
|
20
19
|
/**
|
|
21
20
|
* Inicia um servidor Vatts.js fechado (o usuário não tem acesso ao framework)
|
|
22
21
|
*/
|
|
23
|
-
init: () => Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse
|
|
22
|
+
init: () => Promise<http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>>;
|
|
24
23
|
prepare: () => Promise<void>;
|
|
25
24
|
getRequestHandler: () => (req: any, res: any, next?: any) => Promise<void> | void;
|
|
26
25
|
setupWebSocket: (server: Server | any) => void;
|
package/dist/helpers.js
CHANGED
|
@@ -65,8 +65,8 @@ const path_1 = __importDefault(require("path"));
|
|
|
65
65
|
// Helpers para integração com diferentes frameworks
|
|
66
66
|
const index_js_1 = __importStar(require("./index.js")); // Importando o tipo
|
|
67
67
|
const console_1 = __importStar(require("./api/console"));
|
|
68
|
-
const http2_1 = __importDefault(require("http2")); // <-- ADICIONADO: Import do HTTP/2
|
|
69
68
|
const fs_1 = __importDefault(require("fs"));
|
|
69
|
+
const http3_1 = __importDefault(require("./api/http3"));
|
|
70
70
|
// Registra loaders customizados para importar arquivos não-JS
|
|
71
71
|
const { registerLoaders } = require('./loaders');
|
|
72
72
|
registerLoaders({ projectDir: process.cwd() });
|
|
@@ -325,7 +325,7 @@ function setConfig(newConfig) {
|
|
|
325
325
|
/**
|
|
326
326
|
* Inicializa servidor nativo do Vatts.js usando HTTP ou HTTPS
|
|
327
327
|
*/
|
|
328
|
-
async function initNativeServer(vattsApp, options,
|
|
328
|
+
async function initNativeServer(vattsApp, options, hostname) {
|
|
329
329
|
const time = Date.now();
|
|
330
330
|
const projectDir = options.dir || process.cwd();
|
|
331
331
|
const phase = options.dev ? 'development' : 'production';
|
|
@@ -425,7 +425,7 @@ async function initNativeServer(vattsApp, options, port, hostname) {
|
|
|
425
425
|
// Parse do body com proteções
|
|
426
426
|
req.body = await parseBody(req); // Assumindo que parseBody existe
|
|
427
427
|
// Adiciona host se não existir (necessário para `new URL`)
|
|
428
|
-
req.headers.host = req.headers.host || `localhost:${port}`;
|
|
428
|
+
req.headers.host = req.headers.host || `localhost:${exports.config?.port}`;
|
|
429
429
|
await handler(req, res);
|
|
430
430
|
}
|
|
431
431
|
catch (error) {
|
|
@@ -465,51 +465,12 @@ async function initNativeServer(vattsApp, options, port, hostname) {
|
|
|
465
465
|
}
|
|
466
466
|
};
|
|
467
467
|
// --- FIM DO LISTENER ---
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
ca: exports.config.ssl.ca ? fs_1.default.readFileSync(exports.config.ssl.ca, 'utf8') : undefined,
|
|
475
|
-
allowHTTP1: true // <-- IMPORTANTE: Garante compatibilidade com clientes HTTP/1.1
|
|
476
|
-
};
|
|
477
|
-
// 1. Cria o servidor HTTPS com HTTP/2
|
|
478
|
-
// Substituído https.createServer por http2.createSecureServer
|
|
479
|
-
server = http2_1.default.createSecureServer(sslOptions, requestListener);
|
|
480
|
-
// 2. Cria o servidor de REDIRECIONAMENTO (HTTP -> HTTPS)
|
|
481
|
-
const httpRedirectPort = exports.config.ssl.redirectPort;
|
|
482
|
-
http_1.default.createServer((req, res) => {
|
|
483
|
-
// Evita host header injection/open redirect: prefere hostname configurado
|
|
484
|
-
const rawHost = String(req.headers['host'] || '').trim();
|
|
485
|
-
const configuredHost = (options.hostname || hostname || '').trim();
|
|
486
|
-
let hostToUse = configuredHost;
|
|
487
|
-
// Se não tiver hostname configurado, tenta usar Host do request com validação básica
|
|
488
|
-
if (!hostToUse) {
|
|
489
|
-
const hostWithoutPort = rawHost.split(':')[0];
|
|
490
|
-
// allowlist: hostname simples (sem espaços, sem barras), e sem caracteres perigosos
|
|
491
|
-
const isValidHost = /^[a-zA-Z0-9.-]+$/.test(hostWithoutPort) && !hostWithoutPort.includes('..');
|
|
492
|
-
hostToUse = isValidHost ? hostWithoutPort : 'localhost';
|
|
493
|
-
}
|
|
494
|
-
// Monta a URL de redirecionamento
|
|
495
|
-
let redirectUrl = `https://${hostToUse}`;
|
|
496
|
-
// Adiciona a porta HTTPS apenas se não for a padrão (443)
|
|
497
|
-
if (port !== 443) {
|
|
498
|
-
redirectUrl += `:${port}`;
|
|
499
|
-
}
|
|
500
|
-
redirectUrl += req.url || '/';
|
|
501
|
-
res.writeHead(301, { 'Location': redirectUrl });
|
|
502
|
-
res.end();
|
|
503
|
-
}).listen(httpRedirectPort, hostname, () => { });
|
|
504
|
-
}
|
|
505
|
-
else {
|
|
506
|
-
// --- MODO HTTP (Original) ---
|
|
507
|
-
// Cria o servidor HTTP nativo
|
|
508
|
-
server = http_1.default.createServer(requestListener);
|
|
509
|
-
}
|
|
510
|
-
// Configurações de segurança do servidor (usa configuração personalizada)
|
|
511
|
-
server.setTimeout(vattsConfig.serverTimeout || 35000); // Timeout geral do servidor
|
|
512
|
-
// @ts-ignore (Propriedades específicas do HTTP/1, HTTP/2 gerencia isso de forma diferente, mas mantemos para o server HTTP padrão)
|
|
468
|
+
// Sempre criamos um servidor HTTP padrão do Node.js.
|
|
469
|
+
// Se estivermos em modo SSL (HTTP/3), este servidor será apenas o "backend" local.
|
|
470
|
+
const server = http_1.default.createServer(requestListener);
|
|
471
|
+
// Configurações de timeout (aplicam-se ao backend)
|
|
472
|
+
server.setTimeout(vattsConfig.serverTimeout || 35000);
|
|
473
|
+
// @ts-ignore
|
|
513
474
|
if (server.maxHeadersCount)
|
|
514
475
|
server.maxHeadersCount = vattsConfig.maxHeadersCount || 100;
|
|
515
476
|
// @ts-ignore
|
|
@@ -518,11 +479,49 @@ async function initNativeServer(vattsApp, options, port, hostname) {
|
|
|
518
479
|
// @ts-ignore
|
|
519
480
|
if (server.requestTimeout)
|
|
520
481
|
server.requestTimeout = vattsConfig.requestTimeout || 30000;
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
482
|
+
const isSSL = exports.config.ssl && exports.config.ssl.key && exports.config.ssl.cert;
|
|
483
|
+
if (isSSL) {
|
|
484
|
+
// --- MODO SSL (HTTP/3 Proxy Nativo) ---
|
|
485
|
+
// 1. Definição da Porta do Backend (Onde o Node.js vai rodar escondido)
|
|
486
|
+
// Se a porta pública for 3000, usamos 3001 para o backend para não dar conflito de porta presa.
|
|
487
|
+
const backendPort = exports.config.ssl?.backendPort;
|
|
488
|
+
// 2. Portas Públicas
|
|
489
|
+
const publicPort = exports.config.port || 443;
|
|
490
|
+
const redirectPort = exports.config.ssl?.redirectPort || 80;
|
|
491
|
+
// 3. Inicia o Node.js em localhost (Backend)
|
|
492
|
+
server.listen(backendPort, '127.0.0.1', () => {
|
|
493
|
+
try {
|
|
494
|
+
// 4. Inicia o Proxy Go (HTTP/3 + Fallback H2/H1)
|
|
495
|
+
// Isso não bloqueia o Node, roda em background via CGO
|
|
496
|
+
(0, http3_1.default)({
|
|
497
|
+
httpPort: `:${redirectPort}`, // Porta para redirecionar HTTP -> HTTPS
|
|
498
|
+
httpsPort: `:${publicPort}`, // Porta Principal (UDP/TCP)
|
|
499
|
+
backendUrl: `http://127.0.0.1:${backendPort}`, // Para onde enviar o tráfego
|
|
500
|
+
certPath: exports.config?.ssl?.cert,
|
|
501
|
+
keyPath: exports.config?.ssl?.key
|
|
502
|
+
});
|
|
503
|
+
// Atualiza o box de info para mostrar a porta pública correta
|
|
504
|
+
sendBox({ ...options });
|
|
505
|
+
msg.end(`${console_1.Colors.Bright}Ready on port ${console_1.Colors.BgGreen} ${publicPort} (HTTP/3 Enabled) ${console_1.Colors.Reset}\n` +
|
|
506
|
+
`${console_1.Colors.Dim} ↳ Backend active on 127.0.0.1:${backendPort}${console_1.Colors.Reset}\n` +
|
|
507
|
+
`${console_1.Colors.Bright} in ${Date.now() - time}ms${console_1.Colors.Reset}\n`);
|
|
508
|
+
}
|
|
509
|
+
catch (err) {
|
|
510
|
+
console_1.default.error(`${console_1.Colors.FgRed}[Critical] Failed to start Native HTTP/3 Proxy:${console_1.Colors.Reset} ${err.message}`);
|
|
511
|
+
// Opcional: Fallback ou exit. Geralmente se o SSL falhar, melhor parar.
|
|
512
|
+
process.exit(1);
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
// --- MODO HTTP CLÁSSICO (Sem SSL) ---
|
|
518
|
+
// Roda direto na porta configurada, exposto para o mundo
|
|
519
|
+
server.listen(exports.config?.port, hostname, () => {
|
|
520
|
+
sendBox({ ...options });
|
|
521
|
+
msg.end(`${console_1.Colors.Bright}Ready on port ${console_1.Colors.BgGreen} ${exports.config?.port} ${console_1.Colors.Reset}${console_1.Colors.Bright} in ${Date.now() - time}ms${console_1.Colors.Reset}\n`);
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
// Configura WebSocket (Funciona através do Proxy pois ele suporta upgrade de conexão)
|
|
526
525
|
vattsApp.setupWebSocket(server);
|
|
527
526
|
vattsApp.executeInstrumentation();
|
|
528
527
|
return server;
|
|
@@ -618,12 +617,11 @@ ${console_1.Colors.Bright + console_1.Colors.FgCyan} \\ / /\\ | | /__\
|
|
|
618
617
|
${console_1.Colors.Bright + console_1.Colors.FgCyan} \\/ /~~\\ | | .__/ .${console_1.Colors.FgWhite} \\__/ .__/ ${message}
|
|
619
618
|
|
|
620
619
|
`);
|
|
621
|
-
const actualPort = exports.config?.port || 3000;
|
|
622
620
|
const actualHostname = options.hostname || "0.0.0.0";
|
|
623
621
|
if (framework !== 'native') {
|
|
624
622
|
console_1.default.warn(`The "${framework}" framework was selected, but the init() method only works with the "native" framework. Starting native server...`);
|
|
625
623
|
}
|
|
626
|
-
return await initNativeServer(vattsApp, options,
|
|
624
|
+
return await initNativeServer(vattsApp, options, actualHostname);
|
|
627
625
|
}
|
|
628
626
|
};
|
|
629
627
|
}
|
package/dist/index.js
CHANGED
|
@@ -60,7 +60,6 @@ const crypto_1 = __importDefault(require("crypto")); // Adicionado para gerar ha
|
|
|
60
60
|
const express_1 = require("./adapters/express");
|
|
61
61
|
const builder_1 = require("./builder");
|
|
62
62
|
const router_1 = require("./router");
|
|
63
|
-
// import { renderAsStream } from './renderer'; // REMOVIDO: Carregamento dinâmico agora
|
|
64
63
|
const http_1 = require("./api/http");
|
|
65
64
|
Object.defineProperty(exports, "VattsRequest", { enumerable: true, get: function () { return http_1.VattsRequest; } });
|
|
66
65
|
Object.defineProperty(exports, "VattsResponse", { enumerable: true, get: function () { return http_1.VattsResponse; } });
|
|
@@ -236,33 +235,6 @@ Object.defineProperty(exports, "FrameworkAdapterFactory", { enumerable: true, ge
|
|
|
236
235
|
var helpers_2 = require("./helpers");
|
|
237
236
|
Object.defineProperty(exports, "app", { enumerable: true, get: function () { return helpers_2.app; } });
|
|
238
237
|
// Função para verificar se o projeto é grande o suficiente para se beneficiar de chunks
|
|
239
|
-
function isLargeProject(projectDir) {
|
|
240
|
-
try {
|
|
241
|
-
const srcDir = path_1.default.join(projectDir, 'src');
|
|
242
|
-
if (!fs_1.default.existsSync(srcDir))
|
|
243
|
-
return false;
|
|
244
|
-
let totalFiles = 0;
|
|
245
|
-
let totalSize = 0;
|
|
246
|
-
function scanDirectory(dir) {
|
|
247
|
-
const items = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
248
|
-
for (const item of items) {
|
|
249
|
-
const fullPath = path_1.default.join(dir, item.name);
|
|
250
|
-
if (item.isDirectory() && item.name !== 'node_modules' && item.name !== '.git') {
|
|
251
|
-
scanDirectory(fullPath);
|
|
252
|
-
}
|
|
253
|
-
else if (item.isFile() && /\.(tsx?|jsx?|css|scss|less)$/i.test(item.name)) {
|
|
254
|
-
totalFiles++;
|
|
255
|
-
totalSize += fs_1.default.statSync(fullPath).size;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
scanDirectory(srcDir);
|
|
260
|
-
return totalFiles > 20 || totalSize > 500 * 1024;
|
|
261
|
-
}
|
|
262
|
-
catch (error) {
|
|
263
|
-
return false;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
238
|
// Função para gerar o arquivo de entrada para o esbuild
|
|
267
239
|
function createEntryFile(projectDir, routes, framework) {
|
|
268
240
|
try {
|
|
@@ -556,11 +528,24 @@ function vatts(options) {
|
|
|
556
528
|
const staticPath = path_1.default.join(dir, '.vatts');
|
|
557
529
|
const requestPath = pathname.replace('/_vatts/', '');
|
|
558
530
|
if (!isSuspiciousPathname(requestPath)) {
|
|
559
|
-
|
|
531
|
+
let filePath = resolveWithin(staticPath, requestPath);
|
|
532
|
+
let isGzipped = false;
|
|
533
|
+
// --- LÓGICA GZIP (Compatibilidade com Otimizador Go) ---
|
|
534
|
+
// Se o arquivo solicitado termina em .js e NÃO existe fisicamente (foi deletado pelo otimizador),
|
|
535
|
+
// tentamos encontrar a versão .js.gz
|
|
536
|
+
if (filePath && !fs_1.default.existsSync(filePath) && filePath.endsWith('.js')) {
|
|
537
|
+
const gzipPath = filePath + '.gz';
|
|
538
|
+
if (fs_1.default.existsSync(gzipPath)) {
|
|
539
|
+
filePath = gzipPath;
|
|
540
|
+
isGzipped = true;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
560
543
|
// Verifica se existe E se é arquivo
|
|
561
544
|
if (filePath && fs_1.default.existsSync(filePath) && fs_1.default.statSync(filePath).isFile()) {
|
|
562
545
|
const stats = fs_1.default.statSync(filePath);
|
|
563
|
-
|
|
546
|
+
// Se for GZIP, usamos '.js' para definir o Content-Type correto (application/javascript),
|
|
547
|
+
// caso contrário, pegamos a extensão real do arquivo.
|
|
548
|
+
const ext = isGzipped ? '.js' : path_1.default.extname(filePath).toLowerCase();
|
|
564
549
|
const contentTypes = {
|
|
565
550
|
'.js': 'application/javascript',
|
|
566
551
|
'.css': 'text/css',
|
|
@@ -578,14 +563,17 @@ function vatts(options) {
|
|
|
578
563
|
genericRes.header('Expires', '0');
|
|
579
564
|
}
|
|
580
565
|
else {
|
|
581
|
-
// Em produção, mantemos o cache agressivo
|
|
582
|
-
// Dica: Se o main.js de prod também não tiver hash, use 'no-cache' nele também
|
|
566
|
+
// Em produção, mantemos o cache agressivo
|
|
583
567
|
genericRes.header('Cache-Control', 'public, max-age=31536000, immutable');
|
|
584
568
|
}
|
|
569
|
+
// --- HEADER VITAL PARA GZIP ---
|
|
570
|
+
if (isGzipped) {
|
|
571
|
+
genericRes.header('Content-Encoding', 'gzip');
|
|
572
|
+
}
|
|
585
573
|
const lastModified = stats.mtime.toUTCString();
|
|
586
574
|
genericRes.header('Last-Modified', lastModified);
|
|
587
575
|
genericRes.header('Content-Type', contentTypes[ext] || 'text/plain');
|
|
588
|
-
// Lógica 304
|
|
576
|
+
// Lógica 304
|
|
589
577
|
const ifModifiedSince = req.headers['if-modified-since'];
|
|
590
578
|
if (ifModifiedSince) {
|
|
591
579
|
const requestDate = new Date(ifModifiedSince).getTime();
|
|
@@ -612,8 +600,7 @@ function vatts(options) {
|
|
|
612
600
|
return; // Encerra aqui com sucesso
|
|
613
601
|
}
|
|
614
602
|
else {
|
|
615
|
-
// CORREÇÃO 2:
|
|
616
|
-
// Isso impede que o código continue e caia na rota de renderização do HTML (SPA Fallback)
|
|
603
|
+
// CORREÇÃO 2: 404 para assets não encontrados
|
|
617
604
|
if (adapter.type === 'express') {
|
|
618
605
|
res.status(404).send('Vatts Asset Not Found');
|
|
619
606
|
}
|
|
@@ -151,8 +151,6 @@ function obfuscateData(data) {
|
|
|
151
151
|
const hash = Buffer.from(Date.now().toString()).toString('base64').substring(0, 8);
|
|
152
152
|
return `${hash}.${base64}`;
|
|
153
153
|
}
|
|
154
|
-
// Retorna as URLs dos scripts e CSS
|
|
155
|
-
// Procura em .vatts/ e .vatts/assets/
|
|
156
154
|
function getBuildAssets(req) {
|
|
157
155
|
const projectDir = process.cwd();
|
|
158
156
|
const distDir = path_1.default.join(projectDir, '.vatts');
|
|
@@ -173,8 +171,9 @@ function getBuildAssets(req) {
|
|
|
173
171
|
const fullPath = path_1.default.join(directory, file);
|
|
174
172
|
const stat = fs_1.default.statSync(fullPath);
|
|
175
173
|
if (stat.isFile()) {
|
|
176
|
-
|
|
177
|
-
|
|
174
|
+
// MODIFICADO: Aceita .js OU .js.gz
|
|
175
|
+
if (file.endsWith('.js') || file.endsWith('.js.gz')) {
|
|
176
|
+
scripts.push(`${urlPrefix}/${file.replace(".gz", '')}`);
|
|
178
177
|
}
|
|
179
178
|
else if (file.endsWith('.css')) {
|
|
180
179
|
styles.push(`${urlPrefix}/${file}`);
|
|
@@ -189,8 +188,9 @@ function getBuildAssets(req) {
|
|
|
189
188
|
const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
|
|
190
189
|
const manifestFiles = Object.values(manifest);
|
|
191
190
|
scripts = manifestFiles
|
|
192
|
-
.
|
|
193
|
-
.
|
|
191
|
+
// MODIFICADO: Filtra .js E .js.gz no manifesto
|
|
192
|
+
.filter((f) => f.endsWith('.js') || f.endsWith('.js.gz'))
|
|
193
|
+
.map((f) => `/_vatts/${f.replace(".gz", "")}`);
|
|
194
194
|
styles = manifestFiles
|
|
195
195
|
.filter((f) => f.endsWith('.css'))
|
|
196
196
|
.map((f) => `/_vatts/${f}`);
|
package/dist/types.d.ts
CHANGED
package/dist/vue/renderer.vue.js
CHANGED
|
@@ -101,9 +101,9 @@ function extractComponentPreloads(componentPath) {
|
|
|
101
101
|
tags.add(`<link rel="preload" as="style" href="${publicUrl}">`);
|
|
102
102
|
tags.add(`<link rel="stylesheet" href="${publicUrl}">`);
|
|
103
103
|
}
|
|
104
|
-
else if (['.js'].includes(ext)) {
|
|
104
|
+
else if (['.js', '.js.gz'].includes(ext)) {
|
|
105
105
|
// Adicionado suporte para JS
|
|
106
|
-
tags.add(`<link rel="preload" as="script" href="${publicUrl}">`);
|
|
106
|
+
tags.add(`<link rel="preload" as="script" href="${publicUrl.replace(".gz", '')}">`);
|
|
107
107
|
}
|
|
108
108
|
else if (['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.avif'].includes(ext)) {
|
|
109
109
|
tags.add(`<link rel="preload" as="image" href="${publicUrl}">`);
|
|
@@ -331,8 +331,8 @@ function getBuildAssets(req) {
|
|
|
331
331
|
return;
|
|
332
332
|
const fullPath = path_1.default.join(directory, file);
|
|
333
333
|
if (fs_1.default.statSync(fullPath).isFile()) {
|
|
334
|
-
if (file.endsWith('.js'))
|
|
335
|
-
scripts.push(`${urlPrefix}/${file}`);
|
|
334
|
+
if (file.endsWith('.js') || file.endsWith(".js.gz"))
|
|
335
|
+
scripts.push(`${urlPrefix}/${file.replace(".gz", '')}`);
|
|
336
336
|
else if (file.endsWith('.css'))
|
|
337
337
|
styles.push(`${urlPrefix}/${file}`);
|
|
338
338
|
}
|
|
@@ -343,7 +343,7 @@ function getBuildAssets(req) {
|
|
|
343
343
|
if (fs_1.default.existsSync(manifestPath)) {
|
|
344
344
|
const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
|
|
345
345
|
const manifestFiles = Object.values(manifest);
|
|
346
|
-
scripts = manifestFiles.filter((f) => f.endsWith('.js')).map((f) => `/_vatts/${f}`);
|
|
346
|
+
scripts = manifestFiles.filter((f) => f.endsWith('.js') || f.endsWith(".js.gz")).map((f) => `/_vatts/${f.replace('.gz', '')}`);
|
|
347
347
|
styles = manifestFiles.filter((f) => f.endsWith('.css')).map((f) => `/_vatts/${f}`);
|
|
348
348
|
}
|
|
349
349
|
else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vatts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1-test.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": "mfraz",
|
|
@@ -110,6 +110,7 @@
|
|
|
110
110
|
"chokidar": "^3.6.0",
|
|
111
111
|
"commander": "^14.0.2",
|
|
112
112
|
"esbuild": "^0.27.2",
|
|
113
|
+
"koffi": "^2.15.1",
|
|
113
114
|
"rollup": "^4.56.0",
|
|
114
115
|
"rollup-plugin-esbuild": "^6.2.1",
|
|
115
116
|
"rollup-plugin-vue": "^6.0.0",
|
|
@@ -136,6 +137,6 @@
|
|
|
136
137
|
"vue-tsc": "^3.2.3"
|
|
137
138
|
},
|
|
138
139
|
"scripts": {
|
|
139
|
-
"build": "tsc && copyfiles -u 1 \"src/**/*.{vue,d.ts}\" dist"
|
|
140
|
+
"build": "tsc && copyfiles -u 1 \"src/**/*.{vue,d.ts}\" dist && copyfiles -f \"../../services/core-go/binaries/*.node\" dist/core-go"
|
|
140
141
|
}
|
|
141
142
|
}
|