create-prisma-php-app 1.22.0 → 1.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bootstrap.php +189 -153
- package/dist/index.js +1 -1
- package/dist/settings/bs-config.cjs +1 -0
- package/dist/settings/files-list-config.js +61 -0
- package/dist/settings/start-dev.js +4 -4
- package/dist/src/Lib/Headers/Boom.php +39 -0
- package/dist/src/app/layout.php +1 -1
- package/dist/tailwind.config.js +3 -6
- package/dist/tsconfig.json +9 -9
- package/package.json +1 -1
package/dist/bootstrap.php
CHANGED
|
@@ -25,8 +25,17 @@ function determineContentToInclude()
|
|
|
25
25
|
$baseDir = APP_PATH;
|
|
26
26
|
$includePath = '';
|
|
27
27
|
$layoutsToInclude = [];
|
|
28
|
-
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* ============ Middleware Management ============
|
|
31
|
+
* AuthMiddleware is invoked to handle authentication logic for the current route ($uri).
|
|
32
|
+
* ================================================
|
|
33
|
+
*/
|
|
29
34
|
AuthMiddleware::handle($uri);
|
|
35
|
+
/**
|
|
36
|
+
* ============ End of Middleware Management ======
|
|
37
|
+
* ================================================
|
|
38
|
+
*/
|
|
30
39
|
|
|
31
40
|
$isDirectAccessToPrivateRoute = preg_match('/\/_/', $uri);
|
|
32
41
|
if ($isDirectAccessToPrivateRoute) {
|
|
@@ -107,31 +116,23 @@ function getFilePrecedence()
|
|
|
107
116
|
global $_filesListRoutes;
|
|
108
117
|
|
|
109
118
|
// Normalize the file paths for consistent comparison
|
|
110
|
-
$_filesListRoutes
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return preg_match('/^\.\/src\/app\/route\.php$/', $route);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
// If route.php is found, return just the file name
|
|
120
|
-
if (!empty($routeFile)) {
|
|
121
|
-
return '/route.php';
|
|
119
|
+
foreach ($_filesListRoutes as $route) {
|
|
120
|
+
$normalizedRoute = str_replace('\\', '/', $route);
|
|
121
|
+
// First, check for route.php
|
|
122
|
+
if (preg_match('/^\.\/src\/app\/route\.php$/', $normalizedRoute)) {
|
|
123
|
+
return '/route.php';
|
|
124
|
+
}
|
|
122
125
|
}
|
|
123
126
|
|
|
124
|
-
// If route.php is
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (!empty($indexFile)) {
|
|
131
|
-
return '/index.php';
|
|
127
|
+
// If no route.php is found, check for index.php
|
|
128
|
+
foreach ($_filesListRoutes as $route) {
|
|
129
|
+
$normalizedRoute = str_replace('\\', '/', $route);
|
|
130
|
+
if (preg_match('/^\.\/src\/app\/index\.php$/', $normalizedRoute)) {
|
|
131
|
+
return '/index.php';
|
|
132
|
+
}
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
// If neither file is found, return null
|
|
135
|
+
// If neither file is found, return null
|
|
135
136
|
return null;
|
|
136
137
|
}
|
|
137
138
|
|
|
@@ -145,42 +146,19 @@ function uriExtractor(string $scriptUrl): string
|
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
$escapedIdentifier = preg_quote($projectName, '/');
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (!empty($matches[1])) {
|
|
151
|
-
$leftTrim = ltrim($matches[1], '/');
|
|
152
|
-
$rightTrim = rtrim($leftTrim, '/');
|
|
153
|
-
return "$rightTrim";
|
|
154
|
-
}
|
|
149
|
+
if (preg_match("/(?:.*$escapedIdentifier)(\/.*)$/", $scriptUrl, $matches) && !empty($matches[1])) {
|
|
150
|
+
return rtrim(ltrim($matches[1], '/'), '/');
|
|
155
151
|
}
|
|
156
152
|
|
|
157
153
|
return "/";
|
|
158
154
|
}
|
|
159
155
|
|
|
160
|
-
function
|
|
156
|
+
function getFilesListRoutes()
|
|
161
157
|
{
|
|
162
|
-
|
|
163
|
-
$
|
|
164
|
-
|
|
165
|
-
if (is_dir($directory)) {
|
|
166
|
-
$filesList = [];
|
|
167
|
-
|
|
168
|
-
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));
|
|
169
|
-
|
|
170
|
-
foreach ($iterator as $file) {
|
|
171
|
-
if ($file->isFile()) {
|
|
172
|
-
$filesList[] = $file->getPathname();
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
$jsonData = json_encode($filesList, JSON_PRETTY_PRINT);
|
|
177
|
-
$jsonFileName = SETTINGS_PATH . '/files-list.json';
|
|
178
|
-
file_put_contents($jsonFileName, $jsonData);
|
|
158
|
+
$jsonFileName = SETTINGS_PATH . '/files-list.json';
|
|
159
|
+
$routeFiles = file_exists($jsonFileName) ? json_decode(file_get_contents($jsonFileName), true) : [];
|
|
179
160
|
|
|
180
|
-
|
|
181
|
-
$_filesListRoutes = json_decode(file_get_contents($jsonFileName), true);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
161
|
+
return $routeFiles;
|
|
184
162
|
}
|
|
185
163
|
|
|
186
164
|
function findGroupFolder($uri): string
|
|
@@ -213,14 +191,21 @@ function dynamicRoute($uri)
|
|
|
213
191
|
foreach ($_filesListRoutes as $route) {
|
|
214
192
|
$normalizedRoute = trim(str_replace('\\', '/', $route), '.');
|
|
215
193
|
$routeSegments = explode('/', ltrim($normalizedRoute, '/'));
|
|
194
|
+
|
|
195
|
+
$filteredRouteSegments = array_values(array_filter($routeSegments, function ($segment) {
|
|
196
|
+
return !preg_match('/\(.+\)/', $segment); // Skip segments with parentheses (groups)
|
|
197
|
+
}));
|
|
198
|
+
|
|
216
199
|
$singleDynamic = preg_match_all('/\[[^\]]+\]/', $normalizedRoute, $matches) === 1 && !strpos($normalizedRoute, '[...');
|
|
217
200
|
if ($singleDynamic) {
|
|
218
|
-
$segmentMatch = singleDynamicRoute($uriSegments, $
|
|
219
|
-
|
|
201
|
+
$segmentMatch = singleDynamicRoute($uriSegments, $filteredRouteSegments);
|
|
202
|
+
$index = array_search($segmentMatch, $filteredRouteSegments);
|
|
203
|
+
if ($index !== false && isset($uriSegments[$index])) {
|
|
220
204
|
$trimSegmentMatch = trim($segmentMatch, '[]');
|
|
221
|
-
$dynamicRouteParams = new \ArrayObject([$trimSegmentMatch => $uriSegments[
|
|
222
|
-
|
|
223
|
-
$dynamicRouteUri =
|
|
205
|
+
$dynamicRouteParams = new \ArrayObject([$trimSegmentMatch => $uriSegments[$index]], \ArrayObject::ARRAY_AS_PROPS);
|
|
206
|
+
$dynamicRouteUri = str_replace($segmentMatch, $uriSegments[$index], $normalizedRoute);
|
|
207
|
+
$dynamicRouteUri = preg_replace('/\(.+\)/', '', $dynamicRouteUri);
|
|
208
|
+
$dynamicRouteUri = preg_replace('/\/+/', '/', $dynamicRouteUri);
|
|
224
209
|
$dynamicRouteUriDirname = dirname($dynamicRouteUri);
|
|
225
210
|
$dynamicRouteUriDirname = rtrim($dynamicRouteUriDirname, '/');
|
|
226
211
|
|
|
@@ -235,30 +220,41 @@ function dynamicRoute($uri)
|
|
|
235
220
|
}
|
|
236
221
|
}
|
|
237
222
|
} elseif (strpos($normalizedRoute, '[...') !== false) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
223
|
+
// Clean and normalize the route
|
|
224
|
+
$cleanedNormalizedRoute = preg_replace('/\(.+\)/', '', $normalizedRoute); // Remove any public/private segment
|
|
225
|
+
$cleanedNormalizedRoute = preg_replace('/\/+/', '/', $cleanedNormalizedRoute); // Replace multiple slashes
|
|
226
|
+
$dynamicSegmentRoute = preg_replace('/\[\.\.\..*?\].*/', '', $cleanedNormalizedRoute); // Remove the dynamic segment
|
|
227
|
+
|
|
228
|
+
// Check if the normalized URI starts with the cleaned route
|
|
229
|
+
if (strpos("/src/app/$normalizedUri", $dynamicSegmentRoute) === 0) {
|
|
230
|
+
$trimmedUri = str_replace($dynamicSegmentRoute, '', "/src/app/$normalizedUri");
|
|
231
|
+
$uriParts = explode('/', trim($trimmedUri, '/'));
|
|
232
|
+
|
|
233
|
+
// Extract the dynamic segment content
|
|
234
|
+
if (preg_match('/\[\.\.\.(.*?)\]/', $normalizedRoute, $matches)) {
|
|
235
|
+
$dynamicParam = $matches[1];
|
|
236
|
+
$dynamicRouteParams = new \ArrayObject([$dynamicParam => $uriParts], \ArrayObject::ARRAY_AS_PROPS);
|
|
248
237
|
}
|
|
238
|
+
|
|
239
|
+
// Check for 'route.php'
|
|
249
240
|
if (strpos($normalizedRoute, 'route.php') !== false) {
|
|
250
241
|
$uriMatch = $normalizedRoute;
|
|
251
242
|
break;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Handle matching routes ending with 'index.php'
|
|
246
|
+
if (strpos($normalizedRoute, 'index.php') !== false) {
|
|
247
|
+
$segmentMatch = "[...$dynamicParam]";
|
|
248
|
+
$index = array_search($segmentMatch, $filteredRouteSegments);
|
|
249
|
+
|
|
250
|
+
if ($index !== false && isset($uriSegments[$index])) {
|
|
251
|
+
// Generate the dynamic URI
|
|
252
|
+
$dynamicRouteUri = str_replace($segmentMatch, implode('/', $uriParts), $cleanedNormalizedRoute);
|
|
253
|
+
$dynamicRouteUriDirname = rtrim(dirname($dynamicRouteUri), '/');
|
|
258
254
|
|
|
259
|
-
$expectedUri =
|
|
260
|
-
$expectedUri = rtrim($expectedUri, '/');
|
|
255
|
+
$expectedUri = rtrim("/src/app/$normalizedUri", '/');
|
|
261
256
|
|
|
257
|
+
// Compare the expected and dynamic URIs
|
|
262
258
|
if ($expectedUri === $dynamicRouteUriDirname) {
|
|
263
259
|
$uriMatch = $normalizedRoute;
|
|
264
260
|
break;
|
|
@@ -280,6 +276,7 @@ function isGroupIdentifier($segment): bool
|
|
|
280
276
|
function matchGroupFolder($constructedPath): ?string
|
|
281
277
|
{
|
|
282
278
|
global $_filesListRoutes;
|
|
279
|
+
|
|
283
280
|
$bestMatch = null;
|
|
284
281
|
$normalizedConstructedPath = ltrim(str_replace('\\', '/', $constructedPath), './');
|
|
285
282
|
|
|
@@ -330,6 +327,10 @@ function singleDynamicRoute($uriSegments, $routeSegments)
|
|
|
330
327
|
|
|
331
328
|
function checkForDuplicateRoutes()
|
|
332
329
|
{
|
|
330
|
+
if ($_ENV['APP_ENV'] !== 'development') {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
333
334
|
global $_filesListRoutes;
|
|
334
335
|
$normalizedRoutesMap = [];
|
|
335
336
|
foreach ($_filesListRoutes as $route) {
|
|
@@ -363,72 +364,27 @@ function checkForDuplicateRoutes()
|
|
|
363
364
|
}
|
|
364
365
|
}
|
|
365
366
|
|
|
366
|
-
function setupErrorHandling(&$content)
|
|
367
|
-
{
|
|
368
|
-
set_error_handler(function ($severity, $message, $file, $line) use (&$content) {
|
|
369
|
-
$content .= "<div class='error'>Error: {$severity} - {$message} in {$file} on line {$line}</div>";
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
set_exception_handler(function ($exception) use (&$content) {
|
|
373
|
-
$content .= "<div class='error'>Exception: " . htmlspecialchars($exception->getMessage(), ENT_QUOTES, 'UTF-8') . "</div>";
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
register_shutdown_function(function () use (&$content) {
|
|
377
|
-
$error = error_get_last();
|
|
378
|
-
if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_RECOVERABLE_ERROR])) {
|
|
379
|
-
$formattedError = "<div class='error'>Fatal Error: " . htmlspecialchars($error['message'], ENT_QUOTES, 'UTF-8') .
|
|
380
|
-
" in " . htmlspecialchars($error['file'], ENT_QUOTES, 'UTF-8') .
|
|
381
|
-
" on line " . $error['line'] . "</div>";
|
|
382
|
-
$content .= $formattedError;
|
|
383
|
-
modifyOutputLayoutForError($content);
|
|
384
|
-
}
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
|
|
388
367
|
|
|
389
368
|
function containsChildContent($filePath)
|
|
390
369
|
{
|
|
391
370
|
$fileContent = file_get_contents($filePath);
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
)
|
|
398
|
-
return true;
|
|
399
|
-
} else {
|
|
400
|
-
return false;
|
|
401
|
-
}
|
|
371
|
+
|
|
372
|
+
// Regular expression to match different ways of echoing $childContent
|
|
373
|
+
$pattern = '/\<\?=\s*\$childContent\s*;?\s*\?>|echo\s*\$childContent\s*;?/';
|
|
374
|
+
|
|
375
|
+
// Return true if $childContent variables are found, false otherwise
|
|
376
|
+
return preg_match($pattern, $fileContent) === 1;
|
|
402
377
|
}
|
|
403
378
|
|
|
404
379
|
function containsContent($filePath)
|
|
405
380
|
{
|
|
406
381
|
$fileContent = file_get_contents($filePath);
|
|
407
|
-
if (
|
|
408
|
-
(strpos($fileContent, 'echo $content') === false &&
|
|
409
|
-
strpos($fileContent, 'echo $content;') === false) &&
|
|
410
|
-
(strpos($fileContent, '<?= $content ?>') === false) &&
|
|
411
|
-
(strpos($fileContent, '<?= $content; ?>') === false)
|
|
412
|
-
) {
|
|
413
|
-
return true;
|
|
414
|
-
} else {
|
|
415
|
-
return false;
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
function modifyOutputLayoutForError($contentToAdd)
|
|
420
|
-
{
|
|
421
|
-
if ($_ENV['SHOW_ERRORS'] === "false") exit;
|
|
422
382
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
$newBodyContent = "<body class=\"fatal-error\">$contentToAdd</body>";
|
|
383
|
+
// Improved regular expression to match different ways of echoing $content
|
|
384
|
+
$pattern = '/\<\?=\s*\$content\s*;?\s*\?>|echo\s*\$content\s*;?/';
|
|
426
385
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
echo $modifiedNotFoundContent;
|
|
430
|
-
exit;
|
|
431
|
-
}
|
|
386
|
+
// Return true if content variables are found, false otherwise
|
|
387
|
+
return preg_match($pattern, $fileContent) === 1;
|
|
432
388
|
}
|
|
433
389
|
|
|
434
390
|
function convertToArrayObject($data)
|
|
@@ -579,17 +535,7 @@ function getLoadingsFiles()
|
|
|
579
535
|
return '';
|
|
580
536
|
}
|
|
581
537
|
|
|
582
|
-
|
|
583
|
-
{
|
|
584
|
-
$token = getBearerToken();
|
|
585
|
-
if ($token) {
|
|
586
|
-
$auth = Auth::getInstance();
|
|
587
|
-
$verifyToken = $auth->verifyToken($token);
|
|
588
|
-
if ($verifyToken) {
|
|
589
|
-
$auth->authenticate($verifyToken);
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
}
|
|
538
|
+
|
|
593
539
|
|
|
594
540
|
function getPrismaSettings(): \ArrayObject
|
|
595
541
|
{
|
|
@@ -607,15 +553,103 @@ function getPrismaSettings(): \ArrayObject
|
|
|
607
553
|
}
|
|
608
554
|
}
|
|
609
555
|
|
|
610
|
-
|
|
556
|
+
function modifyOutputLayoutForError($contentToAdd)
|
|
557
|
+
{
|
|
558
|
+
global
|
|
559
|
+
$baseUrl,
|
|
560
|
+
$metadata,
|
|
561
|
+
$content,
|
|
562
|
+
$childContent,
|
|
563
|
+
$uri,
|
|
564
|
+
$pathname,
|
|
565
|
+
$dynamicRouteParams,
|
|
566
|
+
$params,
|
|
567
|
+
$referer,
|
|
568
|
+
$mainLayoutHead,
|
|
569
|
+
$mainLayoutFooter;
|
|
570
|
+
|
|
571
|
+
$errorFile = APP_PATH . '/error.php';
|
|
572
|
+
$errorFileExists = file_exists($errorFile);
|
|
573
|
+
|
|
574
|
+
if ($_ENV['SHOW_ERRORS'] === "false") {
|
|
575
|
+
if ($errorFileExists) {
|
|
576
|
+
$contentToAdd = "<div class='error'>An error occurred</div>";
|
|
577
|
+
} else {
|
|
578
|
+
exit; // Exit if SHOW_ERRORS is false and no error file exists
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if ($errorFileExists) {
|
|
583
|
+
|
|
584
|
+
$errorContent = $contentToAdd;
|
|
585
|
+
|
|
586
|
+
$layoutFile = APP_PATH . '/layout.php';
|
|
587
|
+
if (file_exists($layoutFile)) {
|
|
588
|
+
|
|
589
|
+
ob_start();
|
|
590
|
+
include_once $errorFile;
|
|
591
|
+
$content = ob_get_clean();
|
|
592
|
+
include $layoutFile;
|
|
593
|
+
} else {
|
|
594
|
+
echo $errorContent;
|
|
595
|
+
}
|
|
596
|
+
} else {
|
|
597
|
+
echo $contentToAdd;
|
|
598
|
+
}
|
|
599
|
+
exit;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
set_error_handler(function ($severity, $message, $file, $line) {
|
|
603
|
+
if (!(error_reporting() & $severity)) {
|
|
604
|
+
// This error code is not included in error_reporting
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Capture the specific severity types, including warnings (E_WARNING)
|
|
609
|
+
$errorContent = "<div class='error'>Error: {$severity} - {$message} in {$file} on line {$line}</div>";
|
|
610
|
+
|
|
611
|
+
// If needed, log it or output immediately based on severity
|
|
612
|
+
if ($severity === E_WARNING || $severity === E_NOTICE) {
|
|
613
|
+
modifyOutputLayoutForError($errorContent);
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
set_exception_handler(function ($exception) {
|
|
618
|
+
$errorContent = "<div class='error'>Exception: " . htmlspecialchars($exception->getMessage(), ENT_QUOTES, 'UTF-8') . "</div>";
|
|
619
|
+
modifyOutputLayoutForError($errorContent);
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
register_shutdown_function(function () {
|
|
623
|
+
$error = error_get_last();
|
|
624
|
+
if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_RECOVERABLE_ERROR])) {
|
|
625
|
+
$formattedError = "<div class='error'>Fatal Error: " . htmlspecialchars($error['message'], ENT_QUOTES, 'UTF-8') .
|
|
626
|
+
" in " . htmlspecialchars($error['file'], ENT_QUOTES, 'UTF-8') .
|
|
627
|
+
" on line " . $error['line'] . "</div>";
|
|
628
|
+
$errorContent = $formattedError;
|
|
629
|
+
modifyOutputLayoutForError($errorContent);
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
|
|
611
633
|
require_once SETTINGS_PATH . '/public-functions.php';
|
|
612
634
|
require_once SETTINGS_PATH . '/request-methods.php';
|
|
613
635
|
$_metadataFile = APP_PATH . '/metadata.php';
|
|
614
636
|
$_metadataArray = file_exists($_metadataFile) ? require_once $_metadataFile : [];
|
|
615
|
-
$_filesListRoutes =
|
|
637
|
+
$_filesListRoutes = getFilesListRoutes();
|
|
616
638
|
$_prismaPHPSettings = getPrismaSettings();
|
|
617
639
|
$_fileToInclude = '';
|
|
618
640
|
|
|
641
|
+
function authenticateUserToken()
|
|
642
|
+
{
|
|
643
|
+
$token = getBearerToken();
|
|
644
|
+
if ($token) {
|
|
645
|
+
$auth = Auth::getInstance();
|
|
646
|
+
$verifyToken = $auth->verifyToken($token);
|
|
647
|
+
if ($verifyToken) {
|
|
648
|
+
$auth->authenticate($verifyToken);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
619
653
|
/**
|
|
620
654
|
* @var array $metadata Metadata information
|
|
621
655
|
*/
|
|
@@ -656,7 +690,10 @@ try {
|
|
|
656
690
|
$_layoutsToInclude = $_determineContentToInclude['layouts'] ?? [];
|
|
657
691
|
$uri = $_determineContentToInclude['uri'] ?? '';
|
|
658
692
|
$pathname = $uri ? "/" . $uri : "/";
|
|
659
|
-
$_fileToInclude =
|
|
693
|
+
$_fileToInclude = null;
|
|
694
|
+
if (is_file($_contentToInclude)) {
|
|
695
|
+
$_fileToInclude = basename($_contentToInclude); // returns the file name
|
|
696
|
+
}
|
|
660
697
|
authenticateUserToken();
|
|
661
698
|
|
|
662
699
|
if (empty($_contentToInclude)) {
|
|
@@ -710,11 +747,11 @@ try {
|
|
|
710
747
|
|
|
711
748
|
$_isContentIncluded = false;
|
|
712
749
|
$_isChildContentIncluded = false;
|
|
713
|
-
|
|
750
|
+
$_isContentVariableIncluded = containsContent($_parentLayoutPath);
|
|
751
|
+
if (!$_isContentVariableIncluded) {
|
|
714
752
|
$_isContentIncluded = true;
|
|
715
753
|
}
|
|
716
754
|
|
|
717
|
-
ob_start();
|
|
718
755
|
if (!empty($_contentToInclude)) {
|
|
719
756
|
if (!$_isParentLayout) {
|
|
720
757
|
ob_start();
|
|
@@ -726,7 +763,8 @@ try {
|
|
|
726
763
|
continue;
|
|
727
764
|
}
|
|
728
765
|
|
|
729
|
-
|
|
766
|
+
$_isChildContentVariableIncluded = containsChildContent($layoutPath);
|
|
767
|
+
if (!$_isChildContentVariableIncluded) {
|
|
730
768
|
$_isChildContentIncluded = true;
|
|
731
769
|
}
|
|
732
770
|
|
|
@@ -759,18 +797,16 @@ try {
|
|
|
759
797
|
}
|
|
760
798
|
} else {
|
|
761
799
|
if ($_isContentIncluded) {
|
|
762
|
-
|
|
763
|
-
modifyOutputLayoutForError($content);
|
|
800
|
+
echo "<div class='error'>The parent layout file does not contain <?php echo \$content; ?> Or <?= \$content ?><br>" . "<strong>$_parentLayoutPath</strong></div>";
|
|
764
801
|
} else {
|
|
765
|
-
$
|
|
766
|
-
modifyOutputLayoutForError($
|
|
802
|
+
$errorDetails = "<div class='error'>The layout file does not contain <?php echo \$childContent; ?> or <?= \$childContent ?><br><strong>$layoutPath</strong></div>";
|
|
803
|
+
modifyOutputLayoutForError($errorDetails);
|
|
767
804
|
}
|
|
768
805
|
}
|
|
769
806
|
} catch (Throwable $e) {
|
|
770
|
-
$content = ob_get_clean();
|
|
771
807
|
$errorDetails = "Unhandled Exception: " . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
|
|
772
808
|
$errorDetails .= "<br>File: " . htmlspecialchars($e->getFile(), ENT_QUOTES, 'UTF-8');
|
|
773
809
|
$errorDetails .= "<br>Line: " . htmlspecialchars($e->getLine(), ENT_QUOTES, 'UTF-8');
|
|
774
|
-
$
|
|
775
|
-
modifyOutputLayoutForError($
|
|
810
|
+
$errorDetails = "<div class='error'>$errorDetails</div>";
|
|
811
|
+
modifyOutputLayoutForError($errorDetails);
|
|
776
812
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{execSync}from"child_process";import fs from"fs";import{fileURLToPath}from"url";import path from"path";import chalk from"chalk";import prompts from"prompts";import https from"https";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename);let updateAnswer=null;const nonBackendFiles=["favicon.ico","\\src\\app\\index.php","metadata.php","not-found.php"],dockerFiles=[".dockerignore","docker-compose.yml","Dockerfile","apache.conf"];function bsConfigUrls(e){const s=e.indexOf("\\htdocs\\");if(-1===s)return{bsTarget:"",bsPathRewrite:{}};const n=e.substring(0,s+"\\htdocs\\".length).replace(/\\/g,"\\\\"),t=e.replace(new RegExp(`^${n}`),"").replace(/\\/g,"/");let i=`http://localhost/${t}`;i=i.endsWith("/")?i.slice(0,-1):i;const c=i.replace(/(?<!:)(\/\/+)/g,"/"),r=t.replace(/\/\/+/g,"/");return{bsTarget:`${c}/`,bsPathRewrite:{"^/":`/${r.startsWith("/")?r.substring(1):r}/`}}}function configureBrowserSyncCommand(e){const s=path.join(e,"settings","bs-config.cjs");return fs.writeFileSync(s,'const { createProxyMiddleware } = require("http-proxy-middleware");\nconst fs = require("fs");\n\nconst jsonData = fs.readFileSync("prisma-php.json", "utf8");\nconst config = JSON.parse(jsonData);\n\nmodule.exports = {\n proxy: "http://localhost:3000",\n middleware: [\n (req, res, next) => {\n res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");\n res.setHeader("Pragma", "no-cache");\n res.setHeader("Expires", "0");\n next();\n },\n createProxyMiddleware({\n target: config.bsTarget,\n changeOrigin: true,\n pathRewrite: config.bsPathRewrite,\n }),\n ],\n files: "src/**/*.*",\n notify: false,\n open: false,\n ghostMode: false,\n codeSync: true, // Disable synchronization of code changes across clients\n};',"utf8"),"browser-sync start --config settings/bs-config.cjs"}async function updatePackageJson(e,s){const n=path.join(e,"package.json");if(checkExcludeFiles(n))return;const t=JSON.parse(fs.readFileSync(n,"utf8")),i=configureBrowserSyncCommand(e);t.scripts=Object.assign(Object.assign({},t.scripts),{projectName:"node settings/project-name.cjs"});let c=[];s.tailwindcss&&(t.scripts=Object.assign(Object.assign({},t.scripts),{tailwind:"postcss ./src/app/css/tailwind.css -o ./src/app/css/styles.css --watch"}),c.push("tailwind")),s.websocket&&(t.scripts=Object.assign(Object.assign({},t.scripts),{websocket:"node ./settings/restart-websocket.cjs"}),c.push("websocket")),s.docker&&(t.scripts=Object.assign(Object.assign({},t.scripts),{docker:"docker-compose up"}),c.push("docker")),s.swaggerDocs&&(t.scripts=Object.assign(Object.assign({},t.scripts),{"create-swagger-docs":"node settings/swagger-setup.js"}),c.push("create-swagger-docs"));let r=Object.assign({},t.scripts);r.browserSync=i,r._dev=c.length>0?`npm-run-all -p ${c.join(" ")}`:'echo "No additional scripts to run"',r.startDev="node settings/start-dev.js",r.dev="npm run startDev",t.scripts=r,t.type="module",s.prisma&&(t.prisma={seed:"node prisma/seed.js"}),fs.writeFileSync(n,JSON.stringify(t,null,2))}async function updateComposerJson(e,s){const n=path.join(e,"composer.json");if(checkExcludeFiles(n))return;let t;if(fs.existsSync(n)){{const e=fs.readFileSync(n,"utf8");t=JSON.parse(e)}s.websocket&&(t.require=Object.assign(Object.assign({},t.require),{"cboden/ratchet":"^0.4.4"})),s.prisma&&(t.require=Object.assign(Object.assign({},t.require),{"ramsey/uuid":"5.x-dev","hidehalo/nanoid-php":"1.x-dev"})),fs.writeFileSync(n,JSON.stringify(t,null,2))}}async function updateIndexJsForWebSocket(e,s){if(!s.websocket)return;const n=path.join(e,"src","app","js","index.js");if(checkExcludeFiles(n))return;let t=fs.readFileSync(n,"utf8");t+='\n// WebSocket initialization\nconst ws = new WebSocket("ws://localhost:8080");\n',fs.writeFileSync(n,t,"utf8")}async function createUpdateGitignoreFile(e,s){const n=path.join(e,".gitignore");if(checkExcludeFiles(n))return;let t="";s.forEach((e=>{t.includes(e)||(t+=`\n${e}`)})),t=t.trimStart(),fs.writeFileSync(n,t)}function copyRecursiveSync(e,s,n){var t;const i=fs.existsSync(e),c=i&&fs.statSync(e);if(i&&c&&c.isDirectory()){const i=s.toLowerCase();if(!n.websocket&&i.includes("src\\lib\\websocket"))return;if(!n.prisma&&i.includes("src\\lib\\prisma"))return;if(n.backendOnly&&i.includes("src\\app\\js")||n.backendOnly&&i.includes("src\\app\\css"))return;if(!n.swaggerDocs&&i.includes("src\\app\\swagger-docs"))return;const c=s.replace(/\\/g,"/");if(null===(t=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===t?void 0:t.includes(c))return;fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((t=>{copyRecursiveSync(path.join(e,t),path.join(s,t),n)}))}else{if(checkExcludeFiles(s))return;if(!n.tailwindcss&&(s.includes("tailwind.css")||s.includes("styles.css")))return;if(!n.websocket&&(s.includes("restart-websocket.cjs")||s.includes("restart-websocket.bat")))return;if(!n.docker&&dockerFiles.some((e=>s.includes(e))))return;if(n.backendOnly&&nonBackendFiles.some((e=>s.includes(e))))return;if(!n.backendOnly&&s.includes("route.php"))return;if(n.backendOnly&&!n.swaggerDocs&&(s.includes("start-dev.js")||s.includes("swagger-setup.js")))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s,n){s.forEach((({srcDir:s,destDir:t})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,t),n)}))}function createOrUpdateTailwindConfig(e){const s=path.join(e,"tailwind.config.js");if(checkExcludeFiles(s))return;let n=fs.readFileSync(s,"utf8");const t=["./src/app/**/*.{html,js,php}"].map((e=>` "${e}"`)).join(",\n");n=n.replace(/content: \[\],/g,`content: [\n${t}\n],`),fs.writeFileSync(s,n,{flag:"w"})}function modifyPostcssConfig(e){const s=path.join(e,"postcss.config.js");if(checkExcludeFiles(s))return;fs.writeFileSync(s,"export default {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n cssnano: {},\n },\n};",{flag:"w"})}function modifyLayoutPHP(e,s){const n=path.join(e,"src","app","layout.php");if(!checkExcludeFiles(n))try{let e=fs.readFileSync(n,"utf8"),t="";s.backendOnly||(t='\n <link href="<?= $baseUrl; ?>/css/index.css" rel="stylesheet">\n <script src="<?= $baseUrl; ?>/js/index.js"><\/script>');let i="";s.backendOnly||(i=s.tailwindcss?` <link href="<?= $baseUrl; ?>/css/styles.css" rel="stylesheet"> ${t}`:` <script src="https://cdn.tailwindcss.com"><\/script> ${t}`);const c=i.length>0?"\n":"";e=e.replace("</head>",`${i}${c} \x3c!-- Dynamic Head --\x3e\n <?= implode("\\n", $mainLayoutHead); ?>\n</head>`),fs.writeFileSync(n,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const n=path.join(e,".env");checkExcludeFiles(n)||fs.writeFileSync(n,s,{flag:"w"})}function checkExcludeFiles(e){var s,n;return!!(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(null!==(n=null===(s=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===s?void 0:s.includes(e.replace(/\\/g,"/")))&&void 0!==n&&n)}async function createDirectoryStructure(e,s){const n=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"}];(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(n.push({src:"/tsconfig.json",dest:"/tsconfig.json"}),updateAnswer.tailwindcss&&n.push({src:"/postcss.config.js",dest:"/postcss.config.js"},{src:"/tailwind.config.js",dest:"/tailwind.config.js"}));const t=[{srcDir:"/settings",destDir:"/settings"},{srcDir:"/src",destDir:"/src"}];s.backendOnly&&s.swaggerDocs&&t.push({srcDir:"/swagger-docs-layout.php",destDir:"/src/app/layout.php"}),s.prisma&&t.push({srcDir:"/prisma",destDir:"/prisma"}),s.docker&&t.push({srcDir:"/.dockerignore",destDir:"/.dockerignore"},{srcDir:"/docker-compose.yml",destDir:"/docker-compose.yml"},{srcDir:"/Dockerfile",destDir:"/Dockerfile"},{srcDir:"/apache.conf",destDir:"/apache.conf"}),n.forEach((({src:s,dest:n})=>{const t=path.join(__dirname,s),i=path.join(e,n);if(checkExcludeFiles(i))return;const c=fs.readFileSync(t,"utf8");fs.writeFileSync(i,c,{flag:"w"})})),await executeCopy(e,t,s),await updatePackageJson(e,s),await updateComposerJson(e,s),s.backendOnly||await updateIndexJsForWebSocket(e,s),s.tailwindcss&&(createOrUpdateTailwindConfig(e),modifyPostcssConfig(e)),(s.tailwindcss||!s.backendOnly||s.swaggerDocs)&&modifyLayoutPHP(e,s);const i='# Prisma PHP Auth Secret Key For development only - Change this in production\nAUTH_SECRET=uxsjXVPHN038DEYls2Kw0QUgBcXKUyrjv416nIFWPY4= \n \n# PHPMailer\n# SMTP_HOST=smtp.gmail.com or your SMTP host\n# SMTP_USERNAME=john.doe@gmail.com or your SMTP username\n# SMTP_PASSWORD=123456\n# SMTP_PORT=587 for TLS, 465 for SSL or your SMTP port\n# SMTP_ENCRYPTION=ssl or tls\n# MAIL_FROM=john.doe@gmail.com\n# MAIL_FROM_NAME="John Doe"\n\n# SHOW ERRORS - Set to true to show errors in the browser for development only - Change this in production to false\nSHOW_ERRORS=true\n\n# ChatGPT API Key\n# CHATGPT_API_KEY=sk-your-api-key\n\n# APP TIMEZONE - Set your application timezone - Default is "UTC"\nAPP_TIMEZONE="UTC"';if(s.prisma){const s=`${'# Environment variables declared in this file are automatically made available to Prisma.\n# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema\n\n# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.\n# See the documentation for all the connection string options: https://pris.ly/d/connection-strings\n\nDATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"'}\n\n${i}`;await createOrUpdateEnvFile(e,s)}else await createOrUpdateEnvFile(e,i);await createUpdateGitignoreFile(e,["vendor",".env","node_modules"])}async function getAnswer(e={}){var s,n,t,i,c,r,o,a,l,p,d,u,h;const g=[];e.projectName||g.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.backendOnly||g.push({type:"toggle",name:"backendOnly",message:"Would you like to create a backend-only project?",initial:!1,active:"Yes",inactive:"No"});const m=()=>{process.exit(0)},f=await prompts(g,{onCancel:m}),y=[];f.backendOnly||e.backendOnly?(e.swaggerDocs||y.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||y.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||y.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"}),e.docker||y.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"})):(e.swaggerDocs||y.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.tailwindcss||y.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!0,active:"Yes",inactive:"No"}),e.websocket||y.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||y.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"}),e.docker||y.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"}));const w=await prompts(y,{onCancel:m});return{projectName:f.projectName?String(f.projectName).trim().replace(/ /g,"-"):null!==(s=e.projectName)&&void 0!==s?s:"my-app",backendOnly:null!==(t=null!==(n=f.backendOnly)&&void 0!==n?n:e.backendOnly)&&void 0!==t&&t,swaggerDocs:null!==(c=null!==(i=w.swaggerDocs)&&void 0!==i?i:e.swaggerDocs)&&void 0!==c&&c,tailwindcss:null!==(o=null!==(r=w.tailwindcss)&&void 0!==r?r:e.tailwindcss)&&void 0!==o&&o,websocket:null!==(l=null!==(a=w.websocket)&&void 0!==a?a:e.websocket)&&void 0!==l&&l,prisma:null!==(d=null!==(p=w.prisma)&&void 0!==p?p:e.prisma)&&void 0!==d&&d,docker:null!==(h=null!==(u=w.docker)&&void 0!==u?u:e.docker)&&void 0!==h&&h}}async function installDependencies(e,s,n=!1){fs.existsSync(path.join(e,"package.json"))||execSync("npm init -y",{stdio:"inherit",cwd:e}),s.forEach((e=>{}));const t=`npm install ${n?"--save-dev":""} ${s.join(" ")}`;execSync(t,{stdio:"inherit",cwd:e})}async function uninstallDependencies(e,s,n=!1){s.forEach((e=>{}));const t=`npm uninstall ${n?"--save-dev":"--save"} ${s.join(" ")}`;execSync(t,{stdio:"inherit",cwd:e})}function fetchPackageVersion(e){return new Promise(((s,n)=>{https.get(`https://registry.npmjs.org/${e}`,(e=>{let t="";e.on("data",(e=>t+=e)),e.on("end",(()=>{try{const e=JSON.parse(t);s(e["dist-tags"].latest)}catch(e){n(new Error("Failed to parse JSON response"))}}))})).on("error",(e=>n(e)))}))}const readJsonFile=e=>{const s=fs.readFileSync(e,"utf8");return JSON.parse(s)};function compareVersions(e,s){const n=e.split(".").map(Number),t=s.split(".").map(Number);for(let e=0;e<n.length;e++){if(n[e]>t[e])return 1;if(n[e]<t[e])return-1}return 0}function getInstalledPackageVersion(e){try{const s=execSync(`npm list -g ${e} --depth=0`).toString().match(new RegExp(`${e}@(\\d+\\.\\d+\\.\\d+)`));return s?s[1]:null}catch(e){return null}}async function main(){var e,s,n,t,i,c,r,o,a;try{const l=process.argv.slice(2);let p=l[0],d=null;if(p){const a={projectName:p,backendOnly:l.includes("--backend-only"),swaggerDocs:l.includes("--swagger-docs"),tailwindcss:l.includes("--tailwindcss"),websocket:l.includes("--websocket"),prisma:l.includes("--prisma"),docker:l.includes("--docker")};if(d=await getAnswer(a),null===d)return;const u=process.cwd(),h=path.join(u,"prisma-php.json"),g=readJsonFile(h);let m=[];null===(e=g.excludeFiles)||void 0===e||e.map((e=>{const s=path.join(u,e);fs.existsSync(s)&&m.push(s.replace(/\\/g,"/"))})),updateAnswer={projectName:p,backendOnly:null!==(s=null==d?void 0:d.backendOnly)&&void 0!==s&&s,swaggerDocs:null!==(n=null==d?void 0:d.swaggerDocs)&&void 0!==n&&n,tailwindcss:null!==(t=null==d?void 0:d.tailwindcss)&&void 0!==t&&t,websocket:null!==(i=null==d?void 0:d.websocket)&&void 0!==i&&i,prisma:null!==(c=null==d?void 0:d.prisma)&&void 0!==c&&c,docker:null!==(r=null==d?void 0:d.docker)&&void 0!==r&&r,isUpdate:!0,excludeFiles:null!==(o=g.excludeFiles)&&void 0!==o?o:[],excludeFilePath:null!=m?m:[],filePath:u}}else d=await getAnswer();if(null===d)return;const u=await fetchPackageVersion("create-prisma-php-app"),h=getInstalledPackageVersion("create-prisma-php-app");h?-1===compareVersions(h,u)&&(execSync("npm uninstall -g create-prisma-php-app",{stdio:"inherit"}),execSync("npm install -g create-prisma-php-app",{stdio:"inherit"})):execSync("npm install -g create-prisma-php-app",{stdio:"inherit"});const g=await fetchPackageVersion("browser-sync"),m=getInstalledPackageVersion("browser-sync");m?-1===compareVersions(m,g)&&(execSync("npm uninstall -g browser-sync",{stdio:"inherit"}),execSync("npm install -g browser-sync",{stdio:"inherit"})):execSync("npm install -g browser-sync",{stdio:"inherit"}),p||fs.mkdirSync(d.projectName);const f=process.cwd();let y=p?f:path.join(f,d.projectName);p||process.chdir(d.projectName);const w=["typescript","@types/node","ts-node","http-proxy-middleware@^3.0.0","chalk","npm-run-all"];d.swaggerDocs&&w.push("swagger-jsdoc"),d.tailwindcss&&w.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano"),d.websocket&&w.push("chokidar-cli"),d.prisma&&w.push("prisma","@prisma/client"),await installDependencies(y,w,!0),p||execSync("npx tsc --init",{stdio:"inherit"}),d.tailwindcss&&execSync("npx tailwindcss init -p",{stdio:"inherit"}),d.prisma&&(fs.existsSync(path.join(y,"prisma"))||execSync("npx prisma init",{stdio:"inherit"})),await createDirectoryStructure(y,d);const k=path.join(y,"public");if(fs.existsSync(k)||fs.mkdirSync(k),d.swaggerDocs){const e=path.join(y,"src","app","swagger-docs");fs.existsSync(e)||fs.mkdirSync(e,{recursive:!0}),execSync(`git clone https://github.com/TheSteelNinjaCode/prisma-php-swagger-docs.git ${e}`,{stdio:"inherit"})}if(null==updateAnswer?void 0:updateAnswer.isUpdate){const e=[];if(updateAnswer.backendOnly){nonBackendFiles.forEach((e=>{const s=path.join(y,"src","app",e);fs.existsSync(s)&&fs.unlinkSync(s)}));["js","css"].forEach((e=>{const s=path.join(y,"src","app",e);fs.existsSync(s)&&fs.rmSync(s,{recursive:!0,force:!0})}))}if(!updateAnswer.swaggerDocs){const s=path.join(y,"src","app","swagger-docs");fs.existsSync(s)&&fs.rmSync(s,{recursive:!0,force:!0}),e.push("swagger-jsdoc")}if(!updateAnswer.tailwindcss){["postcss.config.js","tailwind.config.js"].forEach((e=>{const s=path.join(y,e);fs.existsSync(s)&&fs.unlinkSync(s)})),e.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano")}if(updateAnswer.websocket||e.push("chokidar-cli"),updateAnswer.prisma||e.push("prisma","@prisma/client"),!updateAnswer.docker){[".dockerignore","docker-compose.yml","Dockerfile","apache.conf"].forEach((e=>{const s=path.join(y,e);fs.existsSync(s)&&fs.unlinkSync(s)}))}e.length>0&&await uninstallDependencies(y,e,!0)}const b=y.replace(/\\/g,"\\"),S=bsConfigUrls(b),j=d.prisma?"src/Lib/Prisma/Classes":"",v={projectName:d.projectName,projectRootPath:b,phpEnvironment:"XAMPP",phpRootPathExe:"C:\\xampp\\php\\php.exe",phpGenerateClassPath:j,bsTarget:S.bsTarget,bsPathRewrite:S.bsPathRewrite,backendOnly:d.backendOnly,swaggerDocs:d.swaggerDocs,tailwindcss:d.tailwindcss,websocket:d.websocket,prisma:d.prisma,docker:d.docker,version:u,excludeFiles:null!==(a=null==updateAnswer?void 0:updateAnswer.excludeFiles)&&void 0!==a?a:[]};fs.writeFileSync(path.join(y,"prisma-php.json"),JSON.stringify(v,null,2),{flag:"w"}),(null==updateAnswer?void 0:updateAnswer.isUpdate)?execSync("C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar update",{stdio:"inherit"}):execSync("C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar install",{stdio:"inherit"})}catch(e){process.exit(1)}}main();
|
|
2
|
+
import{execSync}from"child_process";import fs from"fs";import{fileURLToPath}from"url";import path from"path";import chalk from"chalk";import prompts from"prompts";import https from"https";const __filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename);let updateAnswer=null;const nonBackendFiles=["favicon.ico","\\src\\app\\index.php","metadata.php","not-found.php"],dockerFiles=[".dockerignore","docker-compose.yml","Dockerfile","apache.conf"];function bsConfigUrls(e){const s=e.indexOf("\\htdocs\\");if(-1===s)return{bsTarget:"",bsPathRewrite:{}};const n=e.substring(0,s+"\\htdocs\\".length).replace(/\\/g,"\\\\"),t=e.replace(new RegExp(`^${n}`),"").replace(/\\/g,"/");let i=`http://localhost/${t}`;i=i.endsWith("/")?i.slice(0,-1):i;const c=i.replace(/(?<!:)(\/\/+)/g,"/"),o=t.replace(/\/\/+/g,"/");return{bsTarget:`${c}/`,bsPathRewrite:{"^/":`/${o.startsWith("/")?o.substring(1):o}/`}}}function configureBrowserSyncCommand(e){const s=path.join(e,"settings","bs-config.cjs");return fs.writeFileSync(s,'const { createProxyMiddleware } = require("http-proxy-middleware");\nconst fs = require("fs");\nconst chokidar = require("chokidar");\nconst { execSync } = require("child_process");\n\nconst jsonData = fs.readFileSync("prisma-php.json", "utf8");\nconst config = JSON.parse(jsonData);\n\n// Watch for file changes (create, delete, save)\nconst watcher = chokidar.watch("src/**/*", {\n ignored: /(^|[\\/\\\\])\\../,\n persistent: true,\n usePolling: true,\n interval: 1000,\n});\n\nwatcher\n .on("add", async (path) => {\n execSync("node settings/files-list-config.js");\n })\n .on("change", async (path) => {\n execSync("node settings/files-list-config.js");\n })\n .on("unlink", async (path) => {\n execSync("node settings/files-list-config.js");\n });\n\nmodule.exports = {\n proxy: "http://localhost:3000",\n middleware: [\n (req, res, next) => {\n res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");\n res.setHeader("Pragma", "no-cache");\n res.setHeader("Expires", "0");\n next();\n },\n createProxyMiddleware({\n target: config.bsTarget,\n changeOrigin: true,\n pathRewrite: config.bsPathRewrite,\n }),\n ],\n files: "src/**/*.*",\n notify: false,\n open: false,\n ghostMode: false,\n codeSync: true, // Disable synchronization of code changes across clients\n watchOptions: {\n usePolling: true,\n interval: 1000,\n },\n};',"utf8"),"browser-sync start --config settings/bs-config.cjs"}async function updatePackageJson(e,s){const n=path.join(e,"package.json");if(checkExcludeFiles(n))return;const t=JSON.parse(fs.readFileSync(n,"utf8")),i=configureBrowserSyncCommand(e);t.scripts=Object.assign(Object.assign({},t.scripts),{projectName:"node settings/project-name.cjs"});let c=[];s.tailwindcss&&(t.scripts=Object.assign(Object.assign({},t.scripts),{tailwind:"postcss ./src/app/css/tailwind.css -o ./src/app/css/styles.css --watch"}),c.push("tailwind")),s.websocket&&(t.scripts=Object.assign(Object.assign({},t.scripts),{websocket:"node ./settings/restart-websocket.cjs"}),c.push("websocket")),s.docker&&(t.scripts=Object.assign(Object.assign({},t.scripts),{docker:"docker-compose up"}),c.push("docker")),s.swaggerDocs&&(t.scripts=Object.assign(Object.assign({},t.scripts),{"create-swagger-docs":"node settings/swagger-setup.js"}),c.push("create-swagger-docs"));let o=Object.assign({},t.scripts);o.browserSync=i,o.npmRunAll=c.length>0?`npm-run-all -p ${c.join(" ")}`:'echo "No additional scripts to run"',o.dev="node settings/start-dev.js",t.scripts=o,t.type="module",s.prisma&&(t.prisma={seed:"node prisma/seed.js"}),fs.writeFileSync(n,JSON.stringify(t,null,2))}async function updateComposerJson(e,s){const n=path.join(e,"composer.json");if(checkExcludeFiles(n))return;let t;if(fs.existsSync(n)){{const e=fs.readFileSync(n,"utf8");t=JSON.parse(e)}s.websocket&&(t.require=Object.assign(Object.assign({},t.require),{"cboden/ratchet":"^0.4.4"})),s.prisma&&(t.require=Object.assign(Object.assign({},t.require),{"ramsey/uuid":"5.x-dev","hidehalo/nanoid-php":"1.x-dev"})),fs.writeFileSync(n,JSON.stringify(t,null,2))}}async function updateIndexJsForWebSocket(e,s){if(!s.websocket)return;const n=path.join(e,"src","app","js","index.js");if(checkExcludeFiles(n))return;let t=fs.readFileSync(n,"utf8");t+='\n// WebSocket initialization\nconst ws = new WebSocket("ws://localhost:8080");\n',fs.writeFileSync(n,t,"utf8")}async function createUpdateGitignoreFile(e,s){const n=path.join(e,".gitignore");if(checkExcludeFiles(n))return;let t="";s.forEach((e=>{t.includes(e)||(t+=`\n${e}`)})),t=t.trimStart(),fs.writeFileSync(n,t)}function copyRecursiveSync(e,s,n){var t;const i=fs.existsSync(e),c=i&&fs.statSync(e);if(i&&c&&c.isDirectory()){const i=s.toLowerCase();if(!n.websocket&&i.includes("src\\lib\\websocket"))return;if(!n.prisma&&i.includes("src\\lib\\prisma"))return;if(n.backendOnly&&i.includes("src\\app\\js")||n.backendOnly&&i.includes("src\\app\\css"))return;if(!n.swaggerDocs&&i.includes("src\\app\\swagger-docs"))return;const c=s.replace(/\\/g,"/");if(null===(t=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===t?void 0:t.includes(c))return;fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((t=>{copyRecursiveSync(path.join(e,t),path.join(s,t),n)}))}else{if(checkExcludeFiles(s))return;if(!n.tailwindcss&&(s.includes("tailwind.css")||s.includes("styles.css")))return;if(!n.websocket&&(s.includes("restart-websocket.cjs")||s.includes("restart-websocket.bat")))return;if(!n.docker&&dockerFiles.some((e=>s.includes(e))))return;if(n.backendOnly&&nonBackendFiles.some((e=>s.includes(e))))return;if(!n.backendOnly&&s.includes("route.php"))return;if(!n.swaggerDocs&&s.includes("swagger-setup.js"))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s,n){s.forEach((({srcDir:s,destDir:t})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,t),n)}))}function createOrUpdateTailwindConfig(e){const s=path.join(e,"tailwind.config.js");if(checkExcludeFiles(s))return;let n=fs.readFileSync(s,"utf8");const t=["./src/**/*.{html,js,php}"].map((e=>` "${e}"`)).join(",\n");n=n.replace(/content: \[\],/g,`content: [\n${t}\n],`),fs.writeFileSync(s,n,{flag:"w"})}function modifyPostcssConfig(e){const s=path.join(e,"postcss.config.js");if(checkExcludeFiles(s))return;fs.writeFileSync(s,"export default {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n cssnano: {},\n },\n};",{flag:"w"})}function modifyLayoutPHP(e,s){const n=path.join(e,"src","app","layout.php");if(!checkExcludeFiles(n))try{let e=fs.readFileSync(n,"utf8"),t="";s.backendOnly||(t='\n <link href="<?= $baseUrl; ?>/css/index.css" rel="stylesheet">\n <script src="<?= $baseUrl; ?>/js/index.js"><\/script>');let i="";s.backendOnly||(i=s.tailwindcss?` <link href="<?= $baseUrl; ?>/css/styles.css" rel="stylesheet"> ${t}`:` <script src="https://cdn.tailwindcss.com"><\/script> ${t}`);const c=i.length>0?"\n":"";e=e.replace("</head>",`${i}${c} \x3c!-- Dynamic Head --\x3e\n <?= implode("\\n", $mainLayoutHead); ?>\n</head>`),fs.writeFileSync(n,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const n=path.join(e,".env");checkExcludeFiles(n)||fs.writeFileSync(n,s,{flag:"w"})}function checkExcludeFiles(e){var s,n;return!!(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(null!==(n=null===(s=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===s?void 0:s.includes(e.replace(/\\/g,"/")))&&void 0!==n&&n)}async function createDirectoryStructure(e,s){const n=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"}];(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(n.push({src:"/tsconfig.json",dest:"/tsconfig.json"}),updateAnswer.tailwindcss&&n.push({src:"/postcss.config.js",dest:"/postcss.config.js"},{src:"/tailwind.config.js",dest:"/tailwind.config.js"}));const t=[{srcDir:"/settings",destDir:"/settings"},{srcDir:"/src",destDir:"/src"}];s.backendOnly&&s.swaggerDocs&&t.push({srcDir:"/swagger-docs-layout.php",destDir:"/src/app/layout.php"}),s.prisma&&t.push({srcDir:"/prisma",destDir:"/prisma"}),s.docker&&t.push({srcDir:"/.dockerignore",destDir:"/.dockerignore"},{srcDir:"/docker-compose.yml",destDir:"/docker-compose.yml"},{srcDir:"/Dockerfile",destDir:"/Dockerfile"},{srcDir:"/apache.conf",destDir:"/apache.conf"}),n.forEach((({src:s,dest:n})=>{const t=path.join(__dirname,s),i=path.join(e,n);if(checkExcludeFiles(i))return;const c=fs.readFileSync(t,"utf8");fs.writeFileSync(i,c,{flag:"w"})})),await executeCopy(e,t,s),await updatePackageJson(e,s),await updateComposerJson(e,s),s.backendOnly||await updateIndexJsForWebSocket(e,s),s.tailwindcss&&(createOrUpdateTailwindConfig(e),modifyPostcssConfig(e)),(s.tailwindcss||!s.backendOnly||s.swaggerDocs)&&modifyLayoutPHP(e,s);const i='# Prisma PHP Auth Secret Key For development only - Change this in production\nAUTH_SECRET=uxsjXVPHN038DEYls2Kw0QUgBcXKUyrjv416nIFWPY4= \n \n# PHPMailer\n# SMTP_HOST=smtp.gmail.com or your SMTP host\n# SMTP_USERNAME=john.doe@gmail.com or your SMTP username\n# SMTP_PASSWORD=123456\n# SMTP_PORT=587 for TLS, 465 for SSL or your SMTP port\n# SMTP_ENCRYPTION=ssl or tls\n# MAIL_FROM=john.doe@gmail.com\n# MAIL_FROM_NAME="John Doe"\n\n# SHOW ERRORS - Set to true to show errors in the browser for development only - Change this in production to false\nSHOW_ERRORS=true\n\n# ChatGPT API Key\n# CHATGPT_API_KEY=sk-your-api-key\n\n# APP TIMEZONE - Set your application timezone - Default is "UTC"\nAPP_TIMEZONE="UTC"\n\n# APP ENV - Set your application environment - Default is "development" - Change this in production to "production"\nAPP_ENV=development';if(s.prisma){const s=`${'# Environment variables declared in this file are automatically made available to Prisma.\n# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema\n\n# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.\n# See the documentation for all the connection string options: https://pris.ly/d/connection-strings\n\nDATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"'}\n\n${i}`;await createOrUpdateEnvFile(e,s)}else await createOrUpdateEnvFile(e,i);await createUpdateGitignoreFile(e,["vendor",".env","node_modules"])}async function getAnswer(e={}){var s,n,t,i,c,o,r,a,l,p,d,u,h;const g=[];e.projectName||g.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.backendOnly||g.push({type:"toggle",name:"backendOnly",message:"Would you like to create a backend-only project?",initial:!1,active:"Yes",inactive:"No"});const m=()=>{process.exit(0)},f=await prompts(g,{onCancel:m}),y=[];f.backendOnly||e.backendOnly?(e.swaggerDocs||y.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||y.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||y.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"}),e.docker||y.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"})):(e.swaggerDocs||y.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.tailwindcss||y.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!0,active:"Yes",inactive:"No"}),e.websocket||y.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||y.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"}),e.docker||y.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"}));const w=await prompts(y,{onCancel:m});return{projectName:f.projectName?String(f.projectName).trim().replace(/ /g,"-"):null!==(s=e.projectName)&&void 0!==s?s:"my-app",backendOnly:null!==(t=null!==(n=f.backendOnly)&&void 0!==n?n:e.backendOnly)&&void 0!==t&&t,swaggerDocs:null!==(c=null!==(i=w.swaggerDocs)&&void 0!==i?i:e.swaggerDocs)&&void 0!==c&&c,tailwindcss:null!==(r=null!==(o=w.tailwindcss)&&void 0!==o?o:e.tailwindcss)&&void 0!==r&&r,websocket:null!==(l=null!==(a=w.websocket)&&void 0!==a?a:e.websocket)&&void 0!==l&&l,prisma:null!==(d=null!==(p=w.prisma)&&void 0!==p?p:e.prisma)&&void 0!==d&&d,docker:null!==(h=null!==(u=w.docker)&&void 0!==u?u:e.docker)&&void 0!==h&&h}}async function installDependencies(e,s,n=!1){fs.existsSync(path.join(e,"package.json"))||execSync("npm init -y",{stdio:"inherit",cwd:e}),s.forEach((e=>{}));const t=`npm install ${n?"--save-dev":""} ${s.join(" ")}`;execSync(t,{stdio:"inherit",cwd:e})}async function uninstallDependencies(e,s,n=!1){s.forEach((e=>{}));const t=`npm uninstall ${n?"--save-dev":"--save"} ${s.join(" ")}`;execSync(t,{stdio:"inherit",cwd:e})}function fetchPackageVersion(e){return new Promise(((s,n)=>{https.get(`https://registry.npmjs.org/${e}`,(e=>{let t="";e.on("data",(e=>t+=e)),e.on("end",(()=>{try{const e=JSON.parse(t);s(e["dist-tags"].latest)}catch(e){n(new Error("Failed to parse JSON response"))}}))})).on("error",(e=>n(e)))}))}const readJsonFile=e=>{const s=fs.readFileSync(e,"utf8");return JSON.parse(s)};function compareVersions(e,s){const n=e.split(".").map(Number),t=s.split(".").map(Number);for(let e=0;e<n.length;e++){if(n[e]>t[e])return 1;if(n[e]<t[e])return-1}return 0}function getInstalledPackageVersion(e){try{const s=execSync(`npm list -g ${e} --depth=0`).toString().match(new RegExp(`${e}@(\\d+\\.\\d+\\.\\d+)`));return s?s[1]:null}catch(e){return null}}async function main(){var e,s,n,t,i,c,o,r,a;try{const l=process.argv.slice(2);let p=l[0],d=null;if(p){const a={projectName:p,backendOnly:l.includes("--backend-only"),swaggerDocs:l.includes("--swagger-docs"),tailwindcss:l.includes("--tailwindcss"),websocket:l.includes("--websocket"),prisma:l.includes("--prisma"),docker:l.includes("--docker")};if(d=await getAnswer(a),null===d)return;const u=process.cwd(),h=path.join(u,"prisma-php.json"),g=readJsonFile(h);let m=[];null===(e=g.excludeFiles)||void 0===e||e.map((e=>{const s=path.join(u,e);fs.existsSync(s)&&m.push(s.replace(/\\/g,"/"))})),updateAnswer={projectName:p,backendOnly:null!==(s=null==d?void 0:d.backendOnly)&&void 0!==s&&s,swaggerDocs:null!==(n=null==d?void 0:d.swaggerDocs)&&void 0!==n&&n,tailwindcss:null!==(t=null==d?void 0:d.tailwindcss)&&void 0!==t&&t,websocket:null!==(i=null==d?void 0:d.websocket)&&void 0!==i&&i,prisma:null!==(c=null==d?void 0:d.prisma)&&void 0!==c&&c,docker:null!==(o=null==d?void 0:d.docker)&&void 0!==o&&o,isUpdate:!0,excludeFiles:null!==(r=g.excludeFiles)&&void 0!==r?r:[],excludeFilePath:null!=m?m:[],filePath:u}}else d=await getAnswer();if(null===d)return;const u=await fetchPackageVersion("create-prisma-php-app"),h=getInstalledPackageVersion("create-prisma-php-app");h?-1===compareVersions(h,u)&&(execSync("npm uninstall -g create-prisma-php-app",{stdio:"inherit"}),execSync("npm install -g create-prisma-php-app",{stdio:"inherit"})):execSync("npm install -g create-prisma-php-app",{stdio:"inherit"});const g=await fetchPackageVersion("browser-sync"),m=getInstalledPackageVersion("browser-sync");m?-1===compareVersions(m,g)&&(execSync("npm uninstall -g browser-sync",{stdio:"inherit"}),execSync("npm install -g browser-sync",{stdio:"inherit"})):execSync("npm install -g browser-sync",{stdio:"inherit"}),p||fs.mkdirSync(d.projectName);const f=process.cwd();let y=p?f:path.join(f,d.projectName);p||process.chdir(d.projectName);const w=["typescript","@types/node","ts-node","http-proxy-middleware@^3.0.0","chalk","npm-run-all"];d.swaggerDocs&&w.push("swagger-jsdoc"),d.tailwindcss&&w.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano"),d.websocket&&w.push("chokidar-cli"),d.prisma&&w.push("prisma","@prisma/client"),await installDependencies(y,w,!0),p||execSync("npx tsc --init",{stdio:"inherit"}),d.tailwindcss&&execSync("npx tailwindcss init -p",{stdio:"inherit"}),d.prisma&&(fs.existsSync(path.join(y,"prisma"))||execSync("npx prisma init",{stdio:"inherit"})),await createDirectoryStructure(y,d);const k=path.join(y,"public");if(fs.existsSync(k)||fs.mkdirSync(k),d.swaggerDocs){const e=path.join(y,"src","app","swagger-docs");fs.existsSync(e)||fs.mkdirSync(e,{recursive:!0}),execSync(`git clone https://github.com/TheSteelNinjaCode/prisma-php-swagger-docs.git ${e}`,{stdio:"inherit"})}if(null==updateAnswer?void 0:updateAnswer.isUpdate){const e=[];if(updateAnswer.backendOnly){nonBackendFiles.forEach((e=>{const s=path.join(y,"src","app",e);fs.existsSync(s)&&fs.unlinkSync(s)}));["js","css"].forEach((e=>{const s=path.join(y,"src","app",e);fs.existsSync(s)&&fs.rmSync(s,{recursive:!0,force:!0})}))}if(!updateAnswer.swaggerDocs){const s=path.join(y,"src","app","swagger-docs");fs.existsSync(s)&&fs.rmSync(s,{recursive:!0,force:!0});["swagger-setup.js"].forEach((e=>{const s=path.join(y,"settings",e);fs.existsSync(s)&&fs.unlinkSync(s)})),e.push("swagger-jsdoc")}if(!updateAnswer.tailwindcss){["postcss.config.js","tailwind.config.js"].forEach((e=>{const s=path.join(y,e);fs.existsSync(s)&&fs.unlinkSync(s)})),e.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano")}if(updateAnswer.websocket||e.push("chokidar-cli"),updateAnswer.prisma||e.push("prisma","@prisma/client"),!updateAnswer.docker){[".dockerignore","docker-compose.yml","Dockerfile","apache.conf"].forEach((e=>{const s=path.join(y,e);fs.existsSync(s)&&fs.unlinkSync(s)}))}e.length>0&&await uninstallDependencies(y,e,!0)}const S=y.replace(/\\/g,"\\"),b=bsConfigUrls(S),j=d.prisma?"src/Lib/Prisma/Classes":"",v={projectName:d.projectName,projectRootPath:S,phpEnvironment:"XAMPP",phpRootPathExe:"C:\\xampp\\php\\php.exe",phpGenerateClassPath:j,bsTarget:b.bsTarget,bsPathRewrite:b.bsPathRewrite,backendOnly:d.backendOnly,swaggerDocs:d.swaggerDocs,tailwindcss:d.tailwindcss,websocket:d.websocket,prisma:d.prisma,docker:d.docker,version:u,excludeFiles:null!==(a=null==updateAnswer?void 0:updateAnswer.excludeFiles)&&void 0!==a?a:[]};fs.writeFileSync(path.join(y,"prisma-php.json"),JSON.stringify(v,null,2),{flag:"w"}),(null==updateAnswer?void 0:updateAnswer.isUpdate)?execSync("C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar update",{stdio:"inherit"}):execSync("C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar install",{stdio:"inherit"})}catch(e){process.exit(1)}}main();
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { readdirSync, statSync, existsSync, writeFileSync } from "fs";
|
|
2
|
+
import path, { dirname } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
|
|
5
|
+
// Define __dirname equivalent in ES modules
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
|
|
9
|
+
const dirPath = path.join(__dirname, "..", "src", "app");
|
|
10
|
+
const jsonFilePath = path.join(__dirname, "files-list.json");
|
|
11
|
+
|
|
12
|
+
// Function to get all files in the directory
|
|
13
|
+
const getAllFiles = (dirPath) => {
|
|
14
|
+
const files = [];
|
|
15
|
+
|
|
16
|
+
// Check if directory exists before reading
|
|
17
|
+
if (!existsSync(dirPath)) {
|
|
18
|
+
console.error(`Directory not found: ${dirPath}`);
|
|
19
|
+
return files; // Return an empty array if the directory doesn't exist
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const items = readdirSync(dirPath);
|
|
23
|
+
items.forEach((item) => {
|
|
24
|
+
const fullPath = path.join(dirPath, item);
|
|
25
|
+
if (statSync(fullPath).isDirectory()) {
|
|
26
|
+
files.push(...getAllFiles(fullPath)); // Recursive call for subdirectories
|
|
27
|
+
} else {
|
|
28
|
+
// Generate the relative path and ensure it starts with ./src
|
|
29
|
+
const relativePath = `.${path.sep}${path.relative(
|
|
30
|
+
path.join(__dirname, ".."),
|
|
31
|
+
fullPath
|
|
32
|
+
)}`;
|
|
33
|
+
// Replace only the root backslashes with forward slashes and leave inner ones
|
|
34
|
+
files.push(relativePath.replace(/\\/g, "\\").replace(/^\.\.\//, ""));
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return files;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Function to generate the files-list.json
|
|
42
|
+
const generateFileListJson = () => {
|
|
43
|
+
const files = getAllFiles(dirPath);
|
|
44
|
+
|
|
45
|
+
// If files exist, generate JSON file
|
|
46
|
+
if (files.length > 0) {
|
|
47
|
+
writeFileSync(jsonFilePath, JSON.stringify(files, null, 2));
|
|
48
|
+
console.log(
|
|
49
|
+
`File list has been saved to: settings/files-list.json`
|
|
50
|
+
);
|
|
51
|
+
} else {
|
|
52
|
+
console.error("No files found to save in the JSON file.");
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Main function
|
|
57
|
+
async function processDirectory() {
|
|
58
|
+
generateFileListJson();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
processDirectory();
|
|
@@ -126,7 +126,7 @@ async function startDev() {
|
|
|
126
126
|
|
|
127
127
|
if (!browserSyncReady) {
|
|
128
128
|
browserSyncReady = true;
|
|
129
|
-
// Start
|
|
129
|
+
// Start npmRunAll after browserSync is ready
|
|
130
130
|
startDevProcess();
|
|
131
131
|
}
|
|
132
132
|
} else if (REGEX_PATTERNS.BROWSERSYNC.test(line)) {
|
|
@@ -148,10 +148,10 @@ async function startDev() {
|
|
|
148
148
|
console.error(`Failed to start browserSync process: ${err.message}`);
|
|
149
149
|
});
|
|
150
150
|
|
|
151
|
-
// Function to start
|
|
151
|
+
// Function to start npmRunAll process
|
|
152
152
|
function startDevProcess() {
|
|
153
|
-
console.log("Starting
|
|
154
|
-
devProcess = spawn("npm", ["run", "
|
|
153
|
+
console.log("Starting npmRunAll...");
|
|
154
|
+
devProcess = spawn("npm", ["run", "npmRunAll"], { shell: true });
|
|
155
155
|
|
|
156
156
|
devProcess.stdout.on("data", (data) => {
|
|
157
157
|
process.stdout.write(data);
|
|
@@ -72,6 +72,19 @@ class Boom
|
|
|
72
72
|
return new self(401, $message, $details);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Factory method for 402 Payment Required.
|
|
77
|
+
*
|
|
78
|
+
* @param string $message Error message.
|
|
79
|
+
* @param array $details Additional error details.
|
|
80
|
+
*
|
|
81
|
+
* @return self
|
|
82
|
+
*/
|
|
83
|
+
public static function paymentRequired(string $message = 'Payment Required', array $details = []): self
|
|
84
|
+
{
|
|
85
|
+
return new self(402, $message, $details);
|
|
86
|
+
}
|
|
87
|
+
|
|
75
88
|
/**
|
|
76
89
|
* Factory method for 403 Forbidden.
|
|
77
90
|
*
|
|
@@ -98,6 +111,32 @@ class Boom
|
|
|
98
111
|
return new self(404, $message, $details);
|
|
99
112
|
}
|
|
100
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Factory method for 405 Method Not Allowed.
|
|
116
|
+
*
|
|
117
|
+
* @param string $message Error message.
|
|
118
|
+
* @param array $details Additional error details.
|
|
119
|
+
*
|
|
120
|
+
* @return self
|
|
121
|
+
*/
|
|
122
|
+
public static function methodNotAllowed(string $message = 'Method Not Allowed', array $details = []): self
|
|
123
|
+
{
|
|
124
|
+
return new self(405, $message, $details);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Factory method for 406 Not Acceptable.
|
|
129
|
+
*
|
|
130
|
+
* @param string $message Error message.
|
|
131
|
+
* @param array $details Additional error details.
|
|
132
|
+
*
|
|
133
|
+
* @return self
|
|
134
|
+
*/
|
|
135
|
+
public static function notAcceptable(string $message = 'Not Acceptable', array $details = []): self
|
|
136
|
+
{
|
|
137
|
+
return new self(406, $message, $details);
|
|
138
|
+
}
|
|
139
|
+
|
|
101
140
|
/**
|
|
102
141
|
* Factory method for 500 Internal Server Error.
|
|
103
142
|
*
|
package/dist/src/app/layout.php
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<meta name="pp-description" content="<?= htmlspecialchars($metadata['description']); ?>">
|
|
8
8
|
<title><?= htmlspecialchars($metadata['title']); ?></title>
|
|
9
|
-
<link rel="icon" href="<?= $baseUrl;
|
|
9
|
+
<link rel="icon" href="<?= $baseUrl; ?>/favicon.ico" type="image/x-icon">
|
|
10
10
|
</head>
|
|
11
11
|
|
|
12
12
|
<body>
|
package/dist/tailwind.config.js
CHANGED
package/dist/tsconfig.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
|
12
12
|
|
|
13
13
|
/* Language and Environment */
|
|
14
|
-
"target": "
|
|
14
|
+
"target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
|
15
15
|
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
|
16
16
|
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
|
17
17
|
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
|
26
26
|
|
|
27
27
|
/* Modules */
|
|
28
|
-
"module": "
|
|
28
|
+
"module": "NodeNext" /* Specify what module code is generated. */,
|
|
29
29
|
// "rootDir": "./", /* Specify the root folder within your source files. */
|
|
30
30
|
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
|
31
31
|
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
|
40
40
|
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
|
41
41
|
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
|
42
|
-
|
|
42
|
+
"resolveJsonModule": true /* Enable importing .json files. */,
|
|
43
43
|
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
|
44
44
|
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
|
45
45
|
|
|
@@ -77,12 +77,12 @@
|
|
|
77
77
|
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
|
78
78
|
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
|
79
79
|
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
|
80
|
-
"esModuleInterop": true
|
|
80
|
+
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
|
|
81
81
|
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
|
82
|
-
"forceConsistentCasingInFileNames": true
|
|
82
|
+
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
|
83
83
|
|
|
84
84
|
/* Type Checking */
|
|
85
|
-
"strict": true
|
|
85
|
+
"strict": true /* Enable all strict type-checking options. */,
|
|
86
86
|
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
|
87
87
|
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
|
88
88
|
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
|
@@ -104,8 +104,8 @@
|
|
|
104
104
|
|
|
105
105
|
/* Completeness */
|
|
106
106
|
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
|
107
|
-
"skipLibCheck": true
|
|
107
|
+
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
|
108
108
|
},
|
|
109
|
-
"include": ["src/**/*.ts"],
|
|
110
|
-
"exclude": ["node_modules"]
|
|
109
|
+
"include": ["src/**/*.ts", "settings/**/*.ts"],
|
|
110
|
+
"exclude": ["node_modules", "vendor"]
|
|
111
111
|
}
|