coregen-cli 3.1.6__py3-none-any.whl
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.
- coregen.py +1585 -0
- coregen_cli-3.1.6.dist-info/METADATA +1211 -0
- coregen_cli-3.1.6.dist-info/RECORD +172 -0
- coregen_cli-3.1.6.dist-info/WHEEL +5 -0
- coregen_cli-3.1.6.dist-info/entry_points.txt +2 -0
- coregen_cli-3.1.6.dist-info/top_level.txt +2 -0
- generators/__init__.py +1 -0
- generators/assets/__init__.py +1 -0
- generators/assets/assets.py +30 -0
- generators/assets/main.py +4 -0
- generators/config/__init__.py +1 -0
- generators/config/analisy_options.py +42 -0
- generators/config/main.py +11 -0
- generators/config/pubspec.py +139 -0
- generators/cursor/__init__.py +3 -0
- generators/cursor/setup.py +87 -0
- generators/helpers/__init__.py +56 -0
- generators/helpers/component.py +1030 -0
- generators/helpers/config.py +255 -0
- generators/helpers/data_source.py +180 -0
- generators/helpers/domain.py +469 -0
- generators/helpers/feature.py +654 -0
- generators/helpers/navigation.py +274 -0
- generators/helpers/page.py +63 -0
- generators/helpers/project.py +33 -0
- generators/helpers/utils.py +338 -0
- generators/helpers/validation.py +364 -0
- generators/initializator.py +68 -0
- generators/main.py +81 -0
- generators/static/assets/logo.png +0 -0
- generators/static/assets/mock/auth_user.json +5 -0
- generators/static/assets/svgs/apple.svg +4 -0
- generators/static/assets/svgs/google.svg +2 -0
- generators/static/cursor/AGENTS.md.jinja +37 -0
- generators/static/cursor/agents/doc-writer.md +20 -0
- generators/static/cursor/agents/epic-orchestrator.md +28 -0
- generators/static/cursor/agents/feature-implementer.md +29 -0
- generators/static/cursor/agents/integration-wiring.md +30 -0
- generators/static/cursor/agents/layer-application.md +21 -0
- generators/static/cursor/agents/layer-guardian.md +21 -0
- generators/static/cursor/agents/layer-infrastructure.md +31 -0
- generators/static/cursor/agents/layer-model.md +24 -0
- generators/static/cursor/agents/layer-presentation.md +33 -0
- generators/static/cursor/docs/architecture/APIS_AND_INTEGRATION.md.jinja +42 -0
- generators/static/cursor/docs/architecture/CARAVAGGIO_COMPONENTS.md.jinja +317 -0
- generators/static/cursor/docs/architecture/DDD_LAYERS.md.jinja +32 -0
- generators/static/cursor/docs/architecture/FILE_TEMPLATES.md.jinja +76 -0
- generators/static/cursor/docs/architecture/MOCK_AND_REMOTE_DATA.md.jinja +87 -0
- generators/static/cursor/docs/architecture/REFERENCE_IMPLEMENTATIONS.md.jinja +48 -0
- generators/static/cursor/docs/architecture/WIDGETS_AND_CARAVAGGIO.md.jinja +76 -0
- generators/static/cursor/docs/epics/README.md +17 -0
- generators/static/cursor/lib/domain/AGENTS.md +23 -0
- generators/static/cursor/lib/features/AGENTS.md +23 -0
- generators/static/cursor/lib/widgets/AGENTS.md +21 -0
- generators/static/cursor/rules/apis-layer.mdc.jinja +21 -0
- generators/static/cursor/rules/application-layer.mdc.jinja +12 -0
- generators/static/cursor/rules/architecture-core.mdc.jinja +43 -0
- generators/static/cursor/rules/dart-conventions.mdc.jinja +14 -0
- generators/static/cursor/rules/domain-layer.mdc.jinja +24 -0
- generators/static/cursor/rules/login-context.mdc.jinja +16 -0
- generators/static/cursor/rules/presentation-layer.mdc.jinja +15 -0
- generators/static/cursor/rules/quality-gate.mdc.jinja +14 -0
- generators/static/cursor/rules/ui-caravaggio.mdc.jinja +22 -0
- generators/static/cursor/skills/caravaggio-ui/SKILL.md +138 -0
- generators/static/cursor/skills/epic-delivery/SKILL.md +74 -0
- generators/static/cursor/skills/epic-delivery/templates/epic-status.md +14 -0
- generators/static/cursor/skills/epic-delivery/templates/epic.md +36 -0
- generators/static/templates/apis/common/constants_template.jinja +48 -0
- generators/static/templates/apis/common/data_source_config_template.jinja +47 -0
- generators/static/templates/apis/core/api_injectable_module_template.jinja +14 -0
- generators/static/templates/apis/interceptors/api_logger_template.jinja +135 -0
- generators/static/templates/apis/interceptors/auth_interceptor_template.jinja +207 -0
- generators/static/templates/auth/application/auth_bloc_template.jinja +36 -0
- generators/static/templates/auth/application/auth_event_template.jinja +7 -0
- generators/static/templates/auth/application/auth_state_template.jinja +8 -0
- generators/static/templates/auth/infrastructure/auth_facade_module_template.jinja +16 -0
- generators/static/templates/auth/infrastructure/auth_facade_template.jinja +288 -0
- generators/static/templates/auth/infrastructure/mock_auth_facade_template.jinja +106 -0
- generators/static/templates/auth/model/auth_failure_template.jinja +11 -0
- generators/static/templates/auth/model/i_auth_facade_template.jinja +24 -0
- generators/static/templates/auth/model/user_template.jinja +34 -0
- generators/static/templates/auth/model/value_objects_template.jinja +54 -0
- generators/static/templates/auth/presentation/login_screen_template.jinja +24 -0
- generators/static/templates/auth/sign_in_form/application/sign_in_form_bloc_template.jinja +139 -0
- generators/static/templates/auth/sign_in_form/application/sign_in_form_event_template.jinja +15 -0
- generators/static/templates/auth/sign_in_form/application/sign_in_form_state_template.jinja +26 -0
- generators/static/templates/auth/sign_in_form/presentation/apple_form_template.jinja +16 -0
- generators/static/templates/auth/sign_in_form/presentation/email_password_form_template_INACTIVE.jinja +94 -0
- generators/static/templates/auth/sign_in_form/presentation/google_form_template.jinja +15 -0
- generators/static/templates/auth/sign_in_form/presentation/sign_in_form_template.jinja +381 -0
- generators/static/templates/component/component_bloc_template.jinja +49 -0
- generators/static/templates/component/component_event_template.jinja +7 -0
- generators/static/templates/component/component_form_bloc_template.jinja +56 -0
- generators/static/templates/component/component_form_event_template.jinja +7 -0
- generators/static/templates/component/component_form_state_template.jinja +18 -0
- generators/static/templates/component/component_form_widget_template.jinja +130 -0
- generators/static/templates/component/component_list_widget_template.jinja +95 -0
- generators/static/templates/component/component_state_template.jinja +9 -0
- generators/static/templates/component/component_widget_template.jinja +113 -0
- generators/static/templates/core/bloc/base_form_bloc_template.jinja +32 -0
- generators/static/templates/core/errors/error_localizer_template.jinja +61 -0
- generators/static/templates/core/infrastructure/base_mapper_template.jinja +21 -0
- generators/static/templates/core/infrastructure/base_repository_mixin_template.jinja +78 -0
- generators/static/templates/core/infrastructure/firebase_injectable_module_template.jinja +11 -0
- generators/static/templates/core/infrastructure/firestore_helpers_template.jinja +22 -0
- generators/static/templates/core/infrastructure/repository_error_handler_template.jinja +44 -0
- generators/static/templates/core/model/common_interfaces_template.jinja +3 -0
- generators/static/templates/core/model/entity_template.jinja +5 -0
- generators/static/templates/core/model/errors_template.jinja +15 -0
- generators/static/templates/core/model/failures_template.jinja +97 -0
- generators/static/templates/core/model/value_objects_template.jinja +135 -0
- generators/static/templates/core/model/value_validators_template.jinja +105 -0
- generators/static/templates/core/presentation/app_drawer_template.jinja +52 -0
- generators/static/templates/core/presentation/app_widget_template.jinja +41 -0
- generators/static/templates/core/presentation/bottom_nav_bar_template.jinja +32 -0
- generators/static/templates/domain/domain_mapper_template.jinja +39 -0
- generators/static/templates/domain/domain_remote_service_template.jinja +46 -0
- generators/static/templates/domain/domain_repository_template.jinja +90 -0
- generators/static/templates/domain/domain_service_module_template.jinja +23 -0
- generators/static/templates/domain/enum_template.jinja +3 -0
- generators/static/templates/domain/i_domain_service_template.jinja +18 -0
- generators/static/templates/domain/mock_domain_service_template.jinja +73 -0
- generators/static/templates/feature/feature_bloc_template.jinja +104 -0
- generators/static/templates/feature/feature_dto_template.jinja +26 -0
- generators/static/templates/feature/feature_entity_template.jinja +23 -0
- generators/static/templates/feature/feature_event_template.jinja +20 -0
- generators/static/templates/feature/feature_extensions_template.jinja +25 -0
- generators/static/templates/feature/feature_failure_template.jinja +11 -0
- generators/static/templates/feature/feature_page_template.jinja +113 -0
- generators/static/templates/feature/feature_repository_template.jinja +112 -0
- generators/static/templates/feature/feature_state_template.jinja +20 -0
- generators/static/templates/feature/i_feature_repository_template.jinja +21 -0
- generators/static/templates/feature/value_object_field_template.jinja +0 -0
- generators/static/templates/feature/value_object_template.jinja +0 -0
- generators/static/templates/feature/value_validators_template.jinja +57 -0
- generators/static/templates/home/home_screen_template.jinja +68 -0
- generators/static/templates/home/presentation/home_screen_template.jinja +52 -0
- generators/static/templates/home/screen_template.jinja +12 -0
- generators/static/templates/injection_template.jinja +8 -0
- generators/static/templates/logging/analytics_logging_template.jinja +42 -0
- generators/static/templates/logging/console_template.jinja +50 -0
- generators/static/templates/logging/logger_injectable_module_template.jinja +24 -0
- generators/static/templates/logging/logger_template.jinja +118 -0
- generators/static/templates/main_template.jinja +43 -0
- generators/static/templates/page_template.jinja +29 -0
- generators/static/templates/router_template.jinja +26 -0
- generators/static/templates/splash/presentation/splash_screen_auth_template.jinja +97 -0
- generators/static/templates/splash/presentation/splash_screen_template.jinja +51 -0
- generators/static/templates/storage/storage_repository_template.jinja +80 -0
- generators/static/templates/user_profile/infrastructure/user_profile_dto_template.jinja +27 -0
- generators/static/templates/user_profile/infrastructure/user_profile_mapper_template.jinja +43 -0
- generators/static/templates/user_profile/infrastructure/user_profile_repository_template.jinja +82 -0
- generators/static/templates/user_profile/infrastructure/user_profile_service_template.jinja +27 -0
- generators/static/templates/user_profile/model/i_user_profile_repository_template.jinja +34 -0
- generators/static/templates/user_profile/model/user_profile_failure_template.jinja +11 -0
- generators/static/templates/user_profile/model/user_profile_template.jinja +31 -0
- generators/static/templates/widgets/common/custom_scaffold_template.jinja +52 -0
- generators/static/templates/widgets/common/error_widget_template.jinja +22 -0
- generators/static/templates/widgets/common/loading_widget_template.jinja +14 -0
- generators/static/templates/widgets/common/unknown_state_widget_template.jinja +23 -0
- generators/templates/__init__.py +1 -0
- generators/templates/_core/core_generator.py +121 -0
- generators/templates/apis/apis_generator.py +50 -0
- generators/templates/auth/auth_generator.py +40 -0
- generators/templates/auth/sign_in_form_generator.py +22 -0
- generators/templates/copier.py +51 -0
- generators/templates/home/home_generator.py +26 -0
- generators/templates/lib/lib_generator.py +40 -0
- generators/templates/logging/logging_generator.py +28 -0
- generators/templates/main.py +75 -0
- generators/templates/splash/splash_generator.py +20 -0
- generators/templates/storage/storage_generator.py +5 -0
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
"""Domain entity generation functions
|
|
2
|
+
|
|
3
|
+
Domain entities are shared business entities that can be used by multiple features.
|
|
4
|
+
They only contain model and infrastructure layers (no application/presentation).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional
|
|
9
|
+
from generators.templates.copier import generate_file
|
|
10
|
+
import re
|
|
11
|
+
from .utils import map_field_type, map_field_type_to_dto, to_pascal_case_preserve, pascal_case_to_kebab_case, pascal_case_to_camel_case, PRIMITIVE_TYPES, KNOWN_VALUE_OBJECTS
|
|
12
|
+
from .validation import parse_field_type
|
|
13
|
+
from .feature import (
|
|
14
|
+
generate_value_objects_and_validators,
|
|
15
|
+
find_domain_models_with_class_names,
|
|
16
|
+
get_domain_model_class_name,
|
|
17
|
+
find_enums_with_info,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def create_domain_entity_layers(
|
|
22
|
+
domain_dir: Path,
|
|
23
|
+
entity_folder_name: str,
|
|
24
|
+
entity_class_name: str,
|
|
25
|
+
field_list: list[dict],
|
|
26
|
+
project_name: str,
|
|
27
|
+
folder: Optional[str],
|
|
28
|
+
*,
|
|
29
|
+
no_repo: bool = False,
|
|
30
|
+
) -> None:
|
|
31
|
+
"""Create model and infrastructure layers for a domain entity
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
domain_dir: Path to domain entity directory
|
|
35
|
+
entity_folder_name: Folder name (snake_case, e.g., "note_item")
|
|
36
|
+
entity_class_name: Class name (PascalCase, e.g., "NoteItem")
|
|
37
|
+
field_list: List of field dictionaries
|
|
38
|
+
project_name: Name of the project
|
|
39
|
+
folder: Domain folder path
|
|
40
|
+
no_repo: If True, skip repository interface, Retrofit service, and repository impl
|
|
41
|
+
(entity, DTO, and mapper only — e.g. nested types for deserialization).
|
|
42
|
+
|
|
43
|
+
Domain entities do NOT include application or presentation layers.
|
|
44
|
+
They are meant to be shared across multiple features.
|
|
45
|
+
"""
|
|
46
|
+
# Build the import path prefix (use folder name for paths)
|
|
47
|
+
if folder:
|
|
48
|
+
import_prefix = f"{folder.replace('/', '/')}/{entity_folder_name}"
|
|
49
|
+
else:
|
|
50
|
+
import_prefix = entity_folder_name
|
|
51
|
+
|
|
52
|
+
# Discover domain models and enums early (needed by VO generation and entity building)
|
|
53
|
+
lib_path = domain_dir.parent.parent if folder else domain_dir.parent
|
|
54
|
+
domain_folder = folder if folder else "domain"
|
|
55
|
+
models_with_classes = find_domain_models_with_class_names(lib_path, domain_folder) if lib_path.exists() else {}
|
|
56
|
+
enums_info = find_enums_with_info(lib_path, domain_folder) if lib_path.exists() else {}
|
|
57
|
+
known_enums = set(enums_info.keys())
|
|
58
|
+
|
|
59
|
+
def _is_enum(t: str) -> bool:
|
|
60
|
+
return t in known_enums
|
|
61
|
+
|
|
62
|
+
# Model layer
|
|
63
|
+
model_dir = domain_dir / "model"
|
|
64
|
+
model_dir.mkdir(exist_ok=True)
|
|
65
|
+
|
|
66
|
+
# Create value objects and validators
|
|
67
|
+
generate_value_objects_and_validators(import_prefix, field_list, model_dir, project_name, known_enums=known_enums, skip_local_validators=True)
|
|
68
|
+
|
|
69
|
+
# Create entity (domain model)
|
|
70
|
+
entity_fields = []
|
|
71
|
+
needs_dartz = False
|
|
72
|
+
|
|
73
|
+
for field in field_list:
|
|
74
|
+
field_name = field['name']
|
|
75
|
+
field_type = field['type']
|
|
76
|
+
is_nullable = field_type.endswith('?')
|
|
77
|
+
base_type_raw = field_type[:-1] if is_nullable else field_type
|
|
78
|
+
|
|
79
|
+
if field_name == 'id':
|
|
80
|
+
entity_fields.append(f" required UniqueId id,")
|
|
81
|
+
elif is_nullable and _is_enum(base_type_raw):
|
|
82
|
+
needs_dartz = True
|
|
83
|
+
entity_fields.append(f" required Option<{base_type_raw}> {field_name},")
|
|
84
|
+
elif is_nullable:
|
|
85
|
+
coll_root, _, _ = parse_field_type(base_type_raw)
|
|
86
|
+
if coll_root.lower() in ('list', 'set', 'map'):
|
|
87
|
+
entity_fields.append(f" {base_type_raw}? {field_name},")
|
|
88
|
+
continue
|
|
89
|
+
needs_dartz = True
|
|
90
|
+
if base_type_raw in PRIMITIVE_TYPES:
|
|
91
|
+
entity_fields.append(f" required Option<{to_pascal_case_preserve(field_name)}> {field_name},")
|
|
92
|
+
elif base_type_raw in KNOWN_VALUE_OBJECTS:
|
|
93
|
+
entity_fields.append(f" required Option<{base_type_raw}> {field_name},")
|
|
94
|
+
else:
|
|
95
|
+
entity_fields.append(f" required Option<{base_type_raw}> {field_name},")
|
|
96
|
+
elif _is_enum(field_type):
|
|
97
|
+
entity_fields.append(f" required {field_type} {field_name},")
|
|
98
|
+
elif '<' in field_type:
|
|
99
|
+
entity_fields.append(f" required {field_type} {field_name},")
|
|
100
|
+
elif field_type in KNOWN_VALUE_OBJECTS:
|
|
101
|
+
entity_fields.append(f" required {field_type} {field_name},")
|
|
102
|
+
elif field_type in PRIMITIVE_TYPES:
|
|
103
|
+
entity_fields.append(f" required {to_pascal_case_preserve(field_name)} {field_name},")
|
|
104
|
+
else:
|
|
105
|
+
entity_fields.append(f" required {field_type} {field_name},")
|
|
106
|
+
|
|
107
|
+
entity_import = f"import 'package:{project_name}/{import_prefix}/model/value_objects.dart';"
|
|
108
|
+
|
|
109
|
+
referenced_imports = []
|
|
110
|
+
if needs_dartz:
|
|
111
|
+
referenced_imports.append("import 'package:dartz/dartz.dart';")
|
|
112
|
+
|
|
113
|
+
def _add_enum_import(enum_name: str) -> None:
|
|
114
|
+
"""Add an import for an enum if it exists in the project."""
|
|
115
|
+
info = enums_info.get(enum_name)
|
|
116
|
+
if info:
|
|
117
|
+
ref_import = f"import 'package:{project_name}/{info['folder']}/{info['file_stem']}.dart';"
|
|
118
|
+
if ref_import not in referenced_imports:
|
|
119
|
+
referenced_imports.append(ref_import)
|
|
120
|
+
|
|
121
|
+
def _add_model_import(class_name: str) -> None:
|
|
122
|
+
"""Add an import for a domain model if it exists in the project."""
|
|
123
|
+
for file_stem, model_info in models_with_classes.items():
|
|
124
|
+
if model_info['class_name'] == class_name:
|
|
125
|
+
ref_import = f"import 'package:{project_name}/{domain_folder}/{model_info['folder']}/model/{file_stem}.dart';"
|
|
126
|
+
if ref_import not in referenced_imports:
|
|
127
|
+
referenced_imports.append(ref_import)
|
|
128
|
+
break
|
|
129
|
+
|
|
130
|
+
for field in field_list:
|
|
131
|
+
field_type = field['type']
|
|
132
|
+
# Strip nullable suffix for import resolution
|
|
133
|
+
ft = field_type[:-1] if field_type.endswith('?') else field_type
|
|
134
|
+
if '<' in ft:
|
|
135
|
+
base_type, gp1, gp2 = parse_field_type(ft)
|
|
136
|
+
for gp in (gp1, gp2):
|
|
137
|
+
if gp and gp[0].isupper() and gp not in PRIMITIVE_TYPES and gp not in KNOWN_VALUE_OBJECTS:
|
|
138
|
+
if _is_enum(gp):
|
|
139
|
+
_add_enum_import(gp)
|
|
140
|
+
else:
|
|
141
|
+
_add_model_import(gp)
|
|
142
|
+
elif ft and ft[0].isupper() and ft not in PRIMITIVE_TYPES and ft not in KNOWN_VALUE_OBJECTS:
|
|
143
|
+
if _is_enum(ft):
|
|
144
|
+
_add_enum_import(ft)
|
|
145
|
+
else:
|
|
146
|
+
_add_model_import(ft)
|
|
147
|
+
|
|
148
|
+
all_entity_imports = entity_import
|
|
149
|
+
if referenced_imports:
|
|
150
|
+
all_entity_imports = entity_import + "\n" + "\n".join(referenced_imports)
|
|
151
|
+
|
|
152
|
+
entity_fields_str = "\n".join(entity_fields)
|
|
153
|
+
generate_file(project_name, model_dir, "feature/feature_entity_template.jinja", f"{entity_folder_name}.dart", {
|
|
154
|
+
"feature_name": entity_class_name, # Use class name for template (PascalCase) - for class name
|
|
155
|
+
"file_name": entity_folder_name, # Use folder name for file references (snake_case) - for part statements
|
|
156
|
+
"fields": entity_fields_str,
|
|
157
|
+
"entity_import": all_entity_imports
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
# Create failure
|
|
161
|
+
generate_file(project_name, model_dir, "feature/feature_failure_template.jinja", f"{entity_folder_name}_failure.dart", {
|
|
162
|
+
"feature_name": entity_class_name, # Use class name for template (PascalCase)
|
|
163
|
+
"file_name": entity_folder_name # Use folder name for file references (snake_case)
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
# Infrastructure layer
|
|
167
|
+
infra_dir = domain_dir / "infrastructure"
|
|
168
|
+
infra_dir.mkdir(exist_ok=True)
|
|
169
|
+
|
|
170
|
+
# Create DTO (convert types to DTO format - List<Model> -> List<ModelDto>, Model -> ModelDto, Enum -> String)
|
|
171
|
+
dto_fields = ",\n".join([f" required {map_field_type_to_dto(field['type'], known_enums=known_enums)} {field['name']}" for field in field_list])
|
|
172
|
+
|
|
173
|
+
# Find referenced DTOs and add imports
|
|
174
|
+
referenced_dto_imports = []
|
|
175
|
+
|
|
176
|
+
for field in field_list:
|
|
177
|
+
field_type = field['type']
|
|
178
|
+
dto_field_type = map_field_type_to_dto(field_type, known_enums=known_enums)
|
|
179
|
+
|
|
180
|
+
if 'Dto' in dto_field_type:
|
|
181
|
+
for model_match in re.finditer(r'(\w+)Dto', dto_field_type):
|
|
182
|
+
model_name = model_match.group(1)
|
|
183
|
+
for file_stem, model_info in models_with_classes.items():
|
|
184
|
+
if model_info['class_name'] == model_name:
|
|
185
|
+
ref_import = f"import 'package:{project_name}/{domain_folder}/{model_info['folder']}/infrastructure/{file_stem}_dto.dart';"
|
|
186
|
+
if ref_import not in referenced_dto_imports:
|
|
187
|
+
referenced_dto_imports.append(ref_import)
|
|
188
|
+
break
|
|
189
|
+
|
|
190
|
+
# Combine DTO imports
|
|
191
|
+
dto_imports = "\n".join(referenced_dto_imports) if referenced_dto_imports else ""
|
|
192
|
+
|
|
193
|
+
generate_file(project_name, infra_dir, "feature/feature_dto_template.jinja", f"{entity_folder_name}_dto.dart", {
|
|
194
|
+
"feature_name": entity_class_name, # Use class name for template (PascalCase)
|
|
195
|
+
"file_name": entity_folder_name, # Use folder name for file references (snake_case)
|
|
196
|
+
"fields": dto_fields,
|
|
197
|
+
"dto_imports": dto_imports
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
if not no_repo:
|
|
201
|
+
# Create repository interface
|
|
202
|
+
i_repo_import = f"""import 'package:{project_name}/{import_prefix}/model/{entity_folder_name}.dart';
|
|
203
|
+
import 'package:{project_name}/{import_prefix}/model/{entity_folder_name}_failure.dart';
|
|
204
|
+
"""
|
|
205
|
+
generate_file(project_name, model_dir, "feature/i_feature_repository_template.jinja", f"i_{entity_folder_name}_repository.dart", {
|
|
206
|
+
"feature_name": entity_class_name,
|
|
207
|
+
"i_repo_import": i_repo_import
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
# Generate mapper fields for DTO-Domain conversions
|
|
211
|
+
from_dto_fields = []
|
|
212
|
+
to_dto_fields = []
|
|
213
|
+
mapper_dependencies = [] # List of (mapper_class_name, mapper_var_name, import_path)
|
|
214
|
+
mapper_needs_dartz = False
|
|
215
|
+
|
|
216
|
+
def _find_mapper_dep(class_name: str):
|
|
217
|
+
"""Find and register a mapper dependency for a domain model, return (mapper_var_name, found)."""
|
|
218
|
+
for file_stem, model_info in models_with_classes.items():
|
|
219
|
+
if model_info['class_name'] == class_name:
|
|
220
|
+
mcn = f"{model_info['class_name']}Mapper"
|
|
221
|
+
mvn = f"_{pascal_case_to_camel_case(model_info['class_name'])}Mapper"
|
|
222
|
+
mi = f"package:{project_name}/{domain_folder}/{model_info['folder']}/infrastructure/{file_stem}_mapper.dart"
|
|
223
|
+
if not any(m[0] == mcn for m in mapper_dependencies):
|
|
224
|
+
mapper_dependencies.append((mcn, mvn, mi))
|
|
225
|
+
return mvn, True
|
|
226
|
+
return None, False
|
|
227
|
+
|
|
228
|
+
def _is_domain_model_local(t: str) -> bool:
|
|
229
|
+
return bool(t and t[0].isupper() and t not in PRIMITIVE_TYPES and t not in KNOWN_VALUE_OBJECTS and t not in known_enums)
|
|
230
|
+
|
|
231
|
+
mapper_enum_imports = []
|
|
232
|
+
|
|
233
|
+
for field in field_list:
|
|
234
|
+
field_name = field['name']
|
|
235
|
+
field_type = field['type']
|
|
236
|
+
capitalized_name = to_pascal_case_preserve(field_name)
|
|
237
|
+
is_nullable = field_type.endswith('?')
|
|
238
|
+
base_type_raw = field_type[:-1] if is_nullable else field_type
|
|
239
|
+
|
|
240
|
+
if field_name == 'id':
|
|
241
|
+
from_dto_fields.append(f" id: UniqueId.fromUniqueString(dto.id)")
|
|
242
|
+
to_dto_fields.append(f" id: entity.id.getOrCrash()")
|
|
243
|
+
continue
|
|
244
|
+
|
|
245
|
+
# --- Nullable enum (EnumName?) ---
|
|
246
|
+
if is_nullable and _is_enum(base_type_raw):
|
|
247
|
+
mapper_needs_dartz = True
|
|
248
|
+
from_dto_fields.append(f" {field_name}: dto.{field_name} != null ? some({base_type_raw}.values.byName(dto.{field_name}!)) : none()")
|
|
249
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}.fold(() => null, ({base_type_raw} v) => v.name)")
|
|
250
|
+
info = enums_info.get(base_type_raw)
|
|
251
|
+
if info:
|
|
252
|
+
ei = f"import 'package:{project_name}/{info['folder']}/{info['file_stem']}.dart';"
|
|
253
|
+
if ei not in mapper_enum_imports:
|
|
254
|
+
mapper_enum_imports.append(ei)
|
|
255
|
+
continue
|
|
256
|
+
|
|
257
|
+
# --- Nullable Map / List / Set (Dart T?, not Option) ---
|
|
258
|
+
coll_root, cg1, cg2 = parse_field_type(base_type_raw)
|
|
259
|
+
if is_nullable and coll_root.lower() == 'map' and cg1 and cg2:
|
|
260
|
+
if _is_enum(cg2):
|
|
261
|
+
from_dto_fields.append(
|
|
262
|
+
f" {field_name}: dto.{field_name} != null ? dto.{field_name}!.map((k, v) => MapEntry(k, {cg2}.values.byName(v))) : null"
|
|
263
|
+
)
|
|
264
|
+
to_dto_fields.append(
|
|
265
|
+
f" {field_name}: entity.{field_name} != null ? entity.{field_name}!.map((k, v) => MapEntry(k, v.name)) : null"
|
|
266
|
+
)
|
|
267
|
+
info = enums_info.get(cg2)
|
|
268
|
+
if info:
|
|
269
|
+
ei = f"import 'package:{project_name}/{info['folder']}/{info['file_stem']}.dart';"
|
|
270
|
+
if ei not in mapper_enum_imports:
|
|
271
|
+
mapper_enum_imports.append(ei)
|
|
272
|
+
elif _is_domain_model_local(cg2):
|
|
273
|
+
mvn, found = _find_mapper_dep(cg2)
|
|
274
|
+
if found:
|
|
275
|
+
from_dto_fields.append(
|
|
276
|
+
f" {field_name}: dto.{field_name} != null ? dto.{field_name}!.map((k, v) => MapEntry(k, {mvn}.toDomain(v))) : null"
|
|
277
|
+
)
|
|
278
|
+
to_dto_fields.append(
|
|
279
|
+
f" {field_name}: entity.{field_name} != null ? entity.{field_name}!.map((k, v) => MapEntry(k, {mvn}.toDto(v))) : null"
|
|
280
|
+
)
|
|
281
|
+
else:
|
|
282
|
+
from_dto_fields.append(f" {field_name}: dto.{field_name}")
|
|
283
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}")
|
|
284
|
+
else:
|
|
285
|
+
from_dto_fields.append(f" {field_name}: dto.{field_name}")
|
|
286
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}")
|
|
287
|
+
continue
|
|
288
|
+
|
|
289
|
+
if is_nullable and coll_root.lower() in ('list', 'set') and cg1:
|
|
290
|
+
if _is_enum(cg1):
|
|
291
|
+
from_dto_fields.append(
|
|
292
|
+
f" {field_name}: dto.{field_name} != null ? dto.{field_name}!.map((e) => {cg1}.values.byName(e)).toList() : null"
|
|
293
|
+
)
|
|
294
|
+
to_dto_fields.append(
|
|
295
|
+
f" {field_name}: entity.{field_name} != null ? entity.{field_name}!.map((e) => e.name).toList() : null"
|
|
296
|
+
)
|
|
297
|
+
info = enums_info.get(cg1)
|
|
298
|
+
if info:
|
|
299
|
+
ei = f"import 'package:{project_name}/{info['folder']}/{info['file_stem']}.dart';"
|
|
300
|
+
if ei not in mapper_enum_imports:
|
|
301
|
+
mapper_enum_imports.append(ei)
|
|
302
|
+
elif _is_domain_model_local(cg1):
|
|
303
|
+
mvn, found = _find_mapper_dep(cg1)
|
|
304
|
+
if found:
|
|
305
|
+
from_dto_fields.append(
|
|
306
|
+
f" {field_name}: dto.{field_name} != null ? {mvn}.toDomainList(dto.{field_name}!) : null"
|
|
307
|
+
)
|
|
308
|
+
to_dto_fields.append(
|
|
309
|
+
f" {field_name}: entity.{field_name} != null ? {mvn}.toDtoList(entity.{field_name}!) : null"
|
|
310
|
+
)
|
|
311
|
+
else:
|
|
312
|
+
from_dto_fields.append(f" {field_name}: dto.{field_name}")
|
|
313
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}")
|
|
314
|
+
else:
|
|
315
|
+
from_dto_fields.append(f" {field_name}: dto.{field_name}")
|
|
316
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}")
|
|
317
|
+
continue
|
|
318
|
+
|
|
319
|
+
# --- Nullable types (Type?) ---
|
|
320
|
+
if is_nullable:
|
|
321
|
+
mapper_needs_dartz = True
|
|
322
|
+
if base_type_raw in PRIMITIVE_TYPES:
|
|
323
|
+
from_dto_fields.append(f" {field_name}: dto.{field_name} != null ? some({capitalized_name}(dto.{field_name}!)) : none()")
|
|
324
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}.fold(() => null, ({capitalized_name} v) => v.getOrCrash())")
|
|
325
|
+
elif base_type_raw in KNOWN_VALUE_OBJECTS:
|
|
326
|
+
from_dto_fields.append(f" {field_name}: dto.{field_name} != null ? some(UniqueId.fromUniqueString(dto.{field_name}!)) : none()")
|
|
327
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}.fold(() => null, (UniqueId v) => v.getOrCrash())")
|
|
328
|
+
elif _is_domain_model_local(base_type_raw):
|
|
329
|
+
mvn, found = _find_mapper_dep(base_type_raw)
|
|
330
|
+
if found:
|
|
331
|
+
from_dto_fields.append(f" {field_name}: dto.{field_name} != null ? some({mvn}.toDomain(dto.{field_name}!)) : none()")
|
|
332
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}.fold(() => null, ({base_type_raw} v) => {mvn}.toDto(v))")
|
|
333
|
+
else:
|
|
334
|
+
from_dto_fields.append(f" {field_name}: dto.{field_name} != null ? some(dto.{field_name}!) : none()")
|
|
335
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}.fold(() => null, ({base_type_raw} v) => v)")
|
|
336
|
+
continue
|
|
337
|
+
|
|
338
|
+
ft_parse = field_type[:-1] if field_type.endswith('?') else field_type
|
|
339
|
+
base_type, gp1, gp2 = parse_field_type(ft_parse)
|
|
340
|
+
|
|
341
|
+
# --- Enum (standalone, no generics) ---
|
|
342
|
+
if _is_enum(field_type):
|
|
343
|
+
from_dto_fields.append(f" {field_name}: {field_type}.values.byName(dto.{field_name})")
|
|
344
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}.name")
|
|
345
|
+
info = enums_info.get(field_type)
|
|
346
|
+
if info:
|
|
347
|
+
ei = f"import 'package:{project_name}/{info['folder']}/{info['file_stem']}.dart';"
|
|
348
|
+
if ei not in mapper_enum_imports:
|
|
349
|
+
mapper_enum_imports.append(ei)
|
|
350
|
+
|
|
351
|
+
# --- Map<K, V> ---
|
|
352
|
+
elif base_type == 'Map' and gp1 and gp2:
|
|
353
|
+
if _is_enum(gp2):
|
|
354
|
+
from_dto_fields.append(f" {field_name}: dto.{field_name}.map((k, v) => MapEntry(k, {gp2}.values.byName(v)))")
|
|
355
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}.map((k, v) => MapEntry(k, v.name))")
|
|
356
|
+
info = enums_info.get(gp2)
|
|
357
|
+
if info:
|
|
358
|
+
ei = f"import 'package:{project_name}/{info['folder']}/{info['file_stem']}.dart';"
|
|
359
|
+
if ei not in mapper_enum_imports:
|
|
360
|
+
mapper_enum_imports.append(ei)
|
|
361
|
+
elif _is_domain_model_local(gp2):
|
|
362
|
+
mvn, found = _find_mapper_dep(gp2)
|
|
363
|
+
if found:
|
|
364
|
+
from_dto_fields.append(f" {field_name}: dto.{field_name}.map((k, v) => MapEntry(k, {mvn}.toDomain(v)))")
|
|
365
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}.map((k, v) => MapEntry(k, {mvn}.toDto(v)))")
|
|
366
|
+
else:
|
|
367
|
+
from_dto_fields.append(f" {field_name}: dto.{field_name}")
|
|
368
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}")
|
|
369
|
+
else:
|
|
370
|
+
from_dto_fields.append(f" {field_name}: dto.{field_name}")
|
|
371
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}")
|
|
372
|
+
|
|
373
|
+
# --- List<T>, Set<T> ---
|
|
374
|
+
elif '<' in ft_parse and gp1:
|
|
375
|
+
if _is_enum(gp1):
|
|
376
|
+
from_dto_fields.append(f" {field_name}: dto.{field_name}.map((e) => {gp1}.values.byName(e)).toList()")
|
|
377
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}.map((e) => e.name).toList()")
|
|
378
|
+
info = enums_info.get(gp1)
|
|
379
|
+
if info:
|
|
380
|
+
ei = f"import 'package:{project_name}/{info['folder']}/{info['file_stem']}.dart';"
|
|
381
|
+
if ei not in mapper_enum_imports:
|
|
382
|
+
mapper_enum_imports.append(ei)
|
|
383
|
+
elif _is_domain_model_local(gp1):
|
|
384
|
+
mvn, found = _find_mapper_dep(gp1)
|
|
385
|
+
if found:
|
|
386
|
+
from_dto_fields.append(f" {field_name}: {mvn}.toDomainList(dto.{field_name})")
|
|
387
|
+
to_dto_fields.append(f" {field_name}: {mvn}.toDtoList(entity.{field_name})")
|
|
388
|
+
else:
|
|
389
|
+
from_dto_fields.append(f" {field_name}: dto.{field_name}")
|
|
390
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}")
|
|
391
|
+
else:
|
|
392
|
+
from_dto_fields.append(f" {field_name}: dto.{field_name}")
|
|
393
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}")
|
|
394
|
+
|
|
395
|
+
# --- UniqueId ---
|
|
396
|
+
elif field_type in KNOWN_VALUE_OBJECTS:
|
|
397
|
+
from_dto_fields.append(f" {field_name}: UniqueId.fromUniqueString(dto.{field_name})")
|
|
398
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}.getOrCrash()")
|
|
399
|
+
|
|
400
|
+
# --- Domain model (standalone, no generics) ---
|
|
401
|
+
elif _is_domain_model_local(field_type):
|
|
402
|
+
mvn, found = _find_mapper_dep(field_type)
|
|
403
|
+
if found:
|
|
404
|
+
from_dto_fields.append(f" {field_name}: {mvn}.toDomain(dto.{field_name})")
|
|
405
|
+
to_dto_fields.append(f" {field_name}: {mvn}.toDto(entity.{field_name})")
|
|
406
|
+
else:
|
|
407
|
+
from_dto_fields.append(f" {field_name}: dto.{field_name}")
|
|
408
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}")
|
|
409
|
+
|
|
410
|
+
# --- Primitive type -> ValueObject ---
|
|
411
|
+
else:
|
|
412
|
+
from_dto_fields.append(f" {field_name}: {capitalized_name}(dto.{field_name})")
|
|
413
|
+
to_dto_fields.append(f" {field_name}: entity.{field_name}.getOrCrash()")
|
|
414
|
+
|
|
415
|
+
from_dto_fields_str = ",\n".join(from_dto_fields)
|
|
416
|
+
to_dto_fields_str = ",\n".join(to_dto_fields)
|
|
417
|
+
|
|
418
|
+
if not no_repo:
|
|
419
|
+
# Create service interface, remote, mock, and DI module
|
|
420
|
+
service_ctx = {
|
|
421
|
+
"entity_name": entity_class_name,
|
|
422
|
+
"file_name": entity_folder_name,
|
|
423
|
+
"entity_name_camel": pascal_case_to_camel_case(entity_class_name),
|
|
424
|
+
"kebab_name": pascal_case_to_kebab_case(entity_class_name),
|
|
425
|
+
"import_prefix": import_prefix,
|
|
426
|
+
}
|
|
427
|
+
generate_file(project_name, infra_dir, "domain/i_domain_service_template.jinja", f"i_{entity_folder_name}_service.dart", service_ctx)
|
|
428
|
+
generate_file(project_name, infra_dir, "domain/domain_remote_service_template.jinja", f"{entity_folder_name}_remote_service.dart", service_ctx)
|
|
429
|
+
generate_file(project_name, infra_dir, "domain/mock_domain_service_template.jinja", f"mock_{entity_folder_name}_service.dart", service_ctx)
|
|
430
|
+
generate_file(project_name, infra_dir, "domain/domain_service_module_template.jinja", f"{entity_folder_name}_service_module.dart", service_ctx)
|
|
431
|
+
|
|
432
|
+
# Create Mapper
|
|
433
|
+
# Build mapper imports and constructor parameters
|
|
434
|
+
extra_mapper_imports = []
|
|
435
|
+
if mapper_needs_dartz:
|
|
436
|
+
extra_mapper_imports.append("import 'package:dartz/dartz.dart';")
|
|
437
|
+
dep_imports = [f"import '{imp}';" for _, _, imp in mapper_dependencies]
|
|
438
|
+
mapper_imports = "\n".join(extra_mapper_imports + mapper_enum_imports + dep_imports)
|
|
439
|
+
if mapper_dependencies:
|
|
440
|
+
mapper_constructor_params = ",\n".join([f" this.{var_name}" for _, var_name, _ in mapper_dependencies])
|
|
441
|
+
else:
|
|
442
|
+
mapper_constructor_params = ""
|
|
443
|
+
mapper_fields = "\n".join([f" final {class_name} {var_name};" for class_name, var_name, _ in mapper_dependencies])
|
|
444
|
+
|
|
445
|
+
generate_file(project_name, infra_dir, "domain/domain_mapper_template.jinja", f"{entity_folder_name}_mapper.dart", {
|
|
446
|
+
"entity_name": entity_class_name, # Use class name for template (PascalCase)
|
|
447
|
+
"file_name": entity_folder_name, # Use folder name for file references (snake_case)
|
|
448
|
+
"import_prefix": import_prefix,
|
|
449
|
+
"from_dto_fields": from_dto_fields_str,
|
|
450
|
+
"to_dto_fields": to_dto_fields_str,
|
|
451
|
+
"mapper_imports": mapper_imports,
|
|
452
|
+
"mapper_fields": mapper_fields,
|
|
453
|
+
"mapper_constructor_params": mapper_constructor_params
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
if not no_repo:
|
|
457
|
+
# Create Repository (uses Service + Mapper)
|
|
458
|
+
repo_import = f"""import 'package:{project_name}/{import_prefix}/model/{entity_folder_name}.dart';
|
|
459
|
+
import 'package:{project_name}/{import_prefix}/model/{entity_folder_name}_failure.dart';
|
|
460
|
+
import 'package:{project_name}/{import_prefix}/model/i_{entity_folder_name}_repository.dart';
|
|
461
|
+
import 'package:{project_name}/{import_prefix}/infrastructure/i_{entity_folder_name}_service.dart';
|
|
462
|
+
import 'package:{project_name}/{import_prefix}/infrastructure/{entity_folder_name}_mapper.dart';
|
|
463
|
+
"""
|
|
464
|
+
generate_file(project_name, infra_dir, "domain/domain_repository_template.jinja", f"{entity_folder_name}_repository.dart", {
|
|
465
|
+
"entity_name": entity_class_name, # Use class name for template (PascalCase)
|
|
466
|
+
"file_name": entity_folder_name, # Use folder name for file references (snake_case)
|
|
467
|
+
"import_prefix": import_prefix,
|
|
468
|
+
"repo_import": repo_import
|
|
469
|
+
})
|