create-prisma-php-app 4.0.0-alpha.3 → 4.0.0-alpha.31
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 +461 -111
- 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 +641 -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)return{projectName:e.projectName??"my-app",starterKit:s,starterKitSource:e.starterKitSource,backendOnly:!1,tailwindcss:!0,websocket:!1,prisma:!0,docker:!1,swaggerDocs:!0,mcp:!1}}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,7 +7,11 @@ 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
|
-
|
|
10
|
+
if (!fs.existsSync(path.join(baseDir, "package.json"))) {
|
|
11
|
+
console.log("Initializing new Node.js project...");
|
|
12
|
+
} else {
|
|
13
|
+
console.log("Updating existing Node.js project...");
|
|
14
|
+
}
|
|
11
15
|
// Initialize a package.json if it doesn't exist
|
|
12
16
|
if (!fs.existsSync(path.join(baseDir, "package.json"))) {
|
|
13
17
|
execSync("npm init -y", {
|
|
@@ -32,27 +36,99 @@ async function installNpmDependencies(baseDir, dependencies, isDev = false) {
|
|
|
32
36
|
cwd: baseDir,
|
|
33
37
|
});
|
|
34
38
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
+
);
|
|
95
|
+
}
|
|
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) {
|
|
105
|
+
console.log("Installing Composer dependencies:");
|
|
106
|
+
dependencies.forEach((d) => console.log(`- ${chalk.blue(d)}`));
|
|
39
107
|
execSync(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
108
|
+
`${cmd} ${[
|
|
109
|
+
...baseArgs,
|
|
110
|
+
"require",
|
|
111
|
+
"--no-interaction",
|
|
112
|
+
...dependencies,
|
|
113
|
+
].join(" ")}`,
|
|
114
|
+
{ stdio: "inherit", cwd: baseDir }
|
|
45
115
|
);
|
|
46
116
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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(" ")}`, {
|
|
56
132
|
stdio: "inherit",
|
|
57
133
|
cwd: baseDir,
|
|
58
134
|
});
|
|
@@ -60,21 +136,20 @@ async function installComposerDependencies(baseDir, dependencies) {
|
|
|
60
136
|
const npmPinnedVersions = {
|
|
61
137
|
"@tailwindcss/postcss": "^4.1.11",
|
|
62
138
|
"@types/browser-sync": "^2.29.0",
|
|
63
|
-
"@types/node": "^24.
|
|
139
|
+
"@types/node": "^24.2.1",
|
|
64
140
|
"@types/prompts": "^2.4.9",
|
|
65
141
|
"browser-sync": "^3.0.4",
|
|
66
|
-
chalk: "^5.
|
|
67
|
-
|
|
68
|
-
cssnano: "^7.0.7",
|
|
142
|
+
chalk: "^5.5.0",
|
|
143
|
+
cssnano: "^7.1.0",
|
|
69
144
|
"http-proxy-middleware": "^3.0.5",
|
|
70
145
|
"npm-run-all": "^4.1.5",
|
|
71
|
-
"php-parser": "^3.2.
|
|
146
|
+
"php-parser": "^3.2.5",
|
|
72
147
|
postcss: "^8.5.6",
|
|
73
148
|
"postcss-cli": "^11.0.1",
|
|
74
149
|
prompts: "^2.4.2",
|
|
75
150
|
tailwindcss: "^4.1.11",
|
|
76
151
|
tsx: "^4.20.3",
|
|
77
|
-
typescript: "^5.
|
|
152
|
+
typescript: "^5.9.2",
|
|
78
153
|
};
|
|
79
154
|
function npmPkg(name) {
|
|
80
155
|
return npmPinnedVersions[name] ? `${name}@${npmPinnedVersions[name]}` : name;
|
|
@@ -88,42 +163,197 @@ const composerPinnedVersions = {
|
|
|
88
163
|
"symfony/uid": "^7.2.0",
|
|
89
164
|
"brick/math": "^0.13.1",
|
|
90
165
|
"cboden/ratchet": "^0.4.4",
|
|
91
|
-
"tsnc/prisma-php": "^1.0.
|
|
166
|
+
"tsnc/prisma-php": "^1.0.0",
|
|
167
|
+
"php-mcp/server": "3.3.0",
|
|
92
168
|
};
|
|
93
169
|
function composerPkg(name) {
|
|
94
170
|
return composerPinnedVersions[name]
|
|
95
171
|
? `${name}:${composerPinnedVersions[name]}`
|
|
96
172
|
: name;
|
|
97
173
|
}
|
|
174
|
+
async function downloadStarterKit(starterKit, tempDir) {
|
|
175
|
+
if (!starterKit.source) {
|
|
176
|
+
throw new Error("No source defined for starter kit");
|
|
177
|
+
}
|
|
178
|
+
const { type, url, branch = "main", subfolder } = starterKit.source;
|
|
179
|
+
switch (type) {
|
|
180
|
+
case "git":
|
|
181
|
+
console.log(chalk.blue(`Cloning ${starterKit.name} from ${url}...`));
|
|
182
|
+
const cloneCommand = branch
|
|
183
|
+
? `git clone -b ${branch} --depth 1 ${url} ${tempDir}`
|
|
184
|
+
: `git clone --depth 1 ${url} ${tempDir}`;
|
|
185
|
+
execSync(cloneCommand, { stdio: "inherit" });
|
|
186
|
+
// Remove .git directory
|
|
187
|
+
const gitDir = path.join(tempDir, ".git");
|
|
188
|
+
if (fs.existsSync(gitDir)) {
|
|
189
|
+
fs.rmSync(gitDir, { recursive: true, force: true });
|
|
190
|
+
}
|
|
191
|
+
// Return the subfolder if specified
|
|
192
|
+
return subfolder ? path.join(tempDir, subfolder) : tempDir;
|
|
193
|
+
case "npm":
|
|
194
|
+
console.log(chalk.blue(`Downloading ${starterKit.name} from npm...`));
|
|
195
|
+
execSync(`npm pack ${url}`, { cwd: tempDir, stdio: "inherit" });
|
|
196
|
+
// Extract the tarball
|
|
197
|
+
const tarball = fs.readdirSync(tempDir).find((f) => f.endsWith(".tgz"));
|
|
198
|
+
if (tarball) {
|
|
199
|
+
execSync(`tar -xzf ${tarball}`, { cwd: tempDir });
|
|
200
|
+
fs.unlinkSync(path.join(tempDir, tarball));
|
|
201
|
+
return path.join(tempDir, "package");
|
|
202
|
+
}
|
|
203
|
+
throw new Error("Failed to extract npm package");
|
|
204
|
+
case "url":
|
|
205
|
+
throw new Error("URL download not implemented yet");
|
|
206
|
+
default:
|
|
207
|
+
throw new Error(`Unsupported source type: ${type}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async function mergeStarterKitFiles(starterKitPath, projectPath, answer) {
|
|
211
|
+
console.log(chalk.blue("Merging starter kit files..."));
|
|
212
|
+
// Copy all files from starter kit, but don't overwrite base files
|
|
213
|
+
copyRecursiveSync(starterKitPath, projectPath, answer);
|
|
214
|
+
// Look for starter kit specific configuration
|
|
215
|
+
const starterKitConfig = path.join(starterKitPath, "starter-kit.json");
|
|
216
|
+
if (fs.existsSync(starterKitConfig)) {
|
|
217
|
+
const config = JSON.parse(fs.readFileSync(starterKitConfig, "utf8"));
|
|
218
|
+
// Handle post-install scripts
|
|
219
|
+
if (config.postInstall) {
|
|
220
|
+
console.log(chalk.blue("Running post-install scripts..."));
|
|
221
|
+
for (const script of config.postInstall) {
|
|
222
|
+
console.log(chalk.gray(`Running: ${script}`));
|
|
223
|
+
execSync(script, { cwd: projectPath, stdio: "inherit" });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// Handle additional dependencies
|
|
227
|
+
if (config.additionalNpmDependencies) {
|
|
228
|
+
await installNpmDependencies(
|
|
229
|
+
projectPath,
|
|
230
|
+
config.additionalNpmDependencies.map(npmPkg),
|
|
231
|
+
true
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
if (config.additionalComposerDependencies) {
|
|
235
|
+
await installComposerDependencies(
|
|
236
|
+
projectPath,
|
|
237
|
+
config.additionalComposerDependencies.map(composerPkg)
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async function setupStarterKit(baseDir, answer) {
|
|
243
|
+
if (!answer.starterKit) return;
|
|
244
|
+
let starterKit = null;
|
|
245
|
+
// Check if it's a built-in starter kit
|
|
246
|
+
if (STARTER_KITS[answer.starterKit]) {
|
|
247
|
+
starterKit = STARTER_KITS[answer.starterKit];
|
|
248
|
+
}
|
|
249
|
+
// Handle custom starter kit URL
|
|
250
|
+
else if (answer.starterKitSource) {
|
|
251
|
+
starterKit = {
|
|
252
|
+
id: answer.starterKit,
|
|
253
|
+
name: `Custom Starter Kit (${answer.starterKit})`,
|
|
254
|
+
description: "Custom starter kit from external source",
|
|
255
|
+
features: {}, // Will be determined from the downloaded kit
|
|
256
|
+
requiredFiles: [],
|
|
257
|
+
source: {
|
|
258
|
+
type: "git", // Assume git for now, could be enhanced
|
|
259
|
+
url: answer.starterKitSource,
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
if (!starterKit) {
|
|
264
|
+
console.warn(
|
|
265
|
+
chalk.yellow(`Starter kit '${answer.starterKit}' not found. Skipping...`)
|
|
266
|
+
);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
console.log(chalk.green(`Setting up ${starterKit.name}...`));
|
|
270
|
+
// If it's a custom starter kit with source, download it
|
|
271
|
+
if (starterKit.source) {
|
|
272
|
+
const tempDir = path.join(baseDir, ".temp-starter-kit");
|
|
273
|
+
try {
|
|
274
|
+
// Create temp directory
|
|
275
|
+
if (fs.existsSync(tempDir)) {
|
|
276
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
277
|
+
}
|
|
278
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
279
|
+
// Download the starter kit
|
|
280
|
+
const kitPath = await downloadStarterKit(starterKit, tempDir);
|
|
281
|
+
// Merge files from starter kit
|
|
282
|
+
await mergeStarterKitFiles(kitPath, baseDir, answer);
|
|
283
|
+
// Check if starter kit has its own configuration
|
|
284
|
+
const kitConfigPath = path.join(kitPath, "prisma-php-starter.json");
|
|
285
|
+
if (fs.existsSync(kitConfigPath)) {
|
|
286
|
+
const kitConfig = JSON.parse(fs.readFileSync(kitConfigPath, "utf8"));
|
|
287
|
+
// Override features with starter kit configuration
|
|
288
|
+
Object.assign(answer, kitConfig.features || {});
|
|
289
|
+
console.log(chalk.green(`Applied starter kit configuration`));
|
|
290
|
+
}
|
|
291
|
+
// Clean up temp directory
|
|
292
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
293
|
+
} catch (error) {
|
|
294
|
+
console.error(chalk.red(`Failed to setup starter kit: ${error}`));
|
|
295
|
+
// Clean up temp directory on error
|
|
296
|
+
if (fs.existsSync(tempDir)) {
|
|
297
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
298
|
+
}
|
|
299
|
+
throw error;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// Run custom setup if defined
|
|
303
|
+
if (starterKit.customSetup) {
|
|
304
|
+
await starterKit.customSetup(baseDir, answer);
|
|
305
|
+
}
|
|
306
|
+
console.log(chalk.green(`✓ ${starterKit.name} setup complete!`));
|
|
307
|
+
}
|
|
308
|
+
function showStarterKits() {
|
|
309
|
+
console.log(chalk.blue("\n🚀 Available Starter Kits:\n"));
|
|
310
|
+
Object.values(STARTER_KITS).forEach((kit) => {
|
|
311
|
+
const isCustom = kit.source ? " (Custom)" : " (Built-in)";
|
|
312
|
+
console.log(chalk.green(` ${kit.id}${chalk.gray(isCustom)}`));
|
|
313
|
+
console.log(` ${kit.name}`);
|
|
314
|
+
console.log(chalk.gray(` ${kit.description}`));
|
|
315
|
+
if (kit.source) {
|
|
316
|
+
console.log(chalk.cyan(` Source: ${kit.source.url}`));
|
|
317
|
+
}
|
|
318
|
+
const features = Object.entries(kit.features)
|
|
319
|
+
.filter(([, value]) => value === true)
|
|
320
|
+
.map(([key]) => key)
|
|
321
|
+
.join(", ");
|
|
322
|
+
if (features) {
|
|
323
|
+
console.log(chalk.magenta(` Features: ${features}`));
|
|
324
|
+
}
|
|
325
|
+
console.log();
|
|
326
|
+
});
|
|
327
|
+
console.log(chalk.yellow("Usage:"));
|
|
328
|
+
console.log(` npx create-prisma-php-app my-project --starter-kit=basic`);
|
|
329
|
+
console.log(
|
|
330
|
+
` npx create-prisma-php-app my-project --starter-kit=custom --starter-kit-source=https://github.com/user/repo`
|
|
331
|
+
);
|
|
332
|
+
console.log();
|
|
333
|
+
}
|
|
98
334
|
async function main() {
|
|
99
335
|
try {
|
|
100
336
|
const args = process.argv.slice(2);
|
|
101
337
|
let projectName = args[0];
|
|
338
|
+
// Parse starter kit arguments
|
|
339
|
+
const starterKitArg = args.find((arg) => arg.startsWith("--starter-kit="));
|
|
340
|
+
const starterKitFromArgs = starterKitArg?.split("=")[1];
|
|
341
|
+
// Parse custom starter kit source
|
|
342
|
+
const starterKitSourceArg = args.find((arg) =>
|
|
343
|
+
arg.startsWith("--starter-kit-source=")
|
|
344
|
+
);
|
|
345
|
+
const starterKitSource = starterKitSourceArg?.split("=")[1];
|
|
346
|
+
// Show help
|
|
347
|
+
if (args.includes("--list-starter-kits")) {
|
|
348
|
+
showStarterKits();
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
102
351
|
let answer = null;
|
|
103
352
|
if (projectName) {
|
|
104
|
-
let useBackendOnly = args.includes("--backend-only");
|
|
105
|
-
let useSwaggerDocs = args.includes("--swagger-docs");
|
|
106
|
-
let useTailwind = args.includes("--tailwindcss");
|
|
107
|
-
let useWebsocket = args.includes("--websocket");
|
|
108
|
-
let usePrisma = args.includes("--prisma");
|
|
109
|
-
let useDocker = args.includes("--docker");
|
|
110
|
-
const predefinedAnswers = {
|
|
111
|
-
projectName,
|
|
112
|
-
backendOnly: useBackendOnly,
|
|
113
|
-
swaggerDocs: useSwaggerDocs,
|
|
114
|
-
tailwindcss: useTailwind,
|
|
115
|
-
websocket: useWebsocket,
|
|
116
|
-
prisma: usePrisma,
|
|
117
|
-
docker: useDocker,
|
|
118
|
-
};
|
|
119
|
-
answer = await getAnswer(predefinedAnswers);
|
|
120
|
-
if (answer === null) {
|
|
121
|
-
console.log(chalk.red("Installation cancelled."));
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
353
|
const currentDir = process.cwd();
|
|
125
354
|
const configPath = path.join(currentDir, "prisma-php.json");
|
|
126
355
|
if (fs.existsSync(configPath)) {
|
|
356
|
+
// It's an update - read existing settings
|
|
127
357
|
const localSettings = readJsonFile(configPath);
|
|
128
358
|
let excludeFiles = [];
|
|
129
359
|
localSettings.excludeFiles?.map((file) => {
|
|
@@ -131,21 +361,75 @@ async function main() {
|
|
|
131
361
|
if (fs.existsSync(filePath))
|
|
132
362
|
excludeFiles.push(filePath.replace(/\\/g, "/"));
|
|
133
363
|
});
|
|
364
|
+
// Set updateAnswer with OLD settings initially (for checkExcludeFiles function)
|
|
134
365
|
updateAnswer = {
|
|
135
366
|
projectName,
|
|
136
|
-
backendOnly:
|
|
137
|
-
swaggerDocs:
|
|
138
|
-
tailwindcss:
|
|
139
|
-
websocket:
|
|
140
|
-
|
|
141
|
-
|
|
367
|
+
backendOnly: localSettings.backendOnly,
|
|
368
|
+
swaggerDocs: localSettings.swaggerDocs,
|
|
369
|
+
tailwindcss: localSettings.tailwindcss,
|
|
370
|
+
websocket: localSettings.websocket,
|
|
371
|
+
mcp: localSettings.mcp,
|
|
372
|
+
prisma: localSettings.prisma,
|
|
373
|
+
docker: localSettings.docker,
|
|
142
374
|
isUpdate: true,
|
|
143
375
|
excludeFiles: localSettings.excludeFiles ?? [],
|
|
144
376
|
excludeFilePath: excludeFiles ?? [],
|
|
145
377
|
filePath: currentDir,
|
|
146
378
|
};
|
|
379
|
+
// For updates, use existing settings but allow CLI overrides
|
|
380
|
+
const predefinedAnswers = {
|
|
381
|
+
projectName,
|
|
382
|
+
backendOnly:
|
|
383
|
+
args.includes("--backend-only") || localSettings.backendOnly,
|
|
384
|
+
swaggerDocs:
|
|
385
|
+
args.includes("--swagger-docs") || localSettings.swaggerDocs,
|
|
386
|
+
tailwindcss:
|
|
387
|
+
args.includes("--tailwindcss") || localSettings.tailwindcss,
|
|
388
|
+
websocket: args.includes("--websocket") || localSettings.websocket,
|
|
389
|
+
prisma: args.includes("--prisma") || localSettings.prisma,
|
|
390
|
+
docker: args.includes("--docker") || localSettings.docker,
|
|
391
|
+
mcp: args.includes("--mcp") || localSettings.mcp,
|
|
392
|
+
};
|
|
393
|
+
answer = await getAnswer(predefinedAnswers);
|
|
394
|
+
// IMPORTANT: Update updateAnswer with the NEW answer after getting user input
|
|
395
|
+
if (answer !== null) {
|
|
396
|
+
updateAnswer = {
|
|
397
|
+
projectName,
|
|
398
|
+
backendOnly: answer.backendOnly,
|
|
399
|
+
swaggerDocs: answer.swaggerDocs,
|
|
400
|
+
tailwindcss: answer.tailwindcss,
|
|
401
|
+
websocket: answer.websocket,
|
|
402
|
+
mcp: answer.mcp,
|
|
403
|
+
prisma: answer.prisma,
|
|
404
|
+
docker: answer.docker,
|
|
405
|
+
isUpdate: true,
|
|
406
|
+
excludeFiles: localSettings.excludeFiles ?? [],
|
|
407
|
+
excludeFilePath: excludeFiles ?? [],
|
|
408
|
+
filePath: currentDir,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
} else {
|
|
412
|
+
// New project
|
|
413
|
+
const predefinedAnswers = {
|
|
414
|
+
projectName,
|
|
415
|
+
starterKit: starterKitFromArgs,
|
|
416
|
+
starterKitSource: starterKitSource,
|
|
417
|
+
backendOnly: args.includes("--backend-only"),
|
|
418
|
+
swaggerDocs: args.includes("--swagger-docs"),
|
|
419
|
+
tailwindcss: args.includes("--tailwindcss"),
|
|
420
|
+
websocket: args.includes("--websocket"),
|
|
421
|
+
mcp: args.includes("--mcp"),
|
|
422
|
+
prisma: args.includes("--prisma"),
|
|
423
|
+
docker: args.includes("--docker"),
|
|
424
|
+
};
|
|
425
|
+
answer = await getAnswer(predefinedAnswers);
|
|
426
|
+
}
|
|
427
|
+
if (answer === null) {
|
|
428
|
+
console.log(chalk.red("Installation cancelled."));
|
|
429
|
+
return;
|
|
147
430
|
}
|
|
148
431
|
} else {
|
|
432
|
+
// Interactive mode
|
|
149
433
|
answer = await getAnswer();
|
|
150
434
|
}
|
|
151
435
|
if (answer === null) {
|
|
@@ -201,7 +485,7 @@ async function main() {
|
|
|
201
485
|
composerPkg("ezyang/htmlpurifier"),
|
|
202
486
|
composerPkg("symfony/uid"),
|
|
203
487
|
composerPkg("brick/math"),
|
|
204
|
-
composerPkg("tsnc/prisma-php"),
|
|
488
|
+
// composerPkg("tsnc/prisma-php"),
|
|
205
489
|
];
|
|
206
490
|
if (answer.swaggerDocs) {
|
|
207
491
|
npmDependencies.push(
|
|
@@ -222,12 +506,18 @@ async function main() {
|
|
|
222
506
|
);
|
|
223
507
|
}
|
|
224
508
|
if (answer.websocket) {
|
|
225
|
-
npmDependencies.push(npmPkg("chokidar-cli"));
|
|
226
509
|
composerDependencies.push("cboden/ratchet");
|
|
227
510
|
}
|
|
511
|
+
if (answer.mcp) {
|
|
512
|
+
composerDependencies.push("php-mcp/server");
|
|
513
|
+
}
|
|
228
514
|
if (answer.prisma) {
|
|
229
515
|
execSync("npm install -g prisma-client-php", { stdio: "inherit" });
|
|
230
516
|
}
|
|
517
|
+
// Add starter kit setup before npm/composer installation
|
|
518
|
+
if (answer.starterKit) {
|
|
519
|
+
await setupStarterKit(projectPath, answer);
|
|
520
|
+
}
|
|
231
521
|
await installNpmDependencies(projectPath, npmDependencies, true);
|
|
232
522
|
await installComposerDependencies(projectPath, composerDependencies);
|
|
233
523
|
if (!projectName) {
|
|
@@ -266,24 +556,57 @@ async function main() {
|
|
|
266
556
|
if (updateAnswer?.isUpdate) {
|
|
267
557
|
const updateUninstallNpmDependencies = [];
|
|
268
558
|
const updateUninstallComposerDependencies = [];
|
|
559
|
+
// Helper function to check if a composer package is installed
|
|
560
|
+
const isComposerPackageInstalled = (packageName) => {
|
|
561
|
+
try {
|
|
562
|
+
const composerJsonPath = path.join(projectPath, "composer.json");
|
|
563
|
+
if (fs.existsSync(composerJsonPath)) {
|
|
564
|
+
const composerJson = JSON.parse(
|
|
565
|
+
fs.readFileSync(composerJsonPath, "utf8")
|
|
566
|
+
);
|
|
567
|
+
return !!(
|
|
568
|
+
composerJson.require && composerJson.require[packageName]
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
return false;
|
|
572
|
+
} catch {
|
|
573
|
+
return false;
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
// Helper function to check if an npm package is installed
|
|
577
|
+
const isNpmPackageInstalled = (packageName) => {
|
|
578
|
+
try {
|
|
579
|
+
const packageJsonPath = path.join(projectPath, "package.json");
|
|
580
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
581
|
+
const packageJson = JSON.parse(
|
|
582
|
+
fs.readFileSync(packageJsonPath, "utf8")
|
|
583
|
+
);
|
|
584
|
+
return !!(
|
|
585
|
+
(packageJson.dependencies &&
|
|
586
|
+
packageJson.dependencies[packageName]) ||
|
|
587
|
+
(packageJson.devDependencies &&
|
|
588
|
+
packageJson.devDependencies[packageName])
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
return false;
|
|
592
|
+
} catch {
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
};
|
|
269
596
|
if (updateAnswer.backendOnly) {
|
|
270
597
|
nonBackendFiles.forEach((file) => {
|
|
271
598
|
const filePath = path.join(projectPath, "src", "app", file);
|
|
272
599
|
if (fs.existsSync(filePath)) {
|
|
273
|
-
fs.unlinkSync(filePath);
|
|
600
|
+
fs.unlinkSync(filePath);
|
|
274
601
|
console.log(`${file} was deleted successfully.`);
|
|
275
|
-
} else {
|
|
276
|
-
console.log(`${file} does not exist.`);
|
|
277
602
|
}
|
|
278
603
|
});
|
|
279
604
|
const backendOnlyFolders = ["js", "css"];
|
|
280
605
|
backendOnlyFolders.forEach((folder) => {
|
|
281
606
|
const folderPath = path.join(projectPath, "src", "app", folder);
|
|
282
607
|
if (fs.existsSync(folderPath)) {
|
|
283
|
-
fs.rmSync(folderPath, { recursive: true, force: true });
|
|
608
|
+
fs.rmSync(folderPath, { recursive: true, force: true });
|
|
284
609
|
console.log(`${folder} was deleted successfully.`);
|
|
285
|
-
} else {
|
|
286
|
-
console.log(`${folder} does not exist.`);
|
|
287
610
|
}
|
|
288
611
|
});
|
|
289
612
|
}
|
|
@@ -295,81 +618,108 @@ async function main() {
|
|
|
295
618
|
"swagger-docs"
|
|
296
619
|
);
|
|
297
620
|
if (fs.existsSync(swaggerDocsFolder)) {
|
|
298
|
-
fs.rmSync(swaggerDocsFolder, { recursive: true, force: true });
|
|
621
|
+
fs.rmSync(swaggerDocsFolder, { recursive: true, force: true });
|
|
299
622
|
console.log(`swagger-docs was deleted successfully.`);
|
|
300
623
|
}
|
|
301
624
|
const swaggerFiles = ["swagger-config.ts"];
|
|
302
625
|
swaggerFiles.forEach((file) => {
|
|
303
626
|
const filePath = path.join(projectPath, "settings", file);
|
|
304
627
|
if (fs.existsSync(filePath)) {
|
|
305
|
-
fs.unlinkSync(filePath);
|
|
628
|
+
fs.unlinkSync(filePath);
|
|
306
629
|
console.log(`${file} was deleted successfully.`);
|
|
307
|
-
} else {
|
|
308
|
-
console.log(`${file} does not exist.`);
|
|
309
630
|
}
|
|
310
631
|
});
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
"
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
632
|
+
// Only add to uninstall list if packages are actually installed
|
|
633
|
+
if (isNpmPackageInstalled("swagger-jsdoc")) {
|
|
634
|
+
updateUninstallNpmDependencies.push("swagger-jsdoc");
|
|
635
|
+
}
|
|
636
|
+
if (isNpmPackageInstalled("@types/swagger-jsdoc")) {
|
|
637
|
+
updateUninstallNpmDependencies.push("@types/swagger-jsdoc");
|
|
638
|
+
}
|
|
639
|
+
if (isNpmPackageInstalled("prompts")) {
|
|
640
|
+
updateUninstallNpmDependencies.push("prompts");
|
|
641
|
+
}
|
|
642
|
+
if (isNpmPackageInstalled("@types/prompts")) {
|
|
643
|
+
updateUninstallNpmDependencies.push("@types/prompts");
|
|
644
|
+
}
|
|
317
645
|
}
|
|
318
646
|
if (!updateAnswer.tailwindcss) {
|
|
319
647
|
const tailwindFiles = ["postcss.config.js"];
|
|
320
648
|
tailwindFiles.forEach((file) => {
|
|
321
649
|
const filePath = path.join(projectPath, file);
|
|
322
650
|
if (fs.existsSync(filePath)) {
|
|
323
|
-
fs.unlinkSync(filePath);
|
|
651
|
+
fs.unlinkSync(filePath);
|
|
324
652
|
console.log(`${file} was deleted successfully.`);
|
|
325
|
-
} else {
|
|
326
|
-
console.log(`${file} does not exist.`);
|
|
327
653
|
}
|
|
328
654
|
});
|
|
329
|
-
|
|
655
|
+
// Only add to uninstall list if packages are actually installed
|
|
656
|
+
const tailwindPackages = [
|
|
330
657
|
"tailwindcss",
|
|
331
658
|
"postcss",
|
|
332
659
|
"postcss-cli",
|
|
333
660
|
"@tailwindcss/postcss",
|
|
334
|
-
"cssnano"
|
|
335
|
-
|
|
661
|
+
"cssnano",
|
|
662
|
+
];
|
|
663
|
+
tailwindPackages.forEach((pkg) => {
|
|
664
|
+
if (isNpmPackageInstalled(pkg)) {
|
|
665
|
+
updateUninstallNpmDependencies.push(pkg);
|
|
666
|
+
}
|
|
667
|
+
});
|
|
336
668
|
}
|
|
337
669
|
if (!updateAnswer.websocket) {
|
|
338
|
-
const websocketFiles = [
|
|
339
|
-
"restart-websocket.ts",
|
|
340
|
-
"restart-websocket.bat",
|
|
341
|
-
];
|
|
670
|
+
const websocketFiles = ["restart-websocket.ts"];
|
|
342
671
|
websocketFiles.forEach((file) => {
|
|
343
672
|
const filePath = path.join(projectPath, "settings", file);
|
|
344
673
|
if (fs.existsSync(filePath)) {
|
|
345
|
-
fs.unlinkSync(filePath);
|
|
674
|
+
fs.unlinkSync(filePath);
|
|
346
675
|
console.log(`${file} was deleted successfully.`);
|
|
347
|
-
} else {
|
|
348
|
-
console.log(`${file} does not exist.`);
|
|
349
676
|
}
|
|
350
677
|
});
|
|
351
|
-
const websocketFolder = path.join(
|
|
678
|
+
const websocketFolder = path.join(
|
|
679
|
+
projectPath,
|
|
680
|
+
"src",
|
|
681
|
+
"Lib",
|
|
682
|
+
"Websocket"
|
|
683
|
+
);
|
|
352
684
|
if (fs.existsSync(websocketFolder)) {
|
|
353
|
-
fs.rmSync(websocketFolder, { recursive: true, force: true });
|
|
685
|
+
fs.rmSync(websocketFolder, { recursive: true, force: true });
|
|
354
686
|
console.log(`Websocket folder was deleted successfully.`);
|
|
355
687
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
688
|
+
// composer package for websocket only
|
|
689
|
+
if (isComposerPackageInstalled("cboden/ratchet")) {
|
|
690
|
+
updateUninstallComposerDependencies.push("cboden/ratchet");
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
if (!updateAnswer.mcp) {
|
|
694
|
+
const mcpFiles = ["restart-mcp.ts"];
|
|
695
|
+
mcpFiles.forEach((file) => {
|
|
696
|
+
const filePath = path.join(projectPath, "settings", file);
|
|
697
|
+
if (fs.existsSync(filePath)) {
|
|
698
|
+
fs.unlinkSync(filePath);
|
|
699
|
+
console.log(`${file} was deleted successfully.`);
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
const mcpFolder = path.join(projectPath, "src", "Lib", "MCP");
|
|
703
|
+
if (fs.existsSync(mcpFolder)) {
|
|
704
|
+
fs.rmSync(mcpFolder, { recursive: true, force: true });
|
|
705
|
+
console.log(`MCP folder was deleted successfully.`);
|
|
706
|
+
}
|
|
707
|
+
// composer package for MCP only
|
|
708
|
+
if (isComposerPackageInstalled("php-mcp/server")) {
|
|
709
|
+
updateUninstallComposerDependencies.push("php-mcp/server");
|
|
363
710
|
}
|
|
364
|
-
updateUninstallNpmDependencies.push("chokidar-cli");
|
|
365
|
-
updateUninstallComposerDependencies.push("cboden/ratchet");
|
|
366
711
|
}
|
|
367
712
|
if (!updateAnswer.prisma) {
|
|
368
|
-
|
|
713
|
+
const prismaPackages = [
|
|
369
714
|
"prisma",
|
|
370
715
|
"@prisma/client",
|
|
371
|
-
"@prisma/internals"
|
|
372
|
-
|
|
716
|
+
"@prisma/internals",
|
|
717
|
+
];
|
|
718
|
+
prismaPackages.forEach((pkg) => {
|
|
719
|
+
if (isNpmPackageInstalled(pkg)) {
|
|
720
|
+
updateUninstallNpmDependencies.push(pkg);
|
|
721
|
+
}
|
|
722
|
+
});
|
|
373
723
|
}
|
|
374
724
|
if (!updateAnswer.docker) {
|
|
375
725
|
const dockerFiles = [
|
|
@@ -381,25 +731,24 @@ async function main() {
|
|
|
381
731
|
dockerFiles.forEach((file) => {
|
|
382
732
|
const filePath = path.join(projectPath, file);
|
|
383
733
|
if (fs.existsSync(filePath)) {
|
|
384
|
-
fs.unlinkSync(filePath);
|
|
734
|
+
fs.unlinkSync(filePath);
|
|
385
735
|
console.log(`${file} was deleted successfully.`);
|
|
386
|
-
} else {
|
|
387
|
-
console.log(`${file} does not exist.`);
|
|
388
736
|
}
|
|
389
737
|
});
|
|
390
738
|
}
|
|
391
|
-
if
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
);
|
|
739
|
+
// Only uninstall if there are packages to uninstall
|
|
740
|
+
const uniq = (arr) => Array.from(new Set(arr));
|
|
741
|
+
const npmToUninstall = uniq(updateUninstallNpmDependencies);
|
|
742
|
+
const composerToUninstall = uniq(updateUninstallComposerDependencies);
|
|
743
|
+
if (npmToUninstall.length > 0) {
|
|
744
|
+
console.log(`Uninstalling npm packages: ${npmToUninstall.join(", ")}`);
|
|
745
|
+
await uninstallNpmDependencies(projectPath, npmToUninstall, true);
|
|
397
746
|
}
|
|
398
|
-
if (
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
updateUninstallComposerDependencies
|
|
747
|
+
if (composerToUninstall.length > 0) {
|
|
748
|
+
console.log(
|
|
749
|
+
`Uninstalling composer packages: ${composerToUninstall.join(", ")}`
|
|
402
750
|
);
|
|
751
|
+
await uninstallComposerDependencies(projectPath, composerToUninstall);
|
|
403
752
|
}
|
|
404
753
|
}
|
|
405
754
|
const projectPathModified = projectPath.replace(/\\/g, "\\");
|
|
@@ -415,6 +764,7 @@ async function main() {
|
|
|
415
764
|
swaggerDocs: answer.swaggerDocs,
|
|
416
765
|
tailwindcss: answer.tailwindcss,
|
|
417
766
|
websocket: answer.websocket,
|
|
767
|
+
mcp: answer.mcp,
|
|
418
768
|
prisma: answer.prisma,
|
|
419
769
|
docker: answer.docker,
|
|
420
770
|
version: latestVersionOfCreatePrismaPhpApp,
|