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,255 @@
|
|
|
1
|
+
"""Configuration management for CoreGen.
|
|
2
|
+
|
|
3
|
+
Supports configuration from multiple sources with priority:
|
|
4
|
+
1. CLI flags (highest priority)
|
|
5
|
+
2. Project-level coregen.yaml
|
|
6
|
+
3. Global ~/.coregenrc
|
|
7
|
+
4. Default values (lowest priority)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any, Optional
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
|
|
15
|
+
import yaml
|
|
16
|
+
from rich.console import Console
|
|
17
|
+
|
|
18
|
+
console = Console()
|
|
19
|
+
|
|
20
|
+
# Default configuration values
|
|
21
|
+
DEFAULTS = {
|
|
22
|
+
"feature_folder": "features", # Default features folder
|
|
23
|
+
"domain_folder": "domain", # Domain entities folder
|
|
24
|
+
"component_folder": "features/components", # Default components folder
|
|
25
|
+
"auto_run_build_runner": True,
|
|
26
|
+
"primary_color": "#2196F3",
|
|
27
|
+
"secondary_color": "#FF9800",
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# Config file names
|
|
31
|
+
PROJECT_CONFIG_FILE = "coregen.yaml"
|
|
32
|
+
GLOBAL_CONFIG_FILE = ".coregenrc"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class CoreGenConfig:
|
|
37
|
+
"""Configuration class for CoreGen."""
|
|
38
|
+
|
|
39
|
+
# Folder defaults
|
|
40
|
+
feature_folder: str = "features"
|
|
41
|
+
domain_folder: str = "domain"
|
|
42
|
+
component_folder: str = "features/components"
|
|
43
|
+
|
|
44
|
+
# Automation
|
|
45
|
+
auto_run_build_runner: bool = True
|
|
46
|
+
|
|
47
|
+
# UI/Styling
|
|
48
|
+
primary_color: str = "#2196F3"
|
|
49
|
+
secondary_color: str = "#FF9800"
|
|
50
|
+
|
|
51
|
+
# Custom templates (optional paths)
|
|
52
|
+
custom_templates: dict = field(default_factory=dict)
|
|
53
|
+
|
|
54
|
+
# Extra dependencies to add
|
|
55
|
+
extra_dependencies: list = field(default_factory=list)
|
|
56
|
+
|
|
57
|
+
# Source of config (for debugging)
|
|
58
|
+
_source: str = "defaults"
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def from_dict(cls, data: dict, source: str = "unknown") -> "CoreGenConfig":
|
|
62
|
+
"""Create config from dictionary."""
|
|
63
|
+
config = cls()
|
|
64
|
+
|
|
65
|
+
# Map nested 'defaults' section
|
|
66
|
+
if "defaults" in data:
|
|
67
|
+
defaults = data["defaults"]
|
|
68
|
+
if "feature_folder" in defaults:
|
|
69
|
+
config.feature_folder = defaults["feature_folder"]
|
|
70
|
+
if "domain_folder" in defaults:
|
|
71
|
+
config.domain_folder = defaults["domain_folder"]
|
|
72
|
+
if "component_folder" in defaults:
|
|
73
|
+
config.component_folder = defaults["component_folder"]
|
|
74
|
+
if "auto_run_build_runner" in defaults:
|
|
75
|
+
config.auto_run_build_runner = defaults["auto_run_build_runner"]
|
|
76
|
+
|
|
77
|
+
# Map 'styling' section
|
|
78
|
+
if "styling" in data:
|
|
79
|
+
styling = data["styling"]
|
|
80
|
+
if "primary_color" in styling:
|
|
81
|
+
config.primary_color = styling["primary_color"]
|
|
82
|
+
if "secondary_color" in styling:
|
|
83
|
+
config.secondary_color = styling["secondary_color"]
|
|
84
|
+
|
|
85
|
+
# Map 'templates' section
|
|
86
|
+
if "templates" in data:
|
|
87
|
+
config.custom_templates = data["templates"]
|
|
88
|
+
|
|
89
|
+
# Map 'dependencies' section
|
|
90
|
+
if "dependencies" in data:
|
|
91
|
+
config.extra_dependencies = data["dependencies"]
|
|
92
|
+
|
|
93
|
+
# Also support flat structure (for simple configs)
|
|
94
|
+
for key in ["feature_folder", "domain_folder", "component_folder",
|
|
95
|
+
"auto_run_build_runner",
|
|
96
|
+
"primary_color", "secondary_color"]:
|
|
97
|
+
if key in data:
|
|
98
|
+
setattr(config, key, data[key])
|
|
99
|
+
|
|
100
|
+
config._source = source
|
|
101
|
+
return config
|
|
102
|
+
|
|
103
|
+
def merge_with(self, other: "CoreGenConfig") -> "CoreGenConfig":
|
|
104
|
+
"""Merge this config with another, other takes precedence for non-default values."""
|
|
105
|
+
result = CoreGenConfig()
|
|
106
|
+
|
|
107
|
+
# Feature folder: use other's if set (default is "features")
|
|
108
|
+
result.feature_folder = other.feature_folder if other.feature_folder != "features" else self.feature_folder
|
|
109
|
+
|
|
110
|
+
# Component folder: use other's if set (default is "features/components")
|
|
111
|
+
result.component_folder = other.component_folder if other.component_folder != "features/components" else self.component_folder
|
|
112
|
+
|
|
113
|
+
# Domain folder has default "domain", so use other's if set
|
|
114
|
+
result.domain_folder = other.domain_folder if other.domain_folder != "domain" else self.domain_folder
|
|
115
|
+
|
|
116
|
+
# Automation: use other's value (it's explicitly set)
|
|
117
|
+
result.auto_run_build_runner = other.auto_run_build_runner
|
|
118
|
+
|
|
119
|
+
for key in ["primary_color", "secondary_color"]:
|
|
120
|
+
setattr(result, key, getattr(other, key))
|
|
121
|
+
|
|
122
|
+
# Merge dictionaries and lists
|
|
123
|
+
result.custom_templates = {**self.custom_templates, **other.custom_templates}
|
|
124
|
+
result.extra_dependencies = self.extra_dependencies + other.extra_dependencies
|
|
125
|
+
|
|
126
|
+
result._source = f"{self._source} + {other._source}"
|
|
127
|
+
return result
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def load_yaml_file(path: Path) -> Optional[dict]:
|
|
131
|
+
"""Load a YAML file, return None if not found or invalid."""
|
|
132
|
+
if not path.exists():
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
with open(path, 'r') as f:
|
|
137
|
+
return yaml.safe_load(f) or {}
|
|
138
|
+
except yaml.YAMLError as e:
|
|
139
|
+
console.print(f"[yellow]⚠️ Warning: Invalid YAML in {path}: {e}[/yellow]")
|
|
140
|
+
return None
|
|
141
|
+
except Exception as e:
|
|
142
|
+
console.print(f"[yellow]⚠️ Warning: Could not read {path}: {e}[/yellow]")
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def get_global_config_path() -> Path:
|
|
147
|
+
"""Get path to global config file."""
|
|
148
|
+
return Path.home() / GLOBAL_CONFIG_FILE
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def get_project_config_path(project_dir: Path) -> Path:
|
|
152
|
+
"""Get path to project config file."""
|
|
153
|
+
return project_dir / PROJECT_CONFIG_FILE
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def load_config(project_dir: Optional[Path] = None) -> CoreGenConfig:
|
|
157
|
+
"""
|
|
158
|
+
Load configuration from all sources with proper priority.
|
|
159
|
+
|
|
160
|
+
Priority (highest to lowest):
|
|
161
|
+
1. CLI flags (handled by caller)
|
|
162
|
+
2. Project-level coregen.yaml
|
|
163
|
+
3. Global ~/.coregenrc
|
|
164
|
+
4. Default values
|
|
165
|
+
"""
|
|
166
|
+
# Start with defaults
|
|
167
|
+
config = CoreGenConfig(_source="defaults")
|
|
168
|
+
|
|
169
|
+
# Load global config
|
|
170
|
+
global_path = get_global_config_path()
|
|
171
|
+
global_data = load_yaml_file(global_path)
|
|
172
|
+
if global_data:
|
|
173
|
+
global_config = CoreGenConfig.from_dict(global_data, f"~/{GLOBAL_CONFIG_FILE}")
|
|
174
|
+
config = config.merge_with(global_config)
|
|
175
|
+
|
|
176
|
+
# Load project config (if project_dir provided)
|
|
177
|
+
if project_dir:
|
|
178
|
+
project_path = get_project_config_path(project_dir)
|
|
179
|
+
project_data = load_yaml_file(project_path)
|
|
180
|
+
if project_data:
|
|
181
|
+
project_config = CoreGenConfig.from_dict(project_data, PROJECT_CONFIG_FILE)
|
|
182
|
+
config = config.merge_with(project_config)
|
|
183
|
+
|
|
184
|
+
return config
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def apply_cli_overrides(config: CoreGenConfig, **cli_args) -> CoreGenConfig:
|
|
188
|
+
"""
|
|
189
|
+
Apply CLI argument overrides to config.
|
|
190
|
+
Only non-None CLI args override config values.
|
|
191
|
+
"""
|
|
192
|
+
if cli_args.get("folder"):
|
|
193
|
+
config.feature_folder = cli_args["folder"]
|
|
194
|
+
config.component_folder = cli_args["folder"]
|
|
195
|
+
|
|
196
|
+
if cli_args.get("no_build") is True:
|
|
197
|
+
config.auto_run_build_runner = False
|
|
198
|
+
|
|
199
|
+
return config
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def create_default_config(project_dir: Path, project_name: str) -> Path:
|
|
203
|
+
"""Create a default coregen.yaml in the project directory."""
|
|
204
|
+
config_path = project_dir / PROJECT_CONFIG_FILE
|
|
205
|
+
|
|
206
|
+
default_config = f"""# CoreGen Configuration
|
|
207
|
+
# Project: {project_name}
|
|
208
|
+
# Documentation: https://github.com/lorenzo9598/coregen
|
|
209
|
+
|
|
210
|
+
# Default folders for generated code
|
|
211
|
+
defaults:
|
|
212
|
+
feature_folder: "features" # Features folder
|
|
213
|
+
domain_folder: "domain" # Domain entities folder (shared entities)
|
|
214
|
+
component_folder: "features/components" # Components folder
|
|
215
|
+
auto_run_build_runner: true # Run build_runner after generation
|
|
216
|
+
|
|
217
|
+
# UI/Styling configuration
|
|
218
|
+
styling:
|
|
219
|
+
primary_color: "#2196F3"
|
|
220
|
+
secondary_color: "#FF9800"
|
|
221
|
+
|
|
222
|
+
# Custom templates (optional)
|
|
223
|
+
# templates:
|
|
224
|
+
# entity: "templates/custom_entity.jinja"
|
|
225
|
+
# bloc: "templates/custom_bloc.jinja"
|
|
226
|
+
|
|
227
|
+
# Additional dependencies to include (optional)
|
|
228
|
+
# dependencies:
|
|
229
|
+
# - package: dartz
|
|
230
|
+
# version: ^0.10.1
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
with open(config_path, 'w') as f:
|
|
234
|
+
f.write(default_config)
|
|
235
|
+
|
|
236
|
+
return config_path
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def show_config(config: CoreGenConfig) -> None:
|
|
240
|
+
"""Display current configuration using rich."""
|
|
241
|
+
from rich.table import Table
|
|
242
|
+
from rich.panel import Panel
|
|
243
|
+
|
|
244
|
+
table = Table(show_header=True, header_style="bold cyan")
|
|
245
|
+
table.add_column("Setting", style="dim")
|
|
246
|
+
table.add_column("Value")
|
|
247
|
+
|
|
248
|
+
table.add_row("Feature Folder", config.feature_folder)
|
|
249
|
+
table.add_row("Domain Folder", config.domain_folder)
|
|
250
|
+
table.add_row("Component Folder", config.component_folder)
|
|
251
|
+
table.add_row("Auto Build Runner", "✅" if config.auto_run_build_runner else "❌")
|
|
252
|
+
table.add_row("Primary Color", config.primary_color)
|
|
253
|
+
table.add_row("Secondary Color", config.secondary_color)
|
|
254
|
+
|
|
255
|
+
console.print(Panel(table, title=f"⚙️ Configuration ({config._source})", border_style="blue"))
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""Mock vs remote data source helpers — DataSourceConfig and mock JSON assets."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from generators.templates.copier import generate_file
|
|
11
|
+
|
|
12
|
+
REMOTE_PATTERN = re.compile(
|
|
13
|
+
r"['\"](?P<key>[a-z][a-z0-9_]*)['\"]\s*:\s*DataSource\.remote",
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def scan_domain_entity_keys(lib_path: Path, domain_folder: str = "domain") -> list[str]:
|
|
18
|
+
"""Return snake_case entity keys that have a repository interface."""
|
|
19
|
+
domain_root = lib_path / domain_folder
|
|
20
|
+
if not domain_root.is_dir():
|
|
21
|
+
return []
|
|
22
|
+
keys: list[str] = []
|
|
23
|
+
for entity_dir in sorted(domain_root.iterdir()):
|
|
24
|
+
if not entity_dir.is_dir():
|
|
25
|
+
continue
|
|
26
|
+
i_repo = entity_dir / "model" / f"i_{entity_dir.name}_repository.dart"
|
|
27
|
+
if i_repo.is_file():
|
|
28
|
+
keys.append(entity_dir.name)
|
|
29
|
+
return keys
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def read_preserved_remote_keys(config_path: Path) -> set[str]:
|
|
33
|
+
"""Parse existing data_source_config.dart for entries already set to remote."""
|
|
34
|
+
if not config_path.is_file():
|
|
35
|
+
return set()
|
|
36
|
+
content = config_path.read_text(encoding="utf-8")
|
|
37
|
+
return {m.group("key") for m in REMOTE_PATTERN.finditer(content)}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def build_entities_map(
|
|
41
|
+
entity_keys: list[str],
|
|
42
|
+
*,
|
|
43
|
+
has_login: bool,
|
|
44
|
+
preserved_remote: set[str],
|
|
45
|
+
) -> dict[str, str]:
|
|
46
|
+
"""Build entity key -> 'mock' | 'remote' for template rendering."""
|
|
47
|
+
entities: dict[str, str] = {}
|
|
48
|
+
if has_login:
|
|
49
|
+
entities["auth"] = "remote" if "auth" in preserved_remote else "mock"
|
|
50
|
+
for key in entity_keys:
|
|
51
|
+
if key in preserved_remote:
|
|
52
|
+
entities[key] = "remote"
|
|
53
|
+
else:
|
|
54
|
+
entities[key] = "mock"
|
|
55
|
+
return entities
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _format_entities_entries(entities: dict[str, str]) -> str:
|
|
59
|
+
if not entities:
|
|
60
|
+
return ""
|
|
61
|
+
lines = []
|
|
62
|
+
for key, source in entities.items():
|
|
63
|
+
enum_val = "DataSource.mock" if source == "mock" else "DataSource.remote"
|
|
64
|
+
lines.append(f" '{key}': {enum_val},")
|
|
65
|
+
return "\n".join(lines)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def regenerate_data_source_config(
|
|
69
|
+
project_name: str,
|
|
70
|
+
lib_path: Path,
|
|
71
|
+
*,
|
|
72
|
+
domain_folder: str = "domain",
|
|
73
|
+
has_login: bool = False,
|
|
74
|
+
) -> None:
|
|
75
|
+
"""Regenerate lib/apis/common/data_source_config.dart from scanned entities."""
|
|
76
|
+
config_rel = "apis/common/data_source_config.dart"
|
|
77
|
+
config_path = lib_path / config_rel
|
|
78
|
+
entity_keys = scan_domain_entity_keys(lib_path, domain_folder)
|
|
79
|
+
preserved_remote = read_preserved_remote_keys(config_path)
|
|
80
|
+
entities = build_entities_map(
|
|
81
|
+
entity_keys,
|
|
82
|
+
has_login=has_login,
|
|
83
|
+
preserved_remote=preserved_remote,
|
|
84
|
+
)
|
|
85
|
+
generate_file(
|
|
86
|
+
project_name,
|
|
87
|
+
lib_path,
|
|
88
|
+
"apis/common/data_source_config_template.jinja",
|
|
89
|
+
config_rel,
|
|
90
|
+
{
|
|
91
|
+
"entities_entries": _format_entities_entries(entities),
|
|
92
|
+
"login": has_login,
|
|
93
|
+
},
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def ensure_mock_assets_dir(project_path: Path) -> Path:
|
|
98
|
+
"""Ensure assets/mock/ exists in the Flutter project root."""
|
|
99
|
+
mock_dir = project_path / "assets" / "mock"
|
|
100
|
+
mock_dir.mkdir(parents=True, exist_ok=True)
|
|
101
|
+
return mock_dir
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _sample_value_for_type(
|
|
105
|
+
field_name: str,
|
|
106
|
+
field_type: str,
|
|
107
|
+
index: int,
|
|
108
|
+
*,
|
|
109
|
+
known_enums: Optional[dict] = None,
|
|
110
|
+
) -> object:
|
|
111
|
+
"""Return a JSON-serializable sample value for a DTO field."""
|
|
112
|
+
known_enums = known_enums or {}
|
|
113
|
+
is_nullable = field_type.endswith("?")
|
|
114
|
+
base = field_type[:-1] if is_nullable else field_type
|
|
115
|
+
|
|
116
|
+
if field_name == "id":
|
|
117
|
+
return str(index)
|
|
118
|
+
|
|
119
|
+
base_lower = base.lower()
|
|
120
|
+
if base_lower in ("string", "str"):
|
|
121
|
+
return f"sample-{field_name}-{index}"
|
|
122
|
+
if base_lower == "bool":
|
|
123
|
+
return False
|
|
124
|
+
if base_lower == "int":
|
|
125
|
+
return index
|
|
126
|
+
if base_lower in ("double", "num", "float"):
|
|
127
|
+
return round(9.99 * index, 2)
|
|
128
|
+
if base_lower in ("datetime", "date"):
|
|
129
|
+
return "2024-01-15T10:00:00.000Z"
|
|
130
|
+
|
|
131
|
+
if base in known_enums:
|
|
132
|
+
values = known_enums[base].get("values") or []
|
|
133
|
+
if values:
|
|
134
|
+
return values[0]
|
|
135
|
+
return "pending"
|
|
136
|
+
|
|
137
|
+
if base.startswith("List<") or base.startswith("Set<"):
|
|
138
|
+
inner = base[base.index("<") + 1 : base.rindex(">")]
|
|
139
|
+
if inner in known_enums:
|
|
140
|
+
vals = known_enums[inner].get("values") or []
|
|
141
|
+
if vals:
|
|
142
|
+
return [vals[0]]
|
|
143
|
+
return []
|
|
144
|
+
|
|
145
|
+
if base.startswith("Map<"):
|
|
146
|
+
return {}
|
|
147
|
+
|
|
148
|
+
if base[0].isupper() if base else False:
|
|
149
|
+
return f"sample-{field_name}-{index}"
|
|
150
|
+
|
|
151
|
+
return f"sample-{field_name}-{index}"
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def generate_mock_json(
|
|
155
|
+
project_path: Path,
|
|
156
|
+
entity_folder_name: str,
|
|
157
|
+
field_list: list[dict],
|
|
158
|
+
*,
|
|
159
|
+
known_enums: Optional[dict] = None,
|
|
160
|
+
item_count: int = 3,
|
|
161
|
+
) -> Path:
|
|
162
|
+
"""Write assets/mock/<entity>.json with sample items."""
|
|
163
|
+
mock_dir = ensure_mock_assets_dir(project_path)
|
|
164
|
+
items = []
|
|
165
|
+
for i in range(1, item_count + 1):
|
|
166
|
+
item: dict[str, object] = {}
|
|
167
|
+
for field in field_list:
|
|
168
|
+
item[field["name"]] = _sample_value_for_type(
|
|
169
|
+
field["name"],
|
|
170
|
+
field["type"],
|
|
171
|
+
i,
|
|
172
|
+
known_enums=known_enums,
|
|
173
|
+
)
|
|
174
|
+
items.append(item)
|
|
175
|
+
out_path = mock_dir / f"{entity_folder_name}.json"
|
|
176
|
+
out_path.write_text(
|
|
177
|
+
json.dumps({"items": items}, indent=2) + "\n",
|
|
178
|
+
encoding="utf-8",
|
|
179
|
+
)
|
|
180
|
+
return out_path
|