@archznn/xavva 2.4.0 → 2.6.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 +25 -4
- package/package.json +4 -2
- package/src/commands/DeployCommand.ts +31 -18
- package/src/commands/DoctorCommand.ts +129 -76
- package/src/commands/LogsCommand.ts +1 -1
- package/src/commands/RunCommand.ts +21 -11
- package/src/index.ts +1 -1
- package/src/services/BrowserService.ts +12 -9
- package/src/services/BuildService.ts +134 -5
- package/src/services/EmbeddedTomcatService.ts +45 -36
- package/src/services/LogAnalyzer.ts +4 -4
- package/src/services/TomcatService.ts +86 -13
- package/src/services/WatcherService.ts +41 -3
- package/src/types/config.ts +1 -0
- package/src/utils/constants.ts +2 -2
- package/src/utils/platform.ts +323 -0
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# XAVVA CLI 🚀
|
|
2
2
|
|
|
3
|
-
> Ultra-fast development toolkit for Java Enterprise (Tomcat) on Windows
|
|
3
|
+
> Ultra-fast development toolkit for Java Enterprise (Tomcat) on Windows, Linux & macOS
|
|
4
4
|
|
|
5
|
-
[](https://github.com/leorsousa05/Xavva)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
8
|
Xavva is a high-performance CLI built with **Bun** that transforms the Java/Tomcat development experience. It brings modern development workflows (like Node.js/Vite) to the Java Enterprise ecosystem with hot-reload, smart logging, and automated deployment.
|
|
@@ -25,7 +25,7 @@ Xavva is a high-performance CLI built with **Bun** that transforms the Java/Tomc
|
|
|
25
25
|
|
|
26
26
|
## 📦 Installation
|
|
27
27
|
|
|
28
|
-
```
|
|
28
|
+
```bash
|
|
29
29
|
# Via NPM
|
|
30
30
|
npm install -g @archznn/xavva
|
|
31
31
|
|
|
@@ -196,12 +196,14 @@ Create `xavva.json` in your project root:
|
|
|
196
196
|
"tui": false
|
|
197
197
|
},
|
|
198
198
|
"tomcat": {
|
|
199
|
-
"path": "
|
|
199
|
+
"path": "/home/user/apache-tomcat",
|
|
200
200
|
"port": 8080
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
203
|
```
|
|
204
204
|
|
|
205
|
+
> **Note:** On Windows use `"path": "C:/apache-tomcat"` format.
|
|
206
|
+
|
|
205
207
|
### CLI Options
|
|
206
208
|
|
|
207
209
|
| Option | Description |
|
|
@@ -224,6 +226,25 @@ Create `xavva.json` in your project root:
|
|
|
224
226
|
|
|
225
227
|
---
|
|
226
228
|
|
|
229
|
+
## 💻 Platform Support
|
|
230
|
+
|
|
231
|
+
Xavva works on all major platforms:
|
|
232
|
+
|
|
233
|
+
| Platform | Status | Notes |
|
|
234
|
+
|----------|--------|-------|
|
|
235
|
+
| Windows | ✅ Full | PowerShell for system integration |
|
|
236
|
+
| Linux | ✅ Full | Bash/Zsh auto-configuration |
|
|
237
|
+
| macOS | ✅ Full | Native terminal support |
|
|
238
|
+
|
|
239
|
+
### Requirements
|
|
240
|
+
|
|
241
|
+
- **Bun** runtime (latest version)
|
|
242
|
+
- **Java** 11 or higher (JDK)
|
|
243
|
+
- **Maven** 3.6+ or **Gradle** 7+
|
|
244
|
+
- **Git** (optional, for version info)
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
227
248
|
## 🏗️ Architecture
|
|
228
249
|
|
|
229
250
|
Xavva uses a modular service-oriented architecture:
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@archznn/xavva",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Ultra-fast CLI tool for Java/Tomcat development
|
|
3
|
+
"version": "2.6.0",
|
|
4
|
+
"description": "Ultra-fast CLI tool for Java/Tomcat development with Hot-Reload and Zero Config. Supports Windows, Linux and macOS.",
|
|
5
5
|
"module": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
"gradle",
|
|
20
20
|
"hot-reload",
|
|
21
21
|
"windows",
|
|
22
|
+
"linux",
|
|
23
|
+
"macos",
|
|
22
24
|
"cli",
|
|
23
25
|
"bun"
|
|
24
26
|
],
|
|
@@ -7,6 +7,11 @@ import { TomcatService } from "../services/TomcatService";
|
|
|
7
7
|
import { Logger } from "../utils/ui";
|
|
8
8
|
import { EndpointService } from "../services/EndpointService";
|
|
9
9
|
import { BrowserService } from "../services/BrowserService";
|
|
10
|
+
import {
|
|
11
|
+
getJavaPath,
|
|
12
|
+
getWarExtractCommand,
|
|
13
|
+
isWindows,
|
|
14
|
+
} from "../utils/platform";
|
|
10
15
|
|
|
11
16
|
export class DeployCommand implements Command {
|
|
12
17
|
constructor(private tomcat: TomcatService, private builder: BuildService) {}
|
|
@@ -14,6 +19,7 @@ export class DeployCommand implements Command {
|
|
|
14
19
|
async execute(config: AppConfig, args?: CLIArguments): Promise<void> {
|
|
15
20
|
const incremental = args?.watch && args?.incremental;
|
|
16
21
|
const isWatching = !!args?.watch;
|
|
22
|
+
const changedFiles = args?.changedFiles;
|
|
17
23
|
const tomcat = this.tomcat;
|
|
18
24
|
const builder = this.builder;
|
|
19
25
|
|
|
@@ -46,11 +52,11 @@ export class DeployCommand implements Command {
|
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
if (incremental) {
|
|
49
|
-
const actualAppFolder = await builder.syncClasses();
|
|
55
|
+
const actualAppFolder = await builder.syncClasses(changedFiles);
|
|
50
56
|
const actualContextPath = contextPath || actualAppFolder || "";
|
|
51
57
|
const actualAppUrl = `http://localhost:${config.tomcat.port}/${actualContextPath}`;
|
|
52
58
|
await BrowserService.reload(actualAppUrl);
|
|
53
|
-
Logger.success(
|
|
59
|
+
Logger.success(`redeploy completed (${changedFiles?.length || 'all'} file(s))`);
|
|
54
60
|
return;
|
|
55
61
|
}
|
|
56
62
|
|
|
@@ -77,15 +83,28 @@ export class DeployCommand implements Command {
|
|
|
77
83
|
Bun.spawnSync(["jar", "xf", artifactInfo.path], { cwd: appWebappPath });
|
|
78
84
|
Logger.server("extracted WAR");
|
|
79
85
|
} catch (e) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
env:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
// Fallback para extração com jar (funciona em todas as plataformas)
|
|
87
|
+
if (isWindows()) {
|
|
88
|
+
const extractCmd = `Expand-Archive -Path $env:ARTIFACT_PATH -DestinationPath $env:DEST_PATH -Force`;
|
|
89
|
+
Bun.spawnSync(["powershell", "-command", extractCmd], {
|
|
90
|
+
env: {
|
|
91
|
+
...process.env,
|
|
92
|
+
ARTIFACT_PATH: artifactInfo.path,
|
|
93
|
+
DEST_PATH: appWebappPath
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
} else {
|
|
97
|
+
// Linux/Mac: usa unzip ou jar
|
|
98
|
+
try {
|
|
99
|
+
Bun.spawnSync(["unzip", "-q", "-o", artifactInfo.path, "-d", appWebappPath]);
|
|
100
|
+
} catch {
|
|
101
|
+
// Fallback final para jar
|
|
102
|
+
Bun.spawnSync(getWarExtractCommand(artifactInfo.path, appWebappPath), {
|
|
103
|
+
cwd: appWebappPath
|
|
104
|
+
});
|
|
86
105
|
}
|
|
87
|
-
}
|
|
88
|
-
Logger.server("extracted WAR (
|
|
106
|
+
}
|
|
107
|
+
Logger.server("extracted WAR (fallback)");
|
|
89
108
|
}
|
|
90
109
|
} else {
|
|
91
110
|
Logger.server("webapp up to date");
|
|
@@ -98,7 +117,7 @@ export class DeployCommand implements Command {
|
|
|
98
117
|
const finalAppUrl = `http://localhost:${config.tomcat.port}/${finalContextPath}`;
|
|
99
118
|
|
|
100
119
|
tomcat.onReady = async () => {
|
|
101
|
-
await this.handleServerReady(config, finalAppUrl, finalContextPath, tomcat, incremental);
|
|
120
|
+
await this.handleServerReady(config, finalAppUrl, finalContextPath, tomcat, !!incremental);
|
|
102
121
|
};
|
|
103
122
|
|
|
104
123
|
tomcat.start(config, isWatching);
|
|
@@ -116,13 +135,7 @@ export class DeployCommand implements Command {
|
|
|
116
135
|
Logger.config("watch", isWatching);
|
|
117
136
|
Logger.config("debug", config.project.debug ? `port ${config.project.debugPort}` : false);
|
|
118
137
|
|
|
119
|
-
|
|
120
|
-
if (process.env.JAVA_HOME) {
|
|
121
|
-
const homeBin = path.join(process.env.JAVA_HOME, "bin", "java.exe");
|
|
122
|
-
if (fs.existsSync(homeBin)) javaBin = homeBin;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const javaVer = Bun.spawnSync([javaBin, "-version"]);
|
|
138
|
+
const javaVer = Bun.spawnSync([getJavaPath(), "-version"]);
|
|
126
139
|
const output = (javaVer.stderr.toString() + javaVer.stdout.toString()).toLowerCase();
|
|
127
140
|
const hasDcevm = ["dcevm", "jetbrains", "trava", "jbr"].some(v => output.includes(v));
|
|
128
141
|
|
|
@@ -4,6 +4,17 @@ import { Logger } from "../utils/ui";
|
|
|
4
4
|
import fs from "fs";
|
|
5
5
|
import path from "path";
|
|
6
6
|
import os from "os";
|
|
7
|
+
import {
|
|
8
|
+
getCatalinaScript,
|
|
9
|
+
getWhichCommand,
|
|
10
|
+
getJbrDownloadUrl,
|
|
11
|
+
getTarExtractCommand,
|
|
12
|
+
getJavaPath,
|
|
13
|
+
getJavaBinary,
|
|
14
|
+
isWindows,
|
|
15
|
+
isLinux,
|
|
16
|
+
isMacOS,
|
|
17
|
+
} from "../utils/platform";
|
|
7
18
|
|
|
8
19
|
export class DoctorCommand implements Command {
|
|
9
20
|
async execute(config: AppConfig, values: CLIArguments = {}): Promise<void> {
|
|
@@ -25,7 +36,7 @@ export class DoctorCommand implements Command {
|
|
|
25
36
|
|
|
26
37
|
if (!jvmInfo.dcevm) {
|
|
27
38
|
Logger.log(
|
|
28
|
-
` ${Logger.C.
|
|
39
|
+
` ${Logger.C.warning}💡 Dica: Sua JVM não suporta mudanças estruturais (novos métodos/campos).${Logger.C.reset}`,
|
|
29
40
|
);
|
|
30
41
|
if (values.fix) {
|
|
31
42
|
await this.installDCEVM();
|
|
@@ -41,12 +52,12 @@ export class DoctorCommand implements Command {
|
|
|
41
52
|
|
|
42
53
|
if (tomcatOk) {
|
|
43
54
|
const binOk = fs.existsSync(
|
|
44
|
-
path.join(config.tomcat.path, "bin",
|
|
55
|
+
path.join(config.tomcat.path, "bin", getCatalinaScript()),
|
|
45
56
|
);
|
|
46
57
|
this.check(
|
|
47
58
|
"Tomcat Bin",
|
|
48
59
|
binOk,
|
|
49
|
-
binOk ? "OK" :
|
|
60
|
+
binOk ? "OK" : `${getCatalinaScript()} não encontrado`,
|
|
50
61
|
);
|
|
51
62
|
}
|
|
52
63
|
|
|
@@ -211,10 +222,7 @@ export class DoctorCommand implements Command {
|
|
|
211
222
|
|
|
212
223
|
private checkBinary(name: string): boolean {
|
|
213
224
|
try {
|
|
214
|
-
const proc = Bun.spawnSync(
|
|
215
|
-
process.platform === "win32" ? "where" : "which",
|
|
216
|
-
name,
|
|
217
|
-
]);
|
|
225
|
+
const proc = Bun.spawnSync(getWhichCommand(name));
|
|
218
226
|
return proc.exitCode === 0;
|
|
219
227
|
} catch {
|
|
220
228
|
return false;
|
|
@@ -224,11 +232,7 @@ export class DoctorCommand implements Command {
|
|
|
224
232
|
private checkJVM(): { name: string, dcevm: boolean } {
|
|
225
233
|
try {
|
|
226
234
|
// Tentar primeiro o binário do JAVA_HOME para evitar cache do Path
|
|
227
|
-
let javaBin =
|
|
228
|
-
if (process.env.JAVA_HOME) {
|
|
229
|
-
const homeBin = path.join(process.env.JAVA_HOME, "bin", "java.exe");
|
|
230
|
-
if (fs.existsSync(homeBin)) javaBin = homeBin;
|
|
231
|
-
}
|
|
235
|
+
let javaBin = getJavaPath();
|
|
232
236
|
|
|
233
237
|
const proc = Bun.spawnSync([javaBin, "-version"]);
|
|
234
238
|
const output = (proc.stderr.toString() + proc.stdout.toString()).toLowerCase();
|
|
@@ -251,8 +255,8 @@ export class DoctorCommand implements Command {
|
|
|
251
255
|
Logger.section("Instalação do JetBrains Runtime (JBR 21)");
|
|
252
256
|
Logger.log("Baixando JDK moderna com DCEVM nativo (JBR 21 SDK)...");
|
|
253
257
|
|
|
254
|
-
// URL para o JetBrains Runtime 21 SDK
|
|
255
|
-
const url = "
|
|
258
|
+
// URL para o JetBrains Runtime 21 SDK (multiplataforma)
|
|
259
|
+
const url = getJbrDownloadUrl("21");
|
|
256
260
|
const installDir = path.join(os.homedir(), ".xavva", "jdk-dcevm");
|
|
257
261
|
|
|
258
262
|
// Limpar instalação anterior se existir
|
|
@@ -272,21 +276,15 @@ export class DoctorCommand implements Command {
|
|
|
272
276
|
|
|
273
277
|
Logger.success("Download concluído. Extraindo binários...");
|
|
274
278
|
|
|
275
|
-
//
|
|
276
|
-
const extractCmd =
|
|
277
|
-
Bun.spawnSync(
|
|
278
|
-
env: {
|
|
279
|
-
...process.env,
|
|
280
|
-
TAR_PATH: tarPath,
|
|
281
|
-
INSTALL_DIR: installDir
|
|
282
|
-
}
|
|
283
|
-
});
|
|
279
|
+
// Extrair .tar.gz usando comando apropriado para a plataforma
|
|
280
|
+
const extractCmd = getTarExtractCommand(tarPath, installDir);
|
|
281
|
+
Bun.spawnSync(extractCmd);
|
|
284
282
|
|
|
285
283
|
fs.rmSync(tarPath);
|
|
286
284
|
|
|
287
|
-
// Busca recursiva para encontrar onde está o bin/java
|
|
285
|
+
// Busca recursiva para encontrar onde está o bin/java
|
|
288
286
|
const findJdkRoot = (dir: string): string | null => {
|
|
289
|
-
if (fs.existsSync(path.join(dir, "bin",
|
|
287
|
+
if (fs.existsSync(path.join(dir, "bin", getJavaBinary()))) return dir;
|
|
290
288
|
const subdirs = fs.readdirSync(dir, { withFileTypes: true })
|
|
291
289
|
.filter(d => d.isDirectory())
|
|
292
290
|
.map(d => path.join(dir, d.name));
|
|
@@ -299,55 +297,110 @@ export class DoctorCommand implements Command {
|
|
|
299
297
|
|
|
300
298
|
const jdkPath = findJdkRoot(installDir) || installDir;
|
|
301
299
|
const binPath = path.join(jdkPath, "bin");
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
300
|
+
|
|
301
|
+
if (isWindows()) {
|
|
302
|
+
await this.configureWindowsEnv(jdkPath, binPath);
|
|
303
|
+
} else {
|
|
304
|
+
await this.configureUnixEnv(jdkPath, binPath);
|
|
305
|
+
}
|
|
306
|
+
} catch (e: any) {
|
|
307
|
+
Logger.error(`Falha na instalação: ${e.message}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private async configureWindowsEnv(jdkPath: string, binPath: string) {
|
|
312
|
+
Logger.process("Configurando variáveis de ambiente do SISTEMA...");
|
|
313
|
+
|
|
314
|
+
const setEnvCmd = `
|
|
315
|
+
$jdk = $env:JDK_PATH;
|
|
316
|
+
$bin = $env:BIN_PATH;
|
|
317
|
+
try {
|
|
318
|
+
[Environment]::SetEnvironmentVariable('JAVA_HOME', $jdk, 'Machine');
|
|
319
|
+
$pathVar = [Environment]::GetEnvironmentVariable('Path', 'Machine');
|
|
320
|
+
$paths = $pathVar -split ';' | Where-Object { $_ -ne '' };
|
|
321
|
+
$normalizedBin = $bin.TrimEnd('\\\\').ToLower();
|
|
322
|
+
|
|
323
|
+
$exists = $false;
|
|
324
|
+
foreach ($p in $paths) {
|
|
325
|
+
if ($p.TrimEnd('\\\\').ToLower() -eq $normalizedBin) { $exists = $true; break; }
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (-not $exists) {
|
|
329
|
+
$newPath = "$bin;" + $pathVar;
|
|
330
|
+
[Environment]::SetEnvironmentVariable('Path', $newPath, 'Machine');
|
|
331
|
+
}
|
|
332
|
+
Write-Output "OK";
|
|
333
|
+
} catch {
|
|
334
|
+
Write-Error $_.Exception.Message;
|
|
335
|
+
}
|
|
336
|
+
`.replace(/\n/g, ' ');
|
|
337
|
+
|
|
338
|
+
const result = Bun.spawnSync(["powershell", "-command", setEnvCmd], {
|
|
339
|
+
env: {
|
|
340
|
+
...process.env,
|
|
341
|
+
JDK_PATH: jdkPath,
|
|
342
|
+
BIN_PATH: binPath
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
const output = result.stdout.toString() + result.stderr.toString();
|
|
346
|
+
|
|
347
|
+
if (output.includes("ACCESS_DENIED")) {
|
|
348
|
+
Logger.error("Falha ao configurar variáveis do SISTEMA (Acesso Negado).");
|
|
349
|
+
Logger.warn("Dica: Execute o terminal como ADMINISTRADOR para permitir esta alteração.");
|
|
350
|
+
Logger.info("JAVA_HOME manual", jdkPath);
|
|
351
|
+
} else {
|
|
352
|
+
Logger.success(`DCEVM configurado no SISTEMA com sucesso!`);
|
|
353
|
+
Logger.info("JAVA_HOME", jdkPath);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
Logger.newline();
|
|
357
|
+
Logger.warn("IMPORTANTE: Reinicie seu terminal (ou o VS Code) para as mudanças surtirem efeito.");
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
private async configureUnixEnv(jdkPath: string, binPath: string) {
|
|
361
|
+
Logger.process("Configurando variáveis de ambiente...");
|
|
362
|
+
|
|
363
|
+
const shell = process.env.SHELL || "/bin/bash";
|
|
364
|
+
let rcFile = path.join(os.homedir(), ".bashrc");
|
|
365
|
+
|
|
366
|
+
if (shell.includes("zsh")) {
|
|
367
|
+
rcFile = path.join(os.homedir(), ".zshrc");
|
|
368
|
+
} else if (shell.includes("fish")) {
|
|
369
|
+
rcFile = path.join(os.homedir(), ".config/fish/config.fish");
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Verifica se já existe JAVA_HOME configurado
|
|
373
|
+
const exportLine = `export JAVA_HOME="${jdkPath}"`;
|
|
374
|
+
const pathLine = `export PATH="${binPath}:$PATH"`;
|
|
375
|
+
|
|
376
|
+
let rcContent = "";
|
|
377
|
+
if (fs.existsSync(rcFile)) {
|
|
378
|
+
rcContent = fs.readFileSync(rcFile, "utf8");
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Remove linhas antigas do Xavva JBR
|
|
382
|
+
const lines = rcContent.split("\n");
|
|
383
|
+
const filteredLines = lines.filter(line =>
|
|
384
|
+
!line.includes("# Xavva JBR") &&
|
|
385
|
+
!line.includes("JAVA_HOME=") &&
|
|
386
|
+
!line.includes("# Added by Xavva")
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
// Adiciona novas configurações
|
|
390
|
+
filteredLines.push("# Added by Xavva - JetBrains Runtime (DCEVM)");
|
|
391
|
+
filteredLines.push(exportLine);
|
|
392
|
+
filteredLines.push(pathLine);
|
|
393
|
+
|
|
394
|
+
fs.writeFileSync(rcFile, filteredLines.join("\n") + "\n");
|
|
395
|
+
|
|
396
|
+
Logger.success(`DCEVM configurado em ${rcFile}`);
|
|
397
|
+
Logger.info("JAVA_HOME", jdkPath);
|
|
398
|
+
Logger.newline();
|
|
399
|
+
Logger.warn("IMPORTANTE: Execute 'source " + rcFile + "' ou reinicie seu terminal para aplicar permanentemente.");
|
|
400
|
+
Logger.info("Dica", "Para esta sessão, o JAVA_HOME já foi configurado temporariamente.");
|
|
401
|
+
|
|
402
|
+
// Configura JAVA_HOME temporariamente para o processo atual
|
|
403
|
+
process.env.JAVA_HOME = jdkPath;
|
|
404
|
+
process.env.PATH = `${binPath}:${process.env.PATH}`;
|
|
405
|
+
}
|
|
353
406
|
}
|
|
@@ -60,7 +60,7 @@ export class LogsCommand implements Command {
|
|
|
60
60
|
currentSize = newStats.size;
|
|
61
61
|
} else if (newStats.size < currentSize) {
|
|
62
62
|
currentSize = newStats.size;
|
|
63
|
-
dashboard.log(Logger.C.
|
|
63
|
+
dashboard.log(Logger.C.warning + "Arquivo de log foi resetado/rotacionado.");
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
});
|
|
@@ -5,6 +5,14 @@ import path from "path";
|
|
|
5
5
|
import fs from "fs";
|
|
6
6
|
import { glob } from "glob";
|
|
7
7
|
import readline from "readline";
|
|
8
|
+
import {
|
|
9
|
+
getJavaPath,
|
|
10
|
+
getMavenCommand,
|
|
11
|
+
getGradleCommand,
|
|
12
|
+
getClasspathSeparator,
|
|
13
|
+
normalizeClasspathPath,
|
|
14
|
+
isWindows,
|
|
15
|
+
} from "../utils/platform";
|
|
8
16
|
|
|
9
17
|
export class RunCommand implements Command {
|
|
10
18
|
async execute(config: AppConfig, args?: CLIArguments): Promise<void> {
|
|
@@ -36,7 +44,8 @@ export class RunCommand implements Command {
|
|
|
36
44
|
const { localCp, dependencyCp } = await this.getClasspath(config);
|
|
37
45
|
const pathingJar = await this.createPathingJar(dependencyCp);
|
|
38
46
|
|
|
39
|
-
const
|
|
47
|
+
const sep = getClasspathSeparator();
|
|
48
|
+
const finalCp = `${localCp}${sep}${pathingJar}`;
|
|
40
49
|
|
|
41
50
|
const javaArgs = [
|
|
42
51
|
"-classpath", finalCp,
|
|
@@ -60,7 +69,7 @@ export class RunCommand implements Command {
|
|
|
60
69
|
Logger.warn(`🚀 Executando ${className}...`);
|
|
61
70
|
}
|
|
62
71
|
|
|
63
|
-
const bin =
|
|
72
|
+
const bin = getJavaPath();
|
|
64
73
|
|
|
65
74
|
const proc = Bun.spawn([bin, ...javaArgs], {
|
|
66
75
|
stdout: "inherit",
|
|
@@ -220,9 +229,10 @@ export class RunCommand implements Command {
|
|
|
220
229
|
const xavvaDir = path.join(process.cwd(), ".xavva");
|
|
221
230
|
const jarPath = path.join(xavvaDir, "classpath.jar");
|
|
222
231
|
|
|
223
|
-
const
|
|
232
|
+
const sep = getClasspathSeparator();
|
|
233
|
+
const paths = dependencyCp.split(sep).filter(p => p.trim());
|
|
224
234
|
const relativePaths = paths.map(p => {
|
|
225
|
-
let rel = path.relative(xavvaDir, p)
|
|
235
|
+
let rel = normalizeClasspathPath(path.relative(xavvaDir, p));
|
|
226
236
|
if (fs.existsSync(p) && fs.statSync(p).isDirectory() && !rel.endsWith("/")) rel += "/";
|
|
227
237
|
// Robust URL encoding for Class-Path as per Java Spec
|
|
228
238
|
return encodeURI(rel)
|
|
@@ -296,10 +306,9 @@ export class RunCommand implements Command {
|
|
|
296
306
|
const stopSpinner = Logger.spinner("Generating project classpath");
|
|
297
307
|
try {
|
|
298
308
|
if (config.project.buildTool === "maven") {
|
|
299
|
-
|
|
300
|
-
Bun.spawnSync([mvnCmd, "dependency:build-classpath", `-Dmdep.outputFile=${cpFile}`]);
|
|
309
|
+
Bun.spawnSync([getMavenCommand(), "dependency:build-classpath", `-Dmdep.outputFile=${cpFile}`]);
|
|
301
310
|
} else if (config.project.buildTool === "gradle") {
|
|
302
|
-
const gradleCmd =
|
|
311
|
+
const gradleCmd = getGradleCommand();
|
|
303
312
|
const initScriptPath = path.join(xavvaDir, "init-cp.gradle");
|
|
304
313
|
const normalizedCpFile = cpFile.replace(/\\/g, "/");
|
|
305
314
|
const initScriptContent = `
|
|
@@ -335,9 +344,10 @@ export class RunCommand implements Command {
|
|
|
335
344
|
|
|
336
345
|
let dependencyCp = fs.existsSync(cpFile) ? fs.readFileSync(cpFile, "utf8").trim() : "";
|
|
337
346
|
|
|
338
|
-
// Normalize platform specific separators
|
|
339
|
-
|
|
340
|
-
|
|
347
|
+
// Normalize platform specific separators para o separador consistente
|
|
348
|
+
const sep = getClasspathSeparator();
|
|
349
|
+
if (path.delimiter !== sep) {
|
|
350
|
+
dependencyCp = dependencyCp.split(path.delimiter).join(sep);
|
|
341
351
|
}
|
|
342
352
|
|
|
343
353
|
const localFolders = [
|
|
@@ -356,7 +366,7 @@ export class RunCommand implements Command {
|
|
|
356
366
|
const localCp = localFolders
|
|
357
367
|
.map(p => path.join(process.cwd(), p))
|
|
358
368
|
.filter(p => fs.existsSync(p))
|
|
359
|
-
.join(
|
|
369
|
+
.join(getClasspathSeparator());
|
|
360
370
|
|
|
361
371
|
return { localCp, dependencyCp };
|
|
362
372
|
}
|
package/src/index.ts
CHANGED
|
@@ -93,7 +93,7 @@ async function main() {
|
|
|
93
93
|
// Registrar ação de restart manual na TUI
|
|
94
94
|
if (dashboard.isTuiActive()) {
|
|
95
95
|
dashboard.onAction("r", () => {
|
|
96
|
-
dashboard.log(Logger.C.
|
|
96
|
+
dashboard.log(Logger.C.warning + "Restart manual solicitado via TUI...");
|
|
97
97
|
deployCmd.execute(config, false, true); // Executa deploy completo mas mantém o watch
|
|
98
98
|
});
|
|
99
99
|
}
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import { Logger } from "../utils/ui";
|
|
2
|
+
import { isWindows, getOpenBrowserArgs } from "../utils/platform";
|
|
2
3
|
|
|
3
4
|
export class BrowserService {
|
|
4
5
|
/**
|
|
5
|
-
* Recarrega a aba ativa do browser (Chrome ou Edge)
|
|
6
|
+
* Recarrega a aba ativa do browser (Chrome ou Edge).
|
|
7
|
+
* Nota: No Linux/Mac, esta funcionalidade é limitada devido às restrições
|
|
8
|
+
* de automação de GUI. Recomenda-se usar extensões de Live Reload.
|
|
6
9
|
*/
|
|
7
10
|
public static async reload(url: string) {
|
|
8
|
-
if (process.platform !== 'win32') return;
|
|
9
|
-
|
|
10
11
|
// Pequeno delay para garantir que o Tomcat processou o novo contexto
|
|
11
12
|
await new Promise(r => setTimeout(r, 800));
|
|
12
13
|
|
|
14
|
+
if (!isWindows()) {
|
|
15
|
+
// No Linux/Mac, tenta notificar via Browser Sync ou similar se disponível
|
|
16
|
+
// Por enquanto, apenas loga (o usuário pode usar extensões de Live Reload)
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
13
20
|
const psCommand = `
|
|
14
21
|
$shell = New-Object -ComObject WScript.Shell
|
|
15
22
|
$process = Get-Process | Where-Object { $_.MainWindowTitle -match "Chrome" -or $_.MainWindowTitle -match "Edge" } | Select-Object -First 1
|
|
@@ -31,11 +38,7 @@ export class BrowserService {
|
|
|
31
38
|
* Abre a URL no browser padrão do sistema.
|
|
32
39
|
*/
|
|
33
40
|
public static open(url: string) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
} else {
|
|
37
|
-
const start = process.platform === 'darwin' ? 'open' : 'xdg-open';
|
|
38
|
-
Bun.spawn([start, url]);
|
|
39
|
-
}
|
|
41
|
+
const args = getOpenBrowserArgs(url);
|
|
42
|
+
Bun.spawn(args);
|
|
40
43
|
}
|
|
41
44
|
}
|