@artilingo/artiframe-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/assets/logo.png +0 -0
  2. package/assets/logo.svg +22 -0
  3. package/assets/logo.webp +0 -0
  4. package/assets/logo@0.5x.png +0 -0
  5. package/assets/logo@0.75x.png +0 -0
  6. package/assets/logo@1.5x.png +0 -0
  7. package/assets/logo@2x.png +0 -0
  8. package/assets/logo@3x.png +0 -0
  9. package/assets/logo@4x.png +0 -0
  10. package/bin/artiframe.js +57 -0
  11. package/bin/artiframe.php +45 -0
  12. package/core-stubs/app/ApiControl.php +81 -0
  13. package/core-stubs/app/Database.php +69 -0
  14. package/core-stubs/app/DotEnv.php +63 -0
  15. package/core-stubs/app/R2Manager.php +130 -0
  16. package/core-stubs/app/ViewControl.php +24 -0
  17. package/core-stubs/bin/SystemMethod.php +271 -0
  18. package/core-stubs/bin/ViewMethod.php +354 -0
  19. package/core-stubs/docs/de.html +951 -0
  20. package/core-stubs/docs/en.html +952 -0
  21. package/core-stubs/docs/es.html +947 -0
  22. package/core-stubs/docs/fr.html +850 -0
  23. package/core-stubs/docs/tr.html +951 -0
  24. package/core-stubs/public/assets/css/components/footer.css +0 -0
  25. package/core-stubs/public/assets/css/components/header.css +0 -0
  26. package/core-stubs/public/assets/css/components/mobilenav.css +0 -0
  27. package/core-stubs/public/assets/css/components/sidebar.css +0 -0
  28. package/core-stubs/public/assets/css/components/theme-modal.css +0 -0
  29. package/core-stubs/public/assets/css/root/app.css +61 -0
  30. package/core-stubs/public/assets/js/components/header.js +0 -0
  31. package/core-stubs/public/assets/js/components/mobilenav.js +0 -0
  32. package/core-stubs/public/assets/js/components/sidebar.js +0 -0
  33. package/core-stubs/public/assets/js/components/theme-modal.js +0 -0
  34. package/core-stubs/public/assets/js/root/app.js +30 -0
  35. package/core-stubs/public/includes/footer.php +5 -0
  36. package/core-stubs/public/includes/head.php +26 -0
  37. package/core-stubs/public/includes/header.php +5 -0
  38. package/core-stubs/public/includes/mobilenav.php +5 -0
  39. package/core-stubs/public/includes/sidebar.php +5 -0
  40. package/core-stubs/public/includes/theme-modal.php +5 -0
  41. package/core-stubs/readme/de.md +29 -0
  42. package/core-stubs/readme/en.md +29 -0
  43. package/core-stubs/readme/es.md +29 -0
  44. package/core-stubs/readme/fr.md +29 -0
  45. package/core-stubs/readme/tr.md +29 -0
  46. package/package.json +49 -0
  47. package/scripts/postinstall.js +21 -0
  48. package/src/App.php +266 -0
  49. package/src/Commands/MakeApiCommand.php +71 -0
  50. package/src/Commands/MakeClassCommand.php +88 -0
  51. package/src/Commands/MakeViewCommand.php +95 -0
  52. package/src/Commands/NewProjectCommand.php +633 -0
  53. package/src/Commands/VersionCommand.php +92 -0
  54. package/src/Lang/de.php +53 -0
  55. package/src/Lang/en.php +53 -0
  56. package/src/Lang/es.php +53 -0
  57. package/src/Lang/fr.php +53 -0
  58. package/src/Lang/tr.php +53 -0
  59. package/src/Services/Safeguard.php +48 -0
  60. package/src/Services/Translator.php +52 -0
package/src/App.php ADDED
@@ -0,0 +1,266 @@
1
+ <?php
2
+ namespace ArtiFrame\Cli;
3
+
4
+ use ArtiFrame\Cli\Services\Translator;
5
+
6
+ class App
7
+ {
8
+ private array $originalArgs;
9
+ private array $args;
10
+ private Translator $translator;
11
+
12
+ public function __construct(array $argv)
13
+ {
14
+ $this->originalArgs = $argv;
15
+ $this->args = $argv;
16
+ $this->initTranslator();
17
+ }
18
+
19
+ private function initTranslator(): void
20
+ {
21
+ $lang = $this->getOrAskLanguage();
22
+ $this->translator = new Translator($lang);
23
+ }
24
+
25
+ private function getOrAskLanguage(): string
26
+ {
27
+ $home = getenv('HOME') ?: getenv('USERPROFILE');
28
+ $configFile = $home ? $home . \DIRECTORY_SEPARATOR . '.artiframe_lang' : null;
29
+
30
+ $lang = null;
31
+
32
+ // 1. Check argument --lang=
33
+ foreach ($this->originalArgs as $arg) {
34
+ if (strpos($arg, '--lang=') === 0) {
35
+ $lang = strtolower(substr($arg, 7, 2));
36
+ if ($configFile) file_put_contents($configFile, $lang);
37
+ return $lang;
38
+ }
39
+ }
40
+
41
+ // 2. Check config file
42
+ if ($configFile && file_exists($configFile)) {
43
+ $lang = trim(file_get_contents($configFile));
44
+ if (in_array($lang, ['tr', 'en', 'de', 'fr', 'es'])) {
45
+ return $lang;
46
+ }
47
+ }
48
+
49
+ // 3. Fallback check system env (optional, but asking is better)
50
+
51
+ // 4. Ask user
52
+ echo PHP_EOL;
53
+ echo "\033[1;32mWelcome to ArtiFrame! / ArtiFrame'e Hoş Geldiniz!\033[0m" . PHP_EOL;
54
+ echo "Please select your preferred language / Lütfen dilinizi seçin:" . PHP_EOL;
55
+ echo " [1] 🇹🇷 Türkçe (TR)" . PHP_EOL;
56
+ echo " [2] 🇬🇧 English (EN)" . PHP_EOL;
57
+ echo " [3] 🇩🇪 Deutsch (DE)" . PHP_EOL;
58
+ echo " [4] 🇫🇷 Français (FR)" . PHP_EOL;
59
+ echo " [5] 🇪🇸 Español (ES)" . PHP_EOL;
60
+ echo PHP_EOL;
61
+
62
+ while (true) {
63
+ echo "Select [1-5]: ";
64
+ $choice = trim(fgets(STDIN));
65
+ $map = [
66
+ '1' => 'tr',
67
+ '2' => 'en',
68
+ '3' => 'de',
69
+ '4' => 'fr',
70
+ '5' => 'es'
71
+ ];
72
+
73
+ if (isset($map[$choice])) {
74
+ $lang = $map[$choice];
75
+ if ($configFile) file_put_contents($configFile, $lang);
76
+ echo PHP_EOL . "Language set to: " . strtoupper($lang) . PHP_EOL;
77
+ return $lang;
78
+ }
79
+ echo "Invalid choice. Please try again." . PHP_EOL;
80
+ }
81
+ }
82
+
83
+ public function run(): void
84
+ {
85
+ $script = array_shift($this->args);
86
+
87
+ // Strip --lang flag globally for args
88
+ $this->args = array_filter($this->args, function($arg) {
89
+ return strpos($arg, '--lang=') !== 0;
90
+ });
91
+ $this->args = array_values($this->args);
92
+
93
+ // If no arguments provided, enter Interactive Mode (REPL)
94
+ if (empty($this->args)) {
95
+ $this->runInteractiveShell();
96
+ return;
97
+ }
98
+
99
+ // One-shot mode
100
+ $commandName = array_shift($this->args);
101
+ $this->routeCommand($commandName, $this->args);
102
+ }
103
+
104
+ private function runInteractiveShell(): void
105
+ {
106
+ $this->showBanner();
107
+
108
+ while (true) {
109
+ echo "\033[1;32martiframe\033[0m\033[38;5;240m>\033[0m ";
110
+ $input = trim(fgets(STDIN));
111
+
112
+ if ($input === '') {
113
+ continue;
114
+ }
115
+
116
+ if (in_array(strtolower($input), ['exit', 'quit'])) {
117
+ echo PHP_EOL . "\033[38;5;240m Goodbye. Build something great.\033[0m" . PHP_EOL . PHP_EOL;
118
+ break;
119
+ }
120
+
121
+ $parts = explode(' ', $input);
122
+ $parts = array_filter($parts, function($v) { return trim($v) !== ''; });
123
+ $parts = array_values($parts);
124
+
125
+ $commandName = array_shift($parts);
126
+
127
+ try {
128
+ $this->routeCommand($commandName, $parts);
129
+ } catch (\Exception $e) {
130
+ echo "\033[1;31m ✖ Error:\033[0m " . $e->getMessage() . PHP_EOL;
131
+ }
132
+ echo PHP_EOL;
133
+ }
134
+ }
135
+
136
+ private function showBanner(): void
137
+ {
138
+ $g = "\033[38;2;0;157;108m"; // #009d6c green
139
+ $lg = "\033[38;2;0;200;140m"; // lighter green for accent
140
+ $d = "\033[38;2;80;80;80m"; // dim gray for decorators
141
+ $w = "\033[1;37m"; // white bold
142
+ $r = "\033[0m"; // reset
143
+
144
+ echo PHP_EOL;
145
+ echo $g . " ┌─────────────────────────────────────────────────────────────┐" . $r . PHP_EOL;
146
+ echo $g . " │" . $r . PHP_EOL;
147
+ echo $g . " │" . $r . " " . $g . " █████╗ ██████╗ ████████╗██╗███████╗██████╗ █████╗ ███╗ ███╗███████╗" . $r . PHP_EOL;
148
+ echo $g . " │" . $r . " " . $g . " ██╔══██╗██╔══██╗╚══██╔══╝██║██╔════╝██╔══██╗██╔══██╗████╗ ████║██╔════╝" . $r . PHP_EOL;
149
+ echo $g . " │" . $r . " " . $lg . " ███████║██████╔╝ ██║ ██║█████╗ ██████╔╝███████║██╔████╔██║█████╗ " . $r . PHP_EOL;
150
+ echo $g . " │" . $r . " " . $g . " ██╔══██║██╔══██╗ ██║ ██║██╔══╝ ██╔══██╗██╔══██║██║╚██╔╝██║██╔══╝ " . $r . PHP_EOL;
151
+ echo $g . " │" . $r . " " . $g . " ██║ ██║██║ ██║ ██║ ██║██║ ██║ ██║██║ ██║██║ ╚═╝ ██║███████╗" . $r . PHP_EOL;
152
+ echo $g . " │" . $r . " " . $g . " ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝" . $r . PHP_EOL;
153
+ echo $g . " │" . $r . PHP_EOL;
154
+ echo $g . " │" . $r . " " . $d . " ─────────────────────────────────────────────────────────" . $r . PHP_EOL;
155
+ echo $g . " │" . $r . " " . $w . " [A] Agile Circuit Core v" . ARTIFRAME_VERSION . $r . $d . " │ Lightweight Native PHP Framework" . $r . PHP_EOL;
156
+ echo $g . " │" . $r . " " . $d . " ─────────────────────────────────────────────────────────" . $r . PHP_EOL;
157
+ echo $g . " │" . $r . PHP_EOL;
158
+ echo $g . " └─────────────────────────────────────────────────────────────┘" . $r . PHP_EOL;
159
+ echo PHP_EOL;
160
+ echo " " . $d . "Type " . $r . $g . "help" . $r . $d . " for available commands, or " . $r . $g . "exit" . $r . $d . " to quit." . $r . PHP_EOL;
161
+ echo PHP_EOL;
162
+ }
163
+
164
+
165
+
166
+ private function routeCommand(string $commandName, array $commandArgs): void
167
+ {
168
+ switch ($commandName) {
169
+ case 'new':
170
+ (new \ArtiFrame\Cli\Commands\NewProjectCommand($this->translator))->execute($commandArgs);
171
+ break;
172
+
173
+ case 'make:view':
174
+ (new \ArtiFrame\Cli\Commands\MakeViewCommand($this->translator))->execute($commandArgs);
175
+ break;
176
+
177
+ case 'make:api':
178
+ (new \ArtiFrame\Cli\Commands\MakeApiCommand($this->translator))->execute($commandArgs);
179
+ break;
180
+
181
+ case 'make:class':
182
+ (new \ArtiFrame\Cli\Commands\MakeClassCommand($this->translator))->execute($commandArgs);
183
+ break;
184
+
185
+ case 'version':
186
+ (new \ArtiFrame\Cli\Commands\VersionCommand($this->translator))->execute($commandArgs);
187
+ break;
188
+
189
+ case 'help':
190
+ default:
191
+ $this->showHelp();
192
+ break;
193
+ }
194
+ }
195
+
196
+ private function showHelp(): void
197
+ {
198
+ $g = "\033[38;2;0;157;108m"; // #009d6c green
199
+ $lg = "\033[38;2;0;200;140m"; // light green
200
+ $y = "\033[38;5;220m"; // yellow for args
201
+ $d = "\033[38;2;100;100;100m"; // dim
202
+ $w = "\033[1;37m"; // white bold
203
+ $c = "\033[38;5;81m"; // cyan for sub-options
204
+ $r = "\033[0m"; // reset
205
+
206
+ echo PHP_EOL;
207
+ echo " " . $w . "COMMANDS" . $r . PHP_EOL;
208
+ echo " " . $d . "─────────────────────────────────────────────────────────────" . $r . PHP_EOL;
209
+ echo PHP_EOL;
210
+
211
+ // new
212
+ echo " " . $g . "new" . $r . " " . $y . "<project-path>" . $r . PHP_EOL;
213
+ echo " " . $d . "│" . $r . " Create a new ArtiFrame project from scratch." . PHP_EOL;
214
+ echo " " . $d . "└── " . $r . "Example: " . $lg . "new my-app" . $r . PHP_EOL;
215
+ echo PHP_EOL;
216
+
217
+ // make:view
218
+ echo " " . $g . "make:view" . $r . " " . $y . "<path>" . $r . PHP_EOL;
219
+ echo " " . $d . "│" . $r . " Generate a new view file with its paired CSS and JS assets." . PHP_EOL;
220
+ echo " " . $d . "│" . $r . " The path can use subdirectories (auto-created)." . PHP_EOL;
221
+ echo " " . $d . "└── " . $r . "Example: " . $lg . "make:view pages/about.php" . $r . PHP_EOL;
222
+ echo PHP_EOL;
223
+
224
+ // make:api
225
+ echo " " . $g . "make:api" . $r . " " . $y . "<template>" . $r . " " . $y . "<path>" . $r . PHP_EOL;
226
+ echo " " . $d . "│" . $r . " Generate a new API endpoint file." . PHP_EOL;
227
+ echo " " . $d . "│" . $r . PHP_EOL;
228
+ echo " " . $d . "├── " . $c . "standart" . $r . " Single-action endpoint (one request, one response)." . PHP_EOL;
229
+ echo " " . $d . "│ " . $r . "Example: " . $lg . "make:api standart api/user/get.php" . $r . PHP_EOL;
230
+ echo " " . $d . "│" . $r . PHP_EOL;
231
+ echo " " . $d . "└── " . $c . "switch-case" . $r . " Multi-action endpoint with action routing." . PHP_EOL;
232
+ echo " " . $d . " " . $r . "Example: " . $lg . "make:api switch-case api/user/crud.php" . $r . PHP_EOL;
233
+ echo PHP_EOL;
234
+
235
+ // make:class
236
+ echo " " . $g . "make:class" . $r . " " . $y . "<path>" . $r . PHP_EOL;
237
+ echo " " . $d . "│" . $r . " Generate a new PHP class file with namespace boilerplate." . PHP_EOL;
238
+ echo " " . $d . "└── " . $r . "Example: " . $lg . "make:class classes/UserManager.php" . $r . PHP_EOL;
239
+ echo PHP_EOL;
240
+
241
+ // version
242
+ echo " " . $g . "version" . $r . " " . $y . "<action>" . $r . " " . $y . "<level>" . $r . PHP_EOL;
243
+ echo " " . $d . "│" . $r . " Manage the semantic version number stored in your project config." . PHP_EOL;
244
+ echo " " . $d . "│" . $r . " Version format: " . $w . "MAJOR.MINOR.PATCH" . $r . " (e.g. 2.4.1)" . PHP_EOL;
245
+ echo " " . $d . "│" . $r . PHP_EOL;
246
+ echo " " . $d . "├── " . $c . "upgrade" . $r . PHP_EOL;
247
+ echo " " . $d . "│ ├── " . $c . "patch" . $r . " Bug fixes, small tweaks. " . $d . "1.0.0 → 1.0.1" . $r . PHP_EOL;
248
+ echo " " . $d . "│ ├── " . $c . "minor" . $r . " New backwards-compat feature. " . $d . "1.0.0 → 1.1.0" . $r . PHP_EOL;
249
+ echo " " . $d . "│ └── " . $c . "major" . $r . " Breaking changes. " . $d . "1.0.0 → 2.0.0" . $r . PHP_EOL;
250
+ echo " " . $d . "│" . $r . PHP_EOL;
251
+ echo " " . $d . "└── " . $c . "downgrade" . $r . PHP_EOL;
252
+ echo " " . $d . " ├── " . $c . "patch" . $r . " Roll back last patch. " . $d . "1.0.3 → 1.0.2" . $r . PHP_EOL;
253
+ echo " " . $d . " ├── " . $c . "minor" . $r . " Roll back last minor release. " . $d . "1.3.0 → 1.2.0" . $r . PHP_EOL;
254
+ echo " " . $d . " └── " . $c . "major" . $r . " Roll back last major release. " . $d . "3.0.0 → 2.0.0" . $r . PHP_EOL;
255
+ echo PHP_EOL;
256
+ echo " " . $d . " Example: " . $lg . "version upgrade minor" . $r . PHP_EOL;
257
+ echo " " . $d . " Example: " . $lg . "version downgrade patch" . $r . PHP_EOL;
258
+ echo PHP_EOL;
259
+
260
+ // help / exit
261
+ echo " " . $d . "─────────────────────────────────────────────────────────────" . $r . PHP_EOL;
262
+ echo " " . $g . "help" . $r . " Show this help message." . PHP_EOL;
263
+ echo " " . $g . "exit" . $r . " / " . $g . "quit" . $r . " Exit the interactive shell." . PHP_EOL;
264
+ echo PHP_EOL;
265
+ }
266
+ }
@@ -0,0 +1,71 @@
1
+ <?php
2
+ namespace ArtiFrame\Cli\Commands;
3
+
4
+ use ArtiFrame\Cli\Services\Translator;
5
+ use ArtiFrame\Cli\Services\Safeguard;
6
+
7
+ class MakeApiCommand
8
+ {
9
+ private Translator $translator;
10
+ private Safeguard $safeguard;
11
+
12
+ public function __construct(Translator $translator)
13
+ {
14
+ $this->translator = $translator;
15
+ $this->safeguard = new Safeguard($translator);
16
+ }
17
+
18
+ public function execute(array $args): void
19
+ {
20
+ $type = $args[0] ?? null;
21
+ $target = $args[1] ?? null;
22
+
23
+ if (!$type || !in_array($type, ['standart', 'switch-case'])) {
24
+ echo "❌ " . $this->translator->get('ERROR_API_TYPE') . PHP_EOL;
25
+ exit(1);
26
+ }
27
+
28
+ if (!$target) {
29
+ echo "❌ " . $this->translator->get('ERROR_API_PATH_REQUIRED') . PHP_EOL;
30
+ exit(1);
31
+ }
32
+
33
+ // Normalize path
34
+ $target = ltrim(str_replace('\\', '/', $target), '/');
35
+
36
+ // Ensure .php extension
37
+ if (!str_ends_with($target, '.php')) {
38
+ $target .= '.php';
39
+ }
40
+
41
+ $projectRoot = getcwd();
42
+ $apiPath = $projectRoot . '/public/api/' . $type . '/' . $target;
43
+
44
+ if (!$this->safeguard->checkTarget($apiPath)) {
45
+ exit(1);
46
+ }
47
+
48
+ $stubName = 'api-' . $type . '.stub';
49
+ $stubPath = $projectRoot . '/bin/stubs/' . $stubName;
50
+
51
+ if (!file_exists($stubPath)) {
52
+ echo "❌ " . $this->translator->get('ERROR_STUB_NOT_FOUND', ['path' => $stubPath]) . PHP_EOL;
53
+ exit(1);
54
+ }
55
+
56
+ // Generate File
57
+ $this->ensureDirectoryExists(dirname($apiPath));
58
+
59
+ $content = file_get_contents($stubPath);
60
+ file_put_contents($apiPath, $content);
61
+
62
+ echo $this->translator->get('SUCCESS_API', ['type' => $type, 'path' => $target]) . PHP_EOL;
63
+ }
64
+
65
+ private function ensureDirectoryExists(string $path): void
66
+ {
67
+ if (!is_dir($path)) {
68
+ mkdir($path, 0755, true);
69
+ }
70
+ }
71
+ }
@@ -0,0 +1,88 @@
1
+ <?php
2
+ namespace ArtiFrame\Cli\Commands;
3
+
4
+ use ArtiFrame\Cli\Services\Translator;
5
+ use ArtiFrame\Cli\Services\Safeguard;
6
+
7
+ class MakeClassCommand
8
+ {
9
+ private Translator $translator;
10
+ private Safeguard $safeguard;
11
+
12
+ public function __construct(Translator $translator)
13
+ {
14
+ $this->translator = $translator;
15
+ $this->safeguard = new Safeguard($translator);
16
+ }
17
+
18
+ public function execute(array $args): void
19
+ {
20
+ $target = $args[0] ?? null;
21
+
22
+ if (!$target || strpos($target, '/') === false && strpos($target, '\\') === false) {
23
+ echo "❌ " . $this->translator->get('DIR_REQUIRED_ERROR') . PHP_EOL;
24
+ echo "Example: /src/Service/PaymentService" . PHP_EOL;
25
+ exit(1);
26
+ }
27
+
28
+ // Normalize path
29
+ $target = ltrim(str_replace('\\', '/', $target), '/');
30
+
31
+ // Allowed roots
32
+ if (!str_starts_with($target, 'app/') && !str_starts_with($target, 'src/')) {
33
+ echo "❌ " . $this->translator->get('ERROR_CLASS_ROOT') . PHP_EOL;
34
+ exit(1);
35
+ }
36
+
37
+ // Ensure .php extension
38
+ if (!str_ends_with($target, '.php')) {
39
+ $target .= '.php';
40
+ }
41
+
42
+ $projectRoot = getcwd();
43
+ $classPath = $projectRoot . '/' . $target;
44
+
45
+ if (!$this->safeguard->checkTarget($classPath)) {
46
+ exit(1);
47
+ }
48
+
49
+ $stubPath = $projectRoot . '/bin/stubs/class.stub';
50
+ if (!file_exists($stubPath)) {
51
+ echo "❌ " . $this->translator->get('ERROR_STUB_NOT_FOUND', ['path' => $stubPath]) . PHP_EOL;
52
+ exit(1);
53
+ }
54
+
55
+ // Determine Namespace and Class Name
56
+ $pathParts = explode('/', str_replace('.php', '', $target));
57
+ $className = array_pop($pathParts);
58
+
59
+ // Create Namespace array with proper casing
60
+ $namespaceParts = [];
61
+ foreach ($pathParts as $part) {
62
+ $namespaceParts[] = ucfirst($part);
63
+ }
64
+ $namespace = implode('\\', $namespaceParts);
65
+
66
+ // Generate File
67
+ $this->ensureDirectoryExists(dirname($classPath));
68
+
69
+ $content = file_get_contents($stubPath);
70
+ $content = str_replace(
71
+ ['{{NAMESPACE}}', '{{CLASS_NAME}}'],
72
+ [$namespace, $className],
73
+ $content
74
+ );
75
+
76
+ file_put_contents($classPath, $content);
77
+
78
+ echo $this->translator->get('SUCCESS_CLASS', ['path' => $target]) . PHP_EOL;
79
+ echo $this->translator->get('NAMESPACE_LABEL') . ' ' . $namespace . PHP_EOL;
80
+ }
81
+
82
+ private function ensureDirectoryExists(string $path): void
83
+ {
84
+ if (!is_dir($path)) {
85
+ mkdir($path, 0755, true);
86
+ }
87
+ }
88
+ }
@@ -0,0 +1,95 @@
1
+ <?php
2
+ namespace ArtiFrame\Cli\Commands;
3
+
4
+ use ArtiFrame\Cli\Services\Translator;
5
+ use ArtiFrame\Cli\Services\Safeguard;
6
+
7
+ class MakeViewCommand
8
+ {
9
+ private Translator $translator;
10
+ private Safeguard $safeguard;
11
+
12
+ public function __construct(Translator $translator)
13
+ {
14
+ $this->translator = $translator;
15
+ $this->safeguard = new Safeguard($translator);
16
+ }
17
+
18
+ public function execute(array $args): void
19
+ {
20
+ $target = $args[0] ?? null;
21
+
22
+ if (!$target) {
23
+ echo "❌ " . $this->translator->get('ERROR_VIEW_PATH_REQUIRED') . PHP_EOL;
24
+ exit(1);
25
+ }
26
+
27
+ // Normalize path
28
+ $target = ltrim(str_replace('\\', '/', $target), '/');
29
+
30
+ // Ensure .php extension
31
+ if (!str_ends_with($target, '.php')) {
32
+ $target .= '.php';
33
+ }
34
+
35
+ $projectRoot = getcwd();
36
+ $viewPath = $projectRoot . '/public/' . $target;
37
+
38
+ if (!$this->safeguard->checkTarget($viewPath)) {
39
+ exit(1);
40
+ }
41
+
42
+ // Determine hierarchy for assets
43
+ $pathParts = explode('/', $target);
44
+ $fileNameWithExt = array_pop($pathParts);
45
+ $fileName = pathinfo($fileNameWithExt, PATHINFO_FILENAME);
46
+
47
+ $subDir = count($pathParts) > 0 ? implode('/', $pathParts) : 'root';
48
+
49
+ $cssRelative = '/assets/css/' . $subDir . '/' . $fileName . '.css';
50
+ $jsRelative = '/assets/js/' . $subDir . '/' . $fileName . '.js';
51
+
52
+ $cssPath = $projectRoot . '/public' . $cssRelative;
53
+ $jsPath = $projectRoot . '/public' . $jsRelative;
54
+
55
+ // Stub path
56
+ $stubPath = $projectRoot . '/bin/stubs/view.stub';
57
+ if (!file_exists($stubPath)) {
58
+ echo "❌ " . $this->translator->get('ERROR_STUB_NOT_FOUND', ['path' => $stubPath]) . PHP_EOL;
59
+ echo $this->translator->get('ERROR_RUN_FROM_ROOT') . PHP_EOL;
60
+ exit(1);
61
+ }
62
+
63
+ // Generate Files
64
+ $this->ensureDirectoryExists(dirname($viewPath));
65
+ $this->ensureDirectoryExists(dirname($cssPath));
66
+ $this->ensureDirectoryExists(dirname($jsPath));
67
+
68
+ // Format Title
69
+ $title = ucfirst(str_replace(['-', '_'], ' ', $fileName));
70
+
71
+ // Read and parse stub
72
+ $lang = $this->translator->getLang();
73
+ $content = file_get_contents($stubPath);
74
+ $content = str_replace(
75
+ ['{{LANG}}', '{{VIEW_TITLE}}', '{{CSS_PATH}}', '{{JS_PATH}}'],
76
+ [$lang, $title, $cssRelative, $jsRelative],
77
+ $content
78
+ );
79
+
80
+ file_put_contents($viewPath, $content);
81
+ file_put_contents($cssPath, "/* CSS for {$title} */\n");
82
+ file_put_contents($jsPath, "/* JS for {$title} */\n");
83
+
84
+ echo $this->translator->get('SUCCESS_VIEW', ['path' => $target]) . PHP_EOL;
85
+ echo $this->translator->get('SUCCESS_CSS', ['path' => $cssRelative]) . PHP_EOL;
86
+ echo $this->translator->get('SUCCESS_JS', ['path' => $jsRelative]) . PHP_EOL;
87
+ }
88
+
89
+ private function ensureDirectoryExists(string $path): void
90
+ {
91
+ if (!is_dir($path)) {
92
+ mkdir($path, 0755, true);
93
+ }
94
+ }
95
+ }