create-prisma-php-app 1.9.16 → 1.9.18

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/composer.json CHANGED
@@ -17,6 +17,7 @@
17
17
  "php": "^8.1",
18
18
  "vlucas/phpdotenv": "^5.6@dev",
19
19
  "firebase/php-jwt": "dev-main",
20
- "phpmailer/phpmailer": "^6.9"
20
+ "phpmailer/phpmailer": "^6.9",
21
+ "psr/log": "^3.0"
21
22
  }
22
23
  }
package/dist/.htaccess CHANGED
@@ -14,16 +14,11 @@ RewriteEngine On
14
14
  Header set Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"
15
15
  </IfModule>
16
16
 
17
- # Exclude OPTIONS method from being redirected
18
- RewriteCond %{REQUEST_METHOD} !OPTIONS
19
-
20
- # Redirect all AJAX requests to bootstrap-ajax.php
21
- RewriteCond %{HTTP:X-Requested-With} ^XMLHttpRequest$
22
- RewriteRule ^(.*)$ bootstrap-ajax.php [L,QSA]
23
-
24
- # Redirect all non-file and non-directory requests not starting with src/app/ to src/app/layout.php
25
- RewriteRule !^src/app/ src/app/layout.php [QSA,L]
17
+ # Exclude static files from being redirected
18
+ RewriteCond %{REQUEST_URI} !\.(css|js|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot|ico|pdf)$ [NC]
19
+ RewriteCond %{REQUEST_URI} !^/bootstrap.php
20
+ RewriteRule ^(.*)$ bootstrap.php [QSA,L]
26
21
 
27
22
  # Add this to ensure OPTIONS requests are handled correctly
28
23
  RewriteCond %{REQUEST_METHOD} OPTIONS
29
- RewriteRule ^(.*)$ $1 [R=200,L]
24
+ RewriteRule ^ - [R=200,L]
@@ -7,24 +7,25 @@ if (session_status() == PHP_SESSION_NONE) {
7
7
  require_once __DIR__ . '/settings/paths.php';
8
8
  require_once __DIR__ . '/vendor/autoload.php';
9
9
 
10
- $metadata = require_once __DIR__ . '/src/app/metadata.php';
10
+ use Lib\Middleware\AuthMiddleware;
11
+ use Dotenv\Dotenv;
12
+
13
+ $dotenv = Dotenv::createImmutable(\DOCUMENT_PATH);
14
+ $dotenv->load();
11
15
 
12
16
  function determineContentToInclude()
13
17
  {
14
- global $metadata;
15
-
16
18
  $subject = $_SERVER["SCRIPT_NAME"];
17
- preg_match("/^(.*)\/src\/app\//", $subject, $matches);
18
-
19
+ $dirname = dirname($subject);
19
20
  $requestUri = explode('?', $_SERVER['REQUEST_URI'], 2)[0];
20
21
  $requestUri = rtrim($requestUri, '/');
21
- $requestUri = str_replace($matches[1], '', $requestUri);
22
+ $requestUri = str_replace($dirname, '', $requestUri);
22
23
  $uri = trim($requestUri, '/');
23
- $baseDir = __DIR__ . '/src/app';
24
+ $baseDir = APP_PATH;
24
25
  $includePath = '';
25
26
  $layoutsToInclude = [];
26
- $metadata = $metadata[$uri] ?? $metadata['default'];
27
27
  writeRoutes();
28
+ AuthMiddleware::handle($uri);
28
29
 
29
30
  $isDirectAccessToPrivateRoute = preg_match('/^_/', $uri);
30
31
  if ($isDirectAccessToPrivateRoute) {
@@ -34,7 +35,7 @@ function determineContentToInclude()
34
35
  if ($uri) {
35
36
  $groupFolder = findGroupFolder($uri);
36
37
  if ($groupFolder) {
37
- $path = $baseDir . $groupFolder;
38
+ $path = __DIR__ . $groupFolder;
38
39
  if (file_exists($path)) {
39
40
  $includePath = $path;
40
41
  }
@@ -44,7 +45,7 @@ function determineContentToInclude()
44
45
  $getGroupFolder = getGroupFolder($groupFolder);
45
46
  $modifiedUri = $uri;
46
47
  if (!empty($getGroupFolder)) {
47
- $modifiedUri = $getGroupFolder;
48
+ $modifiedUri = trim($getGroupFolder, "/src/app/");
48
49
  }
49
50
 
50
51
  foreach (explode('/', $modifiedUri) as $segment) {
@@ -98,7 +99,7 @@ function checkForDuplicateRoutes()
98
99
 
99
100
  function writeRoutes()
100
101
  {
101
- $directory = './';
102
+ $directory = './src/app';
102
103
 
103
104
  if (is_dir($directory)) {
104
105
  $filesList = [];
@@ -146,14 +147,18 @@ function matchGroupFolder($constructedPath): ?string
146
147
  $routes = json_decode(file_get_contents(SETTINGS_PATH . "/files-list.json"), true);
147
148
  $bestMatch = null;
148
149
  $normalizedConstructedPath = ltrim(str_replace('\\', '/', $constructedPath), './');
149
- $normalizedConstructedPath = "/$normalizedConstructedPath/index.php";
150
+
151
+ $routeFile = "/src/app/$normalizedConstructedPath/route.php";
152
+ $indexFile = "/src/app/$normalizedConstructedPath/index.php";
150
153
 
151
154
  foreach ($routes as $route) {
152
155
  $normalizedRoute = trim(str_replace('\\', '/', $route), '.');
153
156
  $cleanedRoute = preg_replace('/\/\([^)]+\)/', '', $normalizedRoute);
154
- if ($cleanedRoute === $normalizedConstructedPath) {
157
+ if ($cleanedRoute === $routeFile) {
155
158
  $bestMatch = $normalizedRoute;
156
159
  break;
160
+ } elseif ($cleanedRoute === $indexFile && !$bestMatch) {
161
+ $bestMatch = $normalizedRoute;
157
162
  }
158
163
  }
159
164
 
@@ -172,66 +177,134 @@ function getGroupFolder($uri): string
172
177
  return "";
173
178
  }
174
179
 
175
- $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
176
- $domainName = $_SERVER['HTTP_HOST'];
177
- $scriptPath = dirname($_SERVER['SCRIPT_NAME']) . '/';
178
- $baseUrl = $protocol . $domainName . rtrim($scriptPath, '/') . '/';
179
- $pathname = "";
180
+ function redirect(string $url): void
181
+ {
182
+ header("Location: $url");
183
+ exit;
184
+ }
185
+
186
+ function setupErrorHandling(&$content)
187
+ {
188
+ set_error_handler(function ($severity, $message, $file, $line) use (&$content) {
189
+ $content .= "<div class='error'>Error: {$severity} - {$message} in {$file} on line {$line}</div>";
190
+ });
191
+
192
+ set_exception_handler(function ($exception) use (&$content) {
193
+ $content .= "<div class='error'>Exception: " . htmlspecialchars($exception->getMessage(), ENT_QUOTES, 'UTF-8') . "</div>";
194
+ });
195
+
196
+ register_shutdown_function(function () use (&$content) {
197
+ $error = error_get_last();
198
+ if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_RECOVERABLE_ERROR])) {
199
+ $formattedError = "<div class='error'>Fatal Error: " . htmlspecialchars($error['message'], ENT_QUOTES, 'UTF-8') .
200
+ " in " . htmlspecialchars($error['file'], ENT_QUOTES, 'UTF-8') .
201
+ " on line " . $error['line'] . "</div>";
202
+ $content .= $formattedError;
203
+ modifyOutputLayoutForError($content);
204
+ }
205
+ });
206
+ }
180
207
 
181
208
  ob_start();
209
+ require_once SETTINGS_PATH . '/request-methods.php';
210
+ $metadataArray = require_once APP_PATH . '/metadata.php';
211
+ $metadata = "";
212
+ $pathname = "";
213
+ $content = "";
214
+ $childContent = "";
215
+
216
+ function containsChildContent($filePath)
217
+ {
218
+ $fileContent = file_get_contents($filePath);
219
+ $pattern = '/<\?(?:php)?[^?]*\$childContent[^?]*\?>/is';
220
+
221
+ if (preg_match($pattern, $fileContent)) {
222
+ return true;
223
+ } else {
224
+ return false;
225
+ }
226
+ }
227
+
228
+ function containsContent($filePath)
229
+ {
230
+ $fileContent = file_get_contents($filePath);
231
+ $pattern = '/<\?(?:php\s+)?(?:=|echo|print)\s*\$content\s*;?\s*\?>/i';
232
+ if (preg_match($pattern, $fileContent)) {
233
+ return true;
234
+ } else {
235
+ return false;
236
+ }
237
+ }
182
238
 
183
- set_error_handler(function ($severity, $message, $file, $line) use (&$content) {
184
- echo "<div class='error'>An error occurred: $severity - $message in $file on line $line</div>";
185
- $content .= ob_get_clean();
186
- });
239
+ function modifyOutputLayoutForError($contentToAdd)
240
+ {
241
+ $layoutContent = file_get_contents(APP_PATH . '/layout.php');
242
+ if ($layoutContent !== false) {
243
+ $newBodyContent = "<body class=\"fatal-error\">$contentToAdd</body>";
187
244
 
188
- set_exception_handler(function ($exception) use (&$content) {
189
- echo "<div class='error'>An error occurred: " . htmlspecialchars($exception->getMessage(), ENT_QUOTES, 'UTF-8') . "</div>";
190
- $content .= ob_get_clean();
191
- });
245
+ $modifiedNotFoundContent = preg_replace('~<body.*?>.*?</body>~s', $newBodyContent, $layoutContent);
192
246
 
193
- register_shutdown_function(function () use (&$content) {
194
- $error = error_get_last();
195
- if ($error && ($error['type'] === E_ERROR || $error['type'] === E_PARSE || $error['type'] === E_CORE_ERROR || $error['type'] === E_COMPILE_ERROR)) {
196
- echo "<div class='error'>An error occurred: " . $error['message'] . "</div>";
197
- $content .= ob_get_clean();
247
+ echo $modifiedNotFoundContent;
248
+ exit;
198
249
  }
199
- });
250
+ }
200
251
 
201
252
  try {
202
253
  $result = determineContentToInclude();
203
254
  checkForDuplicateRoutes();
204
255
  $contentToInclude = $result['path'] ?? '';
205
256
  $layoutsToInclude = $result['layouts'] ?? [];
206
- $pathname = $result['uri'] ? "/" . $result['uri'] : "/";
207
- if (!empty($layoutsToInclude))
208
- $isParentLayout = strpos($layoutsToInclude[0], 'src/app/layout.php') !== false;
209
- else
210
- $isParentLayout = false;
257
+ $uri = $result['uri'] ?? '';
258
+ $pathname = $uri ? "/" . $uri : "/";
259
+ $metadata = $metadataArray[$uri] ?? $metadataArray['default'];
260
+ if (!empty($contentToInclude) && basename($contentToInclude) === 'route.php') {
261
+ require_once SETTINGS_PATH . '/route-request.php';
262
+ require_once $contentToInclude;
263
+ exit;
264
+ }
265
+
266
+ $parentLayoutPath = APP_PATH . '/layout.php';
267
+ $isParentLayout = !empty($layoutsToInclude) && strpos($layoutsToInclude[0], 'src/app/layout.php') !== false;
268
+
269
+ if (!containsContent($parentLayoutPath)) {
270
+ $content .= "<div class='error'>The parent layout file does not contain &lt;?php echo \$content ?&gt; Or &lt;?= \$content ?&gt;<br>" . "<strong>$parentLayoutPath</strong></div>";
271
+ }
211
272
 
212
273
  ob_start();
213
274
  if (!empty($contentToInclude)) {
214
275
  if (!$isParentLayout) {
276
+ ob_start();
215
277
  require_once $contentToInclude;
278
+ $childContent = ob_get_clean();
216
279
  }
217
- $childContent = ob_get_clean();
218
- for ($i = count($layoutsToInclude) - 1; $i >= 0; $i--) {
219
- $layoutPath = $layoutsToInclude[$i];
280
+ foreach (array_reverse($layoutsToInclude) as $layoutPath) {
220
281
  ob_start();
221
- require_once $layoutPath;
282
+ if ($parentLayoutPath === $layoutPath) continue;
283
+ if (containsChildContent($layoutPath)) {
284
+ require_once $layoutPath;
285
+ } else {
286
+ $content .= "<div class='error'>The layout file does not contain &lt;?php echo \$childContent ?&gt; Or &lt;?= \$childContent ?&gt<br>" . "<strong>$layoutPath</strong></div>";
287
+ }
222
288
  $childContent = ob_get_clean();
223
289
  }
224
-
225
- if ($isParentLayout) {
226
- require_once $contentToInclude;
227
- }
228
- $content = $childContent;
229
290
  } else {
230
- require_once 'not-found.php';
231
- $notFound = ob_get_clean();
291
+ ob_start();
292
+ require_once APP_PATH . '/not-found.php';
293
+ $childContent = ob_get_clean();
232
294
  }
233
- $content .= ob_get_clean();
295
+
296
+ if ($isParentLayout && !empty($contentToInclude)) {
297
+ ob_start();
298
+ require_once $contentToInclude;
299
+ $childContent = ob_get_clean();
300
+ }
301
+
302
+ $content .= $childContent;
303
+
304
+ ob_start();
305
+ require_once APP_PATH . '/layout.php';
306
+ echo ob_get_clean();
234
307
  } catch (Throwable $e) {
235
- echo "<div class='error'>An error occurred: " . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8') . "</div>";
236
- $content .= ob_get_clean();
308
+ $content .= "<div class='error'>Unhandled Exception: " . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8') . "</div>";
309
+ modifyOutputLayoutForError($content);
237
310
  }
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;function bsConfigUrls(e){const s=e.PROJECT_ROOT_PATH.indexOf("\\htdocs\\");if(-1===s)return{bsTarget:"",bsPathRewrite:{}};const t=e.PROJECT_ROOT_PATH.substring(0,s+"\\htdocs\\".length).replace(/\\/g,"\\\\"),n=e.PROJECT_ROOT_PATH.replace(new RegExp(`^${t}`),"").replace(/\\/g,"/");let i=`http://localhost/${n}`;i=i.endsWith("/")?i.slice(0,-1):i;const c=i.replace(/(?<!:)(\/\/+)/g,"/"),r=n.replace(/\/\/+/g,"/");return{bsTarget:`${c}/`,bsPathRewrite:{"^/":`/${r.startsWith("/")?r.substring(1):r}/`}}}function configureBrowserSyncCommand(e,s){const t=path.join(e,"settings","bs-config.cjs");return fs.writeFileSync(t,'const { createProxyMiddleware } = require("http-proxy-middleware");\n const fs = require("fs");\n \n const jsonData = fs.readFileSync("prisma-php.json", "utf8");\n const config = JSON.parse(jsonData);\n \n module.exports = {\n // First middleware: Set Cache-Control headers\n function(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 // Use the \'middleware\' option to create a proxy that masks the deep URL.\n middleware: [\n // This middleware intercepts requests to the root and proxies them to the deep path.\n createProxyMiddleware("/", {\n target: config.bsTarget,\n changeOrigin: true,\n pathRewrite: config.bsPathRewrite,\n }),\n ],\n proxy: "http://localhost:3000", // Proxy the BrowserSync server.\n files: "src/**/*.*",\n notify: false,\n open: false,\n ghostMode: false,\n };',"utf8"),"browser-sync start --config settings/bs-config.cjs"}async function updatePackageJson(e,s,t){const n=path.join(e,"package.json");if(checkExcludeFiles(n))return;const i=JSON.parse(fs.readFileSync(n,"utf8")),c=configureBrowserSyncCommand(e,s);i.scripts=Object.assign(Object.assign({},i.scripts),{projectName:"node settings/project-name.cjs"});let r=[];t.tailwindcss&&(i.scripts=Object.assign(Object.assign({},i.scripts),{tailwind:"postcss ./src/app/css/tailwind.css -o ./src/app/css/styles.css --watch"}),r.push("tailwind")),t.websocket&&(i.scripts=Object.assign(Object.assign({},i.scripts),{websocket:"node ./settings/restartWebsocket.cjs"}),r.push("websocket")),t.prisma&&(i.scripts=Object.assign(Object.assign({},i.scripts),{postinstall:"prisma generate"}));const a=Object.assign({},i.scripts);a["browser-sync"]=c,a.dev=r.length>0?`npm-run-all --parallel projectName browser-sync ${r.join(" ")}`:"npm-run-all --parallel projectName browser-sync",i.scripts=a,i.type="module",t.prisma&&(i.prisma={seed:"node prisma/seed.js"}),fs.writeFileSync(n,JSON.stringify(i,null,2))}async function updateComposerJson(e,s){const t=path.join(e,"composer.json");if(checkExcludeFiles(t))return;let n;if(fs.existsSync(t)){{const e=fs.readFileSync(t,"utf8");n=JSON.parse(e)}s.websocket&&(n.require=Object.assign(Object.assign({},n.require),{"cboden/ratchet":"^0.4.4"})),s.prisma&&(n.require=Object.assign(Object.assign({},n.require),{"ramsey/uuid":"5.x-dev","hidehalo/nanoid-php":"1.x-dev"})),fs.writeFileSync(t,JSON.stringify(n,null,2))}}async function updateIndexJsForWebSocket(e,s){if(!s.websocket)return;const t=path.join(e,"src","app","js","index.js");if(checkExcludeFiles(t))return;let n=fs.readFileSync(t,"utf8");n+='\n// WebSocket initialization\nconst ws = new WebSocket("ws://localhost:8080");\n',fs.writeFileSync(t,n,"utf8")}async function createUpdateGitignoreFile(e,s){const t=path.join(e,".gitignore");if(checkExcludeFiles(t))return;let n="";fs.existsSync(t)&&(n=fs.readFileSync(t,"utf8")),s.forEach((e=>{n.includes(e)||(n+=`\n${e}`)})),n=n.trimStart(),fs.writeFileSync(t,n)}function copyRecursiveSync(e,s){const t=fs.existsSync(e),n=t&&fs.statSync(e);if(t&&n&&n.isDirectory())fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((t=>{copyRecursiveSync(path.join(e,t),path.join(s,t))}));else{if(checkExcludeFiles(s))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s,t){s.forEach((({srcDir:s,destDir:n})=>{if(!t.prisma&&"/prisma-client-php"===s)return;copyRecursiveSync(path.join(__dirname,s),path.join(e,n))}))}function createOrUpdateTailwindConfig(e){const s=path.join(e,"tailwind.config.js");if(checkExcludeFiles(s))return;let t=fs.readFileSync(s,"utf8");const n=["./src/app/**/*.{html,js,php}"].map((e=>` "${e}"`)).join(",\n");t=t.replace(/content: \[\],/g,`content: [\n${n}\n],`),fs.writeFileSync(s,t,{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 t=path.join(e,"src","app","layout.php");if(!checkExcludeFiles(t))try{let e=fs.readFileSync(t,"utf8");const n='\n <link href="<?php echo $baseUrl; ?>css/index.css" rel="stylesheet">\n <script src="<?php echo $baseUrl; ?>js/index.js"><\/script>\n <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet">',i=s?` <link href="<?php echo $baseUrl; ?>css/styles.css" rel="stylesheet"> ${n}`:` <script src="https://cdn.tailwindcss.com"><\/script> ${n}`;e=e.replace("</head>",`${i}\n</head>`),fs.writeFileSync(t,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const t=path.join(e,".env");if(checkExcludeFiles(t))return;let n=fs.existsSync(t)?fs.readFileSync(t,"utf8"):"";n.includes(s)||(n+=`${""!==n?"\n\n":""}${s}`,fs.writeFileSync(t,n,{flag:"w"}))}function checkExcludeFiles(e){var s,t;return!!(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(null!==(t=null===(s=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===s?void 0:s.includes(e.replace(/\\/g,"/")))&&void 0!==t&&t)}async function createDirectoryStructure(e,s,t){const n=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/bootstrap-ajax.php",dest:"/bootstrap-ajax.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"}];(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(n.push({src:"/.env",dest:"/.env"},{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"}));n.forEach((({src:s,dest:t})=>{const n=path.join(__dirname,s),i=path.join(e,t);if(checkExcludeFiles(i))return;const c=fs.readFileSync(n,"utf8");fs.writeFileSync(i,c,{flag:"w"})})),await executeCopy(e,[{srcDir:"/settings",destDir:"/settings"},{srcDir:"/prisma",destDir:"/prisma"},{srcDir:"/src",destDir:"/src"},{srcDir:"/../vendor",destDir:"/vendor"}],s),await updatePackageJson(e,t,s),await updateComposerJson(e,s),await updateIndexJsForWebSocket(e,s),s.tailwindcss?(createOrUpdateTailwindConfig(e),modifyLayoutPHP(e,!0),modifyPostcssConfig(e)):modifyLayoutPHP(e,!1);await createOrUpdateEnvFile(e,'# 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"'),await createUpdateGitignoreFile(e,["vendor"])}async function getAnswer(e={}){var s,t,n,i;const c=[];e.projectName||c.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.tailwindcss||c.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!0,active:"Yes",inactive:"No"}),e.websocket||c.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||c.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"});const r=c,a=()=>{process.exit(0)};try{const c=await prompts(r,{onCancel:a});return 0===Object.keys(c).length?null:{projectName:c.projectName?String(c.projectName).trim().replace(/ /g,"-"):null!==(s=e.projectName)&&void 0!==s?s:"my-app",tailwindcss:null!==(t=c.tailwindcss)&&void 0!==t?t:e.tailwindcss,websocket:null!==(n=c.websocket)&&void 0!==n?n:e.websocket,prisma:null!==(i=c.prisma)&&void 0!==i?i:e.prisma}}catch(e){return null}}async function installDependencies(e,s,t=!1){fs.existsSync(path.join(e,"package.json"))||execSync("npm init -y",{stdio:"inherit",cwd:e}),s.forEach((e=>{}));const n=`npm install ${t?"--save-dev":""} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}async function uninstallDependencies(e,s,t=!1){s.forEach((e=>{}));const n=`npm uninstall ${t?"--save-dev":"--save"} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}function fetchPackageVersion(e){return new Promise(((s,t)=>{https.get(`https://registry.npmjs.org/${e}`,(e=>{let n="";e.on("data",(e=>n+=e)),e.on("end",(()=>{try{const e=JSON.parse(n);s(e["dist-tags"].latest)}catch(e){t(new Error("Failed to parse JSON response"))}}))})).on("error",(e=>t(e)))}))}const readJsonFile=e=>{const s=fs.readFileSync(e,"utf8");return JSON.parse(s)};async function main(){var e,s,t,n,i,c;try{const r=process.argv.slice(2);let a=r[0],o=null;if(a){const c={projectName:a,tailwindcss:r.includes("--tailwindcss"),websocket:r.includes("--websocket"),prisma:r.includes("--prisma")};if(o=await getAnswer(c),null===o)return;const p=process.cwd(),l=path.join(p,"prisma-php.json"),d=readJsonFile(l);let u=[];null===(e=d.excludeFiles)||void 0===e||e.map((e=>{const s=path.join(p,e);fs.existsSync(s)&&u.push(s.replace(/\\/g,"/"))})),updateAnswer={projectName:a,tailwindcss:null!==(s=null==o?void 0:o.tailwindcss)&&void 0!==s&&s,websocket:null!==(t=null==o?void 0:o.websocket)&&void 0!==t&&t,prisma:null!==(n=null==o?void 0:o.prisma)&&void 0!==n&&n,isUpdate:!0,excludeFiles:null!==(i=d.excludeFiles)&&void 0!==i?i:[],excludeFilePath:null!=u?u:[],filePath:p}}else o=await getAnswer();if(null===o)return;execSync("npm install -g browser-sync",{stdio:"inherit"}),a||fs.mkdirSync(o.projectName);const p=process.cwd();let l=a?p:path.join(p,o.projectName);a||process.chdir(o.projectName);const d=["typescript","@types/node","ts-node","http-proxy-middleware@^2.0.6","npm-run-all"];o.tailwindcss&&d.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano"),o.websocket&&d.push("chokidar-cli"),o.prisma&&d.push("prisma","@prisma/client"),await installDependencies(l,d,!0),a||execSync("npx tsc --init",{stdio:"inherit"}),o.tailwindcss&&execSync("npx tailwindcss init -p",{stdio:"inherit"}),o.prisma&&execSync("npx prisma init",{stdio:"inherit"});const u=l.replace(/\\/g,"\\"),f=o.prisma?"src/Lib/Prisma/Classes":"",h={PROJECT_NAME:o.projectName,PROJECT_ROOT_PATH:u,PHP_ROOT_PATH_EXE:"D:\\\\xampp\\\\php\\\\php.exe",PHP_GENERATE_CLASS_PATH:f};await createDirectoryStructure(l,o,h);const m=path.join(l,"public");if(fs.existsSync(m)||fs.mkdirSync(m),!o.tailwindcss){const e=path.join(l,"src","app","css");["tailwind.css","styles.css"].forEach((s=>{const t=path.join(e,s);fs.existsSync(t)&&fs.unlinkSync(t)}))}if(!o.websocket){const e=path.join(l,"src","Lib","Websocket");fs.existsSync(e)&&fs.rmSync(e,{recursive:!0,force:!0});const s=path.join(l,"settings");["restartWebsocket.cjs","restart_websocket.bat"].forEach((e=>{const t=path.join(s,e);fs.existsSync(t)&&fs.unlinkSync(t)}))}if(!o.prisma){const e=path.join(l,"prisma"),s=path.join(l,"src","Lib","Prisma");fs.existsSync(e)&&fs.rmSync(e,{recursive:!0,force:!0}),fs.existsSync(s)&&fs.rmSync(s,{recursive:!0,force:!0})}if(null==updateAnswer?void 0:updateAnswer.isUpdate){const e=[];if(!updateAnswer.tailwindcss){["postcss.config.js","tailwind.config.js"].forEach((e=>{const s=path.join(l,e);fs.existsSync(s)&&fs.unlinkSync(s)})),e.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano")}updateAnswer.websocket||e.push("chokidar-cli"),updateAnswer.prisma||e.push("prisma","@prisma/client"),e.length>0&&await uninstallDependencies(l,e,!0)}const w=await fetchPackageVersion("create-prisma-php-app"),y=bsConfigUrls(h),g=o.prisma?"src/Lib/Prisma/Classes":"",j={projectName:o.projectName,projectRootPath:u,phpRootPathExe:"D:\\xampp\\php\\php.exe",phpGenerateClassPath:g,bsTarget:y.bsTarget,bsPathRewrite:y.bsPathRewrite,tailwindcss:o.tailwindcss,websocket:o.websocket,prisma:o.prisma,version:w,excludeFiles:null!==(c=null==updateAnswer?void 0:updateAnswer.excludeFiles)&&void 0!==c?c:[]};fs.writeFileSync(path.join(l,"prisma-php.json"),JSON.stringify(j,null,2),{flag:"w"})}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;function bsConfigUrls(e){const s=e.PROJECT_ROOT_PATH.indexOf("\\htdocs\\");if(-1===s)return{bsTarget:"",bsPathRewrite:{}};const t=e.PROJECT_ROOT_PATH.substring(0,s+"\\htdocs\\".length).replace(/\\/g,"\\\\"),n=e.PROJECT_ROOT_PATH.replace(new RegExp(`^${t}`),"").replace(/\\/g,"/");let i=`http://localhost/${n}`;i=i.endsWith("/")?i.slice(0,-1):i;const c=i.replace(/(?<!:)(\/\/+)/g,"/"),r=n.replace(/\/\/+/g,"/");return{bsTarget:`${c}/`,bsPathRewrite:{"^/":`/${r.startsWith("/")?r.substring(1):r}/`}}}function configureBrowserSyncCommand(e,s){const t=path.join(e,"settings","bs-config.cjs");return fs.writeFileSync(t,'const { createProxyMiddleware } = require("http-proxy-middleware");\n const fs = require("fs");\n \n const jsonData = fs.readFileSync("prisma-php.json", "utf8");\n const config = JSON.parse(jsonData);\n \n module.exports = {\n // First middleware: Set Cache-Control headers\n function(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 // Use the \'middleware\' option to create a proxy that masks the deep URL.\n middleware: [\n // This middleware intercepts requests to the root and proxies them to the deep path.\n createProxyMiddleware("/", {\n target: config.bsTarget,\n changeOrigin: true,\n pathRewrite: config.bsPathRewrite,\n }),\n ],\n proxy: "http://localhost:3000", // Proxy the BrowserSync server.\n files: "src/**/*.*",\n notify: false,\n open: false,\n ghostMode: false,\n };',"utf8"),"browser-sync start --config settings/bs-config.cjs"}async function updatePackageJson(e,s,t){const n=path.join(e,"package.json");if(checkExcludeFiles(n))return;const i=JSON.parse(fs.readFileSync(n,"utf8")),c=configureBrowserSyncCommand(e,s);i.scripts=Object.assign(Object.assign({},i.scripts),{projectName:"node settings/project-name.cjs"});let r=[];t.tailwindcss&&(i.scripts=Object.assign(Object.assign({},i.scripts),{tailwind:"postcss ./src/app/css/tailwind.css -o ./src/app/css/styles.css --watch"}),r.push("tailwind")),t.websocket&&(i.scripts=Object.assign(Object.assign({},i.scripts),{websocket:"node ./settings/restart-websocket.cjs"}),r.push("websocket"));const a=Object.assign({},i.scripts);a["browser-sync"]=c,a.dev=r.length>0?`npm-run-all --parallel projectName browser-sync ${r.join(" ")}`:"npm-run-all --parallel projectName browser-sync",i.scripts=a,i.type="module",t.prisma&&(i.prisma={seed:"node prisma/seed.js"}),fs.writeFileSync(n,JSON.stringify(i,null,2))}async function updateComposerJson(e,s){const t=path.join(e,"composer.json");if(checkExcludeFiles(t))return;let n;if(fs.existsSync(t)){{const e=fs.readFileSync(t,"utf8");n=JSON.parse(e)}s.websocket&&(n.require=Object.assign(Object.assign({},n.require),{"cboden/ratchet":"^0.4.4"})),s.prisma&&(n.require=Object.assign(Object.assign({},n.require),{"ramsey/uuid":"5.x-dev","hidehalo/nanoid-php":"1.x-dev"})),fs.writeFileSync(t,JSON.stringify(n,null,2))}}async function updateIndexJsForWebSocket(e,s){if(!s.websocket)return;const t=path.join(e,"src","app","js","index.js");if(checkExcludeFiles(t))return;let n=fs.readFileSync(t,"utf8");n+='\n// WebSocket initialization\nconst ws = new WebSocket("ws://localhost:8080");\n',fs.writeFileSync(t,n,"utf8")}async function createUpdateGitignoreFile(e,s){const t=path.join(e,".gitignore");if(checkExcludeFiles(t))return;let n="";fs.existsSync(t)&&(n=fs.readFileSync(t,"utf8")),s.forEach((e=>{n.includes(e)||(n+=`\n${e}`)})),n=n.trimStart(),fs.writeFileSync(t,n)}function copyRecursiveSync(e,s){const t=fs.existsSync(e),n=t&&fs.statSync(e);if(t&&n&&n.isDirectory())fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((t=>{copyRecursiveSync(path.join(e,t),path.join(s,t))}));else{if(checkExcludeFiles(s))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s,t){s.forEach((({srcDir:s,destDir:n})=>{if(!t.prisma&&"/prisma-client-php"===s)return;copyRecursiveSync(path.join(__dirname,s),path.join(e,n))}))}function createOrUpdateTailwindConfig(e){const s=path.join(e,"tailwind.config.js");if(checkExcludeFiles(s))return;let t=fs.readFileSync(s,"utf8");const n=["./src/app/**/*.{html,js,php}"].map((e=>` "${e}"`)).join(",\n");t=t.replace(/content: \[\],/g,`content: [\n${n}\n],`),fs.writeFileSync(s,t,{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 t=path.join(e,"src","app","layout.php");if(!checkExcludeFiles(t))try{let e=fs.readFileSync(t,"utf8");const n='\n <link href="<?php echo $baseUrl; ?>css/index.css" rel="stylesheet">\n <script src="<?php echo $baseUrl; ?>js/index.js"><\/script>\n <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet">',i=s?` <link href="<?php echo $baseUrl; ?>css/styles.css" rel="stylesheet"> ${n}`:` <script src="https://cdn.tailwindcss.com"><\/script> ${n}`;e=e.replace("</head>",`${i}\n</head>`),fs.writeFileSync(t,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const t=path.join(e,".env");if(checkExcludeFiles(t))return;let n=fs.existsSync(t)?fs.readFileSync(t,"utf8"):"";n.includes(s)||(n+=`${""!==n?"\n\n":""}${s}`,fs.writeFileSync(t,n,{flag:"w"}))}function checkExcludeFiles(e){var s,t;return!!(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(null!==(t=null===(s=null==updateAnswer?void 0:updateAnswer.excludeFilePath)||void 0===s?void 0:s.includes(e.replace(/\\/g,"/")))&&void 0!==t&&t)}async function createDirectoryStructure(e,s,t){const n=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/bootstrap-ajax.php",dest:"/bootstrap-ajax.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"}];(null==updateAnswer?void 0:updateAnswer.isUpdate)&&(n.push({src:"/.env",dest:"/.env"},{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"}));n.forEach((({src:s,dest:t})=>{const n=path.join(__dirname,s),i=path.join(e,t);if(checkExcludeFiles(i))return;const c=fs.readFileSync(n,"utf8");fs.writeFileSync(i,c,{flag:"w"})})),await executeCopy(e,[{srcDir:"/settings",destDir:"/settings"},{srcDir:"/prisma",destDir:"/prisma"},{srcDir:"/src",destDir:"/src"},{srcDir:"/../vendor",destDir:"/vendor"}],s),await updatePackageJson(e,t,s),await updateComposerJson(e,s),await updateIndexJsForWebSocket(e,s),s.tailwindcss?(createOrUpdateTailwindConfig(e),modifyLayoutPHP(e,!0),modifyPostcssConfig(e)):modifyLayoutPHP(e,!1);await createOrUpdateEnvFile(e,'# 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"'),await createUpdateGitignoreFile(e,["vendor"])}async function getAnswer(e={}){var s,t,n,i;const c=[];e.projectName||c.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.tailwindcss||c.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!0,active:"Yes",inactive:"No"}),e.websocket||c.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!0,active:"Yes",inactive:"No"}),e.prisma||c.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!0,active:"Yes",inactive:"No"});const r=c,a=()=>{process.exit(0)};try{const c=await prompts(r,{onCancel:a});return 0===Object.keys(c).length?null:{projectName:c.projectName?String(c.projectName).trim().replace(/ /g,"-"):null!==(s=e.projectName)&&void 0!==s?s:"my-app",tailwindcss:null!==(t=c.tailwindcss)&&void 0!==t?t:e.tailwindcss,websocket:null!==(n=c.websocket)&&void 0!==n?n:e.websocket,prisma:null!==(i=c.prisma)&&void 0!==i?i:e.prisma}}catch(e){return null}}async function installDependencies(e,s,t=!1){fs.existsSync(path.join(e,"package.json"))||execSync("npm init -y",{stdio:"inherit",cwd:e}),s.forEach((e=>{}));const n=`npm install ${t?"--save-dev":""} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}async function uninstallDependencies(e,s,t=!1){s.forEach((e=>{}));const n=`npm uninstall ${t?"--save-dev":"--save"} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}function fetchPackageVersion(e){return new Promise(((s,t)=>{https.get(`https://registry.npmjs.org/${e}`,(e=>{let n="";e.on("data",(e=>n+=e)),e.on("end",(()=>{try{const e=JSON.parse(n);s(e["dist-tags"].latest)}catch(e){t(new Error("Failed to parse JSON response"))}}))})).on("error",(e=>t(e)))}))}const readJsonFile=e=>{const s=fs.readFileSync(e,"utf8");return JSON.parse(s)};async function main(){var e,s,t,n,i,c;try{const r=process.argv.slice(2);let a=r[0],o=null;if(a){const c={projectName:a,tailwindcss:r.includes("--tailwindcss"),websocket:r.includes("--websocket"),prisma:r.includes("--prisma")};if(o=await getAnswer(c),null===o)return;const p=process.cwd(),l=path.join(p,"prisma-php.json"),d=readJsonFile(l);let u=[];null===(e=d.excludeFiles)||void 0===e||e.map((e=>{const s=path.join(p,e);fs.existsSync(s)&&u.push(s.replace(/\\/g,"/"))})),updateAnswer={projectName:a,tailwindcss:null!==(s=null==o?void 0:o.tailwindcss)&&void 0!==s&&s,websocket:null!==(t=null==o?void 0:o.websocket)&&void 0!==t&&t,prisma:null!==(n=null==o?void 0:o.prisma)&&void 0!==n&&n,isUpdate:!0,excludeFiles:null!==(i=d.excludeFiles)&&void 0!==i?i:[],excludeFilePath:null!=u?u:[],filePath:p}}else o=await getAnswer();if(null===o)return;execSync("npm install -g browser-sync",{stdio:"inherit"}),a||fs.mkdirSync(o.projectName);const p=process.cwd();let l=a?p:path.join(p,o.projectName);a||process.chdir(o.projectName);const d=["typescript","@types/node","ts-node","http-proxy-middleware@^2.0.6","npm-run-all"];o.tailwindcss&&d.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano"),o.websocket&&d.push("chokidar-cli"),o.prisma&&d.push("prisma","@prisma/client"),await installDependencies(l,d,!0),a||execSync("npx tsc --init",{stdio:"inherit"}),o.tailwindcss&&execSync("npx tailwindcss init -p",{stdio:"inherit"}),o.prisma&&execSync("npx prisma init",{stdio:"inherit"});const u=l.replace(/\\/g,"\\"),h=o.prisma?"src/Lib/Prisma/Classes":"",f={PROJECT_NAME:o.projectName,PROJECT_ROOT_PATH:u,PHP_ROOT_PATH_EXE:"D:\\\\xampp\\\\php\\\\php.exe",PHP_GENERATE_CLASS_PATH:h};await createDirectoryStructure(l,o,f);const m=path.join(l,"public");if(fs.existsSync(m)||fs.mkdirSync(m),!o.tailwindcss){const e=path.join(l,"src","app","css");["tailwind.css","styles.css"].forEach((s=>{const t=path.join(e,s);fs.existsSync(t)&&fs.unlinkSync(t)}))}if(!o.websocket){const e=path.join(l,"src","Lib","Websocket");fs.existsSync(e)&&fs.rmSync(e,{recursive:!0,force:!0});const s=path.join(l,"settings");["restart-websocket.cjs","restart-websocket.bat"].forEach((e=>{const t=path.join(s,e);fs.existsSync(t)&&fs.unlinkSync(t)}))}if(!o.prisma){const e=path.join(l,"prisma"),s=path.join(l,"src","Lib","Prisma");fs.existsSync(e)&&fs.rmSync(e,{recursive:!0,force:!0}),fs.existsSync(s)&&fs.rmSync(s,{recursive:!0,force:!0})}if(null==updateAnswer?void 0:updateAnswer.isUpdate){const e=[];if(!updateAnswer.tailwindcss){["postcss.config.js","tailwind.config.js"].forEach((e=>{const s=path.join(l,e);fs.existsSync(s)&&fs.unlinkSync(s)})),e.push("tailwindcss","autoprefixer","postcss","postcss-cli","cssnano")}updateAnswer.websocket||e.push("chokidar-cli"),updateAnswer.prisma||e.push("prisma","@prisma/client"),e.length>0&&await uninstallDependencies(l,e,!0)}const w=await fetchPackageVersion("create-prisma-php-app"),y=bsConfigUrls(f),g=o.prisma?"src/Lib/Prisma/Classes":"",j={projectName:o.projectName,projectRootPath:u,phpEnvironment:"XAMPP",phpRootPathExe:"D:\\xampp\\php\\php.exe",phpGenerateClassPath:g,bsTarget:y.bsTarget,bsPathRewrite:y.bsPathRewrite,tailwindcss:o.tailwindcss,websocket:o.websocket,prisma:o.prisma,version:w,excludeFiles:null!==(c=null==updateAnswer?void 0:updateAnswer.excludeFiles)&&void 0!==c?c:[]};fs.writeFileSync(path.join(l,"prisma-php.json"),JSON.stringify(j,null,2),{flag:"w"})}catch(e){process.exit(1)}}main();