@archznn/xavva 3.1.2 → 3.2.0
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/README.md +221 -12
- package/package.json +3 -2
- package/src/commands/AuditCommand.ts +12 -10
- package/src/commands/BuildCommand.ts +9 -7
- package/src/commands/ChangelogCommand.ts +5 -5
- package/src/commands/CleanCommand.ts +242 -0
- package/src/commands/CompletionCommand.ts +7 -7
- package/src/commands/DbCommand.ts +43 -14
- package/src/commands/DeployCommand.ts +252 -229
- package/src/commands/DepsCommand.ts +174 -174
- package/src/commands/DockerCommand.ts +35 -4
- package/src/commands/DoctorCommand.ts +252 -239
- package/src/commands/EncodingCommand.ts +26 -19
- package/src/commands/HealthCommand.ts +7 -7
- package/src/commands/HelpCommand.ts +34 -14
- package/src/commands/HistoryCommand.ts +5 -5
- package/src/commands/HttpCommand.ts +27 -1
- package/src/commands/IdeCommand.ts +313 -0
- package/src/commands/InitCommand.ts +26 -25
- package/src/commands/LogsCommand.ts +8 -6
- package/src/commands/ProfilesCommand.ts +6 -6
- package/src/commands/RedoCommand.ts +2 -2
- package/src/commands/RunCommand.ts +64 -24
- package/src/commands/StartCommand.ts +9 -7
- package/src/commands/TestCommand.ts +25 -1
- package/src/commands/TomcatCommand.ts +232 -88
- package/src/config/versions.ts +111 -9
- package/src/di/container.ts +239 -105
- package/src/errors/ErrorHandler.ts +23 -19
- package/src/errors/errorMessages.ts +235 -0
- package/src/index.ts +20 -6
- package/src/logging/FileLogger.ts +235 -0
- package/src/logging/Logger.ts +545 -0
- package/src/logging/OperationLogger.ts +296 -0
- package/src/logging/ProgressLogger.ts +187 -0
- package/src/logging/TableLogger.ts +246 -0
- package/src/logging/colors.ts +167 -0
- package/src/logging/constants.ts +176 -0
- package/src/logging/formatters.ts +337 -0
- package/src/logging/index.ts +93 -0
- package/src/logging/types.ts +64 -0
- package/src/plugins/PluginManager.ts +325 -0
- package/src/plugins/types.ts +82 -0
- package/src/services/AuditService.ts +5 -3
- package/src/services/BuildService.ts +15 -17
- package/src/services/DashboardService.ts +14 -3
- package/src/services/DbService.ts +35 -34
- package/src/services/DependencyAnalyzerService.ts +18 -18
- package/src/services/DependencyCacheService.ts +303 -0
- package/src/services/DeployWatcher.ts +127 -23
- package/src/services/DockerService.ts +3 -3
- package/src/services/EmbeddedTomcatService.ts +13 -12
- package/src/services/FileWatcher.ts +15 -7
- package/src/services/HttpService.ts +5 -5
- package/src/services/LogAnalyzer.ts +26 -22
- package/src/services/PerformanceProfiler.ts +267 -0
- package/src/services/ProjectService.ts +3 -0
- package/src/services/TestService.ts +3 -3
- package/src/services/TomcatService.ts +46 -25
- package/src/services/tomcat/TomcatBackupManager.ts +330 -0
- package/src/services/tomcat/TomcatChecksumVerifier.ts +211 -0
- package/src/services/tomcat/TomcatCompatibilityChecker.ts +298 -0
- package/src/services/tomcat/TomcatDownloadCache.ts +250 -0
- package/src/services/tomcat/TomcatDownloadService.ts +335 -0
- package/src/services/tomcat/TomcatInstallerService.ts +474 -0
- package/src/services/tomcat/TomcatMirrorManager.ts +181 -0
- package/src/services/tomcat/index.ts +36 -0
- package/src/services/tomcat/types.ts +120 -0
- package/src/types/args.ts +68 -1
- package/src/types/configSchema.ts +174 -0
- package/src/utils/ChangelogGenerator.ts +11 -11
- package/src/utils/LoggerLevel.ts +44 -20
- package/src/utils/ProgressBar.ts +87 -46
- package/src/utils/argsParser.ts +260 -0
- package/src/utils/config.ts +340 -189
- package/src/utils/constants.ts +87 -9
- package/src/utils/dryRun.ts +192 -0
- package/src/utils/processManager.ts +23 -7
- package/src/utils/security.ts +293 -0
- package/src/utils/ui.ts +299 -428
package/src/utils/ui.ts
CHANGED
|
@@ -1,407 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI Utils - Adaptador para o novo sistema de logging
|
|
3
|
+
*
|
|
4
|
+
* Mantém compatibilidade com código existente enquanto usa
|
|
5
|
+
* internamente o novo sistema de logging em src/logging/
|
|
6
|
+
*
|
|
7
|
+
* @deprecated Use o novo sistema de logging em src/logging/
|
|
8
|
+
*/
|
|
9
|
+
|
|
1
10
|
import pkg from "../../package.json";
|
|
2
11
|
import type { DashboardService } from "../services/DashboardService";
|
|
12
|
+
import { Logger, Colors, Icons, colorize, stripAnsi, padText } from "../logging";
|
|
13
|
+
import { LAYOUT, NOISE_PATTERNS, ESSENTIAL_PATTERNS, SPINNER_FRAMES, SPINNER_INTERVAL } from "../logging/constants";
|
|
3
14
|
|
|
4
|
-
//
|
|
5
|
-
const C =
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
// Estados
|
|
17
|
-
success: "\x1b[32m", // Green
|
|
18
|
-
successBright: "\x1b[92m", // Bright Green
|
|
19
|
-
warning: "\x1b[33m", // Yellow
|
|
20
|
-
warningBright: "\x1b[93m", // Bright Yellow
|
|
21
|
-
error: "\x1b[31m", // Red
|
|
22
|
-
errorBright: "\x1b[91m", // Bright Red
|
|
23
|
-
info: "\x1b[34m", // Blue
|
|
24
|
-
|
|
25
|
-
// Neutros
|
|
26
|
-
white: "\x1b[37m",
|
|
27
|
-
gray: "\x1b[90m",
|
|
28
|
-
lightGray: "\x1b[37m",
|
|
29
|
-
darkGray: "\x1b[38;5;240m",
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export class Logger {
|
|
33
|
-
public static readonly C = C;
|
|
15
|
+
// Re-exporta Cores para compatibilidade
|
|
16
|
+
export const C = Colors;
|
|
17
|
+
|
|
18
|
+
// Largura das colunas para alinhamento
|
|
19
|
+
export const COL = LAYOUT.columns;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @deprecated Use Logger de src/logging/ diretamente
|
|
23
|
+
*
|
|
24
|
+
* Classe Logger legada - adaptador para o novo sistema
|
|
25
|
+
*/
|
|
26
|
+
export class LoggerLegacy {
|
|
34
27
|
private static dashboard: DashboardService | null = null;
|
|
35
28
|
private static activeSpinner: { stop: (success?: boolean) => void } | null = null;
|
|
36
|
-
private static
|
|
29
|
+
private static sectionOpen = false;
|
|
30
|
+
private static logger = Logger.getInstance();
|
|
37
31
|
|
|
38
32
|
static setDashboard(dashboard: DashboardService) {
|
|
39
33
|
this.dashboard = dashboard;
|
|
34
|
+
// Também configura no novo logger se necessário
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Helper: remove ANSI codes para calcular tamanho
|
|
38
|
+
private static plain(s: string): string {
|
|
39
|
+
return stripAnsi(s);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Helper: pad com consideração de ANSI codes
|
|
43
|
+
private static pad(s: string, len: number): string {
|
|
44
|
+
return padText(s, len);
|
|
40
45
|
}
|
|
41
46
|
|
|
42
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Banner clean e moderno
|
|
49
|
+
*/
|
|
43
50
|
static banner(command?: string, profile?: string, encoding?: string) {
|
|
44
51
|
console.clear();
|
|
45
|
-
const git = this.getGitContext();
|
|
46
52
|
const name = process.cwd().split(/[/\\]/).pop() || "project";
|
|
47
53
|
const now = new Date().toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' });
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
// Remove ANSI codes para calcular tamanho
|
|
51
|
-
const plain = (s: string) => s.replace(/\x1b\[\d+m/g, '');
|
|
52
|
-
|
|
53
|
-
// Cria uma linha com conteúdo alinhado à esquerda
|
|
54
|
-
const row = (content: string) => {
|
|
55
|
-
const pad = W - plain(content).length;
|
|
56
|
-
return `${C.gray}║${C.reset} ${content}${' '.repeat(Math.max(0, pad))}${C.gray}║${C.reset}`;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
// Linha superior
|
|
60
|
-
console.log(`${C.gray}╔══════════════════════════════════════════════════════╗${C.reset}`);
|
|
61
|
-
|
|
62
|
-
// Linha 1: XAVVA v2.2.0 + hora alinhada à direita
|
|
63
|
-
const verPlain = `XAVVA v${pkg.version}`; // "XAVVA v2.2.0" = 12 chars
|
|
64
|
-
const timePlain = now; // 5 chars
|
|
65
|
-
const gap1 = W - verPlain.length - timePlain.length - 1; // -1 para deixar 1 espaço antes do ║
|
|
66
|
-
const line1Content = `${C.primary}${C.bold}XAVVA${C.reset}${C.gray} v${pkg.version}${C.reset}${' '.repeat(Math.max(1, gap1))}${C.dim}${now}${C.reset}`;
|
|
67
|
-
console.log(row(line1Content));
|
|
54
|
+
const git = this.getGitContext();
|
|
68
55
|
|
|
69
|
-
//
|
|
70
|
-
|
|
56
|
+
// Header linha única
|
|
57
|
+
const left = `${C.primary}${C.bold}xavva${C.reset} v${pkg.version}`;
|
|
58
|
+
const center = `${C.white}${C.bold}${name}${C.reset}`;
|
|
59
|
+
const right = `${C.dim}${now}${C.reset}`;
|
|
71
60
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const gitStatus = this.getGitStatus();
|
|
75
|
-
const dirty = gitStatus.dirty ? '*' : '';
|
|
76
|
-
const author = git.author ? git.author.split(' ')[0].slice(0, 10) : '';
|
|
77
|
-
const hash = git.hash ? git.hash.slice(0, 7) : '';
|
|
78
|
-
const branchDisplay = git.branch.slice(0, 20); // Limita branch
|
|
79
|
-
const gitLine = `${C.gray}git:${C.reset}${C.secondary}${branchDisplay}${dirty}${C.reset} ${C.dim}${hash}${C.reset} ${C.gray}by${C.reset} ${C.dim}${author}${C.reset}`;
|
|
80
|
-
console.log(row(gitLine));
|
|
81
|
-
}
|
|
61
|
+
console.log();
|
|
62
|
+
console.log(` ${left} ${center} ${right}`);
|
|
82
63
|
|
|
83
|
-
//
|
|
84
|
-
console.log(
|
|
64
|
+
// Linha divisória simples
|
|
65
|
+
console.log(` ${C.darkGray}${'─'.repeat(60)}${C.reset}`);
|
|
85
66
|
|
|
86
|
-
//
|
|
87
|
-
const
|
|
88
|
-
if (command)
|
|
89
|
-
if (profile)
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
92
|
-
if (
|
|
67
|
+
// Contexto em uma linha
|
|
68
|
+
const ctx: string[] = [];
|
|
69
|
+
if (command) ctx.push(`${C.primary}${command}${C.reset}`);
|
|
70
|
+
if (profile) ctx.push(`${C.warning}${profile}${C.reset}`);
|
|
71
|
+
const java = this.getJavaVersion();
|
|
72
|
+
if (java) ctx.push(`java:${C.dim}${java}${C.reset}`);
|
|
73
|
+
if (git.branch) ctx.push(`${C.gray}git:${git.branch}${git.dirty ? '*' : ''}${C.reset}`);
|
|
93
74
|
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
const cfgLine = `${C.dim}mode${C.reset} : ${cfg.join(sep)}`;
|
|
97
|
-
console.log(row(cfgLine));
|
|
75
|
+
if (ctx.length > 0) {
|
|
76
|
+
console.log(` ${ctx.join(' ')}`);
|
|
98
77
|
}
|
|
99
78
|
|
|
100
|
-
|
|
101
|
-
const mem = process.memoryUsage();
|
|
102
|
-
const mb = Math.round((mem.heapUsed || mem.rss || 0) / 1024 / 1024);
|
|
103
|
-
console.log(row(`${C.dim}mem${C.reset} : ${mb}MB ${C.gray}heap${C.reset}`));
|
|
79
|
+
console.log();
|
|
104
80
|
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
console.log(
|
|
81
|
+
// Cabeçalho da tabela
|
|
82
|
+
console.log(` ${C.bold}${this.pad('NOME', COL.name)}${this.pad('STATUS', COL.status)}INFO${C.reset}`);
|
|
83
|
+
console.log(` ${C.darkGray}${'─'.repeat(60)}${C.reset}`);
|
|
108
84
|
|
|
109
|
-
|
|
110
|
-
console.log(`${C.gray}╚══════════════════════════════════════════════════════╝${C.reset}`);
|
|
111
|
-
console.log();
|
|
85
|
+
this.sectionOpen = true;
|
|
112
86
|
}
|
|
113
87
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
} catch {
|
|
120
|
-
return { dirty: false, modified: 0 };
|
|
121
|
-
}
|
|
88
|
+
/**
|
|
89
|
+
* Status lateral estilo docker-compose
|
|
90
|
+
*/
|
|
91
|
+
static status(name: string, status: 'pending' | 'running' | 'done' | 'error' | 'warning', info?: string) {
|
|
92
|
+
this.logger.status(name, status, info);
|
|
122
93
|
}
|
|
123
94
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Seção de arquivos (para watch mode)
|
|
97
|
+
*/
|
|
98
|
+
static filesSection(title: string = "Changes") {
|
|
99
|
+
this.logger.section(title);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Arquivo individual na seção
|
|
104
|
+
*/
|
|
105
|
+
static file(name: string, action: 'changed' | 'compiled' | 'synced' | 'error', path?: string) {
|
|
106
|
+
this.logger.file(name, action, path);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Resultado/sumário
|
|
111
|
+
*/
|
|
112
|
+
static summary(text: string) {
|
|
113
|
+
this.logger.step(text);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* URL formatada
|
|
118
|
+
*/
|
|
119
|
+
static url(label: string, url: string) {
|
|
120
|
+
this.logger.url(label, url);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Divisor simples
|
|
125
|
+
*/
|
|
126
|
+
static divider() {
|
|
127
|
+
this.logger.divider();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Finalização
|
|
132
|
+
*/
|
|
133
|
+
static done() {
|
|
134
|
+
this.logger.newline();
|
|
144
135
|
}
|
|
145
136
|
|
|
146
|
-
//
|
|
137
|
+
// ========== Métodos legados - mantidos para compatibilidade ==========
|
|
138
|
+
|
|
147
139
|
static section(title: string) {
|
|
148
|
-
|
|
140
|
+
this.logger.section(title);
|
|
149
141
|
}
|
|
150
142
|
|
|
151
143
|
static endSection() {
|
|
152
|
-
|
|
144
|
+
this.logger.newline();
|
|
153
145
|
}
|
|
154
146
|
|
|
155
|
-
// Configurações em formato chave: valor alinhado
|
|
156
147
|
static config(label: string, value: string | number | boolean) {
|
|
157
|
-
|
|
158
|
-
const isBool = typeof value === 'boolean';
|
|
159
|
-
const displayValue = isBool
|
|
160
|
-
? (value ? `${C.successBright}✓${C.reset} ${C.success}enabled${C.reset}` : `${C.gray}○${C.reset} ${C.gray}disabled${C.reset}`)
|
|
161
|
-
: `${C.white}${valueStr}${C.reset}`;
|
|
162
|
-
|
|
163
|
-
console.log(`${C.gray}│${C.reset} ${C.dim}${label.padEnd(12)}${C.reset} ${C.gray}:${C.reset} ${displayValue}`);
|
|
148
|
+
this.logger.config(label, value);
|
|
164
149
|
}
|
|
165
150
|
|
|
166
|
-
// Status com ícones minimalistas
|
|
167
151
|
static ready(msg: string) {
|
|
168
|
-
|
|
152
|
+
this.logger.ready(msg);
|
|
169
153
|
}
|
|
170
154
|
|
|
171
155
|
static info(label: string, value?: string) {
|
|
172
156
|
if (value) {
|
|
173
|
-
|
|
157
|
+
this.logger.info(`${label}: ${value}`);
|
|
174
158
|
} else {
|
|
175
|
-
|
|
159
|
+
this.logger.info(label);
|
|
176
160
|
}
|
|
177
161
|
}
|
|
178
162
|
|
|
179
163
|
static success(msg: string) {
|
|
180
|
-
|
|
164
|
+
this.logger.success(msg);
|
|
181
165
|
}
|
|
182
166
|
|
|
183
167
|
static error(msg: string) {
|
|
184
|
-
|
|
168
|
+
this.logger.error(msg);
|
|
185
169
|
}
|
|
186
170
|
|
|
187
171
|
static warn(msg: string) {
|
|
188
|
-
|
|
172
|
+
this.logger.warn(msg);
|
|
189
173
|
}
|
|
190
174
|
|
|
191
175
|
static debug(msg: string) {
|
|
192
|
-
|
|
176
|
+
this.logger.debug(msg);
|
|
193
177
|
}
|
|
194
178
|
|
|
195
179
|
static step(msg: string) {
|
|
196
|
-
|
|
180
|
+
this.logger.step(msg);
|
|
197
181
|
}
|
|
198
182
|
|
|
199
183
|
static log(msg: string) {
|
|
200
184
|
console.log(msg);
|
|
201
185
|
}
|
|
202
186
|
|
|
203
|
-
static
|
|
204
|
-
|
|
187
|
+
static newline() {
|
|
188
|
+
this.logger.newline();
|
|
205
189
|
}
|
|
206
190
|
|
|
207
|
-
|
|
208
|
-
|
|
191
|
+
// Watch mode - arquivo modificado (legado)
|
|
192
|
+
static fileChanged(filepath: string) {
|
|
193
|
+
const filename = filepath.split(/[/\\]/).pop() || filepath;
|
|
194
|
+
this.file(filename, 'changed');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Watch mode - arquivo compilado/sincronizado (legado)
|
|
198
|
+
static fileSynced(filename: string, action: 'compiled' | 'synced' | 'reloaded' = 'synced') {
|
|
199
|
+
const map = { compiled: 'compiled', synced: 'synced', reloaded: 'reloaded' };
|
|
200
|
+
this.file(filename, action === 'reloaded' ? 'synced' : action, `[${map[action]}]`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Watch mode - batch de arquivos (legado)
|
|
204
|
+
static filesBatch(count: number, action: string) {
|
|
205
|
+
this.logger.summary(`${count} arquivo(s) ${action}`);
|
|
209
206
|
}
|
|
210
207
|
|
|
211
208
|
static watcher(msg: string, _type?: string) {
|
|
212
|
-
|
|
209
|
+
this.logger.debug(`watch: ${msg}`);
|
|
213
210
|
}
|
|
214
211
|
|
|
215
212
|
static watch(msg: string) {
|
|
216
|
-
|
|
213
|
+
this.logger.debug(`watch: ${msg}`);
|
|
217
214
|
}
|
|
218
215
|
|
|
219
216
|
static process(msg: string) {
|
|
220
|
-
|
|
217
|
+
this.logger.status('process', 'running', msg);
|
|
221
218
|
}
|
|
222
219
|
|
|
223
220
|
static build(msg: string) {
|
|
224
|
-
|
|
221
|
+
if (msg.includes('completed') || msg.includes('done') || msg.includes('concluído')) {
|
|
222
|
+
this.logger.status('build', 'done', msg);
|
|
223
|
+
} else if (msg.includes('compil')) {
|
|
224
|
+
this.logger.status('build', 'running', msg);
|
|
225
|
+
} else {
|
|
226
|
+
this.logger.status('build', 'pending', msg);
|
|
227
|
+
}
|
|
225
228
|
}
|
|
226
229
|
|
|
227
230
|
static server(msg: string) {
|
|
228
|
-
|
|
231
|
+
if (msg.includes('ready') || msg.includes('done') || msg.includes('pronto')) {
|
|
232
|
+
this.logger.status('server', 'done', msg);
|
|
233
|
+
} else {
|
|
234
|
+
this.logger.status('server', 'running', msg);
|
|
235
|
+
}
|
|
229
236
|
}
|
|
230
237
|
|
|
231
238
|
static hotswap(msg: string) {
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// URL formatada de forma destacada
|
|
236
|
-
static url(label: string, url: string) {
|
|
237
|
-
console.log(`${C.gray}│${C.reset} ${C.dim}${label}${C.reset} ${C.gray}:${C.reset} ${C.primaryBright}${C.bold}${url}${C.reset}`);
|
|
239
|
+
this.logger.debug(`hotswap: ${msg}`);
|
|
238
240
|
}
|
|
239
241
|
|
|
240
|
-
|
|
242
|
+
/**
|
|
243
|
+
* Spinner moderno
|
|
244
|
+
*/
|
|
241
245
|
static spinner(msg: string) {
|
|
242
246
|
if (this.dashboard?.isTuiActive()) {
|
|
243
247
|
return this.dashboard.spinner(msg);
|
|
244
248
|
}
|
|
245
249
|
|
|
246
|
-
|
|
250
|
+
// Usa spinner ASCII simples para melhor compatibilidade com Windows
|
|
251
|
+
const frames = ['|', '/', '-', '\\'];
|
|
247
252
|
let i = 0;
|
|
248
253
|
|
|
249
|
-
process.stdout.write(
|
|
254
|
+
process.stdout.write(" ");
|
|
250
255
|
process.stdout.write("\x1B[?25l");
|
|
251
256
|
|
|
252
257
|
const timer = setInterval(() => {
|
|
253
|
-
process.stdout.write(`\r
|
|
258
|
+
process.stdout.write(`\r ${C.primary}${frames[i]}${C.reset} ${C.dim}${msg}${C.reset}`);
|
|
254
259
|
i = (i + 1) % frames.length;
|
|
255
|
-
},
|
|
260
|
+
}, SPINNER_INTERVAL);
|
|
256
261
|
|
|
257
262
|
return (success = true) => {
|
|
258
263
|
clearInterval(timer);
|
|
259
264
|
process.stdout.write("\x1B[?25h");
|
|
260
265
|
if (success) {
|
|
261
|
-
console.log(`\r
|
|
266
|
+
console.log(`\r ${C.success}✓${C.reset} ${msg}`);
|
|
262
267
|
} else {
|
|
263
|
-
console.log(`\r
|
|
268
|
+
console.log(`\r ${C.error}✗${C.reset} ${C.error}${msg}${C.reset}`);
|
|
264
269
|
}
|
|
265
270
|
};
|
|
266
271
|
}
|
|
267
272
|
|
|
268
|
-
//
|
|
269
|
-
static divider() {
|
|
270
|
-
console.log(`${C.gray}├────────────────────────────────────────────────────────┤${C.reset}`);
|
|
271
|
-
}
|
|
273
|
+
// ========== Helpers privados ==========
|
|
272
274
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
275
|
+
private static getGitStatus(): { dirty: boolean; modified: number } {
|
|
276
|
+
try {
|
|
277
|
+
const result = Bun.spawnSync(["git", "status", "--porcelain"]);
|
|
278
|
+
const lines = result.stdout.toString().trim().split('\n').filter(l => l.trim());
|
|
279
|
+
return { dirty: lines.length > 0, modified: lines.length };
|
|
280
|
+
} catch {
|
|
281
|
+
return { dirty: false, modified: 0 };
|
|
282
|
+
}
|
|
277
283
|
}
|
|
278
284
|
|
|
279
|
-
|
|
280
|
-
static getGitContext(): { branch: string; author: string; hash: string } {
|
|
285
|
+
private static getGitContext(): { branch: string; dirty: boolean } {
|
|
281
286
|
try {
|
|
282
287
|
const branch = Bun.spawnSync(["git", "rev-parse", "--abbrev-ref", "HEAD"]).stdout.toString().trim();
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
return { branch, author, hash };
|
|
288
|
+
const status = this.getGitStatus();
|
|
289
|
+
return { branch, dirty: status.dirty };
|
|
286
290
|
} catch {
|
|
287
|
-
return { branch: "",
|
|
291
|
+
return { branch: "", dirty: false };
|
|
288
292
|
}
|
|
289
293
|
}
|
|
290
294
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
+
private static getJavaVersion(): string | null {
|
|
296
|
+
try {
|
|
297
|
+
const javaBin = process.env.JAVA_HOME
|
|
298
|
+
? `${process.env.JAVA_HOME}/bin/java`
|
|
299
|
+
: 'java';
|
|
300
|
+
const result = Bun.spawnSync([javaBin, "-version"]);
|
|
301
|
+
const output = (result.stderr?.toString() || result.stdout?.toString() || '');
|
|
302
|
+
const match = output.match(/version "?(\d+(?:\.\d+)?)/);
|
|
303
|
+
if (match) {
|
|
304
|
+
const v = match[1];
|
|
305
|
+
if (output.toLowerCase().includes('dcevm') || output.toLowerCase().includes('jbr')) {
|
|
306
|
+
return `${v}+dcevm`;
|
|
307
|
+
}
|
|
308
|
+
return v;
|
|
309
|
+
}
|
|
310
|
+
return null;
|
|
311
|
+
} catch {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
295
315
|
|
|
316
|
+
// ========== Log Formatting (mantidos para compatibilidade) ==========
|
|
317
|
+
|
|
296
318
|
static isSystemNoise(line: string): boolean {
|
|
297
|
-
|
|
298
|
-
"Using CATALINA_", "Using JRE_HOME", "Using CLASSPATH",
|
|
299
|
-
"Scanning for projects...", "Building ", "--- ", "+++ ",
|
|
300
|
-
"Arquivos processados em", "milliseconds",
|
|
301
|
-
"SLF4J: ", "Discovered plugins:",
|
|
302
|
-
"enhanced with plugin initialization", "Hotswap ready",
|
|
303
|
-
"autoHotswap.delay", "watchResources=false",
|
|
304
|
-
// HotswapAgent noise
|
|
305
|
-
"TreeWatcherNIO", "HOTSWAP AGENT", "org.hotswap.agent",
|
|
306
|
-
// Jersey/JAX-RS noise
|
|
307
|
-
"org.glassfish.jersey", "The (sub)resource method",
|
|
308
|
-
// Tomcat noise
|
|
309
|
-
"org.apache.catalina", "org.apache.jasper",
|
|
310
|
-
];
|
|
311
|
-
return noise.some(n => line.includes(n));
|
|
319
|
+
return NOISE_PATTERNS.system.some(n => line.includes(n));
|
|
312
320
|
}
|
|
313
321
|
|
|
314
322
|
static isEssential(line: string): boolean {
|
|
315
|
-
return
|
|
316
|
-
line.includes("Caused by") || line.includes("at ") || line.includes("... ") ||
|
|
317
|
-
line.includes("Server startup in") || line.includes("HOTSWAP AGENT:");
|
|
323
|
+
return ESSENTIAL_PATTERNS.some(pattern => line.includes(pattern));
|
|
318
324
|
}
|
|
319
325
|
|
|
320
|
-
// Sumarização de logs do Tomcat (simplificada)
|
|
321
326
|
static summarize(line: string): string {
|
|
322
327
|
if (this.isSystemNoise(line)) return "";
|
|
323
328
|
|
|
324
|
-
// Server startup
|
|
325
329
|
const startupMatch = line.match(/Server startup in.*?([\d,]+)\s*ms/);
|
|
326
330
|
if (startupMatch) {
|
|
327
331
|
const time = startupMatch[1].replace(",", "");
|
|
328
332
|
const seconds = (parseInt(time) / 1000).toFixed(1);
|
|
329
|
-
return `${C.success}
|
|
333
|
+
return `${C.success}pronto ${C.gray}em ${C.white}${seconds}s${C.reset}`;
|
|
330
334
|
}
|
|
331
335
|
|
|
332
|
-
// Hotswap com rate limiting (evita spam)
|
|
333
336
|
if (line.includes("HOTSWAP AGENT") && line.includes("RELOAD")) {
|
|
334
|
-
|
|
335
|
-
if (now - this.lastHotswapTime < 2000) {
|
|
336
|
-
this.hotswapCount++;
|
|
337
|
-
return ""; // Silencia se dentro de 2s
|
|
338
|
-
}
|
|
339
|
-
this.lastHotswapTime = now;
|
|
340
|
-
const count = this.hotswapCount > 0 ? ` ${C.gray}(${this.hotswapCount} more)${C.reset}` : "";
|
|
341
|
-
this.hotswapCount = 0;
|
|
342
|
-
return `${C.secondary}↻ hotswap${C.reset}${count}`;
|
|
337
|
+
return `${C.primary}↻ hotswap${C.reset}`;
|
|
343
338
|
}
|
|
344
339
|
|
|
345
|
-
// Erros de compilação
|
|
346
340
|
const compilationError = line.match(/\[ERROR\].*?(\w+\.java):\[(\d+).*?\]\s*(.+)/);
|
|
347
341
|
if (compilationError) {
|
|
348
342
|
const [, file, lineNum, msg] = compilationError;
|
|
349
|
-
return `${C.error}✗ ${C.white}${file}${C.gray}:${lineNum}${C.reset} ${C.gray}${msg.slice(0,
|
|
343
|
+
return `${C.error}✗ ${C.white}${file}${C.gray}:${lineNum}${C.reset} ${C.gray}${msg.slice(0, 80)}${C.reset}`;
|
|
350
344
|
}
|
|
351
345
|
|
|
352
|
-
// Erros SEVERE
|
|
353
346
|
if (line.includes("SEVERE") || line.includes("Exception")) {
|
|
354
|
-
return `${C.error}✗ ${C.gray}${line.slice(0,
|
|
347
|
+
return `${C.error}✗ ${C.gray}${line.slice(0, 200)}${C.reset}`;
|
|
355
348
|
}
|
|
356
349
|
|
|
357
|
-
//
|
|
350
|
+
// WARNING/ADVERTÊNCIA: só mostra no modo verbose, não no modo normal
|
|
351
|
+
// (removido do summarize para não aparecer em modo não-verbose)
|
|
358
352
|
if (line.includes("WARNING") || line.includes("ADVERTÊNCIA")) {
|
|
359
|
-
|
|
360
|
-
return `${C.warning}⚠ ${C.gray}${line.slice(0, 80)}${C.reset}`;
|
|
353
|
+
return ""; // Silenciado no modo normal - use --verbose para ver
|
|
361
354
|
}
|
|
362
355
|
|
|
363
356
|
return "";
|
|
364
357
|
}
|
|
365
358
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
/^
|
|
371
|
-
/^Using CLASSPATH/,
|
|
372
|
-
/^Using CATALINA_OPTS/,
|
|
373
|
-
/^NOTE: Picked up JDK_JAVA_OPTIONS/,
|
|
374
|
-
/^HOTSWAP AGENT:.*Plugin.*initialized in ClassLoader/,
|
|
375
|
-
/^HOTSWAP AGENT:.*Registering directory/,
|
|
376
|
-
/^HOTSWAP AGENT:.*WARNING.*TreeWatcherNIO.*Unable to watch/,
|
|
377
|
-
/^HOTSWAP AGENT:.*INFO.*TreeWatcherNIO/,
|
|
378
|
-
/^HOTSWAP AGENT:.*INFO.*PluginRegistry.*Discovered plugins/,
|
|
379
|
-
/^HOTSWAP AGENT:.*INFO.*HotswapAgent.*Loading Hotswap agent/,
|
|
380
|
-
/^HOTSWAP AGENT:.*INFO.*TomcatPlugin.*Tomcat plugin initialized/,
|
|
381
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*VersionLoggerListener/,
|
|
382
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*AprLifecycleListener/,
|
|
383
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Command line argument/,
|
|
384
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*CATALINA_BASE/,
|
|
385
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*CATALINA_HOME/,
|
|
386
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Server version/,
|
|
387
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Server built/,
|
|
388
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*OS Name/,
|
|
389
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*OS Version/,
|
|
390
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Architecture/,
|
|
391
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Java Home/,
|
|
392
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*JVM Version/,
|
|
393
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*JVM Vendor/,
|
|
394
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Loaded Apache Tomcat Native/,
|
|
395
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*APR capabilities/,
|
|
396
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*APR\/OpenSSL configuration/,
|
|
397
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*OpenSSL successfully initialized/,
|
|
398
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Server initialization in/,
|
|
399
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Starting service/,
|
|
400
|
-
/^\d{2}-[A-Za-z]+-\d{4}\s+\d{2}:\d{2}:\d{2}\.\d+\s+(INFORMAÇÕES|INFO)\s+\[main\].*Starting Servlet engine/,
|
|
401
|
-
/^ParallelWebappClassLoader/,
|
|
359
|
+
private static tomcatNoisePatterns = NOISE_PATTERNS.tomcat;
|
|
360
|
+
|
|
361
|
+
// Padrões que são noise em modo não-verbose, mas úteis no verbose
|
|
362
|
+
private static tomcatVerbosePatterns = [
|
|
363
|
+
/^HOTSWAP AGENT:/,
|
|
402
364
|
/^context:/,
|
|
403
365
|
/^delegate:/,
|
|
404
|
-
|
|
366
|
+
/^----------> Parent Classloader:/,
|
|
405
367
|
/^java\.net\.URLClassLoader/,
|
|
406
368
|
];
|
|
407
369
|
|
|
@@ -409,238 +371,147 @@ export class Logger {
|
|
|
409
371
|
return this.tomcatNoisePatterns.some(p => p.test(line));
|
|
410
372
|
}
|
|
411
373
|
|
|
374
|
+
// Logs que são noise em modo normal, mas úteis no verbose
|
|
375
|
+
static isTomcatVerboseLog(line: string): boolean {
|
|
376
|
+
return this.tomcatVerbosePatterns.some(p => p.test(line));
|
|
377
|
+
}
|
|
378
|
+
|
|
412
379
|
static formatTomcatLog(line: string): string {
|
|
413
380
|
const cleanLine = line.trim();
|
|
414
381
|
if (!cleanLine) return "";
|
|
415
|
-
|
|
416
|
-
// Silencia noise completamente
|
|
417
382
|
if (this.isTomcatNoise(cleanLine)) return "";
|
|
418
383
|
|
|
419
|
-
//
|
|
420
|
-
if (cleanLine.includes("
|
|
421
|
-
const
|
|
422
|
-
if (
|
|
423
|
-
|
|
384
|
+
// Server startup
|
|
385
|
+
if (cleanLine.includes("Server startup in")) {
|
|
386
|
+
const match = cleanLine.match(/Server startup in.*?([\d,]+)\s*ms/);
|
|
387
|
+
if (match) {
|
|
388
|
+
const seconds = (parseInt(match[1].replace(",", "")) / 1000).toFixed(1);
|
|
389
|
+
return ` ${C.success}✓ server${C.reset} ${C.dim}pronto em ${seconds}s${C.reset}`;
|
|
424
390
|
}
|
|
425
391
|
}
|
|
426
392
|
|
|
427
|
-
//
|
|
428
|
-
if (cleanLine.
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
if (initMatch) {
|
|
435
|
-
return `${C.gray}│${C.reset} ${C.success}●${C.reset} ${C.dim}initialized in ${initMatch[1]}ms${C.reset}`;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// Starting service [Catalina]
|
|
439
|
-
const serviceMatch = cleanLine.match(/Starting service \[(\w+)\]/);
|
|
440
|
-
if (serviceMatch) {
|
|
441
|
-
return `${C.gray}│${C.reset} ${C.primary}▸${C.reset} ${C.dim}starting ${serviceMatch[1].toLowerCase()}${C.reset}`;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// Starting Servlet engine
|
|
445
|
-
if (cleanLine.includes("Starting Servlet engine")) {
|
|
446
|
-
const versionMatch = cleanLine.match(/Apache Tomcat\/([\d.]+)/);
|
|
447
|
-
if (versionMatch) {
|
|
448
|
-
return `${C.gray}│${C.reset} ${C.primary}▸${C.reset} ${C.dim}Tomcat ${versionMatch[1]}${C.reset}`;
|
|
393
|
+
// Stack trace - linhas começando com "at "
|
|
394
|
+
if (cleanLine.startsWith("at ") && cleanLine.includes("(")) {
|
|
395
|
+
const match = cleanLine.match(/at\s+([\w.$]+)\.(\w+)\s*\(([^:]+):?(\d*)\)/);
|
|
396
|
+
if (match) {
|
|
397
|
+
const [, className, method, file, lineNum] = match;
|
|
398
|
+
const shortClass = className.split('.').pop() || className;
|
|
399
|
+
return ` ${C.dim}at ${C.gray}${shortClass}.${method}(${file}${lineNum ? ':' + lineNum : ''})${C.reset}`;
|
|
449
400
|
}
|
|
401
|
+
return ` ${C.dim}${cleanLine.slice(0, 100)}${C.reset}`;
|
|
450
402
|
}
|
|
451
403
|
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
// Server startup in ( já temos no summarize mas reforça aqui )
|
|
459
|
-
const startupMatch = cleanLine.match(/Server startup in.*?([\d,]+)\s*ms/);
|
|
460
|
-
if (startupMatch) {
|
|
461
|
-
const time = startupMatch[1].replace(",", "");
|
|
462
|
-
const seconds = (parseInt(time) / 1000).toFixed(1);
|
|
463
|
-
return `${C.gray}│${C.reset} ${C.success}●${C.reset} ${C.dim}ready in ${C.white}${seconds}s${C.reset}`;
|
|
404
|
+
// Exception/Caused by
|
|
405
|
+
if (cleanLine.includes("Exception:") || cleanLine.includes("Caused by:")) {
|
|
406
|
+
const isCausedBy = cleanLine.includes("Caused by:");
|
|
407
|
+
const prefix = isCausedBy ? "Caused by: " : "";
|
|
408
|
+
const color = isCausedBy ? C.warning : C.error;
|
|
409
|
+
return ` ${color}${isCausedBy ? '↳' : '✗'} ${prefix}${cleanLine.slice(prefix.length, 120)}${C.reset}`;
|
|
464
410
|
}
|
|
465
411
|
|
|
466
|
-
//
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
412
|
+
// HOTSWAP AGENT logs
|
|
413
|
+
if (cleanLine.startsWith("HOTSWAP AGENT:")) {
|
|
414
|
+
const match = cleanLine.match(/HOTSWAP AGENT:\s+([\d:.]+)\s+(\w+)\s+\(([^)]+)\)\s+-\s*(.+)/);
|
|
415
|
+
if (match) {
|
|
416
|
+
const [, time, level, source, message] = match;
|
|
417
|
+
const levelColor = level === "ERROR" ? C.error : level === "WARN" ? C.warning : C.primary;
|
|
418
|
+
return ` ${C.primary}↻${C.reset} ${C.dim}[${time}]${C.reset} ${levelColor}${level}${C.reset} ${C.gray}${message.slice(0, 80)}${C.reset}`;
|
|
419
|
+
}
|
|
420
|
+
return ` ${C.primary}↻${C.reset} ${C.gray}${cleanLine.slice(15, 120)}${C.reset}`;
|
|
470
421
|
}
|
|
471
422
|
|
|
472
|
-
//
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
return `${C.gray}│${C.reset} ${C.dim}JVM ${jvmMatch[1]}${C.reset}`;
|
|
423
|
+
// Context classloader logs
|
|
424
|
+
if (cleanLine.startsWith("context:") || cleanLine.startsWith("delegate:") || cleanLine.startsWith("'.") || cleanLine.startsWith("java.net.URLClassLoader")) {
|
|
425
|
+
return ` ${C.dim}◆ ${cleanLine.slice(0, 100)}${C.reset}`;
|
|
476
426
|
}
|
|
477
427
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
if (protocolMatch) {
|
|
481
|
-
return `${C.gray}│${C.reset} ${C.primary}▸${C.reset} ${C.dim}${protocolMatch[1]}${C.reset}`;
|
|
428
|
+
if (cleanLine.includes("Parent Classloader:")) {
|
|
429
|
+
return ` ${C.dim}◆ ${cleanLine.slice(0, 100)}${C.reset}`;
|
|
482
430
|
}
|
|
483
431
|
|
|
484
|
-
//
|
|
432
|
+
// SEVERE/ERROR
|
|
485
433
|
if (cleanLine.includes("SEVERE") || cleanLine.includes("ERROR")) {
|
|
486
|
-
return
|
|
434
|
+
return ` ${C.error}✗${C.reset} ${C.gray}${cleanLine.slice(0, 120)}${C.reset}`;
|
|
487
435
|
}
|
|
488
436
|
|
|
437
|
+
// WARNING/ADVERTÊNCIA
|
|
489
438
|
if (cleanLine.includes("WARNING") || cleanLine.includes("ADVERTÊNCIA")) {
|
|
490
|
-
return
|
|
439
|
+
return ` ${C.warning}!${C.reset} ${C.gray}${cleanLine.slice(0, 120)}${C.reset}`;
|
|
491
440
|
}
|
|
492
441
|
|
|
493
|
-
//
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
const msg = infoMatch[1].trim();
|
|
497
|
-
if (msg.length > 0 && !this.isTomcatNoise(msg)) {
|
|
498
|
-
return `${C.gray}│${C.reset} ${C.dim}${msg.slice(0, 70)}${C.reset}`;
|
|
499
|
-
}
|
|
442
|
+
// SLF4J logs
|
|
443
|
+
if (cleanLine.startsWith("SLF4J:")) {
|
|
444
|
+
return ` ${C.warning}⚠${C.reset} ${C.gray}${cleanLine.slice(6, 120)}${C.reset}`;
|
|
500
445
|
}
|
|
501
446
|
|
|
502
|
-
|
|
447
|
+
// No modo verbose, retorna a linha formatada simples para não perder logs
|
|
448
|
+
return ` ${C.gray}${cleanLine.slice(0, 120)}${C.reset}`;
|
|
503
449
|
}
|
|
504
450
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
private static buildNoisePatterns = [
|
|
508
|
-
/^\[INFO\]\s+Scanning for projects/,
|
|
509
|
-
/^\[INFO\]\s+Using the MultiThreadedBuilder/,
|
|
510
|
-
/^\[INFO\]\s+---\s+.*\s+---$/,
|
|
511
|
-
/^\[INFO\]\s+T+E+\s*$/,
|
|
512
|
-
/^\[INFO\]\s+BUILD\s+SUCCESS/i,
|
|
513
|
-
/^\[INFO\]\s+BUILD\s+FAILURE/i,
|
|
514
|
-
/^\[INFO\]\s+Total time:/,
|
|
515
|
-
/^\[INFO\]\s+Finished at:/,
|
|
516
|
-
/^\[INFO\]\s+Final Memory:/,
|
|
517
|
-
/^\[INFO\]\s+http:\/\/cwiki\.apache\.org/,
|
|
518
|
-
/^\[INFO\]\s+-> \[Help 1\]/,
|
|
519
|
-
/^\[INFO\]\s+Re-run Maven using/,
|
|
520
|
-
/^\[INFO\]\s+To see the full stack trace/,
|
|
521
|
-
/^\[INFO\]\s+For more information about the errors/,
|
|
522
|
-
/^\[ERROR\]\s+To see the full stack trace/,
|
|
523
|
-
/^\[ERROR\]\s+Re-run Maven using/,
|
|
524
|
-
/^\[ERROR\]\s+For more information/,
|
|
525
|
-
/^\[ERROR\]\s+-> \[Help 1\]/,
|
|
526
|
-
/^\[WARNING\]\s+It is highly recommended/,
|
|
527
|
-
/^\[WARNING\]\s+For this reason, future Maven/,
|
|
528
|
-
/^\[WARNING\]\s+\[HELP, sysprop:version/,
|
|
529
|
-
/^\[WARNING\]\s+Some problems were encountered/,
|
|
530
|
-
/^\[WARNING\]\s+'dependencies\.dependency/,
|
|
531
|
-
/Building .*war/,
|
|
532
|
-
/from pom\.xml/,
|
|
533
|
-
/\[ war \]/,
|
|
534
|
-
/^\s*$/,
|
|
535
|
-
];
|
|
536
|
-
|
|
537
|
-
private static gradleNoisePatterns = [
|
|
538
|
-
/^> Task/,
|
|
539
|
-
/^Download/,
|
|
540
|
-
/^Expiring/,
|
|
541
|
-
/^BUILD/,
|
|
542
|
-
/^\d+ actionable task/,
|
|
543
|
-
];
|
|
451
|
+
private static buildNoisePatterns = NOISE_PATTERNS.build;
|
|
544
452
|
|
|
545
453
|
static isBuildNoise(line: string): boolean {
|
|
546
|
-
return this.buildNoisePatterns.some(p => p.test(line))
|
|
547
|
-
this.gradleNoisePatterns.some(p => p.test(line));
|
|
454
|
+
return this.buildNoisePatterns.some(p => p.test(line));
|
|
548
455
|
}
|
|
549
456
|
|
|
550
|
-
|
|
551
|
-
private static pendingError: { file: string; line: string; msg: string } | null = null;
|
|
552
|
-
|
|
553
|
-
static formatBuildLog(line: string, buildTool: 'maven' | 'gradle' = 'maven'): string {
|
|
457
|
+
static formatBuildLog(line: string, _buildTool: 'maven' | 'gradle' = 'maven'): string {
|
|
554
458
|
const cleanLine = line.trim();
|
|
555
459
|
if (!cleanLine) return "";
|
|
460
|
+
if (this.isBuildNoise(cleanLine)) return "";
|
|
556
461
|
|
|
557
|
-
//
|
|
558
|
-
const
|
|
559
|
-
if (
|
|
560
|
-
const [, file, lineNum, msg] =
|
|
462
|
+
// ERROR de compilação com arquivo e linha
|
|
463
|
+
const errorMatch = cleanLine.match(/^\[ERROR\]\s+(.+\.java):\[(\d+),\d+\]\s*(.+)/);
|
|
464
|
+
if (errorMatch) {
|
|
465
|
+
const [, file, lineNum, msg] = errorMatch;
|
|
561
466
|
const shortFile = file.split(/[/\\]/).pop() || file;
|
|
562
|
-
return
|
|
467
|
+
return ` ${C.error}✗${C.reset} ${C.white}${shortFile}${C.gray}:${lineNum}${C.reset} ${C.error}${msg.slice(0, 80)}${C.reset}`;
|
|
563
468
|
}
|
|
564
469
|
|
|
565
|
-
//
|
|
566
|
-
if (cleanLine.
|
|
567
|
-
return
|
|
470
|
+
// ERROR genérico
|
|
471
|
+
if (cleanLine.startsWith("[ERROR]")) {
|
|
472
|
+
return ` ${C.error}✗${C.reset} ${C.gray}${cleanLine.slice(7, 120)}${C.reset}`;
|
|
568
473
|
}
|
|
569
474
|
|
|
570
|
-
//
|
|
571
|
-
if (cleanLine.
|
|
572
|
-
|
|
573
|
-
if (match) {
|
|
574
|
-
return `${C.gray}│${C.reset} ${C.warning}⚠${C.reset} ${C.gray}${match[1].slice(0, 60)}${C.reset}`;
|
|
575
|
-
}
|
|
475
|
+
// WARNING
|
|
476
|
+
if (cleanLine.startsWith("[WARNING]")) {
|
|
477
|
+
return ` ${C.warning}!${C.reset} ${C.gray}${cleanLine.slice(9, 120)}${C.reset}`;
|
|
576
478
|
}
|
|
577
479
|
|
|
578
|
-
//
|
|
579
|
-
const invalidPomMatch = cleanLine.match(/^\[WARNING\]\s+The POM for (.+?) is invalid/);
|
|
580
|
-
if (invalidPomMatch) {
|
|
581
|
-
return `${C.gray}│${C.reset} ${C.warning}⚠${C.reset} ${C.gray}Invalid POM: ${invalidPomMatch[1].slice(0, 50)}${C.reset}`;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
// Maven: [INFO] Compiling N source files
|
|
480
|
+
// Compiling
|
|
585
481
|
const compilingMatch = cleanLine.match(/^\[INFO\]\s+Compiling\s+(\d+)\s+source/);
|
|
586
482
|
if (compilingMatch) {
|
|
587
|
-
return
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
// Maven: [INFO] /path/file.java: Some input files use or override a deprecated API
|
|
591
|
-
const deprecatedMatch = cleanLine.match(/^\[INFO\]\s+(.+\.java):\s+Some input files use (.+)/);
|
|
592
|
-
if (deprecatedMatch) {
|
|
593
|
-
const shortFile = deprecatedMatch[1].split(/[/\\]/).pop() || deprecatedMatch[1];
|
|
594
|
-
const type = deprecatedMatch[2].includes('removal') ? 'deprecated (removal)' : 'deprecated';
|
|
595
|
-
return `${C.gray}│${C.reset} ${C.warning}⚠${C.reset} ${C.gray}${shortFile} uses ${type}${C.reset}`;
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
// Maven: [INFO] Changes detected - recompiling
|
|
599
|
-
if (cleanLine.match(/^\[INFO\]\s+Changes detected/)) {
|
|
600
|
-
return `${C.gray}│${C.reset} ${C.primary}▸${C.reset} ${C.dim}changes detected, recompiling${C.reset}`;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
// Maven: [INFO] Copying N resources
|
|
604
|
-
const resourcesMatch = cleanLine.match(/^\[INFO\]\s+Copying\s+(\d+)\s+resources?/);
|
|
605
|
-
if (resourcesMatch) {
|
|
606
|
-
return `${C.gray}│${C.reset} ${C.dim}copying ${C.white}${resourcesMatch[1]}${C.reset} ${C.dim}resources${C.reset}`;
|
|
483
|
+
return ` ${C.primary}●${C.reset} ${C.dim}compilando ${C.white}${compilingMatch[1]}${C.reset} ${C.dim}arquivos${C.reset}`;
|
|
607
484
|
}
|
|
608
485
|
|
|
609
|
-
//
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
// Maven: [INFO] skip non existing resourceDirectory
|
|
616
|
-
if (cleanLine.match(/^\[INFO\]\s+skip non existing/)) {
|
|
617
|
-
return ""; // Silencia
|
|
486
|
+
// Copying resources
|
|
487
|
+
if (cleanLine.includes("Copying") && cleanLine.includes("resource")) {
|
|
488
|
+
const match = cleanLine.match(/Copying\s+(\d+)\s+resource/);
|
|
489
|
+
if (match) {
|
|
490
|
+
return ` ${C.dim}→ copiando ${match[1]} recursos${C.reset}`;
|
|
491
|
+
}
|
|
618
492
|
}
|
|
619
493
|
|
|
620
|
-
//
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
494
|
+
// Building project info
|
|
495
|
+
if (cleanLine.includes("Building ") && cleanLine.includes("<")) {
|
|
496
|
+
const match = cleanLine.match(/Building\s+(.+)/);
|
|
497
|
+
if (match) {
|
|
498
|
+
return ` ${C.primary}●${C.reset} ${C.white}${match[1]}${C.reset}`;
|
|
499
|
+
}
|
|
624
500
|
}
|
|
625
501
|
|
|
626
|
-
//
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
const [, file, lineNum, level, msg] = gradleErrorMatch;
|
|
630
|
-
const shortFile = file.split(/[/\\]/).pop() || file;
|
|
631
|
-
const icon = level === 'error' ? C.error + '✗' + C.reset : C.warning + '⚠' + C.reset;
|
|
632
|
-
return `${C.gray}│${C.reset} ${icon} ${C.white}${shortFile}${C.gray}:${lineNum}${C.reset} ${C.error}${msg.slice(0, 60)}${C.reset}`;
|
|
502
|
+
// Recompile with -X
|
|
503
|
+
if (cleanLine.includes("Recompile with -Xlint")) {
|
|
504
|
+
return ` ${C.warning}⚠${C.reset} ${C.dim}recompile com -Xlint para detalhes${C.reset}`;
|
|
633
505
|
}
|
|
634
506
|
|
|
635
|
-
//
|
|
636
|
-
if (
|
|
637
|
-
|
|
638
|
-
const clean = cleanLine.replace(/^\[(INFO|WARNING|ERROR)\]\s*/, '');
|
|
639
|
-
if (clean.length > 0 && !this.isBuildNoise(clean)) {
|
|
640
|
-
return `${C.gray}│${C.reset} ${C.dim}${clean.slice(0, 70)}${C.reset}`;
|
|
641
|
-
}
|
|
507
|
+
// Some input files use unchecked or unsafe operations
|
|
508
|
+
if (cleanLine.includes("Some input files use") || cleanLine.includes("unchecked or unsafe")) {
|
|
509
|
+
return ` ${C.warning}⚠${C.reset} ${C.gray}${cleanLine.slice(0, 100)}${C.reset}`;
|
|
642
510
|
}
|
|
643
511
|
|
|
644
512
|
return "";
|
|
645
513
|
}
|
|
646
514
|
}
|
|
515
|
+
|
|
516
|
+
// Exporta Logger como padrão para compatibilidade
|
|
517
|
+
export { LoggerLegacy as Logger };
|