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 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
- // --- Comando DEV ---
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'); // Padrão 1: key.pem
53
- const certPath = path.join(sslDir, 'cert.pem'); // Padrão 2: 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 (útil para o initNativeServer)
60
+ // 3. Adiciona a porta de redirecionamento
62
61
  appOptions.ssl.redirectPort = options.httpRedirectPort || 80;
63
62
  }
64
63
  else {
65
- Console.logWithout(Levels.ERROR, null, `Ensure that './certs/key.pem' and './certs/cert.pem' exist.`, `--ssl flag was used, but the files were not found.`);
66
- process.exit(1); // Encerra o processo com erro
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 teste = require("../helpers");
71
- const t = teste.default(appOptions);
72
- t.init();
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
- console.error(`❌ Erro ao copiar ${src} para ${dest}:`, error);
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
- // --- INÍCIO DO SEU CÓDIGO (AGORA CORRIGIDO) ---
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; // ex: 'D:\\'
137
+ const exportDirRoot = path.parse(exportDirResolved).root;
147
138
  const projectDirResolved = path.resolve(projectDir);
148
139
  if (exportDirResolved === exportDirRoot) {
149
- throw new Error(`Refusing to use output directory at drive root: ${exportDirResolved}`);
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
- throw new Error(`Refusing to export to ${exportDirResolved}. Use a subfolder like "exported" or an explicit path inside the project.`);
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: sempre relativo ao exportDir (evita escrever fora sem querer)
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
- throw new Error(`Invalid --assets-dir: must be inside output directory. Received: ${assetsDirInputRaw}`);
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 = './' + (assetsDirUrl.length ? assetsDirUrl + '/' : '');
171
- console.log('🚀 Starting export...\n');
158
+ const assetsBaseHref = '/.' + (assetsDirUrl.length ? assetsDirUrl + '/' : '');
159
+ Console.info('Starting export process...');
172
160
  try {
173
- // 1. Cria a pasta exported (limpa se já existir)
161
+ // 1. Limpa pasta de exportação
174
162
  if (fs.existsSync(exportDirResolved)) {
175
- console.log('🗑️ Cleaning existing export folder...');
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
- console.log('✅ Export folder created\n');
180
- // 2. Inicializa e prepara o build
181
- console.log('🔨 Building application...');
182
- // ATENÇÃO: Ajuste o caminho deste 'require' conforme a estrutura do seu projeto!
183
- const teste = require("../helpers");
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
- console.log('Build complete\n');
173
+ Console.success('Build complete.');
174
+ // 3. Copia JavaScript
187
175
  const distDir = path.join(projectDirResolved, '.vatts');
188
176
  if (fs.existsSync(distDir)) {
189
- console.log('📦 Copying JavaScript files...');
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 a pasta public se existir
181
+ // 4. Copia Public
195
182
  const publicDir = path.join(projectDirResolved, 'public');
196
183
  if (fs.existsSync(publicDir)) {
197
- console.log('📁 Copying public files...');
198
- const exportPublicDir = path.join(exportDirResolved, 'public');
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 o index.html (opcional) / ou só renderiza para extrair h-data
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
- console.log('📝 Generating index.html...');
193
+ Console.info('Generating index.html...');
209
194
  }
210
195
  else {
211
- console.log('🧩 Rendering HTML to extract h-data (--output-h-data)...');
196
+ Console.info('Rendering HTML for h-data extraction...');
212
197
  }
213
- // ATENÇÃO: Ajuste os caminhos destes 'requires' conforme a estrutura do seu projeto!
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
- const html = await render({
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
- console.log(`✅ h-data written to: ${outputHDataPath}`);
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
- console.log('index.html generated\n');
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
- console.log('⏭️ Skipping index.html generation (--no-html)\n');
259
+ Console.info('Skipping index.html generation.');
266
260
  }
267
- console.log('🎉 Export completed successfully!');
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
- // Logar o erro completo (com stack trace) é mais útil
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);