create-prisma-php-app 4.0.0-alpha.5 → 4.0.0-alpha.50
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/.htaccess +54 -41
- package/dist/bootstrap.php +143 -98
- package/dist/index.js +819 -343
- package/dist/settings/auto-swagger-docs.ts +196 -95
- package/dist/settings/bs-config.ts +53 -58
- package/dist/settings/files-list.json +1 -1
- package/dist/settings/project-name.ts +2 -0
- package/dist/settings/restart-mcp.ts +58 -0
- package/dist/settings/restart-websocket.ts +51 -45
- package/dist/settings/utils.ts +240 -0
- package/dist/src/Lib/AI/ChatGPTClient.php +147 -0
- package/dist/src/Lib/Auth/Auth.php +544 -0
- package/dist/src/Lib/Auth/AuthConfig.php +89 -0
- package/dist/src/Lib/CacheHandler.php +121 -0
- package/dist/src/Lib/ErrorHandler.php +322 -0
- package/dist/src/Lib/FileManager/UploadFile.php +383 -0
- package/dist/src/Lib/Headers/Boom.php +192 -0
- package/dist/src/Lib/IncludeTracker.php +59 -0
- package/dist/src/Lib/MCP/WeatherTools.php +104 -0
- package/dist/src/Lib/MCP/mcp-server.php +80 -0
- package/dist/src/Lib/MainLayout.php +230 -0
- package/dist/src/Lib/Middleware/AuthMiddleware.php +157 -0
- package/dist/src/Lib/Middleware/CorsMiddleware.php +145 -0
- package/dist/src/Lib/PHPMailer/Mailer.php +169 -0
- package/dist/src/Lib/PHPX/Exceptions/ComponentValidationException.php +49 -0
- package/dist/src/Lib/PHPX/Fragment.php +32 -0
- package/dist/src/Lib/PHPX/IPHPX.php +22 -0
- package/dist/src/Lib/PHPX/PHPX.php +287 -0
- package/dist/src/Lib/PHPX/TemplateCompiler.php +663 -0
- package/dist/src/Lib/PHPX/TwMerge.php +346 -0
- package/dist/src/Lib/PHPX/TypeCoercer.php +490 -0
- package/dist/src/Lib/PartialRenderer.php +40 -0
- package/dist/src/Lib/PrismaPHPSettings.php +181 -0
- package/dist/src/Lib/Request.php +479 -0
- package/dist/src/Lib/Security/RateLimiter.php +33 -0
- package/dist/src/Lib/Set.php +102 -0
- package/dist/src/Lib/StateManager.php +127 -0
- package/dist/src/Lib/Validator.php +752 -0
- package/dist/src/{Websocket → Lib/Websocket}/ConnectionManager.php +1 -1
- package/dist/src/Lib/Websocket/websocket-server.php +118 -0
- package/dist/src/app/error.php +1 -1
- package/dist/src/app/index.php +22 -5
- package/dist/src/app/js/index.js +1 -1
- package/dist/src/app/layout.php +2 -2
- package/package.json +1 -1
- package/dist/settings/restart-websocket.bat +0 -28
- package/dist/src/app/assets/images/prisma-php-black.svg +0 -6
- package/dist/websocket-server.php +0 -22
- package/vendor/autoload.php +0 -25
- package/vendor/composer/ClassLoader.php +0 -579
- package/vendor/composer/InstalledVersions.php +0 -359
- package/vendor/composer/LICENSE +0 -21
- package/vendor/composer/autoload_classmap.php +0 -10
- package/vendor/composer/autoload_namespaces.php +0 -9
- package/vendor/composer/autoload_psr4.php +0 -10
- package/vendor/composer/autoload_real.php +0 -38
- package/vendor/composer/autoload_static.php +0 -25
- package/vendor/composer/installed.json +0 -825
- package/vendor/composer/installed.php +0 -132
- package/vendor/composer/platform_check.php +0 -26
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";import{randomBytes}from"crypto";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","error.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 o=c.replace(/(?<!:)(\/\/+)/g,"/"),i=n.replace(/\/\/+/g,"/");return{bsTarget:`${o}/`,bsPathRewrite:{"^/":`/${i.startsWith("/")?i.substring(1):i}/`}}}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","tailwind:build":"postcss src/app/css/tailwind.css -o src/app/css/styles.css"},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 o={...n.scripts};o.browserSync="tsx settings/bs-config.ts",o["browserSync:build"]="tsx settings/build.ts",o.dev=`npm-run-all projectName -p browserSync ${c.join(" ")}`,o.build=`npm-run-all${s.tailwindcss?" tailwind:build":""} browserSync:build`,n.scripts=o,n.type="module",fs.writeFileSync(t,JSON.stringify(n,null,2))}async function updateComposerJson(e){const s=path.join(e,"composer.json");if(checkExcludeFiles(s))return;let t;if(fs.existsSync(s)){{const e=fs.readFileSync(s,"utf8");t=JSON.parse(e)}t.autoload={"psr-4":{"":"src/"}},t.version="1.0.0",fs.writeFileSync(s,JSON.stringify(t,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")}function generateAuthSecret(){return randomBytes(33).toString("base64")}function generateHexEncodedKey(e=16){return randomBytes(e).toString("hex")}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\\websocket"))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")||s.includes("websocket-server.php")))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-schema-config.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 modifyPostcssConfig(e){const s=path.join(e,"postcss.config.js");if(checkExcludeFiles(s))return;fs.writeFileSync(s,'export default {\n plugins: {\n "@tailwindcss/postcss": {},\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/morphdom-umd.min.js"><\/script>\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),e=e.replace("</head>",`${c}\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:"/tsconfig.json",dest:"/tsconfig.json"},{src:"/app-gitignore",dest:"/.gitignore"}];s.tailwindcss&&t.push({src:"/postcss.config.js",dest:"/postcss.config.js"}),s.websocket&&t.push({src:"/websocket-server.php",dest:"/websocket-server.php"});const n=[{src:"/settings",dest:"/settings"},{src:"/src",dest:"/src"}];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 o=fs.readFileSync(n,"utf8");fs.writeFileSync(c,o,{flag:"w"})})),await executeCopy(e,n,s),await updatePackageJson(e,s),await updateComposerJson(e),s.backendOnly||await updateIndexJsForWebSocket(e,s),s.tailwindcss&&modifyPostcssConfig(e),(s.tailwindcss||!s.backendOnly||s.swaggerDocs)&&modifyLayoutPHP(e,s);const c=generateAuthSecret(),o=generateHexEncodedKey(),i=`# Authentication secret key for JWT or session encryption.\nAUTH_SECRET="${c}"\n# Name of the authentication cookie.\nAUTH_COOKIE_NAME="${generateHexEncodedKey(8)}"\n\n# PHPMailer SMTP configuration (uncomment and set as needed)\n# SMTP_HOST="smtp.gmail.com" # Your SMTP host\n# SMTP_USERNAME="john.doe@gmail.com" # Your SMTP username\n# SMTP_PASSWORD="123456" # Your SMTP password\n# SMTP_PORT="587" # 587 for TLS, 465 for SSL, or your SMTP port\n# SMTP_ENCRYPTION="ssl" # ssl or tls\n# MAIL_FROM="john.doe@gmail.com" # Sender email address\n# MAIL_FROM_NAME="John Doe" # Sender name\n\n# Show errors in the browser (development only). Set to false in production.\nSHOW_ERRORS="true"\n\n# Application timezone (default: UTC)\nAPP_TIMEZONE="UTC"\n\n# Application environment (development or production)\nAPP_ENV="development"\n\n# Enable or disable application cache (default: false)\nCACHE_ENABLED="false"\n# Cache time-to-live in seconds (default: 600)\nCACHE_TTL="600"\n\n# Local storage key for browser storage (auto-generated if not set).\n# Spaces will be replaced with underscores and converted to lowercase.\nLOCALSTORE_KEY="${o}"\n\n# Secret key for encrypting function calls.\nFUNCTION_CALL_SECRET="${generateHexEncodedKey(32)}"`;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)}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:!1,active:"Yes",inactive:"No"}),e.websocket||c.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!1,active:"Yes",inactive:"No"}),e.prisma||c.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!1,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 o=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:o.swaggerDocs??e.swaggerDocs??!1,tailwindcss:o.tailwindcss??e.tailwindcss??!1,websocket:o.websocket??e.websocket??!1,prisma:o.prisma??e.prisma??!1,docker:o.docker??e.docker??!1}}async function uninstallNpmDependencies(e,s,t=!1){s.forEach((e=>{}));const n=`npm uninstall ${t?"--save-dev":"--save"} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}async function uninstallComposerDependencies(e,s){s.forEach((e=>{}));const t=`C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar remove ${s.join(" ")}`;execSync(t,{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,spawnSync}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";import{randomBytes}from"crypto";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","error.php"],dockerFiles=[".dockerignore","docker-compose.yml","Dockerfile","apache.conf"],STARTER_KITS={basic:{id:"basic",name:"Basic PHP Application",description:"Simple PHP backend with minimal dependencies",features:{backendOnly:!0,tailwindcss:!1,websocket:!1,prisma:!1,docker:!1,swaggerDocs:!1,mcp:!1},requiredFiles:["bootstrap.php",".htaccess","src/app/layout.php","src/app/index.php"]},fullstack:{id:"fullstack",name:"Full-Stack Application",description:"Complete web application with frontend and backend",features:{backendOnly:!1,tailwindcss:!0,websocket:!1,prisma:!0,docker:!1,swaggerDocs:!0,mcp:!1},requiredFiles:["bootstrap.php",".htaccess","postcss.config.js","src/app/layout.php","src/app/index.php","src/app/js/index.js","src/app/css/tailwind.css"]},api:{id:"api",name:"REST API",description:"Backend API with database and documentation",features:{backendOnly:!0,tailwindcss:!1,websocket:!1,prisma:!0,docker:!0,swaggerDocs:!0,mcp:!1},requiredFiles:["bootstrap.php",".htaccess","docker-compose.yml","Dockerfile"]},realtime:{id:"realtime",name:"Real-time Application",description:"Application with WebSocket support and MCP",features:{backendOnly:!1,tailwindcss:!0,websocket:!0,prisma:!0,docker:!1,swaggerDocs:!0,mcp:!0},requiredFiles:["bootstrap.php",".htaccess","postcss.config.js","src/lib/websocket","src/lib/mcp"]},ecommerce:{id:"ecommerce",name:"E-commerce Starter",description:"Full e-commerce application with cart, payments, and admin",features:{backendOnly:!1,tailwindcss:!0,websocket:!1,prisma:!0,docker:!0,swaggerDocs:!0,mcp:!1},requiredFiles:[],source:{type:"git",url:"https://github.com/your-org/prisma-php-ecommerce-starter",branch:"main"}},blog:{id:"blog",name:"Blog CMS",description:"Blog content management system",features:{backendOnly:!1,tailwindcss:!0,websocket:!1,prisma:!0,docker:!1,swaggerDocs:!1,mcp:!1},requiredFiles:[],source:{type:"git",url:"https://github.com/your-org/prisma-php-blog-starter"}}};function bsConfigUrls(e){const s=e.indexOf("\\htdocs\\");if(-1===s)return{bsTarget:"",bsPathRewrite:{}};const t=e.substring(0,s+"\\htdocs\\".length).replace(/\\/g,"\\\\"),c=e.replace(new RegExp(`^${t}`),"").replace(/\\/g,"/");let n=`http://localhost/${c}`;n=n.endsWith("/")?n.slice(0,-1):n;const i=n.replace(/(?<!:)(\/\/+)/g,"/"),o=c.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 c=JSON.parse(fs.readFileSync(t,"utf8"));c.scripts={...c.scripts,projectName:"tsx settings/project-name.ts"};let n=[];if(s.tailwindcss&&(c.scripts={...c.scripts,tailwind:"postcss src/app/css/tailwind.css -o src/app/css/styles.css --watch","tailwind:build":"postcss src/app/css/tailwind.css -o src/app/css/styles.css"},n.push("tailwind")),s.websocket&&(c.scripts={...c.scripts,websocket:"tsx settings/restart-websocket.ts"},n.push("websocket")),s.mcp&&(c.scripts={...c.scripts,mcp:"tsx settings/restart-mcp.ts"},n.push("mcp")),s.docker&&(c.scripts={...c.scripts,docker:"docker-compose up"},n.push("docker")),s.swaggerDocs){const e=s.prisma?"tsx settings/auto-swagger-docs.ts":"tsx settings/swagger-config.ts";c.scripts={...c.scripts,"create-swagger-docs":e}}let i={...c.scripts};i.browserSync="tsx settings/bs-config.ts",i["browserSync:build"]="tsx settings/build.ts",i.dev=`npm-run-all projectName -p browserSync ${n.join(" ")}`,i.build=`npm-run-all${s.tailwindcss?" tailwind:build":""} browserSync:build`,c.scripts=i,c.type="module",fs.writeFileSync(t,JSON.stringify(c,null,2))}async function updateComposerJson(e){checkExcludeFiles(path.join(e,"composer.json"))}async function updateIndexJsForWebSocket(e,s){if(!s.websocket)return;const t=path.join(e,"src","app","js","index.js");if(checkExcludeFiles(t))return;let c=fs.readFileSync(t,"utf8");c+='\n// WebSocket initialization\nvar ws = new WebSocket("ws://localhost:8080");\n',fs.writeFileSync(t,c,"utf8")}function generateAuthSecret(){return randomBytes(33).toString("base64")}function generateHexEncodedKey(e=16){return randomBytes(e).toString("hex")}function copyRecursiveSync(e,s,t){const c=fs.existsSync(e),n=c&&fs.statSync(e);if(c&&n&&n.isDirectory()){const c=s.toLowerCase();if(!t.websocket&&c.includes("src\\lib\\websocket"))return;if(!t.mcp&&c.includes("src\\lib\\mcp"))return;if(t.backendOnly&&c.includes("src\\app\\js")||t.backendOnly&&c.includes("src\\app\\css")||t.backendOnly&&c.includes("src\\app\\assets"))return;if(!t.swaggerDocs&&c.includes("src\\app\\swagger-docs"))return;const n=s.replace(/\\/g,"/");if(updateAnswer?.excludeFilePath?.includes(n))return;fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),fs.readdirSync(e).forEach((c=>{copyRecursiveSync(path.join(e,c),path.join(s,c),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"))return;if(!t.mcp&&s.includes("restart-mcp.ts"))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-schema-config.json")))return;fs.copyFileSync(e,s,0)}}async function executeCopy(e,s,t){s.forEach((({src:s,dest:c})=>{copyRecursiveSync(path.join(__dirname,s),path.join(e,c),t)}))}function modifyPostcssConfig(e){const s=path.join(e,"postcss.config.js");if(checkExcludeFiles(s))return;fs.writeFileSync(s,'export default {\n plugins: {\n "@tailwindcss/postcss": {},\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"),c="";s.backendOnly||(s.tailwindcss||(c='\n <link href="<?= Request::baseUrl; ?>/css/index.css" rel="stylesheet" />'),c+='\n <script src="<?= Request::baseUrl; ?>/js/morphdom-umd.min.js"><\/script>\n <script src="<?= Request::baseUrl; ?>/js/json5.min.js"><\/script>\n <script src="<?= Request::baseUrl; ?>/js/index.js"><\/script>');let n="";s.backendOnly||(n=s.tailwindcss?` <link href="<?= Request::baseUrl; ?>/css/styles.css" rel="stylesheet" /> ${c}`:c),e=e.replace("</head>",`${n}\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:"/tsconfig.json",dest:"/tsconfig.json"},{src:"/app-gitignore",dest:"/.gitignore"}];s.tailwindcss&&t.push({src:"/postcss.config.js",dest:"/postcss.config.js"});const c=[{src:"/settings",dest:"/settings"},{src:"/src",dest:"/src"}];s.docker&&c.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 c=path.join(__dirname,s),n=path.join(e,t);if(checkExcludeFiles(n))return;const i=fs.readFileSync(c,"utf8");fs.writeFileSync(n,i,{flag:"w"})})),await executeCopy(e,c,s),await updatePackageJson(e,s),await updateComposerJson(e),s.backendOnly||await updateIndexJsForWebSocket(e,s),s.tailwindcss&&modifyPostcssConfig(e),(s.tailwindcss||!s.backendOnly||s.swaggerDocs)&&modifyLayoutPHP(e,s);const n=generateAuthSecret(),i=generateHexEncodedKey(),o=`# Authentication secret key for JWT or session encryption.\nAUTH_SECRET="${n}"\n# Name of the authentication cookie.\nAUTH_COOKIE_NAME="${generateHexEncodedKey(8)}"\n\n# PHPMailer SMTP configuration (uncomment and set as needed)\n# SMTP_HOST="smtp.gmail.com" # Your SMTP host\n# SMTP_USERNAME="john.doe@gmail.com" # Your SMTP username\n# SMTP_PASSWORD="123456" # Your SMTP password\n# SMTP_PORT="587" # 587 for TLS, 465 for SSL, or your SMTP port\n# SMTP_ENCRYPTION="ssl" # ssl or tls\n# MAIL_FROM="john.doe@gmail.com" # Sender email address\n# MAIL_FROM_NAME="John Doe" # Sender name\n\n# Show errors in the browser (development only). Set to false in production.\nSHOW_ERRORS="true"\n\n# Application timezone (default: UTC)\nAPP_TIMEZONE="UTC"\n\n# Application environment (development or production)\nAPP_ENV="development"\n\n# Enable or disable application cache (default: false)\nCACHE_ENABLED="false"\n# Cache time-to-live in seconds (default: 600)\nCACHE_TTL="600"\n\n# Local storage key for browser storage (auto-generated if not set).\n# Spaces will be replaced with underscores and converted to lowercase.\nLOCALSTORE_KEY="${i}"\n\n# Secret key for encrypting function calls.\nFUNCTION_CALL_SECRET="${generateHexEncodedKey(32)}"\n\n# Single or multiple origins (CSV or JSON array)\nCORS_ALLOWED_ORIGINS=[]\n\n# If you need cookies/Authorization across origins, keep this true\nCORS_ALLOW_CREDENTIALS="true"\n\n# Optional tuning\nCORS_ALLOWED_METHODS="GET,POST,PUT,PATCH,DELETE,OPTIONS"\nCORS_ALLOWED_HEADERS="Content-Type,Authorization,X-Requested-With"\nCORS_EXPOSE_HEADERS=""\nCORS_MAX_AGE="86400"`;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${o}`;await createOrUpdateEnvFile(e,s)}else await createOrUpdateEnvFile(e,o)}async function getAnswer(e={}){if(e.starterKit){const s=e.starterKit;let t=null;if(STARTER_KITS[s]&&(t=STARTER_KITS[s]),t){const c={projectName:e.projectName??"my-app",starterKit:s,starterKitSource:e.starterKitSource,backendOnly:t.features.backendOnly??!1,tailwindcss:t.features.tailwindcss??!1,websocket:t.features.websocket??!1,prisma:t.features.prisma??!1,docker:t.features.docker??!1,swaggerDocs:t.features.swaggerDocs??!1,mcp:t.features.mcp??!1},n=process.argv.slice(2);return n.includes("--backend-only")&&(c.backendOnly=!0),n.includes("--swagger-docs")&&(c.swaggerDocs=!0),n.includes("--tailwindcss")&&(c.tailwindcss=!0),n.includes("--websocket")&&(c.websocket=!0),n.includes("--mcp")&&(c.mcp=!0),n.includes("--prisma")&&(c.prisma=!0),n.includes("--docker")&&(c.docker=!0),c}if(e.starterKitSource){const t={projectName:e.projectName??"my-app",starterKit:s,starterKitSource:e.starterKitSource,backendOnly:!1,tailwindcss:!0,websocket:!1,prisma:!0,docker:!1,swaggerDocs:!0,mcp:!1},c=process.argv.slice(2);return c.includes("--backend-only")&&(t.backendOnly=!0),c.includes("--swagger-docs")&&(t.swaggerDocs=!0),c.includes("--tailwindcss")&&(t.tailwindcss=!0),c.includes("--websocket")&&(t.websocket=!0),c.includes("--mcp")&&(t.mcp=!0),c.includes("--prisma")&&(t.prisma=!0),c.includes("--docker")&&(t.docker=!0),t}}const s=[];e.projectName||s.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.backendOnly||updateAnswer?.isUpdate||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)},c=await prompts(s,{onCancel:t}),n=[];c.backendOnly??e.backendOnly??!1?(e.swaggerDocs||n.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||n.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!1,active:"Yes",inactive:"No"}),e.mcp||n.push({type:"toggle",name:"mcp",message:`Would you like to use ${chalk.blue("MCP (Model Context Protocol)")}?`,initial:!1,active:"Yes",inactive:"No"}),e.prisma||n.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!1,active:"Yes",inactive:"No"}),e.docker||n.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"})):(e.swaggerDocs||n.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.tailwindcss||n.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||n.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!1,active:"Yes",inactive:"No"}),e.mcp||n.push({type:"toggle",name:"mcp",message:`Would you like to use ${chalk.blue("MCP (Model Context Protocol)")}?`,initial:!1,active:"Yes",inactive:"No"}),e.prisma||n.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma PHP ORM")}?`,initial:!1,active:"Yes",inactive:"No"}),e.docker||n.push({type:"toggle",name:"docker",message:`Would you like to use ${chalk.blue("Docker")}?`,initial:!1,active:"Yes",inactive:"No"}));const i=await prompts(n,{onCancel:t});return{projectName:c.projectName?String(c.projectName).trim().replace(/ /g,"-"):e.projectName??"my-app",backendOnly:c.backendOnly??e.backendOnly??!1,swaggerDocs:i.swaggerDocs??e.swaggerDocs??!1,tailwindcss:i.tailwindcss??e.tailwindcss??!1,websocket:i.websocket??e.websocket??!1,mcp:i.mcp??e.mcp??!1,prisma:i.prisma??e.prisma??!1,docker:i.docker??e.docker??!1}}async function uninstallNpmDependencies(e,s,t=!1){s.forEach((e=>{}));const c=`npm uninstall ${t?"--save-dev":"--save"} ${s.join(" ")}`;execSync(c,{stdio:"inherit",cwd:e})}async function uninstallComposerDependencies(e,s){s.forEach((e=>{}));const t=`C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar remove ${s.join(" ")}`;execSync(t,{stdio:"inherit",cwd:e})}function fetchPackageVersion(e){return new Promise(((s,t)=>{https.get(`https://registry.npmjs.org/${e}`,(e=>{let c="";e.on("data",(e=>c+=e)),e.on("end",(()=>{try{const e=JSON.parse(c);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),c=s.split(".").map(Number);for(let e=0;e<t.length;e++){if(t[e]>c[e])return 1;if(t[e]<c[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.
|
|
@@ -7,380 +7,856 @@ import{execSync}from"child_process";import fs from"fs";import{fileURLToPath}from
|
|
|
7
7
|
* @param {boolean} [isDev=false] - Whether to install the dependencies as devDependencies.
|
|
8
8
|
*/
|
|
9
9
|
async function installNpmDependencies(baseDir, dependencies, isDev = false) {
|
|
10
|
+
if (!fs.existsSync(path.join(baseDir, "package.json"))) {
|
|
10
11
|
console.log("Initializing new Node.js project...");
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
console.log(`${isDev ? "Installing development dependencies" : "Installing dependencies"}:`);
|
|
20
|
-
dependencies.forEach((dep) => console.log(`- ${chalk.blue(dep)}`));
|
|
21
|
-
// Prepare the npm install command with the appropriate flag for dev dependencies
|
|
22
|
-
const npmInstallCommand = `npm install ${isDev ? "--save-dev" : ""} ${dependencies.join(" ")}`;
|
|
23
|
-
// Execute the npm install command
|
|
24
|
-
execSync(npmInstallCommand, {
|
|
25
|
-
stdio: "inherit",
|
|
26
|
-
cwd: baseDir,
|
|
12
|
+
} else {
|
|
13
|
+
console.log("Updating existing Node.js project...");
|
|
14
|
+
}
|
|
15
|
+
// Initialize a package.json if it doesn't exist
|
|
16
|
+
if (!fs.existsSync(path.join(baseDir, "package.json"))) {
|
|
17
|
+
execSync("npm init -y", {
|
|
18
|
+
stdio: "inherit",
|
|
19
|
+
cwd: baseDir,
|
|
27
20
|
});
|
|
21
|
+
}
|
|
22
|
+
// Log the dependencies being installed
|
|
23
|
+
console.log(
|
|
24
|
+
`${
|
|
25
|
+
isDev ? "Installing development dependencies" : "Installing dependencies"
|
|
26
|
+
}:`
|
|
27
|
+
);
|
|
28
|
+
dependencies.forEach((dep) => console.log(`- ${chalk.blue(dep)}`));
|
|
29
|
+
// Prepare the npm install command with the appropriate flag for dev dependencies
|
|
30
|
+
const npmInstallCommand = `npm install ${
|
|
31
|
+
isDev ? "--save-dev" : ""
|
|
32
|
+
} ${dependencies.join(" ")}`;
|
|
33
|
+
// Execute the npm install command
|
|
34
|
+
execSync(npmInstallCommand, {
|
|
35
|
+
stdio: "inherit",
|
|
36
|
+
cwd: baseDir,
|
|
37
|
+
});
|
|
28
38
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
function getComposerCmd() {
|
|
40
|
+
try {
|
|
41
|
+
execSync("composer --version", { stdio: "ignore" });
|
|
42
|
+
return { cmd: "composer", baseArgs: [] };
|
|
43
|
+
} catch {
|
|
44
|
+
return {
|
|
45
|
+
cmd: "C:\\xampp\\php\\php.exe",
|
|
46
|
+
baseArgs: ["C:\\ProgramData\\ComposerSetup\\bin\\composer.phar"],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export async function installComposerDependencies(baseDir, dependencies) {
|
|
51
|
+
const { cmd, baseArgs } = getComposerCmd();
|
|
52
|
+
const composerJsonPath = path.join(baseDir, "composer.json");
|
|
53
|
+
const existsAlready = fs.existsSync(composerJsonPath);
|
|
54
|
+
console.log(
|
|
55
|
+
chalk.green(
|
|
56
|
+
`Composer project initialization: ${
|
|
57
|
+
existsAlready ? "Updating existing project…" : "Setting up new project…"
|
|
58
|
+
}`
|
|
59
|
+
)
|
|
60
|
+
);
|
|
61
|
+
/* ------------------------------------------------------------------ */
|
|
62
|
+
/* 1. Try composer init (quietly fall back if it fails) */
|
|
63
|
+
/* ------------------------------------------------------------------ */
|
|
64
|
+
if (!existsAlready) {
|
|
65
|
+
const initArgs = [
|
|
66
|
+
...baseArgs,
|
|
67
|
+
"init",
|
|
68
|
+
"--no-interaction",
|
|
69
|
+
"--name",
|
|
70
|
+
"tsnc/prisma-php-app",
|
|
71
|
+
"--require",
|
|
72
|
+
"php:^8.2",
|
|
73
|
+
"--type",
|
|
74
|
+
"project",
|
|
75
|
+
"--version",
|
|
76
|
+
"1.0.0",
|
|
77
|
+
];
|
|
78
|
+
const res = spawnSync(cmd, initArgs, { cwd: baseDir });
|
|
79
|
+
if (res.status !== 0) {
|
|
80
|
+
// Silent fallback: no logs, just write a minimal composer.json
|
|
81
|
+
fs.writeFileSync(
|
|
82
|
+
composerJsonPath,
|
|
83
|
+
JSON.stringify(
|
|
84
|
+
{
|
|
85
|
+
name: "tsnc/prisma-php-app",
|
|
86
|
+
type: "project",
|
|
87
|
+
version: "1.0.0",
|
|
88
|
+
require: { php: "^8.2" },
|
|
89
|
+
autoload: { "psr-4": { "": "src/" } },
|
|
90
|
+
},
|
|
91
|
+
null,
|
|
92
|
+
2
|
|
93
|
+
)
|
|
94
|
+
);
|
|
37
95
|
}
|
|
38
|
-
|
|
96
|
+
}
|
|
97
|
+
/* 2. Ensure PSR-4 autoload entry ---------------------------------- */
|
|
98
|
+
const json = JSON.parse(fs.readFileSync(composerJsonPath, "utf8"));
|
|
99
|
+
json.autoload ??= {};
|
|
100
|
+
json.autoload["psr-4"] ??= {};
|
|
101
|
+
json.autoload["psr-4"][""] ??= "src/";
|
|
102
|
+
fs.writeFileSync(composerJsonPath, JSON.stringify(json, null, 2));
|
|
103
|
+
/* 3. Install dependencies ----------------------------------------- */
|
|
104
|
+
if (dependencies.length) {
|
|
39
105
|
console.log("Installing Composer dependencies:");
|
|
40
|
-
dependencies.forEach((
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
106
|
+
dependencies.forEach((d) => console.log(`- ${chalk.blue(d)}`));
|
|
107
|
+
execSync(
|
|
108
|
+
`${cmd} ${[
|
|
109
|
+
...baseArgs,
|
|
110
|
+
"require",
|
|
111
|
+
"--no-interaction",
|
|
112
|
+
...dependencies,
|
|
113
|
+
].join(" ")}`,
|
|
114
|
+
{ stdio: "inherit", cwd: baseDir }
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
/* 4. Refresh lock when updating ----------------------------------- */
|
|
118
|
+
if (existsAlready) {
|
|
119
|
+
execSync(
|
|
120
|
+
`${cmd} ${[
|
|
121
|
+
...baseArgs,
|
|
122
|
+
"update",
|
|
123
|
+
"--lock",
|
|
124
|
+
"--no-install",
|
|
125
|
+
"--no-interaction",
|
|
126
|
+
].join(" ")}`,
|
|
127
|
+
{ stdio: "inherit", cwd: baseDir }
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
/* 5. Regenerate autoloader ---------------------------------------- */
|
|
131
|
+
execSync(`${cmd} ${[...baseArgs, "dump-autoload", "--quiet"].join(" ")}`, {
|
|
132
|
+
stdio: "inherit",
|
|
133
|
+
cwd: baseDir,
|
|
134
|
+
});
|
|
48
135
|
}
|
|
49
136
|
const npmPinnedVersions = {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
typescript: "^5.8.3",
|
|
137
|
+
"@tailwindcss/postcss": "^4.1.11",
|
|
138
|
+
"@types/browser-sync": "^2.29.0",
|
|
139
|
+
"@types/node": "^24.2.1",
|
|
140
|
+
"@types/prompts": "^2.4.9",
|
|
141
|
+
"browser-sync": "^3.0.4",
|
|
142
|
+
chalk: "^5.5.0",
|
|
143
|
+
cssnano: "^7.1.0",
|
|
144
|
+
"http-proxy-middleware": "^3.0.5",
|
|
145
|
+
"npm-run-all": "^4.1.5",
|
|
146
|
+
"php-parser": "^3.2.5",
|
|
147
|
+
postcss: "^8.5.6",
|
|
148
|
+
"postcss-cli": "^11.0.1",
|
|
149
|
+
prompts: "^2.4.2",
|
|
150
|
+
tailwindcss: "^4.1.11",
|
|
151
|
+
tsx: "^4.20.3",
|
|
152
|
+
typescript: "^5.9.2",
|
|
67
153
|
};
|
|
68
154
|
function npmPkg(name) {
|
|
69
|
-
|
|
155
|
+
return npmPinnedVersions[name] ? `${name}@${npmPinnedVersions[name]}` : name;
|
|
70
156
|
}
|
|
71
157
|
const composerPinnedVersions = {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
158
|
+
"vlucas/phpdotenv": "^5.6.2",
|
|
159
|
+
"firebase/php-jwt": "^6.11.1",
|
|
160
|
+
"phpmailer/phpmailer": "^6.10.0",
|
|
161
|
+
"guzzlehttp/guzzle": "^7.9.3",
|
|
162
|
+
"ezyang/htmlpurifier": "^4.18.0",
|
|
163
|
+
"symfony/uid": "^7.2.0",
|
|
164
|
+
"brick/math": "^0.13.1",
|
|
165
|
+
"cboden/ratchet": "^0.4.4",
|
|
166
|
+
"tsnc/prisma-php": "^1.0.0",
|
|
167
|
+
"php-mcp/server": "3.3.0",
|
|
81
168
|
};
|
|
82
169
|
function composerPkg(name) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
170
|
+
return composerPinnedVersions[name]
|
|
171
|
+
? `${name}:${composerPinnedVersions[name]}`
|
|
172
|
+
: name;
|
|
86
173
|
}
|
|
87
|
-
async function
|
|
174
|
+
async function setupStarterKit(baseDir, answer) {
|
|
175
|
+
if (!answer.starterKit) return;
|
|
176
|
+
let starterKit = null;
|
|
177
|
+
// Check if it's a built-in starter kit
|
|
178
|
+
if (STARTER_KITS[answer.starterKit]) {
|
|
179
|
+
starterKit = STARTER_KITS[answer.starterKit];
|
|
180
|
+
}
|
|
181
|
+
// Handle custom starter kit URL
|
|
182
|
+
else if (answer.starterKitSource) {
|
|
183
|
+
starterKit = {
|
|
184
|
+
id: answer.starterKit,
|
|
185
|
+
name: `Custom Starter Kit (${answer.starterKit})`,
|
|
186
|
+
description: "Custom starter kit from external source",
|
|
187
|
+
features: {}, // Will be determined from the downloaded kit
|
|
188
|
+
requiredFiles: [],
|
|
189
|
+
source: {
|
|
190
|
+
type: "git", // Assume git for now, could be enhanced
|
|
191
|
+
url: answer.starterKitSource,
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
if (!starterKit) {
|
|
196
|
+
console.warn(
|
|
197
|
+
chalk.yellow(`Starter kit '${answer.starterKit}' not found. Skipping...`)
|
|
198
|
+
);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
console.log(chalk.green(`Setting up ${starterKit.name}...`));
|
|
202
|
+
// If it's a custom starter kit with source, clone it directly to the target directory
|
|
203
|
+
if (starterKit.source) {
|
|
88
204
|
try {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
prisma: answer?.prisma ?? false,
|
|
130
|
-
docker: answer?.docker ?? false,
|
|
131
|
-
isUpdate: true,
|
|
132
|
-
excludeFiles: localSettings.excludeFiles ?? [],
|
|
133
|
-
excludeFilePath: excludeFiles ?? [],
|
|
134
|
-
filePath: currentDir,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
205
|
+
// Clone directly to the target directory
|
|
206
|
+
const cloneCommand = starterKit.source.branch
|
|
207
|
+
? `git clone -b ${starterKit.source.branch} --depth 1 ${starterKit.source.url} ${baseDir}`
|
|
208
|
+
: `git clone --depth 1 ${starterKit.source.url} ${baseDir}`;
|
|
209
|
+
execSync(cloneCommand, { stdio: "inherit" });
|
|
210
|
+
// Remove .git directory
|
|
211
|
+
const gitDir = path.join(baseDir, ".git");
|
|
212
|
+
if (fs.existsSync(gitDir)) {
|
|
213
|
+
fs.rmSync(gitDir, { recursive: true, force: true });
|
|
214
|
+
}
|
|
215
|
+
console.log(chalk.blue("Starter kit cloned successfully!"));
|
|
216
|
+
// Update the project name in the existing prisma-php.json
|
|
217
|
+
const configPath = path.join(baseDir, "prisma-php.json");
|
|
218
|
+
if (fs.existsSync(configPath)) {
|
|
219
|
+
try {
|
|
220
|
+
const existingConfig = JSON.parse(
|
|
221
|
+
fs.readFileSync(configPath, "utf8")
|
|
222
|
+
);
|
|
223
|
+
// Only update project-specific fields, preserve everything else
|
|
224
|
+
const projectPathModified = baseDir.replace(/\\/g, "\\");
|
|
225
|
+
const bsConfig = bsConfigUrls(projectPathModified);
|
|
226
|
+
existingConfig.projectName = answer.projectName;
|
|
227
|
+
existingConfig.projectRootPath = projectPathModified;
|
|
228
|
+
existingConfig.bsTarget = bsConfig.bsTarget;
|
|
229
|
+
existingConfig.bsPathRewrite = bsConfig.bsPathRewrite;
|
|
230
|
+
// Update version to latest
|
|
231
|
+
const latestVersion = await fetchPackageVersion(
|
|
232
|
+
"create-prisma-php-app"
|
|
233
|
+
);
|
|
234
|
+
existingConfig.version = latestVersion;
|
|
235
|
+
fs.writeFileSync(configPath, JSON.stringify(existingConfig, null, 2));
|
|
236
|
+
console.log(
|
|
237
|
+
chalk.green("Updated prisma-php.json with new project details")
|
|
238
|
+
);
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.warn(
|
|
241
|
+
chalk.yellow(
|
|
242
|
+
"Failed to update prisma-php.json, will create new one"
|
|
243
|
+
)
|
|
244
|
+
);
|
|
137
245
|
}
|
|
138
|
-
|
|
139
|
-
|
|
246
|
+
}
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error(chalk.red(`Failed to setup starter kit: ${error}`));
|
|
249
|
+
throw error;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// Run custom setup if defined
|
|
253
|
+
if (starterKit.customSetup) {
|
|
254
|
+
await starterKit.customSetup(baseDir, answer);
|
|
255
|
+
}
|
|
256
|
+
console.log(chalk.green(`✓ ${starterKit.name} setup complete!`));
|
|
257
|
+
}
|
|
258
|
+
function showStarterKits() {
|
|
259
|
+
console.log(chalk.blue("\n🚀 Available Starter Kits:\n"));
|
|
260
|
+
Object.values(STARTER_KITS).forEach((kit) => {
|
|
261
|
+
const isCustom = kit.source ? " (Custom)" : " (Built-in)";
|
|
262
|
+
console.log(chalk.green(` ${kit.id}${chalk.gray(isCustom)}`));
|
|
263
|
+
console.log(` ${kit.name}`);
|
|
264
|
+
console.log(chalk.gray(` ${kit.description}`));
|
|
265
|
+
if (kit.source) {
|
|
266
|
+
console.log(chalk.cyan(` Source: ${kit.source.url}`));
|
|
267
|
+
}
|
|
268
|
+
const features = Object.entries(kit.features)
|
|
269
|
+
.filter(([, value]) => value === true)
|
|
270
|
+
.map(([key]) => key)
|
|
271
|
+
.join(", ");
|
|
272
|
+
if (features) {
|
|
273
|
+
console.log(chalk.magenta(` Features: ${features}`));
|
|
274
|
+
}
|
|
275
|
+
console.log();
|
|
276
|
+
});
|
|
277
|
+
console.log(chalk.yellow("Usage:"));
|
|
278
|
+
console.log(` npx create-prisma-php-app my-project --starter-kit=basic`);
|
|
279
|
+
console.log(
|
|
280
|
+
` npx create-prisma-php-app my-project --starter-kit=custom --starter-kit-source=https://github.com/user/repo`
|
|
281
|
+
);
|
|
282
|
+
console.log();
|
|
283
|
+
}
|
|
284
|
+
async function main() {
|
|
285
|
+
try {
|
|
286
|
+
const args = process.argv.slice(2);
|
|
287
|
+
let projectName = args[0];
|
|
288
|
+
// Parse starter kit arguments
|
|
289
|
+
const starterKitArg = args.find((arg) => arg.startsWith("--starter-kit="));
|
|
290
|
+
const starterKitFromArgs = starterKitArg?.split("=")[1];
|
|
291
|
+
// Parse custom starter kit source
|
|
292
|
+
const starterKitSourceArg = args.find((arg) =>
|
|
293
|
+
arg.startsWith("--starter-kit-source=")
|
|
294
|
+
);
|
|
295
|
+
const starterKitSource = starterKitSourceArg?.split("=")[1];
|
|
296
|
+
// Show help
|
|
297
|
+
if (args.includes("--list-starter-kits")) {
|
|
298
|
+
showStarterKits();
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
let answer = null;
|
|
302
|
+
let isStarterKitProject = false;
|
|
303
|
+
if (projectName) {
|
|
304
|
+
const currentDir = process.cwd();
|
|
305
|
+
const configPath = path.join(currentDir, "prisma-php.json");
|
|
306
|
+
// Check if it's a starter kit project
|
|
307
|
+
if (starterKitFromArgs && starterKitSource) {
|
|
308
|
+
isStarterKitProject = true;
|
|
309
|
+
const predefinedAnswers = {
|
|
310
|
+
projectName,
|
|
311
|
+
starterKit: starterKitFromArgs,
|
|
312
|
+
starterKitSource: starterKitSource,
|
|
313
|
+
backendOnly: args.includes("--backend-only"),
|
|
314
|
+
swaggerDocs: args.includes("--swagger-docs"),
|
|
315
|
+
tailwindcss: args.includes("--tailwindcss"),
|
|
316
|
+
websocket: args.includes("--websocket"),
|
|
317
|
+
mcp: args.includes("--mcp"),
|
|
318
|
+
prisma: args.includes("--prisma"),
|
|
319
|
+
docker: args.includes("--docker"),
|
|
320
|
+
};
|
|
321
|
+
answer = await getAnswer(predefinedAnswers);
|
|
322
|
+
} else if (fs.existsSync(configPath)) {
|
|
323
|
+
// It's an update - read existing settings
|
|
324
|
+
const localSettings = readJsonFile(configPath);
|
|
325
|
+
let excludeFiles = [];
|
|
326
|
+
localSettings.excludeFiles?.map((file) => {
|
|
327
|
+
const filePath = path.join(currentDir, file);
|
|
328
|
+
if (fs.existsSync(filePath))
|
|
329
|
+
excludeFiles.push(filePath.replace(/\\/g, "/"));
|
|
330
|
+
});
|
|
331
|
+
// Set updateAnswer with OLD settings initially (for checkExcludeFiles function)
|
|
332
|
+
updateAnswer = {
|
|
333
|
+
projectName,
|
|
334
|
+
backendOnly: localSettings.backendOnly,
|
|
335
|
+
swaggerDocs: localSettings.swaggerDocs,
|
|
336
|
+
tailwindcss: localSettings.tailwindcss,
|
|
337
|
+
websocket: localSettings.websocket,
|
|
338
|
+
mcp: localSettings.mcp,
|
|
339
|
+
prisma: localSettings.prisma,
|
|
340
|
+
docker: localSettings.docker,
|
|
341
|
+
isUpdate: true,
|
|
342
|
+
excludeFiles: localSettings.excludeFiles ?? [],
|
|
343
|
+
excludeFilePath: excludeFiles ?? [],
|
|
344
|
+
filePath: currentDir,
|
|
345
|
+
};
|
|
346
|
+
// For updates, use existing settings but allow CLI overrides
|
|
347
|
+
const predefinedAnswers = {
|
|
348
|
+
projectName,
|
|
349
|
+
backendOnly:
|
|
350
|
+
args.includes("--backend-only") || localSettings.backendOnly,
|
|
351
|
+
swaggerDocs:
|
|
352
|
+
args.includes("--swagger-docs") || localSettings.swaggerDocs,
|
|
353
|
+
tailwindcss:
|
|
354
|
+
args.includes("--tailwindcss") || localSettings.tailwindcss,
|
|
355
|
+
websocket: args.includes("--websocket") || localSettings.websocket,
|
|
356
|
+
prisma: args.includes("--prisma") || localSettings.prisma,
|
|
357
|
+
docker: args.includes("--docker") || localSettings.docker,
|
|
358
|
+
mcp: args.includes("--mcp") || localSettings.mcp,
|
|
359
|
+
};
|
|
360
|
+
answer = await getAnswer(predefinedAnswers);
|
|
361
|
+
// IMPORTANT: Update updateAnswer with the NEW answer after getting user input
|
|
362
|
+
if (answer !== null) {
|
|
363
|
+
updateAnswer = {
|
|
364
|
+
projectName,
|
|
365
|
+
backendOnly: answer.backendOnly,
|
|
366
|
+
swaggerDocs: answer.swaggerDocs,
|
|
367
|
+
tailwindcss: answer.tailwindcss,
|
|
368
|
+
websocket: answer.websocket,
|
|
369
|
+
mcp: answer.mcp,
|
|
370
|
+
prisma: answer.prisma,
|
|
371
|
+
docker: answer.docker,
|
|
372
|
+
isUpdate: true,
|
|
373
|
+
excludeFiles: localSettings.excludeFiles ?? [],
|
|
374
|
+
excludeFilePath: excludeFiles ?? [],
|
|
375
|
+
filePath: currentDir,
|
|
376
|
+
};
|
|
140
377
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
378
|
+
} else {
|
|
379
|
+
// New project
|
|
380
|
+
const predefinedAnswers = {
|
|
381
|
+
projectName,
|
|
382
|
+
starterKit: starterKitFromArgs,
|
|
383
|
+
starterKitSource: starterKitSource,
|
|
384
|
+
backendOnly: args.includes("--backend-only"),
|
|
385
|
+
swaggerDocs: args.includes("--swagger-docs"),
|
|
386
|
+
tailwindcss: args.includes("--tailwindcss"),
|
|
387
|
+
websocket: args.includes("--websocket"),
|
|
388
|
+
mcp: args.includes("--mcp"),
|
|
389
|
+
prisma: args.includes("--prisma"),
|
|
390
|
+
docker: args.includes("--docker"),
|
|
391
|
+
};
|
|
392
|
+
answer = await getAnswer(predefinedAnswers);
|
|
393
|
+
}
|
|
394
|
+
if (answer === null) {
|
|
395
|
+
console.log(chalk.red("Installation cancelled."));
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
} else {
|
|
399
|
+
// Interactive mode
|
|
400
|
+
answer = await getAnswer();
|
|
401
|
+
}
|
|
402
|
+
if (answer === null) {
|
|
403
|
+
console.warn(chalk.red("Installation cancelled."));
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
const latestVersionOfCreatePrismaPhpApp = await fetchPackageVersion(
|
|
407
|
+
"create-prisma-php-app"
|
|
408
|
+
);
|
|
409
|
+
const isCreatePrismaPhpAppInstalled = getInstalledPackageVersion(
|
|
410
|
+
"create-prisma-php-app"
|
|
411
|
+
);
|
|
412
|
+
if (isCreatePrismaPhpAppInstalled) {
|
|
413
|
+
if (
|
|
414
|
+
compareVersions(
|
|
415
|
+
isCreatePrismaPhpAppInstalled,
|
|
416
|
+
latestVersionOfCreatePrismaPhpApp
|
|
417
|
+
) === -1
|
|
418
|
+
) {
|
|
419
|
+
execSync("npm uninstall -g create-prisma-php-app", {
|
|
420
|
+
stdio: "inherit",
|
|
421
|
+
});
|
|
422
|
+
execSync("npm install -g create-prisma-php-app", {
|
|
423
|
+
stdio: "inherit",
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
} else {
|
|
427
|
+
execSync("npm install -g create-prisma-php-app", { stdio: "inherit" });
|
|
428
|
+
}
|
|
429
|
+
// Create the project directory
|
|
430
|
+
const currentDir = process.cwd();
|
|
431
|
+
let projectPath;
|
|
432
|
+
if (projectName) {
|
|
433
|
+
if (isStarterKitProject) {
|
|
434
|
+
// For starter kit projects, create directory first
|
|
435
|
+
const projectNamePath = path.join(currentDir, projectName);
|
|
436
|
+
if (!fs.existsSync(projectNamePath)) {
|
|
437
|
+
fs.mkdirSync(projectNamePath, { recursive: true });
|
|
144
438
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
439
|
+
projectPath = projectNamePath;
|
|
440
|
+
// Clone the starter kit first
|
|
441
|
+
await setupStarterKit(projectPath, answer);
|
|
442
|
+
// Change to project directory
|
|
443
|
+
process.chdir(projectPath);
|
|
444
|
+
// Now check if it has prisma-php.json and treat as update
|
|
445
|
+
const configPath = path.join(projectPath, "prisma-php.json");
|
|
446
|
+
if (fs.existsSync(configPath)) {
|
|
447
|
+
// Read the existing config and merge with CLI overrides
|
|
448
|
+
const existingConfig = JSON.parse(
|
|
449
|
+
fs.readFileSync(configPath, "utf8")
|
|
450
|
+
);
|
|
451
|
+
// Override with CLI arguments if provided
|
|
452
|
+
if (args.includes("--backend-only"))
|
|
453
|
+
existingConfig.backendOnly = true;
|
|
454
|
+
if (args.includes("--swagger-docs"))
|
|
455
|
+
existingConfig.swaggerDocs = true;
|
|
456
|
+
if (args.includes("--tailwindcss")) existingConfig.tailwindcss = true;
|
|
457
|
+
if (args.includes("--websocket")) existingConfig.websocket = true;
|
|
458
|
+
if (args.includes("--mcp")) existingConfig.mcp = true;
|
|
459
|
+
if (args.includes("--prisma")) existingConfig.prisma = true;
|
|
460
|
+
if (args.includes("--docker")) existingConfig.docker = true;
|
|
461
|
+
// Update answer with existing config
|
|
462
|
+
answer = {
|
|
463
|
+
...answer,
|
|
464
|
+
backendOnly: existingConfig.backendOnly,
|
|
465
|
+
swaggerDocs: existingConfig.swaggerDocs,
|
|
466
|
+
tailwindcss: existingConfig.tailwindcss,
|
|
467
|
+
websocket: existingConfig.websocket,
|
|
468
|
+
mcp: existingConfig.mcp,
|
|
469
|
+
prisma: existingConfig.prisma,
|
|
470
|
+
docker: existingConfig.docker,
|
|
471
|
+
};
|
|
472
|
+
// Set up as an update
|
|
473
|
+
let excludeFiles = [];
|
|
474
|
+
existingConfig.excludeFiles?.map((file) => {
|
|
475
|
+
const filePath = path.join(projectPath, file);
|
|
476
|
+
if (fs.existsSync(filePath))
|
|
477
|
+
excludeFiles.push(filePath.replace(/\\/g, "/"));
|
|
478
|
+
});
|
|
479
|
+
updateAnswer = {
|
|
480
|
+
...answer,
|
|
481
|
+
isUpdate: true,
|
|
482
|
+
excludeFiles: existingConfig.excludeFiles ?? [],
|
|
483
|
+
excludeFilePath: excludeFiles ?? [],
|
|
484
|
+
filePath: projectPath,
|
|
485
|
+
};
|
|
156
486
|
}
|
|
157
|
-
|
|
158
|
-
|
|
487
|
+
} else {
|
|
488
|
+
// Regular project handling (existing logic)
|
|
489
|
+
const configPath = path.join(currentDir, "prisma-php.json");
|
|
490
|
+
const projectNamePath = path.join(currentDir, projectName);
|
|
491
|
+
const projectNameConfigPath = path.join(
|
|
492
|
+
projectNamePath,
|
|
493
|
+
"prisma-php.json"
|
|
494
|
+
);
|
|
495
|
+
if (fs.existsSync(configPath)) {
|
|
496
|
+
projectPath = currentDir;
|
|
497
|
+
} else if (
|
|
498
|
+
fs.existsSync(projectNamePath) &&
|
|
499
|
+
fs.existsSync(projectNameConfigPath)
|
|
500
|
+
) {
|
|
501
|
+
projectPath = projectNamePath;
|
|
502
|
+
process.chdir(projectNamePath);
|
|
503
|
+
} else {
|
|
504
|
+
if (!fs.existsSync(projectNamePath)) {
|
|
505
|
+
fs.mkdirSync(projectNamePath, { recursive: true });
|
|
506
|
+
}
|
|
507
|
+
projectPath = projectNamePath;
|
|
508
|
+
process.chdir(projectNamePath);
|
|
159
509
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
510
|
+
}
|
|
511
|
+
} else {
|
|
512
|
+
// Interactive mode
|
|
513
|
+
fs.mkdirSync(answer.projectName, { recursive: true });
|
|
514
|
+
projectPath = path.join(currentDir, answer.projectName);
|
|
515
|
+
process.chdir(answer.projectName);
|
|
516
|
+
}
|
|
517
|
+
let npmDependencies = [
|
|
518
|
+
npmPkg("typescript"),
|
|
519
|
+
npmPkg("@types/node"),
|
|
520
|
+
npmPkg("tsx"),
|
|
521
|
+
npmPkg("http-proxy-middleware"),
|
|
522
|
+
npmPkg("chalk"),
|
|
523
|
+
npmPkg("npm-run-all"),
|
|
524
|
+
npmPkg("browser-sync"),
|
|
525
|
+
npmPkg("@types/browser-sync"),
|
|
526
|
+
npmPkg("php-parser"),
|
|
527
|
+
];
|
|
528
|
+
let composerDependencies = [
|
|
529
|
+
composerPkg("vlucas/phpdotenv"),
|
|
530
|
+
composerPkg("firebase/php-jwt"),
|
|
531
|
+
composerPkg("phpmailer/phpmailer"),
|
|
532
|
+
composerPkg("guzzlehttp/guzzle"),
|
|
533
|
+
composerPkg("ezyang/htmlpurifier"),
|
|
534
|
+
composerPkg("symfony/uid"),
|
|
535
|
+
composerPkg("brick/math"),
|
|
536
|
+
// composerPkg("tsnc/prisma-php"),
|
|
537
|
+
];
|
|
538
|
+
if (answer.swaggerDocs) {
|
|
539
|
+
npmDependencies.push(
|
|
540
|
+
npmPkg("swagger-jsdoc"),
|
|
541
|
+
npmPkg("@types/swagger-jsdoc")
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
if (answer.swaggerDocs && answer.prisma) {
|
|
545
|
+
npmDependencies.push(npmPkg("prompts"), npmPkg("@types/prompts"));
|
|
546
|
+
}
|
|
547
|
+
if (answer.tailwindcss) {
|
|
548
|
+
npmDependencies.push(
|
|
549
|
+
npmPkg("tailwindcss"),
|
|
550
|
+
npmPkg("postcss"),
|
|
551
|
+
npmPkg("postcss-cli"),
|
|
552
|
+
npmPkg("@tailwindcss/postcss"),
|
|
553
|
+
npmPkg("cssnano")
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
if (answer.websocket) {
|
|
557
|
+
composerDependencies.push("cboden/ratchet");
|
|
558
|
+
}
|
|
559
|
+
if (answer.mcp) {
|
|
560
|
+
composerDependencies.push("php-mcp/server");
|
|
561
|
+
}
|
|
562
|
+
if (answer.prisma) {
|
|
563
|
+
execSync("npm install -g prisma-client-php", { stdio: "inherit" });
|
|
564
|
+
}
|
|
565
|
+
// Only setup starter kit if it's not already done
|
|
566
|
+
if (answer.starterKit && !isStarterKitProject) {
|
|
567
|
+
await setupStarterKit(projectPath, answer);
|
|
568
|
+
}
|
|
569
|
+
await installNpmDependencies(projectPath, npmDependencies, true);
|
|
570
|
+
await installComposerDependencies(projectPath, composerDependencies);
|
|
571
|
+
if (!projectName) {
|
|
572
|
+
execSync("npx tsc --init", { stdio: "inherit" });
|
|
573
|
+
}
|
|
574
|
+
await createDirectoryStructure(projectPath, answer);
|
|
575
|
+
if (answer.prisma) {
|
|
576
|
+
execSync("npx ppo init --prisma-php", { stdio: "inherit" });
|
|
577
|
+
}
|
|
578
|
+
if (answer.swaggerDocs) {
|
|
579
|
+
const swaggerDocsPath = path.join(
|
|
580
|
+
projectPath,
|
|
581
|
+
"src",
|
|
582
|
+
"app",
|
|
583
|
+
"swagger-docs"
|
|
584
|
+
);
|
|
585
|
+
// Check if the directory exists
|
|
586
|
+
if (fs.existsSync(swaggerDocsPath)) {
|
|
587
|
+
// If it exists and is not empty, remove it before cloning
|
|
588
|
+
if (fs.readdirSync(swaggerDocsPath).length > 0) {
|
|
589
|
+
console.log("Removing existing swagger-docs directory...");
|
|
590
|
+
fs.rmSync(swaggerDocsPath, { recursive: true, force: true });
|
|
192
591
|
}
|
|
193
|
-
|
|
194
|
-
|
|
592
|
+
}
|
|
593
|
+
// Clone the Git repository into the swagger-docs directory
|
|
594
|
+
execSync(
|
|
595
|
+
`git clone https://github.com/TheSteelNinjaCode/prisma-php-swagger-docs.git ${swaggerDocsPath}`,
|
|
596
|
+
{ stdio: "inherit" }
|
|
597
|
+
);
|
|
598
|
+
// delete the folder .git
|
|
599
|
+
fs.rmSync(path.join(swaggerDocsPath, ".git"), {
|
|
600
|
+
recursive: true,
|
|
601
|
+
force: true,
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
if (updateAnswer?.isUpdate) {
|
|
605
|
+
const updateUninstallNpmDependencies = [];
|
|
606
|
+
const updateUninstallComposerDependencies = [];
|
|
607
|
+
// Helper function to check if a composer package is installed
|
|
608
|
+
const isComposerPackageInstalled = (packageName) => {
|
|
609
|
+
try {
|
|
610
|
+
const composerJsonPath = path.join(projectPath, "composer.json");
|
|
611
|
+
if (fs.existsSync(composerJsonPath)) {
|
|
612
|
+
const composerJson = JSON.parse(
|
|
613
|
+
fs.readFileSync(composerJsonPath, "utf8")
|
|
614
|
+
);
|
|
615
|
+
return !!(
|
|
616
|
+
composerJson.require && composerJson.require[packageName]
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
return false;
|
|
620
|
+
} catch {
|
|
621
|
+
return false;
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
// Helper function to check if an npm package is installed
|
|
625
|
+
const isNpmPackageInstalled = (packageName) => {
|
|
626
|
+
try {
|
|
627
|
+
const packageJsonPath = path.join(projectPath, "package.json");
|
|
628
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
629
|
+
const packageJson = JSON.parse(
|
|
630
|
+
fs.readFileSync(packageJsonPath, "utf8")
|
|
631
|
+
);
|
|
632
|
+
return !!(
|
|
633
|
+
(packageJson.dependencies &&
|
|
634
|
+
packageJson.dependencies[packageName]) ||
|
|
635
|
+
(packageJson.devDependencies &&
|
|
636
|
+
packageJson.devDependencies[packageName])
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
return false;
|
|
640
|
+
} catch {
|
|
641
|
+
return false;
|
|
195
642
|
}
|
|
196
|
-
|
|
197
|
-
|
|
643
|
+
};
|
|
644
|
+
if (updateAnswer.backendOnly) {
|
|
645
|
+
nonBackendFiles.forEach((file) => {
|
|
646
|
+
const filePath = path.join(projectPath, "src", "app", file);
|
|
647
|
+
if (fs.existsSync(filePath)) {
|
|
648
|
+
fs.unlinkSync(filePath);
|
|
649
|
+
console.log(`${file} was deleted successfully.`);
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
const backendOnlyFolders = ["js", "css"];
|
|
653
|
+
backendOnlyFolders.forEach((folder) => {
|
|
654
|
+
const folderPath = path.join(projectPath, "src", "app", folder);
|
|
655
|
+
if (fs.existsSync(folderPath)) {
|
|
656
|
+
fs.rmSync(folderPath, { recursive: true, force: true });
|
|
657
|
+
console.log(`${folder} was deleted successfully.`);
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
if (!updateAnswer.swaggerDocs) {
|
|
662
|
+
const swaggerDocsFolder = path.join(
|
|
663
|
+
projectPath,
|
|
664
|
+
"src",
|
|
665
|
+
"app",
|
|
666
|
+
"swagger-docs"
|
|
667
|
+
);
|
|
668
|
+
if (fs.existsSync(swaggerDocsFolder)) {
|
|
669
|
+
fs.rmSync(swaggerDocsFolder, { recursive: true, force: true });
|
|
670
|
+
console.log(`swagger-docs was deleted successfully.`);
|
|
198
671
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
672
|
+
const swaggerFiles = ["swagger-config.ts"];
|
|
673
|
+
swaggerFiles.forEach((file) => {
|
|
674
|
+
const filePath = path.join(projectPath, "settings", file);
|
|
675
|
+
if (fs.existsSync(filePath)) {
|
|
676
|
+
fs.unlinkSync(filePath);
|
|
677
|
+
console.log(`${file} was deleted successfully.`);
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
// Only add to uninstall list if packages are actually installed
|
|
681
|
+
if (isNpmPackageInstalled("swagger-jsdoc")) {
|
|
682
|
+
updateUninstallNpmDependencies.push("swagger-jsdoc");
|
|
202
683
|
}
|
|
203
|
-
if (
|
|
204
|
-
|
|
684
|
+
if (isNpmPackageInstalled("@types/swagger-jsdoc")) {
|
|
685
|
+
updateUninstallNpmDependencies.push("@types/swagger-jsdoc");
|
|
205
686
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (!projectName) {
|
|
209
|
-
execSync("npx tsc --init", { stdio: "inherit" });
|
|
687
|
+
if (isNpmPackageInstalled("prompts")) {
|
|
688
|
+
updateUninstallNpmDependencies.push("prompts");
|
|
210
689
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
execSync("npx ppo init --prisma-php", { stdio: "inherit" });
|
|
690
|
+
if (isNpmPackageInstalled("@types/prompts")) {
|
|
691
|
+
updateUninstallNpmDependencies.push("@types/prompts");
|
|
214
692
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
693
|
+
}
|
|
694
|
+
if (!updateAnswer.tailwindcss) {
|
|
695
|
+
const tailwindFiles = ["postcss.config.js"];
|
|
696
|
+
tailwindFiles.forEach((file) => {
|
|
697
|
+
const filePath = path.join(projectPath, file);
|
|
698
|
+
if (fs.existsSync(filePath)) {
|
|
699
|
+
fs.unlinkSync(filePath);
|
|
700
|
+
console.log(`${file} was deleted successfully.`);
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
// Only add to uninstall list if packages are actually installed
|
|
704
|
+
const tailwindPackages = [
|
|
705
|
+
"tailwindcss",
|
|
706
|
+
"postcss",
|
|
707
|
+
"postcss-cli",
|
|
708
|
+
"@tailwindcss/postcss",
|
|
709
|
+
"cssnano",
|
|
710
|
+
];
|
|
711
|
+
tailwindPackages.forEach((pkg) => {
|
|
712
|
+
if (isNpmPackageInstalled(pkg)) {
|
|
713
|
+
updateUninstallNpmDependencies.push(pkg);
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
if (!updateAnswer.websocket) {
|
|
718
|
+
const websocketFiles = ["restart-websocket.ts"];
|
|
719
|
+
websocketFiles.forEach((file) => {
|
|
720
|
+
const filePath = path.join(projectPath, "settings", file);
|
|
721
|
+
if (fs.existsSync(filePath)) {
|
|
722
|
+
fs.unlinkSync(filePath);
|
|
723
|
+
console.log(`${file} was deleted successfully.`);
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
const websocketFolder = path.join(
|
|
727
|
+
projectPath,
|
|
728
|
+
"src",
|
|
729
|
+
"Lib",
|
|
730
|
+
"Websocket"
|
|
731
|
+
);
|
|
732
|
+
if (fs.existsSync(websocketFolder)) {
|
|
733
|
+
fs.rmSync(websocketFolder, { recursive: true, force: true });
|
|
734
|
+
console.log(`Websocket folder was deleted successfully.`);
|
|
232
735
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if (updateAnswer.backendOnly) {
|
|
237
|
-
nonBackendFiles.forEach((file) => {
|
|
238
|
-
const filePath = path.join(projectPath, "src", "app", file);
|
|
239
|
-
if (fs.existsSync(filePath)) {
|
|
240
|
-
fs.unlinkSync(filePath); // Delete each file if it exists
|
|
241
|
-
console.log(`${file} was deleted successfully.`);
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
console.log(`${file} does not exist.`);
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
const backendOnlyFolders = ["js", "css"];
|
|
248
|
-
backendOnlyFolders.forEach((folder) => {
|
|
249
|
-
const folderPath = path.join(projectPath, "src", "app", folder);
|
|
250
|
-
if (fs.existsSync(folderPath)) {
|
|
251
|
-
fs.rmSync(folderPath, { recursive: true, force: true }); // Use fs.rmSync instead of fs.rmdirSync
|
|
252
|
-
console.log(`${folder} was deleted successfully.`);
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
console.log(`${folder} does not exist.`);
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
if (!updateAnswer.swaggerDocs) {
|
|
260
|
-
const swaggerDocsFolder = path.join(projectPath, "src", "app", "swagger-docs");
|
|
261
|
-
if (fs.existsSync(swaggerDocsFolder)) {
|
|
262
|
-
fs.rmSync(swaggerDocsFolder, { recursive: true, force: true }); // Use fs.rmSync instead of fs.rmdirSync
|
|
263
|
-
console.log(`swagger-docs was deleted successfully.`);
|
|
264
|
-
}
|
|
265
|
-
const swaggerFiles = ["swagger-config.ts"];
|
|
266
|
-
swaggerFiles.forEach((file) => {
|
|
267
|
-
const filePath = path.join(projectPath, "settings", file);
|
|
268
|
-
if (fs.existsSync(filePath)) {
|
|
269
|
-
fs.unlinkSync(filePath); // Delete each file if it exists
|
|
270
|
-
console.log(`${file} was deleted successfully.`);
|
|
271
|
-
}
|
|
272
|
-
else {
|
|
273
|
-
console.log(`${file} does not exist.`);
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
updateUninstallNpmDependencies.push("swagger-jsdoc", "@types/swagger-jsdoc", "prompts", "@types/prompts");
|
|
277
|
-
}
|
|
278
|
-
if (!updateAnswer.tailwindcss) {
|
|
279
|
-
const tailwindFiles = ["postcss.config.js"];
|
|
280
|
-
tailwindFiles.forEach((file) => {
|
|
281
|
-
const filePath = path.join(projectPath, file);
|
|
282
|
-
if (fs.existsSync(filePath)) {
|
|
283
|
-
fs.unlinkSync(filePath); // Delete each file if it exists
|
|
284
|
-
console.log(`${file} was deleted successfully.`);
|
|
285
|
-
}
|
|
286
|
-
else {
|
|
287
|
-
console.log(`${file} does not exist.`);
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
updateUninstallNpmDependencies.push("tailwindcss", "postcss", "postcss-cli", "@tailwindcss/postcss", "cssnano");
|
|
291
|
-
}
|
|
292
|
-
if (!updateAnswer.websocket) {
|
|
293
|
-
const websocketFiles = [
|
|
294
|
-
"restart-websocket.ts",
|
|
295
|
-
"restart-websocket.bat",
|
|
296
|
-
];
|
|
297
|
-
websocketFiles.forEach((file) => {
|
|
298
|
-
const filePath = path.join(projectPath, "settings", file);
|
|
299
|
-
if (fs.existsSync(filePath)) {
|
|
300
|
-
fs.unlinkSync(filePath); // Delete each file if it exists
|
|
301
|
-
console.log(`${file} was deleted successfully.`);
|
|
302
|
-
}
|
|
303
|
-
else {
|
|
304
|
-
console.log(`${file} does not exist.`);
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
const websocketFolder = path.join(projectPath, "src", "Websocket");
|
|
308
|
-
if (fs.existsSync(websocketFolder)) {
|
|
309
|
-
fs.rmSync(websocketFolder, { recursive: true, force: true }); // Use fs.rmSync instead of fs.rmdirSync
|
|
310
|
-
console.log(`Websocket folder was deleted successfully.`);
|
|
311
|
-
}
|
|
312
|
-
const websocketServerFile = path.join(projectPath, "websocket-server.php");
|
|
313
|
-
if (fs.existsSync(websocketServerFile)) {
|
|
314
|
-
fs.unlinkSync(websocketServerFile); // Delete the file if it exists
|
|
315
|
-
console.log(`websocket-server.php was deleted successfully.`);
|
|
316
|
-
}
|
|
317
|
-
updateUninstallNpmDependencies.push("chokidar-cli");
|
|
318
|
-
updateUninstallComposerDependencies.push("cboden/ratchet");
|
|
319
|
-
}
|
|
320
|
-
if (!updateAnswer.prisma) {
|
|
321
|
-
updateUninstallNpmDependencies.push("prisma", "@prisma/client", "@prisma/internals");
|
|
322
|
-
}
|
|
323
|
-
if (!updateAnswer.docker) {
|
|
324
|
-
const dockerFiles = [
|
|
325
|
-
".dockerignore",
|
|
326
|
-
"docker-compose.yml",
|
|
327
|
-
"Dockerfile",
|
|
328
|
-
"apache.conf",
|
|
329
|
-
];
|
|
330
|
-
dockerFiles.forEach((file) => {
|
|
331
|
-
const filePath = path.join(projectPath, file);
|
|
332
|
-
if (fs.existsSync(filePath)) {
|
|
333
|
-
fs.unlinkSync(filePath); // Delete each file if it exists
|
|
334
|
-
console.log(`${file} was deleted successfully.`);
|
|
335
|
-
}
|
|
336
|
-
else {
|
|
337
|
-
console.log(`${file} does not exist.`);
|
|
338
|
-
}
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
if (updateUninstallNpmDependencies.length > 0) {
|
|
342
|
-
await uninstallNpmDependencies(projectPath, updateUninstallNpmDependencies, true);
|
|
343
|
-
}
|
|
344
|
-
if (updateUninstallComposerDependencies.length > 0) {
|
|
345
|
-
await uninstallComposerDependencies(projectPath, updateUninstallComposerDependencies);
|
|
346
|
-
}
|
|
736
|
+
// composer package for websocket only
|
|
737
|
+
if (isComposerPackageInstalled("cboden/ratchet")) {
|
|
738
|
+
updateUninstallComposerDependencies.push("cboden/ratchet");
|
|
347
739
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
docker: answer.docker,
|
|
363
|
-
version: latestVersionOfCreatePrismaPhpApp,
|
|
364
|
-
excludeFiles: updateAnswer?.excludeFiles ?? [],
|
|
365
|
-
};
|
|
366
|
-
fs.writeFileSync(path.join(projectPath, "prisma-php.json"), JSON.stringify(prismaPhpConfig, null, 2), { flag: "w" });
|
|
367
|
-
if (updateAnswer?.isUpdate) {
|
|
368
|
-
execSync("C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar update", {
|
|
369
|
-
stdio: "inherit",
|
|
370
|
-
});
|
|
740
|
+
}
|
|
741
|
+
if (!updateAnswer.mcp) {
|
|
742
|
+
const mcpFiles = ["restart-mcp.ts"];
|
|
743
|
+
mcpFiles.forEach((file) => {
|
|
744
|
+
const filePath = path.join(projectPath, "settings", file);
|
|
745
|
+
if (fs.existsSync(filePath)) {
|
|
746
|
+
fs.unlinkSync(filePath);
|
|
747
|
+
console.log(`${file} was deleted successfully.`);
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
const mcpFolder = path.join(projectPath, "src", "Lib", "MCP");
|
|
751
|
+
if (fs.existsSync(mcpFolder)) {
|
|
752
|
+
fs.rmSync(mcpFolder, { recursive: true, force: true });
|
|
753
|
+
console.log(`MCP folder was deleted successfully.`);
|
|
371
754
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
});
|
|
755
|
+
// composer package for MCP only
|
|
756
|
+
if (isComposerPackageInstalled("php-mcp/server")) {
|
|
757
|
+
updateUninstallComposerDependencies.push("php-mcp/server");
|
|
376
758
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
759
|
+
}
|
|
760
|
+
if (!updateAnswer.prisma) {
|
|
761
|
+
const prismaPackages = [
|
|
762
|
+
"prisma",
|
|
763
|
+
"@prisma/client",
|
|
764
|
+
"@prisma/internals",
|
|
765
|
+
];
|
|
766
|
+
prismaPackages.forEach((pkg) => {
|
|
767
|
+
if (isNpmPackageInstalled(pkg)) {
|
|
768
|
+
updateUninstallNpmDependencies.push(pkg);
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
if (!updateAnswer.docker) {
|
|
773
|
+
const dockerFiles = [
|
|
774
|
+
".dockerignore",
|
|
775
|
+
"docker-compose.yml",
|
|
776
|
+
"Dockerfile",
|
|
777
|
+
"apache.conf",
|
|
778
|
+
];
|
|
779
|
+
dockerFiles.forEach((file) => {
|
|
780
|
+
const filePath = path.join(projectPath, file);
|
|
781
|
+
if (fs.existsSync(filePath)) {
|
|
782
|
+
fs.unlinkSync(filePath);
|
|
783
|
+
console.log(`${file} was deleted successfully.`);
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
// Only uninstall if there are packages to uninstall
|
|
788
|
+
const uniq = (arr) => Array.from(new Set(arr));
|
|
789
|
+
const npmToUninstall = uniq(updateUninstallNpmDependencies);
|
|
790
|
+
const composerToUninstall = uniq(updateUninstallComposerDependencies);
|
|
791
|
+
if (npmToUninstall.length > 0) {
|
|
792
|
+
console.log(`Uninstalling npm packages: ${npmToUninstall.join(", ")}`);
|
|
793
|
+
await uninstallNpmDependencies(projectPath, npmToUninstall, true);
|
|
794
|
+
}
|
|
795
|
+
if (composerToUninstall.length > 0) {
|
|
796
|
+
console.log(
|
|
797
|
+
`Uninstalling composer packages: ${composerToUninstall.join(", ")}`
|
|
798
|
+
);
|
|
799
|
+
await uninstallComposerDependencies(projectPath, composerToUninstall);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
// Skip creating prismaPhpConfig if it's a starter kit project that already has one
|
|
803
|
+
if (
|
|
804
|
+
!isStarterKitProject ||
|
|
805
|
+
!fs.existsSync(path.join(projectPath, "prisma-php.json"))
|
|
806
|
+
) {
|
|
807
|
+
// Create prisma-php.json with all the existing logic
|
|
808
|
+
const projectPathModified = projectPath.replace(/\\/g, "\\");
|
|
809
|
+
const bsConfig = bsConfigUrls(projectPathModified);
|
|
810
|
+
const prismaPhpConfig = {
|
|
811
|
+
projectName: answer.projectName,
|
|
812
|
+
projectRootPath: projectPathModified,
|
|
813
|
+
phpEnvironment: "XAMPP",
|
|
814
|
+
phpRootPathExe: "C:\\xampp\\php\\php.exe",
|
|
815
|
+
bsTarget: bsConfig.bsTarget,
|
|
816
|
+
bsPathRewrite: bsConfig.bsPathRewrite,
|
|
817
|
+
backendOnly: answer.backendOnly,
|
|
818
|
+
swaggerDocs: answer.swaggerDocs,
|
|
819
|
+
tailwindcss: answer.tailwindcss,
|
|
820
|
+
websocket: answer.websocket,
|
|
821
|
+
mcp: answer.mcp,
|
|
822
|
+
prisma: answer.prisma,
|
|
823
|
+
docker: answer.docker,
|
|
824
|
+
version: latestVersionOfCreatePrismaPhpApp,
|
|
825
|
+
excludeFiles: updateAnswer?.excludeFiles ?? [],
|
|
826
|
+
};
|
|
827
|
+
fs.writeFileSync(
|
|
828
|
+
path.join(projectPath, "prisma-php.json"),
|
|
829
|
+
JSON.stringify(prismaPhpConfig, null, 2),
|
|
830
|
+
{ flag: "w" }
|
|
831
|
+
);
|
|
380
832
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
833
|
+
if (updateAnswer?.isUpdate) {
|
|
834
|
+
execSync(
|
|
835
|
+
"C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar update",
|
|
836
|
+
{
|
|
837
|
+
stdio: "inherit",
|
|
838
|
+
}
|
|
839
|
+
);
|
|
840
|
+
} else {
|
|
841
|
+
execSync(
|
|
842
|
+
"C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar install",
|
|
843
|
+
{
|
|
844
|
+
stdio: "inherit",
|
|
845
|
+
}
|
|
846
|
+
);
|
|
384
847
|
}
|
|
848
|
+
console.log("\n=========================\n");
|
|
849
|
+
console.log(
|
|
850
|
+
`${chalk.green(
|
|
851
|
+
"Success!"
|
|
852
|
+
)} Prisma PHP project successfully created in ${chalk.green(
|
|
853
|
+
projectPath.replace(/\\/g, "/")
|
|
854
|
+
)}!`
|
|
855
|
+
);
|
|
856
|
+
console.log("\n=========================");
|
|
857
|
+
} catch (error) {
|
|
858
|
+
console.error("Error while creating the project:", error);
|
|
859
|
+
process.exit(1);
|
|
860
|
+
}
|
|
385
861
|
}
|
|
386
862
|
main();
|