vatts 1.0.2-alpha.2 → 1.0.2-alpha.4
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 +111 -123
- 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/env/env.d.ts +5 -0
- package/dist/env/env.js +69 -0
- package/dist/helpers.js +4 -1
- package/dist/index.js +44 -86
- package/dist/renderer.d.ts +3 -1
- package/dist/renderer.js +151 -170
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
package/dist/bin/vatts.js
CHANGED
|
@@ -22,14 +22,17 @@ require('ts-node').register();
|
|
|
22
22
|
const { registerLoaders } = require('../loaders');
|
|
23
23
|
registerLoaders();
|
|
24
24
|
const { program } = require('commander');
|
|
25
|
+
const fs = require('fs');
|
|
26
|
+
const path = require('path');
|
|
27
|
+
const { Writable } = require('stream');
|
|
28
|
+
// Importa o Console do framework
|
|
29
|
+
const ConsoleModule = require('../api/console');
|
|
30
|
+
const Console = ConsoleModule.default;
|
|
31
|
+
const { Levels, Colors } = ConsoleModule;
|
|
25
32
|
program
|
|
26
33
|
.version('1.0.0')
|
|
27
34
|
.description('CLI to manage the application.');
|
|
28
|
-
// ---
|
|
29
|
-
const fs = require('fs');
|
|
30
|
-
const path = require('path');
|
|
31
|
-
// 'program' já deve estar definido no seu arquivo
|
|
32
|
-
// const { program } = require('commander');
|
|
35
|
+
// --- Helpers ---
|
|
33
36
|
/**
|
|
34
37
|
* Função centralizada para iniciar a aplicação
|
|
35
38
|
* @param {object} options - Opções vindas do commander
|
|
@@ -45,84 +48,75 @@ function initializeApp(options, isDev) {
|
|
|
45
48
|
};
|
|
46
49
|
// 1. Verifica se a flag --ssl foi ativada
|
|
47
50
|
if (options.ssl) {
|
|
48
|
-
const C = require("../api/console");
|
|
49
|
-
const { Levels } = C;
|
|
50
|
-
const Console = C.default;
|
|
51
51
|
const sslDir = path.resolve(process.cwd(), 'certs');
|
|
52
|
-
const keyPath = path.join(sslDir, 'key.pem');
|
|
53
|
-
const certPath = path.join(sslDir, 'cert.pem');
|
|
54
|
-
// (Você pode mudar para 'cert.key' se preferir, apenas ajuste os nomes aqui)
|
|
52
|
+
const keyPath = path.join(sslDir, 'key.pem');
|
|
53
|
+
const certPath = path.join(sslDir, 'cert.pem');
|
|
55
54
|
// 2. Verifica se os arquivos existem
|
|
56
55
|
if (fs.existsSync(keyPath) && fs.existsSync(certPath)) {
|
|
57
56
|
appOptions.ssl = {
|
|
58
57
|
key: keyPath,
|
|
59
58
|
cert: certPath
|
|
60
59
|
};
|
|
61
|
-
// 3. Adiciona a porta de redirecionamento
|
|
60
|
+
// 3. Adiciona a porta de redirecionamento
|
|
62
61
|
appOptions.ssl.redirectPort = options.httpRedirectPort || 80;
|
|
63
62
|
}
|
|
64
63
|
else {
|
|
65
|
-
Console.
|
|
66
|
-
process.exit(1);
|
|
64
|
+
Console.error(`SSL Error: Ensure that './certs/key.pem' and './certs/cert.pem' exist.`);
|
|
65
|
+
process.exit(1);
|
|
67
66
|
}
|
|
68
67
|
}
|
|
69
68
|
// 4. Inicia o helper com as opções
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
|
|
69
|
+
const helperModule = require("../helpers");
|
|
70
|
+
const helper = helperModule.default(appOptions);
|
|
71
|
+
helper.init();
|
|
73
72
|
}
|
|
74
|
-
// --- Comando DEV ---
|
|
75
|
-
program
|
|
76
|
-
.command('dev')
|
|
77
|
-
.description('Starts the application in development mode.')
|
|
78
|
-
.option('-p, --port <number>', 'Specifies the port to run on', '3000')
|
|
79
|
-
.option('-H, --hostname <string>', 'Specifies the hostname to run on', '0.0.0.0')
|
|
80
|
-
.option('--ssl', 'Activates HTTPS/SSL mode (requires ./ssl/key.pem and ./ssl/cert.pem)')
|
|
81
|
-
.option('--http-redirect-port <number>', 'Port for HTTP->HTTPS redirection', '80')
|
|
82
|
-
.action((options) => {
|
|
83
|
-
initializeApp(options, true); // Chama a função com dev: true
|
|
84
|
-
});
|
|
85
|
-
// --- Comando START (Produção) ---
|
|
86
|
-
program
|
|
87
|
-
.command('start')
|
|
88
|
-
.description('Starts the application in production mode.')
|
|
89
|
-
.option('-p, --port <number>', 'Specifies the port to run on', '3000')
|
|
90
|
-
.option('-H, --hostname <string>', 'Specifies the hostname to run on', '0.0.0.0')
|
|
91
|
-
.option('--ssl', 'Activates HTTPS/SSL mode (requires ./ssl/key.pem and ./ssl/cert.pem)')
|
|
92
|
-
.option('--http-redirect-port <number>', 'Port for HTTP->HTTPS redirection', '80')
|
|
93
|
-
.action((options) => {
|
|
94
|
-
initializeApp(options, false); // Chama a função com dev: false
|
|
95
|
-
});
|
|
96
73
|
/**
|
|
97
74
|
* Função corrigida para copiar diretórios recursivamente.
|
|
98
|
-
* Ela agora verifica se um item é um arquivo ou um diretório.
|
|
99
75
|
*/
|
|
100
76
|
function copyDirRecursive(src, dest) {
|
|
101
77
|
try {
|
|
102
|
-
// Garante que o diretório de destino exista
|
|
103
78
|
fs.mkdirSync(dest, { recursive: true });
|
|
104
|
-
// Usamos { withFileTypes: true } para evitar uma chamada extra de fs.statSync
|
|
105
79
|
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
106
80
|
for (let entry of entries) {
|
|
107
81
|
const srcPath = path.join(src, entry.name);
|
|
108
82
|
const destPath = path.join(dest, entry.name);
|
|
109
83
|
if (entry.isDirectory()) {
|
|
110
|
-
// Se for um diretório, chama a si mesma (recursão)
|
|
111
84
|
copyDirRecursive(srcPath, destPath);
|
|
112
85
|
}
|
|
113
86
|
else {
|
|
114
|
-
// Se for um arquivo, apenas copia
|
|
115
87
|
fs.copyFileSync(srcPath, destPath);
|
|
116
88
|
}
|
|
117
89
|
}
|
|
118
90
|
}
|
|
119
91
|
catch (error) {
|
|
120
|
-
|
|
121
|
-
// Lança o erro para parar o processo de exportação se a cópia falhar
|
|
92
|
+
Console.error(`Error copying ${src} to ${dest}:`, error);
|
|
122
93
|
throw error;
|
|
123
94
|
}
|
|
124
95
|
}
|
|
125
|
-
// ---
|
|
96
|
+
// --- Comandos ---
|
|
97
|
+
// Comando DEV
|
|
98
|
+
program
|
|
99
|
+
.command('dev')
|
|
100
|
+
.description('Starts the application in development mode.')
|
|
101
|
+
.option('-p, --port <number>', 'Specifies the port to run on', '3000')
|
|
102
|
+
.option('-H, --hostname <string>', 'Specifies the hostname to run on', '0.0.0.0')
|
|
103
|
+
.option('--ssl', 'Activates HTTPS/SSL mode (requires ./ssl/key.pem and ./ssl/cert.pem)')
|
|
104
|
+
.option('--http-redirect-port <number>', 'Port for HTTP->HTTPS redirection', '80')
|
|
105
|
+
.action((options) => {
|
|
106
|
+
initializeApp(options, true);
|
|
107
|
+
});
|
|
108
|
+
// Comando START (Produção)
|
|
109
|
+
program
|
|
110
|
+
.command('start')
|
|
111
|
+
.description('Starts the application in production mode.')
|
|
112
|
+
.option('-p, --port <number>', 'Specifies the port to run on', '3000')
|
|
113
|
+
.option('-H, --hostname <string>', 'Specifies the hostname to run on', '0.0.0.0')
|
|
114
|
+
.option('--ssl', 'Activates HTTPS/SSL mode (requires ./ssl/key.pem and ./ssl/cert.pem)')
|
|
115
|
+
.option('--http-redirect-port <number>', 'Port for HTTP->HTTPS redirection', '80')
|
|
116
|
+
.action((options) => {
|
|
117
|
+
initializeApp(options, false);
|
|
118
|
+
});
|
|
119
|
+
// Comando EXPORT
|
|
126
120
|
program
|
|
127
121
|
.command('export')
|
|
128
122
|
.description('Exports the application as static HTML to the "exported" folder.')
|
|
@@ -132,94 +126,82 @@ program
|
|
|
132
126
|
.option('--output-h-data <file>', 'Write the data-h value from <script id="__vatts_data__" data-h="..."> into this file')
|
|
133
127
|
.action(async (options) => {
|
|
134
128
|
const projectDir = process.cwd();
|
|
135
|
-
// Resolve output
|
|
136
|
-
// - se vier absoluto, usa como está
|
|
137
|
-
// - se vier relativo, resolve a partir do projeto
|
|
129
|
+
// Resolve output
|
|
138
130
|
const outputInput = typeof options.output === 'string' && options.output.trim().length
|
|
139
131
|
? options.output.trim()
|
|
140
132
|
: 'exported';
|
|
141
133
|
const exportDir = path.isAbsolute(outputInput)
|
|
142
134
|
? path.resolve(outputInput)
|
|
143
135
|
: path.resolve(projectDir, outputInput);
|
|
144
|
-
// Proteções: nunca permitir apagar a raiz do drive (ex: D:\) ou o root do SO (ex: C:\)
|
|
145
136
|
const exportDirResolved = path.resolve(exportDir);
|
|
146
|
-
const exportDirRoot = path.parse(exportDirResolved).root;
|
|
137
|
+
const exportDirRoot = path.parse(exportDirResolved).root;
|
|
147
138
|
const projectDirResolved = path.resolve(projectDir);
|
|
148
139
|
if (exportDirResolved === exportDirRoot) {
|
|
149
|
-
|
|
140
|
+
Console.error(`Refusing to use output directory at drive root: ${exportDirResolved}`);
|
|
141
|
+
process.exit(1);
|
|
150
142
|
}
|
|
151
|
-
// Também evita `--output .` ou `--output ..` que cairia no diretório do projeto (perigoso)
|
|
152
|
-
// Regra: output precisa estar dentro do projeto, ou dentro de uma subpasta do projeto.
|
|
153
|
-
// (mantém comportamento esperado e evita deletar o repo inteiro por acidente)
|
|
154
143
|
const relExportToProject = path.relative(projectDirResolved, exportDirResolved);
|
|
155
144
|
if (relExportToProject === '' || relExportToProject === '.' || relExportToProject.startsWith('..')) {
|
|
156
|
-
|
|
145
|
+
Console.error(`Refusing to export to ${exportDirResolved}. Use a subfolder like "exported" or an explicit path inside the project.`);
|
|
146
|
+
process.exit(1);
|
|
157
147
|
}
|
|
158
|
-
// assetsDir
|
|
159
|
-
// Permite usar '.' (raiz do output)
|
|
148
|
+
// assetsDir
|
|
160
149
|
const assetsDirInputRaw = typeof options.assetsDir === 'string' ? options.assetsDir : '.vatts';
|
|
161
150
|
const assetsDirInput = assetsDirInputRaw.trim().length ? assetsDirInputRaw.trim() : '.';
|
|
162
151
|
const assetsDirResolved = path.resolve(exportDirResolved, assetsDirInput);
|
|
163
152
|
const relAssetsToExport = path.relative(exportDirResolved, assetsDirResolved);
|
|
164
153
|
if (relAssetsToExport.startsWith('..') || path.isAbsolute(relAssetsToExport)) {
|
|
165
|
-
|
|
154
|
+
Console.error(`Invalid --assets-dir: must be inside output directory. Received: ${assetsDirInputRaw}`);
|
|
155
|
+
process.exit(1);
|
|
166
156
|
}
|
|
167
|
-
// Normaliza a pasta para uso em URL no HTML
|
|
168
|
-
// Se assets-dir for '.', relAssetsToExport vira '' e assetsBaseHref vira './'
|
|
169
157
|
const assetsDirUrl = relAssetsToExport.split(path.sep).join('/').replace(/^\.?\/?/, '');
|
|
170
|
-
const assetsBaseHref = '
|
|
171
|
-
|
|
158
|
+
const assetsBaseHref = '/.' + (assetsDirUrl.length ? assetsDirUrl + '/' : '');
|
|
159
|
+
Console.info('Starting export process...');
|
|
172
160
|
try {
|
|
173
|
-
// 1.
|
|
161
|
+
// 1. Limpa pasta de exportação
|
|
174
162
|
if (fs.existsSync(exportDirResolved)) {
|
|
175
|
-
|
|
163
|
+
Console.info('Cleaning existing export folder...');
|
|
176
164
|
fs.rmSync(exportDirResolved, { recursive: true, force: true });
|
|
177
165
|
}
|
|
178
166
|
fs.mkdirSync(exportDirResolved, { recursive: true });
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
//
|
|
183
|
-
const
|
|
184
|
-
const app = teste.default({ dev: false, port: 3000, hostname: '0.0.0.0', framework: 'native' });
|
|
167
|
+
// 2. Build
|
|
168
|
+
Console.info('Building application...');
|
|
169
|
+
const helperModule = require("../helpers");
|
|
170
|
+
// Usando dev: false para produção
|
|
171
|
+
const app = helperModule.default({ dev: false, port: 3000, hostname: '0.0.0.0', framework: 'native' });
|
|
185
172
|
await app.prepare();
|
|
186
|
-
|
|
173
|
+
Console.success('Build complete.');
|
|
174
|
+
// 3. Copia JavaScript
|
|
187
175
|
const distDir = path.join(projectDirResolved, '.vatts');
|
|
188
176
|
if (fs.existsSync(distDir)) {
|
|
189
|
-
|
|
177
|
+
Console.info('Copying JavaScript files...');
|
|
190
178
|
const exportDistDir = assetsDirResolved;
|
|
191
179
|
copyDirRecursive(distDir, exportDistDir);
|
|
192
|
-
console.log(`✅ JavaScript files copied to: ${path.relative(exportDirResolved, exportDistDir) || '.'}\n`);
|
|
193
180
|
}
|
|
194
|
-
// 4. Copia
|
|
181
|
+
// 4. Copia Public
|
|
195
182
|
const publicDir = path.join(projectDirResolved, 'public');
|
|
196
183
|
if (fs.existsSync(publicDir)) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
copyDirRecursive(publicDir, exportPublicDir);
|
|
200
|
-
console.log('✅ Public files copied\n');
|
|
184
|
+
Console.info('Copying public files...');
|
|
185
|
+
copyDirRecursive(publicDir, exportDirResolved);
|
|
201
186
|
}
|
|
202
|
-
// 5. Gera
|
|
187
|
+
// 5. Gera index.html
|
|
203
188
|
const shouldExtractHData = typeof options.outputHData === 'string' && options.outputHData.trim().length > 0;
|
|
204
189
|
const shouldRenderHtml = Boolean(options.html) || shouldExtractHData;
|
|
205
190
|
if (shouldRenderHtml) {
|
|
206
191
|
const writeHtmlToDisk = Boolean(options.html);
|
|
207
192
|
if (writeHtmlToDisk) {
|
|
208
|
-
|
|
193
|
+
Console.info('Generating index.html...');
|
|
209
194
|
}
|
|
210
195
|
else {
|
|
211
|
-
|
|
196
|
+
Console.info('Rendering HTML for h-data extraction...');
|
|
212
197
|
}
|
|
213
|
-
|
|
214
|
-
const { render } = require('../renderer');
|
|
198
|
+
const { renderAsStream } = require('../renderer');
|
|
215
199
|
const { loadRoutes, loadLayout, loadNotFound } = require('../router');
|
|
216
|
-
// Carrega as rotas para gerar o HTML
|
|
217
200
|
const userWebDir = path.join(projectDirResolved, 'src', 'web');
|
|
218
201
|
const userWebRoutesDir = path.join(userWebDir, 'routes');
|
|
219
202
|
const routes = loadRoutes(userWebRoutesDir);
|
|
220
203
|
loadLayout(userWebDir);
|
|
221
204
|
loadNotFound(userWebDir);
|
|
222
|
-
// Gera HTML para a rota raiz
|
|
223
205
|
const rootRoute = routes.find(r => r.pattern === '/') || routes[0];
|
|
224
206
|
if (rootRoute) {
|
|
225
207
|
const mockReq = {
|
|
@@ -229,49 +211,58 @@ program
|
|
|
229
211
|
hwebDev: false,
|
|
230
212
|
hotReloadManager: null
|
|
231
213
|
};
|
|
232
|
-
|
|
214
|
+
let html = '';
|
|
215
|
+
let resolveStream;
|
|
216
|
+
const streamComplete = new Promise(r => resolveStream = r);
|
|
217
|
+
const mockRes = new Writable({
|
|
218
|
+
write(chunk, encoding, callback) {
|
|
219
|
+
html += chunk.toString();
|
|
220
|
+
callback();
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
mockRes.setHeader = () => { };
|
|
224
|
+
mockRes.statusCode = 200;
|
|
225
|
+
mockRes.on('finish', () => {
|
|
226
|
+
resolveStream();
|
|
227
|
+
});
|
|
228
|
+
await renderAsStream({
|
|
233
229
|
req: mockReq,
|
|
230
|
+
res: mockRes,
|
|
234
231
|
route: rootRoute,
|
|
235
232
|
params: {},
|
|
236
233
|
allRoutes: routes
|
|
237
234
|
});
|
|
235
|
+
await streamComplete;
|
|
238
236
|
if (shouldExtractHData) {
|
|
239
237
|
const m = html.match(/<script\b[^>]*\bid=["']__vatts_data__["'][^>]*\bdata-h=["']([^"']*)["'][^>]*>/i);
|
|
240
238
|
if (!m || typeof m[1] !== 'string') {
|
|
241
239
|
throw new Error('Could not find <script id="__vatts_data__" data-h="..."> in rendered HTML.');
|
|
242
240
|
}
|
|
243
241
|
const hDataValue = m[1];
|
|
244
|
-
// O path do arquivo é relativo ao projeto por padrão
|
|
245
242
|
const outputHDataPathInput = options.outputHData.trim();
|
|
246
243
|
const outputHDataPath = path.isAbsolute(outputHDataPathInput)
|
|
247
244
|
? path.resolve(outputHDataPathInput)
|
|
248
245
|
: path.resolve(projectDirResolved, outputHDataPathInput);
|
|
249
246
|
fs.mkdirSync(path.dirname(outputHDataPath), { recursive: true });
|
|
250
247
|
fs.writeFileSync(outputHDataPath, hDataValue, 'utf8');
|
|
251
|
-
|
|
248
|
+
Console.success(`h-data written to: ${path.relative(projectDirResolved, outputHDataPath)}`);
|
|
252
249
|
}
|
|
253
250
|
if (writeHtmlToDisk) {
|
|
254
251
|
const scriptReplaced = html.replace(/\/_vatts\//g, assetsBaseHref);
|
|
255
252
|
const indexPath = path.join(exportDirResolved, 'index.html');
|
|
256
253
|
fs.writeFileSync(indexPath, scriptReplaced, 'utf8');
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
console.log('✅ HTML rendered (no index.html written)\n');
|
|
254
|
+
Console.success('index.html generated.');
|
|
261
255
|
}
|
|
262
256
|
}
|
|
263
257
|
}
|
|
264
258
|
else {
|
|
265
|
-
|
|
259
|
+
Console.info('Skipping index.html generation.');
|
|
266
260
|
}
|
|
267
|
-
|
|
268
|
-
console.log(`📂 Files exported to: ${exportDirResolved}\n`);
|
|
261
|
+
Console.success(`Export completed successfully to: ${path.relative(projectDirResolved, exportDirResolved)}`);
|
|
269
262
|
}
|
|
270
263
|
catch (error) {
|
|
271
|
-
|
|
272
|
-
console.error('❌ Error during export:', error);
|
|
264
|
+
Console.error('Error during export:', error);
|
|
273
265
|
process.exit(1);
|
|
274
266
|
}
|
|
275
267
|
});
|
|
276
|
-
// Faz o "parse" dos argumentos passados na linha de comando
|
|
277
268
|
program.parse(process.argv);
|