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.
Files changed (172) hide show
  1. coregen.py +1585 -0
  2. coregen_cli-3.1.6.dist-info/METADATA +1211 -0
  3. coregen_cli-3.1.6.dist-info/RECORD +172 -0
  4. coregen_cli-3.1.6.dist-info/WHEEL +5 -0
  5. coregen_cli-3.1.6.dist-info/entry_points.txt +2 -0
  6. coregen_cli-3.1.6.dist-info/top_level.txt +2 -0
  7. generators/__init__.py +1 -0
  8. generators/assets/__init__.py +1 -0
  9. generators/assets/assets.py +30 -0
  10. generators/assets/main.py +4 -0
  11. generators/config/__init__.py +1 -0
  12. generators/config/analisy_options.py +42 -0
  13. generators/config/main.py +11 -0
  14. generators/config/pubspec.py +139 -0
  15. generators/cursor/__init__.py +3 -0
  16. generators/cursor/setup.py +87 -0
  17. generators/helpers/__init__.py +56 -0
  18. generators/helpers/component.py +1030 -0
  19. generators/helpers/config.py +255 -0
  20. generators/helpers/data_source.py +180 -0
  21. generators/helpers/domain.py +469 -0
  22. generators/helpers/feature.py +654 -0
  23. generators/helpers/navigation.py +274 -0
  24. generators/helpers/page.py +63 -0
  25. generators/helpers/project.py +33 -0
  26. generators/helpers/utils.py +338 -0
  27. generators/helpers/validation.py +364 -0
  28. generators/initializator.py +68 -0
  29. generators/main.py +81 -0
  30. generators/static/assets/logo.png +0 -0
  31. generators/static/assets/mock/auth_user.json +5 -0
  32. generators/static/assets/svgs/apple.svg +4 -0
  33. generators/static/assets/svgs/google.svg +2 -0
  34. generators/static/cursor/AGENTS.md.jinja +37 -0
  35. generators/static/cursor/agents/doc-writer.md +20 -0
  36. generators/static/cursor/agents/epic-orchestrator.md +28 -0
  37. generators/static/cursor/agents/feature-implementer.md +29 -0
  38. generators/static/cursor/agents/integration-wiring.md +30 -0
  39. generators/static/cursor/agents/layer-application.md +21 -0
  40. generators/static/cursor/agents/layer-guardian.md +21 -0
  41. generators/static/cursor/agents/layer-infrastructure.md +31 -0
  42. generators/static/cursor/agents/layer-model.md +24 -0
  43. generators/static/cursor/agents/layer-presentation.md +33 -0
  44. generators/static/cursor/docs/architecture/APIS_AND_INTEGRATION.md.jinja +42 -0
  45. generators/static/cursor/docs/architecture/CARAVAGGIO_COMPONENTS.md.jinja +317 -0
  46. generators/static/cursor/docs/architecture/DDD_LAYERS.md.jinja +32 -0
  47. generators/static/cursor/docs/architecture/FILE_TEMPLATES.md.jinja +76 -0
  48. generators/static/cursor/docs/architecture/MOCK_AND_REMOTE_DATA.md.jinja +87 -0
  49. generators/static/cursor/docs/architecture/REFERENCE_IMPLEMENTATIONS.md.jinja +48 -0
  50. generators/static/cursor/docs/architecture/WIDGETS_AND_CARAVAGGIO.md.jinja +76 -0
  51. generators/static/cursor/docs/epics/README.md +17 -0
  52. generators/static/cursor/lib/domain/AGENTS.md +23 -0
  53. generators/static/cursor/lib/features/AGENTS.md +23 -0
  54. generators/static/cursor/lib/widgets/AGENTS.md +21 -0
  55. generators/static/cursor/rules/apis-layer.mdc.jinja +21 -0
  56. generators/static/cursor/rules/application-layer.mdc.jinja +12 -0
  57. generators/static/cursor/rules/architecture-core.mdc.jinja +43 -0
  58. generators/static/cursor/rules/dart-conventions.mdc.jinja +14 -0
  59. generators/static/cursor/rules/domain-layer.mdc.jinja +24 -0
  60. generators/static/cursor/rules/login-context.mdc.jinja +16 -0
  61. generators/static/cursor/rules/presentation-layer.mdc.jinja +15 -0
  62. generators/static/cursor/rules/quality-gate.mdc.jinja +14 -0
  63. generators/static/cursor/rules/ui-caravaggio.mdc.jinja +22 -0
  64. generators/static/cursor/skills/caravaggio-ui/SKILL.md +138 -0
  65. generators/static/cursor/skills/epic-delivery/SKILL.md +74 -0
  66. generators/static/cursor/skills/epic-delivery/templates/epic-status.md +14 -0
  67. generators/static/cursor/skills/epic-delivery/templates/epic.md +36 -0
  68. generators/static/templates/apis/common/constants_template.jinja +48 -0
  69. generators/static/templates/apis/common/data_source_config_template.jinja +47 -0
  70. generators/static/templates/apis/core/api_injectable_module_template.jinja +14 -0
  71. generators/static/templates/apis/interceptors/api_logger_template.jinja +135 -0
  72. generators/static/templates/apis/interceptors/auth_interceptor_template.jinja +207 -0
  73. generators/static/templates/auth/application/auth_bloc_template.jinja +36 -0
  74. generators/static/templates/auth/application/auth_event_template.jinja +7 -0
  75. generators/static/templates/auth/application/auth_state_template.jinja +8 -0
  76. generators/static/templates/auth/infrastructure/auth_facade_module_template.jinja +16 -0
  77. generators/static/templates/auth/infrastructure/auth_facade_template.jinja +288 -0
  78. generators/static/templates/auth/infrastructure/mock_auth_facade_template.jinja +106 -0
  79. generators/static/templates/auth/model/auth_failure_template.jinja +11 -0
  80. generators/static/templates/auth/model/i_auth_facade_template.jinja +24 -0
  81. generators/static/templates/auth/model/user_template.jinja +34 -0
  82. generators/static/templates/auth/model/value_objects_template.jinja +54 -0
  83. generators/static/templates/auth/presentation/login_screen_template.jinja +24 -0
  84. generators/static/templates/auth/sign_in_form/application/sign_in_form_bloc_template.jinja +139 -0
  85. generators/static/templates/auth/sign_in_form/application/sign_in_form_event_template.jinja +15 -0
  86. generators/static/templates/auth/sign_in_form/application/sign_in_form_state_template.jinja +26 -0
  87. generators/static/templates/auth/sign_in_form/presentation/apple_form_template.jinja +16 -0
  88. generators/static/templates/auth/sign_in_form/presentation/email_password_form_template_INACTIVE.jinja +94 -0
  89. generators/static/templates/auth/sign_in_form/presentation/google_form_template.jinja +15 -0
  90. generators/static/templates/auth/sign_in_form/presentation/sign_in_form_template.jinja +381 -0
  91. generators/static/templates/component/component_bloc_template.jinja +49 -0
  92. generators/static/templates/component/component_event_template.jinja +7 -0
  93. generators/static/templates/component/component_form_bloc_template.jinja +56 -0
  94. generators/static/templates/component/component_form_event_template.jinja +7 -0
  95. generators/static/templates/component/component_form_state_template.jinja +18 -0
  96. generators/static/templates/component/component_form_widget_template.jinja +130 -0
  97. generators/static/templates/component/component_list_widget_template.jinja +95 -0
  98. generators/static/templates/component/component_state_template.jinja +9 -0
  99. generators/static/templates/component/component_widget_template.jinja +113 -0
  100. generators/static/templates/core/bloc/base_form_bloc_template.jinja +32 -0
  101. generators/static/templates/core/errors/error_localizer_template.jinja +61 -0
  102. generators/static/templates/core/infrastructure/base_mapper_template.jinja +21 -0
  103. generators/static/templates/core/infrastructure/base_repository_mixin_template.jinja +78 -0
  104. generators/static/templates/core/infrastructure/firebase_injectable_module_template.jinja +11 -0
  105. generators/static/templates/core/infrastructure/firestore_helpers_template.jinja +22 -0
  106. generators/static/templates/core/infrastructure/repository_error_handler_template.jinja +44 -0
  107. generators/static/templates/core/model/common_interfaces_template.jinja +3 -0
  108. generators/static/templates/core/model/entity_template.jinja +5 -0
  109. generators/static/templates/core/model/errors_template.jinja +15 -0
  110. generators/static/templates/core/model/failures_template.jinja +97 -0
  111. generators/static/templates/core/model/value_objects_template.jinja +135 -0
  112. generators/static/templates/core/model/value_validators_template.jinja +105 -0
  113. generators/static/templates/core/presentation/app_drawer_template.jinja +52 -0
  114. generators/static/templates/core/presentation/app_widget_template.jinja +41 -0
  115. generators/static/templates/core/presentation/bottom_nav_bar_template.jinja +32 -0
  116. generators/static/templates/domain/domain_mapper_template.jinja +39 -0
  117. generators/static/templates/domain/domain_remote_service_template.jinja +46 -0
  118. generators/static/templates/domain/domain_repository_template.jinja +90 -0
  119. generators/static/templates/domain/domain_service_module_template.jinja +23 -0
  120. generators/static/templates/domain/enum_template.jinja +3 -0
  121. generators/static/templates/domain/i_domain_service_template.jinja +18 -0
  122. generators/static/templates/domain/mock_domain_service_template.jinja +73 -0
  123. generators/static/templates/feature/feature_bloc_template.jinja +104 -0
  124. generators/static/templates/feature/feature_dto_template.jinja +26 -0
  125. generators/static/templates/feature/feature_entity_template.jinja +23 -0
  126. generators/static/templates/feature/feature_event_template.jinja +20 -0
  127. generators/static/templates/feature/feature_extensions_template.jinja +25 -0
  128. generators/static/templates/feature/feature_failure_template.jinja +11 -0
  129. generators/static/templates/feature/feature_page_template.jinja +113 -0
  130. generators/static/templates/feature/feature_repository_template.jinja +112 -0
  131. generators/static/templates/feature/feature_state_template.jinja +20 -0
  132. generators/static/templates/feature/i_feature_repository_template.jinja +21 -0
  133. generators/static/templates/feature/value_object_field_template.jinja +0 -0
  134. generators/static/templates/feature/value_object_template.jinja +0 -0
  135. generators/static/templates/feature/value_validators_template.jinja +57 -0
  136. generators/static/templates/home/home_screen_template.jinja +68 -0
  137. generators/static/templates/home/presentation/home_screen_template.jinja +52 -0
  138. generators/static/templates/home/screen_template.jinja +12 -0
  139. generators/static/templates/injection_template.jinja +8 -0
  140. generators/static/templates/logging/analytics_logging_template.jinja +42 -0
  141. generators/static/templates/logging/console_template.jinja +50 -0
  142. generators/static/templates/logging/logger_injectable_module_template.jinja +24 -0
  143. generators/static/templates/logging/logger_template.jinja +118 -0
  144. generators/static/templates/main_template.jinja +43 -0
  145. generators/static/templates/page_template.jinja +29 -0
  146. generators/static/templates/router_template.jinja +26 -0
  147. generators/static/templates/splash/presentation/splash_screen_auth_template.jinja +97 -0
  148. generators/static/templates/splash/presentation/splash_screen_template.jinja +51 -0
  149. generators/static/templates/storage/storage_repository_template.jinja +80 -0
  150. generators/static/templates/user_profile/infrastructure/user_profile_dto_template.jinja +27 -0
  151. generators/static/templates/user_profile/infrastructure/user_profile_mapper_template.jinja +43 -0
  152. generators/static/templates/user_profile/infrastructure/user_profile_repository_template.jinja +82 -0
  153. generators/static/templates/user_profile/infrastructure/user_profile_service_template.jinja +27 -0
  154. generators/static/templates/user_profile/model/i_user_profile_repository_template.jinja +34 -0
  155. generators/static/templates/user_profile/model/user_profile_failure_template.jinja +11 -0
  156. generators/static/templates/user_profile/model/user_profile_template.jinja +31 -0
  157. generators/static/templates/widgets/common/custom_scaffold_template.jinja +52 -0
  158. generators/static/templates/widgets/common/error_widget_template.jinja +22 -0
  159. generators/static/templates/widgets/common/loading_widget_template.jinja +14 -0
  160. generators/static/templates/widgets/common/unknown_state_widget_template.jinja +23 -0
  161. generators/templates/__init__.py +1 -0
  162. generators/templates/_core/core_generator.py +121 -0
  163. generators/templates/apis/apis_generator.py +50 -0
  164. generators/templates/auth/auth_generator.py +40 -0
  165. generators/templates/auth/sign_in_form_generator.py +22 -0
  166. generators/templates/copier.py +51 -0
  167. generators/templates/home/home_generator.py +26 -0
  168. generators/templates/lib/lib_generator.py +40 -0
  169. generators/templates/logging/logging_generator.py +28 -0
  170. generators/templates/main.py +75 -0
  171. generators/templates/splash/splash_generator.py +20 -0
  172. 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
+ })