eva4j 1.0.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/LICENSE +21 -0
- package/QUICK_REFERENCE.md +204 -0
- package/README.md +912 -0
- package/USAGE.md +349 -0
- package/bin/eva4j.js +234 -0
- package/config/defaults.json +46 -0
- package/package.json +57 -0
- package/src/commands/add-kafka-client.js +193 -0
- package/src/commands/add-module.js +221 -0
- package/src/commands/create.js +92 -0
- package/src/commands/detach.js +495 -0
- package/src/commands/generate-http-exchange.js +309 -0
- package/src/commands/generate-kafka-event.js +453 -0
- package/src/commands/generate-kafka-listener.js +267 -0
- package/src/commands/generate-resource.js +265 -0
- package/src/commands/generate-usecase.js +198 -0
- package/src/commands/info.js +63 -0
- package/src/generators/base-generator.js +150 -0
- package/src/generators/module-generator.js +48 -0
- package/src/generators/shared-generator.js +153 -0
- package/src/utils/config-manager.js +156 -0
- package/src/utils/context-builder.js +149 -0
- package/src/utils/naming.js +137 -0
- package/src/utils/template-engine.js +55 -0
- package/src/utils/validator.js +159 -0
- package/templates/base/application/Application.java.ejs +27 -0
- package/templates/base/docker/docker-compose.yml.ejs +41 -0
- package/templates/base/gradle/build.gradle.ejs +70 -0
- package/templates/base/gradle/settings.gradle.ejs +1 -0
- package/templates/base/resources/application-develop.yml.ejs +5 -0
- package/templates/base/resources/application-local.yml.ejs +5 -0
- package/templates/base/resources/application-production.yml.ejs +9 -0
- package/templates/base/resources/application-test.yml.ejs +5 -0
- package/templates/base/resources/application.yml.ejs +31 -0
- package/templates/base/resources/parameters/develop/cors.yml.ejs +4 -0
- package/templates/base/resources/parameters/develop/db.yaml.ejs +21 -0
- package/templates/base/resources/parameters/develop/kafka.yml.ejs +26 -0
- package/templates/base/resources/parameters/local/cors.yml.ejs +4 -0
- package/templates/base/resources/parameters/local/db.yaml.ejs +21 -0
- package/templates/base/resources/parameters/local/kafka.yml.ejs +26 -0
- package/templates/base/resources/parameters/production/cors.yml.ejs +4 -0
- package/templates/base/resources/parameters/production/db.yaml.ejs +21 -0
- package/templates/base/resources/parameters/production/kafka.yml.ejs +26 -0
- package/templates/base/root/README.md.ejs +126 -0
- package/templates/base/root/gitignore.ejs +42 -0
- package/templates/http-exchange/Adapter.java.ejs +39 -0
- package/templates/http-exchange/Config.java.ejs +24 -0
- package/templates/http-exchange/FeignClient.java.ejs +23 -0
- package/templates/http-exchange/Port.java.ejs +14 -0
- package/templates/kafka-event/Event.java.ejs +10 -0
- package/templates/kafka-event/KafkaConfigBean.java.ejs +7 -0
- package/templates/kafka-event/KafkaMessageBroker.java.ejs +32 -0
- package/templates/kafka-event/MessageBroker.java.ejs +8 -0
- package/templates/kafka-event/MessageBrokerImplMethod.java.ejs +9 -0
- package/templates/kafka-event/MessageBrokerMethod.java.ejs +1 -0
- package/templates/kafka-listener/KafkaController.java.ejs +34 -0
- package/templates/kafka-listener/ListenerMethod.java.ejs +9 -0
- package/templates/kafka-listener/ValueField.java.ejs +4 -0
- package/templates/module/controller.java.ejs +96 -0
- package/templates/module/exception.java.ejs +18 -0
- package/templates/module/mapper.java.ejs +67 -0
- package/templates/module/model.java.ejs +29 -0
- package/templates/module/package-info.java.ejs +7 -0
- package/templates/module/repository.java.ejs +26 -0
- package/templates/module/request-dto.java.ejs +24 -0
- package/templates/module/response-dto.java.ejs +26 -0
- package/templates/module/service-impl.java.ejs +112 -0
- package/templates/module/service.java.ejs +45 -0
- package/templates/module/update-dto.java.ejs +25 -0
- package/templates/resource/Command.java.ejs +9 -0
- package/templates/resource/CommandHandler.java.ejs +22 -0
- package/templates/resource/Controller.java.ejs +73 -0
- package/templates/resource/Query.java.ejs +12 -0
- package/templates/resource/QueryHandler.java.ejs +31 -0
- package/templates/resource/ResponseDto.java.ejs +6 -0
- package/templates/shared/annotations/ApplicationComponent.java.ejs +9 -0
- package/templates/shared/annotations/DomainComponent.java.ejs +9 -0
- package/templates/shared/annotations/LogAfter.java.ejs +11 -0
- package/templates/shared/annotations/LogBefore.java.ejs +11 -0
- package/templates/shared/annotations/LogExceptions.java.ejs +11 -0
- package/templates/shared/annotations/LogTimer.java.ejs +11 -0
- package/templates/shared/configurations/kafkaConfig/KafkaConfig.java.ejs +49 -0
- package/templates/shared/configurations/loggerConfig/HandlerLogs.java.ejs +56 -0
- package/templates/shared/configurations/securityConfig/SecurityConfig.java.ejs +57 -0
- package/templates/shared/configurations/swaggerConfig/SwaggerConfig.java.ejs +31 -0
- package/templates/shared/configurations/useCaseConfig/UseCaseAutoRegister.java.ejs +51 -0
- package/templates/shared/configurations/useCaseConfig/UseCaseConfig.java.ejs +25 -0
- package/templates/shared/configurations/useCaseConfig/UseCaseContainer.java.ejs +29 -0
- package/templates/shared/configurations/useCaseConfig/UseCaseMediator.java.ejs +38 -0
- package/templates/shared/customExceptions/BadRequestException.java.ejs +11 -0
- package/templates/shared/customExceptions/ConflictException.java.ejs +8 -0
- package/templates/shared/customExceptions/ForbiddenException.java.ejs +8 -0
- package/templates/shared/customExceptions/ImportFileException.java.ejs +6 -0
- package/templates/shared/customExceptions/NotFoundException.java.ejs +8 -0
- package/templates/shared/customExceptions/UnauthorizedException.java.ejs +9 -0
- package/templates/shared/customExceptions/ValidationException.java.ejs +17 -0
- package/templates/shared/errorMessage/ErrorMessage.java.ejs +5 -0
- package/templates/shared/errorMessage/FullErrorMessage.java.ejs +9 -0
- package/templates/shared/errorMessage/ShortErrorMessage.java.ejs +6 -0
- package/templates/shared/eventEnvelope/EventEnvelope.java.ejs +13 -0
- package/templates/shared/eventEnvelope/EventMetadata.java.ejs +24 -0
- package/templates/shared/filters/CorrelationIdFilter.java.ejs +45 -0
- package/templates/shared/handlerException/HandlerExceptions.java.ejs +148 -0
- package/templates/shared/interfaces/Command.java.ejs +4 -0
- package/templates/shared/interfaces/CommandHandler.java.ejs +5 -0
- package/templates/shared/interfaces/Dispatchable.java.ejs +4 -0
- package/templates/shared/interfaces/Handler.java.ejs +4 -0
- package/templates/shared/interfaces/Query.java.ejs +4 -0
- package/templates/shared/interfaces/QueryHandler.java.ejs +5 -0
- package/templates/shared/package-info.java.ejs +8 -0
- package/templates/usecase/command/Command.java.ejs +7 -0
- package/templates/usecase/command/CommandHandler.java.ejs +21 -0
- package/templates/usecase/query/Query.java.ejs +10 -0
- package/templates/usecase/query/QueryHandler.java.ejs +22 -0
- package/templates/usecase/query/ResponseDto.java.ejs +5 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
const ora = require('ora');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const ConfigManager = require('../utils/config-manager');
|
|
6
|
+
const { isEva4jProject } = require('../utils/validator');
|
|
7
|
+
const { toPackagePath } = require('../utils/naming');
|
|
8
|
+
const { renderAndWrite } = require('../utils/template-engine');
|
|
9
|
+
|
|
10
|
+
async function addKafkaClientCommand() {
|
|
11
|
+
const projectDir = process.cwd();
|
|
12
|
+
|
|
13
|
+
// Validate we're in an eva4j project
|
|
14
|
+
if (!(await isEva4jProject(projectDir))) {
|
|
15
|
+
console.error(chalk.red('❌ Not in an eva4j project directory'));
|
|
16
|
+
console.error(chalk.gray('Run this command inside a project created with eva4j'));
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Check if kafka is already installed
|
|
21
|
+
const configManager = new ConfigManager(projectDir);
|
|
22
|
+
if (await configManager.featureExists('kafka')) {
|
|
23
|
+
console.error(chalk.red('❌ Kafka client is already installed in this project'));
|
|
24
|
+
console.log(chalk.gray('\nKafka dependencies and configuration already exist.'));
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Load project configuration
|
|
29
|
+
const projectConfig = await configManager.loadProjectConfig();
|
|
30
|
+
if (!projectConfig) {
|
|
31
|
+
console.error(chalk.red('❌ Could not load project configuration'));
|
|
32
|
+
console.error(chalk.gray('Make sure .eva4j.json exists in the project root'));
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const { packageName, projectName, groupId } = projectConfig;
|
|
37
|
+
const packagePath = toPackagePath(packageName);
|
|
38
|
+
|
|
39
|
+
// Check if shared module exists
|
|
40
|
+
const sharedPath = path.join(projectDir, 'src', 'main', 'java', packagePath, 'shared');
|
|
41
|
+
if (!(await fs.pathExists(sharedPath))) {
|
|
42
|
+
console.error(chalk.red('❌ Shared module not found'));
|
|
43
|
+
console.error(chalk.gray('Create at least one module first using: eva4j add module <name>'));
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const spinner = ora('Adding Kafka client support...').start();
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const context = {
|
|
51
|
+
packageName,
|
|
52
|
+
packagePath,
|
|
53
|
+
projectName,
|
|
54
|
+
groupId
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// 1. Add dependencies to build.gradle
|
|
58
|
+
spinner.text = 'Adding Kafka dependencies to build.gradle...';
|
|
59
|
+
await addKafkaDependencies(projectDir);
|
|
60
|
+
|
|
61
|
+
// 2. Generate kafka.yml files for all environments
|
|
62
|
+
spinner.text = 'Generating Kafka configuration files...';
|
|
63
|
+
await generateKafkaConfigFiles(projectDir, context);
|
|
64
|
+
|
|
65
|
+
// 3. Add kafka.yml imports to application-*.yml files
|
|
66
|
+
spinner.text = 'Updating application configuration files...';
|
|
67
|
+
await addKafkaImports(projectDir);
|
|
68
|
+
|
|
69
|
+
// 4. Generate KafkaConfig.java
|
|
70
|
+
spinner.text = 'Generating KafkaConfig class...';
|
|
71
|
+
await generateKafkaConfigClass(projectDir, context);
|
|
72
|
+
|
|
73
|
+
// 5. Save feature to configuration
|
|
74
|
+
await configManager.addFeature('kafka');
|
|
75
|
+
|
|
76
|
+
spinner.succeed(chalk.green('Kafka client support added successfully! ✨'));
|
|
77
|
+
|
|
78
|
+
console.log(chalk.blue('\n📦 Added components:'));
|
|
79
|
+
console.log(chalk.gray(' ├── build.gradle (Kafka dependencies)'));
|
|
80
|
+
console.log(chalk.gray(' ├── src/main/resources/parameters/'));
|
|
81
|
+
console.log(chalk.gray(' │ ├── local/kafka.yml'));
|
|
82
|
+
console.log(chalk.gray(' │ ├── develop/kafka.yml'));
|
|
83
|
+
console.log(chalk.gray(' │ ├── test/kafka.yml'));
|
|
84
|
+
console.log(chalk.gray(' │ └── production/kafka.yml'));
|
|
85
|
+
console.log(chalk.gray(' └── shared/configurations/kafkaConfig/KafkaConfig.java'));
|
|
86
|
+
|
|
87
|
+
console.log(chalk.blue('\n✅ Kafka client configured successfully!'));
|
|
88
|
+
console.log(chalk.white('\n Bootstrap Servers: localhost:9092'));
|
|
89
|
+
console.log(chalk.white(` Consumer Group: ${projectName}-api-group`));
|
|
90
|
+
console.log(chalk.gray('\n Update kafka.yml files to customize broker URLs per environment'));
|
|
91
|
+
console.log();
|
|
92
|
+
|
|
93
|
+
} catch (error) {
|
|
94
|
+
spinner.fail(chalk.red('Failed to add Kafka client support'));
|
|
95
|
+
console.error(chalk.red('\n❌ Error:'), error.message);
|
|
96
|
+
if (error.stack) {
|
|
97
|
+
console.error(chalk.gray(error.stack));
|
|
98
|
+
}
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Add Kafka dependencies to build.gradle
|
|
105
|
+
*/
|
|
106
|
+
async function addKafkaDependencies(projectDir) {
|
|
107
|
+
const buildGradlePath = path.join(projectDir, 'build.gradle');
|
|
108
|
+
let buildGradleContent = await fs.readFile(buildGradlePath, 'utf-8');
|
|
109
|
+
|
|
110
|
+
// Check if dependencies already exist
|
|
111
|
+
if (buildGradleContent.includes('spring-kafka')) {
|
|
112
|
+
return; // Already added
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Find the dependencies block and add Kafka dependencies
|
|
116
|
+
const dependenciesMatch = buildGradleContent.match(/(dependencies\s*\{[^}]*)(implementation 'org\.springframework\.modulith:spring-modulith-starter-core'[^\n]*\n)/s);
|
|
117
|
+
|
|
118
|
+
if (!dependenciesMatch) {
|
|
119
|
+
throw new Error('Could not find dependencies block in build.gradle');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const kafkaDependencies = `\n\t// Kafka\n\timplementation 'org.springframework.kafka:spring-kafka'\n\ttestImplementation 'org.springframework.kafka:spring-kafka-test'\n\n\t`;
|
|
123
|
+
|
|
124
|
+
buildGradleContent = buildGradleContent.replace(
|
|
125
|
+
dependenciesMatch[0],
|
|
126
|
+
dependenciesMatch[1] + dependenciesMatch[2] + kafkaDependencies
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
await fs.writeFile(buildGradlePath, buildGradleContent, 'utf-8');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Generate kafka.yml configuration files for all environments
|
|
134
|
+
*/
|
|
135
|
+
async function generateKafkaConfigFiles(projectDir, context) {
|
|
136
|
+
const templatePath = path.join(__dirname, '..', '..', 'templates', 'base', 'resources', 'parameters');
|
|
137
|
+
const environments = ['local', 'develop', 'test', 'production'];
|
|
138
|
+
|
|
139
|
+
for (const env of environments) {
|
|
140
|
+
const outputPath = path.join(projectDir, 'src', 'main', 'resources', 'parameters', env, 'kafka.yml');
|
|
141
|
+
const templateFile = path.join(templatePath, env, 'kafka.yml.ejs');
|
|
142
|
+
|
|
143
|
+
await renderAndWrite(templateFile, outputPath, context);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Add kafka.yml imports to application-*.yml files
|
|
149
|
+
*/
|
|
150
|
+
async function addKafkaImports(projectDir) {
|
|
151
|
+
const resourcesDir = path.join(projectDir, 'src', 'main', 'resources');
|
|
152
|
+
const environments = ['local', 'develop', 'test', 'production'];
|
|
153
|
+
|
|
154
|
+
for (const env of environments) {
|
|
155
|
+
const appYmlPath = path.join(resourcesDir, `application-${env}.yml`);
|
|
156
|
+
|
|
157
|
+
if (await fs.pathExists(appYmlPath)) {
|
|
158
|
+
let content = await fs.readFile(appYmlPath, 'utf-8');
|
|
159
|
+
|
|
160
|
+
// Check if kafka.yml import already exists
|
|
161
|
+
if (content.includes('kafka.yml')) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Add kafka.yml import after existing imports
|
|
166
|
+
const importPattern = /(spring:\s*\n\s*config:\s*\n\s*import:\s*\n(?:\s*-\s*"[^"]+"\s*\n)*)/;
|
|
167
|
+
|
|
168
|
+
if (importPattern.test(content)) {
|
|
169
|
+
content = content.replace(
|
|
170
|
+
importPattern,
|
|
171
|
+
`$1 - "classpath:parameters/${env}/kafka.yml"\n`
|
|
172
|
+
);
|
|
173
|
+
} else {
|
|
174
|
+
// If no imports section exists, add it
|
|
175
|
+
content = `spring:\n config:\n import:\n - "classpath:parameters/${env}/kafka.yml"\n\n` + content;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
await fs.writeFile(appYmlPath, content, 'utf-8');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Generate KafkaConfig.java class
|
|
185
|
+
*/
|
|
186
|
+
async function generateKafkaConfigClass(projectDir, context) {
|
|
187
|
+
const templatePath = path.join(__dirname, '..', '..', 'templates', 'shared', 'configurations', 'kafkaConfig', 'KafkaConfig.java.ejs');
|
|
188
|
+
const outputPath = path.join(projectDir, 'src', 'main', 'java', context.packagePath, 'shared', 'infrastructure', 'configurations', 'kafkaConfig', 'KafkaConfig.java');
|
|
189
|
+
|
|
190
|
+
await renderAndWrite(templatePath, outputPath, context);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
module.exports = addKafkaClientCommand;
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
const inquirer = require('inquirer');
|
|
2
|
+
const ora = require('ora');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs-extra');
|
|
6
|
+
const SharedGenerator = require('../generators/shared-generator');
|
|
7
|
+
const ModuleGenerator = require('../generators/module-generator');
|
|
8
|
+
const { buildModuleContext } = require('../utils/context-builder');
|
|
9
|
+
const { validateModuleName, isEva4jProject, moduleExists } = require('../utils/validator');
|
|
10
|
+
const { toPackagePath } = require('../utils/naming');
|
|
11
|
+
const ConfigManager = require('../utils/config-manager');
|
|
12
|
+
|
|
13
|
+
async function addModuleCommand(moduleName, options) {
|
|
14
|
+
const projectDir = process.cwd();
|
|
15
|
+
|
|
16
|
+
// Validate we're in a Spring Boot project
|
|
17
|
+
if (!(await isEva4jProject(projectDir))) {
|
|
18
|
+
console.error(chalk.red('❌ Not in a Spring Boot project directory'));
|
|
19
|
+
console.error(chalk.gray('Run this command inside a project created with eva4j'));
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Prompt for module name if not provided
|
|
24
|
+
if (!moduleName) {
|
|
25
|
+
const nameAnswer = await inquirer.prompt([
|
|
26
|
+
{
|
|
27
|
+
type: 'input',
|
|
28
|
+
name: 'moduleName',
|
|
29
|
+
message: 'Enter module name (camelCase):',
|
|
30
|
+
validate: (input) => {
|
|
31
|
+
const validation = validateModuleName(input);
|
|
32
|
+
if (validation !== true) {
|
|
33
|
+
return `${validation}. Examples: user, product, orderItem`;
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
]);
|
|
39
|
+
moduleName = nameAnswer.moduleName;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Read build.gradle to get project info
|
|
43
|
+
const buildGradle = await fs.readFile(path.join(projectDir, 'build.gradle'), 'utf-8');
|
|
44
|
+
const groupMatch = buildGradle.match(/group\s*=\s*['"](.+)['"]/);
|
|
45
|
+
const packageMatch = buildGradle.match(/package\s+([a-z.]+)/);
|
|
46
|
+
|
|
47
|
+
if (!groupMatch) {
|
|
48
|
+
console.error(chalk.red('❌ Could not determine project package'));
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Extract package info from Application.java
|
|
53
|
+
const srcJavaDir = path.join(projectDir, 'src', 'main', 'java');
|
|
54
|
+
const javaFiles = await findJavaFiles(srcJavaDir);
|
|
55
|
+
let packageName = '';
|
|
56
|
+
|
|
57
|
+
for (const file of javaFiles) {
|
|
58
|
+
if (file.includes('Application.java')) {
|
|
59
|
+
const content = await fs.readFile(file, 'utf-8');
|
|
60
|
+
const pkgMatch = content.match(/package\s+([a-zA-Z0-9_.]+);/);
|
|
61
|
+
if (pkgMatch) {
|
|
62
|
+
packageName = pkgMatch[1];
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!packageName) {
|
|
69
|
+
console.error(chalk.red('❌ Could not determine project package'));
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const packagePath = toPackagePath(packageName);
|
|
74
|
+
|
|
75
|
+
// Validate module name
|
|
76
|
+
const validation = validateModuleName(moduleName);
|
|
77
|
+
if (validation !== true) {
|
|
78
|
+
console.error(chalk.red(`❌ ${validation}`));
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Check if module already exists (filesystem check)
|
|
83
|
+
if (await moduleExists(projectDir, packagePath, moduleName)) {
|
|
84
|
+
console.error(chalk.red(`❌ Module '${moduleName}' already exists`));
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check ConfigManager for module tracking
|
|
89
|
+
const configManager = new ConfigManager(projectDir);
|
|
90
|
+
let projectName = 'Project';
|
|
91
|
+
|
|
92
|
+
if (await configManager.exists()) {
|
|
93
|
+
if (await configManager.moduleExists(moduleName)) {
|
|
94
|
+
console.error(chalk.red(`❌ Module '${moduleName}' is already registered`));
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
// Load project config to get projectName
|
|
98
|
+
const projectConfig = await configManager.loadProjectConfig();
|
|
99
|
+
if (projectConfig && projectConfig.projectName) {
|
|
100
|
+
projectName = projectConfig.projectName;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Prompt for module options
|
|
105
|
+
const answers = await inquirer.prompt([
|
|
106
|
+
{
|
|
107
|
+
type: 'confirm',
|
|
108
|
+
name: 'hasSoftDelete',
|
|
109
|
+
message: 'Enable soft delete?',
|
|
110
|
+
default: true
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
type: 'confirm',
|
|
114
|
+
name: 'hasAudit',
|
|
115
|
+
message: 'Enable audit fields (createdAt, updatedAt)?',
|
|
116
|
+
default: true
|
|
117
|
+
}
|
|
118
|
+
]);
|
|
119
|
+
|
|
120
|
+
const baseContext = {
|
|
121
|
+
packageName,
|
|
122
|
+
packagePath,
|
|
123
|
+
projectName,
|
|
124
|
+
groupId: groupMatch[1]
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const moduleContext = buildModuleContext(baseContext, moduleName, answers);
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
// Check if shared module needs to be generated
|
|
131
|
+
const needsShared = await SharedGenerator.needsSharedModule(projectDir, packagePath);
|
|
132
|
+
|
|
133
|
+
if (needsShared) {
|
|
134
|
+
console.log(chalk.blue('\n📦 First module! Creating shared module...\n'));
|
|
135
|
+
const sharedSpinner = ora('Generating shared module...').start();
|
|
136
|
+
|
|
137
|
+
const sharedGenerator = new SharedGenerator(moduleContext);
|
|
138
|
+
await sharedGenerator.generate();
|
|
139
|
+
|
|
140
|
+
sharedSpinner.succeed(chalk.green('Shared module created ✨'));
|
|
141
|
+
|
|
142
|
+
console.log(chalk.gray(' └── shared/'));
|
|
143
|
+
console.log(chalk.gray(' ├── domain/ (BaseEntity, AuditableEntity, SoftDeletableEntity)'));
|
|
144
|
+
console.log(chalk.gray(' ├── dto/ (ApiResponse, PageResponse, ErrorDetail)'));
|
|
145
|
+
console.log(chalk.gray(' ├── enums/ (Status, Currency, ErrorCode)'));
|
|
146
|
+
console.log(chalk.gray(' └── constants/'));
|
|
147
|
+
console.log();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Generate module
|
|
151
|
+
const moduleSpinner = ora(`Generating ${moduleName} module...`).start();
|
|
152
|
+
|
|
153
|
+
const moduleGenerator = new ModuleGenerator(moduleContext);
|
|
154
|
+
await moduleGenerator.generate();
|
|
155
|
+
|
|
156
|
+
moduleSpinner.succeed(chalk.green(`Module '${moduleName}' created successfully! ✨`));
|
|
157
|
+
|
|
158
|
+
console.log(chalk.blue(`\n📦 Module structure:`));
|
|
159
|
+
console.log(chalk.gray(` └── ${moduleName}/`));
|
|
160
|
+
console.log(chalk.gray(` ├── package-info.java (@ApplicationModule)`));
|
|
161
|
+
console.log(chalk.gray(` ├── application/`));
|
|
162
|
+
console.log(chalk.gray(` │ ├── commands/ (write operations)`));
|
|
163
|
+
console.log(chalk.gray(` │ ├── dtos/ (data transfer objects)`));
|
|
164
|
+
console.log(chalk.gray(` │ ├── mappers/ (entity-dto conversions)`));
|
|
165
|
+
console.log(chalk.gray(` │ ├── events/ (domain events)`));
|
|
166
|
+
console.log(chalk.gray(` │ ├── ports/ (interfaces)`));
|
|
167
|
+
console.log(chalk.gray(` │ ├── queries/ (read operations)`));
|
|
168
|
+
console.log(chalk.gray(` │ └── usecases/ (application logic)`));
|
|
169
|
+
console.log(chalk.gray(` ├── domain/`));
|
|
170
|
+
console.log(chalk.gray(` │ ├── models/`));
|
|
171
|
+
console.log(chalk.gray(` │ │ ├── entities/`));
|
|
172
|
+
console.log(chalk.gray(` │ │ └── valueObjects/`));
|
|
173
|
+
console.log(chalk.gray(` │ ├── repositories/ (domain interfaces)`));
|
|
174
|
+
console.log(chalk.gray(` │ └── services/ (domain services)`));
|
|
175
|
+
console.log(chalk.gray(` └── infrastructure/`));
|
|
176
|
+
console.log(chalk.gray(` ├── adapters/ (external adapters)`));
|
|
177
|
+
console.log(chalk.gray(` ├── database/ (repository implementations)`));
|
|
178
|
+
console.log(chalk.gray(` └── rest/`));
|
|
179
|
+
console.log(chalk.gray(` ├── controllers/ (REST controllers)`));
|
|
180
|
+
console.log(chalk.gray(` └── validators/ (request validators)`));
|
|
181
|
+
|
|
182
|
+
console.log(chalk.blue('\n✅ Module created successfully!'));
|
|
183
|
+
console.log(chalk.white(`\n Module: ${moduleName}`));
|
|
184
|
+
console.log(chalk.gray(` Package: ${moduleContext.packageName}.${moduleName}`));
|
|
185
|
+
|
|
186
|
+
// Save module to configuration
|
|
187
|
+
if (await configManager.exists()) {
|
|
188
|
+
await configManager.addModule(moduleName, {
|
|
189
|
+
hasSoftDelete: answers.hasSoftDelete,
|
|
190
|
+
hasAudit: answers.hasAudit
|
|
191
|
+
});
|
|
192
|
+
console.log(chalk.gray(' Configuration saved to .eva4j.json'));
|
|
193
|
+
}
|
|
194
|
+
console.log();
|
|
195
|
+
|
|
196
|
+
} catch (error) {
|
|
197
|
+
console.error(chalk.red('\n❌ Failed to create module'));
|
|
198
|
+
console.error(chalk.red(error.message));
|
|
199
|
+
if (error.stack) {
|
|
200
|
+
console.error(chalk.gray(error.stack));
|
|
201
|
+
}
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function findJavaFiles(dir, files = []) {
|
|
207
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
208
|
+
|
|
209
|
+
for (const entry of entries) {
|
|
210
|
+
const fullPath = path.join(dir, entry.name);
|
|
211
|
+
if (entry.isDirectory()) {
|
|
212
|
+
await findJavaFiles(fullPath, files);
|
|
213
|
+
} else if (entry.name.endsWith('.java')) {
|
|
214
|
+
files.push(fullPath);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return files;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
module.exports = addModuleCommand;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const inquirer = require('inquirer');
|
|
2
|
+
const ora = require('ora');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const BaseGenerator = require('../generators/base-generator');
|
|
5
|
+
const { buildBaseContext } = require('../utils/context-builder');
|
|
6
|
+
const { validateProjectName, validateGroupId } = require('../utils/validator');
|
|
7
|
+
const defaults = require('../../config/defaults.json');
|
|
8
|
+
|
|
9
|
+
async function createCommand(projectName, options) {
|
|
10
|
+
console.log(chalk.blue.bold('\n🚀 Creating new Spring Boot project with eva4j\n'));
|
|
11
|
+
|
|
12
|
+
// Gather project information
|
|
13
|
+
const answers = await inquirer.prompt([
|
|
14
|
+
{
|
|
15
|
+
type: 'input',
|
|
16
|
+
name: 'artifactId',
|
|
17
|
+
message: 'Project artifact ID:',
|
|
18
|
+
default: projectName,
|
|
19
|
+
validate: validateProjectName
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
type: 'input',
|
|
23
|
+
name: 'groupId',
|
|
24
|
+
message: 'Group ID:',
|
|
25
|
+
default: 'com.example',
|
|
26
|
+
validate: validateGroupId
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: 'list',
|
|
30
|
+
name: 'javaVersion',
|
|
31
|
+
message: 'Java version:',
|
|
32
|
+
choices: [21, 22, 23],
|
|
33
|
+
default: 21
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
type: 'input',
|
|
37
|
+
name: 'springBootVersion',
|
|
38
|
+
message: 'Spring Boot version:',
|
|
39
|
+
default: defaults.springBootVersion
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
type: 'list',
|
|
43
|
+
name: 'databaseType',
|
|
44
|
+
message: 'Database type:',
|
|
45
|
+
choices: ['postgresql', 'mysql', 'h2'],
|
|
46
|
+
default: 'postgresql'
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
type: 'input',
|
|
50
|
+
name: 'author',
|
|
51
|
+
message: 'Author name:',
|
|
52
|
+
default: 'Developer'
|
|
53
|
+
}
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
// Set all required dependencies
|
|
57
|
+
answers.dependencies = ['web', 'data-jpa', 'security', 'validation', 'actuator'];
|
|
58
|
+
|
|
59
|
+
// Build context
|
|
60
|
+
const context = buildBaseContext(answers);
|
|
61
|
+
|
|
62
|
+
// Generate project
|
|
63
|
+
const spinner = ora('Generating project structure...').start();
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const generator = new BaseGenerator(context);
|
|
67
|
+
await generator.generate();
|
|
68
|
+
|
|
69
|
+
spinner.succeed(chalk.green('Project created successfully! ✨'));
|
|
70
|
+
|
|
71
|
+
console.log(chalk.blue('\n📦 Project structure:'));
|
|
72
|
+
console.log(chalk.gray(` ${context.artifactId}/`));
|
|
73
|
+
console.log(chalk.gray(` ├── src/main/java/${context.packagePath.replace(/\//g, '.')}`));
|
|
74
|
+
console.log(chalk.gray(` │ ├── ${context.applicationClassName}.java`));
|
|
75
|
+
console.log(chalk.gray(` │ └── common/`));
|
|
76
|
+
console.log(chalk.gray(` ├── build.gradle`));
|
|
77
|
+
console.log(chalk.gray(` └── README.md`));
|
|
78
|
+
|
|
79
|
+
console.log(chalk.blue('\n🚀 Next steps:'));
|
|
80
|
+
console.log(chalk.white(` cd ${context.artifactId}`));
|
|
81
|
+
console.log(chalk.white(` eva4j add module user # Add your first module`));
|
|
82
|
+
console.log(chalk.white(` ./gradlew bootRun # Run the application`));
|
|
83
|
+
console.log();
|
|
84
|
+
|
|
85
|
+
} catch (error) {
|
|
86
|
+
spinner.fail(chalk.red('Failed to create project'));
|
|
87
|
+
console.error(chalk.red(error.message));
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = createCommand;
|