create-prisma-php-app 5.0.0-alpha.24 β 5.0.0-alpha.26
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/.github/copilot-instructions.md +69 -0
- package/dist/AGENTS.md +15 -4
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Project Guidelines
|
|
2
|
+
|
|
3
|
+
## Source Of Truth
|
|
4
|
+
|
|
5
|
+
- For Prisma PHP applications, treat `node_modules/prisma-php/dist/docs/index.md` as the entry point for the installed framework version.
|
|
6
|
+
- Read the matching doc in `node_modules/prisma-php/dist/docs` before generating or editing framework-specific Prisma PHP code.
|
|
7
|
+
- Expect `AGENTS.md` in the project root and keep it aligned with the installed Prisma PHP docs contract.
|
|
8
|
+
- In the Prisma PHP package source repo, keep `AGENTS.md`, `.github/copilot-instructions.md`, and `dist/docs` aligned so the published docs remain correct after install.
|
|
9
|
+
- Do not assume installed consumer apps also ship a root `.github/copilot-instructions.md` unless the generator explicitly creates one.
|
|
10
|
+
- Keep every `dist/docs/*.md` page AI-discoverable on its own: the frontmatter description and opening section should clearly say when agents should read that file and which adjacent docs to consult next.
|
|
11
|
+
|
|
12
|
+
## Framework-Managed Package Scripts
|
|
13
|
+
|
|
14
|
+
- Prisma PHP can generate `package.json` scripts for BrowserSync, Tailwind, TypeScript, WebSocket, MCP, Swagger docs, and related helpers.
|
|
15
|
+
- Prefer `npm run dev` for ordinary local development and `npm run build` for ordinary production-style asset builds.
|
|
16
|
+
- Do not default to telling users to run `npm run tailwind`, `npm run tailwind:build`, `npm run ts:watch`, or `npm run ts:build` after routine file changes, because those are usually orchestrated through the generated top-level scripts.
|
|
17
|
+
- Use `npm run websocket` or `npm run mcp` only when isolating local runtime startup, debugging, or when the project's scripts show those services are not already covered by the normal development flow.
|
|
18
|
+
- Use `npm run create-swagger-docs` only when Swagger or OpenAPI output must be intentionally generated or refreshed.
|
|
19
|
+
- When package-script behavior matters, read `dist/docs/commands.md` first and inspect the actual `package.json` in the target project before assuming which scripts exist.
|
|
20
|
+
|
|
21
|
+
## PulsePoint-First Frontend Rules
|
|
22
|
+
|
|
23
|
+
- In full-stack Prisma PHP apps, treat PulsePoint as the primary JavaScript authoring model for frontend behavior.
|
|
24
|
+
- For page-local interactivity, prefer `index.php` or nested `layout.php` with a plain inline `<script>` that contains PulsePoint state and functions directly, and use `pp.fetchFunction(...)` for backend calls.
|
|
25
|
+
- Do not wrap inline PulsePoint code in `DOMContentLoaded`, IIFEs, manual `pp.mount()` calls, or custom scoping/bootstrap helpers. Prisma PHP scopes the component boundary and runs the script for you.
|
|
26
|
+
- Reserve plain browser JavaScript or TypeScript modules for reusable helpers in `ts/`, third-party libraries, low-level browser APIs, or behavior that does not belong inside a PulsePoint component boundary.
|
|
27
|
+
|
|
28
|
+
## Route File Conventions
|
|
29
|
+
|
|
30
|
+
- For PulsePoint-aware `index.php` and nested `layout.php`, keep file order as PHP first, then one parent HTML element; keep the PulsePoint `<script>` as the last child inside that same root element.
|
|
31
|
+
- `index.php` and nested `layout.php` must render a single parent HTML element. Treat that root like a React-style component boundary rather than loose sibling markup.
|
|
32
|
+
- For pages and nested layouts, author a plain single root element and let Prisma PHP inject the PulsePoint `pp-component` scope automatically.
|
|
33
|
+
- Author plain `<script>` tags inside that root when PulsePoint is needed. Put the PulsePoint code at the top level of that script. Do not manually add `type="text/pp"`, `DOMContentLoaded` wrappers, IIFEs, or manual bootstrap code; Prisma PHP normalizes the script contract for the runtime.
|
|
34
|
+
- Only the root `layout.php` should define `<html>`, `<head>`, and `<body>`. When PulsePoint is present, keep `MainLayout::$children;` and any `<script>` inside one clear wrapper.
|
|
35
|
+
|
|
36
|
+
## Component Boundary Rules
|
|
37
|
+
|
|
38
|
+
- Distinguish PHPX class components from `ImportComponent` partials.
|
|
39
|
+
- `ImportComponent` partials must output exactly one root element because Prisma PHP uses that root as the imported component boundary and serializes props there.
|
|
40
|
+
- Do not manually add `pp-component` inside `ImportComponent` partial source; Prisma PHP injects it there.
|
|
41
|
+
- When imported partials need PulsePoint logic, keep the `<script>` inside that same root element and author it as a plain `<script>` tag without `type="text/pp"`, DOM-ready wrappers, or manual bootstrap code.
|
|
42
|
+
|
|
43
|
+
## Relevant Docs
|
|
44
|
+
|
|
45
|
+
- Project structure and feature placement: `dist/docs/project-structure.md`
|
|
46
|
+
- CLI project creation and update commands: `dist/docs/commands.md`
|
|
47
|
+
- First-time project installation and local setup: `dist/docs/installation.md`
|
|
48
|
+
- Existing-project upgrades and feature refreshes: `dist/docs/upgrading.md`
|
|
49
|
+
- TypeScript frontend tooling, the `typescript` flag, and `ts/main.ts` registration: `dist/docs/typescript.md`
|
|
50
|
+
- Backend-only API usage and `backendOnly`: `dist/docs/backend-only.md`
|
|
51
|
+
- Route and layout structure: `dist/docs/layouts-and-pages.md`
|
|
52
|
+
- AI integration, provider-backed chat, streaming, and MCP boundary: `dist/docs/get-started-ia.md`
|
|
53
|
+
- Data loading, `#[Exposed]`, and SSE streaming: `dist/docs/fetching-data.md`
|
|
54
|
+
- Bootstrap flow, runtime init order, request initialization, and function-call protection: `dist/docs/bootstrap-runtime.md`
|
|
55
|
+
- PulsePoint runtime rules: `dist/docs/pulsepoint.md`
|
|
56
|
+
- Component and `ImportComponent` rules: `dist/docs/components.md`
|
|
57
|
+
- Cache behavior and `CacheHandler`: `dist/docs/caching.md`
|
|
58
|
+
- Validation rules: `dist/docs/validator.md`
|
|
59
|
+
- Prisma ORM schema, migrations, and generated PHP classes: `dist/docs/prisma-php-orm.md`
|
|
60
|
+
- Environment variables and `PP\Env` usage: `dist/docs/env.md`
|
|
61
|
+
- File uploads and file manager behavior: `dist/docs/file-manager.md`
|
|
62
|
+
- Email and SMTP workflows: `dist/docs/email.md`
|
|
63
|
+
- WebSocket and realtime behavior: `dist/docs/websocket.md`
|
|
64
|
+
- MCP server and tool rules: `dist/docs/mcp.md`
|
|
65
|
+
- Authentication: `dist/docs/authentication.md`
|
|
66
|
+
- Error handling, expected failures, and route error files: `dist/docs/error-handling.md`
|
|
67
|
+
- Metadata and icons: `dist/docs/metadata-and-og-images.md`
|
|
68
|
+
- API-style handlers and webhooks: `dist/docs/route-handlers.md`
|
|
69
|
+
- Swagger/OpenAPI generation and `swaggerDocs`: `dist/docs/swagger-docs.md`
|
package/dist/AGENTS.md
CHANGED
|
@@ -194,6 +194,8 @@ When a task involves package scripts, read `commands.md` first and inspect the c
|
|
|
194
194
|
|
|
195
195
|
For normal full-stack Prisma PHP work, assume the user wants the PulsePoint-first approach unless they explicitly ask otherwise.
|
|
196
196
|
|
|
197
|
+
PulsePoint is the primary JavaScript authoring model for frontend work in Prisma PHP. For normal page behavior, keep the client logic inside a plain inline `<script>` within the route or imported-partial root, let Prisma PHP scope and execute it, and prefer `pp.fetchFunction(...)` over ad hoc endpoints.
|
|
198
|
+
|
|
197
199
|
Default interaction stack:
|
|
198
200
|
|
|
199
201
|
1. render route UI with `index.php`
|
|
@@ -220,6 +222,7 @@ Treat this as the default for:
|
|
|
220
222
|
Do **not** default to:
|
|
221
223
|
|
|
222
224
|
- a PHP-only interaction style
|
|
225
|
+
- plain browser-DOM wiring when PulsePoint state, bindings, and native `on*` handlers already fit the task
|
|
223
226
|
- ad hoc `fetch('/api/...')` patterns
|
|
224
227
|
- extra `route.php` files for page-local interactions that already fit `pp.fetchFunction(...)`
|
|
225
228
|
- a separate Node realtime or tool server when the documented Prisma PHP runtime already fits the task
|
|
@@ -240,12 +243,15 @@ Use this pattern:
|
|
|
240
243
|
|
|
241
244
|
1. PHP first
|
|
242
245
|
2. one parent HTML element for the route content
|
|
243
|
-
3. when PulsePoint is present,
|
|
246
|
+
3. when PulsePoint is present, let Prisma PHP inject the route or layout `pp-component` scope on that root automatically
|
|
244
247
|
4. keep one `<script>` block as the last child inside that same root element
|
|
245
248
|
|
|
246
249
|
Also follow these route-file rules:
|
|
247
250
|
|
|
248
251
|
- `index.php` and nested `layout.php` must render a single parent HTML element
|
|
252
|
+
- for normal pages and nested layouts, do **not** manually author `pp-component` on that root; Prisma PHP adds it automatically
|
|
253
|
+
- author a plain `<script>` tag inside that root when PulsePoint logic is needed and do **not** add `type="text/pp"` manually
|
|
254
|
+
- write PulsePoint state, derived values, and functions directly at the top level of that script; do **not** wrap them in `DOMContentLoaded`, an IIFE, manual `pp.mount()` calls, or custom scoping helpers
|
|
249
255
|
- only the root `layout.php` should define `<html>`, `<head>`, and `<body>`
|
|
250
256
|
- when PulsePoint is present in a root `layout.php`, keep `MainLayout::$children` and any `<script>` inside one clear wrapper
|
|
251
257
|
|
|
@@ -260,7 +266,7 @@ MainLayout::$title = 'Todos';
|
|
|
260
266
|
MainLayout::$description = 'Track tasks and view the current item count.';
|
|
261
267
|
?>
|
|
262
268
|
|
|
263
|
-
<section
|
|
269
|
+
<section>
|
|
264
270
|
<h1>Todos</h1>
|
|
265
271
|
<p>Count: {count}</p>
|
|
266
272
|
<script>
|
|
@@ -286,7 +292,7 @@ Example:
|
|
|
286
292
|
|
|
287
293
|
?>
|
|
288
294
|
|
|
289
|
-
<div
|
|
295
|
+
<div>
|
|
290
296
|
<h2>Search</h2>
|
|
291
297
|
<input value="{query}" />
|
|
292
298
|
<script>
|
|
@@ -299,7 +305,8 @@ Do not:
|
|
|
299
305
|
|
|
300
306
|
- put a sibling `<script>` next to a route root or imported partial root
|
|
301
307
|
- manually add `pp-component` inside imported partial source
|
|
302
|
-
-
|
|
308
|
+
- manually add `type="text/pp"` to route or imported-partial scripts
|
|
309
|
+
- wrap imported-partial PulsePoint code in `DOMContentLoaded`, an IIFE, manual `pp.mount()` calls, or custom auto-execute helpers
|
|
303
310
|
|
|
304
311
|
## Metadata rules
|
|
305
312
|
|
|
@@ -565,6 +572,10 @@ When a task involves reactive frontend behavior, read `pulsepoint.md` first.
|
|
|
565
572
|
|
|
566
573
|
Also follow these rules:
|
|
567
574
|
|
|
575
|
+
- treat PulsePoint as the primary JavaScript authoring model for normal full-stack frontend work
|
|
576
|
+
- keep page and imported-partial client logic inside the boundary's plain `<script>` tag instead of building extra DOM-ready or self-executing wrappers
|
|
577
|
+
- prefer `pp.fetchFunction(...)` over ad hoc `fetch('/api/...')` calls for page-local PHP interactions
|
|
578
|
+
- reserve plain browser JavaScript outside PulsePoint for external libraries, low-level browser APIs, and reusable helpers in `ts/`
|
|
568
579
|
- do not invent undocumented PulsePoint helpers or directives
|
|
569
580
|
- do not write React, Vue, Alpine, or Livewire syntax and call it PulsePoint
|
|
570
581
|
- keep backend concerns separate from PulsePoint runtime concerns
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
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"],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,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,swaggerDocs:!0,mcp:!1},requiredFiles:["bootstrap.php",".htaccess","postcss.config.js","src/app/layout.php","src/app/index.php","public/js/main.js","src/app/globals.css"]},api:{id:"api",name:"REST API",description:"Backend API with database and documentation",features:{backendOnly:!0,tailwindcss:!1,websocket:!1,prisma:!0,swaggerDocs:!0,mcp:!1},requiredFiles:["bootstrap.php",".htaccess"]},realtime:{id:"realtime",name:"Real-time Application",description:"Application with WebSocket support and MCP",features:{backendOnly:!1,tailwindcss:!0,websocket:!0,prisma:!0,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,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,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 console.error("Invalid PROJECT_ROOT_PATH. The path does not contain \\htdocs\\"),{bsTarget:"",bsPathRewrite:{}};const t=e.substring(0,s+8).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,"/"),r=n.replace(/\/\/+/g,"/");return{bsTarget:`${o}/`,bsPathRewrite:{"^/":`/${r.startsWith("/")?r.substring(1):r}/`}}}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/globals.css -o public/css/styles.css --watch","tailwind:build":"postcss src/app/globals.css -o public/css/styles.css"},c.push("tailwind")),s.typescript&&!s.backendOnly&&(n.scripts={...n.scripts,"ts:watch":"vite build --watch","ts:build":"vite build"},c.push("ts:watch")),s.websocket&&(n.scripts={...n.scripts,websocket:"tsx settings/restart-websocket.ts"},c.push("websocket")),s.mcp&&(n.scripts={...n.scripts,mcp:"tsx settings/restart-mcp.ts"},c.push("mcp")),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(" ")}`;let r=["browserSync:build"];s.tailwindcss&&r.unshift("tailwind:build"),s.typescript&&!s.backendOnly&&r.unshift("ts:build"),o.build=`npm-run-all ${r.join(" ")}`,n.scripts=o,n.type="module",fs.writeFileSync(t,JSON.stringify(n,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,"public","js","main.js");if(checkExcludeFiles(t))return;let n=fs.readFileSync(t,"utf8");n+='\nwindow.ws = new WebSocket("ws://localhost:9001");\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\\lib\\websocket"))return;if(!t.mcp&&n.includes("src\\lib\\mcp"))return;if((!t.typescript||t.backendOnly)&&(n.endsWith("\\ts")||n.includes("\\ts\\")))return;if((!t.typescript||t.backendOnly)&&(n.endsWith("\\vite-plugins")||n.includes("\\vite-plugins\\")||n.includes("\\vite-plugins")))return;if(t.backendOnly&&n.includes("public\\js")||t.backendOnly&&n.includes("public\\css")||t.backendOnly&&n.includes("public\\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("globals.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.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="/css/index.css" rel="stylesheet" />'),n+='\n <script type="module" src="/js/main.js"><\/script>');let c="";s.backendOnly||(c=s.tailwindcss?` <link href="/css/styles.css" rel="stylesheet" /> ${n}`:n),e=e.replace("</head>",`${c}\n</head>`),fs.writeFileSync(t,e,{flag:"w"})}catch(e){console.error(chalk.red("Error modifying layout.php:"),e)}}async function createOrUpdateEnvFile(e,s){const t=path.join(e,".env");fs.existsSync(t)&&checkExcludeFiles(t)||fs.writeFileSync(t,s,{flag:"w"})}function checkExcludeFiles(e){if(!updateAnswer?.isUpdate)return!1;const s=e.replace(/\\/g,"/");return!!updateAnswer?.excludeFilePath?.includes(s)||!!updateAnswer?.excludeFiles&&updateAnswer.excludeFiles.some(e=>{const t=e.replace(/\\/g,"/");return s.endsWith("/"+t)||s===t})}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"},{src:"/CLAUDE.md",dest:"/CLAUDE.md"},{src:"/AGENTS.md",dest:"/AGENTS.md"}];s.tailwindcss&&t.push({src:"/postcss.config.js",dest:"/postcss.config.js"}),s.typescript&&!s.backendOnly&&t.push({src:"/vite.config.ts",dest:"/vite.config.ts"});const n=[{src:"/settings",dest:"/settings"},{src:"/src",dest:"/src"},{src:"/public",dest:"/public"}];s.typescript&&!s.backendOnly&&n.push({src:"/ts",dest:"/ts"}),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(),r=`# Authentication secret key for JWT or session encryption.\nAUTH_SECRET="${c}"\n# Name of the authentication cookie.\nAUTH_COOKIE_NAME="${generateHexEncodedKey(8)}"\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)}"\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"\n\n# Rate Limiting\nRATE_LIMIT_DEFAULT="200 per minute"\nRATE_LIMIT_RPC="60 per minute"\nRATE_LIMIT_AUTH="60 per minute"`;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${r}`;await createOrUpdateEnvFile(e,s)}else await createOrUpdateEnvFile(e,r)}async function getAnswer(e={},s=!1){if(s)return{projectName:e.projectName??"my-app",backendOnly:e.backendOnly??!1,swaggerDocs:e.swaggerDocs??!1,tailwindcss:e.tailwindcss??!1,typescript:e.typescript??!1,websocket:e.websocket??!1,mcp:e.mcp??!1,prisma:e.prisma??!1};if(e.starterKit){const s=e.starterKit;let t=null;if(STARTER_KITS[s]&&(t=STARTER_KITS[s]),t){const n={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,swaggerDocs:t.features.swaggerDocs??!1,mcp:t.features.mcp??!1,typescript:t.features.typescript??!1},c=process.argv.slice(2);return c.includes("--backend-only")&&(n.backendOnly=!0),c.includes("--swagger-docs")&&(n.swaggerDocs=!0),c.includes("--tailwindcss")&&(n.tailwindcss=!0),c.includes("--websocket")&&(n.websocket=!0),c.includes("--mcp")&&(n.mcp=!0),c.includes("--prisma")&&(n.prisma=!0),c.includes("--typescript")&&(n.typescript=!0),n}if(e.starterKitSource){const t={projectName:e.projectName??"my-app",starterKit:s,starterKitSource:e.starterKitSource,backendOnly:!1,tailwindcss:!0,websocket:!1,prisma:!0,swaggerDocs:!0,mcp:!1,typescript:!1},n=process.argv.slice(2);return n.includes("--backend-only")&&(t.backendOnly=!0),n.includes("--swagger-docs")&&(t.swaggerDocs=!0),n.includes("--tailwindcss")&&(t.tailwindcss=!0),n.includes("--websocket")&&(t.websocket=!0),n.includes("--mcp")&&(t.mcp=!0),n.includes("--prisma")&&(t.prisma=!0),n.includes("--typescript")&&(t.typescript=!0),t}}const t=[];e.projectName||t.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.backendOnly||updateAnswer?.isUpdate||t.push({type:"toggle",name:"backendOnly",message:`Would you like to create a ${chalk.blue("backend-only project")}?`,initial:!1,active:"Yes",inactive:"No"});const n=()=>{console.warn(chalk.red("Operation cancelled by the user.")),process.exit(0)},c=await prompts(t,{onCancel:n}),o=[];c.backendOnly??e.backendOnly??!1?(e.swaggerDocs||o.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||o.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!1,active:"Yes",inactive:"No"}),e.mcp||o.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||o.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma ORM")}?`,initial:!1,active:"Yes",inactive:"No"})):(e.swaggerDocs||o.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.tailwindcss||o.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!1,active:"Yes",inactive:"No"}),e.typescript||o.push({type:"toggle",name:"typescript",message:`Would you like to use ${chalk.blue("TypeScript")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||o.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!1,active:"Yes",inactive:"No"}),e.mcp||o.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||o.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma ORM")}?`,initial:!1,active:"Yes",inactive:"No"}));const r=await prompts(o,{onCancel:n});return{projectName:c.projectName?String(c.projectName).trim().replace(/ /g,"-"):e.projectName??"my-app",backendOnly:c.backendOnly??e.backendOnly??!1,swaggerDocs:r.swaggerDocs??e.swaggerDocs??!1,tailwindcss:r.tailwindcss??e.tailwindcss??!1,typescript:r.typescript??e.typescript??!1,websocket:r.websocket??e.websocket??!1,mcp:r.mcp??e.mcp??!1,prisma:r.prisma??e.prisma??!1}}async function uninstallNpmDependencies(e,s,t=!1){console.log("Uninstalling Node dependencies:"),s.forEach(e=>console.log(`- ${chalk.blue(e)}`));const n=`npm uninstall ${t?"--save-dev":"--save"} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}async function uninstallComposerDependencies(e,s){console.log("Uninstalling Composer dependencies:"),s.forEach(e=>console.log(`- ${chalk.blue(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]:(console.error(`Package ${e} is not installed`),null)}catch(e){return console.error(e instanceof Error?e.message:String(e)),null}}async function installNpmDependencies(e,s,t=!1){fs.existsSync(path.join(e,"package.json"))?console.log("Updating existing Node.js project..."):console.log("Initializing new Node.js project..."),fs.existsSync(path.join(e,"package.json"))||execSync("npm init -y",{stdio:"inherit",cwd:e}),console.log((t?"Installing development dependencies":"Installing dependencies")+":"),s.forEach(e=>console.log(`- ${chalk.blue(e)}`));const n=`npm install ${t?"--save-dev":""} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}function getComposerCmd(){try{return execSync("composer --version",{stdio:"ignore"}),console.log("β Using global composer command"),{cmd:"composer",baseArgs:[]}}catch{const e="C:\\xampp\\php\\php.exe",s="C:\\ProgramData\\ComposerSetup\\bin\\composer.phar";if(!fs.existsSync(e))throw console.error(`β PHP not found at ${e}`),new Error(`PHP executable not found at ${e}`);if(!fs.existsSync(s))throw console.error(`β Composer not found at ${s}`),new Error(`Composer phar not found at ${s}`);return console.log("β Using XAMPP PHP with Composer phar"),{cmd:e,baseArgs:[s]}}}export async function installComposerDependencies(e,s){const{cmd:t,baseArgs:n}=getComposerCmd(),c=path.join(e,"composer.json"),o=fs.existsSync(c);if(console.log(chalk.green("Composer project initialization: "+(o?"Updating existing projectβ¦":"Setting up new projectβ¦"))),fs.existsSync(e)||(console.log(`Creating base directory: ${e}`),fs.mkdirSync(e,{recursive:!0})),!o){const s=[...n,"init","--no-interaction","--name","tsnc/prisma-php-app","--require","php:^8.2","--type","project","--version","1.0.0"];console.log("Attempting composer init...");const o=spawnSync(t,s,{cwd:e,stdio:["ignore","pipe","pipe"],encoding:"utf8"}),r=fs.existsSync(c);if(0===o.status&&r)console.log("β Composer init successful and composer.json created");else{0!==o.status?(console.log(`Composer init failed with status ${o.status}`),o.stderr&&console.log(`Stderr: ${o.stderr}`)):console.log("Composer init reported success but didn't create composer.json"),console.log("Creating composer.json manually...");const s={name:"tsnc/prisma-php-app",type:"project",version:"1.0.0",require:{php:"^8.2"},autoload:{"psr-4":{"":"src/"}}};try{const t=path.resolve(e,"composer.json");if(console.log(`Writing composer.json to: ${t}`),fs.writeFileSync(t,JSON.stringify(s,null,2),{encoding:"utf8"}),!fs.existsSync(t))throw new Error("File creation appeared to succeed but file doesn't exist");console.log("β Successfully created composer.json")}catch(s){if(console.error("β Failed to create composer.json:",s),console.error(`Base directory: ${e}`),console.error(`Absolute base directory: ${path.resolve(e)}`),console.error(`Target file path: ${c}`),console.error(`Absolute target file path: ${path.resolve(c)}`),console.error(`Current working directory: ${process.cwd()}`),console.error(`Base directory exists: ${fs.existsSync(e)}`),fs.existsSync(e))try{const s=fs.statSync(e);console.error(`Base directory is writable: ${s.isDirectory()}`)}catch(e){console.error(`Cannot stat base directory: ${e}`)}throw new Error(`Cannot create composer.json: ${s}`)}}}const r=path.resolve(e,"composer.json");if(!fs.existsSync(r))throw console.error(`β composer.json still not found at ${r}`),console.error("Directory contents:",fs.readdirSync(e)),new Error("Failed to create composer.json - file does not exist after all attempts");let i;try{const e=fs.readFileSync(r,"utf8");console.log("β Successfully read composer.json"),i=JSON.parse(e)}catch(e){throw console.error("β Failed to read/parse composer.json:",e),new Error(`Cannot read composer.json: ${e}`)}i.autoload??={},i.autoload["psr-4"]??={},i.autoload["psr-4"][""]??="src/";try{fs.writeFileSync(r,JSON.stringify(i,null,2)),console.log("β Updated composer.json with PSR-4 autoload")}catch(e){throw console.error("β Failed to update composer.json:",e),e}if(s.length){console.log("Installing Composer dependencies:"),s.forEach(e=>console.log(`- ${chalk.blue(e)}`));try{const c=`${t} ${[...n,"require","--no-interaction","-W",...s].join(" ")}`;execSync(c,{stdio:"inherit",cwd:e,env:{...process.env}}),console.log("β Composer dependencies installed")}catch(e){throw console.error("β Failed to install composer dependencies:",e),e}}if(o)try{execSync(`${t} ${[...n,"update","--lock","--no-install","--no-interaction"].join(" ")}`,{stdio:"inherit",cwd:e}),console.log("β Composer lock updated")}catch(e){throw console.error("β Failed to update composer lock:",e),e}try{execSync(`${t} ${[...n,"dump-autoload","--quiet"].join(" ")}`,{stdio:"inherit",cwd:e}),console.log("β Composer autoloader regenerated")}catch(e){throw console.error("β Failed to regenerate autoloader:",e),e}}const npmPinnedVersions={"@tailwindcss/postcss":"4.2.2","@types/browser-sync":"2.29.1","@types/node":"25.6.0","@types/prompts":"2.4.9","browser-sync":"3.0.4",chalk:"5.6.2","chokidar-cli":"3.0.0",cssnano:"7.1.4","http-proxy-middleware":"3.0.5","npm-run-all":"4.1.5","php-parser":"3.5.1",postcss:"8.5.9","postcss-cli":"11.0.1",prompts:"2.4.2",tailwindcss:"4.2.2",tsx:"4.21.0",typescript:"6.0.2",vite:"7.3.0","fast-glob":"3.3.3","prisma-php":"0.0.x"};function npmPkg(e){return npmPinnedVersions[e]?`${e}@${npmPinnedVersions[e]}`:e}const composerPinnedVersions={"vlucas/phpdotenv":"5.6.3","firebase/php-jwt":"7.0.5","phpmailer/phpmailer":"7.0.1","guzzlehttp/guzzle":"7.10.0","symfony/uid":"7.4.8","brick/math":"0.17.0","cboden/ratchet":"0.4.4","tsnc/prisma-php":"2.0.0","php-mcp/server":"3.3.0","gehrisandro/tailwind-merge-php":"1.2.0"};function composerPkg(e){return composerPinnedVersions[e]?`${e}:${composerPinnedVersions[e]}`:e}function removeDirectorySafe(e){if(fs.existsSync(e))try{return void fs.rmSync(e,{recursive:!0,force:!0,maxRetries:5,retryDelay:250})}catch(s){const t=s;if("win32"===globalThis.process?.platform&&("EPERM"===t.code||"EACCES"===t.code)){try{spawnSync("cmd",["/c","attrib","-R","-H","-S","/S","/D",`${e}\\*`],{stdio:"ignore"})}catch{}return void spawnSync("cmd",["/c","rd","/s","/q",e],{stdio:"ignore"})}throw s}}async function setupStarterKit(e,s){if(!s.starterKit)return;let t=null;if(STARTER_KITS[s.starterKit]?t=STARTER_KITS[s.starterKit]:s.starterKitSource&&(t={id:s.starterKit,name:`Custom Starter Kit (${s.starterKit})`,description:"Custom starter kit from external source",features:{},requiredFiles:[],source:{type:"git",url:s.starterKitSource}}),t){if(console.log(chalk.green(`Setting up ${t.name}...`)),t.source)try{const n=t.source.branch?`git clone -b ${t.source.branch} --depth 1 ${t.source.url} "${e}"`:`git clone --depth 1 ${t.source.url} "${e}"`;execSync(n,{stdio:"inherit"});removeDirectorySafe(path.join(e,".git")),console.log(chalk.blue("Starter kit cloned successfully!"));const c=path.join(e,"prisma-php.json");if(fs.existsSync(c))try{const t=JSON.parse(fs.readFileSync(c,"utf8")),n=e,o=bsConfigUrls(n);t.projectName=s.projectName,t.projectRootPath=n,t.bsTarget=o.bsTarget,t.bsPathRewrite=o.bsPathRewrite;const r=await fetchPackageVersion("create-prisma-php-app");t.version=t.version||r,fs.writeFileSync(c,JSON.stringify(t,null,2)),console.log(chalk.green("Updated prisma-php.json with new project details"))}catch(e){console.warn(chalk.yellow("Failed to update prisma-php.json, will create new one"))}}catch(e){throw console.error(chalk.red(`Failed to setup starter kit: ${e}`)),e}t.customSetup&&await t.customSetup(e,s),console.log(chalk.green(`β ${t.name} setup complete!`))}else console.warn(chalk.yellow(`Starter kit '${s.starterKit}' not found. Skipping...`))}function showStarterKits(){console.log(chalk.blue("\nπ Available Starter Kits:\n")),Object.values(STARTER_KITS).forEach(e=>{const s=e.source?" (Custom)":" (Built-in)";console.log(chalk.green(` ${e.id}${chalk.gray(s)}`)),console.log(` ${e.name}`),console.log(chalk.gray(` ${e.description}`)),e.source&&console.log(chalk.cyan(` Source: ${e.source.url}`));const t=Object.entries(e.features).filter(([,e])=>!0===e).map(([e])=>e).join(", ");t&&console.log(chalk.magenta(` Features: ${t}`)),console.log()}),console.log(chalk.yellow("Usage:")),console.log(" npx create-prisma-php-app my-project --starter-kit=basic"),console.log(" npx create-prisma-php-app my-project --starter-kit=custom --starter-kit-source=https://github.com/user/repo"),console.log()}async function main(){try{const e=process.argv.slice(2),s=e.includes("-y");let t=e[0];const n=e.find(e=>e.startsWith("--starter-kit=")),c=n?.split("=")[1],o=e.find(e=>e.startsWith("--starter-kit-source=")),r=o?.split("=")[1];if(e.includes("--list-starter-kits"))return void showStarterKits();let i=null,a=!1;if(t){const n=process.cwd(),o=path.join(n,"prisma-php.json");if(c&&r){a=!0;const n={projectName:t,starterKit:c,starterKitSource:r,backendOnly:e.includes("--backend-only"),swaggerDocs:e.includes("--swagger-docs"),tailwindcss:e.includes("--tailwindcss"),typescript:e.includes("--typescript"),websocket:e.includes("--websocket"),mcp:e.includes("--mcp"),prisma:e.includes("--prisma")};i=await getAnswer(n,s)}else if(fs.existsSync(o)){const c=readJsonFile(o);let r=[];c.excludeFiles?.map(e=>{const s=path.join(n,e);fs.existsSync(s)&&r.push(s.replace(/\\/g,"/"))}),updateAnswer={projectName:t,backendOnly:c.backendOnly,swaggerDocs:c.swaggerDocs,tailwindcss:c.tailwindcss,websocket:c.websocket,mcp:c.mcp,prisma:c.prisma,typescript:c.typescript,isUpdate:!0,componentScanDirs:c.componentScanDirs??[],excludeFiles:c.excludeFiles??[],excludeFilePath:r??[],filePath:n};const a={projectName:t,backendOnly:e.includes("--backend-only")||c.backendOnly,swaggerDocs:e.includes("--swagger-docs")||c.swaggerDocs,tailwindcss:e.includes("--tailwindcss")||c.tailwindcss,typescript:e.includes("--typescript")||c.typescript,websocket:e.includes("--websocket")||c.websocket,prisma:e.includes("--prisma")||c.prisma,mcp:e.includes("--mcp")||c.mcp};i=await getAnswer(a,s),null!==i&&(updateAnswer={projectName:t,backendOnly:i.backendOnly,swaggerDocs:i.swaggerDocs,tailwindcss:i.tailwindcss,websocket:i.websocket,mcp:i.mcp,prisma:i.prisma,typescript:i.typescript,isUpdate:!0,componentScanDirs:c.componentScanDirs??[],excludeFiles:c.excludeFiles??[],excludeFilePath:r??[],filePath:n})}else{const n={projectName:t,starterKit:c,starterKitSource:r,backendOnly:e.includes("--backend-only"),swaggerDocs:e.includes("--swagger-docs"),tailwindcss:e.includes("--tailwindcss"),typescript:e.includes("--typescript"),websocket:e.includes("--websocket"),mcp:e.includes("--mcp"),prisma:e.includes("--prisma")};i=await getAnswer(n,s)}if(null===i)return void console.log(chalk.red("Installation cancelled."))}else i=await getAnswer({},s);if(null===i)return void console.warn(chalk.red("Installation cancelled."));const p=await fetchPackageVersion("create-prisma-php-app"),l=getInstalledPackageVersion("create-prisma-php-app");l?-1===compareVersions(l,p)&&(execSync("npm uninstall -g create-prisma-php-app",{stdio:"inherit"}),execSync("npm install -g create-prisma-php-app",{stdio:"inherit"})):execSync("npm install -g create-prisma-php-app",{stdio:"inherit"});const d=process.cwd();let u;if(t)if(a){const s=path.join(d,t);fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),u=s,await setupStarterKit(u,i),process.chdir(u);const n=path.join(u,"prisma-php.json");if(fs.existsSync(n)){const s=JSON.parse(fs.readFileSync(n,"utf8"));e.includes("--backend-only")&&(s.backendOnly=!0),e.includes("--swagger-docs")&&(s.swaggerDocs=!0),e.includes("--tailwindcss")&&(s.tailwindcss=!0),e.includes("--typescript")&&(s.typescript=!0),e.includes("--websocket")&&(s.websocket=!0),e.includes("--mcp")&&(s.mcp=!0),e.includes("--prisma")&&(s.prisma=!0),i={...i,backendOnly:s.backendOnly,swaggerDocs:s.swaggerDocs,tailwindcss:s.tailwindcss,typescript:s.typescript,websocket:s.websocket,mcp:s.mcp,prisma:s.prisma};let t=[];s.excludeFiles?.map(e=>{const s=path.join(u,e);fs.existsSync(s)&&t.push(s.replace(/\\/g,"/"))}),updateAnswer={...i,isUpdate:!0,componentScanDirs:s.componentScanDirs??[],excludeFiles:s.excludeFiles??[],excludeFilePath:t??[],filePath:u}}}else{const e=path.join(d,"prisma-php.json"),s=path.join(d,t),n=path.join(s,"prisma-php.json");fs.existsSync(e)?u=d:fs.existsSync(s)&&fs.existsSync(n)?(u=s,process.chdir(s)):(fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),u=s,process.chdir(s))}else fs.mkdirSync(i.projectName,{recursive:!0}),u=path.join(d,i.projectName),process.chdir(i.projectName);let m=[npmPkg("typescript"),npmPkg("@types/node"),npmPkg("tsx"),npmPkg("http-proxy-middleware"),npmPkg("chalk"),npmPkg("npm-run-all"),npmPkg("browser-sync"),npmPkg("@types/browser-sync"),npmPkg("php-parser"),npmPkg("prisma-php")],g=[composerPkg("vlucas/phpdotenv"),composerPkg("firebase/php-jwt"),composerPkg("phpmailer/phpmailer"),composerPkg("guzzlehttp/guzzle"),composerPkg("symfony/uid"),composerPkg("brick/math"),composerPkg("tsnc/prisma-php")];if(i.swaggerDocs&&m.push(npmPkg("swagger-jsdoc"),npmPkg("@types/swagger-jsdoc")),i.swaggerDocs&&i.prisma&&m.push(npmPkg("prompts"),npmPkg("@types/prompts")),i.tailwindcss&&(m.push(npmPkg("tailwindcss"),npmPkg("postcss"),npmPkg("postcss-cli"),npmPkg("@tailwindcss/postcss"),npmPkg("cssnano")),g.push("gehrisandro/tailwind-merge-php")),i.websocket&&g.push("cboden/ratchet"),i.mcp&&g.push("php-mcp/server"),i.prisma&&execSync("npm install -g prisma-client-php@latest",{stdio:"inherit"}),i.typescript&&!i.backendOnly&&m.push(npmPkg("vite"),npmPkg("fast-glob")),i.starterKit&&!a&&await setupStarterKit(u,i),await installNpmDependencies(u,m,!0),await installComposerDependencies(u,g),t||execSync("npx tsc --init",{stdio:"inherit"}),await createDirectoryStructure(u,i),i.prisma&&execSync("npx ppo init --prisma-php",{stdio:"inherit"}),i.swaggerDocs){const e=path.join(u,"src","app","swagger-docs"),s=path.join(e,"apis"),t=path.join(u,"public","assets"),n=path.join(t,"dist");fs.existsSync(e)&&fs.readdirSync(e).length>0&&(console.log("Removing existing swagger-docs directory..."),fs.rmSync(e,{recursive:!0,force:!0})),console.log(chalk.blue("Cloning swagger-docs repository...")),execSync(`git clone https://github.com/TheSteelNinjaCode/prisma-php-swagger-docs.git ${e}`,{stdio:"inherit"});const c=path.join(e,".git");fs.existsSync(c)&&fs.rmSync(c,{recursive:!0,force:!0}),fs.existsSync(t)||(console.log(chalk.blue("Creating public/assets directory...")),fs.mkdirSync(t,{recursive:!0}));const o=path.join(e,"dist");fs.existsSync(o)?(console.log(chalk.blue("Moving dist folder to public/assets/dist...")),fs.existsSync(n)&&fs.rmSync(n,{recursive:!0,force:!0}),fs.renameSync(o,n),console.log(chalk.green("β Moved dist to public/assets/dist"))):console.warn(chalk.yellow("Warning: dist folder not found in cloned repository")),fs.existsSync(s)?console.log(chalk.green("β APIs folder preserved in src/app/swagger-docs/apis")):console.warn(chalk.yellow("Warning: apis folder not found in cloned repository")),console.log(chalk.green("β Swagger docs setup complete"))}if(updateAnswer?.isUpdate){const e=[],s=[],t=e=>{try{const s=path.join(u,"composer.json");if(fs.existsSync(s)){const t=JSON.parse(fs.readFileSync(s,"utf8"));return!(!t.require||!t.require[e])}return!1}catch{return!1}},n=e=>{try{const s=path.join(u,"package.json");if(fs.existsSync(s)){const t=JSON.parse(fs.readFileSync(s,"utf8"));return!!(t.dependencies&&t.dependencies[e]||t.devDependencies&&t.devDependencies[e])}return!1}catch{return!1}};if(updateAnswer.backendOnly){nonBackendFiles.forEach(e=>{const s=path.join(u,"src","app",e);fs.existsSync(s)&&(fs.unlinkSync(s),console.log(`${e} was deleted successfully.`))});["js","css"].forEach(e=>{const s=path.join(u,"src","app",e);fs.existsSync(s)&&(fs.rmSync(s,{recursive:!0,force:!0}),console.log(`${e} was deleted successfully.`))})}if(!updateAnswer.swaggerDocs){const s=path.join(u,"src","app","swagger-docs");fs.existsSync(s)&&(fs.rmSync(s,{recursive:!0,force:!0}),console.log("swagger-docs was deleted successfully."));["swagger-config.ts"].forEach(e=>{const s=path.join(u,"settings",e);fs.existsSync(s)&&(fs.unlinkSync(s),console.log(`${e} was deleted successfully.`))}),n("swagger-jsdoc")&&e.push("swagger-jsdoc"),n("@types/swagger-jsdoc")&&e.push("@types/swagger-jsdoc"),n("prompts")&&e.push("prompts"),n("@types/prompts")&&e.push("@types/prompts")}if(!updateAnswer.tailwindcss){["postcss.config.js"].forEach(e=>{const s=path.join(u,e);fs.existsSync(s)&&(fs.unlinkSync(s),console.log(`${e} was deleted successfully.`))});["tailwindcss","postcss","postcss-cli","@tailwindcss/postcss","cssnano"].forEach(s=>{n(s)&&e.push(s)});const c="gehrisandro/tailwind-merge-php";t(c)&&s.push(c)}if(!updateAnswer.websocket){["restart-websocket.ts"].forEach(e=>{const s=path.join(u,"settings",e);fs.existsSync(s)&&(fs.unlinkSync(s),console.log(`${e} was deleted successfully.`))});const e=path.join(u,"src","Lib","Websocket");fs.existsSync(e)&&(fs.rmSync(e,{recursive:!0,force:!0}),console.log("Websocket folder was deleted successfully.")),t("cboden/ratchet")&&s.push("cboden/ratchet")}if(!updateAnswer.mcp){["restart-mcp.ts"].forEach(e=>{const s=path.join(u,"settings",e);fs.existsSync(s)&&(fs.unlinkSync(s),console.log(`${e} was deleted successfully.`))});const e=path.join(u,"src","Lib","MCP");fs.existsSync(e)&&(fs.rmSync(e,{recursive:!0,force:!0}),console.log("MCP folder was deleted successfully.")),t("php-mcp/server")&&s.push("php-mcp/server")}if(!updateAnswer.prisma){["prisma","@prisma/client","@prisma/internals","better-sqlite3","@prisma/adapter-better-sqlite3","mariadb","@prisma/adapter-mariadb","pg","@prisma/adapter-pg","@types/pg"].forEach(s=>{n(s)&&e.push(s)})}if(!updateAnswer.typescript||updateAnswer.backendOnly){["vite.config.ts"].forEach(e=>{const s=path.join(u,e);fs.existsSync(s)&&(fs.unlinkSync(s),console.log(`${e} was deleted successfully.`))});const s=path.join(u,"ts");fs.existsSync(s)&&(fs.rmSync(s,{recursive:!0,force:!0}),console.log("ts folder was deleted successfully."));const t=path.join(u,"settings","vite-plugins");fs.existsSync(t)&&(fs.rmSync(t,{recursive:!0,force:!0}),console.log("settings/vite-plugins folder was deleted successfully."));["vite","fast-glob"].forEach(s=>{n(s)&&e.push(s)})}const c=e=>Array.from(new Set(e)),o=c(e),r=c(s);o.length>0&&(console.log(`Uninstalling npm packages: ${o.join(", ")}`),await uninstallNpmDependencies(u,o,!0)),r.length>0&&(console.log(`Uninstalling composer packages: ${r.join(", ")}`),await uninstallComposerDependencies(u,r))}if(!a||!fs.existsSync(path.join(u,"prisma-php.json"))){const e=u.replace(/\\/g,"\\"),s=bsConfigUrls(e),t={projectName:i.projectName,projectRootPath:e,phpEnvironment:"XAMPP",phpRootPathExe:"C:\\xampp\\php\\php.exe",bsTarget:s.bsTarget,bsPathRewrite:s.bsPathRewrite,backendOnly:i.backendOnly,swaggerDocs:i.swaggerDocs,tailwindcss:i.tailwindcss,websocket:i.websocket,mcp:i.mcp,prisma:i.prisma,typescript:i.typescript,version:p,componentScanDirs:updateAnswer?.componentScanDirs??["src","vendor/tsnc/prisma-php/src"],excludeFiles:updateAnswer?.excludeFiles??[]};fs.writeFileSync(path.join(u,"prisma-php.json"),JSON.stringify(t,null,2),{flag:"w"})}execSync(updateAnswer?.isUpdate?"C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar update":"C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar install",{stdio:"inherit"}),console.log("\n=========================\n"),console.log(`${chalk.green("Success!")} Prisma PHP project successfully created in ${chalk.green(u.replace(/\\/g,"/"))}!`),console.log("\n=========================")}catch(e){console.error("Error while creating the project:",e),process.exit(1)}}main();
|
|
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"],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,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,swaggerDocs:!0,mcp:!1},requiredFiles:["bootstrap.php",".htaccess","postcss.config.js","src/app/layout.php","src/app/index.php","public/js/main.js","src/app/globals.css"]},api:{id:"api",name:"REST API",description:"Backend API with database and documentation",features:{backendOnly:!0,tailwindcss:!1,websocket:!1,prisma:!0,swaggerDocs:!0,mcp:!1},requiredFiles:["bootstrap.php",".htaccess"]},realtime:{id:"realtime",name:"Real-time Application",description:"Application with WebSocket support and MCP",features:{backendOnly:!1,tailwindcss:!0,websocket:!0,prisma:!0,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,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,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 console.error("Invalid PROJECT_ROOT_PATH. The path does not contain \\htdocs\\"),{bsTarget:"",bsPathRewrite:{}};const t=e.substring(0,s+8).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,"/"),r=n.replace(/\/\/+/g,"/");return{bsTarget:`${o}/`,bsPathRewrite:{"^/":`/${r.startsWith("/")?r.substring(1):r}/`}}}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/globals.css -o public/css/styles.css --watch","tailwind:build":"postcss src/app/globals.css -o public/css/styles.css"},c.push("tailwind")),s.typescript&&!s.backendOnly&&(n.scripts={...n.scripts,"ts:watch":"vite build --watch","ts:build":"vite build"},c.push("ts:watch")),s.websocket&&(n.scripts={...n.scripts,websocket:"tsx settings/restart-websocket.ts"},c.push("websocket")),s.mcp&&(n.scripts={...n.scripts,mcp:"tsx settings/restart-mcp.ts"},c.push("mcp")),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(" ")}`;let r=["browserSync:build"];s.tailwindcss&&r.unshift("tailwind:build"),s.typescript&&!s.backendOnly&&r.unshift("ts:build"),o.build=`npm-run-all ${r.join(" ")}`,n.scripts=o,n.type="module",fs.writeFileSync(t,JSON.stringify(n,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,"public","js","main.js");if(checkExcludeFiles(t))return;let n=fs.readFileSync(t,"utf8");n+='\nwindow.ws = new WebSocket("ws://localhost:9001");\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\\lib\\websocket"))return;if(!t.mcp&&n.includes("src\\lib\\mcp"))return;if((!t.typescript||t.backendOnly)&&(n.endsWith("\\ts")||n.includes("\\ts\\")))return;if((!t.typescript||t.backendOnly)&&(n.endsWith("\\vite-plugins")||n.includes("\\vite-plugins\\")||n.includes("\\vite-plugins")))return;if(t.backendOnly&&n.includes("public\\js")||t.backendOnly&&n.includes("public\\css")||t.backendOnly&&n.includes("public\\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("globals.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.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="/css/index.css" rel="stylesheet" />'),n+='\n <script type="module" src="/js/main.js"><\/script>');let c="";s.backendOnly||(c=s.tailwindcss?` <link href="/css/styles.css" rel="stylesheet" /> ${n}`:n),e=e.replace("</head>",`${c}\n</head>`),fs.writeFileSync(t,e,{flag:"w"})}catch(e){console.error(chalk.red("Error modifying layout.php:"),e)}}async function createOrUpdateEnvFile(e,s){const t=path.join(e,".env");fs.existsSync(t)&&checkExcludeFiles(t)||fs.writeFileSync(t,s,{flag:"w"})}function checkExcludeFiles(e){if(!updateAnswer?.isUpdate)return!1;const s=e.replace(/\\/g,"/");return!!updateAnswer?.excludeFilePath?.includes(s)||!!updateAnswer?.excludeFiles&&updateAnswer.excludeFiles.some(e=>{const t=e.replace(/\\/g,"/");return s.endsWith("/"+t)||s===t})}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"},{src:"/CLAUDE.md",dest:"/CLAUDE.md"},{src:"/AGENTS.md",dest:"/AGENTS.md"}];s.tailwindcss&&t.push({src:"/postcss.config.js",dest:"/postcss.config.js"}),s.typescript&&!s.backendOnly&&t.push({src:"/vite.config.ts",dest:"/vite.config.ts"});const n=[{src:"/settings",dest:"/settings"},{src:"/src",dest:"/src"},{src:"/public",dest:"/public"},{src:"/.github",dest:"/.github"}];s.typescript&&!s.backendOnly&&n.push({src:"/ts",dest:"/ts"}),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(),r=`# Authentication secret key for JWT or session encryption.\nAUTH_SECRET="${c}"\n# Name of the authentication cookie.\nAUTH_COOKIE_NAME="${generateHexEncodedKey(8)}"\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)}"\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"\n\n# Rate Limiting\nRATE_LIMIT_DEFAULT="200 per minute"\nRATE_LIMIT_RPC="60 per minute"\nRATE_LIMIT_AUTH="60 per minute"`;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${r}`;await createOrUpdateEnvFile(e,s)}else await createOrUpdateEnvFile(e,r)}async function getAnswer(e={},s=!1){if(s)return{projectName:e.projectName??"my-app",backendOnly:e.backendOnly??!1,swaggerDocs:e.swaggerDocs??!1,tailwindcss:e.tailwindcss??!1,typescript:e.typescript??!1,websocket:e.websocket??!1,mcp:e.mcp??!1,prisma:e.prisma??!1};if(e.starterKit){const s=e.starterKit;let t=null;if(STARTER_KITS[s]&&(t=STARTER_KITS[s]),t){const n={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,swaggerDocs:t.features.swaggerDocs??!1,mcp:t.features.mcp??!1,typescript:t.features.typescript??!1},c=process.argv.slice(2);return c.includes("--backend-only")&&(n.backendOnly=!0),c.includes("--swagger-docs")&&(n.swaggerDocs=!0),c.includes("--tailwindcss")&&(n.tailwindcss=!0),c.includes("--websocket")&&(n.websocket=!0),c.includes("--mcp")&&(n.mcp=!0),c.includes("--prisma")&&(n.prisma=!0),c.includes("--typescript")&&(n.typescript=!0),n}if(e.starterKitSource){const t={projectName:e.projectName??"my-app",starterKit:s,starterKitSource:e.starterKitSource,backendOnly:!1,tailwindcss:!0,websocket:!1,prisma:!0,swaggerDocs:!0,mcp:!1,typescript:!1},n=process.argv.slice(2);return n.includes("--backend-only")&&(t.backendOnly=!0),n.includes("--swagger-docs")&&(t.swaggerDocs=!0),n.includes("--tailwindcss")&&(t.tailwindcss=!0),n.includes("--websocket")&&(t.websocket=!0),n.includes("--mcp")&&(t.mcp=!0),n.includes("--prisma")&&(t.prisma=!0),n.includes("--typescript")&&(t.typescript=!0),t}}const t=[];e.projectName||t.push({type:"text",name:"projectName",message:"What is your project named?",initial:"my-app"}),e.backendOnly||updateAnswer?.isUpdate||t.push({type:"toggle",name:"backendOnly",message:`Would you like to create a ${chalk.blue("backend-only project")}?`,initial:!1,active:"Yes",inactive:"No"});const n=()=>{console.warn(chalk.red("Operation cancelled by the user.")),process.exit(0)},c=await prompts(t,{onCancel:n}),o=[];c.backendOnly??e.backendOnly??!1?(e.swaggerDocs||o.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||o.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!1,active:"Yes",inactive:"No"}),e.mcp||o.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||o.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma ORM")}?`,initial:!1,active:"Yes",inactive:"No"})):(e.swaggerDocs||o.push({type:"toggle",name:"swaggerDocs",message:`Would you like to use ${chalk.blue("Swagger Docs")}?`,initial:!1,active:"Yes",inactive:"No"}),e.tailwindcss||o.push({type:"toggle",name:"tailwindcss",message:`Would you like to use ${chalk.blue("Tailwind CSS")}?`,initial:!1,active:"Yes",inactive:"No"}),e.typescript||o.push({type:"toggle",name:"typescript",message:`Would you like to use ${chalk.blue("TypeScript")}?`,initial:!1,active:"Yes",inactive:"No"}),e.websocket||o.push({type:"toggle",name:"websocket",message:`Would you like to use ${chalk.blue("Websocket")}?`,initial:!1,active:"Yes",inactive:"No"}),e.mcp||o.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||o.push({type:"toggle",name:"prisma",message:`Would you like to use ${chalk.blue("Prisma ORM")}?`,initial:!1,active:"Yes",inactive:"No"}));const r=await prompts(o,{onCancel:n});return{projectName:c.projectName?String(c.projectName).trim().replace(/ /g,"-"):e.projectName??"my-app",backendOnly:c.backendOnly??e.backendOnly??!1,swaggerDocs:r.swaggerDocs??e.swaggerDocs??!1,tailwindcss:r.tailwindcss??e.tailwindcss??!1,typescript:r.typescript??e.typescript??!1,websocket:r.websocket??e.websocket??!1,mcp:r.mcp??e.mcp??!1,prisma:r.prisma??e.prisma??!1}}async function uninstallNpmDependencies(e,s,t=!1){console.log("Uninstalling Node dependencies:"),s.forEach(e=>console.log(`- ${chalk.blue(e)}`));const n=`npm uninstall ${t?"--save-dev":"--save"} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}async function uninstallComposerDependencies(e,s){console.log("Uninstalling Composer dependencies:"),s.forEach(e=>console.log(`- ${chalk.blue(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]:(console.error(`Package ${e} is not installed`),null)}catch(e){return console.error(e instanceof Error?e.message:String(e)),null}}async function installNpmDependencies(e,s,t=!1){fs.existsSync(path.join(e,"package.json"))?console.log("Updating existing Node.js project..."):console.log("Initializing new Node.js project..."),fs.existsSync(path.join(e,"package.json"))||execSync("npm init -y",{stdio:"inherit",cwd:e}),console.log((t?"Installing development dependencies":"Installing dependencies")+":"),s.forEach(e=>console.log(`- ${chalk.blue(e)}`));const n=`npm install ${t?"--save-dev":""} ${s.join(" ")}`;execSync(n,{stdio:"inherit",cwd:e})}function getComposerCmd(){try{return execSync("composer --version",{stdio:"ignore"}),console.log("β Using global composer command"),{cmd:"composer",baseArgs:[]}}catch{const e="C:\\xampp\\php\\php.exe",s="C:\\ProgramData\\ComposerSetup\\bin\\composer.phar";if(!fs.existsSync(e))throw console.error(`β PHP not found at ${e}`),new Error(`PHP executable not found at ${e}`);if(!fs.existsSync(s))throw console.error(`β Composer not found at ${s}`),new Error(`Composer phar not found at ${s}`);return console.log("β Using XAMPP PHP with Composer phar"),{cmd:e,baseArgs:[s]}}}export async function installComposerDependencies(e,s){const{cmd:t,baseArgs:n}=getComposerCmd(),c=path.join(e,"composer.json"),o=fs.existsSync(c);if(console.log(chalk.green("Composer project initialization: "+(o?"Updating existing projectβ¦":"Setting up new projectβ¦"))),fs.existsSync(e)||(console.log(`Creating base directory: ${e}`),fs.mkdirSync(e,{recursive:!0})),!o){const s=[...n,"init","--no-interaction","--name","tsnc/prisma-php-app","--require","php:^8.2","--type","project","--version","1.0.0"];console.log("Attempting composer init...");const o=spawnSync(t,s,{cwd:e,stdio:["ignore","pipe","pipe"],encoding:"utf8"}),r=fs.existsSync(c);if(0===o.status&&r)console.log("β Composer init successful and composer.json created");else{0!==o.status?(console.log(`Composer init failed with status ${o.status}`),o.stderr&&console.log(`Stderr: ${o.stderr}`)):console.log("Composer init reported success but didn't create composer.json"),console.log("Creating composer.json manually...");const s={name:"tsnc/prisma-php-app",type:"project",version:"1.0.0",require:{php:"^8.2"},autoload:{"psr-4":{"":"src/"}}};try{const t=path.resolve(e,"composer.json");if(console.log(`Writing composer.json to: ${t}`),fs.writeFileSync(t,JSON.stringify(s,null,2),{encoding:"utf8"}),!fs.existsSync(t))throw new Error("File creation appeared to succeed but file doesn't exist");console.log("β Successfully created composer.json")}catch(s){if(console.error("β Failed to create composer.json:",s),console.error(`Base directory: ${e}`),console.error(`Absolute base directory: ${path.resolve(e)}`),console.error(`Target file path: ${c}`),console.error(`Absolute target file path: ${path.resolve(c)}`),console.error(`Current working directory: ${process.cwd()}`),console.error(`Base directory exists: ${fs.existsSync(e)}`),fs.existsSync(e))try{const s=fs.statSync(e);console.error(`Base directory is writable: ${s.isDirectory()}`)}catch(e){console.error(`Cannot stat base directory: ${e}`)}throw new Error(`Cannot create composer.json: ${s}`)}}}const r=path.resolve(e,"composer.json");if(!fs.existsSync(r))throw console.error(`β composer.json still not found at ${r}`),console.error("Directory contents:",fs.readdirSync(e)),new Error("Failed to create composer.json - file does not exist after all attempts");let i;try{const e=fs.readFileSync(r,"utf8");console.log("β Successfully read composer.json"),i=JSON.parse(e)}catch(e){throw console.error("β Failed to read/parse composer.json:",e),new Error(`Cannot read composer.json: ${e}`)}i.autoload??={},i.autoload["psr-4"]??={},i.autoload["psr-4"][""]??="src/";try{fs.writeFileSync(r,JSON.stringify(i,null,2)),console.log("β Updated composer.json with PSR-4 autoload")}catch(e){throw console.error("β Failed to update composer.json:",e),e}if(s.length){console.log("Installing Composer dependencies:"),s.forEach(e=>console.log(`- ${chalk.blue(e)}`));try{const c=`${t} ${[...n,"require","--no-interaction","-W",...s].join(" ")}`;execSync(c,{stdio:"inherit",cwd:e,env:{...process.env}}),console.log("β Composer dependencies installed")}catch(e){throw console.error("β Failed to install composer dependencies:",e),e}}if(o)try{execSync(`${t} ${[...n,"update","--lock","--no-install","--no-interaction"].join(" ")}`,{stdio:"inherit",cwd:e}),console.log("β Composer lock updated")}catch(e){throw console.error("β Failed to update composer lock:",e),e}try{execSync(`${t} ${[...n,"dump-autoload","--quiet"].join(" ")}`,{stdio:"inherit",cwd:e}),console.log("β Composer autoloader regenerated")}catch(e){throw console.error("β Failed to regenerate autoloader:",e),e}}const npmPinnedVersions={"@tailwindcss/postcss":"4.2.2","@types/browser-sync":"2.29.1","@types/node":"25.6.0","@types/prompts":"2.4.9","browser-sync":"3.0.4",chalk:"5.6.2","chokidar-cli":"3.0.0",cssnano:"7.1.4","http-proxy-middleware":"3.0.5","npm-run-all":"4.1.5","php-parser":"3.5.1",postcss:"8.5.9","postcss-cli":"11.0.1",prompts:"2.4.2",tailwindcss:"4.2.2",tsx:"4.21.0",typescript:"6.0.2",vite:"7.3.0","fast-glob":"3.3.3","prisma-php":"0.0.x"};function npmPkg(e){return npmPinnedVersions[e]?`${e}@${npmPinnedVersions[e]}`:e}const composerPinnedVersions={"vlucas/phpdotenv":"5.6.3","firebase/php-jwt":"7.0.5","phpmailer/phpmailer":"7.0.1","guzzlehttp/guzzle":"7.10.0","symfony/uid":"7.4.8","brick/math":"0.17.0","cboden/ratchet":"0.4.4","tsnc/prisma-php":"2.0.0","php-mcp/server":"3.3.0","gehrisandro/tailwind-merge-php":"1.2.0"};function composerPkg(e){return composerPinnedVersions[e]?`${e}:${composerPinnedVersions[e]}`:e}function removeDirectorySafe(e){if(fs.existsSync(e))try{return void fs.rmSync(e,{recursive:!0,force:!0,maxRetries:5,retryDelay:250})}catch(s){const t=s;if("win32"===globalThis.process?.platform&&("EPERM"===t.code||"EACCES"===t.code)){try{spawnSync("cmd",["/c","attrib","-R","-H","-S","/S","/D",`${e}\\*`],{stdio:"ignore"})}catch{}return void spawnSync("cmd",["/c","rd","/s","/q",e],{stdio:"ignore"})}throw s}}async function setupStarterKit(e,s){if(!s.starterKit)return;let t=null;if(STARTER_KITS[s.starterKit]?t=STARTER_KITS[s.starterKit]:s.starterKitSource&&(t={id:s.starterKit,name:`Custom Starter Kit (${s.starterKit})`,description:"Custom starter kit from external source",features:{},requiredFiles:[],source:{type:"git",url:s.starterKitSource}}),t){if(console.log(chalk.green(`Setting up ${t.name}...`)),t.source)try{const n=t.source.branch?`git clone -b ${t.source.branch} --depth 1 ${t.source.url} "${e}"`:`git clone --depth 1 ${t.source.url} "${e}"`;execSync(n,{stdio:"inherit"});removeDirectorySafe(path.join(e,".git")),console.log(chalk.blue("Starter kit cloned successfully!"));const c=path.join(e,"prisma-php.json");if(fs.existsSync(c))try{const t=JSON.parse(fs.readFileSync(c,"utf8")),n=e,o=bsConfigUrls(n);t.projectName=s.projectName,t.projectRootPath=n,t.bsTarget=o.bsTarget,t.bsPathRewrite=o.bsPathRewrite;const r=await fetchPackageVersion("create-prisma-php-app");t.version=t.version||r,fs.writeFileSync(c,JSON.stringify(t,null,2)),console.log(chalk.green("Updated prisma-php.json with new project details"))}catch(e){console.warn(chalk.yellow("Failed to update prisma-php.json, will create new one"))}}catch(e){throw console.error(chalk.red(`Failed to setup starter kit: ${e}`)),e}t.customSetup&&await t.customSetup(e,s),console.log(chalk.green(`β ${t.name} setup complete!`))}else console.warn(chalk.yellow(`Starter kit '${s.starterKit}' not found. Skipping...`))}function showStarterKits(){console.log(chalk.blue("\nπ Available Starter Kits:\n")),Object.values(STARTER_KITS).forEach(e=>{const s=e.source?" (Custom)":" (Built-in)";console.log(chalk.green(` ${e.id}${chalk.gray(s)}`)),console.log(` ${e.name}`),console.log(chalk.gray(` ${e.description}`)),e.source&&console.log(chalk.cyan(` Source: ${e.source.url}`));const t=Object.entries(e.features).filter(([,e])=>!0===e).map(([e])=>e).join(", ");t&&console.log(chalk.magenta(` Features: ${t}`)),console.log()}),console.log(chalk.yellow("Usage:")),console.log(" npx create-prisma-php-app my-project --starter-kit=basic"),console.log(" npx create-prisma-php-app my-project --starter-kit=custom --starter-kit-source=https://github.com/user/repo"),console.log()}async function main(){try{const e=process.argv.slice(2),s=e.includes("-y");let t=e[0];const n=e.find(e=>e.startsWith("--starter-kit=")),c=n?.split("=")[1],o=e.find(e=>e.startsWith("--starter-kit-source=")),r=o?.split("=")[1];if(e.includes("--list-starter-kits"))return void showStarterKits();let i=null,a=!1;if(t){const n=process.cwd(),o=path.join(n,"prisma-php.json");if(c&&r){a=!0;const n={projectName:t,starterKit:c,starterKitSource:r,backendOnly:e.includes("--backend-only"),swaggerDocs:e.includes("--swagger-docs"),tailwindcss:e.includes("--tailwindcss"),typescript:e.includes("--typescript"),websocket:e.includes("--websocket"),mcp:e.includes("--mcp"),prisma:e.includes("--prisma")};i=await getAnswer(n,s)}else if(fs.existsSync(o)){const c=readJsonFile(o);let r=[];c.excludeFiles?.map(e=>{const s=path.join(n,e);fs.existsSync(s)&&r.push(s.replace(/\\/g,"/"))}),updateAnswer={projectName:t,backendOnly:c.backendOnly,swaggerDocs:c.swaggerDocs,tailwindcss:c.tailwindcss,websocket:c.websocket,mcp:c.mcp,prisma:c.prisma,typescript:c.typescript,isUpdate:!0,componentScanDirs:c.componentScanDirs??[],excludeFiles:c.excludeFiles??[],excludeFilePath:r??[],filePath:n};const a={projectName:t,backendOnly:e.includes("--backend-only")||c.backendOnly,swaggerDocs:e.includes("--swagger-docs")||c.swaggerDocs,tailwindcss:e.includes("--tailwindcss")||c.tailwindcss,typescript:e.includes("--typescript")||c.typescript,websocket:e.includes("--websocket")||c.websocket,prisma:e.includes("--prisma")||c.prisma,mcp:e.includes("--mcp")||c.mcp};i=await getAnswer(a,s),null!==i&&(updateAnswer={projectName:t,backendOnly:i.backendOnly,swaggerDocs:i.swaggerDocs,tailwindcss:i.tailwindcss,websocket:i.websocket,mcp:i.mcp,prisma:i.prisma,typescript:i.typescript,isUpdate:!0,componentScanDirs:c.componentScanDirs??[],excludeFiles:c.excludeFiles??[],excludeFilePath:r??[],filePath:n})}else{const n={projectName:t,starterKit:c,starterKitSource:r,backendOnly:e.includes("--backend-only"),swaggerDocs:e.includes("--swagger-docs"),tailwindcss:e.includes("--tailwindcss"),typescript:e.includes("--typescript"),websocket:e.includes("--websocket"),mcp:e.includes("--mcp"),prisma:e.includes("--prisma")};i=await getAnswer(n,s)}if(null===i)return void console.log(chalk.red("Installation cancelled."))}else i=await getAnswer({},s);if(null===i)return void console.warn(chalk.red("Installation cancelled."));const p=await fetchPackageVersion("create-prisma-php-app"),l=getInstalledPackageVersion("create-prisma-php-app");l?-1===compareVersions(l,p)&&(execSync("npm uninstall -g create-prisma-php-app",{stdio:"inherit"}),execSync("npm install -g create-prisma-php-app",{stdio:"inherit"})):execSync("npm install -g create-prisma-php-app",{stdio:"inherit"});const d=process.cwd();let u;if(t)if(a){const s=path.join(d,t);fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),u=s,await setupStarterKit(u,i),process.chdir(u);const n=path.join(u,"prisma-php.json");if(fs.existsSync(n)){const s=JSON.parse(fs.readFileSync(n,"utf8"));e.includes("--backend-only")&&(s.backendOnly=!0),e.includes("--swagger-docs")&&(s.swaggerDocs=!0),e.includes("--tailwindcss")&&(s.tailwindcss=!0),e.includes("--typescript")&&(s.typescript=!0),e.includes("--websocket")&&(s.websocket=!0),e.includes("--mcp")&&(s.mcp=!0),e.includes("--prisma")&&(s.prisma=!0),i={...i,backendOnly:s.backendOnly,swaggerDocs:s.swaggerDocs,tailwindcss:s.tailwindcss,typescript:s.typescript,websocket:s.websocket,mcp:s.mcp,prisma:s.prisma};let t=[];s.excludeFiles?.map(e=>{const s=path.join(u,e);fs.existsSync(s)&&t.push(s.replace(/\\/g,"/"))}),updateAnswer={...i,isUpdate:!0,componentScanDirs:s.componentScanDirs??[],excludeFiles:s.excludeFiles??[],excludeFilePath:t??[],filePath:u}}}else{const e=path.join(d,"prisma-php.json"),s=path.join(d,t),n=path.join(s,"prisma-php.json");fs.existsSync(e)?u=d:fs.existsSync(s)&&fs.existsSync(n)?(u=s,process.chdir(s)):(fs.existsSync(s)||fs.mkdirSync(s,{recursive:!0}),u=s,process.chdir(s))}else fs.mkdirSync(i.projectName,{recursive:!0}),u=path.join(d,i.projectName),process.chdir(i.projectName);let m=[npmPkg("typescript"),npmPkg("@types/node"),npmPkg("tsx"),npmPkg("http-proxy-middleware"),npmPkg("chalk"),npmPkg("npm-run-all"),npmPkg("browser-sync"),npmPkg("@types/browser-sync"),npmPkg("php-parser"),npmPkg("prisma-php")],g=[composerPkg("vlucas/phpdotenv"),composerPkg("firebase/php-jwt"),composerPkg("phpmailer/phpmailer"),composerPkg("guzzlehttp/guzzle"),composerPkg("symfony/uid"),composerPkg("brick/math"),composerPkg("tsnc/prisma-php")];if(i.swaggerDocs&&m.push(npmPkg("swagger-jsdoc"),npmPkg("@types/swagger-jsdoc")),i.swaggerDocs&&i.prisma&&m.push(npmPkg("prompts"),npmPkg("@types/prompts")),i.tailwindcss&&(m.push(npmPkg("tailwindcss"),npmPkg("postcss"),npmPkg("postcss-cli"),npmPkg("@tailwindcss/postcss"),npmPkg("cssnano")),g.push("gehrisandro/tailwind-merge-php")),i.websocket&&g.push("cboden/ratchet"),i.mcp&&g.push("php-mcp/server"),i.prisma&&execSync("npm install -g prisma-client-php@latest",{stdio:"inherit"}),i.typescript&&!i.backendOnly&&m.push(npmPkg("vite"),npmPkg("fast-glob")),i.starterKit&&!a&&await setupStarterKit(u,i),await installNpmDependencies(u,m,!0),await installComposerDependencies(u,g),t||execSync("npx tsc --init",{stdio:"inherit"}),await createDirectoryStructure(u,i),i.prisma&&execSync("npx ppo init --prisma-php",{stdio:"inherit"}),i.swaggerDocs){const e=path.join(u,"src","app","swagger-docs"),s=path.join(e,"apis"),t=path.join(u,"public","assets"),n=path.join(t,"dist");fs.existsSync(e)&&fs.readdirSync(e).length>0&&(console.log("Removing existing swagger-docs directory..."),fs.rmSync(e,{recursive:!0,force:!0})),console.log(chalk.blue("Cloning swagger-docs repository...")),execSync(`git clone https://github.com/TheSteelNinjaCode/prisma-php-swagger-docs.git ${e}`,{stdio:"inherit"});const c=path.join(e,".git");fs.existsSync(c)&&fs.rmSync(c,{recursive:!0,force:!0}),fs.existsSync(t)||(console.log(chalk.blue("Creating public/assets directory...")),fs.mkdirSync(t,{recursive:!0}));const o=path.join(e,"dist");fs.existsSync(o)?(console.log(chalk.blue("Moving dist folder to public/assets/dist...")),fs.existsSync(n)&&fs.rmSync(n,{recursive:!0,force:!0}),fs.renameSync(o,n),console.log(chalk.green("β Moved dist to public/assets/dist"))):console.warn(chalk.yellow("Warning: dist folder not found in cloned repository")),fs.existsSync(s)?console.log(chalk.green("β APIs folder preserved in src/app/swagger-docs/apis")):console.warn(chalk.yellow("Warning: apis folder not found in cloned repository")),console.log(chalk.green("β Swagger docs setup complete"))}if(updateAnswer?.isUpdate){const e=[],s=[],t=e=>{try{const s=path.join(u,"composer.json");if(fs.existsSync(s)){const t=JSON.parse(fs.readFileSync(s,"utf8"));return!(!t.require||!t.require[e])}return!1}catch{return!1}},n=e=>{try{const s=path.join(u,"package.json");if(fs.existsSync(s)){const t=JSON.parse(fs.readFileSync(s,"utf8"));return!!(t.dependencies&&t.dependencies[e]||t.devDependencies&&t.devDependencies[e])}return!1}catch{return!1}};if(updateAnswer.backendOnly){nonBackendFiles.forEach(e=>{const s=path.join(u,"src","app",e);fs.existsSync(s)&&(fs.unlinkSync(s),console.log(`${e} was deleted successfully.`))});["js","css"].forEach(e=>{const s=path.join(u,"src","app",e);fs.existsSync(s)&&(fs.rmSync(s,{recursive:!0,force:!0}),console.log(`${e} was deleted successfully.`))})}if(!updateAnswer.swaggerDocs){const s=path.join(u,"src","app","swagger-docs");fs.existsSync(s)&&(fs.rmSync(s,{recursive:!0,force:!0}),console.log("swagger-docs was deleted successfully."));["swagger-config.ts"].forEach(e=>{const s=path.join(u,"settings",e);fs.existsSync(s)&&(fs.unlinkSync(s),console.log(`${e} was deleted successfully.`))}),n("swagger-jsdoc")&&e.push("swagger-jsdoc"),n("@types/swagger-jsdoc")&&e.push("@types/swagger-jsdoc"),n("prompts")&&e.push("prompts"),n("@types/prompts")&&e.push("@types/prompts")}if(!updateAnswer.tailwindcss){["postcss.config.js"].forEach(e=>{const s=path.join(u,e);fs.existsSync(s)&&(fs.unlinkSync(s),console.log(`${e} was deleted successfully.`))});["tailwindcss","postcss","postcss-cli","@tailwindcss/postcss","cssnano"].forEach(s=>{n(s)&&e.push(s)});const c="gehrisandro/tailwind-merge-php";t(c)&&s.push(c)}if(!updateAnswer.websocket){["restart-websocket.ts"].forEach(e=>{const s=path.join(u,"settings",e);fs.existsSync(s)&&(fs.unlinkSync(s),console.log(`${e} was deleted successfully.`))});const e=path.join(u,"src","Lib","Websocket");fs.existsSync(e)&&(fs.rmSync(e,{recursive:!0,force:!0}),console.log("Websocket folder was deleted successfully.")),t("cboden/ratchet")&&s.push("cboden/ratchet")}if(!updateAnswer.mcp){["restart-mcp.ts"].forEach(e=>{const s=path.join(u,"settings",e);fs.existsSync(s)&&(fs.unlinkSync(s),console.log(`${e} was deleted successfully.`))});const e=path.join(u,"src","Lib","MCP");fs.existsSync(e)&&(fs.rmSync(e,{recursive:!0,force:!0}),console.log("MCP folder was deleted successfully.")),t("php-mcp/server")&&s.push("php-mcp/server")}if(!updateAnswer.prisma){["prisma","@prisma/client","@prisma/internals","better-sqlite3","@prisma/adapter-better-sqlite3","mariadb","@prisma/adapter-mariadb","pg","@prisma/adapter-pg","@types/pg"].forEach(s=>{n(s)&&e.push(s)})}if(!updateAnswer.typescript||updateAnswer.backendOnly){["vite.config.ts"].forEach(e=>{const s=path.join(u,e);fs.existsSync(s)&&(fs.unlinkSync(s),console.log(`${e} was deleted successfully.`))});const s=path.join(u,"ts");fs.existsSync(s)&&(fs.rmSync(s,{recursive:!0,force:!0}),console.log("ts folder was deleted successfully."));const t=path.join(u,"settings","vite-plugins");fs.existsSync(t)&&(fs.rmSync(t,{recursive:!0,force:!0}),console.log("settings/vite-plugins folder was deleted successfully."));["vite","fast-glob"].forEach(s=>{n(s)&&e.push(s)})}const c=e=>Array.from(new Set(e)),o=c(e),r=c(s);o.length>0&&(console.log(`Uninstalling npm packages: ${o.join(", ")}`),await uninstallNpmDependencies(u,o,!0)),r.length>0&&(console.log(`Uninstalling composer packages: ${r.join(", ")}`),await uninstallComposerDependencies(u,r))}if(!a||!fs.existsSync(path.join(u,"prisma-php.json"))){const e=u.replace(/\\/g,"\\"),s=bsConfigUrls(e),t={projectName:i.projectName,projectRootPath:e,phpEnvironment:"XAMPP",phpRootPathExe:"C:\\xampp\\php\\php.exe",bsTarget:s.bsTarget,bsPathRewrite:s.bsPathRewrite,backendOnly:i.backendOnly,swaggerDocs:i.swaggerDocs,tailwindcss:i.tailwindcss,websocket:i.websocket,mcp:i.mcp,prisma:i.prisma,typescript:i.typescript,version:p,componentScanDirs:updateAnswer?.componentScanDirs??["src","vendor/tsnc/prisma-php/src"],excludeFiles:updateAnswer?.excludeFiles??[]};fs.writeFileSync(path.join(u,"prisma-php.json"),JSON.stringify(t,null,2),{flag:"w"})}execSync(updateAnswer?.isUpdate?"C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar update":"C:\\xampp\\php\\php.exe C:\\ProgramData\\ComposerSetup\\bin\\composer.phar install",{stdio:"inherit"}),console.log("\n=========================\n"),console.log(`${chalk.green("Success!")} Prisma PHP project successfully created in ${chalk.green(u.replace(/\\/g,"/"))}!`),console.log("\n=========================")}catch(e){console.error("Error while creating the project:",e),process.exit(1)}}main();
|