eva4j 1.0.14 β 1.0.16
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/.agents/skills/skill-creator/LICENSE.txt +202 -0
- package/.agents/skills/skill-creator/SKILL.md +485 -0
- package/.agents/skills/skill-creator/agents/analyzer.md +274 -0
- package/.agents/skills/skill-creator/agents/comparator.md +202 -0
- package/.agents/skills/skill-creator/agents/grader.md +223 -0
- package/.agents/skills/skill-creator/assets/eval_review.html +146 -0
- package/.agents/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/.agents/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/.agents/skills/skill-creator/references/schemas.md +430 -0
- package/.agents/skills/skill-creator/scripts/__init__.py +0 -0
- package/.agents/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/.agents/skills/skill-creator/scripts/generate_report.py +326 -0
- package/.agents/skills/skill-creator/scripts/improve_description.py +247 -0
- package/.agents/skills/skill-creator/scripts/package_skill.py +136 -0
- package/.agents/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/.agents/skills/skill-creator/scripts/run_eval.py +310 -0
- package/.agents/skills/skill-creator/scripts/run_loop.py +328 -0
- package/.agents/skills/skill-creator/scripts/utils.py +47 -0
- package/AGENTS.md +268 -6
- package/COMMAND_EVALUATION.md +15 -16
- package/DOMAIN_YAML_GUIDE.md +430 -14
- package/FUTURE_FEATURES.md +1627 -1168
- package/README.md +461 -13
- package/bin/eva4j.js +32 -14
- package/config/defaults.json +1 -0
- package/docs/commands/EVALUATE_SYSTEM.md +746 -261
- package/docs/commands/EXPORT_DIAGRAM.md +153 -0
- package/docs/commands/GENERATE_ENTITIES.md +599 -6
- package/docs/commands/INDEX.md +7 -0
- package/examples/domain-events.yaml +166 -20
- package/examples/domain-listeners.yaml +212 -0
- package/examples/domain-one-to-many.yaml +1 -0
- package/examples/domain-one-to-one.yaml +1 -0
- package/examples/domain-ports.yaml +414 -0
- package/examples/domain-soft-delete.yaml +47 -44
- package/examples/system/notification.yaml +147 -0
- package/examples/system/product.yaml +185 -0
- package/examples/system/system.yaml +112 -0
- package/examples/system-report.html +971 -0
- package/examples/system.yaml +46 -3
- package/package.json +2 -1
- package/src/agents/design-reviewer.agent.md +163 -0
- package/src/commands/build.js +714 -0
- package/src/commands/create.js +1 -0
- package/src/commands/detach.js +149 -108
- package/src/commands/evaluate-system.js +234 -8
- package/src/commands/export-diagram.js +522 -0
- package/src/commands/generate-entities.js +685 -66
- package/src/commands/generate-http-exchange.js +2 -0
- package/src/commands/generate-kafka-event.js +43 -10
- package/src/generators/base-generator.js +18 -6
- package/src/generators/postman-generator.js +188 -0
- package/src/generators/shared-generator.js +12 -2
- package/src/skills/build-system-yaml/SKILL.md +323 -0
- package/src/skills/build-system-yaml/references/domain-yaml-spec.md +410 -0
- package/src/skills/build-system-yaml/references/module-spec.md +367 -0
- package/src/skills/build-system-yaml/references/system-yaml-spec.md +178 -0
- package/src/utils/config-manager.js +54 -0
- package/src/utils/context-builder.js +1 -0
- package/src/utils/domain-diagram.js +192 -0
- package/src/utils/domain-validator.js +1020 -0
- package/src/utils/fake-data.js +376 -0
- package/src/utils/system-validator.js +319 -199
- package/src/utils/yaml-to-entity.js +272 -7
- package/templates/aggregate/AggregateMapper.java.ejs +3 -2
- package/templates/aggregate/AggregateRepository.java.ejs +3 -2
- package/templates/aggregate/AggregateRepositoryImpl.java.ejs +6 -5
- package/templates/aggregate/AggregateRoot.java.ejs +60 -2
- package/templates/aggregate/DomainEventHandler.java.ejs +4 -1
- package/templates/aggregate/DomainEventRecord.java.ejs +24 -8
- package/templates/aggregate/DomainEventSnapshot.java.ejs +46 -0
- package/templates/aggregate/JpaAggregateRoot.java.ejs +6 -0
- package/templates/base/docker/Dockerfile.ejs +21 -0
- package/templates/base/gradle/build.gradle.ejs +3 -2
- package/templates/base/root/AGENTS.md.ejs +306 -45
- package/templates/crud/ApplicationMapper.java.ejs +4 -0
- package/templates/crud/Controller.java.ejs +4 -4
- package/templates/crud/CreateCommand.java.ejs +4 -0
- package/templates/crud/CreateCommandHandler.java.ejs +3 -6
- package/templates/crud/CreateItemDto.java.ejs +4 -0
- package/templates/crud/CreateValueObjectDto.java.ejs +4 -0
- package/templates/crud/DeleteCommandHandler.java.ejs +12 -6
- package/templates/crud/EndpointsController.java.ejs +6 -6
- package/templates/crud/FindByQuery.java.ejs +1 -1
- package/templates/crud/FindByQueryHandler.java.ejs +3 -9
- package/templates/crud/GetQueryHandler.java.ejs +2 -5
- package/templates/crud/ListQuery.java.ejs +1 -1
- package/templates/crud/ListQueryHandler.java.ejs +8 -14
- package/templates/crud/ScaffoldCommandHandler.java.ejs +2 -4
- package/templates/crud/ScaffoldQuery.java.ejs +3 -2
- package/templates/crud/ScaffoldQueryHandler.java.ejs +5 -6
- package/templates/crud/SubEntityAddCommand.java.ejs +4 -0
- package/templates/crud/SubEntityAddCommandHandler.java.ejs +2 -6
- package/templates/crud/SubEntityRemoveCommandHandler.java.ejs +2 -7
- package/templates/crud/TransitionCommandHandler.java.ejs +2 -6
- package/templates/crud/UpdateCommand.java.ejs +4 -0
- package/templates/crud/UpdateCommandHandler.java.ejs +2 -5
- package/templates/evaluate/report.html.ejs +394 -2
- package/templates/kafka-event/DomainEventHandlerMethod.ejs +3 -1
- package/templates/kafka-event/Event.java.ejs +9 -0
- package/templates/kafka-listener/KafkaController.java.ejs +1 -1
- package/templates/kafka-listener/KafkaListenerClass.java.ejs +1 -1
- package/templates/kafka-listener/ListenerClass.java.ejs +65 -0
- package/templates/kafka-listener/ListenerCommand.java.ejs +31 -0
- package/templates/kafka-listener/ListenerCommandHandler.java.ejs +25 -0
- package/templates/kafka-listener/ListenerIntegrationEvent.java.ejs +37 -0
- package/templates/kafka-listener/ListenerMethod.java.ejs +1 -1
- package/templates/kafka-listener/ListenerNestedType.java.ejs +28 -0
- package/templates/mock/MockEvent.java.ejs +10 -0
- package/templates/mock/MockMessageBrokerImpl.java.ejs +35 -0
- package/templates/mock/MockMessageBrokerImplMethod.java.ejs +6 -0
- package/templates/mock/SpringEventListener.java.ejs +61 -0
- package/templates/ports/PortDomainModel.java.ejs +35 -0
- package/templates/ports/PortFeignAdapter.java.ejs +67 -0
- package/templates/ports/PortFeignClient.java.ejs +45 -0
- package/templates/ports/PortFeignConfig.java.ejs +24 -0
- package/templates/ports/PortInterface.java.ejs +45 -0
- package/templates/ports/PortNestedType.java.ejs +28 -0
- package/templates/ports/PortRequestDto.java.ejs +30 -0
- package/templates/ports/PortResponseDto.java.ejs +28 -0
- package/templates/postman/Collection.json.ejs +1 -1
- package/templates/postman/UnifiedCollection.json.ejs +185 -0
- package/templates/shared/annotations/LogAfter.java.ejs +2 -0
- package/templates/shared/annotations/LogBefore.java.ejs +2 -0
- package/templates/shared/annotations/LogExceptions.java.ejs +2 -0
- package/templates/shared/annotations/LogLevel.java.ejs +8 -0
- package/templates/shared/annotations/LogTimer.java.ejs +1 -0
- package/templates/shared/annotations/Loggable.java.ejs +17 -0
- package/templates/shared/configurations/eventPublicationConfig/EventPublicationSchemaConfig.java.ejs +109 -0
- package/templates/shared/configurations/loggerConfig/HandlerLogs.java.ejs +120 -32
- package/templates/shared/errorMessage/ErrorResponse.java.ejs +23 -0
- package/templates/shared/handlerException/HandlerExceptions.java.ejs +99 -79
- package/src/commands/generate-system.js +0 -243
- package/templates/base/root/skill-build-domain-yaml-references-generate-entities.md.ejs +0 -1103
- package/templates/base/root/skill-build-domain-yaml.ejs +0 -292
- package/templates/base/root/skill-build-system-yaml.ejs +0 -252
- package/templates/shared/errorMessage/ErrorMessage.java.ejs +0 -5
- package/templates/shared/errorMessage/FullErrorMessage.java.ejs +0 -9
- package/templates/shared/errorMessage/ShortErrorMessage.java.ejs +0 -6
package/src/commands/create.js
CHANGED
|
@@ -76,6 +76,7 @@ async function createCommand(projectName, options) {
|
|
|
76
76
|
console.log(chalk.gray(` βββ src/main/java/${context.packagePath.replace(/\//g, '.')}`));
|
|
77
77
|
console.log(chalk.gray(` β βββ ${context.applicationClassName}.java`));
|
|
78
78
|
console.log(chalk.gray(` β βββ common/`));
|
|
79
|
+
console.log(chalk.gray(` βββ system/`));
|
|
79
80
|
console.log(chalk.gray(` βββ build.gradle`));
|
|
80
81
|
console.log(chalk.gray(` βββ README.md`));
|
|
81
82
|
|
package/src/commands/detach.js
CHANGED
|
@@ -191,6 +191,7 @@ async function detachCommand(moduleName, options) {
|
|
|
191
191
|
createdDate: new Date().toISOString().split('T')[0],
|
|
192
192
|
dependencyManagementVersion: defaults.dependencyManagementVersion,
|
|
193
193
|
springCloudVersion: projectConfig.springCloudVersion || defaults.springCloudVersion,
|
|
194
|
+
springdocVersion: projectConfig.springdocVersion || defaults.springdocVersion,
|
|
194
195
|
gradleVersion: defaults.gradleVersion,
|
|
195
196
|
license: 'MIT',
|
|
196
197
|
description: `Detached microservice: ${newProjectName}`,
|
|
@@ -228,15 +229,20 @@ async function detachCommand(moduleName, options) {
|
|
|
228
229
|
// Step 3: Generate base project using BaseGenerator
|
|
229
230
|
await generateDetachedProject(newProjectDir, detachedContext);
|
|
230
231
|
|
|
232
|
+
spinner.text = 'Copying Gradle wrapper...';
|
|
233
|
+
|
|
234
|
+
// Step 4: Copy Gradle wrapper from parent project
|
|
235
|
+
await copyGradleWrapper(projectDir, newProjectDir);
|
|
236
|
+
|
|
231
237
|
spinner.text = 'Copying module files...';
|
|
232
238
|
|
|
233
|
-
// Step
|
|
239
|
+
// Step 5: Copy module directory
|
|
234
240
|
const newModuleDir = path.join(newProjectDir, 'src', 'main', 'java', packagePath, moduleName);
|
|
235
241
|
await fs.copy(moduleDir, newModuleDir);
|
|
236
242
|
|
|
237
243
|
spinner.text = 'Merging shared components...';
|
|
238
244
|
|
|
239
|
-
// Step
|
|
245
|
+
// Step 6: Merge shared/domain into module/domain (filtering modulith-only components)
|
|
240
246
|
await mergeSharedComponents(
|
|
241
247
|
sharedDir,
|
|
242
248
|
newModuleDir,
|
|
@@ -246,7 +252,7 @@ async function detachCommand(moduleName, options) {
|
|
|
246
252
|
|
|
247
253
|
spinner.text = 'Updating package references...';
|
|
248
254
|
|
|
249
|
-
// Step
|
|
255
|
+
// Step 7: Update all imports in module files
|
|
250
256
|
await updatePackageReferences(
|
|
251
257
|
newModuleDir,
|
|
252
258
|
packageName,
|
|
@@ -255,10 +261,10 @@ async function detachCommand(moduleName, options) {
|
|
|
255
261
|
|
|
256
262
|
spinner.text = 'Cleaning up...';
|
|
257
263
|
|
|
258
|
-
// Step
|
|
264
|
+
// Step 8: Remove package-info.java files
|
|
259
265
|
await removePackageInfoFiles(newModuleDir);
|
|
260
266
|
|
|
261
|
-
// Step
|
|
267
|
+
// Step 9: Copy test files if they exist
|
|
262
268
|
const testModuleDir = path.join(projectDir, 'src', 'test', 'java', packagePath, moduleName);
|
|
263
269
|
if (await fs.pathExists(testModuleDir)) {
|
|
264
270
|
const newTestModuleDir = path.join(newProjectDir, 'src', 'test', 'java', packagePath, moduleName);
|
|
@@ -266,7 +272,7 @@ async function detachCommand(moduleName, options) {
|
|
|
266
272
|
await updatePackageReferences(newTestModuleDir, packageName, moduleName);
|
|
267
273
|
}
|
|
268
274
|
|
|
269
|
-
// Step
|
|
275
|
+
// Step 10: Create detached project configuration
|
|
270
276
|
const detachedConfigManager = new ConfigManager(newProjectDir);
|
|
271
277
|
await detachedConfigManager.saveProjectConfig({
|
|
272
278
|
...detachedContext,
|
|
@@ -280,8 +286,11 @@ async function detachCommand(moduleName, options) {
|
|
|
280
286
|
|
|
281
287
|
spinner.text = 'Copying environment configurations...';
|
|
282
288
|
|
|
283
|
-
// Step
|
|
284
|
-
await
|
|
289
|
+
// Step 11: Copy parameter files from parent (kafka.yaml, urls.yaml, etc.)
|
|
290
|
+
await copyParameterFiles(projectDir, newProjectDir, packageName, moduleName);
|
|
291
|
+
|
|
292
|
+
// Step 12: Copy domain.yaml specification if it exists
|
|
293
|
+
await copyDomainYaml(projectDir, newProjectDir, moduleName);
|
|
285
294
|
|
|
286
295
|
spinner.succeed(chalk.green('β
Module detached successfully! β¨'));
|
|
287
296
|
|
|
@@ -372,74 +381,43 @@ function transformSharedFileContent(content, packageName, moduleName) {
|
|
|
372
381
|
}
|
|
373
382
|
|
|
374
383
|
/**
|
|
375
|
-
*
|
|
384
|
+
* Directories that are specific to Spring Modulith (modular monolith)
|
|
385
|
+
* and should NOT be copied when detaching to a standalone microservice.
|
|
386
|
+
*/
|
|
387
|
+
const MODULITH_ONLY_DIRS = new Set([
|
|
388
|
+
'eventPublicationConfig', // Spring Modulith event_publication table
|
|
389
|
+
'mockEvent', // In-memory event surrogate for mock mode
|
|
390
|
+
]);
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Files specific to Spring Modulith that should be skipped during merge.
|
|
394
|
+
*/
|
|
395
|
+
const MODULITH_ONLY_FILES = new Set([
|
|
396
|
+
'package-info.java', // Contains @ApplicationModule(type = OPEN)
|
|
397
|
+
]);
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Merge shared components into module structure, filtering out modulith-only components.
|
|
376
401
|
*/
|
|
377
402
|
async function mergeSharedComponents(sharedDir, moduleDir, packageName, moduleName) {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
// Copy and transform subdirectories (annotations, interfaces, customExceptions, etc.)
|
|
388
|
-
const destPath = path.join(moduleDir, 'domain', entry);
|
|
389
|
-
await copyAndTransformDirectory(sourcePath, destPath, packageName, moduleName);
|
|
390
|
-
} else if (stat.isFile() && entry.endsWith('.java')) {
|
|
391
|
-
// Copy and transform individual .java files (AuditableEntity.java, etc.)
|
|
392
|
-
const destPath = path.join(moduleDir, 'domain', entry);
|
|
393
|
-
if (!(await fs.pathExists(destPath))) {
|
|
394
|
-
const content = await fs.readFile(sourcePath, 'utf-8');
|
|
395
|
-
const transformed = transformSharedFileContent(content, packageName, moduleName);
|
|
396
|
-
await fs.ensureDir(path.dirname(destPath));
|
|
397
|
-
await fs.writeFile(destPath, transformed, 'utf-8');
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
// Merge shared/infrastructure/* into module/infrastructure/
|
|
404
|
-
const sharedInfraDir = path.join(sharedDir, 'infrastructure');
|
|
405
|
-
if (await fs.pathExists(sharedInfraDir)) {
|
|
406
|
-
const infraEntries = await fs.readdir(sharedInfraDir);
|
|
407
|
-
for (const entry of infraEntries) {
|
|
408
|
-
const sourcePath = path.join(sharedInfraDir, entry);
|
|
409
|
-
const stat = await fs.stat(sourcePath);
|
|
410
|
-
|
|
411
|
-
if (stat.isDirectory()) {
|
|
412
|
-
// Copy and transform subdirectories
|
|
413
|
-
const destPath = path.join(moduleDir, 'infrastructure', entry);
|
|
414
|
-
await copyAndTransformDirectory(sourcePath, destPath, packageName, moduleName);
|
|
415
|
-
} else if (stat.isFile() && entry.endsWith('.java')) {
|
|
416
|
-
// Copy and transform individual .java files
|
|
417
|
-
const destPath = path.join(moduleDir, 'infrastructure', entry);
|
|
418
|
-
if (!(await fs.pathExists(destPath))) {
|
|
419
|
-
const content = await fs.readFile(sourcePath, 'utf-8');
|
|
420
|
-
const transformed = transformSharedFileContent(content, packageName, moduleName);
|
|
421
|
-
await fs.ensureDir(path.dirname(destPath));
|
|
422
|
-
await fs.writeFile(destPath, transformed, 'utf-8');
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// Merge shared/application/* into module/application/
|
|
429
|
-
const sharedApplicationDir = path.join(sharedDir, 'application');
|
|
430
|
-
if (await fs.pathExists(sharedApplicationDir)) {
|
|
431
|
-
const applicationEntries = await fs.readdir(sharedApplicationDir);
|
|
432
|
-
for (const entry of applicationEntries) {
|
|
433
|
-
const sourcePath = path.join(sharedApplicationDir, entry);
|
|
403
|
+
const layers = ['domain', 'infrastructure', 'application'];
|
|
404
|
+
|
|
405
|
+
for (const layer of layers) {
|
|
406
|
+
const sharedLayerDir = path.join(sharedDir, layer);
|
|
407
|
+
if (!(await fs.pathExists(sharedLayerDir))) continue;
|
|
408
|
+
|
|
409
|
+
const entries = await fs.readdir(sharedLayerDir);
|
|
410
|
+
for (const entry of entries) {
|
|
411
|
+
const sourcePath = path.join(sharedLayerDir, entry);
|
|
434
412
|
const stat = await fs.stat(sourcePath);
|
|
435
|
-
|
|
413
|
+
|
|
436
414
|
if (stat.isDirectory()) {
|
|
437
|
-
|
|
438
|
-
const destPath = path.join(moduleDir,
|
|
415
|
+
if (MODULITH_ONLY_DIRS.has(entry)) continue;
|
|
416
|
+
const destPath = path.join(moduleDir, layer, entry);
|
|
439
417
|
await copyAndTransformDirectory(sourcePath, destPath, packageName, moduleName);
|
|
440
418
|
} else if (stat.isFile() && entry.endsWith('.java')) {
|
|
441
|
-
|
|
442
|
-
const destPath = path.join(moduleDir,
|
|
419
|
+
if (MODULITH_ONLY_FILES.has(entry)) continue;
|
|
420
|
+
const destPath = path.join(moduleDir, layer, entry);
|
|
443
421
|
if (!(await fs.pathExists(destPath))) {
|
|
444
422
|
const content = await fs.readFile(sourcePath, 'utf-8');
|
|
445
423
|
const transformed = transformSharedFileContent(content, packageName, moduleName);
|
|
@@ -605,55 +583,118 @@ async function findPackageInfoFiles(dir) {
|
|
|
605
583
|
}
|
|
606
584
|
|
|
607
585
|
/**
|
|
608
|
-
* Copy
|
|
586
|
+
* Copy Gradle wrapper files from parent project to detached project.
|
|
587
|
+
* Includes gradlew, gradlew.bat, and gradle/wrapper/ directory.
|
|
609
588
|
*/
|
|
610
|
-
async function
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
'application-develop.yaml',
|
|
617
|
-
'application-local.yaml',
|
|
618
|
-
'application-production.yaml',
|
|
619
|
-
'application-test.yaml'
|
|
620
|
-
];
|
|
621
|
-
|
|
622
|
-
for (const file of profileFiles) {
|
|
623
|
-
const sourcePath = path.join(parentResourcesDir, file);
|
|
624
|
-
if (await fs.pathExists(sourcePath)) {
|
|
625
|
-
await fs.copy(sourcePath, path.join(newResourcesDir, file));
|
|
589
|
+
async function copyGradleWrapper(parentDir, newProjectDir) {
|
|
590
|
+
const wrapperFiles = ['gradlew', 'gradlew.bat'];
|
|
591
|
+
for (const file of wrapperFiles) {
|
|
592
|
+
const src = path.join(parentDir, file);
|
|
593
|
+
if (await fs.pathExists(src)) {
|
|
594
|
+
await fs.copy(src, path.join(newProjectDir, file));
|
|
626
595
|
}
|
|
627
596
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
await fs.copy(parametersDir, path.join(newResourcesDir, 'parameters'));
|
|
633
|
-
|
|
634
|
-
// Update package references in kafka.yaml files
|
|
635
|
-
await updateKafkaConfigReferences(newResourcesDir, packageName, moduleName);
|
|
597
|
+
|
|
598
|
+
const wrapperDir = path.join(parentDir, 'gradle', 'wrapper');
|
|
599
|
+
if (await fs.pathExists(wrapperDir)) {
|
|
600
|
+
await fs.copy(wrapperDir, path.join(newProjectDir, 'gradle', 'wrapper'));
|
|
636
601
|
}
|
|
637
602
|
}
|
|
638
603
|
|
|
639
604
|
/**
|
|
640
|
-
*
|
|
605
|
+
* Copy parameter files (kafka.yaml, urls.yaml, etc.) from parent to detached project.
|
|
606
|
+
* Updates package references from shared to module name.
|
|
607
|
+
* Adds missing imports to application-{env}.yaml files.
|
|
641
608
|
*/
|
|
642
|
-
async function
|
|
609
|
+
async function copyParameterFiles(parentDir, newProjectDir, packageName, moduleName) {
|
|
610
|
+
const parentParametersDir = path.join(parentDir, 'src', 'main', 'resources', 'parameters');
|
|
611
|
+
const newParametersDir = path.join(newProjectDir, 'src', 'main', 'resources', 'parameters');
|
|
612
|
+
|
|
613
|
+
if (!(await fs.pathExists(parentParametersDir))) return;
|
|
614
|
+
|
|
615
|
+
await fs.copy(parentParametersDir, newParametersDir);
|
|
616
|
+
|
|
617
|
+
// Update package references in yaml config files (kafka.yaml, urls.yaml, etc.)
|
|
643
618
|
const environments = ['local', 'develop', 'test', 'production'];
|
|
644
|
-
|
|
619
|
+
const sharedPattern = new RegExp(
|
|
620
|
+
`${packageName.replace(/\./g, '\\.')}\\.shared\\.infrastructure\\.`,
|
|
621
|
+
'g'
|
|
622
|
+
);
|
|
623
|
+
|
|
624
|
+
// Base config files that BaseGenerator already imports (db.yaml, cors.yaml)
|
|
625
|
+
const baseImports = new Set(['db.yaml', 'cors.yaml']);
|
|
626
|
+
|
|
645
627
|
for (const env of environments) {
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
628
|
+
const envDir = path.join(newParametersDir, env);
|
|
629
|
+
if (!(await fs.pathExists(envDir))) continue;
|
|
630
|
+
|
|
631
|
+
const files = await fs.readdir(envDir);
|
|
632
|
+
const extraImports = [];
|
|
633
|
+
|
|
634
|
+
for (const file of files) {
|
|
635
|
+
if (!file.endsWith('.yaml') && !file.endsWith('.yml')) continue;
|
|
636
|
+
|
|
637
|
+
// Update package references
|
|
638
|
+
const filePath = path.join(envDir, file);
|
|
639
|
+
let content = await fs.readFile(filePath, 'utf-8');
|
|
640
|
+
if (sharedPattern.test(content)) {
|
|
641
|
+
content = content.replace(sharedPattern, `${packageName}.${moduleName}.infrastructure.`);
|
|
642
|
+
await fs.writeFile(filePath, content, 'utf-8');
|
|
643
|
+
}
|
|
644
|
+
sharedPattern.lastIndex = 0;
|
|
645
|
+
|
|
646
|
+
// Collect extra parameter files that need imports
|
|
647
|
+
if (!baseImports.has(file)) {
|
|
648
|
+
extraImports.push(`classpath:parameters/${env}/${file}`);
|
|
649
|
+
}
|
|
656
650
|
}
|
|
651
|
+
|
|
652
|
+
// Add missing imports to application-{env}.yaml
|
|
653
|
+
if (extraImports.length > 0) {
|
|
654
|
+
await addMissingImports(newProjectDir, env, extraImports);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Add missing classpath imports to an application-{env}.yaml file.
|
|
661
|
+
*/
|
|
662
|
+
async function addMissingImports(projectDir, env, imports) {
|
|
663
|
+
const appYmlPath = path.join(projectDir, 'src', 'main', 'resources', `application-${env}.yaml`);
|
|
664
|
+
if (!(await fs.pathExists(appYmlPath))) return;
|
|
665
|
+
|
|
666
|
+
let content = await fs.readFile(appYmlPath, 'utf-8');
|
|
667
|
+
|
|
668
|
+
const linesToAdd = imports.filter(imp => !content.includes(imp));
|
|
669
|
+
if (linesToAdd.length === 0) return;
|
|
670
|
+
|
|
671
|
+
// Append after existing import entries
|
|
672
|
+
const importPattern = /(spring:\s*\n\s*config:\s*\n\s*import:\s*\n(?:\s*-\s*"[^"]+"\s*\n)*)/;
|
|
673
|
+
if (importPattern.test(content)) {
|
|
674
|
+
const suffix = linesToAdd.map(imp => ` - "${imp}"`).join('\n') + '\n';
|
|
675
|
+
content = content.replace(importPattern, `$1${suffix}`);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
await fs.writeFile(appYmlPath, content, 'utf-8');
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Copy domain.yaml specification for the module if it exists.
|
|
683
|
+
* Looks in system/{moduleName}.yaml and domain.yaml at project root.
|
|
684
|
+
*/
|
|
685
|
+
async function copyDomainYaml(parentDir, newProjectDir, moduleName) {
|
|
686
|
+
// Try system/{moduleName}.yaml first (multi-module system layout)
|
|
687
|
+
const systemYaml = path.join(parentDir, 'system', `${moduleName}.yaml`);
|
|
688
|
+
if (await fs.pathExists(systemYaml)) {
|
|
689
|
+
await fs.ensureDir(path.join(newProjectDir, 'system'));
|
|
690
|
+
await fs.copy(systemYaml, path.join(newProjectDir, 'system', `${moduleName}.yaml`));
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// Fallback: domain.yaml at project root (single-module layout)
|
|
695
|
+
const domainYaml = path.join(parentDir, 'domain.yaml');
|
|
696
|
+
if (await fs.pathExists(domainYaml)) {
|
|
697
|
+
await fs.copy(domainYaml, path.join(newProjectDir, 'domain.yaml'));
|
|
657
698
|
}
|
|
658
699
|
}
|
|
659
700
|
|
|
@@ -9,6 +9,7 @@ const ejs = require('ejs');
|
|
|
9
9
|
const ora = require('ora');
|
|
10
10
|
|
|
11
11
|
const { validateSystem } = require('../utils/system-validator');
|
|
12
|
+
const { validateDomain } = require('../utils/domain-validator');
|
|
12
13
|
|
|
13
14
|
// ββ Module icon heuristic ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
14
15
|
|
|
@@ -208,7 +209,7 @@ function buildEventFlows(systemConfig, modulesMap) {
|
|
|
208
209
|
|
|
209
210
|
// ββ Data extraction ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
210
211
|
|
|
211
|
-
function extractReportData(systemConfig, validation) {
|
|
212
|
+
function extractReportData(systemConfig, validation, domainValidation) {
|
|
212
213
|
const modulesConfig = systemConfig.modules || [];
|
|
213
214
|
const asyncEvents = (systemConfig.integrations || {}).async || [];
|
|
214
215
|
const syncIntegrations = (systemConfig.integrations || {}).sync || [];
|
|
@@ -259,6 +260,7 @@ function extractReportData(systemConfig, validation) {
|
|
|
259
260
|
endpoints,
|
|
260
261
|
flows,
|
|
261
262
|
validation,
|
|
263
|
+
domainValidation,
|
|
262
264
|
generatedAt: new Date().toISOString(),
|
|
263
265
|
};
|
|
264
266
|
}
|
|
@@ -277,10 +279,11 @@ async function evaluateSystemCommand(type, options = {}) {
|
|
|
277
279
|
const outputPath = path.resolve(process.cwd(), options.output || './system-report.html');
|
|
278
280
|
|
|
279
281
|
// ββ 1. Read system.yaml βββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
280
|
-
const systemYamlPath = path.join(process.cwd(), 'system.yaml');
|
|
282
|
+
const systemYamlPath = path.join(process.cwd(), 'system', 'system.yaml');
|
|
281
283
|
if (!(await fs.pathExists(systemYamlPath))) {
|
|
282
|
-
console.error(chalk.red('β system.yaml not found
|
|
284
|
+
console.error(chalk.red('β system/system.yaml not found'));
|
|
283
285
|
console.error(chalk.gray('Run this command from the root of an eva4j project'));
|
|
286
|
+
console.error(chalk.gray('Expected location: system/system.yaml'));
|
|
284
287
|
process.exit(1);
|
|
285
288
|
}
|
|
286
289
|
|
|
@@ -289,17 +292,48 @@ async function evaluateSystemCommand(type, options = {}) {
|
|
|
289
292
|
const content = await fs.readFile(systemYamlPath, 'utf-8');
|
|
290
293
|
systemConfig = yaml.load(content);
|
|
291
294
|
} catch (err) {
|
|
292
|
-
console.error(chalk.red('β Failed to parse system.yaml:'), err.message);
|
|
295
|
+
console.error(chalk.red('β Failed to parse system/system.yaml:'), err.message);
|
|
293
296
|
process.exit(1);
|
|
294
297
|
}
|
|
295
298
|
|
|
296
|
-
const spinner = ora('Analyzing system.yaml...').start();
|
|
299
|
+
const spinner = ora('Analyzing system/system.yaml...').start();
|
|
297
300
|
|
|
298
|
-
// ββ
|
|
299
|
-
const
|
|
301
|
+
// ββ 2a. Load domain YAMLs (needed by both system and domain validation) ββ
|
|
302
|
+
const domainConfigs = {};
|
|
303
|
+
let domainValidation = null;
|
|
304
|
+
const systemDir = path.join(process.cwd(), 'system');
|
|
305
|
+
let allFiles;
|
|
306
|
+
try {
|
|
307
|
+
allFiles = await fs.readdir(systemDir);
|
|
308
|
+
} catch {
|
|
309
|
+
allFiles = [];
|
|
310
|
+
}
|
|
311
|
+
const domainFiles = allFiles.filter((f) => f.endsWith('.yaml') && f !== 'system.yaml');
|
|
312
|
+
|
|
313
|
+
if (domainFiles.length === 0) {
|
|
314
|
+
console.warn(chalk.yellow('β No domain YAML files found in system/ (excluding system.yaml). Domain tab will be hidden.'));
|
|
315
|
+
} else {
|
|
316
|
+
for (const file of domainFiles) {
|
|
317
|
+
const moduleName = path.basename(file, '.yaml');
|
|
318
|
+
try {
|
|
319
|
+
const content = await fs.readFile(path.join(systemDir, file), 'utf-8');
|
|
320
|
+
domainConfigs[moduleName] = yaml.load(content) || {};
|
|
321
|
+
} catch (err) {
|
|
322
|
+
console.warn(chalk.yellow(`β Could not parse ${file}: ${err.message}`));
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ββ 2b. Run system validation (receives domainConfigs to cross-check) βββ
|
|
328
|
+
const validation = validateSystem(systemConfig, domainConfigs);
|
|
329
|
+
|
|
330
|
+
// ββ 2c. Run domain validation βββββββββββββββββββββββββββββββββββββββββββ
|
|
331
|
+
if (Object.keys(domainConfigs).length > 0) {
|
|
332
|
+
domainValidation = validateDomain(domainConfigs, systemConfig);
|
|
333
|
+
}
|
|
300
334
|
|
|
301
335
|
// ββ 3. Extract report data ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
302
|
-
const reportData = extractReportData(systemConfig, validation);
|
|
336
|
+
const reportData = extractReportData(systemConfig, validation, domainValidation);
|
|
303
337
|
|
|
304
338
|
// ββ 4. Render HTML ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
305
339
|
const templatePath = path.join(__dirname, '../../templates/evaluate/report.html.ejs');
|
|
@@ -317,6 +351,16 @@ async function evaluateSystemCommand(type, options = {}) {
|
|
|
317
351
|
await fs.ensureDir(path.dirname(outputPath));
|
|
318
352
|
await fs.writeFile(outputPath, htmlContent, 'utf-8');
|
|
319
353
|
|
|
354
|
+
// ββ 5b. Write domain assets βββββββββββββββββββββββββββββββββββββββββββββββ
|
|
355
|
+
if (domainValidation) {
|
|
356
|
+
await writeDomainAssets(domainValidation, process.cwd());
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// ββ 5c. Write system-evaluation.md ββββββββββββββββββββββββββββββββββββββ
|
|
360
|
+
const evalMdPath = path.resolve(process.cwd(), 'assets', 'system-evaluation.md');
|
|
361
|
+
await fs.ensureDir(path.dirname(evalMdPath));
|
|
362
|
+
await writeSystemEvaluation(validation, systemConfig, evalMdPath);
|
|
363
|
+
|
|
320
364
|
spinner.succeed(chalk.green('Analysis complete!'));
|
|
321
365
|
|
|
322
366
|
// ββ 6. Print validation summary βββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -329,6 +373,9 @@ async function evaluateSystemCommand(type, options = {}) {
|
|
|
329
373
|
console.log(
|
|
330
374
|
` ${chalk.yellow('π‘ Warnings:')} ${chalk.yellow.bold(validation.warnings.length)}`
|
|
331
375
|
);
|
|
376
|
+
console.log(
|
|
377
|
+
` ${chalk.cyan('π΅ Info:')} ${chalk.cyan.bold((validation.info || []).length)}`
|
|
378
|
+
);
|
|
332
379
|
console.log(
|
|
333
380
|
` ${chalk.green('π’ Passed:')} ${chalk.green.bold(validation.ok.length)}`
|
|
334
381
|
);
|
|
@@ -349,6 +396,24 @@ async function evaluateSystemCommand(type, options = {}) {
|
|
|
349
396
|
console.log();
|
|
350
397
|
}
|
|
351
398
|
|
|
399
|
+
if ((validation.info || []).length > 0) {
|
|
400
|
+
console.log(chalk.cyan('Info:'));
|
|
401
|
+
validation.info.forEach((i) => console.log(chalk.cyan(` β’ ${i}`)));
|
|
402
|
+
console.log();
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ββ 6b. Print domain validation summary ββββββββββββββββββββββββββββββββ
|
|
406
|
+
if (domainValidation) {
|
|
407
|
+
const ds = domainValidation.summary;
|
|
408
|
+
console.log(chalk.bold('ποΈ Domain Validation Summary'));
|
|
409
|
+
console.log(chalk.gray('β'.repeat(40)));
|
|
410
|
+
console.log(` ${chalk.red('π΄ Errors:')} ${chalk.red.bold(ds.errors)}`);
|
|
411
|
+
console.log(` ${chalk.yellow('π‘ Warnings:')} ${chalk.yellow.bold(ds.warnings)}`);
|
|
412
|
+
console.log(` ${chalk.blue('π΅ Info:')} ${chalk.blue.bold(ds.info)}`);
|
|
413
|
+
console.log(` ${chalk.green('π’ OK:')} ${chalk.green.bold(ds.ok)}`);
|
|
414
|
+
console.log();
|
|
415
|
+
}
|
|
416
|
+
|
|
352
417
|
// ββ 7. Start HTTP server βββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
353
418
|
const server = http.createServer((req, res) => {
|
|
354
419
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
@@ -357,6 +422,7 @@ async function evaluateSystemCommand(type, options = {}) {
|
|
|
357
422
|
|
|
358
423
|
server.listen(port, () => {
|
|
359
424
|
console.log(chalk.gray(`Report written to: ${outputPath}`));
|
|
425
|
+
console.log(chalk.gray(`Evaluation written to: assets/system-evaluation.md`));
|
|
360
426
|
console.log();
|
|
361
427
|
console.log(chalk.bold.green(`π Server running at: http://localhost:${port}`));
|
|
362
428
|
console.log(chalk.gray('Open the URL in your browser to view the report'));
|
|
@@ -375,10 +441,170 @@ async function evaluateSystemCommand(type, options = {}) {
|
|
|
375
441
|
|
|
376
442
|
// ββ Helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
377
443
|
|
|
444
|
+
async function writeSystemEvaluation(validation, systemConfig, filePath) {
|
|
445
|
+
const systemName = (systemConfig.system || {}).name || 'eva4j system';
|
|
446
|
+
const now = new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
447
|
+
const scoreLabel = validation.score > 80 ? 'π’ Bueno' : validation.score > 60 ? 'π‘ Aceptable' : 'π΄ CrΓtico';
|
|
448
|
+
|
|
449
|
+
const lines = [];
|
|
450
|
+
|
|
451
|
+
lines.push(`# EvaluaciΓ³n del sistema β ${systemName}`);
|
|
452
|
+
lines.push('');
|
|
453
|
+
lines.push(`> Generado: ${now} `);
|
|
454
|
+
lines.push(`> Score de calidad: **${validation.score}%** ${scoreLabel} `);
|
|
455
|
+
lines.push(`> π΄ Errores: ${validation.errors.length} | π‘ Advertencias: ${validation.warnings.length}`);
|
|
456
|
+
lines.push('');
|
|
457
|
+
lines.push('---');
|
|
458
|
+
lines.push('');
|
|
459
|
+
|
|
460
|
+
if (validation.errors.length > 0) {
|
|
461
|
+
lines.push('## π΄ Errores crΓticos');
|
|
462
|
+
lines.push('');
|
|
463
|
+
for (const e of validation.errors) {
|
|
464
|
+
lines.push(`- ${e}`);
|
|
465
|
+
}
|
|
466
|
+
lines.push('');
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (validation.warnings.length > 0) {
|
|
470
|
+
lines.push('## π‘ Advertencias');
|
|
471
|
+
lines.push('');
|
|
472
|
+
for (const w of validation.warnings) {
|
|
473
|
+
lines.push(`- ${w}`);
|
|
474
|
+
}
|
|
475
|
+
lines.push('');
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (validation.errors.length === 0 && validation.warnings.length === 0) {
|
|
479
|
+
lines.push('## β
Sin errores ni advertencias');
|
|
480
|
+
lines.push('');
|
|
481
|
+
lines.push('El sistema supera todas las validaciones de errores y advertencias.');
|
|
482
|
+
lines.push('');
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
await fs.writeFile(filePath, lines.join('\n'), 'utf-8');
|
|
486
|
+
}
|
|
487
|
+
|
|
378
488
|
function toPascalCase(str) {
|
|
379
489
|
return str
|
|
380
490
|
.replace(/[-_ ]+(.)/g, (_, c) => c.toUpperCase())
|
|
381
491
|
.replace(/^(.)/, (c) => c.toUpperCase());
|
|
382
492
|
}
|
|
383
493
|
|
|
494
|
+
// ββ writeDomainAssets βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
495
|
+
async function writeDomainAssets(domainValidation, cwd) {
|
|
496
|
+
const assetsDir = path.join(cwd, 'assets', 'evaluation');
|
|
497
|
+
await fs.ensureDir(assetsDir);
|
|
498
|
+
|
|
499
|
+
const { categories, diagrams, summary } = domainValidation;
|
|
500
|
+
const now = new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
501
|
+
|
|
502
|
+
// ββ 1. Write per-module .mmd files ββββββββββββββββββββββββββββββββββββββ
|
|
503
|
+
if (diagrams) {
|
|
504
|
+
for (const [moduleName, diagramText] of Object.entries(diagrams)) {
|
|
505
|
+
if (diagramText) {
|
|
506
|
+
await fs.writeFile(path.join(assetsDir, `${moduleName}.mmd`), diagramText, 'utf-8');
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// ββ 2. Build evaluation.md ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
512
|
+
|
|
513
|
+
// Collect all findings grouped by module
|
|
514
|
+
const byModule = {};
|
|
515
|
+
for (const cat of categories) {
|
|
516
|
+
for (const check of cat.checks) {
|
|
517
|
+
for (const finding of check.findings) {
|
|
518
|
+
const mod = finding.module || '(sin mΓ³dulo)';
|
|
519
|
+
if (!byModule[mod]) byModule[mod] = [];
|
|
520
|
+
byModule[mod].push({
|
|
521
|
+
category: `${cat.id} β ${cat.label}`,
|
|
522
|
+
checkId: check.id,
|
|
523
|
+
checkLabel: check.label,
|
|
524
|
+
severity: check.severity,
|
|
525
|
+
message: finding.message,
|
|
526
|
+
context: finding.context || '',
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const SEV_EMOJI = { error: 'π΄', warning: 'π‘', info: 'π΅', ok: 'π’' };
|
|
533
|
+
|
|
534
|
+
const lines = [];
|
|
535
|
+
lines.push(`# Domain Evaluation Report`);
|
|
536
|
+
lines.push(`> Generated: ${now}`);
|
|
537
|
+
lines.push('');
|
|
538
|
+
|
|
539
|
+
// Summary table
|
|
540
|
+
lines.push(`## Summary`);
|
|
541
|
+
lines.push('');
|
|
542
|
+
lines.push(`| π΄ Errors | π‘ Warnings | π΅ Info | π’ OK |`);
|
|
543
|
+
lines.push(`|-----------|-------------|---------|-------|`);
|
|
544
|
+
lines.push(`| ${summary.errors} | ${summary.warnings} | ${summary.info} | ${summary.ok} |`);
|
|
545
|
+
lines.push('');
|
|
546
|
+
|
|
547
|
+
const moduleNames = Object.keys(byModule).sort();
|
|
548
|
+
|
|
549
|
+
if (moduleNames.length === 0) {
|
|
550
|
+
lines.push('_No findings detected across all modules._');
|
|
551
|
+
} else {
|
|
552
|
+
lines.push(`## Findings by Module`);
|
|
553
|
+
lines.push('');
|
|
554
|
+
|
|
555
|
+
for (const moduleName of moduleNames) {
|
|
556
|
+
const findings = byModule[moduleName];
|
|
557
|
+
const errorCount = findings.filter(f => f.severity === 'error').length;
|
|
558
|
+
const warningCount = findings.filter(f => f.severity === 'warning').length;
|
|
559
|
+
const infoCount = findings.filter(f => f.severity === 'info').length;
|
|
560
|
+
|
|
561
|
+
const badges = [
|
|
562
|
+
errorCount ? `π΄ ${errorCount} error${errorCount !== 1 ? 's' : ''}` : null,
|
|
563
|
+
warningCount ? `π‘ ${warningCount} warning${warningCount !== 1 ? 's' : ''}` : null,
|
|
564
|
+
infoCount ? `π΅ ${infoCount} info` : null,
|
|
565
|
+
].filter(Boolean).join(' Β· ');
|
|
566
|
+
|
|
567
|
+
lines.push(`### \`${moduleName}\`${badges ? ` <sub>${badges}</sub>` : ''}`);
|
|
568
|
+
lines.push('');
|
|
569
|
+
|
|
570
|
+
if (diagrams && diagrams[moduleName]) {
|
|
571
|
+
lines.push(`> π Diagram: [${moduleName}.mmd](./${moduleName}.mmd)`);
|
|
572
|
+
lines.push('');
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
lines.push(`| Severity | Check | Message | Context |`);
|
|
576
|
+
lines.push(`|----------|-------|---------|---------|`);
|
|
577
|
+
|
|
578
|
+
for (const f of findings) {
|
|
579
|
+
const sev = `${SEV_EMOJI[f.severity] || ''} ${f.severity}`;
|
|
580
|
+
const checkCell = `**${f.checkId}** ${f.checkLabel}`;
|
|
581
|
+
const msg = f.message.replace(/\|/g, '\\|');
|
|
582
|
+
const ctx = f.context.replace(/\|/g, '\\|');
|
|
583
|
+
lines.push(`| ${sev} | ${checkCell} | ${msg} | ${ctx} |`);
|
|
584
|
+
}
|
|
585
|
+
lines.push('');
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Modules with diagrams but no findings
|
|
590
|
+
if (diagrams) {
|
|
591
|
+
const cleanModules = Object.keys(diagrams)
|
|
592
|
+
.filter(m => diagrams[m] && !byModule[m])
|
|
593
|
+
.sort();
|
|
594
|
+
if (cleanModules.length > 0) {
|
|
595
|
+
lines.push(`## Clean Modules (no findings)`);
|
|
596
|
+
lines.push('');
|
|
597
|
+
for (const m of cleanModules) {
|
|
598
|
+
lines.push(`- \`${m}\` β π’ no findings Β· [${m}.mmd](./${m}.mmd)`);
|
|
599
|
+
}
|
|
600
|
+
lines.push('');
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
await fs.writeFile(path.join(assetsDir, 'evaluation.md'), lines.join('\n'), 'utf-8');
|
|
605
|
+
|
|
606
|
+
const mmdFiles = diagrams ? Object.keys(diagrams).filter(m => diagrams[m]) : [];
|
|
607
|
+
console.log(chalk.gray(` Domain assets β assets/evaluation/ (evaluation.md + ${mmdFiles.length} .mmd files)`));
|
|
608
|
+
}
|
|
609
|
+
|
|
384
610
|
module.exports = evaluateSystemCommand;
|