@archznn/xavva 1.6.5 → 1.8.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 +84 -48
- package/package.json +1 -1
- package/src/commands/AuditCommand.ts +6 -10
- package/src/commands/BuildCommand.ts +3 -3
- package/src/commands/Command.ts +2 -2
- package/src/commands/CommandRegistry.ts +36 -0
- package/src/commands/DeployCommand.ts +181 -105
- package/src/commands/DocsCommand.ts +11 -11
- package/src/commands/DoctorCommand.ts +331 -68
- package/src/commands/HelpCommand.ts +2 -1
- package/src/commands/RunCommand.ts +108 -31
- package/src/commands/StartCommand.ts +4 -2
- package/src/index.ts +43 -115
- package/src/services/AuditService.ts +7 -7
- package/src/services/BrowserService.ts +41 -0
- package/src/services/BuildCacheService.ts +75 -0
- package/src/services/BuildService.ts +96 -88
- package/src/services/EndpointService.ts +17 -3
- package/src/services/ProjectService.ts +126 -0
- package/src/services/TomcatService.ts +125 -18
- package/src/services/WatcherService.ts +78 -0
- package/src/types/config.ts +30 -1
- package/src/utils/config.ts +22 -18
- package/src/utils/ui.ts +198 -102
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { watch } from "fs";
|
|
2
|
+
import { Logger } from "../utils/ui";
|
|
3
|
+
import { DeployCommand } from "../commands/DeployCommand";
|
|
4
|
+
import type { AppConfig } from "../types/config";
|
|
5
|
+
|
|
6
|
+
export class WatcherService {
|
|
7
|
+
private isDeploying = false;
|
|
8
|
+
private pendingFullBuild = false;
|
|
9
|
+
private coolingFiles = new Set<string>();
|
|
10
|
+
private debounceTimer?: Timer;
|
|
11
|
+
|
|
12
|
+
constructor(private config: AppConfig, private deployCmd: DeployCommand) {}
|
|
13
|
+
|
|
14
|
+
public async start() {
|
|
15
|
+
await this.run(false);
|
|
16
|
+
|
|
17
|
+
watch(process.cwd(), { recursive: true }, async (event, filename) => {
|
|
18
|
+
if (!filename) return;
|
|
19
|
+
|
|
20
|
+
if (this.coolingFiles.has(filename)) return;
|
|
21
|
+
this.coolingFiles.add(filename);
|
|
22
|
+
setTimeout(() => this.coolingFiles.delete(filename), 500);
|
|
23
|
+
|
|
24
|
+
if (this.isIgnored(filename)) return;
|
|
25
|
+
|
|
26
|
+
const isBuildConfig = filename === "pom.xml" || filename === "build.gradle" || filename === "build.gradle.kts";
|
|
27
|
+
const isJava = filename.endsWith(".java") || isBuildConfig;
|
|
28
|
+
const isResource = this.isResourceFile(filename);
|
|
29
|
+
|
|
30
|
+
if (isBuildConfig) {
|
|
31
|
+
Logger.watcher(`Build configuration changed: ${filename}`, 'warn');
|
|
32
|
+
const { BuildCacheService } = await import("./BuildCacheService");
|
|
33
|
+
new BuildCacheService().clearCache();
|
|
34
|
+
this.pendingFullBuild = true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (isResource && !isJava) {
|
|
38
|
+
await this.deployCmd.syncResource(this.config, filename);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!isJava) return;
|
|
43
|
+
|
|
44
|
+
Logger.watcher(filename, 'watch');
|
|
45
|
+
clearTimeout(this.debounceTimer);
|
|
46
|
+
|
|
47
|
+
this.debounceTimer = setTimeout(() => {
|
|
48
|
+
this.run(this.pendingFullBuild ? false : true);
|
|
49
|
+
this.pendingFullBuild = false;
|
|
50
|
+
}, 1000);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private async run(incremental = false) {
|
|
55
|
+
if (this.isDeploying) return;
|
|
56
|
+
this.isDeploying = true;
|
|
57
|
+
try {
|
|
58
|
+
await this.deployCmd.execute(this.config, { watch: true, incremental });
|
|
59
|
+
} catch (e) {
|
|
60
|
+
// Error handled by command
|
|
61
|
+
} finally {
|
|
62
|
+
this.isDeploying = false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private isIgnored(filename: string): boolean {
|
|
67
|
+
return filename.includes("target") ||
|
|
68
|
+
filename.includes("build") ||
|
|
69
|
+
filename.includes("node_modules") ||
|
|
70
|
+
filename.split(/[/\\]/).some(part => part.startsWith("."));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private isResourceFile(filename: string): boolean {
|
|
74
|
+
return filename.endsWith(".jsp") || filename.endsWith(".html") ||
|
|
75
|
+
filename.endsWith(".css") || filename.endsWith(".js") ||
|
|
76
|
+
filename.endsWith(".xml") || filename.endsWith(".properties");
|
|
77
|
+
}
|
|
78
|
+
}
|
package/src/types/config.ts
CHANGED
|
@@ -11,10 +11,12 @@ export interface ProjectConfig {
|
|
|
11
11
|
profile: string;
|
|
12
12
|
skipBuild: boolean;
|
|
13
13
|
skipScan: boolean;
|
|
14
|
-
|
|
14
|
+
clean: boolean;
|
|
15
15
|
quiet: boolean;
|
|
16
16
|
verbose: boolean;
|
|
17
17
|
debug: boolean;
|
|
18
|
+
debugPort: number;
|
|
19
|
+
cleanLogs: boolean;
|
|
18
20
|
grep?: string;
|
|
19
21
|
}
|
|
20
22
|
|
|
@@ -22,3 +24,30 @@ export interface AppConfig {
|
|
|
22
24
|
tomcat: TomcatConfig;
|
|
23
25
|
project: ProjectConfig;
|
|
24
26
|
}
|
|
27
|
+
|
|
28
|
+
export interface CLIArguments {
|
|
29
|
+
path?: string;
|
|
30
|
+
tool?: string;
|
|
31
|
+
name?: string;
|
|
32
|
+
port?: string;
|
|
33
|
+
"no-build"?: boolean;
|
|
34
|
+
scan?: boolean;
|
|
35
|
+
clean?: boolean;
|
|
36
|
+
quiet?: boolean;
|
|
37
|
+
help?: boolean;
|
|
38
|
+
version?: boolean;
|
|
39
|
+
debug?: boolean;
|
|
40
|
+
watch?: boolean;
|
|
41
|
+
profile?: string;
|
|
42
|
+
grep?: string;
|
|
43
|
+
verbose?: boolean;
|
|
44
|
+
dp?: string;
|
|
45
|
+
fix?: boolean;
|
|
46
|
+
incremental?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface CommandContext {
|
|
50
|
+
config: AppConfig;
|
|
51
|
+
positionals: string[];
|
|
52
|
+
values: CLIArguments;
|
|
53
|
+
}
|
package/src/utils/config.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { parseArgs } from "util";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import fs from "fs";
|
|
4
|
-
import type { AppConfig } from "../types/config";
|
|
4
|
+
import type { AppConfig, CLIArguments, CommandContext } from "../types/config";
|
|
5
5
|
|
|
6
6
|
export class ConfigManager {
|
|
7
|
-
static async load(): Promise<
|
|
7
|
+
static async load(): Promise<CommandContext> {
|
|
8
8
|
const args = Bun.argv.slice(Bun.argv[0].endsWith("bun.exe") || Bun.argv[0].endsWith("bun") ? 2 : 1);
|
|
9
9
|
|
|
10
10
|
const { values, positionals } = parseArgs({
|
|
@@ -25,12 +25,14 @@ export class ConfigManager {
|
|
|
25
25
|
profile: { type: "string", short: "P" },
|
|
26
26
|
grep: { type: "string", short: "G" },
|
|
27
27
|
verbose: { type: "boolean", short: "V" },
|
|
28
|
+
dp: { type: "string" },
|
|
28
29
|
fix: { type: "boolean" },
|
|
29
30
|
},
|
|
30
31
|
strict: false,
|
|
31
32
|
allowPositionals: true,
|
|
32
33
|
});
|
|
33
34
|
|
|
35
|
+
const cliValues = values as CLIArguments;
|
|
34
36
|
const isDev = positionals.includes("dev");
|
|
35
37
|
const isRun = positionals.includes("run") || positionals.includes("debug");
|
|
36
38
|
|
|
@@ -47,30 +49,32 @@ export class ConfigManager {
|
|
|
47
49
|
|
|
48
50
|
const config: AppConfig = {
|
|
49
51
|
tomcat: {
|
|
50
|
-
path: String(
|
|
51
|
-
port: parseInt(String(
|
|
52
|
+
path: String(cliValues.path || envTomcatPath),
|
|
53
|
+
port: parseInt(String(cliValues.port || "8080")),
|
|
52
54
|
webapps: "webapps",
|
|
53
|
-
grep:
|
|
55
|
+
grep: cliValues.grep ? String(cliValues.grep) : "",
|
|
54
56
|
},
|
|
55
57
|
project: {
|
|
56
|
-
appName:
|
|
57
|
-
buildTool: (
|
|
58
|
-
profile: String(
|
|
59
|
-
skipBuild: !!
|
|
60
|
-
skipScan:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
58
|
+
appName: cliValues.name ? String(cliValues.name) : "",
|
|
59
|
+
buildTool: (cliValues.tool as "maven" | "gradle") || detectedTool,
|
|
60
|
+
profile: String(cliValues.profile || ""),
|
|
61
|
+
skipBuild: !!cliValues["no-build"],
|
|
62
|
+
skipScan: cliValues.scan !== undefined ? !cliValues.scan : true,
|
|
63
|
+
clean: !!cliValues.clean,
|
|
64
|
+
cleanLogs: cliValues.verbose ? false : true,
|
|
65
|
+
quiet: cliValues.verbose ? false : true,
|
|
66
|
+
verbose: !!cliValues.verbose,
|
|
67
|
+
debug: !!(cliValues.debug || isDev || isRun),
|
|
68
|
+
debugPort: parseInt(String(cliValues.dp || "5005")),
|
|
69
|
+
grep: runClass || (cliValues.grep ? String(cliValues.grep) : ""),
|
|
66
70
|
}
|
|
67
71
|
};
|
|
68
72
|
|
|
69
|
-
if (isDev)
|
|
73
|
+
if (isDev) cliValues.watch = true;
|
|
70
74
|
|
|
71
75
|
this.ensureGitIgnore();
|
|
72
76
|
|
|
73
|
-
return { config, positionals, values };
|
|
77
|
+
return { config, positionals, values: cliValues };
|
|
74
78
|
}
|
|
75
79
|
|
|
76
80
|
private static detectBuildTool(): "maven" | "gradle" {
|
|
@@ -80,7 +84,7 @@ export class ConfigManager {
|
|
|
80
84
|
if (fs.existsSync(path.join(process.cwd(), "build.gradle")) || fs.existsSync(path.join(process.cwd(), "build.gradle.kts"))) {
|
|
81
85
|
return "gradle";
|
|
82
86
|
}
|
|
83
|
-
return "maven";
|
|
87
|
+
return "maven";
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
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() || "PROJECT").toUpperCase();
|
|
58
|
+
const version = `v${pkg.version}`;
|
|
59
|
+
|
|
60
|
+
const mode = command?.toUpperCase() || "DEPLOY";
|
|
61
|
+
const modeColor = mode === "DEV" ? this.C.green : this.C.blue;
|
|
62
|
+
const modeIcon = mode === "DEV" ? "⚡" : "🚀";
|
|
63
|
+
|
|
64
|
+
console.log("");
|
|
65
|
+
console.log(` ${this.C.bold}${this.C.cyan}X A V V A${this.C.reset} ${this.C.dim}─${this.C.reset} ${this.C.bold}${this.C.white}${name}${this.C.reset}`);
|
|
66
|
+
|
|
67
|
+
const gitInfo = git.branch ? `${this.C.magenta}🌿 ${git.branch}${this.C.reset} ${this.C.dim}•${this.C.reset} ${this.C.yellow}${git.hash}${this.C.reset}` : "";
|
|
68
|
+
console.log(` ${this.C.dim}📦 ${version}${gitInfo ? ` ${this.C.dim}•${this.C.reset} ${gitInfo}` : ""}${this.C.reset}`);
|
|
69
|
+
|
|
70
|
+
console.log(` ${modeColor}${this.C.bold}⬢ ${modeIcon} ${mode} MODE${this.C.reset}`);
|
|
71
|
+
console.log(` ${this.C.dim}─────────────────────────────────────────────────${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,153 @@ 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
|
-
"
|
|
100
|
-
"
|
|
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", "initialized in ClassLoader", "Discovered plugins:",
|
|
166
|
+
"enhanced with plugin initialization", "registerJerseyContainer", "JasperLoader@",
|
|
167
|
+
"Hotswap ready (Plugins:", "autoHotswap.delay", "watchResources=false"
|
|
112
168
|
];
|
|
113
169
|
return noise.some(n => line.includes(n));
|
|
114
170
|
}
|
|
115
171
|
|
|
116
172
|
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");
|
|
173
|
+
return line.includes("SEVERE") || line.includes("ERROR") || line.includes("Exception") ||
|
|
174
|
+
line.includes("Caused by") || line.includes("at ") || line.includes("... ") ||
|
|
175
|
+
line.includes("Server startup in") || line.includes("HOTSWAP AGENT:");
|
|
124
176
|
}
|
|
125
177
|
|
|
126
178
|
static summarize(line: string): string {
|
|
179
|
+
if (this.isSystemNoise(line)) return "";
|
|
180
|
+
|
|
127
181
|
const startupMatch = line.match(/Server startup in (\[?)(.*?)(\]?)\s*ms/);
|
|
128
182
|
if (startupMatch) {
|
|
129
|
-
const time = startupMatch[2]
|
|
130
|
-
|
|
183
|
+
const time = (parseInt(startupMatch[2]) / 1000).toFixed(1);
|
|
184
|
+
this.domain("server");
|
|
185
|
+
return `${this.C.green}✔ ${this.C.bold}Server started in ${time}s`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const deployMatch = line.match(/Deployment of web application archive \[(.*?)\] has finished in \[(.*?)\] ms/);
|
|
189
|
+
if (deployMatch) {
|
|
190
|
+
this.domain("build");
|
|
191
|
+
return `${this.C.green}✔ Artifacts deployed`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const hotswapPattern = /HOTSWAP AGENT:.*? (INFO|WARN|ERROR|RELOAD) (.*?) - (.*)/;
|
|
195
|
+
const hotswapMatch = line.match(hotswapPattern);
|
|
196
|
+
if (hotswapMatch) {
|
|
197
|
+
const level = hotswapMatch[1];
|
|
198
|
+
let msg = hotswapMatch[3];
|
|
199
|
+
|
|
200
|
+
if (msg.includes("plugin initialized")) {
|
|
201
|
+
this.hotswapPluginsCount++;
|
|
202
|
+
return "";
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (msg.includes("redefinition") || msg.includes("reloaded") || level === 'RELOAD') {
|
|
206
|
+
if (msg.includes("Reloading classes [")) {
|
|
207
|
+
const classes = msg.match(/\[(.*?)\]/)?.[1] || "";
|
|
208
|
+
const classCount = classes.split(",").length;
|
|
209
|
+
if (classCount > 3) msg = `Reloading ${classCount} classes...`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (msg === this.lastHotswapMsg) return "";
|
|
213
|
+
this.lastHotswapMsg = msg;
|
|
214
|
+
|
|
215
|
+
this.watcher(`Hotswap: ${msg.replace(/Class '.*?'/, (m) => this.C.bold + m + this.C.reset)}`, 'success');
|
|
216
|
+
return "";
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (msg.includes("Loading Hotswap agent")) {
|
|
220
|
+
this.domain("server");
|
|
221
|
+
return `${this.C.blue}▶ ${this.C.reset}Initializing Hotswap Agent ${msg.match(/\d+\.\d+\.\d+/)?.[0] || ""}`;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (this.hotswapPluginsCount > 0) {
|
|
225
|
+
const count = this.hotswapPluginsCount;
|
|
226
|
+
this.hotswapPluginsCount = 0;
|
|
227
|
+
this.domain("server");
|
|
228
|
+
this.write(` ${this.C.green}✔ ${this.C.reset}Hotswap ready (Plugins: ${count} loaded)`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
let color = this.C.cyan;
|
|
232
|
+
let symbol = "●";
|
|
233
|
+
if (level === "WARN") { color = this.C.yellow; symbol = "▲"; }
|
|
234
|
+
else if (level === "ERROR") { color = this.C.red; symbol = "✖"; }
|
|
235
|
+
|
|
236
|
+
this.domain("server");
|
|
237
|
+
return `${color}${symbol} ${this.C.bold}Hotswap:${this.C.reset} ${msg}`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (line.includes("java.lang.UnsupportedOperationException") && (line.includes("add a method") || line.includes("change the schema"))) {
|
|
241
|
+
this.domain("watcher");
|
|
242
|
+
this.write(` ${this.C.red}✖ ${this.C.bold}Hotswap Falhou:${this.C.reset} Mudança estrutural detectada (novo método/campo).`);
|
|
243
|
+
this.write(` ${this.C.yellow}💡 Dica: Sua JVM atual não suporta mudar a estrutura da classe. Reinicie o servidor para aplicar.`);
|
|
244
|
+
return "";
|
|
131
245
|
}
|
|
132
246
|
|
|
133
|
-
|
|
134
|
-
|
|
247
|
+
const tomcatPattern = /^(\d{2}-\w{3}-\d{4} \d{2}:\d{2}:\d{2}\.\d{3})\s+(INFO|WARNING|SEVERE|ERROR)\s+\[(.*?)\]\s+(.*)$/;
|
|
248
|
+
const tMatch = line.match(tomcatPattern);
|
|
249
|
+
if (tMatch) {
|
|
250
|
+
const label = tMatch[2];
|
|
251
|
+
let msg = tMatch[4].trim();
|
|
252
|
+
if (this.isSystemNoise(msg)) return "";
|
|
253
|
+
let color = this.C.dim;
|
|
254
|
+
let symbol = "ℹ";
|
|
255
|
+
if (label === "WARNING") { color = this.C.yellow; symbol = "▲"; }
|
|
256
|
+
else if (label === "SEVERE" || label === "ERROR") { color = this.C.red; symbol = "✖"; }
|
|
257
|
+
msg = msg.replace(/^(org\.apache|com\.sun|java\..*?|org\.glassfish)\.[a-zA-Z0-9.]+\s/, "").trim();
|
|
258
|
+
if (!msg) return "";
|
|
259
|
+
return `${color}${symbol} ${msg}`;
|
|
260
|
+
}
|
|
135
261
|
|
|
136
262
|
const compilationErrorMatch = line.match(/^\[ERROR\]\s+(.*\.java):\[(\d+),(\d+)\]\s+(.*)$/);
|
|
137
263
|
if (compilationErrorMatch) {
|
|
138
264
|
const [_, filePath, row, col, msg] = compilationErrorMatch;
|
|
139
265
|
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`;
|
|
266
|
+
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
267
|
}
|
|
156
268
|
|
|
157
269
|
const logPattern = /^\[(INFO|WARNING|WARN|SEVERE|ERROR)\]\s+(.*)$/;
|
|
158
270
|
const match = line.match(logPattern);
|
|
159
|
-
|
|
160
271
|
if (match) {
|
|
161
272
|
const label = match[1];
|
|
162
|
-
let msg = match[2];
|
|
163
|
-
|
|
273
|
+
let msg = match[2].trim();
|
|
164
274
|
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
|
-
|
|
275
|
+
let color = this.C.dim;
|
|
276
|
+
let symbol = "ℹ";
|
|
277
|
+
if (label === "WARNING") { color = this.C.yellow; symbol = "▲"; }
|
|
278
|
+
else if (label === "SEVERE" || label === "ERROR") { color = this.C.red; symbol = "✖"; }
|
|
180
279
|
msg = msg.replace(/^(org\.apache|com\.sun|java\..*?)\.[a-zA-Z0-9.]+\s/, "").trim();
|
|
181
280
|
if (!msg || msg === "]" || msg.includes("Compilation failure")) return "";
|
|
182
|
-
|
|
183
|
-
return ` ${color}${prefix} ${msg}${this.C.reset}`;
|
|
281
|
+
return `${color}${symbol} ${msg}`;
|
|
184
282
|
}
|
|
185
283
|
|
|
186
|
-
if (line.includes("Exception") || line.includes("
|
|
284
|
+
if (line.includes("Exception") || line.includes("Caused by") || line.includes("at ")) {
|
|
187
285
|
const trimmed = line.trim();
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
return ` ${this.C.yellow}${trimmed}${this.C.reset}`;
|
|
286
|
+
const color = (trimmed.includes("org.apache") || trimmed.includes("java.base") || trimmed.includes("sun.reflect")) ? this.C.dim : this.C.yellow;
|
|
287
|
+
return ` ${color}${trimmed}`;
|
|
192
288
|
}
|
|
193
289
|
|
|
194
290
|
return "";
|