@archznn/xavva 2.8.0 → 2.9.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archznn/xavva",
3
- "version": "2.8.0",
3
+ "version": "2.9.0",
4
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",
@@ -1,7 +1,7 @@
1
1
  import { input, select, confirm, number } from "@inquirer/prompts";
2
- import { writeFile, access } from "fs/promises";
2
+ import { writeFile, access, readFile } from "fs/promises";
3
3
  import { join } from "path";
4
- import { constants } from "fs";
4
+ import { constants, existsSync } from "fs";
5
5
  import type { Command } from "./Command";
6
6
  import type { AppConfig, CLIArguments } from "../types/config";
7
7
  import { Logger } from "../utils/ui";
@@ -9,78 +9,106 @@ import { Logger } from "../utils/ui";
9
9
  export class InitCommand implements Command {
10
10
  async execute(_config: AppConfig, _args?: CLIArguments): Promise<void> {
11
11
  Logger.banner("init");
12
- Logger.section("Wizard de Configuração");
13
- Logger.info("Vamos configurar seu projeto Xavva");
12
+ Logger.section("Project Setup Wizard");
13
+ Logger.info("Let's configure your Xavva project");
14
14
  Logger.newline();
15
15
 
16
- // Detectar build tool
16
+ // Detect build tool and available profiles
17
17
  const buildTool = await this.detectBuildTool();
18
+ const availableProfiles = await this.detectProfiles(buildTool);
18
19
 
19
- // Nome da aplicação
20
+ // Application name
20
21
  const appName = await input({
21
- message: "Nome da aplicação:",
22
+ message: "Application name:",
22
23
  default: process.cwd().split(/[/\\]/).pop() || "my-app",
23
- validate: (value) => value.length > 0 || "Nome é obrigatório"
24
+ validate: (value) => value.length > 0 || "Name is required"
24
25
  });
25
26
 
26
- // Profile
27
- const profile = await select({
28
- message: "Profile padrão:",
29
- choices: [
30
- { name: "desenvolvimento", value: "dev" },
31
- { name: "teste", value: "test" },
32
- { name: "produção", value: "prod" },
33
- { name: "customizado", value: "custom" }
34
- ],
35
- default: "dev"
36
- });
27
+ // Profile selection with explanation
28
+ Logger.newline();
29
+ Logger.dim("The profile is used to activate Maven/Gradle build configurations");
30
+ Logger.dim("(e.g., 'dev' for development, 'prod' for production)");
31
+
32
+ let profile: string;
33
+
34
+ if (availableProfiles.length > 0) {
35
+ // Profiles found in build file
36
+ const profileChoices = [
37
+ ...availableProfiles.map(p => ({
38
+ name: `${p.name}${p.description ? ` - ${p.description}` : ''}`,
39
+ value: p.name
40
+ })),
41
+ { name: "Other (custom)", value: "custom" }
42
+ ];
43
+
44
+ profile = await select({
45
+ message: "Select a profile from your build file:",
46
+ choices: profileChoices,
47
+ default: availableProfiles.find(p => p.name === "dev")?.name || availableProfiles[0]?.name
48
+ });
49
+ } else {
50
+ // No profiles detected, show common options
51
+ profile = await select({
52
+ message: "Default profile:",
53
+ choices: [
54
+ { name: "dev - Development environment", value: "dev" },
55
+ { name: "test - Testing environment", value: "test" },
56
+ { name: "prod - Production environment", value: "prod" },
57
+ { name: "Other (custom)", value: "custom" }
58
+ ],
59
+ default: "dev"
60
+ });
61
+ }
37
62
 
38
- const customProfile = profile === "custom" ? await input({
39
- message: "Nome do profile:",
40
- default: "local"
41
- }) : profile;
63
+ if (profile === "custom") {
64
+ profile = await input({
65
+ message: "Profile name:",
66
+ default: "local",
67
+ validate: (value) => value.length > 0 || "Profile name is required"
68
+ });
69
+ }
42
70
 
43
- // Porta do Tomcat
71
+ // Tomcat port
44
72
  const port = await number({
45
- message: "Porta do Tomcat:",
73
+ message: "Tomcat port:",
46
74
  default: 8080,
47
- validate: (value) => (value && value > 0 && value < 65536) || "Porta inválida"
75
+ validate: (value) => (value && value > 0 && value < 65536) || "Invalid port"
48
76
  }) || 8080;
49
77
 
50
- // Configurações opcionais
78
+ // Optional settings
51
79
  Logger.newline();
52
- Logger.dim("Configurações avançadas:");
80
+ Logger.dim("Advanced settings:");
53
81
 
54
82
  const useEmbedded = await confirm({
55
- message: "Usar Tomcat embutido (auto-download)?",
83
+ message: "Use embedded Tomcat (auto-download)?",
56
84
  default: true
57
85
  });
58
86
 
59
87
  const enableCache = await confirm({
60
- message: "Habilitar cache de build?",
88
+ message: "Enable build cache?",
61
89
  default: true
62
90
  });
63
91
 
64
92
  const enableTui = await confirm({
65
- message: "Habilitar dashboard TUI?",
93
+ message: "Enable TUI dashboard?",
66
94
  default: true
67
95
  });
68
96
 
69
97
  const encoding = await select({
70
- message: "Encoding:",
98
+ message: "Source encoding:",
71
99
  choices: [
72
- { name: "UTF-8", value: "UTF-8" },
73
- { name: "ISO-8859-1", value: "ISO-8859-1" },
100
+ { name: "UTF-8 (recommended)", value: "UTF-8" },
101
+ { name: "ISO-8859-1 (Latin-1)", value: "ISO-8859-1" },
74
102
  { name: "Windows-1252", value: "Windows-1252" }
75
103
  ],
76
104
  default: "UTF-8"
77
105
  });
78
106
 
79
- // Montar configuração
107
+ // Build config object
80
108
  const config: Record<string, unknown> = {
81
109
  appName,
82
110
  buildTool,
83
- profile: customProfile,
111
+ profile,
84
112
  port,
85
113
  cache: enableCache,
86
114
  tui: enableTui,
@@ -89,60 +117,127 @@ export class InitCommand implements Command {
89
117
 
90
118
  if (useEmbedded) {
91
119
  config.embedded = true;
92
- config.tomcatVersion = "10.1.52";
120
+ config.tomcatVersion = await select({
121
+ message: "Tomcat version:",
122
+ choices: [
123
+ { name: "10.1.52 (Jakarta EE 10, recommended)", value: "10.1.52" },
124
+ { name: "9.0.115 (Java EE 8)", value: "9.0.115" },
125
+ { name: "11.0.18 (Jakarta EE 11, preview)", value: "11.0.18" }
126
+ ],
127
+ default: "10.1.52"
128
+ });
93
129
  } else {
94
130
  const tomcatPath = await input({
95
- message: "Caminho do Tomcat (CATALINA_HOME):",
131
+ message: "Tomcat path (CATALINA_HOME):",
96
132
  validate: async (value) => {
97
- if (!value) return "Caminho é obrigatório";
133
+ if (!value) return "Path is required";
98
134
  try {
99
135
  await access(value, constants.R_OK);
100
136
  return true;
101
137
  } catch {
102
- return "Caminho não acessível";
138
+ return "Path not accessible";
103
139
  }
104
140
  }
105
141
  });
106
142
  config.tomcatPath = tomcatPath;
107
143
  }
108
144
 
109
- // Salvar arquivo
145
+ // Save file
110
146
  Logger.newline();
111
- Logger.step("Salvando configuração...");
147
+ Logger.step("Saving configuration...");
112
148
 
113
149
  const configPath = join(process.cwd(), "xavva.json");
114
150
  await writeFile(configPath, JSON.stringify(config, null, 2));
115
151
 
116
- Logger.success(`Configuração salva em ${configPath}`);
152
+ Logger.success(`Configuration saved to ${configPath}`);
117
153
  Logger.newline();
118
- Logger.ready("Projeto configurado!");
119
- Logger.info("Próximos passos:");
120
- Logger.log(` ${Logger.C.gray}│${Logger.C.reset} ${Logger.C.primary}xavva build${Logger.C.reset} ${Logger.C.gray}- Compilar projeto${Logger.C.reset}`);
154
+ Logger.ready("Project configured!");
155
+ Logger.info("Next steps:");
156
+ Logger.log(` ${Logger.C.gray}│${Logger.C.reset} ${Logger.C.primary}xavva build${Logger.C.reset} ${Logger.C.gray}- Compile project${Logger.C.reset}`);
121
157
  Logger.log(` ${Logger.C.gray}│${Logger.C.reset} ${Logger.C.primary}xavva deploy${Logger.C.reset} ${Logger.C.gray}- Build + deploy${Logger.C.reset}`);
122
- Logger.log(` ${Logger.C.gray}│${Logger.C.reset} ${Logger.C.primary}xavva doctor${Logger.C.reset} ${Logger.C.gray}- Verificar ambiente${Logger.C.reset}`);
158
+ Logger.log(` ${Logger.C.gray}│${Logger.C.reset} ${Logger.C.primary}xavva health${Logger.C.reset} ${Logger.C.gray}- Check environment${Logger.C.reset}`);
123
159
  Logger.done();
124
160
  }
125
161
 
126
162
  private async detectBuildTool(): Promise<"maven" | "gradle"> {
127
- try {
128
- await access(join(process.cwd(), "pom.xml"), constants.R_OK);
129
- Logger.info("Detectado: Projeto Maven");
163
+ const hasPom = existsSync(join(process.cwd(), "pom.xml"));
164
+ const hasGradle = existsSync(join(process.cwd(), "build.gradle")) ||
165
+ existsSync(join(process.cwd(), "build.gradle.kts"));
166
+
167
+ if (hasPom && !hasGradle) {
168
+ Logger.info("Detected: Maven project");
130
169
  return "maven";
131
- } catch {
132
- try {
133
- await access(join(process.cwd(), "build.gradle"), constants.R_OK);
134
- Logger.info("Detectado: Projeto Gradle");
135
- return "gradle";
136
- } catch {
137
- const choice = await select({
138
- message: "Build tool:",
139
- choices: [
140
- { name: "Maven", value: "maven" },
141
- { name: "Gradle", value: "gradle" }
142
- ]
143
- });
144
- return choice;
170
+ }
171
+
172
+ if (hasGradle && !hasPom) {
173
+ Logger.info("Detected: Gradle project");
174
+ return "gradle";
175
+ }
176
+
177
+ if (hasPom && hasGradle) {
178
+ Logger.warn("Both pom.xml and build.gradle found");
179
+ const choice = await select({
180
+ message: "Select build tool:",
181
+ choices: [
182
+ { name: "Maven (pom.xml)", value: "maven" },
183
+ { name: "Gradle (build.gradle)", value: "gradle" }
184
+ ]
185
+ });
186
+ return choice;
187
+ }
188
+
189
+ // Neither found
190
+ const choice = await select({
191
+ message: "Build tool:",
192
+ choices: [
193
+ { name: "Maven", value: "maven" },
194
+ { name: "Gradle", value: "gradle" }
195
+ ]
196
+ });
197
+ return choice;
198
+ }
199
+
200
+ private async detectProfiles(buildTool: "maven" | "gradle"): Promise<Array<{name: string, description?: string}>> {
201
+ const profiles: Array<{name: string, description?: string}> = [];
202
+
203
+ try {
204
+ if (buildTool === "maven") {
205
+ const pomPath = join(process.cwd(), "pom.xml");
206
+ if (existsSync(pomPath)) {
207
+ const content = await readFile(pomPath, "utf-8");
208
+ // Parse profiles from pom.xml
209
+ const profileMatches = content.matchAll(/<profile>[\s\S]*?<id>([^<]+)<\/id>[\s\S]*?<\/profile>/g);
210
+ for (const match of profileMatches) {
211
+ const profileContent = match[0];
212
+ const id = match[1].trim();
213
+ // Try to extract description or properties
214
+ const descMatch = profileContent.match(/<description>([^<]+)<\/description>/);
215
+ const desc = descMatch ? descMatch[1].trim() : undefined;
216
+ profiles.push({ name: id, description: desc });
217
+ }
218
+ }
219
+ } else {
220
+ const gradlePath = join(process.cwd(), "build.gradle");
221
+ const gradleKtsPath = join(process.cwd(), "build.gradle.kts");
222
+ const gradleFile = existsSync(gradlePath) ? gradlePath : gradleKtsPath;
223
+
224
+ if (existsSync(gradleFile)) {
225
+ const content = await readFile(gradleFile, "utf-8");
226
+ // Look for common profile-like configurations
227
+ // Gradle doesn't have built-in profiles like Maven, but can use:
228
+ // - Properties (-Pprofile=dev)
229
+ // - Custom configurations
230
+ // - apply from: "profiles/${profile}.gradle"
231
+ const profileMatches = content.matchAll(/(?:apply from:|def\s+\w*[Pp]rofile|ext\.\w*[Pp]rofile)\s*=\s*["']([^"']+)["']/g);
232
+ for (const match of profileMatches) {
233
+ profiles.push({ name: match[1] });
234
+ }
235
+ }
145
236
  }
237
+ } catch {
238
+ // Ignore errors, return empty profiles
146
239
  }
240
+
241
+ return profiles;
147
242
  }
148
243
  }