@archznn/xavva 1.6.5 → 1.7.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 +12 -46
- package/package.json +1 -1
- package/src/commands/AuditCommand.ts +0 -4
- package/src/commands/DeployCommand.ts +84 -41
- package/src/commands/DocsCommand.ts +11 -11
- package/src/commands/DoctorCommand.ts +231 -68
- package/src/commands/RunCommand.ts +4 -3
- package/src/commands/StartCommand.ts +1 -1
- package/src/index.ts +29 -10
- package/src/services/AuditService.ts +0 -5
- package/src/services/BuildService.ts +7 -11
- package/src/services/EndpointService.ts +0 -3
- package/src/services/TomcatService.ts +138 -12
- package/src/utils/config.ts +1 -1
- package/src/utils/ui.ts +196 -102
|
@@ -70,13 +70,129 @@ export class TomcatService {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
private async ensureHotswapAgent(): Promise<string | null> {
|
|
74
|
+
const fs = require("fs");
|
|
75
|
+
const path = require("path");
|
|
76
|
+
const os = require("os");
|
|
77
|
+
const agentDir = path.join(os.homedir(), ".xavva", "agents");
|
|
78
|
+
const agentPath = path.join(agentDir, "hotswap-agent-2.0.3.jar");
|
|
79
|
+
|
|
80
|
+
if (fs.existsSync(agentPath) && fs.statSync(agentPath).size > 1000) return agentPath;
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
if (!fs.existsSync(agentDir)) fs.mkdirSync(agentDir, { recursive: true });
|
|
84
|
+
|
|
85
|
+
Logger.step("Downloading HotswapAgent v2.0.3 (Global)...");
|
|
86
|
+
const url = "https://github.com/HotswapProjects/HotswapAgent/releases/download/RELEASE-2.0.3/hotswap-agent-2.0.3.jar";
|
|
87
|
+
const response = await fetch(url);
|
|
88
|
+
if (!response.ok) throw new Error(`Status: ${response.status}`);
|
|
89
|
+
|
|
90
|
+
const buffer = await response.arrayBuffer();
|
|
91
|
+
fs.writeFileSync(agentPath, Buffer.from(buffer));
|
|
92
|
+
Logger.success("HotswapAgent v2.0.3 installed globally!");
|
|
93
|
+
return agentPath;
|
|
94
|
+
} catch (e) {
|
|
95
|
+
Logger.warn("Falha ao baixar HotswapAgent. Usando hot swap padrão da JVM.");
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private findAllClassPaths(buildTool: 'maven' | 'gradle'): string[] {
|
|
101
|
+
const fs = require("fs");
|
|
102
|
+
const path = require("path");
|
|
103
|
+
const results: string[] = [];
|
|
104
|
+
const root = process.cwd();
|
|
105
|
+
|
|
106
|
+
const scan = (dir: string) => {
|
|
107
|
+
try {
|
|
108
|
+
const files = fs.readdirSync(dir, { withFileTypes: true });
|
|
109
|
+
for (const file of files) {
|
|
110
|
+
if (!file.isDirectory()) continue;
|
|
111
|
+
|
|
112
|
+
const name = file.name;
|
|
113
|
+
if (name.startsWith('.') || ['node_modules', 'out', 'bin', 'src', 'webapps', '.xavva'].includes(name)) continue;
|
|
114
|
+
|
|
115
|
+
const fullPath = path.join(dir, name);
|
|
116
|
+
|
|
117
|
+
const isMavenClasses = buildTool === 'maven' && name === 'classes' && dir.endsWith('target');
|
|
118
|
+
const isGradleClasses = buildTool === 'gradle' && name === 'main' && dir.endsWith(path.join('classes', 'java'));
|
|
119
|
+
|
|
120
|
+
if (isMavenClasses || isGradleClasses) {
|
|
121
|
+
results.push(fullPath.replace(/\\/g, "/"));
|
|
122
|
+
} else {
|
|
123
|
+
scan(fullPath);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} catch (e) {}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
scan(root);
|
|
130
|
+
|
|
131
|
+
if (results.length === 0) {
|
|
132
|
+
const defaultPath = buildTool === 'maven'
|
|
133
|
+
? path.join(root, "target", "classes")
|
|
134
|
+
: path.join(root, "build", "classes", "java", "main");
|
|
135
|
+
results.push(defaultPath.replace(/\\/g, "/"));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return results;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async start(config: any, isWatching: boolean = false) {
|
|
74
142
|
const binPath = `${this.activeConfig.path}\\bin\\catalina.bat`;
|
|
75
|
-
const args = debug ? ["jpda", "run"] : ["run"];
|
|
143
|
+
const args = (config.project.debug || isWatching) ? ["jpda", "run"] : ["run"];
|
|
76
144
|
|
|
77
145
|
const catalinaOpts = [process.env.CATALINA_OPTS || ""];
|
|
78
146
|
|
|
79
|
-
if (
|
|
147
|
+
if (config.project.debug || isWatching) {
|
|
148
|
+
const agentPath = await this.ensureHotswapAgent();
|
|
149
|
+
if (agentPath) {
|
|
150
|
+
catalinaOpts.push(`-javaagent:${agentPath}`);
|
|
151
|
+
|
|
152
|
+
let javaBin = "java";
|
|
153
|
+
if (process.env.JAVA_HOME) {
|
|
154
|
+
javaBin = require("path").join(process.env.JAVA_HOME, "bin", "java.exe");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const javaVer = Bun.spawnSync([javaBin, "-version"]);
|
|
158
|
+
const output = (javaVer.stderr.toString() + javaVer.stdout.toString()).toLowerCase();
|
|
159
|
+
|
|
160
|
+
if (output.includes("dcevm") || output.includes("jbr") || output.includes("trava")) {
|
|
161
|
+
catalinaOpts.push("-XX:+AllowEnhancedClassRedefinition");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
catalinaOpts.push(
|
|
165
|
+
"--add-opens=java.base/jdk.internal.loader=ALL-UNNAMED",
|
|
166
|
+
"--add-opens=java.base/java.lang=ALL-UNNAMED",
|
|
167
|
+
"--add-opens=java.base/java.io=ALL-UNNAMED",
|
|
168
|
+
"--add-opens=java.base/java.net=ALL-UNNAMED",
|
|
169
|
+
"--add-opens=java.base/java.util=ALL-UNNAMED",
|
|
170
|
+
"--add-opens=java.base/java.util.concurrent=ALL-UNNAMED",
|
|
171
|
+
"--add-opens=java.base/java.security=ALL-UNNAMED",
|
|
172
|
+
"--add-opens=java.base/jdk.internal.reflect=ALL-UNNAMED",
|
|
173
|
+
"--add-opens=java.base/java.lang.reflect=ALL-UNNAMED",
|
|
174
|
+
"--add-opens=java.base/sun.nio.ch=ALL-UNNAMED",
|
|
175
|
+
"--add-opens=java.base/java.util.jar=ALL-UNNAMED",
|
|
176
|
+
"--add-opens=java.desktop/java.beans=ALL-UNNAMED"
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const fs = require("fs");
|
|
180
|
+
const path = require("path");
|
|
181
|
+
const xavvaDir = path.join(process.cwd(), ".xavva");
|
|
182
|
+
if (!fs.existsSync(xavvaDir)) fs.mkdirSync(xavvaDir, { recursive: true });
|
|
183
|
+
|
|
184
|
+
const classPaths = this.findAllClassPaths(config.project.buildTool);
|
|
185
|
+
const extraClasspath = classPaths.join(",");
|
|
186
|
+
|
|
187
|
+
const propsPath = path.join(xavvaDir, "hotswap-agent.properties");
|
|
188
|
+
const propsContent = `autoHotswap=true\nautoHotswap.delay=3000\nwatchResources=false\nextraClasspath=${extraClasspath}\nLOGGER=info`;
|
|
189
|
+
fs.writeFileSync(propsPath, propsContent);
|
|
190
|
+
|
|
191
|
+
catalinaOpts.push(`-Dhotswap-agent.properties.path=${propsPath}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (config.project.skipScan) {
|
|
80
196
|
catalinaOpts.push(
|
|
81
197
|
"-Dtomcat.util.scan.StandardJarScanFilter.jarsToSkip=*.jar",
|
|
82
198
|
"-Dtomcat.util.scan.StandardJarScanFilter.jarsToScan=",
|
|
@@ -93,13 +209,18 @@ export class TomcatService {
|
|
|
93
209
|
CATALINA_OPTS: catalinaOpts.join(" ").trim()
|
|
94
210
|
};
|
|
95
211
|
|
|
96
|
-
if (
|
|
97
|
-
|
|
212
|
+
if (process.env.JAVA_HOME) {
|
|
213
|
+
env.JAVA_HOME = process.env.JAVA_HOME;
|
|
214
|
+
env.JRE_HOME = process.env.JAVA_HOME;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (config.project.debug) {
|
|
218
|
+
Logger.debug("Java Debugger habilitado na porta 5005");
|
|
98
219
|
env.JPDA_ADDRESS = "5005";
|
|
99
220
|
env.JPDA_TRANSPORT = "dt_socket";
|
|
100
221
|
}
|
|
101
222
|
|
|
102
|
-
if (cleanLogs || quiet) {
|
|
223
|
+
if ((config.project.cleanLogs || config.project.quiet) && !config.project.verbose) {
|
|
103
224
|
this.stopStartupSpinner = Logger.spinner("Starting Tomcat server");
|
|
104
225
|
}
|
|
105
226
|
|
|
@@ -111,11 +232,11 @@ export class TomcatService {
|
|
|
111
232
|
|
|
112
233
|
this.pid = this.currentProcess.pid;
|
|
113
234
|
|
|
114
|
-
this.processLogStream(this.currentProcess.stdout, cleanLogs, quiet);
|
|
115
|
-
this.processLogStream(this.currentProcess.stderr, cleanLogs, quiet);
|
|
235
|
+
this.processLogStream(this.currentProcess.stdout, config.project.cleanLogs, config.project.quiet, config.project.verbose, config.tomcat.grep);
|
|
236
|
+
this.processLogStream(this.currentProcess.stderr, config.project.cleanLogs, config.project.quiet, config.project.verbose, config.tomcat.grep);
|
|
116
237
|
}
|
|
117
238
|
|
|
118
|
-
private async processLogStream(stream: ReadableStream, clean: boolean, quiet: boolean) {
|
|
239
|
+
private async processLogStream(stream: ReadableStream, clean: boolean, quiet: boolean, verbose: boolean, grep: string) {
|
|
119
240
|
const reader = stream.getReader();
|
|
120
241
|
const decoder = new TextDecoder();
|
|
121
242
|
|
|
@@ -141,6 +262,11 @@ export class TomcatService {
|
|
|
141
262
|
}
|
|
142
263
|
}
|
|
143
264
|
|
|
265
|
+
if (verbose) {
|
|
266
|
+
Logger.log(cleanLine);
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
|
|
144
270
|
if (clean) {
|
|
145
271
|
if (quiet && !Logger.isEssential(cleanLine)) {
|
|
146
272
|
if (Logger.isSystemNoise(cleanLine)) continue;
|
|
@@ -149,14 +275,14 @@ export class TomcatService {
|
|
|
149
275
|
continue;
|
|
150
276
|
}
|
|
151
277
|
|
|
152
|
-
if (
|
|
278
|
+
if (grep && !cleanLine.toLowerCase().includes(grep.toLowerCase())) {
|
|
153
279
|
if (!Logger.isEssential(cleanLine)) continue;
|
|
154
280
|
}
|
|
155
281
|
|
|
156
282
|
const summarized = Logger.summarize(cleanLine);
|
|
157
|
-
|
|
283
|
+
if (summarized) Logger.log(summarized);
|
|
158
284
|
} else {
|
|
159
|
-
|
|
285
|
+
Logger.log(cleanLine);
|
|
160
286
|
}
|
|
161
287
|
}
|
|
162
288
|
}
|
package/src/utils/config.ts
CHANGED
|
@@ -80,7 +80,7 @@ export class ConfigManager {
|
|
|
80
80
|
if (fs.existsSync(path.join(process.cwd(), "build.gradle")) || fs.existsSync(path.join(process.cwd(), "build.gradle.kts"))) {
|
|
81
81
|
return "gradle";
|
|
82
82
|
}
|
|
83
|
-
return "maven";
|
|
83
|
+
return "maven";
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
private static ensureGitIgnore() {
|
package/src/utils/ui.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import pkg from "../../package.json";
|
|
2
2
|
|
|
3
3
|
export class Logger {
|
|
4
|
-
|
|
4
|
+
public static readonly C = {
|
|
5
5
|
reset: "\x1b[0m",
|
|
6
6
|
cyan: "\x1b[36m",
|
|
7
7
|
green: "\x1b[32m",
|
|
@@ -9,60 +9,124 @@ export class Logger {
|
|
|
9
9
|
red: "\x1b[31m",
|
|
10
10
|
dim: "\x1b[90m",
|
|
11
11
|
bold: "\x1b[1m",
|
|
12
|
-
blue: "\x1b[34m"
|
|
12
|
+
blue: "\x1b[34m",
|
|
13
|
+
magenta: "\x1b[35m",
|
|
14
|
+
bgRed: "\x1b[41m",
|
|
15
|
+
white: "\x1b[37m",
|
|
16
|
+
gray: "\x1b[38;5;240m",
|
|
17
|
+
lightGray: "\x1b[38;5;248m",
|
|
18
|
+
darkGray: "\x1b[38;5;238m"
|
|
13
19
|
};
|
|
14
20
|
|
|
15
|
-
static
|
|
21
|
+
private static hotswapPluginsCount = 0;
|
|
22
|
+
private static lastDomain = "";
|
|
23
|
+
private static lastHotswapMsg = "";
|
|
24
|
+
private static activeSpinnerMsg = "";
|
|
25
|
+
|
|
26
|
+
private static write(message: string, isError: boolean = false) {
|
|
27
|
+
if (this.activeSpinnerMsg) {
|
|
28
|
+
process.stdout.write("\r\x1B[K"); // Limpa a linha do spinner
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (isError) {
|
|
32
|
+
console.error(message + this.C.reset);
|
|
33
|
+
} else {
|
|
34
|
+
console.log(message + this.C.reset);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (this.activeSpinnerMsg) {
|
|
38
|
+
// Re-imprime o início da linha do spinner para o próximo frame
|
|
39
|
+
process.stdout.write(` ${this.C.cyan}⠋${this.C.reset} ${this.activeSpinnerMsg}...`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static getGitContext(): { branch: string, author: string, hash: string } {
|
|
16
44
|
try {
|
|
17
45
|
const branch = Bun.spawnSync(["git", "rev-parse", "--abbrev-ref", "HEAD"]).stdout.toString().trim();
|
|
18
46
|
const author = Bun.spawnSync(["git", "log", "-1", "--format=%an"]).stdout.toString().trim();
|
|
19
|
-
|
|
47
|
+
const hash = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"]).stdout.toString().trim();
|
|
48
|
+
return { branch, author, hash };
|
|
20
49
|
} catch (e) {
|
|
21
|
-
return "";
|
|
50
|
+
return { branch: "", author: "", hash: "" };
|
|
22
51
|
}
|
|
23
52
|
}
|
|
24
53
|
|
|
25
54
|
static banner(command?: string) {
|
|
26
55
|
console.clear();
|
|
27
56
|
const git = this.getGitContext();
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
57
|
+
const name = (process.cwd().split(/[/\\]/).pop() || "JAVA").toUpperCase();
|
|
58
|
+
|
|
59
|
+
const width = 62;
|
|
60
|
+
const line = "─".repeat(width);
|
|
61
|
+
|
|
62
|
+
this.write(`${this.C.gray}╭${line}╮`);
|
|
63
|
+
this.write(`${this.C.gray}│ ${this.C.bold}${this.C.blue}${name} CLI${this.C.reset}${" ".repeat(width - name.length - 6)} ${this.C.gray}│`);
|
|
64
|
+
|
|
65
|
+
const info = `Version: ${pkg.version} | Branch: ${git.branch} | ${git.hash}`;
|
|
66
|
+
this.write(`${this.C.gray}│ ${this.C.dim}${info}${" ".repeat(width - info.length - 2)}${this.C.gray}│`);
|
|
67
|
+
|
|
68
|
+
const modeLine = `Mode: ${command?.toUpperCase() || "DEPLOY"}`;
|
|
69
|
+
const status = command === 'dev' ? `${this.C.green}🟢` : `${this.C.blue}🔵`;
|
|
70
|
+
this.write(`${this.C.gray}│ ${this.C.yellow}${this.C.bold}${modeLine}${" ".repeat(width - modeLine.length - 5)}${status} ${this.C.gray}│`);
|
|
71
|
+
this.write(`${this.C.gray}╰${line}╯${this.C.reset}`);
|
|
34
72
|
}
|
|
35
73
|
|
|
36
74
|
static section(title: string) {
|
|
37
|
-
|
|
38
|
-
console.log(` ${this.C.dim}──────────────────────────${this.C.reset}`);
|
|
75
|
+
this.write(`\n${this.C.bold}${this.C.blue}[${title.toUpperCase()}]${this.C.reset}`);
|
|
39
76
|
}
|
|
40
77
|
|
|
41
|
-
static
|
|
42
|
-
|
|
78
|
+
private static domain(name: string) {
|
|
79
|
+
if (this.lastDomain !== name) {
|
|
80
|
+
this.write(`\n${this.C.bold}${this.C.blue}[${name.toUpperCase()}]${this.C.reset}`);
|
|
81
|
+
this.lastDomain = name;
|
|
82
|
+
}
|
|
43
83
|
}
|
|
44
84
|
|
|
45
|
-
static
|
|
46
|
-
|
|
85
|
+
static config(label: string, value: string | number | boolean) {
|
|
86
|
+
this.domain("config");
|
|
87
|
+
this.info(label, value);
|
|
47
88
|
}
|
|
48
89
|
|
|
49
|
-
static
|
|
50
|
-
|
|
90
|
+
static info(label: string, value: string | number | boolean) {
|
|
91
|
+
this.write(` ${this.C.lightGray}${label.padEnd(12)}${this.C.reset} : ${this.C.bold}${value}${this.C.reset}`);
|
|
51
92
|
}
|
|
52
93
|
|
|
53
|
-
static
|
|
54
|
-
|
|
94
|
+
static build(msg: string, status: 'start' | 'success' | 'error' | 'info' = 'success') {
|
|
95
|
+
this.domain("build");
|
|
96
|
+
const symbol = status === 'start' ? `${this.C.blue}▶` : status === 'success' ? `${this.C.green}✔` : status === 'error' ? `${this.C.red}✖` : `${this.C.dim}ℹ`;
|
|
97
|
+
this.write(` ${symbol} ${this.C.reset}${msg}`);
|
|
55
98
|
}
|
|
56
99
|
|
|
57
|
-
static
|
|
58
|
-
|
|
100
|
+
static server(msg: string, status: 'start' | 'success' | 'error' | 'info' = 'info') {
|
|
101
|
+
this.domain("server");
|
|
102
|
+
const symbol = status === 'start' ? `${this.C.blue}▶` : status === 'success' ? `${this.C.green}✔` : status === 'error' ? `${this.C.red}✖` : `${this.C.dim}ℹ`;
|
|
103
|
+
this.write(` ${symbol} ${this.C.reset}${msg}`);
|
|
59
104
|
}
|
|
60
105
|
|
|
61
|
-
static
|
|
62
|
-
|
|
106
|
+
static health(msg: string, status: 'success' | 'error' | 'warn' = 'success') {
|
|
107
|
+
this.domain("health");
|
|
108
|
+
const symbol = status === 'success' ? `${this.C.green}✔` : status === 'error' ? `${this.C.red}✖` : `${this.C.yellow}⚠`;
|
|
109
|
+
this.write(` ${symbol} ${this.C.reset}${msg}`);
|
|
63
110
|
}
|
|
64
111
|
|
|
112
|
+
static watcher(msg: string, status: 'watch' | 'change' | 'start' | 'success' = 'success') {
|
|
113
|
+
this.domain("watcher");
|
|
114
|
+
const symbol = status === 'watch' ? `${this.C.magenta}👀` : status === 'change' ? `${this.C.yellow}▲` : status === 'start' ? `${this.C.blue}▶` : `${this.C.green}✔`;
|
|
115
|
+
this.write(` ${symbol} ${this.C.reset}${msg}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
static success(msg: string) { this.write(` ${this.C.green}✔ ${msg}`); }
|
|
119
|
+
static error(msg: string) { this.write(` ${this.C.red}✖ ${msg}`, true); }
|
|
120
|
+
static warn(msg: string) { this.write(` ${this.C.yellow}⚠ ${msg}`); }
|
|
121
|
+
static log(msg: string) { this.write(` ${msg}`); }
|
|
122
|
+
static step(msg: string) { this.write(` ${this.C.dim}» ${msg}`); }
|
|
123
|
+
static debug(msg: string) { this.write(` ${this.C.magenta}🐛 ${msg}`); }
|
|
124
|
+
static process(msg: string) { this.write(` ${this.C.blue}▶ ${msg}`); }
|
|
125
|
+
static newline() { this.write(""); }
|
|
126
|
+
static dim(msg: string) { this.write(` ${this.C.dim}${msg}${this.C.reset}`); }
|
|
127
|
+
|
|
65
128
|
static spinner(msg: string) {
|
|
129
|
+
this.activeSpinnerMsg = msg;
|
|
66
130
|
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
67
131
|
let i = 0;
|
|
68
132
|
process.stdout.write("\x1B[?25l");
|
|
@@ -74,121 +138,151 @@ export class Logger {
|
|
|
74
138
|
|
|
75
139
|
return (success = true) => {
|
|
76
140
|
clearInterval(timer);
|
|
141
|
+
this.activeSpinnerMsg = "";
|
|
77
142
|
process.stdout.write("\r\x1B[K");
|
|
78
143
|
process.stdout.write("\x1B[?25h");
|
|
79
144
|
if (success) {
|
|
80
|
-
|
|
145
|
+
this.write(` ${this.C.green}✔${this.C.reset} ${msg}`);
|
|
146
|
+
} else {
|
|
147
|
+
this.error(`Falha em ${msg}`);
|
|
81
148
|
}
|
|
82
149
|
};
|
|
83
150
|
}
|
|
84
151
|
|
|
85
152
|
static isSystemNoise(line: string): boolean {
|
|
86
153
|
const noise = [
|
|
87
|
-
"Using CATALINA_",
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"org.apache.catalina.core.AprLifecycleListener",
|
|
100
|
-
"org.apache.coyote.AbstractProtocol.init",
|
|
101
|
-
"org.apache.catalina.startup.Catalina.load",
|
|
102
|
-
"Arquivos processados em",
|
|
103
|
-
"org.apache.jasper.servlet.TldScanner.scanJars",
|
|
104
|
-
"Listening for transport dt_socket",
|
|
105
|
-
"org.apache.catalina.startup.ExpandWar.expand",
|
|
106
|
-
"org.apache.catalina.startup.ContextConfig.configureStart",
|
|
107
|
-
"SLF4J: ",
|
|
108
|
-
"org.glassfish.jersey.internal.Errors.logErrors",
|
|
109
|
-
"contains empty path annotation",
|
|
110
|
-
"org.apache.catalina.core.StandardContext.setPath",
|
|
111
|
-
"milliseconds"
|
|
154
|
+
"Using CATALINA_", "Using JRE_HOME", "Using CLASSPATH", "NOTE: Picked up JDK_JAVA_OPTIONS",
|
|
155
|
+
"Command line argument", "VersionLoggerListener", "Scanning for projects...",
|
|
156
|
+
"Building ", "--- ", "+++ ", "DEBUG: ", "org.apache.catalina.startup.VersionLoggerListener",
|
|
157
|
+
"org.apache.catalina.core.AprLifecycleListener", "org.apache.coyote.AbstractProtocol.init",
|
|
158
|
+
"org.apache.catalina.startup.Catalina.load", "Arquivos processados em",
|
|
159
|
+
"org.apache.jasper.servlet.TldScanner.scanJars", "Listening for transport dt_socket",
|
|
160
|
+
"org.apache.catalina.startup.ExpandWar.expand", "org.apache.catalina.startup.ContextConfig.configureStart",
|
|
161
|
+
"SLF4J: ", "org.glassfish.jersey.internal.Errors.logErrors", "contains empty path annotation",
|
|
162
|
+
"org.apache.catalina.core.StandardContext.setPath", "milliseconds",
|
|
163
|
+
"org.apache.catalina.startup.HostConfig.deployWAR", "org.apache.catalina.startup.HostConfig.deployDirectory",
|
|
164
|
+
"Deployment of web application", "Deploying web application archive", "at org.apache",
|
|
165
|
+
"Registering directory"
|
|
112
166
|
];
|
|
113
167
|
return noise.some(n => line.includes(n));
|
|
114
168
|
}
|
|
115
169
|
|
|
116
170
|
static isEssential(line: string): boolean {
|
|
117
|
-
return line.includes("SEVERE") ||
|
|
118
|
-
line.includes("
|
|
119
|
-
line.includes("
|
|
120
|
-
line.includes("Caused by") ||
|
|
121
|
-
line.includes("at ") ||
|
|
122
|
-
line.includes("... ") ||
|
|
123
|
-
line.includes("Server startup in");
|
|
171
|
+
return line.includes("SEVERE") || line.includes("ERROR") || line.includes("Exception") ||
|
|
172
|
+
line.includes("Caused by") || line.includes("at ") || line.includes("... ") ||
|
|
173
|
+
line.includes("Server startup in") || line.includes("HOTSWAP AGENT:");
|
|
124
174
|
}
|
|
125
175
|
|
|
126
176
|
static summarize(line: string): string {
|
|
177
|
+
if (this.isSystemNoise(line)) return "";
|
|
178
|
+
|
|
127
179
|
const startupMatch = line.match(/Server startup in (\[?)(.*?)(\]?)\s*ms/);
|
|
128
180
|
if (startupMatch) {
|
|
129
|
-
const time = startupMatch[2]
|
|
130
|
-
|
|
181
|
+
const time = (parseInt(startupMatch[2]) / 1000).toFixed(1);
|
|
182
|
+
this.domain("server");
|
|
183
|
+
return `${this.C.green}✔ ${this.C.bold}Server started in ${time}s`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const deployMatch = line.match(/Deployment of web application archive \[(.*?)\] has finished in \[(.*?)\] ms/);
|
|
187
|
+
if (deployMatch) {
|
|
188
|
+
this.domain("build");
|
|
189
|
+
return `${this.C.green}✔ Artifacts deployed`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const hotswapPattern = /HOTSWAP AGENT:.*? (INFO|WARN|ERROR|RELOAD) (.*?) - (.*)/;
|
|
193
|
+
const hotswapMatch = line.match(hotswapPattern);
|
|
194
|
+
if (hotswapMatch) {
|
|
195
|
+
const level = hotswapMatch[1];
|
|
196
|
+
let msg = hotswapMatch[3];
|
|
197
|
+
|
|
198
|
+
if (msg.includes("plugin initialized")) {
|
|
199
|
+
this.hotswapPluginsCount++;
|
|
200
|
+
return "";
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (msg.includes("redefinition") || msg.includes("reloaded") || level === 'RELOAD') {
|
|
204
|
+
if (msg.includes("Reloading classes [")) {
|
|
205
|
+
const classes = msg.match(/\[(.*?)\]/)?.[1] || "";
|
|
206
|
+
const classCount = classes.split(",").length;
|
|
207
|
+
if (classCount > 3) msg = `Reloading ${classCount} classes...`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (msg === this.lastHotswapMsg) return "";
|
|
211
|
+
this.lastHotswapMsg = msg;
|
|
212
|
+
|
|
213
|
+
this.watcher(`Hotswap: ${msg.replace(/Class '.*?'/, (m) => this.C.bold + m + this.C.reset)}`, 'success');
|
|
214
|
+
return "";
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (msg.includes("Loading Hotswap agent")) {
|
|
218
|
+
this.domain("server");
|
|
219
|
+
return `${this.C.blue}▶ ${this.C.reset}Initializing Hotswap Agent ${msg.match(/\d+\.\d+\.\d+/)?.[0] || ""}`;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (this.hotswapPluginsCount > 0) {
|
|
223
|
+
const count = this.hotswapPluginsCount;
|
|
224
|
+
this.hotswapPluginsCount = 0;
|
|
225
|
+
this.domain("server");
|
|
226
|
+
this.write(` ${this.C.green}✔ ${this.C.reset}Hotswap ready (Plugins: ${count} loaded)`);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let color = this.C.cyan;
|
|
230
|
+
let symbol = "●";
|
|
231
|
+
if (level === "WARN") { color = this.C.yellow; symbol = "▲"; }
|
|
232
|
+
else if (level === "ERROR") { color = this.C.red; symbol = "✖"; }
|
|
233
|
+
|
|
234
|
+
this.domain("server");
|
|
235
|
+
return `${color}${symbol} ${this.C.bold}Hotswap:${this.C.reset} ${msg}`;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (line.includes("java.lang.UnsupportedOperationException") && (line.includes("add a method") || line.includes("change the schema"))) {
|
|
239
|
+
this.domain("watcher");
|
|
240
|
+
this.write(` ${this.C.red}✖ ${this.C.bold}Hotswap Falhou:${this.C.reset} Mudança estrutural detectada (novo método/campo).`);
|
|
241
|
+
this.write(` ${this.C.yellow}💡 Dica: Sua JVM atual não suporta mudar a estrutura da classe. Reinicie o servidor para aplicar.`);
|
|
242
|
+
return "";
|
|
131
243
|
}
|
|
132
244
|
|
|
133
|
-
|
|
134
|
-
|
|
245
|
+
const tomcatPattern = /^(\d{2}-\w{3}-\d{4} \d{2}:\d{2}:\d{2}\.\d{3})\s+(INFO|WARNING|SEVERE|ERROR)\s+\[(.*?)\]\s+(.*)$/;
|
|
246
|
+
const tMatch = line.match(tomcatPattern);
|
|
247
|
+
if (tMatch) {
|
|
248
|
+
const label = tMatch[2];
|
|
249
|
+
let msg = tMatch[4].trim();
|
|
250
|
+
if (this.isSystemNoise(msg)) return "";
|
|
251
|
+
let color = this.C.dim;
|
|
252
|
+
let symbol = "ℹ";
|
|
253
|
+
if (label === "WARNING") { color = this.C.yellow; symbol = "▲"; }
|
|
254
|
+
else if (label === "SEVERE" || label === "ERROR") { color = this.C.red; symbol = "✖"; }
|
|
255
|
+
msg = msg.replace(/^(org\.apache|com\.sun|java\..*?|org\.glassfish)\.[a-zA-Z0-9.]+\s/, "").trim();
|
|
256
|
+
if (!msg) return "";
|
|
257
|
+
return `${color}${symbol} ${msg}`;
|
|
258
|
+
}
|
|
135
259
|
|
|
136
260
|
const compilationErrorMatch = line.match(/^\[ERROR\]\s+(.*\.java):\[(\d+),(\d+)\]\s+(.*)$/);
|
|
137
261
|
if (compilationErrorMatch) {
|
|
138
262
|
const [_, filePath, row, col, msg] = compilationErrorMatch;
|
|
139
263
|
const fileName = filePath.split(/[/\\]/).pop();
|
|
140
|
-
|
|
141
|
-
let contextTip = "";
|
|
142
|
-
if (msg.includes("unmappable character") || msg.includes("encoding")) {
|
|
143
|
-
contextTip = `\n ${this.C.yellow}💡 Dica: Erro de encoding detectado. O arquivo parece usar um charset (como UTF-8) diferente do configurado no Maven.${this.C.reset}`;
|
|
144
|
-
} else if (msg.includes("illegal character")) {
|
|
145
|
-
if (msg.includes("\\u00bb") || msg.includes("\\u00bf") || msg.includes("\\u00ef")) {
|
|
146
|
-
contextTip = `\n ${this.C.yellow}💡 Dica: UTF-8 BOM detectado! O arquivo tem caracteres invisíveis no início que o Java não aceita. Use 'xavva doctor' para corrigir.${this.C.reset}`;
|
|
147
|
-
} else {
|
|
148
|
-
contextTip = `\n ${this.C.yellow}💡 Dica: Caractere invisível ou inválido. Tente remover espaços ou quebras de linha estranhas no topo do arquivo.${this.C.reset}`;
|
|
149
|
-
}
|
|
150
|
-
} else if (msg.includes("cannot find symbol")) {
|
|
151
|
-
contextTip = `\n ${this.C.yellow}💡 Dica: Símbolo não encontrado. Verifique se o import está correto ou se a dependência existe.${this.C.reset}`;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return ` ${this.C.red}${this.C.bold}✖ ERROR ${this.C.reset}${this.C.dim}em ${this.C.reset}${this.C.bold}${fileName}${this.C.reset}${this.C.dim}:${row}${this.C.reset}\n ${this.C.red}➜ ${this.C.reset}${msg}${contextTip}\n`;
|
|
264
|
+
return `${this.C.red}✖ ERROR ${this.C.reset}${this.C.dim}em ${this.C.reset}${this.C.bold}${fileName}${this.C.reset}${this.C.dim}:${row}${this.C.reset} ${this.C.red}➜ ${this.C.reset}${msg}`;
|
|
155
265
|
}
|
|
156
266
|
|
|
157
267
|
const logPattern = /^\[(INFO|WARNING|WARN|SEVERE|ERROR)\]\s+(.*)$/;
|
|
158
268
|
const match = line.match(logPattern);
|
|
159
|
-
|
|
160
269
|
if (match) {
|
|
161
270
|
const label = match[1];
|
|
162
|
-
let msg = match[2];
|
|
163
|
-
|
|
271
|
+
let msg = match[2].trim();
|
|
164
272
|
if (msg.includes("Total time:") || msg.includes("Finished at:") || msg.includes("Final Memory:") || msg.includes("-----------------------")) return "";
|
|
165
|
-
|
|
166
|
-
let
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (label === "INFO") {
|
|
170
|
-
color = this.C.dim;
|
|
171
|
-
prefix = "ℹ";
|
|
172
|
-
} else if (label === "WARNING" || label === "WARN") {
|
|
173
|
-
color = this.C.yellow;
|
|
174
|
-
prefix = "⚠";
|
|
175
|
-
} else if (label === "SEVERE" || label === "ERROR") {
|
|
176
|
-
color = this.C.red;
|
|
177
|
-
prefix = "✘";
|
|
178
|
-
}
|
|
179
|
-
|
|
273
|
+
let color = this.C.dim;
|
|
274
|
+
let symbol = "ℹ";
|
|
275
|
+
if (label === "WARNING") { color = this.C.yellow; symbol = "▲"; }
|
|
276
|
+
else if (label === "SEVERE" || label === "ERROR") { color = this.C.red; symbol = "✖"; }
|
|
180
277
|
msg = msg.replace(/^(org\.apache|com\.sun|java\..*?)\.[a-zA-Z0-9.]+\s/, "").trim();
|
|
181
278
|
if (!msg || msg === "]" || msg.includes("Compilation failure")) return "";
|
|
182
|
-
|
|
183
|
-
return ` ${color}${prefix} ${msg}${this.C.reset}`;
|
|
279
|
+
return `${color}${symbol} ${msg}`;
|
|
184
280
|
}
|
|
185
281
|
|
|
186
|
-
if (line.includes("Exception") || line.includes("
|
|
282
|
+
if (line.includes("Exception") || line.includes("Caused by") || line.includes("at ")) {
|
|
187
283
|
const trimmed = line.trim();
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
return ` ${this.C.yellow}${trimmed}${this.C.reset}`;
|
|
284
|
+
const color = (trimmed.includes("org.apache") || trimmed.includes("java.base") || trimmed.includes("sun.reflect")) ? this.C.dim : this.C.yellow;
|
|
285
|
+
return ` ${color}${trimmed}`;
|
|
192
286
|
}
|
|
193
287
|
|
|
194
288
|
return "";
|