@archznn/xavva 2.6.0 → 2.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 +55 -0
- package/package.json +1 -1
- package/src/commands/DeployCommand.ts +2 -1
- package/src/commands/EncodingCommand.ts +351 -0
- package/src/commands/HelpCommand.ts +15 -0
- package/src/config/versions.ts +63 -0
- package/src/di/container.ts +226 -0
- package/src/errors/ErrorHandler.ts +249 -0
- package/src/errors/XavvaError.ts +273 -0
- package/src/index.ts +98 -96
- package/src/services/AuditService.ts +3 -2
- package/src/services/BrowserService.ts +127 -16
- package/src/services/DeployWatcher.ts +183 -0
- package/src/services/EmbeddedTomcatService.ts +5 -19
- package/src/services/EncodingService.ts +548 -0
- package/src/services/FileWatcher.ts +243 -0
- package/src/services/TomcatService.ts +9 -5
- package/src/types/args.ts +136 -0
- package/src/types/config.ts +6 -0
- package/src/types/index.ts +7 -0
- package/src/utils/PathUtils.ts +221 -0
- package/src/utils/config.ts +6 -0
- package/src/utils/parsers/JavaParser.ts +413 -0
- package/src/utils/platform.ts +2 -2
- package/src/services/WatcherService.ts +0 -117
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@ Xavva is a high-performance CLI built with **Bun** that transforms the Java/Tomc
|
|
|
20
20
|
- 🔧 **Auto-Healing** — Automatic diagnosis and repair of common issues
|
|
21
21
|
- 🐱 **Embedded Tomcat** — Auto-install Tomcat, no manual setup needed
|
|
22
22
|
- 📦 **WAR Generation** — Build as .war file or exploded directory
|
|
23
|
+
- 🔤 **Encoding Converter** — Convert file encodings (UTF-8, Windows-1252, ISO-8859-1) and fix mojibake
|
|
23
24
|
|
|
24
25
|
---
|
|
25
26
|
|
|
@@ -56,6 +57,9 @@ xavva deps --update-safe
|
|
|
56
57
|
# Check for security vulnerabilities
|
|
57
58
|
xavva audit
|
|
58
59
|
|
|
60
|
+
# Convert file encoding (UTF-8 → Windows-1252)
|
|
61
|
+
xavva encoding convert --to cp1252 --backup src/main/java/
|
|
62
|
+
|
|
59
63
|
# Use embedded Tomcat (auto-install)
|
|
60
64
|
xavva dev --yes
|
|
61
65
|
```
|
|
@@ -91,6 +95,7 @@ xavva dev --yes
|
|
|
91
95
|
| `xavva profiles` | List available Maven/Gradle profiles |
|
|
92
96
|
| `xavva docs` | Generate endpoint documentation |
|
|
93
97
|
| `xavva tomcat` | Manage embedded Tomcat installations |
|
|
98
|
+
| `xavva encoding` | Convert file encodings (UTF-8, CP1252, ISO-8859-1) |
|
|
94
99
|
|
|
95
100
|
---
|
|
96
101
|
|
|
@@ -126,6 +131,56 @@ xavva dev --tomcat-version 9.0.115
|
|
|
126
131
|
|
|
127
132
|
---
|
|
128
133
|
|
|
134
|
+
## 🔤 File Encoding
|
|
135
|
+
|
|
136
|
+
The `xavva encoding` command helps you convert file encodings and fix mojibake (corrupted characters):
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
# Detect encoding of a file
|
|
140
|
+
xavva encoding detect src/main/java/MyClass.java
|
|
141
|
+
|
|
142
|
+
# Convert from UTF-8 to Windows-1252 (with backup)
|
|
143
|
+
xavva encoding convert --from utf-8 --to cp1252 --backup src/main/java/
|
|
144
|
+
|
|
145
|
+
# Convert a single file
|
|
146
|
+
xavva encoding convert --to cp1252 --backup src/main/java/MyClass.java
|
|
147
|
+
|
|
148
|
+
# Fix mojibake (e.g., "A��o" → "Ação")
|
|
149
|
+
xavva encoding fix src/main/java/MyClass.java
|
|
150
|
+
|
|
151
|
+
# List encodings of all files in src/
|
|
152
|
+
xavva encoding list
|
|
153
|
+
|
|
154
|
+
# Simulate conversion without modifying files
|
|
155
|
+
xavva encoding convert --from utf-8 --to cp1252 --dry-run src/
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Supported Encodings
|
|
159
|
+
|
|
160
|
+
- **utf-8** / **utf8** — UTF-8 (default)
|
|
161
|
+
- **windows-1252** / **cp1252** — Windows CP1252 (ANSI)
|
|
162
|
+
- **iso-8859-1** / **latin1** — ISO-8859-1 (Latin-1)
|
|
163
|
+
|
|
164
|
+
### Configuration
|
|
165
|
+
|
|
166
|
+
Set default encoding in `xavva.json`:
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"project": {
|
|
171
|
+
"encoding": "cp1252"
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Then use without `--to`:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
xavva encoding convert --backup src/main/java/
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
129
184
|
## 🔍 Dependency Analysis
|
|
130
185
|
|
|
131
186
|
The `xavva deps` command provides comprehensive dependency analysis:
|
package/package.json
CHANGED
|
@@ -260,10 +260,11 @@ export class DeployCommand implements Command {
|
|
|
260
260
|
if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
|
|
261
261
|
|
|
262
262
|
fs.copyFileSync(filename, targetPath);
|
|
263
|
-
|
|
263
|
+
Logger.success(`${path.basename(filename)} updated → Tomcat`);
|
|
264
264
|
|
|
265
265
|
const appUrl = `http://localhost:${config.tomcat.port}/${appFolder}`;
|
|
266
266
|
await BrowserService.reload(appUrl);
|
|
267
|
+
Logger.ready(`Browser reloaded`);
|
|
267
268
|
} catch (e) {
|
|
268
269
|
Logger.error(`Failed to sync resource: ${filename}`);
|
|
269
270
|
}
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import type { Command } from "./Command";
|
|
2
|
+
import type { AppConfig, CLIArguments } from "../types/config";
|
|
3
|
+
import { EncodingService } from "../services/EncodingService";
|
|
4
|
+
import { Logger } from "../utils/ui";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { existsSync } from "fs";
|
|
7
|
+
import { ProcessManager } from "../utils/processManager";
|
|
8
|
+
|
|
9
|
+
export class EncodingCommand implements Command {
|
|
10
|
+
private encodingService: EncodingService;
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
this.encodingService = new EncodingService();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async execute(config: AppConfig, args?: CLIArguments, positionals?: string[]): Promise<void> {
|
|
17
|
+
const processManager = ProcessManager.getInstance();
|
|
18
|
+
|
|
19
|
+
// Parse subcomando
|
|
20
|
+
const subcommand = positionals?.[1] || "help";
|
|
21
|
+
const fileArg = positionals?.[2]; // Arquivo específico (opcional)
|
|
22
|
+
|
|
23
|
+
switch (subcommand) {
|
|
24
|
+
case "detect":
|
|
25
|
+
await this.handleDetect(fileArg);
|
|
26
|
+
break;
|
|
27
|
+
case "convert":
|
|
28
|
+
await this.handleConvert(config, args, fileArg);
|
|
29
|
+
break;
|
|
30
|
+
case "fix":
|
|
31
|
+
await this.handleFix(fileArg, args);
|
|
32
|
+
break;
|
|
33
|
+
case "list":
|
|
34
|
+
await this.handleList(config);
|
|
35
|
+
break;
|
|
36
|
+
case "help":
|
|
37
|
+
default:
|
|
38
|
+
this.showHelp();
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await processManager.shutdown(0);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private showHelp() {
|
|
46
|
+
Logger.section("Encoding Command");
|
|
47
|
+
Logger.log("Gerencia conversão de encoding de arquivos de texto");
|
|
48
|
+
Logger.newline();
|
|
49
|
+
|
|
50
|
+
Logger.log(`${Logger.C.primary}Uso:${Logger.C.reset}`);
|
|
51
|
+
Logger.log(" xavva encoding <subcomando> [arquivo] [opções]");
|
|
52
|
+
Logger.newline();
|
|
53
|
+
|
|
54
|
+
Logger.log(`${Logger.C.primary}Subcomandos:${Logger.C.reset}`);
|
|
55
|
+
Logger.log(` ${Logger.C.secondary}detect${Logger.C.reset} [arquivo] Detecta encoding de um arquivo ou diretório`);
|
|
56
|
+
Logger.log(` ${Logger.C.secondary}convert${Logger.C.reset} [arquivo] Converte arquivo(s) para outro encoding`);
|
|
57
|
+
Logger.log(` ${Logger.C.secondary}fix${Logger.C.reset} [arquivo] Tenta corrigir mojibake automaticamente`);
|
|
58
|
+
Logger.log(` ${Logger.C.secondary}list${Logger.C.reset} Lista encodings de todos os arquivos do projeto`);
|
|
59
|
+
Logger.log(` ${Logger.C.secondary}help${Logger.C.reset} Mostra esta ajuda`);
|
|
60
|
+
Logger.newline();
|
|
61
|
+
|
|
62
|
+
Logger.log(`${Logger.C.primary}Opções:${Logger.C.reset}`);
|
|
63
|
+
Logger.log(` --from <encoding> Encoding de origem (padrão: auto-detect)`);
|
|
64
|
+
Logger.log(` --to <encoding> Encoding de destino (padrão: do xavva.json ou UTF-8)`);
|
|
65
|
+
Logger.log(` --backup Cria backup antes de converter`);
|
|
66
|
+
Logger.log(` --dry-run Simula sem modificar arquivos`);
|
|
67
|
+
Logger.log(` --force Força correção mesmo se detectado como UTF-8`);
|
|
68
|
+
Logger.log(` --src <path> Diretório fonte (padrão: src/)`);
|
|
69
|
+
Logger.newline();
|
|
70
|
+
|
|
71
|
+
Logger.log(`${Logger.C.primary}Encodings suportados:${Logger.C.reset}`);
|
|
72
|
+
Logger.log(` utf-8, utf8 UTF-8 (padrão)`);
|
|
73
|
+
Logger.log(` windows-1252, cp1252 Windows CP1252 (ANSI)`);
|
|
74
|
+
Logger.log(` iso-8859-1, latin1 ISO-8859-1 (Latin-1)`);
|
|
75
|
+
Logger.newline();
|
|
76
|
+
|
|
77
|
+
Logger.log(`${Logger.C.primary}Exemplos:${Logger.C.reset}`);
|
|
78
|
+
Logger.log(` xavva encoding detect src/main/java/MinhaClasse.java`);
|
|
79
|
+
Logger.log(` xavva encoding convert --from utf-8 --to cp1252 src/main/java/`);
|
|
80
|
+
Logger.log(` xavva encoding convert --to cp1252 --backup src/main/java/MinhaClasse.java`);
|
|
81
|
+
Logger.log(` xavva encoding fix src/main/java/MinhaClasse.java`);
|
|
82
|
+
Logger.log(` xavva encoding fix --force src/main/java/MinhaClasse.java # Força correção`);
|
|
83
|
+
Logger.log(` xavva encoding list`);
|
|
84
|
+
Logger.endSection();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private async handleDetect(fileArg?: string) {
|
|
88
|
+
const target = fileArg || path.join(process.cwd(), "src");
|
|
89
|
+
|
|
90
|
+
if (!existsSync(target)) {
|
|
91
|
+
Logger.error(`Caminho não encontrado: ${target}`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
Logger.section("Detecção de Encoding");
|
|
96
|
+
Logger.info("Alvo", target);
|
|
97
|
+
Logger.newline();
|
|
98
|
+
|
|
99
|
+
const stat = await Bun.file(target).stat();
|
|
100
|
+
|
|
101
|
+
if (stat.isFile()) {
|
|
102
|
+
// Detectar arquivo único
|
|
103
|
+
const detection = await this.encodingService.detectFileEncoding(target);
|
|
104
|
+
if (detection) {
|
|
105
|
+
Logger.info("Arquivo", path.basename(target));
|
|
106
|
+
Logger.config("Encoding detectado", detection.encoding);
|
|
107
|
+
Logger.config("Confiança", `${Math.round(detection.confidence * 100)}%`);
|
|
108
|
+
Logger.config("Tem BOM", detection.hasBOM ? "Sim" : "Não");
|
|
109
|
+
} else {
|
|
110
|
+
Logger.error("Não foi possível detectar o encoding");
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
// Detectar diretório
|
|
114
|
+
const detections = await this.encodingService.detectDirectoryEncodings(target);
|
|
115
|
+
|
|
116
|
+
if (detections.size === 0) {
|
|
117
|
+
Logger.warn("Nenhum arquivo de texto encontrado");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Agrupa por encoding
|
|
122
|
+
const byEncoding = new Map<string, number>();
|
|
123
|
+
for (const [file, detection] of detections) {
|
|
124
|
+
const count = byEncoding.get(detection.encoding) || 0;
|
|
125
|
+
byEncoding.set(detection.encoding, count + 1);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
Logger.info("Arquivos analisados", String(detections.size));
|
|
129
|
+
Logger.newline();
|
|
130
|
+
|
|
131
|
+
Logger.log(`${Logger.C.primary}Distribuição por encoding:${Logger.C.reset}`);
|
|
132
|
+
for (const [encoding, count] of byEncoding) {
|
|
133
|
+
Logger.config(encoding, `${count} arquivo(s)`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
Logger.newline();
|
|
137
|
+
Logger.log(`${Logger.C.primary}Arquivos com baixa confiança:${Logger.C.reset}`);
|
|
138
|
+
let lowConfidenceFound = false;
|
|
139
|
+
for (const [file, detection] of detections) {
|
|
140
|
+
if (detection.confidence < 0.8) {
|
|
141
|
+
Logger.warn(`${path.relative(target, file)} (${Math.round(detection.confidence * 100)}%)`);
|
|
142
|
+
lowConfidenceFound = true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (!lowConfidenceFound) {
|
|
146
|
+
Logger.success("Todos os arquivos têm confiança alta na detecção");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
Logger.endSection();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private async handleConvert(config: AppConfig, args?: CLIArguments, fileArg?: string) {
|
|
154
|
+
const fromEncoding = args?.["from"] as string || "auto";
|
|
155
|
+
const toEncoding = args?.["to"] as string || config.project.encoding || "utf-8";
|
|
156
|
+
const backup = args?.["backup"] as boolean || false;
|
|
157
|
+
const dryRun = args?.["dry-run"] as boolean || false;
|
|
158
|
+
const srcDir = args?.["src"] as string || path.join(process.cwd(), "src");
|
|
159
|
+
|
|
160
|
+
const target = fileArg || srcDir;
|
|
161
|
+
|
|
162
|
+
if (!existsSync(target)) {
|
|
163
|
+
Logger.error(`Caminho não encontrado: ${target}`);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Valida encoding de destino
|
|
168
|
+
if (!this.encodingService.isValidEncoding(toEncoding)) {
|
|
169
|
+
Logger.error(`Encoding não suportado: "${toEncoding}"`);
|
|
170
|
+
Logger.info("Encodings suportados", this.encodingService.getSupportedEncodings().join(", "));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Valida encoding de origem (se não for auto)
|
|
175
|
+
if (fromEncoding !== "auto" && !this.encodingService.isValidEncoding(fromEncoding)) {
|
|
176
|
+
Logger.error(`Encoding não suportado: "${fromEncoding}"`);
|
|
177
|
+
Logger.info("Encodings suportados", this.encodingService.getSupportedEncodings().join(", "));
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
Logger.section("Conversão de Encoding");
|
|
182
|
+
Logger.info("De", fromEncoding === "auto" ? "auto-detect" : fromEncoding);
|
|
183
|
+
Logger.info("Para", toEncoding);
|
|
184
|
+
Logger.info("Alvo", target);
|
|
185
|
+
if (backup) Logger.config("Backup", "Sim");
|
|
186
|
+
if (dryRun) Logger.config("Modo", "DRY RUN (simulação)");
|
|
187
|
+
Logger.newline();
|
|
188
|
+
|
|
189
|
+
const stat = await Bun.file(target).stat();
|
|
190
|
+
|
|
191
|
+
if (stat.isFile()) {
|
|
192
|
+
// Converter arquivo único
|
|
193
|
+
let actualFrom = fromEncoding;
|
|
194
|
+
|
|
195
|
+
if (fromEncoding === "auto") {
|
|
196
|
+
const detection = await this.encodingService.detectFileEncoding(target);
|
|
197
|
+
if (detection) {
|
|
198
|
+
actualFrom = detection.encoding;
|
|
199
|
+
Logger.info("Detectado", `${actualFrom} (${Math.round(detection.confidence * 100)}%)`);
|
|
200
|
+
} else {
|
|
201
|
+
Logger.error("Não foi possível detectar encoding. Use --from para especificar.");
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const result = await this.encodingService.convertFile(target, actualFrom, toEncoding, {
|
|
207
|
+
backup,
|
|
208
|
+
dryRun
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
if (result.success) {
|
|
212
|
+
Logger.success(result.message);
|
|
213
|
+
if (result.unsupportedChars && result.unsupportedChars.length > 0) {
|
|
214
|
+
const unique = [...new Set(result.unsupportedChars)].slice(0, 5);
|
|
215
|
+
const codes = unique.map(c => `U+${c.charCodeAt(0).toString(16).toUpperCase().padStart(4, '0')}`);
|
|
216
|
+
Logger.warn(`${result.unsupportedChars.length} caractere(s) não suportado(s): ${codes.join(", ")}`);
|
|
217
|
+
Logger.info("Dica", "Caracteres não suportados foram substituídos por '?'");
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
Logger.error(result.message);
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
// Converter diretório
|
|
224
|
+
let actualFrom = fromEncoding;
|
|
225
|
+
|
|
226
|
+
if (fromEncoding === "auto") {
|
|
227
|
+
Logger.warn("Modo auto-detect em diretórios assume UTF-8 ou detecta individualmente");
|
|
228
|
+
actualFrom = "utf-8"; // Default para diretórios em auto
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const result = await this.encodingService.convertDirectory(target, actualFrom, toEncoding, {
|
|
232
|
+
backup,
|
|
233
|
+
dryRun
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
Logger.newline();
|
|
237
|
+
Logger.info("Total", String(result.total));
|
|
238
|
+
Logger.success(`Sucesso: ${result.success}`);
|
|
239
|
+
if (result.failed > 0) {
|
|
240
|
+
Logger.warn(`Falhas: ${result.failed}`);
|
|
241
|
+
}
|
|
242
|
+
if (result.totalUnsupported > 0) {
|
|
243
|
+
Logger.warn(`${result.totalUnsupported} caractere(s) substituído(s) por "?"`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
Logger.endSection();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private async handleFix(fileArg?: string, args?: CLIArguments) {
|
|
251
|
+
const backup = args?.["backup"] as boolean || false;
|
|
252
|
+
const dryRun = args?.["dry-run"] as boolean || false;
|
|
253
|
+
const force = args?.["force"] as boolean || false;
|
|
254
|
+
const target = fileArg || path.join(process.cwd(), "src");
|
|
255
|
+
|
|
256
|
+
if (!existsSync(target)) {
|
|
257
|
+
Logger.error(`Caminho não encontrado: ${target}`);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
Logger.section("Correção de Mojibake");
|
|
262
|
+
Logger.info("Alvo", target);
|
|
263
|
+
if (backup) Logger.config("Backup", "Sim");
|
|
264
|
+
if (dryRun) Logger.config("Modo", "DRY RUN (simulação)");
|
|
265
|
+
Logger.newline();
|
|
266
|
+
|
|
267
|
+
const stat = await Bun.file(target).stat();
|
|
268
|
+
|
|
269
|
+
if (stat.isFile()) {
|
|
270
|
+
const result = await this.encodingService.fixMojibake(target, { backup, dryRun, force });
|
|
271
|
+
|
|
272
|
+
if (result.success) {
|
|
273
|
+
Logger.success(result.message);
|
|
274
|
+
} else {
|
|
275
|
+
Logger.warn(result.message);
|
|
276
|
+
}
|
|
277
|
+
} else {
|
|
278
|
+
// Fix em diretório
|
|
279
|
+
const files = await this.encodingService.findTextFiles(target);
|
|
280
|
+
let fixed = 0;
|
|
281
|
+
let skipped = 0;
|
|
282
|
+
let failed = 0;
|
|
283
|
+
|
|
284
|
+
Logger.info("Arquivos encontrados", String(files.length));
|
|
285
|
+
Logger.newline();
|
|
286
|
+
|
|
287
|
+
for (const file of files) {
|
|
288
|
+
const result = await this.encodingService.fixMojibake(file, { backup, dryRun, force });
|
|
289
|
+
const relativePath = path.relative(target, file);
|
|
290
|
+
|
|
291
|
+
if (result.success && result.detectedFrom !== "utf-8") {
|
|
292
|
+
Logger.success(`${relativePath}: ${result.message}`);
|
|
293
|
+
fixed++;
|
|
294
|
+
} else if (result.detectedFrom === "utf-8") {
|
|
295
|
+
// Já está em UTF-8, não mostra nada em modo silencioso
|
|
296
|
+
skipped++;
|
|
297
|
+
} else {
|
|
298
|
+
Logger.warn(`${relativePath}: ${result.message}`);
|
|
299
|
+
failed++;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
Logger.newline();
|
|
304
|
+
Logger.info("Corrigidos", String(fixed));
|
|
305
|
+
Logger.info("Já em UTF-8", String(skipped));
|
|
306
|
+
if (failed > 0) Logger.warn(`Não corrigidos: ${failed}`);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
Logger.endSection();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private async handleList(config: AppConfig) {
|
|
313
|
+
const srcDir = path.join(process.cwd(), "src");
|
|
314
|
+
|
|
315
|
+
if (!existsSync(srcDir)) {
|
|
316
|
+
Logger.error(`Diretório src/ não encontrado`);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
Logger.section("Lista de Encodings");
|
|
321
|
+
Logger.info("Diretório", srcDir);
|
|
322
|
+
Logger.info("Encoding padrão", config.project.encoding || "utf-8 (padrão)");
|
|
323
|
+
Logger.newline();
|
|
324
|
+
|
|
325
|
+
const detections = await this.encodingService.detectDirectoryEncodings(srcDir);
|
|
326
|
+
|
|
327
|
+
if (detections.size === 0) {
|
|
328
|
+
Logger.warn("Nenhum arquivo de texto encontrado");
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Ordena arquivos
|
|
333
|
+
const sortedFiles = Array.from(detections.entries()).sort((a, b) => a[0].localeCompare(b[0]));
|
|
334
|
+
|
|
335
|
+
Logger.log(`${Logger.C.primary}Arquivos:${Logger.C.reset}`);
|
|
336
|
+
for (const [file, detection] of sortedFiles) {
|
|
337
|
+
const relativePath = path.relative(srcDir, file);
|
|
338
|
+
const confidenceStr = detection.confidence >= 0.9 ? "" :
|
|
339
|
+
` ${Logger.C.gray}(${Math.round(detection.confidence * 100)}%)${Logger.C.reset}`;
|
|
340
|
+
const bomStr = detection.hasBOM ? ` ${Logger.C.warning}[BOM]${Logger.C.reset}` : "";
|
|
341
|
+
|
|
342
|
+
const encodingColor = detection.encoding === (config.project.encoding || "utf-8")
|
|
343
|
+
? Logger.C.success
|
|
344
|
+
: Logger.C.warning;
|
|
345
|
+
|
|
346
|
+
Logger.log(` ${encodingColor}${detection.encoding.padEnd(12)}${Logger.C.reset} ${relativePath}${confidenceStr}${bomStr}`);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
Logger.endSection();
|
|
350
|
+
}
|
|
351
|
+
}
|
|
@@ -30,6 +30,7 @@ export class HelpCommand implements Command {
|
|
|
30
30
|
${this.c("green", "profiles")} List available Maven/Gradle profiles
|
|
31
31
|
${this.c("green", "tomcat")} Manage embedded Tomcat (install, list, installed, use, status)
|
|
32
32
|
${this.c("green", "docs")} Generate endpoint documentation
|
|
33
|
+
${this.c("green", "encoding")} Convert file encoding (detect, convert, fix, list)
|
|
33
34
|
|
|
34
35
|
${this.c("yellow", "GENERAL OPTIONS")}
|
|
35
36
|
${this.c("cyan", "-p, --path")} <path> Tomcat installation path
|
|
@@ -65,6 +66,13 @@ export class HelpCommand implements Command {
|
|
|
65
66
|
${this.c("cyan", "--strict")} Fail on critical conflicts (for CI/CD)
|
|
66
67
|
${this.c("cyan", "-o, --output")} <file> Export report as JSON
|
|
67
68
|
|
|
69
|
+
${this.c("yellow", "ENCODING OPTIONS")} ${this.c("dim", "(for xavva encoding)")}
|
|
70
|
+
${this.c("cyan", "--from")} <encoding> Source encoding (auto-detect if not specified)
|
|
71
|
+
${this.c("cyan", "--to")} <encoding> Target encoding (default: from xavva.json or UTF-8)
|
|
72
|
+
${this.c("cyan", "--backup")} Create backup before conversion
|
|
73
|
+
${this.c("cyan", "--dry-run")} Simulate without modifying files
|
|
74
|
+
${this.c("cyan", "--src")} <path> Source directory (default: src/)
|
|
75
|
+
|
|
68
76
|
${this.c("yellow", "EXAMPLES")}
|
|
69
77
|
${this.c("dim", "# Development with hot reload and dashboard")}
|
|
70
78
|
xavva dev --tui --watch
|
|
@@ -97,6 +105,13 @@ export class HelpCommand implements Command {
|
|
|
97
105
|
xavva tomcat status
|
|
98
106
|
xavva tomcat uninstall 9.0.115
|
|
99
107
|
|
|
108
|
+
${this.c("dim", "# Convert file encoding")}
|
|
109
|
+
xavva encoding detect src/main/java/MinhaClasse.java
|
|
110
|
+
xavva encoding convert --from utf-8 --to cp1252 src/main/java/
|
|
111
|
+
xavva encoding convert --to cp1252 --backup src/main/java/MinhaClasse.java
|
|
112
|
+
xavva encoding fix src/main/java/MinhaClasse.java # Fix mojibake
|
|
113
|
+
xavva encoding list # List all file encodings
|
|
114
|
+
|
|
100
115
|
${this.c("yellow", "CONFIGURATION")}
|
|
101
116
|
Settings are loaded from ${this.c("cyan", "xavva.json")} in the project root:
|
|
102
117
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configurações centralizadas de versões
|
|
3
|
+
* Evita hardcoding espalhado pelo código
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const VERSIONS = {
|
|
7
|
+
// Versões padrão do Tomcat
|
|
8
|
+
TOMCAT: {
|
|
9
|
+
DEFAULT: "10.1.52",
|
|
10
|
+
AVAILABLE: {
|
|
11
|
+
"10.1.52": { sha512: "" },
|
|
12
|
+
"9.0.115": { sha512: "" },
|
|
13
|
+
"11.0.18": { sha512: "" },
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
// HotswapAgent
|
|
18
|
+
HOTSWAP_AGENT: {
|
|
19
|
+
VERSION: "2.0.3",
|
|
20
|
+
URL: "https://github.com/HotswapProjects/HotswapAgent/releases/download/RELEASE-{version}/hotswap-agent-{version}.jar",
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
// Configurações padrão
|
|
24
|
+
DEFAULTS: {
|
|
25
|
+
TOMCAT_PORT: 8080,
|
|
26
|
+
DEBUG_PORT: 5005,
|
|
27
|
+
DEBOUNCE_MS: 300,
|
|
28
|
+
COOLING_MS: 1000,
|
|
29
|
+
},
|
|
30
|
+
} as const;
|
|
31
|
+
|
|
32
|
+
// Type helpers
|
|
33
|
+
export type TomcatVersion = keyof typeof VERSIONS.TOMCAT.AVAILABLE;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Obtém URL de download do HotswapAgent
|
|
37
|
+
*/
|
|
38
|
+
export function getHotswapAgentUrl(version: string = VERSIONS.HOTSWAP_AGENT.VERSION): string {
|
|
39
|
+
return VERSIONS.HOTSWAP_AGENT.URL
|
|
40
|
+
.replace(/{version}/g, version);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Verifica se versão do Tomcat é suportada
|
|
45
|
+
*/
|
|
46
|
+
export function isSupportedTomcatVersion(version: string): version is TomcatVersion {
|
|
47
|
+
return version in VERSIONS.TOMCAT.AVAILABLE;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Obtém versões disponíveis do Tomcat
|
|
52
|
+
*/
|
|
53
|
+
export function getAvailableTomcatVersions(): string[] {
|
|
54
|
+
return Object.keys(VERSIONS.TOMCAT.AVAILABLE);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Obtém SHA512 de uma versão do Tomcat
|
|
59
|
+
*/
|
|
60
|
+
export function getTomcatSha512(version: string): string | undefined {
|
|
61
|
+
const info = VERSIONS.TOMCAT.AVAILABLE[version as TomcatVersion];
|
|
62
|
+
return info?.sha512;
|
|
63
|
+
}
|