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.
Files changed (115) hide show
  1. package/LICENSE +21 -0
  2. package/QUICK_REFERENCE.md +204 -0
  3. package/README.md +912 -0
  4. package/USAGE.md +349 -0
  5. package/bin/eva4j.js +234 -0
  6. package/config/defaults.json +46 -0
  7. package/package.json +57 -0
  8. package/src/commands/add-kafka-client.js +193 -0
  9. package/src/commands/add-module.js +221 -0
  10. package/src/commands/create.js +92 -0
  11. package/src/commands/detach.js +495 -0
  12. package/src/commands/generate-http-exchange.js +309 -0
  13. package/src/commands/generate-kafka-event.js +453 -0
  14. package/src/commands/generate-kafka-listener.js +267 -0
  15. package/src/commands/generate-resource.js +265 -0
  16. package/src/commands/generate-usecase.js +198 -0
  17. package/src/commands/info.js +63 -0
  18. package/src/generators/base-generator.js +150 -0
  19. package/src/generators/module-generator.js +48 -0
  20. package/src/generators/shared-generator.js +153 -0
  21. package/src/utils/config-manager.js +156 -0
  22. package/src/utils/context-builder.js +149 -0
  23. package/src/utils/naming.js +137 -0
  24. package/src/utils/template-engine.js +55 -0
  25. package/src/utils/validator.js +159 -0
  26. package/templates/base/application/Application.java.ejs +27 -0
  27. package/templates/base/docker/docker-compose.yml.ejs +41 -0
  28. package/templates/base/gradle/build.gradle.ejs +70 -0
  29. package/templates/base/gradle/settings.gradle.ejs +1 -0
  30. package/templates/base/resources/application-develop.yml.ejs +5 -0
  31. package/templates/base/resources/application-local.yml.ejs +5 -0
  32. package/templates/base/resources/application-production.yml.ejs +9 -0
  33. package/templates/base/resources/application-test.yml.ejs +5 -0
  34. package/templates/base/resources/application.yml.ejs +31 -0
  35. package/templates/base/resources/parameters/develop/cors.yml.ejs +4 -0
  36. package/templates/base/resources/parameters/develop/db.yaml.ejs +21 -0
  37. package/templates/base/resources/parameters/develop/kafka.yml.ejs +26 -0
  38. package/templates/base/resources/parameters/local/cors.yml.ejs +4 -0
  39. package/templates/base/resources/parameters/local/db.yaml.ejs +21 -0
  40. package/templates/base/resources/parameters/local/kafka.yml.ejs +26 -0
  41. package/templates/base/resources/parameters/production/cors.yml.ejs +4 -0
  42. package/templates/base/resources/parameters/production/db.yaml.ejs +21 -0
  43. package/templates/base/resources/parameters/production/kafka.yml.ejs +26 -0
  44. package/templates/base/root/README.md.ejs +126 -0
  45. package/templates/base/root/gitignore.ejs +42 -0
  46. package/templates/http-exchange/Adapter.java.ejs +39 -0
  47. package/templates/http-exchange/Config.java.ejs +24 -0
  48. package/templates/http-exchange/FeignClient.java.ejs +23 -0
  49. package/templates/http-exchange/Port.java.ejs +14 -0
  50. package/templates/kafka-event/Event.java.ejs +10 -0
  51. package/templates/kafka-event/KafkaConfigBean.java.ejs +7 -0
  52. package/templates/kafka-event/KafkaMessageBroker.java.ejs +32 -0
  53. package/templates/kafka-event/MessageBroker.java.ejs +8 -0
  54. package/templates/kafka-event/MessageBrokerImplMethod.java.ejs +9 -0
  55. package/templates/kafka-event/MessageBrokerMethod.java.ejs +1 -0
  56. package/templates/kafka-listener/KafkaController.java.ejs +34 -0
  57. package/templates/kafka-listener/ListenerMethod.java.ejs +9 -0
  58. package/templates/kafka-listener/ValueField.java.ejs +4 -0
  59. package/templates/module/controller.java.ejs +96 -0
  60. package/templates/module/exception.java.ejs +18 -0
  61. package/templates/module/mapper.java.ejs +67 -0
  62. package/templates/module/model.java.ejs +29 -0
  63. package/templates/module/package-info.java.ejs +7 -0
  64. package/templates/module/repository.java.ejs +26 -0
  65. package/templates/module/request-dto.java.ejs +24 -0
  66. package/templates/module/response-dto.java.ejs +26 -0
  67. package/templates/module/service-impl.java.ejs +112 -0
  68. package/templates/module/service.java.ejs +45 -0
  69. package/templates/module/update-dto.java.ejs +25 -0
  70. package/templates/resource/Command.java.ejs +9 -0
  71. package/templates/resource/CommandHandler.java.ejs +22 -0
  72. package/templates/resource/Controller.java.ejs +73 -0
  73. package/templates/resource/Query.java.ejs +12 -0
  74. package/templates/resource/QueryHandler.java.ejs +31 -0
  75. package/templates/resource/ResponseDto.java.ejs +6 -0
  76. package/templates/shared/annotations/ApplicationComponent.java.ejs +9 -0
  77. package/templates/shared/annotations/DomainComponent.java.ejs +9 -0
  78. package/templates/shared/annotations/LogAfter.java.ejs +11 -0
  79. package/templates/shared/annotations/LogBefore.java.ejs +11 -0
  80. package/templates/shared/annotations/LogExceptions.java.ejs +11 -0
  81. package/templates/shared/annotations/LogTimer.java.ejs +11 -0
  82. package/templates/shared/configurations/kafkaConfig/KafkaConfig.java.ejs +49 -0
  83. package/templates/shared/configurations/loggerConfig/HandlerLogs.java.ejs +56 -0
  84. package/templates/shared/configurations/securityConfig/SecurityConfig.java.ejs +57 -0
  85. package/templates/shared/configurations/swaggerConfig/SwaggerConfig.java.ejs +31 -0
  86. package/templates/shared/configurations/useCaseConfig/UseCaseAutoRegister.java.ejs +51 -0
  87. package/templates/shared/configurations/useCaseConfig/UseCaseConfig.java.ejs +25 -0
  88. package/templates/shared/configurations/useCaseConfig/UseCaseContainer.java.ejs +29 -0
  89. package/templates/shared/configurations/useCaseConfig/UseCaseMediator.java.ejs +38 -0
  90. package/templates/shared/customExceptions/BadRequestException.java.ejs +11 -0
  91. package/templates/shared/customExceptions/ConflictException.java.ejs +8 -0
  92. package/templates/shared/customExceptions/ForbiddenException.java.ejs +8 -0
  93. package/templates/shared/customExceptions/ImportFileException.java.ejs +6 -0
  94. package/templates/shared/customExceptions/NotFoundException.java.ejs +8 -0
  95. package/templates/shared/customExceptions/UnauthorizedException.java.ejs +9 -0
  96. package/templates/shared/customExceptions/ValidationException.java.ejs +17 -0
  97. package/templates/shared/errorMessage/ErrorMessage.java.ejs +5 -0
  98. package/templates/shared/errorMessage/FullErrorMessage.java.ejs +9 -0
  99. package/templates/shared/errorMessage/ShortErrorMessage.java.ejs +6 -0
  100. package/templates/shared/eventEnvelope/EventEnvelope.java.ejs +13 -0
  101. package/templates/shared/eventEnvelope/EventMetadata.java.ejs +24 -0
  102. package/templates/shared/filters/CorrelationIdFilter.java.ejs +45 -0
  103. package/templates/shared/handlerException/HandlerExceptions.java.ejs +148 -0
  104. package/templates/shared/interfaces/Command.java.ejs +4 -0
  105. package/templates/shared/interfaces/CommandHandler.java.ejs +5 -0
  106. package/templates/shared/interfaces/Dispatchable.java.ejs +4 -0
  107. package/templates/shared/interfaces/Handler.java.ejs +4 -0
  108. package/templates/shared/interfaces/Query.java.ejs +4 -0
  109. package/templates/shared/interfaces/QueryHandler.java.ejs +5 -0
  110. package/templates/shared/package-info.java.ejs +8 -0
  111. package/templates/usecase/command/Command.java.ejs +7 -0
  112. package/templates/usecase/command/CommandHandler.java.ejs +21 -0
  113. package/templates/usecase/query/Query.java.ejs +10 -0
  114. package/templates/usecase/query/QueryHandler.java.ejs +22 -0
  115. 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;