@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 +1 -1
- package/src/commands/InitCommand.ts +159 -64
package/package.json
CHANGED
|
@@ -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("
|
|
13
|
-
Logger.info("
|
|
12
|
+
Logger.section("Project Setup Wizard");
|
|
13
|
+
Logger.info("Let's configure your Xavva project");
|
|
14
14
|
Logger.newline();
|
|
15
15
|
|
|
16
|
-
//
|
|
16
|
+
// Detect build tool and available profiles
|
|
17
17
|
const buildTool = await this.detectBuildTool();
|
|
18
|
+
const availableProfiles = await this.detectProfiles(buildTool);
|
|
18
19
|
|
|
19
|
-
//
|
|
20
|
+
// Application name
|
|
20
21
|
const appName = await input({
|
|
21
|
-
message: "
|
|
22
|
+
message: "Application name:",
|
|
22
23
|
default: process.cwd().split(/[/\\]/).pop() || "my-app",
|
|
23
|
-
validate: (value) => value.length > 0 || "
|
|
24
|
+
validate: (value) => value.length > 0 || "Name is required"
|
|
24
25
|
});
|
|
25
26
|
|
|
26
|
-
// Profile
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
//
|
|
71
|
+
// Tomcat port
|
|
44
72
|
const port = await number({
|
|
45
|
-
message: "
|
|
73
|
+
message: "Tomcat port:",
|
|
46
74
|
default: 8080,
|
|
47
|
-
validate: (value) => (value && value > 0 && value < 65536) || "
|
|
75
|
+
validate: (value) => (value && value > 0 && value < 65536) || "Invalid port"
|
|
48
76
|
}) || 8080;
|
|
49
77
|
|
|
50
|
-
//
|
|
78
|
+
// Optional settings
|
|
51
79
|
Logger.newline();
|
|
52
|
-
Logger.dim("
|
|
80
|
+
Logger.dim("Advanced settings:");
|
|
53
81
|
|
|
54
82
|
const useEmbedded = await confirm({
|
|
55
|
-
message: "
|
|
83
|
+
message: "Use embedded Tomcat (auto-download)?",
|
|
56
84
|
default: true
|
|
57
85
|
});
|
|
58
86
|
|
|
59
87
|
const enableCache = await confirm({
|
|
60
|
-
message: "
|
|
88
|
+
message: "Enable build cache?",
|
|
61
89
|
default: true
|
|
62
90
|
});
|
|
63
91
|
|
|
64
92
|
const enableTui = await confirm({
|
|
65
|
-
message: "
|
|
93
|
+
message: "Enable TUI dashboard?",
|
|
66
94
|
default: true
|
|
67
95
|
});
|
|
68
96
|
|
|
69
97
|
const encoding = await select({
|
|
70
|
-
message: "
|
|
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
|
-
//
|
|
107
|
+
// Build config object
|
|
80
108
|
const config: Record<string, unknown> = {
|
|
81
109
|
appName,
|
|
82
110
|
buildTool,
|
|
83
|
-
profile
|
|
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 =
|
|
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: "
|
|
131
|
+
message: "Tomcat path (CATALINA_HOME):",
|
|
96
132
|
validate: async (value) => {
|
|
97
|
-
if (!value) return "
|
|
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 "
|
|
138
|
+
return "Path not accessible";
|
|
103
139
|
}
|
|
104
140
|
}
|
|
105
141
|
});
|
|
106
142
|
config.tomcatPath = tomcatPath;
|
|
107
143
|
}
|
|
108
144
|
|
|
109
|
-
//
|
|
145
|
+
// Save file
|
|
110
146
|
Logger.newline();
|
|
111
|
-
Logger.step("
|
|
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(`
|
|
152
|
+
Logger.success(`Configuration saved to ${configPath}`);
|
|
117
153
|
Logger.newline();
|
|
118
|
-
Logger.ready("
|
|
119
|
-
Logger.info("
|
|
120
|
-
Logger.log(` ${Logger.C.gray}│${Logger.C.reset} ${Logger.C.primary}xavva build${Logger.C.reset} ${Logger.C.gray}-
|
|
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
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
}
|