UNPKG

@ssit-hub/mvp-generate-template

Version:

A beautiful CLI tool to quickly generate MVP project templates with modern frameworks and best practices

59 lines (56 loc) 32.3 kB
#!/usr/bin/env node var xe=Object.defineProperty;var g=(e,o)=>xe(e,"name",{value:o,configurable:!0});import{Command as We}from"commander";import l from"chalk";import A from"fs-extra";import B from"path";import Ne from"ora";import{fileURLToPath as Oe}from"url";import Q from"chalk";function X(){console.log(Q.cyan(` \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551 \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D `)),console.log(Q.white.bold(` \u{1F680} Project Template Generator \u{1F680} `))}g(X,"displayWelcome");import H from"inquirer";async function z(){let{template:e}=await H.prompt([{type:"list",name:"template",message:"Select a project template:",choices:[{name:"\u{1F310} Express + Handlebars (Node.js web server)",value:"express-hbs"},{name:"\u26A1 Express API (REST API server)",value:"express-api"},{name:"\u{1F4E6} Node.js CLI Tool",value:"node-cli"},{name:"\u{1F3D7}\uFE0F Basic Node.js Project",value:"basic-node"}]}]);return e}g(z,"determineTemplate");async function W(){let{useTypeScript:e}=await H.prompt([{type:"confirm",name:"useTypeScript",message:"Add TypeScript support?",default:!0}]);return e}g(W,"confirmTypeScript");async function M(){let{useESBuild:e}=await H.prompt([{type:"confirm",name:"useESBuild",message:"Add ESBuild for fast compilation?",default:!0}]);return e}g(M,"confirmESBuild");async function _(){let{npmInstall:e}=await H.prompt([{type:"confirm",name:"npmInstall",message:"Install dependencies automatically?",default:!0}]);return e}g(_,"confirmNpmInstall");import ee from"fs-extra";import $e from"path";import b from"chalk";import ve from"ora";import{execSync as Ce}from"child_process";function oe(e,o){let r=o.typescript?"ts":"js",t=o.esbuild?"esbuild":"default";return`${r}-${t}-${e}`}g(oe,"getTemplateName");async function te(e,o,r){let t=$e.join(e,"package.json"),n=await ee.readJson(t);n.name=o,n.description=n.description||`${o} - Generated by MVP Template Generator`,n.engines||(n.engines={node:">=16.0.0",npm:">=7.0.0"}),await ee.writeJson(t,n,{spaces:2})}g(te,"updatePackageJson");async function ne(e,o){if(!o.npmInstall)return;let r=ve("Installing dependencies...").start();try{Ce("npm install",{cwd:e,stdio:"inherit",env:{...process.env,npm_config_yes:"true"}}),r.succeed(b.green("Dependencies installed successfully!"))}catch{r.fail(b.red("Failed to install dependencies")),console.log(b.yellow(` \u{1F4A1} You can install them manually by running:`)),console.log(b.white(` npm install `))}}g(ne,"installDependencies");function re(e,o){console.log(` `+b.cyan("\u{1F389} Next steps:")),o.npmInstall||(console.log(b.gray(" # Navigate to your project")),console.log(b.white(` cd ${e}`)),console.log(""),console.log(b.gray(" # Install dependencies")),console.log(b.white(" npm install")),console.log("")),console.log(b.gray(" # Start development server")),o.esbuild?console.log(b.white(" npm run dev")):console.log(b.white(" npm start")),console.log(""),o.esbuild&&(console.log(b.gray(" # Build for production")),console.log(b.white(" npm run build")),console.log("")),o.typescript&&(console.log(b.gray(" # Type check")),console.log(b.white(" npm run typecheck")),console.log("")),console.log(b.green("Happy coding! \u{1F680}"))}g(re,"printNextSteps");import w from"fs-extra";import x from"path";import*as ie from"yaml";import c from"chalk";import{execSync as se}from"child_process";import U from"os";import{fileURLToPath as Te}from"url";import{z as a}from"zod";var ke=a.enum(["ts","esbuild","nextjs","react","vue","docker","mongodb","postgresql"]),Se=a.object({path:a.string().min(1,"Template path cannot be empty"),name:a.string().min(1,"Template name cannot be empty"),description:a.string().optional(),options:a.array(ke).default([]),category:a.string().optional(),priority:a.number().int().min(0).optional().default(0),deprecated:a.boolean().optional().default(!1),experimental:a.boolean().optional().default(!1)}),q=a.object({version:a.string().default("1.0.0"),templates:a.array(Se),defaultOptions:a.object({typescript:a.boolean().optional().default(!0),esbuild:a.boolean().optional().default(!0),npmInstall:a.boolean().optional().default(!0)}).optional()}),Ee=a.object({type:a.enum(["list","confirm","input","checkbox","password"]),name:a.string().min(1),message:a.string().min(1),choices:a.array(a.object({name:a.string(),value:a.any(),description:a.string().optional()})).optional(),default:a.any().optional(),validate:a.string().optional(),when:a.string().optional(),filter:a.string().optional(),required:a.boolean().optional().default(!1),pageSize:a.number().optional(),loop:a.boolean().optional(),templateDisplay:a.object({showDescription:a.boolean().optional().default(!0),showCategory:a.boolean().optional().default(!1),showOptions:a.boolean().optional().default(!0),maxWidth:a.number().optional().default(80),separator:a.string().optional().default(" - ")}).optional()}),J=a.object({version:a.string().default("1.0.0"),name:a.string().min(1),description:a.string().optional(),steps:a.array(Ee),postProcess:a.object({updatePackageJson:a.boolean().optional().default(!0),installDependencies:a.boolean().optional().default(!1),customScripts:a.array(a.string()).optional().default([])}).optional()}),ro=a.object({workflow:J.optional(),templates:q.optional()});import{execSync as Pe}from"child_process";import k from"chalk";function V(e,o,r="main"){let t,n;if(e.startsWith("git@github.com:")){let i=e.match(/git@github\.com:([^\/]+)\/(.+?)(?:\.git)?$/);if(!i)return null;[,t,n]=i}else if(e.includes("github.com")){let i=e.match(/github\.com[\/:]([^\/]+)\/([^\/\.]+?)(?:\.git)?(?:\/.*)?$/);if(!i)return null;[,t,n]=i}else return null;return n=n.replace(/\.git$/,""),`https://raw.githubusercontent.com/${t}/${n}/${r}/${o}`}g(V,"convertToGitHubRawUrl");async function T(e,o,r="main",t=!1){try{let n=V(e,o,r);if(!n)return t&&console.log(k.yellow(`\u26A0\uFE0F Not a GitHub repository: ${e}`)),null;t&&console.log(k.gray(`\u{1F310} Fetching from GitHub raw: ${n}`));let i=`curl -L -s -f "${n}"`;try{let s=Pe(i,{encoding:"utf8",stdio:t?["pipe","pipe","inherit"]:"pipe",timeout:3e4});return s&&s.trim()?(t&&console.log(k.green(`\u2705 Content fetched from GitHub raw: ${o}`)),s.trim()):(t&&console.log(k.yellow(`\u26A0\uFE0F Empty content returned for: ${o}`)),null)}catch(s){if(t){let p=s instanceof Error?s.message:String(s);p.includes("404")||p.includes("Not Found")?console.log(k.yellow(`\u26A0\uFE0F File not found: ${o}`)):p.includes("403")||p.includes("Forbidden")?console.log(k.yellow(`\u26A0\uFE0F Access forbidden (private repo?): ${o}`)):(console.log(k.yellow(`\u26A0\uFE0F curl failed for: ${n}`)),console.log(k.gray(`Error: ${p}`)))}return null}}catch(n){return t&&console.error(k.red(`\u274C GitHub raw download failed: ${n instanceof Error?n.message:n}`)),null}}g(T,"downloadFromGitHubRaw");function D(e){return e.includes("github.com")}g(D,"isGitHubRepository");var je=Te(import.meta.url),De=x.dirname(je);function N(){try{let e=x.dirname(x.dirname(De));for(;e!==x.dirname(e);){let o=x.join(e,"package.json");if(w.existsSync(o)){let r=JSON.parse(w.readFileSync(o,"utf-8"));if(r.repository){let t=r.repository,n=typeof t=="string"?t:t.url,i=typeof t=="object"?t.branch:"main";return{repository:{url:n,branch:i},defaultRepo:n,defaultBranch:i||"main"}}break}e=x.dirname(e)}return{defaultRepo:"https://github.com/duyvu871/mvp-generate-template.git",defaultBranch:"main"}}catch{return{defaultRepo:"https://github.com/duyvu871/mvp-generate-template.git",defaultBranch:"main"}}}g(N,"getPackageInfo");var ae=N(),I=ae.defaultRepo||"https://github.com/duyvu871/mvp-generate-template.git",E=ae.defaultBranch||"main";function Fe(){let e=U.platform(),o=U.homedir();switch(e){case"win32":return x.join(process.env.LOCALAPPDATA||x.join(o,"AppData","Local"),"mvp-generate-template","cache");case"darwin":return x.join(o,"Library","Caches","mvp-generate-template");case"linux":default:return x.join(process.env.XDG_CACHE_HOME||x.join(o,".cache"),"mvp-generate-template")}}g(Fe,"getCacheDirectory");function Re(){try{let e=se("npm config get cache",{encoding:"utf8"}).trim();if(e&&e!=="undefined")return x.join(e,"mvp-generate-template")}catch{}return null}g(Re,"getNpmCacheDirectory");function le(){let e=Re();return e||Fe()}g(le,"getOptimalCacheDirectory");async function ce(e=!1){try{let o=le();await w.pathExists(o)?(e&&console.log(c.gray(`\u{1F9F9} Cleaning cache directory: ${o}`)),await w.remove(o),console.log(c.green("\u2705 Cache cleaned successfully"))):e&&console.log(c.gray(`\u{1F4C1} No cache directory found at: ${o}`))}catch(o){console.error(c.red(`\u274C Failed to clean cache: ${o instanceof Error?o.message:o}`))}}g(ce,"cleanCache");async function Y(){let e=le(),o=await w.pathExists(e),r={cacheDir:e,exists:o};if(o)try{let t=await w.stat(e);r.size=`${(t.size/1024/1024).toFixed(2)} MB`;let n=await w.readdir(e);r.repositories=n.filter(i=>i.startsWith("repo-"))}catch{}return r}g(Y,"getCacheInfo");async function Ie(e,o,r,t={}){try{let n=process.env.NODE_ENV==="development"||process.argv.includes("--debug")||process.argv.includes("--verbose"),i;if(e.startsWith("http://")||e.startsWith("https://")||o){let f=o||I,u=o?e:`config/${e}`;if(n&&console.log(c.gray(`\u{1F310} Loading workflow config from GitHub raw: ${f}/${u}`)),D(f)){let m=await T(f,u,r||E,n);if(!m)throw new Error(`Configuration file not found in GitHub repository: ${u}`);i=m}else throw new Error(`Only GitHub repositories are supported for remote config loading. Repository: ${f} Use local files instead.`)}else{if(!await w.pathExists(e))throw new Error(`Configuration file not found: ${e}`);i=await w.readFile(e,"utf-8")}let s=ie.parse(i),p=J.safeParse(s);if(!p.success)throw console.error(c.red("\u274C Invalid YAML configuration:")),p.error.errors.forEach(f=>{console.error(c.red(` \u2022 ${f.path.join(".")}: ${f.message}`))}),new Error("Configuration validation failed");return p.data}catch(n){throw n instanceof Error?new Error(`Failed to load workflow config: ${n.message}`):n}}g(Ie,"loadWorkflowConfig");async function Ae(e,o,r,t={}){try{let n=process.env.NODE_ENV==="development"||process.argv.includes("--debug")||process.argv.includes("--verbose"),i;if(e.startsWith("http://")||e.startsWith("https://")||o){let f=o||I,u=o?e:`config/${e}`;if(n&&console.log(c.gray(`\u{1F310} Loading templates config from GitHub raw: ${f}/${u}`)),D(f)){let m=await T(f,u,r||E,n);if(!m)throw new Error(`Templates configuration file not found in GitHub repository: ${u}`);i=m}else throw new Error(`Only GitHub repositories are supported for remote config loading. Repository: ${f} Use local files instead.`)}else{if(!await w.pathExists(e))throw new Error(`Templates configuration file not found: ${e}`);i=await w.readFile(e,"utf-8")}let s=JSON.parse(i),p=q.safeParse(s);if(!p.success)throw console.error(c.red("\u274C Invalid JSON templates configuration:")),p.error.errors.forEach(f=>{console.error(c.red(` \u2022 ${f.path.join(".")}: ${f.message}`))}),new Error("Templates configuration validation failed");return p.data}catch(n){throw n instanceof Error?new Error(`Failed to load templates config: ${n.message}`):n}}g(Ae,"loadTemplatesConfig");async function K(e,o,r,t=!1,n={}){let i=process.env.NODE_ENV==="development"||process.argv.includes("--debug")||process.argv.includes("--verbose"),s={workflow:void 0,templates:void 0};i&&(console.log(c.gray(` \u{1F50D} Debug: Searching for config files`)),console.log(c.gray(` Strategy: ${t?"Local first":"Git-first via raw downloads (default)"}`)),o&&console.log(c.gray(` Repository: ${o}`)));let p=["mvp-gen.yml","mvp-gen.yaml",".mvp-gen.yml",".mvp-gen.yaml","config/workflow.yml","config/workflow.yaml"],f=["templates.json",".templates.json","config/templates.json","templates/config.json"];if(t){i&&console.log(c.gray(" \u{1F3E0} Checking local files first..."));for(let u of p){let m=x.join(e,u);if(await w.pathExists(m)){s.workflow=m,i&&console.log(c.green(` \u2713 Found local workflow: ${m}`));break}}for(let u of f){let m=x.join(e,u);if(await w.pathExists(m)){s.templates=m,i&&console.log(c.green(` \u2713 Found local templates: ${m}`));break}}if((!s.workflow||!s.templates)&&o&&D(o)){if(i&&console.log(c.gray(" \u{1F310} Falling back to GitHub raw downloads...")),!s.workflow){for(let u of p)if(await T(o,u,r||E,i)){s.workflow=u,i&&console.log(c.green(` \u2713 Found in GitHub raw: ${u}`));break}}if(!s.templates){for(let u of f)if(await T(o,u,r||E,i)){s.templates=u,i&&console.log(c.green(` \u2713 Found in GitHub raw: ${u}`));break}}}}else{let u=o||I;if(D(u)){i&&console.log(c.gray(` \u{1F310} Checking GitHub raw first: ${u}`));for(let m of p)if(await T(u,m,r||E,i)){s.workflow=m,i&&console.log(c.green(` \u2713 Found workflow in GitHub raw: ${m}`));break}for(let m of f)if(await T(u,m,r||E,i)){s.templates=m,i&&console.log(c.green(` \u2713 Found templates in GitHub raw: ${m}`));break}}if(!s.workflow||!s.templates){if(i&&console.log(c.gray(" \u{1F3E0} Falling back to local files...")),!s.workflow)for(let m of p){let y=x.join(e,m);if(await w.pathExists(y)){s.workflow=y,i&&console.log(c.green(` \u2713 Found local workflow: ${y}`));break}}if(!s.templates)for(let m of f){let y=x.join(e,m);if(await w.pathExists(y)){s.templates=y,i&&console.log(c.green(` \u2713 Found local templates: ${y}`));break}}}}return i&&(console.log(c.gray(` \u{1F3AF} Final results:`)),console.log(c.gray(` Workflow: ${s.workflow||"not found"}`)),console.log(c.gray(` Templates: ${s.templates||"not found"}`))),s}g(K,"findConfigFiles");async function O(e,o,r,t,n=!1){let i={},s=process.env.NODE_ENV==="development"||process.argv.includes("--debug")||process.argv.includes("--verbose"),p=!n&&!r?I:r;if(s&&(console.log(c.gray(` \u{1F50D} Debug: Configuration loading...`)),console.log(c.gray(` Strategy: ${n?"Local first":"GitHub raw downloads (default)"}`)),p&&(console.log(c.gray(` Repository: ${p}`)),console.log(c.gray(` Branch: ${t||E}`))),e&&console.log(c.gray(` Workflow: ${e}`)),o&&console.log(c.gray(` Templates: ${o}`))),e)try{i.workflow=await Ie(e,p,t),s&&console.log(c.green(`\u2713 Loaded workflow config: ${e}`))}catch(f){console.error(c.red(`\u274C Failed to load workflow config: ${f instanceof Error?f.message:String(f)}`))}if(o)try{i.templates=await Ae(o,p,t),s&&console.log(c.green(`\u2713 Loaded templates config: ${o}`))}catch(f){console.error(c.red(`\u274C Failed to load templates config: ${f instanceof Error?f.message:String(f)}`))}return s&&Object.keys(i).length===0&&console.log(c.yellow("\u26A0\uFE0F No configuration files loaded, using defaults")),i}g(O,"loadConfig");function pe(e,o){return e.templates.find(r=>r.name===o||r.path===o)||null}g(pe,"getTemplateConfig");function fe(e,o){let r={showDescription:!0,showCategory:!1,showOptions:!0,maxWidth:200,separator:" - ",...o};return e.templates.filter(t=>!t.deprecated).sort((t,n)=>(n.priority||0)-(t.priority||0)).map(t=>{let n=`${t.experimental?"\u{1F9EA} ":""}${t.name}`;if(r.showCategory&&t.category&&(n=`[${t.category.toUpperCase()}] ${n}`),r.showOptions&&t.options.length>0){let i=t.options.map(s=>{switch(s){case"ts":return"TypeScript";case"esbuild":return"ESBuild";case"nextjs":return"Next.js";case"react":return"React";case"vue":return"Vue";case"docker":return"Docker";case"mongodb":return"MongoDB";case"postgresql":return"PostgreSQL";default:return s}}).join(" + ");n=`${n} (${i})`}return r.showDescription&&t.description&&(n=`${n} ${c.gray(t.description)}`),{name:n,value:t.path,description:t.description}})}g(fe,"getTemplateChoices");function ge(){return{version:"1.0.0",name:"Default MVP Generator Workflow",description:"Standard project generation workflow",steps:[{type:"list",name:"template",message:"Select a project template:",required:!1},{type:"confirm",name:"typescript",message:"Add TypeScript support?",default:!0,required:!1},{type:"confirm",name:"esbuild",message:"Add ESBuild for fast compilation?",default:!0,required:!1},{type:"confirm",name:"npmInstall",message:"Install dependencies automatically?",default:!0,required:!1}],postProcess:{updatePackageJson:!0,installDependencies:!1,customScripts:[]}}}g(ge,"createDefaultWorkflowConfig");function me(){return{version:"1.0.0",templates:[{path:"express-hbs",name:"Express + Handlebars",description:"Full-stack web application with Express and Handlebars",options:["ts","esbuild"],category:"web",priority:100,deprecated:!1,experimental:!1},{path:"express-api",name:"Express API",description:"RESTful API server with Express",options:["ts","esbuild"],category:"api",priority:90,deprecated:!1,experimental:!1},{path:"node-cli",name:"Node.js CLI Tool",description:"Command-line application template",options:["ts","esbuild"],category:"cli",priority:80,deprecated:!1,experimental:!1},{path:"basic-node",name:"Basic Node.js",description:"Minimal Node.js project",options:["ts","esbuild"],category:"basic",priority:70,deprecated:!1,experimental:!1}],defaultOptions:{typescript:!0,esbuild:!0,npmInstall:!0}}}g(me,"createDefaultTemplatesConfig");async function Z(e,o,r=I,t=E,n=!1){try{if(!D(r))throw new Error(`Only GitHub repositories are supported for template downloads. Repository: ${r} Use local templates instead.`);n&&console.log(c.gray(`\u{1F310} Downloading template zip from GitHub: ${r}/templates/${e}.zip`));let i=`templates/${e}.zip`,s=V(r,i,t);if(!s)throw new Error(`Failed to convert to GitHub raw URL: ${r}`);n&&console.log(c.gray(`\u{1F4E1} Fetching: ${s}`));let p=x.join(U.tmpdir(),`mvp-template-${Date.now()}.zip`);try{if(se(`curl -L -s -f "${s}" -o "${p}"`,{stdio:n?"inherit":"pipe",timeout:6e4}),!await w.pathExists(p))throw new Error(`Template zip file not found: ${i} Make sure the template exists and has been packaged with 'npm run package-templates'`);let f=await w.stat(p);n&&console.log(c.gray(`\u{1F4BE} Downloaded zip: ${(f.size/1024).toFixed(1)} KB`)),await w.ensureDir(o);let u=(await import("adm-zip")).default,m=new u(p);if(m.extractAllTo(o,!0),n){let y=m.getEntries();console.log(c.gray(`\u{1F4E6} Extracted ${y.length} files to: ${o}`))}console.log(c.green(`\u2705 Template '${e}' downloaded and extracted successfully`))}finally{await w.pathExists(p)&&await w.remove(p)}}catch(i){throw n&&console.error(c.red(`\u274C Template download failed: ${i instanceof Error?i.message:i}`)),i}}g(Z,"downloadTemplateFilesFromGitHub");import Ge from"inquirer";import P from"chalk";var ue={required:e=>!e||typeof e=="string"&&e.trim()===""?"This field is required":!0,isValidProjectName:e=>/^[a-z0-9-_]+$/i.test(e)?!0:"Project name can only contain letters, numbers, hyphens, and underscores"};var de={hasTypeScript:e=>e.typescript===!0,hasESBuild:e=>e.esbuild===!0,hasDatabase:e=>e.features&&e.features.includes("database"),isExpressTemplate:e=>e.template==="express-hbs"||e.template==="express-api"},ye={trim:e=>e.trim(),toLowerCase:e=>e.toLowerCase(),toKebabCase:e=>e.toLowerCase().replace(/\s+/g,"-")};async function He(e,o,r){if(e.when&&de[e.when]&&!de[e.when](o))return;let t={type:e.type,name:e.name,message:e.message,default:e.default};if(e.name==="template"&&r&&(e.type==="list"||e.type==="checkbox")){let i=fe(r,e.templateDisplay);if(process.env.NODE_ENV==="development"||process.argv.includes("--debug")||process.argv.includes("--verbose")){console.log(P.gray(` \u{1F50D} Debug: Template choices generation`)),console.log(P.gray(` Total templates in config: ${r.templates.length}`)),console.log(P.gray(` Generated choices: ${i.length}`));let p=i.map(u=>u.value),f=[...new Set(p)];if(p.length!==f.length){console.log(P.yellow(` \u26A0\uFE0F Found ${p.length-f.length} duplicate choices!`));let u=p.filter((m,y)=>p.indexOf(m)!==y);console.log(P.yellow(` Duplicates: ${u.join(", ")}`))}}t.choices=i}else if(e.choices&&(e.type==="list"||e.type==="checkbox"))t.choices=e.choices;else if(e.name==="template"&&(e.type==="list"||e.type==="checkbox"))throw new Error("Template step requires either static choices in workflow config or templates configuration to be loaded");return e.pageSize&&(t.pageSize=e.pageSize),e.loop!==void 0&&(t.loop=e.loop),e.validate&&ue[e.validate]&&(t.validate=ue[e.validate]),e.filter&&ye[e.filter]&&(t.filter=ye[e.filter]),(await Ge.prompt([t]))[e.name]}g(He,"executePromptStep");async function we(e,o){let r={};console.log(P.cyan(` \u{1F504} Starting workflow: ${e.name}`)),e.description&&console.log(P.gray(e.description)),console.log("");for(let t of e.steps)try{let n=await He(t,r,o);n!==void 0&&(r[t.name]=n)}catch(n){throw console.error(P.red(`\u274C Error in step "${t.name}": ${n instanceof Error?n.message:n}`)),n}return r}g(we,"executeWorkflowPrompts");var Be=Oe(import.meta.url),zo=B.dirname(Be);function he(e){let o=N();e.command("init <project-name>").description('Initialize a new project (use "." or "./" for current directory)').option("-t, --template <template>","Project template to use").option("-ts, --typescript","Add TypeScript support").option("-es, --esbuild","Add ESBuild configuration").option("-i, --install","Install dependencies automatically").option("-c, --config <path>","Use specific configuration file").option("-w, --workflow <path>","Use specific workflow YAML file").option("--templates <path>","Use specific templates JSON file").option("-r, --repo <url>","Git repository URL for configurations and templates").option("-b, --branch <n>",`Git branch to use (default: ${o.defaultBranch||"main"})`).option("-l, --local","Use local files first instead of Git repository (legacy mode)").option("--debug","Show detailed debug information").option("--verbose","Show verbose output").addHelpText("after",` Examples: $ mvp-gen init my-project Create project (uses GitHub raw downloads) $ mvp-gen init . --local Create project using local files only $ mvp-gen init ./ Create project in current directory $ mvp-gen init my-app -t express-hbs --typescript --esbuild --install $ mvp-gen init . --template express-api --typescript $ mvp-gen init my-project --workflow ./custom-workflow.yml --local $ mvp-gen init my-app --workflow config/workflow.yml --templates config/templates.json $ mvp-gen init my-project --repo https://github.com/user/config-repo.git $ mvp-gen init my-app --repo https://github.com/user/repo.git --branch develop $ mvp-gen init my-project --debug Show debug information Download Behavior: - Default: Uses GitHub raw API for direct file downloads (fast and always up-to-date) - --local: Uses local files first (legacy v0.2.0 behavior) Default Repository: - ${o.defaultRepo||"https://github.com/duyvu871/mvp-generate-template.git"} (branch: ${o.defaultBranch||"main"}) `).action(async(r,t)=>{try{X(),await Le(r,t)}catch(n){console.error(l.red("Error:"),n instanceof Error?n.message:n),process.exit(1)}})}g(he,"initCommand");async function Le(e,o){let r,t;if(e==="."||e==="./"){r=process.cwd(),t=B.basename(r);let d=await A.readdir(r),v=[".git",".gitignore","README.md",".DS_Store","Thumbs.db","mvp-gen.yml","mvp-gen.yaml",".mvp-gen.yml",".mvp-gen.yaml","templates.json",".templates.json"],j=d.filter(R=>!v.includes(R));if(j.length>0)throw new Error(`Current directory is not empty! Found files: ${j.join(", ")} Please use an empty directory or specify a new project name.`)}else if(r=B.resolve(process.cwd(),e),t=e,await A.pathExists(r))throw new Error(`Directory "${r}" already exists!`);let n=process.cwd(),i,s,p,f=process.env.NODE_ENV==="development"||o.debug||o.verbose,u=N(),m=o.repo||u.defaultRepo||"https://github.com/duyvu871/mvp-generate-template.git",y=o.branch||u.defaultBranch||"main";f&&(console.log(l.cyan(` \u{1F680} MVP Generator - GitHub Raw Downloads`)),console.log(l.gray(`Strategy: ${o.local?"Local First (Legacy)":"GitHub Raw Downloads (Default)"}`)),console.log(l.gray(`Repository: ${m}`)),console.log(l.gray(`Branch: ${y}`)));try{if(o.workflow||o.templates)i=await O(o.workflow,o.templates,m,y,o.local||!1);else if(o.config){console.log(l.yellow("\u26A0\uFE0F Unified config files not yet supported. Using workflow and templates separately."));let d=await K(n,m,y,o.local||!1);i=await O(d.workflow,d.templates,m,y,o.local||!1)}else{let d=await K(n,m,y,o.local||!1);i=await O(d.workflow,d.templates,m,y,o.local||!1)}s=i.workflow,p=i.templates,f&&(s&&console.log(l.green("\u2705 Workflow configuration loaded successfully")),p&&(console.log(l.green("\u2705 Templates configuration loaded successfully")),console.log(l.gray(` Available templates: ${p.templates.length}`))))}catch(d){console.warn(l.yellow(`\u26A0\uFE0F Configuration loading failed: ${d instanceof Error?d.message:d}`)),console.log(l.gray("Using default configuration..."))}s||(s=ge()),p||(p=me());let S;if(o.template||o.typescript!==void 0||o.esbuild!==void 0||o.install!==void 0)console.log(l.cyan(` \u{1F916} Using CLI options (non-interactive mode)`)),S={template:o.template||await z(),typescript:o.typescript??await W(),esbuild:o.esbuild??await M(),npmInstall:o.install??await _()};else try{S=await we(s,p)}catch(d){console.warn(l.yellow(`\u26A0\uFE0F Workflow execution failed: ${d instanceof Error?d.message:d}`)),console.log(l.gray("Falling back to default prompts...")),S={template:await z(),typescript:await W(),esbuild:await M(),npmInstall:await _()}}let $=pe(p,S.template);f&&(console.log(l.gray(` \u{1F50D} Debug: Template configuration`)),console.log(l.gray(` Selected template: ${S.template}`)),$?(console.log(l.gray(` Template name: ${$.name}`)),console.log(l.gray(` Template path: ${$.path}`)),console.log(l.gray(` Template options: ${$.options.join(", ")}`)),console.log(l.gray(` TypeScript: ${$.options.includes("ts")}`)),console.log(l.gray(` ESBuild: ${$.options.includes("esbuild")}`))):console.log(l.yellow(" \u26A0\uFE0F Template config not found, using fallback")));let C={typescript:($==null?void 0:$.options.includes("ts"))||!1,esbuild:($==null?void 0:$.options.includes("esbuild"))||!1,npmInstall:!!S.npmInstall};f&&(console.log(l.gray(` \u{1F50D} Debug: Final project configuration`)),console.log(l.gray(` TypeScript: ${C.typescript}`)),console.log(l.gray(` ESBuild: ${C.esbuild}`)),console.log(l.gray(` NPM Install: ${C.npmInstall}`)));let F=Ne("Creating project...").start();try{e!=="."&&e!=="./"&&await A.ensureDir(r);let d;if($?(d=$.path,F.text=`Using template: ${$.name}`):(d=oe(S.template,C),F.text="Using fallback template structure..."),o.repo)f&&console.log(l.blue(` \u{1F4E5} Downloading template files from specified repository...`)),await Z(d,r,o.repo,o.branch||y,f);else if(!o.local)f&&console.log(l.blue(` \u{1F4E5} Downloading template files from package repository...`)),await Z(d,r,m,y,f);else{let v=B.join(process.cwd(),"templates",d);if(await A.pathExists(v))await A.copy(v,r),f&&console.log(l.green(`\u2713 Copied local template: ${d}`));else throw new Error(`Local template not found: ${d}`)}F.text="Updating configuration...",await te(r,t,C),F.succeed(l.green(`Project "${t}" created successfully!`)),C.npmInstall&&await ne(r,C);try{if(s.postProcess){if(console.log(l.cyan(` \u{1F527} Executing post-processing steps...`)),s.postProcess.customScripts&&s.postProcess.customScripts.length>0){let{execSync:v}=await import("child_process");for(let j of s.postProcess.customScripts)try{console.log(l.gray(` Running: ${j}`)),v(j,{cwd:r,stdio:"inherit",env:{...process.env,...Object.fromEntries(Object.entries(S).map(([R,L])=>[R,typeof L=="string"?L:String(L)]))}})}catch(R){throw console.error(l.red(`\u274C Script failed: ${j}`)),R}}console.log(l.green("\u2705 Post-processing completed"))}}catch(v){console.warn(l.yellow(`\u26A0\uFE0F Post-processing failed: ${v instanceof Error?v.message:v}`))}e==="."||e==="./"?ze(t,C):re(t,C)}catch(d){throw F.fail(l.red("Failed to create project")),d}}g(Le,"initializeProject");function ze(e,o){console.log(` `+l.cyan("\u{1F389} Next steps:")),o.npmInstall||(console.log(l.gray(" # Install dependencies")),console.log(l.white(" npm install")),console.log("")),console.log(l.gray(" # Start development server")),o.esbuild?console.log(l.white(" npm run dev")):console.log(l.white(" npm start")),console.log(""),o.esbuild&&(console.log(l.gray(" # Build for production")),console.log(l.white(" npm run build")),console.log("")),o.typescript&&(console.log(l.gray(" # Type check")),console.log(l.white(" npm run typecheck")),console.log("")),console.log(l.green("Happy coding! \u{1F680}"))}g(ze,"printCurrentDirectoryNextSteps");import h from"chalk";function be(e){let o=e.command("cache").description("Manage template and configuration cache");o.command("info").description("Show cache information").option("--debug","Show detailed debug information").action(async r=>{try{let t=await Y();console.log(h.cyan(` \u{1F4C1} Cache Information`)),console.log(h.gray(`Location: ${t.cacheDir}`)),console.log(h.gray(`Exists: ${t.exists?"\u2705 Yes":"\u274C No"}`)),t.exists&&(t.size&&console.log(h.gray(`Size: ${t.size}`)),t.repositories&&t.repositories.length>0?(console.log(h.gray(`Cached repositories: ${t.repositories.length}`)),r.debug&&(console.log(h.gray(` Repository details:`)),t.repositories.forEach((n,i)=>{console.log(h.gray(` ${i+1}. ${n}`))}))):console.log(h.gray("Cached repositories: 0"))),console.log(h.gray(` Cache types:`)),console.log(h.gray(" \u2022 Legacy Git repositories (from previous versions)")),console.log(h.gray(" \u2022 Old configuration files (YAML/JSON)")),console.log(h.gray(" \u2022 Previous template files")),console.log(h.cyan(` \u{1F4A1} Cache Management:`)),console.log(h.gray(" mvp-gen cache clean # Clean legacy cache")),console.log(h.gray(" Note: v0.3.0+ uses direct GitHub raw downloads (no cache needed)"))}catch(t){console.error(h.red("Error getting cache info:"),t instanceof Error?t.message:t),process.exit(1)}}),o.command("clean").description("Clean cache directory").option("--debug","Show detailed debug information").option("-f, --force","Force clean without confirmation").action(async r=>{try{if(!r.force){let{default:t}=await import("inquirer"),{confirm:n}=await t.prompt([{type:"confirm",name:"confirm",message:"Are you sure you want to clean the cache?",default:!1}]);if(!n){console.log(h.yellow("Cache clean cancelled."));return}}await ce(r.debug)}catch(t){console.error(h.red("Error cleaning cache:"),t instanceof Error?t.message:t),process.exit(1)}}),o.command("path").description("Show cache directory path").action(async()=>{try{let r=await Y();console.log(r.cacheDir)}catch(r){console.error(h.red("Error getting cache path:"),r instanceof Error?r.message:r),process.exit(1)}})}g(be,"cacheCommand");var G=new We;G.name("mvp-gen").description("\u{1F680} MVP Template Generator - Rapid prototyping with style!").version("0.3.0");he(G);be(G);process.argv.slice(2).length||(G.outputHelp(),process.exit(0));G.parse(process.argv);