create-prisma-php-app 1.26.0 → 1.26.2

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.
@@ -161,7 +161,7 @@ function getFilePrecedence()
161
161
 
162
162
  function uriExtractor(string $scriptUrl): string
163
163
  {
164
- $projectName = PrismaPHPSettings::$option['projectName'] ?? '';
164
+ $projectName = PrismaPHPSettings::$option->projectName ?? '';
165
165
  if (empty($projectName)) {
166
166
  return "/";
167
167
  }
@@ -675,7 +675,7 @@ try {
675
675
  authenticateUserToken();
676
676
 
677
677
  if (empty($_contentToInclude)) {
678
- if (!Request::$isXFileRequest && PrismaPHPSettings::$option['backendOnly']) {
678
+ if (!Request::$isXFileRequest && PrismaPHPSettings::$option->backendOnly) {
679
679
  // Set the header and output a JSON response for permission denied
680
680
  header('Content-Type: application/json');
681
681
  echo json_encode([
@@ -701,7 +701,7 @@ try {
701
701
  }
702
702
  exit;
703
703
  }
704
- } else if (PrismaPHPSettings::$option['backendOnly']) {
704
+ } else if (PrismaPHPSettings::$option->backendOnly) {
705
705
  // Set the header and output a JSON response for file not found
706
706
  header('Content-Type: application/json');
707
707
  echo json_encode([
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
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 t=e.substring(0,s+"\\htdocs\\".length).replace(/\\/g,"\\\\"),n=e.replace(new RegExp(`^${t}`),"").replace(/\\/g,"/");let c=`http://localhost/${n}`;c=c.endsWith("/")?c.slice(0,-1):c;const i=c.replace(/(?<!:)(\/\/+)/g,"/"),o=n.replace(/\/\/+/g,"/");return{bsTarget:`${i}/`,bsPathRewrite:{"^/":`/${o.startsWith("/")?o.substring(1):o}/`}}}async function updatePackageJson(e,s){const t=path.join(e,"package.json");if(checkExcludeFiles(t))return;const n=JSON.parse(fs.readFileSync(t,"utf8"));n.scripts={...n.scripts,projectName:"tsx settings/project-name.ts"};let c=[];s.tailwindcss&&(n.scripts={...n.scripts,tailwind:"postcss src/app/css/tailwind.css -o src/app/css/styles.css --watch"},c.push("tailwind")),s.websocket&&(n.scripts={...n.scripts,websocket:"tsx settings/restart-websocket.ts"},c.push("websocket")),s.docker&&(n.scripts={...n.scripts,docker:"docker-compose up"},c.push("docker")),s.swaggerDocs&&(n.scripts={...n.scripts,"create-swagger-docs":"tsx settings/swagger-config.ts"});let i={...n.scripts};i.browserSync="tsx settings/bs-config.ts",i.dev=`npm-run-all projectName -p browserSync ${c.join(" ")}`,n.scripts=i,n.type="module",s.prisma&&(n.prisma={seed:"tsx prisma/seed.ts"}),fs.writeFileSync(t,JSON.stringify(n,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={...n.require,"cboden/ratchet":"^0.4.4"}),s.prisma&&(n.require={...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\nvar 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="";s.forEach((e=>{n.includes(e)||(n+=`\n${e}`)})),n=n.trimStart(),fs.writeFileSync(t,n)}function copyRecursiveSync(e,s,t){const n=fs.existsSync(e),c=n&&fs.statSync(e);if(n&&c&&c.isDirectory()){const n=s.toLowerCase();if(!t.websocket&&n.includes("src\\lib\\websocket"))return;if(!t.prisma&&n.includes("src\\lib\\prisma"))return;if(t.backendOnly&&n.includes("src\\app\\js")||t.backendOnly&&n.includes("src\\app\\css")||t.backendOnly&&n.includes("src\\app\\assets"))return;if(!t.swaggerDocs&&n.includes("src\\app\\swagger-docs"))return;const c=s.replace(/\\/g,"/");if(updateAnswer?.excludeFilePath?.includes(c))return;fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((n=>{copyRecursiveSync(path.join(e,n),path.join(s,n),t)}))}else{if(checkExcludeFiles(s))return;if(!t.tailwindcss&&(s.includes("tailwind.css")||s.includes("styles.css")))return;if(!t.websocket&&(s.includes("restart-websocket.ts")||s.includes("restart-websocket.bat")))return;if(!t.docker&&dockerFiles.some((e=>s.includes(e))))return;if(t.backendOnly&&nonBackendFiles.some((e=>s.includes(e))))return;if(!t.backendOnly&&s.includes("route.php"))return;if(t.backendOnly&&!t.swaggerDocs&&s.includes("layout.php"))return;if(!t.swaggerDocs&&s.includes("swagger-config.ts"))return;if(t.tailwindcss&&s.includes("index.css"))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s,t){s.forEach((({src:s,dest:n})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,n),t)}))}function createOrUpdateTailwindConfig(e){const s=path.join(e,"tailwind.config.js");if(checkExcludeFiles(s))return;let t=fs.readFileSync(s,"utf8");const n=["./src/**/*.{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"),n="";s.backendOnly||(s.tailwindcss||(n='\n <link href="<?= Request::baseUrl; ?>/css/index.css" rel="stylesheet">'),n+='\n <script src="<?= Request::baseUrl; ?>/js/json5.min.js"><\/script>\n <script src="<?= Request::baseUrl; ?>/js/index.js"><\/script>');let c="";s.backendOnly||(c=s.tailwindcss?` <link href="<?= Request::baseUrl; ?>/css/styles.css" rel="stylesheet"> ${n}`:n);const i=c.length>0?"\n":"";e=e.replace("</head>",`${c}${i} \x3c!-- Dynamic Head --\x3e\n <?= MainLayout::outputHeadScripts() ?>\n</head>`),fs.writeFileSync(t,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const t=path.join(e,".env");checkExcludeFiles(t)||fs.writeFileSync(t,s,{flag:"w"})}function checkExcludeFiles(e){return!!updateAnswer?.isUpdate&&(updateAnswer?.excludeFilePath?.includes(e.replace(/\\/g,"/"))??!1)}async function createDirectoryStructure(e,s){const t=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"},{src:"/tsconfig.json",dest:"/tsconfig.json"}];s.tailwindcss&&t.push({src:"/postcss.config.js",dest:"/postcss.config.js"},{src:"/tailwind.config.js",dest:"/tailwind.config.js"});const n=[{src:"/settings",dest:"/settings"},{src:"/src",dest:"/src"}];s.prisma&&n.push({src:"/prisma",dest:"/prisma"}),s.docker&&n.push({src:"/.dockerignore",dest:"/.dockerignore"},{src:"/docker-compose.yml",dest:"/docker-compose.yml"},{src:"/Dockerfile",dest:"/Dockerfile"},{src:"/apache.conf",dest:"/apache.conf"}),t.forEach((({src:s,dest:t})=>{const n=path.join(__dirname,s),c=path.join(e,t);if(checkExcludeFiles(c))return;const i=fs.readFileSync(n,"utf8");fs.writeFileSync(c,i,{flag:"w"})})),await executeCopy(e,n,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 c='# 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# 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${c}`;await createOrUpdateEnvFile(e,s)}else await createOrUpdateEnvFile(e,c);await createUpdateGitignoreFile(e,["vendor",".env","node_modules"])}async function getAnswer(e={}){const s=[];e.projectName||s.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.backendOnly||s.push({type:"toggle",name:"backendOnly",message:`Would you like to create a ${chalk.blue("backend-only project")}?`,initial:!1,active:"Yes",inactive:"No"});const t=()=>{process.exit(0)},n=await prompts(s,{onCancel:t}),c=[];n.backendOnly||e.backendOnly?(e.swaggerDocs||c.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,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"}),e.docker||c.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"})):(e.swaggerDocs||c.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),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"}),e.docker||c.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"}));const i=await prompts(c,{onCancel:t});return{projectName:n.projectName?String(n.projectName).trim().replace(/ /g,"-"):e.projectName??"my-app",backendOnly:n.backendOnly??e.backendOnly??!1,swaggerDocs:i.swaggerDocs??e.swaggerDocs??!1,tailwindcss:i.tailwindcss??e.tailwindcss??!1,websocket:i.websocket??e.websocket??!1,prisma:i.prisma??e.prisma??!1,docker:i.docker??e.docker??!1}}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)};function compareVersions(e,s){const t=e.split(".").map(Number),n=s.split(".").map(Number);for(let e=0;e<t.length;e++){if(t[e]>n[e])return 1;if(t[e]<n[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}}
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 t=e.substring(0,s+"\\htdocs\\".length).replace(/\\/g,"\\\\"),n=e.replace(new RegExp(`^${t}`),"").replace(/\\/g,"/");let c=`http://localhost/${n}`;c=c.endsWith("/")?c.slice(0,-1):c;const i=c.replace(/(?<!:)(\/\/+)/g,"/"),o=n.replace(/\/\/+/g,"/");return{bsTarget:`${i}/`,bsPathRewrite:{"^/":`/${o.startsWith("/")?o.substring(1):o}/`}}}async function updatePackageJson(e,s){const t=path.join(e,"package.json");if(checkExcludeFiles(t))return;const n=JSON.parse(fs.readFileSync(t,"utf8"));n.scripts={...n.scripts,projectName:"tsx settings/project-name.ts"};let c=[];if(s.tailwindcss&&(n.scripts={...n.scripts,tailwind:"postcss src/app/css/tailwind.css -o src/app/css/styles.css --watch"},c.push("tailwind")),s.websocket&&(n.scripts={...n.scripts,websocket:"tsx settings/restart-websocket.ts"},c.push("websocket")),s.docker&&(n.scripts={...n.scripts,docker:"docker-compose up"},c.push("docker")),s.swaggerDocs){const e=s.prisma?"tsx settings/auto-swagger-docs.ts":"tsx settings/swagger-config.ts";n.scripts={...n.scripts,"create-swagger-docs":e}}let i={...n.scripts};i.browserSync="tsx settings/bs-config.ts",i.dev=`npm-run-all projectName -p browserSync ${c.join(" ")}`,n.scripts=i,n.type="module",s.prisma&&(n.prisma={seed:"tsx prisma/seed.ts"}),fs.writeFileSync(t,JSON.stringify(n,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={...n.require,"cboden/ratchet":"^0.4.4"}),s.prisma&&(n.require={...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\nvar 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="";s.forEach((e=>{n.includes(e)||(n+=`\n${e}`)})),n=n.trimStart(),fs.writeFileSync(t,n)}function copyRecursiveSync(e,s,t){const n=fs.existsSync(e),c=n&&fs.statSync(e);if(n&&c&&c.isDirectory()){const n=s.toLowerCase();if(!t.websocket&&n.includes("src\\lib\\websocket"))return;if(!t.prisma&&n.includes("src\\lib\\prisma"))return;if(t.backendOnly&&n.includes("src\\app\\js")||t.backendOnly&&n.includes("src\\app\\css")||t.backendOnly&&n.includes("src\\app\\assets"))return;if(!t.swaggerDocs&&n.includes("src\\app\\swagger-docs"))return;const c=s.replace(/\\/g,"/");if(updateAnswer?.excludeFilePath?.includes(c))return;fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((n=>{copyRecursiveSync(path.join(e,n),path.join(s,n),t)}))}else{if(checkExcludeFiles(s))return;if(!t.tailwindcss&&(s.includes("tailwind.css")||s.includes("styles.css")))return;if(!t.websocket&&(s.includes("restart-websocket.ts")||s.includes("restart-websocket.bat")))return;if(!t.docker&&dockerFiles.some((e=>s.includes(e))))return;if(t.backendOnly&&nonBackendFiles.some((e=>s.includes(e))))return;if(!t.backendOnly&&s.includes("route.php"))return;if(t.backendOnly&&!t.swaggerDocs&&s.includes("layout.php"))return;if(!t.swaggerDocs&&s.includes("swagger-config.ts"))return;if(t.tailwindcss&&s.includes("index.css"))return;if((!t.swaggerDocs||!t.prisma)&&(s.includes("auto-swagger-docs.ts")||s.includes("prisma-sdk.ts")||s.includes("prisma-schema-config.json")||s.includes("prisma-schema.json")))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s,t){s.forEach((({src:s,dest:n})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,n),t)}))}function createOrUpdateTailwindConfig(e){const s=path.join(e,"tailwind.config.js");if(checkExcludeFiles(s))return;let t=fs.readFileSync(s,"utf8");const n=["./src/**/*.{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"),n="";s.backendOnly||(s.tailwindcss||(n='\n <link href="<?= Request::baseUrl; ?>/css/index.css" rel="stylesheet">'),n+='\n <script src="<?= Request::baseUrl; ?>/js/json5.min.js"><\/script>\n <script src="<?= Request::baseUrl; ?>/js/index.js"><\/script>');let c="";s.backendOnly||(c=s.tailwindcss?` <link href="<?= Request::baseUrl; ?>/css/styles.css" rel="stylesheet"> ${n}`:n);const i=c.length>0?"\n":"";e=e.replace("</head>",`${c}${i} \x3c!-- Dynamic Head --\x3e\n <?= MainLayout::outputHeadScripts() ?>\n</head>`),fs.writeFileSync(t,e,{flag:"w"})}catch(e){}}async function createOrUpdateEnvFile(e,s){const t=path.join(e,".env");checkExcludeFiles(t)||fs.writeFileSync(t,s,{flag:"w"})}function checkExcludeFiles(e){return!!updateAnswer?.isUpdate&&(updateAnswer?.excludeFilePath?.includes(e.replace(/\\/g,"/"))??!1)}async function createDirectoryStructure(e,s){const t=[{src:"/bootstrap.php",dest:"/bootstrap.php"},{src:"/.htaccess",dest:"/.htaccess"},{src:"/../composer.json",dest:"/composer.json"},{src:"/tsconfig.json",dest:"/tsconfig.json"}];s.tailwindcss&&t.push({src:"/postcss.config.js",dest:"/postcss.config.js"},{src:"/tailwind.config.js",dest:"/tailwind.config.js"});const n=[{src:"/settings",dest:"/settings"},{src:"/src",dest:"/src"}];s.prisma&&n.push({src:"/prisma",dest:"/prisma"}),s.docker&&n.push({src:"/.dockerignore",dest:"/.dockerignore"},{src:"/docker-compose.yml",dest:"/docker-compose.yml"},{src:"/Dockerfile",dest:"/Dockerfile"},{src:"/apache.conf",dest:"/apache.conf"}),t.forEach((({src:s,dest:t})=>{const n=path.join(__dirname,s),c=path.join(e,t);if(checkExcludeFiles(c))return;const i=fs.readFileSync(n,"utf8");fs.writeFileSync(c,i,{flag:"w"})})),await executeCopy(e,n,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 c='# 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# 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${c}`;await createOrUpdateEnvFile(e,s)}else await createOrUpdateEnvFile(e,c);await createUpdateGitignoreFile(e,["vendor",".env","node_modules"])}async function getAnswer(e={}){const s=[];e.projectName||s.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.backendOnly||s.push({type:"toggle",name:"backendOnly",message:`Would you like to create a ${chalk.blue("backend-only project")}?`,initial:!1,active:"Yes",inactive:"No"});const t=()=>{process.exit(0)},n=await prompts(s,{onCancel:t}),c=[];n.backendOnly||e.backendOnly?(e.swaggerDocs||c.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,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"}),e.docker||c.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"})):(e.swaggerDocs||c.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),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"}),e.docker||c.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"}));const i=await prompts(c,{onCancel:t});return{projectName:n.projectName?String(n.projectName).trim().replace(/ /g,"-"):e.projectName??"my-app",backendOnly:n.backendOnly??e.backendOnly??!1,swaggerDocs:i.swaggerDocs??e.swaggerDocs??!1,tailwindcss:i.tailwindcss??e.tailwindcss??!1,websocket:i.websocket??e.websocket??!1,prisma:i.prisma??e.prisma??!1,docker:i.docker??e.docker??!1}}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)};function compareVersions(e,s){const t=e.split(".").map(Number),n=s.split(".").map(Number);for(let e=0;e<t.length;e++){if(t[e]>n[e])return 1;if(t[e]<n[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}}
3
3
  /**
4
4
  * Install dependencies in the specified directory.
5
5
  * @param {string} baseDir - The base directory where to install the dependencies.
@@ -129,6 +129,9 @@ async function main() {
129
129
  if (answer.swaggerDocs) {
130
130
  dependencies.push("swagger-jsdoc", "@types/swagger-jsdoc");
131
131
  }
132
+ if (answer.swaggerDocs && answer.prisma) {
133
+ dependencies.push("prompts", "@types/prompts", "@prisma/internals");
134
+ }
132
135
  if (answer.tailwindcss) {
133
136
  dependencies.push(
134
137
  "tailwindcss",
@@ -175,6 +178,11 @@ async function main() {
175
178
  `git clone https://github.com/TheSteelNinjaCode/prisma-php-swagger-docs.git ${swaggerDocsPath}`,
176
179
  { stdio: "inherit" }
177
180
  );
181
+ // delete the folder .git
182
+ fs.rmSync(path.join(swaggerDocsPath, ".git"), {
183
+ recursive: true,
184
+ force: true,
185
+ });
178
186
  }
179
187
  if (updateAnswer?.isUpdate) {
180
188
  const updateUninstallDependencies = [];
@@ -220,7 +228,13 @@ async function main() {
220
228
  console.log(`${file} does not exist.`);
221
229
  }
222
230
  });
223
- updateUninstallDependencies.push("swagger-jsdoc");
231
+ updateUninstallDependencies.push(
232
+ "swagger-jsdoc",
233
+ "@types/swagger-jsdoc",
234
+ "prompts",
235
+ "@types/prompts",
236
+ "@prisma/internals"
237
+ );
224
238
  }
225
239
  if (!updateAnswer.tailwindcss) {
226
240
  const tailwindFiles = ["postcss.config.js", "tailwind.config.js"];
@@ -0,0 +1,520 @@
1
+ import { writeFileSync, mkdirSync, readFileSync } from "fs";
2
+ import { resolve } from "path";
3
+ import chalk from "chalk";
4
+ import { spawn } from "child_process";
5
+ import { prismaSdk } from "./prisma-sdk.js";
6
+ import { swaggerConfig } from "./swagger-config.js";
7
+ import { getFileMeta } from "./utils.js";
8
+ import prismaSchemaConfigJson from "./prisma-schema-config.json";
9
+ import prompts from "prompts";
10
+
11
+ const { __dirname } = getFileMeta();
12
+ const prismaSchemaJsonPath = resolve(__dirname, "./prisma-schema.json");
13
+
14
+ // Function to generate properties for Swagger annotations
15
+ function generateProperties(fields: any[]): {
16
+ properties: string;
17
+ allProperties: string;
18
+ } {
19
+ let properties = "";
20
+ let allProperties = "";
21
+
22
+ fields.forEach((field) => {
23
+ if (field.kind === "object") {
24
+ return;
25
+ }
26
+
27
+ const example = field.example || (field.isId ? 1 : `"${field.name}"`);
28
+ allProperties += `
29
+ * ${field.name}:
30
+ * type: ${field.type.toLowerCase()}
31
+ * example: ${example}`;
32
+
33
+ if (prismaSchemaConfigJson.skipFields.includes(field.name)) {
34
+ return;
35
+ }
36
+
37
+ if (prismaSchemaConfigJson.skipDefaultName.includes(field.default?.name)) {
38
+ return;
39
+ }
40
+
41
+ properties += `
42
+ * ${field.name}:
43
+ * type: ${field.type.toLowerCase()}
44
+ * example: ${example}`;
45
+ });
46
+
47
+ return { properties, allProperties };
48
+ }
49
+
50
+ function toKebabCase(str: string): string {
51
+ return str
52
+ .replace(/([a-z])([A-Z])/g, "$1-$2")
53
+ .replace(/[\s_]+/g, "-")
54
+ .toLowerCase();
55
+ }
56
+
57
+ // Function to generate Swagger annotation for a CRUD operation
58
+ function generateSwaggerAnnotation(modelName: string, fields: any[]): string {
59
+ const { properties, allProperties } = generateProperties(fields);
60
+ const kebabCaseModelName = toKebabCase(modelName);
61
+
62
+ return `/**
63
+ * @swagger
64
+ * tags:
65
+ * name: ${modelName}
66
+ * description: ${modelName} management API
67
+ */
68
+
69
+ /**
70
+ * @swagger
71
+ * /${kebabCaseModelName}:
72
+ * get:
73
+ * summary: Retrieve a list of ${modelName}
74
+ * tags:
75
+ * - ${modelName}
76
+ * responses:
77
+ * 200:
78
+ * description: A list of ${modelName}
79
+ * content:
80
+ * application/json:
81
+ * schema:
82
+ * type: array
83
+ * items:
84
+ * type: object
85
+ * properties:${allProperties}
86
+ */
87
+
88
+ /**
89
+ * @swagger
90
+ * /${kebabCaseModelName}/{id}:
91
+ * get:
92
+ * summary: Retrieve a single ${modelName} by ID
93
+ * tags:
94
+ * - ${modelName}
95
+ * parameters:
96
+ * - in: path
97
+ * name: id
98
+ * required: true
99
+ * description: The ${modelName} ID
100
+ * schema:
101
+ * type: string
102
+ * responses:
103
+ * 200:
104
+ * description: A single ${modelName} object
105
+ * content:
106
+ * application/json:
107
+ * schema:
108
+ * type: object
109
+ * properties:${allProperties}
110
+ * 404:
111
+ * description: ${modelName} not found
112
+ */
113
+
114
+ /**
115
+ * @swagger
116
+ * /${kebabCaseModelName}/create:
117
+ * post:
118
+ * summary: Create a new ${modelName}
119
+ * tags:
120
+ * - ${modelName}
121
+ * requestBody:
122
+ * required: true
123
+ * content:
124
+ * application/json:
125
+ * schema:
126
+ * type: object
127
+ * properties:${properties}
128
+ * responses:
129
+ * 201:
130
+ * description: ${modelName} created successfully.
131
+ */
132
+
133
+ /**
134
+ * @swagger
135
+ * /${kebabCaseModelName}/update/{id}:
136
+ * put:
137
+ * summary: Update a ${modelName} by ID
138
+ * tags:
139
+ * - ${modelName}
140
+ * parameters:
141
+ * - in: path
142
+ * name: id
143
+ * required: true
144
+ * description: The ${modelName} ID
145
+ * schema:
146
+ * type: string
147
+ * requestBody:
148
+ * required: true
149
+ * content:
150
+ * application/json:
151
+ * schema:
152
+ * type: object
153
+ * properties:${properties}
154
+ * responses:
155
+ * 200:
156
+ * description: ${modelName} updated successfully.
157
+ * 404:
158
+ * description: ${modelName} not found
159
+ */
160
+
161
+ /**
162
+ * @swagger
163
+ * /${kebabCaseModelName}/delete/{id}:
164
+ * delete:
165
+ * summary: Delete a ${modelName} by ID
166
+ * tags:
167
+ * - ${modelName}
168
+ * parameters:
169
+ * - in: path
170
+ * name: id
171
+ * required: true
172
+ * description: The ${modelName} ID
173
+ * schema:
174
+ * type: string
175
+ * responses:
176
+ * 204:
177
+ * description: ${modelName} successfully deleted
178
+ * 404:
179
+ * description: ${modelName} not found
180
+ */
181
+ `;
182
+ }
183
+
184
+ // Function to generate dynamic validation rules and request payloads
185
+ function generateValidationAndPayload(fields: any[]) {
186
+ let validations = "";
187
+ let payload = "";
188
+ let variableAssignments = "";
189
+
190
+ fields.forEach((field) => {
191
+ if (field.kind === "object") return; // Skip relations for now
192
+
193
+ // Skip fields that are explicitly marked to be skipped
194
+ if (prismaSchemaConfigJson.skipFields.includes(field.name)) {
195
+ return;
196
+ }
197
+
198
+ // Skip ID fields with auto-creation during creation
199
+ if (prismaSchemaConfigJson.skipDefaultName.includes(field.default?.name)) {
200
+ return;
201
+ }
202
+
203
+ // Define variable assignments
204
+ const variableName = field.name;
205
+ variableAssignments += `$${variableName} = Request::$params->${variableName} ?? null;\n`;
206
+
207
+ // Dynamic validation for required fields (excluding skipped fields)
208
+ if (field.isRequired) {
209
+ const fieldType = field.type.toLowerCase();
210
+ validations += `
211
+ if (!Validator::${fieldType}($${variableName})) {
212
+ Boom::badRequest("Invalid ${variableName}")->toResponse();
213
+ }`;
214
+ }
215
+
216
+ // Prepare payload dynamically
217
+ payload += `'${variableName}' => $${variableName},\n `;
218
+ });
219
+
220
+ return { validations, payload, variableAssignments };
221
+ }
222
+
223
+ // Function to generate dynamic ID validation logic for update and find-by-ID routes
224
+ function generateIdValidationLogic(idField: any) {
225
+ const fieldType = idField.type.toLowerCase();
226
+
227
+ if (["cuid", "uuid", "autoincrement"].includes(idField.default?.name)) {
228
+ return `
229
+ if (!Validator::${fieldType}($id)) {
230
+ Boom::badRequest("Invalid ${idField.name}")->toResponse();
231
+ }`;
232
+ }
233
+
234
+ return ""; // No specific validation needed otherwise
235
+ }
236
+
237
+ // Function to generate endpoints for a model
238
+ function generateEndpoints(modelName: string, fields: any[]): void {
239
+ const kebabCasedModelName = toKebabCase(modelName);
240
+ const camelCaseModelName =
241
+ modelName.charAt(0).toLowerCase() + modelName.slice(1);
242
+ const baseDir = `src/app/${kebabCasedModelName}`;
243
+ const idField = fields.find((field) => field.isId);
244
+ const baseDirPath = resolve(__dirname, `../${baseDir}`);
245
+
246
+ mkdirSync(baseDirPath, { recursive: true });
247
+
248
+ // Endpoint: GET /{kebabCasedModelName}
249
+ const listRoutePath = `${baseDir}/route.php`;
250
+ const listRouteContent = `<?php
251
+
252
+ use Lib\\Prisma\\Classes\\Prisma;
253
+
254
+ $prisma = Prisma::getInstance();
255
+
256
+ $${camelCaseModelName} = $prisma->${camelCaseModelName}->findMany();
257
+ echo json_encode($${camelCaseModelName});`;
258
+ writeFileSync(
259
+ resolve(__dirname, `../${listRoutePath}`),
260
+ listRouteContent,
261
+ "utf-8"
262
+ );
263
+
264
+ // Endpoint: GET /{kebabCasedModelName}/{id}
265
+ const idDir = `${baseDir}/[id]`;
266
+ mkdirSync(resolve(__dirname, `../${idDir}`), { recursive: true });
267
+ const idRoutePath = `${idDir}/route.php`;
268
+ const idValidationLogic = generateIdValidationLogic(idField);
269
+ const idRouteContent = `<?php
270
+
271
+ use Lib\\Prisma\\Classes\\Prisma;
272
+ use Lib\\Validator;
273
+ use Lib\\Headers\\Boom;
274
+ use Lib\\Request;
275
+
276
+ $prisma = Prisma::getInstance();
277
+ $id = Request::$dynamicParams->id ?? null;
278
+ ${idValidationLogic}
279
+
280
+ $${camelCaseModelName} = $prisma->${camelCaseModelName}->findUnique([
281
+ 'where' => [
282
+ 'id' => $id
283
+ ]
284
+ ]);
285
+
286
+ if (!$${camelCaseModelName}) {
287
+ Boom::notFound()->toResponse();
288
+ }
289
+ echo json_encode($${camelCaseModelName});`;
290
+
291
+ writeFileSync(
292
+ resolve(__dirname, `../${idRoutePath}`),
293
+ idRouteContent,
294
+ "utf-8"
295
+ );
296
+
297
+ // Endpoint: POST /{kebabCasedModelName}/create
298
+ const {
299
+ validations: createValidations,
300
+ payload: createPayload,
301
+ variableAssignments,
302
+ } = generateValidationAndPayload(fields);
303
+
304
+ const createDir = `${baseDir}/create`;
305
+ mkdirSync(resolve(__dirname, `../${createDir}`), { recursive: true });
306
+ const createRoutePath = `${createDir}/route.php`;
307
+ const createRouteContent = `<?php
308
+
309
+ use Lib\\Prisma\\Classes\\Prisma;
310
+ use Lib\\Validator;
311
+ use Lib\\Headers\\Boom;
312
+ use Lib\\Request;
313
+
314
+ $prisma = Prisma::getInstance();
315
+ ${
316
+ variableAssignments.length > 0
317
+ ? variableAssignments
318
+ : "// Your custom variable assignments here"
319
+ }
320
+ ${
321
+ createValidations.length > 0
322
+ ? createValidations
323
+ : "\n// Your custom validation logic here"
324
+ }
325
+
326
+ $new${modelName} = $prisma->${camelCaseModelName}->create([
327
+ 'data' => [
328
+ ${createPayload}
329
+ ]
330
+ ]);
331
+
332
+ if (!$new${modelName}) {
333
+ Boom::internal()->toResponse();
334
+ }
335
+ echo json_encode($new${modelName});`;
336
+
337
+ writeFileSync(
338
+ resolve(__dirname, `../${createRoutePath}`),
339
+ createRouteContent,
340
+ "utf-8"
341
+ );
342
+
343
+ // Endpoint: PUT /{kebabCasedModelName}/update/{id}
344
+ const { validations: updateValidations, payload: updatePayload } =
345
+ generateValidationAndPayload(fields);
346
+
347
+ const updateDir = `${baseDir}/update/[id]`;
348
+ mkdirSync(resolve(__dirname, `../${updateDir}`), { recursive: true });
349
+ const updateRoutePath = `${updateDir}/route.php`;
350
+ const updateRouteContent = `<?php
351
+
352
+ use Lib\\Prisma\\Classes\\Prisma;
353
+ use Lib\\Validator;
354
+ use Lib\\Headers\\Boom;
355
+ use Lib\\Request;
356
+
357
+ $prisma = Prisma::getInstance();
358
+ $id = Request::$dynamicParams->id ?? null;
359
+ ${
360
+ variableAssignments.length > 0
361
+ ? variableAssignments
362
+ : "// Your custom variable assignments here"
363
+ }
364
+ ${idValidationLogic}
365
+ ${
366
+ updateValidations.length > 0
367
+ ? updateValidations
368
+ : "\n// Your custom validation logic here"
369
+ }
370
+
371
+ $updated${modelName} = $prisma->${camelCaseModelName}->update([
372
+ 'where' => ['id' => $id],
373
+ 'data' => [
374
+ ${updatePayload}
375
+ ]
376
+ ]);
377
+
378
+ if (!$updated${modelName}) {
379
+ Boom::notFound()->toResponse();
380
+ }
381
+ echo json_encode($updated${modelName});`;
382
+
383
+ writeFileSync(
384
+ resolve(__dirname, `../${updateRoutePath}`),
385
+ updateRouteContent,
386
+ "utf-8"
387
+ );
388
+
389
+ // Endpoint: DELETE /{kebabCasedModelName}/delete/{id}
390
+ const deleteDir = `${baseDir}/delete/[id]`;
391
+ mkdirSync(resolve(__dirname, `../${deleteDir}`), { recursive: true });
392
+ const deleteRoutePath = `${deleteDir}/route.php`;
393
+ const deleteRouteContent = `<?php
394
+
395
+ use Lib\\Prisma\\Classes\\Prisma;
396
+ use Lib\\Validator;
397
+ use Lib\\Headers\\Boom;
398
+ use Lib\\Request;
399
+
400
+ $prisma = Prisma::getInstance();
401
+ $id = Request::$dynamicParams->id ?? null;
402
+ ${idValidationLogic}
403
+
404
+ $deleted${modelName} = $prisma->${camelCaseModelName}->delete([
405
+ 'where' => [
406
+ 'id' => $id
407
+ ]
408
+ ]);
409
+
410
+ if (!$deleted${modelName}) {
411
+ Boom::notFound()->toResponse();
412
+ }
413
+ echo json_encode($deleted${modelName});`;
414
+
415
+ writeFileSync(
416
+ resolve(__dirname, `../${deleteRoutePath}`),
417
+ deleteRouteContent,
418
+ "utf-8"
419
+ );
420
+ }
421
+
422
+ async function promptUserForGenerationOptions() {
423
+ const response = await prompts([
424
+ {
425
+ type: "confirm",
426
+ name: "generateEndpoints",
427
+ message: "Do you want to generate endpoints?",
428
+ initial: false,
429
+ },
430
+ {
431
+ type: "confirm",
432
+ name: "generatePhpClasses",
433
+ message: "Do you want to generate PHP classes?",
434
+ initial: false,
435
+ },
436
+ ]);
437
+
438
+ // Update the configuration based on user input
439
+ prismaSchemaConfigJson.generateEndpoints = response.generateEndpoints;
440
+ prismaSchemaConfigJson.generatePhpClasses = response.generatePhpClasses;
441
+
442
+ // Optionally, you can save the updated settings back to the JSON file
443
+ writeFileSync(
444
+ resolve(__dirname, "./prisma-schema-config.json"),
445
+ JSON.stringify(prismaSchemaConfigJson, null, 2),
446
+ "utf-8"
447
+ );
448
+ }
449
+
450
+ // Function to read the updated Prisma JSON schema directly
451
+ function readUpdatedSchema() {
452
+ try {
453
+ const schemaContent = readFileSync(prismaSchemaJsonPath, "utf-8");
454
+ return JSON.parse(schemaContent);
455
+ } catch (error) {
456
+ console.error("Error reading updated schema:", error);
457
+ return null;
458
+ }
459
+ }
460
+
461
+ async function generateSwaggerDocs(modelsToGenerate: string[]): Promise<void> {
462
+ // Read the updated schema directly from the file
463
+ const updatedSchema = readUpdatedSchema();
464
+ if (!updatedSchema) {
465
+ console.error("Failed to read updated JSON schema.");
466
+ return;
467
+ }
468
+
469
+ const models = updatedSchema.datamodel.models;
470
+
471
+ if (modelsToGenerate.includes("all")) {
472
+ models.forEach((model: any) => {
473
+ generateAndSaveSwaggerDocsForModel(model);
474
+ });
475
+ } else {
476
+ modelsToGenerate.forEach((modelName) => {
477
+ const model = models.find((m: any) => m.name.toLowerCase() === modelName);
478
+ if (model) {
479
+ generateAndSaveSwaggerDocsForModel(model);
480
+ } else {
481
+ console.error(`Model "${modelName}" not found in the schema.`);
482
+ }
483
+ });
484
+ }
485
+ }
486
+
487
+ function generateAndSaveSwaggerDocsForModel(model: any): void {
488
+ const kebabCaseModelName = toKebabCase(model.name);
489
+ const swaggerAnnotation = generateSwaggerAnnotation(model.name, model.fields);
490
+ const whereToSave = `${prismaSchemaConfigJson.swaggerDocsDir}/${kebabCaseModelName}.js`;
491
+ const outputFilePath = resolve(__dirname, `../${whereToSave}`);
492
+
493
+ writeFileSync(outputFilePath, swaggerAnnotation, "utf-8");
494
+ console.log(
495
+ `Swagger annotations for model "${model.name}" generated at: ${chalk.blue(
496
+ whereToSave
497
+ )}`
498
+ );
499
+
500
+ if (prismaSchemaConfigJson.generateEndpoints) {
501
+ generateEndpoints(model.name, model.fields);
502
+ }
503
+ }
504
+
505
+ await promptUserForGenerationOptions();
506
+
507
+ const args = process.argv.slice(2);
508
+ const modelsToGenerate =
509
+ args.length > 0 ? args.map((arg) => arg.toLowerCase()) : ["all"];
510
+
511
+ await prismaSdk();
512
+ await generateSwaggerDocs(modelsToGenerate);
513
+ await swaggerConfig();
514
+
515
+ if (prismaSchemaConfigJson.generatePhpClasses) {
516
+ spawn("npx", ["php", "generate", "class"], {
517
+ stdio: "inherit",
518
+ shell: true,
519
+ });
520
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "swaggerDocsDir": "src/app/swagger-docs/apis",
3
+ "skipDefaultName": ["autoincrement", "cuid", "uuid", "now"],
4
+ "skipFields": ["updatedAt"],
5
+ "generateEndpoints": false,
6
+ "generatePhpClasses": true
7
+ }
@@ -0,0 +1,103 @@
1
+ {
2
+ "datamodel": {
3
+ "enums": [],
4
+ "models": []
5
+ },
6
+ "schema": {
7
+ "inputObjectTypes": {},
8
+ "outputObjectTypes": {
9
+ "prisma": [
10
+ {
11
+ "name": "Query",
12
+ "fields": []
13
+ },
14
+ {
15
+ "name": "Mutation",
16
+ "fields": [
17
+ {
18
+ "name": "executeRaw",
19
+ "args": [
20
+ {
21
+ "name": "query",
22
+ "isRequired": true,
23
+ "isNullable": false,
24
+ "inputTypes": [
25
+ {
26
+ "type": "String",
27
+ "location": "scalar",
28
+ "isList": false
29
+ }
30
+ ]
31
+ },
32
+ {
33
+ "name": "parameters",
34
+ "isRequired": false,
35
+ "isNullable": false,
36
+ "inputTypes": [
37
+ {
38
+ "type": "Json",
39
+ "location": "scalar",
40
+ "isList": false
41
+ }
42
+ ]
43
+ }
44
+ ],
45
+ "isNullable": false,
46
+ "outputType": {
47
+ "type": "Json",
48
+ "location": "scalar",
49
+ "isList": false
50
+ }
51
+ },
52
+ {
53
+ "name": "queryRaw",
54
+ "args": [
55
+ {
56
+ "name": "query",
57
+ "isRequired": true,
58
+ "isNullable": false,
59
+ "inputTypes": [
60
+ {
61
+ "type": "String",
62
+ "location": "scalar",
63
+ "isList": false
64
+ }
65
+ ]
66
+ },
67
+ {
68
+ "name": "parameters",
69
+ "isRequired": false,
70
+ "isNullable": false,
71
+ "inputTypes": [
72
+ {
73
+ "type": "Json",
74
+ "location": "scalar",
75
+ "isList": false
76
+ }
77
+ ]
78
+ }
79
+ ],
80
+ "isNullable": false,
81
+ "outputType": {
82
+ "type": "Json",
83
+ "location": "scalar",
84
+ "isList": false
85
+ }
86
+ }
87
+ ]
88
+ }
89
+ ]
90
+ },
91
+ "enumTypes": {}
92
+ },
93
+ "mappings": {
94
+ "modelOperations": [],
95
+ "otherOperations": {
96
+ "read": [],
97
+ "write": [
98
+ "executeRaw",
99
+ "queryRaw"
100
+ ]
101
+ }
102
+ }
103
+ }
@@ -0,0 +1,24 @@
1
+ import { resolve } from "path";
2
+ import { readFileSync, writeFileSync } from "fs";
3
+ import psdk from "@prisma/internals";
4
+ import { getFileMeta } from "./utils.js";
5
+
6
+ const { __dirname } = getFileMeta();
7
+ const { getDMMF } = psdk;
8
+ const schemaPath: string = resolve(__dirname, "../prisma/schema.prisma");
9
+ const prismaSchemaJsonPath: string = resolve(__dirname, "prisma-schema.json");
10
+
11
+ export const prismaSdk = async (): Promise<void> => {
12
+ try {
13
+ const schema = readFileSync(schemaPath, "utf-8");
14
+
15
+ // Parse the schema into DMMF (Data Model Meta Format) and then convert to JSON
16
+ const dmmf = await getDMMF({ datamodel: schema });
17
+
18
+ // Write the DMMF schema to JSON
19
+ writeFileSync(prismaSchemaJsonPath, JSON.stringify(dmmf, null, 2));
20
+ console.log("Schema converted to JSON!");
21
+ } catch (error) {
22
+ console.error("Error parsing schema:", error);
23
+ }
24
+ };
@@ -9,8 +9,8 @@ class MainLayout
9
9
  public static string $children = '';
10
10
  public static string $childLayoutChildren = '';
11
11
 
12
- private static $headScripts = [];
13
- private static $footerScripts = [];
12
+ private static array $headScripts = [];
13
+ private static array $footerScripts = [];
14
14
  private static array $customMetadata = [];
15
15
 
16
16
  /**
@@ -5,10 +5,21 @@ namespace Lib;
5
5
  class PrismaPHPSettings
6
6
  {
7
7
  /**
8
+ * The settings from the prisma-php.json file.
8
9
  *
10
+ * @var \stdClass
11
+ * @access public
12
+ * @static
9
13
  */
10
14
  public static \ArrayObject $option;
11
15
 
16
+ /**
17
+ * The list of route files from the files-list.json file.
18
+ *
19
+ * @var array
20
+ * @access public
21
+ * @static
22
+ */
12
23
  public static array $routeFiles = [];
13
24
 
14
25
  public static function init(): void
@@ -28,7 +39,7 @@ class PrismaPHPSettings
28
39
  if (json_last_error() === JSON_ERROR_NONE) {
29
40
  return new \ArrayObject($decodedJson, \ArrayObject::ARRAY_AS_PROPS);
30
41
  } else {
31
- return new \ArrayObject([]);
42
+ return new \ArrayObject([], \ArrayObject::ARRAY_AS_PROPS);
32
43
  }
33
44
  }
34
45
  }
@@ -17,10 +17,6 @@ class Request
17
17
  * This property is used to hold request parameters that are passed to the request.
18
18
  *
19
19
  * Example usage:
20
- * ```php
21
- * Request::$params = new \ArrayObject(['id' => 123]);
22
- * ```
23
- *
24
20
  * The parameters can be accessed using the following syntax:
25
21
  * ```php
26
22
  * $id = Request::$params['id'];
@@ -36,16 +32,22 @@ class Request
36
32
  * This property is used to hold dynamic parameters that are passed to the request.
37
33
  *
38
34
  * Example usage:
39
- *
40
35
  * Single parameter:
41
36
  * ```php
42
- * Request::$dynamicParams = new \ArrayObject(['id' => 123]);
37
+ * $id = Request::$dynamicParams['id'];
38
+ * OR
39
+ * $id = Request::$dynamicParams->id;
43
40
  * ```
44
41
  *
45
42
  * Multiple parameters:
46
43
  * ```php
47
- * Request::$dynamicParams = new \ArrayObject(['url' => ['123', '456']]);
44
+ * $dynamicParams = Request::$dynamicParams;
45
+ * echo '<pre>';
46
+ * print_r($dynamicParams);
47
+ * echo '</pre>';
48
48
  * ```
49
+ *
50
+ * The above code will output the dynamic parameters as an array, which can be useful for debugging purposes.
49
51
  */
50
52
  public static \ArrayObject $dynamicParams;
51
53
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-prisma-php-app",
3
- "version": "1.26.0",
3
+ "version": "1.26.2",
4
4
  "description": "Prisma-PHP: A Revolutionary Library Bridging PHP with Prisma ORM",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",