@archznn/xavva 1.8.0 → 1.8.1

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 CHANGED
@@ -1,4 +1,4 @@
1
- # XAVVA 🚀 (Windows Only) `v1.8.0`
1
+ # XAVVA 🚀 (Windows Only) `v1.8.1`
2
2
 
3
3
  Xavva é uma CLI de alto desempenho construída com **Bun** para automatizar o ciclo de desenvolvimento de aplicações Java (Maven/Gradle) rodando no Apache Tomcat. Ela foi desenhada especificamente para desenvolvedores que buscam a velocidade de ambientes modernos (como Node.js/Vite) dentro do ecossistema Java Enterprise.
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archznn/xavva",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "description": "Ultra-fast CLI tool for Java/Tomcat development on Windows with Hot-Reload and Zero Config.",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
@@ -27,16 +27,13 @@ export class DeployCommand implements Command {
27
27
  const contextPath = (config.project.appName || "").replace(".war", "");
28
28
 
29
29
  if (!incremental) {
30
- tomcat.clearWebapps(contextPath);
30
+ await tomcat.killConflict();
31
+ await tomcat.clearWebapps();
31
32
 
32
- const buildTask = config.project.skipBuild ? Promise.resolve() : builder.runBuild(incremental);
33
- const killTask = tomcat.killConflict();
34
-
35
33
  if (!config.project.skipBuild) {
36
- Logger.watcher("Preparing environment and building in parallel", "start");
34
+ Logger.watcher("Building project", "start");
35
+ await builder.runBuild(incremental);
37
36
  }
38
-
39
- await Promise.all([buildTask, killTask]);
40
37
 
41
38
  if (!config.project.skipBuild) {
42
39
  Logger.build("Full project build and environment ready");
@@ -1,7 +1,6 @@
1
1
  import type { Command } from "./Command";
2
2
  import type { AppConfig, CLIArguments } from "../types/config";
3
3
  import { Logger } from "../utils/ui";
4
- import { spawn } from "child_process";
5
4
  import path from "path";
6
5
 
7
6
  export class RunCommand implements Command {
@@ -54,19 +53,20 @@ export class RunCommand implements Command {
54
53
  Logger.warn(`🚀 Executando ${className}...`);
55
54
  }
56
55
 
57
- const bin = process.env.JAVA_HOME ? path.join(process.env.JAVA_HOME, "bin", "java") : "java";
56
+ const bin = process.env.JAVA_HOME ? path.join(process.env.JAVA_HOME, "bin", "java.exe") : "java";
58
57
 
59
- return new Promise((resolve) => {
60
- const child = spawn(bin, javaArgs, {
61
- stdio: "inherit",
62
- shell: false
63
- });
64
-
65
- child.on("exit", () => {
66
- Logger.log(`Sessão de ${isDebug ? "debug" : "execução"} encerrada.`);
67
- resolve();
68
- });
58
+ const proc = Bun.spawn([bin, ...javaArgs], {
59
+ stdout: "inherit",
60
+ stderr: "inherit",
61
+ stdin: "inherit",
62
+ env: {
63
+ ...process.env,
64
+ JAVA_OPTS: "-Xms256m -Xmx1024m"
65
+ } as any
69
66
  });
67
+
68
+ await proc.exited;
69
+ Logger.log(`Sessão de ${isDebug ? "debug" : "execução"} encerrada.`);
70
70
  }
71
71
 
72
72
  private async discoverClass(simpleName: string): Promise<string | null> {
@@ -41,7 +41,7 @@ export class BuildCacheService {
41
41
  return crypto.createHash("md5").update(hash).digest("hex");
42
42
  }
43
43
 
44
- shouldRebuild(tool: "maven" | "gradle"): boolean {
44
+ shouldRebuild(tool: "maven" | "gradle", projectService?: any): boolean {
45
45
  if (!fs.existsSync(this.cacheFile)) return true;
46
46
 
47
47
  try {
@@ -51,8 +51,16 @@ export class BuildCacheService {
51
51
  // Se o pom/gradle mudou, precisa de rebuild completo
52
52
  if (currentHash !== cache.lastConfigHash) return true;
53
53
 
54
- // Verificar se o artefato (.war) ainda existe
55
- // (Esta parte será integrada ao BuildService)
54
+ // Verificar se o artefato (.war) ainda existe fisicamente
55
+ if (projectService) {
56
+ try {
57
+ const artifact = projectService.getArtifact();
58
+ if (!fs.existsSync(artifact.path)) return true;
59
+ } catch (e) {
60
+ return true;
61
+ }
62
+ }
63
+
56
64
  return false;
57
65
  } catch (e) {
58
66
  return true;
@@ -19,16 +19,22 @@ export class BuildService {
19
19
  }
20
20
 
21
21
  if (!incremental && !this.projectConfig.skipBuild) {
22
- if (!this.projectConfig.clean && !this.cache.shouldRebuild(this.projectConfig.buildTool)) {
22
+ if (!this.projectConfig.clean && !this.cache.shouldRebuild(this.projectConfig.buildTool, this.projectService)) {
23
23
  Logger.success("Build cache hit! Skipping full build.");
24
24
  return;
25
25
  }
26
26
  }
27
27
 
28
28
  const command = [];
29
+ const env = { ...process.env };
29
30
 
30
31
  if (this.projectConfig.buildTool === 'maven') {
31
32
  command.push("mvn");
33
+
34
+ if (!this.cache.shouldRebuild('maven', this.projectService)) {
35
+ command.push("-o");
36
+ }
37
+
32
38
  if (incremental) {
33
39
  command.push("compile");
34
40
  } else {
@@ -38,6 +44,8 @@ export class BuildService {
38
44
  }
39
45
  command.push("-Dmaven.test.skip=true", "-Dmaven.javadoc.skip=true");
40
46
  if (this.projectConfig.profile) command.push(`-P${this.projectConfig.profile}`);
47
+
48
+ env.MAVEN_OPTS = "-Xms512m -Xmx1024m -XX:+UseParallelGC";
41
49
  } else {
42
50
  command.push("gradle");
43
51
  if (incremental) {
@@ -49,13 +57,16 @@ export class BuildService {
49
57
  }
50
58
  command.push("-x", "test", "-x", "javadoc");
51
59
  if (this.projectConfig.profile) command.push(`-Pprofile=${this.projectConfig.profile}`);
60
+
61
+ env.GRADLE_OPTS = "-Xmx1024m -Dorg.gradle.daemon=true";
52
62
  }
53
63
 
54
64
  const stopSpinner = (this.projectConfig.verbose) ? () => {} : Logger.spinner(incremental ? "Incremental compilation" : "Full project build");
55
65
 
56
66
  const proc = Bun.spawn(command, {
57
67
  stdout: "pipe",
58
- stderr: "pipe"
68
+ stderr: "pipe",
69
+ env: env as any
59
70
  });
60
71
 
61
72
  if (this.projectConfig.verbose) {
@@ -42,28 +42,45 @@ export class TomcatService {
42
42
  }
43
43
  }
44
44
 
45
- clearWebapps() {
46
- const fs = require("fs");
45
+ async clearWebapps() {
46
+ const fs = require("fs").promises;
47
+ const { existsSync } = require("fs");
47
48
  const path = require("path");
48
49
  const webappsPath = path.join(this.activeConfig.path, "webapps");
49
50
  const workPath = path.join(this.activeConfig.path, "work");
50
51
  const tempPath = path.join(this.activeConfig.path, "temp");
51
52
 
52
53
  try {
53
- [workPath, tempPath].forEach(p => {
54
- if (fs.existsSync(p)) {
55
- fs.rmSync(p, { recursive: true, force: true });
56
- fs.mkdirSync(p);
54
+ const cleanDir = async (p: string) => {
55
+ if (existsSync(p)) {
56
+ await fs.rm(p, { recursive: true, force: true });
57
+
58
+ // Resiliência para Windows: garante que o diretório foi liberado antes do mkdir
59
+ let retries = 10;
60
+ while (retries > 0 && existsSync(p)) {
61
+ await new Promise(r => setTimeout(r, 50));
62
+ retries--;
63
+ }
64
+
65
+ await fs.mkdir(p, { recursive: true });
66
+ }
67
+ };
68
+
69
+ const tasks: Promise<any>[] = [
70
+ cleanDir(workPath),
71
+ cleanDir(tempPath)
72
+ ];
73
+
74
+ if (existsSync(webappsPath)) {
75
+ const files = await fs.readdir(webappsPath);
76
+ for (const file of files) {
77
+ if (file === "ROOT" || file === "manager" || file === "host-manager") continue;
78
+ const fullPath = path.join(webappsPath, file);
79
+ tasks.push(fs.rm(fullPath, { recursive: true, force: true }));
57
80
  }
58
- });
59
-
60
- const files = fs.readdirSync(webappsPath);
61
- for (const file of files) {
62
- const fullPath = path.join(webappsPath, file);
63
- if (file === "ROOT" || file === "manager" || file === "host-manager") continue;
64
-
65
- fs.rmSync(fullPath, { recursive: true, force: true });
66
81
  }
82
+
83
+ await Promise.all(tasks);
67
84
  } catch (e) {
68
85
  Logger.warn("Não foi possível limpar totalmente a pasta webapps ou cache.");
69
86
  }