@teispace/next-maker 5.0.4 → 5.0.6
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/CHANGELOG.md +14 -0
- package/README.md +1 -1
- package/dist/index.js +154 -154
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as
|
|
3
|
-
`),n=[];for(let o=0;o<r.length;o++){let s=r[o],i=s.match(
|
|
4
|
-
`)},
|
|
2
|
+
import{Command as Vp}from"commander";var De={name:"@teispace/next-maker",version:"5.0.6",description:"Create and grow Next.js 16 applications from the Teispace starter: composable init, feature-aware setup and doctor, and generators for the server-first data layer.",keywords:["nextjs","react","typescript","tailwindcss","starter","template","redux","zustand","tanstack-query","server-actions","cli"],type:"module",homepage:"https://github.com/teispace/npm-packages/tree/main/packages/next-maker#readme",bugs:{url:"https://github.com/teispace/npm-packages/issues"},license:"MIT",author:"Teispace",files:["dist","README.md","LICENSE","CHANGELOG.md"],bin:"dist/index.js",repository:{type:"git",url:"git+https://github.com/teispace/npm-packages.git",directory:"packages/next-maker"},scripts:{build:"tsx build.ts",start:"node dist/index.js",dev:"tsx --watch src/index.ts",test:"vitest run","test:watch":"vitest","test:cov":"vitest run --coverage","type-check":"tsc --noEmit",clean:"rm -rf dist",prepublishOnly:"yarn clean && yarn build",smoke:"tsx scripts/smoke.ts"},dependencies:{commander:"^15.0.0",degit:"^3.8.0",enquirer:"^2.4.1",ora:"^9.4.1",picocolors:"^1.1.1","png-to-ico":"^3.0.2",sharp:"^0.35.3"},devDependencies:{"@types/degit":"^2.8.6","@types/node":"^26.2.0",esbuild:"^0.28.2",tsx:"^4.23.12",typescript:"^6.0.3",vitest:"^4.1.11"},engines:{node:">=24.0.0"},publishConfig:{access:"public",registry:"https://registry.npmjs.org/"}};import{copyFile as sa}from"node:fs/promises";import Mt from"node:path";import F from"picocolors";var ln=/@next-maker:([\w-]+)(?::(start|end))?/,ri=/^\s*(?:\{\s*\/\*.*?\*\/\s*\}|\/\*.*?\*\/|\/\/.*|#.*)\s*$/,ni=e=>e.replace(/\s*\{\s*\/\*[^*]*@next-maker:[^*]*\*\/\s*\}\s*$/,"").replace(/\s*\/\*[^*]*@next-maker:[^*]*\*\/\s*$/,"").replace(/\s*\/\/[^\n]*@next-maker:[^\n]*$/,"").replace(/\s*#[^\n]*@next-maker:[^\n]*$/,""),er=(e,{off:t})=>{let r=e.split(`
|
|
3
|
+
`),n=[];for(let o=0;o<r.length;o++){let s=r[o],i=s.match(ln);if(!i){n.push(s);continue}let[,a,c]=i,l=t.has(a);if(c==="start"){let u=oi(r,o+1,a);l&&(o=u);continue}if(c==="end")continue;let p=ri.test(s);if(l){p&&o++;continue}p||n.push(ni(s))}return n.join(`
|
|
4
|
+
`)},oi=(e,t,r)=>{for(let n=t;n<e.length;n++){let o=e[n].match(ln);if(o&&o[1]===r&&o[2]==="end")return n}throw new Error(`Anchor @next-maker:${r}:start has no matching :end`)},et=e=>e.includes("@next-maker:"),mn=(e,t)=>{let r=e.split(`
|
|
5
5
|
`),n=[],o=new RegExp(`^import\\s+${t}\\s+from\\s+['"][^'"]+['"];?\\s*$`),s=/^(import\s+(?:type\s+)?)\{([^}]+)\}(\s+from\s+['"][^'"]+['"];?\s*)$/;for(let i of r){if(o.test(i))continue;let a=i.match(s);if(a){let c=a[2].split(",").map(p=>p.trim()).filter(Boolean),l=c.filter(p=>p.replace(/^type\s+/,"").split(/\s+as\s+/)[0]!==t);if(l.length===0)continue;if(l.length<c.length){n.push(`${a[1]}{ ${l.join(", ")} }${a[3]}`);continue}}n.push(i)}return n.join(`
|
|
6
|
-
`)},
|
|
7
|
-
`),n=new RegExp(`^([ \\t]*)<${t}\\b`),o=-1,s="";for(let
|
|
8
|
-
`);return
|
|
9
|
-
`,"utf-8")},
|
|
6
|
+
`)},ke=(e,t)=>{let r=e.split(`
|
|
7
|
+
`),n=new RegExp(`^([ \\t]*)<${t}\\b`),o=-1,s="";for(let d=0;d<r.length;d++){let f=r[d].match(n);if(f){o=d,s=f[1];break}}if(o===-1)return e;let i=o;for(;i<r.length&&!/>\s*$/.test(r[i]);)i++;if(i>=r.length)throw new Error(`unwrapJsx: opening <${t}> never closes`);if(/\/>\s*$/.test(r[i]))return e;let a=s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),c=new RegExp(`^${a}</${t}>\\s*$`),l=-1;for(let d=i+1;d<r.length;d++)if(c.test(r[d])){l=d;break}if(l===-1)throw new Error(`unwrapJsx: no closing </${t}> at the opening indent`);let p=r.slice(i+1,l).map(d=>d.startsWith(" ")?d.slice(2):d),u=[...r.slice(0,o),...p,...r.slice(l+1)].join(`
|
|
8
|
+
`);return mn(u,t)},Pe=(e,t)=>{let r=new RegExp(`\\b${t}\\(`,"g"),n=e,o=r.exec(n);for(;o;){let s=o.index,i=0,a=s+o[0].length-1;for(;a<n.length;a++)if(n[a]==="(")i++;else if(n[a]===")"&&(i--,i===0))break;if(i!==0)throw new Error(`unwrapCall: unbalanced parentheses after ${t}(`);let c=n.slice(s+o[0].length,a);n=n.slice(0,s)+c+n.slice(a+1),r.lastIndex=s,o=r.exec(n)}return n};import{cp as bn,mkdir as li,mkdtemp as mi,readdir as wn,readFile as Le,rm as rr,rmdir as ui,stat as di,writeFile as Ue}from"node:fs/promises";import{tmpdir as fi}from"node:os";import _ from"node:path";import{readdir as si}from"node:fs/promises";import $t from"node:path";var un=e=>{let t="^";for(let r=0;r<e.length;r++){let n=e[r];n==="*"?e[r+1]==="*"?(r++,e[r+1]==="/"?(r++,t+="(?:.*/)?"):t+=".*"):t+="[^/]*":t+=n.replace(/[.+?^${}()|[\]\\]/g,"\\$&")}return new RegExp(`${t}$`)},ii=new Set(["node_modules",".git",".next","coverage","playwright-report","test-results"]),A=async(e,t=ii)=>{let r=[],n=async o=>{let s;try{s=await si(o,{withFileTypes:!0})}catch{return}for(let i of s)if(i.isDirectory()){if(t.has(i.name))continue;await n($t.join(o,i.name))}else i.isFile()&&r.push($t.relative(e,$t.join(o,i.name)).split($t.sep).join("/"))};return await n(e),r.sort()},Y=(e,t)=>{let r=un(t);return e.filter(n=>r.test(n))};import{readFile as dn}from"node:fs/promises";import fn from"node:path";var kt="next-maker.json",ae=".next-maker/overlays",gn=1,z=e=>{throw new Error(`Invalid ${kt}: ${e}`)},yn=e=>{(typeof e!="object"||e===null)&&z("not an object");let t=e;t.manifestVersion!==gn&&z(`manifestVersion ${String(t.manifestVersion)} is not supported by this CLI (expected ${gn})`),(!t.starter?.name||!t.starter.version)&&z("starter.name and starter.version are required"),(!t.options||typeof t.options!="object")&&z("options is required"),(!t.features||typeof t.features!="object")&&z("features is required"),(!t.packageManagers||typeof t.packageManagers!="object")&&z("packageManagers is required");let r=t.options,n=t.features;for(let[o,s]of Object.entries(r))["choice","boolean","multi"].includes(s.type)||z(`option ${o} has unknown type ${String(s.type)}`),s.type!=="boolean"&&!Array.isArray(s.values)&&z(`option ${o} needs a values array`),s.default===void 0&&z(`option ${o} needs a default`);for(let[o,s]of Object.entries(n)){(!s.when||typeof s.when!="object")&&z(`feature ${o} needs a when`);for(let i of Object.keys(s.when))r[i]||z(`feature ${o} refers to unknown option ${i}`)}return{...t,always:{remove:t.always?.remove??[]}}},tt=async e=>{let t=fn.join(e,kt),r;try{r=await dn(t,"utf-8")}catch{throw new Error(`The starter at ${e} has no ${kt}. This CLI needs a starter that ships a composition manifest (nextjs-starter 2.x or later).`)}let n=yn(JSON.parse(r));try{let o=JSON.parse(await dn(fn.join(e,"package.json"),"utf-8"));typeof o.version=="string"&&o.version&&(n.starter={...n.starter,version:o.version})}catch{}return n},hn=(e,t)=>e.some(r=>r===t),Pt=(e,t,r)=>t===void 0?!1:e?.type==="multi"&&Array.isArray(t)?t.some(n=>hn(r,n)):hn(r,t),de=(e,t,r)=>Object.entries(t.when).every(([n,o])=>Pt(e.options[n],r[n],o)),ai=e=>e.type==="boolean"?!1:e.type==="multi"?[]:e.default,ci=(e,t,r)=>{if(e.type==="boolean"){if(typeof r=="boolean")return r;if(r==="true")return!0;if(r==="false")return!1;throw new Error(`Option ${t} expects true or false, got ${String(r)}`)}if(e.type==="multi"){let s=(Array.isArray(r)?r:String(r).split(",")).map(i=>String(i).trim()).filter(Boolean);for(let i of s)if(!e.values?.includes(i))throw new Error(`Option ${t} does not accept ${i}; valid: ${e.values?.join(", ")}`);return s}let n=String(r);if(!e.values?.includes(n))throw new Error(`Option ${t} does not accept ${n}; valid: ${e.values?.join(", ")}`);return n},rt=(e,t={})=>{let r={},n=[],o=Object.keys(t).filter(s=>!e.options[s]);for(let[s,i]of Object.entries(e.options)){let a=t[s];r[s]=a===void 0?i.default:ci(i,s,a)}for(let[s,i]of Object.entries(e.options))if(i.requires)for(let[a,c]of Object.entries(i.requires)){if(Pt(e.options[a],r[a],c))continue;let l=ai(i);r[s]!==l&&JSON.stringify(r[s])!==JSON.stringify(l)&&(n.push({option:s,value:l,reason:`${s} requires ${a} to be ${c.map(String).join(" or ")}`}),r[s]=l)}return{answers:r,forced:n,unknown:o}};var fe=(e,t)=>{switch(e){case"npm":return`npm run ${t}`;case"bun":return`bun run ${t}`;case"yarn":return`yarn ${t}`;default:return`pnpm ${t}`}},pi=e=>{switch(e){case"npm":return[[/\bpnpm install --frozen-lockfile\b/g,"npm ci"],[/\bpnpm install\b/g,"npm install"],[/\bpnpm add -D\b/g,"npm install --save-dev"],[/\bpnpm add\b/g,"npm install"],[/\bpnpm dlx\b/g,"npx"],[/\bpnpm exec\b/g,"npx --no --"],[/\bpnpm ([\w:.-]+)/g,"npm run $1"]];case"yarn":return[[/\bpnpm install --frozen-lockfile\b/g,"yarn install --immutable"],[/\bpnpm install\b/g,"yarn install"],[/\bpnpm add -D\b/g,"yarn add -D"],[/\bpnpm add\b/g,"yarn add"],[/\bpnpm dlx\b/g,"yarn dlx"],[/\bpnpm exec\b/g,"yarn"],[/\bpnpm ([\w:.-]+)/g,"yarn $1"]];case"bun":return[[/\bpnpm install --frozen-lockfile\b/g,"bun install --frozen-lockfile"],[/\bpnpm install\b/g,"bun install"],[/\bpnpm add -D\b/g,"bun add -d"],[/\bpnpm add\b/g,"bun add"],[/\bpnpm dlx\b/g,"bunx"],[/\bpnpm exec\b/g,"bunx"],[/\bpnpm ([\w:.-]+)/g,"bun run $1"]];default:return[]}},St=(e,t)=>{if(t==="pnpm")return e;let r=e;for(let[n,o]of pi(t))r=r.replace(n,o);return r},tr=e=>e==="package.json"||e.startsWith(".husky/")||e.endsWith(".md")||e===".lintstagedrc.mjs";var Se=e=>[...new Set(e)],or=(e,t,r)=>{let n=e.packageManagers[r];if(!n)throw new Error(`This starter does not support ${r}; valid: ${Object.keys(e.packageManagers).join(", ")}`);let o=[],s=[],i=[],a=[],c=[],l=[],p=[];for(let[d,f]of Object.entries(e.features)){let y=de(e,f,t);(y?o:s).push(d);let h=y?f.on:f.off;if(!h)continue;i.push(h);let x=Object.keys(f.when);h.overlay&&!a.some(b=>b.name===h.overlay)&&a.push({name:h.overlay,options:x}),h.remove?.length&&c.push({feature:d,options:x,patterns:h.remove});for(let b of h.unwrapJsx??[])l.push({...b,options:x});for(let b of h.unwrapCall??[])p.push({...b,options:x})}let u=Se([...e.always.remove,...n.remove??[]]);return{answers:t,packageManager:r,pmSpec:n,featuresOn:o,featuresOff:s,pmOverlays:n.overlay?[n.overlay]:[],featureOverlays:a,removePatterns:Se([...u,...c.flatMap(d=>d.patterns)]),unconditionalRemovals:u,featureRemovals:c,anchorsOff:new Set(i.flatMap(d=>d.anchors??[])),unwrapJsx:l,unwrapCall:p,packages:Se(i.flatMap(d=>d.packages??[])),devPackages:Se(i.flatMap(d=>d.devPackages??[])),scripts:Se(i.flatMap(d=>d.scripts??[])),env:Se(i.flatMap(d=>d.env??[])),packageJsonKeys:Se(i.flatMap(d=>d.packageJsonKeys??[]))}},gi=new Set([".ts",".tsx",".js",".mjs",".cjs",".css",".json",".yml",".yaml",".md",".sh",".example",".mdx"]),hi=e=>gi.has(_.extname(e))||e.startsWith(".husky/")||e==="Dockerfile",xn=async(e,t,r)=>{let n=_.join(e,r),o=await A(n,new Set);if(o.length===0)throw new Error(`Overlay "${r}" is missing or empty at ${ae}/${r}`);for(let s of o){let i=_.join(t,s);await li(_.dirname(i),{recursive:!0}),await bn(_.join(n,s),i,{force:!0})}return o},sr=async(e,t=e)=>{let r;try{r=await wn(t,{withFileTypes:!0})}catch{return}for(let n of r)n.isDirectory()&&n.name!=="node_modules"&&n.name!==".git"&&await sr(e,_.join(t,n.name));t!==e&&(await wn(t)).length===0&&await ui(t)},vn=(e,t)=>{let r=t.split("."),n=e;for(let s of r.slice(0,-1)){let i=n?.[s];if(typeof i!="object"||i===null)return;n=i}let o=r[r.length-1];if(n&&o in n&&delete n[o],r.length>1){let s=r.slice(0,-1).join("."),i=r.slice(0,-1).reduce((a,c)=>a?.[c],e);i&&typeof i=="object"&&Object.keys(i).length===0&&vn(e,s)}},yi=async(e,t,r)=>{let n=_.join(e,"package.json"),o=JSON.parse(await Le(n,"utf-8"));for(let i of t.packages)delete o.dependencies?.[i];for(let i of t.devPackages)delete o.devDependencies?.[i];for(let i of t.scripts)delete o.scripts?.[i];for(let i of t.packageJsonKeys)vn(o,i);let s=r.validateScript;if(s&&o.scripts?.[s.name]){let i=s.steps.filter(a=>o.scripts[a]);o.scripts[s.name]=i.map(a=>fe(t.packageManager,a)).join(" && ")}if(o.scripts)for(let[i,a]of Object.entries(o.scripts))typeof a=="string"&&(o.scripts[i]=St(a,t.packageManager));if(t.pmSpec.packageManager?o.packageManager=t.pmSpec.packageManager:delete o.packageManager,o.engines){for(let i of Object.keys(r.packageManagers))delete o.engines[i];Object.assign(o.engines,t.pmSpec.engines??{})}await Ue(n,`${JSON.stringify(o,null,2)}
|
|
9
|
+
`,"utf-8")},wi=async(e,t)=>{let r=_.join(e,".env.example"),n;try{n=await Le(r,"utf-8")}catch{return}for(let o of t)n=n.replace(new RegExp(`^${o}=.*\\n?`,"gm"),"");await Ue(r,n.replace(/\n{3,}/g,`
|
|
10
10
|
|
|
11
|
-
`),"utf-8")},
|
|
11
|
+
`),"utf-8")},ir=async(e,t,r)=>{let n={removed:[],overlaid:[],anchored:[],unwrapped:[]},o=_.join(e,ae),s=await mi(_.join(fi(),"next-maker-overlays-"));try{await nr(o)&&await bn(o,s,{recursive:!0});for(let l of r.pmOverlays)n.overlaid.push(...await xn(s,e,l));let a=await A(e),c=new Set(r.removePatterns.flatMap(l=>Y(a,l)));for(let l of c)await rr(_.join(e,l),{force:!0}),n.removed.push(l);for(let l of r.featureOverlays){let p=await xn(s,e,l.name),u=[...r.unconditionalRemovals,...r.featureRemovals.filter(f=>!f.options.some(y=>l.options.includes(y))).flatMap(f=>f.patterns)],d=new Set(u.flatMap(f=>Y(p,f)));for(let f of d)await rr(_.join(e,f),{force:!0}),n.removed.push(f);n.overlaid.push(...p.filter(f=>!d.has(f)))}}finally{await rr(s,{recursive:!0,force:!0})}await sr(e);let i=await A(e);for(let a of i){if(!hi(a))continue;let c=_.join(e,a),l=await Le(c,"utf-8");et(l)&&(await Ue(c,er(l,{off:r.anchorsOff}),"utf-8"),n.anchored.push(a))}for(let{file:a,tag:c}of r.unwrapJsx){let l=_.join(e,a);if(!await nr(l))continue;let p=await Le(l,"utf-8"),u=ke(p,c);u!==p&&(await Ue(l,u,"utf-8"),n.unwrapped.push(`${a}:<${c}>`))}for(let{file:a,name:c}of r.unwrapCall){let l=_.join(e,a);if(!await nr(l))continue;let p=await Le(l,"utf-8"),u=Pe(p,c);u!==p&&(await Ue(l,u,"utf-8"),n.unwrapped.push(`${a}:${c}()`))}if(await yi(e,r,t),await wi(e,r.env),r.packageManager!=="pnpm")for(let a of await A(e)){if(!tr(a)||a==="package.json")continue;let c=_.join(e,a),l=await Le(c,"utf-8"),p=St(l,r.packageManager);p!==l&&await Ue(c,p,"utf-8")}return n},nr=async e=>{try{return await di(e),!0}catch{return!1}};import{readFile as xi}from"node:fs/promises";import bi from"node:path";var ar=async e=>{let t=bi.resolve(process.cwd(),e),r;try{r=await xi(t,"utf-8")}catch{throw new Error(`Config file not found: ${t}`)}let n=JSON.parse(r);if(typeof n!="object"||n===null||Array.isArray(n))throw new Error(`Config file ${t} must contain a JSON object`);if(n.options!==void 0&&(typeof n.options!="object"||n.options===null))throw new Error(`Config file ${t}: "options" must be an object`);return n},Ee=e=>{let t={};for(let r of e??[]){let n=r.indexOf("=");if(n<=0)throw new Error(`--set expects key=value, got "${r}"`);t[r.slice(0,n).trim()]=r.slice(n+1).trim()}return t};var cr={default:{description:"Manifest defaults: Redux, fetch, i18n, dark mode, unit + e2e tests, hooks",answers:{}},minimal:{description:"No state store, no i18n, no dark mode, no tests, no hooks",answers:{state:"none",i18n:!1,darkMode:!1,tests:!1,e2e:!1,hooks:!1,commitizen:!1}},full:{description:"Everything on: WebSocket, both HTTP adapters, Docker, CI, analyzer, OpenAPI, templates",answers:{ws:!0,http:"both",docker:!0,ci:!0,analyzer:!0,openapi:!0,githubTemplates:!0,communityFiles:["CODE_OF_CONDUCT.md","CONTRIBUTING.md","SECURITY.md"]}},zustand:{description:"Defaults with Zustand instead of Redux",answers:{state:"zustand"}},spa:{description:"Client-heavy app: Zustand, Axios, no i18n, Docker and CI on",answers:{state:"zustand",http:"axios",i18n:!1,docker:!0,ci:!0}}},Be=()=>Object.keys(cr),Ge=e=>{let t=cr[e];if(!t)throw new Error(`Unknown preset "${e}". Valid: ${Be().join(", ")}.`);return{...t.answers}};var nt=new Set;function Et(e){return nt.add(e),()=>nt.delete(e)}function $n(e){let{logger:t=c=>console.error(c),exitOnCancel:r=process.env.NODE_ENV!=="test"}=e??{},n=!1,o=async()=>{let c=[...nt];nt.clear();for(let l of c)try{await l()}catch(p){t(`Cleanup failed: ${p instanceof Error?p.message:String(p)}`)}},s=()=>{if(n){r&&process.exit(130);return}n=!0;let c=nt.size>0;o().then(()=>{console.log(""),t("Setup cancelled by user"),console.log(""),r&&process.exit(c?130:0)})},i=c=>{if(c!==null&&typeof c=="object"&&"code"in c&&c.code==="ERR_USE_AFTER_CLOSE"){s();return}if(c instanceof Error){t(`Uncaught exception: ${c.message}`);return}t(`Uncaught exception: ${String(c)}`)},a=c=>{if(c instanceof Error){t(`Unhandled promise rejection: ${c.message}`);return}t(`Unhandled promise rejection: ${String(c)}`)};return process.on("SIGINT",s),process.on("SIGTERM",s),process.on("uncaughtException",i),process.on("unhandledRejection",a),()=>{process.off("SIGINT",s),process.off("SIGTERM",s),process.off("uncaughtException",i),process.off("unhandledRejection",a)}}import Q from"picocolors";var vi={reset:e=>e,red:Q.red,green:Q.green,yellow:Q.yellow,blue:Q.blue,cyan:Q.cyan,magenta:Q.magenta,white:Q.white,gray:Q.gray,bright:Q.bold,dim:Q.dim};function ge(e,t="reset"){let r=vi[t]??(n=>n);console.log(r(e))}function Tt(e){ge(`\u2716 ${e}`,"red")}var v=Tt;function kn(e){ge(`\u26A0 ${e}`,"yellow")}function m(e){ge(e)}function jt(){console.log(""),ge("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557","cyan"),ge("\u2551 \u2551","cyan"),ge("\u2551 \u{1F680} Create Teispace Next.js App \u{1F680} \u2551","cyan"),ge("\u2551 \u2551","cyan"),ge("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D","cyan"),console.log("")}import $i from"ora";function Rt(e="",t){let r=$i({text:e,...t});return r.start(),r}var ki=e=>e.charAt(0).toUpperCase()+e.slice(1),V=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase()),k=e=>ki(V(e));import Pi from"ora";var g=Pi();import{execFile as Si}from"node:child_process";import{cp as Pn,mkdir as Ei,stat as Ti}from"node:fs/promises";import ot from"node:path";import{promisify as ji}from"node:util";import Ri from"degit";var Ci=ji(Si),Sn="teispace/nextjs-starter",Ct="v2.0.0-alpha.3",Oi="NEXT_MAKER_STARTER_PATH",Ai=(e=Ct)=>e==="main"?Sn:`${Sn}#${e}`,W=(e={})=>{let t=e.starterPath??process.env[Oi];return t?{kind:"local",location:ot.resolve(t)}:{kind:"remote",location:Ai(e.ref)}},Ii=new Set(["node_modules",".git",".next","coverage","playwright-report","test-results",".env"]),Ot=async(e,t={})=>{let r=t.source??W();if(r.kind==="local"){try{if(!(await Ti(r.location)).isDirectory())throw new Error("not a directory")}catch{throw new Error(`Starter path ${r.location} is not a directory`)}return await Ni(r.location,e),r}return await Ri(r.location,{cache:!1,force:!0,verbose:t.verbose??!1}).clone(e),r},Ni=async(e,t)=>{let r=null;try{let{stdout:n}=await Ci("git",["-C",e,"ls-files","-z","--cached","--others","--exclude-standard"],{maxBuffer:67108864});r=n.split("\0").filter(Boolean)}catch{r=null}if(r===null){await Pn(e,t,{recursive:!0,filter:n=>!Ii.has(ot.basename(n))});return}for(let n of r){let o=ot.join(t,n);await Ei(ot.dirname(o),{recursive:!0});try{await Pn(ot.join(e,n),o)}catch{}}};import{existsSync as pr}from"node:fs";import st from"node:fs/promises";import Fi from"node:path";var C=async e=>st.readFile(e,"utf-8"),j=async(e,t)=>{await st.mkdir(Fi.dirname(e),{recursive:!0}),await st.writeFile(e,t,"utf-8")};var En=async e=>{pr(e)&&await st.unlink(e)},it=async e=>{pr(e)&&await st.rm(e,{recursive:!0,force:!0})},Tn=async(e,t)=>{let r=await C(e),n=JSON.parse(r),o=t(n);await j(e,JSON.stringify(o,null,2))},w=e=>pr(e);import{execFile as Mi}from"node:child_process";import{promisify as _i}from"node:util";var Di=_i(Mi),ce=(e,t)=>Di("git",e,t?{cwd:t}:void 0);function Li(e){let t=e.trim();if(!t||t.startsWith("-"))throw new Error(`Invalid git remote "${e}".`);if(!(/^https?:\/\/\S+$/.test(t)||/^git@[\w.-]+:\S+$/.test(t)||/^ssh:\/\/\S+$/.test(t)||/^[\w.-]+\/[\w.-]+$/.test(t)))throw new Error(`Invalid git remote "${e}". Expected an https/ssh URL or "owner/repo".`)}var At=async(e,t,r)=>{try{if(await ce(["init"],e),await ce(["add","."],e),await ce(["commit","-m","Initial commit from @teispace/next-maker"],e),t&&(Li(t),await ce(["remote","add","origin",t],e),r)){await ce(["fetch","origin"],e);let n="main";try{await ce(["show-branch","origin/main"],e)}catch{try{await ce(["show-branch","origin/master"],e),n="master"}catch{n="main"}}await ce(["merge",`origin/${n}`,"--allow-unrelated-histories","-X","ours"],e),await ce(["push","origin",`HEAD:${n}`],e)}}catch(n){console.warn("Failed to initialize git repository",n)}};import{execFile as Ui}from"node:child_process";import{promisify as Bi}from"node:util";var Gi=Bi(Ui),jn=(e,t,r)=>Gi(e,t,{cwd:r,maxBuffer:64*1024*1024}),Vi=e=>{let t=e;return(`${t.stdout??""}
|
|
12
12
|
${t.stderr??""}`.trim()||(t.message??String(e))).split(`
|
|
13
13
|
`).filter(Boolean).slice(-12).join(`
|
|
14
|
-
`)},
|
|
15
|
-
${
|
|
16
|
-
`,"utf-8")},
|
|
17
|
-
`),a=[];for(let c=0;c<i.length;c++){let l=i[c].match(n);if(l)if(l[1]==="start"){let p=c+1;for(;p<i.length&&!/@next-maker:[\w-]+:end/.test(i[p]);)a.push(i[p++]);c=p}else l[1]==="end"||(/^\s*(\{\s*\/\*|\/\*|\/\/|#)/.test(i[c])?i[c+1]!==void 0&&a.push(i[++c]):a.push(i[c].replace(/\s*(\{\s*\/\*|\/\*|\/\/|#).*$/,"")))}a.length&&r.push({file:o,lines:a})}return r},
|
|
18
|
-
`,"utf-8"),await
|
|
14
|
+
`)},qi=e=>{switch(e){case"pnpm":return["install","--no-frozen-lockfile"];case"yarn":return["install","--no-immutable"];default:return["install"]}},he=async(e,t)=>{try{await jn(t,qi(t),e)}catch(r){throw new Error(`Failed to install dependencies with ${t}:
|
|
15
|
+
${Vi(r)}`,{cause:r})}},It=async(e,t,r)=>{let n=t==="npm"?["run",r]:[r];try{await jn(t,n,e)}catch(o){console.warn(`Warning: Failed to run script '${r}': ${o}`)}};import Hi from"enquirer";var{prompt:Rn}=Hi,Ji=/^[a-z0-9-_]+$/,zi=/^\d+\.\d+\.\d+$/,Cn=e=>Ji.test(e),On=e=>zi.test(e),at=e=>{if(!Cn(e))throw new Error(`Invalid project name "${e}". Use lowercase letters, digits, hyphens, and underscores only \u2014 no slashes, "..", or absolute paths.`)},ye=(e,t={})=>{let r=t.projectName??e??"my-app";at(r);let n=t.version??"0.1.0";if(!On(n))throw new Error(`Invalid version "${n}". Expected semver (e.g. 0.1.0).`);return{projectName:r,description:t.description??"A Next.js application",author:t.author??"Teispace",version:n,email:t.email??"support@example.com",gitRemote:t.gitRemote??"",pushToRemote:t.pushToRemote??!1,gitIssues:t.gitIssues??"",gitHomepage:t.gitHomepage??"",readme:t.readme??!0,copyEnv:t.copyEnv??!0}},An=e=>e.state?.answers??e.enquirer?.answers??{},In=async e=>{e!==void 0&&at(e);let t=await Rn([{type:"input",name:"projectName",message:"What is the project name?",initial:e||"my-app",skip:!!e,validate:r=>Cn(r)||"Project name must be lowercase and contain only alphanumeric characters, hyphens, and underscores."},{type:"input",name:"description",message:"Project description:",initial:"A Next.js application"},{type:"input",name:"author",message:"Author:",initial:"Teispace"},{type:"input",name:"version",message:"Version:",initial:"0.1.0",validate:r=>On(r)||"Version must be a valid semantic version (x.y.z)."},{type:"input",name:"email",message:"Support email:",initial:"support@example.com"},{type:"input",name:"gitRemote",message:"GitHub repository URL (optional):",initial:"",validate:r=>!r||/^(https:\/\/github\.com\/[\w-]+\/[\w.-]+(\.git)?|git@github\.com:[\w-]+\/[\w.-]+\.git)$/.test(r)||"Enter a GitHub URL (https://github.com/user/repo or git@github.com:user/repo.git)."},{type:"confirm",name:"pushToRemote",message:"Push the initial commit to the remote?",initial:!1,skip:function(){return!An(this).gitRemote}},{type:"confirm",name:"readme",message:"Create a README.md?",initial:!0},{type:"confirm",name:"copyEnv",message:"Copy .env.example to .env?",initial:!0}]);return ye(e,{...t,projectName:e??t.projectName})},Nt=async(e,t={})=>{let r=Object.entries(e.options).map(([s,i])=>{let a=t[s]??i.default,c={name:s,message:i.label??s,skip:function(){if(!i.requires)return!1;let p=An(this);return!Object.entries(i.requires).every(([u,d])=>Pt(e.options[u],p[u],d))}};if(i.type==="boolean")return{...c,type:"confirm",initial:!!a};if(i.type==="multi")return{...c,type:"multiselect",choices:(i.values??[]).map(p=>({name:p,value:p})),initial:Array.isArray(a)?a:[]};let l=i.values??[];return{...c,type:"select",choices:l,initial:Math.max(0,l.indexOf(String(a)))}}),n=await Rn(r),o={};for(let[s,i]of Object.entries(e.options)){let a=n[s];a!==void 0&&(o[s]=i.type==="multi"&&!Array.isArray(a)?[]:a)}return o};import Wi from"node:path";var O={NEXT_CONFIG:"next.config.ts",POSTCSS_CONFIG:"postcss.config.mjs",BIOME_CONFIG:"biome.json",VITEST_CONFIG:"vitest.config.ts",TEST_DIR:"test",TS_CONFIG:"tsconfig.json",PACKAGE_JSON:"package.json",ENV_EXAMPLE:".env.example",GITIGNORE:".gitignore",README:"README.md",LICENSE:"LICENSE",CHANGELOG:"CHANGELOG.md",NVM_RC:".nvmrc",NPM_RC:".npmrc",AGENTS_MD:"AGENTS.md",CLAUDE_MD:"CLAUDE.md",SRC:"src",APP:"src/app",COMPONENTS:"src/components",LIB:"src/lib",PROVIDERS:"src/providers",STYLES:"src/styles",TYPES:"src/types",UTILS:"src/lib/utils",HOOKS:"src/hooks",SERVICES:"src/services",STORE:"src/store",I18N:"src/i18n",GLOBALS_CSS:"src/styles/globals.css",ROOT_LAYOUT:"src/app/layout.tsx",ROOT_PAGE:"src/app/page.tsx",ROOT_PROVIDER:"src/providers/RootProvider.tsx",PROVIDERS_INDEX:"src/providers/index.ts",COMPONENTS_INDEX:"src/components/index.ts",TYPES_INDEX:"src/types/index.ts",UTILS_INDEX:"src/lib/utils/index.ts",CONFIG_INDEX:"src/lib/config/index.ts",CONSTANTS:"src/lib/config/constants.ts",APP_LOCALES:"src/lib/config/app-locales.ts",SEO_FILE:"src/lib/config/seo.ts",SITEMAP_FILE:"src/app/sitemap.ts",MIDDLEWARE:"src/middleware.ts",PROXY:"src/proxy.ts",I18N_TYPES:"src/types/i18n.ts",HTTP_UTILS:"src/lib/utils/http",HTTP_SHARED:"src/lib/utils/http/shared",HTTP_SERVER_FILE:"src/lib/utils/http/server.ts",HTTP_BUNDLE_SENTINEL_DIR:"src/lib/utils/http/__bundle-sentinel__",HTTP_BUNDLE_SENTINEL_FILE:"src/lib/utils/http/__bundle-sentinel__/client-bundle-sentinel.tsx",WS_UTILS:"src/lib/utils/ws",WS_SLICE_FILE:"src/store/slices/ws.slice.ts",WS_SLICE_TEST_FILE:"src/store/slices/ws.slice.test.ts",ROOT_REDUCER:"src/store/rootReducer.ts",TEST_SETUP_FILE:"test/setup.ts",AXIOS_CLIENT:"src/lib/utils/http/axios-client",FETCH_CLIENT:"src/lib/utils/http/fetch-client",AXIOS_CLIENT_FILE:"src/lib/utils/http/axios-client/index.ts",FETCH_CLIENT_FILE:"src/lib/utils/http/fetch-client/index.ts",CLIENT_UTILS:"src/lib/utils/http/client-utils.ts",APP_APIS:"src/lib/config/app-apis.ts",API_URL_FILE:"src/lib/config/api-url.ts",STORAGE_SERVICE:"src/services/storage",COUNTER_FEATURE:"src/features/counter",I18N_DIR:"src/i18n",LOCALE_DIR:"src/app/[locale]",ERRORS_DIR:"src/lib/errors",UTILITY_TYPES_DIR:"src/types/utility",COMMON_TYPES_DIR:"src/types/common",HTTP_TYPES:"src/types/common/http.types.ts",GITHUB_DIR:".github",HUSKY_DIR:".husky",STORE_PROVIDER:"src/providers/StoreProvider.tsx",THEME_PROVIDER:"src/providers/CustomThemeProvider.tsx",COUNTER_COMPONENT:"src/features/counter/components/Counter.tsx",LOCALE_PAGE:"src/app/[locale]/page.tsx",LOCALE_LAYOUT:"src/app/[locale]/layout.tsx",COMMITLINT_CONFIG:"commitlint.config.mjs",LINTSTAGED_RC:".lintstagedrc.mjs",CZRC:".czrc",DOCKERIGNORE:".dockerignore",DOCKERFILE:"Dockerfile",DOCKER_COMPOSE:"docker-compose.yml",GITHUB_WORKFLOWS:".github/workflows",GITHUB_ISSUE_TEMPLATE:"ISSUE_TEMPLATE",GITHUB_PR_TEMPLATE:"PULL_REQUEST_TEMPLATE.md",CODE_OF_CONDUCT:"CODE_OF_CONDUCT.md",CONTRIBUTING:"CONTRIBUTING.md",SECURITY:"SECURITY.md",SCRIPTS_DIR:"scripts",DOCS_DIR:"docs",CLAUDE_DIR:".claude",VSCODE_DIR:".vscode"};var Ki=e=>{let t=e.gitRemote,r=e.gitHomepage,n=e.gitIssues;if(!t)return{remote:"",homepage:"",issues:""};t.startsWith("git@github.com:")&&(t=t.replace("git@github.com:","https://github.com/").replace(/\.git$/,""));let o=t.replace(/\.git$/,"");return r||=`${o}#readme`,n||=`${o}/issues`,t.endsWith(".git")||(t=`${t}.git`),{remote:t,homepage:r,issues:n}},Nn=async(e,t)=>{let{remote:r,homepage:n,issues:o}=Ki(t);await Tn(Wi.join(e,O.PACKAGE_JSON),s=>(s.name=t.projectName,s.version=t.version,s.description=t.description,s.author=t.author,delete s.keywords,r?(s.homepage=n,s.bugs={url:o},s.repository={type:"git",url:r}):(delete s.homepage,delete s.bugs,delete s.repository),s))};import{readdir as ra}from"node:fs/promises";import xe from"node:path";import{cp as Xi,mkdir as Yi,mkdtemp as Qi,readFile as Te,rm as _n,writeFile as Ft}from"node:fs/promises";import{tmpdir as Zi}from"node:os";import N from"node:path";var Ve=".next-maker.json",we=async(e,t)=>{await Ft(N.join(e,Ve),`${JSON.stringify(t,null,2)}
|
|
16
|
+
`,"utf-8")},pe=async e=>{let t=N.join(e,Ve);if(!w(t))throw new Error(`No ${Ve} in ${e}. Only projects created with next-maker 5+ can be reconfigured; run \`next-maker init\` for a new project.`);return JSON.parse(await Te(t,"utf-8"))},qe=async(e,t={})=>{let r=e.starter.source.startsWith("/")&&w(e.starter.source)?e.starter.source:void 0,n=W({starterPath:t.starterPath??r}),o=await Qi(N.join(Zi(),"next-maker-starter-"));await Ot(o,{source:n});let s=await tt(o),i=await A(o);return{dir:o,source:n,manifest:s,files:i,dispose:()=>_n(o,{recursive:!0,force:!0})}},Fn=(e,t)=>e[t]??{},ea=e=>[...e.off?.anchors??[],...e.on?.anchors??[]],ta=async(e,t)=>{if(t.length===0)return[];let r=[],n=new RegExp(`@next-maker:(?:${t.map(o=>o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join("|")})(?::(start|end))?`);for(let o of e.files){if(o.startsWith(".next-maker/"))continue;let s;try{s=await Te(N.join(e.dir,o),"utf-8")}catch{continue}if(!et(s))continue;let i=s.split(`
|
|
17
|
+
`),a=[];for(let c=0;c<i.length;c++){let l=i[c].match(n);if(l)if(l[1]==="start"){let p=c+1;for(;p<i.length&&!/@next-maker:[\w-]+:end/.test(i[p]);)a.push(i[p++]);c=p}else l[1]==="end"||(/^\s*(\{\s*\/\*|\/\*|\/\/|#)/.test(i[c])?i[c+1]!==void 0&&a.push(i[++c]):a.push(i[c].replace(/\s*(\{\s*\/\*|\/\*|\/\/|#).*$/,"")))}a.length&&r.push({file:o,lines:a})}return r},Dn=async(e,t)=>{let r=e.manifest.features[t];if(!r)throw new Error(`Unknown feature "${t}". Valid: ${Object.keys(e.manifest.features).join(", ")}.`);let n=Fn(r,"off"),o=Fn(r,"on"),s=new Set(e.manifest.always.remove.flatMap(c=>Y(e.files,c))),i=[...new Set((n.remove??[]).flatMap(c=>Y(e.files,c)))].filter(c=>!s.has(c)).sort(),a=o.overlay?(await A(N.join(e.dir,ae,o.overlay),new Set)).sort():[];return{id:t,feature:r,files:i,overlayFiles:a,packages:n.packages??[],devPackages:n.devPackages??[],scripts:n.scripts??[],env:n.env??[],snippets:await ta(e,ea(r))}},Ln=e=>/\.(test|spec)\.tsx?$/.test(e)||e.startsWith("test/")||e.includes("__test-utils__"),lr=async(e,t,r)=>{let n=JSON.parse(await Te(N.join(e,"package.json"),"utf-8")),o=t.answers.tests!==!1,s=[],i=Object.values(r.manifest.features).filter(c=>!de(r.manifest,c,t.answers)).flatMap(c=>c.off?.remove??[]),a=new Set(i.flatMap(c=>Y(r.files,c)));for(let[c,l]of Object.entries(r.manifest.features)){let p=de(r.manifest,l,t.answers);if(!p){s.push({id:c,on:p,drift:[]});continue}let u=await Dn(r,c),d=[];for(let f of[...u.files,...u.overlayFiles])!o&&Ln(f)||a.has(f)||w(N.join(e,f))||d.push({kind:"missingFile",file:f});for(let f of u.packages)n.dependencies?.[f]||d.push({kind:"missingPackage",name:f,dev:!1});for(let f of u.devPackages)n.devDependencies?.[f]||d.push({kind:"missingPackage",name:f,dev:!0});for(let f of u.scripts)n.scripts?.[f]||d.push({kind:"missingScript",name:f});s.push({id:c,on:p,drift:d})}return s},Un=async(e,t,r,n)=>{let o=t.answers,s=JSON.parse(await Te(N.join(r.dir,"package.json"),"utf-8")),i=[],a=[];for(let[p,u]of Object.entries(r.manifest.features)){let d=de(r.manifest,u,o),f=de(r.manifest,u,n);d!==f&&(f?i:a).push(await Dn(r,p))}let c=n.tests!==!1,l={before:o,after:n,turningOn:i,turningOff:a,copy:[],remove:[],addPackages:{},addDevPackages:{},removePackages:[],addScripts:{},removeScripts:[],unwrapJsx:[],unwrapCall:[],manual:[]};for(let p of a){for(let u of[...p.files,...p.overlayFiles])w(N.join(e,u))&&l.remove.push(u);l.removePackages.push(...p.packages,...p.devPackages),l.removeScripts.push(...p.scripts),l.unwrapJsx.push(...p.feature.off?.unwrapJsx??[]),l.unwrapCall.push(...p.feature.off?.unwrapCall??[]),p.snippets.length&&l.manual.push({feature:p.id,direction:"delete",snippets:p.snippets})}for(let p of i){for(let u of[...p.files,...p.overlayFiles])!c&&Ln(u)||(!w(N.join(e,u))||p.overlayFiles.includes(u))&&l.copy.push(u);for(let u of p.packages)l.addPackages[u]=s.dependencies?.[u]??"latest";for(let u of p.devPackages)l.addDevPackages[u]=s.devDependencies?.[u]??"latest";for(let u of p.scripts)s.scripts?.[u]&&(l.addScripts[u]=s.scripts[u]);p.snippets.length&&l.manual.push({feature:p.id,direction:"add",snippets:p.snippets})}return l.copy=[...new Set(l.copy)].sort(),l.remove=[...new Set(l.remove)].sort(),l},Bn=async(e,t,r,n)=>{for(let i of r.remove)await _n(N.join(e,i),{force:!0});for(let i of r.copy){let a=r.turningOn.find(p=>p.overlayFiles.includes(i)),c=a?.feature.on?.overlay?N.join(t.dir,ae,a.feature.on.overlay,i):N.join(t.dir,i),l=N.join(e,i);await Yi(N.dirname(l),{recursive:!0}),await Xi(c,l,{force:!0})}for(let{file:i,tag:a}of r.unwrapJsx){let c=N.join(e,i);if(!w(c))continue;let l=await Te(c,"utf-8"),p=ke(l,a);p!==l&&await Ft(c,p,"utf-8")}for(let{file:i,name:a}of r.unwrapCall){let c=N.join(e,i);if(!w(c))continue;let l=await Te(c,"utf-8"),p=Pe(l,a);p!==l&&await Ft(c,p,"utf-8")}let o=N.join(e,"package.json"),s=JSON.parse(await Te(o,"utf-8"));s.dependencies??={},s.devDependencies??={},s.scripts??={};for(let i of r.removePackages)delete s.dependencies[i],delete s.devDependencies[i];for(let i of r.removeScripts)delete s.scripts[i];Object.assign(s.dependencies,r.addPackages),Object.assign(s.devDependencies,r.addDevPackages),Object.assign(s.scripts,r.addScripts),s.dependencies=Mn(s.dependencies),s.devDependencies=Mn(s.devDependencies),await Ft(o,`${JSON.stringify(s,null,2)}
|
|
18
|
+
`,"utf-8"),await we(e,{...n,answers:r.after})},Mn=e=>Object.fromEntries(Object.entries(e).sort(([t],[r])=>t.localeCompare(r)));var na=(e,t)=>e.replace(/support@teispace\.com/g,t.email).replace(/Next\.js Starter/g,t.projectName).replace(/\[AUTHOR\]/g,t.author).replace(/\[COMPANY\]/g,t.author).replace(/\[EMAIL\]/g,t.email),Gn=async(e,t)=>{let r=[xe.join(e,O.GITHUB_DIR,O.GITHUB_PR_TEMPLATE),xe.join(e,O.CODE_OF_CONDUCT),xe.join(e,O.CONTRIBUTING),xe.join(e,O.SECURITY)],n=xe.join(e,O.GITHUB_DIR,O.GITHUB_ISSUE_TEMPLATE);if(w(n))for(let o of await ra(n))r.push(xe.join(n,o));for(let o of r){if(!w(o))continue;let s=await C(o),i=na(s,t);i!==s&&await j(o,i)}},Vn=async(e,t)=>{let r=xe.join(e,O.ENV_EXAMPLE);if(!w(r))return;let n=await C(r);n=n.replace(/^CONTAINER_NAME=.*$/m,`CONTAINER_NAME=${t.projectName} # -public`),n=n.replace(/^IMAGE_NAME=.*$/m,`IMAGE_NAME=${t.projectName} # -public`),await j(r,n)},oa=(e,t,r)=>{let n=e.options[t];if(!n)return null;let o=n.label??t;return n.type==="boolean"?r?o:null:Array.isArray(r)?r.length?`${o}: ${r.join(", ")}`:null:r==="none"?null:`${o}: ${String(r)}`},qn=async(e,t,r,n,o)=>{let s=xe.join(e,O.README);if(!t.readme){await En(s);return}let i=Object.entries(n).filter(([l])=>l!=="packageManager").map(([l,p])=>oa(r,l,p)).filter(l=>l!==null),a=[["dev","development server"],["build","production build"],["start","serve the production build"],["lint:fix","lint, format, and sort imports"],["type-check","route types + tsc"],["check:deprecated","fail on deprecated API usage"],["validate","every gate CI runs"]];n.tests&&a.push(["test","unit and component tests"]),n.e2e&&a.push(["test:e2e","Playwright against a production build"]);let c=`# ${t.projectName}
|
|
19
19
|
|
|
20
20
|
${t.description}
|
|
21
21
|
|
|
@@ -25,7 +25,7 @@ Generated from [@teispace/nextjs-starter](https://github.com/teispace/nextjs-sta
|
|
|
25
25
|
|
|
26
26
|
\`\`\`bash
|
|
27
27
|
${o} install
|
|
28
|
-
${
|
|
28
|
+
${fe(o,"dev")}
|
|
29
29
|
\`\`\`
|
|
30
30
|
|
|
31
31
|
Open [http://localhost:3000](http://localhost:3000). Copy \`.env.example\` to \`.env\` and set \`NEXT_PUBLIC_API_URL\` to your API origin. Production builds also need \`NEXT_PUBLIC_APP_URL\`, the public URL of this app.
|
|
@@ -39,7 +39,7 @@ ${i.map(l=>`- ${l}`).join(`
|
|
|
39
39
|
|
|
40
40
|
| Script | Purpose |
|
|
41
41
|
| :-- | :-- |
|
|
42
|
-
${a.map(([l,p])=>`| \`${
|
|
42
|
+
${a.map(([l,p])=>`| \`${fe(o,l)}\` | ${p} |`).join(`
|
|
43
43
|
`)}
|
|
44
44
|
|
|
45
45
|
## Guides
|
|
@@ -51,9 +51,9 @@ ${a.map(([l,p])=>`| \`${ce(o,l)}\` | ${p} |`).join(`
|
|
|
51
51
|
${n.ws?"- `src/lib/ws/README.md`: the WebSocket client\n":""}${n.i18n?"- `src/i18n/README.md`: internationalization\n":""}- \`docs/ui-libraries.md\`: adding shadcn/ui, Radix, MUI, Mantine, or HeroUI
|
|
52
52
|
|
|
53
53
|
Generators: \`npx @teispace/next-maker feature <name>\`, \`page <name>\`, \`component <name>\`, \`env <NAME>\`${n.i18n?", `locale <code>`":""}.
|
|
54
|
-
`;await
|
|
55
|
-
Cleaning up ${n.projectName}...`)),await
|
|
56
|
-
Process interrupted.`)),await i()});try{let c=
|
|
54
|
+
`;await j(s,c)};var je=async e=>{await Ot(e.projectPath,{source:e.source});let t=await tt(e.projectPath);return ct(t,e.supplied,e.packageManager)},ct=(e,t,r)=>{let{answers:n,forced:o,unknown:s}=rt(e,t),i=n.packageManager??r??"pnpm";return{manifest:e,answers:n,forced:o,unknown:s,packageManager:i,plan:or(e,n,i)}},Re=async(e,t,r,n)=>{let{manifest:o,answers:s,packageManager:i,plan:a}=r,c=await ir(e,o,a);return await Nn(e,t),await Gn(e,t),await Vn(e,t),await qn(e,t,o,s,i),await we(e,{cli:De.version,starter:{name:o.starter.name,version:o.starter.version,source:n.location},packageManager:i,answers:s,identity:{projectName:t.projectName,description:t.description,author:t.author,version:t.version,email:t.email,gitRemote:t.gitRemote,readme:t.readme}}),c};var mr=["pnpm","npm","yarn","bun"],ia=e=>{if(e!==void 0){if(!mr.includes(e))throw new Error(`Invalid --package-manager "${e}". Valid: ${mr.join(", ")}.`);return e}},aa=(e,t=[])=>[...t,e],Hn=e=>{e.command("init").description("Create a new Next.js project from the Teispace starter").argument("[name]","Project name").option("-y, --yes","Skip every prompt and use the starter defaults").option("--preset <name>",`Start from a preset (${Be().join(", ")})`).option("--config <file>","Read identity and options from a JSON file (non-interactive)").option("--set <key=value>","Override one starter option (repeatable), e.g. --set i18n=false",aa).option("--package-manager <pm>",`Package manager (${mr.join(", ")})`).option("--starter-path <dir>","Compose from a local starter checkout instead of the pinned tag").option("--dry-run","Print the composition plan and create nothing").option("--no-install","Skip dependency installation").option("--no-git","Skip git init").action(async(t,r)=>{try{await ma(t,r)}catch(n){v(`${n instanceof Error?n.message:n}`),process.exit(1)}})},pt=async(e,t,r,n)=>{let o=Rt(e);n.current=o;try{let s=await r();return o.succeed(t),s}finally{n.current===o&&(n.current=null)}},ca=e=>Array.isArray(e)?e.length?e.join(", "):"none":String(e),pa=(e,t,r)=>{m(F.bold("Options"));for(let[n,o]of Object.entries(t.answers)){let s=e.options[n]?.label??n;m(` ${F.dim(s.padEnd(44))} ${ca(o)}`)}for(let n of r)m(F.yellow(` ! ${n.reason}; ${n.option} turned off`));m(""),m(F.bold("Composition")),m(` ${F.green("keep")} ${t.featuresOn.join(", ")||"(none)"}`),m(` ${F.red("drop")} ${t.featuresOff.join(", ")||"(none)"}`),(t.featureOverlays.length||t.pmOverlays.length)&&m(` ${F.cyan("overlay")} ${[...t.pmOverlays,...t.featureOverlays].join(", ")}`),(t.packages.length||t.devPackages.length)&&m(` ${F.red("uninstall")} ${[...t.packages,...t.devPackages].join(", ")}`),m("")},la=async(e,t)=>{let r=t.config?await ar(t.config):void 0,n=ia(t.packageManager??r?.packageManager),s={...t.preset?Ge(t.preset):r?.preset?Ge(r.preset):{},...r?.options??{},...Ee(t.set)};n&&(s.packageManager=n);let i=!(t.yes||r);return{identity:i?await In(e):ye(e,{projectName:r?.name,description:r?.description,author:r?.author,version:r?.version,email:r?.email,gitRemote:r?.gitRemote,readme:r?.readme,copyEnv:r?.copyEnv}),packageManager:n??"pnpm",suppliedOptions:s,interactive:i,install:t.install!==!1&&r?.install!==!1,git:t.git!==!1&&r?.git!==!1}},ma=async(e,t)=>{jt();let r=await la(e,t),{identity:n}=r,o=Mt.resolve(process.cwd(),n.projectName);if(w(o))throw new Error(`Directory ${n.projectName} already exists.`);let s={current:null},i=async()=>{w(o)&&(s.current?.stop(),s.current=null,m(F.yellow(`
|
|
55
|
+
Cleaning up ${n.projectName}...`)),await it(o))},a=Et(async()=>{m(F.red(`
|
|
56
|
+
Process interrupted.`)),await i()});try{let c=W({starterPath:t.starterPath}),l=await pt(`Fetching starter (${c.location})...`,"Starter fetched.",()=>je({projectPath:o,source:c,supplied:r.suppliedOptions,packageManager:r.packageManager}),s);if(m(F.dim(` ${l.manifest.starter.name} ${l.manifest.starter.version}`)),m(""),r.interactive){let f={...r.suppliedOptions,...await Nt(l.manifest,l.answers)};t.packageManager&&(f.packageManager=t.packageManager),l=ct(l.manifest,f,r.packageManager),m("")}for(let f of l.unknown)m(F.yellow(` ! unknown option "${f}" ignored`));let{packageManager:p,plan:u,manifest:d}=l;if(pa(d,u,l.forced),t.dryRun){a(),await it(o),m(F.cyan("Dry run: nothing was created."));return}await pt("Composing project...","Project composed.",()=>Re(o,n,l,c),s),r.install&&(await pt(`Installing dependencies with ${p}...`,"Dependencies installed.",()=>he(o,p),s),await pt("Formatting...","Formatted.",()=>It(o,p,"lint:fix"),s)),n.copyEnv&&w(Mt.join(o,".env.example"))&&await sa(Mt.join(o,".env.example"),Mt.join(o,".env")),r.git&&await pt("Initializing git...","Git initialized.",()=>At(o,n.gitRemote,n.pushToRemote),s),a(),m(""),m(F.green(`\u2728 ${n.projectName} is ready.`)),m(""),m("Next:"),m(F.cyan(` cd ${n.projectName}`)),r.install||m(F.cyan(` ${p} install`)),m(F.cyan(` ${p==="pnpm"||p==="yarn"?`${p} dev`:`${p} run dev`}`)),m("")}catch(c){throw a(),s.current?.fail("Failed to create project."),s.current=null,await i(),c}};import Lr from"node:path";import te from"picocolors";import _t from"node:path";var Jn=/^[A-Za-z][A-Za-z0-9-]*$/,ua=/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i;function zn(e){return ua.test(e)}function D(e,t="name"){let r=e?.trim();if(!r)throw new Error(`A ${t} is required.`);if(!Jn.test(r))throw new Error(`Invalid ${t} "${e}". Use letters, digits, and hyphens only \u2014 no slashes, spaces, "..", or absolute paths.`);if(zn(r))throw new Error(`Invalid ${t} "${e}". That is a reserved name on Windows.`);return r}function Z(e,t="path"){let r=e?.trim();if(!r)throw new Error(`A ${t} is required.`);let n=r.replace(/^\.\//,"");if(n.startsWith("/")||/^[a-zA-Z]:/.test(n))throw new Error(`Invalid ${t} "${e}". Must be a relative path inside the project (no leading "/" or drive letter).`);for(let o of n.split("/")){if(!Jn.test(o))throw new Error(`Invalid ${t} "${e}". Each segment must be kebab/camel-case (letters, digits, hyphens), separated by "/" \u2014 no "..", dots, or empty segments.`);if(zn(o))throw new Error(`Invalid ${t} "${e}". Segment "${o}" is reserved on Windows.`)}return n}function le(e,...t){let r=_t.resolve(e),n=_t.resolve(r,...t),o=_t.relative(r,n);if(o===""||o===".")return n;if(o.startsWith("..")||_t.isAbsolute(o))throw new Error(`Refusing to write outside the project directory (resolved path escapes "${r}").`);return n}import{execFile as da}from"node:child_process";import{stat as fa}from"node:fs/promises";import ur from"node:path";import{promisify as ga}from"node:util";var ha=ga(da),ya=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".json",".jsonc",".css"]),wa=e=>ur.join(e,"node_modules",".bin","biome"),lt=async(e,t,r=e,n=!1)=>{let o=wa(r);if(t.length===0||!w(o))return!1;let s=n?["format","--write","--no-errors-on-unmatched",...t]:["check","--write","--no-errors-on-unmatched",...t];try{await ha(o,s,{cwd:e,maxBuffer:64*1024*1024})}catch(i){if(i.code==="ENOENT")return!1}return!0},E=async(e,t)=>{let r=[];for(let n of await A(e))if(ya.has(ur.extname(n)))try{(await fa(ur.join(e,n))).mtimeMs>=t-1e3&&r.push(n)}catch{}return r.length&&await lt(e,r),r};import{access as xa}from"node:fs/promises";import ba from"node:path";var Ce=async(e,t,r)=>{try{return await xa(ba.join(e,r,t)),!0}catch{return!1}};import{access as va,readFile as Kn}from"node:fs/promises";import dr from"node:path";var He=async e=>{try{return await va(e),!0}catch{return!1}},Wn=async e=>{try{return JSON.parse(await Kn(e,"utf-8"))}catch{return null}},I=async e=>{let t=await Wn(dr.join(e,"package.json"));if(!t)throw new Error(`Failed to detect project setup: no package.json in ${e}`);let r={...t.dependencies,...t.devDependencies},n=await Wn(dr.join(e,Ve)),o=(...h)=>dr.join(e,...h),s=await He(o("src","store")),i=!!(r["@reduxjs/toolkit"]&&r["react-redux"])&&s,a=!!r.zustand&&s&&!i,c=i?"redux":a?"zustand":"none",l=await He(o("src","lib","http","core","client.ts")),p=!!r.axios&&await He(o("src","lib","http","adapters","axios.ts")),u="none";if(l)if(!p)u="fetch";else{let h="";try{h=await Kn(o("src","lib","http","client.ts"),"utf-8")}catch{}u=h.includes("axiosAdapter")?"axios":"both"}let d=!!r["next-intl"]&&await He(o("src","i18n")),f=!!r.vitest||await He(o("vitest.config.ts")),y=r["socket.io-client"]&&await He(o("src","lib","ws"))?"present":"none";return{state:c,hasRedux:i,httpClient:u,hasI18n:d,hasTests:f,ws:y,hasQuery:!!r["@tanstack/react-query"],hasActions:!!r["next-safe-action"],record:n??void 0}};import{mkdir as $a,writeFile as ka}from"node:fs/promises";import fr from"node:path";var Xn=({pascalName:e,camelName:t})=>`import { z } from 'zod';
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
59
|
* Wire contracts for the ${t} feature. Types are inferred from the
|
|
@@ -78,7 +78,7 @@ export const update${e}InputSchema = create${e}InputSchema.partial().extend({
|
|
|
78
78
|
id: z.string(),
|
|
79
79
|
});
|
|
80
80
|
export type Update${e}Input = z.infer<typeof update${e}InputSchema>;
|
|
81
|
-
`,
|
|
81
|
+
`,Yn=({kebabName:e,camelName:t})=>`/**
|
|
82
82
|
* Query keys owned by the feature. Everything under \`${t}Keys.all\` can
|
|
83
83
|
* be invalidated at once; narrower keys target one resource.
|
|
84
84
|
*/
|
|
@@ -87,7 +87,7 @@ export const ${t}Keys = {
|
|
|
87
87
|
list: () => [...${t}Keys.all, 'list'] as const,
|
|
88
88
|
detail: (id: string) => [...${t}Keys.all, 'detail', id] as const,
|
|
89
89
|
} as const;
|
|
90
|
-
`,
|
|
90
|
+
`,Qn=({pascalName:e,camelName:t})=>`import 'server-only';
|
|
91
91
|
|
|
92
92
|
import { AppApis } from '@/lib/config/app-apis';
|
|
93
93
|
import { serverHttp } from '@/lib/http/server';
|
|
@@ -109,7 +109,7 @@ export const list${e}s = async () =>
|
|
|
109
109
|
|
|
110
110
|
export const get${e} = async (id: string) =>
|
|
111
111
|
serverHttp.get(AppApis.${t}.detail(id), { schema: ${t}Schema });
|
|
112
|
-
`,
|
|
112
|
+
`,Zn=({pascalName:e,camelName:t})=>`'use server';
|
|
113
113
|
|
|
114
114
|
import { revalidateTag } from 'next/cache';
|
|
115
115
|
|
|
@@ -158,7 +158,7 @@ export const delete${e} = authActionClient
|
|
|
158
158
|
revalidateTag(${t.toUpperCase()}_TAG, 'max');
|
|
159
159
|
return { deleted: true as const };
|
|
160
160
|
});
|
|
161
|
-
`,
|
|
161
|
+
`,eo=({pascalName:e,camelName:t})=>`import { queryOptions, useSuspenseQuery } from '@tanstack/react-query';
|
|
162
162
|
|
|
163
163
|
import { AppApis } from '@/lib/config/app-apis';
|
|
164
164
|
import { http } from '@/lib/http';
|
|
@@ -187,26 +187,26 @@ export const ${t}Query = (id: string) =>
|
|
|
187
187
|
|
|
188
188
|
export const use${e}List = () => useSuspenseQuery(${t}ListQuery());
|
|
189
189
|
export const use${e} = (id: string) => useSuspenseQuery(${t}Query(id));
|
|
190
|
-
`,
|
|
190
|
+
`,to=({camelName:e,kebabName:t})=>` ${e}: {
|
|
191
191
|
list: '/${t}',
|
|
192
192
|
detail: (id: string) => \`/${t}/\${id}\`,
|
|
193
193
|
create: '/${t}',
|
|
194
194
|
update: (id: string) => \`/${t}/\${id}\`,
|
|
195
195
|
delete: (id: string) => \`/${t}/\${id}\`,
|
|
196
|
-
},`;var
|
|
197
|
-
`)},
|
|
198
|
-
`;var
|
|
199
|
-
`);let a=
|
|
200
|
-
`;t.includes(r.trim())||(t+=r,await
|
|
196
|
+
},`;var mt=async e=>{let{name:t,featurePath:r,withActions:n}=e,o={kebabName:t,camelName:V(t),pascalName:k(t)},s=fr.join(r,"api");await $a(s,{recursive:!0});let i=[["schema.ts",Xn(o)],["keys.ts",Yn(o)],["server.ts",Qn(o)],["queries.ts",eo(o)]];return n&&i.push(["actions.ts",Zn(o)]),await Promise.all(i.map(([a,c])=>ka(fr.join(s,a),c))),i.map(([a])=>fr.join("api",a))};import{mkdir as no,writeFile as hr}from"node:fs/promises";import Oe from"node:path";var gr=e=>{let{componentName:t,isClient:r,hasI18n:n}=e,o=e.translationNamespace||t,s=[];return(r||n)&&s.push("'use client';"),n&&s.push("import { useTranslations } from 'next-intl';"),s.push(""),s.push(`export function ${t}() {`),n?(s.push(` const t = useTranslations('${o}');`),s.push(""),s.push(" return ("),s.push(" <div>"),s.push(" <h2>{t('title')}</h2>"),s.push(" </div>"),s.push(" );")):(s.push(" return ("),s.push(" <div>"),s.push(` <h2>${t}</h2>`),s.push(" </div>"),s.push(" );")),s.push("}"),s.push(""),s.join(`
|
|
197
|
+
`)},ro=e=>`export { ${e.componentName} } from './${e.componentName}';
|
|
198
|
+
`;var yr=async e=>{let{name:t,projectPath:r,isClient:n,hasI18n:o,featurePath:s}=e,i=k(t);s?await Pa(i,r,s,n,o):await Sa(i,t,r,n,o)},Pa=async(e,t,r,n,o)=>{let s=Oe.join(t,r,"components");await no(s,{recursive:!0}),await hr(Oe.join(s,`${e}.tsx`),gr({componentName:e,isClient:n,hasI18n:o}))},Sa=async(e,t,r,n,o)=>{let s=Oe.join(r,"src","components","common",e);await no(s,{recursive:!0}),await hr(Oe.join(s,`${e}.tsx`),gr({componentName:e,isClient:n,hasI18n:o})),await hr(Oe.join(s,"index.ts"),ro({componentName:e}));let i=Oe.join(r,"src","components","common","index.ts");await Ea(i,`export { ${e} } from './${e}';
|
|
199
|
+
`);let a=Oe.join(r,"src","components","index.ts");await Ta(a)},Ea=async(e,t)=>{let r="";w(e)&&(r=await C(e)),r.includes(t.trim())||(r+=t,await j(e,r))},Ta=async e=>{let t="";w(e)&&(t=await C(e));let r=`export * from './common';
|
|
200
|
+
`;t.includes(r.trim())||(t+=r,await j(e,t))};import{mkdir as br,writeFile as Je}from"node:fs/promises";import ee from"node:path";import{mkdir as ja,writeFile as Ae}from"node:fs/promises";import Ie from"node:path";var xr=e=>{let{sliceName:t,withPersist:r,state:n="redux"}=e;return n==="zustand"?`export * from './${t}.store';
|
|
201
201
|
`:`export * from './${t}.selectors';
|
|
202
202
|
export * from './${t}.slice';${r?`
|
|
203
203
|
export * from './persist';`:""}
|
|
204
|
-
`},
|
|
204
|
+
`},oo=e=>{let{featureName:t,componentName:r,hookName:n,pascalName:o,withStore:s,withApi:i}=e,a=["/**"," * Client-safe public surface. Server-only pieces live in `./server`."," */"];return i&&(a.push(`export { create${o}, delete${o}, update${o} } from './api/actions';`),a.push(`export { ${wr(t)}Keys } from './api/keys';`),a.push(`export { ${wr(t)}ListQuery, ${wr(t)}Query, use${o}, use${o}List } from './api/queries';`),a.push("export * from './api/schema';")),a.push(`export { ${r} } from './components/${r}';`),i||a.push(`export { ${n} } from './hooks/${n}';`),a.push(`export * from './types/${t}.types';`),s&&a.push("export * from './store';"),`${a.join(`
|
|
205
205
|
`)}
|
|
206
|
-
`},
|
|
206
|
+
`},so=e=>`import 'server-only';
|
|
207
207
|
|
|
208
208
|
export { get${e.pascalName}, list${e.pascalName}s, ${e.pascalName.toUpperCase()}_TAG } from './api/server';
|
|
209
|
-
`,
|
|
209
|
+
`,wr=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase());var io=e=>{let{componentName:t,camelName:r,typesImportPath:n}=e,o=`${r.replace(/[A-Z]/g,s=>`_${s}`).toUpperCase()}_PERSIST_VERSION`;return`import { definePersistence } from '@/store/persistence';
|
|
210
210
|
|
|
211
211
|
import type { ${t}State } from '${n}';
|
|
212
212
|
|
|
@@ -224,12 +224,12 @@ export const ${r}Persistence = definePersistence<${t}State>({
|
|
|
224
224
|
// 2: (previous) => ({ ...(previous as ${t}State), newField: 0 }),
|
|
225
225
|
},
|
|
226
226
|
});
|
|
227
|
-
`};var
|
|
227
|
+
`};var ao=e=>{let{componentName:t,camelName:r}=e;return`import type { RootState } from '@/store/rootReducer';
|
|
228
228
|
|
|
229
229
|
export const select${t}State = (state: RootState) => state.${r};
|
|
230
230
|
export const select${t}Status = (state: RootState) => state.${r}.status;
|
|
231
231
|
export const select${t}Error = (state: RootState) => state.${r}.error;
|
|
232
|
-
`};var
|
|
232
|
+
`};var co=e=>{let{componentName:t,camelName:r,typesImportPath:n}=e;return`import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
|
|
233
233
|
|
|
234
234
|
import type { ${t}State } from '${n}';
|
|
235
235
|
|
|
@@ -256,7 +256,7 @@ export const ${r}Slice = createSlice({
|
|
|
256
256
|
|
|
257
257
|
export const { started, failed, reset } = ${r}Slice.actions;
|
|
258
258
|
export const ${r}Reducer = ${r}Slice.reducer;
|
|
259
|
-
`};var
|
|
259
|
+
`};var Dt=e=>{let{componentName:t,withStore:r}=e;return r?`export type ${t}Status = 'idle' | 'loading' | 'error';
|
|
260
260
|
|
|
261
261
|
export interface ${t}State {
|
|
262
262
|
status: ${t}Status;
|
|
@@ -266,7 +266,7 @@ export interface ${t}State {
|
|
|
266
266
|
/** Extra classes for the root element. Add this feature's own props here. */
|
|
267
267
|
className?: string;
|
|
268
268
|
}
|
|
269
|
-
`};var
|
|
269
|
+
`};var po=e=>{let{componentName:t,camelName:r,typesImportPath:n}=e;return`import type { StateCreator } from 'zustand';
|
|
270
270
|
|
|
271
271
|
import type { ${t}State } from '${n}';
|
|
272
272
|
|
|
@@ -293,7 +293,7 @@ export const create${t}Slice: StateCreator<any, [], [], ${t}Slice> = (set) => ({
|
|
|
293
293
|
${r}Failed: (error: string) => set({ ${r}: { status: 'error', error } }),
|
|
294
294
|
${r}Reset: () => set({ ${r}: initial }),
|
|
295
295
|
});
|
|
296
|
-
`};var
|
|
296
|
+
`};var ut=async e=>{let{name:t,outputPath:r,persist:n,state:o="redux",typesImportPath:s=`./${t}.types`}=e;await ja(r,{recursive:!0});let i=k(t),a=V(t),c=[];s===`./${t}.types`&&c.push(Ae(Ie.join(r,`${t}.types.ts`),Dt({componentName:i,withStore:!0}))),o==="zustand"?c.push(Ae(Ie.join(r,`${t}.store.ts`),po({componentName:i,camelName:a,typesImportPath:s})),Ae(Ie.join(r,"index.ts"),xr({sliceName:t,withPersist:!1,state:o}))):(c.push(Ae(Ie.join(r,`${t}.slice.ts`),co({componentName:i,camelName:a,typesImportPath:s})),Ae(Ie.join(r,`${t}.selectors.ts`),ao({componentName:i,camelName:a,sliceName:t})),Ae(Ie.join(r,"index.ts"),xr({sliceName:t,withPersist:n,state:o}))),n&&c.push(Ae(Ie.join(r,"persist.ts"),io({componentName:i,camelName:a,typesImportPath:s})))),await Promise.all(c)};var lo=e=>{let{componentName:t,hookName:r,withApi:n,pascalName:o,hasI18n:s}=e,i=s?`import { useTranslations } from 'next-intl';
|
|
297
297
|
|
|
298
298
|
`:"",a=s?` const t = useTranslations('${o}');
|
|
299
299
|
`:"",c=s?"{t('title')}":t;return n?`'use client';
|
|
@@ -301,7 +301,7 @@ export const create${t}Slice: StateCreator<any, [], [], ${t}Slice> = (set) => ({
|
|
|
301
301
|
${i}import { use${o}List } from '../api/queries';
|
|
302
302
|
|
|
303
303
|
/**
|
|
304
|
-
* Renders hydrated query data: prefetch \`${
|
|
304
|
+
* Renders hydrated query data: prefetch \`${Ra(o)}ListQuery()\` in the
|
|
305
305
|
* page and wrap this component in \`<HydrateQueries>\` for a no-spinner first paint.
|
|
306
306
|
*/
|
|
307
307
|
export function ${t}() {
|
|
@@ -332,16 +332,16 @@ ${a} const state = ${r}();
|
|
|
332
332
|
</section>
|
|
333
333
|
);
|
|
334
334
|
}
|
|
335
|
-
`},
|
|
335
|
+
`},Ra=e=>e.charAt(0).toLowerCase()+e.slice(1);var mo=e=>{let{hookName:t,componentName:r,featureName:n,state:o}=e;return o==="zustand"?`'use client';
|
|
336
336
|
|
|
337
337
|
import { useAppStore } from '@/store/hooks';
|
|
338
338
|
|
|
339
339
|
export const ${t} = () => {
|
|
340
|
-
const status = useAppStore((state) => state.${
|
|
341
|
-
const error = useAppStore((state) => state.${
|
|
342
|
-
const start = useAppStore((state) => state.${
|
|
343
|
-
const fail = useAppStore((state) => state.${
|
|
344
|
-
const reset = useAppStore((state) => state.${
|
|
340
|
+
const status = useAppStore((state) => state.${dt(n)}.status);
|
|
341
|
+
const error = useAppStore((state) => state.${dt(n)}.error);
|
|
342
|
+
const start = useAppStore((state) => state.${dt(n)}Started);
|
|
343
|
+
const fail = useAppStore((state) => state.${dt(n)}Failed);
|
|
344
|
+
const reset = useAppStore((state) => state.${dt(n)}Reset);
|
|
345
345
|
|
|
346
346
|
return { status, error, start, fail, reset } as const;
|
|
347
347
|
};
|
|
@@ -365,7 +365,7 @@ export const ${t} = () => {
|
|
|
365
365
|
reset: () => dispatch(reset()),
|
|
366
366
|
} as const;
|
|
367
367
|
};
|
|
368
|
-
`},
|
|
368
|
+
`},dt=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase()),Lt=e=>{let{hookName:t}=e;return`'use client';
|
|
369
369
|
|
|
370
370
|
import { useCallback, useState } from 'react';
|
|
371
371
|
|
|
@@ -383,7 +383,7 @@ export const ${t} = () => {
|
|
|
383
383
|
|
|
384
384
|
return { busy, run } as const;
|
|
385
385
|
};
|
|
386
|
-
`};var
|
|
386
|
+
`};var Ut=e=>{let{componentName:t,sourceImportPath:r,testUtilsImportPath:n,hasState:o,hasI18n:s,withQueryData:i}=e,a=[];if(s&&a.push("messages: {}"),o&&a.push("preloadedState: {}"),i)return`import { describe, expect, it } from 'vitest';
|
|
387
387
|
|
|
388
388
|
import { makeTestQueryClient, renderWithProviders, screen } from '${n}';
|
|
389
389
|
import { ${i.keys} } from '${i.keysImport}';
|
|
@@ -410,7 +410,7 @@ describe('${t}', () => {
|
|
|
410
410
|
expect(screen.getByRole('heading')).toBeInTheDocument();
|
|
411
411
|
});
|
|
412
412
|
});
|
|
413
|
-
`},
|
|
413
|
+
`},uo=e=>{let{hookName:t,sourceImportPath:r,withStore:n,testUtilsImportPath:o}=e;return n?`// @vitest-environment jsdom
|
|
414
414
|
import { renderHook } from '@testing-library/react';
|
|
415
415
|
import { describe, expect, it } from 'vitest';
|
|
416
416
|
|
|
@@ -435,7 +435,7 @@ describe('${t}', () => {
|
|
|
435
435
|
expect(result.current).toBeDefined();
|
|
436
436
|
});
|
|
437
437
|
});
|
|
438
|
-
`},
|
|
438
|
+
`},fo=e=>{let{camelName:t,sourceImportPath:r}=e,n=`${t}Slice`;return`import { describe, expect, it } from 'vitest';
|
|
439
439
|
|
|
440
440
|
import { failed, reset, ${n}, started } from '${r}';
|
|
441
441
|
|
|
@@ -457,7 +457,7 @@ describe('${n}', () => {
|
|
|
457
457
|
expect(${n}.reducer({ status: 'error', error: 'boom' }, reset())).toEqual(initialState);
|
|
458
458
|
});
|
|
459
459
|
});
|
|
460
|
-
`};var
|
|
460
|
+
`};var vr=async e=>{let{name:t,outputPath:r,createStore:n,persistStore:o,createApi:s,state:i,hasI18n:a,hasTests:c}=e,l=k(t),p=s?`${l}List`:l,u=`use${l}`,d=n&&i!=="none";await br(ee.join(r,"components"),{recursive:!0}),await br(ee.join(r,"types"),{recursive:!0}),s||await br(ee.join(r,"hooks"),{recursive:!0});let f=[Je(ee.join(r,"components",`${p}.tsx`),lo({componentName:p,hookName:u,withApi:s,pascalName:l,hasI18n:a})),Je(ee.join(r,"types",`${t}.types.ts`),Dt({componentName:l,withStore:d})),Je(ee.join(r,"index.ts"),oo({featureName:t,componentName:p,hookName:u,pascalName:l,withStore:d,withApi:s}))];s||f.push(Je(ee.join(r,"hooks",`${u}.ts`),d?mo({hookName:u,componentName:l,featureName:t,state:i==="zustand"?"zustand":"redux"}):Lt({hookName:u}))),s&&(f.push(mt({name:t,featurePath:r,withActions:!0})),f.push(Je(ee.join(r,"server.ts"),so({pascalName:l})))),c&&f.push(Je(ee.join(r,"components",`${p}.test.tsx`),Ut({componentName:p,sourceImportPath:`./${p}`,testUtilsImportPath:"../../../../test/test-utils",hasState:i!=="none",hasI18n:a,withQueryData:s?{keysImport:"../api/keys",keys:`${Ca(t)}Keys`,key:"list"}:void 0}))),await Promise.all(f),d&&await ut({name:t,outputPath:ee.join(r,"store"),persist:o,state:i==="zustand"?"zustand":"redux",typesImportPath:`../types/${t}.types`})},Ca=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase());import{mkdir as Oa,writeFile as Aa}from"node:fs/promises";import $r from"node:path";var kr=async e=>{let{name:t,projectPath:r,featurePath:n}=e,o=`use${k(t)}`,s=n?$r.join(r,n,"hooks"):$r.join(r,"src","hooks");await Oa(s,{recursive:!0}),await Aa($r.join(s,`${o}.ts`),Lt({hookName:o}))};import{mkdir as Ia}from"node:fs/promises";import Bt from"node:path";var go=({componentName:e})=>`type Props = {
|
|
461
461
|
children: React.ReactNode;
|
|
462
462
|
};
|
|
463
463
|
|
|
@@ -466,16 +466,16 @@ describe('${n}', () => {
|
|
|
466
466
|
export default function ${e}({ children }: Props) {
|
|
467
467
|
return <>{children}</>;
|
|
468
468
|
}
|
|
469
|
-
`;var
|
|
470
|
-
`)}},
|
|
469
|
+
`;var Na=/^[a-z][a-z0-9-]*$/,Fa=/^[a-z][a-z0-9-]*(\/[a-z][a-z0-9-]*)*$/,Ma=e=>{if(!Na.test(e))throw new Error(`Invalid layout segment "${e}". Use kebab-case letters, digits, and hyphens. For route groups pass --group instead of wrapping in parens.`)},_a=e=>{if(!Fa.test(e))throw new Error(`Invalid --at path "${e}". Use kebab-case segments separated by "/", e.g. "dashboard/settings".`)},Pr=async e=>{let{segment:t,at:r,projectPath:n,hasI18n:o,withGroup:s}=e;Ma(t),r&&_a(r);let i=`${k(t)}Layout`,a=s?`(${t})`:t,c=["src","app"];o&&c.push("[locale]"),r&&c.push(...r.split("/")),c.push(a);let l=Bt.join(n,...c),p=Bt.join(l,"layout.tsx");if(w(p))throw new Error(`Layout already exists at ${Bt.relative(n,p)}.`);return await Ia(l,{recursive:!0}),await j(p,go({componentName:i,hasI18n:o})),{layoutFile:p,displayPath:Bt.relative(n,p),componentName:i}};import{copyFile as Da,readFile as La,writeFile as Ua}from"node:fs/promises";import ft from"node:path";var Sr=async e=>{let{code:t,name:r,country:n,flag:o,projectPath:s,copyTranslations:i}=e;await Ba(s,t,i),await Ga(s,t),await Va(s,t,r,n,o)},Ba=async(e,t,r)=>{let n=ft.join(e,"src","i18n","translations"),o=ft.join(n,"en.json"),s=ft.join(n,`${t}.json`);if(r)await Da(o,s);else{let i=await La(o,"utf-8"),a=JSON.parse(i),c=ho(a);await Ua(s,`${JSON.stringify(c,null,2)}
|
|
470
|
+
`)}},ho=e=>{let t={};for(let[r,n]of Object.entries(e))typeof n=="object"&&n!==null?t[r]=ho(n):t[r]="";return t},Ga=async(e,t)=>{let r=ft.join(e,"src","types","i18n.ts");if(!w(r))return;let n=await C(r),o=/export const SUPPORTED_LOCALES = \[([^\]]*)\] as const;/,s=n.match(o);if(s){if(s[1].includes(`'${t}'`))return;let c=s[1].split(",").map(l=>l.trim()).filter(Boolean);c.push(`'${t}'`),n=n.replace(o,`export const SUPPORTED_LOCALES = [${c.join(", ")}] as const;`),await j(r,n);return}let i=/export type SupportedLocale = (.*);/,a=n.match(i);a&&!a[1].includes(`'${t}'`)&&(n=n.replace(i,`export type SupportedLocale = ${a[1]} | '${t}';`),await j(r,n))},Va=async(e,t,r,n,o)=>{let s=ft.join(e,"src","lib","config","app-locales.ts");if(!w(s))return;let i=await C(s);if(i.includes(`locale: '${t}'`))return;let a=i.lastIndexOf("];");if(a===-1)return;let c=` {
|
|
471
471
|
name: '${r}',
|
|
472
472
|
locale: '${t}',
|
|
473
473
|
flag: '${o}',
|
|
474
474
|
country: '${n}',
|
|
475
475
|
ogLocale: '${t}_${t.toUpperCase()}',
|
|
476
476
|
},
|
|
477
|
-
`;i=i.slice(0,a)+c+i.slice(a),await
|
|
478
|
-
async function ${e}${
|
|
477
|
+
`;i=i.slice(0,a)+c+i.slice(a),await j(s,i)};import{mkdir as Ha,writeFile as Tr}from"node:fs/promises";import Gt from"node:path";var qa=({routePath:e,hasI18n:t,paramName:r})=>`${t?"/[locale]":""}${e}${r?`/[${r}]`:""}`,Er=e=>e.replace(/[[\]]/g,"").split(/[-_\s]+/).filter(Boolean).map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(""),yo=(e,t,r)=>`
|
|
478
|
+
async function ${e}${Er(t)}({
|
|
479
479
|
params,
|
|
480
480
|
}: {
|
|
481
481
|
params: PageProps<'${r}'>['params'];
|
|
@@ -484,7 +484,7 @@ async function ${e}${kr(t)}({
|
|
|
484
484
|
|
|
485
485
|
return <p className="text-sm">${t}: {${t}}</p>;
|
|
486
486
|
}
|
|
487
|
-
`,
|
|
487
|
+
`,wo=e=>{let{componentName:t,routePath:r,hasI18n:n,paramName:o}=e,s=qa(e);return n?`import type { Metadata } from 'next';
|
|
488
488
|
import { getLocale, getTranslations } from 'next-intl/server';${o?`
|
|
489
489
|
import { Suspense } from 'react';`:""}
|
|
490
490
|
|
|
@@ -508,12 +508,12 @@ export default async function ${t}Page(${o?`{ params }: PageProps<'${s}'>`:""})
|
|
|
508
508
|
<h1 className="font-bold text-2xl">{t('title')}</h1>
|
|
509
509
|
<p className="text-gray-500 dark:text-gray-400">{t('description')}</p>${o?`
|
|
510
510
|
<Suspense fallback={<p className="text-sm">\u2026</p>}>
|
|
511
|
-
<${t}${
|
|
511
|
+
<${t}${Er(o)} params={params} />
|
|
512
512
|
</Suspense>`:""}
|
|
513
513
|
</div>
|
|
514
514
|
);
|
|
515
515
|
}
|
|
516
|
-
${o?
|
|
516
|
+
${o?yo(t,o,s):""}`:`import type { Metadata } from 'next';${o?`
|
|
517
517
|
import { Suspense } from 'react';`:""}
|
|
518
518
|
|
|
519
519
|
import { generateSEOMetadata } from '@/lib/config/seo';
|
|
@@ -529,19 +529,19 @@ export default function ${t}Page(${o?`{ params }: PageProps<'${s}'>`:""}) {
|
|
|
529
529
|
<div className="flex flex-col gap-2 p-6">
|
|
530
530
|
<h1 className="font-bold text-2xl">${t}</h1>${o?`
|
|
531
531
|
<Suspense fallback={<p className="text-sm">\u2026</p>}>
|
|
532
|
-
<${t}${
|
|
532
|
+
<${t}${Er(o)} params={params} />
|
|
533
533
|
</Suspense>`:""}
|
|
534
534
|
</div>
|
|
535
535
|
);
|
|
536
536
|
}
|
|
537
|
-
${o?
|
|
537
|
+
${o?yo(t,o,s):""}`},xo=e=>`export default function ${e.componentName}Loading() {
|
|
538
538
|
return (
|
|
539
539
|
<div className="flex h-full items-center justify-center p-6">
|
|
540
540
|
<p className="text-gray-500 text-sm dark:text-gray-400">Loading\u2026</p>
|
|
541
541
|
</div>
|
|
542
542
|
);
|
|
543
543
|
}
|
|
544
|
-
`,
|
|
544
|
+
`,bo=e=>`'use client';
|
|
545
545
|
|
|
546
546
|
import { useEffect } from 'react';
|
|
547
547
|
|
|
@@ -571,7 +571,7 @@ export default function ${e.componentName}Error({
|
|
|
571
571
|
</div>
|
|
572
572
|
);
|
|
573
573
|
}
|
|
574
|
-
`;var
|
|
574
|
+
`;var jr=async e=>{let{name:t,projectPath:r,hasI18n:n,group:o,dynamic:s,withLoading:i=!1,withError:a=!1}=e,c=k(t),l=`/${t}`,p=[r,"src","app"];n&&p.push("[locale]"),o&&p.push(`(${o})`),p.push(t),s&&p.push(`[${s}]`);let u=Gt.join(...p);return await Ha(u,{recursive:!0}),await Tr(Gt.join(u,"page.tsx"),wo({componentName:c,routePath:l,hasI18n:n,paramName:s})),i&&await Tr(Gt.join(u,"loading.tsx"),xo({componentName:c})),a&&await Tr(Gt.join(u,"error.tsx"),bo({componentName:c})),{pageDir:u,routePath:s?`${l}/[${s}]`:l}};import{mkdir as Ja}from"node:fs/promises";import Rr from"node:path";var vo=(e,t)=>e.endsWith(t)?e:`${e}${t}`,$o=({baseName:e})=>{let t=vo(e,"Provider"),r=t.replace(/Provider$/,""),n=`${r}ContextValue`,o=`${r}Context`,s=`use${r}`;return`'use client';
|
|
575
575
|
import { createContext, useContext, useMemo } from 'react';
|
|
576
576
|
|
|
577
577
|
type ${n} = {
|
|
@@ -593,139 +593,139 @@ export const ${t} = ({ children }: { children: React.ReactNode }) => {
|
|
|
593
593
|
const value = useMemo<${n}>(() => ({ ready: true }), []);
|
|
594
594
|
return <${o}.Provider value={value}>{children}</${o}.Provider>;
|
|
595
595
|
};
|
|
596
|
-
`},
|
|
596
|
+
`},ko=e=>vo(e,"Provider");var za=/^[A-Za-z][A-Za-z0-9-]*$/,Wa=e=>e.includes("-")?k(e):e.charAt(0).toUpperCase()+e.slice(1),Cr=async e=>{let{name:t,projectPath:r}=e;if(!za.test(t))throw new Error(`Invalid provider name "${t}". Use kebab-case or PascalCase letters and digits only.`);let n=Wa(t).replace(/Provider$/,""),o=ko(n),s=o,i=Rr.join(r,O.PROVIDERS),a=Rr.join(i,`${s}.tsx`);if(w(a))throw new Error(`Provider already exists at ${Rr.relative(r,a)}.`);return await Ja(i,{recursive:!0}),await j(a,$o({baseName:n})),{componentName:o,fileBaseName:s,providerFile:a}};import gt from"node:path";var me=async e=>{let{projectPath:t,sourceFile:r,kind:n,symbolName:o}=e,s=gt.dirname(r),i=gt.basename(r).replace(/\.(tsx?|jsx?)$/i,""),a=n==="component"?".tsx":".ts",c=gt.join(s,`${i}.test${a}`),l=`./${i}`,p=Ka(s,gt.join(t,"test","test-utils")),u;return n==="component"?u=Ut({componentName:o,sourceImportPath:l,testUtilsImportPath:p,hasState:!!e.hasState,hasI18n:!!e.hasI18n}):n==="hook"?u=uo({hookName:o,sourceImportPath:l,withStore:!!e.hookUsesStore,testUtilsImportPath:e.hookUsesStore?p:void 0}):u=fo({camelName:o,sourceImportPath:l}),await j(c,u),c},Ka=(e,t)=>{let r=gt.relative(e,t).replace(/\\/g,"/");return r.startsWith(".")?r:`./${r}`};import{readFile as Xa,writeFile as Ya}from"node:fs/promises";import Qa from"node:path";var ze=(e,t)=>{let r=/import\s+.*\s+from\s+['"].*['"];?\n/g,n=e.match(r);if(n&&n.length>0){let o=n[n.length-1],s=e.lastIndexOf(o);return e.slice(0,s+o.length)+t+`
|
|
597
597
|
`+e.slice(s+o.length)}return`${t}
|
|
598
|
-
${e}`},
|
|
598
|
+
${e}`},Or=(e,t)=>{let r=/combineSlices\(([\s\S]*?)\)/,n=e.match(r);if(!n)throw new Error("Could not find combineSlices in rootReducer.ts");let o=n[1].split(",").map(c=>c.trim()).filter(Boolean);if(o.includes(t))return e;let s=o.indexOf("persistSlice");s===-1?o.push(t):o.splice(s,0,t);let i=`combineSlices(${o.join(", ")})`,a=`combineSlices(
|
|
599
599
|
${o.join(`,
|
|
600
600
|
`)},
|
|
601
|
-
)`;return e.replace(r,i.length<=100?i:a)},
|
|
601
|
+
)`;return e.replace(r,i.length<=100?i:a)},Ar=(e,t)=>{let r=/entries:\s*\[([\s\S]*?)\]/,n=e.match(r);if(!n)throw new Error("Could not find `entries: [...]` in src/store/index.ts");let o=n[1].split(",").map(s=>s.trim()).filter(Boolean);return o.includes(t)?e:(o.push(t),e.replace(r,`entries: [${o.join(", ")}]`))},Ir=(e,t)=>{let r=e.lastIndexOf("} as const;");if(r===-1)throw new Error("Could not find closing brace of AppApis object");let n=e.substring(0,r),o=e.substring(r);return`${n.trimEnd()}
|
|
602
602
|
${t}
|
|
603
|
-
${o}`};var
|
|
604
|
-
`;o=o.slice(0,i)+a+o.slice(i),await
|
|
605
|
-
.describe('${s}')`}return` ${t}: ${r},`},
|
|
603
|
+
${o}`};var ht=async e=>{let{serviceName:t,projectPath:r}=e,n=V(t),o=Qa.join(r,"src","lib","config","app-apis.ts");try{let s=await Xa(o,"utf-8");if(s.includes(`${n}:`))return;if(!s.match(/export const AppApis = \{[\s\S]*?\} as const;/))throw new Error("Could not find AppApis object in app-apis.ts");s=Ir(s,to({kebabName:t,camelName:n,pascalName:k(t)})),await Ya(o,s)}catch(s){throw new Error(`Failed to register API endpoints: ${s}`,{cause:s})}};import{readFile as Za,writeFile as ec}from"node:fs/promises";import tc from"node:path";var Nr=async(e,t,r)=>{let n=tc.join(e,"src","lib","config","app-paths.ts");if(!w(n))return;let o=await Za(n,"utf-8"),s=V(t);if(o.includes(`${s}:`))return;let i=o.lastIndexOf("} as const;");if(i===-1)return;let a=` ${s}: '${r}',
|
|
604
|
+
`;o=o.slice(0,i)+a+o.slice(i),await ec(n,o)};import Fr from"node:path";var rc=/^[A-Z][A-Z0-9_]*$/,Eo=e=>{if(!rc.test(e))throw new Error(`Invalid env var name "${e}". Use UPPER_SNAKE_CASE (e.g. SENTRY_DSN, NEXT_PUBLIC_API_URL).`)},ue=(e,t)=>t?e.startsWith("NEXT_PUBLIC_")?e:`NEXT_PUBLIC_${e}`:e,Mr=e=>e.public?"client":"server",nc=e=>{if(e.default===void 0)return"";switch(e.type){case"number":return`.default(${Number(e.default)})`;case"boolean":return`.default(${e.default==="true"})`;default:return`.default('${e.default.replace(/'/g,"\\'")}')`}},oc=e=>{switch(e.type){case"url":return"e.url()";case"number":return"e.number()";case"boolean":return"e.boolean()";case"enum":{let t=e.enumValues??[];if(t.length===0)throw new Error(`Enum env var "${e.name}" requires --enum values.`);return`e.enum([${t.map(n=>`'${n.replace(/'/g,"\\'")}'`).join(", ")}])`}default:return"e.string()"}},To=e=>{let t=ue(e.name,e.public),r=oc(e),n=e.default!==void 0,o=!e.required&&!n;if(n&&(r+=nc(e)),o&&(r+=".optional()"),e.description){let s=e.description.replace(/'/g,"\\'");r+=`
|
|
605
|
+
.describe('${s}')`}return` ${t}: ${r},`},jo=e=>{let t=ue(e.name,e.public);return` ${t}: process.env.${t},`},sc=e=>new RegExp(`(${e}:\\s*\\{)([\\s\\S]*?)(\\n\\s*\\})`),Po=/(runtimeEnv:\s*\{)([\s\S]*?)(\n\s*\})/,So=(e,t,r,n)=>{let o=t.replace(/\s+$/,""),s=o.length===0||/,\s*$/.test(o)?o:`${o},`;return`${e}${s}
|
|
606
606
|
${n}
|
|
607
|
-
${r.replace(/^\n/,"")}`},
|
|
607
|
+
${r.replace(/^\n/,"")}`},Ro=(e,t)=>{let r=ue(t.name,t.public),n=Mr(t);if(new RegExp(`\\b${r}\\s*:`).test(e))return e;if(!/defineEnv\(\{/.test(e))throw new Error("Could not locate `defineEnv({ \u2026 })` in src/lib/env/index.ts. Is this a @teispace/env project?");let s=sc(n),i=e.match(s);if(!i)throw new Error(`Could not locate the \`${n}: { \u2026 }\` group in src/lib/env/index.ts.`);let[,a,c,l]=i,p=To(t),u=e.replace(s,So(a,c,l,p)),d=u.match(Po);if(!d)throw new Error("Could not locate the `runtimeEnv: { \u2026 }` map in src/lib/env/index.ts.");let[,f,y,h]=d;return u=u.replace(Po,So(f,y,h,jo(t))),u},ic="# -public",ac=e=>{let t=ue(e.name,e.public),r=e.default??"",n=e.public?` ${ic}`:"";return`${t}=${r}${n}`},Co=(e,t)=>{let r=ue(t.name,t.public);if(new RegExp(`^${r}=`,"m").test(e))return e;let o=e===""||e.endsWith(`
|
|
608
608
|
`)?"":`
|
|
609
609
|
`,s=e===""?"":`
|
|
610
610
|
`,i=t.description?`# ${t.description}
|
|
611
|
-
`:"";return`${e}${o}${s}${i}${
|
|
612
|
-
`},
|
|
611
|
+
`:"";return`${e}${o}${s}${i}${ac(t)}
|
|
612
|
+
`},Oo=(e,t)=>{let r=ue(t.name,t.public);if(new RegExp(`^${r}=`,"m").test(e))return e;let o=e===""||e.endsWith(`
|
|
613
613
|
`)?"":`
|
|
614
614
|
`,s=t.default??"";return`${e}${o}${r}=${s}
|
|
615
|
-
`},
|
|
615
|
+
`},_r=async(e,t)=>{Eo(t.name);let r=Fr.join(e,"src/lib/env/index.ts");if(!w(r))throw new Error("src/lib/env/index.ts not found \u2014 run `next-maker init` first.");let n=await C(r),o=Ro(n,t),s=o!==n;s&&await j(r,o);let i=Fr.join(e,O.ENV_EXAMPLE),a=!1;if(w(i)){let p=await C(i),u=Co(p,t);a=u!==p,a&&await j(i,u)}let c=Fr.join(e,".env"),l=!1;if(w(c)){let p=await C(c),u=Oo(p,t);l=u!==p,l&&await j(c,u)}return{schemaUpdated:s,envExampleUpdated:a,envUpdated:l,group:Mr(t)}};import Vt from"node:path";var cc=e=>new RegExp(`<\\s*${e}\\b`),Ao=(e,t)=>{if(cc(t).test(e))return e;let r=/^(?<indent>[ \t]*)\{children\}\s*$/m,n=e.match(r);if(!n||n.index===void 0)throw new Error("Could not locate `{children}` line in RootProvider \u2014 expected a line containing only `{children}`.");let o=n.groups?.indent??"",s=`${o} `,i=`${o}<${t}>
|
|
616
616
|
${s}{children}
|
|
617
|
-
${o}</${t}>`;return e.replace(r,i)},
|
|
617
|
+
${o}</${t}>`;return e.replace(r,i)},Io=(e,t)=>{let r=`export * from './${t}';`;if(e.includes(r))return e;let n=e.split(`
|
|
618
618
|
`),o=[],s=[];for(let u of n)/^export \* from '\.\/.*';\s*$/.test(u)?o.push(u):s.push(u);let i=[...o,r].sort(),a=n.findIndex(u=>/^export \* from '\.\//.test(u));if(a===-1)return`${r}
|
|
619
|
-
${e.replace(/^\n+/,"")}`;let c=n.slice(0,a),l=n.findIndex((u,
|
|
620
|
-
`)},
|
|
621
|
-
`))};import
|
|
619
|
+
${e.replace(/^\n+/,"")}`;let c=n.slice(0,a),l=n.findIndex((u,d)=>d>a&&!/^export \* from '\.\//.test(u)),p=l===-1?[]:n.slice(l);return[...c,...i,...p].join(`
|
|
620
|
+
`)},pc=O.PROVIDERS,lc=O.ROOT_PROVIDER,mc=O.PROVIDERS_INDEX,No=async(e,t={fileExists:w,readFile:C})=>{let r=Vt.join(e,lc);if(t.fileExists(r))return r;let n=Vt.join(e,pc);if(!t.fileExists(n))return null;let{readdir:o}=await import("node:fs/promises"),i=(await o(n).catch(()=>[])).filter(c=>c.endsWith(".tsx")),a=null;for(let c of i){let l=Vt.join(n,c),p=await t.readFile(l);if(!/\{children\}/.test(p))continue;let d=((p.split("{children}")[0]??"").match(/<[A-Z][A-Za-z0-9_]*\b/g)??[]).length;(!a||d>a.depth)&&(a={file:l,depth:d})}return a?.file??null},Dr=async e=>{let{projectPath:t,componentName:r,fileBaseName:n}=e,o=await No(t);if(o){let a=await C(o),c=a;(!/from '\.\//.test(a)||!a.includes(`{ ${r} }`))&&(c=ze(c,`import { ${r} } from './${n}';`)),c=Ao(c,r),c!==a&&await j(o,c)}let s=Vt.join(t,mc),i=null;if(w(s)){let a=await C(s),c=Io(a,n);c!==a&&await j(s,c),i=s}return{rootProviderFile:o,barrelFile:i}};import{readFile as Fo,writeFile as Mo}from"node:fs/promises";import _o from"node:path";var yt=async e=>{let{projectPath:t,name:r,persist:n,importPath:o}=e,s=_o.join(t,"src","store","rootReducer.ts"),i=_o.join(t,"src","store","index.ts"),a=V(r),c=o.replace(/^src\//,"@/");try{let l=await Fo(s,"utf-8");if(l=ze(l,`import { ${a}Slice } from '${c}';`),l=Or(l,`${a}Slice`),await Mo(s,l),n&&w(i)){let p=await Fo(i,"utf-8");p=ze(p,`import { ${a}Persistence } from '${c}';`),p=Ar(p,`${a}Persistence`),await Mo(i,p)}}catch(l){throw new Error(`Failed to register slice '${r}': ${l}`,{cause:l})}};import{readFile as uc,writeFile as dc}from"node:fs/promises";import fc from"node:path";var Ne=async(e,t)=>{let r=fc.join(e,"src","i18n","translations","en.json");if(!w(r))return;let n=await uc(r,"utf-8"),o=JSON.parse(n);o[t]||(o[t]={title:t,description:`${t} page description`},await dc(r,`${JSON.stringify(o,null,2)}
|
|
621
|
+
`))};import gc from"enquirer";var{prompt:hc}=gc,Do=async(e,t={})=>{let r=[];e||r.push({type:"input",name:"componentName",message:"Component name (kebab-case):",validate:o=>o?/^[a-z][a-z0-9-]*$/.test(o)?!0:"Use lowercase letters, numbers, and hyphens only":"Component name is required"}),t.client===void 0&&r.push({type:"confirm",name:"isClient",message:"Add 'use client' directive?",initial:!1});let n=r.length>0?await hc(r):{};return{componentName:e??n.componentName,isClient:t.client??n.isClient??!1}};var Lo=e=>{e.command("component [name]").description("Generate a shared component").option("--client","Add 'use client' directive").option("--no-client","Keep the component a Server Component").option("--i18n","Add useTranslations hook").option("--feature <path>","Generate in feature directory (e.g., src/features/auth)").option("--test","Also generate a sibling test file").option("--no-test","Skip test file generation").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();m(te.cyan(`
|
|
622
622
|
\u{1F9E9} Component Generator
|
|
623
|
-
`)),
|
|
623
|
+
`)),g.start("Detecting project setup...");let s=await I(n);g.succeed("Project setup detected");let i=await Do(t,{client:r.client});D(i.componentName,"component name"),r.feature&&Z(r.feature,"feature path");let a=k(i.componentName),c=i.isClient,l=r.i18n??!1,p=yc(r,s.hasTests);g.start("Generating component..."),await yr({name:i.componentName,projectPath:n,isClient:c||l,hasI18n:l,featurePath:r.feature}),g.succeed("Component generated"),l&&await Ne(n,a);let u=null;if(p){let f=r.feature?Lr.join(n,r.feature,"components",`${a}.tsx`):Lr.join(n,"src/components/common",a,`${a}.tsx`);g.start("Generating test..."),u=await me({projectPath:n,sourceFile:f,kind:"component",symbolName:a,hasState:s.state!=="none",hasI18n:s.hasI18n}),g.succeed("Test generated")}let d=r.feature?`${r.feature}/components/${a}.tsx`:`src/components/common/${a}/${a}.tsx`;await E(n,o),m(te.green(`
|
|
624
624
|
\u2728 Component '${a}' created successfully!
|
|
625
|
-
`)),m(
|
|
625
|
+
`)),m(te.dim(` \u{1F4C4} ${d}`)),r.feature||(m(te.dim(` \u{1F4C4} src/components/common/${a}/index.ts`)),m(te.dim(" \u{1F4DD} Updated src/components/common/index.ts")),m(te.dim(" \u{1F4DD} Updated src/components/index.ts"))),u&&m(te.dim(` \u{1F9EA} ${Lr.relative(n,u)}`)),m(""),m(te.cyan("Usage:")),r.feature?m(te.dim(` import { ${a} } from '${r.feature.replace(/^src\//,"@/")}/components/${a}';`)):m(te.dim(` import { ${a} } from '@/components';`)),m("")}catch(n){g.fail("Component generation failed"),v(`${n}`),process.exit(1)}})},yc=(e,t)=>e.test===!1?!1:e.test===!0?!0:t;import{spawnSync as wc}from"node:child_process";import L from"picocolors";var xc=e=>{switch(e.kind){case"missingFile":return`missing file: ${e.file}`;case"missingPackage":return`missing ${e.dev?"devDependency":"dependency"}: ${e.name}`;case"missingScript":return`missing script: ${e.name}`}},Uo=e=>{e.command("doctor").description("Diagnose drift between the project and the starter features it was generated with").option("--fix","Restore missing files, packages, and scripts from the starter").option("--compile","Run the project type-check as part of the diagnosis").option("--json","Print machine-readable JSON").option("--starter-path <dir>","Compare against a local starter checkout").action(async t=>{let r=process.cwd(),n;try{let o=await pe(r),s=await qe(o,{starterPath:t.starterPath});n=s.dispose;let i=await lr(r,o,s),a=0;if(t.fix&&i.some(p=>p.drift.length)){let p=await Un(r,{...o,answers:{}},s,o.answers),u=new Set(i.flatMap(y=>y.drift.filter(h=>h.kind==="missingFile").map(h=>h.file)));p.copy=p.copy.filter(y=>u.has(y)),p.remove=[],p.unwrapJsx=[],p.unwrapCall=[],p.removePackages=[],p.removeScripts=[],p.manual=[];let d=new Set(i.flatMap(y=>y.drift.filter(h=>h.kind==="missingPackage").map(h=>h.name)));for(let y of Object.keys(p.addPackages))d.has(y)||delete p.addPackages[y];for(let y of Object.keys(p.addDevPackages))d.has(y)||delete p.addDevPackages[y];let f=new Set(i.flatMap(y=>y.drift.filter(h=>h.kind==="missingScript").map(h=>h.name)));for(let y of Object.keys(p.addScripts))f.has(y)||delete p.addScripts[y];await Bn(r,s,p,o),a=p.copy.length+Object.keys(p.addPackages).length+Object.keys(p.addDevPackages).length+Object.keys(p.addScripts).length,i=await lr(r,o,s)}let c;if(t.compile){let[p,...u]=fe(o.packageManager,"type-check").split(" "),d=wc(p,u,{cwd:r,encoding:"utf-8"});c={ok:d.status===0,output:`${d.stdout}${d.stderr}`.trim()}}let l=i.filter(p=>p.on&&p.drift.length);t.json&&(m(JSON.stringify({starter:o.starter,results:i,fixed:a,compile:c},null,2)),process.exit(l.length||c?.ok===!1?1:0)),m(L.cyan(`
|
|
626
626
|
\u{1FA7A} Project Doctor
|
|
627
|
-
`)),m(
|
|
628
|
-
`));for(let p of i){if(!p.on){m(` ${
|
|
629
|
-
restored ${a} item(s) from the starter`)),c&&(m(""),m(c.ok?
|
|
627
|
+
`)),m(L.dim(` ${o.starter.name} ${o.starter.version} \xB7 ${s.source.location}
|
|
628
|
+
`));for(let p of i){if(!p.on){m(` ${L.dim("\u2014")} ${L.dim(p.id)} ${L.dim("(off)")}`);continue}if(p.drift.length===0){m(` ${L.green("\u2713")} ${p.id}`);continue}m(` ${L.yellow("!")} ${L.bold(p.id)} ${L.dim(`(${p.drift.length} issue${p.drift.length===1?"":"s"})`)}`);for(let u of p.drift)m(` ${L.yellow("\u2022")} ${xc(u)}`)}a&&m(L.green(`
|
|
629
|
+
restored ${a} item(s) from the starter`)),c&&(m(""),m(c.ok?L.green(" \u2713 type-check passed"):L.red(" \u2717 type-check failed")),c.ok||m(L.dim(c.output.split(`
|
|
630
630
|
`).slice(-30).join(`
|
|
631
|
-
`)))),m(""),l.length===0&&c?.ok!==!1&&(m(
|
|
632
|
-
`)),process.exit(0)),l.length&&!t.fix&&m(
|
|
633
|
-
`)),process.exit(1)}catch(o){
|
|
631
|
+
`)))),m(""),l.length===0&&c?.ok!==!1&&(m(L.green(`\u2713 No drift detected.
|
|
632
|
+
`)),process.exit(0)),l.length&&!t.fix&&m(L.dim(`Run with --fix to restore missing pieces from the starter.
|
|
633
|
+
`)),process.exit(1)}catch(o){v(`${o instanceof Error?o.message:o}`),process.exit(1)}finally{await n?.()}})};import We from"picocolors";var Bo=new Set(["string","url","number","boolean","enum"]),bc=e=>{let t=(e??"string").toLowerCase();if(!Bo.has(t))throw new Error(`Invalid --type "${e}". Valid: ${Array.from(Bo).join(", ")}.`);return t},vc=e=>{if(!e)return;let t=e.split(",").map(r=>r.trim()).filter(Boolean);return t.length===0?void 0:t},Go=e=>{e.command("env <NAME>").description("Declare a new environment variable across src/lib/env/index.ts, .env.example, and .env").option("--type <type>","Coercer type (string|url|number|boolean|enum)","string").option("--required","Make the variable required (no .optional() / .default())").option("--default <value>","Set a default value (mutually exclusive with --required)").option("--public","Mark as public: place in the client group, auto-prefix NEXT_PUBLIC_, tag .env.example").option("--describe <text>","Description (rendered as .describe() and .env.example comment)").option("--enum <list>",'Comma-separated values when --type=enum (e.g. "dev,prod")').action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();if(m(We.cyan(`
|
|
634
634
|
\u{1F510} Env Var Generator
|
|
635
|
-
`)),r.required&&r.default!==void 0)throw new Error("--required and --default are mutually exclusive.");let s=
|
|
635
|
+
`)),r.required&&r.default!==void 0)throw new Error("--required and --default are mutually exclusive.");let s=bc(r.type),i=vc(r.enum);if(s==="enum"&&(!i||i.length===0))throw new Error("--type=enum requires at least one value via --enum a,b,c.");let a={name:t,type:s,required:!!r.required,default:r.default,description:r.describe,enumValues:i,public:!!r.public};g.start("Updating src/lib/env/index.ts, .env.example, and .env...");let c=await _r(n,a);g.succeed("Env var written");let l=ue(a.name,a.public);await E(n,o),m(We.green(`
|
|
636
636
|
\u2728 ${l} declared in the ${c.group} group.
|
|
637
|
-
`)),m(
|
|
638
|
-
`};var
|
|
637
|
+
`)),m(We.dim("Touched:")),m(We.dim(` ${c.schemaUpdated?"\u270F\uFE0F ":"\u23ED "} src/lib/env/index.ts`)),m(We.dim(` ${c.envExampleUpdated?"\u270F\uFE0F ":"\u23ED "} .env.example`)),m(We.dim(` ${c.envUpdated?"\u270F\uFE0F ":"\u23ED "} .env`)),m("")}catch(n){g.fail("Env var declaration failed"),v(`${n instanceof Error?n.message:n}`),process.exit(1)}})};import Jt from"node:path";import Me from"picocolors";import $c from"enquirer";var{prompt:kc}=$c,Vo=async(e={})=>e.source?{source:e.source}:{source:(await kc({type:"input",name:"source",message:"Path to source image (PNG/JPG/JPEG/SVG/WebP/AVIF):",validate:r=>r?!0:"Source path is required"})).source};import{access as Pc,readFile as Sc,stat as Ec}from"node:fs/promises";import Fe from"node:path";var qo=async e=>{try{return await Pc(e),!0}catch{return!1}},Tc=async e=>{try{return(await Ec(e)).isDirectory()}catch{return!1}},qt=async e=>{let t=[Fe.join(e,"src","app"),Fe.join(e,"app")];for(let r of t)if(await Tc(r))return r;return null},jc=["next-pwa","@ducanh2912/next-pwa","@serwist/next","serwist"],Ur=async e=>{let t={isPwa:!1},r=[Fe.join(e,"public","manifest.json"),Fe.join(e,"public","manifest.webmanifest"),Fe.join(e,"public","site.webmanifest")];for(let o of r)if(await qo(o)){t.publicManifestPath=o,t.isPwa=!0;break}let n=await qt(e);if(n)for(let o of["ts","tsx","js"]){let s=Fe.join(n,`manifest.${o}`);if(await qo(s)){t.appManifestPath=s,t.isPwa=!0;break}}try{let o=JSON.parse(await Sc(Fe.join(e,"package.json"),"utf-8")),s={...o.dependencies,...o.devDependencies};for(let i of jc)if(s[i]){t.framework=i,t.isPwa=!0;break}}catch{}return t};import{mkdir as Ic,readFile as es,stat as Nc,writeFile as Fc}from"node:fs/promises";import re from"node:path";import Mc from"sharp";import Cc from"png-to-ico";import wt from"sharp";var Ho=e=>e.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<"),Br=(e,t,r)=>{switch(e){case"square":return`<svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${t}"><rect width="${t}" height="${t}" fill="white"/></svg>`;case"circle":{let n=t/2;return`<svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${t}"><circle cx="${n}" cy="${n}" r="${n}" fill="white"/></svg>`}case"rounded":{let n=Math.round(t*r/100);return`<svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${t}"><rect width="${t}" height="${t}" rx="${n}" ry="${n}" fill="white"/></svg>`}case"squircle":return Rc(t)}},Rc=e=>{let t=e,r=t*.225,n=[`M 0 ${r}`,`C 0 0 0 0 ${r} 0`,`L ${t-r} 0`,`C ${t} 0 ${t} 0 ${t} ${r}`,`L ${t} ${t-r}`,`C ${t} ${t} ${t} ${t} ${t-r} ${t}`,`L ${r} ${t}`,`C 0 ${t} 0 ${t} 0 ${t-r}`,"Z"].join(" ");return`<svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${t}"><path d="${Ho(n)}" fill="white"/></svg>`},Jo=(e,t)=>`<svg xmlns="http://www.w3.org/2000/svg" width="${e}" height="${e}"><rect width="${e}" height="${e}" fill="${Ho(t)}"/></svg>`;var Ke=async e=>{let{size:t,paddingPercent:r,bg:n,fit:o,shape:s,radiusPercent:i,quality:a,forceSquare:c,sourceBuffer:l}=e,p=Math.max(1,Math.round(t*(1-r*2/100))),u=o==="cover"?"cover":"contain",d=await wt(l,{density:384}).resize(p,p,{fit:u,background:{r:0,g:0,b:0,alpha:0}}).png().toBuffer(),f=Math.round((t-p)/2),y=n==="transparent"?{r:0,g:0,b:0,alpha:0}:await Gr(n),h=await wt({create:{width:t,height:t,channels:4,background:y}}).composite([{input:d,left:f,top:f}]).png().toBuffer();if(!c&&(s!=="square"||o==="clip")){let b=s==="square"?"rounded":s,P=Buffer.from(Br(b,t,i));h=await wt(h).composite([{input:P,blend:"dest-in"}]).png({compressionLevel:zo(a)}).toBuffer()}else h=await wt(h).png({compressionLevel:zo(a)}).toBuffer();return h},Gr=async e=>{let t=await wt(Buffer.from(Jo(1,e))).raw().toBuffer({resolveWithObject:!0}),[r,n,o,s=255]=t.data;return{r,g:n,b:o,alpha:s/255}},zo=e=>{let t=Math.round((100-e)/100*9);return Math.max(0,Math.min(9,t))},Wo=async e=>{if(e.length===0)throw new Error("No frames provided to encodeIco");return Cc(e)};import{readFile as Oc,writeFile as ig}from"node:fs/promises";var Ko=e=>e.map(({src:t,size:r})=>({src:t,sizes:`${r}x${r}`,type:"image/png",purpose:"any maskable"})),Xo=async(e,t)=>{let r=await Oc(e,"utf-8"),n=JSON.parse(r),o=Array.isArray(n.icons)?n.icons:[],s=new Map;for(let i of[...o,...t])i&&typeof i.src=="string"&&s.set(i.src,i);return n.icons=[...s.values()],`${JSON.stringify(n,null,2)}
|
|
638
|
+
`};var Yo=e=>`icons: [
|
|
639
639
|
${e.map(r=>` { src: '${r.src}', sizes: '${r.sizes}', type: '${r.type}'${r.purpose?`, purpose: '${r.purpose}'`:""} }`).join(`,
|
|
640
640
|
`)},
|
|
641
|
-
],`,
|
|
642
|
-
`;import{stat as
|
|
641
|
+
],`,Qo=(e,t,r)=>`${JSON.stringify({name:e,short_name:e,start_url:"/",display:"standalone",background_color:"#ffffff",theme_color:t,icons:r},null,2)}
|
|
642
|
+
`;import{stat as Ac}from"node:fs/promises";import Vr from"node:path";var Zo=[".png",".jpg",".jpeg",".webp",".svg",".avif"],Ht=async e=>{if(!e)throw new Error("Source image path is required (use --path).");let t=Vr.isAbsolute(e)?e:Vr.resolve(process.cwd(),e),r;try{r=await Ac(t)}catch{throw new Error(`Source image not found: ${t}`)}if(!r.isFile())throw new Error(`Source is not a file: ${t}`);let n=Vr.extname(t).toLowerCase();if(!Zo.includes(n))throw new Error(`Unsupported source format '${n}'. Use one of: ${Zo.join(", ")}`);return{absolutePath:t,ext:n,bytes:r.size,isVector:n===".svg"}};var qr=(e,t)=>{if(!e)return t;let r=e.split(",").map(n=>Number.parseInt(n.trim(),10)).filter(n=>Number.isFinite(n)&&n>0&&n<=1024);if(r.length===0)throw new Error(`Invalid --sizes value: '${e}'. Provide comma-separated positive integers.`);return r};var _c=async e=>{try{return await Nc(e),!0}catch{return!1}},Dc=async(e,t,r)=>{let n=[];for(let o of e){if(await _c(o.outPath)&&!t&&!o.alwaysWrite){n.push({path:o.outPath,bytes:o.bytes.length,skipped:!0});continue}r||(await Ic(re.dirname(o.outPath),{recursive:!0}),await Fc(o.outPath,o.bytes)),n.push({path:o.outPath,bytes:o.bytes.length})}return n},Hr=async(e,t)=>{let r=await Ht(t.source),n=await es(r.absolutePath),o=t.ogSource?await es((await Ht(t.ogSource)).absolutePath):n,s=[],i=[];for(let p of t.icoSizes)i.push(await Ke({sourceBuffer:n,size:p,shape:"square",radiusPercent:t.radius,bg:t.bg,paddingPercent:t.padding,fit:t.fit,quality:t.quality,forceSquare:!0}));let a=await Wo(i);if(s.push({outPath:re.join(t.outDir,"favicon.ico"),bytes:a}),t.emitIcon){let p=await Ke({sourceBuffer:n,size:512,shape:t.shape,radiusPercent:t.radius,bg:t.bg,paddingPercent:t.padding,fit:t.fit,quality:t.quality});s.push({outPath:re.join(t.outDir,"icon.png"),bytes:p})}if(t.emitApple){let p=t.bg==="transparent"?"#ffffff":t.bg,u=await Ke({sourceBuffer:n,size:180,shape:t.shape,radiusPercent:t.radius,bg:p,paddingPercent:t.padding,fit:t.fit,quality:t.quality});s.push({outPath:re.join(t.outDir,"apple-icon.png"),bytes:u})}if(t.emitOg){let p=await ts(o,t,!1);s.push({outPath:re.join(t.outDir,"opengraph-image.png"),bytes:p})}if(t.emitTwitter){let p=await ts(o,t,!0);s.push({outPath:re.join(t.outDir,"twitter-image.png"),bytes:p})}let c;if(t.pwa||t.pwaInit){let p=await Ur(e);if(t.pwa&&!p.isPwa)throw new Error("No PWA setup detected. Use --pwa-init to bootstrap a manifest, or set up next-pwa/serwist first.");let u=re.join(e,"public"),d=[192,512],f=[];for(let h of d){let x=await Ke({sourceBuffer:n,size:h,shape:t.shape,radiusPercent:t.radius,bg:t.bg,paddingPercent:t.padding,fit:t.fit,quality:t.quality}),b=`icon-${h}.png`;s.push({outPath:re.join(u,b),bytes:x}),f.push({src:`/${b}`,size:h})}let y=Ko(f);if(p.publicManifestPath){let h=await Xo(p.publicManifestPath,y);s.push({outPath:p.publicManifestPath,bytes:Buffer.from(h,"utf-8"),alwaysWrite:!0})}else if(p.appManifestPath)c=Yo(y);else if(t.pwaInit){let h=re.join(u,"manifest.webmanifest"),x=Qo(re.basename(e)||"App","#000000",y);s.push({outPath:h,bytes:Buffer.from(x,"utf-8")})}}return{outputs:await Dc(s,t.force,t.dryRun),pwaSnippet:c}},ts=async(e,t,r)=>{let o=r?600:630,s=Math.round(Math.min(1200,o)*(1-t.padding*2/100)),i=await Ke({sourceBuffer:e,size:s,shape:t.shape,radiusPercent:t.radius,bg:"transparent",paddingPercent:0,fit:t.fit,quality:t.quality}),a=t.bg==="transparent"?"#ffffff":t.bg,c=await Gr(a),l=Math.round((1200-s)/2),p=Math.round((o-s)/2);return Mc({create:{width:1200,height:o,channels:4,background:c}}).composite([{input:i,left:l,top:p}]).png().toBuffer()};var rs=new Set(["aliceblue","antiquewhite","aqua","aquamarine","azure","beige","bisque","black","blanchedalmond","blue","blueviolet","brown","burlywood","cadetblue","chartreuse","chocolate","coral","cornflowerblue","cornsilk","crimson","cyan","darkblue","darkcyan","darkgoldenrod","darkgray","darkgreen","darkgrey","darkkhaki","darkmagenta","darkolivegreen","darkorange","darkorchid","darkred","darksalmon","darkseagreen","darkslateblue","darkslategray","darkslategrey","darkturquoise","darkviolet","deeppink","deepskyblue","dimgray","dimgrey","dodgerblue","firebrick","floralwhite","forestgreen","fuchsia","gainsboro","ghostwhite","gold","goldenrod","gray","green","greenyellow","grey","honeydew","hotpink","indianred","indigo","ivory","khaki","lavender","lavenderblush","lawngreen","lemonchiffon","lightblue","lightcoral","lightcyan","lightgoldenrodyellow","lightgray","lightgreen","lightgrey","lightpink","lightsalmon","lightseagreen","lightskyblue","lightslategray","lightslategrey","lightsteelblue","lightyellow","lime","limegreen","linen","magenta","maroon","mediumaquamarine","mediumblue","mediumorchid","mediumpurple","mediumseagreen","mediumslateblue","mediumspringgreen","mediumturquoise","mediumvioletred","midnightblue","mintcream","mistyrose","moccasin","navajowhite","navy","oldlace","olive","olivedrab","orange","orangered","orchid","palegoldenrod","palegreen","paleturquoise","palevioletred","papayawhip","peachpuff","peru","pink","plum","powderblue","purple","rebeccapurple","red","rosybrown","royalblue","saddlebrown","salmon","sandybrown","seagreen","seashell","sienna","silver","skyblue","slateblue","slategray","slategrey","snow","springgreen","steelblue","tan","teal","thistle","tomato","turquoise","violet","wheat","white","whitesmoke","yellow","yellowgreen"]);var Lc=["square","rounded","circle","squircle"],Uc=["contain","cover","clip"],ns=(e,t,r,n)=>{if(!e)return r;if(!t.includes(e))throw new Error(`Invalid value for --${n}: '${e}'. Allowed: ${t.join(", ")}`);return e},Jr=(e,t,r,n,o)=>{if(e===void 0)return t;let s=Number(e);if(!Number.isFinite(s)||s<n||s>o)throw new Error(`Invalid value for --${r}: '${e}'. Must be a number between ${n}\u2013${o}.`);return s},ss=e=>{e.command("favicon").description("Generate favicon.ico (and optionally icons, OG image, PWA manifest icons)").option("--path <file>","Path to source image (PNG, JPG, JPEG, WebP, SVG, AVIF)").option("--out <dir>","Output dir (default: src/app or app)").option("--force","Overwrite existing files without prompt",!1).option("--dry-run","Show what would be written without writing",!1).option("--icon","Also emit icon.png (512\xD7512)",!1).option("--apple","Also emit apple-icon.png (180\xD7180)",!1).option("--og","Also emit opengraph-image.png (1200\xD7630)",!1).option("--og-source <file>","Use a separate image for the OG/Twitter card").option("--twitter","Also emit twitter-image.png (1200\xD7600)",!1).option("--all","Shorthand for --icon --apple --og",!1).option("--pwa","Detect PWA setup and emit manifest icons (192/512); errors if not detected",!1).option("--pwa-init","Bootstrap public/manifest.webmanifest with icons",!1).option("--shape <shape>","square | rounded | circle | squircle","square").option("--radius <percent>","Corner radius % when shape=rounded (0-50)","20").option("--bg <color>",'Background color (hex, css name, or "transparent")',"transparent").option("--padding <percent>","Padding around source content (0-30)","0").option("--fit <fit>","How source fits target: contain | cover | clip","contain").option("--sizes <list>","Comma-separated ICO sizes (default: 16,32,48)").option("--quality <n>","PNG compression 1-100 (higher = smaller file; PNG is lossless, default: 90)","90").action(async t=>{try{let r=process.cwd();m(Me.cyan(`
|
|
643
643
|
\u{1F3A8} Favicon Generator
|
|
644
|
-
`));let n=await
|
|
644
|
+
`));let n=await Vo({source:t.path}),o=await Bc(r,t.out),s=ns(t.shape,Lc,"square","shape"),i=ns(t.fit,Uc,"contain","fit"),a=Jr(t.radius,20,"radius",0,50),c=Jr(t.padding,0,"padding",0,30),l=Jr(t.quality,90,"quality",1,100),p=qr(t.sizes,[16,32,48]),u=t.bg??"transparent";qc(u);let d=t.all===!0,f={source:n.source,outDir:o,force:!!t.force,dryRun:!!t.dryRun,emitIcon:!!t.icon||d,emitApple:!!t.apple||d,emitOg:!!t.og||d,emitTwitter:!!t.twitter,ogSource:t.ogSource,pwa:!!t.pwa,pwaInit:!!t.pwaInit,shape:s,radius:a,bg:u,padding:c,fit:i,icoSizes:p,quality:l};g.start("Generating favicon assets...");let y=await Hr(r,f);g.succeed(f.dryRun?"Favicon plan ready":"Favicon assets generated"),m("");for(let h of y.outputs){let x=Jt.relative(r,h.path);h.skipped?m(Me.yellow(` \u21B7 skipped ${x} (exists; use --force to overwrite)`)):f.dryRun?m(Me.dim(` \u{1F4C4} would write ${x} (${os(h.bytes)})`)):m(Me.green(` \u2713 ${x} `)+Me.dim(`(${os(h.bytes)})`))}y.pwaSnippet&&(m(""),m(Me.cyan("Add this to your typed manifest (src/app/manifest.ts):")),m(Me.dim(y.pwaSnippet))),m("")}catch(r){g.fail("Favicon generation failed");let n=r instanceof Error?r.message:String(r);v(n),process.exit(1)}})},Bc=async(e,t)=>{if(t)return Jt.isAbsolute(t)?t:Jt.resolve(e,t);let r=await qt(e);return r||(kn("Could not detect app/ or src/app/ \u2014 falling back to src/app"),Jt.join(e,"src","app"))},Gc=/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/,Vc=/^(?:rgb|rgba|hsl|hsla)\(\s*[\d.,%\s/]+\)$/i,qc=e=>{if(e!=="transparent"&&!Gc.test(e)&&!Vc.test(e)&&!rs.has(e.toLowerCase()))throw new Error(`Invalid value for --bg: '${e}'. Use 'transparent', a hex code (e.g. #0f172a), a CSS color name, or rgb()/rgba()/hsl()/hsla().`)},os=e=>e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(2)} MB`;import zr from"node:path";import U from"picocolors";import Hc from"enquirer";var{prompt:Jc}=Hc,is=async(e,t,r={})=>{let n=[];e||n.push({type:"input",name:"featureName",message:"Feature name (kebab-case):",initial:"my-feature",validate:s=>/^[a-z][a-z0-9-]*$/.test(s)||"Use lowercase letters, digits, and hyphens only."}),r.api===void 0&&n.push({type:"confirm",name:"createApi",message:"Generate the api/ layer (schema, DAL, Server Actions, queries)?",initial:!0}),t!=="none"&&r.store===void 0&&(n.push({type:"confirm",name:"createStore",message:`Generate a ${t==="zustand"?"Zustand":"Redux"} slice for client-only state?`,initial:!1}),t==="redux"&&r.persist===void 0&&n.push({type:"confirm",name:"persistStore",message:"Persist the slice across reloads?",initial:!1,skip(){return!this.state.answers.createStore}}));let o=n.length>0?await Jc(n):{};return{featureName:e??o.featureName,createApi:r.api??o.createApi??!0,createStore:t!=="none"&&(r.store??o.createStore??!1),persistStore:t==="redux"&&(r.persist??o.persistStore??!1)}};var cs=e=>{e.command("feature [name]").description("Generate a feature module (api/, components/, store/, index.ts, server.ts)").option("--api","Generate the api/ layer: schema, DAL, Server Actions, queries").option("--no-api","Skip the api/ layer").option("--store","Generate a client-state slice (Redux or Zustand, whichever the project uses)").option("--no-store","Skip the slice").option("--persist","Persist the Redux slice across reloads").option("--no-persist","Do not persist the slice").option("--path <path>","Base directory (default: src/features)").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();m(U.cyan(`
|
|
645
645
|
\u{1F3AF} Feature Generator
|
|
646
|
-
`)),
|
|
647
|
-
`));let i=await
|
|
646
|
+
`)),g.start("Detecting project setup...");let s=await I(n);g.succeed("Project setup detected"),m(U.dim(` state: ${s.state} i18n: ${s.hasI18n?"\u2713":"\u2717"} tests: ${s.hasTests?"\u2713":"\u2717"}
|
|
647
|
+
`));let i=await is(t,s.state,{api:r.api,store:r.store,persist:r.persist});D(i.featureName,"feature name"),r.path&&Z(r.path,"path");let a=r.path||zr.join("src","features"),c=le(n,a,i.featureName);await Ce(n,i.featureName,a)&&(v(`Feature '${i.featureName}' already exists at ${a}.`),process.exit(1)),g.start("Generating feature files..."),await vr({name:i.featureName,outputPath:c,createStore:i.createStore,persistStore:i.persistStore,createApi:i.createApi,state:s.state,hasI18n:s.hasI18n,hasTests:s.hasTests}),g.succeed("Feature files generated"),s.hasI18n&&await Ne(n,k(i.featureName)),i.createApi&&(g.start("Registering API endpoints..."),await ht({serviceName:i.featureName,projectPath:n}),g.succeed("API endpoints registered")),i.createStore&&s.state==="redux"&&(g.start("Registering slice in the store..."),await yt({projectPath:n,name:i.featureName,persist:i.persistStore,importPath:zr.join(a,i.featureName,"store")}),g.succeed("Slice registered")),await E(n,o),zc(i,a,s.state)}catch(n){g.fail("Feature generation failed"),v(`${n instanceof Error?n.message:n}`),process.exit(1)}})},zc=(e,t,r)=>{let n=zr.join(t,e.featureName);m(U.green(`
|
|
648
648
|
\u2728 Feature '${e.featureName}' created.
|
|
649
|
-
`)),m(
|
|
649
|
+
`)),m(U.dim(` ${n}/`)),e.createApi&&m(U.dim(" api/ schema, keys, server (DAL), actions, queries")),m(U.dim(" components/")),e.createApi||m(U.dim(" hooks/")),e.createStore&&m(U.dim(" store/")),m(U.dim(" types/")),m(U.dim(" index.ts client-safe barrel")),e.createApi&&m(U.dim(" server.ts server-only barrel")),m(""),m(U.cyan("Next:")),e.createApi&&(m(U.dim(` 1. Adjust the contracts in ${n}/api/schema.ts and the paths in src/lib/config/app-apis.ts`)),m(U.dim(` 2. Read in a Server Component: import { list${as(e.featureName)}s } from '@/features/${e.featureName}/server'`)),m(U.dim(` 3. Prefetch + hydrate for the client list: prefetchQuery(${Wc(e.featureName)}ListQuery())`))),e.createStore&&r==="zustand"&&m(U.yellow(` ! Compose create${as(e.featureName)}Slice into AppState in src/store/index.ts`)),m("")},Wc=e=>e.replace(/-([a-z])/g,(t,r)=>r.toUpperCase()),as=k;import Wr from"node:path";import Xe from"picocolors";import Kc from"enquirer";var{prompt:Xc}=Kc,ps=async e=>e?{hookName:e}:await Xc({type:"input",name:"hookName",message:'Hook name (kebab-case, without "use" prefix):',validate:r=>r?/^[a-z][a-z0-9-]*$/.test(r)?!0:"Use lowercase letters, numbers, and hyphens only":"Hook name is required"});var ls=e=>{e.command("hook [name]").description("Generate a custom React hook").option("--client","Add 'use client' directive (default: true)",!0).option("--feature <path>","Generate in feature directory (e.g., src/features/auth)").option("--test","Also generate a sibling test file").option("--no-test","Skip test file generation").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();m(Xe.cyan(`
|
|
650
650
|
\u{1FA9D} Hook Generator
|
|
651
|
-
`));let s=await
|
|
651
|
+
`));let s=await I(n),i=await ps(t);D(i.hookName,"hook name"),r.feature&&Z(r.feature,"feature path");let c=`use${k(i.hookName)}`,l=Yc(r,s.hasTests);g.start("Generating hook..."),await kr({name:i.hookName,projectPath:n,isClient:r.client??!0,featurePath:r.feature}),g.succeed("Hook generated");let p=r.feature?Wr.join(n,r.feature,"hooks",`${c}.ts`):Wr.join(n,"src/hooks",`${c}.ts`),u=null;l&&(g.start("Generating test..."),u=await me({projectPath:n,sourceFile:p,kind:"hook",symbolName:c,hookUsesStore:s.state!=="none"&&!!r.feature}),g.succeed("Test generated"));let d=r.feature?`${r.feature}/hooks/${c}.ts`:`src/hooks/${c}.ts`;await E(n,o),m(Xe.green(`
|
|
652
652
|
\u2728 Hook '${c}' created successfully!
|
|
653
|
-
`)),m(
|
|
653
|
+
`)),m(Xe.dim(` \u{1F4C4} ${d}`)),u&&m(Xe.dim(` \u{1F9EA} ${Wr.relative(n,u)}`)),m(""),m(Xe.cyan("Usage:"));let f=r.feature?`${r.feature.replace(/^src\//,"@/")}/hooks/${c}`:`@/hooks/${c}`;m(Xe.dim(` import { ${c} } from '${f}';`)),m("")}catch(n){g.fail("Hook generation failed"),v(`${n}`),process.exit(1)}})},Yc=(e,t)=>e.test===!1?!1:e.test===!0?!0:t;import zt from"picocolors";var ms=e=>{e.command("layout <segment>").description("Generate a nested layout.tsx (locale-aware when i18n is detected)").option("--group","Treat the segment as a route group (wraps it in parens)").option("--at <path>","Place the layout under this nested path (kebab-case, slash-separated)").option("--no-locale","Skip the locale wrapper even if i18n is detected").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();m(zt.cyan(`
|
|
654
654
|
\u{1F4D0} Layout Generator
|
|
655
|
-
`)),
|
|
656
|
-
`)),
|
|
655
|
+
`)),g.start("Detecting project setup...");let s=await I(n);g.succeed("Project setup detected");let i=s.hasI18n&&r.locale!==!1;m(zt.dim(` i18n: ${i?"\u2713":"\u2717"}
|
|
656
|
+
`)),g.start("Generating layout...");let a=await Pr({segment:t,at:r.at,projectPath:n,hasI18n:i,withGroup:!!r.group});g.succeed("Layout generated"),await E(n,o),m(zt.green(`
|
|
657
657
|
\u2728 ${a.componentName} created!
|
|
658
|
-
`)),m(
|
|
658
|
+
`)),m(zt.dim(` \u{1F4C4} ${a.displayPath}`)),m("")}catch(n){g.fail("Layout generation failed"),v(`${n instanceof Error?n.message:n}`),process.exit(1)}})};import ne from"picocolors";import Qc from"enquirer";var{prompt:Zc}=Qc,us=async(e,t={})=>{let r=[];e||r.push({type:"input",name:"code",message:"Locale code (e.g., es, fr, de):",validate:o=>o?/^[a-z]{2}(-[A-Z]{2})?$/.test(o)?!0:"Use format: xx or xx-XX (e.g., es, pt-BR)":"Locale code is required"}),t.name||r.push({type:"input",name:"name",message:"Language name (e.g., Spanish, French):",validate:o=>o?!0:"Language name is required"}),t.country||r.push({type:"input",name:"country",message:"Country (e.g., Spain, France):",validate:o=>o?!0:"Country is required"}),t.flag||r.push({type:"input",name:"flag",message:"Flag emoji (e.g., \u{1F1EA}\u{1F1F8}, \u{1F1EB}\u{1F1F7}):",validate:o=>o?!0:"Flag emoji is required"}),t.copyTranslations===void 0&&r.push({type:"confirm",name:"copyTranslations",message:"Copy translations from English? (No = empty values)",initial:!1});let n=r.length>0?await Zc(r):{};return{code:e??n.code,name:t.name??n.name,country:t.country??n.country,flag:t.flag??n.flag,copyTranslations:t.copyTranslations??n.copyTranslations??!1}};var ds=e=>{e.command("locale [code]").description("Add a new locale/language").option("--copy-translations","Copy translations from English instead of empty values").option("--no-copy-translations","Start the new locale with empty values").option("--name <name>","Language name, e.g. Spanish").option("--country <country>","Country, e.g. Spain").option("--flag <emoji>","Flag emoji, e.g. \u{1F1EA}\u{1F1F8}").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();m(ne.cyan(`
|
|
659
659
|
\u{1F30D} Locale Generator
|
|
660
|
-
`)),
|
|
660
|
+
`)),g.start("Detecting project setup...");let s=await I(n);g.succeed("Project setup detected"),s.hasI18n||(g.fail("i18n is not set up in this project"),v("Please set up internationalization first"),m(ne.dim(`
|
|
661
661
|
Run: npx @teispace/next-maker setup --i18n
|
|
662
|
-
`)),process.exit(1));let i=await
|
|
662
|
+
`)),process.exit(1));let i=await us(t,{copyTranslations:r.copyTranslations,name:r.name,country:r.country,flag:r.flag});D(i.code,"locale code");let a=le(n,"src","i18n","translations",`${i.code}.json`);w(a)&&(v(`Locale '${i.code}' already exists!`),process.exit(1)),g.start("Adding locale..."),await Sr({...i,projectPath:n}),g.succeed("Locale added"),await E(n,o),m(ne.green(`
|
|
663
663
|
\u2728 Locale '${i.code}' (${i.name}) added successfully!
|
|
664
|
-
`)),m(
|
|
664
|
+
`)),m(ne.dim("Updated files:")),m(ne.dim(` \u{1F4C4} src/i18n/translations/${i.code}.json`)),m(ne.dim(" \u{1F4DD} src/types/i18n.ts (SupportedLocale type)")),m(ne.dim(" \u{1F4DD} src/lib/config/app-locales.ts")),m(""),m(ne.cyan("Next steps:")),m(ne.dim(` 1. Translate: src/i18n/translations/${i.code}.json`)),m(ne.dim(` 2. Visit: http://localhost:3000/${i.code}`)),m("")}catch(n){g.fail("Locale generation failed"),v(`${n}`),process.exit(1)}})};import ep from"node:path";import xt from"picocolors";var fs=e=>{e.command("options").description("List the starter's options and this project's current values").option("--starter-path <dir>","Read the manifest from a local starter checkout").action(async t=>{let r;try{let n=process.cwd(),o=w(ep.join(n,".next-maker.json")),s=o?await pe(n):{cli:"",starter:{name:"",version:"",source:""},packageManager:"pnpm",answers:{}},i=await qe(s,{starterPath:t.starterPath});r=i.dispose,m(xt.cyan(`
|
|
665
665
|
${i.manifest.starter.name} ${i.manifest.starter.version}
|
|
666
|
-
`));for(let[a,c]of Object.entries(i.manifest.options)){let l=o?s.answers[a]:c.default,p=c.type==="boolean"?"true | false":(c.values??[]).join(" | ");m(` ${
|
|
666
|
+
`));for(let[a,c]of Object.entries(i.manifest.options)){let l=o?s.answers[a]:c.default,p=c.type==="boolean"?"true | false":(c.values??[]).join(" | ");m(` ${xt.bold(a.padEnd(18))} ${String(Array.isArray(l)?l.join(",")||"none":l).padEnd(10)} ${xt.dim(p)}`),c.label&&m(` ${"".padEnd(18)} ${xt.dim(c.label)}`)}m(""),m(xt.dim(o?"Change with: next-maker setup --set <option>=<value>":"Use with: next-maker init --set <option>=<value>")),m("")}catch(n){v(`${n instanceof Error?n.message:n}`),process.exit(1)}finally{await r?.()}})};import Kr from"node:path";import K from"picocolors";import tp from"enquirer";var{prompt:rp}=tp,gs=async(e,t={})=>{let r=[];e||r.push({type:"input",name:"pageName",message:"Page name (kebab-case):",validate:o=>o?/^[a-z][a-z0-9-]*$/.test(o)?!0:"Use lowercase letters, numbers, and hyphens only":"Page name is required"}),t.loading===void 0&&r.push({type:"confirm",name:"withLoading",message:"Generate loading.tsx?",initial:!0}),t.error===void 0&&r.push({type:"confirm",name:"withError",message:"Generate error.tsx?",initial:!0});let n=r.length>0?await rp(r):{};return{pageName:e??n.pageName,withLoading:t.loading??n.withLoading??!0,withError:t.error??n.withError??!0}};var hs=e=>{e.command("page [name]").description("Generate a new page/route").option("--dynamic <param>","Create dynamic route with parameter (e.g., id)").option("--group <name>","Route group to place the page in, e.g. app or marketing").option("--loading","Generate loading.tsx").option("--no-loading","Skip loading.tsx").option("--error","Generate error.tsx").option("--no-error","Skip error.tsx").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();m(K.cyan(`
|
|
667
667
|
\u{1F4C4} Page Generator
|
|
668
|
-
`)),
|
|
669
|
-
`));let i=await
|
|
668
|
+
`)),g.start("Detecting project setup...");let s=await I(n);g.succeed("Project setup detected"),m(K.dim(` i18n: ${s.hasI18n?"\u2713":"\u2717"}
|
|
669
|
+
`));let i=await gs(t,{loading:r.loading,error:r.error}),a=i.pageName;D(a,"page name"),r.dynamic&&D(r.dynamic,"dynamic param"),r.group&&D(r.group,"route group");let c=k(a),l=Kr.join(s.hasI18n?"src/app/[locale]":"src/app",r.group?`(${r.group})`:"");await Ce(n,a,l)&&(v(`Page '${a}' already exists at ${l}/${a}!`),process.exit(1)),g.start("Generating page files..."),await jr({name:a,projectPath:n,hasI18n:s.hasI18n,group:r.group,dynamic:r.dynamic,withLoading:r.loading??i.withLoading,withError:r.error??i.withError}),g.succeed("Page files generated"),g.start("Registering route...");let u=r.dynamic?`/${a}/[${r.dynamic}]`:`/${a}`;await Nr(n,a,u),g.succeed("Route registered"),s.hasI18n&&(g.start("Adding translation namespace..."),await Ne(n,c),g.succeed("Translation namespace added"));let d=r.dynamic?Kr.join(l,a,`[${r.dynamic}]`):Kr.join(l,a);await E(n,o),m(K.green(`
|
|
670
670
|
\u2728 Page '${a}' created successfully!
|
|
671
|
-
`)),m(
|
|
671
|
+
`)),m(K.dim("Generated files:")),m(K.dim(` \u{1F4C2} ${d}/`)),m(K.dim(" \u251C\u2500\u2500 page.tsx")),(r.loading??i.withLoading)&&m(K.dim(" \u251C\u2500\u2500 loading.tsx")),(r.error??i.withError)&&m(K.dim(" \u2514\u2500\u2500 error.tsx")),m(""),s.hasI18n&&(m(K.cyan("Next steps:")),m(K.dim(` 1. Add translations in: src/i18n/translations/en.json \u2192 "${c}"`)),m(K.dim(` 2. Visit: http://localhost:3000/${a}`)),m(""))}catch(n){g.fail("Page generation failed"),v(`${n}`),process.exit(1)}})};import Xr from"node:path";import be from"picocolors";var ys=e=>{e.command("provider <name>").description("Generate a context provider in src/providers/ and wire it into RootProvider").action(async t=>{try{let r=process.cwd(),n=Date.now();m(be.cyan(`
|
|
672
672
|
\u{1F50C} Provider Generator
|
|
673
|
-
`)),
|
|
673
|
+
`)),g.start("Generating provider...");let{componentName:o,fileBaseName:s,providerFile:i}=await Cr({name:t,projectPath:r});g.succeed("Provider generated"),g.start("Registering provider in RootProvider and barrel...");let{rootProviderFile:a,barrelFile:c}=await Dr({projectPath:r,componentName:o,fileBaseName:s});g.succeed("Provider registered"),await E(r,n),m(be.green(`
|
|
674
674
|
\u2728 ${o} created!
|
|
675
|
-
`)),m(
|
|
676
|
-
\u26A0\uFE0F Could not locate a RootProvider file. Wire the new provider manually:`)),m(
|
|
677
|
-
${
|
|
678
|
-
`)};import
|
|
675
|
+
`)),m(be.dim("Generated files:")),m(be.dim(` \u{1F4C4} ${Xr.relative(r,i)}`)),a?m(be.dim(` \u270F\uFE0F ${Xr.relative(r,a)} \u2014 wrapped {children}`)):(m(be.yellow(`
|
|
676
|
+
\u26A0\uFE0F Could not locate a RootProvider file. Wire the new provider manually:`)),m(be.dim(` <${o}>{children}</${o}>`))),c&&m(be.dim(` \u270F\uFE0F ${Xr.relative(r,c)} \u2014 added re-export`)),m("")}catch(r){g.fail("Provider generation failed"),v(`${r instanceof Error?r.message:r}`),process.exit(1)}})};import np from"picocolors";var ws=e=>{e.command("remove <feature>").alias("uninstall").description("Turn a boolean starter feature off (shorthand for `setup --set <feature>=false`)").option("-y, --yes","Skip the confirmation prompt").option("--dry-run","Print the plan without writing").option("--no-install","Skip dependency installation after the change").action(async(t,r)=>{try{let n=["setup","--set",`${t}=false`];r.yes&&n.push("--yes"),r.dryRun&&n.push("--dry-run"),r.install===!1&&n.push("--no-install"),await e.parseAsync([process.argv[0],process.argv[1],...n])}catch(n){v(`${n instanceof Error?n.message:n}`),process.exit(1)}}).addHelpText("after",`
|
|
677
|
+
${np.dim("Example: next-maker remove ws")}
|
|
678
|
+
`)};import Yr from"node:path";import Qr from"picocolors";var xs=e=>{e.command("api [name]").alias("service").description("Generate api/{schema,keys,server,queries,actions}.ts for a resource inside a feature").option("--feature <path>","Feature directory (default: src/features/<name>)").option("--no-actions","Skip actions.ts (read-only resource)").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();if(m(Qr.cyan(`
|
|
679
679
|
\u{1F50C} API Layer Generator
|
|
680
|
-
`)),!t)throw new Error("Pass the resource name, e.g. `next-maker api invoice`.");
|
|
681
|
-
\u2728 ${
|
|
682
|
-
`));for(let c of a)m(
|
|
683
|
-
`)),
|
|
684
|
-
`);return t[t.length-1]===""&&t.pop(),t},
|
|
680
|
+
`)),!t)throw new Error("Pass the resource name, e.g. `next-maker api invoice`.");D(t,"resource name"),r.feature&&Z(r.feature,"feature path");let s=await I(n);if(!s.hasQuery||!s.hasActions)throw new Error("This generator needs @tanstack/react-query and next-safe-action (starter 2.x).");let i=r.feature?le(n,r.feature):le(n,"src","features",t);if(w(Yr.join(i,"api","schema.ts")))throw new Error(`${Yr.relative(n,i)}/api already exists.`);g.start("Generating api/ files...");let a=await mt({name:t,featurePath:i,withActions:r.actions!==!1});g.succeed("api/ generated"),g.start("Registering API endpoints..."),await ht({serviceName:t,projectPath:n}),g.succeed("Endpoints registered in src/lib/config/app-apis.ts"),await E(n,o),m(Qr.green(`
|
|
681
|
+
\u2728 ${Yr.relative(n,i)}/api ready.
|
|
682
|
+
`));for(let c of a)m(Qr.dim(` ${c}`));m("")}catch(n){g.fail("API generation failed"),v(`${n instanceof Error?n.message:n}`),process.exit(1)}})};import{readFile as Os,writeFile as Sp}from"node:fs/promises";import an from"node:path";import Ep from"enquirer";import G from"picocolors";import{execFile as mp}from"node:child_process";import{cp as ly,mkdir as Es,readFile as up,rm as nn,stat as my,writeFile as Kt}from"node:fs/promises";import se from"node:path";import{promisify as dp}from"node:util";var Zr=(e,t)=>{let r=e.length,n=t.length;if((r+1)*(n+1)>4e6)return null;let o=n+1,s=new Int32Array((r+1)*o);for(let u=r-1;u>=0;u--)for(let d=n-1;d>=0;d--)s[u*o+d]=e[u]===t[d]?s[(u+1)*o+d+1]+1:Math.max(s[(u+1)*o+d],s[u*o+d+1]);let i=[],a=null,c=0,l=0,p=()=>(a||(a={start:c,end:c,insert:[]},i.push(a)),a);for(;c<r||l<n;){let u=c*o+l;c<r&&l<n&&e[c]===t[l]&&s[u]===s[u+o+1]+1?(a=null,c++,l++):l<n&&(c>=r||s[u+1]>=s[u+o])?(p().insert.push(t[l]),l++):p().end=++c}return i},oe=e=>e.start===e.end,op=(e,t)=>oe(e)&&oe(t)?!1:oe(e)?t.start<e.start&&e.start<t.end:oe(t)?e.start<t.start&&t.start<e.end:Math.max(e.start,t.start)<Math.min(e.end,t.end),sp=(e,t)=>e.start===t.start&&e.end===t.end&&e.insert.length===t.insert.length&&e.insert.every((r,n)=>r===t.insert[n]),bs=(e,t,r)=>{let n=t.start,o=t.insert;for(;n<r.end&&o.length>0&&o[0]===e[n];)o=[...o.slice(1),o[0]],n++;if(n===r.end)return{start:n,end:n,insert:o};for(n=t.start,o=t.insert;n>r.start&&o.length>0&&o[o.length-1]===e[n-1];)o=[o[o.length-1],...o.slice(0,-1)],n--;return n===r.start?{start:n,end:n,insert:o}:null},en=(e,t)=>e.map(r=>({start:r.start+t,end:r.end+t,insert:r.insert})),vs=(e,t,r,n)=>{let o=[],s=0,i=0,a=0,c=p=>{for(let u=s;u<p.start;u++)o.push(e[u]);o.push(...p.insert),s=Math.max(s,p.end)},l=(p,u)=>{if(!n)return!1;let d=Math.min(p.start,u.start),f=Math.max(p.end,u.end),y=i,h=a,x=!0;for(;x;){for(x=!1;y+1<t.length&&t[y+1].start<f;)y++,f=Math.max(f,t[y].end),x=!0;for(;h+1<r.length&&r[h+1].start<f;)h++,f=Math.max(f,r[h].end),x=!0}let b=n(e.slice(d,f),en(t.slice(i,y+1),-d),en(r.slice(a,h+1),-d));return b?(c({start:d,end:f,insert:b}),i=y+1,a=h+1,!0):!1};for(;i<t.length||a<r.length;){let p=t[i],u=r[a];if(p&&u){if(sp(p,u)){c(p),i++,a++;continue}if(op(p,u)){let f=oe(p)&&!oe(u)?bs(e,p,u):oe(u)&&!oe(p)?bs(e,u,p):null;if(f&&oe(p))p=f;else if(f)u=f;else{if(l(p,u))continue;return null}}p.start<u.start||p.start===u.start&&oe(p)?(c(p),i++):(c(u),a++)}else p?(c(p),i++):u&&(c(u),a++)}for(let p=s;p<e.length;p++)o.push(e[p]);return o},ip=(e,t,r,n)=>{let o=Zr(e,t),s=Zr(e,r);return o&&s?vs(e,o,s,n):null},ap=/\s+|[\p{L}\p{N}_$]+\s*|[^\s\p{L}\p{N}_$]\s*/gu,$s=e=>e.match(ap)??[],cp=e=>e.flatMap(t=>$s(`${t}
|
|
683
|
+
`)),bt=e=>{if(e==="")return[];let t=e.split(`
|
|
684
|
+
`);return t[t.length-1]===""&&t.pop(),t},Wt=e=>e.length?`${e.join(`
|
|
685
685
|
`)}
|
|
686
|
-
`:"",
|
|
687
|
-
`));o.push(n.length);let s=l=>{let p=[];for(let u of l){let
|
|
686
|
+
`:"",pp=(e,t,r)=>{let n=[],o=[];for(let l of e)o.push(n.length),n.push(...$s(`${l}
|
|
687
|
+
`));o.push(n.length);let s=l=>{let p=[];for(let u of l){let d=o[u.start],f=Zr(n.slice(d,o[u.end]),cp(u.insert));if(!f)return null;p.push(...en(f,d))}return p},i=s(t),a=s(r);if(!i||!a)return null;let c=vs(n,i,a);return c?bt(c.join("")):null},lp=(e,t,r)=>{let n=ip(bt(t),bt(e),bt(r),pp);return n?Wt(n):null},ks=(e,t)=>{let r=e.split(`
|
|
688
688
|
`),n=e.endsWith(`
|
|
689
|
-
`);n&&r.pop();let o=[],s=0,i=0;for(;i<r.length;){let a=r[i];if(!a.startsWith("<<<<<<< ")){o.push(a),i++;continue}let c=[],l=[],p=[],u=c;i++;let
|
|
689
|
+
`);n&&r.pop();let o=[],s=0,i=0;for(;i<r.length;){let a=r[i];if(!a.startsWith("<<<<<<< ")){o.push(a),i++;continue}let c=[],l=[],p=[],u=c;i++;let d=!1;for(;i<r.length;){let y=r[i++];if(u===c&&y.startsWith("||||||| "))u=l;else if((u===c||u===l)&&y==="=======")u=p;else if(u===p&&y.startsWith(">>>>>>> ")){d=!0;break}else u.push(y)}if(!d)throw new Error("Malformed conflict block in merge output.");let f=lp(Wt(c),Wt(l),Wt(p));if(f!==null){o.push(...bt(f));continue}s++,o.push(`<<<<<<< ${t.ours}`,...c,"=======",...p,`>>>>>>> ${t.theirs}`)}return{content:o.join(`
|
|
690
690
|
`)+(n?`
|
|
691
|
-
`:""),conflicts:s}};var
|
|
692
|
-
`,o),
|
|
691
|
+
`:""),conflicts:s}};var fp=dp(mp),gp=new Set(["pnpm-lock.yaml","package-lock.json","yarn.lock","bun.lock","bun.lockb",".next-maker.json","next-env.d.ts","tsconfig.tsbuildinfo"]),hp=e=>{let t=e.split("/").pop()??e;return t===".env.example"?!1:gp.has(e)||t.startsWith(".env")||e.endsWith(".log")},yp=new Set([".png",".jpg",".jpeg",".gif",".webp",".avif",".ico",".woff",".woff2",".ttf",".otf",".pdf",".zip",".gz"]),tn=(e,t)=>yp.has(se.extname(e))||t.subarray(0,8e3).includes(0),rn=async e=>{try{return await up(e)}catch{return null}},Ye=async(e,t,r)=>{r||(await Es(se.dirname(e),{recursive:!0}),await Kt(e,t))},Ps=async(e,t,r,n={ours:"project",base:"starter (previous)",theirs:"starter (new)"})=>{let o=se.join(process.env.TMPDIR??"/tmp",`next-maker-merge-${process.pid}-${Math.random().toString(36).slice(2)}`);await Es(o,{recursive:!0});try{await Kt(se.join(o,"ours"),e),await Kt(se.join(o,"base"),t),await Kt(se.join(o,"theirs"),r);try{let{stdout:s}=await fp("git",["merge-file","-p","--diff3","-L",n.ours,"-L",n.base,"-L",n.theirs,"ours","base","theirs"],{cwd:o,maxBuffer:67108864});return{content:s,conflicts:0}}catch(s){let i=s;if(typeof i.code=="number"&&i.code>0&&i.code<128&&typeof i.stdout=="string")return ks(i.stdout,n);throw i.code==="ENOENT"?new Error("git is required for merges but was not found on PATH."):s}}finally{await nn(o,{recursive:!0,force:!0})}},Ss=["scripts","dependencies","devDependencies","peerDependencies","optionalDependencies","engines"],wp=(e,t,r)=>{let n=structuredClone(e),o=[],s=(a,c)=>JSON.stringify(a)===JSON.stringify(c),i=(a,c,l,p,u,d)=>{let f=l[u],y=p[u],h=c[u];if(!s(f,y)){if(s(h,f)||s(h,y)){y===void 0?delete a[u]:a[u]=y;return}o.push(d)}};for(let a of Ss){let c=t[a]??{},l=r[a]??{},p=e[a]??{};n[a]||(n[a]={});let u=n[a];for(let d of new Set([...Object.keys(c),...Object.keys(l)]))i(u,p,c,l,d,`${a}.${d}`);Object.keys(u).length===0&&!(a in r)?delete n[a]:a!=="scripts"&&a!=="engines"&&(n[a]=Object.fromEntries(Object.entries(u).sort(([d],[f])=>d.localeCompare(f))))}for(let a of new Set([...Object.keys(t),...Object.keys(r)]))Ss.includes(a)||i(n,e,t,r,a,a);return{result:n,conflicts:o}},Qe=async({base:e,theirs:t,ours:r},n={})=>{let o=n.dryRun??!1,s=n.skip??hp,[i,a,c]=await Promise.all([A(e),A(t),A(r)]),l=new Set(c),p=new Set(i),u=new Set(a),d={entries:[],conflicts:[]},f=(x,b,P)=>{d.entries.push({file:x,outcome:b,note:P}),b==="conflict"&&d.conflicts.push(x)},y=n.variant??(()=>!1),h=new Set([...i,...a,...c.filter(y)]);for(let x of[...h].sort()){if(s(x))continue;let b=p.has(x),P=u.has(x),R=l.has(x),[q,B,H]=await Promise.all([b?rn(se.join(e,x)):null,P?rn(se.join(t,x)):null,R?rn(se.join(r,x)):null]),J=se.join(r,x);if(y(x)){if(P){let ie=R&&H.equals(B);ie||await Ye(J,B,o),f(x,ie?"unchanged":R?"replaced":"added")}else R&&(o||await nn(J,{force:!0}),f(x,"deleted","belonged to the variant you switched away from"));continue}if(P&&!b){if(!R)await Ye(J,B,o),f(x,"added");else if(H.equals(B))f(x,"unchanged");else if(tn(x,B))f(x,"kept","starter added a binary the project also has");else{let ie=await Ps(H.toString("utf8"),"",B.toString("utf8"));await Ye(J,ie.content,o),f(x,ie.conflicts?"conflict":"merged")}continue}if(b&&!P){if(!R)continue;H.equals(q)?(o||await nn(J,{force:!0}),f(x,"deleted")):f(x,"kept","starter removed it, but the project changed it");continue}if(!R){f(x,"kept","deleted in the project");continue}if(q.equals(B)||H.equals(B)){f(x,"unchanged");continue}if(H.equals(q)){await Ye(J,B,o),f(x,"updated");continue}if(tn(x,H)||tn(x,B)){f(x,"kept","binary changed on both sides");continue}if(x==="package.json"){let{result:ie,conflicts:vt}=wp(JSON.parse(H.toString("utf8")),JSON.parse(q.toString("utf8")),JSON.parse(B.toString("utf8")));await Ye(J,`${JSON.stringify(ie,null,2)}
|
|
692
|
+
`,o),f(x,vt.length?"conflict":"merged",vt.length?`project kept: ${vt.join(", ")}`:void 0),vt.length&&d.conflicts.pop();continue}let S=await Ps(H.toString("utf8"),q.toString("utf8"),B.toString("utf8"));await Ye(J,S.content,o),f(x,S.conflicts?"conflict":"merged")}return d};import{mkdtemp as xp,readFile as bp,rm as Ts}from"node:fs/promises";import{tmpdir as vp}from"node:os";import ve from"node:path";var Xt=async(e,t)=>{if(t.identity)return ye(t.identity.projectName,{...t.identity,copyEnv:!1});let r=JSON.parse(await bp(ve.join(e,"package.json"),"utf-8"));return ye(ve.basename(e),{projectName:r.name??ve.basename(e),description:r.description,author:typeof r.author=="string"?r.author:void 0,version:r.version,readme:w(ve.join(e,"README.md")),copyEnv:!1})},js=e=>{let t=e.starter.source;if(t.startsWith("/")||t.startsWith(".")){if(!w(t))throw new Error(`The starter this project was generated from is a local path that no longer exists (${t}). Pass --from <ref|dir>.`);return{kind:"local",location:ve.resolve(t)}}return{kind:"remote",location:t}},on=e=>e.startsWith("/")||e.startsWith(".")||w(e)?{kind:"local",location:ve.resolve(e)}:W({ref:e.includes("#")?e.split("#")[1]:e,starterPath:void 0}),Ze=async(e,t,r,n,o)=>{let s=await xp(ve.join(vp(),"next-maker-ref-")),i=ve.join(s,r.projectName);try{let a=await je({projectPath:i,source:e,supplied:t,packageManager:n});return await Re(i,r,a,e),o&&await lt(i,["."],o),{dir:i,scaffold:a,dispose:()=>Ts(s,{recursive:!0,force:!0})}}catch(a){throw await Ts(s,{recursive:!0,force:!0}),a}};import $p from"enquirer";import T from"picocolors";var{prompt:kp}=$p,Pp={added:T.green("+"),updated:T.cyan("\u2191"),merged:T.cyan("\u21C4"),deleted:T.red("-"),conflict:T.red("\u2717"),replaced:T.cyan("\u21C9"),kept:T.yellow("\xB7"),skipped:T.dim("\xB7")},sn=e=>{let t=e.entries.filter(n=>n.outcome!=="unchanged");if(t.length===0){m(T.dim(" no file changes"));return}for(let n of t)m(` ${Pp[n.outcome]??" "} ${n.outcome.padEnd(8)} ${n.file}${n.note?T.dim(` (${n.note})`):""}`);let r={};for(let n of t)r[n.outcome]=(r[n.outcome]??0)+1;if(m(""),m(T.dim(` ${Object.entries(r).map(([n,o])=>`${o} ${n}`).join(", ")}`)),t.some(n=>n.file.endsWith(".env.example")&&n.outcome!=="kept")&&(m(""),m(T.yellow(" .env.example changed; check your own .env for new or renamed variables."))),e.conflicts.length){m(""),m(T.red(` ${e.conflicts.length} file(s) need attention; look for <<<<<<< markers:`));for(let n of e.conflicts)m(T.red(` ${n}`))}},Rs=e=>{e.command("upgrade").description(`Merge starter changes into this project (default target: ${Ct})`).option("--to <ref|dir>","Starter tag or local checkout to upgrade to").option("--from <ref|dir>","Starter the project was generated from (default: recorded at init)").option("--starter-path <dir>","Alias for --to with a local checkout").option("-y, --yes","Skip the confirmation prompt").option("--dry-run","Report what would change without writing").option("--no-install","Skip dependency installation afterwards").action(async t=>{let r=process.cwd(),n=[];try{let o=await pe(r),s=await Xt(r,o),i=t.from?on(t.from):js(o),a=t.to?on(t.to):W({starterPath:t.starterPath,ref:Ct});if(i.location===a.location){m(T.dim(`Already on ${a.location}; nothing to upgrade.`));return}m(T.cyan(`
|
|
693
693
|
\u2B06 Upgrade
|
|
694
|
-
`)),m(` ${
|
|
695
|
-
`);let c=await
|
|
694
|
+
`)),m(` ${T.dim("from")} ${i.location}`),m(` ${T.dim("to ")} ${a.location}
|
|
695
|
+
`);let c=await Ze(i,o.answers,s,o.packageManager,r);n.push(c.dispose);let l=await Ze(a,o.answers,s,o.packageManager,r);n.push(l.dispose);for(let f of l.scaffold.forced)m(T.yellow(` ! ${f.reason}; ${f.option} turned off`));for(let f of l.scaffold.unknown)m(T.yellow(` ! option "${f}" no longer exists in the new starter`));let p=await Qe({base:c.dir,theirs:l.dir,ours:r},{dryRun:!0});if(sn(p),t.dryRun){m(T.dim(`
|
|
696
696
|
Dry run: nothing was written.
|
|
697
|
-
`));return}if(!t.yes){let{confirm:
|
|
698
|
-
`));return}}let u=Date.now(),
|
|
699
|
-
Installing with ${o.packageManager}...`)),await
|
|
700
|
-
\u2713 Upgraded to ${l.scaffold.manifest.starter.version}.`)),
|
|
697
|
+
`));return}if(!t.yes){let{confirm:f}=await kp({type:"confirm",name:"confirm",message:"Apply these changes?",initial:!1});if(!f){m(T.yellow(`Aborted.
|
|
698
|
+
`));return}}let u=Date.now(),d=await Qe({base:c.dir,theirs:l.dir,ours:r});await E(r,u),await we(r,{...o,cli:De.version,starter:{...l.scaffold.manifest.starter,source:a.location},answers:l.scaffold.answers,identity:o.identity??{projectName:s.projectName,description:s.description,author:s.author,version:s.version,email:s.email,gitRemote:s.gitRemote,readme:s.readme}}),t.install!==!1&&(m(T.dim(`
|
|
699
|
+
Installing with ${o.packageManager}...`)),await he(r,o.packageManager)),m(T.green(`
|
|
700
|
+
\u2713 Upgraded to ${l.scaffold.manifest.starter.version}.`)),d.conflicts.length?m(T.yellow(" Resolve the conflicts above, then run `next-maker doctor --compile`.")):m(T.dim(" Run `next-maker doctor --compile` to confirm.")),m("")}catch(o){v(`${o instanceof Error?o.message:o}`),process.exit(1)}finally{for(let o of n)await o()}})};var{prompt:Tp}=Ep,jp=(e,t=[])=>[...t,e],Cs=async(e,t,r)=>{let n=i=>i.some(a=>r.has(a)),o=[],s=[...t.unwrapJsx.filter(i=>n(i.options)).map(i=>[i.file,a=>ke(a,i.tag)]),...t.unwrapCall.filter(i=>n(i.options)).map(i=>[i.file,a=>Pe(a,i.name)])];for(let[i,a]of s){let c=an.join(e,i),l;try{l=await Os(c,"utf-8")}catch{continue}let p=a(l);p!==l&&(await Sp(c,p),o.includes(i)||o.push(i))}return o},Rp=async(e,t,r,n)=>{let o=a=>a.some(c=>n.has(c)),s=new Set;for(let{plan:a}of r)for(let c of a.featureOverlays)if(o(c.options))for(let l of await A(an.join(e,ae,c.name),new Set))s.add(l);let i=r.flatMap(({plan:a})=>a.featureRemovals.filter(c=>o(c.options)).flatMap(c=>c.patterns));if(i.length){let a=await A(t);for(let c of i)for(let l of Y(a,c))s.add(l)}return s},Cp=async(e,t)=>{let r=[...t.packages,...t.devPackages];if(r.length===0)return[];let n=(await A(e)).filter(s=>/\.(ts|tsx|js|jsx|mjs|cjs)$/.test(s)),o=[];for(let s of n){let i;try{i=await Os(an.join(e,s),"utf-8")}catch{continue}let a=r.filter(c=>new RegExp(`from ['"]${c.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}(/[^'"]*)?['"]`).test(i));a.length&&o.push({file:s,packages:a})}return o},As=e=>{e.command("setup").description("Turn starter features on or off in an existing project (e.g. --set ws=true)").option("--set <key=value>","Option to change (repeatable), e.g. --set i18n=false --set state=zustand",jp).option("-y, --yes","Skip the confirmation prompt").option("--dry-run","Print the plan without writing").option("--no-install","Skip dependency installation after the change").option("--starter-path <dir>","Use a local starter checkout").action(async t=>{let r=process.cwd(),n=[];try{let o=await pe(r),s=Ee(t.set);if(Object.keys(s).length===0)throw new Error("Nothing to change. Pass at least one --set key=value (run `next-maker options` to list them).");let i=await qe(o,{starterPath:t.starterPath});n.push(i.dispose);let{answers:a,forced:c,unknown:l}=rt(i.manifest,{...o.answers,...s});for(let S of l)m(G.yellow(` ! unknown option "${S}" ignored`));for(let S of c)m(G.yellow(` ! ${S.reason}; ${S.option} turned off`));let p=Object.keys(a).filter(S=>JSON.stringify(a[S])!==JSON.stringify(o.answers[S]));if(m(G.cyan(`
|
|
701
701
|
\u{1F527} Setup
|
|
702
|
-
`)),p.length===0){m(
|
|
703
|
-
`));return}for(let
|
|
702
|
+
`)),p.length===0){m(G.dim(`No option changes; the project already has these values.
|
|
703
|
+
`));return}for(let S of p)m(` ${G.dim(S.padEnd(18))} ${String(o.answers[S])} ${G.dim("\u2192")} ${String(a[S])}`);m("");let u=await Xt(r,o),d=await Ze(i.source,o.answers,u,o.packageManager,r);n.push(d.dispose);let f=await Ze(i.source,a,u,o.packageManager,r);n.push(f.dispose);let y=new Set(p),h=await Rp(i.dir,r,[d.scaffold,f.scaffold],y),x={variant:S=>h.has(S)},b=await Cs(r,f.scaffold.plan,y),P=await Cs(d.dir,f.scaffold.plan,y);P.length&&await lt(d.dir,P,r,!0);for(let S of b)m(G.dim(` unwrapped in place: ${S}`));let R=await Qe({base:d.dir,theirs:f.dir,ours:r},{...x,dryRun:!0});if(sn(R),t.dryRun){m(G.dim(`
|
|
704
704
|
Dry run: nothing was written.
|
|
705
|
-
`));return}if(!t.yes){let{confirm:
|
|
706
|
-
`));return}}let
|
|
707
|
-
Installing with ${o.packageManager}...`)),await
|
|
708
|
-
\u2713 Applied.`)),
|
|
705
|
+
`));return}if(!t.yes){let{confirm:S}=await Tp({type:"confirm",name:"confirm",message:"Apply these changes?",initial:!1});if(!S){m(G.yellow(`Aborted.
|
|
706
|
+
`));return}}let q=Date.now(),B=await Qe({base:d.dir,theirs:f.dir,ours:r},x);await E(r,q),await we(r,{...o,answers:a}),t.install!==!1&&(m(G.dim(`
|
|
707
|
+
Installing with ${o.packageManager}...`)),await he(r,o.packageManager));let H=B.entries.filter(S=>S.outcome==="replaced"||S.outcome==="deleted"&&S.note).length;H>0&&(m(""),m(G.dim(` ${H} file(s) came from the variant you switched; run \`next-maker doctor --compile\` to catch code of your own that no longer builds.`)));let J=await Cp(r,f.scaffold.plan);if(J.length){m(""),m(G.yellow(" Still importing packages this change removed; port them by hand:"));for(let{file:S,packages:ie}of J)m(G.yellow(` ${S} \u2014 ${ie.join(", ")}`))}m(G.green(`
|
|
708
|
+
\u2713 Applied.`)),B.conflicts.length&&m(G.yellow(" Resolve the conflicts above, then run `next-maker doctor --compile`.")),m("")}catch(o){v(`${o instanceof Error?o.message:o}`),process.exit(1)}finally{for(let o of n)await o()}})};import cn from"node:path";import Yt from"picocolors";import Op from"enquirer";var{prompt:Ap}=Op,Is=async(e,t)=>{let r=[];e||r.push({type:"input",name:"sliceName",message:"What is the slice name?",initial:"my-slice",validate:o=>/^[a-z0-9-]+$/.test(o)?!0:"Slice name must be lowercase and contain only alphanumeric characters and hyphens."}),t===void 0&&r.push({type:"confirm",name:"persistSlice",message:"Enable persistence for this slice?",initial:!1});let n=r.length>0?await Ap(r):{};return{sliceName:e||n.sliceName,persistSlice:t===void 0?n.persistSlice||!1:t}};var Ns=e=>{e.command("slice [name]").description("Generate a store slice (Redux or Zustand, whichever the project uses)").option("--path <path>","Directory for the slice (default: src/store/slices/<name>)").option("--persist","Persist the slice across reloads (Redux)").option("--no-persist","Do not persist the slice").option("--test","Also generate a sibling test file (Redux)").option("--no-test","Skip test file generation").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now();m(Yt.cyan(`
|
|
709
709
|
\u{1F527} Slice Generator
|
|
710
|
-
`)),
|
|
711
|
-
`));let i=await
|
|
710
|
+
`)),g.start("Detecting project setup...");let s=await I(n);if(g.succeed("Project setup detected"),s.state==="none")throw new Error("This project has no client store. Re-run init with a state store, or add one with `next-maker setup --set state=redux`.");m(Yt.dim(` state: ${s.state}
|
|
711
|
+
`));let i=await Is(t,s.state==="zustand"?!1:r.persist);D(i.sliceName,"slice name"),r.path&&Z(r.path,"path");let a=r.path??cn.join("src","store","slices"),c=le(n,a,i.sliceName);if(await Ce(n,i.sliceName,a))throw new Error(`Slice '${i.sliceName}' already exists at ${a}.`);g.start("Generating slice files..."),await ut({name:i.sliceName,outputPath:c,persist:i.persistSlice,state:s.state}),g.succeed("Slice files generated"),s.state==="redux"&&(g.start("Registering slice in the store..."),await yt({projectPath:n,name:i.sliceName,persist:i.persistSlice,importPath:cn.join(a,i.sliceName)}),g.succeed("Slice registered"),(r.test??s.hasTests)&&(g.start("Generating test..."),await me({projectPath:n,sourceFile:cn.join(c,`${i.sliceName}.slice.ts`),kind:"slice",symbolName:i.sliceName.replace(/-([a-z])/g,(l,p)=>p.toUpperCase())}),g.succeed("Test generated"))),await E(n,o),m(Yt.green(`
|
|
712
712
|
\u2728 Slice '${i.sliceName}' created in ${a}/${i.sliceName}/.
|
|
713
|
-
`)),s.state==="zustand"&&(m(
|
|
714
|
-
Infers the kind from filename/content; pass --kind to override.`).option("--kind <kind>","Override inferred kind: component | hook | slice").option("--force","Overwrite an existing test file").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now(),s=
|
|
713
|
+
`)),s.state==="zustand"&&(m(Yt.yellow(` ! Compose create${k(i.sliceName)}Slice into AppState in src/store/index.ts`)),m(""))}catch(n){g.fail("Slice generation failed"),v(`${n instanceof Error?n.message:n}`),process.exit(1)}})};import X from"node:path";import pn from"picocolors";var Fs=e=>{e.command("test <file>").description(`Generate a sibling test file for an existing component, hook, or slice.
|
|
714
|
+
Infers the kind from filename/content; pass --kind to override.`).option("--kind <kind>","Override inferred kind: component | hook | slice").option("--force","Overwrite an existing test file").action(async(t,r)=>{try{let n=process.cwd(),o=Date.now(),s=X.isAbsolute(t)?t:X.join(n,t);m(pn.cyan(`
|
|
715
715
|
\u{1F9EA} Test Generator
|
|
716
|
-
`)),w(s)||(
|
|
717
|
-
\u2728 Test '${
|
|
718
|
-
`)),m(
|
|
719
|
-
`},
|
|
716
|
+
`)),w(s)||(v(`Source file not found: ${t}`),process.exit(1));let i=await I(n);i.hasTests||(v("Testing is not installed in this project. Run `next-maker setup --tests` first."),process.exit(1));let a=await C(s),c=Ip(r.kind)??Np(s,a);c||(v(`Could not infer test kind for ${X.basename(s)}. Pass --kind component|hook|slice.`),process.exit(1));let l=Fp(s,c),p=Mp(s,c);w(p)&&!r.force&&(v(`Test file already exists: ${X.relative(n,p)}. Use --force to overwrite.`),process.exit(1)),g.start(`Generating ${c} test...`);let u=await me({projectPath:n,sourceFile:s,kind:c,symbolName:l,hasState:i.state!=="none",hasI18n:i.hasI18n,hookUsesStore:c==="hook"&&i.state!=="none"&&a.includes("useAppSelector")});g.succeed("Test generated"),await E(n,o),m(pn.green(`
|
|
717
|
+
\u2728 Test '${X.basename(u)}' created.
|
|
718
|
+
`)),m(pn.dim(` \u{1F9EA} ${X.relative(n,u)}`)),m("")}catch(n){g.fail("Test generation failed"),v(`${n}`),process.exit(1)}})},Ip=e=>{if(!e)return null;if(e==="component"||e==="hook"||e==="slice")return e;throw new Error(`Invalid --kind: ${e}. Use component | hook | slice.`)},Np=(e,t)=>{let r=X.basename(e);return/\.slice\.ts$/.test(r)?"slice":/^use[A-Z]/.test(r.replace(/\.tsx?$/,""))?"hook":r.endsWith(".tsx")?"component":/createSlice\s*\(/.test(t)?"slice":/^export\s+(const|function)\s+use[A-Z]/m.test(t)?"hook":null},Fp=(e,t)=>{let r=X.basename(e).replace(/\.(tsx?|jsx?)$/i,"");return t==="slice"?V(r.replace(/\.slice$/,"")):t==="hook"?/^use[A-Z]/.test(r)?r:`use${k(r.replace(/^use-?/,""))}`:k(r)},Mp=(e,t)=>{let r=X.dirname(e),n=X.basename(e).replace(/\.(tsx?|jsx?)$/i,""),o=t==="component"?".tsx":".ts";return X.join(r,`${n}.test${o}`)};import{chmod as _p,cp as Dp,mkdir as Qt,readFile as Zt,rm as Lp,writeFile as M}from"node:fs/promises";import $ from"node:path";import $e from"picocolors";var Ms=e=>{let t={"@biomejs/biome":e.biomeVersion,turbo:e.turboVersion};e.hooks&&(e.huskyVersion&&(t.husky=e.huskyVersion),e.lintStagedVersion&&(t["lint-staged"]=e.lintStagedVersion),e.commitlintCliVersion&&(t["@commitlint/cli"]=e.commitlintCliVersion),e.commitlintConfigVersion&&(t["@commitlint/config-conventional"]=e.commitlintConfigVersion));let r={name:e.name,version:"0.1.0",description:e.description,author:e.author,private:!0,type:"module",packageManager:e.packageManagerPin,engines:{node:e.nodeEngine,pnpm:">=11.0.0"},scripts:{dev:"turbo run dev",build:"turbo run build",start:"turbo run start",lint:"turbo run lint","lint:fix":"biome check --write . && turbo run lint:fix","ci:check":"biome ci . && turbo run ci:check","type-check":"turbo run type-check","check:deprecated":"turbo run check:deprecated",test:"turbo run test","test:e2e":"turbo run test:e2e",validate:"pnpm ci:check && pnpm type-check && pnpm check:deprecated && pnpm test && pnpm build",...e.hooks?{prepare:"husky","lint-staged":"lint-staged"}:{}},devDependencies:Object.fromEntries(Object.entries(t).sort())};return`${JSON.stringify(r,null,2)}
|
|
719
|
+
`},_s=e=>`packages:
|
|
720
720
|
- 'apps/*'
|
|
721
721
|
- 'packages/*'
|
|
722
722
|
|
|
723
723
|
${e.split(`
|
|
724
724
|
`).filter(r=>!r.startsWith("packages:")&&!r.startsWith("# Single-package")&&!r.startsWith("# Monorepo mode")).join(`
|
|
725
725
|
`).trim()}
|
|
726
|
-
`,
|
|
727
|
-
`,
|
|
728
|
-
`,
|
|
726
|
+
`,Ds=()=>`${JSON.stringify({$schema:"https://turborepo.com/schema.json",ui:"tui",globalEnv:["NODE_ENV","CI"],globalPassThroughEnv:["NEXT_PUBLIC_*","API_INTERNAL_URL","CSP_MODE","BUILD_STANDALONE","DEFAULT_TIMEZONE","DEFAULT_LOCALE","NEXT_TELEMETRY_DISABLED"],tasks:{build:{dependsOn:["^build"],inputs:["$TURBO_DEFAULT$",".env.production",".env"],outputs:[".next/**","!.next/cache/**"]},dev:{cache:!1,persistent:!0},start:{cache:!1,persistent:!0,dependsOn:["build"]},lint:{},"lint:fix":{cache:!1},"ci:check":{},"type-check":{dependsOn:["^build"]},"check:deprecated":{dependsOn:["^build"]},test:{dependsOn:["^build"],outputs:["coverage/**"]},"test:e2e":{dependsOn:["build"],cache:!1}}},null,2)}
|
|
727
|
+
`,Ls=e=>`${JSON.stringify({$schema:`https://biomejs.dev/schemas/${e.replace(/^[\^~]/,"")}/schema.json`,vcs:{enabled:!0,clientKind:"git",useIgnoreFile:!0},files:{includes:["*.json","*.mjs","*.ts",".github/**","!apps","!packages"]},formatter:{enabled:!0,indentStyle:"space",indentWidth:2,lineWidth:100,lineEnding:"lf"},javascript:{formatter:{quoteStyle:"single",semicolons:"always",trailingCommas:"all"}},json:{formatter:{trailingCommas:"none"}},linter:{enabled:!0,rules:{preset:"recommended"}}},null,2)}
|
|
728
|
+
`,Us=()=>`# dependencies
|
|
729
729
|
node_modules
|
|
730
730
|
.pnpm-store
|
|
731
731
|
|
|
@@ -751,7 +751,7 @@ next-env.d.ts
|
|
|
751
751
|
*.pem
|
|
752
752
|
npm-debug.log*
|
|
753
753
|
.pnpm-debug.log*
|
|
754
|
-
`,
|
|
754
|
+
`,Bs=(e,t)=>`# ${e.name}
|
|
755
755
|
|
|
756
756
|
${e.description}
|
|
757
757
|
|
|
@@ -820,7 +820,7 @@ Ranges shared by every app live in the \`catalog:\` section of \`pnpm-workspace.
|
|
|
820
820
|
## Maintenance
|
|
821
821
|
|
|
822
822
|
\`cd apps/<app> && npx @teispace/next-maker doctor --compile\` checks an app against the starter; \`setup --set <option>=<value>\` changes its features.
|
|
823
|
-
`,
|
|
823
|
+
`,Gs=()=>`name: CI
|
|
824
824
|
|
|
825
825
|
on:
|
|
826
826
|
push:
|
|
@@ -862,12 +862,12 @@ jobs:
|
|
|
862
862
|
- run: pnpm check:deprecated
|
|
863
863
|
- run: pnpm test
|
|
864
864
|
- run: pnpm build
|
|
865
|
-
`,
|
|
865
|
+
`,Vs=()=>`const config = {
|
|
866
866
|
'*': ['biome check --write --no-errors-on-unmatched --files-ignore-unknown=true'],
|
|
867
867
|
};
|
|
868
868
|
|
|
869
869
|
export default config;
|
|
870
|
-
`,
|
|
870
|
+
`,qs=()=>`const config = {
|
|
871
871
|
extends: ['@commitlint/config-conventional'],
|
|
872
872
|
rules: {
|
|
873
873
|
'header-max-length': [2, 'always', 150],
|
|
@@ -876,13 +876,13 @@ export default config;
|
|
|
876
876
|
};
|
|
877
877
|
|
|
878
878
|
export default config;
|
|
879
|
-
`,
|
|
879
|
+
`,Hs=()=>({"pre-commit":`#!/usr/bin/env sh
|
|
880
880
|
pnpm lint-staged
|
|
881
881
|
`,"commit-msg":`#!/usr/bin/env sh
|
|
882
882
|
pnpm exec commitlint --edit "$1"
|
|
883
883
|
`,"pre-push":`#!/usr/bin/env sh
|
|
884
884
|
pnpm ci:check && pnpm type-check && pnpm check:deprecated && pnpm test
|
|
885
|
-
`}),
|
|
885
|
+
`}),Js=(e,t="24")=>`# syntax=docker.io/docker/dockerfile:1
|
|
886
886
|
# Build from the workspace root:
|
|
887
887
|
# docker build -f apps/${e}/Dockerfile -t ${e} .
|
|
888
888
|
|
|
@@ -935,7 +935,7 @@ EXPOSE 3000
|
|
|
935
935
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \\
|
|
936
936
|
CMD wget -qO- http://127.0.0.1:3000/robots.txt >/dev/null || exit 1
|
|
937
937
|
CMD ["node", "apps/${e}/server.js"]
|
|
938
|
-
`,
|
|
938
|
+
`,zs=e=>`services:
|
|
939
939
|
${e.map((r,n)=>` ${r}:
|
|
940
940
|
build:
|
|
941
941
|
context: .
|
|
@@ -952,7 +952,7 @@ ${e.map((r,n)=>` ${r}:
|
|
|
952
952
|
API_INTERNAL_URL: \${API_INTERNAL_URL}`).join(`
|
|
953
953
|
|
|
954
954
|
`)}
|
|
955
|
-
`,
|
|
955
|
+
`,Ws=()=>`node_modules
|
|
956
956
|
**/node_modules
|
|
957
957
|
.git
|
|
958
958
|
.turbo
|
|
@@ -962,12 +962,12 @@ ${e.map((r,n)=>` ${r}:
|
|
|
962
962
|
**/test-results
|
|
963
963
|
**/.env
|
|
964
964
|
**/.env.*
|
|
965
|
-
`,
|
|
965
|
+
`,Ks=e=>{if(e.length===0)return{};let t=o=>({...o.dependencies??{},...o.devDependencies??{}}),r=t(e[0]),n={};for(let[o,s]of Object.entries(r))e.every(i=>t(i)[o]===s)&&(n[o]=s);return Object.fromEntries(Object.entries(n).sort(([o],[s])=>o.localeCompare(s)))},Xs=(e,t)=>{let r=structuredClone(e);for(let n of["dependencies","devDependencies"]){let o=r[n];if(o)for(let s of Object.keys(o))t[s]===o[s]&&(o[s]="catalog:")}return r},Ys=e=>Object.keys(e).length?`
|
|
966
966
|
catalog:
|
|
967
967
|
${Object.entries(e).map(([t,r])=>` '${t}': '${r}'`).join(`
|
|
968
968
|
`)}
|
|
969
|
-
`:"";var
|
|
970
|
-
Cleaning up ${e}...`)),await
|
|
971
|
-
Process interrupted.`)),await i()});try{await
|
|
972
|
-
`),t.docker&&await
|
|
973
|
-
`)},s),t.install!==!1&&(await
|
|
969
|
+
`:"";var Up=(e,t=[])=>[...t,e],Qs={hooks:!1,commitizen:!1,ci:!1,docker:!1,githubTemplates:!1,communityFiles:[]},Zs=e=>{e.command("workspace <name>").alias("monorepo").description("Create a pnpm + Turborepo workspace with one starter app per --apps entry").option("--apps <names>","Comma-separated app names","web,admin").option("-y, --yes","Skip prompts and use the starter defaults for every app").option("--preset <name>",`Preset for the apps (${Be().join(", ")})`).option("--set <key=value>","Starter option applied to every app (repeatable)",Up).option("--starter-path <dir>","Compose from a local starter checkout").option("--docker","Per-app Dockerfiles (turbo prune) and a root docker-compose.yml").option("--no-catalog","Keep dependency ranges in each app instead of the pnpm catalog").option("--no-install","Skip dependency installation").option("--no-git","Skip git init").option("--no-ci","Skip the root GitHub Actions workflow").option("--no-hooks","Skip root git hooks (Husky, lint-staged, commitlint)").action(async(t,r)=>{try{await Gp(t,r)}catch(n){v(`${n instanceof Error?n.message:n}`),process.exit(1)}})},_e=async(e,t,r,n)=>{let o=Rt(e);n.current=o;try{let s=await r();return o.succeed(t),s}finally{n.current===o&&(n.current=null)}},Bp=e=>{let t=e.split(",").map(r=>r.trim()).filter(Boolean);if(t.length===0)throw new Error("Pass at least one app name with --apps.");for(let r of t)at(r);if(new Set(t).size!==t.length)throw new Error("App names must be unique.");return t},Gp=async(e,t)=>{jt(),at(e);let r=Bp(t.apps??"web,admin"),n=$.resolve(process.cwd(),e);if(w(n))throw new Error(`Directory ${e} already exists.`);let o={...t.preset?Ge(t.preset):{},...Ee(t.set)};for(let[c,l]of Object.entries(Qs))o[c]!==void 0&&JSON.stringify(o[c])!==JSON.stringify(l)&&m($e.yellow(` ! ${c} is managed at the workspace root; ignoring --set ${c}`)),o[c]=l;o.packageManager="pnpm";let s={current:null},i=async()=>{w(n)&&(s.current?.stop(),s.current=null,m($e.yellow(`
|
|
970
|
+
Cleaning up ${e}...`)),await it(n))},a=Et(async()=>{m($e.red(`
|
|
971
|
+
Process interrupted.`)),await i()});try{await Qt($.join(n,"apps"),{recursive:!0}),await Qt($.join(n,"packages"),{recursive:!0});let c=W({starterPath:t.starterPath}),l=$.join(n,"apps",r[0]),p=await _e(`Fetching starter (${c.location})...`,"Starter fetched.",()=>je({projectPath:l,source:c,supplied:o,packageManager:"pnpm"}),s);if(!t.yes){let b=await Nt(p.manifest,p.answers);p=ct(p.manifest,{...b,...Qs,packageManager:"pnpm"},"pnpm"),m("")}for(let b of p.forced)m($e.yellow(` ! ${b.reason}; ${b.option} turned off`));let u=JSON.parse(await Zt($.join(l,"package.json"),"utf-8")),d=await Zt($.join(l,"pnpm-workspace.yaml"),"utf-8"),f={};for(let b of[".nvmrc",".npmrc",".editorconfig"])w($.join(l,b))&&(f[b]=await Zt($.join(l,b),"utf-8"));let y=b=>ye(b,{projectName:b,description:`${b} app of ${e}`,readme:!0,copyEnv:!0});await _e(`Composing apps/${r[0]}...`,`apps/${r[0]} composed.`,()=>Re(l,y(r[0]),p,c),s);for(let b of r.slice(1)){let P=$.join(n,"apps",b);await _e(`Composing apps/${b}...`,`apps/${b} composed.`,async()=>{let R=await je({projectPath:P,source:c,supplied:p.answers,packageManager:"pnpm"});await Re(P,y(b),R,c)},s)}let h=[];for(let b of r){let P=$.join(n,"apps",b);for(let q of["pnpm-workspace.yaml","pnpm-lock.yaml",".npmrc",".nvmrc"])await Lp($.join(P,q),{force:!0});let R=JSON.parse(await Zt($.join(P,"package.json"),"utf-8"));delete R.packageManager,delete R.engines,h.push(R)}let x=t.catalog===!1?{}:Ks(h);for(let[b,P]of r.entries()){let R=Xs(h[b],x);await M($.join(n,"apps",P,"package.json"),`${JSON.stringify(R,null,2)}
|
|
972
|
+
`),t.docker&&await M($.join(n,"apps",P,"Dockerfile"),Js(P))}await _e("Writing workspace root...","Workspace root written.",async()=>{let b={name:e,description:`${e} workspace`,author:u.author??"",apps:r,packageManager:"pnpm",packageManagerPin:u.packageManager??"pnpm@11.25.0",nodeEngine:u.engines?.node??">=24.0.0",turboVersion:"^2.10.0",biomeVersion:u.devDependencies?.["@biomejs/biome"]??"^2.5.0",hooks:t.hooks!==!1,huskyVersion:u.devDependencies?.husky,lintStagedVersion:u.devDependencies?.["lint-staged"],commitlintCliVersion:u.devDependencies?.["@commitlint/cli"],commitlintConfigVersion:u.devDependencies?.["@commitlint/config-conventional"]};await M($.join(n,"package.json"),Ms(b)),await M($.join(n,"pnpm-workspace.yaml"),_s(d)+Ys(x)),await M($.join(n,"turbo.json"),Ds()),await M($.join(n,"biome.json"),Ls(b.biomeVersion)),await M($.join(n,".gitignore"),Us()),await M($.join(n,"README.md"),Bs(b,p.manifest.starter.version)),await M($.join(n,"packages",".gitkeep"),"");for(let[P,R]of Object.entries(f))await M($.join(n,P),R);if(t.docker&&(await M($.join(n,"docker-compose.yml"),zs(r)),await M($.join(n,".dockerignore"),Ws())),t.ci!==!1&&(await Qt($.join(n,".github","workflows"),{recursive:!0}),await M($.join(n,".github","workflows","ci.yml"),Gs())),t.hooks!==!1){await M($.join(n,".lintstagedrc.mjs"),Vs()),await M($.join(n,"commitlint.config.mjs"),qs()),await Qt($.join(n,".husky"),{recursive:!0});for(let[P,R]of Object.entries(Hs())){let q=$.join(n,".husky",P);await M(q,R),await _p(q,493)}}await M($.join(n,".next-maker.json"),`${JSON.stringify({workspace:!0,apps:r,starter:p.manifest.starter,answers:p.answers,docker:!!t.docker,catalog:t.catalog!==!1},null,2)}
|
|
973
|
+
`)},s),t.install!==!1&&(await _e("Installing dependencies with pnpm...","Dependencies installed.",()=>he(n,"pnpm"),s),await _e("Formatting...","Formatted.",()=>It(n,"pnpm","lint:fix"),s));for(let b of r){let P=$.join(n,"apps",b,".env.example");w(P)&&await Dp(P,$.join(n,"apps",b,".env"))}t.git!==!1&&await _e("Initializing git...","Git initialized.",()=>At(n),s),a(),m(""),m($e.green(`\u2728 Workspace ${e} is ready with ${r.map(b=>`apps/${b}`).join(", ")}.`)),m(""),m("Next:"),m($e.cyan(` cd ${e}`)),t.install===!1&&m($e.cyan(" pnpm install")),m($e.cyan(` pnpm --filter ${r[0]} dev`)),m("")}catch(c){throw a(),s.current?.fail("Failed to create workspace."),s.current=null,await i(),c}};var ei=e=>{Hn(e),Zs(e),As(e),fs(e),Rs(e),cs(e),Ns(e),xs(e),hs(e),ms(e),Lo(e),ds(e),ls(e),ys(e),Go(e),ss(e),Uo(e),ws(e),Fs(e)};var qp=process.emitWarning;process.emitWarning=function(e,t,r,n){if(!(typeof e=="string"&&e.includes("--localstorage-file")&&e.includes("was provided without a valid path")))return qp.apply(process,arguments)};async function Hp(){$n({logger:t=>Tt(t)});let e=new Vp;e.name("next-maker").description("Teispace Next.js Project Generator").version(De.version),ei(e),e.parse()}Hp().catch(e=>{let t=e&&typeof e=="object"&&"message"in e?e.message:String(e);Tt(`Unexpected error: ${t}`)});
|