@archznn/xavva 1.6.5
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/LICENSE +21 -0
- package/README.md +75 -0
- package/package.json +44 -0
- package/src/commands/AuditCommand.ts +118 -0
- package/src/commands/BuildCommand.ts +22 -0
- package/src/commands/Command.ts +5 -0
- package/src/commands/DeployCommand.ts +162 -0
- package/src/commands/DocsCommand.ts +90 -0
- package/src/commands/DoctorCommand.ts +89 -0
- package/src/commands/HelpCommand.ts +49 -0
- package/src/commands/LogsCommand.ts +64 -0
- package/src/commands/RunCommand.ts +283 -0
- package/src/commands/StartCommand.ts +26 -0
- package/src/index.ts +155 -0
- package/src/services/AuditService.ts +148 -0
- package/src/services/BuildService.ts +198 -0
- package/src/services/EndpointService.ts +124 -0
- package/src/services/TomcatService.ts +164 -0
- package/src/types/config.ts +24 -0
- package/src/types/endpoint.ts +15 -0
- package/src/utils/config.ts +100 -0
- package/src/utils/ui.ts +196 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { parseArgs } from "util";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import type { AppConfig } from "../types/config";
|
|
5
|
+
|
|
6
|
+
export class ConfigManager {
|
|
7
|
+
static async load(): Promise<{ config: AppConfig, positionals: string[], values: any }> {
|
|
8
|
+
const args = Bun.argv.slice(Bun.argv[0].endsWith("bun.exe") || Bun.argv[0].endsWith("bun") ? 2 : 1);
|
|
9
|
+
|
|
10
|
+
const { values, positionals } = parseArgs({
|
|
11
|
+
args: args,
|
|
12
|
+
options: {
|
|
13
|
+
path: { type: "string", short: "p" },
|
|
14
|
+
tool: { type: "string", short: "t" },
|
|
15
|
+
name: { type: "string", short: "n" },
|
|
16
|
+
port: { type: "string" },
|
|
17
|
+
"no-build": { type: "boolean", short: "s" },
|
|
18
|
+
"scan": { type: "boolean" },
|
|
19
|
+
clean: { type: "boolean", short: "c" },
|
|
20
|
+
quiet: { type: "boolean", short: "q" },
|
|
21
|
+
help: { type: "boolean", short: "h" },
|
|
22
|
+
version: { type: "boolean", short: "v" },
|
|
23
|
+
debug: { type: "boolean", short: "d" },
|
|
24
|
+
watch: { type: "boolean", short: "w" },
|
|
25
|
+
profile: { type: "string", short: "P" },
|
|
26
|
+
grep: { type: "string", short: "G" },
|
|
27
|
+
verbose: { type: "boolean", short: "V" },
|
|
28
|
+
fix: { type: "boolean" },
|
|
29
|
+
},
|
|
30
|
+
strict: false,
|
|
31
|
+
allowPositionals: true,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const isDev = positionals.includes("dev");
|
|
35
|
+
const isRun = positionals.includes("run") || positionals.includes("debug");
|
|
36
|
+
|
|
37
|
+
const envTomcatPath = process.env.TOMCAT_HOME || process.env.CATALINA_HOME || "C:\\apache-tomcat";
|
|
38
|
+
const detectedTool = this.detectBuildTool();
|
|
39
|
+
|
|
40
|
+
let runClass = "";
|
|
41
|
+
if (isRun) {
|
|
42
|
+
const runIdx = positionals.indexOf("run");
|
|
43
|
+
const debugIdx = positionals.indexOf("debug");
|
|
44
|
+
const idx = runIdx !== -1 ? runIdx : debugIdx;
|
|
45
|
+
runClass = positionals[idx + 1] || "";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const config: AppConfig = {
|
|
49
|
+
tomcat: {
|
|
50
|
+
path: String(values.path || envTomcatPath),
|
|
51
|
+
port: parseInt(String(values.port || "8080")),
|
|
52
|
+
webapps: "webapps",
|
|
53
|
+
grep: values.grep ? String(values.grep) : "",
|
|
54
|
+
},
|
|
55
|
+
project: {
|
|
56
|
+
appName: values.name ? String(values.name) : "",
|
|
57
|
+
buildTool: (values.tool as "maven" | "gradle") || detectedTool,
|
|
58
|
+
profile: String(values.profile || ""),
|
|
59
|
+
skipBuild: !!values["no-build"],
|
|
60
|
+
skipScan: values.scan !== undefined ? !values.scan : true,
|
|
61
|
+
cleanLogs: !!(values.clean || isDev),
|
|
62
|
+
quiet: !!(values.quiet || isDev),
|
|
63
|
+
verbose: !!values.verbose,
|
|
64
|
+
debug: !!(values.debug || isDev || isRun),
|
|
65
|
+
grep: runClass || (values.grep ? String(values.grep) : ""),
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
if (isDev) values.watch = true;
|
|
70
|
+
|
|
71
|
+
this.ensureGitIgnore();
|
|
72
|
+
|
|
73
|
+
return { config, positionals, values };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private static detectBuildTool(): "maven" | "gradle" {
|
|
77
|
+
if (fs.existsSync(path.join(process.cwd(), "pom.xml"))) {
|
|
78
|
+
return "maven";
|
|
79
|
+
}
|
|
80
|
+
if (fs.existsSync(path.join(process.cwd(), "build.gradle")) || fs.existsSync(path.join(process.cwd(), "build.gradle.kts"))) {
|
|
81
|
+
return "gradle";
|
|
82
|
+
}
|
|
83
|
+
return "maven"; // Default to maven if not found
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private static ensureGitIgnore() {
|
|
87
|
+
const gitignorePath = path.join(process.cwd(), ".gitignore");
|
|
88
|
+
|
|
89
|
+
if (!fs.existsSync(gitignorePath)) return;
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const content = fs.readFileSync(gitignorePath, "utf8");
|
|
93
|
+
if (!content.includes(".xavva")) {
|
|
94
|
+
const newContent = content.trim() + "\n\n# Xavva CLI\n.xavva/\n";
|
|
95
|
+
fs.writeFileSync(gitignorePath, newContent);
|
|
96
|
+
}
|
|
97
|
+
} catch (e) {
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
package/src/utils/ui.ts
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import pkg from "../../package.json";
|
|
2
|
+
|
|
3
|
+
export class Logger {
|
|
4
|
+
private static readonly C = {
|
|
5
|
+
reset: "\x1b[0m",
|
|
6
|
+
cyan: "\x1b[36m",
|
|
7
|
+
green: "\x1b[32m",
|
|
8
|
+
yellow: "\x1b[33m",
|
|
9
|
+
red: "\x1b[31m",
|
|
10
|
+
dim: "\x1b[90m",
|
|
11
|
+
bold: "\x1b[1m",
|
|
12
|
+
blue: "\x1b[34m"
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
static getGitContext(): string {
|
|
16
|
+
try {
|
|
17
|
+
const branch = Bun.spawnSync(["git", "rev-parse", "--abbrev-ref", "HEAD"]).stdout.toString().trim();
|
|
18
|
+
const author = Bun.spawnSync(["git", "log", "-1", "--format=%an"]).stdout.toString().trim();
|
|
19
|
+
return branch ? `${this.C.blue} ${branch}${this.C.reset} ${this.C.dim}(${author})${this.C.reset}` : "";
|
|
20
|
+
} catch (e) {
|
|
21
|
+
return "";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static banner(command?: string) {
|
|
26
|
+
console.clear();
|
|
27
|
+
const git = this.getGitContext();
|
|
28
|
+
console.log(`${this.C.cyan}
|
|
29
|
+
${this.C.bold}XAVVA ${this.C.reset}${this.C.dim}v${pkg.version}${this.C.reset} ${git}
|
|
30
|
+
${this.C.dim}──────────────────────────${this.C.reset}`);
|
|
31
|
+
if (command) {
|
|
32
|
+
console.log(` ${this.C.yellow}${this.C.bold}MODO: ${command.toUpperCase()}${this.C.reset}\n`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static section(title: string) {
|
|
37
|
+
console.log(`\n ${this.C.bold}${this.C.blue}◈ ${title.toUpperCase()} ${this.C.reset}`);
|
|
38
|
+
console.log(` ${this.C.dim}──────────────────────────${this.C.reset}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static info(label: string, value: string | number | boolean) {
|
|
42
|
+
console.log(` ${this.C.cyan}${label.padEnd(12)}${this.C.reset} ${this.C.bold}${value}${this.C.reset}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static success(msg: string) {
|
|
46
|
+
console.log(`\n ${this.C.green}✔ ${msg}${this.C.reset}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static error(msg: string) {
|
|
50
|
+
console.error(`\n ${this.C.red}✘ ${msg}${this.C.reset}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static warn(msg: string) {
|
|
54
|
+
console.log(` ${this.C.yellow}⚠ ${msg}${this.C.reset}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static log(msg: string) {
|
|
58
|
+
console.log(` ${msg}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static step(msg: string) {
|
|
62
|
+
console.log(` ${this.C.dim}➜ ${msg}${this.C.reset}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static spinner(msg: string) {
|
|
66
|
+
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
67
|
+
let i = 0;
|
|
68
|
+
process.stdout.write("\x1B[?25l");
|
|
69
|
+
|
|
70
|
+
const timer = setInterval(() => {
|
|
71
|
+
process.stdout.write(`\r ${this.C.cyan}${frames[i]}${this.C.reset} ${msg}...`);
|
|
72
|
+
i = (i + 1) % frames.length;
|
|
73
|
+
}, 80);
|
|
74
|
+
|
|
75
|
+
return (success = true) => {
|
|
76
|
+
clearInterval(timer);
|
|
77
|
+
process.stdout.write("\r\x1B[K");
|
|
78
|
+
process.stdout.write("\x1B[?25h");
|
|
79
|
+
if (success) {
|
|
80
|
+
console.log(` ${this.C.green}✔${this.C.reset} ${msg}`);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static isSystemNoise(line: string): boolean {
|
|
86
|
+
const noise = [
|
|
87
|
+
"Using CATALINA_",
|
|
88
|
+
"Using JRE_HOME",
|
|
89
|
+
"Using CLASSPATH",
|
|
90
|
+
"NOTE: Picked up JDK_JAVA_OPTIONS",
|
|
91
|
+
"Command line argument",
|
|
92
|
+
"VersionLoggerListener",
|
|
93
|
+
"Scanning for projects...",
|
|
94
|
+
"Building ",
|
|
95
|
+
"--- ",
|
|
96
|
+
"+++ ",
|
|
97
|
+
"DEBUG: ",
|
|
98
|
+
"org.apache.catalina.startup.VersionLoggerListener",
|
|
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"
|
|
112
|
+
];
|
|
113
|
+
return noise.some(n => line.includes(n));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static isEssential(line: string): boolean {
|
|
117
|
+
return line.includes("SEVERE") ||
|
|
118
|
+
line.includes("ERROR") ||
|
|
119
|
+
line.includes("Exception") ||
|
|
120
|
+
line.includes("Caused by") ||
|
|
121
|
+
line.includes("at ") ||
|
|
122
|
+
line.includes("... ") ||
|
|
123
|
+
line.includes("Server startup in");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
static summarize(line: string): string {
|
|
127
|
+
const startupMatch = line.match(/Server startup in (\[?)(.*?)(\]?)\s*ms/);
|
|
128
|
+
if (startupMatch) {
|
|
129
|
+
const time = startupMatch[2] || "???";
|
|
130
|
+
return `\n ${this.C.green}${this.C.bold}🚀 TOMCAT PRONTO EM ${time}ms${this.C.reset}\n`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (line.match(/^\[(INFO|WARN|ERROR)\]\s*$/) || line.includes("--- maven-") || line.includes("--- bpo-")) return "";
|
|
134
|
+
if (line.includes("Scanning for projects...") || line.includes("Building bpo-consig") || line.includes("--- ")) return "";
|
|
135
|
+
|
|
136
|
+
const compilationErrorMatch = line.match(/^\[ERROR\]\s+(.*\.java):\[(\d+),(\d+)\]\s+(.*)$/);
|
|
137
|
+
if (compilationErrorMatch) {
|
|
138
|
+
const [_, filePath, row, col, msg] = compilationErrorMatch;
|
|
139
|
+
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`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const logPattern = /^\[(INFO|WARNING|WARN|SEVERE|ERROR)\]\s+(.*)$/;
|
|
158
|
+
const match = line.match(logPattern);
|
|
159
|
+
|
|
160
|
+
if (match) {
|
|
161
|
+
const label = match[1];
|
|
162
|
+
let msg = match[2];
|
|
163
|
+
|
|
164
|
+
if (msg.includes("Total time:") || msg.includes("Finished at:") || msg.includes("Final Memory:") || msg.includes("-----------------------")) return "";
|
|
165
|
+
|
|
166
|
+
let color = "";
|
|
167
|
+
let prefix = "";
|
|
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
|
+
|
|
180
|
+
msg = msg.replace(/^(org\.apache|com\.sun|java\..*?)\.[a-zA-Z0-9.]+\s/, "").trim();
|
|
181
|
+
if (!msg || msg === "]" || msg.includes("Compilation failure")) return "";
|
|
182
|
+
|
|
183
|
+
return ` ${color}${prefix} ${msg}${this.C.reset}`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (line.includes("Exception") || line.includes("at ") || line.includes("Caused by")) {
|
|
187
|
+
const trimmed = line.trim();
|
|
188
|
+
if (trimmed.includes("org.apache") || trimmed.includes("java.base") || trimmed.includes("sun.reflect")) {
|
|
189
|
+
return ` ${this.C.dim}${trimmed}${this.C.reset}`;
|
|
190
|
+
}
|
|
191
|
+
return ` ${this.C.yellow}${trimmed}${this.C.reset}`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return "";
|
|
195
|
+
}
|
|
196
|
+
}
|